|
|
@@ -0,0 +1,113 @@
|
|
|
+<template>
|
|
|
+ <div class="ep-container">
|
|
|
+ <!-- 流程 -->
|
|
|
+ <div class="ep-process" :style="`transform: scale(${zoom / 100});`">
|
|
|
+ <!-- 递归节点 -->
|
|
|
+ <NodeWrap v-if="processData" :node="processData" />
|
|
|
+ <!-- 结束节点 -->
|
|
|
+ <NodeEnd />
|
|
|
+ </div>
|
|
|
+ <!-- 缩放 -->
|
|
|
+ <div class="ep-zoom">
|
|
|
+ <el-icon class="ep-zoom-icon" color="#ffffff" :class="zoom == 50 && 'disabled'" @click="setZoom(-10)">
|
|
|
+ <Minus />
|
|
|
+ </el-icon>
|
|
|
+ <span>{{ zoom }}%</span>
|
|
|
+ <el-icon class="ep-zoom-icon" color="#ffffff" :class="zoom == 300 && 'disabled'" @click="setZoom(10)">
|
|
|
+ <Plus />
|
|
|
+ </el-icon>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script setup lang="ts" name="ProcessDesigner">
|
|
|
+ import { ref, provide, nextTick, watch } from 'vue'
|
|
|
+ import { Minus, Plus } from '@element-plus/icons-vue'
|
|
|
+ import NodeWrap from './core/node-wrap.vue'
|
|
|
+ import NodeEnd from './core/node-end.vue'
|
|
|
+ import { copy } from './common/common.util'
|
|
|
+ import { Validator } from './common/validator.util'
|
|
|
+ import { blankProcessData, ProcessConstant } from './common/data'
|
|
|
+ import { EasyProcess } from './common/data.type'
|
|
|
+
|
|
|
+ const props = defineProps<{ data?: EasyProcess.NodeConfig }>()
|
|
|
+
|
|
|
+ const processData = ref<EasyProcess.NodeConfig>(props.data || blankProcessData)
|
|
|
+ provide(ProcessConstant.KEY_PROCESS_DATA, processData)
|
|
|
+
|
|
|
+ // 初始化流程数据
|
|
|
+ const initProcess = (data?: EasyProcess.NodeConfig) => {
|
|
|
+ processData.value = copy(data || blankProcessData)
|
|
|
+ nextTick(() => validate())
|
|
|
+ }
|
|
|
+ watch(
|
|
|
+ () => props.data,
|
|
|
+ (val) => {
|
|
|
+ if (val && Object.keys(val).length) {
|
|
|
+ initProcess(val)
|
|
|
+ }
|
|
|
+ },
|
|
|
+ { immediate: true, deep: true }
|
|
|
+ )
|
|
|
+
|
|
|
+ /* ====== 节点数据验证 ====== start */
|
|
|
+ // 注册节点验证器
|
|
|
+ let validator = new Validator()
|
|
|
+ provide(ProcessConstant.KEY_VALIDATOR, validator)
|
|
|
+ // 验证流程节点数据
|
|
|
+ const validate = (callback?: (valid: boolean, messages: string[]) => void) => {
|
|
|
+ let result = validator.validate()
|
|
|
+ if (callback && typeof callback === 'function') {
|
|
|
+ callback(result.valid, result.messages)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ /* ====== 节点数据验证 ====== end */
|
|
|
+
|
|
|
+ /* ====== 缩放 ====== start */
|
|
|
+ const zoom = ref<number>(100)
|
|
|
+ const setZoom = (step: number) => (zoom.value = Math.min(300, Math.max(50, zoom.value + step)))
|
|
|
+ /** ====== 缩放 ====== end */
|
|
|
+
|
|
|
+ // 暴露方法给父组件使用
|
|
|
+ defineExpose({ validate, getResult: () => copy(processData.value) })
|
|
|
+</script>
|
|
|
+<style lang="scss" scoped>
|
|
|
+ .ep-container {
|
|
|
+ position: relative;
|
|
|
+ box-sizing: border-box;
|
|
|
+ display: flex;
|
|
|
+ justify-content: center;
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+ padding: 16px;
|
|
|
+ overflow: auto;
|
|
|
+ background-color: #f5f5f7;
|
|
|
+ }
|
|
|
+
|
|
|
+ .ep-process {
|
|
|
+ position: relative;
|
|
|
+ height: 100%;
|
|
|
+ }
|
|
|
+
|
|
|
+ .ep-zoom {
|
|
|
+ position: absolute;
|
|
|
+ top: 70px;
|
|
|
+ right: 40px;
|
|
|
+ z-index: 10;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: space-between;
|
|
|
+ width: 125px;
|
|
|
+ height: 40px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .ep-zoom-icon {
|
|
|
+ width: 25px;
|
|
|
+ height: 25px;
|
|
|
+ color: #fff;
|
|
|
+ cursor: pointer;
|
|
|
+ background-color: #5a5e66;
|
|
|
+ border: 1px solid #5a5e66;
|
|
|
+ border-radius: 5px;
|
|
|
+ }
|
|
|
+</style>
|