@teamix-evo/skills 0.4.0 → 0.6.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 (97) hide show
  1. package/README.md +7 -3
  2. package/manifest.json +3 -2
  3. package/package.json +2 -2
  4. package/src/teamix-evo-code-opentrek/SKILL.md +12 -10
  5. package/src/teamix-evo-code-opentrek/api-layering.md +8 -5
  6. package/src/teamix-evo-code-opentrek/checklist.md +2 -0
  7. package/src/teamix-evo-code-opentrek/error-and-loading.md +38 -25
  8. package/src/teamix-evo-code-opentrek/file-structure.md +63 -54
  9. package/src/teamix-evo-code-opentrek/forms-and-validation.md +14 -12
  10. package/src/teamix-evo-code-opentrek/reuse-first.md +2 -0
  11. package/src/teamix-evo-code-opentrek/routing-and-codesplit.md +23 -21
  12. package/src/teamix-evo-code-opentrek/testing.md +32 -28
  13. package/src/teamix-evo-code-uni-manager/SKILL.md +12 -10
  14. package/src/teamix-evo-code-uni-manager/api-layering.md +2 -0
  15. package/src/teamix-evo-code-uni-manager/checklist.md +2 -0
  16. package/src/teamix-evo-code-uni-manager/error-and-loading.md +3 -1
  17. package/src/teamix-evo-code-uni-manager/file-structure.md +2 -0
  18. package/src/teamix-evo-code-uni-manager/forms-and-validation.md +2 -0
  19. package/src/teamix-evo-code-uni-manager/reuse-first.md +3 -1
  20. package/src/teamix-evo-code-uni-manager/routing-and-codesplit.md +3 -1
  21. package/src/teamix-evo-code-uni-manager/testing.md +2 -0
  22. package/src/teamix-evo-design-opentrek/SKILL.md +213 -52
  23. package/src/teamix-evo-design-opentrek/boundaries.md +25 -5
  24. package/src/teamix-evo-design-opentrek/brand.md +7 -7
  25. package/src/teamix-evo-design-opentrek/checklist.md +15 -13
  26. package/src/teamix-evo-design-opentrek/components.md +89 -39
  27. package/src/teamix-evo-design-opentrek/examples/detail-ai-gateway-1.html +1069 -0
  28. package/src/teamix-evo-design-opentrek/examples/detail-ai-gateway-instance.html +941 -0
  29. package/src/teamix-evo-design-opentrek/examples/detail-page-api-doc.html +906 -0
  30. package/src/teamix-evo-design-opentrek/examples/detail-page-config.html +993 -0
  31. package/src/teamix-evo-design-opentrek/examples/detail-page-monitor.html +1339 -0
  32. package/src/teamix-evo-design-opentrek/examples/detail-page.html +933 -0
  33. package/src/teamix-evo-design-opentrek/examples/settings-page.html +1119 -0
  34. package/src/teamix-evo-design-opentrek/examples/standard-card-list.html +1094 -0
  35. package/src/teamix-evo-design-opentrek/examples/standard-table-list.html +1361 -0
  36. package/src/teamix-evo-design-opentrek/examples/wizard-form-page.html +877 -0
  37. package/src/teamix-evo-design-opentrek/flows.md +85 -12
  38. package/src/teamix-evo-design-opentrek/foundations.md +12 -9
  39. package/src/teamix-evo-design-opentrek/generation-flow.md +84 -14
  40. package/src/teamix-evo-design-opentrek/pages/detail-page/SKILL.md +260 -0
  41. package/src/teamix-evo-design-opentrek/pages/detail-page/patterns/api-doc-detail.md +163 -0
  42. package/src/teamix-evo-design-opentrek/pages/detail-page/patterns/comparison-detail.md +100 -0
  43. package/src/teamix-evo-design-opentrek/pages/detail-page/patterns/monitor-detail.md +190 -0
  44. package/src/teamix-evo-design-opentrek/pages/detail-page/patterns/resource-detail.md +148 -0
  45. package/src/teamix-evo-design-opentrek/pages/form-page/SKILL.md +362 -0
  46. package/src/teamix-evo-design-opentrek/pages/list-page/SKILL.md +286 -0
  47. package/src/teamix-evo-design-opentrek/pages/list-page/_shared/action-column-spec.md +60 -0
  48. package/src/teamix-evo-design-opentrek/pages/list-page/_shared/column-meta-rules.md +117 -0
  49. package/src/teamix-evo-design-opentrek/pages/list-page/_shared/search-combo-spec.md +194 -0
  50. package/src/teamix-evo-design-opentrek/pages/list-page/_shared/state-action-pattern.md +51 -0
  51. package/src/teamix-evo-design-opentrek/pages/list-page/patterns/advanced-filter-list.md +94 -0
  52. package/src/teamix-evo-design-opentrek/pages/list-page/patterns/card-list.md +558 -0
  53. package/src/teamix-evo-design-opentrek/pages/list-page/patterns/drawer-list.md +76 -0
  54. package/src/teamix-evo-design-opentrek/pages/list-page/patterns/expandable-list.md +70 -0
  55. package/src/teamix-evo-design-opentrek/pages/list-page/patterns/l2-sidebar-list.md +73 -0
  56. package/src/teamix-evo-design-opentrek/pages/list-page/patterns/standard-list.md +198 -0
  57. package/src/teamix-evo-design-opentrek/patterns/color-mapping.md +96 -0
  58. package/src/teamix-evo-design-opentrek/patterns/dashboard.md +2 -0
  59. package/src/teamix-evo-design-opentrek/patterns/detail-page.md +218 -152
  60. package/src/teamix-evo-design-opentrek/patterns/form-page.md +437 -228
  61. package/src/teamix-evo-design-opentrek/patterns/list-page.md +221 -260
  62. package/src/teamix-evo-design-opentrek/patterns/page-types.md +40 -125
  63. package/src/teamix-evo-design-opentrek/philosophy.md +7 -5
  64. package/src/teamix-evo-design-opentrek/rules/_assets/OP_AGENT RUNTIME.svg +1 -0
  65. package/src/teamix-evo-design-opentrek/rules/_assets/OP_AI GATEWAY.svg +1 -0
  66. package/src/teamix-evo-design-opentrek/rules/_assets/OP_AI STUDIO.svg +1 -0
  67. package/src/teamix-evo-design-opentrek/rules/_assets/OP_DEV-2.svg +1 -0
  68. package/src/teamix-evo-design-opentrek/rules/_assets/OP_LOGO.svg +1 -0
  69. package/src/teamix-evo-design-opentrek/rules/_assets/OP_OPS.svg +1 -0
  70. package/src/teamix-evo-design-opentrek/rules/boundaries.rules.json +3 -3
  71. package/src/teamix-evo-design-opentrek/rules/business-mapping.json +124 -0
  72. package/src/teamix-evo-design-opentrek/rules/common-components.json +924 -0
  73. package/src/teamix-evo-design-opentrek/rules/component-specs.json +1083 -0
  74. package/src/teamix-evo-design-opentrek/rules/design-tokens.css +433 -0
  75. package/src/teamix-evo-design-opentrek/rules/design-tokens.json +2798 -0
  76. package/src/teamix-evo-design-opentrek/rules/layout-rules.json +218 -0
  77. package/src/teamix-evo-design-opentrek/rules/page-flow.json +351 -0
  78. package/src/teamix-evo-design-opentrek/rules/page-frame.json +241 -0
  79. package/src/teamix-evo-design-opentrek/rules/page-header-spec.md +123 -0
  80. package/src/teamix-evo-design-opentrek/rules/page-types.json +206 -0
  81. package/src/teamix-evo-design-opentrek/rules/sidebar-spec.md +217 -0
  82. package/src/teamix-evo-design-opentrek/rules/styling.json +188 -0
  83. package/src/teamix-evo-design-opentrek/rules/token-mapping.md +284 -0
  84. package/src/teamix-evo-design-uni-manager/SKILL.md +18 -27
  85. package/src/teamix-evo-design-uni-manager/boundaries.md +7 -4
  86. package/src/teamix-evo-design-uni-manager/brand.md +1 -1
  87. package/src/teamix-evo-design-uni-manager/components.md +33 -28
  88. package/src/teamix-evo-design-uni-manager/foundations.md +24 -21
  89. package/src/teamix-evo-design-uni-manager/generation-flow.md +46 -8
  90. package/src/teamix-evo-design-uni-manager/patterns/dashboard.md +3 -1
  91. package/src/teamix-evo-design-uni-manager/patterns/detail-page.md +42 -13
  92. package/src/teamix-evo-design-uni-manager/patterns/form-page.md +67 -30
  93. package/src/teamix-evo-design-uni-manager/patterns/list-page.md +73 -40
  94. package/src/teamix-evo-design-uni-manager/patterns/page-types.md +14 -12
  95. package/src/teamix-evo-design-uni-manager/philosophy.md +4 -2
  96. package/src/teamix-evo-design-uni-manager/rules/boundaries.rules.json +3 -3
  97. package/src/teamix-evo-manage/SKILL.md +74 -66
@@ -0,0 +1,993 @@
1
+ <!DOCTYPE html>
2
+ <html lang="zh-CN">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>配置详情页 · 标准参考范例 — v7.8.4</title>
7
+ <!--
8
+ ★ 配置详情页标准参考范例(Config Detail Page Reference)
9
+ ─────────────────────────────────────────────────────────────
10
+ 用途:AI 生成配置详情页时的基础参考,展示 YAML/JSON 配置展示、版本历史与对比
11
+ ─────────────────────────────────────────────────────────────
12
+ 布局:TWO_COL(Sidebar + ContentCard 单例容器)
13
+ Sidebar:完整 L2 框架(折叠/展开 240px↔68px)
14
+ 内容区模式:模式B + 模式C 组合(Card + Tabs)
15
+ 子类型:配置详情页
16
+ ─────────────────────────────────────────────────────────────
17
+ 组件清单:
18
+ • PageHeader — 面包屑模式 + 操作按钮组
19
+ • InfoCard — 顶部摘要(嵌入模式B)
20
+ • Tabs — 当前配置/版本历史/基本信息
21
+ • CodeBlock — YAML 语法高亮 + 行号 + 复制按钮
22
+ • VersionHistory — 时间线 + ConfigDiff 双栏对比
23
+ • DescriptionList — 键值对列表,3列布局
24
+ • Badge — 状态展示
25
+ • DropdownMenu — “更多”操作
26
+ ─────────────────────────────────────────────────────────────
27
+ 规范来源:pages/detail-page/SKILL.md v7.8.4
28
+ Tokens 版本:v7.8.4(v7.6 ContentCard 表面规则 + v7.8.4 字体族变量)
29
+ 表面边界:PageHeader / Tabs / Separator 的 border 为「功能性分割线」,不受 v7.6 surface 约束
30
+ -->
31
+ <style>
32
+ *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
33
+
34
+ :root {
35
+ --primary: 218.6 100% 46.7%;
36
+ --primary-foreground: 0 0% 100%;
37
+ --primary-hover: 218.6 100% 42%;
38
+ --primary-click: 218.6 100% 37.5%;
39
+ --gray-primary: 220 9% 7%;
40
+ --gray-primary-foreground: 210 33% 99%;
41
+ --gray-secondary-foreground: 216 4% 26%;
42
+ --muted-foreground: 216 2% 45%;
43
+ --gray-disabled: 210 3% 63%;
44
+ --gray-line: 210 9% 91%;
45
+ --gray-sidebar-accent: 210 12% 93%;
46
+ --gray-muted: 220 18% 97%;
47
+ --gray-white: 0 0% 100%;
48
+ --gray-title: 220 9% 7%;
49
+ --gray-secondary: 216 4% 26%;
50
+ --gray-tertiary: 216 2% 45%;
51
+ --gray-border: 210 9% 91%;
52
+ --gray-fill: 220 18% 97%;
53
+ --success: 142 76% 36%;
54
+ --success-bg: 152 82% 96%;
55
+ --success-border: 142 77% 73%;
56
+ --warning: 40 88% 48%;
57
+ --warning-bg: 43 100% 94%;
58
+ --destructive: 0 72% 51%;
59
+ --destructive-bg: 0 86% 97%;
60
+ --info: 221 83% 53%;
61
+ --info-bg: 214 100% 97%;
62
+ --background: 220 18% 97%;
63
+ --card: 0 0% 100%;
64
+ --card-foreground: 220 9% 7%;
65
+ --border: 210 9% 91%;
66
+ --ring: 218.6 100% 46.7%;
67
+ --sidebar: 220 18% 97%;
68
+ --sidebar-foreground: 220 9% 7%;
69
+ --sidebar-active: 210 12% 93%;
70
+ --sidebar-active-foreground: 220 9% 7%;
71
+ --sidebar-hover: 220 18% 97%;
72
+ --sidebar-group: 216 2% 45%;
73
+ --sidebar-item: 220 9% 7%;
74
+ --sidebar-item-muted: 216 4% 26%;
75
+ --sidebar-border: 210 9% 91%;
76
+ --radius: 1rem;
77
+ --radius-sm: 4px;
78
+ --radius-md: 8px;
79
+ --radius-lg: 12px;
80
+ --gap-xs: 4px;
81
+ --btn-padding-x: 16px;
82
+ --btn-padding-x-sm: 12px;
83
+ --button-gap: 8px;
84
+ --tabs-gap: 24px;
85
+ --card-gap: 16px;
86
+ --page-container-gap: 16px;
87
+ --page-container-padding: 16px;
88
+ --page-container-padding-left: 0px;
89
+ --card-padding-x: 20px;
90
+ --shadow-sm: 0 1px 2px rgba(0,0,0,0.05);
91
+ --font-size-base: 12px;
92
+ --font-size-lg: 14px;
93
+ --font-size-xl: 16px;
94
+ --font-size-2xl: 18px;
95
+ --font-size-page-header: 18px;
96
+ --font-sans: 'PingFang SC', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', sans-serif;
97
+ --font-mono: 'SF Mono', Monaco, 'Courier New', monospace;
98
+ --font-weight-normal: 400;
99
+ --font-weight-medium: 500;
100
+ --font-weight-black: 900;
101
+ --input-height: 32px;
102
+ --input-radius: 8px;
103
+ --layout-sidebar-width: 240px;
104
+ --duration-fast: 150ms;
105
+ --duration-normal: 200ms;
106
+ --easing-ease: ease;
107
+ }
108
+
109
+ body {
110
+ font-family: var(--font-sans);
111
+ font-size: var(--font-size-base);
112
+ color: hsl(var(--gray-primary));
113
+ background: hsl(var(--background));
114
+ min-height: 100vh;
115
+ }
116
+
117
+ /* ===== Sidebar ===== */
118
+ .sidebar {
119
+ position: fixed; top: 0; left: 0; bottom: 0;
120
+ width: var(--layout-sidebar-width);
121
+ background: hsl(var(--sidebar));
122
+ z-index: 100;
123
+ display: flex; flex-direction: column;
124
+ overflow: hidden;
125
+ transition: width 0.2s ease;
126
+ }
127
+ .sidebar-header {
128
+ height: 56px; padding: 0 16px;
129
+ display: flex; align-items: center; gap: var(--gap-xs);
130
+ flex-shrink: 0;
131
+ }
132
+ .sidebar-logo {
133
+ height: 28px; flex-shrink: 0;
134
+ display: flex; align-items: center;
135
+ overflow: hidden;
136
+ }
137
+ .sidebar-logo img {
138
+ height: 28px; width: auto; display: block;
139
+ }
140
+ .sidebar-logo .collapsed-logo {
141
+ display: none; width: 30px; height: 30px;
142
+ }
143
+ .sidebar-logo .expand-logo {
144
+ display: none; width: 18px; height: 18px;
145
+ color: hsl(var(--muted-foreground));
146
+ }
147
+ .sidebar-collapse-btn {
148
+ margin-left: auto; width: 18px; height: 18px;
149
+ background: none; border: none; padding: 0; cursor: pointer;
150
+ display: flex; align-items: center; justify-content: center;
151
+ color: hsl(var(--muted-foreground)); flex-shrink: 0;
152
+ }
153
+ .sidebar-collapse-btn svg { width: 18px; height: 18px; }
154
+
155
+ /* ===== Sidebar Collapsed State ===== */
156
+ .sidebar.collapsed { width: 68px; overflow: visible; }
157
+ .sidebar.collapsed .sidebar-header { padding: 0 12px; justify-content: center; }
158
+ .sidebar.collapsed .sidebar-logo { cursor: pointer; }
159
+ .sidebar.collapsed .sidebar-logo img { display: none; }
160
+ .sidebar.collapsed .sidebar-logo .collapsed-logo { display: block; }
161
+ .sidebar.collapsed .sidebar-logo:hover .collapsed-logo { display: none; }
162
+ .sidebar.collapsed .sidebar-logo:hover .expand-logo { display: block; }
163
+ .sidebar.collapsed .sidebar-collapse-btn { display: none; }
164
+ .sidebar.collapsed .sidebar-content { padding: 12px 0; overflow-y: auto; overflow-x: hidden; }
165
+ .sidebar.collapsed .sidebar-menu { padding: 0; }
166
+ .sidebar.collapsed .sidebar-menu > li { display: flex; justify-content: center; }
167
+ .sidebar.collapsed .sidebar-menu > li > a.sidebar-menu-item {
168
+ justify-content: center; padding: 0; width: 40px; height: 40px;
169
+ border-radius: var(--radius-md); position: relative; flex-shrink: 0;
170
+ gap: 0;
171
+ }
172
+ .sidebar.collapsed .sidebar-menu > li > a.sidebar-menu-item .item-icon { margin: 0; }
173
+ .sidebar.collapsed .sidebar-menu > li > a > span:not(.item-icon),
174
+ .sidebar.collapsed .sidebar-menu > li > a > .expand-icon,
175
+ .sidebar.collapsed .sidebar-menu-item:not(.item-icon):after { display: none; }
176
+ /* 隐藏文字节点(直接文本) */
177
+ .sidebar.collapsed .sidebar-menu > li > a { font-size: 0; }
178
+ .sidebar.collapsed .sidebar-menu > li > a > .item-icon { font-size: var(--font-size-lg); }
179
+ .sidebar.collapsed .sidebar-sub-menu { display: none !important; }
180
+ .sidebar.collapsed .sidebar-divider { margin: 0 14px; }
181
+ .sidebar.collapsed .sidebar-footer { padding: 12px 10px 16px; justify-content: center; }
182
+ .sidebar.collapsed .sidebar-username,
183
+ .sidebar.collapsed .sidebar-more { display: none; }
184
+
185
+ /* ===== Sidebar Collapsed Flyout (hover 浮层展示二级菜单) ===== */
186
+ .sidebar.collapsed .sidebar-menu > li { position: relative; }
187
+ .sidebar.collapsed .sidebar-menu > li:hover > .sidebar-flyout {
188
+ display: block;
189
+ }
190
+ .sidebar-flyout {
191
+ display: none;
192
+ position: absolute; left: 100%; top: 0;
193
+ min-width: 160px; padding: 4px;
194
+ background: hsl(var(--card));
195
+ border: 1px solid hsl(var(--gray-line));
196
+ border-radius: var(--radius-md);
197
+ box-shadow: 0 4px 12px rgba(0,0,0,0.08);
198
+ z-index: 200;
199
+ }
200
+ .sidebar-flyout .sidebar-menu-item {
201
+ height: 36px; padding: 0 12px;
202
+ font-size: var(--font-size-base) !important;
203
+ font-weight: var(--font-weight-normal);
204
+ color: hsl(var(--gray-primary));
205
+ border-radius: var(--radius-sm);
206
+ white-space: nowrap;
207
+ }
208
+ .sidebar-flyout .sidebar-menu-item:hover {
209
+ background: hsl(var(--gray-muted));
210
+ }
211
+
212
+ /* Main content 响应 sidebar 收起 */
213
+ .main-content {
214
+ margin-left: var(--layout-sidebar-width);
215
+ min-height: 100vh;
216
+ transition: margin-left 0.2s ease;
217
+ }
218
+ .sidebar.collapsed ~ .main-content { margin-left: 68px; }
219
+ .sidebar-product {
220
+ font-size: var(--font-size-xl); font-weight: var(--font-weight-black);
221
+ color: hsl(var(--sidebar-foreground)); letter-spacing: -0.025em;
222
+ white-space: nowrap; line-height: 1;
223
+ }
224
+ .sidebar-content {
225
+ flex: 1; overflow-y: auto; padding: 12px 0;
226
+ }
227
+ .sidebar-group-label {
228
+ padding: 0 16px; height: 26px; line-height: 26px;
229
+ font-size: var(--font-size-base); font-weight: var(--font-weight-normal);
230
+ color: hsl(var(--sidebar-group));
231
+ }
232
+ .sidebar-group-label:not(:first-child) { margin-top: 16px; }
233
+ .sidebar-menu { list-style: none; padding: 0 8px; display: flex; flex-direction: column; gap: 2px; }
234
+ .sidebar-menu-item {
235
+ display: flex; align-items: center; gap: var(--button-gap);
236
+ padding: 0 12px; height: 40px;
237
+ border-radius: var(--radius-md);
238
+ font-size: var(--font-size-lg); font-weight: var(--font-weight-medium);
239
+ color: hsl(var(--sidebar-item-muted));
240
+ text-decoration: none; cursor: pointer;
241
+ transition: background var(--duration-fast), color var(--duration-fast);
242
+ white-space: nowrap;
243
+ }
244
+ .sidebar-menu-item:hover {
245
+ background: hsl(var(--sidebar-hover));
246
+ color: hsl(var(--sidebar-item));
247
+ }
248
+ .sidebar-menu-item.active {
249
+ background: hsl(var(--sidebar-active));
250
+ color: hsl(var(--sidebar-active-foreground));
251
+ }
252
+ .sidebar-menu-item .item-icon {
253
+ width: 16px; height: 16px; flex-shrink: 0;
254
+ display: flex; align-items: center; justify-content: center;
255
+ }
256
+ .sidebar-menu-item .expand-icon {
257
+ margin-left: auto; width: 16px; height: 16px;
258
+ transition: transform var(--duration-fast);
259
+ }
260
+ .sidebar-menu-item.expanded .expand-icon { transform: rotate(90deg); }
261
+ .sidebar-sub-menu {
262
+ list-style: none; padding: 0 0 0 24px;
263
+ display: none;
264
+ }
265
+ .sidebar-menu > li > .sidebar-sub-menu { padding-left: 0; }
266
+ .sidebar-sub-menu.show { display: block; }
267
+ .sidebar-sub-menu .sidebar-menu-item {
268
+ height: 36px; font-size: var(--font-size-base); font-weight: var(--font-weight-normal);
269
+ padding-left: 36px;
270
+ }
271
+ .sidebar-divider {
272
+ height: 1px; background: hsl(var(--sidebar-border));
273
+ margin: 0 12px; flex-shrink: 0;
274
+ }
275
+ .sidebar-footer {
276
+ flex-shrink: 0; display: flex; align-items: center;
277
+ gap: var(--button-gap); padding: 12px 12px 16px 24px;
278
+ }
279
+ .sidebar-avatar {
280
+ width: 28px; height: 28px; border-radius: 50%;
281
+ background: hsl(var(--sidebar-active));
282
+ display: flex; align-items: center; justify-content: center;
283
+ font-size: var(--font-size-base); font-weight: 600;
284
+ color: hsl(var(--gray-primary)); flex-shrink: 0;
285
+ }
286
+ .sidebar-username {
287
+ flex: 1; font-size: var(--font-size-lg); font-weight: var(--font-weight-medium);
288
+ color: hsl(var(--sidebar-item)); overflow: hidden;
289
+ text-overflow: ellipsis; white-space: nowrap;
290
+ }
291
+ .sidebar-more {
292
+ width: 28px; height: 28px; border-radius: var(--radius-sm);
293
+ border: none; background: transparent; cursor: pointer;
294
+ display: flex; align-items: center; justify-content: center;
295
+ color: hsl(var(--muted-foreground));
296
+ }
297
+ .sidebar-more:hover { background: hsl(var(--sidebar-hover)); color: hsl(var(--gray-primary)); }
298
+
299
+ /* ===== Main Content ===== */
300
+
301
+ .page-container {
302
+ padding: var(--page-container-padding) var(--page-container-padding) var(--page-container-padding) var(--page-container-padding-left);
303
+ }
304
+ .card {
305
+ background: hsl(var(--card));
306
+ border-radius: var(--radius-lg);
307
+ /* v7.6:去掉 1px border,使用最弱阴影 var(--shadow-sm) */
308
+ box-shadow: var(--shadow-sm, 0 1px 2px rgba(0,0,0,0.05));
309
+ overflow: hidden;
310
+ display: flex; flex-direction: column;
311
+ min-height: calc(100vh - 40px);
312
+ padding: 0 var(--card-padding-x);
313
+ }
314
+
315
+ /* ===== PageHeader — Breadcrumb Mode ===== */
316
+ /* L2 面包屑模式:无 border、无 background、无 box-shadow;高度 64px,padding 由外层 Card 提供 */
317
+ .page-header {
318
+ height: 64px; padding: 0;
319
+ display: flex; align-items: center; justify-content: space-between;
320
+ flex-shrink: 0;
321
+ }
322
+ .page-header-left { display: flex; align-items: center; }
323
+ .breadcrumb {
324
+ display: flex; align-items: center; gap: 4px;
325
+ font-size: var(--font-size-page-header); font-weight: var(--font-weight-medium);
326
+ color: hsl(var(--gray-secondary-foreground));
327
+ }
328
+ .breadcrumb a { color: hsl(var(--gray-secondary-foreground)); text-decoration: none; }
329
+ .breadcrumb a:hover { color: hsl(var(--gray-primary)); }
330
+ .breadcrumb .separator { color: hsl(var(--muted-foreground)); display: flex; align-items: center; }
331
+ .breadcrumb .separator svg { width: 20px; height: 20px; }
332
+ .breadcrumb .current { color: hsl(var(--gray-primary)); font-weight: var(--font-weight-medium); }
333
+ .page-header-right { display: flex; align-items: center; gap: var(--button-gap); }
334
+
335
+ /* ===== Buttons ===== */
336
+ .btn {
337
+ display: inline-flex; align-items: center; justify-content: center; gap: var(--gap-xs);
338
+ height: var(--input-height); padding: 0 var(--btn-padding-x);
339
+ border-radius: var(--radius-md); font-size: var(--font-size-base);
340
+ font-weight: var(--font-weight-medium); border: none; cursor: pointer;
341
+ transition: background var(--duration-fast), opacity var(--duration-fast);
342
+ white-space: nowrap;
343
+ }
344
+ .btn-primary { background: hsl(var(--primary)); color: hsl(var(--primary-foreground)); }
345
+ .btn-primary:hover { background: hsl(var(--primary-hover)); }
346
+ .btn-outline { background: hsl(var(--card)); color: hsl(var(--gray-primary)); border: 1px solid hsl(var(--gray-line)); }
347
+ .btn-outline:hover { background: hsl(var(--gray-muted)); }
348
+ .btn-ghost { background: transparent; color: hsl(var(--gray-primary)); }
349
+ .btn-ghost:hover { background: hsl(var(--gray-muted)); }
350
+ .btn-icon { width: var(--input-height); padding: 0; }
351
+
352
+ /* ===== InfoCard — 嵌入模式B(无分割线) ===== */
353
+ .info-card {
354
+ padding: 16px 0;
355
+ display: flex; align-items: center; gap: 24px; flex-wrap: wrap;
356
+ }
357
+ .info-card-field { display: flex; align-items: center; gap: 6px; }
358
+ .info-card-field-label { font-size: var(--font-size-base); color: hsl(var(--muted-foreground)); }
359
+ .info-card-field-value { font-size: var(--font-size-base); color: hsl(var(--gray-primary)); font-weight: var(--font-weight-medium); }
360
+
361
+ /* ===== Tabs ===== */
362
+ .tabs {
363
+ border-bottom: 1px solid hsl(var(--gray-line));
364
+ padding-top: 4px;
365
+ }
366
+ .tabs-list { display: flex; gap: var(--tabs-gap); list-style: none; }
367
+ .tab-item {
368
+ padding: 10px 0; font-size: var(--font-size-lg);
369
+ color: hsl(var(--gray-secondary-foreground)); cursor: pointer;
370
+ border-bottom: 2px solid transparent;
371
+ transition: color var(--duration-fast), border-color var(--duration-fast);
372
+ font-weight: var(--font-weight-normal);
373
+ }
374
+ .tab-item:hover { color: hsl(var(--gray-primary)); }
375
+ .tab-item.active {
376
+ color: hsl(var(--primary));
377
+ border-bottom-color: hsl(var(--primary));
378
+ font-weight: var(--font-weight-medium);
379
+ }
380
+ .tab-content { padding: 20px 0; display: none; }
381
+ .tab-content.active { display: block; }
382
+
383
+ /* ===== CodeBlock ===== */
384
+ .code-block {
385
+ position: relative;
386
+ background: hsl(var(--gray-muted));
387
+ border-radius: var(--radius-md);
388
+ overflow: hidden;
389
+ }
390
+ .code-block-header {
391
+ display: flex; align-items: center; justify-content: space-between;
392
+ padding: 8px 16px; font-size: var(--font-size-base);
393
+ color: hsl(var(--muted-foreground)); border-bottom: 1px solid hsl(var(--gray-line));
394
+ }
395
+ .code-block-copy {
396
+ position: absolute; top: 8px; right: 12px;
397
+ width: 24px; height: 24px; border: none; background: transparent;
398
+ cursor: pointer; display: flex; align-items: center; justify-content: center;
399
+ border-radius: var(--radius-sm); color: hsl(var(--muted-foreground));
400
+ opacity: 0; transition: opacity var(--duration-fast);
401
+ }
402
+ .code-block:hover .code-block-copy { opacity: 1; }
403
+ .code-block-copy:hover { background: hsl(var(--gray-line)); color: hsl(var(--gray-primary)); }
404
+ .code-block-body { display: flex; padding: 16px 0; overflow-x: auto; }
405
+ .code-line-numbers {
406
+ width: 40px; flex-shrink: 0; text-align: right; padding-right: 12px;
407
+ border-right: 1px solid hsl(var(--gray-line));
408
+ font-family: var(--font-mono); font-size: 13px; line-height: 1.6;
409
+ color: hsl(var(--gray-disabled)); user-select: none;
410
+ }
411
+ .code-content {
412
+ flex: 1; padding-left: 16px; padding-right: 16px;
413
+ font-family: var(--font-mono); font-size: 13px; line-height: 1.6;
414
+ white-space: pre; color: hsl(var(--gray-primary));
415
+ }
416
+ .code-content .kw { color: hsl(218 80% 50%); } /* keyword blue */
417
+ .code-content .str { color: hsl(142 60% 35%); } /* string green */
418
+ .code-content .cmt { color: hsl(var(--muted-foreground)); } /* comment gray */
419
+ .code-content .num { color: hsl(30 80% 45%); } /* number orange */
420
+
421
+ /* ===== VersionHistory ===== */
422
+ .version-history { display: flex; gap: 24px; }
423
+ .version-timeline { width: 320px; flex-shrink: 0; }
424
+ .version-item { display: flex; gap: 12px; position: relative; padding-bottom: 20px; }
425
+ .version-item:last-child { padding-bottom: 0; }
426
+ .version-item::before {
427
+ content: ''; position: absolute; left: 3.5px; top: 16px; bottom: 0;
428
+ width: 1px; background: hsl(var(--gray-line));
429
+ }
430
+ .version-item:last-child::before { display: none; }
431
+ .version-dot {
432
+ width: 8px; height: 8px; border-radius: 50%;
433
+ background: hsl(var(--gray-disabled)); flex-shrink: 0; margin-top: 5px;
434
+ }
435
+ .version-item.current .version-dot { background: hsl(var(--primary)); }
436
+ .version-info { flex: 1; min-width: 0; }
437
+ .version-header { display: flex; align-items: center; gap: 8px; margin-bottom: 2px; }
438
+ .version-number { font-size: var(--font-size-lg); font-weight: var(--font-weight-medium); color: hsl(var(--gray-primary)); }
439
+ .version-tag {
440
+ font-size: 10px; padding: 1px 6px; border-radius: var(--radius-sm);
441
+ background: hsl(var(--info-bg)); color: hsl(var(--primary)); font-weight: var(--font-weight-medium);
442
+ }
443
+ .version-meta { font-size: var(--font-size-base); color: hsl(var(--muted-foreground)); margin-bottom: 2px; }
444
+ .version-msg { font-size: var(--font-size-base); color: hsl(var(--gray-secondary-foreground)); }
445
+ .version-actions { margin-top: 4px; }
446
+ .version-actions .btn { height: 24px; padding: 0 8px; font-size: 11px; opacity: 0; transition: opacity var(--duration-fast); }
447
+ .version-item:hover .version-actions .btn { opacity: 1; }
448
+
449
+ /* ===== ConfigDiff ===== */
450
+ .config-diff { flex: 1; min-width: 0; border: 1px solid hsl(var(--gray-line)); border-radius: var(--radius-md); overflow: hidden; }
451
+ .config-diff-header {
452
+ height: 36px; display: flex; align-items: center;
453
+ background: hsl(var(--gray-muted)); padding: 0 12px;
454
+ font-size: var(--font-size-base); color: hsl(var(--muted-foreground));
455
+ border-bottom: 1px solid hsl(var(--gray-line));
456
+ }
457
+ .config-diff-body { display: flex; }
458
+ .config-diff-panel { flex: 1; min-width: 0; }
459
+ .config-diff-panel + .config-diff-panel { border-left: 1px solid hsl(var(--gray-line)); }
460
+ .config-diff-panel-header {
461
+ height: 28px; display: flex; align-items: center;
462
+ padding: 0 12px; font-size: 11px; font-weight: var(--font-weight-medium);
463
+ color: hsl(var(--muted-foreground)); background: hsl(var(--gray-muted));
464
+ border-bottom: 1px solid hsl(var(--gray-line));
465
+ }
466
+ .diff-line {
467
+ padding: 2px 12px; font-family: var(--font-mono);
468
+ font-size: 12px; line-height: 1.6; white-space: pre;
469
+ }
470
+ .diff-line.added { background: hsl(152 82% 96%); }
471
+ .diff-line.removed { background: hsl(0 72% 96%); }
472
+ .diff-line.context { background: transparent; color: hsl(var(--muted-foreground)); }
473
+ .config-diff-footer {
474
+ height: 28px; display: flex; align-items: center; gap: 12px;
475
+ padding: 0 12px; font-size: 11px; color: hsl(var(--muted-foreground));
476
+ border-top: 1px solid hsl(var(--gray-line)); background: hsl(var(--gray-muted));
477
+ }
478
+ .diff-stat-add { color: hsl(var(--success)); }
479
+ .diff-stat-del { color: hsl(var(--destructive)); }
480
+
481
+ /* ===== DescriptionList ===== */
482
+ .description-list { display: grid; grid-template-columns: repeat(3, 1fr); gap: 0; }
483
+ .description-item { display: flex; align-items: center; height: 36px; padding: 8px 0; gap: 12px; }
484
+ .description-label {
485
+ min-width: 80px; max-width: 140px;
486
+ font-size: var(--font-size-base); color: hsl(var(--muted-foreground));
487
+ font-weight: var(--font-weight-normal); flex-shrink: 0;
488
+ }
489
+ .description-value {
490
+ font-size: var(--font-size-base); color: hsl(var(--gray-primary));
491
+ font-weight: var(--font-weight-normal);
492
+ overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
493
+ display: flex; align-items: center; gap: 4px;
494
+ }
495
+ .description-value.copyable { cursor: pointer; }
496
+ .description-value.copyable .copy-icon {
497
+ width: 16px; height: 16px; color: hsl(var(--muted-foreground));
498
+ opacity: 0; transition: opacity var(--duration-fast); flex-shrink: 0;
499
+ }
500
+ .description-item:hover .description-value.copyable .copy-icon { opacity: 1; }
501
+ .description-value.link { color: hsl(var(--primary)); cursor: pointer; }
502
+ .description-value.link:hover { text-decoration: underline; }
503
+
504
+ /* ===== Badge ===== */
505
+ .badge {
506
+ display: inline-flex; align-items: center; gap: 4px;
507
+ padding: 2px 8px; border-radius: var(--radius-sm);
508
+ font-size: var(--font-size-base); font-weight: var(--font-weight-medium); line-height: 1.5;
509
+ }
510
+ .badge-success { background: hsl(var(--success-bg)); color: hsl(var(--success)); }
511
+ .badge-dot { width: 6px; height: 6px; border-radius: 50%; }
512
+ .badge-success .badge-dot { background: hsl(var(--success)); }
513
+
514
+ /* ===== DropdownMenu ===== */
515
+ .dropdown { position: relative; display: inline-block; }
516
+ .dropdown-menu {
517
+ display: none; position: absolute; top: calc(100% + 4px); right: 0;
518
+ min-width: 140px; padding: 4px;
519
+ background: hsl(var(--card)); border: 1px solid hsl(var(--gray-line));
520
+ border-radius: var(--radius-md);
521
+ box-shadow: 0 6px 24px rgba(0,0,0,0.1); z-index: 50;
522
+ }
523
+ .dropdown.open .dropdown-menu { display: block; }
524
+ .dropdown-item {
525
+ display: flex; align-items: center; gap: 8px;
526
+ padding: 8px 12px; border-radius: var(--radius-sm);
527
+ font-size: var(--font-size-base); color: hsl(var(--gray-primary));
528
+ cursor: pointer; transition: background var(--duration-fast);
529
+ }
530
+ .dropdown-item:hover { background: hsl(var(--gray-muted)); }
531
+ .dropdown-item.danger { color: hsl(var(--destructive)); }
532
+ .dropdown-item.danger:hover { background: hsl(var(--destructive-bg)); }
533
+
534
+ /* ===== Responsive ===== */
535
+ @media (max-width: 1199px) {
536
+ .description-list { grid-template-columns: repeat(2, 1fr); }
537
+ .version-history { flex-direction: column; }
538
+ .version-timeline { width: 100%; }
539
+ }
540
+ @media (max-width: 768px) {
541
+ .description-list { grid-template-columns: 1fr; }
542
+ }
543
+ </style>
544
+ </head>
545
+ <body>
546
+
547
+ <!-- ===== Sidebar ===== -->
548
+ <aside class="sidebar">
549
+ <div class="sidebar-header">
550
+ <div class="sidebar-logo" id="sidebarLogoArea">
551
+ <img src="./_assets/op-ai-gateway-logo.svg" alt="OP AI Gateway" />
552
+ <img class="collapsed-logo" src="./_assets/op-logo-collapsed.png" alt="OP" />
553
+ <svg class="expand-logo" viewBox="0 0 1024 1024" fill="currentColor" width="18" height="18"><path d="M151.68 616.96l82.56-82.56c12.8-12.8 12.8-32.64 0-45.44L151.68 406.4c-19.84-19.84-54.4-5.76-54.4 22.4v165.76c-0.64 28.16 33.92 42.88 54.4 22.4zM128.64 288h768c14.08 0 32-14.08 32-32s-17.92-32-32-32h-768c-14.08 0-32 14.08-32 32s17.92 32 32 32zM896.64 736h-768c-14.08 0-32 14.08-32 32s17.92 32 32 32h768c14.08 0 32-14.08 32-32s-17.28-32-32-32zM896.64 480h-480c-14.08 0-32 14.08-32 32s17.92 32 32 32h480c14.08 0 32-14.08 32-32s-17.28-32-32-32z"/></svg>
554
+ </div>
555
+ <button class="sidebar-collapse-btn" title="收起侧边栏">
556
+ <svg viewBox="0 0 1024 1024" fill="currentColor" width="18" height="18"><path d="M874.24 406.4l-82.56 82.56c-12.8 12.8-12.8 32.64 0 45.44l82.56 82.56c19.84 19.84 54.4 5.76 54.4-22.4V428.8c0-28.16-33.92-42.88-54.4-22.4zM128.64 288h768c14.08 0 32-14.08 32-32s-17.92-32-32-32h-768c-14.08 0-32 14.08-32 32s17.92 32 32 32zM896.64 736h-768c-14.08 0-32 14.08-32 32s17.92 32 32 32h768c14.08 0 32-14.08 32-32s-17.28-32-32-32zM128.64 544h480c14.08 0 32-14.08 32-32s-17.92-32-32-32h-480c-14.08 0-32 14.08-32 32s17.92 32 32 32z"/></svg>
557
+ </button>
558
+ </div>
559
+
560
+ <!-- sidebar:按 PRD 导航结构,L1 为一级菜单,L2 为二级子菜单。 -->
561
+ <div class="sidebar-content">
562
+ <ul class="sidebar-menu">
563
+ <!-- L1: AI 网关(叶子菜单,当前活跃) -->
564
+ <li><a class="sidebar-menu-item active" href="#">
565
+ <span class="item-icon">
566
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="7" height="7" rx="1"/><rect x="14" y="3" width="7" height="7" rx="1"/><rect x="3" y="14" width="7" height="7" rx="1"/><rect x="14" y="14" width="7" height="7" rx="1"/></svg>
567
+ </span>
568
+ AI 网关
569
+ </a></li>
570
+
571
+ <!-- L1: API 网关(父菜单) -->
572
+ <li>
573
+ <a class="sidebar-menu-item expanded" href="#" data-toggle="api-gateway">
574
+ <span class="item-icon">
575
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M4 6h16M4 12h16M4 18h16"/></svg>
576
+ </span>
577
+ API 网关
578
+ <span class="expand-icon">
579
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M9 18l6-6-6-6"/></svg>
580
+ </span>
581
+ </a>
582
+ <ul class="sidebar-sub-menu show" id="api-gateway">
583
+ <li><a class="sidebar-menu-item" href="#">实例管理</a></li>
584
+ <li><a class="sidebar-menu-item" href="#">共享实例管理</a></li>
585
+ </ul>
586
+ <div class="sidebar-flyout">
587
+ <a class="sidebar-menu-item" href="#">实例管理</a>
588
+ <a class="sidebar-menu-item" href="#">共享实例管理</a>
589
+ </div>
590
+ </li>
591
+
592
+ <!-- L1: 云原生网关(父菜单) -->
593
+ <li>
594
+ <a class="sidebar-menu-item" href="#" data-toggle="cloud-native-gw">
595
+ <span class="item-icon">
596
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M18 10h-1.26A8 8 0 109 20h9a5 5 0 000-10z"/></svg>
597
+ </span>
598
+ 云原生网关
599
+ <span class="expand-icon">
600
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M9 18l6-6-6-6"/></svg>
601
+ </span>
602
+ </a>
603
+ <ul class="sidebar-sub-menu" id="cloud-native-gw">
604
+ <li><a class="sidebar-menu-item" href="#">实例列表</a></li>
605
+ <li><a class="sidebar-menu-item" href="#">Nginx Ingress 迁移</a></li>
606
+ </ul>
607
+ <div class="sidebar-flyout">
608
+ <a class="sidebar-menu-item" href="#">实例列表</a>
609
+ <a class="sidebar-menu-item" href="#">Nginx Ingress 迁移</a>
610
+ </div>
611
+ </li>
612
+
613
+ <!-- L1: 集群管理(叶子菜单) -->
614
+ <li><a class="sidebar-menu-item" href="#">
615
+ <span class="item-icon">
616
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="3"/><path d="M12 1v4M12 19v4M4.22 4.22l2.83 2.83M16.95 16.95l2.83 2.83M1 12h4M19 12h4M4.22 19.78l2.83-2.83M16.95 7.05l2.83-2.83"/></svg>
617
+ </span>
618
+ 集群管理
619
+ </a></li>
620
+
621
+ <!-- L1: 级联管理(父菜单) -->
622
+ <li>
623
+ <a class="sidebar-menu-item" href="#" data-toggle="cascade-mgmt">
624
+ <span class="item-icon">
625
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M4 6v12M12 3v18M20 6v12M2 6h4M10 3h4M18 6h4M2 12h4M10 9h4M18 12h4M2 18h4M10 21h4M18 18h4"/></svg>
626
+ </span>
627
+ 级联管理
628
+ <span class="expand-icon">
629
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M9 18l6-6-6-6"/></svg>
630
+ </span>
631
+ </a>
632
+ <ul class="sidebar-sub-menu" id="cascade-mgmt">
633
+ <li><a class="sidebar-menu-item" href="#">级联网关</a></li>
634
+ <li><a class="sidebar-menu-item" href="#">级联链路</a></li>
635
+ </ul>
636
+ <div class="sidebar-flyout">
637
+ <a class="sidebar-menu-item" href="#">级联网关</a>
638
+ <a class="sidebar-menu-item" href="#">级联链路</a>
639
+ </div>
640
+ </li>
641
+
642
+ <!-- L1: 容灾管理(父菜单) -->
643
+ <li>
644
+ <a class="sidebar-menu-item" href="#" data-toggle="disaster-mgmt">
645
+ <span class="item-icon">
646
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/></svg>
647
+ </span>
648
+ 容灾管理
649
+ <span class="expand-icon">
650
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M9 18l6-6-6-6"/></svg>
651
+ </span>
652
+ </a>
653
+ <ul class="sidebar-sub-menu" id="disaster-mgmt">
654
+ <li><a class="sidebar-menu-item" href="#">容灾网关实例</a></li>
655
+ </ul>
656
+ <div class="sidebar-flyout">
657
+ <a class="sidebar-menu-item" href="#">容灾网关实例</a>
658
+ </div>
659
+ </li>
660
+
661
+ <!-- L1: 认证授权(父菜单) -->
662
+ <li>
663
+ <a class="sidebar-menu-item" href="#" data-toggle="auth-mgmt">
664
+ <span class="item-icon">
665
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="11" width="18" height="11" rx="2"/><path d="M7 11V7a5 5 0 0110 0v4"/></svg>
666
+ </span>
667
+ 认证授权
668
+ <span class="expand-icon">
669
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M9 18l6-6-6-6"/></svg>
670
+ </span>
671
+ </a>
672
+ <ul class="sidebar-sub-menu" id="auth-mgmt">
673
+ <li><a class="sidebar-menu-item" href="#">用户管理</a></li>
674
+ <li><a class="sidebar-menu-item" href="#">角色管理</a></li>
675
+ <li><a class="sidebar-menu-item" href="#">权限插件</a></li>
676
+ <li><a class="sidebar-menu-item" href="#">访问凭证</a></li>
677
+ </ul>
678
+ <div class="sidebar-flyout">
679
+ <a class="sidebar-menu-item" href="#">用户管理</a>
680
+ <a class="sidebar-menu-item" href="#">角色管理</a>
681
+ <a class="sidebar-menu-item" href="#">权限插件</a>
682
+ <a class="sidebar-menu-item" href="#">访问凭证</a>
683
+ </div>
684
+ </li>
685
+ </ul>
686
+ </div>
687
+
688
+ <div class="sidebar-divider"></div>
689
+ <div class="sidebar-footer">
690
+ <div class="sidebar-avatar">A</div>
691
+ <span class="sidebar-username">admin</span>
692
+ <button class="sidebar-more">
693
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor"><circle cx="12" cy="5" r="2"/><circle cx="12" cy="12" r="2"/><circle cx="12" cy="19" r="2"/></svg>
694
+ </button>
695
+ </div>
696
+ </aside>
697
+
698
+ <!-- ===== Main Content ===== -->
699
+ <main class="main-content">
700
+ <div class="page-container">
701
+ <div class="card">
702
+
703
+ <!-- PageHeader — 面包屑模式 -->
704
+ <div class="page-header">
705
+ <div class="page-header-left">
706
+ <nav class="breadcrumb">
707
+ <a href="#">路由管理</a>
708
+ <span class="separator"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M9 18l6-6-6-6"/></svg></span>
709
+ <a href="#">规则列表</a>
710
+ <span class="separator"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M9 18l6-6-6-6"/></svg></span>
711
+ <span class="current">default-route</span>
712
+ </nav>
713
+ </div>
714
+ <div class="page-header-right">
715
+ <button class="btn btn-primary">
716
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/><path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"/></svg>
717
+ 编辑
718
+ </button>
719
+ <button class="btn btn-outline">回滚</button>
720
+ <div class="dropdown" id="moreDropdown">
721
+ <button class="btn btn-outline btn-icon" onclick="toggleDropdown()">
722
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="1"/><circle cx="12" cy="5" r="1"/><circle cx="12" cy="19" r="1"/></svg>
723
+ </button>
724
+ <div class="dropdown-menu">
725
+ <div class="dropdown-item">复制规则</div>
726
+ <div class="dropdown-item">导出 YAML</div>
727
+ <div class="dropdown-item danger">删除规则</div>
728
+ </div>
729
+ </div>
730
+ <button class="btn btn-ghost btn-icon" title="关闭">
731
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M18 6L6 18M6 6l12 12"/></svg>
732
+ </button>
733
+ </div>
734
+ </div>
735
+
736
+ <!-- InfoCard — 嵌入模式B,仅 padding 控制间距 -->
737
+ <div class="info-card">
738
+ <div class="info-card-field">
739
+ <span class="info-card-field-label">规则名称:</span>
740
+ <span class="info-card-field-value">default-route</span>
741
+ </div>
742
+ <div class="info-card-field">
743
+ <span class="info-card-field-label">状态:</span>
744
+ <span class="badge badge-success"><span class="badge-dot"></span>已生效</span>
745
+ </div>
746
+ <div class="info-card-field">
747
+ <span class="info-card-field-label">当前版本:</span>
748
+ <span class="info-card-field-value">v3</span>
749
+ </div>
750
+ <div class="info-card-field">
751
+ <span class="info-card-field-label">最后修改:</span>
752
+ <span class="info-card-field-value">2026-05-25 14:30</span>
753
+ </div>
754
+ </div>
755
+
756
+ <!-- Tabs -->
757
+ <div class="tabs">
758
+ <ul class="tabs-list">
759
+ <li class="tab-item active" onclick="switchTab(0)">当前配置</li>
760
+ <li class="tab-item" onclick="switchTab(1)">版本历史</li>
761
+ <li class="tab-item" onclick="switchTab(2)">基本信息</li>
762
+ </ul>
763
+ </div>
764
+
765
+ <!-- Tab 0: 当前配置 — CodeBlock -->
766
+ <div class="tab-content active" id="tab-0">
767
+ <div class="code-block">
768
+ <button class="code-block-copy" title="复制">
769
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg>
770
+ </button>
771
+ <div class="code-block-body">
772
+ <div class="code-line-numbers">1
773
+ 2
774
+ 3
775
+ 4
776
+ 5
777
+ 6
778
+ 7
779
+ 8
780
+ 9
781
+ 10
782
+ 11
783
+ 12
784
+ 13
785
+ 14
786
+ 15
787
+ 16
788
+ 17
789
+ 18
790
+ 19
791
+ 20
792
+ 21
793
+ 22</div>
794
+ <div class="code-content"><span class="kw">apiVersion</span>: networking.k8s.io/v1
795
+ <span class="kw">kind</span>: HTTPRoute
796
+ <span class="kw">metadata</span>:
797
+ <span class="kw">name</span>: <span class="str">default-route</span>
798
+ <span class="kw">namespace</span>: <span class="str">ai-gateway</span>
799
+ <span class="kw">spec</span>:
800
+ <span class="kw">parentRefs</span>:
801
+ - <span class="kw">name</span>: <span class="str">ai-gateway</span>
802
+ <span class="kw">sectionName</span>: <span class="str">https</span>
803
+ <span class="kw">rules</span>:
804
+ - <span class="kw">matches</span>:
805
+ - <span class="kw">path</span>:
806
+ <span class="kw">type</span>: <span class="str">PathPrefix</span>
807
+ <span class="kw">value</span>: <span class="str">/v1/chat/completions</span>
808
+ <span class="kw">backendRefs</span>:
809
+ - <span class="kw">name</span>: <span class="str">openai-service</span>
810
+ <span class="kw">port</span>: <span class="num">8080</span>
811
+ <span class="kw">weight</span>: <span class="num">80</span>
812
+ - <span class="kw">name</span>: <span class="str">deepseek-service</span>
813
+ <span class="kw">port</span>: <span class="num">8080</span>
814
+ <span class="kw">weight</span>: <span class="num">20</span>
815
+ </div>
816
+ </div>
817
+ </div>
818
+ </div>
819
+
820
+ <!-- Tab 1: 版本历史 — Timeline + Diff -->
821
+ <div class="tab-content" id="tab-1">
822
+ <div class="version-history">
823
+ <!-- 左侧:时间线 -->
824
+ <div class="version-timeline">
825
+ <div class="version-item current">
826
+ <div class="version-dot"></div>
827
+ <div class="version-info">
828
+ <div class="version-header">
829
+ <span class="version-number">v3</span>
830
+ <span class="version-tag">当前</span>
831
+ </div>
832
+ <div class="version-meta">张三 · 2026-05-25 14:30</div>
833
+ <div class="version-msg">调整权重比例为 80:20</div>
834
+ </div>
835
+ </div>
836
+ <div class="version-item">
837
+ <div class="version-dot"></div>
838
+ <div class="version-info">
839
+ <div class="version-header">
840
+ <span class="version-number">v2</span>
841
+ </div>
842
+ <div class="version-meta">李四 · 2026-05-20 09:15</div>
843
+ <div class="version-msg">新增 deepseek-service 后端</div>
844
+ <div class="version-actions">
845
+ <button class="btn btn-ghost">回滚</button>
846
+ </div>
847
+ </div>
848
+ </div>
849
+ <div class="version-item">
850
+ <div class="version-dot"></div>
851
+ <div class="version-info">
852
+ <div class="version-header">
853
+ <span class="version-number">v1</span>
854
+ </div>
855
+ <div class="version-meta">张三 · 2026-05-15 16:00</div>
856
+ <div class="version-msg">初始创建路由规则</div>
857
+ <div class="version-actions">
858
+ <button class="btn btn-ghost">回滚</button>
859
+ </div>
860
+ </div>
861
+ </div>
862
+ </div>
863
+
864
+ <!-- 右侧:ConfigDiff -->
865
+ <div class="config-diff">
866
+ <div class="config-diff-header">v2 → v3 配置对比</div>
867
+ <div class="config-diff-body">
868
+ <div class="config-diff-panel">
869
+ <div class="config-diff-panel-header">v2 (旧版本)</div>
870
+ <div class="diff-line context"> backendRefs:</div>
871
+ <div class="diff-line context"> - name: openai-service</div>
872
+ <div class="diff-line context"> port: 8080</div>
873
+ <div class="diff-line removed"> weight: 50</div>
874
+ <div class="diff-line context"> - name: deepseek-service</div>
875
+ <div class="diff-line context"> port: 8080</div>
876
+ <div class="diff-line removed"> weight: 50</div>
877
+ </div>
878
+ <div class="config-diff-panel">
879
+ <div class="config-diff-panel-header">v3 (新版本)</div>
880
+ <div class="diff-line context"> backendRefs:</div>
881
+ <div class="diff-line context"> - name: openai-service</div>
882
+ <div class="diff-line context"> port: 8080</div>
883
+ <div class="diff-line added"> weight: 80</div>
884
+ <div class="diff-line context"> - name: deepseek-service</div>
885
+ <div class="diff-line context"> port: 8080</div>
886
+ <div class="diff-line added"> weight: 20</div>
887
+ </div>
888
+ </div>
889
+ <div class="config-diff-footer">
890
+ <span class="diff-stat-add">+2 行</span>
891
+ <span class="diff-stat-del">-2 行</span>
892
+ </div>
893
+ </div>
894
+ </div>
895
+ </div>
896
+
897
+ <!-- Tab 2: 基本信息 — DescriptionList -->
898
+ <div class="tab-content" id="tab-2">
899
+ <div class="description-list">
900
+ <div class="description-item">
901
+ <span class="description-label">规则名称</span>
902
+ <span class="description-value copyable">
903
+ default-route
904
+ <svg class="copy-icon" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg>
905
+ </span>
906
+ </div>
907
+ <div class="description-item">
908
+ <span class="description-label">命名空间</span>
909
+ <span class="description-value">ai-gateway</span>
910
+ </div>
911
+ <div class="description-item">
912
+ <span class="description-label">关联网关</span>
913
+ <span class="description-value link">ai-gateway-prod</span>
914
+ </div>
915
+ <div class="description-item">
916
+ <span class="description-label">匹配路径</span>
917
+ <span class="description-value copyable">
918
+ /v1/chat/completions
919
+ <svg class="copy-icon" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg>
920
+ </span>
921
+ </div>
922
+ <div class="description-item">
923
+ <span class="description-label">后端服务</span>
924
+ <span class="description-value"><span class="link" style="margin-right:4px">openai-service</span><span class="link">deepseek-service</span></span>
925
+ </div>
926
+ <div class="description-item">
927
+ <span class="description-label">创建时间</span>
928
+ <span class="description-value">2026-05-15 16:00:00</span>
929
+ </div>
930
+ <div class="description-item">
931
+ <span class="description-label">最后修改</span>
932
+ <span class="description-value">2026-05-25 14:30:00</span>
933
+ </div>
934
+ <div class="description-item">
935
+ <span class="description-label">修改人</span>
936
+ <span class="description-value">张三</span>
937
+ </div>
938
+ <div class="description-item">
939
+ <span class="description-label">描述</span>
940
+ <span class="description-value">AI 聊天补全接口的默认路由规则</span>
941
+ </div>
942
+ </div>
943
+ </div>
944
+
945
+ </div><!-- /card -->
946
+ </div><!-- /page-container -->
947
+ </main>
948
+
949
+ <script>
950
+ // Sidebar collapse toggle
951
+ const sidebar = document.querySelector('.sidebar');
952
+ document.querySelector('.sidebar-collapse-btn').addEventListener('click', function() {
953
+ sidebar.classList.toggle('collapsed');
954
+ });
955
+ // Sidebar expand via logo click (collapsed state)
956
+ document.getElementById('sidebarLogoArea').addEventListener('click', function() {
957
+ if (sidebar.classList.contains('collapsed')) {
958
+ sidebar.classList.remove('collapsed');
959
+ }
960
+ });
961
+
962
+ // Sidebar sub-menu toggle
963
+ document.querySelectorAll('.sidebar-menu-item[data-toggle]').forEach(item => {
964
+ item.addEventListener('click', function(e) {
965
+ e.preventDefault();
966
+ this.classList.toggle('expanded');
967
+ const targetId = this.getAttribute('data-toggle');
968
+ const target = document.getElementById(targetId);
969
+ if (target) target.classList.toggle('show');
970
+ });
971
+ });
972
+
973
+ function toggleDropdown() {
974
+ document.getElementById('moreDropdown').classList.toggle('open');
975
+ }
976
+
977
+ document.addEventListener('click', (e) => {
978
+ if (!e.target.closest('#moreDropdown')) {
979
+ document.getElementById('moreDropdown').classList.remove('open');
980
+ }
981
+ });
982
+
983
+ function switchTab(index) {
984
+ document.querySelectorAll('.tab-item').forEach((t, i) => {
985
+ t.classList.toggle('active', i === index);
986
+ });
987
+ document.querySelectorAll('.tab-content').forEach((c, i) => {
988
+ c.classList.toggle('active', i === index);
989
+ });
990
+ }
991
+ </script>
992
+ </body>
993
+ </html>