|
@@ -0,0 +1,445 @@
|
|
|
+<template>
|
|
|
+ <!-- `branch-wrap--${nodeConfig.local_status}`-->
|
|
|
+ <div class="branch-wrap">
|
|
|
+ <div class="branch-box-wrap">
|
|
|
+ <div class="branch-box" style="margin-bottom: 15px">
|
|
|
+ <el-button class="add-branch" color="#345da2" plain round @click="addTerm"> 添加条件 </el-button>
|
|
|
+ <div v-for="(item, index) in nodeConfig.parallelNodes" :key="index" class="col-box">
|
|
|
+ <div class="condition-node">
|
|
|
+ <div class="condition-node-box">
|
|
|
+ <div :class="['auto-judge', `auto-judge--${item.local_status}`]" @click="show(index)">
|
|
|
+ <!-- 不是第一个 也不是 最后一个-->
|
|
|
+ <div v-if="index != 0 && index != nodeConfig.parallelNodes.length - 1" class="sort-left" @click.stop="arrTransfer(index, -1)">
|
|
|
+ <el-icon><ArrowLeft /></el-icon>
|
|
|
+ </div>
|
|
|
+ <div class="title">
|
|
|
+ <span style="display: inline-block; width: 115px"
|
|
|
+ ><le-text class="flex-1" tag="b" :value="index === nodeConfig.parallelNodes.length - 1 ? '默认条件' : item.nodeName"
|
|
|
+ /></span>
|
|
|
+ <div class="close" v-if="index != nodeConfig.parallelNodes.length - 1">
|
|
|
+ <el-icon class="mx-1" @click.stop="copyTerm(index)">
|
|
|
+ <CopyDocument />
|
|
|
+ </el-icon>
|
|
|
+ <el-icon @click.stop="delTerm(index)">
|
|
|
+ <Close />
|
|
|
+ </el-icon>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ </div>
|
|
|
+ <div
|
|
|
+ class="content"
|
|
|
+ :class="[index === nodeConfig.parallelNodes.length - 1 ? 'last-child-title' : '']"
|
|
|
+ style="width: 200px; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; -o-text-overflow: ellipsis"
|
|
|
+ >
|
|
|
+ <span v-if="toText(nodeConfig, index)" :title="toText(nodeConfig, index)">{{ toText(nodeConfig, index) }}</span>
|
|
|
+ <span v-else class="placeholder"> 请设置条件 </span>
|
|
|
+ </div>
|
|
|
+ <!-- 最后一个没有,长度大于2倒数第二个没有 -->
|
|
|
+ <div
|
|
|
+ v-if="
|
|
|
+ !(nodeConfig.parallelNodes.length == 2 && (index === nodeConfig.parallelNodes.length - 1 || index === 0)) &&
|
|
|
+ !(
|
|
|
+ (nodeConfig.parallelNodes.length > 2 && index === nodeConfig.parallelNodes.length - 1) ||
|
|
|
+ index === nodeConfig.parallelNodes.length - 2
|
|
|
+ )
|
|
|
+ "
|
|
|
+ class="sort-right"
|
|
|
+ @click.stop="arrTransfer(index)"
|
|
|
+ >
|
|
|
+ <el-icon><ArrowRight /></el-icon>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <add-node v-model="item.childNode" :disabled="disabled"></add-node>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <slot v-if="item.childNode" :node="item"></slot>
|
|
|
+ <div v-if="index == 0" class="top-left-cover-line"></div>
|
|
|
+ <div v-if="index == 0" class="bottom-left-cover-line"></div>
|
|
|
+ <div v-if="index == nodeConfig.parallelNodes.length - 1" class="top-right-cover-line"></div>
|
|
|
+ <div v-if="index == nodeConfig.parallelNodes.length - 1" class="bottom-right-cover-line"></div>
|
|
|
+ </div>
|
|
|
+ <div class="svg-icon-box">
|
|
|
+ <svg-icon style="font-size: 18px" color="#345da2" icon-class="flow-merge" />
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <add-node v-model="nodeConfig.childNode" :disabled="disabled"></add-node>
|
|
|
+ </div>
|
|
|
+ <el-drawer v-model="drawer" title="条件设置" destroy-on-close append-to-body :size="600">
|
|
|
+ <template #header>
|
|
|
+ <div class="node-wrap-drawer__title">
|
|
|
+ <label v-if="!isEditTitle" @click="editTitle">
|
|
|
+ {{ form.nodeName }}<el-icon class="node-wrap-drawer__title-edit"><Edit /></el-icon>
|
|
|
+ <!-- <div @click="rmConditionGroup(conditionGroup)">-->
|
|
|
+ <!-- -->
|
|
|
+ <!-- </div>-->
|
|
|
+ </label>
|
|
|
+ <el-input v-if="isEditTitle" ref="nodeTitle" v-model="form.nodeName" clearable @blur="saveTitle" @keyup.enter="saveTitle"></el-input>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ <el-container>
|
|
|
+ <el-main style="padding: 0 0 20px 0">
|
|
|
+ <div class="top-tips">满足以下条件时进入当前分支</div>
|
|
|
+ <template v-for="(conditionGroup, conditionGroupIdx) in form.conditionList" :key="conditionGroupIdx">
|
|
|
+ <div v-if="conditionGroupIdx != 0" class="or-branch-link-tip">或满足</div>
|
|
|
+ <div class="condition-group-editor">
|
|
|
+ <div class="header">
|
|
|
+ <span>条件组 {{ conditionGroupIdx + 1 }}</span>
|
|
|
+ <div @click="deleteConditionGroup(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(conditionGroupIdx, idx)"
|
|
|
+ />
|
|
|
+ <el-select
|
|
|
+ v-if="condition.type === 'form'"
|
|
|
+ v-model="condition.field"
|
|
|
+ filterable
|
|
|
+ placeholder="表单条件字段"
|
|
|
+ @change="getCurrentItemLabel(conditionGroupIdx, 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"> 添加条件组 </el-button>
|
|
|
+ </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>
|
|
|
+import addNode from './addNode.vue'
|
|
|
+import { operatorType } from './config'
|
|
|
+import useFlowStore from '@/store/modules/flow'
|
|
|
+import { Delete, Plus, ArrowLeft, Close, ArrowRight, Edit } from '@element-plus/icons-vue'
|
|
|
+import { mapState } from 'pinia'
|
|
|
+import { getNodeKey } from '@/utils/workflow'
|
|
|
+
|
|
|
+export default {
|
|
|
+ components: {
|
|
|
+ addNode
|
|
|
+ },
|
|
|
+ props: {
|
|
|
+ modelValue: { type: Object, default: () => {} },
|
|
|
+ disabled: {
|
|
|
+ type: Boolean,
|
|
|
+ default: false
|
|
|
+ }
|
|
|
+ },
|
|
|
+ data() {
|
|
|
+ return {
|
|
|
+ operatorType,
|
|
|
+ nodeConfig: {},
|
|
|
+ drawer: false,
|
|
|
+ isEditTitle: false,
|
|
|
+ index: 0,
|
|
|
+ form: {},
|
|
|
+ expressionFormList: []
|
|
|
+ }
|
|
|
+ },
|
|
|
+ computed: {
|
|
|
+ Plus() {
|
|
|
+ return Plus
|
|
|
+ },
|
|
|
+ ...mapState(useFlowStore, ['processForm']) //映射函数,取出processForm
|
|
|
+ },
|
|
|
+ watch: {
|
|
|
+ modelValue() {
|
|
|
+ this.nodeConfig = this.modelValue
|
|
|
+ }
|
|
|
+ },
|
|
|
+ mounted() {
|
|
|
+ this.nodeConfig = this.modelValue
|
|
|
+ },
|
|
|
+ methods: {
|
|
|
+ show(index) {
|
|
|
+ if (this.disabled) return
|
|
|
+ if (index === this.nodeConfig.parallelNodes.length - 1) {
|
|
|
+ // 最后一个节点不能编辑
|
|
|
+ return
|
|
|
+ }
|
|
|
+ this.index = index
|
|
|
+ this.form = {}
|
|
|
+ this.form = JSON.parse(JSON.stringify(this.nodeConfig.parallelNodes[index]))
|
|
|
+ const { formStructure } = JSON.parse(this.processForm) // 表单设计字段
|
|
|
+ // 使用 filter 方法找到 required 属性为 true 的对象
|
|
|
+ const requiredList = (formStructure?.fields || []).filter(item => item.options && item.options.required === true)
|
|
|
+ console.log(`%c 这里打印出符合条件的表单对象-=== ${JSON.stringify(requiredList)}`, 'background: orange; color: #fff')
|
|
|
+ this.expressionFormList = requiredList || []
|
|
|
+ this.drawer = true
|
|
|
+ },
|
|
|
+ editTitle() {
|
|
|
+ this.isEditTitle = true
|
|
|
+ this.$nextTick(() => {
|
|
|
+ this.$refs.nodeTitle.focus()
|
|
|
+ })
|
|
|
+ },
|
|
|
+ saveTitle() {
|
|
|
+ this.isEditTitle = false
|
|
|
+ },
|
|
|
+ save() {
|
|
|
+ this.nodeConfig.parallelNodes[this.index] = this.form
|
|
|
+ this.$emit('update:modelValue', this.nodeConfig)
|
|
|
+ this.drawer = false
|
|
|
+ },
|
|
|
+ addTerm() {
|
|
|
+ let len = this.nodeConfig.parallelNodes.length + 1
|
|
|
+ this.nodeConfig.parallelNodes.push({
|
|
|
+ nodeName: '包容条件' + len,
|
|
|
+ nodeKey: getNodeKey(),
|
|
|
+ type: 3,
|
|
|
+ priorityLevel: len,
|
|
|
+ conditionMode: 1,
|
|
|
+ conditionList: []
|
|
|
+ })
|
|
|
+ },
|
|
|
+ delTerm(index) {
|
|
|
+ this.nodeConfig.parallelNodes.splice(index, 1)
|
|
|
+ if (this.nodeConfig.parallelNodes.length == 1) {
|
|
|
+ if (this.nodeConfig.childNode) {
|
|
|
+ if (this.nodeConfig.parallelNodes[0].childNode) {
|
|
|
+ this.reData(this.nodeConfig.parallelNodes[0].childNode, this.nodeConfig.childNode)
|
|
|
+ } else {
|
|
|
+ this.nodeConfig.parallelNodes[0].childNode = this.nodeConfig.childNode
|
|
|
+ }
|
|
|
+ }
|
|
|
+ this.$emit('update:modelValue', this.nodeConfig.parallelNodes[0].childNode)
|
|
|
+ }
|
|
|
+ },
|
|
|
+ copyTerm(index) {
|
|
|
+ let len = this.nodeConfig.parallelNodes.length + 1
|
|
|
+ const currentNode = JSON.parse(JSON.stringify(this.nodeConfig.parallelNodes[index]))
|
|
|
+ const item = {
|
|
|
+ ...currentNode,
|
|
|
+ nodeKey: getNodeKey(),
|
|
|
+ nodeName: currentNode.nodeName + '-Copy',
|
|
|
+ priorityLevel: len
|
|
|
+ }
|
|
|
+ this.nodeConfig.parallelNodes.push(item)
|
|
|
+ this.$emit('update:modelValue', this.nodeConfig)
|
|
|
+ },
|
|
|
+ reData(data, addData) {
|
|
|
+ if (!data.childNode) {
|
|
|
+ data.childNode = addData
|
|
|
+ } else {
|
|
|
+ this.reData(data.childNode, addData)
|
|
|
+ }
|
|
|
+ },
|
|
|
+ arrTransfer(index, type = 1) {
|
|
|
+ this.nodeConfig.parallelNodes[index] = this.nodeConfig.parallelNodes.splice(index + type, 1, this.nodeConfig.parallelNodes[index])[0]
|
|
|
+ this.nodeConfig.parallelNodes.map((item, index) => {
|
|
|
+ item.priorityLevel = index + 1
|
|
|
+ })
|
|
|
+ this.$emit('update:modelValue', this.nodeConfig)
|
|
|
+ },
|
|
|
+ addConditionList(conditionList, type) {
|
|
|
+ conditionList.push({
|
|
|
+ label: '',
|
|
|
+ field: '',
|
|
|
+ operator: '==',
|
|
|
+ value: '',
|
|
|
+ type
|
|
|
+ })
|
|
|
+ },
|
|
|
+ deleteConditionList(conditionList, index) {
|
|
|
+ conditionList.splice(index, 1)
|
|
|
+ },
|
|
|
+ addConditionGroup() {
|
|
|
+ this.addConditionList(this.form.conditionList[this.form.conditionList.push([]) - 1], 'custom')
|
|
|
+ },
|
|
|
+ deleteConditionGroup(index) {
|
|
|
+ this.form.conditionList.splice(index, 1)
|
|
|
+ },
|
|
|
+ toText(nodeConfig, index) {
|
|
|
+ var { conditionList } = nodeConfig.parallelNodes[index]
|
|
|
+ if (conditionList && conditionList.length == 1) {
|
|
|
+ const text = conditionList.map(conditionGroup =>
|
|
|
+ conditionGroup
|
|
|
+ .map(item => {
|
|
|
+ const showOperator = this.operatorType.find(i => i.value === item.operator).label
|
|
|
+ if (item.type === 'form') {
|
|
|
+ return `${item.showLabel}${showOperator}${item.value}`
|
|
|
+ } else {
|
|
|
+ return `${item.label}${showOperator}${item.value}`
|
|
|
+ }
|
|
|
+ })
|
|
|
+ .join(' 和 ')
|
|
|
+ )
|
|
|
+ return text
|
|
|
+ } else if (conditionList && conditionList.length > 1) {
|
|
|
+ return conditionList.length + '个条件,或满足'
|
|
|
+ } else {
|
|
|
+ if (index == nodeConfig.parallelNodes.length - 1) {
|
|
|
+ return '未满足时其他条件时,将进入默认流程'
|
|
|
+ } else {
|
|
|
+ return false
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+ getCurrentItemLabel(conditionIdx, idx) {
|
|
|
+ const currentCondition = this.form.conditionList[conditionIdx]
|
|
|
+ const field = currentCondition[idx].field
|
|
|
+ const labelObj = this.expressionFormList.find(i => i.key === field)
|
|
|
+ currentCondition[idx].showLabel = labelObj.label
|
|
|
+ currentCondition[idx].label = field
|
|
|
+ },
|
|
|
+ getCurrentItemField(conditionIdx, idx) {
|
|
|
+ const currentCondition = this.form.conditionList[conditionIdx]
|
|
|
+ currentCondition[idx].field = currentCondition[idx].label
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+</script>
|
|
|
+
|
|
|
+<style scoped lang="scss">
|
|
|
+.top-tips {
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-between;
|
|
|
+ align-items: center;
|
|
|
+ margin-bottom: 12px;
|
|
|
+ color: #646a73;
|
|
|
+}
|
|
|
+
|
|
|
+.or-branch-link-tip {
|
|
|
+ margin: 10px 0;
|
|
|
+ color: #646a73;
|
|
|
+}
|
|
|
+
|
|
|
+.condition-group-editor {
|
|
|
+ user-select: none;
|
|
|
+ border-radius: 4px;
|
|
|
+ border: 1px solid #e4e5e7;
|
|
|
+ position: relative;
|
|
|
+ margin-bottom: 16px;
|
|
|
+
|
|
|
+ .branch-delete-icon {
|
|
|
+ font-size: 18px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .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;
|
|
|
+ }
|
|
|
+}
|
|
|
+</style>
|