Browse Source

feat: FlowChart 流程图组件 & test

lanceJiang 1 year ago
parent
commit
121d3e9494

+ 154 - 0
src/components/FlowChart/FlowChartToolbar.vue

@@ -0,0 +1,154 @@
+<template>
+	<div class="le-flow-chart_toolbar">
+		<template v-for="item in toolbarItemList" :key="item.type">
+			<el-tooltip placement="bottom" v-bind="item.disabled ? { visible: false } : {}">
+				<template #content>{{ item.tooltip }}</template>
+				<span v-if="item.icon" class="le-flow-chart_toolbar__icon" @click="onControl(item)">
+					<PickerIcon :icon-class="item.icon" :class="item.disabled ? 'cursor-not-allowed disabled' : 'cursor-pointer'"/>
+<!--					<LeIcon :icon="item.icon" :class="item.disabled ? 'cursor-not-allowed disabled' : 'cursor-pointer'" />-->
+				</span>
+			</el-tooltip>
+		</template>
+	</div>
+</template>
+<script lang="ts" setup>
+import { ToolbarConfig, ToolbarTypeEnum } from './types'
+import { ref, onUnmounted, unref, nextTick, watchEffect } from 'vue'
+import { useFlowChartContext } from './useFlowContext'
+import PickerIcon from '@/components/IconPicker/PickerIcon.vue'
+defineOptions({ name: 'FlowChartToolbar' })
+
+defineProps({})
+
+const emit = defineEmits(['view-data'])
+
+const toolbarItemList = ref<ToolbarConfig[]>([
+	{
+		type: ToolbarTypeEnum.ZOOM_IN,
+		icon: 'zoom-out',
+		tooltip: '缩小'
+	},
+	{
+		type: ToolbarTypeEnum.ZOOM_OUT,
+		icon: 'zoom-in',
+		tooltip: '放大'
+	},
+	{
+		type: ToolbarTypeEnum.RESET_ZOOM,
+		icon: 'le-suoxiao1',
+		tooltip: '重置比例'
+	},
+	{ separate: true },
+	{
+		type: ToolbarTypeEnum.UNDO,
+		icon: 'DArrowLeft',
+		tooltip: '后退',
+		disabled: true
+	},
+	{
+		type: ToolbarTypeEnum.REDO,
+		icon: 'DArrowRight',
+		tooltip: '前进',
+		disabled: true
+	},
+	{ separate: true },
+	{
+		type: ToolbarTypeEnum.SNAPSHOT,
+		icon: 'Download',
+		tooltip: '下载'
+	},
+	{
+		type: ToolbarTypeEnum.VIEW_DATA,
+		icon: 'Document',
+		tooltip: '查看数据'
+	}
+])
+
+const { logicFlow } = useFlowChartContext()
+
+function onHistoryChange({ data: { undoAble, redoAble } }) {
+	const itemsList = unref(toolbarItemList)
+	const undoIndex = itemsList.findIndex(item => item.type === ToolbarTypeEnum.UNDO)
+	const redoIndex = itemsList.findIndex(item => item.type === ToolbarTypeEnum.REDO)
+	if (undoIndex !== -1) {
+		unref(toolbarItemList)[undoIndex].disabled = !undoAble
+	}
+	if (redoIndex !== -1) {
+		unref(toolbarItemList)[redoIndex].disabled = !redoAble
+	}
+}
+
+const onControl = item => {
+	const lf = unref(logicFlow)
+	if (!lf) {
+		return
+	}
+	switch (item.type) {
+		case ToolbarTypeEnum.ZOOM_IN:
+			lf.zoom()
+			break
+		case ToolbarTypeEnum.ZOOM_OUT:
+			lf.zoom(true)
+			break
+		case ToolbarTypeEnum.RESET_ZOOM:
+			lf.resetZoom()
+			break
+		case ToolbarTypeEnum.UNDO:
+			lf.undo()
+			break
+		case ToolbarTypeEnum.REDO:
+			lf.redo()
+			break
+		case ToolbarTypeEnum.SNAPSHOT:
+			lf.getSnapshot()
+			break
+		case ToolbarTypeEnum.VIEW_DATA:
+			emit('view-data')
+			break
+	}
+}
+
+watchEffect(async () => {
+	if (unref(logicFlow)) {
+		await nextTick()
+		unref(logicFlow)?.on('history:change', onHistoryChange)
+	}
+})
+
+onUnmounted(() => {
+	unref(logicFlow)?.off('history:change', onHistoryChange)
+})
+</script>
+<style lang="scss">
+//@prefix-cls: ~'@{namespace}-flow-chart_toolbar';
+.#{$prefix}flow-chart_toolbar {
+	display: flex;
+	align-items: center;
+	padding: 0.25rem 0.5rem;
+	//padding-bottom: 0.25rem;
+	height: 36px;
+	border-bottom: 1px solid var(--el-border-color);
+	background-color: var(--el-bg-color-page);
+
+	.disabeld {
+		color: var(--el-text-color-disabled);
+	}
+
+	&__icon {
+		display: inline-block;
+		margin-right: 10px;
+		padding: 2px 4px;
+		font-size: 14px;
+		color: var(--el-text-color-primary);
+		cursor: pointer;
+		&:hover {
+			color: var(--el-color-primary);
+		}
+	}
+}
+/*html[data-theme='dark'] {
+	.lf-dnd {
+		background: #080808;
+	}
+}*/
+</style>

+ 75 - 0
src/components/FlowChart/adpterForTurbo.ts

@@ -0,0 +1,75 @@
+const TurboType = {
+	SEQUENCE_FLOW: 1,
+	START_EVENT: 2,
+	END_EVENT: 3,
+	USER_TASK: 4,
+	SERVICE_TASK: 5,
+	EXCLUSIVE_GATEWAY: 6
+}
+
+function convertFlowElementToEdge(element: any) {
+	const { incoming, outgoing, properties, key } = element
+	const { text, startPoint, endPoint, pointsList, logicFlowType } = properties
+	const edge = {
+		id: key,
+		type: logicFlowType,
+		sourceNodeId: incoming[0],
+		targetNodeId: outgoing[0],
+		text,
+		startPoint,
+		endPoint,
+		pointsList,
+		properties: {} as any
+	}
+	const excludeProperties = ['startPoint', 'endPoint', 'pointsList', 'text', 'logicFlowType']
+	Object.keys(element.properties).forEach(property => {
+		if (excludeProperties.indexOf(property) === -1) {
+			edge.properties[property] = element.properties[property]
+		}
+	})
+	return edge
+}
+
+function convertFlowElementToNode(element: any) {
+	const { properties, key } = element
+	const { x, y, text, logicFlowType } = properties
+	const node = {
+		id: key,
+		type: logicFlowType,
+		x,
+		y,
+		text,
+		properties: {} as any
+	}
+	const excludeProperties = ['x', 'y', 'text', 'logicFlowType']
+	Object.keys(element.properties).forEach(property => {
+		if (excludeProperties.indexOf(property) === -1) {
+			node.properties[property] = element.properties[property]
+		}
+	})
+	return node
+}
+
+export function toLogicFlowData(data: any) {
+	const lfData: {
+		// TODO type
+		nodes: any[]
+		edges: any[]
+	} = {
+		nodes: [],
+		edges: []
+	}
+	const list = data.flowElementList
+	list &&
+		list.length > 0 &&
+		list.forEach((element: any) => {
+			if (element.type === TurboType.SEQUENCE_FLOW) {
+				const edge = convertFlowElementToEdge(element)
+				lfData.edges.push(edge)
+			} else {
+				const node = convertFlowElementToNode(element)
+				lfData.nodes.push(node)
+			}
+		})
+	return lfData
+}

+ 96 - 0
src/components/FlowChart/config.ts

@@ -0,0 +1,96 @@
+export const nodeList = [
+	{
+		text: '开始',
+		type: 'start',
+		class: 'node-start'
+	},
+	{
+		text: '矩形',
+		type: 'rect',
+		class: 'node-rect'
+	},
+	{
+		type: 'user',
+		text: '用户',
+		class: 'node-user'
+	},
+	{
+		type: 'push',
+		text: '推送',
+		class: 'node-push'
+	},
+	{
+		type: 'download',
+		text: '位置',
+		class: 'node-download'
+	},
+	{
+		type: 'end',
+		text: '结束',
+		class: 'node-end'
+	}
+]
+
+export const BpmnNode = [
+	{
+		type: 'bpmn:startEvent',
+		text: '开始',
+		class: 'bpmn-start'
+	},
+	{
+		type: 'bpmn:endEvent',
+		text: '结束',
+		class: 'bpmn-end'
+	},
+	{
+		type: 'bpmn:exclusiveGateway',
+		text: '网关',
+		class: 'bpmn-exclusiveGateway'
+	},
+	{
+		type: 'bpmn:userTask',
+		text: '用户',
+		class: 'bpmn-user'
+	}
+]
+
+export function configDefaultDndPanel(lf: any) {
+	return [
+		{
+			text: '选区',
+			icon: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAAH6ji2bAAAABGdBTUEAALGPC/xhBQAAAOVJREFUOBGtVMENwzAIjKP++2026ETdpv10iy7WFbqFyyW6GBywLCv5gI+Dw2Bluj1znuSjhb99Gkn6QILDY2imo60p8nsnc9bEo3+QJ+AKHfMdZHnl78wyTnyHZD53Zzx73MRSgYvnqgCUHj6gwdck7Zsp1VOrz0Uz8NbKunzAW+Gu4fYW28bUYutYlzSa7B84Fh7d1kjLwhcSdYAYrdkMQVpsBr5XgDGuXwQfQr0y9zwLda+DUYXLaGKdd2ZTtvbolaO87pdo24hP7ov16N0zArH1ur3iwJpXxm+v7oAJNR4JEP8DoAuSFEkYH7cAAAAASUVORK5CYII=',
+			callback: () => {
+				lf.updateEditConfig({
+					stopMoveGraph: true
+				})
+			}
+		},
+		{
+			type: 'circle',
+			text: '开始',
+			icon: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAAH6ji2bAAAABGdBTUEAALGPC/xhBQAAAnBJREFUOBGdVL1rU1EcPfdGBddmaZLiEhdx1MHZQXApraCzQ7GKLgoRBxMfcRELuihWKcXFRcEWF8HBf0DdDCKYRZpnl7p0svLe9Zzbd29eQhTbC8nv+9zf130AT63jvooOGS8Vf9Nt5zxba7sXQwODfkWpkbjTQfCGUd9gIp3uuPP8bZ946g56dYQvnBg+b1HB8VIQmMFrazKcKSvFW2dQTxJnJdQ77urmXWOMBCmXM2Rke4S7UAW+/8ywwFoewmBps2tu7mbTdp8VMOkIRAkKfrVawalJTtIliclFbaOBqa0M2xImHeVIfd/nKAfVq/LGnPss5Kh00VEdSzfwnBXPUpmykNss4lUI9C1ga+8PNrBD5YeqRY2Zz8PhjooIbfJXjowvQJBqkmEkVnktWhwu2SM7SMx7Cj0N9IC0oQXRo8xwAGzQms+xrB/nNSUWVveI48ayrFGyC2+E2C+aWrZHXvOuz+CiV6iycWe1Rd1Q6+QUG07nb5SbPrL4426d+9E1axKjY3AoRrlEeSQo2Eu0T6BWAAr6COhTcWjRaYfKG5csnvytvUr/WY4rrPMB53Uo7jZRjXaG6/CFfNMaXEu75nG47X+oepU7PKJvvzGDY1YLSKHJrK7vFUwXKkaxwhCW3u+sDFMVrIju54RYYbFKpALZAo7sB6wcKyyrd+aBMryMT2gPyD6GsQoRFkGHr14TthZni9ck0z+Pnmee460mHXbRAypKNy3nuMdrWgVKj8YVV8E7PSzp1BZ9SJnJAsXdryw/h5ctboUVi4AFiCd+lQaYMw5z3LGTBKjLQOeUF35k89f58Vv/tGh+l+PE/wG0rgfIUbZK5AAAAABJRU5ErkJggg=='
+		},
+		{
+			type: 'rect',
+			text: '用户任务',
+			icon: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABMAAAATCAYAAAEFVwZaAAAABGdBTUEAALGPC/xhBQAAAqlJREFUOBF9VM9rE0EUfrMJNUKLihGbpLGtaCOIR8VjQMGDePCgCCIiCNqzCAp2MyYUCXhUtF5E0D+g1t48qAd7CCLqQUQKEWkStcEfVGlLdp/fm3aW2QQdyLzf33zz5m2IsAZ9XhDpyaaIZkTS4ASzK41TFao88GuJ3hsr2pAbipHxuSYyKRugagICGANkfFnNh3HeE2N0b3nN2cgnpcictw5veJIzxmDamSlxxQZicq/mflxhbaH8BLRbuRwNtZp0JAhoplVRUdzmCe/vO27wFuuA3S5qXruGdboy5/PRGFsbFGKo/haRtQHIrM83bVeTrOgNhZReWaYGnE4aUQgTJNvijJFF4jQ8BxJE5xfKatZWmZcTQ+BVgh7s8SgPlCkcec4mGTmieTP4xd7PcpIEg1TX6gdeLW8rTVMVLVvb7ctXoH0Cydl2QOPJBG21STE5OsnbweVYzAnD3A7PVILuY0yiiyDwSm2g441r6rMSgp6iK42yqroI2QoXeJVeA+YeZSa47gZdXaZWQKTrG93rukk/l2Al6Kzh5AZEl7dDQy+JjgFahQjRopSxPbrbvK7GRe9ePWBo1wcU7sYrFZtavXALwGw/7Dnc50urrHJuTPSoO2IMV3gUQGNg87IbSOIY9BpiT9HV7FCZ94nPXb3MSnwHn/FFFE1vG6DTby+r31KAkUktB3Qf6ikUPWxW1BkXSPQeMHHiW0+HAd2GelJsZz1OJegCxqzl+CLVHa/IibuHeJ1HAKzhuDR+ymNaRFM+4jU6UWKXorRmbyqkq/D76FffevwdCp+jN3UAN/C9JRVTDuOxC/oh+EdMnqIOrlYteKSfadVRGLJFJPSB/ti/6K8f0CNymg/iH2gO/f0DwE0yjAFO6l8JaR5j0VPwPwfaYHqOqrCI319WzwhwzNW/aQAAAABJRU5ErkJggg==',
+			cls: 'important-node'
+		},
+		{
+			type: 'rect',
+			text: '系统任务',
+			icon: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABMAAAATCAYAAAEFVwZaAAAABGdBTUEAALGPC/xhBQAAAqlJREFUOBF9VM9rE0EUfrMJNUKLihGbpLGtaCOIR8VjQMGDePCgCCIiCNqzCAp2MyYUCXhUtF5E0D+g1t48qAd7CCLqQUQKEWkStcEfVGlLdp/fm3aW2QQdyLzf33zz5m2IsAZ9XhDpyaaIZkTS4ASzK41TFao88GuJ3hsr2pAbipHxuSYyKRugagICGANkfFnNh3HeE2N0b3nN2cgnpcictw5veJIzxmDamSlxxQZicq/mflxhbaH8BLRbuRwNtZp0JAhoplVRUdzmCe/vO27wFuuA3S5qXruGdboy5/PRGFsbFGKo/haRtQHIrM83bVeTrOgNhZReWaYGnE4aUQgTJNvijJFF4jQ8BxJE5xfKatZWmZcTQ+BVgh7s8SgPlCkcec4mGTmieTP4xd7PcpIEg1TX6gdeLW8rTVMVLVvb7ctXoH0Cydl2QOPJBG21STE5OsnbweVYzAnD3A7PVILuY0yiiyDwSm2g441r6rMSgp6iK42yqroI2QoXeJVeA+YeZSa47gZdXaZWQKTrG93rukk/l2Al6Kzh5AZEl7dDQy+JjgFahQjRopSxPbrbvK7GRe9ePWBo1wcU7sYrFZtavXALwGw/7Dnc50urrHJuTPSoO2IMV3gUQGNg87IbSOIY9BpiT9HV7FCZ94nPXb3MSnwHn/FFFE1vG6DTby+r31KAkUktB3Qf6ikUPWxW1BkXSPQeMHHiW0+HAd2GelJsZz1OJegCxqzl+CLVHa/IibuHeJ1HAKzhuDR+ymNaRFM+4jU6UWKXorRmbyqkq/D76FffevwdCp+jN3UAN/C9JRVTDuOxC/oh+EdMnqIOrlYteKSfadVRGLJFJPSB/ti/6K8f0CNymg/iH2gO/f0DwE0yjAFO6l8JaR5j0VPwPwfaYHqOqrCI319WzwhwzNW/aQAAAABJRU5ErkJggg==',
+			cls: 'import_icon'
+		},
+		{
+			type: 'diamond',
+			text: '条件判断',
+			icon: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABUAAAAVCAYAAAHeEJUAAAAABGdBTUEAALGPC/xhBQAAAvVJREFUOBGNVEFrE0EU/mY3bQoiFlOkaUJrQUQoWMGePLX24EH0IIoHKQiCV0G8iE1covgLiqA/QTzVm1JPogc9tIJYFaQtlhQxqYjSpunu+L7JvmUTU3AgmTfvffPNN++9WSA1DO182f6xwILzD5btfAoQmwL5KJEwiQyVbSVZ0IgRyV6PTpIJ81E5ZvqfHQR0HUOBHW4L5Et2kQ6Zf7iAOhTFAA8s0pEP7AXO1uAA52SbqGk6h/6J45LaLhO64ByfcUzM39V7ZiAdS2yCePPEIQYvTUHqM/n7dgQNfBKWPjpF4ISk8q3J4nB11qw6X8l+FsF3EhlkEMfrjIer3wJTLwS2aCNcj4DbGxXTw00JmAuO+Ni6bBxVUCvS5d9aa04+so4pHW5jLTywuXAL7jJ+D06sl82Sgl2JuVBQn498zkc2bGKxULHjCnSMadBKYDYYHAtsby1EQ5lNGrQd4Y3v4Zo0XdGEmDno46yCM9Tk+RiJmUYHS/aXHPNTcjxcbTFna000PFJHIVZ5lFRqRpJWk9/+QtlOUYJj9HG5pVFEU7zqIYDVsw2s+AJaD8wTd2umgSCCyUxgGsS1Y6TBwXQQTFuZaHcd8gAGioE90hlsY+wMcs30RduYtxanjMGal8H5dMW67dmT1JFtYUEe8LiQLRsPZ6IIc7A4J5tqco3T0pnv/4u0kyzrYUq7gASuEyI8VXKvB9Odytv6jS/PNaZBln0nioJG/AVQRZvApOdhjj3Jt8QC8Im09SafwdBdvIpztpxWxpeKCC+EsFdS8DCyuCn2munFpL7ctHKp+Xc5cMybeIyMAN33SPL3ZR9QV1XVwLyzHm6Iv0/yeUuUb7PPlZC4D4HZkeu6dpF4v9j9MreGtMbxMMRLIcjJic9yHi7WQ3yVKzZVWUr5UrViJvn1FfUlwe/KYVfYyWRLSGNu16hR01U9IacajXPei0wx/5BqgInvJN+MMNtNme7ReU9SBbgntovn0kKHpFg7UogZvaZiOue/q1SBo9ktHzQAAAAASUVORK5CYII='
+		},
+		{
+			type: 'circle',
+			text: '结束',
+			icon: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAAH6ji2bAAAABGdBTUEAALGPC/xhBQAAA1BJREFUOBFtVE1IVUEYPXOf+tq40Y3vPcmFIdSjIorWoRG0ERWUgnb5FwVhYQSl72oUoZAboxKNFtWiwKRN0M+jpfSzqJAQclHo001tKkjl3emc8V69igP3znzfnO/M9zcDcKT67azmjYWTwl9Vn7Vumeqzj1DVb6cleQY4oAVnIOPb+mKAGxQmKI5CWNJ2aLPatxWa3aB9K7/fB+/Z0jUF6TmMlFLQqrkECWQzOZxYGjTlOl8eeKaIY5yHnFn486xBustDjWT6dG7pmjHOJd+33t0iitTPkK6tEvjxq4h2MozQ6WFSX/LkDUGfFwfhEZj1Auz/U4pyAi5Sznd7uKzznXeVHlI/Aywmk6j7fsUsEuCGADrWARXXwjxWQsUbIupDHJI7kF5dRktg0eN81IbiZXiTESic50iwS+t1oJgL83jAiBupLDCQqwziaWSoAFSeIR3P5Xv5az00wyIn35QRYTwdSYbz8pH8fxUUAtxnFvYmEmgI0wYXUXcCCSpeEVpXlsRhBnCEATxWylL9+EKCAYhe1NGstUa6356kS9NVvt3DU2fd+Wtbm/+lSbylJqsqkSm9CRhvoJVlvKPvF1RKY/FcPn5j4UfIMLn8D4UYb54BNsilTDXKnF4CfTobA0FpoW/LSp306wkXM+XaOJhZaFkcNM82ASNAWMrhrUbRfmyeI1FvRBTpN06WKxa9BK0o2E4Pd3zfBBEwPsv9sQBnmLVbLEIZ/Xe9LYwJu/Er17W6HYVBc7vmuk0xUQ+pqxdom5Fnp55SiytXLPYoMXNM4u4SNSCFWnrVIzKG3EGyMXo6n/BQOe+bX3FClY4PwydVhthOZ9NnS+ntiLh0fxtlUJHAuGaFoVmttpVMeum0p3WEXbcll94l1wM/gZ0Ccczop77VvN2I7TlsZCsuXf1WHvWEhjO8DPtyOVg2/mvK9QqboEth+7pD6NUQC1HN/TwvydGBARi9MZSzLE4b8Ru3XhX2PBxf8E1er2A6516o0w4sIA+lwURhAON82Kwe2iDAC1Watq4XHaGQ7skLcFOtI5lDxuM2gZe6WFIotPAhbaeYlU4to5cuarF1QrcZ/lwrLaCJl66JBocYZnrNlvm2+MBCTmUymPrYZVbjdlr/BxlMjmNmNI3SAAAAAElFTkSuQmCC'
+		}
+	]
+}

+ 158 - 0
src/components/FlowChart/index.vue

@@ -0,0 +1,158 @@
+<template>
+	<div class="le-flow-chart">
+		<FlowChartToolbar v-if="toolbar" @view-data="handlePreview" />
+		<div ref="lfElRef" class="chart-ref"></div>
+		<el-dialog v-model="visible" class="le-dialog" title="流程数据" width="50%">
+			<div class="test">{{ graphData }}</div>
+			<!--			<JsonPreview :data="graphData" />-->
+		</el-dialog>
+	</div>
+</template>
+<script lang="ts" setup>
+import type { Ref, PropType } from 'vue'
+import type { Definition } from '@logicflow/core'
+import { ref, onMounted, unref, nextTick, computed, watch } from 'vue'
+import FlowChartToolbar from './FlowChartToolbar.vue'
+import LogicFlow from '@logicflow/core'
+import { Snapshot, BpmnElement, Menu, DndPanel, SelectionSelect } from '@logicflow/extension'
+import { createFlowChartContext } from './useFlowContext'
+import { toLogicFlowData } from './adpterForTurbo'
+// import { JsonPreview } from '@/components/CodeEditor'
+import { configDefaultDndPanel } from './config'
+import '@logicflow/core/dist/style/index.css'
+import '@logicflow/extension/lib/style/index.css'
+
+defineOptions({ name: 'LeFlowChart' })
+
+const props = defineProps({
+	flowOptions: {
+		type: Object as PropType<Definition>,
+		default: () => ({})
+	},
+
+	data: {
+		type: Object as PropType<any>,
+		default: () => ({})
+	},
+
+	toolbar: {
+		type: Boolean,
+		default: true
+	},
+	patternItems: {
+		type: Array
+	}
+})
+
+const lfElRef = ref(null)
+const graphData = ref({})
+
+const lfInstance = ref(null) as Ref<LogicFlow | null>
+const visible = ref(false)
+// const appStore = useAppStore()
+createFlowChartContext({
+	logicFlow: lfInstance as unknown as LogicFlow
+})
+
+const getFlowOptions = computed(() => {
+	const { flowOptions } = props
+
+	const defaultOptions: Partial<Definition> = {
+		grid: true,
+		background: {
+			// color: appStore.getDarkMode === 'light' ? '#f7f9ff' : '#151515'
+			color: '#f7f9ff'
+		},
+		keyboard: {
+			enabled: true
+		},
+		...flowOptions
+	}
+	return defaultOptions as Definition
+})
+
+watch(
+	() => props.data,
+	() => {
+		onRender()
+	}
+)
+
+// TODO
+// watch(
+//   () => appStore.getDarkMode,
+//   () => {
+//     init();
+//   }
+// );
+
+watch(
+	() => unref(getFlowOptions),
+	options => {
+		unref(lfInstance)?.updateEditConfig(options)
+	}
+)
+
+// init logicFlow
+async function init() {
+	await nextTick()
+
+	const lfEl = unref(lfElRef)
+	if (!lfEl) {
+		return
+	}
+	LogicFlow.use(DndPanel)
+
+	// Canvas configuration
+	LogicFlow.use(Snapshot)
+	// Use the bpmn plug-in to introduce bpmn elements, which can be used after conversion in turbo
+	LogicFlow.use(BpmnElement)
+	// Start the right-click menu
+	LogicFlow.use(Menu)
+	LogicFlow.use(SelectionSelect)
+
+	lfInstance.value = new LogicFlow({
+		...unref(getFlowOptions),
+		container: lfEl
+	})
+	const lf = unref(lfInstance)!
+	lf?.setDefaultEdgeType('line')
+	onRender()
+	lf?.setPatternItems(props.patternItems || configDefaultDndPanel(lf))
+}
+
+async function onRender() {
+	await nextTick()
+	const lf = unref(lfInstance)
+	if (!lf) {
+		return
+	}
+	const lFData = toLogicFlowData(props.data)
+	lf.render(lFData)
+}
+
+function handlePreview() {
+	const lf = unref(lfInstance)
+	if (!lf) {
+		return
+	}
+	const data = unref(lf).getGraphData()
+	graphData.value = data
+	console.error(data, 'data')
+	visible.value = true
+}
+
+onMounted(init)
+</script>
+<style lang="scss">
+.#{$prefix}flow-chart {
+	height: 100%;
+	.chart-ref {
+		height: 100%;
+	}
+	&_toolbar {
+		&__icon {
+		}
+	}
+}
+</style>

+ 24 - 0
src/components/FlowChart/types.ts

@@ -0,0 +1,24 @@
+import { NodeConfig } from '@logicflow/core'
+export enum ToolbarTypeEnum {
+	ZOOM_IN = 'zoomIn',
+	ZOOM_OUT = 'zoomOut',
+	RESET_ZOOM = 'resetZoom',
+
+	UNDO = 'undo',
+	REDO = 'redo',
+
+	SNAPSHOT = 'snapshot',
+	VIEW_DATA = 'viewData'
+}
+
+export interface NodeItem extends NodeConfig {
+	icon: string
+}
+
+export interface ToolbarConfig {
+	type?: string | ToolbarTypeEnum
+	tooltip?: string | boolean
+	icon?: string
+	disabled?: boolean
+	separate?: boolean
+}

+ 17 - 0
src/components/FlowChart/useFlowContext.ts

@@ -0,0 +1,17 @@
+import type LogicFlow from '@logicflow/core'
+
+import { provide, inject } from 'vue'
+
+const key = Symbol('flow-chart')
+
+type Instance = {
+	logicFlow: LogicFlow
+}
+
+export function createFlowChartContext(instance: Instance) {
+	provide(key, instance)
+}
+
+export function useFlowChartContext(): Instance {
+	return inject(key) as Instance
+}

+ 1 - 1
src/components/Icon.vue

@@ -4,7 +4,7 @@
 	</svg>
 </template>
 
-<script lang="ts" setup>
+<script lang="ts" setup name="LeIcon">
 defineOptions({ name: 'LeIcon' })
 const props = defineProps({
 	iconClass: {

+ 240 - 0
src/views/approve/pendingApproval/dataTurbo.json

@@ -0,0 +1,240 @@
+{
+  "flowElementList": [
+    {
+      "incoming": [],
+      "outgoing": ["Flow_33inf2k"],
+      "dockers": [],
+      "type": 2,
+      "properties": {
+        "a": "efrwe",
+        "b": "wewe",
+        "name": "开始",
+        "x": 280,
+        "y": 200,
+        "text": {
+          "x": 280,
+          "y": 200,
+          "value": "开始"
+        },
+        "logicFlowType": "bpmn:startEvent"
+      },
+      "key": "Event_1d42u4p"
+    },
+    {
+      "incoming": ["Flow_379e0o9"],
+      "outgoing": [],
+      "dockers": [],
+      "type": 3,
+      "properties": {
+        "a": "efrwe",
+        "b": "wewe",
+        "name": "结束",
+        "x": 920,
+        "y": 200,
+        "text": {
+          "x": 920,
+          "y": 200,
+          "value": "结束"
+        },
+        "logicFlowType": "bpmn:endEvent"
+      },
+      "key": "Event_08p8i6q"
+    },
+    {
+      "incoming": ["Flow_0pfouf0"],
+      "outgoing": ["Flow_3918lhh"],
+      "dockers": [],
+      "type": 6,
+      "properties": {
+        "a": "efrwe",
+        "b": "wewe",
+        "name": "网关",
+        "x": 580,
+        "y": 200,
+        "text": {
+          "x": 580,
+          "y": 200,
+          "value": "网关"
+        },
+        "logicFlowType": "bpmn:exclusiveGateway"
+      },
+      "key": "Gateway_1fngqgj"
+    },
+    {
+      "incoming": ["Flow_33inf2k"],
+      "outgoing": ["Flow_0pfouf0"],
+      "dockers": [],
+      "type": 4,
+      "properties": {
+        "a": "efrwe",
+        "b": "wewe",
+        "name": "用户",
+        "x": 420,
+        "y": 200,
+        "text": {
+          "x": 420,
+          "y": 200,
+          "value": "用户"
+        },
+        "logicFlowType": "bpmn:userTask"
+      },
+      "key": "Activity_2mgtaia"
+    },
+    {
+      "incoming": ["Flow_3918lhh"],
+      "outgoing": ["Flow_379e0o9"],
+      "dockers": [],
+      "type": 5,
+      "properties": {
+        "a": "efrwe",
+        "b": "wewe",
+        "name": "服务",
+        "x": 760,
+        "y": 200,
+        "text": {
+          "x": 760,
+          "y": 200,
+          "value": "服务"
+        },
+        "logicFlowType": "bpmn:serviceTask"
+      },
+      "key": "Activity_1sp8qc8"
+    },
+    {
+      "incoming": ["Event_1d42u4p"],
+      "outgoing": ["Activity_2mgtaia"],
+      "type": 1,
+      "dockers": [],
+      "properties": {
+        "name": "边",
+        "text": {
+          "x": 331,
+          "y": 200,
+          "value": "边"
+        },
+        "startPoint": {
+          "x": 298,
+          "y": 200
+        },
+        "endPoint": {
+          "x": 370,
+          "y": 200
+        },
+        "pointsList": [
+          {
+            "x": 298,
+            "y": 200
+          },
+          {
+            "x": 370,
+            "y": 200
+          }
+        ],
+        "logicFlowType": "bpmn:sequenceFlow"
+      },
+      "key": "Flow_33inf2k"
+    },
+    {
+      "incoming": ["Activity_2mgtaia"],
+      "outgoing": ["Gateway_1fngqgj"],
+      "type": 1,
+      "dockers": [],
+      "properties": {
+        "name": "边2",
+        "text": {
+          "x": 507,
+          "y": 200,
+          "value": "边2"
+        },
+        "startPoint": {
+          "x": 470,
+          "y": 200
+        },
+        "endPoint": {
+          "x": 555,
+          "y": 200
+        },
+        "pointsList": [
+          {
+            "x": 470,
+            "y": 200
+          },
+          {
+            "x": 555,
+            "y": 200
+          }
+        ],
+        "logicFlowType": "bpmn:sequenceFlow"
+      },
+      "key": "Flow_0pfouf0"
+    },
+    {
+      "incoming": ["Gateway_1fngqgj"],
+      "outgoing": ["Activity_1sp8qc8"],
+      "type": 1,
+      "dockers": [],
+      "properties": {
+        "name": "边3",
+        "text": {
+          "x": 664,
+          "y": 200,
+          "value": "边3"
+        },
+        "startPoint": {
+          "x": 605,
+          "y": 200
+        },
+        "endPoint": {
+          "x": 710,
+          "y": 200
+        },
+        "pointsList": [
+          {
+            "x": 605,
+            "y": 200
+          },
+          {
+            "x": 710,
+            "y": 200
+          }
+        ],
+        "logicFlowType": "bpmn:sequenceFlow"
+      },
+      "key": "Flow_3918lhh"
+    },
+    {
+      "incoming": ["Activity_1sp8qc8"],
+      "outgoing": ["Event_08p8i6q"],
+      "type": 1,
+      "dockers": [],
+      "properties": {
+        "name": "边4",
+        "text": {
+          "x": 871,
+          "y": 200,
+          "value": "边4"
+        },
+        "startPoint": {
+          "x": 810,
+          "y": 200
+        },
+        "endPoint": {
+          "x": 902,
+          "y": 200
+        },
+        "pointsList": [
+          {
+            "x": 810,
+            "y": 200
+          },
+          {
+            "x": 902,
+            "y": 200
+          }
+        ],
+        "logicFlowType": "bpmn:sequenceFlow"
+      },
+      "key": "Flow_379e0o9"
+    }
+  ]
+}

+ 7 - 3
src/views/approve/pendingApproval/index_flowTest.vue → src/views/approve/pendingApproval/index_.vue

@@ -1,13 +1,17 @@
+<!--_flowTest-->
 <template>
-	<div class="test">
-		...
-		<div id="draw-container"></div>
+	<div class="column-page-wrap">
+		<!--		...-->
+		<!--		<div id="draw-container"></div>-->
+		<LeFlowChart :data="demoData" />
 	</div>
 </template>
 
 <script setup lang="tsx">
 import ApprovalIndex from '../components/approvalIndex.vue'
+import LeFlowChart from '@/components/FlowChart'
 import { ref, onMounted } from 'vue'
+import demoData from './dataTurbo.json'
 /**
  * pendingApproval 待审批
  * myApplication 我的申请