从零搭建Vue3.0组件库之表单组件

一.表单组件

1.Form组件

表单组件用于接收表单数据和表单验证规则,没有任何实际功能

<template>
    <form class="z-form">
        <slot></slot>
    </form>
</template>
<script>
import { defineComponent } from 'vue'
export default defineComponent({
    name:'ZForm',
    props:{
        model:Object, // 表单数据
        rules:Object // 校验规则
    },
    emits:['validate'] // 表单验证
})
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

2.FormItem组件

用于实现表单验证逻辑

<template>
    <div class="z-form-item">
        <label v-if="label">{{ label }}</label>
        <slot></slot>
        <div class="z-form-item__error">
            显示错误信息
        </div>
    </div>
</template>
<script>
import { defineComponent } from 'vue'
export default defineComponent({
    name:'ZFormItem',
    props: {
       label: String,
       prop: String,
    }
})
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

二.数据交互实现

父组件提供数据给子组件,并收集formItem组件实例

const formMitt = mitt(); // 用于子组件触发事件
const zForm = reactive({
  // 用于提供给子组件
  ...toRefs(props),
  formMitt,
});
provide("zForm", zForm); // 暴露数据

const fields = []; // 用于收集子组件实例
formMitt.on("z.form.add", (field) => {
  fields.push(field);
});
1
2
3
4
5
6
7
8
9
10
11
12

子组件将自己的validate方法传递给父组件。并监听输入框事件

const formItemMitt = mitt();
const zFormItem = reactive({
    ...toRefs(props),
    formItemMitt,
    validate, // 提供一个最核心的方法validate,好让父级可以调用到此方法
});
provide("zFormItem", zFormItem); // 提供给其他组件使用 checkbox,input

const zForm = inject("zForm", {} as ZFormContext);
const getRules = (): any[] => {
    // 获取规则 ,并且绑定事件
    return zForm.rules[props.prop] as any[];
};
const addValidateEvents = () => {
    // 绑定事件
    const rules: any[] = getRules();
    if (rules && rules.length > 0) {
        formItemMitt.on("z.form.blur", onFieldBlur);
        formItemMitt.on("z.form.change", onFieldChange);
    }
};
onMounted(() => {
    zForm.formMitt?.emit("z.form.add", zFormItem); // 将自己传递给父组件
    addValidateEvents(); // 添加验证事件,子组件会触发这些事件
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

三.监控输入框中的内容

得到输入框中的内容进行校验

const validateState = ref("");
const validateMessage = ref("");
const getFilteredRule = (trigger) => {
    // 根据触发方式获得对应的规则
    const rules: any[] = getRules();
    return rules
        .filter((rule) => {
        if (!rule.trigger || trigger == "") return true;
        if (Array.isArray(rule.trigger)) {
            return rule.trigger.indexOf(trigger) > -1; // 说明有
        } else {
            return rule.trigger === trigger;
        }
    }).map((rule) => ({ ...rule }));
};
const validate = (trigger, callback?) => {
    const rules = getFilteredRule(trigger); // 获取过滤后的规则
    if (!rules || rules.length == 0) return;
    const descriptor = {};
    rules.forEach((rule) => {
        delete rule.trigger;
    });
    descriptor[props.prop] = rules;
    // 校验器
    const validator = new AsyncValidator(descriptor);
    // 数据值
    const model = {};
    model[props.prop] = zForm.model[props.prop];
    // 使用async-validate校验内容
    validator.validate(model, {}, (errors: any, invalidFields: any) => {
        validateState.value = !errors ? "success" : "error";
        validateMessage.value = errors ? errors[0].message : "";
        callback?.(validateMessage.value, invalidFields);
    });
};
// 提供给儿子用
const onFieldBlur = () => {
    validate("blur");
};
const onFieldChange = () => {
    validate("change");
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42

四.暴露校验方法

const validate = (callback?) => {
    // 这里需要触发所有子组件的校验方法
    let valid = true;
    let invalidFields = {};
    let count = 0;
    let promise: Promise<boolean> | undefined;
    if (typeof callback !== "function") {
        promise = new Promise((resolve, reject) => {
            callback = function (valid, invalidFields) {
                if (valid) resolve(true);
                reject(invalidFields);
            };
        });
    }
    for (const field of fields) {
        field.validate("", (message, field) => {
            if (message) {
                valid = false;
            }
            invalidFields = { ...invalidFields, ...field };
            console.log(count);
            if (++count === fields.length) {
                callback(valid, invalidFields);
            }
        });
    }
    return promise;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28