@vue-ui-kit/ant 1.8.5 → 1.8.7
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 +934 -308
- package/dist/cjs/index.js +1 -1
- package/dist/declarations/antProxy.d.ts +3 -4
- package/dist/es/index.js +11 -12
- package/dist/index.d.ts +8 -0
- package/dist/packages/store/renderStore.d.ts +4 -4
- package/package.json +1 -1
- package/src/declarations/antProxy.ts +15 -10
- package/src/index.ts +7 -8
- package/src/packages/store/renderStore.tsx +125 -118
- package/src/packages/utils/treeHelper.ts +62 -72
package/README.md
CHANGED
|
@@ -50,14 +50,14 @@ import '@vue-ui-kit/ant/style.scss';
|
|
|
50
50
|
// Configure global defaults
|
|
51
51
|
setup({
|
|
52
52
|
form: {
|
|
53
|
-
labelCol: { span: 8 },
|
|
53
|
+
labelCol: { span: 8 }, // Modify form label column width
|
|
54
54
|
wrapperCol: { span: 14 }, // Modify form input column width
|
|
55
55
|
},
|
|
56
56
|
grid: {
|
|
57
|
-
align: 'center',
|
|
58
|
-
lazyReset: true,
|
|
59
|
-
fitHeight: 200,
|
|
60
|
-
}
|
|
57
|
+
align: 'center', // Set default table alignment
|
|
58
|
+
lazyReset: true, // Don't auto-submit after reset
|
|
59
|
+
fitHeight: 200, // Set adaptive height
|
|
60
|
+
},
|
|
61
61
|
});
|
|
62
62
|
|
|
63
63
|
createApp(App).use(Antd).use(UIKit).mount('#app');
|
|
@@ -78,6 +78,7 @@ require('@vue-ui-kit/ant/style.css');
|
|
|
78
78
|
```
|
|
79
79
|
|
|
80
80
|
### 4. For Vite users
|
|
81
|
+
|
|
81
82
|
If using Vite, make sure your vite.config.js includes sass support:
|
|
82
83
|
|
|
83
84
|
```js
|
|
@@ -86,11 +87,11 @@ export default {
|
|
|
86
87
|
css: {
|
|
87
88
|
preprocessorOptions: {
|
|
88
89
|
scss: {
|
|
89
|
-
additionalData: `@import '@vue-ui-kit/ant/dist/style.scss'
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
}
|
|
90
|
+
additionalData: `@import '@vue-ui-kit/ant/dist/style.scss';`,
|
|
91
|
+
},
|
|
92
|
+
},
|
|
93
|
+
},
|
|
94
|
+
};
|
|
94
95
|
```
|
|
95
96
|
|
|
96
97
|
## 🎯 样式文件导入 - 故障排除指南
|
|
@@ -98,6 +99,7 @@ export default {
|
|
|
98
99
|
如果遇到样式文件导入问题,请按顺序尝试以下方法:
|
|
99
100
|
|
|
100
101
|
### 方法1: 标准导入(推荐)
|
|
102
|
+
|
|
101
103
|
```typescript
|
|
102
104
|
import '@vue-ui-kit/ant/style.scss';
|
|
103
105
|
// 或
|
|
@@ -105,13 +107,15 @@ import '@vue-ui-kit/ant/style.css';
|
|
|
105
107
|
```
|
|
106
108
|
|
|
107
109
|
### 方法2: 完整路径导入
|
|
108
|
-
|
|
110
|
+
|
|
111
|
+
```typescript
|
|
109
112
|
import '@vue-ui-kit/ant/dist/style.scss';
|
|
110
113
|
// 或
|
|
111
114
|
import '@vue-ui-kit/ant/dist/style.css';
|
|
112
115
|
```
|
|
113
116
|
|
|
114
117
|
### 方法3: SCSS @use 语法
|
|
118
|
+
|
|
115
119
|
```scss
|
|
116
120
|
@use '@vue-ui-kit/ant/style.scss';
|
|
117
121
|
// 或者使用完整路径
|
|
@@ -119,38 +123,41 @@ import '@vue-ui-kit/ant/dist/style.css';
|
|
|
119
123
|
```
|
|
120
124
|
|
|
121
125
|
### 方法4: 在 style 标签中
|
|
126
|
+
|
|
122
127
|
```vue
|
|
123
128
|
<style lang="scss">
|
|
124
|
-
@import '@vue-ui-kit/ant/style.scss';
|
|
129
|
+
@import '@vue-ui-kit/ant/style.scss';
|
|
125
130
|
</style>
|
|
126
131
|
```
|
|
127
132
|
|
|
128
133
|
### 方法5: Vite 配置导入
|
|
134
|
+
|
|
129
135
|
```javascript
|
|
130
136
|
// vite.config.js
|
|
131
137
|
export default defineConfig({
|
|
132
138
|
css: {
|
|
133
139
|
preprocessorOptions: {
|
|
134
140
|
scss: {
|
|
135
|
-
additionalData: `@import '@vue-ui-kit/ant/dist/style.scss'
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
})
|
|
141
|
+
additionalData: `@import '@vue-ui-kit/ant/dist/style.scss';`,
|
|
142
|
+
},
|
|
143
|
+
},
|
|
144
|
+
},
|
|
145
|
+
});
|
|
140
146
|
```
|
|
141
147
|
|
|
142
148
|
### 方法6: Webpack 配置
|
|
149
|
+
|
|
143
150
|
```javascript
|
|
144
151
|
// webpack.config.js 或 vue.config.js
|
|
145
152
|
module.exports = {
|
|
146
153
|
css: {
|
|
147
154
|
loaderOptions: {
|
|
148
155
|
sass: {
|
|
149
|
-
additionalData: `@import '@vue-ui-kit/ant/dist/style.scss'
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
}
|
|
156
|
+
additionalData: `@import '@vue-ui-kit/ant/dist/style.scss';`,
|
|
157
|
+
},
|
|
158
|
+
},
|
|
159
|
+
},
|
|
160
|
+
};
|
|
154
161
|
```
|
|
155
162
|
|
|
156
163
|
### 🔧 调试步骤
|
|
@@ -161,7 +168,9 @@ module.exports = {
|
|
|
161
168
|
4. **清除缓存**: 删除 `node_modules` 和 lock 文件后重新安装
|
|
162
169
|
|
|
163
170
|
### 📦 包内容确认
|
|
171
|
+
|
|
164
172
|
安装后可以在以下位置找到样式文件:
|
|
173
|
+
|
|
165
174
|
```
|
|
166
175
|
node_modules/@vue-ui-kit/ant/
|
|
167
176
|
├── dist/
|
|
@@ -253,34 +262,34 @@ Enhanced data table component with integrated search form, pagination, and toolb
|
|
|
253
262
|
|
|
254
263
|
```vue
|
|
255
264
|
<script setup lang="ts">
|
|
256
|
-
import { computed } from 'vue';
|
|
257
|
-
import { PGridProps, labelColDict } from '@vue-ui-kit/ant';
|
|
258
|
-
|
|
259
|
-
const gridSetting = computed<PGridProps<Student, { keyword?: string }>>(() => ({
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
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
|
+
},
|
|
274
284
|
},
|
|
285
|
+
],
|
|
286
|
+
},
|
|
287
|
+
proxyConfig: {
|
|
288
|
+
ajax: {
|
|
289
|
+
query: ({ form, page }) => api.getStudents({ ...form, ...page }),
|
|
275
290
|
},
|
|
276
|
-
],
|
|
277
|
-
},
|
|
278
|
-
proxyConfig: {
|
|
279
|
-
ajax: {
|
|
280
|
-
query: ({ form, page }) => api.getStudents({ ...form, ...page }),
|
|
281
291
|
},
|
|
282
|
-
}
|
|
283
|
-
}));
|
|
292
|
+
}));
|
|
284
293
|
</script>
|
|
285
294
|
|
|
286
295
|
<template>
|
|
@@ -294,44 +303,44 @@ Enhanced Form component with simplified configuration and dynamic fields.
|
|
|
294
303
|
|
|
295
304
|
#### Props
|
|
296
305
|
|
|
297
|
-
| Prop
|
|
298
|
-
|
|
|
299
|
-
| items
|
|
300
|
-
| data
|
|
301
|
-
| customReset | `() => void`
|
|
302
|
-
| labelCol
|
|
303
|
-
| wrapperCol
|
|
304
|
-
| ...others
|
|
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 | - |
|
|
305
314
|
|
|
306
315
|
#### Events
|
|
307
316
|
|
|
308
|
-
| Event | Description
|
|
309
|
-
|
|
|
317
|
+
| Event | Description | Parameters |
|
|
318
|
+
| ----- | -------------------------------- | ----------------------- |
|
|
310
319
|
| apply | Triggered when form is submitted | `(formData: T) => void` |
|
|
311
|
-
| reset | Triggered when form is reset
|
|
320
|
+
| reset | Triggered when form is reset | `() => void` |
|
|
312
321
|
|
|
313
322
|
#### PFormItemProps
|
|
314
323
|
|
|
315
|
-
| Prop
|
|
316
|
-
|
|
|
317
|
-
| field
|
|
318
|
-
| title
|
|
319
|
-
| span
|
|
320
|
-
| colon
|
|
321
|
-
| labelCol
|
|
322
|
-
| wrapperCol
|
|
323
|
-
| forceRequired | `boolean`
|
|
324
|
-
| align
|
|
325
|
-
| col
|
|
326
|
-
| rule
|
|
327
|
-
| itemRender
|
|
328
|
-
| tooltipConfig | `TooltipConfig`
|
|
329
|
-
| slots
|
|
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 | - |
|
|
330
339
|
|
|
331
340
|
#### Built-in Renderers
|
|
332
341
|
|
|
333
342
|
- `$input`: Input field
|
|
334
|
-
- `$textarea`: Textarea field
|
|
343
|
+
- `$textarea`: Textarea field
|
|
335
344
|
- `$number`: Number input
|
|
336
345
|
- `$select`: Select dropdown
|
|
337
346
|
- `$date`: Date picker
|
|
@@ -346,97 +355,93 @@ Enhanced Form component with simplified configuration and dynamic fields.
|
|
|
346
355
|
|
|
347
356
|
```vue
|
|
348
357
|
<script setup lang="ts">
|
|
349
|
-
import { ref, computed } from 'vue';
|
|
350
|
-
import { PFormProps } from '@vue-ui-kit/ant';
|
|
351
|
-
|
|
352
|
-
interface UserForm {
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
}
|
|
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
|
+
}
|
|
359
368
|
|
|
360
|
-
const formData = ref<UserForm>({
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
});
|
|
369
|
+
const formData = ref<UserForm>({
|
|
370
|
+
name: '',
|
|
371
|
+
email: '',
|
|
372
|
+
age: undefined,
|
|
373
|
+
gender: '',
|
|
374
|
+
skills: [],
|
|
375
|
+
});
|
|
367
376
|
|
|
368
|
-
const formSetting = computed<PFormProps<UserForm>>(() => ({
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
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
|
+
},
|
|
378
388
|
},
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
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
|
+
},
|
|
391
401
|
},
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
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
|
+
},
|
|
406
416
|
},
|
|
407
417
|
},
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
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
|
+
},
|
|
423
433
|
},
|
|
424
434
|
},
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
}));
|
|
435
|
+
],
|
|
436
|
+
}));
|
|
428
437
|
|
|
429
|
-
const handleSubmit = (data: UserForm) => {
|
|
430
|
-
|
|
431
|
-
};
|
|
438
|
+
const handleSubmit = (data: UserForm) => {
|
|
439
|
+
console.log('Form submitted:', data);
|
|
440
|
+
};
|
|
432
441
|
</script>
|
|
433
442
|
|
|
434
443
|
<template>
|
|
435
|
-
<p-form
|
|
436
|
-
v-bind="formSetting"
|
|
437
|
-
:data="formData"
|
|
438
|
-
@apply="handleSubmit"
|
|
439
|
-
/>
|
|
444
|
+
<p-form v-bind="formSetting" :data="formData" @apply="handleSubmit" />
|
|
440
445
|
</template>
|
|
441
446
|
```
|
|
442
447
|
|
|
@@ -444,31 +449,31 @@ const handleSubmit = (data: UserForm) => {
|
|
|
444
449
|
|
|
445
450
|
```vue
|
|
446
451
|
<script setup lang="tsx">
|
|
447
|
-
import { ref, computed } from 'vue';
|
|
452
|
+
import { ref, computed } from 'vue';
|
|
448
453
|
|
|
449
|
-
const formData = ref({
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
});
|
|
454
|
+
const formData = ref({
|
|
455
|
+
name: '',
|
|
456
|
+
isActive: false,
|
|
457
|
+
});
|
|
453
458
|
|
|
454
|
-
const formSetting = computed(() => ({
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
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
|
+
},
|
|
468
474
|
},
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
}));
|
|
475
|
+
],
|
|
476
|
+
}));
|
|
472
477
|
</script>
|
|
473
478
|
```
|
|
474
479
|
|
|
@@ -497,113 +502,107 @@ Dynamic form group component for managing multiple form instances with add/remov
|
|
|
497
502
|
|
|
498
503
|
#### Exposed Methods
|
|
499
504
|
|
|
500
|
-
| Method
|
|
501
|
-
|
|
|
502
|
-
| validateAll
|
|
503
|
-
| validate
|
|
504
|
-
| setActiveKey | Set active tab
|
|
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` |
|
|
505
510
|
|
|
506
511
|
#### Basic Example
|
|
507
512
|
|
|
508
513
|
```vue
|
|
509
514
|
<script setup lang="ts">
|
|
510
|
-
import { ref, computed } from 'vue';
|
|
511
|
-
import { PFormGroupProps } from '@vue-ui-kit/ant';
|
|
512
|
-
|
|
513
|
-
interface Project {
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
}
|
|
520
|
-
|
|
521
|
-
const projects = ref<Project[]>([
|
|
522
|
-
{
|
|
523
|
-
__index: 0,
|
|
524
|
-
name: 'Project A',
|
|
525
|
-
startDate: '',
|
|
526
|
-
endDate: '',
|
|
527
|
-
budget: undefined,
|
|
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;
|
|
528
524
|
}
|
|
529
|
-
]);
|
|
530
525
|
|
|
531
|
-
const
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
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
|
+
},
|
|
545
551
|
},
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
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
|
+
},
|
|
555
561
|
},
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
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
|
+
},
|
|
565
571
|
},
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
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
|
+
},
|
|
578
584
|
},
|
|
579
585
|
},
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
})
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
}
|
|
592
|
-
};
|
|
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
|
+
};
|
|
593
598
|
|
|
594
|
-
const groupRef = ref();
|
|
599
|
+
const groupRef = ref();
|
|
595
600
|
</script>
|
|
596
601
|
|
|
597
602
|
<template>
|
|
598
603
|
<div>
|
|
599
|
-
<p-form-group
|
|
600
|
-
|
|
601
|
-
v-model="projects"
|
|
602
|
-
v-bind="groupSetting"
|
|
603
|
-
/>
|
|
604
|
-
<a-button type="primary" @click="handleSave">
|
|
605
|
-
Save All Projects
|
|
606
|
-
</a-button>
|
|
604
|
+
<p-form-group ref="groupRef" v-model="projects" v-bind="groupSetting" />
|
|
605
|
+
<a-button type="primary" @click="handleSave"> Save All Projects </a-button>
|
|
607
606
|
</div>
|
|
608
607
|
</template>
|
|
609
608
|
```
|
|
@@ -612,43 +611,43 @@ const groupRef = ref();
|
|
|
612
611
|
|
|
613
612
|
```vue
|
|
614
613
|
<script setup lang="ts">
|
|
615
|
-
import { ref, computed } from 'vue';
|
|
614
|
+
import { ref, computed } from 'vue';
|
|
615
|
+
|
|
616
|
+
const projects = ref([]);
|
|
616
617
|
|
|
617
|
-
const
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
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
|
+
};
|
|
629
636
|
},
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
archiveProject(data, index);
|
|
645
|
-
break;
|
|
646
|
-
}
|
|
647
|
-
},
|
|
648
|
-
getFormSetting: (data) => ({
|
|
649
|
-
// ... form configuration
|
|
650
|
-
}),
|
|
651
|
-
}));
|
|
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
|
+
}));
|
|
652
651
|
</script>
|
|
653
652
|
```
|
|
654
653
|
|
|
@@ -669,7 +668,7 @@ import { labelColDict } from '@vue-ui-kit/ant';
|
|
|
669
668
|
}
|
|
670
669
|
|
|
671
670
|
{
|
|
672
|
-
field: 'longerField',
|
|
671
|
+
field: 'longerField',
|
|
673
672
|
title: 'Description', // 4+ characters
|
|
674
673
|
labelCol: labelColDict[4],
|
|
675
674
|
}
|
|
@@ -684,10 +683,7 @@ import UIKit from '@vue-ui-kit/ant';
|
|
|
684
683
|
import { Switch } from 'ant-design-vue';
|
|
685
684
|
|
|
686
685
|
UIKit.addRender('$switch', {
|
|
687
|
-
renderItemContent(
|
|
688
|
-
{ props = {}, events = {} },
|
|
689
|
-
{ data, field }
|
|
690
|
-
) {
|
|
686
|
+
renderItemContent({ props = {}, events = {} }, { data, field }) {
|
|
691
687
|
return (
|
|
692
688
|
<Switch
|
|
693
689
|
v-model:checked={data[field]}
|
|
@@ -741,20 +737,650 @@ toolbarConfig: {
|
|
|
741
737
|
|
|
742
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.
|
|
743
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
|
+
|
|
744
931
|
## TypeScript Support
|
|
745
932
|
|
|
746
933
|
This library is fully written in TypeScript and provides comprehensive type definitions:
|
|
747
934
|
|
|
748
935
|
```ts
|
|
749
|
-
import type {
|
|
750
|
-
PFormProps,
|
|
936
|
+
import type {
|
|
937
|
+
PFormProps,
|
|
751
938
|
PFormItemProps,
|
|
752
939
|
PFormGroupProps,
|
|
753
940
|
PGridProps,
|
|
754
|
-
ColumnProps
|
|
941
|
+
ColumnProps,
|
|
755
942
|
} from '@vue-ui-kit/ant';
|
|
756
943
|
```
|
|
757
944
|
|
|
758
945
|
## License
|
|
759
946
|
|
|
760
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()` - 添加渲染器
|