Table.vue
<template>
<div class="table-wrapper">
<div class="actions-wrapper">
<slot name="actions"></slot>
<el-button icon="el-icon-setting" size="mini" @click="visible = true" v-if="$props.filterColumn">
显示元素
</el-button>
</div>
<div class="elTable-wrapper">
<template>
<el-table height="100%" :key="tableId" :data="data ? data[$props.props.content] : []"
:highlight-current-row="highlightCurrentRow" @selection-change="selectionChange"
@current-change="handleCurrentChange" v-loading="loading" :element-loading-text="btnLoadingLabel"
:border="border" :stripe="stripe" :show-overflow-tooltip="showOverflowTooltip"
:max-height="maxHeight" :size="size" :align="align" :ref="`table${$route.path}`" style="width:100%">
<el-table-column type="selection" width="55" v-if="showBatchDelete"></el-table-column>
<template v-for="column in tableColumns">
<el-table-column header-align="left" align="left" :prop="column.prop" :label="column.label"
:width="column.width" :min-width="column.minWidth" :fixed="column.fixed" :key="column.prop"
:type="column.type" :formatter="column.formatter"
:sortable="column.sortable == null ? false : column.sortable" v-if="!column.hide">
<span slot-scope="{ row }">
<span v-if="column.slotScope">
<slot :name="column.slotScope.slot" :row="row" />
</span>
<span v-else> {{row[column.prop]}} </span>
</span>
</el-table-column>
</template>
<slot></slot>
</el-table>
</template>
</div>
<div class="pagination-wrapper" v-if="data && data[$props.props.totalSize] > 0">
<el-pagination layout="total, sizes, prev, pager, next, jumper" :page-sizes="[3,10, 20, 50, 100, 300, 500]"
@current-change="handlePageChange" :current-page="page" :page-size="pageSize"
@size-change="handleSizeChange" :total="data ? data[$props.props.totalSize] : 0">
</el-pagination>
</div>
<c-table-column-filter v-if="visible" :columns="currentColumn" @columns-change="handleColumnsChange"
@close="visible = false" />
</div>
</template>
<script>
import CTableColumnFilter from "./TableColumnFilter";
export default {
components: {
CTableColumnFilter
},
props: {
columns: Array, // 表格列配置
data: Object, // 表格分页数据
props: {
type: Object,
default: () => {
return {
content: "content",
totalSize: "totalSize"
};
}
},
size: {
// 尺寸样式
type: String,
default: "mini"
},
align: {
// 文本对齐方式
type: String,
default: "left"
},
maxHeight: {
// 表格最大高度
type: Number,
default: 800
},
minHeight: {
// 表格最小高度
type: Number,
default: 180
},
// heights: {
// // 表格高度
// type: String,
// default: "auto"
// },
border: {
// 是否显示边框
type: Boolean,
default: false
},
stripe: {
// 是否显示斑马线
type: Boolean,
default: true
},
highlightCurrentRow: {
// // 是否高亮当前行
type: Boolean,
default: false
},
showOverflowTooltip: {
// 是否单行显示
type: Boolean,
default: true
},
showBatchDelete: {
// 是否显示操作组件
type: Boolean,
default: true
},
loading: {
type: Boolean,
default: false
},
btnLoadingLabel: {
type: String,
default: "加载中..."
},
page: {
type: Number,
default: 1
},
pageSize: {
type: Number,
default: 20
},
filterColumn: {
type: Boolean,
default: false
}
},
data() {
return {
selections: [], // 列表选中列
visible: false,
currentColumn: [],
tableId: new Date().getTime()
};
},
computed: {
tableColumns() {
const props = this.currentColumn.map(
item => `${item.prop}-${item.label}`
);
const list = new Array(props.length).fill(null);
this.$props.columns.map((column, columnIndex) => {
const index = props.indexOf(`${column.prop}-${column.label}`);
if (index >= 0) {
list[index] = {
...column,
...this.currentColumn[index]
};
} else {
const hide = column.hide != undefined ? column.hide : false;
const isExport = column.export != undefined ? column.export : false;
list.push({
...column,
hide,
export: isExport
});
}
});
list.forEach((item, index) => {
if (!item) {
list.splice(index, 1);
}
});
return list;
}
},
methods: {
// 选择切换
selectionChange: function(selections) {
this.selections = selections;
this.$emit("selection-change", selections);
},
// 选择切换
handleCurrentChange: function(val) {
this.$emit("current-change", val);
},
// 换页刷新
handlePageChange: function(pageNum) {
this.$emit("page-change", pageNum);
},
//
handleSizeChange(size) {
this.$emit("size-change", size);
},
handleColumnsChange(column) {
this.currentColumn = column;
let history = localStorage.getItem("columnOption");
history ? (history = JSON.parse(history)) : (history = {});
const key = this.$route.path;
history[key] = column;
localStorage.setItem("columnOption", JSON.stringify(history));
this.visible = false;
this.tableId = new Date().getTime();
},
getColumnSetting() {
if (!this.$props.filterColumn) {
this.currentColumn = this.$props.columns.map(item => {
return {
label: item.label,
prop: item.prop,
hide: item.hide,
export: false
};
});
return false;
}
let history = localStorage.getItem(`columnOption`);
history ? (history = JSON.parse(history)) : (history = {});
const historyCurrentTable = history[this.$route.path];
if (!historyCurrentTable) {
this.currentColumn = this.$props.columns.map(item => {
return {
label: item.label,
prop: item.prop,
hide: item.hide,
export: false
};
});
return false;
}
const historyColumns = historyCurrentTable.map(item => {
return `${item.prop}-${item.label}`;
});
let container = new Array(historyColumns.length).fill(null);
this.$props.columns.forEach((column, columnIndex) => {
const index = historyColumns.indexOf(`${column.prop}-${column.label}`);
if (index >= 0) {
container[index] = {
...column,
...historyCurrentTable[index]
};
} else {
const hide = column.hide != undefined ? column.hide : false;
const isExport = column.export != undefined ? column.export : false;
container.push({
...column,
hide,
export: isExport
});
}
});
container.forEach((item, index) => {
if (!item) {
container.splice(index, 1);
}
});
this.currentColumn = container;
}
},
created() {
this.getColumnSetting();
}
};
</script>
<style lang="scss" scoped>
.actions-wrapper {
margin-bottom: 10px;
margin: 10px;
}
.pagination-wrapper {
margin-top: 20px;
display: flex;
justify-content: flex-end;
}
</style>
TableColumnFilter.vue
<template>
<el-dialog title="显示元素" :visible="true" width="500px" @close="$emit('close')">
<div class="table">
<div class="table-row">
<div class="table-cell">排序</div>
<div class="table-cell">列表名称</div>
<div class="table-cell">是否显示</div>
</div>
<!-- 拖拽插件 -->
<vuedraggable v-model="tableData">
<!-- 过度效果 -->
<transition-group>
<div class="table-row" v-for="(item, index) in tableData" :key="item.label">
<div class="table-cell">{{ index + 1 }}</div>
<div class="table-cell">{{ item.label }}</div>
<div class="table-cell">
<el-switch v-model="item.hide"></el-switch>
</div>
</div>
</transition-group>
</vuedraggable>
</div>
<div class="actions-wrapper" slot="footer">
<el-button size="small" @click="$emit('close')">取消</el-button>
<el-button type="primary" size="small" @click="save">
保存
</el-button>
</div>
</el-dialog>
</template>
<script>
import vuedraggable from "vuedraggable";
export default {
components: {
vuedraggable
},
props: {
columns: Array
},
data() {
return {
tableData: []
};
},
methods: {
init() {
const data = Object.assign([], this.$props.columns);
this.tableData = data.map(item => {
return {
...item,
hide: !item.hide
};
});
},
save() {
let data = Object.assign([], this.tableData);
data = data.map(item => {
return {
...item,
hide: !item.hide
};
});
this.$emit("columns-change", data);
}
},
created() {
this.init();
}
};
</script>
<style lang="scss" scoped>
.table>.table-row {
font-weight: 600;
}
.table-row {
display: flex;
padding: 10px;
border-bottom: 1px solid #eee;
}
.table-cell {
width: 30%;
height: 35px;
display: flex;
align-items: center;
&:nth-child(1) {
width: 55px;
}
&:nth-child(2) {
width: 40%;
}
}
</style>