Skip to content

Commit c9b12c8

Browse files
committed
【功能优化】仿钉钉流程设计优化
1 parent a43bca5 commit c9b12c8

File tree

13 files changed

+1316
-717
lines changed

13 files changed

+1316
-717
lines changed
Lines changed: 1 addition & 0 deletions
Loading

src/components/SimpleProcessDesignerV2/src/ProcessNodeTree.vue

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,10 @@
4747
/>
4848

4949
<!-- 结束节点 -->
50-
<EndEventNode v-if="currentNode && currentNode.type === NodeType.END_EVENT_NODE" :flow-node="currentNode" />
50+
<EndEventNode
51+
v-if="currentNode && currentNode.type === NodeType.END_EVENT_NODE"
52+
:flow-node="currentNode"
53+
/>
5154
</template>
5255
<script setup lang="ts">
5356
import StartUserNode from './nodes/StartUserNode.vue'

src/components/SimpleProcessDesignerV2/src/SimpleProcessDesigner.vue

Lines changed: 33 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,11 @@
11
<template>
2-
<div class="simple-flow-canvas" v-loading="loading">
3-
<div class="simple-flow-container">
4-
<div class="top-area-container">
5-
<div class="top-actions">
6-
<div class="canvas-control">
7-
<span class="control-scale-group">
8-
<span class="control-scale-button"> <Icon icon="ep:plus" @click="zoomOut()" /></span>
9-
<span class="control-scale-label">{{ scaleValue }}%</span>
10-
<span class="control-scale-button"><Icon icon="ep:minus" @click="zoomIn()" /></span>
11-
</span>
12-
</div>
13-
<el-button type="primary" @click="saveSimpleFlowModel">保存</el-button>
14-
<!-- <el-button type="primary">全局设置</el-button> -->
15-
</div>
16-
</div>
17-
<div class="scale-container" :style="`transform: scale(${scaleValue / 100});`">
18-
<ProcessNodeTree v-if="processNodeTree" v-model:flow-node="processNodeTree" />
19-
</div>
20-
</div>
2+
<div v-loading="loading">
3+
<SimpleProcessModel
4+
v-if="processNodeTree"
5+
:flow-node="processNodeTree"
6+
:readonly="false"
7+
@save="saveSimpleFlowModel"
8+
/>
219
<Dialog v-model="errorDialogVisible" title="保存失败" width="400" :fullscreen="false">
2210
<div class="mb-2">以下节点内容不完善,请修改后保存</div>
2311
<div
@@ -35,7 +23,7 @@
3523
</template>
3624

3725
<script setup lang="ts">
38-
import ProcessNodeTree from './ProcessNodeTree.vue'
26+
import SimpleProcessModel from './SimpleProcessModel.vue'
3927
import { updateBpmSimpleModel, getBpmSimpleModel } from '@/api/bpm/simple'
4028
import { SimpleFlowNode, NodeType, NodeId, NODE_DEFAULT_TEXT } from './consts'
4129
import { getModel } from '@/api/bpm/model'
@@ -50,13 +38,15 @@ import * as UserGroupApi from '@/api/bpm/userGroup'
5038
defineOptions({
5139
name: 'SimpleProcessDesigner'
5240
})
53-
const router = useRouter() // 路由
41+
const emits = defineEmits(['success']) // 保存成功事件
42+
5443
const props = defineProps({
5544
modelId: {
5645
type: String,
5746
required: true
5847
}
5948
})
49+
6050
const loading = ref(false)
6151
const formFields = ref<string[]>([])
6252
const formType = ref(20)
@@ -66,7 +56,6 @@ const userOptions = ref<UserApi.UserVO[]>([]) // 用户列表
6656
const deptOptions = ref<DeptApi.DeptVO[]>([]) // 部门列表
6757
const deptTreeOptions = ref()
6858
const userGroupOptions = ref<UserGroupApi.UserGroupVO[]>([]) // 用户组列表
69-
provide('readonly', false)
7059
provide('formFields', formFields)
7160
provide('formType', formType)
7261
provide('roleList', roleOptions)
@@ -80,28 +69,26 @@ const message = useMessage() // 国际化
8069
const processNodeTree = ref<SimpleFlowNode | undefined>()
8170
const errorDialogVisible = ref(false)
8271
let errorNodes: SimpleFlowNode[] = []
83-
const saveSimpleFlowModel = async () => {
84-
if (!props.modelId) {
85-
message.error('缺少模型 modelId 编号')
72+
const saveSimpleFlowModel = async (simpleModelNode: SimpleFlowNode) => {
73+
if (!simpleModelNode) {
74+
message.error('模型数据为空')
8675
return
8776
}
88-
errorNodes = []
89-
validateNode(processNodeTree.value, errorNodes)
90-
if (errorNodes.length > 0) {
91-
errorDialogVisible.value = true
92-
return
93-
}
94-
const data = {
95-
id: props.modelId,
96-
simpleModel: processNodeTree.value
97-
}
98-
99-
const result = await updateBpmSimpleModel(data)
100-
if (result) {
101-
message.success('修改成功')
102-
close()
103-
} else {
104-
message.alert('修改失败')
77+
try {
78+
loading.value = true
79+
const data = {
80+
id: props.modelId,
81+
simpleModel: simpleModelNode
82+
}
83+
const result = await updateBpmSimpleModel(data)
84+
if (result) {
85+
message.success('修改成功')
86+
emits('success')
87+
} else {
88+
message.alert('修改失败')
89+
}
90+
} finally {
91+
loading.value = false
10592
}
10693
}
10794
// 校验节点设置。 暂时以 showText 为空 未节点错误配置
@@ -126,12 +113,13 @@ const validateNode = (node: SimpleFlowNode | undefined, errorNodes: SimpleFlowNo
126113
}
127114
validateNode(node.childNode, errorNodes)
128115
}
129-
116+
130117
if (
131118
type == NodeType.CONDITION_BRANCH_NODE ||
132119
type == NodeType.PARALLEL_BRANCH_NODE ||
133120
type == NodeType.INCLUSIVE_BRANCH_NODE
134-
) { // 分支节点
121+
) {
122+
// 分支节点
135123
// 1. 先校验各个分支
136124
conditionNodes?.forEach((item) => {
137125
validateNode(item, errorNodes)
@@ -142,27 +130,6 @@ const validateNode = (node: SimpleFlowNode | undefined, errorNodes: SimpleFlowNo
142130
}
143131
}
144132
145-
const close = () => {
146-
router.push({ path: '/bpm/manager/model' })
147-
}
148-
let scaleValue = ref(100)
149-
const MAX_SCALE_VALUE = 200
150-
const MIN_SCALE_VALUE = 50
151-
// 放大
152-
const zoomOut = () => {
153-
if (scaleValue.value == MAX_SCALE_VALUE) {
154-
return
155-
}
156-
scaleValue.value += 10
157-
}
158-
// 缩小
159-
const zoomIn = () => {
160-
if (scaleValue.value == MIN_SCALE_VALUE) {
161-
return
162-
}
163-
scaleValue.value -= 10
164-
}
165-
166133
onMounted(async () => {
167134
try {
168135
loading.value = true
@@ -188,7 +155,7 @@ onMounted(async () => {
188155
// 获取用户组列表
189156
userGroupOptions.value = await UserGroupApi.getUserGroupSimpleList()
190157
191-
// 获取 SIMPLE 设计器模型
158+
//获取 SIMPLE 设计器模型
192159
const result = await getBpmSimpleModel(props.modelId)
193160
if (result) {
194161
processNodeTree.value = result
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
<template>
2+
<div
3+
class="simple-process-model-container position-relative"
4+
:style="`transform: scale(${scaleValue / 100});`"
5+
>
6+
<div class="position-absolute top-0px right-0px bg-#fff">
7+
<el-row type="flex" justify="end">
8+
<el-button-group key="scale-control" size="default">
9+
<el-button size="default" :icon="ScaleToOriginal" @click="processReZoom()" />
10+
<el-button size="default" :plain="true" :icon="ZoomOut" @click="zoomOut()" />
11+
<el-button size="default" class="w-80px"> {{ scaleValue }}% </el-button>
12+
<el-button size="default" :plain="true" :icon="ZoomIn" @click="zoomIn()" />
13+
</el-button-group>
14+
<el-button
15+
v-if="!readonly"
16+
size="default"
17+
class="ml-4px"
18+
type="primary"
19+
:icon="Select"
20+
@click="saveSimpleFlowModel"
21+
>保存模型</el-button
22+
>
23+
</el-row>
24+
</div>
25+
<ProcessNodeTree v-if="processNodeTree" v-model:flow-node="processNodeTree" />
26+
</div>
27+
<Dialog v-model="errorDialogVisible" title="保存失败" width="400" :fullscreen="false">
28+
<div class="mb-2">以下节点内容不完善,请修改后保存</div>
29+
<div
30+
class="mb-3 b-rounded-1 bg-gray-100 p-2 line-height-normal"
31+
v-for="(item, index) in errorNodes"
32+
:key="index"
33+
>
34+
{{ item.name }} : {{ NODE_DEFAULT_TEXT.get(item.type) }}
35+
</div>
36+
<template #footer>
37+
<el-button type="primary" @click="errorDialogVisible = false">知道了</el-button>
38+
</template>
39+
</Dialog>
40+
</template>
41+
42+
<script setup lang="ts">
43+
import ProcessNodeTree from './ProcessNodeTree.vue'
44+
import { SimpleFlowNode, NodeType, NODE_DEFAULT_TEXT } from './consts'
45+
import { useWatchNode } from './node'
46+
import { Select, ZoomOut, ZoomIn, ScaleToOriginal } from '@element-plus/icons-vue'
47+
defineOptions({
48+
name: 'SimpleProcessModel'
49+
})
50+
51+
const props = defineProps({
52+
flowNode: {
53+
type: Object as () => SimpleFlowNode,
54+
required: true
55+
},
56+
readonly: {
57+
type: Boolean,
58+
required: false,
59+
default: true
60+
}
61+
})
62+
const emits = defineEmits<{
63+
'save': [node: SimpleFlowNode | undefined]
64+
}>()
65+
66+
const processNodeTree = useWatchNode(props)
67+
68+
provide('readonly', props.readonly)
69+
let scaleValue = ref(100)
70+
const MAX_SCALE_VALUE = 200
71+
const MIN_SCALE_VALUE = 50
72+
// 放大
73+
const zoomIn = () => {
74+
if (scaleValue.value == MAX_SCALE_VALUE) {
75+
return
76+
}
77+
scaleValue.value += 10
78+
}
79+
// 缩小
80+
const zoomOut = () => {
81+
if (scaleValue.value == MIN_SCALE_VALUE) {
82+
return
83+
}
84+
scaleValue.value -= 10
85+
}
86+
const processReZoom = () => {
87+
scaleValue.value = 100
88+
}
89+
90+
const errorDialogVisible = ref(false)
91+
let errorNodes: SimpleFlowNode[] = []
92+
const saveSimpleFlowModel = async () => {
93+
errorNodes = []
94+
validateNode(processNodeTree.value, errorNodes)
95+
if (errorNodes.length > 0) {
96+
errorDialogVisible.value = true
97+
return
98+
}
99+
emits('save', processNodeTree.value)
100+
}
101+
// 校验节点设置。 暂时以 showText 为空 未节点错误配置
102+
const validateNode = (node: SimpleFlowNode | undefined, errorNodes: SimpleFlowNode[]) => {
103+
if (node) {
104+
const { type, showText, conditionNodes } = node
105+
if (type == NodeType.END_EVENT_NODE) {
106+
return
107+
}
108+
if (type == NodeType.START_USER_NODE) {
109+
// 发起人节点暂时不用校验,直接校验孩子节点
110+
validateNode(node.childNode, errorNodes)
111+
}
112+
113+
if (
114+
type === NodeType.USER_TASK_NODE ||
115+
type === NodeType.COPY_TASK_NODE ||
116+
type === NodeType.CONDITION_NODE
117+
) {
118+
if (!showText) {
119+
errorNodes.push(node)
120+
}
121+
validateNode(node.childNode, errorNodes)
122+
}
123+
124+
if (
125+
type == NodeType.CONDITION_BRANCH_NODE ||
126+
type == NodeType.PARALLEL_BRANCH_NODE ||
127+
type == NodeType.INCLUSIVE_BRANCH_NODE
128+
) {
129+
// 分支节点
130+
// 1. 先校验各个分支
131+
conditionNodes?.forEach((item) => {
132+
validateNode(item, errorNodes)
133+
})
134+
// 2. 校验孩子节点
135+
validateNode(node.childNode, errorNodes)
136+
}
137+
}
138+
}
139+
</script>
140+
141+
<style lang="scss" scoped></style>
Lines changed: 9 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,63 +1,25 @@
11
<template>
2-
<div class="simple-flow-canvas" v-loading="loading">
3-
<div class="simple-flow-container">
4-
<div class="scale-container" :style="`transform: scale(${scaleValue / 100});`">
5-
<ProcessNodeTree v-if="processNodeTree" v-model:flow-node="processNodeTree"/>
6-
</div>
7-
</div>
8-
</div>
2+
<SimpleProcessModel :flow-node="simpleModel" :readonly="true"/>
93
</template>
104

115
<script setup lang="ts">
12-
import ProcessNodeTree from './ProcessNodeTree.vue'
6+
import { useWatchNode } from './node'
137
import { SimpleFlowNode } from './consts'
148
159
defineOptions({
16-
name: 'SimpleProcessRender'
10+
name: 'SimpleProcessViewer'
1711
})
1812
1913
const props = defineProps({
2014
flowNode: {
2115
type: Object as () => SimpleFlowNode,
2216
required: true
17+
},
18+
// 流程任务
19+
tasks : {
20+
type : Array,
21+
default: () => [] as any[]
2322
}
2423
})
25-
const loading = ref(false)
26-
27-
watch(
28-
() => props.flowNode,
29-
(newValue) => {
30-
processNodeTree.value = newValue
31-
}
32-
)
33-
const processNodeTree = ref<SimpleFlowNode | undefined>(props.flowNode)
34-
provide('readonly', true)
35-
let scaleValue = ref(100)
36-
const MAX_SCALE_VALUE = 200
37-
const MIN_SCALE_VALUE = 50
38-
// 放大
39-
const zoomOut = () => {
40-
if (scaleValue.value == MAX_SCALE_VALUE) {
41-
return
42-
}
43-
scaleValue.value += 10
44-
}
45-
// 缩小
46-
const zoomIn = () => {
47-
if (scaleValue.value == MIN_SCALE_VALUE) {
48-
return
49-
}
50-
scaleValue.value -= 10
51-
}
52-
53-
// onMounted(async () => {
54-
// try {
55-
// loading.value = true
56-
// if (props.view) {
57-
// processNodeTree.value = props.view.simpleModel
58-
// }
59-
// } finally {
60-
// loading.value = false
61-
// }
62-
// })
24+
const simpleModel = useWatchNode(props)
6325
</script>

0 commit comments

Comments
 (0)