封装组件库-表单(3)
2023, Mar 07
实现表单的封装
基于Element-plus框架的Form表单组件进行封装。
实现的功能
- 实现配置型表单,通过json对象的方式自动生成表单
- 具备更加完备的功能,表单验证,动态删减表单,集成第三方插件(富文本编辑器)
- 用法更简单,扩展性强(配置项形式),可维护
- 能够进行弹框嵌套表单等多场景。
初始配置
- 注册路由组件form,配置为Container组件的子路由组件
- 采用路由懒加载,箭头函数异步引入组件。
- 注册通用组件from,为路由组件的子组件,以便组件间通信。
- 将通用组件form注册为全局组件
实现upload上传组件
- 接口类型定义:在type里添加upload,定义uploadAttrs将upload组件的原生基本类型API添加进来,实现原生的所有功能
export interface FormOptions {
type: 'upload',
//处理上传组件的属性和方法
uploadAttrs?: {
//上传地址
action: string,
//设置上传的请求头部
header?: object,
//设置上传的方法
methods?: 'post' | 'put' | 'patch',
//是否支持多选文件
multiple?: boolean,
//上传的额外参数
data?: any,
//上传的字段
name?: string,
//是否允许携带token
withCreentials?: boolean,
//是否显示上传文件列表
showFileList?: boolean,
//是否拖拽
drag?: boolean,
//接受上传的文件类型
accept?: string,
//缩略图
thumbailMode?: boolean,
//上传的文件列表
fileList?: any[],
//文件列表的类型
listType?: 'text' | 'picture' | 'picture-card',
//是否自动上传
autoUpload?: boolean,
//是否禁用
disable?: boolean,
//允许上传的最大数量
limit?: number
}
}
- 父组件配置表单数据:这里基于官网进行定义url。
let options: FormOptions[] =[
{
type: 'upload',
prop: 'pic',
label: '上传',
uploadAttrs:{
action: 'https://run.mocky.io/v3/9d059bf9-4660-45f2-925d-ce80ad6c4d15',
multiple: true,
limit: 3
},
rule: [
{
required: true,
message: '图片不能为空',
trigger: 'blur'
}
],
}
]
- 子组件:由于上传组件的API等逻辑较多,单独处理该组件。
<el-form-item :prop="item.prop" :label="item.label"
v-if="!item.children || !item.children.length">
<component
<!-- 渲染不为上传组件的组件 -->
v-if="item.type !== 'upload'"
v-bind="item.attrs"
:is="`el-${item.type}`"
v-model="model[item.prop]"
:placeholder="item.placeholder">
</component>
<el-upload
<!-- 渲染上传组件 -->
v-if="item.type === 'upload'"
v-bind="item.uploadAttrs"
<!-- 这里处理原生api,定义事件钩子 -->
:on-preview="onPreview"
:on-remove="onRemove"
:on-success="onSuccess"
:on-error="onError"
:on-progress="onProgress"
:on-change="onChange"
:on-exceed="onExceed"
:before-upload="beforeUpload"
:before-remove="beforeRemove"
:http-request ='httpRequest'>
<!-- 子组件设置插槽,用于放置按钮和提示模块 -->
<slot name="uploadArea"></slot>
<slot name="uploadTip"></slot>
</el-upload>
</el-form-item>
- 子组件处理原生钩子:这里原来定义了http-request,这个api是用来覆盖默认xhr行为,应该是父组件传递过来的,所以用props接收。
let emits = defineEmits(['on-preview','on-remove','on-success','on-error','on-progress',
'on-change','on-exceed','before-upload','before-remove'])
//上传组件的所有方法
let onPreview = (file: File)=>{
emits('on-preview',file)
}
let onRemove = (file: File,fileList: FileList)=>{
emits('on-remove',{file,fileList})
}
let onSuccess = (response: any,file: File,fileList: FileList)=>{
// 上传成功,给表单上传项赋值
//找表单项
let uploadItem = props.options.find(item=>item.type==='upload')!
model.value[uploadItem.prop!] = {response,file,fileList}
emits('on-success',{response,file,fileList})
}
let onError = (err: any,file: File,fileList: FileList)=>{
emits('on-error',{err,file,fileList})
}
let onProgress = (event: any,file: File,fileList: FileList)=>{
emits('on-progress',{event,file,fileList})
}
let onChange = ( file: File,fileList: FileList)=>{
emits('on-change',{file,fileList})
}
let onExceed = (files: File,fileList: FileList)=>{
emits('on-exceed',{files,fileList})
}
let beforeUpload = (file: File)=>{
emits('before-upload',file)
}
let beforeRemove = (file: File,fileList: FileList)=>{
emits('before-remove',{file,fileList})
}
- 父组件接收,完成原生api的功能
<my-form
ref="form"
label-width="100px"
:options="options"
@on-change="handleChange"
@before-upload="beforeUpload"
@on-preview="handlePreview"
@on-remove="handleRemove"
@before-remove="beforeRemove"
@on-exceed="handleExceed"
@on-success="handleSuccess">
</my-form>
const handleChange = (val: any) => {
console.log(val)
}
const beforeUpload = (val: any) => {
console.log(val);
}
const handleRemove = (val: any) => {
console.log(val.file, val.fileList)
}
const handlePreview = (uploadFile: any) => {
console.log(uploadFile)
}
const handleExceed = (val: any) => {
ElMessage.warning(
`The limit is 3, you selected ${val.files.length} files this time, add up to ${
val.files.length + val.fileList.length
} totally`
)
}
const handleSuccess = (val: any) => {
console.log(val);
}
const beforeRemove = (val: any) => {
return ElMessageBox.confirm(
`Cancel the transfert of ${val.file.name} ?`
).then(
() => true,
() => false
)
}
- 父组件插槽
<template #uploadArea>
<el-button type="primary" size="small">Click to upload</el-button>
</template>
<template #uploadTip>
<div style="color: #ccc;font-size:12px; margin-left:10px">
jpg/png files with a size less than 500KB.
</div>
</template>
radio单选框组件
{
type: 'radio-group',
value: '',
prop: 'gender',
label: '性别',
rule: [
{
required: true,
message: '性别不能为空',
trigger: 'blur'
}
],
children:[
{
type: 'radio',
value: 'male',
prop: 'male',
label: '男'
},
{
type: 'radio',
value: 'female',
prop: 'female',
label: '女'
},
{
type: 'radio',
value: 'not',
prop: 'baomi',
label: '保密'
}
]
}