Pārlūkot izejas kodu

仿钉钉流程设计器- 条件节点,新增条件规则配置

jason 1 gadu atpakaļ
vecāks
revīzija
6cf69f04ab

+ 71 - 2
src/components/SimpleProcessDesignerV2/src/consts.ts

@@ -47,7 +47,21 @@ export enum NodeType {
   INCLUSIVE_NODE_JOIN = 8
 }
 
-// 候选人策略 (用于审批节点。抄送节点
+// 条件配置类型 ( 用于条件节点配置 )
+export enum ConditionConfigType  {
+
+   /**
+   * 条件表达式
+   */
+  EXPRESSION = 1,
+
+   /**
+   * 条件规则
+   */
+  RULE = 2
+}
+
+// 候选人策略 ( 用于审批节点。抄送节点 )
 export enum CandidateStrategy {
   /**
    * 指定角色
@@ -96,6 +110,31 @@ export type SimpleFlowNode = {
   conditionNodes?: SimpleFlowNode[]
 }
 
+// 条件组
+export type ConditionGroup =  {
+  // 条件组的逻辑关系是否为且
+  and : boolean,
+  // 条件数组
+  conditions: Condition[]
+}
+
+// 条件
+export type Condition = {
+  // 条件规则的逻辑关系是否为且
+  and : boolean,
+  rules: ConditionRule[]
+}
+
+// 条件规则
+export type ConditionRule = {
+  type : number,
+  opName: string,
+  opCode: string,
+  leftSide: string,
+  rightSide: string
+}
+
+
 export const NODE_DEFAULT_TEXT = new Map<number,string>()
 NODE_DEFAULT_TEXT.set(NodeType.USER_TASK_NODE, '请配置审批人')
 NODE_DEFAULT_TEXT.set(NodeType.COPY_TASK_NODE, '请配置抄送人')
@@ -115,5 +154,35 @@ export const APPROVE_METHODS: DictDataVO [] = [
 ]
 
 export const CONDITION_CONFIG_TYPES: DictDataVO [] = [
-  { label: '自定义条件表达式', value: 1 }
+  { label: '条件表达式', value: 1 },
+  { label: '条件规则', value: 2 }
+]
+
+// 比较运算符
+export const COMPARISON_OPERATORS : DictDataVO = [
+  {
+    value: '==',
+    label: '等于',
+  },
+  {
+    value: '!=',
+    label: '不等于',
+  }
+  // 待完善
+  // {
+  //   value: '>',
+  //   label: '大于',
+  // },
+  // {
+  //   value: '>=',
+  //   label: '大于等于',
+  // },
+  // {
+  //   value: '<',
+  //   label: '小于',
+  // },
+  // {
+  //   value: '<=',
+  //   label: '小于等于',
+  // }
 ]

+ 292 - 30
src/components/SimpleProcessDesignerV2/src/nodes-config/ConditionNodeConfig.vue

@@ -3,8 +3,8 @@
     :append-to-body="true"
     v-model="settingVisible"
     :show-close="false"
-    :size="550"
-    :before-close="saveConfig"
+    :size="588"
+    :before-close="handleClose"
    >
    <template #header>
       <div class="config-header">
@@ -25,14 +25,18 @@
     <div> 
       <div class="mb-3 text-size-sm" v-if="currentNode.attributes.defaultFlow">其它条件不满足进入此分支(该分支不可编辑和删除)</div>
       <div v-else>
-          <el-form label-position="top">
-            <el-form-item label="条件类型" prop="conditionType">
+          <el-form
+            ref="formRef"
+            :model="currentNode.attributes"
+            :rules="formRules"
+            label-position="top">
+            <el-form-item label="配置方式" prop="conditionType">
               <el-radio-group
                 v-model="currentNode.attributes.conditionType"
                 @change="changeConditionType"
               >
                 <el-radio
-                  v-for="(dict, index) in CONDITION_CONFIG_TYPES"
+                  v-for="(dict, index) in conditionConfigTypes"
                   :key="index"
                   :value="dict.value"
                   :label="dict.value"
@@ -42,25 +46,97 @@
               </el-radio-group>
             </el-form-item>
               
-            <el-form-item
-              v-if="currentNode.attributes.conditionType === 1"
-              label="条件表达式"
-              prop="conditionExpression"
-            >
-              <el-input
-                type="textarea"
-                v-model="currentNode.attributes.conditionExpression"
-                clearable
-                style="width: 100%"
-              />
-            </el-form-item>
-            <!-- <el-form-item
-              v-if="currentNode.attributes.conditionType === 1"
+          <el-form-item
+            v-if="currentNode.attributes.conditionType === 1"
+            label="条件表达式"
+            prop="conditionExpression"
+          >
+            <el-input
+              type="textarea"
+              v-model="currentNode.attributes.conditionExpression"
+              clearable
+              style="width: 100%"
+            />
+          </el-form-item>
+           <el-form-item
+              v-if="currentNode.attributes.conditionType === 2"
               label="条件规则"
-              prop="conditionExpression"
             >
-              <span class="text-red-400">待实现</span>
-            </el-form-item> -->
+              <div class="condition-group-tool">
+                <div class="flex items-center">
+                  <div class="mr-4">条件组关系</div>
+                  <el-switch
+                      v-model="conditionGroups.and"
+                      inline-prompt
+                      active-text="且"
+                      inactive-text="或"/>
+                </div>
+                <!-- <div class="flex items-center"> 
+                  <el-button size="small" type="primary">添加条件组</el-button>
+                </div>  -->
+              </div>
+              <el-space direction="vertical"  :spacer="conditionGroups.and ? '且' : '或' ">
+                <el-card 
+                    class="condition-group"
+                    style="width: 530px"
+                    v-for="(condition, cIdx) in conditionGroups.conditions" 
+                    :key="cIdx">
+                  <div class="condition-group-delete" v-if="conditionGroups.conditions.length > 1">
+                    <Icon color="#0089ff"  icon="ep:circle-close-filled"  :size="18"  @click="deleteConditionGroup(cIdx)"/>
+                  </div>
+                  <template #header>
+                    <div class="flex items-center justify-between">
+                      <div>条件组</div>
+                      <div class="flex">
+                        <div class="mr-4">规则关系</div>
+                        <el-switch
+                            v-model="condition.and"
+                            inline-prompt
+                            active-text="且"
+                            inactive-text="或"/>
+                      </div>
+                    </div>
+                  </template>
+                 
+                  <div class="flex pt-2" v-for="(rule, rIdx) in condition.rules" :key="rIdx">
+                    <div class="mr-2">
+                      <el-select  style="width: 160px" v-model="rule.leftSide">
+                        <el-option
+                          v-for="(item, index) in fieldsInfo"
+                          :key="index"
+                          :label="item.title"
+                          :value="item.field"
+                        />
+                      </el-select>
+                   </div>
+                   <div class="mr-2">
+                      <el-select v-model="rule.opCode" style="width: 100px">
+                        <el-option
+                          v-for="item in COMPARISON_OPERATORS"
+                          :key="item.value"
+                          :label="item.label"
+                          :value="item.value"
+                        />
+                      </el-select>
+                    </div>
+                    <div class="mr-2">
+                      <el-input v-model="rule.rightSide" style="width: 160px" />
+                    </div>
+                    <div class="mr-1 flex items-center" v-if="condition.rules.length > 1">
+                      <Icon icon="ep:delete" :size="18" @click="deleteConditionRule(condition,rIdx)"/>
+                    </div>
+                    <div class="flex items-center">
+                      <Icon icon="ep:plus" :size="18" @click="addConditionRule(condition,rIdx)"/>
+                    </div>
+                  </div>
+            
+                </el-card>
+              </el-space>
+              <div title="添加条件组" class="mt-4 cursor-pointer">
+                <Icon color="#0089ff"  icon="ep:plus"  :size="24"  @click="addConditionGroup"/>
+              </div>
+            </el-form-item>
+            
           </el-form>
       </div>    
     </div>
@@ -74,11 +150,27 @@
   </el-drawer>
 </template>
 <script setup lang="ts">
-import { SimpleFlowNode, CONDITION_CONFIG_TYPES } from '../consts'
+import { SimpleFlowNode, CONDITION_CONFIG_TYPES, ConditionConfigType } from '../consts'
 import { getDefaultConditionNodeName } from '../utils';
+import { COMPARISON_OPERATORS, ConditionGroup, Condition, ConditionRule } from '../consts';
+const message = useMessage() // 消息弹窗
 defineOptions({
-  name: 'ConditionNode'
+  name: 'ConditionNodeConfig'
+})
+const formFields = inject<Ref<string[]>>('formFields')
+const formType = inject<Ref<number>>('formType') // 表单类型
+
+const conditionConfigTypes = computed( ()=> {
+  return CONDITION_CONFIG_TYPES.filter(item => {
+    // 业务表单暂时去掉条件规则 选项
+    if (formType?.value !== 10 ){
+      return item.value === 1
+    } else {
+      return true;
+    }
+  });
 })
+
 const props = defineProps({
   conditionNode: {
     type: Object as () => SimpleFlowNode,
@@ -91,6 +183,12 @@ const props = defineProps({
 })
 const settingVisible = ref(false)
 const open = () => {
+  getFieldsInfo()
+  if (currentNode.value.attributes.conditionType === ConditionConfigType.RULE) {
+     if (currentNode.value.attributes.conditionGroups) {
+        conditionGroups.value = currentNode.value.attributes.conditionGroups
+     }
+  }
   settingVisible.value = true
 }
 
@@ -117,31 +215,195 @@ defineExpose({ open }) // 提供 open 方法,用于打开弹窗
 const closeDrawer = () => {
   settingVisible.value = false
 }
+
+const handleClose = async (done: (cancel?: boolean) => void) => {
+  if( await saveConfig()){
+    done(false); // 传入 false 阻止关闭  
+  }else {
+    done();
+  }
+}
+// 表单校验规则
+const formRules = reactive({
+  conditionType: [{ required: true, message: '配置方式不能为空', trigger: 'blur' }],
+  conditionExpression: [{ required: true, message: '条件表达式不能为空', trigger: 'blur' }]
+})
+const formRef = ref() // 表单 Ref
+
 // 保存配置
-const saveConfig = () => {
+const saveConfig = async () => {
   if (!currentNode.value.attributes.defaultFlow) {
-    currentNode.value.showText = getShowText();
+      // 校验表单
+    if (!formRef) return false
+    const valid = await formRef.value.validate()
+    if (!valid) return false
+    
+    const showText = getShowText();
+    if(!showText){
+      return false;
+    }
+    currentNode.value.showText = showText
+    if(currentNode.value.attributes.conditionType === ConditionConfigType.EXPRESSION ){
+      currentNode.value.attributes.conditionGroups = undefined 
+    }
+    if(currentNode.value.attributes.conditionType === ConditionConfigType.RULE ){
+      currentNode.value.attributes.conditionExpression = undefined
+      currentNode.value.attributes.conditionGroups = conditionGroups.value 
+    }
+    
   }
   settingVisible.value = false
+  return true;
 }
 const getShowText = () : string => {
   let showText ='';
-  // if (currentNode.value.attributes.conditionType === 1) {
-  //   showText = '待实现'
-  // } 
-  if (currentNode.value.attributes.conditionType === 1) {
+  if (currentNode.value.attributes.conditionType === ConditionConfigType.EXPRESSION) {
     if (currentNode.value.attributes.conditionExpression) {
       showText = `表达式:${currentNode.value.attributes.conditionExpression}`
     }
   }
+  if (currentNode.value.attributes.conditionType === ConditionConfigType.RULE ) {
+    // 条件组是否为与关系
+    const groupAnd = conditionGroups.value.and;
+    let warningMesg : undefined | string = undefined
+    const conditionGroup = conditionGroups.value.conditions.map( item => {
+      return  '(' + item.rules.map( rule => {
+          if ( rule.leftSide  && rule.rightSide) {
+            return getFieldTitle(rule.leftSide) + " " + rule.opName + " " + rule.rightSide 
+          }else {
+            // 又一条规则不完善。提示错误
+            warningMesg = '请完善条件规则'
+            return ''
+          }
+        }).join(item.and ? ' 且 ' : ' 或 ' ) + ' ) '
+      }
+    )
+    if (warningMesg) {
+      message.warning(warningMesg);
+      showText = '';
+    } else {
+      showText = conditionGroup.join( groupAnd ?  ' 且 ' : ' 或 ' );
+    }
+   
+  } 
   return showText
 }
+
+
+
 // 改变条件配置方式
 const changeConditionType = () => {
 
 }
+
+const conditionGroups = ref<ConditionGroup>({
+  and : true,
+  conditions: [
+    {
+      and : true,
+      rules: [
+        {
+          type: 1,
+          opName: "等于",
+          opCode: "==",
+          leftSide: "",
+          rightSide: ""
+        }
+      ]
+    }
+  ]
+})
+// 添加条件组
+const addConditionGroup = () => {
+  const condition = {
+    and : true,
+    rules: [
+      {
+        type: 1,
+        opName: "等于",
+        opCode: "==",
+        leftSide: "",
+        rightSide: ""
+      }
+    ]
+  }
+  conditionGroups.value.conditions.push(condition)
+}
+// 删除条件组
+const deleteConditionGroup = (idx:number) => {
+  conditionGroups.value.conditions.splice(idx, 1)
+}
+
+// 添加条件规则
+const addConditionRule = (condition:Condition, idx:number) => {
+  const rule: ConditionRule = {
+    type: 1,
+    opName: "等于",
+    opCode: "==",
+    leftSide: "",
+    rightSide: ""
+  }
+  condition.rules.splice(idx+1, 0, rule)
+}
+
+const deleteConditionRule = (condition:Condition, idx:number) => {
+  condition.rules.splice(idx, 1)
+}
+
+const fieldsInfo : any[] = [];
+
+const getFieldsInfo = () => {
+  if(formFields){
+    formFields.value.forEach((fieldStr: string) => {
+      const { field, title, type } = JSON.parse(fieldStr)
+      fieldsInfo.push({
+        field,
+        title,
+        type
+      })
+    })
+  }
+}
+const getFieldTitle = (field:string) : string => {
+  const item = fieldsInfo.find( item => item.field === field)
+  return item?.title;
+}
+
 </script>
 
 <style lang="scss" scoped>
 
+.condition-group-tool {
+  display: flex;
+  justify-content: space-between;
+  width: 500px;
+  margin-bottom: 20px;
+}
+
+.condition-group {
+  position: relative;
+
+  &:hover {
+    border-color: #0089ff;
+
+    .condition-group-delete {
+      opacity: 1;
+    }
+  }
+
+  .condition-group-delete {
+    position: absolute;
+    top: 0;
+    left: 0;
+    display: flex;
+    cursor: pointer;
+    opacity: 0;
+  }
+}
+
+::v-deep(.el-card__header) {
+  padding: 8px var(--el-card-padding);
+  border-bottom: 1px solid var(--el-card-border-color);
+  box-sizing: border-box;
+}
 </style>