pda-kit-mcp-server 2.0.1

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 (81) hide show
  1. package/README.md +107 -0
  2. package/bin/pda-kit-mcp.js +35 -0
  3. package/dist/file-reader.d.ts +83 -0
  4. package/dist/file-reader.d.ts.map +1 -0
  5. package/dist/file-reader.js +170 -0
  6. package/dist/file-reader.js.map +1 -0
  7. package/dist/http-entry.d.ts +12 -0
  8. package/dist/http-entry.d.ts.map +1 -0
  9. package/dist/http-entry.js +149 -0
  10. package/dist/http-entry.js.map +1 -0
  11. package/dist/http-server.d.ts +5 -0
  12. package/dist/http-server.d.ts.map +1 -0
  13. package/dist/http-server.js +114 -0
  14. package/dist/http-server.js.map +1 -0
  15. package/dist/index.d.ts +8 -0
  16. package/dist/index.d.ts.map +1 -0
  17. package/dist/index.js +90 -0
  18. package/dist/index.js.map +1 -0
  19. package/dist/paths.d.ts +37 -0
  20. package/dist/paths.d.ts.map +1 -0
  21. package/dist/paths.js +52 -0
  22. package/dist/paths.js.map +1 -0
  23. package/dist/server.d.ts +12 -0
  24. package/dist/server.d.ts.map +1 -0
  25. package/dist/server.js +308 -0
  26. package/dist/server.js.map +1 -0
  27. package/dist/stdio-server.d.ts +8 -0
  28. package/dist/stdio-server.d.ts.map +1 -0
  29. package/dist/stdio-server.js +83 -0
  30. package/dist/stdio-server.js.map +1 -0
  31. package/dist/tools/generate-component.d.ts +54 -0
  32. package/dist/tools/generate-component.d.ts.map +1 -0
  33. package/dist/tools/generate-component.js +80 -0
  34. package/dist/tools/generate-component.js.map +1 -0
  35. package/dist/tools/get-component-spec.d.ts +70 -0
  36. package/dist/tools/get-component-spec.d.ts.map +1 -0
  37. package/dist/tools/get-component-spec.js +95 -0
  38. package/dist/tools/get-component-spec.js.map +1 -0
  39. package/dist/tools/get-global-rules.d.ts +42 -0
  40. package/dist/tools/get-global-rules.d.ts.map +1 -0
  41. package/dist/tools/get-global-rules.js +83 -0
  42. package/dist/tools/get-global-rules.js.map +1 -0
  43. package/dist/tools/list-components.d.ts +52 -0
  44. package/dist/tools/list-components.d.ts.map +1 -0
  45. package/dist/tools/list-components.js +104 -0
  46. package/dist/tools/list-components.js.map +1 -0
  47. package/dist/tools/search-components.d.ts +54 -0
  48. package/dist/tools/search-components.d.ts.map +1 -0
  49. package/dist/tools/search-components.js +124 -0
  50. package/dist/tools/search-components.js.map +1 -0
  51. package/dist/tools/validate-html.d.ts +43 -0
  52. package/dist/tools/validate-html.d.ts.map +1 -0
  53. package/dist/tools/validate-html.js +116 -0
  54. package/dist/tools/validate-html.js.map +1 -0
  55. package/package.json +59 -0
  56. package/spec-data/badge.md +461 -0
  57. package/spec-data/button.md +148 -0
  58. package/spec-data/camera.md +622 -0
  59. package/spec-data/dialog.md +419 -0
  60. package/spec-data/divider.md +208 -0
  61. package/spec-data/drawer.md +301 -0
  62. package/spec-data/empty-state.md +336 -0
  63. package/spec-data/home-page.md +352 -0
  64. package/spec-data/input-row.md +346 -0
  65. package/spec-data/layout-page.md +317 -0
  66. package/spec-data/list-item.md +128 -0
  67. package/spec-data/location-selector.md +630 -0
  68. package/spec-data/more-actions.md +427 -0
  69. package/spec-data/navbar.md +482 -0
  70. package/spec-data/photo-list.md +461 -0
  71. package/spec-data/picker-single.md +199 -0
  72. package/spec-data/picker.md +586 -0
  73. package/spec-data/scan-input.md +304 -0
  74. package/spec-data/section-heading.md +268 -0
  75. package/spec-data/selectable-list.md +331 -0
  76. package/spec-data/stepper.md +247 -0
  77. package/spec-data/tab-bar.md +254 -0
  78. package/spec-data/tab.md +349 -0
  79. package/spec-data/tag.md +363 -0
  80. package/spec-data/tooltip.md +202 -0
  81. package/spec-data/waybill-number.md +257 -0
@@ -0,0 +1,331 @@
1
+ ---
2
+ id: selectable-list
3
+ name: 可勾选列表 Selectable List
4
+ category: "表单/Form"
5
+ version: "1.0.0"
6
+ source: PDAKit-基础组件
7
+ source_url: https://mastergo.ky-tech.com.cn/goto/UgTWrOWt
8
+ source_doc_id: 103417331857122
9
+ component_count: 1
10
+ ---
11
+
12
+ # 可勾选列表 Selectable List
13
+
14
+ 白底勾选列表,支持**单选**和**多选**两种选择模式。每条占一行 `480×70px`,左侧勾选图标 + 标题文字,右侧可选统计徽章。常用于目的地选择、服务类型勾选、分类筛选等场景。
15
+
16
+ > **颜色规则**:全部使用 Token(`--color-{色系}-{色阶}`),禁止 Hex。
17
+ > **字体**:PingFang SC。
18
+
19
+ ---
20
+
21
+ ## 容器规格
22
+
23
+ | 属性 | 值 |
24
+ |------|-----|
25
+ | 宽度 | `480px`(撑满父容器) |
26
+ | 背景 | `--color-grey-1`(`#FFFFFF`) |
27
+ | 布局 | 垂直排列,可选顶部分割线 |
28
+
29
+ ---
30
+
31
+ ## 列表项规格
32
+
33
+ | 属性 | 值 |
34
+ |------|-----|
35
+ | 高度 | `70px` |
36
+ | 布局 | `flex` + `space-between` + `align-items: center` |
37
+ | 内边距 | `0 16px`(左右 16px) |
38
+ | 背景 | `--color-grey-1`(`#FFFFFF`) |
39
+ | 按下态 | `:active → background: var(--color-grey-2)` |
40
+ | 底部分割线 | `1px` 高,`--color-black-2`(`#DDDDDD`),撑满宽度 |
41
+
42
+ ### 左侧:标题(+ 描述文字)
43
+
44
+ | 属性 | 值 |
45
+ |------|------|
46
+ | 布局 | `flex-col` + `justify-content: center` |
47
+ | 标题 | `font-size: 20px / line-height: 28px`,`font-weight: 600` |
48
+ | 描述文字(可选) | `font-size: 16px / line-height: 22px`,`color: --color-black-4` |
49
+
50
+ ### 右侧:勾选图标 + 统计徽章
51
+
52
+ | 属性 | 值 |
53
+ |------|------|
54
+ | 布局 | `flex` + `align-items: center` + `gap: 8px` |
55
+ | 勾选图标 | `32×32px` |
56
+ | 徽章背景 | `--color-grey-2`(`#F3F4F6`) |
57
+ | 徽章边框 | `1px solid --color-grey-3`(`#E0E4F0`) |
58
+ | 徽章圆角 | `8px` |
59
+ | 徽章内边距 | `4px 8px` |
60
+ | 徽章文字 | `font-size: 18px / line-height: 25px`,`color: --color-grey-7`(`#383B6F`) |
61
+
62
+ > 右侧勾选图标必填,统计徽章可选。
63
+
64
+ ### 选中态 vs 未选中态
65
+
66
+ | 状态 | 勾选图标 | 图标颜色 | 文字颜色 |
67
+ |------|---------|---------|---------|
68
+ | **选中** | `common_ic_check_two.svg` | `--color-green-6`(`#03C15F`) | 不变 |
69
+ | **未选中** | `common_ic_radio_o.svg` | 原始色 | `--color-black-6`(`#333333`) |
70
+
71
+ > 选中态图标需通过 CSS `filter` 染为绿色:`filter: invert(48%) sepia(79%) saturate(2476%) hue-rotate(116deg) brightness(96%) contrast(101%);`
72
+
73
+ ---
74
+
75
+ ## 两种选择模式
76
+
77
+ ### 单选(Radio 行为)
78
+
79
+ 同一列表组中同时只有 1 项处于选中态。点击某项 → 该项选中,其余全部取消。
80
+
81
+ | 交互 | 行为 |
82
+ |------|------|
83
+ | 点击未选中项 | 清除全组选中 → 该项变为选中态 |
84
+ | 点击已选中项 | 无变化(不取消) |
85
+
86
+ ### 多选(Checkbox 行为)
87
+
88
+ 同一列表组中可同时选中多项。点击某项 → 切换该项选中/未选状态。
89
+
90
+ | 交互 | 行为 |
91
+ |------|------|
92
+ | 点击未选中项 | 该项 → 选中态 |
93
+ | 点击已选中项 | 该项 → 未选中态 |
94
+
95
+ > 切换通过 JS 添加/移除 `.selected` class 控制文字颜色,同时替换 `img.src` 切换图标。不依赖 CSS `content` 属性。
96
+
97
+ ---
98
+
99
+ ## 布局结构
100
+
101
+ ```
102
+ ┌──────────────────────────────────────────┐
103
+ │ ─── 1px 顶部分割线(可选) ─── │
104
+ ├──────────────────────────────────────────┤
105
+ │ 北京 [✓] 12 │ ← 选中态:文字紫色
106
+ │ 北京市辖区 │
107
+ ├──────────────────────────────────────────┤
108
+ │ ─── 1px 分割线 ─── │
109
+ ├──────────────────────────────────────────┤
110
+ │ 上海 [○] │ ← 未选中态
111
+ ├──────────────────────────────────────────┤
112
+ │ ─── 1px 分割线 ─── │
113
+ ├──────────────────────────────────────────┤
114
+ │ 广州 [○] 8 │ ← 右侧徽章可选
115
+ │ 广东省广州市 │
116
+ └──────────────────────────────────────────┘
117
+ ```
118
+
119
+ ---
120
+
121
+ ## 尺寸速查
122
+
123
+ | 元素 | 值 |
124
+ |------|------|
125
+ | 列表项高度 | `70px` |
126
+ | 列表项左右内边距 | `16px` |
127
+ | 勾选图标 | `32 × 32px` |
128
+ | 左侧间距(图标 ↔ 文字) | `12px` |
129
+ | 标题字号 | `20px / 28px`,Semibold(`600`) |
130
+ | 徽章字号 | `18px / 25px` |
131
+ | 徽章圆角 | `8px` |
132
+ | 徽章内边距 | `4px 8px` |
133
+ | 分割线 | `1px`,`--color-black-2` |
134
+
135
+ ---
136
+
137
+ ## HTML 示例
138
+
139
+ ### 单选项
140
+
141
+ ```html
142
+ <div class="selectable-list">
143
+ <div class="selectable-item" data-value="beijing" onclick="selectSingle(this, 'single-group')">
144
+ <div class="selectable-left">
145
+ <span class="selectable-text">北京</span>
146
+ <span class="selectable-desc">北京市辖区</span>
147
+ </div>
148
+ <div class="selectable-right">
149
+ <img class="selectable-checkbox" src="./icons/assets/common_ic_radio_o.svg" width="32" height="32" alt="">
150
+ </div>
151
+ </div>
152
+ <div class="selectable-divider"></div>
153
+ <div class="selectable-item" data-value="shanghai" onclick="selectSingle(this, 'single-group')">
154
+ <div class="selectable-left">
155
+ <span class="selectable-text">上海</span>
156
+ </div>
157
+ <div class="selectable-right">
158
+ <img class="selectable-checkbox" src="./icons/assets/common_ic_radio_o.svg" width="32" height="32" alt="">
159
+ </div>
160
+ </div>
161
+ </div>
162
+ ```
163
+
164
+ ### 多选项(带徽章)
165
+
166
+ ```html
167
+ <div class="selectable-list">
168
+ <div class="selectable-item" data-value="express" onclick="toggleSelect(this)">
169
+ <div class="selectable-left">
170
+ <span class="selectable-text">快递服务</span>
171
+ </div>
172
+ <div class="selectable-right">
173
+ <img class="selectable-checkbox" src="./icons/assets/common_ic_radio_o.svg" width="32" height="32" alt="">
174
+ <span class="selectable-badge">12</span>
175
+ </div>
176
+ </div>
177
+ <div class="selectable-divider"></div>
178
+ <div class="selectable-item" data-value="logistics" onclick="toggleSelect(this)">
179
+ <div class="selectable-left">
180
+ <span class="selectable-text">物流配送</span>
181
+ </div>
182
+ <div class="selectable-right">
183
+ <img class="selectable-checkbox" src="./icons/assets/common_ic_radio_o.svg" width="32" height="32" alt="">
184
+ </div>
185
+ </div>
186
+ </div>
187
+ ```
188
+
189
+ ### JS 交互脚本
190
+
191
+ ```javascript
192
+ // 单选
193
+ function selectSingle(el, groupId) {
194
+ const parent = el.parentElement;
195
+ parent.querySelectorAll('.selectable-item').forEach(item => {
196
+ item.classList.remove('selected');
197
+ item.querySelector('.selectable-checkbox').src = './icons/assets/common_ic_radio_o.svg';
198
+ });
199
+ el.classList.add('selected');
200
+ el.querySelector('.selectable-checkbox').src = './icons/assets/common_ic_check_two.svg';
201
+ }
202
+
203
+ // 多选
204
+ function toggleSelect(el) {
205
+ const checkbox = el.querySelector('.selectable-checkbox');
206
+ if (el.classList.contains('selected')) {
207
+ el.classList.remove('selected');
208
+ checkbox.src = './icons/assets/common_ic_radio_o.svg';
209
+ } else {
210
+ el.classList.add('selected');
211
+ checkbox.src = './icons/assets/common_ic_check_two.svg';
212
+ }
213
+ }
214
+ ```
215
+
216
+ ---
217
+
218
+ ## CSS
219
+
220
+ ```css
221
+ /* 列表容器 */
222
+ .selectable-list {
223
+ width: 100%;
224
+ background: var(--color-grey-1);
225
+ }
226
+
227
+ /* 列表项 */
228
+ .selectable-item {
229
+ width: 100%;
230
+ height: 70px;
231
+ display: flex;
232
+ align-items: center;
233
+ justify-content: space-between;
234
+ padding: 0 16px;
235
+ background: var(--color-grey-1);
236
+ cursor: pointer;
237
+ }
238
+
239
+ /* 按下态 */
240
+ .selectable-item:active {
241
+ background: var(--color-grey-2);
242
+ }
243
+
244
+ /* 选中态:图标变绿 */
245
+ .selectable-item.selected .selectable-checkbox {
246
+ filter: invert(48%) sepia(79%) saturate(2476%) hue-rotate(116deg) brightness(96%) contrast(101%);
247
+ }
248
+
249
+ /* 左侧:标题 + 描述 */
250
+ .selectable-left {
251
+ display: flex;
252
+ flex-direction: column;
253
+ justify-content: center;
254
+ flex: 1;
255
+ min-width: 0;
256
+ }
257
+
258
+ /* 标题文字 */
259
+ .selectable-text {
260
+ font: 600 20px/28px "PingFang SC", -apple-system, sans-serif;
261
+ color: var(--color-black-6);
262
+ }
263
+
264
+ /* 描述文字(可选) */
265
+ .selectable-desc {
266
+ font: 400 16px/22px "PingFang SC", -apple-system, sans-serif;
267
+ color: var(--color-black-4);
268
+ margin-top: 2px;
269
+ }
270
+
271
+ /* 右侧:勾选图标 + 徽章 */
272
+ .selectable-right {
273
+ display: flex;
274
+ align-items: center;
275
+ gap: 8px;
276
+ flex-shrink: 0;
277
+ }
278
+
279
+ /* 勾选图标 */
280
+ .selectable-checkbox {
281
+ width: 32px;
282
+ height: 32px;
283
+ flex-shrink: 0;
284
+ }
285
+
286
+ /* 统计徽章 */
287
+ .selectable-badge {
288
+ display: flex;
289
+ align-items: center;
290
+ padding: 4px 8px;
291
+ background: var(--color-grey-2);
292
+ border: 1px solid var(--color-grey-3);
293
+ border-radius: 8px;
294
+ font: 400 18px/25px "PingFang SC", -apple-system, sans-serif;
295
+ color: var(--color-grey-7);
296
+ }
297
+
298
+ /* 分割线 */
299
+ .selectable-divider {
300
+ width: 100%;
301
+ height: 1px;
302
+ background: var(--color-black-2);
303
+ }
304
+ ```
305
+
306
+ ---
307
+
308
+ ## 颜色 Token
309
+
310
+ | Token | 色值 | 使用场景 |
311
+ |-------|------|---------|
312
+ | `--color-grey-1` | `#FFFFFF` | 列表容器、列表项背景 |
313
+ | `--color-grey-2` | `#F3F4F6` | 按下态背景、徽章背景 |
314
+ | `--color-grey-3` | `#E0E4F0` | 徽章边框 |
315
+ | `--color-grey-7` | `#383B6F` | 徽章文字 |
316
+ | `--color-main-6` | `#6445D1` | 选中态文字 |
317
+ | `--color-black-2` | `#DDDDDD` | 分割线 |
318
+ | `--color-black-6` | `#333333` | 未选中文字 |
319
+
320
+ ---
321
+
322
+ ## 使用规则(AI 必读)
323
+
324
+ 1. **勾选图标固定 `32×32`**,比 input-row 的 Radio 图标(`28×28`)更大
325
+ 2. **两种选择模式**:`radio` 行为(单选,同一组仅一项选中)和 `checkbox` 行为(多选,可同时选多项)
326
+ 3. **选中态**:图标切为 `common_ic_check_two.svg`,文字色变为 `--color-main-6`
327
+ 4. **未选中态**:图标用 `common_ic_radio_o.svg`,文字色 `--color-black-6`
328
+ 5. **按下态必须响应**:所有列表项 `:active` 时背景变 `--color-grey-2`
329
+ 6. **右侧徽章可选**:有统计数据时展示,无统计数据时省略
330
+ 7. **分割线固定**:每行底部 `1px` `--color-black-2`,列表顶部可选
331
+ 8. **切换通过 JS**:添加/移除 `.selected` class + 替换 `img.src`,不依赖 CSS `content` 伪属性
@@ -0,0 +1,247 @@
1
+ ---
2
+ id: stepper
3
+ name: 步进器
4
+ category: "表单/Form"
5
+ version: "1.0.0"
6
+ source: PDAKit-业务组件
7
+ source_url: https://mastergo.ky-tech.com.cn/goto/TZDV10eK
8
+ source_doc_id: 103417331857122
9
+ ---
10
+
11
+ # 步进器 Stepper
12
+
13
+ 数值步进控件,**[-] 按钮 + 数值显示 + [+] 按钮**,用于数量调整、数字输入等场景。支持边界禁用。
14
+
15
+ > **核心规则**:点击 [-] 减 1,点击 [+] 加 1。数值达到最小值 ↘ [-] 禁用;达到最大值 ↗ [+] 禁用。
16
+ > **数值范围**:`0` ~ `9999`。
17
+ > **颜色规则**:全部使用 Token(`--color-{色系}-{色阶}`),禁止 Hex。
18
+ > **字体**:PingFang SC。
19
+
20
+ ---
21
+
22
+ ## Props
23
+
24
+ | Prop | 类型 | 值 | 默认 |
25
+ |------|------|-----|------|
26
+ | 数值 | number | 当前数值(0 ~ 9999) | `0` |
27
+ | 最小值 | number | 最小允许值 | `0` |
28
+ | 最大值 | number | 最大允许值 | `9999` |
29
+ | 步长 | number | 每次加减量 | `1` |
30
+ | 状态 | enum | `正常` / `禁用` | `正常` |
31
+ | 宽度 | number | 组件整体宽度 | `146px` |
32
+
33
+ ---
34
+
35
+ ## 整体规格
36
+
37
+ | 属性 | 值 |
38
+ |------|------|
39
+ | 宽度 | `146px` |
40
+ | 高度 | `40px` |
41
+ | 布局 | `flex-row`, `justify-content: center`, `align-items: center` |
42
+ | 背景 | `--color-grey-1`(`#FFFFFF`) |
43
+ | 描边 | `1px solid --color-black-2`(`#DDDDDD`) |
44
+ | 圆角 | `4px` |
45
+
46
+ ### 内部结构
47
+
48
+ ```
49
+ ┌── 146px ──────────┐
50
+ │ ┌──┐ ┌────────┐ ┌──┐ │
51
+ │ │- │ │ 9999 │ │+ │ │
52
+ │ └──┘ └────────┘ └──┘ │
53
+ │ 40 66(flex:1) 40 │
54
+ └──────────────────────┘
55
+ ```
56
+
57
+ | 元素 | 宽 | 说明 |
58
+ |------|:--:|------|
59
+ | [-] 按钮 | `40px` | 左按钮,点击减 1 |
60
+ | 数值容器 | `flex: 1` | 居中显示当前值 |
61
+ | [+] 按钮 | `40px` | 右按钮,点击加 1 |
62
+
63
+ ---
64
+
65
+ ## 1. [-] / [+] 按钮
66
+
67
+ | 属性 | 值 |
68
+ |------|------|
69
+ | 尺寸 | `40×40px` |
70
+ | 图标 | `svg` 内嵌 +/- 符号 |
71
+
72
+ ### 按钮状态
73
+
74
+ | 状态 | 外观 | 行为 |
75
+ |------|------|------|
76
+ | 正常 | 背景 `--color-grey-1`,图标 `--color-black-6`,可点击 | 点击执行 ±1 |
77
+ | 禁用 | 背景 `--color-black-1`(`#EEEEEE`),图标 `--color-black-2` | 点击无响应 |
78
+
79
+ ---
80
+
81
+ ## 2. 数值显示区
82
+
83
+ | 属性 | 值 |
84
+ |------|------|
85
+ | 宽度 | `flex: 1`(≈66px) |
86
+ | 背景 | `transparent`(继承容器白底) |
87
+ | 布局 | `flex-column`, `justify-content: center`, `align-items: center` |
88
+ | 内边距 | `6px 8px` |
89
+ | 溢出 | `overflow: hidden` |
90
+ | 文字 | `20px / 28px`, font-weight `600`, `--color-black-6`(`#333333`), text-align `center` |
91
+ | 缩略 | `flex-shrink: 0` |
92
+
93
+ ---
94
+
95
+ ## 3. 边界禁用逻辑
96
+
97
+ | 条件 | [-] | [+] |
98
+ |------|:--:|:--:|
99
+ | min < value < max | 正常 | 正常 |
100
+ | value = min(= 0) | **禁用** | 正常 |
101
+ | value = max(= 9999) | 正常 | **禁用** |
102
+
103
+ ---
104
+
105
+ ## CSS 示例
106
+
107
+ ```css
108
+ .stepper {
109
+ width: 146px;
110
+ height: 40px;
111
+ display: flex;
112
+ justify-content: center;
113
+ align-items: center;
114
+ flex-shrink: 0;
115
+ background: var(--color-grey-1);
116
+ border: 1px solid var(--color-black-2);
117
+ border-radius: 4px;
118
+ overflow: hidden;
119
+ }
120
+
121
+ .stepper-btn {
122
+ width: 40px;
123
+ height: 40px;
124
+ display: flex;
125
+ justify-content: center;
126
+ align-items: center;
127
+ cursor: pointer;
128
+ flex-shrink: 0;
129
+ border: none;
130
+ background: var(--color-grey-1);
131
+ color: var(--color-black-6);
132
+ }
133
+ .stepper-btn:active:not(.disabled) { opacity: 0.7; }
134
+ .stepper-btn.disabled {
135
+ background: var(--color-black-1);
136
+ color: var(--color-black-2);
137
+ cursor: not-allowed;
138
+ pointer-events: none;
139
+ }
140
+ .stepper-btn svg {
141
+ width: 24px;
142
+ height: 24px;
143
+ }
144
+
145
+ .stepper-value {
146
+ flex: 1;
147
+ display: flex;
148
+ flex-direction: column;
149
+ justify-content: center;
150
+ align-items: center;
151
+ padding: 6px 8px;
152
+ overflow: hidden;
153
+ }
154
+ .stepper-value-text {
155
+ flex-shrink: 0;
156
+ color: var(--color-black-6);
157
+ font-size: 20px;
158
+ line-height: 28px;
159
+ font-weight: 600;
160
+ font-family: PingFang SC;
161
+ text-align: center;
162
+ }
163
+ ```
164
+
165
+ ---
166
+
167
+ ## HTML 骨架
168
+
169
+ ```html
170
+ <div class="stepper" id="stepper">
171
+ <!-- [-] 按钮 -->
172
+ <button class="stepper-btn" onclick="stepDown()">
173
+ <svg viewBox="0 0 24 24" fill="none">
174
+ <rect x="4" y="11" width="16" height="2" rx="1" fill="currentColor"/>
175
+ </svg>
176
+ </button>
177
+
178
+ <!-- 数值 -->
179
+ <div class="stepper-value">
180
+ <span class="stepper-value-text">0</span>
181
+ </div>
182
+
183
+ <!-- [+] 按钮 -->
184
+ <button class="stepper-btn" onclick="stepUp()">
185
+ <svg viewBox="0 0 24 24" fill="none">
186
+ <rect x="4" y="11" width="16" height="2" rx="1" fill="currentColor"/>
187
+ <rect x="11" y="4" width="2" height="16" rx="1" fill="currentColor"/>
188
+ </svg>
189
+ </button>
190
+ </div>
191
+ ```
192
+
193
+ ---
194
+
195
+ ## JS 交互逻辑
196
+
197
+ ```javascript
198
+ let value = 0;
199
+ const min = 0;
200
+ const max = 9999;
201
+
202
+ function stepUp() {
203
+ if (value >= max) return;
204
+ value += 1;
205
+ render();
206
+ }
207
+
208
+ function stepDown() {
209
+ if (value <= min) return;
210
+ value -= 1;
211
+ render();
212
+ }
213
+
214
+ function render() {
215
+ document.getElementById('stepperValue').textContent = value;
216
+
217
+ const btnMinus = document.getElementById('btnMinus');
218
+ const btnPlus = document.getElementById('btnPlus');
219
+
220
+ btnMinus.classList.toggle('disabled', value <= min);
221
+ btnPlus.classList.toggle('disabled', value >= max);
222
+ }
223
+ ```
224
+
225
+ ---
226
+
227
+ ## ⚙️ PDA 硬性约束
228
+
229
+ | # | 约束 | 值 | 原因 |
230
+ |---|------|-----|------|
231
+ | 1 | 最小触控热区 | [-] [+] 按钮 **40×40px** | 仓储手套操作 |
232
+ | 2 | 数值范围 | **0 ~ 9999** | 上限防止溢出 |
233
+ | 3 | 字体下界 | 数值文字 ≥ 20px, font-weight 600 | 小屏可读性 |
234
+ | 4 | 边界禁用 | 值=min 时 [-] 禁用;值=max 时 [+] 禁用 | 防止越界 |
235
+ | 5 | 禁用反馈 | 按钮背景变 `--color-black-1`(`#EEEEEE`)+ 图标变 `--color-black-2` + cursor: not-allowed | 明确不可操作 |
236
+
237
+ ---
238
+
239
+ ## 🔴 常见 AI 错误
240
+
241
+ | # | ❌ 错误 | ✅ 正确 |
242
+ |---|--------|--------|
243
+ | 1 | [-] [+] 按钮用文字而非图标 | 必须用 SVG 图标,40×40px |
244
+ | 2 | 数值范围未限制 | min=0, max=9999,边界禁用 |
245
+ | 3 | 数值文字未加粗 | font-weight: 600 |
246
+ | 4 | 组件宽度随意 | 固定 `146px` |
247
+ | 5 | 点击边界不阻止 | 值=min 时 stepDown 直接 return,不加也不减 |