tang-ui-x 1.1.2 → 1.1.3

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