@[TOC]
- elementUI版本
"element-ui": "^2.15.0"
element官网- 本次封装移植了elementUI上的所有方法,只需要通过columns去配置即可
- 本次二次封装中集成了,表格分页,前端下载(支持下载当前页和指定数据量数据),表格列筛选功能,如只需表格功能请移步基于element-Ui 2.15.0 table二次封装表格
- 使用时注意文件路径。
- 文章结尾会有使用示例
- 使用插件
"xlsx": "^0.16.9"
;"file-saver": "^2.0.5"
用于文件下载
主要封装tables文件
tables.vue
<template>
<!--表格默认开启悬浮提示,及文字剧中-->
<div class="data-table-box-">
<el-table
ref="tableDataRef"
:data="tableData"
style="width: 100%;border-radius:4px;padding:0 20px 20px"
:height="height"
v-loading="loadingTb"
:max-height="maxHeight"
:stripe="stripe"
:border="border"
:size="size"
:fit="fit"
:show-header="showHeader"
:highlight-current-row="highlightCurrentRow"
:current-row-key="currentRowKey"
:row-class-name="rowClassName"
:row-style="rowStyle"
:cell-class-name="cellClassName"
:cell-style="cellStyle"
:header-row-class-name="headerRowClassName"
:header-row-style="headerRowStyle"
:header-cell-class-name="headerCellClassName"
:header-cell-style="headerCellStyle"
:row-key="rowKey"
:empty-text="emptyText"
:expand-row-keys="expandRowKeys"
:default-sort="defaultSort"
:tooltip-effect="tooltipEffect"
:show-summary="showSummary"
:sum-text="sumText"
:summary-method="summaryMethod"
:span-method="spanMethod"
:select-on-indeterminate="selectOnIndeterminate"
:indent="indent"
:lazy="lazy"
:load="load"
:tree-props="treeProps"
@select="onSelect"
@select-all="onSelectAll"
@selection-change="onSelectionChange"
@cell-mouse-enter="onCellMouseEnter"
@cell-mouse-leave="onCellMouseLeave"
@cell-click="onCellClick"
@cell-dblclick="onCellDblclick"
@row-click="onRowClick"
@row-contextmenu="onRowContextmenu"
@row-dblclick="onRowDblclick"
@header-click="onHeaderClick"
@header-contextmenu="onHeaderContextmenu"
@sort-change="onSortChange"
@filter-change="onFilterChange"
@current-change="onCurrentChange"
@header-dragend="onHeaderDragend"
@expand-change="onExpandChange"
>
<template v-for="(col, index) in insideColumns">
<!-- 操作列/自定义列 -->
<slot v-if="col.slot" :name="col.prop"></slot>
<!-- 普通列 -->
<el-table-column
v-else
:key="index"
:prop="col.prop"
:label="col.label"
:width="col.width"
:formatter="col.formatter"
:type="col.type"
:index="col.index"
:column-key="col.columnKey"
:min-width="col.minWidth"
:fixed="col.fixed"
:render-header="col.renderHeader"
:sortable="col.sortable"
:sort-method="col.sortMethod"
:sort-by="col.sortBy"
:sort-orders="col.sortOrders"
:resizable="col.resizable"
:show-overflow-tooltip="col.showOverflowTooltip"
:align="col.align"
:header-align="col.headerAlign"
:class-name="col.className"
:label-class-name="col.labelClassName"
:selectable="col.selectable"
:reserve-selection="col.reserveSelection"
:filters="col.filters"
:filter-placement="col.filterPlacement"
:filter-multiple="col.filterMultiple"
:filtered-value="col.filteredValue"
>
<template slot-scope="scope">
<!-- render渲染-->
<exSlot
v-if="col.render"
:column="col"
:row="scope.row"
:render="col.render"
:index="index"
></exSlot>
<!-- 直接渲染-->
<span v-else>{{ scope.row[col.prop] }}</span>
</template>
</el-table-column>
</template>
</el-table>
<div class="jb-table-footer-">
<el-row>
<el-col :span="21" v-if="showPage">
<el-pagination
background
:layout="layout"
:page-size="pageSizeTb"
:total="totalTb"
:page-sizes="pageSizes"
:current-page="pageTb"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
>
</el-pagination>
</el-col>
<el-col :offset="showPage ? 0 : 20" :span="1" v-if="selectColumns">
<el-dropdown trigger="click" placement="top" :hide-on-click="false">
<el-button type="primary">
筛选
</el-button>
<el-dropdown-menu slot="dropdown">
<el-row
style="border-bottom: solid 1px #C0C4CC;margin-bottom: 10px;padding-bottom: 10px"
>
<el-col :span="20" :offset="4">列筛选</el-col>
</el-row>
<el-checkbox-group
v-model="tableColumnsChecked"
@change="tableColumns"
>
<el-dropdown-item v-for="(col, index) in columns" :key="index">
<el-checkbox
v-if="col.label && !col.slot"
:label="col.prop"
>{{ col.label }}</el-checkbox
>
</el-dropdown-item>
</el-checkbox-group>
</el-dropdown-menu>
</el-dropdown>
</el-col>
<el-col :span="2" v-if="exportExcel">
<el-dropdown trigger="click" placement="top" :hide-on-click="false">
<el-button type="primary">
导出
</el-button>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item>
<el-button type="primary" @click="exprotAll" v-click-disabled
>全 量 导 出</el-button
>
</el-dropdown-item>
<el-row
style="border-bottom: solid 1px #C0C4CC;margin-bottom: 10px;padding-bottom: 10px"
>
</el-row>
<el-dropdown-item>
<el-button type="primary" @click="exportPage" v-click-disabled
>导出当前页</el-button
>
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</el-col>
</el-row>
</div>
</div>
</template>
<script>
import request from "@/utils/request";
import { deepCopy } from "@/utils/utils";
var exSlot = {
functional: true,
props: {
row: Object,
render: Function,
index: Number,
column: {
type: Object,
default: null
}
},
render: (h, data) => {
const params = {
row: data.props.row,
index: data.props.index
};
if (data.props.column) params.column = data.props.column;
return data.props.render(h, params);
}
};
export default {
name: "tables-test",
components: { exSlot },
data() {
return {
tableColumnsChecked: [],
insideColumns: [],
loadingTb: false,
tableData: [],
pageSizeTb: 10,
pageTb: 1,
totalTb: 0
};
},
props: {
maxHeight: {
type: String || Number
},
stripe: {
type: Boolean,
default: true
},
border: {
type: Boolean,
default: false
},
size: {
type: String,
default: "medium" //medium / small / mini
},
fit: {
type: Boolean,
default: true
},
showHeader: {
type: Boolean,
default: true
},
highlightCurrentRow: {
type: Boolean,
default: false
},
currentRowKey: {
type: [String, Number]
},
rowClassName: {
type: [Function, String]
},
rowStyle: {
type: [Function, Object]
},
cellClassName: {
type: [Function, String]
},
cellStyle: {
type: [Function, Object]
},
headerRowClassName: {
type: [Function, String]
},
headerRowStyle: {
type: [Function, Object]
},
headerCellClassName: {
type: [Function, String]
},
headerCellStyle: {
type: [Object, Function],
default() {
return {
background: "#f2f2f2",
fontWeight: "400",
color: "#555555"
};
}
},
rowKey: {
type: [Function, String]
},
expandRowKeys: {
type: [Function, String]
},
emptyText: {
type: String,
default: ""
},
defaultSort: {
type: Object,
default() {
return {};
}
},
tooltipEffect: {
type: String,
default: ""
},
showSummary: {
type: Boolean,
default: false
},
sumText: {
type: String,
default: ""
},
summaryMethod: {
type: Function
},
spanMethod: {
type: Function
},
selectOnIndeterminate: {
type: Boolean,
default: true
},
indent: {
type: Number,
default: 16
},
lazy: {
type: Boolean
},
load: {
type: Function
},
treeProps: {
type: Object,
default() {
return {
hasChildren: "hasChildren",
children: "children"
};
}
},
resTableData: {
type: Array,
default() {
return [];
}
},
height: {
type: String,
default: "620px"
},
loading: {
type: Boolean,
default: false
},
columns: {
type: Array,
default() {
return [];
}
},
axiosData: {
type: Object,
default() {
return {};
}
},
axiosUrl: {
type: String,
default: ""
},
axiosMethod: {
type: String,
default: "post"
},
showPage: {
// 是否显示页码
type: Boolean,
default: true
},
pageSizes: {
type: Array,
default() {
return [10, 30, 50, 100];
}
},
layout: {
type: String,
default: "total, prev, pager, next, sizes, jumper"
},
total: {
type: Number,
default: 0
},
pageSize: {
type: Number,
default: 10
},
page: {
type: Number,
default: 1
},
exportExcel: {
//是否显示导出按钮
type: Boolean,
default: true
},
exportExcelTitle: {
type: String,
default: "张家口涉奥保电"
},
selectColumns: {
//是否显示列筛选
type: Boolean,
default: true
},
},
created() {
this.tableColumnsChecked = this.getHead(this.columns);
this.tableColumns();
},
methods: {
outSideQuery() {
this.tableData = deepCopy(this.resTableData);
this.totalTb = this.total;
this.pageSizeTb = this.pageSize;
this.pageTb = this.page;
},
queryData(page, pageSize) {
let queryData = deepCopy(this.axiosData);
queryData["page"] = page || this.pageTb;
queryData["pageSize"] = pageSize || this.pageSizeTb;
this.loadingTb = true;
request({
url: this.axiosUrl,
method: this.axiosMethod,
data: queryData
})
.then(res => {
this.loadingTb = false;
this.tableData = res.data.list;
this.totalTb = res.data.total;
this.pageSizeTb = res.data.pageSize;
this.pageTb = res.data.page;
})
.catch(() => {
this.loadingTb = false;
});
},
handleSizeChange(val) {
this.pageSizeTb = val;
this.$emit("handleSizeChange", this.pageSizeTb);
this.queryData(this.pageTb, this.pageSizeTb);
},
handleCurrentChange(val) {
this.pageTb = val;
this.$emit("handleCurrentChange", this.pageTb);
this.queryData(this.pageTb, this.pageSizeTb);
},
tableColumns() {
let columns = this.columns;
this.insideColumns = [];
columns.forEach(col => {
if (col.slot) {
this.insideColumns.push(col);
}
this.tableColumnsChecked.forEach(che => {
// 默认居中
if (!col.align) {
col.align = "center";
}
// 超出悬浮提示
if (!col.showOverflowTooltip) {
col.showOverflowTooltip = true;
}
if (che === col.prop) {
this.insideColumns.push(col);
}
});
});
// 表格错位
this.$nextTick(() => {
this.$refs.tableDataRef.doLayout();
});
},
getHead(item) {
let tmp = [];
item.forEach(it => {
if (it.show !== false && !it.slot) {
tmp.push(it.prop);
}
});
return tmp;
},
getExporthead(data) {
let head = {};
let headTitle = [];
let headKey = [];
data.forEach(item => {
if (item.label && item.prop) {
headKey.push(item.prop);
headTitle.push(item.label);
}
});
head.headKey = headKey;
head.headTitle = headTitle;
return head;
},
exprotAll() {
this.loadingTb = true;
let queryData = deepCopy(this.axiosData);
queryData["page"] = 1;
queryData["pageSize"] = 20000; //下载指定条数数据(全量下载)
request({
url: this.axiosUrl,
method: this.axiosMethod,
data: queryData
})
.then(res => {
this.loadingTb = false;
this.exprotExcel(res.data.list);
})
.catch(() => {
this.loadingTb = false;
});
},
exportPage() {
this.exprotExcel(this.tableData);
},
exprotExcel(d) {
import("@/libs/Export2Excel").then(excel => {
const tHeader = this.getExporthead(this.columns).headTitle;
const filterVal = this.getExporthead(this.columns).headKey;
const data = this.formatJson(filterVal, d);
const title =
this.exportExcelTitle
excel.export_json_to_excel({
header: tHeader,
data,
filename: title,
autoWidth:true,
bookType: "xlsx"
});
});
},
formatJson(filterVal, jsonData) {
return jsonData.map(v =>
filterVal.map(j => {
if (j === "timestamp") {
// return parseTime(v[j])
} else {
return v[j];
}
})
);
},
onSelect(selection, row) {
this.$emit("select", selection, row);
},
onSelectAll(selection) {
this.$emit("select-all", selection);
},
onSelectionChange(selection) {
this.$emit("selection-change", selection);
},
onCellMouseEnter(row, column, cell, event) {
this.$emit("cell-mouse-enter", row, column, cell, event);
},
onCellMouseLeave(row, column, cell, event) {
this.$emit("cell-mouse-leave", row, column, cell, event);
},
onCellClick(row, column, cell, event) {
this.$emit("cell-click", row, column, cell, event);
},
onCellDblclick(row, column, cell, event) {
this.$emit("cell-dblclick", row, column, cell, event);
},
onRowClick(row, column, event) {
this.$emit("row-click", row, column, event);
},
onRowContextmenu(row, column, event) {
this.$emit("row-contextmenu", row, column, event);
},
onRowDblclick(row, column, event) {
this.$emit("row-dblclick", row, column, event);
},
onHeaderClick(column, event) {
this.$emit("header-click", column, event);
},
onHeaderContextmenu(column, event) {
this.$emit("header-contextmenu", column, event);
},
onSortChange({ column, prop, order }) {
this.$emit("sort-change", { column, prop, order });
},
onFilterChange(filters) {
this.$emit("filter-change", filters);
},
onCurrentChange(currentRow, oldCurrentRow) {
this.$emit("current-change", currentRow, oldCurrentRow);
},
onHeaderDragend(newWidth, oldWidth, column, event) {
this.$emit("header-dragend", newWidth, oldWidth, column, event);
},
onExpandChange(row, expand) {
this.$emit("expand-change", row, expand);
}
},
watch: {
loading(newV) {
this.loadingTb = newV;
},
resTableData: {
handler(newVal) {
this.tableData = deepCopy(newVal);
},
deep: true
}
}
};
</script>
<style scoped lang="scss">
.data-table-box- {
box-shadow: 0 3px 11px rgba(27, 95, 230, 0.149019607843137);
border-radius: $--border-radius;
}
.jb-table-footer- {
background-color: #fff;
width: calc(100% - 10px);
padding-top: 10px;
padding-left: 10px;
padding-bottom: 10px;
border-bottom-left-radius: $--border-radius;
border-bottom-right-radius: $--border-radius;
}
</style>
excel前端导出文件
excel表格下载封装使用 支持列/行合并 宽度自适应....(本js封装来源网络,具体使用请异步)
src/libs/Export2Excel.js
/* eslint-disable */
import { saveAs } from 'file-saver'
import XLSX from 'xlsx'
function generateArray(table) {
var out = [];
var rows = table.querySelectorAll('tr');
var ranges = [];
for (var R = 0; R < rows.length; ++R) {
var outRow = [];
var row = rows[R];
var columns = row.querySelectorAll('td');
for (var C = 0; C < columns.length; ++C) {
var cell = columns[C];
var colspan = cell.getAttribute('colspan');
var rowspan = cell.getAttribute('rowspan');
var cellValue = cell.innerText;
if (cellValue !== "" && cellValue == +cellValue) cellValue = +cellValue;
//Skip ranges
ranges.forEach(function (range) {
if (R >= range.s.r && R <= range.e.r && outRow.length >= range.s.c && outRow.length <= range.e.c) {
for (var i = 0; i <= range.e.c - range.s.c; ++i) outRow.push(null);
}
});
//Handle Row Span
if (rowspan || colspan) {
rowspan = rowspan || 1;
colspan = colspan || 1;
ranges.push({
s: {
r: R,
c: outRow.length
},
e: {
r: R + rowspan - 1,
c: outRow.length + colspan - 1
}
});
};
//Handle Value
outRow.push(cellValue !== "" ? cellValue : null);
//Handle Colspan
if (colspan)
for (var k = 0; k < colspan - 1; ++k) outRow.push(null);
}
out.push(outRow);
}
return [out, ranges];
};
function datenum(v, date1904) {
if (date1904) v += 1462;
var epoch = Date.parse(v);
return (epoch - new Date(Date.UTC(1899, 11, 30))) / (24 * 60 * 60 * 1000);
}
function sheet_from_array_of_arrays(data, opts) {
var ws = {};
var range = {
s: {
c: 10000000,
r: 10000000
},
e: {
c: 0,
r: 0
}
};
for (var R = 0; R != data.length; ++R) {
for (var C = 0; C != data[R].length; ++C) {
if (range.s.r > R) range.s.r = R;
if (range.s.c > C) range.s.c = C;
if (range.e.r < R) range.e.r = R;
if (range.e.c < C) range.e.c = C;
var cell = {
v: data[R][C]
};
if (cell.v == null) continue;
var cell_ref = XLSX.utils.encode_cell({
c: C,
r: R
});
if (typeof cell.v === 'number') cell.t = 'n';
else if (typeof cell.v === 'boolean') cell.t = 'b';
else if (cell.v instanceof Date) {
cell.t = 'n';
cell.z = XLSX.SSF._table[14];
cell.v = datenum(cell.v);
} else cell.t = 's';
ws[cell_ref] = cell;
}
}
if (range.s.c < 10000000) ws['!ref'] = XLSX.utils.encode_range(range);
return ws;
}
function Workbook() {
if (!(this instanceof Workbook)) return new Workbook();
this.SheetNames = [];
this.Sheets = {};
}
function s2ab(s) {
var buf = new ArrayBuffer(s.length);
var view = new Uint8Array(buf);
for (var i = 0; i != s.length; ++i) view[i] = s.charCodeAt(i) & 0xFF;
return buf;
}
export function export_table_to_excel(id) {
var theTable = document.getElementById(id);
var oo = generateArray(theTable);
var ranges = oo[1];
/* original data */
var data = oo[0];
var ws_name = "SheetJS";
var wb = new Workbook(),
ws = sheet_from_array_of_arrays(data);
/* add ranges to worksheet */
// ws['!cols'] = ['apple', 'banan'];
ws['!merges'] = ranges;
/* add worksheet to workbook */
wb.SheetNames.push(ws_name);
wb.Sheets[ws_name] = ws;
var wbout = XLSX.write(wb, {
bookType: 'xlsx',
bookSST: false,
type: 'binary'
});
saveAs(new Blob([s2ab(wbout)], {
type: "application/octet-stream"
}), "test.xlsx")
}
export function export_json_to_excel({
multiHeader = [],
header,
data,
filename,
merges = [],
autoWidth = true,
bookType = 'xlsx'
} = {}) {
/* original data */
filename = filename || 'excel-list'
data = [...data]
data.unshift(header);
for (let i = multiHeader.length - 1; i > -1; i--) {
data.unshift(multiHeader[i])
}
var ws_name = "SheetJS";
var wb = new Workbook(),
ws = sheet_from_array_of_arrays(data);
if (merges.length > 0) {
if (!ws['!merges']) ws['!merges'] = [];
merges.forEach(item => {
ws['!merges'].push(XLSX.utils.decode_range(item))
})
}
if (autoWidth) {
/*设置worksheet每列的最大宽度*/
const colWidth = data.map(row => row.map(val => {
/*先判断是否为null/undefined*/
if (val == null) {
return {
'wch': 10
};
}
/*再判断是否为中文*/
else if (val.toString().charCodeAt(0) > 255) {
return {
'wch': val.toString().length * 2
};
} else {
return {
'wch': val.toString().length
};
}
}))
/*以第一行为初始值*/
let result = colWidth[0];
for (let i = 1; i < colWidth.length; i++) {
for (let j = 0; j < colWidth[i].length; j++) {
if (result[j]['wch'] < colWidth[i][j]['wch']) {
result[j]['wch'] = colWidth[i][j]['wch'];
}
}
}
ws['!cols'] = result;
}
/* add worksheet to workbook */
wb.SheetNames.push(ws_name);
wb.Sheets[ws_name] = ws;
var wbout = XLSX.write(wb, {
bookType: bookType,
bookSST: false,
type: 'binary'
});
saveAs(new Blob([s2ab(wbout)], {
type: "application/octet-stream"
}), `${filename}.${bookType}`);
}
对象深拷贝
/utils/utils
/**
* 对象深拷贝
* @param obj
*/
export const deepCopy = obj => {
let result = Array.isArray(obj) ? [] : {};
for (let key in obj) {
// eslint-disable-next-line no-prototype-builtins
if (obj.hasOwnProperty(key)) {
if (typeof obj[key] === "object") {
result[key] = deepCopy(obj[key]); // 递归复制
} else {
result[key] = obj[key];
}
}
}
return result;
};
请求方式
/utils/request
import axios from "axios";
import { MessageBox, Message } from "element-ui";
import store from "@/store";
import { getToken } from "@/utils/auth";
import { Base64 } from "js-base64";
import con from "@/config/index";
// create an axios instance
let base;
if (process.env.VUE_APP_MODE === "development") {
base = con.dev;
} else {
base = con.pro;
}
console.log('base=====>',base);
const service = axios.create({
baseURL: base, // url = base url + request url
// withCredentials: true, // send cookies when cross-domain requests
timeout: con.timeout * 1000, // request timeout
headers:{
'Content-Type': 'application/json'
}
});
// request interceptor
service.interceptors.request.use(
config => {
config.headers["Authorization"] = `Basic ${Base64.encode(
`${con.clientId}:${con.clientSecret}`
)}`;
if (getToken()) {
config.headers["Blade-Auth"] = "bearer " + getToken(); // 让每个请求携带token--['Authorization']为自定义key 请根据实际情况自行修改
}
return config;
},
error => {
// do something with request error
console.log(error); // for debug
return Promise.reject(error);
}
);
// response interceptor
service.interceptors.response.use(
/**
* If you want to get http information such as headers or status
* Please return response => response
*/
/**
* Determine the request status by custom code
* Here is just an example
* You can also judge the status by HTTP Status Code
*/
response => {
const res = response.data;
// if the custom code is not 20000, it is judged as an error.
if (res.code !== 200) {
Message({
message: res.message || "Error",
type: "error",
duration: 5 * 1000
});
// 50008: Illegal token; 50012: Other clients logged in; 50014: Token expired;
if (res.code === 500) {
// to re-login
MessageBox.confirm(
"You have been logged out, you can cancel to stay on this page, or log in again",
"Confirm logout",
{
confirmButtonText: "Re-Login",
cancelButtonText: "Cancel",
type: "warning"
}
).then(() => {
store.dispatch("user/resetToken").then(() => {
location.reload();
});
});
}
return Promise.reject(new Error(res.message || "Error"));
} else {
return res;
}
},
error => {
console.log("err" + error); // for debug
Message({
message: error.message,
type: "error",
duration: 5 * 1000
});
return Promise.reject(error);
}
);
export default service;
使用方式
1、传入url表格组件自动处理请求数据
<template>
<my-tables
ref="tablesRef"
:axios-outside="false"
:axios-data="axiosData"
:axios-url="axiosUrl"
:columns="columns"
>
<el-table-column
slot="operation"
label="操作"
align="center"
fixed="right"
>
<template slot-scope="scope">
<div class="operation">
<a class="pointer" @click="find(scope.row)">查看</a>
<a class="pointer" style="margin: 0 14px" @click="edit(scope.row)"
>编辑</a
>
<a class="pointer" @click="del(scope.row)">删除</a>
</div>
</template>
</el-table-column>
</my-tables>
</template>
<script>
export default {
name: "",
data() {
return {
columns: [
{
type: "selection",
width: "55"
},
{
label: "供电单位",
prop: "orgName"
},
{
label: "所属变电站",
prop: "subsName"
},
{
label: "所属线路",
prop: "lineName"
},
{
label: "台区名称",
prop: "tgName",
width: "200px"
},
{
label: "台区编号",
prop: "tgNo"
},
{
label: "用户名称",
prop: "consName"
},
{
label: "用户编号",
prop: "consNo"
},
{
label: "保电时间",
prop: "sDate",
width: "200px"
},
{
label: "保电原因",
prop: "signboard"
},
{
label: "操作",
slot: "operation",
prop: "operation"
}
],
axiosData: {},
axiosUrl: "",
},
methods: {
query() {
this.axiosUrl = "api/system/articleList";
this.$nextTick(() => {
this.$refs.tablesRef.queryData();
});
},
}
}
2、数据在外部请求
- 主要通过传递请求数据,resTableData
- 总条数total,
当前页条数pageSize
第几页 page- 调用 this.$refs.tablesRef.outSideQuery();
- 获取组件第几页详情见elementUi官网