用vue+element ui开发管理系统时,使用el-table做表格,当表格列过多的时候,想要做成可选表头的,实现表格列的筛选显示,效果如下:
代码文件结构:
废话不多说,直接上代码:
第一步:新建名为 TableHeaderRender.vue 的文件
placement="bottom" width="200" trigger="manual" v-model="visible" @show="showPopover" popper-class="table-header-popover" > placeholder="请输入内容" v-model="value" size="small" clearable @keyup.enter.native="confirm" ref="sInput" >
slot="reference" style="margin-left: 5px" @click.stop="popClick" v-click-outside="closeOver" > class="filter-icon el-icon-search" :style="{ color: iconColor ? '#9a4b9b' : '#909399' }" >
export default {
name: "tableHeaderRender",
data() {
return {
// input 绑定的值
value: "",
visible: false,
iconColor: false,
};
},
props: {
tableColumn: {
type: Object,
default: () => {},
},
columnProp: {
type: String,
default: "",
},
defaultValue: {
type: String,
default: "",
},
inputFilteredMap: {
type: Object,
default: () => {},
},
},
created() {
this.value = this.defaultValue;
this.iconColor = this.value.trim() ? true : false;
},
methods: {
showPopover() {
this.$nextTick(() => {
this.$refs.sInput.focus();
});
},
resetData() {
console.log("reset");
this.value = "";
this.visible = false;
this.iconColor = false;
const self = this;
if (this.inputFilteredMap[self.columnProp]) {
delete this.inputFilteredMap[self.columnProp];
}
self.$emit("resetChangeMethod", this.tableColumn, self.columnProp);
},
closeOver() {
this.visible = false;
},
popClick(e) {
// e.stopPropagation()
this.visible = !this.visible;
},
confirm() {
this.visible = false;
if (this.value.trim()) {
this.iconColor = true;
this.inputFilteredMap[this.columnProp] = this.value;
this.$emit(
"filterInputMethod",
this.tableColumn,
this.inputFilteredMap
);
} else {
// 如果搜索input输入为空,等同重置
this.resetData();
}
},
},
directives: {
clickOutside: {
bind(el, binding, vnode) {
function clickHandler(e) {
// 这里判断点击的元素是否是本身,是本身,则返回
if (el.contains(e.target)) {
return false;
}
// 判断指令中是否绑定了函数
if (binding.expression) {
// 如果绑定了函数 则调用那个函数,此处binding.value就是handleClose方法
binding.value(e);
}
}
// 给当前元素绑定个私有变量,方便在unbind中可以解除事件监听
el.__vueClickOutside__ = clickHandler;
document.addEventListener("click", clickHandler);
},
update() {},
unbind(el, binding) {
// 解除事件监听
document.removeEventListener("click", el.__vueClickOutside__);
delete el.__vueClickOutside__;
},
},
},
};
.filter-icon {
font-size: 14px;
color: #909399;
cursor: pointer;
font-weight: 400;
}
.table-header-popover {
padding: 0;
}
.table-header-popover .table-header-popover-template {
margin: 10px;
}
第二步:新建名为 operateTable.vue 的文件
size="small" trigger="click" v-if="options.columnsSelect || options.columnsTreeSelect" class="column-dropdown" > v-if="isInit" size="small" type="primary" plain style="width:70px;" @click="initColumns(true)" >初始化 v-if="isSave" size="small" type="primary" plain style="width:70px;" @click="$emit('saveSettingColumns',checkedList)" >保存
v-if="!options.columnsTreeSelect" v-model="checkedList" style="max-height: 300px" @change="handleCheckedCitiesChange" > -1"> :key="index" class="checkbox" :label="option" > :key="index" class="checkbox" :label="option" >
ref="tree" :check-on-click-node="true" :data="treeColumn" show-checkbox node-key="label" :default-expanded-keys="defaultExpanded" :default-checked-keys="defaultChecked" :props="{ children: 'children', label: 'label', }" @check="checkChange" style="max-height: 300px; overflow-y: auto" >
id="iTable" ref="operateTable" border :data="dataSource" :stripe="options.stripe" :highlight-current-row="options.highlightCurrentRow" :max-height="options.maxHeight" :size="options.size" :fit="options.fit" :show-header="options.showHeader" :empty-text="options.emptyText" :default-sort="options.defaultSort" :row-key="getRowKeys" :default-expand-all="options.defaultExpandAll" :tree-props="options.treeProps" :lazy="options.lazy" :load="load" @cell-mouse-enter="cellMouseEnter" @cell-mouse-leave="cellMouseLeave" @cell-click="cellClick" @cell-dblclick="cellDblclick" @row-click="rowClick" @row-contextmenu="rowContextmenu" @row-dblclick="rowDblclick" @header-click="headerClick" @header-contextmenu="headerContextmenu" @sort-change="sortChange" @select="select" @select-all="selectAll" @selection-change="checkboxSelected" @filter-change="filterChange" > :reserve-selection="options.reserveSelection" :key="0" type="selection" :selectable="selectable" width="40" align="left" v-if="options.showCheckBox" :resizable="false" > ref="fixedColumn" label="操作" align="left" :width="operates.dropDown ? '50' : operates.width" :fixed="operates.fixed" :min-width="operates.minwidth" :resizable="operates.resizable" v-if="operates.list.length > 0" > class="operate-group" v-if="!operates.dropDown && !operates.isText" > v-for="item in operates.list[0] instanceof Array ? operates.list[scope.$index] : operates.list" > v-if="item.type === 'switch'" v-model="scope.row[item.prop]" active-color="#13ce66" @change="item.method(scope.$index, scope.row)" > v-else-if="item.type === 'tooltipIcon'" :enterable="false" effect="light" placement="bottom" > type="primary" plain :icon="item.icon" size="mini" :disabled="item.disabled" @click="item.method(scope.$index, scope.row)" > v-else-if="item.type === 'icon'" type="primary" plain :icon="item.icon" size="mini" :disabled="item.disabled" @click="item.method(scope.$index, scope.row)" > class="operate-group" v-if="!operates.dropDown && operates.isText" > v-for="item in operates.list[0] instanceof Array ? operates.list[scope.$index] : operates.list" > size="small" type="text" :disabled="item.disabled" @click.native.prevent="item.method(scope.$index, scope.row)" >{{ item.label }} >
@command="handleCommand" trigger="hover" placement="bottom-start" > v-for="(item, index) in operates.list[0] instanceof Array ? operates.list[scope.$index] : operates.list" :disabled="item.disabled" :key="index" :command="composeValue(item, scope.row, scope.$index)" >{{ item.label }} >
v-if=" options.columnsSelect || options.columnsTreeSelect ? checkedList.includes(column.label) : true " :prop="column.prop" :key="column.label" :label="column.label" align="left" :width="column.width" :min-width="column.minwidth" :resizable="column.resizable" :sortable="column.sortable" :filters="column.filters" :filter-method="column.filters ? column.filterMethod : undefined" :filtered-value="column.filteredValue" :fixed="column.fixed" :column-key="column.prop" > >{{ column.label }} v-if="column.filterInput" :columnProp="column.prop" :tableColumn="scope.column" :defaultValue="column.defaultValue" :inputFilteredMap="inputFilteredMap" @filterInputMethod="filterInputMethod" @resetChangeMethod="resetChangeMethod" > {{ scope.row[column.prop] }} :column="column" :row="scope.row" :render="column.render" :index="scope.$index" >
import TableHeaderRender from "./TableHeaderRender.vue";
export default {
name: "OperateTable",
props: {
// 表格的数据源
dataSource: {
type: Array,
default: () => [],
},
// 需要展示的列
columns: {
type: Array,
default: () => [{}],
},
// table 表格的控制参数
options: {
type: Object,
default: () => {
return {
stripe: true, // 是否为斑马纹 table
};
},
},
// 操作按钮组 === label: 文本,show:是否显示,icon:按钮图标,disabled:是否禁用,method:回调方法, 等等
operates: {
type: Object,
default: () => {
return {
list: [],
};
},
},
defaultSelectedColumn: {
type: Array,
default: () => [],
},
defaultColumn: {
type: Array,
default: () => [],
},
totalColumn: {
type: Array,
default: () => [],
},
treeColumn: {
type: Array,
default: () => [],
},
defaultChecked: {
type: Array,
default: () => [],
},
defaultExpanded: {
type: Array,
default: () => [],
},
isInit: {
type: Boolean,
default: false
},
isSave: {
type: Boolean,
default: false
}
},
components: {
TableHeaderRender,
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);
},
},
},
data() {
return {
cloumnKeyword:"",//字段关键字搜索
isIndeterminate:true,//全选状态
checkAll:false,//字段全选
checkBoxOptions: [], // 全部表头
checkedList: [], // 选中表头
count: 0, // 用于判断表格是否刚渲染
isCheckBoxSort: false, // 用于判断是否是由自定义数据列引发的排序
// 以下是之前放于vuex中用于记录状态
preCheckedList: [], // 前一次的checkbox
// 排序的状态
sort: {
prop: "",
order: "",
label: "",
},
// 筛选的状态
checkBoxFilteredMap: {},
// input 所有的筛选
inputFilteredMap: {},
// columns label及prop对应的键值对
columnsLabelMap: {}
};
},
watch: {
// 监听defaultColumn,若这个发生变化,代表传入的默认column变化,即数据表格发生了切换
defaultColumn() {
this.initColumns();
},
checkedList() {
// if(this.checkedList.length>0){
// this.$emit("selectedColumn",this.checkedList);
// }
// 处理当点击checkbox显示的是排序列时,恢复排序列的显示
let showLabelArray = this.checkboxShowLabel();
console.log("showLabelArray", showLabelArray);
// if (value.length !== 0) {
// value.map((item) => {
// this.handleStatusRevert(item);
// });
this.columns.map((column, index) => {
this.handleStatusRevert(column, index, showLabelArray);
});
},
},
created() {
this.normalizeColumnsLabelMap();
},
mounted() {
if (!this.options.columnsTreeSelect) {
this.checkedList = this.$props.defaultColumn;
this.checkBoxOptions = this.$props.totalColumn;
} else {
this.checkedList = this.$refs.tree
.getCheckedNodes()
.map((item) => item.label);
}
// 挂载时将defaultSort属性传给vuex
this.handleDefaultSort();
},
// 动态切换表头的时候闪烁是因为表头重新计算高度导致的,以下方法解决此bug
beforeUpdate() {
this.$nextTick(() => {
//在数据加载完,重新渲染表格
this.$refs["operateTable"].doLayout();
});
},
methods: {
//全选字段
handleCheckAllChange(val){
this.checkedList = val ? this.checkBoxOptions : [];
this.isIndeterminate = false;
},
//反选
handleCheckedCitiesChange(value){
let checkedCount = value.length;
this.checkAll = checkedCount === this.checkBoxOptions.length;
this.isIndeterminate = checkedCount > 0 && checkedCount < this.checkBoxOptions.length;
},
//初始化字段
initColumns(flag){
if(flag){
this.checkedList = this.$props.defaultSelectedColumn;
}else{
this.checkedList = this.$props.defaultColumn;
}
this.checkBoxOptions = this.$props.totalColumn;
},
// 处理判断checkbox 点击时,显示的是哪个字段名
checkboxShowLabel() {
// 判断显示的字段是哪个字段,如果是树形,可能有多个,故更改为数组接收
let value = [];
console.log("this.checkedList", this.checkedList);
if (this.count === 0) {
this.saveCheckedList(this.checkedList);
// this.$componentsStore.commit("table/saveCheckedList", this.checkedList);
this.count++;
} else {
if (this.checkedList.length > this.preCheckedList.length) {
for (let index = 0; index < this.checkedList.length; index++) {
if (!this.preCheckedList.includes(this.checkedList[index])) {
value.push(this.checkedList[index]);
}
// if (this.checkedList[index] !== this.preCheckedList[index]) {
// value = this.checkedList[index];
// }
}
}
this.saveCheckedList(this.checkedList);
// this.$componentsStore.commit("table/saveCheckedList", this.checkedList);
}
return value;
},
// 处理sort\filterd由隐藏变为显示状态的恢复
handleStatusRevert(column, index, showLabelArray) {
let compareLabel = column.label;
if (showLabelArray.includes(compareLabel)) {
// 如果是有checkbox 筛选的字段,恢复筛选
let filteredValue =
this.checkBoxFilteredMap[this.columnsLabelMap[compareLabel]];
// 如果是有input 筛选的字段,恢复筛选
let filteredInputValue =
this.inputFilteredMap[this.columnsLabelMap[compareLabel]];
this.columns[index].filteredValue = filteredValue;
this.columns[index].defaultValue = filteredInputValue;
this.$nextTick(() => {
this.$refs.operateTable.store.states.columns.map((column) => {
if (column.filteredValue && column.filteredValue.length) {
this.$refs.operateTable.store.commit("filterChange", {
column,
values: column.filteredValue,
silent: true,
});
}
});
});
} else {
this.columns[index].filteredValue = [];
this.columns[index].defaultValue = "";
}
// 如果是有排序的字段,恢复排序
if (showLabelArray.includes(this.sort.label)) {
this.$nextTick(() => {
//在数据加载完,重新渲染表格
this.isCheckBoxSort = true;
this.$refs.operateTable.sort(this.sort.prop, this.sort.order);
});
}
/**
// 如果是有checkbox 筛选的字段,恢复筛选
let filteredValue = this.checkBoxFilteredMap[this.columnsLabelMap[value]];
// 如果是有input 筛选的字段,恢复筛选
let filteredInputValue = this.inputFilteredMap[
this.columnsLabelMap[value]
];
for (let i = 0; i < this.columns.length; i++) {
if (this.columns[i].label === value) {
this.columns[i].filteredValue = filteredValue;
this.columns[i].defaultValue = filteredInputValue;
this.$nextTick(() => {
this.$refs.operateTable.store.states.columns.map((column) => {
if (column.filteredValue && column.filteredValue.length) {
console.log("!11");
this.$refs.operateTable.store.commit("filterChange", {
column,
values: column.filteredValue,
silent: true,
});
}
});
});
} else {
this.columns[i].filteredValue = [];
this.columns[i].defaultValue = "";
}
}
// for (let i = 0; i < this.columns.length; i++) {
// if (this.columns[i].label === value) {
// this.columns[i].defaultValue = filteredInputValue;
// } else {
// this.columns[i].defaultValue = "";
// }
// }
// 如果是有排序的字段,恢复排序
if (value === this.sort.label) {
this.$nextTick(() => {
//在数据加载完,重新渲染表格
this.isCheckBoxSort = true;
this.$refs.operateTable.sort(this.sort.prop, this.sort.order);
});
}
*/
},
// 处理生成columns 的label prop键值对
normalizeColumnsLabelMap() {
this.columns.map((column) => {
this.columnsLabelMap[column.label] = column.prop;
});
},
filterInputMethod(column, inputFilteredMap) {
console.log("column, inputFilteredMap", column, inputFilteredMap);
this.inputFilteredMap = inputFilteredMap;
this.$emit("filterInputMethod", column, inputFilteredMap);
},
resetChangeMethod(tableColumn, columnProp) {
this.$emit("resetChangeMethod", tableColumn, this.inputFilteredMap);
},
cellMouseEnter(row, column, cell, event) {
this.$emit("cell-mouse-enter", row, column, cell, event);
},
cellMouseLeave(row, column, cell, event) {
this.$emit("cell-mouse-leave", row, column, cell, event);
},
cellClick(row, column, cell, event) {
this.$emit("cell-click", row, column, cell, event);
},
cellDblclick(row, column, cell, event) {
this.$emit("cell-dblclick", row, column, cell, event);
},
rowClick(row, column, event) {
this.$emit("row-click", row, column, event);
},
rowContextmenu(row, column, event) {
this.$emit("row-contextmenu", row, column, event);
},
rowDblclick(row, column, event) {
this.$emit("row-dblclick", row, column, event);
},
headerClick(column, event) {
this.$emit("header-click", column, event);
},
headerContextmenu(column, event) {
this.$emit("header-contextmenu", column, event);
},
sortChange(sortObj) {
this.changeSort(sortObj);
// this.$componentsStore.commit("table/changeSort", sortObj);
if (!this.isCheckBoxSort) {
this.$emit("sort-change", sortObj);
}
// 还原isCheckBoxSort
this.isCheckBoxSort = false;
},
handleDefaultSort() {
if (this.options.defaultSort !== undefined) {
let column = { label: "" };
// for (let index = 0; index < this.columns.length; index++) {
// if (this.columns[index].prop === this.options.defaultSort.prop) {
// column.label = this.columns[index].label;
// break;
// }
// }
column.label = this.columnsLabelMap[this.options.defaultSort.prop];
this.changeSort({
...this.options.defaultSort,
column,
});
}
},
// 点击操作的下拉项目后触发事件
handleCommand(command) {
if (command.method) {
command.method(command.index, command.row, command.label);
}
},
// 点击dropDown传参方法
composeValue(item, row, index) {
return {
label: item.label,
row: row,
index: index,
method: item.method,
};
},
select(selection, row) {
this.$emit("select", selection, row);
},
selectAll(selection) {
this.$emit("select-all", selection);
},
checkboxSelected(selection) {
this.$emit("selection-change", selection);
},
selectable(row, index) {
let data = true;
this.$emit("selectable", row, index, (val) => {
data = val;
});
return data;
},
getRowKeys(row) {
let data;
for (let index = 0; index < this.dataSource.length; index++) {
if (row === this.dataSource[index]) {
data = index;
break;
}
}
// this.dataSource.map((item, index) => {
// if (row === item) {
// data = index;
// }
// });
this.$emit("row-key", row, (val) => {
data = val;
});
return data;
},
load(tree, treeNode, resolve) {
this.$emit("load", tree, treeNode, resolve);
},
// 记录表格总的筛选状态,用于列显示隐藏时保存checkbox状态
filterChange(filters) {
let currKey = Object.keys(filters)[0];
if (filters[currKey].length === 0) {
delete this.checkBoxFilteredMap[currKey];
} else {
this.checkBoxFilteredMap[currKey] = filters[currKey];
}
this.$emit("filter-change", filters);
},
checkChange(nodeObj, checkObj) {
this.checkedList = checkObj.checkedNodes.map((item) => {
return item.label;
});
},
// 之前写在vuex里的方法
changeSort(sort) {
this.sort.prop = sort.prop;
this.sort.order = sort.order;
this.sort.label = sort.column.label;
},
saveCheckedList(preCheckedList) {
this.preCheckedList = preCheckedList;
},
},
};
.operateTable{
position: relative;
width: 100%;
}
.operateTable .column-dropdown{
position: absolute;
right: 0px;
top: -42px;
z-index: 99;
}
.caoz_ft_warp{
text-align: center;
}
.caoz_ft_warp .el-input.is-active .el-input__inner, .caoz_ft_warp .el-input__inner:focus{
border-color: #9A4B9B;
outline: 0;
}
.el-checkbox__input.is-checked .el-checkbox__inner,.el-checkbox__input.is-indeterminate .el-checkbox__inner {
background-color: #9a4b9b;
border-color: #9a4b9b;
}
.el-checkbox__inner:hover {
border-color: #9A4B9B;
}
.el-checkbox__input.is-focus .el-checkbox__inner{
border-color: #9A4B9B;
}
.el-checkbox__input.is-checked+.el-checkbox__label {
color: #9A4B9B;
}
.checkboxScorll .el-checkbox__input.is-checked + .el-checkbox__label {
color: #666;
}
.column-dropdown .el-button{margin-right: 0 !important;min-width:0;}
.column-dropdown .el-button:focus, .el-button:hover{
color: #9A4B9B;
border-color: #e1c9e1;
background-color: #f5edf5;
}
.checkboxScorll {
max-height: 300px;
overflow-y: auto;
}
.checkboxScorll .checkbox {
display: block;
margin-top: 10px;
padding-left: 20px;
}
第三步:在页面中引入operateTable并使用
v-loading="loading" :dataSource="operateTableData" :columns="operateTableColumns" :options="operateTableOption" :defaultColumn="defaultColumns" :totalColumn="totalColumns" :defaultSelectedColumn="defaultSelectedColumn" @sort-change="sortChange" @saveSettingColumns="saveSettingColumns" >
import operateTable from "./components/operateTable.vue";
export default {
name: "",
components: {
operateTable,
},
data() {
return {
loading: false,
operateTableData: [
{
date: "2016-05-02",
name: "王小虎",
address: "上海市普陀区金沙江路 1518 弄",
},
{
date: "2016-05-04",
name: "王小虎",
address: "上海市普陀区金沙江路 1517 弄",
},
{
date: "2016-05-01",
name: "王小虎",
address: "上海市普陀区金沙江路 1519 弄",
},
{
date: "2016-05-03",
name: "王小虎",
address: "上海市普陀区金沙江路 1516 弄",
},
],
operateTableColumns: [
{
prop: "action",
label: "操作",
width: 100,
fixed: true,
render: (h, params) => {
return h(
"div",
{
class: "operate-group",
},
[
h(
"el-tooltip",
{
props: {
content: "处理",
placement: "bottom",
enterable: false,
effect: "light",
},
class: "item",
},
[
h("i", {
props: {},
class: "el-icon-edit",
on: {
click: () => {
console.log(params.row);
},
},
}),
]
),
]
);
},
},
{
prop: "date",
label: "日期",
minwidth: 150,
sortable: "custom",
},
{
prop: "name",
label: "姓名",
minwidth: 150,
sortable: "custom",
},
{
prop: "address",
label: "地址",
minwidth: 150,
sortable: "custom",
},
],
operateTableOption: {
stripe: true, // 是否为斑马纹 table
highlightCurrentRow: true, // 是否要高亮当前行
columnsSelect: true,
maxHeight: 300,
},
defaultColumns: ["操作", "日期", "姓名", "地址"],
totalColumns: ["操作", "日期", "姓名", "地址"],
//所有用户默认勾选的列 用于初始化
defaultSelectedColumn: [],
};
},
methods: {
//表头排序
sortChange(column, prop, order) {
if (column.order === "ascending") {
this.orderfield = column.prop;
this.orderby = "ASC";
} else if (column.order === "descending") {
this.orderfield = column.prop;
this.orderby = "DESC";
} else {
this.orderfield = "";
this.orderby = "";
}
// this.getTabldHandle();
},
//保存自定义字段
saveSettingColumns(data) {
console.log(data);
},
},
mounted() {},
};
.tableView {
width: 100%;
height: 100%;
}
.content {
padding: 60px;
}
.disableIcon {
color: #c0c4cc;
cursor: not-allowed;
}
精彩内容
发表评论