|
@@ -0,0 +1,450 @@
|
|
|
+<!--路由分支-->
|
|
|
+<template>
|
|
|
+ <div class="node-wrap">
|
|
|
+ <div class="node-wrap-box" :class="[disabled ? 'node-wrap-box--disabled' : '', `node-wrap-box--${nodeConfig.local_status}`]">
|
|
|
+ <div class="title bg-red-600">
|
|
|
+ <el-icon class="icon"><SvgIcon icon-class="route" /></el-icon>
|
|
|
+ <span v-show="!isEditTitle" class="title_label" @click="editTitle('box_nodeTitle')"
|
|
|
+ >{{ nodeConfig.nodeName }}<el-icon v-if="!disabled" class="edit-icon"><edit /></el-icon
|
|
|
+ ></span>
|
|
|
+ <el-input
|
|
|
+ v-show="isEditTitle"
|
|
|
+ ref="box_nodeTitle"
|
|
|
+ v-model="nodeConfig.nodeName"
|
|
|
+ clearable
|
|
|
+ size="small"
|
|
|
+ @blur="saveTitle"
|
|
|
+ @keyup.enter="saveTitle"
|
|
|
+ ></el-input>
|
|
|
+ <el-icon v-if="!disabled" class="close" @click.stop="delNode()"><close /></el-icon>
|
|
|
+ </div>
|
|
|
+ <div class="content" @click="show">
|
|
|
+ <div v-html="toText(nodeConfig)" />
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <add-node v-model="nodeConfig.childNode" :disabled="disabled"></add-node>
|
|
|
+ <el-drawer v-model="drawer" title="路由分支设置" destroy-on-close append-to-body :size="600" class="aDrawer">
|
|
|
+ <template #header>
|
|
|
+ <div class="node-wrap-drawer__title">
|
|
|
+ <label v-show="!isEditTitle" @click="editTitle('nodeTitle')"
|
|
|
+ >{{ form.nodeName }}<el-icon class="node-wrap-drawer__title-edit"><edit /></el-icon
|
|
|
+ ></label>
|
|
|
+ <el-input
|
|
|
+ v-show="isEditTitle"
|
|
|
+ ref="nodeTitle"
|
|
|
+ v-model="form.nodeName"
|
|
|
+ clearable
|
|
|
+ class="w-40"
|
|
|
+ @blur="saveTitle"
|
|
|
+ @keyup.enter="saveTitle"
|
|
|
+ ></el-input>
|
|
|
+ <el-input v-model="form.nodeKey" clearable class="w-40 pl-1.5" placeholder="请填写nodeKey"></el-input>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ <el-container>
|
|
|
+ <el-main>
|
|
|
+ <el-form ref="ruleFormRef" label-position="top" :model="form">
|
|
|
+ <el-card v-for="(route, index) of form.routeNodes" :key="route.nodeKey" shadow="hover" class="mb-[10px]">
|
|
|
+ <template #header>
|
|
|
+ <div class="flex justify-between route-header">
|
|
|
+ <label
|
|
|
+ v-show="!route.local_isEdit"
|
|
|
+ class="inline-flex items-center cursor-pointer text-[14px]"
|
|
|
+ @click="editRouteTitle(route, index)"
|
|
|
+ >{{ route.nodeName }}<el-icon class="le-link ml-2"><edit /></el-icon
|
|
|
+ ></label>
|
|
|
+ <el-input
|
|
|
+ v-show="route.local_isEdit"
|
|
|
+ ref="routeNodeName"
|
|
|
+ v-model="route.nodeName"
|
|
|
+ size="small"
|
|
|
+ clearable
|
|
|
+ class="w-40"
|
|
|
+ @blur="saveRouteTitle(route)"
|
|
|
+ @keyup.enter="saveRouteTitle(route)"
|
|
|
+ ></el-input>
|
|
|
+ <el-form-item
|
|
|
+ style="margin-bottom: 0"
|
|
|
+ class="ml-2"
|
|
|
+ :prop="`routeNodes.${index}.nodeKey`"
|
|
|
+ :rules="[{ required: true, message: '请选择流程节点' }]"
|
|
|
+ >
|
|
|
+ <el-select v-model="route.nodeKey" size="small" placeholder="请选择流程节点">
|
|
|
+ <el-option v-for="item in validNodeList" :key="item.nodeKey" :label="item.nodeName" :value="item.nodeKey">
|
|
|
+ <div class="w-[230px] flex justify-between -ml-[8px] -mr-[20px]">
|
|
|
+ <le-text :value="item.nodeName || '-'"></le-text>
|
|
|
+ <span class="text-gray-400">{{ item.nodeKey }}</span>
|
|
|
+ </div>
|
|
|
+ </el-option>
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ <el-button class="ml-auto" link type="danger" icon="Delete" @click="removeRouteNode(index)" />
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ <template v-for="(conditionGroup, conditionGroupIdx) in route.conditionList" :key="conditionGroupIdx">
|
|
|
+ <div v-if="conditionGroupIdx === 0" class="tip">满足以下条件时进入当前分支</div>
|
|
|
+ <div v-else class="tip mt-[10px]">或满足</div>
|
|
|
+ <div class="condition-group-editor">
|
|
|
+ <div class="header">
|
|
|
+ <span>条件组 {{ conditionGroupIdx + 1 }}</span>
|
|
|
+ <div @click="deleteConditionGroup(route, conditionGroupIdx)">
|
|
|
+ <el-icon class="branch-delete-icon"><Delete /></el-icon>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="main-content">
|
|
|
+ <!-- 单个条件 -->
|
|
|
+ <div class="condition-content-box cell-box">
|
|
|
+ <div v-if="false">描述</div>
|
|
|
+ <div>条件字段</div>
|
|
|
+ <div>运算符</div>
|
|
|
+ <div>值</div>
|
|
|
+ </div>
|
|
|
+ <div v-for="(condition, idx) in conditionGroup" :key="idx" class="condition-content">
|
|
|
+ <div class="condition-relation">
|
|
|
+ <span>{{ idx === 0 ? '当' : '且' }}</span>
|
|
|
+ <div v-if="conditionGroup.length > 1" @click="deleteConditionList(conditionGroup, idx)">
|
|
|
+ <el-icon class="branch-delete-icon"><Delete /></el-icon>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="condition-content">
|
|
|
+ <div class="condition-content-box">
|
|
|
+ <el-input
|
|
|
+ v-if="condition.type === 'custom'"
|
|
|
+ v-model="condition.label"
|
|
|
+ placeholder="自定义条件字段"
|
|
|
+ @input="getCurrentItemField(conditionGroup, idx)"
|
|
|
+ />
|
|
|
+ <el-select
|
|
|
+ v-if="condition.type === 'form'"
|
|
|
+ v-model="condition.field"
|
|
|
+ filterable
|
|
|
+ placeholder="表单条件字段"
|
|
|
+ @change="getCurrentItemLabel(conditionGroup, idx)"
|
|
|
+ >
|
|
|
+ <el-option v-for="{ id, label, key } in expressionFormList" :key="id" :label="label" :value="key" />
|
|
|
+ </el-select>
|
|
|
+ <el-select v-model="condition.operator" placeholder="请选择表达式">
|
|
|
+ <el-option v-for="{ value, label } in operatorType" :key="label" :label="label" :value="value"></el-option>
|
|
|
+ </el-select>
|
|
|
+ <el-input v-model="condition.value" placeholder="值" />
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="sub-content">
|
|
|
+ <el-button link type="primary" icon="Plus" @click="addConditionList(conditionGroup, 'custom')"> 添加自定义条件 </el-button>
|
|
|
+ <el-button
|
|
|
+ v-if="expressionFormList.length"
|
|
|
+ link
|
|
|
+ type="primary"
|
|
|
+ icon="Plus"
|
|
|
+ style="margin-left: 8px"
|
|
|
+ @click="addConditionList(conditionGroup, 'form')"
|
|
|
+ >
|
|
|
+ 添加表单条件
|
|
|
+ </el-button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+
|
|
|
+ <el-button style="width: 100%" type="info" icon="Plus" text bg @click="addConditionGroup(route)"> 添加条件组 </el-button>
|
|
|
+ </el-card>
|
|
|
+ <!-- <el-form-item label="选择要抄送的人员">
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="">
|
|
|
+ <el-checkbox v-model="form.allowSelection" label="允许发起人自选抄送人"></el-checkbox>
|
|
|
+ </el-form-item>-->
|
|
|
+ <el-button style="width: 100%" type="primary" icon="Plus" @click="addRouteNode"> 添加路由分支 </el-button>
|
|
|
+ </el-form>
|
|
|
+ </el-main>
|
|
|
+ <el-footer>
|
|
|
+ <el-button type="primary" @click="save">保存</el-button>
|
|
|
+ <el-button @click="drawer = false">取消</el-button>
|
|
|
+ </el-footer>
|
|
|
+ </el-container>
|
|
|
+ </el-drawer>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script lang="jsx">
|
|
|
+import addNode from './addNode'
|
|
|
+import { operatorType } from './config'
|
|
|
+import { ElMessage } from 'element-plus'
|
|
|
+import { mapState } from 'pinia'
|
|
|
+import useFlowStore from '@/store/modules/flow'
|
|
|
+import { getNodeKey } from '@/utils/workflow'
|
|
|
+import { $log } from '@/utils'
|
|
|
+
|
|
|
+export default {
|
|
|
+ components: {
|
|
|
+ addNode
|
|
|
+ },
|
|
|
+ inject: ['getRootConfig'],
|
|
|
+ props: {
|
|
|
+ modelValue: { type: Object, default: () => {} },
|
|
|
+ disabled: {
|
|
|
+ type: Boolean,
|
|
|
+ default: false
|
|
|
+ }
|
|
|
+ },
|
|
|
+ data() {
|
|
|
+ return {
|
|
|
+ operatorType: JSON.parse(JSON.stringify(operatorType)),
|
|
|
+ nodeConfig: {},
|
|
|
+ drawer: false,
|
|
|
+ isEditTitle: false,
|
|
|
+ form: {},
|
|
|
+ expressionFormList: [],
|
|
|
+ validNodeList: []
|
|
|
+ }
|
|
|
+ },
|
|
|
+ computed: {
|
|
|
+ ...mapState(useFlowStore, ['processForm']) //映射函数,取出processForm
|
|
|
+ },
|
|
|
+ watch: {
|
|
|
+ modelValue() {
|
|
|
+ this.nodeConfig = this.modelValue
|
|
|
+ },
|
|
|
+ processForm: {
|
|
|
+ handler(processForm) {
|
|
|
+ const { formStructure } = JSON.parse(processForm) // 表单设计字段
|
|
|
+ this.local_formStructure = formStructure
|
|
|
+ // 使用 filter 方法找到 required 属性为 true 的对象
|
|
|
+ this.expressionFormList = (formStructure?.fields || []).filter(item => item.options && item.options.required === true)
|
|
|
+ $log(this.expressionFormList, '这里打印出符合条件的表单对象')
|
|
|
+ },
|
|
|
+ immediate: true
|
|
|
+ }
|
|
|
+ },
|
|
|
+ mounted() {
|
|
|
+ this.nodeConfig = this.modelValue
|
|
|
+ },
|
|
|
+ methods: {
|
|
|
+ queryValidNodeList() {
|
|
|
+ const validNodeList = []
|
|
|
+ const fn = data => {
|
|
|
+ if (!data) return
|
|
|
+ // validNodeList.push({ nodeKey: data.nodeKey, nodeName: data.nodeName, type: data.type })
|
|
|
+ validNodeList.push(data)
|
|
|
+ if (data.conditionNodes && Array.isArray(data.conditionNodes)) {
|
|
|
+ // 条件分支节点
|
|
|
+ data.conditionNodes.forEach(v => {
|
|
|
+ fn(v.childNode)
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+ if (data.childNode) {
|
|
|
+ // 正常子节点
|
|
|
+ fn(data.childNode)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ const rootConfig = this.getRootConfig()
|
|
|
+ fn(rootConfig)
|
|
|
+ // 只允许选择审核人
|
|
|
+ this.validNodeList = validNodeList.filter(v => v.type === 1)
|
|
|
+ },
|
|
|
+ show() {
|
|
|
+ if (this.disabled) return
|
|
|
+ this.form = {}
|
|
|
+ this.form = JSON.parse(JSON.stringify(this.nodeConfig))
|
|
|
+ this.queryValidNodeList()
|
|
|
+ this.drawer = true
|
|
|
+ },
|
|
|
+ editTitle(refName) {
|
|
|
+ if (this.disabled) return
|
|
|
+ this.isEditTitle = true
|
|
|
+ this.$nextTick(() => {
|
|
|
+ if (this.$refs[refName]) {
|
|
|
+ this.$refs[refName].focus()
|
|
|
+ }
|
|
|
+ })
|
|
|
+ },
|
|
|
+ saveTitle() {
|
|
|
+ this.isEditTitle = false
|
|
|
+ },
|
|
|
+ save() {
|
|
|
+ if (!this.form.nodeKey) {
|
|
|
+ return ElMessage.error('请填写nodeKey')
|
|
|
+ }
|
|
|
+ this.$refs.ruleFormRef.validate(valid => {
|
|
|
+ if (valid) {
|
|
|
+ this.$emit('update:modelValue', this.form)
|
|
|
+ this.drawer = false
|
|
|
+ }
|
|
|
+ })
|
|
|
+ },
|
|
|
+ delNode() {
|
|
|
+ this.$emit('update:modelValue', this.nodeConfig.childNode)
|
|
|
+ },
|
|
|
+ addRouteNode() {
|
|
|
+ const len = this.form.routeNodes.length + 1
|
|
|
+ this.form.routeNodes.push({
|
|
|
+ nodeName: `路由${len}`,
|
|
|
+ nodeKey: undefined, // 必填
|
|
|
+ // type: 3, // ==> type: 23
|
|
|
+ type: 23,
|
|
|
+ priorityLevel: len,
|
|
|
+ conditionMode: 1,
|
|
|
+ conditionList: []
|
|
|
+ })
|
|
|
+ },
|
|
|
+ removeRouteNode(index) {
|
|
|
+ this.form.routeNodes.splice(index, 1)
|
|
|
+ },
|
|
|
+ editRouteTitle(route, index) {
|
|
|
+ if (this.disabled) return
|
|
|
+ route.local_isEdit = true
|
|
|
+ this.$nextTick(() => {
|
|
|
+ const curRef = this.$refs.routeNodeName[index]
|
|
|
+ if (curRef) {
|
|
|
+ curRef.focus()
|
|
|
+ }
|
|
|
+ })
|
|
|
+ },
|
|
|
+ saveRouteTitle(route) {
|
|
|
+ route.local_isEdit = false
|
|
|
+ },
|
|
|
+ addConditionList(conditionList, type) {
|
|
|
+ conditionList.push({
|
|
|
+ label: '',
|
|
|
+ field: '',
|
|
|
+ operator: '==',
|
|
|
+ value: '',
|
|
|
+ type
|
|
|
+ })
|
|
|
+ },
|
|
|
+ addConditionGroup(route) {
|
|
|
+ this.addConditionList(route.conditionList[route.conditionList.push([]) - 1], 'custom')
|
|
|
+ },
|
|
|
+ deleteConditionGroup(route, index) {
|
|
|
+ route.conditionList.splice(index, 1)
|
|
|
+ },
|
|
|
+ deleteConditionList(conditionList, index) {
|
|
|
+ conditionList.splice(index, 1)
|
|
|
+ },
|
|
|
+ getCurrentItemLabel(conditionGroup, idx) {
|
|
|
+ // const currentCondition = this.form.conditionList[conditionIdx]
|
|
|
+ // const field = currentCondition[idx].field
|
|
|
+ const field = conditionGroup[idx].field
|
|
|
+ const labelObj = this.expressionFormList.find(i => i.key === field)
|
|
|
+ conditionGroup[idx].showLabel = labelObj.label
|
|
|
+ conditionGroup[idx].label = field
|
|
|
+ },
|
|
|
+ getCurrentItemField(conditionGroup, idx) {
|
|
|
+ // const currentCondition = this.form.conditionList[conditionIdx]
|
|
|
+ conditionGroup[idx].field = conditionGroup[idx].label
|
|
|
+ },
|
|
|
+ toText(nodeConfig) {
|
|
|
+ const routeNodes = nodeConfig.routeNodes || []
|
|
|
+ if (routeNodes.length > 0) {
|
|
|
+ return `${routeNodes.length}条动态路由`
|
|
|
+ // return routeNodes.map(v => `路由至(${v.nodeName})`).join(',')
|
|
|
+ }
|
|
|
+ return '<span class="placeholder">请设置路由节点</span>'
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+</script>
|
|
|
+
|
|
|
+<style scoped lang="scss">
|
|
|
+.tip {
|
|
|
+ color: #646a73;
|
|
|
+ margin-bottom: 10px;
|
|
|
+}
|
|
|
+.condition-group-editor {
|
|
|
+ user-select: none;
|
|
|
+ border-radius: 4px;
|
|
|
+ border: 1px solid #e4e5e7;
|
|
|
+ position: relative;
|
|
|
+ margin-bottom: 10px;
|
|
|
+
|
|
|
+ .branch-delete-icon {
|
|
|
+ font-size: 18px;
|
|
|
+ cursor: pointer;
|
|
|
+ &:hover {
|
|
|
+ color: var(--el-color-danger);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .header {
|
|
|
+ background-color: #f4f6f8;
|
|
|
+ padding: 0 12px;
|
|
|
+ font-size: 14px;
|
|
|
+ color: #171e31;
|
|
|
+ height: 36px;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+
|
|
|
+ span {
|
|
|
+ flex: 1;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .main-content {
|
|
|
+ padding: 0 12px;
|
|
|
+
|
|
|
+ .condition-relation {
|
|
|
+ color: #9ca2a9;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ height: 36px;
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-between;
|
|
|
+ padding: 0 2px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .condition-content-box {
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-between;
|
|
|
+ align-items: center;
|
|
|
+
|
|
|
+ div {
|
|
|
+ width: 100%;
|
|
|
+ min-width: 120px;
|
|
|
+ }
|
|
|
+
|
|
|
+ div:not(:first-child) {
|
|
|
+ margin-left: 16px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .cell-box {
|
|
|
+ div {
|
|
|
+ padding: 16px 0;
|
|
|
+ width: 100%;
|
|
|
+ min-width: 120px;
|
|
|
+ color: #909399;
|
|
|
+ font-size: 14px;
|
|
|
+ font-weight: 600;
|
|
|
+ text-align: center;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .condition-content {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+
|
|
|
+ :deep(.el-input__wrapper) {
|
|
|
+ border-top-left-radius: 0;
|
|
|
+ border-bottom-left-radius: 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ .content {
|
|
|
+ flex: 1;
|
|
|
+ padding: 0 0 4px 0;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ min-height: 31.6px;
|
|
|
+ flex-wrap: wrap;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .sub-content {
|
|
|
+ padding: 12px;
|
|
|
+ }
|
|
|
+}
|
|
|
+.route-header {
|
|
|
+}
|
|
|
+</style>
|