持续创作,加速成长!这是我参与「掘金日新计划 · 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>