branch.vue 13 KB

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