mirror of
https://gitee.com/wot-design-uni/wot-design-uni.git
synced 2025-12-06 17:18:40 +08:00
parent
8d528cb9c0
commit
8e6096ab74
@ -1,4 +1,4 @@
|
||||
# Form 表单 <el-tag text style="vertical-align: middle;margin-left:8px;" effect="plain">0.2.0</el-tag>
|
||||
# Form 表单
|
||||
|
||||
用于数据录入、校验,支持输入框、单选框、复选框、文件上传等类型,常见的 form 表单为`单元格`形式的展示,即左侧为表单的标题描述,右侧为表单的输入。
|
||||
|
||||
@ -394,7 +394,7 @@ const submit = () => {
|
||||
|
||||
## 指定字段校验
|
||||
|
||||
`validate` 方法可以传入一个 `prop` 参数,指定校验的字段,可以实现在表单组件的`blur`、`change`等事件触发时对该字段的校验。
|
||||
`validate` 方法可以传入一个 `prop` 参数,指定校验的字段,可以实现在表单组件的`blur`、`change`等事件触发时对该字段的校验。`prop` 参数也可以是一个字段数组,指定多个字段进行校验。
|
||||
|
||||
::: details 查看指定字段校验示例
|
||||
::: code-group
|
||||
@ -424,6 +424,7 @@ const submit = () => {
|
||||
</wd-cell-group>
|
||||
<view class="footer">
|
||||
<wd-button type="primary" size="large" @click="handleSubmit" block>提交</wd-button>
|
||||
<wd-button type="primary" size="large" @click="handleValidate" block>校验用户名和密码</wd-button>
|
||||
</view>
|
||||
</wd-form>
|
||||
```
|
||||
@ -459,6 +460,85 @@ function handleSubmit() {
|
||||
console.log(error, 'error')
|
||||
})
|
||||
}
|
||||
|
||||
function handleValidate() {
|
||||
form
|
||||
.value!.validate(['value1', 'value2'])
|
||||
.then(({ valid, errors }) => {
|
||||
if (valid) {
|
||||
showSuccess({
|
||||
msg: '校验通过'
|
||||
})
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log(error, 'error')
|
||||
})
|
||||
}
|
||||
</script>
|
||||
```
|
||||
|
||||
```css [css]
|
||||
.footer {
|
||||
padding: 12px;
|
||||
}
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
## 不对隐藏组件做校验
|
||||
|
||||
在表单中,如果某个组件使用 `v-if` 隐藏,则不会对该组件进行校验。
|
||||
|
||||
::: details 查看不对隐藏组件做校验示例
|
||||
::: code-group
|
||||
|
||||
```html [vue]
|
||||
<wd-form ref="form" :model="model" :rules="rules">
|
||||
<wd-cell-group border>
|
||||
<wd-input
|
||||
label="用户名"
|
||||
label-width="100px"
|
||||
prop="value1"
|
||||
clearable
|
||||
v-model="model.value1"
|
||||
placeholder="请输入用户名"
|
||||
:rules="[{ required: true, message: '请填写用户名' }]"
|
||||
/>
|
||||
<wd-input
|
||||
v-if="showPassword"
|
||||
label="密码"
|
||||
label-width="100px"
|
||||
prop="value2"
|
||||
show-password
|
||||
clearable
|
||||
v-model="model.value2"
|
||||
placeholder="请输入密码"
|
||||
:rules="[{ required: true, message: '请填写密码' }]"
|
||||
/>
|
||||
</wd-cell-group>
|
||||
<view class="footer">
|
||||
<wd-button type="primary" size="large" @click="handleSubmit" block>提交</wd-button>
|
||||
</view>
|
||||
</wd-form>
|
||||
```
|
||||
|
||||
```typescript [typescript]
|
||||
<script lang="ts" setup>
|
||||
import { useToast } from '@/uni_modules/wot-design-uni'
|
||||
import type { FormInstance } from '@/uni_modules/wot-design-uni/components/wd-form/types'
|
||||
import { reactive, ref } from 'vue'
|
||||
|
||||
const { success: showSuccess } = useToast()
|
||||
const model = reactive<{
|
||||
value1: string
|
||||
value2: string
|
||||
}>({
|
||||
value1: '',
|
||||
value2: ''
|
||||
})
|
||||
})
|
||||
}
|
||||
</script>
|
||||
```
|
||||
|
||||
@ -938,7 +1018,7 @@ function handleIconClick() {
|
||||
|
||||
| 事件名称 | 说明 | 参数 | 最低版本 |
|
||||
| -------- | ------------------------------------------------------------------------------ | --------------- | -------- |
|
||||
| validate | 验证表单,支持传入一个 prop 来验证单个表单项,不传入 prop 时,会验证所有表单项 | `prop?: string` | 0.2.0 |
|
||||
| validate | 验证表单,支持传入一个 prop 来验证单个表单项,不传入 prop 时,会验证所有表单项,$LOWEST_VERSION$ 版本起支持传入数组 | `prop?: string\|string[]` | 0.2.0 |
|
||||
| reset | 重置校验结果 | - | 0.2.0 |
|
||||
|
||||
## 外部样式类
|
||||
|
||||
@ -1,3 +1,12 @@
|
||||
<!--
|
||||
* @Author: weisheng
|
||||
* @Date: 2024-11-09 12:35:25
|
||||
* @LastEditTime: 2025-01-13 23:46:45
|
||||
* @LastEditors: weisheng
|
||||
* @Description:
|
||||
* @FilePath: /wot-design-uni/src/pages/form/demo2.vue
|
||||
* 记得注释
|
||||
-->
|
||||
<template>
|
||||
<page-wraper>
|
||||
<wd-form ref="form" :model="model" :reset-on-change="false">
|
||||
@ -22,11 +31,23 @@
|
||||
placeholder="玛卡巴卡单号"
|
||||
:rules="[{ required: true, message: '请填写玛卡巴卡单号' }]"
|
||||
/>
|
||||
|
||||
<wd-input
|
||||
label="玛卡巴卡id"
|
||||
prop="id"
|
||||
label-width="100px"
|
||||
clearable
|
||||
@blur="handleBlur('id')"
|
||||
v-model="model.id"
|
||||
placeholder="玛卡巴卡id"
|
||||
:rules="[{ required: true, message: '请填写玛卡巴卡id' }]"
|
||||
/>
|
||||
</wd-cell-group>
|
||||
</wd-form>
|
||||
|
||||
<view class="footer">
|
||||
<wd-button type="primary" size="large" block @click="handleSubmit">提交</wd-button>
|
||||
<wd-button type="primary" @click="handleSubmit">提交</wd-button>
|
||||
<wd-button type="primary" @click="handleValidate">校验单号和ID</wd-button>
|
||||
</view>
|
||||
</page-wraper>
|
||||
</template>
|
||||
@ -38,9 +59,11 @@ import { reactive, ref } from 'vue'
|
||||
const model = reactive<{
|
||||
name: string
|
||||
phoneNumber: string
|
||||
id: string
|
||||
}>({
|
||||
name: '',
|
||||
phoneNumber: ''
|
||||
phoneNumber: '',
|
||||
id: ''
|
||||
})
|
||||
|
||||
const { success: showSuccess } = useToast()
|
||||
@ -50,6 +73,19 @@ function handleBlur(prop: string) {
|
||||
form.value!.validate(prop)
|
||||
}
|
||||
|
||||
function handleValidate() {
|
||||
form
|
||||
.value!.validate(['phoneNumber', 'id'])
|
||||
.then(({ valid }) => {
|
||||
if (valid) {
|
||||
showSuccess('校验通过')
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log(error, 'error')
|
||||
})
|
||||
}
|
||||
|
||||
function handleSubmit() {
|
||||
form
|
||||
.value!.validate()
|
||||
@ -66,5 +102,6 @@ function handleSubmit() {
|
||||
<style lang="scss" scoped>
|
||||
.footer {
|
||||
padding: 12px;
|
||||
display: flex;
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -2,7 +2,6 @@
|
||||
<view>
|
||||
<page-wraper>
|
||||
<wd-message-box />
|
||||
<wd-toast />
|
||||
<wd-form ref="form" :model="model" :rules="rules">
|
||||
<wd-cell-group custom-class="group" title="基础信息" border>
|
||||
<wd-input
|
||||
@ -89,6 +88,15 @@
|
||||
<wd-switch v-model="model.switchVal" />
|
||||
</view>
|
||||
</wd-cell>
|
||||
<wd-input
|
||||
label="折扣"
|
||||
v-if="model.switchVal"
|
||||
label-width="100px"
|
||||
prop="discount"
|
||||
placeholder="请输入优惠金额"
|
||||
clearable
|
||||
v-model="model.discount"
|
||||
/>
|
||||
<wd-input
|
||||
label="歪比巴卜"
|
||||
label-width="100px"
|
||||
@ -100,7 +108,11 @@
|
||||
/>
|
||||
<wd-input label="玛卡巴卡" label-width="100px" prop="phone" placeholder="请输入玛卡巴卡" clearable v-model="model.phone" />
|
||||
<wd-cell title="活动图片" title-width="100px" prop="fileList">
|
||||
<wd-upload :file-list="model.fileList" action="https://ftf.jd.com/api/uploadImg" @change="handleFileChange"></wd-upload>
|
||||
<wd-upload
|
||||
:file-list="model.fileList"
|
||||
action="https://mockapi.eolink.com/zhTuw2P8c29bc981a741931bdd86eb04dc1e8fd64865cb5/upload"
|
||||
@change="handleFileChange"
|
||||
></wd-upload>
|
||||
</wd-cell>
|
||||
</wd-cell-group>
|
||||
<view class="tip">
|
||||
@ -144,6 +156,7 @@ const model = reactive<{
|
||||
phone: string
|
||||
read: boolean
|
||||
fileList: UploadFileItem[]
|
||||
discount: number
|
||||
}>({
|
||||
couponName: '',
|
||||
platform: [],
|
||||
@ -159,7 +172,8 @@ const model = reactive<{
|
||||
cardId: '',
|
||||
phone: '',
|
||||
read: false,
|
||||
fileList: []
|
||||
fileList: [],
|
||||
discount: 1
|
||||
})
|
||||
|
||||
const rules: FormRules = {
|
||||
@ -319,6 +333,19 @@ const rules: FormRules = {
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
discount: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入优惠金额',
|
||||
validator: (value) => {
|
||||
if (value) {
|
||||
return Promise.resolve()
|
||||
} else {
|
||||
return Promise.reject()
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@ -397,6 +424,9 @@ function handleSubmit() {
|
||||
form
|
||||
.value!.validate()
|
||||
.then(({ valid, errors }) => {
|
||||
if (valid) {
|
||||
toast.success('提交成功')
|
||||
}
|
||||
console.log(valid)
|
||||
console.log(errors)
|
||||
})
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
/*
|
||||
* @Author: weisheng
|
||||
* @Date: 2023-12-14 11:21:58
|
||||
* @LastEditTime: 2024-03-18 12:50:41
|
||||
* @LastEditTime: 2025-01-11 13:31:20
|
||||
* @LastEditors: weisheng
|
||||
* @Description:
|
||||
* @FilePath: \wot-design-uni\src\uni_modules\wot-design-uni\components\wd-form\types.ts
|
||||
* @FilePath: /wot-design-uni/src/uni_modules/wot-design-uni/components/wd-form/types.ts
|
||||
* 记得注释
|
||||
*/
|
||||
import { type ComponentPublicInstance, type ExtractPropTypes, type InjectionKey, type PropType } from 'vue'
|
||||
@ -72,7 +72,7 @@ export type FormExpose = {
|
||||
* 表单校验
|
||||
* @param prop 指定校验字段
|
||||
*/
|
||||
validate: (prop?: string) => Promise<{
|
||||
validate: (prop?: string | Array<string>) => Promise<{
|
||||
valid: boolean
|
||||
errors: ErrorMessage[]
|
||||
}>
|
||||
|
||||
@ -19,7 +19,7 @@ export default {
|
||||
<script lang="ts" setup>
|
||||
import wdToast from '../wd-toast/wd-toast.vue'
|
||||
import { reactive, watch } from 'vue'
|
||||
import { deepClone, getPropByPath, isDef, isPromise, isString } from '../common/util'
|
||||
import { deepClone, getPropByPath, isArray, isDef, isPromise, isString } from '../common/util'
|
||||
import { useChildren } from '../composables/useChildren'
|
||||
import { useToast } from '../wd-toast'
|
||||
import { type FormRules, FORM_KEY, type ErrorMessage, formProps, type FormExpose } from './types'
|
||||
@ -30,7 +30,7 @@ const props = defineProps(formProps)
|
||||
const { children, linkChildren } = useChildren(FORM_KEY)
|
||||
let errorMessages = reactive<Record<string, string>>({})
|
||||
|
||||
linkChildren({ props, errorMessages: errorMessages })
|
||||
linkChildren({ props, errorMessages })
|
||||
|
||||
watch(
|
||||
() => props.model,
|
||||
@ -44,22 +44,33 @@ watch(
|
||||
|
||||
/**
|
||||
* 表单校验
|
||||
* @param prop 指定校验字段
|
||||
* @param prop 指定校验字段或字段数组
|
||||
*/
|
||||
async function validate(prop?: string): Promise<{ valid: boolean; errors: ErrorMessage[] }> {
|
||||
async function validate(prop?: string | string[]): Promise<{ valid: boolean; errors: ErrorMessage[] }> {
|
||||
const errors: ErrorMessage[] = []
|
||||
let valid: boolean = true
|
||||
const promises: Promise<void>[] = []
|
||||
const formRules: FormRules = getMergeRules()
|
||||
const rulesToValidate: FormRules = prop ? { [prop]: formRules[prop] } : formRules
|
||||
for (const prop in rulesToValidate) {
|
||||
const rules = rulesToValidate[prop]
|
||||
const value = getPropByPath(props.model, prop)
|
||||
const propsToValidate = isArray(prop) ? prop : isDef(prop) ? [prop] : []
|
||||
const rulesToValidate: FormRules =
|
||||
propsToValidate.length > 0
|
||||
? propsToValidate.reduce((acc, key) => {
|
||||
if (formRules[key]) {
|
||||
acc[key] = formRules[key]
|
||||
}
|
||||
return acc
|
||||
}, {} as FormRules)
|
||||
: formRules
|
||||
|
||||
for (const propName in rulesToValidate) {
|
||||
const rules = rulesToValidate[propName]
|
||||
const value = getPropByPath(props.model, propName)
|
||||
|
||||
if (rules && rules.length > 0) {
|
||||
for (const rule of rules) {
|
||||
if (rule.required && (!isDef(value) || value === '')) {
|
||||
errors.push({
|
||||
prop,
|
||||
prop: propName,
|
||||
message: rule.message
|
||||
})
|
||||
valid = false
|
||||
@ -67,7 +78,7 @@ async function validate(prop?: string): Promise<{ valid: boolean; errors: ErrorM
|
||||
}
|
||||
if (rule.pattern && !rule.pattern.test(value)) {
|
||||
errors.push({
|
||||
prop,
|
||||
prop: propName,
|
||||
message: rule.message
|
||||
})
|
||||
valid = false
|
||||
@ -82,13 +93,13 @@ async function validate(prop?: string): Promise<{ valid: boolean; errors: ErrorM
|
||||
.then((res) => {
|
||||
if (typeof res === 'string') {
|
||||
errors.push({
|
||||
prop,
|
||||
prop: propName,
|
||||
message: res
|
||||
})
|
||||
valid = false
|
||||
} else if (typeof res === 'boolean' && !res) {
|
||||
errors.push({
|
||||
prop,
|
||||
prop: propName,
|
||||
message: rule.message
|
||||
})
|
||||
valid = false
|
||||
@ -96,14 +107,14 @@ async function validate(prop?: string): Promise<{ valid: boolean; errors: ErrorM
|
||||
})
|
||||
.catch((error?: string | Error) => {
|
||||
const message = isDef(error) ? (isString(error) ? error : error.message || rule.message) : rule.message
|
||||
errors.push({ prop, message })
|
||||
errors.push({ prop: propName, message })
|
||||
valid = false
|
||||
})
|
||||
)
|
||||
} else {
|
||||
if (!result) {
|
||||
errors.push({
|
||||
prop,
|
||||
prop: propName,
|
||||
message: rule.message
|
||||
})
|
||||
valid = false
|
||||
@ -119,8 +130,8 @@ async function validate(prop?: string): Promise<{ valid: boolean; errors: ErrorM
|
||||
showMessage(errors)
|
||||
|
||||
if (valid) {
|
||||
if (prop) {
|
||||
clearMessage(prop)
|
||||
if (propsToValidate.length) {
|
||||
propsToValidate.forEach(clearMessage)
|
||||
} else {
|
||||
clearMessage()
|
||||
}
|
||||
@ -135,6 +146,15 @@ async function validate(prop?: string): Promise<{ valid: boolean; errors: ErrorM
|
||||
// 合并子组件的rules到父组件的rules
|
||||
function getMergeRules() {
|
||||
const mergedRules: FormRules = deepClone(props.rules)
|
||||
const childrenProps = children.map((child) => child.prop)
|
||||
|
||||
// 过滤掉在 children 中不存在对应子组件的规则
|
||||
Object.keys(mergedRules).forEach((key) => {
|
||||
if (!childrenProps.includes(key)) {
|
||||
delete mergedRules[key]
|
||||
}
|
||||
})
|
||||
|
||||
children.forEach((item) => {
|
||||
if (isDef(item.prop) && isDef(item.rules) && item.rules.length) {
|
||||
if (mergedRules[item.prop]) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user