|
@@ -3,6 +3,62 @@
|
|
|
<div class="chart-block">
|
|
|
<div class="chart-ref" ref="ref_chart"></div>
|
|
|
</div>
|
|
|
+ <div class="tools">
|
|
|
+ <div class="mini-map" ref="ref_miniMap"/>
|
|
|
+ <div class="operations">
|
|
|
+ <div class="zoom">
|
|
|
+ <el-tooltip content="缩小" effect="light" placement="top" :show-arrow="false">
|
|
|
+ <div class="__hover-bg" :class="{__disabled: state.zoom <= 0.25}" @click="graphZoom(state.zoom - 0.1)">
|
|
|
+ <SvgIcon name="zoom-"/>
|
|
|
+ </div>
|
|
|
+ </el-tooltip>
|
|
|
+ <el-dropdown :teleported="false" :popper-options="{
|
|
|
+ modifiers: [
|
|
|
+ {
|
|
|
+ name: 'offset',
|
|
|
+ options: {
|
|
|
+ offset: [0, 20],
|
|
|
+ },
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: 'arrow',
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ }">
|
|
|
+ <div class="__hover-bg px-0.5">{{(state.zoom * 100).toFixed(0)}}%</div>
|
|
|
+ <template #dropdown>
|
|
|
+ <el-dropdown-menu>
|
|
|
+ <el-dropdown-item @click="graphZoom(2)">200%</el-dropdown-item>
|
|
|
+ <el-dropdown-item @click="graphZoom(1)">100%</el-dropdown-item>
|
|
|
+ <el-dropdown-item @click="graphZoom(0.75)">75%</el-dropdown-item>
|
|
|
+ <el-dropdown-item @click="graphZoom(0.5)">50%</el-dropdown-item>
|
|
|
+ <el-dropdown-item divided @click="graphZoom(0)">自适应视图</el-dropdown-item>
|
|
|
+ </el-dropdown-menu>
|
|
|
+ </template>
|
|
|
+ </el-dropdown>
|
|
|
+ <el-tooltip content="放大" effect="light" placement="top" :show-arrow="false">
|
|
|
+ <div class="__hover-bg" :class="{__disabled: state.zoom >= 2}" @click="graphZoom(state.zoom + 0.1)">
|
|
|
+ <SvgIcon name="zoom+"/>
|
|
|
+ </div>
|
|
|
+ </el-tooltip>
|
|
|
+ </div>
|
|
|
+ <div class="history">
|
|
|
+ <el-tooltip content="撤销" effect="light" placement="top" :show-arrow="false">
|
|
|
+ <div class="__hover-bg" :class="{__disabled: !state.history.canUndo}" @click="onUndo">
|
|
|
+ <SvgIcon name="back"/>
|
|
|
+ </div>
|
|
|
+ </el-tooltip>
|
|
|
+ <el-tooltip content="重做" effect="light" placement="top" :show-arrow="false">
|
|
|
+ <div class="__hover-bg" :class="{__disabled: !state.history.canRedo}" @click="onRedo">
|
|
|
+ <SvgIcon name="back" rotate="180"/>
|
|
|
+ </div>
|
|
|
+ </el-tooltip>
|
|
|
+<!-- <div class="__hover-bg">-->
|
|
|
+<!-- <SvgIcon name="history"/>-->
|
|
|
+<!-- </div>-->
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
</template>
|
|
|
|
|
@@ -18,6 +74,8 @@ import {Snapline} from '@antv/x6-plugin-snapline'
|
|
|
import {ContextMenuTool} from "./context-menu-tool";
|
|
|
import {useWorkflowStore} from "@/stores/modules/workflow";
|
|
|
import {NodeType} from "@/views/workflow/types";
|
|
|
+import { MiniMap } from '@antv/x6-plugin-minimap'
|
|
|
+import { History } from '@antv/x6-plugin-history'
|
|
|
|
|
|
register({
|
|
|
shape: 'workflow-node',
|
|
@@ -100,15 +158,28 @@ const {proxy}: any = getCurrentInstance()
|
|
|
const state: any = reactive({
|
|
|
graph: null,
|
|
|
isPort: false,
|
|
|
- isInitEdges: false
|
|
|
+ isInitEdges: false,
|
|
|
+ zoom: 1,
|
|
|
+ history: {
|
|
|
+ isInit: false,
|
|
|
+ canUndo: false,
|
|
|
+ canRedo: false,
|
|
|
+ }
|
|
|
})
|
|
|
const ref_chart = ref()
|
|
|
+const ref_miniMap = ref()
|
|
|
const initChart = () => {
|
|
|
state.graph = new Graph({
|
|
|
container: ref_chart.value,
|
|
|
autoResize: true,
|
|
|
panning: true,
|
|
|
- mousewheel: true,
|
|
|
+ mousewheel: {
|
|
|
+ enabled: true,
|
|
|
+ },
|
|
|
+ scaling: {
|
|
|
+ min: 0.25,
|
|
|
+ max: 2
|
|
|
+ },
|
|
|
grid: {
|
|
|
visible: true,
|
|
|
type: 'doubleMesh',
|
|
@@ -137,13 +208,13 @@ const initChart = () => {
|
|
|
},
|
|
|
allowMulti: 'withPort', // 当设置为 'withPort' 时,在起始和终止节点的相同连接桩之间只允许创建一条边(即,起始和终止节点之间可以创建多条边,但必须要要链接在不同的连接桩上)
|
|
|
highlight: true,
|
|
|
- router: {
|
|
|
- name: 'manhattan',
|
|
|
- args: {
|
|
|
- startDirections: ['right'],
|
|
|
- endDirections: ['left'],
|
|
|
- },
|
|
|
- },
|
|
|
+ // router: {
|
|
|
+ // name: 'manhattan',
|
|
|
+ // args: {
|
|
|
+ // startDirections: ['right'],
|
|
|
+ // endDirections: ['left'],
|
|
|
+ // },
|
|
|
+ // },
|
|
|
connector: 'algo-connector',
|
|
|
// connector: {
|
|
|
// name: 'rounded',
|
|
@@ -168,16 +239,10 @@ const initChart = () => {
|
|
|
}
|
|
|
})
|
|
|
WorkflowStore.init(state.graph)
|
|
|
- initNodes()
|
|
|
- state.graph.zoomToFit({ maxScale: 1 })
|
|
|
- state.graph.centerContent() // 居中显示
|
|
|
- state.graph.use(
|
|
|
- new Snapline({
|
|
|
- enabled: true,
|
|
|
- clean: false,
|
|
|
- }),
|
|
|
- )
|
|
|
+ initPlug()
|
|
|
initWatch()
|
|
|
+ initNodes()
|
|
|
+ graphZoom(0)
|
|
|
}
|
|
|
const initNodes = () => {
|
|
|
props.data.nodes.forEach(v => {
|
|
@@ -198,6 +263,7 @@ const initEdges = () => {
|
|
|
state.graph.addEdge(handleEdge(v))
|
|
|
})
|
|
|
state.isInitEdges = true
|
|
|
+ state.graph.cleanHistory() // 初始化完成后清空历史队列
|
|
|
}
|
|
|
}
|
|
|
const initWatch = () => {
|
|
@@ -214,8 +280,54 @@ const initWatch = () => {
|
|
|
state.isPort = false
|
|
|
}, 100)
|
|
|
})
|
|
|
+ state.graph.on('scale', ({ sx, sy, ox, oy }) => {
|
|
|
+ state.zoom = sx
|
|
|
+ })
|
|
|
+ state.graph.on('history:change', () => {
|
|
|
+ if (state.isInitEdges) {
|
|
|
+ state.history.canRedo = state.graph.canRedo()
|
|
|
+ state.history.canUndo = state.graph.canUndo()
|
|
|
+ }
|
|
|
+ })
|
|
|
+}
|
|
|
+const initPlug = () => {
|
|
|
+ state.graph.use(
|
|
|
+ new Snapline({
|
|
|
+ enabled: true,
|
|
|
+ clean: false,
|
|
|
+ }),
|
|
|
+ )
|
|
|
+ state.graph.use(
|
|
|
+ new MiniMap({
|
|
|
+ container: ref_miniMap.value,
|
|
|
+ width: 100,
|
|
|
+ height: 70,
|
|
|
+ padding: 6
|
|
|
+ }),
|
|
|
+ )
|
|
|
+ state.graph.use(
|
|
|
+ new History({
|
|
|
+ enabled: true,
|
|
|
+ }),
|
|
|
+ )
|
|
|
+}
|
|
|
+const graphZoom = (z) => {
|
|
|
+ if (z) {
|
|
|
+ state.graph.zoomTo(z)
|
|
|
+ } else {
|
|
|
+ state.graph.zoomToFit({ maxScale: 1 })
|
|
|
+ }
|
|
|
+}
|
|
|
+const onUndo = () => {
|
|
|
+ if (state.history.canUndo) {
|
|
|
+ state.graph.undo()
|
|
|
+ }
|
|
|
+}
|
|
|
+const onRedo = () => {
|
|
|
+ if (state.history.canRedo) {
|
|
|
+ state.graph.redo()
|
|
|
+ }
|
|
|
}
|
|
|
-
|
|
|
watch(() => props.data, (n) => {
|
|
|
if (n) {
|
|
|
initChart()
|
|
@@ -233,6 +345,7 @@ defineExpose({
|
|
|
width: 100%;
|
|
|
height: 100%;
|
|
|
background-color: #f2f4f7;
|
|
|
+ position: relative;
|
|
|
.chart-block {
|
|
|
width: 100%;
|
|
|
height: 100%;
|
|
@@ -241,5 +354,40 @@ defineExpose({
|
|
|
height: 100%;
|
|
|
}
|
|
|
}
|
|
|
+ .tools {
|
|
|
+ position: absolute;
|
|
|
+ left: 20px;
|
|
|
+ bottom: 20px;
|
|
|
+ z-index: 2;
|
|
|
+ .mini-map {
|
|
|
+ position: absolute;
|
|
|
+ bottom: calc(100% + 10px);
|
|
|
+ }
|
|
|
+ :deep(.operations) {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ font-size: 14px;
|
|
|
+ color: #676f83;
|
|
|
+ gap: 10px;
|
|
|
+ .el-popper__arrow {
|
|
|
+ display: none;
|
|
|
+ }
|
|
|
+ >div {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ background-color: rgba(255, 255, 255, 0.95);
|
|
|
+ box-shadow: 0 0px 8px rgba(0, 0, 0, 0.1);
|
|
|
+ border-radius: 6px;
|
|
|
+ padding: 4px;
|
|
|
+ >div {
|
|
|
+ min-width: 32px;
|
|
|
+ height: 32px;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
</style>
|