dengzedong
5 天以前 f9b459a3fefd5fab0ee8e19268adb9d9eadab2a7
提交 | 用户 | 时间
3e359e 1 <template>
H 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
25         <div class="divide-line"></div>
26       </div>
27     </template>
28     <div>
29       <div class="mb-3 font-size-16px" v-if="currentNode.defaultFlow">未满足其它条件时,将进入此分支(该分支不可编辑和删除)</div>
30       <div v-else>
31         <el-form
32           ref="formRef"
33           :model="currentNode"
34           :rules="formRules"
35           label-position="top"
36         >
37           <el-form-item label="配置方式" prop="conditionType">
38             <el-radio-group
39               v-model="currentNode.conditionType"
40               @change="changeConditionType"
41             >
42               <el-radio
43                 v-for="(dict, index) in conditionConfigTypes"
44                 :key="index"
45                 :value="dict.value"
46                 :label="dict.value"
47               >
48                 {{ dict.label }}
49               </el-radio>
50             </el-radio-group>
51           </el-form-item>
52
53           <el-form-item
54             v-if="currentNode.conditionType === 1"
55             label="条件表达式"
56             prop="conditionExpression"
57           >
58             <el-input
59               type="textarea"
60               v-model="currentNode.conditionExpression"
61               clearable
62               style="width: 100%"
63             />
64           </el-form-item>
65           <el-form-item v-if="currentNode.conditionType === 2" label="条件规则">
66             <div class="condition-group-tool">
67               <div class="flex items-center">
68                 <div class="mr-4">条件组关系</div>
69                 <el-switch
70                   v-model="conditionGroups.and"
71                   inline-prompt
72                   active-text="且"
73                   inactive-text="或"
74                 />
75               </div>
76             </div>
77             <el-space direction="vertical" :spacer="conditionGroups.and ? '且' : '或'">
78               <el-card
79                 class="condition-group"
80                 style="width: 530px"
81                 v-for="(condition, cIdx) in conditionGroups.conditions"
82                 :key="cIdx"
83               >
84                 <div class="condition-group-delete" v-if="conditionGroups.conditions.length > 1">
85                   <Icon
86                     color="#0089ff"
87                     icon="ep:circle-close-filled"
88                     :size="18"
89                     @click="deleteConditionGroup(cIdx)"
90                   />
91                 </div>
92                 <template #header>
93                   <div class="flex items-center justify-between">
94                     <div>条件组</div>
95                     <div class="flex">
96                       <div class="mr-4">规则关系</div>
97                       <el-switch
98                         v-model="condition.and"
99                         inline-prompt
100                         active-text="且"
101                         inactive-text="或"
102                       />
103                     </div>
104                   </div>
105                 </template>
106
107                 <div class="flex pt-2" v-for="(rule, rIdx) in condition.rules" :key="rIdx">
108                   <div class="mr-2">
109                     <el-select style="width: 160px" v-model="rule.leftSide">
110                       <el-option
111                         v-for="(item, index) in fieldsInfo"
112                         :key="index"
113                         :label="item.title"
114                         :value="item.field"
115                       />
116                     </el-select>
117                   </div>
118                   <div class="mr-2">
119                     <el-select v-model="rule.opCode" style="width: 100px">
120                       <el-option
121                         v-for="item in COMPARISON_OPERATORS"
122                         :key="item.value"
123                         :label="item.label"
124                         :value="item.value"
125                       />
126                     </el-select>
127                   </div>
128                   <div class="mr-2">
129                     <el-input v-model="rule.rightSide" style="width: 160px" />
130                   </div>
131                   <div class="mr-1 flex items-center" v-if="condition.rules.length > 1">
132                     <Icon
133                       icon="ep:delete"
134                       :size="18"
135                       @click="deleteConditionRule(condition, rIdx)"
136                     />
137                   </div>
138                   <div class="flex items-center">
139                     <Icon icon="ep:plus" :size="18" @click="addConditionRule(condition, rIdx)" />
140                   </div>
141                 </div>
142               </el-card>
143             </el-space>
144             <div title="添加条件组" class="mt-4 cursor-pointer">
145               <Icon color="#0089ff" icon="ep:plus" :size="24" @click="addConditionGroup" />
146             </div>
147           </el-form-item>
148         </el-form>
149       </div>
150     </div>
151     <template #footer>
152       <el-divider />
153       <div>
154         <el-button type="primary" @click="saveConfig">确 定</el-button>
155         <el-button @click="closeDrawer">取 消</el-button>
156       </div>
157     </template>
158   </el-drawer>
159 </template>
160 <script setup lang="ts">
161 import {
162   SimpleFlowNode,
163   CONDITION_CONFIG_TYPES,
164   ConditionType,
165   COMPARISON_OPERATORS,
166   ConditionGroup,
167   Condition,
168   ConditionRule
169 } from '../consts'
170 import { getDefaultConditionNodeName } from '../utils'
171 import { useFormFields } from '../node'
172 const message = useMessage() // 消息弹窗
173 defineOptions({
174   name: 'ConditionNodeConfig'
175 })
176 const formType = inject<Ref<number>>('formType') // 表单类型
177 const conditionConfigTypes = computed(() => {
178   return CONDITION_CONFIG_TYPES.filter((item) => {
179     // 业务表单暂时去掉条件规则选项
180     if (formType?.value !== 10) {
181       return item.value === ConditionType.RULE
182     } else {
183       return true
184     }
185   })
186 })
187
188 const props = defineProps({
189   conditionNode: {
190     type: Object as () => SimpleFlowNode,
191     required: true
192   },
193   nodeIndex: {
194     type: Number,
195     required: true
196   }
197 })
198 const settingVisible = ref(false)
199 const open = () => {
200   if (currentNode.value.conditionType === ConditionType.RULE) {
201     if (currentNode.value.conditionGroups) {
202       conditionGroups.value = currentNode.value.conditionGroups
203     }
204   }
205   settingVisible.value = true
206 }
207
208 watch(
209   () => props.conditionNode,
210   (newValue) => {
211     currentNode.value = newValue
212   }
213 )
214 // 显示名称输入框
215 const showInput = ref(false)
216
217 const clickIcon = () => {
218   showInput.value = true
219 }
220 // 输入框失去焦点
221 const blurEvent = () => {
222   showInput.value = false
223   currentNode.value.name =
224     currentNode.value.name ||
225     getDefaultConditionNodeName(props.nodeIndex, currentNode.value?.defaultFlow)
226 }
227
228 const currentNode = ref<SimpleFlowNode>(props.conditionNode)
229
230 defineExpose({ open }) // 提供 open 方法,用于打开弹窗
231
232 // 关闭
233 const closeDrawer = () => {
234   settingVisible.value = false
235 }
236
237 const handleClose = async (done: (cancel?: boolean) => void) => {
238   const isSuccess = await saveConfig()
239   if (!isSuccess) {
240     done(true) // 传入 true 阻止关闭
241   } else {
242     done()
243   }
244 }
245 // 表单校验规则
246 const formRules = reactive({
247   conditionType: [{ required: true, message: '配置方式不能为空', trigger: 'blur' }],
248   conditionExpression: [{ required: true, message: '条件表达式不能为空', trigger: 'blur' }]
249 })
250 const formRef = ref() // 表单 Ref
251
252 // 保存配置
253 const saveConfig = async () => {
254   if (!currentNode.value.defaultFlow) {
255     // 校验表单
256     if (!formRef) return false
257     const valid = await formRef.value.validate()
258     if (!valid) return false
259     const showText = getShowText()
260     if (!showText) {
261       return false
262     }
263     currentNode.value.showText = showText
264     if (currentNode.value.conditionType === ConditionType.EXPRESSION) {
265       currentNode.value.conditionGroups = undefined
266     }
267     if (currentNode.value.conditionType === ConditionType.RULE) {
268       currentNode.value.conditionExpression = undefined
269       currentNode.value.conditionGroups = conditionGroups.value
270     }
271   }
272   settingVisible.value = false
273   return true
274 }
275 const getShowText = (): string => {
276   let showText = ''
277   if (currentNode.value.conditionType === ConditionType.EXPRESSION) {
278     if (currentNode.value.conditionExpression) {
279       showText = `表达式:${currentNode.value.conditionExpression}`
280     }
281   }
282   if (currentNode.value.conditionType === ConditionType.RULE) {
283     // 条件组是否为与关系
284     const groupAnd = conditionGroups.value.and
285     let warningMesg: undefined | string = undefined
286     const conditionGroup = conditionGroups.value.conditions.map((item) => {
287       return (
288         '(' +
289         item.rules
290           .map((rule) => {
291             if (rule.leftSide && rule.rightSide) {
292               return (
293                 getFieldTitle(rule.leftSide) + ' ' + getOpName(rule.opCode) + ' ' + rule.rightSide
294               )
295             } else {
296               // 有一条规则不完善。提示错误
297               warningMesg = '请完善条件规则'
298               return ''
299             }
300           })
301           .join(item.and ? ' 且 ' : ' 或 ') +
302         ' ) '
303       )
304     })
305     if (warningMesg) {
306       message.warning(warningMesg)
307       showText = ''
308     } else {
309       showText = conditionGroup.join(groupAnd ? ' 且 ' : ' 或 ')
310     }
311   }
312   return showText
313 }
314
315 // 改变条件配置方式
316 const changeConditionType = () => {}
317
318 const conditionGroups = ref<ConditionGroup>({
319   and: true,
320   conditions: [
321     {
322       and: true,
323       rules: [
324         {
325           type: 1,
326           opName: '等于',
327           opCode: '==',
328           leftSide: '',
329           rightSide: ''
330         }
331       ]
332     }
333   ]
334 })
335 // 添加条件组
336 const addConditionGroup = () => {
337   const condition = {
338     and: true,
339     rules: [
340       {
341         type: 1,
342         opName: '等于',
343         opCode: '==',
344         leftSide: '',
345         rightSide: ''
346       }
347     ]
348   }
349   conditionGroups.value.conditions.push(condition)
350 }
351 // 删除条件组
352 const deleteConditionGroup = (idx: number) => {
353   conditionGroups.value.conditions.splice(idx, 1)
354 }
355
356 // 添加条件规则
357 const addConditionRule = (condition: Condition, idx: number) => {
358   const rule: ConditionRule = {
359     type: 1,
360     opName: '等于',
361     opCode: '==',
362     leftSide: '',
363     rightSide: ''
364   }
365   condition.rules.splice(idx + 1, 0, rule)
366 }
367
368 const deleteConditionRule = (condition: Condition, idx: number) => {
369   condition.rules.splice(idx, 1)
370 }
371
372 const fieldsInfo = useFormFields()
373
374 const getFieldTitle = (field: string) => {
375   const item = fieldsInfo.find((item) => item.field === field)
376   return item?.title
377 }
378
379 const getOpName = (opCode: string): string => {
380   const opName = COMPARISON_OPERATORS.find((item) => item.value === opCode)
381   return opName?.label
382 }
383 </script>
384
385 <style lang="scss" scoped>
386 .condition-group-tool {
387   display: flex;
388   justify-content: space-between;
389   width: 500px;
390   margin-bottom: 20px;
391 }
392
393 .condition-group {
394   position: relative;
395
396   &:hover {
397     border-color: #0089ff;
398
399     .condition-group-delete {
400       opacity: 1;
401     }
402   }
403
404   .condition-group-delete {
405     position: absolute;
406     top: 0;
407     left: 0;
408     display: flex;
409     cursor: pointer;
410     opacity: 0;
411   }
412 }
413
414 ::v-deep(.el-card__header) {
415   padding: 8px var(--el-card-padding);
416   border-bottom: 1px solid var(--el-card-border-color);
417   box-sizing: border-box;
418 }
419 </style>