You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
232 lines
7.1 KiB
232 lines
7.1 KiB
import {isEmpty} from "@/util"
|
|
import request from "@/api/request"
|
|
import {elError} from "@/util/message"
|
|
import {download} from "@/util/file"
|
|
import {flatTree} from "@/util/tree"
|
|
|
|
/**
|
|
* 导出excel
|
|
* 响应头为json时进行前端导出,否则直接下载文件
|
|
*
|
|
* @param url 搜索的请求地址
|
|
* @param searchForm 搜索参数
|
|
* @param options {columns, merge},只有前端导出时才需要
|
|
* @param json2workbook
|
|
* @param workbook2excel
|
|
*/
|
|
export function abstractExportExcel(url, searchForm, options, json2workbook, workbook2excel) {
|
|
request({url, method: 'post', responseType: 'blob', data: searchForm})
|
|
.then(({headers, data}) => {
|
|
const contentType = headers['content-type']
|
|
const contentDisposition = headers['content-disposition']
|
|
const filename = window.decodeURI(contentDisposition.split('=')[1])
|
|
|
|
if (contentType.includes('json')) {
|
|
const reader = new FileReader()
|
|
reader.onload = () => {
|
|
const response = JSON.parse(reader.result)
|
|
const {columns, merge} = options
|
|
const workbook = json2workbook(response.data, columns, merge)
|
|
workbook2excel(workbook, filename)
|
|
}
|
|
reader.readAsText(data)
|
|
}
|
|
else download(data, filename)
|
|
})
|
|
.catch(e => elError(e))
|
|
}
|
|
|
|
/**
|
|
* 合并excel
|
|
* 此方法会修改原始json数据(重写序号)
|
|
*
|
|
* @param props 需要合并的字段数组,用于映射json
|
|
* @param data json数组
|
|
* @param primaryKey 该行json的唯一标识字段名
|
|
* @param orderKey 该行json的序号字段名
|
|
* @param ignoreRows 忽略的行数,默认为1(忽略表头)
|
|
* @return 包含合并信息的数组,形如['A1:A10',...]
|
|
*/
|
|
export function mergeExcel(props, data, primaryKey, orderKey, ignoreRows = 1) {
|
|
const result = [], merge = [], temp = []
|
|
|
|
props.forEach((prop, index) => {
|
|
merge[index] = prop ? [1] : undefined
|
|
temp[index] = prop ? 1 : undefined
|
|
})
|
|
|
|
//序号从1开始
|
|
let indexAfterMerge = data[0][orderKey] = 1
|
|
|
|
function compare(currentRow, lastRow, nextRow, colIndex) {
|
|
const attr = props[colIndex]
|
|
|
|
//若与上一行的primaryKey相同且属性相同,则temp对应项+1
|
|
if (currentRow[primaryKey] === lastRow[primaryKey]
|
|
&& currentRow[attr] === lastRow[attr]) {
|
|
temp[colIndex]++
|
|
//最后一行特殊处理
|
|
if (!nextRow) return merge[colIndex].push(temp[colIndex])
|
|
|
|
//若与下一行相同
|
|
if (currentRow[primaryKey] === nextRow[primaryKey]
|
|
&& currentRow[attr] === nextRow[attr]) {
|
|
merge[colIndex].push(1)
|
|
}
|
|
//否则存入
|
|
else merge[colIndex].push(temp[colIndex])
|
|
}
|
|
//否则清零
|
|
else {
|
|
temp[colIndex] = 1
|
|
merge[colIndex].push(1)
|
|
}
|
|
}
|
|
|
|
for (let i = 1; i < data.length; i++) {
|
|
const currentRow = data[i]
|
|
const lastRow = data[i - 1]
|
|
const nextRow = data[i + 1]
|
|
|
|
//重写序号
|
|
if (currentRow[primaryKey] === lastRow[primaryKey]) {
|
|
currentRow[orderKey] = lastRow[orderKey]
|
|
}
|
|
else currentRow[orderKey] = ++indexAfterMerge
|
|
if (nextRow && nextRow[primaryKey] === currentRow[primaryKey]) {
|
|
nextRow[orderKey] = currentRow[orderKey]
|
|
}
|
|
|
|
props.forEach((_, colIndex) => !isEmpty(_) && compare(currentRow, lastRow, nextRow, colIndex))
|
|
}
|
|
|
|
function mergeResultConstructor(arr, colIndex) {
|
|
const colHeader = number2excelColumnHeader(colIndex)
|
|
const startRows = ignoreRows + 1
|
|
|
|
arr.forEach((rowspan, rowIndex) => {
|
|
if (rowspan > 1) {
|
|
const start = colHeader + (startRows + rowIndex - (rowspan - 1))
|
|
const end = colHeader + (startRows + rowIndex)
|
|
result.push(`${start}:${end}`)
|
|
}
|
|
})
|
|
}
|
|
|
|
merge.forEach((arr, index) => arr && mergeResultConstructor(arr, index))
|
|
|
|
return result
|
|
}
|
|
|
|
/**
|
|
* 生成表头的二维数组以及合并结果
|
|
* 参考element的table-header
|
|
*
|
|
* @param columns 列配置
|
|
* @param separator 分隔符
|
|
* @return {{header: [], mergeCells: []}}
|
|
*/
|
|
export function generateHeader(columns, separator = '-') {
|
|
const tree = []
|
|
let maxDepth = 1
|
|
|
|
for (const col of columns) {
|
|
let header = col.header
|
|
|
|
if (typeof header === 'string') {
|
|
header = header.split(separator)
|
|
}
|
|
|
|
let arr = tree, depth = header.length
|
|
|
|
if (depth > maxDepth) maxDepth = depth
|
|
|
|
for (let i = 0; i < depth; i++) {
|
|
const value = header[i]
|
|
|
|
let obj = arr.find(item => item.value === value)
|
|
|
|
if (!obj) {
|
|
obj = {value, depth: i + 1}
|
|
if (i !== depth - 1) obj.children = []
|
|
|
|
arr.push(obj)
|
|
}
|
|
|
|
arr = obj.children
|
|
}
|
|
}
|
|
|
|
const traverse = column => {
|
|
if (column.children) {
|
|
let colSpan = 0
|
|
column.children.forEach(subColumn => {
|
|
traverse(subColumn)
|
|
colSpan += subColumn.colSpan
|
|
})
|
|
column.rowSpan = 1
|
|
column.colSpan = colSpan
|
|
}
|
|
else {
|
|
column.rowSpan = maxDepth - column.depth + 1
|
|
column.colSpan = 1
|
|
}
|
|
}
|
|
|
|
tree.forEach(node => traverse(node))
|
|
|
|
const header = []
|
|
for (let i = 0; i < maxDepth; i++) header.push([])
|
|
|
|
const mergeCells = []
|
|
|
|
let lastDepth = 0, colIndex = 0 //当前是第几列,从0开始
|
|
|
|
for (const node of flatTree(tree)) {
|
|
//判断是否是下一列,节点深度从1开始
|
|
if (node.depth <= lastDepth) colIndex++
|
|
lastDepth = node.depth
|
|
|
|
//填充表头
|
|
header[node.depth - 1][colIndex] = node.value
|
|
|
|
//获取合并结果,要么rowSpan大于1要么colSpan大于1,两者不可能同时大于1
|
|
const startColHeader = number2excelColumnHeader(colIndex)
|
|
const startCell = startColHeader + node.depth
|
|
if (node.rowSpan > 1) {
|
|
const endCell = startColHeader + (node.depth + node.rowSpan - 1)
|
|
mergeCells.push(`${startCell}:${endCell}`)
|
|
}
|
|
if (node.colSpan > 1) {
|
|
const endCell = number2excelColumnHeader(colIndex + node.colSpan - 1) + node.depth
|
|
mergeCells.push(`${startCell}:${endCell}`)
|
|
}
|
|
}
|
|
|
|
return {header, mergeCells}
|
|
}
|
|
|
|
/**
|
|
* 生成二维数组,例如json为[{id:1,name:'x'}],结果为[[1,'x']]
|
|
*
|
|
* @param data json数组
|
|
* @param propMap 表头和字段名的映射表
|
|
* @return 基数组为excel中每一行的二维数组
|
|
*/
|
|
export function jsonArray2rowArray(data, propMap) {
|
|
return data.map(i => Object.keys(i)
|
|
.reduce((arr, key) => {
|
|
arr[propMap[key]] = i[key]
|
|
return arr
|
|
}, []))
|
|
}
|
|
|
|
//数字转excel列名
|
|
export function number2excelColumnHeader(n) {
|
|
let s = ""
|
|
while (n >= 0) {
|
|
s = String.fromCharCode(n % 26 + 65) + s
|
|
n = Math.floor(n / 26) - 1
|
|
}
|
|
return s
|
|
}
|
|
|