ww664853070
2 years ago
22 changed files with 1284 additions and 636 deletions
@ -0,0 +1,220 @@ |
|||
'use strict'; |
|||
import React, { useState, useEffect } from 'react'; |
|||
import { connect } from 'react-redux'; |
|||
import moment from 'moment'; |
|||
import { Modal, Form, Button, Notification } from '@douyinfe/semi-ui'; |
|||
import { IconUpload } from '@douyinfe/semi-icons'; |
|||
import XLSX from 'xlsx'; |
|||
import { achievementColumnKeys } from '../../constants/index'; |
|||
//下载模板和上传文件读取 |
|||
const ImportAchieveModal = props => { |
|||
const { dispatch, actions, onCancel } = props; |
|||
const { businessManagement } = actions |
|||
const [msg, setMsg] = useState(''); |
|||
const [loading, setLoading] = useState(''); |
|||
const [postData, setPostData] = useState([]); |
|||
//初始化 |
|||
useEffect(() => { |
|||
}, []); |
|||
|
|||
const confirm = () => { |
|||
if (postData.length) { |
|||
setLoading(true) |
|||
dispatch(businessManagement.importAchieveDetails(postData)).then(res => { |
|||
if (res.success) { |
|||
onCancel() |
|||
} |
|||
setLoading(false) |
|||
}) |
|||
} else { |
|||
Notification.warning({ content: '没有数据可以提交,请上传数据文件', duration: 2 }) |
|||
} |
|||
} |
|||
|
|||
const dldCsvMb = () => { |
|||
//表头 |
|||
let head = []; |
|||
Object.keys(achievementColumnKeys).map(key => { |
|||
head.push(achievementColumnKeys[key]); |
|||
}) |
|||
head = head.join(',') + "\n"; |
|||
//数据 |
|||
//let data = 1 + ',' + 2 + ',' + 3 + ',' + 4 + ',' + 5 |
|||
let templateCsv = "data:text/csv;charset=utf-8,\ufeff" + head; |
|||
//创建一个a标签 |
|||
let link = document.createElement("a"); |
|||
//为a标签设置属性 |
|||
link.setAttribute("href", templateCsv); |
|||
link.setAttribute("download", "业绩明细表导入模板.csv"); |
|||
//点击a标签 |
|||
link.click(); |
|||
} |
|||
const download = () => { |
|||
dldCsvMb(); |
|||
} |
|||
|
|||
const fileLimit = '.csv'; |
|||
|
|||
const getFileBlob = (url) => { |
|||
return new Promise((resolve, reject) => { |
|||
let request = new XMLHttpRequest() |
|||
request.open("GET", url, true) |
|||
request.responseType = "blob" |
|||
request.onreadystatechange = e => { |
|||
if (request.readyState == 4) { |
|||
if (request.status == 200) { |
|||
if (window.FileReader) { |
|||
let reader = new FileReader(); |
|||
reader.readAsBinaryString(request.response); |
|||
reader.onload = event => { |
|||
try { |
|||
const { result } = event.target; |
|||
// 以二进制流方式读取得到整份excel表格对象 |
|||
const workbook = XLSX.read(result, { |
|||
type: "binary", |
|||
cellDates: true,//设为true,将天数的时间戳转为时间格式 |
|||
codepage: 936//解决了乱码问题 |
|||
}); |
|||
let data = []; // 存储获取到的数据 |
|||
// 遍历每张工作表进行读取(这里默认只读取第一张表) |
|||
for (const sheet in workbook.Sheets) { |
|||
if (workbook.Sheets.hasOwnProperty(sheet)) { |
|||
data = data.concat(XLSX.utils.sheet_to_json(workbook.Sheets[sheet])); |
|||
} |
|||
} |
|||
resolve(data);//导出数据 |
|||
} catch (e) { |
|||
reject("失败"); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
request.send(); |
|||
}) |
|||
} |
|||
|
|||
|
|||
const judgeNull = (value) => { |
|||
return value ? String(value).trim().replace(/\s*/g, "") : null; |
|||
} |
|||
|
|||
const judgeNullTime = (v) => { |
|||
//注意的点:xlsx将excel中的时间内容解析后,会小一天 |
|||
//如2020/8/1,xlsx会解析成 Fri Jul 31 2020 23:59:17 GMT+0800 小了43秒 |
|||
let a = new Date(v); |
|||
a.setTime(a.getTime() + 43 * 1000); |
|||
return v ? a : null; |
|||
} |
|||
|
|||
const judgeTimeValid = (time) => { |
|||
let valid = true; |
|||
if (!time) { |
|||
return valid;//可以不填 |
|||
} |
|||
const ymd = /^((19|20)[0-9]{2})[\/\-]((0[1-9])|(1[0-2]))[\/\-]((0[1-9])|((1|2)[0-9])|(3[0-1]))$/;//年月日 |
|||
if (time instanceof Date) { |
|||
let timeStr = moment(time).format('YYYY/MM/DD'); |
|||
if (!ymd.test(timeStr)) { |
|||
valid = false; |
|||
} |
|||
} else { |
|||
valid = false; |
|||
} |
|||
return valid; |
|||
} |
|||
|
|||
return ( |
|||
<Modal |
|||
title="导入" visible={true} |
|||
onOk={confirm} width={620} |
|||
confirmLoading={loading} |
|||
onCancel={() => { |
|||
setMsg('') |
|||
setLoading(false) |
|||
setPostData([]) |
|||
onCancel() |
|||
}} |
|||
> |
|||
<div style={{ borderBottom: '1px solid #DCDEE0', margin: '0px -24px' }}></div> |
|||
<Form> |
|||
<Form.Upload |
|||
label={'业绩明细表'} labelPosition='left' |
|||
action={'/'} accept={fileLimit} |
|||
maxSize={200} limit={1} |
|||
onRemove={(currentFile, fileList, fileItem) => { |
|||
setMsg(''); |
|||
setPostData([]); |
|||
}} |
|||
customRequest={(data) => { |
|||
const { file, onSuccess, onError } = data |
|||
getFileBlob(file.url).then(res => { |
|||
const error = (msg) => { |
|||
setMsg(msg) |
|||
onError({ message: msg }) |
|||
} |
|||
if (res.length > 1000) { |
|||
error('一次性上传数据行数应小于1000行,请分批上传') |
|||
return |
|||
} |
|||
if (!res.length) { |
|||
error('请填写至少一行数据') |
|||
return |
|||
} |
|||
let postData = []; |
|||
for (let i = 0; i < res.length; i++) { |
|||
let d = res[i]; |
|||
let obj = {}; |
|||
Object.keys(achievementColumnKeys).map(key => { |
|||
if (key === 'recConDate') {//收到合同日期 |
|||
obj[key] = judgeNullTime(d[achievementColumnKeys[key]]); |
|||
} else { |
|||
obj[key] = d[achievementColumnKeys[key]] || null; |
|||
} |
|||
}) |
|||
let tValid = judgeTimeValid(obj.recConDate); |
|||
if (!tValid) { |
|||
error(`第${i + 2}行【收到合同日期】错误,请填写yyyy/mm/dd格式`) |
|||
return |
|||
} |
|||
|
|||
if (obj.isApproval && ['是', '否'].indexOf(obj.isApproval) == -1) { |
|||
error(`第${i + 2}行【价格是否特批】错误,请填写是或否`) |
|||
return |
|||
} |
|||
|
|||
if (obj.reproducible && ['是', '否'].indexOf(obj.reproducible) == -1) { |
|||
error(`第${i + 2}行【是否可复制的业务路径】错误,请填写是或否`) |
|||
return |
|||
} |
|||
postData.push(obj); |
|||
} |
|||
setPostData(postData) |
|||
let msg = '文件解析完成,点击确定按钮上传保存!' |
|||
setMsg(msg) |
|||
onSuccess({ message: msg }) |
|||
}) |
|||
}}> |
|||
<Button icon={<IconUpload />} theme="light"> |
|||
请选择文件 |
|||
</Button> |
|||
</Form.Upload> |
|||
<span>{msg}</span> |
|||
<div style={{ color: '#ccc' }}>最大不超过200M,导入文件需与 |
|||
<span onClick={() => download()} style={{ cursor: 'pointer', color: '#0066FF' }}>导入模板</span> |
|||
一致</div> |
|||
</Form> |
|||
</Modal > |
|||
) |
|||
} |
|||
|
|||
function mapStateToProps(state) { |
|||
const { auth, global } = state; |
|||
return { |
|||
user: auth.user, |
|||
actions: global.actions, |
|||
} |
|||
} |
|||
|
|||
export default connect(mapStateToProps)(ImportAchieveModal); |
@ -0,0 +1,256 @@ |
|||
'use strict'; |
|||
import React, { useState, useEffect } from 'react'; |
|||
import { connect } from 'react-redux'; |
|||
import moment from 'moment'; |
|||
import { Modal, Form, Button, Notification } from '@douyinfe/semi-ui'; |
|||
import { IconUpload } from '@douyinfe/semi-icons'; |
|||
import XLSX from 'xlsx'; |
|||
import { backMoneyColumnKeys } from '../../constants/index'; |
|||
//下载模板和上传文件读取 |
|||
const ImportBackModal = props => { |
|||
const { dispatch, actions, onCancel } = props; |
|||
const { businessManagement } = actions |
|||
const [msg, setMsg] = useState(''); |
|||
const [loading, setLoading] = useState(''); |
|||
const [postData, setPostData] = useState([]); |
|||
const [uploadAble, setUploadAble] = useState(false); |
|||
const [allNumbers, setAllNumbers] = useState([]); |
|||
//初始化 |
|||
useEffect(() => { |
|||
dispatch(businessManagement.getReceivedNumbers()).then(r => { |
|||
if (r.success) { |
|||
setUploadAble(true); |
|||
setAllNumbers(r.payload.data); |
|||
} |
|||
}) |
|||
}, []); |
|||
|
|||
const confirm = () => { |
|||
if (postData.length) { |
|||
setLoading(true) |
|||
dispatch(businessManagement.importBackDetails(postData)).then(res => { |
|||
if (res.success) { |
|||
onCancel() |
|||
} |
|||
setLoading(false) |
|||
}) |
|||
} else { |
|||
Notification.warning({ content: '没有数据可以提交,请上传数据文件', duration: 2 }) |
|||
} |
|||
} |
|||
|
|||
const dldCsvMb = () => { |
|||
//表头 |
|||
let head = []; |
|||
Object.keys(backMoneyColumnKeys).map(key => { |
|||
head.push(backMoneyColumnKeys[key]); |
|||
}) |
|||
head = head.join(',') + "\n"; |
|||
//数据 |
|||
//let data = 1 + ',' + 2 + ',' + 3 + ',' + 4 + ',' + 5 |
|||
let templateCsv = "data:text/csv;charset=utf-8,\ufeff" + head; |
|||
//创建一个a标签 |
|||
let link = document.createElement("a"); |
|||
//为a标签设置属性 |
|||
link.setAttribute("href", templateCsv); |
|||
link.setAttribute("download", "回款明细表导入模板.csv"); |
|||
//点击a标签 |
|||
link.click(); |
|||
} |
|||
const download = () => { |
|||
dldCsvMb(); |
|||
} |
|||
|
|||
const fileLimit = '.csv'; |
|||
|
|||
const getFileBlob = (url) => { |
|||
return new Promise((resolve, reject) => { |
|||
let request = new XMLHttpRequest() |
|||
request.open("GET", url, true) |
|||
request.responseType = "blob" |
|||
request.onreadystatechange = e => { |
|||
if (request.readyState == 4) { |
|||
if (request.status == 200) { |
|||
if (window.FileReader) { |
|||
let reader = new FileReader(); |
|||
reader.readAsBinaryString(request.response); |
|||
reader.onload = event => { |
|||
try { |
|||
const { result } = event.target; |
|||
// 以二进制流方式读取得到整份excel表格对象 |
|||
const workbook = XLSX.read(result, { |
|||
type: "binary", |
|||
cellDates: true,//设为true,将天数的时间戳转为时间格式 |
|||
codepage: 936//解决了乱码问题 |
|||
}); |
|||
let data = []; // 存储获取到的数据 |
|||
// 遍历每张工作表进行读取(这里默认只读取第一张表) |
|||
for (const sheet in workbook.Sheets) { |
|||
if (workbook.Sheets.hasOwnProperty(sheet)) { |
|||
data = data.concat(XLSX.utils.sheet_to_json(workbook.Sheets[sheet])); |
|||
} |
|||
} |
|||
resolve(data);//导出数据 |
|||
} catch (e) { |
|||
reject("失败"); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
request.send(); |
|||
}) |
|||
} |
|||
|
|||
|
|||
const judgeNull = (value) => { |
|||
return value ? String(value).trim().replace(/\s*/g, "") : null; |
|||
} |
|||
|
|||
const judgeNullTime = (v) => { |
|||
//注意的点:xlsx将excel中的时间内容解析后,会小一天 |
|||
//如2020/8/1,xlsx会解析成 Fri Jul 31 2020 23:59:17 GMT+0800 小了43秒 |
|||
let a = new Date(v); |
|||
a.setTime(a.getTime() + 43 * 1000); |
|||
return v ? a : null; |
|||
} |
|||
|
|||
const judgeTimeValid = (time) => { |
|||
let valid = true; |
|||
if (!time) { |
|||
return valid;//可以不填 |
|||
} |
|||
const ymd = /^((19|20)[0-9]{2})[\/\-]((0[1-9])|(1[0-2]))[\/\-]((0[1-9])|((1|2)[0-9])|(3[0-1]))$/;//年月日 |
|||
if (time instanceof Date) { |
|||
let timeStr = moment(time).format('YYYY/MM/DD'); |
|||
if (!ymd.test(timeStr)) { |
|||
valid = false; |
|||
} |
|||
} else { |
|||
valid = false; |
|||
} |
|||
return valid; |
|||
} |
|||
|
|||
return ( |
|||
<Modal |
|||
title="导入" visible={true} |
|||
onOk={confirm} width={620} |
|||
confirmLoading={loading} |
|||
onCancel={() => { |
|||
setMsg('') |
|||
setLoading(false) |
|||
setPostData([]) |
|||
onCancel() |
|||
}} |
|||
> |
|||
<div style={{ borderBottom: '1px solid #DCDEE0', margin: '0px -24px' }}></div> |
|||
<Form> |
|||
<Form.Upload |
|||
label={'回款明细表'} labelPosition='left' |
|||
action={'/'} accept={fileLimit} |
|||
maxSize={200} limit={1} |
|||
onRemove={(currentFile, fileList, fileItem) => { |
|||
setMsg(''); |
|||
setPostData([]); |
|||
}} |
|||
customRequest={(data) => { |
|||
const { file, onSuccess, onError } = data |
|||
getFileBlob(file.url).then(res => { |
|||
const error = (msg) => { |
|||
setMsg(msg) |
|||
onError({ message: msg }) |
|||
} |
|||
if (res.length > 1000) { |
|||
error('一次性上传数据行数应小于1000行,请分批上传') |
|||
return |
|||
} |
|||
if (!res.length) { |
|||
error('请填写至少一行数据') |
|||
return |
|||
} |
|||
let postData = []; |
|||
const numPattern = /^\d+(\.\d+)?$/;//非负浮点数 判断金额 |
|||
for (let i = 0; i < res.length; i++) { |
|||
let d = res[i]; |
|||
let obj = {}; |
|||
Object.keys(backMoneyColumnKeys).map(key => { |
|||
if (['receivableDate', 'incomeConfirmdate'].indexOf(key) != -1) {//两个时间 |
|||
obj[key] = judgeNullTime(d[backMoneyColumnKeys[key]]); |
|||
} else { |
|||
obj[key] = d[backMoneyColumnKeys[key]] || null; |
|||
} |
|||
}) |
|||
//年度 序号 编号必填 |
|||
if (!obj.year || !obj.serialNo || !obj.number) { |
|||
error(`第${i + 2}行【年度】、【序号】、【编号】存在空值,请填写`) |
|||
return |
|||
} |
|||
let exist = allNumbers.find(m => m.number == obj.number);//数据库中 已有该编号 |
|||
if (exist) { |
|||
error(`第${i + 2}行的【编号】在系统中已存在`) |
|||
return |
|||
} |
|||
if (postData.some(p => p.number == obj.number)) {//编号 唯一 |
|||
error(`第${i + 2}行【编号】重复,请更改后重新上传`) |
|||
return |
|||
} |
|||
//金额 数字 |
|||
if (obj.amount && !numPattern.test(obj.amount)) { |
|||
error(`第${i + 2}行【合同金额】填写错误,需要为数字`) |
|||
return |
|||
} |
|||
if (obj.changeAmount && !numPattern.test(obj.changeAmount)) { |
|||
error(`第${i + 2}行【变更后合同金额】填写错误,需要为数字`) |
|||
return |
|||
} |
|||
if (obj.receivableAmount && !numPattern.test(obj.receivableAmount)) { |
|||
error(`第${i + 2}行【回款金额】填写错误,需要为数字`) |
|||
return |
|||
} |
|||
if (obj.remainConAmount && !numPattern.test(obj.remainConAmount)) { |
|||
error(`第${i + 2}行【剩余合同金额】填写错误,需要为数字`) |
|||
return |
|||
} |
|||
//时间 |
|||
let tValid = judgeTimeValid(obj.receivableDate); |
|||
if (!tValid) { |
|||
error(`第${i + 2}行回款日期错误,请填写yyyy/mm/dd格式`) |
|||
return |
|||
} |
|||
let cValid = judgeTimeValid(obj.incomeConfirmdate); |
|||
if (!cValid) { |
|||
error(`第${i + 2}行收入确认时间错误,请填写yyyy/mm/dd格式`) |
|||
return |
|||
} |
|||
postData.push(obj); |
|||
} |
|||
setPostData(postData) |
|||
let msg = '文件解析完成,点击确定按钮上传保存!' |
|||
setMsg(msg) |
|||
onSuccess({ message: msg }) |
|||
}) |
|||
}}> |
|||
<Button icon={<IconUpload />} theme="light" disabled={!uploadAble}> |
|||
请选择文件 |
|||
</Button> |
|||
</Form.Upload> |
|||
<span>{msg}</span> |
|||
<div style={{ color: '#ccc' }}>最大不超过200M,导入文件需与 |
|||
<span onClick={() => download()} style={{ cursor: 'pointer', color: '#0066FF' }}>导入模板</span> |
|||
一致</div> |
|||
</Form> |
|||
</Modal > |
|||
) |
|||
} |
|||
|
|||
function mapStateToProps(state) { |
|||
const { auth, global } = state; |
|||
return { |
|||
user: auth.user, |
|||
actions: global.actions, |
|||
} |
|||
} |
|||
|
|||
export default connect(mapStateToProps)(ImportBackModal); |
Loading…
Reference in new issue