import {isEmpty} from "@/utils" import request from "@/config/request" import {elError} from "@/utils/message" import {download} from "@/utils/file" /** * 搜索表格页导出excel * 响应头为json时进行前端导出,否则直接下载文件 * * @param url 搜索的请求地址 * @param searchForm 搜索参数 * @param options 合并选项{column, merge},只有前端导出时才需要 * @param json2workbook * @param exportExcelByJs */ export function abstractExportExcel(url, searchForm, options, json2workbook, exportExcelByJs) { 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) if (response.status !== 200) return elError(response.msg) const {column, merge} = options const workbook = json2workbook(column, response.data, merge) exportExcelByJs(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 } /** * 生成二维数组,例如json为[{id:1,name:'x'}],结果为[[1,'x']] * * @param data json数组 * @param propMap 表头和字段名的映射表 */ 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 }