Vue3组件库之Form表单

472 阅读1分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第1天,点击查看活动详情

npm仓库地址zgy-ui

文档地址zgy-ui-docs

一入编程深似海,从此公司是我家,毫无疑问,表单在任何组件库里面都占据着重要位置 下面分享一下开发表单组件的代码 采用Vue3 setup ts

Form.vue

这里用到了一下插件 async-validator 据我所知 ElementUI表单也是采用了这个 到这去

主要接收四个参数

ref:DOM元素 model:表单对象 rules:校验规则 lableWidth:统一设置label宽度

validate 抛出校验方法用于父组件调用

<template>
  <div class="z-form">
    <slot></slot>
  </div>
</template>
<script lang="ts" setup name="Form">
import "./style/index.less";

import {
  withDefaults,
  defineProps,
  provide,
} from "vue";

import Schema from "async-validator";
import type { Rules } from "async-validator";

import EventBus from './../../store/eventBus'

interface FormProps {
  ref: string;
  model: Object;
  rules: Rules;
  labelWidth?: string;
}

const props = withDefaults(defineProps<FormProps>(), {});

provide("rules", props.rules);

const validate = (cb: (v: boolean, f: any) => void) => {
  const validator = new Schema(props.rules);
  validator.validate(props.model, (errors: any, fields: any) => {
    if (!errors) {
      EventBus.emitEvent('fields',[])
      cb(true, fields);
    } else {
      EventBus.emitEvent('fields',fields)
      cb(false, fields);
    }
  });
};

defineExpose({
  validate,
});

</script>

eventBus 用于实现消息订阅


class EventBus {
    private _eventList: any;
    static _instance: any;
    constructor() {
        this._eventList = {}
    }

    static Instance() {
        if (!EventBus._instance) {
            Object.defineProperty(EventBus, "_instance", {
                value: new EventBus()
            });
        }
        return EventBus._instance;
    }
    /**
     * 注册事件至调度中心
     * @param type 事件类型,特指具体事件名
     * @param fn 事件注册的回调
     */
    onEvent(type:any, fn:any) {
        if (!this.isKeyInObj(this._eventList, type)) {
            Object.defineProperty(this._eventList, type, {
                value: [],
                writable: true,
                enumerable: true,
                configurable: true
            })
        }
        this._eventList[type].push(fn)
    }
    /**
     * 触发调度中心的某个或者某些该事件类型下注册的函数
     * @param type 事件类型,特指具体事件名
     * @param data 发布者传递的参数
     */
    emitEvent(type:any, data:any) {
        if (this.isKeyInObj(this._eventList, type)) {
            for (let i = 0; i < this._eventList[type].length; i++) {
                this._eventList[type][i] && this._eventList[type][i](data)
            }
        }
    }
    offEvent(type:any, fn:any) {
        for (let i = 0; i < this._eventList[type].length; i++) {
            if (this._eventList[type][i] && this._eventList[type][i] === fn) {
                this._eventList[type][i] = null
            }
        }
    }
    /**
     * 检查对象是否包含该属性,除原型链
     * @param obj 被检查对象
     * @param key 被检查对象的属性
     */
    isKeyInObj(obj:any, key:any) {
        if (Object.hasOwnProperty.call(obj, key)) {
            return true
        }
        return false
    }
}

export default EventBus.Instance()

FormItem.vue

<template>
    <div class="z-form-item" v-bind="$attrs">
        <div class="z-form-item-label" :class="isRequired" :style="{width:props.labelWidth}">
            <span>{{ props.label }}</span>
        </div>
        <div class="z-form-item-slot" :style="{border:props.prop && msg ? '0.5px solid #f56c6c' : '',borderRadius:'4px'}">
            <slot></slot>
            <template v-if="props.prop && msg">
                <div class="z-form-item-error">{{ msg }}</div>
            </template>
        </div>
    </div>
</template>
<script lang="ts" setup name="FormItem">
import './style/index.less'

import { withDefaults, defineProps, inject, computed, onMounted,ref } from "vue";

import EventBus from './../../store/eventBus'

interface FormItemProps {
    label?: string
    labelWidth?: string
    prop?: any
    [key: string]: any
}

const props = withDefaults(defineProps<FormItemProps>(), {
    label: '',
    labelWidth: 'auto',
    prop: 'none'
})

const rules = inject("rules") as any;

onMounted(()=>{
    EventBus.onEvent('fields',changeFileds)
})

const msg = ref<string>("")

const changeFileds = (fields:any)=>{
    let f = fields[props.prop]
    if(f){
        msg.value = f[0].message;
    }else{
        msg.value = "";
    }
}

const isRequired = computed(() => {
    let obj = rules[props.prop]
    if (obj && obj[0]) {
        if (obj[0] && obj[0].required) {
            return 'required'
        } else {
            return 'n-required'
        }
    } else {
        return 'n-required'
    }
})

</script>