@@ -121,3 +235,54 @@ const columns = [
width: 100%;
}
+
+
+
diff --git a/code/fms/fms-vue/src/views/module-management/components/ModuleQueryConfigPanel.vue b/code/fms/fms-vue/src/views/module-management/components/ModuleQueryConfigPanel.vue
index 3cd3b9e5..9add15f5 100644
--- a/code/fms/fms-vue/src/views/module-management/components/ModuleQueryConfigPanel.vue
+++ b/code/fms/fms-vue/src/views/module-management/components/ModuleQueryConfigPanel.vue
@@ -38,7 +38,10 @@ watch(
);
function emitUpdate() {
- emit("update", localQuery.value.map((r) => ({ ...r })));
+ emit(
+ "update",
+ localQuery.value.map((r) => ({ ...r })),
+ );
}
const availableFields = computed(() =>
@@ -111,7 +114,14 @@ const columns = [
title: "操作",
width: 60,
align: "center",
- actions: [{ type: "danger", label: "删除", confirm: { title: "确定删除该记录?", okText: "删除" }, onClick: (row) => deleteRow(row) }],
+ actions: [
+ {
+ type: "danger",
+ label: "删除",
+ confirm: { title: "确定删除该记录?", okText: "删除" },
+ onClick: (row) => deleteRow(row),
+ },
+ ],
},
];
diff --git a/code/fms/fms-vue/src/views/module-management/index.vue b/code/fms/fms-vue/src/views/module-management/index.vue
index 77cc6f6e..7bec3469 100644
--- a/code/fms/fms-vue/src/views/module-management/index.vue
+++ b/code/fms/fms-vue/src/views/module-management/index.vue
@@ -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();
-
-
+
+
-
+
@@ -596,6 +636,7 @@ loadTree();
/>
diff --git a/code/fms/fms-vue/src/views/system/i18n-type/index.vue b/code/fms/fms-vue/src/views/system/i18n-type/index.vue
index 4778bc72..192d89b7 100644
--- a/code/fms/fms-vue/src/views/system/i18n-type/index.vue
+++ b/code/fms/fms-vue/src/views/system/i18n-type/index.vue
@@ -64,7 +64,14 @@ function createActionsCell() {
children.push(
h(
Button,
- { type: "link", size: "small", onClick: (e) => { e.stopPropagation(); openEdit(row); } },
+ {
+ type: "link",
+ size: "small",
+ onClick: (e) => {
+ e.stopPropagation();
+ openEdit(row);
+ },
+ },
"编辑",
),
);
@@ -97,10 +104,28 @@ function createActionsCell() {
const columns = [
{ dataIndex: "b_code", title: "简称 / 编码", width: 150 },
{ dataIndex: "b_name", title: "名称", width: 160 },
- { dataIndex: "b_default", title: "默认", width: 80, align: "center", customCell: createDefaultCell() },
- { dataIndex: "b_canuse", title: "是否启用", width: 100, align: "center", customCell: createCanuseCell() },
+ {
+ dataIndex: "b_default",
+ title: "默认",
+ width: 80,
+ align: "center",
+ customCell: createDefaultCell(),
+ },
+ {
+ dataIndex: "b_canuse",
+ title: "是否启用",
+ width: 100,
+ align: "center",
+ customCell: createCanuseCell(),
+ },
{ dataIndex: "b_xh", title: "排序", width: 80, align: "center" },
- { dataIndex: "actions", title: "操作", width: 130, align: "center", customCell: createActionsCell() },
+ {
+ dataIndex: "actions",
+ title: "操作",
+ width: 130,
+ align: "center",
+ customCell: createActionsCell(),
+ },
];
/* ---------- 行内开关即时保存 ---------- */
@@ -157,10 +182,10 @@ function openEdit(row) {
modalMode.value = "edit";
editingId.value = row.b_id;
editingDefault.value = row.b_default === 1;
- form.value = {
- b_code: row.b_code,
- b_name: row.b_name,
- b_xh: row.b_xh ?? 0,
+ form.value = {
+ b_code: row.b_code,
+ b_name: row.b_name,
+ b_xh: row.b_xh ?? 0,
defaultOn: row.b_default === 1,
canuseOn: row.b_canuse === 1,
};
@@ -240,10 +265,7 @@ async function saveForm() {
>
-
+
diff --git a/code/fms/开发规范.md b/code/fms/开发规范.md
index 292f3ae6..a767b9bc 100644
--- a/code/fms/开发规范.md
+++ b/code/fms/开发规范.md
@@ -3,9 +3,10 @@
## 一、构建与测试
- **不要过多封装**
-- 完成功能后不主动执行构建和测试。
-- 只有用户明确要求时,才执行构建、测试或启动服务。
-- 交付时说明本次未执行构建和测试。
+- 完成功能后不主动执行构建。
+- 允许执行测试以验证改动(如后端 `./mvnw test`、前端 `pnpm fmt:check` 及页面功能验证)。
+- 只有用户明确要求时,才执行构建或启动服务。
+- 交付时说明本次是否执行了测试及测试结果,并说明未执行构建。
## 二、后端接口