From a1d7e81859f554f3a53680cc35f0f49bf1f77098 Mon Sep 17 00:00:00 2001
From: wwf <1971391498@qq.com>
Date: 星期四, 14 五月 2026 14:37:02 +0800
Subject: [PATCH] 导入项目
---
src/components/SimpleProcessDesignerV2/src/SimpleProcessModel.vue | 265 +++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 265 insertions(+), 0 deletions(-)
diff --git a/src/components/SimpleProcessDesignerV2/src/SimpleProcessModel.vue b/src/components/SimpleProcessDesignerV2/src/SimpleProcessModel.vue
new file mode 100644
index 0000000..a8a0ac6
--- /dev/null
+++ b/src/components/SimpleProcessDesignerV2/src/SimpleProcessModel.vue
@@ -0,0 +1,265 @@
+<template>
+ <div class="simple-process-model-container position-relative">
+ <div class="position-absolute top-0px right-0px bg-#fff z-index-button-group">
+ <el-row type="flex" justify="end">
+ <el-button-group key="scale-control" size="default">
+ <el-button v-if="!readonly" size="default" @click="exportJson">
+ <Icon icon="ep:download" /> 瀵煎嚭
+ </el-button>
+ <el-button v-if="!readonly" size="default" @click="importJson">
+ <Icon icon="ep:upload" />瀵煎叆
+ </el-button>
+ <!-- 鐢ㄤ簬鎵撳紑鏈湴鏂囦欢-->
+ <input
+ v-if="!readonly"
+ type="file"
+ id="files"
+ ref="refFile"
+ style="display: none"
+ accept=".json"
+ @change="importLocalFile"
+ />
+ <el-button size="default" :icon="ScaleToOriginal" @click="processReZoom()" />
+ <el-button size="default" :plain="true" :icon="ZoomOut" @click="zoomOut()" />
+ <el-button size="default" class="w-80px"> {{ scaleValue }}% </el-button>
+ <el-button size="default" :plain="true" :icon="ZoomIn" @click="zoomIn()" />
+ <el-button size="default" @click="resetPosition">閲嶇疆</el-button>
+ </el-button-group>
+ </el-row>
+ </div>
+ <div
+ class="simple-process-model"
+ :style="`transform: translate(${currentX}px, ${currentY}px) scale(${scaleValue / 100});`"
+ @mousedown="startDrag"
+ @mousemove="onDrag"
+ @mouseup="stopDrag"
+ @mouseleave="stopDrag"
+ @mouseenter="setGrabCursor"
+ >
+ <ProcessNodeTree v-if="processNodeTree" v-model:flow-node="processNodeTree" />
+ </div>
+ </div>
+ <Dialog v-model="errorDialogVisible" title="淇濆瓨澶辫触" width="400" :fullscreen="false">
+ <div class="mb-2">浠ヤ笅鑺傜偣鍐呭涓嶅畬鍠勶紝璇蜂慨鏀瑰悗淇濆瓨</div>
+ <div
+ class="mb-3 b-rounded-1 bg-gray-100 p-2 line-height-normal"
+ v-for="(item, index) in errorNodes"
+ :key="index"
+ >
+ {{ item.name }} : {{ NODE_DEFAULT_TEXT.get(item.type) }}
+ </div>
+ <template #footer>
+ <el-button type="primary" @click="errorDialogVisible = false">鐭ラ亾浜�</el-button>
+ </template>
+ </Dialog>
+</template>
+
+<script setup lang="ts">
+import ProcessNodeTree from './ProcessNodeTree.vue'
+import { SimpleFlowNode, NodeType, NODE_DEFAULT_TEXT } from './consts'
+import { useWatchNode } from './node'
+import { ZoomOut, ZoomIn, ScaleToOriginal } from '@element-plus/icons-vue'
+import { isString } from '@/utils/is'
+import download from '@/utils/download'
+
+defineOptions({
+ name: 'SimpleProcessModel'
+})
+
+const props = defineProps({
+ flowNode: {
+ type: Object as () => SimpleFlowNode,
+ required: true
+ },
+ readonly: {
+ type: Boolean,
+ required: false,
+ default: true
+ }
+})
+
+const emits = defineEmits<{
+ save: [node: SimpleFlowNode | undefined]
+}>()
+
+const processNodeTree = useWatchNode(props)
+
+provide('readonly', props.readonly)
+
+// TODO 鍙紭鍖栵細鎷栨嫿鏈夌偣鍗¢】
+/** 鎷栨嫿銆佹斁澶х缉灏忕瓑鎿嶄綔 */
+let scaleValue = ref(100)
+const MAX_SCALE_VALUE = 200
+const MIN_SCALE_VALUE = 50
+const isDragging = ref(false)
+const startX = ref(0)
+const startY = ref(0)
+const currentX = ref(0)
+const currentY = ref(0)
+const initialX = ref(0)
+const initialY = ref(0)
+
+const setGrabCursor = () => {
+ document.body.style.cursor = 'grab'
+}
+
+const resetCursor = () => {
+ document.body.style.cursor = 'default'
+}
+
+const startDrag = (e: MouseEvent) => {
+ isDragging.value = true
+ startX.value = e.clientX - currentX.value
+ startY.value = e.clientY - currentY.value
+ setGrabCursor() // 璁剧疆灏忔墜鍏夋爣
+}
+
+const onDrag = (e: MouseEvent) => {
+ if (!isDragging.value) return
+ e.preventDefault() // 绂佺敤鏂囨湰閫夋嫨
+
+ // 浣跨敤 requestAnimationFrame 浼樺寲鎬ц兘
+ requestAnimationFrame(() => {
+ currentX.value = e.clientX - startX.value
+ currentY.value = e.clientY - startY.value
+ })
+}
+
+const stopDrag = () => {
+ isDragging.value = false
+ resetCursor() // 閲嶇疆鍏夋爣
+}
+
+const zoomIn = () => {
+ if (scaleValue.value == MAX_SCALE_VALUE) {
+ return
+ }
+ scaleValue.value += 10
+}
+
+const zoomOut = () => {
+ if (scaleValue.value == MIN_SCALE_VALUE) {
+ return
+ }
+ scaleValue.value -= 10
+}
+
+const processReZoom = () => {
+ scaleValue.value = 100
+}
+
+const resetPosition = () => {
+ currentX.value = initialX.value
+ currentY.value = initialY.value
+}
+
+/** 鏍¢獙鑺傜偣璁剧疆 */
+const errorDialogVisible = ref(false)
+let errorNodes: SimpleFlowNode[] = []
+
+const validateNode = (node: SimpleFlowNode | undefined, errorNodes: SimpleFlowNode[]) => {
+ if (node) {
+ const { type, showText, conditionNodes } = node
+ if (type == NodeType.END_EVENT_NODE) {
+ return
+ }
+ if (type == NodeType.START_USER_NODE) {
+ // 鍙戣捣浜鸿妭鐐规殏鏃朵笉鐢ㄦ牎楠岋紝鐩存帴鏍¢獙瀛╁瓙鑺傜偣
+ validateNode(node.childNode, errorNodes)
+ }
+
+ if (
+ type === NodeType.USER_TASK_NODE ||
+ type === NodeType.COPY_TASK_NODE ||
+ type === NodeType.CONDITION_NODE
+ ) {
+ if (!showText) {
+ errorNodes.push(node)
+ }
+ validateNode(node.childNode, errorNodes)
+ }
+
+ if (
+ type == NodeType.CONDITION_BRANCH_NODE ||
+ type == NodeType.PARALLEL_BRANCH_NODE ||
+ type == NodeType.INCLUSIVE_BRANCH_NODE
+ ) {
+ // 鍒嗘敮鑺傜偣
+ // 1. 鍏堟牎楠屽悇涓垎鏀�
+ conditionNodes?.forEach((item) => {
+ validateNode(item, errorNodes)
+ })
+ // 2. 鏍¢獙瀛╁瓙鑺傜偣
+ validateNode(node.childNode, errorNodes)
+ }
+ }
+}
+
+/** 鑾峰彇褰撳墠娴佺▼鏁版嵁 */
+const getCurrentFlowData = async () => {
+ try {
+ errorNodes = []
+ validateNode(processNodeTree.value, errorNodes)
+ if (errorNodes.length > 0) {
+ errorDialogVisible.value = true
+ return undefined
+ }
+ return processNodeTree.value
+ } catch (error) {
+ console.error('鑾峰彇娴佺▼鏁版嵁澶辫触:', error)
+ return undefined
+ }
+}
+
+defineExpose({
+ getCurrentFlowData
+})
+
+/** 瀵煎嚭 JSON */
+const exportJson = () => {
+ download.json(new Blob([JSON.stringify(processNodeTree.value)]), 'model.json')
+}
+
+/** 瀵煎叆 JSON */
+const refFile = ref()
+const importJson = () => {
+ refFile.value.click()
+}
+const importLocalFile = () => {
+ const file = refFile.value.files[0]
+ const reader = new FileReader()
+ reader.readAsText(file)
+ reader.onload = function () {
+ if (isString(this.result)) {
+ processNodeTree.value = JSON.parse(this.result)
+ emits('save', processNodeTree.value)
+ }
+ }
+}
+
+// 鍦ㄧ粍浠跺垵濮嬪寲鏃惰褰曞垵濮嬩綅缃�
+onMounted(() => {
+ initialX.value = currentX.value
+ initialY.value = currentY.value
+})
+</script>
+
+<style lang="scss" scoped>
+.simple-process-model-container {
+ width: 100%;
+ height: 100%;
+ position: relative;
+ overflow: hidden;
+ user-select: none; // 绂佺敤鏂囨湰閫夋嫨
+}
+
+.simple-process-model {
+ position: relative; // 纭繚鐩稿瀹氫綅
+ min-width: 100%; // 纭繚瀹藉害涓�100%
+ min-height: 100%; // 纭繚楂樺害涓�100%
+}
+
+.z-index-button-group {
+ z-index: 10;
+}
+</style>
--
Gitblit v1.8.0