tang-ui-x 1.1.2 → 1.1.4
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/components/TCheckboxGroup/index.uvue +30 -11
- package/components/TCheckboxGroup/type.uts +10 -0
- package/components/TForm/index.uvue +394 -413
- package/components/TForm/type.uts +90 -84
- package/components/TRadioGroup/index.uvue +32 -12
- package/components/TRadioGroup/type.uts +7 -0
- package/composables/i18n/manager.uts +198 -3
- package/locales/en-US/common.json +7 -0
- package/locales/en-US/empty.json +1 -0
- package/locales/en-US/form.json +7 -0
- package/locales/zh-CN/common.json +7 -0
- package/locales/zh-CN/empty.json +1 -0
- package/locales/zh-CN/form.json +7 -0
- package/locales/zh-TW/common.json +8 -1
- package/locales/zh-TW/empty.json +1 -0
- package/locales/zh-TW/form.json +7 -0
- package/package.json +2 -2
|
@@ -1,413 +1,394 @@
|
|
|
1
|
-
<script setup lang="uts">
|
|
2
|
-
import { computed } from 'vue'
|
|
3
|
-
import
|
|
4
|
-
import
|
|
5
|
-
import TSwitch from '../TSwitch/index.uvue'
|
|
6
|
-
import TRate from '../TRate/index.uvue'
|
|
7
|
-
import TSlider from '../TSlider/index.uvue'
|
|
8
|
-
import type { FormOption, FormSchema, TFormProps, ComponentProps } from './type.uts'
|
|
9
|
-
import { useI18n } from '../../composables/useI18n.uts'
|
|
10
|
-
|
|
11
|
-
const props = withDefaults(defineProps<TFormProps>(), {
|
|
12
|
-
labelWidth: '
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
return
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
const
|
|
56
|
-
const
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
const
|
|
92
|
-
model
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
<
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
:name="item.field"
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
:name="item.field"
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
.
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
.
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
transition: all 0.3s;
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
.btn-submit {
|
|
399
|
-
background-color: #007aff;
|
|
400
|
-
color: #fff;
|
|
401
|
-
}
|
|
402
|
-
|
|
403
|
-
.btn-reset {
|
|
404
|
-
background-color: #fff;
|
|
405
|
-
color: #007aff;
|
|
406
|
-
border: 1rpx solid #007aff;
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
.btn:active {
|
|
410
|
-
opacity: 0.7;
|
|
411
|
-
transform: scale(0.98);
|
|
412
|
-
}
|
|
413
|
-
</style>
|
|
1
|
+
<script setup lang="uts">
|
|
2
|
+
import { computed } from 'vue'
|
|
3
|
+
import TRadioGroup from '../TRadioGroup/index.uvue'
|
|
4
|
+
import TCheckboxGroup from '../TCheckboxGroup/index.uvue'
|
|
5
|
+
import TSwitch from '../TSwitch/index.uvue'
|
|
6
|
+
import TRate from '../TRate/index.uvue'
|
|
7
|
+
import TSlider from '../TSlider/index.uvue'
|
|
8
|
+
import type { FormOption, FormSchema, TFormProps, ComponentProps } from './type.uts'
|
|
9
|
+
import { useI18n } from '../../composables/useI18n.uts'
|
|
10
|
+
|
|
11
|
+
const props = withDefaults(defineProps<TFormProps>(), {
|
|
12
|
+
labelWidth: '110rpx',
|
|
13
|
+
layout: 'vertical',
|
|
14
|
+
hideButtons: false,
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
const emit = defineEmits(['submit', 'reset'])
|
|
18
|
+
|
|
19
|
+
const model = defineModel<Record<string, any>>({ default: () => ({}) })
|
|
20
|
+
const errors = reactive<Record<string, string>>({})
|
|
21
|
+
|
|
22
|
+
// 使用 i18n
|
|
23
|
+
const { $t } = useI18n()
|
|
24
|
+
|
|
25
|
+
// 按钮文本
|
|
26
|
+
const submitButtonText = computed(() => {
|
|
27
|
+
return props.submitText !== undefined && props.submitText !== ''
|
|
28
|
+
? props.submitText
|
|
29
|
+
: $t('form.submitButton')
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
const resetButtonText = computed(() => {
|
|
33
|
+
return props.resetText !== undefined && props.resetText !== ''
|
|
34
|
+
? props.resetText
|
|
35
|
+
: $t('form.resetButton')
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
// 获取单个配置项的布局方向(单个配置优先)
|
|
39
|
+
const getItemLayout = (item : FormSchema) : string => {
|
|
40
|
+
return item.layout || props.layout
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// 获取单个配置项的标签宽度(单个配置优先)
|
|
44
|
+
const getItemLabelWidth = (item : FormSchema) : string => {
|
|
45
|
+
return item.labelWidth || props.labelWidth
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const handlePlaceholder = (item : FormSchema) : string => {
|
|
49
|
+
if (item.componentProps?.placeholder) {
|
|
50
|
+
return item.componentProps.placeholder
|
|
51
|
+
}
|
|
52
|
+
return $t('form.inputPlaceholder', { label: item.label })
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const handleRange = (item : FormSchema) => {
|
|
56
|
+
const options = item.componentProps?.options as FormOption[] | undefined
|
|
57
|
+
return options?.map(o => o.label) || []
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const getSelectLabel = (item : FormSchema) => {
|
|
61
|
+
const options = item.componentProps?.options as FormOption[] | undefined
|
|
62
|
+
const opt = options?.find(o => o.value === model.value[item.field])
|
|
63
|
+
return opt?.label || ''
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const onSelectChange = (e : any, item : FormSchema) => {
|
|
67
|
+
const index = e.detail.value as number
|
|
68
|
+
const options = item.componentProps?.options as FormOption[] | undefined
|
|
69
|
+
const value = options?.[index]?.value ?? ''
|
|
70
|
+
model.value[item.field] = value
|
|
71
|
+
validateField(item)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const onTimeChange = (e : any, item : FormSchema) => {
|
|
75
|
+
model.value[item.field] = e.detail.value
|
|
76
|
+
validateField(item)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const validateField = (item : FormSchema) => {
|
|
80
|
+
if (item.required && !model.value[item.field]) {
|
|
81
|
+
errors[item.field] = $t('form.requiredError', { label: item.label })
|
|
82
|
+
} else {
|
|
83
|
+
delete errors[item.field]
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const onRadioChange = (value : string | number) => {
|
|
88
|
+
// 值已通过 v-model 更新,只需验证
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const onCheckboxChange = (values : (string | number)[]) => {
|
|
92
|
+
// 值已通过 v-model 更新,只需验证
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const onSwitchChange = (value : boolean, item : FormSchema) => {
|
|
96
|
+
model.value[item.field] = value
|
|
97
|
+
validateField(item)
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const onRateChange = (value : number, item : FormSchema) => {
|
|
101
|
+
model.value[item.field] = value
|
|
102
|
+
validateField(item)
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const onSliderChange = (value : number, item : FormSchema) => {
|
|
106
|
+
model.value[item.field] = value
|
|
107
|
+
validateField(item)
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const onFormSubmit = async (e : UniFormSubmitEvent) : Promise<{ valid : boolean, values : any }> => {
|
|
111
|
+
Object.keys(errors).forEach(k => delete errors[k])
|
|
112
|
+
|
|
113
|
+
let hasError = false
|
|
114
|
+
for (const item of props.schemas) {
|
|
115
|
+
validateField(item)
|
|
116
|
+
if (item.required && !model.value[item.field]) {
|
|
117
|
+
hasError = true
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (hasError) {
|
|
122
|
+
console.warn($t('form.validationFailed'))
|
|
123
|
+
return { valid: false, values: null }
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
try {
|
|
127
|
+
await emit('submit', model.value)
|
|
128
|
+
return { valid: true, values: model.value }
|
|
129
|
+
} catch (err) {
|
|
130
|
+
console.error($t('form.submitFailed'), err)
|
|
131
|
+
return { valid: false, values: null }
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const onFormReset = () => {
|
|
136
|
+
Object.keys(model.value).forEach(k => (model.value[k] = ''))
|
|
137
|
+
Object.keys(errors).forEach(k => delete errors[k])
|
|
138
|
+
emit('reset')
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
defineExpose({
|
|
142
|
+
submit: async () => {
|
|
143
|
+
return await onFormSubmit({} as UniFormSubmitEvent)
|
|
144
|
+
},
|
|
145
|
+
reset: () => {
|
|
146
|
+
onFormReset()
|
|
147
|
+
},
|
|
148
|
+
validate: () => {
|
|
149
|
+
Object.keys(errors).forEach(k => delete errors[k])
|
|
150
|
+
let hasError = false
|
|
151
|
+
for (const item of props.schemas) {
|
|
152
|
+
validateField(item)
|
|
153
|
+
if (item.required && !model.value[item.field]) {
|
|
154
|
+
hasError = true
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
return !hasError
|
|
158
|
+
},
|
|
159
|
+
getFormData: () => ({ ...model.value }),
|
|
160
|
+
getErrors: () => ({ ...errors })
|
|
161
|
+
})
|
|
162
|
+
</script>
|
|
163
|
+
|
|
164
|
+
<template>
|
|
165
|
+
<view class="t-form">
|
|
166
|
+
<form @submit="onFormSubmit" @reset="onFormReset">
|
|
167
|
+
<view v-for="(item, index) in schemas" class="form-item" :key="index">
|
|
168
|
+
<view class="form-item-content" :class="`form-item--${getItemLayout(item)}`">
|
|
169
|
+
<view class="form-label" >
|
|
170
|
+
<text class="text" :style="{ width: getItemLayout(item) === 'horizontal' ? getItemLabelWidth(item) : labelWidth }">{{ item.label }}</text>
|
|
171
|
+
<text v-if="item.required" class="required">*</text>
|
|
172
|
+
</view>
|
|
173
|
+
|
|
174
|
+
<view class="form-control">
|
|
175
|
+
<!-- 自定义插槽 -->
|
|
176
|
+
<slot :name="item.field" :item="item" :value="model[item.field]"
|
|
177
|
+
:error="errors[item.field]">
|
|
178
|
+
|
|
179
|
+
<!-- 输入框 -->
|
|
180
|
+
<input v-if="item.component === 'Input'" v-model="model[item.field]"
|
|
181
|
+
:placeholder="handlePlaceholder(item)" :name="item.field" class="input"
|
|
182
|
+
v-bind="item.componentProps || {}" @input="validateField(item)" />
|
|
183
|
+
|
|
184
|
+
<!-- 数字输入 -->
|
|
185
|
+
<input v-else-if="item.component === 'InputNumber'" v-model="model[item.field]"
|
|
186
|
+
:placeholder="handlePlaceholder(item)" type="number" :name="item.field" class="input"
|
|
187
|
+
v-bind="item.componentProps || {}" @input="validateField(item)" />
|
|
188
|
+
|
|
189
|
+
<!-- 文本域 -->
|
|
190
|
+
<textarea v-else-if="item.component === 'Textarea'" v-model="model[item.field]"
|
|
191
|
+
:placeholder="handlePlaceholder(item)" :name="item.field" class="textarea"
|
|
192
|
+
v-bind="item.componentProps || {}" @input="validateField(item)"></textarea>
|
|
193
|
+
|
|
194
|
+
<!-- 下拉选择 -->
|
|
195
|
+
<picker v-else-if="item.component === 'Select'" mode="selector" :name="item.field"
|
|
196
|
+
:range="handleRange(item)" v-bind="item.componentProps || {}"
|
|
197
|
+
@change="onSelectChange($event, item)">
|
|
198
|
+
<view class="picker">
|
|
199
|
+
{{ getSelectLabel(item) || $t('form.selectPlaceholder') }}
|
|
200
|
+
</view>
|
|
201
|
+
</picker>
|
|
202
|
+
|
|
203
|
+
<!-- 日期选择 -->
|
|
204
|
+
<picker v-else-if="item.component === 'Date'" mode="date" :name="item.field"
|
|
205
|
+
:value="model[item.field]" v-bind="item.componentProps || {}"
|
|
206
|
+
@change="onTimeChange($event, item)">
|
|
207
|
+
<view class="picker">
|
|
208
|
+
{{ model[item.field] || $t('form.datePlaceholder') }}
|
|
209
|
+
</view>
|
|
210
|
+
</picker>
|
|
211
|
+
|
|
212
|
+
<!-- 时间选择 -->
|
|
213
|
+
<picker v-else-if="item.component === 'Time'" mode="time" :name="item.field"
|
|
214
|
+
:value="model[item.field]" v-bind="item.componentProps || {}"
|
|
215
|
+
@change="onTimeChange($event, item)">
|
|
216
|
+
<view class="picker">
|
|
217
|
+
{{ model[item.field] || $t('form.timePlaceholder') }}
|
|
218
|
+
</view>
|
|
219
|
+
</picker>
|
|
220
|
+
|
|
221
|
+
<!-- 单选 -->
|
|
222
|
+
<TRadioGroup v-else-if="item.component === 'Radio'" v-model="model[item.field]"
|
|
223
|
+
:options="(item.componentProps?.options as FormOption[])" :name="item.field"
|
|
224
|
+
v-bind="item.componentProps || {}" @change="validateField(item)" />
|
|
225
|
+
|
|
226
|
+
<!-- 多选 -->
|
|
227
|
+
<TCheckboxGroup v-else-if="item.component === 'Checkbox'" v-model="model[item.field]"
|
|
228
|
+
:options="(item.componentProps?.options as FormOption[])" :name="item.field"
|
|
229
|
+
v-bind="item.componentProps || {}" @change="validateField(item)" />
|
|
230
|
+
|
|
231
|
+
<!-- 开关 -->
|
|
232
|
+
<TSwitch v-else-if="item.component === 'Switch'" :checked="model[item.field]"
|
|
233
|
+
v-bind="item.componentProps || {}" @change="onSwitchChange($event, item)" />
|
|
234
|
+
|
|
235
|
+
<!-- 评分 -->
|
|
236
|
+
<TRate v-else-if="item.component === 'Rate'" :value="model[item.field] || 0"
|
|
237
|
+
v-bind="item.componentProps || {}" @change="onRateChange($event, item)" />
|
|
238
|
+
|
|
239
|
+
<!-- 滑块 -->
|
|
240
|
+
<TSlider v-else-if="item.component === 'Slider'" :value="model[item.field] || 0"
|
|
241
|
+
v-bind="item.componentProps || {}" @change="onSliderChange($event, item)" />
|
|
242
|
+
</slot>
|
|
243
|
+
</view>
|
|
244
|
+
</view>
|
|
245
|
+
|
|
246
|
+
<!-- 错误提示 -->
|
|
247
|
+
<view v-if="errors[item.field]" class="error-message">
|
|
248
|
+
<text>{{ errors[item.field] }}</text>
|
|
249
|
+
</view>
|
|
250
|
+
</view>
|
|
251
|
+
|
|
252
|
+
<!-- 按钮区域 -->
|
|
253
|
+
<view v-if="!hideButtons" class="form-footer">
|
|
254
|
+
<button class="btn btn-submit" form-type="submit">{{ submitButtonText }}</button>
|
|
255
|
+
<button class="btn btn-reset" form-type="reset">{{ resetButtonText }}</button>
|
|
256
|
+
</view>
|
|
257
|
+
</form>
|
|
258
|
+
</view>
|
|
259
|
+
</template>
|
|
260
|
+
|
|
261
|
+
<style lang="scss" scoped>
|
|
262
|
+
.t-form {
|
|
263
|
+
padding: 32rpx;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
.form-item {
|
|
267
|
+
margin-bottom: 32rpx;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
.form-item-content {
|
|
271
|
+
&.form-item--horizontal {
|
|
272
|
+
display: flex;
|
|
273
|
+
flex-direction: row;
|
|
274
|
+
align-items: flex-start;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
&.form-item--vertical {
|
|
278
|
+
display: flex;
|
|
279
|
+
flex-direction: column;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
.form-label {
|
|
284
|
+
display: flex;
|
|
285
|
+
flex-direction: row;
|
|
286
|
+
gap: 2px;
|
|
287
|
+
text-align: right;
|
|
288
|
+
margin-bottom: 12rpx;
|
|
289
|
+
font-weight: 500;
|
|
290
|
+
font-size: 28rpx;
|
|
291
|
+
padding: 10rpx 0;
|
|
292
|
+
margin-right: 12rpx;
|
|
293
|
+
|
|
294
|
+
.form-item-content.form-item--vertical & {
|
|
295
|
+
text-align: left;
|
|
296
|
+
margin-right: 0;
|
|
297
|
+
margin-bottom: 16rpx;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
.text {
|
|
301
|
+
display: flow;
|
|
302
|
+
text-align: justify;
|
|
303
|
+
text-justify: inter-ideograph;
|
|
304
|
+
text-align-last: justify;
|
|
305
|
+
width: 100%;
|
|
306
|
+
letter-spacing: 0.1em;
|
|
307
|
+
word-break: break-all;
|
|
308
|
+
white-space: normal;
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
.required {
|
|
313
|
+
color: #ff4d4f;
|
|
314
|
+
margin-left: 4rpx;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
.form-control {
|
|
318
|
+
flex: 1;
|
|
319
|
+
width: 100%;
|
|
320
|
+
|
|
321
|
+
.form-item-content.form-item--horizontal & {
|
|
322
|
+
width: auto;
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
.input,
|
|
327
|
+
.textarea,
|
|
328
|
+
.picker {
|
|
329
|
+
width: 100%;
|
|
330
|
+
padding: 20rpx 24rpx;
|
|
331
|
+
font-size: 28rpx;
|
|
332
|
+
color: #333;
|
|
333
|
+
background-color: #f5f5f5;
|
|
334
|
+
border-radius: 8rpx;
|
|
335
|
+
border: 1rpx solid #e0e0e0;
|
|
336
|
+
transition: all 0.3s;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
.input:focus,
|
|
340
|
+
.textarea:focus {
|
|
341
|
+
background-color: #fff;
|
|
342
|
+
border-color: #007aff;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
.textarea {
|
|
346
|
+
min-height: 120rpx;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
.picker {
|
|
350
|
+
display: flex;
|
|
351
|
+
align-items: center;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
|
|
355
|
+
|
|
356
|
+
.error-message {
|
|
357
|
+
margin-top: 8rpx;
|
|
358
|
+
font-size: 24rpx;
|
|
359
|
+
color: #ff4d4f;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
.form-footer {
|
|
363
|
+
display: flex;
|
|
364
|
+
flex-direction: row;
|
|
365
|
+
gap: 24rpx;
|
|
366
|
+
margin-top: 48rpx;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
.btn {
|
|
370
|
+
flex: 1;
|
|
371
|
+
height: 88rpx;
|
|
372
|
+
line-height: 88rpx;
|
|
373
|
+
text-align: center;
|
|
374
|
+
font-size: 32rpx;
|
|
375
|
+
border-radius: 8rpx;
|
|
376
|
+
transition: all 0.3s;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
.btn-submit {
|
|
380
|
+
background-color: #007aff;
|
|
381
|
+
color: #fff;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
.btn-reset {
|
|
385
|
+
background-color: #fff;
|
|
386
|
+
color: #007aff;
|
|
387
|
+
border: 1rpx solid #007aff;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
.btn:active {
|
|
391
|
+
opacity: 0.7;
|
|
392
|
+
transform: scale(0.98);
|
|
393
|
+
}
|
|
394
|
+
</style>
|