移除了xlsx,exportExcelByJs更名为workbook2excel

表格滚动条使用overlay
master
toesbieya 6 years ago
parent 3d6f0f1fe0
commit 6f19417e3d
  1. 1
      vue/public/index.html
  2. 20
      vue/public/static/js/xlsx.full.min.js
  3. 8
      vue/src/asset/style/element-ui.scss
  4. 26
      vue/src/util/excel/common.js
  5. 55
      vue/src/util/excel/exceljs.js
  6. 12
      vue/src/util/excel/index.js
  7. 132
      vue/src/util/excel/xlsx.js
  8. 21
      vue/src/view/example/test/develop/component/ExportExcelTest.vue

@ -30,7 +30,6 @@
<script defer src="<%= BASE_URL %>static/lightgallery/lg-zoom.min.js"></script> <script defer src="<%= BASE_URL %>static/lightgallery/lg-zoom.min.js"></script>
<!--excel--> <!--excel-->
<script async src="<%= BASE_URL %>static/js/xlsx.full.min.js"></script>
<script async src="<%= BASE_URL %>static/js/exceljs.min.js"></script> <script async src="<%= BASE_URL %>static/js/exceljs.min.js"></script>
<!--富文本--> <!--富文本-->

File diff suppressed because one or more lines are too long

@ -218,6 +218,14 @@
thead > tr > th { thead > tr > th {
background: #fbfbfb; background: #fbfbfb;
} }
.el-table--scrollable-x .el-table__body-wrapper {
overflow-x: overlay;
}
.el-table--scrollable-y .el-table__body-wrapper {
overflow-y: overlay;
}
} }
.el-textarea { .el-textarea {

@ -10,11 +10,11 @@ import {flatTree} from "@/util/tree"
* *
* @param url 搜索的请求地址 * @param url 搜索的请求地址
* @param searchForm 搜索参数 * @param searchForm 搜索参数
* @param options 合并选项{columns, merge}只有前端导出时才需要 * @param options {columns, merge}只有前端导出时才需要
* @param json2workbook * @param json2workbook
* @param exportExcelByJs * @param workbook2excel
*/ */
export function abstractExportExcel(url, searchForm, options, json2workbook, exportExcelByJs) { export function abstractExportExcel(url, searchForm, options, json2workbook, workbook2excel) {
request({url, method: 'post', responseType: 'blob', data: searchForm}) request({url, method: 'post', responseType: 'blob', data: searchForm})
.then(({headers, data}) => { .then(({headers, data}) => {
const contentType = headers['content-type'] const contentType = headers['content-type']
@ -27,7 +27,7 @@ export function abstractExportExcel(url, searchForm, options, json2workbook, exp
const response = JSON.parse(reader.result) const response = JSON.parse(reader.result)
const {columns, merge} = options const {columns, merge} = options
const workbook = json2workbook(response.data, columns, merge) const workbook = json2workbook(response.data, columns, merge)
exportExcelByJs(workbook, filename) workbook2excel(workbook, filename)
} }
reader.readAsText(data) reader.readAsText(data)
} }
@ -124,9 +124,9 @@ export function mergeExcel(props, data, primaryKey, orderKey, ignoreRows = 1) {
* *
* @param columns 列配置 * @param columns 列配置
* @param separator 分隔符 * @param separator 分隔符
* @return {headers, headerMerge} * @return {{header: [], mergeCells: []}}
*/ */
export function generateHeaders(columns, separator = '-') { export function generateHeader(columns, separator = '-') {
const tree = [] const tree = []
let maxDepth = 1 let maxDepth = 1
@ -175,10 +175,10 @@ export function generateHeaders(columns, separator = '-') {
tree.forEach(node => traverse(node)) tree.forEach(node => traverse(node))
const headers = [] const header = []
for (let i = 0; i < maxDepth; i++) headers.push([]) for (let i = 0; i < maxDepth; i++) header.push([])
const headerMerge = [] const mergeCells = []
let lastDepth = 0, colIndex = 0 //当前是第几列,从0开始 let lastDepth = 0, colIndex = 0 //当前是第几列,从0开始
@ -188,22 +188,22 @@ export function generateHeaders(columns, separator = '-') {
lastDepth = node.depth lastDepth = node.depth
//填充表头 //填充表头
headers[node.depth - 1][colIndex] = node.value header[node.depth - 1][colIndex] = node.value
//获取合并结果,要么rowSpan大于1要么colSpan大于1,两者不可能同时大于1 //获取合并结果,要么rowSpan大于1要么colSpan大于1,两者不可能同时大于1
const startColHeader = number2excelColumnHeader(colIndex) const startColHeader = number2excelColumnHeader(colIndex)
const startCell = startColHeader + node.depth const startCell = startColHeader + node.depth
if (node.rowSpan > 1) { if (node.rowSpan > 1) {
const endCell = startColHeader + (node.depth + node.rowSpan - 1) const endCell = startColHeader + (node.depth + node.rowSpan - 1)
headerMerge.push(`${startCell}:${endCell}`) mergeCells.push(`${startCell}:${endCell}`)
} }
if (node.colSpan > 1) { if (node.colSpan > 1) {
const endCell = number2excelColumnHeader(colIndex + node.colSpan - 1) + node.depth const endCell = number2excelColumnHeader(colIndex + node.colSpan - 1) + node.depth
headerMerge.push(`${startCell}:${endCell}`) mergeCells.push(`${startCell}:${endCell}`)
} }
} }
return {headers, headerMerge} return {header, mergeCells}
} }
/** /**

@ -1,6 +1,6 @@
import {isEmpty} from "@/util" import {isEmpty} from "@/util"
import {download} from "@/util/file" import {download} from "@/util/file"
import {abstractExportExcel, jsonArray2rowArray, mergeExcel, generateHeaders, number2excelColumnHeader} from "./common" import {abstractExportExcel, jsonArray2rowArray, mergeExcel, generateHeader, number2excelColumnHeader} from "./common"
//默认的excel单元格样式 //默认的excel单元格样式
const defaultCellStyle = { const defaultCellStyle = {
@ -22,11 +22,10 @@ const defaultHeaderStyle = {
} }
export function exportExcel(url, searchForm, options) { export function exportExcel(url, searchForm, options) {
abstractExportExcel(url, searchForm, options, json2workbook, exportExcelByJs) abstractExportExcel(url, searchForm, options, json2workbook, workbook2excel)
} }
//纯前端导出excel export function workbook2excel(workbook, filename) {
export function exportExcelByJs(workbook, filename) {
return workbook.xlsx.writeBuffer() return workbook.xlsx.writeBuffer()
.then(buffer => download(new Blob([buffer], {type: "application/octet-stream"}), filename)) .then(buffer => download(new Blob([buffer], {type: "application/octet-stream"}), filename))
} }
@ -35,29 +34,40 @@ export function exportExcelByJs(workbook, filename) {
* @desc 通过json数组生成workbook对象 * @desc 通过json数组生成workbook对象
* @param data json数组 * @param data json数组
* @param columns 列配置 * @param columns 列配置
* @param merge 合并配置项 * @param mergeOption 合并配置项
* @returns workbook * @returns workbook
*/ */
export function json2workbook(data, columns, merge) { export function json2workbook(data, columns, mergeOption) {
const workbook = new window.ExcelJS.Workbook() const columnStyle = [] //列设置
const sheet = workbook.addWorksheet('Sheet1')
const columnSettings = []
const propMap = {} //表头和字段名的映射表,propMap[字段名]=对应的表头的数组下标 const propMap = {} //表头和字段名的映射表,propMap[字段名]=对应的表头的数组下标
const needMerge = [] //合并行配置,[i]=字段名 const needMerge = [] //合并行配置,[i]=字段名
columns.forEach((col, i) => { columns.forEach((col, i) => {
if (!isEmpty(col.prop)) propMap[col.prop] = i if (!isEmpty(col.prop)) propMap[col.prop] = i
if (col.merge) needMerge[i] = col.prop if (col.merge) needMerge[i] = col.prop
columnSettings[i] = {width: col.width || 20, style: defaultCellStyle} columnStyle[i] = {width: col.width || 20, style: defaultCellStyle}
}) })
sheet.columns = columnSettings const {header, mergeCells} = generateHeader(columns)
if (mergeOption && needMerge.length > 0) {
const {primaryKey, orderKey} = mergeOption
mergeCells.push(...mergeExcel(needMerge, data, primaryKey, orderKey, header.length))
}
const body = jsonArray2rowArray(data, propMap)
return generateWorkbook('Sheet1', columnStyle, header, body, mergeCells)
}
const {headers, headerMerge} = generateHeaders(columns) export function generateWorkbook(sheetName, columnStyle, header, body, mergeCells) {
const workbook = new window.ExcelJS.Workbook()
const sheet = workbook.addWorksheet(sheetName)
if (headers.length > 0) { if (header) {
sheet.addRows(headers) sheet.addRows(header)
headers.forEach((rows, rowIndex) => { //设置头部单元格样式
header.forEach((rows, rowIndex) => {
rows.forEach((cellValue, colIndex) => { rows.forEach((cellValue, colIndex) => {
if (cellValue !== undefined) { if (cellValue !== undefined) {
const cell = sheet.getCell(number2excelColumnHeader(colIndex) + (rowIndex + 1)) const cell = sheet.getCell(number2excelColumnHeader(colIndex) + (rowIndex + 1))
@ -68,21 +78,10 @@ export function json2workbook(data, columns, merge) {
}) })
}) })
} }
body && sheet.addRows(body)
let bodyMerge = []
if (merge && needMerge.length > 0) {
const {primaryKey, orderKey} = merge
bodyMerge = mergeExcel(needMerge, data, primaryKey, orderKey, headers.length)
}
const result = jsonArray2rowArray(data, propMap)
sheet.addRows(result)
//exceljs的合并行必须在数据添加后进行 //exceljs的合并行必须在数据添加后进行
headerMerge.forEach(i => sheet.mergeCells(i)) mergeCells && mergeCells.forEach(i => sheet.mergeCells(i))
bodyMerge.forEach(i => sheet.mergeCells(i))
return workbook return workbook
} }

@ -1 +1,11 @@
export {exportExcel, exportExcelByJs, json2workbook} from "./exceljs" import {exportExcel, json2workbook, workbook2excel} from "./exceljs"
/**
* @desc el-table导出excel
* @param tableInstance el-table实例
*/
export function elementUiTable2excel(tableInstance) {
}
export {exportExcel, json2workbook, workbook2excel}

@ -1,132 +0,0 @@
/**
* @deprecated
* xlsx问题多多转向excelJS
*/
import {isEmpty} from "@/util"
import {download} from "@/util/file"
import {abstractExportExcel, jsonArray2rowArray, mergeExcel} from "./common"
/**
* @param url
* @param searchForm
* @param options
*/
export function exportExcel(url, searchForm, options) {
abstractExportExcel(url, searchForm, options, json2workbook, exportExcelByJs)
}
//纯前端导出excel
export function exportExcelByJs(workbook, filename) {
const out = window.XLSX.write(workbook, {
bookType: 'xlsx',
bookSST: false,
type: 'binary'
})
download(new Blob([s2ab(out)], {type: "application/octet-stream"}), filename)
}
/**
* @param data json数组
* @param columns 列配置
* @param merge 合并配置项
* @returns
*/
export function json2workbook(data, columns, merge) {
const header = [] //表格头部
const propMap = {} //表头和字段名的映射表,propMap[字段名]=对应的表头的数组下标
const columnSettings = [] //列宽配置,默认20
const needMerge = [] //合并行配置,[i]=字段名
columns.forEach((col, i) => {
if (!isEmpty(col.header)) header[i] = col.header
if (!isEmpty(col.prop)) propMap[col.prop] = i
if (col.merge) needMerge[i] = col.prop
columnSettings[i] = {wch: col.width || 20}
})
const headerRows = header.length > 0 ? 1 : 0
let bodyMerge = []
if (merge && needMerge.length > 0) {
const {primaryKey, orderKey} = merge
bodyMerge = mergeExcel(needMerge, data, primaryKey, orderKey, headerRows)
}
const result = jsonArray2rowArray(data, propMap)
headerRows && result.unshift(header)
const sheet = aoa_to_sheet(result, headerRows)
sheet['!cols'] = columnSettings
sheet['!merges'] = bodyMerge.map(i => window.XLSX.utils.decode_range(i))
return {SheetNames: ['Sheet1'], Sheets: {Sheet1: sheet}}
}
//默认的excel单元格样式
const defaultCellStyle = {
font: {name: '宋体'},
alignment: {
wrapText: 1,// 自动换行
horizontal: "center",
vertical: "center",
indent: 0
}
}
//默认的头部样式
const defaultHeaderStyle = {
...defaultCellStyle,
fill: {fgColor: {rgb: "C0C0C0"}},
font: {name: '宋体', sz: 14, bold: true},
border: {top: {style: 'thin'}, left: {style: 'thin'}, bottom: {style: 'thin'}, right: {style: 'thin'}}
}
function datenum(v, date1904) {
if (date1904) v += 1462
const epoch = Date.parse(v)
return (epoch - new Date(Date.UTC(1899, 11, 30))) / (24 * 60 * 60 * 1000)
}
function aoa_to_sheet(data, headerRows) {
const ws = {}
const range = {s: {c: 10000000, r: 10000000}, e: {c: 0, r: 0}}
for (let R = 0; R !== data.length; ++R) {
for (let 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
//生成cell的时候,使用默认样式
const cell = {
v: data[R][C],
s: R === 0 && headerRows > R ? defaultHeaderStyle : defaultCellStyle
}
if (cell.v == null) continue
const cell_ref = window.XLSX.utils.encode_cell({c: C, r: R})
/* TEST: proper cell types and value handling */
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 = window.XLSX.SSF._table[14]
cell.v = datenum(cell.v)
}
else cell.t = 's'
ws[cell_ref] = cell
}
}
/* TEST: proper range */
if (range.s.c < 10000000) ws['!ref'] = window.XLSX.utils.encode_range(range)
return ws
}
function s2ab(s) {
const buf = new ArrayBuffer(s.length)
const view = new Uint8Array(buf)
for (let i = 0; i !== s.length; ++i) view[i] = s.charCodeAt(i) & 0xFF
return buf
}

@ -12,10 +12,11 @@
</template> </template>
<script> <script>
import {exportExcelByJs, json2workbook} from "@/util/excel" import {workbook2excel, json2workbook} from "@/util/excel"
export default { export default {
name: "ExportExcelTest", name: "ExportExcelTest",
data() { data() {
return { return {
loading: false, loading: false,
@ -34,22 +35,24 @@ export default {
cost: 0 cost: 0
} }
}, },
methods: { methods: {
exportExcel() { exportExcel() {
if (this.loading) return if (this.loading) return
const start = Date.now()
this.loading = true this.loading = true
const start = Date.now()
const arr = new Array(this.rows).fill(this.row) const arr = new Array(this.rows).fill(this.row)
const workbook = json2workbook(arr, this.columns, this.merge ? { const workbook = json2workbook(
primaryKey: 'name', arr,
orderKey: 'no' this.columns,
} : null) this.merge ? {primaryKey: 'name', orderKey: 'no'} : null)
exportExcelByJs(workbook, '测试导出.xlsx')
workbook2excel(workbook, '测试导出.xlsx')
this.cost = Date.now() - start
this.loading = false this.loading = false
const end = Date.now()
this.cost = end - start
}, },
}, },
} }

Loading…
Cancel
Save