20260727225141

This commit is contained in:
oneao committed 2026-07-27 22:51:41 +08:00
1 parent 9dbaef7787
commit 63588bd217
97 files changed
+1309 -145

No files matched your search

@@ -13,7 +13,12 @@ import {
hasChanges,
} from "@/utils/dataChanges";
import { createTempId } from "@/utils/tempId";
import { buildModuleItemI18nKey, syncModuleItemI18nKeys } from "@/utils/i18n";
import {
buildModuleI18nKey,
buildModuleItemI18nKey,
syncModuleItemI18nKeys,
validateModuleCode,
} from "@/utils/i18n";
import { Plus } from "@lucide/vue";
import ModuleBasicPanel from "./components/ModuleBasicPanel.vue";
import ModuleFieldsPanel from "./components/ModuleFieldsPanel.vue";
@@ -95,17 +100,61 @@ const isCurrentModule = computed(
() =>
loadedModuleId.value != null && String(loadedModuleId.value) === String(selectedModuleId.value),
);
const i18nKeys = computed(() => {
const keys = new Set();
const add = (key) => {
if (key) keys.add(String(key));
// 构建受管多语言项。managedSource 提供模块/字段/权限的受管键来源,
// plainSource 提供分组/列表标题等非受管键来源(手动填写,需即时反馈)。
function buildI18nItems(managedSource, plainSource) {
const items = new Map();
const add = (item) => {
if (!item.key) return;
const key = String(item.key);
const existing = items.get(key);
items.set(key, existing?.managed ? existing : { ...item, key });
};
add(drafts.value.module?.b_i18n);
for (const section of ["fields", "groups", "listConfig", "powers"]) {
for (const row of drafts.value[section] || []) add(row.b_i18n);
const module = managedSource.module;
if (module?.b_code) {
const moduleKey = buildModuleI18nKey(module.b_module_type, module.b_code);
add({
id: `module:${module.b_id}`,
key: moduleKey,
sourceKey: module.b_i18n,
defaultValue: module.b_name || module.b_code,
managed: true,
});
}
return [...keys];
});
for (const field of managedSource.fields || []) {
add({
id: `field:${field.b_id}`,
key: buildModuleItemI18nKey("field", module?.b_code, field.b_field),
sourceKey: field.b_i18n,
defaultValue: field.b_name || field.b_field,
managed: true,
});
}
for (const power of managedSource.powers || []) {
add({
id: `power:${power.b_id}`,
key: buildModuleItemI18nKey("power", module?.b_code, power.b_code),
sourceKey: power.b_i18n,
defaultValue: power.b_name || power.b_code,
managed: true,
});
}
// 分组和列表标题仍显示在本模块的多语言面板中,但不是系统生成的受管键。
for (const section of ["groups", "listConfig"]) {
for (const row of plainSource[section] || []) {
add({ key: row.b_i18n, managed: false });
}
}
return [...items.values()];
}
// 实时展示:受管键基于已保存快照(originals),编辑过程中不随草稿实时生成;
// 非受管键(分组/列表标题)基于草稿,保留手动编辑的即时反馈。
const i18nItems = computed(() => buildI18nItems(originals.value, drafts.value));
// 保存时:受管键基于最终草稿态(drafts),此时才按模块编码生成并落库。
const finalI18nItems = computed(() => buildI18nItems(drafts.value, drafts.value));
const referencedGroupIds = computed(() => {
const ids = new Set();
for (const section of ["listConfig", "editConfig"]) {
@@ -435,7 +484,24 @@ async function onDeleteNodeById(moduleId) {
return;
}
try {
await saveObjectApi([{ table: "s_module", key_field: "b_id", deletes: [{ b_id: moduleId }] }]);
const id = String(moduleId);
const deleteByModuleId = (table) => ({
table,
key_field: "b_module_id",
deletes: [{ b_module_id: id }],
});
await saveObjectApi([
deleteByModuleId("b_user_module"),
deleteByModuleId("b_user_power"),
deleteByModuleId("s_module_field_list"),
deleteByModuleId("s_module_field_edit"),
deleteByModuleId("s_module_field_query"),
deleteByModuleId("s_module_power"),
deleteByModuleId("s_module_field"),
deleteByModuleId("s_module_field_group"),
{ table: "s_module", key_field: "b_id", deletes: [{ b_id: id }] },
]);
message.success("模块已删除");
if (selectedModuleId.value === moduleId) {
selectedModuleId.value = null;
@@ -460,10 +526,16 @@ async function saveAll() {
// s_module 主表
const moduleData = drafts.value.module;
const moduleOriginal = originals.value.module;
if (!String(moduleData?.b_code || "").trim()) {
const moduleCode = String(moduleData?.b_code || "").trim();
if (!moduleCode) {
message.warning(`请填写${moduleData?.b_module_type === "data" ? "模块编码" : "菜单编码"}`);
return;
}
const codeError = validateModuleCode(moduleCode);
if (codeError) {
message.error(`编码格式不正确:${codeError}`);
return;
}
if (!String(moduleData?.b_name || "").trim()) {
message.warning("请填写模块名称");
return;
@@ -478,6 +550,7 @@ async function saveAll() {
}
syncModuleItemI18nKeys(drafts.value.fields, "field", moduleData.b_code, "b_field");
syncModuleItemI18nKeys(drafts.value.powers, "power", moduleData.b_code, "b_code");
moduleData.b_i18n = buildModuleI18nKey(moduleData.b_module_type, moduleData.b_code);
const isNew = !moduleOriginal?.b_code;
if (moduleData && JSON.stringify(moduleData) !== JSON.stringify(moduleOriginal)) {
saveReqs.push({
@@ -502,6 +575,13 @@ async function saveAll() {
const req = tableChange(table, changes);
if (hasChanges(req)) {
await allocateTemporaryIds([req.inserts || [], req.updates || [], req.deletes || []]);
if (table === "s_module_power" && changes.deletes.length) {
saveReqs.push({
table: "b_user_power",
key_field: "b_power_id",
deletes: changes.deletes.map(({ b_id }) => ({ b_power_id: String(b_id) })),
});
}
saveReqs.push(req);
}
}
@@ -510,6 +590,10 @@ async function saveAll() {
if (currentOperationId !== operationId || String(selectedModule.value?.b_id) !== moduleId)
return;
const i18nPanel = i18nPanelRef.value;
if (i18nPanel && typeof i18nPanel.reconcileManagedRows === "function") {
// 保存时才按最终草稿态生成受管键(模块编码/字段/权限),避免编辑过程中实时生成。
i18nPanel.reconcileManagedRows(finalI18nItems.value);
}
if (typeof i18nPanel?.buildSaveRequest === "function") {
const i18nReq = await i18nPanel.buildSaveRequest();
if (i18nReq) saveReqs.push(i18nReq);
@@ -859,19 +943,6 @@ void loadTree().catch((e) => message.error("模块树加载失败:" + (e.messa
/>
</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-type">
<a-tag>{{
selectedModule.b_module_type === "directory"
? "目录"
: selectedModule.b_module_type === "page"
? "页面"
: "数据"
}}</a-tag>
</span>
</div>
<!-- Tab 面板 -->
@@ -943,7 +1014,7 @@ void loadTree().catch((e) => message.error("模块树加载失败:" + (e.messa
v-else-if="tab.key === 'i18n'"
:ref="setI18nPanelRef"
:module-id="String(selectedModule.b_id)"
:i18n-keys="i18nKeys"
:i18n-items="i18nItems"
:loading="busy"
/>
</a-tab-pane>
@@ -1051,22 +1122,6 @@ void loadTree().catch((e) => message.error("模块树加载失败:" + (e.messa
justify-content: center;
}
.mm-header-title {
font-size: 16px;
font-weight: 600;
color: var(--fms-text);
}
.mm-header-code {
font-size: 13px;
color: var(--fms-text-secondary);
font-family: monospace;
}
.mm-header-type {
flex: none;
}
.mm-body {
flex: 1;
min-height: 0;