20260806224941

This commit is contained in:
oneao committed 2026-08-06 22:49:41 +08:00
1 parent 6baa339efa
commit 6938deef39
12 files changed
+367 -300

No files matched your search

@@ -1,5 +1,5 @@
<script setup>
import { computed, h, onBeforeUnmount, ref, watch } from "vue";
import { computed, h, ref, watch } from "vue";
import { message, Modal } from "antdv-next";
import { Plus, Trash2 } from "@lucide/vue";
import FmsLoadingOverlay from "@/components/FmsLoadingOverlay.vue";
@@ -41,9 +41,7 @@ const treeLoading = ref(false);
const expandedTreeKeys = ref([]);
const selectedModuleId = ref(null);
const skipLoad = ref(false);
const expandTimer = ref(null);
const activeTab = ref("basic");
let expandDelayResolve = null;
function buildTreeContextMenu(node) {
const items = [];
const iconSize = 14;
@@ -361,71 +359,16 @@ async function loadModuleConfig(moduleId, options = {}) {
}
// ---- 树节点选择 ----
function clearExpandTimer() {
if (expandTimer.value != null) {
clearTimeout(expandTimer.value);
expandTimer.value = null;
}
if (expandDelayResolve) {
expandDelayResolve(false);
expandDelayResolve = null;
}
}
function waitForExpandAnimation() {
return new Promise((resolve) => {
expandDelayResolve = (ready) => {
resolve(ready);
};
expandTimer.value = setTimeout(() => {
expandTimer.value = null;
expandDelayResolve = null;
resolve(true);
}, 300);
});
}
function onTreeSelect(keys, info) {
if (busy.value) return;
if (info?.node?.isFmsRoot) {
selectedModuleId.value = null;
clearExpandTimer();
return;
}
const nodeKey = info?.node?.key ?? keys[0];
if (nodeKey == null) return;
const key = String(nodeKey);
const node = findNode(key);
const hasChildren = node?.children?.length;
clearExpandTimer();
if (!hasChildren) {
// 叶子节点:立即加载数据
selectedModuleId.value = key;
return;
}
// 父节点:处理展开/收起
const expandedKeys = expandedTreeKeys.value.map(String);
const isCurrentlyExpanded = expandedKeys.includes(key);
if (isCurrentlyExpanded) {
// 收起:立即生效,同时加载数据
expandedTreeKeys.value = expandedKeys.filter((item) => item !== key);
selectedModuleId.value = key;
} else {
// 展开:立即高亮,下一帧发请求;结果回写最多等到展开动画结束。
expandedTreeKeys.value = [...expandedKeys, key];
skipLoad.value = true;
selectedModuleId.value = key;
loadedModuleId.value = null;
void loadModuleConfig(key, {
waitForPaint: true,
applyReady: waitForExpandAnimation(),
});
}
selectedModuleId.value = String(nodeKey);
}
watch(selectedModuleId, (id) => {
@@ -846,6 +789,8 @@ async function saveAll() {
unsavedModuleIds.delete(moduleId);
updateSavedTreeNode(moduleData);
if (appStore.locale) await appStore.loadTranslations(appStore.locale);
// 刷新权限数据,让菜单立即反映模块结构变更
permissionStore.load().catch(() => {});
message.success("保存成功");
} catch (e) {
message.error("保存失败:" + (e.message || e));
@@ -1091,8 +1036,6 @@ void permissionStore
.load()
.then(loadTree)
.catch((e) => message.error("模块树加载失败:" + (e.message || e)));
onBeforeUnmount(clearExpandTimer);
</script>
<template>
@@ -1161,15 +1104,17 @@ onBeforeUnmount(clearExpandTimer);
:disabled="busy"
@update:checked="(v) => onSetField('b_canuse', v ? 1 : 0)"
/>
<a-divider type="vertical" />
<span>显示菜单</span>
<a-checkbox
:checked="drafts.module?.b_canmenu === 1"
:disabled="
busy || ['data', 'data_category'].includes(drafts.module?.b_module_type)
"
@update:checked="(v) => onSetField('b_canmenu', v ? 1 : 0)"
/>
<template v-if="drafts.module?.b_module_type !== 'data'">
<a-divider type="vertical" />
<span>显示菜单</span>
<a-checkbox
:checked="drafts.module?.b_canmenu === 1"
:disabled="
busy || ['data', 'data_category'].includes(drafts.module?.b_module_type)
"
@update:checked="(v) => onSetField('b_canmenu', v ? 1 : 0)"
/>
</template>
<template v-if="drafts.module?.b_module_type === 'data'">
<a-divider type="vertical" />
<span>自动编码</span>