Skip to content

表单组件 基础版本

  • 实现的就是通过配置json格式来动态的渲染表单

(最重要) 表单的数据结构

interfaces/chooseForm/type

ts
// 引入验证规则
import { type RuleItem } from './rules'
// 引入css style类型
import { type CSSProperties } from 'vue'
// 表单每一项的配置选项
export interface FormOptions {
    // 表单显示的元素
    // 表单项显示的元素
    type:
        | 'cascader'
        | 'checkbox'
        | 'checkbox-group'
        | 'checkbox-button'
        | 'color-picker'
        | 'date-picker'
        | 'input'
        | 'input-number'
        | 'radio'
        | 'radio-group'
        | 'radio-button'
        | 'rate'
        | 'select'
        | 'option'
        | 'slider'
        | 'switch'
        | 'time-picker'
        | 'time-select'
        | 'transfer'
        | 'upload'
        | 'editor'

    // 表单项的值
    value: any
    // 表单的表示
    prop: string
    // 表单项的label
    label?: string
    // 表单项的验证规则
    rules?: RuleItem[]
    // 表单项的占位符
    placeholder?: string
    // 表单元素特有的属性
    attrs?: {
        // css样式
        style?: CSSProperties
        clearable?: boolean
        showPassword?: boolean
        disabled?: boolean
    }
}

interfaces/chooseForm/rules

  • 这个rules 就是验证规则
ts
/*
 * @Author: jsopy
 * @Date: 2025-02-02 07:05:39
 * @LastEditTime: 2025-02-02 07:05:45
 * @FilePath: /zujian/src/interfaces/chooseForm/rules.ts
 * @Description: 验证规则
 *
 */
export type RuleType =
    | 'string'
    | 'number'
    | 'boolean'
    | 'method'
    | 'regexp'
    | 'integer'
    | 'float'
    | 'array'
    | 'object'
    | 'enum'
    | 'date'
    | 'url'
    | 'hex'
    | 'email'
    | 'pattern'
    | 'any'

export interface ValidateOption {
    // whether to suppress internal warning
    suppressWarning?: boolean

    // when the first validation rule generates an error stop processed
    first?: boolean

    // when the first validation rule of the specified field generates an error stop the field processed, 'true' means all fields.
    firstFields?: boolean | string[]

    messages?: Partial<ValidateMessages>

    /** The name of rules need to be trigger. Will validate all rules if leave empty */
    keys?: string[]

    error?: (rule: InternalRuleItem, message: string) => ValidateError
}

export type SyncErrorType = Error | string
export type SyncValidateResult = boolean | SyncErrorType | SyncErrorType[]
export type ValidateResult = void | Promise<void> | SyncValidateResult

export interface RuleItem {
    type?: RuleType // default type is 'string'
    required?: boolean
    pattern?: RegExp | string
    min?: number // Range of type 'string' and 'array'
    max?: number // Range of type 'string' and 'array'
    len?: number // Length of type 'string' and 'array'
    enum?: Array<string | number | boolean | null | undefined> // possible values of type 'enum'
    whitespace?: boolean
    trigger?: string | string[]
    fields?: Record<string, Rule> // ignore when without required
    options?: ValidateOption
    defaultField?: Rule // 'object' or 'array' containing validation rules
    transform?: (value: Value) => Value
    message?: string | ((a?: string) => string)
    asyncValidator?: (
        rule: InternalRuleItem,
        value: Value,
        callback: (error?: string | Error) => void,
        source: Values,
        options: ValidateOption
    ) => void | Promise<void>
    validator?: (
        rule: InternalRuleItem,
        value: Value,
        callback: (error?: string | Error) => void,
        source: Values,
        options: ValidateOption
    ) => SyncValidateResult | void
}

export type Rule = RuleItem | RuleItem[]

export type Rules = Record<string, Rule>

/**
 *  Rule for validating a value exists in an enumerable list.
 *
 *  @param rule The validation rule.
 *  @param value The value of the field on the source object.
 *  @param source The source object being validated.
 *  @param errors An array of errors that this rule may add
 *  validation errors to.
 *  @param options The validation options.
 *  @param options.messages The validation messages.
 *  @param type Rule type
 */
export type ExecuteRule = (
    rule: InternalRuleItem,
    value: Value,
    source: Values,
    errors: string[],
    options: ValidateOption,
    type?: string
) => void

/**
 *  Performs validation for any type.
 *
 *  @param rule The validation rule.
 *  @param value The value of the field on the source object.
 *  @param callback The callback function.
 *  @param source The source object being validated.
 *  @param options The validation options.
 *  @param options.messages The validation messages.
 */
export type ExecuteValidator = (
    rule: InternalRuleItem,
    value: Value,
    callback: (error?: string[]) => void,
    source: Values,
    options: ValidateOption
) => void

// >>>>> Message
type ValidateMessage<T extends any[] = unknown[]> = string | ((...args: T) => string)
type FullField = string | undefined
type EnumString = string | undefined
type Pattern = string | RegExp | undefined
type Range = number | undefined
type Type = string | undefined

export interface ValidateMessages {
    default?: ValidateMessage
    required?: ValidateMessage<[FullField]>
    enum?: ValidateMessage<[FullField, EnumString]>
    whitespace?: ValidateMessage<[FullField]>
    date?: {
        format?: ValidateMessage
        parse?: ValidateMessage
        invalid?: ValidateMessage
    }
    types?: {
        string?: ValidateMessage<[FullField, Type]>
        method?: ValidateMessage<[FullField, Type]>
        array?: ValidateMessage<[FullField, Type]>
        object?: ValidateMessage<[FullField, Type]>
        number?: ValidateMessage<[FullField, Type]>
        date?: ValidateMessage<[FullField, Type]>
        boolean?: ValidateMessage<[FullField, Type]>
        integer?: ValidateMessage<[FullField, Type]>
        float?: ValidateMessage<[FullField, Type]>
        regexp?: ValidateMessage<[FullField, Type]>
        email?: ValidateMessage<[FullField, Type]>
        url?: ValidateMessage<[FullField, Type]>
        hex?: ValidateMessage<[FullField, Type]>
    }
    string?: {
        len?: ValidateMessage<[FullField, Range]>
        min?: ValidateMessage<[FullField, Range]>
        max?: ValidateMessage<[FullField, Range]>
        range?: ValidateMessage<[FullField, Range, Range]>
    }
    number?: {
        len?: ValidateMessage<[FullField, Range]>
        min?: ValidateMessage<[FullField, Range]>
        max?: ValidateMessage<[FullField, Range]>
        range?: ValidateMessage<[FullField, Range, Range]>
    }
    array?: {
        len?: ValidateMessage<[FullField, Range]>
        min?: ValidateMessage<[FullField, Range]>
        max?: ValidateMessage<[FullField, Range]>
        range?: ValidateMessage<[FullField, Range, Range]>
    }
    pattern?: {
        mismatch?: ValidateMessage<[FullField, Value, Pattern]>
    }
}

export interface InternalValidateMessages extends ValidateMessages {
    clone: () => InternalValidateMessages
}

// >>>>> Values
export type Value = any
export type Values = Record<string, Value>

// >>>>> Validate
export interface ValidateError {
    message?: string
    fieldValue?: Value
    field?: string
}

export type ValidateFieldsError = Record<string, ValidateError[]>

export type ValidateCallback = (errors: ValidateError[] | null, fields: ValidateFieldsError | Values) => void

export interface RuleValuePackage {
    rule: InternalRuleItem
    value: Value
    source: Values
    field: string
}

export interface InternalRuleItem extends Omit<RuleItem, 'validator'> {
    field?: string
    fullField?: string
    fullFields?: string[]
    validator?: RuleItem['validator'] | ExecuteValidator
}

自己封装的表单组件

  • components/chooseForm/index.vue
vue

<template>
    <!--改变后不需要立即验证-->
    <el-form v-bind="$attrs" :model="model" :rules="rules" :validate-on-rule-change="false">
        <el-form-item :prop="item.prop" v-for="(item, index) in options" :key="index" :label="item.label">
            <component :is="`el-${item.type}`" v-bind="item.attrs" v-model="model[item.prop!]"></component>
        </el-form-item>
    </el-form>
</template>

<script setup lang="ts">
    import { type PropType, ref, onMounted } from 'vue'
    import { type FormOptions } from '@/interfaces/chooseForm/type'
    let props = defineProps({
        options: {
            type: Array as PropType<FormOptions[]>,
            required: true
        }
    })
    // 最后的结果应该就是 {username: '', password: ''}
    // rules: { username: [{ required: true, message: '请输入用户名', trigger: 'blur' }] }
    let model = ref<any>({})
    let rules = ref<any>({})
    // 赋值数据属性和验证规则
    let setRulesAndValue = () => {
        let m: any = {}
        let r: any = {}
        props.options.map((item: FormOptions) => {
            m[item.prop!] = item.value
            r[item.prop!] = item.rules
        })
        model.value = JSON.parse(JSON.stringify(m))
        rules.value = JSON.parse(JSON.stringify(r))
        console.log(model.value)
        console.log(rules.value)
    }
    onMounted(() => {
        setRulesAndValue()
    })
</script>

<style scoped></style>

父元素调用

vue
<template>
    <div>
        <YJ-choose-form :options="options" label-width="120px"></YJ-choose-form>
    </div>
</template>

<script setup lang="ts">
    import { type FormOptions } from '@/interfaces/chooseForm/type'

    let options: FormOptions[] = [
        {
            type: 'input',
            value: '',
            label: '用户名',
            prop: 'username',
            attrs: {
                clearable: true
            }
        },
        {
            type: 'input',
            value: '',
            label: '密码',
            prop: 'password',
            rules: [
                { required: true, message: '请输入密码', trigger: 'blur' },
                { min: 6, max: 15, message: '密码在6-15位之间', trigger: 'blur' }
            ],
            attrs: {
                showPassword: true,
                clearable: true
            }
        }
    ]
</script>

<style scoped></style>