教你写一个可以提升50%开发效率的组件

263 阅读2分钟
原文链接: mp.weixin.qq.com

在开发后台管理系统中,有不少页面是头部表单搜素,中间是表格展示,底部是分页。从页面中可抽象出,表格的内容根据表单的变化或分页的变化而变化。既然如此,设计该业务组件时,表格部分可以通过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