vue_zhongyou 1.0.9 → 1.0.11

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vue_zhongyou",
3
- "version": "1.0.9",
3
+ "version": "1.0.11",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "keywords": [],
@@ -8,8 +8,9 @@
8
8
  v-model="formData[field.field]"
9
9
  :type="field.type === 'textarea' ? 'textarea' : 'text'"
10
10
  :label="field.label"
11
+ :label-width="60"
11
12
  :placeholder="field.placeholder || `请输入${field.label}`"
12
- :rules="field.rules"
13
+ :rules="field.rules || []"
13
14
  :maxlength="field.maxlength"
14
15
  :rows="field.rows || (field.type === 'textarea' ? 3 : undefined)"
15
16
  :autosize="field.type === 'textarea'"
@@ -19,7 +20,7 @@
19
20
  />
20
21
 
21
22
  <!-- 单选 -->
22
- <van-field v-else-if="field.type === 'radio'" name="radio" :label="field.label">
23
+ <van-field v-else-if="field.type === 'radio'" name="radio" :label="field.label" :rules="field.rules || []">
23
24
  <template #input>
24
25
  <van-radio-group
25
26
  v-model="formData[field.field]"
@@ -40,7 +41,7 @@
40
41
 
41
42
 
42
43
  <!-- 多选 -->
43
- <van-field v-else-if="field.type === 'checkbox'" name="checkboxGroup" :label="field.label">
44
+ <van-field v-else-if="field.type === 'checkbox'" name="checkboxGroup" :label="field.label" :rules="field.rules || []">
44
45
  <template #input>
45
46
  <van-checkbox-group
46
47
  v-model="formData[field.field]"
@@ -67,6 +68,7 @@
67
68
  :label="field.label"
68
69
  :placeholder="field.placeholder || `请选择${field.label}`"
69
70
  :model-value="getSelectLabel(field)"
71
+ :rules="field.rules || []"
70
72
  @click="openSelect(field)"
71
73
  />
72
74
 
@@ -77,10 +79,24 @@
77
79
  readonly
78
80
  :label="field.label"
79
81
  :placeholder="field.placeholder || '请选择时间范围'"
82
+ :rules="field.rules || []"
80
83
  :model-value="getDateRangeText(field)"
81
84
  @click="openDateRange(field)"
82
85
  />
83
86
 
87
+ <!-- 日期时间范围 -->
88
+ <van-field
89
+ v-else-if="field.type === 'datetimeRange'"
90
+ is-link
91
+ readonly
92
+ :label="field.label"
93
+ :label-width="60"
94
+ :placeholder="field.placeholder || '请选择日期时间范围'"
95
+ :model-value="getDateTimeRangeText(field)"
96
+ :rules="field.rules || []"
97
+ @click="openDateTimeRange(field)"
98
+ />
99
+
84
100
  <!-- 地址选择 -->
85
101
  <van-field
86
102
  v-else-if="field.type === 'address'"
@@ -88,10 +104,24 @@
88
104
  readonly
89
105
  :label="field.label"
90
106
  :placeholder="field.placeholder || '请选择地址'"
107
+ :rules="field.rules || []"
91
108
  :model-value="getAddressText(field)"
92
109
  @click="openAddressPicker(field)"
93
110
  />
94
111
 
112
+ <!-- 单个日期时间 -->
113
+ <van-field
114
+ v-else-if="field.type === 'datetime'"
115
+ is-link
116
+ readonly
117
+ :label="field.label"
118
+ :label-width="60"
119
+ :placeholder="field.placeholder || '请选择日期时间'"
120
+ :model-value="getDateTimeText(field)"
121
+ :rules="field.rules || []"
122
+ @click="openDateTime(field)"
123
+ />
124
+
95
125
  <!-- 自定义内容 -->
96
126
  <slot v-else :field="field" :value="formData[field.field]" />
97
127
  </template>
@@ -122,6 +152,7 @@
122
152
  @confirm="onSelectConfirm"
123
153
  @cancel="closeSelect"
124
154
  />
155
+
125
156
  </van-popup>
126
157
 
127
158
  <!-- 时间范围 -->
@@ -135,6 +166,33 @@
135
166
  @cancel="closeDateRange"
136
167
  />
137
168
 
169
+ <!-- 日期时间范围选择器 -->
170
+ <van-popup v-model:show="dateTimeRangePopup.visible" position="bottom" round>
171
+ <div class="datetime-range-popup">
172
+ <div class="popup-header">
173
+ <span class="cancel-btn" @click="closeDateTimeRange">取消</span>
174
+ <span class="confirm-btn" @click="confirmDateTimeRange">确定</span>
175
+ </div>
176
+ <div class="time_title">开始时间</div>
177
+ <nut-date-picker
178
+ v-model="dateTimeRangePopup.startTime"
179
+ type="datetime"
180
+ :show-toolbar="false"
181
+ :filter="startTimeFilter"
182
+ @change="(e)=>onDateTimeChange(e,'startTime')"
183
+ />
184
+ <div class="time_title">结束时间</div>
185
+ <nut-date-picker
186
+ v-model="dateTimeRangePopup.endTime"
187
+ type="datetime"
188
+ :show-toolbar="false"
189
+ :filter="endTimeFilter"
190
+ @change="(e)=>onDateTimeChange(e,'endTime')"
191
+ />
192
+ </div>
193
+ </van-popup>
194
+
195
+
138
196
  <!-- 地址 -->
139
197
  <van-popup v-model:show="addressPopup.visible" position="bottom" round>
140
198
  <van-area
@@ -144,11 +202,30 @@
144
202
  @cancel="closeAddressPicker"
145
203
  />
146
204
  </van-popup>
205
+
206
+ <!-- 单个日期时间 -->
207
+ <van-popup v-model:show="dateTimePopup.visible" position="bottom" round>
208
+ <div class="datetime-popup">
209
+ <div class="popup-header">
210
+ <span class="cancel-btn" @click="closeDateTime">取消</span>
211
+ <span class="confirm-btn" @click="confirmDateTime">确定</span>
212
+ </div>
213
+ <nut-date-picker
214
+ v-model="dateTimePopup.selectedTime"
215
+ type="datetime"
216
+ :show-toolbar="false"
217
+ :filter="dateTimeFilter"
218
+ @change="onSingleDateTimeChange"
219
+ />
220
+ </div>
221
+ </van-popup>
147
222
  </template>
148
223
 
149
224
  <script setup>
150
225
  import { computed, reactive, ref, watch } from 'vue'
151
226
  import { areaList } from '@vant/area-data'
227
+ import { showNotify } from 'vant';
228
+
152
229
  const props = defineProps({
153
230
  schema: {
154
231
  type: Array,
@@ -188,6 +265,16 @@ const dateRangePopup = ref({
188
265
  minDate: null,
189
266
  maxDate: null
190
267
  })
268
+ // 日期时间范围弹窗状态
269
+ const dateTimeRangePopup = ref({
270
+ visible: false, // 弹窗显示状态
271
+ field: null, // 当前操作的字段
272
+ startTime: new Date(), // 开始时间
273
+ endTime: new Date(), // 结束时间
274
+ currentDate: new Date(), // 当前选中日期
275
+ currentHour: '8' // 当前选择的小时值,默认为'12'
276
+ })
277
+
191
278
 
192
279
  const addressPopup = ref({
193
280
  visible: false,
@@ -196,6 +283,17 @@ const addressPopup = ref({
196
283
  columnsPlaceholder: null
197
284
  })
198
285
 
286
+ // 单个日期时间弹窗状态
287
+ const dateTimePopup = ref({
288
+ visible: false,
289
+ field: null,
290
+ selectedTime: new Date(),
291
+ currentHour: '8'
292
+ })
293
+
294
+
295
+
296
+
199
297
  const normalizedSchema = computed(() =>
200
298
  props.schema.map((item) => ({
201
299
  ...item,
@@ -206,6 +304,8 @@ const normalizedSchema = computed(() =>
206
304
  const getDefaultValue = (type) => {
207
305
  if (type === 'checkbox') return []
208
306
  if (type === 'dateRange') return ['', '']
307
+ if (type === 'datetimeRange') return ['', '']
308
+ if (type === 'datetime') return ''
209
309
  if (type === 'address') return { province: '', city: '', county: '', code: '' }
210
310
  return ''
211
311
  }
@@ -303,6 +403,137 @@ const closeDateRange = () => {
303
403
  dateRangePopup.value.visible = false
304
404
  }
305
405
 
406
+ // 打开日期时间范围选择
407
+ const openDateTimeRange = (field) => {
408
+ dateTimeRangePopup.value.visible = true
409
+ dateTimeRangePopup.value.field = field
410
+
411
+ // 设置默认时间:开始时间为当天8:30,结束时间为当天17:30
412
+ const today = new Date()
413
+ const defaultStartTime = new Date(today)
414
+ defaultStartTime.setHours(8, 30, 0, 0)
415
+ const defaultEndTime = new Date(today)
416
+ defaultEndTime.setHours(17, 30, 0, 0)
417
+
418
+ // 将field的startTime和endTime转换为日期时间格式
419
+ dateTimeRangePopup.value.startTime = typeof field.startTime === 'string' ? reverseFormatDateTime(field.startTime) : defaultStartTime
420
+ dateTimeRangePopup.value.endTime = typeof field.endTime === 'string' ? reverseFormatDateTime(field.endTime) : defaultEndTime
421
+ }
422
+
423
+ const closeDateTimeRange = () => {
424
+ dateTimeRangePopup.value.visible = false
425
+ }
426
+
427
+ // 日期时间选择器改变事件处理
428
+ const onDateTimeChange = (params,type) => {
429
+
430
+ // 更新当前选择的小时值
431
+ if (params && params.selectedValue && params.columnIndex === 3) {
432
+ // 更新小时值
433
+ // 根据小时值设置默认分钟值
434
+ if (['08', '17'].includes(params.selectedValue[3])) {
435
+ // 小时为08或17时,分钟设为30
436
+ if(type === 'startTime'){
437
+ dateTimeRangePopup.value.startTime.setMinutes(30)
438
+ }else{
439
+ dateTimeRangePopup.value.endTime.setMinutes(30)
440
+ }
441
+ } else if (['12', '13'].includes(params.selectedValue[3])) {
442
+ // 小时为12或13时,分钟设为00
443
+ if(type === 'startTime'){
444
+ dateTimeRangePopup.value.startTime.setMinutes(0)
445
+ }else{
446
+ dateTimeRangePopup.value.endTime.setMinutes(0)
447
+ }
448
+ }
449
+ if(type === 'startTime'){
450
+ dateTimeRangePopup.value.currentStartHour = params.selectedValue[3];
451
+ }else{
452
+ dateTimeRangePopup.value.currentEndHour = params.selectedValue[3];
453
+ }
454
+
455
+ }
456
+ }
457
+
458
+ // 确认日期时间范围选择
459
+ const confirmDateTimeRange = () => {
460
+ if(!dateTimeRangePopup.value.startTime || !dateTimeRangePopup.value.endTime){
461
+ return
462
+ }
463
+ if(dateTimeRangePopup.value.startTime.getTime()>=dateTimeRangePopup.value.endTime.getTime()){
464
+ showNotify({ type: 'warning', message: '结束时间必须晚于开始时间' });
465
+ return
466
+ }
467
+ console.log(dateTimeRangePopup.value.startTime.getTime()<dateTimeRangePopup.value.endTime.getTime())
468
+ // 格式化日期时间范围
469
+ const startDateStr = formatDateTime(dateTimeRangePopup.value.startTime)
470
+ const endDateStr = formatDateTime(dateTimeRangePopup.value.endTime)
471
+ console.log('开始时间:', startDateStr);
472
+ console.log('结束时间:', endDateStr);
473
+ // 更新表单数据
474
+ if (dateTimeRangePopup.value.field) {
475
+ updateFieldValue(dateTimeRangePopup.value.field.field, [startDateStr, endDateStr])
476
+ }
477
+ // 关闭弹窗
478
+ closeDateTimeRange()
479
+ }
480
+
481
+ // 日期时间选择器筛选函数
482
+ // 通用时间过滤函数,根据类型(开始时间/结束时间)和当前小时值过滤选项
483
+ const timeFilter = (type, options, timeType) => {
484
+ if (type === 'hour') {
485
+ return options.filter(option => ['08', '12', '13', '17'].includes(option.value))
486
+ } else if (type === 'minute') {
487
+ // 获取对应时间类型的当前小时值
488
+ const currentHour = timeType === 'start'
489
+ ? dateTimeRangePopup.value.currentStartHour
490
+ : dateTimeRangePopup.value.currentEndHour
491
+
492
+ // 当小时位是'12'或者'13'时,分钟只保留'00'
493
+ if (['12','13'].includes(currentHour)) {
494
+ return options.filter(option => ['00'].includes(option.value))
495
+ } else {
496
+ return options.filter(option => ['30'].includes(option.value))
497
+ }
498
+ }
499
+ return options
500
+ }
501
+
502
+ // 开始时间和结束时间的过滤器包装函数
503
+ const startTimeFilter = (type, options) => timeFilter(type, options, 'start')
504
+ const endTimeFilter = (type, options) => timeFilter(type, options, 'end')
505
+
506
+ // 单个日期时间的过滤函数
507
+ const dateTimeFilter = (type, options) => {
508
+ if (type === 'hour') {
509
+ return options.filter(option => ['08', '12', '13', '17'].includes(option.value))
510
+ } else if (type === 'minute') {
511
+ // 当小时位是'12'或者'13'时,分钟只保留'00'
512
+ if (['12','13'].includes(dateTimePopup.value.currentHour)) {
513
+ return options.filter(option => ['00'].includes(option.value))
514
+ } else {
515
+ return options.filter(option => ['30'].includes(option.value))
516
+ }
517
+ }
518
+ return options
519
+ }
520
+ // 格式化日期时间为字符串
521
+ var formatDateTime = (date) => {
522
+ const y = date.getFullYear()
523
+ const m = `${date.getMonth() + 1}`.padStart(2, '0')
524
+ const d = `${date.getDate()}`.padStart(2, '0')
525
+ const h = `${date.getHours()}`.padStart(2, '0')
526
+ const min = `${date.getMinutes()}`.padStart(2, '0')
527
+ return `${y}-${m}-${d} ${h}:${min}`
528
+ }
529
+ // 解析字符串为日期时间对象
530
+ var reverseFormatDateTime = (dateStr) => {
531
+ const [datePart, timePart] = dateStr.split(' ')
532
+ const [y, m, d] = datePart.split('-')
533
+ const [h, min] = timePart.split(':')
534
+ return new Date(`${y}-${m}-${d}T${h}:${min}`)
535
+ }
536
+
306
537
  const formatDate = (date) => {
307
538
  const y = date.getFullYear()
308
539
  const m = `${date.getMonth() + 1}`.padStart(2, '0')
@@ -328,6 +559,17 @@ const getDateRangeText = (field) => {
328
559
  return ''
329
560
  }
330
561
 
562
+ const getDateTimeRangeText = (field) => {
563
+ const value = formData[field.field]
564
+ if (Array.isArray(value) && value[0] && value[1]) {
565
+ // 只显示日期部分用于预览
566
+ const startDate = value[0]
567
+ const endDate = value[1]
568
+ return `${startDate} ~ ${endDate}`
569
+ }
570
+ return ''
571
+ }
572
+
331
573
  // Address
332
574
  const openAddressPicker = (field) => {
333
575
  addressPopup.value = {
@@ -363,8 +605,78 @@ const getAddressText = (field) => {
363
605
  const value = formData[field.field]
364
606
  if (!value) return ''
365
607
  const parts = [value.province, value.city, value.county].filter(Boolean)
366
- return parts.join(' ')
608
+ return parts.join('')
609
+ }
610
+
611
+ // 单个日期时间
612
+ const openDateTime = (field) => {
613
+ dateTimePopup.value.visible = true
614
+ dateTimePopup.value.field = field
615
+
616
+ // 设置默认时间为当天8:30
617
+ const today = new Date()
618
+ const defaultTime = new Date(today)
619
+ defaultTime.setHours(8, 30, 0, 0)
620
+
621
+ // 将field的时间转换为日期时间格式
622
+ dateTimePopup.value.selectedTime = typeof field.defaultValue === 'string' ? reverseFormatDateTime(field.defaultValue) : defaultTime
623
+
624
+ // 初始化当前小时值
625
+ dateTimePopup.value.currentHour = '8'
626
+ }
627
+
628
+ const closeDateTime = () => {
629
+ dateTimePopup.value.visible = false
630
+ }
631
+
632
+ const confirmDateTime = () => {
633
+ if (!dateTimePopup.value.selectedTime) {
634
+ return
635
+ }
636
+
637
+ // 格式化日期时间
638
+ const dateTimeStr = formatDateTime(dateTimePopup.value.selectedTime)
639
+
640
+ // 更新表单数据
641
+ if (dateTimePopup.value.field) {
642
+ updateFieldValue(dateTimePopup.value.field.field, dateTimeStr)
643
+ }
644
+
645
+ // 关闭弹窗
646
+ closeDateTime()
647
+ }
648
+
649
+ const getDateTimeText = (field) => {
650
+ const value = formData[field.field]
651
+ return value || ''
652
+ }
653
+
654
+ const onSingleDateTimeChange = (params) => {
655
+ // 更新当前选择的时间
656
+ if (params && params.selectedValue) {
657
+ const [year, month, day, hour, minute] = params.selectedValue
658
+ const newDate = new Date(year, month - 1, day, hour, minute)
659
+ dateTimePopup.value.selectedTime = newDate
660
+
661
+ // 更新当前选择的小时值
662
+ if (params && params.selectedValue && params.columnIndex === 3) {
663
+ // 更新小时值
664
+ dateTimePopup.value.currentHour = params.selectedValue[3];
665
+
666
+ // 根据小时值设置默认分钟值
667
+ if (['08', '17'].includes(params.selectedValue[3])) {
668
+ // 小时为08或17时,分钟设为30
669
+ dateTimePopup.value.selectedTime.setMinutes(30)
670
+ } else if (['12', '13'].includes(params.selectedValue[3])) {
671
+ // 小时为12或13时,分钟设为00
672
+ dateTimePopup.value.selectedTime.setMinutes(0)
673
+ }
674
+ }
675
+ }
367
676
  }
677
+
678
+
679
+
368
680
  </script>
369
681
 
370
682
  <style scoped lang="scss">
@@ -392,6 +704,73 @@ const getAddressText = (field) => {
392
704
  }
393
705
  }
394
706
 
707
+ .datetime-range-popup {
708
+ .popup-header {
709
+ display: flex;
710
+ justify-content: space-between;
711
+ align-items: center;
712
+ padding: 16px;
713
+ border-bottom: 1px solid #f0f0f0;
714
+
715
+ .cancel-btn, .confirm-btn {
716
+ font-size: 16px;
717
+ padding: 4px 8px;
718
+ cursor: pointer;
719
+ }
720
+
721
+ .cancel-btn {
722
+ color: #666;
723
+ }
724
+
725
+ .confirm-btn {
726
+ color: #1989fa;
727
+ }
728
+
729
+ .title {
730
+ font-size: 16px;
731
+ font-weight: 500;
732
+ }
733
+ }
734
+
735
+ .datetime-content {
736
+ padding: 20px 16px;
737
+
738
+ :deep(.van-picker-column__item--selected) {
739
+ font-weight: 500;
740
+ }
741
+ }
742
+
743
+ .time_title {
744
+ font-size: 14px;
745
+ color: #666;
746
+ text-align: center;
747
+ }
748
+ }
749
+
750
+ .datetime-popup {
751
+ .popup-header {
752
+ display: flex;
753
+ justify-content: space-between;
754
+ align-items: center;
755
+ padding: 16px;
756
+ border-bottom: 1px solid #f0f0f0;
757
+
758
+ .cancel-btn, .confirm-btn {
759
+ font-size: 16px;
760
+ padding: 4px 8px;
761
+ cursor: pointer;
762
+ }
763
+
764
+ .cancel-btn {
765
+ color: #666;
766
+ }
767
+
768
+ .confirm-btn {
769
+ color: #1989fa;
770
+ }
771
+ }
772
+ }
773
+
395
774
  :deep(.van-cell) {
396
775
  padding: 8px;
397
776
  }