Browse Source

(+)个人培训记录,列表,导入

master
ww664853070 2 years ago
parent
commit
352afdba46
  1. 4
      web/client/src/sections/humanAffairs/actions/index.js
  2. 72
      web/client/src/sections/humanAffairs/actions/personalTrainRecord.js
  3. 222
      web/client/src/sections/humanAffairs/containers/importPersonalTrainRecord.jsx
  4. 43
      web/client/src/sections/humanAffairs/containers/personalTrainRecord.jsx
  5. 3
      web/client/src/utils/webapi.js

4
web/client/src/sections/humanAffairs/actions/index.js

@ -4,9 +4,11 @@ import * as personnelFiles from './personnelFiles'
import * as employeeInformation from './employeeInformation' import * as employeeInformation from './employeeInformation'
import * as salesDistribution from './salesDistribution' import * as salesDistribution from './salesDistribution'
import * as departmentTrain from './departmentTrain' import * as departmentTrain from './departmentTrain'
import * as personalTrainRecord from './personalTrainRecord'
export default { export default {
...personnelFiles, ...personnelFiles,
...employeeInformation, ...employeeInformation,
...salesDistribution, ...salesDistribution,
...departmentTrain ...departmentTrain,
...personalTrainRecord
} }

72
web/client/src/sections/humanAffairs/actions/personalTrainRecord.js

@ -0,0 +1,72 @@
'use strict';
import { ApiTable, basicAction } from '$utils'
export function personalTrainRecordAll(values) {
return dispatch => basicAction({
type: 'post',
dispatch: dispatch,
actionType: 'SALES_MEMBER_BULK_ADD',
url: ApiTable.addSalesMemberBulk,
data: values,
msg: { option: '导入销售人员信息' },
});
}
export function getPersonalTrainRecord(query) {//查询
return (dispatch) => basicAction({
type: "get",
dispatch: dispatch,
actionType: "GET_PERSONAL_TRAIN_RECORD_LIST",
query: query,
url: `${ApiTable.getPersonalTrainRecord}`,
msg: { option: "查询个人培训记录列表" },
reducer: { name: "personalTrainRecordList", params: { noClear: true } },
});
}
// export function delSalesMember(data) {//删除
// let msg = ''
// if (data) {
// msg = data.msg
// }
// return (dispatch) =>
// basicAction({
// type: "del",
// query: data,
// dispatch: dispatch,
// actionType: "DEL_SALES_MEMBER",
// url: `${ApiTable.delSalesMember}`,
// msg: { option: msg }, //删除
// reducer: {},
// });
// }
// export function getMemberExport(query) {//导出员工信息
// return (dispatch) => basicAction({
// type: "get",
// dispatch: dispatch,
// actionType: "GET_MemberEXPORT",
// query: query,
// url: `${ApiTable.getMemberExport}`,
// msg: { option: "导出员工信息" },
// reducer: { name: "MemberExport", params: { noClear: true } },
// });
// }
// export function editSalesMember(data) {//更新
// let msg = ''
// if (data) {
// msg = data.msg
// }
// return (dispatch) =>
// basicAction({
// type: "put",
// dispatch: dispatch,
// data,
// actionType: "PUT_SALES_MEMBER",
// url: `${ApiTable.editSalesMember}`,
// msg: { option: msg }, //更新
// reducer: {},
// });
// }

222
web/client/src/sections/humanAffairs/containers/importPersonalTrainRecord.jsx

@ -0,0 +1,222 @@
'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'
//
const ImportPerformanceSummaryModal = props => {
const { dispatch, actions, onCancel } = props;
const { businessManagement } = actions
const [msg, setMsg] = useState('');
const [loading, setLoading] = useState('');
const [postData, setPostData] = useState([]);
let personalTrainRecord = { index: '序号', personalName: '姓名', departmentName: '部门', trainingType: '培训类型', topic: '课程主题', trainer: '培训讲师', trainDate: '培训时间', trainTime: '培训时长', trainMethod: '培训方式', attendanceScore: '考勤分数', appraisalMethod: '考核形式', appraisalScore: '考核分数', totalScore: '总分' }
//
const confirm = () => {
if (postData.length) {
setLoading(true)
// dispatch(businessManagement.importSalePerformance(postData)).then(res => {
// if (res.success) {
// onCancel()
// }
// setLoading(false)
// })
} else {
Notification.warning({ content: '没有数据可以提交,请上传数据文件', duration: 2 })
}
}
const dldCsvMb = () => {
//
let head = [];
Object.keys(personalTrainRecord).map(key => {
head.push(personalTrainRecord[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) => {
// //xlsxexcel
// //2020/8/1xlsx 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 = [];
let zzsPattern = /^[+]{0,1}(\d+)$/;//
for (let i = 0; i < res.length; i++) {
let d = res[i];
let obj = {};
Object.keys(personalTrainRecord).map(key => {
obj[key] = d[personalTrainRecord[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.repurchaseCount && !zzsPattern.test(obj.repurchaseCount)) {
// 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)(ImportPerformanceSummaryModal);

43
web/client/src/sections/humanAffairs/containers/personalTrainRecord.jsx

@ -3,13 +3,24 @@ import { connect } from 'react-redux';
import { Table, Pagination, Skeleton } from '@douyinfe/semi-ui'; import { Table, Pagination, Skeleton } from '@douyinfe/semi-ui';
import { SkeletonScreen } from "$components"; import { SkeletonScreen } from "$components";
import '../style.less' import '../style.less'
import ImportPersonalTrainRecord from './importPersonalTrainRecord'
const PersonalTrainRecord = (props) => { const PersonalTrainRecord = (props) => {
const { dispatch, actions } = props const { dispatch, actions } = props
const [limits, setLimits] = useState()// const [limits, setLimits] = useState()//
const [query, setQuery] = useState({ limit: 10, page: 0 }); // const [query, setQuery] = useState({ limit: 10, page: 0 }); //
const [importModalV, setImportModalV] = useState(false); //
console.log(actions, '================')
useEffect(() => {
const { getPersonalTrainRecord } = actions.humanAffairs
dispatch(getPersonalTrainRecord(query))
}, []);
const [tableData, setTableData] = useState([{ id: 1, personalName: '张三', departmentName: '营销第一军团', trainingType: '内部培训', topic: '新员工入职培训', trainer: '李四', trainDate: '2022-12-30 18:30', trainTime: '1小时', trainMethod: '线下', attendanceScore: '95', appraisalMethod: '笔试', appraisalScore: '86', totalScore: '80' }, { id: 2, personalName: '张三', departmentName: '营销第一军团', trainingType: '内部培训', topic: '新员工入职培训', trainer: '李四', trainDate: '2022-12-30 18:30', trainTime: '1小时', trainMethod: '线下', attendanceScore: '95', appraisalMethod: '笔试', appraisalScore: '86', totalScore: '80' }, { id: 3, personalName: '张三', departmentName: '营销第一军团', trainingType: '内部培训', topic: '新员工入职培训', trainer: '李四', trainDate: '2022-12-30 18:30', trainTime: '1小时', trainMethod: '线下', attendanceScore: '95', appraisalMethod: '笔试', appraisalScore: '86', totalScore: '80' }, { id: 4, personalName: '张三', departmentName: '营销第一军团', trainingType: '内部培训', topic: '新员工入职培训', trainer: '李四', trainDate: '2022-12-30 18:30', trainTime: '1小时', trainMethod: '线下', attendanceScore: '95', appraisalMethod: '笔试', appraisalScore: '86', totalScore: '80' }]); const [tableData, setTableData] = useState([{ id: 1, personalName: '张三', departmentName: '营销第一军团', trainingType: '内部培训', topic: '新员工入职培训', trainer: '李四', trainDate: '2022-12-30 18:30', trainTime: '1小时', trainMethod: '线下', attendanceScore: '95', appraisalMethod: '笔试', appraisalScore: '86', totalScore: '80' }, { id: 2, personalName: '张三', departmentName: '营销第一军团', trainingType: '内部培训', topic: '新员工入职培训', trainer: '李四', trainDate: '2022-12-30 18:30', trainTime: '1小时', trainMethod: '线下', attendanceScore: '95', appraisalMethod: '笔试', appraisalScore: '86', totalScore: '80' }, { id: 3, personalName: '张三', departmentName: '营销第一军团', trainingType: '内部培训', topic: '新员工入职培训', trainer: '李四', trainDate: '2022-12-30 18:30', trainTime: '1小时', trainMethod: '线下', attendanceScore: '95', appraisalMethod: '笔试', appraisalScore: '86', totalScore: '80' }, { id: 4, personalName: '张三', departmentName: '营销第一军团', trainingType: '内部培训', topic: '新员工入职培训', trainer: '李四', trainDate: '2022-12-30 18:30', trainTime: '1小时', trainMethod: '线下', attendanceScore: '95', appraisalMethod: '笔试', appraisalScore: '86', totalScore: '80' }]);
useEffect(() => {
}, [])
function handleRow(record, index) {// function handleRow(record, index) {//
if (index % 2 === 0) { if (index % 2 === 0) {
@ -23,25 +34,6 @@ const PersonalTrainRecord = (props) => {
} }
} }
const download = () => {
//
let head = [];
let headData = { index: '序号', personalName: '姓名', departmentName: '部门', trainingType: '培训类型', topic: '课程主题', trainer: '培训讲师', trainDate: '培训时间', trainTime: '培训时长', trainMethod: '培训方式', attendanceScore: '考勤分数', appraisalMethod: '考核形式', appraisalScore: '考核分数', totalScore: '总分' }
Object.keys(headData).map(key => {
head.push(headData[key]);
})
head = head.join(',') + "\n";
//
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 columns = [{ const columns = [{
title: '序号', title: '序号',
dataIndex: 'id', dataIndex: 'id',
@ -114,7 +106,7 @@ const PersonalTrainRecord = (props) => {
width: 120 width: 120
}]; }];
const scroll = useMemo(() => ({}), []);
return ( return (
<> <>
@ -139,13 +131,10 @@ const PersonalTrainRecord = (props) => {
<div style={{ display: 'flex', margin: '16px 0px', justifyContent: 'space-between' }}> <div style={{ display: 'flex', margin: '16px 0px', justifyContent: 'space-between' }}>
<div style={{ display: 'flex', marginRight: 20 }}> <div style={{ display: 'flex', marginRight: 20 }}>
<div style={{ padding: '6px 20px', background: '#0F7EFB', color: '#FFFFFF', fontSize: 14, cursor: "pointer" }} <div style={{ padding: '6px 20px', background: '#0F7EFB', color: '#FFFFFF', fontSize: 14, cursor: "pointer" }}
// onClick={() => { setImportModalV(true); }} onClick={() => { setImportModalV(true); }}
> >
导入 导入
</div> </div>
<div style={{ padding: '6px 20px', background: '#00BA85', color: '#FFFFFF', fontSize: 14, marginLeft: 18, cursor: "pointer" }} onClick={() => download()}>
导入模板下载
</div>
</div> </div>
</div> </div>
<div style={{ marginTop: 20 }}> <div style={{ marginTop: 20 }}>
@ -190,6 +179,12 @@ const PersonalTrainRecord = (props) => {
</div> </div>
</div> </div>
</div> </div>
{
importModalV ? <ImportPersonalTrainRecord
onCancel={() => {
setImportModalV(false);
}} /> : ''
}
</div> </div>
</> </>
) )

3
web/client/src/utils/webapi.js

@ -42,6 +42,9 @@ export const ApiTable = {
getDepartmentTrainRecord: 'department/train/record/list', getDepartmentTrainRecord: 'department/train/record/list',
importDepartmentTrainRecord: 'department/train/record/bulk', importDepartmentTrainRecord: 'department/train/record/bulk',
modifyDepartmentTrainRecord: 'department/train/record/modify', modifyDepartmentTrainRecord: 'department/train/record/modify',
//个人培训记录
getPersonalTrainRecord: 'personal/train/record/list',
}; };
export const RouteTable = { export const RouteTable = {
apiRoot: "/api/root", apiRoot: "/api/root",

Loading…
Cancel
Save