Files
school-developer/src/flow/designer/components/flow-con-rule.vue
吴红兵 1f645dad3e init
2025-12-02 10:37:49 +08:00

528 lines
22 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<el-form-item label="起始节点(可修改)">
<el-select class="input-attr"
v-model="data.fromFlowNodeId"
@change="methods.changeLinkFlowNodeIds('0')"
filterable clearable>
<el-option
v-for="(item, index) in data.fromFlowNodeIds"
:key="item.id"
:label="item.nodeName"
:value="item.id">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="目标节点(可修改)">
<el-select class="input-attr"
v-model="data.toFlowNodeId"
@change="methods.changeLinkFlowNodeIds('1')"
filterable clearable>
<el-option
v-for="(item, index) in data.toFlowNodeIds"
:key="item.id"
:label="item.nodeName"
:value="item.id">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="条件组关系">
<el-switch
v-model="data.cond.groupsType" @change="methods.changeGroupsType"
active-value="0"
inactive-value="1"
inactive-text=""
active-text="">
</el-switch>
</el-form-item>
<el-form-item label="条件模式">
<el-radio-group @change="methods.handleCondValType" :disabled="data.existData"
v-model="data.cond.valType">
<el-radio v-for="(item, index) in DIC_PROP.VAL_TYPE.slice(2, 6)" :key="index" :label="item.value"
style="width: 56px">
{{ item.label }}
</el-radio>
</el-radio-group>
<el-tooltip placement="top">
<template #content>若无法切换模式请先清空条件组列表</template>
<el-icon style="margin-left: 30px">
<QuestionFilled/>
</el-icon>
</el-tooltip>
</el-form-item>
<el-divider>组内条件配置</el-divider>
<template v-if="data.cond.valType === '0'">
<el-form-item label="组内条件关系">
<el-switch
v-model="data.cond.groupType"
active-value="0"
inactive-value="1"
inactive-text=""
active-text="">
</el-switch>
</el-form-item>
<el-table :data="data.cond.condGroup"
border style="width: 100%; margin-bottom: 10px" max-height="500">
<el-table-column type="index" label="操作" width="80">
<template #header>
<el-button icon="Plus" size="small" type="primary" circle @click="methods.onAddItem(true)"></el-button>
</template>
<template #default="scope">
<el-button icon="Minus" size="small" type="danger" circle @click="methods.handleCondDelete(scope.$index, scope.row)"></el-button>
</template>
</el-table-column>
<el-table-column prop="varKeyVal" :label="t('flowClazz.varKeyVal')" show-overflow-tooltip>
<template #default="scope">
<el-select v-model="scope.row.varKeyVal" @change="methods.handleVarKeyVal(scope.row)"
clearable
filterable>
<el-option-group
v-for="(group, index) in data.allFieldPerms"
:key="index"
:label="group.label">
<el-option
v-for="(item, index) in group.options"
:disabled="item.prop.indexOf('_define_') !== -1"
:key="index"
:label="item.label"
:value="item.prop">
</el-option>
</el-option-group>
</el-select>
</template>
</el-table-column>
<el-table-column prop="operator" :label="t('flowClazz.operator')" show-overflow-tooltip>
<template #default="scope">
<el-select v-model="scope.row.operator" @change="methods.handleVarKeyVal(scope.row)"
clearable>
<el-option
v-for="(item, index) in data.varValOperator"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</template>
</el-table-column>
<el-table-column prop="varVal" :label="t('flowClazz.varVal')" show-overflow-tooltip>
<template #default="scope">
<el-input v-model="scope.row.varVal" clearable/>
</template>
</el-table-column>
</el-table>
</template>
<el-form-item label="SpEL表达式" v-if="data.cond.valType === '1'">
<el-input class="input-attr" v-model="data.cond.varKeyVal" placeholder="请输入SpEL表达式" clearable @blur="methods.handleCondVarKeyVal"/>
<el-tooltip placement="top">
<template #content>{{ PROP_CONST.TEXT_DESC.condSpELExplain }}</template>
<el-icon style="margin-left: 10px">
<QuestionFilled/>
</el-icon>
</el-tooltip>
</el-form-item>
<el-form-item label="函数表达式" v-if="data.cond.valType === '2'">
<el-input class="input-attr" v-model="data.cond.varKeyVal" placeholder="请输入函数表达式" clearable @blur="methods.handleCondVarKeyVal"/>
<el-tooltip placement="top">
<template #content>{{ PROP_CONST.TEXT_DESC.condMethodExplain1 }}
<br />{{ PROP_CONST.TEXT_DESC.condMethodExplain2 }}
<br />{{ PROP_CONST.TEXT_DESC.condMethodExplain3 }}
<br />{{ PROP_CONST.TEXT_DESC.condMethodExplain4 }}
</template>
<el-icon style="margin-left: 10px">
<QuestionFilled/>
</el-icon>
</el-tooltip>
</el-form-item>
<div v-if="data.cond.valType === '2'" style="margin: 10px 13px">
<span style="color: #409EFF;font-size: 14px">: 当前函数表达式的返回值为字符串 1 ( 满足 ) 0 ( 不满足 )</span>
</div>
<template v-if="data.cond.valType === '3'">
<el-form-item label="Http请求地址">
<el-input class="input-attr" placeholder="可输入全路径或相对路径" v-model="data.cond.varKeyVal"
@blur="methods.handleCondVarKeyVal" clearable>
<template #prepend>
<el-select v-model="data.cond.httpMethod">
<el-option v-for="(item, index) in DIC_PROP.HTTP_METHODS" :key="index" :label="item.label" :value="item.value"></el-option>
</el-select>
</template>
</el-input>
<el-tooltip content="点击可设置Http请求的参数信息" placement="bottom">
<el-button type="primary" size="small" round style="margin-left: 10px"
@click="methods.openHttpUrlParams()">
{{ data.httpUrlParamsVisible ? '清空' : '参数' }}
</el-button>
</el-tooltip>
</el-form-item>
<flow-http-param ref="flowHttpParam" :currFlowForm="props.currFlowForm" :flowData="props.flowData"
:httpParam="props.currSelect.attributes.attrs.cdata.attrs"
:httpParamType="DIC_PROP.PARAM_RULE_TYPE[1].value"
v-if="data.httpUrlParamsVisible">
</flow-http-param>
</template>
<template v-if="data.cond.valType === '0'">
<el-button type="primary" round style="margin-left: 185px; margin-top: 30px; margin-bottom: 30px; width: 200px"
@click="methods.addFlowNodeCondGroup()">
{{ data.oldCurrentRow ? '修改完成': '添加条件组' }}
</el-button>
<el-divider>已添加条件组列表(点击行可再次修改)</el-divider>
<el-empty description="条件组列表为空" style="margin: 10px 230px" v-if="!data.existData">
</el-empty>
<template v-for="(item, index) in data.condGroups" v-else
:key="index">
<el-collapse v-model="data.collapse">
<el-collapse-item :name="index">
<template #title>
{{ '条件组 ' + (index + 1) }}
<el-icon style="margin-left: 10px" @click="methods.handleCondGroupDelete(index)">
<Delete />
</el-icon>
</template>
<el-form-item label="组内条件关系">
<el-switch
v-model="item.groupType" @change="methods.changeGroupType(item, index)"
active-value="0"
inactive-value="1"
inactive-text=""
active-text="">
</el-switch>
</el-form-item>
<el-table :data="item.condGroup" border style="width: 100%" max-height="500"
highlight-current-row @current-change="methods.handleCurrentChange" :ref="'tableDataRef' + index">
<el-table-column type="index" label="操作" width="80">
<template #default="scope">
<el-button icon="Minus" size="small" type="danger" circle
@click="methods.handleSingleCondDelete(scope.$index, scope.row, index)"></el-button>
</template>
</el-table-column>
<el-table-column prop="varKeyVal" :label="t('flowClazz.varKeyVal')" show-overflow-tooltip>
<template #default="scope">
<convert-group-name :options="data.allFieldPerms"
:value="scope.row.varKeyVal"
:valueKey="'prop'" :showKey="'label'"></convert-group-name>
</template>
</el-table-column>
<el-table-column prop="operator" :label="t('flowClazz.operator')" show-overflow-tooltip>
<template #default="scope">
<dict-tag :options="DIC_PROP.OPERATOR" :value="scope.row.operator"></dict-tag>
</template>
</el-table-column>
<el-table-column prop="varVal" :label="t('flowClazz.varVal')" show-overflow-tooltip/>
</el-table>
</el-collapse-item>
</el-collapse>
</template>
</template>
</template>
<script setup lang="ts" name="FlowConRule">
import {useI18n} from "vue-i18n";
import {useMessage} from "/@/hooks/message";
import {rule, validateNull} from "/@/utils/validate";
import {deepClone} from "/@/utils/other";
import {DIC_PROP} from "/@/flow/support/dict-prop";
import {buildSysFieldsFormOption} from "../../utils/form-perm";
import {PROP_CONST} from "../../support/prop-const";
import {changeLinkFlowNodeIds, handleLinkFlowNodeIds} from "./index";
const {proxy} = getCurrentInstance();
const FlowHttpParam = defineAsyncComponent(() => import('./flow-http-param.vue'));
const {t} = useI18n();
const $message = useMessage();
const $emit = defineEmits(["hideAttrConfig"]);
const props = defineProps({
currFlowForm: {
type: Object,
default: null,
},
currSelect: {
type: Object,
default: null,
},
flowData: {
type: Object,
default: null,
},
attrConfigVisible: null
});
const condData = {
httpMethod: 'GET',
groupsType: '0',
condGroup: [],
groupType: '0',
valType: '0',
varKeyVal: null,
operator: null,
varVal: null,
}
const data = reactive({
collapse: [0],
allFieldPerms: [],
formFieldPerms: [],
cond: deepClone(condData),
httpUrlParamsVisible: false,
oldCurrentRow: null,
condGroups: [],
existData: false,
modifyPointType: null,
fromFlowNodeId: null,
fromFlowNodeIds: [],
toFlowNodeId: null,
toFlowNodeIds: []
})
onMounted(() => {
methods.changeTabPane(props.currSelect)
})
const methods = {
openHttpUrlParams() {
if (!data.cond.varKeyVal) {
$message.warning("请先输入【Http请求地址】")
return
}
if (data.httpUrlParamsVisible) {
props.currSelect.attributes.attrs.cdata.attrs.httpParams = []
data.httpUrlParamsVisible = false
} else {
data.httpUrlParamsVisible = true
}
},
handleCurrentChange(row) {
if (!row) return
let condGroupsRow;
for (let i = 0; i < data.condGroups.length; i++) {
let index = data.condGroups[i].condGroup.indexOf(row);
if (index !== -1) {
condGroupsRow = data.condGroups[i];
}
}
// 先清空之前选中的其他条件组
if (data.oldCurrentRow !== condGroupsRow) {
let oldIndex = data.condGroups.indexOf(data.oldCurrentRow);
if (oldIndex !== -1) {
proxy.$refs['tableDataRef' + oldIndex][0].setCurrentRow(null)
}
}
data.cond = condGroupsRow
data.oldCurrentRow = condGroupsRow
},
handleVarKeyVal(row) {
let dots = row.varKeyVal.split('.').length - 1;
if (dots === 2) {
let find = DIC_PROP.OPERATOR.slice(6).find(f => f.value === row.operator);
if (!find) {
$message.warning("子表单的字段只能选择包含或不包含")
row.operator = null
}
}
},
changeGroupsType(groupsType) {
let condGroups = props.currSelect.attributes.attrs.cdata.attrs.condGroups;
if (validateNull(condGroups)) return
props.currSelect.attributes.attrs.cdata.attrs.condGroups.forEach(each => {
each.groupsType = groupsType
})
methods.validateCondData()
},
changeGroupType(item, index) {
let condGroups = props.currSelect.attributes.attrs.cdata.attrs.condGroups;
if (validateNull(condGroups)) return
props.currSelect.attributes.attrs.cdata.attrs.condGroups[index].groupType = item.groupType
methods.validateCondData()
},
onAddItem(isAdd) {
if (validateNull(data.formFieldPerms)) {
buildSysFieldsFormOption(data, props, $message)
}
if (data.cond.condGroup.length > 0) {
let find = data.cond.condGroup.find(f => !f.varKeyVal || !f.operator || !f.varVal);
if (find) {
let b = !find.varKeyVal || !find.operator || !find.varVal;
if (isAdd && b) {
$message.warning("请先填写 表单字段 或 运算符 或 值")
return
}
}
if (!isAdd) data.cond.condGroup.splice(0, data.cond.condGroup.length);
}
let obj = {varKeyVal: '', operator: '', varVal: ''};
data.cond.condGroup.push(obj);
},
addFlowNodeCondGroup() {
if (validateNull(data.cond.condGroup)) {
$message.warning("请先添加组内条件")
return
}
let valType = data.cond.valType;
let find = data.cond.condGroup.find(f => !f.varKeyVal || !f.operator || !f.varVal);
if (find) {
if (valType === DIC_PROP.VAL_TYPE[2].value) {
if (!find.varKeyVal || !find.operator || !find.varVal) {
$message.warning("表单字段 或 运算符 或 值 不能为空")
return
}
}
}
if (data.oldCurrentRow) {
// 先清空之前选中
let index = data.condGroups.indexOf(data.cond);
if (index !== -1) {
proxy.$refs['tableDataRef' + index][0].setCurrentRow(null)
}
data.oldCurrentRow = null
data.cond = deepClone(condData);
} else {
let cond = deepClone(data.cond);
props.currSelect.attributes.attrs.cdata.attrs.condGroups.unshift(cond)
methods.validateCondData()
methods.updateCondVarKeyVal(true)
}
},
handleCondGroupDelete(index: number) {
props.currSelect.attributes.attrs.cdata.attrs.condGroups.splice(index, 1)
methods.validateCondData()
},
handleSingleCondDelete(index: number, row: any, groupIndex) {
props.currSelect.attributes.attrs.cdata.attrs.condGroups[groupIndex].condGroup.splice(index, 1)
if (validateNull(props.currSelect.attributes.attrs.cdata.attrs.condGroups[groupIndex].condGroup)) {
methods.handleCondGroupDelete(groupIndex)
}
methods.validateCondData()
},
handleCondDelete(index: number, row: any) {
data.cond.condGroup.splice(index, 1)
},
handleCondValType(type?) {
if (type) {
data.cond.varKeyVal = null
methods.updateCondVarKeyVal(false)
}
if (!type) {
type = methods.initCurrVarKeyVal()
}
if (type === DIC_PROP.VAL_TYPE[2].value) {
data.varValOperator = DIC_PROP.OPERATOR
methods.onAddItem(false)
} else {
data.varValOperator = []
}
data.cond.operator = null
data.cond.varVal = null
},
handleCondVarKeyVal() {
let val = data.cond.varKeyVal
let valType = data.cond.valType;
if (!val) {
methods.updateCondVarKeyVal(false)
return
}
if (valType === DIC_PROP.VAL_TYPE[2].value) return
if (valType === DIC_PROP.VAL_TYPE[3].value && val.indexOf("#") === -1) {
data.cond.varKeyVal = null
$message.warning("当选择SpEL模式时, SpEL表达式必须符合SpEL格式")
return;
} else if (valType === DIC_PROP.VAL_TYPE[4].value && val.indexOf("#") === -1) {
data.cond.varKeyVal = null
$message.warning("当选择专业模式时, 函数表达式必须符合规定的格式")
return;
}
methods.updateCondVarKeyVal(true)
},
updateCondVarKeyVal(isSave) {
props.currSelect.attributes.attrs.cdata.attrs.valType = data.cond.valType
if (isSave) {
if (data.cond.valType === DIC_PROP.VAL_TYPE[2].value) {
props.currSelect.attributes.attrs.cdata.attrs.varKeyVal = PROP_CONST.VAR_KEY_VAL.link
} else {
props.currSelect.attributes.attrs.cdata.attrs.varKeyVal = data.cond.varKeyVal
}
props.currSelect.attributes.attrs.cdata.attrs.httpMethod = data.cond.httpMethod
} else {
props.currSelect.attributes.attrs.cdata.attrs.varKeyVal = null
props.currSelect.attributes.attrs.cdata.attrs.varKeyValName = null
props.currSelect.attributes.attrs.cdata.attrs.condGroups = []
props.currSelect.attributes.attrs.cdata.attrs.httpParams = []
props.currSelect.attributes.attrs.cdata.attrs.httpMethod = null
data.httpUrlParamsVisible = false
}
},
validateCondData() {
let condGroups = props.currSelect.attributes.attrs.cdata.attrs.condGroups;
if (!condGroups) {
props.currSelect.attributes.attrs.cdata.attrs.condGroups = []
}
data.condGroups.splice(0, data.condGroups.length);
props.currSelect.attributes.attrs.cdata.attrs.condGroups.forEach(each => data.condGroups.push(each))
data.existData = !validateNull(data.condGroups)
},
initCurrVarKeyVal() {
data.cond.valType = props.currSelect.attributes.attrs.cdata.attrs.valType
if (data.condGroups.length <= 0) {
data.cond.varKeyVal = props.currSelect.attributes.attrs.cdata.attrs.varKeyVal
let httpMethod = props.currSelect.attributes.attrs.cdata.attrs.httpMethod
if (httpMethod) data.cond.httpMethod = httpMethod
} else {
data.cond.groupsType = data.condGroups[0].groupsType
}
let httpParams = props.currSelect.attributes.attrs.cdata.attrs.httpParams;
if (!validateNull(httpParams)) {
data.httpUrlParamsVisible = true
}
return data.cond.valType
},
handleLinkFlowNodeIds() {
handleLinkFlowNodeIds(data, props)
},
changeLinkFlowNodeIds(type) {
data.modifyPointType = type
changeLinkFlowNodeIds(data, props, methods, $emit)
},
changeTabPane(val) {
methods.validateCondData()
methods.handleCondValType()
methods.handleLinkFlowNodeIds()
}
}
// 监听双向绑定
watch(
() => props.currSelect,
(val) => {
if (Object.keys(val).length === 0 || !props.attrConfigVisible) {
return
}
methods.changeTabPane(val)
}
);
</script>
<style lang="scss">
@import '../assets/style/flow-attr.scss';
</style>