com-angel-authorization 1.0.8 → 1.0.10

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.
@@ -170,6 +170,14 @@ type MenuResource = {
170
170
  resourceId: string;
171
171
  /** 上级菜单 ID;无上级时为 null */
172
172
  parentId: string | null;
173
+ /**
174
+ * 菜单层级:无上级(根)为 1,其余为上级 depth + 1
175
+ */
176
+ depth: number;
177
+ /**
178
+ * 是否叶子节点:`1` 表示无下级菜单,不可再展开
179
+ */
180
+ leaf: number;
173
181
  type: MenuResourceType;
174
182
  name: string;
175
183
  identification: string;
@@ -181,6 +189,8 @@ type MenuResource = {
181
189
  type MenuResourceFormValues = {
182
190
  /** 上级菜单 ID;选择「无」时为 null */
183
191
  parentId: string | null;
192
+ /** 由 parentId 自动计算:无为 1,否则上级 depth + 1 */
193
+ depth: number;
184
194
  type: MenuResourceType;
185
195
  name: string;
186
196
  identification: string;
@@ -192,6 +202,8 @@ type CreateMenuResourceBody = MenuResourceFormValues & {
192
202
  };
193
203
  type UpdateMenuResourceBody = Partial<MenuResourceFormValues>;
194
204
  type FetchMenuResourcesParams = {
205
+ /** 列表默认传 1,只拉根层 */
206
+ depth?: number;
195
207
  pagination?: ListPaginationParams;
196
208
  };
197
209
  type MenuResourceListResult = {
@@ -200,6 +212,9 @@ type MenuResourceListResult = {
200
212
  hasPreviousPage: boolean;
201
213
  hasNextPage: boolean;
202
214
  };
215
+ /** leaf === 1 表示无下级 */
216
+ declare const MENU_LEAF_YES = 1;
217
+ declare const MENU_LEAF_NO = 0;
203
218
  declare const RESOURCE_TYPE_API: ApiResourceType;
204
219
  declare const MENU_TYPE_PAGE: MenuResourceType;
205
220
  declare const MENU_TYPE_MENU: MenuResourceType;
@@ -211,6 +226,8 @@ declare const MENU_LIST_TYPE_PARAM = "page,menu,button";
211
226
  * 提交接口时会转换为 parentId: null。
212
227
  */
213
228
  declare const ROOT_PARENT_ID = "";
229
+ /** 无上级时的菜单层级 */
230
+ declare const ROOT_MENU_DEPTH = 1;
214
231
  declare const RESOURCE_STATUS_ENABLED: ResourceStatus;
215
232
  declare const RESOURCE_STATUS_DISABLED: ResourceStatus;
216
233
  declare const RESOURCE_STATUS_OPTIONS: {
@@ -251,6 +268,8 @@ type AuthorizationResourceApi = {
251
268
  };
252
269
  type MenuResourceApi = {
253
270
  list: (params?: FetchMenuResourcesParams, signal?: AbortSignal) => Promise<MenuResourceListResult>;
271
+ /** 展开下级:GET /authorization-resources/{resource-id}/sub-resources */
272
+ listSubResources: (resourceId: string, signal?: AbortSignal) => Promise<MenuResource[]>;
254
273
  /** 拉取权限点(type=api),供菜单表单多选 */
255
274
  listPermissionPoints: (params?: FetchAuthorizationResourcesParams, signal?: AbortSignal) => Promise<AuthorizationResourceListResult>;
256
275
  create: (values: MenuResourceFormValues, signal?: AbortSignal) => Promise<unknown>;
@@ -259,6 +278,14 @@ type MenuResourceApi = {
259
278
  };
260
279
  declare function mapAuthorizationResource(item: unknown): AuthorizationResource | null;
261
280
  declare function mapMenuResource(item: unknown): MenuResource | null;
281
+ /**
282
+ * leaf === 1 表示无下级菜单。
283
+ */
284
+ declare function isMenuLeaf(row: Pick<MenuResource, 'leaf'>): boolean;
285
+ /**
286
+ * 根据上级菜单计算 depth:无上级为 1,否则为上级 depth + 1。
287
+ */
288
+ declare function resolveMenuDepth(parentId: string | null | undefined, menus: Array<Pick<MenuResource, 'resourceId' | 'depth'>>): number;
262
289
  declare function buildCreateBody(values: AuthorizationResourceFormValues, resourceId?: string): CreateAuthorizationResourceBody;
263
290
  declare function buildUpdateBody(values: AuthorizationResourceFormValues): UpdateAuthorizationResourceBody;
264
291
  declare function buildCreateMenuBody(values: MenuResourceFormValues, resourceId?: string): CreateMenuResourceBody;
@@ -359,4 +386,4 @@ declare const snowyflake: ConfigurableSnowflake;
359
386
  declare function getAppClientId(): string;
360
387
  declare function getDeviceWorkerId(): string;
361
388
 
362
- export { type ApiResourceType, type AuthorizationResource, type AuthorizationResourceApi, type AuthorizationResourceFormValues, type AuthorizationResourceListResult, Can, type CanProps, type CheckOptions, type CreateAuthorizationResourceBody, type CreateDefaultResourceRequestOptions, type CreateMenuResourceBody, type FetchAuthorizationResourcesParams, type FetchMenuResourcesParams, type HttpMethod, type ListPaginationParams, type ListPaginationResult, MENU_LIST_TYPE_PARAM, MENU_TYPE_BUTTON, MENU_TYPE_LABEL, MENU_TYPE_MENU, MENU_TYPE_OPTIONS, MENU_TYPE_PAGE, MENU_VISIBILITY_OPTIONS, type MatchMode, MenuManager, type MenuManagerProps, type MenuResource, type MenuResourceApi, type MenuResourceFormValues, type MenuResourceListResult, type MenuResourceType, type PageDirection, type PermissionChecker, type PermissionCode, type PermissionListener, PermissionProvider, type PermissionProviderProps, type PermissionState, PermissionStore, RESOURCE_PRIVATE_OPTIONS, RESOURCE_STATUS_DISABLED, RESOURCE_STATUS_ENABLED, RESOURCE_STATUS_OPTIONS, RESOURCE_TYPE_API, ROOT_PARENT_ID, type ResourceHttpRequest, ResourceManager, type ResourceManagerProps, type ResourceRequestOptions, type ResourceStatus, type ResourceType, type RoleCode, SYSTEM_ADMIN_DEFAULT_KEY, SYSTEM_ADMIN_MENU, SystemAdmin, type SystemAdminMenuGroup, type SystemAdminMenuKey, type SystemAdminProps, type SystemAdminSubMenu, type UpdateAuthorizationResourceBody, type UpdateMenuResourceBody, type UsePermissionResult, appendQueryParam, buildCreateBody, buildCreateMenuBody, buildUpdateBody, buildUpdateMenuBody, createAuthorizationResourceApi, createDefaultResourceRequest, createMenuResourceApi, createPermissionStore, extractPagination, extractRecords, getAppClientId, getDeviceWorkerId, mapAuthorizationResource, mapMenuResource, snowyflake, useHasPermission, useHasRole, usePermission };
389
+ export { type ApiResourceType, type AuthorizationResource, type AuthorizationResourceApi, type AuthorizationResourceFormValues, type AuthorizationResourceListResult, Can, type CanProps, type CheckOptions, type CreateAuthorizationResourceBody, type CreateDefaultResourceRequestOptions, type CreateMenuResourceBody, type FetchAuthorizationResourcesParams, type FetchMenuResourcesParams, type HttpMethod, type ListPaginationParams, type ListPaginationResult, MENU_LEAF_NO, MENU_LEAF_YES, MENU_LIST_TYPE_PARAM, MENU_TYPE_BUTTON, MENU_TYPE_LABEL, MENU_TYPE_MENU, MENU_TYPE_OPTIONS, MENU_TYPE_PAGE, MENU_VISIBILITY_OPTIONS, type MatchMode, MenuManager, type MenuManagerProps, type MenuResource, type MenuResourceApi, type MenuResourceFormValues, type MenuResourceListResult, type MenuResourceType, type PageDirection, type PermissionChecker, type PermissionCode, type PermissionListener, PermissionProvider, type PermissionProviderProps, type PermissionState, PermissionStore, RESOURCE_PRIVATE_OPTIONS, RESOURCE_STATUS_DISABLED, RESOURCE_STATUS_ENABLED, RESOURCE_STATUS_OPTIONS, RESOURCE_TYPE_API, ROOT_MENU_DEPTH, ROOT_PARENT_ID, type ResourceHttpRequest, ResourceManager, type ResourceManagerProps, type ResourceRequestOptions, type ResourceStatus, type ResourceType, type RoleCode, SYSTEM_ADMIN_DEFAULT_KEY, SYSTEM_ADMIN_MENU, SystemAdmin, type SystemAdminMenuGroup, type SystemAdminMenuKey, type SystemAdminProps, type SystemAdminSubMenu, type UpdateAuthorizationResourceBody, type UpdateMenuResourceBody, type UsePermissionResult, appendQueryParam, buildCreateBody, buildCreateMenuBody, buildUpdateBody, buildUpdateMenuBody, createAuthorizationResourceApi, createDefaultResourceRequest, createMenuResourceApi, createPermissionStore, extractPagination, extractRecords, getAppClientId, getDeviceWorkerId, isMenuLeaf, mapAuthorizationResource, mapMenuResource, resolveMenuDepth, snowyflake, useHasPermission, useHasRole, usePermission };
@@ -170,6 +170,14 @@ type MenuResource = {
170
170
  resourceId: string;
171
171
  /** 上级菜单 ID;无上级时为 null */
172
172
  parentId: string | null;
173
+ /**
174
+ * 菜单层级:无上级(根)为 1,其余为上级 depth + 1
175
+ */
176
+ depth: number;
177
+ /**
178
+ * 是否叶子节点:`1` 表示无下级菜单,不可再展开
179
+ */
180
+ leaf: number;
173
181
  type: MenuResourceType;
174
182
  name: string;
175
183
  identification: string;
@@ -181,6 +189,8 @@ type MenuResource = {
181
189
  type MenuResourceFormValues = {
182
190
  /** 上级菜单 ID;选择「无」时为 null */
183
191
  parentId: string | null;
192
+ /** 由 parentId 自动计算:无为 1,否则上级 depth + 1 */
193
+ depth: number;
184
194
  type: MenuResourceType;
185
195
  name: string;
186
196
  identification: string;
@@ -192,6 +202,8 @@ type CreateMenuResourceBody = MenuResourceFormValues & {
192
202
  };
193
203
  type UpdateMenuResourceBody = Partial<MenuResourceFormValues>;
194
204
  type FetchMenuResourcesParams = {
205
+ /** 列表默认传 1,只拉根层 */
206
+ depth?: number;
195
207
  pagination?: ListPaginationParams;
196
208
  };
197
209
  type MenuResourceListResult = {
@@ -200,6 +212,9 @@ type MenuResourceListResult = {
200
212
  hasPreviousPage: boolean;
201
213
  hasNextPage: boolean;
202
214
  };
215
+ /** leaf === 1 表示无下级 */
216
+ declare const MENU_LEAF_YES = 1;
217
+ declare const MENU_LEAF_NO = 0;
203
218
  declare const RESOURCE_TYPE_API: ApiResourceType;
204
219
  declare const MENU_TYPE_PAGE: MenuResourceType;
205
220
  declare const MENU_TYPE_MENU: MenuResourceType;
@@ -211,6 +226,8 @@ declare const MENU_LIST_TYPE_PARAM = "page,menu,button";
211
226
  * 提交接口时会转换为 parentId: null。
212
227
  */
213
228
  declare const ROOT_PARENT_ID = "";
229
+ /** 无上级时的菜单层级 */
230
+ declare const ROOT_MENU_DEPTH = 1;
214
231
  declare const RESOURCE_STATUS_ENABLED: ResourceStatus;
215
232
  declare const RESOURCE_STATUS_DISABLED: ResourceStatus;
216
233
  declare const RESOURCE_STATUS_OPTIONS: {
@@ -251,6 +268,8 @@ type AuthorizationResourceApi = {
251
268
  };
252
269
  type MenuResourceApi = {
253
270
  list: (params?: FetchMenuResourcesParams, signal?: AbortSignal) => Promise<MenuResourceListResult>;
271
+ /** 展开下级:GET /authorization-resources/{resource-id}/sub-resources */
272
+ listSubResources: (resourceId: string, signal?: AbortSignal) => Promise<MenuResource[]>;
254
273
  /** 拉取权限点(type=api),供菜单表单多选 */
255
274
  listPermissionPoints: (params?: FetchAuthorizationResourcesParams, signal?: AbortSignal) => Promise<AuthorizationResourceListResult>;
256
275
  create: (values: MenuResourceFormValues, signal?: AbortSignal) => Promise<unknown>;
@@ -259,6 +278,14 @@ type MenuResourceApi = {
259
278
  };
260
279
  declare function mapAuthorizationResource(item: unknown): AuthorizationResource | null;
261
280
  declare function mapMenuResource(item: unknown): MenuResource | null;
281
+ /**
282
+ * leaf === 1 表示无下级菜单。
283
+ */
284
+ declare function isMenuLeaf(row: Pick<MenuResource, 'leaf'>): boolean;
285
+ /**
286
+ * 根据上级菜单计算 depth:无上级为 1,否则为上级 depth + 1。
287
+ */
288
+ declare function resolveMenuDepth(parentId: string | null | undefined, menus: Array<Pick<MenuResource, 'resourceId' | 'depth'>>): number;
262
289
  declare function buildCreateBody(values: AuthorizationResourceFormValues, resourceId?: string): CreateAuthorizationResourceBody;
263
290
  declare function buildUpdateBody(values: AuthorizationResourceFormValues): UpdateAuthorizationResourceBody;
264
291
  declare function buildCreateMenuBody(values: MenuResourceFormValues, resourceId?: string): CreateMenuResourceBody;
@@ -359,4 +386,4 @@ declare const snowyflake: ConfigurableSnowflake;
359
386
  declare function getAppClientId(): string;
360
387
  declare function getDeviceWorkerId(): string;
361
388
 
362
- export { type ApiResourceType, type AuthorizationResource, type AuthorizationResourceApi, type AuthorizationResourceFormValues, type AuthorizationResourceListResult, Can, type CanProps, type CheckOptions, type CreateAuthorizationResourceBody, type CreateDefaultResourceRequestOptions, type CreateMenuResourceBody, type FetchAuthorizationResourcesParams, type FetchMenuResourcesParams, type HttpMethod, type ListPaginationParams, type ListPaginationResult, MENU_LIST_TYPE_PARAM, MENU_TYPE_BUTTON, MENU_TYPE_LABEL, MENU_TYPE_MENU, MENU_TYPE_OPTIONS, MENU_TYPE_PAGE, MENU_VISIBILITY_OPTIONS, type MatchMode, MenuManager, type MenuManagerProps, type MenuResource, type MenuResourceApi, type MenuResourceFormValues, type MenuResourceListResult, type MenuResourceType, type PageDirection, type PermissionChecker, type PermissionCode, type PermissionListener, PermissionProvider, type PermissionProviderProps, type PermissionState, PermissionStore, RESOURCE_PRIVATE_OPTIONS, RESOURCE_STATUS_DISABLED, RESOURCE_STATUS_ENABLED, RESOURCE_STATUS_OPTIONS, RESOURCE_TYPE_API, ROOT_PARENT_ID, type ResourceHttpRequest, ResourceManager, type ResourceManagerProps, type ResourceRequestOptions, type ResourceStatus, type ResourceType, type RoleCode, SYSTEM_ADMIN_DEFAULT_KEY, SYSTEM_ADMIN_MENU, SystemAdmin, type SystemAdminMenuGroup, type SystemAdminMenuKey, type SystemAdminProps, type SystemAdminSubMenu, type UpdateAuthorizationResourceBody, type UpdateMenuResourceBody, type UsePermissionResult, appendQueryParam, buildCreateBody, buildCreateMenuBody, buildUpdateBody, buildUpdateMenuBody, createAuthorizationResourceApi, createDefaultResourceRequest, createMenuResourceApi, createPermissionStore, extractPagination, extractRecords, getAppClientId, getDeviceWorkerId, mapAuthorizationResource, mapMenuResource, snowyflake, useHasPermission, useHasRole, usePermission };
389
+ export { type ApiResourceType, type AuthorizationResource, type AuthorizationResourceApi, type AuthorizationResourceFormValues, type AuthorizationResourceListResult, Can, type CanProps, type CheckOptions, type CreateAuthorizationResourceBody, type CreateDefaultResourceRequestOptions, type CreateMenuResourceBody, type FetchAuthorizationResourcesParams, type FetchMenuResourcesParams, type HttpMethod, type ListPaginationParams, type ListPaginationResult, MENU_LEAF_NO, MENU_LEAF_YES, MENU_LIST_TYPE_PARAM, MENU_TYPE_BUTTON, MENU_TYPE_LABEL, MENU_TYPE_MENU, MENU_TYPE_OPTIONS, MENU_TYPE_PAGE, MENU_VISIBILITY_OPTIONS, type MatchMode, MenuManager, type MenuManagerProps, type MenuResource, type MenuResourceApi, type MenuResourceFormValues, type MenuResourceListResult, type MenuResourceType, type PageDirection, type PermissionChecker, type PermissionCode, type PermissionListener, PermissionProvider, type PermissionProviderProps, type PermissionState, PermissionStore, RESOURCE_PRIVATE_OPTIONS, RESOURCE_STATUS_DISABLED, RESOURCE_STATUS_ENABLED, RESOURCE_STATUS_OPTIONS, RESOURCE_TYPE_API, ROOT_MENU_DEPTH, ROOT_PARENT_ID, type ResourceHttpRequest, ResourceManager, type ResourceManagerProps, type ResourceRequestOptions, type ResourceStatus, type ResourceType, type RoleCode, SYSTEM_ADMIN_DEFAULT_KEY, SYSTEM_ADMIN_MENU, SystemAdmin, type SystemAdminMenuGroup, type SystemAdminMenuKey, type SystemAdminProps, type SystemAdminSubMenu, type UpdateAuthorizationResourceBody, type UpdateMenuResourceBody, type UsePermissionResult, appendQueryParam, buildCreateBody, buildCreateMenuBody, buildUpdateBody, buildUpdateMenuBody, createAuthorizationResourceApi, createDefaultResourceRequest, createMenuResourceApi, createPermissionStore, extractPagination, extractRecords, getAppClientId, getDeviceWorkerId, isMenuLeaf, mapAuthorizationResource, mapMenuResource, resolveMenuDepth, snowyflake, useHasPermission, useHasRole, usePermission };
@@ -232,12 +232,15 @@ import {
232
232
  } from "react";
233
233
 
234
234
  // src/resources/types.ts
235
+ var MENU_LEAF_YES = 1;
236
+ var MENU_LEAF_NO = 0;
235
237
  var RESOURCE_TYPE_API = "api";
236
238
  var MENU_TYPE_PAGE = "page";
237
239
  var MENU_TYPE_MENU = "menu";
238
240
  var MENU_TYPE_BUTTON = "button";
239
241
  var MENU_LIST_TYPE_PARAM = "page,menu,button";
240
242
  var ROOT_PARENT_ID = "";
243
+ var ROOT_MENU_DEPTH = 1;
241
244
  var RESOURCE_STATUS_ENABLED = 1;
242
245
  var RESOURCE_STATUS_DISABLED = 0;
243
246
  var RESOURCE_STATUS_OPTIONS = [
@@ -456,9 +459,16 @@ function mapMenuResource(item) {
456
459
  const permissionPointNames = Array.isArray(permissionNamesRaw) ? permissionNamesRaw.map((n) => String(n ?? "")).filter(Boolean) : void 0;
457
460
  const rawParentId = row.parentId ?? row.parent_id;
458
461
  const parentId = rawParentId === void 0 || rawParentId === null || rawParentId === "" || String(rawParentId) === "0" ? null : String(rawParentId);
462
+ const rawDepth = row.depth;
463
+ const parsedDepth = Number(rawDepth);
464
+ const depth = Number.isFinite(parsedDepth) && parsedDepth > 0 ? Math.floor(parsedDepth) : parentId ? ROOT_MENU_DEPTH + 1 : ROOT_MENU_DEPTH;
465
+ const rawLeaf = row.leaf;
466
+ const leaf = rawLeaf === true || rawLeaf === 1 || rawLeaf === "1" || String(rawLeaf).toLowerCase() === "true" ? MENU_LEAF_YES : Number(rawLeaf) === MENU_LEAF_YES ? MENU_LEAF_YES : 0;
459
467
  return {
460
468
  resourceId: String(resourceId),
461
469
  parentId,
470
+ depth,
471
+ leaf,
462
472
  type: toMenuType(row.type),
463
473
  name: String(row.name ?? ""),
464
474
  identification: String(row.identification ?? row.identity ?? row.path ?? ""),
@@ -467,9 +477,21 @@ function mapMenuResource(item) {
467
477
  status: toResourceStatus(row.status)
468
478
  };
469
479
  }
480
+ function isMenuLeaf(row) {
481
+ return Number(row.leaf) === MENU_LEAF_YES;
482
+ }
483
+ function resolveMenuDepth(parentId, menus) {
484
+ if (!parentId) return ROOT_MENU_DEPTH;
485
+ const parent = menus.find((item) => item.resourceId === parentId);
486
+ const parentDepth = parent && Number.isFinite(parent.depth) && parent.depth > 0 ? Math.floor(parent.depth) : ROOT_MENU_DEPTH;
487
+ return parentDepth + 1;
488
+ }
470
489
  function buildListUrl(type, params) {
471
490
  const parts = [];
472
491
  appendQueryParam(parts, "type", type);
492
+ if (params?.depth !== void 0 && params?.depth !== null && String(params.depth) !== "") {
493
+ appendQueryParam(parts, "depth", String(params.depth));
494
+ }
473
495
  appendQueryParam(parts, "page-token", params?.pagination?.pageToken ?? "");
474
496
  appendQueryParam(parts, "page-size", params?.pagination?.pageSize ?? "20");
475
497
  appendQueryParam(parts, "page-direction", params?.pagination?.pageDirection ?? "current");
@@ -497,9 +519,11 @@ function buildUpdateBody(values) {
497
519
  };
498
520
  }
499
521
  function buildCreateMenuBody(values, resourceId = getAppClientId()) {
522
+ const parentId = values.parentId ? values.parentId : null;
500
523
  return {
501
524
  resourceId,
502
- parentId: values.parentId ? values.parentId : null,
525
+ parentId,
526
+ depth: values.depth > 0 ? Math.floor(values.depth) : parentId ? ROOT_MENU_DEPTH + 1 : ROOT_MENU_DEPTH,
503
527
  type: values.type,
504
528
  name: values.name.trim(),
505
529
  identification: values.identification.trim(),
@@ -508,8 +532,10 @@ function buildCreateMenuBody(values, resourceId = getAppClientId()) {
508
532
  };
509
533
  }
510
534
  function buildUpdateMenuBody(values) {
535
+ const parentId = values.parentId ? values.parentId : null;
511
536
  return {
512
- parentId: values.parentId ? values.parentId : null,
537
+ parentId,
538
+ depth: values.depth > 0 ? Math.floor(values.depth) : parentId ? ROOT_MENU_DEPTH + 1 : ROOT_MENU_DEPTH,
513
539
  type: values.type,
514
540
  name: values.name.trim(),
515
541
  identification: values.identification.trim(),
@@ -563,7 +589,10 @@ function createMenuResourceApi(request) {
563
589
  async list(params, signal) {
564
590
  const json = await request({
565
591
  method: "GET",
566
- url: buildListUrl(MENU_LIST_TYPE_PARAM, params),
592
+ url: buildListUrl(MENU_LIST_TYPE_PARAM, {
593
+ pagination: params?.pagination,
594
+ depth: params?.depth ?? ROOT_MENU_DEPTH
595
+ }),
567
596
  signal
568
597
  });
569
598
  const records = extractRecords(json).map(mapMenuResource).filter((item) => item !== null);
@@ -573,6 +602,14 @@ function createMenuResourceApi(request) {
573
602
  ...pagination
574
603
  };
575
604
  },
605
+ async listSubResources(resourceId, signal) {
606
+ const json = await request({
607
+ method: "GET",
608
+ url: `/authorization-resources/${encodeURIComponent(resourceId)}/sub-resources`,
609
+ signal
610
+ });
611
+ return extractRecords(json).map(mapMenuResource).filter((item) => item !== null);
612
+ },
576
613
  listPermissionPoints(params, signal) {
577
614
  return permissionApi.list(params, signal);
578
615
  },
@@ -1179,6 +1216,7 @@ import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
1179
1216
  var PAGE_SIZE_OPTIONS2 = ["10", "20", "50", "100"];
1180
1217
  var emptyForm2 = () => ({
1181
1218
  parentId: null,
1219
+ depth: ROOT_MENU_DEPTH,
1182
1220
  type: MENU_TYPE_MENU,
1183
1221
  name: "",
1184
1222
  identification: "",
@@ -1192,6 +1230,9 @@ function MenuManager({
1192
1230
  toolbarExtra
1193
1231
  }) {
1194
1232
  const [records, setRecords] = useState3([]);
1233
+ const [childrenMap, setChildrenMap] = useState3({});
1234
+ const [expandedIds, setExpandedIds] = useState3({});
1235
+ const [expandingIds, setExpandingIds] = useState3({});
1195
1236
  const [permissionPoints, setPermissionPoints] = useState3([]);
1196
1237
  const [loading, setLoading] = useState3(false);
1197
1238
  const [error, setError] = useState3("");
@@ -1210,18 +1251,40 @@ function MenuManager({
1210
1251
  permissionPoints.forEach((p) => map.set(p.resourceId, p.name || p.identification));
1211
1252
  return map;
1212
1253
  }, [permissionPoints]);
1254
+ const allKnownMenus = useMemo3(() => {
1255
+ const map = /* @__PURE__ */ new Map();
1256
+ records.forEach((row) => map.set(row.resourceId, row));
1257
+ Object.values(childrenMap).forEach((list) => {
1258
+ list.forEach((row) => map.set(row.resourceId, row));
1259
+ });
1260
+ return Array.from(map.values());
1261
+ }, [records, childrenMap]);
1213
1262
  const parentOptions = useMemo3(() => {
1214
- return records.filter((row) => !editing || row.resourceId !== editing.resourceId).map((row) => ({
1263
+ return allKnownMenus.filter((row) => !editing || row.resourceId !== editing.resourceId).map((row) => ({
1215
1264
  value: row.resourceId,
1216
1265
  label: `${row.name}\uFF08${MENU_TYPE_LABEL[row.type]}\uFF09`
1217
1266
  }));
1218
- }, [records, editing]);
1267
+ }, [allKnownMenus, editing]);
1268
+ const flatRows = useMemo3(() => {
1269
+ const rows = [];
1270
+ const walk = (list) => {
1271
+ list.forEach((row) => {
1272
+ rows.push(row);
1273
+ if (expandedIds[row.resourceId] && childrenMap[row.resourceId]?.length) {
1274
+ walk(childrenMap[row.resourceId]);
1275
+ }
1276
+ });
1277
+ };
1278
+ walk(records);
1279
+ return rows;
1280
+ }, [records, childrenMap, expandedIds]);
1219
1281
  const loadList = useCallback3(
1220
1282
  async (direction = "current", token = pageToken) => {
1221
1283
  setLoading(true);
1222
1284
  setError("");
1223
1285
  try {
1224
1286
  const result = await api.list({
1287
+ depth: ROOT_MENU_DEPTH,
1225
1288
  pagination: {
1226
1289
  pageToken: token,
1227
1290
  pageSize,
@@ -1229,6 +1292,9 @@ function MenuManager({
1229
1292
  }
1230
1293
  });
1231
1294
  setRecords(result.records);
1295
+ setChildrenMap({});
1296
+ setExpandedIds({});
1297
+ setExpandingIds({});
1232
1298
  setPageToken(result.pageToken);
1233
1299
  setHasPreviousPage(result.hasPreviousPage);
1234
1300
  setHasNextPage(result.hasNextPage);
@@ -1249,6 +1315,29 @@ function MenuManager({
1249
1315
  } catch {
1250
1316
  }
1251
1317
  }, [api]);
1318
+ const toggleExpand = async (row) => {
1319
+ if (isMenuLeaf(row)) return;
1320
+ const id = row.resourceId;
1321
+ if (expandedIds[id]) {
1322
+ setExpandedIds((prev) => ({ ...prev, [id]: false }));
1323
+ return;
1324
+ }
1325
+ if (!childrenMap[id]) {
1326
+ setExpandingIds((prev) => ({ ...prev, [id]: true }));
1327
+ setError("");
1328
+ try {
1329
+ const children = await api.listSubResources(id);
1330
+ setChildrenMap((prev) => ({ ...prev, [id]: children }));
1331
+ setExpandedIds((prev) => ({ ...prev, [id]: true }));
1332
+ } catch (err) {
1333
+ setError(err instanceof Error ? err.message : "\u52A0\u8F7D\u4E0B\u7EA7\u83DC\u5355\u5931\u8D25");
1334
+ } finally {
1335
+ setExpandingIds((prev) => ({ ...prev, [id]: false }));
1336
+ }
1337
+ return;
1338
+ }
1339
+ setExpandedIds((prev) => ({ ...prev, [id]: true }));
1340
+ };
1252
1341
  useEffect3(() => {
1253
1342
  void loadList("current", "");
1254
1343
  void loadPermissionPoints();
@@ -1263,6 +1352,7 @@ function MenuManager({
1263
1352
  setEditing(row);
1264
1353
  setForm({
1265
1354
  parentId: row.parentId ?? null,
1355
+ depth: resolveMenuDepth(row.parentId ?? null, allKnownMenus),
1266
1356
  type: row.type,
1267
1357
  name: row.name,
1268
1358
  identification: row.identification,
@@ -1350,35 +1440,54 @@ function MenuManager({
1350
1440
  /* @__PURE__ */ jsx3("th", { style: styles2.th, children: "\u6743\u9650\u6807\u8BC6" }),
1351
1441
  /* @__PURE__ */ jsx3("th", { style: { ...styles2.th, width: 140 }, children: "\u64CD\u4F5C" })
1352
1442
  ] }) }),
1353
- /* @__PURE__ */ jsx3("tbody", { children: loading ? /* @__PURE__ */ jsx3("tr", { children: /* @__PURE__ */ jsx3("td", { colSpan: 6, style: styles2.empty, children: "\u52A0\u8F7D\u4E2D\u2026" }) }) : records.length === 0 ? /* @__PURE__ */ jsx3("tr", { children: /* @__PURE__ */ jsx3("td", { colSpan: 6, style: styles2.empty, children: "\u6682\u65E0\u6570\u636E" }) }) : records.map((row) => /* @__PURE__ */ jsxs2("tr", { children: [
1354
- /* @__PURE__ */ jsx3("td", { style: styles2.td, children: row.name }),
1355
- /* @__PURE__ */ jsx3("td", { style: styles2.td, children: /* @__PURE__ */ jsx3("code", { style: styles2.code, children: row.identification || "\u2014" }) }),
1356
- /* @__PURE__ */ jsx3("td", { style: styles2.td, children: MENU_TYPE_LABEL[row.type] }),
1357
- /* @__PURE__ */ jsx3("td", { style: styles2.td, children: /* @__PURE__ */ jsx3(
1358
- "span",
1359
- {
1360
- style: {
1361
- ...styles2.badge,
1362
- background: row.status === RESOURCE_STATUS_ENABLED ? "#e8f7ef" : "#f4f4f5",
1363
- color: row.status === RESOURCE_STATUS_ENABLED ? "#067647" : "#667085"
1364
- },
1365
- children: row.status === RESOURCE_STATUS_ENABLED ? "\u663E\u793A" : "\u9690\u85CF"
1366
- }
1367
- ) }),
1368
- /* @__PURE__ */ jsx3("td", { style: styles2.td, children: renderPermissionLabels(row) }),
1369
- /* @__PURE__ */ jsxs2("td", { style: styles2.td, children: [
1370
- /* @__PURE__ */ jsx3("button", { type: "button", style: styles2.linkBtn, onClick: () => openEdit(row), children: "\u7F16\u8F91" }),
1371
- /* @__PURE__ */ jsx3(
1372
- "button",
1443
+ /* @__PURE__ */ jsx3("tbody", { children: loading ? /* @__PURE__ */ jsx3("tr", { children: /* @__PURE__ */ jsx3("td", { colSpan: 6, style: styles2.empty, children: "\u52A0\u8F7D\u4E2D\u2026" }) }) : flatRows.length === 0 ? /* @__PURE__ */ jsx3("tr", { children: /* @__PURE__ */ jsx3("td", { colSpan: 6, style: styles2.empty, children: "\u6682\u65E0\u6570\u636E" }) }) : flatRows.map((row) => {
1444
+ const leaf = isMenuLeaf(row);
1445
+ const expanded = Boolean(expandedIds[row.resourceId]);
1446
+ const expanding = Boolean(expandingIds[row.resourceId]);
1447
+ const indent = Math.max(0, (row.depth || 1) - 1) * 18;
1448
+ return /* @__PURE__ */ jsxs2("tr", { children: [
1449
+ /* @__PURE__ */ jsx3("td", { style: styles2.td, children: /* @__PURE__ */ jsxs2("div", { style: { ...styles2.treeCell, paddingLeft: indent }, children: [
1450
+ leaf ? /* @__PURE__ */ jsx3("span", { style: styles2.treeSpacer }) : /* @__PURE__ */ jsx3(
1451
+ "button",
1452
+ {
1453
+ type: "button",
1454
+ style: styles2.treeToggle,
1455
+ onClick: () => void toggleExpand(row),
1456
+ disabled: expanding,
1457
+ "aria-label": expanded ? "\u6536\u8D77" : "\u5C55\u5F00",
1458
+ children: expanding ? "\u2026" : expanded ? "\u25BC" : "\u25B6"
1459
+ }
1460
+ ),
1461
+ /* @__PURE__ */ jsx3("span", { children: row.name })
1462
+ ] }) }),
1463
+ /* @__PURE__ */ jsx3("td", { style: styles2.td, children: /* @__PURE__ */ jsx3("code", { style: styles2.code, children: row.identification || "\u2014" }) }),
1464
+ /* @__PURE__ */ jsx3("td", { style: styles2.td, children: MENU_TYPE_LABEL[row.type] }),
1465
+ /* @__PURE__ */ jsx3("td", { style: styles2.td, children: /* @__PURE__ */ jsx3(
1466
+ "span",
1373
1467
  {
1374
- type: "button",
1375
- style: { ...styles2.linkBtn, color: "#d92d20" },
1376
- onClick: () => askDelete(row),
1377
- children: "\u5220\u9664"
1468
+ style: {
1469
+ ...styles2.badge,
1470
+ background: row.status === RESOURCE_STATUS_ENABLED ? "#e8f7ef" : "#f4f4f5",
1471
+ color: row.status === RESOURCE_STATUS_ENABLED ? "#067647" : "#667085"
1472
+ },
1473
+ children: row.status === RESOURCE_STATUS_ENABLED ? "\u663E\u793A" : "\u9690\u85CF"
1378
1474
  }
1379
- )
1380
- ] })
1381
- ] }, row.resourceId)) })
1475
+ ) }),
1476
+ /* @__PURE__ */ jsx3("td", { style: styles2.td, children: renderPermissionLabels(row) }),
1477
+ /* @__PURE__ */ jsxs2("td", { style: styles2.td, children: [
1478
+ /* @__PURE__ */ jsx3("button", { type: "button", style: styles2.linkBtn, onClick: () => openEdit(row), children: "\u7F16\u8F91" }),
1479
+ /* @__PURE__ */ jsx3(
1480
+ "button",
1481
+ {
1482
+ type: "button",
1483
+ style: { ...styles2.linkBtn, color: "#d92d20" },
1484
+ onClick: () => askDelete(row),
1485
+ children: "\u5220\u9664"
1486
+ }
1487
+ )
1488
+ ] })
1489
+ ] }, row.resourceId);
1490
+ }) })
1382
1491
  ] }) }),
1383
1492
  /* @__PURE__ */ jsxs2("div", { style: styles2.pagination, children: [
1384
1493
  /* @__PURE__ */ jsxs2("label", { style: styles2.pageSize, children: [
@@ -1439,10 +1548,14 @@ function MenuManager({
1439
1548
  {
1440
1549
  style: styles2.input,
1441
1550
  value: form.parentId ?? ROOT_PARENT_ID,
1442
- onChange: (e) => setForm((prev) => ({
1443
- ...prev,
1444
- parentId: e.target.value ? e.target.value : null
1445
- })),
1551
+ onChange: (e) => {
1552
+ const parentId = e.target.value ? e.target.value : null;
1553
+ setForm((prev) => ({
1554
+ ...prev,
1555
+ parentId,
1556
+ depth: resolveMenuDepth(parentId, allKnownMenus)
1557
+ }));
1558
+ },
1446
1559
  children: [
1447
1560
  /* @__PURE__ */ jsx3("option", { value: ROOT_PARENT_ID, children: "\u65E0" }),
1448
1561
  parentOptions.map((opt) => /* @__PURE__ */ jsx3("option", { value: opt.value, children: opt.label }, opt.value))
@@ -1450,6 +1563,10 @@ function MenuManager({
1450
1563
  }
1451
1564
  )
1452
1565
  ] }),
1566
+ /* @__PURE__ */ jsxs2("div", { style: styles2.field, children: [
1567
+ /* @__PURE__ */ jsx3("span", { style: styles2.label, children: "\u83DC\u5355\u5C42\u7EA7\uFF08depth\uFF09" }),
1568
+ /* @__PURE__ */ jsx3("input", { style: styles2.input, value: form.depth, readOnly: true })
1569
+ ] }),
1453
1570
  /* @__PURE__ */ jsxs2("fieldset", { style: styles2.fieldset, children: [
1454
1571
  /* @__PURE__ */ jsx3("legend", { style: styles2.label, children: "\u83DC\u5355\u7C7B\u578B" }),
1455
1572
  /* @__PURE__ */ jsx3("div", { style: styles2.radioGroup, children: MENU_TYPE_OPTIONS.map((opt) => /* @__PURE__ */ jsxs2("label", { style: styles2.radioItem, children: [
@@ -1634,6 +1751,30 @@ var styles2 = {
1634
1751
  borderBottom: "1px solid #f2f4f7",
1635
1752
  verticalAlign: "middle"
1636
1753
  },
1754
+ treeCell: {
1755
+ display: "flex",
1756
+ alignItems: "center",
1757
+ gap: 6
1758
+ },
1759
+ treeToggle: {
1760
+ width: 22,
1761
+ height: 22,
1762
+ border: "1px solid #d0d5dd",
1763
+ borderRadius: 4,
1764
+ background: "#fff",
1765
+ color: "#475467",
1766
+ cursor: "pointer",
1767
+ fontSize: 10,
1768
+ lineHeight: "20px",
1769
+ padding: 0,
1770
+ flexShrink: 0
1771
+ },
1772
+ treeSpacer: {
1773
+ width: 22,
1774
+ height: 22,
1775
+ flexShrink: 0,
1776
+ display: "inline-block"
1777
+ },
1637
1778
  empty: { padding: 28, textAlign: "center", color: "#98a2b3", fontSize: 14 },
1638
1779
  code: {
1639
1780
  fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace",
@@ -1942,6 +2083,8 @@ var styles3 = {
1942
2083
  };
1943
2084
  export {
1944
2085
  Can,
2086
+ MENU_LEAF_NO,
2087
+ MENU_LEAF_YES,
1945
2088
  MENU_LIST_TYPE_PARAM,
1946
2089
  MENU_TYPE_BUTTON,
1947
2090
  MENU_TYPE_LABEL,
@@ -1957,6 +2100,7 @@ export {
1957
2100
  RESOURCE_STATUS_ENABLED,
1958
2101
  RESOURCE_STATUS_OPTIONS,
1959
2102
  RESOURCE_TYPE_API,
2103
+ ROOT_MENU_DEPTH,
1960
2104
  ROOT_PARENT_ID,
1961
2105
  ResourceManager,
1962
2106
  SYSTEM_ADMIN_DEFAULT_KEY,
@@ -1975,8 +2119,10 @@ export {
1975
2119
  extractRecords,
1976
2120
  getAppClientId,
1977
2121
  getDeviceWorkerId,
2122
+ isMenuLeaf,
1978
2123
  mapAuthorizationResource,
1979
2124
  mapMenuResource,
2125
+ resolveMenuDepth,
1980
2126
  snowyflake,
1981
2127
  useHasPermission,
1982
2128
  useHasRole,