大家在做后盾管理系统的时候,写的最多的可能就是表格页面了,个别分三局部:搜寻功能区、表格内容区和分页器区。个别这些性能都是应用第三方组件库实现,比如说element-ui,或者vuetify。这两个组件库都各有各的长处,但就table组件来说,我还是比拟喜爱vuetify的实现,不必手写一个个column,只有传入headers的配置数组就行,甚至分页器都内置在了table组件里,用起来非常不便。有趣味能够看看:vuetify data table。

下面是一个经典的用element-ui开发的table页面,而且理论工作中如果每个table页面都写一遍,反复代码太多了,所以无妨写一个table模板组件,缩小反复代码。我的思路是这样的:

  1. 搜寻功能区:

    提供searchBar插槽,能够自定义搜寻输入框,搜寻、重置按钮必有,新增按钮通过props管制显隐。这里对应的代码如下:

    genSearchBar() {    if (this.noSearchBar || !this.$scopedSlots.searchBar) return '';    return (        <el-form class="seatch-form" inline={true} label-width="100">            {this.$scopedSlots.searchBar()}            <el-button                class="filter-item"                icon="el-icon-search"                type="primary"                onClick={this.handleSearchBtnClick}            >                查问            </el-button>            <el-button                class="filter-item"                icon="el-icon-refresh"                onClick={this.handleResetBtnClick}            >                重置            </el-button>            <el-button                class="filter-item"                icon="el-icon-plus"                type="primary"                v-show={this.showAddBtn}                onClick={this.handleAddBtnClick}            >                新增            </el-button>        </el-form>    );}
  2. 表格内容区:

    通过传入headers主动生成columns,参数如下:

    {    label: '性别',    prop: 'sex',    width: '180',    filter: 'sexFilter'}

    可对应如下代码:

    <el-table-column                 prop="sex"                 label="性别"                 width="180">    <template slot-scope="scope">{{scope.row.sex | sexFilter}}</template></el-table-column>

    留神,只反对全局filter

    如果你想自定义column,也提供tableColumn插槽,反对自定义column,能够如下配置:

    {    prop: 'action'}
    <el-table-column                 prop="action"                 label="操作"                 width="180">    <template slot-scope="scope">        <el-button>编辑</el-button>        <el-button>删除</el-button>    </template></el-table-column>

    这样,就会按传入的prop匹配对应的column,非常不便。

    实现代码如下:

    genTableSlot(h) {    let customeColumns = this.$scopedSlots.tableColumn        ? this.$scopedSlots.tableColumn()        : [];    return this.headers.map((item) => {        // 依据item.prop判断是否应用传入的插槽内容        let foundItem = customeColumns.find(            (ele) =>                ele.componentOptions &&                ele.componentOptions.propsData.prop === item.prop        );        return foundItem            ? foundItem            : h('el-table-column', {                  props: {                      ...item,                  },                  scopedSlots: {                      default: (props) => {                          // 依据传入的全局filter解决column数据                          let filter = this.$options.filters[                              item.filter                          ];                          let itemValue = props.row[item.prop];                          return h(                              'span',                              filter ? filter(itemValue) : itemValue                          );                      },                  },              });    });}
    genTable(h) {    return h(        'el-table',        {            ref: 'tableRef',            props: {                ...this.$attrs,                data: this.data,            },            on: {                'selection-change': (val) => {                    this.$emit('selection-change', val);                },            },        },        [...this.genTableSlot(h)]    );}
  3. 分页器区:

    如无非凡需要,分页器性能统一,所以间接内置。

    实现代码如下:

    genPagination() {    return (        <div class="pagination-wrap">            <el-pagination                layout="total,prev,pager,next,jumper"                current-page={this.current}                page-size={this.pageSize}                total={this.total}                {...{                    on: { 'current-change': this.handleCurrentChange },                }}            ></el-pagination>        </div>    );}

    最初附残缺代码和demo:

    <script>export default {    name: 'TableTemplate',    props: {        data: {            type: Array,            default: () => [],            required: true,        },        headers: {            type: Array,            default: () => [],            required: true,        },        current: {            type: Number,            default: 1,        },        pageSize: {            type: Number,            default: 10,        },        total: {            type: Number,            default: 0,        },        noSearchBar: Boolean,        showAddBtn: Boolean,    },    mounted() {        this.$nextTick(() => {            this.$emit('search');        });    },    methods: {        genSearchBar() {            if (this.noSearchBar || !this.$scopedSlots.searchBar) return '';            return (                <el-form class="seatch-form" inline={true} label-width="100">                    {this.$scopedSlots.searchBar()}                    <el-button                        class="filter-item"                        icon="el-icon-search"                        type="primary"                        onClick={this.handleSearchBtnClick}                    >                        查问                    </el-button>                    <el-button                        class="filter-item"                        icon="el-icon-refresh"                        onClick={this.handleResetBtnClick}                    >                        重置                    </el-button>                    <el-button                        class="filter-item"                        icon="el-icon-plus"                        type="primary"                        v-show={this.showAddBtn}                        onClick={this.handleAddBtnClick}                    >                        新增                    </el-button>                </el-form>            );        },        genTableSlot(h) {            let customeColumns = this.$scopedSlots.tableColumn                ? this.$scopedSlots.tableColumn()                : [];            return this.headers.map((item) => {                // 依据item.prop判断是否应用传入的插槽内容                let foundItem = customeColumns.find(                    (ele) =>                        ele.componentOptions &&                        ele.componentOptions.propsData.prop === item.prop                );                return foundItem                    ? foundItem                    : h('el-table-column', {                          props: {                              ...item,                          },                          scopedSlots: {                              default: (props) => {                                  let filter = this.$options.filters[                                      item.filter                                  ];                                  let itemValue = props.row[item.prop];                                  return h(                                      'span',                                      filter ? filter(itemValue) : itemValue                                  );                              },                          },                      });            });        },        genTable(h) {            return h(                'el-table',                {                    ref: 'tableRef',                    props: {                        ...this.$attrs,                        data: this.data,                    },                    on: {                        'selection-change': (val) => {                            this.$emit('selection-change', val);                        },                    },                },                [...this.genTableSlot(h)]            );        },        genPagination() {            return (                <div class="pagination-wrap">                    <el-pagination                        layout="total,prev,pager,next,jumper"                        current-page={this.current}                        page-size={this.pageSize}                        total={this.total}                        {...{                            on: { 'current-change': this.handleCurrentChange },                        }}                    ></el-pagination>                </div>            );        },        resetPagination() {            this.$emit('update:current', 1);        },        handleCurrentChange(val) {            this.$emit('update:current', val);            this.$emit('search');        },        handleSearchBtnClick() {            this.$emit('search');        },        handleResetBtnClick() {            this.resetPagination();            this.$emit('reset');        },        handleAddBtnClick() {            this.$emit('add');        },        getTableRef() {            return this.$refs.tableRef;        },    },    render(h) {        return (            <div>                {this.genSearchBar()}                {this.genTable(h)}                {this.genPagination()}            </div>        );    },};</script><style scoped>.seatch-form {    text-align: left;}.pagination-wrap {    margin-top: 20px;    text-align: right;}</style>

    Demo:

    <template>    <div>        <table-template            border            :headers="headers"            :data="tableData"            :current.sync="current"            :total="total"            ref="tableTemplate"            showAddBtn            @search="handleSearch"            @reset="handleReset"            @add="handleAdd"            @selection-change="handleSelectionChange"        >            <template #searchBar>                <el-form-item label="姓名:" prop="title">                    <el-input class="filter-item" v-model="searchForm.title" ></el-input>                </el-form-item>            </template>            <template #tableColumn>                <el-table-column                    prop="selection"                    type="selection"                    width="55"                ></el-table-column>                <el-table-column prop="test" label="姓名" width="180">                    <template slot-scope="scope">                        <el-popover trigger="hover" placement="top">                            <p>姓名:{{ scope.row.name }}</p>                            <p>住址:{{ scope.row.address }}</p>                            <div slot="reference" class="name-wrapper">                                <el-tag size="medium">{{scope.row.name}}</el-tag>                            </div>                        </el-popover>                    </template>                </el-table-column>            </template>        </table-template>    </div></template><script>import TableTemplate from './TableTemplate';export default {    name: 'Demo',    components: {        TableTemplate,    },    data() {        return {            current: 1,            total: 100,            headers: [                {                    prop: 'selection',                },                {                    label: '姓名',                    prop: 'name',                    width: '100',                },                {                    label: '年龄',                    prop: 'year',                },                {                    label: '性别',                    prop: 'sex',                    width: 'sexFilter',                },                {                    prop: 'test',                },            ],            tableData: [                {                    name: 'curry',                    year: 18,                    sex: 'female',                    address: '天安门',                },            ],            searchForm: {                title: '',            },        };    },    methods: {        handleSearch() {            console.log(this.current);        },        handleReset() {            this.searchForm = {                title: '',            };        },        handleAdd() {            console.log('增加');        },        handleSelectionChange(val) {            console.log(val);        },        getTableRef() {            console.log(this.$refs.tableTemplate.getTableRef());        },    },};</script>