在开发后台管理系统中,有不少页面是头部表单搜素,中间是表格展示,底部是分页。从页面中可抽象出,表格的内容根据表单的变化或分页的变化而变化。既然如此,设计该业务组件时,表格部分可以通过vue中插槽插入组件内。搜素表单一般是输入表单、选择表单、时间选择器等,这些可以通过传入自定义配置自动生成。底部分页一般是不会有什么变化的。如此一来,我们便可以通过搜素表单数据的变化或底部分页的数据变化,通知父组件,父组件发送请求,获取数据重新渲染表格。那我我们代码上要怎么实现这个子组件呢?
本文中的组件依赖于elementUI
实现组件
<template> <div class="search-form-pagination"> <div v-if="componentImstances.length > 0" class="search-form" > <component :is="item.imstance" // 加载表单组件 v-for="(item, i) in componentImstances" :key="i" :config="item" @change="change" /> <el-button type="primary" @click="onSearch" >{{ searchBtnText }}</el-button> </div> <div class="table"> <slot name="table"></slot> </div> <div class="pagination"> <el-pagination @size-change="handleSizeChange" @current-change="handleCurrentChange" :current-page="currentPage" :page-sizes="[10, 50, 100, 200]" :page-size="pageSize" layout="total, sizes, prev, pager, next,jumper" :total="total"> </el-pagination> </div> </div></template><script>import { deepClone as $deepClone } from '@/utils/deepClone';export default { name: 'SearchAndPagination', props: { config: { type: Array, default() { return []; } }, total: { // 总条目数 type: Number, required: true, default: 1 }, searchBtnText: { type: String, default: '查询' }, loadTrigger: { // 值为true时,挂载完触发onSearch通知父组件 type: Boolean, default: true }, }, data() { return { searchFormConfig: [], searchParam: {}, currentPage: 1, pageSize: 10, }; }, computed: { componentImstances() { const { searchFormConfig } = this; return searchFormConfig.map((item, i) => { try { item.imstance = resolve => require([`./components/${item.name}.vue`], resolve); item.index = i; } catch (e) { console.log(e); } return item; }); } }, watch: { config(newValue) { this.init(); }, }, created() { this.init(); }, mounted() { this.loadTrigger && this.onSearch(); }, methods: { init() { // 初始化 const { config } = this; // 深克隆父组件入的对象,再做处理,以免数据错乱 this.searchFormConfig = $deepClone(config); for (const item of config) { this.setSearchParam(item.key, item.value); } }, onSearch() { // 点击搜素按钮触发 const { pageSize, currentPage, searchParam } = this; this.$emit('onSearch', { page: currentPage, per_page: pageSize, ...searchParam }); }, searchChange() { // 通知父组件搜素表单值改变 const { pageSize, currentPage, searchParam } = this; this.$emit('change', { page: currentPage, per_page: pageSize, ...searchParam }); }, change(index, value) { // 表单组件改变时触发 const item = this.searchFormConfig[index]; item.value = value; this.setSearchParam(item.key, value); this.searchChange(); }, setSearchParam(key, value) { this.searchParam[key] = value || ''; }, handleSizeChange (pageSize) { // 分页组件分页条数改变时触发 this.pageSize = pageSize this.onSearch() }, handleCurrentChange (currentPage) { // 分页组件值改变时触发 this.currentPage = currentPage this.onSearch() } }};</script>
重写element组件
使用上述的动态组件,我们要重写一下element组件,统一其传参跟事件。
<template> <div class="gn-select"> <label v-if="config.label" class="label" >{{ config.label }}:</label> <el-select v-model="value" :clearable="config.clearable || true" :placeholder="config.placeholder || `请选择${config.label}`" > <el-option v-for="(item, i) in config.options" :key="i" :label="item.label" :value="item.value" > </el-option> </el-select> </div></template><script>export default { name: 'GnSelect', props: { config: { type: Object, required: true } }, data() { return { value: '' }; }, watch: { value(newValue) { this.$emit('change', this.config.index, newValue); } }, created() { this.init(); }, methods: { init() { this.value = this.config.value || ''; } }};</script>
如何使用该组件
<template> <search-and-pagination ref="searchForm" :config="searchFormConfig" :total="total" @onSearch="onSearch" @change="searchChange"> <template v-slot:table> <!--表格内容--> </template> </search-and-pagination></template><script>export default { components: { SearchAndPagination: () => import('@/components/SearchAndPagination') }, data () { return { searchFormConfig:[ { key: 'search_data', name: 'gn-input', label: '商品名称' }, { key: 'classify_name', name: 'gn-cascader', label: '商品分类', options: [] }, { key: 'status', name: 'gn-select', label: '商品状态', options: [{ value: '0', label: '上架' },{ value: '1', label: '下架' },{ value: '2', label: '删除' }] } ], total: 1 } }, methods: { searchChange (param) { // 搜索表单改变时触发 console.log(param) // 表单的值跟分页的值 }, onSearch (param) { // 点击搜索按钮时触发 console.log(param) // 表单的值跟分页的值 } }}</script>
完整代码:https://github.com/liangdiyuan/search-and-pagination