Browse Source

导入员工信息web代码和api提交

master
wuqun 2 years ago
parent
commit
6fc479767e
  1. 78
      api/app/lib/controllers/member/index.js
  2. 4
      api/app/lib/routes/member/index.js
  3. 13
      web/client/src/sections/humanAffairs/actions/personnelFiles.js
  4. 212
      web/client/src/sections/humanAffairs/containers/import-members-modal.js
  5. 10
      web/client/src/sections/humanAffairs/containers/table-input-rule.js
  6. 1
      web/client/src/utils/webapi.js

78
api/app/lib/controllers/member/index.js

@ -1,7 +1,7 @@
'use strict';
const moment = require('moment')
async function add (ctx) {
async function add(ctx) {
try {
const { models } = ctx.fs.dc;
const {
@ -44,7 +44,7 @@ async function add (ctx) {
}
}
async function edit (ctx) {
async function edit(ctx) {
try {
const { models } = ctx.fs.dc;
const {
@ -84,7 +84,7 @@ async function edit (ctx) {
}
}
async function searchPepMember (ctx) {
async function searchPepMember(ctx) {
try {
const { models } = ctx.fs.dc;
const { clickHouse } = ctx.app.fs
@ -160,7 +160,7 @@ async function searchPepMember (ctx) {
}
}
async function del (ctx) {
async function del(ctx) {
try {
const { models } = ctx.fs.dc;
const { pepUserId } = ctx.query
@ -183,7 +183,7 @@ async function del (ctx) {
}
}
async function list (ctx) {
async function list(ctx) {
try {
const { models } = ctx.fs.dc;
const { judgeHoliday } = ctx.app.fs.utils
@ -307,10 +307,78 @@ async function list (ctx) {
}
}
async function addMembersBulk(ctx) {
let errorMsg = { message: '批量添加员工信息失败' };
const transaction = await ctx.fs.dc.orm.transaction();
try {
const models = ctx.fs.dc.models;
const data = ctx.request.body;
let addMembers = [];
let editMembers = [];
let memberList = await models.Member.findAll({
attributes: ['pepUserId']
});
data.map(d => {
let exist = memberList.find(d => d.pepUserId == d.number);//项企的人员编号字段还没有
if (exist) {
editMembers.push(d);
} else {
addMembers.push(d);
}
})
//处理新增的
if (addMembers.length) {
await models.Member.bulkCreate(addMembers);
}
//处理编辑的
if (editMembers.length) {
for (let i in editMembers) {
let { pepUserId, name, idNumber, gender, birthday, nativePlace, marital,
politicsStatus, phoneNumber, workPlace, graduatedFrom, educationBackground, specialty, graduationDate,
hiredate, turnProbationPeriod, regularDate, dimissionDate, experienceYear, occupationalHistory } = editMembers[i];
let dataToUpdate = {
name,
idNumber,
gender,
birthday,
nativePlace,
marital,
politicsStatus,
phoneNumber,
workPlace,
graduatedFrom,
educationBackground,
specialty,
graduationDate,
hiredate,
turnProbationPeriod,
regularDate,
dimissionDate,
experienceYear,
occupationalHistory
}
await models.Member.update(dataToUpdate, { where: { pepUserId: pepUserId } });
}
}
await transaction.commit();
ctx.status = 204;
} catch (error) {
await transaction.rollback();
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
ctx.status = 400;
ctx.body = errorMsg;
}
}
module.exports = {
add,
edit,
del,
searchPepMember,
list,
addMembersBulk
};

4
api/app/lib/routes/member/index.js

@ -17,4 +17,8 @@ module.exports = function (app, router, opts) {
app.fs.api.logAttr['GET/member/list'] = { content: '查询人员列表', visible: true };
router.get('/member/list', member.list);
app.fs.api.logAttr['POST/add/members/bulk'] = { content: '导入员工信息', visible: true };
router.post('/add/members/bulk', member.addMembersBulk);
};

13
web/client/src/sections/humanAffairs/actions/personnelFiles.js

@ -2,7 +2,7 @@
import { ApiTable, basicAction } from '$utils'
export function getMemberSearch (query) {//搜索项企用户
export function getMemberSearch(query) {//搜索项企用户
return (dispatch) => basicAction({
type: "get",
dispatch: dispatch,
@ -12,4 +12,15 @@ export function getMemberSearch (query) {//搜索项企用户
msg: { option: "搜索项企用户" },
reducer: { name: "MemberSearch", params: { noClear: true } },
});
}
export function membersBulkAdd(values) {
return dispatch => basicAction({
type: 'post',
dispatch: dispatch,
actionType: 'MEMBER_BULK_ADD',
url: ApiTable.addMembersBulk,
data: values,
msg: { option: '导入员工信息' },
});
}

212
web/client/src/sections/humanAffairs/containers/import-members-modal.js

@ -1,17 +1,16 @@
'use strict';
import React, { useState } from 'react';
import { connect } from 'react-redux';
//import { Button, Input, Card, Modal, Upload, message } from 'antd';
import { Modal, Form, Button, Upload, Notification } from '@douyinfe/semi-ui';
import { Modal, Form, Button, Notification } from '@douyinfe/semi-ui';
import { IconUpload } from '@douyinfe/semi-icons';
//import { Request } from '@peace/utils'
//import request from 'superagent'
import cityData from '../components/city.json';
import XLSX from 'xlsx'
//import { userBulkAdd } from '../../actions'
//TODO 下载模板和上传文件读取
const ImportUser = props => {
const { user, dispatch, onCancel } = props
import { membersBulkAdd } from '../actions/personnelFiles'
import ExportJsonExcel from 'js-export-excel';
import { rule } from './table-input-rule'
//下载模板和上传文件读取
const ImportMembersModal = props => {
const { dispatch, onCancel } = props
const [msg, setMsg] = useState('')
const [loading, setLoading] = useState('')
const [postData, setPostData] = useState([])
@ -19,12 +18,13 @@ const ImportUser = props => {
const confirm = () => {
if (postData.length) {
setLoading(true)
// dispatch(userBulkAdd(postData, params.departmentId ? params.departmentId : null)).then(res => {
// if (res.success) {
// }
// setLoading(false)
// })
dispatch(membersBulkAdd(postData)).then(res => {
if (res.success) {
Notification.success('导入员工信息成功')
onCancel()
}
setLoading(false)
})
} else {
Notification.warn('没有数据可以提交,请上传数据文件')
}
@ -32,7 +32,11 @@ const ImportUser = props => {
const download = () => {
dldTemplate();
dldText("填写说明.txt", "12121212121212");
let str = "";
rule.forEach((v, i) => {
str += `${v}\r\n`
})
dldText("填写说明.txt", str);
}
const dldTemplate = () => {
@ -100,9 +104,38 @@ const ImportUser = props => {
}
}
}
request.send();
})
}
const judgePlace = (place) => {
let valid = true;
if (place.split('-').length == 1) {//判断籍贯
if (['北京市', '上海市', '天津市', '重庆市'].indexOf(place) == -1) {
valid = false;
}
} else if (place.split('-').length == 2) {
let province = place.split('-')[0];
let city = place.split('-')[1];
let existProvince = cityData.find(cd => cd.name == province);
if (!existProvince) {
valid = false;
} else {
let existCity = existProvince.children.find(cd => cd.name == city);
if (!existCity) {
valid = false;
}
}
} else {
valid = false;
}
return valid;
}
const judgeNull = (value) => {
return value ? String(value).trim().replace(/\s*/g, "") : null;
}
return (
<Modal
title="导入信息" visible={true}
@ -136,57 +169,122 @@ const ImportUser = props => {
error('请填写至少一行数据')
return
}
let postData = []
let postData = [];
const zmsz = /^[A-Za-z0-9]+$/;//字母数字组合
const pattern = /^1[3|4|5|6|7|8|9]\d{9}$/;
const sfz = /^[1-9]\d{5}(18|19|([23]\d))\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$/;//身份证
const ymd = /^((19|20)[0-9]{2})[\/\-]((0[1-9])|(1[0-2]))[\/\-]((0[1-9])|((1|2)[0-9])|(3[0-1]))$/;//年月日
const num0 = /^\d+$/;//正整数+0
for (let i = 0; i < res.length; i++) {
let d = res[i]
let number = String(d['人员编号']).trim();
let name = String(d['姓名']).trim();
let idNumber = String(d['证件照']).trim();
let gender = String(d['性别(男/女)']).trim();
let birthday = String(d['出生年月日(例2022/02/01)']).trim();
let nativePlace = String(d['籍贯']).trim();
let marital = String(d['婚育状态(已婚/未婚/已婚已育)']).trim();
let politicsStatus = String(d['政治面貌']).trim();
let phoneNumber = String(d['联系方式']).trim();
let workPlace = String(d['工作地点']).trim();
let graduatedFrom = String(d['毕业院校']).trim();
let educationBackground = String(d['学历']).trim();
let specialty = String(d['专业']).trim();
let graduationDate = String(d['毕业时间']).trim();
let hiredate = String(d['入职时间']).trim();
let turnProbationPeriod = String(d['转试用期时间']).trim();
let regularDate = String(d['转正时间']).trim();
let dimissionDate = String(d['离职日期']).trim();
let experienceYear = String(d['工作经验(年)']).trim();
let occupationalHistory = String(d['历史工作经历与职务']).trim();
//let account = String(d['账号']).trim()
if (!number || !name) {
error(`${i + 1} 行有空值,请填写后重新上传`)
let d = res[i];
let number = judgeNull(d['人员编号']);
let name = judgeNull(d['姓名']);
let idNumber = judgeNull(d['证件号']);
let gender = judgeNull(d['性别(男/女)']);
let birthday = judgeNull(d['出生年月日(例2022/02/01)']);
let nativePlace = judgeNull(d['籍贯']);
let marital = judgeNull(d['婚育状态(已婚/未婚/已婚已育)']);
let politicsStatus = judgeNull(d['政治面貌']);
let phoneNumber = judgeNull(d['联系方式']);
let workPlace = judgeNull(d['工作地点']);
let graduatedFrom = judgeNull(d['毕业院校']);
let educationBackground = judgeNull(d['学历']);
let specialty = judgeNull(d['专业']);
let graduationDate = judgeNull(d['毕业时间']);
let hiredate = judgeNull(d['入职时间']);
let turnProbationPeriod = judgeNull(d['转试用期时间']);
let regularDate = judgeNull(d['转正时间']);
let dimissionDate = judgeNull(d['离职日期']);
let experienceYear = judgeNull(d['工作经验(年)']);
let occupationalHistory = judgeNull(d['历史工作经历与职务']);
if (!number) {//人员编号不为空,唯一,字母和数字
error(`${i + 1}行人员编号为空,请填写`)
return
}
if (!sfz.test(idcard)) {
error(`${i + 1} 行证件号错误`)
if (postData.some(p => p.number == number)) {//人员编号 唯一
error(`${i + 1}行人员编号重复,请更改后重新上传`)
return
}
if (!zmsz.test(number)) {
error(`${i + 1}行人员编号错误,请填写字母和数字的组合`)
return
}
if (!name) {//姓名必填
error(`${i + 1}行姓名为空,请填写`)
return
}
if (!sfz.test(idNumber)) {
error(`${i + 1}行证件号错误`)
return
}
if (['男', '女'].indexOf(gender) == -1) {
error(`${i + 1}行性别错误`)
return
}
// if (!ymd.test(birthday)) {
// error(`第${i + 1}行出生年月日错误,请填写yyyy/mm/dd格式`)
// return
// }
let jgValid = judgePlace(nativePlace);
if (!jgValid) {
error(`${i + 1}行籍贯错误`)
return
}
if (['已婚', '未婚', '已婚已育'].indexOf(marital) == -1) {
error(`${i + 1}行婚育状态错误`)
return
}
if (['群众', '党员'].indexOf(politicsStatus) == -1) {
error(`${i + 1}行政治面貌错误`)
return
}
if (!pattern.test(phoneNumber)) {
error(`${i + 1} 行手机号码错误`)
error(`${i + 1}行联系方式错误`)
return
}
let wpValid = judgePlace(workPlace);
if (!wpValid) {
error(`${i + 1}行工作地点错误`)
return
}
if (name.length > 128 || account.length > 128) {
error(`${i + 1} 行数据字符长度大于 128,请更改后重新上传`)
if (['小学', '初中', '高中', '大专', '本科', '研究生', '博士'].indexOf(educationBackground) == -1) {
error(`${i + 1}行学历错误`)
return
}
if (postData.some(p => p.number == number)) {
error(`${i + 1} 行人员编号重复,请更改后重新上传`)
// if (!ymd.test(graduationDate)) {
// error(`第${i + 1}行毕业时间错误,请填写yyyy/mm/dd格式`)
// return
// }
// if (!ymd.test(hiredate)) {
// error(`第${i + 1}行入职时间错误,请填写yyyy/mm/dd格式`)
// return
// }
// if (!ymd.test(turnProbationPeriod)) {
// error(`第${i + 1}行转试用期时间错误,请填写yyyy/mm/dd格式`)
// return
// }
// if (!ymd.test(regularDate)) {
// error(`第${i + 1}行转正时间错误,请填写yyyy/mm/dd格式`)
// return
// }
// if (!ymd.test(dimissionDate)) {
// error(`第${i + 1}行离职日期错误,请填写yyyy/mm/dd格式`)
// return
// }
if (!num0.test(experienceYear)) {
error(`${i + 1}行工作经验(年)错误,请填写非负整数`)
return
}
postData.push({
name, phoneNumber, account
postData.push({//人员编号 待办todotodo
pepUserId: 555, name, idNumber, gender, birthday, nativePlace, marital,
politicsStatus, phoneNumber, workPlace, graduatedFrom, educationBackground, specialty, graduationDate,
hiredate, turnProbationPeriod, regularDate, dimissionDate, experienceYear, occupationalHistory
})
}
if (postData.length) {
@ -196,12 +294,12 @@ const ImportUser = props => {
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>
@ -211,10 +309,10 @@ const ImportUser = props => {
}
function mapStateToProps(state) {
const { auth, customizeList } = state;
const { auth } = state;
return {
user: auth.user,
}
}
export default connect(mapStateToProps)(ImportUser);
export default connect(mapStateToProps)(ImportMembersModal);

10
web/client/src/sections/humanAffairs/containers/table-input-rule.js

@ -0,0 +1,10 @@
export const rule = [
"人员编号:必填,唯一,数字和字母的组合;",
"姓名:必填,若导入数据的姓名与编号对应的项企姓名不同,将以项企数据为准;",
"日期:(年/月/日,例2022/02/01);",
"地址:格式为“省-市”,如“江苏省-镇江市”,直辖市直接显示市,如“北京市”;",
"政治面貌:党员或群众;",
"学历:小学、初中、高中、大专、本科、研究生或博士;",
"工作经验:0或正整数;",
"没有的数据可不填写。"
]

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

@ -18,6 +18,7 @@ export const ApiTable = {
//人事管理-人员档案
getMemberSearch: 'member/search',//搜索项企用户
addMembersBulk: 'add/members/bulk'
};
export const RouteTable = {

Loading…
Cancel
Save