openxiangda 1.0.11 → 1.0.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/cli.js CHANGED
@@ -1325,6 +1325,9 @@ async function permission(args) {
1325
1325
  fail('用法: openxiangda permission page-group-create <groupCode> --name <text> [--menu-codes <menuCode...>|--page-codes <pageCode...>|--form-codes <formCode...>|--menu-ids <id...>]');
1326
1326
  }
1327
1327
  const target = getWorkspaceTarget(config, profileName, flags);
1328
+ const roles = splitList(flags.roles);
1329
+ const menuFormUuids = resolvePagePermissionMenuTargets(target.bound, flags);
1330
+ const runtimeAliasTargets = resolvePagePermissionRuntimeAliasTargets(target.bound, flags);
1328
1331
  const data = await requestWithAuth(
1329
1332
  config,
1330
1333
  target.profileName,
@@ -1333,14 +1336,38 @@ async function permission(args) {
1333
1336
  method: 'POST',
1334
1337
  body: {
1335
1338
  name,
1336
- roles: splitList(flags.roles),
1337
- menuFormUuids: resolvePagePermissionMenuTargets(target.bound, flags),
1339
+ roles,
1340
+ menuFormUuids,
1338
1341
  },
1339
1342
  }
1340
1343
  );
1341
1344
  if (data?.id) savePagePermissionGroupResource(target, groupCode, data.id, { name });
1342
- if (flags.json) return writeJson(data);
1343
- print(JSON.stringify(data, null, 2));
1345
+ let runtimeAliasData = null;
1346
+ if (runtimeAliasTargets.length > 0) {
1347
+ const runtimeAliasGroupCode = `${groupCode}__runtime_aliases`;
1348
+ const runtimeAliasName = `${name}(运行时别名)`;
1349
+ runtimeAliasData = await requestWithAuth(
1350
+ config,
1351
+ target.profileName,
1352
+ `/openxiangda-api/v1/apps/${encodeURIComponent(target.appType)}/page-permission-groups`,
1353
+ {
1354
+ method: 'POST',
1355
+ body: {
1356
+ name: runtimeAliasName,
1357
+ roles,
1358
+ menuFormUuids: runtimeAliasTargets,
1359
+ },
1360
+ }
1361
+ );
1362
+ if (runtimeAliasData?.id) {
1363
+ savePagePermissionGroupResource(target, runtimeAliasGroupCode, runtimeAliasData.id, {
1364
+ name: runtimeAliasName,
1365
+ });
1366
+ }
1367
+ }
1368
+ const output = runtimeAliasData ? { ...data, runtimeAliasGroup: runtimeAliasData } : data;
1369
+ if (flags.json) return writeJson(output);
1370
+ print(JSON.stringify(output, null, 2));
1344
1371
  return;
1345
1372
  }
1346
1373
 
@@ -1920,24 +1947,44 @@ function resolveMenuPermissionTargets(bound, menuCode) {
1920
1947
  const menuEntry = bound.resources?.menus?.[menuCode];
1921
1948
  const menuId = resolveMenuId(bound, menuCode);
1922
1949
  if (menuEntry?.formUuid) return [menuEntry.formUuid];
1923
- // Custom code pages are checked by three platform surfaces: the admin tree
1924
- // uses menu IDs, /view uses route keys, and bootstrap uses legacy PAGE IDs.
1925
- if (menuEntry?.pageId || menuEntry?.pageCode || menuEntry?.routeKey || menuEntry?.legacyFormUuid) {
1926
- return unique([menuId, ...resolveCodePagePermissionAliases(bound, menuEntry)]);
1927
- }
1928
1950
  return [menuId];
1929
1951
  }
1930
1952
 
1931
1953
  function resolvePagePermissionTargets(bound, pageCode) {
1954
+ const menuEntry = findPagePermissionMenuEntry(bound, pageCode);
1955
+ if (menuEntry?.menuId) {
1956
+ return [menuEntry.menuId];
1957
+ }
1958
+ fail(`页面 ${pageCode} 未找到已绑定菜单。请先发布/创建菜单,或直接传 --menu-codes/--menu-ids。`);
1959
+ }
1960
+
1961
+ function resolvePagePermissionRuntimeAliasTargets(bound, flags = {}) {
1962
+ const fromMenuCodes = splitList(flags['menu-codes']).flatMap(code => resolveMenuPermissionRuntimeAliases(bound, code));
1963
+ const fromPageCodes = splitList(flags['page-codes']).flatMap(code => resolvePagePermissionRuntimeAliases(bound, code));
1964
+ return unique([...fromMenuCodes, ...fromPageCodes].filter(Boolean));
1965
+ }
1966
+
1967
+ function resolveMenuPermissionRuntimeAliases(bound, menuCode) {
1968
+ const menuEntry = bound.resources?.menus?.[menuCode];
1969
+ if (!menuEntry || menuEntry.formUuid) return [];
1970
+ if (menuEntry.pageId || menuEntry.pageCode || menuEntry.routeKey || menuEntry.legacyFormUuid) {
1971
+ return resolveCodePagePermissionAliases(bound, menuEntry);
1972
+ }
1973
+ return [];
1974
+ }
1975
+
1976
+ function resolvePagePermissionRuntimeAliases(bound, pageCode) {
1977
+ const menuEntry = findPagePermissionMenuEntry(bound, pageCode);
1978
+ if (menuEntry?.menuId) return resolveCodePagePermissionAliases(bound, menuEntry, pageCode);
1979
+ fail(`页面 ${pageCode} 未找到已绑定菜单。请先发布/创建菜单,或直接传 --menu-codes/--menu-ids。`);
1980
+ }
1981
+
1982
+ function findPagePermissionMenuEntry(bound, pageCode) {
1932
1983
  const pageId = resolveOptionalPageId(bound, pageCode);
1933
- const menuEntry = findMenuEntry(bound, entry => {
1984
+ return findMenuEntry(bound, entry => {
1934
1985
  if (!entry) return false;
1935
1986
  return entry.pageId === pageId || entry.pageCode === pageCode || entry.routeKey === pageCode;
1936
1987
  });
1937
- if (menuEntry?.menuId) {
1938
- return unique([menuEntry.menuId, ...resolveCodePagePermissionAliases(bound, menuEntry, pageCode)]);
1939
- }
1940
- fail(`页面 ${pageCode} 未找到已绑定菜单。请先发布/创建菜单,或直接传 --menu-codes/--menu-ids。`);
1941
1988
  }
1942
1989
 
1943
1990
  function resolveCodePagePermissionAliases(bound, menuEntry = {}, pageCode) {
@@ -369,11 +369,11 @@ Body:
369
369
  {
370
370
  "name": "销售可见页面",
371
371
  "roles": ["sales"],
372
- "menuFormUuids": ["FORM_XXX", "MENU_ID_FOR_CODE_PAGE", "CODE_PAGE_ROUTE_KEY", "PAGE_LEGACY_FORM_UUID"]
372
+ "menuFormUuids": ["FORM_XXX", "MENU_ID_FOR_CODE_PAGE"]
373
373
  }
374
374
  ```
375
375
 
376
- An empty `menuFormUuids` array means all menus/pages are visible to the matched roles. For form menus this field can contain form UUIDs. For custom code page menus, OpenXiangda CLI `--page-codes` / `--menu-codes` writes the actual menu ID used by the platform admin tree, the route key used by `/view`, and the legacy `PAGE_...` alias used by custom-page bootstrap.
376
+ An empty `menuFormUuids` array means all menus/pages are visible to the matched roles. For form menus this field can contain form UUIDs. For custom code page menus, the user-facing permission group should contain only the actual menu ID used by the platform admin tree. Because the current `/view` runtime guard also checks route keys and legacy `PAGE_...` aliases, OpenXiangda CLI `--page-codes` / `--menu-codes` creates a companion `(运行时别名)` page permission group for those aliases instead of mixing them into the editable menu group.
377
377
 
378
378
  ### GET `/apps/:appType/page-permission-groups/:groupId`
379
379
 
@@ -19,14 +19,26 @@ Role IDs are platform-specific. Store them only in `.openxiangda/state.json` und
19
19
  ## Page Permission Groups
20
20
 
21
21
  Page permission groups map role codes to visible menu targets. Form menus use their `FORM_...`
22
- form UUIDs; custom code page/display menus need the menu ID, route key, and legacy `PAGE_...`
23
- alias because current platform surfaces check different identifiers:
22
+ form UUIDs. Custom code page/display menus need split targets because current platform
23
+ surfaces check different identifiers: the admin edit tree expects menu IDs, while `/view`
24
+ runtime checks route keys and legacy `PAGE_...` aliases.
24
25
 
25
26
  ```json
26
27
  {
27
28
  "name": "销售页面",
28
29
  "roles": ["sales"],
29
- "menuFormUuids": ["FORM_CUSTOMER", "FORM_ORDER", "MENU_ID_FOR_CODE_PAGE", "CODE_PAGE_ROUTE_KEY", "PAGE_LEGACY_FORM_UUID"]
30
+ "menuFormUuids": ["FORM_CUSTOMER", "FORM_ORDER", "MENU_ID_FOR_CODE_PAGE"]
31
+ }
32
+ ```
33
+
34
+ For custom code pages, keep the user-facing group editable by storing only menu IDs, then add a
35
+ companion runtime alias group with the same roles:
36
+
37
+ ```json
38
+ {
39
+ "name": "销售页面(运行时别名)",
40
+ "roles": ["sales"],
41
+ "menuFormUuids": ["CODE_PAGE_ROUTE_KEY", "PAGE_LEGACY_FORM_UUID"]
30
42
  }
31
43
  ```
32
44
 
@@ -35,7 +47,7 @@ Rules:
35
47
  - `roles: []` means all roles can match.
36
48
  - `menuFormUuids: []` means all menus/pages are visible to matched roles.
37
49
  - Prefer `--form-codes` in CLI for form menus so each profile resolves its own form UUIDs.
38
- - Prefer `--page-codes` or `--menu-codes` in CLI for custom code page menus. The CLI writes the menu ID for the admin tree, the route key for `/view`, and the legacy `PAGE_...` alias for custom-page bootstrap.
50
+ - Prefer `--page-codes` or `--menu-codes` in CLI for custom code page menus. The CLI writes menu IDs to the editable group and creates the companion `(运行时别名)` group automatically.
39
51
 
40
52
  ## Form Permission Groups
41
53
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openxiangda",
3
- "version": "1.0.11",
3
+ "version": "1.0.12",
4
4
  "description": "OpenXiangda CLI, workspace build tools, runtime SDK, and form components.",
5
5
  "private": false,
6
6
  "bin": {