@uxda/appkit 4.3.24 → 4.3.25

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.
@@ -0,0 +1,2166 @@
1
+ ---
2
+ alwaysApply: true
3
+ ---
4
+
5
+ # Nutshell UI 组件库使用规则 - 移动端
6
+
7
+ > 基于 Vue 3 + NutUI + Taro 的移动端组件库
8
+ > 版本:1.6.92
9
+ > 支持:H5、微信小程序
10
+
11
+ ## 📚 目录
12
+
13
+ - [快速开始](#快速开始)
14
+ - [平台支持](#平台支持)
15
+ - [基础组件](#基础组件)
16
+ - [表单组件](#表单组件)
17
+ - [数据展示组件](#数据展示组件)
18
+ - [反馈组件](#反馈组件)
19
+ - [布局组件](#布局组件)
20
+ - [导航组件](#导航组件)
21
+ - [通用属性](#通用属性)
22
+ - [服务方法](#服务方法)
23
+ - [最佳实践](#最佳实践)
24
+ - [移动端特性](#移动端特性)
25
+
26
+ ---
27
+
28
+ ## 快速开始
29
+
30
+ ### 安装
31
+
32
+ ```bash
33
+ npm install @uxda/nutshell
34
+ # 或
35
+ yarn add @uxda/nutshell
36
+ ```
37
+
38
+ ### H5 项目引入
39
+
40
+ ```javascript
41
+ import { createApp } from 'vue'
42
+ import Nutshell from '@uxda/nutshell/taro'
43
+ import '@uxda/nutshell/nutshell.css'
44
+ import '@uxda/nutshell/nutui.css' // 移动端样式
45
+
46
+ const app = createApp(App)
47
+ app.use(Nutshell)
48
+ app.mount('#app')
49
+ ```
50
+
51
+ ### Taro 小程序项目引入
52
+
53
+ ```javascript
54
+ // app.ts
55
+ import { createApp } from 'vue'
56
+ import Nutshell from '@uxda/nutshell/taro'
57
+
58
+ const app = createApp({
59
+ // ...
60
+ })
61
+
62
+ app.use(Nutshell)
63
+
64
+ export default app
65
+ ```
66
+
67
+ ### 按需引入组件
68
+
69
+ ```javascript
70
+ import { NsButton, NsInput, NsList } from '@uxda/nutshell/components'
71
+ ```
72
+
73
+ ---
74
+
75
+ ## 平台支持
76
+
77
+ Nutshell 移动端组件库支持以下平台:
78
+
79
+ - ✅ H5(移动端网页)
80
+ - ✅ 微信小程序
81
+ - ✅ 支付宝小程序(部分)
82
+ - ✅ 钉钉小程序(部分)
83
+
84
+ ### 平台检测
85
+
86
+ ```vue
87
+ <script setup>
88
+ import { usePlatform } from '@uxda/nutshell/taro'
89
+
90
+ const platform = usePlatform()
91
+
92
+ console.log(platform.mobile) // 是否移动端
93
+ console.log(platform.h5) // 是否H5
94
+ console.log(platform.weixin) // 是否微信环境
95
+ console.log(platform.android) // 是否Android
96
+ console.log(platform.ios) // 是否iOS
97
+ </script>
98
+ ```
99
+
100
+ ---
101
+
102
+ ## 基础组件
103
+
104
+ ### 1. Button 按钮
105
+
106
+ **组件名称**: `<ns-button>`
107
+
108
+ **用途**: 触发操作、提交表单、跳转页面等交互行为
109
+
110
+ **Props**:
111
+
112
+ | 属性名 | 类型 | 默认值 | 说明 |
113
+ |--------|------|--------|------|
114
+ | `label` | `string` | - | 按钮文字 |
115
+ | `color` | `Color` | - | 按钮颜色 |
116
+ | `size` | `'xs'\|'sm'\|'md'\|'lg'\|'xl'` | `'md'` | 按钮尺寸 |
117
+ | `variant` | `'solid'\|'outlined'\|'soft'\|'plain'` | - | 按钮样式风格 |
118
+ | `disabled` | `boolean` | `false` | 是否禁用 |
119
+ | `loading` | `boolean` | `false` | 是否显示加载状态 |
120
+ | `icon` | `string` | - | 图标名称 |
121
+ | `block` | `boolean` | `false` | 是否块级元素(宽度100%) |
122
+
123
+ **Slots**:
124
+
125
+ - `default`: 按钮内容
126
+ - `icon`: 自定义图标
127
+
128
+ **示例**:
129
+
130
+ ```vue
131
+ <!-- 基础用法 -->
132
+ <ns-button label="提交" />
133
+
134
+ <!-- 块级按钮 -->
135
+ <ns-button label="立即购买" color="primary" block />
136
+
137
+ <!-- 带图标 -->
138
+ <ns-button label="分享" icon="share" />
139
+
140
+ <!-- 加载状态 -->
141
+ <ns-button label="提交中..." :loading="true" />
142
+
143
+ <!-- 不同尺寸 -->
144
+ <ns-button label="小按钮" size="sm" />
145
+ <ns-button label="中按钮" size="md" />
146
+ <ns-button label="大按钮" size="lg" />
147
+ ```
148
+
149
+ ---
150
+
151
+ ### 2. Icon 图标
152
+
153
+ **组件名称**: `<ns-icon>`
154
+
155
+ **用途**: 展示图标
156
+
157
+ **Props**:
158
+
159
+ | 属性名 | 类型 | 默认值 | 说明 |
160
+ |--------|------|--------|------|
161
+ | `name` | `string` | - | 图标名称 |
162
+ | `size` | `Size\|number` | `'md'` | 图标尺寸 |
163
+ | `fill` | `Color` | - | 图标颜色 |
164
+
165
+ **示例**:
166
+
167
+ ```vue
168
+ <ns-icon name="home" />
169
+ <ns-icon name="user" size="lg" fill="primary" />
170
+ <ns-icon name="setting" :size="24" />
171
+ ```
172
+
173
+ ---
174
+
175
+ ### 3. Avatar 头像
176
+
177
+ **组件名称**: `<ns-avatar>`
178
+
179
+ **用途**: 展示用户头像
180
+
181
+ **Props**:
182
+
183
+ | 属性名 | 类型 | 默认值 | 说明 |
184
+ |--------|------|--------|------|
185
+ | `src` | `string` | - | 头像图片地址 |
186
+ | `size` | `Size\|number` | `'md'` | 头像尺寸 |
187
+ | `shape` | `'circle'\|'square'` | `'circle'` | 头像形状 |
188
+
189
+ **示例**:
190
+
191
+ ```vue
192
+ <ns-avatar src="/avatar.jpg" />
193
+ <ns-avatar size="lg" shape="square">用</ns-avatar>
194
+ ```
195
+
196
+ ---
197
+
198
+ ### 4. Image 图片
199
+
200
+ **组件名称**: `<ns-image>`
201
+
202
+ **用途**: 展示图片,支持懒加载、预览
203
+
204
+ **Props**:
205
+
206
+ | 属性名 | 类型 | 默认值 | 说明 |
207
+ |--------|------|--------|------|
208
+ | `src` | `string` | - | 图片地址 |
209
+ | `width` | `string\|number` | - | 图片宽度 |
210
+ | `height` | `string\|number` | - | 图片高度 |
211
+ | `fit` | `'fill'\|'contain'\|'cover'\|'none'\|'scale-down'` | `'cover'` | 图片填充模式 |
212
+ | `lazy` | `boolean` | `false` | 是否懒加载 |
213
+ | `preview` | `boolean` | `false` | 是否支持预览 |
214
+
215
+ **示例**:
216
+
217
+ ```vue
218
+ <ns-image
219
+ src="/photo.jpg"
220
+ width="200"
221
+ height="200"
222
+ lazy
223
+ preview
224
+ />
225
+ ```
226
+
227
+ ---
228
+
229
+ ### 5. Chip 标签
230
+
231
+ **组件名称**: `<ns-chip>`
232
+
233
+ **用途**: 展示标签或状态
234
+
235
+ **Props**:
236
+
237
+ | 属性名 | 类型 | 默认值 | 说明 |
238
+ |--------|------|--------|------|
239
+ | `label` | `string` | - | 标签文字 |
240
+ | `color` | `Color` | - | 标签颜色 |
241
+ | `size` | `Size` | `'md'` | 标签尺寸 |
242
+ | `closable` | `boolean` | `false` | 是否可关闭 |
243
+
244
+ **示例**:
245
+
246
+ ```vue
247
+ <ns-chip label="新品" color="danger" />
248
+ <ns-chip label="热销" color="warning" />
249
+ <ns-chip label="标签" closable @close="handleClose" />
250
+ ```
251
+
252
+ ---
253
+
254
+ ### 6. Chips 标签组
255
+
256
+ **组件名称**: `<ns-chips>`
257
+
258
+ **用途**: 展示多个标签
259
+
260
+ **Props**:
261
+
262
+ | 属性名 | 类型 | 默认值 | 说明 |
263
+ |--------|------|--------|------|
264
+ | `items` | `ChipItem[]` | `[]` | 标签数据 |
265
+
266
+ **示例**:
267
+
268
+ ```vue
269
+ <ns-chips
270
+ :items="[
271
+ { text: 'JavaScript', color: 'warning' },
272
+ { text: 'Vue.js', color: 'success' },
273
+ { text: 'TypeScript', color: 'primary' }
274
+ ]"
275
+ />
276
+ ```
277
+
278
+ ---
279
+
280
+ ## 表单组件
281
+
282
+ ### 1. Form 表单
283
+
284
+ **组件名称**: `<ns-form>`
285
+
286
+ **用途**: 表单容器,用于数据收集、校验和提交
287
+
288
+ **Props**:
289
+
290
+ | 属性名 | 类型 | 默认值 | 说明 |
291
+ |--------|------|--------|------|
292
+ | `modelValue` | `object` | - | 表单数据对象 |
293
+ | `name` | `string` | - | 表单名称 |
294
+ | `failed` | `'toast'\|'notice'\|'none'` | `'toast'` | 校验失败时的提示方式 |
295
+
296
+ **Events**:
297
+
298
+ | 事件名 | 参数 | 说明 |
299
+ |--------|------|------|
300
+ | `update:modelValue` | `(value: Record<string, any>)` | 表单数据更新 |
301
+
302
+ **Methods**:
303
+
304
+ | 方法名 | 参数 | 返回值 | 说明 |
305
+ |--------|------|--------|------|
306
+ | `validate` | `nameList?: NamePath` | `Promise<boolean>` | 触发表单验证 |
307
+ | `resetFields` | `nameList?: NamePath[]` | - | 重置表单字段 |
308
+ | `reset` | - | - | 重置整个表单 |
309
+ | `clearValidate` | `nameList?: NamePath[]` | - | 移除表单校验结果 |
310
+
311
+ **示例**:
312
+
313
+ ```vue
314
+ <template>
315
+ <ns-form v-model="formData" ref="formRef">
316
+ <ns-input
317
+ name="username"
318
+ label="用户名"
319
+ :rules="['required']"
320
+ placeholder="请输入用户名"
321
+ />
322
+ <ns-input
323
+ name="phone"
324
+ label="手机号"
325
+ :rules="['required', 'mobile']"
326
+ placeholder="请输入手机号"
327
+ />
328
+ <ns-button label="提交" block @click="handleSubmit" />
329
+ </ns-form>
330
+ </template>
331
+
332
+ <script setup>
333
+ import { ref } from 'vue'
334
+ import { useNutshell } from '@uxda/nutshell/taro'
335
+
336
+ const $n = useNutshell()
337
+ const formRef = ref()
338
+ const formData = ref({
339
+ username: '',
340
+ phone: ''
341
+ })
342
+
343
+ const handleSubmit = async () => {
344
+ const valid = await formRef.value.validate()
345
+ if (valid) {
346
+ $n.toast('提交成功', { type: 'success' })
347
+ }
348
+ }
349
+ </script>
350
+ ```
351
+
352
+ ---
353
+
354
+ ### 2. Input 输入框
355
+
356
+ **组件名称**: `<ns-input>`
357
+
358
+ **用途**: 单行文本输入
359
+
360
+ **Props**:
361
+
362
+ | 属性名 | 类型 | 默认值 | 说明 |
363
+ |--------|------|--------|------|
364
+ | `modelValue` | `string\|number` | - | 输入值(v-model) |
365
+ | `name` | `string` | - | 表单项名称(必填) |
366
+ | `label` | `string` | - | 标签文字 |
367
+ | `type` | `'text'\|'number'\|'tel'\|'password'` | `'text'` | 输入框类型 |
368
+ | `placeholder` | `string` | - | 占位符 |
369
+ | `maxlength` | `number` | - | 最大输入长度 |
370
+ | `clearable` | `boolean` | `true` | 是否显示清空按钮 |
371
+ | `disabled` | `boolean` | `false` | 是否禁用 |
372
+ | `readonly` | `boolean` | `false` | 是否只读 |
373
+ | `rules` | `ValidationRule[]` | - | 校验规则 |
374
+ | `textAlign` | `'left'\|'right'\|'center'` | `'left'` | 文字对齐方向 |
375
+
376
+ **Events**:
377
+
378
+ | 事件名 | 参数 | 说明 |
379
+ |--------|------|------|
380
+ | `update:modelValue` | `(value: string\|number)` | 值更新 |
381
+ | `change` | `(value: string\|number)` | 值改变 |
382
+ | `blur` | `()` | 失去焦点 |
383
+ | `focus` | `()` | 获得焦点 |
384
+ | `enter` | `()` | 按下回车(H5) |
385
+
386
+ **校验规则**:
387
+
388
+ - `'required'`: 必填
389
+ - `'numeric'`: 纯数字
390
+ - `'email'`: 邮箱格式
391
+ - `'id'`: 身份证号
392
+ - `'mobile'`: 手机号
393
+
394
+ **示例**:
395
+
396
+ ```vue
397
+ <!-- 基础输入 -->
398
+ <ns-input
399
+ v-model="username"
400
+ name="username"
401
+ label="用户名"
402
+ placeholder="请输入用户名"
403
+ />
404
+
405
+ <!-- 手机号输入 -->
406
+ <ns-input
407
+ v-model="phone"
408
+ name="phone"
409
+ label="手机号"
410
+ type="tel"
411
+ :rules="['required', 'mobile']"
412
+ placeholder="请输入手机号"
413
+ />
414
+
415
+ <!-- 密码输入 -->
416
+ <ns-input
417
+ v-model="password"
418
+ name="password"
419
+ label="密码"
420
+ type="password"
421
+ :rules="['required']"
422
+ placeholder="请输入密码"
423
+ />
424
+ ```
425
+
426
+ ---
427
+
428
+ ### 3. MobileInput 手机号输入
429
+
430
+ **组件名称**: `<ns-mobile-input>`
431
+
432
+ **用途**: 手机号输入(自动格式化为 xxx-xxxx-xxxx)
433
+
434
+ **Props**: 继承自 `NsInput`,自动设置 `type="tel"` 和 `rules="['mobile']"`
435
+
436
+ **示例**:
437
+
438
+ ```vue
439
+ <ns-mobile-input
440
+ v-model="phone"
441
+ name="phone"
442
+ label="手机号"
443
+ />
444
+ ```
445
+
446
+ ---
447
+
448
+ ### 4. IdInput 身份证号输入
449
+
450
+ **组件名称**: `<ns-id-input>`
451
+
452
+ **用途**: 身份证号输入
453
+
454
+ **Props**: 继承自 `NsInput`,自动设置校验规则
455
+
456
+ **示例**:
457
+
458
+ ```vue
459
+ <ns-id-input
460
+ v-model="idCard"
461
+ name="idCard"
462
+ label="身份证号"
463
+ />
464
+ ```
465
+
466
+ ---
467
+
468
+ ### 5. Textarea 多行文本框
469
+
470
+ **组件名称**: `<ns-textarea>`
471
+
472
+ **用途**: 多行文本输入
473
+
474
+ **Props**: 继承自 `NsInput`,额外支持:
475
+
476
+ | 属性名 | 类型 | 默认值 | 说明 |
477
+ |--------|------|--------|------|
478
+ | `rows` | `number` | `3` | 行数 |
479
+ | `autoSize` | `boolean` | `false` | 自适应高度 |
480
+
481
+ **示例**:
482
+
483
+ ```vue
484
+ <ns-textarea
485
+ v-model="comment"
486
+ name="comment"
487
+ label="备注"
488
+ :rows="5"
489
+ placeholder="请输入备注"
490
+ />
491
+ ```
492
+
493
+ ---
494
+
495
+ ### 6. Select 下拉选择
496
+
497
+ **组件名称**: `<ns-select>`
498
+
499
+ **用途**: 下拉选择单个选项(移动端会弹出选择器)
500
+
501
+ **Props**:
502
+
503
+ | 属性名 | 类型 | 默认值 | 说明 |
504
+ |--------|------|--------|------|
505
+ | `modelValue` | `string\|number` | - | 选中值 |
506
+ | `name` | `string` | - | 表单项名称 |
507
+ | `label` | `string` | - | 标签文字 |
508
+ | `options` | `SelectOption[]` | - | 选项数据 |
509
+ | `placeholder` | `string` | - | 占位符 |
510
+ | `disabled` | `boolean` | `false` | 是否禁用 |
511
+ | `rules` | `ValidationRule[]` | - | 校验规则 |
512
+
513
+ **选项数据格式**:
514
+
515
+ ```typescript
516
+ type SelectOption = {
517
+ text: string // 显示文字
518
+ value: string | number // 选项值
519
+ disabled?: boolean // 是否禁用
520
+ }
521
+ ```
522
+
523
+ **示例**:
524
+
525
+ ```vue
526
+ <template>
527
+ <ns-select
528
+ v-model="selectedCity"
529
+ name="city"
530
+ label="城市"
531
+ :options="cityOptions"
532
+ placeholder="请选择城市"
533
+ />
534
+ </template>
535
+
536
+ <script setup>
537
+ import { ref } from 'vue'
538
+
539
+ const selectedCity = ref('')
540
+ const cityOptions = [
541
+ { text: '北京', value: 'beijing' },
542
+ { text: '上海', value: 'shanghai' },
543
+ { text: '深圳', value: 'shenzhen' }
544
+ ]
545
+ </script>
546
+ ```
547
+
548
+ ---
549
+
550
+ ### 7. DateSelect 日期选择
551
+
552
+ **组件名称**: `<ns-date-select>`
553
+
554
+ **用途**: 日期选择(移动端友好的日期选择器)
555
+
556
+ **Props**:
557
+
558
+ | 属性名 | 类型 | 默认值 | 说明 |
559
+ |--------|------|--------|------|
560
+ | `modelValue` | `string` | - | 日期值(YYYY-MM-DD) |
561
+ | `name` | `string` | - | 表单项名称 |
562
+ | `label` | `string` | - | 标签文字 |
563
+ | `minDate` | `Date` | - | 最小日期 |
564
+ | `maxDate` | `Date` | - | 最大日期 |
565
+
566
+ **示例**:
567
+
568
+ ```vue
569
+ <ns-date-select
570
+ v-model="birthDate"
571
+ name="birthDate"
572
+ label="出生日期"
573
+ />
574
+ ```
575
+
576
+ ---
577
+
578
+ ### 8. Checkbox 复选框
579
+
580
+ **组件名称**: `<ns-checkbox>`
581
+
582
+ **用途**: 单个复选框
583
+
584
+ **Props**:
585
+
586
+ | 属性名 | 类型 | 默认值 | 说明 |
587
+ |--------|------|--------|------|
588
+ | `modelValue` | `boolean` | - | 选中状态 |
589
+ | `label` | `string` | - | 标签文字 |
590
+ | `disabled` | `boolean` | `false` | 是否禁用 |
591
+
592
+ **示例**:
593
+
594
+ ```vue
595
+ <ns-checkbox v-model="agreed" label="我已阅读并同意用户协议" />
596
+ ```
597
+
598
+ ---
599
+
600
+ ### 9. CheckboxGroup 复选框组
601
+
602
+ **组件名称**: `<ns-checkbox-group>`
603
+
604
+ **用途**: 多个复选框组
605
+
606
+ **Props**:
607
+
608
+ | 属性名 | 类型 | 默认值 | 说明 |
609
+ |--------|------|--------|------|
610
+ | `modelValue` | `(string\|number)[]` | - | 选中值数组 |
611
+ | `name` | `string` | - | 表单项名称 |
612
+ | `options` | `SelectOption[]` | - | 选项数据 |
613
+ | `max` | `number` | - | 最大可选数 |
614
+
615
+ **示例**:
616
+
617
+ ```vue
618
+ <ns-checkbox-group
619
+ v-model="interests"
620
+ name="interests"
621
+ :options="[
622
+ { text: '篮球', value: 'basketball' },
623
+ { text: '足球', value: 'football' },
624
+ { text: '游泳', value: 'swimming' }
625
+ ]"
626
+ :max="2"
627
+ />
628
+ ```
629
+
630
+ ---
631
+
632
+ ### 10. RadioGroup 单选框组
633
+
634
+ **组件名称**: `<ns-radio-group>`
635
+
636
+ **用途**: 单选框组
637
+
638
+ **Props**:
639
+
640
+ | 属性名 | 类型 | 默认值 | 说明 |
641
+ |--------|------|--------|------|
642
+ | `modelValue` | `string\|number` | - | 选中值 |
643
+ | `name` | `string` | - | 表单项名称 |
644
+ | `options` | `SelectOption[]` | - | 选项数据 |
645
+ | `direction` | `'horizontal'\|'vertical'` | `'vertical'` | 排列方向 |
646
+
647
+ **示例**:
648
+
649
+ ```vue
650
+ <ns-radio-group
651
+ v-model="gender"
652
+ name="gender"
653
+ :options="[
654
+ { text: '男', value: 'male' },
655
+ { text: '女', value: 'female' }
656
+ ]"
657
+ direction="horizontal"
658
+ />
659
+ ```
660
+
661
+ ---
662
+
663
+ ### 11. Switch 开关
664
+
665
+ **组件名称**: `<ns-switch>`
666
+
667
+ **用途**: 开关切换
668
+
669
+ **Props**:
670
+
671
+ | 属性名 | 类型 | 默认值 | 说明 |
672
+ |--------|------|--------|------|
673
+ | `modelValue` | `boolean` | - | 开关状态 |
674
+ | `disabled` | `boolean` | `false` | 是否禁用 |
675
+ | `activeText` | `string` | - | 打开时的文字 |
676
+ | `inactiveText` | `string` | - | 关闭时的文字 |
677
+
678
+ **示例**:
679
+
680
+ ```vue
681
+ <ns-switch
682
+ v-model="enabled"
683
+ active-text="开启"
684
+ inactive-text="关闭"
685
+ />
686
+ ```
687
+
688
+ ---
689
+
690
+ ### 12. Rating 评分
691
+
692
+ **组件名称**: `<ns-rating-input>`
693
+
694
+ **用途**: 星级评分输入
695
+
696
+ **Props**:
697
+
698
+ | 属性名 | 类型 | 默认值 | 说明 |
699
+ |--------|------|--------|------|
700
+ | `modelValue` | `number` | - | 评分值 |
701
+ | `name` | `string` | - | 表单项名称 |
702
+ | `count` | `number` | `5` | 星星总数 |
703
+ | `allowHalf` | `boolean` | `false` | 是否允许半星 |
704
+ | `disabled` | `boolean` | `false` | 是否禁用 |
705
+
706
+ **示例**:
707
+
708
+ ```vue
709
+ <ns-rating-input
710
+ v-model="rating"
711
+ name="rating"
712
+ :count="5"
713
+ allow-half
714
+ />
715
+ ```
716
+
717
+ ---
718
+
719
+ ### 13. Stepper 步进器
720
+
721
+ **组件名称**: `<ns-stepper>`
722
+
723
+ **用途**: 数字增减器
724
+
725
+ **Props**:
726
+
727
+ | 属性名 | 类型 | 默认值 | 说明 |
728
+ |--------|------|--------|------|
729
+ | `modelValue` | `number` | - | 数字值 |
730
+ | `min` | `number` | `0` | 最小值 |
731
+ | `max` | `number` | `999` | 最大值 |
732
+ | `step` | `number` | `1` | 步长 |
733
+ | `disabled` | `boolean` | `false` | 是否禁用 |
734
+ | `inputWidth` | `string` | `'40px'` | 输入框宽度 |
735
+
736
+ **示例**:
737
+
738
+ ```vue
739
+ <ns-stepper
740
+ v-model="quantity"
741
+ :min="1"
742
+ :max="99"
743
+ :step="1"
744
+ />
745
+ ```
746
+
747
+ ---
748
+
749
+ ### 14. Upload 文件上传
750
+
751
+ **组件名称**: `<ns-upload>`
752
+
753
+ **用途**: 文件/图片上传
754
+
755
+ **Props**:
756
+
757
+ | 属性名 | 类型 | 默认值 | 说明 |
758
+ |--------|------|--------|------|
759
+ | `modelValue` | `File[]` | - | 文件列表 |
760
+ | `accept` | `string` | `'image/*'` | 接受的文件类型 |
761
+ | `multiple` | `boolean` | `false` | 是否支持多选 |
762
+ | `maxSize` | `number` | - | 最大文件大小(字节) |
763
+ | `maxCount` | `number` | `9` | 最大文件数量 |
764
+ | `capture` | `boolean` | `false` | 是否调起摄像头 |
765
+
766
+ **示例**:
767
+
768
+ ```vue
769
+ <!-- 图片上传 -->
770
+ <ns-upload
771
+ v-model="images"
772
+ accept="image/*"
773
+ :max-count="9"
774
+ multiple
775
+ />
776
+
777
+ <!-- 拍照上传 -->
778
+ <ns-upload
779
+ v-model="photo"
780
+ capture
781
+ />
782
+ ```
783
+
784
+ ---
785
+
786
+ ## 数据展示组件
787
+
788
+ ### 1. List 列表
789
+
790
+ **组件名称**: `<ns-list>`
791
+
792
+ **用途**: 展示列表数据,支持下拉刷新、上拉加载
793
+
794
+ **Props**:
795
+
796
+ | 属性名 | 类型 | 默认值 | 说明 |
797
+ |--------|------|--------|------|
798
+ | `data` | `any[]` | `[]` | 列表数据 |
799
+ | `loading` | `boolean` | `false` | 是否加载中 |
800
+ | `finished` | `boolean` | `false` | 是否加载完成 |
801
+ | `refreshable` | `boolean` | `false` | 是否支持下拉刷新 |
802
+ | `loadmore` | `boolean` | `false` | 是否支持上拉加载更多 |
803
+
804
+ **Events**:
805
+
806
+ | 事件名 | 参数 | 说明 |
807
+ |--------|------|------|
808
+ | `refresh` | `()` | 下拉刷新 |
809
+ | `load` | `()` | 上拉加载更多 |
810
+
811
+ **示例**:
812
+
813
+ ```vue
814
+ <template>
815
+ <ns-list
816
+ :data="items"
817
+ :loading="loading"
818
+ :finished="finished"
819
+ refreshable
820
+ loadmore
821
+ @refresh="handleRefresh"
822
+ @load="handleLoad"
823
+ >
824
+ <template #default="{ item }">
825
+ <div class="list-item">
826
+ <h3>{{ item.title }}</h3>
827
+ <p>{{ item.desc }}</p>
828
+ </div>
829
+ </template>
830
+ </ns-list>
831
+ </template>
832
+
833
+ <script setup>
834
+ import { ref } from 'vue'
835
+
836
+ const items = ref([])
837
+ const loading = ref(false)
838
+ const finished = ref(false)
839
+ const page = ref(1)
840
+
841
+ const handleRefresh = async () => {
842
+ page.value = 1
843
+ finished.value = false
844
+ await loadData()
845
+ }
846
+
847
+ const handleLoad = async () => {
848
+ page.value++
849
+ await loadData()
850
+ }
851
+
852
+ const loadData = async () => {
853
+ loading.value = true
854
+ try {
855
+ const response = await api.getList(page.value)
856
+ if (page.value === 1) {
857
+ items.value = response.data
858
+ } else {
859
+ items.value.push(...response.data)
860
+ }
861
+ finished.value = !response.hasMore
862
+ } finally {
863
+ loading.value = false
864
+ }
865
+ }
866
+ </script>
867
+ ```
868
+
869
+ ---
870
+
871
+ ### 2. Card 卡片
872
+
873
+ **组件名称**: `<ns-card>`
874
+
875
+ **用途**: 卡片容器
876
+
877
+ **Props**:
878
+
879
+ | 属性名 | 类型 | 默认值 | 说明 |
880
+ |--------|------|--------|------|
881
+ | `title` | `string` | - | 卡片标题 |
882
+ | `padding` | `Size` | `'md'` | 内边距 |
883
+ | `radius` | `Size` | `'md'` | 圆角大小 |
884
+ | `shadow` | `boolean` | `false` | 是否显示阴影 |
885
+
886
+ **Slots**:
887
+
888
+ - `default`: 卡片内容
889
+ - `header`: 自定义头部
890
+ - `footer`: 底部内容
891
+
892
+ **示例**:
893
+
894
+ ```vue
895
+ <ns-card title="商品信息">
896
+ <p>商品名称:iPhone 15 Pro</p>
897
+ <p>价格:¥7999</p>
898
+ <template #footer>
899
+ <ns-button label="立即购买" block />
900
+ </template>
901
+ </ns-card>
902
+ ```
903
+
904
+ ---
905
+
906
+ ### 3. Empty 空状态
907
+
908
+ **组件名称**: `<ns-empty>`
909
+
910
+ **用途**: 展示空状态
911
+
912
+ **Props**:
913
+
914
+ | 属性名 | 类型 | 默认值 | 说明 |
915
+ |--------|------|--------|------|
916
+ | `description` | `string` | `'暂无数据'` | 描述文字 |
917
+ | `image` | `string` | - | 空状态图片 |
918
+
919
+ **示例**:
920
+
921
+ ```vue
922
+ <ns-empty description="暂无订单" />
923
+ ```
924
+
925
+ ---
926
+
927
+ ### 4. Timeline 时间轴
928
+
929
+ **组件名称**: `<ns-timeline>`
930
+
931
+ **用途**: 展示时间流信息
932
+
933
+ **Props**:
934
+
935
+ | 属性名 | 类型 | 默认值 | 说明 |
936
+ |--------|------|--------|------|
937
+ | `items` | `TimelineItem[]` | `[]` | 时间轴数据 |
938
+
939
+ **示例**:
940
+
941
+ ```vue
942
+ <ns-timeline
943
+ :items="[
944
+ { time: '2024-01-01 10:00', content: '订单创建' },
945
+ { time: '2024-01-01 12:00', content: '商家接单' },
946
+ { time: '2024-01-01 14:00', content: '配送中' },
947
+ { time: '2024-01-01 15:30', content: '订单完成' }
948
+ ]"
949
+ />
950
+ ```
951
+
952
+ ---
953
+
954
+ ### 5. Facts 事实列表
955
+
956
+ **组件名称**: `<ns-facts>`
957
+
958
+ **用途**: 展示键值对信息(详情页常用)
959
+
960
+ **示例**:
961
+
962
+ ```vue
963
+ <ns-facts>
964
+ <ns-facts-item label="订单号" value="202401010001" />
965
+ <ns-facts-item label="下单时间" value="2024-01-01 10:00" />
966
+ <ns-facts-item label="支付方式" value="微信支付" />
967
+ <ns-facts-item label="配送地址" value="北京市朝阳区..." />
968
+ </ns-facts>
969
+ ```
970
+
971
+ ---
972
+
973
+ ### 6. Skeleton 骨架屏
974
+
975
+ **组件名称**: `<ns-skeleton>`
976
+
977
+ **用途**: 加载状态占位
978
+
979
+ **Props**:
980
+
981
+ | 属性名 | 类型 | 默认值 | 说明 |
982
+ |--------|------|--------|------|
983
+ | `loading` | `boolean` | `true` | 是否显示骨架屏 |
984
+ | `rows` | `number` | `3` | 骨架屏行数 |
985
+ | `animated` | `boolean` | `true` | 是否显示动画 |
986
+
987
+ **示例**:
988
+
989
+ ```vue
990
+ <ns-skeleton :loading="loading" :rows="5">
991
+ <div v-if="!loading">
992
+ <!-- 实际内容 -->
993
+ </div>
994
+ </ns-skeleton>
995
+ ```
996
+
997
+ ---
998
+
999
+ ### 7. Preview 图片预览
1000
+
1001
+ **组件名称**: `<ns-preview>`
1002
+
1003
+ **用途**: 图片预览(点击放大查看)
1004
+
1005
+ **Props**:
1006
+
1007
+ | 属性名 | 类型 | 默认值 | 说明 |
1008
+ |--------|------|--------|------|
1009
+ | `images` | `string[]` | - | 图片列表 |
1010
+ | `current` | `number` | `0` | 当前显示的图片索引 |
1011
+
1012
+ **示例**:
1013
+
1014
+ ```vue
1015
+ <ns-preview
1016
+ :images="[
1017
+ '/image1.jpg',
1018
+ '/image2.jpg',
1019
+ '/image3.jpg'
1020
+ ]"
1021
+ :current="0"
1022
+ />
1023
+ ```
1024
+
1025
+ ---
1026
+
1027
+ ### 8. Watermark 水印
1028
+
1029
+ **组件名称**: `<ns-watermark>`
1030
+
1031
+ **用途**: 为页面添加水印
1032
+
1033
+ **Props**:
1034
+
1035
+ | 属性名 | 类型 | 默认值 | 说明 |
1036
+ |--------|------|--------|------|
1037
+ | `content` | `string` | - | 水印文字 |
1038
+ | `rotate` | `number` | `-22` | 旋转角度 |
1039
+ | `zIndex` | `number` | `1000` | 层级 |
1040
+
1041
+ **示例**:
1042
+
1043
+ ```vue
1044
+ <ns-watermark content="内部资料">
1045
+ <div>页面内容</div>
1046
+ </ns-watermark>
1047
+ ```
1048
+
1049
+ ---
1050
+
1051
+ ## 反馈组件
1052
+
1053
+ ### 1. Dialog 对话框
1054
+
1055
+ **组件名称**: `<ns-dialog>`
1056
+
1057
+ **用途**: 模态对话框
1058
+
1059
+ **Props**:
1060
+
1061
+ | 属性名 | 类型 | 默认值 | 说明 |
1062
+ |--------|------|--------|------|
1063
+ | `modelValue` | `boolean` | - | 是否显示 |
1064
+ | `title` | `string` | - | 对话框标题 |
1065
+ | `okText` | `string` | `'确定'` | 确定按钮文字 |
1066
+ | `cancelText` | `string` | `'取消'` | 取消按钮文字 |
1067
+ | `footer` | `boolean` | `true` | 是否显示底部 |
1068
+ | `closeOnClickOverlay` | `boolean` | `true` | 点击遮罩是否关闭 |
1069
+
1070
+ **Events**:
1071
+
1072
+ | 事件名 | 参数 | 说明 |
1073
+ |--------|------|------|
1074
+ | `ok` | `()` | 点击确定 |
1075
+ | `close` | `()` | 关闭对话框 |
1076
+
1077
+ **示例**:
1078
+
1079
+ ```vue
1080
+ <template>
1081
+ <ns-button label="打开对话框" @click="visible = true" />
1082
+
1083
+ <ns-dialog
1084
+ v-model="visible"
1085
+ title="提示"
1086
+ @ok="handleOk"
1087
+ >
1088
+ <p>确定要执行此操作吗?</p>
1089
+ </ns-dialog>
1090
+ </template>
1091
+
1092
+ <script setup>
1093
+ import { ref } from 'vue'
1094
+
1095
+ const visible = ref(false)
1096
+
1097
+ const handleOk = () => {
1098
+ // 处理确定逻辑
1099
+ visible.value = false
1100
+ }
1101
+ </script>
1102
+ ```
1103
+
1104
+ ---
1105
+
1106
+ ### 2. Sheet 底部弹出层
1107
+
1108
+ **组件名称**: `<ns-sheet>`
1109
+
1110
+ **用途**: 从底部弹出的操作菜单
1111
+
1112
+ **Props**:
1113
+
1114
+ | 属性名 | 类型 | 默认值 | 说明 |
1115
+ |--------|------|--------|------|
1116
+ | `modelValue` | `boolean` | - | 是否显示 |
1117
+ | `title` | `string` | - | 标题 |
1118
+ | `items` | `SheetItem[]` | - | 菜单项 |
1119
+
1120
+ **Events**:
1121
+
1122
+ | 事件名 | 参数 | 说明 |
1123
+ |--------|------|------|
1124
+ | `select` | `(item: SheetItem)` | 选择菜单项 |
1125
+
1126
+ **示例**:
1127
+
1128
+ ```vue
1129
+ <ns-sheet
1130
+ v-model="sheetVisible"
1131
+ title="选择操作"
1132
+ :items="[
1133
+ { text: '拍照', value: 'camera' },
1134
+ { text: '从相册选择', value: 'album' },
1135
+ { text: '取消', value: 'cancel' }
1136
+ ]"
1137
+ @select="handleSelect"
1138
+ />
1139
+ ```
1140
+
1141
+ ---
1142
+
1143
+ ### 3. Popover 气泡弹出框
1144
+
1145
+ **组件名称**: `<ns-popover>`
1146
+
1147
+ **用途**: 气泡式提示
1148
+
1149
+ **Props**:
1150
+
1151
+ | 属性名 | 类型 | 默认值 | 说明 |
1152
+ |--------|------|--------|------|
1153
+ | `content` | `string` | - | 显示内容 |
1154
+ | `trigger` | `'click'\|'manual'` | `'click'` | 触发方式 |
1155
+ | `placement` | `'top'\|'bottom'\|'left'\|'right'` | `'bottom'` | 弹出位置 |
1156
+
1157
+ **示例**:
1158
+
1159
+ ```vue
1160
+ <ns-popover content="这是提示信息" placement="top">
1161
+ <ns-button label="点击查看" />
1162
+ </ns-popover>
1163
+ ```
1164
+
1165
+ ---
1166
+
1167
+ ### 4. Loading 加载
1168
+
1169
+ **组件名称**: `<ns-loading>`
1170
+
1171
+ **用途**: 局部加载状态
1172
+
1173
+ **Props**:
1174
+
1175
+ | 属性名 | 类型 | 默认值 | 说明 |
1176
+ |--------|------|--------|------|
1177
+ | `visible` | `boolean` | `true` | 是否显示 |
1178
+ | `tip` | `string` | - | 提示文字 |
1179
+ | `size` | `Size` | `'md'` | 加载图标大小 |
1180
+
1181
+ **示例**:
1182
+
1183
+ ```vue
1184
+ <ns-loading :visible="loading" tip="加载中..." />
1185
+ ```
1186
+
1187
+ ---
1188
+
1189
+ ### 5. Drawer 抽屉
1190
+
1191
+ **组件名称**: `<ns-drawer>`
1192
+
1193
+ **用途**: 从侧边滑出的抽屉面板
1194
+
1195
+ **Props**:
1196
+
1197
+ | 属性名 | 类型 | 默认值 | 说明 |
1198
+ |--------|------|--------|------|
1199
+ | `modelValue` | `boolean` | - | 是否显示 |
1200
+ | `title` | `string` | - | 抽屉标题 |
1201
+ | `placement` | `'left'\|'right'\|'top'\|'bottom'` | `'right'` | 抽屉方向 |
1202
+ | `height` | `string` | `'60%'` | 高度(bottom/top时) |
1203
+
1204
+ **示例**:
1205
+
1206
+ ```vue
1207
+ <ns-drawer
1208
+ v-model="drawerVisible"
1209
+ title="筛选"
1210
+ placement="bottom"
1211
+ height="70%"
1212
+ >
1213
+ <!-- 筛选内容 -->
1214
+ </ns-drawer>
1215
+ ```
1216
+
1217
+ ---
1218
+
1219
+ ## 布局组件
1220
+
1221
+ ### 1. Row / Column 弹性布局
1222
+
1223
+ **组件名称**: `<ns-row>` / `<ns-column>`
1224
+
1225
+ **用途**: Flex 布局容器
1226
+
1227
+ **Props**:
1228
+
1229
+ | 属性名 | 类型 | 默认值 | 说明 |
1230
+ |--------|------|--------|------|
1231
+ | `gap` | `Size\|number` | - | 子元素间距 |
1232
+ | `justify` | `'start'\|'center'\|'end'\|'between'\|'around'` | - | 主轴对齐方式 |
1233
+ | `align` | `'start'\|'center'\|'end'\|'stretch'` | - | 交叉轴对齐方式 |
1234
+ | `wrap` | `boolean` | `false` | 是否换行 |
1235
+
1236
+ **示例**:
1237
+
1238
+ ```vue
1239
+ <!-- 水平排列 -->
1240
+ <ns-row gap="md" justify="between" align="center">
1241
+ <ns-button label="按钮1" />
1242
+ <ns-button label="按钮2" />
1243
+ <ns-button label="按钮3" />
1244
+ </ns-row>
1245
+
1246
+ <!-- 垂直排列 -->
1247
+ <ns-column gap="lg">
1248
+ <ns-card title="卡片1" />
1249
+ <ns-card title="卡片2" />
1250
+ <ns-card title="卡片3" />
1251
+ </ns-column>
1252
+ ```
1253
+
1254
+ ---
1255
+
1256
+ ### 2. Grid 网格
1257
+
1258
+ **组件名称**: `<ns-grid>`
1259
+
1260
+ **用途**: Grid 布局容器
1261
+
1262
+ **Props**:
1263
+
1264
+ | 属性名 | 类型 | 默认值 | 说明 |
1265
+ |--------|------|--------|------|
1266
+ | `columns` | `number` | `4` | 列数 |
1267
+ | `gap` | `Size\|number` | - | 间距 |
1268
+
1269
+ **示例**:
1270
+
1271
+ ```vue
1272
+ <ns-grid :columns="3" gap="md">
1273
+ <div v-for="i in 9" :key="i">
1274
+ <ns-button :label="`按钮${i}`" block />
1275
+ </div>
1276
+ </ns-grid>
1277
+ ```
1278
+
1279
+ ---
1280
+
1281
+ ### 3. Scrollable 滚动容器
1282
+
1283
+ **组件名称**: `<ns-scrollable>`
1284
+
1285
+ **用途**: 可滚动区域(支持下拉刷新)
1286
+
1287
+ **Props**:
1288
+
1289
+ | 属性名 | 类型 | 默认值 | 说明 |
1290
+ |--------|------|--------|------|
1291
+ | `height` | `string` | - | 容器高度 |
1292
+ | `refreshable` | `boolean` | `false` | 是否支持下拉刷新 |
1293
+
1294
+ **Events**:
1295
+
1296
+ | 事件名 | 参数 | 说明 |
1297
+ |--------|------|------|
1298
+ | `refresh` | `()` | 下拉刷新 |
1299
+ | `scrollEnd` | `()` | 滚动到底部 |
1300
+
1301
+ **示例**:
1302
+
1303
+ ```vue
1304
+ <ns-scrollable
1305
+ height="100vh"
1306
+ refreshable
1307
+ @refresh="handleRefresh"
1308
+ @scroll-end="handleScrollEnd"
1309
+ >
1310
+ <div v-for="item in items" :key="item.id">
1311
+ {{ item.content }}
1312
+ </div>
1313
+ </ns-scrollable>
1314
+ ```
1315
+
1316
+ ---
1317
+
1318
+ ### 4. Divider 分割线
1319
+
1320
+ **组件名称**: `<ns-divider>`
1321
+
1322
+ **用途**: 分隔内容
1323
+
1324
+ **Props**:
1325
+
1326
+ | 属性名 | 类型 | 默认值 | 说明 |
1327
+ |--------|------|--------|------|
1328
+ | `dashed` | `boolean` | `false` | 是否虚线 |
1329
+ | `hairline` | `boolean` | `true` | 是否细线(0.5px) |
1330
+
1331
+ **示例**:
1332
+
1333
+ ```vue
1334
+ <ns-divider />
1335
+ <ns-divider dashed />
1336
+ ```
1337
+
1338
+ ---
1339
+
1340
+ ## 导航组件
1341
+
1342
+ ### 1. Tabbar 标签栏
1343
+
1344
+ **组件名称**: `<ns-tabbar>`
1345
+
1346
+ **用途**: 底部导航栏
1347
+
1348
+ **Props**:
1349
+
1350
+ | 属性名 | 类型 | 默认值 | 说明 |
1351
+ |--------|------|--------|------|
1352
+ | `modelValue` | `string\|number` | - | 当前激活的标签 |
1353
+ | `items` | `TabbarItem[]` | - | 标签项数据 |
1354
+ | `fixed` | `boolean` | `true` | 是否固定在底部 |
1355
+ | `safeAreaInsetBottom` | `boolean` | `true` | 是否开启底部安全区适配 |
1356
+
1357
+ **标签项数据格式**:
1358
+
1359
+ ```typescript
1360
+ type TabbarItem = {
1361
+ text: string
1362
+ value: string | number
1363
+ icon?: string
1364
+ badge?: string | number
1365
+ }
1366
+ ```
1367
+
1368
+ **示例**:
1369
+
1370
+ ```vue
1371
+ <template>
1372
+ <ns-tabbar v-model="activeTab" :items="tabbarItems" />
1373
+ </template>
1374
+
1375
+ <script setup>
1376
+ import { ref } from 'vue'
1377
+
1378
+ const activeTab = ref('home')
1379
+ const tabbarItems = [
1380
+ { text: '首页', value: 'home', icon: 'home' },
1381
+ { text: '分类', value: 'category', icon: 'category' },
1382
+ { text: '购物车', value: 'cart', icon: 'cart', badge: 5 },
1383
+ { text: '我的', value: 'mine', icon: 'user' }
1384
+ ]
1385
+ </script>
1386
+ ```
1387
+
1388
+ ---
1389
+
1390
+ ### 2. Tabs 标签页
1391
+
1392
+ **组件名称**: `<ns-tabs>`
1393
+
1394
+ **用途**: 选项卡切换
1395
+
1396
+ **Props**:
1397
+
1398
+ | 属性名 | 类型 | 默认值 | 说明 |
1399
+ |--------|------|--------|------|
1400
+ | `modelValue` | `string\|number` | - | 当前激活的标签 |
1401
+ | `items` | `TabsItem[]` | - | 标签项数据 |
1402
+ | `variant` | `'line'\|'card'\|'button'` | `'line'` | 标签样式 |
1403
+ | `swipeable` | `boolean` | `false` | 是否开启手势滑动切换 |
1404
+ | `sticky` | `boolean` | `false` | 是否吸顶 |
1405
+
1406
+ **示例**:
1407
+
1408
+ ```vue
1409
+ <ns-tabs
1410
+ v-model="activeTab"
1411
+ :items="[
1412
+ { text: '全部', value: 'all' },
1413
+ { text: '待付款', value: 'unpaid' },
1414
+ { text: '待发货', value: 'unshipped' },
1415
+ { text: '已完成', value: 'finished' }
1416
+ ]"
1417
+ swipeable
1418
+ sticky
1419
+ >
1420
+ <ns-tabs-item value="all">
1421
+ <!-- 全部订单内容 -->
1422
+ </ns-tabs-item>
1423
+ <ns-tabs-item value="unpaid">
1424
+ <!-- 待付款订单内容 -->
1425
+ </ns-tabs-item>
1426
+ <!-- ... -->
1427
+ </ns-tabs>
1428
+ ```
1429
+
1430
+ ---
1431
+
1432
+ ### 3. Pagination 分页
1433
+
1434
+ **组件名称**: `<ns-pagination>`
1435
+
1436
+ **用途**: 分页导航(移动端简化版)
1437
+
1438
+ **Props**:
1439
+
1440
+ | 属性名 | 类型 | 默认值 | 说明 |
1441
+ |--------|------|--------|------|
1442
+ | `modelValue` | `number` | - | 当前页码 |
1443
+ | `total` | `number` | - | 总页数 |
1444
+
1445
+ **示例**:
1446
+
1447
+ ```vue
1448
+ <ns-pagination
1449
+ v-model="currentPage"
1450
+ :total="10"
1451
+ @change="handlePageChange"
1452
+ />
1453
+ ```
1454
+
1455
+ ---
1456
+
1457
+ ## 通用属性
1458
+
1459
+ ### 尺寸(Size)
1460
+
1461
+ | 值 | 说明 | 移动端常用场景 |
1462
+ |----|------|----------------|
1463
+ | `'xs'` | 超小 | 小图标、小标签 |
1464
+ | `'sm'` | 小 | 次要按钮、小卡片 |
1465
+ | `'md'` | 中等(默认) | 主按钮、标准组件 |
1466
+ | `'lg'` | 大 | 大按钮、大卡片 |
1467
+ | `'xl'` | 超大 | 重要操作按钮 |
1468
+
1469
+ ---
1470
+
1471
+ ### 颜色(Color)
1472
+
1473
+ | 值 | 说明 | 适用场景 |
1474
+ |----|------|----------|
1475
+ | `'primary'` | 主色 | 主要操作、强调 |
1476
+ | `'success'` | 成功色 | 成功提示、完成状态 |
1477
+ | `'warning'` | 警告色 | 警告提示、待处理 |
1478
+ | `'danger'` | 危险色 | 删除、错误提示 |
1479
+ | `'info'` | 信息色 | 一般信息提示 |
1480
+
1481
+ ---
1482
+
1483
+ ## 服务方法
1484
+
1485
+ ### 1. Toast 轻提示
1486
+
1487
+ **方法**: `$n.toast(message, options?)`
1488
+
1489
+ **参数**:
1490
+
1491
+ ```typescript
1492
+ type ToastOptions = {
1493
+ type?: 'info' | 'success' | 'error' | 'warning'
1494
+ duration?: number // 持续时间(秒),默认2秒
1495
+ position?: 'top' | 'center' | 'bottom'
1496
+ }
1497
+ ```
1498
+
1499
+ **示例**:
1500
+
1501
+ ```javascript
1502
+ // 普通提示
1503
+ $n.toast('操作成功')
1504
+
1505
+ // 成功提示
1506
+ $n.toast('保存成功', { type: 'success' })
1507
+
1508
+ // 错误提示
1509
+ $n.toast('操作失败', { type: 'error' })
1510
+
1511
+ // 长时间提示
1512
+ $n.toast('提交中,请稍候...', { duration: 5 })
1513
+ ```
1514
+
1515
+ ---
1516
+
1517
+ ### 2. Loading 全局加载
1518
+
1519
+ **方法**:
1520
+ - `$n.loading.show(message?)` - 显示加载
1521
+ - `$n.loading.hide()` - 隐藏加载
1522
+
1523
+ **示例**:
1524
+
1525
+ ```javascript
1526
+ // 显示加载
1527
+ $n.loading.show('加载中...')
1528
+
1529
+ // 异步操作
1530
+ await fetchData()
1531
+
1532
+ // 隐藏加载
1533
+ $n.loading.hide()
1534
+ ```
1535
+
1536
+ ---
1537
+
1538
+ ### 3. Dialog 对话框
1539
+
1540
+ **方法**: `$n.dialog(options)`
1541
+
1542
+ **示例**:
1543
+
1544
+ ```javascript
1545
+ $n.dialog({
1546
+ title: '提示',
1547
+ content: '确定要删除吗?',
1548
+ onOk: () => {
1549
+ // 确定操作
1550
+ }
1551
+ })
1552
+ ```
1553
+
1554
+ ---
1555
+
1556
+ ### 4. Confirm 确认框
1557
+
1558
+ **方法**: `$n.confirm(message, onOk)`
1559
+
1560
+ **示例**:
1561
+
1562
+ ```javascript
1563
+ $n.confirm('确定要退出登录吗?', () => {
1564
+ // 退出登录
1565
+ logout()
1566
+ })
1567
+ ```
1568
+
1569
+ ---
1570
+
1571
+ ### 5. Sheet 底部弹出层
1572
+
1573
+ **方法**: `$n.sheet.open(options)`
1574
+
1575
+ **示例**:
1576
+
1577
+ ```javascript
1578
+ $n.sheet.open({
1579
+ title: '选择操作',
1580
+ items: [
1581
+ { text: '编辑', value: 'edit' },
1582
+ { text: '删除', value: 'delete' },
1583
+ { text: '取消', value: 'cancel' }
1584
+ ],
1585
+ onSelect: (item) => {
1586
+ if (item.value === 'edit') {
1587
+ // 编辑操作
1588
+ }
1589
+ }
1590
+ })
1591
+ ```
1592
+
1593
+ ---
1594
+
1595
+ ## 移动端特性
1596
+
1597
+ ### 1. 安全区域适配
1598
+
1599
+ 在 iPhone X 等设备上,组件会自动适配安全区域(刘海屏、底部横条)。
1600
+
1601
+ ```vue
1602
+ <!-- Tabbar 自动适配底部安全区 -->
1603
+ <ns-tabbar
1604
+ :items="tabbarItems"
1605
+ safe-area-inset-bottom
1606
+ />
1607
+ ```
1608
+
1609
+ ---
1610
+
1611
+ ### 2. 触摸反馈
1612
+
1613
+ 移动端组件支持触摸反馈效果,提供更好的交互体验。
1614
+
1615
+ ```vue
1616
+ <!-- 按钮支持触摸高亮 -->
1617
+ <ns-button label="点击我" />
1618
+ ```
1619
+
1620
+ ---
1621
+
1622
+ ### 3. 手势操作
1623
+
1624
+ 支持常见的移动端手势:
1625
+
1626
+ - **滑动**: Tabs 可左右滑动切换
1627
+ - **下拉刷新**: List、Scrollable 支持下拉刷新
1628
+ - **上拉加载**: List 支持上拉加载更多
1629
+
1630
+ ---
1631
+
1632
+ ### 4. 底部弹出选择器
1633
+
1634
+ 移动端的 Select、DateSelect 等组件会从底部弹出选择器,符合移动端交互习惯。
1635
+
1636
+ ```vue
1637
+ <ns-select
1638
+ v-model="city"
1639
+ :options="cityOptions"
1640
+ placeholder="选择城市"
1641
+ />
1642
+ <!-- 点击后从底部弹出选择器 -->
1643
+ ```
1644
+
1645
+ ---
1646
+
1647
+ ### 5. 虚拟列表
1648
+
1649
+ 对于长列表,使用虚拟滚动提升性能:
1650
+
1651
+ ```vue
1652
+ <ns-list
1653
+ :data="longList"
1654
+ virtual
1655
+ :item-height="60"
1656
+ />
1657
+ ```
1658
+
1659
+ ---
1660
+
1661
+ ## 最佳实践
1662
+
1663
+ ### 1. 移动端表单设计
1664
+
1665
+ ```vue
1666
+ <template>
1667
+ <ns-page>
1668
+ <ns-page-header title="用户信息" />
1669
+
1670
+ <ns-page-content>
1671
+ <ns-form v-model="formData" ref="formRef">
1672
+ <ns-input
1673
+ name="name"
1674
+ label="姓名"
1675
+ :rules="['required']"
1676
+ placeholder="请输入姓名"
1677
+ />
1678
+
1679
+ <ns-mobile-input
1680
+ name="phone"
1681
+ label="手机号"
1682
+ :rules="['required', 'mobile']"
1683
+ placeholder="请输入手机号"
1684
+ />
1685
+
1686
+ <ns-select
1687
+ name="city"
1688
+ label="城市"
1689
+ :options="cityOptions"
1690
+ :rules="['required']"
1691
+ placeholder="请选择城市"
1692
+ />
1693
+
1694
+ <ns-date-select
1695
+ name="birthDate"
1696
+ label="出生日期"
1697
+ />
1698
+
1699
+ <ns-textarea
1700
+ name="remark"
1701
+ label="备注"
1702
+ :rows="4"
1703
+ placeholder="请输入备注"
1704
+ />
1705
+ </ns-form>
1706
+ </ns-page-content>
1707
+
1708
+ <ns-page-footer>
1709
+ <ns-button
1710
+ label="提交"
1711
+ color="primary"
1712
+ block
1713
+ @click="handleSubmit"
1714
+ />
1715
+ </ns-page-footer>
1716
+ </ns-page>
1717
+ </template>
1718
+
1719
+ <script setup>
1720
+ import { ref } from 'vue'
1721
+ import { useNutshell } from '@uxda/nutshell/taro'
1722
+
1723
+ const $n = useNutshell()
1724
+ const formRef = ref()
1725
+ const formData = ref({
1726
+ name: '',
1727
+ phone: '',
1728
+ city: '',
1729
+ birthDate: '',
1730
+ remark: ''
1731
+ })
1732
+
1733
+ const cityOptions = [
1734
+ { text: '北京', value: 'beijing' },
1735
+ { text: '上海', value: 'shanghai' },
1736
+ { text: '深圳', value: 'shenzhen' }
1737
+ ]
1738
+
1739
+ const handleSubmit = async () => {
1740
+ const valid = await formRef.value.validate()
1741
+ if (valid) {
1742
+ try {
1743
+ $n.loading.show('提交中...')
1744
+ await api.submitForm(formData.value)
1745
+ $n.toast('提交成功', { type: 'success' })
1746
+ // 返回上一页或跳转
1747
+ } catch (error) {
1748
+ $n.toast('提交失败', { type: 'error' })
1749
+ } finally {
1750
+ $n.loading.hide()
1751
+ }
1752
+ }
1753
+ }
1754
+ </script>
1755
+ ```
1756
+
1757
+ ---
1758
+
1759
+ ### 2. 移动端列表页
1760
+
1761
+ ```vue
1762
+ <template>
1763
+ <ns-page>
1764
+ <ns-page-header title="订单列表" />
1765
+
1766
+ <ns-tabs
1767
+ v-model="activeTab"
1768
+ :items="tabItems"
1769
+ sticky
1770
+ >
1771
+ <ns-tabs-item value="all">
1772
+ <ns-list
1773
+ :data="orders"
1774
+ :loading="loading"
1775
+ :finished="finished"
1776
+ refreshable
1777
+ loadmore
1778
+ @refresh="handleRefresh"
1779
+ @load="handleLoad"
1780
+ >
1781
+ <template #default="{ item }">
1782
+ <ns-card class="order-card">
1783
+ <ns-row justify="between" align="center">
1784
+ <h3>订单号:{{ item.orderNo }}</h3>
1785
+ <ns-chip
1786
+ :label="item.statusText"
1787
+ :color="getStatusColor(item.status)"
1788
+ />
1789
+ </ns-row>
1790
+
1791
+ <ns-divider />
1792
+
1793
+ <ns-row gap="md" align="center">
1794
+ <ns-image
1795
+ :src="item.productImage"
1796
+ width="80"
1797
+ height="80"
1798
+ />
1799
+ <ns-column flex="1">
1800
+ <p>{{ item.productName }}</p>
1801
+ <p class="price">¥{{ item.price }}</p>
1802
+ </ns-column>
1803
+ </ns-row>
1804
+
1805
+ <ns-divider />
1806
+
1807
+ <ns-row justify="end" gap="sm">
1808
+ <ns-button
1809
+ label="查看详情"
1810
+ size="sm"
1811
+ @click="viewDetail(item)"
1812
+ />
1813
+ <ns-button
1814
+ v-if="item.canPay"
1815
+ label="去支付"
1816
+ size="sm"
1817
+ color="primary"
1818
+ @click="goPay(item)"
1819
+ />
1820
+ </ns-row>
1821
+ </ns-card>
1822
+ </template>
1823
+ </ns-list>
1824
+ </ns-tabs-item>
1825
+ </ns-tabs>
1826
+ </ns-page>
1827
+ </template>
1828
+
1829
+ <script setup>
1830
+ import { ref, onMounted } from 'vue'
1831
+
1832
+ const activeTab = ref('all')
1833
+ const orders = ref([])
1834
+ const loading = ref(false)
1835
+ const finished = ref(false)
1836
+ const page = ref(1)
1837
+
1838
+ const tabItems = [
1839
+ { text: '全部', value: 'all' },
1840
+ { text: '待付款', value: 'unpaid' },
1841
+ { text: '待发货', value: 'unshipped' },
1842
+ { text: '已完成', value: 'finished' }
1843
+ ]
1844
+
1845
+ const handleRefresh = async () => {
1846
+ page.value = 1
1847
+ finished.value = false
1848
+ await loadData()
1849
+ }
1850
+
1851
+ const handleLoad = async () => {
1852
+ page.value++
1853
+ await loadData()
1854
+ }
1855
+
1856
+ const loadData = async () => {
1857
+ loading.value = true
1858
+ try {
1859
+ const response = await api.getOrders({
1860
+ page: page.value,
1861
+ status: activeTab.value
1862
+ })
1863
+
1864
+ if (page.value === 1) {
1865
+ orders.value = response.data
1866
+ } else {
1867
+ orders.value.push(...response.data)
1868
+ }
1869
+
1870
+ finished.value = !response.hasMore
1871
+ } finally {
1872
+ loading.value = false
1873
+ }
1874
+ }
1875
+
1876
+ const getStatusColor = (status) => {
1877
+ const colorMap = {
1878
+ unpaid: 'warning',
1879
+ unshipped: 'info',
1880
+ shipped: 'primary',
1881
+ finished: 'success'
1882
+ }
1883
+ return colorMap[status] || 'info'
1884
+ }
1885
+
1886
+ const viewDetail = (order) => {
1887
+ // 跳转到详情页
1888
+ }
1889
+
1890
+ const goPay = (order) => {
1891
+ // 跳转到支付页
1892
+ }
1893
+
1894
+ onMounted(() => {
1895
+ loadData()
1896
+ })
1897
+ </script>
1898
+
1899
+ <style scoped>
1900
+ .order-card {
1901
+ margin-bottom: 12px;
1902
+ }
1903
+
1904
+ .price {
1905
+ color: #ff4d4f;
1906
+ font-size: 18px;
1907
+ font-weight: bold;
1908
+ }
1909
+ </style>
1910
+ ```
1911
+
1912
+ ---
1913
+
1914
+ ### 3. 移动端详情页
1915
+
1916
+ ```vue
1917
+ <template>
1918
+ <ns-page>
1919
+ <ns-page-header
1920
+ title="订单详情"
1921
+ back
1922
+ @back="goBack"
1923
+ />
1924
+
1925
+ <ns-page-content>
1926
+ <!-- 订单状态 -->
1927
+ <ns-card>
1928
+ <ns-row align="center" gap="md">
1929
+ <ns-icon name="success" size="xl" fill="success" />
1930
+ <ns-column>
1931
+ <h2>订单已完成</h2>
1932
+ <p>感谢您的购买</p>
1933
+ </ns-column>
1934
+ </ns-row>
1935
+ </ns-card>
1936
+
1937
+ <!-- 物流信息 -->
1938
+ <ns-card title="物流信息">
1939
+ <ns-timeline :items="logisticsSteps" />
1940
+ </ns-card>
1941
+
1942
+ <!-- 商品信息 -->
1943
+ <ns-card title="商品信息">
1944
+ <ns-row gap="md" align="center">
1945
+ <ns-image
1946
+ :src="order.productImage"
1947
+ width="80"
1948
+ height="80"
1949
+ />
1950
+ <ns-column flex="1">
1951
+ <p>{{ order.productName }}</p>
1952
+ <p class="price">¥{{ order.price }}</p>
1953
+ </ns-column>
1954
+ </ns-row>
1955
+ </ns-card>
1956
+
1957
+ <!-- 订单详情 -->
1958
+ <ns-card title="订单详情">
1959
+ <ns-facts>
1960
+ <ns-facts-item
1961
+ label="订单号"
1962
+ :value="order.orderNo"
1963
+ />
1964
+ <ns-facts-item
1965
+ label="下单时间"
1966
+ :value="order.createdAt"
1967
+ />
1968
+ <ns-facts-item
1969
+ label="支付方式"
1970
+ :value="order.paymentMethod"
1971
+ />
1972
+ <ns-facts-item
1973
+ label="配送地址"
1974
+ :value="order.address"
1975
+ />
1976
+ </ns-facts>
1977
+ </ns-card>
1978
+ </ns-page-content>
1979
+
1980
+ <ns-page-footer>
1981
+ <ns-row gap="sm">
1982
+ <ns-button
1983
+ label="联系客服"
1984
+ flex="1"
1985
+ @click="contactService"
1986
+ />
1987
+ <ns-button
1988
+ label="再来一单"
1989
+ color="primary"
1990
+ flex="1"
1991
+ @click="reorder"
1992
+ />
1993
+ </ns-row>
1994
+ </ns-page-footer>
1995
+ </ns-page>
1996
+ </template>
1997
+
1998
+ <script setup>
1999
+ import { ref, onMounted } from 'vue'
2000
+ import { useRouter } from 'vue-router'
2001
+
2002
+ const router = useRouter()
2003
+ const order = ref({})
2004
+ const logisticsSteps = ref([])
2005
+
2006
+ const goBack = () => {
2007
+ router.back()
2008
+ }
2009
+
2010
+ const contactService = () => {
2011
+ // 联系客服
2012
+ }
2013
+
2014
+ const reorder = () => {
2015
+ // 再来一单
2016
+ }
2017
+
2018
+ onMounted(async () => {
2019
+ // 加载订单详情
2020
+ })
2021
+ </script>
2022
+ ```
2023
+
2024
+ ---
2025
+
2026
+ ### 4. 性能优化建议
2027
+
2028
+ **长列表优化**:
2029
+
2030
+ ```vue
2031
+ <!-- 使用虚拟列表 -->
2032
+ <ns-list
2033
+ :data="longList"
2034
+ virtual
2035
+ :item-height="80"
2036
+ :buffer-size="5"
2037
+ />
2038
+ ```
2039
+
2040
+ **图片懒加载**:
2041
+
2042
+ ```vue
2043
+ <ns-image
2044
+ :src="imageUrl"
2045
+ lazy
2046
+ placeholder="/loading.png"
2047
+ />
2048
+ ```
2049
+
2050
+ **防抖处理**:
2051
+
2052
+ ```javascript
2053
+ import { debounce } from 'lodash-es'
2054
+
2055
+ const handleSearch = debounce((keyword) => {
2056
+ // 搜索逻辑
2057
+ }, 300)
2058
+ ```
2059
+
2060
+ ---
2061
+
2062
+ ### 5. 响应式设计
2063
+
2064
+ ```vue
2065
+ <script setup>
2066
+ import { usePlatform } from '@uxda/nutshell/taro'
2067
+
2068
+ const platform = usePlatform()
2069
+
2070
+ // 根据屏幕宽度调整布局
2071
+ const columns = computed(() => {
2072
+ if (platform.screen.width < 375) return 3
2073
+ if (platform.screen.width < 414) return 4
2074
+ return 5
2075
+ })
2076
+ </script>
2077
+
2078
+ <template>
2079
+ <ns-grid :columns="columns">
2080
+ <!-- 网格内容 -->
2081
+ </ns-grid>
2082
+ </template>
2083
+ ```
2084
+
2085
+ ---
2086
+
2087
+ ### 6. 小程序特殊处理
2088
+
2089
+ **页面高度设置**:
2090
+
2091
+ ```vue
2092
+ <style>
2093
+ /* 小程序页面高度100% */
2094
+ page {
2095
+ height: 100%;
2096
+ }
2097
+ </style>
2098
+ ```
2099
+
2100
+ **授权处理**:
2101
+
2102
+ ```javascript
2103
+ import Taro from '@tarojs/taro'
2104
+
2105
+ // 获取用户信息
2106
+ const getUserInfo = async () => {
2107
+ const { authSetting } = await Taro.getSetting()
2108
+
2109
+ if (!authSetting['scope.userInfo']) {
2110
+ // 请求授权
2111
+ await Taro.authorize({ scope: 'scope.userInfo' })
2112
+ }
2113
+
2114
+ const userInfo = await Taro.getUserInfo()
2115
+ return userInfo
2116
+ }
2117
+ ```
2118
+
2119
+ ---
2120
+
2121
+ ## 附录
2122
+
2123
+ ### 移动端尺寸规范
2124
+
2125
+ | 元素 | 推荐尺寸 |
2126
+ |------|----------|
2127
+ | 按钮高度 | 44px - 48px |
2128
+ | 输入框高度 | 44px - 48px |
2129
+ | 列表项高度 | 不小于 44px |
2130
+ | 图标尺寸 | 24px - 32px |
2131
+ | 字体大小(正文) | 14px - 16px |
2132
+ | 字体大小(标题) | 18px - 20px |
2133
+ | 安全触摸区域 | 不小于 44px × 44px |
2134
+
2135
+ ---
2136
+
2137
+ ### 常见问题
2138
+
2139
+ **Q: 如何处理移动端1px边框问题?**
2140
+
2141
+ A: 组件库已内置处理,使用 `hairline` 属性可以实现0.5px细线效果。
2142
+
2143
+ **Q: 如何适配刘海屏?**
2144
+
2145
+ A: 使用 `safe-area-inset-bottom` 等属性自动适配安全区域。
2146
+
2147
+ **Q: 小程序中如何使用?**
2148
+
2149
+ A: 引入 `@uxda/nutshell/taro` 版本,详见"快速开始"章节。
2150
+
2151
+ **Q: 如何实现下拉刷新?**
2152
+
2153
+ A: 使用 `<ns-list>` 或 `<ns-scrollable>` 组件的 `refreshable` 属性。
2154
+
2155
+ ---
2156
+
2157
+ ## 技术支持
2158
+
2159
+ - GitLab: http://gitlab.ddjf.local/fed/nutshell
2160
+ - 版本: 1.6.92
2161
+ - 支持平台: H5、微信小程序
2162
+
2163
+ ---
2164
+
2165
+ **最后更新**: 2024-11-13
2166
+