com-angel-authorization 1.0.9 → 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.
@@ -174,6 +174,10 @@ type MenuResource = {
174
174
  * 菜单层级:无上级(根)为 1,其余为上级 depth + 1
175
175
  */
176
176
  depth: number;
177
+ /**
178
+ * 是否叶子节点:`1` 表示无下级菜单,不可再展开
179
+ */
180
+ leaf: number;
177
181
  type: MenuResourceType;
178
182
  name: string;
179
183
  identification: string;
@@ -198,6 +202,8 @@ type CreateMenuResourceBody = MenuResourceFormValues & {
198
202
  };
199
203
  type UpdateMenuResourceBody = Partial<MenuResourceFormValues>;
200
204
  type FetchMenuResourcesParams = {
205
+ /** 列表默认传 1,只拉根层 */
206
+ depth?: number;
201
207
  pagination?: ListPaginationParams;
202
208
  };
203
209
  type MenuResourceListResult = {
@@ -206,6 +212,9 @@ type MenuResourceListResult = {
206
212
  hasPreviousPage: boolean;
207
213
  hasNextPage: boolean;
208
214
  };
215
+ /** leaf === 1 表示无下级 */
216
+ declare const MENU_LEAF_YES = 1;
217
+ declare const MENU_LEAF_NO = 0;
209
218
  declare const RESOURCE_TYPE_API: ApiResourceType;
210
219
  declare const MENU_TYPE_PAGE: MenuResourceType;
211
220
  declare const MENU_TYPE_MENU: MenuResourceType;
@@ -259,6 +268,8 @@ type AuthorizationResourceApi = {
259
268
  };
260
269
  type MenuResourceApi = {
261
270
  list: (params?: FetchMenuResourcesParams, signal?: AbortSignal) => Promise<MenuResourceListResult>;
271
+ /** 展开下级:GET /authorization-resources/{resource-id}/sub-resources */
272
+ listSubResources: (resourceId: string, signal?: AbortSignal) => Promise<MenuResource[]>;
262
273
  /** 拉取权限点(type=api),供菜单表单多选 */
263
274
  listPermissionPoints: (params?: FetchAuthorizationResourcesParams, signal?: AbortSignal) => Promise<AuthorizationResourceListResult>;
264
275
  create: (values: MenuResourceFormValues, signal?: AbortSignal) => Promise<unknown>;
@@ -267,6 +278,10 @@ type MenuResourceApi = {
267
278
  };
268
279
  declare function mapAuthorizationResource(item: unknown): AuthorizationResource | null;
269
280
  declare function mapMenuResource(item: unknown): MenuResource | null;
281
+ /**
282
+ * leaf === 1 表示无下级菜单。
283
+ */
284
+ declare function isMenuLeaf(row: Pick<MenuResource, 'leaf'>): boolean;
270
285
  /**
271
286
  * 根据上级菜单计算 depth:无上级为 1,否则为上级 depth + 1。
272
287
  */
@@ -371,4 +386,4 @@ declare const snowyflake: ConfigurableSnowflake;
371
386
  declare function getAppClientId(): string;
372
387
  declare function getDeviceWorkerId(): string;
373
388
 
374
- 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_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, mapAuthorizationResource, mapMenuResource, resolveMenuDepth, 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 };
@@ -174,6 +174,10 @@ type MenuResource = {
174
174
  * 菜单层级:无上级(根)为 1,其余为上级 depth + 1
175
175
  */
176
176
  depth: number;
177
+ /**
178
+ * 是否叶子节点:`1` 表示无下级菜单,不可再展开
179
+ */
180
+ leaf: number;
177
181
  type: MenuResourceType;
178
182
  name: string;
179
183
  identification: string;
@@ -198,6 +202,8 @@ type CreateMenuResourceBody = MenuResourceFormValues & {
198
202
  };
199
203
  type UpdateMenuResourceBody = Partial<MenuResourceFormValues>;
200
204
  type FetchMenuResourcesParams = {
205
+ /** 列表默认传 1,只拉根层 */
206
+ depth?: number;
201
207
  pagination?: ListPaginationParams;
202
208
  };
203
209
  type MenuResourceListResult = {
@@ -206,6 +212,9 @@ type MenuResourceListResult = {
206
212
  hasPreviousPage: boolean;
207
213
  hasNextPage: boolean;
208
214
  };
215
+ /** leaf === 1 表示无下级 */
216
+ declare const MENU_LEAF_YES = 1;
217
+ declare const MENU_LEAF_NO = 0;
209
218
  declare const RESOURCE_TYPE_API: ApiResourceType;
210
219
  declare const MENU_TYPE_PAGE: MenuResourceType;
211
220
  declare const MENU_TYPE_MENU: MenuResourceType;
@@ -259,6 +268,8 @@ type AuthorizationResourceApi = {
259
268
  };
260
269
  type MenuResourceApi = {
261
270
  list: (params?: FetchMenuResourcesParams, signal?: AbortSignal) => Promise<MenuResourceListResult>;
271
+ /** 展开下级:GET /authorization-resources/{resource-id}/sub-resources */
272
+ listSubResources: (resourceId: string, signal?: AbortSignal) => Promise<MenuResource[]>;
262
273
  /** 拉取权限点(type=api),供菜单表单多选 */
263
274
  listPermissionPoints: (params?: FetchAuthorizationResourcesParams, signal?: AbortSignal) => Promise<AuthorizationResourceListResult>;
264
275
  create: (values: MenuResourceFormValues, signal?: AbortSignal) => Promise<unknown>;
@@ -267,6 +278,10 @@ type MenuResourceApi = {
267
278
  };
268
279
  declare function mapAuthorizationResource(item: unknown): AuthorizationResource | null;
269
280
  declare function mapMenuResource(item: unknown): MenuResource | null;
281
+ /**
282
+ * leaf === 1 表示无下级菜单。
283
+ */
284
+ declare function isMenuLeaf(row: Pick<MenuResource, 'leaf'>): boolean;
270
285
  /**
271
286
  * 根据上级菜单计算 depth:无上级为 1,否则为上级 depth + 1。
272
287
  */
@@ -371,4 +386,4 @@ declare const snowyflake: ConfigurableSnowflake;
371
386
  declare function getAppClientId(): string;
372
387
  declare function getDeviceWorkerId(): string;
373
388
 
374
- 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_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, mapAuthorizationResource, mapMenuResource, resolveMenuDepth, 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,6 +232,8 @@ 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";
@@ -460,10 +462,13 @@ function mapMenuResource(item) {
460
462
  const rawDepth = row.depth;
461
463
  const parsedDepth = Number(rawDepth);
462
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;
463
467
  return {
464
468
  resourceId: String(resourceId),
465
469
  parentId,
466
470
  depth,
471
+ leaf,
467
472
  type: toMenuType(row.type),
468
473
  name: String(row.name ?? ""),
469
474
  identification: String(row.identification ?? row.identity ?? row.path ?? ""),
@@ -472,6 +477,9 @@ function mapMenuResource(item) {
472
477
  status: toResourceStatus(row.status)
473
478
  };
474
479
  }
480
+ function isMenuLeaf(row) {
481
+ return Number(row.leaf) === MENU_LEAF_YES;
482
+ }
475
483
  function resolveMenuDepth(parentId, menus) {
476
484
  if (!parentId) return ROOT_MENU_DEPTH;
477
485
  const parent = menus.find((item) => item.resourceId === parentId);
@@ -481,6 +489,9 @@ function resolveMenuDepth(parentId, menus) {
481
489
  function buildListUrl(type, params) {
482
490
  const parts = [];
483
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
+ }
484
495
  appendQueryParam(parts, "page-token", params?.pagination?.pageToken ?? "");
485
496
  appendQueryParam(parts, "page-size", params?.pagination?.pageSize ?? "20");
486
497
  appendQueryParam(parts, "page-direction", params?.pagination?.pageDirection ?? "current");
@@ -578,7 +589,10 @@ function createMenuResourceApi(request) {
578
589
  async list(params, signal) {
579
590
  const json = await request({
580
591
  method: "GET",
581
- 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
+ }),
582
596
  signal
583
597
  });
584
598
  const records = extractRecords(json).map(mapMenuResource).filter((item) => item !== null);
@@ -588,6 +602,14 @@ function createMenuResourceApi(request) {
588
602
  ...pagination
589
603
  };
590
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
+ },
591
613
  listPermissionPoints(params, signal) {
592
614
  return permissionApi.list(params, signal);
593
615
  },
@@ -1208,6 +1230,9 @@ function MenuManager({
1208
1230
  toolbarExtra
1209
1231
  }) {
1210
1232
  const [records, setRecords] = useState3([]);
1233
+ const [childrenMap, setChildrenMap] = useState3({});
1234
+ const [expandedIds, setExpandedIds] = useState3({});
1235
+ const [expandingIds, setExpandingIds] = useState3({});
1211
1236
  const [permissionPoints, setPermissionPoints] = useState3([]);
1212
1237
  const [loading, setLoading] = useState3(false);
1213
1238
  const [error, setError] = useState3("");
@@ -1226,18 +1251,40 @@ function MenuManager({
1226
1251
  permissionPoints.forEach((p) => map.set(p.resourceId, p.name || p.identification));
1227
1252
  return map;
1228
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]);
1229
1262
  const parentOptions = useMemo3(() => {
1230
- return records.filter((row) => !editing || row.resourceId !== editing.resourceId).map((row) => ({
1263
+ return allKnownMenus.filter((row) => !editing || row.resourceId !== editing.resourceId).map((row) => ({
1231
1264
  value: row.resourceId,
1232
1265
  label: `${row.name}\uFF08${MENU_TYPE_LABEL[row.type]}\uFF09`
1233
1266
  }));
1234
- }, [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]);
1235
1281
  const loadList = useCallback3(
1236
1282
  async (direction = "current", token = pageToken) => {
1237
1283
  setLoading(true);
1238
1284
  setError("");
1239
1285
  try {
1240
1286
  const result = await api.list({
1287
+ depth: ROOT_MENU_DEPTH,
1241
1288
  pagination: {
1242
1289
  pageToken: token,
1243
1290
  pageSize,
@@ -1245,6 +1292,9 @@ function MenuManager({
1245
1292
  }
1246
1293
  });
1247
1294
  setRecords(result.records);
1295
+ setChildrenMap({});
1296
+ setExpandedIds({});
1297
+ setExpandingIds({});
1248
1298
  setPageToken(result.pageToken);
1249
1299
  setHasPreviousPage(result.hasPreviousPage);
1250
1300
  setHasNextPage(result.hasNextPage);
@@ -1265,6 +1315,29 @@ function MenuManager({
1265
1315
  } catch {
1266
1316
  }
1267
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
+ };
1268
1341
  useEffect3(() => {
1269
1342
  void loadList("current", "");
1270
1343
  void loadPermissionPoints();
@@ -1279,7 +1352,7 @@ function MenuManager({
1279
1352
  setEditing(row);
1280
1353
  setForm({
1281
1354
  parentId: row.parentId ?? null,
1282
- depth: resolveMenuDepth(row.parentId ?? null, records),
1355
+ depth: resolveMenuDepth(row.parentId ?? null, allKnownMenus),
1283
1356
  type: row.type,
1284
1357
  name: row.name,
1285
1358
  identification: row.identification,
@@ -1367,35 +1440,54 @@ function MenuManager({
1367
1440
  /* @__PURE__ */ jsx3("th", { style: styles2.th, children: "\u6743\u9650\u6807\u8BC6" }),
1368
1441
  /* @__PURE__ */ jsx3("th", { style: { ...styles2.th, width: 140 }, children: "\u64CD\u4F5C" })
1369
1442
  ] }) }),
1370
- /* @__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: [
1371
- /* @__PURE__ */ jsx3("td", { style: styles2.td, children: row.name }),
1372
- /* @__PURE__ */ jsx3("td", { style: styles2.td, children: /* @__PURE__ */ jsx3("code", { style: styles2.code, children: row.identification || "\u2014" }) }),
1373
- /* @__PURE__ */ jsx3("td", { style: styles2.td, children: MENU_TYPE_LABEL[row.type] }),
1374
- /* @__PURE__ */ jsx3("td", { style: styles2.td, children: /* @__PURE__ */ jsx3(
1375
- "span",
1376
- {
1377
- style: {
1378
- ...styles2.badge,
1379
- background: row.status === RESOURCE_STATUS_ENABLED ? "#e8f7ef" : "#f4f4f5",
1380
- color: row.status === RESOURCE_STATUS_ENABLED ? "#067647" : "#667085"
1381
- },
1382
- children: row.status === RESOURCE_STATUS_ENABLED ? "\u663E\u793A" : "\u9690\u85CF"
1383
- }
1384
- ) }),
1385
- /* @__PURE__ */ jsx3("td", { style: styles2.td, children: renderPermissionLabels(row) }),
1386
- /* @__PURE__ */ jsxs2("td", { style: styles2.td, children: [
1387
- /* @__PURE__ */ jsx3("button", { type: "button", style: styles2.linkBtn, onClick: () => openEdit(row), children: "\u7F16\u8F91" }),
1388
- /* @__PURE__ */ jsx3(
1389
- "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",
1390
1467
  {
1391
- type: "button",
1392
- style: { ...styles2.linkBtn, color: "#d92d20" },
1393
- onClick: () => askDelete(row),
1394
- 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"
1395
1474
  }
1396
- )
1397
- ] })
1398
- ] }, 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
+ }) })
1399
1491
  ] }) }),
1400
1492
  /* @__PURE__ */ jsxs2("div", { style: styles2.pagination, children: [
1401
1493
  /* @__PURE__ */ jsxs2("label", { style: styles2.pageSize, children: [
@@ -1461,7 +1553,7 @@ function MenuManager({
1461
1553
  setForm((prev) => ({
1462
1554
  ...prev,
1463
1555
  parentId,
1464
- depth: resolveMenuDepth(parentId, records)
1556
+ depth: resolveMenuDepth(parentId, allKnownMenus)
1465
1557
  }));
1466
1558
  },
1467
1559
  children: [
@@ -1659,6 +1751,30 @@ var styles2 = {
1659
1751
  borderBottom: "1px solid #f2f4f7",
1660
1752
  verticalAlign: "middle"
1661
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
+ },
1662
1778
  empty: { padding: 28, textAlign: "center", color: "#98a2b3", fontSize: 14 },
1663
1779
  code: {
1664
1780
  fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace",
@@ -1967,6 +2083,8 @@ var styles3 = {
1967
2083
  };
1968
2084
  export {
1969
2085
  Can,
2086
+ MENU_LEAF_NO,
2087
+ MENU_LEAF_YES,
1970
2088
  MENU_LIST_TYPE_PARAM,
1971
2089
  MENU_TYPE_BUTTON,
1972
2090
  MENU_TYPE_LABEL,
@@ -2001,6 +2119,7 @@ export {
2001
2119
  extractRecords,
2002
2120
  getAppClientId,
2003
2121
  getDeviceWorkerId,
2122
+ isMenuLeaf,
2004
2123
  mapAuthorizationResource,
2005
2124
  mapMenuResource,
2006
2125
  resolveMenuDepth,