vue+elementui 二次封装el-table

最近做了一个后台管理系统,用的是vue+elementui,里面包含了很多表格 ,为了节省开发时间,将el-table进行了二次封装,特此记录下,有需要的也可以参考

表格封装


<template>

    <div class="base-table-container">
        <!-- 表格过滤 -->
        <div class="filter-box">
            <table-filter v-if="filterList" ref="tableFilter" :filterList="filterList" @filterChange="filterChange"></table-filter>
        </div>
        <!-- 添加插槽 -->
        <slot name="table-top" />
        <!-- 表格 -->
        <div class="table-wrapper">
            <el-table v-on="$listeners" v-bind="$attrs" :data="tableList||list" v-loading="loading">
                <template v-for="(item, i) in columns">
                    <!-- 根据角色权限判断当前列是否显示 -->
                    <template v-if="item.condition===undefined||item.condition(item)">
                        <!-- 操作按钮 直接在生成操作列时 获取item.btns-->
                        <el-table-column :key="i" v-bind="item.attrs" v-if="item.btns">
                            <template slot-scope="{row}">
                                <template v-for="(btn,key) in item.btns(row)">
                                    <el-button class="table-button" type="text" v-bind="btn.attrs" v-if="btn.condition===undefined||btn.condition(row)" v-on="btn.listeners" :key="key">{{btn.name}}
                                    </el-button>
                                </template>
                            </template>
                        </el-table-column>
                        <!-- 渲染render -->
                        <el-table-column :key="i" v-bind="item.attrs" v-else-if="item.render">
                            <template slot-scope="{row}">
                                <expand-dom :column="item.attrs" :row="row" :render="item.render" :index="i"></expand-dom>
                            </template>
                        </el-table-column>
                        <!-- 状态数据 -->
                        <el-table-column :key="i" v-bind="item.attrs" v-else-if="item.statusList">
                            <template slot-scope="{row}">
                                <template v-for="(subItem,subI) in item.statusList">
                                    <span v-if="subItem.value==row[item.attrs.prop]" :key="subI" :style="{...subItem.style}">{{subItem.label}}</span>
                                </template>
                            </template>
                        </el-table-column>
                        <!-- 基础数据 -->
                        <el-table-column :key="i" v-bind="item.attrs" v-else></el-table-column>
                    </template>

                </template>
            </el-table>
            <!-- 分页 -->
            <table-pagination v-if="pagination&&page&&page.total" style="margin-top:20px;" @size-change="handleSizeChange" @current-change="handleCurrentChange" v-bind="{...page,}"></table-pagination>
        </div>

    </div>
</template>
<script>
import tableFilter from './tableFilter'
import tablePagination from './tablePagination'
export default {
    name: "BaseTable",
    components: {
        expandDom: {
            functional: true,
            props: {
                row: Object, render: Function, index: Number,
                column: { type: Object, default: null }
            },
            render: (h, ctx) => {
                const params = {
                    row: ctx.props.row,
                    index: ctx.props.index
                }
                if (ctx.props.column) params.column = ctx.props.column
                return ctx.props.render(h, params)
            }
        },
        tableFilter,
        tablePagination
    },
    data() {
        return {
            /* 分页参数 */
            page: {
                total: undefined,
                currentPage: 1,
                pageSize: 10
            },
            // 表格加载状态
            loading: false,
            // 筛选参数
            form: {},
            // 列表数据
            list: [],
        }
    },
    props: {
        /* 表格头 */
        columns: {
            type: Array,
        },
        // 表格数据 若有接收该数据 则用该数据
        tableList: {
            type: Array
        },
        // filter配置项列表(配置过滤条件)
        filterList: {
            type: Array
        },
        // 获取列表的方法
        http: {
            type: Function
        },
        // 获取列表时的固定参数
        defaultParme: {
            type: Object,
            default: null
        },
        // 后台列表数据版本是否为老版本(与老版本后台返回的列表数据格式做兼容)
        isOld: {
            type: Boolean,
            default: false
        },
        // 是否展示分页组件
        pagination: {
            type: Boolean,
            default: true
        }
    },
    created() {
        this.getList()
    },
    methods: {
        //每页条数改变*
        handleSizeChange(val) {
            this.page.currentPage = 1
            this.page.pageSize = val;
            this.getList();
        },
        //当前页改变*
        handleCurrentChange(val) {
            this.page.currentPage = val;
            this.getList();
        },
        /* filter条件发生改变 */
        filterChange(form) {
            this.form = form
            this.page.currentPage = 1
            this.$emit('queryForm', form)
            this.getList();
        },
        /* filter条件发生改变 */
        search() {
            this.$refs.tableFilter.search()
        },
        /* 重置表格过滤参数 */
        reset() {
            this.$refs.tableFilter.reset()
        },
        /* 获取表格数据 */
        getList() {
            if (!this.http||this.loading) return
            
            let parme = {
                ...this.defaultParme,
                [this.isOld ? 'pageSize' : 'size']: this.page.pageSize,//每页显示的数据数
                [this.isOld ? 'pageNo' : 'current']: this.page.currentPage,//当前所在页
                ...this.form
            }
            // 获取列表前做点什么
            this.$emit('beforeHttp', parme)
            
            this.loading = true
            this.http(parme).then(res => {
                if (res.code != 200) {
                    this.$message.error(res.message || '数据加载错误!')
                    return
                }
                const d = res.data

                // 后端接口框架更改 导致总数用total字段接收  之前框架用的count
                this.page.total = this.isOld ? d.count : d.total
                let list = this.isOld ? d.list : d.records
                this.list = list
                // 数据获取成功
                this.$emit('currentData',res.data)
            }).finally(_ => this.loading = false)
        },
    },
};
</script>

<style scoped lang="scss">
.table-wrapper {
    padding: 20px;
    background-color: #fff;
}
.base-table-container {
    margin: 20px;
}
.filter-box {
    margin-bottom: 20px;
}
.table-button{
    margin-left: 0;
    &:not(:last-child){
        margin-right: 10px;
    }
}
</style>

筛选项组件 tableFilter 封装

<template>
    <div class="filter-wrapper" v-if="formList.length">
        <el-form ref="form" :inline="true" v-bind="$attrs">
            <template v-for="(item, key) in formList">
                <template v-if="item.condition===undefined||item.condition(item)">
                    <!-- 文本输入框 -->
                    <el-form-item :label="item.name" :key="key" v-if="item.type == 'input'">
                        <el-input v-model="item.value" v-on="item.listeners" v-bind="item.attrs" clearable @change="sendForm">
                        </el-input>
                    </el-form-item>
                    <!-- 自动搜索 -->
                    <el-form-item :label="item.name" :key="key" v-if="item.type == 'autocomplete'">
                        <el-autocomplete v-model="item.value" v-on="item.listeners" v-bind="item.attrs" clearable @select="sendForm">
                        </el-autocomplete>
                    </el-form-item>
                    <!-- select选择器 -->
                    <el-form-item :label="item.name" :key="key" v-if="item.type == 'select'">
                        <el-select v-model="item.value" v-on="item.listeners" v-bind="item.attrs" clearable @change="sendForm">
                            <el-option label="全部" value="">
                            </el-option>
                            <el-option v-for="sub in item.options" :key="sub[item.props?item.props.value:'value']" :label="sub[item.props?item.props.label:'label']" :value="sub[item.props?item.props.value:'value']">
                            </el-option>
                        </el-select>
                    </el-form-item>
                    <!-- 日期选择器 -->
                    <el-form-item :label="item.name" :key="key" v-if="item.type == 'date'">
                        <el-date-picker clearable v-model="item.value" v-on="item.listeners" v-bind="item.attrs" :picker-options="pickerOptions" @change="sendForm" start-placeholder="开始日期" end-placeholder="结束日期" range-separator="-">
                        </el-date-picker>
                    </el-form-item>
                    <!-- 级联选择器 -->
                    <el-form-item :label="item.name" :key="key" v-if="item.type == 'cascader'">
                        <el-cascader clearable v-model="item.value" v-on="item.listeners" v-bind="item.attrs" @change="sendForm">
                        </el-cascader>
                    </el-form-item>
                    <!-- 区间输入 -->
                    <el-form-item :label="item.name" :key="key" v-if="item.type == 'range'">
                        <div style="width:300px;display:flex;">
                            <el-input v-model="item.value[0]" :placeholder="item.attrs.startPlaceholder" v-on="item.listeners" v-bind="item.attrs" @change="sendForm" clearable></el-input>
                            <span style="margin:0 10px;">-</span>
                            <el-input v-model="item.value[1]" :placeholder="item.attrs.endPlaceholder" v-on="item.listeners" v-bind="item.attrs" @change="sendForm" clearable></el-input>
                        </div>
                    </el-form-item>
                    <!-- 按钮 -->
                    <el-form-item :key="key" v-if="item.type == 'button'">
                        <el-button v-bind="item.attrs" v-on="item.listeners">{{ item.name }}</el-button>
                    </el-form-item>
                </template>

            </template>
        </el-form>
    </div>
</template>

<script>
export default {
    name: "TableFilter",
    data() {
        return {
            formList: [],//表单数据
            pickerOptions: {//日期选择器参数
                shortcuts: [{
                    text: '最近一周',
                    onClick(picker) {
                        const end = new Date();
                        const start = new Date();
                        start.setTime(start.getTime() - 3600 * 1000 * 24 * 7);
                        picker.$emit('pick', [start, end]);
                    }
                }, {
                    text: '最近一个月',
                    onClick(picker) {
                        const end = new Date();
                        const start = new Date();
                        start.setTime(start.getTime() - 3600 * 1000 * 24 * 30);
                        picker.$emit('pick', [start, end]);
                    }
                }, {
                    text: '最近三个月',
                    onClick(picker) {
                        const end = new Date();
                        const start = new Date();
                        start.setTime(start.getTime() - 3600 * 1000 * 24 * 90);
                        picker.$emit('pick', [start, end]);
                    }
                }]
            },

        };
    },
    props: {
        filterList: {
            type: Array,
            default() {
                return [];
            },
        },
    },
    created() {
        /* 获取筛选项列表  用于数据的动态绑定 */
        this.init()
    },
    methods: {
        /* 重置 */
        reset() {
            this.init()
            this.sendForm()
        },
        // 搜索 查询
        search() {
            this.sendForm()
        },
        /* 初始化数据 */
        init() {
            this.$set(this, 'formList', this.filterList)
            this.formList.forEach((item, i) => {
                if (item.type === 'range') {
                    this.$set(this.formList[i], 'value', ['', ''])
                    return
                }
                this.$set(this.formList[i], 'value', '')
            })
        },
        /* 筛选条件发生变化  input输入不会触发该事件  但是input后点击enter键会触发 */
        sendForm() {
            let form = {}
            this.formList.forEach(item => {
                if (item.value !== '') {
                    if (item.type == 'date'&&item.props) {
                        if (item.value[0] !== '') {
                            form[item.props.start] = item.value[0] + ' 00:00:00'
                        }
                        if (item.value[1] !== '') {
                            form[item.props.end] = item.value[1] + ' 23:59:59'
                        }
                        return
                    }
                    if (item.type == 'date'&&item.prop) {
                        if (item.value[0] !== '') {
                            form[item.prop] = item.value + ' 23:59:59'
                        }
                        return
                    }
                    if (item.type === 'range') {
                        if (item.value[0] !== '') {
                            form[item.props.start] = item.value[0]
                        }
                        if (item.value[1] !== '') {
                            form[item.props.end] = item.value[1]
                        }
                        return
                    }

                    // 返回的数据是数组 但是后台需要用多个字段接收  因此遍历配置的 prop 分别接收value(数组)中的值
                    if (item.type == "cascader") {
                        if (item.props) {
                            item.props.forEach((lable, index) => form[lable] = item.value[index] || '')
                            return
                        }

                    }
                    form[item.prop] = item.value
                }
            })
            // 筛选条件发生变化 暴露filterChange方法,并返回参数form(当前的筛选条件)
            this.$emit('filterChange', form)
        }
    },
};
</script>

<style scoped lang="scss">
.filter-wrapper {
    padding: 20px;
    padding-bottom: 0;
    background-color: #fff;
}
</style>

分页组件 tablePagination

<template>
    <div class="container">
        <!-- 分页 -->
        <el-pagination
            v-on="$listeners"
            v-bind="$attrs"
            :pageSizes="[10, 20, 50, 100, 300, 500]"
            :layout="'total, sizes, prev, pager, next, jumper'"
        ></el-pagination>
    </div>
</template>

<script>
export default {
    data() {
        return {
            
        };
    },
};
</script>

<style scoped lang="scss">
</style>

使用效果,超级简单,原本一个页面几百行代码,现在几十行解决,而且还方便维护

<template>
    <my-table ref="myTable" :isOld="true" :filterList="filterList" :columns="columns" :http="findPurchasePage"></my-table>
</template> 

<script>
import { findPurchasePage } from '@/api/supplier/purchase'
import { receivGoods } from '@/api/orderManage/list'
import ShopItem from "@/components/Base/shopItem";
import MyTable from "@/components/MyTable";
import PreSaleOrderDetail from "./components/preSaleOrderDetail";
import {
    orderTypeList,//单据类别
    payStateList,//支付状态
    billStateList//单据状态
} from '@/utils/statusManage'
export default {
    name: "purchaseOrderList",
    components: {
        ShopItem,
        MyTable,
    },
    data() {
        return {
            findPurchasePage,
            form: {},
            filterList: [
                { name: '单据号', type: 'input', prop: 'conditions', attrs: { placeholder: '单据号', style: { width: '380px' } } },
                { name: '单据类别', type: 'select', prop: 'orderType', options: orderTypeList },
                { name: '单据状态', type: 'select', prop: 'orderStatus', options: billStateList },
                { name: '创建日期', type: 'date', props: { start: 'beginTime', end: 'endTime' }, attrs: { type: "daterange", valueFormat: "yyyy-MM-dd" } },
                { name: '支付状态', type: 'select', prop: 'payState', options: payStateList },
                { name: '查询', type: 'button', attrs: { type: 'primary' }, listeners: { click: _ => this.$refs.myTable.search() } },
                { name: '重置', type: 'button', listeners: { click: _ => this.$refs.myTable.reset() } },
            ],
            /* 表头 */
            columns: [
                { attrs: { prop: 'id', label: '单据号', minWidth: '140', align: 'center' } },
                { attrs: { prop: 'orderId', label: '商品', minWidth: '300', align: 'center' }, render: (h, { row }) => h(ShopItem, { props: { list: row.orderItemList } }) },
                { attrs: { prop: 'supplierName', label: '供应商信息', width: '140', align: 'center' } },
                { attrs: { prop: 'orderType', label: '单据类别', width: '140', align: 'center' }, statusList: orderTypeList },
                { attrs: { prop: 'payState', label: '支付状态', width: '140', align: 'center' }, statusList: payStateList },
                { attrs: { prop: 'orderStatus', label: '单据状态', width: '140', align: 'center' }, statusList: billStateList },
                { attrs: { prop: 'totalMoney', label: '总金额', width: '160', align: 'center' } },
                { attrs: { prop: 'orderTime', label: '创建时间', minWidth: '100', align: 'center' } },
                {
                    attrs: { label: '操作', minWidth: '150', fixed: 'right', align: 'center' }, btns: row => [
                        { name: '立即支付', condition: row => row.payState == 0 && row.orderStatus == 1, listeners: { click: _ => this.toPay(row) } },
                        { name: '确认收货', condition: row => row.orderStatus == 3, listeners: { click: _ => this.confirmReceipt(row) } },
                    ]
                },

            ]
        };
    }
};
</script>

  • 4
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值