yqform-edit 1.0.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.
Files changed (45) hide show
  1. package/.prettierrc +20 -0
  2. package/LICENSE +21 -0
  3. package/README.md +27 -0
  4. package/dist/demo.html +10 -0
  5. package/dist/fonts/element-icons.535877f5.woff +0 -0
  6. package/dist/fonts/element-icons.732389de.ttf +0 -0
  7. package/dist/yqform-edit.common.js +73796 -0
  8. package/dist/yqform-edit.css +1 -0
  9. package/dist/yqform-edit.umd.js +73806 -0
  10. package/dist/yqform-edit.umd.min.js +23 -0
  11. package/package.json +59 -0
  12. package/src/App.vue +126 -0
  13. package/src/assets/global.scss +12 -0
  14. package/src/assets/images/content/add.png +0 -0
  15. package/src/assets/images/content/arrow-left.png +0 -0
  16. package/src/assets/images/content/arrow-right.png +0 -0
  17. package/src/assets/images/content/delete.png +0 -0
  18. package/src/assets/images/content/edit.png +0 -0
  19. package/src/assets/images/content/electricity.png +0 -0
  20. package/src/assets/images/content/item_plus.png +0 -0
  21. package/src/assets/images/content/radio_checked.png +0 -0
  22. package/src/assets/images/content/radio_uncheck.png +0 -0
  23. package/src/assets/images/content/slider_btn.png +0 -0
  24. package/src/assets/images/content/wifi.png +0 -0
  25. package/src/assets/logo.png +0 -0
  26. package/src/components/common/content/components/form-item/components/Picker.vue +144 -0
  27. package/src/components/common/content/components/form-item/form-item.vue +829 -0
  28. package/src/components/common/content/components/form-item/index.js +5 -0
  29. package/src/components/common/content/components/input-box/input-box.vue +29 -0
  30. package/src/components/common/content/content.vue +504 -0
  31. package/src/components/common/content/index.js +5 -0
  32. package/src/components/common/rightConfig/formConfig.vue +319 -0
  33. package/src/components/dynamic-form/components/component-content.vue +530 -0
  34. package/src/components/dynamic-form/components/component-options.vue +1112 -0
  35. package/src/components/dynamic-form/components/component-type.vue +588 -0
  36. package/src/components/dynamic-form/dynamic-form.vue +120 -0
  37. package/src/components/dynamic-form/index.js +40 -0
  38. package/src/index.js +69 -0
  39. package/src/main.js +62 -0
  40. package/src/store/e7ingForm.js +10 -0
  41. package/src/store/getters.js +22 -0
  42. package/src/store/index.js +10 -0
  43. package/src/store/mutations.js +106 -0
  44. package/src/store/state.js +321 -0
  45. package/src/utils/index.js +23 -0
@@ -0,0 +1,588 @@
1
+ <template>
2
+ <div class="container">
3
+ <div class="import-btn">
4
+ <button @click="showModal = true">
5
+ 导入问卷
6
+ <el-tooltip class="item" effect="dark" placement="right">
7
+ <template slot="content">
8
+ <div>导入说明:</div>
9
+ <div>1、如果当前问卷中已经有题目,则只能为当前页导入题目,不能导入新的页面</div>
10
+ <div>2、如果当前问卷中没有题目,则可以为其导入题目,也可以导入分好页的问卷</div>
11
+ <div>3、目前只支持导入输入类题目、单选和多选题目</div>
12
+ <div>4、不支持直接在弹出框中编写题目</div>
13
+ <div>导入格式:</div>
14
+ <div>1、各页面之间、题目之间要用空行做区分;题目和选项都要在新的行中编写</div>
15
+ <div>2、输入类题目的输入框用“___”来代替</div>
16
+ <div>3、多选题要在题目后面标注“多选”字样说明</div>
17
+ </template>
18
+ <i class="el-icon-warning-outline"></i>
19
+ </el-tooltip>
20
+ </button>
21
+ </div>
22
+ <div class="title">基本类型</div>
23
+ <div>
24
+ <draggable
25
+ :list="baseTypes"
26
+ :group="{ name: 'form', pull: 'clone', put: 'false' }"
27
+ :sort="false"
28
+ class="content"
29
+ :clone="handleClone">
30
+ <div class="content-item" v-for="(item, index) in baseTypes" :key="index" :ref="'item' + (index + 1)">
31
+ {{ item.label }}
32
+ </div>
33
+ </draggable>
34
+ </div>
35
+ <div class="title">时间相关</div>
36
+ <div>
37
+ <draggable
38
+ :list="timeTypes"
39
+ :group="{ name: 'form', pull: 'clone', put: 'false' }"
40
+ :sort="false"
41
+ class="content"
42
+ :clone="handleClone">
43
+ <div class="content-item" v-for="(item, index) in timeTypes" :key="index" :ref="'item' + (index + 1)">
44
+ {{ item.label }}
45
+ </div>
46
+ </draggable>
47
+ </div>
48
+ <div class="title">其它</div>
49
+ <div>
50
+ <draggable
51
+ :list="otherTypes"
52
+ :group="{ name: 'form', pull: 'clone', put: 'false' }"
53
+ :sort="false"
54
+ class="content"
55
+ :clone="handleClone">
56
+ <div class="content-item" v-for="(item, index) in otherTypes" :key="index" :ref="'item' + (index + 1)">
57
+ {{ item.label }}
58
+ </div>
59
+ </draggable>
60
+ </div>
61
+ <div class="title" v-if="showCustomizeType">自定义类型</div>
62
+ <div v-if="showCustomizeType">
63
+ <draggable
64
+ :list="customizeTypes"
65
+ :group="{ name: 'form', pull: 'clone', put: 'false' }"
66
+ :sort="false"
67
+ class="content"
68
+ :clone="handleClone">
69
+ <div class="content-item" v-for="(item, index) in customizeTypes" :key="index" :ref="'item' + (index + 1)">
70
+ {{ item.label }}
71
+ </div>
72
+ </draggable>
73
+ </div>
74
+ <div class="float" ref="float"></div>
75
+ <el-dialog title="导入问卷" :visible.sync="showModal" :close-on-click-modal="false">
76
+ <div class="dialog-content">
77
+ <div class="submit-btn" @click="doneCopy">完成</div>
78
+ <div id="modalTextarea" class="left-copy">
79
+ <el-input type="textarea" :rows="rowCount" placeholder="请输入内容" v-model="itemContent"> </el-input>
80
+ </div>
81
+ <div class="center-border"></div>
82
+ <div class="right-preview">
83
+ <ComponentContent
84
+ :noControl="true"
85
+ :modalContentHeight="modalContentHeight"
86
+ :form="singlePageObj"
87
+ :currentKey="currentKey"
88
+ @onSelect="selectPreviewItem" />
89
+ </div>
90
+ <div class="center-border" v-if="currentKey"></div>
91
+ <div class="right-options" v-if="currentKey">
92
+ <ComponentOptions :copyPreview="true" :item="currentCopyItem" :form="form" />
93
+ </div>
94
+ </div>
95
+ </el-dialog>
96
+ </div>
97
+ </template>
98
+
99
+ <script>
100
+ import { mapState } from 'vuex'
101
+ import { generateGid, generateKey, randomIdFactory } from '@/utils'
102
+ import draggable from 'vuedraggable'
103
+ import ComponentContent from '@/components/dynamic-form/components/component-content'
104
+ import ComponentOptions from './component-options.vue'
105
+ import _ from 'lodash'
106
+
107
+ export default {
108
+ name: 'component-type',
109
+ components: { draggable, ComponentContent, ComponentOptions },
110
+ inject: ['expendQuestion'],
111
+ props: ['currentPageIndex', 'form'],
112
+ data() {
113
+ return {
114
+ list2: [],
115
+ showCustomizeType: false, // 标记是否展示自定义的题目
116
+ customizeTypes: [],
117
+ dragItem: null,
118
+ moveItem: null,
119
+ showModal: false,
120
+ rowCount: 15,
121
+ modalContentHeight: 0,
122
+ itemContent: '',
123
+ singlePageObj: {}, // 不是第一次粘贴时将粘贴的数据保存在该对象中
124
+ copyDoneClick: false, // 标志是否点击了完成按钮
125
+ currentCopyItem: {}, // 当前选中粘贴中的项目
126
+ currentKey: '', // 当前选中的粘贴中的项目key
127
+ copyPageIndex: 0,
128
+ }
129
+ },
130
+ computed: {
131
+ ...mapState('e7ingForm', ['baseTypes', 'timeTypes', 'otherTypes']),
132
+ },
133
+ watch: {
134
+ itemContent: {
135
+ handler(newContent) {
136
+ if (newContent) {
137
+ this.getFormWithCopyStr(newContent)
138
+ }
139
+ },
140
+ },
141
+ showModal(newShowValue) {
142
+ // 如果在第一次打开弹窗时问卷中已经有题目了,则要重置数据
143
+ if (newShowValue) {
144
+ this.copyDoneClick = false
145
+ this.initModalData()
146
+ this.currentCopyItem = {}
147
+ setTimeout(() => {
148
+ const height = document.getElementById('modalTextarea').offsetHeight
149
+ this.modalContentHeight = height - 20
150
+ // 弹窗打开时计算左侧能够展示的行数
151
+ this.rowCount = Math.ceil(height / 23)
152
+ })
153
+ } else {
154
+ this.currentKey = ''
155
+ }
156
+ },
157
+ },
158
+ mounted() {
159
+ if (this.expendQuestion && this.expendQuestion.length > 0) {
160
+ this.expendQuestion.forEach((q) => {
161
+ if (q === 'picker') {
162
+ const pickerObj = {
163
+ label: '自定义下拉',
164
+ key: '',
165
+ type: 'customizePicker',
166
+ title: '请输入题目名称',
167
+ subtitle: '',
168
+ placeholder: '请选择',
169
+ required: true,
170
+ emsg: '当前题目答案不能为空',
171
+ weight: false,
172
+ depends: [],
173
+ options: [
174
+ {
175
+ id: randomIdFactory(),
176
+ content: '选项1',
177
+ default: true,
178
+ },
179
+ ],
180
+ }
181
+ this.customizeTypes.push(pickerObj)
182
+ this.showCustomizeType = true
183
+ }
184
+ })
185
+ }
186
+ },
187
+ methods: {
188
+ // 判断是否需要重置数据
189
+ isInitData() {
190
+ if (
191
+ this.form.groups.length > 1 ||
192
+ (this.form.groups.length === 1 && this.form.groups[0].gid !== 'templeteData')
193
+ ) {
194
+ this.initModalData()
195
+ }
196
+ },
197
+ // 判断弹窗当前页面是否有数据
198
+ hasDialogObj() {},
199
+ // 初始化弹窗中的数据
200
+ initModalData() {
201
+ const tempForm = {
202
+ spec: {
203
+ name: '',
204
+ theme: {
205
+ name: 'show',
206
+ color: '#07c160',
207
+ header: '',
208
+ footer: '',
209
+ },
210
+ unit: '页',
211
+ },
212
+ groups: [
213
+ {
214
+ gid: 'fjkdslafjsda',
215
+ spec: {
216
+ title: '',
217
+ subtitle: '',
218
+ unit: '页',
219
+ },
220
+ items: [],
221
+ },
222
+ ],
223
+ }
224
+ this.singlePageObj = tempForm
225
+ this.itemContent = ''
226
+ this.aleadyCopy = true
227
+ },
228
+ /** 剔除label属性,添加key */
229
+ handleClone(cloneData) {
230
+ const data = _.omit(_.cloneDeep(cloneData), ['label'])
231
+ this.$set(data, 'key', generateKey())
232
+ return data
233
+ },
234
+ down(item, index) {
235
+ let div = this.$refs['item' + (index + 1)][0]
236
+ let float = this.$refs.float
237
+ console.log(this.$refs, item, index)
238
+ console.log(float, div)
239
+ float.innerHTML = item.label
240
+ float.style.display = 'block'
241
+ document.onmousemove = (e) => {
242
+ float.style.left = e.pageX - 54 + 'px'
243
+ float.style.top = e.pageY - 16 + 'px'
244
+ // console.log(e.offsetX,e.offsetY,float.style.left)
245
+ }
246
+ document.onmouseup = () => {
247
+ float.innerHTML = ''
248
+ float.style.display = 'none'
249
+ }
250
+ },
251
+ // 判断一行文字是否是分组标题
252
+ isPartTitle(line) {
253
+ let tag = false
254
+ const reg = /^第[\u4e00-\u9fa5]{1}(部分|阶段|组|页)/g
255
+ if (reg.test(line)) {
256
+ tag = true
257
+ }
258
+ return tag
259
+ },
260
+ // 判断解析出的第一部分是否是项目题目
261
+ isProjectTitle(partArr) {
262
+ let isTitle = false
263
+ if (partArr.length <= 3) {
264
+ /* 如果第一行不为空并且第一行不是分组标题并且不是输入类题目
265
+ 那么久有理由认为这是标题数组 */
266
+ if (
267
+ partArr[0] &&
268
+ !this.isPartTitle(partArr[0]) &&
269
+ (!partArr[1].includes('————') || !partArr[1].includes('___'))
270
+ ) {
271
+ isTitle = true
272
+ }
273
+ }
274
+ return isTitle
275
+ },
276
+ // 由题目数组得到题目对象
277
+ getFormatItemData(questionArray) {
278
+ if (questionArray.length === 2 && (questionArray[1].includes('————') || questionArray[1].includes('___'))) {
279
+ // 判断是否是输入框题目
280
+ const inputObj = this.handleClone(this.baseTypes.find((item) => item.type === 'text'))
281
+ const inputTitle = questionArray[0]
282
+ const itemIndexStr = inputTitle.match(/^\d+(.|、| ){0,1}/)
283
+ if (itemIndexStr) {
284
+ // 如果自己写了标号,则将其去掉
285
+ inputObj.title = inputTitle.split(itemIndexStr[0])[1]
286
+ } else {
287
+ inputObj.title = inputTitle
288
+ }
289
+ return inputObj
290
+ } else if (questionArray[0].includes('多选')) {
291
+ // 判断是否是多选题
292
+ return this.getRadioOrCheckboxObj('checkbox', questionArray)
293
+ } else {
294
+ return this.getRadioOrCheckboxObj('radio', questionArray)
295
+ }
296
+ },
297
+ // 获取单选或多选题对象
298
+ getRadioOrCheckboxObj(type, questionArray) {
299
+ const checkBoxObj = this.handleClone(this.baseTypes.find((item) => item.type === type))
300
+ checkBoxObj.options.pop()
301
+ questionArray.forEach((item, index) => {
302
+ if (index === 0) {
303
+ // 匹配问题前面的标号
304
+ const itemIndexStr = item.match(/^\d+(.|、| ){0,1}/)
305
+ if (itemIndexStr) {
306
+ // 如果自己写了标号,则将其去掉
307
+ checkBoxObj.title = item.split(itemIndexStr[0])[1]
308
+ } else {
309
+ checkBoxObj.title = item
310
+ }
311
+ } else {
312
+ const itemObj = {
313
+ id: randomIdFactory(),
314
+ content: item,
315
+ default: true,
316
+ }
317
+ checkBoxObj.options.push(itemObj)
318
+ }
319
+ })
320
+ return checkBoxObj
321
+ },
322
+ // 将粘贴的字符串解析为表单的数据结构
323
+ getFormWithCopyStr(formStr) {
324
+ const form = {
325
+ spec: {
326
+ name: '', // 问卷名
327
+ theme: {
328
+ name: 'show',
329
+ color: '#07c160',
330
+ header: '',
331
+ footer: '',
332
+ },
333
+ unit: '',
334
+ },
335
+ groups: [],
336
+ }
337
+ const everyLineArr = formStr.split('\n')
338
+ // 分离出每个部分
339
+ const partArr = []
340
+ let start = 0
341
+ let end = 0
342
+ everyLineArr.forEach((item, index) => {
343
+ if (this.isPartTitle(item)) {
344
+ end = index
345
+ partArr.push(everyLineArr.slice(start, end))
346
+ start = end
347
+ }
348
+ })
349
+ if (end + 1 < everyLineArr.length) {
350
+ partArr.push(everyLineArr.slice(end))
351
+ }
352
+ // 如果最开始的部分为空行则删除掉
353
+ if (partArr[0].length === 0) {
354
+ partArr.shift()
355
+ }
356
+ // 如果识别出第一部分为项目标题,则删除掉
357
+ if (this.isProjectTitle(partArr[0])) {
358
+ partArr.shift()
359
+ }
360
+ // 分离出每一个题目
361
+ partArr.forEach((item1) => {
362
+ // 过滤掉可能为空的部分
363
+ if (item1.length > 0) {
364
+ let isTitle = false
365
+ const partObj = {
366
+ gid: generateGid(),
367
+ spec: {
368
+ title: '',
369
+ subtitle: '',
370
+ unit: '',
371
+ },
372
+ items: [],
373
+ }
374
+ const sliceIndex = [0, 0] // 一个题目前后空行的index值
375
+ item1.forEach((item2, index2) => {
376
+ // 判断每一部分的第一行是否是部分名称
377
+ if (index2 === 0 && this.isPartTitle(item2)) {
378
+ const unitStr = item2.match(/^第[\u4e00-\u9fa5]{1}(部分|阶段|组|页)(:|:| ){0,1}/g)
379
+ if (unitStr.length > 0) {
380
+ const unit = unitStr[0].match(/部分|阶段|组|页/g)
381
+ partObj.spec.title = item2.split(unitStr[0])[1]
382
+ partObj.spec.unit = unit[0]
383
+ form.spec.unit = unit[0]
384
+ }
385
+ } else if (item2 === '') {
386
+ sliceIndex.shift()
387
+ sliceIndex.push(index2 + 1)
388
+ // 截取出一个题目,用数组保存
389
+ const questionArray = item1.slice(sliceIndex[0], sliceIndex[1] - 1)
390
+ /* 忽略多个换行导致的空数组问题(所以要大于0)
391
+ 忽略项目标题所占的数组(所以要大于1)
392
+ 题目的标题不能是是分页标题 */
393
+ if (questionArray.length > 1 && !this.isPartTitle(questionArray[0])) {
394
+ const itemObj = this.getFormatItemData(questionArray)
395
+ partObj.items.push(itemObj)
396
+ }
397
+ }
398
+ })
399
+ if (sliceIndex[1] < item1.length) {
400
+ const itemObj = this.getFormatItemData(item1.slice(sliceIndex[1]))
401
+ partObj.items.push(itemObj)
402
+ }
403
+ if (!partObj.spec.unit) {
404
+ partObj.spec.unit = '页'
405
+ }
406
+ if (isTitle) {
407
+ form.spec.name = item1.join(' ').trim()
408
+ } else {
409
+ form.groups.push(partObj)
410
+ }
411
+ }
412
+ })
413
+ if (this.aleadyCopy) {
414
+ // 如果粘贴过题目,则只有一页数据,将该页数据传递给展示组件
415
+ this.singlePageObj = form
416
+ console.log('this.singlePageObj', this.singlePageObj)
417
+ this.currentCopyItem = form.groups[this.copyPageIndex].items[0]
418
+ this.currentKey = this.currentCopyItem.key
419
+ } else {
420
+ // 如果没有粘贴过题目,则更新整个表单项数据
421
+ console.log('[ form ] >', form)
422
+ }
423
+ },
424
+ // 粘贴完成的回调
425
+ doneCopy() {
426
+ this.showModal = false
427
+ this.copyDoneClick = true
428
+ if (this.aleadyCopy) {
429
+ let page = this.singlePageObj.groups[this.copyPageIndex]
430
+ // 如果已经粘贴过,则将这次粘贴的一页题目添加到问卷的当前页中
431
+ if (page.items && page.items.length > 0) {
432
+ page.items.forEach((item) => {
433
+ this.addItemsToCurrentPage(item)
434
+ })
435
+ }
436
+ }
437
+ },
438
+ // 选择弹框中的项目
439
+ selectPreviewItem(key) {
440
+ console.log('key', key)
441
+ this.currentCopyItem = this.singlePageObj.groups[0].items.find((item) => item.key === key)
442
+ this.currentKey = key
443
+ },
444
+ addItemsToCurrentPage(item) {
445
+ this.form.groups[this.currentPageIndex].items.push(item)
446
+ },
447
+ },
448
+ }
449
+ </script>
450
+
451
+ <style scoped lang="scss">
452
+ ::-webkit-scrollbar {
453
+ width: 0;
454
+ }
455
+ .container {
456
+ width: 100%;
457
+ .import-btn {
458
+ margin-bottom: 10px;
459
+ button {
460
+ height: 30px;
461
+ width: 100px;
462
+ border: none;
463
+ color: white;
464
+ background: rgb(20, 157, 248);
465
+ }
466
+ }
467
+ .title {
468
+ text-align: left;
469
+ font-size: 18.08px;
470
+ font-weight: 400;
471
+ letter-spacing: 0px;
472
+ color: rgba(128, 128, 128, 1);
473
+ padding-bottom: 8px;
474
+ border-bottom: 1px solid rgba(159, 237, 232, 1);
475
+ }
476
+
477
+ .content {
478
+ display: flex;
479
+ flex-wrap: wrap;
480
+ justify-content: space-between;
481
+ padding-top: 3px;
482
+ margin-bottom: 20px;
483
+
484
+ .content-item {
485
+ width: 108px;
486
+ height: 35px;
487
+ text-align: center;
488
+ font-size: 16px;
489
+ font-weight: 400;
490
+ letter-spacing: 0px;
491
+ color: rgba(56, 56, 56, 1);
492
+ line-height: 35px;
493
+ border: 1px solid rgba(166, 166, 166, 1);
494
+ margin-top: 13px;
495
+ cursor: move;
496
+ }
497
+ }
498
+
499
+ .float {
500
+ display: none;
501
+ width: 108px;
502
+ height: 35px;
503
+ text-align: center;
504
+ font-size: 16px;
505
+ font-weight: 400;
506
+ letter-spacing: 0px;
507
+ color: rgba(56, 56, 56, 1);
508
+ line-height: 35px;
509
+ border: 1px solid rgba(166, 166, 166, 1);
510
+ margin-top: 13px;
511
+ position: absolute;
512
+ pointer-events: none;
513
+ }
514
+
515
+ .demo {
516
+ position: relative;
517
+
518
+ .demo-item {
519
+ position: absolute;
520
+ width: 108px;
521
+ height: 35px;
522
+ text-align: center;
523
+ font-size: 16px;
524
+ font-weight: 400;
525
+ letter-spacing: 0px;
526
+ color: rgba(56, 56, 56, 1);
527
+ line-height: 35px;
528
+ border: 1px solid rgba(166, 166, 166, 1);
529
+ }
530
+ }
531
+ :deep(.el-dialog) {
532
+ width: 70%;
533
+ height: 60%;
534
+ }
535
+ :deep(.el-dialog__wrapper) {
536
+ overflow: hidden;
537
+ }
538
+ :deep(.el-dialog__body) {
539
+ height: calc(100% - 60px);
540
+ padding: 5px 20px 0 20px;
541
+ .dialog-content {
542
+ width: 100%;
543
+ height: 100%;
544
+ display: flex;
545
+ position: relative;
546
+ .submit-btn {
547
+ position: absolute;
548
+ width: 60px;
549
+ height: 30px;
550
+ line-height: 30px;
551
+ text-align: center;
552
+ top: -45px;
553
+ right: 50px;
554
+ z-index: 999;
555
+ color: white;
556
+ background: rgb(20, 157, 248);
557
+ cursor: pointer;
558
+ }
559
+ .left-copy {
560
+ // width: 33%;
561
+ flex: 1;
562
+ height: 100%;
563
+ // padding-right: 10px;
564
+ }
565
+ .center-border {
566
+ height: 97%;
567
+ width: 10px;
568
+ border: 1px dotted black;
569
+ background: rgb(238, 229, 229);
570
+ margin: 0 10px;
571
+ }
572
+ .right-preview {
573
+ // width: 34%;
574
+ flex: 1;
575
+ height: 760px;
576
+ overflow: auto;
577
+ // margin-left: 10px;
578
+ }
579
+ .right-options {
580
+ // width: 33%;
581
+ flex: 1;
582
+ height: 100%;
583
+ overflow-y: scroll;
584
+ }
585
+ }
586
+ }
587
+ }
588
+ </style>
@@ -0,0 +1,120 @@
1
+ <template>
2
+ <div class="dynamic-form-container">
3
+ <ComponentType :currentPageIndex="currentPageIndex" :form="form" />
4
+ <ComponentContent
5
+ :currentKey="currentItemKey"
6
+ :currentPageIndex="currentPageIndex"
7
+ :form="form"
8
+ @onSelect="setItemIndex" />
9
+ <ComponentOptions
10
+ :item="currentItem"
11
+ :currentItemKey="currentItemKey"
12
+ :currentPageIndex="currentPageIndex"
13
+ :form="form" />
14
+ </div>
15
+ </template>
16
+
17
+ <script>
18
+ import ComponentType from '@/components/dynamic-form/components/component-type'
19
+ import ComponentContent from '@/components/dynamic-form/components/component-content'
20
+ import ComponentOptions from '@/components/dynamic-form/components/component-options'
21
+
22
+ export default {
23
+ name: 'DynamicForm',
24
+ components: { ComponentOptions, ComponentContent, ComponentType },
25
+ data() {
26
+ return {
27
+ currentPageIndex: 0,
28
+ currentItemKey: '',
29
+ currentPage: {
30
+ gid: 'templeteData',
31
+ spec: {
32
+ title: '',
33
+ subtitle: '',
34
+ unit: '页',
35
+ },
36
+ items: [],
37
+ },
38
+ currentItem: {},
39
+ currentSpec: {},
40
+ dependValues: [],
41
+ movePage: 0,
42
+ }
43
+ },
44
+ props: {
45
+ form: {
46
+ type: Object,
47
+ required: false,
48
+ },
49
+ },
50
+ provide() {
51
+ return {
52
+ form: this.form,
53
+ currentItem: () => this.currentItem,
54
+ setPageIndex: this.setPageIndex,
55
+ currentFormSpec: this.currentFormSpec,
56
+ currentSpec: this.currentSpec,
57
+ dependValues: this.dependValues,
58
+ }
59
+ },
60
+ computed: {
61
+ currentFormSpec() {
62
+ return this.form.spec
63
+ },
64
+ },
65
+ watch: {
66
+ currentItemKey: {
67
+ handler(key) {
68
+ this.currentItem = this.form.groups[this.currentPageIndex].items.find((item) => item.key === key)
69
+ },
70
+ immediate: true,
71
+ },
72
+ currentPageIndex: {
73
+ handler(index) {
74
+ this.currentItem = this.form.groups[index].items.find((item) => item.key === this.currentItemKey)
75
+ this.currentSpec = this.form.groups[index].spec
76
+ },
77
+ },
78
+ form: {
79
+ handler() {
80
+ // console.log('[ newForm ] >', JSON.stringify(newForm))
81
+ // this.totalPage = newForm.groups.length
82
+ this.currentItem = this.form.groups[this.currentPageIndex].items.find(
83
+ (item) => item.key === this.currentItemKey,
84
+ )
85
+ },
86
+ deep: true,
87
+ },
88
+ dependValues: {
89
+ handler(newValues) {
90
+ console.log('[ newValues ] >', newValues)
91
+ },
92
+ },
93
+ },
94
+ methods: {
95
+ setItemIndex(index) {
96
+ this.currentItemKey = index
97
+ },
98
+ setPageIndex(index) {
99
+ this.currentPageIndex = index
100
+ },
101
+ resetPage() {
102
+ this.currentPageIndex = 0
103
+ this.currentItemKey = ''
104
+ },
105
+ clearDepend() {
106
+ this.dependValues = []
107
+ },
108
+ },
109
+ mounted() {},
110
+ }
111
+ </script>
112
+
113
+ <style scoped lang="scss">
114
+ .dynamic-form-container {
115
+ width: 100%;
116
+ display: grid;
117
+ grid-template-columns: 22% 32% 22%;
118
+ gap: 12%;
119
+ }
120
+ </style>