1、需要提前安装拖拽插件
npm install awe-dnd --save
// 导出功能
import { export_array_to_excel2 } from '@/utils/tableToExcel'
export default {
methods: {
// 公共的导出方法
//第一个参数:所有的表头字段
//第二参数:选择展示的表头字段
//第三参数:表格数据
//第四个参数:导出excel表格的名字
downloadExcel(tableColumns, visibleCol, data, fileName) {
let titleArr = []
let keyArr = []
if (!visibleCol) {
titleArr = tableColumns.map(item => {
return item.label
})
keyArr = tableColumns.map(item => {
return item.prop
})
} else {
for (const key in visibleCol) {
if (visibleCol[key].visible) {
titleArr.push(visibleCol[key].label)
}
}
tableColumns.map(item => {
if (titleArr.indexOf(item.label) !== -1) {
keyArr.push(item.prop)
}
})
}
const param = {
title: titleArr,
key: keyArr,
data: data,
autoWidth: true,
filename: fileName
}
export_array_to_excel2(param)
}
}
}
2、tableToExcel.js 文件代码如下:
/* eslint-disable */
import XLSX from 'xlsx';
function auto_width(ws, data) {
/*set worksheet max width per col*/
const colWidth = data.map(row => row.map(val => {
/*if null/undefined*/
if (val == null) {
return {
'wch': 10
};
}
/*if chinese*/
else if (val.toString().charCodeAt(0) > 255) {
return {
'wch': val.toString().length * 2
};
} else {
return {
'wch': val.toString().length
};
}
}))
/*start in the first row*/
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;
}
function json_to_array(key, jsonData) {
return jsonData.map(v => key.map(j => {
return v[j]
}));
}
// fix data,return string
function fixdata(data) {
let o = ''
let l = 0
const w = 10240
for (; l < data.byteLength / w; ++l) o += String.fromCharCode.apply(null, new Uint8Array(data.slice(l * w, l * w + w)))
o += String.fromCharCode.apply(null, new Uint8Array(data.slice(l * w)))
return o
}
// get head from excel file,return array
function get_header_row(sheet) {
const headers = []
const range = XLSX.utils.decode_range(sheet['!ref'])
let C
const R = range.s.r /* start in the first row */
for (C = range.s.c; C <= range.e.c; ++C) { /* walk every column in the range */
var cell = sheet[XLSX.utils.encode_cell({
c: C,
r: R
})] /* find the cell in the first row */
var hdr = 'UNKNOWN ' + C // <-- replace with your desired default
if (cell && cell.t) hdr = XLSX.utils.format_cell(cell)
headers.push(hdr)
}
return headers
}
export const export_table_to_excel = (id, filename) => {
const table = document.getElementById(id);
const wb = XLSX.utils.table_to_book(table);
XLSX.writeFile(wb, filename);
/* the second way */
// const table = document.getElementById(id);
// const wb = XLSX.utils.book_new();
// const ws = XLSX.utils.table_to_sheet(table);
// XLSX.utils.book_append_sheet(wb, ws, filename);
// XLSX.writeFile(wb, filename);
}
export const export_json_to_excel = ({
data,
key,
title,
filename,
autoWidth
}) => {
const wb = XLSX.utils.book_new();
data.unshift(title);
const ws = XLSX.utils.json_to_sheet(data, {
header: key,
skipHeader: true
});
if (autoWidth) {
const arr = json_to_array(key, data);
auto_width(ws, arr);
}
XLSX.utils.book_append_sheet(wb, ws, filename);
XLSX.writeFile(wb, filename + '.xlsx');
}
// 导出不带有汉字标题的execel内容
export const export_array_to_excel = ({
key,
data,
title,
filename,
autoWidth
}) => {
const wb = XLSX.utils.book_new();
const arr = json_to_array(key, data);
arr.unshift(title)
const ws = XLSX.utils.aoa_to_sheet(arr);
if (autoWidth) {
auto_width(ws, arr);
}
XLSX.utils.book_append_sheet(wb, ws, filename);
XLSX.writeFile(wb, filename + '.xlsx');
}
// 导出带有汉字标题的execel内容
export const export_array_to_excel2 = ({
key,
data,
title,
filename,
autoWidth
}) => {
const wb = XLSX.utils.book_new();
const arr = json_to_array(key, data);
// arr.unshift(key)
arr.unshift(title)
// arr.unshift(['','','','物料基本档案','','','',''])
const ws = XLSX.utils.aoa_to_sheet(arr);
if (autoWidth) {
auto_width(ws, arr);
}
XLSX.utils.book_append_sheet(wb, ws, filename);
XLSX.writeFile(wb, filename + '.xlsx');
}
export const read = (data, type) => {
/* if type == 'base64' must fix data first */
// const fixedData = fixdata(data)
// const workbook = XLSX.read(btoa(fixedData), { type: 'base64' })
const workbook = XLSX.read(data, {
type: type
});
const firstSheetName = workbook.SheetNames[0];
const worksheet = workbook.Sheets[firstSheetName];
const header = get_header_row(worksheet);
const results = XLSX.utils.sheet_to_json(worksheet);
return {
header,
results
};
}
export const readesxle = async (file, header, jsointitle) => {
return new Promise(function (resolve, reject) {
const resultdata = {
ErrCode: "9",
ErrText: '导入文件格式不正确。',
Rows: []
}
const fileExt = file.name.split('.').pop().toLocaleLowerCase()
if (fileExt === 'xlsx' || fileExt === 'xls') {
const reader = new FileReader();
const thisXLSX = XLSX;
const thisheader = header;
const thisjsointitle = jsointitle;
reader.readAsArrayBuffer(file)
reader.onloadstart = e => {}
// reader.onprogress = e => {
// this.progressPercent = Math.round(e.loaded / e.total * 100)
// }
reader.onerror = e => {
resultdata.ErrText = '文件读取出错';
resultdata.ErrCode = "1";
resolve(resultdata);
}
reader.onload = e => {
const data = e.target.result
const
workbook = thisXLSX.read(data, {
type: "array"
});
let tempFlag = true;
const firstSheetName = workbook.SheetNames[0];
const worksheet = workbook.Sheets[firstSheetName];
const sheetsheader = get_header_row(worksheet);
const sheetarray = thisXLSX.utils.sheet_to_json(worksheet);
thisheader.forEach((item, index) => {
if (sheetsheader.findIndex(x => x == item) == -1) {
tempFlag = false
}
});
if (tempFlag) {
let sheetresult = [];
for (let i = 0; i < sheetarray.length; i++) {
sheetresult.push({});
for (let j = 0; j < thisheader.length; j++) {
if (sheetarray[i][thisheader[j]] == undefined || sheetarray[i][thisheader[j]] == null)
sheetresult[i][thisjsointitle[j]] = "";
else
sheetresult[i][thisjsointitle[j]] = sheetarray[i][thisheader[j]];
}
}
resultdata.ErrCode = "0";
resultdata.EErrText = "文件导入成功";
resultdata.Rows = sheetresult;
} else {
resultdata.ErrCode = "1";
resultdata.EErrText = "导入文件格式不正确。";
resultdata.Rows = [];
}
resolve(resultdata);
}
} else {
resultdata.ErrCode = "1";
resultdata.ErrText = '文件:' + file.name + '不是EXCEL文件,请选择后缀为.xlsx或者.xls的EXCEL文件。';
resolve(resultdata);
}
})
}
export default {
export_table_to_excel,
export_array_to_excel,
export_json_to_excel,
export_array_to_excel2,
read,
readesxle
}
3、使用导出功能:
<template>
<div class="app-container">
<!-- 按钮 -->
<div class="row-box">
<div id="leftStyle" >
<el-button
class="filter-item"
size="mini"
type="primary"
icon="el-icon-download"
@click="download"
>导出</el-button>
<!-- 筛选组件 -->
<buttonGroup :table-columns="tableColumns" @initCol="initCol" @openQuery="openQuery" @refresh="refresh"/>
<tables ref="table" :loading="loading" :data="data" :table-columns="tableColumns" :height="height" @selectData="selectData" @edit="edit" />
</div>
<!-- 多条件查询组件 -->
<div v-if="isQuery" class="right-box">
<multiQuery ref="queryForm" :table-columns="tableColumns" :table-name="tableName" @mQuery="mQuery" @close="close" />
</div>
</div>
<!--分页组件-->
<el-pagination
:total="total"
:current-page="page + 1"
:page-sizes="[20, 40, 80, 100]"
:page-size="size"
style="margin-top: 8px;"
layout="total, prev, pager, next, sizes"
@size-change="sizeChange"
@current-change="pageChange"/>
</div>
</template>
<script>
import tables from '@/views/equipment/table'// 引入表格组件
import buttonGroup from '@/views/components/buttonGroup' // 筛选组件
import multiQuery from '@/views/components/multiQuery' // 多条件查询组件
import { del, allQueryCrmDevice } from '@/api/crmDevice'
import initData from '@/mixins/initData'
import publicMethod from '@/mixins/publicMethod'
export default {
// 注册组件
components: { tables, buttonGroup, multiQuery },
mixins: [initData, publicMethod],
data() {
return {
height: 625,
delLoading: false,
visibleCol: null,
// 查询数据
isQuery: false,
deleteIds: [],
visible: null,
columns: this.obColumns(), //选择显示的表格列
tableName: 'crm_equipment', //表在数据库中的名字
// 固定表头数据
tableColumns: [
{
hasSort: false, // <Boolean> 是否排序
isShow: true, // <Boolean> 是否展示
prop: 'code', // <String> 对应属性名
label: '代码', // <String> 表头标签
align: 'center', // 表头内容是否居中
mwidth: 100 // 列宽
}, {
hasSort: false,
isShow: true,
prop: 'name',
label: '名称',
align: 'center',
mwidth: 100
}, {
hasSort: false,
isShow: true,
prop: 'customer',
label: '客户',
align: 'center',
mwidth: 100
}, {
hasSort: false,
isShow: true,
prop: 'type',
label: '类型',
align: 'center',
mwidth: 100
}, {
hasSort: false,
isShow: true,
prop: 'status',
label: '状态',
align: 'center',
mwidth: 100
}, {
hasSort: false,
isShow: true,
prop: 'date',
label: '日期',
align: 'center',
mwidth: 100
}, {
hasSort: false,
isShow: true,
prop: 'creatTime',
label: '创建时间',
align: 'center',
mwidth: 100
}, {
hasSort: false,
isShow: true,
prop: 'creatPeople',
label: '创建人',
align: 'center',
mwidth: 100
},{
hasSort: false,
isShow: true,
prop: 'remark',
label: '备注',
align: 'center',
mwidth: 100
}
]
}
},
created() {
this.$nextTick(() => {
this.height = document.documentElement.clientHeight - 200
document.getElementById('leftStyle').className = 'maxBox'
})
//获取数据data
this.init()
},
methods: {
// 选择的数据
selectData() {
this.deleteIds = []
const selectData = arguments[0]
for (let i = 0; i < selectData.length; i++) {
if (this.deleteIds.indexOf(selectData[i].id) === -1) {
this.deleteIds.push(selectData[i].id)
}
}
},
// 新增
add() {
this.isAdd = true
window.sessionStorage.setItem('crmOutEquipmentForm', null)
this.$router.push({ path: 'outEquipmentForm', query: { isAdd: this.isAdd, id: null }})
},
// 双击表格编辑
edit() {
this.isAdd = false
window.sessionStorage.setItem('crmOutEquipmentForm', null)
this.$router.push({ path: 'outEquipmentForm', query: { isAdd: this.isAdd, id: arguments[0].id }})
console.log(arguments[0], '表格编辑数据')
},
// 多条件查询
mQuery() {
console.log(arguments[0].conditionVoList, '查询值')
const queryData = {
page: this.page,
size: this.size,
query: ''
}
queryData.query = JSON.stringify(arguments[0])
allQueryCrmDevice(queryData).then(res => {
console.log(res, '查询返回值')
this.data = res.content
})
},
// 刷新
refresh() {
this.init()
},
initCol() {
this.visibleCol = arguments[0]
const that = this.$refs.table
that.initCol(arguments[0])
},
obColumns(columns) {
return {
visible(col) {
return !columns || !columns[col] ? true : columns[col].visible
}
}
},
openQuery() {
document.getElementById('leftStyle').className = 'minBox'
// const element = document.getElementById('leftStyle')
// element.setAttribute('style', 'width: 100% ')
this.isQuery = true
},
close() {
// const element = document.getElementById('leftStyle')
// element.setAttribute('style', 'width: 74% ')
document.getElementById('leftStyle').className = 'maxBox'
this.isQuery = false
},
//导出
download() {
this.downloadExcel(this.tableColumns, this.visibleCol, this.data, '站点数据')
console.log('导出')
}
}
}
</script>
<style lang="scss" >
@import "@/styles/btnColor.scss";
.minBox{
width: 76%;
}
.maxBox{
width: 100%;
}
</style>
4、选择展示表格列和拖动排序
<template>
<div class="buttonGroup-box">
<el-button-group class="crud-opts-right">
<el-button size="mini" icon="el-icon-search" @click="toQuery"/>
<el-button size="mini" icon="el-icon-refresh" @click="refresh"/>
<el-button size="mini" icon="el-icon-folder-checked" @click="saveConfig"/>
<el-popover placement="bottom-end" width="150" trigger="click">
<el-button slot="reference" size="mini" icon="el-icon-s-grid">
<i class="fa fa-caret-down" aria-hidden="true"/>
</el-button>
<el-scrollbar :height="height">
<el-checkbox v-model="allColumnsSelected" :indeterminate="allColumnsSelectedIndeterminate" @change="handleCheckAllChange">全选</el-checkbox>
<el-checkbox
v-dragging="{ item: column, list: tableColumns, group: 'column' }"
v-for="(column,index) in tableColumns"
:label="column.label"
v-model="column.visible"
:key="index"
@change="handleCheckedTableColumnsChange(column)">{{ column.label }}</el-checkbox>
</el-scrollbar>
</el-popover>
</el-button-group>
</div>
</template>
<script>
export default {
props: {
tableColumns: {// 表格数据源 默认为空数组
type: Array,
default: () => []
}
},
data() {
return {
allColumnsSelected: true,
allColumnsSelectedIndeterminate: false,
height: 625 + 'px',
columnConfig: null
}
},
mounted() {
this.height = document.documentElement.clientHeight - 100
},
methods: {
// 点击全选复选框时
handleCheckAllChange(val) {
console.log(val, '点击全选')
this.allColumnsSelected = val
this.allColumnsSelectedIndeterminate = false // 只改变全选框的样式
if (val === false) {
// 全选状态
this.initColData(val)
for (const key in this.tableColumns) {
this.tableColumns[key].visible = val
}
} else {
for (const key in this.tableColumns) {
this.tableColumns[key].visible = val
}
this.initColData(val)
}
},
initColData(val) {
const columns = {}
this.tableColumns.forEach(e => {
columns[e.prop] = {
label: e.label,
visible: e.visible === val
}
})
console.log(columns, '列数')
this.columnConfig = columns
this.$emit('initCol', columns)
},
// 选择需要展示的表格列数据和拖动排序
handleCheckedTableColumnsChange(item) {
console.log(item, '选择')
let totalCount = 0
let selectedCount = 0
// 排序
const checkedCount = item.length
this.allColumnsSelected = checkedCount === this.tableColumns.length
this.allColumnsSelectedIndeterminate = checkedCount > 0 && checkedCount < this.tableColumns.length
for (const key in this.tableColumns) {
++totalCount // 全部的列数
selectedCount += this.tableColumns[key].visible ? 1 : 0
}
// 当选择的数量与总数相等时 则设置全选状态为选中
this.allColumnsSelected = selectedCount === totalCount
this.allColumnsSelectedIndeterminate = selectedCount !== totalCount && selectedCount !== 0
const columns = {}
this.tableColumns.forEach(e => {
columns[e.prop] = {
label: e.label,
visible: e.visible === true
}
})
this.columnConfig = columns
this.$emit('initCol', columns)
},
// 保存表头配置
saveConfig() {
this.$confirm('确定保存当前表头配置吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
console.log(this.tableColumns, '123', this.columnConfig)
}).catch((err) => {
console.log(err)
})
},
toQuery() {
this.$emit('openQuery')
},
refresh() {
this.$emit('refresh')
}
}
}
</script>
<style scoped>
.el-button--mini {
padding: 7px 15px;
font-size: 14px;
border-radius: 3px;
}
.buttonGroup-box{
float: right;
}
</style>
<style lang="scss">
.el-checkbox__input.is-checked .el-checkbox__inner {
background-color: #49ADF4;
border-color: #49ADF4;
}
.el-checkbox__input.is-indeterminate .el-checkbox__inner {
background-color: #49ADF4;
border-color: #49ADF4;
}
</style>
4、表格组件
<template>
<div style="padding-top:4px;">
<el-table
v-loading="loading"
id="drag-table"
ref="table"
:data="data"
:height="theight"
:span-method="arraySpanMethod"
:header-cell-style="tableHeaderColor"
:row-class-name="tableRowClassName"
:summary-method="getSummaries"
show-summary
resizable
border
style="width: 100%"
@filter-change="handleFilterChange"
@row-click="clickRow"
@row-dblclick="edit"
@selection-change = "selectData">
<!-- 明细 -->
<el-table-column
fixed
type="selection"
width="50"/>
<!--数据源-->
<el-table-column
v-for="(column, index) in tableColumns"
v-if="columns.visible(`${column.prop}`)"
:sortable="column.hasSort"
:key="index"
:prop="column.prop"
:label="column.label"
:align="column.align"
:width="column.width"
:min-width="column.mwidth"
header-align="center">
<template slot-scope="scope">
//时间格式转换
<span v-if="column.prop === 'createTime'" >{{ parseTime1(scope.row.createTime) }}</span>
//字典值转换
<template v-else-if="column.prop==='status'">
<span >{{ statusArr.labelByValue[scope.row[column.prop]] }}</span>
</template>
<span v-else-if="column.prop === 'thirdSite'" >{{ scope.row.third?'是':'否' }}</span>
<!-- 点击名称 时跳转 -->
<template v-else-if="column.prop==='name'" slot-scope="scope">
<span class="linkText" style="color:#49ADF4" @click.prevent="toDetail(scope.row[column.prop])">{{ scope.row[column.prop] }}</span>
</template>
<template v-else>
<span>{{ scope.row[column.prop] }}</span>
</template>
</template>
</el-table-column>
</el-table>
</div>
</template>
<script>
import { parseTime1 } from '@/utils/index'
import statusDict from '@/mixins/statusDict'
import Vue from 'vue'
export default {
filters: {
},
mixins: [statusDict],
props: {
loading: {
type: Boolean,
default: () => false
},
data: {// 表格数据源 默认为空数组
type: Array,
default: () => []
},
tableColumns: {// 表格的字段展示 默认为空数组
type: Array,
default: () => []
},
height: {// 表格高度
type: Number,
default: () => 625
}
},
data() {
return {
columns: this.obColumns(),
listLoading: false,
visible: false,
delLoading: false,
rowIds: [],
deleteIds: [],
detailIds: [],
isAdd: true,
theight: this.height,
multipleSelection: []
}
},
created() {
this.$nextTick(() => {
this.initColData()
})
},
methods: {
parseTime1,
initColData() {
const columns = {}
this.$refs.table.columns.forEach(e => {
if (!e.property || e.type !== 'default') {
return
}
columns[e.property] = {
label: e.label,
visible: true
}
})
this.columns = this.obColumns(columns)
this.updateProp('tableColumns', columns)
},
initCol(columns) {
this.columns = this.obColumns(columns)
this.updateProp('tableColumns', columns)
this.$parent.init()
},
handleCurrentChange(row) {
this.currentRow = JSON.parse(JSON.stringify(row))
},
// 选择改变
selectionChangeHandler(val) {
this.selections = val
},
obColumns(columns) {
return {
visible(col) {
return !columns || !columns[col] ? true : columns[col].visible
}
}
},
// Vue.set( target, key, value )
// 在对象上设置一个属性。如果属性还不存在,则添加新属性并触发更改通知
// target:要更改的数据源(可以是对象或者数组)
// key:要更改的具体数据
// value :重新赋的值
updateProp(name, value) {
console.log(name === 'tableColumns', 'name名字', value)
// const table = this.$refs.table
Vue.set(this.tableColumns, name, value)
},
// 表头点击
handleFilterChange() {
console.log(arguments, '表头点击')
},
// 双击编辑
edit() {
this.$emit('edit', arguments[0])
},
// 单击行时触发方法
clickRow(row, event, column) {
// this.$emit('clickRow', row, event, column)
this.$refs.table.clearSelection()
const index = this.rowIds.indexOf(row.id)
if (index === -1) {
this.$refs.table.toggleRowSelection(row, true)
} else {
this.rowIds = []
this.$refs.table.toggleRowSelection(row, false)
}
},
// 多选改变时触发方法
selectData(arr) {
this.multipleSelection = arr
this.$emit('selectData', arr)
},
// 合并表格行
arraySpanMethod({ row, column, rowIndex, columnIndex }) {
// const length = this.tableColumns.length + 2
// if (columnIndex === 0 && this.dataSource[rowIndex].isShow) {
// return [1, length]
// }
// for (let i = 1; i < length; i++) {
// if (columnIndex === i && this.dataSource[rowIndex].isShow) {
// return [1, 0]
// }
// }
},
// 斑马纹表格样式
tableRowClassName({ row, rowIndex }) {
let color = ''
// if (rowIndex % 2 !== 0) {
// color = 'success-row'
// }
for (let i = 0; i < this.multipleSelection.length; i++) {
if (row === this.multipleSelection[i]) {
color = 'select-row'
}
}
return color
},
// 更改表头样式 #373F52 #4F81CA(蓝色) #1D2F3D(黑色)
tableHeaderColor({ row, column, rowIndex, columnIndex }) {
if (rowIndex === 0) {
return 'background-color:#F0F0F0;color: #606266;font-weight: 700;'
}
},
toDetail() {
this.$router.push({ path: 'otherForm', query: { data: arguments[0] }})
console.log(arguments[0], '点击跳转')
},
// 表格合计行自定义
getSummaries(param) {
const { columns, data } = param
const sums = []
// 解决elementUI table合计行初始页面不显示问腿
window.setTimeout(() => {
if (this.theight === this.height) {
this.theight -= 0.5
} else {
this.theight = this.height
}
}, 1000)
columns.forEach((column, index) => {
if (index === 0) {
sums[index] = '合计'
return
}
if (column.property === 'serviceDistance' || column.property === 'rangePrice' || column.property === 'mileageCharge' || column.property === 'addVolume' || column.property === 'maintenanceTeamSum') {
const values = data.map(item => Number(item[column.property]))
if (!values.every(value => isNaN(value))) {
sums[index] = values.reduce((prev, curr) => {
const value = Number(curr)
if (!isNaN(value)) {
return prev + curr
} else {
return prev
}
}, 0)
if (column.property === 'maintenanceTeamSum') {
sums[index] += ' 人'
} else if (column.property === 'addVolume') {
sums[index] += ' kg/月'
} else if (column.property === 'serviceDistance') {
sums[index] += ' km'
} else if (column.property === 'rangePrice') {
sums[index] += ' 元/km'
} else {
sums[index] += ' 元'
}
} else {
sums[index] = ''
}
}
})
return sums
},
formatClass(state) {
if (state === 0) return 'info'
else if (state === 1 || state === 3) return 'success'
else if (state === 2 || state === 4) return 'danger'
else return 'success'
}
}
}
</script>
<style lang="scss">
@import "@/styles/table.scss";
.el-tag {
height: 26px;
line-height: 26px;
}
.linkText:hover{
color:#0060DF;
cursor: pointer;
text-decoration: underline;
}
.el-table .successRow {
background: #F5F5F5;
}
.info{
color: #AA93A5;
}
.success{
color: #67C23A;
}
.danger{
color:#E6A23C ;
}
</style>