@vue-ui-kit/ant 2.0.4 → 2.0.6

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 CHANGED
@@ -1,1386 +1,1393 @@
1
- <div align="center">
2
- English | [简体中文](./README.zh.md)
3
- </div>
4
-
5
- # Vue UI Kit Documentation
6
-
7
- A Vue 3 UI component library based on Ant Design Vue, providing enhanced form, grid, and utility components.
8
-
9
- ## Installation
10
-
11
- ```bash
12
- npm install @vue-ui-kit/ant
13
- # or
14
- yarn add @vue-ui-kit/ant
15
- # or
16
- pnpm add @vue-ui-kit/ant
17
- ```
18
-
19
- ## Quick Start
20
-
21
- ### 1. Basic Usage
22
-
23
- ```ts
24
- /*main.ts*/
25
- import { createApp } from 'vue';
26
- import App from './App.vue';
27
- import Antd from 'ant-design-vue';
28
- import UIKit from '@vue-ui-kit/ant';
29
-
30
- // Import styles - choose one of the following methods:
31
- // Method 1: Import SCSS source files (recommended, allows variable override)
32
- import '@vue-ui-kit/ant/style.scss';
33
-
34
- // Method 2: Import compiled CSS file
35
- // import '@vue-ui-kit/ant/style.css';
36
-
37
- createApp(App).use(Antd).use(UIKit).mount('#app');
38
- ```
39
-
40
- ### 2. Configure Global Defaults
41
-
42
- ```ts
43
- /*main.ts*/
44
- import { createApp } from 'vue';
45
- import App from './App.vue';
46
- import Antd from 'ant-design-vue';
47
- import UIKit, { setup } from '@vue-ui-kit/ant';
48
- import '@vue-ui-kit/ant/style.scss';
49
-
50
- // Configure global defaults
51
- setup({
52
- form: {
53
- labelCol: { span: 8 }, // Modify form label column width
54
- wrapperCol: { span: 14 }, // Modify form input column width
55
- },
56
- grid: {
57
- align: 'center', // Set default table alignment
58
- lazyReset: true, // Don't auto-submit after reset
59
- fitHeight: 200, // Set adaptive height
60
- },
61
- });
62
-
63
- createApp(App).use(Antd).use(UIKit).mount('#app');
64
- ```
65
-
66
- ### 3. Alternative import methods (if main method doesn't work)
67
-
68
- If you encounter issues with the standard import, try these alternatives:
69
-
70
- ```scss
71
- // In your main.scss file
72
- @import '@vue-ui-kit/ant/dist/style.scss';
73
- ```
74
-
75
- ```ts
76
- // Or using require in JavaScript/TypeScript
77
- require('@vue-ui-kit/ant/style.css');
78
- ```
79
-
80
- ### 4. For Vite users
81
-
82
- If using Vite, make sure your vite.config.js includes sass support:
83
-
84
- ```js
85
- // vite.config.js
86
- export default {
87
- css: {
88
- preprocessorOptions: {
89
- scss: {
90
- additionalData: `@import '@vue-ui-kit/ant/dist/style.scss';`,
91
- },
92
- },
93
- },
94
- };
95
- ```
96
-
97
- ## 🎯 样式文件导入 - 故障排除指南
98
-
99
- 如果遇到样式文件导入问题,请按顺序尝试以下方法:
100
-
101
- ### 方法1: 标准导入(推荐)
102
-
103
- ```typescript
104
- import '@vue-ui-kit/ant/style.scss';
105
- // 或
106
- import '@vue-ui-kit/ant/style.css';
107
- ```
108
-
109
- ### 方法2: 完整路径导入
110
-
111
- ```typescript
112
- import '@vue-ui-kit/ant/dist/style.scss';
113
- // 或
114
- import '@vue-ui-kit/ant/dist/style.css';
115
- ```
116
-
117
- ### 方法3: SCSS @use 语法
118
-
119
- ```scss
120
- @use '@vue-ui-kit/ant/style.scss';
121
- // 或者使用完整路径
122
- @use '@vue-ui-kit/ant/dist/style.scss';
123
- ```
124
-
125
- ### 方法4: 在 style 标签中
126
-
127
- ```vue
128
- <style lang="scss">
129
- @import '@vue-ui-kit/ant/style.scss';
130
- </style>
131
- ```
132
-
133
- ### 方法5: Vite 配置导入
134
-
135
- ```javascript
136
- // vite.config.js
137
- export default defineConfig({
138
- css: {
139
- preprocessorOptions: {
140
- scss: {
141
- additionalData: `@import '@vue-ui-kit/ant/dist/style.scss';`,
142
- },
143
- },
144
- },
145
- });
146
- ```
147
-
148
- ### 方法6: Webpack 配置
149
-
150
- ```javascript
151
- // webpack.config.js 或 vue.config.js
152
- module.exports = {
153
- css: {
154
- loaderOptions: {
155
- sass: {
156
- additionalData: `@import '@vue-ui-kit/ant/dist/style.scss';`,
157
- },
158
- },
159
- },
160
- };
161
- ```
162
-
163
- ### 🔧 调试步骤
164
-
165
- 1. **检查包版本**: 确保使用 `@vue-ui-kit/ant@1.8.4` 或更高版本
166
- 2. **验证文件存在**: 检查 `node_modules/@vue-ui-kit/ant/dist/` 目录下是否有 `style.scss` 和 `style.css`
167
- 3. **检查构建工具**: 确保您的构建工具支持处理 `.scss` 文件
168
- 4. **清除缓存**: 删除 `node_modules` 和 lock 文件后重新安装
169
-
170
- ### 📦 包内容确认
171
-
172
- 安装后可以在以下位置找到样式文件:
173
-
174
- ```
175
- node_modules/@vue-ui-kit/ant/
176
- ├── dist/
177
- │ ├── style.css # 编译后的CSS
178
- │ └── style.scss # SCSS源文件(已内联variables)
179
- └── src/packages/styles/ # 源文件(开发时参考)
180
- ```
181
-
182
- ```tsx
183
- /*kit.tsx*/
184
- import UIKit from '@vue-ui-kit/ant';
185
- import { TypographyParagraph, Switch } from 'ant-design-vue';
186
-
187
- export const setupKit = () => {
188
- /*Register $paragraph as cell renderer*/
189
- UIKit.addRender('$paragraph', {
190
- renderDefault({ props = {} }: RenderOptions, { row, field }: RenderTableParams) {
191
- const content = props.getContent?.({ row, field }) ?? (valued(field) ? row[field!] : '');
192
- return valued(field) ? (
193
- <TypographyParagraph
194
- {...merge({}, defaultProps.$paragraph, omit(props, ['content', 'getContent']))}
195
- content={content}
196
- />
197
- ) : null;
198
- },
199
- });
200
- /*Register $switch as form renderer*/
201
- UIKit.addRender('$switch', {
202
- renderItemContent(
203
- { props = {}, events = {} }: RenderOptions,
204
- { data, field }: RenderFormParams,
205
- ) {
206
- return valued(field) ? (
207
- <Switch
208
- v-model:checked={data[field!]}
209
- {...props}
210
- onChange={(...arg) => {
211
- events.change?.({ data, field }, ...arg);
212
- }}
213
- />
214
- ) : null;
215
- },
216
- });
217
- };
218
-
219
- /*
220
- * Built-in renderers:
221
- *
222
- $input: Input,
223
- AInput: Input,
224
- $textarea: Textarea,
225
- Textarea: Textarea,
226
- $number: InputNumber,
227
- AInputNumber: InputNumber,
228
- $select: Select,
229
- ASelect: Select,
230
- $date: DatePicker,
231
- ADatePicker: DatePicker,
232
- $range: RangePicker,
233
- ARangePicker: RangePicker,
234
- AAutoComplete: AutoComplete,
235
- $Cascader: Cascader,
236
- ACascader: Cascader,
237
- ACheckbox: Checkbox,
238
- AMentions: Mentions,
239
- ARate: Rate,
240
- ASlider: Slider,
241
- $time: TimePicker,
242
- ATimePicker: TimePicker,
243
- ATreeSelect: TreeSelect,
244
- *
245
- * */
246
- ```
247
-
248
- ## Requirements
249
-
250
- - Node.js >= 16
251
- - Vue >= 3.2.0
252
- - ant-design-vue >= 4.0.0
253
- - @ant-design/icons-vue >= 7.0.0
254
-
255
- ## Components
256
-
257
- ### PGrid
258
-
259
- Enhanced data table component with integrated search form, pagination, and toolbar.
260
-
261
- #### Basic Example
262
-
263
- ```vue
264
- <script setup lang="ts">
265
- import { computed } from 'vue';
266
- import { PGridProps, labelColDict } from '@vue-ui-kit/ant';
267
-
268
- const gridSetting = computed<PGridProps<Student, { keyword?: string }>>(() => ({
269
- columns: [
270
- { field: 'name', title: 'Name', width: 200 },
271
- { field: 'email', title: 'Email', width: 200 },
272
- { field: 'age', title: 'Age', width: 100 },
273
- ],
274
- formConfig: {
275
- items: [
276
- {
277
- field: 'keyword',
278
- title: 'Keyword',
279
- labelCol: labelColDict[3],
280
- itemRender: {
281
- name: '$input',
282
- props: { placeholder: 'Search...' },
283
- },
284
- },
285
- ],
286
- },
287
- proxyConfig: {
288
- ajax: {
289
- query: ({ form, page }) => api.getStudents({ ...form, ...page }),
290
- },
291
- },
292
- }));
293
- </script>
294
-
295
- <template>
296
- <p-grid v-bind="gridSetting" />
297
- </template>
298
- ```
299
-
300
- ### PForm
301
-
302
- Enhanced Form component with simplified configuration and dynamic fields.
303
-
304
- #### Props
305
-
306
- | Prop | Type | Description | Default |
307
- | ----------- | ------------------ | ------------------------------ | -------------- |
308
- | items | `PFormItemProps[]` | Form items configuration array | - |
309
- | data | `T` | Form data object | - |
310
- | customReset | `() => void` | Custom reset function | - |
311
- | labelCol | `ColProps` | Label column layout | `{ span: 6 }` |
312
- | wrapperCol | `ColProps` | Wrapper column layout | `{ span: 16 }` |
313
- | ...others | `FormProps` | All ant-design-vue Form props | - |
314
-
315
- #### Events
316
-
317
- | Event | Description | Parameters |
318
- | ----- | -------------------------------- | ----------------------- |
319
- | apply | Triggered when form is submitted | `(formData: T) => void` |
320
- | reset | Triggered when form is reset | `() => void` |
321
-
322
- #### PFormItemProps
323
-
324
- | Prop | Type | Description | Default |
325
- | ------------- | ------------------------------- | -------------------------------- | -------- |
326
- | field | `string` | Form field name | - |
327
- | title | `string` | Field label | - |
328
- | span | `number` | Grid span (1-24) | - |
329
- | colon | `boolean` | Show colon after label | `true` |
330
- | labelCol | `ColProps` | Label column layout | - |
331
- | wrapperCol | `ColProps` | Wrapper column layout | - |
332
- | forceRequired | `boolean` | Show required mark (visual only) | `false` |
333
- | align | `'left' \| 'right' \| 'center'` | Text alignment | `'left'` |
334
- | col | `ColProps` | Grid column properties | - |
335
- | rule | `Rule[]` | Validation rules | - |
336
- | itemRender | `ItemRender` | Field renderer configuration | - |
337
- | tooltipConfig | `TooltipConfig` | Tooltip configuration | - |
338
- | slots | `object` | Custom slot renderers | - |
339
-
340
- #### Built-in Renderers
341
-
342
- - `$input`: Input field
343
- - `$textarea`: Textarea field
344
- - `$number`: Number input
345
- - `$select`: Select dropdown
346
- - `$date`: Date picker
347
- - `$range`: Date range picker
348
- - `$time`: Time picker
349
- - `ASwitch`: Switch component
350
- - `ACheckbox`: Checkbox
351
- - `ARate`: Rate component
352
- - `ASlider`: Slider component
353
-
354
- #### Basic Example
355
-
356
- ```vue
357
- <script setup lang="ts">
358
- import { ref, computed } from 'vue';
359
- import { PFormProps } from '@vue-ui-kit/ant';
360
-
361
- interface UserForm {
362
- name: string;
363
- email: string;
364
- age?: number;
365
- gender: string;
366
- skills: string[];
367
- }
368
-
369
- const formData = ref<UserForm>({
370
- name: '',
371
- email: '',
372
- age: undefined,
373
- gender: '',
374
- skills: [],
375
- });
376
-
377
- const formSetting = computed<PFormProps<UserForm>>(() => ({
378
- items: [
379
- {
380
- field: 'name',
381
- title: 'Name',
382
- span: 12,
383
- rule: [{ required: true, message: 'Please enter name' }],
384
- itemRender: {
385
- name: '$input',
386
- props: { placeholder: 'Enter name' },
387
- },
388
- },
389
- {
390
- field: 'email',
391
- title: 'Email',
392
- span: 12,
393
- rule: [
394
- { required: true, message: 'Please enter email' },
395
- { type: 'email', message: 'Invalid email format' },
396
- ],
397
- itemRender: {
398
- name: '$input',
399
- props: { placeholder: 'Enter email' },
400
- },
401
- },
402
- {
403
- field: 'gender',
404
- title: 'Gender',
405
- span: 12,
406
- rule: [{ required: true, message: 'Please select gender' }],
407
- itemRender: {
408
- name: '$select',
409
- props: {
410
- placeholder: 'Select gender',
411
- options: [
412
- { label: 'Male', value: 'male' },
413
- { label: 'Female', value: 'female' },
414
- ],
415
- },
416
- },
417
- },
418
- {
419
- field: 'skills',
420
- title: 'Skills',
421
- span: 24,
422
- itemRender: {
423
- name: '$select',
424
- props: {
425
- mode: 'multiple',
426
- placeholder: 'Select skills',
427
- options: [
428
- { label: 'Vue.js', value: 'vue' },
429
- { label: 'React', value: 'react' },
430
- { label: 'Angular', value: 'angular' },
431
- ],
432
- },
433
- },
434
- },
435
- ],
436
- }));
437
-
438
- const handleSubmit = (data: UserForm) => {
439
- console.log('Form submitted:', data);
440
- };
441
- </script>
442
-
443
- <template>
444
- <p-form v-bind="formSetting" :data="formData" @apply="handleSubmit" />
445
- </template>
446
- ```
447
-
448
- #### Advanced Example with Custom Slots
449
-
450
- ```vue
451
- <script setup lang="tsx">
452
- import { ref, computed } from 'vue';
453
-
454
- const formData = ref({
455
- name: '',
456
- isActive: false,
457
- });
458
-
459
- const formSetting = computed(() => ({
460
- items: [
461
- {
462
- field: 'isActive',
463
- title: 'Status',
464
- span: 24,
465
- slots: {
466
- default: ({ data }) => (
467
- <a-switch
468
- v-model:checked={data.isActive}
469
- checkedChildren="Active"
470
- unCheckedChildren="Inactive"
471
- />
472
- ),
473
- },
474
- },
475
- ],
476
- }));
477
- </script>
478
- ```
479
-
480
- ### PFormGroup
481
-
482
- Dynamic form group component for managing multiple form instances with add/remove capabilities.
483
-
484
- #### Props
485
-
486
- | Prop | Type | Description | Default |
487
- | --- | --- | --- | --- |
488
- | v-model | `Array<T & { __index: number }>` | Form group data array | `[]` |
489
- | getFormSetting | `(data: T) => PFormProps<T>` | Function to get form configuration | - |
490
- | title | `string` | Group title | - |
491
- | tabLabel | `string` | Custom tab label template | - |
492
- | editAble | `boolean` | Whether tabs are editable | `true` |
493
- | showAdd | `boolean` | Show add button | `true` |
494
- | lazyErrorMark | `boolean` | Lazy error marking | `false` |
495
- | forceRender | `boolean` | Force render all tabs | `false` |
496
- | keepSerial | `boolean` | Keep serial indexing | `false` |
497
- | loading | `boolean` | Loading state | `false` |
498
- | max | `number` | Maximum number of items | `Infinity` |
499
- | itemMenus | `GroupMenuItem[]` | Custom menu items | Default copy/delete |
500
- | createItem | `(opts: { list: T[] }) => Promise<T>` | Custom item creator | - |
501
- | menuHandler | `GroupMenuItemHandler<T>` | Custom menu handler | - |
502
-
503
- #### Exposed Methods
504
-
505
- | Method | Description | Parameters | Returns |
506
- | ------------ | ------------------------------- | ----------------- | --------------- |
507
- | validateAll | Validate all form instances | - | `Promise<void>` |
508
- | validate | Validate specific form instance | `__index: number` | `Promise<void>` |
509
- | setActiveKey | Set active tab | `key: number` | `void` |
510
-
511
- #### Basic Example
512
-
513
- ```vue
514
- <script setup lang="ts">
515
- import { ref, computed } from 'vue';
516
- import { PFormGroupProps } from '@vue-ui-kit/ant';
517
-
518
- interface Project {
519
- __index: number;
520
- name: string;
521
- startDate: string;
522
- endDate: string;
523
- budget?: number;
524
- }
525
-
526
- const projects = ref<Project[]>([
527
- {
528
- __index: 0,
529
- name: 'Project A',
530
- startDate: '',
531
- endDate: '',
532
- budget: undefined,
533
- },
534
- ]);
535
-
536
- const groupSetting = computed<PFormGroupProps<Project>>(() => ({
537
- title: 'Project Management',
538
- showAdd: true,
539
- max: 10,
540
- getFormSetting: (data) => ({
541
- items: [
542
- {
543
- field: 'name',
544
- title: 'Project Name',
545
- span: 24,
546
- rule: [{ required: true, message: 'Please enter project name' }],
547
- itemRender: {
548
- name: '$input',
549
- props: { placeholder: 'Enter project name' },
550
- },
551
- },
552
- {
553
- field: 'startDate',
554
- title: 'Start Date',
555
- span: 12,
556
- rule: [{ required: true, message: 'Please select start date' }],
557
- itemRender: {
558
- name: '$date',
559
- props: { placeholder: 'Select start date' },
560
- },
561
- },
562
- {
563
- field: 'endDate',
564
- title: 'End Date',
565
- span: 12,
566
- rule: [{ required: true, message: 'Please select end date' }],
567
- itemRender: {
568
- name: '$date',
569
- props: { placeholder: 'Select end date' },
570
- },
571
- },
572
- {
573
- field: 'budget',
574
- title: 'Budget',
575
- span: 24,
576
- itemRender: {
577
- name: '$number',
578
- props: {
579
- min: 0,
580
- placeholder: 'Enter budget',
581
- formatter: (value) => `$ ${value}`.replace(/\B(?=(\d{3})+(?!\d))/g, ','),
582
- parser: (value) => value.replace(/\$\s?|(,*)/g, ''),
583
- },
584
- },
585
- },
586
- ],
587
- }),
588
- }));
589
-
590
- const handleSave = async () => {
591
- try {
592
- await groupRef.value?.validateAll();
593
- console.log('All projects saved:', projects.value);
594
- } catch (error) {
595
- console.error('Validation failed:', error);
596
- }
597
- };
598
-
599
- const groupRef = ref();
600
- </script>
601
-
602
- <template>
603
- <div>
604
- <p-form-group ref="groupRef" v-model="projects" v-bind="groupSetting" />
605
- <a-button type="primary" @click="handleSave"> Save All Projects </a-button>
606
- </div>
607
- </template>
608
- ```
609
-
610
- #### Advanced Example with Custom Menu
611
-
612
- ```vue
613
- <script setup lang="ts">
614
- import { ref, computed } from 'vue';
615
-
616
- const projects = ref([]);
617
-
618
- const groupSetting = computed(() => ({
619
- title: 'Advanced Project Management',
620
- itemMenus: [
621
- { content: 'Copy', code: 'copy' },
622
- { content: 'Delete', code: 'delete' },
623
- { content: 'Export', code: 'export' },
624
- {
625
- content: 'Archive',
626
- code: 'archive',
627
- visibleMethod: ({ data }) => data.status !== 'archived',
628
- },
629
- ],
630
- createItem: async ({ list }) => {
631
- return {
632
- name: `Project ${list.length + 1}`,
633
- status: 'active',
634
- startDate: new Date().toISOString().split('T')[0],
635
- };
636
- },
637
- menuHandler: ({ code, data, index }) => {
638
- switch (code) {
639
- case 'export':
640
- exportProject(data);
641
- break;
642
- case 'archive':
643
- archiveProject(data, index);
644
- break;
645
- }
646
- },
647
- getFormSetting: (data) => ({
648
- // ... form configuration
649
- }),
650
- }));
651
- </script>
652
- ```
653
-
654
- ## Utility Functions
655
-
656
- ### labelColDict
657
-
658
- Pre-defined label column configurations for different text lengths:
659
-
660
- ```ts
661
- import { labelColDict } from '@vue-ui-kit/ant';
662
-
663
- // Usage in form items
664
- {
665
- field: 'shortField',
666
- title: 'Name', // 2 characters
667
- labelCol: labelColDict[2],
668
- }
669
-
670
- {
671
- field: 'longerField',
672
- title: 'Description', // 4+ characters
673
- labelCol: labelColDict[4],
674
- }
675
- ```
676
-
677
- ### Custom Renderers
678
-
679
- Register custom form field renderers:
680
-
681
- ```tsx
682
- import UIKit from '@vue-ui-kit/ant';
683
- import { Switch } from 'ant-design-vue';
684
-
685
- UIKit.addRender('$switch', {
686
- renderItemContent({ props = {}, events = {} }, { data, field }) {
687
- return (
688
- <Switch
689
- v-model:checked={data[field]}
690
- {...props}
691
- onChange={(...args) => {
692
- events.change?.({ data, field }, ...args);
693
- }}
694
- />
695
- );
696
- },
697
- });
698
- ```
699
-
700
- ### Custom Formatters
701
-
702
- Register custom cell formatters for PGrid:
703
-
704
- ```ts
705
- UIKit.addFormatter({
706
- currency: ({ cellValue }) => `$${cellValue?.toLocaleString() || '0'}`,
707
- percentage: ({ cellValue }) => `${(cellValue * 100).toFixed(2)}%`,
708
- });
709
- ```
710
-
711
- ## FAQ
712
-
713
- ### 1. When using computed + v-bind to generate dynamic tables + forms, form option updates cause table re-rendering. How to solve?
714
-
715
- Answer: Separate the formConfig into another computed property
716
-
717
- ```vue
718
- <p-grid v-bind="gridSetting" :form-config="computedFormConfig" />
719
- ```
720
-
721
- ### 2. Does the table ajax currently only support multiDelete and table data fetching?
722
-
723
- Answer: Edit details in most cases cannot correspond one-to-one with the table, so there's no development significance. Multi-row deletion needs to be used together with:
724
-
725
- ```ts
726
- selectConfig: {
727
- multiple: true,
728
- },
729
- toolbarConfig: {
730
- buttons: {
731
- code: "multipleDelete"
732
- }
733
- }
734
- ```
735
-
736
- ### 3. Why is the computed approach recommended?
737
-
738
- Answer: It simplifies all dynamic logic. For complex scenarios, you can try using reactive and maintain it yourself, but in most cases, computed is more convenient.
739
-
740
- ## 🔤 TypeScript 类型使用
741
-
742
- ### 基本类型导入
743
-
744
- ```typescript
745
- import {
746
- PGridProps,
747
- PFormProps,
748
- PFormItemProps,
749
- ColumnProps,
750
- ToolbarConfig,
751
- UIKitConfig,
752
- } from '@vue-ui-kit/ant';
753
- ```
754
-
755
- ### PGrid 类型使用示例
756
-
757
- ```typescript
758
- import { computed } from 'vue';
759
- import { PGridProps } from '@vue-ui-kit/ant';
760
-
761
- // 定义数据类型
762
- interface Student {
763
- id: number;
764
- name: string;
765
- email: string;
766
- age: number;
767
- }
768
-
769
- // 定义查询表单类型
770
- interface StudentQuery {
771
- keyword?: string;
772
- age?: number;
773
- }
774
-
775
- // 使用PGridProps类型
776
- const gridSetting = computed<PGridProps<Student, StudentQuery>>(() => ({
777
- columns: [
778
- { field: 'name', title: '姓名', width: 200 },
779
- { field: 'email', title: '邮箱', width: 200 },
780
- { field: 'age', title: '年龄', width: 100 },
781
- ],
782
- formConfig: {
783
- items: [
784
- {
785
- field: 'keyword',
786
- title: '关键字',
787
- itemRender: {
788
- name: '$input',
789
- props: { placeholder: '请输入关键字' },
790
- },
791
- },
792
- ],
793
- },
794
- proxyConfig: {
795
- ajax: {
796
- query: ({ form, page }) => api.getStudents({ ...form, ...page }),
797
- },
798
- },
799
- }));
800
- ```
801
-
802
- ### PForm 类型使用示例
803
-
804
- ```typescript
805
- import { ref, computed } from 'vue';
806
- import { PFormProps, PFormItemProps } from '@vue-ui-kit/ant';
807
-
808
- // 定义表单数据类型
809
- interface UserForm {
810
- name: string;
811
- email: string;
812
- age?: number;
813
- gender: string;
814
- }
815
-
816
- const formData = ref<UserForm>({
817
- name: '',
818
- email: '',
819
- age: undefined,
820
- gender: '',
821
- });
822
-
823
- // 使用PFormProps类型
824
- const formSetting = computed<PFormProps<UserForm>>(() => ({
825
- items: [
826
- {
827
- field: 'name',
828
- title: '姓名',
829
- rule: [{ required: true, message: '请输入姓名' }],
830
- itemRender: {
831
- name: '$input',
832
- props: { placeholder: '请输入姓名' },
833
- },
834
- },
835
- {
836
- field: 'email',
837
- title: '邮箱',
838
- rule: [
839
- { required: true, message: '请输入邮箱' },
840
- { type: 'email', message: '邮箱格式不正确' },
841
- ],
842
- itemRender: {
843
- name: '$input',
844
- props: { placeholder: '请输入邮箱' },
845
- },
846
- },
847
- ] as PFormItemProps<UserForm>[],
848
- }));
849
- ```
850
-
851
- ### 高级类型使用
852
-
853
- ```typescript
854
- // 1. 自定义列类型
855
- import { ColumnProps } from '@vue-ui-kit/ant';
856
-
857
- const customColumns: ColumnProps<Student>[] = [
858
- {
859
- field: 'name',
860
- title: '姓名',
861
- formatter: (arg) => arg.cellValue?.toUpperCase(),
862
- },
863
- {
864
- field: 'actions',
865
- title: '操作',
866
- slots: {
867
- default: ({ row, rowIndex }) => (
868
- <button onClick={() => handleEdit(row)}>编辑</button>
869
- ),
870
- },
871
- },
872
- ];
873
-
874
- // 2. 工具栏配置类型
875
- import { ToolbarConfig } from '@vue-ui-kit/ant';
876
-
877
- const toolbarConfig: ToolbarConfig = {
878
- buttons: [
879
- {
880
- code: 'add',
881
- content: '新增',
882
- type: 'primary',
883
- icon: 'PlusOutlined',
884
- },
885
- ],
886
- tools: [
887
- {
888
- code: 'refresh',
889
- icon: 'ReloadOutlined',
890
- },
891
- ],
892
- };
893
-
894
- // 3. Setup配置类型
895
- import { UIKitConfig } from '@vue-ui-kit/ant';
896
-
897
- const kitConfig: UIKitConfig = {
898
- form: {
899
- labelCol: { span: 8 },
900
- wrapperCol: { span: 14 },
901
- },
902
- grid: {
903
- align: 'center',
904
- lazyReset: true,
905
- fitHeight: 200,
906
- },
907
- };
908
- ```
909
-
910
- ### 组件实例类型
911
-
912
- ```typescript
913
- import { ref } from 'vue';
914
- import { PGridInstance, PFormInstance } from '@vue-ui-kit/ant';
915
-
916
- // PGrid 组件实例
917
- const gridRef = ref<PGridInstance<Student, StudentQuery>>();
918
-
919
- // 调用实例方法
920
- gridRef.value?.commitProxy.reload();
921
- gridRef.value?.resizeTable();
922
-
923
- // PForm 组件实例
924
- const formRef = ref<PFormInstance>();
925
-
926
- // 调用实例方法
927
- formRef.value?.reset();
928
- formRef.value?.$form.validateFields();
929
- ```
930
-
931
- ## TypeScript Support
932
-
933
- This library is fully written in TypeScript and provides comprehensive type definitions:
934
-
935
- ```ts
936
- import type {
937
- PFormProps,
938
- PFormItemProps,
939
- PFormGroupProps,
940
- PGridProps,
941
- ColumnProps,
942
- } from '@vue-ui-kit/ant';
943
- ```
944
-
945
- ## License
946
-
947
- MIT
948
-
949
- ### 🛠️ 工具方法使用
950
-
951
- #### 从npm包导入工具方法
952
-
953
- ```typescript
954
- import {
955
- // 响应式布局工具
956
- getButtonResponsive,
957
- defaultItemResponsive,
958
- defaultLabelCol,
959
- labelColDict,
960
- get24rest,
961
-
962
- // 表格工具
963
- cleanCol,
964
-
965
- // 其他工具
966
- UIKitConfig,
967
- setup,
968
- } from '@vue-ui-kit/ant';
969
- ```
970
-
971
- #### getButtonResponsive 使用示例
972
-
973
- ```typescript
974
- import { getButtonResponsive, defaultItemResponsive } from '@vue-ui-kit/ant';
975
-
976
- // 方式1: 根据表单项数量自动计算按钮响应式布局
977
- const buttonSpan1 = getButtonResponsive(3); // 3个表单项
978
- console.log(buttonSpan1);
979
- // 结果: { xs: 24, sm: 24, md: 24, lg: 0, xl: 0, xxl: 6 }
980
-
981
- // 方式2: 传入自定义响应式配置数组
982
- const customResponsive = [
983
- { xs: 24, sm: 12, md: 8, lg: 6, xl: 6, xxl: 6 },
984
- { xs: 24, sm: 12, md: 8, lg: 6, xl: 6, xxl: 6 },
985
- ];
986
- const buttonSpan2 = getButtonResponsive(customResponsive);
987
- console.log(buttonSpan2);
988
- // 计算剩余空间作为按钮区域
989
- ```
990
-
991
- #### labelColDict 使用示例
992
-
993
- ```typescript
994
- import { labelColDict } from '@vue-ui-kit/ant';
995
-
996
- // 根据标签字符数获取合适的labelCol配置
997
- const labelCol = labelColDict[4]; // 4个字符的标签
998
- console.log(labelCol);
999
- // 结果: { xs: 12, sm: 4, md: 6, lg: 9, xl: 7, xxl: 6 }
1000
-
1001
- // 在表单配置中使用
1002
- const formSetting = computed(() => ({
1003
- items: [
1004
- {
1005
- field: 'username',
1006
- title: '用户名称', // 4个字符
1007
- labelCol: labelColDict[4],
1008
- itemRender: {
1009
- name: '$input',
1010
- props: { placeholder: '请输入用户名称' },
1011
- },
1012
- },
1013
- ],
1014
- }));
1015
- ```
1016
-
1017
- #### 响应式布局工具完整示例
1018
-
1019
- ```typescript
1020
- import { getButtonResponsive, defaultItemResponsive, labelColDict } from '@vue-ui-kit/ant';
1021
-
1022
- // 创建一个动态表单布局计算器
1023
- function createFormLayout(items: Array<{ title: string }>) {
1024
- return {
1025
- // 表单项布局
1026
- items: items.map((item) => ({
1027
- ...item,
1028
- col: defaultItemResponsive, // 使用默认响应式布局
1029
- labelCol: labelColDict[item.title.length] || labelColDict[4], // 根据标题长度选择
1030
- })),
1031
-
1032
- // 按钮区域布局
1033
- buttonCol: getButtonResponsive(items.length),
1034
- };
1035
- }
1036
-
1037
- // 使用示例
1038
- const layout = createFormLayout([{ title: '姓名' }, { title: '邮箱地址' }, { title: '手机号码' }]);
1039
-
1040
- console.log('按钮布局:', layout.buttonCol);
1041
- // 自动计算最后一行剩余空间给按钮使用
1042
- ```
1043
-
1044
- #### cleanCol 表格列处理
1045
-
1046
- ```typescript
1047
- import { cleanCol, type ColumnProps } from '@vue-ui-kit/ant';
1048
-
1049
- // 自定义列配置
1050
- const customColumn: ColumnProps<User> = {
1051
- field: 'name',
1052
- title: '用户名',
1053
- formatter: 'capitalize',
1054
- cellRender: {
1055
- name: '$link',
1056
- props: { href: '#' },
1057
- },
1058
- slots: {
1059
- default: ({ row }) => <span>{row.name}</span>,
1060
- },
1061
- };
1062
-
1063
- // 清理列配置,移除UI Kit特有属性,转换为Ant Design Vue标准格式
1064
- const antColumn = cleanCol(customColumn);
1065
- console.log(antColumn);
1066
- // 结果会移除 formatter, cellRender, slots 等UI Kit特有属性
1067
- // 保留 Ant Design Vue 原生支持的属性
1068
- ```
1069
-
1070
- #### get24rest 栅格计算
1071
-
1072
- ```typescript
1073
- import { get24rest } from '@vue-ui-kit/ant';
1074
-
1075
- // 计算24栅格系统中的剩余空间
1076
- const usedSpans = [6, 8, 4]; // 已使用的栅格数
1077
- const restSpan = get24rest(usedSpans);
1078
- console.log(restSpan); // 结果: 6 (24 - 6 - 8 - 4 = 6)
1079
-
1080
- // 在表单中动态计算按钮位置
1081
- const formItems = [
1082
- { span: 8 }, // 表单项1
1083
- { span: 8 }, // 表单项2
1084
- { span: 6 }, // 表单项3
1085
- ];
1086
- const buttonSpan = get24rest(formItems.map((item) => item.span));
1087
- // 按钮将占据剩余的2格空间,如果小于等于1则占满整行(24格)
1088
- ```
1089
-
1090
- #### 在Vue组件中使用
1091
-
1092
- ```vue
1093
- <template>
1094
- <div>
1095
- <a-row :gutter="[16, 16]">
1096
- <!-- 动态表单项 -->
1097
- <a-col v-for="(item, index) in formItems" :key="index" v-bind="defaultItemResponsive">
1098
- <a-form-item :label="item.label" :label-col="getLabelCol(item.label)">
1099
- <a-input v-model:value="item.value" />
1100
- </a-form-item>
1101
- </a-col>
1102
-
1103
- <!-- 动态按钮区域 -->
1104
- <a-col v-bind="buttonLayout">
1105
- <a-button type="primary">提交</a-button>
1106
- <a-button>重置</a-button>
1107
- </a-col>
1108
- </a-row>
1109
- </div>
1110
- </template>
1111
-
1112
- <script setup lang="ts">
1113
- import { computed } from 'vue';
1114
- import { getButtonResponsive, defaultItemResponsive, labelColDict } from '@vue-ui-kit/ant';
1115
-
1116
- const formItems = ref([
1117
- { label: '姓名', value: '' },
1118
- { label: '电子邮箱', value: '' },
1119
- { label: '联系电话', value: '' },
1120
- ]);
1121
-
1122
- // 动态计算按钮布局
1123
- const buttonLayout = computed(() => getButtonResponsive(formItems.value.length));
1124
-
1125
- // 根据标签长度获取合适的labelCol
1126
- const getLabelCol = (label: string) => labelColDict[label.length] || labelColDict[4];
1127
- </script>
1128
- ```
1129
-
1130
- ## 📦 组件和工具导入
1131
-
1132
- ### ✅ 正确的导入方式
1133
-
1134
- ```typescript
1135
- // 方式1: 分别导入 (推荐)
1136
- import {
1137
- // 组件
1138
- PForm,
1139
- PGrid,
1140
- PFormGroup,
1141
- PGroupBlock,
1142
- PromisePicker,
1143
-
1144
- // 类型
1145
- PGridProps,
1146
- PFormProps,
1147
- PFormItemProps,
1148
- ColumnProps,
1149
- UIKitConfig,
1150
-
1151
- // 工具方法
1152
- getButtonResponsive,
1153
- labelColDict,
1154
- defaultItemResponsive,
1155
- get24rest,
1156
- cleanCol,
1157
-
1158
- // 配置方法
1159
- setup,
1160
- addFormatter,
1161
- addRender,
1162
- } from '@vue-ui-kit/ant';
1163
-
1164
- // 方式2: 默认导入 + 命名导入
1165
- import UIKit, { PForm, PGrid, setup } from '@vue-ui-kit/ant';
1166
- ```
1167
-
1168
- ### 🔧 完整使用示例
1169
-
1170
- ```vue
1171
- <template>
1172
- <div class="demo-page">
1173
- <!-- PForm 组件 -->
1174
- <PForm
1175
- ref="formRef"
1176
- v-bind="formSetting"
1177
- :data="formData"
1178
- @apply="handleFormSubmit"
1179
- @reset="handleFormReset"
1180
- />
1181
-
1182
- <!-- PGrid 组件 -->
1183
- <PGrid ref="gridRef" v-bind="gridSetting" @toolbar-button-click="handleToolbarClick" />
1184
- </div>
1185
- </template>
1186
-
1187
- <script setup lang="ts">
1188
- import { ref, computed } from 'vue';
1189
- import {
1190
- // 直接导入组件
1191
- PForm,
1192
- PGrid,
1193
-
1194
- // 导入类型
1195
- PFormProps,
1196
- PGridProps,
1197
- PFormInstance,
1198
- PGridInstance,
1199
-
1200
- // 导入工具方法
1201
- getButtonResponsive,
1202
- labelColDict,
1203
-
1204
- // 导入配置方法
1205
- setup,
1206
- } from '@vue-ui-kit/ant';
1207
-
1208
- // 配置全局默认值
1209
- setup({
1210
- grid: {
1211
- align: 'center',
1212
- fitHeight: 180,
1213
- },
1214
- form: {
1215
- labelCol: labelColDict[4],
1216
- },
1217
- });
1218
-
1219
- // 类型定义
1220
- interface User {
1221
- id: number;
1222
- name: string;
1223
- email: string;
1224
- }
1225
-
1226
- interface UserForm {
1227
- name: string;
1228
- email: string;
1229
- }
1230
-
1231
- interface UserQuery {
1232
- keyword?: string;
1233
- }
1234
-
1235
- // 组件引用
1236
- const formRef = ref<PFormInstance>();
1237
- const gridRef = ref<PGridInstance<User, UserQuery>>();
1238
-
1239
- // 表单数据
1240
- const formData = ref<UserForm>({
1241
- name: '',
1242
- email: '',
1243
- });
1244
-
1245
- // 表单配置
1246
- const formSetting = computed<PFormProps<UserForm>>(() => ({
1247
- items: [
1248
- {
1249
- field: 'name',
1250
- title: '用户名',
1251
- labelCol: labelColDict[3], // 3个字符的标签
1252
- rule: [{ required: true, message: '请输入用户名' }],
1253
- itemRender: {
1254
- name: '$input',
1255
- props: { placeholder: '请输入用户名' },
1256
- },
1257
- },
1258
- {
1259
- field: 'email',
1260
- title: '邮箱地址',
1261
- labelCol: labelColDict[4], // 4个字符的标签
1262
- rule: [
1263
- { required: true, message: '请输入邮箱' },
1264
- { type: 'email', message: '邮箱格式不正确' },
1265
- ],
1266
- itemRender: {
1267
- name: '$input',
1268
- props: { placeholder: '请输入邮箱地址' },
1269
- },
1270
- },
1271
- ],
1272
- }));
1273
-
1274
- // 网格配置
1275
- const gridSetting = computed<PGridProps<User, UserQuery>>(() => ({
1276
- columns: [
1277
- { field: 'name', title: '用户名', width: 200 },
1278
- { field: 'email', title: '邮箱', width: 200 },
1279
- ],
1280
- formConfig: {
1281
- items: [
1282
- {
1283
- field: 'keyword',
1284
- title: '关键字',
1285
- labelCol: labelColDict[3],
1286
- itemRender: {
1287
- name: '$input',
1288
- props: { placeholder: '请输入关键字' },
1289
- },
1290
- },
1291
- ],
1292
- },
1293
- toolbarConfig: {
1294
- buttons: [
1295
- {
1296
- code: 'add',
1297
- content: '新增',
1298
- type: 'primary',
1299
- icon: 'PlusOutlined',
1300
- },
1301
- ],
1302
- },
1303
- proxyConfig: {
1304
- ajax: {
1305
- query: ({ form, page }) => api.getUsers({ ...form, ...page }),
1306
- },
1307
- },
1308
- }));
1309
-
1310
- // 事件处理
1311
- const handleFormSubmit = (data: UserForm) => {
1312
- console.log('表单提交:', data);
1313
- };
1314
-
1315
- const handleFormReset = () => {
1316
- console.log('表单重置');
1317
- };
1318
-
1319
- const handleToolbarClick = ({ code, data, selectedKeys }) => {
1320
- console.log('工具栏点击:', { code, data, selectedKeys });
1321
-
1322
- if (code === 'add') {
1323
- // 处理新增逻辑
1324
- }
1325
- };
1326
-
1327
- // 使用工具方法
1328
- const buttonLayout = computed(
1329
- () => getButtonResponsive(2), // 根据表单项数量计算按钮布局
1330
- );
1331
- </script>
1332
- ```
1333
-
1334
- ### 🚨 常见导入错误
1335
-
1336
- ```typescript
1337
- // ❌ 错误:这样无法导入组件
1338
- import { PForm, PGrid } from '@vue-ui-kit/ant/components';
1339
-
1340
- // ❌ 错误:组件不能从utils路径导入
1341
- import { PForm } from '@vue-ui-kit/ant/utils';
1342
-
1343
- // ❌ 错误:混合默认导入和组件导入
1344
- import UIKit, PForm from '@vue-ui-kit/ant'; // 语法错误
1345
-
1346
- // ✅ 正确:从主包路径导入所有内容
1347
- import {
1348
- PForm,
1349
- PGrid,
1350
- getButtonResponsive,
1351
- labelColDict
1352
- } from '@vue-ui-kit/ant';
1353
- ```
1354
-
1355
- ### 📋 可导入的完整列表
1356
-
1357
- #### 组件
1358
-
1359
- - `PForm` - 增强表单组件
1360
- - `PGrid` - 增强数据表格组件
1361
- - `PFormGroup` - 动态表单组组件
1362
- - `PGroupBlock` - 表单块组件
1363
- - `PromisePicker` - 数据选择器组件
1364
-
1365
- #### 类型定义
1366
-
1367
- - `PFormProps<T>` - 表单属性类型
1368
- - `PGridProps<D, F>` - 网格属性类型
1369
- - `PFormInstance` - 表单实例类型
1370
- - `PGridInstance<D, F>` - 网格实例类型
1371
- - `ColumnProps<T>` - 列配置类型
1372
- - `UIKitConfig` - 全局配置类型
1373
-
1374
- #### 工具方法
1375
-
1376
- - `getButtonResponsive()` - 计算按钮响应式布局
1377
- - `labelColDict` - 标签宽度配置字典
1378
- - `defaultItemResponsive` - 默认响应式配置
1379
- - `get24rest()` - 栅格剩余空间计算
1380
- - `cleanCol()` - 清理列配置
1381
-
1382
- #### 配置方法
1383
-
1384
- - `setup()` - 全局配置设置
1385
- - `addFormatter()` - 添加格式化器
1386
- - `addRender()` - 添加渲染器
1
+ <div align="center">
2
+ English | [简体中文](./README.zh.md)
3
+ </div>
4
+
5
+ # Vue UI Kit Documentation
6
+
7
+ A Vue 3 UI component library based on Ant Design Vue, providing enhanced form, grid, and utility components.
8
+
9
+ ## Installation
10
+
11
+ ```bash
12
+ npm install @vue-ui-kit/ant
13
+ # or
14
+ yarn add @vue-ui-kit/ant
15
+ # or
16
+ pnpm add @vue-ui-kit/ant
17
+ ```
18
+
19
+ ## Quick Start
20
+
21
+ ### 1. Basic Usage
22
+
23
+ ```ts
24
+ /*main.ts*/
25
+ import { createApp } from 'vue';
26
+ import App from './App.vue';
27
+ import Antd from 'ant-design-vue';
28
+ import UIKit from '@vue-ui-kit/ant';
29
+
30
+ // Import styles - choose one of the following methods:
31
+ // Method 1: Import SCSS source files (for development, allows variable override)
32
+ import '@vue-ui-kit/ant/style.scss';
33
+
34
+ // Method 1.1: Import compiled SCSS file (for production, standalone file)
35
+ // import '@vue-ui-kit/ant/dist/style.scss';
36
+
37
+ // Method 2: Import compiled CSS file
38
+ // import '@vue-ui-kit/ant/style.css';
39
+
40
+ createApp(App).use(Antd).use(UIKit).mount('#app');
41
+ ```
42
+
43
+ ### 2. Configure Global Defaults
44
+
45
+ ```ts
46
+ /*main.ts*/
47
+ import { createApp } from 'vue';
48
+ import App from './App.vue';
49
+ import Antd from 'ant-design-vue';
50
+ import UIKit, { setup } from '@vue-ui-kit/ant';
51
+ import '@vue-ui-kit/ant/style.scss';
52
+
53
+ // Configure global defaults
54
+ setup({
55
+ form: {
56
+ labelCol: { span: 8 }, // Modify form label column width
57
+ wrapperCol: { span: 14 }, // Modify form input column width
58
+ },
59
+ grid: {
60
+ align: 'center', // Set default table alignment
61
+ lazyReset: true, // Don't auto-submit after reset
62
+ fitHeight: 200, // Set adaptive height
63
+ },
64
+ });
65
+
66
+ createApp(App).use(Antd).use(UIKit).mount('#app');
67
+ ```
68
+
69
+ ### 3. Alternative import methods (if main method doesn't work)
70
+
71
+ If you encounter issues with the standard import, try these alternatives:
72
+
73
+ ```scss
74
+ // In your main.scss file
75
+ @use '@vue-ui-kit/ant/dist/style.scss';
76
+ // or
77
+ @import '@vue-ui-kit/ant/dist/style.scss';
78
+ ```
79
+
80
+ ```ts
81
+ // Or using require in JavaScript/TypeScript
82
+ require('@vue-ui-kit/ant/style.css');
83
+ ```
84
+
85
+ ### 4. For Vite users
86
+
87
+ If using Vite, make sure your vite.config.js includes sass support:
88
+
89
+ ```js
90
+ // vite.config.js
91
+ export default {
92
+ css: {
93
+ preprocessorOptions: {
94
+ scss: {
95
+ additionalData: `@import '@vue-ui-kit/ant/dist/style.scss';`,
96
+ },
97
+ },
98
+ },
99
+ };
100
+ ```
101
+
102
+ ## 🎯 样式文件导入 - 故障排除指南
103
+
104
+ 如果遇到样式文件导入问题,请按顺序尝试以下方法:
105
+
106
+ ### 方法1: 标准导入(推荐)
107
+
108
+ ```typescript
109
+ import '@vue-ui-kit/ant/style.scss';
110
+ // 或
111
+ import '@vue-ui-kit/ant/style.css';
112
+ ```
113
+
114
+ ### 方法2: 完整路径导入
115
+
116
+ ```typescript
117
+ import '@vue-ui-kit/ant/dist/style.scss';
118
+ // 或
119
+ import '@vue-ui-kit/ant/dist/style.css';
120
+ ```
121
+
122
+ ### 方法3: SCSS @use 语法
123
+
124
+ ```scss
125
+ // 开发环境(源码文件)
126
+ @use '@vue-ui-kit/ant/style.scss';
127
+
128
+ // 生产环境(编译后的独立文件)
129
+ @use '@vue-ui-kit/ant/dist/style.scss';
130
+ ```
131
+
132
+ ### 方法4: 在 style 标签中
133
+
134
+ ```vue
135
+ <style lang="scss">
136
+ @import '@vue-ui-kit/ant/style.scss';
137
+ </style>
138
+ ```
139
+
140
+ ### 方法5: Vite 配置导入
141
+
142
+ ```javascript
143
+ // vite.config.js
144
+ export default defineConfig({
145
+ css: {
146
+ preprocessorOptions: {
147
+ scss: {
148
+ additionalData: `@import '@vue-ui-kit/ant/dist/style.scss';`,
149
+ },
150
+ },
151
+ },
152
+ });
153
+ ```
154
+
155
+ ### 方法6: Webpack 配置
156
+
157
+ ```javascript
158
+ // webpack.config.js 或 vue.config.js
159
+ module.exports = {
160
+ css: {
161
+ loaderOptions: {
162
+ sass: {
163
+ additionalData: `@import '@vue-ui-kit/ant/dist/style.scss';`,
164
+ },
165
+ },
166
+ },
167
+ };
168
+ ```
169
+
170
+ ### 🔧 调试步骤
171
+
172
+ 1. **检查包版本**: 确保使用 `@vue-ui-kit/ant@1.8.4` 或更高版本
173
+ 2. **验证文件存在**: 检查 `node_modules/@vue-ui-kit/ant/dist/` 目录下是否有 `style.scss` 和 `style.css`
174
+ 3. **检查构建工具**: 确保您的构建工具支持处理 `.scss` 文件
175
+ 4. **清除缓存**: 删除 `node_modules` 和 lock 文件后重新安装
176
+
177
+ ### 📦 包内容确认
178
+
179
+ 安装后可以在以下位置找到样式文件:
180
+
181
+ ```
182
+ node_modules/@vue-ui-kit/ant/
183
+ ├── dist/
184
+ │ ├── style.css # 编译后的CSS
185
+ │ └── style.scss # SCSS源文件(已内联variables)
186
+ └── src/packages/styles/ # 源文件(开发时参考)
187
+ ```
188
+
189
+ ```tsx
190
+ /*kit.tsx*/
191
+ import UIKit from '@vue-ui-kit/ant';
192
+ import { TypographyParagraph, Switch } from 'ant-design-vue';
193
+
194
+ export const setupKit = () => {
195
+ /*Register $paragraph as cell renderer*/
196
+ UIKit.addRender('$paragraph', {
197
+ renderDefault({ props = {} }: RenderOptions, { row, field }: RenderTableParams) {
198
+ const content = props.getContent?.({ row, field }) ?? (valued(field) ? row[field!] : '');
199
+ return valued(field) ? (
200
+ <TypographyParagraph
201
+ {...merge({}, defaultProps.$paragraph, omit(props, ['content', 'getContent']))}
202
+ content={content}
203
+ />
204
+ ) : null;
205
+ },
206
+ });
207
+ /*Register $switch as form renderer*/
208
+ UIKit.addRender('$switch', {
209
+ renderItemContent(
210
+ { props = {}, events = {} }: RenderOptions,
211
+ { data, field }: RenderFormParams,
212
+ ) {
213
+ return valued(field) ? (
214
+ <Switch
215
+ v-model:checked={data[field!]}
216
+ {...props}
217
+ onChange={(...arg) => {
218
+ events.change?.({ data, field }, ...arg);
219
+ }}
220
+ />
221
+ ) : null;
222
+ },
223
+ });
224
+ };
225
+
226
+ /*
227
+ * Built-in renderers:
228
+ *
229
+ $input: Input,
230
+ AInput: Input,
231
+ $textarea: Textarea,
232
+ Textarea: Textarea,
233
+ $number: InputNumber,
234
+ AInputNumber: InputNumber,
235
+ $select: Select,
236
+ ASelect: Select,
237
+ $date: DatePicker,
238
+ ADatePicker: DatePicker,
239
+ $range: RangePicker,
240
+ ARangePicker: RangePicker,
241
+ AAutoComplete: AutoComplete,
242
+ $Cascader: Cascader,
243
+ ACascader: Cascader,
244
+ ACheckbox: Checkbox,
245
+ AMentions: Mentions,
246
+ ARate: Rate,
247
+ ASlider: Slider,
248
+ $time: TimePicker,
249
+ ATimePicker: TimePicker,
250
+ ATreeSelect: TreeSelect,
251
+ *
252
+ * */
253
+ ```
254
+
255
+ ## Requirements
256
+
257
+ - Node.js >= 16
258
+ - Vue >= 3.2.0
259
+ - ant-design-vue >= 4.0.0
260
+ - @ant-design/icons-vue >= 7.0.0
261
+
262
+ ## Components
263
+
264
+ ### PGrid
265
+
266
+ Enhanced data table component with integrated search form, pagination, and toolbar.
267
+
268
+ #### Basic Example
269
+
270
+ ```vue
271
+ <script setup lang="ts">
272
+ import { computed } from 'vue';
273
+ import { PGridProps, labelColDict } from '@vue-ui-kit/ant';
274
+
275
+ const gridSetting = computed<PGridProps<Student, { keyword?: string }>>(() => ({
276
+ columns: [
277
+ { field: 'name', title: 'Name', width: 200 },
278
+ { field: 'email', title: 'Email', width: 200 },
279
+ { field: 'age', title: 'Age', width: 100 },
280
+ ],
281
+ formConfig: {
282
+ items: [
283
+ {
284
+ field: 'keyword',
285
+ title: 'Keyword',
286
+ labelCol: labelColDict[3],
287
+ itemRender: {
288
+ name: '$input',
289
+ props: { placeholder: 'Search...' },
290
+ },
291
+ },
292
+ ],
293
+ },
294
+ proxyConfig: {
295
+ ajax: {
296
+ query: ({ form, page }) => api.getStudents({ ...form, ...page }),
297
+ },
298
+ },
299
+ }));
300
+ </script>
301
+
302
+ <template>
303
+ <p-grid v-bind="gridSetting" />
304
+ </template>
305
+ ```
306
+
307
+ ### PForm
308
+
309
+ Enhanced Form component with simplified configuration and dynamic fields.
310
+
311
+ #### Props
312
+
313
+ | Prop | Type | Description | Default |
314
+ | ----------- | ------------------ | ------------------------------ | -------------- |
315
+ | items | `PFormItemProps[]` | Form items configuration array | - |
316
+ | data | `T` | Form data object | - |
317
+ | customReset | `() => void` | Custom reset function | - |
318
+ | labelCol | `ColProps` | Label column layout | `{ span: 6 }` |
319
+ | wrapperCol | `ColProps` | Wrapper column layout | `{ span: 16 }` |
320
+ | ...others | `FormProps` | All ant-design-vue Form props | - |
321
+
322
+ #### Events
323
+
324
+ | Event | Description | Parameters |
325
+ | ----- | -------------------------------- | ----------------------- |
326
+ | apply | Triggered when form is submitted | `(formData: T) => void` |
327
+ | reset | Triggered when form is reset | `() => void` |
328
+
329
+ #### PFormItemProps
330
+
331
+ | Prop | Type | Description | Default |
332
+ | ------------- | ------------------------------- | -------------------------------- | -------- |
333
+ | field | `string` | Form field name | - |
334
+ | title | `string` | Field label | - |
335
+ | span | `number` | Grid span (1-24) | - |
336
+ | colon | `boolean` | Show colon after label | `true` |
337
+ | labelCol | `ColProps` | Label column layout | - |
338
+ | wrapperCol | `ColProps` | Wrapper column layout | - |
339
+ | forceRequired | `boolean` | Show required mark (visual only) | `false` |
340
+ | align | `'left' \| 'right' \| 'center'` | Text alignment | `'left'` |
341
+ | col | `ColProps` | Grid column properties | - |
342
+ | rule | `Rule[]` | Validation rules | - |
343
+ | itemRender | `ItemRender` | Field renderer configuration | - |
344
+ | tooltipConfig | `TooltipConfig` | Tooltip configuration | - |
345
+ | slots | `object` | Custom slot renderers | - |
346
+
347
+ #### Built-in Renderers
348
+
349
+ - `$input`: Input field
350
+ - `$textarea`: Textarea field
351
+ - `$number`: Number input
352
+ - `$select`: Select dropdown
353
+ - `$date`: Date picker
354
+ - `$range`: Date range picker
355
+ - `$time`: Time picker
356
+ - `ASwitch`: Switch component
357
+ - `ACheckbox`: Checkbox
358
+ - `ARate`: Rate component
359
+ - `ASlider`: Slider component
360
+
361
+ #### Basic Example
362
+
363
+ ```vue
364
+ <script setup lang="ts">
365
+ import { ref, computed } from 'vue';
366
+ import { PFormProps } from '@vue-ui-kit/ant';
367
+
368
+ interface UserForm {
369
+ name: string;
370
+ email: string;
371
+ age?: number;
372
+ gender: string;
373
+ skills: string[];
374
+ }
375
+
376
+ const formData = ref<UserForm>({
377
+ name: '',
378
+ email: '',
379
+ age: undefined,
380
+ gender: '',
381
+ skills: [],
382
+ });
383
+
384
+ const formSetting = computed<PFormProps<UserForm>>(() => ({
385
+ items: [
386
+ {
387
+ field: 'name',
388
+ title: 'Name',
389
+ span: 12,
390
+ rule: [{ required: true, message: 'Please enter name' }],
391
+ itemRender: {
392
+ name: '$input',
393
+ props: { placeholder: 'Enter name' },
394
+ },
395
+ },
396
+ {
397
+ field: 'email',
398
+ title: 'Email',
399
+ span: 12,
400
+ rule: [
401
+ { required: true, message: 'Please enter email' },
402
+ { type: 'email', message: 'Invalid email format' },
403
+ ],
404
+ itemRender: {
405
+ name: '$input',
406
+ props: { placeholder: 'Enter email' },
407
+ },
408
+ },
409
+ {
410
+ field: 'gender',
411
+ title: 'Gender',
412
+ span: 12,
413
+ rule: [{ required: true, message: 'Please select gender' }],
414
+ itemRender: {
415
+ name: '$select',
416
+ props: {
417
+ placeholder: 'Select gender',
418
+ options: [
419
+ { label: 'Male', value: 'male' },
420
+ { label: 'Female', value: 'female' },
421
+ ],
422
+ },
423
+ },
424
+ },
425
+ {
426
+ field: 'skills',
427
+ title: 'Skills',
428
+ span: 24,
429
+ itemRender: {
430
+ name: '$select',
431
+ props: {
432
+ mode: 'multiple',
433
+ placeholder: 'Select skills',
434
+ options: [
435
+ { label: 'Vue.js', value: 'vue' },
436
+ { label: 'React', value: 'react' },
437
+ { label: 'Angular', value: 'angular' },
438
+ ],
439
+ },
440
+ },
441
+ },
442
+ ],
443
+ }));
444
+
445
+ const handleSubmit = (data: UserForm) => {
446
+ console.log('Form submitted:', data);
447
+ };
448
+ </script>
449
+
450
+ <template>
451
+ <p-form v-bind="formSetting" :data="formData" @apply="handleSubmit" />
452
+ </template>
453
+ ```
454
+
455
+ #### Advanced Example with Custom Slots
456
+
457
+ ```vue
458
+ <script setup lang="tsx">
459
+ import { ref, computed } from 'vue';
460
+
461
+ const formData = ref({
462
+ name: '',
463
+ isActive: false,
464
+ });
465
+
466
+ const formSetting = computed(() => ({
467
+ items: [
468
+ {
469
+ field: 'isActive',
470
+ title: 'Status',
471
+ span: 24,
472
+ slots: {
473
+ default: ({ data }) => (
474
+ <a-switch
475
+ v-model:checked={data.isActive}
476
+ checkedChildren="Active"
477
+ unCheckedChildren="Inactive"
478
+ />
479
+ ),
480
+ },
481
+ },
482
+ ],
483
+ }));
484
+ </script>
485
+ ```
486
+
487
+ ### PFormGroup
488
+
489
+ Dynamic form group component for managing multiple form instances with add/remove capabilities.
490
+
491
+ #### Props
492
+
493
+ | Prop | Type | Description | Default |
494
+ | --- | --- | --- | --- |
495
+ | v-model | `Array<T & { __index: number }>` | Form group data array | `[]` |
496
+ | getFormSetting | `(data: T) => PFormProps<T>` | Function to get form configuration | - |
497
+ | title | `string` | Group title | - |
498
+ | tabLabel | `string` | Custom tab label template | - |
499
+ | editAble | `boolean` | Whether tabs are editable | `true` |
500
+ | showAdd | `boolean` | Show add button | `true` |
501
+ | lazyErrorMark | `boolean` | Lazy error marking | `false` |
502
+ | forceRender | `boolean` | Force render all tabs | `false` |
503
+ | keepSerial | `boolean` | Keep serial indexing | `false` |
504
+ | loading | `boolean` | Loading state | `false` |
505
+ | max | `number` | Maximum number of items | `Infinity` |
506
+ | itemMenus | `GroupMenuItem[]` | Custom menu items | Default copy/delete |
507
+ | createItem | `(opts: { list: T[] }) => Promise<T>` | Custom item creator | - |
508
+ | menuHandler | `GroupMenuItemHandler<T>` | Custom menu handler | - |
509
+
510
+ #### Exposed Methods
511
+
512
+ | Method | Description | Parameters | Returns |
513
+ | ------------ | ------------------------------- | ----------------- | --------------- |
514
+ | validateAll | Validate all form instances | - | `Promise<void>` |
515
+ | validate | Validate specific form instance | `__index: number` | `Promise<void>` |
516
+ | setActiveKey | Set active tab | `key: number` | `void` |
517
+
518
+ #### Basic Example
519
+
520
+ ```vue
521
+ <script setup lang="ts">
522
+ import { ref, computed } from 'vue';
523
+ import { PFormGroupProps } from '@vue-ui-kit/ant';
524
+
525
+ interface Project {
526
+ __index: number;
527
+ name: string;
528
+ startDate: string;
529
+ endDate: string;
530
+ budget?: number;
531
+ }
532
+
533
+ const projects = ref<Project[]>([
534
+ {
535
+ __index: 0,
536
+ name: 'Project A',
537
+ startDate: '',
538
+ endDate: '',
539
+ budget: undefined,
540
+ },
541
+ ]);
542
+
543
+ const groupSetting = computed<PFormGroupProps<Project>>(() => ({
544
+ title: 'Project Management',
545
+ showAdd: true,
546
+ max: 10,
547
+ getFormSetting: (data) => ({
548
+ items: [
549
+ {
550
+ field: 'name',
551
+ title: 'Project Name',
552
+ span: 24,
553
+ rule: [{ required: true, message: 'Please enter project name' }],
554
+ itemRender: {
555
+ name: '$input',
556
+ props: { placeholder: 'Enter project name' },
557
+ },
558
+ },
559
+ {
560
+ field: 'startDate',
561
+ title: 'Start Date',
562
+ span: 12,
563
+ rule: [{ required: true, message: 'Please select start date' }],
564
+ itemRender: {
565
+ name: '$date',
566
+ props: { placeholder: 'Select start date' },
567
+ },
568
+ },
569
+ {
570
+ field: 'endDate',
571
+ title: 'End Date',
572
+ span: 12,
573
+ rule: [{ required: true, message: 'Please select end date' }],
574
+ itemRender: {
575
+ name: '$date',
576
+ props: { placeholder: 'Select end date' },
577
+ },
578
+ },
579
+ {
580
+ field: 'budget',
581
+ title: 'Budget',
582
+ span: 24,
583
+ itemRender: {
584
+ name: '$number',
585
+ props: {
586
+ min: 0,
587
+ placeholder: 'Enter budget',
588
+ formatter: (value) => `$ ${value}`.replace(/\B(?=(\d{3})+(?!\d))/g, ','),
589
+ parser: (value) => value.replace(/\$\s?|(,*)/g, ''),
590
+ },
591
+ },
592
+ },
593
+ ],
594
+ }),
595
+ }));
596
+
597
+ const handleSave = async () => {
598
+ try {
599
+ await groupRef.value?.validateAll();
600
+ console.log('All projects saved:', projects.value);
601
+ } catch (error) {
602
+ console.error('Validation failed:', error);
603
+ }
604
+ };
605
+
606
+ const groupRef = ref();
607
+ </script>
608
+
609
+ <template>
610
+ <div>
611
+ <p-form-group ref="groupRef" v-model="projects" v-bind="groupSetting" />
612
+ <a-button type="primary" @click="handleSave"> Save All Projects </a-button>
613
+ </div>
614
+ </template>
615
+ ```
616
+
617
+ #### Advanced Example with Custom Menu
618
+
619
+ ```vue
620
+ <script setup lang="ts">
621
+ import { ref, computed } from 'vue';
622
+
623
+ const projects = ref([]);
624
+
625
+ const groupSetting = computed(() => ({
626
+ title: 'Advanced Project Management',
627
+ itemMenus: [
628
+ { content: 'Copy', code: 'copy' },
629
+ { content: 'Delete', code: 'delete' },
630
+ { content: 'Export', code: 'export' },
631
+ {
632
+ content: 'Archive',
633
+ code: 'archive',
634
+ visibleMethod: ({ data }) => data.status !== 'archived',
635
+ },
636
+ ],
637
+ createItem: async ({ list }) => {
638
+ return {
639
+ name: `Project ${list.length + 1}`,
640
+ status: 'active',
641
+ startDate: new Date().toISOString().split('T')[0],
642
+ };
643
+ },
644
+ menuHandler: ({ code, data, index }) => {
645
+ switch (code) {
646
+ case 'export':
647
+ exportProject(data);
648
+ break;
649
+ case 'archive':
650
+ archiveProject(data, index);
651
+ break;
652
+ }
653
+ },
654
+ getFormSetting: (data) => ({
655
+ // ... form configuration
656
+ }),
657
+ }));
658
+ </script>
659
+ ```
660
+
661
+ ## Utility Functions
662
+
663
+ ### labelColDict
664
+
665
+ Pre-defined label column configurations for different text lengths:
666
+
667
+ ```ts
668
+ import { labelColDict } from '@vue-ui-kit/ant';
669
+
670
+ // Usage in form items
671
+ {
672
+ field: 'shortField',
673
+ title: 'Name', // 2 characters
674
+ labelCol: labelColDict[2],
675
+ }
676
+
677
+ {
678
+ field: 'longerField',
679
+ title: 'Description', // 4+ characters
680
+ labelCol: labelColDict[4],
681
+ }
682
+ ```
683
+
684
+ ### Custom Renderers
685
+
686
+ Register custom form field renderers:
687
+
688
+ ```tsx
689
+ import UIKit from '@vue-ui-kit/ant';
690
+ import { Switch } from 'ant-design-vue';
691
+
692
+ UIKit.addRender('$switch', {
693
+ renderItemContent({ props = {}, events = {} }, { data, field }) {
694
+ return (
695
+ <Switch
696
+ v-model:checked={data[field]}
697
+ {...props}
698
+ onChange={(...args) => {
699
+ events.change?.({ data, field }, ...args);
700
+ }}
701
+ />
702
+ );
703
+ },
704
+ });
705
+ ```
706
+
707
+ ### Custom Formatters
708
+
709
+ Register custom cell formatters for PGrid:
710
+
711
+ ```ts
712
+ UIKit.addFormatter({
713
+ currency: ({ cellValue }) => `$${cellValue?.toLocaleString() || '0'}`,
714
+ percentage: ({ cellValue }) => `${(cellValue * 100).toFixed(2)}%`,
715
+ });
716
+ ```
717
+
718
+ ## FAQ
719
+
720
+ ### 1. When using computed + v-bind to generate dynamic tables + forms, form option updates cause table re-rendering. How to solve?
721
+
722
+ Answer: Separate the formConfig into another computed property
723
+
724
+ ```vue
725
+ <p-grid v-bind="gridSetting" :form-config="computedFormConfig" />
726
+ ```
727
+
728
+ ### 2. Does the table ajax currently only support multiDelete and table data fetching?
729
+
730
+ Answer: Edit details in most cases cannot correspond one-to-one with the table, so there's no development significance. Multi-row deletion needs to be used together with:
731
+
732
+ ```ts
733
+ selectConfig: {
734
+ multiple: true,
735
+ },
736
+ toolbarConfig: {
737
+ buttons: {
738
+ code: "multipleDelete"
739
+ }
740
+ }
741
+ ```
742
+
743
+ ### 3. Why is the computed approach recommended?
744
+
745
+ Answer: It simplifies all dynamic logic. For complex scenarios, you can try using reactive and maintain it yourself, but in most cases, computed is more convenient.
746
+
747
+ ## 🔤 TypeScript 类型使用
748
+
749
+ ### 基本类型导入
750
+
751
+ ```typescript
752
+ import {
753
+ PGridProps,
754
+ PFormProps,
755
+ PFormItemProps,
756
+ ColumnProps,
757
+ ToolbarConfig,
758
+ UIKitConfig,
759
+ } from '@vue-ui-kit/ant';
760
+ ```
761
+
762
+ ### PGrid 类型使用示例
763
+
764
+ ```typescript
765
+ import { computed } from 'vue';
766
+ import { PGridProps } from '@vue-ui-kit/ant';
767
+
768
+ // 定义数据类型
769
+ interface Student {
770
+ id: number;
771
+ name: string;
772
+ email: string;
773
+ age: number;
774
+ }
775
+
776
+ // 定义查询表单类型
777
+ interface StudentQuery {
778
+ keyword?: string;
779
+ age?: number;
780
+ }
781
+
782
+ // 使用PGridProps类型
783
+ const gridSetting = computed<PGridProps<Student, StudentQuery>>(() => ({
784
+ columns: [
785
+ { field: 'name', title: '姓名', width: 200 },
786
+ { field: 'email', title: '邮箱', width: 200 },
787
+ { field: 'age', title: '年龄', width: 100 },
788
+ ],
789
+ formConfig: {
790
+ items: [
791
+ {
792
+ field: 'keyword',
793
+ title: '关键字',
794
+ itemRender: {
795
+ name: '$input',
796
+ props: { placeholder: '请输入关键字' },
797
+ },
798
+ },
799
+ ],
800
+ },
801
+ proxyConfig: {
802
+ ajax: {
803
+ query: ({ form, page }) => api.getStudents({ ...form, ...page }),
804
+ },
805
+ },
806
+ }));
807
+ ```
808
+
809
+ ### PForm 类型使用示例
810
+
811
+ ```typescript
812
+ import { ref, computed } from 'vue';
813
+ import { PFormProps, PFormItemProps } from '@vue-ui-kit/ant';
814
+
815
+ // 定义表单数据类型
816
+ interface UserForm {
817
+ name: string;
818
+ email: string;
819
+ age?: number;
820
+ gender: string;
821
+ }
822
+
823
+ const formData = ref<UserForm>({
824
+ name: '',
825
+ email: '',
826
+ age: undefined,
827
+ gender: '',
828
+ });
829
+
830
+ // 使用PFormProps类型
831
+ const formSetting = computed<PFormProps<UserForm>>(() => ({
832
+ items: [
833
+ {
834
+ field: 'name',
835
+ title: '姓名',
836
+ rule: [{ required: true, message: '请输入姓名' }],
837
+ itemRender: {
838
+ name: '$input',
839
+ props: { placeholder: '请输入姓名' },
840
+ },
841
+ },
842
+ {
843
+ field: 'email',
844
+ title: '邮箱',
845
+ rule: [
846
+ { required: true, message: '请输入邮箱' },
847
+ { type: 'email', message: '邮箱格式不正确' },
848
+ ],
849
+ itemRender: {
850
+ name: '$input',
851
+ props: { placeholder: '请输入邮箱' },
852
+ },
853
+ },
854
+ ] as PFormItemProps<UserForm>[],
855
+ }));
856
+ ```
857
+
858
+ ### 高级类型使用
859
+
860
+ ```typescript
861
+ // 1. 自定义列类型
862
+ import { ColumnProps } from '@vue-ui-kit/ant';
863
+
864
+ const customColumns: ColumnProps<Student>[] = [
865
+ {
866
+ field: 'name',
867
+ title: '姓名',
868
+ formatter: (arg) => arg.cellValue?.toUpperCase(),
869
+ },
870
+ {
871
+ field: 'actions',
872
+ title: '操作',
873
+ slots: {
874
+ default: ({ row, rowIndex }) => (
875
+ <button onClick={() => handleEdit(row)}>编辑</button>
876
+ ),
877
+ },
878
+ },
879
+ ];
880
+
881
+ // 2. 工具栏配置类型
882
+ import { ToolbarConfig } from '@vue-ui-kit/ant';
883
+
884
+ const toolbarConfig: ToolbarConfig = {
885
+ buttons: [
886
+ {
887
+ code: 'add',
888
+ content: '新增',
889
+ type: 'primary',
890
+ icon: 'PlusOutlined',
891
+ },
892
+ ],
893
+ tools: [
894
+ {
895
+ code: 'refresh',
896
+ icon: 'ReloadOutlined',
897
+ },
898
+ ],
899
+ };
900
+
901
+ // 3. Setup配置类型
902
+ import { UIKitConfig } from '@vue-ui-kit/ant';
903
+
904
+ const kitConfig: UIKitConfig = {
905
+ form: {
906
+ labelCol: { span: 8 },
907
+ wrapperCol: { span: 14 },
908
+ },
909
+ grid: {
910
+ align: 'center',
911
+ lazyReset: true,
912
+ fitHeight: 200,
913
+ },
914
+ };
915
+ ```
916
+
917
+ ### 组件实例类型
918
+
919
+ ```typescript
920
+ import { ref } from 'vue';
921
+ import { PGridInstance, PFormInstance } from '@vue-ui-kit/ant';
922
+
923
+ // PGrid 组件实例
924
+ const gridRef = ref<PGridInstance<Student, StudentQuery>>();
925
+
926
+ // 调用实例方法
927
+ gridRef.value?.commitProxy.reload();
928
+ gridRef.value?.resizeTable();
929
+
930
+ // PForm 组件实例
931
+ const formRef = ref<PFormInstance>();
932
+
933
+ // 调用实例方法
934
+ formRef.value?.reset();
935
+ formRef.value?.$form.validateFields();
936
+ ```
937
+
938
+ ## TypeScript Support
939
+
940
+ This library is fully written in TypeScript and provides comprehensive type definitions:
941
+
942
+ ```ts
943
+ import type {
944
+ PFormProps,
945
+ PFormItemProps,
946
+ PFormGroupProps,
947
+ PGridProps,
948
+ ColumnProps,
949
+ } from '@vue-ui-kit/ant';
950
+ ```
951
+
952
+ ## License
953
+
954
+ MIT
955
+
956
+ ### 🛠️ 工具方法使用
957
+
958
+ #### 从npm包导入工具方法
959
+
960
+ ```typescript
961
+ import {
962
+ // 响应式布局工具
963
+ getButtonResponsive,
964
+ defaultItemResponsive,
965
+ defaultLabelCol,
966
+ labelColDict,
967
+ get24rest,
968
+
969
+ // 表格工具
970
+ cleanCol,
971
+
972
+ // 其他工具
973
+ UIKitConfig,
974
+ setup,
975
+ } from '@vue-ui-kit/ant';
976
+ ```
977
+
978
+ #### getButtonResponsive 使用示例
979
+
980
+ ```typescript
981
+ import { getButtonResponsive, defaultItemResponsive } from '@vue-ui-kit/ant';
982
+
983
+ // 方式1: 根据表单项数量自动计算按钮响应式布局
984
+ const buttonSpan1 = getButtonResponsive(3); // 3个表单项
985
+ console.log(buttonSpan1);
986
+ // 结果: { xs: 24, sm: 24, md: 24, lg: 0, xl: 0, xxl: 6 }
987
+
988
+ // 方式2: 传入自定义响应式配置数组
989
+ const customResponsive = [
990
+ { xs: 24, sm: 12, md: 8, lg: 6, xl: 6, xxl: 6 },
991
+ { xs: 24, sm: 12, md: 8, lg: 6, xl: 6, xxl: 6 },
992
+ ];
993
+ const buttonSpan2 = getButtonResponsive(customResponsive);
994
+ console.log(buttonSpan2);
995
+ // 计算剩余空间作为按钮区域
996
+ ```
997
+
998
+ #### labelColDict 使用示例
999
+
1000
+ ```typescript
1001
+ import { labelColDict } from '@vue-ui-kit/ant';
1002
+
1003
+ // 根据标签字符数获取合适的labelCol配置
1004
+ const labelCol = labelColDict[4]; // 4个字符的标签
1005
+ console.log(labelCol);
1006
+ // 结果: { xs: 12, sm: 4, md: 6, lg: 9, xl: 7, xxl: 6 }
1007
+
1008
+ // 在表单配置中使用
1009
+ const formSetting = computed(() => ({
1010
+ items: [
1011
+ {
1012
+ field: 'username',
1013
+ title: '用户名称', // 4个字符
1014
+ labelCol: labelColDict[4],
1015
+ itemRender: {
1016
+ name: '$input',
1017
+ props: { placeholder: '请输入用户名称' },
1018
+ },
1019
+ },
1020
+ ],
1021
+ }));
1022
+ ```
1023
+
1024
+ #### 响应式布局工具完整示例
1025
+
1026
+ ```typescript
1027
+ import { getButtonResponsive, defaultItemResponsive, labelColDict } from '@vue-ui-kit/ant';
1028
+
1029
+ // 创建一个动态表单布局计算器
1030
+ function createFormLayout(items: Array<{ title: string }>) {
1031
+ return {
1032
+ // 表单项布局
1033
+ items: items.map((item) => ({
1034
+ ...item,
1035
+ col: defaultItemResponsive, // 使用默认响应式布局
1036
+ labelCol: labelColDict[item.title.length] || labelColDict[4], // 根据标题长度选择
1037
+ })),
1038
+
1039
+ // 按钮区域布局
1040
+ buttonCol: getButtonResponsive(items.length),
1041
+ };
1042
+ }
1043
+
1044
+ // 使用示例
1045
+ const layout = createFormLayout([{ title: '姓名' }, { title: '邮箱地址' }, { title: '手机号码' }]);
1046
+
1047
+ console.log('按钮布局:', layout.buttonCol);
1048
+ // 自动计算最后一行剩余空间给按钮使用
1049
+ ```
1050
+
1051
+ #### cleanCol 表格列处理
1052
+
1053
+ ```typescript
1054
+ import { cleanCol, type ColumnProps } from '@vue-ui-kit/ant';
1055
+
1056
+ // 自定义列配置
1057
+ const customColumn: ColumnProps<User> = {
1058
+ field: 'name',
1059
+ title: '用户名',
1060
+ formatter: 'capitalize',
1061
+ cellRender: {
1062
+ name: '$link',
1063
+ props: { href: '#' },
1064
+ },
1065
+ slots: {
1066
+ default: ({ row }) => <span>{row.name}</span>,
1067
+ },
1068
+ };
1069
+
1070
+ // 清理列配置,移除UI Kit特有属性,转换为Ant Design Vue标准格式
1071
+ const antColumn = cleanCol(customColumn);
1072
+ console.log(antColumn);
1073
+ // 结果会移除 formatter, cellRender, slots 等UI Kit特有属性
1074
+ // 保留 Ant Design Vue 原生支持的属性
1075
+ ```
1076
+
1077
+ #### get24rest 栅格计算
1078
+
1079
+ ```typescript
1080
+ import { get24rest } from '@vue-ui-kit/ant';
1081
+
1082
+ // 计算24栅格系统中的剩余空间
1083
+ const usedSpans = [6, 8, 4]; // 已使用的栅格数
1084
+ const restSpan = get24rest(usedSpans);
1085
+ console.log(restSpan); // 结果: 6 (24 - 6 - 8 - 4 = 6)
1086
+
1087
+ // 在表单中动态计算按钮位置
1088
+ const formItems = [
1089
+ { span: 8 }, // 表单项1
1090
+ { span: 8 }, // 表单项2
1091
+ { span: 6 }, // 表单项3
1092
+ ];
1093
+ const buttonSpan = get24rest(formItems.map((item) => item.span));
1094
+ // 按钮将占据剩余的2格空间,如果小于等于1则占满整行(24格)
1095
+ ```
1096
+
1097
+ #### 在Vue组件中使用
1098
+
1099
+ ```vue
1100
+ <template>
1101
+ <div>
1102
+ <a-row :gutter="[16, 16]">
1103
+ <!-- 动态表单项 -->
1104
+ <a-col v-for="(item, index) in formItems" :key="index" v-bind="defaultItemResponsive">
1105
+ <a-form-item :label="item.label" :label-col="getLabelCol(item.label)">
1106
+ <a-input v-model:value="item.value" />
1107
+ </a-form-item>
1108
+ </a-col>
1109
+
1110
+ <!-- 动态按钮区域 -->
1111
+ <a-col v-bind="buttonLayout">
1112
+ <a-button type="primary">提交</a-button>
1113
+ <a-button>重置</a-button>
1114
+ </a-col>
1115
+ </a-row>
1116
+ </div>
1117
+ </template>
1118
+
1119
+ <script setup lang="ts">
1120
+ import { computed } from 'vue';
1121
+ import { getButtonResponsive, defaultItemResponsive, labelColDict } from '@vue-ui-kit/ant';
1122
+
1123
+ const formItems = ref([
1124
+ { label: '姓名', value: '' },
1125
+ { label: '电子邮箱', value: '' },
1126
+ { label: '联系电话', value: '' },
1127
+ ]);
1128
+
1129
+ // 动态计算按钮布局
1130
+ const buttonLayout = computed(() => getButtonResponsive(formItems.value.length));
1131
+
1132
+ // 根据标签长度获取合适的labelCol
1133
+ const getLabelCol = (label: string) => labelColDict[label.length] || labelColDict[4];
1134
+ </script>
1135
+ ```
1136
+
1137
+ ## 📦 组件和工具导入
1138
+
1139
+ ### ✅ 正确的导入方式
1140
+
1141
+ ```typescript
1142
+ // 方式1: 分别导入 (推荐)
1143
+ import {
1144
+ // 组件
1145
+ PForm,
1146
+ PGrid,
1147
+ PFormGroup,
1148
+ PGroupBlock,
1149
+ PromisePicker,
1150
+
1151
+ // 类型
1152
+ PGridProps,
1153
+ PFormProps,
1154
+ PFormItemProps,
1155
+ ColumnProps,
1156
+ UIKitConfig,
1157
+
1158
+ // 工具方法
1159
+ getButtonResponsive,
1160
+ labelColDict,
1161
+ defaultItemResponsive,
1162
+ get24rest,
1163
+ cleanCol,
1164
+
1165
+ // 配置方法
1166
+ setup,
1167
+ addFormatter,
1168
+ addRender,
1169
+ } from '@vue-ui-kit/ant';
1170
+
1171
+ // 方式2: 默认导入 + 命名导入
1172
+ import UIKit, { PForm, PGrid, setup } from '@vue-ui-kit/ant';
1173
+ ```
1174
+
1175
+ ### 🔧 完整使用示例
1176
+
1177
+ ```vue
1178
+ <template>
1179
+ <div class="demo-page">
1180
+ <!-- PForm 组件 -->
1181
+ <PForm
1182
+ ref="formRef"
1183
+ v-bind="formSetting"
1184
+ :data="formData"
1185
+ @apply="handleFormSubmit"
1186
+ @reset="handleFormReset"
1187
+ />
1188
+
1189
+ <!-- PGrid 组件 -->
1190
+ <PGrid ref="gridRef" v-bind="gridSetting" @toolbar-button-click="handleToolbarClick" />
1191
+ </div>
1192
+ </template>
1193
+
1194
+ <script setup lang="ts">
1195
+ import { ref, computed } from 'vue';
1196
+ import {
1197
+ // 直接导入组件
1198
+ PForm,
1199
+ PGrid,
1200
+
1201
+ // 导入类型
1202
+ PFormProps,
1203
+ PGridProps,
1204
+ PFormInstance,
1205
+ PGridInstance,
1206
+
1207
+ // 导入工具方法
1208
+ getButtonResponsive,
1209
+ labelColDict,
1210
+
1211
+ // 导入配置方法
1212
+ setup,
1213
+ } from '@vue-ui-kit/ant';
1214
+
1215
+ // 配置全局默认值
1216
+ setup({
1217
+ grid: {
1218
+ align: 'center',
1219
+ fitHeight: 180,
1220
+ },
1221
+ form: {
1222
+ labelCol: labelColDict[4],
1223
+ },
1224
+ });
1225
+
1226
+ // 类型定义
1227
+ interface User {
1228
+ id: number;
1229
+ name: string;
1230
+ email: string;
1231
+ }
1232
+
1233
+ interface UserForm {
1234
+ name: string;
1235
+ email: string;
1236
+ }
1237
+
1238
+ interface UserQuery {
1239
+ keyword?: string;
1240
+ }
1241
+
1242
+ // 组件引用
1243
+ const formRef = ref<PFormInstance>();
1244
+ const gridRef = ref<PGridInstance<User, UserQuery>>();
1245
+
1246
+ // 表单数据
1247
+ const formData = ref<UserForm>({
1248
+ name: '',
1249
+ email: '',
1250
+ });
1251
+
1252
+ // 表单配置
1253
+ const formSetting = computed<PFormProps<UserForm>>(() => ({
1254
+ items: [
1255
+ {
1256
+ field: 'name',
1257
+ title: '用户名',
1258
+ labelCol: labelColDict[3], // 3个字符的标签
1259
+ rule: [{ required: true, message: '请输入用户名' }],
1260
+ itemRender: {
1261
+ name: '$input',
1262
+ props: { placeholder: '请输入用户名' },
1263
+ },
1264
+ },
1265
+ {
1266
+ field: 'email',
1267
+ title: '邮箱地址',
1268
+ labelCol: labelColDict[4], // 4个字符的标签
1269
+ rule: [
1270
+ { required: true, message: '请输入邮箱' },
1271
+ { type: 'email', message: '邮箱格式不正确' },
1272
+ ],
1273
+ itemRender: {
1274
+ name: '$input',
1275
+ props: { placeholder: '请输入邮箱地址' },
1276
+ },
1277
+ },
1278
+ ],
1279
+ }));
1280
+
1281
+ // 网格配置
1282
+ const gridSetting = computed<PGridProps<User, UserQuery>>(() => ({
1283
+ columns: [
1284
+ { field: 'name', title: '用户名', width: 200 },
1285
+ { field: 'email', title: '邮箱', width: 200 },
1286
+ ],
1287
+ formConfig: {
1288
+ items: [
1289
+ {
1290
+ field: 'keyword',
1291
+ title: '关键字',
1292
+ labelCol: labelColDict[3],
1293
+ itemRender: {
1294
+ name: '$input',
1295
+ props: { placeholder: '请输入关键字' },
1296
+ },
1297
+ },
1298
+ ],
1299
+ },
1300
+ toolbarConfig: {
1301
+ buttons: [
1302
+ {
1303
+ code: 'add',
1304
+ content: '新增',
1305
+ type: 'primary',
1306
+ icon: 'PlusOutlined',
1307
+ },
1308
+ ],
1309
+ },
1310
+ proxyConfig: {
1311
+ ajax: {
1312
+ query: ({ form, page }) => api.getUsers({ ...form, ...page }),
1313
+ },
1314
+ },
1315
+ }));
1316
+
1317
+ // 事件处理
1318
+ const handleFormSubmit = (data: UserForm) => {
1319
+ console.log('表单提交:', data);
1320
+ };
1321
+
1322
+ const handleFormReset = () => {
1323
+ console.log('表单重置');
1324
+ };
1325
+
1326
+ const handleToolbarClick = ({ code, data, selectedKeys }) => {
1327
+ console.log('工具栏点击:', { code, data, selectedKeys });
1328
+
1329
+ if (code === 'add') {
1330
+ // 处理新增逻辑
1331
+ }
1332
+ };
1333
+
1334
+ // 使用工具方法
1335
+ const buttonLayout = computed(
1336
+ () => getButtonResponsive(2), // 根据表单项数量计算按钮布局
1337
+ );
1338
+ </script>
1339
+ ```
1340
+
1341
+ ### 🚨 常见导入错误
1342
+
1343
+ ```typescript
1344
+ // 错误:这样无法导入组件
1345
+ import { PForm, PGrid } from '@vue-ui-kit/ant/components';
1346
+
1347
+ // ❌ 错误:组件不能从utils路径导入
1348
+ import { PForm } from '@vue-ui-kit/ant/utils';
1349
+
1350
+ // ❌ 错误:混合默认导入和组件导入
1351
+ import UIKit, PForm from '@vue-ui-kit/ant'; // 语法错误
1352
+
1353
+ // ✅ 正确:从主包路径导入所有内容
1354
+ import {
1355
+ PForm,
1356
+ PGrid,
1357
+ getButtonResponsive,
1358
+ labelColDict
1359
+ } from '@vue-ui-kit/ant';
1360
+ ```
1361
+
1362
+ ### 📋 可导入的完整列表
1363
+
1364
+ #### 组件
1365
+
1366
+ - `PForm` - 增强表单组件
1367
+ - `PGrid` - 增强数据表格组件
1368
+ - `PFormGroup` - 动态表单组组件
1369
+ - `PGroupBlock` - 表单块组件
1370
+ - `PromisePicker` - 数据选择器组件
1371
+
1372
+ #### 类型定义
1373
+
1374
+ - `PFormProps<T>` - 表单属性类型
1375
+ - `PGridProps<D, F>` - 网格属性类型
1376
+ - `PFormInstance` - 表单实例类型
1377
+ - `PGridInstance<D, F>` - 网格实例类型
1378
+ - `ColumnProps<T>` - 列配置类型
1379
+ - `UIKitConfig` - 全局配置类型
1380
+
1381
+ #### 工具方法
1382
+
1383
+ - `getButtonResponsive()` - 计算按钮响应式布局
1384
+ - `labelColDict` - 标签宽度配置字典
1385
+ - `defaultItemResponsive` - 默认响应式配置
1386
+ - `get24rest()` - 栅格剩余空间计算
1387
+ - `cleanCol()` - 清理列配置
1388
+
1389
+ #### 配置方法
1390
+
1391
+ - `setup()` - 全局配置设置
1392
+ - `addFormatter()` - 添加格式化器
1393
+ - `addRender()` - 添加渲染器