20260729173317

This commit is contained in:
oneao committed 2026-07-29 17:33:21 +08:00
1 parent f6b79ea09a
commit 1c839b707a
26 files changed
+1902 -29

No files matched your search

@@ -27,8 +27,10 @@ import ModuleFieldsPanel from "./components/ModuleFieldsPanel.vue";
import ModuleListConfigPanel from "./components/ModuleListConfigPanel.vue";
import ModuleEditConfigPanel from "./components/ModuleEditConfigPanel.vue";
import ModuleQueryConfigPanel from "./components/ModuleQueryConfigPanel.vue";
import ModuleAutoCodePanel from "./components/ModuleAutoCodePanel.vue";
import ModulePowersPanel from "./components/ModulePowersPanel.vue";
import ModuleI18nPanel from "./components/ModuleI18nPanel.vue";
import { createAutoCodeDraft } from "./autoCode";
// ---- 状态 ----
const appStore = useAppStore();
@@ -63,6 +65,7 @@ const originals = ref({
listConfig: [],
editConfig: [],
queryConfig: [],
autoCode: [],
powers: [],
});
@@ -74,6 +77,7 @@ const drafts = ref({
listConfig: [],
editConfig: [],
queryConfig: [],
autoCode: [],
powers: [],
});
@@ -90,6 +94,7 @@ function setI18nPanelRef(instance) {
// 当前选中模块对象(从 drafts.module 获取)
const selectedModule = computed(() => drafts.value.module);
const autoCodeEnabled = computed(() => Number(drafts.value.autoCode?.[0]?.b_canuse) === 1);
const busy = computed(() => configLoading.value || configSaving.value || configSyncing.value);
const syncDisabled = computed(
() => busy.value || !String(selectedModule.value?.b_viewtable || "").trim(),
@@ -185,6 +190,7 @@ const visibleTabs = computed(() => {
{ key: "list", tab: "列表配置" },
{ key: "edit", tab: "编辑配置" },
{ key: "query", tab: "查询配置" },
{ key: "auto-code", tab: "自动编码" },
);
}
if (type === "page") {
@@ -252,6 +258,7 @@ async function loadModuleConfig(moduleId) {
let listResponse;
let editResponse;
let queryResponse;
let autoCodeResponse;
let powersResponse;
if (moduleType === "data") {
@@ -262,6 +269,7 @@ async function loadModuleConfig(moduleId) {
loadDataApi("s_module_field_list", `b_module_id = ${id}`, "b_xh ASC, b_id ASC"),
loadDataApi("s_module_field_edit", `b_module_id = ${id}`, "b_xh ASC, b_id ASC"),
loadDataApi("s_module_field_query", `b_module_id = ${id}`, "b_xh ASC, b_id ASC"),
loadDataApi("s_module_auto_code", `b_module_id = ${id}`, "b_id ASC"),
]);
moduleResponse = responses[0];
fieldsResponse = responses[1];
@@ -269,6 +277,7 @@ async function loadModuleConfig(moduleId) {
listResponse = responses[3];
editResponse = responses[4];
queryResponse = responses[5];
autoCodeResponse = responses[6];
} else if (moduleType === "page") {
const responses = await Promise.all([
loadDataApi("s_module", `b_id = ${id}`),
@@ -289,6 +298,7 @@ async function loadModuleConfig(moduleId) {
listConfig: listResponse ? listResponse.data || [] : [],
editConfig: editResponse ? editResponse.data || [] : [],
queryConfig: queryResponse ? queryResponse.data || [] : [],
autoCode: autoCodeResponse ? autoCodeResponse.data || [] : [],
powers: powersResponse ? powersResponse.data || [] : [],
};
originals.value = original;
@@ -309,11 +319,20 @@ async function loadModuleConfig(moduleId) {
}
// ---- 树节点选择 ----
function onTreeSelect(keys) {
function onTreeSelect(keys, info) {
if (busy.value) return;
if (keys.length > 0) {
selectedModuleId.value = String(keys[0]);
}
const nodeKey = info?.node?.key ?? keys[0];
if (nodeKey == null) return;
const key = String(nodeKey);
selectedModuleId.value = key;
const node = findNode(key);
if (!node?.children?.length) return;
const expandedKeys = expandedTreeKeys.value.map(String);
expandedTreeKeys.value = expandedKeys.includes(key)
? expandedKeys.filter((item) => item !== key)
: [...expandedKeys, key];
}
watch(selectedModuleId, (id) => {
@@ -337,6 +356,128 @@ function onUpdate(section, newData) {
drafts.value[section] = newData;
}
function isFieldEnabled(field) {
if (!field) return false;
return Number(field.b_canuse) === 1;
}
function applyDisabledFieldDefaults(previousFields, nextFields) {
const previousEnabledStates = new Map();
for (const field of previousFields || []) {
if (field.b_id != null) {
previousEnabledStates.set(String(field.b_id), isFieldEnabled(field));
}
}
const disabledFieldIds = new Set();
for (const field of nextFields || []) {
if (field.b_id == null) continue;
const fieldId = String(field.b_id);
if (previousEnabledStates.get(fieldId) && !isFieldEnabled(field)) {
disabledFieldIds.add(fieldId);
}
}
if (disabledFieldIds.size === 0) return;
for (const row of drafts.value.listConfig || []) {
if (disabledFieldIds.has(String(row.b_field_id))) row.b_visible = 0;
}
for (const row of drafts.value.editConfig || []) {
if (disabledFieldIds.has(String(row.b_field_id))) row.b_visible = 0;
}
for (const row of drafts.value.queryConfig || []) {
if (disabledFieldIds.has(String(row.b_field_id))) row.b_canuse = 0;
}
}
function onFieldsUpdate(newFields) {
applyDisabledFieldDefaults(drafts.value.fields, newFields);
drafts.value.fields = newFields;
}
function onAutoCodeUpdate(rows) {
const previousRule = drafts.value.autoCode?.[0];
const previousFieldId =
previousRule && Number(previousRule.b_canuse) === 1 ? previousRule.b_field_id : null;
drafts.value.autoCode = rows;
const rule = rows && rows.length ? rows[0] : null;
const nextFieldId = rule && Number(rule.b_canuse) === 1 ? rule.b_field_id : null;
if (previousFieldId != null && String(previousFieldId) !== String(nextFieldId)) {
const original = (originals.value.editConfig || []).find(
(config) => String(config.b_field_id) === String(previousFieldId),
);
for (const config of drafts.value.editConfig || []) {
if (String(config.b_field_id) === String(previousFieldId)) {
config.b_readonly = Number(original?.b_readonly) === 1 ? 1 : 0;
}
}
}
if (nextFieldId == null) return;
for (const config of drafts.value.editConfig || []) {
if (String(config.b_field_id) === String(nextFieldId)) {
config.b_readonly = 1;
}
}
}
function setAutoCodeEnabled(enabled) {
const current = drafts.value.autoCode?.[0];
const rule = current
? { ...current, b_canuse: enabled ? 1 : 0 }
: createAutoCodeDraft(selectedModule.value?.b_id, drafts.value.fields, enabled);
onAutoCodeUpdate([rule]);
}
function validateAutoCode() {
const rows = drafts.value.autoCode || [];
const rule = rows.length ? rows[0] : null;
if (!rule || Number(rule.b_canuse) !== 1) return "";
if (!String(selectedModule.value?.b_savetable || "").trim()) {
return "启用自动编码前请先配置保存表";
}
const field = (drafts.value.fields || []).find(
(item) => String(item.b_id) === String(rule.b_field_id),
);
if (!field || Number(field.b_canuse) !== 1) return "请选择已启用的业务编号字段";
if (field.b_type !== "input" && field.b_type !== "textarea") {
return "业务编号字段必须使用文本控件类型";
}
if (!Number.isInteger(Number(rule.b_start_value)) || Number(rule.b_start_value) < 0) {
return "起始值必须是大于或等于 0 的整数";
}
if (!["none", "year", "month", "day"].includes(rule.b_reset_type)) {
return "请选择正确的重置周期";
}
const width = Number(rule.b_sequence_width);
if (!Number.isInteger(width) || width < 1 || width > 18) {
return "流水号位数必须是 1 到 18 之间的整数";
}
if (String(rule.b_prefix || "").length > 100) return "固定前缀长度不能超过 100 个字符";
if (String(rule.b_separator || "").length > 10) return "分隔符长度不能超过 10 个字符";
const dateFormat = String(rule.b_date_format || "").trim();
if (dateFormat.length > 30) return "日期格式长度不能超过 30 个字符";
const dateLiterals = dateFormat
.replaceAll("yyyy", "")
.replaceAll("yy", "")
.replaceAll("MM", "")
.replaceAll("dd", "");
if (/[A-Za-z]/.test(dateLiterals)) return "日期格式只支持 yyyy、yy、MM 和 dd";
const hasYear = dateFormat.includes("yyyy") || dateFormat.includes("yy");
const hasMonth = dateFormat.includes("MM");
const hasDay = dateFormat.includes("dd");
if (rule.b_reset_type === "year" && !hasYear) return "按年重置时,日期格式必须包含年份";
if (rule.b_reset_type === "month" && (!hasYear || !hasMonth)) {
return "按月重置时,日期格式必须包含年份和月份";
}
if (rule.b_reset_type === "day" && (!hasYear || !hasMonth || !hasDay)) {
return "按日重置时,日期格式必须包含年月日";
}
for (const config of drafts.value.editConfig || []) {
if (String(config.b_field_id) === String(rule.b_field_id)) config.b_readonly = 1;
}
return "";
}
function onSetField(field, value) {
if (drafts.value.module) {
drafts.value.module[field] = value;
@@ -388,6 +529,7 @@ async function onAddModule(parentId, moduleType = "directory") {
listConfig: [],
editConfig: [],
queryConfig: [],
autoCode: [],
powers: [],
};
drafts.value = cloneData(originals.value);
@@ -469,6 +611,7 @@ async function onDeleteNodeById(moduleId) {
deleteByModuleId("s_module_field_list"),
deleteByModuleId("s_module_field_edit"),
deleteByModuleId("s_module_field_query"),
deleteByModuleId("s_module_auto_code"),
deleteByModuleId("s_module_power"),
deleteByModuleId("s_module_field"),
deleteByModuleId("s_module_field_group"),
@@ -497,6 +640,12 @@ async function saveAll() {
try {
const saveReqs = [];
const autoCodeError = validateAutoCode();
if (autoCodeError) {
message.warning(autoCodeError);
return;
}
// s_module 主表
const moduleData = drafts.value.module;
const moduleOriginal = originals.value.module;
@@ -543,18 +692,25 @@ async function saveAll() {
saveReqs.push(moduleRequest);
}
// 子表:fields, listConfig, editConfig, queryConfig, powers
// 子表:fields, listConfig, editConfig, queryConfig, autoCode, powers
const subTables = [
["s_module_field", "fields"],
["s_module_field_list", "listConfig"],
["s_module_field_edit", "editConfig"],
["s_module_field_query", "queryConfig"],
["s_module_auto_code", "autoCode"],
["s_module_power", "powers"],
];
for (const [table, section] of subTables) {
const current = drafts.value[section] || [];
const previous = originals.value[section] || [];
const changes = diffRows(current, previous);
if (table === "s_module_auto_code") {
changes.updates = changes.updates.map((row) => {
const { b_current_period: _period, b_current_value: _value, ...config } = row;
return config;
});
}
const req = tableChange(table, changes);
if (hasChanges(req)) {
await allocateTemporaryIds([req.inserts || [], req.updates || [], req.deletes || []]);
@@ -654,6 +810,7 @@ function buildMissingDefaultConfigs(module, fields) {
b_required: 0,
b_readonly: 0,
b_disabled: 0,
b_visible: 1,
b_group_id: null,
b_xh: order,
});
@@ -787,6 +944,7 @@ async function syncFields() {
}
}
applyDisabledFieldDefaults(existingFields, mergedFields);
const generated = buildMissingDefaultConfigs(module, mergedFields);
const generatedSections = ["listConfig", "editConfig", "queryConfig"];
let generatedCount = 0;
@@ -898,6 +1056,15 @@ void loadTree().catch((e) => message.error("模块树加载失败:" + (e.messa
:disabled="busy || drafts.module?.b_module_type === 'data'"
@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="autoCodeEnabled"
:disabled="busy"
@update:checked="setAutoCodeEnabled"
/>
</template>
</a-space>
</div>
</div>
@@ -926,7 +1093,7 @@ void loadTree().catch((e) => message.error("模块树加载失败:" + (e.messa
:module-code="selectedModule?.b_code || ''"
:fields="drafts.fields"
:loading="busy"
@update="(v) => onUpdate('fields', v)"
@update="onFieldsUpdate"
@i18n-saved="onI18nSaved"
/>
<ModuleListConfigPanel
@@ -959,6 +1126,14 @@ void loadTree().catch((e) => message.error("模块树加载失败:" + (e.messa
:loading="busy"
@update="(v) => onUpdate('queryConfig', v)"
/>
<ModuleAutoCodePanel
v-else-if="tab.key === 'auto-code'"
:module-id="String(selectedModule.b_id)"
:auto-code="drafts.autoCode"
:fields="drafts.fields"
:loading="busy"
@update="onAutoCodeUpdate"
/>
<ModulePowersPanel
v-else-if="tab.key === 'powers'"
:module-id="String(selectedModule.b_id)"