com-angel-authorization 1.0.5 → 1.0.7

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.
@@ -233,16 +233,35 @@ import {
233
233
 
234
234
  // src/resources/types.ts
235
235
  var RESOURCE_TYPE_API = "api";
236
+ var MENU_TYPE_PAGE = "page";
237
+ var MENU_TYPE_MENU = "menu";
238
+ var MENU_TYPE_BUTTON = "button";
239
+ var MENU_LIST_TYPE_PARAM = "page,menu,button";
240
+ var ROOT_PARENT_ID = "0";
236
241
  var RESOURCE_STATUS_ENABLED = 1;
237
242
  var RESOURCE_STATUS_DISABLED = 0;
238
243
  var RESOURCE_STATUS_OPTIONS = [
239
244
  { label: "\u542F\u7528", value: RESOURCE_STATUS_ENABLED },
240
245
  { label: "\u7981\u7528", value: RESOURCE_STATUS_DISABLED }
241
246
  ];
247
+ var MENU_VISIBILITY_OPTIONS = [
248
+ { label: "\u663E\u793A", value: RESOURCE_STATUS_ENABLED },
249
+ { label: "\u9690\u85CF", value: RESOURCE_STATUS_DISABLED }
250
+ ];
242
251
  var RESOURCE_PRIVATE_OPTIONS = [
243
252
  { label: "\u662F", value: true },
244
253
  { label: "\u5426", value: false }
245
254
  ];
255
+ var MENU_TYPE_OPTIONS = [
256
+ { label: "\u76EE\u5F55", value: MENU_TYPE_PAGE },
257
+ { label: "\u83DC\u5355", value: MENU_TYPE_MENU },
258
+ { label: "\u6309\u94AE", value: MENU_TYPE_BUTTON }
259
+ ];
260
+ var MENU_TYPE_LABEL = {
261
+ page: "\u76EE\u5F55",
262
+ menu: "\u83DC\u5355",
263
+ button: "\u6309\u94AE"
264
+ };
246
265
 
247
266
  // src/utils/snowflake.ts
248
267
  var DEVICE_WORKER_ID_KEY = "hgams_device_worker_id";
@@ -387,6 +406,29 @@ function toBoolean(value) {
387
406
  }
388
407
  return false;
389
408
  }
409
+ function toStringArray(value) {
410
+ if (Array.isArray(value)) {
411
+ return value.map((item) => {
412
+ if (item && typeof item === "object") {
413
+ const row = item;
414
+ const id = row.resourceId ?? row.resource_id ?? row.id;
415
+ return id === void 0 || id === null ? "" : String(id);
416
+ }
417
+ return String(item ?? "");
418
+ }).filter(Boolean);
419
+ }
420
+ if (typeof value === "string" && value.trim()) {
421
+ return value.split(",").map((s) => s.trim()).filter(Boolean);
422
+ }
423
+ return [];
424
+ }
425
+ function toMenuType(value) {
426
+ const v = String(value ?? "").toLowerCase();
427
+ if (v === MENU_TYPE_PAGE || v === MENU_TYPE_MENU || v === MENU_TYPE_BUTTON) {
428
+ return v;
429
+ }
430
+ return MENU_TYPE_MENU;
431
+ }
390
432
  function mapAuthorizationResource(item) {
391
433
  if (!item || typeof item !== "object") return null;
392
434
  const row = item;
@@ -402,9 +444,31 @@ function mapAuthorizationResource(item) {
402
444
  description: String(row.description ?? row.desc ?? "")
403
445
  };
404
446
  }
405
- function buildListUrl(params) {
447
+ function mapMenuResource(item) {
448
+ if (!item || typeof item !== "object") return null;
449
+ const row = item;
450
+ const resourceId = row.resourceId ?? row.resource_id ?? row.id;
451
+ if (resourceId === void 0 || resourceId === null || resourceId === "") return null;
452
+ const permissionPointIds = toStringArray(
453
+ row.permissionPointIds ?? row.permission_point_ids ?? row.permissionPoints ?? row.permissions
454
+ );
455
+ const permissionNamesRaw = row.permissionPointNames ?? row.permission_point_names ?? row.permissionNames;
456
+ const permissionPointNames = Array.isArray(permissionNamesRaw) ? permissionNamesRaw.map((n) => String(n ?? "")).filter(Boolean) : void 0;
457
+ const parentId = row.parentId ?? row.parent_id ?? ROOT_PARENT_ID;
458
+ return {
459
+ resourceId: String(resourceId),
460
+ parentId: parentId === void 0 || parentId === null ? ROOT_PARENT_ID : String(parentId),
461
+ type: toMenuType(row.type),
462
+ name: String(row.name ?? ""),
463
+ identification: String(row.identification ?? row.identity ?? row.path ?? ""),
464
+ permissionPointIds,
465
+ permissionPointNames,
466
+ status: toResourceStatus(row.status)
467
+ };
468
+ }
469
+ function buildListUrl(type, params) {
406
470
  const parts = [];
407
- appendQueryParam(parts, "type", RESOURCE_TYPE_API);
471
+ appendQueryParam(parts, "type", type);
408
472
  appendQueryParam(parts, "page-token", params?.pagination?.pageToken ?? "");
409
473
  appendQueryParam(parts, "page-size", params?.pagination?.pageSize ?? "20");
410
474
  appendQueryParam(parts, "page-direction", params?.pagination?.pageDirection ?? "current");
@@ -431,12 +495,33 @@ function buildUpdateBody(values) {
431
495
  description: values.description.trim()
432
496
  };
433
497
  }
498
+ function buildCreateMenuBody(values, resourceId = getAppClientId()) {
499
+ return {
500
+ resourceId,
501
+ parentId: values.parentId || ROOT_PARENT_ID,
502
+ type: values.type,
503
+ name: values.name.trim(),
504
+ identification: values.identification.trim(),
505
+ permissionPointIds: [...values.permissionPointIds],
506
+ status: values.status
507
+ };
508
+ }
509
+ function buildUpdateMenuBody(values) {
510
+ return {
511
+ parentId: values.parentId || ROOT_PARENT_ID,
512
+ type: values.type,
513
+ name: values.name.trim(),
514
+ identification: values.identification.trim(),
515
+ permissionPointIds: [...values.permissionPointIds],
516
+ status: values.status
517
+ };
518
+ }
434
519
  function createAuthorizationResourceApi(request) {
435
520
  return {
436
521
  async list(params, signal) {
437
522
  const json = await request({
438
523
  method: "GET",
439
- url: buildListUrl(params),
524
+ url: buildListUrl(RESOURCE_TYPE_API, params),
440
525
  signal
441
526
  });
442
527
  const records = extractRecords(json).map(mapAuthorizationResource).filter((item) => item !== null);
@@ -471,6 +556,50 @@ function createAuthorizationResourceApi(request) {
471
556
  }
472
557
  };
473
558
  }
559
+ function createMenuResourceApi(request) {
560
+ const permissionApi = createAuthorizationResourceApi(request);
561
+ return {
562
+ async list(params, signal) {
563
+ const json = await request({
564
+ method: "GET",
565
+ url: buildListUrl(MENU_LIST_TYPE_PARAM, params),
566
+ signal
567
+ });
568
+ const records = extractRecords(json).map(mapMenuResource).filter((item) => item !== null);
569
+ const pagination = extractPagination(json);
570
+ return {
571
+ records,
572
+ ...pagination
573
+ };
574
+ },
575
+ listPermissionPoints(params, signal) {
576
+ return permissionApi.list(params, signal);
577
+ },
578
+ async create(values, signal) {
579
+ return request({
580
+ method: "POST",
581
+ url: "/authorization-resources",
582
+ body: buildCreateMenuBody(values),
583
+ signal
584
+ });
585
+ },
586
+ async update(resourceId, values, signal) {
587
+ return request({
588
+ method: "PATCH",
589
+ url: `/authorization-resources/${encodeURIComponent(resourceId)}`,
590
+ body: buildUpdateMenuBody(values),
591
+ signal
592
+ });
593
+ },
594
+ async remove(resourceId, signal) {
595
+ return request({
596
+ method: "DELETE",
597
+ url: `/authorization-resources/${encodeURIComponent(resourceId)}`,
598
+ signal
599
+ });
600
+ }
601
+ };
602
+ }
474
603
  function createDefaultResourceRequest(options = {}) {
475
604
  const baseUrl = (options.baseUrl ?? "").replace(/\/+$/, "");
476
605
  const credentials = options.credentials ?? "include";
@@ -1037,8 +1166,786 @@ var styles = {
1037
1166
  paddingTop: 4
1038
1167
  }
1039
1168
  };
1169
+
1170
+ // src/react/MenuManager.tsx
1171
+ import {
1172
+ useCallback as useCallback3,
1173
+ useEffect as useEffect3,
1174
+ useMemo as useMemo3,
1175
+ useState as useState3
1176
+ } from "react";
1177
+ import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
1178
+ var PAGE_SIZE_OPTIONS2 = ["10", "20", "50", "100"];
1179
+ var emptyForm2 = () => ({
1180
+ parentId: ROOT_PARENT_ID,
1181
+ type: MENU_TYPE_MENU,
1182
+ name: "",
1183
+ identification: "",
1184
+ permissionPointIds: [],
1185
+ status: RESOURCE_STATUS_ENABLED
1186
+ });
1187
+ function MenuManager({
1188
+ api,
1189
+ title = "\u83DC\u5355\u7BA1\u7406",
1190
+ pageSize: initialPageSize = "20",
1191
+ toolbarExtra
1192
+ }) {
1193
+ const [records, setRecords] = useState3([]);
1194
+ const [permissionPoints, setPermissionPoints] = useState3([]);
1195
+ const [loading, setLoading] = useState3(false);
1196
+ const [error, setError] = useState3("");
1197
+ const [pageSize, setPageSize] = useState3(initialPageSize);
1198
+ const [pageToken, setPageToken] = useState3("");
1199
+ const [hasPreviousPage, setHasPreviousPage] = useState3(false);
1200
+ const [hasNextPage, setHasNextPage] = useState3(false);
1201
+ const [dialogOpen, setDialogOpen] = useState3(false);
1202
+ const [editing, setEditing] = useState3(null);
1203
+ const [form, setForm] = useState3(emptyForm2);
1204
+ const [saving, setSaving] = useState3(false);
1205
+ const [deletingRow, setDeletingRow] = useState3(null);
1206
+ const [deleting, setDeleting] = useState3(false);
1207
+ const permissionNameMap = useMemo3(() => {
1208
+ const map = /* @__PURE__ */ new Map();
1209
+ permissionPoints.forEach((p) => map.set(p.resourceId, p.name || p.identification));
1210
+ return map;
1211
+ }, [permissionPoints]);
1212
+ const parentOptions = useMemo3(() => {
1213
+ return records.filter((row) => !editing || row.resourceId !== editing.resourceId).map((row) => ({
1214
+ value: row.resourceId,
1215
+ label: `${row.name}\uFF08${MENU_TYPE_LABEL[row.type]}\uFF09`
1216
+ }));
1217
+ }, [records, editing]);
1218
+ const loadList = useCallback3(
1219
+ async (direction = "current", token = pageToken) => {
1220
+ setLoading(true);
1221
+ setError("");
1222
+ try {
1223
+ const result = await api.list({
1224
+ pagination: {
1225
+ pageToken: token,
1226
+ pageSize,
1227
+ pageDirection: direction
1228
+ }
1229
+ });
1230
+ setRecords(result.records);
1231
+ setPageToken(result.pageToken);
1232
+ setHasPreviousPage(result.hasPreviousPage);
1233
+ setHasNextPage(result.hasNextPage);
1234
+ } catch (err) {
1235
+ setError(err instanceof Error ? err.message : "\u52A0\u8F7D\u5931\u8D25");
1236
+ } finally {
1237
+ setLoading(false);
1238
+ }
1239
+ },
1240
+ [api, pageSize, pageToken]
1241
+ );
1242
+ const loadPermissionPoints = useCallback3(async () => {
1243
+ try {
1244
+ const result = await api.listPermissionPoints({
1245
+ pagination: { pageSize: "200", pageDirection: "current" }
1246
+ });
1247
+ setPermissionPoints(result.records);
1248
+ } catch {
1249
+ }
1250
+ }, [api]);
1251
+ useEffect3(() => {
1252
+ void loadList("current", "");
1253
+ void loadPermissionPoints();
1254
+ }, [api, pageSize]);
1255
+ const openCreate = () => {
1256
+ setEditing(null);
1257
+ setForm(emptyForm2());
1258
+ setDialogOpen(true);
1259
+ void loadPermissionPoints();
1260
+ };
1261
+ const openEdit = (row) => {
1262
+ setEditing(row);
1263
+ setForm({
1264
+ parentId: row.parentId || ROOT_PARENT_ID,
1265
+ type: row.type,
1266
+ name: row.name,
1267
+ identification: row.identification,
1268
+ permissionPointIds: [...row.permissionPointIds],
1269
+ status: row.status
1270
+ });
1271
+ setDialogOpen(true);
1272
+ void loadPermissionPoints();
1273
+ };
1274
+ const closeDialog = () => {
1275
+ if (saving) return;
1276
+ setDialogOpen(false);
1277
+ };
1278
+ const handleSubmit = async (event) => {
1279
+ event.preventDefault();
1280
+ if (!form.name.trim()) {
1281
+ setError("\u8BF7\u586B\u5199\u83DC\u5355\u540D\u79F0");
1282
+ return;
1283
+ }
1284
+ setSaving(true);
1285
+ setError("");
1286
+ try {
1287
+ if (editing) {
1288
+ await api.update(editing.resourceId, form);
1289
+ } else {
1290
+ await api.create(form);
1291
+ }
1292
+ setDialogOpen(false);
1293
+ await loadList("current", "");
1294
+ } catch (err) {
1295
+ setError(err instanceof Error ? err.message : "\u4FDD\u5B58\u5931\u8D25");
1296
+ } finally {
1297
+ setSaving(false);
1298
+ }
1299
+ };
1300
+ const askDelete = (row) => setDeletingRow(row);
1301
+ const closeDeleteDialog = () => {
1302
+ if (!deleting) setDeletingRow(null);
1303
+ };
1304
+ const confirmDelete = async () => {
1305
+ if (!deletingRow) return;
1306
+ setDeleting(true);
1307
+ setError("");
1308
+ try {
1309
+ await api.remove(deletingRow.resourceId);
1310
+ setDeletingRow(null);
1311
+ await loadList("current", "");
1312
+ } catch (err) {
1313
+ setError(err instanceof Error ? err.message : "\u5220\u9664\u5931\u8D25");
1314
+ } finally {
1315
+ setDeleting(false);
1316
+ }
1317
+ };
1318
+ const togglePermission = (id) => {
1319
+ setForm((prev) => {
1320
+ const exists = prev.permissionPointIds.includes(id);
1321
+ return {
1322
+ ...prev,
1323
+ permissionPointIds: exists ? prev.permissionPointIds.filter((x) => x !== id) : [...prev.permissionPointIds, id]
1324
+ };
1325
+ });
1326
+ };
1327
+ const renderPermissionLabels = (row) => {
1328
+ if (row.permissionPointNames?.length) {
1329
+ return row.permissionPointNames.join("\u3001");
1330
+ }
1331
+ if (!row.permissionPointIds.length) return "\u2014";
1332
+ return row.permissionPointIds.map((id) => permissionNameMap.get(id) ?? id).join("\u3001");
1333
+ };
1334
+ return /* @__PURE__ */ jsxs2("div", { style: styles2.root, children: [
1335
+ /* @__PURE__ */ jsxs2("div", { style: styles2.toolbar, children: [
1336
+ /* @__PURE__ */ jsx3("h2", { style: styles2.title, children: title }),
1337
+ /* @__PURE__ */ jsxs2("div", { style: styles2.toolbarRight, children: [
1338
+ toolbarExtra,
1339
+ /* @__PURE__ */ jsx3("button", { type: "button", style: styles2.primaryBtn, onClick: openCreate, children: "\u65B0\u589E" })
1340
+ ] })
1341
+ ] }),
1342
+ error ? /* @__PURE__ */ jsx3("div", { style: styles2.error, children: error }) : null,
1343
+ /* @__PURE__ */ jsx3("div", { style: styles2.tableWrap, children: /* @__PURE__ */ jsxs2("table", { style: styles2.table, children: [
1344
+ /* @__PURE__ */ jsx3("thead", { children: /* @__PURE__ */ jsxs2("tr", { children: [
1345
+ /* @__PURE__ */ jsx3("th", { style: styles2.th, children: "\u83DC\u5355\u540D\u79F0" }),
1346
+ /* @__PURE__ */ jsx3("th", { style: styles2.th, children: "\u8BF7\u6C42\u5730\u5740" }),
1347
+ /* @__PURE__ */ jsx3("th", { style: { ...styles2.th, width: 90 }, children: "\u7C7B\u578B" }),
1348
+ /* @__PURE__ */ jsx3("th", { style: { ...styles2.th, width: 80 }, children: "\u53EF\u89C1" }),
1349
+ /* @__PURE__ */ jsx3("th", { style: styles2.th, children: "\u6743\u9650\u6807\u8BC6" }),
1350
+ /* @__PURE__ */ jsx3("th", { style: { ...styles2.th, width: 140 }, children: "\u64CD\u4F5C" })
1351
+ ] }) }),
1352
+ /* @__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: [
1353
+ /* @__PURE__ */ jsx3("td", { style: styles2.td, children: row.name }),
1354
+ /* @__PURE__ */ jsx3("td", { style: styles2.td, children: /* @__PURE__ */ jsx3("code", { style: styles2.code, children: row.identification || "\u2014" }) }),
1355
+ /* @__PURE__ */ jsx3("td", { style: styles2.td, children: MENU_TYPE_LABEL[row.type] }),
1356
+ /* @__PURE__ */ jsx3("td", { style: styles2.td, children: /* @__PURE__ */ jsx3(
1357
+ "span",
1358
+ {
1359
+ style: {
1360
+ ...styles2.badge,
1361
+ background: row.status === RESOURCE_STATUS_ENABLED ? "#e8f7ef" : "#f4f4f5",
1362
+ color: row.status === RESOURCE_STATUS_ENABLED ? "#067647" : "#667085"
1363
+ },
1364
+ children: row.status === RESOURCE_STATUS_ENABLED ? "\u663E\u793A" : "\u9690\u85CF"
1365
+ }
1366
+ ) }),
1367
+ /* @__PURE__ */ jsx3("td", { style: styles2.td, children: renderPermissionLabels(row) }),
1368
+ /* @__PURE__ */ jsxs2("td", { style: styles2.td, children: [
1369
+ /* @__PURE__ */ jsx3("button", { type: "button", style: styles2.linkBtn, onClick: () => openEdit(row), children: "\u7F16\u8F91" }),
1370
+ /* @__PURE__ */ jsx3(
1371
+ "button",
1372
+ {
1373
+ type: "button",
1374
+ style: { ...styles2.linkBtn, color: "#d92d20" },
1375
+ onClick: () => askDelete(row),
1376
+ children: "\u5220\u9664"
1377
+ }
1378
+ )
1379
+ ] })
1380
+ ] }, row.resourceId)) })
1381
+ ] }) }),
1382
+ /* @__PURE__ */ jsxs2("div", { style: styles2.pagination, children: [
1383
+ /* @__PURE__ */ jsxs2("label", { style: styles2.pageSize, children: [
1384
+ "\u6BCF\u9875",
1385
+ /* @__PURE__ */ jsx3(
1386
+ "select",
1387
+ {
1388
+ value: pageSize,
1389
+ onChange: (e) => {
1390
+ setPageToken("");
1391
+ setPageSize(e.target.value);
1392
+ },
1393
+ style: styles2.select,
1394
+ children: PAGE_SIZE_OPTIONS2.map((size) => /* @__PURE__ */ jsx3("option", { value: size, children: size }, size))
1395
+ }
1396
+ )
1397
+ ] }),
1398
+ /* @__PURE__ */ jsxs2("div", { style: styles2.pageBtns, children: [
1399
+ /* @__PURE__ */ jsx3(
1400
+ "button",
1401
+ {
1402
+ type: "button",
1403
+ style: styles2.secondaryBtn,
1404
+ disabled: !hasPreviousPage || loading,
1405
+ onClick: () => void loadList("previous"),
1406
+ children: "\u4E0A\u4E00\u9875"
1407
+ }
1408
+ ),
1409
+ /* @__PURE__ */ jsx3(
1410
+ "button",
1411
+ {
1412
+ type: "button",
1413
+ style: styles2.secondaryBtn,
1414
+ disabled: !hasNextPage || loading,
1415
+ onClick: () => void loadList("next"),
1416
+ children: "\u4E0B\u4E00\u9875"
1417
+ }
1418
+ )
1419
+ ] })
1420
+ ] }),
1421
+ dialogOpen ? /* @__PURE__ */ jsx3("div", { style: styles2.mask, onClick: closeDialog, children: /* @__PURE__ */ jsxs2(
1422
+ "div",
1423
+ {
1424
+ style: { ...styles2.dialog, maxWidth: 520 },
1425
+ onClick: (e) => e.stopPropagation(),
1426
+ role: "dialog",
1427
+ "aria-modal": "true",
1428
+ children: [
1429
+ /* @__PURE__ */ jsxs2("div", { style: styles2.dialogHeader, children: [
1430
+ /* @__PURE__ */ jsx3("h3", { style: styles2.dialogTitle, children: editing ? "\u7F16\u8F91\u83DC\u5355" : "\u65B0\u589E\u83DC\u5355" }),
1431
+ /* @__PURE__ */ jsx3("button", { type: "button", style: styles2.iconBtn, onClick: closeDialog, "aria-label": "\u5173\u95ED", children: "\xD7" })
1432
+ ] }),
1433
+ /* @__PURE__ */ jsxs2("form", { style: styles2.form, onSubmit: (e) => void handleSubmit(e), children: [
1434
+ /* @__PURE__ */ jsxs2("label", { style: styles2.field, children: [
1435
+ /* @__PURE__ */ jsx3("span", { style: styles2.label, children: "\u4E0A\u7EA7\u83DC\u5355" }),
1436
+ /* @__PURE__ */ jsxs2(
1437
+ "select",
1438
+ {
1439
+ style: styles2.input,
1440
+ value: form.parentId,
1441
+ onChange: (e) => setForm((prev) => ({ ...prev, parentId: e.target.value })),
1442
+ children: [
1443
+ /* @__PURE__ */ jsx3("option", { value: ROOT_PARENT_ID, children: "\u6839\u76EE\u5F55" }),
1444
+ parentOptions.map((opt) => /* @__PURE__ */ jsx3("option", { value: opt.value, children: opt.label }, opt.value))
1445
+ ]
1446
+ }
1447
+ )
1448
+ ] }),
1449
+ /* @__PURE__ */ jsxs2("fieldset", { style: styles2.fieldset, children: [
1450
+ /* @__PURE__ */ jsx3("legend", { style: styles2.label, children: "\u83DC\u5355\u7C7B\u578B" }),
1451
+ /* @__PURE__ */ jsx3("div", { style: styles2.radioGroup, children: MENU_TYPE_OPTIONS.map((opt) => /* @__PURE__ */ jsxs2("label", { style: styles2.radioItem, children: [
1452
+ /* @__PURE__ */ jsx3(
1453
+ "input",
1454
+ {
1455
+ type: "radio",
1456
+ name: "menu-type",
1457
+ checked: form.type === opt.value,
1458
+ onChange: () => setForm((prev) => ({
1459
+ ...prev,
1460
+ type: opt.value
1461
+ }))
1462
+ }
1463
+ ),
1464
+ opt.label
1465
+ ] }, opt.value)) })
1466
+ ] }),
1467
+ /* @__PURE__ */ jsxs2("label", { style: styles2.field, children: [
1468
+ /* @__PURE__ */ jsx3("span", { style: styles2.label, children: "\u83DC\u5355\u540D\u79F0" }),
1469
+ /* @__PURE__ */ jsx3(
1470
+ "input",
1471
+ {
1472
+ style: styles2.input,
1473
+ value: form.name,
1474
+ onChange: (e) => setForm((prev) => ({ ...prev, name: e.target.value })),
1475
+ placeholder: "\u8BF7\u8F93\u5165\u83DC\u5355\u540D\u79F0",
1476
+ required: true
1477
+ }
1478
+ )
1479
+ ] }),
1480
+ /* @__PURE__ */ jsxs2("label", { style: styles2.field, children: [
1481
+ /* @__PURE__ */ jsx3("span", { style: styles2.label, children: "\u8BF7\u6C42\u5730\u5740" }),
1482
+ /* @__PURE__ */ jsx3(
1483
+ "input",
1484
+ {
1485
+ style: styles2.input,
1486
+ value: form.identification,
1487
+ onChange: (e) => setForm((prev) => ({ ...prev, identification: e.target.value })),
1488
+ placeholder: "\u5982 /system/user"
1489
+ }
1490
+ )
1491
+ ] }),
1492
+ /* @__PURE__ */ jsxs2("fieldset", { style: styles2.fieldset, children: [
1493
+ /* @__PURE__ */ jsx3("legend", { style: styles2.label, children: "\u6743\u9650\u6807\u8BC6" }),
1494
+ /* @__PURE__ */ jsx3("div", { style: styles2.multiSelect, children: permissionPoints.length === 0 ? /* @__PURE__ */ jsx3("div", { style: styles2.hint, children: "\u6682\u65E0\u6743\u9650\u70B9\uFF0C\u8BF7\u5148\u5728\u6743\u9650\u70B9\u7BA1\u7406\u4E2D\u65B0\u589E" }) : permissionPoints.map((p) => /* @__PURE__ */ jsxs2("label", { style: styles2.checkItem, children: [
1495
+ /* @__PURE__ */ jsx3(
1496
+ "input",
1497
+ {
1498
+ type: "checkbox",
1499
+ checked: form.permissionPointIds.includes(p.resourceId),
1500
+ onChange: () => togglePermission(p.resourceId)
1501
+ }
1502
+ ),
1503
+ /* @__PURE__ */ jsxs2("span", { children: [
1504
+ p.name,
1505
+ /* @__PURE__ */ jsx3("code", { style: { ...styles2.code, marginLeft: 6 }, children: p.identification })
1506
+ ] })
1507
+ ] }, p.resourceId)) })
1508
+ ] }),
1509
+ /* @__PURE__ */ jsxs2("fieldset", { style: styles2.fieldset, children: [
1510
+ /* @__PURE__ */ jsx3("legend", { style: styles2.label, children: "\u72B6\u6001" }),
1511
+ /* @__PURE__ */ jsx3("div", { style: styles2.radioGroup, children: MENU_VISIBILITY_OPTIONS.map((opt) => /* @__PURE__ */ jsxs2("label", { style: styles2.radioItem, children: [
1512
+ /* @__PURE__ */ jsx3(
1513
+ "input",
1514
+ {
1515
+ type: "radio",
1516
+ name: "menu-status",
1517
+ checked: form.status === opt.value,
1518
+ onChange: () => setForm((prev) => ({ ...prev, status: opt.value }))
1519
+ }
1520
+ ),
1521
+ opt.label
1522
+ ] }, opt.value)) })
1523
+ ] }),
1524
+ /* @__PURE__ */ jsxs2("div", { style: styles2.dialogFooter, children: [
1525
+ /* @__PURE__ */ jsx3("button", { type: "button", style: styles2.secondaryBtn, onClick: closeDialog, children: "\u53D6\u6D88" }),
1526
+ /* @__PURE__ */ jsx3("button", { type: "submit", style: styles2.primaryBtn, disabled: saving, children: saving ? "\u4FDD\u5B58\u4E2D\u2026" : "\u4FDD\u5B58" })
1527
+ ] })
1528
+ ] })
1529
+ ]
1530
+ }
1531
+ ) }) : null,
1532
+ deletingRow ? /* @__PURE__ */ jsx3("div", { style: styles2.mask, onClick: closeDeleteDialog, children: /* @__PURE__ */ jsxs2(
1533
+ "div",
1534
+ {
1535
+ style: { ...styles2.dialog, maxWidth: 420 },
1536
+ onClick: (e) => e.stopPropagation(),
1537
+ role: "dialog",
1538
+ "aria-modal": "true",
1539
+ children: [
1540
+ /* @__PURE__ */ jsxs2("div", { style: styles2.dialogHeader, children: [
1541
+ /* @__PURE__ */ jsx3("h3", { style: styles2.dialogTitle, children: "\u786E\u8BA4\u5220\u9664" }),
1542
+ /* @__PURE__ */ jsx3(
1543
+ "button",
1544
+ {
1545
+ type: "button",
1546
+ style: styles2.iconBtn,
1547
+ onClick: closeDeleteDialog,
1548
+ "aria-label": "\u5173\u95ED",
1549
+ children: "\xD7"
1550
+ }
1551
+ )
1552
+ ] }),
1553
+ /* @__PURE__ */ jsxs2("div", { style: styles2.confirmBody, children: [
1554
+ /* @__PURE__ */ jsxs2("p", { style: styles2.confirmText, children: [
1555
+ "\u786E\u8BA4\u5220\u9664\u83DC\u5355\u300C",
1556
+ /* @__PURE__ */ jsx3("strong", { children: deletingRow.name }),
1557
+ "\u300D\u5417\uFF1F\u5220\u9664\u540E\u4E0D\u53EF\u6062\u590D\u3002"
1558
+ ] }),
1559
+ /* @__PURE__ */ jsxs2("div", { style: styles2.dialogFooter, children: [
1560
+ /* @__PURE__ */ jsx3(
1561
+ "button",
1562
+ {
1563
+ type: "button",
1564
+ style: styles2.secondaryBtn,
1565
+ onClick: closeDeleteDialog,
1566
+ disabled: deleting,
1567
+ children: "\u53D6\u6D88"
1568
+ }
1569
+ ),
1570
+ /* @__PURE__ */ jsx3(
1571
+ "button",
1572
+ {
1573
+ type: "button",
1574
+ style: styles2.dangerBtn,
1575
+ onClick: () => void confirmDelete(),
1576
+ disabled: deleting,
1577
+ children: deleting ? "\u5220\u9664\u4E2D\u2026" : "\u786E\u8BA4\u5220\u9664"
1578
+ }
1579
+ )
1580
+ ] })
1581
+ ] })
1582
+ ]
1583
+ }
1584
+ ) }) : null
1585
+ ] });
1586
+ }
1587
+ var styles2 = {
1588
+ root: {
1589
+ display: "flex",
1590
+ flexDirection: "column",
1591
+ gap: 16,
1592
+ padding: 16,
1593
+ color: "#101828",
1594
+ fontFamily: '"PingFang SC", "Microsoft YaHei", -apple-system, BlinkMacSystemFont, sans-serif'
1595
+ },
1596
+ toolbar: {
1597
+ display: "flex",
1598
+ alignItems: "center",
1599
+ justifyContent: "space-between",
1600
+ gap: 12
1601
+ },
1602
+ title: { margin: 0, fontSize: 18, fontWeight: 600 },
1603
+ toolbarRight: { display: "flex", alignItems: "center", gap: 8 },
1604
+ error: {
1605
+ padding: "10px 12px",
1606
+ borderRadius: 8,
1607
+ background: "#fef3f2",
1608
+ color: "#b42318",
1609
+ fontSize: 13
1610
+ },
1611
+ tableWrap: {
1612
+ overflow: "auto",
1613
+ border: "1px solid #eaecf0",
1614
+ borderRadius: 10,
1615
+ background: "#fff"
1616
+ },
1617
+ table: { width: "100%", borderCollapse: "collapse", minWidth: 820 },
1618
+ th: {
1619
+ textAlign: "left",
1620
+ padding: "12px 14px",
1621
+ fontSize: 13,
1622
+ fontWeight: 600,
1623
+ color: "#475467",
1624
+ background: "#f9fafb",
1625
+ borderBottom: "1px solid #eaecf0"
1626
+ },
1627
+ td: {
1628
+ padding: "12px 14px",
1629
+ fontSize: 14,
1630
+ borderBottom: "1px solid #f2f4f7",
1631
+ verticalAlign: "middle"
1632
+ },
1633
+ empty: { padding: 28, textAlign: "center", color: "#98a2b3", fontSize: 14 },
1634
+ code: {
1635
+ fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace",
1636
+ fontSize: 13,
1637
+ background: "#f2f4f7",
1638
+ padding: "2px 6px",
1639
+ borderRadius: 4
1640
+ },
1641
+ badge: {
1642
+ display: "inline-block",
1643
+ padding: "2px 8px",
1644
+ borderRadius: 999,
1645
+ fontSize: 12,
1646
+ fontWeight: 500
1647
+ },
1648
+ linkBtn: {
1649
+ border: "none",
1650
+ background: "transparent",
1651
+ color: "#049BAD",
1652
+ cursor: "pointer",
1653
+ padding: "0 8px 0 0",
1654
+ fontSize: 13
1655
+ },
1656
+ pagination: {
1657
+ display: "flex",
1658
+ alignItems: "center",
1659
+ justifyContent: "space-between",
1660
+ gap: 12
1661
+ },
1662
+ pageSize: { display: "flex", alignItems: "center", gap: 8, fontSize: 13, color: "#475467" },
1663
+ pageBtns: { display: "flex", gap: 8 },
1664
+ primaryBtn: {
1665
+ border: "none",
1666
+ background: "#049BAD",
1667
+ color: "#fff",
1668
+ borderRadius: 8,
1669
+ padding: "8px 14px",
1670
+ cursor: "pointer",
1671
+ fontSize: 14
1672
+ },
1673
+ secondaryBtn: {
1674
+ border: "1px solid #d0d5dd",
1675
+ background: "#fff",
1676
+ color: "#344054",
1677
+ borderRadius: 8,
1678
+ padding: "8px 14px",
1679
+ cursor: "pointer",
1680
+ fontSize: 14
1681
+ },
1682
+ dangerBtn: {
1683
+ border: "none",
1684
+ background: "#d92d20",
1685
+ color: "#fff",
1686
+ borderRadius: 8,
1687
+ padding: "8px 14px",
1688
+ cursor: "pointer",
1689
+ fontSize: 14
1690
+ },
1691
+ select: {
1692
+ border: "1px solid #d0d5dd",
1693
+ borderRadius: 6,
1694
+ padding: "4px 8px",
1695
+ fontSize: 13,
1696
+ backgroundColor: "#ffffff",
1697
+ color: "#101828"
1698
+ },
1699
+ mask: {
1700
+ position: "fixed",
1701
+ inset: 0,
1702
+ background: "rgba(16, 24, 40, 0.45)",
1703
+ display: "flex",
1704
+ alignItems: "center",
1705
+ justifyContent: "center",
1706
+ zIndex: 1e3,
1707
+ padding: 16
1708
+ },
1709
+ dialog: {
1710
+ width: "100%",
1711
+ maxWidth: 480,
1712
+ background: "#fff",
1713
+ borderRadius: 12,
1714
+ boxShadow: "0 20px 40px rgba(16,24,40,0.18)",
1715
+ maxHeight: "90vh",
1716
+ overflow: "auto"
1717
+ },
1718
+ dialogHeader: {
1719
+ display: "flex",
1720
+ alignItems: "center",
1721
+ justifyContent: "space-between",
1722
+ padding: "14px 16px",
1723
+ borderBottom: "1px solid #eaecf0",
1724
+ position: "sticky",
1725
+ top: 0,
1726
+ background: "#fff",
1727
+ zIndex: 1
1728
+ },
1729
+ dialogTitle: { margin: 0, fontSize: 16, fontWeight: 600 },
1730
+ iconBtn: {
1731
+ border: "none",
1732
+ background: "transparent",
1733
+ fontSize: 22,
1734
+ lineHeight: 1,
1735
+ cursor: "pointer",
1736
+ color: "#667085"
1737
+ },
1738
+ form: { display: "flex", flexDirection: "column", gap: 12, padding: 16 },
1739
+ confirmBody: { display: "flex", flexDirection: "column", gap: 16, padding: 16 },
1740
+ confirmText: { margin: 0, fontSize: 14, color: "#344054", lineHeight: 1.6 },
1741
+ field: { display: "flex", flexDirection: "column", gap: 6 },
1742
+ fieldset: {
1743
+ margin: 0,
1744
+ padding: 0,
1745
+ border: "none",
1746
+ display: "flex",
1747
+ flexDirection: "column",
1748
+ gap: 8
1749
+ },
1750
+ label: { fontSize: 13, color: "#344054", fontWeight: 500 },
1751
+ input: {
1752
+ border: "1px solid #d0d5dd",
1753
+ borderRadius: 8,
1754
+ padding: "8px 10px",
1755
+ fontSize: 14,
1756
+ outline: "none",
1757
+ backgroundColor: "#ffffff",
1758
+ color: "#101828",
1759
+ boxSizing: "border-box",
1760
+ width: "100%"
1761
+ },
1762
+ radioGroup: { display: "flex", flexWrap: "wrap", gap: 16 },
1763
+ radioItem: {
1764
+ display: "flex",
1765
+ alignItems: "center",
1766
+ gap: 6,
1767
+ fontSize: 14,
1768
+ color: "#344054",
1769
+ cursor: "pointer"
1770
+ },
1771
+ multiSelect: {
1772
+ maxHeight: 160,
1773
+ overflow: "auto",
1774
+ border: "1px solid #eaecf0",
1775
+ borderRadius: 8,
1776
+ padding: 8,
1777
+ background: "#f9fafb"
1778
+ },
1779
+ checkItem: {
1780
+ display: "flex",
1781
+ alignItems: "center",
1782
+ gap: 8,
1783
+ padding: "6px 4px",
1784
+ fontSize: 13,
1785
+ color: "#344054",
1786
+ cursor: "pointer"
1787
+ },
1788
+ hint: { fontSize: 13, color: "#98a2b3", padding: 8 },
1789
+ dialogFooter: {
1790
+ display: "flex",
1791
+ justifyContent: "flex-end",
1792
+ gap: 8,
1793
+ paddingTop: 4
1794
+ }
1795
+ };
1796
+
1797
+ // src/react/SystemAdmin.tsx
1798
+ import { useMemo as useMemo4, useState as useState4 } from "react";
1799
+
1800
+ // src/admin/menu.ts
1801
+ var SYSTEM_ADMIN_MENU = {
1802
+ key: "system",
1803
+ label: "\u7CFB\u7EDF\u7BA1\u7406",
1804
+ children: [
1805
+ { key: "menu", label: "\u83DC\u5355\u7BA1\u7406" },
1806
+ { key: "resource", label: "\u6743\u9650\u70B9\u7BA1\u7406" }
1807
+ ]
1808
+ };
1809
+ var SYSTEM_ADMIN_DEFAULT_KEY = "menu";
1810
+
1811
+ // src/react/SystemAdmin.tsx
1812
+ import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
1813
+ function SystemAdmin({
1814
+ menuApi,
1815
+ resourceApi,
1816
+ defaultActiveKey = SYSTEM_ADMIN_DEFAULT_KEY,
1817
+ activeKey: controlledKey,
1818
+ onActiveKeyChange,
1819
+ title = SYSTEM_ADMIN_MENU.label,
1820
+ toolbarExtra
1821
+ }) {
1822
+ const [innerKey, setInnerKey] = useState4(defaultActiveKey);
1823
+ const activeKey = controlledKey ?? innerKey;
1824
+ const setActiveKey = (key) => {
1825
+ if (controlledKey === void 0) setInnerKey(key);
1826
+ onActiveKeyChange?.(key);
1827
+ };
1828
+ const activeLabel = useMemo4(
1829
+ () => SYSTEM_ADMIN_MENU.children.find((item) => item.key === activeKey)?.label ?? "",
1830
+ [activeKey]
1831
+ );
1832
+ return /* @__PURE__ */ jsxs3("div", { style: styles3.root, children: [
1833
+ /* @__PURE__ */ jsxs3("aside", { style: styles3.sidebar, children: [
1834
+ /* @__PURE__ */ jsx4("div", { style: styles3.sidebarTitle, children: title }),
1835
+ /* @__PURE__ */ jsx4("nav", { style: styles3.nav, children: SYSTEM_ADMIN_MENU.children.map((item) => {
1836
+ const active = item.key === activeKey;
1837
+ return /* @__PURE__ */ jsx4(
1838
+ "button",
1839
+ {
1840
+ type: "button",
1841
+ style: {
1842
+ ...styles3.navItem,
1843
+ ...active ? styles3.navItemActive : null
1844
+ },
1845
+ onClick: () => setActiveKey(item.key),
1846
+ children: item.label
1847
+ },
1848
+ item.key
1849
+ );
1850
+ }) })
1851
+ ] }),
1852
+ /* @__PURE__ */ jsxs3("main", { style: styles3.content, children: [
1853
+ /* @__PURE__ */ jsxs3("div", { style: styles3.contentHeader, children: [
1854
+ /* @__PURE__ */ jsxs3("div", { style: styles3.breadcrumb, children: [
1855
+ /* @__PURE__ */ jsx4("span", { style: styles3.breadcrumbParent, children: title }),
1856
+ /* @__PURE__ */ jsx4("span", { style: styles3.breadcrumbSep, children: "/" }),
1857
+ /* @__PURE__ */ jsx4("span", { style: styles3.breadcrumbCurrent, children: activeLabel })
1858
+ ] }),
1859
+ toolbarExtra
1860
+ ] }),
1861
+ /* @__PURE__ */ jsx4("div", { style: styles3.panel, children: activeKey === "menu" ? /* @__PURE__ */ jsx4(MenuManager, { api: menuApi, title: "\u83DC\u5355\u7BA1\u7406" }) : /* @__PURE__ */ jsx4(ResourceManager, { api: resourceApi, title: "\u6743\u9650\u70B9\u7BA1\u7406" }) })
1862
+ ] })
1863
+ ] });
1864
+ }
1865
+ var styles3 = {
1866
+ root: {
1867
+ display: "flex",
1868
+ minHeight: 560,
1869
+ border: "1px solid #eaecf0",
1870
+ borderRadius: 12,
1871
+ overflow: "hidden",
1872
+ background: "#fff",
1873
+ fontFamily: '"PingFang SC", "Microsoft YaHei", -apple-system, BlinkMacSystemFont, sans-serif',
1874
+ color: "#101828"
1875
+ },
1876
+ sidebar: {
1877
+ width: 200,
1878
+ flexShrink: 0,
1879
+ borderRight: "1px solid #eaecf0",
1880
+ background: "#f9fafb",
1881
+ padding: "16px 12px"
1882
+ },
1883
+ sidebarTitle: {
1884
+ fontSize: 14,
1885
+ fontWeight: 600,
1886
+ color: "#344054",
1887
+ padding: "4px 10px 12px"
1888
+ },
1889
+ nav: {
1890
+ display: "flex",
1891
+ flexDirection: "column",
1892
+ gap: 4
1893
+ },
1894
+ navItem: {
1895
+ border: "none",
1896
+ background: "transparent",
1897
+ textAlign: "left",
1898
+ padding: "10px 12px",
1899
+ borderRadius: 8,
1900
+ fontSize: 14,
1901
+ color: "#475467",
1902
+ cursor: "pointer"
1903
+ },
1904
+ navItemActive: {
1905
+ background: "#e6f7f9",
1906
+ color: "#049BAD",
1907
+ fontWeight: 600
1908
+ },
1909
+ content: {
1910
+ flex: 1,
1911
+ minWidth: 0,
1912
+ display: "flex",
1913
+ flexDirection: "column",
1914
+ background: "#fff"
1915
+ },
1916
+ contentHeader: {
1917
+ display: "flex",
1918
+ alignItems: "center",
1919
+ justifyContent: "space-between",
1920
+ gap: 12,
1921
+ padding: "12px 16px",
1922
+ borderBottom: "1px solid #f2f4f7"
1923
+ },
1924
+ breadcrumb: {
1925
+ display: "flex",
1926
+ alignItems: "center",
1927
+ gap: 8,
1928
+ fontSize: 13
1929
+ },
1930
+ breadcrumbParent: { color: "#98a2b3" },
1931
+ breadcrumbSep: { color: "#d0d5dd" },
1932
+ breadcrumbCurrent: { color: "#344054", fontWeight: 600 },
1933
+ panel: {
1934
+ flex: 1,
1935
+ minHeight: 0,
1936
+ overflow: "auto"
1937
+ }
1938
+ };
1040
1939
  export {
1041
1940
  Can,
1941
+ MENU_LIST_TYPE_PARAM,
1942
+ MENU_TYPE_BUTTON,
1943
+ MENU_TYPE_LABEL,
1944
+ MENU_TYPE_MENU,
1945
+ MENU_TYPE_OPTIONS,
1946
+ MENU_TYPE_PAGE,
1947
+ MENU_VISIBILITY_OPTIONS,
1948
+ MenuManager,
1042
1949
  PermissionProvider,
1043
1950
  PermissionStore,
1044
1951
  RESOURCE_PRIVATE_OPTIONS,
@@ -1046,18 +1953,26 @@ export {
1046
1953
  RESOURCE_STATUS_ENABLED,
1047
1954
  RESOURCE_STATUS_OPTIONS,
1048
1955
  RESOURCE_TYPE_API,
1956
+ ROOT_PARENT_ID,
1049
1957
  ResourceManager,
1958
+ SYSTEM_ADMIN_DEFAULT_KEY,
1959
+ SYSTEM_ADMIN_MENU,
1960
+ SystemAdmin,
1050
1961
  appendQueryParam,
1051
1962
  buildCreateBody,
1963
+ buildCreateMenuBody,
1052
1964
  buildUpdateBody,
1965
+ buildUpdateMenuBody,
1053
1966
  createAuthorizationResourceApi,
1054
1967
  createDefaultResourceRequest,
1968
+ createMenuResourceApi,
1055
1969
  createPermissionStore,
1056
1970
  extractPagination,
1057
1971
  extractRecords,
1058
1972
  getAppClientId,
1059
1973
  getDeviceWorkerId,
1060
1974
  mapAuthorizationResource,
1975
+ mapMenuResource,
1061
1976
  snowyflake,
1062
1977
  useHasPermission,
1063
1978
  useHasRole,