/** * Created by Xumeng 2020/04/22. */ import React, { useState, useEffect } from 'react'; import PropTypes from 'prop-types'; import { Row, Col, Space, Button, message, notification, Form, Input, Tooltip, } from 'antd'; import moment from 'moment'; import XLSX from 'xlsx'; import { fromJS } from 'immutable'; import { Request } from '@peace/utils'; import FileSaver from 'file-saver'; import './index.less'; // 通用前端导入导出组件 使用方法查看底部propTypes function ExportAndImport (props) { const [form] = Form.useForm(); const [exportLoading, setExportLoading] = useState(false); const [importLoading, setImportLoading] = useState(false); const { importDataCallback, onImportSucess, handelData, importMethod = 'post', } = props; useEffect(() => () => { // 只有unmount 时调用 notification.close('import-notification'); }); const importExcel = (file, type) => { setImportLoading(true); // 获取上传的文件对象 const { files } = file.target; // 判断xls、xlsx格式 if (files[0].type.indexOf('sheet') > -1 || files[0].type.indexOf('ms-excel') > -1) { // 通过FileReader对象读取文件 const fileReader = new FileReader(); fileReader.onload = (event) => { try { const { importRequest = true, importUrl, importQuery } = props; if (importRequest && !importUrl) { message.error('获取导入接口失败!'); form.resetFields(); return; } const { result } = event.target; // 以二进制流方式读取得到整份excel表格对象 const workbook = XLSX.read(result, { type: 'binary', cellDates: true }); let data = []; // 存储获取到的数据 // 遍历每张工作表进行读取(这里默认只读取第一张表) for (const sheet in workbook.Sheets) { if (workbook.Sheets.hasOwnProperty(sheet)) { // 利用 sheet_to_json 方法将 excel 转成 json 数据 data = data.concat(XLSX.utils.sheet_to_json(workbook.Sheets[sheet])); break; // 如果只取第一张表,就取消注释这行 } } if (data.length > 10000) { message.error('一次最多导入10000条数据,请分批导入!'); form.resetFields(); setImportLoading(false); return; } if (importRequest) { message.success('获取文件数据成功,开始处理导入...'); const importData = handelData ? handelData(data) : data; Request[importMethod](importUrl, { data: importData }, importQuery || {}).then((res) => { message.success('导入数据成功'); form.resetFields(); notification.close('import-notification'); setImportLoading(false); onImportSucess && onImportSucess(); }, (err) => { if (err.status === 500) { message.error('数据导入出错,导入失败'); } else if (err.status === 400) { message.error(err.body.message || '数据验证出错,请检查数据格式是否正确'); } else { message.error('导入失败'); } form.resetFields(); setImportLoading(false); }); } else { form.resetFields(); setImportLoading(false); importDataCallback && importDataCallback(data, type); notification.close('import-notification'); } } catch (e) { console.log(e); // 这里可以抛出文件类型错误不正确的相关提示 message.error('文件格式不正确!'); setImportLoading(false); form.resetFields(); } }; // fileReader.onloadend = (event) => { // console.log(event) // } // 以二进制方式打开文件 fileReader.readAsBinaryString(files[0]); } else { message.error('文件格式不正确!'); form.resetFields(); setImportLoading(false); } }; const loop = (data, keypath, values) => { // deal with array const dkey = keypath.slice(0, 1)[0]; console.log(dkey); if (dkey) { const dvalue = data[dkey]; const otherKeypath = keypath.slice(1); if (Array.isArray(dvalue)) { if (otherKeypath.length) { const immutableData = fromJS(data); for (let index = 0; index < dvalue.length; index++) { const tmp = immutableData.getIn([dkey, index]).toJS(); loop(tmp, otherKeypath, values); } } } else { values.push(dvalue); } } return values; }; const getColumnData = (opts) => { const { data, keypath, render, spliter, rawdata, } = opts; let v = null; const outer = data[keypath[0]]; if (Array.isArray(outer)) { const values = loop(data, keypath, []); v = rawdata ? values : values.join(spliter || ','); } else { v = fromJS(data).getIn(keypath); } // 处理render if (render && typeof render === 'function') { v = render(outer, data); } return v; }; const getDataSource = (attrs, filterData) => { // let token = JSON.parse(sessionStorage.getItem('user')).token; const dataSource = filterData.map((item) => { const record = {}; attrs.forEach((attr) => { const { key, dataIndex, render, child, } = attr; if (child) { record[key] = getDataSource(child, item[key]); } else { const v = getColumnData({ data: item, keypath: dataIndex || [key], render: render || null, }); record[key] = v; } }); return record; }); return dataSource; }; // 暂时只处理两层 const getFlatData = (attrs, filterData, dataToAoa, deep = 0) => { filterData.map((item) => { let cur = dataToAoa[deep]; if (!cur) { cur = dataToAoa[deep] = []; } attrs.map((attr, index) => { const { key, child } = attr; if (child) { if (Array.isArray(item[key])) { // getFlatData(child,item[key],dataToAoa,deep) item[key].map((s, i) => { if (i == 0) { child.map((c) => { cur.push(s[c.key]); }); } else { deep++; const childCur = dataToAoa[deep] = []; pushNull(childCur, index); child.map((c) => { childCur.push(s[c.key]); }); } }); } } else { cur.push(item[key]); } }); deep++; }); }; const getHeader = (headers, excelHeader, deep, perOffset) => { let offset = 0; let cur = excelHeader[deep]; if (!cur) { cur = excelHeader[deep] = []; } pushNull(cur, perOffset - cur.length); for (let i = 0; i < headers.length; i++) { const head = headers[i]; cur.push(head.name); if (head.hasOwnProperty('child') && Array.isArray(head.child) && head.child.length > 0) { const childOffset = getHeader(head.child, excelHeader, deep + 1, cur.length - 1); pushNull(cur, childOffset - 1); offset += childOffset; } else { offset++; } } return offset; }; const pushNull = (arr, count) => { for (let i = 0; i < count; i++) { arr.push(null); } }; const fillNull = (arr) => { const max = Math.max(...(arr.map((a) => a.length))); arr.filter((e) => e.length < max).forEach((e) => pushNull(e, max - e.length)); }; const doMerges = (arr) => { // 要么横向合并 要么纵向合并 const deep = arr.length; const merges = []; for (let y = 0; y < deep; y++) { // 先处理横向合并 const row = arr[y]; let colSpan = 0; for (let x = 0; x < row.length; x++) { if (row[x] === null) { colSpan++; if (((x + 1) === row.length) && (colSpan > 0 && x > colSpan)) { merges.push({ s: { r: y, c: x - colSpan }, e: { r: y, c: x } }); } } else if (colSpan > 0 && x > colSpan) { merges.push({ s: { r: y, c: x - colSpan - 1 }, e: { r: y, c: x - 1 } }); colSpan = 0; } else { colSpan = 0; } } } // 再处理纵向合并 const colLength = arr[0].length; for (let x = 0; x < colLength; x++) { let rowSpan = 0; for (let y = 0; y < deep; y++) { if (arr[y][x] != null) { rowSpan = 0; } else { rowSpan++; } } if (rowSpan > 0) { merges.push({ s: { r: deep - rowSpan - 1, c: x }, e: { r: deep - 1, c: x } }); } } return merges; }; // 内容暂只出了纵向合并 const doContetMerges = (arr, headerLength) => { const deep = arr.length; const merges = []; // 处理纵向合并 const colLength = arr[0].length; for (let x = 0; x < colLength; x++) { let rowSpan = 0; const mergY = 0; for (let y = 0; y < deep; y++) { if (rowSpan > 0) { // 如果还有null 继续加 if (arr[y][x] === null) { rowSpan += 1; } else { // 不为null 增加merge merges.push({ s: { r: headerLength + (y - rowSpan - 1), c: x }, e: { r: headerLength + y - 1, c: x } }); rowSpan = 0; } } else if (arr[y][x] === null) { rowSpan += 1; } } if (rowSpan > 0) { merges.push({ s: { r: headerLength + (deep - rowSpan - 1), c: x }, e: { r: headerLength + deep - 1, c: x } }); rowSpan = 0; } } return merges; }; const exportMergeExcel = async () => { setExportLoading(true); const { column, data, fileName, exportUrl, exportQuery, exportBody, requestType, header, showYearMouth, } = props || {}; let resultData = []; if (exportUrl) { resultData = requestType == 'post' ? await Request.post(exportUrl, exportBody || {}, exportQuery || {}).then((data) => { // 数据接口返回的结果 如果是对象 必须把返回数组放入rows if (typeof data === 'object' && data.rows) { return data.rows; } return data; }, (err) => { message.error('获取数据失败,导出失败!'); }) : await Request.get(exportUrl, exportQuery || {}).then((data) => { if (typeof data === 'object' && data.rows) { return data.rows; } return data; }, (err) => { message.error('获取数据失败,导出失败!'); }); if (!resultData) { return; } } else { resultData = data; } const excelHeader = []; getHeader(column, excelHeader, 0, 0); fillNull(excelHeader); // console.log(excelHeader); const loopData = getDataSource(column, resultData); // console.log(loopData) const dataToAoa = []; getFlatData(column, loopData, dataToAoa, 0); fillNull(dataToAoa); // console.log(dataToAoa); const aoa = [].concat(excelHeader, dataToAoa); // console.log(aoa) const headerMerges = doMerges(excelHeader); const contentMerages = doContetMerges(dataToAoa, excelHeader.length); const merges = [].concat(headerMerges, contentMerages); // console.log(contentMerages) // let opts = { // defaultCellStyle: { // font: { name: "宋体", sz: 11, color: { auto: 1 } }, // border: { // color: { auto: 1 } // }, // alignment: { // /// 自动换行 // wrapText: 1, // // 居中 // horizontal: "center", // vertical: "center", // indent: 0 // } // } // } const sheet = XLSX.utils.aoa_to_sheet(aoa); // let newSheet = {}; // for (let [key, value] of Object.entries(sheet)) { // if(key == '!ref'){ // newSheet[key] = value // }else if(typeof value === 'object'){ // newSheet[key] = { // ...value, // s: opts.defaultCellStyle // } // } // } const wpx = column.map((c) => ({ wpx: Number.parseInt(c.wpx) ? Number.parseInt(c.wpx) : 100, })); sheet['!cols'] = wpx; sheet['!merges'] = merges; // 构建 workbook 对象 const workbook = XLSX.utils.book_new(); const time = moment().format('YYYY-MM-DD'); XLSX.utils.book_append_sheet(workbook, sheet, 'mySheet'); // 导出 Excel XLSX.writeFile(workbook, fileName ? `${fileName}-${time}.xlsx` : '导出数据.xlsx'); setExportLoading(false); // message.success(`成功导出了 ${loopData.length || 0} 条数据`); }; const exportProExcel = async () => { setExportLoading(true); const { column, data, fileName, exportUrl, exportQuery, exportBody, requestType, showYearMouth, } = props || {}; let resultData = []; if (exportUrl) { resultData = requestType == 'post' ? await Request.post(exportUrl, exportBody || {}, exportQuery || {}).then((data) => { // 数据接口返回的结果 如果是对象 必须把返回数组放入rows if (typeof data === 'object') { return data.data ? data.data : data.rows; } return data; }, (err) => { message.error('获取数据失败,导出失败!'); }) : await Request.get(exportUrl, exportQuery || {}).then((data) => { if (showYearMouth) { } if (typeof data === 'object' && data.rows) { return data.rows; } return data; }, (err) => { message.error('获取数据失败,导出失败!'); }); if (!resultData) { return; } } else { resultData = data; } const loopData = getDataSource(column, resultData); let content = ''; let header = '