17 changed files with 2993 additions and 13 deletions
@ -0,0 +1,23 @@ |
|||
import request from '@/utils/request'; |
|||
import { AxiosPromise } from 'axios'; |
|||
|
|||
import { |
|||
formTableName |
|||
} from '@/api/workflowapi/types' |
|||
|
|||
//初始化工作流
|
|||
export function initializeWorkFlow(data: formTableName) { |
|||
return request({ |
|||
url: '/systemapi/task_flow/init_workflow', |
|||
method: 'post', |
|||
data: data |
|||
}); |
|||
} |
|||
//保存工作流
|
|||
export function setWorkFlowData(data: any):any { |
|||
return request({ |
|||
url: '/systemapi/task_flow/init_workflow', |
|||
method: 'post', |
|||
data: data |
|||
}); |
|||
} |
|||
@ -0,0 +1,9 @@ |
|||
//表单名称
|
|||
export interface formTableName{ |
|||
name?: string; |
|||
} |
|||
|
|||
//错误提示
|
|||
export interface tipListStrucr{ |
|||
|
|||
} |
|||
@ -0,0 +1,249 @@ |
|||
<!-- |
|||
@ 作者: 秦东 |
|||
@ 时间: 2023-10-10 14:23:45 |
|||
@ 备注: 添加节点弹窗 |
|||
--> |
|||
<script lang='ts' setup> |
|||
let props = defineProps({ |
|||
childNodeP: { |
|||
type: Object, |
|||
default: ()=> ({}) |
|||
} |
|||
}) |
|||
let emits = defineEmits(['update:childNodeP']) |
|||
let visible = ref(false) |
|||
//添加节点 |
|||
const addType = (type)=> { |
|||
visible.value = false; |
|||
if (type != 4) { |
|||
var data; |
|||
if (type == 1) { |
|||
data = { |
|||
"nodeName": "审核人", |
|||
"error": true, |
|||
"type": 1, |
|||
"settype": 1, |
|||
"selectMode": 0, |
|||
"selectRange": 0, |
|||
"directorLevel": 1, |
|||
"examineMode": 1, |
|||
"noHanderAction": 1, |
|||
"examineEndDirectorLevel": 0, |
|||
"childNode": props.childNodeP, |
|||
"nodeUserList": [] |
|||
} |
|||
} else if (type == 2) { |
|||
data = { |
|||
"nodeName": "抄送人", |
|||
"type": 2, |
|||
"ccSelfSelectFlag": 1, |
|||
"childNode": props.childNodeP, |
|||
"nodeUserList": [] |
|||
} |
|||
}else if (type == 3) { |
|||
data = { |
|||
"nodeName": "审核人", |
|||
"error": true, |
|||
"type": 3, |
|||
"settype": 1, |
|||
"selectMode": 0, |
|||
"selectRange": 0, |
|||
"directorLevel": 1, |
|||
"examineMode": 1, |
|||
"noHanderAction": 1, |
|||
"examineEndDirectorLevel": 0, |
|||
"childNode": props.childNodeP, |
|||
"nodeUserList": [] |
|||
} |
|||
} |
|||
emits("update:childNodeP", data) |
|||
} else { |
|||
emits("update:childNodeP", { |
|||
"nodeName": "路由", |
|||
"type": 5, |
|||
"childNode": null, |
|||
"conditionNodes": [{ |
|||
"nodeName": "条件1", |
|||
"error": true, |
|||
"type": 3, |
|||
"priorityLevel": 1, |
|||
"conditionList": [], |
|||
"nodeUserList": [], |
|||
"childNode": props.childNodeP, |
|||
}, { |
|||
"nodeName": "条件2", |
|||
"type": 3, |
|||
"priorityLevel": 2, |
|||
"conditionList": [], |
|||
"nodeUserList": [], |
|||
"childNode": null |
|||
}] |
|||
}) |
|||
} |
|||
} |
|||
</script> |
|||
<template> |
|||
<div class="add-node-btn-box"> |
|||
<div class="add-node-btn"> |
|||
<el-popover v-model="visible" placement="right-start" width="auto"> |
|||
<div class="add-node-popover-body"> |
|||
<a class="add-node-popover-item approver" @click="addType(1)"> |
|||
<div class="item-wrapper"> |
|||
<span class="iconfont"><el-icon><Stamp /></el-icon></span> |
|||
</div> |
|||
<p>审批人</p> |
|||
</a> |
|||
<a class="add-node-popover-item notifier" @click="addType(2)"> |
|||
<div class="item-wrapper"> |
|||
<span class="iconfont"><i class="fa fa-send"></i></span> |
|||
</div> |
|||
<p>抄送人</p> |
|||
</a> |
|||
<a class="add-node-popover-item approver" @click="addType(3)"> |
|||
<div class="item-wrapper"> |
|||
<span class="iconfont"><i class="fa fa-pencil-square-o"></i></span> |
|||
</div> |
|||
<p>处理</p> |
|||
</a> |
|||
<a class="add-node-popover-item condition" @click="addType(4)"> |
|||
<div class="item-wrapper"> |
|||
<span class="iconfont"><i class="fa fa-sitemap"></i></span> |
|||
</div> |
|||
<p>条件分支</p> |
|||
</a> |
|||
</div> |
|||
<template #reference> |
|||
<button class="btn" type="button"> |
|||
<span class="iconfont"></span> |
|||
</button> |
|||
</template> |
|||
</el-popover> |
|||
</div> |
|||
</div> |
|||
</template> |
|||
<style lang='scss' scoped> |
|||
|
|||
</style> |
|||
<style scoped lang="less"> |
|||
.add-node-btn-box { |
|||
width: 240px; |
|||
display: -webkit-inline-box; |
|||
display: -ms-inline-flexbox; |
|||
display: inline-flex; |
|||
-ms-flex-negative: 0; |
|||
flex-shrink: 0; |
|||
-webkit-box-flex: 1; |
|||
-ms-flex-positive: 1; |
|||
position: relative; |
|||
&:before { |
|||
content: ""; |
|||
position: absolute; |
|||
top: 0; |
|||
left: 0; |
|||
right: 0; |
|||
bottom: 0; |
|||
z-index: -1; |
|||
margin: auto; |
|||
width: 2px; |
|||
height: 100%; |
|||
background-color: #cacaca |
|||
} |
|||
.add-node-btn { |
|||
user-select: none; |
|||
width: 240px; |
|||
padding: 20px 0 32px; |
|||
display: flex; |
|||
-webkit-box-pack: center; |
|||
justify-content: center; |
|||
flex-shrink: 0; |
|||
-webkit-box-flex: 1; |
|||
flex-grow: 1; |
|||
.btn { |
|||
outline: none; |
|||
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, .1); |
|||
width: 30px; |
|||
height: 30px; |
|||
background: #3296fa; |
|||
border-radius: 50%; |
|||
position: relative; |
|||
border: none; |
|||
line-height: 30px; |
|||
-webkit-transition: all .3s cubic-bezier(.645, .045, .355, 1); |
|||
transition: all .3s cubic-bezier(.645, .045, .355, 1); |
|||
.iconfont { |
|||
color: #fff; |
|||
font-size: 16px |
|||
} |
|||
&:hover { |
|||
transform: scale(1.3); |
|||
box-shadow: 0 13px 27px 0 rgba(0, 0, 0, .1) |
|||
} |
|||
&:active { |
|||
transform: none; |
|||
background: #1e83e9; |
|||
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, .1) |
|||
} |
|||
} |
|||
} |
|||
} |
|||
</style> |
|||
<style lang="less"> |
|||
.add-node-popover-body { |
|||
display: flex; |
|||
.add-node-popover-item { |
|||
margin-right: 10px; |
|||
cursor: pointer; |
|||
text-align: center; |
|||
flex: 1; |
|||
color: #191f25!important; |
|||
.item-wrapper { |
|||
user-select: none; |
|||
display: inline-block; |
|||
width: 80px; |
|||
height: 80px; |
|||
margin-bottom: 5px; |
|||
background: #fff; |
|||
border: 1px solid #e2e2e2; |
|||
border-radius: 50%; |
|||
transition: all .3s cubic-bezier(.645, .045, .355, 1); |
|||
.iconfont { |
|||
font-size: 35px; |
|||
line-height: 80px |
|||
} |
|||
} |
|||
&.approver{ |
|||
.item-wrapper { |
|||
color: #ff943e |
|||
} |
|||
} |
|||
&.notifier{ |
|||
.item-wrapper { |
|||
color: #3296fa |
|||
} |
|||
} |
|||
&.condition{ |
|||
.item-wrapper { |
|||
color: #15bc83 |
|||
} |
|||
} |
|||
&:hover{ |
|||
.item-wrapper { |
|||
background: #3296fa; |
|||
box-shadow: 0 10px 20px 0 rgba(50, 150, 250, .4) |
|||
} |
|||
.iconfont { |
|||
color: #fff |
|||
} |
|||
} |
|||
&:active{ |
|||
.item-wrapper { |
|||
box-shadow: none; |
|||
background: #eaeaea |
|||
} |
|||
.iconfont { |
|||
color: inherit |
|||
} |
|||
} |
|||
} |
|||
} |
|||
</style> |
|||
@ -0,0 +1,299 @@ |
|||
<!-- |
|||
@ 作者: 秦东 |
|||
@ 时间: 2023-10-10 11:30:42 |
|||
@ 备注: 工作流引擎主干 |
|||
--> |
|||
<script lang='ts' setup> |
|||
import $func from '@/utils/workflow/index' |
|||
import { useStore } from '@/store/workflow/index' |
|||
import { bgColors, placeholderList } from '@/utils/workflow/const' |
|||
|
|||
let _uid = getCurrentInstance().uid; |
|||
|
|||
let props = defineProps({ |
|||
nodeConfig: { |
|||
type: Object, |
|||
default: () => ({}), |
|||
}, |
|||
flowPermission: { |
|||
type: Object, |
|||
// eslint-disable-next-line vue/require-valid-default-prop |
|||
default: () => [], |
|||
}, |
|||
}); |
|||
//pinia 相关数据操作 |
|||
let store = useStore(); |
|||
let { |
|||
setPromoter, |
|||
setApprover, |
|||
setCopyer, |
|||
setCondition, |
|||
setFlowPermission, |
|||
setApproverConfig, |
|||
setCopyerConfig, |
|||
setConditionsConfig, |
|||
} = store; |
|||
let isTried = computed(()=> store.isTried) |
|||
let flowPermission1 = computed(()=> store.flowPermission1) |
|||
let approverConfig1 = computed(()=> store.approverConfig1) |
|||
let copyerConfig1 = computed(()=> store.copyerConfig1) |
|||
let conditionsConfig1 = computed(()=> store.conditionsConfig1) |
|||
|
|||
let defaultText = computed(() => { |
|||
return placeholderList[props.nodeConfig.type] |
|||
}); |
|||
let showText = computed(() => { |
|||
if (props.nodeConfig.type == 0) return $func.arrToStr(props.flowPermission) || '所有人' |
|||
if (props.nodeConfig.type == 1) return $func.setApproverStr(props.nodeConfig) |
|||
return $func.copyerStr(props.nodeConfig) |
|||
}); |
|||
let isInputList = ref([]); |
|||
let isInput = ref(false); |
|||
const resetConditionNodesErr = () => { |
|||
for (var i = 0; i < props.nodeConfig.conditionNodes.length; i++) { |
|||
// eslint-disable-next-line vue/no-mutating-props |
|||
props.nodeConfig.conditionNodes[i].error = $func.conditionStr(props.nodeConfig, i) == "请设置条件" && i != props.nodeConfig.conditionNodes.length - 1; |
|||
} |
|||
} |
|||
|
|||
const clickEvent = (index) => { |
|||
if (index || index === 0) { |
|||
isInputList.value[index] = true; |
|||
} else { |
|||
isInput.value = true; |
|||
} |
|||
}; |
|||
const blurEvent = (index) => { |
|||
if (index || index === 0) { |
|||
isInputList.value[index] = false; |
|||
// eslint-disable-next-line vue/no-mutating-props |
|||
props.nodeConfig.conditionNodes[index].nodeName = props.nodeConfig.conditionNodes[index].nodeName || "条件"; |
|||
} else { |
|||
isInput.value = false; |
|||
// eslint-disable-next-line vue/no-mutating-props |
|||
props.nodeConfig.nodeName = props.nodeConfig.nodeName || defaultText |
|||
} |
|||
}; |
|||
//删除节点 |
|||
const delNode = () => { |
|||
emits("update:nodeConfig", props.nodeConfig.childNode); |
|||
}; |
|||
let emits = defineEmits(["update:flowPermission", "update:nodeConfig"]); |
|||
const addTerm = () => { |
|||
let len = props.nodeConfig.conditionNodes.length + 1; |
|||
// eslint-disable-next-line vue/no-mutating-props |
|||
props.nodeConfig.conditionNodes.push({ |
|||
nodeName: "条件" + len, |
|||
type: 3, |
|||
priorityLevel: len, |
|||
conditionList: [], |
|||
nodeUserList: [], |
|||
childNode: null, |
|||
}); |
|||
resetConditionNodesErr() |
|||
emits("update:nodeConfig", props.nodeConfig); |
|||
}; |
|||
const delTerm = (index) => { |
|||
// eslint-disable-next-line vue/no-mutating-props |
|||
props.nodeConfig.conditionNodes.splice(index, 1); |
|||
props.nodeConfig.conditionNodes.map((item, index) => { |
|||
item.priorityLevel = index + 1; |
|||
item.nodeName = `条件${index + 1}`; |
|||
}); |
|||
resetConditionNodesErr() |
|||
emits("update:nodeConfig", props.nodeConfig); |
|||
if (props.nodeConfig.conditionNodes.length == 1) { |
|||
if (props.nodeConfig.childNode) { |
|||
if (props.nodeConfig.conditionNodes[0].childNode) { |
|||
reData(props.nodeConfig.conditionNodes[0].childNode, props.nodeConfig.childNode); |
|||
} else { |
|||
// eslint-disable-next-line vue/no-mutating-props |
|||
props.nodeConfig.conditionNodes[0].childNode = props.nodeConfig.childNode; |
|||
} |
|||
} |
|||
emits("update:nodeConfig", props.nodeConfig.conditionNodes[0].childNode); |
|||
} |
|||
}; |
|||
const reData = (data, addData) => { |
|||
if (!data.childNode) { |
|||
data.childNode = addData; |
|||
} else { |
|||
reData(data.childNode, addData); |
|||
} |
|||
}; |
|||
const setPerson = (priorityLevel) => { |
|||
var { type } = props.nodeConfig; |
|||
if (type == 0) { |
|||
setPromoter(true); |
|||
setFlowPermission({ |
|||
value: props.flowPermission, |
|||
flag: false, |
|||
id: _uid, |
|||
}); |
|||
} else if (type == 1) { |
|||
setApprover(true); |
|||
setApproverConfig({ |
|||
value: { |
|||
...JSON.parse(JSON.stringify(props.nodeConfig)), |
|||
...{ settype: props.nodeConfig.settype ? props.nodeConfig.settype : 1 }, |
|||
}, |
|||
flag: false, |
|||
id: _uid, |
|||
}); |
|||
} else if (type == 2) { |
|||
setCopyer(true); |
|||
setCopyerConfig({ |
|||
value: JSON.parse(JSON.stringify(props.nodeConfig)), |
|||
flag: false, |
|||
id: _uid, |
|||
}); |
|||
} else { |
|||
setCondition(true); |
|||
setConditionsConfig({ |
|||
value: JSON.parse(JSON.stringify(props.nodeConfig)), |
|||
priorityLevel, |
|||
flag: false, |
|||
id: _uid, |
|||
}); |
|||
} |
|||
}; |
|||
const arrTransfer = (index, type = 1) => { |
|||
//向左-1,向右1 |
|||
// eslint-disable-next-line vue/no-mutating-props |
|||
props.nodeConfig.conditionNodes[index] = props.nodeConfig.conditionNodes.splice( |
|||
index + type, |
|||
1, |
|||
props.nodeConfig.conditionNodes[index] |
|||
)[0]; |
|||
props.nodeConfig.conditionNodes.map((item, index) => { |
|||
item.priorityLevel = index + 1; |
|||
}); |
|||
resetConditionNodesErr() |
|||
emits("update:nodeConfig", props.nodeConfig); |
|||
}; |
|||
//监听组块 |
|||
watch(flowPermission1, (flow) => { |
|||
if (flow.flag && flow.id === _uid) { |
|||
emits("update:flowPermission", flow.value); |
|||
} |
|||
}); |
|||
watch(approverConfig1, (approver) => { |
|||
if (approver.flag && approver.id === _uid) { |
|||
emits("update:nodeConfig", approver.value); |
|||
} |
|||
}); |
|||
watch(copyerConfig1, (copyer) => { |
|||
if (copyer.flag && copyer.id === _uid) { |
|||
emits("update:nodeConfig", copyer.value); |
|||
} |
|||
}); |
|||
watch(conditionsConfig1, (condition) => { |
|||
if (condition.flag && condition.id === _uid) { |
|||
emits("update:nodeConfig", condition.value); |
|||
} |
|||
}); |
|||
|
|||
|
|||
onMounted(() => { |
|||
if (props.nodeConfig.type == 1) { |
|||
// eslint-disable-next-line vue/no-mutating-props |
|||
props.nodeConfig.error = !$func.setApproverStr(props.nodeConfig); |
|||
} else if (props.nodeConfig.type == 2) { |
|||
// eslint-disable-next-line vue/no-mutating-props |
|||
props.nodeConfig.error = !$func.copyerStr(props.nodeConfig); |
|||
} else if (props.nodeConfig.type == 4) { |
|||
resetConditionNodesErr() |
|||
} |
|||
}) |
|||
</script> |
|||
<template> |
|||
<!--通用模块(0 发起人 1审批 2抄送 3处理 4条件 5路由)--> |
|||
<div v-if="nodeConfig.type < 4" class="node-wrap" > |
|||
<div class="node-wrap-box" :class="(nodeConfig.type == 0 ? 'start-node ' : '') +(isTried && nodeConfig.error ? 'active error' : '')"> |
|||
<!--标签头部--> |
|||
<div class="title" :style="`background: rgb(${bgColors[nodeConfig.type]});`"> |
|||
<span v-if="nodeConfig.type == 0">{{ nodeConfig.nodeName }}</span> |
|||
<template v-else> |
|||
<span class="iconfont">{{nodeConfig.type == 1?'':''}}</span> |
|||
<input |
|||
v-if="isInput" |
|||
v-model="nodeConfig.nodeName" |
|||
type="text" |
|||
class="ant-input editable-title-input" |
|||
@blur="blurEvent()" |
|||
@focus="$event.currentTarget.select()" |
|||
v-focus |
|||
:placeholder="defaultText" |
|||
/> |
|||
<span v-else class="editable-title" @click="clickEvent()">{{ nodeConfig.nodeName }}</span> |
|||
<i class="anticon anticon-close close" @click="delNode"></i> |
|||
</template> |
|||
</div> |
|||
<!--标签主体--> |
|||
<div class="content" @click="setPerson"> |
|||
<div class="text"> |
|||
<span class="placeholder" v-if="!showText">请选择{{defaultText}}</span> |
|||
{{showText}} |
|||
</div> |
|||
<i class="anticon anticon-right arrow"></i> |
|||
</div> |
|||
<!--标签错误提示--> |
|||
<div class="error_tip" v-if="isTried && nodeConfig.error"> |
|||
<i class="anticon anticon-exclamation-circle"></i> |
|||
</div> |
|||
</div> |
|||
<addNode v-model:childNodeP="nodeConfig.childNode" /> |
|||
</div> |
|||
<!--路由模块--> |
|||
<div v-if="nodeConfig.type == 5" class="node-wrap" > |
|||
<div class="branch-box-wrap"> |
|||
<div class="branch-box"> |
|||
<button class="add-branch" @click="addTerm">添加条件</button> |
|||
<div class="col-box" v-for="(item, index) in nodeConfig.conditionNodes" :key="index"> |
|||
<div class="condition-node"> |
|||
<div class="condition-node-box"> |
|||
<div class="auto-judge" :class="isTried && item.error ? 'error active' : ''"> |
|||
<div class="sort-left" v-if="index != 0" @click="arrTransfer(index, -1)"><</div> |
|||
<div class="title-wrapper"> |
|||
<input |
|||
v-if="isInputList[index]" |
|||
type="text" |
|||
class="ant-input editable-title-input" |
|||
@blur="blurEvent(index)" |
|||
@focus="$event.currentTarget.select()" |
|||
v-focus |
|||
v-model="item.nodeName" |
|||
/> |
|||
<span v-else class="editable-title" @click="clickEvent(index)">{{ item.nodeName }}</span> |
|||
<span class="priority-title" @click="setPerson(item.priorityLevel)">优先级{{ item.priorityLevel }}</span> |
|||
<i class="anticon anticon-close close" @click="delTerm(index)"></i> |
|||
</div> |
|||
<div class="sort-right" v-if="index != nodeConfig.conditionNodes.length - 1" @click="arrTransfer(index)">></div> |
|||
<div class="content" @click="setPerson(item.priorityLevel)">{{ $func.conditionStr(nodeConfig, index) }}</div> |
|||
<div class="error_tip" v-if="isTried && item.error"> |
|||
<i class="anticon anticon-exclamation-circle"></i> |
|||
</div> |
|||
</div> |
|||
<addNode v-model:childNodeP="item.childNode" /> |
|||
</div> |
|||
</div> |
|||
<nodeWrap v-if="item.childNode" v-model:nodeConfig="item.childNode" /> |
|||
<template v-if="index == 0"> |
|||
<div class="top-left-cover-line"></div> |
|||
<div class="bottom-left-cover-line"></div> |
|||
</template> |
|||
<template v-if="index == nodeConfig.conditionNodes.length - 1"> |
|||
<div class="top-right-cover-line"></div> |
|||
<div class="bottom-right-cover-line"></div> |
|||
</template> |
|||
</div> |
|||
</div> |
|||
<addNode v-model:childNodeP="nodeConfig.childNode" /> |
|||
</div> |
|||
</div> |
|||
<!--循环组件--> |
|||
<nodeWrap v-if="nodeConfig.childNode" v-model:nodeConfig="nodeConfig.childNode"/> |
|||
</template> |
|||
<style lang='scss' scoped> |
|||
|
|||
</style> |
|||
@ -0,0 +1,52 @@ |
|||
/** |
|||
* 缓存工作流节点内容 |
|||
*/ |
|||
import { defineStore } from 'pinia'; |
|||
export const useStore = defineStore('store', { |
|||
state: () => ({ |
|||
tableId: '', |
|||
isTried: false, |
|||
promoterDrawer: false, |
|||
flowPermission1: {}, |
|||
approverDrawer: false, |
|||
approverConfig1: {}, |
|||
copyerDrawer: false, |
|||
copyerConfig1: {}, |
|||
conditionDrawer: false, |
|||
conditionsConfig1: { |
|||
conditionNodes: [], |
|||
}, |
|||
}), |
|||
actions: { |
|||
setTableId(payload:any) { |
|||
this.tableId = payload |
|||
}, |
|||
setIsTried(payload:any) { |
|||
this.isTried = payload |
|||
}, |
|||
setPromoter(payload:any) { |
|||
this.promoterDrawer = payload |
|||
}, |
|||
setFlowPermission(payload:any) { |
|||
this.flowPermission1 = payload |
|||
}, |
|||
setApprover(payload:any) { |
|||
this.approverDrawer = payload |
|||
}, |
|||
setApproverConfig(payload:any) { |
|||
this.approverConfig1 = payload |
|||
}, |
|||
setCopyer(payload:any) { |
|||
this.copyerDrawer = payload |
|||
}, |
|||
setCopyerConfig(payload:any) { |
|||
this.copyerConfig1 = payload |
|||
}, |
|||
setCondition(payload:any) { |
|||
this.conditionDrawer = payload |
|||
}, |
|||
setConditionsConfig(payload:any) { |
|||
this.conditionsConfig1 = payload |
|||
}, |
|||
} |
|||
}) |
|||
@ -0,0 +1,114 @@ |
|||
.el-drawer__header { |
|||
margin-bottom: 0 !important; |
|||
padding: 14px 5px 14px 5px !important; |
|||
/* border-bottom: 1px solid #f2f2f2 !important; */ |
|||
color: #323232 !important; |
|||
font-size: 16px !important; |
|||
} |
|||
.el-drawer__header .el-drawer__title{ |
|||
font-size: 16px !important; |
|||
} |
|||
.demo-drawer__content { |
|||
display: flex !important; |
|||
flex-direction: column !important; |
|||
height: 100% !important; |
|||
} |
|||
|
|||
.drawer_content { |
|||
flex: 1 !important; |
|||
} |
|||
|
|||
.demo-drawer__content>div { |
|||
border-top: 1px solid #F2F2F2 !important; |
|||
} |
|||
|
|||
/* .el-button { |
|||
min-width: 79px !important; |
|||
padding: 8px 12px !important; |
|||
font-size: 12px !important; |
|||
border-radius: 2px !important; |
|||
color: #323232 !important; |
|||
background: #f2f2f2 !important; |
|||
height: 30px !important; |
|||
} |
|||
|
|||
.el-button.el-button--primary { |
|||
background: #46A6FE !important; |
|||
color: #fff !important; |
|||
} */ |
|||
|
|||
.demo-drawer__footer { |
|||
padding: 10px 30px !important; |
|||
border-top: 1px solid #F2F2F2 !important; |
|||
} |
|||
|
|||
.demo-drawer__footer .el-button { |
|||
float: right !important; |
|||
margin-right: 10px !important; |
|||
} |
|||
|
|||
/* .el-dialog { |
|||
width: 520px; |
|||
border: 1px solid #DDE1E5 !important; |
|||
border-radius: 3px !important; |
|||
} |
|||
|
|||
.el-dialog__header { |
|||
padding: 0 0 0 20px !important; |
|||
line-height: 50px !important; |
|||
height: 50px !important; |
|||
background: #fff !important; |
|||
border-bottom: 1px solid #F2F2F2 !important; |
|||
} |
|||
|
|||
.el-dialog__header .el-dialog__title { |
|||
font-size: 16px !important; |
|||
line-height: 50px !important; |
|||
color: #333333 !important; |
|||
} |
|||
|
|||
.el-dialog__header .el-dialog__headerbtn { |
|||
height: 12px !important; |
|||
width: 12px !important; |
|||
} |
|||
|
|||
.el-dialog__header .el-icon-close { |
|||
width: 12px !important; |
|||
height: 12px !important; |
|||
float: left !important; |
|||
} */ |
|||
|
|||
/* .el-dialog__header .el-icon-close::before { |
|||
display: block !important; |
|||
width: 12px !important; |
|||
height: 12px !important; |
|||
background: url(~@/assets/images/add-close.png) no-repeat center !important; |
|||
background-size: 100% 100% !important; |
|||
content: "" !important; |
|||
} */ |
|||
.el-drawer__body { |
|||
padding: 0 !important; |
|||
} |
|||
/* .el-dialog__footer { |
|||
border-top: 1px solid #F2F2F2 !important; |
|||
padding-bottom: 10px !important; |
|||
} */ |
|||
|
|||
/* .el-checkbox, |
|||
.el-checkbox__input.is-checked+.el-checkbox__label, |
|||
.el-radio, |
|||
.el-radio__input.is-checked+.el-radio__label, |
|||
.el-dialog__body, |
|||
.el-tree { |
|||
color: #333 !important; |
|||
} |
|||
|
|||
.el-radio__label, .el-checkbox__label { |
|||
font-size: 12px !important; |
|||
} |
|||
.my-el-custom-spinner { |
|||
display: inline-block !important; |
|||
width: 80px !important; |
|||
height: 80px !important; |
|||
background: url(~@/assets/images/loading.gif) no-repeat center !important; |
|||
} */ |
|||
File diff suppressed because it is too large
@ -0,0 +1,38 @@ |
|||
/** |
|||
* 公共量 |
|||
*/ |
|||
export let bgColors = ['87, 106, 149', '255, 148, 62', '50, 150, 250','255,102,0','255,255,255'] |
|||
export let placeholderList = ["发起人", "审核人", "抄送人", "执行人"]; |
|||
|
|||
export let setTypes = [ |
|||
{value: 1, label: '指定成员'}, |
|||
{value: 2, label: '主管'}, |
|||
{value: 4, label: '发起人自选'}, |
|||
{value: 5, label: '发起人自己'}, |
|||
{value: 7, label: '连续多级主管'}, |
|||
] |
|||
|
|||
export let selectModes = [ |
|||
{value: 1, label: '选一个人'}, |
|||
{value: 2, label: '选多个人'}, |
|||
] |
|||
|
|||
export let selectRanges = [ |
|||
{value: 1, label: '全公司'}, |
|||
{value: 2, label: '指定成员'}, |
|||
{value: 3, label: '指定角色'}, |
|||
] |
|||
|
|||
export let optTypes = [ |
|||
{value: '1', label: '小于'}, |
|||
{value: '2', label: '大于'}, |
|||
{value: '3', label: '小于等于'}, |
|||
{value: '4', label: '等于'}, |
|||
{value: '5', label: '大于等于'}, |
|||
{value: '6', label: '介于两个数之间'}, |
|||
] |
|||
|
|||
export let opt1s = [ |
|||
{value: '<', label: '<'}, |
|||
{value: '≤', label: '≤'}, |
|||
] |
|||
@ -0,0 +1,157 @@ |
|||
function All() {} |
|||
All.prototype = { |
|||
timer: "", |
|||
debounce(fn:any, delay = 500) { |
|||
var _this = this; |
|||
return (arg: any) => { |
|||
//获取函数的作用域和变量
|
|||
let that = this; |
|||
let args = arg; |
|||
clearTimeout(_this.timer) // 清除定时器
|
|||
_this.timer = setTimeout(function() { |
|||
fn.call(that, args) |
|||
}, delay) |
|||
} |
|||
}, |
|||
setCookie(val:any) { //cookie设置[{key:value}]、获取key、清除['key1','key2']
|
|||
for (var i = 0, len = val.length; i < len; i++) { |
|||
for (var key in val[i]) { |
|||
document.cookie = key + '=' + encodeURIComponent(val[i][key]) + "; path=/"; |
|||
} |
|||
} |
|||
}, |
|||
getCookie(name:any) { |
|||
var strCookie = document.cookie; |
|||
var arrCookie = strCookie.split("; "); |
|||
for (var i = 0, len = arrCookie.length; i < len; i++) { |
|||
var arr = arrCookie[i].split("="); |
|||
if (name == arr[0]) { |
|||
return decodeURIComponent(arr[1]); |
|||
} |
|||
} |
|||
}, |
|||
clearCookie(name:any) { |
|||
var myDate = new Date(); |
|||
myDate.setTime(-1000); //设置时间
|
|||
for (var i = 0, len = name.length; i < len; i++) { |
|||
document.cookie = "" + name[i] + "=''; path=/; expires=" + myDate.toUTCString(); |
|||
} |
|||
}, |
|||
arrToStr(arr:any) { |
|||
if (arr) { |
|||
return arr.map((item: { name: any; }) => { return item.name }).toString() |
|||
} |
|||
}, |
|||
toggleClass(arr:any, elem:any, key = 'id') { |
|||
return arr.some((item: { [x: string]: any; }) => { return item[key] == elem[key] }); |
|||
}, |
|||
toChecked(arr:any, elem:any, key = 'id') { |
|||
var isIncludes = this.toggleClass(arr, elem, key); |
|||
!isIncludes ? arr.push(elem) : this.removeEle(arr, elem, key); |
|||
}, |
|||
removeEle(arr:any, elem:any, key = 'id') { |
|||
var includesIndex; |
|||
arr.map((item:any, index:any) => { |
|||
if (item[key] == elem[key]) { |
|||
includesIndex = index |
|||
} |
|||
}); |
|||
arr.splice(includesIndex, 1); |
|||
}, |
|||
setApproverStr(nodeConfig:any) { |
|||
if (nodeConfig.settype == 1) { |
|||
if (nodeConfig.nodeUserList.length == 1) { |
|||
return nodeConfig.nodeUserList[0].name |
|||
} else if (nodeConfig.nodeUserList.length > 1) { |
|||
if (nodeConfig.examineMode == 1) { |
|||
return this.arrToStr(nodeConfig.nodeUserList) |
|||
} else if (nodeConfig.examineMode == 2) { |
|||
return nodeConfig.nodeUserList.length + "人会签" |
|||
} |
|||
} |
|||
} else if (nodeConfig.settype == 2) { |
|||
let level = nodeConfig.directorLevel == 1 ? '直接主管' : '第' + nodeConfig.directorLevel + '级主管' |
|||
if (nodeConfig.examineMode == 1) { |
|||
return level |
|||
} else if (nodeConfig.examineMode == 2) { |
|||
return level + "会签" |
|||
} |
|||
} else if (nodeConfig.settype == 4) { |
|||
if (nodeConfig.selectRange == 1) { |
|||
return "发起人自选" |
|||
} else { |
|||
if (nodeConfig.nodeUserList.length > 0) { |
|||
if (nodeConfig.selectRange == 2) { |
|||
return "发起人自选" |
|||
} else { |
|||
return '发起人从' + nodeConfig.nodeUserList[0].name + '中自选' |
|||
} |
|||
} else { |
|||
return ""; |
|||
} |
|||
} |
|||
} else if (nodeConfig.settype == 5) { |
|||
return "发起人自己" |
|||
} else if (nodeConfig.settype == 7) { |
|||
return '从直接主管到通讯录中级别最高的第' + nodeConfig.examineEndDirectorLevel + '个层级主管' |
|||
} |
|||
}, |
|||
dealStr(str:any, obj:any) { |
|||
let arr: any[] = []; |
|||
let list = str.split(","); |
|||
for (var elem in obj) { |
|||
list.map((item: string) => { |
|||
if (item == elem) { |
|||
arr.push(obj[elem].value) |
|||
} |
|||
}) |
|||
} |
|||
return arr.join("或") |
|||
}, |
|||
conditionStr(nodeConfig:any, index:any) { |
|||
var { conditionList, nodeUserList } = nodeConfig.conditionNodes[index]; |
|||
if (conditionList.length == 0) { |
|||
return (index == nodeConfig.conditionNodes.length - 1) && nodeConfig.conditionNodes[0].conditionList.length != 0 ? '其他条件进入此流程' : '请设置条件' |
|||
} else { |
|||
let str = "" |
|||
for (var i = 0; i < conditionList.length; i++) { |
|||
var { columnId, columnType, showType, showName, optType, zdy1, opt1, zdy2, opt2, fixedDownBoxValue } = conditionList[i]; |
|||
if (columnId == 0) { |
|||
if (nodeUserList.length != 0) { |
|||
str += '发起人属于:' |
|||
str += nodeUserList.map((item: { name: any; }) => { return item.name }).join("或") + " 并且 " |
|||
} |
|||
} |
|||
if (columnType == "String" && showType == "3") { |
|||
if (zdy1) { |
|||
str += showName + '属于:' + this.dealStr(zdy1, JSON.parse(fixedDownBoxValue)) + " 并且 " |
|||
} |
|||
} |
|||
if (columnType == "Double") { |
|||
if (optType != 6 && zdy1) { |
|||
var optTypeStr = ["", "<", ">", "≤", "=", "≥"][optType] |
|||
str += `${showName} ${optTypeStr} ${zdy1} 并且 ` |
|||
} else if (optType == 6 && zdy1 && zdy2) { |
|||
str += `${zdy1} ${opt1} ${showName} ${opt2} ${zdy2} 并且 ` |
|||
} |
|||
} |
|||
} |
|||
return str ? str.substring(0, str.length - 4) : '请设置条件' |
|||
} |
|||
}, |
|||
copyerStr(nodeConfig:any) { |
|||
if (nodeConfig.nodeUserList.length != 0) { |
|||
return this.arrToStr(nodeConfig.nodeUserList) |
|||
} else { |
|||
if (nodeConfig.ccSelfSelectFlag == 1) { |
|||
return "发起人自选" |
|||
} |
|||
} |
|||
}, |
|||
toggleStrClass(item: { zdy1: string; }, key: any) { |
|||
let a = item.zdy1 ? item.zdy1.split(",") : [] |
|||
return a.some((item: any) => { return item == key }); |
|||
}, |
|||
} |
|||
|
|||
export default new (All as any)(); |
|||
@ -0,0 +1,129 @@ |
|||
<!-- |
|||
@ 作者: 秦东 |
|||
@ 时间: 2023-05-26 09:19:46 |
|||
@ 备注: |
|||
--> |
|||
<script lang='ts' setup> |
|||
import '@/styles/workflowcss/workflow.css' |
|||
|
|||
import { useStore } from '@/store/workflow/index' |
|||
import { tipListStrucr } from '@/api/workflowapi/types' |
|||
import { initializeWorkFlow,setWorkFlowData } from '@/api/workflowapi/index' |
|||
|
|||
let { setTableId, setIsTried } = useStore() |
|||
|
|||
let tipList = ref<any>([]); |
|||
let tipVisible = ref(false); |
|||
let nowVal = ref(100); //缩放比例 |
|||
let processConfig = ref<any>({}); |
|||
let nodeConfig = ref({}); |
|||
let workFlowDef = ref({}); |
|||
let flowPermission = ref([]); |
|||
let directorMaxLevel = ref(0); |
|||
|
|||
//首次加载页面 |
|||
onMounted(async ()=>{ |
|||
let { data } = await initializeWorkFlow({name:""}) |
|||
console.log("data",data) |
|||
let { |
|||
nodeConfig: nodes, |
|||
flowPermission: flows, |
|||
directorMaxLevel: directors, |
|||
workFlowDef: works, |
|||
tableId, |
|||
} = data; |
|||
nodeConfig.value = nodes; |
|||
flowPermission.value = flows; |
|||
directorMaxLevel.value = directors; |
|||
workFlowDef.value = works; |
|||
setTableId(tableId); |
|||
}) |
|||
//错误提示 |
|||
const reErr = ({ childNode }:any) => { |
|||
if (childNode) { |
|||
let { type, error, nodeName, conditionNodes } = childNode; |
|||
if (type == 1 || type == 2 || type == 3) { |
|||
if (error) { |
|||
tipList.value.push({ |
|||
name: nodeName, |
|||
type: ["", "审核人", "抄送人", "执行人"][type], |
|||
}); |
|||
} |
|||
reErr(childNode); |
|||
} else if (type == 4) { |
|||
reErr(childNode); |
|||
} else if (type == 5) { |
|||
reErr(childNode); |
|||
for (var i = 0; i < conditionNodes.length; i++) { |
|||
if (conditionNodes[i].error) { |
|||
tipList.value.push({ name: conditionNodes[i].nodeName, type: "条件" }); |
|||
} |
|||
reErr(conditionNodes[i]); |
|||
} |
|||
} |
|||
} else { |
|||
childNode = null; |
|||
} |
|||
}; |
|||
//保存 |
|||
const saveSet = async () => { |
|||
setIsTried(true); |
|||
tipList.value = []; |
|||
reErr(nodeConfig.value); |
|||
if (tipList.value.length != 0) { |
|||
tipVisible.value = true; |
|||
return; |
|||
} |
|||
processConfig.value.flowPermission = flowPermission.value; |
|||
// eslint-disable-next-line no-console |
|||
console.log(JSON.stringify(processConfig.value)); |
|||
let res = await setWorkFlowData(processConfig.value); |
|||
if (res.code == 200) { |
|||
ElMessage.success("设置成功") |
|||
setTimeout(function () { |
|||
window.location.href = ""; |
|||
}, 200); |
|||
} |
|||
}; |
|||
//缩放画布 |
|||
const zoomSize = (type:number) => { |
|||
if (type == 1) { |
|||
if (nowVal.value == 50) { |
|||
return; |
|||
} |
|||
nowVal.value -= 10; |
|||
} else { |
|||
if (nowVal.value == 300) { |
|||
return; |
|||
} |
|||
nowVal.value += 10; |
|||
} |
|||
}; |
|||
</script> |
|||
<template> |
|||
<div class="canvas_body"> |
|||
<div class="fd-nav-content-new"> |
|||
<!--画板--> |
|||
<section class="dingflow-design"> |
|||
<div class="zoom"> |
|||
<div class="zoom-out" :class="nowVal == 50 && 'disabled'" @click="zoomSize(1)"></div> |
|||
<span>{{ nowVal }}%</span> |
|||
<div class="zoom-in" :class="nowVal == 300 && 'disabled'" @click="zoomSize(2)"></div> |
|||
</div> |
|||
<div class="box-scale" :style="`transform: scale(${ nowVal / 100});`"> |
|||
<nodeWrap v-model:nodeConfig="nodeConfig" v-model:flowPermission="flowPermission" /> |
|||
<div class="end-node"> |
|||
<div class="end-node-circle"></div> |
|||
<div class="end-node-text">流程结束</div> |
|||
</div> |
|||
</div> |
|||
</section> |
|||
</div> |
|||
</div> |
|||
</template> |
|||
<style lang='scss' scoped> |
|||
.canvas_body{ |
|||
height: 100%; |
|||
width: inherit; |
|||
} |
|||
</style> |
|||
Loading…
Reference in new issue