dynamicformdjx 0.4.3 → 0.5.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
@@ -4,29 +4,14 @@
4
4
 
5
5
  [Document](https://xczcdjx.github.io/dynamicFormDoc/v3/install.html)
6
6
 
7
- [Vue2 版本](https://www.npmjs.com/package/dynamicformdjx-vue2)
7
+ [Vue2 版本](https://www.npmjs.com/package/dynamicformdjx-vue2)
8
8
 
9
- [React 版本](https://www.npmjs.com/package/dynamicformdjx-react)
10
-
11
- [//]: # (### 组件版本说明)
12
-
13
- [//]: # (- **v0.2 及以上版本**)
14
-
15
- [//]: # (- Form字段改为Input, ElementPlus缩短为Ele, NaiveUi缩短为Nai)
16
-
17
- [//]: # (- 请使用:`DynamicInput`, `DynamicCascadeInput`,`EleDynamicInput`,`NaiDynamicInput` 等导入)
18
-
19
-
20
- [//]: # (| 版本范围 | 组件导入方式 |)
21
-
22
- [//]: # (|---------|--------------|)
23
-
24
- [//]: # (| ≥ 0.2 | DynamicInput, DynamicCascadeInput |)
25
-
26
- [//]: # (| < 0.2 | DynamicForm, DynamicCascadeForm |)
9
+ [React 版本](https://www.npmjs.com/package/dynamicformdjx-react)
27
10
 
28
11
  ## 概述
29
12
 
13
+ > 新增综合`CRUD`模板
14
+
30
15
  `DynamicForm` 一个灵活且动态的表单组件,使用数组,简化模版操作,提供多种hook快速操作表单等。
31
16
 
32
17
  - 简化template代码,快速处理表单
@@ -38,6 +23,7 @@
38
23
  - 支持通过 `v-model` 双向绑定任意对象,(包含受控和非受控) 可动态增删字段
39
24
  - 支持将值解析为:字符串 / 数字 / 数组(字符串数组、数字数组)
40
25
  - 文案、样式、数组分隔符等均可配置
26
+
41
27
  ---
42
28
 
43
29
  ## 安装
@@ -50,11 +36,542 @@ yarn add dynamicformdjx
50
36
  # or
51
37
  pnpm add dynamicformdjx
52
38
  ```
53
- ### 动态表单 (**新**)
54
- > (该表单依赖于naive ui或element plus,请配合一起使用)
39
+
40
+ ### 综合`CRUD` template **新**
41
+
42
+ > (依赖于naive ui或element plus组件库,请配合一起使用)
43
+
55
44
  #### 与Naive ui配合
56
- ##### 1.简单表单
45
+
46
+ ```vue
47
+
48
+ <script setup lang="ts">
49
+ import {h, nextTick, onMounted, ref} from "vue";
50
+ import {type DataTableColumnKey, type DataTableColumns, NButton, NDataTable, NSpace, useMessage} from "naive-ui";
51
+ import {
52
+ NaiPopupModal,
53
+ useDecorateForm,
54
+ NaiZealCard,
55
+ NaiDynamicForm,
56
+ NaiZealTableSearch,
57
+ NaiZealTablePaginationControl,
58
+ renderInput,
59
+ renderInputNumber,
60
+ } from "dynamicformdjx/naiveUi";
61
+ import type {
62
+ naiPopupModalRef,
63
+ naiDynamicFormRef,
64
+ naiZealTableSearchRef
65
+ } from "dynamicformdjx/naiveUi"
66
+ import {useDyForm, useReactiveForm, usePagination} from "dynamicformdjx";
67
+
68
+ interface SongType {
69
+ no: number | string
70
+ title: string
71
+ length: string
72
+ }
73
+
74
+ const zealData = ref<SongType[]>([
75
+ {no: 3, title: 'Wonderwall', length: '4:18'},
76
+ {no: 4, title: 'Don\'t Look Back in Anger', length: '4:48'},
77
+ {no: 12, title: 'Champagne Supernova', length: '7:27'},
78
+ ...Array.from({length: 50}).map((_, i) => ({no: i + 13, title: `test Data ${i + 1}`, length: `${i * i}`}))
79
+ ])
80
+ const message = useMessage()
81
+ const referId = ref<string | number>('-1')
82
+ const tableData = ref<SongType[]>([])
83
+ const handleDynamicFormRef = ref<naiDynamicFormRef | null>(null)
84
+ const naiZealTableSearchRef = ref<naiZealTableSearchRef | null>(null)
85
+ const naiPopupModalRef = ref<naiPopupModalRef | null>(null)
86
+ const tableLoading = ref<boolean>(false)
87
+ const selectOpts = ref<(number | string)[]>([])
88
+ // search form
89
+ const searchFormItems = useDecorateForm([
90
+ {
91
+ key: "no",
92
+ label: "No",
93
+ renderType: 'renderInputNumber',
94
+ },
95
+ {
96
+ key: "title",
97
+ label: "Title",
98
+ },
99
+ {
100
+ key: "length",
101
+ label: "Length",
102
+ },
103
+ ].map(it => ({
104
+ value: null,
105
+ clearable: true,
106
+ renderType: 'renderInput',
107
+ span: 8,
108
+ ...it,
109
+ })) as any[])
110
+ // table column
111
+ const columns: DataTableColumns<SongType> = [
112
+ {
113
+ type: 'selection'
114
+ },
115
+ {
116
+ title: 'No',
117
+ key: 'no'
118
+ },
119
+ {
120
+ title: 'Title',
121
+ key: 'title'
122
+ },
123
+ {
124
+ title: 'Length',
125
+ key: 'length'
126
+ },
127
+ {
128
+ title: 'Action',
129
+ key: 'actions',
130
+ fixed: 'right',
131
+ render(row) {
132
+ return h(
133
+ NSpace, {}, [
134
+ h(NButton,
135
+ {
136
+ size: 'small',
137
+ onClick: () => upItem(row)
138
+ },
139
+ {default: () => 'update'}),
140
+ h(NButton,
141
+ {
142
+ size: 'small',
143
+ type: 'error',
144
+ onClick: () => delItem(row)
145
+ },
146
+ {default: () => 'delete'})
147
+ ]
148
+ )
149
+ }
150
+ }
151
+ ]
152
+ const pagination = usePagination(fetchData)
153
+ const updateFormItems = useReactiveForm<SongType>([
154
+ {
155
+ key: "no",
156
+ label: "No",
157
+ clearable: true,
158
+ value: null,
159
+ render2: (f) => renderInputNumber(f.value, {}, f)
160
+ },
161
+ {
162
+ key: "title",
163
+ label: "Title",
164
+ value: null,
165
+ clearable: true,
166
+ render2: (f) => renderInput(f.value, {}, f),
167
+ },
168
+ {
169
+ key: "length",
170
+ label: "Length",
171
+ value: null,
172
+ clearable: true,
173
+ render2: (f) => renderInput(f.value, {}, f),
174
+ },
175
+ ])
176
+ const useForm = useDyForm(updateFormItems)
177
+ const doSearch = () => {
178
+ fetchData()
179
+ pagination.pageNo = 1
180
+ }
181
+ const doReset = () => {
182
+ fetchData()
183
+ pagination.pageNo = 1
184
+ }
185
+
186
+ function rowKey(row: SongType) {
187
+ return row.no
188
+ }
189
+
190
+ // mock http request
191
+ async function fetchData() {
192
+ tableLoading.value = true
193
+ const {pageNo, pageSize} = pagination
194
+ const params = naiZealTableSearchRef.value?.getParams<SongType>?.()
195
+ const r = await new Promise<{ data: SongType[], total: number }>((resolve, reject) => {
196
+ setTimeout(() => {
197
+ const start = (pageNo - 1) * pageSize
198
+ const {length, no, title} = params!
199
+ const data = zealData.value.filter(it => (!length || it.length.includes(length)) && (!title || it.title.includes(title)) && (!no || it.no === parseInt(no as string)))
200
+ resolve({
201
+ data: data.slice(start, start + pageSize),
202
+ total: data.length
203
+ })
204
+ }, 1500)
205
+ })
206
+ tableData.value = r.data
207
+ pagination.setTotalSize(r.total)
208
+ tableLoading.value = false
209
+ }
210
+
211
+ const newItem = () => {
212
+ referId.value = '-1'
213
+ useForm.onReset()
214
+ nextTick(() => {
215
+ naiPopupModalRef.value?.toggle?.(true)
216
+ })
217
+ }
218
+
219
+ function upItem(r: SongType) {
220
+ referId.value = r.no
221
+ useForm.setValues(r)
222
+ nextTick(() => {
223
+ naiPopupModalRef.value?.toggle?.(true)
224
+ })
225
+ }
226
+
227
+ function delItem(r: SongType) {
228
+ zealData.value = zealData.value.filter(it2 => it2.no !== r.no)
229
+ message.success('delete successful')
230
+ fetchData()
231
+ }
232
+
233
+ const deleteAll = () => {
234
+ zealData.value = zealData.value.filter(it2 => !selectOpts.value.includes(it2.no))
235
+ message.success('delete all successful')
236
+ fetchData()
237
+ }
238
+ const onSubmit = async () => {
239
+ handleDynamicFormRef.value?.validator().then((v: any) => {
240
+ if (referId.value === '-1') {
241
+ zealData.value.unshift({...v, key: Date.now()})
242
+ message.success('Add successful')
243
+ } else {
244
+ zealData.value = zealData.value.map(it => {
245
+ if (referId.value === it.no) return v as SongType
246
+ return it
247
+ })
248
+ message.success('Update successful')
249
+ }
250
+ nextTick(() => {
251
+ naiPopupModalRef.value?.toggle?.(false)
252
+ fetchData()
253
+ })
254
+ })
255
+ }
256
+ const handleSelectionChange = (v: DataTableColumnKey[]) => {
257
+ selectOpts.value = v
258
+ }
259
+ onMounted(() => {
260
+ fetchData()
261
+ })
262
+ </script>
263
+
264
+ <template>
265
+ <NaiZealCard>
266
+ <template #header="{isMobile}">
267
+ <NaiZealTableSearch :isMobile="isMobile" :search-items="searchFormItems" ref="naiZealTableSearchRef"
268
+ :mobile-drawer="true"
269
+ title="zeal test" @onReset="doReset"
270
+ @onSearch="doSearch"/>
271
+ </template>
272
+ <template #controlBtn>
273
+ <n-button type="success" size="small" @click="newItem">Add</n-button>&nbsp;
274
+ <n-button type="error" size="small" @click="deleteAll" :disabled="!selectOpts.length">Del Selected</n-button>
275
+ </template>
276
+ <template #toolBtn>
277
+ <n-button type="default" size="small" @click="()=>{}">
278
+ Tool...
279
+ </n-button>
280
+ </template>
281
+ <template #default="{tableHeight}">
282
+ <n-data-table
283
+ :row-key="rowKey"
284
+ :loading="tableLoading"
285
+ :columns="columns"
286
+ :data="tableData"
287
+ :bordered="false"
288
+ :style="{ height: tableHeight+'px'}"
289
+ :flex-height="true"
290
+ :scroll-x="600"
291
+ @update:checked-row-keys="handleSelectionChange"
292
+ />
293
+ </template>
294
+ <template #footer="{isMobile}">
295
+ <NaiZealTablePaginationControl :is-mobile="isMobile" :pagination="pagination">
296
+ <template #prefix="{ itemCount }">
297
+ Total {{ itemCount }}
298
+ </template>
299
+ </NaiZealTablePaginationControl>
300
+ </template>
301
+ <template #rest>
302
+ <NaiPopupModal :title="referId==='-1'?'add Test':'update Test'" ref="naiPopupModalRef" :on-submit="onSubmit">
303
+ <NaiDynamicForm :items="updateFormItems" ref="handleDynamicFormRef"/>
304
+ </NaiPopupModal>
305
+ </template>
306
+ </NaiZealCard>
307
+ </template>
308
+
309
+ <style scoped>
310
+
311
+ </style>
312
+ ```
313
+
314
+ #### 与Element-plus配合
315
+
57
316
  ```vue
317
+
318
+ <script setup lang="ts">
319
+ import {h, nextTick, onMounted, ref} from "vue";
320
+ import {ElMessage, ElButton, ElSpace} from "element-plus";
321
+ import {
322
+ ElePopupModal,
323
+ useDecorateForm,
324
+ EleZealCard,
325
+ EleDynamicForm,
326
+ EleZealTableSearch,
327
+ EleZealTablePaginationControl,
328
+ EleZealTable,
329
+ renderInput,
330
+ renderInputNumber,
331
+ } from "dynamicformdjx/elementPlus";
332
+ import type {
333
+ elePopupModalRef,
334
+ eleDynamicFormRef,
335
+ eleZealTableSearchRef
336
+ } from "dynamicformdjx/elementPlus"
337
+ import {useDyForm, useReactiveForm, usePagination} from "dynamicformdjx";
338
+ import type {ZealColumn} from "dynamicformdjx/types/form";
339
+
340
+ interface SongType {
341
+ no: number | string
342
+ title: string
343
+ length: string
344
+ }
345
+
346
+ const zealData = ref<SongType[]>([
347
+ {no: 3, title: 'Wonderwall', length: '4:18'},
348
+ {no: 4, title: 'Don\'t Look Back in Anger', length: '4:48'},
349
+ {no: 12, title: 'Champagne Supernova', length: '7:27'},
350
+ ...Array.from({length: 50}).map((_, i) => ({no: i + 13, title: `test Data ${i + 1}`, length: `${i * i}`}))
351
+ ])
352
+ const referId = ref<string | number>('-1')
353
+ const tableData = ref<SongType[]>([])
354
+ const handleDynamicFormRef = ref<eleDynamicFormRef | null>(null)
355
+ const naiZealTableSearchRef = ref<eleZealTableSearchRef | null>(null)
356
+ const naiPopupModalRef = ref<elePopupModalRef | null>(null)
357
+ const tableLoading = ref<boolean>(false)
358
+ const selectOpts = ref<(number | string)[]>([])
359
+ // search form
360
+ const searchFormItems = useDecorateForm([
361
+ {
362
+ key: "no",
363
+ label: "No",
364
+ renderType: 'renderInputNumber',
365
+ },
366
+ {
367
+ key: "title",
368
+ label: "Title",
369
+ },
370
+ {
371
+ key: "length",
372
+ label: "Length",
373
+ },
374
+ ].map(it => ({
375
+ value: null,
376
+ clearable: true,
377
+ renderType: 'renderInput',
378
+ span: 8,
379
+ ...it,
380
+ })) as any[])
381
+ // table column
382
+ const columns: ZealColumn<SongType>[] = [
383
+ {type: 'selection', width: 55},
384
+ {type: 'expand', render2: row => h('div', {}, JSON.stringify(row, null, 2))},
385
+ {label: "No", prop: "no", width: 80},
386
+ {label: "Title", prop: "title", slot: "title"},
387
+ {label: "Length", prop: "length"},
388
+ {
389
+ label: "Actions", fixed: 'right', width: 160, render2: (row) => h(
390
+ ElSpace, {}, [
391
+ h(ElButton,
392
+ {
393
+ size: 'small',
394
+ onClick: () => upItem(row)
395
+ },
396
+ 'update'),
397
+ h(ElButton,
398
+ {
399
+ size: 'small',
400
+ type: 'danger',
401
+ onClick: () => delItem(row)
402
+ },
403
+ 'delete')
404
+ ]
405
+ )
406
+ },
407
+ ];
408
+
409
+ const pagination = usePagination(fetchData)
410
+ const updateFormItems = useReactiveForm<SongType>([
411
+ {
412
+ key: "no",
413
+ label: "No",
414
+ clearable: true,
415
+ value: null,
416
+ render2: (f) => renderInputNumber(f.value, {}, f)
417
+ },
418
+ {
419
+ key: "title",
420
+ label: "Title",
421
+ value: null,
422
+ clearable: true,
423
+ render2: (f) => renderInput(f.value, {}, f),
424
+ },
425
+ {
426
+ key: "length",
427
+ label: "Length",
428
+ value: null,
429
+ clearable: true,
430
+ render2: (f) => renderInput(f.value, {}, f),
431
+ },
432
+ ])
433
+ const useForm = useDyForm(updateFormItems)
434
+ const doSearch = () => {
435
+ fetchData()
436
+ pagination.pageNo = 1
437
+ }
438
+ const doReset = () => {
439
+ fetchData()
440
+ pagination.pageNo = 1
441
+ }
442
+
443
+ // mock http request
444
+ async function fetchData() {
445
+ tableLoading.value = true
446
+ const {pageNo, pageSize} = pagination
447
+ const params = naiZealTableSearchRef.value?.getParams<SongType>?.()
448
+ const r = await new Promise<{ data: SongType[], total: number }>((resolve, reject) => {
449
+ setTimeout(() => {
450
+ const start = (pageNo - 1) * pageSize
451
+ const {length, no, title} = params!
452
+ const data = zealData.value.filter(it => (!length || it.length.includes(length)) && (!title || it.title.includes(title)) && (!no || it.no === parseInt(no as string)))
453
+ resolve({
454
+ data: data.slice(start, start + pageSize),
455
+ total: data.length
456
+ })
457
+ }, 1500)
458
+ })
459
+ tableData.value = r.data
460
+ pagination.setTotalSize(r.total)
461
+ tableLoading.value = false
462
+ }
463
+
464
+ const newItem = () => {
465
+ referId.value = '-1'
466
+ useForm.onReset()
467
+ nextTick(() => {
468
+ naiPopupModalRef.value?.toggle?.(true)
469
+ })
470
+ }
471
+
472
+ function upItem(r: SongType) {
473
+ referId.value = r.no
474
+ useForm.setValues(r)
475
+ nextTick(() => {
476
+ naiPopupModalRef.value?.toggle?.(true)
477
+ })
478
+ }
479
+
480
+ function delItem(r: SongType) {
481
+ zealData.value = zealData.value.filter(it2 => it2.no !== r.no)
482
+ ElMessage.success('delete successful')
483
+ fetchData()
484
+ }
485
+
486
+ const deleteAll = () => {
487
+ zealData.value = zealData.value.filter(it2 => !selectOpts.value.includes(it2.no))
488
+ ElMessage.success('delete all successful')
489
+ fetchData()
490
+ }
491
+ const onSubmit = async () => {
492
+ handleDynamicFormRef.value?.validator().then((v: any) => {
493
+ if (referId.value === '-1') {
494
+ zealData.value.unshift({...v, key: Date.now()})
495
+ ElMessage.success('Add successful')
496
+ } else {
497
+ zealData.value = zealData.value.map(it => {
498
+ if (referId.value === it.no) return v as SongType
499
+ return it
500
+ })
501
+ ElMessage.success('Update successful')
502
+ }
503
+ nextTick(() => {
504
+ naiPopupModalRef.value?.toggle?.(false)
505
+ fetchData()
506
+ })
507
+ })
508
+ }
509
+ const handleSelectionChange = (v: SongType[]) => {
510
+ selectOpts.value = v.map(it => it.no)
511
+ }
512
+ onMounted(() => {
513
+ fetchData()
514
+ })
515
+ </script>
516
+
517
+ <template>
518
+ <EleZealCard>
519
+ <template #header="{isMobile}">
520
+ <EleZealTableSearch :is-mobile="isMobile" :search-items="searchFormItems" ref="naiZealTableSearchRef"
521
+ :mobile-drawer="true"
522
+ title="zeal test" @onReset="doReset"
523
+ @onSearch="doSearch">
524
+ <template #drawerBtn="{openDrawer}">
525
+ <el-button @click="openDrawer">+</el-button>
526
+ </template>
527
+ </EleZealTableSearch>
528
+ </template>
529
+ <template #controlBtn>
530
+ <el-button type="success" size="small" @click="newItem">Add</el-button>
531
+ <el-button type="danger" size="small" @click="deleteAll" :disabled="!selectOpts.length">Del Selected</el-button>
532
+ </template>
533
+ <template #toolBtn>
534
+ <el-button type="default" size="small" @click="()=>{}">
535
+ Tool...
536
+ </el-button>
537
+ </template>
538
+ <template #default="{tableHeight}">
539
+ <EleZealTable :data="tableData" :columns="columns" :max-height="tableHeight" :loading="tableLoading"
540
+ @selection-change="handleSelectionChange">
541
+ <template #title="{ row }">
542
+ <el-tag>{{ row.title }}</el-tag>
543
+ </template>
544
+ <template #empty>
545
+ <p> no data</p>
546
+ </template>
547
+ </EleZealTable>
548
+ </template>
549
+ <template #footer="{isMobile}">
550
+ <EleZealTablePaginationControl :is-mobile="isMobile" :pagination="pagination"/>
551
+ </template>
552
+ <template #rest>
553
+ <ElePopupModal :title="referId==='-1'?'add Test':'update Test'" ref="naiPopupModalRef" :on-submit="onSubmit">
554
+ <EleDynamicForm :items="updateFormItems" ref="handleDynamicFormRef"/>
555
+ </ElePopupModal>
556
+ </template>
557
+ </EleZealCard>
558
+ </template>
559
+
560
+ <style scoped>
561
+
562
+ </style>
563
+ ```
564
+
565
+ ### 动态表单
566
+
567
+ #### 与Naive ui配合
568
+
569
+ ##### 简单表单
570
+
571
+ > 还有自定义表单,装饰表单请参考文档
572
+
573
+ ```vue
574
+
58
575
  <script setup lang="ts">
59
576
  import {ref} from "vue";
60
577
  import {NButton} from "naive-ui";
@@ -91,7 +608,7 @@ pnpm add dynamicformdjx
91
608
  render2: f => renderInput(f.value, {showPasswordOn: 'click'}, f),
92
609
  span: 8,
93
610
  offset: 2,
94
- requiredHint:l=>`${l} is not empty`
611
+ requiredHint: l => `${l} is not empty`
95
612
  },
96
613
  {
97
614
  key: "preset",
@@ -154,314 +671,95 @@ pnpm add dynamicformdjx
154
671
  </template>
155
672
 
156
673
  <style scoped>
157
- h3{
674
+ h3 {
158
675
  text-align: center;
159
- margin:0 0 10px 0;
676
+ margin: 0 0 10px 0;
160
677
  }
678
+
161
679
  .control {
162
680
  display: flex;
163
681
  gap: 5px;
164
682
  }
165
683
  </style>
166
684
  ```
167
- ##### 2.自定义表单
168
- > (所有render2函数使用自定义)
169
- ##### InputTest.vue
170
- ```vue
171
- <script setup lang="ts">
172
- import {NInput} from "naive-ui";
173
- import {useAttrs} from "vue";
174
- const fv=defineModel()
175
- const attrs=useAttrs()
176
- </script>
177
-
178
- <template>
179
- <n-input v-model="fv" v-bind="attrs"/>
180
- </template>
181
685
 
182
- <style scoped>
686
+ #### 与Element-plus配合
183
687
 
184
- </style>
185
- ```
186
- ##### Render.vue
187
- ```vue
188
- <script setup lang="ts">
189
- import {h, ref} from "vue";
190
- import {NButton, NInput} from "naive-ui";
191
- import {useDyForm, useReactiveForm} from "dynamicformdjx";
192
- import {type naiDynamicFormRef, NaiDynamicForm, NaiDynamicInput, type naiDynamicInputRef} from "dynamicformdjx/naiveUi";
193
- import type {FormItemRule, FormRules} from "naive-ui/es/form/src/interface";
194
- import InputTest from "./InputTest.vue";
195
-
196
- type FormRow = {
197
- name: string
198
- desc: string
199
- json: object
200
- }
201
- const naiDynamicFormRef = ref<naiDynamicFormRef | null>(null)
202
- const naiDynamicInputRef = ref<naiDynamicInputRef | null>(null)
203
- const formItems = useReactiveForm<FormRow, FormRules | FormItemRule>([
204
- {
205
- key: "name",
206
- label: "姓名",
207
- value: ref<string | null>(null),
208
- clearable: true,
209
- placeholder: '请输入姓名',
210
- required: true,
211
- // @ts-ignore
212
- render2: f => h(NInput, {
213
- ...f,
214
- value: f.value.value, "onUpdate:value"(v) {
215
- f.value.value = v
216
- }
217
- }),
218
- },
219
- {
220
- key: "desc",
221
- label: "描述",
222
- value: ref<string | null>(null),
223
- clearable: true,
224
- placeholder: '请输入描述',
225
- required: true,
226
- type: 'textarea',
227
- render2: f => h(InputTest, {
228
- ...f,
229
- value: f.value.value, "onUpdate:value"(v) {
230
- f.value.value = v
231
- }
232
- }),
233
- },
234
- {
235
- key: "json",
236
- label: "Json",
237
- value: ref<object>({}),
238
- rule: {
239
- required: true,
240
- validator(_: FormItemRule, value: object) {
241
- return Object.keys(value).length > 0
242
- },
243
- trigger: ['blur', 'change'],
244
- message: 'json 不能为空'
245
- },
246
- render2: f => h(NaiDynamicInput, {
247
- modelValue: f.value.value, "onUpdate:modelValue"(v) {
248
- f.value.value = v
249
- },
250
- isController: true,
251
- ref: naiDynamicInputRef
252
- }),
253
- },
254
- ])
255
- const useForm = useDyForm<FormRow>(formItems)
256
- const getData = () => {
257
- console.log(useForm.getValues())
258
- }
259
- const resetData = () => {
260
- useForm.onReset()
261
- naiDynamicInputRef.value?.onSet?.({})
262
- }
263
- const setData = () => {
264
- useForm.setValues({
265
- name: 'naive-ui',
266
- desc:`A Vue 3 Component Library Fairly Complete, Theme Customizable, Uses TypeScript, Fast Kinda Interesting`
267
- })
268
- naiDynamicInputRef.value?.onSet?.({
269
- question: 'how are you?',
270
- answer: "I'm fine,Thank you"
271
- })
272
- }
273
- const validatorData = () => {
274
- // 校验
275
- naiDynamicFormRef.value?.validator().then(data => {
276
- console.log(data)
277
- }).catch(err => {
278
- console.log(err)
279
- })
280
- }
281
- </script>
688
+ > (只是导入从"dynamicformdjx/elementPlus"中,类型方法与上方naive ui一致)
282
689
 
283
- <template>
284
- <NaiDynamicForm :items="formItems" ref="naiDynamicFormRef"/>
285
- <div class="control">
286
- <n-button @click="getData" type="success" size="small">get Data</n-button>
287
- <n-button @click="setData" type="warning" size="small">set Data</n-button>
288
- <n-button @click="validatorData" type="default" size="small">validate Data</n-button>
289
- <n-button @click="resetData" type="error" size="small">reset Data</n-button>
290
- </div>
291
- </template>
690
+ ##### 简单表单
292
691
 
293
- <style scoped>
294
- .control {
295
- display: flex;
296
- gap: 5px;
297
- }
298
- </style>
299
- ```
300
- ##### 3.装饰表单
301
- > (可省略render2函数)
302
692
  ```vue
303
- <script setup lang="ts">
304
- import { ref} from "vue";
305
- import {NButton} from "naive-ui";
306
- import {useDyForm} from "dynamicformdjx";
307
- import {
308
- type naiDynamicFormRef,
309
- NaiDynamicForm,
310
- useDecorateForm,
311
- renderDatePicker
312
- } from "dynamicformdjx/naiveUi";
313
-
314
-
315
- type FormRow = {
316
- password: string
317
- job: number
318
- birthday: number
319
- }
320
- const naiDynamicFormRef = ref<naiDynamicFormRef | null>(null)
321
- const formItems = useDecorateForm<FormRow>([
322
- {
323
- key: "password",
324
- label: "密码",
325
- value: null,
326
- clearable: true,
327
- placeholder: '请输入密码',
328
- required: true,
329
- type:'password',
330
- renderType: 'renderInput',
331
- renderProps:{
332
- showPasswordOn:'click'
333
- }
334
- },
335
- {
336
- key: "job",
337
- label: "职位",
338
- value: null,
339
- clearable: true,
340
- options: ['前端', '后端'].map((label, value) => ({label, value})),
341
- renderType: 'renderSelect',
342
- },
343
- {
344
- key: "birthday",
345
- label: "生日",
346
- value: null,
347
- render2: f => renderDatePicker(f.value, {type: 'datetime'}, f),
348
- },
349
- ])
350
- const useForm = useDyForm<FormRow>(formItems)
351
- const getData = () => {
352
- const res = naiDynamicFormRef.value?.getResult?.()
353
- console.log(res)
354
- }
355
- const resetData = () => {
356
- naiDynamicFormRef.value?.reset?.()
357
- }
358
- const setData = () => {
359
- useForm.setValues({
360
- password: 'naive-ui',
361
- job: 0,
362
- birthday: Date.now(),
363
- })
364
- }
365
- const validatorData = () => {
366
- naiDynamicFormRef.value?.validator().then(data => {
367
- console.log(data)
368
- }).catch(err => {
369
- console.log(err)
370
- })
371
- }
372
- </script>
373
-
374
- <template>
375
- <NaiDynamicForm :items="formItems" ref="naiDynamicFormRef"/>
376
- <div class="control">
377
- <n-button @click="getData" type="success" size="small">get Data</n-button>
378
- <n-button @click="setData" type="warning" size="small">set Data</n-button>
379
- <n-button @click="validatorData" type="default" size="small">validate Data</n-button>
380
- <n-button @click="resetData" type="error" size="small">reset Data</n-button>
381
- </div>
382
- </template>
383
693
 
384
- <style scoped>
385
- .control {
386
- display: flex;
387
- gap: 5px;
388
- }
389
- </style>
390
- ```
391
-
392
- #### 与Element-plus配合
393
- > (这里只展示一种,只是导入从"dynamicformdjx/elementPlus"中,类型方法与上方naive ui一致)
394
- ##### 简单表单
395
- ```vue
396
694
  <script setup lang="ts">
397
- import {ref} from "vue";
398
- import {ElButton} from "element-plus";
399
- import {useDyForm, useReactiveForm} from "dynamicformdjx";
400
- import {type eleDynamicFormRef, renderInput, renderRadioGroup, EleDynamicForm} from "dynamicformdjx/elementPlus";
401
- import type {PresetType} from "dynamicformdjx/types/index";
402
-
403
- type FormRow = {
404
- username: string
405
- password: string
406
- preset: PresetType
407
- }
408
- const eleDynamicFormRef = ref<eleDynamicFormRef | null>(null)
409
- const presetType = ref<PresetType>('fullRow')
410
- const formItems = useReactiveForm<FormRow>([
411
- {
412
- key: "username",
413
- label: "姓名",
414
- value: ref<string | null>(null),
415
- clearable: true,
416
- placeholder: '请输入姓名',
417
- required: true, // 是否必填 (简化rules规则)
418
- render2: f => renderInput(f.value, {}, f),
419
- span: 8
420
- },
421
- {
422
- key: "password",
423
- label: "密码",
424
- value: ref<string | null>(null),
425
- clearable: true,
426
- type: 'password',
427
- required: true,
428
- placeholder: '请输入密码',
429
- render2: f => renderInput(f.value, {showPassword: true}, f),
430
- span: 8,
431
- offset: 2,
432
- requiredHint: l => `${l} is not empty`
433
- },
434
- {
435
- key: "preset",
436
- label: "表格预设",
437
- value: ref<PresetType | null>(presetType.value),
438
- render2: f => renderRadioGroup(f.value, [
439
- {label: '整行', value: 'fullRow'},
440
- {label: '表格', value: 'grid'},
441
- ], {name: 'preset'}, f),
442
- onChange: (v) => {
443
- presetType.value = v
444
- }
445
- },
446
- ])
447
- const useForm = useDyForm<FormRow>(formItems)
448
- const getData = () => {
449
- const res = eleDynamicFormRef.value?.getResult?.()
450
- console.log(res)
451
- }
452
- const resetData = () => eleDynamicFormRef.value?.reset?.()
453
- const setData = () => useForm.setValues({
454
- username: 'element-plus',
455
- password: '520'
456
- })
457
- const validatorData = () => {
458
- // 校验
459
- eleDynamicFormRef.value?.validator().then(data => {
460
- console.log(data)
461
- }).catch(err => {
462
- console.log(err)
695
+ import {ref} from "vue";
696
+ import {ElButton} from "element-plus";
697
+ import {useDyForm, useReactiveForm} from "dynamicformdjx";
698
+ import {type eleDynamicFormRef, renderInput, renderRadioGroup, EleDynamicForm} from "dynamicformdjx/elementPlus";
699
+ import type {PresetType} from "dynamicformdjx/types/index";
700
+
701
+ type FormRow = {
702
+ username: string
703
+ password: string
704
+ preset: PresetType
705
+ }
706
+ const eleDynamicFormRef = ref<eleDynamicFormRef | null>(null)
707
+ const presetType = ref<PresetType>('fullRow')
708
+ const formItems = useReactiveForm<FormRow>([
709
+ {
710
+ key: "username",
711
+ label: "姓名",
712
+ value: ref<string | null>(null),
713
+ clearable: true,
714
+ placeholder: '请输入姓名',
715
+ required: true, // 是否必填 (简化rules规则)
716
+ render2: f => renderInput(f.value, {}, f),
717
+ span: 8
718
+ },
719
+ {
720
+ key: "password",
721
+ label: "密码",
722
+ value: ref<string | null>(null),
723
+ clearable: true,
724
+ type: 'password',
725
+ required: true,
726
+ placeholder: '请输入密码',
727
+ render2: f => renderInput(f.value, {showPassword: true}, f),
728
+ span: 8,
729
+ offset: 2,
730
+ requiredHint: l => `${l} is not empty`
731
+ },
732
+ {
733
+ key: "preset",
734
+ label: "表格预设",
735
+ value: ref<PresetType | null>(presetType.value),
736
+ render2: f => renderRadioGroup(f.value, [
737
+ {label: '整行', value: 'fullRow'},
738
+ {label: '表格', value: 'grid'},
739
+ ], {name: 'preset'}, f),
740
+ onChange: (v) => {
741
+ presetType.value = v
742
+ }
743
+ },
744
+ ])
745
+ const useForm = useDyForm<FormRow>(formItems)
746
+ const getData = () => {
747
+ const res = eleDynamicFormRef.value?.getResult?.()
748
+ console.log(res)
749
+ }
750
+ const resetData = () => eleDynamicFormRef.value?.reset?.()
751
+ const setData = () => useForm.setValues({
752
+ username: 'element-plus',
753
+ password: '520'
463
754
  })
464
- }
755
+ const validatorData = () => {
756
+ // 校验
757
+ eleDynamicFormRef.value?.validator().then(data => {
758
+ console.log(data)
759
+ }).catch(err => {
760
+ console.log(err)
761
+ })
762
+ }
465
763
  </script>
466
764
 
467
765
  <template>
@@ -481,21 +779,26 @@ const validatorData = () => {
481
779
  </template>
482
780
 
483
781
  <style scoped>
484
- h3 {
485
- text-align: center;
486
- margin: 0 0 10px 0;
487
- }
488
-
489
- .control {
490
- display: flex;
491
- gap: 5px;
492
- }
782
+ h3 {
783
+ text-align: center;
784
+ margin: 0 0 10px 0;
785
+ }
786
+
787
+ .control {
788
+ display: flex;
789
+ gap: 5px;
790
+ }
493
791
  </style>
494
792
  ```
495
- ### 动态录入
793
+
794
+ ### 动态录入
795
+
796
+ > 此录入无需组件库依赖
496
797
 
497
798
  #### 1.单组件
799
+
498
800
  ```vue
801
+
499
802
  <script setup lang="ts">
500
803
  import {ref} from "vue";
501
804
  import {DynamicInput, type dynamicInputRef} from "dynamicformdjx";
@@ -525,9 +828,10 @@ h3 {
525
828
  #### 2.级联基本使用
526
829
 
527
830
  ```vue
831
+
528
832
  <script setup lang="ts">
529
833
  import {ref} from "vue";
530
- import {dynamicCascadeInputRef,DynamicCascadeInput} from "dynamicformdjx";
834
+ import {dynamicCascadeInputRef, DynamicCascadeInput} from "dynamicformdjx";
531
835
 
532
836
  const dyCascadeRef = ref<dynamicCascadeInputRef | null>(null)
533
837
  const test2 = ref({
@@ -550,7 +854,7 @@ h3 {
550
854
 
551
855
  <template>
552
856
  <p>Cascade dynamicInput</p>
553
- <dynamic-cascade-input v-model="test2" :depth="5" ref="dyCascadeRef" is-controller/>
857
+ <dynamic-cascade-input v-model="test2" :depth="5" ref="dyCascadeRef" is-controller/>
554
858
  <pre>{{ test2 }}</pre>
555
859
  <p>Result</p>
556
860
  <button @click="setData">setData 8888</button>