workflow-bpmn-modeler-andtv-vue3 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -0
- package/LICENSE +21 -0
- package/dist/demo.html +9 -0
- package/dist/fonts/bpmn.5d33bee4.eot +0 -0
- package/dist/fonts/bpmn.67058807.woff2 +0 -0
- package/dist/fonts/bpmn.b5c9250d.ttf +0 -0
- package/dist/fonts/bpmn.e9e7d076.woff +0 -0
- package/dist/img/bpmn.74eea12b.svg +224 -0
- package/dist/workflow-bpmn-modeler-andtv-vue3.common.js +5617 -0
- package/dist/workflow-bpmn-modeler-andtv-vue3.umd.js +5628 -0
- package/dist/workflow-bpmn-modeler-andtv-vue3.umd.min.js +5628 -0
- package/package/BpmData.js +68 -0
- package/package/PropertyPanel.vue +342 -0
- package/package/common/customTranslate.js +20 -0
- package/package/common/mixinExecutionListener.js +24 -0
- package/package/common/mixinPanel.js +70 -0
- package/package/common/mixinXcrud.js +22 -0
- package/package/common/parseElement.js +53 -0
- package/package/components/custom/customContextPad.vue +24 -0
- package/package/components/nodePanel/gateway.vue +165 -0
- package/package/components/nodePanel/process.vue +201 -0
- package/package/components/nodePanel/property/executionListener.vue +240 -0
- package/package/components/nodePanel/property/listenerParam.vue +137 -0
- package/package/components/nodePanel/property/multiInstance.vue +177 -0
- package/package/components/nodePanel/property/signal.vue +178 -0
- package/package/components/nodePanel/property/taskListener.vue +242 -0
- package/package/components/nodePanel/sequenceFlow.vue +180 -0
- package/package/components/nodePanel/startEnd.vue +174 -0
- package/package/components/nodePanel/task.vue +452 -0
- package/package/flowable/flowable.json +1218 -0
- package/package/flowable/init.js +24 -0
- package/package/flowable/showConfig.js +51 -0
- package/package/index.js +8 -0
- package/package/index.ts +9 -0
- package/package/index.vue +730 -0
- package/package/lang/zh.js +227 -0
- package/package/types/components.d.ts +35 -0
- package/package.json +73 -0
@@ -0,0 +1,730 @@
|
|
1
|
+
<template>
|
2
|
+
<div v-loading="isView" class="flow-containers" :class="{ 'view-mode': isView }">
|
3
|
+
<a-layout class="main-layout">
|
4
|
+
<!-- 顶部工具栏 -->
|
5
|
+
<a-layout-header class="toolbar-header">
|
6
|
+
<div class="toolbar-container">
|
7
|
+
<!-- 左侧工具组 -->
|
8
|
+
<div class="toolbar-left">
|
9
|
+
<div class="toolbar-group">
|
10
|
+
<span class="group-label">文件操作</span>
|
11
|
+
<a-upload :before-upload="openBpmn" class="toolbar-item">
|
12
|
+
<a-tooltip title="加载XML文件" placement="bottom">
|
13
|
+
<a-button class="toolbar-btn" size="small">
|
14
|
+
<template #icon><FolderOpenOutlined /></template>
|
15
|
+
</a-button>
|
16
|
+
</a-tooltip>
|
17
|
+
</a-upload>
|
18
|
+
<a-tooltip title="新建流程图" placement="bottom">
|
19
|
+
<a-button class="toolbar-btn" size="small" @click="newDiagram">
|
20
|
+
<template #icon><PlusCircleOutlined /></template>
|
21
|
+
</a-button>
|
22
|
+
</a-tooltip>
|
23
|
+
</div>
|
24
|
+
|
25
|
+
<div class="toolbar-group">
|
26
|
+
<span class="group-label">视图控制</span>
|
27
|
+
<a-tooltip title="自适应屏幕" placement="bottom">
|
28
|
+
<a-button class="toolbar-btn" size="small" @click="fitViewport">
|
29
|
+
<template #icon><FullscreenOutlined /></template>
|
30
|
+
</a-button>
|
31
|
+
</a-tooltip>
|
32
|
+
<a-tooltip title="放大视图" placement="bottom">
|
33
|
+
<a-button class="toolbar-btn" size="small" @click="zoomViewport(true)">
|
34
|
+
<template #icon><ZoomInOutlined /></template>
|
35
|
+
</a-button>
|
36
|
+
</a-tooltip>
|
37
|
+
<a-tooltip title="缩小视图" placement="bottom">
|
38
|
+
<a-button class="toolbar-btn" size="small" @click="zoomViewport(false)">
|
39
|
+
<template #icon><ZoomOutOutlined /></template>
|
40
|
+
</a-button>
|
41
|
+
</a-tooltip>
|
42
|
+
</div>
|
43
|
+
|
44
|
+
<div class="toolbar-group">
|
45
|
+
<span class="group-label">历史操作</span>
|
46
|
+
<a-tooltip title="撤销操作" placement="bottom">
|
47
|
+
<a-button class="toolbar-btn" size="small" @click="() => modeler?.get('commandStack').undo()">
|
48
|
+
<template #icon><UndoOutlined /></template>
|
49
|
+
</a-button>
|
50
|
+
</a-tooltip>
|
51
|
+
<a-tooltip title="重做操作" placement="bottom">
|
52
|
+
<a-button class="toolbar-btn" size="small" @click="() => modeler?.get('commandStack').redo()">
|
53
|
+
<template #icon><RedoOutlined /></template>
|
54
|
+
</a-button>
|
55
|
+
</a-tooltip>
|
56
|
+
</div>
|
57
|
+
</div>
|
58
|
+
|
59
|
+
<!-- 右侧操作组 -->
|
60
|
+
<div class="toolbar-right">
|
61
|
+
<div class="toolbar-group">
|
62
|
+
<span class="group-label">导出</span>
|
63
|
+
<a-button class="toolbar-btn export-btn" size="small" @click="saveXML(true)">
|
64
|
+
<template #icon><DownloadOutlined /></template>
|
65
|
+
下载XML
|
66
|
+
</a-button>
|
67
|
+
<a-button class="toolbar-btn export-btn" size="small" @click="saveImg('svg', true)">
|
68
|
+
<template #icon><PictureOutlined /></template>
|
69
|
+
下载SVG
|
70
|
+
</a-button>
|
71
|
+
</div>
|
72
|
+
<a-button class="save-btn" type="primary" @click="save">
|
73
|
+
<template #icon><DownloadOutlined /></template>
|
74
|
+
保存模型
|
75
|
+
</a-button>
|
76
|
+
</div>
|
77
|
+
</div>
|
78
|
+
</a-layout-header>
|
79
|
+
|
80
|
+
<!-- 主内容区域 -->
|
81
|
+
<a-layout class="content-layout">
|
82
|
+
<a-layout-content class="canvas-container">
|
83
|
+
<div ref="canvas" class="canvas" />
|
84
|
+
</a-layout-content>
|
85
|
+
<a-layout-sider class="property-panel">
|
86
|
+
<PropertyPanel v-if="modeler" :modeler="modeler" :users="users" :groups="groups" :categorys="categorys" />
|
87
|
+
</a-layout-sider>
|
88
|
+
</a-layout>
|
89
|
+
</a-layout>
|
90
|
+
</div>
|
91
|
+
</template>
|
92
|
+
|
93
|
+
<script setup lang="ts">
|
94
|
+
import { ref, watch, onMounted, nextTick } from 'vue'
|
95
|
+
|
96
|
+
defineOptions({
|
97
|
+
name: 'WorkflowBpmnModeler'
|
98
|
+
})
|
99
|
+
// 汉化
|
100
|
+
import customTranslate from './common/customTranslate'
|
101
|
+
import Modeler from 'bpmn-js/lib/Modeler'
|
102
|
+
import PropertyPanel from './PropertyPanel.vue'
|
103
|
+
import BpmData from './BpmData'
|
104
|
+
import getInitStr from './flowable/init'
|
105
|
+
// 引入flowable的节点文件
|
106
|
+
import flowableModdle from './flowable/flowable.json'
|
107
|
+
// 引入图标
|
108
|
+
import {
|
109
|
+
FolderOpenOutlined,
|
110
|
+
PlusCircleOutlined,
|
111
|
+
FullscreenOutlined,
|
112
|
+
ZoomInOutlined,
|
113
|
+
ZoomOutOutlined,
|
114
|
+
UndoOutlined,
|
115
|
+
RedoOutlined,
|
116
|
+
DownloadOutlined,
|
117
|
+
PictureOutlined
|
118
|
+
} from '@ant-design/icons-vue'
|
119
|
+
|
120
|
+
interface User {
|
121
|
+
name: string
|
122
|
+
id: string
|
123
|
+
}
|
124
|
+
|
125
|
+
interface Group {
|
126
|
+
name: string
|
127
|
+
id: string
|
128
|
+
}
|
129
|
+
|
130
|
+
interface Category {
|
131
|
+
name: string
|
132
|
+
id: string
|
133
|
+
}
|
134
|
+
|
135
|
+
interface SaveData {
|
136
|
+
process: any
|
137
|
+
xml: string | undefined
|
138
|
+
svg: string | undefined
|
139
|
+
}
|
140
|
+
|
141
|
+
interface Props {
|
142
|
+
xml?: string
|
143
|
+
users?: User[]
|
144
|
+
groups?: Group[]
|
145
|
+
categorys?: Category[]
|
146
|
+
isView?: boolean
|
147
|
+
}
|
148
|
+
|
149
|
+
const props = withDefaults(defineProps<Props>(), {
|
150
|
+
xml: '',
|
151
|
+
users: () => [],
|
152
|
+
groups: () => [],
|
153
|
+
categorys: () => [],
|
154
|
+
isView: false
|
155
|
+
})
|
156
|
+
|
157
|
+
const emit = defineEmits<{
|
158
|
+
save: [data: SaveData]
|
159
|
+
}>()
|
160
|
+
const canvas = ref<HTMLElement | null>(null)
|
161
|
+
const modeler = ref<Modeler | null>(null)
|
162
|
+
const taskList = ref<any[]>([])
|
163
|
+
const zoom = ref<number>(1)
|
164
|
+
|
165
|
+
watch(() => props.xml, (val: string) => {
|
166
|
+
if (val) {
|
167
|
+
createNewDiagram(val)
|
168
|
+
}
|
169
|
+
})
|
170
|
+
|
171
|
+
onMounted(() => {
|
172
|
+
// 生成实例
|
173
|
+
modeler.value = new Modeler({
|
174
|
+
container: canvas.value,
|
175
|
+
additionalModules: [
|
176
|
+
{
|
177
|
+
translate: ['value', customTranslate]
|
178
|
+
}
|
179
|
+
],
|
180
|
+
moddleExtensions: {
|
181
|
+
flowable: flowableModdle
|
182
|
+
}
|
183
|
+
})
|
184
|
+
// 新增流程定义
|
185
|
+
if (!props.xml) {
|
186
|
+
newDiagram()
|
187
|
+
} else {
|
188
|
+
createNewDiagram(props.xml)
|
189
|
+
}
|
190
|
+
})
|
191
|
+
|
192
|
+
const newDiagram = (): void => {
|
193
|
+
createNewDiagram(getInitStr())
|
194
|
+
}
|
195
|
+
|
196
|
+
// 让图能自适应屏幕
|
197
|
+
const fitViewport = (): void => {
|
198
|
+
if (!modeler.value) return
|
199
|
+
zoom.value = modeler.value.get('canvas').zoom('fit-viewport')
|
200
|
+
const viewport = document.querySelector('.flow-containers .viewport') as SVGSVGElement
|
201
|
+
const bbox = viewport?.getBBox()
|
202
|
+
if (!bbox) return
|
203
|
+
const currentViewbox = modeler.value.get('canvas').viewbox()
|
204
|
+
const elementMid = {
|
205
|
+
x: bbox.x + bbox.width / 2 - 65,
|
206
|
+
y: bbox.y + bbox.height / 2
|
207
|
+
}
|
208
|
+
modeler.value.get('canvas').viewbox({
|
209
|
+
x: elementMid.x - currentViewbox.width / 2,
|
210
|
+
y: elementMid.y - currentViewbox.height / 2,
|
211
|
+
width: currentViewbox.width,
|
212
|
+
height: currentViewbox.height
|
213
|
+
})
|
214
|
+
zoom.value = bbox.width / currentViewbox.width * 1.8
|
215
|
+
}
|
216
|
+
|
217
|
+
// 放大缩小
|
218
|
+
const zoomViewport = (zoomIn: boolean = true): void => {
|
219
|
+
if (!modeler.value) return
|
220
|
+
zoom.value = modeler.value.get('canvas').zoom()
|
221
|
+
zoom.value += (zoomIn ? 0.1 : -0.1)
|
222
|
+
modeler.value.get('canvas').zoom(zoom.value)
|
223
|
+
}
|
224
|
+
|
225
|
+
const createNewDiagram = async (data: string): Promise<void> => {
|
226
|
+
// 将字符串转换成图显示出来
|
227
|
+
// data = data.replace(/<!\[CDATA\[(.+?)]]>/g, '<![CDATA[$1]]>')
|
228
|
+
data = data.replace(/<!\[CDATA\[(.+?)]]>/g, function(match: string, str: string) {
|
229
|
+
return str.replace(/</g, '<')
|
230
|
+
})
|
231
|
+
try {
|
232
|
+
if (!modeler.value) return
|
233
|
+
await modeler.value.importXML(data)
|
234
|
+
adjustPalette()
|
235
|
+
fitViewport()
|
236
|
+
// fillColor()
|
237
|
+
} catch (err: any) {
|
238
|
+
console.error(err.message, err.warnings)
|
239
|
+
}
|
240
|
+
}
|
241
|
+
|
242
|
+
// 调整左侧工具栏排版
|
243
|
+
const adjustPalette = (): void => {
|
244
|
+
try {
|
245
|
+
// 获取 bpmn 设计器实例
|
246
|
+
const canvasElement = canvas.value
|
247
|
+
if (!canvasElement) return
|
248
|
+
const djsPalette = canvasElement.children[0].children[1].children[4] as HTMLElement
|
249
|
+
const djsPalStyle: Record<string, string> = {
|
250
|
+
width: '130px',
|
251
|
+
padding: '5px',
|
252
|
+
background: 'white',
|
253
|
+
left: '20px',
|
254
|
+
borderRadius: '0'
|
255
|
+
}
|
256
|
+
for (const key in djsPalStyle) {
|
257
|
+
(djsPalette.style as any)[key] = djsPalStyle[key]
|
258
|
+
}
|
259
|
+
const palette = djsPalette.children[0] as HTMLElement
|
260
|
+
const allGroups = palette.children
|
261
|
+
;(allGroups[0] as HTMLElement).style.display = 'none'
|
262
|
+
// 修改控件样式
|
263
|
+
for (let gKey = 0; gKey < allGroups.length; gKey++) {
|
264
|
+
const group = allGroups[gKey] as HTMLElement
|
265
|
+
for (let cKey = 0; cKey < group.children.length; cKey++) {
|
266
|
+
const control = group.children[cKey] as HTMLElement
|
267
|
+
const controlStyle: Record<string, string> = {
|
268
|
+
display: 'flex',
|
269
|
+
justifyContent: 'flex-start',
|
270
|
+
alignItems: 'center',
|
271
|
+
width: '100%',
|
272
|
+
padding: '5px'
|
273
|
+
}
|
274
|
+
if (
|
275
|
+
control.className &&
|
276
|
+
control.dataset &&
|
277
|
+
control.className.indexOf('entry') !== -1
|
278
|
+
) {
|
279
|
+
const controlProps = new BpmData().getControl(
|
280
|
+
control.dataset.action
|
281
|
+
)
|
282
|
+
control.innerHTML = `<div style='font-size: 14px;font-weight:500;margin-left:15px;'>${
|
283
|
+
controlProps['title']
|
284
|
+
}</div>`
|
285
|
+
for (const csKey in controlStyle) {
|
286
|
+
(control.style as any)[csKey] = controlStyle[csKey]
|
287
|
+
}
|
288
|
+
}
|
289
|
+
}
|
290
|
+
}
|
291
|
+
} catch (e) {
|
292
|
+
console.log(e)
|
293
|
+
}
|
294
|
+
}
|
295
|
+
const fillColor = (): void => {
|
296
|
+
if (!modeler.value) return
|
297
|
+
const canvasElement = modeler.value.get('canvas')
|
298
|
+
(modeler.value as any)._definitions.rootElements[0].flowElements.forEach((n: any) => {
|
299
|
+
if (n.$type === 'bpmn:UserTask') {
|
300
|
+
const completeTask = taskList.value.find(m => m.key === n.id) || { completed: true }
|
301
|
+
const todoTask = taskList.value.find(m => !m.completed)
|
302
|
+
const endTask = taskList.value[taskList.value.length - 1]
|
303
|
+
if (completeTask) {
|
304
|
+
canvasElement.addMarker(n.id, completeTask.completed ? 'highlight' : 'highlight-todo')
|
305
|
+
n.outgoing?.forEach((nn: any) => {
|
306
|
+
const targetTask = taskList.value.find(m => m.key === nn.targetRef.id)
|
307
|
+
if (targetTask) {
|
308
|
+
canvasElement.addMarker(nn.id, targetTask.completed ? 'highlight' : 'highlight-todo')
|
309
|
+
} else if (nn.targetRef.$type === 'bpmn:ExclusiveGateway') {
|
310
|
+
// canvasElement.addMarker(nn.id, 'highlight');
|
311
|
+
canvasElement.addMarker(nn.id, completeTask.completed ? 'highlight' : 'highlight-todo')
|
312
|
+
canvasElement.addMarker(nn.targetRef.id, completeTask.completed ? 'highlight' : 'highlight-todo')
|
313
|
+
} else if (nn.targetRef.$type === 'bpmn:EndEvent') {
|
314
|
+
if (!todoTask && endTask.key === n.id) {
|
315
|
+
canvasElement.addMarker(nn.id, 'highlight')
|
316
|
+
canvasElement.addMarker(nn.targetRef.id, 'highlight')
|
317
|
+
}
|
318
|
+
if (!completeTask.completed) {
|
319
|
+
canvasElement.addMarker(nn.id, 'highlight-todo')
|
320
|
+
canvasElement.addMarker(nn.targetRef.id, 'highlight-todo')
|
321
|
+
}
|
322
|
+
}
|
323
|
+
})
|
324
|
+
}
|
325
|
+
} else if (n.$type === 'bpmn:ExclusiveGateway') {
|
326
|
+
n.outgoing.forEach((nn: any) => {
|
327
|
+
const targetTask = taskList.value.find(m => m.key === nn.targetRef.id)
|
328
|
+
if (targetTask) {
|
329
|
+
canvasElement.addMarker(nn.id, targetTask.completed ? 'highlight' : 'highlight-todo')
|
330
|
+
}
|
331
|
+
})
|
332
|
+
}
|
333
|
+
if (n.$type === 'bpmn:StartEvent') {
|
334
|
+
n.outgoing.forEach((nn: any) => {
|
335
|
+
const completeTask = taskList.value.find(m => m.key === nn.targetRef.id)
|
336
|
+
if (completeTask) {
|
337
|
+
canvasElement.addMarker(nn.id, 'highlight')
|
338
|
+
canvasElement.addMarker(n.id, 'highlight')
|
339
|
+
return
|
340
|
+
}
|
341
|
+
})
|
342
|
+
}
|
343
|
+
})
|
344
|
+
}
|
345
|
+
|
346
|
+
// 对外 api
|
347
|
+
const getProcess = (): any => {
|
348
|
+
const element = getProcessElement()
|
349
|
+
return {
|
350
|
+
id: element.id,
|
351
|
+
name: element.name,
|
352
|
+
category: element.$attrs['flowable:processCategory']
|
353
|
+
}
|
354
|
+
}
|
355
|
+
|
356
|
+
const getProcessElement = (): any => {
|
357
|
+
if (!modeler.value) return null
|
358
|
+
const rootElements = modeler.value.getDefinitions().rootElements
|
359
|
+
for (let i = 0; i < rootElements.length; i++) {
|
360
|
+
if (rootElements[i].$type === 'bpmn:Process') return rootElements[i]
|
361
|
+
}
|
362
|
+
}
|
363
|
+
|
364
|
+
const saveXML = async (download: boolean = false): Promise<string | undefined> => {
|
365
|
+
try {
|
366
|
+
if (!modeler.value) return
|
367
|
+
const { xml } = await modeler.value.saveXML({ format: true })
|
368
|
+
if (download) {
|
369
|
+
const processElement = getProcessElement()
|
370
|
+
if (processElement) {
|
371
|
+
downloadFile(`${processElement.name}.bpmn20.xml`, xml, 'application/xml')
|
372
|
+
}
|
373
|
+
}
|
374
|
+
return xml
|
375
|
+
} catch (err) {
|
376
|
+
console.log(err)
|
377
|
+
}
|
378
|
+
}
|
379
|
+
|
380
|
+
const saveImg = async (type: string = 'svg', download: boolean = false): Promise<string | undefined> => {
|
381
|
+
try {
|
382
|
+
if (!modeler.value) return
|
383
|
+
const { svg } = await modeler.value.saveSVG({ format: true })
|
384
|
+
if (download) {
|
385
|
+
const processElement = getProcessElement()
|
386
|
+
if (processElement) {
|
387
|
+
downloadFile(processElement.name, svg, 'image/svg+xml')
|
388
|
+
}
|
389
|
+
}
|
390
|
+
return svg
|
391
|
+
} catch (err) {
|
392
|
+
console.log(err)
|
393
|
+
}
|
394
|
+
}
|
395
|
+
|
396
|
+
const save = async (): Promise<void> => {
|
397
|
+
const process = getProcess()
|
398
|
+
const xml = await saveXML()
|
399
|
+
const svg = await saveImg()
|
400
|
+
const result = { process, xml, svg }
|
401
|
+
emit('save', result)
|
402
|
+
window.parent.postMessage(result, '*')
|
403
|
+
}
|
404
|
+
|
405
|
+
const openBpmn = (file: File): boolean => {
|
406
|
+
const reader = new FileReader()
|
407
|
+
reader.readAsText(file, 'utf-8')
|
408
|
+
reader.onload = () => {
|
409
|
+
createNewDiagram(reader.result as string)
|
410
|
+
}
|
411
|
+
return false
|
412
|
+
}
|
413
|
+
|
414
|
+
const downloadFile = (filename: string, data: string, type: string): void => {
|
415
|
+
const a = document.createElement('a')
|
416
|
+
const url = window.URL.createObjectURL(new Blob([data], { type: type }))
|
417
|
+
a.href = url
|
418
|
+
a.download = filename
|
419
|
+
a.click()
|
420
|
+
window.URL.revokeObjectURL(url)
|
421
|
+
}
|
422
|
+
|
423
|
+
</script>
|
424
|
+
|
425
|
+
<style lang="scss">
|
426
|
+
/* BPMN.js 样式导入 */
|
427
|
+
@import "~bpmn-js/dist/assets/diagram-js.css";
|
428
|
+
@import "~bpmn-js/dist/assets/bpmn-font/css/bpmn.css";
|
429
|
+
@import "~bpmn-js/dist/assets/bpmn-font/css/bpmn-codes.css";
|
430
|
+
@import "~bpmn-js/dist/assets/bpmn-font/css/bpmn-embedded.css";
|
431
|
+
|
432
|
+
/* 视图模式样式 */
|
433
|
+
.view-mode {
|
434
|
+
.ant-layout-header,
|
435
|
+
.ant-layout-sider,
|
436
|
+
.djs-palette,
|
437
|
+
.bjs-powered-by {
|
438
|
+
display: none;
|
439
|
+
}
|
440
|
+
.ant-spin-mask {
|
441
|
+
background-color: initial;
|
442
|
+
}
|
443
|
+
.ant-spin-spinning {
|
444
|
+
display: none;
|
445
|
+
}
|
446
|
+
}
|
447
|
+
|
448
|
+
/* 主容器样式 */
|
449
|
+
.flow-containers {
|
450
|
+
width: 100%;
|
451
|
+
height: 100vh;
|
452
|
+
background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
|
453
|
+
overflow: hidden;
|
454
|
+
|
455
|
+
/* 主布局 */
|
456
|
+
.main-layout {
|
457
|
+
height: 100%;
|
458
|
+
background: transparent;
|
459
|
+
}
|
460
|
+
|
461
|
+
/* 工具栏头部 */
|
462
|
+
.toolbar-header {
|
463
|
+
background: #f8f9fa;
|
464
|
+
border-bottom: 1px solid #e9ecef;
|
465
|
+
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
|
466
|
+
padding: 0;
|
467
|
+
height: 64px;
|
468
|
+
line-height: 64px;
|
469
|
+
|
470
|
+
.toolbar-container {
|
471
|
+
display: flex;
|
472
|
+
justify-content: space-between;
|
473
|
+
align-items: center;
|
474
|
+
padding: 0 32px;
|
475
|
+
height: 100%;
|
476
|
+
}
|
477
|
+
|
478
|
+
.toolbar-left {
|
479
|
+
display: flex;
|
480
|
+
align-items: center;
|
481
|
+
gap: 20px;
|
482
|
+
}
|
483
|
+
|
484
|
+
.toolbar-right {
|
485
|
+
display: flex;
|
486
|
+
align-items: center;
|
487
|
+
gap: 16px;
|
488
|
+
}
|
489
|
+
|
490
|
+
.toolbar-group {
|
491
|
+
display: flex;
|
492
|
+
align-items: center;
|
493
|
+
gap: 8px;
|
494
|
+
padding: 0;
|
495
|
+
min-width: fit-content;
|
496
|
+
|
497
|
+
.group-label {
|
498
|
+
color: #6c757d;
|
499
|
+
font-size: 12px;
|
500
|
+
font-weight: 600;
|
501
|
+
margin-right: 8px;
|
502
|
+
white-space: nowrap;
|
503
|
+
}
|
504
|
+
}
|
505
|
+
|
506
|
+
.toolbar-btn {
|
507
|
+
background: #ffffff;
|
508
|
+
border: 1px solid #e9ecef;
|
509
|
+
color: #495057;
|
510
|
+
border-radius: 6px;
|
511
|
+
transition: all 0.3s ease;
|
512
|
+
height: 36px;
|
513
|
+
padding: 0 12px;
|
514
|
+
font-size: 13px;
|
515
|
+
font-weight: 500;
|
516
|
+
min-width: 36px;
|
517
|
+
display: flex;
|
518
|
+
align-items: center;
|
519
|
+
justify-content: center;
|
520
|
+
|
521
|
+
&:hover {
|
522
|
+
background: #f8f9fa;
|
523
|
+
border-color: #dee2e6;
|
524
|
+
color: #212529;
|
525
|
+
transform: translateY(-1px);
|
526
|
+
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
527
|
+
}
|
528
|
+
|
529
|
+
&:active {
|
530
|
+
transform: translateY(0);
|
531
|
+
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
532
|
+
}
|
533
|
+
}
|
534
|
+
|
535
|
+
.export-btn {
|
536
|
+
background: #f8f9fa;
|
537
|
+
border: 1px solid #e9ecef;
|
538
|
+
color: #6c757d;
|
539
|
+
|
540
|
+
&:hover {
|
541
|
+
background: #e9ecef;
|
542
|
+
border-color: #dee2e6;
|
543
|
+
color: #495057;
|
544
|
+
}
|
545
|
+
}
|
546
|
+
|
547
|
+
.save-btn {
|
548
|
+
background: #007bff;
|
549
|
+
border: 1px solid #007bff;
|
550
|
+
border-radius: 6px;
|
551
|
+
font-weight: 600;
|
552
|
+
height: 36px;
|
553
|
+
padding: 0 20px;
|
554
|
+
font-size: 13px;
|
555
|
+
color: white;
|
556
|
+
min-width: 120px;
|
557
|
+
|
558
|
+
&:hover {
|
559
|
+
background: #0056b3;
|
560
|
+
border-color: #0056b3;
|
561
|
+
transform: translateY(-1px);
|
562
|
+
box-shadow: 0 2px 8px rgba(0, 123, 255, 0.3);
|
563
|
+
}
|
564
|
+
|
565
|
+
&:active {
|
566
|
+
transform: translateY(0);
|
567
|
+
box-shadow: 0 1px 3px rgba(0, 123, 255, 0.3);
|
568
|
+
}
|
569
|
+
}
|
570
|
+
}
|
571
|
+
|
572
|
+
/* 内容布局 */
|
573
|
+
.content-layout {
|
574
|
+
height: calc(100vh - 64px);
|
575
|
+
background: transparent;
|
576
|
+
}
|
577
|
+
|
578
|
+
/* 画布容器 */
|
579
|
+
.canvas-container {
|
580
|
+
background: white;
|
581
|
+
border-radius: 8px;
|
582
|
+
margin: 16px;
|
583
|
+
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
|
584
|
+
overflow: hidden;
|
585
|
+
position: relative;
|
586
|
+
|
587
|
+
&::before {
|
588
|
+
content: '';
|
589
|
+
position: absolute;
|
590
|
+
top: 0;
|
591
|
+
left: 0;
|
592
|
+
right: 0;
|
593
|
+
bottom: 0;
|
594
|
+
background: linear-gradient(45deg, #f8f9fa 25%, transparent 25%),
|
595
|
+
linear-gradient(-45deg, #f8f9fa 25%, transparent 25%),
|
596
|
+
linear-gradient(45deg, transparent 75%, #f8f9fa 75%),
|
597
|
+
linear-gradient(-45deg, transparent 75%, #f8f9fa 75%);
|
598
|
+
background-size: 20px 20px;
|
599
|
+
background-position: 0 0, 0 10px, 10px -10px, -10px 0px;
|
600
|
+
opacity: 0.3;
|
601
|
+
pointer-events: none;
|
602
|
+
}
|
603
|
+
}
|
604
|
+
|
605
|
+
.canvas {
|
606
|
+
width: 100%;
|
607
|
+
height: 100%;
|
608
|
+
position: relative;
|
609
|
+
z-index: 1;
|
610
|
+
}
|
611
|
+
|
612
|
+
/* 属性面板 */
|
613
|
+
.property-panel {
|
614
|
+
width: 380px !important;
|
615
|
+
min-width: 380px !important;
|
616
|
+
max-width: 380px !important;
|
617
|
+
background: white;
|
618
|
+
border-radius: 8px;
|
619
|
+
margin: 16px 20px 16px 0;
|
620
|
+
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
|
621
|
+
overflow: hidden;
|
622
|
+
display: flex;
|
623
|
+
flex-direction: column;
|
624
|
+
height: calc(100vh - 96px);
|
625
|
+
|
626
|
+
:deep(.property-panel) {
|
627
|
+
height: 100%;
|
628
|
+
overflow-y: auto;
|
629
|
+
overflow-x: hidden;
|
630
|
+
}
|
631
|
+
}
|
632
|
+
|
633
|
+
/* BPMN 工具栏样式优化 */
|
634
|
+
.djs-palette {
|
635
|
+
left: 20px !important;
|
636
|
+
top: 20px !important;
|
637
|
+
border: none !important;
|
638
|
+
border-radius: 12px !important;
|
639
|
+
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15) !important;
|
640
|
+
background: white !important;
|
641
|
+
padding: 16px !important;
|
642
|
+
width: 160px !important;
|
643
|
+
min-width: 160px !important;
|
644
|
+
|
645
|
+
.djs-palette-entries {
|
646
|
+
.djs-palette-entry {
|
647
|
+
border-radius: 8px !important;
|
648
|
+
margin: 6px 0 !important;
|
649
|
+
transition: all 0.3s ease !important;
|
650
|
+
width: 100% !important;
|
651
|
+
min-height: 40px !important;
|
652
|
+
display: flex !important;
|
653
|
+
align-items: center !important;
|
654
|
+
|
655
|
+
&:hover {
|
656
|
+
background: linear-gradient(135deg, #e6f7ff 0%, #bae7ff 100%) !important;
|
657
|
+
transform: translateY(-1px) !important;
|
658
|
+
box-shadow: 0 4px 12px rgba(24, 144, 255, 0.2) !important;
|
659
|
+
}
|
660
|
+
|
661
|
+
.entry {
|
662
|
+
padding: 10px 12px !important;
|
663
|
+
border-radius: 8px !important;
|
664
|
+
font-size: 13px !important;
|
665
|
+
font-weight: 500 !important;
|
666
|
+
color: #262626 !important;
|
667
|
+
width: 100% !important;
|
668
|
+
white-space: nowrap !important;
|
669
|
+
overflow: hidden !important;
|
670
|
+
text-overflow: ellipsis !important;
|
671
|
+
display: flex !important;
|
672
|
+
align-items: center !important;
|
673
|
+
gap: 8px !important;
|
674
|
+
}
|
675
|
+
}
|
676
|
+
}
|
677
|
+
}
|
678
|
+
|
679
|
+
/* BPMN 画布样式 */
|
680
|
+
.djs-container svg {
|
681
|
+
min-height: 100%;
|
682
|
+
background: transparent;
|
683
|
+
}
|
684
|
+
|
685
|
+
/* 表单样式优化 */
|
686
|
+
.ant-form-item-label > label {
|
687
|
+
font-size: 13px;
|
688
|
+
font-weight: 500;
|
689
|
+
color: #262626;
|
690
|
+
}
|
691
|
+
|
692
|
+
/* 响应式设计 */
|
693
|
+
@media (max-width: 1200px) {
|
694
|
+
.property-panel {
|
695
|
+
width: 320px !important;
|
696
|
+
min-width: 320px !important;
|
697
|
+
max-width: 320px !important;
|
698
|
+
margin: 16px 16px 16px 0;
|
699
|
+
}
|
700
|
+
|
701
|
+
.djs-palette {
|
702
|
+
width: 140px !important;
|
703
|
+
min-width: 140px !important;
|
704
|
+
}
|
705
|
+
}
|
706
|
+
|
707
|
+
@media (max-width: 768px) {
|
708
|
+
.toolbar-container {
|
709
|
+
padding: 0 16px;
|
710
|
+
}
|
711
|
+
|
712
|
+
.toolbar-group {
|
713
|
+
padding: 6px 12px;
|
714
|
+
gap: 6px;
|
715
|
+
|
716
|
+
.group-label {
|
717
|
+
display: none;
|
718
|
+
}
|
719
|
+
}
|
720
|
+
|
721
|
+
.property-panel {
|
722
|
+
width: 100% !important;
|
723
|
+
min-width: 100% !important;
|
724
|
+
max-width: 100% !important;
|
725
|
+
margin: 0;
|
726
|
+
border-radius: 0;
|
727
|
+
}
|
728
|
+
}
|
729
|
+
}
|
730
|
+
</style>
|