vue_zhongyou 1.0.8 → 1.0.10
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
|
@@ -0,0 +1,637 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<van-form class="dynamic-mobile-form" @submit="handleSubmit" >
|
|
3
|
+
<van-cell-group inset style="margin: 0px;">
|
|
4
|
+
<template v-for="field in normalizedSchema" :key="field.field">
|
|
5
|
+
<!-- 输入框 -->
|
|
6
|
+
<van-field
|
|
7
|
+
v-if="field.type === 'input' || field.type === 'textarea'"
|
|
8
|
+
v-model="formData[field.field]"
|
|
9
|
+
:type="field.type === 'textarea' ? 'textarea' : 'text'"
|
|
10
|
+
:label="field.label"
|
|
11
|
+
:label-width="60"
|
|
12
|
+
:placeholder="field.placeholder || `请输入${field.label}`"
|
|
13
|
+
:rules="field.rules || []"
|
|
14
|
+
:maxlength="field.maxlength"
|
|
15
|
+
:rows="field.rows || (field.type === 'textarea' ? 3 : undefined)"
|
|
16
|
+
:autosize="field.type === 'textarea'"
|
|
17
|
+
:disabled="field.disabled"
|
|
18
|
+
clearable
|
|
19
|
+
@update:model-value="(val) => updateFieldValue(field.field, val)"
|
|
20
|
+
/>
|
|
21
|
+
|
|
22
|
+
<!-- 单选 -->
|
|
23
|
+
<van-field v-else-if="field.type === 'radio'" name="radio" :label="field.label" :rules="field.rules || []">
|
|
24
|
+
<template #input>
|
|
25
|
+
<van-radio-group
|
|
26
|
+
v-model="formData[field.field]"
|
|
27
|
+
direction="horizontal"
|
|
28
|
+
@change="(val) => updateFieldValue(field.field, val)"
|
|
29
|
+
>
|
|
30
|
+
<van-radio
|
|
31
|
+
v-for="option in field.options"
|
|
32
|
+
:key="option.value"
|
|
33
|
+
:name="option.value"
|
|
34
|
+
shape="dot"
|
|
35
|
+
>
|
|
36
|
+
{{ option.label }}
|
|
37
|
+
</van-radio>
|
|
38
|
+
</van-radio-group>
|
|
39
|
+
</template>
|
|
40
|
+
</van-field>
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
<!-- 多选 -->
|
|
44
|
+
<van-field v-else-if="field.type === 'checkbox'" name="checkboxGroup" :label="field.label" :rules="field.rules || []">
|
|
45
|
+
<template #input>
|
|
46
|
+
<van-checkbox-group
|
|
47
|
+
v-model="formData[field.field]"
|
|
48
|
+
direction="horizontal"
|
|
49
|
+
@change="(val) => updateFieldValue(field.field, val)"
|
|
50
|
+
>
|
|
51
|
+
<van-checkbox
|
|
52
|
+
v-for="option in field.options"
|
|
53
|
+
:key="option.value"
|
|
54
|
+
:name="option.value"
|
|
55
|
+
shape="square"
|
|
56
|
+
>
|
|
57
|
+
{{ option.label }}
|
|
58
|
+
</van-checkbox>
|
|
59
|
+
</van-checkbox-group>
|
|
60
|
+
</template>
|
|
61
|
+
</van-field>
|
|
62
|
+
|
|
63
|
+
<!-- 选择器 -->
|
|
64
|
+
<van-field
|
|
65
|
+
v-else-if="field.type === 'select'"
|
|
66
|
+
is-link
|
|
67
|
+
readonly
|
|
68
|
+
:label="field.label"
|
|
69
|
+
:placeholder="field.placeholder || `请选择${field.label}`"
|
|
70
|
+
:model-value="getSelectLabel(field)"
|
|
71
|
+
:rules="field.rules || []"
|
|
72
|
+
@click="openSelect(field)"
|
|
73
|
+
/>
|
|
74
|
+
|
|
75
|
+
<!-- 时间范围 -->
|
|
76
|
+
<van-field
|
|
77
|
+
v-else-if="field.type === 'dateRange'"
|
|
78
|
+
is-link
|
|
79
|
+
readonly
|
|
80
|
+
:label="field.label"
|
|
81
|
+
:placeholder="field.placeholder || '请选择时间范围'"
|
|
82
|
+
:rules="field.rules || []"
|
|
83
|
+
:model-value="getDateRangeText(field)"
|
|
84
|
+
@click="openDateRange(field)"
|
|
85
|
+
/>
|
|
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
|
+
|
|
100
|
+
<!-- 地址选择 -->
|
|
101
|
+
<van-field
|
|
102
|
+
v-else-if="field.type === 'address'"
|
|
103
|
+
is-link
|
|
104
|
+
readonly
|
|
105
|
+
:label="field.label"
|
|
106
|
+
:placeholder="field.placeholder || '请选择地址'"
|
|
107
|
+
:rules="field.rules || []"
|
|
108
|
+
:model-value="getAddressText(field)"
|
|
109
|
+
@click="openAddressPicker(field)"
|
|
110
|
+
/>
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
<!-- 自定义内容 -->
|
|
114
|
+
<slot v-else :field="field" :value="formData[field.field]" />
|
|
115
|
+
</template>
|
|
116
|
+
</van-cell-group>
|
|
117
|
+
|
|
118
|
+
<slot name="actions">
|
|
119
|
+
<div class="form-actions">
|
|
120
|
+
<van-button size="small" @click.prevent="handleReset">
|
|
121
|
+
{{ resetButtonLabel }}
|
|
122
|
+
</van-button>
|
|
123
|
+
<van-button
|
|
124
|
+
type="primary"
|
|
125
|
+
size="small"
|
|
126
|
+
native-type="submit"
|
|
127
|
+
:loading="submitting"
|
|
128
|
+
>
|
|
129
|
+
{{ submitButtonLabel }}
|
|
130
|
+
</van-button>
|
|
131
|
+
</div>
|
|
132
|
+
</slot>
|
|
133
|
+
</van-form>
|
|
134
|
+
|
|
135
|
+
<!-- 下拉选择 -->
|
|
136
|
+
<van-popup v-model:show="selectPopup.visible" position="bottom" round>
|
|
137
|
+
<van-picker
|
|
138
|
+
show-toolbar
|
|
139
|
+
:columns="selectPopup.options"
|
|
140
|
+
@confirm="onSelectConfirm"
|
|
141
|
+
@cancel="closeSelect"
|
|
142
|
+
/>
|
|
143
|
+
|
|
144
|
+
</van-popup>
|
|
145
|
+
|
|
146
|
+
<!-- 时间范围 -->
|
|
147
|
+
<van-calendar
|
|
148
|
+
v-model:show="dateRangePopup.visible"
|
|
149
|
+
type="range"
|
|
150
|
+
color="#1989fa"
|
|
151
|
+
:min-date="dateRangePopup.minDate"
|
|
152
|
+
:max-date="dateRangePopup.maxDate"
|
|
153
|
+
@confirm="onDateRangeConfirm"
|
|
154
|
+
@cancel="closeDateRange"
|
|
155
|
+
/>
|
|
156
|
+
|
|
157
|
+
<!-- 日期时间范围选择器 -->
|
|
158
|
+
<van-popup v-model:show="dateTimeRangePopup.visible" position="bottom" round>
|
|
159
|
+
<div class="datetime-range-popup">
|
|
160
|
+
<div class="popup-header">
|
|
161
|
+
<span class="cancel-btn" @click="closeDateTimeRange">取消</span>
|
|
162
|
+
<span class="confirm-btn" @click="confirmDateTimeRange">确定</span>
|
|
163
|
+
</div>
|
|
164
|
+
<div class="time_title">开始时间</div>
|
|
165
|
+
<nut-date-picker
|
|
166
|
+
v-model="dateTimeRangePopup.startTime"
|
|
167
|
+
type="datetime"
|
|
168
|
+
:show-toolbar="false"
|
|
169
|
+
:filter="startTimeFilter"
|
|
170
|
+
@change="(e)=>onDateTimeChange(e,'startTime')"
|
|
171
|
+
/>
|
|
172
|
+
<div class="time_title">结束时间</div>
|
|
173
|
+
<nut-date-picker
|
|
174
|
+
v-model="dateTimeRangePopup.endTime"
|
|
175
|
+
type="datetime"
|
|
176
|
+
:show-toolbar="false"
|
|
177
|
+
:filter="endTimeFilter"
|
|
178
|
+
@change="(e)=>onDateTimeChange(e,'endTime')"
|
|
179
|
+
/>
|
|
180
|
+
</div>
|
|
181
|
+
</van-popup>
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
<!-- 地址 -->
|
|
185
|
+
<van-popup v-model:show="addressPopup.visible" position="bottom" round>
|
|
186
|
+
<van-area
|
|
187
|
+
:area-list="areaList"
|
|
188
|
+
:columns-placeholder="addressPopup.columnsPlaceholder || ['请选择', '请选择', '请选择']"
|
|
189
|
+
@confirm="onAddressConfirm"
|
|
190
|
+
@cancel="closeAddressPicker"
|
|
191
|
+
/>
|
|
192
|
+
</van-popup>
|
|
193
|
+
</template>
|
|
194
|
+
|
|
195
|
+
<script setup>
|
|
196
|
+
import { computed, reactive, ref, watch } from 'vue'
|
|
197
|
+
import { areaList } from '@vant/area-data'
|
|
198
|
+
import { showNotify } from 'vant';
|
|
199
|
+
|
|
200
|
+
const props = defineProps({
|
|
201
|
+
schema: {
|
|
202
|
+
type: Array,
|
|
203
|
+
default: () => []
|
|
204
|
+
},
|
|
205
|
+
modelValue: {
|
|
206
|
+
type: Object,
|
|
207
|
+
default: () => ({})
|
|
208
|
+
},
|
|
209
|
+
submitButtonLabel: {
|
|
210
|
+
type: String,
|
|
211
|
+
default: '提交'
|
|
212
|
+
},
|
|
213
|
+
resetButtonLabel: {
|
|
214
|
+
type: String,
|
|
215
|
+
default: '重置'
|
|
216
|
+
},
|
|
217
|
+
submitting: {
|
|
218
|
+
type: Boolean,
|
|
219
|
+
default: false
|
|
220
|
+
}
|
|
221
|
+
})
|
|
222
|
+
|
|
223
|
+
const emit = defineEmits(['update:modelValue', 'change', 'submit', 'reset'])
|
|
224
|
+
|
|
225
|
+
const formData = reactive({})
|
|
226
|
+
|
|
227
|
+
const selectPopup = ref({
|
|
228
|
+
visible: false,
|
|
229
|
+
field: null,
|
|
230
|
+
options: []
|
|
231
|
+
})
|
|
232
|
+
|
|
233
|
+
const dateRangePopup = ref({
|
|
234
|
+
visible: false,
|
|
235
|
+
field: null,
|
|
236
|
+
minDate: null,
|
|
237
|
+
maxDate: null
|
|
238
|
+
})
|
|
239
|
+
// 日期时间范围弹窗状态
|
|
240
|
+
const dateTimeRangePopup = ref({
|
|
241
|
+
visible: false, // 弹窗显示状态
|
|
242
|
+
field: null, // 当前操作的字段
|
|
243
|
+
startTime: new Date(), // 开始时间
|
|
244
|
+
endTime: new Date(), // 结束时间
|
|
245
|
+
currentDate: new Date(), // 当前选中日期
|
|
246
|
+
currentHour: '8' // 当前选择的小时值,默认为'12'
|
|
247
|
+
})
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
const addressPopup = ref({
|
|
251
|
+
visible: false,
|
|
252
|
+
field: null,
|
|
253
|
+
areaList: null,
|
|
254
|
+
columnsPlaceholder: null
|
|
255
|
+
})
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
const normalizedSchema = computed(() =>
|
|
261
|
+
props.schema.map((item) => ({
|
|
262
|
+
...item,
|
|
263
|
+
field: item.field || item.name
|
|
264
|
+
}))
|
|
265
|
+
)
|
|
266
|
+
|
|
267
|
+
const getDefaultValue = (type) => {
|
|
268
|
+
if (type === 'checkbox') return []
|
|
269
|
+
if (type === 'dateRange') return ['', '']
|
|
270
|
+
if (type === 'datetimeRange') return ['', '']
|
|
271
|
+
if (type === 'address') return { province: '', city: '', county: '', code: '' }
|
|
272
|
+
return ''
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
const snapshot = () => JSON.parse(JSON.stringify(formData))
|
|
276
|
+
|
|
277
|
+
const initializeFormData = () => {
|
|
278
|
+
normalizedSchema.value.forEach((field) => {
|
|
279
|
+
const key = field.field
|
|
280
|
+
const incoming = props.modelValue[key]
|
|
281
|
+
formData[key] = incoming !== undefined ? incoming : field.default ?? getDefaultValue(field.type)
|
|
282
|
+
})
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
watch(
|
|
286
|
+
() => props.schema,
|
|
287
|
+
() => {
|
|
288
|
+
initializeFormData()
|
|
289
|
+
},
|
|
290
|
+
{ immediate: true, deep: true }
|
|
291
|
+
)
|
|
292
|
+
|
|
293
|
+
watch(
|
|
294
|
+
() => props.modelValue,
|
|
295
|
+
(val) => {
|
|
296
|
+
if (!val) return
|
|
297
|
+
Object.keys(val).forEach((key) => {
|
|
298
|
+
formData[key] = val[key]
|
|
299
|
+
})
|
|
300
|
+
},
|
|
301
|
+
{ deep: true }
|
|
302
|
+
)
|
|
303
|
+
|
|
304
|
+
const updateFieldValue = (field, value) => {
|
|
305
|
+
formData[field] = value
|
|
306
|
+
emit('update:modelValue', snapshot())
|
|
307
|
+
emit('change', { field, value, values: snapshot() })
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
const handleSubmit = () => {
|
|
311
|
+
emit('submit', snapshot())
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
const handleReset = () => {
|
|
315
|
+
initializeFormData()
|
|
316
|
+
emit('update:modelValue', snapshot())
|
|
317
|
+
emit('reset', snapshot())
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// Select
|
|
321
|
+
const openSelect = (field) => {
|
|
322
|
+
selectPopup.value = {
|
|
323
|
+
visible: true,
|
|
324
|
+
field,
|
|
325
|
+
options: (field.options || []).map((opt) => ({
|
|
326
|
+
text: opt.label,
|
|
327
|
+
value: opt.value
|
|
328
|
+
}))
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
const closeSelect = () => {
|
|
333
|
+
selectPopup.value.visible = false
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
const onSelectConfirm = ({ selectedOptions }) => {
|
|
337
|
+
const option = selectedOptions?.[0]
|
|
338
|
+
if (option && selectPopup.value.field) {
|
|
339
|
+
updateFieldValue(selectPopup.value.field.field, option.value)
|
|
340
|
+
}
|
|
341
|
+
closeSelect()
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
const getSelectLabel = (field) => {
|
|
345
|
+
const value = formData[field.field]
|
|
346
|
+
const option = (field.options || []).find((opt) => opt.value === value)
|
|
347
|
+
return option ? option.label : ''
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
// Date range
|
|
351
|
+
const openDateRange = (field) => {
|
|
352
|
+
dateRangePopup.value.visible = true
|
|
353
|
+
dateRangePopup.value.field = field
|
|
354
|
+
// 计算前后五年的日期范围
|
|
355
|
+
const currentDate = new Date()
|
|
356
|
+
const fiveYearsAgo = new Date(currentDate.getFullYear() - 5, currentDate.getMonth(), currentDate.getDate())
|
|
357
|
+
const fiveYearsLater = new Date(currentDate.getFullYear() + 5, currentDate.getMonth(), currentDate.getDate())
|
|
358
|
+
|
|
359
|
+
// 如果没有设置minDate和maxDate,则使用前后五年的范围
|
|
360
|
+
dateRangePopup.value.minDate = field.minDate || fiveYearsAgo
|
|
361
|
+
dateRangePopup.value.maxDate = field.maxDate || fiveYearsLater
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
const closeDateRange = () => {
|
|
365
|
+
dateRangePopup.value.visible = false
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
// 打开日期时间范围选择
|
|
369
|
+
const openDateTimeRange = (field) => {
|
|
370
|
+
dateTimeRangePopup.value.visible = true
|
|
371
|
+
dateTimeRangePopup.value.field = field
|
|
372
|
+
|
|
373
|
+
// 设置默认时间:开始时间为当天8:30,结束时间为当天17:30
|
|
374
|
+
const today = new Date()
|
|
375
|
+
const defaultStartTime = new Date(today)
|
|
376
|
+
defaultStartTime.setHours(8, 30, 0, 0)
|
|
377
|
+
const defaultEndTime = new Date(today)
|
|
378
|
+
defaultEndTime.setHours(17, 30, 0, 0)
|
|
379
|
+
|
|
380
|
+
// 将field的startTime和endTime转换为日期时间格式
|
|
381
|
+
dateTimeRangePopup.value.startTime = typeof field.startTime === 'string' ? reverseFormatDateTime(field.startTime) : defaultStartTime
|
|
382
|
+
dateTimeRangePopup.value.endTime = typeof field.endTime === 'string' ? reverseFormatDateTime(field.endTime) : defaultEndTime
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
const closeDateTimeRange = () => {
|
|
386
|
+
dateTimeRangePopup.value.visible = false
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
// 日期时间选择器改变事件处理
|
|
390
|
+
const onDateTimeChange = (params,type) => {
|
|
391
|
+
|
|
392
|
+
// 更新当前选择的小时值
|
|
393
|
+
if (params && params.selectedValue && params.columnIndex === 3) {
|
|
394
|
+
// 更新小时值
|
|
395
|
+
// 根据小时值设置默认分钟值
|
|
396
|
+
if (['08', '17'].includes(params.selectedValue[3])) {
|
|
397
|
+
// 小时为08或17时,分钟设为30
|
|
398
|
+
if(type === 'startTime'){
|
|
399
|
+
dateTimeRangePopup.value.startTime.setMinutes(30)
|
|
400
|
+
}else{
|
|
401
|
+
dateTimeRangePopup.value.endTime.setMinutes(30)
|
|
402
|
+
}
|
|
403
|
+
} else if (['12', '13'].includes(params.selectedValue[3])) {
|
|
404
|
+
// 小时为12或13时,分钟设为00
|
|
405
|
+
if(type === 'startTime'){
|
|
406
|
+
dateTimeRangePopup.value.startTime.setMinutes(0)
|
|
407
|
+
}else{
|
|
408
|
+
dateTimeRangePopup.value.endTime.setMinutes(0)
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
if(type === 'startTime'){
|
|
412
|
+
dateTimeRangePopup.value.currentStartHour = params.selectedValue[3];
|
|
413
|
+
}else{
|
|
414
|
+
dateTimeRangePopup.value.currentEndHour = params.selectedValue[3];
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
// 确认日期时间范围选择
|
|
421
|
+
const confirmDateTimeRange = () => {
|
|
422
|
+
if(!dateTimeRangePopup.value.startTime || !dateTimeRangePopup.value.endTime){
|
|
423
|
+
return
|
|
424
|
+
}
|
|
425
|
+
if(dateTimeRangePopup.value.startTime.getTime()>=dateTimeRangePopup.value.endTime.getTime()){
|
|
426
|
+
showNotify({ type: 'warning', message: '结束时间必须晚于开始时间' });
|
|
427
|
+
return
|
|
428
|
+
}
|
|
429
|
+
console.log(dateTimeRangePopup.value.startTime.getTime()<dateTimeRangePopup.value.endTime.getTime())
|
|
430
|
+
// 格式化日期时间范围
|
|
431
|
+
const startDateStr = formatDateTime(dateTimeRangePopup.value.startTime)
|
|
432
|
+
const endDateStr = formatDateTime(dateTimeRangePopup.value.endTime)
|
|
433
|
+
console.log('开始时间:', startDateStr);
|
|
434
|
+
console.log('结束时间:', endDateStr);
|
|
435
|
+
// 更新表单数据
|
|
436
|
+
if (dateTimeRangePopup.value.field) {
|
|
437
|
+
updateFieldValue(dateTimeRangePopup.value.field.field, [startDateStr, endDateStr])
|
|
438
|
+
}
|
|
439
|
+
// 关闭弹窗
|
|
440
|
+
closeDateTimeRange()
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
// 日期时间选择器筛选函数
|
|
444
|
+
const startTimeFilter = (type, options) => {
|
|
445
|
+
if (type === 'hour') {
|
|
446
|
+
return options.filter(option => ['08', '12', '13', '17'].includes(option.value))
|
|
447
|
+
} else if (type === 'minute') {
|
|
448
|
+
// 当小时位是'08'或者'12'时,分钟只保留'00'
|
|
449
|
+
if (['12','13'].includes(dateTimeRangePopup.value.currentStartHour)) {
|
|
450
|
+
return options.filter(option => ['00'].includes(option.value))
|
|
451
|
+
}else{
|
|
452
|
+
return options.filter(option => ['30'].includes(option.value))
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
return options
|
|
456
|
+
}
|
|
457
|
+
const endTimeFilter = (type, options) => {
|
|
458
|
+
if (type === 'hour') {
|
|
459
|
+
return options.filter(option => ['08', '12', '13', '17'].includes(option.value))
|
|
460
|
+
} else if (type === 'minute') {
|
|
461
|
+
// 当小时位是'08'或者'12'时,分钟只保留'00'
|
|
462
|
+
if (['12','13'].includes(dateTimeRangePopup.value.currentEndHour)) {
|
|
463
|
+
return options.filter(option => ['00'].includes(option.value))
|
|
464
|
+
}else{
|
|
465
|
+
return options.filter(option => ['30'].includes(option.value))
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
return options
|
|
469
|
+
}
|
|
470
|
+
// 格式化日期时间为字符串
|
|
471
|
+
var formatDateTime = (date) => {
|
|
472
|
+
const y = date.getFullYear()
|
|
473
|
+
const m = `${date.getMonth() + 1}`.padStart(2, '0')
|
|
474
|
+
const d = `${date.getDate()}`.padStart(2, '0')
|
|
475
|
+
const h = `${date.getHours()}`.padStart(2, '0')
|
|
476
|
+
const min = `${date.getMinutes()}`.padStart(2, '0')
|
|
477
|
+
return `${y}-${m}-${d} ${h}:${min}`
|
|
478
|
+
}
|
|
479
|
+
// 解析字符串为日期时间对象
|
|
480
|
+
var reverseFormatDateTime = (dateStr) => {
|
|
481
|
+
const [datePart, timePart] = dateStr.split(' ')
|
|
482
|
+
const [y, m, d] = datePart.split('-')
|
|
483
|
+
const [h, min] = timePart.split(':')
|
|
484
|
+
return new Date(`${y}-${m}-${d}T${h}:${min}`)
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
const formatDate = (date) => {
|
|
488
|
+
const y = date.getFullYear()
|
|
489
|
+
const m = `${date.getMonth() + 1}`.padStart(2, '0')
|
|
490
|
+
const d = `${date.getDate()}`.padStart(2, '0')
|
|
491
|
+
return `${y}-${m}-${d}`
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
const onDateRangeConfirm = (values) => {
|
|
495
|
+
if (!values || values.length !== 2 || !dateRangePopup.value.field) {
|
|
496
|
+
closeDateRange()
|
|
497
|
+
return
|
|
498
|
+
}
|
|
499
|
+
const [start, end] = values
|
|
500
|
+
updateFieldValue(dateRangePopup.value.field.field, [formatDate(start), formatDate(end)])
|
|
501
|
+
closeDateRange()
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
const getDateRangeText = (field) => {
|
|
505
|
+
const value = formData[field.field]
|
|
506
|
+
if (Array.isArray(value) && value[0] && value[1]) {
|
|
507
|
+
return `${value[0]} ~ ${value[1]}`
|
|
508
|
+
}
|
|
509
|
+
return ''
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
const getDateTimeRangeText = (field) => {
|
|
513
|
+
const value = formData[field.field]
|
|
514
|
+
if (Array.isArray(value) && value[0] && value[1]) {
|
|
515
|
+
// 只显示日期部分用于预览
|
|
516
|
+
const startDate = value[0]
|
|
517
|
+
const endDate = value[1]
|
|
518
|
+
return `${startDate} ~ ${endDate}`
|
|
519
|
+
}
|
|
520
|
+
return ''
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
// Address
|
|
524
|
+
const openAddressPicker = (field) => {
|
|
525
|
+
addressPopup.value = {
|
|
526
|
+
visible: true,
|
|
527
|
+
field,
|
|
528
|
+
areaList: areaList,
|
|
529
|
+
columnsPlaceholder: field.columnsPlaceholder
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
const closeAddressPicker = () => {
|
|
534
|
+
addressPopup.value.visible = false
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
const onAddressConfirm = ({ selectedOptions }) => {
|
|
538
|
+
if (!selectedOptions || !addressPopup.value.field) {
|
|
539
|
+
closeAddressPicker()
|
|
540
|
+
return
|
|
541
|
+
}
|
|
542
|
+
console.log(selectedOptions);
|
|
543
|
+
|
|
544
|
+
const [province, city, county] = selectedOptions
|
|
545
|
+
updateFieldValue(addressPopup.value.field.field, {
|
|
546
|
+
province: province?.text || '',
|
|
547
|
+
city: city?.text || '',
|
|
548
|
+
county: county?.text || '',
|
|
549
|
+
code: county?.value || city?.value || province?.value || ''
|
|
550
|
+
})
|
|
551
|
+
closeAddressPicker()
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
const getAddressText = (field) => {
|
|
555
|
+
const value = formData[field.field]
|
|
556
|
+
if (!value) return ''
|
|
557
|
+
const parts = [value.province, value.city, value.county].filter(Boolean)
|
|
558
|
+
return parts.join('')
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
|
|
562
|
+
|
|
563
|
+
</script>
|
|
564
|
+
|
|
565
|
+
<style scoped lang="scss">
|
|
566
|
+
.dynamic-mobile-form {
|
|
567
|
+
.field-wrapper {
|
|
568
|
+
padding: 12px 16px;
|
|
569
|
+
border-bottom: 1px solid #f7f8fa;
|
|
570
|
+
&:last-of-type {
|
|
571
|
+
border-bottom: none;
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
.field-label {
|
|
575
|
+
font-size: 14px;
|
|
576
|
+
color: #666;
|
|
577
|
+
margin-bottom: 8px;
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
.form-actions {
|
|
582
|
+
display: flex;
|
|
583
|
+
flex-direction: row-reverse;
|
|
584
|
+
|
|
585
|
+
padding: 16px;
|
|
586
|
+
gap: 12px;
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
.datetime-range-popup {
|
|
591
|
+
.popup-header {
|
|
592
|
+
display: flex;
|
|
593
|
+
justify-content: space-between;
|
|
594
|
+
align-items: center;
|
|
595
|
+
padding: 16px;
|
|
596
|
+
border-bottom: 1px solid #f0f0f0;
|
|
597
|
+
|
|
598
|
+
.cancel-btn, .confirm-btn {
|
|
599
|
+
font-size: 16px;
|
|
600
|
+
padding: 4px 8px;
|
|
601
|
+
cursor: pointer;
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
.cancel-btn {
|
|
605
|
+
color: #666;
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
.confirm-btn {
|
|
609
|
+
color: #1989fa;
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
.title {
|
|
613
|
+
font-size: 16px;
|
|
614
|
+
font-weight: 500;
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
.datetime-content {
|
|
619
|
+
padding: 20px 16px;
|
|
620
|
+
|
|
621
|
+
:deep(.van-picker-column__item--selected) {
|
|
622
|
+
font-weight: 500;
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
.time_title {
|
|
627
|
+
font-size: 14px;
|
|
628
|
+
color: #666;
|
|
629
|
+
text-align: center;
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
:deep(.van-cell) {
|
|
634
|
+
padding: 8px;
|
|
635
|
+
}
|
|
636
|
+
</style>
|
|
637
|
+
|