This commit is contained in:
xuliangzhan
2020-12-22 21:15:06 +08:00
parent 126277619c
commit 68b5514589
5 changed files with 226 additions and 85 deletions

View File

@@ -5,12 +5,12 @@
[![npm downloads](https://img.shields.io/npm/dm/vxe-table-plugin-export-xlsx.svg?style=flat-square)](http://npm-stat.com/charts.html?package=vxe-table-plugin-export-xlsx)
[![npm license](https://img.shields.io/github/license/mashape/apistatus.svg)](LICENSE)
基于 [vxe-table](https://www.npmjs.com/package/vxe-table) 的表格插件,支持导出 xlsx 格式
基于 [vxe-table](https://www.npmjs.com/package/vxe-table) 的表格插件,支持导出 xlsx 格式,依赖 [exceljs](https://github.com/exceljs/exceljs) 库
## Installing
```shell
npm install xe-utils vxe-table vxe-table-plugin-export-xlsx xlsx
npm install xe-utils vxe-table vxe-table-plugin-export-xlsx exceljs
```
```javascript

View File

@@ -12,7 +12,7 @@ const tsconfig = require('./tsconfig.json')
const exportModuleName = 'VXETablePluginExportXLSX'
gulp.task('build_commonjs', function () {
return gulp.src(['index.d.ts', 'depend.ts', 'index.ts'])
return gulp.src(['depend.ts', 'index.ts'])
.pipe(sourcemaps.init())
.pipe(ts(tsconfig.compilerOptions))
.pipe(babel({
@@ -27,7 +27,7 @@ gulp.task('build_commonjs', function () {
})
gulp.task('build_umd', function () {
return gulp.src(['index.d.ts', 'depend.ts', 'index.ts'])
return gulp.src(['depend.ts', 'index.ts'])
.pipe(ts(tsconfig.compilerOptions))
.pipe(replace(`import XEUtils from 'xe-utils/ctor';`, `import XEUtils from 'xe-utils';`))
.pipe(babel({

17
index.d.ts vendored
View File

@@ -1,17 +0,0 @@
declare module 'xlsx' {
interface utils {
/* eslint-disable camelcase */
book_new(): any;
json_to_sheet(list: any[], options: any): any;
sheet_to_csv(Sheet: any): any;
sheet_to_json(Sheet: any): any[];
book_append_sheet(book: any, sheet: any, sheetName?: string): any;
}
interface XLSXMethods {
utils: utils;
read(result: any, options: any): any;
write(book: any, options: any): any;
}
const XLSX: XLSXMethods
export default XLSX
}

278
index.ts
View File

@@ -6,13 +6,19 @@ import {
InterceptorExportParams,
InterceptorImportParams,
ColumnConfig,
TableExportConfig
TableExportConfig,
ColumnAlign
} from 'vxe-table/lib/vxe-table'
import XLSX from 'xlsx'
import * as ExcelJS from 'exceljs'
/* eslint-enable no-unused-vars */
const defaultHeaderBackgroundColor = 'f8f8f9'
const defaultCellFontColor = '606266'
const defaultCellBorderStyle = 'thin'
const defaultCellBorderColor = 'e8eaec'
function getFooterCellValue ($table: Table, opts: TableExportConfig, rows: any[], column: ColumnConfig) {
const cellValue = rows[$table.$getColumnIndex(column)]
const cellValue = rows[$table.getVMColumnIndex(column)]
return cellValue
}
@@ -66,25 +72,72 @@ function getValidColumn (column: ColumnConfig): ColumnConfig {
return column
}
function setExcelRowHeight (excelRow: ExcelJS.Row, height: number) {
if (height) {
excelRow.height = XEUtils.floor(height * 0.8, 1)
}
}
function setExcelCellStyle (excelCell: ExcelJS.Cell, align?: ColumnAlign) {
excelCell.protection = {
locked: false
}
excelCell.alignment = {
vertical: 'middle',
horizontal: align || 'left'
}
}
function getDefaultBorderStyle () {
return {
top: {
style: defaultCellBorderStyle,
color: {
argb: defaultCellBorderColor
}
},
left: {
style: defaultCellBorderStyle,
color: {
argb: defaultCellBorderColor
}
},
bottom: {
style: defaultCellBorderStyle,
color: {
argb: defaultCellBorderColor
}
},
right: {
style: defaultCellBorderStyle,
color: {
argb: defaultCellBorderColor
}
}
}
}
function exportXLSX (params: InterceptorExportParams) {
const msgKey = 'xlsx'
const { $table, options, columns, colgroups, datas } = params
const { $vxe } = $table
const { $vxe, rowHeight, headerAlign: allHeaderAlign, align: allAlign, footerAlign: allFooterAlign } = $table
const { modal, t } = $vxe
const { message, sheetName, isHeader, isFooter, isMerge, isColgroup, original } = options
const { message, sheetName, isHeader, isFooter, isMerge, isColgroup, original, useStyle, sheetMethod } = options
const showMsg = message !== false
const mergeCells = $table.getMergeCells()
const colList: any[] = []
const footList: any[] = []
const sheetCols: { wpx: number }[] = []
const sheetCols: any[] = []
const sheetMerges: { s: { r: number, c: number }, e: { r: number, c: number } }[] = []
// 处理表头
if (isHeader) {
const colHead: any = {}
columns.forEach((column) => {
colHead[column.id] = original ? column.property : column.getTitle()
const { id, property, renderWidth } = column
colHead[id] = original ? property : column.getTitle()
sheetCols.push({
wpx: XEUtils.toInteger(column.renderWidth * 0.75)
key: id,
width: XEUtils.ceil(renderWidth / 8, 1)
})
})
if (isColgroup && !original && colgroups) {
@@ -150,21 +203,100 @@ function exportXLSX (params: InterceptorExportParams) {
})
}
const exportMethod = () => {
const book = XLSX.utils.book_new()
const list = (isHeader ? colList : []).concat(rowList).concat(footList)
const sheet = XLSX.utils.json_to_sheet(list.length ? list : [{}], { skipHeader: true })
sheet['!cols'] = sheetCols
sheet['!merges'] = sheetMerges
// 转换数据
XLSX.utils.book_append_sheet(book, sheet, sheetName)
const wbout = XLSX.write(book, { bookType: 'xlsx', bookSST: false, type: 'binary' })
const blob = new Blob([toBuffer(wbout)], { type: 'application/octet-stream' })
// 导出 xlsx
downloadFile(params, blob, options)
if (showMsg) {
modal.close(msgKey)
modal.message({ message: t('vxe.table.expSuccess'), status: 'success' })
const workbook = new ExcelJS.Workbook()
const sheet = workbook.addWorksheet(sheetName)
workbook.creator = 'vxe-table'
sheet.columns = sheetCols
if (isHeader) {
sheet.addRows(colList).forEach(excelRow => {
if (useStyle) {
setExcelRowHeight(excelRow, rowHeight)
}
excelRow.eachCell(excelCell => {
const excelCol = sheet.getColumn(excelCell.col)
const column: any = $table.getColumnById(excelCol.key as string)
const { headerAlign, align } = column
setExcelCellStyle(excelCell, headerAlign || align || allHeaderAlign || allAlign)
if (useStyle) {
Object.assign(excelCell, {
font: {
bold: true,
color: {
argb: defaultCellFontColor
}
},
fill: {
type: 'pattern',
pattern:'solid',
fgColor: {
argb: defaultHeaderBackgroundColor
}
},
border: getDefaultBorderStyle()
})
}
})
})
}
sheet.addRows(rowList).forEach(excelRow => {
if (useStyle) {
setExcelRowHeight(excelRow, rowHeight)
}
excelRow.eachCell(excelCell => {
const excelCol = sheet.getColumn(excelCell.col)
const column: any = $table.getColumnById(excelCol.key as string)
const { align } = column
setExcelCellStyle(excelCell, align || allAlign)
if (useStyle) {
Object.assign(excelCell, {
font: {
color: {
argb: defaultCellFontColor
}
},
border: getDefaultBorderStyle()
})
}
})
})
if (isFooter) {
sheet.addRows(footList).forEach(excelRow => {
if (useStyle) {
setExcelRowHeight(excelRow, rowHeight)
}
excelRow.eachCell(excelCell => {
const excelCol = sheet.getColumn(excelCell.col)
const column: any = $table.getColumnById(excelCol.key as string)
const { footerAlign, align } = column
setExcelCellStyle(excelCell, footerAlign || align || allFooterAlign || allAlign)
if (useStyle) {
Object.assign(excelCell, {
font: {
color: {
argb: defaultCellFontColor
}
},
border: getDefaultBorderStyle()
})
}
})
})
}
if (useStyle && sheetMethod) {
sheetMethod({ options, workbook, worksheet: sheet })
}
sheetMerges.forEach(({ s, e }) => {
sheet.mergeCells(s.r + 1, s.c + 1, e.r + 1, e.c + 1)
})
workbook.xlsx.writeBuffer().then(buffer => {
var blob = new Blob([buffer], { type: 'application/octet-stream' })
// 导出 xlsx
downloadFile(params, blob, options)
if (showMsg) {
modal.close(msgKey)
modal.message({ message: t('vxe.table.expSuccess'), status: 'success' })
}
})
}
if (showMsg) {
modal.message({ id: msgKey, message: t('vxe.table.expLoading'), status: 'loading', duration: -1 })
@@ -209,14 +341,29 @@ declare module 'vxe-table/lib/vxe-table' {
_importReject?: Function | null;
}
}
function importError (params: InterceptorImportParams) {
const { $table, options } = params
const { $vxe, _importReject } = $table
const showMsg = options.message !== false
const { modal, t } = $vxe
if (showMsg) {
modal.message({ message: t('vxe.error.impFields'), status: 'error' })
}
if (_importReject) {
_importReject({ status: false })
}
}
function importXLSX (params: InterceptorImportParams) {
const { $table, columns, options, file } = params
const { $vxe, _importResolve, _importReject } = $table
const { $vxe, _importResolve } = $table
const { modal, t } = $vxe
const showMsg = options.message !== false
const fileReader = new FileReader()
fileReader.onload = (e: any) => {
fileReader.onerror = () => {
importError(params)
}
fileReader.onload = (evnt) => {
const tableFields: string[] = []
columns.forEach((column) => {
const field = column.property
@@ -224,46 +371,57 @@ function importXLSX (params: InterceptorImportParams) {
tableFields.push(field)
}
})
const workbook = XLSX.read(e.target.result, { type: 'binary' })
const rest = XLSX.utils.sheet_to_json(XEUtils.first(workbook.Sheets)) as any[]
const fields = rest ? XEUtils.keys(rest[0]) : []
const list = rest
const status = checkImportData(tableFields, fields)
if (status) {
const records: any[] = list.map(item => {
const record: any = {}
tableFields.forEach(field => {
record[field] = XEUtils.isUndefined(item[field]) ? null : item[field]
})
return record
})
$table.createData(records)
.then((data: any[]) => {
let loadRest: Promise<any>
if (options.mode === 'insert') {
loadRest = $table.insertAt(data, -1)
} else {
loadRest = $table.reloadData(data)
}
return loadRest.then(() => {
if (_importResolve) {
_importResolve({ status: true })
const workbook = new ExcelJS.Workbook()
const readerTarget = evnt.target
if (readerTarget) {
workbook.xlsx.load(readerTarget.result as ArrayBuffer).then(wb => {
const firstSheet = wb.worksheets[0]
if (firstSheet) {
const sheetValues = firstSheet.getSheetValues() as string[][]
const fieldIndex = XEUtils.findIndexOf(sheetValues, (list) => list && list.length > 0)
const fields = sheetValues[fieldIndex] as string[]
const status = checkImportData(tableFields, fields)
if (status) {
const records = sheetValues.slice(fieldIndex).map(list => {
const item : any= {}
list.forEach((cellValue, cIndex) => {
item[fields[cIndex]] = cellValue
})
const record: any = {}
tableFields.forEach(field => {
record[field] = XEUtils.isUndefined(item[field]) ? null : item[field]
})
return record
})
$table.createData(records)
.then((data: any[]) => {
let loadRest: Promise<any>
if (options.mode === 'insert') {
loadRest = $table.insertAt(data, -1)
} else {
loadRest = $table.reloadData(data)
}
return loadRest.then(() => {
if (_importResolve) {
_importResolve({ status: true })
}
})
})
if (showMsg) {
modal.message({ message: t('vxe.table.impSuccess', [records.length]), status: 'success' })
}
})
})
if (showMsg) {
modal.message({ message: t('vxe.table.impSuccess', [records.length]), status: 'success' })
}
} else {
importError(params)
}
} else {
importError(params)
}
})
} else {
if (showMsg) {
modal.message({ message: t('vxe.error.impFields'), status: 'error' })
}
if (_importReject) {
_importReject({ status: false })
}
importError(params)
}
}
fileReader.readAsBinaryString(file)
fileReader.readAsArrayBuffer(file)
}
function handleImportEvent (params: InterceptorImportParams) {

View File

@@ -1,6 +1,6 @@
{
"name": "vxe-table-plugin-export-xlsx",
"version": "2.0.9",
"version": "2.1.0-beta.1",
"description": "基于 vxe-table 的表格插件,支持导出 xlsx 格式",
"scripts": {
"lib": "gulp build"
@@ -33,6 +33,7 @@
"eslint-plugin-promise": "^4.2.1",
"eslint-plugin-standard": "^4.0.2",
"eslint-plugin-typescript": "^0.14.0",
"exceljs": "^4.2.0",
"gulp": "^4.0.2",
"gulp-autoprefixer": "^7.0.1",
"gulp-babel": "^8.0.0",
@@ -49,11 +50,10 @@
"typescript": "^4.0.5",
"vue": "^2.6.12",
"vxe-table": "^2.10.0",
"xe-utils": "^3.0.4",
"xlsx": "^0.16.7"
"xe-utils": "^3.0.4"
},
"peerDependencies": {
"vxe-table": ">= 2.9"
"vxe-table": ">= 2.10"
},
"repository": {
"type": "git",