|
|
|
@ -1,6 +1,7 @@ |
|
|
|
<script setup lang="ts"> |
|
|
|
import { Wunderbaum } from 'wunderbaum'; |
|
|
|
import 'wunderbaum/dist/wunderbaum.css'; |
|
|
|
import 'bootstrap-icons/font/bootstrap-icons.css'; |
|
|
|
import { deptTreeSelect } from '@/api/system/user'; |
|
|
|
import type { DeptTreeVO } from '@/api/system/dept/types'; |
|
|
|
|
|
|
|
@ -64,8 +65,13 @@ const initTrees = async () => { |
|
|
|
dragEnd: () => { |
|
|
|
currentDragNode = null; |
|
|
|
}, |
|
|
|
dragEnter: () => { |
|
|
|
// 允许放置在 before / after / over 三个位置 |
|
|
|
dragEnter: (e: any) => { |
|
|
|
const targetNode = e.node; |
|
|
|
// 父节点(有子节点):只允许 over(放入内部)和 before |
|
|
|
if (targetNode.children?.length) { |
|
|
|
return new Set(['before', 'over'] as const); |
|
|
|
} |
|
|
|
// 叶子节点:允许 before / after / over |
|
|
|
return new Set(['before', 'after', 'over'] as const); |
|
|
|
}, |
|
|
|
drop: (e: any) => { |
|
|
|
@ -74,9 +80,25 @@ const initTrees = async () => { |
|
|
|
const targetNode = e.node; |
|
|
|
const suggestedDropMode = e.suggestedDropMode; |
|
|
|
|
|
|
|
if (sourceNode && targetNode) { |
|
|
|
if (!sourceNode || !targetNode) return; |
|
|
|
|
|
|
|
// 判断是否从右树拖到左树 |
|
|
|
const isRightToLeft = sourceNode.tree?.id === 'rightTree' && targetNode.tree?.id === 'leftTree'; |
|
|
|
|
|
|
|
if (isRightToLeft) { |
|
|
|
// 暂存原始标题,异步弹框确认 |
|
|
|
const originalTitle = sourceNode.title; |
|
|
|
openDropDialog().then((result) => { |
|
|
|
if (!result) return; // 用户取消 |
|
|
|
sourceNode.moveTo(targetNode, suggestedDropMode); |
|
|
|
sourceNode.setTitle(`${originalTitle}【${result.type}】`); |
|
|
|
setTimeout(() => { |
|
|
|
leftTree?.update('any' as any); |
|
|
|
rightTree?.update('any' as any); |
|
|
|
}, 0); |
|
|
|
}); |
|
|
|
} else { |
|
|
|
sourceNode.moveTo(targetNode, suggestedDropMode); |
|
|
|
// moveTo 内部仅通过 setTimeout 更新源树,需额外更新目标树 |
|
|
|
setTimeout(() => { |
|
|
|
leftTree?.update('any' as any); |
|
|
|
rightTree?.update('any' as any); |
|
|
|
@ -151,6 +173,35 @@ const collapseAll = (tree: Wunderbaum | undefined) => { |
|
|
|
tree?.expandAll(false); |
|
|
|
}; |
|
|
|
|
|
|
|
// ===== 右→左拖拽设置弹框 ===== |
|
|
|
const dropDialogVisible = ref(false); |
|
|
|
const dropOptionType = ref('全部'); |
|
|
|
const dropRemark = ref(''); |
|
|
|
// 存放弹框 resolve/reject,用于将弹框变成 Promise |
|
|
|
let dropDialogResolve: ((value: { type: string; remark: string } | null) => void) | null = null; |
|
|
|
|
|
|
|
/** 打开设置弹框,返回 Promise;确认返回选项,取消返回 null */ |
|
|
|
const openDropDialog = (): Promise<{ type: string; remark: string } | null> => { |
|
|
|
dropOptionType.value = '全部'; |
|
|
|
dropRemark.value = ''; |
|
|
|
dropDialogVisible.value = true; |
|
|
|
return new Promise((resolve) => { |
|
|
|
dropDialogResolve = resolve; |
|
|
|
}); |
|
|
|
}; |
|
|
|
|
|
|
|
const onDropDialogConfirm = () => { |
|
|
|
dropDialogVisible.value = false; |
|
|
|
dropDialogResolve?.({ type: dropOptionType.value, remark: dropRemark.value }); |
|
|
|
dropDialogResolve = null; |
|
|
|
}; |
|
|
|
|
|
|
|
const onDropDialogCancel = () => { |
|
|
|
dropDialogVisible.value = false; |
|
|
|
dropDialogResolve?.(null); |
|
|
|
dropDialogResolve = null; |
|
|
|
}; |
|
|
|
|
|
|
|
// ===== 生命周期 ===== |
|
|
|
onMounted(() => { |
|
|
|
initTrees(); |
|
|
|
@ -212,6 +263,33 @@ onBeforeUnmount(() => { |
|
|
|
<div ref="rightTreeEl" class="tree-wrapper"></div> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
|
|
|
|
<!-- 右→左拖拽设置弹框 --> |
|
|
|
<el-dialog |
|
|
|
v-model="dropDialogVisible" |
|
|
|
title="节点放置设置" |
|
|
|
width="420px" |
|
|
|
:close-on-click-modal="false" |
|
|
|
:close-on-press-escape="false" |
|
|
|
:show-close="false" |
|
|
|
> |
|
|
|
<el-form label-width="80px"> |
|
|
|
<el-form-item label="类型"> |
|
|
|
<el-radio-group v-model="dropOptionType"> |
|
|
|
<el-radio value="全部">全部</el-radio> |
|
|
|
<el-radio value="查询">查询</el-radio> |
|
|
|
<el-radio value="统计">统计</el-radio> |
|
|
|
</el-radio-group> |
|
|
|
</el-form-item> |
|
|
|
<el-form-item label="备注"> |
|
|
|
<el-input v-model="dropRemark" type="textarea" :rows="3" placeholder="请输入备注" /> |
|
|
|
</el-form-item> |
|
|
|
</el-form> |
|
|
|
<template #footer> |
|
|
|
<el-button @click="onDropDialogCancel">取消</el-button> |
|
|
|
<el-button type="primary" @click="onDropDialogConfirm">确定</el-button> |
|
|
|
</template> |
|
|
|
</el-dialog> |
|
|
|
</div> |
|
|
|
</template> |
|
|
|
|
|
|
|
@ -235,7 +313,7 @@ onBeforeUnmount(() => { |
|
|
|
display: flex; |
|
|
|
align-items: stretch; |
|
|
|
gap: 12px; |
|
|
|
min-height: 500px; |
|
|
|
min-height: 1200px; |
|
|
|
} |
|
|
|
|
|
|
|
.tree-panel { |
|
|
|
|