branch.vue 14 KB

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