diff --git a/code/fms/CLAUDE.md b/code/fms/CLAUDE.md index c07a0522..13202bc0 100644 --- a/code/fms/CLAUDE.md +++ b/code/fms/CLAUDE.md @@ -82,10 +82,9 @@ Vue 3 ` @@ -355,7 +817,7 @@ defineExpose({ :data-source="dataSource" :row-key="resolveRowKey" :column-extensions="columnExtensions" - :row-actions="permittedRowActions" + :row-actions="rowActions" :row-selection="rowSelection" :row-class-name="rowClassName" :expandable="expandable" diff --git a/code/fms/fms-vue/src/components/fms-module-list/FmsModuleTable.vue b/code/fms/fms-vue/src/components/fms-module-list/FmsModuleTable.vue index 21596729..a1d8a48a 100644 --- a/code/fms/fms-vue/src/components/fms-module-list/FmsModuleTable.vue +++ b/code/fms/fms-vue/src/components/fms-module-list/FmsModuleTable.vue @@ -1,9 +1,9 @@ @@ -157,6 +167,19 @@ onMounted(async () => { margin: 0; border-radius: 0; line-height: 36px; + transition: padding-inline-start 0.2s ease; + } + :deep(.ant-menu-inline-collapsed) { + .ant-menu-title-content { + display: none !important; + } + .ant-menu-item, + .ant-menu-submenu-title { + padding-inline-start: 20px !important; + } + .ant-menu-item-icon { + vertical-align: middle; + } } } .brand { diff --git a/code/fms/fms-vue/src/module-runtime/actionRegistry.js b/code/fms/fms-vue/src/module-runtime/actionRegistry.js deleted file mode 100644 index 9140f9ac..00000000 --- a/code/fms/fms-vue/src/module-runtime/actionRegistry.js +++ /dev/null @@ -1,37 +0,0 @@ -function contextSuffix(context) { - const parts = [ - context?.moduleId ? `moduleId=${context.moduleId}` : "", - context?.profileId ? `profileId=${context.profileId}` : "", - ].filter(Boolean); - return parts.length ? `(${parts.join(",")})` : ""; -} - -export function createModuleActionRegistry(context = () => ({})) { - const handlers = new Map(); - - function register(key, handler) { - const normalizedKey = String(key || "").trim(); - if (!normalizedKey) throw new Error("模块操作 key 不能为空"); - if (typeof handler !== "function") throw new Error(`模块操作 ${normalizedKey} 缺少处理函数`); - handlers.set(normalizedKey, handler); - return () => handlers.delete(normalizedKey); - } - - function has(key) { - return handlers.has(String(key || "").trim()); - } - - async function run(key, payload) { - const normalizedKey = String(key || "").trim(); - const handler = handlers.get(normalizedKey); - const currentContext = typeof context === "function" ? context() : context; - if (!handler) { - throw new Error( - `未注册模块操作:${normalizedKey || ""}${contextSuffix(currentContext)}`, - ); - } - return handler(payload, currentContext); - } - - return { register, has, run }; -} diff --git a/code/fms/fms-vue/src/module-runtime/conditionBuilder.js b/code/fms/fms-vue/src/module-runtime/conditionBuilder.js deleted file mode 100644 index 95c4643d..00000000 --- a/code/fms/fms-vue/src/module-runtime/conditionBuilder.js +++ /dev/null @@ -1,21 +0,0 @@ -import { buildQueryCondition } from "@/components/fms-module-list/moduleListUtils"; - -export function buildSearchConditions(entries = []) { - const conditions = []; - for (const entry of entries) { - const condition = buildQueryCondition(entry.item, entry.value); - if (condition) conditions.push(condition); - } - return conditions; -} - -export function combineSearchConditions(fixedCondition, conditions = []) { - const result = []; - const fixed = String(fixedCondition || "").trim(); - if (fixed) result.push(`(${fixed})`); - for (const condition of conditions) { - const normalized = String(condition || "").trim(); - if (normalized) result.push(normalized); - } - return result.join(" AND "); -} diff --git a/code/fms/fms-vue/src/module-runtime/configLoader.js b/code/fms/fms-vue/src/module-runtime/configLoader.js deleted file mode 100644 index 23212991..00000000 --- a/code/fms/fms-vue/src/module-runtime/configLoader.js +++ /dev/null @@ -1,141 +0,0 @@ -import { loadDataApi } from "@/services/api"; -import { - buildQueryCondition, - defaultQueryValue, - identifierRe, - parseOptions, - requireModuleId, -} from "@/components/fms-module-list/moduleListUtils"; -import { MODULE_TABLES } from "./constants"; -import { requireFieldType } from "./fieldRegistry"; - -const configCache = new Map(); - -function resolveKeyField(module, fields) { - if (identifierRe.test(module.b_keyfield || "")) return module.b_keyfield; - for (const field of fields) { - if (field.b_field === "b_id") return field.b_field; - } - for (const field of fields) { - if (identifierRe.test(field.b_field || "")) return field.b_field; - } - return ""; -} - -function configKey(runtime, orgId) { - return JSON.stringify([ - String(orgId || ""), - String(runtime.b_module_id), - String(runtime.b_profile_id), - ]); -} - -async function loadConfig(runtime) { - const moduleId = requireModuleId(runtime.b_module_id); - const profileId = requireModuleId(runtime.b_profile_id); - const responses = await Promise.all([ - loadDataApi(MODULE_TABLES.field, `b_module_id = ${moduleId}`, "b_id ASC"), - loadDataApi(MODULE_TABLES.fieldList, `b_profile_id = ${profileId}`, "b_xh ASC, b_id ASC"), - loadDataApi(MODULE_TABLES.fieldForm, `b_profile_id = ${profileId}`, "b_xh ASC, b_id ASC"), - loadDataApi( - MODULE_TABLES.fieldQuery, - `b_profile_id = ${profileId} AND b_canuse = 1`, - "b_xh ASC, b_id ASC", - ), - loadDataApi(MODULE_TABLES.layoutGroup, `b_profile_id = ${profileId}`, "b_xh ASC, b_id ASC"), - loadDataApi( - MODULE_TABLES.action, - `b_module_id = ${moduleId} AND b_canuse = 1`, - "b_xh ASC, b_id ASC", - ), - ]); - - const fields = []; - const fieldById = new Map(); - for (const field of responses[0].data || []) { - if (Number(field.b_canuse) !== 1 || !identifierRe.test(field.b_field || "")) continue; - requireFieldType(field, { - moduleId: runtime.b_module_id, - profileId: runtime.b_profile_id, - }); - fields.push(field); - fieldById.set(String(field.b_id), field); - } - - const listConfig = []; - for (const item of responses[1].data || []) { - if (Number(item.b_visible) !== 1) continue; - if (!fieldById.has(String(item.b_field_id))) continue; - listConfig.push(item); - } - - const formConfig = []; - const formConfigByFieldId = new Map(); - for (const item of responses[2].data || []) { - if (Number(item.b_visible) !== 1) continue; - if (!fieldById.has(String(item.b_field_id))) continue; - formConfig.push(item); - formConfigByFieldId.set(String(item.b_field_id), item); - } - - const queryItems = []; - for (const config of responses[3].data || []) { - const field = fieldById.get(String(config.b_field_id)); - if (!field) continue; - const optionInfo = parseOptions(field); - queryItems.push({ - config, - field, - optionConfig: optionInfo.optionConfig, - options: optionInfo.options, - }); - } - - const defaultSearchConditions = []; - for (const item of queryItems) { - const condition = buildQueryCondition(item, defaultQueryValue(item)); - if (condition) defaultSearchConditions.push(condition); - } - - return { - fields, - fieldById, - listConfig, - formConfig, - formConfigByFieldId, - queryItems, - defaultSearchConditions, - groups: responses[4].data || [], - actions: responses[5].data || [], - keyField: resolveKeyField(runtime, fields), - }; -} - -export async function loadModuleRuntimeConfig(runtime, options = {}) { - const key = configKey(runtime, options.orgId); - if (!options.force && configCache.has(key)) return configCache.get(key).promise; - const entry = { - orgId: String(options.orgId || ""), - moduleId: String(runtime.b_module_id), - profileId: String(runtime.b_profile_id), - promise: null, - }; - entry.promise = loadConfig(runtime).catch((error) => { - if (configCache.get(key) === entry) configCache.delete(key); - throw error; - }); - configCache.set(key, entry); - return entry.promise; -} - -export function invalidateModuleConfigCache(filter = {}) { - const orgId = String(filter.orgId || ""); - const moduleId = String(filter.moduleId || ""); - const profileId = String(filter.profileId || ""); - for (const [key, entry] of configCache) { - if (orgId && entry.orgId !== orgId) continue; - if (moduleId && entry.moduleId !== moduleId) continue; - if (profileId && entry.profileId !== profileId) continue; - configCache.delete(key); - } -} diff --git a/code/fms/fms-vue/src/module-runtime/constants.js b/code/fms/fms-vue/src/module-runtime/constants.js deleted file mode 100644 index 6ab71a56..00000000 --- a/code/fms/fms-vue/src/module-runtime/constants.js +++ /dev/null @@ -1,36 +0,0 @@ -export const MODULE_TABLES = Object.freeze({ - module: "s_module", - profile: "s_module_profile", - field: "s_module_field", - fieldList: "s_module_field_list", - fieldForm: "s_module_field_form", - fieldQuery: "s_module_field_query", - layoutGroup: "s_module_layout_group", - action: "s_module_action", -}); - -export const MODULE_VIEWS = Object.freeze({ - runtime: "v_s_module_runtime", - functionNode: "v_s_function_node", - pageNode: "v_s_page_node", -}); - -export const MODULE_TYPES = Object.freeze({ - page: "page", - data: "data", - report: "report", - dashboard: "dashboard", - workflow: "workflow", -}); - -export const FUNCTION_NODE_TYPES = Object.freeze({ - directory: "directory", - entry: "entry", -}); - -export const PAGE_NODE_TYPES = Object.freeze({ - group: "group", - tab: "tab", - section: "section", - moduleRef: "module_ref", -}); diff --git a/code/fms/fms-vue/src/module-runtime/fieldRegistry.js b/code/fms/fms-vue/src/module-runtime/fieldRegistry.js deleted file mode 100644 index 72c53280..00000000 --- a/code/fms/fms-vue/src/module-runtime/fieldRegistry.js +++ /dev/null @@ -1,119 +0,0 @@ -import { h } from "vue"; -import { Checkbox } from "antdv-next"; -import { formatDateValue } from "@/components/fms-module-list/moduleListUtils"; - -const fieldTypes = new Map(); - -function textValue(value) { - return value == null ? "" : String(value); -} - -function groupedNumberValue(value, minimumFractionDigits = 0) { - if (value == null || value === "") return ""; - const text = String(value).trim(); - const match = /^([+-]?)(\d+)(?:\.(\d*))?$/.exec(text); - if (!match) return text; - const integer = match[2].replace(/\B(?=(\d{3})+(?!\d))/g, ","); - const fraction = String(match[3] || "").padEnd(minimumFractionDigits, "0"); - return `${match[1]}${integer}${fraction ? `.${fraction}` : ""}`; -} - -function moneyValue(value, { optionInfo }) { - const precision = Number.isInteger(optionInfo.optionConfig.precision) - ? optionInfo.optionConfig.precision - : 2; - return groupedNumberValue(value, precision); -} - -function checkboxValue(value) { - return h(Checkbox, { - checked: value === true || Number(value) === 1, - disabled: true, - }); -} - -function selectValue(value, { optionInfo }) { - if (value == null || value === "") return ""; - const option = optionInfo.options.find((item) => String(item.value) === String(value)); - return option?.label ?? String(value); -} - -function dateEditor(type) { - return ({ format }) => ({ - type: "input", - inputType: type, - format, - }); -} - -export function registerFieldType(key, definition) { - const normalizedKey = String(key || "").trim(); - if (!normalizedKey) throw new Error("字段控件 key 不能为空"); - if (!definition || typeof definition !== "object") { - throw new Error(`字段控件 ${normalizedKey} 的定义无效`); - } - fieldTypes.set(normalizedKey, Object.freeze({ ...definition })); -} - -export function hasFieldType(key) { - return fieldTypes.has(String(key || "").trim()); -} - -export function requireFieldType(field, context = {}) { - const key = String(field?.b_type || "input").trim(); - const definition = fieldTypes.get(key); - if (definition) return definition; - - const locations = [ - context.moduleId ? `moduleId=${context.moduleId}` : "", - context.profileId ? `profileId=${context.profileId}` : "", - field?.b_field ? `field=${field.b_field}` : "", - ].filter(Boolean); - const suffix = locations.length ? `(${locations.join(",")})` : ""; - throw new Error(`未注册字段控件:${key || ""}${suffix}`); -} - -export function formatFieldValue(value, field, optionInfo, context = {}) { - const definition = requireFieldType(field, context); - return (definition.format || textValue)(value, { field, optionInfo, context }); -} - -export function createFieldEditor(field, formConfig, optionInfo, context = {}) { - const definition = requireFieldType(field, context); - const format = (value) => formatFieldValue(value, field, optionInfo, context); - const editor = - typeof definition.editor === "function" - ? definition.editor({ field, formConfig, optionInfo, context, format }) - : definition.editor; - return { ...(editor || { type: "input" }), format }; -} - -registerFieldType("input", { format: textValue, editor: { type: "input" } }); -registerFieldType("textarea", { format: textValue, editor: { type: "textarea" } }); -registerFieldType("number", { - format: (value) => groupedNumberValue(value), - editor: { type: "number", stringMode: true }, -}); -registerFieldType("money", { - format: moneyValue, - editor: ({ optionInfo }) => ({ - type: "number", - stringMode: true, - precision: Number.isInteger(optionInfo.optionConfig.precision) - ? optionInfo.optionConfig.precision - : 2, - }), -}); -registerFieldType("date", { - format: (value) => formatDateValue(value, false), - editor: dateEditor("date"), -}); -registerFieldType("datetime", { - format: (value) => formatDateValue(value, true), - editor: dateEditor("datetime-local"), -}); -registerFieldType("checkbox", { format: checkboxValue, editor: { type: "checkbox" } }); -registerFieldType("select", { - format: selectValue, - editor: ({ optionInfo }) => ({ type: "select", options: optionInfo.options }), -}); diff --git a/code/fms/fms-vue/src/module-runtime/index.js b/code/fms/fms-vue/src/module-runtime/index.js deleted file mode 100644 index 797e4dc8..00000000 --- a/code/fms/fms-vue/src/module-runtime/index.js +++ /dev/null @@ -1,11 +0,0 @@ -export { invalidateModuleConfigCache } from "./configLoader"; -export { invalidateModuleRuntimeCache, resolveRoutePageModuleId } from "./runtimeResolver"; -export { createModuleActionRegistry } from "./actionRegistry"; -export { buildSearchConditions, combineSearchConditions } from "./conditionBuilder"; -export { - createFieldEditor, - formatFieldValue, - hasFieldType, - registerFieldType, - requireFieldType, -} from "./fieldRegistry"; diff --git a/code/fms/fms-vue/src/module-runtime/runtimeResolver.js b/code/fms/fms-vue/src/module-runtime/runtimeResolver.js deleted file mode 100644 index 2831300e..00000000 --- a/code/fms/fms-vue/src/module-runtime/runtimeResolver.js +++ /dev/null @@ -1,146 +0,0 @@ -import { loadDataApi } from "@/services/api"; -import { escapeSqlText, requireModuleId } from "@/components/fms-module-list/moduleListUtils"; -import { FUNCTION_NODE_TYPES, MODULE_TYPES, MODULE_VIEWS, PAGE_NODE_TYPES } from "./constants"; - -const runtimeCache = new Map(); - -function normalizeId(value) { - return value == null || value === "" ? "" : requireModuleId(value); -} - -function locatorValue(locator, key) { - return String(locator?.[key] || "").trim(); -} - -export function createRuntimeKey(locator) { - return [ - String(locator?.orgId || ""), - locatorValue(locator, "moduleId"), - locatorValue(locator, "profileId"), - locatorValue(locator, "pageNodeId"), - locatorValue(locator, "routePath"), - ].join(":"); -} - -export async function resolveRoutePageModuleId(routePath) { - const escapedRoute = escapeSqlText(routePath); - const response = await loadDataApi( - MODULE_VIEWS.functionNode, - `b_route = N'${escapedRoute}' AND b_node_type = '${FUNCTION_NODE_TYPES.entry}' AND b_canuse = 1`, - "b_id ASC", - ); - const pageModuleIds = new Set(); - for (const row of response.data || []) { - if (row.b_module_id != null) pageModuleIds.add(String(row.b_module_id)); - } - if (!pageModuleIds.size) throw new Error(`路由 ${routePath} 没有对应的启用功能入口`); - if (pageModuleIds.size > 1) throw new Error(`路由 ${routePath} 对应多个页面模块,无法自动定位`); - return [...pageModuleIds][0]; -} - -async function resolvePageTarget(locator) { - const pageNodeId = locatorValue(locator, "pageNodeId"); - let condition = ""; - if (pageNodeId) { - condition = - `b_id = ${normalizeId(pageNodeId)} AND b_node_type = '${PAGE_NODE_TYPES.moduleRef}' ` + - "AND b_canuse = 1"; - } else { - const routePath = locatorValue(locator, "routePath"); - if (!routePath) throw new Error("未提供 moduleId、profileId、pageNodeId 或页面路由"); - const pageModuleId = await resolveRoutePageModuleId(routePath); - condition = - `b_page_module_id = ${normalizeId(pageModuleId)} ` + - `AND b_node_type = '${PAGE_NODE_TYPES.moduleRef}' AND b_canuse = 1`; - } - - const response = await loadDataApi(MODULE_VIEWS.pageNode, condition, "b_xh ASC, b_id ASC"); - const targets = []; - for (const row of response.data || []) { - if (row.b_module_id == null || row.b_profile_id == null) continue; - targets.push(row); - } - if (!targets.length) throw new Error("当前页面节点没有配置启用的数据模块引用"); - if (targets.length > 1) { - throw new Error("当前页面包含多个数据模块引用,请使用 pageNodeId 明确定位"); - } - return { - moduleId: normalizeId(targets[0].b_module_id), - profileId: normalizeId(targets[0].b_profile_id), - }; -} - -async function resolveRuntime(locator) { - let moduleId = normalizeId(locatorValue(locator, "moduleId")); - let profileId = normalizeId(locatorValue(locator, "profileId")); - - if (!moduleId && !profileId) { - const target = await resolvePageTarget(locator); - moduleId = target.moduleId; - profileId = target.profileId; - } - - const conditions = [ - `b_module_type = '${MODULE_TYPES.data}'`, - "b_canuse = 1", - "b_profile_canuse = 1", - ]; - if (moduleId) conditions.push(`b_module_id = ${moduleId}`); - if (profileId) { - conditions.push(`b_profile_id = ${profileId}`); - } else { - conditions.push("b_default = 1"); - } - - const response = await loadDataApi( - MODULE_VIEWS.runtime, - conditions.join(" AND "), - "b_module_id ASC, b_profile_id ASC", - ); - const rows = response.data || []; - if (!rows.length) throw new Error("未找到启用的数据模块使用方式"); - if (rows.length > 1) throw new Error("模块运行时定位结果不唯一"); - const runtime = rows[0]; - return { - ...runtime, - b_id: String(runtime.b_module_id), - b_module_id: String(runtime.b_module_id), - b_profile_id: String(runtime.b_profile_id), - }; -} - -export async function resolveModuleRuntime(locator, options = {}) { - const key = createRuntimeKey(locator); - if (!options.force && runtimeCache.has(key)) return runtimeCache.get(key).promise; - - const entry = { - orgId: String(locator?.orgId || ""), - moduleId: "", - profileId: "", - promise: null, - }; - entry.promise = resolveRuntime(locator) - .then((runtime) => { - entry.moduleId = runtime.b_module_id; - entry.profileId = runtime.b_profile_id; - return runtime; - }) - .catch((error) => { - if (runtimeCache.get(key) === entry) runtimeCache.delete(key); - throw error; - }); - runtimeCache.set(key, entry); - return entry.promise; -} - -export function invalidateModuleRuntimeCache(filter = {}) { - const orgId = String(filter.orgId || ""); - const moduleId = String(filter.moduleId || ""); - const profileId = String(filter.profileId || ""); - for (const [key, entry] of runtimeCache) { - if (orgId && entry.orgId !== orgId) continue; - if (moduleId && entry.moduleId && entry.moduleId !== moduleId) continue; - if (profileId && entry.profileId && entry.profileId !== profileId) continue; - runtimeCache.delete(key); - } -} diff --git a/code/fms/fms-vue/src/module-runtime/saveRequestBuilder.js b/code/fms/fms-vue/src/module-runtime/saveRequestBuilder.js deleted file mode 100644 index 68c7911e..00000000 --- a/code/fms/fms-vue/src/module-runtime/saveRequestBuilder.js +++ /dev/null @@ -1,13 +0,0 @@ -export function buildTableSaveRequest({ table, keyField, inserts, updates, deletes }) { - const request = { table, key_field: keyField }; - if (inserts?.length) request.inserts = inserts; - if (updates?.length) request.updates = updates; - if (deletes?.length) request.deletes = deletes; - return request; -} - -export function buildKeyDeletes(keyField, keys) { - const rows = []; - for (const key of keys || []) rows.push({ [keyField]: key }); - return rows; -} diff --git a/code/fms/fms-vue/src/module-runtime/useModuleDataSource.js b/code/fms/fms-vue/src/module-runtime/useModuleDataSource.js deleted file mode 100644 index 687441e5..00000000 --- a/code/fms/fms-vue/src/module-runtime/useModuleDataSource.js +++ /dev/null @@ -1,132 +0,0 @@ -import { reactive, ref, shallowRef } from "vue"; -import { pageDataApi } from "@/services/api"; -import { identifierRe } from "@/components/fms-module-list/moduleListUtils"; -import { buildSearchConditions, combineSearchConditions } from "./conditionBuilder"; - -function optionValue(option) { - return typeof option === "function" ? option() : option; -} - -export function useModuleDataSource(options) { - const dataSource = shallowRef([]); - const searchConditions = shallowRef([]); - const loading = ref(false); - const error = ref(""); - const pagination = reactive({ - current: 1, - pageSize: Number(optionValue(options.pageSize)) || 20, - total: 0, - }); - let requestVersion = 0; - - function resolveOrderBy() { - const module = options.dataModule.value; - if (!module) throw new Error("数据模块尚未加载"); - const configured = String(module.b_orderfield || "").trim(); - if (configured) return configured; - if (options.keyField.value) return `${options.keyField.value} DESC`; - - if (options.listConfig.value.length > 0) { - const firstConfig = options.listConfig.value[0]; - const firstField = options.activeFieldById.value.get(String(firstConfig.b_field_id)); - if (firstField && identifierRe.test(firstField.b_field || "")) { - return `${firstField.b_field} ASC`; - } - } - throw new Error("数据模块未配置可用的默认排序字段"); - } - - async function fetchData() { - const currentModule = options.dataModule.value; - if (!currentModule?.b_viewtable) return; - - const currentRuntimeKey = options.runtimeKey(); - const version = ++requestVersion; - const fixedCondition = String(optionValue(options.fixedSearchCondition) || "").trim(); - loading.value = true; - error.value = ""; - - try { - const response = await pageDataApi({ - viewName: currentModule.b_viewtable, - orderBy: resolveOrderBy(), - searchCondition: combineSearchConditions(fixedCondition, searchConditions.value), - pageNo: pagination.current, - pageSize: pagination.pageSize, - }); - if (version !== requestVersion || options.dataModule.value !== currentModule) return; - if (options.runtimeKey() !== currentRuntimeKey) return; - dataSource.value = response.data?.rows || []; - pagination.total = Number(response.data?.total ?? response.data?.row_count) || 0; - } catch (loadError) { - if ( - version === requestVersion && - options.dataModule.value === currentModule && - options.runtimeKey() === currentRuntimeKey - ) { - dataSource.value = []; - pagination.total = 0; - error.value = loadError?.message || String(loadError); - } - } finally { - if ( - version === requestVersion && - options.dataModule.value === currentModule && - options.runtimeKey() === currentRuntimeKey - ) { - loading.value = false; - } - } - } - - function reset(initialConditions = []) { - requestVersion++; - dataSource.value = []; - searchConditions.value = [...initialConditions]; - loading.value = false; - error.value = ""; - pagination.current = 1; - pagination.pageSize = Number(optionValue(options.pageSize)) || 20; - pagination.total = 0; - } - - function search({ entries = [] }) { - if (loading.value) return; - searchConditions.value = buildSearchConditions(entries); - pagination.current = 1; - return fetchData(); - } - - function resetSearch() { - if (loading.value) return; - searchConditions.value = []; - pagination.current = 1; - return fetchData(); - } - - function changePage(page, pageSize) { - if (loading.value) return; - pagination.current = pageSize === pagination.pageSize ? page : 1; - pagination.pageSize = pageSize; - return fetchData(); - } - - function reload() { - if (loading.value) return; - return fetchData(); - } - - return { - dataSource, - searchConditions, - loading, - error, - pagination, - reset, - fetchData, - search, - resetSearch, - changePage, - reload, - }; -} diff --git a/code/fms/fms-vue/src/module-runtime/useModuleEditor.js b/code/fms/fms-vue/src/module-runtime/useModuleEditor.js deleted file mode 100644 index 189465e8..00000000 --- a/code/fms/fms-vue/src/module-runtime/useModuleEditor.js +++ /dev/null @@ -1,289 +0,0 @@ -import { computed, nextTick, ref, shallowRef } from "vue"; -import { message } from "antdv-next"; -import { nextIdApi, saveObjectApi } from "@/services/api"; -import { identifierRe } from "@/components/fms-module-list/moduleListUtils"; -import { buildKeyDeletes, buildTableSaveRequest } from "./saveRequestBuilder"; - -const SYSTEM_FIELDS = ["b_inputdatetime", "b_inputuser_id", "b_updatedatetime", "b_updateuser_id"]; - -function optionValue(option) { - return typeof option === "function" ? option() : option; -} - -export function useModuleEditor(options) { - const dirtyRows = shallowRef(new Map()); - const newRowKeys = shallowRef(new Set()); - const saving = ref(false); - - const readonlyFields = computed(() => { - const fields = new Set(SYSTEM_FIELDS); - if (options.keyField.value) fields.add(options.keyField.value); - return [...fields]; - }); - - function emitDirty() { - options.onDirtyChange?.(dirtyRows.value.size); - } - - function reset() { - dirtyRows.value = new Map(); - newRowKeys.value = new Set(); - saving.value = false; - emitDirty(); - } - - function resolveRowKey(row) { - if (options.keyField.value) return row[options.keyField.value]; - return JSON.stringify(row); - } - - function canAction(actionCode) { - return options.canAction?.(actionCode) === true; - } - - function requireAction(actionCode) { - if (canAction(actionCode)) return true; - message.warning("没有该操作的权限"); - return false; - } - - function handleCellChange({ row, field }) { - if (!optionValue(options.editable) || !options.keyField.value) return; - const rowKey = String(resolveRowKey(row)); - const current = dirtyRows.value.get(rowKey); - if (current) { - current.add(field); - } else { - dirtyRows.value.set(rowKey, new Set([field])); - } - emitDirty(); - } - - function resolveSaveTable() { - const module = options.dataModule.value; - if (!module) throw new Error("数据模块尚未加载"); - return String(module.b_savetable || module.b_viewtable || ""); - } - - function dirtyUpdateRows() { - const keyField = options.keyField.value; - if (!keyField) return []; - const updates = []; - const rowByKey = new Map( - options.dataSource.value.map((row) => [String(resolveRowKey(row)), row]), - ); - for (const [rowKey, fields] of dirtyRows.value) { - if (newRowKeys.value.has(rowKey)) continue; - const row = rowByKey.get(rowKey); - if (!row) continue; - const update = { [keyField]: row[keyField] }; - for (const field of fields) { - if (field !== keyField) update[field] = row[field]; - } - updates.push(update); - } - return updates; - } - - function dirtyInsertRows() { - const keyField = options.keyField.value; - if (!keyField) return []; - const rowByKey = new Map( - options.dataSource.value.map((row) => [String(resolveRowKey(row)), row]), - ); - const inserts = []; - for (const rowKey of newRowKeys.value) { - const fields = dirtyRows.value.get(rowKey); - const row = rowByKey.get(rowKey); - if (!fields || !row) continue; - const insert = { [keyField]: row[keyField] }; - for (const field of fields) { - if (field !== keyField) insert[field] = row[field]; - } - inserts.push(insert); - } - return inserts; - } - - function firstEditableField() { - const readonly = new Set(readonlyFields.value); - for (const config of options.listConfig.value) { - const field = options.activeFieldById.value.get(String(config.b_field_id)); - if (!field || readonly.has(field.b_field) || field.b_type === "checkbox") continue; - return field.b_field; - } - return null; - } - - async function addRow() { - if (!optionValue(options.editable) || saving.value || optionValue(options.loading)) return; - if (!requireAction("create")) return; - const keyField = options.keyField.value; - if (!keyField) { - message.error("该模块未配置主键,无法新增"); - return; - } - const response = await nextIdApi(); - const newId = String(response.data); - const newRow = { [keyField]: newId }; - const orderField = String(optionValue(options.orderField) || ""); - if (orderField && identifierRe.test(orderField)) { - let maximum = 0; - for (const row of options.dataSource.value) { - maximum = Math.max(maximum, Number(row[orderField]) || 0); - } - newRow[orderField] = maximum + 10; - } - for (const field of options.activeFields.value) { - if (field.b_field === keyField || field.b_field === orderField) continue; - if (newRow[field.b_field] !== undefined) continue; - if (field.b_type === "checkbox") newRow[field.b_field] = 0; - } - options.dataSource.value = [...options.dataSource.value, newRow]; - newRowKeys.value = new Set([...newRowKeys.value, newId]); - await nextTick(); - options.getTable?.()?.editRow?.(newId, firstEditableField()); - } - - async function saveChanges() { - if (saving.value || !requireAction("update")) return; - const table = resolveSaveTable(); - if (!table) { - message.error("该模块未配置保存表,无法保存"); - return; - } - const updates = dirtyUpdateRows(); - const inserts = dirtyInsertRows(); - if (!updates.length && !inserts.length) { - message.info("没有需要保存的修改"); - return; - } - saving.value = true; - try { - await saveObjectApi([ - buildTableSaveRequest({ - table, - keyField: options.keyField.value, - updates, - inserts, - }), - ]); - const insertedSet = new Set(inserts.map((row) => String(row[options.keyField.value]))); - newRowKeys.value = new Set([...newRowKeys.value].filter((key) => !insertedSet.has(key))); - dirtyRows.value = new Map(); - options.getTable?.()?.clearEditing?.(); - message.success("保存成功"); - emitDirty(); - await options.reload?.(); - } catch (saveError) { - message.error("保存失败:" + (saveError?.message || saveError)); - } finally { - saving.value = false; - } - } - - async function deleteSelected(rowKeys) { - if (saving.value || !rowKeys?.length || !requireAction("delete")) return; - const table = resolveSaveTable(); - const keyField = options.keyField.value; - if (!table) { - message.error("该模块未配置保存表,无法删除"); - return; - } - if (!keyField) { - message.error("该模块未配置主键,无法删除"); - return; - } - - const pendingKeys = []; - const persistedKeys = []; - for (const key of rowKeys) { - if (newRowKeys.value.has(String(key))) pendingKeys.push(key); - else persistedKeys.push(key); - } - if (pendingKeys.length) { - const pendingSet = new Set(pendingKeys.map((key) => String(key))); - options.dataSource.value = options.dataSource.value.filter( - (row) => !pendingSet.has(String(resolveRowKey(row))), - ); - const remaining = new Map(); - for (const [rowKey, fields] of dirtyRows.value) { - if (!pendingSet.has(rowKey)) remaining.set(rowKey, fields); - } - dirtyRows.value = remaining; - newRowKeys.value = new Set([...newRowKeys.value].filter((key) => !pendingSet.has(key))); - options.getTable?.()?.clearEditing?.(); - emitDirty(); - } - if (!persistedKeys.length) { - message.success(`已删除 ${rowKeys.length} 条记录`); - return; - } - - saving.value = true; - try { - await saveObjectApi([ - buildTableSaveRequest({ - table, - keyField, - deletes: buildKeyDeletes(keyField, persistedKeys), - }), - ]); - const deletedSet = new Set(persistedKeys.map((key) => String(key))); - const remaining = new Map(); - for (const [rowKey, fields] of dirtyRows.value) { - if (!deletedSet.has(rowKey)) remaining.set(rowKey, fields); - } - dirtyRows.value = remaining; - options.getTable?.()?.clearEditing?.(); - message.success(`已删除 ${rowKeys.length} 条记录`); - emitDirty(); - await options.reload?.(); - } catch (deleteError) { - message.error("删除失败:" + (deleteError?.message || deleteError)); - } finally { - saving.value = false; - } - } - - function handleRowOrderChange(orderedRows) { - if (!optionValue(options.rowDraggable)) return; - const orderField = String(optionValue(options.orderField) || ""); - if (!orderField || !identifierRe.test(orderField)) return; - const rowByOld = new Map( - options.dataSource.value.map((row) => [String(resolveRowKey(row)), row]), - ); - const dirtyFieldsByKey = new Map(); - const nextRows = []; - let order = 10; - for (const row of orderedRows) { - const key = String(resolveRowKey(row)); - const oldRow = rowByOld.get(key); - const oldValue = oldRow ? Number(oldRow[orderField]) || 0 : 0; - nextRows.push({ ...row, [orderField]: order }); - if (oldValue !== order) dirtyFieldsByKey.set(key, new Set([orderField])); - order += 10; - } - options.dataSource.value = nextRows; - for (const [key, fields] of dirtyFieldsByKey) { - const current = dirtyRows.value.get(key); - if (current) fields.forEach((field) => current.add(field)); - else dirtyRows.value.set(key, fields); - } - emitDirty(); - } - - return { - dirtyRows, - newRowKeys, - saving, - readonlyFields, - reset, - resolveRowKey, - handleCellChange, - addRow, - saveChanges, - deleteSelected, - handleRowOrderChange, - }; -} diff --git a/code/fms/fms-vue/src/module-runtime/useModuleRuntime.js b/code/fms/fms-vue/src/module-runtime/useModuleRuntime.js deleted file mode 100644 index b0c6570d..00000000 --- a/code/fms/fms-vue/src/module-runtime/useModuleRuntime.js +++ /dev/null @@ -1,126 +0,0 @@ -import { ref, shallowRef } from "vue"; -import { loadModuleRuntimeConfig, invalidateModuleConfigCache } from "./configLoader"; -import { - createRuntimeKey, - invalidateModuleRuntimeCache, - resolveModuleRuntime, -} from "./runtimeResolver"; - -function optionValue(option) { - return typeof option === "function" ? option() : option; -} - -export function useModuleRuntime(options) { - const dataModule = shallowRef(null); - const activeFields = shallowRef([]); - const activeFieldById = shallowRef(new Map()); - const listConfig = shallowRef([]); - const formConfig = shallowRef([]); - const formConfigByFieldId = shallowRef(new Map()); - const queryItems = shallowRef([]); - const defaultSearchConditions = shallowRef([]); - const groups = shallowRef([]); - const actions = shallowRef([]); - const keyField = ref(""); - const loading = ref(false); - const error = ref(""); - let loadVersion = 0; - - function locator() { - return { - orgId: String(optionValue(options.orgId) || ""), - moduleId: String(optionValue(options.moduleId) || ""), - profileId: String(optionValue(options.profileId) || ""), - pageNodeId: String(optionValue(options.pageNodeId) || ""), - routePath: String(optionValue(options.routePath) || ""), - }; - } - - function runtimeKey() { - return createRuntimeKey(locator()); - } - - function reset() { - loadVersion++; - dataModule.value = null; - activeFields.value = []; - activeFieldById.value = new Map(); - listConfig.value = []; - formConfig.value = []; - formConfigByFieldId.value = new Map(); - queryItems.value = []; - defaultSearchConditions.value = []; - groups.value = []; - actions.value = []; - keyField.value = ""; - loading.value = false; - error.value = ""; - } - - async function load(loadOptions = {}) { - const version = ++loadVersion; - const currentLocator = locator(); - const currentKey = createRuntimeKey(currentLocator); - loading.value = true; - error.value = ""; - try { - const runtime = await resolveModuleRuntime(currentLocator, loadOptions); - const config = await loadModuleRuntimeConfig(runtime, { - orgId: currentLocator.orgId, - force: loadOptions.force, - }); - if (version !== loadVersion || currentKey !== runtimeKey()) return false; - dataModule.value = runtime; - activeFields.value = [...config.fields]; - activeFieldById.value = new Map(config.fieldById); - listConfig.value = [...config.listConfig]; - formConfig.value = [...config.formConfig]; - formConfigByFieldId.value = new Map(config.formConfigByFieldId); - queryItems.value = [...config.queryItems]; - defaultSearchConditions.value = [...config.defaultSearchConditions]; - groups.value = [...config.groups]; - actions.value = [...config.actions]; - keyField.value = config.keyField; - return true; - } catch (loadError) { - if (version === loadVersion && currentKey === runtimeKey()) { - error.value = loadError?.message || String(loadError); - } - return false; - } finally { - if (version === loadVersion && currentKey === runtimeKey()) loading.value = false; - } - } - - function invalidate() { - const currentLocator = locator(); - const runtime = dataModule.value; - const filter = { - orgId: currentLocator.orgId, - moduleId: runtime?.b_module_id || currentLocator.moduleId, - profileId: runtime?.b_profile_id || currentLocator.profileId, - }; - invalidateModuleRuntimeCache(filter); - invalidateModuleConfigCache(filter); - } - - return { - dataModule, - activeFields, - activeFieldById, - listConfig, - formConfig, - formConfigByFieldId, - queryItems, - defaultSearchConditions, - groups, - actions, - keyField, - loading, - error, - runtimeKey, - reset, - load, - invalidate, - }; -} diff --git a/code/fms/fms-vue/src/router/index.js b/code/fms/fms-vue/src/router/index.js index 13ccd251..cb113840 100644 --- a/code/fms/fms-vue/src/router/index.js +++ b/code/fms/fms-vue/src/router/index.js @@ -34,11 +34,11 @@ router.beforeEach(async (to) => { return getRedirectPath(to.query.redirect); } - if (to.meta.requiresAuth && to.path !== "/dashboard" && to.path !== "/") { + if (to.meta.moduleId) { const permissionStore = usePermissionStore(); try { await permissionStore.load(); - if (!permissionStore.canAccessRoute(to.path)) { + if (!permissionStore.canAccess(to.meta.moduleId)) { message.warning("没有该模块的访问权限"); return false; } diff --git a/code/fms/fms-vue/src/router/pageComponents.js b/code/fms/fms-vue/src/router/pageComponents.js deleted file mode 100644 index cfe2bfe2..00000000 --- a/code/fms/fms-vue/src/router/pageComponents.js +++ /dev/null @@ -1,20 +0,0 @@ -const pageComponents = Object.freeze({ - dashboard: () => import("@/views/dashboard/index.vue"), - base_othercompany: () => import("@/views/base/othercompany/index.vue"), - base_contact: () => import("@/views/base/contact/index.vue"), - base_otherdata: () => import("@/views/base/otherdata/index.vue"), - module_management: () => import("@/views/module-management/index.vue"), - i18n_type: () => import("@/views/system/i18n-type/index.vue"), - login_log: () => import("@/views/system/login-log/index.vue"), - user_management: () => import("@/views/system/user/index.vue"), -}); - -export function getPageComponent(componentKey) { - const component = pageComponents[componentKey]; - if (!component) throw new Error(`未注册的页面组件:${componentKey}`); - return component; -} - -export function hasPageComponent(componentKey) { - return Object.hasOwn(pageComponents, componentKey); -} diff --git a/code/fms/fms-vue/src/router/routes.js b/code/fms/fms-vue/src/router/routes.js index 16463c0f..08f6ddd8 100644 --- a/code/fms/fms-vue/src/router/routes.js +++ b/code/fms/fms-vue/src/router/routes.js @@ -1,5 +1,3 @@ -import { getPageComponent } from "./pageComponents"; - const routes = [ { path: "/", @@ -13,13 +11,13 @@ const routes = [ { path: "dashboard", name: "dashboard", - component: getPageComponent("dashboard"), + component: () => import("@/views/dashboard/index.vue"), meta: { title: "工作台", menuKey: "/dashboard" }, }, { path: "base/othercompany", name: "base-othercompany", - component: getPageComponent("base_othercompany"), + component: () => import("@/views/base/othercompany/index.vue"), meta: { title: "合作伙伴", section: "基础数据", @@ -30,7 +28,7 @@ const routes = [ { path: "base/contact", name: "base-contact", - component: getPageComponent("base_contact"), + component: () => import("@/views/base/contact/index.vue"), meta: { title: "联系人", section: "基础数据", @@ -41,7 +39,7 @@ const routes = [ { path: "base/otherdata", name: "base-otherdata", - component: getPageComponent("base_otherdata"), + component: () => import("@/views/base/otherdata/index.vue"), meta: { title: "其他数据", section: "基础数据", @@ -52,7 +50,7 @@ const routes = [ { path: "system/module-management", name: "system-module-management", - component: getPageComponent("module_management"), + component: () => import("@/views/module-management/index.vue"), meta: { title: "模块管理", section: "系统管理", @@ -63,7 +61,7 @@ const routes = [ { path: "system/i18n-type", name: "system-i18n-type", - component: getPageComponent("i18n_type"), + component: () => import("@/views/system/i18n-type/index.vue"), meta: { title: "多语言类型", section: "系统管理", @@ -74,7 +72,7 @@ const routes = [ { path: "system/login-log", name: "system-login-log", - component: getPageComponent("login_log"), + component: () => import("@/views/system/login-log/index.vue"), meta: { title: "登录日志", section: "系统管理", @@ -85,7 +83,7 @@ const routes = [ { path: "system/user", name: "system-user", - component: getPageComponent("user_management"), + component: () => import("@/views/system/user/index.vue"), meta: { title: "用户管理", section: "系统管理", diff --git a/code/fms/fms-vue/src/services/api.js b/code/fms/fms-vue/src/services/api.js index db8d8338..1ef3215f 100644 --- a/code/fms/fms-vue/src/services/api.js +++ b/code/fms/fms-vue/src/services/api.js @@ -1,4 +1,4 @@ -import { get, post } from "./http"; +import { post } from "./http"; /** * @template T @@ -94,7 +94,7 @@ export function saveObjectApi(requests) { * @returns {Promise>} count 为 1 时 data 为单个 ID 字符串,大于 1 时为 ID 字符串数组。 */ export function nextIdApi(count = 1) { - return get("/data/nextid", { params: { count } }); + return post("/data/nextid", { count }); } /** @@ -104,7 +104,7 @@ export function nextIdApi(count = 1) { * @returns {Promise>} 业务编号或编号数组。 */ export function nextCodeApi(moduleId, count = 1) { - return get("/data/nextcode", { params: { moduleId: String(moduleId), count } }); + return post("/data/nextcode", { moduleId: String(moduleId), count }); } /** diff --git a/code/fms/fms-vue/src/services/fileService.js b/code/fms/fms-vue/src/services/fileService.js index 366de246..5f6cb320 100644 --- a/code/fms/fms-vue/src/services/fileService.js +++ b/code/fms/fms-vue/src/services/fileService.js @@ -1,4 +1,4 @@ -import { post, get } from "./http"; +import { post } from "./http"; let storageType = null; let ossCredCache = null; @@ -53,7 +53,7 @@ async function uploadToOss({ files, father, moduleId, cateId }) { const results = []; for (const file of files) { - const idRes = await get("/data/nextid"); + const idRes = await post("/data/nextid"); const subid = String(idRes.data); const ext = extname(file.name); diff --git a/code/fms/fms-vue/src/services/http.js b/code/fms/fms-vue/src/services/http.js index 999bf0ad..f21f7995 100644 --- a/code/fms/fms-vue/src/services/http.js +++ b/code/fms/fms-vue/src/services/http.js @@ -87,8 +87,4 @@ export function post(path, data, config) { return http.post(path, data, config); } -export function get(path, config) { - return http.get(path, config); -} - export default http; diff --git a/code/fms/fms-vue/src/stores/permissions.js b/code/fms/fms-vue/src/stores/permissions.js index 48ae9851..2c739a1e 100644 --- a/code/fms/fms-vue/src/stores/permissions.js +++ b/code/fms/fms-vue/src/stores/permissions.js @@ -1,82 +1,83 @@ import { computed, ref, watch } from "vue"; import { defineStore } from "pinia"; import { loadDataApi } from "@/services/api"; -import { hasPageComponent } from "@/router/pageComponents"; import { useAuthStore } from "./auth"; export const usePermissionStore = defineStore("permissions", () => { const authStore = useAuthStore(); const loadedPrincipalKey = ref(""); const modules = ref([]); - const functionNodes = ref([]); - const pageNodes = ref([]); - const actions = ref([]); - const grantedFunctionNodeIds = ref([]); - const grantedPageNodeIds = ref([]); - const grantedActionIds = ref([]); + const grantedPageIds = ref([]); + const moduleCodes = ref([]); + const powerCodes = ref([]); let loadingPromise = null; let loadingPrincipalKey = ""; let loadVersion = 0; - const functionNodeById = computed( - () => new Map(functionNodes.value.map((node) => [String(node.b_id), node])), + const moduleByCode = computed( + () => new Map(modules.value.map((module) => [String(module.b_code || ""), module])), ); - const grantedFunctionSet = computed(() => new Set(grantedFunctionNodeIds.value)); - const grantedPageSet = computed(() => new Set(grantedPageNodeIds.value)); - const grantedActionSet = computed(() => new Set(grantedActionIds.value)); - const isSystemUser = computed( - () => String(authStore.user?.account || "").toLowerCase() === "g3soft", + const moduleById = computed( + () => new Map(modules.value.map((module) => [String(module.b_id || ""), module])), ); + const grantedPageSet = computed(() => new Set(grantedPageIds.value)); + const powerSet = computed(() => new Set(powerCodes.value)); + const isG3soft = computed(() => { + return ( + String(authStore.user?.account || "") + .trim() + .toLowerCase() === "g3soft" + ); + }); async function load() { const userId = String(authStore.user?.id || ""); - if (!/^\d+$/.test(userId)) throw new Error("登录用户 ID 无效,请重新登录"); const principalKey = `${String(authStore.orgId || "")}:${userId}`; + if (isG3soft.value) { + const elevatedKey = `g3soft:${principalKey}`; + if (loadedPrincipalKey.value === elevatedKey) return; + loadVersion++; + loadingPromise = null; + loadingPrincipalKey = ""; + loadedPrincipalKey.value = elevatedKey; + modules.value = []; + grantedPageIds.value = []; + moduleCodes.value = []; + powerCodes.value = []; + return; + } + if (!/^\d+$/.test(userId)) throw new Error("登录用户 ID 无效,请重新登录"); if (loadedPrincipalKey.value === principalKey) return; if (loadingPromise && loadingPrincipalKey === principalKey) return loadingPromise; const requestVersion = ++loadVersion; loadingPrincipalKey = principalKey; const request = Promise.all([ - loadDataApi("v_s_module_runtime", "b_canuse = 1", "b_module_id ASC, b_profile_id ASC"), - loadDataApi("v_s_function_node", "b_canuse = 1", "b_xh ASC, b_id ASC"), - loadDataApi("v_s_page_node", "b_canuse = 1", "b_xh ASC, b_id ASC"), - loadDataApi("v_s_module_action", "b_canuse = 1", "b_xh ASC, b_id ASC"), - loadDataApi( - "v_s_user_function_permission", - `b_user_id = ${userId}`, - "b_function_node_id ASC", - ), - loadDataApi("v_s_user_page_permission", `b_user_id = ${userId}`, "b_page_node_id ASC"), - loadDataApi("v_s_user_action_permission", `b_user_id = ${userId}`, "b_action_id ASC"), + loadDataApi("s_module", "b_canuse = 1", "b_id ASC"), + loadDataApi("b_user_module", `b_user_id = ${userId}`, "b_id ASC"), + loadDataApi("b_user_power", `b_user_id = ${userId}`, "b_id ASC"), + loadDataApi("s_module_power", "b_canuse = 1", "b_id ASC"), ]) - .then( - ([ - moduleResponse, - functionResponse, - pageResponse, - actionResponse, - functionGrants, - pageGrants, - actionGrants, - ]) => { - if (requestVersion !== loadVersion) return; - modules.value = moduleResponse.data || []; - functionNodes.value = functionResponse.data || []; - pageNodes.value = pageResponse.data || []; - actions.value = actionResponse.data || []; - grantedFunctionNodeIds.value = (functionGrants.data || []).map((item) => - String(item.b_function_node_id), - ); - grantedPageNodeIds.value = (pageGrants.data || []).map((item) => - String(item.b_page_node_id), - ); - grantedActionIds.value = (actionGrants.data || []).map((item) => - String(item.b_action_id), - ); - loadedPrincipalKey.value = principalKey; - }, - ) + .then(([moduleResponse, userModules, userPowers, powers]) => { + if (requestVersion !== loadVersion) return; + const activeModules = moduleResponse.data || []; + const moduleById = new Map(activeModules.map((module) => [String(module.b_id), module])); + const powerById = new Map(powers.data.map((power) => [String(power.b_id), power])); + const pageIds = userModules.data + .map((item) => String(item.b_module_id || "")) + .filter(Boolean); + modules.value = activeModules; + grantedPageIds.value = pageIds; + moduleCodes.value = userModules.data + .map((item) => moduleById.get(String(item.b_module_id))?.b_code) + .filter(Boolean); + powerCodes.value = userPowers.data + .map((item) => powerById.get(String(item.b_power_id))) + .filter(Boolean) + .map((power) => `${String(power.b_module_id)}.${power.b_code}`) + .filter(Boolean); + loadedPrincipalKey.value = principalKey; + }) .finally(() => { if (requestVersion !== loadVersion) return; loadingPromise = null; @@ -86,58 +87,39 @@ export const usePermissionStore = defineStore("permissions", () => { return request; } - function hasGrantedDescendant(nodeId) { - const ancestorIds = new Set(); - for (const grantedId of grantedFunctionSet.value) { - let current = functionNodeById.value.get(grantedId); - while (current?.b_parent_id != null) { - const parentId = String(current.b_parent_id); - ancestorIds.add(parentId); - current = functionNodeById.value.get(parentId); - } + function resolvePageId(code) { + let module = moduleByCode.value.get(String(code || "")); + const visited = new Set(); + while (module) { + const moduleId = String(module.b_id || ""); + if (!moduleId || visited.has(moduleId)) return ""; + visited.add(moduleId); + if (module.b_module_type === "page") return moduleId; + const parentId = String(module.b_parent_id || ""); + module = parentId ? moduleById.value.get(parentId) : null; } - return ancestorIds.has(String(nodeId)); + return ""; } - function canAccessFunctionNode(nodeId) { - const id = String(nodeId || ""); - if (!id) return false; - const node = functionNodeById.value.get(id); - if (!node) return false; - if (isSystemUser.value) { - if (node.b_node_type === "directory") return true; - if (node.b_page_mode === "custom" && !hasPageComponent(node.b_component_key)) return false; - return true; - } - if (node.b_node_type === "directory") return hasGrantedDescendant(id); - if (node.b_page_mode === "custom" && !hasPageComponent(node.b_component_key)) return false; - return grantedFunctionSet.value.has(id); + function canAccess(moduleCode) { + if (isG3soft.value || !moduleCode) return true; + const module = moduleByCode.value.get(String(moduleCode)); + if (!module) return false; + const moduleId = String(module.b_id || ""); + const pageId = resolvePageId(moduleCode); + return grantedPageSet.value.has(moduleId) || (!!pageId && grantedPageSet.value.has(pageId)); } - function canAccessRoute(path) { - if (path === "/dashboard") return true; - if (isSystemUser.value) return true; - const node = functionNodes.value.find( - (item) => item.b_node_type === "entry" && String(item.b_route || "") === String(path || ""), + function canPower(scopeCode, powerCode) { + if (isG3soft.value) return true; + const module = moduleByCode.value.get(String(scopeCode || "")); + if (!module || !canAccess(scopeCode)) return false; + const moduleId = String(module.b_id || ""); + const pageId = resolvePageId(scopeCode); + return ( + powerSet.value.has(`${moduleId}.${powerCode}`) || + (!!pageId && powerSet.value.has(`${pageId}.${powerCode}`)) ); - return node ? canAccessFunctionNode(node.b_id) : false; - } - - function canViewPageNode(nodeId, permissionRequired = true) { - if (!permissionRequired) return true; - if (isSystemUser.value) return true; - return grantedPageSet.value.has(String(nodeId || "")); - } - - function canAction(moduleIdOrCode, actionCode) { - if (isSystemUser.value) return true; - const action = actions.value.find( - (item) => - (String(item.b_module_id) === String(moduleIdOrCode) || - String(item.b_module_code) === String(moduleIdOrCode)) && - String(item.b_code) === String(actionCode), - ); - return action ? grantedActionSet.value.has(String(action.b_id)) : false; } function clear() { @@ -146,32 +128,25 @@ export const usePermissionStore = defineStore("permissions", () => { loadingPrincipalKey = ""; loadedPrincipalKey.value = ""; modules.value = []; - functionNodes.value = []; - pageNodes.value = []; - actions.value = []; - grantedFunctionNodeIds.value = []; - grantedPageNodeIds.value = []; - grantedActionIds.value = []; + grantedPageIds.value = []; + moduleCodes.value = []; + powerCodes.value = []; } - watch(() => [authStore.orgId, authStore.user?.id, authStore.token], clear); + watch( + () => [authStore.orgId, authStore.user?.id, authStore.user?.account, authStore.token], + clear, + ); return { load, + canAccess, + canPower, clear, - canAccessFunctionNode, - canAccessRoute, - canViewPageNode, - canAction, - canAccess: canAccessRoute, - canPower: canAction, - isSystemUser, modules, - functionNodes, - pageNodes, - actions, - grantedFunctionNodeIds, - grantedPageNodeIds, - grantedActionIds, + grantedPageIds, + moduleCodes, + powerCodes, + isG3soft, }; }); diff --git a/code/fms/fms-vue/src/views/base/contact/index.vue b/code/fms/fms-vue/src/views/base/contact/index.vue index 71a4e018..7118c7a8 100644 --- a/code/fms/fms-vue/src/views/base/contact/index.vue +++ b/code/fms/fms-vue/src/views/base/contact/index.vue @@ -4,7 +4,7 @@ import FmsModuleListPage from "@/components/fms-module-list/FmsModuleListPage.vu diff --git a/code/fms/fms-vue/src/views/base/othercompany/index.vue b/code/fms/fms-vue/src/views/base/othercompany/index.vue index 9d42e23b..e9bf5c0c 100644 --- a/code/fms/fms-vue/src/views/base/othercompany/index.vue +++ b/code/fms/fms-vue/src/views/base/othercompany/index.vue @@ -396,6 +396,7 @@ onMounted(() => {
@@ -418,6 +419,7 @@ onMounted(() => { + @@ -477,4 +479,5 @@ onMounted(() => { border: 0; border-radius: 0; } + diff --git a/code/fms/fms-vue/src/views/base/otherdata/index.vue b/code/fms/fms-vue/src/views/base/otherdata/index.vue index 5ce14937..5054dc83 100644 --- a/code/fms/fms-vue/src/views/base/otherdata/index.vue +++ b/code/fms/fms-vue/src/views/base/otherdata/index.vue @@ -1,54 +1,46 @@ @@ -710,14 +518,12 @@ onMounted(() => { min-width: 0; min-height: 0; } - .otherdata-page { overflow: hidden; border: 1px solid var(--fms-border); border-radius: 6px; background: var(--fms-surface); } - .category-panel, .data-panel { display: flex; @@ -727,7 +533,6 @@ onMounted(() => { min-height: 0; flex-direction: column; } - .category-tree-stage { position: relative; flex: 1; @@ -735,17 +540,14 @@ onMounted(() => { overflow: auto; padding: 12px; } - .category-tree-stage :deep(.ant-tree) { min-width: 100%; } - .category-tree-stage :deep(.ant-tree-node-content-wrapper) { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } - .category-loading { position: absolute; inset: 0; @@ -753,12 +555,10 @@ onMounted(() => { place-items: center; background: rgb(255 255 255 / 70%); } - .data-panel :deep(.fms-module-list-page) { border: 0; border-radius: 0; } - .data-empty { display: flex; flex: 1; diff --git a/code/fms/fms-vue/src/views/module-management/components/BusinessStructurePanel.vue b/code/fms/fms-vue/src/views/module-management/components/BusinessStructurePanel.vue deleted file mode 100644 index 9233a11a..00000000 --- a/code/fms/fms-vue/src/views/module-management/components/BusinessStructurePanel.vue +++ /dev/null @@ -1,1163 +0,0 @@ - - - - - diff --git a/code/fms/fms-vue/src/views/module-management/components/FieldGroupManager.vue b/code/fms/fms-vue/src/views/module-management/components/FieldGroupManager.vue index a656e041..a894b232 100644 --- a/code/fms/fms-vue/src/views/module-management/components/FieldGroupManager.vue +++ b/code/fms/fms-vue/src/views/module-management/components/FieldGroupManager.vue @@ -17,7 +17,6 @@ import { buildTree } from "@/utils/tree"; const props = defineProps({ visible: { type: Boolean, default: false }, moduleId: { type: String, required: true }, - profileId: { type: String, required: true }, groups: { type: Array, default: () => [] }, referencedGroupIds: { type: Array, default: () => [] }, }); @@ -39,7 +38,7 @@ const treeData = computed(() => buildTree(localGroups.value, { mapNode: (item) => ({ key: String(item.b_id), - title: item.b_name || `分组-${item.b_id}`, + title: item.b_title || `分组-${item.b_id}`, children: [], }), }), @@ -64,10 +63,8 @@ function addGroup(parentId) { const newGroup = { b_id: createTempId(), b_module_id: props.moduleId, - b_profile_id: props.profileId, b_parent_id: parentId || null, - b_scene: "shared", - b_name: "", + b_title: "", b_i18n: null, b_xh: (localGroups.value.length + 1) * 10, b_canuse: 1, @@ -126,7 +123,7 @@ function onMenuClick({ key, node }) { async function saveAllGroups() { let hasEmptyTitle = false; for (const group of localGroups.value) { - if (!String(group.b_name || "").trim()) { + if (!String(group.b_title || "").trim()) { hasEmptyTitle = true; break; } @@ -137,7 +134,7 @@ async function saveAllGroups() { } const original = cloneData(props.groups || []); const changes = diffRows(localGroups.value, original); - const req = tableChange("s_module_layout_group", changes); + const req = tableChange("s_module_field_group", changes); if (!hasChanges(req)) { message.info("没有需要保存的变更"); return; @@ -149,7 +146,6 @@ async function saveAllGroups() { req.deletes || [], ]); remapForeignKeys(localGroups.value, ["b_parent_id"], idMapping); - for (const group of localGroups.value) group.b_i18n = `meta.group.${group.b_id}`; await saveObjectApi([req]); emit("saved", cloneData(localGroups.value), idMapping); message.success("分组已保存"); @@ -188,7 +184,7 @@ function onClose() {