@usethink/cf-admin-fe 0.1.1 → 0.2.8

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.
Files changed (91) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +244 -53
  3. package/dist/composables/createAdminFlagSession.d.ts +42 -0
  4. package/dist/composables/createAdminRequest.d.ts +95 -0
  5. package/dist/composables/createAdminTokenSession.d.ts +49 -0
  6. package/dist/composables/index.d.ts +7 -1
  7. package/dist/composables/useAdminListLoader.d.ts +45 -0
  8. package/dist/composables/useAdminSidebar.d.ts +17 -0
  9. package/dist/composables/useClipboard.d.ts +24 -2
  10. package/dist/composables/useConfirmDialog.d.ts +10 -4
  11. package/dist/composables/useNow.d.ts +3 -0
  12. package/dist/composables/useTablePagination.d.ts +6 -0
  13. package/dist/composables/useTableSelection.d.ts +2 -2
  14. package/dist/composables/useToast.d.ts +1 -1
  15. package/dist/types/index.d.ts +1 -0
  16. package/dist/types/system-config.d.ts +34 -0
  17. package/dist/utils/admin-navigation.d.ts +66 -0
  18. package/dist/utils/admin-query.d.ts +29 -0
  19. package/dist/utils/batch-limit.d.ts +38 -0
  20. package/dist/utils/batch-result.d.ts +41 -0
  21. package/dist/utils/body-scroll-lock.d.ts +13 -0
  22. package/dist/utils/csv-export.d.ts +8 -0
  23. package/dist/utils/currency-cents.d.ts +16 -0
  24. package/dist/utils/datetime.d.ts +20 -0
  25. package/dist/utils/error-message.d.ts +7 -0
  26. package/dist/utils/format-metadata.d.ts +5 -0
  27. package/dist/utils/i18n.d.ts +14 -0
  28. package/dist/utils/index.d.ts +13 -0
  29. package/dist/utils/otp-input.d.ts +11 -0
  30. package/dist/utils/system-config-engine.d.ts +65 -0
  31. package/docs/001_cf-admin-fe/346/217/220/345/217/226/345/267/256/350/267/235/345/210/206/346/236/220/346/212/245/345/221/212_2026-08-05.md +225 -0
  32. package/docs/002_cf-admin-fe/344/275/277/347/224/250/350/257/264/346/230/216/344/271/246/344/270/216/346/263/250/346/204/217/344/272/213/351/241/271.md +805 -0
  33. package/docs/003_cf-admin-fe/346/236/201/350/207/264/345/214/226/346/226/271/346/241/210/344/270/216/345/217/226/350/210/215_2026-08-05.md +332 -0
  34. package/package.json +35 -6
  35. package/recipes/AdminLayout.vue +73 -0
  36. package/recipes/README.md +131 -0
  37. package/recipes/i18n-merge.snippet.ts +29 -0
  38. package/recipes/list-page.snippet.ts +141 -0
  39. package/recipes/main-styles.snippet.ts +13 -0
  40. package/recipes/reexports/components.ts +19 -0
  41. package/recipes/reexports/composables.ts +29 -0
  42. package/recipes/reexports/utils.ts +31 -0
  43. package/recipes/router-guard.snippet.ts +32 -0
  44. package/recipes/useAdminAuth.cookie.ts +29 -0
  45. package/recipes/useAdminAuth.shop.ts +29 -0
  46. package/recipes/useAdminRequest.cookie.ts +36 -0
  47. package/recipes/useAdminRequest.ts +37 -0
  48. package/src/components/AdminLoginShell.vue +31 -0
  49. package/src/components/AdminMetadataDetail.vue +121 -0
  50. package/src/components/AdminShell.vue +384 -0
  51. package/src/components/ConfigField.vue +387 -0
  52. package/src/components/ConfirmDialog.vue +67 -3
  53. package/src/components/index.ts +6 -0
  54. package/src/composables/createAdminFlagSession.ts +129 -0
  55. package/src/composables/createAdminRequest.ts +322 -0
  56. package/src/composables/createAdminTokenSession.ts +149 -0
  57. package/src/composables/index.ts +51 -0
  58. package/src/composables/useAdminBatchOperation.ts +4 -0
  59. package/src/composables/useAdminListLoader.ts +124 -0
  60. package/src/composables/useAdminSidebar.ts +68 -0
  61. package/src/composables/useClipboard.ts +41 -2
  62. package/src/composables/useConfirmDialog.ts +26 -4
  63. package/src/composables/useNow.ts +38 -0
  64. package/src/composables/useTablePagination.ts +6 -0
  65. package/src/composables/useTableSelection.ts +2 -2
  66. package/src/composables/useToast.ts +4 -1
  67. package/src/env.d.ts +2 -2
  68. package/src/i18n/index.ts +8 -0
  69. package/src/i18n/kit-messages.ts +297 -0
  70. package/src/index.ts +147 -4
  71. package/src/styles/admin-primitives.css +28 -1
  72. package/src/styles/index.css +1 -0
  73. package/src/styles/login-primitives.css +149 -0
  74. package/src/types/index.ts +7 -0
  75. package/src/types/system-config.ts +38 -0
  76. package/src/utils/admin-navigation.ts +138 -0
  77. package/src/utils/admin-query.ts +51 -0
  78. package/src/utils/batch-limit.ts +66 -0
  79. package/src/utils/batch-result.ts +81 -0
  80. package/src/utils/body-scroll-lock.ts +63 -0
  81. package/src/utils/csv-export.ts +25 -0
  82. package/src/utils/currency-cents.ts +30 -0
  83. package/src/utils/datetime.ts +54 -0
  84. package/src/utils/error-message.ts +32 -0
  85. package/src/utils/format-metadata.ts +11 -0
  86. package/src/utils/i18n.ts +63 -0
  87. package/src/utils/index.ts +87 -0
  88. package/src/utils/otp-input.ts +18 -0
  89. package/src/utils/system-config-engine.ts +212 -0
  90. package/dist/components/index.d.ts +0 -4
  91. package/dist/index.d.ts +0 -17
@@ -1,4 +1,26 @@
1
- /** 剪贴板工具:一键复制文本到剪贴板,带按钮视觉反馈 */
2
- /** 复制文本,更新按钮文字反馈 */
1
+ /** 剪贴板工具:一键复制;按钮视觉反馈;或成功/失败 toast(管理端列表/详情常用) */
2
+ import { type ToastType } from './useToast';
3
+ /** 写入剪贴板(无 UI 反馈) */
3
4
  export declare function writeClipboardText(text: string): Promise<void>;
5
+ /**
6
+ * 复制到剪贴板,并用全局 toast 提示成功/失败。
7
+ * 与 `copyText`(按钮内 ✅/❌ 反馈)互补:管理端列表/详情更常用 toast。
8
+ *
9
+ * 文案一律由产品传入(i18n),套件不内置默认中英文案。
10
+ *
11
+ * @returns 是否复制成功(调用方可在成功后关弹层等)
12
+ */
13
+ export type CopyWithToastOptions = {
14
+ /** 成功 toast 文案(由产品 i18n 提供) */
15
+ successMessage: string;
16
+ /** 失败 toast 文案(由产品 i18n 提供) */
17
+ errorMessage: string;
18
+ /**
19
+ * 自定义 toast 出口;默认 `useToast().showToast`。
20
+ * 测试或未挂 ToastContainer 的场景可注入。
21
+ */
22
+ showToast?: (message: string, type: ToastType) => void;
23
+ };
24
+ export declare function copyWithToast(text: string, options: CopyWithToastOptions): Promise<boolean>;
25
+ /** 复制文本,更新触发按钮的文字反馈(前台交付明细等) */
4
26
  export declare function copyText(text: string, e: Event): void;
@@ -3,7 +3,7 @@ export type ConfirmOptionDef = {
3
3
  label: string;
4
4
  /** 补充说明,显示在 label 下方 */
5
5
  hint?: string;
6
- /** 默认 false */
6
+ /** 默认是否勾选;默认 false */
7
7
  defaultChecked?: boolean;
8
8
  };
9
9
  export type ConfirmAskOptions = {
@@ -11,6 +11,11 @@ export type ConfirmAskOptions = {
11
11
  danger?: boolean;
12
12
  /** 可选勾选项,默认全部未勾选;打开弹窗时按 defaultChecked 重置 */
13
13
  options?: ConfirmOptionDef[];
14
+ /**
15
+ * 须键入的确认短语(如硬删除的 "DELETE")。
16
+ * 有值时确认按钮在输入完全匹配前禁用;与服务端 confirm 字面量对齐。
17
+ */
18
+ requirePhrase?: string;
14
19
  };
15
20
  export type ConfirmResult = {
16
21
  confirmed: boolean;
@@ -24,19 +29,20 @@ export declare function useConfirmDialog(): {
24
29
  key: string;
25
30
  label: string;
26
31
  hint?: string
27
- /** 默认 false */
32
+ /** 默认是否勾选;默认 false */
28
33
  | undefined;
29
34
  defaultChecked?: boolean | undefined;
30
35
  }[], ConfirmOptionDef[] | {
31
36
  key: string;
32
37
  label: string;
33
38
  hint?: string
34
- /** 默认 false */
39
+ /** 默认是否勾选;默认 false */
35
40
  | undefined;
36
41
  defaultChecked?: boolean | undefined;
37
42
  }[]>;
38
43
  confirmOptionValues: import("vue").Ref<Record<string, boolean>, Record<string, boolean>>;
39
- askConfirm: (message: string) => Promise<boolean>;
44
+ confirmRequirePhrase: import("vue").Ref<string | null, string | null>;
45
+ askConfirm: (message: string, options?: ConfirmAskOptions) => Promise<boolean>;
40
46
  askConfirmWithOptions: (message: string, options?: ConfirmAskOptions) => Promise<ConfirmResult>;
41
47
  onConfirm: () => void;
42
48
  setConfirmOption: (key: string, checked: boolean) => void;
@@ -0,0 +1,3 @@
1
+ export declare function useNow(): {
2
+ nowMs: import("vue").Ref<number, number>;
3
+ };
@@ -1,6 +1,12 @@
1
+ /**
2
+ * 管理端表格 offset 分页状态(page / limit / total)。
3
+ * 列表请求与列定义仍由产品页面负责。
4
+ */
1
5
  import { type Ref, type ComputedRef } from 'vue';
2
6
  export interface UseTablePaginationOptions {
7
+ /** 初始页码;默认 1 */
3
8
  initialPage?: number;
9
+ /** 初始每页条数;默认 20 */
4
10
  initialLimit?: number;
5
11
  }
6
12
  export interface UseTablePaginationReturn {
@@ -3,8 +3,8 @@ export interface UseTableSelectionOptions<T> {
3
3
  isSelectable?: (row: T) => boolean;
4
4
  }
5
5
  /**
6
- * Accepts Ref or ComputedRef of row lists (MaybeRef) so product views can pass
7
- * filtered computed tables without dual-package Ref assignability friction.
6
+ * 接受行列表的 Ref ComputedRefMaybeRef),使产品视图可以直接传入
7
+ * 过滤后的 computed 表格,避免双包 Ref 类型赋值的摩擦。
8
8
  */
9
9
  export declare function useTableSelection<T>(rows: MaybeRef<T[]>, getKey: (row: T) => string, options?: UseTableSelectionOptions<T>): {
10
10
  selectedIds: import("vue").Ref<string[], string[]>;
@@ -4,7 +4,7 @@ export type ToastItem = {
4
4
  message: string;
5
5
  type: ToastType;
6
6
  };
7
- /** Max stacked toasts; older entries drop when exceeded (lottery hardening). */
7
+ /** 最多叠加显示的 toast 数;超过后丢弃较早的条目(抽奖加固)。 */
8
8
  export declare const MAX_TOASTS = 5;
9
9
  declare function showToast(message: string, type?: ToastType, duration?: number): void;
10
10
  export declare function useToast(): {
@@ -0,0 +1 @@
1
+ export type { AdminConfigFieldType, AdminConfigFieldUnit, AdminSystemConfigFieldDefinition, AdminSystemConfigDefinition, ConfigFieldStatus, } from './system-config';
@@ -0,0 +1,34 @@
1
+ /**
2
+ * 供 Admin ConfigField UI 共用的系统配置字段定义。
3
+ * 是 shop + lottery 结构的超集(enum / difficulty 可选)。
4
+ */
5
+ export type AdminConfigFieldType = 'string' | 'boolean' | 'integer';
6
+ export type AdminConfigFieldUnit = 'cents' | 'count';
7
+ export interface AdminSystemConfigFieldDefinition {
8
+ key: string;
9
+ label: string;
10
+ description?: string;
11
+ type: AdminConfigFieldType;
12
+ /** 整数存储单位:cents → 在 UI 中按主货币单位编辑 */
13
+ unit?: AdminConfigFieldUnit;
14
+ sensitive?: boolean;
15
+ configured?: boolean;
16
+ defaultValue?: string;
17
+ min?: number;
18
+ max?: number;
19
+ maxLength?: number;
20
+ effect?: string;
21
+ group?: string;
22
+ order?: number;
23
+ scope?: 'public' | 'admin';
24
+ difficulty?: 'basic' | 'advanced';
25
+ /** 多选枚举(以逗号分隔的字符串存储) */
26
+ enum?: string[];
27
+ enumLabels?: Record<string, string>;
28
+ }
29
+ /**
30
+ * shop/lottery 历史使用的产品别名(`AdminSystemConfigDefinition`)。
31
+ * 在领域类型的再导出中优先使用此名称,便于无缝对齐。
32
+ */
33
+ export type AdminSystemConfigDefinition = AdminSystemConfigFieldDefinition;
34
+ export type ConfigFieldStatus = 'idle' | 'saving' | 'saved' | 'error';
@@ -0,0 +1,66 @@
1
+ /**
2
+ * 供批量脚手架生成的 CF 管理端应用共用的导航辅助函数。
3
+ *
4
+ * 纯 / 可注入 — 不依赖 Vue Router。产品将决策映射到
5
+ * `router.push` / 命名路由,并注入产品特定的会话清理逻辑。
6
+ */
7
+ export type CreateAdminUnauthorizedHandlerOptions = {
8
+ /** 登录路径前缀;默认 `/admin/login` */
9
+ loginPath?: string;
10
+ /** 当前地址为空时,编码进 `?redirect=` 的回退路径 */
11
+ defaultRedirectPath?: string;
12
+ /** 产品侧清除 token / 完整业务会话 */
13
+ clearSession?: () => void;
14
+ /**
15
+ * 覆盖 location(测试用)。默认在可用时使用 `window.location`。
16
+ * 存在 `assign` 时使用它,否则设置 `href`。
17
+ */
18
+ location?: {
19
+ pathname: string;
20
+ search: string;
21
+ hash: string;
22
+ assign?: (url: string) => void;
23
+ href?: string;
24
+ };
25
+ };
26
+ /**
27
+ * 构建 shop + lottery 使用的标准 401 副作用:
28
+ * 清除会话 → 带 `redirect` 查询参数硬跳转到登录页 → 已在登录页则跳过。
29
+ */
30
+ export declare function createAdminUnauthorizedHandler(options?: CreateAdminUnauthorizedHandlerOptions): () => void;
31
+ export type AdminRouteAuthDecision = {
32
+ type: 'allow';
33
+ } | {
34
+ type: 'login';
35
+ redirect: string;
36
+ } | {
37
+ type: 'home';
38
+ };
39
+ export type DecideAdminRouteAccessOptions = {
40
+ hasToken: boolean;
41
+ requiresAuth?: boolean;
42
+ requiresGuest?: boolean;
43
+ /** 登录跳转查询参数使用的当前完整路径(如 `to.fullPath`) */
44
+ fullPath: string;
45
+ };
46
+ /**
47
+ * shop-simple 和 lottery 共用的核心 Bearer 管理员路由守卫(在
48
+ * 产品专属的引导 / 平台 / 模拟身份规则之前执行)。
49
+ */
50
+ export declare function decideAdminRouteAccess(options: DecideAdminRouteAccessOptions): AdminRouteAuthDecision;
51
+ export type ResolveAdminLoginRedirectOptions = {
52
+ /** 登录后的默认落地页;默认 `/admin` */
53
+ defaultPath?: string;
54
+ /**
55
+ * 仅允许此前缀下的跳转(开放重定向防护)。
56
+ * 默认 `/admin`。平台应用可传入 `/platform` 或自定义校验器。
57
+ */
58
+ allowPrefix?: string;
59
+ /** 额外的白名单谓词(如 lottery 平台路径) */
60
+ isAllowed?: (path: string) => boolean;
61
+ };
62
+ /**
63
+ * 从 `route.query.redirect` 解析登录后跳转目标。
64
+ * 拒绝协议相对路径和前缀之外的路径。
65
+ */
66
+ export declare function resolveAdminLoginRedirect(redirectQuery: unknown, options?: ResolveAdminLoginRedirectOptions): string;
@@ -0,0 +1,29 @@
1
+ /**
2
+ * 管理端列表常用的 offset 分页查询字段(**字符串**形态)。
3
+ *
4
+ * 适用:URLSearchParams / query string(如 lottery 平台审计、租户、webhook、兑换码)。
5
+ * 不适用:类型化 filter / JSON body 需要 **number** 的 limit/offset(如 shop 余额/充值/代金券)—
6
+ * 那些场景请产品手写 `offset: (page - 1) * limit`,不要把本函数的 string 结果硬塞进 number 字段。
7
+ *
8
+ * 过滤器键名因产品而异,只抽取这两项稳定字段。
9
+ * 完整对照见 docs/002 §5.8。
10
+ */
11
+ export type AdminOffsetParams = {
12
+ limit: string;
13
+ offset: string;
14
+ };
15
+ /**
16
+ * 由 1-based 页码与每页条数生成 **字符串化** 的 limit/offset(便于 URLSearchParams)。
17
+ * 非法页码回退为 1;非法 limit 回退为 20。
18
+ *
19
+ * @returns `{ limit: string, offset: string }` — 不是 number
20
+ */
21
+ export declare function buildAdminOffsetParams(page: number, limit: number, options?: {
22
+ fallbackLimit?: number;
23
+ }): AdminOffsetParams;
24
+ /**
25
+ * 将 limit/offset 写入已有 URLSearchParams(会覆盖同名键)。
26
+ */
27
+ export declare function applyAdminOffsetParams(params: URLSearchParams, page: number, limit: number, options?: {
28
+ fallbackLimit?: number;
29
+ }): URLSearchParams;
@@ -0,0 +1,38 @@
1
+ /**
2
+ * 在调用服务端批量接口前,守卫管理端的多选数量。
3
+ *
4
+ * 默认上限 {@link DEFAULT_ADMIN_BATCH_LIMIT}(20)对齐 **cf-auth** / docs 风格 API。
5
+ * 后端若为 `z.array(...).max(N)` 且 N≠20(例如 cf-shop 订单/卡密/日志批量 **200**),
6
+ * 必须传入 `options.limit: N`,否则会在未达后端上限前误拦,或(反过来)默认过宽时漏拦。
7
+ *
8
+ * 单位词 `unit` 只影响默认中文提示;完整文案可用 `message` 覆盖。
9
+ */
10
+ export declare const DEFAULT_ADMIN_BATCH_LIMIT = 20;
11
+ export type AdminBatchLimitOk = {
12
+ ok: true;
13
+ count: number;
14
+ limit: number;
15
+ };
16
+ export type AdminBatchLimitExceeded = {
17
+ ok: false;
18
+ count: number;
19
+ limit: number;
20
+ message: string;
21
+ };
22
+ export type AdminBatchLimitCheck = AdminBatchLimitOk | AdminBatchLimitExceeded;
23
+ export type CheckAdminBatchLimitOptions = {
24
+ /** 每次请求的最大条数;默认 {@link DEFAULT_ADMIN_BATCH_LIMIT} */
25
+ limit?: number;
26
+ /**
27
+ * 默认中文消息中的单位词,如 `人` / `条` / `个`。
28
+ * 设置了 `message` 时忽略。
29
+ */
30
+ unit?: string;
31
+ /** 超限时的完整覆盖消息 */
32
+ message?: string;
33
+ };
34
+ /**
35
+ * @param selection - 选中的 id 列表,或预先计算好的数量
36
+ * @param options.limit - 单次最大条数;省略则用 {@link DEFAULT_ADMIN_BATCH_LIMIT}(20)
37
+ */
38
+ export declare function checkAdminBatchLimit(selection: readonly unknown[] | number, options?: CheckAdminBatchLimitOptions): AdminBatchLimitCheck;
@@ -0,0 +1,41 @@
1
+ /**
2
+ * 供管理端批量操作 toast / 汇总文案使用的纯辅助函数。
3
+ *
4
+ * 各产品在 i18n 键和成功文案上存在差异;kit 只负责判定
5
+ * 成功/失败基调,以及可选的 `{success}` / `{failed}` / `{count}` 填充。
6
+ */
7
+ export type AdminBatchCounts = {
8
+ success: number;
9
+ failed: number;
10
+ };
11
+ export type AdminBatchToast = {
12
+ message: string;
13
+ type: 'success' | 'error';
14
+ };
15
+ export declare function hasBatchFailures(result: AdminBatchCounts): boolean;
16
+ export declare function resolveBatchToastType(result: AdminBatchCounts): 'success' | 'error';
17
+ /**
18
+ * 在模板中替换 `{success}`、`{failed}` 和 `{count}`(success 的别名)。
19
+ * 不依赖 vue-i18n — 产品可传入已翻译好的模板。
20
+ */
21
+ export declare function formatBatchCountsMessage(template: string, result: AdminBatchCounts): string;
22
+ /**
23
+ * 从混合/成功模板构建 toast 载荷。
24
+ *
25
+ * @example
26
+ * buildBatchToast(res, {
27
+ * mixed: '完成:成功 {success},失败 {failed}',
28
+ * success: '已处理 {count} 条',
29
+ * })
30
+ */
31
+ export declare function buildBatchToast(result: AdminBatchCounts, templates: {
32
+ /** 当存在任一失败项时 */
33
+ mixed: string;
34
+ /** 当失败数 === 0 时 */
35
+ success: string;
36
+ }): AdminBatchToast;
37
+ /**
38
+ * cf-auth 管理端页面大量使用的默认中文混合汇总文案。
39
+ * 有产品 i18n 时优先使用;这是零依赖的回退方案。
40
+ */
41
+ export declare function formatDefaultBatchMixedMessage(result: AdminBatchCounts, prefix?: string): string;
@@ -0,0 +1,13 @@
1
+ /**
2
+ * 供叠加弹层(确认框 → 弹窗交接)共用的 body 滚动锁定。
3
+ * - 引用计数,保证嵌套层级不会让页面闪烁解锁
4
+ * - 用 padding-right 补偿滚动条宽度,避免布局跳动
5
+ *
6
+ * 仅修改 document.body 的内联样式;无 document 环境(SSR/测试)下为空操作。
7
+ */
8
+ /** 当前模块是否持有 body 滚动锁(测试 / 调试用)。 */
9
+ export declare function isBodyScrollLocked(): boolean;
10
+ /** 仅供测试使用的重置 — 请勿在生产路径中调用。 */
11
+ export declare function __resetBodyScrollLockForTests(): void;
12
+ export declare function lockBodyScroll(): void;
13
+ export declare function unlockBodyScroll(): void;
@@ -0,0 +1,8 @@
1
+ /**
2
+ * CSV 下载,针对电子表格客户端做了公式注入防护。
3
+ * 语义与后端服务端导出 CSV 的产品实现保持一致。
4
+ */
5
+ /** 转义单个单元格:加引号 + 内部双引号加倍;中和开头的公式字符。 */
6
+ export declare function safeCsvCell(value: unknown): string;
7
+ /** 通过临时 object URL 下载 UTF-8 BOM 的 CSV。 */
8
+ export declare function downloadCsv(filename: string, rows: string[][]): void;
@@ -0,0 +1,16 @@
1
+ /**
2
+ * 供管理表单使用的纯最小单位(分)辅助函数。
3
+ * 不依赖产品资金库 — 人民币风格 2 位小数主单位。
4
+ * 使用多币种的产品应保留 `@shared/money`,仅对 ConfigField 风格的整数分字段
5
+ * 重新导出或包装这些函数。
6
+ */
7
+ /** 将未知值强制转换为安全的整数分;否则返回 0。 */
8
+ export declare function normalizeCents(value: unknown): number;
9
+ /** 分 → 保留 2 位小数的主单位字符串(`123` → `"1.23"`)。 */
10
+ export declare function formatCents(cents: number | unknown): string;
11
+ /**
12
+ * 主单位字符串 → 整数分。
13
+ * 空 / 非数字 → `null`,让调用方可以拒绝非法输入
14
+ * (避免意外把 `"50"` 元写成 50 分)。
15
+ */
16
+ export declare function parseYuanToCents(yuan: string): number | null;
@@ -0,0 +1,20 @@
1
+ /**
2
+ * 供 CF 产品管理员共用的日期时间辅助函数。
3
+ * 纯函数 — 不依赖 Vue,不含领域标签。
4
+ */
5
+ /**
6
+ * ISO 字符串 / Date → 本地 `YYYY-MM-DD HH:mm:ss`;空值 → `-`。
7
+ * 接受 `Date` 对象,使 cookie/JSON 管理表格无需产品包装层(cf-auth)。
8
+ */
9
+ export declare function formatDate(value?: string | Date | null): string;
10
+ /** 后端时间 → `datetime-local` 输入值(本地挂钟时间,不含秒) */
11
+ export declare function toDateTimeLocalValue(value?: string | null): string;
12
+ /** `datetime-local` 值 → ISO 字符串(跨浏览器稳定) */
13
+ export declare function dateTimeLocalToIso(value?: string | null): string;
14
+ /** {@link toDateTimeLocalValue} 的别名,用于编辑表单回填 */
15
+ export declare function isoToDateTimeLocal(iso?: string | null): string;
16
+ /**
17
+ * 对长不透明字符串(如哈希后的客户端 IP)生成短指纹。
18
+ * 完整值保留用于详情/导出;列表展示缩写形式。
19
+ */
20
+ export declare function formatIpFingerprint(value?: string | null, maxPlain?: number): string;
@@ -0,0 +1,7 @@
1
+ /**
2
+ * 将未知的 catch 值规范化为管理端 toast / loading 展示用的字符串。
3
+ *
4
+ * 供 cf-shop / cf-lottery / cf-auth 管理端 UI 共用,它们反复编写:
5
+ * `err instanceof Error ? err.message : fallback`。
6
+ */
7
+ export declare function toErrorMessage(error: unknown, fallback?: string): string;
@@ -0,0 +1,5 @@
1
+ /**
2
+ * 格式化元数据为可读的 JSON 字符串。
3
+ * 用于日志详情等场景展示结构化数据。
4
+ */
5
+ export declare function formatMetadata(metadata: Record<string, unknown>): string;
@@ -0,0 +1,14 @@
1
+ export type AdminI18nProxy = {
2
+ $t?: (key: string, params?: Record<string, unknown>) => unknown;
3
+ $te?: (key: string) => boolean;
4
+ } | null | undefined;
5
+ /**
6
+ * 纯函数:可在单测中直接注入 proxy,不依赖 setup。
7
+ */
8
+ export declare function resolveAdminT(proxy: AdminI18nProxy, key: string, fallback: string, params?: Record<string, unknown>): string;
9
+ /**
10
+ * 从当前组件实例的 appContext.globalProperties 读取 `$t` / `$te`。
11
+ * 使用 globalProperties 而非 `proxy.$t`,避免未安装 vue-i18n 时访问实例代理触发 Vue warn。
12
+ * 勿在模块顶层调用。
13
+ */
14
+ export declare function tKit(key: string, fallback: string, params?: Record<string, unknown>): string;
@@ -0,0 +1,13 @@
1
+ export { formatDate, toDateTimeLocalValue, dateTimeLocalToIso, isoToDateTimeLocal, formatIpFingerprint, } from './datetime';
2
+ export { downloadCsv, safeCsvCell } from './csv-export';
3
+ export { lockBodyScroll, unlockBodyScroll, isBodyScrollLocked, __resetBodyScrollLockForTests, } from './body-scroll-lock';
4
+ export { normalizeCents, formatCents, parseYuanToCents } from './currency-cents';
5
+ export { createAdminUnauthorizedHandler, decideAdminRouteAccess, resolveAdminLoginRedirect, type CreateAdminUnauthorizedHandlerOptions, type AdminRouteAuthDecision, type DecideAdminRouteAccessOptions, type ResolveAdminLoginRedirectOptions, } from './admin-navigation';
6
+ export { groupDefinitionsByName, countSectionItems, buildActiveGroupedDefinitions, countAdvancedItems, resolveSystemConfigSectionId, getSystemConfigSection, listedSystemConfigGroups, auditSystemConfigSectionCoverage, type SystemConfigDefinitionLike, type SystemConfigSectionLike, type SystemConfigGroupedBlock, type BuildActiveGroupedOptions, type SystemConfigCoverageAudit, } from './system-config-engine';
7
+ export { normalizeOtpDigits, isOtpComplete, OTP_CODE_LENGTH, } from './otp-input';
8
+ export { toErrorMessage } from './error-message';
9
+ export { hasBatchFailures, resolveBatchToastType, formatBatchCountsMessage, buildBatchToast, formatDefaultBatchMixedMessage, type AdminBatchCounts, type AdminBatchToast, } from './batch-result';
10
+ export { DEFAULT_ADMIN_BATCH_LIMIT, checkAdminBatchLimit, type AdminBatchLimitOk, type AdminBatchLimitExceeded, type AdminBatchLimitCheck, type CheckAdminBatchLimitOptions, } from './batch-limit';
11
+ export { formatMetadata, } from './format-metadata';
12
+ export { buildAdminOffsetParams, applyAdminOffsetParams, type AdminOffsetParams, } from './admin-query';
13
+ export { resolveAdminT, tKit, type AdminI18nProxy, } from './i18n';
@@ -0,0 +1,11 @@
1
+ /**
2
+ * OTP 输入规范化(纯函数)。
3
+ *
4
+ * - 只保留数字,最长 6 位
5
+ * - 便于 SMS/邮件自动填充后触发「满 6 位自动提交」
6
+ */
7
+ export declare const OTP_CODE_LENGTH = 6;
8
+ /** 去掉非数字并截断到 OTP 长度 */
9
+ export declare function normalizeOtpDigits(raw: string, maxLen?: number): string;
10
+ /** 是否已满位、可触发自动提交 */
11
+ export declare function isOtpComplete(digits: string, maxLen?: number): boolean;
@@ -0,0 +1,65 @@
1
+ /**
2
+ * shop/lottery AdminSystemConfigView 共用的纯系统配置页面引擎。
3
+ *
4
+ * 产品负责:分区 id 联合、分组名列表、危险区 UI、API 加载/保存。
5
+ * 套件负责:分组、分区计数、高级筛选、tab 解析、覆盖率审计。
6
+ */
7
+ /** 引擎使用的最小定义结构(ConfigField 项满足此结构)。 */
8
+ export type SystemConfigDefinitionLike = {
9
+ key: string;
10
+ group?: string;
11
+ order?: number;
12
+ difficulty?: 'basic' | 'advanced' | string;
13
+ };
14
+ /** 最小分区结构 — 产品负责填充标签/描述。 */
15
+ export type SystemConfigSectionLike = {
16
+ id: string;
17
+ groups: readonly string[];
18
+ includeDangerZone?: boolean;
19
+ };
20
+ export type SystemConfigGroupedBlock<T> = {
21
+ name: string;
22
+ items: T[];
23
+ };
24
+ /**
25
+ * 按 `group` 对定义分组(为空时使用回退名称),并按 `order` 对每组排序。
26
+ */
27
+ export declare function groupDefinitionsByName<T extends SystemConfigDefinitionLike>(definitions: readonly T[], fallbackGroupName?: string): Map<string, T[]>;
28
+ /**
29
+ * 按分区 id 统计定义项数量(所列分组长度之和)。
30
+ */
31
+ export declare function countSectionItems(groupsByName: Map<string, readonly unknown[]>, sections: readonly SystemConfigSectionLike[]): Record<string, number>;
32
+ export type BuildActiveGroupedOptions = {
33
+ /**
34
+ * 为 false 时,隐藏 `difficulty === 'advanced'` 的项(lottery 基础模式)。
35
+ * 默认 true = 显示全部(shop)。
36
+ */
37
+ showAdvanced?: boolean;
38
+ };
39
+ /**
40
+ * 为当前激活分区构建有序分组块;丢弃空分组。
41
+ */
42
+ export declare function buildActiveGroupedDefinitions<T extends SystemConfigDefinitionLike>(sectionGroups: readonly string[], groupsByName: Map<string, readonly T[]>, options?: BuildActiveGroupedOptions): SystemConfigGroupedBlock<T>[];
43
+ /** 统计给定分区分组中高级难度的项数。 */
44
+ export declare function countAdvancedItems<T extends SystemConfigDefinitionLike>(sectionGroups: readonly string[], groupsByName: Map<string, readonly T[]>): number;
45
+ /**
46
+ * 将 `?tab=` 解析为已知分区 id;无效/为空 → defaultId(绝不凭空编造 id)。
47
+ */
48
+ export declare function resolveSystemConfigSectionId<T extends string>(raw: unknown, validIds: readonly T[], defaultId: T): T;
49
+ export declare function getSystemConfigSection<T extends SystemConfigSectionLike>(sections: readonly T[], id: string): T;
50
+ /** 按声明顺序展平 section.groups,并去重。 */
51
+ export declare function listedSystemConfigGroups(sections: readonly SystemConfigSectionLike[]): string[];
52
+ export type SystemConfigCoverageAudit = {
53
+ ok: boolean;
54
+ unmappedGroups: string[];
55
+ unmappedKeys: string[];
56
+ duplicateGroups: string[];
57
+ extraGroupsInMap: string[];
58
+ sectionKeyCounts: Record<string, number>;
59
+ totalKeys: number;
60
+ };
61
+ /**
62
+ * 校验分区映射 ↔ 定义分组覆盖情况(单元/契约测试)。
63
+ * `fallbackGroupName` 必须与 UI 对空 `group` 字段的分组方式一致。
64
+ */
65
+ export declare function auditSystemConfigSectionCoverage(definitions: readonly SystemConfigDefinitionLike[], sections: readonly SystemConfigSectionLike[], fallbackGroupName?: string): SystemConfigCoverageAudit;