<template>
	<div class="workflow-container">
		<div class="workflow-mask" v-if="isShow"></div>
		<div class="layout-view-bg-white flex" :style="{ height: `calc(100vh - ${setViewHeight}` }">
			<div class="workflow">
				<!-- 顶部工具栏 -->
				<Tool @tool="onToolClick" />

				<!-- 左侧导航区 -->
				<div class="workflow-content">
					<div class="workflow-left">
						<el-scrollbar>
							<div
								ref="leftNavRefs"
								v-for="(val, key) in leftNavList"
								:key="val.id"
								:style="{ height: val.isOpen ? 'auto' : '50px', overflow: 'hidden' }"
								class="workflow-left-id"
							>
								<div class="workflow-left-title" @click="onTitleClick(val)">
									<span>{{ val.title }}</span>
									<SvgIcon :name="val.isOpen ? 'ele-ArrowDown' : 'ele-ArrowRight'" />
								</div>
								<div class="workflow-left-item" v-for="(v, k) in val.children" :key="k" :data-name="v.name" :data-icon="v.icon" :data-id="v.id">
									<div class="workflow-left-item-icon">
										<SvgIcon :name="v.icon" class="workflow-icon-drag" />
										<div class="font10 pl5 name">{{ v.name }}</div>
									</div>
								</div>
							</div>
						</el-scrollbar>
					</div>

					<!-- 右侧绘画区 -->
					<div class="workflow-right" ref="workflowRightRef">
						<div
							v-for="(v, k) in jsplumbData.nodeList"
							:key="v.nodeId"
							:id="v.nodeId"
							:data-node-id="v.nodeId"
							:class="v.class"
							:style="{ left: v.left, top: v.top }"
							@click="onItemCloneClick(k)"
							@contextmenu.prevent="onContextmenu(v, k, $event)"
						>
							<div class="workflow-right-box" :class="{ 'workflow-right-active': jsPlumbNodeIndex === k }">
								<div class="workflow-left-item-icon">
									<SvgIcon :name="v.icon" class="workflow-icon-drag" />
									<div class="font10 pl5 name">{{ v.name }}</div>
								</div>
							</div>
						</div>
					</div>
				</div>
			</div>
		</div>

		<!-- 节点右键菜单 -->
		<Contextmenu :dropdown="dropdownNode" ref="contextmenuNodeRef" @current="onCurrentNodeClick" />
		<!-- 线右键菜单 -->
		<Contextmenu :dropdown="dropdownLine" ref="contextmenuLineRef" @current="onCurrentLineClick" />
		<!-- 抽屉表单、线 -->
		<Drawer ref="drawerRef" @label="setLineLabel" @node="setNodeContent" />

		<!-- 顶部工具栏-帮助弹窗 -->
		<Help ref="helpRef" />
	</div>
</template>

<script lang="ts">
import { defineComponent, toRefs, reactive, computed, onMounted, onUnmounted, nextTick, ref } from 'vue';
import { ElMessage, ElMessageBox } from 'element-plus';
import { jsPlumb } from 'jsplumb';
import Sortable from 'sortablejs';
import { storeToRefs } from 'pinia';
import { useThemeConfig } from '/@/stores/themeConfig';
import { useTagsViewRoutes } from '/@/stores/tagsViewRoutes';
import Tool from './component/tool/index.vue';
import Help from './component/tool/help.vue';
import Contextmenu from './component/contextmenu/index.vue';
import Drawer from './component/drawer/index.vue';
import commonFunction from '/@/utils/commonFunction';
import { leftNavList } from './js/mock';
import { jsplumbDefaults, jsplumbMakeSource, jsplumbMakeTarget, jsplumbConnect } from './js/config';

// 定义接口来定义对象的类型
interface NodeListState {
	id: string | number;
	nodeId: string | undefined;
	class: HTMLElement | string;
	left: number | string;
	top: number | string;
	icon: string;
	name: string;
}
interface LineListState {
	sourceId: string;
	targetId: string;
	label: string;
}
interface XyState {
	x: string | number;
	y: string | number;
}
interface WorkflowState {
	workflowRightRef: HTMLDivElement | null;
	leftNavRefs: any[];
	leftNavList: any[];
	dropdownNode: XyState;
	dropdownLine: XyState;
	isShow: boolean;
	jsPlumb: any;
	jsPlumbNodeIndex: null | number;
	jsplumbDefaults: any;
	jsplumbMakeSource: any;
	jsplumbMakeTarget: any;
	jsplumbConnect: any;
	jsplumbData: {
		nodeList: Array<NodeListState>;
		lineList: Array<LineListState>;
	};
}

export default defineComponent({
	name: 'pagesWorkflow',
	components: { Tool, Contextmenu, Drawer, Help },
	setup() {
		const contextmenuNodeRef = ref();
		const contextmenuLineRef = ref();
		const drawerRef = ref();
		const helpRef = ref();
		const stores = useTagsViewRoutes();
		const storesThemeConfig = useThemeConfig();
		const { themeConfig } = storeToRefs(storesThemeConfig);
		const { isTagsViewCurrenFull } = storeToRefs(stores);
		const { copyText } = commonFunction();
		const state = reactive<WorkflowState>({
			workflowRightRef: null as HTMLDivElement | null,
			leftNavRefs: [],
			leftNavList: [],
			dropdownNode: { x: '', y: '' },
			dropdownLine: { x: '', y: '' },
			isShow: false,
			jsPlumb: null,
			jsPlumbNodeIndex: null,
			jsplumbDefaults,
			jsplumbMakeSource,
			jsplumbMakeTarget,
			jsplumbConnect,
			jsplumbData: {
				nodeList: [],
				lineList: [],
			},
		});
		// 设置 view 的高度
		const setViewHeight = computed(() => {
			let { isTagsview } = themeConfig.value;
			if (isTagsViewCurrenFull.value) {
				return `30px`;
			} else {
				if (isTagsview) return `114px`;
				else return `80px`;
			}
		});
		// 设置 宽度小于 768，不支持操
		const setClientWidth = () => {
			const clientWidth = document.body.clientWidth;
			clientWidth < 768 ? (state.isShow = true) : (state.isShow = false);
		};
		// 左侧导航-数据初始化
		const initLeftNavList = () => {
			state.leftNavList = leftNavList;
			state.jsplumbData = {
				nodeList: [
					{ nodeId: 'huej738hbji', left: '148px', top: '93px', class: 'workflow-right-clone', icon: 'iconfont icon-gongju', name: '引擎', id: '11' },
					{
						nodeId: '52kcszzyxrd',
						left: '458px',
						top: '203px',
						class: 'workflow-right-clone',
						icon: 'iconfont icon-shouye_dongtaihui',
						name: '模版',
						id: '12',
					},
					{
						nodeId: 'nltskl6k4me',
						left: '164px',
						top: '350px',
						class: 'workflow-right-clone',
						icon: 'iconfont icon-zhongduancanshuchaxun',
						name: '名称',
						id: '13',
					},
				],
				lineList: [
					{ sourceId: 'huej738hbji', targetId: '52kcszzyxrd', label: '传送' },
					{ sourceId: 'huej738hbji', targetId: 'nltskl6k4me', label: '' },
				],
			};
		};
		// 左侧导航-初始化拖动
		const initSortable = () => {
			state.leftNavRefs.forEach((v) => {
				Sortable.create(v as HTMLDivElement, {
					group: {
						name: 'vue-next-admin-1',
						pull: 'clone',
						put: false,
					},
					animation: 0,
					sort: false,
					draggable: '.workflow-left-item',
					forceFallback: true,
					onEnd: function (evt: any) {
						const { name, icon, id } = evt.clone.dataset;
						const { layerX, layerY, clientX, clientY } = evt.originalEvent;
						const el = state.workflowRightRef!;
						const { x, y, width, height } = el.getBoundingClientRect();
						if (clientX < x || clientX > width + x || clientY < y || y > y + height) {
							ElMessage.warning('请把节点拖入到画布中');
						} else {
							// 节点id（唯一）
							const nodeId = Math.random().toString(36).substr(2, 12);
							// 处理节点数据
							const node = {
								nodeId,
								left: `${layerX - 40}px`,
								top: `${layerY - 15}px`,
								class: 'workflow-right-clone',
								name,
								icon,
								id,
							};
							// 右侧视图内容数组
							state.jsplumbData.nodeList.push(node);
							// 元素加载完毕时
							nextTick(() => {
								// 整个节点作为source或者target
								state.jsPlumb.makeSource(nodeId, state.jsplumbMakeSource);
								// // 整个节点作为source或者target
								state.jsPlumb.makeTarget(nodeId, state.jsplumbMakeTarget, jsplumbConnect);
								// 设置节点可以拖拽（此处为id值，非class）
								state.jsPlumb.draggable(nodeId, {
									containment: 'parent',
									stop: (el: any) => {
										state.jsplumbData.nodeList.forEach((v) => {
											if (v.nodeId === el.el.id) {
												// 节点x, y重新赋值，防止再次从左侧导航中拖拽节点时，x, y恢复默认
												v.left = `${el.pos[0]}px`;
												v.top = `${el.pos[1]}px`;
											}
										});
									},
								});
							});
						}
					},
				});
			});
		};
		// 初始化 jsPlumb
		const initJsPlumb = () => {
			(<any>jsPlumb).ready(() => {
				state.jsPlumb = (<any>jsPlumb).getInstance({
					detachable: false,
					Container: 'workflow-right',
				});
				state.jsPlumb.fire('jsPlumbDemoLoaded', state.jsPlumb);
				// 导入默认配置
				state.jsPlumb.importDefaults(state.jsplumbDefaults);
				// 会使整个jsPlumb立即重绘。
				state.jsPlumb.setSuspendDrawing(false, true);
				// 初始化节点、线的链接
				initJsPlumbConnection();
				// 点击线弹出右键菜单
				state.jsPlumb.bind('contextmenu', (conn: any, originalEvent: MouseEvent) => {
					originalEvent.preventDefault();
					const { sourceId, targetId } = conn;
					const { clientX, clientY } = originalEvent;
					state.dropdownLine.x = clientX;
					state.dropdownLine.y = clientY;
					const v: any = state.jsplumbData.nodeList.find((v) => v.nodeId === targetId);
					const line: any = state.jsplumbData.lineList.find((v) => v.sourceId === sourceId && v.targetId === targetId);
					v.type = 'line';
					v.label = line.label;
					contextmenuLineRef.value.openContextmenu(v, conn);
				});
				// 连线之前
				state.jsPlumb.bind('beforeDrop', (conn: any) => {
					const { sourceId, targetId } = conn;
					const item = state.jsplumbData.lineList.find((v) => v.sourceId === sourceId && v.targetId === targetId);
					if (item) {
						ElMessage.warning('关系已存在，不可重复连接');
						return false;
					} else {
						return true;
					}
				});
				// 连线时
				state.jsPlumb.bind('connection', (conn: any) => {
					const { sourceId, targetId } = conn;
					state.jsplumbData.lineList.push({
						sourceId,
						targetId,
						label: '',
					});
				});
				// 删除连线时回调函数
				state.jsPlumb.bind('connectionDetached', (conn: any) => {
					const { sourceId, targetId } = conn;
					state.jsplumbData.lineList = state.jsplumbData.lineList.filter((line) => {
						if (line.sourceId == sourceId && line.targetId == targetId) {
							return false;
						}
						return true;
					});
				});
			});
		};
		// 初始化节点、线的链接
		const initJsPlumbConnection = () => {
			// 节点
			state.jsplumbData.nodeList.forEach((v) => {
				// 整个节点作为source或者target
				state.jsPlumb.makeSource(v.nodeId, state.jsplumbMakeSource);
				// 整个节点作为source或者target
				state.jsPlumb.makeTarget(v.nodeId, state.jsplumbMakeTarget, jsplumbConnect);
				// 设置节点可以拖拽（此处为id值，非class）
				state.jsPlumb.draggable(v.nodeId, {
					containment: 'parent',
					stop: (el: any) => {
						state.jsplumbData.nodeList.forEach((v) => {
							if (v.nodeId === el.el.id) {
								// 节点x, y重新赋值，防止再次从左侧导航中拖拽节点时，x, y恢复默认
								v.left = `${el.pos[0]}px`;
								v.top = `${el.pos[1]}px`;
							}
						});
					},
				});
			});
			// 线
			state.jsplumbData.lineList.forEach((v) => {
				state.jsPlumb.connect(
					{
						source: v.sourceId,
						target: v.targetId,
						label: v.label,
					},
					state.jsplumbConnect
				);
			});
		};
		// 左侧导航-菜单标题点击
		const onTitleClick = (val: any) => {
			val.isOpen = !val.isOpen;
		};
		// 右侧内容区-当前项点击
		const onItemCloneClick = (k: number) => {
			state.jsPlumbNodeIndex = k;
		};
		// 右侧内容区-当前项右键菜单点击
		const onContextmenu = (v: any, k: number, e: MouseEvent) => {
			state.jsPlumbNodeIndex = k;
			const { clientX, clientY } = e;
			state.dropdownNode.x = clientX;
			state.dropdownNode.y = clientY;
			v.type = 'node';
			v.label = '';
			let item: any = {};
			state.leftNavList.forEach((l) => {
				if (l.children) if (l.children.find((c: any) => c.id === v.id)) item = l.children.find((c: any) => c.id === v.id);
			});
			v.from = item.form;
			contextmenuNodeRef.value.openContextmenu(v);
		};
		// 右侧内容区-当前项右键菜单点击回调(节点)
		const onCurrentNodeClick = (item: any) => {
			const { contextMenuClickId, nodeId } = item;
			if (contextMenuClickId === 0) {
				const nodeIndex = state.jsplumbData.nodeList.findIndex((item) => item.nodeId === nodeId);
				state.jsplumbData.nodeList.splice(nodeIndex, 1);
				state.jsPlumb.removeAllEndpoints(nodeId);
				state.jsPlumbNodeIndex = null;
			} else if (contextMenuClickId === 1) {
				drawerRef.value.open(item);
			}
		};
		// 右侧内容区-当前项右键菜单点击回调(线)
		const onCurrentLineClick = (item: any, conn: any) => {
			const { contextMenuClickId } = item;
			const { endpoints } = conn;
			const intercourse: any = [];
			endpoints.forEach((v: any) => {
				intercourse.push({
					id: v.element.id,
					innerText: v.element.innerText,
				});
			});
			item.contact = `${intercourse[0].innerText}(${intercourse[0].id}) => ${intercourse[1].innerText}(${intercourse[1].id})`;
			if (contextMenuClickId === 0) state.jsPlumb.deleteConnection(conn);
			else if (contextMenuClickId === 1) drawerRef.value.open(item, conn);
		};
		// 设置线的 label
		const setLineLabel = (obj: any) => {
			const { sourceId, targetId, label } = obj;
			const conn = state.jsPlumb.getConnections({
				source: sourceId,
				target: targetId,
			})[0];
			conn.setLabel(label);
			if (!label || label === '') {
				conn.addClass('workflow-right-empty-label');
			} else {
				conn.removeClass('workflow-right-empty-label');
				conn.addClass('workflow-right-label');
			}
			state.jsplumbData.lineList.forEach((v) => {
				if (v.sourceId === sourceId && v.targetId === targetId) v.label = label;
			});
		};
		// 设置节点内容
		const setNodeContent = (obj: any) => {
			const { nodeId, name, icon } = obj;
			// 设置节点 name 与 icon
			state.jsplumbData.nodeList.forEach((v) => {
				if (v.nodeId === nodeId) {
					v.name = name;
					v.icon = icon;
				}
			});
			// 重绘
			nextTick(() => {
				state.jsPlumb.setSuspendDrawing(false, true);
			});
		};
		// 顶部工具栏-当前项点击
		const onToolClick = (fnName: String) => {
			switch (fnName) {
				case 'help':
					onToolHelp();
					break;
				case 'download':
					onToolDownload();
					break;
				case 'submit':
					onToolSubmit();
					break;
				case 'copy':
					onToolCopy();
					break;
				case 'del':
					onToolDel();
					break;
				case 'fullscreen':
					onToolFullscreen();
					break;
			}
		};
		// 顶部工具栏-帮助
		const onToolHelp = () => {
			nextTick(() => {
				helpRef.value.open();
			});
		};
		// 顶部工具栏-下载
		const onToolDownload = () => {
			const { globalTitle } = themeConfig.value;
			const href = 'data:text/json;charset=utf-8,' + encodeURIComponent(JSON.stringify(state.jsplumbData, null, '\t'));
			const aLink = document.createElement('a');
			aLink.setAttribute('href', href);
			aLink.setAttribute('download', `${globalTitle}工作流.json`);
			aLink.click();
			aLink.remove();
			ElMessage.success('下载成功');
		};
		// 顶部工具栏-提交
		const onToolSubmit = () => {
			// console.log(state.jsplumbData);
			ElMessage.success('数据提交成功');
		};
		// 顶部工具栏-复制
		const onToolCopy = () => {
			copyText(JSON.stringify(state.jsplumbData));
		};
		// 顶部工具栏-删除
		const onToolDel = () => {
			ElMessageBox.confirm('此操作将清空画布，是否继续？', '提示', {
				confirmButtonText: '清空',
				cancelButtonText: '取消',
			})
				.then(() => {
					state.jsplumbData.nodeList.forEach((v) => {
						state.jsPlumb.removeAllEndpoints(v.nodeId);
					});
					nextTick(() => {
						state.jsplumbData = {
							nodeList: [],
							lineList: [],
						};
						ElMessage.success('清空画布成功');
					});
				})
				.catch(() => {});
		};
		// 顶部工具栏-全屏
		const onToolFullscreen = () => {
			stores.setCurrenFullscreen(true);
		};
		// 页面加载时
		onMounted(async () => {
			await initLeftNavList();
			initSortable();
			initJsPlumb();
			setClientWidth();
			window.addEventListener('resize', setClientWidth);
		});
		// 页面卸载时
		onUnmounted(() => {
			window.removeEventListener('resize', setClientWidth);
		});
		return {
			setViewHeight,
			setClientWidth,
			setLineLabel,
			setNodeContent,
			onTitleClick,
			onItemCloneClick,
			onContextmenu,
			onCurrentNodeClick,
			onCurrentLineClick,
			contextmenuNodeRef,
			contextmenuLineRef,
			drawerRef,
			helpRef,
			onToolClick,
			...toRefs(state),
		};
	},
});
</script>

<style scoped lang="scss">
.workflow-container {
	position: relative;
	.workflow {
		display: flex;
		height: 100%;
		width: 100%;
		flex-direction: column;
		.workflow-content {
			display: flex;
			height: calc(100% - 35px);
			.workflow-left {
				width: 220px;
				height: 100%;
				border-right: 1px solid var(--el-border-color-light, #ebeef5);
				:deep(.el-collapse-item__content) {
					padding-bottom: 0;
				}
				.workflow-left-title {
					height: 50px;
					display: flex;
					align-items: center;
					padding: 0 10px;
					border-top: 1px solid var(--el-border-color-light, #ebeef5);
					color: var(--el-text-color-primary);
					cursor: default;
					span {
						flex: 1;
					}
				}
				.workflow-left-item {
					display: inline-block;
					width: calc(50% - 15px);
					position: relative;
					cursor: move;
					margin: 0 0 10px 10px;
					.workflow-left-item-icon {
						height: 35px;
						display: flex;
						align-items: center;
						transition: all 0.3s ease;
						padding: 5px 10px;
						border: 1px dashed transparent;
						background: var(--next-bg-color);
						border-radius: 3px;
						i,
						.name {
							color: var(--el-text-color-secondary);
							transition: all 0.3s ease;
							white-space: nowrap;
							text-overflow: ellipsis;
							overflow: hidden;
						}
						&:hover {
							transition: all 0.3s ease;
							border: 1px dashed var(--el-color-primary);
							background: var(--el-color-primary-light-9);
							border-radius: 5px;
							i,
							.name {
								transition: all 0.3s ease;
								color: var(--el-color-primary);
							}
						}
					}
				}
				& .workflow-left-id:first-of-type {
					.workflow-left-title {
						border-top: none;
					}
				}
			}
			.workflow-right {
				flex: 1;
				position: relative;
				overflow: hidden;
				height: 100%;
				background-image: linear-gradient(90deg, rgb(156 214 255 / 15%) 10%, rgba(0, 0, 0, 0) 10%),
					linear-gradient(rgb(156 214 255 / 15%) 10%, rgba(0, 0, 0, 0) 10%);
				background-size: 10px 10px;
				.workflow-right-clone {
					position: absolute;
					.workflow-right-box {
						height: 35px;
						align-items: center;
						color: var(--el-text-color-secondary);
						padding: 0 10px;
						border-radius: 3px;
						cursor: move;
						transition: all 0.3s ease;
						min-width: 94.5px;
						background: var(--el-color-white);
						border: 1px solid var(--el-border-color-light, #ebeef5);
						.workflow-left-item-icon {
							display: flex;
							align-items: center;
							height: 35px;
						}
						&:hover {
							border: 1px dashed var(--el-color-primary);
							background: var(--el-color-primary-light-9);
							transition: all 0.3s ease;
							color: var(--el-color-primary);
							i {
								cursor: Crosshair;
							}
						}
					}
					.workflow-right-active {
						border: 1px dashed var(--el-color-primary);
						background: var(--el-color-primary-light-9);
						color: var(--el-color-primary);
					}
				}
				:deep(.jtk-overlay):not(.aLabel) {
					padding: 4px 10px;
					border: 1px solid var(--el-border-color-light, #ebeef5) !important;
					color: var(--el-text-color-secondary) !important;
					background: var(--el-color-white) !important;
					border-radius: 3px;
					font-size: 10px;
				}
				:deep(.jtk-overlay.workflow-right-empty-label) {
					display: none;
				}
			}
		}
	}
	.workflow-mask {
		position: absolute;
		top: 0;
		right: 0;
		bottom: 0;
		left: 0;
		&::after {
			content: '手机版不支持 jsPlumb 操作';
			position: absolute;
			top: 0;
			right: 0;
			bottom: 0;
			left: 0;
			z-index: 1;
			background: rgba(255, 255, 255, 0.9);
			color: #666666;
			display: flex;
			align-items: center;
			justify-content: center;
		}
	}
}
</style>
