@vue-ui-kit/ant 1.7.3 → 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 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 | Type | Description |
118
- | ----------- | ------------------ | ------------------------------ |
119
- | items | `PFormItemProps[]` | Form items configuration array |
120
- | customReset | `() => void` | Custom reset function |
121
- | others | `FormProps` | All ant-design-vue Form props |
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
- | forceRequired | `boolean` | Show required mark (without validation) | false |
132
- | align | `'left' \| 'right' \| 'center'` | Text alignment | 'left' |
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="tsx">
141
- import { computed, ref } from 'vue';
142
- import { TextAdAccount } from '...somewhere';
143
- import { PFormProps } from '@vue-ui-kit/ant';
144
-
145
- const createFormData = ref<TextAdAccount>({
146
- account_code: '',
147
- key_id: '',
148
- org_id: '',
149
- secret_key: '',
150
- });
151
- const createFormSetting = computed<PFormProps<TestAdAccount>>(() => ({
152
- rules: {
153
- account_code: [{ required: true, message: 'Please select Test account', trigger: 'change' }],
154
- org_id: [
155
- { required: true, message: 'Please select authorization organization', trigger: 'change' },
156
- ],
157
- secret_key: [{ required: true, message: 'Please enter Secret Key', trigger: 'change' }],
158
- key_id: [{ required: true, message: 'Please enter Key ID', trigger: 'change' }],
159
- },
160
- items: [
161
- {
162
- title: 'Channel',
163
- span: 24,
164
- itemRender: {
165
- name: 'readonly',
166
- props: {
167
- getText: () => 'Test',
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
- field: 'org_id',
195
- title: 'Authorization Org',
196
- span: 24,
197
- slots: {
198
- default: ({ data }) => <my-custom-input v-model={data.org_id}></my-custom-input>,
199
- },
200
- tooltipConfig: {
201
- title: 'Organization name, ID, and Core ID under the corresponding account',
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
- field: 'secret_key',
206
- title: 'Secret Key',
207
- span: 24,
208
- itemRender: {
209
- name: '$input',
210
- },
211
- tooltipConfig: {
212
- title: 'I am a tooltip',
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
- field: 'key_id',
217
- title: 'Key ID',
218
- span: 24,
219
- itemRender: {
220
- name: '$input',
221
- },
222
- tooltipConfig: {
223
- title: 'I am a tooltip',
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 ref="cf" :data="createFormData" v-bind="createFormSetting" />
298
+ <p-form
299
+ v-bind="formSetting"
300
+ :data="formData"
301
+ @apply="handleSubmit"
302
+ />
231
303
  </template>
232
304
  ```
233
305
 
234
- ### PGrid
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
- Advanced grid component with built-in data management and toolbar functionality.
340
+ Dynamic form group component for managing multiple form instances with add/remove capabilities.
237
341
 
238
342
  #### Props
239
343
 
240
- | Prop | Type | Description | Default |
241
- | ------------- | -------------------- | ----------------------------- | ------- |
242
- | rowKey | `string` | Unique identifier field | 'id' |
243
- | columns | `ColumnProps[]` | Table columns configuration | - |
244
- | formConfig | `PFormProps` | Search form configuration | - |
245
- | toolbarConfig | `ToolbarConfig` | Toolbar buttons configuration | - |
246
- | pageConfig | `PageConfig` | Pagination configuration | - |
247
- | proxyConfig | `ProxyConfig` | Data fetching configuration | - |
248
- | selectConfig | `SelectConfig` | Row selection configuration | - |
249
- | scrollMode | `'outer' \| 'inner'` | Scroll mode | 'outer' |
250
- | fitHeight | `number` | Auto-fit height adjustment | - |
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
- import { ColumnProps, getButtonResponsive, labelColDict, PGridProps } from '@vue-ui-kit/ant';
257
- import { computed } from 'vue';
258
- import { hasAuth } from '...somewhere';
259
- import { getChannelPage, deleteChannel, deleteChannels } from '...somewhere';
260
- import type { Channel, ChannelParam } from '...somewhere';
261
-
262
- const gridSetting = computed<PGridProps<Channel, ChannelParam>>(() => ({
263
- selectConfig: {
264
- multiple: true,
265
- },
266
- toolbarConfig: {
267
- buttons: [
268
- {
269
- code: 'multiDelete',
270
- content: t('批量删除'),
271
- type: 'danger',
272
- },
273
- ],
274
- },
275
- columns: [
276
- {
277
- field: 'id',
278
- title: 'ID',
279
- width: 110,
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: 'Channel Name',
284
- width: 140,
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: 'short_name',
288
- title: 'Short Name',
289
- width: 140,
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: 'operator',
293
- title: 'Operator',
294
- width: 120,
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: 'update_time',
298
- title: 'Last Modified',
299
- width: 180,
300
- },
301
- ...(hasAuth('common_channel_admin')
302
- ? ([
303
- {
304
- title: 'Actions',
305
- fixed: 'right',
306
- width: 160,
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
- <p-grid ref="gridEl" v-bind="gridSetting" />
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
- ### PFormGroup
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
- Form group component for organizing form fields into collapsible sections.
518
+ ## Utility Functions
391
519
 
392
- #### Props
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
- | Prop | Type | Description |
395
- | -------------- | ---------------------- | ----------------------------- |
396
- | getFormSetting | `(data) => PFormProps` | Function to get form settings |
397
- | source | `object` | Form data source |
541
+ ### Custom Renderers
398
542
 
399
- ## Q&A
543
+ Register custom form field renderers:
400
544
 
401
- ### 1. When using computed + v-bind to generate dynamic tables + forms, form option updates cause table re-rendering. How to solve this?
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, that's all. For complex scenarios, you can try using reactive and maintain it yourself, but in most cases, computed is more convenient.
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