基於element-ui封裝一個Table模板組件

你們在作後臺管理系統的時候,寫的最多的可能就是表格頁面了,通常分三部分:搜索功能區、表格內容區和分頁器區。通常這些功能都是使用第三方組件庫實現,好比說element-ui,或者vuetify。這兩個組件庫都各有各的優勢,但就table組件來講,我仍是比較喜歡vuetify的實現,不用手寫一個個column,只要傳入headers的配置數組就行,甚至分頁器都內置在了table組件裏,用起來十分方便。有興趣能夠看看:vuetify data tablejavascript

經典table組件

上面是一個經典的用element-ui開發的table頁面,並且實際工做中若是每一個table頁面都寫一遍,重複代碼太多了,因此不妨寫一個table模板組件,減小重複代碼。個人思路是這樣的:html

  1. 搜索功能區:vue

    提供searchBar插槽,能夠自定義搜索輸入框,搜索、重置按鈕必有,新增按鈕經過props控制顯隱。這裏對應的代碼以下:java

    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. 表格內容區:element-ui

    經過傳入headers自動生成columns,參數以下:數組

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

    可對應以下代碼:app

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

    注意,只支持全局filterui

    若是你想自定義column,也提供tableColumn插槽,支持自定義column,能夠以下配置:this

    {
        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,十分方便。spa

    實現代碼以下:

    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>
相關文章
相關標籤/搜索