@morehao/ark-iam-react 0.0.0-stage → 1.0.0

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.

Potentially problematic release.


This version of @morehao/ark-iam-react might be problematic. Click here for more details.

Files changed (66) hide show
  1. package/README.md +83 -2
  2. package/dist/_types/api/index.d.ts +5 -0
  3. package/dist/_types/api/index.js +185 -0
  4. package/dist/_types/api/index.js.map +1 -0
  5. package/dist/_types/api/request.d.ts +34 -0
  6. package/dist/_types/api/resources/auth.d.ts +13 -0
  7. package/dist/_types/api/resources/platform.d.ts +80 -0
  8. package/dist/_types/api/sessionProbe.d.ts +13 -0
  9. package/dist/_types/auth/AuthProvider.d.ts +27 -0
  10. package/dist/_types/auth/config.d.ts +23 -0
  11. package/dist/_types/auth/guards.d.ts +1 -0
  12. package/dist/_types/auth/hooks.d.ts +11 -0
  13. package/dist/_types/auth/index.d.ts +8 -0
  14. package/dist/_types/auth/index.js +190 -0
  15. package/dist/_types/auth/index.js.map +1 -0
  16. package/dist/_types/auth/tenant.d.ts +3 -0
  17. package/dist/_types/types/auth.d.ts +62 -0
  18. package/dist/_types/types/department.d.ts +52 -0
  19. package/dist/_types/types/index.d.ts +5 -0
  20. package/dist/_types/types/index.js +14 -0
  21. package/dist/_types/types/index.js.map +1 -0
  22. package/dist/_types/types/platform.d.ts +347 -0
  23. package/dist/_types/types/response.d.ts +14 -0
  24. package/dist/_types/types/tenant.d.ts +243 -0
  25. package/dist/_types/ui/AppShell.d.ts +16 -0
  26. package/dist/_types/ui/EllipsisCell.d.ts +16 -0
  27. package/dist/_types/ui/IDCell.d.ts +14 -0
  28. package/dist/_types/ui/InitialPasswordModal.d.ts +18 -0
  29. package/dist/_types/ui/LoginPage.d.ts +6 -0
  30. package/dist/_types/ui/MainLayout.d.ts +14 -0
  31. package/dist/_types/ui/NameLink.d.ts +18 -0
  32. package/dist/_types/ui/PageContainer.d.ts +15 -0
  33. package/dist/_types/ui/ProfileCenter.d.ts +6 -0
  34. package/dist/_types/ui/RemoteSelect.d.ts +57 -0
  35. package/dist/_types/ui/RowActions.d.ts +36 -0
  36. package/dist/_types/ui/TenantSwitcher.d.ts +1 -0
  37. package/dist/_types/ui/TimeCell.d.ts +53 -0
  38. package/dist/_types/ui/columns.d.ts +116 -0
  39. package/dist/_types/ui/format.d.ts +20 -0
  40. package/dist/_types/ui/index.d.ts +25 -0
  41. package/dist/_types/ui/index.js +1233 -0
  42. package/dist/_types/ui/index.js.map +1 -0
  43. package/dist/_types/ui/status.d.ts +38 -0
  44. package/dist/_types/ui/theme.d.ts +49 -0
  45. package/dist/api.d.ts +1 -0
  46. package/dist/api.js +50 -0
  47. package/dist/api.js.map +1 -0
  48. package/dist/auth.d.ts +1 -0
  49. package/dist/auth.js +17 -0
  50. package/dist/auth.js.map +1 -0
  51. package/dist/index-BQuRl5A6.js +185 -0
  52. package/dist/index-BQuRl5A6.js.map +1 -0
  53. package/dist/index-D3uUqZ5c.js +190 -0
  54. package/dist/index-D3uUqZ5c.js.map +1 -0
  55. package/dist/index-DnhMUOxg.js +1233 -0
  56. package/dist/index-DnhMUOxg.js.map +1 -0
  57. package/dist/index.d.ts +4 -0
  58. package/dist/index.js +109 -0
  59. package/dist/index.js.map +1 -0
  60. package/dist/types.d.ts +1 -0
  61. package/dist/types.js +14 -0
  62. package/dist/types.js.map +1 -0
  63. package/dist/ui.d.ts +1 -0
  64. package/dist/ui.js +46 -0
  65. package/dist/ui.js.map +1 -0
  66. package/package.json +80 -3
@@ -0,0 +1,1233 @@
1
+ import { jsxs, jsx, Fragment } from "react/jsx-runtime";
2
+ import { useState, useMemo, useCallback, useEffect, forwardRef, useRef } from "react";
3
+ import { ConfigProvider, App, Tooltip, Tag, Modal, Alert, Space, Button, Typography, theme, Layout, Menu, Dropdown, Badge, Avatar, Card, Form, Tabs, Descriptions, Input, message, Table, Select, Spin, Popconfirm } from "antd";
4
+ import { CopyOutlined, SafetyCertificateOutlined, ArrowRightOutlined, UserSwitchOutlined, LogoutOutlined, UserOutlined, DownOutlined, SwapOutlined, MenuUnfoldOutlined, MenuFoldOutlined } from "@ant-design/icons";
5
+ import { useNavigate, useLocation, Outlet } from "react-router-dom";
6
+ import { u as useAuthGuard, h as useLogout, i as useTenantSwitching, g as getCurrentTenantId } from "./index-D3uUqZ5c.js";
7
+ import { useAuth } from "react-oidc-context";
8
+ import { y as getPersonDetail, z as getSessionList, I as revokeAllSessions, Q as updatePassword, J as revokeSession } from "./index-BQuRl5A6.js";
9
+ import zhCN from "antd/locale/zh_CN";
10
+ import dayjs from "dayjs";
11
+ import "dayjs/locale/zh-cn";
12
+ const tokens = {
13
+ // 品牌 Brand:靛蓝 → 紫,仅用于主按钮 / 链接 / 选中态 / 登录与 Logo 品牌区
14
+ primary: "#4f6ef7",
15
+ primaryHover: "#6b86ff",
16
+ primaryActive: "#3a55d6",
17
+ gradient: "linear-gradient(135deg, #4f6ef7 0%, #7a5af8 55%, #a855f7 100%)",
18
+ gradientSoft: "linear-gradient(135deg, #eef2ff 0%, #f5f0ff 100%)",
19
+ purple: "#7a5af8",
20
+ // 品牌紫:分类图标/点缀强调(极少量)
21
+ selectedBg: "rgba(79, 110, 247, 0.14)",
22
+ // 深色导航选中项柔和底(非整块亮色)
23
+ // 表面 Surface(中性灰阶,禁用靛蓝淡底 tint)
24
+ bg: "#f6f7f9",
25
+ // 页面布局底色(冷白)
26
+ cardBg: "#ffffff",
27
+ sidebarBg: "#0f172a",
28
+ headerBg: "#ffffff",
29
+ tableHeaderBg: "#f7f8fa",
30
+ rowHoverBg: "#f3f4f6",
31
+ softFill: "#f1f3f5",
32
+ // 中性填充底(统计卡图标底、应用固定条等)
33
+ codeBg: "#fafafa",
34
+ // 代码/内容块底色
35
+ // 边框 Border(冷调 hairline,卡片分层的主语言)
36
+ border: "#e6e9ef",
37
+ borderStrong: "#d3d8e0",
38
+ // 更强的分割线(Header 内分割线等)
39
+ // 文字 Text(三级 hex 灰阶)
40
+ text: "#1f2430",
41
+ textSecondary: "#64748d",
42
+ // 借鉴 Stripe ink-mute
43
+ textPlaceholder: "#94a3b8",
44
+ // 空态/占位辅助文字
45
+ // 语义色 Semantic(与 antd colorSuccess/Warning/Error 对齐,仅状态 Tag/告警使用)
46
+ success: "#22c55e",
47
+ warning: "#f59e0b",
48
+ error: "#ef4444",
49
+ warningBg: "#fffbe6",
50
+ // 轻量告警底色
51
+ warningBorder: "#ffe58f"
52
+ // 轻量告警描边
53
+ };
54
+ const brand = {
55
+ primary: tokens.primary,
56
+ primaryHover: tokens.primaryHover,
57
+ primaryActive: tokens.primaryActive,
58
+ gradient: tokens.gradient,
59
+ gradientSoft: tokens.gradientSoft,
60
+ bg: tokens.bg,
61
+ sidebarBg: tokens.sidebarBg,
62
+ text: tokens.text,
63
+ textSecondary: tokens.textSecondary
64
+ };
65
+ const themeConfig = {
66
+ token: {
67
+ colorPrimary: tokens.primary,
68
+ colorInfo: tokens.primary,
69
+ colorLink: tokens.primary,
70
+ colorSuccess: tokens.success,
71
+ colorWarning: tokens.warning,
72
+ colorError: tokens.error,
73
+ colorBgLayout: tokens.bg,
74
+ colorBorderSecondary: tokens.border,
75
+ // hairline 统一为冷调
76
+ borderRadius: 8,
77
+ borderRadiusLG: 12,
78
+ colorText: tokens.text,
79
+ colorTextSecondary: tokens.textSecondary,
80
+ fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', sans-serif",
81
+ boxShadowSecondary: "0 6px 24px rgba(15, 23, 42, 0.08)"
82
+ // 仅浮层/Modal 使用
83
+ },
84
+ components: {
85
+ Layout: {
86
+ headerBg: tokens.headerBg,
87
+ headerHeight: 56,
88
+ siderBg: tokens.sidebarBg,
89
+ bodyBg: tokens.bg
90
+ },
91
+ Menu: {
92
+ darkItemBg: tokens.sidebarBg,
93
+ darkItemSelectedBg: tokens.selectedBg,
94
+ darkItemSelectedColor: "#ffffff",
95
+ darkItemHoverBg: "rgba(255,255,255,0.08)",
96
+ itemBorderRadius: 8,
97
+ itemHeight: 42
98
+ },
99
+ Card: {
100
+ headerFontSize: 15
101
+ },
102
+ Table: {
103
+ headerBg: tokens.tableHeaderBg,
104
+ headerColor: tokens.textSecondary,
105
+ rowHoverBg: tokens.rowHoverBg
106
+ },
107
+ Modal: {
108
+ borderRadiusLG: 14
109
+ }
110
+ }
111
+ };
112
+ function TenantSwitcher() {
113
+ var _a;
114
+ const { tenants, loadTenants, handleSwitchTenant } = useTenantSwitching();
115
+ const currentId = getCurrentTenantId();
116
+ const currentName = ((_a = tenants.find((t) => String(t.tenantID) === currentId)) == null ? void 0 : _a.name) || "当前租户";
117
+ return /* @__PURE__ */ jsx(
118
+ Dropdown,
119
+ {
120
+ menu: {
121
+ items: tenants.map((t) => ({
122
+ key: `tenant-${t.tenantID}`,
123
+ label: /* @__PURE__ */ jsxs("span", { style: { display: "flex", alignItems: "center", gap: 8 }, children: [
124
+ t.name,
125
+ String(t.tenantID) === currentId && /* @__PURE__ */ jsx(Tag, { color: "blue", style: { marginInlineEnd: 0 }, children: "当前" })
126
+ ] }),
127
+ disabled: String(t.tenantID) === currentId,
128
+ onClick: () => handleSwitchTenant(t.tenantID)
129
+ }))
130
+ },
131
+ onOpenChange: (open) => {
132
+ if (open) void loadTenants();
133
+ },
134
+ children: /* @__PURE__ */ jsxs(
135
+ "span",
136
+ {
137
+ style: {
138
+ color: tokens.text,
139
+ cursor: "pointer",
140
+ fontSize: 13,
141
+ lineHeight: 1,
142
+ display: "inline-flex",
143
+ alignItems: "center",
144
+ height: 32,
145
+ padding: "0 12px",
146
+ boxSizing: "border-box",
147
+ borderRadius: 8,
148
+ border: `1px solid ${tokens.borderStrong}`,
149
+ background: tokens.tableHeaderBg,
150
+ transition: "all 0.2s"
151
+ },
152
+ className: "tenant-switcher",
153
+ children: [
154
+ /* @__PURE__ */ jsx(SwapOutlined, { style: { color: tokens.primary } }),
155
+ /* @__PURE__ */ jsx("span", { style: { fontWeight: 500, lineHeight: 1 }, children: currentName })
156
+ ]
157
+ }
158
+ )
159
+ }
160
+ );
161
+ }
162
+ function IDCell({ value, prefix = 8, suffix = 4 }) {
163
+ const text = value == null ? "" : String(value);
164
+ if (!text) return /* @__PURE__ */ jsx("span", { children: "-" });
165
+ const short = text.length > prefix + suffix + 1 ? `${text.slice(0, prefix)}…${text.slice(-suffix)}` : text;
166
+ return /* @__PURE__ */ jsx(
167
+ Tooltip,
168
+ {
169
+ title: text,
170
+ mouseEnterDelay: 0.3,
171
+ mouseLeaveDelay: 0.6,
172
+ styles: { body: { userSelect: "text" } },
173
+ children: /* @__PURE__ */ jsx("span", { style: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace", fontSize: 12 }, children: short })
174
+ }
175
+ );
176
+ }
177
+ function EllipsisCell({ value, limit, monospace = false, placeholder = "-" }) {
178
+ const text = value == null ? "" : String(value);
179
+ if (!text) return /* @__PURE__ */ jsx("span", { children: placeholder });
180
+ const display = limit && text.length > limit ? `${text.slice(0, limit)}…` : text;
181
+ return /* @__PURE__ */ jsx(
182
+ Tooltip,
183
+ {
184
+ title: text,
185
+ mouseEnterDelay: 0.3,
186
+ mouseLeaveDelay: 0.6,
187
+ styles: { body: { userSelect: "text" } },
188
+ children: /* @__PURE__ */ jsx(
189
+ "span",
190
+ {
191
+ style: {
192
+ display: "inline-block",
193
+ maxWidth: "100%",
194
+ overflow: "hidden",
195
+ textOverflow: "ellipsis",
196
+ whiteSpace: "nowrap",
197
+ verticalAlign: "bottom",
198
+ ...monospace ? { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace", fontSize: 12 } : {}
199
+ },
200
+ children: display
201
+ }
202
+ )
203
+ }
204
+ );
205
+ }
206
+ function RowActions({ actions, max = 3 }) {
207
+ const { modal } = App.useApp();
208
+ const visible = actions.filter((action) => !action.hidden);
209
+ if (visible.length === 0) return null;
210
+ const renderInline = (action) => {
211
+ if (!action.confirm) {
212
+ return /* @__PURE__ */ jsx(
213
+ Button,
214
+ {
215
+ type: "link",
216
+ size: "small",
217
+ danger: action.danger,
218
+ disabled: action.disabled,
219
+ icon: action.icon,
220
+ onClick: action.onClick,
221
+ children: action.label
222
+ },
223
+ action.key
224
+ );
225
+ }
226
+ return /* @__PURE__ */ jsx(
227
+ Popconfirm,
228
+ {
229
+ title: action.confirm,
230
+ disabled: action.disabled,
231
+ onConfirm: action.onClick,
232
+ children: /* @__PURE__ */ jsx(Button, { type: "link", size: "small", danger: action.danger, disabled: action.disabled, icon: action.icon, children: action.label })
233
+ },
234
+ action.key
235
+ );
236
+ };
237
+ if (visible.length <= max) {
238
+ return /* @__PURE__ */ jsx(Space, { size: 4, children: visible.map(renderInline) });
239
+ }
240
+ const inline = visible.slice(0, Math.max(1, max - 1));
241
+ const overflow = visible.slice(Math.max(1, max - 1));
242
+ const items = overflow.map((action) => ({
243
+ key: action.key,
244
+ label: action.label,
245
+ icon: action.icon,
246
+ danger: action.danger,
247
+ disabled: action.disabled
248
+ }));
249
+ const handleMenuClick = ({ key }) => {
250
+ var _a;
251
+ const action = overflow.find((item) => item.key === key);
252
+ if (!action) return;
253
+ if (action.confirm) {
254
+ modal.confirm({
255
+ title: action.confirm,
256
+ okButtonProps: { danger: action.danger },
257
+ onOk: () => {
258
+ var _a2;
259
+ return (_a2 = action.onClick) == null ? void 0 : _a2.call(action);
260
+ }
261
+ });
262
+ return;
263
+ }
264
+ (_a = action.onClick) == null ? void 0 : _a.call(action);
265
+ };
266
+ return /* @__PURE__ */ jsxs(Space, { size: 4, children: [
267
+ inline.map(renderInline),
268
+ /* @__PURE__ */ jsx(Dropdown, { menu: { items, onClick: handleMenuClick }, trigger: ["click"], children: /* @__PURE__ */ jsxs(Button, { type: "link", size: "small", children: [
269
+ "更多 ",
270
+ /* @__PURE__ */ jsx(DownOutlined, { style: { fontSize: 10 } })
271
+ ] }) })
272
+ ] });
273
+ }
274
+ function fmtTime(value) {
275
+ if (value == null || value === "" || value === 0) return "-";
276
+ if (typeof value === "number") {
277
+ const ms = toMillis(value);
278
+ const d = new Date(ms);
279
+ if (Number.isNaN(d.getTime())) return String(value);
280
+ const pad = (n) => String(n).padStart(2, "0");
281
+ return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`;
282
+ }
283
+ return value;
284
+ }
285
+ function toMillis(value) {
286
+ return value < 1e12 ? value * 1e3 : value;
287
+ }
288
+ const REL_MINUTE = 60;
289
+ const REL_HOUR = 60 * REL_MINUTE;
290
+ const REL_DAY = 24 * REL_HOUR;
291
+ const REL_MONTH = 30 * REL_DAY;
292
+ const REL_YEAR = 365 * REL_DAY;
293
+ function fmtRelativeTime(value, now = Date.now()) {
294
+ if (value == null || value === "" || value === 0) return "";
295
+ if (typeof value !== "number") return String(value);
296
+ const ms = toMillis(value);
297
+ if (Number.isNaN(ms)) return String(value);
298
+ const diff = Math.floor((now - ms) / 1e3);
299
+ if (diff < 0) return fmtTime(value);
300
+ if (diff < REL_MINUTE) return "刚刚";
301
+ if (diff < REL_HOUR) return `${Math.floor(diff / REL_MINUTE)} 分钟前`;
302
+ if (diff < REL_DAY) return `${Math.floor(diff / REL_HOUR)} 小时前`;
303
+ if (diff < REL_MONTH) return `${Math.floor(diff / REL_DAY)} 天前`;
304
+ if (diff < REL_YEAR) return `${Math.floor(diff / REL_MONTH)} 个月前`;
305
+ return `${Math.floor(diff / REL_YEAR)} 年前`;
306
+ }
307
+ const TIME_COL_WIDTH = 180;
308
+ const TIME_COL_WIDTH_RELATIVE = 120;
309
+ function TimeCell({ value, placeholder = "-", relative = false }) {
310
+ if (value == null || value === "" || value === 0) {
311
+ return /* @__PURE__ */ jsx("span", { style: { color: tokens.textPlaceholder }, children: placeholder });
312
+ }
313
+ const absolute = fmtTime(value);
314
+ if (!relative) return /* @__PURE__ */ jsx("span", { style: { whiteSpace: "nowrap" }, children: absolute });
315
+ const text = fmtRelativeTime(value);
316
+ return /* @__PURE__ */ jsx(Tooltip, { title: absolute, mouseEnterDelay: 0.3, children: /* @__PURE__ */ jsx("span", { style: { whiteSpace: "nowrap" }, children: text || absolute }) });
317
+ }
318
+ function timeColumn({
319
+ title,
320
+ dataIndex,
321
+ width,
322
+ placeholder,
323
+ relative = false
324
+ }) {
325
+ return {
326
+ title,
327
+ dataIndex,
328
+ key: dataIndex,
329
+ width: width ?? (relative ? TIME_COL_WIDTH_RELATIVE : TIME_COL_WIDTH),
330
+ render: (value) => /* @__PURE__ */ jsx(TimeCell, { value, placeholder, relative })
331
+ };
332
+ }
333
+ function ProfileCenter({ open, onClose }) {
334
+ var _a, _b;
335
+ const [person, setPerson] = useState(null);
336
+ const [sessions, setSessions] = useState([]);
337
+ const [loading, setLoading] = useState(false);
338
+ const [form] = Form.useForm();
339
+ const loadPerson = useCallback(async () => {
340
+ try {
341
+ setPerson(await getPersonDetail());
342
+ } catch {
343
+ }
344
+ }, []);
345
+ const loadSessions = useCallback(async () => {
346
+ setLoading(true);
347
+ try {
348
+ const pages = await Promise.all(
349
+ [1, 2, 3, 4, 5].map((p) => getSessionList({ page: p, pageSize: 10 }).catch(() => ({ list: [], total: 0 })))
350
+ );
351
+ const seen = /* @__PURE__ */ new Set();
352
+ const merged = [];
353
+ for (const resp of pages) {
354
+ for (const s of resp.list || []) {
355
+ if (!seen.has(s.id)) {
356
+ seen.add(s.id);
357
+ merged.push(s);
358
+ }
359
+ }
360
+ }
361
+ setSessions(merged);
362
+ } catch {
363
+ } finally {
364
+ setLoading(false);
365
+ }
366
+ }, []);
367
+ useEffect(() => {
368
+ if (open) {
369
+ void loadPerson();
370
+ void loadSessions();
371
+ }
372
+ }, [open, loadPerson, loadSessions]);
373
+ const submitPassword = async () => {
374
+ try {
375
+ const values = await form.validateFields();
376
+ if (values.newPassword !== values.confirmPassword) {
377
+ message.warning("两次输入的新密码不一致");
378
+ return;
379
+ }
380
+ await updatePassword({ oldPassword: values.oldPassword, newPassword: values.newPassword });
381
+ message.success("密码已修改");
382
+ form.resetFields();
383
+ } catch {
384
+ }
385
+ };
386
+ const columns = [
387
+ { title: "会话ID", dataIndex: "sessionID", key: "sessionID", width: 160, render: (v) => /* @__PURE__ */ jsx(IDCell, { value: v }) },
388
+ { title: "客户端", dataIndex: "clientType", key: "clientType", width: 110 },
389
+ { title: "IP", dataIndex: "clientIP", key: "clientIP", width: 130 },
390
+ {
391
+ title: "状态",
392
+ dataIndex: "isActive",
393
+ key: "isActive",
394
+ width: 90,
395
+ render: (v) => v ? /* @__PURE__ */ jsx(Tag, { color: "success", children: "活跃" }) : /* @__PURE__ */ jsx(Tag, { color: "default", children: "已失效" })
396
+ },
397
+ {
398
+ title: "UserAgent",
399
+ dataIndex: "userAgent",
400
+ key: "userAgent",
401
+ render: (v) => /* @__PURE__ */ jsx(EllipsisCell, { value: v })
402
+ },
403
+ timeColumn({ title: "创建时间", dataIndex: "createdAt" }),
404
+ {
405
+ title: "操作",
406
+ key: "action",
407
+ width: 90,
408
+ render: (_, record) => /* @__PURE__ */ jsx(
409
+ RowActions,
410
+ {
411
+ actions: [
412
+ {
413
+ key: "revoke",
414
+ label: "撤销",
415
+ danger: true,
416
+ confirm: "确认撤销该会话?",
417
+ onClick: () => {
418
+ void revokeSession(record.id).then(() => {
419
+ message.success("已撤销");
420
+ void loadSessions();
421
+ }).catch(() => message.error("撤销会话失败"));
422
+ }
423
+ }
424
+ ]
425
+ }
426
+ )
427
+ }
428
+ ];
429
+ return /* @__PURE__ */ jsx(Modal, { title: "个人中心", open, onCancel: onClose, footer: null, width: 760, styles: { body: { paddingTop: 12 } }, children: /* @__PURE__ */ jsx(
430
+ Tabs,
431
+ {
432
+ items: [
433
+ {
434
+ key: "info",
435
+ label: "个人信息",
436
+ children: /* @__PURE__ */ jsxs("div", { children: [
437
+ /* @__PURE__ */ jsxs(Space, { size: 20, align: "center", style: { marginBottom: 20 }, children: [
438
+ /* @__PURE__ */ jsx(Avatar, { size: 56, style: { background: brand.gradient, fontSize: 22 }, children: ((_b = (_a = person == null ? void 0 : person.name) == null ? void 0 : _a.charAt(0)) == null ? void 0 : _b.toUpperCase()) || "U" }),
439
+ /* @__PURE__ */ jsxs("div", { children: [
440
+ /* @__PURE__ */ jsxs("div", { style: { fontSize: 18, fontWeight: 700 }, children: [
441
+ (person == null ? void 0 : person.name) || "-",
442
+ (person == null ? void 0 : person.status) === "suspended" && /* @__PURE__ */ jsx(Tag, { color: "error", style: { marginLeft: 8 }, children: "已挂起" })
443
+ ] }),
444
+ /* @__PURE__ */ jsxs("div", { style: { color: brand.textSecondary }, children: [
445
+ "@",
446
+ (person == null ? void 0 : person.username) || "-"
447
+ ] })
448
+ ] })
449
+ ] }),
450
+ /* @__PURE__ */ jsxs(Descriptions, { column: 1, bordered: true, size: "small", children: [
451
+ /* @__PURE__ */ jsx(Descriptions.Item, { label: "账号", children: (person == null ? void 0 : person.username) ?? "-" }),
452
+ /* @__PURE__ */ jsx(Descriptions.Item, { label: "姓名", children: (person == null ? void 0 : person.name) ?? "-" }),
453
+ /* @__PURE__ */ jsx(Descriptions.Item, { label: "邮箱", children: (person == null ? void 0 : person.primaryEmail) ?? "-" }),
454
+ /* @__PURE__ */ jsx(Descriptions.Item, { label: "手机号", children: (person == null ? void 0 : person.primaryPhone) ?? "-" })
455
+ ] })
456
+ ] })
457
+ },
458
+ {
459
+ key: "password",
460
+ label: "修改密码",
461
+ children: /* @__PURE__ */ jsxs(Form, { form, layout: "vertical", style: { maxWidth: 420 }, children: [
462
+ /* @__PURE__ */ jsx(Form.Item, { name: "oldPassword", label: "原密码", rules: [{ required: true, message: "请输入原密码" }], children: /* @__PURE__ */ jsx(Input.Password, {}) }),
463
+ /* @__PURE__ */ jsx(
464
+ Form.Item,
465
+ {
466
+ name: "newPassword",
467
+ label: "新密码",
468
+ rules: [
469
+ { required: true, message: "请输入新密码" },
470
+ { min: 8, message: "至少 8 位,需含大小写字母和数字" }
471
+ ],
472
+ children: /* @__PURE__ */ jsx(Input.Password, {})
473
+ }
474
+ ),
475
+ /* @__PURE__ */ jsx(
476
+ Form.Item,
477
+ {
478
+ name: "confirmPassword",
479
+ label: "确认新密码",
480
+ rules: [{ required: true, message: "请再次输入新密码" }],
481
+ children: /* @__PURE__ */ jsx(Input.Password, {})
482
+ }
483
+ ),
484
+ /* @__PURE__ */ jsx(Button, { type: "primary", onClick: () => void submitPassword(), children: "确认修改" })
485
+ ] })
486
+ },
487
+ {
488
+ key: "session",
489
+ label: "会话管理",
490
+ children: /* @__PURE__ */ jsxs(Fragment, { children: [
491
+ /* @__PURE__ */ jsxs(Space, { style: { marginBottom: 12 }, children: [
492
+ /* @__PURE__ */ jsx(
493
+ Button,
494
+ {
495
+ danger: true,
496
+ onClick: () => void revokeAllSessions().then(() => {
497
+ message.success("已撤销全部会话");
498
+ void loadSessions();
499
+ }).catch(() => message.error("撤销全部会话失败")),
500
+ children: "撤销全部会话"
501
+ }
502
+ ),
503
+ /* @__PURE__ */ jsxs("span", { style: { color: brand.textSecondary, fontSize: 12 }, children: [
504
+ "共 ",
505
+ sessions.length,
506
+ " 条(含历史失效会话)"
507
+ ] })
508
+ ] }),
509
+ /* @__PURE__ */ jsx(
510
+ Table,
511
+ {
512
+ rowKey: "id",
513
+ columns,
514
+ dataSource: sessions,
515
+ loading,
516
+ pagination: { pageSize: 10, showSizeChanger: false, showTotal: (t) => `共 ${t} 条` },
517
+ scroll: { x: 820 }
518
+ }
519
+ )
520
+ ] })
521
+ }
522
+ ]
523
+ }
524
+ ) });
525
+ }
526
+ const { Header, Sider, Content } = Layout;
527
+ function MainLayout({ title, subtitle, menuItems, hasTenantSwitch = true }) {
528
+ const [collapsed, setCollapsed] = useState(false);
529
+ const [profileOpen, setProfileOpen] = useState(false);
530
+ const navigate = useNavigate();
531
+ const location = useLocation();
532
+ const auth = useAuth();
533
+ useAuthGuard();
534
+ const logout = useLogout();
535
+ const { token } = theme.useToken();
536
+ const displayName = useMemo(() => {
537
+ var _a;
538
+ const profile = (_a = auth.user) == null ? void 0 : _a.profile;
539
+ return (profile == null ? void 0 : profile.name) || (profile == null ? void 0 : profile.preferred_username) || "IAM 用户";
540
+ }, [auth.user]);
541
+ const userMenuItems = useMemo(
542
+ () => [
543
+ { key: "profile", icon: /* @__PURE__ */ jsx(UserSwitchOutlined, {}), label: "个人中心", onClick: () => setProfileOpen(true) },
544
+ { type: "divider" },
545
+ { key: "logout", icon: /* @__PURE__ */ jsx(LogoutOutlined, {}), label: "退出登录", onClick: () => void logout() }
546
+ ],
547
+ [logout]
548
+ );
549
+ const selectedKey = useMemo(() => {
550
+ const parts = location.pathname.split("/");
551
+ const level1 = parts[1] ? `/${parts[1]}` : "/";
552
+ const level2 = parts.length > 2 ? `/${parts[1]}/${parts[2]}` : "";
553
+ const flat = (items) => items.flatMap((i) => [i.key, ...i.children ? flat(i.children) : []]);
554
+ const all = flat(menuItems);
555
+ if (all.includes(level2)) return level2;
556
+ if (all.includes(level1)) return level1;
557
+ return "/";
558
+ }, [location.pathname, menuItems]);
559
+ return /* @__PURE__ */ jsxs(Layout, { style: { minHeight: "100vh" }, children: [
560
+ /* @__PURE__ */ jsxs(
561
+ Sider,
562
+ {
563
+ trigger: null,
564
+ collapsible: true,
565
+ collapsed,
566
+ width: 232,
567
+ theme: "dark",
568
+ style: { position: "sticky", top: 0, height: "100vh" },
569
+ children: [
570
+ /* @__PURE__ */ jsxs(
571
+ "div",
572
+ {
573
+ style: {
574
+ height: 64,
575
+ display: "flex",
576
+ alignItems: "center",
577
+ justifyContent: "center",
578
+ gap: 10,
579
+ background: brand.gradient,
580
+ color: "#fff",
581
+ overflow: "hidden"
582
+ },
583
+ children: [
584
+ /* @__PURE__ */ jsx(SafetyCertificateOutlined, { style: { fontSize: collapsed ? 20 : 24 } }),
585
+ !collapsed && /* @__PURE__ */ jsxs("div", { style: { lineHeight: 1.1 }, children: [
586
+ /* @__PURE__ */ jsx("div", { style: { fontSize: 16, fontWeight: 700, letterSpacing: 0.5 }, children: title }),
587
+ subtitle && /* @__PURE__ */ jsx("div", { style: { fontSize: 11, opacity: 0.8 }, children: subtitle })
588
+ ] })
589
+ ]
590
+ }
591
+ ),
592
+ /* @__PURE__ */ jsx(
593
+ Menu,
594
+ {
595
+ theme: "dark",
596
+ mode: "inline",
597
+ selectedKeys: [selectedKey],
598
+ defaultOpenKeys: [selectedKey],
599
+ items: menuItems,
600
+ onClick: ({ key }) => {
601
+ if (key !== location.pathname) navigate(key);
602
+ },
603
+ style: { padding: "8px 10px", borderInlineEnd: "none" }
604
+ }
605
+ )
606
+ ]
607
+ }
608
+ ),
609
+ /* @__PURE__ */ jsxs(Layout, { children: [
610
+ /* @__PURE__ */ jsxs(
611
+ Header,
612
+ {
613
+ style: {
614
+ padding: "0 20px",
615
+ background: "#fff",
616
+ display: "flex",
617
+ alignItems: "center",
618
+ justifyContent: "space-between",
619
+ borderBottom: `1px solid ${tokens.border}`,
620
+ position: "sticky",
621
+ top: 0,
622
+ zIndex: 10,
623
+ lineHeight: 1
624
+ },
625
+ children: [
626
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: 12, lineHeight: "normal" }, children: [
627
+ /* @__PURE__ */ jsx(Tooltip, { title: collapsed ? "展开菜单" : "收起菜单", children: /* @__PURE__ */ jsx(ButtonIcon, { collapsed, onClick: () => setCollapsed(!collapsed) }) }),
628
+ hasTenantSwitch && /* @__PURE__ */ jsxs(Fragment, { children: [
629
+ /* @__PURE__ */ jsx("div", { style: { width: 1, height: 22, background: tokens.borderStrong } }),
630
+ /* @__PURE__ */ jsx(TenantSwitcher, {})
631
+ ] })
632
+ ] }),
633
+ /* @__PURE__ */ jsx("div", { style: { display: "flex", alignItems: "center", gap: 14, lineHeight: "normal" }, children: /* @__PURE__ */ jsx(Dropdown, { menu: { items: userMenuItems }, placement: "bottomRight", trigger: ["click"], children: /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: 10, height: 42, padding: "0 8px", lineHeight: 1, borderRadius: 8, cursor: "pointer" }, className: "user-entry", children: [
634
+ /* @__PURE__ */ jsx(Badge, { dot: true, color: brand.primary, offset: [-2, 2], children: /* @__PURE__ */ jsx(
635
+ Avatar,
636
+ {
637
+ size: 34,
638
+ style: { background: brand.gradient, fontWeight: 600, fontSize: 14 },
639
+ icon: !displayName ? /* @__PURE__ */ jsx(UserOutlined, {}) : void 0,
640
+ children: displayName.charAt(0).toUpperCase()
641
+ }
642
+ ) }),
643
+ /* @__PURE__ */ jsx("span", { style: { fontSize: 14, color: token.colorText, fontWeight: 500, lineHeight: 1 }, children: displayName })
644
+ ] }) }) })
645
+ ]
646
+ }
647
+ ),
648
+ /* @__PURE__ */ jsx(Content, { style: { margin: 20 }, children: /* @__PURE__ */ jsx(Outlet, {}) })
649
+ ] }),
650
+ /* @__PURE__ */ jsx(ProfileCenter, { open: profileOpen, onClose: () => setProfileOpen(false) })
651
+ ] });
652
+ }
653
+ const ButtonIcon = forwardRef(function ButtonIcon2({ collapsed, onClick }, ref) {
654
+ return /* @__PURE__ */ jsx(
655
+ "button",
656
+ {
657
+ ref,
658
+ onClick,
659
+ style: {
660
+ width: 36,
661
+ height: 36,
662
+ display: "flex",
663
+ alignItems: "center",
664
+ justifyContent: "center",
665
+ border: "none",
666
+ borderRadius: 8,
667
+ background: "transparent",
668
+ cursor: "pointer",
669
+ fontSize: 16,
670
+ color: brand.textSecondary
671
+ },
672
+ children: collapsed ? /* @__PURE__ */ jsx(MenuUnfoldOutlined, {}) : /* @__PURE__ */ jsx(MenuFoldOutlined, {})
673
+ }
674
+ );
675
+ });
676
+ function LoginPage({ title, subtitle }) {
677
+ const auth = useAuth();
678
+ return /* @__PURE__ */ jsxs(
679
+ "div",
680
+ {
681
+ style: {
682
+ minHeight: "100vh",
683
+ display: "flex",
684
+ alignItems: "stretch",
685
+ background: brand.gradientSoft
686
+ },
687
+ children: [
688
+ /* @__PURE__ */ jsxs(
689
+ "div",
690
+ {
691
+ style: {
692
+ flex: 1.2,
693
+ display: "flex",
694
+ flexDirection: "column",
695
+ justifyContent: "center",
696
+ padding: "0 8vw",
697
+ background: brand.gradient,
698
+ color: "#fff",
699
+ position: "relative",
700
+ overflow: "hidden"
701
+ },
702
+ children: [
703
+ /* @__PURE__ */ jsx(
704
+ "div",
705
+ {
706
+ style: {
707
+ position: "absolute",
708
+ width: 420,
709
+ height: 420,
710
+ borderRadius: "50%",
711
+ background: "rgba(255,255,255,0.08)",
712
+ top: -120,
713
+ right: -120
714
+ }
715
+ }
716
+ ),
717
+ /* @__PURE__ */ jsx(
718
+ "div",
719
+ {
720
+ style: {
721
+ position: "absolute",
722
+ width: 260,
723
+ height: 260,
724
+ borderRadius: "50%",
725
+ background: "rgba(255,255,255,0.06)",
726
+ bottom: -60,
727
+ left: -60
728
+ }
729
+ }
730
+ ),
731
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: 14, marginBottom: 28, position: "relative" }, children: [
732
+ /* @__PURE__ */ jsx(
733
+ "div",
734
+ {
735
+ style: {
736
+ width: 52,
737
+ height: 52,
738
+ borderRadius: 14,
739
+ background: "rgba(255,255,255,0.18)",
740
+ display: "flex",
741
+ alignItems: "center",
742
+ justifyContent: "center",
743
+ fontSize: 26,
744
+ backdropFilter: "blur(4px)"
745
+ },
746
+ children: /* @__PURE__ */ jsx(SafetyCertificateOutlined, {})
747
+ }
748
+ ),
749
+ /* @__PURE__ */ jsxs("div", { children: [
750
+ /* @__PURE__ */ jsx("div", { style: { fontSize: 22, fontWeight: 700 }, children: title }),
751
+ subtitle && /* @__PURE__ */ jsx("div", { style: { fontSize: 13, opacity: 0.85 }, children: subtitle })
752
+ ] })
753
+ ] }),
754
+ /* @__PURE__ */ jsx(Typography.Title, { level: 2, style: { color: "#fff", marginBottom: 12, position: "relative" }, children: "统一身份认证平台" }),
755
+ /* @__PURE__ */ jsx(Typography.Paragraph, { style: { color: "rgba(255,255,255,0.82)", fontSize: 15, maxWidth: 480, position: "relative" }, children: "一个平台管理多应用的账号、权限与安全策略。基于标准 OIDC/OAuth2 协议, 支持单点登录、多租户切换与细粒度权限控制。" }),
756
+ /* @__PURE__ */ jsx("div", { style: { display: "flex", gap: 24, marginTop: 28, position: "relative" }, children: ["单点登录 SSO", "多租户隔离", "细粒度授权"].map((f) => /* @__PURE__ */ jsx(
757
+ "div",
758
+ {
759
+ style: {
760
+ padding: "8px 16px",
761
+ borderRadius: 20,
762
+ background: "rgba(255,255,255,0.14)",
763
+ fontSize: 13,
764
+ backdropFilter: "blur(4px)"
765
+ },
766
+ children: f
767
+ },
768
+ f
769
+ )) })
770
+ ]
771
+ }
772
+ ),
773
+ /* @__PURE__ */ jsx("div", { style: { flex: 1, display: "flex", alignItems: "center", justifyContent: "center", padding: 40 }, children: /* @__PURE__ */ jsxs(
774
+ "div",
775
+ {
776
+ style: {
777
+ width: 380,
778
+ background: "#fff",
779
+ borderRadius: 16,
780
+ boxShadow: "0 20px 60px rgba(15,23,42,0.12)",
781
+ padding: "40px 36px"
782
+ },
783
+ children: [
784
+ /* @__PURE__ */ jsx("div", { style: { fontSize: 20, fontWeight: 700, marginBottom: 8 }, children: "欢迎回来" }),
785
+ /* @__PURE__ */ jsxs("div", { style: { fontSize: 14, color: brand.textSecondary, marginBottom: 32 }, children: [
786
+ "使用统一身份账号登录 ",
787
+ title
788
+ ] }),
789
+ /* @__PURE__ */ jsxs(
790
+ Button,
791
+ {
792
+ type: "primary",
793
+ size: "large",
794
+ block: true,
795
+ loading: auth.isLoading,
796
+ onClick: () => void auth.signinRedirect(),
797
+ style: { height: 46, borderRadius: 10, fontWeight: 600, background: brand.gradient, border: "none", boxShadow: "0 8px 20px rgba(79,110,247,0.35)" },
798
+ children: [
799
+ "IAM 账号登录 ",
800
+ /* @__PURE__ */ jsx(ArrowRightOutlined, {})
801
+ ]
802
+ }
803
+ ),
804
+ /* @__PURE__ */ jsx("div", { style: { marginTop: 24, textAlign: "center", fontSize: 12, color: brand.textSecondary }, children: "登录即代表同意平台的安全与隐私策略" })
805
+ ]
806
+ }
807
+ ) })
808
+ ]
809
+ }
810
+ );
811
+ }
812
+ function PageContainer({ title, description, extra, children, loading }) {
813
+ return /* @__PURE__ */ jsxs("div", { children: [
814
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "flex-start", marginBottom: 16 }, children: [
815
+ /* @__PURE__ */ jsxs("div", { children: [
816
+ /* @__PURE__ */ jsx(Typography.Title, { level: 4, style: { margin: 0, fontWeight: 700 }, children: title }),
817
+ description && /* @__PURE__ */ jsx(Typography.Text, { type: "secondary", style: { fontSize: 13 }, children: description })
818
+ ] }),
819
+ extra && /* @__PURE__ */ jsx(Space, { children: extra })
820
+ ] }),
821
+ /* @__PURE__ */ jsx(
822
+ Card,
823
+ {
824
+ loading,
825
+ styles: {
826
+ body: { padding: 20 }
827
+ },
828
+ style: { borderRadius: 12, border: `1px solid ${tokens.border}` },
829
+ children
830
+ }
831
+ )
832
+ ] });
833
+ }
834
+ const monospaceStyle = { fontFamily: "Consolas, Monaco, monospace" };
835
+ function InitialPasswordModal({ password, title, description, onClose }) {
836
+ const handleCopy = async () => {
837
+ try {
838
+ await navigator.clipboard.writeText(password);
839
+ message.success("已复制");
840
+ } catch {
841
+ message.error("复制失败,请手动复制");
842
+ }
843
+ };
844
+ return /* @__PURE__ */ jsx(
845
+ Modal,
846
+ {
847
+ title: title || "初始密码已生成",
848
+ open: !!password,
849
+ onCancel: onClose,
850
+ footer: /* @__PURE__ */ jsx(Button, { type: "primary", onClick: onClose, children: "我已保存" }),
851
+ destroyOnClose: true,
852
+ width: 560,
853
+ children: /* @__PURE__ */ jsx(
854
+ Alert,
855
+ {
856
+ type: "warning",
857
+ showIcon: true,
858
+ message: "请立即保存,关闭后不再显示",
859
+ description: /* @__PURE__ */ jsxs(Space, { direction: "vertical", size: 12, style: { width: "100%" }, children: [
860
+ description && /* @__PURE__ */ jsx("div", { style: { color: tokens.textSecondary }, children: description }),
861
+ /* @__PURE__ */ jsx(
862
+ "div",
863
+ {
864
+ style: {
865
+ ...monospaceStyle,
866
+ wordBreak: "break-all",
867
+ background: tokens.warningBg,
868
+ border: `1px solid ${tokens.warningBorder}`,
869
+ borderRadius: 8,
870
+ padding: "10px 12px"
871
+ },
872
+ children: password
873
+ }
874
+ ),
875
+ /* @__PURE__ */ jsx(Button, { size: "small", icon: /* @__PURE__ */ jsx(CopyOutlined, {}), onClick: () => void handleCopy(), children: "复制" })
876
+ ] })
877
+ }
878
+ )
879
+ }
880
+ );
881
+ }
882
+ function NameLink({ value, onClick, title, monospace = false }) {
883
+ const empty = value == null || value === "";
884
+ const text = empty ? "-" : value;
885
+ const style = {
886
+ display: "inline-block",
887
+ fontWeight: 500,
888
+ maxWidth: "100%",
889
+ overflow: "hidden",
890
+ textOverflow: "ellipsis",
891
+ whiteSpace: "nowrap",
892
+ verticalAlign: "bottom",
893
+ ...monospace ? { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace", fontSize: 12 } : {}
894
+ };
895
+ if (empty || !onClick) {
896
+ return /* @__PURE__ */ jsx("span", { style, children: text });
897
+ }
898
+ const tooltip = title ?? (typeof value === "string" || typeof value === "number" ? String(value) : void 0);
899
+ return /* @__PURE__ */ jsx(Tooltip, { title: tooltip, mouseEnterDelay: 0.3, children: /* @__PURE__ */ jsx(Typography.Link, { onClick, style, children: text }) });
900
+ }
901
+ const ID_COL_WIDTH = 130;
902
+ const NAME_COL_WIDTH = 180;
903
+ const CODE_COL_WIDTH = 150;
904
+ const TAG_COL_WIDTH = 110;
905
+ const STATUS_COL_WIDTH = 100;
906
+ const COUNT_COL_WIDTH = 90;
907
+ const TEXT_COL_WIDTH = 200;
908
+ const LONG_TEXT_COL_WIDTH = 320;
909
+ const CELL_PADDING = 32;
910
+ const ACTION_BUTTON_BUDGET = 56;
911
+ const ACTION_GAP = 4;
912
+ function idColumn({ dataIndex, title = "ID", width }) {
913
+ return {
914
+ title,
915
+ dataIndex,
916
+ key: dataIndex,
917
+ width: width ?? ID_COL_WIDTH,
918
+ render: (value) => /* @__PURE__ */ jsx(IDCell, { value })
919
+ };
920
+ }
921
+ function textColumn({
922
+ title,
923
+ dataIndex,
924
+ width,
925
+ placeholder,
926
+ monospace
927
+ }) {
928
+ return {
929
+ title,
930
+ dataIndex,
931
+ key: dataIndex,
932
+ width: width ?? TEXT_COL_WIDTH,
933
+ render: (value) => /* @__PURE__ */ jsx(
934
+ EllipsisCell,
935
+ {
936
+ value,
937
+ placeholder,
938
+ monospace
939
+ }
940
+ )
941
+ };
942
+ }
943
+ function nameColumn({
944
+ title,
945
+ dataIndex,
946
+ width,
947
+ monospace,
948
+ onClick
949
+ }) {
950
+ return {
951
+ title,
952
+ dataIndex,
953
+ key: dataIndex,
954
+ width: width ?? NAME_COL_WIDTH,
955
+ render: (value, record) => /* @__PURE__ */ jsx(
956
+ NameLink,
957
+ {
958
+ value,
959
+ monospace,
960
+ onClick: onClick ? () => onClick(record) : void 0
961
+ }
962
+ )
963
+ };
964
+ }
965
+ function actionColumn({ actions, max = 3, width }) {
966
+ return {
967
+ title: "操作",
968
+ key: "action",
969
+ fixed: "right",
970
+ width: width ?? actionColumnWidth(max),
971
+ render: (_, record) => /* @__PURE__ */ jsx(RowActions, { actions: actions(record), max })
972
+ };
973
+ }
974
+ function actionColumnWidth(max) {
975
+ const inline = Math.max(1, max);
976
+ const raw = CELL_PADDING + inline * ACTION_BUTTON_BUDGET + (inline - 1) * ACTION_GAP;
977
+ return Math.ceil(raw / 10) * 10;
978
+ }
979
+ function tableScrollX(columns) {
980
+ let total = 0;
981
+ const walk = (list) => {
982
+ var _a;
983
+ for (const column of list) {
984
+ if ((_a = column.children) == null ? void 0 : _a.length) {
985
+ walk(column.children);
986
+ continue;
987
+ }
988
+ total += typeof column.width === "number" ? column.width : 0;
989
+ }
990
+ };
991
+ walk(columns);
992
+ return { x: total };
993
+ }
994
+ function useRemoteOptions({ fetchOptions, debounceMs = 300, labelCache }) {
995
+ const [options, setOptions] = useState([]);
996
+ const [loading, setLoading] = useState(false);
997
+ const internalLabelsRef = useRef(/* @__PURE__ */ new Map());
998
+ const labels = labelCache ?? internalLabelsRef.current;
999
+ const timerRef = useRef(null);
1000
+ const seqRef = useRef(0);
1001
+ const search = useCallback(
1002
+ async (keyword) => {
1003
+ const seq = ++seqRef.current;
1004
+ setLoading(true);
1005
+ try {
1006
+ const list = await fetchOptions(keyword);
1007
+ if (seq !== seqRef.current) return;
1008
+ for (const item of list) labels.set(item.value, item.label);
1009
+ setOptions(list);
1010
+ } catch {
1011
+ } finally {
1012
+ if (seq === seqRef.current) setLoading(false);
1013
+ }
1014
+ },
1015
+ [fetchOptions, labels]
1016
+ );
1017
+ useEffect(
1018
+ () => () => {
1019
+ if (timerRef.current) clearTimeout(timerRef.current);
1020
+ },
1021
+ []
1022
+ );
1023
+ const handleSearch = useCallback(
1024
+ (keyword) => {
1025
+ if (timerRef.current) clearTimeout(timerRef.current);
1026
+ timerRef.current = setTimeout(() => void search(keyword), debounceMs);
1027
+ },
1028
+ [search, debounceMs]
1029
+ );
1030
+ const labelOf = useCallback(
1031
+ (v) => {
1032
+ var _a;
1033
+ return labels.get(v) ?? ((_a = options.find((o) => o.value === v)) == null ? void 0 : _a.label) ?? v;
1034
+ },
1035
+ [labels, options]
1036
+ );
1037
+ const withSelected = useCallback(
1038
+ (values) => {
1039
+ const missing = values.filter((v) => !options.some((o) => o.value === v));
1040
+ return missing.length ? [...missing.map((v) => ({ value: v, label: labelOf(v) })), ...options] : options;
1041
+ },
1042
+ [labelOf, options]
1043
+ );
1044
+ const remember = useCallback(
1045
+ (values) => values.map((v) => {
1046
+ const label = labelOf(v);
1047
+ labels.set(v, label);
1048
+ return { value: v, label };
1049
+ }),
1050
+ [labelOf, labels]
1051
+ );
1052
+ return { loading, search, handleSearch, withSelected, remember, labels };
1053
+ }
1054
+ function RemoteSelect({
1055
+ value,
1056
+ onChange,
1057
+ initialLabel,
1058
+ placeholder,
1059
+ disabled,
1060
+ allowClear,
1061
+ width,
1062
+ style,
1063
+ id,
1064
+ ...rest
1065
+ }) {
1066
+ const remote = useRemoteOptions(rest);
1067
+ useEffect(() => {
1068
+ if (value && initialLabel) remote.labels.set(value, initialLabel);
1069
+ }, [value, initialLabel, remote.labels]);
1070
+ useEffect(() => {
1071
+ if (disabled) return;
1072
+ void remote.search("");
1073
+ }, [remote.search, disabled]);
1074
+ const handleChange = (next) => {
1075
+ onChange == null ? void 0 : onChange(next, next ? remote.remember([next])[0] : void 0);
1076
+ };
1077
+ return /* @__PURE__ */ jsx(
1078
+ Select,
1079
+ {
1080
+ id,
1081
+ showSearch: true,
1082
+ filterOption: false,
1083
+ allowClear,
1084
+ placeholder,
1085
+ disabled,
1086
+ loading: remote.loading,
1087
+ style: width ? { width, ...style } : style,
1088
+ value,
1089
+ onChange: handleChange,
1090
+ onSearch: remote.handleSearch,
1091
+ options: remote.withSelected(value ? [value] : []),
1092
+ notFoundContent: remote.loading ? /* @__PURE__ */ jsx(Spin, { size: "small" }) : null
1093
+ }
1094
+ );
1095
+ }
1096
+ function RemoteMultiSelect({
1097
+ value,
1098
+ onChange,
1099
+ initialLabels,
1100
+ placeholder,
1101
+ disabled,
1102
+ allowClear,
1103
+ width,
1104
+ style,
1105
+ id,
1106
+ ...rest
1107
+ }) {
1108
+ const remote = useRemoteOptions(rest);
1109
+ const values = value ?? [];
1110
+ useEffect(() => {
1111
+ if (!initialLabels) return;
1112
+ Object.entries(initialLabels).forEach(([v, label]) => {
1113
+ if (v && label) remote.labels.set(v, label);
1114
+ });
1115
+ }, [initialLabels, remote.labels]);
1116
+ useEffect(() => {
1117
+ if (disabled) return;
1118
+ void remote.search("");
1119
+ }, [remote.search, disabled]);
1120
+ const handleChange = (next) => {
1121
+ const nextValues = next ?? [];
1122
+ onChange == null ? void 0 : onChange(nextValues, nextValues.length ? remote.remember(nextValues) : []);
1123
+ };
1124
+ return /* @__PURE__ */ jsx(
1125
+ Select,
1126
+ {
1127
+ id,
1128
+ mode: "multiple",
1129
+ showSearch: true,
1130
+ filterOption: false,
1131
+ allowClear,
1132
+ placeholder,
1133
+ disabled,
1134
+ loading: remote.loading,
1135
+ style: width ? { width, ...style } : style,
1136
+ value: values,
1137
+ onChange: handleChange,
1138
+ onSearch: remote.handleSearch,
1139
+ options: remote.withSelected(values),
1140
+ notFoundContent: remote.loading ? /* @__PURE__ */ jsx(Spin, { size: "small" }) : null
1141
+ }
1142
+ );
1143
+ }
1144
+ dayjs.locale("zh-cn");
1145
+ function AppShell({ children }) {
1146
+ return /* @__PURE__ */ jsxs("div", { style: { fontVariantNumeric: "tabular-nums" }, children: [
1147
+ /* @__PURE__ */ jsx("style", { children: `
1148
+ .ant-select-dropdown .ant-select-tree .ant-select-tree-switcher {
1149
+ width: 0;
1150
+ overflow: hidden;
1151
+ }
1152
+ ` }),
1153
+ /* @__PURE__ */ jsx(ConfigProvider, { locale: zhCN, theme: themeConfig, children: /* @__PURE__ */ jsx(App, { children }) })
1154
+ ] });
1155
+ }
1156
+ function EnableTag({ value }) {
1157
+ if (value === "enable") {
1158
+ return /* @__PURE__ */ jsx(Tag, { color: "success", children: "启用" });
1159
+ }
1160
+ if (value === "disable") {
1161
+ return /* @__PURE__ */ jsx(Tag, { color: "default", children: "停用" });
1162
+ }
1163
+ return /* @__PURE__ */ jsx(Tag, { children: value || "-" });
1164
+ }
1165
+ function SuspendedTag({ value }) {
1166
+ return value === "suspended" ? /* @__PURE__ */ jsx(Tag, { color: "error", children: "挂起" }) : /* @__PURE__ */ jsx(Tag, { color: "success", children: "正常" });
1167
+ }
1168
+ function VerifiedTag({ value }) {
1169
+ return value === "verified" ? /* @__PURE__ */ jsx(Tag, { color: "success", children: "已验证" }) : /* @__PURE__ */ jsx(Tag, { color: "warning", children: "未验证" });
1170
+ }
1171
+ const TYPE_META = {
1172
+ platform: { label: "平台", color: "geekblue" },
1173
+ customer: { label: "客户", color: "cyan" }
1174
+ };
1175
+ function TypeTag({ value }) {
1176
+ const meta = TYPE_META[value ?? ""];
1177
+ return meta ? /* @__PURE__ */ jsx(Tag, { color: meta.color, children: meta.label }) : /* @__PURE__ */ jsx(Tag, { children: value || "-" });
1178
+ }
1179
+ const SOURCE_META = {
1180
+ builtin: { label: "内置", color: "gold" },
1181
+ first_party: { label: "第一方", color: "blue" },
1182
+ third_party: { label: "第三方", color: "orange" },
1183
+ custom: { label: "自定义", color: "blue" }
1184
+ };
1185
+ function SourceTag({ value }) {
1186
+ const meta = SOURCE_META[value ?? ""];
1187
+ return meta ? /* @__PURE__ */ jsx(Tag, { color: meta.color, children: meta.label }) : /* @__PURE__ */ jsx(Tag, { children: value || "-" });
1188
+ }
1189
+ export {
1190
+ AppShell as A,
1191
+ themeConfig as B,
1192
+ CODE_COL_WIDTH as C,
1193
+ timeColumn as D,
1194
+ EllipsisCell as E,
1195
+ toMillis as F,
1196
+ tokens as G,
1197
+ IDCell as I,
1198
+ LONG_TEXT_COL_WIDTH as L,
1199
+ MainLayout as M,
1200
+ NAME_COL_WIDTH as N,
1201
+ PageContainer as P,
1202
+ RemoteMultiSelect as R,
1203
+ STATUS_COL_WIDTH as S,
1204
+ TAG_COL_WIDTH as T,
1205
+ VerifiedTag as V,
1206
+ COUNT_COL_WIDTH as a,
1207
+ EnableTag as b,
1208
+ ID_COL_WIDTH as c,
1209
+ InitialPasswordModal as d,
1210
+ LoginPage as e,
1211
+ NameLink as f,
1212
+ ProfileCenter as g,
1213
+ RemoteSelect as h,
1214
+ RowActions as i,
1215
+ SourceTag as j,
1216
+ SuspendedTag as k,
1217
+ TEXT_COL_WIDTH as l,
1218
+ TIME_COL_WIDTH as m,
1219
+ TIME_COL_WIDTH_RELATIVE as n,
1220
+ TenantSwitcher as o,
1221
+ TimeCell as p,
1222
+ TypeTag as q,
1223
+ actionColumn as r,
1224
+ actionColumnWidth as s,
1225
+ brand as t,
1226
+ fmtRelativeTime as u,
1227
+ fmtTime as v,
1228
+ idColumn as w,
1229
+ nameColumn as x,
1230
+ tableScrollX as y,
1231
+ textColumn as z
1232
+ };
1233
+ //# sourceMappingURL=index-DnhMUOxg.js.map