vue_zhongyou 1.0.7 → 1.0.9
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 +1 -1
- package//345/212/237/350/203/275/344/273/243/347/240/201/AI/345/257/271/350/257/235/ChatForm.vue +378 -0
- package//345/212/237/350/203/275/344/273/243/347/240/201/AI/345/257/271/350/257/235/DynamicMobileForm.vue +399 -0
- package//345/212/237/350/203/275/344/273/243/347/240/201/AI/345/257/271/350/257/235/aiChatPage.vue +757 -0
- package//345/212/237/350/203/275/344/273/243/347/240/201/AI/345/257/271/350/257/235/newChatCardList.vue +202 -0
- package//346/226/207/346/241/243/ai/346/250/241/345/235/227.md +33 -0
package/package.json
CHANGED
package//345/212/237/350/203/275/344/273/243/347/240/201/AI/345/257/271/350/257/235/ChatForm.vue
ADDED
|
@@ -0,0 +1,378 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="chat-form">
|
|
3
|
+
<van-form :model="formData" ref="formRef">
|
|
4
|
+
<template v-for="field in schema" :key="field.field">
|
|
5
|
+
<!-- 输入框 -->
|
|
6
|
+
<van-field
|
|
7
|
+
v-if="field.type === 'input'"
|
|
8
|
+
v-model="formData[field.field]"
|
|
9
|
+
:label="field.label"
|
|
10
|
+
:placeholder="field.placeholder"
|
|
11
|
+
:rules="field.rules"
|
|
12
|
+
:required="field.rules?.some(rule => rule.required)"
|
|
13
|
+
/>
|
|
14
|
+
|
|
15
|
+
<!-- 文本域 -->
|
|
16
|
+
<van-field
|
|
17
|
+
v-else-if="field.type === 'textarea'"
|
|
18
|
+
v-model="formData[field.field]"
|
|
19
|
+
:label="field.label"
|
|
20
|
+
type="textarea"
|
|
21
|
+
:placeholder="field.placeholder"
|
|
22
|
+
:rows="field.rows || 2"
|
|
23
|
+
:autosize="field.autosize || false"
|
|
24
|
+
/>
|
|
25
|
+
|
|
26
|
+
<!-- 选择器 -->
|
|
27
|
+
<van-field
|
|
28
|
+
v-else-if="field.type === 'select'"
|
|
29
|
+
v-model="formData[field.field]"
|
|
30
|
+
:label="field.label"
|
|
31
|
+
is-link
|
|
32
|
+
readonly
|
|
33
|
+
:placeholder="field.placeholder"
|
|
34
|
+
@click="() => showPicker(field)"
|
|
35
|
+
>
|
|
36
|
+
<template #input>
|
|
37
|
+
<span v-if="formData[field.field]">{{ getOptionLabel(field, formData[field.field]) }}</span>
|
|
38
|
+
</template>
|
|
39
|
+
</van-field>
|
|
40
|
+
|
|
41
|
+
<!-- 单选框 -->
|
|
42
|
+
<van-cell-group v-else-if="field.type === 'radio'" :title="field.label">
|
|
43
|
+
<van-radio-group v-model="formData[field.field]" direction="horizontal">
|
|
44
|
+
<van-radio
|
|
45
|
+
v-for="option in field.options"
|
|
46
|
+
:key="option.value"
|
|
47
|
+
:name="option.value"
|
|
48
|
+
icon-size="16px"
|
|
49
|
+
checked-color="#1989fa"
|
|
50
|
+
>
|
|
51
|
+
{{ option.label }}
|
|
52
|
+
</van-radio>
|
|
53
|
+
</van-radio-group>
|
|
54
|
+
</van-cell-group>
|
|
55
|
+
|
|
56
|
+
<!-- 复选框 -->
|
|
57
|
+
<van-cell-group v-else-if="field.type === 'checkbox'" :title="field.label">
|
|
58
|
+
<van-checkbox-group v-model="formData[field.field]" direction="horizontal">
|
|
59
|
+
<van-checkbox
|
|
60
|
+
v-for="option in field.options"
|
|
61
|
+
:key="option.value"
|
|
62
|
+
:name="option.value"
|
|
63
|
+
icon-size="16px"
|
|
64
|
+
checked-color="#1989fa"
|
|
65
|
+
>
|
|
66
|
+
{{ option.label }}
|
|
67
|
+
</van-checkbox>
|
|
68
|
+
</van-checkbox-group>
|
|
69
|
+
</van-cell-group>
|
|
70
|
+
|
|
71
|
+
<!-- 日期选择 -->
|
|
72
|
+
<van-field
|
|
73
|
+
v-else-if="field.type === 'date'"
|
|
74
|
+
v-model="formData[field.field]"
|
|
75
|
+
:label="field.label"
|
|
76
|
+
is-link
|
|
77
|
+
readonly
|
|
78
|
+
:placeholder="field.placeholder"
|
|
79
|
+
@click="() => showDatePicker(field)"
|
|
80
|
+
/>
|
|
81
|
+
|
|
82
|
+
<!-- 日期范围 -->
|
|
83
|
+
<van-cell-group v-else-if="field.type === 'dateRange'" :title="field.label">
|
|
84
|
+
<van-cell
|
|
85
|
+
:title="`${field.label}开始`"
|
|
86
|
+
:value="formData[field.field]?.[0] || '请选择'"
|
|
87
|
+
is-link
|
|
88
|
+
@click="() => showDateRangePicker(field, 0)"
|
|
89
|
+
/>
|
|
90
|
+
<van-cell
|
|
91
|
+
:title="`${field.label}结束`"
|
|
92
|
+
:value="formData[field.field]?.[1] || '请选择'"
|
|
93
|
+
is-link
|
|
94
|
+
@click="() => showDateRangePicker(field, 1)"
|
|
95
|
+
/>
|
|
96
|
+
</van-cell-group>
|
|
97
|
+
</template>
|
|
98
|
+
|
|
99
|
+
<!-- 操作按钮 -->
|
|
100
|
+
<div class="form-actions" v-if="showActions">
|
|
101
|
+
<van-button
|
|
102
|
+
v-if="submitButtonLabel"
|
|
103
|
+
type="primary"
|
|
104
|
+
block
|
|
105
|
+
:loading="isSubmitting"
|
|
106
|
+
@click="onSubmit"
|
|
107
|
+
>
|
|
108
|
+
{{ submitButtonLabel }}
|
|
109
|
+
</van-button>
|
|
110
|
+
<van-button
|
|
111
|
+
v-if="resetButtonLabel"
|
|
112
|
+
type="default"
|
|
113
|
+
block
|
|
114
|
+
@click="onReset"
|
|
115
|
+
>
|
|
116
|
+
{{ resetButtonLabel }}
|
|
117
|
+
</van-button>
|
|
118
|
+
</div>
|
|
119
|
+
</van-form>
|
|
120
|
+
|
|
121
|
+
<!-- 选择器弹窗 -->
|
|
122
|
+
<van-popup v-model:show="showPickerDialog" position="bottom">
|
|
123
|
+
<van-picker
|
|
124
|
+
:columns="currentPickerColumns"
|
|
125
|
+
@confirm="onPickerConfirm"
|
|
126
|
+
@cancel="showPickerDialog = false"
|
|
127
|
+
/>
|
|
128
|
+
</van-popup>
|
|
129
|
+
|
|
130
|
+
<!-- 日期选择器弹窗 -->
|
|
131
|
+
<van-popup v-model:show="showDatePickerDialog" position="bottom">
|
|
132
|
+
<van-date-picker
|
|
133
|
+
v-model="currentDateValue"
|
|
134
|
+
@confirm="onDatePickerConfirm"
|
|
135
|
+
@cancel="showDatePickerDialog = false"
|
|
136
|
+
/>
|
|
137
|
+
</van-popup>
|
|
138
|
+
|
|
139
|
+
<!-- 日期范围选择器弹窗 -->
|
|
140
|
+
<van-popup v-model:show="showDateRangePickerDialog" position="bottom">
|
|
141
|
+
<van-date-picker
|
|
142
|
+
v-model="currentDateRangeValue"
|
|
143
|
+
@confirm="onDateRangePickerConfirm"
|
|
144
|
+
@cancel="showDateRangePickerDialog = false"
|
|
145
|
+
/>
|
|
146
|
+
</van-popup>
|
|
147
|
+
</div>
|
|
148
|
+
</template>
|
|
149
|
+
|
|
150
|
+
<script setup>
|
|
151
|
+
import { ref, reactive, watch, onMounted } from 'vue'
|
|
152
|
+
import { showToast } from 'vant'
|
|
153
|
+
|
|
154
|
+
// 定义组件属性
|
|
155
|
+
const props = defineProps({
|
|
156
|
+
modelValue: {
|
|
157
|
+
type: Object,
|
|
158
|
+
default: () => ({})
|
|
159
|
+
},
|
|
160
|
+
schema: {
|
|
161
|
+
type: Array,
|
|
162
|
+
required: true
|
|
163
|
+
},
|
|
164
|
+
submitButtonLabel: {
|
|
165
|
+
type: String,
|
|
166
|
+
default: '提交'
|
|
167
|
+
},
|
|
168
|
+
resetButtonLabel: {
|
|
169
|
+
type: String,
|
|
170
|
+
default: '重置'
|
|
171
|
+
},
|
|
172
|
+
showActions: {
|
|
173
|
+
type: Boolean,
|
|
174
|
+
default: true
|
|
175
|
+
}
|
|
176
|
+
})
|
|
177
|
+
|
|
178
|
+
// 定义事件
|
|
179
|
+
const emit = defineEmits(['update:modelValue', 'submit', 'change'])
|
|
180
|
+
|
|
181
|
+
// 表单数据
|
|
182
|
+
const formData = ref({})
|
|
183
|
+
|
|
184
|
+
// 表单引用
|
|
185
|
+
const formRef = ref(null)
|
|
186
|
+
|
|
187
|
+
// 提交状态
|
|
188
|
+
const isSubmitting = ref(false)
|
|
189
|
+
|
|
190
|
+
// 弹窗相关
|
|
191
|
+
const showPickerDialog = ref(false)
|
|
192
|
+
const showDatePickerDialog = ref(false)
|
|
193
|
+
const showDateRangePickerDialog = ref(false)
|
|
194
|
+
const currentField = ref(null)
|
|
195
|
+
const currentPickerColumns = ref([])
|
|
196
|
+
const currentDateValue = ref(new Date())
|
|
197
|
+
const currentDateRangeValue = ref([new Date(), new Date()])
|
|
198
|
+
|
|
199
|
+
// 初始化表单数据
|
|
200
|
+
const initializeFormData = () => {
|
|
201
|
+
const data = { ...props.modelValue }
|
|
202
|
+
|
|
203
|
+
props.schema.forEach(field => {
|
|
204
|
+
// 如果没有初始值但有默认值,则使用默认值
|
|
205
|
+
if (!(field.field in data) && 'default' in field) {
|
|
206
|
+
data[field.field] = field.default
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// 确保所有字段都在数据中
|
|
210
|
+
if (!(field.field in data)) {
|
|
211
|
+
if (field.type === 'checkbox') {
|
|
212
|
+
data[field.field] = []
|
|
213
|
+
} else if (field.type === 'dateRange') {
|
|
214
|
+
data[field.field] = []
|
|
215
|
+
} else {
|
|
216
|
+
data[field.field] = ''
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
})
|
|
220
|
+
|
|
221
|
+
formData.value = data
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// 获取选项标签
|
|
225
|
+
const getOptionLabel = (field, value) => {
|
|
226
|
+
const option = field.options.find(opt => opt.value === value)
|
|
227
|
+
return option ? option.label : value
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// 显示选择器
|
|
231
|
+
const showPicker = (field) => {
|
|
232
|
+
currentField.value = field
|
|
233
|
+
currentPickerColumns.value = field.options.map(option => ({
|
|
234
|
+
text: option.label,
|
|
235
|
+
value: option.value
|
|
236
|
+
}))
|
|
237
|
+
showPickerDialog.value = true
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// 选择器确认
|
|
241
|
+
const onPickerConfirm = ({ selectedValues }) => {
|
|
242
|
+
const value = selectedValues[0]
|
|
243
|
+
formData.value[currentField.value.field] = value
|
|
244
|
+
emit('update:modelValue', formData.value)
|
|
245
|
+
emit('change', { field: currentField.value.field, value })
|
|
246
|
+
showPickerDialog.value = false
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// 显示日期选择器
|
|
250
|
+
const showDatePicker = (field) => {
|
|
251
|
+
currentField.value = field
|
|
252
|
+
currentDateValue.value = formData.value[field.field] ? new Date(formData.value[field.field]) : new Date()
|
|
253
|
+
showDatePickerDialog.value = true
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// 日期选择器确认
|
|
257
|
+
const onDatePickerConfirm = ({ selectedValues }) => {
|
|
258
|
+
const date = new Date(selectedValues[0], selectedValues[1] - 1, selectedValues[2])
|
|
259
|
+
const dateString = date.toISOString().split('T')[0]
|
|
260
|
+
formData.value[currentField.value.field] = dateString
|
|
261
|
+
emit('update:modelValue', formData.value)
|
|
262
|
+
emit('change', { field: currentField.value.field, value: dateString })
|
|
263
|
+
showDatePickerDialog.value = false
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// 显示日期范围选择器
|
|
267
|
+
const showDateRangePicker = (field, index) => {
|
|
268
|
+
currentField.value = field
|
|
269
|
+
const range = formData.value[field.field] || []
|
|
270
|
+
if (range[index]) {
|
|
271
|
+
currentDateRangeValue.value[index] = new Date(range[index])
|
|
272
|
+
}
|
|
273
|
+
showDateRangePickerDialog.value = true
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// 日期范围选择器确认
|
|
277
|
+
const onDateRangePickerConfirm = ({ selectedValues }) => {
|
|
278
|
+
const startDate = new Date(selectedValues[0][0], selectedValues[0][1] - 1, selectedValues[0][2])
|
|
279
|
+
const endDate = new Date(selectedValues[1][0], selectedValues[1][1] - 1, selectedValues[1][2])
|
|
280
|
+
|
|
281
|
+
const range = [...(formData.value[currentField.value.field] || [])]
|
|
282
|
+
range[0] = startDate.toISOString().split('T')[0]
|
|
283
|
+
range[1] = endDate.toISOString().split('T')[0]
|
|
284
|
+
|
|
285
|
+
formData.value[currentField.value.field] = range
|
|
286
|
+
emit('update:modelValue', formData.value)
|
|
287
|
+
emit('change', { field: currentField.value.field, value: range })
|
|
288
|
+
showDateRangePickerDialog.value = false
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
// 提交表单
|
|
292
|
+
const onSubmit = async () => {
|
|
293
|
+
if (formRef.value) {
|
|
294
|
+
try {
|
|
295
|
+
await formRef.value.validate()
|
|
296
|
+
isSubmitting.value = true
|
|
297
|
+
emit('submit', formData.value)
|
|
298
|
+
} catch (error) {
|
|
299
|
+
showToast(error.message || '请检查表单填写是否正确')
|
|
300
|
+
} finally {
|
|
301
|
+
isSubmitting.value = false
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// 重置表单
|
|
307
|
+
const onReset = () => {
|
|
308
|
+
initializeFormData()
|
|
309
|
+
emit('update:modelValue', formData.value)
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
// 监听表单数据变化
|
|
313
|
+
watch(formData, (newVal) => {
|
|
314
|
+
emit('update:modelValue', newVal)
|
|
315
|
+
}, { deep: true })
|
|
316
|
+
|
|
317
|
+
// 监听外部传入的值变化
|
|
318
|
+
watch(() => props.modelValue, (newVal) => {
|
|
319
|
+
formData.value = { ...newVal }
|
|
320
|
+
}, { deep: true })
|
|
321
|
+
|
|
322
|
+
// 组件挂载时初始化数据
|
|
323
|
+
onMounted(() => {
|
|
324
|
+
initializeFormData()
|
|
325
|
+
})
|
|
326
|
+
</script>
|
|
327
|
+
|
|
328
|
+
<style lang="scss" scoped>
|
|
329
|
+
.chat-form {
|
|
330
|
+
background: #fff;
|
|
331
|
+
border-radius: 12px;
|
|
332
|
+
padding: 16px;
|
|
333
|
+
margin: 8px 0;
|
|
334
|
+
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.05);
|
|
335
|
+
|
|
336
|
+
// 当在消息气泡中显示时,调整样式
|
|
337
|
+
:deep(.message-bubble) & {
|
|
338
|
+
background: transparent;
|
|
339
|
+
box-shadow: none;
|
|
340
|
+
margin: 0;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
.form-actions {
|
|
344
|
+
margin-top: 20px;
|
|
345
|
+
display: flex;
|
|
346
|
+
flex-direction: column;
|
|
347
|
+
gap: 12px;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
:deep(.van-cell-group) {
|
|
351
|
+
margin-bottom: 16px;
|
|
352
|
+
|
|
353
|
+
.van-cell {
|
|
354
|
+
padding: 12px 0;
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
:deep(.van-radio-group--horizontal) {
|
|
359
|
+
flex-wrap: wrap;
|
|
360
|
+
gap: 8px;
|
|
361
|
+
padding: 8px 0;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
:deep(.van-checkbox-group--horizontal) {
|
|
365
|
+
flex-wrap: wrap;
|
|
366
|
+
gap: 8px;
|
|
367
|
+
padding: 8px 0;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
:deep(.van-radio) {
|
|
371
|
+
margin-bottom: 4px;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
:deep(.van-checkbox) {
|
|
375
|
+
margin-bottom: 4px;
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
</style>
|