From ce3a9d87f8206d3427546db1e8094a2266fcdd21 Mon Sep 17 00:00:00 2001 From: ZhangAo Date: Fri, 28 Aug 2026 16:25:58 +0800 Subject: [PATCH] =?UTF-8?q?refactor(module-management):=20R2a=20UI=20?= =?UTF-8?q?=E5=BD=92=E4=B8=80=E2=80=94=E2=80=94=E5=B0=8F=E6=96=87=E4=BB=B6?= =?UTF-8?q?=E6=9B=BF=E6=8D=A2=E4=B8=BA=20ui=20=E7=BB=84=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - ui Input 补 defineExpose(focus/select/blur),供内联重命名程序化聚焦 - index.vue 树头新建按钮、ModuleBasicPanel 多语言按钮 → ui Button - StructureTreeNode 内联重命名 input → ui Input,六个操作钮 → ui Button - ModuleStructureTree 自绘右键菜单 → ui Dropdown(trigger=contextmenu), 删除自绘定位/关闭监听与 .mst-context-* 样式 --- .../fms-vue/src/components/ui/input/input.vue | 9 + .../components/ModuleBasicPanel.vue | 8 +- .../structure-tree/ModuleStructureTree.vue | 220 ++++-------------- .../structure-tree/StructureTreeNode.vue | 88 +++---- .../views/system/module-management/index.vue | 4 +- 5 files changed, 108 insertions(+), 221 deletions(-) diff --git a/code/fms/fms-vue/src/components/ui/input/input.vue b/code/fms/fms-vue/src/components/ui/input/input.vue index efe4cc16..8853990a 100644 --- a/code/fms/fms-vue/src/components/ui/input/input.vue +++ b/code/fms/fms-vue/src/components/ui/input/input.vue @@ -34,6 +34,7 @@ const slots = useSlots() /* ---------- 公共状态 ---------- */ const focused = ref(false) const hovered = ref(false) +const inputEl = ref(null) /* ---------- 公共 computed ---------- */ const isTextarea = computed(() => props.type === 'textarea') @@ -255,6 +256,13 @@ onMounted(() => { nextTick(measureDefaultTextareaHeight) }) +/* 程序化聚焦/选中文本(内联重命名等场景需要 focus + select) */ +defineExpose({ + focus: () => inputEl.value?.focus(), + select: () => inputEl.value?.select(), + blur: () => inputEl.value?.blur(), +}) + watch( () => [props.rows, isTextarea.value], () => { @@ -285,6 +293,7 @@ onBeforeUnmount(() => {
- +
diff --git a/code/fms/fms-vue/src/views/system/module-management/components/structure-tree/ModuleStructureTree.vue b/code/fms/fms-vue/src/views/system/module-management/components/structure-tree/ModuleStructureTree.vue index e9a6da51..15defeb4 100644 --- a/code/fms/fms-vue/src/views/system/module-management/components/structure-tree/ModuleStructureTree.vue +++ b/code/fms/fms-vue/src/views/system/module-management/components/structure-tree/ModuleStructureTree.vue @@ -14,7 +14,7 @@ * - 选中:受控 selectedKey,双向同步面板属性面板 / 预览高亮。 * - 键盘导航:roving tabindex(↑↓←→ / Enter / Space)。 */ -import { computed, nextTick, onBeforeUnmount, onMounted, provide, ref, watch } from 'vue' +import { computed, nextTick, onBeforeUnmount, provide, ref, watch } from 'vue' import { Clipboard, Copy, @@ -25,6 +25,7 @@ import { Trash2, } from '@lucide/vue' import draggable from 'vuedraggable' +import { Dropdown } from '@/components/ui' import StructureTreeNode from './StructureTreeNode.vue' import { useGroupTree } from '../../composables/useGroupTree' import { useFieldLookup } from '../../composables/useFieldLookup' @@ -523,12 +524,17 @@ function resetDragVisual() { removeDragBadge() } -/* ================= 右键菜单 ================= */ -const contextState = ref(null) // { items, x, y, node, key } -const contextMenuEl = ref(null) +/* ================= 右键菜单(ui Dropdown,trigger=contextmenu,鼠标位置锚定) ================= */ +// 右键目标节点:由 StructureTreeNode 在 contextmenu 冒泡前写入 +const contextNode = ref(null) -function buildContextItems(node) { - if (node.key === 'g:ungrouped') return [] +function setContextNode(node) { + contextNode.value = node +} + +const contextItems = computed(() => { + const node = contextNode.value + if (!node || node.key === 'g:ungrouped') return [] if (node.kind === 'group') { return [ { key: 'add-sub', label: '新增子分组', icon: FolderPlus }, @@ -543,58 +549,14 @@ function buildContextItems(node) { { key: 'paste', label: '粘贴列属性', icon: Clipboard, disabled: !props.pasteEnabled }, { key: 'delete', label: '删除列', icon: Trash2, danger: true }, ] -} - -function openContextMenu(node, event) { - const items = buildContextItems(node) - if (!items || !items.length) return - contextState.value = { items, x: event.clientX, y: event.clientY, node, key: node.key } - nextTick(() => { - const el = contextMenuEl.value - if (!el || !contextState.value) return - const rect = el.getBoundingClientRect() - const vw = window.innerWidth - const vh = window.innerHeight - let { x, y } = contextState.value - if (x + rect.width > vw - 8) x = Math.max(8, x - rect.width) - if (y + rect.height > vh - 8) y = Math.max(8, y - rect.height) - contextState.value.x = x - contextState.value.y = y - }) -} - -function hideContextMenu() { - contextState.value = null -} - -function onMenuItemClick(item, event) { - if (item.disabled) return - const state = contextState.value - emit('context-menu-click', { - item, - node: state?.node ?? null, - key: state?.key ?? null, - event, - }) - hideContextMenu() -} - -function onDocMouseDown(e) { - if (!contextState.value) return - const el = contextMenuEl.value - if (el && el.contains(e.target)) return - hideContextMenu() -} -function onDocKeydown(e) { - if (e.key === 'Escape') hideContextMenu() -} -onMounted(() => { - document.addEventListener('mousedown', onDocMouseDown, true) - document.addEventListener('keydown', onDocKeydown, true) }) + +function onMenuItemClick({ item }) { + const node = contextNode.value + emit('context-menu-click', { item, node, key: node?.key ?? null }) +} + onBeforeUnmount(() => { - document.removeEventListener('mousedown', onDocMouseDown, true) - document.removeEventListener('keydown', onDocKeydown, true) // 拖拽进行中卸载:清理鼠标/键盘监听与跟手卡片,避免泄漏 if (dragKey.value) cleanupDrag() }) @@ -668,66 +630,44 @@ provide('moduleStructureTree', { onNodeMove, onNodeDelete, onNodeAddSub, - openContextMenu, + setContextNode, commitRename, cancelRename, })