@vue-ui-kit/ant 1.7.4 → 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +430 -233
- package/README.zh.md +446 -303
- package/dist/cjs/index.js +1 -1
- package/dist/declarations/antProxy.d.ts +6 -1
- package/dist/es/index.js +2987 -2178
- package/dist/packages/components/PFormCol.vue.d.ts +31 -0
- package/dist/packages/components/PFormGroup.vue.d.ts +2 -2
- package/dist/packages/components/PGrid.vue.d.ts +3 -2
- package/dist/packages/utils/core.d.ts +1 -1
- package/dist/style.css +1 -1
- package/package.json +3 -4
- package/src/declarations/antProxy.ts +6 -1
- package/src/packages/components/PForm.vue +21 -76
- package/src/packages/components/PFormCol.vue +89 -0
- package/src/packages/components/PFormGroup.vue +90 -28
- package/src/packages/components/PGrid.vue +12 -1
- package/src/packages/renders/TableInput.vue +1 -1
- package/src/packages/store/renderStore.tsx +1 -1
- package/src/packages/styles/index.scss +29 -1
- package/src/packages/utils/AFormatters.ts +1 -1
- package/src/packages/utils/core.ts +1 -1
package/README.md
CHANGED
|
@@ -108,17 +108,70 @@ export const setupKit = () => {
|
|
|
108
108
|
|
|
109
109
|
## Components
|
|
110
110
|
|
|
111
|
+
### PGrid
|
|
112
|
+
|
|
113
|
+
Enhanced data table component with integrated search form, pagination, and toolbar.
|
|
114
|
+
|
|
115
|
+
#### Basic Example
|
|
116
|
+
|
|
117
|
+
```vue
|
|
118
|
+
<script setup lang="ts">
|
|
119
|
+
import { computed } from 'vue';
|
|
120
|
+
import { PGridProps, labelColDict } from '@vue-ui-kit/ant';
|
|
121
|
+
|
|
122
|
+
const gridSetting = computed<PGridProps<Student, { keyword?: string }>>(() => ({
|
|
123
|
+
columns: [
|
|
124
|
+
{ field: 'name', title: 'Name', width: 200 },
|
|
125
|
+
{ field: 'email', title: 'Email', width: 200 },
|
|
126
|
+
{ field: 'age', title: 'Age', width: 100 },
|
|
127
|
+
],
|
|
128
|
+
formConfig: {
|
|
129
|
+
items: [
|
|
130
|
+
{
|
|
131
|
+
field: 'keyword',
|
|
132
|
+
title: 'Keyword',
|
|
133
|
+
labelCol: labelColDict[3],
|
|
134
|
+
itemRender: {
|
|
135
|
+
name: '$input',
|
|
136
|
+
props: { placeholder: 'Search...' },
|
|
137
|
+
},
|
|
138
|
+
},
|
|
139
|
+
],
|
|
140
|
+
},
|
|
141
|
+
proxyConfig: {
|
|
142
|
+
ajax: {
|
|
143
|
+
query: ({ form, page }) => api.getStudents({ ...form, ...page }),
|
|
144
|
+
},
|
|
145
|
+
},
|
|
146
|
+
}));
|
|
147
|
+
</script>
|
|
148
|
+
|
|
149
|
+
<template>
|
|
150
|
+
<p-grid v-bind="gridSetting" />
|
|
151
|
+
</template>
|
|
152
|
+
```
|
|
153
|
+
|
|
111
154
|
### PForm
|
|
112
155
|
|
|
113
156
|
Enhanced Form component with simplified configuration and dynamic fields.
|
|
114
157
|
|
|
115
158
|
#### Props
|
|
116
159
|
|
|
117
|
-
| Prop
|
|
118
|
-
|
|
|
119
|
-
| items
|
|
120
|
-
|
|
|
121
|
-
|
|
|
160
|
+
| Prop | Type | Description | Default |
|
|
161
|
+
| --- | --- | --- | --- |
|
|
162
|
+
| items | `PFormItemProps[]` | Form items configuration array | - |
|
|
163
|
+
| data | `T` | Form data object | - |
|
|
164
|
+
| customReset | `() => void` | Custom reset function | - |
|
|
165
|
+
| labelCol | `ColProps` | Label column layout | `{ span: 6 }` |
|
|
166
|
+
| wrapperCol | `ColProps` | Wrapper column layout | `{ span: 16 }` |
|
|
167
|
+
| ...others | `FormProps` | All ant-design-vue Form props | - |
|
|
168
|
+
|
|
169
|
+
#### Events
|
|
170
|
+
|
|
171
|
+
| Event | Description | Parameters |
|
|
172
|
+
| --- | --- | --- |
|
|
173
|
+
| apply | Triggered when form is submitted | `(formData: T) => void` |
|
|
174
|
+
| reset | Triggered when form is reset | `() => void` |
|
|
122
175
|
|
|
123
176
|
#### PFormItemProps
|
|
124
177
|
|
|
@@ -126,279 +179,405 @@ Enhanced Form component with simplified configuration and dynamic fields.
|
|
|
126
179
|
| --- | --- | --- | --- |
|
|
127
180
|
| field | `string` | Form field name | - |
|
|
128
181
|
| title | `string` | Field label | - |
|
|
129
|
-
| span | `number` | Grid span | - |
|
|
130
|
-
| colon | `boolean` | Show colon | true |
|
|
131
|
-
|
|
|
132
|
-
|
|
|
182
|
+
| span | `number` | Grid span (1-24) | - |
|
|
183
|
+
| colon | `boolean` | Show colon after label | `true` |
|
|
184
|
+
| labelCol | `ColProps` | Label column layout | - |
|
|
185
|
+
| wrapperCol | `ColProps` | Wrapper column layout | - |
|
|
186
|
+
| forceRequired | `boolean` | Show required mark (visual only) | `false` |
|
|
187
|
+
| align | `'left' \| 'right' \| 'center'` | Text alignment | `'left'` |
|
|
133
188
|
| col | `ColProps` | Grid column properties | - |
|
|
134
189
|
| rule | `Rule[]` | Validation rules | - |
|
|
190
|
+
| itemRender | `ItemRender` | Field renderer configuration | - |
|
|
135
191
|
| tooltipConfig | `TooltipConfig` | Tooltip configuration | - |
|
|
192
|
+
| slots | `object` | Custom slot renderers | - |
|
|
193
|
+
|
|
194
|
+
#### Built-in Renderers
|
|
195
|
+
|
|
196
|
+
- `$input`: Input field
|
|
197
|
+
- `$textarea`: Textarea field
|
|
198
|
+
- `$number`: Number input
|
|
199
|
+
- `$select`: Select dropdown
|
|
200
|
+
- `$date`: Date picker
|
|
201
|
+
- `$range`: Date range picker
|
|
202
|
+
- `$time`: Time picker
|
|
203
|
+
- `ASwitch`: Switch component
|
|
204
|
+
- `ACheckbox`: Checkbox
|
|
205
|
+
- `ARate`: Rate component
|
|
206
|
+
- `ASlider`: Slider component
|
|
136
207
|
|
|
137
|
-
#### Example
|
|
208
|
+
#### Basic Example
|
|
138
209
|
|
|
139
210
|
```vue
|
|
140
|
-
<script setup lang="
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
},
|
|
170
|
-
},
|
|
171
|
-
{
|
|
172
|
-
field: 'account_code',
|
|
173
|
-
title: 'Account',
|
|
174
|
-
span: 24,
|
|
175
|
-
itemRender: {
|
|
176
|
-
name: '$select',
|
|
177
|
-
props: {
|
|
178
|
-
options: [
|
|
179
|
-
{ label: 'Test Account 1', value: '1' },
|
|
180
|
-
{ label: 'Test Account 2', value: '2' },
|
|
181
|
-
],
|
|
182
|
-
},
|
|
183
|
-
events: {
|
|
184
|
-
change: ({ data }) => {
|
|
185
|
-
data.org_id = '';
|
|
186
|
-
},
|
|
187
|
-
},
|
|
188
|
-
},
|
|
189
|
-
tooltipConfig: {
|
|
190
|
-
title: 'Account refers to the email used to log in to the Test backend',
|
|
191
|
-
},
|
|
211
|
+
<script setup lang="ts">
|
|
212
|
+
import { ref, computed } from 'vue';
|
|
213
|
+
import { PFormProps } from '@vue-ui-kit/ant';
|
|
214
|
+
|
|
215
|
+
interface UserForm {
|
|
216
|
+
name: string;
|
|
217
|
+
email: string;
|
|
218
|
+
age?: number;
|
|
219
|
+
gender: string;
|
|
220
|
+
skills: string[];
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
const formData = ref<UserForm>({
|
|
224
|
+
name: '',
|
|
225
|
+
email: '',
|
|
226
|
+
age: undefined,
|
|
227
|
+
gender: '',
|
|
228
|
+
skills: [],
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
const formSetting = computed<PFormProps<UserForm>>(() => ({
|
|
232
|
+
items: [
|
|
233
|
+
{
|
|
234
|
+
field: 'name',
|
|
235
|
+
title: 'Name',
|
|
236
|
+
span: 12,
|
|
237
|
+
rule: [{ required: true, message: 'Please enter name' }],
|
|
238
|
+
itemRender: {
|
|
239
|
+
name: '$input',
|
|
240
|
+
props: { placeholder: 'Enter name' },
|
|
192
241
|
},
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
},
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
242
|
+
},
|
|
243
|
+
{
|
|
244
|
+
field: 'email',
|
|
245
|
+
title: 'Email',
|
|
246
|
+
span: 12,
|
|
247
|
+
rule: [
|
|
248
|
+
{ required: true, message: 'Please enter email' },
|
|
249
|
+
{ type: 'email', message: 'Invalid email format' },
|
|
250
|
+
],
|
|
251
|
+
itemRender: {
|
|
252
|
+
name: '$input',
|
|
253
|
+
props: { placeholder: 'Enter email' },
|
|
203
254
|
},
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
255
|
+
},
|
|
256
|
+
{
|
|
257
|
+
field: 'gender',
|
|
258
|
+
title: 'Gender',
|
|
259
|
+
span: 12,
|
|
260
|
+
rule: [{ required: true, message: 'Please select gender' }],
|
|
261
|
+
itemRender: {
|
|
262
|
+
name: '$select',
|
|
263
|
+
props: {
|
|
264
|
+
placeholder: 'Select gender',
|
|
265
|
+
options: [
|
|
266
|
+
{ label: 'Male', value: 'male' },
|
|
267
|
+
{ label: 'Female', value: 'female' },
|
|
268
|
+
],
|
|
213
269
|
},
|
|
214
270
|
},
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
271
|
+
},
|
|
272
|
+
{
|
|
273
|
+
field: 'skills',
|
|
274
|
+
title: 'Skills',
|
|
275
|
+
span: 24,
|
|
276
|
+
itemRender: {
|
|
277
|
+
name: '$select',
|
|
278
|
+
props: {
|
|
279
|
+
mode: 'multiple',
|
|
280
|
+
placeholder: 'Select skills',
|
|
281
|
+
options: [
|
|
282
|
+
{ label: 'Vue.js', value: 'vue' },
|
|
283
|
+
{ label: 'React', value: 'react' },
|
|
284
|
+
{ label: 'Angular', value: 'angular' },
|
|
285
|
+
],
|
|
224
286
|
},
|
|
225
287
|
},
|
|
226
|
-
|
|
227
|
-
|
|
288
|
+
},
|
|
289
|
+
],
|
|
290
|
+
}));
|
|
291
|
+
|
|
292
|
+
const handleSubmit = (data: UserForm) => {
|
|
293
|
+
console.log('Form submitted:', data);
|
|
294
|
+
};
|
|
228
295
|
</script>
|
|
296
|
+
|
|
229
297
|
<template>
|
|
230
|
-
<p-form
|
|
298
|
+
<p-form
|
|
299
|
+
v-bind="formSetting"
|
|
300
|
+
:data="formData"
|
|
301
|
+
@apply="handleSubmit"
|
|
302
|
+
/>
|
|
231
303
|
</template>
|
|
232
304
|
```
|
|
233
305
|
|
|
234
|
-
|
|
306
|
+
#### Advanced Example with Custom Slots
|
|
307
|
+
|
|
308
|
+
```vue
|
|
309
|
+
<script setup lang="tsx">
|
|
310
|
+
import { ref, computed } from 'vue';
|
|
311
|
+
|
|
312
|
+
const formData = ref({
|
|
313
|
+
name: '',
|
|
314
|
+
isActive: false,
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
const formSetting = computed(() => ({
|
|
318
|
+
items: [
|
|
319
|
+
{
|
|
320
|
+
field: 'isActive',
|
|
321
|
+
title: 'Status',
|
|
322
|
+
span: 24,
|
|
323
|
+
slots: {
|
|
324
|
+
default: ({ data }) => (
|
|
325
|
+
<a-switch
|
|
326
|
+
v-model:checked={data.isActive}
|
|
327
|
+
checkedChildren="Active"
|
|
328
|
+
unCheckedChildren="Inactive"
|
|
329
|
+
/>
|
|
330
|
+
),
|
|
331
|
+
},
|
|
332
|
+
},
|
|
333
|
+
],
|
|
334
|
+
}));
|
|
335
|
+
</script>
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
### PFormGroup
|
|
235
339
|
|
|
236
|
-
|
|
340
|
+
Dynamic form group component for managing multiple form instances with add/remove capabilities.
|
|
237
341
|
|
|
238
342
|
#### Props
|
|
239
343
|
|
|
240
|
-
| Prop
|
|
241
|
-
|
|
|
242
|
-
|
|
|
243
|
-
|
|
|
244
|
-
|
|
|
245
|
-
|
|
|
246
|
-
|
|
|
247
|
-
|
|
|
248
|
-
|
|
|
249
|
-
|
|
|
250
|
-
|
|
|
344
|
+
| Prop | Type | Description | Default |
|
|
345
|
+
| --- | --- | --- | --- |
|
|
346
|
+
| v-model | `Array<T & { __index: number }>` | Form group data array | `[]` |
|
|
347
|
+
| getFormSetting | `(data: T) => PFormProps<T>` | Function to get form configuration | - |
|
|
348
|
+
| title | `string` | Group title | - |
|
|
349
|
+
| tabLabel | `string` | Custom tab label template | - |
|
|
350
|
+
| editAble | `boolean` | Whether tabs are editable | `true` |
|
|
351
|
+
| showAdd | `boolean` | Show add button | `true` |
|
|
352
|
+
| lazyErrorMark | `boolean` | Lazy error marking | `false` |
|
|
353
|
+
| forceRender | `boolean` | Force render all tabs | `false` |
|
|
354
|
+
| keepSerial | `boolean` | Keep serial indexing | `false` |
|
|
355
|
+
| loading | `boolean` | Loading state | `false` |
|
|
356
|
+
| max | `number` | Maximum number of items | `Infinity` |
|
|
357
|
+
| itemMenus | `GroupMenuItem[]` | Custom menu items | Default copy/delete |
|
|
358
|
+
| createItem | `(opts: { list: T[] }) => Promise<T>` | Custom item creator | - |
|
|
359
|
+
| menuHandler | `GroupMenuItemHandler<T>` | Custom menu handler | - |
|
|
360
|
+
|
|
361
|
+
#### Exposed Methods
|
|
362
|
+
|
|
363
|
+
| Method | Description | Parameters | Returns |
|
|
364
|
+
| --- | --- | --- | --- |
|
|
365
|
+
| validateAll | Validate all form instances | - | `Promise<void>` |
|
|
366
|
+
| validate | Validate specific form instance | `__index: number` | `Promise<void>` |
|
|
367
|
+
| setActiveKey | Set active tab | `key: number` | `void` |
|
|
251
368
|
|
|
252
|
-
#### Example
|
|
369
|
+
#### Basic Example
|
|
253
370
|
|
|
254
371
|
```vue
|
|
255
372
|
<script setup lang="ts">
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
373
|
+
import { ref, computed } from 'vue';
|
|
374
|
+
import { PFormGroupProps } from '@vue-ui-kit/ant';
|
|
375
|
+
|
|
376
|
+
interface Project {
|
|
377
|
+
__index: number;
|
|
378
|
+
name: string;
|
|
379
|
+
startDate: string;
|
|
380
|
+
endDate: string;
|
|
381
|
+
budget?: number;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
const projects = ref<Project[]>([
|
|
385
|
+
{
|
|
386
|
+
__index: 0,
|
|
387
|
+
name: 'Project A',
|
|
388
|
+
startDate: '',
|
|
389
|
+
endDate: '',
|
|
390
|
+
budget: undefined,
|
|
391
|
+
}
|
|
392
|
+
]);
|
|
393
|
+
|
|
394
|
+
const groupSetting = computed<PFormGroupProps<Project>>(() => ({
|
|
395
|
+
title: 'Project Management',
|
|
396
|
+
showAdd: true,
|
|
397
|
+
max: 10,
|
|
398
|
+
getFormSetting: (data) => ({
|
|
399
|
+
items: [
|
|
281
400
|
{
|
|
282
401
|
field: 'name',
|
|
283
|
-
title: '
|
|
284
|
-
|
|
402
|
+
title: 'Project Name',
|
|
403
|
+
span: 24,
|
|
404
|
+
rule: [{ required: true, message: 'Please enter project name' }],
|
|
405
|
+
itemRender: {
|
|
406
|
+
name: '$input',
|
|
407
|
+
props: { placeholder: 'Enter project name' },
|
|
408
|
+
},
|
|
285
409
|
},
|
|
286
410
|
{
|
|
287
|
-
field: '
|
|
288
|
-
title: '
|
|
289
|
-
|
|
411
|
+
field: 'startDate',
|
|
412
|
+
title: 'Start Date',
|
|
413
|
+
span: 12,
|
|
414
|
+
rule: [{ required: true, message: 'Please select start date' }],
|
|
415
|
+
itemRender: {
|
|
416
|
+
name: '$date',
|
|
417
|
+
props: { placeholder: 'Select start date' },
|
|
418
|
+
},
|
|
290
419
|
},
|
|
291
420
|
{
|
|
292
|
-
field: '
|
|
293
|
-
title: '
|
|
294
|
-
|
|
421
|
+
field: 'endDate',
|
|
422
|
+
title: 'End Date',
|
|
423
|
+
span: 12,
|
|
424
|
+
rule: [{ required: true, message: 'Please select end date' }],
|
|
425
|
+
itemRender: {
|
|
426
|
+
name: '$date',
|
|
427
|
+
props: { placeholder: 'Select end date' },
|
|
428
|
+
},
|
|
295
429
|
},
|
|
296
430
|
{
|
|
297
|
-
field: '
|
|
298
|
-
title: '
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
cellRender: {
|
|
308
|
-
name: 'ButtonTree',
|
|
309
|
-
children: [
|
|
310
|
-
{
|
|
311
|
-
content: 'Edit',
|
|
312
|
-
type: 'link',
|
|
313
|
-
clickEvt: ({ row }: { row: Channel }) => doEdit(row),
|
|
314
|
-
},
|
|
315
|
-
{
|
|
316
|
-
content: 'Delete',
|
|
317
|
-
type: 'link',
|
|
318
|
-
danger: true,
|
|
319
|
-
clickEvt: ({ row }: { row: Channel }) => confirmDelete(row),
|
|
320
|
-
},
|
|
321
|
-
],
|
|
322
|
-
},
|
|
323
|
-
},
|
|
324
|
-
] as ColumnProps<Channel>[])
|
|
325
|
-
: []),
|
|
326
|
-
],
|
|
327
|
-
formConfig: {
|
|
328
|
-
items: [
|
|
329
|
-
{
|
|
330
|
-
field: 'name',
|
|
331
|
-
title: 'Channel Name',
|
|
332
|
-
labelCol: labelColDict[4],
|
|
333
|
-
itemRender: {
|
|
334
|
-
name: '$input',
|
|
335
|
-
props: {
|
|
336
|
-
placeholder: 'Please enter channel name',
|
|
337
|
-
},
|
|
338
|
-
},
|
|
339
|
-
},
|
|
340
|
-
{
|
|
341
|
-
col: getButtonResponsive(1),
|
|
342
|
-
align: 'right',
|
|
343
|
-
itemRender: {
|
|
344
|
-
name: '$buttons',
|
|
345
|
-
children: [
|
|
346
|
-
{
|
|
347
|
-
props: {
|
|
348
|
-
content: 'Search',
|
|
349
|
-
htmlType: 'submit',
|
|
350
|
-
type: 'primary',
|
|
351
|
-
},
|
|
352
|
-
},
|
|
353
|
-
{
|
|
354
|
-
props: {
|
|
355
|
-
content: 'Reset',
|
|
356
|
-
htmlType: 'reset',
|
|
357
|
-
},
|
|
358
|
-
},
|
|
359
|
-
],
|
|
431
|
+
field: 'budget',
|
|
432
|
+
title: 'Budget',
|
|
433
|
+
span: 24,
|
|
434
|
+
itemRender: {
|
|
435
|
+
name: '$number',
|
|
436
|
+
props: {
|
|
437
|
+
min: 0,
|
|
438
|
+
placeholder: 'Enter budget',
|
|
439
|
+
formatter: (value) => `$ ${value}`.replace(/\B(?=(\d{3})+(?!\d))/g, ','),
|
|
440
|
+
parser: (value) => value.replace(/\$\s?|(,*)/g, ''),
|
|
360
441
|
},
|
|
361
442
|
},
|
|
362
|
-
],
|
|
363
|
-
},
|
|
364
|
-
pageConfig: {
|
|
365
|
-
pageSize: 10,
|
|
366
|
-
},
|
|
367
|
-
proxyConfig: {
|
|
368
|
-
response: {
|
|
369
|
-
total: 'total',
|
|
370
|
-
result: 'list',
|
|
371
|
-
},
|
|
372
|
-
ajax: {
|
|
373
|
-
query: ({ form, page }) =>
|
|
374
|
-
getChannelPage({
|
|
375
|
-
...form,
|
|
376
|
-
...page,
|
|
377
|
-
} as ChannelParam),
|
|
378
|
-
multiDelete: deleteChannels,
|
|
379
443
|
},
|
|
380
|
-
|
|
381
|
-
})
|
|
444
|
+
],
|
|
445
|
+
}),
|
|
446
|
+
}));
|
|
447
|
+
|
|
448
|
+
const handleSave = async () => {
|
|
449
|
+
try {
|
|
450
|
+
await groupRef.value?.validateAll();
|
|
451
|
+
console.log('All projects saved:', projects.value);
|
|
452
|
+
} catch (error) {
|
|
453
|
+
console.error('Validation failed:', error);
|
|
454
|
+
}
|
|
455
|
+
};
|
|
456
|
+
|
|
457
|
+
const groupRef = ref();
|
|
382
458
|
</script>
|
|
459
|
+
|
|
383
460
|
<template>
|
|
384
|
-
<
|
|
461
|
+
<div>
|
|
462
|
+
<p-form-group
|
|
463
|
+
ref="groupRef"
|
|
464
|
+
v-model="projects"
|
|
465
|
+
v-bind="groupSetting"
|
|
466
|
+
/>
|
|
467
|
+
<a-button type="primary" @click="handleSave">
|
|
468
|
+
Save All Projects
|
|
469
|
+
</a-button>
|
|
470
|
+
</div>
|
|
385
471
|
</template>
|
|
386
472
|
```
|
|
387
473
|
|
|
388
|
-
|
|
474
|
+
#### Advanced Example with Custom Menu
|
|
475
|
+
|
|
476
|
+
```vue
|
|
477
|
+
<script setup lang="ts">
|
|
478
|
+
import { ref, computed } from 'vue';
|
|
479
|
+
|
|
480
|
+
const projects = ref([]);
|
|
481
|
+
|
|
482
|
+
const groupSetting = computed(() => ({
|
|
483
|
+
title: 'Advanced Project Management',
|
|
484
|
+
itemMenus: [
|
|
485
|
+
{ content: 'Copy', code: 'copy' },
|
|
486
|
+
{ content: 'Delete', code: 'delete' },
|
|
487
|
+
{ content: 'Export', code: 'export' },
|
|
488
|
+
{
|
|
489
|
+
content: 'Archive',
|
|
490
|
+
code: 'archive',
|
|
491
|
+
visibleMethod: ({ data }) => data.status !== 'archived'
|
|
492
|
+
},
|
|
493
|
+
],
|
|
494
|
+
createItem: async ({ list }) => {
|
|
495
|
+
return {
|
|
496
|
+
name: `Project ${list.length + 1}`,
|
|
497
|
+
status: 'active',
|
|
498
|
+
startDate: new Date().toISOString().split('T')[0],
|
|
499
|
+
};
|
|
500
|
+
},
|
|
501
|
+
menuHandler: ({ code, data, index }) => {
|
|
502
|
+
switch (code) {
|
|
503
|
+
case 'export':
|
|
504
|
+
exportProject(data);
|
|
505
|
+
break;
|
|
506
|
+
case 'archive':
|
|
507
|
+
archiveProject(data, index);
|
|
508
|
+
break;
|
|
509
|
+
}
|
|
510
|
+
},
|
|
511
|
+
getFormSetting: (data) => ({
|
|
512
|
+
// ... form configuration
|
|
513
|
+
}),
|
|
514
|
+
}));
|
|
515
|
+
</script>
|
|
516
|
+
```
|
|
389
517
|
|
|
390
|
-
|
|
518
|
+
## Utility Functions
|
|
391
519
|
|
|
392
|
-
|
|
520
|
+
### labelColDict
|
|
521
|
+
|
|
522
|
+
Pre-defined label column configurations for different text lengths:
|
|
523
|
+
|
|
524
|
+
```ts
|
|
525
|
+
import { labelColDict } from '@vue-ui-kit/ant';
|
|
526
|
+
|
|
527
|
+
// Usage in form items
|
|
528
|
+
{
|
|
529
|
+
field: 'shortField',
|
|
530
|
+
title: 'Name', // 2 characters
|
|
531
|
+
labelCol: labelColDict[2],
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
{
|
|
535
|
+
field: 'longerField',
|
|
536
|
+
title: 'Description', // 4+ characters
|
|
537
|
+
labelCol: labelColDict[4],
|
|
538
|
+
}
|
|
539
|
+
```
|
|
393
540
|
|
|
394
|
-
|
|
395
|
-
| -------------- | ---------------------- | ----------------------------- |
|
|
396
|
-
| getFormSetting | `(data) => PFormProps` | Function to get form settings |
|
|
397
|
-
| source | `object` | Form data source |
|
|
541
|
+
### Custom Renderers
|
|
398
542
|
|
|
399
|
-
|
|
543
|
+
Register custom form field renderers:
|
|
400
544
|
|
|
401
|
-
|
|
545
|
+
```tsx
|
|
546
|
+
import UIKit from '@vue-ui-kit/ant';
|
|
547
|
+
import { Switch } from 'ant-design-vue';
|
|
548
|
+
|
|
549
|
+
UIKit.addRender('$switch', {
|
|
550
|
+
renderItemContent(
|
|
551
|
+
{ props = {}, events = {} },
|
|
552
|
+
{ data, field }
|
|
553
|
+
) {
|
|
554
|
+
return (
|
|
555
|
+
<Switch
|
|
556
|
+
v-model:checked={data[field]}
|
|
557
|
+
{...props}
|
|
558
|
+
onChange={(...args) => {
|
|
559
|
+
events.change?.({ data, field }, ...args);
|
|
560
|
+
}}
|
|
561
|
+
/>
|
|
562
|
+
);
|
|
563
|
+
},
|
|
564
|
+
});
|
|
565
|
+
```
|
|
566
|
+
|
|
567
|
+
### Custom Formatters
|
|
568
|
+
|
|
569
|
+
Register custom cell formatters for PGrid:
|
|
570
|
+
|
|
571
|
+
```ts
|
|
572
|
+
UIKit.addFormatter({
|
|
573
|
+
currency: ({ cellValue }) => `$${cellValue?.toLocaleString() || '0'}`,
|
|
574
|
+
percentage: ({ cellValue }) => `${(cellValue * 100).toFixed(2)}%`,
|
|
575
|
+
});
|
|
576
|
+
```
|
|
577
|
+
|
|
578
|
+
## FAQ
|
|
579
|
+
|
|
580
|
+
### 1. When using computed + v-bind to generate dynamic tables + forms, form option updates cause table re-rendering. How to solve?
|
|
402
581
|
|
|
403
582
|
Answer: Separate the formConfig into another computed property
|
|
404
583
|
|
|
@@ -423,4 +602,22 @@ toolbarConfig: {
|
|
|
423
602
|
|
|
424
603
|
### 3. Why is the computed approach recommended?
|
|
425
604
|
|
|
426
|
-
Answer: It simplifies all dynamic logic
|
|
605
|
+
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.
|
|
606
|
+
|
|
607
|
+
## TypeScript Support
|
|
608
|
+
|
|
609
|
+
This library is fully written in TypeScript and provides comprehensive type definitions:
|
|
610
|
+
|
|
611
|
+
```ts
|
|
612
|
+
import type {
|
|
613
|
+
PFormProps,
|
|
614
|
+
PFormItemProps,
|
|
615
|
+
PFormGroupProps,
|
|
616
|
+
PGridProps,
|
|
617
|
+
ColumnProps
|
|
618
|
+
} from '@vue-ui-kit/ant';
|
|
619
|
+
```
|
|
620
|
+
|
|
621
|
+
## License
|
|
622
|
+
|
|
623
|
+
MIT
|