20260724173425
This commit is contained in:
1 parent
4f40ad9f11
commit
b33c743236
27 files changed
+1310
-437
No files matched your search
@@ -3,7 +3,13 @@ import { computed, h, ref, watch } from "vue";
|
||||
import { message, Modal } from "antdv-next";
|
||||
import FmsTree from "@/components/FmsTree.vue";
|
||||
import { loadDataApi, saveObjectApi, nextIdApi, describeApi } from "@/services/api";
|
||||
import { cloneData, diffRows, allocateTemporaryIds, tableChange, hasChanges } from "@/utils/dataChanges";
|
||||
import {
|
||||
cloneData,
|
||||
diffRows,
|
||||
allocateTemporaryIds,
|
||||
tableChange,
|
||||
hasChanges,
|
||||
} from "@/utils/dataChanges";
|
||||
import { createTempId } from "@/utils/tempId";
|
||||
import { Plus } from "@lucide/vue";
|
||||
import ModuleBasicPanel from "./components/ModuleBasicPanel.vue";
|
||||
@@ -65,6 +71,7 @@ const drafts = ref({
|
||||
|
||||
const configLoading = ref(false);
|
||||
const configSaving = ref(false);
|
||||
const i18nPanelRef = ref(null);
|
||||
|
||||
// 当前选中模块对象(从 drafts.module 获取)
|
||||
const selectedModule = computed(() => drafts.value.module);
|
||||
@@ -245,9 +252,7 @@ async function onAddModule(parentId, moduleType = "directory") {
|
||||
}
|
||||
|
||||
const addModuleMenu = {
|
||||
items: [
|
||||
{ key: "directory", label: "目录" },
|
||||
],
|
||||
items: [{ key: "directory", label: "目录" }],
|
||||
onClick: ({ key }) => onAddModule(null, key),
|
||||
};
|
||||
|
||||
@@ -265,6 +270,13 @@ function onContextMenuClick({ key }) {
|
||||
if (key.startsWith("add-")) {
|
||||
onAddModule(node.key, key.replace("add-", ""));
|
||||
} else if (key === "delete") {
|
||||
if (hasChildNode(node.key)) {
|
||||
Modal.warning({
|
||||
title: "无法删除",
|
||||
content: "该节点下还有子节点,请先删除子节点后再删除当前节点。",
|
||||
});
|
||||
return;
|
||||
}
|
||||
Modal.confirm({
|
||||
title: `确定删除「${node.title}」?`,
|
||||
content: "此操作不可恢复",
|
||||
@@ -276,7 +288,31 @@ function onContextMenuClick({ key }) {
|
||||
}
|
||||
}
|
||||
|
||||
function findNode(key) {
|
||||
const walk = (nodes) => {
|
||||
for (const n of nodes || []) {
|
||||
if (String(n.b_id) === String(key)) return n;
|
||||
const child = walk(n.children);
|
||||
if (child) return child;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
return walk(treeData.value);
|
||||
}
|
||||
|
||||
function hasChildNode(moduleId) {
|
||||
const node = findNode(moduleId);
|
||||
return !!node?.children?.length;
|
||||
}
|
||||
|
||||
async function onDeleteNodeById(moduleId) {
|
||||
if (hasChildNode(moduleId)) {
|
||||
Modal.warning({
|
||||
title: "无法删除",
|
||||
content: "该节点下还有子节点,请先删除子节点后再删除当前节点。",
|
||||
});
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await saveObjectApi([{ table: "b_module", key_field: "b_id", deletes: [{ b_id: moduleId }] }]);
|
||||
message.success("模块已删除");
|
||||
@@ -329,6 +365,12 @@ async function saveAll() {
|
||||
}
|
||||
}
|
||||
|
||||
// 多语言:由各子面板收集变更,统一在此保存
|
||||
if (i18nPanelRef.value) {
|
||||
const i18nReq = await i18nPanelRef.value.buildSaveRequest();
|
||||
if (i18nReq) saveReqs.push(i18nReq);
|
||||
}
|
||||
|
||||
if (saveReqs.length === 0) {
|
||||
message.info("没有需要保存的变更");
|
||||
return;
|
||||
@@ -338,6 +380,7 @@ async function saveAll() {
|
||||
|
||||
// 更新 originals
|
||||
originals.value = cloneData(drafts.value);
|
||||
i18nPanelRef.value?.markSaved();
|
||||
message.success("保存成功");
|
||||
await loadTree();
|
||||
} catch (e) {
|
||||
@@ -393,9 +436,7 @@ async function syncFields() {
|
||||
const viewColumnSet = new Set(columns.map((c) => c.name.toLowerCase()));
|
||||
|
||||
// 已有字段映射(按字段名)
|
||||
const existingMap = new Map(
|
||||
existingFields.map((f) => [f.b_field?.toLowerCase(), f]),
|
||||
);
|
||||
const existingMap = new Map(existingFields.map((f) => [f.b_field?.toLowerCase(), f]));
|
||||
|
||||
const mergedFields = [];
|
||||
|
||||
@@ -434,7 +475,9 @@ async function syncFields() {
|
||||
if (hasChanges(req)) {
|
||||
await allocateTemporaryIds([req.inserts || [], req.updates || [], req.deletes || []]);
|
||||
await saveObjectApi([req]);
|
||||
message.success(`字段同步完成:${columns.length} 列,新增 ${req.inserts?.length || 0},停用 ${req.deletes?.length || 0}`);
|
||||
message.success(
|
||||
`字段同步完成:${columns.length} 列,新增 ${req.inserts?.length || 0},停用 ${req.deletes?.length || 0}`,
|
||||
);
|
||||
} else {
|
||||
message.info("字段已是最新,无需同步");
|
||||
}
|
||||
@@ -471,12 +514,10 @@ loadTree();
|
||||
<div v-if="treeData.length === 0" class="mm-tree-empty">
|
||||
<a-empty description="暂无模块" />
|
||||
</div>
|
||||
<a-dropdown
|
||||
v-else
|
||||
:trigger="['contextmenu']"
|
||||
:menu="contextMenuItems"
|
||||
>
|
||||
<div style="flex:1;min-height:0;overflow:hidden;display:flex;flex-direction:column;">
|
||||
<a-dropdown v-else :trigger="['contextmenu']" :menu="contextMenuItems">
|
||||
<div
|
||||
style="flex: 1; min-height: 0; overflow: hidden; display: flex; flex-direction: column"
|
||||
>
|
||||
<FmsTree
|
||||
class="mm-tree"
|
||||
:tree-data="treeData"
|
||||
@@ -502,9 +543,6 @@ loadTree();
|
||||
<div class="mm-header">
|
||||
<a-space>
|
||||
<a-button type="primary" :loading="configSaving" @click="saveAll">保存</a-button>
|
||||
<a-popconfirm title="确定删除该模块?" @confirm="onDeleteNodeById(selectedModuleId)">
|
||||
<a-button danger>删除模块</a-button>
|
||||
</a-popconfirm>
|
||||
</a-space>
|
||||
<div class="mm-header-center">
|
||||
<a-space size="middle">
|
||||
@@ -523,20 +561,22 @@ loadTree();
|
||||
</a-space>
|
||||
</div>
|
||||
<span class="mm-header-title">{{ selectedModule.b_name || selectedModule.b_code }}</span>
|
||||
<span class="mm-header-code">{{ selectedModule.b_code || '(未设置编码)' }}</span>
|
||||
<span class="mm-header-code">{{ selectedModule.b_code || "(未设置编码)" }}</span>
|
||||
<span class="mm-header-type">
|
||||
<a-tag>{{ selectedModule.b_module_type === 'directory' ? '目录' : selectedModule.b_module_type === 'page' ? '页面' : '数据' }}</a-tag>
|
||||
<a-tag>{{
|
||||
selectedModule.b_module_type === "directory"
|
||||
? "目录"
|
||||
: selectedModule.b_module_type === "page"
|
||||
? "页面"
|
||||
: "数据"
|
||||
}}</a-tag>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<!-- Tab 面板 -->
|
||||
<div class="mm-body">
|
||||
<a-tabs v-model:active-key="activeTab" class="mm-tabs">
|
||||
<a-tab-pane
|
||||
v-for="tab in visibleTabs"
|
||||
:key="tab.key"
|
||||
:tab="tab.tab"
|
||||
>
|
||||
<a-tab-pane v-for="tab in visibleTabs" :key="tab.key" :tab="tab.tab">
|
||||
<template v-if="configLoading">
|
||||
<div class="mm-loading"><a-spin /></div>
|
||||
</template>
|
||||
@@ -596,6 +636,7 @@ loadTree();
|
||||
/>
|
||||
<ModuleI18nPanel
|
||||
v-else-if="tab.key === 'i18n'"
|
||||
ref="i18nPanelRef"
|
||||
:module-id="String(selectedModule.b_id)"
|
||||
:loading="configSaving"
|
||||
/>
|
||||
|
||||
Reference in new issue
Block a user