@pactor-app/skills 1.33.0 → 1.35.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.
Files changed (44) hide show
  1. package/package.json +1 -1
  2. package/pactor-prd/SKILL.md +20 -23
  3. package/pactor-prd/references/acceptance-checklist.md +1 -1
  4. package/pactor-prd/references/data-api.md +35 -0
  5. package/pactor-prd/references/incremental-wireframe-delta.md +8 -9
  6. package/pactor-prd/references/page-inventory-and-files.md +2 -2
  7. package/pactor-prd/references/page-templates.md +59 -12
  8. package/pactor-prd/references/project-structure.md +1 -1
  9. package/pactor-prd/template/README.md +4 -4
  10. package/pactor-prd/template/app/api/d/[...parts]/route.ts +80 -0
  11. package/pactor-prd/template/app/layout.tsx +1 -3
  12. package/pactor-prd/template/next-env.d.ts +2 -2
  13. package/pactor-prd/template/next.config.js +2 -2
  14. package/pactor-prd/template/package.json +4 -4
  15. package/pactor-prd/template/pnpm-lock.yaml +97 -97
  16. package/pactor-prd/template/src/pactor/App.tsx +3 -2
  17. package/pactor-prd/template/src/pactor/actions.ts +5 -431
  18. package/pactor-prd/template/src/pactor/config.ts +12 -4
  19. package/pactor-prd/template/src/server/yaml/common/dashboard.yaml +1 -1
  20. package/pactor-prd/template/src/server/yaml/common/login.yaml +46 -75
  21. package/pactor-prd/template/src/server/yaml/models/data-list.yaml +344 -0
  22. package/pactor-prd/template/src/server/yaml/provider/provider-detail.yaml +2 -2
  23. package/pactor-prd/template/src/server/yaml/provider/provider-edit.yaml +1 -1
  24. package/pactor-prd/template/src/server/yaml/provider/provider-list.yaml +1 -1
  25. package/pactor-prd/template/src/server/yaml/route/route-detail.yaml +1 -1
  26. package/pactor-prd/template/src/server/yaml/route/route-edit.yaml +1 -1
  27. package/pactor-prd/template/src/server/yaml/route/route-list.yaml +1 -1
  28. package/pactor-prd/template/src/server/yaml/sitemap.yaml +23 -6
  29. package/pactor-prd/references/mock-data-routes.md +0 -66
  30. package/pactor-prd/template/app/api/data/[...parts]/route.ts +0 -118
  31. package/pactor-prd/template/app/login.css +0 -122
  32. package/pactor-prd/template/src/pactor/mocks/audit-mock.ts +0 -403
  33. package/pactor-prd/template/src/pactor/mocks/call-logs-mock.ts +0 -875
  34. package/pactor-prd/template/src/pactor/mocks/client-debug-mock.ts +0 -188
  35. package/pactor-prd/template/src/pactor/mocks/client-templates-mock.ts +0 -497
  36. package/pactor-prd/template/src/pactor/mocks/clients-mock.ts +0 -483
  37. package/pactor-prd/template/src/pactor/mocks/costs-mock.ts +0 -406
  38. package/pactor-prd/template/src/pactor/mocks/dashboard-mock.ts +0 -192
  39. package/pactor-prd/template/src/pactor/mocks/model-debug-mock.ts +0 -144
  40. package/pactor-prd/template/src/pactor/mocks/models-mock.ts +0 -799
  41. package/pactor-prd/template/src/pactor/mocks/ops-usage-mock.ts +0 -806
  42. package/pactor-prd/template/src/pactor/mocks/providers-mock.ts +0 -424
  43. package/pactor-prd/template/src/pactor/mocks/routes-mock.ts +0 -604
  44. package/pactor-prd/template/src/pactor/services.ts +0 -185
@@ -1,604 +0,0 @@
1
- /**
2
- * Route(模型路由)演示数据服务(MVP mock,静态页面)。
3
- * route.list / route.detail 注册为 Pactor service;接入真实后端后替换为
4
- * http / service 数据源,DSL 绑定路径保持不变。
5
- * 数据形态对齐 PRD《01-路由列表页》《02-路由详情页》《03-路由新增编辑页》(§8.1、§8.2)。
6
- * 模型引用与 models-mock 的 routeRefs 互相印证(reasoning-default / general-chat / coding-assistant)。
7
- */
8
-
9
- export type RouteStatus = "enabled" | "disabled" | "draft";
10
- export type RouteType = "single" | "pool" | "conditional";
11
- export type RouteStrategy =
12
- | "WEIGHTED"
13
- | "HASH"
14
- | "PRIORITY"
15
- | "ROUND_ROBIN"
16
- | "RANDOM"
17
- | "FIXED";
18
-
19
- /** 路由下的模型行(详情/编辑「模型与路由策略」表) */
20
- export interface RouteModelEntry {
21
- modelCode: string;
22
- modelName: string;
23
- /** 主供应商摘要,如 `阿里云百炼 · qwen-vl-plus-latest` */
24
- providerSummary: string;
25
- weight: number;
26
- priority: "P0" | "P1" | "P2";
27
- /** 供应成本倍率 C;null = 留空继承 Model「提供商」配置 */
28
- costRate: number | null;
29
- /** 用户计费倍率 M;null = 留空继承 */
30
- billingRate: number | null;
31
- status: "enabled" | "disabled";
32
- }
33
-
34
- export interface RouteRecord {
35
- code: string;
36
- name: string;
37
- type: RouteType;
38
- typeLabel: string;
39
- /** 逻辑模型 Alias(对外暴露的模型名);空串 = 未设置 */
40
- alias: string;
41
- tags: string[];
42
- description: string;
43
- status: RouteStatus;
44
- statusLabel: string;
45
- /** 请求路径,如 /v1/chat/completions */
46
- path: string;
47
- /** 协议格式(Ingress Protocol),如 OpenAI / Chat Completions */
48
- protocol: string;
49
- /** Ingress Operation,随协议格式带出,如 chat.completions */
50
- operation: string;
51
- strategy: RouteStrategy;
52
- strategyLabel: string;
53
- /** 粘性 Session:同会话固定到同一模型实例 */
54
- stickySession: boolean;
55
- /** 健康过滤:仅调度 HEALTHY 实例(默认开) */
56
- healthFilter: boolean;
57
- models: RouteModelEntry[];
58
- createdAt: string;
59
- updatedAt: string;
60
- operator: string;
61
- // ── 预计算摘要(表达式不适合拼接复合文案) ──
62
- /** 入口摘要:`/v1/chat/completions · OpenAI / Chat Completions` */
63
- entrySummary: string;
64
- /** 策略摘要:`3 模型 · 加权轮询` */
65
- policySummary: string;
66
- /** 策略完整文案:`加权轮询 (WEIGHTED)` */
67
- strategySummary: string;
68
- tagsText: string;
69
- /** 启用模型行数 */
70
- enabledModelCount: number;
71
- /** 启用项权重合计 */
72
- weightTotal: number;
73
- /** 最近更新短日期(MM-DD) */
74
- shortDate: string;
75
- }
76
-
77
- export interface RouteListParams {
78
- /** all | enabled | disabled | draft */
79
- status?: string;
80
- /** all | single | pool | conditional */
81
- type?: string;
82
- /** 标签多选 */
83
- tags?: string[];
84
- }
85
-
86
- /** 快筛 Chip 计数(数据构成口径,不随筛选变化) */
87
- export interface RouteCounts {
88
- all: { enabled: number; total: number };
89
- status: Record<RouteStatus, number>;
90
- types: Record<RouteType, { enabled: number; total: number }>;
91
- /** 键为标签的表达式安全形式(连字符转下划线,如 low-cost → low_cost) */
92
- tags: Record<string, number>;
93
- }
94
-
95
- /** 快筛 Chip 选项(ChipFilter 组件入项;文案在接口侧预计算) */
96
- export interface RouteChipOption {
97
- value: string;
98
- text: string;
99
- dot?: string;
100
- }
101
-
102
- export interface RouteListResult {
103
- items: RouteRecord[];
104
- total: number;
105
- counts: RouteCounts;
106
- /** 快筛 Chip 组:status / type 单选(含「全部」),tags 多选(含「全部」,点击清空) */
107
- filters: {
108
- status: RouteChipOption[];
109
- type: RouteChipOption[];
110
- tags: RouteChipOption[];
111
- };
112
- }
113
-
114
- export const TYPE_LABELS: Record<RouteType, string> = {
115
- single: "单模型",
116
- pool: "多模型池",
117
- conditional: "条件路由",
118
- };
119
-
120
- export const STATUS_LABELS: Record<RouteStatus, string> = {
121
- enabled: "启用",
122
- disabled: "停用",
123
- draft: "草稿",
124
- };
125
-
126
- export const STRATEGY_LABELS: Record<RouteStrategy, string> = {
127
- WEIGHTED: "加权轮询",
128
- HASH: "哈希分配",
129
- PRIORITY: "优先级",
130
- ROUND_ROBIN: "轮询",
131
- RANDOM: "随机",
132
- FIXED: "固定",
133
- };
134
-
135
- /** 协议格式 → Ingress Operation */
136
- const PROTOCOL_OPERATIONS: Record<string, string> = {
137
- "OpenAI / Chat Completions": "chat.completions",
138
- "OpenAI / Responses": "responses",
139
- "Anthropic / Messages": "messages",
140
- "OpenAI / Embeddings": "embeddings",
141
- };
142
-
143
- /** 支持的协议格式(编辑页下拉,对齐 PRD《02》MVP 清单) */
144
- export const PROTOCOL_OPTIONS = Object.keys(PROTOCOL_OPERATIONS).map((p) => ({
145
- label: p,
146
- value: p,
147
- }));
148
-
149
- export function protocolOperation(protocol: string): string {
150
- return PROTOCOL_OPERATIONS[protocol] ?? "—";
151
- }
152
-
153
- /** 模型路由策略选项(编辑页下拉;单模型固定 FIXED) */
154
- export const STRATEGY_OPTIONS = (
155
- Object.keys(STRATEGY_LABELS) as RouteStrategy[]
156
- ).map((s) => ({ label: `${STRATEGY_LABELS[s]} (${s})`, value: s }));
157
-
158
- function joinList(items: string[]): string {
159
- return items.join(" · ");
160
- }
161
-
162
- /** 列表卡/详情页摘要字段派生 */
163
- function derive(
164
- record: Omit<
165
- RouteRecord,
166
- | "typeLabel"
167
- | "statusLabel"
168
- | "entrySummary"
169
- | "policySummary"
170
- | "strategyLabel"
171
- | "strategySummary"
172
- | "tagsText"
173
- | "enabledModelCount"
174
- | "weightTotal"
175
- | "shortDate"
176
- >,
177
- ): RouteRecord {
178
- const enabledModels = record.models.filter((m) => m.status === "enabled");
179
- return {
180
- ...record,
181
- typeLabel: TYPE_LABELS[record.type],
182
- statusLabel: STATUS_LABELS[record.status],
183
- entrySummary: `${record.path} · ${record.protocol}`,
184
- policySummary: `${record.models.length} 模型 · ${STRATEGY_LABELS[record.strategy]}`,
185
- strategyLabel: STRATEGY_LABELS[record.strategy],
186
- strategySummary: `${STRATEGY_LABELS[record.strategy]} (${record.strategy})`,
187
- tagsText: joinList(record.tags),
188
- enabledModelCount: enabledModels.length,
189
- weightTotal: enabledModels.reduce((sum, m) => sum + m.weight, 0),
190
- shortDate: record.updatedAt.slice(5, 10),
191
- };
192
- }
193
-
194
- const ROUTES: RouteRecord[] = [
195
- derive({
196
- code: "reasoning-default",
197
- name: "推理模型默认路由",
198
- type: "pool",
199
- alias: "reasoning",
200
- tags: ["reasoning", "premium"],
201
- description: "默认推理链路:旗舰视觉模型为主,GPT-5 兜底。",
202
- status: "enabled",
203
- path: "/v1/chat/completions",
204
- protocol: "OpenAI / Chat Completions",
205
- operation: "chat.completions",
206
- strategy: "WEIGHTED",
207
- stickySession: false,
208
- healthFilter: true,
209
- models: [
210
- {
211
- modelCode: "qwen3-vl-plus",
212
- modelName: "Qwen3-VL Plus",
213
- providerSummary: "阿里云百炼 · qwen-vl-plus-latest",
214
- weight: 60,
215
- priority: "P0",
216
- costRate: 1.0,
217
- billingRate: 1.2,
218
- status: "enabled",
219
- },
220
- {
221
- modelCode: "gpt-5",
222
- modelName: "GPT-5",
223
- providerSummary: "OpenAI · gpt-5",
224
- weight: 40,
225
- priority: "P0",
226
- costRate: null,
227
- billingRate: null,
228
- status: "enabled",
229
- },
230
- ],
231
- createdAt: "2026-06-01 10:00",
232
- updatedAt: "2026-09-10 09:30",
233
- operator: "张三",
234
- }),
235
- derive({
236
- code: "general-chat",
237
- name: "通用对话路由",
238
- type: "single",
239
- alias: "general",
240
- tags: ["general"],
241
- description: "高频通用对话入口,固定指向轻量模型。",
242
- status: "enabled",
243
- path: "/v1/chat/completions",
244
- protocol: "OpenAI / Chat Completions",
245
- operation: "chat.completions",
246
- strategy: "FIXED",
247
- stickySession: false,
248
- healthFilter: true,
249
- models: [
250
- {
251
- modelCode: "qwen3.5-flash",
252
- modelName: "Qwen3.5 Flash",
253
- providerSummary: "阿里云百炼 · qwen3.5-flash",
254
- weight: 100,
255
- priority: "P0",
256
- costRate: null,
257
- billingRate: null,
258
- status: "enabled",
259
- },
260
- ],
261
- createdAt: "2026-06-10 15:00",
262
- updatedAt: "2026-09-08 11:05",
263
- operator: "李四",
264
- }),
265
- derive({
266
- code: "coding-assistant",
267
- name: "代码助手路由",
268
- type: "single",
269
- alias: "coding",
270
- tags: ["coding"],
271
- description: "编程助手专用入口,固定指向 GPT-5。",
272
- status: "enabled",
273
- path: "/v1/chat/completions",
274
- protocol: "OpenAI / Chat Completions",
275
- operation: "chat.completions",
276
- strategy: "FIXED",
277
- stickySession: true,
278
- healthFilter: true,
279
- models: [
280
- {
281
- modelCode: "gpt-5",
282
- modelName: "GPT-5",
283
- providerSummary: "OpenAI · gpt-5",
284
- weight: 100,
285
- priority: "P0",
286
- costRate: 1.0,
287
- billingRate: 1.0,
288
- status: "enabled",
289
- },
290
- ],
291
- createdAt: "2026-07-01 09:00",
292
- updatedAt: "2026-09-05 17:42",
293
- operator: "王五",
294
- }),
295
- derive({
296
- code: "mobile-lite",
297
- name: "移动端轻量路由",
298
- type: "pool",
299
- alias: "chat-lite",
300
- tags: ["low-cost", "mobile"],
301
- description: "移动端低延迟场景:轻量模型轮询调度。",
302
- status: "enabled",
303
- path: "/v1/chat/completions",
304
- protocol: "OpenAI / Chat Completions",
305
- operation: "chat.completions",
306
- strategy: "ROUND_ROBIN",
307
- stickySession: false,
308
- healthFilter: true,
309
- models: [
310
- {
311
- modelCode: "qwen3.5-flash",
312
- modelName: "Qwen3.5 Flash",
313
- providerSummary: "阿里云百炼 · qwen3.5-flash",
314
- weight: 50,
315
- priority: "P0",
316
- costRate: null,
317
- billingRate: null,
318
- status: "enabled",
319
- },
320
- {
321
- modelCode: "gemini-3-flash",
322
- modelName: "Gemini 3 Flash",
323
- providerSummary: "Google Gemini · gemini-3-flash",
324
- weight: 50,
325
- priority: "P1",
326
- costRate: null,
327
- billingRate: null,
328
- status: "enabled",
329
- },
330
- ],
331
- createdAt: "2026-07-15 14:00",
332
- updatedAt: "2026-09-02 08:20",
333
- operator: "李四",
334
- }),
335
- derive({
336
- code: "vision-router",
337
- name: "视觉模型条件路由",
338
- type: "conditional",
339
- alias: "vision",
340
- tags: ["vision"],
341
- description: "按输入模态与规则表达式命中不同视觉模型(草稿,规则待完善)。",
342
- status: "draft",
343
- path: "/v1/chat/completions",
344
- protocol: "Anthropic / Messages",
345
- operation: "messages",
346
- strategy: "HASH",
347
- stickySession: true,
348
- healthFilter: true,
349
- models: [
350
- {
351
- modelCode: "qwen3-vl-plus",
352
- modelName: "Qwen3-VL Plus",
353
- providerSummary: "阿里云百炼 · qwen-vl-plus-latest",
354
- weight: 50,
355
- priority: "P0",
356
- costRate: null,
357
- billingRate: null,
358
- status: "enabled",
359
- },
360
- {
361
- modelCode: "gemini-3-flash",
362
- modelName: "Gemini 3 Flash",
363
- providerSummary: "Google Gemini · gemini-3-flash",
364
- weight: 50,
365
- priority: "P1",
366
- costRate: null,
367
- billingRate: null,
368
- status: "disabled",
369
- },
370
- ],
371
- createdAt: "2026-08-20 11:30",
372
- updatedAt: "2026-09-11 16:00",
373
- operator: "张三",
374
- }),
375
- derive({
376
- code: "embeddings-std",
377
- name: "标准向量路由",
378
- type: "single",
379
- alias: "embedding",
380
- tags: ["embedding"],
381
- description: "检索与聚类场景的统一 Embedding 入口。",
382
- status: "enabled",
383
- path: "/v1/embeddings",
384
- protocol: "OpenAI / Embeddings",
385
- operation: "embeddings",
386
- strategy: "FIXED",
387
- stickySession: false,
388
- healthFilter: true,
389
- models: [
390
- {
391
- modelCode: "text-embedding-3-large",
392
- modelName: "Text Embedding 3 Large",
393
- providerSummary: "OpenAI · text-embedding-3-large",
394
- weight: 100,
395
- priority: "P0",
396
- costRate: null,
397
- billingRate: null,
398
- status: "enabled",
399
- },
400
- ],
401
- createdAt: "2026-06-20 10:30",
402
- updatedAt: "2026-09-01 10:00",
403
- operator: "王五",
404
- }),
405
- derive({
406
- code: "long-context",
407
- name: "长上下文路由",
408
- type: "pool",
409
- alias: "",
410
- tags: ["reasoning"],
411
- description: "百万级长文档处理:按优先级带调度(当前停用整改中)。",
412
- status: "disabled",
413
- path: "/v1/chat/completions",
414
- protocol: "OpenAI / Responses",
415
- operation: "responses",
416
- strategy: "PRIORITY",
417
- stickySession: true,
418
- healthFilter: true,
419
- models: [
420
- {
421
- modelCode: "qwen3.5-flash",
422
- modelName: "Qwen3.5 Flash",
423
- providerSummary: "阿里云百炼 · qwen3.5-flash",
424
- weight: 60,
425
- priority: "P0",
426
- costRate: null,
427
- billingRate: null,
428
- status: "enabled",
429
- },
430
- {
431
- modelCode: "qwen3-vl-plus",
432
- modelName: "Qwen3-VL Plus",
433
- providerSummary: "阿里云百炼 · qwen-vl-plus-latest",
434
- weight: 30,
435
- priority: "P1",
436
- costRate: null,
437
- billingRate: null,
438
- status: "enabled",
439
- },
440
- {
441
- modelCode: "gemini-3-flash",
442
- modelName: "Gemini 3 Flash",
443
- providerSummary: "Google Gemini · gemini-3-flash",
444
- weight: 10,
445
- priority: "P2",
446
- costRate: null,
447
- billingRate: null,
448
- status: "disabled",
449
- },
450
- ],
451
- createdAt: "2026-07-28 09:00",
452
- updatedAt: "2026-08-30 22:15",
453
- operator: "张三",
454
- }),
455
- derive({
456
- code: "failover-chat",
457
- name: "通用兜底路由",
458
- type: "pool",
459
- alias: "",
460
- tags: ["general", "low-cost"],
461
- description: "主路异常时的兜底链路:加权调度两个轻量模型(当前停用)。",
462
- status: "disabled",
463
- path: "/v1/chat/completions",
464
- protocol: "OpenAI / Chat Completions",
465
- operation: "chat.completions",
466
- strategy: "WEIGHTED",
467
- stickySession: false,
468
- healthFilter: true,
469
- models: [
470
- {
471
- modelCode: "qwen3.5-flash",
472
- modelName: "Qwen3.5 Flash",
473
- providerSummary: "阿里云百炼 · qwen3.5-flash",
474
- weight: 70,
475
- priority: "P0",
476
- costRate: null,
477
- billingRate: null,
478
- status: "enabled",
479
- },
480
- {
481
- modelCode: "gemini-3-flash",
482
- modelName: "Gemini 3 Flash",
483
- providerSummary: "Google Gemini · gemini-3-flash",
484
- weight: 30,
485
- priority: "P1",
486
- costRate: null,
487
- billingRate: null,
488
- status: "enabled",
489
- },
490
- ],
491
- createdAt: "2026-08-01 16:00",
492
- updatedAt: "2026-08-25 19:30",
493
- operator: "李四",
494
- }),
495
- ];
496
-
497
- /** 快筛选项常量:状态/类型(单选)与标签(多选) */
498
- export const ROUTE_STATUS_OPTIONS: { value: string; label: string }[] = [
499
- { value: "all", label: "全部" },
500
- { value: "enabled", label: "已启用" },
501
- { value: "disabled", label: "已停用" },
502
- { value: "draft", label: "草稿" },
503
- ];
504
-
505
- export const ROUTE_TYPE_OPTIONS: { value: string; label: string }[] = [
506
- { value: "all", label: "全部" },
507
- { value: "single", label: "单模型" },
508
- { value: "pool", label: "多模型池" },
509
- { value: "conditional", label: "条件路由" },
510
- ];
511
-
512
- /** 标签快筛(按数据中出现顺序展示;多选取交集) */
513
- export const ROUTE_TAG_FILTERS: { value: string; label: string }[] = [
514
- ...new Set(ROUTES.flatMap((r) => r.tags)),
515
- ].map((t) => ({ value: t, label: t }));
516
-
517
- /** 快筛计数:基于全量数据(构成口径,与当前筛选无关) */
518
- function computeCounts(): RouteCounts {
519
- return {
520
- all: {
521
- enabled: ROUTES.filter((r) => r.status === "enabled").length,
522
- total: ROUTES.length,
523
- },
524
- status: {
525
- enabled: ROUTES.filter((r) => r.status === "enabled").length,
526
- disabled: ROUTES.filter((r) => r.status === "disabled").length,
527
- draft: ROUTES.filter((r) => r.status === "draft").length,
528
- },
529
- types: {
530
- single: {
531
- enabled: ROUTES.filter(
532
- (r) => r.type === "single" && r.status === "enabled",
533
- ).length,
534
- total: ROUTES.filter((r) => r.type === "single").length,
535
- },
536
- pool: {
537
- enabled: ROUTES.filter(
538
- (r) => r.type === "pool" && r.status === "enabled",
539
- ).length,
540
- total: ROUTES.filter((r) => r.type === "pool").length,
541
- },
542
- conditional: {
543
- enabled: ROUTES.filter(
544
- (r) => r.type === "conditional" && r.status === "enabled",
545
- ).length,
546
- total: ROUTES.filter((r) => r.type === "conditional").length,
547
- },
548
- },
549
- tags: ROUTE_TAG_FILTERS.reduce<Record<string, number>>((acc, t) => {
550
- const key = t.value.replace(/-/g, "_");
551
- acc[key] = ROUTES.filter((r) => r.tags.includes(t.value)).length;
552
- return acc;
553
- }, {}),
554
- };
555
- }
556
-
557
- /** 快筛 Chip 选项:由计数派生,文案在此预计算(表达式不支持复合文案拼接) */
558
- function computeFilters(counts: RouteCounts): RouteListResult["filters"] {
559
- return {
560
- status: [
561
- { value: "all", text: `全部 ${counts.all.enabled}/${counts.all.total}` },
562
- { value: "enabled", text: `已启用 ${counts.status.enabled}` },
563
- { value: "disabled", text: `已停用 ${counts.status.disabled}` },
564
- { value: "draft", text: `草稿 ${counts.status.draft}` },
565
- ],
566
- type: [
567
- { value: "all", text: `全部 ${counts.all.enabled}/${counts.all.total}` },
568
- ...ROUTE_TYPE_OPTIONS.filter((o) => o.value !== "all").map((o) => ({
569
- value: o.value,
570
- text: `${o.label} ${counts.types[o.value as RouteType].enabled}/${counts.types[o.value as RouteType].total}`,
571
- })),
572
- ],
573
- tags: [
574
- { value: "all", text: `全部 ${counts.all.total}` },
575
- ...ROUTE_TAG_FILTERS.map((t) => ({
576
- value: t.value,
577
- text: `${t.label} ${counts.tags[t.value.replace(/-/g, "_")]}`,
578
- })),
579
- ],
580
- };
581
- }
582
-
583
- /** route.list:状态/类型单选 + 标签多选取交集,按 Updated At 降序(「新建路由」卡置顶由页面保证) */
584
- export function queryRoutes(params: RouteListParams = {}): RouteListResult {
585
- const status = params.status ?? "all";
586
- const type = params.type ?? "all";
587
- const tags = params.tags ?? [];
588
- const items = ROUTES.filter(
589
- (r) =>
590
- (status === "all" || r.status === status) &&
591
- (type === "all" || r.type === type) &&
592
- (tags.length === 0 || tags.every((t) => r.tags.includes(t))),
593
- ).sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));
594
- const counts = computeCounts();
595
- return { items, total: ROUTES.length, counts, filters: computeFilters(counts) };
596
- }
597
-
598
- /** route.detail:按路由标识取详情;未命中返回 undefined(页面做空态) */
599
- export function getRoute(code: string): RouteRecord | undefined {
600
- return ROUTES.find((r) => r.code === code);
601
- }
602
-
603
- /** 全量路由标识(静态导出 generateStaticParams 用) */
604
- export const ROUTE_CODES: string[] = ROUTES.map((r) => r.code);