From b3364fe6dc525c97f90d930cd4b6204a4b8697ab Mon Sep 17 00:00:00 2001 From: oneao Date: Sun, 20 Sep 2026 22:05:20 +0800 Subject: [PATCH] 20260920220520 --- .../documents/分组编码手填与新建分组弹窗.md | 229 ++ .../fmsapi/FmsApiApplicationTests.class | Bin 537 -> 537 bytes .../fmsapi/config/JwtAuthFilterTests.class | Bin 8112 -> 8067 bytes .../database/OrgDataSourceFactoryTests.class | Bin 2363 -> 2390 bytes .../OrgDataSourceManagerTests$1.class | Bin 1973 -> 1959 bytes .../database/OrgDataSourceManagerTests.class | Bin 7388 -> 7332 bytes .../OrgDatabaseConfigLoaderTests.class | Bin 2498 -> 2456 bytes .../OrgDatabaseConfigWatcherTests$1.class | Bin 1740 -> 1749 bytes .../OrgDatabaseConfigWatcherTests.class | Bin 7213 -> 7181 bytes .../GlobalExceptionHandlerTests.class | Bin 3157 -> 3189 bytes .../service/ActiveSessionRegistryTests.class | Bin 4780 -> 4684 bytes .../fmsapi/service/AuthServiceTests.class | Bin 3828 -> 2557 bytes .../DataSaveServiceTests$JdbcFixture.class | Bin 3894 -> 2053 bytes .../fmsapi/service/DataSaveServiceTests.class | Bin 8884 -> 2931 bytes .../ExportServiceIntegrationTests.class | Bin 18831 -> 18900 bytes .../fmsapi/service/ExportServiceTests.class | Bin 15663 -> 15608 bytes .../service/ExportWorkbookWriterTests$1.class | Bin 1166 -> 1158 bytes .../service/ExportWorkbookWriterTests.class | Bin 9929 -> 10019 bytes .../g3soft/fmsapi/utils/JwtUtilsTests.class | Bin 2051 -> 2051 bytes .../snowflake/DefaultIdGeneratorTests.class | Bin 7684 -> 7463 bytes .../fms-api/tools/migration/PowerProbe.java | 93 + .../fms-module-list/FmsModuleListPage.vue | 8 +- code/fms/fms-vue/src/router/index.js | 12 +- code/fms/fms-vue/src/stores/menus.js | 46 +- code/fms/fms-vue/src/stores/permissions.js | 143 +- code/fms/fms-vue/src/utils/powerCodes.js | 59 + code/fms/fms-vue/src/utils/schemaJson.js | 59 + .../views/module/menu-management/index.vue | 57 +- .../src/views/module/menu-management/utils.js | 65 + .../design-editor/FormDesignEditor.vue | 42 +- .../design-editor/TableDesignEditor.vue | 64 +- .../design-editor/common/GroupCreateModal.vue | 111 + .../common/useStructureEditing.js | 57 +- .../views/module/module-management/utils.js | 39 +- .../src/views/system/user/UserPowerDrawer.vue | 60 +- .../src/views/system/user/UserPowerPanel.vue | 581 +++- code/fms/fms-vue/tests/menus.spec.js | 127 +- code/fms/fms-vue/tests/permissions.spec.js | 147 +- .../fms-vue/tests/unit/schema-json.spec.js | 71 + .../tests/views/menu-module-binding.spec.js | 130 +- .../tests/views/user-power-drawer.spec.js | 65 + .../tests/views/user-power-panel.spec.js | 389 +++ code/fms/sql/fms_menu_power.sql | 46 + code/fms/权限分配原型.html | 2350 ----------------- 44 files changed, 2313 insertions(+), 2737 deletions(-) create mode 100644 code/fms/.trae/documents/分组编码手填与新建分组弹窗.md create mode 100644 code/fms/fms-api/tools/migration/PowerProbe.java create mode 100644 code/fms/fms-vue/src/utils/powerCodes.js create mode 100644 code/fms/fms-vue/src/views/module/module-management/components/design-editor/common/GroupCreateModal.vue create mode 100644 code/fms/fms-vue/tests/views/user-power-drawer.spec.js create mode 100644 code/fms/fms-vue/tests/views/user-power-panel.spec.js create mode 100644 code/fms/sql/fms_menu_power.sql delete mode 100644 code/fms/权限分配原型.html diff --git a/code/fms/.trae/documents/分组编码手填与新建分组弹窗.md b/code/fms/.trae/documents/分组编码手填与新建分组弹窗.md new file mode 100644 index 00000000..554f03b9 --- /dev/null +++ b/code/fms/.trae/documents/分组编码手填与新建分组弹窗.md @@ -0,0 +1,229 @@ +# 分组编码手填(新建分组弹窗)实现方案 + +## Context + +**问题**:模块管理页(/module/module-management)界面配置设计器里的分组节点 id(形如 `group1`)由前端自动取号,用户既看不见也无法指定。这个 id 同时是分组在配置内的稳定身份、运行时归组的键、以及多语言资源键 `group.{模块编码}.{schema类型}.{id}` 的派生源。将来页面代码要按分组引用时(例如把同一主表的多个分组拆到不同 tab,参见 g3hd 旧系统 `detail.vue` 里 `groupName="提单"` 的写法),技术人员不知道哪个分组对应哪个 id。 + +**目标**:新建分组时**必须手填编码**,创建后**不可修改**;弹窗同时可填分组名称。 + +**已确认的四条决策** +1. 新建分组必填编码,不再自动取号;创建后无编辑入口。 +2. 弹窗字段 = 分组编码 + 分组名称;名称填了直接完成,留空则关闭后仍进入现有的「创建后立即内联命名」。 +3. 编码格式与模块编码同构:`/^[a-z][a-z0-9_]*$/`,长度 2-30。 +4. 校验必须含**同配置内唯一性**(已有分组 id + 从多语言表反解的保留编号)。 + +**为什么唯一性校验是硬要求**:`normalizeGroupIds`([schemaJson.js](file:///d:/workspace/code/fms/fms-vue/src/utils/schemaJson.js#L126-L138))对重复 id 的处理是**静默改号**(首次出现保留、重复的重新发号,已有单测固化 `group1, group1, basic` → `group1, group2, basic`)。漏过校验时用户填的编码会被悄悄替换,其多语言键随之漂移;设计器内部还全程用 `b_id` 当 Map 键([useStructureEditing.js](file:///d:/workspace/code/fms/fms-vue/src/views/module/module-management/components/design-editor/common/useStructureEditing.js#L37-L58)),重复会导致分组互相覆盖、字段渲染两遍。 + +**明确不做**:不给分组加 tab/area 建模(含子表的 tab 是页面编排单元,不是分组分区);不新增任何「编辑编码」入口。 + +--- + +## 改动清单 + +### 1. 校验纯函数 — [schemaJson.js](file:///d:/workspace/code/fms/fms-vue/src/utils/schemaJson.js) + +在文件末尾新增「分组编码校验」区块: + +```js +/** 编码长度上限:键 = group.(模块编码≤50).(view|edit).(编码) ≤ 93 < s_i18n.b_key varchar(150) */ +export const GROUP_CODE_MAX_LENGTH = 30 +const GROUP_CODE_RE = /^[a-z][a-z0-9_]*$/ +/** 结构树用 'g:ungrouped' 做虚拟「未分组」节点 key,真实分组不能叫这个名字 */ +const RESERVED_GROUP_CODES = new Set(['ungrouped']) + +/** + * 分组编码校验,契约与 ui Form 的 validator 一致:true 通过 / 非空字符串为错误文案。 + * @param {unknown} rawCode + * @param {{ existingIds?: Iterable, reservedIds?: Iterable, maxLength?: number }} [opts] + * @returns {true|string} + */ +export function validateGroupCode(rawCode, { existingIds = [], reservedIds = [], maxLength = GROUP_CODE_MAX_LENGTH } = {}) +``` + +校验顺序与文案: + +| # | 条件 | 返回 | +|---|---|---| +| 1 | trim 后为空 | `分组编码不能为空` | +| 2 | 长度 < 2 或 > maxLength | `分组编码长度需为 2-${maxLength} 个字符` | +| 3 | 不匹配 `GROUP_CODE_RE` | `分组编码须以小写字母开头,且只能包含小写字母、数字和下划线` | +| 4 | 命中 `RESERVED_GROUP_CODES` | `分组编码 ${code} 为系统保留字,请换一个` | +| 5 | 命中 existingIds | `分组编码 ${code} 已被本配置的其他分组使用,请换一个` | +| 6 | 命中 reservedIds | `分组编码 ${code} 曾被使用(多语言资源中仍留有记录),请换一个` | + +实现要点: +- 复用文件内已有的私有 `textOf`;入参用 `new Set([...ids].map(textOf).filter(Boolean))` 归一,**同时兼容 Array 与 Set**。 +- 大小写敏感比较,与 `normalizeGroupIds` 的 `claimed` 集合语义一致。 +- **不传 moduleCode / schemaType**:长度上限是静态常量(推算见上方注释);view/edit 编号本就独立,传 schemaType 会引入"跨 schema 比较"的错觉,调用方只传当前 schema 的那一套。 + +第 4 条(保留字 `ungrouped`)略超出已确认范围,但必须加:真实分组若叫 `ungrouped`,其树 key `g:ungrouped` 会与虚拟「未分组」节点正面冲突([TableDesignEditor.vue](file:///d:/workspace/code/fms/fms-vue/src/views/module/module-management/components/design-editor/TableDesignEditor.vue#L237-L240) 的删除判断会直接忽略它)。 + +`nextGroupId`(96-105 行)**保留导出**:移除两处调用后它只剩单测引用,但它是老数据 `group{n}` 编号规则的唯一可执行说明,且删除需连带删除 `schema-json.spec.js` 里的 4 条断言,不值得。 + +### 2. 新增共用弹窗 — `components/design-editor/common/GroupCreateModal.vue` + +与 `FieldLibrary.vue` / `EditDataView.vue` 同目录(被两个设计器共用,满足「全局/共用样式至少两处使用」的项目约定)。 + +``` +props: open(Boolean) / parentLabel(String) / existingIds(Array) / reservedIds(Array) / loading(Boolean) +emits: update:open / confirm({ id, title }) // 均已 trim +``` + +- 标题:`parentLabel` 非空 → 「新增子分组」并显示父分组名;否则「新增分组」。 +- 校验用 `Form` + `FormItem` 的 `rules` + `validator`,validator 直接返回 `validateGroupCode(...)` 的结果 —— [validate.js](file:///d:/workspace/code/fms/fms-vue/src/components/ui/form/validate.js#L92-L106) 明确「返回非空字符串即错误消息」,与 [ModuleBasicPanel.vue](file:///d:/workspace/code/fms/fms-vue/src/views/module/module-management/ModuleBasicPanel.vue#L36-L56) 的写法同构,无需手写 `validateStatus/help`。`rules` 用 `computed` 包一层,两集合变化时自动重算,输入即报「编码已被使用」。 +- `onOk()`:`await formRef.value.validateAll()`([form.vue](file:///d:/workspace/code/fms/fms-vue/src/components/ui/form/form.vue) expose)→ 有错误则 `return`(不关弹窗)→ `emit('confirm', ...)` + `emit('update:open', false)`。 +- Modal 必须显式 `:fullscreen="false" :resizable="false"`(两者默认 true),写法参照 [ModuleRuleEditModal.vue](file:///d:/workspace/code/fms/fms-vue/src/views/module/module-management/ModuleRuleEditModal.vue);点确定只 `emit('ok')` 不会自动关闭,由 `onOk` 自己 emit。 +- `watch(() => props.open)` 打开时重置两个字段。 +- 两个输入:`分组编码`(`placeholder="如 basic_info,创建后不可修改"`,`@keydown.enter.prevent="onOk"`)、`分组名称`(`maxlength="100"`,与内联重命名一致,`placeholder="可留空,创建后立即命名"`)。 + +### 3. 表单设计器 — [FormDesignEditor.vue](file:///d:/workspace/code/fms/fms-vue/src/views/module/module-management/components/design-editor/FormDesignEditor.vue) + +1. 删除 `nextGroupCode()`(569-575)与第 54 行的 `import { nextGroupId }`(否则 `pnpm lint` 因未使用导入失败)。**保留 `nextGroupXh()`**(b_xh 仍需用于画布分区顺序)。 +2. `reservedGroupIds` prop 保留,用途从「取号时避开」变为「传给弹窗做唯一性校验」。 +3. 新增状态:`groupCreateOpen = ref(false)`、`groupIdList = computed(() => groups.value.map(g => String(g.b_id)))`、`openGroupCreate()`(`props.loading` 时直接 return)。 +4. `addGroup()` 拆为「打开弹窗」+「确认创建」: + +```js +function onGroupCreateConfirm({ id, title }) { + if (props.loading || !String(id || '').trim()) return + flushEditingGroupTitle() // 与原 addGroup 一致 + const newGroup = { + b_id: String(id), b_module_id: props.moduleId, b_parent_id: null, + b_title: String(title ?? ''), b_xh: nextGroupXh(), b_canuse: 1, + b_layout_direction: 'vertical', // 必须保留新分组默认值 + b_colspan: GROUP_GRID, // 必须保留 + } + groups.value = [...(groups.value || []), newGroup] + if (newGroup.b_title) return // 填了名称 → 直接完成 + editingGroupKey.value = `g-${newGroup.b_id}` // 没填名称 → 原双重 nextTick 内联命名 + groupTitleDraft.value = '' + nextTick(() => nextTick(() => sectionEditInput.value?.focus({ cursor: 'all' }))) +} +``` + +5. 模板:第 1422 行按钮改 `@click="openGroupCreate"`;在现有「分组设置」Modal(1805-1865 行)旁挂 ``。 + +表单画布只建顶级分组(`b_parent_id: null`,现状不变),故不传 `parentLabel`;数据里已存在的嵌套分组其 id 也在 `groupIdList` 内,唯一性天然覆盖。 + +### 4. 结构编辑内核 — [useStructureEditing.js](file:///d:/workspace/code/fms/fms-vue/src/views/module/module-management/components/design-editor/common/useStructureEditing.js) + +唯一消费者是 TableDesignEditor(已确认),故可安全改签名。 + +1. 删除 `nextGroupCode()`(537-542)与第 23 行 `import { nextGroupId }`;`nextGroupXh()` 保留。 +2. 删除选项参数 `reservedGroupIds`(417 行 + 402-403 行 JSDoc):内核里已无消费者,保留编号改由宿主直接喂给弹窗。 +3. 收敛为单一创建函数,去掉 band 对象依赖: + +```js +function addRootGroup({ id, title } = {}) { return createBand({ id, title, parentGid: null }) } +function addSubGroup(parentGid, { id, title } = {}) { + const gid = parentGid == null ? null : String(parentGid) + if (!gid || !bandMeta.value.metaMap.has(gid)) return // 父分组已被删除:静默放弃 + return createBand({ id, title, parentGid: gid }) +} +function createBand({ id, title, parentGid }) { + if (loading()) return + const groupId = String(id ?? '').trim() + if (!groupId) return // 域函数边界:不落空编码 + const newGroup = { + b_id: groupId, b_module_id: moduleId(), b_parent_id: parentGid, + b_title: String(title ?? '').trim(), + b_xh: nextGroupXh(), b_canuse: 1, b_colspan: 48, // 列表侧无 b_layout_direction,保持现状 + } + emitGroupsRenumbered([...(getGroups() || []), newGroup]) + if (newGroup.b_title) selectBandAfterCreate(groupId) // 跳过内联命名,仅选中 + else focusNewBandTitle(groupId) // 原行为 +} +``` + +4. `focusNewBandTitle`(574-580) 不变,调用点改为「仅 title 为空时」;新增 `selectBandAfterCreate(gid)`:`nextTick` 内 `if (bandMeta.value.metaMap.has(gid)) selectedNodeKey.value = 'g:' + gid`(不调 `requestRename`)。 +5. 删除 `onNodeAddSub()`(778-782)及 return 清单里的同名项(1002 行)—— 它原先只做 `addSubGroup(band)`,签名变更后不可用,改由宿主解析 key。 +6. **不保留自动取号兜底**:兜底会让「人工编码」与「自动号」同时进入同一份 JSON,静默改号的风险重新出现。空编码只做「不创建」短路(有 bug 时表现为「点了没反应」,比编码漂移安全且好排查)。 + +### 5. 列表设计器 — [TableDesignEditor.vue](file:///d:/workspace/code/fms/fms-vue/src/views/module/module-management/components/design-editor/TableDesignEditor.vue) + +1. 弹窗状态(沿用同族 `sectionSettingsKey` 的「null 即关闭」惯例): + +```js +const groupCreate = ref(null) // { parentGid, parentLabel } | null +const groupCreateOpen = computed({ get: () => groupCreate.value !== null, set: (v) => { if (!v) groupCreate.value = null } }) +const groupIdList = computed(() => (groups.value || []).map((g) => String(g.b_id))) + +function openGroupCreate(parentGid = null) { + if (props.loading) return + const gid = parentGid == null ? null : String(parentGid) + const band = gid ? bandMeta.value.metaMap.get(gid) : null + if (gid && !band) return + groupCreate.value = { parentGid: gid, parentLabel: band ? band.group.b_title || `分组-${gid}` : '' } +} +function openSubGroupCreate(gidOrBand) { // 兼容入口 2/4 传 band 对象、入口 3 传 gid 字符串 + const gid = gidOrBand && typeof gidOrBand === 'object' ? gidOrBand.gid : gidOrBand + if (gid == null) return + openGroupCreate(gid) +} +function onGroupCreateConfirm({ id, title }) { + const parentGid = groupCreate.value?.parentGid ?? null + if (parentGid) addSubGroup(parentGid, { id, title }) + else addRootGroup({ id, title }) + groupCreate.value = null +} +``` + +2. **4 个入口全部改为打开弹窗**: + - 工具栏按钮(473-475)→ `@click="openGroupCreate()"`; + - 结构树右键(`onStructureContextMenuClick` 263-266 的 `add-sub`)→ `openSubGroupCreate(band.gid)`(band 存在性判断已内聚进 `openGroupCreate`); + - `@node-add-sub`(549)→ 不再接 hook 的 `onNodeAddSub`,改为本地 `onNodeAddSubRequest({ key })`:`key` 形如 `g:`,`!key?.startsWith('g:') || key === 'g:ungrouped'` 直接 return,否则 `openSubGroupCreate(key.slice(2))`;同时从第 204 行解构里删掉 `onNodeAddSub`; + - 属性面板 `@add-sub-group`(573)→ `openSubGroupCreate(selectedBand)`(`selectedBand` 为 null 时必须不退化成分组创建,`openSubGroupCreate` 已挡住)。 +3. `useStructureEditing({...})` 里删掉 `reservedGroupIds: () => props.reservedGroupIds`(178 行);props 本体保留给弹窗用。 +4. 模板挂载 ``。 + +--- + +## 不变量与边界 + +- **编码创建后不可修改**靠三件事保证:① 弹窗只在新建路径打开(5 个入口全为 create);② `b_id` 只出现在 `createBand` / `onGroupCreateConfirm` 的构造里,写回路径(`onRenameCommit`、`onBandTitleChange`、`commitEditGroupTitle`、`commitBandDrag`、`renumberAllSiblings`)只改 `b_title` / `b_xh` / `b_canuse` / `b_parent_id`;③ 本次不把编码加进 Inspector 或「分组设置」弹窗。 +- 「未分组」是虚拟分区(表单侧 `section.key === 'ungrouped'`,树侧 `g:ungrouped`),不参与创建,仅由保留字校验挡住同名冲突。 +- view / edit 各自独立编号:[index.vue](file:///d:/workspace/code/fms/fms-vue/src/views/module/module-management/index.vue#L253-L265) 的 `reservedGroupIds` 已按 schema 分桶,`existingIds` 取各自设计器的 `groups`,**不跨 schema 校验**(同一编码可同时存在于列表与表单)。 +- 填写名称的新分组**不再进入内联命名**;留空的仍进入,与改造前一致。 +- 空分组、嵌套子分组均不受影响(子分组编码在整份 schema 内全局唯一,不分层判定)。 +- 正向副作用:编码一落库,`collectSchemaGroupItems` 自动按 `group.{模块编码}.{schema类型}.{编码}` 生成受管多语言行,默认译文跟随 `b_title`,无需额外改动。 + +## 测试计划 + +在 [schema-json.spec.js](file:///d:/workspace/code/fms/fms-vue/tests/unit/schema-json.spec.js) 追加 `describe('validateGroupCode')`(沿用该文件的东西、纯数据、中文 it 描述风格): + +1. 空值:`''` / `' '` / `null` / `undefined` → 非空文案。 +2. 长度边界:`'a'`、`'a'.repeat(31)` → 长度文案;`'ab'`、`'a'.repeat(30)` → `true`。 +3. 正则:`'1abc'` / `'Abc'` / `'a-b'` / `'a b'` / `'分组'` → 正则文案。 +4. 与已有冲突:`validateGroupCode('basic', { existingIds: ['basic'] })` → 含 `basic`、不含 `多语言`。 +5. 与保留编号冲突:`validateGroupCode('group2', { reservedIds: ['group2'] })` → 含 `多语言`(与第 4 条文案可区分)。 +6. 合法自定义:`'basic_info'`、`'cw_fee_detail'` → `true`。 +7. `group{n}` 形式不冲突即允许:`validateGroupCode('group7', { existingIds: ['group1'], reservedIds: ['group2'] })` → `true`。 +8. 集合类型兼容:`existingIds: new Set(['basic'])` 与数组结果一致。 +9. 首尾空白归一:`' basic '` 命中冲突;`' ok_code '` → `true`。 +10. 保留字:`'ungrouped'` → 保留字文案。 +11. 上限可覆盖:`{ maxLength: 10 }` 下 11 字符报错、10 字符通过。 + +**回归**(会经过被改文件):`schema-json.spec.js` 现有全部用例(尤其「重复 id 后来者重新发号」—— 本次风险的反向锚点);`module-table-edit-panel-dnd.spec.js`、`module-list-config-panel-dnd.spec.js`(`group1/group2` 期望值);`form-edit-panel-mount.spec.js`(新增弹窗在 `open=false` 时不得渲染、不得引入递归更新);`module-i18n-reconcile.spec.js`。 + +## 验证步骤 + +```bash +cd d:\workspace\code\fms\fms-vue +pnpm test # 或 pnpm test:file tests/unit/schema-json.spec.js +pnpm lint && pnpm fmt:check +pnpm dev # 起前端 +``` + +手工路径(模块管理 → 选一个 data/virtual 模块): + +**表单设计器**:工具栏「添加分组」→ 弹窗标题「新增分组」;点确定停住报「不能为空」;填 `A-b` 报正则;填已存在的编码报冲突;编码 `basic` + 名称留空 → 创建后进入内联命名;编码 `extra` + 名称 `附加信息` → 直接出现带标题分区、不进入命名态;保存后切走再切回,编码未被改号,重命名只改标题。 + +**列表设计器 4 个入口逐一验证**(都应弹窗、确定后编码即手填值):① 工具栏「添加分组」;② 结构树节点行内「新增子分组」;③ 结构树右键「新增子分组」;④ 属性面板「新增子分组」(未选中分组时点它不应弹出顶级创建弹窗)。 + +**交叉验证**:列表里用过的编码在表单设计器里仍可再用(view/edit 独立);新建分组后切「多语言」页签,出现 `group.{模块编码}.{view|edit}.{编码}` 行且默认语言值 = 分组名称。 + +## 风险 + +**最高**:`useStructureEditing.js` 上「删 `nextGroupCode` + 改 `addRootGroup`/`addSubGroup` 签名 + 删 `onNodeAddSub` 导出」这组联动是全案唯一的破坏性接口变更,且它服务 4 个入口(其中 2 个由 StructureTree / TableInspector 事件间接驱动)。漏改一处的后果是「点添加分组没反应」或模板调用 `undefined` 抛错,而单测只覆盖到 `schemaModel` 层、抓不到 UI 入口。缓解:创建逻辑收敛到唯一的 `createBand`,宿主与模板不再出现任何取号逻辑;4 个入口 + 第 204 行解构 + return 清单在同一提交内改完,并强制走一遍上面的 4 条点击路径。 + +**次高**:弹窗唯一性校验依赖宿主 `existingIds` 的实时快照(`computed` 保证与 `groups` 同步),若将来出现弹窗打开期间改写 `groups` 的新路径,已通过的编码可能瞬时冲突 —— 目前遮罩层挡住画布交互,不存在该路径。 \ No newline at end of file diff --git a/code/fms/fms-api/target/test-classes/cn/g3soft/fmsapi/FmsApiApplicationTests.class b/code/fms/fms-api/target/test-classes/cn/g3soft/fmsapi/FmsApiApplicationTests.class index 16e04d81c048d6b6f1e53410307ca618a8b30e76..ba7577c47e24d2bd38fb7e408545b3384c3debb4 100644 GIT binary patch delta 112 zcmbQqGLwb()W2Q(7#J8F8AR9_m?qk2F|#wUOcYlVWMp7*&QD3@Vqj+A z&8W->+7#Y}sEPWu&2ollS&cHEwHlq$NKadBK6#&xglTR=jOlDyc1pwq{ B4#xlh delta 110 zcmbQqGLuF8)W2Q(7#J8F8AP}km>8JZ8CZB2SQ*$RS}6h9920r9*clmEobyvsH|BCP zDzgLCfB=XDs$yVd-~_VtfixpXL~A<(L@&m;{Y6K=TGD!dcx$zFg diff --git a/code/fms/fms-api/target/test-classes/cn/g3soft/fmsapi/config/JwtAuthFilterTests.class b/code/fms/fms-api/target/test-classes/cn/g3soft/fmsapi/config/JwtAuthFilterTests.class index 3818e35d0f7fbf16feffa3a5ecbb25e6f992d282..b68ad01cb8c1ab5a2e01ad24a36e50bacff25624 100644 GIT binary patch delta 3605 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 b7hkKFP#=Hkr#&=4mU-){jnFC&W2 zPPf6tig*{0^5F&Ks)DG1fGkT_?p{z76!3ym1XNJO8zPDq(DzHzQF!3-;qi~;yyv^T z@B90`w{ua;?JX@2zkd8)0If7^zyYTQmp~433Em;|pc#ysv4LP`-;mXB6LQx@ViCKU z;H+7)Q-e;Z8h@b5Z$N`H4{itz`2rryBGk&qoO5KGE8o>)CGGKBSF`2z-%~ml#O2)FqAw`pd1zQ3C9weFJE(1S5>O*YJxG6h>uta zJ7OgbEW%<9H3CaeOIRjH@{44*bDkV^hWwSN7YHagF6Fw0AYtLOYx4{=D1FNb`p_YJ zhaHKsHaAuXti&oAcC65JtS0zP+3G0D)8Wyume9x+8sr5z+JaMAZC7V^kB%00DHt|w zCM<4-4K$%u(YRikx$~vS(bw!$Ya0bNp@QH^T6VXUI2g6;F4G>=v4xP|I%*Hb6Or>x zI}(riI3yu~({YB>b2sTtXcy=}r`(adDxhN<+Z|0>2_4-mZE4)PQO6EKUeZb?`EtNC zP>EiFGqFpKYkPGs&K5WayXC7|X#HM7*^s&4Ot7=8Lpc%L9EsXiV#A;ri5W1_r=edU z42w`0PYeWy41d_02Q(n9KAP#9bEN~vqmzU&K$UE|u>wEAXfeY|n`C?v=?#BBB zF2wug0@rHJhX#B=?R`*gGxn94_^`l7a6V_{K`Rm2cbCp26p zFpkTKQ|LNRv8-_C$$)#C=L&pM!>0s3t@2_CZ_Sf&x0uaHBFRm|BQ~qL5?5*XtiaW* z?IHK#8qSab%kE4JggCfY;97y})aBervSTzFHQ>b!0-wX@Wtj-+m*Pfc_a+$?D?B&j zOB%i`@D<#mszMpNOytWa#k%I(IO@|Oi`#=%!obbAUEmINlzrK3Mw6C~ug!>TpQ^Q^ z0x2AmFXwNXGr{rikB2P-lek;MJp%XQ7~>s(V73f+^akD@;S5vT_X|9L2MIZ5G8E%r zaSewIT#9cfU%yER)`a8%Q6ca4_&wjocQiaK@Ljc`oUScqCzzcBqh_=xu5#oNfk*Lu zSuv}LgZh{X#pBYP)l+TYhxn0(9}D~hPZEkE@!(K09y?_9+422WEO`1MJ9)V`;5mVx zY52LoQ}_j;RC>Loa)-BN!3jJg@T^*MM8Z0L#r|>zM#65Kz;go6<2N$p?Wn{H_??Cq z1%8h|5DGG_%8m?M!B8w|n=xBf7u4kOp_c^yf|uovf|_}F1%K1;Ye&W*=_a5V_|MtS5lRrYNbr5 zT8t#D=}e_XDu|b>BQ=7Ss3ZB=BRjbGL*enR291+s1Lu%DLO01roW1fvSEIZn_X@c- zKk6|y9qzYAR7*-~v{44NmGZ{?mGbDU#qv*u>x+lF03=_6Q&EdYVSuLN z*2`Cl)|6<}#ZBamQPMM*h#xZhqLys%H!anP+m^oGG80y!lF4eDl}l%G3#MIy&d9u- zZO}QiTcbVP3~8?%@h_HF`>*ww2K7V|<2TT9*5#nU7|TsrWr7uX zS&{E33g4=zil}cQ`k`N+gx`XTfwcc?If0GN{Yv3V5ywu9hKCi;>__V z!XT|>_3Kz|4p9?7K&LXBPGyl+v&9@tjBiiITj$t5;1N9Rab%0LA z2|Ue@U;4IB;MdME4)1S^@W&MXoW@@#@K4@){Z2xPa?>Ox$mhTW@z`w3!f&63-=WZF zn5k#r83H&P-av-K&Qsr)%-4~(H?BE$mIxVkLJenX(;|&v-ITa0_>@t(b#Q#vM^S qb)DL&g99;Lu$^*SMPbERT)I7UF88#Z)XTpCR$}nlAPrFzCI1BkB>#T^ diff --git a/code/fms/fms-api/target/test-classes/cn/g3soft/fmsapi/database/OrgDataSourceFactoryTests.class b/code/fms/fms-api/target/test-classes/cn/g3soft/fmsapi/database/OrgDataSourceFactoryTests.class index 6f18a08a038583f2e4336bd97dd64d8a8ab3dd38..50c335c909248d4a7ea296ebc63961d2ffc45813 100644 GIT binary patch delta 923 zcmX|9$xjne82`Q5-gMd`9o7-BECNa?Fo*>zAd8?NAWCI(V}J(H#uD0qw;oJ986$bo zn@7XN3zvd6(HllD-aH!p54@^T{H7Y`&CIuNzxU3Qas$pNJsu);LUtR`FG<7mqZ1xC`)n8?x7#BN_qA@Yw6PsiMnVINwkvzWByv2lu{ol%k=t%1sQ z;IufDnmnE8=ICMwiqCSXn2KDr$AI`K_vki;d^m?; zQKZ!CojC7?ToC}zY zF{sOlWQw4j!5qgdL(v^)(`ijO_f}hDw>Oeo&Qc=o!#oy5yBd-iZ1F{FAg~{Zu+~-J zpH4f=cPE_nnCmCzxSe5iOYS4lM^^;~PFyQd=4zVupzr3*2K`+#;DML^zP}Dc{UiYe zWR-A)IM=I?C?#UcB)JKnJGmqkb)pp9*S6?iH%?@Q({asjVl*! zXzq`2&885WXySr}iCg2=pW#M4Q;3N($vNlV?>pz*^WB*rX4$O&_UF@Q0Fy}Tkl^5u z4Jc5H@lKUPV;C%YJH!Xoqv_x_>=Few)ULy!!NZ{E_OrS4em;}7ba+wCVH&uE8u6VC z3sstBbz)Hpu?F!@intmX2#U+ue`rc9k16bkDkYDYuC|#p{{CB*!fS zQ@DMe9dt6N87sMekQTq>E}Ik#Aa39eVx&mMil;KW@syR<5x}g0yHFXF?cJ=E=17Qk zwNBSDhXn)ouqa|`FR6OJ=#FrjdMdkK`}lu(T3DuNoER(F{W-z zTxzZ{apRV)BrqhBNZk5A+`H19dhS4+0udf4+PLP{bn+jbTXI z`amet$6+uO=+ecf?8mh==lQ$&VwfQ~QmIyIqw>B!o(O{>A`n2(gpLLQjz$JO+r1<| z=|3YT;%Mb)VaO3rhiqhKKp=snY+;#j3hf+i3x+#8cbLi5Ew*(fxG2e z$$nN=a?T2zLy|PEJF5zFL10*6f`nOAsW?UiE-6T8-7Py+zs|yCfiZ=PDBS;i9OD8L z3LJdmR5r?vqP#LBFr~1~gq`%$6RWoC9!Rh-BQUGRWT-is$l{vPe_a;!q|Q;2?Rtyc zHeSfLMyIp_`*kr}t(G01qU@5aQrWG|thqHB7`=lw$6_(H0*W*`93k>yiU!H6jMt&A_s$mailrooHj<`T*QBPSR*PB38>%>Sq*owjCOTR;T|4P%A`(e Lcl#C`XCU delta 910 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 literal 7388 zcmbVR33waD75>L|w2HitL~#m zHZ27Ty<2+Gma8o-&;q4EK^Rhw-jvdl-q+Xrpa(taK@0t7SCS=LPVCyBwX2zVGw=QL z-v8c=pL+3;#{jg6#sJDtuAoB2EcgWGj%Y`OBtc4=4gYOt%tg;p&fkh zHPco^@6?jokZ$$qw!@_ZNqsciA}~-_N{do$_2HSV*6PO+0o9C0n^Kx>kD69gs(+@6 z8Y~m29JLHbZ#R=MV@O~@Rt*>FUdJ+$L#_4weyk9v=-b)b7r-jiDp;*z4b}=Y&rB78 z(}r|s91nr5rPd2a%tT0w8-s)}6t&VuG?X&UcxZ=w9hatI^#WDtq@`<-VQnz3=M9`q z0~=MGg(g}a(~Nje)^d7;hiRdK5{{6Hb<)vh52h2TPU$ex)D0>&N|5H5Ne`j+VY;AB z05{`o1zS{{gL4IznbuH`@R_(TOgBhLWb26ctbm zdzTisb-HWa3>RI69u+T_e<uOWNY`95HP0V;`N1F`W0hT-Wsh9Ke8rgDNh@B}L@b zH%wh8h|c-b8pKT0Aab)XkL8 zr*LBWmN_bE(A&dW_d5x^oy*8(ny?vfQSny1jj(7gJ4r%v$0fHD@S(cWE^^A5r_d

&N`_tPFR$& zYSF6vB@(X+bA5?IwiVxw#ry{!|Q8%atbjzFu*A)qJOKzv4leT1H9V2U+ zo_weUa2v|y$46D%DWgKQb;nDT3|iT7w~CM99>xPZ_UbW)-EjM`9yu%*w~lIb|6HUL z)o83ar1ZTi?vv7IxYCYc>Cqmh1vjbe(Bs-M1)mUDKP|02wZf(r)v4M2DEA}7ygbeu z0gT~c1&^pWflqQ)>0EY7RzNX}q|3VLa7YqjDD4>W5RHkm45hQmYHlB!82*0hxy#Vw zQ}MXWIu)|SRq!c+&8IYSa*0{HK7c3i8CgPq*7f)QV-9yG zR!OwJ!0q)2*#ps!FAB_)4&tr6iv@#e9rNQ$0zGLfzHKBr7-_MOF!NeRb*m*5TEAgS z^E&>nZ&|--vfZEmVbXA_+FEte9ut+oRJd>LO+@KqIG!`F*4s+V$@EMlgW z&>Tlr8*A$hmO!?kDVq@$JVWOyX7cWop6qs6NHYGnRD2uX$yLd@wJs^i?gjI+OtgCS zBWayn)0)*Kp9;oTAmJ1*?q;JqwEuJvqq$eu-Zx__d1P;I{(Hi*+NmmNA%i^pH$;eXNk_ zpE1i!j2Ev9f2ZR2_yZFvV<)WHPNx|)Vul{|76_jN={%q%W-bY=K=ob{ zG&S!xY(pl>WYTop6vNE^zXG$SrFl1}kZWVvNLD~>)rks_hB(%^P8O1tNTjila{ zP7LZ+pG>XtSYa}q_iL6Rzh@gOoMD4AZ=PX-myBX_vE>Cvn=XCSW3WA}8}076ON6W= zV}@nZAy`99@65#7C*hq!1=h__FRFA}ucJi{cWNouLIwZHRYLB0DxY#mtofvED|o4B zRK7=G=@ght1i0ENk8iv~SMR9dM22;{&mk@&C+wN#aWlo0n8(L6*yb#QQ+RHt?hKpJ z9!+LG1_GV9Hm)TGqgw4rfUI3dWoKT^_7kjZ3gNy8)O1gEXVNSgiDcGA_vqAR^ii?7 zJSjRh&#Ng4{`E7APED=i;ai%J21P}BpU2b!o)<_;T59^_@ zYaXZ2^1xNn_VI5|7uJheA#cSCwPcj#e{I+_52sVD1$iKwX9`-11pAJq^upoO@AQ^D zxoqJPF*~z73iOiMxW#HmGO1f`-K*P*SV@{HSx)C;+AB^cohlSjOXrx5D7VU;tl&tZ zh_wQ%C+$TkSQWv`o|=;nWzcwgfaN?!mhr45|9F;!z#sYMhpL3(#LhmUENx6I^UPFwFZl@jGrq|i%iF<9N0E^g!GVc`9GW-VFqCBLUe& zc+TdxE&MzeThW4cUeSd4b^zz&GF*TWUd^Q0dW=_M$N9X9&ug&L1+{^8*I<7Z)OKtT zWrVj}B9VtTJrUmHMezEkgtt5gFP`Bt(&NG<;C045t`Np)de}A^lk>1Jh|Bo z58!C<6%XTh8IN^WWkGlZuLr-rIe1+L?~uXu-Z9*i!TU?bS(p~(P&k|aO#m(;3Ijyp zAW^u4E^;YRI7AdQc~3`dqQpd}RForij*Jzc(VR!)U>=R#6Vd3MCK{5nJlc7QXvi2F zI2G;WqN-6wPL^=SqbeUlIm($BHytz${M+k33(04Mpu`DEf}ogmjTGtT2+7Ch1&G6o z>NH(rlmv7+B}dWE=K)5IBp)wM8neJ`MpZse2J#SfOoXVX2%@Rtq)h%_h5;{W{dq#A z@OkJ2ZaeT0?&xZgzLCL4n&<#`HPQ5spTPa>d7!1Dx+3@>spg^TiVPk-fhP_y6@0p7 z)(Na)|L0nK)xO~8t1ILHU+~H5S>B5$;k%b>cp1av^9 zJU1X~zwBxo_>`;p8{9lxFteB54;9ShcL7A+gcndD=1J{EP|W93>ivYM;u}{M)hHJW e#6tcpXUk%KTOn47)xO!hdU1cm8c`=2Q2jq9DhJa5 diff --git a/code/fms/fms-api/target/test-classes/cn/g3soft/fmsapi/database/OrgDatabaseConfigLoaderTests.class b/code/fms/fms-api/target/test-classes/cn/g3soft/fmsapi/database/OrgDatabaseConfigLoaderTests.class index b771450edb62f595197c32dde0dea97387236186..8dd518fedc8d7b174de6a182fd4b54a76219db9e 100644 GIT binary patch delta 822 zcmYLHOHUI~6#njXIx`HnQin%HDG#AgW{QQ1DDqM&Dgp(Shxi^~~*dyq%nm4o=KDlj+sh)uK|4mn-eF$=P%#gC|@+${@!VrW_p}_&D;Q ziy8S5D-&IEAb;3NrQ1{#GeiaRMoV=R!IuYyeH>mWuDhS(0LoUkExDYbiK0T{t;~Zu zg79gmV$c_t78aeQ^sJN8QH_v_8ZpC~y&Scu6KkxuIEZ=<6FM<*d8vpn#Kej>rW@4M zA&w{-MT@kc#L%qbuy`R=)9WK>5uc>#;4y}R?X}JAr}w@+yZhtelke;6IxHOLXhXXg z^VDiFbaHgzgt+f%6~E=IPn)-IPfXc0jSMZ}2i{tjvFK0?c1{c_B>@fn4C+XtFEKcl za2rW*3}Q%p_6&#_rCZZ6f-x1N;-eBGB9|~Oekotf0tLf<=sL-BjpH=V5O9?M2Hu-Y zI!p0+JC$-$Dkj8N)nrp5Uu#&V8BmXmKGKt{N;lVPWZgeB5AyQq7A2dDGdI9r5+Gp@ zoo+DTLjj$9CjfrzB@Zy@vGhM>At7#}=ag6`+rEXOp79No^jqdD1TrYyLXiB5frwFQ zWpOZ)LD;Y&n`i*CXnF@LVH|D9pf!t*T>>;X@ly*dmmx$_S+or?RFL+SIZEbEsehC- zG^2{vlokjz=ti9A#fao7^uXiSUC{_ROXOsV>CMrtBgAb{HI2Ru26hYYbRNu=kz9@* eoToSm7jTiRE5>EQuHZUu;3grik~Svcp!6@*Ffl%$KKS4tAWi|(Y-Z^lHuZiv*!c*c52_3S z8VOnjI*1I0g~;QGKM_gJ`@_)%HI`!#yW+`su7^R>piT_|;X5nQ*yWI?p9ySI!ok^wDOfIfwWmKa^ zLal;2oa8e?8($TMEN0jhoGReb7BEO?Ud`?h#zVuQ@MK7a8J!Bc(9QqqM)E6tI&FVyPR)OR#agL#BatHW zKiX%kHos=&FPKgyz=_DC=v>6fgUl^DTZp1Vl#QmW!B>iS0=RL5z?$CF2h6N|;t~5BK?V(Z%13-z*HuP%^1z`V*0C zR;9WT1{XgNtvoD=Wm#u1m3p|CmJnk^^>KJPON%}_fSq~>#4jVEos#Y$OC;S(T0=rl zg}h5-`AHY@be=7+cVr0ksE(3>Qd}lmIZ8l32FL>j>9TNmd7X-BDblrzWoc>)B|)#} z1LRE@c3~#Ja>Qq}dbd&K+eE$5;oE@|*hbSH93kVG=1sJ0qpjG~0X-En)87IEtgz8n zk18~vhE`jTPMWL$$aB>67?2)ql(&3^P}->eRa_%<0mvA_b;9?5^kKjt7kEF$9w1#9 zT<W>!j$xc+hC(u>%_539 GRQ&^STA|$l diff --git a/code/fms/fms-api/target/test-classes/cn/g3soft/fmsapi/database/OrgDatabaseConfigWatcherTests$1.class b/code/fms/fms-api/target/test-classes/cn/g3soft/fmsapi/database/OrgDatabaseConfigWatcherTests$1.class index 0c37f74f2981628288000c981905259c282373ce..109918f7aab78cb563a13a1e1cdd12e120310b5a 100644 GIT binary patch delta 769 zcmX|9O-~b16g_V~+v%$eDAfWjAC?M~7C{gw2r8g}P$l>g7Mct&i)tOPGZIhy_ujd4-nr-A_ddnH#O;s2-o68H0V^gHhH-IQ z&54xCBbh-H8bg5+owW&Dd{<^;#$?$m2U84_J>sV_F1D3uOB4(-4jqOC6>$y|M;O$e z-kV}q@kGD+#4vH3AuFb}Y=eaa;?lHmw4$x{EMuh6VWPeEp`degqO10!eJ_CwM>pCS z4BriIR4W$JIK|O}E{4cM->rJieOJ=-apWW}CUO7sY2-Ny5^QW%%R6pWLQiuHN@x>7 zXT$7F#qs^pA=4R-VX0gzdwMxAaaO!wnaBvnWJlxT8%rf87@F@oJI;Xrpfa$!vNZQ- z!`%+bTb}r;J{7OkY-4;ia5nBOJKNWt^@?lalGxSOv?~mw|K>_fE~6xx^voV#@I1E~ zig11EtK|B@{FWb}gGQ>-p)lK)bEQb3WgFZxj=E~z4msIZOJJ%>(V9b?!G^$h>5bEtYuW6g_Wd;L%|ywAv5)VT)4A*MQa54@#QYN~M)znpO?%YDy=H;(!cRcQr9_ zX+qx0U*J|Zkl3W|)va;m|8S+=hsKx8J@1}(?!D*D`xf{bnE3qb<0*g}xT8aX%ApzX zz{`;7gd&}0XgFVUYV8#UHJ-S~;bVx2pDY&C!J*3FH(($j_LS&g5Fw5s1H(AOkm`g| zqNmL4bBq`mMS#KU+U>iox{e^m4TPo2_sq3hj`hTrttJgjNu6Kn{_{bcGcY5?-osYy z$Zkn##K3u)x}c7k98m*v(y}J4a?CS~JhG0gtoyW{-PtY`UR3R7yVh_x5@Jt{#^OlH zOQspd_?iUE9ShO#0Ds$>>3r z@FMN5gZ*WaJ+Ke`;{}RdqJjIkL~-rap4_;Iqd}TN?7Y5`N@1w4d;+R8Zqi6 z{Uo7_Y47BGl{gC-9MHhSzPm-5m4S{NmZ8b1AfIB}8>y^P7KZYhN;D37ur-b#r&nYg lF@N9wfPy@!R_N1Epr~LSxB6KTo7n0zMU)A3@?;Xy`U9REb}#?{ diff --git a/code/fms/fms-api/target/test-classes/cn/g3soft/fmsapi/database/OrgDatabaseConfigWatcherTests.class b/code/fms/fms-api/target/test-classes/cn/g3soft/fmsapi/database/OrgDatabaseConfigWatcherTests.class index f6a9a4173740772978c8448812efb864c5409780..27facbf75f24ee75b4d59607a8447fd0a5276e67 100644 GIT binary patch delta 3106 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 diff --git a/code/fms/fms-api/target/test-classes/cn/g3soft/fmsapi/exception/GlobalExceptionHandlerTests.class b/code/fms/fms-api/target/test-classes/cn/g3soft/fmsapi/exception/GlobalExceptionHandlerTests.class index c4ed5132bc366793c33aa4827cba81bd6e063c0c..62a855e6c1d165a51191d5713a39e4e69fcba2ba 100644 GIT binary patch delta 1290 zcmZuwZBHCk6n^f$&CKo;*kQNjb$4N*WubILEJ|OXl%gUmZ3U^TRvdN*I(6J#nB9V} ztF13B`i0y-Vq>zqk;M49F;ScNMPs5rpot&!tMS}f0tqpboO{o?=bZaI&$;tU+dpkB zkN*AkI{;JoN`i-BjQ=5x$V;YEawPb6PxOWveA9)T8H5*#3O@pTTH58m%HshE8pDVv z2n7*_A|c8!$`kVFu#6K_ckSxpl8knSX5A=Sx=}9O((S^!l@B6CTIuG0 z`J_Z@Swf5R3`aF)**+{ui_+5@fCJ2 zkiw9JLH>mGcdIy!VM354!}V{VGRj*6!V^@3+gIg4i{ zj4|p#8GOqVC5+EultHnanT@hxFBO7F;hc)|xWIq+BttqTMENAcB)`NWJnkK4Q#|cW zD$|&eaD^e|_B!j0G>cx7qW%RHFJhKI^d`FI7@AzRwvk`gmkU--;7p623;c=qf^rQn zOSsO^d&O+I?zj-Xlj7OD|G>tbI&d@YE`I^#tPR3gdp;c#Pwd`1V z({W_1QdPN_GfJk69K*53KCxz4wn_Ki#F`Lxo&V^&Ou||!ZsTp*p}KUHX^^l0H)dXiv} z$kSM zXvej-8amQLd#H40w0I3Yr>p2&7_MTVjx%BQcNNdvAaA^mi`u0sE(_=R@9|O{^Hp3G zfV?iCLD#lGroSeFRz%6h$i``(L>orXjtO*N4xKLO!3LOp&@!%6u!L6#Z-k!IGHwuq zB)zLwk)aD-qia+;YeZgmC3@rkSh6kxAXx4kvb>qmRy4!E2O^jyf;E`wA!2#!fTi>^ zv8)`>v=3?K>nLiDwo*lTpST-&Uo(zGKJ?d7ITHI!%ZkXMMicH(;sh~4bb4q|y)>RA uDb|M${#z(HC&t-OX6HbeyZge#VB-tiCt>=qc0`y)i)*CO>cNW?xyJvT1p7))1pXZsG{j>Ym?jt|` zeQy`QEWT6`K#(J(A&f|Eekj7BFpSsQ$Js~NjovK$pE60(fWaAHJ!6YbSjy)DZrO{dm5Iz=MT(|?#DQ7Ai(S@5i~GjPSQ zY_q7M1Ko1sBVr-Yqi`J6a16b|4&06RV}N5&!w`}TL*k$CP%m*Pl#3=qa3nRaVi=Ed zoX{|W6hptbpmd4Fpeil}XXB$d$uXwkF&Rqxp?kqD;t;jm^Vh0|y;fGykHADJ*DWzH|8lI`(0z+)C|Ioq(tV#>l7|!_Ki%~vER<3KG{! zh=t)Vo`Sb1)pCmFO%-p$;V5gU;2N!P%ZPc$GfU={h((7rm(mMH)ipV)wb&G=bv#Gw zSi4pGt`d$g5o1%ri;Rfr=+E08?MitC>7jiIhU?VJP|GBeZh(DDod9moTZRD*H~qJt zfOqgNjez$k8t^CYB1AW^cpp61K$KcFr)zq=f%c`*`{>B&T@4&LSw~Oy>{uOrO&m|K zKk7(t&?wWygg#lvX*rnr0naw^Tpi~niJ(uSj}8ma4h0F?>%XZT+mcRt@00s<6wKik z-Y2@9IE@dmP1KS|;Wj>`Dt9Pfqu3sDmynMLk>Wn47Wm|W6sFKuq?DbOl!ctWsAt2z zj1`j6!1l{aqa@~)>;oP^Q1Jl1_!nrK@jL(k diff --git a/code/fms/fms-api/target/test-classes/cn/g3soft/fmsapi/service/ActiveSessionRegistryTests.class b/code/fms/fms-api/target/test-classes/cn/g3soft/fmsapi/service/ActiveSessionRegistryTests.class index 6310143e8574a3802cf85fcfe3a8ab610df75df7..acc383b6bb71bb5f38ce3b80a5a1db2583951766 100644 GIT binary patch literal 4684 zcmbtX`&$&(8GZ+W* zV%lixg|yLDYnqrQn%FdIY5=v?{?tBCpFaHy`Xh__C$vwWrtg{AWfxqnwp?cCeCM3+ zd%yR)oU?!V_l-XQXu=H{c@pZwG5=t_WenKwZf!NA$4fZwlM`h!)Z;OE+RW zv_ajn&HX)^Wm_`xB`nyZj;Q{q8XNR?_wUidwgiuvS|wp!@bUBpBosF4G2PxOVP0ib zuY~+&Bck~*4@C-Ocw`hysCzu6J}4;if>;$4pin|dP>*R{iQ#_D>{0upnuOAz5muwU zs;P@{N|6V1&9@7Vh8#>~TX``AMGzN9j7LTf7&Dw5iBf62WQf#&vW=I{@n!;ft z5hHOGJNTgwYrVRq6Gl@kX4tAt+KFOW&@c!6dlKY^&$w=DreB0fSPOViL8e})Z*TEn zDJk3;YTfa2lLyNscr7<_wW^?Dg@UK>Rfb3vQB`fazNIpl31P@K_1IwGQ6yEp9<0jJ z-!Jq(tzZqFp?=+R^XC81zuSi@td&tMVTJHrwV0Cj9)H*{HOKWPMfG zC(DB^5|qLEp-62i>K-r=3%YySTX%Tyyo6%I9@5P8tb}d%-aMWhyPO<5aqq(E=}*s3 zpF28z>d4*86Vq2;pMLFwV?osp5Bbo6plCRqtgFh7s$L(u z@l6@qB|MpPd+RnjO0 z+GB`_9Z>M9psOGpH7uT?2k}E0KbTb+%uZuegjm5Lyv9;gBau)d9%n^cqMR3J9}Y8% z1L9yVaB5z{)46?}lVLm(v{Z>mSd)9p+#;Zj0o}C7=?^_kc=}qf;bCAJvs9)+7}&}lTpY61 z@ZL@}?%XY8{A||E$u&-nqUvyeL|x58sNq31GpkRfD>pl3c#?JC=L&w|#9kd$bz5Z7 zSp~nuIi^0lZ9?m2r7RLBWQKt>+zgEM?5ZInVTQFfUEGpZJoqkHC-l%KDzU}s8t>HX zAtPe>@Bu!Q@oNbyGnVw&h_P4m2OVd*P#xfDY?%8Me1r=UiiR^H34hH_!PHJydAk~m zL^W%5&@lEU;t#*H%-Yz)U}i6W^hwv`!}n%$n$j#9U5Hn*lDq@bGdz&f3gandReb z=44E!k2NQcaxG*&#O5lUcPPPti;a+)f4bl--NPj0`tE> z$yLho@C2Xpo&80~M=?rJL_Ofu(bjE(7m0L+;*1P4}XL0UE!))K;6N?6MY>q){|krUR|oUpEutvtFGnekt+m5NAk zZJNKd=?4e|sdJ|4`dk60(wj55R}u@binv#k$Ti~o05+kL-!!XuHL6A!YaJgJx!6+F zmh*n~H7C}<*K>Mfrefci_C`uu&kAr;4q0ltJ-~H*D-R6??)bau zapBr=yj)SR5dEJcavgi}@khkRVQ!Qw3hx(s%4KGAxn~?BedY2QcuJRDL_z7w2Cq9> zDMrg0d}($nycxG=WWKbluCF5hPuNhAe;xZJTtsE382eJ=n%iA9e}<>F^CF6CYCnRf zrgj|Puep7dAuGcb+@{0D_#1x(Euy3wSk&Ox7%xLzhRqCrBkCE*O$>XGX{(|cQMBLy zT5*JQo+Fi4(2gk{Rf6`HobpblX!qjpcpZmHcp15S13#jbRXC0x<4x9TJ=?$$z7Uf)TA^5(;2)r^R867eX zI)A%l?B$=thSMbDr~g6`pW^LHI#@7gt=M_+pHMh|(ChsNqy>4RkJD%-cSHwkrYoW* z%NhLB>9~k4#zH!Cg%fUz^f`;RaB)LcT9&)~P&M2BBdT_5^gh+kQ}x#SRWsLr<$!n( h@AFfTc9F7A@F_lX-Y(;F{0`SRCl0GGFu?#-{0F`E@sR)k literal 4780 zcmbtY{Z|y%8GZ&AW?4r?1RMN_R*CW<%!v?BL8SXwoz$ zri~_krBPe0X=0jcV$-Op0o0oG^z`(Hp5(s})IXu;^wd6gW_DR{HJYer_ulv3`@YZn zydU?j|Nh^#uK?6yG=MBjli^d4jT{Mied?eZjH&USU`tn@7O^GdRO@lwu8}aUq_jhZ zU&6{rJlM0=GP>+tc}?Epw^~YmTtsbwI1EF&AkE0n3jv_P-M(d zFcY&RtbH^U?V4p<68xs8Qo^e6qiG2FFh)%5|U_?k^z+G+cRTLFrlB z8?Ep{=f@fe*)8pj^{sw9DPe|T_iEN*M1Vm~%;Wl)XjsdF-_BrJ7;o3Qm5Ig3XU zrm4m4V4abO+nbD`cv!U~y#Z9BS`^9C%%YN}(vAS0!Lu@I6>LPEgvT@LsNbVS61HKI z`24sw)S&OtqV2t=rbf4tSP7-n&cUCZe@k?S+4a88nBu6BWjF?UnzOS8gQz} zOWdjUCVLyY52`vER=Z+aXgZqkoQ$6+2xE(c;s<)~`M~g5iLQQ~iC5zl)IBJY`tM`E zAz>#>jbuou7ZKU2;HRQ(&%4Sb|eFXf}l9=uyxsx`Cow&4yjSJE6wf4H2WB7{VUX%c@IgE!`64ik>rJ)blFDtgbVnbYfOmk890|{w~dI7hqU9VS@?Zp_;mw zdqSVxtFx+}`d%f#&c|xh%&^g;^O_onsS(ZEYHEYJk+8hmtHCha)PAit;-r>@qE@y8 zrb~xz>4Z@mj~ljXyG?b0(?t6c%%osnVnDYw(`me-;ON-~i;A|fDK|x%;+ypDZcQ|p z7PCi<>o2=H8da+)9@Pdke#C7L0{z=~=dHtI$1aW?J96jT$??z5j-NR=e&WFGizDNg z-xz=Wqp^<<-~8L@+ZW%zz3?}JXJzS{J^oXw?ANNb?c_ZlwX)R;k^PDtw3Y1C7>D30n)4uH~^)c5@x!K z6T+M!Js#IgkqZ;4<6XqGDYBW1e$8vQ5|(8)Keem!tiz0iCo&(H(iWM%laq1pKr$iM zWLDrhT%86xnejEuj2UgV8rii)9dNQ*#&0LRz+4A;XV>o$?1)^Gae7kCrdA|W$JG9= zs9O9z!dARmLe>3RaPhRUT+F5@tke&_%f~%J_(M?yi+0_@syw z+TwjOutl?bji{A}k0ccESuv3u?&~!)D;pmN`1Jgfg7ICli-{K`6Y&a* z<{nNmxhG>d`95;eW)fzP2{r_m3oK?3vUq{B*2PZ+j{M>}M-C->IjX@lj)1(f5lCg% zA@3ML?w1H$rYs9D^Hcn!0k!xr1FvwTxcl)7yviByOX|vUbiK+wLRVec6$JA0^7Hcx zhA}ranU_A{%7RfW9>%iH^bM|TMdyV1xdaQ$rty5tLjfPIg?u8;O@mVBL3xe)gj@Ug z4ZQvUSivNc)x@|qIT3)ll7!&E@-`%8Q(?^`toekskgy&ntVQ1m>x~qwD`ZPB8%_By z*zzI}E>H58G<_d|Aa%;pvQAgPrR3oW+)JGJFD33`61j|jmt#Fj_}jG1Z6R8)pV=c}GRbeIyfMAlS0}xZlGf7#{8R?n`lKv9==szJ1ThDzh;KDQSd+$V zb;f}Iidd$JJj}}B>0F-w_Btwe_(l*4XOE(0%Zjq{YuE%_MPnA4omsOttMZi(qotw% ztzV%1Dt7wtcf2%=u65Z(zI%l^h19pWFlQLuorT$_;Lk5OkL>(;RkAyqCuRjz{v;Je z@`R5mnLjC8*16(eC|+?Dx`gwX-^^J-ayGrnS5f2}M!e$d%M8d&Zn(nN*894u^1KO7;x|m1_h>;RlxyL5%EED4IKj1T@ZUv0WEs`%)qb9I5;tn*AMBi;i?EJ!|u_#EnESqWf*(x~RcIhTlwvCf_Zb z#3e4Ix4@^lxhAbR3Ozr0-19SKkVbevKG2BU0PitUNh4JcXM{BV!GZVzKIFF`_|KG` X#UF9b8-2pj7hDUFg}>rUjG*ZMQoTjG diff --git a/code/fms/fms-api/target/test-classes/cn/g3soft/fmsapi/service/AuthServiceTests.class b/code/fms/fms-api/target/test-classes/cn/g3soft/fmsapi/service/AuthServiceTests.class index e9bfccb42b1b690f3e3adae1f2b0cbc6ef1987a3..6ab1928791a00ed89b9d435183190f1f44385718 100644 GIT binary patch literal 2557 zcmeHJ-A@xi5T9*JzbJwrB8s}AVhehW(I+7!6>Fj?YG}duc)f1ht#`YZ-EEP-%tRA? z_m48p^;+7*t2PL4zTD2<&ir=vxAU<-et!7|041@5&Yz71X;>n0=TIo7rQ2f->PXM$u5Ivm)K{|ZGEYCF*-Wj? zDEEA+40VJRvTKFxTHSyt>qWiK7 zc~cSFj@vXQo-Nes*^1l9RW@_3uD%jYrkHRTZAwK=n`_ENPdefC$R_oeY0C!nr1LRf zvn;DXn@W19$@-koeO%y3kzxp>17CwBk7M|XR;aS)+FI`nx!b)^8ZVam;TisNm*?xm zw0vb@0hV(x2UoF*1OkDr zsft0r%wa{C(aaqB1Xd5a!f=lram{gyQjn|ta6-iOid3y)r;DWnzdkoi6@%*eoMHH} zvhB3D({P)_)~Bc!|1&16r(^Z$EP;0xo9U^)xQrq2xXl|4CaPVZsZztkeuS;kHd5`` zK|AE)JvZ&L=bMvvSb&bDYGAJ|e{z@~^d9WYcL^+w(&Ti7z}kc`4MH6Qbb|o?()7(n~DcB5_qFx6nc!)2J;T;gj?T2-_hTTj91?=AVs7XK! zW*~`D3{sHpj~U4J$63hX7--GGCDfls=_$?$j@hrU@L_TBGhF$Ovly)4H`!l_!!?w1 zp%$#db({g*K!g~qp%iG{#PN5uTM=k$k!Tdr;&2C15{R~bihC~-_kIMf6@eRr2WTII NJUqfNiBbW!egVg|9;N^Q literal 3828 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 U_=v_bWv)WMkKqb@GRb7+AN7nW#Q*>R literal 8884 zcmc&(d3+S-d44`gXqIKL#b794Y-A%q5=hHo3|Jf%AdEmlpp}G$jmcoL>O*=Wqgw-sj=n)7o&i}51@3&urj zwOnJs$O|Y#dZu7>^b0JmZLMpYGNLJtrC6rmW);hEi@*juKag;8dD9x`&+A#^jGaG~ za1F;z6kIct=rsB>)Y-JHF1B^E$q`UZYsfxjv>Wb8J1uaqHnNS6QB$NP>B_~M?u#@9 zgQDUEO);#X;{6^N5|%h_!|e)Is;I##frXwvu9-CwZFWk}5M6_S*;IE~ZEI^u;fVvN z@*31CsFSeO3*0acN}FvBaMjuaQBh=LlY(^4AdY*DaP#WpEc-K>`sm}@#Mrwe+f%hpK$CKcPUL!g4#W&~2TZL=kI z(+rAqoy^;3^uCPY>rmsplMj2ifo!I68nH{o-E=)@<7j!~tlh%`1GUpb^tLO{AW?Gw z?WFm%_#T0EQ}i{?ri`3)fW+>CV_Jsew3J%nXu&=O_o R?fXH?31>WUgWe|j;mWK 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 diff --git a/code/fms/fms-api/target/test-classes/cn/g3soft/fmsapi/service/ExportServiceIntegrationTests.class b/code/fms/fms-api/target/test-classes/cn/g3soft/fmsapi/service/ExportServiceIntegrationTests.class index b2ce9c11508eac7542befa25e966020e97420f1a..42ba670b9bf0531a3f342fba482cc89a6893e629 100644 GIT binary patch literal 18900 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 O9>w!9wO2i!>HdERB%JyH literal 18831 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 bsfX45FngQvd;rgFc($VcLG`eDgtGq^XEu6h diff --git a/code/fms/fms-api/target/test-classes/cn/g3soft/fmsapi/service/ExportServiceTests.class b/code/fms/fms-api/target/test-classes/cn/g3soft/fmsapi/service/ExportServiceTests.class index 47e20269ab023d7c13542b3655a9eb696cbd8581..67525ed0553808139bf7a0682cae97473f41f851 100644 GIT binary patch literal 15608 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 literal 15663 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 CAW(1s literal 1166 zcmb7DTTc^F5dNmL-Cnj(YZdW=w~B2MR*=X8gv1bx#9EA)lK6JIEakv<581OV{3stZ z(HMO&KKrALv%A$cf)Cx~+-7IK^UZf=e*XIQ9l$dzXOX~!fuxBP(u5V;vpP?-ItZ+T zt`;ZK(vIJkwqtFbohU!pQ~q&VspCCg29E#M(Sfck5zM}DE1^XM$|sZ?N1`vRULaj- zdthD5A%m=eoQXV4LNRi2h1aom+DDEZ5Yp??lfedIqFQU^F^MSyMH81$B9zV}5{yHk zo6Z@Ba>TnZqHN*{t`d@7WG+{0F_n>f)(l)DOb+oHQU?aE6IRDczVb$$?y8<|JA~=# zMH$yxOk`8-v%^$FdQP*~ZL?^i?XsoRP_}Se!k6KBU`z&wQWKsw#$qyfhBX%Tl@lDQ z{nx@5U5ABdtn9Arcp~We;hlfvh+Zbln`7qA8Q0Hiw!x0MINRqkq(A%LdRPpV<&3vH z+f`b6otTq>8ew_-yiCmF5!Z&VPW8?K;pM2*Q3|#3)Ed4U=agx33BOR9OUT^zJjdU3 zh1QNX@R+bL7F(A3Z$;n^w^as&8Bnnpwib8W1dlv!@OqPXE9{ZrHCLbi461*D@sUpn z+~PUSk-$UD@@z)?9BwlvaF<7lkYdn#yiG*Exk1<|2Bp}&{t5II1;YEtYY1Au0F=0A z?lV@1u*&h_<%JmGGT{fNKl}q?jerye%wjqfB=I2fo@4&tQx$bAM3yQRc`flpVcy|4 J0}D&(%x{^0A8r5u diff --git a/code/fms/fms-api/target/test-classes/cn/g3soft/fmsapi/service/ExportWorkbookWriterTests.class b/code/fms/fms-api/target/test-classes/cn/g3soft/fmsapi/service/ExportWorkbookWriterTests.class index b03b69d70821be0372d724ec33e8dbde01106b07..035c521763a75d516e26d0eedd7ba8b9411e80d8 100644 GIT binary patch literal 10019 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 diff --git a/code/fms/fms-api/target/test-classes/cn/g3soft/fmsapi/utils/JwtUtilsTests.class b/code/fms/fms-api/target/test-classes/cn/g3soft/fmsapi/utils/JwtUtilsTests.class index 09ae1d75b033d6eca8c360f442f2cd46a40572bc..e9485923faad7138af93a148179429db36bb36a3 100644 GIT binary patch delta 846 zcmY*XO-~b16g_X+=}dy7Z}`Q#ca-i3w0bC zbn(G$i9t2t(Qt|(BAV2QZlD=;I)at0xZaEwj5z=sh!CzG>=eKg^_)f_x z=Zgi7FfLZUMTpKLTa<}(wsE2BDwi89%j9M?f} zdhMKTlcyd_%8qclushmex~Q zS$V?oR95~OOJ&8rx+H?0kzLvfZInTg*Fu>@v=yp&DKjZDifZf-#sMV?IDI|UB1b*t z{Dgo8G!g=QbXY+XWs>EmC>!lkV})YhF`AbTaXKCM2OOM<$BZMKchD9)Muh5JpAe0G zMARRTAE8HP4*F`k;-{y5S1JZhI@J_)#L`%|`>hnrB!cA3=2C YIV^IiV3l|Z){v!W(7z--h#Xq}0tRA_@Bjb+ delta 825 zcmX|9%Tg0j5Ir|D$qbVV#_*B|1OXw*00V;W5R8Bj2qJIsg##I>P)K4D+)@@lV7x1L zZu|hNFi=uu6-(Xt16H~A7hG8O4Z+29_v!B2=k(mW@7wo9zyJO66+j#>47i|ZP&wT2 zF!;9Y*S587?`&GL>s!u7fx#0^@1zSc1|<|;)}V_hGd&s<9bWO0nYxB*jvAa3A6VFX z9)1nA9CfG{KUlr^&AOdiIM5D`6FERv+zM$Vg07?~F5!BGeaS83k>h9u{)uN-5 z!N}$^StnOWJ9z_L=++S82qPlix`xCzS0WZb4@a+5tV(q{EE2l3Fh0K)*D*lZiNVP+ z9fJ&2c_*JwXLfpRNqLpy8m@~IWlay@CdVz@79n-PJB*ly5so{Ei#KYYI8fWYqZrdL z&T$VD;-}g!6!%+FEq- z1ySc2-D{;w0~lHQZxku?(e+Vgfg+1MlbOgN*e42HSR^aU0LN0Dd>|QiVb*i;r4xTIy&!om$aRg8@9nGvaSV2y1vwD$f+z)a}B0*=`_3 N-k`o=dN&T5{sG5okKO

Cd6v3 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 literal 7684 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 - String(row.b_field), - ) + const sequence = mergeRevealedColumns(visibleRows, revealedRows).map((row) => String(row.b_field)) // 只保留确实存在于本次列集合里的字段,并保持个人层的相对顺序 const kept = orderList.filter((field) => sequence.includes(field)) const merged = sequence.filter((field) => kept.includes(field) || !orderList.includes(field)) @@ -228,7 +226,7 @@ const configError = ref('') const dataError = ref('') const pagination = reactive({ current: 1, - pageSize: Number(props.pageSize) || 20, + pageSize: Number(props.pageSize) || 50, total: 0, }) let dataRequestVersion = 0 diff --git a/code/fms/fms-vue/src/router/index.js b/code/fms/fms-vue/src/router/index.js index 2c89f532..ba190f61 100644 --- a/code/fms/fms-vue/src/router/index.js +++ b/code/fms/fms-vue/src/router/index.js @@ -39,10 +39,7 @@ router.beforeEach(async (to) => { // 菜单一次性到位、不会“跳出”;load 内部按主体去重,重复进入同一主体不重复请求 const permissionStore = usePermissionStore() const menuStore = useMenuStore() - await Promise.all([ - permissionStore.load().catch(() => {}), - menuStore.load().catch(() => {}), - ]) + await Promise.all([permissionStore.load().catch(() => {}), menuStore.load().catch(() => {})]) // 业务数据多语言配置(s_i18n_type 语言清单 + s_i18n 译文):随权限一并加载, // 供菜单/模块等多语言编辑读取;内部去重、失败不阻断页面。 @@ -56,6 +53,13 @@ router.beforeEach(async (to) => { return '/dashboard' } + // 菜单入口:路由命中 s_menu 里的页面菜单时,必须有该菜单的 menu.* 授权。 + // 侧栏已按授权裁剪(stores/menus),这里兜住「手输地址直接访问」。 + const menu = menuStore.findMenuByRoute(to.path) + if (menu && !permissionStore.canEnterMenu(menu.b_id)) { + return '/dashboard' + } + // 系统管理员专用页面(meta.adminOnly,当前为模块中心 /module/*): // 非 g3soft 一律拦回工作台。按路由标记而非路径前缀判断,避免「菜单上看不见、 // 手输地址却能进」的越权;侧栏入口同样仅对 g3soft 渲染(AppSidebar)。 diff --git a/code/fms/fms-vue/src/stores/menus.js b/code/fms/fms-vue/src/stores/menus.js index decc7c54..31b87d0b 100644 --- a/code/fms/fms-vue/src/stores/menus.js +++ b/code/fms/fms-vue/src/stores/menus.js @@ -2,18 +2,26 @@ import { computed, ref, watch } from 'vue' import { defineStore } from 'pinia' import { loadDataApi } from '@/services/api' import { useAuthStore } from './auth' +import { usePermissionStore } from './permissions' import { buildTree, sortTree } from '@/utils/tree' -/** 递归裁剪菜单树:目录仅当存在可见子菜单时渲染(空目录不显示) */ -function pruneByAccess(nodes) { +/** + * 递归裁剪菜单树: + * - 页面 / 外链菜单:没有该菜单的 menu.* 授权时不渲染(白名单:无记录即无入口); + * - 目录:仅当存在可见子菜单时渲染(空目录不显示)。 + * @param {Array} nodes 菜单树 + * @param {(node: Object) => boolean} canEnter 是否能进入该菜单页面 + */ +function pruneByAccess(nodes, canEnter) { const result = [] for (const node of nodes) { - const children = node.children?.length ? pruneByAccess(node.children) : [] + const children = node.children?.length ? pruneByAccess(node.children, canEnter) : [] // 目录:没有可访问子菜单时不渲染 if (node.b_menu_type === 'directory') { if (children.length) result.push({ ...node, children }) continue } + if (!canEnter(node)) continue result.push(children.length ? { ...node, children } : { ...node }) } return result @@ -24,13 +32,15 @@ function pruneByAccess(nodes) { * * 菜单树由「菜单管理」维护在 s_menu 表,含目录 / 页面 / 外链三类节点。 * 可见性规则由前端执行(不改后端): - * - 全部启用菜单按登录主体加载,目录仅当存在可见子菜单时渲染(空目录不显示); + * - 全部启用菜单按登录主体加载,再按 menu.* 授权裁剪(无授权不渲染), + * 目录仅当存在可见子菜单时渲染(空目录不显示); * - 模块中心等系统管理员专用页面不进 s_menu:入口固定写在 AppSidebar(仅 g3soft 渲染), * 路由层由 meta.adminOnly 守卫拦截非 g3soft,菜单管理也配不出这些路由。 * 与权限 store 一致:同一登录主体加载去重、并发请求复用、登录主体切换清空缓存。 */ export const useMenuStore = defineStore('menus', () => { const authStore = useAuthStore() + const permissionStore = usePermissionStore() /** s_menu 原始记录(b_canuse = 1,接口按 b_xh / b_id 排序返回) */ const rawMenus = ref([]) const loadedPrincipalKey = ref('') @@ -67,14 +77,37 @@ export const useMenuStore = defineStore('menus', () => { } /** - * 当前主体可见的菜单树(s_menu 原始行结构,含 children 子树)。 + * 当前主体可见的菜单树(s_menu 原始行结构,含 children 子树): + * 已按 menu.* 授权裁剪,未授权的页面菜单与因此变空的目录都不出现。 */ const visibleMenus = computed(() => { if (!rawMenus.value.length) return [] const tree = sortTree(buildTree(JSON.parse(JSON.stringify(rawMenus.value)))) - return pruneByAccess(tree) + return pruneByAccess(tree, (node) => permissionStore.canEnterMenu(node.b_id)) }) + /** + * 按路由地址找菜单:b_route 精确匹配,或命中它的子路径(如详情页 /base/othercompany/123)。 + * 返回 null 表示该路由不在 s_menu(工作台、模块中心等),路由守卫据此放行。 + * @param {string} path 路由 path(不含 query) + * @returns {Object|null} s_menu 行 + */ + function findMenuByRoute(path) { + const target = String(path || '') + .split('?')[0] + .replace(/\/+$/, '') + if (!target) return null + return ( + rawMenus.value.find((row) => { + const route = String(row.b_route || '') + .trim() + .replace(/\/+$/, '') + if (!route) return false + return target === route || target.startsWith(`${route}/`) + }) || null + ) + } + function clear() { // 仅当“正在进行中的请求属于别的主体”时才作废它(如切换账号), // 否则保留进行中请求、只清空已加载数据,交由后续 load 结果填充。 @@ -105,5 +138,6 @@ export const useMenuStore = defineStore('menus', () => { clear, rawMenus, visibleMenus, + findMenuByRoute, } }) diff --git a/code/fms/fms-vue/src/stores/permissions.js b/code/fms/fms-vue/src/stores/permissions.js index f4059312..498180c1 100644 --- a/code/fms/fms-vue/src/stores/permissions.js +++ b/code/fms/fms-vue/src/stores/permissions.js @@ -1,33 +1,43 @@ import { computed, ref, watch } from 'vue' import { defineStore } from 'pinia' import { loadDataApi } from '@/services/api' +import { + buildActionPowerCode, + buildMenuPowerCode, + buildModulePowerCode, + isStandardModuleAction, +} from '@/utils/powerCodes' import { useAuthStore } from './auth' /** - * 权限 store:负责拉取模块 / 用户授权数据并暴露访问与功能点判断。 - * 逻辑与 fms-vue-old2 完全一致: - * - 管理员账号(g3soft)提权,直接可见全部启用模块 - * - 普通用户按 b_user_module / b_user_power 授权 - * - 同一主体(orgId:userId)加载结果去重,并发请求复用 + * 权限 store:按《FMS新系统核心表结构设计》§14 读取新表并暴露访问与功能点判断。 + * + * s_power 系统有哪些权限点(menu.* / module.* / action.*) + * s_user_power 当前用户拥有哪些权限点(白名单:无记录即无权限) + * + * 判定口径(与后端 SqlPermissionService 一致): + * - s_power 中不存在该权限点、或任一侧 b_canuse = 0,授权一律不生效; + * - 标准操作(read / create / update / delete / export)由「模块使用权限」统一表达, + * 有 module.* 即允许(见 @/utils/powerCodes); + * - 业务动作(审核 / 反审 / 结算 …)按 action.{模块}.{动作} 单独授权; + * - 菜单入口(menu.*)只控制「能否进入页面」,与模块权限相互独立。 + * + * - 管理员账号(g3soft)提权:全部启用模块可见、一切判断放行,不查授权表; + * - 同一主体(orgId:userId)加载结果去重,并发请求复用。 */ export const usePermissionStore = defineStore('permissions', () => { const authStore = useAuthStore() const loadedPrincipalKey = ref('') const modules = ref([]) - const grantedPageIds = ref([]) - const moduleCodes = ref([]) - const powerCodes = ref([]) + /** 当前用户拥有的权限点编码(s_user_power ∩ s_power 启用行) */ + const grantedPowerCodes = ref([]) let loadingPromise = null let loadingPrincipalKey = '' let loadVersion = 0 // 字符串业务键:模块编码即主键 b_id - const moduleByCode = computed( - () => new Map(modules.value.map((m) => [String(m.b_id || ''), m])), - ) - const moduleById = computed(() => new Map(modules.value.map((m) => [String(m.b_id || ''), m]))) - const grantedPageSet = computed(() => new Set(grantedPageIds.value)) - const powerSet = computed(() => new Set(powerCodes.value)) + const moduleByCode = computed(() => new Map(modules.value.map((m) => [String(m.b_id || ''), m]))) + const grantedPowerSet = computed(() => new Set(grantedPowerCodes.value)) const isG3soft = computed(() => { return ( String(authStore.loginInfo.user?.account || '') @@ -55,9 +65,7 @@ export const usePermissionStore = defineStore('permissions', () => { .then((res) => { if (requestVersion !== loadVersion) return modules.value = res.data || [] - grantedPageIds.value = [] - moduleCodes.value = [] - powerCodes.value = [] + grantedPowerCodes.value = [] loadedPrincipalKey.value = elevatedKey }) .finally(() => { @@ -74,34 +82,28 @@ export const usePermissionStore = defineStore('permissions', () => { const requestVersion = ++loadVersion loadingPrincipalKey = principalKey - // 授权表(b_user_module/b_user_power/s_module_power)在部分机构库不存在, - // 失败时按无授权处理,不阻塞登录主流程。 + // 授权表在部分机构库可能尚未建立:失败时按无授权处理,不阻塞登录主流程。 const failSafe = () => ({ data: [] }) const quotedUserId = `N'${String(userId).replaceAll("'", "''")}'` const request = Promise.all([ loadDataApi('s_module', 'b_canuse = 1', 'b_id ASC'), - loadDataApi('b_user_module', `b_user_id = ${quotedUserId}`, 'b_id ASC').catch(failSafe), - loadDataApi('b_user_power', `b_user_id = ${quotedUserId}`, 'b_id ASC').catch(failSafe), - loadDataApi('s_module_power', 'b_canuse = 1', 'b_id ASC').catch(failSafe), + loadDataApi('s_power', 'b_canuse = 1', 'b_id ASC').catch(failSafe), + loadDataApi( + 's_user_power', + `b_user_id = ${quotedUserId} AND b_canuse = 1`, + 'b_power_id ASC', + ).catch(failSafe), ]) - .then(([moduleResponse, userModules, userPowers, powers]) => { + .then(([moduleResponse, powerResponse, userPowerResponse]) => { if (requestVersion !== loadVersion) return - const activeModules = moduleResponse.data || [] - const mById = new Map(activeModules.map((m) => [String(m.b_id), m])) - const pById = new Map(powers.data.map((p) => [String(p.b_id), p])) - const pageIds = userModules.data - .map((item) => String(item.b_module_id || '')) - .filter(Boolean) - modules.value = activeModules - grantedPageIds.value = pageIds - // 模块编码即主键 b_id - moduleCodes.value = userModules.data - .map((item) => mById.get(String(item.b_module_id))?.b_id) - .filter(Boolean) - powerCodes.value = userPowers.data - .map((item) => pById.get(String(item.b_power_id))) - .filter((p) => Boolean(p)) - .map((power) => `${String(power.b_module_id)}.${power.b_id}`) + // 只有 s_power 中启用的权限点、且用户被授权才算数(设计文档 14.2 判定规则 2) + const activePowers = new Set( + (powerResponse.data || []).map((row) => String(row.b_id || '')), + ) + modules.value = moduleResponse.data || [] + grantedPowerCodes.value = (userPowerResponse.data || []) + .map((row) => String(row.b_power_id || '')) + .filter((code) => code && activePowers.has(code)) loadedPrincipalKey.value = principalKey }) .finally(() => { @@ -114,47 +116,37 @@ export const usePermissionStore = defineStore('permissions', () => { } /** - * 由模块编码向上查找其所属 page 类型模块的 ID。 - */ - 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) : undefined - } - return '' - } - - /** - * 判断当前用户是否有某模块的访问权限(管理员恒为 true)。 + * 判断当前用户是否有某模块的使用权限(管理员恒为 true)。 + * 白名单语义:没有 module.{模块编码} 授权即不可访问。 */ 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)) + if (!moduleByCode.value.has(String(moduleCode))) return false + return grantedPowerSet.value.has(buildModulePowerCode(moduleCode)) } /** * 判断当前用户是否有某模块的功能点权限。 + * 标准操作(read / create / update / delete / export)由模块使用权限统一表达, + * 有 module.* 即允许;业务动作(audit 等)要求单独的 action.{模块}.{动作} 授权。 */ - function canPower(scopeCode, powerCode) { + function canPower(scopeCode, action) { 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}`)) - ) + const code = String(scopeCode || '') + const actionCode = String(action || '') + if (!code || !actionCode) return false + if (!canAccess(code)) return false + if (isStandardModuleAction(actionCode)) return true + return grantedPowerSet.value.has(buildActionPowerCode(code, actionCode)) + } + + /** + * 判断当前用户能否进入某个菜单页面(侧栏渲染与路由入口共用)。 + * 白名单语义:没有 menu.{菜单编码} 授权即不可见、不可进入。 + */ + function canEnterMenu(menuCode) { + if (isG3soft.value || !menuCode) return true + return grantedPowerSet.value.has(buildMenuPowerCode(menuCode)) } function clear() { @@ -170,9 +162,7 @@ export const usePermissionStore = defineStore('permissions', () => { } loadedPrincipalKey.value = '' modules.value = [] - grantedPageIds.value = [] - moduleCodes.value = [] - powerCodes.value = [] + grantedPowerCodes.value = [] } // 登录主体变化(退出/切换账号)时清空权限缓存 @@ -190,11 +180,10 @@ export const usePermissionStore = defineStore('permissions', () => { load, canAccess, canPower, + canEnterMenu, clear, modules, - grantedPageIds, - moduleCodes, - powerCodes, + grantedPowerCodes, isG3soft, } }) diff --git a/code/fms/fms-vue/src/utils/powerCodes.js b/code/fms/fms-vue/src/utils/powerCodes.js new file mode 100644 index 00000000..6adc74e3 --- /dev/null +++ b/code/fms/fms-vue/src/utils/powerCodes.js @@ -0,0 +1,59 @@ +/** + * 权限点编码与分类(《FMS新系统核心表结构设计》§14.1)。 + * + * 系统只有三类权限点: + * menu.{菜单编码} 菜单入口 —— 能否进入菜单页面 + * module.{模块编码} 模块使用 —— 标准操作(查看 / 新增 / 修改 / 删除 / 导出) + * action.{模块编码}.{业务动作} 业务动作 —— 审核 / 反审 / 结算 等 + * + * 标准操作不生成独立权限点(整模块一个使用权限点),因此新口径下 + * 「能否新增」等价于「能否使用该模块」;业务动作才需要单独授权。 + * + * 权限点的维护方(menu-management / module-management)与校验方(stores/permissions) + * 共用本文件,避免两侧口径漂移。 + */ + +/** 标准操作:归入模块使用权限,不再按操作拆分 */ +export const STANDARD_MODULE_ACTIONS = ['read', 'create', 'update', 'delete', 'export'] + +/** 模块使用权限的固定动作(s_power.b_action) */ +export const MODULE_ACCESS_ACTION = 'access' + +/** 动作是否属于「模块使用权限」:access 与标准操作都算 */ +export function isStandardModuleAction(action) { + const value = String(action || '').trim() + return value === MODULE_ACCESS_ACTION || STANDARD_MODULE_ACTIONS.includes(value) +} + +/** + * 菜单入口权限编码:menu.{菜单编码}。 + * @param {string} menuCode 菜单编码(s_menu.b_id) + * @returns {string|null} 权限编码;编码为空时返回 null + */ +export function buildMenuPowerCode(menuCode) { + const code = String(menuCode || '').trim() + return code ? `menu.${code}` : null +} + +/** + * 模块使用权限编码:module.{模块编码}(标准操作也归到它)。 + * @param {string} moduleCode 模块编码(s_module.b_id) + * @returns {string|null} 权限编码;编码为空时返回 null + */ +export function buildModulePowerCode(moduleCode) { + const code = String(moduleCode || '').trim() + return code ? `module.${code}` : null +} + +/** + * 业务动作权限编码:action.{模块编码}.{动作}。 + * @param {string} moduleCode 模块编码(s_module.b_id) + * @param {string} action 业务动作(audit / unaudit / ...) + * @returns {string|null} 权限编码;任一段为空时返回 null + */ +export function buildActionPowerCode(moduleCode, action) { + const moduleId = String(moduleCode || '').trim() + const actionCode = String(action || '').trim() + if (!moduleId || !actionCode) return null + return `action.${moduleId}.${actionCode}` +} diff --git a/code/fms/fms-vue/src/utils/schemaJson.js b/code/fms/fms-vue/src/utils/schemaJson.js index 574ab1fb..5d8b27dc 100644 --- a/code/fms/fms-vue/src/utils/schemaJson.js +++ b/code/fms/fms-vue/src/utils/schemaJson.js @@ -173,3 +173,62 @@ export function buildGroupI18nKey(moduleCode, schemaType, groupId) { const prefix = buildGroupI18nKeyPrefix(moduleCode, schemaType) return prefix && groupIdValue ? `${prefix}${groupIdValue}` : null } + +/* ================= 分组编码校验 ================= */ + +/** + * 分组编码长度上限:键为 group.{模块编码}.{schema 类型}.{分组编码}, + * 即 6 + 1 + 50 + 1 + 5 + 1 + 30 = 94,远小于 s_i18n.b_key varchar(150)。 + */ +export const GROUP_CODE_MAX_LENGTH = 30 + +/** 分组编码格式与模块编码(s_module.b_id)同构,另见 ModuleBasicPanel 的同款校验 */ +const GROUP_CODE_RE = /^[a-z][a-z0-9_]*$/ + +/** 保留字:结构树用 'g:ungrouped' 作虚拟「未分组」节点 key,真实分组不能占用该名字 */ +const RESERVED_GROUP_CODES = new Set(['ungrouped']) + +/** 编码集合归一:兼容数组与 Set,去空白后丢弃空值 */ +function normalizeCodes(codes) { + const set = new Set() + for (const value of codes || []) { + const code = textOf(value) + if (code) set.add(code) + } + return set +} + +/** + * 校验分组编码(新建分组时手填)。契约与 ui Form 的 validator 一致: + * 返回 true 表示通过,返回非空字符串即错误文案。 + * + * 唯一性只在单个配置(单份 b_schema_json)内判定:view 与 edit 各自独立编号, + * 同一编码可以同时存在于列表与表单,故不需要 schema 类型入参。 + * + * @param {unknown} rawCode 待校验的编码 + * @param {{ existingIds?: Iterable, reservedIds?: Iterable, maxLength?: number }} [options] + * existingIds 本配置内已占用的编码;reservedIds 多语言资源里仍留有记录的编码 + * (已删除分组的编号,复用会连带继承其残留译文) + * @returns {true|string} + */ +export function validateGroupCode( + rawCode, + { existingIds = [], reservedIds = [], maxLength = GROUP_CODE_MAX_LENGTH } = {}, +) { + const code = textOf(rawCode) + if (!code) return '分组编码不能为空' + if (code.length < 2 || code.length > maxLength) { + return `分组编码长度需为 2-${maxLength} 个字符` + } + if (!GROUP_CODE_RE.test(code)) { + return '分组编码须以小写字母开头,且只能包含小写字母、数字和下划线' + } + if (RESERVED_GROUP_CODES.has(code)) return `分组编码 ${code} 为系统保留字,请换一个` + if (normalizeCodes(existingIds).has(code)) { + return `分组编码 ${code} 已被本配置的其他分组使用,请换一个` + } + if (normalizeCodes(reservedIds).has(code)) { + return `分组编码 ${code} 曾被使用(多语言资源中仍留有记录),请换一个` + } + return true +} diff --git a/code/fms/fms-vue/src/views/module/menu-management/index.vue b/code/fms/fms-vue/src/views/module/menu-management/index.vue index c422c72a..86a33889 100644 --- a/code/fms/fms-vue/src/views/module/menu-management/index.vue +++ b/code/fms/fms-vue/src/views/module/menu-management/index.vue @@ -13,7 +13,9 @@ * 面板各自的字段编辑与表格交互均在对应面板组件内部, * 数据以 v-model 直接读写本文件持有的草稿,不存在第二份状态源。 * - * 权限点(s_power)统一在「模块管理 → 权限」维护,菜单保存不再派生 menu.* 权限点。 + * 权限点分两类:模块使用与业务动作权限点(module.* / action.*)在「模块管理 → 权限」维护; + * 菜单入口权限点(menu.*)由本页自动派生 —— 保存菜单时同步(名称/顺序/启用状态跟随菜单), + * 删除菜单时连同 s_user_power 里的授权行一并清理。 */ import { computed, nextTick, ref, watch } from 'vue' import { useHotkeys } from '@/composables/useHotkeys' @@ -38,7 +40,15 @@ import I18nNameModal from '@/components/I18nNameModal.vue' import MenuTreePanel from './MenuTreePanel.vue' import MenuBasicPanel from './MenuBasicPanel.vue' import ModulePickerModal from './ModulePickerModal.vue' -import { MENU_NON_LEAF_TYPES, MENU_TYPE_ICON_MAP, buildMenuI18nKey, menuRowOf } from './utils' +import { + MENU_NON_LEAF_TYPES, + MENU_TYPE_ICON_MAP, + buildMenuI18nKey, + buildMenuPowerCode, + buildMenuPowerRow, + menuRowOf, + normalizeMenuPowerRow, +} from './utils' import { useI18nStore } from '@/stores/i18n' /** 生成安全 SQL 字符串字面量(search_condition 为后端拼 SQL,需要转义引号) */ @@ -109,6 +119,8 @@ const i18nModalVisible = ref(false) const menuModules = ref([]) /** 绑定模块加载基线(保存时行级 diffRows 的原始集合) */ const menuModules_org = ref([]) +/** 菜单入口权限点(s_power, b_object_type='menu')加载基线;保存时按菜单行派生并 diff 同步 */ +const menuPower_org = ref(null) /** 模块绑定选择弹层 */ const modulePickerOpen = ref(false) @@ -331,6 +343,19 @@ async function deleteMenu(id) { // 模块绑定关系随菜单删除(b_menu_id 为复合主键首列,按它批量删) { table: 's_menu_module', key_field: 'b_menu_id', deletes: [{ b_menu_id: menuId }] }, ] + // 入口权限点与它的授权一起清理:删除是条件式删除、0 行不报错, + // 所以目录菜单(没有权限点)与历史数据都能安全走这里。 + // s_user_power 主键是 (b_user_id, b_power_id),按 b_power_id 一次删掉所有用户对该菜单的授权, + // 避免留下指向已删除菜单的孤儿行。 + const powerCode = buildMenuPowerCode(menuId) + if (powerCode) { + reqs.push({ + table: 's_user_power', + key_field: 'b_power_id', + deletes: [{ b_power_id: powerCode }], + }) + reqs.push({ table: 's_power', key_field: 'b_id', deletes: [{ b_id: powerCode }] }) + } reqs.push({ table: 's_menu', key_field: 'b_id', deletes: [{ b_id: menuId }] }) await saveObjectApi(reqs) collectUnsavedMenuIds(menu).forEach((k) => unsavedMenuIds.delete(k)) @@ -386,6 +411,15 @@ async function loadMenuConfig(menuId) { if (String(menuSelectedId.value) !== id) return menuModules_org.value = (moduleRes.data || []).map((row) => String(row.b_module_id)) menuModules.value = [...menuModules_org.value] + // 菜单入口权限点(s_power):页面/外链菜单各一行 menu.{编码},完全由菜单行派生 + // (见 buildMenuPowerRow),这里只取基线供保存时 diff + const powerRes = await loadDataApi( + 's_power', + `b_object_type = 'menu' and b_object_id = ${sqlText(id)}`, + 'b_id ASC', + ) + if (String(menuSelectedId.value) !== id) return + menuPower_org.value = normalizeMenuPowerRow(powerRes.data?.[0] || null) loadedMenuId.value = id } catch (e) { if (String(menuSelectedId.value) === String(menuId)) { @@ -410,6 +444,7 @@ watch(menuSelectedId, (id) => { maindata.value = null menuModules.value = [] menuModules_org.value = [] + menuPower_org.value = null return } // 未落库的新节点:临时 id 在库里查不到行,不能走 loadMenuConfig @@ -430,6 +465,7 @@ watch(menuSelectedId, (id) => { i18n_org.value = [] menuModules.value = [] menuModules_org.value = [] + menuPower_org.value = null loadedMenuId.value = String(id) return } @@ -655,6 +691,23 @@ async function buildSaveData() { reqs.push(moduleReq) } + // 菜单入口权限点(s_power):页面 / 外链菜单必须有一行 menu.{编码},目录没有。 + // 它完全由菜单行派生(名称、多语言键、启用状态、顺序跟随菜单),不单独做草稿; + // 保存时按目标行与该菜单的加载基线 diff,只提交插入 / 更新 / 删除。 + const targetPowerRow = buildMenuPowerRow({ ...menuData, b_id: menuId }) + const powerChanges = diffRows( + targetPowerRow ? [targetPowerRow] : [], + menuPower_org.value ? [menuPower_org.value] : [], + { keyFields: ['b_id'] }, + ) + if (powerChanges.inserts.length || powerChanges.updates.length || powerChanges.deletes.length) { + const powerReq = { table: 's_power', key_field: 'b_id' } + if (powerChanges.inserts.length) powerReq.inserts = powerChanges.inserts + if (powerChanges.updates.length) powerReq.updates = powerChanges.updates + if (powerChanges.deletes.length) powerReq.deletes = powerChanges.deletes + reqs.push(powerReq) + } + return reqs.length ? reqs : null } diff --git a/code/fms/fms-vue/src/views/module/menu-management/utils.js b/code/fms/fms-vue/src/views/module/menu-management/utils.js index 8a3990a5..8e8fef87 100644 --- a/code/fms/fms-vue/src/views/module/menu-management/utils.js +++ b/code/fms/fms-vue/src/views/module/menu-management/utils.js @@ -4,6 +4,7 @@ * 树本身的通用查找/过滤/插入/移除/位置派生见 @/utils/tree,不在此重复。 */ import { ExternalLink, FileText, Folder } from '@lucide/vue' +import { buildMenuPowerCode } from '@/utils/powerCodes' /** 菜单类型下拉选项(directory 目录 / page 页面 / external 外链) */ export const MENU_TYPE_OPTIONS = [ @@ -66,3 +67,67 @@ export function buildMenuI18nKey(menuId) { const normalizedMenuId = String(menuId || '').trim() return normalizedMenuId ? `menu.${normalizedMenuId}` : null } + +/* ================= 菜单入口权限点(s_power,b_object_type = 'menu') ================= */ + +/** + * 需要入口权限点的菜单类型:目录只是导航分组(侧栏按子菜单裁剪), + * 页面与外链才是「能否进入」的入口,各对应一行 menu.{菜单编码}(b_action = 'access')。 + */ +export const MENU_POWER_MENU_TYPES = ['page', 'external'] + +/** + * 菜单入口权限编码:menu.{菜单编码}(定义在 @/utils/powerCodes,与授权校验共用)。 + * 与 buildMenuI18nKey 字符串同形但语义不同:前者是 s_power.b_id,后者是 s_i18n.b_key。 + */ +export { buildMenuPowerCode } + +/** 参与同步的 s_power 列:b_bz 是备注,不随菜单变化,不参与 diff */ +const MENU_POWER_FIELDS = [ + 'b_id', + 'b_name', + 'b_i18n', + 'b_type', + 'b_object_type', + 'b_object_id', + 'b_action', + 'b_canuse', + 'b_xh', +] + +/** + * 归一化 s_power 行为入口权限点的可比字段(剔除 b_bz 等不参与同步的列, + * 数字列统一为 number,避免 '10' 与 10 产生假差异)。 + * @param {Object|null} row s_power 行 + * @returns {Object|null} 可比行;入参为空时返回 null + */ +export function normalizeMenuPowerRow(row) { + if (!row) return null + const picked = {} + for (const field of MENU_POWER_FIELDS) picked[field] = row[field] ?? null + picked.b_canuse = Number(picked.b_canuse ?? 0) + picked.b_xh = Number(picked.b_xh ?? 0) + return picked +} + +/** + * 由菜单行派生入口权限点行:名称、多语言键、启用状态、顺序全部跟随菜单。 + * 保存菜单时按它与加载基线 diff,权限点因此不需要单独的编辑界面。 + * @param {Object} menuRow s_menu 行(b_id 为最终菜单编码) + * @returns {Object|null} s_power 行;目录等不需要入口权限的菜单返回 null + */ +export function buildMenuPowerRow(menuRow) { + const menuId = String(menuRow?.b_id || '').trim() + if (!menuId || !MENU_POWER_MENU_TYPES.includes(String(menuRow?.b_menu_type || ''))) return null + return { + b_id: buildMenuPowerCode(menuId), + b_name: String(menuRow.b_name ?? ''), + b_i18n: buildMenuI18nKey(menuId), + b_type: 'menu', + b_object_type: 'menu', + b_object_id: menuId, + b_action: 'access', + b_canuse: Number(menuRow.b_canuse ?? 1) === 0 ? 0 : 1, + b_xh: Number(menuRow.b_xh ?? 0) || 0, + } +} diff --git a/code/fms/fms-vue/src/views/module/module-management/components/design-editor/FormDesignEditor.vue b/code/fms/fms-vue/src/views/module/module-management/components/design-editor/FormDesignEditor.vue index 73cf992e..0e24a609 100644 --- a/code/fms/fms-vue/src/views/module/module-management/components/design-editor/FormDesignEditor.vue +++ b/code/fms/fms-vue/src/views/module/module-management/components/design-editor/FormDesignEditor.vue @@ -40,6 +40,7 @@ import { } from './common/useStructureEditing' import FieldLibrary from './common/FieldLibrary.vue' import EditDataView from './common/EditDataView.vue' +import GroupCreateModal from './common/GroupCreateModal.vue' import FormInspector from './form/FormInspector.vue' import FormPreview from './form/FormPreview.vue' import { @@ -51,7 +52,6 @@ import { sectionEndIndex, } from './form/useFormGrid' import { schemaToEditor, editorToSchema, stableStringify } from './common/schemaModel' -import { nextGroupId } from '@/utils/schemaJson' import { createDefaultSchema } from '../../utils' const props = defineProps({ @@ -566,31 +566,34 @@ function nextGroupXh() { return (groups.value || []).reduce((m, g) => Math.max(m, Number(g.b_xh || 0)), 0) + 10 } -/** 新分组编号:避开本配置已有编号 + 宿主保留编号(多语言表里已存在的分组键) */ -function nextGroupCode() { - return nextGroupId([ - ...(groups.value || []).map((group) => group.b_id), - ...(props.reservedGroupIds || []), - ]) +/** 本配置内已占用的分组编码(新建分组时判唯一性) */ +const groupIdList = computed(() => (groups.value || []).map((group) => String(group.b_id))) + +/* 新建分组弹窗:编码手填,创建后不可修改(弹窗不提供编辑入口) */ +const groupCreateOpen = ref(false) + +function openGroupCreate() { + if (props.loading) return + groupCreateOpen.value = true } -/** 添加分组:插入一个空分组并立即进入内联命名 */ -function addGroup() { - if (props.loading) return +/** 弹窗确认:按手填编码插入空分组;名称留空则仍进入内联命名 */ +function onGroupCreateConfirm({ id, title }) { + if (props.loading || !String(id || '').trim()) return flushEditingGroupTitle() const newGroup = { - b_id: nextGroupCode(), + b_id: String(id), b_module_id: props.moduleId, b_parent_id: null, - b_title: '', + b_title: String(title ?? ''), b_xh: nextGroupXh(), b_canuse: 1, b_layout_direction: 'vertical', b_colspan: GROUP_GRID, } groups.value = [...(groups.value || []), newGroup] - const key = `g-${newGroup.b_id}` - editingGroupKey.value = key + if (newGroup.b_title) return // 已填名称:直接完成,不再进入命名态 + editingGroupKey.value = `g-${newGroup.b_id}` groupTitleDraft.value = '' // 双重 nextTick:第一次等父组件更新 groups.value,第二次等新分区的 Input 组件渲染到 DOM nextTick(() => { @@ -1419,7 +1422,7 @@ onBeforeUnmount(() => {

-
+ + + diff --git a/code/fms/fms-vue/src/views/module/module-management/components/design-editor/TableDesignEditor.vue b/code/fms/fms-vue/src/views/module/module-management/components/design-editor/TableDesignEditor.vue index da69f3e5..6c43cbd1 100644 --- a/code/fms/fms-vue/src/views/module/module-management/components/design-editor/TableDesignEditor.vue +++ b/code/fms/fms-vue/src/views/module/module-management/components/design-editor/TableDesignEditor.vue @@ -44,6 +44,7 @@ import FieldLibrary from './common/FieldLibrary.vue' import TablePreview from './table/TablePreview.vue' import TableInspector from './table/TableInspector.vue' import EditDataView from './common/EditDataView.vue' +import GroupCreateModal from './common/GroupCreateModal.vue' const props = defineProps({ /** 编辑场景:subtable 子表编辑 | list 列表配置 */ @@ -175,7 +176,6 @@ const editing = useStructureEditing({ rows.value = nextRows }, requestRename: (gid) => structureTreeRef.value?.startRename(gid), - reservedGroupIds: () => props.reservedGroupIds, // 子表:全量键集;列表:仅 列宽 clipboardKeys: isSubtable.value ? undefined : ['b_width'], fieldNameOf: (row) => fieldName(row), @@ -201,7 +201,6 @@ const { removeRow, removeSelected, onNodeMove, - onNodeAddSub, selectRow, selectField, onFieldDrop, @@ -258,11 +257,54 @@ function onBandDrop({ gid, line }) { commitBandDrag(gid, line) } +/* ================= 新建分组(编码手填,创建后不可修改) ================= */ +// 弹窗上下文:null 即关闭;parentGid 非空表示新建子分组 +const groupCreate = ref(null) +const groupCreateOpen = computed({ + get: () => groupCreate.value !== null, + set: (value) => { + if (!value) groupCreate.value = null + }, +}) +/** 本配置内已占用的分组编码(新建分组时判唯一性) */ +const groupIdList = computed(() => (groups.value || []).map((group) => String(group.b_id))) + +function openGroupCreate(parentGid = null) { + if (props.loading) return + const gid = parentGid == null ? null : String(parentGid) + const band = gid ? bandMeta.value.metaMap.get(gid) : null + if (gid && !band) return // 目标父分组已不存在:不打开弹窗 + groupCreate.value = { + parentGid: gid, + parentLabel: band ? band.group.b_title || `分组-${gid}` : '', + } +} + +/** 子分组入口:右键与属性面板传 band 元信息,结构树事件传 gid 字符串 */ +function openSubGroupCreate(gidOrBand) { + const gid = gidOrBand && typeof gidOrBand === 'object' ? gidOrBand.gid : gidOrBand + if (gid == null) return + openGroupCreate(gid) +} + +function onGroupCreateConfirm({ id, title }) { + const parentGid = groupCreate.value?.parentGid ?? null + if (parentGid) addSubGroup(parentGid, { id, title }) + else addRootGroup({ id, title }) + groupCreate.value = null +} + +/** 结构树「新增子分组」:节点 key 形如 g: */ +function onNodeAddSubRequest({ key }) { + if (props.loading || !key?.startsWith('g:') || key === 'g:ungrouped') return + openSubGroupCreate(key.slice(2)) +} + function onStructureContextMenuClick({ item, node }) { if (node?.kind === 'group') { const band = bandMeta.value.metaMap.get(String(node.gid)) if (!band) return - if (item.key === 'add-sub') addSubGroup(band) + if (item.key === 'add-sub') openSubGroupCreate(band.gid) else if (item.key === 'rename') structureTreeRef.value?.startRename(String(node.gid)) else if (item.key === 'enable') { editing.selectBand(band) @@ -470,7 +512,7 @@ const treeFeatures = computed(() => ({
- - 已保存 - - -
- - -
- - -
-
-
系统设置权限管理
-
-
-

用户权限分配

-

先给入口,再给模块能力;模块权限会在所有菜单和接口中保持一致。

-
-
-
-
- - 当前授权原则:菜单控制能否进入页面,模块控制能否使用数据;选择“海运”只做批量操作,不会通过数据关联自动扩大权限。 -
- -
-
- 授权来源 -

海运·经办角色方案

-

角色方案会一次生成菜单入口和模块动作,之后仍可按用户单独微调。

-
-
- - - -
-
- -
-
-
推荐授权方式

按业务方案分配

管理员只需要选择业务域的工作方式,系统会自动准备菜单和模块权限。

- -
-
-

业务域

先选择客户熟悉的业务域,再选择该业务域的工作方式。

当前配置:海运
-
-
-
-
-

海运业务

包含主单、装箱信息和费用模块

-
-

选择授权方案

点击后可查看授权预览
-
-
-
-

授权预览

选择方案后,这里会显示实际授权内容

确认后一次保存
-
-
-
-
-
“只读 / 经办 / 主管”只是管理员的快捷入口。保存后仍按具体模块、动作、字段和数据范围校验,不会因为数据关联自动扩大权限。
-
- - -
-
-
- - - - - - -
- - - -