一、组件化封装
1.首先创建一个form文件夹,将搜索框组件的内容全部写在这个里面,然后再在需要的页面直接引入相应的组件即可
2.首先先在goods.vue文件里面写对应的文本数组formItems,将所定义的类型IFormItem引用进去,这个里面写的字段都是对应goods.vue文件里面的文本数组formItems里面所拥有的字段
const formItems: IFormItem[] = [
{
field: "id",
type: "input",
label: "id",
placeholder: "请输入账号"
},
{
field: "realname",
type: "input",
label: "姓名",
placeholder: "请输入姓名"
},
{
field: "cellphone",
type: "input",
label: "电话号码",
placeholder: "请输入电话号码"
}
]
3.然后在form.vue文件里面去判断获取到的prop的数据,也就是父组件goods.vue传给子组件form.vue的formItems,根据里面写的type的类型去进行组件的匹配
form.vue
form.vue
<el-form label-width="100px">
<el-row>
<!-- :key="item.label"动态匹配-->
<template v-for="item in formItems" :key="item.label">
<el-col :span="8">
<!-- 动态匹配label值 -->
<el-form-item :label="item.label">
<!-- 动态判断文本框类型,从而去使用对应的element组件 -->
<template
v-if="item.type === 'input' || item.type === 'password'"
>
<!-- type是文本框 -->
<el-input
:placeholder="item.placeholder"
:show-password="item.type === 'password'"
/>
</template>
<template v-else-if="item.type === 'select'">
<!-- type是下拉框 -->
<el-select :placeholder="item.placeholder" style="width: 100%">
<el-option
v-for="option in item.options"
:key="option.value"
:value="option.value"
>{{ option.title }}</el-option
>
</el-select>
</template>
</el-form-item>
</el-col>
</template>
</el-row>
</el-form>
4.可以选择在form.vue里面接收相应的样式itemStyle和labelWidth,或者直接在form.vue里面给他们默认值,然后绑定到上面对应的组件上,就能进行响应
5.一开始可以将所有的itemLayout、formItems、lableWidth和colLayout全部由父组件传给子组件,但是这样的话hy-form组件里面要传的东西就太多了,所以可以考虑把上面这些数据全部放在一个新变量里面,然后给这个新变量定义一个类型,这个类型里面就包括了这些变量值
6.虽然这样一定程度上简化了代码,但是goods.vue文件里面的代码还是太多了,为了更加简便,我们可以把formConfig单独抽离到一个文件里面,然后再在goods.vue里面去引用
search.config.ts
import { IForm } from '@/base-ui/form'
export const searchformConfig: IForm = {
labelWidth: '120px',
itemStyle: {
padding: '10px 40px'
},
colLayout: {
span: 8
},
formItems: [
{
field: "id",
type: "input",
label: "id",
placeholder: "请输入商品id"
},
{
field: "realname",
type: "input",
label: "商品名称",
placeholder: "请输入商品名称"
},
{
field: "cellphone",
type: "input",
label: "商品编号",
placeholder: "请输入商品编号"
}
]
}
goods.vue
<template>
<div class="main">
<div class="search">
<!-- <hy-form
:formItems="formItems"
:collayout="collayout"
:lableWidth="lableWith"
:itemLayout="itemLayout"
></hy-form> -->
<!-- <hy-form v-bind="formConfig"></hy-form> -->
<hy-form v-bind="searchformConfig"></hy-form>
</div>
</div>
</template>
<script lang="ts">
import { defineComponent } from "vue"
// import HyForm, { IFormItem, IForm } from "@/base-ui/form"
import HyForm from "@/base-ui/form"
import { searchformConfig } from "./config/search.config"
export default defineComponent({
name: "goods",
components: {
HyForm
},
setup() {
// const formItems: IFormItem[] = [
// {
// field: "id",
// type: "input",
// label: "id",
// placeholder: "请输入账号"
// },
// {
// field: "realname",
// type: "input",
// label: "姓名",
// placeholder: "请输入姓名"
// },
// {
// field: "cellphone",
// type: "input",
// label: "电话号码",
// placeholder: "请输入电话号码"
// }
// ]
// const labelWidth = "120px"
// const itemStyle = { padding: "10px 40px" }
// const collayout = { span: 8 }
// 简化后的变量
// const formConfig: IForm = {
// formItems: [
// {
// field: "id",
// type: "input",
// label: "id",
// placeholder: "请输入商品id"
// },
// {
// field: "realname",
// type: "input",
// label: "商品名称",
// placeholder: "请输入商品名称"
// },
// {
// field: "cellphone",
// type: "input",
// label: "商品编号",
// placeholder: "请输入商品编号"
// }
// ],
// labelWidth: "120px",
// itemStyle: { padding: "10px 40px" },
// collayout: { span: 8 }
// }
return {
// formItems,
// labelWidth,
// itemStyle,
// collayout
// formConfig
searchformConfig
}
}
})
</script>
二、解决文本框的双向绑定问题
1.可以使用父组件向子组件传参数的办法,首先在一开始封装的formItem里面定义一个字段field,然后再在goods组件上去双向绑定
2.然后在form.vue文件里面,给文本框或下拉框这些动态匹配绑定V-model
goods.vue
search.config.ts
form.vue
3.但是这样做的话会有一个弊端,就是直接修改从父组件传过来的数据,一定程度上不是很严谨,所以可以选择将从父组件获取到的数据变量赋值拷贝给另一个变量,然后在form.vue里面去动态监听这个变量是否发生变化,从而去发送监听事件
goods.vue
form.vue
三、给搜索框动态绑定一个title标题
1.可以先给form.vue组件一个自定义的插槽
2.然后在之前的formItem数组中再多定义一个字段title
3.最后在引用form.vue组件对应的地方加入一个插槽template,然后动态匹配对应的title字段
四、增加搜索和重置按钮
1、跟前面增加title标题一样的道理,先给form.vue增加一个底部插槽
2.在goods.vue组件对应的位置加一个template,里面对应相应的按钮
五、组件的封装简化
1.为了使搜索组件能够多次使用,不用重复写代码,可以创建一个page-search文件夹,专门编写搜索组件的代码,这样方便之后直接引用这个组件到相应的页面即可
简化后的goods.vue
<template>
<div class="goods">
<page-search :searchformConfig="searchformConfig"></page-search>
</div>
</template>
<script lang="ts">
import { defineComponent, ref } from "vue"
import { searchformConfig } from "./config/search.config"
import PageSearch from "@/components/page-search"
export default defineComponent({
name: "goods",
components: {
PageSearch
},
setup() {
return {
searchformConfig
}
}
})
</script>
<style scoped></style>
简化后的page-search.vue
<template>
<div class="page-search">
<div class="search">
<!-- <hy-form v-bind="formConfig"></hy-form> -->
<!-- <hy-form v-bind="searchformConfig" :formData="formData"></hy-form> -->
<hy-form v-bind="searchformConfig" v-model="formData">
<!-- 插槽对应form.vue组件里面定义的一个插槽slot,这里的title可以自定义,从serch.config.ts传过来 -->
<!-- 头部标题插槽 -->
<template #header>
<h1>{{ searchformConfig.title }}</h1>
</template>
<!-- 底部按钮插槽 -->
<template #footer>
<div class="handle-btns">
<el-button @click="handleResetClick">
<el-icon> <Refresh /> </el-icon>重置
</el-button>
<el-button type="primary" @click="handleQueryClick">
<el-icon> <Search /> </el-icon>搜索
</el-button>
<slot></slot>
</div>
</template>
</hy-form>
</div>
</div>
</template>
<script lang="ts">
import { defineComponent, ref } from "vue"
import HyForm from "@/base-ui/form"
// import { searchformConfig } from './config/search.config'
import { Search, Refresh } from "@element-plus/icons"
export default defineComponent({
props: {
searchformConfig: {
type: Object,
required: true
}
},
components: {
HyForm,
Search,
Refresh
},
setup(props, { emit }) {
const formData = ref({
id: "",
realname: "",
cellphone: ""
})
return {
formData
}
}
})
</script>
<style scoped>
.handle-btns {
text-align: right;
padding: 0 50px 20px 0;
}
</style>
form.vue文件跟之前的一样
<template>
<div class="hy-form">
<div class="header">
<slot name="header"></slot>
</div>
<el-form :label-width="labelWidth">
<el-row>
<!-- :key="item.label"动态匹配-->
<template v-for="item in formItems" :key="item.label">
<el-col v-bind="collayout">
<!-- 动态匹配label值 -->
<el-form-item :label="item.label" :style="itemStyle">
<!-- 动态判断文本框类型,从而去使用对应的element组件 -->
<template
v-if="item.type === 'input' || item.type === 'password'"
>
<!-- type是文本框 -->
<el-input
:placeholder="item.placeholder"
:show-password="item.type === 'password'"
v-model="formData[`${item.field}`]"
/>
</template>
<template v-else-if="item.type === 'select'">
<!-- type是下拉框 -->
<el-select
:placeholder="item.placeholder"
style="width: 100%"
v-model="formData[`${item.field}`]"
>
<el-option
v-for="option in item.options"
:key="option.value"
:value="option.value"
>{{ option.title }}</el-option
>
</el-select>
</template>
</el-form-item>
</el-col>
</template>
</el-row>
</el-form>
<div class="footer">
<slot name="footer"></slot>
</div>
</div>
</template>
<script lang="ts">
import { defineComponent, PropType, ref, watch } from "vue"
import { IFormItem } from "../types"
export default defineComponent({
props: {
// 改进之后的formData
modelValue: {
type: Object,
required: true
},
// 这是子组件接收父组件传过来的数组数据
formItems: {
// 默认传过来的数据是IFormItem类型
type: Array as PropType<IFormItem[]>,
// 如果默认数据是数组或者对象时需要用箭头函数表示
default: () => []
},
labelWidth: {
type: String,
default: "100px"
},
title: {
type: String,
default: ""
},
itemStyle: {
type: Object,
default: () => ({ padding: "10px 40px" })
},
collayout: {
type: Object,
default: () => ({
xl: 6, //>1920 显示4个
lg: 8,
md: 12,
sm: 24,
xs: 24
})
}
},
emits: ["update:modelValue"],
setup(props, { emit }) {
// 这里是将获取到的props.modelValue重新拷贝一份放在formData
const formData = ref({ ...props.modelValue })
// 深度监听,当formData发生变化,就发送事件给父组件
watch(
formData,
(newValue: any) => {
console.log(newValue)
emit("update:modelValue", newValue)
},
{
deep: true
}
)
return {
formData
}
}
})
</script>
<style scoped lang="less">
.hy-form {
padding-top: 22px;
}
</style>