ConditionNodeConfig.vue 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  1. <template>
  2. <el-drawer
  3. :append-to-body="true"
  4. v-model="settingVisible"
  5. :show-close="false"
  6. :size="588"
  7. :before-close="handleClose"
  8. >
  9. <template #header>
  10. <div class="config-header">
  11. <input
  12. v-if="showInput"
  13. type="text"
  14. class="config-editable-input"
  15. @blur="blurEvent()"
  16. v-mountedFocus
  17. v-model="currentNode.name"
  18. :placeholder="currentNode.name"
  19. />
  20. <div v-else class="node-name"
  21. >{{ currentNode.name }}
  22. <Icon class="ml-1" icon="ep:edit-pen" :size="16" @click="clickIcon()"
  23. /></div>
  24. <div class="divide-line"></div>
  25. </div>
  26. </template>
  27. <div>
  28. <div class="mb-3 font-size-16px" v-if="currentNode.conditionSetting?.defaultFlow"
  29. >未满足其它条件时,将进入此分支(该分支不可编辑和删除)</div
  30. >
  31. <div v-else>
  32. <Condition ref="conditionRef" v-model="condition" />
  33. </div>
  34. </div>
  35. <template #footer>
  36. <el-divider />
  37. <div>
  38. <el-button type="primary" @click="saveConfig">确 定</el-button>
  39. <el-button @click="closeDrawer">取 消</el-button>
  40. </div>
  41. </template>
  42. </el-drawer>
  43. </template>
  44. <script setup lang="ts">
  45. import {
  46. SimpleFlowNode,
  47. ConditionType,
  48. COMPARISON_OPERATORS,
  49. ProcessVariableEnum
  50. } from '../consts'
  51. import { getDefaultConditionNodeName } from '../utils'
  52. import { useFormFields } from '../node'
  53. import Condition from './components/Condition.vue'
  54. const message = useMessage() // 消息弹窗
  55. defineOptions({
  56. name: 'ConditionNodeConfig'
  57. })
  58. const props = defineProps({
  59. conditionNode: {
  60. type: Object as () => SimpleFlowNode,
  61. required: true
  62. },
  63. nodeIndex: {
  64. type: Number,
  65. required: true
  66. }
  67. })
  68. const settingVisible = ref(false)
  69. const currentNode = ref<SimpleFlowNode>(props.conditionNode)
  70. const condition = ref<any>()
  71. const open = () => {
  72. condition.value = currentNode.value.conditionSetting
  73. settingVisible.value = true
  74. }
  75. watch(
  76. () => props.conditionNode,
  77. (newValue) => {
  78. currentNode.value = newValue
  79. }
  80. )
  81. // 显示名称输入框
  82. const showInput = ref(false)
  83. const clickIcon = () => {
  84. showInput.value = true
  85. }
  86. // 输入框失去焦点
  87. const blurEvent = () => {
  88. showInput.value = false
  89. currentNode.value.name =
  90. currentNode.value.name ||
  91. getDefaultConditionNodeName(props.nodeIndex, currentNode.value?.conditionSetting?.defaultFlow)
  92. }
  93. defineExpose({ open }) // 提供 open 方法,用于打开弹窗
  94. // 关闭
  95. const closeDrawer = () => {
  96. settingVisible.value = false
  97. }
  98. const handleClose = async (done: (cancel?: boolean) => void) => {
  99. const isSuccess = await saveConfig()
  100. if (!isSuccess) {
  101. done(true) // 传入 true 阻止关闭
  102. } else {
  103. done()
  104. }
  105. }
  106. const conditionRef = ref()
  107. // 保存配置
  108. const saveConfig = async () => {
  109. if (!currentNode.value.conditionSetting?.defaultFlow) {
  110. // 校验表单
  111. const valid = await conditionRef.value.validate()
  112. if (!valid) return false
  113. const showText = getShowText()
  114. if (!showText) {
  115. return false
  116. }
  117. currentNode.value.showText = showText
  118. currentNode.value.conditionSetting!.conditionType = condition.value?.conditionType
  119. if (currentNode.value.conditionSetting?.conditionType === ConditionType.EXPRESSION) {
  120. currentNode.value.conditionSetting.conditionGroups = undefined
  121. currentNode.value.conditionSetting.conditionExpression = condition.value?.conditionExpression
  122. }
  123. if (currentNode.value.conditionSetting!.conditionType === ConditionType.RULE) {
  124. currentNode.value.conditionSetting!.conditionExpression = undefined
  125. currentNode.value.conditionSetting!.conditionGroups = condition.value?.conditionGroups
  126. }
  127. }
  128. settingVisible.value = false
  129. return true
  130. }
  131. const getShowText = (): string => {
  132. let showText = ''
  133. if (condition.value?.conditionType === ConditionType.EXPRESSION) {
  134. if (condition.value.conditionExpression) {
  135. showText = `表达式:${condition.value.conditionExpression}`
  136. }
  137. }
  138. if (condition.value?.conditionType === ConditionType.RULE) {
  139. // 条件组是否为与关系
  140. const groupAnd = condition.value.conditionGroups?.and
  141. let warningMesg: undefined | string = undefined
  142. const conditionGroup = condition.value.conditionGroups?.conditions.map((item) => {
  143. return (
  144. '(' +
  145. item.rules
  146. .map((rule) => {
  147. if (rule.leftSide && rule.rightSide) {
  148. return (
  149. getFieldTitle(rule.leftSide) + ' ' + getOpName(rule.opCode) + ' ' + rule.rightSide
  150. )
  151. } else {
  152. // 有一条规则不完善。提示错误
  153. warningMesg = '请完善条件规则'
  154. return ''
  155. }
  156. })
  157. .join(item.and ? ' 且 ' : ' 或 ') +
  158. ' ) '
  159. )
  160. })
  161. if (warningMesg) {
  162. message.warning(warningMesg)
  163. showText = ''
  164. } else {
  165. showText = conditionGroup!.join(groupAnd ? ' 且 ' : ' 或 ')
  166. }
  167. }
  168. return showText
  169. }
  170. const fieldsInfo = useFormFields()
  171. /** 条件规则可选择的表单字段 */
  172. const fieldOptions = computed(() => {
  173. const fieldsCopy = fieldsInfo.slice()
  174. // 固定添加发起人 ID 字段
  175. fieldsCopy.unshift({
  176. field: ProcessVariableEnum.START_USER_ID,
  177. title: '发起人',
  178. required: true
  179. })
  180. return fieldsCopy
  181. })
  182. /** 获取字段名称 */
  183. const getFieldTitle = (field: string) => {
  184. const item = fieldOptions.value.find((item) => item.field === field)
  185. return item?.title
  186. }
  187. /** 获取操作符名称 */
  188. const getOpName = (opCode: string): string => {
  189. const opName = COMPARISON_OPERATORS.find((item: any) => item.value === opCode)
  190. return opName?.label
  191. }
  192. </script>
  193. <style lang="scss" scoped>
  194. .condition-group-tool {
  195. display: flex;
  196. justify-content: space-between;
  197. width: 500px;
  198. margin-bottom: 20px;
  199. }
  200. .condition-group {
  201. position: relative;
  202. &:hover {
  203. border-color: #0089ff;
  204. .condition-group-delete {
  205. opacity: 1;
  206. }
  207. }
  208. .condition-group-delete {
  209. position: absolute;
  210. top: 0;
  211. left: 0;
  212. display: flex;
  213. cursor: pointer;
  214. opacity: 0;
  215. }
  216. }
  217. ::v-deep(.el-card__header) {
  218. padding: 8px var(--el-card-padding);
  219. border-bottom: 1px solid var(--el-card-border-color);
  220. box-sizing: border-box;
  221. }
  222. </style>