branch.vue 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453
  1. <!--条件分支-->
  2. <template>
  3. <!-- `branch-wrap--${nodeConfig.local_status}`-->
  4. <div class="branch-wrap" :class="[disabled ? 'branch-wrap--disabled' : '']">
  5. <div class="branch-box-wrap">
  6. <div class="branch-box">
  7. <el-button class="add-branch" type="success" plain round @click="addTerm"> 添加条件 </el-button>
  8. <div v-for="(item, index) in nodeConfig.conditionNodes" :key="index" class="col-box">
  9. <div class="condition-node">
  10. <div class="condition-node-box">
  11. <div :class="['auto-judge', `auto-judge--${item.local_status}`]" @click="show(index)">
  12. <!-- 不是第一个 也不是 最后一个-->
  13. <div v-if="index != 0 && index != nodeConfig.conditionNodes.length - 1" class="sort-left" @click.stop="arrTransfer(index, -1)">
  14. <el-icon><ArrowLeft /></el-icon>
  15. </div>
  16. <div class="title">
  17. <span class="node-title" :class="[index === nodeConfig.conditionNodes.length - 1 ? 'last-child-title' : '']">{{
  18. index === nodeConfig.conditionNodes.length - 1 ? '默认条件' : item.nodeName
  19. }}</span>
  20. <span class="priority-title">优先级{{ item.priorityLevel }}</span>
  21. <el-icon v-if="index != nodeConfig.conditionNodes.length - 1" class="close" @click.stop="delTerm(index)">
  22. <Close />
  23. </el-icon>
  24. </div>
  25. <div class="content" :class="[index === nodeConfig.conditionNodes.length - 1 ? 'last-child-title' : '']" style="width: 200px">
  26. <el-tooltip v-if="toText(nodeConfig, index)" effect="dark" placement="top">
  27. <div class="text-overflow_ellipsis_line_2" v-html="toText(nodeConfig, index)" />
  28. <template #content>
  29. <div v-html="toText(nodeConfig, index)" />
  30. </template>
  31. </el-tooltip>
  32. <span v-else class="placeholder"> 请设置条件 </span>
  33. </div>
  34. <!-- 最后一个没有,长度大于2倒数第二个没有 -->
  35. <div
  36. v-if="
  37. !(nodeConfig.conditionNodes.length == 2 && (index === nodeConfig.conditionNodes.length - 1 || index === 0)) &&
  38. !(
  39. (nodeConfig.conditionNodes.length > 2 && index === nodeConfig.conditionNodes.length - 1) ||
  40. index === nodeConfig.conditionNodes.length - 2
  41. )
  42. "
  43. class="sort-right"
  44. @click.stop="arrTransfer(index)"
  45. >
  46. <el-icon><ArrowRight /></el-icon>
  47. </div>
  48. </div>
  49. <add-node v-model="item.childNode" :disabled="disabled"></add-node>
  50. </div>
  51. </div>
  52. <slot v-if="item.childNode" :node="item"></slot>
  53. <div v-if="index == 0" class="top-left-cover-line"></div>
  54. <div v-if="index == 0" class="bottom-left-cover-line"></div>
  55. <div v-if="index == nodeConfig.conditionNodes.length - 1" class="top-right-cover-line"></div>
  56. <div v-if="index == nodeConfig.conditionNodes.length - 1" class="bottom-right-cover-line"></div>
  57. </div>
  58. </div>
  59. <add-node v-model="nodeConfig.childNode" :disabled="disabled"></add-node>
  60. </div>
  61. <el-drawer v-model="drawer" title="条件设置" destroy-on-close append-to-body :size="600">
  62. <template #header>
  63. <div class="node-wrap-drawer__title">
  64. <label v-if="!isEditTitle" @click="editTitle">
  65. {{ form.nodeName }}<el-icon class="node-wrap-drawer__title-edit"><Edit /></el-icon>
  66. <!-- <div @click="rmConditionGroup(conditionGroup)">-->
  67. <!-- -->
  68. <!-- </div>-->
  69. </label>
  70. <el-input
  71. v-if="isEditTitle"
  72. ref="nodeTitle"
  73. v-model="form.nodeName"
  74. clearable
  75. class="w-40"
  76. @blur="saveTitle"
  77. @keyup.enter="saveTitle"
  78. ></el-input>
  79. <el-input v-model="form.nodeKey" clearable class="w-40 pl-1.5" placeholder="请填写nodeKey"></el-input>
  80. </div>
  81. </template>
  82. <el-container>
  83. <el-main style="padding: 0 0 20px 0">
  84. <div class="top-tips">满足以下条件时进入当前分支</div>
  85. <template v-for="(conditionGroup, conditionGroupIdx) in form.conditionList" :key="conditionGroupIdx">
  86. <div v-if="conditionGroupIdx != 0" class="or-branch-link-tip">或满足</div>
  87. <div class="condition-group-editor">
  88. <div class="header">
  89. <span>条件组 {{ conditionGroupIdx + 1 }}</span>
  90. <div @click="deleteConditionGroup(conditionGroupIdx)">
  91. <el-icon class="branch-delete-icon"><Delete /></el-icon>
  92. </div>
  93. </div>
  94. <div class="main-content">
  95. <!-- 单个条件 -->
  96. <div class="condition-content-box cell-box">
  97. <div v-if="false">描述</div>
  98. <div>条件字段</div>
  99. <div>运算符</div>
  100. <div>值</div>
  101. </div>
  102. <div v-for="(condition, idx) in conditionGroup" :key="idx" class="condition-content">
  103. <div class="condition-relation">
  104. <span>{{ idx == 0 ? '当' : '且' }}</span>
  105. <div v-if="conditionGroup.length > 1" @click="deleteConditionList(conditionGroup, idx)">
  106. <el-icon class="branch-delete-icon"><Delete /></el-icon>
  107. </div>
  108. </div>
  109. <div class="condition-content">
  110. <div class="condition-content-box">
  111. <el-input
  112. v-if="condition.type === 'custom'"
  113. v-model="condition.label"
  114. placeholder="自定义条件字段"
  115. @input="getCurrentItemField(conditionGroupIdx, idx)"
  116. />
  117. <el-select
  118. v-if="condition.type === 'form'"
  119. v-model="condition.field"
  120. filterable
  121. placeholder="表单条件字段"
  122. @change="getCurrentItemLabel(conditionGroupIdx, idx)"
  123. >
  124. <el-option v-for="{ id, label, key } in expressionFormList" :key="id" :label="label" :value="key" />
  125. </el-select>
  126. <el-select v-model="condition.operator" placeholder="请选择表达式">
  127. <el-option v-for="{ value, label } in operatorType" :key="label" :label="label" :value="value"></el-option>
  128. </el-select>
  129. <el-input v-model="condition.value" placeholder="值" />
  130. </div>
  131. </div>
  132. </div>
  133. </div>
  134. <div class="sub-content">
  135. <el-button link type="primary" icon="Plus" @click="addConditionList(conditionGroup, 'custom')"> 添加自定义条件 </el-button>
  136. <el-button
  137. v-if="expressionFormList.length"
  138. link
  139. type="primary"
  140. icon="Plus"
  141. style="margin-left: 8px"
  142. @click="addConditionList(conditionGroup, 'form')"
  143. >
  144. 添加表单条件
  145. </el-button>
  146. </div>
  147. </div>
  148. </template>
  149. <el-button style="width: 100%" type="info" icon="Plus" text bg @click="addConditionGroup"> 添加条件组 </el-button>
  150. </el-main>
  151. <el-footer>
  152. <el-button type="primary" @click="save"> 保存 </el-button>
  153. <el-button @click="drawer = false">取消</el-button>
  154. </el-footer>
  155. </el-container>
  156. </el-drawer>
  157. </div>
  158. </template>
  159. <script>
  160. import addNode from './addNode.vue'
  161. import { operatorType } from './config'
  162. import useFlowStore from '@/store/modules/flow'
  163. import { Delete, Plus, ArrowLeft, Close, ArrowRight, Edit } from '@element-plus/icons-vue'
  164. import { mapState } from 'pinia'
  165. import { getNodeKey } from '@/utils/workflow'
  166. import { ElMessage } from 'element-plus'
  167. import { $log } from '@/utils'
  168. export default {
  169. components: {
  170. addNode
  171. },
  172. props: {
  173. modelValue: { type: Object, default: () => {} },
  174. disabled: {
  175. type: Boolean,
  176. default: false
  177. }
  178. },
  179. data() {
  180. return {
  181. operatorType,
  182. nodeConfig: {},
  183. drawer: false,
  184. isEditTitle: false,
  185. index: 0,
  186. form: {},
  187. expressionFormList: [],
  188. local_formStructure: {}
  189. }
  190. },
  191. computed: {
  192. ...mapState(useFlowStore, ['processForm']) //映射函数,取出processForm
  193. },
  194. watch: {
  195. modelValue() {
  196. this.nodeConfig = this.modelValue
  197. },
  198. processForm: {
  199. handler(processForm) {
  200. // console.error(val, 'processForm change...', typeof val)
  201. const { formStructure } = JSON.parse(processForm) // 表单设计字段
  202. this.local_formStructure = formStructure
  203. // 使用 filter 方法找到 required 属性为 true 的对象
  204. this.expressionFormList = (formStructure?.fields || []).filter(item => item.options && item.options.required === true)
  205. $log(this.expressionFormList, '这里打印出符合条件的表单对象')
  206. },
  207. immediate: true
  208. }
  209. },
  210. mounted() {
  211. this.nodeConfig = this.modelValue
  212. },
  213. methods: {
  214. show(index) {
  215. if (this.disabled) return
  216. if (index === this.nodeConfig.conditionNodes.length - 1) {
  217. // 最后一个节点不能编辑
  218. return
  219. }
  220. this.index = index
  221. this.form = {}
  222. this.form = JSON.parse(JSON.stringify(this.nodeConfig.conditionNodes[index]))
  223. // const { formStructure } = JSON.parse(this.processForm) // 表单设计字段
  224. // // 使用 filter 方法找到 required 属性为 true 的对象
  225. // const requiredList = (formStructure?.fields || []).filter(item => item.options && item.options.required === true)
  226. // console.log(`%c 这里打印出符合条件的表单对象-=== ${JSON.stringify(requiredList)}`, 'background: orange; color: #fff')
  227. // this.expressionFormList = requiredList || []
  228. this.drawer = true
  229. },
  230. editTitle() {
  231. this.isEditTitle = true
  232. this.$nextTick(() => {
  233. this.$refs.nodeTitle.focus()
  234. })
  235. },
  236. saveTitle() {
  237. this.isEditTitle = false
  238. },
  239. save() {
  240. if (!this.form.nodeKey) {
  241. return ElMessage.error('请填写nodeKey')
  242. }
  243. this.nodeConfig.conditionNodes[this.index] = this.form
  244. this.$emit('update:modelValue', this.nodeConfig)
  245. this.drawer = false
  246. },
  247. addTerm() {
  248. let len = this.nodeConfig.conditionNodes.length + 1
  249. this.nodeConfig.conditionNodes.push({
  250. nodeName: '条件' + len,
  251. nodeKey: getNodeKey(),
  252. type: 3,
  253. priorityLevel: len,
  254. conditionMode: 1,
  255. conditionList: []
  256. })
  257. },
  258. delTerm(index) {
  259. this.nodeConfig.conditionNodes.splice(index, 1)
  260. if (this.nodeConfig.conditionNodes.length == 1) {
  261. if (this.nodeConfig.childNode) {
  262. if (this.nodeConfig.conditionNodes[0].childNode) {
  263. this.reData(this.nodeConfig.conditionNodes[0].childNode, this.nodeConfig.childNode)
  264. } else {
  265. this.nodeConfig.conditionNodes[0].childNode = this.nodeConfig.childNode
  266. }
  267. }
  268. this.$emit('update:modelValue', this.nodeConfig.conditionNodes[0].childNode)
  269. }
  270. },
  271. reData(data, addData) {
  272. if (!data.childNode) {
  273. data.childNode = addData
  274. } else {
  275. this.reData(data.childNode, addData)
  276. }
  277. },
  278. arrTransfer(index, type = 1) {
  279. this.nodeConfig.conditionNodes[index] = this.nodeConfig.conditionNodes.splice(index + type, 1, this.nodeConfig.conditionNodes[index])[0]
  280. this.nodeConfig.conditionNodes.map((item, index) => {
  281. item.priorityLevel = index + 1
  282. })
  283. this.$emit('update:modelValue', this.nodeConfig)
  284. },
  285. addConditionList(conditionList, type) {
  286. conditionList.push({
  287. label: '',
  288. field: '',
  289. operator: '==',
  290. value: '',
  291. type
  292. })
  293. },
  294. deleteConditionList(conditionList, index) {
  295. conditionList.splice(index, 1)
  296. },
  297. addConditionGroup() {
  298. this.addConditionList(this.form.conditionList[this.form.conditionList.push([]) - 1], 'custom')
  299. },
  300. deleteConditionGroup(index) {
  301. this.form.conditionList.splice(index, 1)
  302. },
  303. toText(nodeConfig, index) {
  304. var { conditionList } = nodeConfig.conditionNodes[index]
  305. if (conditionList && conditionList.length) {
  306. const text = conditionList
  307. .map(conditionGroup =>
  308. conditionGroup
  309. .map(item => {
  310. const showOperator = this.operatorType.find(i => i.value === item.operator).label
  311. if (item.type === 'form') {
  312. let showLabel = item.showLabel
  313. if (!showLabel) showLabel = this.expressionFormList.find(i => i.key === item.field)?.label
  314. return `${showLabel}${showOperator}${item.value}`
  315. } else {
  316. return `${item.label}${showOperator}${item.value}`
  317. }
  318. })
  319. .join(' <span style="color: var(--el-color-warning)">且</span> ')
  320. )
  321. .join('<br/> <span style="color: var(--el-color-success)">或</span> <br/>')
  322. // console.error(text, 'text')
  323. return text
  324. } else {
  325. if (index === nodeConfig.conditionNodes.length - 1) {
  326. return '未满足时其他条件时,将进入默认流程'
  327. } else {
  328. return ''
  329. }
  330. }
  331. },
  332. getCurrentItemLabel(conditionIdx, idx) {
  333. const currentCondition = this.form.conditionList[conditionIdx]
  334. const field = currentCondition[idx].field
  335. const labelObj = this.expressionFormList.find(i => i.key === field)
  336. currentCondition[idx].showLabel = labelObj.label
  337. currentCondition[idx].label = field
  338. },
  339. getCurrentItemField(conditionIdx, idx) {
  340. const currentCondition = this.form.conditionList[conditionIdx]
  341. currentCondition[idx].field = currentCondition[idx].label
  342. }
  343. }
  344. }
  345. </script>
  346. <style scoped lang="scss">
  347. .top-tips {
  348. display: flex;
  349. justify-content: space-between;
  350. align-items: center;
  351. margin-bottom: 12px;
  352. color: #646a73;
  353. }
  354. .or-branch-link-tip {
  355. margin: 10px 0;
  356. color: #646a73;
  357. }
  358. .condition-group-editor {
  359. user-select: none;
  360. border-radius: 4px;
  361. border: 1px solid #e4e5e7;
  362. position: relative;
  363. margin-bottom: 16px;
  364. .branch-delete-icon {
  365. font-size: 18px;
  366. }
  367. .header {
  368. background-color: #f4f6f8;
  369. padding: 0 12px;
  370. font-size: 14px;
  371. color: #171e31;
  372. height: 36px;
  373. display: flex;
  374. align-items: center;
  375. span {
  376. flex: 1;
  377. }
  378. }
  379. .main-content {
  380. padding: 0 12px;
  381. .condition-relation {
  382. color: #9ca2a9;
  383. display: flex;
  384. align-items: center;
  385. height: 36px;
  386. display: flex;
  387. justify-content: space-between;
  388. padding: 0 2px;
  389. }
  390. .condition-content-box {
  391. display: flex;
  392. justify-content: space-between;
  393. align-items: center;
  394. div {
  395. width: 100%;
  396. min-width: 120px;
  397. }
  398. div:not(:first-child) {
  399. margin-left: 16px;
  400. }
  401. }
  402. .cell-box {
  403. div {
  404. padding: 16px 0;
  405. width: 100%;
  406. min-width: 120px;
  407. color: #909399;
  408. font-size: 14px;
  409. font-weight: 600;
  410. text-align: center;
  411. }
  412. }
  413. .condition-content {
  414. display: flex;
  415. flex-direction: column;
  416. :deep(.el-input__wrapper) {
  417. border-top-left-radius: 0;
  418. border-bottom-left-radius: 0;
  419. }
  420. .content {
  421. flex: 1;
  422. padding: 0 0 4px 0;
  423. display: flex;
  424. align-items: center;
  425. min-height: 31.6px;
  426. flex-wrap: wrap;
  427. }
  428. }
  429. }
  430. .sub-content {
  431. padding: 12px;
  432. }
  433. }
  434. </style>