init
This commit is contained in:
229
src/flow/components/contextmenu/index.vue
Normal file
229
src/flow/components/contextmenu/index.vue
Normal file
@@ -0,0 +1,229 @@
|
||||
<template>
|
||||
<ul class="vue-contextmenu-listWrapper"
|
||||
@contextmenu.stop=""
|
||||
:class="'vue-contextmenuName-' + props.contextMenuData.menuName">
|
||||
<li v-for="item in props.contextMenuData.menulists"
|
||||
class="context-menu-list"
|
||||
:key="item.btnName">
|
||||
<div v-if="item.children && item.children.length > 0" class="has-child">
|
||||
<div class="parent-name btn-wrapper-simple"
|
||||
:class="{'no-allow': item.disabled ? item.disabled : false}">
|
||||
<i :class="item.icoName ? item.icoName : ''" class="nav-icon-fontawe" :style="item.icoStyle ? item.icoStyle : ''"></i>
|
||||
<span class="nav-name-right">{{ item.btnName }}</span>
|
||||
<i class="icon"></i>
|
||||
</div>
|
||||
<Tree :itemchildren="item.children" @childhandler="fnHandler" :float="data.floatDirection"/>
|
||||
</div>
|
||||
<div v-else>
|
||||
<div class="node-connect" v-if="item.nodeConnect === true">
|
||||
<div @click.stop="fnHandler(item)">
|
||||
<img :src="item.icon" :alt="item.btnName">
|
||||
<!-- <i :class="item.icoName ? item.icoName : ''"></i>-->
|
||||
<span>{{item.btnName}}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div @click.stop="item.disabled === true ? '' : fnHandler(item)" v-else
|
||||
class="no-child-btn btn-wrapper-simple"
|
||||
:class="{'no-allow': item.disabled ? item.disabled : false}">
|
||||
<i :class="item.icoName ? item.icoName : ''" class="nav-icon-fontawe" :style="item.icoStyle ? item.icoStyle : ''"></i>
|
||||
<span class="nav-name-right">{{ item.btnName }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</template>
|
||||
<script setup lang="ts" name="VueContextMenu">
|
||||
|
||||
// 引入组件
|
||||
const Tree = defineAsyncComponent(() => import('./tree.vue'));
|
||||
|
||||
const props = defineProps({
|
||||
contextMenuData: {
|
||||
type: Object,
|
||||
required: false,
|
||||
default() {
|
||||
return {
|
||||
menuName: null,
|
||||
axis: {
|
||||
x: null,
|
||||
y: null
|
||||
},
|
||||
menulists: [
|
||||
{
|
||||
fnHandler: '',
|
||||
icoName: '',
|
||||
btnName: ''
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
transferIndex: {
|
||||
type: Number,
|
||||
default: 0
|
||||
}
|
||||
})
|
||||
|
||||
const data = reactive({
|
||||
floatDirection: 'float-statue-1'
|
||||
})
|
||||
|
||||
let menuLists = props.contextMenuData.menulists;
|
||||
|
||||
function contextMenuDataAxis(val) {
|
||||
let x = val.x
|
||||
let y = val.y
|
||||
let innerWidth = window.innerWidth
|
||||
let innerHeight = window.innerHeight
|
||||
let index = props.transferIndex
|
||||
let menuName = 'vue-contextmenuName-' + props.contextMenuData.menuName
|
||||
let menu = document.getElementsByClassName(menuName)[index]
|
||||
menu.style.display = 'block'
|
||||
let menuHeight = menuLists.length * 30
|
||||
let menuWidth = 150
|
||||
menu.style.top = (y + menuHeight > innerHeight ? innerHeight - menuHeight : y) + 'px'
|
||||
menu.style.left = (x + menuWidth > innerWidth ? innerWidth - menuWidth : x) + 'px'
|
||||
/*document.addEventListener('mouseup', function (e) {
|
||||
// 解决mac电脑在鼠标右键后会执行mouseup事件
|
||||
if (e.button === 0) {
|
||||
menu.style.display = 'none'
|
||||
}
|
||||
}, false)*/
|
||||
if ((x + 2 * menuWidth) > innerWidth && (y + 2 * menuHeight) > innerHeight) {
|
||||
data.floatDirection = 'float-status-4'
|
||||
}
|
||||
if ((x + 2 * menuWidth) < innerWidth && (y + 2 * menuHeight) > innerHeight) {
|
||||
data.floatDirection = 'float-status-1'
|
||||
}
|
||||
if ((x + 2 * menuWidth) > innerWidth && (y + 2 * menuHeight) < innerHeight) {
|
||||
data.floatDirection = 'float-status-3'
|
||||
}
|
||||
if ((x + 2 * menuWidth) < innerWidth && (y + 2 * menuHeight) < innerHeight) {
|
||||
data.floatDirection = 'float-status-2'
|
||||
}
|
||||
}
|
||||
|
||||
let $emit = defineEmits(["flowInfo", "paste", "setNodeAttr", "copyNode", "deleteNode", "deleteLink", "setLinkAttr"
|
||||
, "setSerialNode", "setParallelNode", "setSerialGate", "setParallelGate", "setConnectNode", "modifySourceNode", "modifyTargetNode"]);
|
||||
|
||||
function fnHandler(item) {
|
||||
$emit(item.fnHandler)
|
||||
}
|
||||
|
||||
// 监听双向绑定
|
||||
watch(
|
||||
() => props.contextMenuData.axis,
|
||||
(val) => {
|
||||
contextMenuDataAxis(val);
|
||||
}
|
||||
);
|
||||
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.vue-contextmenu-listWrapper {
|
||||
display: none;
|
||||
position: fixed;
|
||||
z-index: 2000;
|
||||
background: #2c2c2c;
|
||||
top: 0;
|
||||
left: 0;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 2px 0 #cccccc;
|
||||
font-family: 'Courier New', Courier, monospace;
|
||||
font-size: 12px;
|
||||
|
||||
padding-left: 10px !important;
|
||||
color: white !important;
|
||||
}
|
||||
|
||||
.vue-contextmenu-listWrapper .context-menu-list {
|
||||
position: relative;
|
||||
text-decoration: none;
|
||||
list-style: none;
|
||||
|
||||
background: #2c2c2c;
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
|
||||
.vue-contextmenu-listWrapper .node-connect {
|
||||
div {
|
||||
display: inline-block;
|
||||
cursor: pointer;
|
||||
width: 130px;
|
||||
position: relative;
|
||||
span {
|
||||
position: absolute;
|
||||
font-size: 14px;
|
||||
margin-left: 16px;
|
||||
margin-top: 3px;
|
||||
}
|
||||
i {
|
||||
font-size: 25px;
|
||||
border: 1px solid white;
|
||||
border-radius: 15px;
|
||||
margin-left: 10px;
|
||||
}
|
||||
img {
|
||||
display: inline-block;
|
||||
width: 25px;
|
||||
height: 25px;
|
||||
margin-left: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.context-menu-list:hover {
|
||||
color: #6e6e6e;
|
||||
}
|
||||
|
||||
.context-menu-list .has-child {
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
padding: 5px 10px;
|
||||
}
|
||||
|
||||
.context-menu-list:hover > .has-child > .child-ul-wrapper {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.parent-name .icon {
|
||||
position: absolute;
|
||||
display: block;
|
||||
top: 4px;
|
||||
right: 0;
|
||||
border-top: 4px solid transparent;
|
||||
border-bottom: 4px solid transparent;
|
||||
border-right: 4px solid transparent;
|
||||
|
||||
border-left: 8px solid white;
|
||||
}
|
||||
|
||||
.no-child-btn {
|
||||
//padding: 5px 10px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.nav-icon-fontawe {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.nav-name-right {
|
||||
margin: 0 20px;
|
||||
//height: 16px;
|
||||
line-height: 16px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.no-allow {
|
||||
color: #d3cfcf;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.btn-wrapper-simple {
|
||||
position: relative;
|
||||
//height: 16px;
|
||||
line-height: 16px;
|
||||
}
|
||||
</style>
|
||||
107
src/flow/components/contextmenu/tree.vue
Normal file
107
src/flow/components/contextmenu/tree.vue
Normal file
@@ -0,0 +1,107 @@
|
||||
<template>
|
||||
<ul class="child-ul-wrapper" :class="props.float">
|
||||
<li v-for="i in props.itemchildren" :key="i.name" class="child-li-wrapper">
|
||||
<div v-if="i.children && i.children.length > 0" class="has-child">
|
||||
<div class="parent-name btn-wrapper-simple">
|
||||
<i :class="i.icoName ? i.icoName : ''" class="nav-icon-fontawe" :style="item.icoStyle ? item.icoStyle : ''"></i>
|
||||
<span class="nav-name-right">{{i.btnName}}</span>
|
||||
<i class="icon"></i></div>
|
||||
<VueContextMenuTree :itemchildren="i.children" @childhandler="fnHandler" :float="props.float" />
|
||||
</div>
|
||||
<div v-else>
|
||||
<div @click.stop="fnHandler(i)" class="no-child-btn btn-wrapper-simple">
|
||||
<i :class="i.icoName ? i.icoName : ''" class="nav-icon-fontawe" :style="item.icoStyle ? item.icoStyle : ''"></i>
|
||||
<span class="nav-name-right">{{i.btnName}}</span>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</template>
|
||||
<script setup lang="ts" name="VueContextMenuTree">
|
||||
|
||||
const props = defineProps({
|
||||
itemchildren: [],
|
||||
float: ''
|
||||
})
|
||||
|
||||
let $emit = defineEmits(["flowInfo", "paste", "setNodeAttr", "copyNode", "deleteNode", "deleteLink", "setLinkAttr"
|
||||
, "setSerialNode", "setParallelNode", "setSerialGate", "setParallelGate", "setConnectNode", "modifySourceNode", "modifyTargetNode"]);
|
||||
|
||||
function fnHandler (item) {
|
||||
$emit('childhandler', item)
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.float-status-1 {
|
||||
left: 100%;
|
||||
bottom: -1px;
|
||||
z-index: 10001
|
||||
}
|
||||
.float-status-2 {
|
||||
left: 100%;
|
||||
top: -1px;
|
||||
z-index: 10001
|
||||
}
|
||||
.float-status-3 {
|
||||
right: 100%;
|
||||
top: -1px;
|
||||
z-index: 10001
|
||||
}
|
||||
.float-status-4 {
|
||||
right: 100%;
|
||||
bottom: -1px;
|
||||
z-index: 10001
|
||||
}
|
||||
|
||||
.child-ul-wrapper .has-child {
|
||||
padding: 5px 10px;
|
||||
position: relative;
|
||||
}
|
||||
li {
|
||||
list-style: none;
|
||||
}
|
||||
.parent-name .icon {
|
||||
position: absolute;
|
||||
display: block;
|
||||
top: 4px;
|
||||
right: 0;
|
||||
border-top: 4px solid transparent;
|
||||
border-left: 8px solid #111111;
|
||||
border-bottom: 4px solid transparent;
|
||||
border-right: 4px solid transparent;
|
||||
}
|
||||
|
||||
.no-child-btn {
|
||||
//padding: 5px 10px;
|
||||
}
|
||||
|
||||
.child-ul-wrapper {
|
||||
background: #ffffff;
|
||||
position: absolute;
|
||||
display: none;
|
||||
border: 1px solid #e8e8e8;
|
||||
border-radius: 3px;
|
||||
}
|
||||
.child-li-wrapper:hover > .has-child > .child-ul-wrapper{
|
||||
display: block;
|
||||
}
|
||||
.context-menu-list:hover, .child-li-wrapper:hover {
|
||||
background: #2894f8;
|
||||
}
|
||||
|
||||
.nav-icon-fontawe {
|
||||
position: absolute;
|
||||
}
|
||||
.nav-name-right {
|
||||
white-space: nowrap;
|
||||
display: block;
|
||||
margin: auto 20px;
|
||||
}
|
||||
.btn-wrapper-simple {
|
||||
position: relative;
|
||||
//height: 16px;
|
||||
line-height: 16px;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
464
src/flow/components/convert-name/convert.ts
Normal file
464
src/flow/components/convert-name/convert.ts
Normal file
@@ -0,0 +1,464 @@
|
||||
import {ElMessage} from "element-plus";
|
||||
import {validateNull} from "/@/utils/validate";
|
||||
import {listDicUrlArr, listDicUrl} from "/@/api/jsonflow/common";
|
||||
import {deepClone} from "/@/utils/other";
|
||||
import {DIC_PROP} from "/@/flow/support/dict-prop";
|
||||
import {useMessage} from "/@/hooks/message";
|
||||
/**
|
||||
* 远程请求数据字典工具类
|
||||
*
|
||||
* @author luolin
|
||||
* @date 2023-04-25 16:41:52
|
||||
*/
|
||||
interface DicDataInterface {
|
||||
key: string;
|
||||
type?: string;
|
||||
typeVal?: string;
|
||||
field?: string;
|
||||
method?: string;
|
||||
dicUrl?: string;
|
||||
prefix?: string;
|
||||
cascades?: string[];
|
||||
}
|
||||
|
||||
const dicArrUrls = {
|
||||
listUsersByUserIds: "/jsonflow/user-role-auditor/list/user-ids",
|
||||
listRolesByRoleIds: "/jsonflow/user-role-auditor/list/role-ids",
|
||||
listPostsByPostIds: "/jsonflow/user-role-auditor/list/post-ids",
|
||||
listDeptsByDeptIds: "/jsonflow/user-role-auditor/list/dept-ids",
|
||||
listByFlowInstIds: "/jsonflow/run-flow/list/flow-inst-ids",
|
||||
listByOrderIds: "/order/run-application/list/order-ids",
|
||||
listByRunNodeIds: "/jsonflow/run-node/list/run-node-ids",
|
||||
listByRunJobIds: "/jsonflow/run-job/list/run-job-ids",
|
||||
}
|
||||
|
||||
/**
|
||||
* 默认回调映射关系
|
||||
*/
|
||||
const dicArrFun = {
|
||||
createUser: dicArrUrls.listUsersByUserIds,
|
||||
handoverUser: dicArrUrls.listUsersByUserIds,
|
||||
receiveUser: dicArrUrls.listUsersByUserIds,
|
||||
initiatorId: dicArrUrls.listUsersByUserIds,
|
||||
carbonCopy: dicArrUrls.listUsersByUserIds,
|
||||
carbonCopyPerson: dicArrUrls.listUsersByUserIds,
|
||||
userId: dicArrUrls.listUsersByUserIds,
|
||||
delRoleId: dicArrUrls.listUsersByUserIds,
|
||||
roleId: dicArrUrls.listUsersByUserIds,
|
||||
permission: dicArrUrls.listRolesByRoleIds,
|
||||
users: dicArrUrls.listUsersByUserIds,
|
||||
roles: dicArrUrls.listRolesByRoleIds,
|
||||
posts: dicArrUrls.listPostsByPostIds,
|
||||
depts: dicArrUrls.listDeptsByDeptIds,
|
||||
handoverUserDept: dicArrUrls.listDeptsByDeptIds,
|
||||
receiveDept: dicArrUrls.listDeptsByDeptIds,
|
||||
handleUserId: dicArrUrls.listUsersByUserIds,
|
||||
parFlowInstId: dicArrUrls.listByFlowInstIds,
|
||||
subFlowInstId: dicArrUrls.listByFlowInstIds,
|
||||
flowInstId: dicArrUrls.listByFlowInstIds,
|
||||
orderId: dicArrUrls.listByOrderIds,
|
||||
runNodeId: dicArrUrls.listByRunNodeIds,
|
||||
runJobId: dicArrUrls.listByRunJobIds,
|
||||
fromRunNodeId: dicArrUrls.listByRunNodeIds,
|
||||
toRunNodeId: dicArrUrls.listByRunNodeIds,
|
||||
}
|
||||
|
||||
/**
|
||||
* 表格数据加载完成后事件
|
||||
*/
|
||||
export const onLoaded = (...keyObjs: DicDataInterface[]) => {
|
||||
return async (state: any, keyObjs2?: DicDataInterface) => {
|
||||
let dataList = state.dataList;
|
||||
if (dataList && dataList.length > 0) {
|
||||
try {
|
||||
let dicData = {}
|
||||
let realKeyObjs = validateNull(keyObjs) ? {} : deepClone(keyObjs)
|
||||
if (keyObjs2 && !validateNull(keyObjs2)) realKeyObjs = [deepClone(keyObjs2)]
|
||||
for (const keyObj of realKeyObjs) {
|
||||
let {key, type, typeVal, field, dicUrl} = keyObj;
|
||||
if (!field) field = key
|
||||
let keys = keyObjs2 ? dataList : dataList.filter(f => !type || f[type] === typeVal).map(m => m[field])
|
||||
.flat().filter(f => !validateNull(f));
|
||||
let ids = keys.filter((item, index) => {
|
||||
return keys.indexOf(item, 0) === index;
|
||||
});
|
||||
if (validateNull(ids)) {
|
||||
dicData[key] = []
|
||||
continue;
|
||||
}
|
||||
if (!dicUrl) dicUrl = dicArrFun[key];
|
||||
if (dicUrl.indexOf("?") !== -1) {
|
||||
let params = dicUrl.split('?')[1].split('=');
|
||||
dicUrl = dicUrl.replace("{{" + params[0] + "}}", typeVal)
|
||||
}
|
||||
let res = await listDicUrlArr(dicUrl, ids);
|
||||
validateNull(res.data) ? dicData[key] = [] : dicData[key] = res.data;
|
||||
}
|
||||
Object.assign(state.dicData, dicData);
|
||||
} catch (err: any) {
|
||||
// 捕获异常并显示错误提示
|
||||
ElMessage.error(err.msg || err.data.msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 表单数据加载完成后事件
|
||||
*/
|
||||
export const onFormLoadedUrl = (...keyObjs: DicDataInterface[]) => {
|
||||
return async (dicData: any, form?: any, keyObjs2?: DicDataInterface) => {
|
||||
try {
|
||||
let realKeyObjs = validateNull(keyObjs) ? {} : deepClone(keyObjs)
|
||||
if (keyObjs2 && !validateNull(keyObjs2)) realKeyObjs = [deepClone(keyObjs2)]
|
||||
for (const keyObj of realKeyObjs) {
|
||||
let {key, type, typeVal, field, dicUrl} = keyObj;
|
||||
if (!field) field = key
|
||||
let keys = validateNull(typeVal) ? form[field] : form[type] === typeVal ? form[field] : null;
|
||||
if (form instanceof Array) {
|
||||
keys = form.filter(f => !type || f[type] === typeVal).map(m => m[field])
|
||||
.flat().filter(f => !validateNull(f));
|
||||
}
|
||||
if (!(keys instanceof Array)) keys = keys ? [keys] : []
|
||||
let ids = keys.filter((item, index) => {
|
||||
return keys.indexOf(item, 0) === index;
|
||||
});
|
||||
if (validateNull(ids)) {
|
||||
// 参与者翻译需要
|
||||
dicData[key] = []
|
||||
continue;
|
||||
}
|
||||
if (!dicUrl) dicUrl = dicArrFun[key];
|
||||
let res = await listDicUrlArr(dicUrl, ids);
|
||||
validateNull(res.data) ? dicData[key] = [] : dicData[key] = res.data;
|
||||
}
|
||||
} catch (err: any) {
|
||||
// 捕获异常并显示错误提示
|
||||
ElMessage.error(err.msg || err.data.msg);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const cascadeUrls = {
|
||||
listFlowNodeByDefFlowId: "/jsonflow/flow-node/list/def-flow-id/{{key}}",
|
||||
listFlowNodeRelByDefFlowId: "/jsonflow/flow-node-rel/list/def-flow-id/{{key}}",
|
||||
listNodeJobByFlowNodeId: "/jsonflow/node-job/list/flow-node-id/{{key}}",
|
||||
listPreRunNodeByFlowInstId: "/jsonflow/run-node/flow-inst-id/pre/{{key}}?runNodeId={{runNodeId}}",
|
||||
listAnyJumpRunNodeByFlowInstId: "/jsonflow/run-node/flow-inst-id/any-jump/{{key}}?runNodeId={{runNodeId}}",
|
||||
listRunNodeByFlowInstId: "/jsonflow/run-node/list/flow-inst-id/{{key}}",
|
||||
listUsersByRunNodeId: "/jsonflow/run-job/list/users/run-node-id/{{key}}",
|
||||
listUserByRunNodeId: "/jsonflow/run-job/list/user/run-node-id/{{key}}",
|
||||
listRoleByRunNodeId: "/jsonflow/run-job/list/role/run-node-id/{{key}}",
|
||||
listPostByRunNodeId: "/jsonflow/run-job/list/post/run-node-id/{{key}}",
|
||||
listDeptByRunNodeId: "/jsonflow/run-job/list/dept/run-node-id/{{key}}",
|
||||
listRunJobByRunNodeId: "/jsonflow/run-job/list/run-job/run-node-id/{{key}}",
|
||||
listDistRunJobByRunNodeId: "/jsonflow/run-job/list/dist-run-job/run-node-id/{{key}}",
|
||||
getRunFlowByCode: "/jsonflow/run-flow/code/{{key}}",
|
||||
getRunFlowByFlowInstId: "/jsonflow/run-flow/{{key}}",
|
||||
listUserByRoleId: "/jsonflow/user-role-auditor/list-users/{{key}}?jobType={{jobType}}",
|
||||
}
|
||||
|
||||
/**
|
||||
* 默认回调映射关系
|
||||
*/
|
||||
const dicCascadeFun = {
|
||||
flowNodeId: cascadeUrls.listFlowNodeByDefFlowId,
|
||||
flowNodeRelId: cascadeUrls.listFlowNodeRelByDefFlowId,
|
||||
nodeJobId: cascadeUrls.listNodeJobByFlowNodeId,
|
||||
"runReject.toRunNodeId": cascadeUrls.listPreRunNodeByFlowInstId,
|
||||
"runJob.toRunNodeId": cascadeUrls.listAnyJumpRunNodeByFlowInstId,
|
||||
flowKey: cascadeUrls.getRunFlowByFlowInstId,
|
||||
defFlowId: cascadeUrls.getRunFlowByFlowInstId,
|
||||
userId: cascadeUrls.listUserByRoleId,
|
||||
flowInstId: cascadeUrls.getRunFlowByCode,
|
||||
runNodeId: cascadeUrls.listRunNodeByFlowInstId,
|
||||
runJobId: cascadeUrls.listRunJobByRunNodeId,
|
||||
distRunJobId: cascadeUrls.listDistRunJobByRunNodeId,
|
||||
fromRunNodeId: cascadeUrls.listRunNodeByFlowInstId,
|
||||
toRunNodeId: cascadeUrls.listRunNodeByFlowInstId,
|
||||
handleUserId: cascadeUrls.listUsersByRunNodeId,
|
||||
anyJumpUserId: cascadeUrls.listUserByRunNodeId,
|
||||
anyJumpRoleId: cascadeUrls.listRoleByRunNodeId,
|
||||
anyJumpPostId: cascadeUrls.listPostByRunNodeId,
|
||||
anyJumpDeptId: cascadeUrls.listDeptByRunNodeId,
|
||||
}
|
||||
|
||||
/**
|
||||
* 表格级联数据事件
|
||||
* dynkey 动态表格KEY
|
||||
*/
|
||||
export const onCascaded = (...keyObjs: DicDataInterface[]) => {
|
||||
return async (state: any, form?: any, dynkey?: any) => {
|
||||
let dataList = dynkey? form[dynkey] : state.dataList;
|
||||
if (dataList && dataList.length > 0) {
|
||||
try {
|
||||
let cascadeDic = {}
|
||||
for (const keyObj of keyObjs) {
|
||||
let {key, dicUrl, cascades} = keyObj;
|
||||
if (!cascades) continue;
|
||||
for (const cascade of cascades) {
|
||||
let realDicUrl = dicUrl;
|
||||
if (!realDicUrl) realDicUrl = dicCascadeFun[cascade];
|
||||
for (let i = 0; i < dataList.length; i++) {
|
||||
let param = dataList[i][key];
|
||||
if (validateNull(param)) continue;
|
||||
if (!validateNull(cascadeDic[param])) continue
|
||||
// @ts-ignore
|
||||
let url = realDicUrl.replace("{{key}}", param)
|
||||
if (url.indexOf("?") !== -1) {
|
||||
let params = url.split('?')[1].split('=');
|
||||
let p = dataList[i][params[0]];
|
||||
if (validateNull(p)) continue;
|
||||
url = url.replace("{{" + params[0] + "}}", p)
|
||||
}
|
||||
// @ts-ignore
|
||||
let res = await listDicUrl(url);
|
||||
if (validateNull(res.data)) continue;
|
||||
cascadeDic[param] = res.data
|
||||
}
|
||||
}
|
||||
}
|
||||
Object.assign(state.cascadeDic, cascadeDic);
|
||||
} catch (err: any) {
|
||||
// 捕获异常并显示错误提示
|
||||
ElMessage.error(err.msg || err.data.msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 表单级联数据变更事件
|
||||
* index 动态表格index
|
||||
*/
|
||||
export const onCascadeChange = (cascadeDic: any, ...keyObjs: DicDataInterface[]) => {
|
||||
return async (form: any, keyObjs2?: DicDataInterface, dynkey?: any, index?: number) => {
|
||||
try {
|
||||
let realKeyObjs = validateNull(keyObjs) ? {} : deepClone(keyObjs)
|
||||
if (keyObjs2 && !validateNull(keyObjs2)) realKeyObjs = [deepClone(keyObjs2)]
|
||||
for (const keyObj of realKeyObjs) {
|
||||
let {key, dicUrl, cascades, prefix} = keyObj;
|
||||
if (!cascades) continue;
|
||||
for (const cascade of cascades) {
|
||||
let realDicUrl = dicUrl;
|
||||
let reqKey = cascade;
|
||||
if (prefix) reqKey = prefix + "." + cascade;
|
||||
if (!realDicUrl) realDicUrl = dicCascadeFun[reqKey];
|
||||
// @ts-ignore
|
||||
let param = dynkey ? form[dynkey][index][key] : form[key];
|
||||
if (!realDicUrl || validateNull(param)) {
|
||||
if (validateNull(param)) clearCascadeVal(form, keyObjs2, cascade, dynkey, index)
|
||||
continue;
|
||||
}
|
||||
let url = realDicUrl.replace("{{key}}", param)
|
||||
if (url.indexOf("?") !== -1) {
|
||||
let params = url.split('?')[1].split('=');
|
||||
// @ts-ignore
|
||||
let p = dynkey ? form[dynkey][index][params[0]] : form[params[0]];
|
||||
if (validateNull(p)) continue;
|
||||
url = url.replace("{{" + params[0] + "}}", p)
|
||||
}
|
||||
// @ts-ignore
|
||||
let res = await listDicUrl(url);
|
||||
if (validateNull(res.data)) res.data = [];
|
||||
let data = res.data;
|
||||
if (!(data instanceof Array)) data = data ? [data] : []
|
||||
if (typeof(index) !== 'undefined') {
|
||||
cascadeDic[param] = data;
|
||||
} else {
|
||||
cascadeDic[cascade] = data;
|
||||
}
|
||||
clearCascadeVal(form, keyObjs2, cascade, dynkey, index)
|
||||
}
|
||||
}
|
||||
} catch (err: any) {
|
||||
// 捕获异常并显示错误提示
|
||||
ElMessage.error(err.msg || err.data.msg);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function clearCascadeVal(form, keyObjs2, cascade, dynkey, index){
|
||||
if (typeof(index) !== 'undefined') {
|
||||
if (!validateNull(keyObjs2)) form[dynkey][index][cascade] = null
|
||||
} else {
|
||||
if (!validateNull(keyObjs2)) form[cascade] = null
|
||||
}
|
||||
}
|
||||
|
||||
const dicUrls = {
|
||||
flowApplicationGroupName: "/order/flow-application/list/group-name",
|
||||
listFlowApplication: "/order/flow-application/list",
|
||||
defFlows: "/jsonflow/def-flow/list",
|
||||
defFlowGroupName: "/jsonflow/def-flow/list/group-name",
|
||||
listRoleName: "/jsonflow/user-role-auditor/list/roles?roleName={{roleName}}",
|
||||
listTableName: "/order/create-table/list",
|
||||
listUserKey: "/jsonflow/node-job/list/user-key",
|
||||
listFlowNodeId: "/jsonflow/flow-node/list",
|
||||
listNodeJobId: "/jsonflow/node-job/list",
|
||||
runFlows: "/jsonflow/run-flow/list",
|
||||
listDept: "/jsonflow/user-role-auditor/list/depts?deptName={{deptName}}",
|
||||
listPost: "/jsonflow/user-role-auditor/list/posts?postName={{postName}}",
|
||||
listUserKeyVal: "/jsonflow/node-job/list/user-key-val",
|
||||
listVarKey: "/jsonflow/flow-node-rel/list/var-key",
|
||||
listVarKeyVal: "/jsonflow/flow-node-rel/list/var-key-val",
|
||||
listVarVal: "/jsonflow/flow-clazz/list/var-val",
|
||||
listUsers: "/jsonflow/user-role-auditor/list/users?userName={{userName}}",
|
||||
}
|
||||
|
||||
/**
|
||||
* 默认回调映射关系
|
||||
*/
|
||||
const dicUrlFun = {
|
||||
groupName: dicUrls.flowApplicationGroupName,
|
||||
"defFlow.groupName": dicUrls.defFlowGroupName,
|
||||
flowKey: dicUrls.defFlows,
|
||||
carbonCopy: dicUrls.listUsers,
|
||||
carbonCopyPerson: dicUrls.listUsers,
|
||||
tableName: dicUrls.listTableName,
|
||||
permission: dicUrls.listRoleName,
|
||||
defFlowId: dicUrls.defFlows,
|
||||
userKey: dicUrls.listUserKey,
|
||||
roleId: dicUrls.listRoleName,
|
||||
userId: dicUrls.listUsers,
|
||||
handleUserId: dicUrls.listUsers,
|
||||
flowNodeId: dicUrls.listFlowNodeId,
|
||||
nodeJobId: dicUrls.listNodeJobId,
|
||||
initiatorId: dicUrls.listUsers,
|
||||
createUser: dicUrls.listUsers,
|
||||
code: dicUrls.runFlows,
|
||||
flowInstId: dicUrls.runFlows,
|
||||
receiveUser: dicUrls.listUsers,
|
||||
receiveDept: dicUrls.listDept,
|
||||
defFlows: dicUrls.defFlows,
|
||||
userKeys: dicUrls.listUserKey,
|
||||
userKeyVals: dicUrls.listUserKeyVal,
|
||||
varKeyVals: dicUrls.listVarKeyVal,
|
||||
varVals: dicUrls.listVarVal,
|
||||
roles: dicUrls.listRoleName,
|
||||
roles2: dicUrls.listRoleName,
|
||||
users: dicUrls.listUsers,
|
||||
users2: dicUrls.listUsers,
|
||||
delRoleId: dicUrls.listUsers,
|
||||
posts: dicUrls.listPost,
|
||||
posts2: dicUrls.listPost,
|
||||
depts: dicUrls.listDept,
|
||||
depts2: dicUrls.listDept,
|
||||
formId: dicUrls.listFlowApplication,
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载业务字典数据
|
||||
*/
|
||||
export const onLoadDicUrl = (...keyObjs: DicDataInterface[]) => {
|
||||
return async (dicData: any, form?: any, keyObjs2?: DicDataInterface) => {
|
||||
try {
|
||||
let realKeyObjs = validateNull(keyObjs) ? {} : deepClone(keyObjs)
|
||||
if (keyObjs2 && !validateNull(keyObjs2)) realKeyObjs = [deepClone(keyObjs2)]
|
||||
if (!realKeyObjs) return
|
||||
for (const keyObj of realKeyObjs) {
|
||||
let {key, dicUrl, prefix} = keyObj;
|
||||
let res;
|
||||
if (dicUrl) res = await listDicUrl(dicUrl);
|
||||
else {
|
||||
let reqKey = key;
|
||||
if (prefix) reqKey = prefix + "." + key;
|
||||
let url = dicUrlFun[reqKey]
|
||||
if (url.indexOf("?") !== -1) {
|
||||
if (!validateNull(form)) {
|
||||
let params = url.split('?')[1].split('=');
|
||||
let param = form[params[0]];
|
||||
if (validateNull(param)) continue;
|
||||
url = url.replace("{{" + params[0] + "}}", param)
|
||||
} else {
|
||||
url = url.split('?')[0]
|
||||
}
|
||||
}
|
||||
res = await listDicUrl(url);
|
||||
}
|
||||
validateNull(res.data) ? dicData[key] = [] : dicData[key] = res.data;
|
||||
}
|
||||
} catch (err: any) {
|
||||
// 捕获异常并显示错误提示
|
||||
ElMessage.error(err.msg || err.data.msg);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 更新业务字典数据
|
||||
*/
|
||||
export const onUpdateDicData = (...keyObjs: DicDataInterface[]) => {
|
||||
return async (dicData: any, form: any, formKey?: any) => {
|
||||
if (!validateNull(dicData) && !validateNull(form)) {
|
||||
try {
|
||||
for (const keyObj of keyObjs) {
|
||||
let { key } = keyObj;
|
||||
let newVal = form[key];
|
||||
let dicDataVal = dicData[key];
|
||||
if (validateNull(newVal) || validateNull(dicDataVal)) continue;
|
||||
else {
|
||||
let exist = dicDataVal.filter(f => f[key] === newVal);
|
||||
if (!validateNull(exist)) continue;
|
||||
}
|
||||
let realKey = formKey ? formKey : key;
|
||||
dicData[key].unshift({[realKey]: newVal});
|
||||
}
|
||||
} catch (err: any) {
|
||||
// 捕获异常并显示错误提示
|
||||
ElMessage.error(err.msg || err.data.msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export async function remoteMethodByKey(query, onLoad, dicData, param, key) {
|
||||
if (query) {
|
||||
let form = {}
|
||||
form[param] = query
|
||||
await onLoad(dicData, form, {key: key});
|
||||
} else {
|
||||
dicData[key] = []
|
||||
}
|
||||
}
|
||||
|
||||
export function initRemoteMethodAllByKey(onLoad, dicData) {
|
||||
return {
|
||||
async remoteMethodUser(query: string) {
|
||||
await remoteMethodByKey(query, onLoad, dicData, 'userName', "users")
|
||||
},
|
||||
async remoteMethodRole(query: string) {
|
||||
await remoteMethodByKey(query, onLoad, dicData, 'roleName', "roles")
|
||||
},
|
||||
async remoteMethodPost(query: string) {
|
||||
await remoteMethodByKey(query, onLoad, dicData, 'postName', "posts")
|
||||
},
|
||||
async remoteMethodDept(query: string) {
|
||||
await remoteMethodByKey(query, onLoad, dicData, 'deptName', "depts")
|
||||
},
|
||||
async remoteMethodUser2(query: string) {
|
||||
await remoteMethodByKey(query, onLoad, dicData, 'userName', "users2")
|
||||
},
|
||||
async remoteMethodRole2(query: string) {
|
||||
await remoteMethodByKey(query, onLoad, dicData, 'roleName', "roles2")
|
||||
},
|
||||
async remoteMethodPost2(query: string) {
|
||||
await remoteMethodByKey(query, onLoad, dicData, 'postName', "posts2")
|
||||
},
|
||||
async remoteMethodDept2(query: string) {
|
||||
await remoteMethodByKey(query, onLoad, dicData, 'deptName', "depts2")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export async function remoteMethodAllByKey(onLoad, dicData, query: string, jobType) {
|
||||
if (jobType === DIC_PROP.JOB_USER_TYPE[0].value) {
|
||||
await remoteMethodByKey(query, onLoad, dicData, 'userName', "users")
|
||||
} else if (jobType === DIC_PROP.JOB_USER_TYPE[1].value) {
|
||||
await remoteMethodByKey(query, onLoad, dicData, 'roleName', "roles")
|
||||
} else if (jobType === DIC_PROP.JOB_USER_TYPE[2].value) {
|
||||
await remoteMethodByKey(query, onLoad, dicData, 'postName', "posts")
|
||||
} else if (jobType === DIC_PROP.JOB_USER_TYPE[3].value) {
|
||||
await remoteMethodByKey(query, onLoad, dicData, 'deptName', "depts")
|
||||
} else useMessage().warning("请先选择参与者类型")
|
||||
}
|
||||
74
src/flow/components/convert-name/group-index.vue
Normal file
74
src/flow/components/convert-name/group-index.vue
Normal file
@@ -0,0 +1,74 @@
|
||||
<template>
|
||||
<div>
|
||||
<template v-for="(item, index) in values">
|
||||
<template v-if="validateGroupOptions(item)">
|
||||
<span v-if="!props.elTagType" :key="index" :class="props.elTagClass" :style="props.style">
|
||||
{{ showKeyName(item, values , index) }}</span>
|
||||
<el-tag
|
||||
v-else
|
||||
:disable-transitions="true"
|
||||
:key="index * 2"
|
||||
:index="index"
|
||||
:type="props.elTagType === 'primary' ? null : props.elTagType"
|
||||
:class="props.elTagClass" :style="props.style"
|
||||
>{{ showKeyName(item, values, index) }}</el-tag>
|
||||
</template>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="convert-group-name">
|
||||
import { computed } from 'vue';
|
||||
|
||||
const props = defineProps({
|
||||
// 数据
|
||||
options: {
|
||||
type: Array as any,
|
||||
default: [],
|
||||
},
|
||||
// 当前的值
|
||||
value: [Number, String, Array],
|
||||
// 当前KEY
|
||||
valueKey: String,
|
||||
// 显示KEY
|
||||
showKey: String,
|
||||
// type
|
||||
elTagType: String,
|
||||
// class
|
||||
elTagClass: String,
|
||||
// style
|
||||
style: [String, Object]
|
||||
});
|
||||
|
||||
const values = computed(() => {
|
||||
if (props.value !== null && typeof props.value !== 'undefined') {
|
||||
return Array.isArray(props.value) ? props.value : [String(props.value)];
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
});
|
||||
|
||||
function validateGroupOptions(item){
|
||||
let exist = null
|
||||
for (const option of props.options) {
|
||||
let find = option.options.find(f => f[props.valueKey] === item);
|
||||
if (find) {
|
||||
exist = find
|
||||
break;
|
||||
}
|
||||
}
|
||||
return exist
|
||||
}
|
||||
|
||||
function showKeyName(item, values, index){
|
||||
let exist = validateGroupOptions(item)
|
||||
return exist[props.showKey] + (index !== (values.length - 1) ? "," : "")
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.el-tag + .el-tag {
|
||||
margin-left: 10px;
|
||||
}
|
||||
</style>
|
||||
62
src/flow/components/convert-name/index.vue
Normal file
62
src/flow/components/convert-name/index.vue
Normal file
@@ -0,0 +1,62 @@
|
||||
<template>
|
||||
<div>
|
||||
<template v-for="(item, index) in values">
|
||||
<template v-if="props.options.some(s => s[props.valueKey] === item)">
|
||||
<span v-if="!props.elTagType" :key="index" :class="props.elTagClass" :style="props.style">
|
||||
{{ showKeyName(item, values , index) }}</span>
|
||||
<el-tag
|
||||
v-else
|
||||
:disable-transitions="true"
|
||||
:key="index * 2"
|
||||
:index="index"
|
||||
:type="props.elTagType === 'primary' ? null : props.elTagType"
|
||||
:class="props.elTagClass" :style="props.style"
|
||||
>{{ showKeyName(item, values, index) }}</el-tag>
|
||||
</template>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="convert-name">
|
||||
import { computed } from 'vue';
|
||||
|
||||
const props = defineProps({
|
||||
// 数据
|
||||
options: {
|
||||
type: Array as any,
|
||||
default: [],
|
||||
},
|
||||
// 当前的值
|
||||
value: [Number, String, Array],
|
||||
// 当前KEY
|
||||
valueKey: String,
|
||||
// 显示KEY
|
||||
showKey: String,
|
||||
// type
|
||||
elTagType: String,
|
||||
// class
|
||||
elTagClass: String,
|
||||
// style
|
||||
style: [String, Object]
|
||||
});
|
||||
|
||||
const values = computed(() => {
|
||||
if (props.value !== null && typeof props.value !== 'undefined') {
|
||||
return Array.isArray(props.value) ? props.value : [String(props.value)];
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
});
|
||||
|
||||
function showKeyName(item, values, index){
|
||||
let find = props.options.find(f => f[props.valueKey] === item);
|
||||
return find[props.showKey] + (index !== (values.length - 1) ? "," : "")
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.el-tag + .el-tag {
|
||||
margin-left: 10px;
|
||||
}
|
||||
</style>
|
||||
156
src/flow/components/convert-name/role-index.vue
Normal file
156
src/flow/components/convert-name/role-index.vue
Normal file
@@ -0,0 +1,156 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-dialog
|
||||
v-if="data.showRoleUsers"
|
||||
v-model="data.showRoleUsers"
|
||||
top="20px" width="70%"
|
||||
title="参与者候选人员"
|
||||
append-to-body>
|
||||
<el-table :data="data.dataList"
|
||||
border
|
||||
style="width: 100%">
|
||||
<el-table-column :label="$t('sysuser.index')" type="index" width="40" />
|
||||
<el-table-column :label="$t('sysuser.username')" prop="username" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column :label="$t('sysuser.name')" prop="name" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column :label="$t('sysuser.phone')" prop="phone" show-overflow-tooltip></el-table-column>
|
||||
</el-table>
|
||||
</el-dialog>
|
||||
|
||||
<template v-for="(item, index) in values">
|
||||
<template v-if="!validateNull(props.options) && validateOptionsNotNull()">
|
||||
<span v-if="!props.elTagType && !validateJobRoleType(item)" :key="index" :class="props.elTagClass">
|
||||
{{ showKeyNameByJobType(item, values , index) }}</span>
|
||||
<el-tooltip content="点击可查看参与者具体的人员信息" placement="top" v-if="!props.elTagType && validateJobRoleType(item)" :key="index">
|
||||
<el-link type="primary" @click="handleShowJobRoleUsers(item)">
|
||||
{{ showKeyNameByJobType(item, values , index) }}</el-link>
|
||||
</el-tooltip>
|
||||
<el-tag
|
||||
v-if="props.elTagType"
|
||||
:disable-transitions="true"
|
||||
:key="index * 2"
|
||||
:index="index"
|
||||
:type="props.elTagType === 'primary' ? null : props.elTagType"
|
||||
:class="props.elTagClass"
|
||||
>{{ showKeyNameByJobType(item, values, index) }}</el-tag>
|
||||
</template>
|
||||
|
||||
<template v-else-if="validateNull(props.options) && item.jobType">
|
||||
<span v-if="!props.elTagType && !validateJobRoleType(item)" :key="index" :class="props.elTagClass">
|
||||
{{ showRoleNameByJobType(item, values , index) }}</span>
|
||||
<el-tooltip content="点击可查看参与者具体的人员信息" placement="top" v-if="!props.elTagType && validateJobRoleType(item)" :key="index">
|
||||
<el-link type="primary" @click="handleShowJobRoleUsers(item)">
|
||||
{{ showRoleNameByJobType(item, values , index) }}</el-link>
|
||||
</el-tooltip>
|
||||
<el-tag
|
||||
v-if="props.elTagType"
|
||||
:disable-transitions="true"
|
||||
:key="index * 2"
|
||||
:index="index"
|
||||
:type="props.elTagType === 'primary' ? null : props.elTagType"
|
||||
:class="props.elTagClass"
|
||||
>{{ showRoleNameByJobType(item, values, index) }}</el-tag>
|
||||
</template>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="convert-role-name">
|
||||
import { computed } from 'vue';
|
||||
import {validateNull} from "/@/utils/validate";
|
||||
import {DIC_PROP} from "/@/flow/support/dict-prop";
|
||||
import {handleShowNameByJobType, handleShowRoleNameByJobType} from "/@/flow";
|
||||
import {listUsersByRoleId} from "/@/api/jsonflow/common";
|
||||
import {useI18n} from "vue-i18n";
|
||||
|
||||
const {t} = useI18n();
|
||||
const props = defineProps({
|
||||
// 数据
|
||||
options: {
|
||||
type: Object as any,
|
||||
default: {},
|
||||
},
|
||||
// 当前的值
|
||||
value: [Number, String, Object, Array],
|
||||
// type
|
||||
elTagType: String,
|
||||
// class
|
||||
elTagClass: String,
|
||||
isJobType: String
|
||||
});
|
||||
|
||||
const data = reactive({
|
||||
showRoleUsers: false,
|
||||
dataList: [],
|
||||
});
|
||||
|
||||
const values = computed(() => {
|
||||
if (props.value !== null && typeof props.value !== 'undefined') {
|
||||
if (Array.isArray(props.value)) return props.value
|
||||
else if (typeof props.value === 'object') {
|
||||
return [props.value]
|
||||
}
|
||||
return [String(props.value)];
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
});
|
||||
|
||||
function validateOptionsNotNull(){
|
||||
return props.options.users && props.options.roles && props.options.posts && props.options.depts
|
||||
}
|
||||
|
||||
function showRoleNameByJobType(role, values, index){
|
||||
if (role.roleName) {
|
||||
let name = handleShowRoleNameByJobType(role, props.isJobType)
|
||||
return name + (index !== (values.length - 1) ? "," : "")
|
||||
} else {
|
||||
showNameByJobType(role, values, index)
|
||||
}
|
||||
}
|
||||
|
||||
function showNameByJobType(role, values, index){
|
||||
let name = handleShowNameByJobType(role, props.isJobType)
|
||||
return name + (index !== (values.length - 1) ? "," : "")
|
||||
}
|
||||
|
||||
function validateExist(role){
|
||||
let exist;
|
||||
if (role.jobType === DIC_PROP.JOB_USER_TYPE[0].value) {
|
||||
exist = props.options.users.find(s => s.userId === role.roleId)
|
||||
} else if (role.jobType === DIC_PROP.JOB_USER_TYPE[1].value) {
|
||||
exist = props.options.roles.find(s => s.roleId === role.roleId)
|
||||
} else if (role.jobType === DIC_PROP.JOB_USER_TYPE[2].value) {
|
||||
exist = props.options.posts.find(s => s.postId === role.roleId)
|
||||
} else if (role.jobType === DIC_PROP.JOB_USER_TYPE[3].value) {
|
||||
exist = props.options.depts.find(s => s.deptId === role.roleId)
|
||||
}
|
||||
if (exist) exist.jobType = role.jobType
|
||||
else exist = {}
|
||||
return exist
|
||||
}
|
||||
|
||||
function handleShowJobRoleUsers(item){
|
||||
if (!item.roleId || !item.jobType) return
|
||||
listUsersByRoleId(item.roleId, item.jobType).then(resp => {
|
||||
data.dataList = resp.data
|
||||
data.showRoleUsers = true
|
||||
})
|
||||
}
|
||||
|
||||
function validateJobRoleType(item){
|
||||
let isRoleType = item.jobType !== DIC_PROP.JOB_USER_NONE_TYPE[0].value && item.jobType !== DIC_PROP.JOB_USER_NONE_TYPE[1].value;
|
||||
return isRoleType && item.roleId
|
||||
}
|
||||
|
||||
function showKeyNameByJobType(role, values, index){
|
||||
let exist = validateExist(role);
|
||||
return showNameByJobType(exist, values, index)
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.el-tag + .el-tag {
|
||||
margin-left: 10px;
|
||||
}
|
||||
</style>
|
||||
141
src/flow/components/custom-form/handle.vue
Normal file
141
src/flow/components/custom-form/handle.vue
Normal file
@@ -0,0 +1,141 @@
|
||||
<template>
|
||||
<div class="layout-padding">
|
||||
<div>
|
||||
<!-- 动态组件 -->
|
||||
<dynamic-link v-if="data.currElTab.active" :currJob="props.currJob" :currElTab="data.currElTab"></dynamic-link>
|
||||
|
||||
<template v-if="operType !== 'view'">
|
||||
<div style="text-align: center">
|
||||
<span class="dialog-footer">
|
||||
<template v-if="props.currJob.status !== DIC_PROP.ORDER_STATUS[0].value">
|
||||
<el-button type="primary" @click="methods.handleForm('onSubmit')" :disabled="loading">{{ $t('jfI18n.initialBtn') }}</el-button>
|
||||
<el-button @click="methods.cancelButton">{{ $t('common.cancelButtonText') }}</el-button>
|
||||
<el-button type="primary" @click="methods.handleForm('onTemp')" :disabled="loading">{{ $t('jfI18n.temp') }}</el-button>
|
||||
</template>
|
||||
<template v-else>
|
||||
<el-button type="primary" @click="methods.handleForm('onSubmit')" :disabled="loading">{{ $t('common.editBtn') }}</el-button>
|
||||
<el-button type="primary" @click="printForm" v-if="props.currJob.printInfo">{{
|
||||
t('jfI18n.print')
|
||||
}}
|
||||
</el-button>
|
||||
</template>
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<div style="text-align: center">
|
||||
<span class="dialog-footer">
|
||||
<el-button type="primary" @click="printForm" v-if="props.currJob.printInfo">{{
|
||||
t('jfI18n.print')
|
||||
}}
|
||||
</el-button>
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<!-- 打印表单 -->
|
||||
<el-dialog v-model="data.showTinymceView" top="20px" width="700px"
|
||||
:title="data.tinymceTitle" append-to-body
|
||||
@close="closePrint">
|
||||
<tinymce-view v-if="data.showTinymceView" :currFlowForm="props.currJob" :elTab="data.currElTab"></tinymce-view>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="CustomForm">
|
||||
import {useI18n} from 'vue-i18n';
|
||||
import {useMessage} from "/@/hooks/message";
|
||||
|
||||
const {t} = useI18n();
|
||||
|
||||
// 引入组件
|
||||
const DynamicLink = defineAsyncComponent(() => import('../handle-job/dynamic-link.vue'));
|
||||
const TinymceView = defineAsyncComponent(() => import('/@/flow/components/tinymce/TinymceView.vue'));
|
||||
|
||||
const loading = ref(false);
|
||||
const operType = ref(false);
|
||||
const title = ref('');
|
||||
|
||||
const props = defineProps({
|
||||
currJob: {
|
||||
type: Object,
|
||||
default: null,
|
||||
}
|
||||
});
|
||||
|
||||
const data = reactive({
|
||||
currElTab: {active: null},
|
||||
showTinymceView: false,
|
||||
tinymceTitle: null,
|
||||
});
|
||||
const $emit = defineEmits(['onHandleForm']);
|
||||
|
||||
const methods = {
|
||||
// 初始化数据
|
||||
initJobData() {
|
||||
methods.openDialog(props.currJob.operType)
|
||||
data.currElTab = props.currJob.currElTab
|
||||
data.currElTab.active = data.currElTab.id
|
||||
},
|
||||
// 打开弹窗
|
||||
openDialog(type: string) {
|
||||
operType.value = type;
|
||||
|
||||
if (type === 'add') {
|
||||
title.value = t('common.addBtn');
|
||||
} else if (type === 'edit') {
|
||||
title.value = t('common.editBtn');
|
||||
} else if (type === 'view') {
|
||||
title.value = t('common.viewBtn');
|
||||
} else if (type === 'copy') {
|
||||
title.value = t('common.copyBtn');
|
||||
}
|
||||
},
|
||||
// 提交表单
|
||||
async handleForm(method) {
|
||||
let exist = props.currJob[method]
|
||||
if (exist) {
|
||||
let res = await exist();
|
||||
if (res === true) methods.cancelButton()
|
||||
} else {
|
||||
useMessage().info('正在加载, 请稍等')
|
||||
}
|
||||
},
|
||||
cancelButton(){
|
||||
$emit('onHandleForm', false)
|
||||
}
|
||||
}
|
||||
|
||||
function printForm() {
|
||||
props.currJob.resolvePrintForm()
|
||||
data.tinymceTitle = props.currJob.formName
|
||||
data.showTinymceView = true
|
||||
}
|
||||
|
||||
function closePrint(){
|
||||
props.currJob.resolveClosePrint()
|
||||
}
|
||||
|
||||
async function getFormData() {
|
||||
return await props.currJob.getFormData()
|
||||
}
|
||||
|
||||
// 暴露变量
|
||||
defineExpose({
|
||||
getFormData,
|
||||
})
|
||||
|
||||
// 监听双向绑定
|
||||
watch(
|
||||
() => props.currJob.id,
|
||||
(val) => {
|
||||
methods.initJobData();
|
||||
}
|
||||
);
|
||||
|
||||
onMounted(() => {
|
||||
methods.initJobData();
|
||||
});
|
||||
</script>
|
||||
|
||||
44
src/flow/components/form-create/api.ts
Normal file
44
src/flow/components/form-create/api.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
import FcDesigner from 'form-create-designer';
|
||||
import {listDicData, listDicUrl} from "/@/api/jsonflow/common";
|
||||
import {validateNull} from "/@/utils/validate";
|
||||
import request from '/@/utils/request';
|
||||
|
||||
export function initFcDesignerFetch(formRef, formData, globalData) {
|
||||
// 配置表单请求拦截器
|
||||
FcDesigner.designerForm.fetch = FcDesigner.formCreate.fetch = async (options: any) => {
|
||||
// 发起请求
|
||||
let res
|
||||
if (options.method === 'GET') {
|
||||
res = await listDicUrl(options.action, options.query);
|
||||
} else {
|
||||
if (options.file) {
|
||||
res = await handleHttpUpload(options)
|
||||
options.onSuccess(res);
|
||||
return
|
||||
} else {
|
||||
res = await listDicData(options.action, options.data);
|
||||
}
|
||||
}
|
||||
if (validateNull(res.data)) return
|
||||
options.onSuccess(res.data);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
const handleHttpUpload = async (options) => {
|
||||
let formData = new FormData();
|
||||
formData.append('file', options.file);
|
||||
try {
|
||||
return await request({
|
||||
url: options.action,
|
||||
method: 'post',
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data',
|
||||
'Enc-Flag': 'false',
|
||||
},
|
||||
data: formData,
|
||||
});
|
||||
} catch (error) {
|
||||
options.onError(error as any);
|
||||
}
|
||||
};
|
||||
167
src/flow/components/form-create/designer.vue
Normal file
167
src/flow/components/form-create/designer.vue
Normal file
@@ -0,0 +1,167 @@
|
||||
<template>
|
||||
<div :class="!props.layoutPadding ? '' : 'layout-padding'">
|
||||
<div :class="!props.layoutPadding ? '' : 'layout-padding-auto layout-padding-view'">
|
||||
<fc-designer ref="designer" height="100vh" :config="config.designer" :handle="config.handle" @save="props.saveFormInfo"/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="FlowForm">
|
||||
import FcDesigner from 'form-create-designer';
|
||||
import type { Config, DragRule } from 'form-create-designer';
|
||||
import ZhCn from 'form-create-designer/src/locale/zh-cn';
|
||||
import En from 'form-create-designer/src/locale/en';
|
||||
import {Local} from '/@/utils/storage';
|
||||
import { rules } from './rules';
|
||||
import {useI18n} from "vue-i18n"
|
||||
import {validateNull} from "/@/utils/validate";
|
||||
import {useUserInfo} from "/@/stores/userInfo";
|
||||
import {deepClone} from "/@/utils/other";
|
||||
import {notifyLeft} from "../../index";
|
||||
import {initFcDesignerFetch} from "./api";
|
||||
|
||||
const {t} = useI18n();
|
||||
const {proxy} = getCurrentInstance();
|
||||
const props = defineProps({
|
||||
currFlowForm: {
|
||||
type: Object,
|
||||
default: null,
|
||||
},
|
||||
layoutPadding: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
showSaveBtn: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
saveFormInfo: {
|
||||
type: Function,
|
||||
default: null,
|
||||
},
|
||||
});
|
||||
|
||||
// 组件引用
|
||||
const designer = ref<InstanceType<typeof FcDesigner> | null>(null);
|
||||
|
||||
// 设计器配置
|
||||
const config = {
|
||||
designer: {
|
||||
fieldReadonly: false, // 字段是否只读
|
||||
showSaveBtn: props.showSaveBtn,
|
||||
},
|
||||
handle: [
|
||||
{
|
||||
label: '中英切换',
|
||||
handle: () => {
|
||||
changeLocale();
|
||||
},
|
||||
},
|
||||
],
|
||||
locale: null,
|
||||
}
|
||||
|
||||
function changeLocale(isInit?) {
|
||||
if (!isInit) {
|
||||
if (config.locale === En) {
|
||||
config.locale = ZhCn;
|
||||
} else {
|
||||
config.locale = En;
|
||||
}
|
||||
} else {
|
||||
// 语言
|
||||
let globalI18n = Local.get('themeConfig').globalI18n;
|
||||
if (globalI18n === 'en') {
|
||||
config.locale = En;
|
||||
} else {
|
||||
config.locale = ZhCn;
|
||||
}
|
||||
}
|
||||
FcDesigner.useLocale(config.locale);
|
||||
}
|
||||
|
||||
const methods = {
|
||||
syncCurrFlowForm() {
|
||||
// 保存表单
|
||||
let json = designer.value.getRule()
|
||||
if (validateNull(json)) props.currFlowForm.formInfo = null
|
||||
else {
|
||||
let options = designer.value.getOptions()
|
||||
options.widgetList = json
|
||||
props.currFlowForm.formInfo = options
|
||||
}
|
||||
},
|
||||
handleSubmit(callback, isNotify) {
|
||||
methods.syncCurrFlowForm()
|
||||
let json = props.currFlowForm.formInfo
|
||||
if (validateNull(json)) {
|
||||
if (isNotify) notifyLeft('表单设计请添加组件', 'warning')
|
||||
return false
|
||||
}
|
||||
props.currFlowForm.formDesign = true
|
||||
if (callback) callback()
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
const data = reactive({
|
||||
globalDsv: {
|
||||
type: Object,
|
||||
default: {},
|
||||
}
|
||||
});
|
||||
|
||||
function initFcDesigner() {
|
||||
designer.value.addMenu({ name: 'biz', title: '业务组件', list: []});
|
||||
// Add all rules at once
|
||||
rules.forEach((rule) => {
|
||||
designer.value?.addComponent(rule as DragRule);
|
||||
});
|
||||
|
||||
initFcDesignerFetch(designer.value, null, data.globalDsv)
|
||||
changeLocale(true);
|
||||
}
|
||||
|
||||
function getFormInfo() {
|
||||
initFcDesigner()
|
||||
data.globalDsv.userInfos = useUserInfo().userInfos
|
||||
let formInfo = deepClone(props.currFlowForm.formInfo)
|
||||
if (validateNull(formInfo)) {
|
||||
designer.value.setRule([]);
|
||||
return
|
||||
}
|
||||
designer.value.setRule(formInfo.widgetList);
|
||||
delete formInfo.widgetList
|
||||
designer.value.setOptions(formInfo);
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
nextTick(() => {
|
||||
getFormInfo();
|
||||
})
|
||||
});
|
||||
|
||||
// 暴露变量
|
||||
defineExpose({
|
||||
handleSubmit: methods.handleSubmit
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.fc-form-row {
|
||||
// 选中效果
|
||||
._fd-drag-tool.active {
|
||||
outline: 2px solid #2e73ff !important;
|
||||
}
|
||||
// 栅格线条
|
||||
._fd-drag-tool {
|
||||
outline: 1px dashed var(--fc-tool-border-color)!important;
|
||||
}
|
||||
}
|
||||
// 设置事件样式
|
||||
._fd-event-l {
|
||||
.el-menu-item {
|
||||
line-height: 1em!important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
46
src/flow/components/form-create/email.vue
Normal file
46
src/flow/components/form-create/email.vue
Normal file
@@ -0,0 +1,46 @@
|
||||
<template>
|
||||
<div style="width: 100%">
|
||||
<el-input v-model="data.modelValue" @blur="changeModelValue" :disabled="props.disabled" clearable>
|
||||
</el-input>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="UserRolePickerIndex">
|
||||
import {useMessage} from "/@/hooks/message";
|
||||
import {verifyEmail} from "/@/utils/toolsValidate";
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
});
|
||||
|
||||
const emits = defineEmits(['update:modelValue']);
|
||||
|
||||
const $message = useMessage();
|
||||
const data = reactive({
|
||||
modelValue: null
|
||||
})
|
||||
|
||||
const changeModelValue = () => {
|
||||
if (data.modelValue) {
|
||||
let boolean = verifyEmail(data.modelValue);
|
||||
if (!boolean) {
|
||||
data.modelValue = null
|
||||
$message.success("请输入正确的邮箱")
|
||||
}
|
||||
emits('update:modelValue', data.modelValue);
|
||||
} else {
|
||||
emits('update:modelValue', null);
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
data.modelValue = props.modelValue
|
||||
})
|
||||
</script>
|
||||
40
src/flow/components/form-create/flow-name.vue
Normal file
40
src/flow/components/form-create/flow-name.vue
Normal file
@@ -0,0 +1,40 @@
|
||||
<template>
|
||||
<div style="width: 100%">
|
||||
<el-input v-model="data.modelValue" @blur="changeModelValue" :disabled="props.disabled" clearable>
|
||||
</el-input>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="UserRolePickerIndex">
|
||||
import {useMessage} from "/@/hooks/message";
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
});
|
||||
|
||||
const emits = defineEmits(['update:modelValue']);
|
||||
|
||||
const $message = useMessage();
|
||||
const data = reactive({
|
||||
modelValue: null
|
||||
})
|
||||
|
||||
const changeModelValue = () => {
|
||||
if (data.modelValue) {
|
||||
emits('update:modelValue', data.modelValue);
|
||||
} else {
|
||||
emits('update:modelValue', null);
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
data.modelValue = props.modelValue
|
||||
})
|
||||
</script>
|
||||
40
src/flow/components/form-create/form-code.vue
Normal file
40
src/flow/components/form-create/form-code.vue
Normal file
@@ -0,0 +1,40 @@
|
||||
<template>
|
||||
<div style="width: 100%">
|
||||
<el-input v-model="data.modelValue" @blur="changeModelValue" :disabled="props.disabled" clearable>
|
||||
</el-input>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="UserRolePickerIndex">
|
||||
import {useMessage} from "/@/hooks/message";
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
});
|
||||
|
||||
const emits = defineEmits(['update:modelValue']);
|
||||
|
||||
const $message = useMessage();
|
||||
const data = reactive({
|
||||
modelValue: null
|
||||
})
|
||||
|
||||
const changeModelValue = () => {
|
||||
if (data.modelValue) {
|
||||
emits('update:modelValue', data.modelValue);
|
||||
} else {
|
||||
emits('update:modelValue', null);
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
data.modelValue = props.modelValue
|
||||
})
|
||||
</script>
|
||||
40
src/flow/components/form-create/form-name.vue
Normal file
40
src/flow/components/form-create/form-name.vue
Normal file
@@ -0,0 +1,40 @@
|
||||
<template>
|
||||
<div style="width: 100%">
|
||||
<el-input v-model="data.modelValue" @blur="changeModelValue" :disabled="props.disabled" clearable>
|
||||
</el-input>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="UserRolePickerIndex">
|
||||
import {useMessage} from "/@/hooks/message";
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
});
|
||||
|
||||
const emits = defineEmits(['update:modelValue']);
|
||||
|
||||
const $message = useMessage();
|
||||
const data = reactive({
|
||||
modelValue: null
|
||||
})
|
||||
|
||||
const changeModelValue = () => {
|
||||
if (data.modelValue) {
|
||||
emits('update:modelValue', data.modelValue);
|
||||
} else {
|
||||
emits('update:modelValue', null);
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
data.modelValue = props.modelValue
|
||||
})
|
||||
</script>
|
||||
46
src/flow/components/form-create/id-card.vue
Normal file
46
src/flow/components/form-create/id-card.vue
Normal file
@@ -0,0 +1,46 @@
|
||||
<template>
|
||||
<div style="width: 100%">
|
||||
<el-input v-model="data.modelValue" @blur="changeModelValue" :disabled="props.disabled" clearable>
|
||||
</el-input>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="UserRolePickerIndex">
|
||||
import {useMessage} from "/@/hooks/message";
|
||||
import {verifyIdCard} from "/@/utils/toolsValidate";
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
});
|
||||
|
||||
const emits = defineEmits(['update:modelValue']);
|
||||
|
||||
const $message = useMessage();
|
||||
const data = reactive({
|
||||
modelValue: null
|
||||
})
|
||||
|
||||
const changeModelValue = () => {
|
||||
if (data.modelValue) {
|
||||
let boolean = verifyIdCard(data.modelValue);
|
||||
if (!boolean) {
|
||||
data.modelValue = null
|
||||
$message.success("请输入正确的身份证号")
|
||||
}
|
||||
emits('update:modelValue', data.modelValue);
|
||||
} else {
|
||||
emits('update:modelValue', null);
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
data.modelValue = props.modelValue
|
||||
})
|
||||
</script>
|
||||
51
src/flow/components/form-create/index.ts
Normal file
51
src/flow/components/form-create/index.ts
Normal file
@@ -0,0 +1,51 @@
|
||||
import {validateNull} from "/@/utils/validate";
|
||||
|
||||
const PRINT_REPLACE = {
|
||||
value: "value",
|
||||
date: ["yyyy", "MM", "dd", "HH", "mm", "ss"]
|
||||
}
|
||||
|
||||
// 自定义组件打印格式
|
||||
const PRINT_FORMAT = [{props: [], propTypes: ["elImage", "SignInput"], tag: "<img src=\"value\" alt=\"\" />"}
|
||||
,{props: [], propTypes: ["checkbox"], typeVals: [{value: "0", label: "□"},{value: "1", label: "☑"}]}
|
||||
,{props: [], propTypes: ["radio"], typeVals: [{value: "0", label: "☑否 □是"},{value: "1", label: "□否 ☑是"}]}
|
||||
,{props: [], propTypes: ["datePicker"], format: "yyyy_年_MM_月_dd_日_HH_时_mm_分_ss_秒"}]
|
||||
|
||||
export function handlePrintValue(optionItems, formDatum, value, label, prop?, propType?) {
|
||||
if (!validateNull(optionItems)) {
|
||||
if (Array.isArray(formDatum)) {
|
||||
let showKeys = ''
|
||||
for (let i = 0; i < formDatum.length; i++) {
|
||||
let item = optionItems.find(f => f[value] === formDatum[i]);
|
||||
if (item) {
|
||||
if (i === formDatum.length -1) showKeys += handleFormatValue(item[value], item[label], prop, propType)
|
||||
else showKeys += handleFormatValue(item[value], item[label], prop, propType) + ","
|
||||
}
|
||||
}
|
||||
return showKeys
|
||||
} else {
|
||||
let item = optionItems.find(f => f[value] === formDatum);
|
||||
if (item) return handleFormatValue(item[value], item[label], prop, propType)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function handleFormatValue(value, label, prop?, propType?) {
|
||||
if (validateNull(PRINT_FORMAT) || (!prop && !propType)) return label
|
||||
let find = PRINT_FORMAT.find(f => f.props.includes(prop) || f.propTypes.includes(propType));
|
||||
if (validateNull(find) || !value) return label
|
||||
if (find.format) {
|
||||
if (value instanceof Array) return label
|
||||
let format = find.format
|
||||
for (let i = 0; i < PRINT_REPLACE.date.length; i++) {
|
||||
const eachFormat = PRINT_REPLACE.date[i];
|
||||
let eachValue = ''
|
||||
if (i <= 2) eachValue = value.split(" ")[0].split("-")[i]
|
||||
else eachValue = value.split(" ")[1].split(":")[i - 3]
|
||||
format = format.replace(eachFormat, eachValue)
|
||||
}
|
||||
return format;
|
||||
}
|
||||
if (find.typeVals) return find.typeVals.find(f => f.value === value).label
|
||||
if (find.tag) return find.tag.replace(PRINT_REPLACE.value, value)
|
||||
}
|
||||
47
src/flow/components/form-create/phone.vue
Normal file
47
src/flow/components/form-create/phone.vue
Normal file
@@ -0,0 +1,47 @@
|
||||
<template>
|
||||
<div style="width: 100%">
|
||||
<el-input v-model="data.modelValue" @blur="changeModelValue" :disabled="props.disabled" clearable>
|
||||
</el-input>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="UserRolePickerIndex">
|
||||
import {rule} from "/@/utils/validate";
|
||||
import {useMessage} from "/@/hooks/message";
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
});
|
||||
|
||||
const emits = defineEmits(['update:modelValue']);
|
||||
|
||||
const $message = useMessage();
|
||||
const data = reactive({
|
||||
modelValue: null
|
||||
})
|
||||
|
||||
const changeModelValue = () => {
|
||||
if (data.modelValue) {
|
||||
rule.validatePhone(null, data.modelValue, (err) => {
|
||||
if (err) {
|
||||
data.modelValue = null
|
||||
$message.success(err.message)
|
||||
}
|
||||
emits('update:modelValue', data.modelValue);
|
||||
})
|
||||
} else {
|
||||
emits('update:modelValue', null);
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
data.modelValue = props.modelValue
|
||||
})
|
||||
</script>
|
||||
93
src/flow/components/form-create/render.vue
Normal file
93
src/flow/components/form-create/render.vue
Normal file
@@ -0,0 +1,93 @@
|
||||
<template>
|
||||
<div>
|
||||
<form-create ref="formCreateRef" v-model="design.formData" v-model:api="design.fApi" :rule="design.rule"
|
||||
:option="design.option">
|
||||
</form-create>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="FormRender">
|
||||
import {useUserInfo} from "/@/stores/userInfo";
|
||||
import {initFcDesignerFetch} from "./api";
|
||||
import {validateNull} from "/@/utils/validate";
|
||||
import {parseWithFunctions} from "../../index";
|
||||
import {compatibleAppHeight} from "/@/api/order/order-key-vue";
|
||||
import {validateApp} from "/@/views/order";
|
||||
|
||||
const formCreateRef = ref(null)
|
||||
|
||||
const props = defineProps({
|
||||
currFlowForm: {
|
||||
type: Object,
|
||||
default: {},
|
||||
},
|
||||
initFormPermPrint: null,
|
||||
renderType: null,
|
||||
});
|
||||
|
||||
const design = reactive({
|
||||
rule: [],
|
||||
option: {},
|
||||
fApi: null,
|
||||
formData: null
|
||||
});
|
||||
|
||||
const data = reactive({
|
||||
globalDsv: {
|
||||
type: Object,
|
||||
default: {},
|
||||
}
|
||||
});
|
||||
|
||||
function doInitFcDesigner(formInfo) {
|
||||
design.rule = formInfo.widgetList
|
||||
delete formInfo.widgetList
|
||||
design.option = formInfo
|
||||
design.formData = parseWithFunctions(props.currFlowForm.formData)
|
||||
// 隐藏提交按钮
|
||||
design.option.submitBtn = false
|
||||
}
|
||||
|
||||
const $route = useRoute();
|
||||
async function initFcDesigner() {
|
||||
if (validateNull(props.currFlowForm)) return
|
||||
data.globalDsv.userInfos = useUserInfo().userInfos
|
||||
let formInfoStr = props.currFlowForm.formInfo;
|
||||
if (validateNull(formInfoStr)) return
|
||||
initFcDesignerFetch(formCreateRef.value, design.formData, data.globalDsv)
|
||||
let formInfo = parseWithFunctions(formInfoStr, true)
|
||||
let elTab = null
|
||||
// 保证组件事件后执行
|
||||
if (props.initFormPermPrint) elTab = await props.initFormPermPrint(formInfo);
|
||||
// 保证子表单新增行的字段权限生效
|
||||
doInitFcDesigner(formInfo)
|
||||
nextTick(async () => {
|
||||
// 在nextTick中保证生效
|
||||
if (elTab && elTab.isFormEdit === '0') design.fApi.disabled(true)
|
||||
// 查看时在表单权限后disabled
|
||||
if (props.renderType === '-1') design.fApi.disabled(true)
|
||||
compatibleAppHeight(validateApp($route))
|
||||
})
|
||||
}
|
||||
|
||||
// 监听双向绑定
|
||||
watch(
|
||||
() => props.currFlowForm.id,
|
||||
() => {
|
||||
initFcDesigner();
|
||||
}
|
||||
);
|
||||
|
||||
onMounted(() => {
|
||||
initFcDesigner()
|
||||
})
|
||||
|
||||
// 暴露变量
|
||||
defineExpose({
|
||||
design
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
</style>
|
||||
397
src/flow/components/form-create/rules.ts
Normal file
397
src/flow/components/form-create/rules.ts
Normal file
@@ -0,0 +1,397 @@
|
||||
import type { DragRule } from 'form-create-designer';
|
||||
import other from "/@/utils/other";
|
||||
|
||||
// Create a map of rule creators
|
||||
const ruleCreators: Record<string, () => DragRule> = {
|
||||
UserPicker: () => ({
|
||||
menu: 'biz',
|
||||
icon: 'iconfont icon-icon-',
|
||||
label: '人员',
|
||||
name: 'UserPicker',
|
||||
mask: true,
|
||||
rule() {
|
||||
return {
|
||||
type: 'UserPicker',
|
||||
field: 'UserPicker' + other.getNonDuplicateID(),
|
||||
title: '人员',
|
||||
$required: true,
|
||||
props: {
|
||||
multiple: false,
|
||||
},
|
||||
};
|
||||
},
|
||||
props() {
|
||||
return [
|
||||
{
|
||||
type: 'switch',
|
||||
title: '多选',
|
||||
field: 'multiple',
|
||||
value: false,
|
||||
},
|
||||
{
|
||||
type: 'switch',
|
||||
title: '禁用',
|
||||
field: 'disabled',
|
||||
value: false,
|
||||
},
|
||||
];
|
||||
},
|
||||
}),
|
||||
RolePicker: () => ({
|
||||
menu: 'biz',
|
||||
icon: 'iconfont icon-gerenzhongxin',
|
||||
label: '角色',
|
||||
name: 'RolePicker',
|
||||
mask: true,
|
||||
rule() {
|
||||
return {
|
||||
type: 'RolePicker',
|
||||
field: 'RolePicker' + other.getNonDuplicateID(),
|
||||
title: '角色',
|
||||
$required: true,
|
||||
props: {
|
||||
multiple: false,
|
||||
},
|
||||
};
|
||||
},
|
||||
props() {
|
||||
return [
|
||||
{
|
||||
type: 'switch',
|
||||
title: '多选',
|
||||
field: 'multiple',
|
||||
value: false,
|
||||
},
|
||||
{
|
||||
type: 'switch',
|
||||
title: '禁用',
|
||||
field: 'disabled',
|
||||
value: false,
|
||||
}
|
||||
];
|
||||
},
|
||||
}),
|
||||
PostPicker: () => ({
|
||||
menu: 'biz',
|
||||
icon: 'iconfont icon-siweidaotu font14',
|
||||
label: '岗位',
|
||||
name: 'PostPicker',
|
||||
mask: true,
|
||||
rule() {
|
||||
return {
|
||||
type: 'PostPicker',
|
||||
field: 'PostPicker' + other.getNonDuplicateID(),
|
||||
title: '岗位',
|
||||
$required: true,
|
||||
props: {
|
||||
multiple: false,
|
||||
},
|
||||
};
|
||||
},
|
||||
props() {
|
||||
return [
|
||||
{
|
||||
type: 'switch',
|
||||
title: '多选',
|
||||
field: 'multiple',
|
||||
value: false,
|
||||
},
|
||||
{
|
||||
type: 'switch',
|
||||
title: '禁用',
|
||||
field: 'disabled',
|
||||
value: false,
|
||||
}
|
||||
];
|
||||
},
|
||||
}),
|
||||
DeptPicker: () => ({
|
||||
menu: 'biz',
|
||||
icon: 'iconfont icon-shouye',
|
||||
label: '部门',
|
||||
name: 'DeptPicker',
|
||||
mask: true,
|
||||
rule() {
|
||||
return {
|
||||
type: 'DeptPicker',
|
||||
field: 'DeptPicker' + other.getNonDuplicateID(),
|
||||
title: '部门',
|
||||
$required: true,
|
||||
props: {
|
||||
multiple: false,
|
||||
},
|
||||
};
|
||||
},
|
||||
props() {
|
||||
return [
|
||||
{
|
||||
type: 'switch',
|
||||
title: '多选',
|
||||
field: 'multiple',
|
||||
value: false,
|
||||
},
|
||||
{
|
||||
type: 'switch',
|
||||
title: '禁用',
|
||||
field: 'disabled',
|
||||
value: false,
|
||||
}
|
||||
];
|
||||
},
|
||||
}),
|
||||
PhoneInput: () => ({
|
||||
menu: 'biz',
|
||||
icon: 'iconfont icon-dianhua',
|
||||
label: '手机号',
|
||||
name: 'PhoneInput',
|
||||
mask: true,
|
||||
rule() {
|
||||
return {
|
||||
type: 'PhoneInput',
|
||||
field: 'PhoneInput' + other.getNonDuplicateID(),
|
||||
title: '手机号',
|
||||
$required: true,
|
||||
props: {
|
||||
disabled: false,
|
||||
},
|
||||
};
|
||||
},
|
||||
props() {
|
||||
return [
|
||||
{
|
||||
type: 'switch',
|
||||
title: '禁用',
|
||||
field: 'disabled',
|
||||
value: false,
|
||||
}
|
||||
];
|
||||
},
|
||||
}),
|
||||
IdCartInput: () => ({
|
||||
menu: 'biz',
|
||||
icon: 'iconfont icon-tupianyulan',
|
||||
label: '身份证号',
|
||||
name: 'IdCartInput',
|
||||
mask: true,
|
||||
rule() {
|
||||
return {
|
||||
type: 'IdCartInput',
|
||||
field: 'IdCartInput' + other.getNonDuplicateID(),
|
||||
title: '身份证号',
|
||||
$required: true,
|
||||
props: {
|
||||
disabled: false,
|
||||
},
|
||||
};
|
||||
},
|
||||
props() {
|
||||
return [
|
||||
{
|
||||
type: 'switch',
|
||||
title: '禁用',
|
||||
field: 'disabled',
|
||||
value: false,
|
||||
}
|
||||
];
|
||||
},
|
||||
}),
|
||||
FormNameInput: () => ({
|
||||
menu: 'biz',
|
||||
icon: 'iconfont icon-putong',
|
||||
label: '表单名称',
|
||||
name: 'FormNameInput',
|
||||
mask: true,
|
||||
rule() {
|
||||
return {
|
||||
type: 'FormNameInput',
|
||||
field: 'formName',
|
||||
title: '表单名称',
|
||||
$required: true,
|
||||
props: {
|
||||
disabled: false,
|
||||
},
|
||||
};
|
||||
},
|
||||
props() {
|
||||
return [
|
||||
{
|
||||
type: 'switch',
|
||||
title: '禁用',
|
||||
field: 'disabled',
|
||||
value: false,
|
||||
}
|
||||
];
|
||||
},
|
||||
}),
|
||||
FlowNameInput: () => ({
|
||||
menu: 'biz',
|
||||
icon: 'iconfont icon-shuxingtu',
|
||||
label: '流程名称',
|
||||
name: 'FlowNameInput',
|
||||
mask: true,
|
||||
rule() {
|
||||
return {
|
||||
type: 'FlowNameInput',
|
||||
field: 'flowName',
|
||||
title: '流程名称',
|
||||
$required: true,
|
||||
props: {
|
||||
disabled: false,
|
||||
},
|
||||
};
|
||||
},
|
||||
props() {
|
||||
return [
|
||||
{
|
||||
type: 'switch',
|
||||
title: '禁用',
|
||||
field: 'disabled',
|
||||
value: false,
|
||||
}
|
||||
];
|
||||
},
|
||||
}),
|
||||
FormCodeInput: () => ({
|
||||
menu: 'biz',
|
||||
icon: 'iconfont icon-quanjushezhi_o',
|
||||
label: '流程CODE',
|
||||
name: 'FormCodeInput',
|
||||
mask: true,
|
||||
rule() {
|
||||
return {
|
||||
type: 'FormCodeInput',
|
||||
field: 'code',
|
||||
title: '流程CODE',
|
||||
$required: true,
|
||||
props: {
|
||||
disabled: false,
|
||||
},
|
||||
};
|
||||
},
|
||||
props() {
|
||||
return [
|
||||
{
|
||||
type: 'switch',
|
||||
title: '禁用',
|
||||
field: 'disabled',
|
||||
value: false,
|
||||
}
|
||||
];
|
||||
},
|
||||
}),
|
||||
EmailInput: () => ({
|
||||
menu: 'biz',
|
||||
icon: 'iconfont icon-xingqiu',
|
||||
label: '邮箱',
|
||||
name: 'EmailInput',
|
||||
mask: true,
|
||||
rule() {
|
||||
return {
|
||||
type: 'EmailInput',
|
||||
field: 'EmailInput' + other.getNonDuplicateID(),
|
||||
title: '邮箱',
|
||||
$required: true,
|
||||
props: {
|
||||
disabled: false,
|
||||
},
|
||||
};
|
||||
},
|
||||
props() {
|
||||
return [
|
||||
{
|
||||
type: 'switch',
|
||||
title: '禁用',
|
||||
field: 'disabled',
|
||||
value: false,
|
||||
}
|
||||
];
|
||||
},
|
||||
}),
|
||||
SignInput: () => ({
|
||||
menu: 'biz',
|
||||
icon: 'icon-edit',
|
||||
label: '签名',
|
||||
name: 'SignInput',
|
||||
mask: true,
|
||||
rule() {
|
||||
return {
|
||||
type: 'SignInput',
|
||||
field: 'SignInput'+ other.getNonDuplicateID(),
|
||||
title: '签名',
|
||||
$required: true,
|
||||
props: {
|
||||
bgColor: '#F6F8FA',
|
||||
isClearBgColor: false,
|
||||
disabled: false,
|
||||
},
|
||||
};
|
||||
},
|
||||
props() {
|
||||
return [
|
||||
{
|
||||
type: 'switch',
|
||||
title: '禁用',
|
||||
field: 'disabled',
|
||||
value: false,
|
||||
},
|
||||
{
|
||||
type: 'number',
|
||||
title: '宽度',
|
||||
field: 'width',
|
||||
value: 300,
|
||||
props: {
|
||||
min: 100,
|
||||
max: 1000,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'number',
|
||||
title: '高度',
|
||||
field: 'height',
|
||||
value: 150,
|
||||
props: {
|
||||
min: 50,
|
||||
max: 500,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'number',
|
||||
title: '线宽',
|
||||
field: 'lineWidth',
|
||||
value: 4,
|
||||
props: {
|
||||
min: 1,
|
||||
max: 20,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'colorPicker',
|
||||
title: '线条颜色',
|
||||
field: 'lineColor',
|
||||
value: '#000000',
|
||||
},
|
||||
{
|
||||
type: 'colorPicker',
|
||||
title: '背景颜色',
|
||||
field: 'bgColor',
|
||||
value: '#F6F8FA',
|
||||
},
|
||||
{
|
||||
type: 'switch',
|
||||
title: '裁剪空白',
|
||||
field: 'isCrop',
|
||||
value: false,
|
||||
},
|
||||
{
|
||||
type: 'switch',
|
||||
title: '清除背景',
|
||||
field: 'isClearBgColor',
|
||||
value: false,
|
||||
},
|
||||
];
|
||||
},
|
||||
}),
|
||||
};
|
||||
|
||||
// Export all rules as an array
|
||||
export const rules: any[] = Object.values(ruleCreators).map((creator) => creator());
|
||||
176
src/flow/components/handle-job/copy-pass-job.vue
Normal file
176
src/flow/components/handle-job/copy-pass-job.vue
Normal file
@@ -0,0 +1,176 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-form ref="dataFormRef" :model="form" :rules="dataRules" label-width="150px" v-loading="loading" :disabled="operType === 'view'">
|
||||
<span>什么是抄送任务?被抄送的参与者需要参与审批,而传阅任务不需要参与审批只参与阅览</span>
|
||||
<el-row :gutter="24">
|
||||
<el-divider> 参与者设置 </el-divider>
|
||||
|
||||
<el-col :span="12" class="mb20">
|
||||
<el-form-item label="参与者类型" prop="jobType">
|
||||
<el-radio-group @change="handleRoleType"
|
||||
v-model="form.jobType">
|
||||
<el-radio v-for="(item, index) in DIC_PROP.JOB_USER_TYPE" :key="index" :label="item.value">
|
||||
{{ item.label }}
|
||||
</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="12" class="mb20" v-if="form.jobType === DIC_PROP.JOB_USER_TYPE[0].value">
|
||||
<el-form-item label="指定参与人员" prop="roleId">
|
||||
<el-select v-model="form.roleId" placeholder="请输入用户名称进行模糊搜索" clearable filterable
|
||||
remote :remote-method="methodsRemote.remoteMethodUser" :reserve-keyword="false">
|
||||
<el-option v-for="(item, index) in dicData.users" :key="index" :label="item.name" :value="item.userId"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="12" class="mb20" v-if="form.jobType === DIC_PROP.JOB_USER_TYPE[1].value">
|
||||
<el-form-item label="指定参与角色" prop="roleId">
|
||||
<el-select v-model="form.roleId" placeholder="请输入角色名称进行模糊搜索" clearable filterable
|
||||
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-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="12" class="mb20" v-if="form.jobType === DIC_PROP.JOB_USER_TYPE[2].value">
|
||||
<el-form-item label="指定参与岗位" prop="roleId">
|
||||
<el-select v-model="form.roleId" placeholder="请输入岗位名称进行模糊搜索" clearable filterable
|
||||
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-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="12" class="mb20" v-if="form.jobType === DIC_PROP.JOB_USER_TYPE[3].value">
|
||||
<el-form-item label="指定参与部门" prop="roleId">
|
||||
<el-select v-model="form.roleId" placeholder="请输入部门名称进行模糊搜索" clearable filterable
|
||||
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-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="12" class="mb20">
|
||||
<el-form-item :label="t('runJob.jobName')" prop="jobName">
|
||||
<el-input v-model="form.jobName" :placeholder="t('runJob.inputJobNameTip')"/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
</el-row>
|
||||
</el-form>
|
||||
<footer class="el-dialog__footer">
|
||||
<span class="dialog-footer">
|
||||
<el-button type="primary" @click="handleUpdate" :loading="loading">{{ $t('common.confirmButtonText') }}</el-button>
|
||||
</span>
|
||||
</footer>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="CopyPassJob">
|
||||
import { useI18n } from "vue-i18n"
|
||||
import {onLoadDicUrl, remoteMethodByKey} from "../convert-name/convert";
|
||||
import {useMessage} from "/@/hooks/message";
|
||||
import {handleChangeJobType} from "../../index";
|
||||
|
||||
const { t } = useI18n();
|
||||
const $message = useMessage();
|
||||
const $emit = defineEmits(['onJobSignature']);
|
||||
// 定义变量内容
|
||||
const dataFormRef = ref();
|
||||
const loading = ref(false);
|
||||
const operType = ref(false);
|
||||
|
||||
// 定义字典
|
||||
const dicData = reactive({});
|
||||
const cascadeDic = reactive({});
|
||||
const onLoad = onLoadDicUrl();
|
||||
onMounted(() => {
|
||||
// onLoad(dicData);
|
||||
});
|
||||
|
||||
// 提交表单数据
|
||||
const form = reactive({
|
||||
jobType: '',
|
||||
roleId: null,
|
||||
});
|
||||
|
||||
// 定义校验规则
|
||||
const dataRules = ref({
|
||||
jobType: [{required: true, message: '参与者类型不能为空', trigger: 'blur'}],
|
||||
roleId: [{required: true, message: '任务参与者不能为空', trigger: 'blur'}],
|
||||
})
|
||||
|
||||
const props = defineProps({
|
||||
currJob: {
|
||||
default: null,
|
||||
}
|
||||
});
|
||||
|
||||
function initJobData() {
|
||||
Object.assign(form, props.currJob)
|
||||
form.roleId = null
|
||||
}
|
||||
|
||||
function handleRoleType() {
|
||||
handleChangeJobType(dicData, form)
|
||||
}
|
||||
|
||||
const methodsRemote = {
|
||||
remoteMethodUser(query: string) {
|
||||
remoteMethodByKey(query, onLoad, dicData, 'userName', "users")
|
||||
},
|
||||
remoteMethodRole(query: string) {
|
||||
remoteMethodByKey(query, onLoad, dicData, 'roleName', "roles")
|
||||
},
|
||||
remoteMethodPost(query: string) {
|
||||
remoteMethodByKey(query, onLoad, dicData, 'postName', "posts")
|
||||
},
|
||||
remoteMethodDept(query: string) {
|
||||
remoteMethodByKey(query, onLoad, dicData, 'deptName', "depts")
|
||||
},
|
||||
}
|
||||
|
||||
async function handleUpdate() {
|
||||
const valid = await dataFormRef.value.validate().catch(() => {});
|
||||
if (!valid) return false;
|
||||
|
||||
form.nextUserRole = {jobType: form.jobType, roleId: form.roleId, jobName: form.jobName}
|
||||
|
||||
props.currJob.nextUserRole = form.nextUserRole
|
||||
|
||||
loading.value = true
|
||||
try {
|
||||
$emit('onJobSignature', props.currJob)
|
||||
setTimeout(() => { // 异步异常
|
||||
loading.value = false
|
||||
}, 3000)
|
||||
} catch (e) {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 监听双向绑定
|
||||
watch(
|
||||
() => props.currJob.id,
|
||||
(val) => {
|
||||
initJobData();
|
||||
}
|
||||
);
|
||||
|
||||
onMounted(() => {
|
||||
initJobData();
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.el-dialog__footer {
|
||||
text-align: center;
|
||||
|
||||
.dialog-footer {
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
58
src/flow/components/handle-job/dynamic-iframe.vue
Normal file
58
src/flow/components/handle-job/dynamic-iframe.vue
Normal file
@@ -0,0 +1,58 @@
|
||||
<template>
|
||||
<div class="layout-padding">
|
||||
<el-scrollbar class="main">
|
||||
<iframe :src="src" class="iframe" />
|
||||
</el-scrollbar>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" name="DynamicIframe" setup>
|
||||
import { Session } from '/@/utils/storage';
|
||||
const { proxy } = getCurrentInstance();
|
||||
const route = useRoute();
|
||||
const src = ref('');
|
||||
|
||||
const emits = defineEmits(["handleJob"]);
|
||||
|
||||
const props = defineProps({
|
||||
currJob: {
|
||||
type: Object,
|
||||
default: null,
|
||||
},
|
||||
currElTab: {
|
||||
type: Object,
|
||||
default: null,
|
||||
}
|
||||
});
|
||||
|
||||
const init = () => {
|
||||
const token = Session.getToken();
|
||||
const tenantId = Session.getTenant();
|
||||
let flowInstId = props.currJob.flowInstId;
|
||||
let runJobId = props.currJob.id;
|
||||
src.value = props.currElTab.path + `?token=${token}&tenantId=${tenantId}&flowInstId=${flowInstId}&runJobId=${runJobId}`;
|
||||
};
|
||||
|
||||
function handleJob(jobBtn) {
|
||||
emits("handleJob", jobBtn);
|
||||
}
|
||||
|
||||
watch([route], () => {
|
||||
init();
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
init();
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.iframe {
|
||||
width: 100%;
|
||||
height: 80vh;
|
||||
border: 0;
|
||||
overflow: hidden;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
</style>
|
||||
62
src/flow/components/handle-job/dynamic-link.vue
Normal file
62
src/flow/components/handle-job/dynamic-link.vue
Normal file
@@ -0,0 +1,62 @@
|
||||
<template>
|
||||
<keep-alive v-if="data.currTabComp">
|
||||
<component v-if="data.currTabComp" :key="props.currElTab.path" :is="data.currTabComp" :currJob="props.currJob" :currElTab="props.currElTab"
|
||||
@handleJob="handleJob"></component>
|
||||
</keep-alive>
|
||||
</template>
|
||||
<script setup lang="ts" name="DynamicLink">
|
||||
import {deepClone} from "/@/utils/other";
|
||||
import {useMessage} from "/@/hooks/message";
|
||||
|
||||
const emits = defineEmits(["handleJob"]);
|
||||
|
||||
const props = defineProps({
|
||||
currJob: {
|
||||
type: Object,
|
||||
default: null,
|
||||
},
|
||||
currElTab: {
|
||||
type: Object,
|
||||
default: null,
|
||||
}
|
||||
});
|
||||
const data = reactive({
|
||||
currTabComp: null,
|
||||
preElTab: null
|
||||
});
|
||||
|
||||
function handLoader() {
|
||||
let path = props.currElTab.path;
|
||||
if (!path) {
|
||||
data.currTabComp = null
|
||||
useMessage().error("不存在的表单,请检查")
|
||||
return
|
||||
}
|
||||
data.currTabComp = `../../views${path}.vue`
|
||||
}
|
||||
|
||||
function handleJob(jobBtn) {
|
||||
emits("handleJob", jobBtn);
|
||||
}
|
||||
|
||||
// 监听双向绑定
|
||||
watch(
|
||||
() => props.currElTab.active,
|
||||
(val) => {
|
||||
let b = props.currElTab.active !== data.preElTab.active && props.currElTab.path === data.preElTab.path;
|
||||
if (b) {
|
||||
data.preElTab = deepClone(props.currElTab)
|
||||
data.currTabComp = null
|
||||
}
|
||||
handLoader()// 重载
|
||||
}
|
||||
);
|
||||
|
||||
onMounted(() => {
|
||||
data.preElTab = deepClone(props.currElTab)
|
||||
// 初始化
|
||||
if (props.currElTab.path) handLoader()
|
||||
});
|
||||
|
||||
|
||||
</script>
|
||||
572
src/flow/components/handle-job/handle.vue
Normal file
572
src/flow/components/handle-job/handle.vue
Normal file
@@ -0,0 +1,572 @@
|
||||
<template>
|
||||
<div class="layout-container">
|
||||
<div class="layout-padding w100">
|
||||
<div class="layout-padding-auto layout-padding-view">
|
||||
<!-- <div style="margin-bottom: 10px;">
|
||||
<div style="font-size: 20px;">{{ data.currJob.flowName }}</div>
|
||||
<div style="font-weight: 300;">
|
||||
<span >发起人:{{ data.currJob.initiatorName }}</span>
|
||||
<span style="margin-left: 10px">发起时间:{{ data.currJob.initiatorTime }}</span>
|
||||
</div>
|
||||
</div>-->
|
||||
<!-- 选项卡tabs -->
|
||||
<el-tabs ref="tabs" v-model="data.currElTab.active" @tab-click="methods.handleClick">
|
||||
<el-tab-pane v-for="(item, index) in data.elTabs"
|
||||
:key="index"
|
||||
:label="item.formName"
|
||||
:name="item.id">
|
||||
<template #label>
|
||||
<div>
|
||||
<i :class="item.icon" style="vertical-align: middle;margin-right: 3px"></i>
|
||||
<span style="vertical-align: middle;">{{ item.formName }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
|
||||
<div id="handle_job">
|
||||
|
||||
<!-- 动态组件 -->
|
||||
<template v-if="data.currElTab.path && data.currElTab.path.startsWith('http')">
|
||||
<dynamic-iframe v-if="data.currElTab.active" :currJob="data.currJob" :currElTab="data.currElTab" @handleJob="methods.handleJob"></dynamic-iframe>
|
||||
</template>
|
||||
<template v-else>
|
||||
<dynamic-link v-if="data.currElTab.active" :currJob="data.currJob" :currElTab="data.currElTab" @handleJob="methods.handleJob"></dynamic-link>
|
||||
</template>
|
||||
|
||||
<!-- 审批项 -->
|
||||
<el-form v-if="!data.currJob.hiJob && !orderVue.vueKeySys.sysPaths.includes(data.currElTab.path)" label-width="72px" style="margin-top: 22px;">
|
||||
<template v-if="data.currElTab.isAutoAudit !== '1'">
|
||||
<el-form-item :label="t('jfcomment.remark')">
|
||||
<el-input v-model="data.currJob.comment" :autosize="{ minRows: 3, maxRows: 6}" maxlength="50" show-word-limit
|
||||
type="textarea" :placeholder="t('jfcomment.inputRemarkTip')">
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('jfcomment.signName')">
|
||||
<sign-name ref="signNameRef" :currJob="data.currJob"></sign-name>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<template v-for="(jobBtn, index) in data.jobBtns.length > 6 ? data.jobBtns.slice(0, 6) : data.jobBtns" :key="index">
|
||||
<el-button :loading="data.loading"
|
||||
v-if="/*methods.handleJobBtns(jobBtn) */ true"
|
||||
icon="Check"
|
||||
plain
|
||||
@click="methods.handleJob(jobBtn, true)">{{ methods.getJobBtnName(jobBtn) }}
|
||||
</el-button>
|
||||
</template>
|
||||
<el-dropdown max-height="500px" size="small" style="vertical-align: middle; margin-left: 10px" placement="top" v-if="data.jobBtns.length > 6">
|
||||
<el-button icon="More">{{ t('jfI18n.more') }}</el-button>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu v-if="/*methods.handleJobBtns(jobBtn) */ true">
|
||||
<template v-for="(jobBtn, index) in data.jobBtns.slice(6)" :key="index">
|
||||
<el-dropdown-item @click.native="methods.handleJob(jobBtn, true)" divided>
|
||||
{{ methods.getJobBtnName(jobBtn) }}
|
||||
</el-dropdown-item>
|
||||
</template>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</el-form-item>
|
||||
</template>
|
||||
</el-form>
|
||||
|
||||
</div>
|
||||
|
||||
<el-dialog
|
||||
v-if="data.showToRunNode"
|
||||
v-model="data.showToRunNode"
|
||||
top="20px"
|
||||
width="70%"
|
||||
title="选择任意驳回节点"
|
||||
append-to-body>
|
||||
<run-reject :curr-job="data.currJob" @onRejectJob="btnMethods.onRejectJob"></run-reject>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog
|
||||
v-if="data.showJumpToRunNode"
|
||||
v-model="data.showJumpToRunNode"
|
||||
top="20px"
|
||||
width="70%"
|
||||
title="选择任意跳转节点"
|
||||
append-to-body>
|
||||
<run-any-jump :curr-job="data.currJob" @onAnyJumpJob="btnMethods.onAnyJumpJob"></run-any-jump>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog
|
||||
v-if="data.showJobSignature"
|
||||
v-model="data.showJobSignature"
|
||||
top="20px"
|
||||
width="70%"
|
||||
:title="data.jobSignatureTitle"
|
||||
append-to-body>
|
||||
<job-signature :curr-job="data.currJob" @onJobSignature="btnMethods.onJobSignature"></job-signature>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog
|
||||
v-if="data.showJobCopyPass"
|
||||
v-model="data.showJobCopyPass"
|
||||
top="20px"
|
||||
width="70%"
|
||||
:title="data.jobSignatureTitle"
|
||||
append-to-body>
|
||||
<copy-pass-job :curr-job="data.currJob" @onJobSignature="btnMethods.onJobSignature"></copy-pass-job>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog
|
||||
v-if="data.showNodeSignature"
|
||||
v-model="data.showNodeSignature"
|
||||
top="20px"
|
||||
width="70%"
|
||||
:title="data.nodeSignatureTitle"
|
||||
append-to-body>
|
||||
<node-signature :curr-job="data.currJob" @onNodeSignature="btnMethods.onNodeSignature"></node-signature>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog
|
||||
v-if="data.showDistPerson"
|
||||
v-model="data.showDistPerson"
|
||||
top="20px"
|
||||
width="70%"
|
||||
title="分配参与者"
|
||||
append-to-body>
|
||||
<dist-person :curr-job="data.currJob"></dist-person>
|
||||
</el-dialog>
|
||||
|
||||
<user-role-picker ref="userRolePicker" :isOnlyOne="true" @onSelectItems="btnMethods.onSelectItems">
|
||||
</user-role-picker>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="HandleJob">
|
||||
import {useI18n} from 'vue-i18n';
|
||||
import {deepClone} from "/@/utils/other";
|
||||
import {useMessage, useMessageBox} from "/@/hooks/message";
|
||||
import {useUserInfo} from "/@/stores/userInfo";
|
||||
import {TabsPaneContext} from "element-plus";
|
||||
import * as orderVue from "/@/api/order/order-key-vue";
|
||||
import {validateNull} from "/@/utils/validate";
|
||||
import {DIC_PROP} from "../../support/dict-prop";
|
||||
import {getJobBtnName} from "../../index";
|
||||
import * as doJob from "/@/api/jsonflow/do-job";
|
||||
import * as runNode from "/@/api/jsonflow/run-node";
|
||||
import * as runFlow from "/@/api/jsonflow/run-flow";
|
||||
import mittBus from '/@/utils/mitt';
|
||||
import {useFlowJob} from "../../stores/flowJob";
|
||||
import {setPropsDataValue} from "../../support/common";
|
||||
import {initHandleJobHeight} from "../../utils";
|
||||
import {handleTodoDetail, handleToDoneDetail} from "../../support/extend";
|
||||
|
||||
const {t} = useI18n();
|
||||
const userInfo = useUserInfo();
|
||||
const $message = useMessage();
|
||||
const flowJob = useFlowJob();
|
||||
const route = useRoute();
|
||||
const {proxy} = getCurrentInstance();
|
||||
|
||||
// 引入组件
|
||||
const DynamicLink = defineAsyncComponent(() => import('./dynamic-link.vue'));
|
||||
const DynamicIframe = defineAsyncComponent(() => import('./dynamic-iframe.vue'));
|
||||
const SignName = defineAsyncComponent(() => import('../sign-name/index.vue'));
|
||||
const UserRolePicker = defineAsyncComponent(() => import('../user-role/picker.vue'));
|
||||
const JobSignature = defineAsyncComponent(() => import('./job-signature.vue'));
|
||||
const CopyPassJob = defineAsyncComponent(() => import('./copy-pass-job.vue'));
|
||||
const NodeSignature = defineAsyncComponent(() => import('./node-signature.vue'));
|
||||
const RunReject = defineAsyncComponent(() => import('/@/views/jsonflow/run-reject/flow.vue'));
|
||||
const RunAnyJump = defineAsyncComponent(() => import('/@/views/jsonflow/run-job/flow.vue'));
|
||||
const DistPerson = defineAsyncComponent(() => import('/@/views/jsonflow/dist-person/flow.vue'));
|
||||
|
||||
const props = defineProps({
|
||||
currJob: {
|
||||
type: Object,
|
||||
default: {},
|
||||
}
|
||||
});
|
||||
const data = reactive({
|
||||
currJob: {},
|
||||
// 0、转办 1、下一办理人
|
||||
nodeUserType: undefined,
|
||||
jobSignatureTitle: undefined,
|
||||
showJobSignature: false,
|
||||
showJobCopyPass: false,
|
||||
nodeSignatureTitle: undefined,
|
||||
showNodeSignature: false,
|
||||
showJumpToRunNode: false,
|
||||
showToRunNode: false,
|
||||
showDistPerson: false,
|
||||
elTabs: {},
|
||||
jobBtns: [],
|
||||
currElTab: {active: null},
|
||||
loading: false,
|
||||
});
|
||||
|
||||
const channel = new BroadcastChannel('currJob_channel');
|
||||
const btnMethods = {
|
||||
onHandleJob(jobBtn) {
|
||||
data.currJob.jobBtn = jobBtn
|
||||
let nodeSignatures = [DIC_PROP.JOB_BTNS[1].value, DIC_PROP.JOB_BTNS[2].value, DIC_PROP.JOB_BTNS[3].value];
|
||||
if (jobBtn === DIC_PROP.JOB_BTNS[13].value) btnMethods.openJobRoleUserId('1')
|
||||
else if (jobBtn === DIC_PROP.JOB_BTNS[14].value) btnMethods.openJobRoleUserId('0');
|
||||
else if (jobBtn === DIC_PROP.JOB_BTNS[15].value) btnMethods.handleTerminateFlow(data.currJob);
|
||||
else if (jobBtn === DIC_PROP.JOB_BTNS[16].value) btnMethods.handleEarlyComplete(data.currJob);
|
||||
else if (jobBtn === DIC_PROP.JOB_BTNS[17].value) btnMethods.handleInvalidFlow(data.currJob);
|
||||
else if (jobBtn === DIC_PROP.JOB_BTNS[18].value) btnMethods.doDistPerson();
|
||||
else if (jobBtn === DIC_PROP.JOB_BTNS[11].value) btnMethods.doJobSignature(jobBtn)
|
||||
else if (jobBtn === DIC_PROP.JOB_BTNS[12].value) btnMethods.doJobSignature(jobBtn)
|
||||
else if (jobBtn === DIC_PROP.JOB_BTNS[7].value) btnMethods.onBackFirstJob(data.currJob)
|
||||
else if (jobBtn === DIC_PROP.JOB_BTNS[8].value) btnMethods.onBackPreJob(data.currJob)
|
||||
else if (nodeSignatures.includes(jobBtn)) btnMethods.doNodeSignature(jobBtn)
|
||||
else btnMethods.doHandleJob(jobBtn)
|
||||
},
|
||||
doHandleJob(jobBtn) {
|
||||
data.currJob.jobBtn = jobBtn
|
||||
if (jobBtn === DIC_PROP.JOB_BTNS[9].value) {
|
||||
return btnMethods.doAnyJumpJob()
|
||||
}
|
||||
if (jobBtn === DIC_PROP.JOB_BTNS[10].value) {
|
||||
return btnMethods.doRejectJob()
|
||||
}
|
||||
let jobSignatures = [DIC_PROP.JOB_BTNS[4].value, DIC_PROP.JOB_BTNS[5].value, DIC_PROP.JOB_BTNS[6].value];
|
||||
if (jobSignatures.includes(jobBtn)) {
|
||||
return btnMethods.doJobSignature(jobBtn)
|
||||
}
|
||||
doJob.complete(data.currJob).then(() => {
|
||||
$message.success('审批成功')
|
||||
btnMethods.onContextmenuClose()
|
||||
})
|
||||
},
|
||||
onBackFirstJob(currJob) {
|
||||
doJob.backFirstJob(currJob).then(() => {
|
||||
$message.success('退回首节点成功')
|
||||
btnMethods.onContextmenuClose()
|
||||
})
|
||||
},
|
||||
onBackPreJob(currJob) {
|
||||
doJob.backPreJob(currJob).then(() => {
|
||||
$message.success('退回上一步成功')
|
||||
btnMethods.onContextmenuClose()
|
||||
})
|
||||
},
|
||||
doAnyJumpJob() {
|
||||
data.currJob.runRejectVO = {}
|
||||
data.showJumpToRunNode = true
|
||||
},
|
||||
onAnyJumpJob(currJob) {
|
||||
doJob.anyJump(currJob).then(() => {
|
||||
$message.success('跳转成功')
|
||||
data.showJumpToRunNode = false
|
||||
btnMethods.onContextmenuClose()
|
||||
})
|
||||
},
|
||||
doRejectJob() {
|
||||
data.currJob.runRejectVO = {}
|
||||
data.showToRunNode = true
|
||||
},
|
||||
onRejectJob(currJob) {
|
||||
doJob.reject(currJob).then(() => {
|
||||
$message.success('驳回成功')
|
||||
data.showToRunNode = false
|
||||
btnMethods.onContextmenuClose()
|
||||
})
|
||||
},
|
||||
doDistPerson() {
|
||||
data.showDistPerson = true
|
||||
},
|
||||
doJobSignature(jobBtn) {
|
||||
data.currJob.nodeJobSigned = {}
|
||||
data.jobSignatureTitle = getJobBtnName(jobBtn);
|
||||
if (jobBtn === DIC_PROP.JOB_BTNS[11].value || jobBtn === DIC_PROP.JOB_BTNS[12].value) {
|
||||
data.currJob.nodeJobSigned.signedType = DIC_PROP.SIGNATURE_TYPE[2].value
|
||||
if (jobBtn === DIC_PROP.JOB_BTNS[11].value) data.currJob.nodeJobSigned.belongType = '1'
|
||||
else data.currJob.nodeJobSigned.belongType = '2'
|
||||
data.showJobCopyPass = true
|
||||
} else {
|
||||
let type = DIC_PROP.SIGNATURE_TYPE[0].value
|
||||
if (DIC_PROP.JOB_BTNS[5].value === jobBtn) type = DIC_PROP.SIGNATURE_TYPE[1].value
|
||||
else if (DIC_PROP.JOB_BTNS[6].value === jobBtn) type = DIC_PROP.SIGNATURE_TYPE[2].value
|
||||
data.currJob.nodeJobSigned.signedType = type
|
||||
data.currJob.nodeJobSigned.belongType = '0'
|
||||
data.showJobSignature = true
|
||||
}
|
||||
},
|
||||
doNodeSignature(jobBtn) {
|
||||
data.currJob.nodeJobSigned = {}
|
||||
data.nodeSignatureTitle = getJobBtnName(jobBtn);
|
||||
data.currJob.nodeJobSigned.signedType = DIC_PROP.NODE_SEQUENTIAL_TYPE[2].value
|
||||
let nodeType = DIC_PROP.NODE_TYPE[1].value
|
||||
let type = DIC_PROP.NODE_SIGNATURE_TYPE[0].value
|
||||
if (DIC_PROP.JOB_BTNS[2].value === jobBtn) type = DIC_PROP.NODE_SIGNATURE_TYPE[1].value
|
||||
else if (DIC_PROP.JOB_BTNS[3].value === jobBtn) {
|
||||
type = DIC_PROP.NODE_SIGNATURE_TYPE[2].value
|
||||
nodeType = DIC_PROP.NODE_TYPE[2].value
|
||||
if (data.currJob.runNodeVO.nodeType !== nodeType) {
|
||||
$message.info('当前节点不是并行节点,必须在并行节点上才能加并节点')
|
||||
return
|
||||
}
|
||||
}
|
||||
data.currJob.nodeJobSigned.nodeSignedType = type
|
||||
data.currJob.nodeJobSigned.nodeType = nodeType
|
||||
data.showNodeSignature = true
|
||||
},
|
||||
onJobSignature(currJob) {
|
||||
doJob.signature(currJob).then(() => {
|
||||
if (data.currJob.nodeJobSigned.belongType !== '0') {
|
||||
if (data.currJob.nodeJobSigned.belongType === '1') $message.success('抄送成功')
|
||||
else $message.success('传阅成功')
|
||||
data.showJobCopyPass = false
|
||||
}
|
||||
else {
|
||||
$message.success('加签成功')
|
||||
data.showJobSignature = false
|
||||
}
|
||||
if (DIC_PROP.SIGNATURE_TYPE[2].value !== data.currJob.nodeJobSigned.signedType) {
|
||||
btnMethods.onContextmenuClose()
|
||||
}
|
||||
})
|
||||
},
|
||||
onNodeSignature(currJob) {
|
||||
runNode.signature(currJob).then(() => {
|
||||
$message.success('加节点成功')
|
||||
data.showNodeSignature = false
|
||||
if (DIC_PROP.NODE_SIGNATURE_TYPE[2].value !== data.currJob.nodeJobSigned.nodeSignedType) {
|
||||
btnMethods.onContextmenuClose()
|
||||
}
|
||||
})
|
||||
},
|
||||
// 选择参与者
|
||||
openJobRoleUserId(type) {
|
||||
data.nodeUserType = type
|
||||
proxy.$refs.userRolePicker.onOpen();
|
||||
},
|
||||
onSelectItems(items) {
|
||||
if (data.nodeUserType === '1') {
|
||||
btnMethods.onJobNextUserRole(items[0])
|
||||
} else {
|
||||
btnMethods.onTurnRunJob(items[0])
|
||||
}
|
||||
},
|
||||
// 提前结束流程
|
||||
handleEarlyComplete(row) {
|
||||
useMessageBox().prompt('请输入提前结束流程理由')
|
||||
.then(({ value }) => {
|
||||
row.jobBtn = DIC_PROP.JOB_BTNS[16].value
|
||||
row.invalidReason = value
|
||||
row.flowStatus = '0'
|
||||
return runFlow.earlyComplete(row)
|
||||
}).then(() => {
|
||||
$message.success('操作成功')
|
||||
btnMethods.onContextmenuClose()
|
||||
})
|
||||
},
|
||||
// 终止流程
|
||||
handleTerminateFlow(row) {
|
||||
useMessageBox().prompt('请输入终止理由')
|
||||
.then(({ value }) => {
|
||||
row.jobBtn = DIC_PROP.JOB_BTNS[15].value
|
||||
row.invalidReason = value
|
||||
row.flowStatus = '0'
|
||||
return runFlow.terminateFlow(row)
|
||||
}).then(() => {
|
||||
$message.success('操作成功')
|
||||
btnMethods.onContextmenuClose()
|
||||
})
|
||||
},
|
||||
// 作废流程
|
||||
handleInvalidFlow(row) {
|
||||
useMessageBox().prompt('请输入作废理由')
|
||||
.then(({ value }) => {
|
||||
row.jobBtn = DIC_PROP.JOB_BTNS[17].value
|
||||
row.invalidReason = value
|
||||
row.flowStatus = '0'
|
||||
return runFlow.invalidFlow(row)
|
||||
}).then(() => {
|
||||
$message.success('操作成功')
|
||||
btnMethods.onContextmenuClose()
|
||||
})
|
||||
},
|
||||
// 转办任务
|
||||
onTurnRunJob(role) {
|
||||
useMessageBox()
|
||||
.confirm('是否确认转办名称为"' + data.currJob.jobName + '"的任务?')
|
||||
.then(() => {
|
||||
data.currJob.jobType = role.jobType
|
||||
data.currJob.roleId = role.roleId
|
||||
data.currJob.jobBtn = DIC_PROP.JOB_BTNS[14].value
|
||||
return doJob.turnRunJob(data.currJob)
|
||||
}).then(() => {
|
||||
$message.success('操作成功')
|
||||
btnMethods.onContextmenuClose()
|
||||
})
|
||||
},
|
||||
// 指定下一办理人
|
||||
onJobNextUserRole(role) {
|
||||
useMessageBox()
|
||||
.confirm('是否确认指定下一步参与者?')
|
||||
.then(() => {
|
||||
data.currJob.nextUserRole = role
|
||||
return btnMethods.doHandleJob(DIC_PROP.JOB_BTNS[13].value)
|
||||
})
|
||||
},
|
||||
onContextmenuClose() {
|
||||
// flowJob.delJobLen()
|
||||
btnMethods.postMessage()
|
||||
// 防止重复进入
|
||||
if (route.fullPath.indexOf('tokenLoaded') === -1) {
|
||||
let source = { contextMenuClickId: 1, ...route }
|
||||
mittBus.emit('onCurrentContextmenuClick', Object.assign({}, source));
|
||||
} else {
|
||||
let timer = setTimeout(() => {
|
||||
window.close();
|
||||
clearTimeout(timer);
|
||||
}, 2000);
|
||||
}
|
||||
},
|
||||
postMessage() {
|
||||
channel.postMessage({ type: 'currJob', data: '' });
|
||||
}
|
||||
}
|
||||
|
||||
const $route = useRoute();
|
||||
const methods = {
|
||||
// 初始化数据
|
||||
async initJobData() {
|
||||
data.loading = false
|
||||
let row = { }
|
||||
if (!validateNull(props.currJob)) row = props.currJob
|
||||
else {
|
||||
setPropsDataValue(row, $route.query, 'id', 'flowInstId', 'isHiJob', 'isView', 'isRead', 'isApp', 'isForm')
|
||||
}
|
||||
if (row.isHiJob !== '0') {
|
||||
await handleToDoneDetail(row, data, row.isApp, row.isForm);
|
||||
} else {
|
||||
await handleTodoDetail(row, data, row.isView, row.isRead, $message, btnMethods.postMessage);
|
||||
}
|
||||
data.jobBtns = data.currJob.jobBtns
|
||||
methods.handleElTabs()
|
||||
data.currJob.resolveSaves = []
|
||||
data.currJob.resolveMethods = []
|
||||
nextTick(() => {
|
||||
initHandleJobHeight(data)
|
||||
})
|
||||
},
|
||||
// 处理tabs
|
||||
handleElTabs() {
|
||||
data.elTabs = methods.removeCloneElTab()
|
||||
// 排序
|
||||
let elTab = data.elTabs.sort((s1, s2) => {
|
||||
return s1.sort - s2.sort
|
||||
}).find(f => f.isActive === '1')
|
||||
// 默认展示的tab
|
||||
if (elTab) data.currElTab = elTab
|
||||
else data.currElTab = data.elTabs[0]
|
||||
|
||||
data.currElTab.active = data.currElTab.id
|
||||
data.currJob.currElTab = data.currElTab
|
||||
},
|
||||
removeCloneElTab() {
|
||||
// 判断是否显隐
|
||||
let clone = deepClone(data.currJob.elTabs)
|
||||
let conditions = orderVue.handleElTab(data.currJob)
|
||||
conditions.forEach(cd => {
|
||||
if (!cd.isShow || cd.isShow === '0') {
|
||||
data.currJob.elTabs = data.currJob.elTabs.filter(f => f.path !== cd.path);
|
||||
clone = clone.filter(f => f.path !== cd.path)
|
||||
}
|
||||
})
|
||||
return clone
|
||||
},
|
||||
// 页面选择
|
||||
handleClick(tab: TabsPaneContext) {
|
||||
if (data.currElTab.id === tab.paneName) return
|
||||
// 切换页面
|
||||
data.currElTab = data.elTabs.find(f => f.id === tab.paneName)
|
||||
data.currElTab.active = data.currElTab.id
|
||||
data.currJob.currElTab = data.currElTab
|
||||
// 影藏弹出框滚动条
|
||||
initHandleJobHeight(data)
|
||||
},
|
||||
// 提交任务
|
||||
async handleJob(jobBtn, isAudit) {
|
||||
// 提交时自动审批
|
||||
if (proxy.$refs.signNameRef) {
|
||||
await proxy.$refs.signNameRef.handleGenerate()
|
||||
}
|
||||
let b = jobBtn !== DIC_PROP.JOB_BTNS[0].value
|
||||
if (b) {
|
||||
methods.timeoutLoading()
|
||||
btnMethods.onHandleJob(jobBtn)
|
||||
return
|
||||
}
|
||||
// 审批前回调函数
|
||||
let resolveMethods = data.currJob.resolveMethods
|
||||
if (!validateNull(resolveMethods)) {
|
||||
for (const resolveMethod of resolveMethods) {
|
||||
if (resolveMethod) {
|
||||
methods.timeoutLoading()
|
||||
let isPass = await resolveMethod();
|
||||
if (isPass !== true) return
|
||||
}
|
||||
}
|
||||
}
|
||||
// 审批前保存回调函数
|
||||
let resolveSaves = data.currJob.resolveSaves
|
||||
if (isAudit && !validateNull(resolveSaves)) {
|
||||
for (const resolveSave of resolveSaves) {
|
||||
if (resolveSave) {
|
||||
methods.timeoutLoading()
|
||||
await resolveSave();
|
||||
}
|
||||
}
|
||||
}
|
||||
// 判断页面是否保存
|
||||
let hiJob = data.currJob.hiJob;
|
||||
let elTabs = data.currJob.elTabs.filter(f => (f.isFormEdit !== '0' && !hiJob))
|
||||
if (elTabs) {
|
||||
let find = elTabs.find(f => f.isSave !== true);
|
||||
if (find) useMessage().info(find.formName + ' 未保存')
|
||||
else {
|
||||
methods.timeoutLoading()
|
||||
btnMethods.onHandleJob(jobBtn)
|
||||
}
|
||||
return
|
||||
}
|
||||
methods.timeoutLoading()
|
||||
btnMethods.onHandleJob(jobBtn)
|
||||
},
|
||||
handleJobBtns(jobBtn) {
|
||||
// TODO 可在此处做全局权限判断
|
||||
let value = DIC_PROP.JOB_BTNS[13].value;
|
||||
let next = userInfo.userInfos.authBtnList.some(v => v === 'jsonflow_runjob_invalid');
|
||||
if (next) {
|
||||
return (jobBtn === value && next) || jobBtn !== value
|
||||
} else return jobBtn !== value
|
||||
},
|
||||
getJobBtnName(jobBtn) {
|
||||
return getJobBtnName(jobBtn)
|
||||
},
|
||||
timeoutLoading() {
|
||||
data.loading = true
|
||||
setTimeout(() => {// 防重复提交
|
||||
data.loading = false
|
||||
}, 3 * 1000)
|
||||
}
|
||||
}
|
||||
|
||||
// 监听双向绑定
|
||||
watch(
|
||||
() => props.currJob.id,
|
||||
(val) => {
|
||||
methods.initJobData();
|
||||
}
|
||||
);
|
||||
|
||||
onMounted(() => {
|
||||
methods.initJobData();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
@import "../../../flow/components/style/flow-drawer.scss";
|
||||
</style>
|
||||
130
src/flow/components/handle-job/index.ts
Normal file
130
src/flow/components/handle-job/index.ts
Normal file
@@ -0,0 +1,130 @@
|
||||
import * as doJob from "/@/api/jsonflow/do-job";
|
||||
import {useMessageBox} from "/@/hooks/message";
|
||||
import {DIC_PROP} from "/@/flow/support/dict-prop";
|
||||
import * as runFlow from "/@/api/jsonflow/run-flow";
|
||||
import {appointUser} from "/@/api/jsonflow/do-job";
|
||||
|
||||
// 签收反签收任务
|
||||
export function handleSignForJob(methods, $message, getDataList, row, type) {
|
||||
let obj = {id: row.id, signForType: type};
|
||||
if (type === '0') {
|
||||
// 判断反签收
|
||||
methods.onSignForJob(obj)
|
||||
return
|
||||
}
|
||||
doJob.signForJob(obj)
|
||||
.then(() => {
|
||||
$message.success('操作成功');
|
||||
getDataList();
|
||||
})
|
||||
}
|
||||
|
||||
// 反签收任务
|
||||
export function onSignForJob($message, flowJob, getDataList, obj) {
|
||||
let msg = '是否确认反签收当前任务, 将放回待认领列表?'
|
||||
useMessageBox()
|
||||
.confirm(msg)
|
||||
.then(() => {
|
||||
return doJob.signForJob(obj)
|
||||
}).then(() => {
|
||||
$message.success('操作成功')
|
||||
flowJob.delJobLen();
|
||||
getDataList();
|
||||
})
|
||||
}
|
||||
|
||||
// 挂起激活任务
|
||||
export function handleSuspension($message, getDataList, row, suspension) {
|
||||
if (suspension === '0') {
|
||||
row.suspension = suspension
|
||||
doJob.suspension(row).then(() => {
|
||||
$message.success('操作成功');
|
||||
getDataList();
|
||||
})
|
||||
return
|
||||
}
|
||||
// 增加挂起原因
|
||||
useMessageBox()
|
||||
.prompt('请输入挂起原因').then(({value}) => {
|
||||
row.suspension = suspension
|
||||
row.suspensionReason = value
|
||||
return doJob.suspension(row)
|
||||
}).then(() => {
|
||||
$message.success('操作成功');
|
||||
getDataList();
|
||||
})
|
||||
}
|
||||
|
||||
// 提前结束流程
|
||||
export function handleEarlyComplete($message, flowJob, getDataList, row) {
|
||||
useMessageBox().prompt('请输入提前结束流程理由')
|
||||
.then(({ value }) => {
|
||||
row.jobBtn = DIC_PROP.JOB_BTNS[16].value
|
||||
row.invalidReason = value
|
||||
row.flowStatus = '0'
|
||||
return runFlow.earlyComplete(row)
|
||||
}).then(() => {
|
||||
$message.success('操作成功')
|
||||
flowJob.delJobLen();
|
||||
getDataList();
|
||||
})
|
||||
}
|
||||
|
||||
// 终止流程
|
||||
export function handleTerminateFlow($message, flowJob, getDataList, row) {
|
||||
useMessageBox().prompt('请输入终止理由')
|
||||
.then(({ value }) => {
|
||||
row.jobBtn = DIC_PROP.JOB_BTNS[15].value
|
||||
row.invalidReason = value
|
||||
row.flowStatus = '0'
|
||||
return runFlow.terminateFlow(row)
|
||||
}).then(() => {
|
||||
$message.success('操作成功')
|
||||
flowJob.delJobLen();
|
||||
getDataList();
|
||||
})
|
||||
}
|
||||
|
||||
// 作废流程
|
||||
export function handleInvalidFlow($message, flowJob, getDataList, row) {
|
||||
useMessageBox().prompt('请输入作废理由')
|
||||
.then(({ value }) => {
|
||||
row.jobBtn = DIC_PROP.JOB_BTNS[17].value
|
||||
row.invalidReason = value
|
||||
row.flowStatus = '0'
|
||||
return runFlow.invalidFlow(row)
|
||||
}).then(() => {
|
||||
$message.success('操作成功')
|
||||
flowJob.delJobLen();
|
||||
getDataList();
|
||||
})
|
||||
}
|
||||
|
||||
// 转办任务
|
||||
export function onTurnRunJob($message, flowJob, getDataList, data, role) {
|
||||
useMessageBox()
|
||||
.confirm('是否确认转办名称为"' + data.currJob.jobName + '"的任务?')
|
||||
.then(() => {
|
||||
data.currJob.jobType = role.jobType
|
||||
data.currJob.roleId = role.roleId
|
||||
data.currJob.jobBtn = DIC_PROP.JOB_BTNS[14].value
|
||||
return doJob.turnRunJob(data.currJob)
|
||||
}).then(() => {
|
||||
$message.success('操作成功')
|
||||
flowJob.delJobLen();
|
||||
getDataList();
|
||||
})
|
||||
}
|
||||
|
||||
// 指定任务参与者
|
||||
export function onJobNextUserRole($message, items, getDataList, data) {
|
||||
useMessageBox()
|
||||
.confirm('是否确认指派该参与者?')
|
||||
.then(() => {
|
||||
data.currJob.nextUserRole = items[0]
|
||||
appointUser(data.currJob).then(() => {
|
||||
$message.success('操作成功');
|
||||
getDataList();
|
||||
})
|
||||
})
|
||||
}
|
||||
188
src/flow/components/handle-job/job-signature.vue
Normal file
188
src/flow/components/handle-job/job-signature.vue
Normal file
@@ -0,0 +1,188 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-form ref="dataFormRef" :model="form" :rules="dataRules" label-width="150px" v-loading="loading" :disabled="operType === 'view'">
|
||||
<span style="margin-left: 30px">什么是加签减签?加签指在同一个节点里增加其他【参与者】参与审批,加签分为前加签、后加签、加并签<br/><span style="margin-left: 30px">1、前加签即在当前审批人之前增加一个参与者参与审批,该参与者审批完成之后会回到当前审批人</span>
|
||||
<br/><span style="margin-left: 30px">2、后加签即在当前审批人之后增加一个参与者参与审批,当前审批人审批之后流转到该参与者</span><br/><span style="margin-left: 30px">3、加并签即与当前审批人同时审批</span><br/><span style="margin-left: 30px">4、减签即减少参与者</span></span>
|
||||
<el-row :gutter="24">
|
||||
<el-divider> 加签设置 </el-divider>
|
||||
|
||||
<el-col :span="12" class="mb20">
|
||||
<el-form-item label="在当前节点加签" prop="runNodeId">
|
||||
<el-select v-model="form.runNodeId" placeholder="默认当前节点" clearable filterable disabled>
|
||||
<el-option v-for="(item, index) in cascadeDic.runNodeId" :key="index" :label="item.nodeName" :value="item.id"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="12" class="mb20">
|
||||
<el-form-item :label="t('runJob.jobName')" prop="jobName">
|
||||
<el-input v-model="form.jobName" :placeholder="t('runJob.inputJobNameTip')"/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="12" class="mb20">
|
||||
<el-form-item label="加签参与者类型" prop="jobType">
|
||||
<el-radio-group @change="handleRoleType"
|
||||
v-model="form.jobType">
|
||||
<el-radio v-for="(item, index) in DIC_PROP.JOB_USER_TYPE" :key="index" :label="item.value">
|
||||
{{ item.label }}
|
||||
</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="12" class="mb20" v-if="form.jobType === DIC_PROP.JOB_USER_TYPE[0].value">
|
||||
<el-form-item label="指定加签人员" prop="roleId">
|
||||
<el-select v-model="form.roleId" placeholder="请输入用户名称进行模糊搜索" clearable filterable
|
||||
remote :remote-method="methodsRemote.remoteMethodUser" :reserve-keyword="false">
|
||||
<el-option v-for="(item, index) in dicData.users" :key="index" :label="item.name" :value="item.userId"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="12" class="mb20" v-if="form.jobType === DIC_PROP.JOB_USER_TYPE[1].value">
|
||||
<el-form-item label="指定加签角色" prop="roleId">
|
||||
<el-select v-model="form.roleId" placeholder="请输入角色名称进行模糊搜索" clearable filterable
|
||||
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-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="12" class="mb20" v-if="form.jobType === DIC_PROP.JOB_USER_TYPE[2].value">
|
||||
<el-form-item label="指定加签岗位" prop="roleId">
|
||||
<el-select v-model="form.roleId" placeholder="请输入岗位名称进行模糊搜索" clearable filterable
|
||||
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-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="12" class="mb20" v-if="form.jobType === DIC_PROP.JOB_USER_TYPE[3].value">
|
||||
<el-form-item label="指定加签部门" prop="roleId">
|
||||
<el-select v-model="form.roleId" placeholder="请输入部门名称进行模糊搜索" clearable filterable
|
||||
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-form-item>
|
||||
</el-col>
|
||||
|
||||
</el-row>
|
||||
</el-form>
|
||||
<footer class="el-dialog__footer">
|
||||
<span class="dialog-footer">
|
||||
<el-button type="primary" @click="handleUpdate" :loading="loading">{{ $t('common.confirmButtonText') }}</el-button>
|
||||
</span>
|
||||
</footer>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="JobSignature">
|
||||
import { useI18n } from "vue-i18n"
|
||||
import {onCascadeChange, onLoadDicUrl, remoteMethodByKey} from "../convert-name/convert";
|
||||
import {useMessage} from "/@/hooks/message";
|
||||
import {handleChangeJobType} from "../../index";
|
||||
|
||||
const { t } = useI18n();
|
||||
const $message = useMessage();
|
||||
const $emit = defineEmits(['onJobSignature']);
|
||||
// 定义变量内容
|
||||
const dataFormRef = ref();
|
||||
const loading = ref(false);
|
||||
const operType = ref(false);
|
||||
|
||||
// 定义字典
|
||||
const dicData = reactive({});
|
||||
const cascadeDic = reactive({});
|
||||
const onLoad = onLoadDicUrl();
|
||||
const onCascade = onCascadeChange(cascadeDic, {key: "flowInstId", cascades: ["runNodeId"]});
|
||||
onMounted(() => {
|
||||
// onLoad(dicData);
|
||||
});
|
||||
|
||||
// 提交表单数据
|
||||
const form = reactive({
|
||||
runNodeId: '',
|
||||
jobType: '',
|
||||
roleId: null,
|
||||
});
|
||||
|
||||
// 定义校验规则
|
||||
const dataRules = ref({
|
||||
jobType: [{required: true, message: '加签参与者类型不能为空', trigger: 'blur'}],
|
||||
roleId: [{required: true, message: '加签参与者不能为空', trigger: 'blur'}],
|
||||
})
|
||||
|
||||
const props = defineProps({
|
||||
currJob: {
|
||||
default: null,
|
||||
}
|
||||
});
|
||||
|
||||
function initJobData() {
|
||||
Object.assign(form, props.currJob)
|
||||
form.roleId = null
|
||||
onCascade(form)
|
||||
}
|
||||
|
||||
function handleRoleType() {
|
||||
handleChangeJobType(dicData, form)
|
||||
}
|
||||
|
||||
const methodsRemote = {
|
||||
remoteMethodUser(query: string) {
|
||||
remoteMethodByKey(query, onLoad, dicData, 'userName', "users")
|
||||
},
|
||||
remoteMethodRole(query: string) {
|
||||
remoteMethodByKey(query, onLoad, dicData, 'roleName', "roles")
|
||||
},
|
||||
remoteMethodPost(query: string) {
|
||||
remoteMethodByKey(query, onLoad, dicData, 'postName', "posts")
|
||||
},
|
||||
remoteMethodDept(query: string) {
|
||||
remoteMethodByKey(query, onLoad, dicData, 'deptName', "depts")
|
||||
},
|
||||
}
|
||||
|
||||
async function handleUpdate() {
|
||||
const valid = await dataFormRef.value.validate().catch(() => {});
|
||||
if (!valid) return false;
|
||||
|
||||
form.nextUserRole = {jobType: form.jobType, roleId: form.roleId, jobName: form.jobName}
|
||||
|
||||
props.currJob.nextUserRole = form.nextUserRole
|
||||
|
||||
loading.value = true
|
||||
try {
|
||||
$emit('onJobSignature', props.currJob)
|
||||
setTimeout(() => { // 异步异常
|
||||
loading.value = false
|
||||
}, 3000)
|
||||
} catch (e) {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 监听双向绑定
|
||||
watch(
|
||||
() => props.currJob.id,
|
||||
(val) => {
|
||||
initJobData();
|
||||
}
|
||||
);
|
||||
|
||||
onMounted(() => {
|
||||
initJobData();
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.el-dialog__footer {
|
||||
text-align: center;
|
||||
|
||||
.dialog-footer {
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
224
src/flow/components/handle-job/node-signature.vue
Normal file
224
src/flow/components/handle-job/node-signature.vue
Normal file
@@ -0,0 +1,224 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-form ref="dataFormRef" :model="form" :rules="dataRules" label-width="150px" v-loading="loading" :disabled="operType === 'view'">
|
||||
<span style="margin-left: 30px">什么是加减节点?加节点指在流程图里增加其他节点及【参与者】参与审批,加节点分为前加节点、后加节点、加并节点 <br/><span style="margin-left: 30px">1、前加节点即在当前审批人节点之前增加一个节点及参与者参与审批,该参与者节点审批完成之后会回到当前审批人节点</span>
|
||||
<br/><span style="margin-left: 30px">2、后加节点即在当前审批人节点之后增加一个节点及参与者参与审批,当前审批人节点审批之后流转到该参与者节点</span><br/><span style="margin-left: 30px">3、加并节点即与当前审批人节点在同一个节点层级,若当前审批人节点为会签节点,则需所有节点通过。</span>
|
||||
若当前审批人节点为或签节点,则一个节点通过即可。若当前审批人节点为依次审批,则按顺序依次通过即可<br/><span style="margin-left: 30px">4、减节点即减少节点及参与者</span></span>
|
||||
<el-row :gutter="24">
|
||||
<el-divider> 加节点设置 </el-divider>
|
||||
|
||||
<el-col :span="12" class="mb20">
|
||||
<el-form-item label="基于当前节点加节点" prop="runNodeId">
|
||||
<el-select v-model="form.runNodeId" placeholder="默认当前节点" clearable filterable disabled>
|
||||
<el-option v-for="(item, index) in cascadeDic.runNodeId" :key="index" :label="item.nodeName" :value="item.id"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="12" class="mb20">
|
||||
<el-form-item :label="t('runNode.nodeName')" :rules="{ required: true, message: '节点名称不能为空', trigger: 'blur' }">
|
||||
<el-input v-model="form.nodeJobSigned.nodeName" :placeholder="t('runNode.inputNodeNameTip')"/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="12" class="mb20">
|
||||
<el-form-item label="加节点参与者类型" prop="jobType">
|
||||
<el-radio-group @change="handleRoleType"
|
||||
v-model="form.jobType">
|
||||
<el-radio v-for="(item, index) in DIC_PROP.JOB_USER_TYPE" :key="index" :label="item.value">
|
||||
{{ item.label }}
|
||||
</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="24" class="mb20" v-if="props.currJob.runNodeVO.nodeApproveMethod === DIC_PROP.NODE_APPROVE_METHOD[2].value
|
||||
&& props.currJob.nodeJobSigned.nodeSignedType === DIC_PROP.NODE_SIGNATURE_TYPE[2].value">
|
||||
<el-form-item label="依次审批节点顺序" prop="signedType">
|
||||
<el-radio-group v-model="form.nodeJobSigned.signedType">
|
||||
<el-radio v-for="(item, index) in DIC_PROP.NODE_SEQUENTIAL_TYPE" :key="index" :label="item.value">
|
||||
{{ item.label }}
|
||||
</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="12" class="mb20" v-if="form.jobType === DIC_PROP.JOB_USER_TYPE[0].value">
|
||||
<el-form-item label="指定参与者" prop="roleId">
|
||||
<el-tooltip content="请输入用户名称进行模糊搜索" placement="top">
|
||||
<el-select v-model="form.roleId" placeholder="请输入用户名称进行模糊搜索" clearable filterable
|
||||
remote :remote-method="methodsRemote.remoteMethodUser" :reserve-keyword="false">
|
||||
<el-option v-for="(item, index) in dicData.users" :key="index" :label="item.name" :value="item.userId"></el-option>
|
||||
</el-select>
|
||||
</el-tooltip>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="12" class="mb20" v-if="form.jobType === DIC_PROP.JOB_USER_TYPE[1].value">
|
||||
<el-form-item label="指定参与者" prop="roleId">
|
||||
<el-tooltip content="请输入角色名称进行模糊搜索" placement="top">
|
||||
<el-select v-model="form.roleId" placeholder="请输入角色名称进行模糊搜索" clearable filterable
|
||||
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-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="12" class="mb20" v-if="form.jobType === DIC_PROP.JOB_USER_TYPE[2].value">
|
||||
<el-form-item label="指定参与者" prop="roleId">
|
||||
<el-tooltip content="请输入岗位名称进行模糊搜索" placement="top">
|
||||
<el-select v-model="form.roleId" placeholder="请输入岗位名称进行模糊搜索" clearable filterable
|
||||
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-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="12" class="mb20" v-if="form.jobType === DIC_PROP.JOB_USER_TYPE[3].value">
|
||||
<el-form-item label="指定参与者" prop="roleId">
|
||||
<el-tooltip content="请输入部门名称进行模糊搜索" placement="top">
|
||||
<el-select v-model="form.roleId" placeholder="请输入部门名称进行模糊搜索" clearable filterable
|
||||
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-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="12" class="mb20">
|
||||
<el-form-item :label="t('runNode.nodeType')" prop="nodeType">
|
||||
<el-select v-model="form.nodeJobSigned.nodeType" :placeholder="t('runNode.inputNodeTypeTip')" filterable>
|
||||
<el-option v-for="(item, index) in DIC_PROP.NODE_TYPE.slice(1, 3)" :key="index" :label="item.label" :value="item.value"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
</el-row>
|
||||
</el-form>
|
||||
<footer class="el-dialog__footer">
|
||||
<span class="dialog-footer">
|
||||
<el-button type="primary" @click="handleUpdate" :loading="loading">{{ $t('common.confirmButtonText') }}</el-button>
|
||||
</span>
|
||||
</footer>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="JobSignature">
|
||||
import { useI18n } from "vue-i18n"
|
||||
import {onCascadeChange, onLoadDicUrl, remoteMethodByKey} from "../convert-name/convert";
|
||||
import {useMessage} from "/@/hooks/message";
|
||||
import {handleChangeJobType} from "../../index";
|
||||
|
||||
const { t } = useI18n();
|
||||
const $message = useMessage();
|
||||
const $emit = defineEmits(['onNodeSignature']);
|
||||
// 定义变量内容
|
||||
const dataFormRef = ref();
|
||||
const loading = ref(false);
|
||||
const operType = ref(false);
|
||||
|
||||
// 定义字典
|
||||
const dicData = reactive({});
|
||||
const cascadeDic = reactive({});
|
||||
const onLoad = onLoadDicUrl();
|
||||
const onCascade = onCascadeChange(cascadeDic, {key: "flowInstId", cascades: ["runNodeId"]});
|
||||
onMounted(() => {
|
||||
// onLoad(dicData);
|
||||
});
|
||||
|
||||
// 提交表单数据
|
||||
const form = reactive({
|
||||
runNodeId: null,
|
||||
jobType: '',
|
||||
roleId: null,
|
||||
nodeJobSigned: {},
|
||||
});
|
||||
|
||||
// 定义校验规则
|
||||
const dataRules = ref({
|
||||
jobType: [{required: true, message: '参与者类型不能为空', trigger: 'blur'}],
|
||||
roleId: [{required: true, message: '参与者不能为空', trigger: 'blur'}],
|
||||
// nodeName: [{required: true, message: '节点名称不能为空', trigger: 'blur'}],
|
||||
})
|
||||
|
||||
const props = defineProps({
|
||||
currJob: {
|
||||
default: null,
|
||||
}
|
||||
});
|
||||
|
||||
function initJobData() {
|
||||
Object.assign(form, props.currJob)
|
||||
form.roleId = null
|
||||
onCascade(form)
|
||||
}
|
||||
|
||||
function handleRoleType() {
|
||||
handleChangeJobType(dicData, form)
|
||||
}
|
||||
|
||||
const methodsRemote = {
|
||||
remoteMethodUser(query: string) {
|
||||
remoteMethodByKey(query, onLoad, dicData, 'userName', "users")
|
||||
},
|
||||
remoteMethodRole(query: string) {
|
||||
remoteMethodByKey(query, onLoad, dicData, 'roleName', "roles")
|
||||
},
|
||||
remoteMethodPost(query: string) {
|
||||
remoteMethodByKey(query, onLoad, dicData, 'postName', "posts")
|
||||
},
|
||||
remoteMethodDept(query: string) {
|
||||
remoteMethodByKey(query, onLoad, dicData, 'deptName', "depts")
|
||||
},
|
||||
}
|
||||
|
||||
async function handleUpdate() {
|
||||
// TOD 上面两种方式都校验不了
|
||||
if (!form.nodeJobSigned.nodeName) {
|
||||
$message.error("节点名称不能为空");
|
||||
return
|
||||
}
|
||||
const valid = await dataFormRef.value.validate().catch(() => {});
|
||||
if (!valid) return false;
|
||||
|
||||
form.nextUserRole = {jobType: form.jobType, roleId: form.roleId}
|
||||
|
||||
props.currJob.nextUserRole = form.nextUserRole
|
||||
props.currJob.nodeJobSigned = form.nodeJobSigned
|
||||
|
||||
loading.value = true
|
||||
try {
|
||||
$emit('onNodeSignature', props.currJob)
|
||||
setTimeout(() => { // 异步异常
|
||||
loading.value = false
|
||||
}, 3000)
|
||||
} catch (e) {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 监听双向绑定
|
||||
watch(
|
||||
() => props.currJob.id,
|
||||
(val) => {
|
||||
initJobData();
|
||||
}
|
||||
);
|
||||
|
||||
onMounted(() => {
|
||||
initJobData();
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.el-dialog__footer {
|
||||
text-align: center;
|
||||
|
||||
.dialog-footer {
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
43
src/flow/components/i18n/en.ts
Normal file
43
src/flow/components/i18n/en.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
export default {
|
||||
jfI18n: {
|
||||
temp: 'Temp',
|
||||
invalid: 'Invalid',
|
||||
pub: 'Publish',
|
||||
oneBtnDesign: 'OneBtn Design',
|
||||
viewPageBtn: 'View Page',
|
||||
onceMoreDesign: 'Once More Design',
|
||||
designFlow: 'Design Flow',
|
||||
viewFlow: 'View Flow',
|
||||
print: 'Print',
|
||||
printTemplate: 'Print Template',
|
||||
submit: 'Submit',
|
||||
viewForm: 'View Form',
|
||||
initHandover: 'Initial Handover',
|
||||
initialBtn: 'Initiate',
|
||||
batchBtn: 'Batch Save',
|
||||
saveBtn: 'Save',
|
||||
cancelBtn: 'Cancel',
|
||||
updateFlow: 'Update Flow',
|
||||
rejectBtn: 'Reject Check',
|
||||
receiveBtn: 'Confirm Receive',
|
||||
recallBtn: 'Recall',
|
||||
resetBtn: 'Reset',
|
||||
operate: 'Operate',
|
||||
more: 'More',
|
||||
},
|
||||
jfAttr: {
|
||||
paramValType: 'Parameter Value Type',
|
||||
varKeyVal: 'Form Field OR Fixed Value',
|
||||
sysVarKeyVal: 'Sys Field OR Fixed Value',
|
||||
subVarKeyVal: 'Subform Field',
|
||||
subVarKeyVal2: 'Subform Field OR Fixed Value',
|
||||
requestHeader: 'Request Header',
|
||||
requestParam: 'Request Param',
|
||||
returnParam: 'Return Param',
|
||||
},
|
||||
jfForm: {
|
||||
urgentTask: 'Urgent task',
|
||||
urgentSubFlow: 'Urgent Sub Flow',
|
||||
reduceSign: 'Reduce Sign'
|
||||
}
|
||||
}
|
||||
43
src/flow/components/i18n/zh-cn.ts
Normal file
43
src/flow/components/i18n/zh-cn.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
export default {
|
||||
jfI18n: {
|
||||
temp: '暂存',
|
||||
invalid: '作废',
|
||||
pub: '发布',
|
||||
oneBtnDesign: '一键设计',
|
||||
viewPageBtn: '查看表单',
|
||||
onceMoreDesign: '再次设计',
|
||||
designFlow: '设计流程图',
|
||||
viewFlow: '查看流程图',
|
||||
print: '打印',
|
||||
printTemplate: '打印设计',
|
||||
submit: '提交',
|
||||
viewForm: '查看表单',
|
||||
initHandover: '发起交接',
|
||||
initialBtn: '发起',
|
||||
batchBtn: '批量保存',
|
||||
saveBtn: '保存',
|
||||
cancelBtn: '取消',
|
||||
updateFlow: '修改工单',
|
||||
rejectBtn: '驳回选中项',
|
||||
receiveBtn: '确认接收',
|
||||
recallBtn: '撤回',
|
||||
resetBtn: '重发',
|
||||
operate: '操作',
|
||||
more: '更多',
|
||||
},
|
||||
jfAttr: {
|
||||
paramValType: '参数值类型',
|
||||
varKeyVal: '表单字段或固定值',
|
||||
sysVarKeyVal: '系统字段或固定值',
|
||||
subVarKeyVal: '子流程表单字段',
|
||||
subVarKeyVal2: '子流程表单字段或固定值',
|
||||
requestHeader: '请求头',
|
||||
requestParam: '请求参数',
|
||||
returnParam: '返回值',
|
||||
},
|
||||
jfForm: {
|
||||
urgentTask: '催办任务',
|
||||
urgentSubFlow: '催办子流程',
|
||||
reduceSign: '减签'
|
||||
}
|
||||
}
|
||||
42
src/flow/components/index.ts
Normal file
42
src/flow/components/index.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
import ConvertName from './convert-name/index.vue';
|
||||
import ConvertRoleName from './convert-name/role-index.vue';
|
||||
import ConvertGroupName from './convert-name/group-index.vue';
|
||||
import {App} from "vue";
|
||||
import UserPicker from "./user-role/user.vue";
|
||||
import RolePicker from "./user-role/role.vue";
|
||||
import PostPicker from "./user-role/post.vue";
|
||||
import DeptPicker from "./user-role/dept.vue";
|
||||
import PhoneInput from "./form-create/phone.vue";
|
||||
import IdCartInput from "./form-create/id-card.vue";
|
||||
import FlowNameInput from "./form-create/flow-name.vue";
|
||||
import FormNameInput from "./form-create/form-name.vue";
|
||||
import FormCodeInput from "./form-create/form-code.vue";
|
||||
import EmailInput from "./form-create/email.vue";
|
||||
import SignInput from "./sign/index.vue";
|
||||
|
||||
// vite glob导入
|
||||
const modules: Record<string, () => Promise<unknown>> = import.meta.glob(['../../views/jsonflow/*/*.vue', '../../views/order/*/*.vue'])
|
||||
|
||||
/**
|
||||
* 导出全局注册工作流审批表单组件
|
||||
* @param app vue 实例
|
||||
*/
|
||||
function dynamicImport(app: App) {
|
||||
for (const module in modules) {
|
||||
// @ts-ignore
|
||||
app.component(module, defineAsyncComponent(modules[module]));
|
||||
}
|
||||
app.component('UserPicker', UserPicker);
|
||||
app.component('RolePicker', RolePicker);
|
||||
app.component('PostPicker', PostPicker);
|
||||
app.component('DeptPicker', DeptPicker);
|
||||
app.component('PhoneInput', PhoneInput);
|
||||
app.component('IdCartInput', IdCartInput);
|
||||
app.component('FlowNameInput', FlowNameInput);
|
||||
app.component('FormNameInput', FormNameInput);
|
||||
app.component('FormCodeInput', FormCodeInput);
|
||||
app.component('EmailInput', EmailInput);
|
||||
app.component('SignInput', SignInput);
|
||||
}
|
||||
|
||||
export { ConvertName, ConvertRoleName, ConvertGroupName, dynamicImport };
|
||||
180
src/flow/components/onebtn-design/design.vue
Normal file
180
src/flow/components/onebtn-design/design.vue
Normal file
@@ -0,0 +1,180 @@
|
||||
<template>
|
||||
<div class="layout-padding">
|
||||
<el-container>
|
||||
<el-header style="background: white">
|
||||
<flow-design-header :currFlowForm="props.currFlowForm" @handleDesignFlow="methods.handleDesignFlow"
|
||||
@syncCurrFlowForm="methods.syncCurrFlowForm"
|
||||
@publish="methods.publishFlow"></flow-design-header>
|
||||
</el-header>
|
||||
<div style="min-width: 980px;">
|
||||
<form-design :currFlowForm="props.currFlowForm" ref="formDesign" v-show="methods.showFormDesign()"/>
|
||||
<form-def-perm :currFlowForm="props.currFlowForm" ref="formDefPerm" v-show="methods.showFormDefPerm()"/>
|
||||
</div>
|
||||
<div id="initClientHeight">
|
||||
<form-setting :currFlowForm="props.currFlowForm" ref="formSetting" v-show="props.currFlowForm.active === 'formSetting'"/>
|
||||
<flow-design :currFlowForm="props.currFlowForm" ref="flowDesign" v-show="props.currFlowForm.active === 'flowDesign'"/>
|
||||
</div>
|
||||
</el-container>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="FlowFormDesign">
|
||||
import {useI18n} from "vue-i18n"
|
||||
import {useMessageBox} from "/@/hooks/message";
|
||||
import {notifyLeft, validateRunFlowId} from "/@/flow";
|
||||
import {confirmCancelAndClose, formWidgetDesignHeight, handleUpgradeVersion} from "/@/flow/utils";
|
||||
import {DIC_PROP} from "../../support/dict-prop";
|
||||
|
||||
const {t} = useI18n();
|
||||
const {proxy} = getCurrentInstance();
|
||||
// 引入组件
|
||||
const FlowDesignHeader = defineAsyncComponent(() => import('./header.vue'));
|
||||
const FormDesign = defineAsyncComponent(() => import('../form-create/designer.vue'));
|
||||
const FlowDesign = defineAsyncComponent(() => import('/@/views/jsonflow/flow-design/index.vue'));
|
||||
const FormSetting = defineAsyncComponent(() => import('./setting.vue'));
|
||||
const FormDefPerm = defineAsyncComponent(() => import('/@/views/jsonflow/form-option/form-def-perm.vue'));
|
||||
|
||||
const $emit = defineEmits(['handleDesignFlow']);
|
||||
const props = defineProps({
|
||||
currFlowForm: {
|
||||
type: Object,
|
||||
default: null,
|
||||
}
|
||||
});
|
||||
|
||||
const methods = {
|
||||
handleDesignFlow(bool) {
|
||||
$emit("handleDesignFlow", bool);
|
||||
},
|
||||
initClientHeight() {
|
||||
nextTick(() => {
|
||||
let browserHeight = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight;
|
||||
formWidgetDesignHeight(browserHeight);
|
||||
})
|
||||
},
|
||||
showFormDesign() {
|
||||
return props.currFlowForm.active === 'formDesign' && props.currFlowForm.type !== DIC_PROP.FORM_TYPE[1].value
|
||||
},
|
||||
showFormDefPerm() {
|
||||
return props.currFlowForm.active === 'formDesign' && props.currFlowForm.type === DIC_PROP.FORM_TYPE[1].value
|
||||
},
|
||||
initCurrFlowInfo(active) {
|
||||
nextTick(() => {
|
||||
window._jfOperate.reAutoInitNodes();
|
||||
methods.syncCurrFlowInfo(active)
|
||||
})
|
||||
},
|
||||
syncCurrFlowInfo(active, isInit?) {
|
||||
let attrs = methods.refsFlowDataAttrs();
|
||||
if (active !== 'flowDesign' || isInit) {
|
||||
attrs.flowKey = props.currFlowForm.flowKey
|
||||
attrs.groupName = props.currFlowForm.groupName
|
||||
attrs.flowName = props.currFlowForm.formName
|
||||
} else {
|
||||
if (attrs.flowKey) {
|
||||
props.currFlowForm.defFlowId = attrs.id
|
||||
props.currFlowForm.flowKey = attrs.flowKey
|
||||
props.currFlowForm.groupName = attrs.groupName
|
||||
props.currFlowForm.formName = attrs.flowName
|
||||
}
|
||||
}
|
||||
validateRunFlowId(props, attrs)
|
||||
},
|
||||
syncCurrFormInfo(callback?) {
|
||||
if (props.currFlowForm.type !== DIC_PROP.FORM_TYPE[1].value) {
|
||||
proxy.$refs.formDefPerm.syncCurrFormInfo(true)
|
||||
// 保存表单信息
|
||||
proxy.$refs.formDesign.handleSubmit(() => {
|
||||
if (callback) callback()
|
||||
});
|
||||
} else {
|
||||
proxy.$refs.formDefPerm.syncCurrFormInfo(false)
|
||||
props.currFlowForm.formInfo = null
|
||||
}
|
||||
},
|
||||
refsFlowDataAttrs() {
|
||||
// 每次取最新
|
||||
return proxy.$refs.flowDesign.flowData.attrs;
|
||||
},
|
||||
async syncCurrFlowForm(menu?, active?, callback?) {
|
||||
let preSave = await proxy.$refs.formSetting.validatePreSave();
|
||||
if (callback) callback(preSave)
|
||||
if (!preSave) return
|
||||
if (active === 'formDesign') methods.syncCurrFormInfo();
|
||||
let defFlowId = props.currFlowForm.defFlowId;
|
||||
let flowInstId = props.currFlowForm.flowInstId;
|
||||
if (menu === 'flowDesign') {
|
||||
let attrs = methods.refsFlowDataAttrs();
|
||||
if (defFlowId && !attrs.flowKey) {
|
||||
// 切换到才初始化
|
||||
proxy.$refs.flowDesign.initFlow(defFlowId, flowInstId, () => {
|
||||
methods.syncCurrFlowInfo(menu, true)
|
||||
});
|
||||
} else methods.initCurrFlowInfo(active)
|
||||
} else if (active === 'flowDesign'){
|
||||
methods.syncCurrFlowInfo(active)
|
||||
}
|
||||
},
|
||||
async publishFlow(menu, callback, status, version) {
|
||||
if (version === true) {
|
||||
try {
|
||||
if (props.currFlowForm.isNew) {
|
||||
notifyLeft('当前设计版本已最新, 请确认是否已保存')
|
||||
return
|
||||
}
|
||||
await useMessageBox().confirm('是否确定升级版本?');
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
await handleUpgradeVersion(props)
|
||||
// 升级流程信息
|
||||
proxy.$refs.flowDesign.initNewFlow(() => {
|
||||
methods.syncCurrFlowInfo('flowDesign')
|
||||
let confirmObj = {text: "发布流程", callback: () => { methods.publishFlow(menu, callback, '1', null) }}
|
||||
let cancelObj = {text: "暂存流程", callback: () => { methods.publishFlow(menu, callback, '-1', null) }}
|
||||
confirmCancelAndClose(confirmObj, cancelObj, '流程设计升级版本成功! 是否立即暂存或发布?')
|
||||
}, true);
|
||||
return
|
||||
}
|
||||
methods.syncCurrFormInfo(callback);
|
||||
// 校验表单设计
|
||||
let otherSave = await proxy.$refs.formSetting.validateOtherSave();
|
||||
if (!otherSave) return
|
||||
// 保存流程信息
|
||||
props.currFlowForm.flowDesigning = true
|
||||
proxy.$refs.flowDesign.publishFlow(() => {
|
||||
methods.syncCurrFlowInfo('flowDesign')
|
||||
methods.submitFormSetting(callback, status)
|
||||
if (callback) callback()
|
||||
}, status);
|
||||
},
|
||||
async submitFormSetting(callback, status) {
|
||||
// 保存字段信息
|
||||
if (props.currFlowForm.type === DIC_PROP.FORM_TYPE[1].value) {
|
||||
await proxy.$refs.formDefPerm.handleSubmit(() => {
|
||||
props.currFlowForm.formDesign = true
|
||||
if (callback) callback()
|
||||
});
|
||||
}
|
||||
await proxy.$refs.formSetting.handleSubmit(() => {
|
||||
if (callback) callback()
|
||||
}, status)
|
||||
}
|
||||
}
|
||||
|
||||
// 监听双向绑定
|
||||
watch(
|
||||
() => props.currFlowForm.id,
|
||||
(val) => {
|
||||
methods.initClientHeight()
|
||||
}
|
||||
);
|
||||
|
||||
onMounted(() => {
|
||||
methods.initClientHeight();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
</style>
|
||||
312
src/flow/components/onebtn-design/form.vue
Normal file
312
src/flow/components/onebtn-design/form.vue
Normal file
@@ -0,0 +1,312 @@
|
||||
<template>
|
||||
<el-dialog :title="title" v-model="visible" width="80%"
|
||||
:close-on-click-modal="false" draggable>
|
||||
<el-form ref="dataFormRef" :model="form" :rules="dataRules" label-width="130px" v-loading="loading" :disabled="operType === 'view'">
|
||||
<el-row :gutter="24">
|
||||
<el-col :span="12" class="mb20">
|
||||
<el-form-item :label="t('flowApplication.flowKey')" prop="flowKey">
|
||||
<el-input v-model="form.flowKey" :placeholder="t('flowApplication.inputFlowKeyTip')" clearable/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="12" class="mb20">
|
||||
<el-form-item :label="t('flowApplication.formName')" prop="formName">
|
||||
<el-input v-model="form.formName" :placeholder="t('flowApplication.inputFormNameTip')" maxlength="20" show-word-limit clearable/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="12" class="mb20">
|
||||
<el-form-item :label="t('flowApplication.icon')" prop="icon">
|
||||
<IconSelector :placeholder="$t('flowApplication.inputIconTip')" v-model="form.icon" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="12" class="mb20">
|
||||
<el-form-item :label="t('flowApplication.groupName')" prop="groupName">
|
||||
<el-select v-model="form.groupName" :placeholder="t('flowApplication.inputGroupNameTip')" style="width: 80%!important;"
|
||||
clearable filterable allowCreate defaultFirstOption>
|
||||
<el-option v-for="(item, index) in dicData.groupName" :key="index" :label="item.groupName" :value="item.groupName"></el-option>
|
||||
</el-select>
|
||||
|
||||
<el-button
|
||||
type="primary" size="small" round style="margin-left: 10px"
|
||||
@click="handleAddGroupName"
|
||||
>新增分组
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="12" class="mb20">
|
||||
<el-form-item :label="t('flowApplication.type')" prop="type">
|
||||
<el-select v-model="form.type" :placeholder="t('flowApplication.inputTypeTip')" clearable filterable>
|
||||
<el-option v-for="(item, index) in DIC_PROP.FORM_TYPE" :key="index" :label="item.label" :value="item.value"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="12" class="mb20" v-if="form.type === DIC_PROP.FORM_TYPE[1].value">
|
||||
<el-form-item :label="t('tabsOption.path')" prop="path">
|
||||
<el-input v-model="form.path" :placeholder="t('tabsOption.inputPathTip')" clearable>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="12" class="mb20">
|
||||
<el-form-item :label="t('flowApplication.tableName')" prop="tableName">
|
||||
<el-tooltip content="若自行调用接口保存表单数据,则此处需为空" placement="top">
|
||||
<el-select v-model="form.tableName" :placeholder="t('flowApplication.inputTableNameTip')" clearable filterable>
|
||||
<el-option v-for="(item, index) in dicData.tableName" :key="index" :label="item.tableName" :value="item.tableName"></el-option>
|
||||
</el-select>
|
||||
</el-tooltip>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="12" class="mb20">
|
||||
<el-form-item :label="t('flowApplication.permission')" prop="permission">
|
||||
<el-tooltip content="请输入角色名称进行模糊搜索,默认所有人都可以发起该流程" placement="top">
|
||||
<el-select v-model="form.permission" :placeholder="t('flowApplication.inputPermissionTip')" clearable filterable multiple
|
||||
remote :remote-method="remoteMethod" :reserve-keyword="false">
|
||||
<el-option v-for="(item, index) in dicData.permission" :key="index" :label="item.roleName" :value="item.roleId"></el-option>
|
||||
</el-select>
|
||||
</el-tooltip>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="12" class="mb20">
|
||||
<el-form-item :label="t('flowApplication.subTableName')" prop="subTableName">
|
||||
<el-tooltip content="当存在多个关联子表时,多个子表名称名顺序与《关联子表属性》顺序一一对应" placement="top">
|
||||
<el-select v-model="form.subTableName" :placeholder="t('flowApplication.inputSubTableNameTip')"
|
||||
@change="changeSubTableName" clearable filterable multiple>
|
||||
<el-option v-for="(item, index) in dicData.tableName" :key="index" :label="item.tableName" :value="item.tableName"></el-option>
|
||||
</el-select>
|
||||
</el-tooltip>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="12" class="mb20" v-if="!validateNull(form.subTableName)">
|
||||
<el-tooltip content="当关联子表名称存在时,可配置子表中关联【主表主键】的列名,如main_id" placement="top">
|
||||
<el-form-item :label="t('flowApplication.subMainField')" prop="subMainField">
|
||||
<el-input v-model="form.subMainField" :placeholder="t('flowApplication.inputSubMainFieldTip')"></el-input>
|
||||
</el-form-item>
|
||||
</el-tooltip>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="12" class="mb20" v-if="!validateNull(form.subTableName)">
|
||||
<el-tooltip content="当关联子表名称存在时,可配置主表中关联【子表集合数据】的属性名,如subFormList。多个子表属性名以逗号分割,与《关联子表名称》顺序一一对应" placement="top">
|
||||
<el-form-item :label="t('flowApplication.mainSubProp')" prop="mainSubProp">
|
||||
<el-input v-model="form.mainSubProp" :placeholder="t('flowApplication.inputMainSubPropTip')"></el-input>
|
||||
</el-form-item>
|
||||
</el-tooltip>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="12" class="mb20">
|
||||
<el-form-item :label="t('flowApplication.remark')" prop="remark">
|
||||
<el-input v-model="form.remark" type="textarea" :placeholder="t('flowApplication.inputRemarkTip')"/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="12" class="mb20">
|
||||
<el-form-item :label="t('flowApplication.sort')" prop="sort">
|
||||
<el-input-number :min="1" :max="1000" v-model="form.sort" :placeholder="t('flowApplication.inputSortTip')"></el-input-number>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="12" class="mb20">
|
||||
<el-form-item :label="t('tabsOption.isActive')" prop="isActive">
|
||||
<el-radio-group v-model="form.isActive">
|
||||
<el-radio v-for="(item, index) in DIC_PROP.YES_OR_NO" :key="index" :label="item.value" >
|
||||
{{ getLabelByLanguage(item) }}
|
||||
</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="12" class="mb20">
|
||||
<el-form-item :label="t('tabsOption.isAutoAudit')" prop="isAutoAudit">
|
||||
<el-radio-group v-model="form.isAutoAudit">
|
||||
<el-radio v-for="(item, index) in DIC_PROP.YES_OR_NO" :key="index" :label="item.value" >
|
||||
{{ getLabelByLanguage(item) }}
|
||||
</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="12" class="mb20">
|
||||
<el-form-item :label="t('flowApplication.status')" prop="status">
|
||||
<el-radio-group v-model="form.status">
|
||||
<el-radio v-for="(item, index) in DIC_PROP.TEMP_STATUS" :key="index" :label="item.value" >
|
||||
{{ getLabelByLanguage(item) }}
|
||||
</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
</el-row>
|
||||
</el-form>
|
||||
<template #footer v-if="operType !== 'view'">
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="visible = false">{{ $t('common.cancelButtonText') }}</el-button>
|
||||
<el-button type="primary" @click="onSubmit" :disabled="loading">{{ $t('common.confirmButtonText') }}</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="FlowApplicationDialog">
|
||||
|
||||
import {useMessage, useMessageBox} from "/@/hooks/message";
|
||||
import { getObj, addObj, putObj } from '/@/api/order/flow-application'
|
||||
import { useI18n } from "vue-i18n"
|
||||
import {rule, validateNull} from '/@/utils/validate';
|
||||
import {
|
||||
onFormLoadedUrl,
|
||||
onLoadDicUrl,
|
||||
onUpdateDicData,
|
||||
remoteMethodByKey
|
||||
} from "/@/flow/components/convert-name/convert";
|
||||
import {notifyLeft} from "/@/flow";
|
||||
import {setPropsNull} from "../../support/common";
|
||||
import {DIC_PROP} from "../../support/dict-prop";
|
||||
import {validateFormTypeSave, vueKey} from "/@/api/order/order-key-vue";
|
||||
const emit = defineEmits(['refresh']);
|
||||
|
||||
const { t } = useI18n();
|
||||
// 引入组件
|
||||
const IconSelector = defineAsyncComponent(() => import('/@/components/IconSelector/index.vue'));
|
||||
// 定义变量内容
|
||||
const dataFormRef = ref();
|
||||
const visible = ref(false);
|
||||
const loading = ref(false);
|
||||
const operType = ref(false);
|
||||
const title = ref('');
|
||||
// 定义字典
|
||||
const dicData = reactive({});
|
||||
const onLoad = onLoadDicUrl({key: "groupName"}, {key: "tableName"});
|
||||
const onUpdate = onUpdateDicData({key: "groupName"});
|
||||
const onFormLoaded = onFormLoadedUrl({key: "permission"});
|
||||
onMounted(() => {
|
||||
onLoad(dicData);
|
||||
});
|
||||
// 提交表单数据
|
||||
const form = reactive({
|
||||
flowKey: '',
|
||||
icon: '',
|
||||
formName: '',
|
||||
groupName: '',
|
||||
tableName: '',
|
||||
permission: '',
|
||||
remark: '',
|
||||
status: '-1',
|
||||
isActive: '1',
|
||||
isAutoAudit: '0',
|
||||
sort: 1,
|
||||
});
|
||||
|
||||
// 定义校验规则
|
||||
const dataRules = ref({
|
||||
flowKey: [{required: true, message: '流程KEY不能为空', trigger: 'blur'}],
|
||||
icon: [{required: true, message: '表单图标不能为空', trigger: 'blur'}],
|
||||
formName: [{required: true, message: '表单名称不能为空', trigger: 'blur'}],
|
||||
groupName: [{required: true, message: '分组名称不能为空', trigger: 'blur'}],
|
||||
isActive: [{required: true, message: '默认展示不能为空', trigger: 'blur'}],
|
||||
isAutoAudit: [{required: true, message: '提交时自动审批不能为空', trigger: 'blur'}],
|
||||
sort: [{required: true, message: '排序值不能为空', trigger: 'blur'}],
|
||||
type: [{required: true, message: '表单类型不能为空', trigger: 'blur'}],
|
||||
status: [{required: true, message: '状态不能为空', trigger: 'blur'}],
|
||||
})
|
||||
|
||||
// 打开弹窗
|
||||
const openDialog = (type: string, id: string) => {
|
||||
visible.value = true
|
||||
operType.value = type;
|
||||
setPropsNull(form, 'id', 'flowKey', 'defFlowId')
|
||||
|
||||
if (type === 'add') {
|
||||
title.value = t('common.addBtn');
|
||||
} else if (type === 'edit') {
|
||||
title.value = t('common.editBtn');
|
||||
} else if (type === 'view') {
|
||||
title.value = t('common.viewBtn');
|
||||
}
|
||||
|
||||
// 重置表单数据
|
||||
nextTick(() => {
|
||||
dataFormRef.value?.resetFields();
|
||||
// 获取FlowApplication信息
|
||||
if (id) {
|
||||
form.id = id
|
||||
getFlowApplicationData(id)
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
function handleAddGroupName() {
|
||||
useMessageBox().prompt("请输入新的分组名称")
|
||||
.then(({ value }) => {
|
||||
form.groupName = value
|
||||
onUpdate(dicData, form);
|
||||
})
|
||||
}
|
||||
|
||||
function changeSubTableName() {
|
||||
form.subMainField = null
|
||||
form.mainSubProp = null
|
||||
}
|
||||
|
||||
function remoteMethod(query: string) {
|
||||
remoteMethodByKey(query, onLoad, dicData, 'roleName', "permission")
|
||||
}
|
||||
|
||||
// 提交
|
||||
const onSubmit = async () => {
|
||||
const valid = await dataFormRef.value.validate().catch(() => {});
|
||||
if (!valid) return false;
|
||||
|
||||
if (form.type === DIC_PROP.FORM_TYPE[1].value) {
|
||||
if (!form.path) {
|
||||
notifyLeft('当前表单类型为系统表单,【PC端路径】不能为空', 'warning')
|
||||
return false
|
||||
}
|
||||
}
|
||||
if(!validateNull(form.subTableName)) {
|
||||
if (!form.subMainField || !form.mainSubProp) {
|
||||
notifyLeft('请填写 关联主表列名 或者 关联子表属性', 'warning')
|
||||
return false
|
||||
}
|
||||
}
|
||||
let isDesign = form.type !== DIC_PROP.FORM_TYPE[1].value;
|
||||
if (isDesign && !form.formInfo) {
|
||||
notifyLeft('当前表单类型为设计表单,请先完善第二项设计的表单内容', 'warning')
|
||||
return false
|
||||
}
|
||||
validateFormTypeSave(form)
|
||||
try {
|
||||
loading.value = true;
|
||||
form.id ? await putObj(form) : await addObj(form);
|
||||
useMessage().success(t(form.id ? 'common.editSuccessText' : 'common.addSuccessText'));
|
||||
visible.value = false;
|
||||
emit('refresh');
|
||||
} catch (err: any) {
|
||||
useMessage().error(err.msg);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 初始化表单数据
|
||||
const getFlowApplicationData = (id: string) => {
|
||||
// 获取数据
|
||||
loading.value = true
|
||||
getObj(id).then((res: any) => {
|
||||
Object.assign(form, res.data)
|
||||
onFormLoaded(dicData, form);
|
||||
}).finally(() => {
|
||||
loading.value = false
|
||||
})
|
||||
};
|
||||
|
||||
// 暴露变量
|
||||
defineExpose({
|
||||
openDialog
|
||||
});
|
||||
</script>
|
||||
177
src/flow/components/onebtn-design/header.vue
Normal file
177
src/flow/components/onebtn-design/header.vue
Normal file
@@ -0,0 +1,177 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="header">
|
||||
<el-tabs v-model="form.active" @tab-click="methods.activeMenu">
|
||||
<el-tab-pane label="① 表单设置" name="formSetting"></el-tab-pane>
|
||||
<el-tab-pane label="② 表单设计" name="formDesign"></el-tab-pane>
|
||||
<el-tab-pane label="③ 流程设计" name="flowDesign"></el-tab-pane>
|
||||
</el-tabs>
|
||||
|
||||
<div class="btn-publish" v-if="form.active !== 'flowDesign'">
|
||||
<el-button icon="CaretRight" type="primary" @click="methods.activeMenu({paneName: form.active === 'formSetting' ? 'formDesign' : 'flowDesign'})">下一步
|
||||
</el-button>
|
||||
</div>
|
||||
|
||||
<div class="btn-publish" v-if="form.active === 'flowDesign'">
|
||||
<template v-if="!validateRunFlow(props)">
|
||||
<el-button icon="Upload" type="primary" @click="methods.publish(null, true)">升版本
|
||||
</el-button>
|
||||
<el-button icon="CirclePlus" type="primary" @click="methods.publish('-1')">暂存
|
||||
</el-button>
|
||||
</template>
|
||||
<el-button icon="Promotion" type="primary" @click="methods.publish('1')">发布
|
||||
</el-button>
|
||||
</div>
|
||||
<div class="btn-back">
|
||||
<el-button @click="methods.exitFlowForm" icon="Back" circle></el-button>
|
||||
<span style="margin-left: 20px;">
|
||||
<i :class="props.currFlowForm.icon"></i>
|
||||
<span>{{props.currFlowForm.formName }} V{{ props.currFlowForm.version }}
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="FlowDesignHeader">
|
||||
import {useI18n} from "vue-i18n"
|
||||
import {useMessageBox} from "/@/hooks/message";
|
||||
import {notifyLeft} from "/@/flow";
|
||||
import {validateNull} from "/@/utils/validate";
|
||||
|
||||
const {t} = useI18n();
|
||||
const {proxy} = getCurrentInstance();
|
||||
const $emit = defineEmits(["handleDesignFlow", "syncCurrFlowForm", "publish"]);
|
||||
const props = defineProps({
|
||||
currFlowForm: {
|
||||
type: Object,
|
||||
default: null,
|
||||
}
|
||||
});
|
||||
const form = reactive({
|
||||
active: "formSetting",
|
||||
interval: {},
|
||||
intervalTime: 2
|
||||
});
|
||||
|
||||
const methods = {
|
||||
publish(status, version) {
|
||||
$emit('publish', form.active, () => {
|
||||
props.currFlowForm[form.active] = true
|
||||
}, status, version)
|
||||
},
|
||||
async exitFlowForm() {
|
||||
let text = ''
|
||||
// 判断是否都保存
|
||||
if (props.currFlowForm.formSetting !== true) text += '【表单设置】,'
|
||||
if (props.currFlowForm.formDesign !== true) text += '【表单设计】,'
|
||||
if (props.currFlowForm.flowDesign !== true) text += '【流程设计】'
|
||||
if (!text) {
|
||||
$emit("handleDesignFlow", false);
|
||||
return
|
||||
}
|
||||
try {
|
||||
await useMessageBox().confirm(text + '未保存,是否继续退出?');
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
$emit("handleDesignFlow", false);
|
||||
},
|
||||
activeMenu(tab) {
|
||||
let menu = tab.paneName
|
||||
let active = form.active;
|
||||
// 判断是否在操作中
|
||||
if (props.currFlowForm.flowDesigning === true) {
|
||||
if (!validateNull(form.interval)) clearInterval(form.interval);
|
||||
let intervalTime = 0;
|
||||
form.interval = setInterval(() => {
|
||||
methods.validateFlowDesigning(menu, active, intervalTime)
|
||||
intervalTime++;
|
||||
}, 1000);
|
||||
return true
|
||||
}
|
||||
methods.doActiveMenu(menu, active);
|
||||
},
|
||||
validateFlowDesigning(menu, active, intervalTime = 0){
|
||||
notifyLeft('流程设计操作中,请稍后', 'warning')
|
||||
if (intervalTime >= form.intervalTime) {
|
||||
props.currFlowForm.flowDesigning = false
|
||||
}
|
||||
if (props.currFlowForm.flowDesigning === true) return
|
||||
methods.doActiveMenu(menu, active)
|
||||
clearInterval(form.interval);
|
||||
},
|
||||
doActiveMenu(menu, active){
|
||||
$emit("syncCurrFlowForm", menu, active, (preSave)=>{
|
||||
if (!preSave) menu = active
|
||||
props.currFlowForm[menu] = false;
|
||||
form.active = menu;
|
||||
props.currFlowForm.active = menu;
|
||||
});
|
||||
},
|
||||
// 关闭提示
|
||||
listenPage() {
|
||||
window.onbeforeunload = function (e) {
|
||||
e = e || window.event;
|
||||
if (e) {
|
||||
e.returnValue = "关闭提示";
|
||||
}
|
||||
return "关闭提示";
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
methods.listenPage();
|
||||
});
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.header {
|
||||
min-width: 980px;
|
||||
|
||||
.el-tabs {
|
||||
position: absolute;
|
||||
top: 15px;
|
||||
z-index: 999;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.btn-publish {
|
||||
position: absolute;
|
||||
top: 20px;
|
||||
z-index: 1000;
|
||||
right: 40px !important;
|
||||
|
||||
i {
|
||||
margin-right: 6px;
|
||||
}
|
||||
|
||||
button {
|
||||
width: 74px;
|
||||
height: 28px;
|
||||
border-radius: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-back {
|
||||
position: absolute;
|
||||
top: 20px;
|
||||
z-index: 1000;
|
||||
left: 20px !important;
|
||||
font-size: 18px;
|
||||
|
||||
i {
|
||||
margin-right: 6px;
|
||||
}
|
||||
|
||||
button {
|
||||
width: 44px;
|
||||
height: 28px;
|
||||
border-radius: 15px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
377
src/flow/components/onebtn-design/setting.vue
Normal file
377
src/flow/components/onebtn-design/setting.vue
Normal file
@@ -0,0 +1,377 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-form ref="dataFormRef" :model="props.currFlowForm" :rules="dataRules" label-width="150px"
|
||||
v-loading="loading"
|
||||
label-position="left"
|
||||
:disabled="operType === 'view'">
|
||||
<el-row :gutter="24">
|
||||
<el-col :span="12" class="mb20" :offset="6">
|
||||
<el-form-item :label="t('flowApplication.flowKey')" prop="flowKey">
|
||||
<el-input v-model="props.currFlowForm.flowKey" clearable
|
||||
:placeholder="t('flowApplication.inputFlowKeyTip')"/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="12" class="mb20" :offset="6">
|
||||
<el-form-item :label="t('flowApplication.formName')" prop="formName">
|
||||
<el-input v-model="props.currFlowForm.formName" maxlength="20" show-word-limit clearable
|
||||
:placeholder="t('flowApplication.inputFormNameTip')"/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="12" class="mb20" :offset="6">
|
||||
<el-form-item :label="t('flowApplication.icon')" prop="icon">
|
||||
<IconSelector :placeholder="$t('flowApplication.inputIconTip')"
|
||||
v-model="props.currFlowForm.icon"/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="12" class="mb20" :offset="6">
|
||||
<el-form-item :label="t('flowApplication.groupName')" prop="groupName">
|
||||
<el-select v-model="props.currFlowForm.groupName" style="width: 83%!important;"
|
||||
:placeholder="t('flowApplication.inputGroupNameTip')"
|
||||
clearable filterable allowCreate defaultFirstOption>
|
||||
<el-option v-for="(item, index) in dicData.groupName" :key="index" :label="item.groupName"
|
||||
:value="item.groupName"></el-option>
|
||||
</el-select>
|
||||
|
||||
<el-button
|
||||
type="primary" size="small" round style="margin-left: 10px"
|
||||
@click="handleAddGroupName"
|
||||
>新增分组
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="12" class="mb20" :offset="6">
|
||||
<el-form-item :label="t('flowApplication.type')" prop="type">
|
||||
<el-select v-model="props.currFlowForm.type" :disabled="!!validateRunFlow(props)"
|
||||
:placeholder="t('flowApplication.inputTypeTip')" clearable filterable>
|
||||
<el-option v-for="(item, index) in DIC_PROP.FORM_TYPE" :key="index" :label="item.label" :value="item.value"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="12" class="mb20" :offset="6" v-if="props.currFlowForm.type === DIC_PROP.FORM_TYPE[1].value">
|
||||
<el-tooltip content="当表单设计的页面为空时,可配置本地自定义主表单Vue页面路径" placement="top">
|
||||
<el-form-item :label="t('tabsOption.path')" prop="path">
|
||||
<el-input v-model="props.currFlowForm.path" :placeholder="t('tabsOption.inputPathTip')" clearable>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
</el-tooltip>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="12" class="mb20" :offset="6">
|
||||
<el-form-item :label="t('flowApplication.tableName')" prop="tableName">
|
||||
<el-tooltip content="若自行调用接口保存表单数据,则此处需为空" placement="top">
|
||||
<el-select v-model="props.currFlowForm.tableName" @clear="props.currFlowForm.tableName = null"
|
||||
:placeholder="t('flowApplication.inputTableNameTip')" style="width: 83%!important;"
|
||||
clearable filterable>
|
||||
<el-option v-for="(item, index) in dicData.tableName" :key="index" :label="item.tableName"
|
||||
:value="item.tableName"></el-option>
|
||||
</el-select>
|
||||
</el-tooltip>
|
||||
|
||||
<el-button
|
||||
type="primary" size="small" round style="margin-left: 10px"
|
||||
@click="addUpdateTableName()"
|
||||
>{{ props.currFlowForm.tableName ? '修改表' : '新增表'}}
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="12" class="mb20" :offset="6" v-if="!validateRunFlow(props)">
|
||||
<el-form-item :label="t('flowApplication.permission')" prop="permission">
|
||||
<el-tooltip content="请输入角色名称进行模糊搜索,默认所有人都可以发起该流程" placement="top">
|
||||
<el-select v-model="props.currFlowForm.permission" :placeholder="t('flowApplication.inputPermissionTip')"
|
||||
clearable filterable multiple
|
||||
remote :remote-method="remoteMethod" :reserve-keyword="false">
|
||||
<el-option v-for="(item, index) in dicData.permission" :key="index" :label="item.roleName"
|
||||
:value="item.roleId"></el-option>
|
||||
</el-select>
|
||||
</el-tooltip>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="12" class="mb20" :offset="6">
|
||||
<el-form-item :label="t('flowApplication.subTableName')" prop="subTableName">
|
||||
<el-tooltip content="当存在多个关联子表时,多个子表名称名顺序与《关联子表属性》顺序一一对应" placement="top">
|
||||
<el-select v-model="props.currFlowForm.subTableName" :placeholder="t('flowApplication.inputSubTableNameTip')" style="width: 83%!important;"
|
||||
@change="changeSubTableName" clearable filterable multiple>
|
||||
|
||||
<el-option v-for="(item, index) in dicData.tableName" :key="index"
|
||||
:label="item.tableName" :value="item.tableName">
|
||||
<span> {{ item.tableName }} </span>
|
||||
<span style="float: right; color: #409EFF" @click="addUpdateTableName(item.tableName, true)"> 点击修改 </span>
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-tooltip>
|
||||
|
||||
<el-button
|
||||
type="primary" size="small" round style="margin-left: 10px"
|
||||
@click="addUpdateTableName(null, true)"
|
||||
>新增子表
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="12" class="mb20" :offset="6" v-if="!validateNull(props.currFlowForm.subTableName)">
|
||||
<el-tooltip content="当关联子表名称存在时,可配置子表中关联【主表主键】的列名,如main_id" placement="top">
|
||||
<el-form-item :label="t('flowApplication.subMainField')" prop="subMainField">
|
||||
<el-input v-model="props.currFlowForm.subMainField" :placeholder="t('flowApplication.inputSubMainFieldTip')"></el-input>
|
||||
</el-form-item>
|
||||
</el-tooltip>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="12" class="mb20" :offset="6" v-if="!validateNull(props.currFlowForm.subTableName)">
|
||||
<el-tooltip content="当关联子表名称存在时,可配置主表中关联【子表集合数据】的属性名,如subFormList。多个子表属性名以逗号分割,注意与《关联子表名称》顺序一一对应" placement="top">
|
||||
<el-form-item :label="t('flowApplication.mainSubProp')" prop="mainSubProp">
|
||||
<el-input v-model="props.currFlowForm.mainSubProp" :placeholder="t('flowApplication.inputMainSubPropTip')"></el-input>
|
||||
</el-form-item>
|
||||
</el-tooltip>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="12" class="mb20" :offset="6">
|
||||
<el-form-item :label="t('flowApplication.remark')" prop="remark">
|
||||
<el-input v-model="props.currFlowForm.remark" type="textarea" :placeholder="t('flowApplication.inputRemarkTip')"/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="12" class="mb20" :offset="6">
|
||||
<el-form-item :label="t('flowApplication.sort')" prop="sort">
|
||||
<el-input-number :min="1" :max="1000" v-model="props.currFlowForm.sort"
|
||||
:placeholder="t('flowApplication.inputSortTip')"></el-input-number>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="12" class="mb20" v-if="!validateRunFlow(props)" :offset="6">
|
||||
<el-tooltip content="在审批时默认展示的第1个页面。当存在多个默认展示页面时,优先展示排序值最小的" placement="top">
|
||||
<el-form-item :label="t('tabsOption.isActive')" prop="isActive">
|
||||
<el-radio-group v-model="props.currFlowForm.isActive">
|
||||
<el-radio v-for="(item, index) in DIC_PROP.YES_OR_NO" :key="index" :label="item.value" >
|
||||
{{ getLabelByLanguage(item) }}
|
||||
</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-tooltip>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="12" class="mb20" v-if="!validateRunFlow(props)" :offset="6">
|
||||
<el-tooltip content="在审批时不会显示审批按钮,在页面点提交按钮会自动流转到下一步" placement="top">
|
||||
<el-form-item :label="t('tabsOption.isAutoAudit')" prop="isAutoAudit">
|
||||
<el-radio-group v-model="props.currFlowForm.isAutoAudit">
|
||||
<el-radio v-for="(item, index) in DIC_PROP.YES_OR_NO" :key="index" :label="item.value" >
|
||||
{{ getLabelByLanguage(item) }}
|
||||
</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-tooltip>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="12" class="mb20" :offset="6">
|
||||
<el-form-item :label="t('flowApplication.status')" prop="status">
|
||||
<el-radio-group v-model="props.currFlowForm.status">
|
||||
<el-radio v-for="(item, index) in DIC_PROP.TEMP_STATUS" :key="index" :label="item.value">
|
||||
{{ getLabelByLanguage(item) }}
|
||||
</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
</el-row>
|
||||
</el-form>
|
||||
|
||||
<!-- 编辑、新增 -->
|
||||
<create-table ref="createTableRef" @refreshDone="createTableDone" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="FlowApplicationForm">
|
||||
import {useMessage, useMessageBox} from "/@/hooks/message";
|
||||
import * as runApplication from "/@/api/order/run-application";
|
||||
import * as flowApplication from "/@/api/order/flow-application";
|
||||
import other from '/@/utils/other';
|
||||
import {notifyLeft, stringifyWithFunctions, validateRunFlow} from "/@/flow";
|
||||
import {useI18n} from "vue-i18n"
|
||||
import {
|
||||
onFormLoadedUrl,
|
||||
onLoadDicUrl,
|
||||
onUpdateDicData,
|
||||
remoteMethodByKey
|
||||
} from "/@/flow/components/convert-name/convert";
|
||||
import {validateNull} from "/@/utils/validate";
|
||||
import {DIC_PROP} from "../../support/dict-prop";
|
||||
import {validateFormTypeSave, vueKey} from "/@/api/order/order-key-vue";
|
||||
import {PROP_CONST} from "../../support/prop-const";
|
||||
|
||||
const {t} = useI18n();
|
||||
const {proxy} = getCurrentInstance();
|
||||
const props = defineProps({
|
||||
currFlowForm: {
|
||||
type: Object,
|
||||
default: null,
|
||||
}
|
||||
});
|
||||
// 引入组件
|
||||
const IconSelector = defineAsyncComponent(() => import('/@/components/IconSelector/index.vue'));
|
||||
const CreateTable = defineAsyncComponent(() => import('/@/views/order/create-table/form.vue'));
|
||||
|
||||
// 定义变量内容
|
||||
const dataFormRef = ref();
|
||||
const loading = ref(false);
|
||||
const operType = ref(false);
|
||||
// 定义字典
|
||||
const dicData = reactive({});
|
||||
const onLoad = onLoadDicUrl({key: "groupName"}, {key: "tableName"});
|
||||
const onFormLoaded = onFormLoadedUrl({key: "permission"});
|
||||
const onUpdate = onUpdateDicData({key: "groupName"});
|
||||
onMounted(async () => {
|
||||
await onLoad(dicData);
|
||||
await onFormLoaded(dicData, props.currFlowForm);
|
||||
let isNoGroupName = !props.currFlowForm.groupName && dicData.groupName.length > 0;
|
||||
if (isNoGroupName) {
|
||||
props.currFlowForm.groupName = dicData.groupName[0].groupName
|
||||
}
|
||||
});
|
||||
|
||||
const data = reactive({subTableName: null, updateSubTableName: null, isSubTableName: false});
|
||||
|
||||
// 定义校验规则
|
||||
const dataRules = ref({
|
||||
flowKey: [{required: true, message: '流程KEY不能为空', trigger: 'blur'}],
|
||||
icon: [{required: true, message: '表单图标不能为空', trigger: 'blur'}],
|
||||
type: [{required: true, message: '表单类型不能为空', trigger: 'blur'}],
|
||||
formName: [{required: true, message: '表单名称不能为空', trigger: 'blur'}],
|
||||
groupName: [{required: true, message: '分组名称不能为空', trigger: 'blur'}],
|
||||
status: [{required: true, message: '状态不能为空', trigger: 'blur'}],
|
||||
})
|
||||
|
||||
function handleAddGroupName() {
|
||||
useMessageBox().prompt("请输入新的分组名称")
|
||||
.then(({ value }) => {
|
||||
props.currFlowForm.groupName = value
|
||||
onUpdate(dicData, props.currFlowForm);
|
||||
})
|
||||
}
|
||||
|
||||
function changeSubTableName() {
|
||||
if (data.isSubTableName) {
|
||||
if (data.subTableName) props.currFlowForm.subTableName = data.subTableName
|
||||
return
|
||||
}
|
||||
if (!validateNull(props.currFlowForm.subTableName)) {
|
||||
return
|
||||
}
|
||||
props.currFlowForm.subMainField = null
|
||||
props.currFlowForm.mainSubProp = null
|
||||
}
|
||||
|
||||
function addUpdateTableName(subTableName?, isSubTableName?) {
|
||||
data.isSubTableName = isSubTableName
|
||||
if (isSubTableName && subTableName) {
|
||||
data.subTableName = props.currFlowForm.subTableName
|
||||
data.updateSubTableName = subTableName
|
||||
}
|
||||
let tableName = isSubTableName ? subTableName : props.currFlowForm.tableName;
|
||||
if (tableName) {
|
||||
if (tableName === PROP_CONST.COMMON.tableName) {
|
||||
notifyLeft('当前表为系统表,请勿修改', 'warning')
|
||||
} else {
|
||||
let find = dicData.tableName.find(f => f.tableName === tableName);
|
||||
proxy.$refs.createTableRef.openDialog('edit', find.id)
|
||||
}
|
||||
} else {
|
||||
proxy.$refs.createTableRef.openDialog('add')
|
||||
}
|
||||
}
|
||||
|
||||
function createTableDone(tableName, operType, isOnSubmit) {
|
||||
if (data.isSubTableName) {
|
||||
if (isOnSubmit && operType === 'edit' && data.updateSubTableName !== tableName){
|
||||
let includes = props.currFlowForm.subTableName.includes(data.updateSubTableName);
|
||||
if (includes) {
|
||||
// 修改表名
|
||||
let findIndex = props.currFlowForm.subTableName.findIndex(f => f === data.updateSubTableName);
|
||||
props.currFlowForm.subTableName.splice(findIndex, 1, tableName)
|
||||
}
|
||||
} else if (isOnSubmit && operType === 'add'){
|
||||
props.currFlowForm.subTableName.push(tableName)
|
||||
}
|
||||
data.isSubTableName = false
|
||||
data.updateSubTableName = null
|
||||
data.subTableName = null
|
||||
} else if (isOnSubmit){
|
||||
props.currFlowForm.tableName = tableName
|
||||
}
|
||||
if (!isOnSubmit) return
|
||||
const onLoad = onLoadDicUrl({key: "tableName"});
|
||||
onLoad(dicData)
|
||||
}
|
||||
|
||||
async function validatePreSave() {
|
||||
const valid = await dataFormRef.value.validate().catch(() => {});
|
||||
if (!valid) {
|
||||
notifyLeft('请先完善表单设置内容', 'warning')
|
||||
return false;
|
||||
}
|
||||
let isSys = props.currFlowForm.type === DIC_PROP.FORM_TYPE[1].value;
|
||||
if (isSys && !props.currFlowForm.path) {
|
||||
notifyLeft('当前表单类型为系统表单,【PC端路径】不能为空', 'warning')
|
||||
return false
|
||||
}
|
||||
if(!validateNull(props.currFlowForm.subTableName)) {
|
||||
if (!props.currFlowForm.subMainField || !props.currFlowForm.mainSubProp) {
|
||||
notifyLeft('请填写 关联主表列名 或者 关联子表属性', 'warning')
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
function validateOtherSave() {
|
||||
let isDesign = props.currFlowForm.type !== DIC_PROP.FORM_TYPE[1].value;
|
||||
if (isDesign && !props.currFlowForm.formInfo) {
|
||||
notifyLeft('当前表单类型为设计表单,请先完善第二项设计的表单内容', 'warning')
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
function remoteMethod(query: string) {
|
||||
remoteMethodByKey(query, onLoad, dicData, 'roleName', "permission")
|
||||
}
|
||||
|
||||
const handleSubmit = async (callback, status) => {
|
||||
if (status) {
|
||||
props.currFlowForm.status = status
|
||||
}
|
||||
let preSave = await validatePreSave();
|
||||
if (!preSave) return
|
||||
if (!validateOtherSave()) return
|
||||
let formJson = other.deepClone(props.currFlowForm)
|
||||
formJson.formInfo = stringifyWithFunctions(formJson.formInfo)
|
||||
|
||||
validateFormTypeSave(formJson)
|
||||
try {
|
||||
loading.value = true;
|
||||
// 判断是否为低代码
|
||||
if (validateRunFlow(props) && props.currFlowForm.tableName) {
|
||||
await runApplication.putObjNoStatus(formJson)
|
||||
} else {
|
||||
await flowApplication.addObj(formJson);
|
||||
}
|
||||
props.currFlowForm.formSetting = true
|
||||
notifyLeft('当前表单设置保存成功')
|
||||
if (callback) callback()
|
||||
} catch (err: any) {
|
||||
useMessage().error(err);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
// 暴露变量
|
||||
defineExpose({
|
||||
handleSubmit, validatePreSave, validateOtherSave
|
||||
});
|
||||
</script>
|
||||
53
src/flow/components/sign-name/index.vue
Normal file
53
src/flow/components/sign-name/index.vue
Normal file
@@ -0,0 +1,53 @@
|
||||
<template>
|
||||
<div>
|
||||
<sign ref="signRef"
|
||||
v-model:bg-color="bgColor"
|
||||
:width="800"
|
||||
:height="150"
|
||||
:is-crop="false"
|
||||
:isClearBgColor="false"
|
||||
:line-width="3"
|
||||
:line-color="'#000000'"
|
||||
></sign>
|
||||
</div>
|
||||
<div style="margin-left: 15px">
|
||||
<el-link type="primary" @click.stop="handleReset">{{ t('jfcomment.reSign') }}</el-link>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup name="SignName">
|
||||
import {useMessage} from "/@/hooks/message";
|
||||
import {useI18n} from "vue-i18n";
|
||||
|
||||
const {t, locale} = useI18n();
|
||||
const $message = useMessage();
|
||||
const {proxy} = getCurrentInstance();
|
||||
|
||||
const Sign = defineAsyncComponent(() => import('./sign.vue'));
|
||||
|
||||
const bgColor = ref('#F6F8FA');
|
||||
let props = defineProps({
|
||||
currJob: {
|
||||
type: Object,
|
||||
default: {},
|
||||
},
|
||||
});
|
||||
|
||||
const handleReset = () => {
|
||||
proxy.$refs.signRef.reset();
|
||||
props.currJob.signName = null
|
||||
};
|
||||
|
||||
const handleGenerate = async () => {
|
||||
await proxy.$refs.signRef.generate()
|
||||
.then((res: any) => {
|
||||
props.currJob.signName = res;
|
||||
})
|
||||
.catch(() => {
|
||||
});
|
||||
};
|
||||
|
||||
defineExpose({ handleGenerate });
|
||||
</script>
|
||||
<style scoped lang="less">
|
||||
|
||||
</style>
|
||||
283
src/flow/components/sign-name/sign.vue
Normal file
283
src/flow/components/sign-name/sign.vue
Normal file
@@ -0,0 +1,283 @@
|
||||
<template>
|
||||
<canvas
|
||||
id="canvas"
|
||||
@mousedown="mouseDown"
|
||||
@mousemove="mouseMove"
|
||||
@mouseup="mouseUp"
|
||||
@touchstart="touchStart"
|
||||
@touchmove="touchMove"
|
||||
@touchend="touchEnd"
|
||||
></canvas>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
const props = defineProps({
|
||||
width: {
|
||||
type: Number,
|
||||
default: 800,
|
||||
},
|
||||
height: {
|
||||
type: Number,
|
||||
default: 300,
|
||||
},
|
||||
lineWidth: {
|
||||
type: Number,
|
||||
default: 4,
|
||||
},
|
||||
lineColor: {
|
||||
type: String,
|
||||
default: '#000000',
|
||||
},
|
||||
bgColor: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
isCrop: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
isClearBgColor: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
});
|
||||
let canvas: any = null; //document.getElementById('canvas')
|
||||
let hasDrew = false;
|
||||
let resultImg = '';
|
||||
let points: any = [];
|
||||
let canvasTxt: any = null;
|
||||
let startX = 0;
|
||||
let startY = 0;
|
||||
let isDrawing = false;
|
||||
let sratio: any = 1;
|
||||
|
||||
onBeforeMount(() => {
|
||||
window.addEventListener('resize', resizeHandler);
|
||||
});
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener('resize', resizeHandler);
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
// const canvas = this.$refs.canvas
|
||||
canvas = document.getElementById('canvas');
|
||||
canvas.height = props.height;
|
||||
canvas.width = props.width;
|
||||
canvas.style.background = myBg.value;
|
||||
resizeHandler();
|
||||
// 在画板以外松开鼠标后冻结画笔
|
||||
document.onmouseup = () => {
|
||||
isDrawing = false;
|
||||
};
|
||||
});
|
||||
|
||||
const ratio: any = computed(() => {
|
||||
return props.height / props.width;
|
||||
});
|
||||
|
||||
const stageInfo: any = computed(() => {
|
||||
return canvas.getBoundingClientRect();
|
||||
});
|
||||
const myBg = computed(() => {
|
||||
return props.bgColor ? props.bgColor : 'rgba(255, 255, 255, 0)';
|
||||
});
|
||||
|
||||
watch(myBg, (newVal) => {
|
||||
canvas.style.background = newVal;
|
||||
});
|
||||
|
||||
const resizeHandler = () => {
|
||||
canvas.style.width = props.width + 'px';
|
||||
const realw = parseFloat(window.getComputedStyle(canvas).width);
|
||||
canvas.style.height = ratio.value * realw + 'px';
|
||||
canvasTxt = canvas.getContext('2d');
|
||||
canvasTxt.scale(1 * sratio, 1 * sratio);
|
||||
sratio = realw / props.width;
|
||||
canvasTxt.scale(1 / sratio, 1 / sratio);
|
||||
};
|
||||
// pc
|
||||
const mouseDown = (e) => {
|
||||
e = e || event;
|
||||
e.preventDefault();
|
||||
isDrawing = true;
|
||||
hasDrew = true;
|
||||
let obj = {
|
||||
x: e.offsetX,
|
||||
y: e.offsetY,
|
||||
};
|
||||
drawStart(obj);
|
||||
};
|
||||
const mouseMove = (e) => {
|
||||
e = e || event;
|
||||
e.preventDefault();
|
||||
if (isDrawing) {
|
||||
let obj = {
|
||||
x: e.offsetX,
|
||||
y: e.offsetY,
|
||||
};
|
||||
drawMove(obj);
|
||||
}
|
||||
};
|
||||
const mouseUp = (e) => {
|
||||
e = e || event;
|
||||
e.preventDefault();
|
||||
let obj = {
|
||||
x: e.offsetX,
|
||||
y: e.offsetY,
|
||||
};
|
||||
drawEnd(obj);
|
||||
isDrawing = false;
|
||||
};
|
||||
// mobile
|
||||
const touchStart = (e) => {
|
||||
e = e || event;
|
||||
e.preventDefault();
|
||||
hasDrew = true;
|
||||
if (e.touches.length === 1) {
|
||||
let obj = {
|
||||
x: e.targetTouches[0].clientX - canvas.getBoundingClientRect().left,
|
||||
y: e.targetTouches[0].clientY - canvas.getBoundingClientRect().top,
|
||||
};
|
||||
drawStart(obj);
|
||||
}
|
||||
};
|
||||
const touchMove = (e) => {
|
||||
e = e || event;
|
||||
e.preventDefault();
|
||||
if (e.touches.length === 1) {
|
||||
let obj = {
|
||||
x: e.targetTouches[0].clientX - canvas.getBoundingClientRect().left,
|
||||
y: e.targetTouches[0].clientY - canvas.getBoundingClientRect().top,
|
||||
};
|
||||
drawMove(obj);
|
||||
}
|
||||
};
|
||||
const touchEnd = (e) => {
|
||||
e = e || event;
|
||||
e.preventDefault();
|
||||
if (e.touches.length === 1) {
|
||||
let obj = {
|
||||
x: e.targetTouches[0].clientX - canvas.getBoundingClientRect().left,
|
||||
y: e.targetTouches[0].clientY - canvas.getBoundingClientRect().top,
|
||||
};
|
||||
drawEnd(obj);
|
||||
}
|
||||
};
|
||||
// 绘制
|
||||
const drawStart = (obj) => {
|
||||
startX = obj.x;
|
||||
startY = obj.y;
|
||||
canvasTxt.beginPath();
|
||||
canvasTxt.moveTo(startX, startY);
|
||||
canvasTxt.lineTo(obj.x, obj.y);
|
||||
canvasTxt.lineCap = 'round';
|
||||
canvasTxt.lineJoin = 'round';
|
||||
canvasTxt.lineWidth = props.lineWidth * sratio;
|
||||
canvasTxt.stroke();
|
||||
canvasTxt.closePath();
|
||||
points.push(obj);
|
||||
};
|
||||
const drawMove = (obj) => {
|
||||
canvasTxt.beginPath();
|
||||
canvasTxt.moveTo(startX, startY);
|
||||
canvasTxt.lineTo(obj.x, obj.y);
|
||||
canvasTxt.strokeStyle = props.lineColor;
|
||||
canvasTxt.lineWidth = props.lineWidth * sratio;
|
||||
canvasTxt.lineCap = 'round';
|
||||
canvasTxt.lineJoin = 'round';
|
||||
canvasTxt.stroke();
|
||||
canvasTxt.closePath();
|
||||
startY = obj.y;
|
||||
startX = obj.x;
|
||||
points.push(obj);
|
||||
};
|
||||
const drawEnd = (obj) => {
|
||||
canvasTxt.beginPath();
|
||||
canvasTxt.moveTo(startX, startY);
|
||||
canvasTxt.lineCap = 'round';
|
||||
canvasTxt.lineJoin = 'round';
|
||||
canvasTxt.stroke();
|
||||
canvasTxt.closePath();
|
||||
points.push(obj);
|
||||
points.push({ x: -1, y: -1 });
|
||||
};
|
||||
// 操作
|
||||
const generate = () => {
|
||||
const pm = new Promise((resolve, reject) => {
|
||||
if (!hasDrew) {
|
||||
reject(`Warning: Not Signned!`);
|
||||
return;
|
||||
}
|
||||
var resImgData = canvasTxt.getImageData(0, 0, canvas.width, canvas.height);
|
||||
canvasTxt.globalCompositeOperation = 'destination-over';
|
||||
canvasTxt.fillStyle = myBg.value;
|
||||
canvasTxt.fillRect(0, 0, canvas.width, canvas.height);
|
||||
resultImg = canvas.toDataURL();
|
||||
var resultImg = resultImg;
|
||||
canvasTxt.clearRect(0, 0, canvas.width, canvas.height);
|
||||
canvasTxt.putImageData(resImgData, 0, 0);
|
||||
canvasTxt.globalCompositeOperation = 'source-over';
|
||||
if (props.isCrop) {
|
||||
const crop_area = getCropArea(resImgData.data);
|
||||
var crop_canvas: any = document.createElement('canvas');
|
||||
const crop_ctx: any = crop_canvas.getContext('2d');
|
||||
crop_canvas.width = crop_area[2] - crop_area[0];
|
||||
crop_canvas.height = crop_area[3] - crop_area[1];
|
||||
const crop_imgData = canvasTxt.getImageData(...crop_area);
|
||||
crop_ctx.globalCompositeOperation = 'destination-over';
|
||||
crop_ctx.putImageData(crop_imgData, 0, 0);
|
||||
crop_ctx.fillStyle = myBg.value;
|
||||
crop_ctx.fillRect(0, 0, crop_canvas.width, crop_canvas.height);
|
||||
resultImg = crop_canvas.toDataURL();
|
||||
crop_canvas = null;
|
||||
}
|
||||
resolve(resultImg);
|
||||
});
|
||||
return pm;
|
||||
};
|
||||
|
||||
const emits = defineEmits(['update:bgColor']);
|
||||
const reset = () => {
|
||||
canvasTxt.clearRect(0, 0, canvas.width, canvas.height);
|
||||
if (props.isClearBgColor) {
|
||||
// this.$emit('update:bgColor', '')
|
||||
emits('update:bgColor', '');
|
||||
canvas.style.background = 'rgba(255, 255, 255, 0)';
|
||||
}
|
||||
points = [];
|
||||
hasDrew = false;
|
||||
resultImg = '';
|
||||
};
|
||||
const getCropArea = (imgData) => {
|
||||
var topX = canvas.width;
|
||||
var btmX = 0;
|
||||
var topY = canvas.height;
|
||||
var btnY = 0;
|
||||
for (var i = 0; i < canvas.width; i++) {
|
||||
for (var j = 0; j < canvas.height; j++) {
|
||||
var pos = (i + canvas.width * j) * 4;
|
||||
if (imgData[pos] > 0 || imgData[pos + 1] > 0 || imgData[pos + 2] || imgData[pos + 3] > 0) {
|
||||
btnY = Math.max(j, btnY);
|
||||
btmX = Math.max(i, btmX);
|
||||
topY = Math.min(j, topY);
|
||||
topX = Math.min(i, topX);
|
||||
}
|
||||
}
|
||||
}
|
||||
topX++;
|
||||
btmX++;
|
||||
topY++;
|
||||
btnY++;
|
||||
const data = [topX, topY, btmX, btnY];
|
||||
return data;
|
||||
};
|
||||
|
||||
defineExpose({ reset, generate });
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
canvas {
|
||||
max-width: 100%;
|
||||
display: block;
|
||||
}
|
||||
</style>
|
||||
322
src/flow/components/sign/index.vue
Normal file
322
src/flow/components/sign/index.vue
Normal file
@@ -0,0 +1,322 @@
|
||||
<template>
|
||||
<div class="sign-wrapper">
|
||||
<div v-show="!modelValue" class="sign-container">
|
||||
<canvas
|
||||
ref="canvasRef"
|
||||
@mousedown="handleMouseDown"
|
||||
@mousemove="handleMouseMove"
|
||||
@mouseup="handleMouseUp"
|
||||
@touchstart="handleTouchStart"
|
||||
@touchmove="handleTouchMove"
|
||||
@touchend="handleTouchEnd"
|
||||
></canvas>
|
||||
</div>
|
||||
<div v-if="!props.disabled && !modelValue" class="sign-controls">
|
||||
<el-space>
|
||||
<el-button type="primary" @click="handleGenerate" size="small">
|
||||
<el-icon><Check /></el-icon>
|
||||
确认
|
||||
</el-button>
|
||||
<el-button @click="handleReset" size="small">
|
||||
<el-icon><Refresh /></el-icon>
|
||||
清空
|
||||
</el-button>
|
||||
<div>
|
||||
<el-color-picker v-model="currentLineColor" size="small" />
|
||||
</div>
|
||||
</el-space>
|
||||
</div>
|
||||
<div v-if="modelValue" class="flex flex-col items-center sign-preview">
|
||||
<el-image :src="modelValue" fit="contain" />
|
||||
<div class="mt-2" v-if="!props.disabled">
|
||||
<el-button @click="handleReset" size="small">
|
||||
<el-icon><Refresh /></el-icon>
|
||||
重签
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, onMounted, onBeforeUnmount, computed, watch } from 'vue';
|
||||
import { Check, Refresh } from '@element-plus/icons-vue';
|
||||
import { ElMessage } from 'element-plus';
|
||||
import type { Point } from './types';
|
||||
|
||||
interface Props {
|
||||
width?: number;
|
||||
height?: number;
|
||||
lineWidth?: number;
|
||||
lineColor?: string;
|
||||
bgColor?: string;
|
||||
isCrop?: boolean;
|
||||
isClearBgColor?: boolean;
|
||||
modelValue?: string;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
const props = defineProps({
|
||||
width: {
|
||||
type: Number,
|
||||
default: 300,
|
||||
},
|
||||
height: {
|
||||
type: Number,
|
||||
default: 150,
|
||||
},
|
||||
lineWidth: {
|
||||
type: Number,
|
||||
default: 2,
|
||||
},
|
||||
lineColor: {
|
||||
type: String,
|
||||
default: '#000000',
|
||||
},
|
||||
bgColor: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
isCrop: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
isClearBgColor: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
modelValue: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
});
|
||||
|
||||
const emit = defineEmits(['update:modelValue']);
|
||||
|
||||
const canvasRef = ref<HTMLCanvasElement | null>(null);
|
||||
const ctx = ref<CanvasRenderingContext2D | null>(null);
|
||||
const isDrawing = ref(false);
|
||||
const hasDrew = ref(false);
|
||||
const currentLineColor = ref(props.lineColor);
|
||||
const points = ref<Point[]>([]);
|
||||
const startPoint = ref<Point>({ x: 0, y: 0 });
|
||||
const scaleRatio = ref(1);
|
||||
|
||||
const backgroundColor = computed(() => props.bgColor || 'rgba(255, 255, 255, 0)');
|
||||
|
||||
function initCanvas(): void {
|
||||
const canvas = canvasRef.value;
|
||||
if (!canvas) return;
|
||||
|
||||
const container = canvas.parentElement;
|
||||
if (!container) return;
|
||||
|
||||
// Set fixed dimensions
|
||||
canvas.width = props.width;
|
||||
canvas.height = props.height;
|
||||
|
||||
// Set display dimensions
|
||||
canvas.style.width = `${props.width}px`;
|
||||
canvas.style.height = `${props.height}px`;
|
||||
canvas.style.background = backgroundColor.value;
|
||||
|
||||
ctx.value = canvas.getContext('2d');
|
||||
if (ctx.value) {
|
||||
ctx.value.strokeStyle = currentLineColor.value;
|
||||
ctx.value.lineWidth = props.lineWidth;
|
||||
ctx.value.lineCap = 'round';
|
||||
ctx.value.lineJoin = 'round';
|
||||
}
|
||||
}
|
||||
|
||||
function handleResize(): void {
|
||||
const canvas = canvasRef.value;
|
||||
if (!canvas || !ctx.value) return;
|
||||
|
||||
// Keep the same dimensions
|
||||
canvas.width = props.width;
|
||||
canvas.height = props.height;
|
||||
|
||||
ctx.value = canvas.getContext('2d');
|
||||
if (!ctx.value) return;
|
||||
|
||||
ctx.value.strokeStyle = currentLineColor.value;
|
||||
ctx.value.lineWidth = props.lineWidth;
|
||||
ctx.value.lineCap = 'round';
|
||||
ctx.value.lineJoin = 'round';
|
||||
}
|
||||
|
||||
function drawPoint(point: Point): void {
|
||||
if (!ctx.value) return;
|
||||
|
||||
ctx.value.beginPath();
|
||||
ctx.value.moveTo(startPoint.value.x, startPoint.value.y);
|
||||
ctx.value.lineTo(point.x, point.y);
|
||||
ctx.value.strokeStyle = currentLineColor.value;
|
||||
ctx.value.lineWidth = props.lineWidth * scaleRatio.value;
|
||||
ctx.value.lineCap = 'round';
|
||||
ctx.value.lineJoin = 'round';
|
||||
ctx.value.stroke();
|
||||
ctx.value.closePath();
|
||||
|
||||
startPoint.value = point;
|
||||
points.value.push(point);
|
||||
}
|
||||
|
||||
// Event handlers
|
||||
function handleMouseDown(e: MouseEvent): void {
|
||||
if (props.disabled) return;
|
||||
|
||||
e.preventDefault();
|
||||
isDrawing.value = true;
|
||||
hasDrew.value = true;
|
||||
|
||||
const point = {
|
||||
x: e.offsetX,
|
||||
y: e.offsetY,
|
||||
};
|
||||
startPoint.value = point;
|
||||
points.value.push(point);
|
||||
}
|
||||
|
||||
function handleMouseMove(e: MouseEvent): void {
|
||||
if (!isDrawing.value || props.disabled) return;
|
||||
|
||||
e.preventDefault();
|
||||
drawPoint({
|
||||
x: e.offsetX,
|
||||
y: e.offsetY,
|
||||
});
|
||||
}
|
||||
|
||||
function handleMouseUp(e: MouseEvent): void {
|
||||
if (props.disabled) return;
|
||||
|
||||
e.preventDefault();
|
||||
isDrawing.value = false;
|
||||
points.value.push({ x: -1, y: -1 }); // Mark end of stroke
|
||||
}
|
||||
|
||||
// Touch events
|
||||
function handleTouchStart(e: TouchEvent): void {
|
||||
if (props.disabled || !canvasRef.value) return;
|
||||
|
||||
e.preventDefault();
|
||||
hasDrew.value = true;
|
||||
|
||||
const touch = e.touches[0];
|
||||
const rect = canvasRef.value.getBoundingClientRect();
|
||||
const point = {
|
||||
x: touch.clientX - rect.left,
|
||||
y: touch.clientY - rect.top,
|
||||
};
|
||||
|
||||
startPoint.value = point;
|
||||
points.value.push(point);
|
||||
}
|
||||
|
||||
function handleTouchMove(e: TouchEvent): void {
|
||||
if (props.disabled || !canvasRef.value) return;
|
||||
|
||||
e.preventDefault();
|
||||
const touch = e.touches[0];
|
||||
const rect = canvasRef.value.getBoundingClientRect();
|
||||
|
||||
drawPoint({
|
||||
x: touch.clientX - rect.left,
|
||||
y: touch.clientY - rect.top,
|
||||
});
|
||||
}
|
||||
|
||||
function handleTouchEnd(e: TouchEvent): void {
|
||||
if (props.disabled) return;
|
||||
|
||||
e.preventDefault();
|
||||
points.value.push({ x: -1, y: -1 }); // Mark end of stroke
|
||||
}
|
||||
|
||||
// Actions
|
||||
async function handleGenerate(): Promise<void> {
|
||||
try {
|
||||
const result = await generate();
|
||||
emit('update:modelValue', result);
|
||||
} catch (error) {
|
||||
ElMessage.warning('请先进行签名');
|
||||
}
|
||||
}
|
||||
|
||||
function handleReset(): void {
|
||||
reset();
|
||||
emit('update:modelValue', '');
|
||||
}
|
||||
|
||||
function generate(): Promise<string> {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!hasDrew.value || !canvasRef.value || !ctx.value) {
|
||||
reject('请先进行签名');
|
||||
return;
|
||||
}
|
||||
|
||||
const canvas = canvasRef.value;
|
||||
const context = ctx.value;
|
||||
|
||||
// Save current drawing
|
||||
const imageData = context.getImageData(0, 0, canvas.width, canvas.height);
|
||||
|
||||
// Add background
|
||||
context.globalCompositeOperation = 'destination-over';
|
||||
context.fillStyle = backgroundColor.value;
|
||||
context.fillRect(0, 0, canvas.width, canvas.height);
|
||||
|
||||
// Get result
|
||||
const result = canvas.toDataURL();
|
||||
|
||||
// Restore original drawing
|
||||
context.clearRect(0, 0, canvas.width, canvas.height);
|
||||
context.putImageData(imageData, 0, 0);
|
||||
context.globalCompositeOperation = 'source-over';
|
||||
|
||||
resolve(result);
|
||||
});
|
||||
}
|
||||
|
||||
function reset(): void {
|
||||
if (!ctx.value || !canvasRef.value) return;
|
||||
|
||||
ctx.value.clearRect(0, 0, canvasRef.value.width, canvasRef.value.height);
|
||||
points.value = [];
|
||||
hasDrew.value = false;
|
||||
|
||||
if (props.isClearBgColor) {
|
||||
canvasRef.value.style.background = 'rgba(255, 255, 255, 0)';
|
||||
}
|
||||
}
|
||||
|
||||
// Lifecycle
|
||||
onMounted(() => {
|
||||
initCanvas();
|
||||
window.addEventListener('resize', handleResize);
|
||||
document.addEventListener('mouseup', () => (isDrawing.value = false));
|
||||
});
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
window.removeEventListener('resize', handleResize);
|
||||
document.removeEventListener('mouseup', () => (isDrawing.value = false));
|
||||
});
|
||||
|
||||
// Watch
|
||||
watch(
|
||||
() => backgroundColor.value,
|
||||
(newVal) => {
|
||||
if (canvasRef.value) {
|
||||
canvasRef.value.style.background = newVal;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
defineExpose({ reset, generate });
|
||||
</script>
|
||||
21
src/flow/components/sign/types.ts
Normal file
21
src/flow/components/sign/types.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
export interface SignProps {
|
||||
width?: number;
|
||||
height?: number;
|
||||
lineWidth?: number;
|
||||
lineColor?: string;
|
||||
bgColor?: string;
|
||||
isCrop?: boolean;
|
||||
isClearBgColor?: boolean;
|
||||
modelValue?: string;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
export interface Point {
|
||||
x: number;
|
||||
y: number;
|
||||
}
|
||||
|
||||
export interface SignInstance {
|
||||
reset: () => void;
|
||||
generate: () => Promise<string>;
|
||||
}
|
||||
43
src/flow/components/style/flow-drawer.scss
Normal file
43
src/flow/components/style/flow-drawer.scss
Normal file
@@ -0,0 +1,43 @@
|
||||
.flow-header-drawer {
|
||||
/*抽屉内容靠上*/
|
||||
.el-drawer__header {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.tinymce-print-drawer {
|
||||
/*抽屉内容靠上*/
|
||||
.el-drawer__header {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.flow-overflow-drawer {
|
||||
/*抽屉内容靠上*/
|
||||
.el-drawer__header {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
/*隐藏滚动条*/
|
||||
.el-drawer__body {
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
.flow-dialog-drawer {
|
||||
/*隐藏滚动条*/
|
||||
.el-dialog__body {
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
.flow-attr-drawer {
|
||||
/*抽屉内容靠上*/
|
||||
.el-drawer__header {
|
||||
margin-bottom: 0;
|
||||
margin-left: 15px;
|
||||
}
|
||||
.el-tabs__header .is-top{
|
||||
//display: none;
|
||||
}
|
||||
}
|
||||
|
||||
109
src/flow/components/tabs-option/design.vue
Normal file
109
src/flow/components/tabs-option/design.vue
Normal file
@@ -0,0 +1,109 @@
|
||||
<template>
|
||||
<el-container>
|
||||
<el-header style="background: white">
|
||||
<tabs-design-header :currFlowForm="props.currFlowForm" @handleDesignTabs="handleDesignTabs"
|
||||
@syncCurrFlowForm="syncCurrFlowForm"
|
||||
@publish="publishTabs"></tabs-design-header>
|
||||
</el-header>
|
||||
<div id="initClientHeight" style="min-width: 980px;">
|
||||
<tabs-setting :currFlowForm="props.currFlowForm" ref="tabsSetting" v-show="props.currFlowForm.active === 'tabsSetting'"/>
|
||||
<form-design :currFlowForm="props.currFlowForm" ref="formDesign" v-show="showFormDesign()"/>
|
||||
<form-def-perm :currFlowForm="props.currFlowForm" ref="formDefPerm" v-show="showFormDefPerm()"/>
|
||||
</div>
|
||||
</el-container>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="TabsFormDesign">
|
||||
import {useI18n} from "vue-i18n"
|
||||
import {formWidgetDesignHeight} from "/@/flow/utils";
|
||||
import {DIC_PROP} from "../../support/dict-prop";
|
||||
|
||||
const {t} = useI18n();
|
||||
const { proxy } = getCurrentInstance();
|
||||
// 引入组件
|
||||
const TabsDesignHeader = defineAsyncComponent(() => import('./header.vue'));
|
||||
const FormDesign = defineAsyncComponent(() => import('../form-create/designer.vue'));
|
||||
const TabsSetting = defineAsyncComponent(() => import('./setting.vue'));
|
||||
const FormDefPerm = defineAsyncComponent(() => import('/@/views/jsonflow/form-option/form-def-perm.vue'));
|
||||
|
||||
const $emit = defineEmits(['handleDesignTabs']);
|
||||
const props = defineProps({
|
||||
currFlowForm: {
|
||||
type: Object,
|
||||
default: null,
|
||||
}
|
||||
});
|
||||
|
||||
function initClientHeight() {
|
||||
nextTick(() => {
|
||||
let browserHeight = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight;
|
||||
formWidgetDesignHeight(browserHeight);
|
||||
})
|
||||
}
|
||||
|
||||
function handleDesignTabs(bool: boolean) {
|
||||
$emit("handleDesignTabs", bool);
|
||||
}
|
||||
|
||||
function showFormDesign() {
|
||||
return props.currFlowForm.active === 'formDesign' && props.currFlowForm.type !== DIC_PROP.FORM_TYPE[1].value
|
||||
}
|
||||
function showFormDefPerm() {
|
||||
return props.currFlowForm.active === 'formDesign' && props.currFlowForm.type === DIC_PROP.FORM_TYPE[1].value
|
||||
}
|
||||
|
||||
async function syncCurrFlowForm(callback) {
|
||||
let preSave = await proxy.$refs.tabsSetting.validatePreSave();
|
||||
callback(preSave)
|
||||
}
|
||||
|
||||
async function syncCurrFormInfo(callback?) {
|
||||
if (props.currFlowForm.type !== DIC_PROP.FORM_TYPE[1].value) {
|
||||
proxy.$refs.formDefPerm.syncCurrFormInfo(true)
|
||||
// 保存表单信息
|
||||
return await proxy.$refs.formDesign.handleSubmit(() => {
|
||||
if (callback) callback()
|
||||
}, true);
|
||||
} else {
|
||||
proxy.$refs.formDefPerm.syncCurrFormInfo(false)
|
||||
props.currFlowForm.formInfo = null
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
async function publishTabs(menu, callback, status) {
|
||||
// 保存表单信息
|
||||
let preSave = await syncCurrFormInfo(callback)
|
||||
if (!preSave) return
|
||||
// 校验表单设计
|
||||
let otherSave = await proxy.$refs.tabsSetting.validateOtherSave();
|
||||
if (!otherSave) return
|
||||
// 保存字段信息
|
||||
if (props.currFlowForm.type === DIC_PROP.FORM_TYPE[1].value) {
|
||||
await proxy.$refs.formDefPerm.handleSubmit(() => {
|
||||
if (callback) callback()
|
||||
});
|
||||
}
|
||||
// 保存页面配置
|
||||
proxy.$refs.tabsSetting.handleSubmit(() => {
|
||||
if (callback) callback()
|
||||
}, status)
|
||||
}
|
||||
|
||||
// 监听双向绑定
|
||||
watch(
|
||||
() => props.currFlowForm.id,
|
||||
(val) => {
|
||||
initClientHeight()
|
||||
}
|
||||
);
|
||||
|
||||
onMounted(() => {
|
||||
initClientHeight();
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
</style>
|
||||
244
src/flow/components/tabs-option/form.vue
Normal file
244
src/flow/components/tabs-option/form.vue
Normal file
@@ -0,0 +1,244 @@
|
||||
<template>
|
||||
<el-dialog :title="title" v-model="visible" width="80%"
|
||||
:close-on-click-modal="false" draggable>
|
||||
<el-form ref="dataFormRef" :model="form" :rules="dataRules" label-width="130px" v-loading="loading" :disabled="operType === 'view'">
|
||||
<el-row :gutter="24">
|
||||
<el-col :span="12" class="mb20">
|
||||
<el-form-item :label="t('tabsOption.formName')" prop="formName">
|
||||
<el-input v-model="form.formName" :placeholder="t('tabsOption.inputFormNameTip')" maxlength="20" show-word-limit clearable/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="12" class="mb20">
|
||||
<el-form-item :label="t('tabsOption.icon')" prop="icon">
|
||||
<IconSelector :placeholder="$t('tabsOption.inputIconTip')" v-model="form.icon" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="12" class="mb20">
|
||||
<el-form-item :label="t('tabsOption.groupName')" prop="groupName">
|
||||
<el-select v-model="form.groupName" :placeholder="t('tabsOption.inputGroupNameTip')" style="width: 80%!important;"
|
||||
clearable filterable allowCreate defaultFirstOption>
|
||||
<el-option v-for="(item, index) in dicData.groupName" :key="index" :label="item.groupName" :value="item.groupName"></el-option>
|
||||
</el-select>
|
||||
|
||||
<el-button
|
||||
type="primary" size="small" round style="margin-left: 10px"
|
||||
@click="handleAddGroupName"
|
||||
>新增分组
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="12" class="mb20">
|
||||
<el-form-item :label="t('tabsOption.type')" prop="type">
|
||||
<el-select v-model="form.type" :placeholder="t('tabsOption.inputTypeTip')" clearable filterable>
|
||||
<el-option v-for="(item, index) in DIC_PROP.FORM_TYPE" :key="index" :label="item.label" :value="item.value"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="12" class="mb20" v-if="form.type === DIC_PROP.FORM_TYPE[1].value">
|
||||
<el-form-item :label="t('tabsOption.path')" prop="path">
|
||||
<el-tooltip content="当表单类型为系统表单时,可输入本地Vue组件路径,或者外部系统http或https开头的页面路径" placement="top">
|
||||
<el-input v-model="form.path" :placeholder="t('tabsOption.inputPathTip')" clearable>
|
||||
</el-input>
|
||||
</el-tooltip>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="12" class="mb20">
|
||||
<el-form-item :label="t('flowApplication.remark')" prop="remark">
|
||||
<el-input v-model="form.remark" type="textarea" :placeholder="t('flowApplication.inputRemarkTip')"/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="12" class="mb20">
|
||||
<el-form-item :label="t('tabsOption.sort')" prop="sort">
|
||||
<el-input-number :min="1" :max="1000" v-model="form.sort" :placeholder="t('tabsOption.inputSortTip')"></el-input-number>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="12" class="mb20">
|
||||
<el-form-item :label="t('tabsOption.version')" prop="version">
|
||||
<el-input-number :min="1" :max="1000" v-model="form.version" :placeholder="t('tabsOption.inputVersionTip')"></el-input-number>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="12" class="mb20">
|
||||
<el-form-item :label="t('tabsOption.isActive')" prop="isActive">
|
||||
<el-radio-group v-model="form.isActive">
|
||||
<el-radio v-for="(item, index) in DIC_PROP.YES_OR_NO" :key="index" :label="item.value" >
|
||||
{{ getLabelByLanguage(item) }}
|
||||
</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="12" class="mb20">
|
||||
<el-form-item :label="t('tabsOption.isAutoAudit')" prop="isAutoAudit">
|
||||
<el-radio-group v-model="form.isAutoAudit">
|
||||
<el-radio v-for="(item, index) in DIC_PROP.YES_OR_NO" :key="index" :label="item.value" >
|
||||
{{ getLabelByLanguage(item) }}
|
||||
</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="12" class="mb20">
|
||||
<el-form-item :label="t('tabsOption.status')" prop="status">
|
||||
<el-radio-group v-model="form.status">
|
||||
<el-radio v-for="(item, index) in DIC_PROP.TEMP_STATUS" :key="index" :label="item.value" >
|
||||
{{ getLabelByLanguage(item) }}
|
||||
</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
</el-row>
|
||||
</el-form>
|
||||
<template #footer v-if="operType !== 'view'">
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="visible = false">{{ $t('common.cancelButtonText') }}</el-button>
|
||||
<el-button type="primary" @click="onSubmit" :disabled="loading">{{ $t('common.confirmButtonText') }}</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="TabsOptionDialog">
|
||||
|
||||
import {useMessage, useMessageBox} from "/@/hooks/message";
|
||||
import { getObj, addObj, putObj } from '/@/api/order/flow-application'
|
||||
import { useI18n } from "vue-i18n"
|
||||
import {onLoadDicUrl, onUpdateDicData} from "/@/flow/components/convert-name/convert";
|
||||
import {DIC_PROP} from "../../support/dict-prop";
|
||||
import {notifyLeft} from "../../index";
|
||||
import {validateFormTypeSave} from "/@/api/order/order-key-vue";
|
||||
const emit = defineEmits(['refresh']);
|
||||
// 引入组件
|
||||
const IconSelector = defineAsyncComponent(() => import('/@/components/IconSelector/index.vue'));
|
||||
const { t } = useI18n();
|
||||
|
||||
// 定义变量内容
|
||||
const dataFormRef = ref();
|
||||
const visible = ref(false);
|
||||
const loading = ref(false);
|
||||
const operType = ref(false);
|
||||
const title = ref('');
|
||||
// 定义字典
|
||||
const dicData = reactive({});
|
||||
const onLoad = onLoadDicUrl({key: "groupName"});
|
||||
const onUpdate = onUpdateDicData({key: "groupName"});
|
||||
onMounted(() => {
|
||||
onLoad(dicData);
|
||||
});
|
||||
|
||||
// 提交表单数据
|
||||
const form = reactive({
|
||||
icon: '',
|
||||
formName: '',
|
||||
groupName: '',
|
||||
path: '',
|
||||
prop: '',
|
||||
isActive: '1',
|
||||
isAutoAudit: '0',
|
||||
status: '-1',
|
||||
version: 1,
|
||||
sort: 1,
|
||||
});
|
||||
|
||||
// 定义校验规则
|
||||
const dataRules = ref({
|
||||
icon: [{required: true, message: '图标不能为空', trigger: 'blur'}],
|
||||
formName: [{required: true, message: '表单名称不能为空', trigger: 'blur'}],
|
||||
groupName: [{required: true, message: '分组名称不能为空', trigger: 'blur'}],
|
||||
prop: [{required: true, message: 'tab标识不能为空', trigger: 'blur'}],
|
||||
isActive: [{required: true, message: '默认展示不能为空', trigger: 'blur'}],
|
||||
isAutoAudit: [{required: true, message: '提交时自动审批不能为空', trigger: 'blur'}],
|
||||
sort: [{required: true, message: '排序值不能为空', trigger: 'blur'}],
|
||||
type: [{required: true, message: '表单类型不能为空', trigger: 'blur'}],
|
||||
status: [{required: true, message: '状态不能为空', trigger: 'blur'}],
|
||||
version: [{required: true, message: '版本不能为空', trigger: 'blur'}],
|
||||
})
|
||||
|
||||
// 打开弹窗
|
||||
const openDialog = (type: string, id: string) => {
|
||||
visible.value = true
|
||||
operType.value = type;
|
||||
form.id = ''
|
||||
|
||||
if (type === 'add') {
|
||||
title.value = t('common.addBtn');
|
||||
} else if (type === 'edit') {
|
||||
title.value = t('common.editBtn');
|
||||
} else if (type === 'view') {
|
||||
title.value = t('common.viewBtn');
|
||||
}
|
||||
|
||||
// 重置表单数据
|
||||
nextTick(() => {
|
||||
dataFormRef.value?.resetFields();
|
||||
});
|
||||
|
||||
// 获取TabsOption信息
|
||||
if (id) {
|
||||
form.id = id
|
||||
getTabsOptionData(id)
|
||||
}
|
||||
};
|
||||
|
||||
function handleAddGroupName() {
|
||||
useMessageBox().prompt("请输入新的分组名称")
|
||||
.then(({ value }) => {
|
||||
form.groupName = value
|
||||
onUpdate(dicData, form);
|
||||
})
|
||||
}
|
||||
|
||||
// 提交
|
||||
const onSubmit = async () => {
|
||||
const valid = await dataFormRef.value.validate().catch(() => {});
|
||||
if (!valid) return false;
|
||||
|
||||
if (form.type === DIC_PROP.FORM_TYPE[1].value) {
|
||||
if (!form.path) {
|
||||
notifyLeft('当前表单类型为系统表单,【PC端路径】不能为空', 'warning')
|
||||
return false
|
||||
}
|
||||
}
|
||||
let isDesign = form.type !== DIC_PROP.FORM_TYPE[1].value;
|
||||
if (isDesign && !form.formInfo) {
|
||||
notifyLeft('当前表单类型为设计表单,请先完善第二项设计的表单内容', 'warning')
|
||||
return false
|
||||
}
|
||||
validateFormTypeSave(form)
|
||||
try {
|
||||
loading.value = true;
|
||||
form.id ? await putObj(form) : await addObj(form);
|
||||
useMessage().success(t(form.id ? 'common.editSuccessText' : 'common.addSuccessText'));
|
||||
visible.value = false;
|
||||
emit('refresh');
|
||||
} catch (err: any) {
|
||||
useMessage().error(err.msg);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 初始化表单数据
|
||||
const getTabsOptionData = (id: string) => {
|
||||
// 获取数据
|
||||
loading.value = true
|
||||
getObj(id).then((res: any) => {
|
||||
Object.assign(form, res.data)
|
||||
}).finally(() => {
|
||||
loading.value = false
|
||||
})
|
||||
};
|
||||
|
||||
// 暴露变量
|
||||
defineExpose({
|
||||
openDialog
|
||||
});
|
||||
</script>
|
||||
140
src/flow/components/tabs-option/header.vue
Normal file
140
src/flow/components/tabs-option/header.vue
Normal file
@@ -0,0 +1,140 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="header">
|
||||
<el-tabs v-model="form.active" @tab-click="activeMenu">
|
||||
<el-tab-pane label="① 表单设置" name="tabsSetting"></el-tab-pane>
|
||||
<el-tab-pane label="② 表单设计" name="formDesign"></el-tab-pane>
|
||||
</el-tabs>
|
||||
|
||||
<div class="btn-publish" v-if="form.active !== 'formDesign'">
|
||||
<el-button icon="CaretRight" type="primary" @click="activeMenu({paneName: 'formDesign'})">下一步
|
||||
</el-button>
|
||||
</div>
|
||||
<div class="btn-publish" v-if="form.active === 'formDesign'">
|
||||
<el-button icon="CirclePlus" type="primary" @click="publish('-1')">暂存
|
||||
</el-button>
|
||||
<el-button icon="Promotion" type="primary" @click="publish('1')">发布
|
||||
</el-button>
|
||||
</div>
|
||||
<div class="btn-back">
|
||||
<el-button @click="exitTabsForm" icon="Back" circle></el-button>
|
||||
<span style="margin-left: 20px;">
|
||||
<i :class="props.currFlowForm.icon"></i> <span>{{ props.currFlowForm.formName }}</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="TabsDesignHeader">
|
||||
import {useI18n} from "vue-i18n"
|
||||
import {useMessageBox} from "/@/hooks/message";
|
||||
|
||||
const {t} = useI18n();
|
||||
const $emit = defineEmits(["handleDesignTabs", "syncCurrFlowForm", "publish"]);
|
||||
const props = defineProps({
|
||||
currFlowForm: {
|
||||
type: Object,
|
||||
default: null,
|
||||
}
|
||||
});
|
||||
const form = reactive({
|
||||
active: "tabsSetting"
|
||||
});
|
||||
|
||||
function publish(status) {
|
||||
$emit('publish', form.active, () => {
|
||||
props.currFlowForm[form.active] = true
|
||||
}, status)
|
||||
}
|
||||
async function exitTabsForm() {
|
||||
let text = ''
|
||||
// 判断是否都保存
|
||||
if (props.currFlowForm.formDesign !== true) text += '【表单设计】,'
|
||||
if (props.currFlowForm.tabsSetting !== true) text += '【表单设置】'
|
||||
if (!text) {
|
||||
$emit("handleDesignTabs", false);
|
||||
return
|
||||
}
|
||||
try {
|
||||
await useMessageBox().confirm(text + '未保存,是否继续退出?');
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
$emit("handleDesignTabs", false);
|
||||
}
|
||||
|
||||
function activeMenu(tab) {
|
||||
let menu = tab.paneName
|
||||
let active = form.active;
|
||||
$emit("syncCurrFlowForm", (preSave)=>{
|
||||
if (!preSave) menu = active
|
||||
props.currFlowForm[menu] = false;
|
||||
form.active = menu;
|
||||
props.currFlowForm.active = menu;
|
||||
});
|
||||
}
|
||||
|
||||
// 关闭提示
|
||||
function listenPage() {
|
||||
window.onbeforeunload = function (e) {
|
||||
e = e || window.event;
|
||||
if (e) {
|
||||
e.returnValue = "关闭提示";
|
||||
}
|
||||
return "关闭提示";
|
||||
};
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
listenPage();
|
||||
});
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.header {
|
||||
min-width: 980px;
|
||||
|
||||
.el-tabs {
|
||||
position: absolute;
|
||||
top: 15px;
|
||||
z-index: 999;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.btn-publish {
|
||||
position: absolute;
|
||||
top: 20px;
|
||||
z-index: 1000;
|
||||
right: 40px !important;
|
||||
i {
|
||||
margin-right: 6px;
|
||||
}
|
||||
button {
|
||||
width: 74px;
|
||||
height: 28px;
|
||||
border-radius: 15px;
|
||||
}
|
||||
}
|
||||
.btn-back {
|
||||
position: absolute;
|
||||
top: 20px;
|
||||
z-index: 1000;
|
||||
left: 20px !important;
|
||||
font-size: 18px;
|
||||
i {
|
||||
margin-right: 6px;
|
||||
}
|
||||
button {
|
||||
width: 44px;
|
||||
height: 28px;
|
||||
border-radius: 15px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
</style>
|
||||
42
src/flow/components/tabs-option/i18n/en.ts
Normal file
42
src/flow/components/tabs-option/i18n/en.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
export default {
|
||||
tabsOption: {
|
||||
index: '#',
|
||||
importtabsOptionTip: 'import TabsOption',
|
||||
id: 'id',
|
||||
icon: 'icon',
|
||||
formName: 'formName',
|
||||
groupName: 'groupName',
|
||||
path: 'path',
|
||||
sort: 'sort',
|
||||
isActive: 'isActive',
|
||||
isAutoAudit: 'isAutoAudit',
|
||||
status: 'status',
|
||||
version: 'version',
|
||||
createTime: 'createTime',
|
||||
createUser: 'createUser',
|
||||
updateUser: 'updateUser',
|
||||
formInfo: 'formInfo',
|
||||
type: 'type',
|
||||
updateTime: 'updateTime',
|
||||
delFlag: 'delFlag',
|
||||
|
||||
inputIdTip: 'input id',
|
||||
inputIconTip: 'input icon',
|
||||
inputFormNameTip: 'input formName',
|
||||
inputGroupNameTip: 'input groupName',
|
||||
inputPathTip: 'input path',
|
||||
inputSortTip: 'input sort',
|
||||
inputIsActiveTip: 'input isActive',
|
||||
inputIsAutoAuditTip: 'input isAutoAudit',
|
||||
inputStatusTip: 'input status',
|
||||
inputVersionTip: 'input version',
|
||||
inputCreateTimeTip: 'input createTime',
|
||||
inputCreateUserTip: 'input createUser',
|
||||
inputUpdateUserTip: 'input updateUser',
|
||||
inputFormInfoTip: 'input formInfo',
|
||||
inputTypeTip: 'input type',
|
||||
inputUpdateTimeTip: 'input updateTime',
|
||||
inputDelFlagTip: 'input delFlag',
|
||||
|
||||
}
|
||||
}
|
||||
42
src/flow/components/tabs-option/i18n/zh-cn.ts
Normal file
42
src/flow/components/tabs-option/i18n/zh-cn.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
export default {
|
||||
tabsOption: {
|
||||
index: '#',
|
||||
importtabsOptionTip: '导入流程表单',
|
||||
id: '主键ID',
|
||||
icon: '图标',
|
||||
formName: '表单名称',
|
||||
groupName: '分组名称',
|
||||
path: 'PC端路径',
|
||||
sort: '排序值',
|
||||
isActive: '默认展示',
|
||||
isAutoAudit: '提交时自动审批',
|
||||
status: '状态',
|
||||
version: '版本',
|
||||
createTime: '创建时间',
|
||||
createUser: '创建人',
|
||||
updateUser: '更新人',
|
||||
formInfo: '表单信息',
|
||||
type: '表单类型',
|
||||
updateTime: '更新时间',
|
||||
delFlag: '删除标识',
|
||||
|
||||
inputIdTip: '请输入主键ID',
|
||||
inputIconTip: '请输入图标',
|
||||
inputFormNameTip: '请输入表单名称',
|
||||
inputGroupNameTip: '请输入分组名称',
|
||||
inputPathTip: '请输入PC端路径',
|
||||
inputSortTip: '请输入排序值',
|
||||
inputIsActiveTip: '请输入默认展示',
|
||||
inputIsAutoAuditTip: '请输入提交时自动审批',
|
||||
inputStatusTip: '请输入状态',
|
||||
inputVersionTip: '请输入版本',
|
||||
inputCreateTimeTip: '请输入创建时间',
|
||||
inputCreateUserTip: '请输入创建人',
|
||||
inputUpdateUserTip: '请输入更新人',
|
||||
inputFormInfoTip: '请输入表单信息',
|
||||
inputTypeTip: '请选择表单类型',
|
||||
inputUpdateTimeTip: '请输入更新时间',
|
||||
inputDelFlagTip: '请输入删除标识',
|
||||
|
||||
}
|
||||
}
|
||||
226
src/flow/components/tabs-option/setting.vue
Normal file
226
src/flow/components/tabs-option/setting.vue
Normal file
@@ -0,0 +1,226 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-form ref="dataFormRef" :model="props.currFlowForm" :rules="dataRules" label-width="150px"
|
||||
v-loading="loading"
|
||||
label-position="left"
|
||||
:disabled="operType === 'view'">
|
||||
<el-row :gutter="24">
|
||||
|
||||
<el-col :span="12" class="mb20" :offset="6">
|
||||
<el-form-item :label="t('tabsOption.formName')" prop="formName">
|
||||
<el-input v-model="props.currFlowForm.formName" :placeholder="t('tabsOption.inputFormNameTip')" maxlength="20" show-word-limit clearable/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="12" class="mb20" :offset="6">
|
||||
<el-form-item :label="t('tabsOption.icon')" prop="icon">
|
||||
<IconSelector :placeholder="$t('tabsOption.inputIconTip')" v-model="props.currFlowForm.icon"/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="12" class="mb20" :offset="6">
|
||||
<el-form-item :label="t('tabsOption.groupName')" prop="groupName">
|
||||
<el-select v-model="props.currFlowForm.groupName" style="width: 83%!important;"
|
||||
:placeholder="t('tabsOption.inputGroupNameTip')" clearable
|
||||
filterable allowCreate defaultFirstOption>
|
||||
<el-option v-for="(item, index) in dicData.groupName" :key="index" :label="item.groupName"
|
||||
:value="item.groupName"></el-option>
|
||||
</el-select>
|
||||
|
||||
<el-button
|
||||
type="primary" size="small" round style="margin-left: 10px"
|
||||
@click="handleAddGroupName"
|
||||
>新增分组
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="12" class="mb20" :offset="6">
|
||||
<el-form-item :label="t('tabsOption.type')" prop="type">
|
||||
<el-select v-model="props.currFlowForm.type" :placeholder="t('tabsOption.inputTypeTip')" clearable filterable>
|
||||
<el-option v-for="(item, index) in DIC_PROP.FORM_TYPE" :key="index" :label="item.label" :value="item.value"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="12" class="mb20" :offset="6" v-if="props.currFlowForm.type === DIC_PROP.FORM_TYPE[1].value">
|
||||
<el-form-item :label="t('tabsOption.path')" prop="path">
|
||||
<el-tooltip content="当表单类型为系统表单时,可输入本地Vue组件路径,或者外部系统http或https开头的页面路径" placement="top">
|
||||
<el-input v-model="props.currFlowForm.path" :placeholder="t('tabsOption.inputPathTip')" clearable>
|
||||
</el-input>
|
||||
</el-tooltip>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="12" class="mb20" :offset="6">
|
||||
<el-form-item :label="t('flowApplication.remark')" prop="remark">
|
||||
<el-input v-model="props.currFlowForm.remark" type="textarea" :placeholder="t('flowApplication.inputRemarkTip')"/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="12" class="mb20" :offset="6">
|
||||
<el-form-item :label="t('tabsOption.sort')" prop="sort">
|
||||
<el-input-number :min="1" :max="1000" v-model="props.currFlowForm.sort"
|
||||
:placeholder="t('tabsOption.inputSortTip')"></el-input-number>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="12" class="mb20" :offset="6">
|
||||
<el-form-item :label="t('tabsOption.version')" prop="version">
|
||||
<el-input-number :min="1" :max="1000" v-model="props.currFlowForm.version" :placeholder="t('tabsOption.inputVersionTip')"></el-input-number>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="12" class="mb20" :offset="6">
|
||||
<el-tooltip content="在审批时默认展示的第1个页面。当存在多个默认展示页面时,优先展示排序值最小的" placement="top">
|
||||
<el-form-item :label="t('tabsOption.isActive')" prop="isActive">
|
||||
<el-radio-group v-model="props.currFlowForm.isActive">
|
||||
<el-radio v-for="(item, index) in DIC_PROP.YES_OR_NO" :key="index" :label="item.value">
|
||||
{{ getLabelByLanguage(item) }}
|
||||
</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-tooltip>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="12" class="mb20" :offset="6">
|
||||
<el-tooltip content="在审批时不会显示审批按钮,在页面点提交按钮会自动流转到下一步" placement="top">
|
||||
<el-form-item :label="t('tabsOption.isAutoAudit')" prop="isAutoAudit">
|
||||
<el-radio-group v-model="props.currFlowForm.isAutoAudit">
|
||||
<el-radio v-for="(item, index) in DIC_PROP.YES_OR_NO" :key="index" :label="item.value" >
|
||||
{{ getLabelByLanguage(item) }}
|
||||
</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-tooltip>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="12" class="mb20" :offset="6">
|
||||
<el-form-item :label="t('tabsOption.status')" prop="status">
|
||||
<el-radio-group v-model="props.currFlowForm.status">
|
||||
<el-radio v-for="(item, index) in DIC_PROP.TEMP_STATUS" :key="index" :label="item.value">
|
||||
{{ getLabelByLanguage(item) }}
|
||||
</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
</el-row>
|
||||
</el-form>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="TabsOptionForm">
|
||||
import {useMessage, useMessageBox} from "/@/hooks/message";
|
||||
import {getObj, addObj, putObj} from '/@/api/order/flow-application'
|
||||
import other from '/@/utils/other';
|
||||
import {notifyLeft, stringifyWithFunctions} from "/@/flow";
|
||||
import {useI18n} from "vue-i18n"
|
||||
import {onLoadDicUrl, onUpdateDicData} from "/@/flow/components/convert-name/convert";
|
||||
import {PROP_CONST} from "../../support/prop-const";
|
||||
import {DIC_PROP} from "../../support/dict-prop";
|
||||
import {validateFormTypeSave} from "/@/api/order/order-key-vue";
|
||||
|
||||
const {t} = useI18n();
|
||||
const {proxy} = getCurrentInstance();
|
||||
const props = defineProps({
|
||||
currFlowForm: {
|
||||
type: Object,
|
||||
default: null,
|
||||
}
|
||||
});
|
||||
// 引入组件
|
||||
const IconSelector = defineAsyncComponent(() => import('/@/components/IconSelector/index.vue'));
|
||||
|
||||
// 定义变量内容
|
||||
const dataFormRef = ref();
|
||||
const loading = ref(false);
|
||||
const operType = ref(false);
|
||||
// 定义字典
|
||||
const dicData = reactive({});
|
||||
const onLoad = onLoadDicUrl({key: "groupName"});
|
||||
const onUpdate = onUpdateDicData({key: "groupName"});
|
||||
onMounted(async () => {
|
||||
await onLoad(dicData);
|
||||
let isNoGroupName = !props.currFlowForm.groupName && dicData.groupName.length > 0;
|
||||
if (isNoGroupName) {
|
||||
props.currFlowForm.groupName = dicData.groupName[0].groupName
|
||||
}
|
||||
});
|
||||
|
||||
// 定义校验规则
|
||||
const dataRules = ref({
|
||||
icon: [{required: true, message: '图标不能为空', trigger: 'blur'}],
|
||||
type: [{required: true, message: '表单类型不能为空', trigger: 'blur'}],
|
||||
formName: [{required: true, message: '表单名称不能为空', trigger: 'blur'}],
|
||||
groupName: [{required: true, message: '分组名称不能为空', trigger: 'blur'}],
|
||||
prop: [{required: true, message: 'tab标识不能为空', trigger: 'blur'}],
|
||||
sort: [{required: true, message: '排序值不能为空', trigger: 'blur'}],
|
||||
isActive: [{required: true, message: '默认展示不能为空', trigger: 'blur'}],
|
||||
isAutoAudit: [{required: true, message: '提交时自动审批不能为空', trigger: 'blur'}],
|
||||
status: [{required: true, message: '状态不能为空', trigger: 'blur'}],
|
||||
version: [{required: true, message: '版本不能为空', trigger: 'blur'}],
|
||||
})
|
||||
|
||||
function handleAddGroupName() {
|
||||
useMessageBox().prompt("请输入新的分组名称")
|
||||
.then(({ value }) => {
|
||||
props.currFlowForm.groupName = value
|
||||
onUpdate(dicData, props.currFlowForm);
|
||||
})
|
||||
}
|
||||
|
||||
async function validatePreSave() {
|
||||
const valid = await dataFormRef.value.validate().catch(() => {});
|
||||
if (!valid) {
|
||||
notifyLeft('请先完善表单设置内容', 'warning')
|
||||
return false;
|
||||
}
|
||||
let isSys = props.currFlowForm.type === DIC_PROP.FORM_TYPE[1].value;
|
||||
if (isSys && !props.currFlowForm.path) {
|
||||
notifyLeft('当前表单类型为系统表单,【PC端路径】不能为空', 'warning')
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
function validateOtherSave() {
|
||||
let isDesign = props.currFlowForm.type !== DIC_PROP.FORM_TYPE[1].value;
|
||||
if (isDesign && !props.currFlowForm.formInfo) {
|
||||
notifyLeft('当前表单类型为设计表单,请先完善第二项设计的表单内容', 'warning')
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
const handleSubmit = async (callback, status) => {
|
||||
if (status) {
|
||||
props.currFlowForm.status = status
|
||||
}
|
||||
let preSave = await validatePreSave();
|
||||
if (!preSave) return
|
||||
if (!validateOtherSave()) return
|
||||
let formJson = other.deepClone(props.currFlowForm)
|
||||
formJson.formInfo = stringifyWithFunctions(formJson.formInfo)
|
||||
|
||||
validateFormTypeSave(formJson)
|
||||
try {
|
||||
loading.value = true;
|
||||
await onUpdate(dicData, props.currFlowForm);
|
||||
await addObj(formJson)
|
||||
props.currFlowForm.tabsSetting = true
|
||||
notifyLeft('当前表单设置保存成功')
|
||||
if (callback) callback()
|
||||
} catch (err: any) {
|
||||
useMessage().error(err);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
// 暴露变量
|
||||
defineExpose({
|
||||
handleSubmit, validatePreSave, validateOtherSave
|
||||
});
|
||||
</script>
|
||||
378
src/flow/components/tinymce/TinymceEditor.vue
Normal file
378
src/flow/components/tinymce/TinymceEditor.vue
Normal file
@@ -0,0 +1,378 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-form ref="dataFormRef" :model="props.currFlowForm" :rules="dataRules" label-width="80px"
|
||||
v-loading="loading" v-if="!props.currFlowForm.isForm">
|
||||
<el-row :gutter="24">
|
||||
<el-col :span="12" class="mb20">
|
||||
<el-form-item :label="t('formoption.formType')" prop="formType">
|
||||
<el-select v-model="props.currFlowForm.formType" :placeholder="t('formoption.inputFormTypeTip')" clearable
|
||||
filterable
|
||||
@change="formTypeChange">
|
||||
<el-option v-for="(item, index) in DIC_PROP.FORM_TYPE" :key="index" :label="item.label"
|
||||
:value="item.value"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="12" class="mb20">
|
||||
<el-form-item :label="t('formoption.formId')" prop="formId">
|
||||
<el-select v-model="props.currFlowForm.formId" :placeholder="t('formoption.inputFormIdTip')" clearable
|
||||
filterable
|
||||
@change="formIdChange">
|
||||
<el-option v-for="(item, index) in dicData.formIdByType" :key="index"
|
||||
:label="item.formName" :value="item.id"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<div style="margin-bottom: 10px; color: #72767b;">请拖拽字段到设计器中</div>
|
||||
<el-container>
|
||||
<el-aside width="200px">
|
||||
<el-divider> 系统字段 </el-divider>
|
||||
<template v-for="(item, index) in data.sysFields" :key="index">
|
||||
<div :draggable="item.prop!=='_define_'" v-if="item.label"
|
||||
class="tinymce-form-prop" @dragstart="dragNode(item)">
|
||||
{{ item.label }}
|
||||
</div>
|
||||
</template>
|
||||
<el-divider> 表单字段 </el-divider>
|
||||
<template v-for="(item, index) in data.formFields" :key="index">
|
||||
<div draggable="true" v-if="item.label"
|
||||
class="tinymce-form-prop" @dragstart="dragNode(item)">
|
||||
{{ item.label }}
|
||||
</div>
|
||||
</template>
|
||||
</el-aside>
|
||||
<el-main>
|
||||
<editor v-model="props.currFlowForm.printInfo" @dragover="allowDrop" @drop="drop"
|
||||
:init="init"
|
||||
:disabled="loading"
|
||||
:id="tinymceId">
|
||||
</editor>
|
||||
<footer class="el-dialog__footer">
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="cancelClick">清空模板</el-button>
|
||||
<el-button type="primary" @click="confirmClick">保存模板</el-button>
|
||||
</span>
|
||||
</footer>
|
||||
</el-main>
|
||||
</el-container>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="TinymceEditor">
|
||||
import tinymce from "tinymce/tinymce";
|
||||
import Editor from "@tinymce/tinymce-vue";
|
||||
import "tinymce/icons/default/icons";
|
||||
import "tinymce/themes/silver";
|
||||
import "tinymce/plugins/print";
|
||||
import "tinymce/plugins/hr";
|
||||
import "tinymce/plugins/anchor";
|
||||
import "tinymce/plugins/fullscreen";
|
||||
import "tinymce/plugins/pagebreak";
|
||||
import "tinymce/plugins/image";
|
||||
import "tinymce/plugins/table";
|
||||
import "tinymce/plugins/lists";
|
||||
import "tinymce/plugins/wordcount";
|
||||
import "tinymce/plugins/preview";
|
||||
import "tinymce/plugins/emoticons";
|
||||
import "tinymce/plugins/emoticons/js/emojis.js";
|
||||
import "tinymce/plugins/code";
|
||||
import "tinymce/plugins/link";
|
||||
import "tinymce/plugins/advlist";
|
||||
import "tinymce/plugins/autoresize";
|
||||
import "tinymce/plugins/quickbars";
|
||||
import "tinymce/plugins/nonbreaking";
|
||||
import "tinymce/plugins/searchreplace";
|
||||
import "tinymce/plugins/autolink";
|
||||
import "tinymce/plugins/directionality";
|
||||
import "tinymce/plugins/visualblocks";
|
||||
import "tinymce/plugins/visualchars";
|
||||
import "tinymce/plugins/charmap";
|
||||
import "tinymce/plugins/nonbreaking";
|
||||
import "tinymce/plugins/insertdatetime";
|
||||
import "tinymce/plugins/importcss";
|
||||
import "tinymce/plugins/help";
|
||||
import {buildFieldPerms} from "/@/flow/utils/form-perm";
|
||||
import {validateNull} from "/@/utils/validate";
|
||||
import {useMessage} from "/@/hooks/message";
|
||||
import {PROP_CONST} from "../../support/prop-const";
|
||||
import {onLoadDicUrl} from "../convert-name/convert";
|
||||
import {DIC_PROP} from "../../support/dict-prop";
|
||||
import {listPrintTemp, savePrintTemp} from "/@/api/jsonflow/form-option";
|
||||
import {useI18n} from "vue-i18n";
|
||||
import {parseWithFunctions} from "../../index";
|
||||
import {deepClone} from "/@/utils/other";
|
||||
|
||||
const $message = useMessage();
|
||||
const { t } = useI18n()
|
||||
const props = defineProps({
|
||||
currFlowForm: {
|
||||
type: Object,
|
||||
default: null,
|
||||
},
|
||||
baseUrl: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
plugins: {
|
||||
type: [String, Array],
|
||||
default:
|
||||
"print preview hr anchor fullscreen pagebreak help searchreplace autoresize quickbars autolink directionality code visualblocks visualchars image link table nonbreaking charmap insertdatetime advlist lists wordcount emoticons help",
|
||||
},
|
||||
toolbar: {
|
||||
type: [String, Array],
|
||||
default:
|
||||
"code undo redo | cut copy paste pastetext | forecolor backcolor bold italic underline strikethrough link anchor | alignleft aligncenter alignright alignjustify outdent indent | \
|
||||
blocks fontfamily fontsize | bullist numlist | blockquote subscript superscript removeformat | \
|
||||
table image charmap pagebreak insertdatetime emoticons hr | print preview fullscreen | styleselect formatselect fontselect fontsizeselect",
|
||||
},
|
||||
});
|
||||
|
||||
const loading = ref(false);
|
||||
const tinymceId = ref(
|
||||
"tinymce-id-" + +new Date() + ((Math.random() * 1000).toFixed(0) + "")
|
||||
);
|
||||
|
||||
// 定义init初始化对象
|
||||
const init = reactive({
|
||||
selector: "#" + tinymceId.value,
|
||||
language_url: "/flow/tinymce/langs/zh_CN.js",
|
||||
language: "zh_CN",
|
||||
skin_url: "/flow/tinymce/skins/ui/oxide",
|
||||
branding: false,
|
||||
promotion: false,
|
||||
menubar: "file edit view insert format tools table help",
|
||||
paste_data_images: true,
|
||||
image_dimensions: false,
|
||||
plugins: props.plugins,
|
||||
toolbar: props.toolbar,
|
||||
quickbars_image_toolbar: "alignleft aligncenter alignright | rotateleft rotateright | imageoptions",
|
||||
editimage_toolbar: "rotateleft rotateright | flipv fliph | editimage imageoptions",
|
||||
font_formats: "Arial=arial,helvetica,sans-serif; 宋体=SimSun; 微软雅黑=Microsoft Yahei; Impact=impact,chicago;",
|
||||
fontsize_formats: "11px 12px 14px 16px 18px 24px 36px 48px 64px 72px",
|
||||
image_caption: true,
|
||||
editimage_cors_hosts: ["picsum.photos"],
|
||||
noneditable_class: "mceNonEditable",
|
||||
toolbar_mode: "wrap",
|
||||
content_style: "body { font-family:Helvetica,Arial,sans-serif; font-size:16px }",
|
||||
image_advtab: true,
|
||||
importcss_append: true,
|
||||
paste_webkit_styles: "all",
|
||||
paste_merge_formats: true,
|
||||
nonbreaking_force_tab: false,
|
||||
paste_auto_cleanup_on_paste: false,
|
||||
file_picker_types: "file",
|
||||
quickbars_insert_toolbar: "",
|
||||
quickbars_selection_toolbar: "bold italic | quicklink h2 h3 blockquote quickimage quicktable",
|
||||
autoresize_bottom_margin: 50,
|
||||
autoresize_max_height: 500,
|
||||
autoresize_min_height: 350,
|
||||
content_css: "/flow/tinymce/skins/content/default/content.css",
|
||||
images_upload_handler: (blobInfo, success, failure) => {
|
||||
// base64形式上传图片
|
||||
const img = 'data:image/jpeg;base64,' + blobInfo.base64()
|
||||
success(img)
|
||||
}
|
||||
});
|
||||
|
||||
const data = reactive({
|
||||
formFields: [],
|
||||
sysFields: deepClone(PROP_CONST.SYS_FIELDS),
|
||||
})
|
||||
const prop = reactive({
|
||||
propId: null,
|
||||
prop: null,
|
||||
propVar: null,
|
||||
propType: null,
|
||||
label: null
|
||||
});
|
||||
// 开始拖拽
|
||||
function dragNode(item) {
|
||||
prop.propId = item.propId
|
||||
prop.prop = item.prop
|
||||
prop.propVar = "${" + item.prop + "}"
|
||||
prop.subForm = item.subForm
|
||||
prop.propType = item.propType
|
||||
prop.label = item.label
|
||||
}
|
||||
|
||||
function allowDrop(e) {
|
||||
tinymce.activeEditor.selection.select(e.target, true).focus()
|
||||
tinymce.activeEditor.selection.collapse(false)
|
||||
e.preventDefault();
|
||||
}
|
||||
function drop(e) {
|
||||
let tagName = e.target.tagName;
|
||||
// 判断子表单信息
|
||||
if (prop.subForm && tagName !== 'TD') {
|
||||
$message.warning('子表单字段只能拖拽到表格中,请先创建表格');
|
||||
return
|
||||
} else {
|
||||
// 处理子表单
|
||||
if (prop.propType && prop.propType.indexOf(PROP_CONST.FORM_DESIGN.subForm) !== -1) {
|
||||
// TODO 创建对应列的表格
|
||||
}
|
||||
}
|
||||
let content = prop.propVar
|
||||
tinymce.activeEditor.insertContent(content)
|
||||
}
|
||||
|
||||
function extractedFormFields(formFields) {
|
||||
formFields.forEach(each => {
|
||||
if (each.subForm) {
|
||||
each.prop = each.subForm + '.' + each.prop
|
||||
each.label += "(子表单)"
|
||||
}
|
||||
})
|
||||
data.formFields = formFields
|
||||
}
|
||||
|
||||
function buildFormFieldPerms(formInfoStr) {
|
||||
if (validateNull(formInfoStr)) return
|
||||
let formInfo = parseWithFunctions(formInfoStr, true)
|
||||
let formFields = []
|
||||
buildFieldPerms(formFields, formInfo.widgetList);
|
||||
extractedFormFields(formFields)
|
||||
}
|
||||
|
||||
// 定义查询字典
|
||||
const dicData = reactive({});
|
||||
const onLoad = onLoadDicUrl({key: "formId"});
|
||||
|
||||
// 定义校验规则
|
||||
const dataRules = ref({
|
||||
formType: [{required: true, message: '表单来源不能为空', trigger: 'blur'}],
|
||||
formId: [{required: true, message: '表单名称不能为空', trigger: 'blur'}],
|
||||
})
|
||||
|
||||
const formTypeChange = (value, isInit) => {
|
||||
if (value) {
|
||||
dicData.formIdByType = dicData.formId.filter(f => f.type === value)
|
||||
} else dicData.formIdByType = []
|
||||
clearFormFields()
|
||||
if (!isInit) props.currFlowForm.formId = null
|
||||
}
|
||||
|
||||
const formIdChange = (value) => {
|
||||
clearFormFields()
|
||||
if (!value) return
|
||||
let find = dicData.formIdByType.find(f => f.id === value);
|
||||
// 表单实例
|
||||
if (!validateNull(props.currFlowForm.formInfo)) {
|
||||
handleFormPrintDef(value, props.currFlowForm.type, props.currFlowForm)
|
||||
return;
|
||||
}
|
||||
props.currFlowForm.formName = find.formName
|
||||
props.currFlowForm.path = find.path
|
||||
handleFormPrintDef(value, props.currFlowForm.type, find)
|
||||
}
|
||||
|
||||
function clearFormFields(){
|
||||
data.formFields = [];
|
||||
props.currFlowForm.printInfo = null
|
||||
tinymce.activeEditor.setContent('')
|
||||
}
|
||||
|
||||
// 查询字段信息
|
||||
function handleFormPrintDef(formId, type, find) {
|
||||
clearFormFields()
|
||||
listPrintTemp({
|
||||
flowInstId: props.currFlowForm.flowInstId,
|
||||
type: type, formType: props.currFlowForm.formType, formId: formId,
|
||||
}).then(resp => {
|
||||
let res = resp.data;
|
||||
props.currFlowForm.printInfo = res.printInfo
|
||||
if (!validateNull(res.columns)) extractedFormFields(res.columns)
|
||||
else if (!validateNull(find.formInfo)) {
|
||||
buildFormFieldPerms(find.formInfo);
|
||||
} else if (props.currFlowForm.formType === DIC_PROP.FORM_TYPE[0].value){
|
||||
$message.warning("当前选择的设计表单无字段信息,请先在《表单设计器》中设计")
|
||||
} else {
|
||||
$message.warning("当前选择的系统表单无字段信息,请先在表单设计中录入")
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
$message.error("获取表单字段定义失败");
|
||||
})
|
||||
}
|
||||
|
||||
async function initFormParams() {
|
||||
await onLoad(dicData);
|
||||
// 初始化
|
||||
props.currFlowForm.type = DIC_PROP.FORM_DATA_TYPE[2].value
|
||||
let formId = props.currFlowForm.formId;
|
||||
if (formId) {
|
||||
formTypeChange(props.currFlowForm.formType, true)
|
||||
formIdChange(formId)
|
||||
}
|
||||
}
|
||||
|
||||
async function confirmClick() {
|
||||
await savePrintTemp(props.currFlowForm)
|
||||
useMessage().success(t('common.editSuccessText'));
|
||||
}
|
||||
async function cancelClick() {
|
||||
props.currFlowForm.printInfo = null
|
||||
tinymce.activeEditor.setContent('')
|
||||
await confirmClick()
|
||||
}
|
||||
|
||||
// 监听变化
|
||||
watch(
|
||||
() => props.currFlowForm.id,
|
||||
async () => {
|
||||
await initFormParams()
|
||||
}
|
||||
);
|
||||
|
||||
// 初始化
|
||||
onMounted(async () => {
|
||||
await initFormParams()
|
||||
await tinymce.init({});
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.el-dialog__footer {
|
||||
text-align: center;
|
||||
margin-top: 10px;
|
||||
|
||||
.dialog-footer {
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="scss">
|
||||
@import "/flow/tinymce/skins/content/default/content.css";
|
||||
|
||||
.tox-tinymce {
|
||||
border: 1px solid #dcdfe6;
|
||||
border-radius: 4px;
|
||||
.tox-statusbar {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* 在el-dialog中z-index被遮挡 */
|
||||
.tox-tinymce-aux {
|
||||
z-index: 9999 !important;
|
||||
}
|
||||
|
||||
.tinymce-form-prop {
|
||||
cursor: move;
|
||||
background: rgb(245, 246, 246);
|
||||
border: 1px solid rgb(245, 246, 246);
|
||||
border-radius: 8px;
|
||||
margin-bottom: 5px;
|
||||
padding: 8px;
|
||||
|
||||
&:hover {
|
||||
color: #0960bd;
|
||||
outline: 1px dashed #0960bd;
|
||||
border: 1px solid #0960bd;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
246
src/flow/components/tinymce/TinymceView.vue
Normal file
246
src/flow/components/tinymce/TinymceView.vue
Normal file
@@ -0,0 +1,246 @@
|
||||
<template>
|
||||
<div>
|
||||
<div id="printInfo" style="word-break: break-all;" v-html="printInfoHtml"></div>
|
||||
<div>
|
||||
<footer class="el-dialog__footer" style="text-align: center;">
|
||||
<span class="dialog-footer">
|
||||
<el-button type="primary" @click="confirmClick">打印</el-button>
|
||||
</span>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="TinymceView">
|
||||
import {printHtml, validateRunFlowId} from "/@/flow";
|
||||
import {validateNull} from "/@/utils/validate";
|
||||
import {onFormLoadedUrl, onLoadDicUrl} from "../convert-name/convert";
|
||||
import {PROP_CONST} from "../../support/prop-const";
|
||||
import {handleFormatValue, handlePrintValue} from "../form-create";
|
||||
import {buildFieldPerms} from "../../utils/form-perm";
|
||||
import {listFormOption} from "/@/api/jsonflow/form-option";
|
||||
import {useMessage} from "/@/hooks/message";
|
||||
import {DIC_PROP} from "../../support/dict-prop";
|
||||
|
||||
const $message = useMessage();
|
||||
|
||||
const props = defineProps({
|
||||
currFlowForm: {
|
||||
type: Object,
|
||||
default: null,
|
||||
},
|
||||
elTab: {
|
||||
type: Object,
|
||||
default: null,
|
||||
}
|
||||
});
|
||||
|
||||
// 定义字典
|
||||
const dicData = reactive({});
|
||||
const onLoad = onLoadDicUrl();
|
||||
const onFormLoaded = onFormLoadedUrl();
|
||||
|
||||
const printInfoHtml = ref(null)
|
||||
|
||||
const data = reactive({
|
||||
formFields: [],
|
||||
// 自定义字段
|
||||
formDefFields: [],
|
||||
});
|
||||
|
||||
async function initPrintInfoHtml(printInfo) {
|
||||
if (props.currFlowForm.rule) {
|
||||
buildFieldPerms(data.formFields, props.currFlowForm.rule, null, true)
|
||||
} else {
|
||||
// 初始化自定义字段
|
||||
data.formDefFields = handleCustomFormDef()
|
||||
}
|
||||
buildPrintInfoHtml(printInfo)
|
||||
}
|
||||
|
||||
// 查询字段信息
|
||||
async function handleCustomFormDef() {
|
||||
if (validateNull(props.elTab)) return []
|
||||
// 判断流程实例独立配置
|
||||
let form = validateRunFlowId(props, {})
|
||||
let resp = await listFormOption({
|
||||
flowInstId: form.flowInstId,
|
||||
type: DIC_PROP.FORM_DATA_TYPE[0].value, formType: data.elTab.type, formId: data.elTab.id
|
||||
}).catch(() => {
|
||||
$message.error("获取表单字段定义失败");
|
||||
})
|
||||
return resp.data;
|
||||
}
|
||||
|
||||
async function buildPrintInfoHtml(printInfo) {
|
||||
let regTable = /<\/tbody>.*?/g;
|
||||
let regTr = /<tr>.*?<\/tr>/g;
|
||||
let regSubVar = /\$\{.*?\..*?\}/g;
|
||||
let regVar = /\$\{.*?\}/g;
|
||||
printInfo = printInfo.replaceAll('\n', '')
|
||||
let tables = printInfo.split(regTable)
|
||||
for (let j = 0; j < tables.length; j++) {
|
||||
let table = tables[j]
|
||||
regSubVar.lastIndex = 0
|
||||
if (regSubVar.test(table)) {
|
||||
let newTable = table.replace(regTr, function (str) {
|
||||
// 确保完整匹配
|
||||
regSubVar.lastIndex = 0
|
||||
if (regSubVar.test(str)) {
|
||||
let propVar = str.substr(str.indexOf("${"), str.indexOf("}") - str.indexOf("${") + 1)
|
||||
let prop = propVar.substring(propVar.indexOf("${") + 2, propVar.indexOf("."))
|
||||
// 获取表格数据
|
||||
let tableData = props.currFlowForm.formData[prop]
|
||||
// 构造动态数据行
|
||||
let dynamicRows = "";
|
||||
if (!validateNull(tableData)) {
|
||||
for (let i = 0; i < tableData.length; i++) {
|
||||
let dynStr = str.replace(regSubVar, function (str) {
|
||||
let propVar = str.substr(str.indexOf("${"), str.indexOf("}") - str.indexOf("${") + 1)
|
||||
let prop = propVar.substring(propVar.indexOf(".") + 1, propVar.indexOf("}"))
|
||||
let existSubForm = validateSubFormField(props.currFlowForm.modelRefList, prop)
|
||||
let parProp = propVar.substring(propVar.indexOf("${") + 2, propVar.indexOf("}"))
|
||||
let formDatum = tableData[i][prop]
|
||||
if (existSubForm) {
|
||||
// 判断自定义组件
|
||||
let printValue = existSubForm.__fc__.prop.props.formCreateInject.printValue
|
||||
if (!printValue) {
|
||||
let optionItems = existSubForm.__fc__.prop.options
|
||||
printValue = handlePrintValue(optionItems, formDatum, 'value', 'label', parProp, existSubForm.type)
|
||||
}
|
||||
if (printValue) return printValue
|
||||
}
|
||||
if (!existSubForm) existSubForm = data.formFields.find(f => f.prop === parProp);
|
||||
// 自定义字段
|
||||
if (!existSubForm) existSubForm = data.formDefFields.find(f => f.subForm + '.' + f.prop === parProp);
|
||||
let propType = existSubForm ? existSubForm.propType || existSubForm.type : null
|
||||
return formDatum ? handleFormatValue(formDatum, formDatum, parProp, propType) : ''
|
||||
})
|
||||
dynamicRows += dynStr;
|
||||
}
|
||||
}
|
||||
return dynamicRows;
|
||||
} else {
|
||||
return str;
|
||||
}
|
||||
})
|
||||
printInfo = printInfo.replace(table, newTable);
|
||||
}
|
||||
}
|
||||
// 替换外层变量值
|
||||
if (regVar.test(printInfo)) {
|
||||
// 确保完整匹配
|
||||
regVar.lastIndex = 0
|
||||
const matches = [];
|
||||
let match;
|
||||
while ((match = regVar.exec(printInfo)) !== null) {
|
||||
matches.push({index: match.index, fullMatch: match[0], length: match[0].length});
|
||||
}
|
||||
// 为每个匹配项创建异步任务
|
||||
const promises = matches.map(async (match) => {
|
||||
const propVar = match.fullMatch;
|
||||
let prop = propVar.substring(propVar.indexOf("${") + 2, propVar.indexOf("}"))
|
||||
let formDatum = props.currFlowForm.formData[prop];
|
||||
let isSysFields = false, existForm = null;
|
||||
if (props.currFlowForm.rule) {
|
||||
existForm = data.formFields.find(f => f.prop === prop);
|
||||
if (existForm) {
|
||||
// 判断自定义组件
|
||||
let printValue = existForm.__fc__.prop.props.formCreateInject.printValue
|
||||
if (!printValue) {
|
||||
let optionItems = existForm.__fc__.prop.options
|
||||
printValue = handlePrintValue(optionItems, formDatum, 'value', 'label', prop, existForm.propType)
|
||||
}
|
||||
if (printValue) return printValue;
|
||||
} else {
|
||||
isSysFields = true;
|
||||
}
|
||||
} else {
|
||||
let optionItems = props.currFlowForm.dicData[prop];
|
||||
if (!validateNull(optionItems)) {
|
||||
let valueKey = props.currFlowForm[prop + '.valueKey']
|
||||
let showKey = props.currFlowForm[prop + '.showKey']
|
||||
existForm = data.formDefFields.find(f => f.prop === prop);
|
||||
let propType = existForm ? existForm.propType : null
|
||||
let printValue = handlePrintValue(optionItems, formDatum, valueKey, showKey, prop, propType)
|
||||
if (printValue) return printValue;
|
||||
} else {
|
||||
isSysFields = true;
|
||||
}
|
||||
}
|
||||
if (isSysFields) {
|
||||
formDatum = props.currFlowForm[prop];
|
||||
let field = PROP_CONST.SYS_FIELDS.find(f => f.prop === prop);
|
||||
if (!validateNull(field) && field.valueKey) {
|
||||
await onFormLoaded(dicData, {[prop]: formDatum}, {key: prop});
|
||||
let find = dicData[prop].find(f => f[field.valueKey] === formDatum);
|
||||
existForm = data.formDefFields.find(f => f.prop === prop);
|
||||
let propType = existForm ? existForm.propType : null
|
||||
if (find) return handleFormatValue(find[field.showKey], find[field.showKey], prop, propType)
|
||||
}
|
||||
}
|
||||
let propType = existForm ? existForm.propType : null
|
||||
return formDatum ? handleFormatValue(formDatum, formDatum, prop, propType) : ''
|
||||
})
|
||||
const replacements = await Promise.all(promises);
|
||||
// 从后往前替换,避免索引变化问题
|
||||
let dynStr = printInfo;
|
||||
for (let i = matches.length - 1; i >= 0; i--) {
|
||||
const match = matches[i];
|
||||
const replacement = replacements[i];
|
||||
dynStr = dynStr.substring(0, match.index) + replacement + dynStr.substring(match.index + match.length);
|
||||
}
|
||||
printInfo = dynStr;
|
||||
}
|
||||
printInfoHtml.value = printInfo
|
||||
}
|
||||
|
||||
function validateSubFormField(modelRefList, field) {
|
||||
if (validateNull(modelRefList)) return null
|
||||
let resExistField = null
|
||||
for (const each of modelRefList) {
|
||||
if (validateNull(each.model())) continue
|
||||
let existField = each.model()[field];
|
||||
if (existField) {
|
||||
resExistField = existField
|
||||
break;
|
||||
}
|
||||
}
|
||||
return resExistField
|
||||
}
|
||||
|
||||
function confirmClick() {
|
||||
printHtml("printInfo", props.currFlowForm.formName, null)
|
||||
}
|
||||
|
||||
// 监听变化
|
||||
watch(
|
||||
() => props.currFlowForm.id,
|
||||
() => {
|
||||
initPrintInfoHtml(props.currFlowForm.printInfo);
|
||||
}
|
||||
);
|
||||
|
||||
// 初始化
|
||||
onMounted(async () => {
|
||||
// await onLoad(dicData);
|
||||
initPrintInfoHtml(props.currFlowForm.printInfo);
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
// 排除tailwind.css全局样式的影响
|
||||
/*border-width: 0;
|
||||
border-style: solid;*/
|
||||
#printInfo {
|
||||
table, th, td {
|
||||
border: 1px solid; /* 设置边框样式 */
|
||||
border-collapse: collapse; /* 合并边框 */
|
||||
}
|
||||
}
|
||||
/*img { TODO 本地设置默认大小
|
||||
width: 660px;
|
||||
height: 150px;
|
||||
}*/
|
||||
</style>
|
||||
89
src/flow/components/user-role/dept.vue
Normal file
89
src/flow/components/user-role/dept.vue
Normal file
@@ -0,0 +1,89 @@
|
||||
<template>
|
||||
<div style="width: 100%" v-if="data.updateMultiple">
|
||||
<el-tooltip content="请输入部门名称进行模糊搜索" placement="top">
|
||||
<el-select v-model="data.modelValue" clearable filterable @change="changeModelValue"
|
||||
remote :remote-method="remoteMethod" :reserve-keyword="false"
|
||||
:multiple="props.multiple" :disabled="props.disabled">
|
||||
<el-option v-for="(item, index) in dicData.depts" :key="index" :label="item.name"
|
||||
:value="item.deptId"></el-option>
|
||||
</el-select>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="UserRolePickerIndex">
|
||||
// 定义字典
|
||||
import {onFormLoadedUrl, onLoadDicUrl, remoteMethodByKey} from "/@/flow/components/convert-name/convert";
|
||||
import {validateNull} from "/@/utils/validate";
|
||||
import {handlePrintValue} from "/@/flow/components/form-create";
|
||||
|
||||
const props = defineProps({
|
||||
formCreateInject: {
|
||||
type: Object,
|
||||
default: {},
|
||||
},
|
||||
modelValue: {
|
||||
type: [Number, String, Array],
|
||||
default: null,
|
||||
},
|
||||
multiple: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
});
|
||||
|
||||
const emits = defineEmits(['update:modelValue']);
|
||||
|
||||
const dicData = reactive({});
|
||||
const onLoad = onLoadDicUrl();
|
||||
const onFormLoaded = onFormLoadedUrl({key: "depts", field: "modelValue"});
|
||||
onMounted(async () => {
|
||||
data.modelValue = props.modelValue
|
||||
await onFormLoaded(dicData, data);
|
||||
changePrintValue()
|
||||
});
|
||||
|
||||
const data = reactive({
|
||||
modelValue: null,
|
||||
updateMultiple: true
|
||||
})
|
||||
|
||||
function remoteMethod(query: string) {
|
||||
remoteMethodByKey(query, onLoad, dicData, 'deptName', "depts")
|
||||
}
|
||||
|
||||
// 注入打印设计的内容,该逻辑非必须
|
||||
const changePrintValue = () => {
|
||||
if (!validateNull(data.modelValue)) {
|
||||
let printValue = handlePrintValue(dicData.depts, data.modelValue, 'deptId', 'name');
|
||||
if (printValue) props.formCreateInject.printValue = printValue
|
||||
else props.formCreateInject.printValue = ""
|
||||
} else {
|
||||
props.formCreateInject.printValue = ""
|
||||
}
|
||||
}
|
||||
|
||||
const changeModelValue = (value) => {
|
||||
data.modelValue = value
|
||||
emits('update:modelValue', value);
|
||||
changePrintValue()
|
||||
}
|
||||
|
||||
// 监听双向绑定
|
||||
watch(
|
||||
() => props.multiple,
|
||||
(val) => {
|
||||
data.updateMultiple = false
|
||||
changeModelValue(null)
|
||||
setTimeout(()=>{
|
||||
// 防止切换时报错
|
||||
data.updateMultiple = true
|
||||
})
|
||||
}
|
||||
);
|
||||
|
||||
</script>
|
||||
1001
src/flow/components/user-role/picker.vue
Normal file
1001
src/flow/components/user-role/picker.vue
Normal file
File diff suppressed because it is too large
Load Diff
89
src/flow/components/user-role/post.vue
Normal file
89
src/flow/components/user-role/post.vue
Normal file
@@ -0,0 +1,89 @@
|
||||
<template>
|
||||
<div style="width: 100%" v-if="data.updateMultiple">
|
||||
<el-tooltip content="请输入岗位名称进行模糊搜索" placement="top">
|
||||
<el-select v-model="data.modelValue" clearable filterable @change="changeModelValue"
|
||||
remote :remote-method="remoteMethod" :reserve-keyword="false"
|
||||
:multiple="props.multiple" :disabled="props.disabled">
|
||||
<el-option v-for="(item, index) in dicData.posts" :key="index" :label="item.postName"
|
||||
:value="item.postId"></el-option>
|
||||
</el-select>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="UserRolePickerIndex">
|
||||
// 定义字典
|
||||
import {onFormLoadedUrl, onLoadDicUrl, remoteMethodByKey} from "/@/flow/components/convert-name/convert";
|
||||
import {validateNull} from "/@/utils/validate";
|
||||
import {handlePrintValue} from "/@/flow/components/form-create";
|
||||
|
||||
const props = defineProps({
|
||||
formCreateInject: {
|
||||
type: Object,
|
||||
default: {},
|
||||
},
|
||||
modelValue: {
|
||||
type: [Number, String, Array],
|
||||
default: null,
|
||||
},
|
||||
multiple: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
});
|
||||
|
||||
const emits = defineEmits(['update:modelValue']);
|
||||
|
||||
const dicData = reactive({});
|
||||
const onLoad = onLoadDicUrl();
|
||||
const onFormLoaded = onFormLoadedUrl({key: "posts", field: "modelValue"});
|
||||
onMounted(async () => {
|
||||
data.modelValue = props.modelValue
|
||||
await onFormLoaded(dicData, data);
|
||||
changePrintValue()
|
||||
});
|
||||
|
||||
const data = reactive({
|
||||
modelValue: null,
|
||||
updateMultiple: true
|
||||
})
|
||||
|
||||
function remoteMethod(query: string) {
|
||||
remoteMethodByKey(query, onLoad, dicData, 'postName', "posts")
|
||||
}
|
||||
|
||||
// 注入打印设计的内容,该逻辑非必须
|
||||
const changePrintValue = () => {
|
||||
if (!validateNull(data.modelValue)) {
|
||||
let printValue = handlePrintValue(dicData.posts, data.modelValue, 'postId', 'postName');
|
||||
if (printValue) props.formCreateInject.printValue = printValue
|
||||
else props.formCreateInject.printValue = ""
|
||||
} else {
|
||||
props.formCreateInject.printValue = ""
|
||||
}
|
||||
}
|
||||
|
||||
const changeModelValue = (value) => {
|
||||
data.modelValue = value
|
||||
emits('update:modelValue', value);
|
||||
changePrintValue()
|
||||
}
|
||||
|
||||
// 监听双向绑定
|
||||
watch(
|
||||
() => props.multiple,
|
||||
(val) => {
|
||||
data.updateMultiple = false
|
||||
changeModelValue(null)
|
||||
setTimeout(()=>{
|
||||
// 防止切换时报错
|
||||
data.updateMultiple = true
|
||||
})
|
||||
}
|
||||
);
|
||||
|
||||
</script>
|
||||
89
src/flow/components/user-role/role.vue
Normal file
89
src/flow/components/user-role/role.vue
Normal file
@@ -0,0 +1,89 @@
|
||||
<template>
|
||||
<div style="width: 100%" v-if="data.updateMultiple">
|
||||
<el-tooltip content="请输入角色名称进行模糊搜索" placement="top">
|
||||
<el-select v-model="data.modelValue" clearable filterable @change="changeModelValue"
|
||||
remote :remote-method="remoteMethod" :reserve-keyword="false"
|
||||
:multiple="props.multiple" :disabled="props.disabled">
|
||||
<el-option v-for="(item, index) in dicData.roles" :key="index" :label="item.roleName"
|
||||
:value="item.roleId"></el-option>
|
||||
</el-select>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="UserRolePickerIndex">
|
||||
// 定义字典
|
||||
import {onFormLoadedUrl, onLoadDicUrl, remoteMethodByKey} from "/@/flow/components/convert-name/convert";
|
||||
import {validateNull} from "/@/utils/validate";
|
||||
import {handlePrintValue} from "/@/flow/components/form-create";
|
||||
|
||||
const props = defineProps({
|
||||
formCreateInject: {
|
||||
type: Object,
|
||||
default: {},
|
||||
},
|
||||
modelValue: {
|
||||
type: [Number, String, Array],
|
||||
default: null,
|
||||
},
|
||||
multiple: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
});
|
||||
|
||||
const emits = defineEmits(['update:modelValue']);
|
||||
|
||||
const dicData = reactive({});
|
||||
const onLoad = onLoadDicUrl();
|
||||
const onFormLoaded = onFormLoadedUrl({key: "roles", field: "modelValue"});
|
||||
onMounted(async () => {
|
||||
data.modelValue = props.modelValue
|
||||
await onFormLoaded(dicData, data);
|
||||
changePrintValue()
|
||||
});
|
||||
|
||||
const data = reactive({
|
||||
modelValue: null,
|
||||
updateMultiple: true
|
||||
})
|
||||
|
||||
function remoteMethod(query: string) {
|
||||
remoteMethodByKey(query, onLoad, dicData, 'roleName', "roles")
|
||||
}
|
||||
|
||||
// 注入打印设计的内容,该逻辑非必须
|
||||
const changePrintValue = () => {
|
||||
if (!validateNull(data.modelValue)) {
|
||||
let printValue = handlePrintValue(dicData.roles, data.modelValue, 'roleId', 'roleName');
|
||||
if (printValue) props.formCreateInject.printValue = printValue
|
||||
else props.formCreateInject.printValue = ""
|
||||
} else {
|
||||
props.formCreateInject.printValue = ""
|
||||
}
|
||||
}
|
||||
|
||||
const changeModelValue = (value) => {
|
||||
data.modelValue = value
|
||||
emits('update:modelValue', value);
|
||||
changePrintValue()
|
||||
}
|
||||
|
||||
// 监听双向绑定
|
||||
watch(
|
||||
() => props.multiple,
|
||||
(val) => {
|
||||
data.updateMultiple = false
|
||||
changeModelValue(null)
|
||||
setTimeout(()=>{
|
||||
// 防止切换时报错
|
||||
data.updateMultiple = true
|
||||
})
|
||||
}
|
||||
);
|
||||
|
||||
</script>
|
||||
89
src/flow/components/user-role/user.vue
Normal file
89
src/flow/components/user-role/user.vue
Normal file
@@ -0,0 +1,89 @@
|
||||
<template>
|
||||
<div style="width: 100%" v-if="data.updateMultiple">
|
||||
<el-tooltip content="请输入用户名称进行模糊搜索" placement="top">
|
||||
<el-select v-model="data.modelValue" clearable filterable @change="changeModelValue"
|
||||
remote :remote-method="remoteMethod" :reserve-keyword="false"
|
||||
:multiple="props.multiple" :disabled="props.disabled">
|
||||
<el-option v-for="(item, index) in dicData.users" :key="index" :label="item.name"
|
||||
:value="item.userId"></el-option>
|
||||
</el-select>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="UserRolePickerIndex">
|
||||
// 定义字典
|
||||
import {onFormLoadedUrl, onLoadDicUrl, remoteMethodByKey} from "/@/flow/components/convert-name/convert";
|
||||
import {validateNull} from "/@/utils/validate";
|
||||
import {handlePrintValue} from "/@/flow/components/form-create";
|
||||
|
||||
const props = defineProps({
|
||||
formCreateInject: {
|
||||
type: Object,
|
||||
default: {},
|
||||
},
|
||||
modelValue: {
|
||||
type: [Number, String, Array],
|
||||
default: null,
|
||||
},
|
||||
multiple: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
});
|
||||
|
||||
const emits = defineEmits(['update:modelValue']);
|
||||
|
||||
const dicData = reactive({});
|
||||
const onLoad = onLoadDicUrl();
|
||||
const onFormLoaded = onFormLoadedUrl({key: "users", field: "modelValue"});
|
||||
onMounted(async () => {
|
||||
data.modelValue = props.modelValue
|
||||
await onFormLoaded(dicData, data);
|
||||
changePrintValue()
|
||||
});
|
||||
|
||||
const data = reactive({
|
||||
modelValue: null,
|
||||
updateMultiple: true
|
||||
})
|
||||
|
||||
function remoteMethod(query: string) {
|
||||
remoteMethodByKey(query, onLoad, dicData, 'userName', "users")
|
||||
}
|
||||
|
||||
// 注入打印设计的内容,该逻辑非必须
|
||||
const changePrintValue = () => {
|
||||
if (!validateNull(data.modelValue)) {
|
||||
let printValue = handlePrintValue(dicData.users, data.modelValue, 'userId', 'name');
|
||||
if (printValue) props.formCreateInject.printValue = printValue
|
||||
else props.formCreateInject.printValue = ""
|
||||
} else {
|
||||
props.formCreateInject.printValue = ""
|
||||
}
|
||||
}
|
||||
|
||||
const changeModelValue = (value) => {
|
||||
data.modelValue = value
|
||||
emits('update:modelValue', value);
|
||||
changePrintValue()
|
||||
}
|
||||
|
||||
// 监听双向绑定
|
||||
watch(
|
||||
() => props.multiple,
|
||||
(val) => {
|
||||
data.updateMultiple = false
|
||||
changeModelValue(null)
|
||||
setTimeout(()=>{
|
||||
// 防止切换时报错
|
||||
data.updateMultiple = true
|
||||
})
|
||||
}
|
||||
);
|
||||
|
||||
</script>
|
||||
100
src/flow/designer/api/jsonflow/index.ts
Normal file
100
src/flow/designer/api/jsonflow/index.ts
Normal file
@@ -0,0 +1,100 @@
|
||||
import request from "/@/utils/request"
|
||||
|
||||
export function predictFlow(obj?: Object, query?: Object) {
|
||||
return request({
|
||||
url: '/jsonflow/run-flow/predict',
|
||||
method: 'post',
|
||||
data: obj,
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
export function delFlowInfo(obj: any) {
|
||||
return request({
|
||||
url: '/jsonflow/run-flow/del/flow/info',
|
||||
method: 'delete',
|
||||
data: obj
|
||||
})
|
||||
}
|
||||
|
||||
export function getNodesByIdType(id, flowType, isEdit) {
|
||||
// flowType: 0 模板 1 实例
|
||||
// viewType: 0 查看 1 编辑
|
||||
if (flowType === '1') return getNodesByFlowInstId(id, isEdit)
|
||||
else return getNodesByDefFlowId(id)
|
||||
}
|
||||
|
||||
export function getNodesByDefFlowId(id) {
|
||||
return request({
|
||||
url: '/jsonflow/def-flow/nodes/' + id,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
export function getNodesByFlowInstId(id, isEdit) {
|
||||
return request({
|
||||
url: '/jsonflow/run-flow/nodes/' + id + "/" + isEdit,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
export function addObjByDefFlowId(obj) {
|
||||
return request({
|
||||
url: '/jsonflow/def-flow',
|
||||
method: 'post',
|
||||
data: obj
|
||||
})
|
||||
}
|
||||
|
||||
export function addObjByFlowInstId(obj) {
|
||||
return request({
|
||||
url: '/jsonflow/run-flow/flow-inst-id',
|
||||
method: 'post',
|
||||
data: obj
|
||||
})
|
||||
}
|
||||
|
||||
export function listRunFlowsByDefFlowId(defFlowId, version) {
|
||||
return request({
|
||||
url: '/jsonflow/run-flow/def-flow-id/' + defFlowId + "/" + version,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
export function listDefFlowByFlowKey(flowKey) {
|
||||
return request({
|
||||
url: '/jsonflow/def-flow/flow-key/' + flowKey,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
export function listFlowApplicationByFlowKey(flowKey) {
|
||||
return request({
|
||||
url: '/order/flow-application/flow-key/' + flowKey,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
export function fetchComment(query) {
|
||||
return request({
|
||||
url: '/jsonflow/comment/comment/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
export function fetchRunJobs(flowInstId) {
|
||||
return request({
|
||||
url: '/jsonflow/run-job/list/' + flowInstId,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
export function fetchRunRejects(flowInstId) {
|
||||
return request({
|
||||
url: '/jsonflow/run-reject/list/' + flowInstId,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
76
src/flow/designer/assets/style/flow-area.scss
Normal file
76
src/flow/designer/assets/style/flow-area.scss
Normal file
@@ -0,0 +1,76 @@
|
||||
.child-ul-wrapper {
|
||||
padding-left: 1px !important;
|
||||
}
|
||||
|
||||
.jf-wrap-paper {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
//position: relative;
|
||||
position: absolute;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
#jfDragWrapPaper {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.jsonflow-navigator{
|
||||
position: absolute;
|
||||
bottom: 12px;
|
||||
right: 8px;
|
||||
border: #ccc solid 1px;
|
||||
overflow: hidden;
|
||||
|
||||
.minimap-view{
|
||||
position: absolute;
|
||||
left: 2px;
|
||||
top: 2px;
|
||||
border: 2px dotted #999;
|
||||
cursor: move;
|
||||
}
|
||||
}
|
||||
|
||||
.jf-wrap-paper-active {
|
||||
background-color: #e4e4e4;
|
||||
cursor: crosshair;
|
||||
}
|
||||
|
||||
.container-scale {
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
right: 10px;
|
||||
|
||||
> span {
|
||||
display: inline-block;
|
||||
width: 40px;
|
||||
text-align: center;
|
||||
font-size: 14px;
|
||||
color: rgba(0, 0, 0, .65);
|
||||
}
|
||||
|
||||
/*缩放按钮*/
|
||||
.el-button--small.is-circle {
|
||||
padding: 5px 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.horizontal-line-x {
|
||||
position: absolute;
|
||||
//width: 5000px;
|
||||
height: 1px;
|
||||
background: #409EFF;
|
||||
//margin-left: -2500px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.vertical-line-y {
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
//height: 5000px;
|
||||
background: #409EFF;
|
||||
//margin-top: -2500px;
|
||||
display: none;
|
||||
}
|
||||
63
src/flow/designer/assets/style/flow-attr.scss
Normal file
63
src/flow/designer/assets/style/flow-attr.scss
Normal file
@@ -0,0 +1,63 @@
|
||||
.flow-attr {
|
||||
/*属性面板*/
|
||||
.el-form-item {
|
||||
margin: 0 15px 10px!important;
|
||||
}
|
||||
|
||||
/*属性面板在一行*/
|
||||
.el-form--label-top .el-form-item__label{
|
||||
display: inline-block;
|
||||
text-align: left;
|
||||
padding: 0 0 10px!important;
|
||||
}
|
||||
|
||||
/*tab选中样式*/
|
||||
.el-tabs__active-bar {
|
||||
background-color: revert;
|
||||
}
|
||||
|
||||
/*tab头居中*/
|
||||
.el-tabs__header{
|
||||
margin: 8px 20px!important;
|
||||
}
|
||||
|
||||
.el-form-item__content {
|
||||
.el-select {
|
||||
width: 303px!important;
|
||||
}
|
||||
}
|
||||
|
||||
.input-attr {
|
||||
width: 303px;
|
||||
|
||||
.el-input-group__prepend {
|
||||
background-color: #F5F7FA;
|
||||
width: 97px;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.flow-config-attr {
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.flow-param-attr {
|
||||
margin-left: 25px;
|
||||
}
|
||||
|
||||
.audit-endpoint {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 200px;
|
||||
}
|
||||
|
||||
.audit-endpoint-extract {
|
||||
position: absolute;
|
||||
bottom: 9px;
|
||||
left: 150px;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
93
src/flow/designer/assets/style/flow-designer.scss
Normal file
93
src/flow/designer/assets/style/flow-designer.scss
Normal file
@@ -0,0 +1,93 @@
|
||||
.flow-container {
|
||||
height: 100%;
|
||||
-moz-user-select: none;
|
||||
-webkit-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.select-area {
|
||||
position: relative;
|
||||
z-index: 1001;
|
||||
box-shadow: 0 3px 5px #ddd;
|
||||
width: 54px;
|
||||
background: #fff;
|
||||
padding-top: 6px;
|
||||
.el-row-tab {
|
||||
text-align: center;
|
||||
margin-bottom: 10px;
|
||||
font-size: 12px;
|
||||
height: 26px;
|
||||
line-height: 24px;
|
||||
margin-top: 7px;
|
||||
margin-left: 17px;
|
||||
}
|
||||
/*左侧菜单栏*/
|
||||
.el-row {
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.header-option {
|
||||
background: #fff;
|
||||
height: 46px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
box-shadow: 0 3px 5px #ddd;
|
||||
position: relative;
|
||||
z-index: 1000;
|
||||
&__tools {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
width: 570px;
|
||||
|
||||
.el-select__wrapper {
|
||||
border-radius: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.el-button {
|
||||
height: 30px;
|
||||
border-radius: 16px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.el-button+.el-button {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.el-input__wrapper {
|
||||
height: 30px;
|
||||
border-radius: 16px;
|
||||
}
|
||||
.el-input__inner {
|
||||
width: 70px;
|
||||
}
|
||||
}
|
||||
|
||||
.header-view-option {
|
||||
background: #fff;
|
||||
height: 46px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
z-index: 1000;
|
||||
&__tools {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
.header-option-button {
|
||||
border: 0;
|
||||
padding: 8px 8px;
|
||||
}
|
||||
|
||||
.flow-content {
|
||||
background: #fafafa;
|
||||
height: 100%;
|
||||
border: 1px dashed rgba(170, 170, 170, 0.7);
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
3
src/flow/designer/assets/style/flow-paper.css
Normal file
3
src/flow/designer/assets/style/flow-paper.css
Normal file
File diff suppressed because one or more lines are too long
15
src/flow/designer/assets/svges/index.ts
Normal file
15
src/flow/designer/assets/svges/index.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import {nodeJobSvgIcons as nodeJobPath} from "./path";
|
||||
|
||||
// SVG ICON图标
|
||||
export const nodeJobSvgIcons = {
|
||||
serial: nodeJobPath.serial,
|
||||
serialGate: nodeJobPath.serialGate
|
||||
,parallel: nodeJobPath.parallel
|
||||
,parallelGate: nodeJobPath.parallelGate
|
||||
,job: nodeJobPath.job
|
||||
,virtual: '/flow/menu/virtual-icon.svg'
|
||||
,startMenu: '/flow/menu/start.svg'
|
||||
,endMenu: '/flow/menu/end.svg'
|
||||
,xLaneMenu: '/flow/menu/x-lane.svg'
|
||||
,yLaneMenu: '/flow/menu/y-lane.svg'
|
||||
}
|
||||
17
src/flow/designer/assets/svges/path.ts
Normal file
17
src/flow/designer/assets/svges/path.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
// SVG ICON图标
|
||||
export const nodeJobSvgIcons = {
|
||||
start: "/flow/icons/start-icon.svg"
|
||||
,arrow: "/flow/icons/arrow-icon.svg"
|
||||
,serial: "/flow/icons/serial-icon.svg"
|
||||
,serialGate: "/flow/icons/serialGate.svg"
|
||||
,parallel: "/flow/icons/parallel-icon.svg"
|
||||
,parallelGate: "/flow/icons/parallelGate.svg"
|
||||
,linkIcon: "/flow/icons/link-icon.svg"
|
||||
,job: "/flow/icons/job-icon.svg"
|
||||
,virtual: "/flow/icons/virtual-icon.svg"
|
||||
,success: "/flow/icons/success.svg"
|
||||
,warning: "/flow/icons/warning.svg"
|
||||
,loading: "/flow/icons/loading.svg"
|
||||
}
|
||||
|
||||
window._shapesSvgPath = nodeJobSvgIcons
|
||||
138
src/flow/designer/components/flow-area-view.vue
Normal file
138
src/flow/designer/components/flow-area-view.vue
Normal 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>
|
||||
384
src/flow/designer/components/flow-area.vue
Normal file
384
src/flow/designer/components/flow-area.vue
Normal 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>
|
||||
1303
src/flow/designer/components/flow-attr.vue
Normal file
1303
src/flow/designer/components/flow-attr.vue
Normal file
File diff suppressed because it is too large
Load Diff
411
src/flow/designer/components/flow-clazz.vue
Normal file
411
src/flow/designer/components/flow-clazz.vue
Normal 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>
|
||||
527
src/flow/designer/components/flow-con-rule.vue
Normal file
527
src/flow/designer/components/flow-con-rule.vue
Normal 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>
|
||||
125
src/flow/designer/components/flow-curr-job.vue
Normal file
125
src/flow/designer/components/flow-curr-job.vue
Normal 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>
|
||||
207
src/flow/designer/components/flow-form-http.vue
Normal file
207
src/flow/designer/components/flow-form-http.vue
Normal 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>
|
||||
295
src/flow/designer/components/flow-form-perm.vue
Normal file
295
src/flow/designer/components/flow-form-perm.vue
Normal 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>
|
||||
352
src/flow/designer/components/flow-http-param.vue
Normal file
352
src/flow/designer/components/flow-http-param.vue
Normal 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>
|
||||
360
src/flow/designer/components/flow-method.vue
Normal file
360
src/flow/designer/components/flow-method.vue
Normal 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><{{ data.whoseLeaderName }} 用户>的第  </span>
|
||||
<el-input-number :min="1" :max="20" :step="1" style="width: 150px"
|
||||
v-model="data.leaderLevel"></el-input-number>
|
||||
<span>  级主管</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">不超过<{{ data.whoseLeaderName }} 用户>的</el-radio>
|
||||
</el-radio-group>
|
||||
<div class="audit-endpoint">
|
||||
第 <el-input-number :min="1" :max="20" :step="1" style="width: 80px" controls-position="right"
|
||||
v-model="data.leaderLevel"></el-input-number> 级主管
|
||||
</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>
|
||||
810
src/flow/designer/components/flow-node-route.vue
Normal file
810
src/flow/designer/components/flow-node-route.vue
Normal 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>
|
||||
361
src/flow/designer/components/flow-sub-param.vue
Normal file
361
src/flow/designer/components/flow-sub-param.vue
Normal 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>
|
||||
666
src/flow/designer/components/flow-user-rule.vue
Normal file
666
src/flow/designer/components/flow-user-rule.vue
Normal 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>
|
||||
252
src/flow/designer/components/index.ts
Normal file
252
src/flow/designer/components/index.ts
Normal 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');
|
||||
}
|
||||
87
src/flow/designer/components/node-menu.vue
Normal file
87
src/flow/designer/components/node-menu.vue
Normal 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>
|
||||
127
src/flow/designer/config/attr-config.ts
Normal file
127
src/flow/designer/config/attr-config.ts
Normal file
@@ -0,0 +1,127 @@
|
||||
import {deepClone} from "/@/utils/other";
|
||||
|
||||
const clazzAttr = {
|
||||
clazzes: [],
|
||||
}
|
||||
|
||||
const fieldsAttr = {
|
||||
formFieldPerms: [],
|
||||
formId: null,
|
||||
}
|
||||
|
||||
const nodeJobLinkAttr = {
|
||||
condGroups: [],
|
||||
httpMethod: null,
|
||||
httpParams: [],
|
||||
}
|
||||
|
||||
const subFlowAttr = {
|
||||
subFlowParams: [],
|
||||
startSubFlow: null,
|
||||
restartSubFlow: null,
|
||||
backParFlow: null,
|
||||
startSubMethod: null,
|
||||
restartSubMethod: null,
|
||||
backParMethod: null,
|
||||
}
|
||||
|
||||
const flowNodeAttr = {
|
||||
...deepClone(clazzAttr),
|
||||
...deepClone(nodeJobLinkAttr),
|
||||
...deepClone(fieldsAttr),
|
||||
...deepClone(subFlowAttr),
|
||||
}
|
||||
|
||||
const commonAttr = {
|
||||
jobBtns: null,
|
||||
carbonCopy: null,
|
||||
approveMethod: '1',
|
||||
ticketCompRate: 100,
|
||||
routeKeyVal: null,
|
||||
valType: null,
|
||||
...deepClone(flowNodeAttr),
|
||||
pcTodoUrl: null,
|
||||
subDefFlowId: null,
|
||||
subFlowVersion: '0',
|
||||
timeout: 0,
|
||||
sort: 1,
|
||||
}
|
||||
|
||||
export const startAttr = {
|
||||
...deepClone(commonAttr),
|
||||
rejectType: '0'
|
||||
}
|
||||
|
||||
export const syncNodeAttr = {
|
||||
...deepClone(commonAttr),
|
||||
nodeApproveMethod: '1',
|
||||
isAutoNext: '1',
|
||||
rejectType: '0',
|
||||
isContinue: '0',
|
||||
isAutoAudit: '0',
|
||||
isPassSame: '0'
|
||||
}
|
||||
|
||||
export const nodeAttr = {
|
||||
description: '',
|
||||
...deepClone(syncNodeAttr),
|
||||
}
|
||||
|
||||
export const gateAttr = {
|
||||
...deepClone(flowNodeAttr),
|
||||
nodeApproveMethod: '1',
|
||||
isAutoNext: '1',
|
||||
isAutoAudit: '1',
|
||||
isGateway: '1',
|
||||
}
|
||||
|
||||
const baseJobAttr = {
|
||||
...deepClone(nodeJobLinkAttr),
|
||||
valType: '-2',
|
||||
userKey: null,
|
||||
userKeyVal: null,
|
||||
isNowRun: '0',
|
||||
timeout: 0,
|
||||
isSkipRejected: '0',
|
||||
belongType: null,
|
||||
sort: 1,
|
||||
}
|
||||
|
||||
export const jobAttr = {
|
||||
jobName: '节点任务',
|
||||
...deepClone(baseJobAttr),
|
||||
distFlowNodeId: null,
|
||||
}
|
||||
|
||||
// currRunJobs
|
||||
export const syncJobAttr = {
|
||||
...deepClone(baseJobAttr),
|
||||
userKeyValName: null,
|
||||
roleUserId: [],
|
||||
}
|
||||
|
||||
export const endAttr = {
|
||||
...deepClone(commonAttr),
|
||||
isAutoAudit: '1',
|
||||
}
|
||||
|
||||
export const virtualAttr = {
|
||||
...deepClone(commonAttr),
|
||||
rejectType: '1'
|
||||
}
|
||||
|
||||
export const highAttr = {
|
||||
childFlowKey: null,
|
||||
childOrderId: null
|
||||
}
|
||||
|
||||
export const linkAttr = {
|
||||
...deepClone(nodeJobLinkAttr),
|
||||
valType: null,
|
||||
varKeyVal: null,
|
||||
}
|
||||
|
||||
export const laneAttr = {
|
||||
id: null,
|
||||
nodeName: null,
|
||||
}
|
||||
354
src/flow/designer/config/flow-config.ts
Normal file
354
src/flow/designer/config/flow-config.ts
Normal file
@@ -0,0 +1,354 @@
|
||||
import {nodeJobSvgIcons as nodeJobPath} from "/@/flow/designer/assets/svges/path";
|
||||
|
||||
const clazzAttr = {
|
||||
clazzes: [],
|
||||
}
|
||||
|
||||
export const flowAttr = {
|
||||
id: null,
|
||||
...clazzAttr,
|
||||
isIndependent: '0',
|
||||
allowJobLink: '0',
|
||||
isJobSeparated: '0',
|
||||
isSimpleMode: '1',
|
||||
connector: null,
|
||||
router: null,
|
||||
queryOrder: null,
|
||||
updateOrder: null,
|
||||
queryMethod: null,
|
||||
updateMethod: null,
|
||||
orderParams: [],
|
||||
flowName: null,
|
||||
flowKey: null,
|
||||
groupName: null,
|
||||
formId: null,
|
||||
fromType: '1',
|
||||
version: 1,
|
||||
status: '-1',
|
||||
remark: null,
|
||||
isNew: true,
|
||||
sort: 1
|
||||
}
|
||||
|
||||
export let flowConfig = {
|
||||
// ID类型
|
||||
// 1.uuid uuid 2.time_stamp 时间戳 3.sequence 序列 4.time_stamp_and_sequence 时间戳加序列 5.custom 自定义
|
||||
idType: ['time_stamp_and_sequence', 6],
|
||||
flowData: {
|
||||
// null时init
|
||||
graph: null,
|
||||
nodeList: [],
|
||||
linkList: [],
|
||||
attrs: flowAttr,
|
||||
},
|
||||
mobileConfig: {
|
||||
mobilePrefix: "appdata",
|
||||
mobileCode: "jf320920",
|
||||
},
|
||||
globalConfig: {
|
||||
isHideShortcut: '0',
|
||||
allowJobLink: '0',
|
||||
isJobSeparated: '0',
|
||||
isSimpleMode: '1',
|
||||
connectors: [{value: "normal", label: "简单"}, {value: "rounded", label: "圆角"}, {
|
||||
value: "jumpover",
|
||||
label: "跳线"
|
||||
}, {value: "smooth", label: "平滑"}],
|
||||
routers: [{value: "normal", label: "简单"}, {value: "manhattan", label: "智能正交"}, {
|
||||
value: "metro",
|
||||
label: "智能地铁线"
|
||||
}, {value: "orthogonal", label: "垂直直角"}, {value: "oneSide", label: "受限正交"}],
|
||||
rankDirs: [{value: "TB", label: "上下"}, {value: "BT", label: "下上"}, {value: "LR", label: "左右"}, {
|
||||
value: "RL",
|
||||
label: "右左"
|
||||
}],
|
||||
defaultAutoLayoutName: "TB",
|
||||
defaultConnectorName: "rounded",
|
||||
defaultRouterName: "normal",
|
||||
maxNodeJobNum: 50
|
||||
},
|
||||
gridConfig: {
|
||||
showGrid: false,
|
||||
showGridText: "显示网格",
|
||||
showGridIcon: "SwitchButton",
|
||||
gridSize: 1
|
||||
},
|
||||
defaultStyle: {
|
||||
containerScale: {
|
||||
init: 1
|
||||
}
|
||||
},
|
||||
viewShortcut: {
|
||||
paper: {
|
||||
codeName: '鼠标滚动或点击画布拖拽或小地图',
|
||||
shortcutName: '拖动画布'
|
||||
},
|
||||
flow: {
|
||||
codeName: '鼠标点击画布隐藏节点信息',
|
||||
shortcutName: '隐藏节点信息'
|
||||
},
|
||||
node: {
|
||||
codeName: '鼠标放节点上即可查看',
|
||||
shortcutName: '查看节点信息'
|
||||
},
|
||||
link: {
|
||||
codeName: '鼠标放连线上即可查看',
|
||||
shortcutName: '查看连线信息'
|
||||
},
|
||||
zoomInTool: {
|
||||
codeName: '画布右上角',
|
||||
shortcutName: '放大工具'
|
||||
},
|
||||
zoomOutTool: {
|
||||
codeName: '画布右上角',
|
||||
shortcutName: '缩小工具'
|
||||
}
|
||||
},
|
||||
shortcut: {
|
||||
note: {
|
||||
codeName: '图中节点、连线均可拖拽(专业模式),缩放大小',
|
||||
shortcutName: '简约但不简单'
|
||||
},
|
||||
flow: {
|
||||
codeName: '左上角操作栏',
|
||||
shortcutName: '切换简单模式与专业模式'
|
||||
},
|
||||
flow2: {
|
||||
codeName: '左上角操作栏',
|
||||
shortcutName: '设置流程属性'
|
||||
},
|
||||
link: {
|
||||
codeName: '左上角操作栏',
|
||||
shortcutName: '设置连线、路由、布局'
|
||||
},
|
||||
paper: {
|
||||
codeName: '鼠标滚动或点击画布拖拽或小地图',
|
||||
shortcutName: '拖动画布'
|
||||
},
|
||||
node: {
|
||||
codeName: '从左侧节点菜单栏拖拽节点(专业模式)',
|
||||
shortcutName: '新增加点'
|
||||
},
|
||||
node1: {
|
||||
codeName: '鼠标点击节点拖动右下角灰色小圈',
|
||||
shortcutName: '缩放节点大小'
|
||||
},
|
||||
node2: {
|
||||
codeName: '鼠标放节点边缘显示(+)开始拖出连线',
|
||||
shortcutName: '节点连线'
|
||||
},
|
||||
node3: {
|
||||
codeName: '鼠标放节点文本上开始拖动节点',
|
||||
shortcutName: '拖拽节点'
|
||||
},
|
||||
node4: {
|
||||
codeName: '鼠标放节点上双击或右键菜单(专业模式)设置',
|
||||
shortcutName: '设置节点属性'
|
||||
},
|
||||
node41: {
|
||||
codeName: '鼠标放节点的任务项上单击(节点任务不分离时)设置',
|
||||
shortcutName: '设置任务属性'
|
||||
},
|
||||
node5: {
|
||||
codeName: '鼠标放节点上右键菜单删除(专业模式)或点击再点X删除',
|
||||
shortcutName: '删除节点'
|
||||
},
|
||||
link2: {
|
||||
codeName: '鼠标放连线上点击增加拖拽点拖动(专业模式)',
|
||||
shortcutName: '拖拽连线'
|
||||
},
|
||||
link3: {
|
||||
codeName: '鼠标放连线拖拽点上再次点击删除(专业模式)',
|
||||
shortcutName: '删除拖拽点'
|
||||
},
|
||||
link4: {
|
||||
codeName: '鼠标放连线上双击(简单模式)或右键菜单(专业模式)设置',
|
||||
shortcutName: '设置连线属性'
|
||||
},
|
||||
link5: {
|
||||
codeName: '鼠标放连线上点X删除或右键菜单(专业模式)删除',
|
||||
shortcutName: '删除连线'
|
||||
},
|
||||
zoomInTool: {
|
||||
codeName: '画布右上角',
|
||||
shortcutName: '放大工具'
|
||||
},
|
||||
zoomOutTool: {
|
||||
codeName: '画布右上角',
|
||||
shortcutName: '缩小工具'
|
||||
}
|
||||
},
|
||||
contextMenu: {
|
||||
container: {
|
||||
menuName: 'flow-menu',
|
||||
axis: {
|
||||
x: null,
|
||||
y: null
|
||||
},
|
||||
menulists: [
|
||||
{
|
||||
fnHandler: 'flowInfo',
|
||||
icoName: 'iconfont icon-xianshimima',
|
||||
btnName: '流程图信息'
|
||||
},
|
||||
{
|
||||
fnHandler: 'paste',
|
||||
icoName: 'iconfont icon-fuzhiyemian',
|
||||
btnName: '粘贴'
|
||||
}
|
||||
]
|
||||
},
|
||||
node: {
|
||||
menuName: 'node-menu',
|
||||
axis: {
|
||||
x: null,
|
||||
y: null
|
||||
},
|
||||
menulists: [
|
||||
{
|
||||
fnHandler: 'setNodeAttr',
|
||||
icoName: 'iconfont icon-quanjushezhi_o',
|
||||
btnName: '设置属性'
|
||||
},
|
||||
{
|
||||
fnHandler: 'setConnectNode',
|
||||
icoName: 'iconfont icon-shuxingtu',
|
||||
btnName: '连接节点'
|
||||
},
|
||||
{
|
||||
fnHandler: 'copyNode',
|
||||
icoName: 'iconfont icon-fuzhiyemian',
|
||||
btnName: '复制节点'
|
||||
},
|
||||
{
|
||||
fnHandler: 'deleteNode',
|
||||
icoName: 'iconfont icon-yincangmima',
|
||||
btnName: '删除节点'
|
||||
}
|
||||
]
|
||||
},
|
||||
nodeConnect: {
|
||||
menuName: 'node-connect-menu',
|
||||
axis: {
|
||||
x: null,
|
||||
y: null
|
||||
},
|
||||
menulists: [
|
||||
{
|
||||
fnHandler: 'setConnectNode',
|
||||
icon: nodeJobPath.linkIcon,
|
||||
icoName: 'iconfont icon-tuodong',
|
||||
btnName: '连接节点',
|
||||
nodeConnect: true,
|
||||
},
|
||||
{
|
||||
fnHandler: 'setSerialNode',
|
||||
icon: nodeJobPath.serial,
|
||||
icoName: 'iconfont icon-icon-',
|
||||
btnName: '串行节点',
|
||||
nodeConnect: true,
|
||||
},
|
||||
{
|
||||
fnHandler: 'setParallelNode',
|
||||
icon: nodeJobPath.parallel,
|
||||
icoName: 'iconfont icon-shuxingtu',
|
||||
btnName: '并行节点',
|
||||
nodeConnect: true,
|
||||
},
|
||||
{
|
||||
fnHandler: 'setSerialGate',
|
||||
icon: nodeJobPath.serialGate,
|
||||
icoName: 'iconfont icon-shuaxin',
|
||||
btnName: '串行网关',
|
||||
nodeConnect: true,
|
||||
},
|
||||
{
|
||||
fnHandler: 'setParallelGate',
|
||||
icon: nodeJobPath.parallelGate,
|
||||
icoName: 'iconfont icon-tuodong',
|
||||
btnName: '并行网关',
|
||||
nodeConnect: true,
|
||||
}
|
||||
]
|
||||
},
|
||||
link: {
|
||||
menuName: 'link-menu',
|
||||
axis: {
|
||||
x: null,
|
||||
y: null
|
||||
},
|
||||
menulists: [
|
||||
{
|
||||
fnHandler: 'setLinkAttr',
|
||||
icoName: 'iconfont icon-quanjushezhi_o',
|
||||
btnName: '设置属性'
|
||||
},
|
||||
{
|
||||
fnHandler: 'modifySourceNode',
|
||||
icoName: 'iconfont icon-step',
|
||||
icoStyle: 'font-size: 13px',
|
||||
btnName: '修改起点'
|
||||
},
|
||||
{
|
||||
fnHandler: 'modifyTargetNode',
|
||||
icoName: 'iconfont icon-radio-off-full',
|
||||
icoStyle: 'font-size: 13px',
|
||||
btnName: '修改终点'
|
||||
},
|
||||
{
|
||||
fnHandler: 'deleteLink',
|
||||
icoName: 'iconfont icon-yincangmima',
|
||||
btnName: '删除连线'
|
||||
}
|
||||
]
|
||||
},
|
||||
nodeView: {
|
||||
menuName: 'node-menu',
|
||||
axis: {
|
||||
x: null,
|
||||
y: null
|
||||
},
|
||||
menulists: [
|
||||
{
|
||||
fnHandler: '',
|
||||
icoName: 'iconfont icon-shuaxin',
|
||||
btnName: '节点名称: '
|
||||
},
|
||||
{
|
||||
fnHandler: '',
|
||||
icoName: 'iconfont icon-xianshimima',
|
||||
btnName: '审批开始时间: '
|
||||
},
|
||||
{
|
||||
fnHandler: '',
|
||||
icoName: 'iconfont icon-gerenzhongxin',
|
||||
btnName: '参与者: '
|
||||
},
|
||||
{
|
||||
fnHandler: '',
|
||||
icoName: 'iconfont icon-icon-',
|
||||
btnName: '审批人: '
|
||||
},
|
||||
{
|
||||
fnHandler: '',
|
||||
icoName: 'iconfont icon-tongzhi1',
|
||||
btnName: '审批备注: '
|
||||
}
|
||||
]
|
||||
},
|
||||
linkView: {
|
||||
menuName: 'link-menu',
|
||||
axis: {
|
||||
x: null,
|
||||
y: null
|
||||
},
|
||||
menulists: [
|
||||
{
|
||||
fnHandler: '',
|
||||
icoName: 'iconfont icon-shuxingtu',
|
||||
btnName: '连线条件: '
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
70
src/flow/designer/config/menu-config.ts
Normal file
70
src/flow/designer/config/menu-config.ts
Normal file
@@ -0,0 +1,70 @@
|
||||
import {CommonNodeType, HighNodeType, LaneNodeType} from './type'
|
||||
import {nodeJobSvgIcons} from "../assets/svges";
|
||||
|
||||
export const commonNodes = [
|
||||
{
|
||||
type: CommonNodeType.START,
|
||||
nodeName: '开始',
|
||||
icon: nodeJobSvgIcons.startMenu
|
||||
},
|
||||
{
|
||||
type: CommonNodeType.SERIAL,
|
||||
nodeName: '串行节点',
|
||||
nodeDesc: '(表示流程同时只能流转到一个串行分支)',
|
||||
icon: nodeJobSvgIcons.serial
|
||||
},
|
||||
{
|
||||
type: CommonNodeType.PARALLEL,
|
||||
nodeName: '并行节点',
|
||||
nodeDesc: '(表示流程同时可以流转到多个并行分支)',
|
||||
icon: nodeJobSvgIcons.parallel
|
||||
},
|
||||
{
|
||||
type: CommonNodeType.END,
|
||||
nodeName: '结束',
|
||||
icon: nodeJobSvgIcons.endMenu
|
||||
}
|
||||
];
|
||||
|
||||
export const highNodes = [
|
||||
{
|
||||
type: HighNodeType.VIRTUAL,
|
||||
nodeName: '虚拟节点',
|
||||
nodeDesc: '(可选的自由节点,不常用)',
|
||||
icon: nodeJobSvgIcons.virtual
|
||||
},
|
||||
{
|
||||
type: HighNodeType.JOB,
|
||||
nodeName: '节点任务',
|
||||
nodeDesc: '(可选的辅助节点,不常用)',
|
||||
icon: nodeJobSvgIcons.job
|
||||
},
|
||||
{
|
||||
type: CommonNodeType.SERIAL,
|
||||
nodeName: '串行网关',
|
||||
icon: nodeJobSvgIcons.serialGate
|
||||
},
|
||||
{
|
||||
type: CommonNodeType.PARALLEL,
|
||||
nodeName: '并行网关',
|
||||
icon: nodeJobSvgIcons.parallelGate
|
||||
},
|
||||
{
|
||||
type: HighNodeType.CHILD_FLOW,
|
||||
nodeName: '子流程',
|
||||
icon: 'set-up'
|
||||
}
|
||||
];
|
||||
|
||||
export const laneNodes = [
|
||||
{
|
||||
type: LaneNodeType.Y_LANE,
|
||||
nodeName: '纵向泳道',
|
||||
icon: nodeJobSvgIcons.yLaneMenu
|
||||
},
|
||||
{
|
||||
type: LaneNodeType.X_LANE,
|
||||
nodeName: '横向泳道',
|
||||
icon: nodeJobSvgIcons.xLaneMenu
|
||||
}
|
||||
];
|
||||
110
src/flow/designer/config/node-config.ts
Normal file
110
src/flow/designer/config/node-config.ts
Normal file
@@ -0,0 +1,110 @@
|
||||
import { CommonNodeType, HighNodeType, LaneNodeType } from './type'
|
||||
import {
|
||||
endAttr, gateAttr,
|
||||
jobAttr,
|
||||
laneAttr,
|
||||
linkAttr,
|
||||
nodeAttr, startAttr,
|
||||
virtualAttr
|
||||
} from "./attr-config";
|
||||
import JsonflowDesign from "@jackrolling/jsonflow3"
|
||||
import {deepClone} from "/@/utils/other";
|
||||
|
||||
export function initNodeShapes() {
|
||||
|
||||
window._defShapes = JsonflowDesign.shapes.initDefShapes();
|
||||
let initShapes = JsonflowDesign.shapes.initShapes(true);
|
||||
|
||||
let cdata = window._defShapes.cdata
|
||||
|
||||
initShapes.start.attr(cdata, {
|
||||
type: CommonNodeType.START,
|
||||
attrs: deepClone(startAttr),
|
||||
defJob: deepClone(jobAttr),
|
||||
})
|
||||
|
||||
initShapes.serial.attr(cdata, {
|
||||
type: CommonNodeType.SERIAL,
|
||||
attrs: deepClone(nodeAttr),
|
||||
defJob: deepClone(jobAttr),
|
||||
})
|
||||
|
||||
initShapes.parallel.attr(cdata, {
|
||||
type: CommonNodeType.PARALLEL,
|
||||
attrs: deepClone(nodeAttr),
|
||||
defJob: deepClone(jobAttr),
|
||||
})
|
||||
|
||||
initShapes.serialGate.attr(cdata, {
|
||||
type: CommonNodeType.SERIAL,
|
||||
attrs: deepClone(gateAttr),
|
||||
defJob: deepClone(jobAttr)
|
||||
})
|
||||
|
||||
initShapes.parallelGate.attr(cdata, {
|
||||
type: CommonNodeType.PARALLEL,
|
||||
attrs: deepClone(gateAttr),
|
||||
defJob: deepClone(jobAttr)
|
||||
})
|
||||
|
||||
initShapes.end.attr(cdata, {
|
||||
type: CommonNodeType.END,
|
||||
attrs: deepClone(endAttr),
|
||||
defJob: deepClone(jobAttr),
|
||||
})
|
||||
|
||||
const commonNodes = [
|
||||
initShapes.start,
|
||||
initShapes.serial,
|
||||
initShapes.parallel,
|
||||
initShapes.end
|
||||
];
|
||||
|
||||
initShapes.virtual.attr(cdata, {
|
||||
type: HighNodeType.VIRTUAL,
|
||||
attrs: deepClone(virtualAttr),
|
||||
defJob: deepClone(jobAttr),
|
||||
})
|
||||
|
||||
initShapes.job.attr(cdata, {
|
||||
type: HighNodeType.JOB,
|
||||
defJob: deepClone(jobAttr),
|
||||
})
|
||||
|
||||
const highNodes = [
|
||||
initShapes.serialGate,
|
||||
initShapes.parallelGate,
|
||||
initShapes.virtual,
|
||||
initShapes.job
|
||||
/*,
|
||||
{ cdata: {
|
||||
type: HighNodeType.CHILD_FLOW,
|
||||
attrs: deepClone(highAttr)
|
||||
}
|
||||
}*/
|
||||
];
|
||||
|
||||
initShapes.xLane.attr(cdata, {
|
||||
type: LaneNodeType.X_LANE,
|
||||
attrs: deepClone(laneAttr)
|
||||
})
|
||||
|
||||
initShapes.yLane.attr(cdata, {
|
||||
type: LaneNodeType.Y_LANE,
|
||||
attrs: deepClone(laneAttr)
|
||||
})
|
||||
|
||||
const laneNodes = [
|
||||
initShapes.xLane,
|
||||
initShapes.yLane
|
||||
];
|
||||
|
||||
const defLink = initShapes.link.attr(cdata, {
|
||||
type: CommonNodeType.LINK,
|
||||
attrs: deepClone(linkAttr),
|
||||
})
|
||||
|
||||
return {commonNodes, highNodes, laneNodes, defLink}
|
||||
}
|
||||
|
||||
window._nodeConfig = { initNodeShapes }
|
||||
18
src/flow/designer/config/type.ts
Normal file
18
src/flow/designer/config/type.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
export const CommonNodeType = {
|
||||
START : 'start',
|
||||
SERIAL : 'serial',
|
||||
PARALLEL : 'parallel',
|
||||
END : 'end',
|
||||
LINK : 'link'
|
||||
}
|
||||
|
||||
export const HighNodeType = {
|
||||
CHILD_FLOW : 'child_flow',
|
||||
VIRTUAL : 'virtual',
|
||||
JOB : 'job'
|
||||
}
|
||||
|
||||
export const LaneNodeType = {
|
||||
X_LANE : 'x_lane',
|
||||
Y_LANE : 'y_lane'
|
||||
}
|
||||
110
src/flow/designer/utils/common.ts
Normal file
110
src/flow/designer/utils/common.ts
Normal file
@@ -0,0 +1,110 @@
|
||||
import { flowConfig } from '../config/flow-config.ts'
|
||||
|
||||
export let utils = {
|
||||
seqNo: 1,
|
||||
getId: function() {
|
||||
let idType = flowConfig.idType;
|
||||
if (typeof idType == 'string') {
|
||||
if (idType === 'uuid') {
|
||||
return this.getUUID();
|
||||
} else if (idType === 'time_stamp') {
|
||||
return this.getTimeStamp();
|
||||
}
|
||||
} else if (idType instanceof Array) {
|
||||
if (idType[0] === 'sequence') {
|
||||
return this.getSequence(idType[1]);
|
||||
} else if (idType[0] === 'time_stamp_and_sequence') {
|
||||
return this.getTimeStampAndSequence(idType[1]);
|
||||
} else if (idType[0] === 'custom') {
|
||||
return idType[1]();
|
||||
}
|
||||
}
|
||||
},
|
||||
getUUID: function() {
|
||||
let s = [];
|
||||
let hexDigits = "0123456789abcdef";
|
||||
for(let i = 0; i < 36; i++) {
|
||||
s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1);
|
||||
}
|
||||
s[14] = "4";
|
||||
s[19] = hexDigits.substr((s[19] & 0x3) | 0x8, 1);
|
||||
s[8] = s[13] = s[18] = s[23] = "-";
|
||||
|
||||
let uuid = s.join("");
|
||||
return uuid.replace(/-/g, '');
|
||||
},
|
||||
getTimeStamp: function() {
|
||||
return new Date().getTime();
|
||||
},
|
||||
getSequence: function(seqNoLength) {
|
||||
let zeroStr = new Array(seqNoLength).fill('0').join('');
|
||||
return (zeroStr + (this.seqNo++)).slice(-seqNoLength);
|
||||
},
|
||||
getTimeStampAndSequence: function(seqNoLength) {
|
||||
return this.getTimeStamp() + this.getSequence(seqNoLength);
|
||||
},
|
||||
add: function(a, b) {
|
||||
let c, d, e;
|
||||
try {
|
||||
c = a.toString().split(".")[1].length;
|
||||
} catch (f) {
|
||||
c = 0;
|
||||
}
|
||||
try {
|
||||
d = b.toString().split(".")[1].length;
|
||||
} catch (f) {
|
||||
d = 0;
|
||||
}
|
||||
return e = Math.pow(10, Math.max(c, d)), (this.mul(a, e) + this.mul(b, e)) / e;
|
||||
},
|
||||
sub: function(a, b) {
|
||||
let c, d, e;
|
||||
try {
|
||||
c = a.toString().split(".")[1].length;
|
||||
} catch (f) {
|
||||
c = 0;
|
||||
}
|
||||
try {
|
||||
d = b.toString().split(".")[1].length;
|
||||
} catch (f) {
|
||||
d = 0;
|
||||
}
|
||||
return e = Math.pow(10, Math.max(c, d)), (this.mul(a, e) - this.mul(b, e)) / e;
|
||||
},
|
||||
mul: function(a, b) {
|
||||
let c = 0, d = a.toString(), e = b.toString();
|
||||
try {
|
||||
c += d.split(".")[1].length;
|
||||
} catch (f) {}
|
||||
try {
|
||||
c += e.split(".")[1].length;
|
||||
} catch (f) {}
|
||||
return Number(d.replace(".", "")) * Number(e.replace(".", "")) / Math.pow(10, c);
|
||||
},
|
||||
div: function(a, b) {
|
||||
let c, d, e = 0, f = 0;
|
||||
try {
|
||||
e = a.toString().split(".")[1].length;
|
||||
} catch (g) {}
|
||||
try {
|
||||
f = b.toString().split(".")[1].length;
|
||||
} catch (g) {}
|
||||
return c = Number(a.toString().replace(".", "")), d = Number(b.toString().replace(".", "")), this.mul(c / d, Math.pow(10, f - e));
|
||||
},
|
||||
deepClone(obj) {
|
||||
let newObj;
|
||||
try {
|
||||
newObj = obj.push ? [] : {};
|
||||
} catch (error) {
|
||||
newObj = {};
|
||||
}
|
||||
for (let attr in obj) {
|
||||
if (obj[attr] && typeof obj[attr] === 'object') {
|
||||
newObj[attr] = this.deepClone(obj[attr]);
|
||||
} else {
|
||||
newObj[attr] = obj[attr];
|
||||
}
|
||||
}
|
||||
return newObj;
|
||||
}
|
||||
};
|
||||
84
src/flow/designer/views/json-view.vue
Normal file
84
src/flow/designer/views/json-view.vue
Normal file
@@ -0,0 +1,84 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-drawer
|
||||
class="flow-header-drawer"
|
||||
title="导入导出"
|
||||
direction="rtl"
|
||||
append-to-body
|
||||
:size="600"
|
||||
v-model="data.viewJsonVisible"
|
||||
@close="methods.onClose">
|
||||
<div style="padding: 24px">
|
||||
<div>当前JSON数据:</div>
|
||||
<json-view
|
||||
:value="data.flowData"
|
||||
:expand-depth=3
|
||||
boxed
|
||||
copyable/>
|
||||
|
||||
<div style="margin-top: 12px;">导入导出数据:</div>
|
||||
<el-input type="textarea" minlength="10" v-model="data.flowDataJson" @change="methods.editFlowDataJson"/>
|
||||
|
||||
<el-divider/>
|
||||
<el-button @click="methods.tempSave" :style="{ marginRight: '8px' }">导出数据</el-button>
|
||||
<el-button @click="methods.onLoad" type="primary">导入数据</el-button>
|
||||
</div>
|
||||
</el-drawer>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="JsonView">
|
||||
import JsonView from "vue-json-viewer"
|
||||
import {useI18n} from "vue-i18n";
|
||||
import {useMessage} from "/@/hooks/message";
|
||||
import {stringifyRemoveNullKey} from "../../index";
|
||||
|
||||
const {t} = useI18n();
|
||||
const $message = useMessage();
|
||||
const $emit = defineEmits(["loadFlow"]);
|
||||
|
||||
const data = reactive({
|
||||
viewJsonVisible: false,
|
||||
flowData: {},
|
||||
flowDataJson: ""
|
||||
})
|
||||
|
||||
const methods = {
|
||||
open(flowData) {
|
||||
flowData = stringifyRemoveNullKey(flowData);
|
||||
data.flowData = !window.isWebTest ? {contact: "演示环境不能操作,如需了解联系我们"}: JSON.parse(flowData);
|
||||
data.viewJsonVisible = true;
|
||||
},
|
||||
onClose() {
|
||||
data.viewJsonVisible = false;
|
||||
},
|
||||
// 编辑框
|
||||
editFlowDataJson(value) {
|
||||
data.flowDataJson = value;
|
||||
},
|
||||
// 导出数据
|
||||
tempSave() {
|
||||
if (!data.flowData.attrs) return
|
||||
let tempObj = Object.assign({}, data.flowData);
|
||||
data.flowDataJson = stringifyRemoveNullKey(tempObj);
|
||||
},
|
||||
// 导入数据
|
||||
onLoad() {
|
||||
if (!data.flowDataJson) {
|
||||
$message.warning("请先导入数据")
|
||||
return
|
||||
}
|
||||
$emit('loadFlow', JSON.parse(data.flowDataJson));
|
||||
methods.onClose();
|
||||
}
|
||||
}
|
||||
|
||||
// 暴露变量
|
||||
defineExpose({
|
||||
open: methods.open
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
@import "../../../flow/components/style/flow-drawer.scss";
|
||||
</style>
|
||||
109
src/flow/designer/views/setting.vue
Normal file
109
src/flow/designer/views/setting.vue
Normal file
@@ -0,0 +1,109 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-drawer
|
||||
class="flow-header-drawer"
|
||||
title="画布设置"
|
||||
direction="rtl"
|
||||
append-to-body
|
||||
:size="600"
|
||||
v-if="data.settingVisible"
|
||||
v-model="data.settingVisible"
|
||||
style="z-index: 1001"
|
||||
@close="methods.close">
|
||||
<el-form
|
||||
class="flow-drawer-form"
|
||||
:model="data.settingForm"
|
||||
label-position="right">
|
||||
<el-divider content-position="left">画布</el-divider>
|
||||
<el-form-item label="网格大小">
|
||||
<el-slider
|
||||
:min="1"
|
||||
:max="20"
|
||||
v-model="data.gridConfig.gridSize"
|
||||
@change="methods.setGridSize"/>
|
||||
</el-form-item>
|
||||
<el-divider content-position="left">连线</el-divider>
|
||||
<!-- <el-form-item label="是否允许节点与任务分离显示">
|
||||
<el-switch
|
||||
v-model="data.globalConfig.isJobSeparated"
|
||||
active-value="1"
|
||||
inactive-value="0"
|
||||
@change="methods.toggleJobSeparated"/>
|
||||
</el-form-item>-->
|
||||
<el-tooltip content="当节点与任务分离显示时,该设置在专业模式下生效" placement="left">
|
||||
<el-form-item label="是否允许任务连线到其他节点">
|
||||
<el-switch
|
||||
v-model="data.globalConfig.allowJobLink"
|
||||
active-value="1"
|
||||
inactive-value="0"
|
||||
@change="methods.toggleAllowJobLink"/>
|
||||
</el-form-item>
|
||||
</el-tooltip>
|
||||
<el-divider content-position="left">样式</el-divider>
|
||||
<el-form-item label="是否切换节点UI图形显示效果">
|
||||
<el-switch
|
||||
v-model="data.globalConfig.isJobSeparated"
|
||||
active-value="1"
|
||||
inactive-value="0"
|
||||
@change="methods.toggleJobSeparated"/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-drawer>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="FlowSetting">
|
||||
import {flowConfig} from '../config/flow-config'
|
||||
import * as nodeConfig from "/@/flow/designer/config/node-config";
|
||||
|
||||
const $emit = defineEmits(["flowSeparated"]);
|
||||
|
||||
const data = reactive({
|
||||
settingVisible: false,
|
||||
settingForm: {},
|
||||
globalConfig: flowConfig.globalConfig,
|
||||
gridConfig: flowConfig.gridConfig
|
||||
})
|
||||
|
||||
const methods = {
|
||||
open() {
|
||||
data.settingVisible = true
|
||||
},
|
||||
close() {
|
||||
data.settingVisible = false
|
||||
},
|
||||
toggleAllowJobLink(flag) {
|
||||
data.globalConfig.allowJobLink = flag
|
||||
flowConfig.globalConfig.allowJobLink = flag
|
||||
},
|
||||
toggleJobSeparated(flag) {
|
||||
data.globalConfig.isJobSeparated = flag
|
||||
flowConfig.globalConfig.isJobSeparated = flag
|
||||
$emit('flowSeparated', flag);
|
||||
},
|
||||
setGridSize(v) {
|
||||
data.gridConfig.gridSize = v
|
||||
window._jfOperate.resetGridSize(v)
|
||||
}
|
||||
}
|
||||
|
||||
// 暴露变量
|
||||
defineExpose({
|
||||
open: methods.open
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
@import "../../../flow/components/style/flow-drawer.scss";
|
||||
|
||||
.flow-drawer-form {
|
||||
/*属性面板*/
|
||||
.el-form-item {
|
||||
margin: 0 40px 10px;
|
||||
}
|
||||
|
||||
.el-form-item .el-form-item__content {
|
||||
margin-left: 150px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
87
src/flow/designer/views/shortcut.vue
Normal file
87
src/flow/designer/views/shortcut.vue
Normal file
@@ -0,0 +1,87 @@
|
||||
<template>
|
||||
<el-dialog
|
||||
title="快捷入门"
|
||||
width="60%"
|
||||
append-to-body
|
||||
top="5vh"
|
||||
v-model="data.modalVisible">
|
||||
<el-table
|
||||
row-key="code"
|
||||
:data="data.dataSource">
|
||||
<el-table-column
|
||||
v-for="item in data.columns"
|
||||
:key="item.dataIndex"
|
||||
:label="item.title"
|
||||
:prop="item.key"
|
||||
style="width: 100%">
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="methods.cancel">取 消</el-button>
|
||||
<el-button type="primary" @click="methods.saveSetting">确 定</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="FlowShortcut">
|
||||
|
||||
const props = defineProps({
|
||||
shortcut: {
|
||||
type: Object,
|
||||
default: null
|
||||
}
|
||||
});
|
||||
|
||||
const data = reactive({
|
||||
modalVisible: false,
|
||||
columns: [
|
||||
{
|
||||
title: '功能',
|
||||
align: 'center',
|
||||
key: 'shortcutName',
|
||||
dataIndex: 'shortcutName',
|
||||
width: '50%'
|
||||
},
|
||||
{
|
||||
title: '快捷入门',
|
||||
align: 'center',
|
||||
key: 'codeName',
|
||||
dataIndex: 'codeName',
|
||||
width: '50%'
|
||||
}
|
||||
],
|
||||
dataSource: []
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
let obj = Object.assign({}, props.shortcut)
|
||||
for (let k in obj) {
|
||||
data.dataSource.push(obj[k])
|
||||
}
|
||||
})
|
||||
|
||||
const methods = {
|
||||
open() {
|
||||
data.modalVisible = true
|
||||
},
|
||||
close() {
|
||||
data.modalVisible = false
|
||||
},
|
||||
saveSetting() {
|
||||
methods.close()
|
||||
},
|
||||
cancel() {
|
||||
methods.close()
|
||||
}
|
||||
}
|
||||
|
||||
// 暴露变量
|
||||
defineExpose({
|
||||
open: methods.open
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
</style>
|
||||
199
src/flow/index.ts
Normal file
199
src/flow/index.ts
Normal file
@@ -0,0 +1,199 @@
|
||||
import {validateNull} from "/@/utils/validate";
|
||||
import {ElNotification} from 'element-plus'
|
||||
import printJS from 'print-js'
|
||||
import {DIC_PROP} from "/@/flow/support/dict-prop";
|
||||
import {Local} from "/@/utils/storage";
|
||||
import FcDesigner from 'form-create-designer';
|
||||
import {validateFormTypeSave} from "/@/api/order/order-key-vue";
|
||||
|
||||
// 判断是否为实例
|
||||
export const validateRunFlow = (props) => {
|
||||
if (props.currFlowForm) return props.currFlowForm.flowInstId
|
||||
else {
|
||||
if (props.flowData.attrs.orderId) return props.flowData.attrs.id
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
// 判断是否为实例
|
||||
export const validateRunFlowId = (props, form) => {
|
||||
if (!props.currFlowForm) return form
|
||||
let flowInstId = validateRunFlow(props);
|
||||
let id = props.currFlowForm.id;
|
||||
if (flowInstId) {
|
||||
let tableName = props.currFlowForm.tableName;
|
||||
// 判断是否为低代码
|
||||
form.formId = tableName ? props.currFlowForm.formId: id
|
||||
form.flowInstId = props.currFlowForm.flowInstId
|
||||
} else {
|
||||
form.formId = id
|
||||
}
|
||||
return form
|
||||
}
|
||||
|
||||
// 初始化流程表单参数
|
||||
export const initDefFlowFromAttrs = (props, form) => {
|
||||
if (!props.currFlowForm) return
|
||||
// 流程比表单先保存
|
||||
validateFormTypeSave(props.currFlowForm)
|
||||
validateRunFlowId(props, form)
|
||||
// 表单权限主表单首次未入库
|
||||
form.formName = props.currFlowForm.formName
|
||||
form.formType = props.currFlowForm.type
|
||||
form.formPath = props.currFlowForm.path
|
||||
// 判断是否为低代码
|
||||
let tableName = props.currFlowForm.tableName;
|
||||
form.fromType = tableName ? '0' : '1'
|
||||
}
|
||||
|
||||
// 初始化表单必要参数
|
||||
export const initFromSomeAttrs = (props, form) => {
|
||||
if (!props.currFlowForm) return
|
||||
// 字段定义比表单先保存,在initDefFlowFromAttrs之后
|
||||
validateRunFlowId(props, form) // 可能会升版本
|
||||
form.formName = props.currFlowForm.formName
|
||||
form.formType = props.currFlowForm.type
|
||||
form.path = props.currFlowForm.path
|
||||
}
|
||||
|
||||
export function getLabelByLanguage(item) {
|
||||
let language = Local.get('themeConfig').globalI18n;
|
||||
return language === 'en' && item.labelEn ? item.labelEn : item.label
|
||||
}
|
||||
|
||||
export function getJobBtnName(jobBtn) {
|
||||
let item = DIC_PROP.JOB_BTNS.find((f) => f.value === jobBtn);
|
||||
return getLabelByLanguage(item)
|
||||
}
|
||||
|
||||
export function handleChangeJobType(dicData, item) {
|
||||
let exist
|
||||
if (item.jobType === DIC_PROP.JOB_USER_TYPE[0].value) {
|
||||
exist = !dicData.users ? null : dicData.users.find(f => f.userId === item.roleId);
|
||||
} else if (item.jobType === DIC_PROP.JOB_USER_TYPE[1].value) {
|
||||
exist = !dicData.roles ? null : dicData.roles.find(f => f.roleId === item.roleId);
|
||||
} else if (item.jobType === DIC_PROP.JOB_USER_TYPE[2].value) {
|
||||
exist = !dicData.posts ? null : dicData.posts.find(f => f.postId === item.roleId);
|
||||
} else if (item.jobType === DIC_PROP.JOB_USER_TYPE[3].value) {
|
||||
exist = !dicData.depts ? null : dicData.depts.find(f => f.deptId === item.roleId);
|
||||
}
|
||||
// 当不存在情况已选值
|
||||
if (!exist) item.roleId = null
|
||||
}
|
||||
|
||||
export function handleShowRoleNameByJobType(role, isJobType){
|
||||
let name = '';
|
||||
if (isJobType === '0') {
|
||||
name = role.roleName ? role.roleName.substring(0, role.roleName.indexOf('(')) : ''
|
||||
} else {
|
||||
name = role.roleName ? role.roleName + (isJobType ? '(' + getLabelByLanguage(DIC_PROP.JOB_USER_TYPE[1]) + ')' : '') : ''
|
||||
}
|
||||
return name
|
||||
}
|
||||
|
||||
export function handleShowNameByJobType(role, isJobType){
|
||||
let name = '';
|
||||
if (role.jobType === DIC_PROP.JOB_USER_TYPE[0].value) {
|
||||
if (isJobType === '0') {
|
||||
name = role.name ? role.name.substring(0, role.name.indexOf('(')) : ''
|
||||
} else {
|
||||
name = role.name ? role.name + (isJobType ? '(' + getLabelByLanguage(DIC_PROP.JOB_USER_TYPE[0]) + ')' : '') : ''
|
||||
}
|
||||
} else if (role.jobType === DIC_PROP.JOB_USER_TYPE[1].value) {
|
||||
name = handleShowRoleNameByJobType(role, isJobType)
|
||||
} else if (role.jobType === DIC_PROP.JOB_USER_TYPE[2].value) {
|
||||
if (isJobType === '0') {
|
||||
name = role.postName ? role.postName.substring(0, role.postName.indexOf('(')) : ''
|
||||
} else {
|
||||
name = role.postName ? role.postName + (isJobType ? '(' + getLabelByLanguage(DIC_PROP.JOB_USER_TYPE[2]) + ')' : '') : ''
|
||||
}
|
||||
} else if (role.jobType === DIC_PROP.JOB_USER_TYPE[3].value) {
|
||||
if (isJobType === '0') {
|
||||
name = role.name ? role.name.substring(0, role.name.indexOf('(')) : ''
|
||||
} else {
|
||||
name = role.name ? role.name + (isJobType ? '(' + getLabelByLanguage(DIC_PROP.JOB_USER_TYPE[3]) + ')' : '') : ''
|
||||
}
|
||||
}
|
||||
return name
|
||||
}
|
||||
|
||||
/**
|
||||
* 请求参数去重,并去掉$与空值
|
||||
*/
|
||||
export function paramsFilter(params) {
|
||||
if (validateNull(params)) return null
|
||||
let res = {}
|
||||
let map = new Map(Object.entries(params))
|
||||
map.forEach((value, key) => {
|
||||
if (key.includes("$") || validateNull(value)) return
|
||||
res[key] = value
|
||||
})
|
||||
return res
|
||||
}
|
||||
|
||||
export function notifyLeft(msg, type = 'success', time = 2000) {
|
||||
ElNotification({
|
||||
title: '提示',
|
||||
message: msg,
|
||||
// @ts-ignore
|
||||
type: type,
|
||||
// @ts-ignore
|
||||
position: 'top-left',
|
||||
duration: time
|
||||
})
|
||||
}
|
||||
|
||||
export function parseRemoveNullKey(jsonStr) {
|
||||
const obj = JSON.parse(jsonStr);
|
||||
return JSON.stringify(obj, (key, value) => {
|
||||
if (validateNull(value)) {
|
||||
return undefined; // 返回undefined跳过属性
|
||||
}
|
||||
return value;
|
||||
});
|
||||
}
|
||||
|
||||
export function stringifyRemoveNullKey(object) {
|
||||
return JSON.stringify(object, (key, value) => {
|
||||
if (validateNull(value)) {
|
||||
return undefined; // 返回undefined跳过属性
|
||||
}
|
||||
return value;
|
||||
})
|
||||
}
|
||||
|
||||
export function stringifyWithFunctions(object) {
|
||||
return FcDesigner.formCreate.toJson(object)
|
||||
}
|
||||
|
||||
export const parseWithFunctions = (obj, func?) => {
|
||||
if (!func) {
|
||||
return parseWithString(obj)
|
||||
}
|
||||
if (typeof obj === "string") {
|
||||
return FcDesigner.formCreate.parseJson(obj)
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
export const parseWithString = (obj) => {
|
||||
if (typeof obj === "string") {
|
||||
return JSON.parse(obj);
|
||||
} else {
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
export const printHtml = (id, header, data) => {
|
||||
setTimeout(() => {
|
||||
printJS({
|
||||
type: 'html',
|
||||
printable: id,
|
||||
header: header,
|
||||
targetStyles: ["*"],
|
||||
scanStyles: true,
|
||||
maxWidth: 1100,
|
||||
})
|
||||
if (data) data.width = "-"
|
||||
}, 100)
|
||||
}
|
||||
64
src/flow/stores/flowJob.ts
Normal file
64
src/flow/stores/flowJob.ts
Normal file
@@ -0,0 +1,64 @@
|
||||
/**
|
||||
* top顶端待办任务
|
||||
*/
|
||||
import { defineStore } from 'pinia';
|
||||
import {fetchTodoSize} from '/@/api/jsonflow/do-job'
|
||||
import {Local} from "/@/utils/storage";
|
||||
import {topJobList} from "./index";
|
||||
import {locationHash} from "/@/api/order/order-key-vue";
|
||||
import {windowLocationHref} from "/@/flow/support/extend";
|
||||
|
||||
interface Job {
|
||||
jobLen: number | null
|
||||
}
|
||||
|
||||
// 是否开启JsonFlow
|
||||
const jsonFlowEnable = ref(import.meta.env.VITE_JSON_FLOW_ENABLE === 'true');
|
||||
|
||||
export const useFlowJob = defineStore('flow/job', {
|
||||
state: (): Job => ({
|
||||
jobLen: Local.get('jobLen') || 0
|
||||
}),
|
||||
actions: {
|
||||
jsonFlowEnable() {
|
||||
return jsonFlowEnable.value;
|
||||
},
|
||||
topJobList() {
|
||||
if (this.jsonFlowEnable()) topJobList();
|
||||
},
|
||||
onTodoJobClick() {
|
||||
// 因a标签href="#",会先跳转/
|
||||
setTimeout(() => {
|
||||
windowLocationHref(locationHash.TodoJobHash)
|
||||
}, 0)
|
||||
},
|
||||
// 获取待办任务数
|
||||
getJobList() {
|
||||
if (!this.jsonFlowEnable()) return Promise.resolve();
|
||||
return new Promise((resolve, reject) => {
|
||||
fetchTodoSize({belongType: '-1', current: 1, size: 1})
|
||||
.then(response => {
|
||||
this.jobLen = response.data.total
|
||||
Local.set('jobLen', this.jobLen);
|
||||
resolve(this.jobLen)
|
||||
}).catch(error => {
|
||||
reject(error)
|
||||
})
|
||||
})
|
||||
},
|
||||
setJobLen(len){
|
||||
this.jobLen = len
|
||||
Local.set('jobLen', this.jobLen);
|
||||
},
|
||||
async addJobLen() {
|
||||
await this.getJobList()
|
||||
},
|
||||
async delJobLen() {
|
||||
await this.getJobList()
|
||||
},
|
||||
clearJobLen(){
|
||||
this.jobLen = 0
|
||||
Local.set('jobLen', 0);
|
||||
}
|
||||
}
|
||||
});
|
||||
72
src/flow/stores/index.ts
Normal file
72
src/flow/stores/index.ts
Normal file
@@ -0,0 +1,72 @@
|
||||
import {useFlowJob} from "/@/flow/stores/flowJob";
|
||||
import {ElNotification} from "element-plus";
|
||||
import {flowConfig} from "/@/flow/designer/config/flow-config";
|
||||
|
||||
// 登录通知待办任务数
|
||||
export function topJobList() {
|
||||
// 兼容移动端
|
||||
const route = useRoute();
|
||||
let index = route.fullPath.indexOf(flowConfig.mobileConfig.mobilePrefix);
|
||||
if (index !== -1) return
|
||||
useFlowJob().getJobList().then((jobLen) => {
|
||||
if (!jobLen || jobLen === 0) return
|
||||
ElNotification({
|
||||
title: "总任务数提醒",
|
||||
type: 'warning',
|
||||
dangerouslyUseHTMLString: true,
|
||||
message: `<a href="#" style="color:#FF4500;"><i class="iconfont icon-xianshimima"></i>当前总共 ${jobLen} 条待办任务,请及时处理!</a>`,
|
||||
position: 'top-right',
|
||||
offset: 60,
|
||||
onClick: () => {
|
||||
useFlowJob().onTodoJobClick();
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// 消息通知工作流任务
|
||||
export function notifyJsonFlowJob(res) {
|
||||
const data = JSON.parse(res.data)
|
||||
// 延迟解决可能没有工单数据
|
||||
setTimeout(() => {
|
||||
handleJsonFlowJob(data)
|
||||
}, 6000)
|
||||
}
|
||||
|
||||
// 任务状态判断
|
||||
function handleJsonFlowJob(data) {
|
||||
const opts: any = {}
|
||||
if (data.status === '0') { // 运行中
|
||||
const jobType = data.jobType === '0' ? '个人任务:' : '组任务:'
|
||||
const type = 'warning'
|
||||
const msg = '<a href="#" style="color:#FF4500;">' +
|
||||
'<i class="iconfont icon-xianshimima"></i> ' + jobType + data.data + '</a>' + ',请及时处理'
|
||||
opts.onClick = function () {
|
||||
useFlowJob().onTodoJobClick();
|
||||
}
|
||||
jobNotify(opts, type, msg)
|
||||
} else { // 发起 或 结束
|
||||
const text = data.status === '-1' ? data.data : data.data + '已完成, 请悉知'
|
||||
const type = data.status === '-1' ? 'warning' : 'success'
|
||||
const msg = '<a href="#" style="color:#FF4500;">' + text + '</a>'
|
||||
jobNotify(opts, type, msg)
|
||||
}
|
||||
}
|
||||
|
||||
// 任务提醒
|
||||
function jobNotify(opts, type, msg) {
|
||||
useFlowJob().addJobLen();
|
||||
const defOpts = {
|
||||
title: '任务提醒',
|
||||
type: type,
|
||||
dangerouslyUseHTMLString: true,
|
||||
message: msg,
|
||||
offset: 60
|
||||
}
|
||||
ElNotification(Object.assign(defOpts, opts))
|
||||
}
|
||||
|
||||
export function isJSON(str) {
|
||||
const jsonRegex = /^\s*[\[{].*[\]}]\s*$/;
|
||||
return jsonRegex.test(str);
|
||||
}
|
||||
98
src/flow/support/common.ts
Normal file
98
src/flow/support/common.ts
Normal file
@@ -0,0 +1,98 @@
|
||||
/**
|
||||
* 业务公共处理类
|
||||
* @author luolin
|
||||
*/
|
||||
import {validateNull} from "/@/utils/validate";
|
||||
import {deepClone} from "/@/utils/other";
|
||||
|
||||
// 校验是否为对象或数组
|
||||
export function validateObjArr(objArr) {
|
||||
return objArr instanceof Array || objArr instanceof Object;
|
||||
}
|
||||
|
||||
// 处理克隆
|
||||
export function handleClone(_this, form: any, ...keys?: string[]) {
|
||||
if (_this.operType !== 'add' && _this.operType !== 'copy') return
|
||||
if (!form) form = _this.form
|
||||
setPropsNull(form, 'code', 'status', 'finishTime', 'createUser', 'createTime', 'updateUser', 'updateTime')
|
||||
// 自定义清空字段
|
||||
setPropsNull(form, ...keys)
|
||||
return form
|
||||
}
|
||||
|
||||
// 处理克隆提交前
|
||||
export function handleCloneSubmit(_this, row?: any, callback?: Function) {
|
||||
if (_this.operType !== 'add' && _this.operType !== 'copy') return
|
||||
if (!row) row = _this.form
|
||||
row = handleClone(_this, row)
|
||||
setPropsNull(row, 'id', 'flowInstId')
|
||||
if (callback) callback(row)
|
||||
}
|
||||
|
||||
// 设置属性值null
|
||||
// @ts-ignore
|
||||
export function setPropsNull(form: any, ...keys?: string[]) {
|
||||
if (arguments.length < 2) return
|
||||
let args = Array.prototype.slice.call(arguments)
|
||||
for (let i = 1; i < args.length; i++) {
|
||||
form[args[i]] = null
|
||||
}
|
||||
}
|
||||
|
||||
// 统一设置属性值
|
||||
// @ts-ignore
|
||||
export function setPropsValue(form: any, value: any, ...keys?: string[]) {
|
||||
if (arguments.length < 3) return
|
||||
let args = Array.prototype.slice.call(arguments)
|
||||
for (let i = 2; i < args.length; i++) {
|
||||
form[args[i]] = value
|
||||
}
|
||||
}
|
||||
|
||||
// 重置表单数据
|
||||
// @ts-ignore
|
||||
export function setPropsNullValue(form: any, data: any, ...keys?: string[]) {
|
||||
if (arguments.length < 3) {
|
||||
keys = Object.keys(form)
|
||||
}
|
||||
setPropsNull(form, ...keys)
|
||||
if (validateNull(data)) return
|
||||
setPropsDataValue(form, data, keys)
|
||||
}
|
||||
|
||||
// 设置属性为data值
|
||||
// @ts-ignore
|
||||
export function setPropsDataValue(form, data, keys?: string[]) {
|
||||
if (arguments.length < 3) {
|
||||
let keys = Object.keys(data);
|
||||
for (let i = 2; i < keys.length; i++) {
|
||||
if (!validateNull(data[keys[i]])) form[keys[i]] = data[keys[i]]
|
||||
}
|
||||
return
|
||||
}
|
||||
let args = Array.prototype.slice.call(arguments)
|
||||
// 第三个参数是否为数组
|
||||
if (args[2] instanceof Array) {
|
||||
args[2].forEach(key => {
|
||||
if (!validateNull(data[key])) {
|
||||
// 防止相同引用
|
||||
if (validateObjArr(data[key])) form[key] = deepClone(data[key])
|
||||
else form[key] = data[key]
|
||||
}
|
||||
})
|
||||
} else {
|
||||
for (let i = 2; i < args.length; i++) {
|
||||
if (!validateNull(data[args[i]])) form[args[i]] = data[args[i]]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 设置Crud queryForm
|
||||
export function setCrudQueryForm(_this, ...params?) {
|
||||
if (arguments.length < 2) return
|
||||
let args = Array.prototype.slice.call(arguments)
|
||||
// 单个修改
|
||||
for (let i = 1; i < args.length; i++) {
|
||||
_this.queryForm[args[i].prop] = args[i].value
|
||||
}
|
||||
}
|
||||
782
src/flow/support/dict-prop.ts
Normal file
782
src/flow/support/dict-prop.ts
Normal file
@@ -0,0 +1,782 @@
|
||||
/**
|
||||
* 字典属性常量
|
||||
* @author luolin
|
||||
*/
|
||||
|
||||
let paramValTypes = [{
|
||||
label: '表单字段',
|
||||
value: '0',
|
||||
}, {
|
||||
label: 'SpEL表达式',
|
||||
value: '1',
|
||||
}, {
|
||||
label: '固定值',
|
||||
value: '2',
|
||||
}]
|
||||
|
||||
let jobTypes = [{
|
||||
label: '人员',
|
||||
value: '0',
|
||||
labelEn: 'User'
|
||||
}, {
|
||||
label: '角色',
|
||||
value: '1',
|
||||
labelEn: 'Role'
|
||||
}, {
|
||||
label: '岗位',
|
||||
value: '2',
|
||||
labelEn: 'Post'
|
||||
}, {
|
||||
label: '部门',
|
||||
value: '3',
|
||||
labelEn: 'Dept'
|
||||
}]
|
||||
|
||||
let methods = [{
|
||||
label: '当开启任务时',
|
||||
value: 'startJob'
|
||||
}, {
|
||||
label: '当完成任务时',
|
||||
value: 'completeJob'
|
||||
}, {
|
||||
label: '当开启下一步任务时',
|
||||
value: 'startNextJob'
|
||||
}, {
|
||||
label: '当开启节点时',
|
||||
value: 'startNode'
|
||||
}, {
|
||||
label: '当完成节点时',
|
||||
value: 'completeNode'
|
||||
}, {
|
||||
label: '当开启下一步节点时',
|
||||
value: 'startNextNode'
|
||||
}, {
|
||||
label: '当任务跳转时',
|
||||
value: 'anyJump'
|
||||
}, {
|
||||
label: '当任务被跳转时',
|
||||
value: 'anyJumped'
|
||||
}, {
|
||||
label: '当任务驳回时',
|
||||
value: 'reject'
|
||||
}, {
|
||||
label: '当任务被驳回时',
|
||||
value: 'rejected'
|
||||
}, {
|
||||
label: '当需分配参与者时',
|
||||
value: 'distPerson'
|
||||
}, {
|
||||
label: '退回首节点时',
|
||||
value: 'backFirst'
|
||||
}, {
|
||||
label: '退回上一步时',
|
||||
value: 'backPre'
|
||||
}]
|
||||
|
||||
export let DIC_PROP = {
|
||||
YES_OR_NO: [{
|
||||
label: '否',
|
||||
value: '0',
|
||||
labelEn: 'No'
|
||||
}, {
|
||||
label: '是',
|
||||
value: '1',
|
||||
labelEn: 'Yes'
|
||||
}],
|
||||
YES_OR_NO_BOOL: [{
|
||||
label: '否',
|
||||
value: false,
|
||||
labelEn: 'No'
|
||||
}, {
|
||||
label: '是',
|
||||
value: true,
|
||||
labelEn: 'Yes'
|
||||
}],
|
||||
YES_OR_NO_NUM: [{
|
||||
label: '否',
|
||||
value: 0,
|
||||
labelEn: 'No'
|
||||
}, {
|
||||
label: '是',
|
||||
value: 1,
|
||||
labelEn: 'Yes'
|
||||
}],
|
||||
DELEGATE_STATUS: [{
|
||||
label: '暂存',
|
||||
value: '-1',
|
||||
labelEn: 'Temp'
|
||||
}, {
|
||||
label: '启用',
|
||||
value: '0',
|
||||
labelEn: 'Enable'
|
||||
}, {
|
||||
label: '停用',
|
||||
value: '1',
|
||||
labelEn: 'Disable'
|
||||
}],
|
||||
TEMP_STATUS: [{
|
||||
label: '暂存',
|
||||
value: '-1',
|
||||
labelEn: 'Temp'
|
||||
}, {
|
||||
label: '作废',
|
||||
value: '0',
|
||||
labelEn: 'Invalid'
|
||||
}, {
|
||||
label: '发布',
|
||||
value: '1',
|
||||
labelEn: 'Publish'
|
||||
}],
|
||||
ORDER_STATUS: [{
|
||||
label: '撤回',
|
||||
value: '-2'
|
||||
}, {
|
||||
label: '暂存',
|
||||
value: '-1'
|
||||
}, {
|
||||
label: '运行中',
|
||||
value: '0'
|
||||
}, {
|
||||
label: '完成',
|
||||
value: '1'
|
||||
}, {
|
||||
label: '作废',
|
||||
value: '2'
|
||||
}, {
|
||||
label: '终止',
|
||||
value: '3'
|
||||
}],
|
||||
HANDOVER_TYPE: [{
|
||||
label: '任务交接',
|
||||
value: '-1'
|
||||
}, {
|
||||
label: '可自定义更多',
|
||||
value: '0'
|
||||
}],
|
||||
HANDOVER_REASON: [{
|
||||
label: '日常交接',
|
||||
value: '-1'
|
||||
}, {
|
||||
label: '晋升',
|
||||
value: '0'
|
||||
}, {
|
||||
label: '转岗',
|
||||
value: '1'
|
||||
}, {
|
||||
label: '离职',
|
||||
value: '2'
|
||||
}, {
|
||||
label: '平调',
|
||||
value: '3'
|
||||
}],
|
||||
HANDOVER_STATUS: [{
|
||||
label: '撤回',
|
||||
value: '-2'
|
||||
}, {
|
||||
label: '未交接',
|
||||
value: '-1'
|
||||
}, {
|
||||
label: '交接中',
|
||||
value: '0'
|
||||
}, {
|
||||
label: '已交接',
|
||||
value: '1'
|
||||
}, {
|
||||
label: '作废',
|
||||
value: '2'
|
||||
}, {
|
||||
label: '终止',
|
||||
value: '3'
|
||||
}],
|
||||
NODE_TYPE: [
|
||||
{
|
||||
label: '开始节点',
|
||||
value: '-1'
|
||||
}, {
|
||||
label: '串行节点',
|
||||
value: '0'
|
||||
}, {
|
||||
label: '并行节点',
|
||||
value: '1'
|
||||
}, {
|
||||
label: '结束节点',
|
||||
value: '2'
|
||||
}, {
|
||||
label: '虚拟节点',
|
||||
value: '3'
|
||||
}, {
|
||||
label: '横向泳道',
|
||||
value: '8'
|
||||
}, {
|
||||
label: '纵向泳道',
|
||||
value: '9'
|
||||
}
|
||||
],
|
||||
LINK_TYPE: [
|
||||
{
|
||||
label: '节点到节点',
|
||||
value: '0'
|
||||
}, {
|
||||
label: '节点到任务',
|
||||
value: '1'
|
||||
}
|
||||
],
|
||||
FLOW_METHOD_TYPE: [{
|
||||
label: '发起人本人',
|
||||
value: '0'
|
||||
}, {
|
||||
label: '单级部门主管',
|
||||
value: '1'
|
||||
}, {
|
||||
label: '多级部门主管',
|
||||
value: '2'
|
||||
}, {
|
||||
label: '指定部门主管',
|
||||
value: '3'
|
||||
}, {
|
||||
label: '表单内人员',
|
||||
value: '4'
|
||||
}, {
|
||||
label: '可自定义更多',
|
||||
value: '-1'
|
||||
}],
|
||||
JOB_MSG_TYPE: [{
|
||||
label: '个人任务',
|
||||
value: '0'
|
||||
}, {
|
||||
label: '组任务',
|
||||
value: '1'
|
||||
}],
|
||||
JOB_USER_TYPE: jobTypes,
|
||||
JOB_USER_NONE_TYPE: [{
|
||||
label: '无',
|
||||
value: '-1',
|
||||
labelEn: 'None'
|
||||
},
|
||||
...jobTypes
|
||||
],
|
||||
NODE_APPROVE_METHOD: [{
|
||||
label: '会签(需所有节点通过)',
|
||||
value: '1'
|
||||
}, {
|
||||
label: '或签(一个节点通过即可)',
|
||||
value: '2'
|
||||
}, {
|
||||
label: '依次审批(按顺序依次通过)',
|
||||
value: '3'
|
||||
}],
|
||||
APPROVE_METHOD: [{
|
||||
label: '会签(需所有审批人同意)',
|
||||
value: '1'
|
||||
}, {
|
||||
label: '或签(一名审批人同意即可)',
|
||||
value: '2'
|
||||
}, {
|
||||
label: '依次审批(按顺序依次审批)',
|
||||
value: '3'
|
||||
}, {
|
||||
label: '票签(自定义完成比率%)',
|
||||
value: '4'
|
||||
}],
|
||||
SIGNATURE_TYPE: [{
|
||||
label: '前加签',
|
||||
value: '1',
|
||||
labelEn: 'Before Signature'
|
||||
}, {
|
||||
label: '后加签',
|
||||
value: '2',
|
||||
labelEn: 'After Signature'
|
||||
}, {
|
||||
label: '加并签',
|
||||
value: '3',
|
||||
labelEn: 'Sign Together'
|
||||
}],
|
||||
NODE_SEQUENTIAL_TYPE: [{
|
||||
label: '在当前节点之前审批',
|
||||
value: '1'
|
||||
}, {
|
||||
label: '在当前节点之后审批',
|
||||
value: '2'
|
||||
}, {
|
||||
label: '与当前节点同时审批',
|
||||
value: '3'
|
||||
}],
|
||||
NODE_SIGNATURE_TYPE: [{
|
||||
label: '前加节点',
|
||||
value: '1'
|
||||
}, {
|
||||
label: '后加节点',
|
||||
value: '2'
|
||||
}, {
|
||||
label: '加并节点',
|
||||
value: '3'
|
||||
}],
|
||||
BELONG_TYPE: [{
|
||||
label: '普通任务',
|
||||
value: '0',
|
||||
labelEn: 'Common Task'
|
||||
}, {
|
||||
label: '抄送任务',
|
||||
value: '1',
|
||||
labelEn: 'Copy Task'
|
||||
}, {
|
||||
label: '传阅任务',
|
||||
value: '2',
|
||||
labelEn: 'Pass task'
|
||||
}],
|
||||
JOB_BTNS: [{
|
||||
label: '同意',
|
||||
value: '0',
|
||||
labelEn: 'Agree'
|
||||
}, {
|
||||
label: '前加节点',
|
||||
value: '13',
|
||||
labelEn: 'Before Node'
|
||||
}, {
|
||||
label: '后加节点',
|
||||
value: '14',
|
||||
labelEn: 'After Node'
|
||||
}, {
|
||||
label: '加并节点',
|
||||
value: '15',
|
||||
labelEn: 'Node Together'
|
||||
}, {
|
||||
label: '前加签',
|
||||
value: '5',
|
||||
labelEn: 'Before Signature'
|
||||
}, {
|
||||
label: '后加签',
|
||||
value: '6',
|
||||
labelEn: 'After Signature'
|
||||
}, {
|
||||
label: '加并签',
|
||||
value: '7',
|
||||
labelEn: 'Sign Together'
|
||||
}, {
|
||||
label: '退回首节点',
|
||||
value: '1',
|
||||
labelEn: 'Back First'
|
||||
}, {
|
||||
label: '退回上一步',
|
||||
value: '10',
|
||||
labelEn: 'Back Pre'
|
||||
}, {
|
||||
label: '任意跳转',
|
||||
value: '8',
|
||||
labelEn: 'Any Jump'
|
||||
}, {
|
||||
label: '任意驳回',
|
||||
value: '2',
|
||||
labelEn: 'Any Reject'
|
||||
}, {
|
||||
label: '抄送任务',
|
||||
value: '11',
|
||||
labelEn: 'Carbon Copy'
|
||||
}, {
|
||||
label: '传阅任务',
|
||||
value: '12',
|
||||
labelEn: 'Pass Read'
|
||||
}, {
|
||||
label: '下一办理人',
|
||||
value: '3',
|
||||
labelEn: 'Next Handler'
|
||||
}, {
|
||||
label: '转办任务',
|
||||
value: '17',
|
||||
labelEn: 'Turn Job'
|
||||
}, {
|
||||
label: '终止流程',
|
||||
value: '18',
|
||||
labelEn: 'Terminate'
|
||||
}, {
|
||||
label: '提前结束',
|
||||
value: '9',
|
||||
labelEn: 'Early End'
|
||||
}, {
|
||||
label: '作废流程',
|
||||
value: '4',
|
||||
labelEn: 'Invalid'
|
||||
}, {
|
||||
label: '分配参与者',
|
||||
value: '-9',
|
||||
labelEn: 'Dist Person'
|
||||
}],
|
||||
ROUTE_ACTIONS: [{
|
||||
label: '暂停当前节点',
|
||||
value: '-4',
|
||||
labelEn: 'STop Current Node'
|
||||
}, {
|
||||
label: '开启下一节点',
|
||||
value: '8',
|
||||
labelEn: 'Start Next Node'
|
||||
}, {
|
||||
label: '通过',
|
||||
value: '0',
|
||||
labelEn: 'Agree'
|
||||
}, {
|
||||
label: '驳回到其他节点',
|
||||
value: '2',
|
||||
labelEn: 'Reject To Node'
|
||||
}, {
|
||||
label: '退回首节点',
|
||||
value: '1',
|
||||
labelEn: 'Back First'
|
||||
}, {
|
||||
label: '退回上一步',
|
||||
value: '10',
|
||||
labelEn: 'Back Pre'
|
||||
}, {
|
||||
label: '终止流程',
|
||||
value: '18',
|
||||
labelEn: 'Terminate'
|
||||
}, {
|
||||
label: '提前结束',
|
||||
value: '9',
|
||||
labelEn: 'Early End'
|
||||
}, {
|
||||
label: '可自定义更多',
|
||||
value: '_define_',
|
||||
labelEn: '_define_'
|
||||
}],
|
||||
MSG_TYPE: [{
|
||||
label: '个人消息',
|
||||
value: '0'
|
||||
}, {
|
||||
label: '群消息',
|
||||
value: '1'
|
||||
}],
|
||||
NODE_STATUS: [{
|
||||
label: '未开始',
|
||||
value: '-1',
|
||||
labelEn: 'No Start'
|
||||
}, {
|
||||
label: '办理中',
|
||||
value: '0',
|
||||
labelEn: 'Running'
|
||||
}, {
|
||||
label: '完成',
|
||||
value: '1',
|
||||
labelEn: 'Complete'
|
||||
}, {
|
||||
label: '驳回中',
|
||||
value: '2',
|
||||
labelEn: 'Rejecting'
|
||||
}, {
|
||||
label: '跳过',
|
||||
value: '3',
|
||||
labelEn: 'Skip'
|
||||
}, {
|
||||
label: '被驳回',
|
||||
value: '9',
|
||||
labelEn: 'Rejected'
|
||||
}],
|
||||
REJECT_STATUS: [{
|
||||
label: '驳回中',
|
||||
value: '0'
|
||||
}, {
|
||||
label: '结束',
|
||||
value: '1'
|
||||
}],
|
||||
REJECT_TYPE: [{
|
||||
label: '依次返回',
|
||||
value: '0'
|
||||
}, {
|
||||
label: '直接返回',
|
||||
value: '1'
|
||||
}],
|
||||
OPERATOR: [{
|
||||
label: '等于',
|
||||
value: '0'
|
||||
}, {
|
||||
label: '不等于',
|
||||
value: '1'
|
||||
}, {
|
||||
label: '大于',
|
||||
value: '2'
|
||||
}, {
|
||||
label: '大于等于',
|
||||
value: '3'
|
||||
}, {
|
||||
label: '小于',
|
||||
value: '4'
|
||||
}, {
|
||||
label: '小于等于',
|
||||
value: '5'
|
||||
}, {
|
||||
label: '包含',
|
||||
value: '6'
|
||||
}, {
|
||||
label: '不包含',
|
||||
value: '7'
|
||||
}],
|
||||
VAL_TYPE: [{
|
||||
label: '普通模式',
|
||||
value: '-2'
|
||||
}, {
|
||||
label: '分配模式',
|
||||
value: '-1'
|
||||
}, {
|
||||
label: '简单模式',
|
||||
value: '0'
|
||||
}, {
|
||||
label: 'SpEL模式',// 针对人员为固定模式
|
||||
value: '1'
|
||||
}, {
|
||||
label: '专业模式',
|
||||
value: '2'
|
||||
}, {
|
||||
label: 'Http模式',
|
||||
value: '3'
|
||||
}],
|
||||
NOTICE_STATUS: [{
|
||||
label: '发起',
|
||||
value: '-1'
|
||||
}, {
|
||||
label: '办理中',
|
||||
value: '0'
|
||||
}, {
|
||||
label: '完成',
|
||||
value: '1'
|
||||
}],
|
||||
TODO_JOB_STATUS: [{
|
||||
label: '未开始',
|
||||
value: '-1'
|
||||
}, {
|
||||
label: '办理中',
|
||||
value: '0'
|
||||
}, {
|
||||
label: '驳回中',
|
||||
value: '2'
|
||||
}, {
|
||||
label: '被驳回',
|
||||
value: '9'
|
||||
}],
|
||||
FLOW_STATUS: [{
|
||||
label: '撤回',
|
||||
value: '-2',
|
||||
labelEn: 'Recall'
|
||||
}, {
|
||||
label: '发起',
|
||||
value: '-1',
|
||||
labelEn: 'Initiate'
|
||||
}, {
|
||||
label: '运行中',
|
||||
value: '0',
|
||||
labelEn: 'Running'
|
||||
}, {
|
||||
label: '完结',
|
||||
value: '1',
|
||||
labelEn: 'End'
|
||||
}, {
|
||||
label: '作废',
|
||||
value: '2',
|
||||
labelEn: 'Invalid'
|
||||
}, {
|
||||
label: '终止',
|
||||
value: '3',
|
||||
labelEn: 'Terminate'
|
||||
}],
|
||||
NODE_METHODS: methods,
|
||||
FLOW_METHODS: [{
|
||||
label: '当流程发起时',
|
||||
value: 'initiate'
|
||||
}, {
|
||||
label: '当流程完成时',
|
||||
value: 'finish'
|
||||
}, {
|
||||
label: '当流程被撤回时',
|
||||
value: 'recall'
|
||||
}, {
|
||||
label: '当流程被重发时',
|
||||
value: 'reset'
|
||||
}, {
|
||||
label: '当流程被终止时',
|
||||
value: 'terminate'
|
||||
}, {
|
||||
label: '当流程被作废时',
|
||||
value: 'invalid'
|
||||
}, {
|
||||
label: '当流程被恢复时',
|
||||
value: 'recover'
|
||||
}, ...methods],
|
||||
CLAZZ_TYPE: [{
|
||||
label: '节点事件',
|
||||
value: '0'
|
||||
}, {
|
||||
label: '全局事件',
|
||||
value: '1'
|
||||
}],
|
||||
COLUMN_TYPE: [
|
||||
{
|
||||
label: "varchar",
|
||||
value: "varchar"
|
||||
},{
|
||||
label: "bigint",
|
||||
value: "bigint"
|
||||
},{
|
||||
label: "int",
|
||||
value: "int"
|
||||
},{
|
||||
label: "double",
|
||||
value: "double"
|
||||
},{
|
||||
label: "timestamp",
|
||||
value: "timestamp"
|
||||
},{
|
||||
label: "date",
|
||||
value: "date"
|
||||
},{
|
||||
label: "float",
|
||||
value: "float"
|
||||
},{
|
||||
label: "bit",
|
||||
value: "bit"
|
||||
}
|
||||
],
|
||||
LOCK_FLAG: [{
|
||||
label: '有效',
|
||||
value: '0'
|
||||
}, {
|
||||
label: '锁定',
|
||||
value: '9'
|
||||
}],
|
||||
DATABASE_TYPE: [{
|
||||
label: 'MySQL数据库',
|
||||
value: 'MySQL'
|
||||
}, {
|
||||
label: '达梦数据库',
|
||||
value: 'DM'
|
||||
}],
|
||||
FORM_TYPE: [{
|
||||
label: '设计表单',
|
||||
value: '0'
|
||||
}, {
|
||||
label: '系统表单',
|
||||
value: '1'
|
||||
}],
|
||||
FORM_DATA_TYPE: [{
|
||||
label: '字段定义',
|
||||
value: '0'
|
||||
}, {
|
||||
label: '权限配置',
|
||||
value: '1'
|
||||
}, {
|
||||
label: '打印设计',
|
||||
value: '2'
|
||||
}],
|
||||
FORM_PERM_TYPE: [{
|
||||
label: '隐藏',
|
||||
value: '-1'
|
||||
}, {
|
||||
label: '只读',
|
||||
value: '0'
|
||||
}, {
|
||||
label: '可编辑',
|
||||
value: '1'
|
||||
}],
|
||||
ALL_FORM_PERM_TYPE: [{
|
||||
label: '全部只读',
|
||||
value: '0'
|
||||
}, {
|
||||
label: '全部可编辑',
|
||||
value: '1'
|
||||
}],
|
||||
PARAM_RULE_TYPE: [{
|
||||
label: '人员规则',
|
||||
value: '0'
|
||||
}, {
|
||||
label: '条件规则',
|
||||
value: '1'
|
||||
}, {
|
||||
label: '父子流程',
|
||||
value: '2'
|
||||
}, {
|
||||
label: '监听事件',
|
||||
value: '3'
|
||||
}, {
|
||||
label: '查询/更新表单Http参数',
|
||||
value: '4'
|
||||
}, {
|
||||
label: '保存子流程表单Http接口',
|
||||
value: '5'
|
||||
}, {
|
||||
label: '更新子流程表单Http接口',
|
||||
value: '6'
|
||||
}, {
|
||||
label: '更新父流程表单Http接口',
|
||||
value: '7'
|
||||
}, {
|
||||
label: '节点路由',
|
||||
value: '8'
|
||||
}],
|
||||
OR_OR_AND: [{
|
||||
label: '或',
|
||||
value: '0',
|
||||
}, {
|
||||
label: '且',
|
||||
value: '1',
|
||||
}],
|
||||
PARAM_FROM: [{
|
||||
label: '请求头',
|
||||
value: '0',
|
||||
}, {
|
||||
label: '传参',
|
||||
value: '1',
|
||||
}, {
|
||||
label: '回参',
|
||||
value: '2',
|
||||
}],
|
||||
PARAM_VAL_TYPE: paramValTypes,
|
||||
SYS_PARAM_VAL_TYPE: [{
|
||||
label: '系统字段',
|
||||
value: '0',
|
||||
}, ...paramValTypes.slice(1)
|
||||
],
|
||||
HTTP_METHODS: [{
|
||||
label: 'GET',
|
||||
value: 'GET',
|
||||
}, {
|
||||
label: 'POST',
|
||||
value: 'POST',
|
||||
}, {
|
||||
label: 'PUT',
|
||||
value: 'PUT',
|
||||
}, {
|
||||
label: 'DELETE',
|
||||
value: 'DELETE',
|
||||
}],
|
||||
PARAM_TYPES: [{
|
||||
label: 'json',
|
||||
value: '0',
|
||||
}, {
|
||||
label: 'form',
|
||||
value: '1',
|
||||
}],
|
||||
PREDICT_OPTIONS: [{
|
||||
label: '预测参与者',
|
||||
value: 1,
|
||||
}, {
|
||||
label: '预测监听事件',
|
||||
value: 2,
|
||||
}, {
|
||||
label: '预测节点路由',
|
||||
value: 4,
|
||||
}, {
|
||||
label: '预测子流程',
|
||||
value: 8
|
||||
}, {
|
||||
label: '预测审批过程',
|
||||
value: 16,
|
||||
}, {
|
||||
label: '预测非自动流转',
|
||||
value: 32,
|
||||
}, {
|
||||
label: '预测流程轨迹',
|
||||
value: 0,
|
||||
disabled: true
|
||||
}, {
|
||||
label: '预测连线条件',
|
||||
value: 0,
|
||||
disabled: true
|
||||
}],
|
||||
}
|
||||
255
src/flow/support/extend.ts
Normal file
255
src/flow/support/extend.ts
Normal file
@@ -0,0 +1,255 @@
|
||||
import {RouteRecordRaw} from "vue-router";
|
||||
import {flowConfig} from "/@/flow/designer/config/flow-config";
|
||||
import {Session} from "/@/utils/storage";
|
||||
import {locationHash, vueKeySys} from "/@/api/order/order-key-vue";
|
||||
import {useUserInfo} from "/@/stores/userInfo";
|
||||
import {validateNull} from "/@/utils/validate";
|
||||
import {getToDoneDetail} from "/@/api/jsonflow/hi-job";
|
||||
import * as doJob from "/@/api/jsonflow/do-job";
|
||||
import {DIC_PROP} from "/@/flow/support/dict-prop";
|
||||
import {paramsFilter} from "/@/flow";
|
||||
|
||||
// 移动端path
|
||||
export const vueAppKeyHash = {
|
||||
flowview: 'jsonflow/flow-design/view'
|
||||
, jobview: 'order/run-application/view'
|
||||
, jobinitiate: 'order/flow-application/initiate'
|
||||
, jobinitagain: 'order/run-application/initiate'
|
||||
, jobform: 'order/run-application/flow'
|
||||
}
|
||||
|
||||
export async function initJobDataByApp($route, props) {
|
||||
let appdata = flowConfig.mobileConfig.mobilePrefix
|
||||
let app = !validateNull($route.query[appdata]);
|
||||
if (!app) return
|
||||
let split = $route.query[appdata].split('-');
|
||||
let query = {orderId: split[1], id: split[2], flowInstId: split[3], runNodeId: split[4], elTabId: split[5], hiJob: split[6] === 'Y'}
|
||||
if (query.hiJob) {
|
||||
await handleToDoneDetail(query, props, '1')
|
||||
} else {
|
||||
await handleTodoDetail(query, props)
|
||||
}
|
||||
props.currJob.hiJob = query.hiJob
|
||||
props.currElTab.id = query.elTabId
|
||||
}
|
||||
|
||||
export async function handleTodoDetail(row, data, isView?, isRead?, $message?, getDataList?) {
|
||||
let query = Object.assign({}, row, {order: null, elTabs: null});
|
||||
query = paramsFilter(query)
|
||||
await doJob.getTodoDetail(query).then(response => {
|
||||
if ($message) data.currJob = response.data
|
||||
// 兼容移动端
|
||||
else Object.assign(data.currJob, response.data);
|
||||
if (isRead !== '1' && isView !== '1' && DIC_PROP.NODE_STATUS[2].value === data.currJob.status) {
|
||||
if ($message) {
|
||||
$message.warning('当前任务已被其他办理人审批')
|
||||
getDataList();
|
||||
}
|
||||
} else {
|
||||
if (isView !== '1') {
|
||||
if (isRead === '1') data.currJob.hiJob = true
|
||||
doJob.isRead(data.currJob).then(() => {
|
||||
row.isRead = '1'
|
||||
// 独立标签都需执行
|
||||
if (isRead === '1') getDataList();
|
||||
})
|
||||
} else data.currJob.hiJob = true
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const channel = new BroadcastChannel('currJob_channel');
|
||||
export async function handleFlowPreview($router, row, isView?, isRead?, $message?, flowJob?, getDataList?) {
|
||||
channel.onmessage = (event) => {
|
||||
flowJob.delJobLen();
|
||||
// getDataList();
|
||||
};
|
||||
row.isHiJob = '0'
|
||||
windowOpenFlow($router, row, isView, isRead)
|
||||
}
|
||||
|
||||
function windowOpenFlow($router, row, isView?, isRead?, isApp?, isForm?) {
|
||||
let query = { id: row.id, flowInstId: row.flowInstId, isHiJob: row.isHiJob, isView, isRead, isApp, isForm }
|
||||
$router.push({
|
||||
name: dynamicRoutesFlow[0].name,
|
||||
query: query
|
||||
})
|
||||
/*const routeUrl = $router.resolve({
|
||||
path: dynamicRoutesFlow[0].path,
|
||||
query: query,
|
||||
});
|
||||
window.open(routeUrl.href, '_blank');*/
|
||||
}
|
||||
|
||||
export async function handleToDoneDetail(row, data, isApp?, isForm?) {
|
||||
let query = Object.assign({}, row, {order: null, elTabs: null});
|
||||
query = paramsFilter(query)
|
||||
await getToDoneDetail(query).then(response => {
|
||||
// 兼容移动端
|
||||
if (isApp === '1') {
|
||||
Object.assign(data.currJob, response.data);
|
||||
return
|
||||
}
|
||||
data.currJob = response.data
|
||||
if (isForm === '1') {
|
||||
let elTabs = data.currJob.elTabs;
|
||||
data.currJob.elTabs = elTabs.filter(f => vueKeySys.sysPaths.includes(f.path))
|
||||
// 判断是否只配置表单
|
||||
if (validateNull(data.currJob.elTabs)) data.currJob.elTabs = elTabs
|
||||
}
|
||||
// 历史查看标识
|
||||
data.currJob.hiJob = true
|
||||
})
|
||||
}
|
||||
|
||||
export async function openFlowPreview($router, row, isForm?) {
|
||||
row.isHiJob = '1'
|
||||
windowOpenFlow($router, row, null, null, null, isForm)
|
||||
}
|
||||
|
||||
export async function replaceRouterRoute(route, router) {
|
||||
// 兼容移动端
|
||||
let appPath = '', appdata = flowConfig.mobileConfig.mobilePrefix;
|
||||
// 防止重复进入
|
||||
if (route.fullPath.indexOf('?' + appdata + '=') !== -1) {
|
||||
return
|
||||
}
|
||||
let index = route.fullPath.indexOf(appdata);
|
||||
if (index !== -1) {
|
||||
let last = route.fullPath.indexOf("-");
|
||||
if (route.fullPath.indexOf('?' + appdata + '=') !== -1) last = route.fullPath.indexOf("?");
|
||||
appPath = route.fullPath.substring(route.fullPath.indexOf("/") + 1, last);
|
||||
|
||||
router.currentRoute.value.name = "flowRoutes." + appPath
|
||||
let fullPath = route.fullPath.replace('-', '?');
|
||||
fullPath = fullPath.replace('-', '=');
|
||||
fullPath = fullPath.replace(appPath, vueAppKeyHash[appPath]);
|
||||
|
||||
let split = fullPath.split('=')[1].split('-');
|
||||
// 存储token 信息
|
||||
if (split[0].indexOf('_') !== -1) {
|
||||
// 判断是否微服务版本
|
||||
let tokenTenant = split[0].replace('_', '-').split('-')
|
||||
Session.set('tenantId', tokenTenant[0]);
|
||||
Session.set('token', tokenTenant[1].replaceAll('_', '-'));
|
||||
} else {
|
||||
Session.set('token', split[0]);
|
||||
}
|
||||
// 触发初始化用户信息 pinia
|
||||
await useUserInfo().setUserInfos();
|
||||
await router.replace(fullPath);
|
||||
}
|
||||
}
|
||||
|
||||
export async function replaceRouterThirdRoute(route, router) {
|
||||
let fullPath = route.fullPath;
|
||||
// 防止重复进入
|
||||
if (fullPath.indexOf('tokenLoaded') !== -1) {
|
||||
// 防止刷新后无用户信息
|
||||
await useUserInfo().setUserInfos();
|
||||
return
|
||||
}
|
||||
// 三方系统单点
|
||||
if (fullPath.indexOf('token') !== -1) {
|
||||
Session.clear()
|
||||
fullPath = fullPath.replace('token', 'tokenLoaded');
|
||||
// 存储token 信息
|
||||
Session.set('tenantId', route.query.tenantId);
|
||||
Session.set('token', route.query.token);
|
||||
} else {
|
||||
fullPath = route.fullPath + '&tokenLoaded=1';
|
||||
}
|
||||
// 必须获取用户信息
|
||||
await useUserInfo().setUserInfos();
|
||||
router.currentRoute.value.name = "staticRoutes.handleoajob"
|
||||
await router.replace(fullPath);
|
||||
}
|
||||
|
||||
export function initJsonFlowViewByApp($route, props) {
|
||||
let appdata = flowConfig.mobileConfig.mobilePrefix
|
||||
let app = !validateNull($route.query[appdata]);
|
||||
if (app) {
|
||||
let split = $route.query[appdata].split('-');
|
||||
let query = {defFlowId: split[1], flowInstId: split[2] === 'undefined' ? '' : split[2]}
|
||||
Object.assign(props.currJob, query);
|
||||
}
|
||||
}
|
||||
|
||||
export function windowLocationHrefParam(hash, param = '') {
|
||||
return window.location.href = window.location.origin + window.location.pathname + locationHash[hash] + param
|
||||
}
|
||||
|
||||
export function windowLocationHref(hash) {
|
||||
window.location.href = window.location.origin + window.location.pathname + hash
|
||||
}
|
||||
|
||||
/**
|
||||
* 定义静态路由(默认路由)
|
||||
*/
|
||||
export const staticRoutesFlow: Array<RouteRecordRaw> = [
|
||||
{
|
||||
path: '/jsonflow/flow-design/view',
|
||||
name: 'flowRoutes.flowview',
|
||||
component: () => import('/@/views/jsonflow/flow-design/view.vue'),
|
||||
meta: {
|
||||
isAuth: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '/order/run-application/view',
|
||||
name: 'flowRoutes.jobview',
|
||||
component: () => import('/@/views/order/run-application/view.vue'),
|
||||
meta: {
|
||||
isAuth: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '/order/flow-application/initiate',
|
||||
name: 'flowRoutes.jobinitiate',
|
||||
component: () => import('/@/views/order/flow-application/initiate.vue'),
|
||||
meta: {
|
||||
isAuth: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '/order/run-application/initiate',
|
||||
name: 'flowRoutes.jobinitagain',
|
||||
component: () => import('/@/views/order/run-application/initiate.vue'),
|
||||
meta: {
|
||||
isAuth: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '/order/run-application/flow',
|
||||
name: 'flowRoutes.jobform',
|
||||
component: () => import('/@/views/order/run-application/flow.vue'),
|
||||
meta: {
|
||||
isAuth: false,
|
||||
},
|
||||
}
|
||||
];
|
||||
|
||||
/**
|
||||
* 定义静态路由(默认路由)
|
||||
* 前端添加路由,请在此处加
|
||||
*/
|
||||
export const dynamicRoutesFlow: Array<RouteRecordRaw> = [
|
||||
{
|
||||
path: '/flow/components/handle-job/handle',
|
||||
name: 'flowRoutes.handlejob',
|
||||
component: () => import('/@/flow/components/handle-job/handle.vue'),
|
||||
meta: {
|
||||
isKeepAlive: false,
|
||||
isHide: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '/jsonflow/flow-design/index',
|
||||
name: 'flowRoutes.flowdesign',
|
||||
component: () => import('/@/views/jsonflow/flow-design/index.vue'),
|
||||
meta: {
|
||||
isKeepAlive: false,
|
||||
isHide: true,
|
||||
},
|
||||
},
|
||||
];
|
||||
85
src/flow/support/prop-const.ts
Normal file
85
src/flow/support/prop-const.ts
Normal file
@@ -0,0 +1,85 @@
|
||||
/**
|
||||
* 字段属性常量
|
||||
* @author luolin
|
||||
*/
|
||||
const varKeyVal = {
|
||||
sys: '#sys.',
|
||||
flow: '#flow.',
|
||||
order: '#order.',
|
||||
form: '#form.',
|
||||
user: '#user.',
|
||||
var: '#var.',
|
||||
dist: '#dist.',
|
||||
}
|
||||
|
||||
export let PROP_CONST = {
|
||||
COMMON: {
|
||||
flowInstId: 'flowInstId',
|
||||
code: 'code',
|
||||
userUserPrefix: 'USER_',
|
||||
userRolePrefix: 'ROLE_',
|
||||
userPostPrefix: 'POST_',
|
||||
userDeptPrefix: 'DEPT_',
|
||||
tableName: 'order_run_application',
|
||||
},
|
||||
FORM_DESIGN: {
|
||||
// 注意相反的!
|
||||
subForm: 'group',
|
||||
group: 'subForm',
|
||||
fcRow: 'fcRow',
|
||||
tableForm: 'tableForm',
|
||||
fcTable: 'fcTable',
|
||||
elTabs: 'elTabs',
|
||||
elCollapse: 'elCollapse',
|
||||
elCard: 'elCard',
|
||||
},
|
||||
TEXT_DESC: {
|
||||
condSpELExplain: "当选择SpEL模式时, SpEL表达式必须符合SpEL格式, #anyKey表示表单的字段 ( 默认#form.前缀 ), 例如0<#days && #days>=3 ( 加前缀#var.anyKey表示从流程条件中取值,#user.anyKey表示从当前用户中取值 )",
|
||||
condUserExplain: "1、SpEL上下文表达式 ( 常用于发起时可确定的参与者值 ), #anyKey表示表单的字段 ( 默认#form.前缀 ), 例如#userId ( 加前缀#var.anyKey表示从流程条件中取值,#user.anyKey表示从当前用户中取值 )",
|
||||
condMethodExplain1: "采用函数表达式 ( 以下两种方式均支持自定义任意扩展 ), 返回值为字符串 1 ( 满足 ) 或 0 ( 不满足 ), 满足您复杂条件的场景 :",
|
||||
condMethodExplain2: "1、SpEL上下文表达式, #anyKey表示表单的字段 ( 默认#form.前缀 ), 例如#isGoEnd ( 加前缀#var.anyKey表示从流程条件中取值,#user.anyKey表示从当前用户中取值 )",
|
||||
condMethodExplain3: '2、SpringBean函数表达式, 如某个Bean对象的beanName为bean,取值#bean.anyMethod(), 带参数格式#bean.anyMethod(String#admin,SysUser#{"username": "admin"},SysRole#NULL)',
|
||||
condMethodExplain4: '备注:函数表达式 ( 参数支持SpEL上下文表达式且需带前缀如Long#form.userId ), 参数格式为#bean.anyMethod(参数类型#参数值), 多个参数逗号分割。类型为复杂对象时参数值为Json格式',
|
||||
condMethodExplain5: '字段可包含参与者类型、参与者ID、参与者任务名称、参与者任务排序( 只有ID值时,参与者类型默认人员,参与者ID是人员ID )',
|
||||
condMethodExplain6: '返回值要求请参考路由指定动作接口的入参',
|
||||
},
|
||||
HANDOVER_FLOW: {
|
||||
userKey: {
|
||||
create_user:'create_user'
|
||||
,receive_user:'receive_user'
|
||||
,curr_dept_manager:'curr_dept_manager'
|
||||
}
|
||||
},
|
||||
LOAD_USER_ROLE: [
|
||||
{key: "users", type: "jobType", typeVal: "0", field: 'roleId'}, {key: "roles", type: "jobType", typeVal: "1", field: 'roleId'},
|
||||
{key: "posts", type: "jobType", typeVal: "2", field: 'roleId'}, {key: "depts", type: "jobType", typeVal: "3", field: 'roleId'}
|
||||
],
|
||||
SYS_FIELDS: [
|
||||
// 用 order 简化报表打印回显
|
||||
{prefix: varKeyVal.order, prop:"flowInstId", label:"流程实例ID", valueKey: "id", showKey: "flowName"}
|
||||
,{prefix: varKeyVal.order, prop:"flowKey", label:"流程业务KEY"}
|
||||
,{prefix: varKeyVal.order, prop:"createUser", label:"发起人", valueKey: "userId", showKey: "name"}
|
||||
,{prefix: varKeyVal.order, prop:"code", label:"工单编号"}
|
||||
,{prefix: varKeyVal.order, prop:"status", label:"工单状态"}
|
||||
,{prefix: varKeyVal.order, prop:"createTime", label:"发起时间"}
|
||||
,{prefix: varKeyVal.order, prop:"finishTime", label:"完成时间"}
|
||||
,{prefix: varKeyVal.order, prop:"_define_", label:"可自定义更多"}
|
||||
],
|
||||
VAR_KEY_VAL: {
|
||||
route: '#route.',
|
||||
routeName: '路由规则',
|
||||
person: '#person.',
|
||||
personName: '审批规则',
|
||||
link: '#link.',
|
||||
// 取值来源
|
||||
order: varKeyVal.order,
|
||||
form: varKeyVal.form,
|
||||
user: varKeyVal.user,
|
||||
var: varKeyVal.var,
|
||||
dist: varKeyVal.dist,
|
||||
},
|
||||
FLOW_METHOD: {
|
||||
whoseLeader: {name: '发起人本人', userId: varKeyVal.order.replace("#", "") + 'createUser'}
|
||||
},
|
||||
}
|
||||
|
||||
380
src/flow/utils/form-perm.ts
Normal file
380
src/flow/utils/form-perm.ts
Normal file
@@ -0,0 +1,380 @@
|
||||
import {listFormOption, listPrintTemp, listStartPerm} from "/@/api/jsonflow/form-option";
|
||||
import {DIC_PROP} from "/@/flow/support/dict-prop";
|
||||
import {useMessage} from "/@/hooks/message";
|
||||
import {validateNull} from "/@/utils/validate";
|
||||
import {PROP_CONST} from "/@/flow/support/prop-const";
|
||||
import {deepClone} from "/@/utils/other";
|
||||
import {validateRunFlow} from "/@/flow";
|
||||
|
||||
// 处理表单权限
|
||||
export async function handleFormStartPerm(hiddenFields, disabledFields, formInfo, defFlowId, flowKey, formType) {
|
||||
if (!formType) formType = DIC_PROP.FORM_TYPE[1].value
|
||||
let resp = await listStartPerm({
|
||||
type: DIC_PROP.FORM_DATA_TYPE[1].value, formType: formType,
|
||||
defFlowId: defFlowId, flowKey: flowKey
|
||||
}).catch(() => {
|
||||
useMessage().error("获取表单字段权限失败");
|
||||
})
|
||||
let data = resp?.data;
|
||||
// 表单默认权限及查看时查询打印模板
|
||||
if (validateNull(data.columns)) return {elTab: data.tabsOption};
|
||||
let widgetList = formInfo ? formInfo.widgetList : null
|
||||
// 全部只读部分可编辑
|
||||
let callback = () => { handleFormPerms(hiddenFields, disabledFields, widgetList, data.tabsOption, data.columns, data.defFlowId, data.flowNodeId) }
|
||||
initIsFormEdit(data.tabsOption, data.columns, data.defFlowId, data.flowNodeId)
|
||||
return {elTab: data.tabsOption, callback, widgetList};
|
||||
}
|
||||
|
||||
export async function handleDesignFormPerm(props, formInfo, elTab, formType, formId) {
|
||||
let defFlowId = props.currJob.defFlowId;
|
||||
let flowInstId = props.currJob.flowInstId;
|
||||
let flowNodeId = props.currJob.flowNodeId;
|
||||
let resp = await listFormOption({
|
||||
type: DIC_PROP.FORM_DATA_TYPE[1].value, formType: formType, formId: formId,
|
||||
defFlowId: defFlowId, flowInstId: flowInstId, flowNodeId: flowNodeId, isOnlyCurr: '1'
|
||||
}).catch(() => {
|
||||
useMessage().error("获取表单字段权限失败");
|
||||
})
|
||||
if (validateNull(resp.data)) return {};
|
||||
// 全部只读部分可编辑
|
||||
let callback = () => { handleFormPerms(null, null, formInfo.widgetList, elTab, resp.data, defFlowId, flowNodeId) }
|
||||
initIsFormEdit(elTab, resp.data, defFlowId, flowNodeId)
|
||||
return {columns: resp.data, callback, widgetList: formInfo.widgetList};
|
||||
}
|
||||
|
||||
export async function handleCustomFormPerm(props, hiddenFields, disabledFields, elTab: Object) {
|
||||
let defFlowId = props.currJob.defFlowId;
|
||||
let flowInstId = props.currJob.flowInstId;
|
||||
let flowNodeId = props.currJob.flowNodeId;
|
||||
let resp = await listFormOption({
|
||||
type: DIC_PROP.FORM_DATA_TYPE[1].value, formType: DIC_PROP.FORM_TYPE[1].value, formId: elTab.id,
|
||||
defFlowId: defFlowId, flowInstId: flowInstId, flowNodeId: flowNodeId, isOnlyCurr: '1'
|
||||
}).catch(() => {
|
||||
useMessage().error("获取表单字段权限失败");
|
||||
})
|
||||
if (validateNull(resp.data)) return {};
|
||||
// 全部只读部分可编辑
|
||||
let callback = () => { handleFormPerms(hiddenFields, disabledFields, null, elTab, resp.data, defFlowId, flowNodeId) }
|
||||
initIsFormEdit(elTab, resp.data, defFlowId, flowNodeId)
|
||||
return {columns: resp.data, callback};
|
||||
}
|
||||
|
||||
// 查询配置信息
|
||||
export async function handleFormPrint(form, formType, formId, isOnlyCurr) {
|
||||
await listPrintTemp({
|
||||
flowInstId: form.flowInstId,
|
||||
type: DIC_PROP.FORM_DATA_TYPE[2].value, formType: formType, formId: formId,
|
||||
isOnlyCurr: isOnlyCurr
|
||||
}).then(resp => {
|
||||
let res = resp.data;
|
||||
form.printInfo = res.printInfo
|
||||
}).catch(() => {
|
||||
useMessage().error("获取表单打印模板失败");
|
||||
})
|
||||
}
|
||||
|
||||
// 构建字段权限列表
|
||||
export function buildFieldPerms(formFieldPerms: any[], widgetList: any[], subForm?, isField?) {
|
||||
widgetList.forEach(f => {
|
||||
// 处理子表单元素、分组元素
|
||||
let isSubForm = f.type.indexOf(PROP_CONST.FORM_DESIGN.subForm) !== -1;
|
||||
if (isSubForm || f.type.indexOf(PROP_CONST.FORM_DESIGN.group) !== -1) {
|
||||
let widgetList = f.props.rule;
|
||||
if (validateNull(widgetList)) return;
|
||||
if (isSubForm) pushFormFieldPerms(formFieldPerms, f, null, isField)
|
||||
let subForm2 = isSubForm ? f.field : subForm
|
||||
buildFieldPerms(formFieldPerms, widgetList, subForm2, isField);
|
||||
}
|
||||
// 处理栅格元素、标签页元素、折叠面板元素
|
||||
else if (f.type.indexOf(PROP_CONST.FORM_DESIGN.fcRow) !== -1 || f.type.indexOf(PROP_CONST.FORM_DESIGN.elTabs) !== -1 || f.type.indexOf(PROP_CONST.FORM_DESIGN.elCollapse) !== -1) {
|
||||
let cols = f.children;
|
||||
if (validateNull(cols)) return;
|
||||
cols.forEach(col => {
|
||||
if (validateNull(col.children)) return;
|
||||
buildFieldPerms(formFieldPerms, col.children, subForm, isField);
|
||||
})
|
||||
}
|
||||
// 处理表格表单元素
|
||||
else if (f.type.indexOf(PROP_CONST.FORM_DESIGN.tableForm) !== -1) {
|
||||
let cols = f.props.columns;
|
||||
if (validateNull(cols)) return;
|
||||
pushFormFieldPerms(formFieldPerms, f, null, isField)
|
||||
cols.forEach(col => {
|
||||
if (validateNull(col.rule)) return;
|
||||
buildFieldPerms(formFieldPerms, col.rule, f.field, isField);
|
||||
})
|
||||
}
|
||||
// 处理表格布局元素、卡片元素
|
||||
else if (f.type.indexOf(PROP_CONST.FORM_DESIGN.fcTable) !== -1 || f.type.indexOf(PROP_CONST.FORM_DESIGN.elCard) !== -1) {
|
||||
let cols = f.children;
|
||||
if (validateNull(cols)) return;
|
||||
buildFieldPerms(formFieldPerms, cols, subForm, isField);
|
||||
}
|
||||
else {
|
||||
pushFormFieldPerms(formFieldPerms, f, subForm, isField)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function pushFormFieldPerms(formFieldPerms, f, subForm?, isField?){
|
||||
let label = f.title;
|
||||
/*if (f.type.indexOf(PROP_CONST.FORM_DESIGN.subForm) !== -1) {
|
||||
label = "(子表单)" + label
|
||||
}
|
||||
if (f.type.indexOf(PROP_CONST.FORM_DESIGN.tableForm) !== -1) {
|
||||
label = "(表单表格)" + label
|
||||
}*/
|
||||
// 排除无名称字段
|
||||
if (!label) return
|
||||
formFieldPerms.push({
|
||||
propId: f.name,
|
||||
prop: f.field,
|
||||
label: label,
|
||||
subForm: subForm,
|
||||
propType: f.type,
|
||||
__fc__: isField ? f.__fc__ : null
|
||||
})
|
||||
}
|
||||
|
||||
export function handleFormFieldPerms(data, $message, form, prefix) {
|
||||
let isDesign = form.type !== DIC_PROP.FORM_TYPE[1].value;
|
||||
if (isDesign) {
|
||||
if (validateNull(form.formInfo)) {
|
||||
data.formFieldPerms = [];
|
||||
if ($message) $message.warning("当前选择的设计表单无字段信息,请先在《表单设计器》中设计")
|
||||
return true;
|
||||
} else {
|
||||
buildFormFieldPerms(data, form.formInfo, prefix)
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
let formFieldPerms = form.formFieldPerms;
|
||||
if (!validateNull(formFieldPerms)) {
|
||||
// 不影响表单设计信息
|
||||
data.formFieldPerms = deepClone(formFieldPerms)
|
||||
handleFieldProp(data.formFieldPerms, prefix)
|
||||
return true;
|
||||
}
|
||||
// 此处要查询接口
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export function buildSysFieldsFormOption(data, props, $message) {
|
||||
let formFieldPerms = deepClone(PROP_CONST.SYS_FIELDS);
|
||||
handleFieldProp(formFieldPerms, null)
|
||||
data.allFieldPerms = [{label: '系统字段', options: formFieldPerms}]
|
||||
validateListFormOption(data, props, $message, () => {
|
||||
let options = {label: '表单字段', options: data.formFieldPerms}
|
||||
data.allFieldPerms.push(options)
|
||||
})
|
||||
}
|
||||
|
||||
export async function validateListFormOption(data, props, $message?, callback?) {
|
||||
let form = props.currFlowForm ? props.currFlowForm : {}
|
||||
let formId = props.flowData.attrs.formId;
|
||||
if (validateNull(props.currFlowForm)) {
|
||||
if (!formId) {
|
||||
if ($message) $message.warning("流程属性【关联表单】为空,请点击左上角《流程属性》选择");
|
||||
return
|
||||
}
|
||||
form.type = DIC_PROP.FORM_TYPE[1].value
|
||||
form.id = formId
|
||||
} else {
|
||||
let isReturn = handleFormFieldPerms(data, $message, form, PROP_CONST.VAR_KEY_VAL.form);
|
||||
if (isReturn) {
|
||||
if (callback) callback()
|
||||
return
|
||||
}
|
||||
}
|
||||
// 判断流程实例独立配置
|
||||
let flowInstId = validateRunFlow(props);
|
||||
// 当系统表单没配置时查表数据
|
||||
await listFormOption({
|
||||
flowInstId: flowInstId, type: DIC_PROP.FORM_DATA_TYPE[0].value,
|
||||
formType: form.type, formId: form.id
|
||||
}).then(resp => {
|
||||
let res = resp.data;
|
||||
if (!validateNull(res)) {
|
||||
data.formFieldPerms = res
|
||||
handleFieldProp(data.formFieldPerms, PROP_CONST.VAR_KEY_VAL.form)
|
||||
} else {
|
||||
validateFormType(data, form, $message)
|
||||
}
|
||||
if (callback) callback()
|
||||
}).catch(() => {
|
||||
$message.error("获取系统表单字段权限失败");
|
||||
})
|
||||
}
|
||||
|
||||
function validateFormType(data, form, $message) {
|
||||
// 判断系统表单
|
||||
if (form.type === DIC_PROP.FORM_TYPE[1].value) {
|
||||
data.formFieldPerms = [];
|
||||
if ($message) $message.warning("当前选择的系统表单无字段信息,请先在表单设计中录入")
|
||||
return;
|
||||
}
|
||||
buildFormFieldPerms(data, form.formInfo, PROP_CONST.VAR_KEY_VAL.form)
|
||||
}
|
||||
|
||||
function buildFormFieldPerms(data, formInfo, prefix) {
|
||||
data.formFieldPerms = []
|
||||
buildFieldPerms(data.formFieldPerms, formInfo.widgetList);
|
||||
handleFieldProp(data.formFieldPerms, prefix)
|
||||
}
|
||||
|
||||
export function handleFieldProp(formFieldPerms, prefix) {
|
||||
if (validateNull(formFieldPerms)) return
|
||||
formFieldPerms.forEach(each => {
|
||||
// 暂时未用each.prefix
|
||||
each.prop = (each.prefix ? each.prefix : prefix) + (each.subForm ? each.subForm + '.' + each.prop : each.prop)
|
||||
})
|
||||
}
|
||||
|
||||
// 初始化
|
||||
function initIsFormEdit(elTab, formPerms, defFlowId, flowNodeId) {
|
||||
if (validateNull(formPerms)) return;
|
||||
// 判断是否存在
|
||||
let formFieldPerms = formPerms.filter(f => f.defFlowId === defFlowId && f.flowNodeId === flowNodeId);
|
||||
if (validateNull(formFieldPerms)) return
|
||||
// 判断全部只读部分可编辑
|
||||
let find = formFieldPerms.find(f => f.permType === '1');
|
||||
if (find && elTab.isFormEdit === '0') {
|
||||
// 记录初始值
|
||||
elTab.defFormEdit = elTab.isFormEdit
|
||||
elTab.isFormEdit = '1'
|
||||
}
|
||||
}
|
||||
|
||||
// 处理表单字段权限
|
||||
function handleFormPerms(hiddenFields, disabledFields, columns, elTab, formPerms, defFlowId, flowNodeId) {
|
||||
if (validateNull(formPerms)) return;
|
||||
// 判断是否存在
|
||||
let formFieldPerms = formPerms.filter(f => f.defFlowId === defFlowId && f.flowNodeId === flowNodeId);
|
||||
if (validateNull(formFieldPerms)) return
|
||||
// 处理权限
|
||||
formFieldPerms.forEach(each => {
|
||||
if (validateNull(columns)) {
|
||||
extractedPermType(hiddenFields, disabledFields, elTab, each, null);
|
||||
} else {
|
||||
doFormFieldPerms(hiddenFields, disabledFields, columns, each, elTab);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function doFormFieldPerms(hiddenFields, disabledFields, columns, each, elTab) {
|
||||
let find = columns.find(f => each.propId ? f.name === each.propId : f.field === each.prop)
|
||||
if (find) {
|
||||
extractedPermType(hiddenFields, disabledFields, elTab, each, find);
|
||||
return
|
||||
}
|
||||
columns.forEach(f => {
|
||||
// 处理子表单元素、分组元素
|
||||
let isSubForm = f.type.indexOf(PROP_CONST.FORM_DESIGN.subForm) !== -1;
|
||||
if (isSubForm || f.type.indexOf(PROP_CONST.FORM_DESIGN.group) !== -1) {
|
||||
let widgetList = f.props.rule;
|
||||
if (validateNull(widgetList)) return;
|
||||
doFormFieldPerms(hiddenFields, disabledFields, widgetList, each, elTab)
|
||||
}
|
||||
// 处理栅格元素、标签页元素、折叠面板元素
|
||||
else if (f.type.indexOf(PROP_CONST.FORM_DESIGN.fcRow) !== -1 || f.type.indexOf(PROP_CONST.FORM_DESIGN.elTabs) !== -1 || f.type.indexOf(PROP_CONST.FORM_DESIGN.elCollapse) !== -1) {
|
||||
let cols = f.children;
|
||||
if (validateNull(cols)) return;
|
||||
cols.forEach(col => {
|
||||
if (validateNull(col.children)) return;
|
||||
doFormFieldPerms(hiddenFields, disabledFields, col.children, each, elTab)
|
||||
})
|
||||
}
|
||||
// 处理表格表单元素
|
||||
else if (f.type.indexOf(PROP_CONST.FORM_DESIGN.tableForm) !== -1) {
|
||||
let cols = f.props.columns;
|
||||
if (validateNull(cols)) return;
|
||||
cols.forEach(col => {
|
||||
if (validateNull(col.rule)) return;
|
||||
doFormFieldPerms(hiddenFields, disabledFields, col.rule, each, elTab)
|
||||
})
|
||||
}
|
||||
// 处理表格布局元素、卡片元素
|
||||
else if (f.type.indexOf(PROP_CONST.FORM_DESIGN.fcTable) !== -1 || f.type.indexOf(PROP_CONST.FORM_DESIGN.elCard) !== -1) {
|
||||
let cols = f.children;
|
||||
if (validateNull(cols)) return;
|
||||
doFormFieldPerms(hiddenFields, disabledFields, cols, each, elTab)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function extractedPermType(hiddenFields, disabledFields, elTab, each, find) {
|
||||
if (elTab.type === DIC_PROP.FORM_TYPE[1].value) {
|
||||
let propDef = each.subForm ? each.subForm + '.' + each.prop : each.prop;
|
||||
if (each.permType === '-1') {
|
||||
hiddenFields[propDef] = true
|
||||
} else if (each.permType === '1') {
|
||||
disabledFields[propDef] = false
|
||||
} else if (each.permType === '0') {
|
||||
disabledFields[propDef] = true
|
||||
}
|
||||
} else {
|
||||
if (each.permType === '-1') {
|
||||
find.hidden = true
|
||||
} else if (each.permType === '1') {
|
||||
if (!find.props) find.props = {}
|
||||
find.props.disabled = false
|
||||
} else if (each.permType === '0') {
|
||||
if (!find.props) find.props = {}
|
||||
find.props.disabled = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function disabledAllFormFields(columns, permType) {
|
||||
if (validateNull(columns)) return
|
||||
columns.forEach(f => {
|
||||
// 处理子表单元素、分组元素
|
||||
let isSubForm = f.type.indexOf(PROP_CONST.FORM_DESIGN.subForm) !== -1;
|
||||
if (isSubForm || f.type.indexOf(PROP_CONST.FORM_DESIGN.group) !== -1) {
|
||||
let widgetList = f.props.rule;
|
||||
if (validateNull(widgetList)) return;
|
||||
if (isSubForm) disabledFormField(f, permType)
|
||||
disabledAllFormFields(widgetList, permType)
|
||||
}
|
||||
// 处理栅格元素、标签页元素、折叠面板元素
|
||||
else if (f.type.indexOf(PROP_CONST.FORM_DESIGN.fcRow) !== -1 || f.type.indexOf(PROP_CONST.FORM_DESIGN.elTabs) !== -1 || f.type.indexOf(PROP_CONST.FORM_DESIGN.elCollapse) !== -1) {
|
||||
let cols = f.children;
|
||||
if (validateNull(cols)) return;
|
||||
cols.forEach(col => {
|
||||
if (validateNull(col.children)) return;
|
||||
disabledAllFormFields(col.children, permType)
|
||||
})
|
||||
}
|
||||
// 处理表格表单元素
|
||||
else if (f.type.indexOf(PROP_CONST.FORM_DESIGN.tableForm) !== -1) {
|
||||
let cols = f.props.columns;
|
||||
if (validateNull(cols)) return;
|
||||
disabledFormField(f, permType)
|
||||
cols.forEach(col => {
|
||||
if (validateNull(col.rule)) return;
|
||||
disabledAllFormFields(col.rule, permType)
|
||||
})
|
||||
}
|
||||
// 处理表格布局元素、卡片元素
|
||||
else if (f.type.indexOf(PROP_CONST.FORM_DESIGN.fcTable) !== -1 || f.type.indexOf(PROP_CONST.FORM_DESIGN.elCard) !== -1) {
|
||||
let cols = f.children;
|
||||
if (validateNull(cols)) return;
|
||||
disabledAllFormFields(cols, permType)
|
||||
} else {
|
||||
disabledFormField(f, permType)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function disabledFormField(find, permType) {
|
||||
if (permType === '-1') {
|
||||
find.hidden = true
|
||||
} else if (permType === '1') {
|
||||
if (!find.props) find.props = {}
|
||||
find.props.disabled = false
|
||||
} else if (permType === '0') {
|
||||
if (!find.props) find.props = {}
|
||||
find.props.disabled = true
|
||||
}
|
||||
}
|
||||
129
src/flow/utils/index.ts
Normal file
129
src/flow/utils/index.ts
Normal file
@@ -0,0 +1,129 @@
|
||||
import * as orderVue from "/@/api/order/order-key-vue";
|
||||
import {isMobile} from "/@/utils/other";
|
||||
import {dynamicRoutesFlow} from "/@/flow/support/extend";
|
||||
import {listFlowApplicationByFlowKey} from "/@/flow/designer/api/jsonflow";
|
||||
import {utils} from "/@/flow/designer/utils/common";
|
||||
import {setPropsNull} from "/@/flow/support/common";
|
||||
import {ElMessageBox} from "element-plus";
|
||||
import {i18n} from "/@/i18n";
|
||||
import {validateNull} from "/@/utils/validate";
|
||||
import {validateNodeType} from "/@/flow/designer/components";
|
||||
import {notifyLeft} from "/@/flow";
|
||||
|
||||
/**
|
||||
* 常用工具类
|
||||
*
|
||||
* @author luolin
|
||||
*/
|
||||
const { t } = i18n.global;
|
||||
|
||||
export const formWidgetDesignHeight = (browserHeight) => {
|
||||
let initClientHeight = document.getElementById("initClientHeight");
|
||||
let height = (browserHeight - initClientHeight.offsetTop) + "px";
|
||||
initClientHeight.style.height = height;
|
||||
setTimeout(() => {
|
||||
let sidePanel = document.getElementsByClassName("el-aside _fc-l")[0];
|
||||
if (sidePanel) sidePanel.style.height = height;
|
||||
// TODO 预览时不保存默认formData
|
||||
// let formWidgetMain = document.getElementsByClassName("el-container is-vertical _fc-m")[0];
|
||||
// if (formWidgetMain) formWidgetMain.style.height = height;
|
||||
}, 2000)
|
||||
}
|
||||
|
||||
// 可视窗口默认无高度
|
||||
export const initFlowDesignHeight = ($route, browserHeight) => {
|
||||
let initClientHeight = document.getElementById("initClientHeight");
|
||||
if (isMobile()) {
|
||||
// 判断是否在APP中访问
|
||||
if (initClientHeight.parentNode.id !== 'app') {
|
||||
browserHeight = browserHeight - 187
|
||||
if ($route.path !== dynamicRoutesFlow[0].path) browserHeight = browserHeight + 120
|
||||
}
|
||||
} else {
|
||||
if ($route.path === dynamicRoutesFlow[0].path) browserHeight = browserHeight - 120
|
||||
browserHeight = browserHeight - initClientHeight.offsetTop
|
||||
}
|
||||
initClientHeight.style.height = browserHeight + "px"
|
||||
}
|
||||
|
||||
// 隐藏弹出框滚动条
|
||||
export const initHandleJobHeight = (data) => {
|
||||
let handleJob = document.getElementById('handle_job');
|
||||
if (handleJob) {
|
||||
handleJob.style.overflowX = 'hidden'
|
||||
if (data.currElTab.path === orderVue.vueKey.FlowDesignView) {
|
||||
handleJob.style.overflowY = 'hidden'
|
||||
} else {
|
||||
handleJob.style.overflowY = 'auto'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 隐藏流程图弹出框
|
||||
export const hideVueContextmenuName = () => {
|
||||
let element = document.querySelector(".vue-contextmenuName-node-menu");
|
||||
if (element) element.style.display = "none";
|
||||
let element2 = document.querySelector(".vue-contextmenuName-link-menu");
|
||||
if (element2) element2.style.display = "none";
|
||||
let element3 = document.querySelector(".vue-contextmenuName-node-connect-menu");
|
||||
if (element3) element3.style.display = "none";
|
||||
let element4 = document.querySelector(".vue-contextmenuName-flow-menu");
|
||||
if (element4) element4.style.display = "none";
|
||||
}
|
||||
|
||||
// 确认窗体
|
||||
export const confirmCancelAndClose = (confirmObj, cancelObj, msg) => {
|
||||
// 无法await
|
||||
ElMessageBox.confirm(msg, t('message.box.title'), {
|
||||
distinguishCancelAndClose: true,
|
||||
confirmButtonText: confirmObj.text,
|
||||
cancelButtonText: cancelObj.text,
|
||||
type: 'warning',
|
||||
}).then(() => {
|
||||
if (confirmObj.callback) confirmObj.callback()
|
||||
}).catch((err) => {
|
||||
if (err === 'cancel') {
|
||||
if (cancelObj.callback) cancelObj.callback()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export const handleUpgradeVersion = async (props) => {
|
||||
if (!props.currFlowForm) return
|
||||
if (props.currFlowForm.isNew) return
|
||||
props.currFlowForm.isNew = true
|
||||
// TODO 只有模板才能升版本
|
||||
let oldFormId = props.currFlowForm.id;
|
||||
setPropsNull(props.currFlowForm, 'id', 'createUser', 'createTime')
|
||||
let newFormId = utils.getId()
|
||||
props.currFlowForm.id = newFormId
|
||||
let resp = await listFlowApplicationByFlowKey(props.currFlowForm.flowKey);
|
||||
if (validateNull(resp.data)) return
|
||||
props.currFlowForm.version = resp.data[0].version + 1
|
||||
replaceFlowNodeFormId(newFormId, oldFormId)
|
||||
}
|
||||
|
||||
// 替换节点旧表单ID
|
||||
export function replaceFlowNodeFormId(newFormId, oldFormId, isNotified?) {
|
||||
let models = window._jfGraph.getElements();
|
||||
if (validateNull(models)) return
|
||||
let isExistReplace = false
|
||||
models.forEach(model => {
|
||||
if (!validateNodeType(model, null, true)) return
|
||||
let pcTodoUrl = model.attributes.attrs.cdata.attrs.pcTodoUrl
|
||||
if (validateNull(pcTodoUrl)) return;
|
||||
const index = pcTodoUrl.findIndex(item => item === oldFormId);
|
||||
if (index !== -1) {
|
||||
model.attributes.attrs.cdata.attrs.pcTodoUrl.splice(index, 1, newFormId);
|
||||
let formId = model.attributes.attrs.cdata.attrs.formId
|
||||
if (formId === oldFormId) model.attributes.attrs.cdata.attrs.formId = newFormId
|
||||
isExistReplace = true
|
||||
}
|
||||
})
|
||||
if (!isNotified) return;
|
||||
if (isExistReplace) {
|
||||
notifyLeft('替换成功')
|
||||
} else {
|
||||
notifyLeft('节点中不存在该旧的页面', 'warning')
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user