@cghbcf/xp-ui 1.0.0 → 1.1.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.
package/README.md ADDED
@@ -0,0 +1,1162 @@
1
+ # @cghbcf/xp-ui
2
+
3
+ > Vue3 组件库 - 基于 Vue3 + Vite 构建的现代化 UI 组件库
4
+
5
+ ## 特性
6
+
7
+ - ⚡ 基于 Vue3 和 Vite,性能卓越
8
+ - 📦 开箱即用,无需繁琐配置
9
+ - 🎨 现代化设计风格,支持主题定制(浅色 / 暗黑双主题)
10
+ - 🎛️ 可视化主题定制面板 —— 实时修改颜色、一键导出 JSON/CSS
11
+ - 🌈 主色自动派生(-light / -dark / -bg / -border),无需手动配置
12
+ - 🔧 丰富的组件库,覆盖常用场景(Button / Input / Dialog / Card / Icon / Tabs / Empty / Toast / ContextMenu 等 20+ 组件)
13
+ - 💎 完整的 CSS 变量(Design Tokens)系统,便于深度定制
14
+ - 💾 主题配置自动持久化到 localStorage,刷新页面不丢失
15
+
16
+ ---
17
+
18
+ ## 快速开始
19
+
20
+ ### 安装
21
+
22
+ ```bash
23
+ # npm
24
+ npm install @cghbcf/xp-ui --save
25
+
26
+ # yarn
27
+ yarn add @cghbcf/xp-ui
28
+
29
+ # pnpm
30
+ pnpm install @cghbcf/xp-ui
31
+ ```
32
+
33
+ ### 完整引入
34
+
35
+ ```js
36
+ import { createApp } from 'vue'
37
+ import App from './App.vue'
38
+ import XpUi from '@cghbcf/xp-ui'
39
+ import '@cghbcf/xp-ui/dist/style.css'
40
+
41
+ const app = createApp(App)
42
+ app.use(XpUi)
43
+ app.mount('#app')
44
+ ```
45
+
46
+ ### 按需引入
47
+
48
+ ```js
49
+ import { createApp } from 'vue'
50
+ import App from './App.vue'
51
+ import { XpButton, XpInput, XpDialog, XpCard, XpTabs } from '@cghbcf/xp-ui'
52
+ import '@cghbcf/xp-ui/dist/style.css'
53
+
54
+ const app = createApp(App)
55
+ app.component('XpButton', XpButton)
56
+ app.component('XpInput', XpInput)
57
+ app.mount('#app')
58
+ ```
59
+
60
+ 也可以直接使用主题 composable:
61
+
62
+ ```js
63
+ import { useTheme, applyThemeConfig, setTheme, toggleTheme } from '@cghbcf/xp-ui'
64
+
65
+ const { theme, themeConfig, initTheme, applyThemeConfig, buildCssString } = useTheme()
66
+ initTheme()
67
+ ```
68
+
69
+ ---
70
+
71
+ ## 组件
72
+
73
+ ### Icon 图标
74
+
75
+ 基于内联 SVG 的图标组件,支持预设/自定义尺寸、颜色、旋转与加载动画。
76
+
77
+ 图标文件统一存放在 `src/components/icon/icons/` 目录下,组件在构建时通过 `import.meta.glob` 自动扫描该目录,**新增图标只需放入 `.svg` 文件**,即可用同名 `name` 直接引用(例如 `jiantou.svg` 对应 `name="jiantou"`)。
78
+
79
+ 图标内部硬编码的 `fill` / `stroke` 颜色(除 `none` 外)会被自动替换为 `currentColor`,因此 `color` 属性始终生效。当 `name` 不存在时,会展示占位符(`?`)便于排查。
80
+
81
+ #### 基础用法
82
+
83
+ ```vue
84
+ <template>
85
+ <!-- 内置图标 -->
86
+ <XpIcon name="jiantou" />
87
+
88
+ <!-- 方向:通过 rotate 或内联样式旋转 -->
89
+ <XpIcon name="jiantou" :rotate="180" />
90
+
91
+ <!-- 预设尺寸 -->
92
+ <XpIcon name="jiantou" size="small" />
93
+ <XpIcon name="jiantou" size="medium" />
94
+ <XpIcon name="jiantou" size="large" />
95
+
96
+ <!-- 自定义尺寸(数字按 px 处理) -->
97
+ <XpIcon name="jiantou" :size="32" />
98
+
99
+ <!-- 自定义颜色 -->
100
+ <XpIcon name="jiantou" color="#67c23a" />
101
+
102
+ <!-- 加载动画 -->
103
+ <XpIcon name="jiantou" spin color="var(--xp-color-primary)" />
104
+
105
+ <!-- name 不存在时显示占位符 -->
106
+ <XpIcon name="not-exist" />
107
+ </template>
108
+ ```
109
+
110
+ ##### Props
111
+
112
+ | 属性 | 类型 | 默认值 | 说明 |
113
+ |------|------|--------|------|
114
+ | name | string | '' | 图标名称,对应 `icons/` 目录下 `.svg` 文件名(不含后缀) |
115
+ | size | string / number | medium | 尺寸:预设值 `small`(14) / `medium`(18) / `large`(24),或自定义数字(px) / 带单位字符串(如 `48px`) |
116
+ | color | string | '' | 图标颜色,值为任意合法 CSS 颜色 |
117
+ | spin | boolean | false | 是否持续旋转(加载动画) |
118
+ | rotate | number | 0 | 旋转角度(单位:度) |
119
+
120
+ ##### Slots
121
+
122
+ | 插槽名 | 说明 |
123
+ |--------|------|
124
+ | default | 自定义图标内容(传入则优先渲染插槽内容,而非 SVG) |
125
+
126
+ ---
127
+
128
+ ### Button 按钮
129
+
130
+ 按钮用于触发一个操作,如提交表单、打开对话框等。
131
+
132
+ #### 基础用法
133
+
134
+ ```vue
135
+ <template>
136
+ <XpButton>Default</XpButton>
137
+ <XpButton type="primary">Primary</XpButton>
138
+ <XpButton type="success">Success</XpButton>
139
+ <XpButton type="warning">Warning</XpButton>
140
+ <XpButton type="danger">Danger</XpButton>
141
+ <XpButton type="info">Info</XpButton>
142
+
143
+ <XpButton type="primary" plain>Plain Primary</XpButton>
144
+ <XpButton type="primary" text>Text Primary</XpButton>
145
+
146
+ <XpButton size="small" type="primary">Small</XpButton>
147
+ <XpButton size="medium" type="primary">Medium</XpButton>
148
+ <XpButton size="large" type="primary">Large</XpButton>
149
+
150
+ <XpButton type="primary" disabled>Disabled</XpButton>
151
+ <XpButton type="primary" loading @click="handleClick">Loading</XpButton>
152
+ </template>
153
+
154
+ <script setup>
155
+ const handleClick = () => {}
156
+ </script>
157
+ ```
158
+
159
+ ##### Props
160
+
161
+ | 属性 | 类型 | 默认值 | 说明 |
162
+ |------|------|--------|------|
163
+ | type | string | default | 按钮类型,可选值:primary / default / success / warning / danger / info / ghost |
164
+ | size | string | medium | 按钮尺寸,可选值:small / medium / large |
165
+ | plain | boolean | false | 是否朴素按钮 |
166
+ | text | boolean | false | 是否文字按钮 |
167
+ | disabled | boolean | false | 是否禁用按钮 |
168
+ | loading | boolean | false | 是否显示加载状态 |
169
+
170
+ ##### Events
171
+
172
+ | 事件名 | 说明 | 回调参数 |
173
+ |--------|------|----------|
174
+ | click | 点击按钮时触发 | event |
175
+
176
+ ---
177
+
178
+ ### Input 输入框
179
+
180
+ 用于收集用户输入信息。
181
+
182
+ #### 基础用法
183
+
184
+ ```vue
185
+ <template>
186
+ <XpInput v-model="inputValue" placeholder="请输入内容" showClear />
187
+
188
+ <XpInput placeholder="Small" size="small" />
189
+ <XpInput placeholder="Medium" size="medium" />
190
+ <XpInput placeholder="Large" size="large" />
191
+
192
+ <XpInput placeholder="用户名">
193
+ <template #prefix>👤</template>
194
+ </XpInput>
195
+
196
+ <XpInput placeholder="密码">
197
+ <template #suffix>🔒</template>
198
+ </XpInput>
199
+
200
+ <XpInput placeholder="禁用状态" disabled />
201
+ </template>
202
+
203
+ <script setup>
204
+ import { ref } from 'vue'
205
+ const inputValue = ref('')
206
+ </script>
207
+ ```
208
+
209
+ ##### Props
210
+
211
+ | 属性 | 类型 | 默认值 | 说明 |
212
+ |------|------|--------|------|
213
+ | modelValue | string / number | '' | 输入框的值,支持 v-model |
214
+ | id | string | '' | 原生 input 的 id,配合 label[for] 使用 |
215
+ | type | string | text | 输入框类型,可选值:text / password / number / email / tel / url |
216
+ | placeholder | string | '' | 占位符文本 |
217
+ | disabled | boolean | false | 是否禁用 |
218
+ | size | string | medium | 输入框尺寸,可选值:small / medium / large |
219
+ | maxlength | string / number | null | 最大输入长度 |
220
+ | showClear | boolean | false | 是否显示清空按钮 |
221
+
222
+ ##### Events
223
+
224
+ | 事件名 | 说明 | 回调参数 |
225
+ |--------|------|----------|
226
+ | update:modelValue | 值改变时触发 | value |
227
+ | focus | 获得焦点时触发 | event |
228
+ | blur | 失去焦点时触发 | event |
229
+ | clear | 点击清空按钮时触发 | - |
230
+
231
+ ##### Slots
232
+
233
+ | 插槽名 | 说明 |
234
+ |--------|------|
235
+ | prefix | 前缀内容 |
236
+ | suffix | 后缀内容 |
237
+
238
+ ##### 实例方法
239
+
240
+ 通过模板 ref 可调用内部原生 input 的方法(如自动聚焦 / 全选):
241
+
242
+ ```vue
243
+ <template>
244
+ <XpInput ref="inputRef" v-model="value" placeholder="请输入" />
245
+ </template>
246
+
247
+ <script setup>
248
+ import { ref, nextTick } from 'vue'
249
+ const inputRef = ref(null)
250
+ nextTick(() => {
251
+ inputRef.value.focus()
252
+ inputRef.value.select()
253
+ })
254
+ </script>
255
+ ```
256
+
257
+ | 方法 | 说明 |
258
+ |------|------|
259
+ | focus | 聚焦输入框 |
260
+ | select | 全选输入框内容 |
261
+ | blur | 失焦 |
262
+
263
+ ---
264
+
265
+ ### Switch 开关
266
+
267
+ 表示两种相互对立的状态间的切换。
268
+
269
+ #### 基础用法
270
+
271
+ ```vue
272
+ <template>
273
+ <XpSwitch v-model="value" />
274
+ <XpSwitch v-model="value" size="small" />
275
+ <XpSwitch v-model="value" size="large" />
276
+ <XpSwitch :model-value="true" disabled />
277
+ </template>
278
+
279
+ <script setup>
280
+ import { ref } from 'vue'
281
+ const value = ref(false)
282
+ </script>
283
+ ```
284
+
285
+ ##### Props
286
+
287
+ | 属性 | 类型 | 默认值 | 说明 |
288
+ |------|------|--------|------|
289
+ | modelValue | boolean | false | 绑定值,支持 v-model |
290
+ | size | string | medium | 尺寸:small / medium / large |
291
+ | disabled | boolean | false | 是否禁用 |
292
+
293
+ ##### Events
294
+
295
+ | 事件名 | 说明 | 回调参数 |
296
+ |--------|------|----------|
297
+ | update:modelValue | 值改变时触发 | value |
298
+ | change | 值改变时触发 | value |
299
+
300
+ ---
301
+
302
+ ### Checkbox 多选框
303
+
304
+ 一组备选项中进行多选。
305
+
306
+ #### 基础用法
307
+
308
+ ```vue
309
+ <template>
310
+ <XpCheckbox v-model="value1">同意协议</XpCheckbox>
311
+ <XpCheckbox v-model="value2">接收邮件</XpCheckbox>
312
+ <XpCheckbox :model-value="true" disabled>已勾选(禁用)</XpCheckbox>
313
+ <XpCheckbox :model-value="false" disabled>未勾选(禁用)</XpCheckbox>
314
+ </template>
315
+
316
+ <script setup>
317
+ import { ref } from 'vue'
318
+ const value1 = ref(true)
319
+ const value2 = ref(false)
320
+ </script>
321
+ ```
322
+
323
+ ##### Props
324
+
325
+ | 属性 | 类型 | 默认值 | 说明 |
326
+ |------|------|--------|------|
327
+ | modelValue | boolean | false | 绑定值,支持 v-model |
328
+ | disabled | boolean | false | 是否禁用 |
329
+ | indeterminate | boolean | false | 是否半选状态 |
330
+
331
+ ##### Events
332
+
333
+ | 事件名 | 说明 | 回调参数 |
334
+ |--------|------|----------|
335
+ | update:modelValue | 值改变时触发 | value |
336
+ | change | 值改变时触发 | value |
337
+
338
+ ---
339
+
340
+ ### Radio 单选框
341
+
342
+ 在一组备选项中进行单选。
343
+
344
+ #### 基础用法
345
+
346
+ ```vue
347
+ <template>
348
+ <XpRadio v-model="value" value="A">选项 A</XpRadio>
349
+ <XpRadio v-model="value" value="B">选项 B</XpRadio>
350
+ <XpRadio v-model="value" value="C">选项 C</XpRadio>
351
+ </template>
352
+
353
+ <script setup>
354
+ import { ref } from 'vue'
355
+ const value = ref('A')
356
+ </script>
357
+ ```
358
+
359
+ ##### Props
360
+
361
+ | 属性 | 类型 | 默认值 | 说明 |
362
+ |------|------|--------|------|
363
+ | modelValue | string / number / boolean | '' | 绑定值 |
364
+ | value | string / number / boolean | '' | 选项的值 |
365
+ | disabled | boolean | false | 是否禁用 |
366
+
367
+ ##### Events
368
+
369
+ | 事件名 | 说明 | 回调参数 |
370
+ |--------|------|----------|
371
+ | update:modelValue | 值改变时触发 | value |
372
+ | change | 值改变时触发 | value |
373
+
374
+ ---
375
+
376
+ ### Tag 标签 & Badge 徽标
377
+
378
+ Tag 用于标记和分类;Badge 用于消息数量提示。
379
+
380
+ #### 基础用法
381
+
382
+ ```vue
383
+ <template>
384
+ <XpTag>Default</XpTag>
385
+ <XpTag type="primary">Primary</XpTag>
386
+ <XpTag type="success">Success</XpTag>
387
+ <XpTag type="warning">Warning</XpTag>
388
+ <XpTag type="danger">Danger</XpTag>
389
+ <XpTag type="info">Info</XpTag>
390
+
391
+ <XpTag round type="primary">Round</XpTag>
392
+ <XpTag size="small">Small</XpTag>
393
+ <XpTag size="large">Large</XpTag>
394
+ <XpTag closable>Closable</XpTag>
395
+
396
+ <XpBadge value="5">
397
+ <XpButton size="small">消息</XpButton>
398
+ </XpBadge>
399
+ <XpBadge value="120" type="warning">
400
+ <XpButton size="small">通知</XpButton>
401
+ </XpBadge>
402
+ <XpBadge isDot type="danger">
403
+ <XpButton size="small">新消息</XpButton>
404
+ </XpBadge>
405
+ </template>
406
+ ```
407
+
408
+ ##### Tag Props
409
+
410
+ | 属性 | 类型 | 默认值 | 说明 |
411
+ |------|------|--------|------|
412
+ | type | string | default | 类型:primary / default / success / warning / danger / info |
413
+ | size | string | medium | 尺寸:small / medium / large |
414
+ | color | string | '' | 自定义颜色(#xxx 或 rgb) |
415
+ | plain | boolean | false | 是否朴素样式 |
416
+ | round | boolean | false | 是否圆角 |
417
+ | closable | boolean | false | 是否可关闭 |
418
+
419
+ ##### Badge Props
420
+
421
+ | 属性 | 类型 | 默认值 | 说明 |
422
+ |------|------|--------|------|
423
+ | value | string / number | 0 | 显示值 |
424
+ | max | number | 99 | 最大值,超出显示 {max}+ |
425
+ | type | string | danger | 类型:primary / success / warning / danger / info |
426
+ | isDot | boolean | false | 是否以小圆点显示 |
427
+ | hidden | boolean | false | 是否隐藏徽标 |
428
+
429
+ ---
430
+
431
+ ### Alert 提示
432
+
433
+ 用于页面中展示重要的提示信息。
434
+
435
+ #### 基础用法
436
+
437
+ ```vue
438
+ <template>
439
+ <XpAlert title="这是一条成功消息" type="success" />
440
+ <XpAlert title="这是一条警告消息" type="warning" />
441
+ <XpAlert title="这是一条错误消息" type="danger" />
442
+ <XpAlert title="这是一条提示消息" type="info" />
443
+ </template>
444
+ ```
445
+
446
+ ##### Props
447
+
448
+ | 属性 | 类型 | 默认值 | 说明 |
449
+ |------|------|--------|------|
450
+ | title | string | '' | 标题 |
451
+ | type | string | info | 类型:success / warning / danger / info |
452
+ | effect | string | light | 样式:light / dark / plain |
453
+ | closable | boolean | true | 是否可关闭 |
454
+ | showIcon | boolean | true | 是否显示图标 |
455
+
456
+ ##### Events
457
+
458
+ | 事件名 | 说明 | 回调参数 |
459
+ |--------|------|----------|
460
+ | close | 关闭 alert 时触发 | - |
461
+
462
+ ---
463
+
464
+ ### Avatar 头像
465
+
466
+ 用图标、图片或者字符展示用户头像。
467
+
468
+ #### 基础用法
469
+
470
+ ```vue
471
+ <template>
472
+ <XpAvatar size="small">Xp</XpAvatar>
473
+ <XpAvatar>User</XpAvatar>
474
+ <XpAvatar size="large">Demo</XpAvatar>
475
+ <XpAvatar shape="square">Sq</XpAvatar>
476
+ <XpAvatar shape="square" size="large">UI</XpAvatar>
477
+ <XpAvatar src="https://example.com/avatar.png" alt="Avatar" />
478
+ </template>
479
+ ```
480
+
481
+ ##### Props
482
+
483
+ | 属性 | 类型 | 默认值 | 说明 |
484
+ |------|------|--------|------|
485
+ | src | string | '' | 图片头像地址 |
486
+ | alt | string | '' | 替代文本 |
487
+ | shape | string | circle | 形状:circle / square |
488
+ | size | string | medium | 尺寸:small / medium / large |
489
+ | icon | string | '' | 图标 |
490
+
491
+ ##### Events
492
+
493
+ | 事件名 | 说明 | 回调参数 |
494
+ |--------|------|----------|
495
+ | error | 图片加载失败时触发 | - |
496
+
497
+ ---
498
+
499
+ ### Progress 进度条 & Rate 评分
500
+
501
+ 进度条用于展示操作进度;评分用于对事物进行评级操作。
502
+
503
+ #### 基础用法
504
+
505
+ ```vue
506
+ <template>
507
+ <XpProgress :percentage="30" style="width:240px" />
508
+ <XpProgress :percentage="60" status="warning" style="width:240px" />
509
+ <XpProgress :percentage="85" status="success" style="width:240px" />
510
+ <XpProgress :percentage="100" status="exception" style="width:240px" />
511
+
512
+ <XpRate v-model="rate" showText />
513
+ </template>
514
+
515
+ <script setup>
516
+ import { ref } from 'vue'
517
+ const rate = ref(3)
518
+ </script>
519
+ ```
520
+
521
+ ##### Progress Props
522
+
523
+ | 属性 | 类型 | 默认值 | 说明 |
524
+ |------|------|--------|------|
525
+ | percentage | number | 0 | 百分比(0-100) |
526
+ | color | string | '' | 自定义进度条颜色 |
527
+ | showText | boolean | true | 是否显示百分比文字 |
528
+ | type | string | line | 类型:line / circle |
529
+ | status | string | '' | 状态:success / warning / exception |
530
+ | strokeWidth | number | 8 | line 模式下进度条高度(px),紧凑场景可设小值 |
531
+
532
+ ##### Rate Props
533
+
534
+ | 属性 | 类型 | 默认值 | 说明 |
535
+ |------|------|--------|------|
536
+ | modelValue | number | 0 | 绑定值 |
537
+ | max | number | 5 | 最大分值 |
538
+ | color | string | #f7ba2a | 颜色 |
539
+ | disabled | boolean | false | 是否禁用 |
540
+ | showText | boolean | false | 是否显示文字 |
541
+
542
+ ---
543
+
544
+ ### Tooltip 提示
545
+
546
+ 常用于展示鼠标 hover 时的提示信息。
547
+
548
+ #### 基础用法
549
+
550
+ ```vue
551
+ <template>
552
+ <XpTooltip content="顶部提示">
553
+ <XpButton type="primary">Top</XpButton>
554
+ </XpTooltip>
555
+ <XpTooltip content="底部提示" placement="bottom">
556
+ <XpButton>Bottom</XpButton>
557
+ </XpTooltip>
558
+ <XpTooltip content="左侧提示" placement="left">
559
+ <XpButton type="success">Left</XpButton>
560
+ </XpTooltip>
561
+ <XpTooltip content="右侧提示" placement="right">
562
+ <XpButton type="warning">Right</XpButton>
563
+ </XpTooltip>
564
+ </template>
565
+ ```
566
+
567
+ ##### Props
568
+
569
+ | 属性 | 类型 | 默认值 | 说明 |
570
+ |------|------|--------|------|
571
+ | content | string | '' | 显示的内容 |
572
+ | placement | string | top | 出现位置:top / bottom / left / right |
573
+ | trigger | string | hover | 触发方式:hover / click / manual |
574
+ | disabled | boolean | false | 是否禁用 |
575
+
576
+ ---
577
+
578
+ ### Dropdown 下拉菜单
579
+
580
+ 将动作或菜单折叠到下拉菜单中。
581
+
582
+ #### 基础用法
583
+
584
+ ```vue
585
+ <template>
586
+ <XpDropdown :items="items" @select="handleSelect">
587
+ <XpButton type="primary">下拉菜单 ▾</XpButton>
588
+ </XpDropdown>
589
+ </template>
590
+
591
+ <script setup>
592
+ const items = [
593
+ { label: '选项一', command: 'a' },
594
+ { label: '选项二', command: 'b' },
595
+ { label: '选项三(禁用)', command: 'c', disabled: true },
596
+ { divider: true },
597
+ { label: '选项四', command: 'd' }
598
+ ]
599
+ const handleSelect = (item) => {
600
+ console.log('选中:', item)
601
+ }
602
+ </script>
603
+ ```
604
+
605
+ ##### Props
606
+
607
+ | 属性 | 类型 | 默认值 | 说明 |
608
+ |------|------|--------|------|
609
+ | items | Array | [] | 菜单项:{ label, command, disabled, divider } |
610
+ | disabled | boolean | false | 是否禁用 |
611
+
612
+ ##### Events
613
+
614
+ | 事件名 | 说明 | 回调参数 |
615
+ |--------|------|----------|
616
+ | select | 点击菜单项时触发 | item |
617
+ | command | 点击菜单项时触发(传 command) | command, item |
618
+ | open | 菜单打开时触发 | - |
619
+ | close | 菜单关闭时触发 | - |
620
+
621
+ ---
622
+
623
+ ### Card 卡片
624
+
625
+ 将信息聚合在卡片容器中展示。
626
+
627
+ #### 基础用法
628
+
629
+ ```vue
630
+ <template>
631
+ <XpCard title="卡片标题">
632
+ <p>这是卡片内容</p>
633
+ </XpCard>
634
+
635
+ <XpCard title="卡片标题">
636
+ <p>卡片内容区域</p>
637
+ <template #footer>
638
+ <div style="display:flex;justify-content:flex-end;gap:8px">
639
+ <XpButton size="small">取消</XpButton>
640
+ <XpButton size="small" type="primary">确定</XpButton>
641
+ </div>
642
+ </template>
643
+ </XpCard>
644
+
645
+ <XpCard title="卡片标题" shadow="hover">
646
+ <p>悬停出现阴影</p>
647
+ </XpCard>
648
+ </template>
649
+ ```
650
+
651
+ ##### Props
652
+
653
+ | 属性 | 类型 | 默认值 | 说明 |
654
+ |------|------|--------|------|
655
+ | title | string | '' | 卡片标题 |
656
+ | shadow | string | always | 阴影效果:always / hover / never |
657
+ | bordered | boolean | true | 是否显示边框 |
658
+ | hover | boolean | false | 是否 hover 时上移 |
659
+
660
+ ##### Slots
661
+
662
+ | 插槽名 | 说明 |
663
+ |--------|------|
664
+ | header | 自定义头部内容 |
665
+ | default | 卡片主体内容 |
666
+ | footer | 底部内容 |
667
+ | extra | 头部右侧内容 |
668
+
669
+ ---
670
+
671
+ ### Tabs 标签页
672
+
673
+ 分隔内容上有关联但属于不同类别的数据集合。
674
+
675
+ #### 基础用法
676
+
677
+ ```vue
678
+ <template>
679
+ <XpTabs v-model="active">
680
+ <XpTabPane name="tab1" title="选项一">
681
+ <p>这是选项一的内容。</p>
682
+ </XpTabPane>
683
+ <XpTabPane name="tab2" title="选项二">
684
+ <p>这是选项二的内容。</p>
685
+ </XpTabPane>
686
+ <XpTabPane name="tab3" title="选项三">
687
+ <p>这是选项三的内容。</p>
688
+ </XpTabPane>
689
+ </XpTabs>
690
+ </template>
691
+
692
+ <script setup>
693
+ import { ref } from 'vue'
694
+ const active = ref('tab1')
695
+ </script>
696
+ ```
697
+
698
+ ##### Tabs Props
699
+
700
+ | 属性 | 类型 | 默认值 | 说明 |
701
+ |------|------|--------|------|
702
+ | modelValue | string / number | null | 当前激活的 name,支持 v-model |
703
+
704
+ ##### TabPane Props
705
+
706
+ | 属性 | 类型 | 默认值 | 说明 |
707
+ |------|------|--------|------|
708
+ | name | string / number | - | 必填,唯一标识 |
709
+ | title | string | '' | 标签标题 |
710
+ | disabled | boolean | false | 是否禁用 |
711
+
712
+ ##### Tabs Events
713
+
714
+ | 事件名 | 说明 | 回调参数 |
715
+ |--------|------|----------|
716
+ | update:modelValue | 切换时触发 | name |
717
+ | change | 切换时触发 | name |
718
+
719
+ ---
720
+
721
+ ### Dialog 对话框
722
+
723
+ 在保留当前页面状态的情况下,告知用户并承载相关操作。
724
+
725
+ #### 基础用法
726
+
727
+ ```vue
728
+ <template>
729
+ <XpButton type="primary" @click="visible = true">打开对话框</XpButton>
730
+ <XpDialog
731
+ v-model:visible="visible"
732
+ title="提示"
733
+ @confirm="handleConfirm"
734
+ @cancel="handleCancel"
735
+ >
736
+ <p>这是一段弹窗内容</p>
737
+ </XpDialog>
738
+ </template>
739
+
740
+ <script setup>
741
+ import { ref } from 'vue'
742
+ const visible = ref(false)
743
+ const handleConfirm = () => { visible.value = false }
744
+ const handleCancel = () => { visible.value = false }
745
+ </script>
746
+ ```
747
+
748
+ #### 自定义宽度
749
+
750
+ ```vue
751
+ <template>
752
+ <XpDialog
753
+ v-model:visible="visible"
754
+ title="宽弹窗"
755
+ width="700px"
756
+ >
757
+ <p>弹窗内容</p>
758
+ </XpDialog>
759
+ </template>
760
+ ```
761
+
762
+ #### 自定义按钮文字
763
+
764
+ ```vue
765
+ <template>
766
+ <XpDialog
767
+ v-model:visible="visible"
768
+ title="确认删除"
769
+ confirm-button-text="删除"
770
+ cancel-button-text="我再想想"
771
+ >
772
+ <p>确定要删除这条记录吗?</p>
773
+ </XpDialog>
774
+ </template>
775
+ ```
776
+
777
+ ##### Props
778
+
779
+ | 属性 | 类型 | 默认值 | 说明 |
780
+ |------|------|--------|------|
781
+ | visible | boolean | false | 是否显示,支持 v-model |
782
+ | title | string | '' | 弹窗标题 |
783
+ | width | string | 500px | 弹窗宽度 |
784
+ | confirmButtonText | string | 确定 | 确认按钮文字 |
785
+ | cancelButtonText | string | 取消 | 取消按钮文字 |
786
+ | closeOnClickModal | boolean | true | 点击遮罩层是否关闭弹窗 |
787
+ | showDefaultFooter | boolean | true | 是否显示默认底部按钮 |
788
+
789
+ ##### Events
790
+
791
+ | 事件名 | 说明 | 回调参数 |
792
+ |--------|------|----------|
793
+ | confirm | 点击确认按钮时触发 | - |
794
+ | cancel | 点击取消按钮时触发 | - |
795
+ | close | 关闭弹窗时触发 | - |
796
+ | update:visible | 可见性改变时触发 | visible |
797
+
798
+ ##### Slots
799
+
800
+ | 插槽名 | 说明 |
801
+ |--------|------|
802
+ | default | 弹窗主体内容 |
803
+ | footer | 自定义底部按钮区域 |
804
+
805
+ > 按 Esc 键同样可以关闭弹窗。
806
+
807
+ ---
808
+
809
+ ### Empty 空状态
810
+
811
+ 用于列表、目录等数据为空时的占位展示。
812
+
813
+ #### 基础用法
814
+
815
+ ```vue
816
+ <template>
817
+ <XpEmpty title="目录为空" hint="拖入文件或点击工具栏上传" />
818
+ </template>
819
+ ```
820
+
821
+ #### 自定义图标与操作
822
+
823
+ ```vue
824
+ <template>
825
+ <XpEmpty title="连接失败" hint="无法连接到服务器,请检查网络后重试">
826
+ <template #icon>
827
+ <div class="my-icon" />
828
+ </template>
829
+ <XpButton type="primary" size="small">重新加载</XpButton>
830
+ </XpEmpty>
831
+ </template>
832
+ ```
833
+
834
+ ##### Props
835
+
836
+ | 属性 | 类型 | 默认值 | 说明 |
837
+ |------|------|--------|------|
838
+ | title | string | 暂无内容 | 主文案 |
839
+ | hint | string | '' | 辅助说明文案 |
840
+
841
+ ##### Slots
842
+
843
+ | 插槽名 | 说明 |
844
+ |--------|------|
845
+ | icon | 自定义图标(默认是一个文件占位图形) |
846
+ | default | 底部操作区(如重试按钮) |
847
+
848
+ ---
849
+
850
+ ### FileIcon 文件图标
851
+
852
+ 按文件类型展示对应的矢量图标,内置文件夹与常见文件类型,适用于文件管理器等场景。
853
+
854
+ #### 基础用法
855
+
856
+ ```vue
857
+ <template>
858
+ <XpFileIcon type="folder" :size="34" />
859
+ <XpFileIcon type="pdf" :size="34" />
860
+ <XpFileIcon type="code" :size="20" />
861
+ </template>
862
+ ```
863
+
864
+ ##### Props
865
+
866
+ | 属性 | 类型 | 默认值 | 说明 |
867
+ |------|------|--------|------|
868
+ | type | string | file | 类型:folder / image / video / audio / pdf / doc / sheet / slide / archive / code / text / file |
869
+ | size | number | 20 | 图标边长(px) |
870
+
871
+ ---
872
+
873
+ ### ContextMenu 右键菜单
874
+
875
+ 在指定屏幕坐标位置弹出的菜单,常用于右键 / 长按等交互场景。
876
+
877
+ #### 基础用法
878
+
879
+ ```vue
880
+ <template>
881
+ <div @contextmenu.prevent="openMenu($event, items)">右键点击此区域</div>
882
+ <XpContextMenu :visible="menu.visible" :x="menu.x" :y="menu.y" :items="menu.items" @close="menu.visible = false" />
883
+ </template>
884
+
885
+ <script setup>
886
+ import { reactive } from 'vue'
887
+ const menu = reactive({ visible: false, x: 0, y: 0, items: [] })
888
+ const openMenu = (e, items) => {
889
+ menu.x = e.clientX
890
+ menu.y = e.clientY
891
+ menu.items = items
892
+ menu.visible = true
893
+ }
894
+ const items = [
895
+ { label: '打开', command: 'open' },
896
+ { label: '禁用项', disabled: true },
897
+ { divider: true },
898
+ { label: '删除', command: 'delete', danger: true }
899
+ ]
900
+ </script>
901
+ ```
902
+
903
+ > 菜单会自动进行视口边界校正;点击菜单项、遮罩或按 Esc 均会关闭菜单。
904
+
905
+ ##### Props
906
+
907
+ | 属性 | 类型 | 默认值 | 说明 |
908
+ |------|------|--------|------|
909
+ | visible | boolean | false | 是否显示 |
910
+ | x | number | 0 | 菜单左上角 x 坐标(px) |
911
+ | y | number | 0 | 菜单左上角 y 坐标(px) |
912
+ | items | Array | [] | 菜单项:{ label, icon?, command?, danger?, disabled?, divider?, onClick? } |
913
+
914
+ ##### Events
915
+
916
+ | 事件名 | 说明 | 回调参数 |
917
+ |--------|------|----------|
918
+ | select | 点击菜单项时触发 | item |
919
+ | command | 点击菜单项时触发(传 command) | command, item |
920
+ | close | 菜单关闭时触发 | - |
921
+
922
+ > 兼容用法:菜单项里直接传 `onClick` 函数也会在点击时被调用。
923
+
924
+ ---
925
+
926
+ ### Toast 轻提示
927
+
928
+ 轻量级的全局消息提示,需要配合 `useToast()` 组合式函数使用。
929
+
930
+ #### 基础用法
931
+
932
+ ```vue
933
+ <template>
934
+ <!-- 在应用根节点挂载一次即可 -->
935
+ <XpToast />
936
+ </template>
937
+
938
+ <script setup>
939
+ import { useToast } from '@cghbcf/xp-ui'
940
+ const { toast, success, error, warn, info } = useToast()
941
+
942
+ success('操作成功')
943
+ error('操作失败')
944
+ warn('请注意该操作的后果')
945
+ info('这是一条普通提示')
946
+ </script>
947
+ ```
948
+
949
+ ##### useToast API
950
+
951
+ | 方法 | 说明 | 参数 |
952
+ |------|------|------|
953
+ | toast | 触发提示,可自定义类型与时长 | (message, type?, duration?) |
954
+ | success | 成功提示(2200ms) | message |
955
+ | error | 错误提示(3600ms) | message |
956
+ | warn | 警告提示(2800ms) | message |
957
+ | info | 信息提示(2600ms) | message |
958
+ | toasts | 响应式提示列表(ref) | - |
959
+
960
+ toast 的 `type` 可选值:`info` / `success` / `error` / `warn`。
961
+
962
+ ---
963
+
964
+ ## 主题定制
965
+
966
+ Xp UI 内置完整的 **浅色 / 暗黑双主题** 系统,并支持 **可视化颜色定制**、**配置导入导出**,可在任意项目中按需使用。
967
+
968
+ ### 主题色(Design Tokens)
969
+
970
+ 组件库的所有颜色都通过 **CSS 变量** 控制,你可以在浏览器开发者工具中实时查看和调试:
971
+
972
+ | 变量名 | 说明 |
973
+ |--------|------|
974
+ | `--xp-color-primary` | 主色(品牌色) |
975
+ | `--xp-color-success` | 成功色 |
976
+ | `--xp-color-warning` | 警告色 |
977
+ | `--xp-color-danger` | 危险色 |
978
+ | `--xp-color-info` | 信息色 |
979
+ | `--xp-text-color-primary` | 主要文字色 |
980
+ | `--xp-text-color-regular` | 常规文字色 |
981
+ | `--xp-border-color` | 边框色 |
982
+ | `--xp-bg-color` | 背景色 |
983
+ | `--xp-fill-color` | 填充色 |
984
+ | `--xp-shadow-sm` / `--xp-shadow-md` / `--xp-shadow-lg` | 阴影 |
985
+
986
+ > 每个主题色还自动派生出 `-light`(30% 浅色)、`-dark`(20% 深色)、`-bg`(背景色)、`-border`(边框色),无需手动配置。
987
+
988
+ ### 浅色 / 暗黑模式切换
989
+
990
+ 在入口文件中调用 `initTheme()` 即可自动初始化主题(读取 localStorage 或跟随系统偏好):
991
+
992
+ ```js
993
+ import { useTheme } from '@cghbcf/xp-ui'
994
+
995
+ const { initTheme, toggleTheme, setTheme, theme } = useTheme()
996
+
997
+ // 1. 页面加载时初始化主题(读取 localStorage)
998
+ initTheme()
999
+
1000
+ // 2. 切换主题按钮
1001
+ const handleToggle = () => toggleTheme()
1002
+
1003
+ // 3. 强制切换到指定主题
1004
+ setTheme('dark') // 或 'light'
1005
+
1006
+ // 4. 响应式读取当前主题
1007
+ console.log(theme.value) // 'light' | 'dark'
1008
+ ```
1009
+
1010
+ Vue 组件中的示例用法:
1011
+
1012
+ ```vue
1013
+ <template>
1014
+ <button @click="toggleTheme">
1015
+ 当前主题:{{ theme === 'dark' ? '🌙 暗黑' : '☀️ 浅色' }}
1016
+ </button>
1017
+ </template>
1018
+
1019
+ <script setup>
1020
+ import { useTheme } from '@cghbcf/xp-ui'
1021
+ const { theme, toggleTheme, initTheme } = useTheme()
1022
+ initTheme() // 在页面挂载时调用
1023
+ </script>
1024
+ ```
1025
+
1026
+ ### 自定义主题色(applyThemeConfig)
1027
+
1028
+ 通过 `applyThemeConfig` 可以一次性替换所有颜色变量,支持:
1029
+ - 修改主色系(primary / success / warning / danger / info)
1030
+ - 分别定制浅色和暗黑两套文本、边框、背景、填充、阴影
1031
+
1032
+ ```js
1033
+ import { applyThemeConfig } from '@cghbcf/xp-ui'
1034
+
1035
+ // 修改主色为绿色 —— 只需一个颜色值,其他衍生色自动生成
1036
+ applyThemeConfig({
1037
+ primary: {
1038
+ primary: '#22c55e', // 绿色主色
1039
+ success: '#67c23a',
1040
+ warning: '#e6a23c',
1041
+ danger: '#f56c6c',
1042
+ info: '#909399'
1043
+ }
1044
+ })
1045
+
1046
+ // 完整配置:浅色 + 暗黑各一套
1047
+ applyThemeConfig({
1048
+ primary: {
1049
+ primary: '#22c55e',
1050
+ success: '#67c23a',
1051
+ warning: '#e6a23c',
1052
+ danger: '#f56c6c',
1053
+ info: '#909399'
1054
+ },
1055
+ light: {
1056
+ text: { primary: '#303133', regular: '#606266', secondary: '#909399', placeholder: '#c0c4cc' },
1057
+ border: { base: '#dcdfe6', light: '#ebeef5', lighter: '#f2f6fc' },
1058
+ bg: { base: '#ffffff', page: '#f2f3f5', soft: '#f5f7fa', overlay: 'rgba(0, 0, 0, 0.5)' },
1059
+ fill: { base: '#f0f2f5', light: '#f5f7fa', lighter: '#fafafa' },
1060
+ shadow: { sm: '0 1px 2px 0 rgba(0, 0, 0, 0.05)', md: '0 2px 12px 0 rgba(0, 0, 0, 0.1)', lg: '0 4px 20px 0 rgba(0, 0, 0, 0.15)', overlay: '0 8px 24px 0 rgba(0, 0, 0, 0.2)' }
1061
+ },
1062
+ dark: {
1063
+ text: { primary: '#e5eaf3', regular: '#cfd3dc', secondary: '#a3a6ad', placeholder: '#8d9095' },
1064
+ border: { base: '#4c4d4f', light: '#414243', lighter: '#363637' },
1065
+ bg: { base: '#1d1e1f', page: '#141414', soft: '#2a2b2c', overlay: 'rgba(0, 0, 0, 0.7)' },
1066
+ fill: { base: '#303030', light: '#262727', lighter: '#1d1d1d' },
1067
+ shadow: { sm: '0 1px 2px 0 rgba(0, 0, 0, 0.4)', md: '0 2px 12px 0 rgba(0, 0, 0, 0.45)', lg: '0 4px 20px 0 rgba(0, 0, 0, 0.5)', overlay: '0 8px 24px 0 rgba(0, 0, 0, 0.6)' }
1068
+ }
1069
+ })
1070
+ ```
1071
+
1072
+ > 注意:`applyThemeConfig` 会自动做**深合并**——你只需传入要修改的部分,未传的部分保持默认。
1073
+
1074
+ ### 导出主题配置(buildCssString)
1075
+
1076
+ 可以将当前的主题配置导出为完整的 CSS 文件,放到其他项目(非 Vue 项目也可使用):
1077
+
1078
+ ```js
1079
+ import { buildCssString, applyThemeConfig, getDefaultThemeConfig } from '@cghbcf/xp-ui'
1080
+
1081
+ // 1. 先应用你的自定义主题
1082
+ applyThemeConfig({ primary: { primary: '#22c55e' } })
1083
+
1084
+ // 2. 生成 CSS(包含 :root 和 [data-theme="dark"] 两套规则)
1085
+ const css = buildCssString({
1086
+ primary: { primary: '#22c55e' },
1087
+ light: { text: { primary: '#303133' } },
1088
+ dark: { text: { primary: '#e5eaf3' } }
1089
+ })
1090
+
1091
+ // 3. 写入文件(Node / Vite 构建时使用)
1092
+ // fs.writeFileSync('dist/xp-ui-theme.css', css)
1093
+ console.log(css)
1094
+ ```
1095
+
1096
+ 也可以直接读取 `themeConfig` 响应式对象:
1097
+
1098
+ ```js
1099
+ const { themeConfig } = useTheme()
1100
+ console.log(JSON.stringify(themeConfig.value, null, 2))
1101
+ // 输出完整的主题配置,供导入/导出
1102
+ ```
1103
+
1104
+ ### 导出 JSON 配置文件
1105
+
1106
+ 导出 JSON 后可以保存到项目配置文件中,下次启动时重新导入:
1107
+
1108
+ ```js
1109
+ import { useTheme } from '@cghbcf/xp-ui'
1110
+
1111
+ const { themeConfig, applyThemeConfig, resetThemeConfig } = useTheme()
1112
+
1113
+ // 导出
1114
+ const json = JSON.stringify(themeConfig.value, null, 2)
1115
+ // 可以保存到 localStorage 或上传到服务器
1116
+
1117
+ // 导入(从文件 / localStorage / API 读取)
1118
+ applyThemeConfig(JSON.parse(json))
1119
+
1120
+ // 恢复默认
1121
+ resetThemeConfig()
1122
+ ```
1123
+
1124
+ ### 主题 API 汇总
1125
+
1126
+ ```js
1127
+ import { useTheme } from '@cghbcf/xp-ui'
1128
+
1129
+ const {
1130
+ theme, // 响应式 ref: 当前主题 ('light' | 'dark')
1131
+ themeConfig, // 响应式 ref: 完整主题配置对象
1132
+ setTheme, // 设置主题: setTheme('dark' | 'light')
1133
+ toggleTheme, // 在 light/dark 之间切换
1134
+ initTheme, // 初始化:读取 localStorage + 跟随系统偏好
1135
+ applyThemeConfig, // 应用自定义主题配置(CSS 变量会立即更新)
1136
+ resetThemeConfig, // 恢复默认配置
1137
+ getDefaultThemeConfig, // 获取默认配置对象
1138
+ buildCssString, // 根据配置生成 CSS 字符串
1139
+ buildCssVars // 根据配置生成 { '--xp-color-xxx': value } 映射
1140
+ } = useTheme()
1141
+ ```
1142
+
1143
+ ### 主题持久化
1144
+
1145
+ `initTheme()` 和 `applyThemeConfig()` 都会自动将设置写入 `localStorage`(键名 `xp-ui-theme` 和 `xp-ui-theme-config`),下次访问时自动恢复,无需额外处理。
1146
+
1147
+ ---
1148
+
1149
+ ## 浏览器支持
1150
+
1151
+ 支持所有现代浏览器:
1152
+
1153
+ - Chrome(推荐)
1154
+ - Firefox
1155
+ - Safari
1156
+ - Edge
1157
+
1158
+ ---
1159
+
1160
+ ## License
1161
+
1162
+ MIT