This commit is contained in:
吴红兵
2025-12-02 10:37:49 +08:00
commit 1f645dad3e
1183 changed files with 147673 additions and 0 deletions

View File

@@ -0,0 +1,138 @@
<template>
<div id="jfDragWrapPaper">
<div
id="jfWrapPaper"
class="jf-wrap-paper"
>
<div id="jsonflowMainPaper"></div>
</div>
<div id="minimapPaperView" class="jsonflow-navigator"></div>
<div class="container-scale">
<el-button icon="ZoomIn" circle size="small" type="default" @click="methods.enlargePaper"></el-button>
<span>{{ data.container.scaleShow }}% </span>
<el-button icon="ZoomOut" circle size="small" type="default" @click="methods.narrowPaper"></el-button>
</div>
<vue-context-menu
:contextMenuData="data.nodeContMenuData"
>
</vue-context-menu>
<vue-context-menu
:contextMenuData="data.linkContMenuData"
>
</vue-context-menu>
</div>
</template>
<script setup lang="ts" name="FlowAreaView">
import {flowConfig} from "../config/flow-config";
import {utils} from "../utils/common";
import {deepClone} from "/@/utils/other";
import {useI18n} from "vue-i18n";
import {validateNull} from "/@/utils/validate";
import {hideVueContextmenuName} from "/@/flow/utils";
// 引入组件
const VueContextMenu = defineAsyncComponent(() => import('../../components/contextmenu/index.vue'));
const {t} = useI18n();
const $emit = defineEmits(["initJsonFlowView"]);
const props = defineProps({
currSelect: {
type: Object,
default: {},
}
});
const data = reactive({
container: {
scaleShow: utils.mul(flowConfig.defaultStyle.containerScale.init, 100)
},
linkContMenuData: deepClone(flowConfig.contextMenu.linkView),
nodeContMenuData: deepClone(flowConfig.contextMenu.nodeView)
})
const methods = {
// 画布放大
enlargePaper() {
data.container.scaleShow = window._jfOperate.zoomOut();
},
// 画布缩小
narrowPaper() {
let zoomIn = window._jfOperate.zoomIn();
data.container.scaleShow = parseInt(zoomIn)
},
// 节点click事件
showNodeClickMenu(currSelect, e) {
// TODO 业务侧替换其他内容
methods.showNodeContMenu(currSelect, e)
},
// 节点hover事件
showNodeContMenu(currSelect, e) {
if (validateNull(currSelect)) {
currSelect = props.currSelect
}
if (Object.keys(currSelect).length === 0) return
// 计算节点信息
methods.updateNodeContMenuData(currSelect)
let event = window.event || e;
event.preventDefault();
hideVueContextmenuName()
let x = event.clientX;
let y = event.clientY;
data.nodeContMenuData.axis = {x, y};
},
updateNodeContMenuData(currSelect) {
let nodeNameBtnName = flowConfig.contextMenu.nodeView.menulists[0].btnName;
let text;
if (!currSelect.attributes.attrs.label) text = currSelect.attrs.label.text
else text = currSelect.attributes.attrs.label.text
data.nodeContMenuData.menulists[0].btnName = nodeNameBtnName + text
data.nodeContMenuData.menulists[1].btnName = currSelect.attributes.startTime
data.nodeContMenuData.menulists[2].btnName = currSelect.attributes.userRoleName
data.nodeContMenuData.menulists[3].btnName = currSelect.attributes.userName
data.nodeContMenuData.menulists[4].btnName = currSelect.attributes.remark
},
// 连接click事件
showLinkClickMenu(currSelect, e) {
// TODO 业务侧替换其他内容
methods.showLinkContMenu(currSelect, e)
},
// 连接线hover事件
showLinkContMenu(currSelect, e) {
if (validateNull(currSelect)) {
currSelect = props.currSelect
}
if (Object.keys(currSelect).length === 0) return
// 计算连线条件
let varKeyVal = currSelect.attributes.attrs.cdata.attrs.varKeyVal;
hideVueContextmenuName()
if (!varKeyVal) return
let btnName = flowConfig.contextMenu.linkView.menulists[0].btnName;
data.linkContMenuData.menulists[0].btnName = btnName + varKeyVal
let event = window.event || e;
event.preventDefault();
event.stopPropagation();
let x = event.clientX;
let y = event.clientY;
data.linkContMenuData.axis = {x, y};
}
}
onMounted(() => {
nextTick(() => {
$emit("initJsonFlowView");
})
});
// 暴露变量
defineExpose({
showNodeContMenu: methods.showNodeContMenu,
showNodeClickMenu: methods.showNodeClickMenu,
showLinkContMenu: methods.showLinkContMenu,
showLinkClickMenu: methods.showLinkClickMenu,
});
</script>
<style lang="scss">
@import "../assets/style/flow-area.scss";
@import "../assets/style/flow-paper.css";
</style>

View File

@@ -0,0 +1,384 @@
<template>
<div id="jfDragWrapPaper">
<div id="jfWrapPaper"
class="jf-wrap-paper"
>
<div id="jsonflowMainPaper" @dragover="methods.allowDrop" @drop="methods.drop"></div>
<div class="horizontal-line-x"></div>
<div class="vertical-line-y"></div>
</div>
<div id="minimapPaperView" class="jsonflow-navigator"></div>
<div class="container-scale">
<el-button icon="ZoomIn" circle size="small" type="default"
@click="methods.enlargePaper"></el-button>
<span>{{ data.container.scaleShow }}% </span>
<el-button icon="ZoomOut" circle size="small" type="default"
@click="methods.narrowPaper"></el-button>
</div>
<!-- 选择连接的节点 -->
<el-dialog
v-model="data.showSetConnectNode" v-if="data.showSetConnectNode"
top="20px"
width="50%"
title="请选择连接到的节点"
append-to-body>
<el-form label-position="left" class="flow-config-attr" label-width="170px">
<el-form-item label="连接到的节点">
<el-select class="input-attr" placeholder="请选择连接到的节点" style="width: 80%!important;"
@change="methods.doConnectNode"
v-model="data.toFlowNodeId"
filterable clearable>
<el-option
v-for="(item, index) in data.flowNodeIds"
:key="item.id"
:label="item.nodeName"
:value="item.id">
</el-option>
</el-select>
<el-tooltip placement="top">
<template #content>从当前节点连接到目标节点</template>
<el-icon style="margin-left: 10px"><QuestionFilled /></el-icon>
</el-tooltip>
</el-form-item>
</el-form>
</el-dialog>
<!-- 选择连线的起始节点 -->
<el-dialog
v-model="data.showLinkFlowNodeIds" v-if="data.showLinkFlowNodeIds"
top="20px"
width="50%"
:title="'请选择连接到的' + (data.modifyPointType === '0' ? '起点' : '终点')"
append-to-body>
<el-form label-position="left" class="flow-config-attr" label-width="170px">
<el-form-item label="起始节点(可修改)" v-if="data.modifyPointType ==='0'">
<el-select class="input-attr" style="width: 80%!important;"
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="目标节点(可修改)" v-if="data.modifyPointType ==='1'">
<el-select class="input-attr" style="width: 80%!important;"
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>
</el-dialog>
<vue-context-menu
:contextMenuData="data.paperContMenuData"
@flowInfo="methods.flowInfo"
@paste="methods.paste"
>
</vue-context-menu>
<vue-context-menu
:contextMenuData="data.nodeContMenuData"
@setNodeAttr="methods.setNodeAttr('0')"
@copyNode="methods.copyNode"
@deleteNode="methods.deleteNode"
@setConnectNode="methods.setConnectNode"
>
</vue-context-menu>
<vue-context-menu
:contextMenuData="data.nodeConnectMenuData"
@setSerialNode="methods.setSerialNode"
@setSerialGate="methods.setSerialGate"
@setParallelNode="methods.setParallelNode"
@setParallelGate="methods.setParallelGate"
@setConnectNode="methods.setConnectNode"
>
</vue-context-menu>
<vue-context-menu
:contextMenuData="data.linkContMenuData"
@deleteLink="methods.deleteLink"
@setLinkAttr="methods.setNodeAttr('1')"
@modifySourceNode="methods.handleLinkFlowNodeIds('0')"
@modifyTargetNode="methods.handleLinkFlowNodeIds('1')"
>
</vue-context-menu>
</div>
</template>
<script setup lang="ts" name="FlowArea">
import {flowConfig} from "../config/flow-config";
import {utils} from "../utils/common";
import {useI18n} from "vue-i18n";
import {useMessage, useMessageBox} from "/@/hooks/message";
import {notifyLeft} from "/@/flow";
import {CommonNodeType, HighNodeType} from "../config/type";
import {hideVueContextmenuName} from "/@/flow/utils";
import {validateNull} from "/@/utils/validate";
import {changeLinkFlowNodeIds, handleLinkFlowNodeIds, validateNodeType} from "./index";
// 引入组件
const VueContextMenu = defineAsyncComponent(() => import('../../components/contextmenu/index.vue'));
const {t} = useI18n();
const {proxy} = getCurrentInstance();
const $message = useMessage();
const $emit = defineEmits(["removeEleTools", "showAttrConfig", "initJsonFlow"]);
const props = defineProps({
dragInfo: {
type: Object,
default: null,
},
currSelect: {
type: Object,
default: {},
}
});
const data = reactive({
container: {
scaleShow: utils.mul(flowConfig.defaultStyle.containerScale.init, 100)
},
paperContMenuData: flowConfig.contextMenu.container,
nodeContMenuData: flowConfig.contextMenu.node,
nodeConnectMenuData: flowConfig.contextMenu.nodeConnect,
linkContMenuData: flowConfig.contextMenu.link,
clipboard: {},
showSetConnectNode: false,
showLinkFlowNodeIds: false,
toFlowNodeId: null,
flowNodeIds: [],
modifyPointType: null,
fromFlowNodeId: null,
fromFlowNodeIds: [],
toFlowNodeIds: []
})
const methods = {
allowDrop(e) {
e.preventDefault();
},
drop(e) {
// 增加节点
window._jfOperate.dropNewNode(e, props.dragInfo, true);
},
// 画布放大
enlargePaper() {
data.container.scaleShow = window._jfOperate.zoomOut();
},
// 画布缩小
narrowPaper() {
let zoomIn = window._jfOperate.zoomIn();
data.container.scaleShow = parseInt(zoomIn)
},
// 画布右健
showPaperContMenu(e) {
let event = window.event || e;
event.preventDefault();
hideVueContextmenuName()
let x = event.clientX;
let y = event.clientY;
data.paperContMenuData.axis = {x, y};
},
// 流程图信息
flowInfo() {
$message.info(
"当前流程图中有 " +
window._jfGraph.getElements().length +
" 个节点,有 " +
window._jfGraph.getLinks().length +
" 条连线。"
);
},
handleFlowNodeIds() {
data.flowNodeIds = []
let models = window._jfGraph.getElements();
if (validateNull(models)) return
models.forEach(each => {
if (!validateNodeType(each)) return
if (props.currSelect.id !== each.id) {
let id = each.id
let nodeName = each.attributes.attrs.label.text + "ID:" + id + ""
data.flowNodeIds.push({id, nodeName})
}
})
},
// 粘贴
paste() {
let e = window.event;
let b = Object.keys(data.clipboard).length === 0;
if (b) {
$message.info("请将鼠标放节点上, 右键菜单复制节点");
hideVueContextmenuName()
return
}
let newNode = data.clipboard.clone()
window._jfOperate.pasteNode(newNode, e);
data.clipboard = {}
hideVueContextmenuName()
},
// 节点右键
showNodeContMenu(e) {
let event = window.event || e;
event.preventDefault();
hideVueContextmenuName()
let x = event.clientX;
let y = event.clientY;
data.nodeContMenuData.axis = {x, y};
},
// 复制节点
copyNode() {
data.clipboard = {};
if (methods.validateCurrSelect("0")) {
return
}
data.clipboard = props.currSelect;
hideVueContextmenuName()
},
// 删除节点
deleteNode() {
if (methods.validateCurrSelect("0")) {
return
}
props.currSelect.remove()
$emit("removeEleTools");
},
setNodeAttr(type) {
if (methods.validateCurrSelect(type)) {
return
}
$emit("showAttrConfig", true);
},
// 连接线右键
showLinkContMenu(e) {
let event = window.event || e;
event.preventDefault();
event.stopPropagation();
hideVueContextmenuName()
let x = event.clientX;
let y = event.clientY;
data.linkContMenuData.axis = {x, y};
},
// 删除线
deleteLink() {
if (methods.validateCurrSelect("1")) {
return
}
props.currSelect.remove()
$emit("removeEleTools");
},
validateCurrSelect(type) {
let b = Object.keys(props.currSelect).length === 0;
if (b !== true) return false
if (type === '0') {
notifyLeft('请先选择节点', 'warning')
} else {
notifyLeft('请先移动到连线上方', 'warning')
}
return true
},
// 节点连接右键
showNodeConnectMenu(params, e) {
let event = window.event || e;
hideVueContextmenuName()
let x = event.clientX;
let y = event.clientY;
data.nodeConnectMenuData.axis = {x, y};
data.nodeConnectMenuData.params = params;
},
setSerialNode() {
methods.connectAction({belongTo: "commonNodes", type: CommonNodeType.SERIAL})
},
setParallelNode() {
methods.connectAction({belongTo: "commonNodes", type: CommonNodeType.PARALLEL})
},
setSerialGate() {
methods.connectAction({belongTo: "highNodes", type: CommonNodeType.SERIAL})
},
setParallelGate() {
methods.connectAction({belongTo: "highNodes", type: CommonNodeType.PARALLEL})
},
setConnectNode() {
if (methods.hideValidateCurrSelect("0")) {
return
}
methods.handleFlowNodeIds()
data.toFlowNodeId = null
data.showSetConnectNode = true
},
doConnectNode(val) {
if (methods.hideValidateCurrSelect("0")) {
return
}
let simpleMode = window._flowConfig.globalConfig.isSimpleMode;
if (simpleMode !== '1') {
data.nodeConnectMenuData.params = {view: {model: props.currSelect}}
}
useMessageBox()
.confirm('是否确认连接到当前选中的节点?')
.then(() => {
let params = data.nodeConnectMenuData.params;
params.toFlowNodeId = val
window._jfOperate.doConnectNode(params)
data.showSetConnectNode = false
if (simpleMode !== '1') {
notifyLeft('专业模式不会自动调整连线轨迹,有必要时请手动调整', 'warning', 3000)
}
})
},
connectAction(dragInfo) {
if (methods.hideValidateCurrSelect("0")) {
return
}
let params = data.nodeConnectMenuData.params;
window._jfOperate.connectAction(params, dragInfo)
},
hideValidateCurrSelect(type) {
hideVueContextmenuName()
return methods.validateCurrSelect(type);
},
changeLinkFlowNodeIds(type) {
if (methods.hideValidateCurrSelect("1")) {
return
}
changeLinkFlowNodeIds(data, props)
data.showLinkFlowNodeIds = false
},
handleLinkFlowNodeIds(type) {
if (methods.hideValidateCurrSelect("1")) {
return
}
data.modifyPointType = type
handleLinkFlowNodeIds(data, props)
data.showLinkFlowNodeIds = true
},
}
onMounted(() => {
nextTick(() => {
$emit("initJsonFlow");
})
});
// 暴露变量
defineExpose({
showPaperContMenu: methods.showPaperContMenu,
showNodeContMenu: methods.showNodeContMenu,
showNodeConnectMenu: methods.showNodeConnectMenu,
showLinkContMenu: methods.showLinkContMenu,
});
</script>
<style lang="scss">
@import "../assets/style/flow-area.scss";
@import "../assets/style/flow-paper.css";
</style>

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,411 @@
<template>
<div>
<el-form label-position="left" class="flow-config-attr" label-width="160px">
<el-form-item label="监听类">
<el-input class="input-attr" :placeholder="props.clazzPlaceholder" v-model="data.clazz.clazz" clearable></el-input>
</el-form-item>
<el-form-item label="Http请求地址">
<el-input class="input-attr" placeholder="可输入全路径或相对路径" v-model="data.clazz.httpUrl"
@blur="methods.handleHttpUrlBlur" clearable>
<template #prepend>
<el-select v-model="data.clazz.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(true)">
参数
</el-button>
</el-tooltip>
</el-form-item>
<el-form-item label="触发时机">
<el-select class="input-attr"
v-model="data.clazz.methods" multiple
clearable placeholder="多个方法名称, 英文逗号分隔, 顺序从左到右">
<el-option
v-for="(item, index) in props.addType === '0' && props.currSelect.attributes.attrs.cdata.attrs.isAutoAudit === '1' ?
props.flowMethods.slice(3) : props.flowMethods"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</el-form-item>
<el-divider v-if="!data.moreInter">
<el-button type="primary" link @click="methods.showMoreSetting()"
style="margin-left: 10px; font-size: 14px">
{{ data.moreInter ? '隐藏更多' : '更多配置' }}
</el-button>
</el-divider>
<template v-if="data.moreInter">
<div style="margin: 10px 13px">
<span style="color: red;font-size: 14px">注: 监听事件允许设置生效条件,用于控制在某种条件下才会执行</span>
</div>
<el-form-item label="条件模式">
<el-radio-group style="width: 313px" @change="methods.handleClazzValType" :disabled="data.existData"
v-model="data.clazz.valType">
<el-radio v-for="(item, index) in DIC_PROP.VAL_TYPE.slice(2, 5)" :key="index" :label="item.value" style="width: 75px">
{{ item.label }}
</el-radio>
</el-radio-group>
<el-tooltip placement="top">
<template #content>若无法切换模式,请先清空监听事件列表</template>
<el-icon><QuestionFilled /></el-icon>
</el-tooltip>
</el-form-item>
<el-table :data="data.tableFieldData" v-if="data.clazz.valType === '0'"
border style="width: 100%; margin-bottom: 10px" max-height="500">
<el-table-column prop="varKeyVal" :label="t('flowClazz.varKeyVal')" show-overflow-tooltip>
<template #default="scope">
<el-select v-model="data.clazz.varKeyVal" @change="methods.handleVarKeyVal"
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="data.clazz.operator"
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="data.clazz.varVal" clearable/>
</template>
</el-table-column>
</el-table>
<el-form-item label="SpEL表达式" v-if="data.clazz.valType === '1'">
<el-input class="input-attr" v-model="data.clazz.varKeyVal" clearable @blur="methods.handleClazzVarKeyVal"/>
<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.clazz.valType === '2'">
<el-input class="input-attr" v-model="data.clazz.varKeyVal" placeholder="请输入函数表达式" clearable @blur="methods.handleClazzVarKeyVal"/>
<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>
<el-form-item label="监听备注">
<el-input class="input-attr" type="textarea" v-model="data.clazz.remark"></el-input>
</el-form-item>
<el-form-item label="监听排序">
<el-input-number class="input-attr" v-model="data.clazz.sort" :min="1"></el-input-number>
</el-form-item>
</template>
<el-button type="primary" round style="margin-left: 185px; margin-top: 30px; margin-bottom: 30px; width: 200px" @click="methods.addFlowNodeClazz(props.addType)">
{{ data.clazz.isSelectedRow ? '修改完成': '添加事件' }}
</el-button>
<el-divider>已添加监听事件列表(点击行可再次修改)</el-divider>
<el-empty description="监听事件列表为空" style="margin: 10px 230px" v-if="!data.existData">
</el-empty>
<el-table :data="data.tableData" border style="width: 100%" max-height="500" v-else
highlight-current-row @current-change="methods.handleCurrentChange" ref="tableDataRef">
<el-table-column type="index" label="操作" width="55">
<template #default="scope">
<el-button icon="Minus" size="small" type="danger" circle @click="methods.handleClazzDelete(scope.$index, scope.row, props.addType)"></el-button>
</template>
</el-table-column>
<el-table-column prop="clazz" :label="t('flowClazz.clazz')" width="130" show-overflow-tooltip/>
<el-table-column prop="httpUrl" :label="t('flowClazz.httpUrl')" width="130" show-overflow-tooltip>
</el-table-column>
<el-table-column prop="methods" label="触发时机" width="130" show-overflow-tooltip>
<template #default="scope">
<dict-tag :options="props.flowMethods" :value="scope.row.methods"></dict-tag>
</template>
</el-table-column>
<el-table-column prop="varKeyVal" label="函数表达式" width="130" show-overflow-tooltip v-if="data.clazz.valType === '2'"/>
<el-table-column prop="varKeyVal" label="SpEL表达式" width="130" show-overflow-tooltip v-if="data.clazz.valType === '1'"/>
<el-table-column prop="varKeyVal" :label="t('flowClazz.varKeyVal')" width="130" show-overflow-tooltip v-if="data.clazz.valType === '0'">
<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')" width="100" show-overflow-tooltip v-if="data.clazz.valType === '0'">
<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')" width="100" show-overflow-tooltip v-if="data.clazz.valType === '0'"/>
<el-table-column prop="remark" :label="t('flowClazz.remark')" width="70" show-overflow-tooltip/>
<el-table-column prop="sort" :label="t('flowClazz.sort')" width="70" show-overflow-tooltip/>
</el-table>
</el-form>
<el-drawer
class="flow-attr-drawer"
title="Http请求的参数信息"
direction="rtl"
append-to-body
:size="630" v-if="data.httpUrlParamsVisible"
v-model="data.httpUrlParamsVisible">
<el-form label-position="left" class="flow-attr flow-param-attr" label-width="150px">
<flow-http-param ref="flowHttpParam" :currFlowForm="props.currFlowForm" :httpParam="data.clazz"
:flowData="props.flowData"></flow-http-param>
</el-form>
</el-drawer>
</div>
</template>
<script setup lang="ts" name="FlowClazz">
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";
const { proxy } = getCurrentInstance();
const FlowHttpParam = defineAsyncComponent(() => import('./flow-http-param.vue'));
const {t} = useI18n();
const $message = useMessage();
const props = defineProps({
currFlowForm: {
type: Object,
default: null,
},
currSelect: {
type: Object,
default: null,
},
flowData: {
type: Object,
default: null,
},
clazzPlaceholder: {
type: String,
default: '',
},
flowMethods: {
type: Array,
default: [],
},
addType: {
type: String,
default: '',
}
});
const clazzData = {
clazz: null,
httpUrl: null,
httpMethod: 'GET',
httpParams: null,
methods: null,
valType: null,
varKeyVal: null,
operator: null,
varVal: null,
remark: null,
sort: 1,
}
const data = reactive({
tableFieldData: [],
allFieldPerms: [],
formFieldPerms: [],
clazz: deepClone(clazzData),
httpUrlParamsVisible: false,
moreInter: false,
existData: false,
tableData: []
})
onMounted(() => {
methods.changeTabPane(props.currSelect)
})
const methods = {
openHttpUrlParams(bool) {
if (!data.clazz.httpUrl) {
$message.warning("请先输入【Http请求地址】")
return
}
data.httpUrlParamsVisible = bool
},
handleHttpUrlBlur() {
if (!data.clazz.httpUrl) {
data.clazz.httpParams = []
return;
}
},
handleCurrentChange(row) {
if (!row) return
data.clazz = row
data.clazz.isSelectedRow = true
},
handleVarKeyVal(varKeyVal) {
let dots = varKeyVal.split('.').length - 1;
if (dots === 2) {
data.varValOperator = DIC_PROP.OPERATOR.slice(6)
} else {
data.varValOperator = DIC_PROP.OPERATOR
}
},
onAddItem(){
if (validateNull(data.formFieldPerms)) {
buildSysFieldsFormOption(data, props, $message)
}
if (data.tableFieldData.length > 0) return
let obj = {varKeyVal: '', operator: '', varVal: ''};
data.tableFieldData.push(obj);
},
addFlowNodeClazz(type) {
let clazzes = type === '0' ? props.currSelect.attributes.attrs.cdata.attrs.clazzes : props.flowData.attrs.clazzes;
if (!data.clazz.clazz && !data.clazz.httpUrl) {
$message.warning("请填写 监听类 或者 Http请求地址")
return
}
if (validateNull(data.clazz.methods)) {
$message.warning("触发时机不能为空")
return
}
if (!validateNull(clazzes) && !data.clazz.isSelectedRow) {
if (data.clazz.clazz) {
let b = methods.validateClazzHttpUrl(clazzes, 'clazz', '监听类');
if (b) return;
} else {
let b = methods.validateClazzHttpUrl(clazzes, 'httpUrl', 'Http请求地址');
if (b) return;
}
}
if (data.clazz.clazz && data.clazz.httpUrl) {
$message.error("监听类 与 Http请求地址不能同时存在")
return;
}
if (data.clazz.isSelectedRow) {
data.clazz.isSelectedRow = false
proxy.$refs.tableDataRef.setCurrentRow(null)
data.clazz = deepClone(clazzData);
} else {
let clazz = deepClone(data.clazz);
if (type === '0') props.currSelect.attributes.attrs.cdata.attrs.clazzes.unshift(clazz)
else props.flowData.attrs.clazzes.unshift(clazz)
methods.validateClazzData()
}
},
validateClazzHttpUrl(clazzes, key, errMsg){
let find = clazzes.find(f => f[key] === data.clazz[key]);
if (find) {
$message.warning("请勿重复添加 " + errMsg)
return true
}
return false
},
handleClazzDelete(index: number, row: any, type: string){
if (type === '0') props.currSelect.attributes.attrs.cdata.attrs.clazzes.splice(index, 1)
else props.flowData.attrs.clazzes.splice(index, 1)
methods.validateClazzData()
},
handleClazzValType(type?) {
if (!type) {
type = methods.initCurrVarKeyVal()
}
if (type === DIC_PROP.VAL_TYPE[2].value) {
data.varValOperator = DIC_PROP.OPERATOR
methods.onAddItem()
} else {
data.varValOperator = []
}
data.clazz.varKeyVal = null
data.clazz.operator = null
data.clazz.varVal = null
},
handleClazzVarKeyVal() {
let val = data.clazz.varKeyVal
if (!val) return
let valType = data.clazz.valType;
if (valType === DIC_PROP.VAL_TYPE[3].value && val.indexOf("#") === -1) {
data.clazz.varKeyVal = null
$message.warning("当选择SpEL模式时, SpEL表达式必须符合SpEL格式")
} else if (valType === DIC_PROP.VAL_TYPE[4].value && val.indexOf("#") === -1) {
data.clazz.varKeyVal = null
$message.warning("当选择专业模式时, 函数表达式必须符合规定的格式")
}
},
showMoreSetting() {
data.moreInter = !data.moreInter
},
validateClazzData() {
let clazzes = props.addType === '0' ? props.currSelect.attributes.attrs.cdata.attrs.clazzes : props.flowData.attrs.clazzes;
if (!clazzes) {
if (props.addType === '0') props.currSelect.attributes.attrs.cdata.attrs.clazzes = []
else props.flowData.attrs.clazzes = []
}
data.tableData.splice(0, data.tableData.length);
if (props.addType === '0') {
props.currSelect.attributes.attrs.cdata.attrs.clazzes.forEach(each => data.tableData.push(each))
} else {
props.flowData.attrs.clazzes.forEach(each => data.tableData.push(each))
}
data.existData = !validateNull(data.tableData)
},
initCurrVarKeyVal() {
if (data.tableData.length > 0) {
data.clazz.valType = data.tableData[0].valType
}
data.clazz.methods = []
return data.clazz.valType
},
changeTabPane(val) {
methods.validateClazzData()
methods.handleClazzValType()
}
}
// 监听双向绑定
watch(
() => props.currSelect,
(val) => {
if (Object.keys(val).length === 0) {
return
}
methods.changeTabPane(val)
}
);
</script>
<style lang="scss">
@import '../assets/style/flow-attr.scss';
</style>

View File

@@ -0,0 +1,527 @@
<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>

View File

@@ -0,0 +1,125 @@
<template>
<el-table :data="props.currSelect.attributes.attrs.cdata.defJob.currRunJobs" style="width: 100%">
<el-table-column type="index" :label="t('createTable.index')" width="80">
<template #header>
<el-button icon="Plus" size="small" type="primary" circle @click="onAddItem"></el-button>
</template>
<template #default="scope">
<el-button icon="Minus" size="small" type="danger" circle @click="handleDelete(scope.$index, scope.row)"
:disabled="scope.row.isConfigJob === '1'"></el-button>
</template>
</el-table-column>
<el-table-column prop="sort" :label="t('runJob.sort')" show-overflow-tooltip width="70">
<template #default="scope">
<el-input v-model="scope.row.sort" :placeholder="t('runJob.inputSortTip')"></el-input>
</template>
</el-table-column>
<el-table-column prop="jobName" :label="t('runJob.jobName')" show-overflow-tooltip width="150">
<template #default="scope">
<el-input v-model="scope.row.jobName" :placeholder="t('runJob.inputJobNameTip')"></el-input>
</template>
</el-table-column>
<el-table-column prop="jobType" :label="t('runJob.jobType')" show-overflow-tooltip width="100">
<template #default="scope">
<el-select v-model="scope.row.jobType" :placeholder="t('runJob.inputJobTypeTip')" clearable filterable
@change="handleRoleType(scope.row)">
<el-option v-for="(item, index) in DIC_PROP.JOB_USER_NONE_TYPE" :key="index" :label="item.label" :value="item.value"></el-option>
</el-select>
</template>
</el-table-column>
<el-table-column prop="roleId" :label="t('runJob.roleId')" show-overflow-tooltip width="130">
<template #default="scope">
<el-tooltip content="请输入名称进行模糊搜索" placement="top">
<el-select v-model="scope.row.roleId" :placeholder="t('runJob.inputRoleIdTip')" clearable filterable
remote :remote-method="(query) => remoteMethodAll(query, scope.row.jobType)" :reserve-keyword="false">
<el-option v-for="(item, index) in dicData.users" :key="index" :label="item.name" :value="item.userId"
v-if="scope.row.jobType === DIC_PROP.JOB_USER_TYPE[0].value"></el-option>
<el-option v-for="(item, index) in dicData.roles" :key="index" :label="item.roleName" :value="item.roleId"
v-if="scope.row.jobType === DIC_PROP.JOB_USER_TYPE[1].value"></el-option>
<el-option v-for="(item, index) in dicData.posts" :key="index" :label="item.postName" :value="item.postId"
v-if="scope.row.jobType === DIC_PROP.JOB_USER_TYPE[2].value"></el-option>
<el-option v-for="(item, index) in dicData.depts" :key="index" :label="item.name" :value="item.deptId"
v-if="scope.row.jobType === DIC_PROP.JOB_USER_TYPE[3].value"></el-option>
</el-select>
</el-tooltip>
</template>
</el-table-column>
<el-table-column prop="userId" :label="t('runJob.userId')" show-overflow-tooltip>
<template #default="scope">
<convert-name :options="dicData.userId" :value="scope.row.userId"
:valueKey="'userId'" :showKey="'name'"></convert-name>
</template>
</el-table-column>
<el-table-column prop="startTime" :label="t('runJob.startTime')" show-overflow-tooltip/>
<el-table-column prop="status" :label="t('runJob.status')" show-overflow-tooltip>
<template #default="scope">
<dict-tag :options="DIC_PROP.NODE_STATUS" :value="scope.row.status"></dict-tag>
</template>
</el-table-column>
</el-table>
</template>
<script setup lang="ts" name="FlowCurrJob">
import {useI18n} from "vue-i18n";
import {useMessage} from "/@/hooks/message";
import {onFormLoadedUrl, onLoadDicUrl, remoteMethodAllByKey} from "../../components/convert-name/convert";
import {handleChangeJobType} from "../../index";
import {DIC_PROP} from "../../support/dict-prop";
import {validateNull} from "../../../utils/validate";
import {PROP_CONST} from "../../support/prop-const";
const {proxy} = getCurrentInstance();
const {t} = useI18n();
const $message = useMessage();
const props = defineProps({
currSelect: {
type: Object,
default: null,
},
});
// 定义字典
const dicData = reactive({});
const onLoad = onLoadDicUrl();
const onFormLoaded = onFormLoadedUrl({key: "userId"}, ...PROP_CONST.LOAD_USER_ROLE);
onMounted(async () => {
// await onLoad(dicData);
await onFormLoadedCurrRunJobs()
})
const onAddItem = () => {
let jobName = props.currSelect.attributes.attrs.cdata.defJob.jobName;
let obj = {sort: 1, jobName: jobName, jobType: DIC_PROP.JOB_USER_NONE_TYPE[0].value, roleId: null, userId: null,
startTime: null, status: DIC_PROP.NODE_STATUS[0].value};
props.currSelect.attributes.attrs.cdata.defJob.currRunJobs.push(obj);
}
const handleDelete = (index: number, row: any) => {
let currRunJobs = props.currSelect.attributes.attrs.cdata.defJob.currRunJobs;
if (currRunJobs.length === 1) {
useMessage().warning("当前节点参与者至少需存在一个参与者");
return
}
props.currSelect.attributes.attrs.cdata.defJob.currRunJobs.splice(index, 1)
}
function remoteMethodAll(query: string, jobType) {
remoteMethodAllByKey(onLoad, dicData, query, jobType)
}
function onFormLoadedCurrRunJobs() {
let currRunJobs = props.currSelect.attributes.attrs.cdata.defJob.currRunJobs
if (validateNull(currRunJobs)) return
onFormLoaded(dicData, currRunJobs)
}
function handleRoleType(row) {
handleChangeJobType(dicData, row)
}
</script>
<style lang="scss">
@import '../assets/style/flow-attr.scss';
</style>

View File

@@ -0,0 +1,207 @@
<template>
<el-form label-position="left" class="flow-config-attr" label-width="160px">
<el-form-item label="查询表单Http请求地址">
<el-input class="input-attr" placeholder="可输入全路径或相对路径" v-model="props.flowData.attrs.queryOrder"
clearable>
<template #prepend>
<el-select v-model="props.flowData.attrs.queryMethod">
<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 placement="top">
<template #content>查询表单信息接口可输入全路径或相对路径</template>
<el-icon style="margin-left: 10px">
<QuestionFilled/>
</el-icon>
</el-tooltip>
</el-form-item>
<el-form-item label="更新表单Http请求地址">
<el-input class="input-attr" placeholder="可输入全路径或相对路径" v-model="props.flowData.attrs.updateOrder"
clearable>
<template #prepend>
<el-select v-model="props.flowData.attrs.updateMethod">
<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 placement="top">
<template #content>更新表单信息接口可输入全路径或相对路径</template>
<el-icon style="margin-left: 10px">
<QuestionFilled/>
</el-icon>
</el-tooltip>
</el-form-item>
<el-divider>关联表单Http请求头</el-divider>
<el-table :data="data.httpParams.filter(f => f.paramFrom === DIC_PROP.PARAM_FROM[0].value)"
border style="width: 100%; margin-bottom: 10px" max-height="500">
<el-table-column type="index" :label="t('jfI18n.operate')" width="80">
<template #header>
<el-button icon="Plus" size="small" type="primary" circle
@click="methods.onAddItem(DIC_PROP.PARAM_FROM[0].value, true)"></el-button>
</template>
<template #default="scope">
<el-button icon="Minus" size="small" type="danger" circle
@click="methods.handleHttpUrlDelete(scope.$index, scope.row)"></el-button>
</template>
</el-table-column>
<el-table-column prop="targetProp" :label="t('jfAttr.requestHeader')" show-overflow-tooltip>
<template #default="scope">
<el-input v-model="scope.row.targetProp" :placeholder="scope.row.paramValType === DIC_PROP.PARAM_VAL_TYPE[0].value ? scope.row.varKeyVal : null"
clearable></el-input>
</template>
</el-table-column>
<el-table-column prop="paramValType" :label="t('jfAttr.paramValType')" width="145px">
<template #default="scope">
<el-select v-model="scope.row.paramValType" clearable>
<el-option v-for="(item, index) in DIC_PROP.SYS_PARAM_VAL_TYPE" :key="index" :label="item.label" :value="item.value"></el-option>
</el-select>
</template>
</el-table-column>
<el-table-column prop="varKeyVal" :label="t('jfAttr.sysVarKeyVal')" show-overflow-tooltip>
<template #default="scope">
<el-select v-model="scope.row.varKeyVal"
v-if="scope.row.paramValType === DIC_PROP.PARAM_VAL_TYPE[0].value"
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>
<el-input v-else v-model="scope.row.varKeyVal" clearable> </el-input>
</template>
</el-table-column>
</el-table>
</el-form>
</template>
<script setup lang="ts" name="FlowHttpParam">
import {useI18n} from "vue-i18n";
import {useMessage} from "/@/hooks/message";
import {validateNull} from "/@/utils/validate";
import {handleFieldProp} from "../../utils/form-perm";
import {DIC_PROP} from "../../support/dict-prop";
import {PROP_CONST} from "../../support/prop-const";
import {deepClone} from "/@/utils/other";
const {proxy} = getCurrentInstance();
const {t} = useI18n();
const $message = useMessage();
const props = defineProps({
currFlowForm: {
type: Object,
default: null,
},
flowData: {
type: Object,
default: null,
},
attrConfigVisible: null
});
const data = reactive({
allFieldPerms: [],
formFieldPerms: [],
httpParams: [],
paramType: '0',
})
// 定义字典
onMounted(async () => {
if (!props.attrConfigVisible) return
methods.changeTabPane()
})
const methods = {
listFormFieldPerms() {
if (validateNull(data.formFieldPerms)) {
let formFieldPerms = deepClone(PROP_CONST.SYS_FIELDS);
handleFieldProp(formFieldPerms, null)
data.allFieldPerms = [{label: '系统字段', options: formFieldPerms}]
}
},
onAddItem(paramFrom, isAdd) {
methods.listFormFieldPerms()
let value = DIC_PROP.PARAM_VAL_TYPE[0].value;
if (data.httpParams.length > 0) {
let find = data.httpParams.filter(f => f.paramFrom === paramFrom).find(f => !f.varKeyVal || (f.paramValType !== value && !f.targetProp));
if (find) {
if (isAdd) {
if (!find.varKeyVal) {
$message.warning("请先填写 表单字段")
return
}
if (find.paramValType !== value && !find.targetProp) {
$message.warning("请先填写 请求头")
return
}
}
}
if (!isAdd) data.httpParams.splice(0, data.httpParams.length);
}
let obj = {paramFrom: paramFrom, varKeyVal: null, paramValType: value, targetProp: null};
data.httpParams.push(obj);
methods.changeHttpUrlParams()
},
handleHttpUrlDelete(index: number, row: any) {
let splice = data.httpParams.filter(f => f.paramFrom === row.paramFrom);
splice.splice(index, 1);
data.httpParams = splice
methods.changeHttpUrlParams()
},
validateHttpUrlData() {
// 兼容老版本
let httpParams = props.flowData.attrs.orderParams;
if (!httpParams) {
props.flowData.attrs.orderParams = []
}
let queryMethod = props.flowData.attrs.queryMethod;
if (!queryMethod) props.flowData.attrs.queryMethod = 'GET'
let updateMethod = props.flowData.attrs.updateMethod;
if (!updateMethod) props.flowData.attrs.updateMethod = 'PUT'
data.httpParams = props.flowData.attrs.orderParams
},
changeHttpUrlParams() {
props.flowData.attrs.orderParams = data.httpParams
},
changeTabPane() {
methods.validateHttpUrlData()
methods.listFormFieldPerms()
}
}
// 监听双向绑定
watch(
() => props.attrConfigVisible,
(val) => {
if (!val) return
methods.changeTabPane()
}
);
</script>
<style lang="scss">
@import '../assets/style/flow-attr.scss';
</style>

View File

@@ -0,0 +1,295 @@
<template>
<el-form v-if="methods.validateNode(props.currSelect)"
label-position="left" class="flow-config-attr" label-width="130px">
<el-form-item label="全部只读或可编辑" prop="formPermType">
<el-tooltip placement="top">
<template #content>1对当前节点表单的字段全部只读 全部可编辑默认全部可编辑
<br/> 2可在下方进一步约束权限如这里选择全部只读下方配置某些字段可编辑
</template>
<el-radio-group v-model="props.currSelect.attributes.attrs.cdata.attrs.formPermType">
<el-radio v-for="(item, index) in DIC_PROP.ALL_FORM_PERM_TYPE" :key="index" :label="item.value" style="width: 135px">
{{ item.label }}
</el-radio>
</el-radio-group>
</el-tooltip>
<el-tooltip placement="top" v-if="props.currSelect.attributes.attrs.cdata.attrs.formPermType">
<template #content>清空后可在下方单个字段配置权限或业务侧自行判断</template>
<el-button text type="primary" icon="delete" @click="props.currSelect.attributes.attrs.cdata.attrs.formPermType = null">
清空
</el-button>
</el-tooltip>
</el-form-item>
<el-divider>请选择表单名称</el-divider>
<el-form-item label="PC待办页面">
<el-tooltip placement="top">
<template #content>若下拉选项为空或没有期望的表单请先在节点属性-PC待办页面选择</template>
<el-select class="input-attr"
v-model="props.currSelect.attributes.attrs.cdata.attrs.formId"
@change="methods.changeNodeFormId"
clearable filterable placeholder="请选择节点属性-PC待办页面">
<template v-for="(item, index) in props.formIds">
<el-option
v-if="methods.filterNodeFormId(item)"
:key="index"
:label="item.formName+' V'+item.version"
:value="item.id">
</el-option>
</template>
</el-select>
</el-tooltip>
<el-button @click="methods.addFormPermission" type="primary" round v-if="props.isShowAdd"
style="margin-left: 10px;">新增字段
</el-button>
<el-button @click="methods.resetFormPerm" type="primary" size="small" round v-if="!props.isShowAdd"
style="margin-left: 10px;">重置权限
</el-button>
</el-form-item>
<el-divider>参与者可以操作表单字段权限</el-divider>
<el-empty description="表单字段权限列表为空" style="margin: 10px 230px" v-if="validateNull(data.formFieldPerms)">
</el-empty>
<el-form-item v-for="(item, index) in data.formFieldPerms" v-else
:label="item.label"
:key="index">
<el-input style="width: 131px; margin-right: 10px" v-model="item.label" v-if="props.isShowAdd"></el-input>
<el-input style="width: 131px; margin-right: 10px" v-model="item.prop" v-if="props.isShowAdd"></el-input>
<el-radio-group v-model="item.permType">
<el-radio v-for="(item, index) in DIC_PROP.FORM_PERM_TYPE" :key="index"
:disabled="props.currSelect.attributes.attrs.cdata.attrs.formPermType === item.value"
:label="item.value" style="width: 86px">
{{ item.label }}
</el-radio>
</el-radio-group>
<el-button @click.prevent="methods.removeFormPermission(item.prop)" type="primary" size="small" round>
删除
</el-button>
</el-form-item>
</el-form>
</template>
<script setup lang="ts" name="FlowFormPerm">
import {useI18n} from "vue-i18n";
import {useMessage} from "/@/hooks/message";
import {validateNull} from "/@/utils/validate";
import {parseWithFunctions, validateRunFlow} from "../../index";
import {buildFieldPerms, handleFormFieldPerms} from "../../utils/form-perm";
import {CommonNodeType, HighNodeType} from "../config/type";
import {listFormOption} from "/@/api/jsonflow/form-option";
import {DIC_PROP} from "../../support/dict-prop";
import {deepClone} from "/@/utils/other";
const { proxy } = getCurrentInstance();
const {t} = useI18n();
const $message = useMessage();
const props = defineProps({
currFlowForm: {
type: Object,
default: null,
},
currSelect: {
type: Object,
default: null,
},
flowData: {
type: Object,
default: null,
},
formIds: {
type: Array,
default: null,
},
isShowAdd: {
type: Boolean,
default: false,
},
});
const data = reactive({
formFieldPerms: [],
})
onMounted(() => {
methods.changeTabPane(props.currSelect)
})
const methods = {
validateCurrSelectAttrs(currSelect?) {
if (!currSelect) currSelect = props.currSelect;
if (validateNull(currSelect.attributes)) return false
return true
},
validateNode(currSelect) {
if (!methods.validateCurrSelectAttrs()) return false;
let type = currSelect.attributes.attrs.cdata.type;
return type === CommonNodeType.START || type === CommonNodeType.END || type === CommonNodeType.SERIAL || type === CommonNodeType.PARALLEL || type === HighNodeType.VIRTUAL
},
filterNodeFormId(item) {
let pcTodoUrl = props.currSelect.attributes.attrs.cdata.attrs.pcTodoUrl
if (!validateNull(pcTodoUrl)) {
return pcTodoUrl.includes(item.id)
} else return false
},
changeNodeFormId(value) {
if (!methods.validateNode(props.currSelect)) return;
if (!value) {
methods.clearPermData()
return
}
data.formFieldPerms = []
let find = props.formIds.find(f => f.id === value);
if (validateNull(find.formInfo)) {
methods.handleFormFieldPerms(find)
} else {
let formInfo = parseWithFunctions(find.formInfo, true)
buildFieldPerms(data.formFieldPerms, formInfo.widgetList);
}
methods.setNodeAttrsFormFieldPerms()
},
handleFormFieldPerms(find) {
// 无需前缀
let isReturn = handleFormFieldPerms(data, $message, find, '');
if (isReturn) return
if (find.isCurrForm === '1' && !validateNull(find.formFieldPerms)) {
methods.buildCustomFormPerm(find.formFieldPerms)
return;
}
// 已配置或全部字段
methods.handleCustomFormPerm(find.id, null, props.flowData.attrs.id , props.currSelect.id)
},
initNodeFormId() {
if (Object.keys(props.currSelect).length === 0) return
if (!methods.validateNode(props.currSelect)) return;
let formId = props.currSelect.attributes.attrs.cdata.attrs.formId;
if (!formId) {
methods.clearPermData()
return
}
// formId必存在权限
data.formFieldPerms = props.currSelect.attributes.attrs.cdata.attrs.formFieldPerms;
methods.initAddFormPermission(formId)
},
async initAddFormPermission(formId) {
let find = props.formIds.find(f => f.id === formId);
if (!find) return
let formFieldPerms = []
if (find.type === DIC_PROP.FORM_TYPE[1].value) {
if (find.isCurrForm === '1') {
formFieldPerms = find.formFieldPerms
} else {
formFieldPerms = await methods.reqCustomFormPerm(formId, DIC_PROP.FORM_DATA_TYPE[0].value, null, null)
}
}
if (find.type !== DIC_PROP.FORM_TYPE[1].value) {
if (!validateNull(find.formInfo)) {
let formInfo = parseWithFunctions(find.formInfo, true)
buildFieldPerms(formFieldPerms, formInfo.widgetList);
}
}
if (validateNull(formFieldPerms)) return
formFieldPerms.forEach(each => {
let index = data.formFieldPerms.findIndex(f => f.propId ? each.propId === f.propId : each.prop === f.prop);
if (index !== -1) {
let exist = data.formFieldPerms[index]
data.formFieldPerms.splice(index, 1);
each.permType = exist.permType
data.formFieldPerms.splice(index, 0, each);
return
}
data.formFieldPerms.push(each)
})
},
removeFormPermission(prop) {
if (data.formFieldPerms.length === 0) return
data.formFieldPerms = data.formFieldPerms.filter(f => f.prop !== prop)
methods.setNodeAttrsFormFieldPerms()
},
setNodeAttrsFormFieldPerms() {
// 相同引用
props.currSelect.attributes.attrs.cdata.attrs.formFieldPerms = data.formFieldPerms
},
addFormPermission() {
let formId = props.currSelect.attributes.attrs.cdata.attrs.formId;
if (!formId) {
$message.warning("请先选择表单名称")
return
}
if (!data.formFieldPerms) data.formFieldPerms = []
let number = data.formFieldPerms.length + 1;
data.formFieldPerms.unshift({
prop: 'propName' + number,
label: '请输入字段名称'
})
methods.setNodeAttrsFormFieldPerms()
},
async reqCustomFormPerm(formId, type, defFlowId, flowNodeId) {
if (!type) type = DIC_PROP.FORM_DATA_TYPE[1].value
// 判断流程实例独立配置
let flowInstId = validateRunFlow(props);
let resp = await listFormOption({
type: type, formType: DIC_PROP.FORM_TYPE[1].value, formId: formId,
flowInstId: flowInstId, defFlowId: defFlowId, flowNodeId: flowNodeId
}).catch(() => {
$message.error("获取系统表单字段权限失败");
})
return resp.data
},
async handleCustomFormPerm(formId, type, defFlowId, flowNodeId) {
let formFieldPerms = await methods.reqCustomFormPerm(formId, type, defFlowId, flowNodeId)
methods.buildCustomFormPerm(formFieldPerms)
},
buildCustomFormPerm(formFieldPerms) {
if (!validateNull(formFieldPerms)) {
// 不影响表单设计信息
data.formFieldPerms = deepClone(formFieldPerms)
methods.setNodeAttrsFormFieldPerms()
} else {
methods.clearPermData()
$message.warning("当前选择的系统表单无字段信息,请先在表单设计中录入")
}
},
resetFormPerm(){
let formId = props.currSelect.attributes.attrs.cdata.attrs.formId;
if (!formId) {
$message.warning("请先选择表单名称")
return
}
methods.clearPermData()
let find = props.formIds.find(f => f.id === formId);
if (find.type === DIC_PROP.FORM_TYPE[1].value) {
if (find.isCurrForm === '1' && !validateNull(find.formFieldPerms)) {
methods.buildCustomFormPerm(find.formFieldPerms)
} else {
methods.handleCustomFormPerm(formId, DIC_PROP.FORM_DATA_TYPE[0].value, null, null)
}
} else {
methods.changeNodeFormId(formId)
}
},
clearPermData() {
data.formFieldPerms = []
delete props.currSelect.attributes.attrs.cdata.attrs.formFieldPerms
},
changeTabPane(val) {
methods.initNodeFormId()
}
}
// 监听双向绑定
watch(
() => props.currSelect,
(val) => {
if (Object.keys(val).length === 0) {
return
}
methods.changeTabPane(val)
}
);
</script>
<style lang="scss">
@import '../assets/style/flow-attr.scss';
</style>

View File

@@ -0,0 +1,352 @@
<template>
<el-divider>请求头</el-divider>
<el-table :data="data.httpParams.filter(f => f.paramFrom === DIC_PROP.PARAM_FROM[0].value)"
border style="width: 100%; margin-bottom: 10px" max-height="500">
<el-table-column type="index" :label="t('jfI18n.operate')" width="80">
<template #header>
<el-button icon="Plus" size="small" type="primary" circle
@click="methods.onAddItem(DIC_PROP.PARAM_FROM[0].value, true)"></el-button>
</template>
<template #default="scope">
<el-button icon="Minus" size="small" type="danger" circle
@click="methods.handleHttpUrlDelete(scope.$index, scope.row)"></el-button>
</template>
</el-table-column>
<el-table-column prop="targetProp" :label="t('jfAttr.requestHeader')" show-overflow-tooltip>
<template #default="scope">
<el-input v-model="scope.row.targetProp" :placeholder="scope.row.paramValType === DIC_PROP.PARAM_VAL_TYPE[0].value ? scope.row.varKeyVal : null"
clearable></el-input>
</template>
</el-table-column>
<el-table-column prop="paramValType" :label="t('jfAttr.paramValType')" width="145px">
<template #default="scope">
<el-select v-model="scope.row.paramValType" clearable>
<el-option v-for="(item, index) in DIC_PROP.PARAM_VAL_TYPE" :key="index" :label="item.label" :value="item.value"></el-option>
</el-select>
</template>
</el-table-column>
<el-table-column prop="varKeyVal" :label="t('jfAttr.varKeyVal')" show-overflow-tooltip>
<template #default="scope">
<el-select v-model="scope.row.varKeyVal"
v-if="scope.row.paramValType === DIC_PROP.PARAM_VAL_TYPE[0].value"
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>
<el-input v-else v-model="scope.row.varKeyVal" clearable> </el-input>
</template>
</el-table-column>
</el-table>
<el-divider>请求参数</el-divider>
<el-form-item label="参数类型 :">
<el-radio-group v-model="data.paramType">
<el-radio v-for="(item, index) in DIC_PROP.PARAM_TYPES" :key="index" :label="item.value"
style="width: 56px" @change="methods.handleParamType">
{{ item.label }}
</el-radio>
</el-radio-group>
<el-tooltip placement="top">
<template #content> 表示以指定的参数类型传递请求参数 </template>
<el-icon style="margin-left: 20px">
<QuestionFilled/>
</el-icon>
</el-tooltip>
</el-form-item>
<el-table :data="data.httpParams.filter(f => f.paramFrom === DIC_PROP.PARAM_FROM[1].value)"
border style="width: 100%; margin-bottom: 10px" max-height="500">
<el-table-column type="index" :label="t('jfI18n.operate')" width="80">
<template #header>
<el-button icon="Plus" size="small" type="primary" circle
@click="methods.onAddItem(DIC_PROP.PARAM_FROM[1].value, true)"></el-button>
</template>
<template #default="scope">
<el-button icon="Minus" size="small" type="danger" circle
@click="methods.handleHttpUrlDelete(scope.$index, scope.row)"></el-button>
</template>
</el-table-column>
<el-table-column prop="targetProp" :label="t('jfAttr.requestParam')" show-overflow-tooltip>
<template #default="scope">
<el-input v-model="scope.row.targetProp" :placeholder="scope.row.paramValType === DIC_PROP.PARAM_VAL_TYPE[0].value ? scope.row.varKeyVal : null"
clearable></el-input>
</template>
</el-table-column>
<el-table-column prop="paramValType" :label="t('jfAttr.paramValType')" width="145px">
<template #default="scope">
<el-select v-model="scope.row.paramValType" clearable>
<el-option v-for="(item, index) in DIC_PROP.PARAM_VAL_TYPE" :key="index" :label="item.label" :value="item.value"></el-option>
</el-select>
</template>
</el-table-column>
<el-table-column prop="varKeyVal" :label="t('jfAttr.varKeyVal')" show-overflow-tooltip>
<template #default="scope">
<el-select v-model="scope.row.varKeyVal"
v-if="scope.row.paramValType === DIC_PROP.PARAM_VAL_TYPE[0].value"
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>
<el-input v-else v-model="scope.row.varKeyVal" clearable> </el-input>
</template>
</el-table-column>
</el-table>
<el-divider>返回值</el-divider>
<div v-if="props.httpParamType === DIC_PROP.PARAM_RULE_TYPE[0].value" style="margin: 10px 13px">
<span style="color: #409EFF;font-size: 14px">: 当前Http请求的返回值可以为对象 数组{{ PROP_CONST.TEXT_DESC.condMethodExplain5 }}</span>
</div>
<div v-if="props.httpParamType === DIC_PROP.PARAM_RULE_TYPE[8].value" style="margin: 10px 13px">
<span style="color: #409EFF;font-size: 14px">: 当前Http请求的返回值可以为对象 数组{{ PROP_CONST.TEXT_DESC.condMethodExplain6 }}</span>
</div>
<div v-if="props.httpParamType === DIC_PROP.PARAM_RULE_TYPE[1].value" style="margin: 10px 13px">
<span style="color: #409EFF;font-size: 14px">: 当前Http请求的返回值为字符串 1 ( 满足 ) 0 ( 不满足 )</span>
</div>
<el-table :data="data.httpParams.filter(f => f.paramFrom === DIC_PROP.PARAM_FROM[2].value)"
v-if="props.httpParamType !== DIC_PROP.PARAM_RULE_TYPE[0].value && props.httpParamType !== DIC_PROP.PARAM_RULE_TYPE[1].value
&& props.httpParamType !== DIC_PROP.PARAM_RULE_TYPE[8].value"
border style="width: 100%; margin-bottom: 10px" max-height="500">
<el-table-column type="index" :label="t('jfI18n.operate')" width="80">
<template #header>
<el-button icon="Plus" size="small" type="primary" circle
@click="methods.onAddItem(DIC_PROP.PARAM_FROM[2].value, true)"></el-button>
</template>
<template #default="scope">
<el-button icon="Minus" size="small" type="danger" circle
@click="methods.handleHttpUrlDelete(scope.$index, scope.row)"></el-button>
</template>
</el-table-column>
<el-table-column prop="targetProp" :label="t('jfAttr.returnParam')" show-overflow-tooltip>
<template #default="scope">
<el-input v-model="scope.row.targetProp"
:placeholder="scope.row.paramValType === DIC_PROP.PARAM_VAL_TYPE[0].value ? scope.row.varKeyVal : null"
clearable></el-input>
</template>
</el-table-column>
<el-table-column prop="paramValType" :label="t('jfAttr.paramValType')" width="145px">
<template #default="scope">
<el-switch
v-model="scope.row.paramValType"
:active-value="DIC_PROP.PARAM_VAL_TYPE[0].value"
:active-text="DIC_PROP.PARAM_VAL_TYPE[0].label"
:inactive-value="DIC_PROP.PARAM_VAL_TYPE[2].value"
:inactive-text="DIC_PROP.PARAM_VAL_TYPE[2].label"
inline-prompt>
</el-switch>
</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"
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>
</template>
<script setup lang="ts" name="FlowHttpParam">
import {useI18n} from "vue-i18n";
import {useMessage} from "/@/hooks/message";
import {validateNull} from "/@/utils/validate";
import {buildSysFieldsFormOption} from "../../utils/form-perm";
import {DIC_PROP} from "../../support/dict-prop";
const {proxy} = getCurrentInstance();
const {t} = useI18n();
const $message = useMessage();
const props = defineProps({
currFlowForm: {
type: Object,
default: null,
},
httpParam: {
type: Object,
default: null,
},
httpParamType: {
type: String,
default: null,
},
flowData: {
type: Object,
default: null,
},
});
const data = reactive({
allFieldPerms: [],
formFieldPerms: [],
httpParams: [],
paramType: '0',
})
// 定义字典
onMounted(async () => {
methods.changeTabPane(props.httpParam)
})
const methods = {
initParamType() {
let find = data.httpParams.find(f => f.paramFrom === DIC_PROP.PARAM_FROM[1].value);
if (find) data.paramType = find.paramType
},
handleParamType(val) {
let splice = data.httpParams.filter(f => f.paramFrom === DIC_PROP.PARAM_FROM[1].value);
splice.forEach(each => each.paramType = val)
let res = data.httpParams.filter(f => f.paramFrom === DIC_PROP.PARAM_FROM[0].value);
let res2 = data.httpParams.filter(f => f.paramFrom === DIC_PROP.PARAM_FROM[2].value);
splice.push(...res)
splice.push(...res2)
data.httpParams = splice
methods.changeHttpUrlParams()
},
listFormFieldPerms() {
if (validateNull(data.formFieldPerms)) {
buildSysFieldsFormOption(data, props, $message)
}
},
onAddItem(paramFrom, isAdd) {
methods.listFormFieldPerms()
let value = DIC_PROP.PARAM_VAL_TYPE[0].value;
if (data.httpParams.length > 0) {
let find = data.httpParams.filter(f => f.paramFrom === paramFrom).find(f => !f.varKeyVal || (f.paramValType !== value && !f.targetProp));
if (find) {
if (isAdd) {
if (!find.varKeyVal) {
$message.warning("请先填写 表单字段")
return
}
if (find.paramValType !== value && !find.targetProp) {
if (DIC_PROP.PARAM_FROM[0].value === paramFrom) {
$message.warning("请先填写 请求头")
} else if (DIC_PROP.PARAM_FROM[1].value === paramFrom) {
$message.warning("请先填写 请求参数")
} else {
$message.warning("请先填写 返回值")
}
return
}
}
}
if (!isAdd) data.httpParams.splice(0, data.httpParams.length);
}
let obj = {paramFrom: paramFrom, varKeyVal: null, paramValType: value, targetProp: null};
if (paramFrom === DIC_PROP.PARAM_FROM[1].value) {
obj.paramType = data.paramType
}
data.httpParams.push(obj);
methods.changeHttpUrlParams()
},
handleHttpUrlDelete(index: number, row: any) {
let splice = data.httpParams.filter(f => f.paramFrom === row.paramFrom);
splice.splice(index, 1);
if (DIC_PROP.PARAM_FROM[0].value === row.paramFrom) {
let res = data.httpParams.filter(f => f.paramFrom === DIC_PROP.PARAM_FROM[1].value);
let res2 = data.httpParams.filter(f => f.paramFrom === DIC_PROP.PARAM_FROM[2].value);
splice.push(...res)
splice.push(...res2)
} else if (DIC_PROP.PARAM_FROM[1].value === row.paramFrom) {
let res = data.httpParams.filter(f => f.paramFrom === DIC_PROP.PARAM_FROM[0].value);
let res2 = data.httpParams.filter(f => f.paramFrom === DIC_PROP.PARAM_FROM[2].value);
splice.push(...res)
splice.push(...res2)
} else if (DIC_PROP.PARAM_FROM[2].value === row.paramFrom) {
let res = data.httpParams.filter(f => f.paramFrom === DIC_PROP.PARAM_FROM[0].value);
let res2 = data.httpParams.filter(f => f.paramFrom === DIC_PROP.PARAM_FROM[1].value);
splice.push(...res)
splice.push(...res2)
}
data.httpParams = splice
methods.changeHttpUrlParams()
},
validateHttpUrlData() {
// 兼容老版本
let httpParams = props.httpParam.httpParams;
if (!httpParams) {
props.httpParam.httpParams = []
}
data.httpParams = props.httpParam.httpParams
},
changeHttpUrlParams() {
props.httpParam.httpParams = data.httpParams
},
changeTabPane(val) {
methods.validateHttpUrlData()
methods.listFormFieldPerms()
methods.initParamType()
}
}
// 监听双向绑定
watch(
() => props.httpParam,
(val) => {
if (Object.keys(val).length === 0) {
return
}
methods.changeTabPane(val)
}
);
</script>
<style lang="scss">
@import '../assets/style/flow-attr.scss';
</style>

View File

@@ -0,0 +1,360 @@
<template>
<div>
<el-tabs class="flow-attr"
v-model="data.activeKey">
<el-tab-pane name="flow-method">
<template #label>
<div>
<el-icon style="vertical-align: middle;margin-right: 3px">
<User/>
</el-icon>
<span style="vertical-align: middle;">选择审批对象</span>
</div>
</template>
<el-form label-position="left" class="flow-config-attr" label-width="170px">
<div style="margin: 5px 20px">
<span style="color: red;font-size: 14px">: 当前审批对象可自定义任意扩展(只需写好接口即可), 满足您分配参与者复杂的场景</span>
</div>
<el-divider> 审批对象设置 </el-divider>
<el-form-item label="审批对象类型">
<el-radio-group style="width: 283px" @change="methods.handleUserKeyValFrom"
v-model="data.userKeyValFrom">
<el-radio v-for="(item, index) in DIC_PROP.FLOW_METHOD_TYPE" :key="index" :label="item.value"
:disabled="item.value === '-1'">
{{ item.label }}
</el-radio>
</el-radio-group>
</el-form-item>
<template v-if="data.userKeyValFrom === '0'">
<div style="margin-left: 15px"> 发起人本人将作为审批人</div>
</template>
<template v-if="data.userKeyValFrom === '1'">
<el-divider> </el-divider>
<el-form-item label="谁的主管">
<el-tooltip content="请输入用户名称进行模糊搜索" placement="top">
<el-select class="input-attr"
v-model="data.whoseLeader" @change="methods.changeWhoseLeader"
clearable filterable
remote :remote-method="remoteMethod" :reserve-keyword="false">
<template v-for="(item, index) in dicData.users" :key="index">
<el-option :label="item.name"
:value="item.userId">
</el-option>
</template>
</el-select>
</el-tooltip>
<el-tooltip placement="top" content="用于指定哪个用户的部门主管">
<el-icon style="margin-left: 10px"><QuestionFilled /></el-icon>
</el-tooltip>
</el-form-item>
<el-divider> </el-divider>
<el-form-item label="指定主管">
<div>
<span>&lt;{{ data.whoseLeaderName }} 用户&gt;的第 &nbsp</span>
<el-input-number :min="1" :max="20" :step="1" style="width: 150px"
v-model="data.leaderLevel"></el-input-number>
<span> &nbsp级主管</span>
<div style="color: #409EFF; font-size: small;">👉 指定主管为 {{ data.leaderLevel }} 级主管</div>
</div>
</el-form-item>
<el-divider> </el-divider>
<el-form-item label="提取规则">
<el-radio-group style="width: 283px"
v-model="data.levelExtract">
<el-radio v-for="(item, index) in EXTRACTS.SINGLE_EXTRACT" :key="index" :label="item.value">
{{ item.label }}
</el-radio>
</el-radio-group>
</el-form-item>
</template>
<template v-if="data.userKeyValFrom === '2'">
<el-divider> </el-divider>
<el-form-item label="谁的主管">
<el-tooltip content="请输入用户名称进行模糊搜索" placement="top">
<el-select class="input-attr"
v-model="data.whoseLeader" @change="methods.changeWhoseLeader"
clearable filterable
remote :remote-method="remoteMethod" :reserve-keyword="false">
<template v-for="(item, index) in dicData.users" :key="index">
<el-option :label="item.name"
:value="item.userId">
</el-option>
</template>
</el-select>
</el-tooltip>
<el-tooltip placement="top" content="用于指定哪个用户的部门主管">
<el-icon style="margin-left: 10px"><QuestionFilled /></el-icon>
</el-tooltip>
</el-form-item>
<el-divider> </el-divider>
<el-form-item label="审批终点">
<el-radio-group style="width: 200px" v-model="data.auditEndpoint">
<el-radio label="0">直到最上层主管</el-radio>
<el-radio label="1">不超过&lt;{{ data.whoseLeaderName }} 用户&gt;</el-radio>
</el-radio-group>
<div class="audit-endpoint">
&nbsp<el-input-number :min="1" :max="20" :step="1" style="width: 80px" controls-position="right"
v-model="data.leaderLevel"></el-input-number>&nbsp级主管
</div>
</el-form-item>
<el-divider> </el-divider>
<el-form-item label="依次审批顺序">
<el-tooltip content="当节点属性【多人审批方式】为依次审批时,审批顺序由选择的顺序决定" placement="bottom">
<el-radio-group style="width: 200px" v-model="data.seqAuditSort">
<el-radio label="0">下级到上级</el-radio>
<el-radio label="1">上级到下级</el-radio>
</el-radio-group>
</el-tooltip>
<el-button v-if="data.seqAuditSort" text type="primary" icon="delete" @click="data.seqAuditSort = null">
清空
</el-button>
</el-form-item>
<el-divider> </el-divider>
<el-form-item label="提取规则">
<el-radio-group style="width: 283px"
v-model="data.levelExtract">
<el-radio v-for="(item, index) in EXTRACTS.MULTI_EXTRACT" :key="index" :label="item.value">
{{ item.label }}
</el-radio>
</el-radio-group>
<el-tooltip placement="top" content="必须保证最终至少存在一个主管">
<el-icon class="audit-endpoint-extract"><QuestionFilled /></el-icon>
</el-tooltip>
</el-form-item>
</template>
<template v-if="data.userKeyValFrom === '3'">
<el-divider> </el-divider>
<el-form-item label="哪个部门">
<el-tooltip content="请输入部门名称进行模糊搜索" placement="top">
<el-select class="input-attr"
v-model="data.appointDeptId"
clearable filterable
remote :remote-method="remoteMethodDept" :reserve-keyword="false">
<template v-for="(item, index) in dicData.depts" :key="index">
<el-option :label="item.name"
:value="item.deptId">
</el-option>
</template>
</el-select>
</el-tooltip>
<el-tooltip placement="top" content="用于指定哪个部门的主管">
<el-icon style="margin-left: 10px"><QuestionFilled /></el-icon>
</el-tooltip>
</el-form-item>
<el-divider> </el-divider>
<el-form-item label="提取规则">
<el-radio-group style="width: 283px"
v-model="data.levelExtract">
<el-radio v-for="(item, index) in EXTRACTS.DEPT_EXTRACT" :key="index" :label="item.value">
{{ item.label }}
</el-radio>
</el-radio-group>
</el-form-item>
</template>
<template v-if="data.userKeyValFrom === '4'">
<el-divider> </el-divider>
<el-form-item label="表单字段">
<el-select v-model="data.userKeyVal"
clearable
filterable>
<el-option
v-for="(item, index) in data.formFieldPerms"
:key="index"
:label="item.label"
:value="item.prop">
</el-option>
</el-select>
</el-form-item>
</template>
<el-button
type="primary" round style="margin-left: 185px; margin-top: 50px; width: 200px"
@click="methods.confirmMethods">
确定
</el-button>
</el-form>
</el-tab-pane>
<el-tab-pane name="flow-rule">
<template #label>
<div>
<el-icon style="vertical-align: middle;margin-right: 3px">
<Setting/>
</el-icon>
<span style="vertical-align: middle;">设置审批规则</span>
</div>
</template>
<el-form label-position="left" class="flow-config-attr" label-width="150px">
<flow-user-rule :currSelect="props.currSelect" :currFlowForm="props.currFlowForm" :flowData="props.flowData"></flow-user-rule>
</el-form>
</el-tab-pane>
<el-tab-pane name="flow-curr-job" v-if="methods.validateNodeJobCurrJob()">
<template #label>
<div>
<el-icon style="vertical-align: middle;margin-right: 3px">
<Setting/>
</el-icon>
<span style="vertical-align: middle;">节点参与者列表</span>
</div>
</template>
<el-form label-position="left" class="flow-config-attr" label-width="150px">
<flow-curr-job :currSelect="props.currSelect"></flow-curr-job>
</el-form>
</el-tab-pane>
</el-tabs>
</div>
</template>
<script setup lang="ts" name="FlowMethod">
import {useI18n} from "vue-i18n";
import {useMessage} from "/@/hooks/message";
import {validateNull} from "/@/utils/validate";
import {onFormLoadedUrl, onLoadDicUrl, remoteMethodByKey} from "/@/flow/components/convert-name/convert";
import {validateListFormOption} from "../../utils/form-perm";
import {parseUserKeyValName, revParseUserKeyValName, revParseWhoseLeaderName} from "./index";
const { proxy } = getCurrentInstance();
const FlowUserRule = defineAsyncComponent(() => import('./flow-user-rule.vue'));
const FlowCurrJob = defineAsyncComponent(() => import('./flow-curr-job.vue'));
const $emit = defineEmits(["openFlowMethods"]);
const {t} = useI18n();
const $message = useMessage();
const props = defineProps({
currFlowForm: {
type: Object,
default: null,
},
currSelect: {
type: Object,
default: {},
},
flowData: {
type: Object,
default: null,
},
});
const EXTRACTS = {
SINGLE_EXTRACT: [{
label: '无主管时由上级主管代审批',
value: '0'
}, {
label: '无主管时[报错]处理',
value: '1'
}],
MULTI_EXTRACT: [{
label: '无主管时由上级主管代审批,直到满足等级的人数',
value: '0'
}, {
label: '无主管时[按空]处理',
value: '1'
}],
DEPT_EXTRACT: [{
label: '无主管时由上级主管代审批',
value: '0'
}, {
label: '无主管时[报错]处理',
value: '1'
}],
}
const data = reactive({
activeKey: 'flow-method',
formFieldPerms: [],
userKeyValFrom: '1',
whoseLeader: null,
whoseLeaderName: '请先指定',
leaderLevel: 1,
auditEndpoint: '0',
seqAuditSort: null,
levelExtract: '0',
appointDeptId: null,
userKeyVal: null,
})
// 定义字典
const dicData = reactive({});
const onLoad = onLoadDicUrl();
const onFormLoaded = onFormLoadedUrl({key: "users", field: "whoseLeader"}, {key: "depts", field: "appointDeptId"});
onMounted(async () => {
// await onLoad(dicData);
methods.changeTabPane(props.currSelect)
})
function remoteMethodDept(query: string) {
remoteMethodByKey(query, onLoad, dicData, 'deptName', "depts")
}
async function remoteMethod(query: string) {
await remoteMethodByKey(query, onLoad, dicData, 'userName', "users")
revParseWhoseLeaderName(data, dicData)
}
const methods = {
handleUserKeyValFrom(type) {
if (type !== '4' || !validateNull(data.formFieldPerms)) return
validateListFormOption(data, props, $message)
},
confirmMethods() {
parseUserKeyValName(props, data, methods)
$emit("openFlowMethods", false);
},
$message(type) {
if (type === 'whoseLeader') $message.warning('请选择谁的主管');
if (type === 'appointDeptId') $message.warning('请选择指定部门');
},
changeWhoseLeader(userId) {
if (!userId) {
data.whoseLeaderName = '请先指定'
return
}
data.whoseLeaderName = dicData.users.find(f => f.userId === userId).name;
},
validateNodeJobCurrJob() {
return !validateNull(props.currSelect.attributes) && props.currSelect.attributes.attrs.cdata.defJob
&& !validateNull(props.currSelect.attributes.attrs.cdata.defJob.currRunJobs);
},
async changeTabPane(val) {
if (Object.keys(val).length === 0) {
data.activeKey = ''
return
}
// 反解析出已配置项
await revParseUserKeyValName(props, data, dicData, methods)
await onFormLoaded(dicData, data)
revParseWhoseLeaderName(data, dicData)
},
}
// 监听双向绑定
watch(
() => props.currSelect,
(val) => {
if (validateNull(val) || Object.keys(val).length === 0) {
data.activeKey = ''
return
}
methods.changeTabPane(val)
}
);
</script>
<style lang="scss">
@import '../assets/style/flow-attr.scss';
</style>

View File

@@ -0,0 +1,810 @@
<template>
<div>
<el-tabs class="flow-attr"
v-model="data.activeKey">
<el-tab-pane name="flow-rule">
<template #label>
<div>
<el-icon style="vertical-align: middle;margin-right: 3px">
<Setting/>
</el-icon>
<span style="vertical-align: middle;">设置路由规则</span>
</div>
</template>
<el-form label-position="left" class="flow-config-attr" label-width="150px">
<div style="margin: 5px 15px"><span
style="color: red;font-size: 14px">:可根据接口返回值 表单数据的字段值配置对应的路由动作可自定义随意扩展</span>
</div>
<el-form-item label="条件组关系">
<el-switch
v-model="data.cond.groupsType" @change="methods.changeGroupsType"
active-value="0"
inactive-value="1"
inactive-text=""
active-text="" disabled>
</el-switch>
<el-tooltip placement="top">
<template #content>一个组决定一个路由动作多条件组满足时只有开启下一节点驳回到其他节点可以累加路由动作</template>
<el-icon style="margin-left: 10px">
<QuestionFilled/>
</el-icon>
</el-tooltip>
</el-form-item>
<el-form-item label="路由规则模式">
<el-radio-group style="width: 313px" @change="methods.handleCondValType" :disabled="data.existData"
v-model="data.cond.valType">
<el-radio v-for="(item, index) in [DIC_PROP.VAL_TYPE[2], DIC_PROP.VAL_TYPE[4], DIC_PROP.VAL_TYPE[5]]" :key="index" :label="item.value"
style="width: 75px">
{{ item.label }}
</el-radio>
</el-radio-group>
<el-tooltip placement="top">
<template #content>若无法切换模式请先清空条件组列表</template>
<el-icon>
<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="index"
: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>
<el-form-item label="路由动作">
<el-select v-model="data.cond.routeAction" @change="methods.changeRouteAction(data.cond)">
<el-option v-for="(item, index) in DIC_PROP.ROUTE_ACTIONS" :key="index" :disabled="item.value.indexOf('_define_') !== -1"
:label="item.label" :value="item.value"></el-option>
</el-select>
</el-form-item>
<el-form-item label="路由到哪个节点" v-if="data.cond.routeAction === DIC_PROP.ROUTE_ACTIONS[1].value">
<el-select class="input-attr" v-model="data.cond.toFlowNodeId"
clearable filterable>
<template v-for="(item, index) in data.toFlowNodeIds" :key="index">
<el-option :label="item.nodeName"
:value="item.id">
</el-option>
</template>
</el-select>
<el-tooltip placement="top" content="组内条件满足时,路由到哪个节点(注意:若节点参与者为空,可指定默认的参与者)">
<el-icon style="margin-left: 10px"><QuestionFilled /></el-icon>
</el-tooltip>
</el-form-item>
<template v-if="data.cond.routeAction === DIC_PROP.ROUTE_ACTIONS[1].value">
<el-form-item label="参与者类型">
<el-tooltip placement="top" content="若节点参与者为空,可指定默认的参与者">
<el-select v-model="data.cond.jobType" @change="methods.changeJobType(data.cond)" clearable>
<el-option v-for="(item, index) in DIC_PROP.JOB_USER_TYPE" :key="index" :label="item.label" :value="item.value"></el-option>
</el-select>
</el-tooltip>
</el-form-item>
<el-form-item label="哪个人来审批" v-if="data.cond.jobType === DIC_PROP.JOB_USER_TYPE[0].value">
<el-tooltip content="请输入用户名称进行模糊搜索" placement="top">
<el-select class="input-attr"
v-model="data.cond.roleId"
clearable filterable
remote :remote-method="methodsRemote.remoteMethodUser2" :reserve-keyword="false">
<template v-for="(item, index) in dicData.users2" :key="index">
<el-option :label="item.name"
:value="item.userId">
</el-option>
</template>
</el-select>
</el-tooltip>
<el-tooltip placement="top" content="组内条件满足时,由哪个人来审批">
<el-icon style="margin-left: 10px"><QuestionFilled /></el-icon>
</el-tooltip>
</el-form-item>
<el-form-item label="哪个角色审批" v-if="data.cond.jobType === DIC_PROP.JOB_USER_TYPE[1].value">
<el-tooltip content="请输入角色名称进行模糊搜索" placement="top">
<el-select class="input-attr" v-model="data.cond.roleId"
filterable
clearable
remote :remote-method="methodsRemote.remoteMethodRole2" :reserve-keyword="false">
<el-option
v-for="(item, index) in dicData.roles2"
:key="index"
:label="item.roleName"
:value="item.roleId">
</el-option>
</el-select>
</el-tooltip>
<el-tooltip placement="top" content="组内条件满足时,由哪个角色来审批">
<el-icon style="margin-left: 10px"><QuestionFilled /></el-icon>
</el-tooltip>
</el-form-item>
<el-form-item label="哪个岗位审批" v-if="data.cond.jobType === DIC_PROP.JOB_USER_TYPE[2].value">
<el-tooltip content="请输入岗位名称进行模糊搜索" placement="top">
<el-select class="input-attr" v-model="data.cond.roleId"
filterable
clearable
remote :remote-method="methodsRemote.remoteMethodPost2" :reserve-keyword="false">
<el-option
v-for="(item, index) in dicData.posts2"
:key="index"
:label="item.postName"
:value="item.postId">
</el-option>
</el-select>
</el-tooltip>
<el-tooltip placement="top" content="组内条件满足时,由哪个岗位来审批">
<el-icon style="margin-left: 10px"><QuestionFilled /></el-icon>
</el-tooltip>
</el-form-item>
<el-form-item label="哪个部门审批" v-if="data.cond.jobType === DIC_PROP.JOB_USER_TYPE[3].value">
<el-tooltip content="请输入部门名称进行模糊搜索" placement="top">
<el-select class="input-attr" v-model="data.cond.roleId"
filterable
clearable
remote :remote-method="methodsRemote.remoteMethodDept2" :reserve-keyword="false">
<el-option
v-for="(item, index) in dicData.depts2"
:key="index"
:label="item.name"
:value="item.deptId">
</el-option>
</el-select>
</el-tooltip>
<el-tooltip placement="top" content="组内条件满足时,由哪个部门审批">
<el-icon style="margin-left: 10px"><QuestionFilled /></el-icon>
</el-tooltip>
</el-form-item>
</template>
<el-form-item label="驳回到哪个节点" v-if="data.cond.routeAction === DIC_PROP.ROUTE_ACTIONS[3].value">
<el-select class="input-attr" v-model="data.cond.toFlowNodeId"
clearable filterable>
<template v-for="(item, index) in data.toFlowNodeIds" :key="index">
<el-option :label="item.nodeName"
:value="item.id">
</el-option>
</template>
</el-select>
<el-tooltip placement="top" content="组内条件满足时,驳回到哪个节点(注意:必须是已审批过的节点)">
<el-icon style="margin-left: 10px"><QuestionFilled /></el-icon>
</el-tooltip>
</el-form-item>
</template>
<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>采用表达式取值 ( 以下两种方式均支持自定义任意扩展 ), 值可以为对象 数组, 满足您复杂路由的场景:
<br />{{ PROP_CONST.TEXT_DESC.condUserExplain }}
<br />{{ PROP_CONST.TEXT_DESC.condMethodExplain3 }}, 返回值可以为对象 数组{{ PROP_CONST.TEXT_DESC.condMethodExplain6 }}
<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">: 当前函数表达式的返回值可以为对象 数组{{ PROP_CONST.TEXT_DESC.condMethodExplain6 }}</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[8].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%; margin-bottom: 10px" 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-form-item label="路由动作">
<el-select v-model="item.routeAction" @change="methods.changeRouteAction(item)">
<el-option v-for="(item, index) in DIC_PROP.ROUTE_ACTIONS" :key="index" :disabled="item.value.indexOf('_define_') !== -1"
:label="item.label" :value="item.value"></el-option>
</el-select>
</el-form-item>
<el-form-item label="路由到哪个节点" v-if="item.routeAction === DIC_PROP.ROUTE_ACTIONS[1].value">
<el-select class="input-attr" v-model="item.toFlowNodeId"
clearable filterable>
<template v-for="(item, index) in data.toFlowNodeIds" :key="index">
<el-option :label="item.nodeName"
:value="item.id">
</el-option>
</template>
</el-select>
<el-tooltip placement="top" content="组内条件满足时,路由到哪个节点(注意:若节点参与者为空,可指定默认的参与者)">
<el-icon style="margin-left: 10px"><QuestionFilled /></el-icon>
</el-tooltip>
</el-form-item>
<template v-if="item.routeAction === DIC_PROP.ROUTE_ACTIONS[1].value">
<el-form-item label="参与者类型">
<el-tooltip placement="top" content="若节点参与者为空,可指定默认的参与者">
<el-select v-model="item.jobType" @change="methods.changeJobType(item)" clearable>
<el-option v-for="(item, index) in DIC_PROP.JOB_USER_TYPE" :key="index" :label="item.label" :value="item.value"></el-option>
</el-select>
</el-tooltip>
</el-form-item>
<el-form-item label="哪个人来审批" v-if="item.jobType === DIC_PROP.JOB_USER_TYPE[0].value">
<el-tooltip content="请输入用户名称进行模糊搜索" placement="top">
<el-select class="input-attr"
v-model="item.roleId"
clearable filterable
remote :remote-method="methodsRemote.remoteMethodUser" :reserve-keyword="false">
<template v-for="(item, index) in dicData.users" :key="index">
<el-option :label="item.name"
:value="item.userId">
</el-option>
</template>
</el-select>
</el-tooltip>
<el-tooltip placement="top" content="组内条件满足时,由哪个人来审批">
<el-icon style="margin-left: 10px"><QuestionFilled /></el-icon>
</el-tooltip>
</el-form-item>
<el-form-item label="哪个角色审批" v-if="item.jobType === DIC_PROP.JOB_USER_TYPE[1].value">
<el-tooltip content="请输入角色名称进行模糊搜索" placement="top">
<el-select class="input-attr" v-model="item.roleId"
filterable
clearable
remote :remote-method="methodsRemote.remoteMethodRole" :reserve-keyword="false">
<el-option
v-for="(item, index) in dicData.roles"
:key="index"
:label="item.roleName"
:value="item.roleId">
</el-option>
</el-select>
</el-tooltip>
<el-tooltip placement="top" content="组内条件满足时,由哪个角色来审批">
<el-icon style="margin-left: 10px"><QuestionFilled /></el-icon>
</el-tooltip>
</el-form-item>
<el-form-item label="哪个岗位审批" v-if="item.jobType === DIC_PROP.JOB_USER_TYPE[2].value">
<el-tooltip content="请输入岗位名称进行模糊搜索" placement="top">
<el-select class="input-attr" v-model="item.roleId"
filterable
clearable
remote :remote-method="methodsRemote.remoteMethodPost" :reserve-keyword="false">
<el-option
v-for="(item, index) in dicData.posts"
:key="index"
:label="item.postName"
:value="item.postId">
</el-option>
</el-select>
</el-tooltip>
<el-tooltip placement="top" content="组内条件满足时,由哪个岗位来审批">
<el-icon style="margin-left: 10px"><QuestionFilled /></el-icon>
</el-tooltip>
</el-form-item>
<el-form-item label="哪个部门审批" v-if="item.jobType === DIC_PROP.JOB_USER_TYPE[3].value">
<el-tooltip content="请输入部门名称进行模糊搜索" placement="top">
<el-select class="input-attr" v-model="item.roleId"
filterable
clearable
remote :remote-method="methodsRemote.remoteMethodDept" :reserve-keyword="false">
<el-option
v-for="(item, index) in dicData.depts"
:key="index"
:label="item.name"
:value="item.deptId">
</el-option>
</el-select>
</el-tooltip>
<el-tooltip placement="top" content="组内条件满足时,由哪个部门审批">
<el-icon style="margin-left: 10px"><QuestionFilled /></el-icon>
</el-tooltip>
</el-form-item>
</template>
<el-form-item label="驳回到哪个节点" v-if="item.routeAction === DIC_PROP.ROUTE_ACTIONS[3].value">
<el-select class="input-attr" v-model="item.toFlowNodeId"
clearable filterable>
<template v-for="(item, index) in data.toFlowNodeIds" :key="index">
<el-option :label="item.nodeName"
:value="item.id">
</el-option>
</template>
</el-select>
<el-tooltip placement="top" content="组内条件满足时,驳回到哪个节点(注意:必须是已审批过的节点)">
<el-icon style="margin-left: 10px"><QuestionFilled /></el-icon>
</el-tooltip>
</el-form-item>
</el-collapse-item>
</el-collapse>
</template>
</template>
</el-form>
</el-tab-pane>
</el-tabs>
</div>
</template>
<script setup lang="ts" name="FlowMethod">
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 {
onFormLoadedUrl,
onLoadDicUrl,
initRemoteMethodAllByKey
} from "../../components/convert-name/convert";
import {PROP_CONST} from "../../support/prop-const";
import {handleChangeJobType} from "../../index";
import {validateNodeType} from "./index";
const { proxy } = getCurrentInstance();
const FlowHttpParam = defineAsyncComponent(() => import('./flow-http-param.vue'));
const $emit = defineEmits(["openFlowRoutes"]);
const {t} = useI18n();
const $message = useMessage();
const props = defineProps({
currFlowForm: {
type: Object,
default: null,
},
currSelect: {
type: Object,
default: {},
},
flowData: {
type: Object,
default: null,
},
});
const condData = {
httpMethod: 'GET',
roleId: null,
jobType: null,
routeAction: null,
toFlowNodeId: null,
groupsType: '1',
condGroup: [],
groupType: '0',
valType: '0',
varKeyVal: null,
operator: null,
varVal: null,
}
const data = reactive({
activeKey: 'flow-rule',
collapse: [0],
allFieldPerms: [],
formFieldPerms: [],
cond: deepClone(condData),
httpUrlParamsVisible: false,
oldCurrentRow: null,
condGroups: [],
toFlowNodeIds: [],
existData: false,
})
// 定义字典
const dicData = reactive({});
const onLoad = onLoadDicUrl();
const onFormLoaded = onFormLoadedUrl(...PROP_CONST.LOAD_USER_ROLE);
onMounted(async () => {
// await onLoad(dicData);
methods.changeTabPane(props.currSelect)
})
const methodsRemote = initRemoteMethodAllByKey(onLoad, dicData)
const methods = {
changeJobType(item) {
handleChangeJobType(dicData, item)
},
onFormLoaded() {
let condGroups = props.currSelect.attributes.attrs.cdata.attrs.condGroups
if (validateNull(condGroups)) return
onFormLoaded(dicData, condGroups)
},
changeRouteAction(item) {
// 切换清空
let b = item.routeAction === DIC_PROP.ROUTE_ACTIONS[1].value;
if(!b && item.routeAction !== DIC_PROP.ROUTE_ACTIONS[3].value) {
data.cond.toFlowNodeId = null
}
},
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 && row.operator) {
$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 (!methods.validateCondGroupAttrs()) 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.push(cond)
methods.validateCondData()
methods.updateCondVarKeyVal(true)
}
methods.onFormLoaded()
},
validateCondGroupAttrs() {
if (!data.cond.routeAction) {
$message.warning("路由动作 不能为空")
return false
}
// 校验动作类型参数
let b = data.cond.routeAction === DIC_PROP.ROUTE_ACTIONS[1].value;
if(b || data.cond.routeAction === DIC_PROP.ROUTE_ACTIONS[3].value) {
if (!data.cond.toFlowNodeId) {
$message.warning("路由到哪个节点 不能为空")
return false
}
}
return 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[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.routeKeyVal = PROP_CONST.VAR_KEY_VAL.route
props.currSelect.attributes.attrs.cdata.attrs.routeKeyValName = PROP_CONST.VAR_KEY_VAL.routeName
} else {
props.currSelect.attributes.attrs.cdata.attrs.routeKeyVal = data.cond.varKeyVal
}
props.currSelect.attributes.attrs.cdata.attrs.httpMethod = data.cond.httpMethod
} else {
props.currSelect.attributes.attrs.cdata.attrs.routeKeyVal = null
props.currSelect.attributes.attrs.cdata.attrs.routeKeyValName = 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.routeKeyVal
let httpMethod = props.currSelect.attributes.attrs.cdata.attrs.httpMethod
if (httpMethod) data.cond.httpMethod = httpMethod
}
let httpParams = props.currSelect.attributes.attrs.cdata.attrs.httpParams;
if (!validateNull(httpParams)) {
data.httpUrlParamsVisible = true
}
return data.cond.valType
},
handleFlowNodeIds() {
data.toFlowNodeIds = []
let models = window._jfGraph.getElements();
if (validateNull(models)) return
models.forEach(each => {
if (!validateNodeType(each)) return
if (props.currSelect.id !== each.id) {
let id = each.id
let nodeName = each.attributes.attrs.label.text
data.toFlowNodeIds.push({id, nodeName})
}
})
},
changeTabPane(val) {
methods.validateCondData()
methods.handleCondValType()
methods.handleFlowNodeIds()
methods.onFormLoaded()
}
}
// 监听双向绑定
watch(
() => props.currSelect,
(val) => {
if (Object.keys(val).length === 0) {
return
}
methods.changeTabPane(val)
}
);
</script>
<style lang="scss">
@import '../assets/style/flow-attr.scss';
</style>

View File

@@ -0,0 +1,361 @@
<template>
<el-form label-position="left" class="flow-attr flow-param-attr" label-width="200px">
<el-divider>关联子流程Http接口</el-divider>
<el-form-item label="保存子流程表单Http请求地址">
<el-input class="input-attr" placeholder="可输入全路径或相对路径" v-model="props.currSelect.attributes.attrs.cdata.attrs.startSubFlow"
clearable>
<template #prepend>
<el-select v-model="props.currSelect.attributes.attrs.cdata.attrs.startSubMethod">
<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 placement="top">
<template #content>启动子流程时更新子流程表单接口可输入全路径或相对路径</template>
<el-icon style="margin-left: 10px">
<QuestionFilled/>
</el-icon>
</el-tooltip>
</el-form-item>
<el-form-item label="更新子流程表单Http请求地址">
<el-input class="input-attr" placeholder="可输入全路径或相对路径" v-model="props.currSelect.attributes.attrs.cdata.attrs.restartSubFlow"
clearable>
<template #prepend>
<el-select v-model="props.currSelect.attributes.attrs.cdata.attrs.restartSubMethod">
<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 placement="top">
<template #content>重入或重启子流程时更新子流程表单接口可输入全路径或相对路径</template>
<el-icon style="margin-left: 10px">
<QuestionFilled/>
</el-icon>
</el-tooltip>
</el-form-item>
<el-form-item label="更新父流程表单Http请求地址">
<el-input class="input-attr" placeholder="可输入全路径或相对路径" v-model="props.currSelect.attributes.attrs.cdata.attrs.backParFlow"
clearable>
<template #prepend>
<el-select v-model="props.currSelect.attributes.attrs.cdata.attrs.backParMethod">
<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 placement="top">
<template #content>返回父流程时更新父流程表单接口可输入全路径或相对路径</template>
<el-icon style="margin-left: 10px">
<QuestionFilled/>
</el-icon>
</el-tooltip>
</el-form-item>
<el-divider>关联子流程Http请求头</el-divider>
<el-table :data="data.subFlowParams.filter(f => f.paramFrom === DIC_PROP.PARAM_FROM[0].value)"
border style="width: 100%; margin-bottom: 10px" max-height="500">
<el-table-column type="index" :label="t('jfI18n.operate')" width="80">
<template #header>
<el-button icon="Plus" size="small" type="primary" circle
@click="methods.onAddItem(DIC_PROP.PARAM_FROM[0].value, true)"></el-button>
</template>
<template #default="scope">
<el-button icon="Minus" size="small" type="danger" circle
@click="methods.handleSubFlowDelete(scope.$index, scope.row)"></el-button>
</template>
</el-table-column>
<el-table-column prop="targetProp" :label="t('jfAttr.requestHeader')" show-overflow-tooltip>
<template #default="scope">
<el-input v-model="scope.row.targetProp" :placeholder="scope.row.paramValType === DIC_PROP.PARAM_VAL_TYPE[0].value ? scope.row.varKeyVal : null"
clearable></el-input>
</template>
</el-table-column>
<el-table-column prop="paramValType" :label="t('jfAttr.paramValType')" width="145px">
<template #default="scope">
<el-select v-model="scope.row.paramValType" clearable>
<el-option v-for="(item, index) in DIC_PROP.PARAM_VAL_TYPE" :key="index" :label="item.label" :value="item.value"></el-option>
</el-select>
</template>
</el-table-column>
<el-table-column prop="varKeyVal" :label="t('jfAttr.varKeyVal')" show-overflow-tooltip>
<template #default="scope">
<el-select v-model="scope.row.varKeyVal"
v-if="scope.row.paramValType === DIC_PROP.PARAM_VAL_TYPE[0].value"
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>
<el-input v-else v-model="scope.row.varKeyVal" clearable> </el-input>
</template>
</el-table-column>
</el-table>
<el-divider>父流程传参到子流程</el-divider>
<el-table :data="data.subFlowParams.filter(f => f.paramFrom === DIC_PROP.PARAM_FROM[1].value)"
border style="width: 100%; margin-bottom: 10px" max-height="500">
<el-table-column type="index" :label="t('jfI18n.operate')" width="80">
<template #header>
<el-button icon="Plus" size="small" type="primary" circle
@click="methods.onAddItem(DIC_PROP.PARAM_FROM[1].value, true)"></el-button>
</template>
<template #default="scope">
<el-button icon="Minus" size="small" type="danger" circle
@click="methods.handleSubFlowDelete(scope.$index, scope.row)"></el-button>
</template>
</el-table-column>
<el-table-column prop="varKeyVal" :label="t('jfAttr.varKeyVal')" show-overflow-tooltip>
<template #default="scope">
<el-select v-model="scope.row.varKeyVal"
v-if="scope.row.paramValType === DIC_PROP.PARAM_VAL_TYPE[0].value"
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>
<el-input v-else v-model="scope.row.varKeyVal" clearable> </el-input>
</template>
</el-table-column>
<el-table-column prop="paramValType" :label="t('jfAttr.paramValType')" width="145px">
<template #default="scope">
<el-select v-model="scope.row.paramValType" clearable>
<el-option v-for="(item, index) in DIC_PROP.PARAM_VAL_TYPE" :key="index" :label="item.label" :value="item.value"></el-option>
</el-select>
</template>
</el-table-column>
<el-table-column prop="targetProp" :label="t('jfAttr.subVarKeyVal')" show-overflow-tooltip>
<template #default="scope">
<el-input v-model="scope.row.targetProp" :placeholder="scope.row.paramValType === DIC_PROP.PARAM_VAL_TYPE[0].value ? scope.row.varKeyVal : null"
clearable></el-input>
</template>
</el-table-column>
</el-table>
<el-divider>子流程回参到父流程</el-divider>
<el-table :data="data.subFlowParams.filter(f => f.paramFrom === DIC_PROP.PARAM_FROM[2].value)"
border style="width: 100%; margin-bottom: 10px" max-height="500">
<el-table-column type="index" :label="t('jfI18n.operate')" width="80">
<template #header>
<el-button icon="Plus" size="small" type="primary" circle
@click="methods.onAddItem(DIC_PROP.PARAM_FROM[2].value, true)"></el-button>
</template>
<template #default="scope">
<el-button icon="Minus" size="small" type="danger" circle
@click="methods.handleSubFlowDelete(scope.$index, scope.row)"></el-button>
</template>
</el-table-column>
<el-table-column prop="targetProp" :label="t('jfAttr.subVarKeyVal2')" show-overflow-tooltip>
<template #default="scope">
<el-input v-model="scope.row.targetProp"
:placeholder="scope.row.paramValType === DIC_PROP.PARAM_VAL_TYPE[0].value ? scope.row.varKeyVal : null"
clearable></el-input>
</template>
</el-table-column>
<el-table-column prop="paramValType" :label="t('jfAttr.paramValType')" width="145px">
<template #default="scope">
<el-switch
v-model="scope.row.paramValType"
:active-value="DIC_PROP.PARAM_VAL_TYPE[0].value"
:active-text="DIC_PROP.PARAM_VAL_TYPE[0].label"
:inactive-value="DIC_PROP.PARAM_VAL_TYPE[2].value"
:inactive-text="DIC_PROP.PARAM_VAL_TYPE[2].label"
inline-prompt>
</el-switch>
</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"
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>
</el-form>
</template>
<script setup lang="ts" name="FlowSubParam">
import {useI18n} from "vue-i18n";
import {useMessage} from "/@/hooks/message";
import {validateNull} from "/@/utils/validate";
import {buildSysFieldsFormOption} from "../../utils/form-perm";
import {DIC_PROP} from "../../support/dict-prop";
const {proxy} = getCurrentInstance();
const {t} = useI18n();
const $message = useMessage();
const props = defineProps({
currFlowForm: {
type: Object,
default: null,
},
currSelect: {
type: Object,
default: null,
},
flowData: {
type: Object,
default: null,
},
});
const data = reactive({
allFieldPerms: [],
formFieldPerms: [],
subFlowParams: [],
})
// 定义字典
onMounted(async () => {
methods.changeTabPane(props.currSelect)
})
const methods = {
listFormFieldPerms() {
if (validateNull(data.formFieldPerms)) {
buildSysFieldsFormOption(data, props, $message)
}
},
onAddItem(paramFrom, isAdd) {
methods.listFormFieldPerms()
let value = DIC_PROP.PARAM_VAL_TYPE[0].value;
if (data.subFlowParams.length > 0) {
let find = data.subFlowParams.filter(f => f.paramFrom === paramFrom).find(f => !f.varKeyVal || (f.paramValType !== value && !f.targetProp));
if (find) {
if (isAdd) {
if (!find.varKeyVal) {
$message.warning("请先填写 表单字段")
return
}
if (find.paramValType !== value && !find.targetProp) {
if (DIC_PROP.PARAM_FROM[0].value === paramFrom) {
$message.warning("请先填写 请求头")
} else {
$message.warning("请先填写 子流程表单字段")
}
return
}
}
}
if (!isAdd) data.subFlowParams.splice(0, data.subFlowParams.length);
}
let obj = {paramFrom: paramFrom, varKeyVal: null, paramValType: value, targetProp: null};
data.subFlowParams.push(obj);
methods.changeSubFlowParams()
},
handleSubFlowDelete(index: number, row: any) {
let splice = data.subFlowParams.filter(f => f.paramFrom === row.paramFrom);
splice.splice(index, 1);
if (DIC_PROP.PARAM_FROM[0].value === row.paramFrom) {
let res = data.subFlowParams.filter(f => f.paramFrom === DIC_PROP.PARAM_FROM[1].value);
let res2 = data.subFlowParams.filter(f => f.paramFrom === DIC_PROP.PARAM_FROM[2].value);
splice.push(...res)
splice.push(...res2)
} else if (DIC_PROP.PARAM_FROM[1].value === row.paramFrom) {
let res = data.subFlowParams.filter(f => f.paramFrom === DIC_PROP.PARAM_FROM[0].value);
let res2 = data.subFlowParams.filter(f => f.paramFrom === DIC_PROP.PARAM_FROM[2].value);
splice.push(...res)
splice.push(...res2)
} else if (DIC_PROP.PARAM_FROM[2].value === row.paramFrom) {
let res = data.subFlowParams.filter(f => f.paramFrom === DIC_PROP.PARAM_FROM[0].value);
let res2 = data.subFlowParams.filter(f => f.paramFrom === DIC_PROP.PARAM_FROM[1].value);
splice.push(...res)
splice.push(...res2)
}
data.subFlowParams = splice
methods.changeSubFlowParams()
},
validateSubFlowData() {
// 兼容老版本
let subFlowParams = props.currSelect.attributes.attrs.cdata.attrs.subFlowParams;
if (!subFlowParams) {
props.currSelect.attributes.attrs.cdata.attrs.subFlowParams = []
}
let startSubMethod = props.currSelect.attributes.attrs.cdata.attrs.startSubMethod;
if (!startSubMethod) props.currSelect.attributes.attrs.cdata.attrs.startSubMethod = 'POST'
let restartSubMethod = props.currSelect.attributes.attrs.cdata.attrs.restartSubMethod;
if (!restartSubMethod) props.currSelect.attributes.attrs.cdata.attrs.restartSubMethod = 'PUT'
let backParMethod = props.currSelect.attributes.attrs.cdata.attrs.backParMethod;
if (!backParMethod) props.currSelect.attributes.attrs.cdata.attrs.backParMethod = 'PUT'
data.subFlowParams = props.currSelect.attributes.attrs.cdata.attrs.subFlowParams
},
changeSubFlowParams() {
props.currSelect.attributes.attrs.cdata.attrs.subFlowParams = data.subFlowParams
},
changeTabPane(val) {
methods.validateSubFlowData()
methods.listFormFieldPerms()
}
}
// 监听双向绑定
watch(
() => props.currSelect,
(val) => {
if (Object.keys(val).length === 0) {
return
}
methods.changeTabPane(val)
}
);
</script>
<style lang="scss">
@import '../assets/style/flow-attr.scss';
</style>

View File

@@ -0,0 +1,666 @@
<template>
<el-form-item label="条件组关系">
<el-switch
v-model="data.cond.groupsType" @change="methods.changeGroupsType"
active-value="0"
inactive-value="1"
inactive-text=""
active-text="" disabled>
</el-switch>
<el-tooltip placement="top">
<template #content>一个组决定一个参与者多条件组满足则累加参与者</template>
<el-icon style="margin-left: 10px">
<QuestionFilled/>
</el-icon>
</el-tooltip>
</el-form-item>
<el-form-item label="审批规则模式">
<el-radio-group style="width: 313px" @change="methods.handleCondValType" :disabled="data.existData"
v-model="data.cond.valType">
<el-radio v-for="(item, index) in [DIC_PROP.VAL_TYPE[2], DIC_PROP.VAL_TYPE[4], DIC_PROP.VAL_TYPE[5]]" :key="index" :label="item.value"
style="width: 75px">
{{ item.label }}
</el-radio>
</el-radio-group>
<el-tooltip placement="top">
<template #content>若无法切换模式请先清空条件组列表</template>
<el-icon>
<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="index"
: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>
<el-form-item label="参与者类型">
<el-select v-model="data.cond.jobType" @change="methods.changeJobType(data.cond)">
<el-option v-for="(item, index) in DIC_PROP.JOB_USER_TYPE" :key="index" :label="item.label" :value="item.value"></el-option>
</el-select>
</el-form-item>
<el-form-item label="哪个人来审批" v-if="data.cond.jobType === DIC_PROP.JOB_USER_TYPE[0].value">
<el-tooltip content="请输入用户名称进行模糊搜索" placement="top">
<el-select class="input-attr"
v-model="data.cond.roleId"
clearable filterable
remote :remote-method="methodsRemote.remoteMethodUser2" :reserve-keyword="false">
<template v-for="(item, index) in dicData.users2" :key="index">
<el-option :label="item.name"
:value="item.userId">
</el-option>
</template>
</el-select>
</el-tooltip>
<el-tooltip placement="top" content="组内条件满足时,由哪个人来审批">
<el-icon style="margin-left: 10px"><QuestionFilled /></el-icon>
</el-tooltip>
</el-form-item>
<el-form-item label="哪个角色审批" v-if="data.cond.jobType === DIC_PROP.JOB_USER_TYPE[1].value">
<el-tooltip content="请输入角色名称进行模糊搜索" placement="top">
<el-select class="input-attr" v-model="data.cond.roleId"
filterable
clearable
remote :remote-method="methodsRemote.remoteMethodRole2" :reserve-keyword="false">
<el-option
v-for="(item, index) in dicData.roles2"
:key="index"
:label="item.roleName"
:value="item.roleId">
</el-option>
</el-select>
</el-tooltip>
<el-tooltip placement="top" content="组内条件满足时,由哪个角色来审批">
<el-icon style="margin-left: 10px"><QuestionFilled /></el-icon>
</el-tooltip>
</el-form-item>
<el-form-item label="哪个岗位审批" v-if="data.cond.jobType === DIC_PROP.JOB_USER_TYPE[2].value">
<el-tooltip content="请输入岗位名称进行模糊搜索" placement="top">
<el-select class="input-attr" v-model="data.cond.roleId"
filterable
clearable
remote :remote-method="methodsRemote.remoteMethodPost2" :reserve-keyword="false">
<el-option
v-for="(item, index) in dicData.posts2"
:key="index"
:label="item.postName"
:value="item.postId">
</el-option>
</el-select>
</el-tooltip>
<el-tooltip placement="top" content="组内条件满足时,由哪个岗位来审批">
<el-icon style="margin-left: 10px"><QuestionFilled /></el-icon>
</el-tooltip>
</el-form-item>
<el-form-item label="哪个部门审批" v-if="data.cond.jobType === DIC_PROP.JOB_USER_TYPE[3].value">
<el-tooltip content="请输入部门名称进行模糊搜索" placement="top">
<el-select class="input-attr" v-model="data.cond.roleId"
filterable
clearable
remote :remote-method="methodsRemote.remoteMethodDept2" :reserve-keyword="false">
<el-option
v-for="(item, index) in dicData.depts2"
:key="index"
:label="item.name"
:value="item.deptId">
</el-option>
</el-select>
</el-tooltip>
<el-tooltip placement="top" content="组内条件满足时,由哪个部门审批">
<el-icon style="margin-left: 10px"><QuestionFilled /></el-icon>
</el-tooltip>
</el-form-item>
</template>
<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>采用表达式取值 ( 以下两种方式均支持自定义任意扩展 ), 值可以为对象 数组, 满足您分配参与者复杂的场景:
<br />{{ PROP_CONST.TEXT_DESC.condUserExplain }}
<br />{{ PROP_CONST.TEXT_DESC.condMethodExplain3 }}, 返回值可以为对象 数组{{ PROP_CONST.TEXT_DESC.condMethodExplain5 }}
<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">: 当前函数表达式的返回值可以为对象 数组{{ PROP_CONST.TEXT_DESC.condMethodExplain5 }}</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.defJob"
:httpParamType="DIC_PROP.PARAM_RULE_TYPE[0].value"
v-if="data.httpUrlParamsVisible"></flow-http-param>
</template>
<template v-if="data.cond.valType === '0'">
<el-tooltip content="当节点属性【多人审批方式】为依次审批时,审批顺序由下方条件组的顺序决定" placement="bottom">
<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-tooltip>
<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%; margin-bottom: 10px" 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-form-item label="参与者类型">
<el-select v-model="item.jobType" @change="methods.changeJobType(item)">
<el-option v-for="(item, index) in DIC_PROP.JOB_USER_TYPE" :key="index" :label="item.label" :value="item.value"></el-option>
</el-select>
</el-form-item>
<el-form-item label="哪个人来审批" v-if="item.jobType === DIC_PROP.JOB_USER_TYPE[0].value">
<el-tooltip content="请输入用户名称进行模糊搜索" placement="top">
<el-select class="input-attr"
v-model="item.roleId"
clearable filterable
remote :remote-method="methodsRemote.remoteMethodUser" :reserve-keyword="false">
<template v-for="(item, index) in dicData.users" :key="index">
<el-option :label="item.name"
:value="item.userId">
</el-option>
</template>
</el-select>
</el-tooltip>
<el-tooltip placement="top" content="组内条件满足时,由哪个人来审批">
<el-icon style="margin-left: 10px"><QuestionFilled /></el-icon>
</el-tooltip>
</el-form-item>
<el-form-item label="哪个角色审批" v-if="item.jobType === DIC_PROP.JOB_USER_TYPE[1].value">
<el-tooltip content="请输入角色名称进行模糊搜索" placement="top">
<el-select class="input-attr" v-model="item.roleId"
filterable
clearable
remote :remote-method="methodsRemote.remoteMethodRole" :reserve-keyword="false">
<el-option
v-for="(item, index) in dicData.roles"
:key="index"
:label="item.roleName"
:value="item.roleId">
</el-option>
</el-select>
</el-tooltip>
<el-tooltip placement="top" content="组内条件满足时,由哪个角色来审批">
<el-icon style="margin-left: 10px"><QuestionFilled /></el-icon>
</el-tooltip>
</el-form-item>
<el-form-item label="哪个岗位审批" v-if="item.jobType === DIC_PROP.JOB_USER_TYPE[2].value">
<el-tooltip content="请输入岗位名称进行模糊搜索" placement="top">
<el-select class="input-attr" v-model="item.roleId"
filterable
clearable
remote :remote-method="methodsRemote.remoteMethodPost" :reserve-keyword="false">
<el-option
v-for="(item, index) in dicData.posts"
:key="index"
:label="item.postName"
:value="item.postId">
</el-option>
</el-select>
</el-tooltip>
<el-tooltip placement="top" content="组内条件满足时,由哪个岗位来审批">
<el-icon style="margin-left: 10px"><QuestionFilled /></el-icon>
</el-tooltip>
</el-form-item>
<el-form-item label="哪个部门审批" v-if="item.jobType === DIC_PROP.JOB_USER_TYPE[3].value">
<el-tooltip content="请输入部门名称进行模糊搜索" placement="top">
<el-select class="input-attr" v-model="item.roleId"
filterable
clearable
remote :remote-method="methodsRemote.remoteMethodDept" :reserve-keyword="false">
<el-option
v-for="(item, index) in dicData.depts"
:key="index"
:label="item.name"
:value="item.deptId">
</el-option>
</el-select>
</el-tooltip>
<el-tooltip placement="top" content="组内条件满足时,由哪个部门审批">
<el-icon style="margin-left: 10px"><QuestionFilled /></el-icon>
</el-tooltip>
</el-form-item>
</el-collapse-item>
</el-collapse>
</template>
</template>
</template>
<script setup lang="ts" name="FlowUserRule">
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 {onFormLoadedUrl, onLoadDicUrl, initRemoteMethodAllByKey} from "../../components/convert-name/convert";
import {PROP_CONST} from "../../support/prop-const";
import {handleChangeJobType} from "../../index";
const {proxy} = getCurrentInstance();
const FlowHttpParam = defineAsyncComponent(() => import('./flow-http-param.vue'));
const {t} = useI18n();
const $message = useMessage();
const props = defineProps({
currFlowForm: {
type: Object,
default: null,
},
currSelect: {
type: Object,
default: null,
},
flowData: {
type: Object,
default: null,
},
});
const condData = {
httpMethod: 'GET',
roleId: null,
jobType: null,
groupsType: '1',
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
})
// 定义字典
const dicData = reactive({});
const onLoad = onLoadDicUrl();
const onFormLoaded = onFormLoadedUrl(...PROP_CONST.LOAD_USER_ROLE);
onMounted(async () => {
// await onLoad(dicData);
methods.changeTabPane(props.currSelect)
})
const methodsRemote = initRemoteMethodAllByKey(onLoad, dicData)
const methods = {
changeJobType(item) {
handleChangeJobType(dicData, item)
},
onFormLoaded() {
let condGroups = props.currSelect.attributes.attrs.cdata.defJob.condGroups
if (validateNull(condGroups)) return
onFormLoaded(dicData, condGroups)
},
openHttpUrlParams() {
if (!data.cond.varKeyVal) {
$message.warning("请先输入【Http请求地址】")
return
}
if (data.httpUrlParamsVisible) {
props.currSelect.attributes.attrs.cdata.defJob.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 && row.operator) {
$message.warning("子表单的字段只能选择包含或不包含")
row.operator = null
}
}
},
changeGroupsType(groupsType) {
let condGroups = props.currSelect.attributes.attrs.cdata.defJob.condGroups;
if (validateNull(condGroups)) return
props.currSelect.attributes.attrs.cdata.defJob.condGroups.forEach(each => {
each.groupsType = groupsType
})
methods.validateCondData()
},
changeGroupType(item, index) {
let condGroups = props.currSelect.attributes.attrs.cdata.defJob.condGroups;
if (validateNull(condGroups)) return
props.currSelect.attributes.attrs.cdata.defJob.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.cond.roleId) {
$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.defJob.condGroups.push(cond)
methods.validateCondData()
methods.updateCondVarKeyVal(true)
}
methods.onFormLoaded()
},
handleCondGroupDelete(index: number) {
props.currSelect.attributes.attrs.cdata.defJob.condGroups.splice(index, 1)
methods.validateCondData()
},
handleSingleCondDelete(index: number, row: any, groupIndex) {
props.currSelect.attributes.attrs.cdata.defJob.condGroups[groupIndex].condGroup.splice(index, 1)
if (validateNull(props.currSelect.attributes.attrs.cdata.defJob.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[4].value && val.indexOf("#") === -1) {
data.cond.varKeyVal = null
$message.warning("当选择专业模式时, 函数表达式必须符合规定的格式")
return;
}
methods.updateCondVarKeyVal(true)
},
updateCondVarKeyVal(isSave) {
props.currSelect.attributes.attrs.cdata.defJob.valType = data.cond.valType
if (isSave) {
if (data.cond.valType === DIC_PROP.VAL_TYPE[2].value) {
props.currSelect.attributes.attrs.cdata.defJob.userKeyVal = PROP_CONST.VAR_KEY_VAL.person
props.currSelect.attributes.attrs.cdata.defJob.userKeyValName = PROP_CONST.VAR_KEY_VAL.personName
} else {
props.currSelect.attributes.attrs.cdata.defJob.userKeyVal = data.cond.varKeyVal
}
props.currSelect.attributes.attrs.cdata.defJob.httpMethod = data.cond.httpMethod
} else {
props.currSelect.attributes.attrs.cdata.defJob.userKeyVal = null
props.currSelect.attributes.attrs.cdata.defJob.userKeyValName = null
props.currSelect.attributes.attrs.cdata.defJob.condGroups = []
props.currSelect.attributes.attrs.cdata.defJob.httpParams = []
props.currSelect.attributes.attrs.cdata.defJob.httpMethod = null
data.httpUrlParamsVisible = false
}
},
validateCondData() {
let condGroups = props.currSelect.attributes.attrs.cdata.defJob.condGroups;
if (!condGroups) {
props.currSelect.attributes.attrs.cdata.defJob.condGroups = []
}
data.condGroups.splice(0, data.condGroups.length);
props.currSelect.attributes.attrs.cdata.defJob.condGroups.forEach(each => data.condGroups.push(each))
data.existData = !validateNull(data.condGroups)
},
initCurrVarKeyVal() {
data.cond.valType = props.currSelect.attributes.attrs.cdata.defJob.valType
if (data.condGroups.length <= 0) {
data.cond.varKeyVal = props.currSelect.attributes.attrs.cdata.defJob.userKeyVal
let httpMethod = props.currSelect.attributes.attrs.cdata.defJob.httpMethod
if (httpMethod) data.cond.httpMethod = httpMethod
}
let httpParams = props.currSelect.attributes.attrs.cdata.defJob.httpParams;
if (!validateNull(httpParams)) {
data.httpUrlParamsVisible = true
}
return data.cond.valType
},
changeTabPane(val) {
methods.validateCondData()
methods.handleCondValType()
methods.onFormLoaded()
}
}
// 监听双向绑定
watch(
() => props.currSelect,
(val) => {
if (Object.keys(val).length === 0) {
return
}
methods.changeTabPane(val)
}
);
</script>
<style lang="scss">
@import '../assets/style/flow-attr.scss';
</style>

View File

@@ -0,0 +1,252 @@
import {DIC_PROP} from "/@/flow/support/dict-prop";
import {PROP_CONST} from "/@/flow/support/prop-const";
import {CommonNodeType, HighNodeType} from "/@/flow/designer/config/type";
import {notifyLeft} from "/@/flow";
import {useMessageBox} from "/@/hooks/message";
import {validateNull} from "/@/utils/validate";
import {gateAttr, linkAttr, syncJobAttr, syncNodeAttr} from "/@/flow/designer/config/attr-config";
import {setPropsNullValue} from "/@/flow/support/common";
import {validateListFormOption} from "/@/flow/utils/form-perm";
/**
* 常用工具类
*
* @author luolin
*/
// 反解析名称时未加载该常量
export function revParseWhoseLeaderName(data, dicData) {
// 谁的主管,可自定义更多
dicData.users.unshift(PROP_CONST.FLOW_METHOD.whoseLeader)
if (!data.whoseLeader) return
if (data.whoseLeader === PROP_CONST.FLOW_METHOD.whoseLeader.userId) {
data.whoseLeaderName = PROP_CONST.FLOW_METHOD.whoseLeader.name
} else {
let find = dicData.users.find(f => f.userId === data.whoseLeader);
if (find) data.whoseLeaderName = find.name;
}
}
export async function revParseUserKeyValName(props, data, dicData, methods) {
if (methods.validateCurrSelectDefJob) {
if (!methods.validateCurrSelectDefJob()) return;
}
let valType = props.currSelect.attributes.attrs.cdata.defJob.valType;
let userKeyVal = props.currSelect.attributes.attrs.cdata.defJob.userKeyVal;
if (!userKeyVal) return;
let userKeyValFrom;
if (userKeyVal === PROP_CONST.VAR_KEY_VAL.order + 'createUser') {
userKeyValFrom = DIC_PROP.FLOW_METHOD_TYPE[0].value
data.activeKey = 'flow-method'
} else if (userKeyVal.indexOf('getUserDeptLeaderId') !== -1) {
userKeyValFrom = DIC_PROP.FLOW_METHOD_TYPE[1].value
data.whoseLeader = userKeyVal.substring(userKeyVal.indexOf('(Long#') + 6, userKeyVal.indexOf(',Integer#'))
data.leaderLevel = parseInt(userKeyVal.substring(userKeyVal.indexOf(',Integer#') + 9, userKeyVal.indexOf(',String#')))
data.levelExtract = userKeyVal.substring(userKeyVal.indexOf(',String#') + 8, userKeyVal.indexOf(')'))
data.activeKey = 'flow-method'
} else if (userKeyVal.indexOf('listUserDeptMultiLeaderId') !== -1) {
userKeyValFrom = DIC_PROP.FLOW_METHOD_TYPE[2].value
let lastIndex = userKeyVal.indexOf('String#') - 1;
data.whoseLeader = userKeyVal.substring(userKeyVal.indexOf('(Long#') + 6, lastIndex)
userKeyVal = userKeyVal.substr(lastIndex)
lastIndex = userKeyVal.indexOf('Integer#') - 1;
data.auditEndpoint = userKeyVal.substring(userKeyVal.indexOf('String#') + 7, lastIndex)
userKeyVal = userKeyVal.substr(lastIndex)
lastIndex = userKeyVal.indexOf('String#') - 1;
let leaderLevel = userKeyVal.substring(userKeyVal.indexOf('Integer#') + 8, lastIndex)
userKeyVal = userKeyVal.substr(lastIndex)
if (data.auditEndpoint === '1') data.leaderLevel = parseInt(leaderLevel)
lastIndex = userKeyVal.lastIndexOf('String#') - 1;
data.seqAuditSort = userKeyVal.substring(userKeyVal.indexOf('String#') + 7, lastIndex)
userKeyVal = userKeyVal.substr(lastIndex)
data.levelExtract = userKeyVal.substring(userKeyVal.indexOf(')') - 1, userKeyVal.indexOf(')'))
data.activeKey = 'flow-method'
} else if (userKeyVal.indexOf('getDeptLeaderId') !== -1) {
userKeyValFrom = DIC_PROP.FLOW_METHOD_TYPE[3].value
data.appointDeptId = userKeyVal.substring(userKeyVal.indexOf('(Long#') + 6, userKeyVal.indexOf(',String#'))
data.levelExtract = userKeyVal.substring(userKeyVal.indexOf(',String#') + 8, userKeyVal.indexOf(')'))
} else if (props.currSelect.attributes.attrs.cdata.defJob.userKeyValName) {
let find = DIC_PROP.FLOW_METHOD_TYPE.find(f => f.label === props.currSelect.attributes.attrs.cdata.defJob.userKeyValName);
// 再次编辑时优先显示为专业模式
if (find) {
userKeyValFrom = DIC_PROP.FLOW_METHOD_TYPE[4].value
data.userKeyVal = userKeyVal
if (methods.handleUserKeyValFrom) methods.handleUserKeyValFrom(userKeyValFrom)
data.activeKey = 'flow-method'
}
} else if (DIC_PROP.VAL_TYPE[4].value === valType && userKeyVal) {
if (validateNull(data.formFieldPerms)) await validateListFormOption(data, props)
if (!validateNull(data.formFieldPerms)) {
let exist = data.formFieldPerms.find(f => f.prop === userKeyVal);
if (exist) userKeyValFrom = DIC_PROP.FLOW_METHOD_TYPE[4].value
}
}
if (userKeyValFrom) {
data.userKeyValFrom = userKeyValFrom
} else {
data.activeKey = 'flow-rule'
}
}
export function parseUserKeyValName(props, data, methods) {
if (methods.validateCurrSelectDefJob) {
if (!methods.validateCurrSelectDefJob()) return;
}
let userKeyVal;
if (data.userKeyValFrom === '0') {
userKeyVal = PROP_CONST.VAR_KEY_VAL.order + 'createUser'
} else if (data.userKeyValFrom === '1') {
userKeyVal = '#distActorServiceImpl.getUserDeptLeaderId(Long#'+ data.whoseLeader +',Integer#'+ data.leaderLevel +',String#'+ data.levelExtract +')'
} else if (data.userKeyValFrom === '2') {
let leaderLevel = 'NULL'
if (data.auditEndpoint === '1') leaderLevel = data.leaderLevel
let seqAuditSort = 'NULL'
if (data.seqAuditSort) seqAuditSort = data.seqAuditSort
userKeyVal = '#distActorServiceImpl.listUserDeptMultiLeaderId(Long#'+ data.whoseLeader +',String#'+ data.auditEndpoint +',Integer#'+ leaderLevel +',String#'+ seqAuditSort +',String#'+ data.levelExtract +')'
} else if (data.userKeyValFrom === '3') {
userKeyVal = '#distActorServiceImpl.getDeptLeaderId(Long#'+ data.appointDeptId +',String#'+ data.levelExtract +')'
} else if (data.userKeyValFrom === '4') {
userKeyVal = data.userKeyVal
}
if (data.userKeyValFrom === '1' || data.userKeyValFrom === '2') {
if (!data.whoseLeader) {
if (methods.$message) methods.$message('whoseLeader')
return
}
}
if (data.userKeyValFrom === '3') {
if (!data.appointDeptId) {
if (methods.$message) methods.$message('appointDeptId')
return
}
}
props.currSelect.attributes.attrs.cdata.defJob.userKeyVal = userKeyVal;
props.currSelect.attributes.attrs.cdata.defJob.userKeyValName = DIC_PROP.FLOW_METHOD_TYPE.find(f => f.value === data.userKeyValFrom).label;
props.currSelect.attributes.attrs.cdata.defJob.valType = DIC_PROP.VAL_TYPE[4].value
// 清空其他参数
props.currSelect.attributes.attrs.cdata.defJob.condGroups = []
props.currSelect.attributes.attrs.cdata.defJob.httpParams = []
props.currSelect.attributes.attrs.cdata.defJob.httpMethod = null
}
export function handleLinkFlowNodeIds(data, props) {
data.toFlowNodeIds = []
data.fromFlowNodeIds = []
let models = window._jfGraph.getElements();
data.fromFlowNodeId = props.currSelect.attributes.source.id
data.toFlowNodeId = props.currSelect.attributes.target.id
// 修正拖拽连线箭头更改目标节点
props.currSelect.attributes.attrs.cdata.attrs.fromFlowNodeId = data.fromFlowNodeId
props.currSelect.attributes.attrs.cdata.attrs.toFlowNodeId = data.toFlowNodeId
models.forEach(each => {
if (!validateNodeType(each)) return
let id = each.id
let nodeName = each.attributes.attrs.label.text
if (id !== props.currSelect.attributes.target.id) {
data.fromFlowNodeIds.push({id, nodeName})
}
if (id !== props.currSelect.attributes.source.id) {
data.toFlowNodeIds.push({id, nodeName})
}
})
}
export function changeLinkFlowNodeIds(data, props, methods?, $emit?) {
useMessageBox()
.confirm('是否确认修改连线的' + (data.modifyPointType === '0' ? '起点?' : '终点?'))
.then(() => {
doLinkFlowNodeIds(data, props, methods, $emit)
})
}
function doLinkFlowNodeIds(data, props, methods?, $emit?) {
if (data.modifyPointType === '0') {
props.currSelect.attributes.attrs.cdata.attrs.fromFlowNodeId = data.fromFlowNodeId
props.currSelect.set('source', { id: data.fromFlowNodeId });
} else {
props.currSelect.attributes.attrs.cdata.attrs.toFlowNodeId = data.toFlowNodeId
props.currSelect.set('target', { id: data.toFlowNodeId });
}
if (methods) methods.handleLinkFlowNodeIds()
if (window._flowConfig.globalConfig.isSimpleMode === '1') window._jfOperate.layout()
else notifyLeft('专业模式不会自动调整连线轨迹,有必要时请手动调整', 'warning', 3000)
if ($emit) $emit("hideAttrConfig", false, '1');
}
export function validateNodeType(currSelect, methods?, isVirtual?) {
if (methods && !methods.validateCurrSelectAttrs()) return false;
let type = currSelect.attributes.attrs.cdata.type;
let noVirtual = type === CommonNodeType.START || type === CommonNodeType.END || type === CommonNodeType.SERIAL || type === CommonNodeType.PARALLEL;
if (!isVirtual) return noVirtual
return noVirtual || type === HighNodeType.VIRTUAL
}
export function handleSyncFlowNodeIds(data, props, methods) {
data.syncFlowNodeIds = []
if (!methods.validateCurrSelectAttrsAttrs()) return;
props.currSelect.attributes.attrs.cdata.attrs.syncFlowNodeId = null
let isGateway = props.currSelect.attributes.attrs.cdata.attrs.isGateway;
let models = window._jfGraph.getElements();
if (validateNull(models)) return
models.forEach(each => {
let cdata = each.attributes.attrs.cdata;
let b = cdata.type === CommonNodeType.SERIAL || cdata.type === CommonNodeType.PARALLEL;
if (b && props.currSelect.id !== each.id) {
let id = each.id
let nodeName = each.attributes.attrs.label.text + "ID:" + id + ""
let isExist = false
if (isGateway === '1') {
if (cdata.attrs.isGateway === '1') isExist = true
} else {
if (cdata.attrs.isGateway !== '1') isExist = true
}
if (isExist) data.syncFlowNodeIds.push({id, nodeName})
}
})
}
export function changeSyncFlowNodeId(id, props, methods, $message) {
let models = window._jfGraph.getElements();
if (validateNull(models) || !id) return
let isGateway = props.currSelect.attributes.attrs.cdata.attrs.isGateway;
let cdata = models.find(f => f.id === id).attributes.attrs.cdata;
let nodeAttrs: any[];
if (isGateway !== '1') {
nodeAttrs = Object.keys(syncNodeAttr);
if (!validateNull(cdata.defJob)) {
setPropsNullValue(props.currSelect.attributes.attrs.cdata.defJob, cdata.defJob, ...Object.keys(syncJobAttr))
}
} else {
nodeAttrs = Object.keys(gateAttr);
}
setPropsNullValue(props.currSelect.attributes.attrs.cdata.attrs, cdata.attrs, ...nodeAttrs)
$message.warning("已同步其他节点的配置,请重新打开查看")
methods.hideAttrConfig(false, '1');
}
export function handleSyncFlowNodeRelIds(data, props, methods) {
data.syncFlowNodeRelIds = []
if (!methods.validateCurrSelectAttrsAttrs()) return;
props.currSelect.attributes.attrs.cdata.attrs.syncFlowNodeRelId = null
let links = window._jfGraph.getLinks();
if (validateNull(links)) return
links.forEach(each => {
if (props.currSelect.id !== each.id) {
let id = each.id
let linkName = "ID:" + id
let text = each.attributes.labels[0].attrs.text.text;
if (text) linkName = text + "ID:" + id + ""
data.syncFlowNodeRelIds.push({id, linkName})
}
})
}
export function changeSyncFlowNodeRelId(id, props, methods, $message) {
let links = window._jfGraph.getLinks();
if (validateNull(links) || !id) return
let cdata = links.find(f => f.id === id).attributes.attrs.cdata;
let linkAttrs = Object.keys(linkAttr);
setPropsNullValue(props.currSelect.attributes.attrs.cdata.attrs, cdata.attrs, ...linkAttrs)
$message.warning("已同步其他连线的配置,请重新打开查看")
methods.hideAttrConfig(false, '1');
}

View File

@@ -0,0 +1,87 @@
<template>
<div class="flow-node-menu">
<el-menu style="border-right: 0;">
<template v-for="(node, index) in props.menuList" :key="index">
<el-menu-item style="padding-top: 4px; padding-left: 14px;" v-if="isShowNode(node) && node.type !== HighNodeType.CHILD_FLOW">
<el-tooltip :content="getNodeName(node)" placement="right">
<div
class="el-node-item"
draggable="true"
@dragstart="dragNode(node.type, props.type)"
>
<img :src="node.icon" alt="node">
</div>
</el-tooltip>
</el-menu-item>
</template>
</el-menu>
</div>
</template>
<script setup lang="ts" name="NodeMenu">
import {HighNodeType} from '../config/type'
import {validateNull} from "/@/utils/validate";
const $emit = defineEmits(["setDragInfo"]);
const props = defineProps({
menuList: {
type: Array,
default: () => []
},
flowData: {
type: Object,
default: {}
},
type: {
type: String,
default: null
}
});
function isShowNode(node) {
if (validateNull(props.flowData)) return true
if (node.type !== HighNodeType.JOB && node.type !== HighNodeType.VIRTUAL) {
return true
}
let isJobSeparated = props.flowData.attrs.isJobSeparated;
return isJobSeparated === '1';
}
function getNodeName(node) {
return node.nodeName + (node.nodeDesc ? node.nodeDesc : '')
}
// 开始拖拽
function dragNode(type, belongTo) {
$emit("setDragInfo", {
type,
belongTo
});
}
</script>
<style scoped lang="scss">
/*菜单间距*/
.flow-node-menu {
.el-menu-item {
padding: 11px!important;
height: 40px!important;
}
.el-node-item {
height: 32px;
width: 32px;
color: #fff;
border-radius: 5px;
line-height: 30px;
text-align: center;
cursor: move;
align-items: center;
justify-content: center;
&:hover {
color: #0960bd;
outline: 1px dashed #0960bd;
}
}
}
</style>