@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,1119 @@
1
+ <!DOCTYPE html>
2
+ <html lang="zh-CN">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=1400">
6
+ <title>配置表单页 · 标准参考范例 — v7.8.4</title>
7
+ <!--
8
+ ★ 配置表单页标准参考范例(Standard Settings Form Reference)
9
+ ─────────────────────────────────────────────────────────────
10
+ 用途:AI 生成配置/创建表单页时的基础参考,展示分组表单骨架与组件组合
11
+ ─────────────────────────────────────────────────────────────
12
+ 布局:TWO_COL(Sidebar + ContentCard 单例容器)
13
+ Sidebar:完整 L1 框架(折叠/展开 240px↔68px / flyout / sub-menu)
14
+ 内容区模式:D-FORM(settings-form 分组表单)
15
+ ─────────────────────────────────────────────────────────────
16
+ 组件清单:
17
+ • Breadcrumb — 2级面包屑(L2 模式,禁止标题组件)
18
+ • Settings Section — 分组表单(title + description + form-rows)
19
+ • Form Controls — Input / Select / Stepper / Switch
20
+ • Settings Footer — 固定底栏(取消 + 提交)
21
+ ─────────────────────────────────────────────────────────────
22
+ 规范来源:pages/form-page/SKILL.md
23
+ Tokens 版本:v7.8.4
24
+ -->
25
+ <style>
26
+ /* ===== Reset & v7 Design Tokens ===== */
27
+ *, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }
28
+ :root {
29
+ /* Primary */
30
+ --primary: 218.6 100% 46.7%;
31
+ --primary-foreground: 0 0% 100%;
32
+ --primary-hover: 218.6 100% 42%;
33
+
34
+ /* Gray (v7.4) */
35
+ --gray-primary: 220 9% 7%;
36
+ --gray-primary-foreground: 210 33% 99%;
37
+ --gray-secondary-foreground: 216 4% 26%;
38
+ --muted-foreground: 216 2% 45%;
39
+ --gray-disabled: 210 3% 63%;
40
+ --gray-line: 210 9% 91%;
41
+ --gray-sidebar-accent: 210 12% 93%;
42
+ --gray-muted: 220 18% 97%;
43
+ --gray-white: 0 0% 100%;
44
+ /* Legacy 别名(v7.4 向后兼容) */
45
+ --gray-title: 220 9% 7%;
46
+ --gray-secondary: 216 4% 26%;
47
+ --gray-tertiary: 216 2% 45%;
48
+ --gray-border: 210 9% 91%;
49
+ --gray-fill: 220 18% 97%;
50
+ --gray-bg: 220 18% 97%;
51
+
52
+ /* Feedback */
53
+ --success: 142 76% 36%;
54
+ --success-bg: 145 65% 95%;
55
+ --success-border: 142 72% 73%;
56
+ --warning: 40 88% 48%;
57
+ --warning-bg: 40 95% 93%;
58
+ --warning-border: 44 92% 70%;
59
+ --destructive: 0 72% 51%;
60
+ --destructive-bg: 0 93% 96%;
61
+ --destructive-border: 0 91% 82%;
62
+ --info: 217 91% 60%;
63
+ --info-bg: 214 85% 95%;
64
+ --info-border: 214 89% 82%;
65
+
66
+ /* Semantic */
67
+ --background: 220 18% 97%;
68
+ --card: 0 0% 100%;
69
+ --ring: 218.6 100% 46.7%;
70
+ --sidebar: 220 18% 97%;
71
+ --sidebar-foreground: 220 9% 7%;
72
+ --sidebar-active: 210 12% 93%;
73
+ --sidebar-active-foreground: 220 9% 7%;
74
+ --sidebar-hover: 220 18% 97%;
75
+ --sidebar-group: 216 2% 45%;
76
+ --sidebar-item: 220 9% 7%;
77
+ --sidebar-item-muted: 216 4% 26%;
78
+ --sidebar-border: 210 9% 91%;
79
+
80
+ /* Radius */
81
+ --radius-sm: 4px;
82
+ --radius-md: 8px;
83
+ --radius: 12px;
84
+ --radius-lg: 16px;
85
+
86
+ /* Spacing */
87
+ --gap-xs: 4px;
88
+ --gap-sm: 8px;
89
+ --gap-md: 12px;
90
+ --gap-lg: 16px;
91
+ --gap-xl: 20px;
92
+ --gap-2xl: 24px;
93
+ --btn-padding-x: 16px;
94
+ --btn-padding-x-sm: 12px;
95
+ --button-gap: 8px;
96
+ --tabs-gap: 24px;
97
+
98
+ /* Typography */
99
+ --font-size-xs: 12px;
100
+ --font-size-base: 12px;
101
+ --font-size-sm: 13px;
102
+ --font-size-lg: 14px;
103
+ --font-size-xl: 16px;
104
+ --font-size-2xl: 18px;
105
+ --font-size-page-header: 18px;
106
+ --font-sans: 'PingFang SC', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', sans-serif;
107
+ --font-mono: 'SF Mono', Monaco, 'Courier New', monospace;
108
+ --font-weight-normal: 400;
109
+ --font-weight-medium: 500;
110
+ --font-weight-semibold: 600;
111
+ --font-weight-black: 900;
112
+
113
+ /* Layout(分层模型 v7.5)*/
114
+ --layout-sidebar-width: 240px;
115
+ --sidebar-collapsed-width: 68px;
116
+ --page-container-padding: 16px; /* 层级 1:PageContainer 外层间距(v7.5 由 20→16) */
117
+ --page-container-padding-left: 0px; /* sidebar 与内容区之间无间距 */
118
+ --card-padding-x: 20px; /* 层级 2:白卡容器内部左右内边距(v7.5 由 24→20;独立于 page-container-padding) */
119
+ --shadow-sm: 0 1px 2px rgba(0,0,0,0.05); /* v7.6 ContentCard 唯一阴影源(无边框方案) */
120
+ --card-gap: 16px;
121
+
122
+ /* Transitions */
123
+ --duration-fast: 150ms;
124
+ --duration-normal: 200ms;
125
+ --easing-ease: ease;
126
+ }
127
+
128
+ body {
129
+ font-family: var(--font-sans);
130
+ font-size: var(--font-size-lg);
131
+ color: hsl(var(--gray-primary));
132
+ background: hsl(var(--background));
133
+ line-height: 1.5;
134
+ }
135
+
136
+ /* ===== Sidebar (v7 fixed positioning, twoLevel variant) ===== */
137
+ .sidebar {
138
+ position: fixed;
139
+ top: 0; left: 0; bottom: 0;
140
+ width: var(--layout-sidebar-width);
141
+ background: hsl(var(--sidebar));
142
+ z-index: 100;
143
+ display: flex;
144
+ flex-direction: column;
145
+ overflow: hidden;
146
+ transition: width 0.2s ease;
147
+ }
148
+
149
+ /* Sidebar Header */
150
+ .sidebar-header {
151
+ height: 56px; padding: 0 16px;
152
+ display: flex; align-items: center; gap: var(--gap-xs);
153
+ flex-shrink: 0;
154
+ }
155
+ .sidebar-logo {
156
+ height: 28px; flex-shrink: 0;
157
+ display: flex; align-items: center;
158
+ overflow: hidden;
159
+ }
160
+ .sidebar-logo img {
161
+ height: 28px; width: auto; display: block;
162
+ }
163
+ .sidebar-logo .collapsed-logo {
164
+ display: none; width: 30px; height: 30px;
165
+ }
166
+ .sidebar-logo .expand-logo {
167
+ display: none; width: 18px; height: 18px;
168
+ color: hsl(var(--muted-foreground));
169
+ }
170
+ .sidebar-collapse-btn {
171
+ margin-left: auto; width: 18px; height: 18px;
172
+ background: none; border: none; padding: 0; cursor: pointer;
173
+ display: flex; align-items: center; justify-content: center;
174
+ color: hsl(var(--muted-foreground)); flex-shrink: 0;
175
+ }
176
+ .sidebar-collapse-btn svg { width: 18px; height: 18px; }
177
+
178
+ /* Sidebar Content */
179
+ .sidebar-content {
180
+ flex: 1;
181
+ flex-shrink: 0;
182
+ overflow-y: auto;
183
+ padding: 12px 0;
184
+ }
185
+ .sidebar-menu {
186
+ list-style: none;
187
+ padding: 0 8px;
188
+ display: flex;
189
+ flex-direction: column;
190
+ gap: 2px;
191
+ }
192
+ .sidebar-menu-item {
193
+ display: flex;
194
+ align-items: center;
195
+ gap: var(--button-gap);
196
+ padding: 0 12px;
197
+ height: 40px;
198
+ border-radius: var(--radius-md);
199
+ font-size: var(--font-size-lg);
200
+ font-weight: var(--font-weight-medium);
201
+ color: hsl(var(--sidebar-item-muted));
202
+ text-decoration: none;
203
+ cursor: pointer;
204
+ transition: background var(--duration-fast), color var(--duration-fast);
205
+ white-space: nowrap;
206
+ }
207
+ .sidebar-menu-item:hover {
208
+ background: hsl(var(--sidebar-hover));
209
+ color: hsl(var(--sidebar-item));
210
+ }
211
+ .sidebar-menu-item.active {
212
+ background: hsl(var(--sidebar-active));
213
+ color: hsl(var(--sidebar-active-foreground));
214
+ }
215
+
216
+ /* 展开箭头 */
217
+ .sidebar-menu-item .expand-icon {
218
+ margin-left: auto;
219
+ width: 16px;
220
+ height: 16px;
221
+ transition: transform var(--duration-fast);
222
+ }
223
+ .sidebar-menu-item.expanded .expand-icon {
224
+ transform: rotate(90deg);
225
+ }
226
+
227
+ /* 子菜单容器 - twoLevel变体对齐规则 */
228
+ .sidebar-sub-menu {
229
+ list-style: none;
230
+ padding: 0 0 0 24px;
231
+ display: none;
232
+ }
233
+ .sidebar-menu > li > .sidebar-sub-menu {
234
+ padding-left: 0;
235
+ }
236
+ .sidebar-sub-menu.show {
237
+ display: block;
238
+ }
239
+
240
+ /* 子菜单项:36px = padding(12) + icon(16) + gap(8) */
241
+ .sidebar-sub-menu .sidebar-menu-item {
242
+ padding-left: 36px;
243
+ height: 36px;
244
+ font-size: var(--font-size-base);
245
+ font-weight: var(--font-weight-normal);
246
+ }
247
+
248
+ /* 图标 */
249
+ .item-icon {
250
+ width: 16px;
251
+ height: 16px;
252
+ display: flex;
253
+ align-items: center;
254
+ justify-content: center;
255
+ flex-shrink: 0;
256
+ }
257
+ .item-icon svg {
258
+ width: 16px;
259
+ height: 16px;
260
+ }
261
+
262
+ /* ===== Sidebar Collapsed State ===== */
263
+ .sidebar.collapsed { width: 68px; overflow: visible; }
264
+ .sidebar.collapsed .sidebar-header { padding: 0 12px; justify-content: center; }
265
+ .sidebar.collapsed .sidebar-logo { cursor: pointer; }
266
+ .sidebar.collapsed .sidebar-logo img { display: none; }
267
+ .sidebar.collapsed .sidebar-logo .collapsed-logo { display: block; }
268
+ .sidebar.collapsed .sidebar-logo:hover .collapsed-logo { display: none; }
269
+ .sidebar.collapsed .sidebar-logo:hover .expand-logo { display: block; }
270
+ .sidebar.collapsed .sidebar-collapse-btn { display: none; }
271
+ .sidebar.collapsed .sidebar-content { padding: 12px 0; overflow-y: auto; overflow-x: hidden; }
272
+ .sidebar.collapsed .sidebar-menu { padding: 0; }
273
+ .sidebar.collapsed .sidebar-menu > li { display: flex; justify-content: center; }
274
+ .sidebar.collapsed .sidebar-menu > li > a.sidebar-menu-item {
275
+ justify-content: center; padding: 0; width: 40px; height: 40px;
276
+ border-radius: var(--radius-md); position: relative; flex-shrink: 0;
277
+ gap: 0;
278
+ }
279
+ .sidebar.collapsed .sidebar-menu > li > a.sidebar-menu-item .item-icon { margin: 0; }
280
+ .sidebar.collapsed .sidebar-menu > li > a.sidebar-menu-item > span:not(.item-icon),
281
+ .sidebar.collapsed .sidebar-menu > li > a.sidebar-menu-item > .expand-icon,
282
+ .sidebar.collapsed .sidebar-menu > li > a.sidebar-menu-item:not(.item-icon):after { display: none; }
283
+ /* 隐藏文字节点(直接文本) */
284
+ .sidebar.collapsed .sidebar-menu > li > a { font-size: 0; }
285
+ .sidebar.collapsed .sidebar-menu > li > a > .item-icon { font-size: var(--font-size-lg); }
286
+ .sidebar.collapsed .sidebar-sub-menu { display: none !important; }
287
+ .sidebar.collapsed .sidebar-divider { margin: 0 14px; }
288
+ .sidebar.collapsed .sidebar-footer { padding: 12px 10px 16px; justify-content: center; }
289
+ .sidebar.collapsed .sidebar-username,
290
+ .sidebar.collapsed .sidebar-more { display: none; }
291
+
292
+ /* ===== Sidebar Collapsed Flyout (hover 浮层展示二级菜单) ===== */
293
+ .sidebar.collapsed .sidebar-menu > li { position: relative; }
294
+ .sidebar.collapsed .sidebar-menu > li:hover > .sidebar-flyout {
295
+ display: block;
296
+ }
297
+ .sidebar-flyout {
298
+ display: none;
299
+ position: absolute; left: 100%; top: 0;
300
+ min-width: 160px; padding: 4px;
301
+ background: hsl(var(--card));
302
+ border: 1px solid hsl(var(--gray-line));
303
+ border-radius: var(--radius-md);
304
+ box-shadow: 0 4px 12px rgba(0,0,0,0.08);
305
+ z-index: 200;
306
+ }
307
+ .sidebar-flyout .sidebar-menu-item {
308
+ height: 36px; padding: 0 12px;
309
+ font-size: var(--font-size-base) !important;
310
+ font-weight: var(--font-weight-normal);
311
+ color: hsl(var(--gray-primary));
312
+ border-radius: var(--radius-sm);
313
+ white-space: nowrap;
314
+ }
315
+ .sidebar-flyout .sidebar-menu-item:hover {
316
+ background: hsl(var(--gray-muted));
317
+ }
318
+
319
+ /* Sidebar Separator */
320
+ .sidebar-divider {
321
+ height: 1px;
322
+ background: hsl(var(--sidebar-border));
323
+ margin: 0 12px;
324
+ flex-shrink: 0;
325
+ }
326
+
327
+ /* Sidebar Footer */
328
+ .sidebar-footer {
329
+ flex-shrink: 0;
330
+ display: flex;
331
+ align-items: center;
332
+ gap: 8px;
333
+ padding: 12px 12px 16px 24px;
334
+ }
335
+ .sidebar-avatar {
336
+ width: 28px; height: 28px; border-radius: 50%;
337
+ background: hsl(var(--sidebar-active));
338
+ display: flex; align-items: center; justify-content: center;
339
+ font-size: var(--font-size-base); font-weight: 600;
340
+ color: hsl(var(--gray-primary)); flex-shrink: 0;
341
+ }
342
+ .sidebar-username {
343
+ flex: 1; font-size: var(--font-size-lg); font-weight: var(--font-weight-medium);
344
+ color: hsl(var(--sidebar-item)); overflow: hidden;
345
+ text-overflow: ellipsis; white-space: nowrap;
346
+ }
347
+ .sidebar-more {
348
+ width: 28px; height: 28px; border-radius: var(--radius-sm);
349
+ border: none; background: transparent; cursor: pointer;
350
+ display: flex; align-items: center; justify-content: center;
351
+ color: hsl(var(--muted-foreground));
352
+ }
353
+ .sidebar-more:hover { background: hsl(var(--sidebar-hover)); color: hsl(var(--gray-primary)); }
354
+
355
+ /* ===== PageContainer ===== */
356
+ .page-container {
357
+ margin-left: var(--layout-sidebar-width);
358
+ padding: var(--page-container-padding) var(--page-container-padding) var(--page-container-padding) var(--page-container-padding-left);
359
+ min-height: 100vh;
360
+ transition: margin-left 0.2s ease;
361
+ }
362
+ .sidebar.collapsed ~ .page-container { margin-left: 68px; }
363
+
364
+ /* ===== Breadcrumb ===== */
365
+ .breadcrumb {
366
+ display: flex;
367
+ align-items: center;
368
+ gap: 4px;
369
+ font-size: var(--font-size-2xl);
370
+ font-weight: var(--font-weight-medium);
371
+ color: hsl(var(--gray-secondary-foreground));
372
+ padding: 0;
373
+ height: 64px;
374
+ flex-shrink: 0;
375
+ margin-bottom: 0; /* Breadcrumb 底边直接与 section 顶边贴合 */
376
+ }
377
+ .breadcrumb a {
378
+ color: hsl(var(--gray-secondary-foreground));
379
+ text-decoration: none;
380
+ max-width: 160px;
381
+ overflow: hidden;
382
+ text-overflow: ellipsis;
383
+ white-space: nowrap;
384
+ }
385
+ .breadcrumb a:hover { color: hsl(var(--gray-primary)); }
386
+ .breadcrumb-sep {
387
+ display: flex;
388
+ align-items: center;
389
+ color: hsl(var(--muted-foreground));
390
+ }
391
+ .breadcrumb-sep svg { width: 20px; height: 20px; }
392
+ .breadcrumb-current {
393
+ color: hsl(var(--gray-primary));
394
+ font-weight: var(--font-weight-medium);
395
+ cursor: default;
396
+ }
397
+
398
+ /* ===== PageHeader — L2面包屑模式下禁止标题组件,已移除 ===== */
399
+
400
+ /* ===== ContentWrapper + Card (白卡容器) ===== */
401
+ .content-wrapper {
402
+ /* wraps the card */
403
+ }
404
+ .card {
405
+ background: hsl(var(--card));
406
+ border-radius: var(--radius-lg);
407
+ /* v7.6:去掉 1px border,使用最弱阴影 var(--shadow-sm) */
408
+ box-shadow: var(--shadow-sm, 0 1px 2px rgba(0,0,0,0.05));
409
+ overflow: hidden;
410
+ display: flex;
411
+ flex-direction: column;
412
+ max-height: calc(100vh - 2 * var(--page-container-padding));
413
+ padding-left: var(--card-padding-x);
414
+ padding-right: var(--card-padding-x);
415
+ }
416
+ .card-scrollable {
417
+ overflow-y: auto;
418
+ flex: 1 1 auto;
419
+ min-height: 0;
420
+ }
421
+
422
+ /* ===== Section (分区) ===== */
423
+ .settings-section {
424
+ padding: var(--card-gap) 0;
425
+ }
426
+ .settings-section + .settings-section {
427
+ border-top: 1px solid hsl(var(--gray-line));
428
+ }
429
+ .section-title {
430
+ font-size: var(--font-size-xl);
431
+ font-weight: var(--font-weight-semibold);
432
+ color: hsl(var(--gray-primary));
433
+ margin-bottom: var(--gap-lg);
434
+ }
435
+ .section-desc {
436
+ font-size: var(--font-size-sm);
437
+ color: hsl(var(--gray-secondary-foreground));
438
+ margin-bottom: var(--gap-xl);
439
+ }
440
+
441
+ /* ===== Form Row ===== */
442
+ .form-row {
443
+ display: flex;
444
+ align-items: center;
445
+ gap: var(--gap-2xl);
446
+ padding: 12px 0;
447
+ }
448
+ .form-row + .form-row {
449
+ border-top: none;
450
+ }
451
+ .form-label {
452
+ width: 140px;
453
+ flex-shrink: 0;
454
+ font-size: var(--font-size-lg);
455
+ font-weight: var(--font-weight-medium);
456
+ color: hsl(var(--gray-primary));
457
+ }
458
+ .form-label .required {
459
+ color: hsl(var(--destructive));
460
+ margin-left: 2px;
461
+ }
462
+ .form-control {
463
+ flex: 1;
464
+ min-width: 0;
465
+ max-width: 400px;
466
+ }
467
+
468
+ /* ===== Input ===== */
469
+ .input {
470
+ height: 32px;
471
+ padding: 0 8px;
472
+ border: 1px solid hsl(var(--gray-line));
473
+ border-radius: var(--radius-md);
474
+ font-size: var(--font-size-base);
475
+ color: hsl(var(--gray-primary));
476
+ background: hsl(var(--card));
477
+ outline: none;
478
+ transition: border-color var(--duration-fast), box-shadow var(--duration-fast);
479
+ width: 100%;
480
+ }
481
+ .input:focus {
482
+ border-color: hsl(var(--primary));
483
+ box-shadow: 0 0 0 2px hsl(var(--ring) / 0.2);
484
+ }
485
+ .input::placeholder { color: hsl(var(--muted-foreground)); }
486
+ .input[disabled] {
487
+ background: hsl(var(--gray-muted));
488
+ color: hsl(var(--gray-disabled));
489
+ cursor: not-allowed;
490
+ }
491
+ textarea.input {
492
+ height: auto;
493
+ padding: 8px 12px;
494
+ resize: vertical;
495
+ min-height: 80px;
496
+ }
497
+ /* ===== Custom Select (@teamix-evo/ui C23-Select) ===== */
498
+ .select-wrapper {
499
+ position: relative;
500
+ width: 100%;
501
+ }
502
+ .select-trigger {
503
+ height: 32px;
504
+ padding: 0 8px;
505
+ padding-right: 28px;
506
+ border: 1px solid hsl(var(--gray-line));
507
+ border-radius: var(--radius-md);
508
+ background: hsl(var(--card));
509
+ font-size: var(--font-size-base);
510
+ color: hsl(var(--gray-primary));
511
+ display: flex;
512
+ align-items: center;
513
+ width: 100%;
514
+ cursor: pointer;
515
+ outline: none;
516
+ transition: border-color var(--duration-fast), box-shadow var(--duration-fast);
517
+ position: relative;
518
+ }
519
+ .select-trigger:focus {
520
+ border-color: hsl(var(--primary));
521
+ box-shadow: 0 0 0 2px hsl(var(--ring) / 0.2);
522
+ }
523
+ .select-trigger::after {
524
+ content: '';
525
+ position: absolute;
526
+ right: 7px;
527
+ top: 50%;
528
+ transform: translateY(-50%);
529
+ width: 14px;
530
+ height: 14px;
531
+ background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='14' viewBox='0 0 24 24' fill='none' stroke='%231A1A1A' stroke-width='2' stroke-linecap='round' stroke-linejoin='round' opacity='0.8'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
532
+ background-size: contain;
533
+ background-repeat: no-repeat;
534
+ pointer-events: none;
535
+ }
536
+ .select-trigger.placeholder {
537
+ color: hsl(var(--muted-foreground));
538
+ }
539
+ .select-content {
540
+ display: none;
541
+ position: absolute;
542
+ z-index: 50;
543
+ top: calc(100% + 4px);
544
+ left: 0;
545
+ right: 0;
546
+ background: hsl(var(--card));
547
+ border: 1px solid hsl(var(--gray-line));
548
+ border-radius: var(--radius-md);
549
+ box-shadow: 0 6px 24px rgba(0,0,0,0.1);
550
+ padding: 8px;
551
+ min-width: 100%;
552
+ }
553
+ .select-content.show {
554
+ display: block;
555
+ }
556
+ .select-item {
557
+ height: 32px;
558
+ padding: 0 8px;
559
+ border-radius: var(--radius-sm);
560
+ font-size: var(--font-size-base);
561
+ color: hsl(var(--gray-primary));
562
+ display: flex;
563
+ align-items: center;
564
+ cursor: pointer;
565
+ user-select: none;
566
+ }
567
+ .select-item:hover {
568
+ background: hsl(var(--gray-muted));
569
+ }
570
+ .select-item.selected {
571
+ color: hsl(var(--primary));
572
+ font-weight: var(--font-weight-medium);
573
+ }
574
+ .select-item[data-disabled] {
575
+ opacity: 0.45;
576
+ pointer-events: none;
577
+ cursor: not-allowed;
578
+ }
579
+
580
+ /* ===== Switch ===== */
581
+ .switch {
582
+ position: relative;
583
+ width: 28px;
584
+ height: 16px;
585
+ cursor: pointer;
586
+ }
587
+ .switch input { display: none; }
588
+ .switch-track {
589
+ position: absolute;
590
+ inset: 0;
591
+ background: hsl(var(--gray-line));
592
+ border-radius: 8px;
593
+ transition: background var(--duration-fast);
594
+ }
595
+ .switch input:checked + .switch-track {
596
+ background: hsl(var(--success));
597
+ }
598
+ .switch-thumb {
599
+ position: absolute;
600
+ top: 2px; left: 2px;
601
+ width: 12px; height: 12px;
602
+ background: white;
603
+ border-radius: 50%;
604
+ box-shadow: 0 1px 3px rgba(0,0,0,0.15);
605
+ transition: transform var(--duration-fast);
606
+ pointer-events: none;
607
+ }
608
+ .switch input:checked ~ .switch-thumb {
609
+ transform: translateX(12px);
610
+ }
611
+
612
+ /* ===== Buttons ===== */
613
+ .btn {
614
+ display: inline-flex;
615
+ align-items: center;
616
+ justify-content: center;
617
+ height: 36px;
618
+ padding: 0 var(--btn-padding-x);
619
+ border-radius: var(--radius-md);
620
+ font-size: var(--font-size-lg);
621
+ font-weight: var(--font-weight-medium);
622
+ cursor: pointer;
623
+ border: 1px solid transparent;
624
+ transition: all var(--duration-fast);
625
+ white-space: nowrap;
626
+ gap: 6px;
627
+ }
628
+ .btn-primary {
629
+ background: hsl(var(--primary));
630
+ color: hsl(var(--primary-foreground));
631
+ }
632
+ .btn-primary:hover { background: hsl(var(--primary-hover)); }
633
+ .btn-secondary {
634
+ background: hsl(var(--card));
635
+ color: hsl(var(--gray-primary));
636
+ border-color: hsl(var(--gray-line));
637
+ }
638
+ .btn-secondary:hover { background: hsl(var(--gray-muted)); }
639
+ .btn-ghost {
640
+ background: transparent;
641
+ color: hsl(var(--gray-secondary-foreground));
642
+ border: none;
643
+ }
644
+ .btn-ghost:hover { background: hsl(var(--gray-muted)); color: hsl(var(--gray-primary)); }
645
+ .btn-danger {
646
+ background: hsl(var(--destructive));
647
+ color: white;
648
+ }
649
+ .btn-danger:hover { background: hsl(var(--destructive) / 0.9); }
650
+ .btn:disabled {
651
+ opacity: 0.5;
652
+ cursor: not-allowed;
653
+ }
654
+
655
+ /* ===== Action footer (SubmitBar) — justify-content: flex-end,行动点居右 ===== */
656
+ .settings-footer {
657
+ display: flex;
658
+ gap: var(--gap-sm);
659
+ height: 72px;
660
+ align-items: center;
661
+ border-top: 1px solid hsl(var(--gray-line));
662
+ background: hsl(var(--card));
663
+ justify-content: flex-end;
664
+ flex-shrink: 0;
665
+ z-index: 10;
666
+ /* 分割线出血:负外边距抵消 Card.paddingX,使 border-top 延伸到 Card 左右边缘 */
667
+ margin-left: calc(-1 * var(--card-padding-x));
668
+ margin-right: calc(-1 * var(--card-padding-x));
669
+ padding-left: var(--card-padding-x);
670
+ padding-right: var(--card-padding-x);
671
+ }
672
+
673
+ /* ===== Stepper ===== */
674
+ .stepper {
675
+ display: flex;
676
+ align-items: center;
677
+ gap: 0;
678
+ }
679
+ .stepper-btn {
680
+ width: 32px;
681
+ height: 32px;
682
+ border: 1px solid hsl(var(--border, var(--gray-line)));
683
+ background: hsl(var(--card));
684
+ cursor: pointer;
685
+ display: flex;
686
+ align-items: center;
687
+ justify-content: center;
688
+ font-size: 16px;
689
+ color: hsl(var(--gray-primary));
690
+ }
691
+ .stepper-btn:first-child { border-radius: var(--radius-md) 0 0 var(--radius-md); }
692
+ .stepper-btn:last-child { border-radius: 0 var(--radius-md) var(--radius-md) 0; }
693
+ .stepper input {
694
+ width: 60px;
695
+ height: 32px;
696
+ text-align: center;
697
+ border: 1px solid hsl(var(--gray-line));
698
+ border-left: none;
699
+ border-right: none;
700
+ font-size: var(--font-size-lg);
701
+ color: hsl(var(--gray-primary));
702
+ outline: none;
703
+ }
704
+
705
+ /* ===== Optional Tag ===== */
706
+ .optional-tag {
707
+ font-size: 12px;
708
+ color: hsl(var(--gray-secondary-foreground));
709
+ font-weight: normal;
710
+ margin-left: 4px;
711
+ }
712
+
713
+ /* ===== Form Description ===== */
714
+ .form-description {
715
+ font-size: 13px;
716
+ color: hsl(var(--gray-secondary-foreground));
717
+ margin-top: 4px;
718
+ }
719
+ </style>
720
+ </head>
721
+ <body>
722
+
723
+ <!-- ===== Sidebar (v7: fixed, ul>li>a, pill items) ===== -->
724
+ <aside class="sidebar">
725
+ <div class="sidebar-header">
726
+ <div class="sidebar-logo" id="sidebarLogoArea">
727
+ <img src="./_assets/op-ai-gateway-logo.svg" alt="OP AI Gateway" />
728
+ <img class="collapsed-logo" src="./_assets/op-logo-collapsed.png" alt="OP" />
729
+ <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>
730
+ </div>
731
+ <button class="sidebar-collapse-btn" title="收起侧边栏">
732
+ <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>
733
+ </button>
734
+ </div>
735
+
736
+ <!-- sidebar:按 PRD 导航结构,L1 为一级菜单,L2 为二级子菜单。 -->
737
+ <div class="sidebar-content">
738
+ <ul class="sidebar-menu">
739
+ <!-- L1: AI 网关(叶子菜单,当前活跃) -->
740
+ <li><a class="sidebar-menu-item active" href="#">
741
+ <span class="item-icon">
742
+ <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>
743
+ </span>
744
+ AI 网关
745
+ </a></li>
746
+
747
+ <!-- L1: API 网关(父菜单) -->
748
+ <li>
749
+ <a class="sidebar-menu-item expanded" href="#" data-toggle="api-gateway">
750
+ <span class="item-icon">
751
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M4 6h16M4 12h16M4 18h16"/></svg>
752
+ </span>
753
+ API 网关
754
+ <span class="expand-icon">
755
+ <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>
756
+ </span>
757
+ </a>
758
+ <ul class="sidebar-sub-menu show" id="api-gateway">
759
+ <li><a class="sidebar-menu-item" href="#">实例管理</a></li>
760
+ <li><a class="sidebar-menu-item" href="#">共享实例管理</a></li>
761
+ </ul>
762
+ <div class="sidebar-flyout">
763
+ <a class="sidebar-menu-item" href="#">实例管理</a>
764
+ <a class="sidebar-menu-item" href="#">共享实例管理</a>
765
+ </div>
766
+ </li>
767
+
768
+ <!-- L1: 云原生网关(父菜单) -->
769
+ <li>
770
+ <a class="sidebar-menu-item" href="#" data-toggle="cloud-native-gw">
771
+ <span class="item-icon">
772
+ <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>
773
+ </span>
774
+ 云原生网关
775
+ <span class="expand-icon">
776
+ <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>
777
+ </span>
778
+ </a>
779
+ <ul class="sidebar-sub-menu" id="cloud-native-gw">
780
+ <li><a class="sidebar-menu-item" href="#">实例列表</a></li>
781
+ <li><a class="sidebar-menu-item" href="#">Nginx Ingress 迁移</a></li>
782
+ </ul>
783
+ <div class="sidebar-flyout">
784
+ <a class="sidebar-menu-item" href="#">实例列表</a>
785
+ <a class="sidebar-menu-item" href="#">Nginx Ingress 迁移</a>
786
+ </div>
787
+ </li>
788
+
789
+ <!-- L1: 集群管理(叶子菜单) -->
790
+ <li><a class="sidebar-menu-item" href="#">
791
+ <span class="item-icon">
792
+ <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>
793
+ </span>
794
+ 集群管理
795
+ </a></li>
796
+
797
+ <!-- L1: 级联管理(父菜单) -->
798
+ <li>
799
+ <a class="sidebar-menu-item" href="#" data-toggle="cascade-mgmt">
800
+ <span class="item-icon">
801
+ <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>
802
+ </span>
803
+ 级联管理
804
+ <span class="expand-icon">
805
+ <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>
806
+ </span>
807
+ </a>
808
+ <ul class="sidebar-sub-menu" id="cascade-mgmt">
809
+ <li><a class="sidebar-menu-item" href="#">级联网关</a></li>
810
+ <li><a class="sidebar-menu-item" href="#">级联链路</a></li>
811
+ </ul>
812
+ <div class="sidebar-flyout">
813
+ <a class="sidebar-menu-item" href="#">级联网关</a>
814
+ <a class="sidebar-menu-item" href="#">级联链路</a>
815
+ </div>
816
+ </li>
817
+
818
+ <!-- L1: 容灾管理(父菜单) -->
819
+ <li>
820
+ <a class="sidebar-menu-item" href="#" data-toggle="disaster-mgmt">
821
+ <span class="item-icon">
822
+ <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>
823
+ </span>
824
+ 容灾管理
825
+ <span class="expand-icon">
826
+ <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>
827
+ </span>
828
+ </a>
829
+ <ul class="sidebar-sub-menu" id="disaster-mgmt">
830
+ <li><a class="sidebar-menu-item" href="#">容灾网关实例</a></li>
831
+ </ul>
832
+ <div class="sidebar-flyout">
833
+ <a class="sidebar-menu-item" href="#">容灾网关实例</a>
834
+ </div>
835
+ </li>
836
+
837
+ <!-- L1: 认证授权(父菜单) -->
838
+ <li>
839
+ <a class="sidebar-menu-item" href="#" data-toggle="auth-mgmt">
840
+ <span class="item-icon">
841
+ <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>
842
+ </span>
843
+ 认证授权
844
+ <span class="expand-icon">
845
+ <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>
846
+ </span>
847
+ </a>
848
+ <ul class="sidebar-sub-menu" id="auth-mgmt">
849
+ <li><a class="sidebar-menu-item" href="#">用户管理</a></li>
850
+ <li><a class="sidebar-menu-item" href="#">角色管理</a></li>
851
+ <li><a class="sidebar-menu-item" href="#">权限插件</a></li>
852
+ <li><a class="sidebar-menu-item" href="#">访问凭证</a></li>
853
+ </ul>
854
+ <div class="sidebar-flyout">
855
+ <a class="sidebar-menu-item" href="#">用户管理</a>
856
+ <a class="sidebar-menu-item" href="#">角色管理</a>
857
+ <a class="sidebar-menu-item" href="#">权限插件</a>
858
+ <a class="sidebar-menu-item" href="#">访问凭证</a>
859
+ </div>
860
+ </li>
861
+ </ul>
862
+ </div>
863
+
864
+ <div class="sidebar-divider"></div>
865
+
866
+ <div class="sidebar-footer">
867
+ <div class="sidebar-avatar">A</div>
868
+ <span class="sidebar-username">admin</span>
869
+ <button class="sidebar-more">
870
+ <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>
871
+ </button>
872
+ </div>
873
+ </aside>
874
+
875
+ <!-- ===== PageContainer ===== -->
876
+ <div class="page-container">
877
+
878
+ <!-- ContentWrapper + Card -->
879
+ <div class="content-wrapper">
880
+ <div class="card">
881
+ <!-- 可滚动区域,Breadcrumb + PageHeader 在白卡内顶部 -->
882
+ <div class="card-scrollable" id="cardScrollable">
883
+
884
+ <!-- Breadcrumb (两级) — L2面包屑模式,禁止标题组件 -->
885
+ <nav class="breadcrumb">
886
+ <a href="#">AI 网关</a>
887
+ <span class="breadcrumb-sep"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="9 18 15 12 9 6"/></svg></span>
888
+ <span class="breadcrumb-current">创建实例</span>
889
+ </nav>
890
+
891
+ <!-- 分组 1: 基本信息 -->
892
+ <div class="settings-section">
893
+ <h2 class="section-title">基本信息</h2>
894
+ <p class="section-desc">配置实例的基本属性</p>
895
+
896
+ <div class="form-row">
897
+ <div class="form-label">实例类型<span class="required">*</span></div>
898
+ <div class="form-control">
899
+ <div class="select-wrapper">
900
+ <button type="button" class="select-trigger" data-value="AI网关">AI网关</button>
901
+ <div class="select-content">
902
+ <div class="select-item selected" data-value="AI网关">AI网关</div>
903
+ <div class="select-item" data-value="API网关">API网关</div>
904
+ </div>
905
+ </div>
906
+ </div>
907
+ </div>
908
+
909
+ <div class="form-row">
910
+ <div class="form-label">实例名称<span class="required">*</span></div>
911
+ <div class="form-control">
912
+ <input class="input" type="text" placeholder="请输入实例名称(小写字母、数字、连字符)">
913
+ </div>
914
+ </div>
915
+
916
+ <div class="form-row">
917
+ <div class="form-label">实例规格<span class="required">*</span></div>
918
+ <div class="form-control">
919
+ <div class="select-wrapper">
920
+ <button type="button" class="select-trigger" data-value="mini">mini</button>
921
+ <div class="select-content">
922
+ <div class="select-item selected" data-value="mini">mini</div>
923
+ <div class="select-item" data-value="small">small</div>
924
+ <div class="select-item" data-value="standard">standard</div>
925
+ <div class="select-item" data-value="large">large</div>
926
+ </div>
927
+ </div>
928
+ </div>
929
+ </div>
930
+
931
+ <div class="form-row">
932
+ <div class="form-label">实例节点数<span class="required">*</span></div>
933
+ <div class="form-control">
934
+ <div class="stepper">
935
+ <button class="stepper-btn">-</button><input type="number" value="1" min="1"><button class="stepper-btn">+</button>
936
+ </div>
937
+ </div>
938
+ </div>
939
+ </div>
940
+
941
+ <!-- 分组 2: 集群配置 -->
942
+ <div class="settings-section">
943
+ <h2 class="section-title">集群配置</h2>
944
+ <p class="section-desc">选择实例部署的集群和命名空间</p>
945
+
946
+ <div class="form-row">
947
+ <div class="form-label">集群<span class="required">*</span></div>
948
+ <div class="form-control">
949
+ <div class="select-wrapper">
950
+ <button type="button" class="select-trigger placeholder" data-value="">请选择集群</button>
951
+ <div class="select-content">
952
+ <div class="select-item" data-value="cluster-prod-01">cluster-prod-01</div>
953
+ <div class="select-item" data-value="cluster-staging-02">cluster-staging-02</div>
954
+ </div>
955
+ </div>
956
+ </div>
957
+ </div>
958
+
959
+ <div class="form-row">
960
+ <div class="form-label">命名空间<span class="required">*</span></div>
961
+ <div class="form-control">
962
+ <div class="select-wrapper">
963
+ <button type="button" class="select-trigger placeholder" data-value="">请选择命名空间</button>
964
+ <div class="select-content">
965
+ <div class="select-item" data-value="default">default</div>
966
+ <div class="select-item" data-value="higress-system">higress-system</div>
967
+ <div class="select-item" data-value="istio-system">istio-system</div>
968
+ </div>
969
+ </div>
970
+ </div>
971
+ </div>
972
+
973
+ <div class="form-row">
974
+ <div class="form-label">Ingress 类名<span class="required">*</span></div>
975
+ <div class="form-control">
976
+ <input class="input" type="text" placeholder="请输入 IngressClass 名称">
977
+ </div>
978
+ </div>
979
+
980
+ <div class="form-row">
981
+ <div class="form-label">镜像拉取密钥</div>
982
+ <div class="form-control">
983
+ <input class="input" type="text" placeholder="请输入 imagePullSecret 名称(可选)">
984
+ </div>
985
+ </div>
986
+
987
+ <div class="form-row">
988
+ <div class="form-label">服务暴露方式</div>
989
+ <div class="form-control">
990
+ <div class="select-wrapper">
991
+ <button type="button" class="select-trigger" data-value="LoadBalancer(负载均衡)">LoadBalancer(负载均衡)</button>
992
+ <div class="select-content">
993
+ <div class="select-item selected" data-value="LoadBalancer(负载均衡)">LoadBalancer(负载均衡)</div>
994
+ <div class="select-item" data-value="NodePort(节点端口)">NodePort(节点端口)</div>
995
+ <div class="select-item" data-value="ClusterIP(集群内部)">ClusterIP(集群内部)</div>
996
+ </div>
997
+ </div>
998
+ </div>
999
+ </div>
1000
+ </div>
1001
+
1002
+ <!-- 分组 3: 可观测性配置 -->
1003
+ <div class="settings-section">
1004
+ <h2 class="section-title">可观测性配置</h2>
1005
+ <p class="section-desc">启用日志和监控服务</p>
1006
+
1007
+ <div class="form-row">
1008
+ <div class="form-label">日志服务</div>
1009
+ <div class="form-control">
1010
+ <div style="display: flex; align-items: center; gap: 8px;">
1011
+ <label class="switch">
1012
+ <input type="checkbox" checked>
1013
+ <span class="switch-track"></span>
1014
+ <span class="switch-thumb"></span>
1015
+ </label>
1016
+ <div class="form-description" style="margin: 0;">开启后为实例采集运行日志</div>
1017
+ </div>
1018
+ </div>
1019
+ </div>
1020
+
1021
+ <div class="form-row">
1022
+ <div class="form-label">Prometheus 监控</div>
1023
+ <div class="form-control">
1024
+ <div style="display: flex; align-items: center; gap: 8px;">
1025
+ <label class="switch">
1026
+ <input type="checkbox" checked>
1027
+ <span class="switch-track"></span>
1028
+ <span class="switch-thumb"></span>
1029
+ </label>
1030
+ <div class="form-description" style="margin: 0;">启用 Prometheus 指标采集</div>
1031
+ </div>
1032
+ </div>
1033
+ </div>
1034
+ </div>
1035
+
1036
+ </div><!-- /.card-scrollable -->
1037
+
1038
+ <!-- Footer -->
1039
+ <div class="settings-footer">
1040
+ <button class="btn btn-secondary">取消</button>
1041
+ <button class="btn btn-primary">提交</button>
1042
+ </div>
1043
+ </div><!-- /.card -->
1044
+ </div><!-- /.content-wrapper -->
1045
+ </div><!-- /.page-container -->
1046
+
1047
+ <script>
1048
+ // Sidebar collapse toggle
1049
+ const sidebar = document.querySelector('.sidebar');
1050
+ document.querySelector('.sidebar-collapse-btn').addEventListener('click', function() {
1051
+ sidebar.classList.toggle('collapsed');
1052
+ });
1053
+ // Sidebar expand via logo click (collapsed state)
1054
+ document.getElementById('sidebarLogoArea').addEventListener('click', function() {
1055
+ if (sidebar.classList.contains('collapsed')) {
1056
+ sidebar.classList.remove('collapsed');
1057
+ }
1058
+ });
1059
+
1060
+ // Sidebar expand toggle
1061
+ document.querySelectorAll('.sidebar-menu-item[data-toggle]').forEach(item => {
1062
+ item.addEventListener('click', function(e) {
1063
+ e.preventDefault();
1064
+ this.classList.toggle('expanded');
1065
+ const targetId = this.getAttribute('data-toggle');
1066
+ const target = document.getElementById(targetId);
1067
+ if (target) target.classList.toggle('show');
1068
+ });
1069
+ });
1070
+
1071
+ // Custom Select component
1072
+ document.querySelectorAll('.select-wrapper').forEach(wrapper => {
1073
+ const trigger = wrapper.querySelector('.select-trigger');
1074
+ const content = wrapper.querySelector('.select-content');
1075
+ const items = wrapper.querySelectorAll('.select-item');
1076
+
1077
+ trigger.addEventListener('click', function(e) {
1078
+ e.stopPropagation();
1079
+ // Close all other selects
1080
+ document.querySelectorAll('.select-content.show').forEach(c => {
1081
+ if (c !== content) c.classList.remove('show');
1082
+ });
1083
+ content.classList.toggle('show');
1084
+ });
1085
+
1086
+ items.forEach(item => {
1087
+ if (item.hasAttribute('data-disabled')) return;
1088
+ item.addEventListener('click', function() {
1089
+ items.forEach(i => i.classList.remove('selected'));
1090
+ this.classList.add('selected');
1091
+ trigger.textContent = this.textContent;
1092
+ trigger.dataset.value = this.dataset.value;
1093
+ trigger.classList.remove('placeholder');
1094
+ content.classList.remove('show');
1095
+ });
1096
+ });
1097
+ });
1098
+
1099
+ // Close selects on outside click
1100
+ document.addEventListener('click', function(e) {
1101
+ if (!e.target.closest('.select-wrapper')) {
1102
+ document.querySelectorAll('.select-content.show').forEach(c => c.classList.remove('show'));
1103
+ }
1104
+ });
1105
+
1106
+ // Stepper buttons
1107
+ document.querySelectorAll('.stepper-btn').forEach(btn => {
1108
+ btn.addEventListener('click', function() {
1109
+ const input = this.parentElement.querySelector('input[type="number"]');
1110
+ const min = parseInt(input.min) || 1;
1111
+ let val = parseInt(input.value) || min;
1112
+ if (this.textContent.trim() === '+') val++;
1113
+ else if (val > min) val--;
1114
+ input.value = val;
1115
+ });
1116
+ });
1117
+ </script>
1118
+ </body>
1119
+ </html>