对elementui表格组件进行二次封装
elementui是我们在做vue项目时常用的组件库,但是这些组件库并不能满足我们的需求,因此我在我需要的情况下对表格组件进行了二次封装
父组件使用:
<cu-table
ref="videoTable"
:columns="columns"
:buttons="buttons"
:searchItem="searchItem"
@add-row="handlerAdd" // 表格头部按钮事件
@del-row="handlerDel"
@menuClick="menuClick"
:requireUrl="requireUrl">
<!-- <template slot="button">
<el-button class="group-btn" type="primary" size="mini">xxx</el-button>
<el-button class="group-btn" type="primary" size="mini">xxx</el-button>
</template> -->
<template v-slot:title=" { row }">
<a href="#">{{row.title}}</a>
</template>
<template v-slot:opt="row">
<el-dropdown trigger="click">
<span class="el-dropdown-link">
操作<i class="el-icon-arrow-down el-icon--right"></i>
</span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item @click.native="handlerEdit(row)">编辑</el-dropdown-item>
<el-dropdown-item @click.native="handlerDelete(row)">删除</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</template>
</cu-table>
data() {
return {
requireUrl: 'back/videoList', //表格数据调用的接口
columns: [
code: 'title',
label: '标题',
slot: 'title' //是否排序
code: 'createTime',
label: '创建时间',
sort: true,
width: 200
code: 'auth',
label: '创建人'
code: 'updateTime',
label: '修改时间',
width: 200
code: 'url',
label: '播放地址',
width: 300
code: 'likeNum',
label: '点赞数',
sort: true
code: 'commentNum',
label: '评论数',
sort: true
code: 'shareNum',
label: '分享数',
sort: true
code: 'playNums',
label: '播放量',
sort: true
code: 'status',
label: '状态',
code: 'operation',
label: '操作',
fixed: 'right', // 固定栏
slot: 'opt' // 操作栏插槽
buttons: ['add', 'del', 'frozen', 'thaw', 'export'], // 表格头部按钮,可利用插槽自定义
}
表格子组件:
<template>
<div class="curstom-vue">
<div style="display:flex;justify-content: space-between;align-items: center;">
<div class="btn-group-box">
<div v-if="buttons.length">
<span class="btn-span" v-for="btn in buttons" :key="btn" >
<el-button @click="add1" v-if="btn === 'add'" class="group-btn" type="primary" size="mini">增加</el-button>
<el-button @click="del1" v-if="btn === 'del'" class="group-btn" type="primary" size="mini">删除</el-button>
<el-button @click="frozen1" v-if="btn === 'frozen'" class="group-btn" type="primary" size="mini">冻结</el-button>
<el-button @click="thaw1" v-if="btn === 'thaw'" class="group-btn" type="primary" size="mini">解冻</el-button>
<el-button @click="export1" v-if="btn === 'export'" class="group-btn" type="primary" size="mini">导出</el-button>
</span>
<div class="solt-btn">
<slot name="button"></slot>
<header-search @search="search" :options="searchItem"></header-search>
<el-table
class="vue-table"
:data="tableData"
:border="border"
:stripe="stripe"
@row-contextmenu="rightClick"
@selection-change="selectChange">
<el-table-column
v-if="select"
type="selection"
width="55"
fixed="left">
</el-table-column>
<el-table-column
v-if="index"
type="index"
label="序号"
width="55"
:index="index">
</el-table-column>
<template v-for="(column, index) in columns">
<!-- <slot v-if="column.slot" :name="column.slot"></slot> -->
<el-table-column v-if="column.slot"
:key="'index-'+index"
:label="column.label"
:width="column.width"
:sortable="column.sort"
:fixed="column.fixed">
<template slot-scope="scope">
<slot :row="scope.row" :name="column.slot">{{scope.row.title}}</slot>
</template>
</el-table-column>
<el-table-column v-else-if="column.code !== 'operation'"
:key=index
:prop="column.code"
:label="column.label"
:width="column.width"
:sortable="column.sort"
:fixed="column.fixed">
</el-table-column>
<el-table-column v-else-if="column.code === 'operation'"
:key=index
:prop="column.code"
:label="column.label"
:width="column.width"
:fixed="column.fixed">
<template slot-scope="scope">
<template slot-scope="scope" v-if="column.slot">
<slot :row="scope" :name="column.slot">{{scope}}</slot>
</template>
<el-dropdown trigger="click">
<span class="el-dropdown-link">
操作<i class="el-icon-arrow-down el-icon--right"></i>
</span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item @click.native="handlerEdit(scope)">编辑</el-dropdown-item>
<el-dropdown-item @click.native="handlerDelete(scope)">删除</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</template>
</el-table-column>
</template>
<template slot="empty">
<img src="./../assets/images/noData.png" alt="" srcset="">
</template>
</el-table>
<div id="menu" ref="rightMenu">
<div class="menu" v-for="item in menus" :key="item" @click.stop="infoClick(item)">{{item.label}}</div>
<el-pagination
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:page-sizes="[2, 10, 20, 50]"
layout="total, sizes, prev, pager, next"
:total="total">
</el-pagination>
</template>
<script>
import HearderSearch from './headerSearch'
import * as API from './../api/index'
export default {
name: 'Table',
props: {
columns: {
type: Array,
default: () => {
return []
border: {
type: Boolean,
default: true
tableData: {
type: Array,
default: () => {
return []
buttons: {
type: Array,
default: () => {
return []
select: {
type: Boolean,
default: true
index: {
type: Boolean,
default: true
stripe: {
type: Boolean,
default: true
menus: {
type: Array,
default: () => {
return [{
code: 'edit',
label: '编辑'
code: 'del',
label: '删除'
searchItem: {
type: Array
requireUrl: {
type: String,
default: ''
data() {
return {
currentRowIndex : 0,
showMenu: false,
rowData: {},
activeName: '1',
tableParams: {
limit: 1,
offset: 10
total: 0,
selectData: []
created() {
components: {
'header-search': HearderSearch
mounted() {
document.addEventListener('click', () => {
const menu = document.querySelector("#menu");
menu.style.display = 'none';
this.getData();
methods: {
// 查询数据
getData() {
API.GET(this.requireUrl, this.tableParams).then(res => {
this.tableData = res.data;
this.total = res.total;
selectChange(val) {
this.selectData = val;
search(item) {
alert(22);
console.log(item)
handlerEdit(item) {
console.log(item);
console.log('edit');
handlerDelete(item) {
console.log(item);
console.log('delete');
add1() {
this.$emit('add-row');
del1() {
this.$emit('del-row', this.selectData);
frozen1() {
this.$emit('frozen-row');
thaw1() {
this.$emit('thaw-row');
export1() {
this.$emit('export-row');
// 自定义菜单的点击事件
infoClick(item) {
this.$emit('menuClick', {item, row: this.rowData});
// table的右键点击当前行事件
rightClick(row, column, event) {
const menu = document.querySelector("#menu");
event.preventDefault();
//获取我们自定义的右键菜单
// 根据事件对象中鼠标点击的位置,进行定位
menu.style.left = event.clientX + 'px';
menu.style.top = event.clientY + 'px';
// 改变自定义菜单的隐藏与显示
menu.style.display = 'block';
this.rowData = row;
handleSizeChange(val) {
// console.log(`每页 ${val} 条`);
this.tableParams.offset = val;
this.getData();
handleCurrentChange(val) {
// console.log(`当前页: ${val}`);
this.tableParams.limit = val;
this.getData();
</script>
<style lang='scss'>
.curstom-vue {
height: 100%;
display: flex;
flex-direction: column;
.btn-group-box {
display: flex;
.solt-btn .el-button:nth-child(n+2){
margin-left: 0.1rem;
.btn-span:nth-child(n+2) {
margin-left: 0.1rem;
.btn-span:last-child {
margin-right: 0.1rem;
.vue-table {
margin-top: 0.1rem;
.el-dropdown-link {
cursor: pointer;
color: #409EFF;
.el-icon-arrow-down {
font-size: 12px;
#menu {
border-radius: 5px;
overflow: hidden; /*隐藏溢出的元素*/
box-shadow: 0 1px 1px #888, 1px 0 1px #ccc;
position: absolute;
display: none;
background: #ffffff;
z-index: 10;
padding-bottom: 10px;
background: #ccc;
.menu {
width: 125px;
height: 25px;
line-height: 25px;
text-indent: 10px;
cursor: pointer;
.menu:hover {
color: deeppink;
text-decoration: underline;
.is-scrolling-none {
height: 100%;
</style>
搜索栏子组件:
<template>
<div class="hearder-search">
<el-select style="width:100px" v-model="value" clearable placeholder="请选择" size="mini" @change="changeItem">
<el-option
v-for="item in options"
:key="item.value"
:label="item.label"
:value="item.value"
size="mini">
</el-option>
</el-select>
<div class="input" v-if="!type">
<el-input size="mini" style="width:200px" v-model="searchValue" placeholder="请输入内容"></el-input>
<div class="dateTime" v-else-if="type === 'dateTime'">
<el-date-picker
size="mini"
v-model="dateTime"
type="datetimerange"
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期"
value-format="yyyy-MM-dd HH:mm:ss"
align="right"
@change="changeDateTime">
</el-date-picker>
<div class="num" v-else-if="type === 'range'">
<div class="range-num">
<el-input size="mini" style="width:100px" v-model="range1" placeholder="请输入内容"></el-input> 至
<el-input size="mini" style="width:100px" v-model="range2" placeholder="请输入内容"></el-input>
<el-button @click="search" style="margin-left:10px" type="primary" icon="el-icon-search" size="mini">搜索</el-button>
</template>
<script>
export default {
name: 'HeaderSearch',
props: {
options: {
type: Array
data() {
return {
searchValue: '',
value: '',
dateTime: '',
type: '',
range1: '',
range2: ''
computed: {
methods: {
changeItem(item) {
const type = this.options.find(val => val.value === item);
this.type = type.type;
changeDateTime(item) {
console.log(item)
search() {
if (!this.value) {
this.$message({
message: '请输入搜索类型',
type: 'warning'
} else {
this.$emit('search', {
type: this.value,
value: this.searchValue
</script>
<style lang='scss'>
.hearder-search {
display: flex;
.el-select {
.el-input__inner {
// border-right: none;
border-top-left-radius: 4px;
border-bottom-left-radius: 4px;
border-top-right-radius: 0;
border-bottom-right-radius: 0;
.input {
.el-input__inner {
border-left: none;
border-top-left-radius: 0;
border-bottom-left-radius: 0;
border-top-right-radius: 4px;
border-bottom-right-radius: 4px;
.dateTime {
.el-input__inner {
border-left: none;
border-top-left-radius: 0;
border-bottom-left-radius: 0;
border-top-right-radius: 4px;
border-bottom-right-radius: 4px;
.num {
.range-num {
box-sizing: content-box;
border: 1px solid #DCDFE6;
border-left: none;
border-top-left-radius: 0;
border-bottom-left-radius: 0;
border-top-right-radius: 4px;
border-bottom-right-radius: 4px;
background: #fff;
.el-input__inner {
border: none;