tang-ui-x 1.1.4 → 1.1.5
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/TForm/index.uvue +51 -34
- package/package.json +1 -1
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
import TSwitch from '../TSwitch/index.uvue'
|
|
6
6
|
import TRate from '../TRate/index.uvue'
|
|
7
7
|
import TSlider from '../TSlider/index.uvue'
|
|
8
|
+
import TSelect from '../TSelect/index.uvue'
|
|
8
9
|
import type { FormOption, FormSchema, TFormProps, ComponentProps } from './type.uts'
|
|
9
10
|
import { useI18n } from '../../composables/useI18n.uts'
|
|
10
11
|
|
|
@@ -52,23 +53,9 @@
|
|
|
52
53
|
return $t('form.inputPlaceholder', { label: item.label })
|
|
53
54
|
}
|
|
54
55
|
|
|
55
|
-
const
|
|
56
|
+
const getSelectOptions = (item : FormSchema) => {
|
|
56
57
|
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)
|
|
58
|
+
return options?.map(o => ({ label: o.label, value: o.value })) || []
|
|
72
59
|
}
|
|
73
60
|
|
|
74
61
|
const onTimeChange = (e : any, item : FormSchema) => {
|
|
@@ -79,11 +66,23 @@
|
|
|
79
66
|
const validateField = (item : FormSchema) => {
|
|
80
67
|
if (item.required && !model.value[item.field]) {
|
|
81
68
|
errors[item.field] = $t('form.requiredError', { label: item.label })
|
|
69
|
+
// 触发抖动动画
|
|
70
|
+
triggerShake(item.field)
|
|
82
71
|
} else {
|
|
83
72
|
delete errors[item.field]
|
|
84
73
|
}
|
|
85
74
|
}
|
|
86
75
|
|
|
76
|
+
// 抖动动画状态
|
|
77
|
+
const shakeFields = reactive<Record<string, boolean>>({})
|
|
78
|
+
|
|
79
|
+
const triggerShake = (field : string) => {
|
|
80
|
+
shakeFields[field] = true
|
|
81
|
+
setTimeout(() => {
|
|
82
|
+
shakeFields[field] = false
|
|
83
|
+
}, 500)
|
|
84
|
+
}
|
|
85
|
+
|
|
87
86
|
const onRadioChange = (value : string | number) => {
|
|
88
87
|
// 值已通过 v-model 更新,只需验证
|
|
89
88
|
}
|
|
@@ -164,7 +163,9 @@
|
|
|
164
163
|
<template>
|
|
165
164
|
<view class="t-form">
|
|
166
165
|
<form @submit="onFormSubmit" @reset="onFormReset">
|
|
167
|
-
<view v-for="(item, index) in schemas" class="form-item"
|
|
166
|
+
<view v-for="(item, index) in schemas" class="form-item"
|
|
167
|
+
:class="{ 'form-item-error': errors[item.field], 'form-item-shake': shakeFields[item.field] }"
|
|
168
|
+
:key="index">
|
|
168
169
|
<view class="form-item-content" :class="`form-item--${getItemLayout(item)}`">
|
|
169
170
|
<view class="form-label" >
|
|
170
171
|
<text class="text" :style="{ width: getItemLayout(item) === 'horizontal' ? getItemLabelWidth(item) : labelWidth }">{{ item.label }}</text>
|
|
@@ -192,13 +193,12 @@
|
|
|
192
193
|
v-bind="item.componentProps || {}" @input="validateField(item)"></textarea>
|
|
193
194
|
|
|
194
195
|
<!-- 下拉选择 -->
|
|
195
|
-
<
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
</picker>
|
|
196
|
+
<TSelect v-else-if="item.component === 'Select'"
|
|
197
|
+
v-model="model[item.field]"
|
|
198
|
+
:options="getSelectOptions(item)"
|
|
199
|
+
:placeholder="item.componentProps?.placeholder || $t('form.selectPlaceholder')"
|
|
200
|
+
v-bind="item.componentProps || {}"
|
|
201
|
+
@change="validateField(item)" />
|
|
202
202
|
|
|
203
203
|
<!-- 日期选择 -->
|
|
204
204
|
<picker v-else-if="item.component === 'Date'" mode="date" :name="item.field"
|
|
@@ -245,7 +245,7 @@
|
|
|
245
245
|
|
|
246
246
|
<!-- 错误提示 -->
|
|
247
247
|
<view v-if="errors[item.field]" class="error-message">
|
|
248
|
-
<text>{{ errors[item.field] }}</text>
|
|
248
|
+
<text class="error-text">{{ errors[item.field] }}</text>
|
|
249
249
|
</view>
|
|
250
250
|
</view>
|
|
251
251
|
|
|
@@ -265,6 +265,31 @@
|
|
|
265
265
|
|
|
266
266
|
.form-item {
|
|
267
267
|
margin-bottom: 32rpx;
|
|
268
|
+
padding: 24rpx;
|
|
269
|
+
border-radius: 12rpx;
|
|
270
|
+
border: 2rpx solid transparent;
|
|
271
|
+
transition: all 0.3s ease;
|
|
272
|
+
|
|
273
|
+
&.form-item-error {
|
|
274
|
+
border-color: #ff4d4f;
|
|
275
|
+
background-color: #fff1f0;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
&.form-item-shake {
|
|
279
|
+
animation: shake 0.5s ease-in-out;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
@keyframes shake {
|
|
284
|
+
0%, 100% {
|
|
285
|
+
transform: translateX(0);
|
|
286
|
+
}
|
|
287
|
+
10%, 30%, 50%, 70%, 90% {
|
|
288
|
+
transform: translateX(-8rpx);
|
|
289
|
+
}
|
|
290
|
+
20%, 40%, 60%, 80% {
|
|
291
|
+
transform: translateX(8rpx);
|
|
292
|
+
}
|
|
268
293
|
}
|
|
269
294
|
|
|
270
295
|
.form-item-content {
|
|
@@ -324,8 +349,7 @@
|
|
|
324
349
|
}
|
|
325
350
|
|
|
326
351
|
.input,
|
|
327
|
-
.textarea
|
|
328
|
-
.picker {
|
|
352
|
+
.textarea {
|
|
329
353
|
width: 100%;
|
|
330
354
|
padding: 20rpx 24rpx;
|
|
331
355
|
font-size: 28rpx;
|
|
@@ -346,13 +370,6 @@
|
|
|
346
370
|
min-height: 120rpx;
|
|
347
371
|
}
|
|
348
372
|
|
|
349
|
-
.picker {
|
|
350
|
-
display: flex;
|
|
351
|
-
align-items: center;
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
373
|
.error-message {
|
|
357
374
|
margin-top: 8rpx;
|
|
358
375
|
font-size: 24rpx;
|