Commit 07dc7258 by jason

整合仿钉钉流程设计器 https://github.com/StavinLi/Workflow-Vue3

parent d16194b7
/* stylelint-disable order/properties-order */
<template>
<div class="add-node-btn-box">
<div class="add-node-btn">
<el-popover placement="right-start" v-model="visible" 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"></span>
</div>
<p>审批人</p>
</a>
<a class="add-node-popover-item notifier" @click="addType(2)">
<div class="item-wrapper">
<span class="iconfont"></span>
</div>
<p>抄送人</p>
</a>
<a class="add-node-popover-item condition" @click="addType(4)">
<div class="item-wrapper">
<span class="iconfont"></span>
</div>
<p>条件分支</p>
</a>
</div>
<template #reference>
<button class="btn" type="button">
<span class="iconfont"></span>
</button>
</template>
</el-popover>
</div>
</div>
</template>
<script setup>
import { ref } from 'vue'
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: []
}
}
emits('update:childNodeP', data)
} else {
emits('update:childNodeP', {
nodeName: '路由',
type: 4,
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>
<style scoped lang="scss">
.add-node-btn-box {
width: 240px;
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, 0.1);
width: 30px;
height: 30px;
background: #3296fa;
border-radius: 50%;
position: relative;
border: none;
line-height: 30px;
-webkit-transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
.iconfont {
color: #fff;
font-size: 16px;
}
&:hover {
transform: scale(1.3);
box-shadow: 0 13px 27px 0 rgba(0, 0, 0, 0.1);
}
&:active {
transform: none;
background: #1e83e9;
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.1);
}
}
}
}
.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 0.3s cubic-bezier(0.645, 0.045, 0.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, 0.4);
}
.iconfont {
color: #fff;
}
}
&:active {
.item-wrapper {
box-shadow: none;
background: #eaeaea;
}
.iconfont {
color: inherit;
}
}
}
}
</style>
<template>
<el-drawer
:append-to-body="true"
title="审批人设置"
v-model="visible"
class="set_promoter"
:show-close="false"
:size="550"
:before-close="saveApprover"
>
<div class="demo-drawer__content">
<div class="drawer_content">
<div class="approver_content">
<el-radio-group v-model="approverConfig.settype" class="clear" @change="changeType">
<el-radio v-for="{ value, label } in setTypes" :key="value" :label="value">{{
label
}}</el-radio>
</el-radio-group>
<el-button type="primary" @click="addApprover" v-if="approverConfig.settype == 1"
>添加/修改成员</el-button
>
<p class="selected_list" v-if="approverConfig.settype == 1">
<span v-for="(item, index) in approverConfig.nodeUserList" :key="index"
>{{ item.name }}
<img
src="@/assets/images/add-close1.png"
@click="removeEle(approverConfig.nodeUserList, item, 'targetId')"
/>
</span>
<a
v-if="approverConfig.nodeUserList.length != 0"
@click="approverConfig.nodeUserList = []"
>清除</a
>
</p>
</div>
<div class="approver_manager" v-if="approverConfig.settype == 2">
<p>
<span>发起人的:</span>
<select v-model="approverConfig.directorLevel">
<option v-for="item in directorMaxLevel" :value="item" :key="item"
>{{ item == 1 ? '直接' : '第' + item + '级' }}主管</option
>
</select>
</p>
<p class="tip">找不到主管时,由上级主管代审批</p>
</div>
<div class="approver_self" v-if="approverConfig.settype == 5">
<p>该审批节点设置“发起人自己”后,审批人默认为发起人</p>
</div>
<div class="approver_self_select" v-show="approverConfig.settype == 4">
<el-radio-group v-model="approverConfig.selectMode" style="width: 100%">
<el-radio v-for="{ value, label } in selectModes" :label="value" :key="value">{{
label
}}</el-radio>
</el-radio-group>
<h3>选择范围</h3>
<el-radio-group
v-model="approverConfig.selectRange"
style="width: 100%"
@change="changeRange"
>
<el-radio v-for="{ value, label } in selectRanges" :label="value" :key="value">{{
label
}}</el-radio>
</el-radio-group>
<template v-if="approverConfig.selectRange == 2 || approverConfig.selectRange == 3">
<el-button type="primary" @click="addApprover" v-if="approverConfig.selectRange == 2"
>添加/修改成员</el-button
>
<el-button type="primary" @click="addRoleApprover" v-else>添加/修改角色</el-button>
<p class="selected_list">
<span v-for="(item, index) in approverConfig.nodeUserList" :key="index"
>{{ item.name }}
<img
src="@/assets/images/add-close1.png"
@click="removeEle(approverConfig.nodeUserList, item, 'targetId')"
/>
</span>
<a
v-if="approverConfig.nodeUserList.length != 0 && approverConfig.selectRange != 1"
@click="approverConfig.nodeUserList = []"
>清除</a
>
</p>
</template>
</div>
<div class="approver_manager" v-if="approverConfig.settype == 7">
<p>审批终点</p>
<p style="padding-bottom: 20px">
<span>发起人的:</span>
<select v-model="approverConfig.examineEndDirectorLevel">
<option v-for="item in directorMaxLevel" :value="item" :key="item"
>{{ item == 1 ? '最高' : '第' + item }}层级主管</option
>
</select>
</p>
</div>
<div
class="approver_some"
v-if="
(approverConfig.settype == 1 && approverConfig.nodeUserList.length > 1) ||
approverConfig.settype == 2 ||
(approverConfig.settype == 4 && approverConfig.selectMode == 2)
"
>
<p>多人审批时采用的审批方式</p>
<el-radio-group v-model="approverConfig.examineMode" class="clear">
<el-radio :label="1">依次审批</el-radio>
<br />
<el-radio :label="2" v-if="approverConfig.settype != 2"
>会签(须所有审批人同意)</el-radio
>
</el-radio-group>
</div>
<div
class="approver_some"
v-if="approverConfig.settype == 2 || approverConfig.settype == 7"
>
<p>审批人为空时</p>
<el-radio-group v-model="approverConfig.noHanderAction" class="clear">
<el-radio :label="1">自动审批通过/不允许发起</el-radio>
<br />
<el-radio :label="2">转交给审核管理员</el-radio>
</el-radio-group>
</div>
</div>
<div class="demo-drawer__footer clear">
<el-button type="primary" @click="saveApprover">确 定</el-button>
<el-button @click="closeDrawer">取 消</el-button>
</div>
</div>
</el-drawer>
</template>
<script lang="ts" setup>
import { ref, watch, computed } from 'vue'
import { useWorkFlowStoreWithOut } from '@/store/modules/simpleWorkflow'
import { setTypes, selectModes, selectRanges } from '../util'
import { removeEle, setApproverStr } from '../util'
let props = defineProps({
directorMaxLevel: {
type: Number,
default: 0
}
})
let approverConfig = ref({})
let approverVisible = ref(false)
let approverRoleVisible = ref(false)
let checkedRoleList = ref([])
let checkedList = ref([])
let store = useWorkFlowStoreWithOut()
let { setApproverConfig, setApprover } = store
let approverConfig1 = computed(() => store.approverConfig1)
let approverDrawer = computed(() => store.approverDrawer)
let visible = computed({
get() {
return approverDrawer.value
},
set() {
closeDrawer()
}
})
watch(approverConfig1, (val: any) => {
approverConfig.value = val.value
})
let changeRange = () => {
approverConfig.value.nodeUserList = []
}
const changeType = (val) => {
approverConfig.value.nodeUserList = []
approverConfig.value.examineMode = 1
approverConfig.value.noHanderAction = 2
if (val == 2) {
approverConfig.value.directorLevel = 1
} else if (val == 4) {
approverConfig.value.selectMode = 1
approverConfig.value.selectRange = 1
} else if (val == 7) {
approverConfig.value.examineEndDirectorLevel = 1
}
}
const addApprover = () => {
approverVisible.value = true
checkedList.value = approverConfig.value.nodeUserList
}
const addRoleApprover = () => {
approverRoleVisible.value = true
checkedRoleList.value = approverConfig.value.nodeUserList
}
const sureApprover = (data) => {
approverConfig.value.nodeUserList = data
approverVisible.value = false
}
const sureRoleApprover = (data) => {
approverConfig.value.nodeUserList = data
approverRoleVisible.value = false
}
const saveApprover = () => {
approverConfig.value.error = !setApproverStr(approverConfig.value)
setApproverConfig({
value: approverConfig.value,
flag: true,
id: approverConfig1.value.id
})
closeDrawer()
}
const closeDrawer = () => {
setApprover(false)
}
</script>
<style lang="scss" scoped>
.set_promoter {
.approver_content {
padding-bottom: 10px;
border-bottom: 1px solid #f2f2f2;
}
.approver_self_select,
.approver_content {
.el-button {
margin-bottom: 20px;
}
}
.approver_content,
.approver_some,
.approver_self_select {
.el-radio-group {
display: unset;
}
.el-radio {
width: 27%;
margin-bottom: 20px;
height: 16px;
}
}
.approver_manager p {
line-height: 32px;
}
.approver_manager select {
width: 420px;
height: 32px;
background: rgba(255, 255, 255, 1);
border-radius: 4px;
border: 1px solid rgba(217, 217, 217, 1);
}
.approver_manager p.tip {
margin: 10px 0 22px 0;
font-size: 12px;
line-height: 16px;
color: #f8642d;
}
.approver_self {
padding: 28px 20px;
}
.approver_self_select,
.approver_manager,
.approver_content,
.approver_some {
padding: 20px 20px 0;
}
.approver_manager p:first-of-type,
.approver_some p {
line-height: 19px;
font-size: 14px;
margin-bottom: 14px;
}
.approver_self_select h3 {
margin: 5px 0 20px;
font-size: 14px;
font-weight: bold;
line-height: 19px;
}
}
</style>
/**
* todo
*/
export const arrToStr = (arr?: [{ name: string }]) => {
if (arr) {
return arr
.map((item) => {
return item.name
})
.toString()
}
}
export const 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 arrToStr(nodeConfig.nodeUserList)
} else if (nodeConfig.examineMode == 2) {
return nodeConfig.nodeUserList.length + '人会签'
}
}
} else if (nodeConfig.settype == 2) {
const 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 + '个层级主管'
}
}
export const copyerStr = (nodeConfig: any) => {
if (nodeConfig.nodeUserList.length != 0) {
return arrToStr(nodeConfig.nodeUserList)
} else {
if (nodeConfig.ccSelfSelectFlag == 1) {
return '发起人自选'
}
}
}
export const conditionStr = (nodeConfig, index) => {
const { 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 (let i = 0; i < conditionList.length; i++) {
const {
columnId,
columnType,
showType,
showName,
optType,
zdy1,
opt1,
zdy2,
opt2,
fixedDownBoxValue
} = conditionList[i]
if (columnId == 0) {
if (nodeUserList.length != 0) {
str += '发起人属于:'
str +=
nodeUserList
.map((item) => {
return item.name
})
.join('或') + ' 并且 '
}
}
if (columnType == 'String' && showType == '3') {
if (zdy1) {
str += showName + '属于:' + dealStr(zdy1, JSON.parse(fixedDownBoxValue)) + ' 并且 '
}
}
if (columnType == 'Double') {
if (optType != 6 && zdy1) {
const 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) : '请设置条件'
}
}
export const dealStr = (str: string, obj) => {
const arr = []
const list = str.split(',')
for (const elem in obj) {
list.map((item) => {
if (item == elem) {
arr.push(obj[elem].value)
}
})
}
return arr.join('或')
}
export const removeEle = (arr, elem, key = 'id') => {
let includesIndex
arr.map((item, index) => {
if (item[key] == elem[key]) {
includesIndex = index
}
})
arr.splice(includesIndex, 1)
}
export const bgColors = ['87, 106, 149', '255, 148, 62', '50, 150, 250']
export const placeholderList = ['发起人', '审核人', '抄送人']
export const setTypes = [
{ value: 1, label: '指定成员' },
{ value: 2, label: '主管' },
{ value: 4, label: '发起人自选' },
{ value: 5, label: '发起人自己' },
{ value: 7, label: '连续多级主管' }
]
export const selectModes = [
{ value: 1, label: '选一个人' },
{ value: 2, label: '选多个人' }
]
export const selectRanges = [
{ value: 1, label: '全公司' },
{ value: 2, label: '指定成员' },
{ value: 3, label: '指定角色' }
]
export const optTypes = [
{ value: '1', label: '小于' },
{ value: '2', label: '大于' },
{ value: '3', label: '小于等于' },
{ value: '4', label: '等于' },
{ value: '5', label: '大于等于' },
{ value: '6', label: '介于两个数之间' }
]
import { store } from '../index'
import { defineStore } from 'pinia'
export const useWorkFlowStore = defineStore('simpleWorkflow', {
state: () => ({
tableId: '',
isTried: false,
promoterDrawer: false,
flowPermission1: {},
approverDrawer: false,
approverConfig1: {},
copyerDrawer: false,
copyerConfig1: {},
conditionDrawer: false,
conditionsConfig1: {
conditionNodes: []
}
}),
actions: {
setTableId(payload) {
this.tableId = payload
},
setIsTried(payload) {
this.isTried = payload
},
setPromoter(payload) {
this.promoterDrawer = payload
},
setFlowPermission(payload) {
this.flowPermission1 = payload
},
setApprover(payload) {
this.approverDrawer = payload
},
setApproverConfig(payload) {
this.approverConfig1 = payload
},
setCopyer(payload) {
this.copyerDrawer = payload
},
setCopyerConfig(payload) {
this.copyerConfig1 = payload
},
setCondition(payload) {
this.conditionDrawer = payload
},
setConditionsConfig(payload) {
this.conditionsConfig1 = payload
}
}
})
export const useWorkFlowStoreWithOut = () => {
return useWorkFlowStore(store)
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment