From 731201073f8f7595cedc870fc74ccdff0fee0e70 Mon Sep 17 00:00:00 2001 From: zhouxin Date: Tue, 27 Dec 2022 15:53:10 +0800 Subject: [PATCH] =?UTF-8?q?(*)=E5=B2=97=E4=BD=8D=E8=AF=84=E7=BA=A7?= =?UTF-8?q?=E5=AF=BC=E5=85=A5=E6=A0=A1=E9=AA=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/importPositionRating.jsx | 213 ++++++++++++++++++ .../containers/positionRating.jsx | 73 +++--- 2 files changed, 253 insertions(+), 33 deletions(-) create mode 100644 web/client/src/sections/humanAffairs/components/importPositionRating.jsx diff --git a/web/client/src/sections/humanAffairs/components/importPositionRating.jsx b/web/client/src/sections/humanAffairs/components/importPositionRating.jsx new file mode 100644 index 0000000..0c5dba7 --- /dev/null +++ b/web/client/src/sections/humanAffairs/components/importPositionRating.jsx @@ -0,0 +1,213 @@ +'use strict'; +import React, { useState, useEffect } from 'react'; +import { Modal, Form, Button, Notification, Toast } from '@douyinfe/semi-ui'; +import { IconUpload } from '@douyinfe/semi-icons'; +import XLSX from 'xlsx'; + +const IMPORT_FIELD = { + userCode: { label: "员工编号" }, + ratingTime: { label: "评级时间" }, + theoryBasicScore: { label: "理论基础测评成绩" }, + theoryPassed: { label: "理论基础测评是否通过(≥60)" }, + totalScore: { label: "评级总成绩" }, + totalRatingPassed: { label: "评级总成绩是否通过(K≥60)" }, + technicalGrade: { label: "技术职级等级" } +} +const SHEETNAME = "岗位评级"; + +const ImportPositionRatingModal = props => { + const { onCancel, user, memberList } = props; + const [msg, setMsg] = useState(''); + const [loading, setLoading] = useState(''); + const [postData, setPostData] = useState([]); + const fileLimit = '.xlsx'; + //初始化 + + const confirm = () => { + if (postData.length) { + setLoading(true) + // dispatch(postAllPersonalTrainRecord(postData)).then(res => { + // if (res.success) { + // onCancel() + // dispatch(getPersonalTrainRecord(query)) + // } + // setLoading(false) + // }) + } else { + Notification.warning({ content: '没有数据可以提交,请上传数据文件', duration: 2 }) + } + } + + const download = () => { + const head = [Object.keys(IMPORT_FIELD).map(key => IMPORT_FIELD[key].label)]; + let workbook = { SheetNames: [SHEETNAME], Sheets: {} }; + workbook.Sheets[SHEETNAME] = XLSX.utils.aoa_to_sheet(head);//json转excel + workbook.Sheets[SHEETNAME]['!cols'] = [ + { wch: 13 }, { wch: 12 }, { wch: 18 }, { wch: 30 }, { wch: 11 }, + { wch: 30 }, { wch: 13 }]; + let wopts = { bookType: 'xlsx', type: 'buffer' };// 生成excel的配置项 + XLSX.writeFile(workbook, `${SHEETNAME}导入模板.xlsx`, wopts); + } + + 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 changeTime = (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 a; + } + + return ( + { + setMsg('') + setLoading(false) + setPostData([]) + onCancel() + }} + > +
+
+ { + 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(IMPORT_FIELD).map(key => { + obj[key] = d[IMPORT_FIELD[key].label] || null; + }) + let errmsg = null; + for (let item in obj) { + if (!obj[item]) { + errmsg = `第${i + 2}行【${IMPORT_FIELD[item].label}】为空,请填写`; + break; + } else { + if ("userCode" == item) { + const member = memberList.find(m => m.userCode == obj.userCode); + if (!member) { + errmsg = `第${i + 2}行【${IMPORT_FIELD[item].label}】${obj.userCode}未匹配到数据,请检查该员工是否添加`; + break; + } else if (postData.find(m => m.userCode == obj.userCode)) { + errmsg = `第${i + 2}行【${IMPORT_FIELD[item].label}】${obj.userCode}已存在,请检查`; + break; + } else { + obj.pepUserId = member.pepUserId; + } + } + else if (["theoryPassed", "totalRatingPassed"].includes(item)) { + if (!["是", "否"].includes(obj[item])) { + errmsg = `第${i + 2}行【${IMPORT_FIELD[item].label}】不合法,请修改`; + break; + } + } + else if ("ratingTime" == item) { + if (obj.ratingTime.toString().match(/^(\d{1,4})(-|.|\/)(\d{1,2})\2(\d{1,2})$/)) { + obj["ratingTime"] = changeTime(obj.ratingTime); + } else { + errmsg = `第${i + 2}行【${IMPORT_FIELD[item].label}】不合法,请修改`; + break; + } + } else if (["theoryBasicScore", "totalScore"].includes(item)) { + if (isNaN(obj[item])) { + errmsg = `第${i + 2}行【${IMPORT_FIELD[item].label}】不合法,请修改`; + break; + } + } + } + } + if (errmsg) { + error(errmsg); + return + } + postData.push(obj); + } + setPostData(postData) + let msg = '文件解析完成,点击确定按钮上传保存!' + setMsg(msg) + onSuccess({ message: msg }) + }) + }}> + + + {msg} +
最大不超过200M,导入文件需与 + download()} style={{ cursor: 'pointer', color: '#0066FF' }}>导入模板 + 一致
+
+
+ ) +} + +export default ImportPositionRatingModal; \ No newline at end of file diff --git a/web/client/src/sections/humanAffairs/containers/positionRating.jsx b/web/client/src/sections/humanAffairs/containers/positionRating.jsx index 63749a5..6e6519f 100644 --- a/web/client/src/sections/humanAffairs/containers/positionRating.jsx +++ b/web/client/src/sections/humanAffairs/containers/positionRating.jsx @@ -5,39 +5,28 @@ import { Table, Button, Pagination, Skeleton, Form, Tooltip } from '@douyinfe/se import { IconSearch } from '@douyinfe/semi-icons'; import { SkeletonScreen, Setup } from "$components"; import { UserAttribute } from '$utils'; +import ImportPositionRatingModal from '../components/importPositionRating'; import '../style.less'; const PositionRating = (props) => { - const { dispatch, actions, history, user, loading, socket, xqMembers } = props - + const { dispatch, actions, memberList, history, user, loading, socket, xqMembers } = props const { humanAffairs } = actions; + const { getMemberList, getPositionRating } = actions.humanAffairs; const form = useRef();//表单 let [archivesList, setArchivesList] = useState([]);//人员列表 - - const [setup, setSetup] = useState(false);//表格设置是否显现 - const [setupp, setSetupp] = useState([]);//实际显示的表格列表 - const [lookup, setLookup] = useState({});//搜索 const [query, setQuery] = useState({ limit: 10, page: 0 }); //页码信息 const [order, setOrder] = useState({ orderBy: 'hiredate', orderDirection: 'DESC' }); //页码信息 + const [importModal, setImportModal] = useState(false); const [limits, setLimits] = useState()//每页实际条数 const page = useRef(query.page);//哪一页 useEffect(() => { - getMemberSearchList()//查询人员列表 - }, [query, order]) + getMainList() + }, [query]) - function getMemberSearchList() {//查询人员列表 - let obj = lookup - if (lookup.entryTime?.length > 1) { - obj.hiredateStart = moment(lookup.entryTime[0]).format('YYYY-MM-DD') - obj.hiredateEnd = moment(lookup.entryTime[1]).format('YYYY-MM-DD') - } - else { - obj.hiredateStart = '' - obj.hiredateEnd = '' - } - dispatch(humanAffairs.getPositionRating({ ...obj, ...query, ...order })).then((res) => {//查询人员列表 + function getMainList() { + dispatch(getPositionRating(query)).then((res) => { if (res.success) { setArchivesList(res.payload?.data?.rows) setLimits(res.payload?.data?.count) @@ -120,32 +109,32 @@ const PositionRating = (props) => { }, { title: '评级时间', width: 100, - dataIndex: "time", - key: "time", + dataIndex: "ratingTime", + key: "ratingTime", render: (_, r, index) => -, }, { title: '理论基础测评成绩', width: 150, - dataIndex: "technicalGrade", - key: "technicalGrade", + dataIndex: "theoryBasicScore", + key: "theoryBasicScore", render: (_, r, index) => -, }, { title: '理论基础测评是否通过(≥60)', width: 150, - dataIndex: "technicalGrade", - key: "technicalGrade", + dataIndex: "theoryPassed", + key: "theoryPassed", render: (_, r, index) => -, }, { title: '评级总成绩', width: 150, - dataIndex: "technicalGrade", - key: "technicalGrade", + dataIndex: "totalScore", + key: "totalScore", render: (_, r, index) => -, }, { title: '评级总成绩是否通过(K≥60)', width: 150, - dataIndex: "technicalGrade", - key: "technicalGrade", + dataIndex: "totalRatingPassed", + key: "totalRatingPassed", render: (_, r, index) => -, }, { title: '技术职级等级', @@ -155,7 +144,6 @@ const PositionRating = (props) => { render: (_, r, index) => -, }]; - function handleRow(record, index) {//斑马条纹 // 给偶数行设置斑马纹 if (index % 2 === 0) { @@ -168,6 +156,12 @@ const PositionRating = (props) => { return {}; } } + const handleImportModal = () => { + if (!memberList.length) { + dispatch(getMemberList())//查询人员列表 + } + setImportModal(true); + } const scroll = useMemo(() => ({}), []); return ( <> @@ -188,6 +182,15 @@ const PositionRating = (props) => {
+
+
+
{ handleImportModal(); }} + > + 导入 +
+
+
表格中带有认证标识" @@ -260,6 +263,11 @@ const PositionRating = (props) => {
+ {importModal ? + { setImportModal(false) }} + /> : ''} @@ -267,13 +275,12 @@ const PositionRating = (props) => { } function mapStateToProps(state) { - const { auth, global, MemberSearch, webSocket } = state; + const { auth, global, MemberSearch, MemberList } = state; return { - // loading: members.isRequesting, user: auth.user, actions: global.actions, xqMembers: MemberSearch.data, - // socket: webSocket.socket + memberList: MemberList.data && MemberList.data.rows || [] }; }