import React from 'react'; import { Button, Modal, Steps, Upload, Icon, message, Spin, Table } from 'antd'; import styles from './ImportUtil.less'; import reqwest from 'reqwest'; import ButtonDiy from '@/baseComponent/ButtonDiy'; import { getUploadUrl } from '@/config/config'; import { Row, Col } from 'antd'; import { getToken } from '@/webPublic/one_stop_public/utils/token'; const Step = Steps.Step; const get_length = (s) => { let char_length = 0; for (let i = 0; i < s.length; i++) { let son_char = s.charAt(i); encodeURI(son_char).length > 2 ? (char_length += 2) : (char_length += 1); } if (char_length > 0 && char_length < 5) { char_length = 5; } return char_length; }; const calcWidth = (columns = []) => { let width = 0; columns.forEach((i) => { width += get_length(i.title) * 15; }); return width; }; const calcColumns = (columns) => { return columns.map((i) => { let length = 1; if (i.title) { length = get_length(i.title); i.width = length * 16; //i. } return i; }); }; export default class ImportUtil extends React.PureComponent { constructor(props) { super(props); const { api, serviceBean, serviceKey } = props; var importFileUrl, analyseFileUrl, queryFileUrl, exportTemplateUrl; if (api != null) { const x = api; // 学工的 接口地址 importFileUrl = x + `/importApi/execute?token=${getToken()}&cacheKey=`; queryFileUrl = x + `/importApi/findData?token=${getToken()}&cacheKey=`; analyseFileUrl = x + `/importApi/analyse?token=${getToken()}&filePath=`; exportTemplateUrl = x + `/importApi/downLoadTemplate?serviceBean=${serviceBean}&serviceKey=${serviceKey}`; } else { importFileUrl = props.importFileUrl; queryFileUrl = props.queryFileUrl; analyseFileUrl = props.analyseFileUrl; exportTemplateUrl = props.exportTemplateUrl; } this.state = { visible: false, current: 0, isShow: true, filekey: '', sucData: [], errData: [], tableWidth: 0, column: [], importFileUrl, queryFileUrl, analyseFileUrl, exportTemplateUrl, loading: false, isNextDisabled: false, }; } static getDerivedStateFromProps(props, state) { if (props.api != null) { const { api, serviceBean, serviceKey } = props; const x = api; // // 学工的 接口地址 state.importFileUrl = x + `/importApi/execute?token=${getToken()}&cacheKey=`; state.queryFileUrl = x + `/importApi/findData?token=${getToken()}&cacheKey=`; state.analyseFileUrl = x + `/importApi/analyse?token=${getToken()}&filePath=`; state.exportTemplateUrl = x + `/importApi/downLoadTemplate?serviceBean=${serviceBean}&serviceKey=${serviceKey}`; } else { state.importFileUrl = props.importFileUrl; state.queryFileUrl = props.queryFileUrl; state.analyseFileUrl = props.analyseFileUrl; state.exportTemplateUrl = props.exportTemplateUrl; } return { ...state, }; } //导入按钮 showModal = () => { //console.log('导入按钮'); this.setState({ visible: true, }); }; handleOk = (e) => { this.setState({ visible: false, }); }; handleCancel = (e) => { this.setState({ visible: false, }); }; next() { const current = this.state.current + 1; if (current === 2) { if (this.state.errData.length > 0) { message.warning('请检查数据'); return; } this.setState({ current: current, isShow: false, }); this.import(); } if (current === 3) { this.setState({ current: current, isShow: true, }); if (this.props.callback) { this.props.callback(); } } } prev() { const current = this.state.current - 1; this.setState({ current }); } import() { const importFileUrl = this.state.importFileUrl; this.setState({ loading: true }); const { serviceParam, serviceKey, serviceBean } = this.props; let data = { cacheKey: this.state.filekey, serviceBean, serviceKey, serviceParam, }; reqwest({ url: importFileUrl, type: 'json', method: 'post', data: JSON.stringify(data), headers: { Accept: 'application/json;charset=UTF-8', 'Content-Type': 'application/json', }, crossOrigin: true, withCredentials: false, error: (err) => { this.setState({ loading: false }); message.warning('网络故障'); }, success: (res) => { if (res && res.errMsg) { this.setState({ loading: false, isNextDisabled: true, }); message.warning('服务器返回错误' + res.errMsg); return false; } this.next(); message.success('导入成功'); this.setState({ current: 3, isShow: true, visible: false, loading: false, }); if (this.props.callback) { this.props.callback(); } }, }); } getCachKey = (filePath) => { const analyseFileUrl = this.state.analyseFileUrl; const { serviceBean, serviceKey } = this.props; this.setState({ loading: true }); let dataAnalyse = { serviceBean, serviceKey, filePath, isLocal: true, }; const { importParams } = this.props; if (importParams) { dataAnalyse.serviceParam = importParams; } const urlAnaly = analyseFileUrl; reqwest({ url: urlAnaly, type: 'json', method: 'post', data: JSON.stringify(dataAnalyse), headers: { Accept: 'application/json;charset=UTF-8', 'Content-Type': 'application/json', }, crossOrigin: true, withCredentials: false, error: (err) => { message.warning('网络故障'); this.setState({ loading: false }); }, success: (res) => { let key = ''; if (res && res.cacheKey) { key = res.cacheKey; } if (key) { this.setState({ filekey: key, loading: false, }); this.queryFile(key); } else { if (res.errMsg) { message.warning(res.errMsg); } this.setState({ loading: false, }); } }, }); }; queryFile = (key) => { const { importParams, serviceBean, serviceKey } = this.props; const queryFileUrl = this.state.queryFileUrl; this.setState({ loading: true }); let queryData = { serviceParam: { ...importParams, }, serviceBean, serviceKey, cacheKey: key, }; reqwest({ url: queryFileUrl, type: 'json', method: 'post', data: JSON.stringify(queryData), headers: { Accept: 'application/json;charset=UTF-8', 'Content-Type': 'application/json', }, crossOrigin: true, withCredentials: false, error: (err) => { this.setState({ loading: false }); message.warning('网络故障'); }, success: (res) => { if (!res || !res[key] || typeof res[key].columns === 'undefined') { if (res.errMsg) { message.warning('导入数据错误!'); } return false; } res = res[key]; let id = 1; res.passList = res.passList.filter((x) => { return x && typeof x === 'object'; }); if (res.passList) { res.passList.filter((x) => { return !!x; }); for (let item of res.passList) { item.rowKeyId = id++; } } res.column = []; if (res.columns) { for (let item in res.columns) { if (res.columns[item] !== '错误信息') { res.column.push({ dataIndex: item, title: res.columns[item], }); } } } id = 1; res.noPassList = res.noPassList.filter((x) => { return x && typeof x === 'object'; }); if (res.noPassList) { res.noPassList.filter((x) => { return !!x; }); for (let item of res.noPassList) { item.rowKeyId = id++; } } this.setState({ current: 1, isShow: false, loading: false, sucData: res.passList, errData: res.noPassList, column: res.column, tableWidth: calcWidth(res.column), }); if (!!res.pass && !res.pass.length) { message.warning('当前没有验证成功的数据,无法导入。'); this.setState({ isNextDisabled: true, }); } else { this.setState({ isNextDisabled: false, }); } }, }); }; // 分析数据后,导出表格里的数据 // key='pass', 导出通过数据; key='notPass',导出未通过数据 exportData = (isPass) => { const type = isPass ? 'passList' : 'noPassList'; const { filekey, exportTemplateUrl } = this.state; window.open(`${exportTemplateUrl}&cacheKey=${filekey}&dataType=${type}&token=${getToken()}`); }; continueImport = () => { this.setState({ current: 2, isShow: false, }); this.import(); }; render() { const props = { name: 'file', action: getUploadUrl(), headers: { Authorization: `bearer ${getToken()}`, }, data: { token: getToken(), }, accept: '.xlsx', showUploadList: false, onChange: (info) => { if (info.file.status !== 'uploading') { } if (info.file.status === 'done') { message.success(`${info.file.name} 上传成功`); let x = info.file.response.filePath; this.getCachKey(x); } else if (info.file.status === 'error') { message.warning(`${info.file.name} 上传文件失败.`); } }, }; const steps = [ { title: '上传Excel', }, { title: '验证数据', }, { title: '数据导入', }, { title: '导入完成', }, ]; const { current, loading, sucData, errData } = this.state; const { name } = this.props; const column = calcColumns(this.state.column); const hasErrorData = !!errData && !!errData.length; const hasSucData = !!sucData && !!sucData.length; return ( <span> <ButtonDiy name={name || '批量导入'} type="default" className="defaultBlue" handleClick={this.showModal} /> <Modal visible={this.state.visible} onOk={this.handleOk} maskClosable={false} destroyOnClose onCancel={this.handleCancel} title={null} footer={null} style={{ height: '700px' }} width={'80%'}> <Spin spinning={loading}> <div className={styles.nomal}> <p className={styles.import}>EXCEL导入向导</p> <div style={{ margin: 'auto 12px', fontSize: '12px' }}> <Steps current={current}> {steps.map((item) => ( <Step key={item.title} title={item.title} /> ))} </Steps> <div className="steps-content">{steps[this.state.current].content}</div> <div className={styles.button}> {this.state.current < steps.length - 1 && this.state.current !== 0 && ( <ButtonDiy name={this.state.current == 3 ? '确认导入' : '下一步'} type="primary" disabled={this.state.current != 3 && this.state.isNextDisabled} className="primaryBlue" handleClick={() => this.next()} /> )} {this.state.current > 0 && ( <ButtonDiy style={{ marginLeft: 8 }} name="上一步" className="defaultBlue" handleClick={() => this.prev()} /> )} </div> </div> <div className={styles.tip}> <p className={styles.tipContent}> <span>欢迎使用EXCEL导入向导,首先请您上传需要导入的EXCEL文件。</span> <span> 点击“选择文件”选择您要导入的EXCEL文件,点击“开始上传”将其上传到服务器上,点击“清空队列”清空当前上传文件队列。 </span> </p> </div> {this.state.isShow ? ( <div className={styles.buttonDown}> <Upload {...props}> <Button shape="round"> <Icon type="upload" /> 点击上传 </Button> </Upload> <Button shape="round" style={{ marginLeft: 14 }} href={ this.props.fileName ? this.state.exportTemplateUrl + '&token=' + getToken() + '&fileName=' + this.props.fileName : this.state.exportTemplateUrl + '&token=' + getToken() } target="_blank" type="primary" ghost> 下载模板 </Button> </div> ) : ( <Row> <Col span={12}> <Upload {...props}> <Button shape="round"> <Icon type="upload" /> 重新上传 </Button> </Upload> {hasErrorData && hasSucData && ( <Button style={{ marginLeft: 12 }} shape="round" onClick={this.continueImport}> 继续上传 </Button> )} {hasSucData && ( <Button style={{ marginLeft: 12 }} shape="round" onClick={() => this.exportData(true)}> 正确信息导出 </Button> )} </Col> <Col span={12}> {hasErrorData && ( <Button shape="round" onClick={() => this.exportData(false)}> 错误信息导出 </Button> )} </Col> </Row> )} {this.state.isShow ? ( <div className={styles.attentionItem}> <p>导入事项</p> <p>1. 导入操作一次只能上传 1 个EXCEL文件。</p> <p>2. 导入文件最大文件大小上传 1 GB。</p> <p>3. 只能上传EXCEL文件(XLS, XLSX) </p> <p>4. 请将EXCEL文件的所有单元格格式设置为“文本”格式</p> </div> ) : ( <div className={styles.error}> <div className={styles.contentTable}> {/* 左边 */} <div className={styles.left}> <p>验证成功列表</p> <Table columns={column} rowKey={'rowKeyId'} dataSource={this.state.sucData} pagination={{ size: 'small', total: this.state.sucData.length, }} scroll={{ x: this.state.tableWidth }} style={{ overflow: 'auto' }} bordered={true} /> </div> {/* 右边 */} <div className={styles.right}> <p>验证错误列表</p> <Table columns={ column && column.length ? [ { title: '错误信息', dataIndex: 'errMsg', width: 150, }, ...column, ] : [] } style={{ overflow: 'auto' }} rowKey={'rowKeyId'} pagination={{ size: 'small', total: this.state.errData.length, }} scroll={{ x: this.state.tableWidth }} dataSource={this.state.errData} bordered={true} /> </div> </div> </div> )} </div> </Spin> </Modal> </span> ); } }