封装的table支持复合头部表格,单元格支持自定义组件,支持分页,支持自定义显示列,支持点击事件等等的复合型通用表格,废话不多说,上代码~
table/index.vue 文件
<template>
<div class="commonTable">
<div class="flex-row-reverse">
<img v-if="showExport" class="icons m-l-10" src="../../assets/导出.png" alt="导出表格" srcset="" title="导出表格" @click="exportExcel">
<SelecPop v-if="!hideConfig" :columns="columns" @changeColumns="changeColumns" />
</div>
<Table id="comTable" ref="comTable" v-loading="tableData.loading" stripe :data="tableData.data" border :height="height" highlight-current-row @sort-change="sortChange">
<TableColumn type="index" width="50" label="序号" />
<Column
v-for="(item,itemIndex) in selectedColumns"
:key="itemIndex"
:item="item"
:item-index="itemIndex"
@tableClick="tableClick"
>
<!-- 花了两天时间的成果 -->
<template v-for="slotName in soltsNames" :slot="slotName" slot-scope="scope">
<slot :name="slotName" :scope="scope" />
</template>
</Column>
</Table>
<div class="flex-row-reverse m-t-20">
<Pagination
:current-page="tableData.page?tableData.page:1"
:page-size="10"
layout="total, prev, pager, next, jumper"
:total="tableData.total"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</div>
</div>
</template>
<script>
import Column from './Column.vue'
// import { export_json_to_excel, export_table_to_excel } from '@/vendor/Export2Excel'
import SelecPop from './SelecPop.vue'
import { Table, TableColumn, Pagination } from 'element-ui'
export default {
components: {
TableColumn,
Table,
Pagination,
SelecPop,
Column
},
props: {
hideConfig: {
type: Boolean,
default: false
},
height: {
type: [String, Number],
default: 550
},
columns: {
type: Array,
default: () => []
},
tableData: {
type: Object,
default: () => ({})
},
showExport: {
type: Boolean,
default: false
}
},
data() {
return {
page: 1,
selectedColumns: []
}
},
computed: {
soltsNames() {
const temp = []
for (const key in this.$scopedSlots) {
console.log(key, this.$scopedSlots, 'this.$slots')
temp.push(key)
}
return temp
}
},
created() {
this.selectedColumns = this.columns.filter(item => !item.isHide)
},
methods: {
exportExcel() {
this.$emit('exportExcel')
},
async generateExcel() {
await this.$nextTick()
import('file-saver').then(FileSaver => {
import('xlsx').then(XLSX => {
var wb = XLSX.utils.table_to_book(document.querySelector('#comTable'), { raw: true })
var wbout = XLSX.write(wb, { bookType: 'xlsx', bookSST: true, type: 'array' })
try {
FileSaver.saveAs(new Blob([wbout], { type: 'application/octet-stream' }), `表格.xlsx`)
} catch (e) {
if (typeof console !== 'undefined') {
console.log(e, wbout)
}
}
return wbout
})
})
},
sortChange({ column, prop, order }) {
console.log({ column, prop, order })
},
changeColumns(e) {
this.selectedColumns = this.columns.filter(item => {
for (let i = 0; i < e.length; i++) {
if (item.label === e[i]) {
return true
}
}
return false
})
},
tableClick({ index, name, row, label }) {
this.$emit('tableClick', { index, name, row, label })
},
handleCurrentChange(e) {
this.$emit('pageChange', e)
},
handleSizeChange(e) {
console.log('sizeChange', e)
},
getButtonType(index) {
const finallIndex = index % 5
switch (finallIndex) {
case 0:
return 'danger'
case 1:
return 'warning'
case 2:
return ''
case 3:
return 'success'
case 4:
return 'info'
default:
break
}
}
}
}
</script>
<style lang="scss" >
.icons{
width: 15px;
height: 15px;
}
.el-divider--horizontal{
margin: 0px 0;
}
.commonTable {
.buttons{
color: $light-blue;
margin-right: 10px;
white-space: nowrap;
}
.buttons:hover{
cursor: pointer;
opacity: 0.8;
}
.el-table{
font-size: 12px;
}
.commonLabel{
color: #63656e;
display: inline-block;
text-overflow: ellipsis;
white-space:nowrap;
overflow: hidden;
}
.cellButton{
display: flex;
align-items: center;
flex-wrap: nowrap;
justify-content:center;
}
.el-table td,
.el-table th {
text-align: center;
}
.el-table td, .el-table th {
padding: 8px 0;
}
.el-table .cell{
display: flex;
align-items: center;
justify-content: center;
line-height: 31px;
}
}
</style>
然后是Column.vue文件
<template>
<TableColumn
:key="item.prop"
:prop="item.prop"
:label="item.label"
:width="item.width"
:fixed="item.fixed"
:sortable="item.sortable"
>
<template v-if="item.children">
<MyColumns
v-for="(childItem,childIndex) in item.children"
:key="childIndex"
:item="childItem"
:item-index="childIndex"
v-bind="$attrs"
@tableClick="tableClick"
/>
</template>
<template v-if="!item.children" slot-scope="scope">
<div v-if="item.type === 'button'" class="cellButton">
<span
v-for="(elem) in getButtons(item.buttons,scope.row)"
:key="elem"
class="buttons"
@click="tableClick({index:itemIndex, name:elem, row: scope.row, label: scope.column.label})"
v-html="elem"
/>
</div>
<slot v-if="item.type==='slot'" :name="item.prop" :row="scope.row" />
<Tooltip v-else :open-delay="1500" class="item" effect="dark" :content="getValue(item.value,scope)" placement="top-start">
<div
class="commonLabel"
:style="{color:getColor(item.color,scope.row),cursor:item.color?'pointer':'',width:'100%'}"
@click="tableClick({index:itemIndex, name:getValue(item.value,scope), row: scope.row, label: scope.column.label})"
v-html="getValue(item.value,scope)"
/>
</Tooltip>
</template>
</TableColumn>
</template>
<script>
import { TableColumn, Tooltip } from 'element-ui'
export default {
name: 'MyColumns',
components: {
TableColumn, Tooltip
},
props: {
item: {
type: Object,
default: () => ({})
},
itemIndex: {
type: Number
}
},
mounted () {
},
methods: {
tableClick({ index, name, row, label }) {
this.$emit('tableClick', { index, name, row, label })
},
getValue(value, scope) {
if (value) {
if (typeof value === 'function') {
return value({ row: scope.row })
}
return value
}
return scope.row[scope.column.property]
},
getColor(color, row) {
if (typeof color === 'function') {
return color({ row })
}
return color
},
getButtons(buttons, row) {
if (typeof buttons === 'function') {
return buttons({ row })
}
return buttons
}
}
}
</script>
<style lang="scss" scoped>
.cellButton{
display: flex;
align-items: center;
flex-wrap: nowrap;
justify-content:center;
}
</style>
还有SelecPop.vue文件
<template>
<div class="flex-row-reverse">
<Popover
v-model="visible"
placement="right"
width="560"
>
<div class="font-16-b">表格设置</div>
<Divider />
<div class=" flex-row-between">
<span>字段显示设置</span>
<Checkbox v-model="checkAll" label="全选" @change="handleCheckAllChange" />
</div>
<CheckboxGroup v-model="test">
<Checkbox v-for="item in selectedColumns" :key="item.label" :label="item.label" class="brand" />
</CheckboxGroup>
<Divider />
<div class="flex-row-reverse">
<Button type="primary" size="mini" @click="changeColums">确定</Button>
<div class="m-r-20">
<Button size="mini" @click="visible=false">取消</Button>
</div>
</div>
<i slot="reference" class="el-icon-s-tools" />
</Popover>
</div>
</template>
<script>
import { Button, Popover, Checkbox, Divider, CheckboxGroup } from 'element-ui'
export default {
components: {
Button,
Popover,
Checkbox,
Divider,
CheckboxGroup
},
props: {
columns: {
type: Array,
default: () => ([])
}
},
data() {
return {
selectedColumns: this.columns.filter(item => !item.buttons),
visible: false,
test: this.columns.filter(item => (!item.buttons && !item.isHide)).map(item => item.label),
checkAll: false
}
},
methods: {
handleCheckAllChange(val) {
this.test = val ? this.columns.map(item => item.label) : []
},
changeColums() {
const otherColumn = this.columns.filter(item => item.buttons).map(item => item.label)
this.$emit('changeColumns', [...this.test, ...otherColumn])
this.visible = false
}
}
}
</script>
<style lang="scss" scoped>
.brand{
margin-right: 20px;
line-height: 30px;
font-size: 16px;
display: inline-block;
}
</style>
欢迎大家补充
好了 上git地址 https://github.com/tybt/element-table.git