28 changed files with 1658 additions and 174 deletions
@ -0,0 +1,336 @@ |
|||
'use strict'; |
|||
const moment = require('moment') |
|||
const fs = require('fs'); |
|||
|
|||
async function salesList(ctx) { |
|||
try { |
|||
const { memberList, packageUserData } = ctx.app.fs.utils |
|||
const { |
|||
keywordTarget, keyword, limit, page, state, |
|||
hiredateStart, hiredateEnd, marital, native, workPlace, |
|||
orderBy, orderDirection, placeSearch |
|||
} = ctx.query |
|||
|
|||
const userRes = await memberList({ |
|||
keywordTarget, keyword, limit: '', page: '', state, |
|||
hiredateStart, hiredateEnd, marital, native, workPlace, |
|||
orderBy, orderDirection, |
|||
nowAttendanceTime: true |
|||
}) |
|||
|
|||
let { packageUser: members } = await packageUserData(userRes, { |
|||
state: true, |
|||
}) |
|||
|
|||
const { models } = ctx.fs.dc; |
|||
|
|||
let mIds = members.map(m => m.pepUserId); |
|||
|
|||
|
|||
let where = { |
|||
del: false, |
|||
pepUserId: { $in: mIds } |
|||
} |
|||
if (placeSearch) { |
|||
where.$or = [{ |
|||
provinces: { $like: `%${placeSearch}%` } |
|||
}, { |
|||
cities: { $like: `%${placeSearch}%` } |
|||
}] |
|||
} |
|||
let res = await models.SalesDistribution.findAndCountAll({ |
|||
where: where, |
|||
offset: Number(page) * Number(limit), |
|||
limit: Number(limit), |
|||
order: [['id', 'DESC']] |
|||
}) |
|||
|
|||
let rslt = [] |
|||
res.rows.map(d => { |
|||
let info = members.find(m => m.pepUserId == d.dataValues.pepUserId); |
|||
let item = { |
|||
name: info.userName, |
|||
userCode: info.userCode, |
|||
post: info.userPost, |
|||
department: info.departmrnt, |
|||
hireDate: info.hiredate,//入职时间
|
|||
regularDate: info.regularDate,//转正时间
|
|||
...d.dataValues |
|||
} |
|||
rslt.push(item); |
|||
}) |
|||
ctx.status = 200; |
|||
ctx.body = { |
|||
count: res.count, |
|||
rows: rslt |
|||
}; |
|||
} catch (error) { |
|||
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`); |
|||
ctx.status = 400; |
|||
ctx.body = { |
|||
message: typeof error == 'string' ? error : undefined |
|||
} |
|||
} |
|||
} |
|||
|
|||
async function add(ctx) { |
|||
try { |
|||
const { models } = ctx.fs.dc; |
|||
const { pepUserId, provinces, cities, businessLines } = ctx.request.body |
|||
|
|||
const existRes = await models.SalesDistribution.findOne({ |
|||
where: { pepUserId } |
|||
}) |
|||
|
|||
if (existRes && !existRes.del) { |
|||
throw '当前销售人员信息已存在' |
|||
} |
|||
|
|||
let storageData = { pepUserId, provinces, cities, businessLines, del: false } |
|||
if (existRes && existRes.del) { |
|||
await models.SalesDistribution.update(storageData, { |
|||
where: { pepUserId } |
|||
}) |
|||
} else { |
|||
await models.SalesDistribution.create(storageData) |
|||
} |
|||
ctx.status = 204; |
|||
} catch (error) { |
|||
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`); |
|||
ctx.status = 400; |
|||
ctx.body = { |
|||
message: typeof error == 'string' ? error : undefined |
|||
} |
|||
} |
|||
} |
|||
|
|||
async function edit(ctx) { |
|||
try { |
|||
const { models } = ctx.fs.dc; |
|||
const { pepUserId, provinces, cities, businessLines } = ctx.request.body |
|||
|
|||
const existRes = await models.SalesDistribution.findOne({ |
|||
where: { pepUserId } |
|||
}) |
|||
|
|||
if (!existRes) { |
|||
throw '当前销售人员信息不存在' |
|||
} |
|||
|
|||
let storageData = { pepUserId, provinces, cities, businessLines, del: false } |
|||
|
|||
await models.SalesDistribution.update(storageData, { |
|||
where: { |
|||
pepUserId: pepUserId |
|||
} |
|||
}) |
|||
|
|||
ctx.status = 204; |
|||
} catch (error) { |
|||
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`); |
|||
ctx.status = 400; |
|||
ctx.body = { |
|||
message: typeof error == 'string' ? error : undefined |
|||
} |
|||
} |
|||
} |
|||
|
|||
async function del(ctx) { |
|||
try { |
|||
const { models } = ctx.fs.dc; |
|||
const { pepUserId } = ctx.query |
|||
|
|||
await models.SalesDistribution.update({ |
|||
del: true, |
|||
}, { |
|||
where: { |
|||
pepUserId, |
|||
} |
|||
}) |
|||
ctx.status = 204; |
|||
} catch (error) { |
|||
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`); |
|||
ctx.status = 400; |
|||
ctx.body = { |
|||
message: typeof error == 'string' ? error : undefined |
|||
} |
|||
} |
|||
} |
|||
|
|||
// async function exportData(ctx) {
|
|||
// try {
|
|||
// const { models } = ctx.fs.dc;
|
|||
// const { clickHouse, opts: { qiniu } } = ctx.app.fs
|
|||
// const { simpleExcelDown, memberList, packageUserData } = ctx.app.fs.utils
|
|||
// const {
|
|||
// keywordTarget, keyword, limit, page, state, keys = '',
|
|||
// hiredateStart, hiredateEnd, marital, native, workPlace,
|
|||
// orderBy, orderDirection,
|
|||
// } = ctx.query
|
|||
|
|||
// const userRes = await memberList({
|
|||
// keywordTarget, keyword, limit, page, state,
|
|||
// hiredateStart, hiredateEnd, marital, native, workPlace,
|
|||
// orderBy, orderDirection,
|
|||
// nowAttendanceTime: true
|
|||
// })
|
|||
|
|||
// const tableAttributes = models['Member'].tableAttributes
|
|||
// const optionKeys = keys.split(',')
|
|||
|
|||
// let { packageUser: exportD, pepUserIds } = await packageUserData(userRes)
|
|||
|
|||
// let preHeader = [{
|
|||
// title: '员工编号',
|
|||
// key: 'userCode',
|
|||
// }, {
|
|||
// title: '姓名',
|
|||
// key: 'userName',
|
|||
// }]
|
|||
// let header = [].concat(preHeader)
|
|||
// for (let k in tableAttributes) {
|
|||
// const comment = tableAttributes[k].comment
|
|||
// if (k != 'id' && k != 'pepUserId' && comment) {
|
|||
// if ([].includes(k)) {
|
|||
// // 截住不想导出的字段
|
|||
// continue
|
|||
// }
|
|||
// header.push({
|
|||
// title: comment || '-',
|
|||
// key: k,
|
|||
// // index: tableAttributes[k].index,
|
|||
// })
|
|||
// }
|
|||
// }
|
|||
|
|||
// if (optionKeys.includes('overtimeStatistic')) {
|
|||
// header = header.concat([{
|
|||
// title: '累计加班次数',
|
|||
// key: 'overTimeCount',
|
|||
// }, {
|
|||
// title: '累计加班总时长 / h',
|
|||
// key: 'overTimeDuration',
|
|||
// },])
|
|||
// }
|
|||
// if (optionKeys.includes('vacateStatistic')) {
|
|||
// header = header.concat([{
|
|||
// title: '累计请假次数',
|
|||
// key: 'vacateCount',
|
|||
// }, {
|
|||
// title: '累计请假总时长 / h',
|
|||
// key: 'vacateDuration',
|
|||
// },])
|
|||
// }
|
|||
|
|||
// // 查询累计加班次数及总时长
|
|||
// const statisticOvertimeRes = await clickHouse.hr.query(`
|
|||
// SELECT
|
|||
// pep_user_id AS pepUserId,
|
|||
// count(id) AS count,
|
|||
// sum(duration) AS duration
|
|||
// FROM
|
|||
// overtime
|
|||
// WHERE pep_user_id IN (${pepUserIds.join(',')})
|
|||
// GROUP BY pep_user_id
|
|||
// `).toPromise()
|
|||
|
|||
// const statisticVacateRes = await clickHouse.hr.query(`
|
|||
// SELECT
|
|||
// pep_user_id AS pepUserId,
|
|||
// count(id) AS count,
|
|||
// sum(duration) AS duration
|
|||
// FROM
|
|||
// vacate
|
|||
// WHERE pep_user_id IN (${pepUserIds.join(',')})
|
|||
// GROUP BY pep_user_id
|
|||
// `).toPromise()
|
|||
|
|||
// exportD.forEach(d => {
|
|||
// d.departmrnt = d.departmrnt.map(dep => dep.name).join('、')
|
|||
// d.role = d.role.map(r => r.name).join('、')
|
|||
|
|||
// d.idPhoto ? d.idPhoto = qiniu.domain + '/' + d.idPhoto : ''
|
|||
// d.vitae ? d.vitae = qiniu.domain + '/' + d.vitae : ''
|
|||
|
|||
// const corOverTime = statisticOvertimeRes.find(so => so.pepUserId == d.pepUserId)
|
|||
// d.overTimeCount = corOverTime ? corOverTime.count : 0
|
|||
// d.overTimeDuration = corOverTime ? (corOverTime.duration / 3600).toFixed(1) : 0
|
|||
// const corVacate = statisticVacateRes.find(so => so.pepUserId == d.pepUserId)
|
|||
// d.vacateCount = corVacate ? corVacate.count : 0
|
|||
// d.vacateDuration = corVacate ? (corVacate.duration / 3600).toFixed(1) : 0
|
|||
// })
|
|||
|
|||
// const fileName = `人员信息_${moment().format('YYYYMMDDHHmmss')}` + '.csv'
|
|||
// const filePath = await simpleExcelDown({ data: exportD, header, fileName: fileName })
|
|||
// const fileData = fs.readFileSync(filePath);
|
|||
|
|||
// ctx.status = 200;
|
|||
// ctx.set('Content-Type', 'application/x-xls');
|
|||
// ctx.set('Content-disposition', 'attachment; filename=' + encodeURI(fileName));
|
|||
// ctx.body = fileData;
|
|||
// } catch (error) {
|
|||
// ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
|
|||
// ctx.status = 400;
|
|||
// ctx.body = {
|
|||
// message: typeof error == 'string' ? error : undefined
|
|||
// }
|
|||
// }
|
|||
// }
|
|||
|
|||
async function addSalesMemberBulk(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 addArr = []; |
|||
let editArr = []; |
|||
let list = await models.SalesDistribution.findAll({ |
|||
attributes: ['pepUserId'] |
|||
}); |
|||
data.map(d => { |
|||
let exist = list.find(m => m.pepUserId == d.pepUserId);//项企的人员编号字段还没有
|
|||
if (exist) { |
|||
editArr.push(d); |
|||
} else { |
|||
addArr.push(d); |
|||
} |
|||
}) |
|||
|
|||
//处理新增的
|
|||
if (addArr.length) { |
|||
await models.SalesDistribution.bulkCreate(addArr); |
|||
} |
|||
|
|||
//处理编辑的
|
|||
if (editArr.length) { |
|||
for (let i in editArr) { |
|||
let { pepUserId, provinces, cities, businessLines, del = false } = editArr[i]; |
|||
|
|||
let dataToUpdate = { |
|||
provinces, |
|||
cities, |
|||
businessLines, |
|||
del |
|||
} |
|||
await models.SalesDistribution.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 = { |
|||
salesList, |
|||
add, |
|||
edit, |
|||
del, |
|||
//exportData,
|
|||
addSalesMemberBulk, |
|||
} |
@ -0,0 +1,71 @@ |
|||
/* eslint-disable*/ |
|||
|
|||
'use strict'; |
|||
|
|||
module.exports = dc => { |
|||
const DataTypes = dc.ORM; |
|||
const sequelize = dc.orm; |
|||
const SalesDistribution = sequelize.define("salesDistribution", { |
|||
id: { |
|||
type: DataTypes.INTEGER, |
|||
allowNull: false, |
|||
defaultValue: null, |
|||
comment: null, |
|||
primaryKey: true, |
|||
field: "id", |
|||
autoIncrement: true, |
|||
unique: "sales_distribution_id_uindex" |
|||
}, |
|||
pepUserId: { |
|||
type: DataTypes.INTEGER, |
|||
allowNull: false, |
|||
defaultValue: null, |
|||
comment: null, |
|||
primaryKey: false, |
|||
field: "pep_user_id", |
|||
autoIncrement: false |
|||
}, |
|||
provinces: { |
|||
type: DataTypes.STRING, |
|||
allowNull: false, |
|||
defaultValue: null, |
|||
comment: null, |
|||
primaryKey: false, |
|||
field: "provinces", |
|||
autoIncrement: false |
|||
}, |
|||
cities: { |
|||
type: DataTypes.STRING, |
|||
allowNull: true, |
|||
defaultValue: null, |
|||
comment: null, |
|||
primaryKey: false, |
|||
field: "cities", |
|||
autoIncrement: false |
|||
}, |
|||
del: { |
|||
type: DataTypes.BOOLEAN, |
|||
allowNull: true, |
|||
defaultValue: null, |
|||
comment: null, |
|||
primaryKey: false, |
|||
field: "del", |
|||
autoIncrement: false |
|||
}, |
|||
businessLines: { |
|||
type: DataTypes.STRING, |
|||
allowNull: true, |
|||
defaultValue: null, |
|||
comment: null, |
|||
primaryKey: false, |
|||
field: "business_lines", |
|||
autoIncrement: false |
|||
} |
|||
}, { |
|||
tableName: "sales_distribution", |
|||
comment: "", |
|||
indexes: [] |
|||
}); |
|||
dc.models.SalesDistribution = SalesDistribution; |
|||
return SalesDistribution; |
|||
}; |
@ -0,0 +1,24 @@ |
|||
'use strict'; |
|||
|
|||
const salesDistribution = require('../../controllers/salesDistribution'); |
|||
|
|||
module.exports = function (app, router, opts) { |
|||
|
|||
app.fs.api.logAttr['GET/sales/member/list'] = { content: '查询销售人员列表', visible: true }; |
|||
router.get('/sales/member/list', salesDistribution.salesList); |
|||
|
|||
app.fs.api.logAttr['POST/sales/member/add'] = { content: '添加销售人员信息', visible: true }; |
|||
router.post('/sales/member/add', salesDistribution.add); |
|||
|
|||
app.fs.api.logAttr['PUT/sales/member/modify'] = { content: '编辑销售人员信息', visible: true }; |
|||
router.put('/sales/member/modify', salesDistribution.edit); |
|||
|
|||
app.fs.api.logAttr['DEL/sales/member/del'] = { content: '删除销售人员信息', visible: true }; |
|||
router.del('/sales/member/del', salesDistribution.del); |
|||
|
|||
app.fs.api.logAttr['POST/add/sales/members/bulk'] = { content: '导入销售人员信息', visible: true }; |
|||
router.post('/add/sales/members/bulk', salesDistribution.addSalesMemberBulk); |
|||
|
|||
// app.fs.api.logAttr['GET/sales/members/export'] = { content: '导出销售人员信息', visible: true };
|
|||
// router.get('/sales/members/export', salesDistribution.exportData);
|
|||
}; |
@ -0,0 +1,11 @@ |
|||
|
|||
|
|||
CREATE TABLE sales_distribution |
|||
( |
|||
id serial PRIMARY KEY NOT NULL, |
|||
pep_user_id int NOT NULL, |
|||
provinces text NOT NULL, |
|||
cities text, |
|||
del boolean DEFAULT false NULL |
|||
); |
|||
CREATE UNIQUE INDEX sales_distribution_id_uindex ON sales_distribution (id); |
@ -0,0 +1,4 @@ |
|||
|
|||
|
|||
alter table sales_distribution |
|||
add "business_lines" text; |
@ -0,0 +1,89 @@ |
|||
'use strict'; |
|||
|
|||
import { ApiTable, basicAction } from '$utils' |
|||
|
|||
export function addSalesMemberBulk(values) { |
|||
return dispatch => basicAction({ |
|||
type: 'post', |
|||
dispatch: dispatch, |
|||
actionType: 'SALES_MEMBER_BULK_ADD', |
|||
url: ApiTable.addSalesMemberBulk, |
|||
data: values, |
|||
msg: { option: '导入销售人员信息' }, |
|||
}); |
|||
} |
|||
|
|||
export function postSalesMember(data) {//添加/编辑
|
|||
let msg = '' |
|||
if (data) { |
|||
msg = data.msg |
|||
} |
|||
return (dispatch) => |
|||
basicAction({ |
|||
type: "post", |
|||
dispatch: dispatch, |
|||
data, |
|||
actionType: "POST_SALES_MEMBER", |
|||
url: `${ApiTable.addSalesMember}`, |
|||
msg: { option: msg }, //添加/编辑
|
|||
reducer: { name: "" }, |
|||
}); |
|||
} |
|||
|
|||
export function getSalesList(query) {//查询
|
|||
return (dispatch) => basicAction({ |
|||
type: "get", |
|||
dispatch: dispatch, |
|||
actionType: "GET_SALES_MENBER_LIST", |
|||
query: query, |
|||
url: `${ApiTable.getSalesList}`, |
|||
msg: { option: "查询销售人员列表" }, |
|||
reducer: { name: "SalesMemberList", 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: {}, |
|||
}); |
|||
} |
@ -0,0 +1,272 @@ |
|||
'use strict'; |
|||
import React, { useState, useEffect } from 'react'; |
|||
import { connect } from 'react-redux'; |
|||
import { Modal, Form, Button, Notification } from '@douyinfe/semi-ui'; |
|||
import { IconUpload } from '@douyinfe/semi-icons'; |
|||
import cityData from '../../components/city.json'; |
|||
import XLSX from 'xlsx' |
|||
//下载模板和上传文件读取
|
|||
const ImportSalersModal = props => { |
|||
const { dispatch, actions, onCancel, rzMembers } = props |
|||
const { humanAffairs } = actions; |
|||
const [msg, setMsg] = useState('') |
|||
const [loading, setLoading] = useState('') |
|||
const [postData, setPostData] = useState([]) |
|||
const [allProvinces, setAllProvinces] = useState([]) |
|||
//初始化
|
|||
useEffect(() => { |
|||
let allProvinces = [];//所有省
|
|||
cityData.map(cd => { |
|||
allProvinces.push(cd.name); |
|||
}); |
|||
setAllProvinces(allProvinces); |
|||
}, []); |
|||
|
|||
const confirm = () => { |
|||
if (postData.length) { |
|||
setLoading(true) |
|||
dispatch(humanAffairs.addSalesMemberBulk(postData)).then(res => { |
|||
if (res.success) { |
|||
onCancel() |
|||
} |
|||
setLoading(false) |
|||
}) |
|||
} else { |
|||
Notification.warning({ content: '没有数据可以提交,请上传数据文件', duration: 2 }) |
|||
} |
|||
} |
|||
|
|||
const dldCsvMb = () => { |
|||
//表头
|
|||
let head = "员工编号,姓名,销售区域(省/直辖市),销售区域(市),业务线\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 = () => { |
|||
//dldTemplate();
|
|||
dldCsvMb(); |
|||
// let str = "";
|
|||
// rule.forEach((v, i) => {
|
|||
// str += `${v}\r\n`
|
|||
// })
|
|||
// dldText("填写说明.txt", str);
|
|||
} |
|||
|
|||
// const dldText = (filename, text) => {
|
|||
// var element = document.createElement('a');
|
|||
// element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(text));
|
|||
// element.setAttribute('download', filename);
|
|||
|
|||
// element.style.display = 'none';
|
|||
// document.body.appendChild(element);
|
|||
|
|||
// element.click();
|
|||
// document.body.removeChild(element);
|
|||
// }
|
|||
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 judgeProvinces = (provinces) => { |
|||
let noMark = 0; |
|||
provinces?.split('、')?.map(p => { |
|||
if (allProvinces.indexOf(p) == -1) { |
|||
noMark++ |
|||
} |
|||
}) |
|||
return !noMark; |
|||
} |
|||
|
|||
const judgeCities = (provinces, cities) => { |
|||
if (!cities) {//可以不填
|
|||
return true; |
|||
} |
|||
let nowCities = []; |
|||
cityData.filter(cd => provinces?.split('、').indexOf(cd.name) != -1).map(d => { |
|||
d.children?.map(c => { |
|||
nowCities.push(c.name) |
|||
}) |
|||
}) |
|||
let noMark = 0; |
|||
cities?.split('、')?.map(p => { |
|||
if (nowCities.indexOf(p) == -1) { |
|||
noMark++ |
|||
} |
|||
}) |
|||
return !noMark; |
|||
} |
|||
|
|||
const judgeNull = (value) => { |
|||
return value ? String(value).trim().replace(/\s*/g, "") : null; |
|||
} |
|||
|
|||
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 zmsz = /^[A-Za-z0-9]+$/;//字母数字组合
|
|||
for (let i = 0; i < res.length; i++) { |
|||
let d = res[i]; |
|||
let number = judgeNull(d['员工编号']); |
|||
let name = judgeNull(d['姓名']); |
|||
let provinces = judgeNull(d['销售区域(省/直辖市)']); |
|||
let cities = judgeNull(d['销售区域(市)']); |
|||
let businessLines = judgeNull(d['业务线']); |
|||
if (!number) {//人员编号不为空,唯一,字母和数字
|
|||
error(`第${i + 2}行人员编号为空,请填写`) |
|||
return |
|||
} |
|||
let rzExist = rzMembers.find(m => m.userCode == number); |
|||
if (!rzExist) { |
|||
error(`第${i + 2}行的人员编号无对应的员工信息`) |
|||
return |
|||
} |
|||
if (postData.some(p => p.number == number)) {//人员编号 唯一
|
|||
error(`第${i + 2}行人员编号重复,请更改后重新上传`) |
|||
return |
|||
} |
|||
if (!zmsz.test(number)) { |
|||
error(`第${i + 2}行人员编号错误,请填写字母和数字的组合`) |
|||
return |
|||
} |
|||
if (!name) {//姓名必填
|
|||
error(`第${i + 2}行姓名为空,请填写`) |
|||
return |
|||
} |
|||
if (!provinces) {//销售区域(省/直辖市)必填
|
|||
error(`第${i + 2}行销售区域(省/直辖市)为空,请填写`) |
|||
return |
|||
} |
|||
let pValid = judgeProvinces(provinces); |
|||
if (!pValid) { |
|||
error(`第${i + 2}行销售区域(省/直辖市)错误`) |
|||
return |
|||
} |
|||
let cValid = judgeCities(provinces, cities); |
|||
if (!cValid) { |
|||
error(`第${i + 2}行销售区域(市)错误`) |
|||
return |
|||
} |
|||
//todo 业务线判断
|
|||
postData.push({ |
|||
pepUserId: rzExist.pepUserId, name, number, |
|||
provinces: provinces || '', cities: cities || '', businessLines: businessLines || '', |
|||
del: false |
|||
}) |
|||
} |
|||
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> |
|||
<div style={{ marginTop: 20, border: '1px solid #C7E1FF', background: '#F4F8FF', borderRadius: 2, padding: '8px 0px 7px 12px', alignItems: 'center', color: '#0F7EFB', fontSize: 12 }}> |
|||
<div>填写要求:</div> |
|||
<div>员工编号:必填,唯一,数字和字母的组合;</div> |
|||
<div>姓名:必填,若与员工编号对应的项企用户姓名不同,将以项企数据为准;</div> |
|||
<div>销售区域(省/直辖市):必填,省或直辖市顿号隔开,如“北京市、江西省、江苏省”;</div> |
|||
<div>销售区域(市):非必填,归属所填省的地级市顿号隔开,如“南昌市、镇江市”。</div> |
|||
</div> |
|||
</Form> |
|||
</Modal > |
|||
) |
|||
} |
|||
|
|||
function mapStateToProps(state) { |
|||
const { auth, global, MemberList } = state; |
|||
return { |
|||
user: auth.user, |
|||
actions: global.actions, |
|||
rzMembers: MemberList.data?.rows || [], |
|||
} |
|||
} |
|||
|
|||
export default connect(mapStateToProps)(ImportSalersModal); |
@ -0,0 +1,350 @@ |
|||
import React, { useEffect, useRef, useState, useMemo } from 'react'; |
|||
import { connect } from 'react-redux'; |
|||
import moment from 'moment' |
|||
import { Select, Input, Button, Popconfirm, Radio, Tooltip, Table, Pagination, Skeleton } from '@douyinfe/semi-ui'; |
|||
import SalesMemberModal from './salesMemberModal' |
|||
import ImportSalersModal from './importSalersModal' |
|||
import { IconSearch } from '@douyinfe/semi-icons'; |
|||
import { SkeletonScreen } from "$components"; |
|||
import '../../style.less' |
|||
|
|||
const PersonnelDistribution = (props) => { |
|||
const { dispatch, actions } = props |
|||
const { humanAffairs } = actions; |
|||
const [keywordTarget, setKeywordTarget] = useState('dep'); |
|||
const [keyword, setKeyword] = useState('');//搜索内容 |
|||
const [limits, setLimits] = useState()//每页实际条数 |
|||
const [query, setQuery] = useState({ limit: 10, page: 0 }); //页码信息 |
|||
const [modalV, setModalV] = useState(false); |
|||
const [dataToEdit, setDataToEdit] = useState(null); |
|||
const [tableData, setTableData] = useState([]); |
|||
const [importModalV, setImportModalV] = useState(false); |
|||
const page = useRef(query.page); |
|||
function seachValueChange(value) { |
|||
setKeyword(value) |
|||
} |
|||
|
|||
useEffect(() => { |
|||
dispatch(humanAffairs.getMemberList()) |
|||
getMemberSearchList() |
|||
}, []); |
|||
|
|||
useEffect(() => { |
|||
getMemberSearchList()//查询人员列表 |
|||
}, [query]) |
|||
|
|||
function getMemberSearchList() { |
|||
let kt = keywordTarget == 'place' ? '' : keywordTarget; |
|||
let k = keywordTarget == 'place' ? '' : keyword; |
|||
let placeSearch = keywordTarget == 'place' ? keyword : ''; |
|||
dispatch(humanAffairs.getSalesList({ keywordTarget: kt, keyword: k, placeSearch, ...query })).then(r => { |
|||
if (r.success) { |
|||
setTableData(r.payload?.data?.rows); |
|||
setLimits(r.payload?.data?.count) |
|||
} |
|||
}) |
|||
} |
|||
|
|||
function handleRow(record, index) {// 给偶数行设置斑马纹 |
|||
if (index % 2 === 0) { |
|||
return { |
|||
style: { |
|||
background: '#FAFCFF', |
|||
} |
|||
}; |
|||
} else { |
|||
return {}; |
|||
} |
|||
} |
|||
|
|||
const closeAndFetch = () => { |
|||
setModalV(false) |
|||
getMemberSearchList(); |
|||
} |
|||
|
|||
const starHeader = (header) => { |
|||
return <div> |
|||
<img src="/assets/images/hrImg/V.png" style={{ width: 14, height: 14 }} /> {header} |
|||
</div> |
|||
} |
|||
|
|||
const getMultis = (arrStr) => {//默认展示2个 |
|||
return <div style={{ display: 'flex' }}> |
|||
{ |
|||
arrStr.length ? |
|||
arrStr.map((ite, idx) => { |
|||
return ( |
|||
<div key={idx} style={{ display: 'flex' }}> |
|||
{idx < 2 ? |
|||
<div style={{ padding: '0px 4px 1px 4px', color: '#FFF', fontSize: 12, background: 'rgba(0,90,189,0.8)', borderRadius: 2, marginRight: 4 }}> |
|||
{ite} |
|||
</div> : '' |
|||
} |
|||
{ |
|||
arrStr.length > 2 && idx == 2 ? |
|||
<Tooltip content={arrStr.join(',')} trigger="click" style={{ lineHeight: 2 }}> |
|||
<div style={{ padding: '0px 4px 1px 4px ', color: 'rgba(0,90,189,0.8)', fontSize: 12, marginRight: 4, cursor: "pointer" }}> |
|||
+{arrStr.length - 2} |
|||
</div> |
|||
</Tooltip> |
|||
: '' |
|||
} |
|||
</div> |
|||
) |
|||
}) : '-' |
|||
} |
|||
</div> |
|||
} |
|||
|
|||
const columns = [{ |
|||
title: '序号', |
|||
dataIndex: 'id', |
|||
key: 'id', |
|||
width: 60, |
|||
render: (text, record, index) => index + 1 |
|||
}, { |
|||
title: starHeader('姓名'), |
|||
dataIndex: 'name', |
|||
key: 'name', |
|||
width: 80 |
|||
}, |
|||
{ |
|||
title: starHeader('部门名称'), |
|||
dataIndex: 'department', |
|||
key: 'department', |
|||
width: 200, |
|||
render: (text, r, index) => { |
|||
let arrStr = text.map(t => t.name); |
|||
return getMultis(arrStr); |
|||
} |
|||
}, { |
|||
title: '销售区域(省/直辖市)', |
|||
dataIndex: 'provinces', |
|||
key: 'provinces', |
|||
width: 160, |
|||
render: (text, record, index) => { |
|||
return getMultis(text?.split('、') || []); |
|||
} |
|||
}, { |
|||
title: '销售区域(市)', |
|||
dataIndex: 'cities', |
|||
key: 'cities', |
|||
width: 160, |
|||
render: (text, record, index) => { |
|||
return text ? getMultis(text?.split('、') || []) : '-'; |
|||
} |
|||
}, { |
|||
title: '业务线', |
|||
dataIndex: 'businessLines', |
|||
key: 'businessLines', |
|||
width: 120, |
|||
render: (text, record, index) => { |
|||
return text ? getMultis(text?.split('、') || []) : '-'; |
|||
} |
|||
}, { |
|||
title: starHeader('岗位'), |
|||
dataIndex: 'post', |
|||
key: 'post', |
|||
width: 120, |
|||
render: (text, record) => <span>{text || '-'}</span> |
|||
}, { |
|||
title: starHeader('入职时间'), |
|||
dataIndex: 'hireDate', |
|||
key: 'hireDate', |
|||
width: 120, |
|||
render: (text, record) => <span>{text || '-'}</span> |
|||
}, { |
|||
title: starHeader('转正时间'), |
|||
dataIndex: 'regularDate', |
|||
key: 'regularDate', |
|||
width: 120, |
|||
render: (text, record) => <span>{text || '-'}</span> |
|||
}, { |
|||
title: starHeader('工龄'), |
|||
dataIndex: 'workYears', |
|||
key: 'workYears', |
|||
width: 120, |
|||
render: (_, r, index) => { |
|||
return (r.hireDate ? <span style={{ color: '#1890FF' }}>{String(moment(new Date()).diff(r.hireDate, 'years', true)).substring(0, 3) + '年'}</span> : '-') |
|||
}, |
|||
}, { |
|||
title: '操作', |
|||
dataIndex: 'action', |
|||
width: 120, |
|||
render: (text, record) => { |
|||
return <div> |
|||
<span style={{ color: '#1890FF', cursor: 'pointer' }} onClick={() => onEdit(record)}>编辑</span> |
|||
<Popconfirm |
|||
title='提示' content="确认删除该销售人员信息?" position='topLeft' |
|||
onConfirm={() => confirmDelete(record.pepUserId)} style={{ width: 330 }} |
|||
> <span style={{ color: '#1890FF', cursor: 'pointer' }}>删除</span></Popconfirm> |
|||
</div> |
|||
} |
|||
}]; |
|||
|
|||
const onEdit = (data) => { |
|||
setModalV(true); |
|||
setDataToEdit(data); |
|||
} |
|||
|
|||
const confirmDelete = (pepUserId) => { |
|||
dispatch(humanAffairs.delSalesMember({ pepUserId, msg: '删除销售人员信息' })).then(res => { |
|||
if (res.success) { |
|||
getMemberSearchList(); |
|||
} |
|||
}); |
|||
} |
|||
const scroll = useMemo(() => ({}), []); |
|||
return (<div style={{ padding: '0px 12px' }}> |
|||
<div style={{ display: 'flex' }}> |
|||
<div style={{ color: 'rgba(0,0,0,0.45)', fontSize: 14 }}>招聘</div> |
|||
<div style={{ color: 'rgba(0,0,0,0.45)', fontSize: 14, margin: '0px 8px' }}>/</div> |
|||
<div style={{ color: 'rgba(0,0,0,0.45)', fontSize: 14 }}>销售统计</div> |
|||
<div style={{ color: '#033C9A', fontSize: 14, margin: '0px 8px' }}>/</div> |
|||
<div style={{ color: '#033C9A', fontSize: 14 }}>销售人员分布</div> |
|||
</div> |
|||
<div style={{ background: '#FFFFFF', boxShadow: '0px 0px 12px 2px rgba(220,222,224,0.2)', borderRadius: 2, padding: '20px 0px 20px 19px ', marginTop: 12 }}> |
|||
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}> |
|||
<div style={{ display: 'flex', alignItems: 'baseline' }}> |
|||
<div style={{ width: 0, height: 20, borderLeft: '3px solid #0F7EFB', borderTop: '3px solid transparent', borderBottom: '3px solid transparent' }}></div> |
|||
<div style={{ fontFamily: "YouSheBiaoTiHei", fontSize: 24, color: '#033C9A', marginLeft: 8 }}>销售人员分布</div> |
|||
<div style={{ marginLeft: 6, fontSize: 12, color: '#969799', fontFamily: "DINExp", }}>DISTRIBUTION OF SALES PERSONNEL</div> |
|||
</div> |
|||
</div> |
|||
<div style={{ margin: '18px 0px' }}> |
|||
<div style={{ display: 'flex', margin: '16px 0px', justifyContent: 'space-between' }}> |
|||
<div style={{ display: 'flex' }}> |
|||
<div style={{ padding: '6px 20px', background: '#0F7EFB', color: '#FFFFFF', fontSize: 14, cursor: "pointer", marginRight: 18 }} |
|||
onClick={() => { |
|||
setModalV(true); |
|||
setDataToEdit(null); |
|||
}}> |
|||
新增 |
|||
</div> |
|||
<div> |
|||
<Select value={keywordTarget} onChange={setKeywordTarget} style={{ width: 100 }} > |
|||
<Select.Option value='dep'>部门</Select.Option> |
|||
<Select.Option value='place'>地区</Select.Option> |
|||
</Select> |
|||
</div> |
|||
<div style={{ margin: '0px 18px' }}> |
|||
<Input suffix={<IconSearch />} |
|||
showClear |
|||
placeholder='请输入关键词搜索' |
|||
value={keyword} |
|||
style={{ width: 346 }} |
|||
onChange={seachValueChange}> |
|||
</Input> |
|||
</div> |
|||
<Button theme='solid' type='primary' style={{ width: 80, borderRadius: 2, height: 32, background: '#DBECFF', color: '#005ABD' }} |
|||
onClick={() => { |
|||
setQuery({ limit: 10, page: 0 }) |
|||
}}>查询</Button> |
|||
</div> |
|||
<div style={{ display: 'flex', marginRight: 20 }}> |
|||
<div style={{ padding: '6px 20px', background: '#0F7EFB', color: '#FFFFFF', fontSize: 14, cursor: "pointer" }} |
|||
onClick={() => { setImportModalV(true); }}> |
|||
导入 |
|||
</div> |
|||
{/* <div style={{ padding: '6px 20px', background: '#00BA85', color: '#FFFFFF', fontSize: 14, marginLeft: 18 }}> |
|||
导出 |
|||
</div> */} |
|||
</div> |
|||
</div> |
|||
|
|||
<div style={{ border: '1px solid #C7E1FF', background: '#F4F8FF', borderRadius: 2, height: 32, width: 669, padding: '8px 0px 7px 12px', display: 'flex', alignItems: 'center', color: '#0F7EFB', fontSize: 12 }}> |
|||
<img src="/assets/images/hrImg/!.png" alt="" style={{ width: 14, height: 14, marginRight: 8 }} /> |
|||
表格中带有认证标识" |
|||
<img src="/assets/images/hrImg/V.png" alt="" style={{ width: 14, height: 14 }} /> |
|||
"信息的为系统基础数据,来源于项企PEP、钉钉等系统,其他数据均为导入或自定义数据 |
|||
</div> |
|||
|
|||
<div style={{ marginTop: 20 }}> |
|||
<Skeleton |
|||
// loading={loading} |
|||
loading={false} |
|||
active={true} |
|||
placeholder={SkeletonScreen()} |
|||
> |
|||
<Table |
|||
columns={columns} |
|||
dataSource={tableData} |
|||
bordered={false} |
|||
empty="暂无数据" |
|||
pagination={false} |
|||
onChange={({ sorter }) => { |
|||
if (sorter.key == 'userCode') { |
|||
if (sorter.sortOrder == 'descend') { |
|||
setOrder({ orderBy: 'code', orderDirection: 'DESC' }) |
|||
} else { |
|||
setOrder({ orderBy: 'code', orderDirection: 'ASC' }) |
|||
} |
|||
} else if (sorter.key == 'age') { |
|||
if (sorter.sortOrder == 'descend') { |
|||
setOrder({ orderBy: 'age', orderDirection: 'DESC' }) |
|||
} else { |
|||
setOrder({ orderBy: 'age', orderDirection: 'ASC' }) |
|||
} |
|||
} else { |
|||
if (sorter.sortOrder == 'descend') { |
|||
setOrder({ orderBy: 'hiredate', orderDirection: 'DESC' }) |
|||
} else { |
|||
setOrder({ orderBy: 'hiredate', orderDirection: 'ASC' }) |
|||
} |
|||
} |
|||
}} |
|||
onRow={handleRow} |
|||
scroll={scroll} |
|||
/> |
|||
</Skeleton> |
|||
<div style={{ |
|||
display: "flex", |
|||
justifyContent: "space-between", |
|||
padding: "20px 20px", |
|||
}}> |
|||
<div></div> |
|||
<div style={{ display: 'flex', }}> |
|||
<span style={{ lineHeight: "30px", fontSize: 13, color: 'rgba(0,90,189,0.8)' }}> |
|||
共{limits}条信息 |
|||
</span> |
|||
<Pagination |
|||
total={limits} |
|||
showSizeChanger |
|||
currentPage={query.page + 1} |
|||
pageSizeOpts={[10, 20, 30, 40]} |
|||
onChange={(currentPage, pageSize) => { |
|||
setQuery({ limit: pageSize, page: currentPage - 1 }); |
|||
page.current = currentPage - 1 |
|||
}} |
|||
/> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
{ |
|||
modalV ? <SalesMemberModal |
|||
dataToEdit={dataToEdit} getMultis={getMultis} |
|||
close={() => closeAndFetch()} |
|||
onCancel={() => setModalV(false)} /> : '' |
|||
} |
|||
{ |
|||
importModalV ? <ImportSalersModal |
|||
onCancel={() => { |
|||
setImportModalV(false); |
|||
getMemberSearchList(); |
|||
}} /> : '' |
|||
} |
|||
</div>) |
|||
} |
|||
|
|||
function mapStateToProps(state) { |
|||
const { auth, global, SalesMemberList } = state; |
|||
return { |
|||
user: auth.user, |
|||
actions: global.actions, |
|||
salesMemberList: SalesMemberList.data |
|||
}; |
|||
} |
|||
|
|||
export default connect(mapStateToProps)(PersonnelDistribution); |
@ -0,0 +1,263 @@ |
|||
import React, { useEffect, useRef, useState } from 'react'; |
|||
import moment from 'moment'; |
|||
import { connect } from "react-redux"; |
|||
import { Select, Modal, Form, Notification } from "@douyinfe/semi-ui"; |
|||
import cityData from '../../components/city.json'; |
|||
const businessLines = ['市政', '地灾', '水利', '智慧城市', '工地', '环保', '安防', '产品投标', '交通', '矿山', '产品线'] |
|||
const SalesMemberModal = (props) => { |
|||
const { dispatch, actions, user, meetingList, onConfirm, getMultis, onCancel, close, rzMembers, dataToEdit } = props; |
|||
const { humanAffairs } = actions; |
|||
const form = useRef();//表单
|
|||
const [lineOptions, setLineOptions] = useState([]); |
|||
const [options, setOptions] = useState([]); |
|||
const [cityOptions, setCityOptions] = useState([]); |
|||
const [peoplePro, setPeoplePro] = useState({}); //人员信息
|
|||
//初始化
|
|||
useEffect(() => { |
|||
let optionItems = cityData.map(m => { |
|||
return <Select.Option value={m.name} key={m.code}> |
|||
{m.name} |
|||
</Select.Option> |
|||
}) |
|||
setOptions(optionItems); |
|||
|
|||
let lineOptions = businessLines.map((l, index) => { |
|||
return <Select.Option value={l} key={index}> |
|||
{l} |
|||
</Select.Option> |
|||
}) |
|||
setLineOptions(lineOptions); |
|||
|
|||
if (dataToEdit) { |
|||
setPeoplePro(dataToEdit); |
|||
onChange(dataToEdit.provinces?.split('、') || []);//市options
|
|||
} |
|||
}, []); |
|||
|
|||
const onChange = (value) => { |
|||
let cityOptions = [], citiesRange = []; |
|||
cityData.filter(cd => value.indexOf(cd.name) !== -1).map(d => { |
|||
d.children?.map(c => { |
|||
cityOptions.push(<Select.Option value={c.name} key={c.code}> |
|||
{c.name} |
|||
</Select.Option>) |
|||
citiesRange.push(c.name) |
|||
}) |
|||
}) |
|||
setCityOptions(cityOptions) |
|||
|
|||
let citiesValue = form?.current?.getValues()?.cities || []; |
|||
if (citiesValue.length) { |
|||
let newCities = []; |
|||
citiesValue?.map(cv => { |
|||
if (citiesRange.indexOf(cv) != -1) { |
|||
newCities.push(cv); |
|||
} |
|||
}) |
|||
form.current.setValue('cities', newCities) |
|||
} |
|||
} |
|||
|
|||
function handleOk() { |
|||
form.current.validate().then((values) => { |
|||
if (peoplePro == 'noValid') { |
|||
Notification.error({ |
|||
content: '你填写的员工编号无对应的人员信息', |
|||
duration: 2, |
|||
}) |
|||
} else if (peoplePro?.userCode) { |
|||
if (values.userCode == peoplePro.userCode) { |
|||
values.provinces = values.provinces.join('、') |
|||
values.cities = values.cities.join('、') |
|||
values.businessLines = values.businessLines.join('、') |
|||
if (dataToEdit) { |
|||
dispatch(humanAffairs.editSalesMember({ pepUserId: peoplePro.pepUserId, msg: '编辑销售人员信息', ...values })).then((res) => { |
|||
if (res.success) { |
|||
close(); |
|||
} |
|||
}) |
|||
} else { |
|||
dispatch(humanAffairs.postSalesMember({ pepUserId: peoplePro.pepUserId, msg: '新增销售人员', ...values })).then((res) => { |
|||
if (res.success) { |
|||
close(); |
|||
} |
|||
}) |
|||
} |
|||
} else { |
|||
Notification.error({ |
|||
content: '你填写的员工编号无对应的人员信息', |
|||
duration: 2, |
|||
}) |
|||
} |
|||
} else { |
|||
Notification.error({ |
|||
content: '请查询人员编号对应的员工信息', |
|||
duration: 2, |
|||
}) |
|||
} |
|||
}) |
|||
} |
|||
|
|||
const memberSeach = (id) => {//搜索项企用户
|
|||
// dispatch(humanAffairs.getMemberSearch({ code: id })).then((res) => {//搜索项企用户
|
|||
// if (res.success) {
|
|||
// if (res.payload.data.length) {
|
|||
// let user = res.payload.data[0]
|
|||
let exist = rzMembers.find(m => m.userCode == id);//人员档案里面需要有
|
|||
if (exist) { |
|||
let item = { |
|||
pepUserId: exist.pepUserId, |
|||
name: exist.userName, |
|||
department: exist.departmrnt, |
|||
hireDate: exist.hiredate, |
|||
regularDate: exist.regularDate, |
|||
userCode: exist.userCode, |
|||
post: exist.userPost//岗位
|
|||
} |
|||
setPeoplePro(item) |
|||
} else { |
|||
setPeoplePro('noValid') |
|||
} |
|||
// } else {
|
|||
// setPeoplePro('noValid')
|
|||
// }
|
|||
// }
|
|||
// })
|
|||
} |
|||
|
|||
const renderSimpleInfo = () => { |
|||
let arrStr = peoplePro?.department?.map(t => t.name) || []; |
|||
return <div> |
|||
{ |
|||
peoplePro == 'noValid' ? |
|||
<div style={{ background: '#F4F5FC', border: '1px solid rgba(0, 90, 189, 0.2)', marginTop: 8, padding: '10px 0px', textAlign: 'center', color: '#4A4A4A' }}> |
|||
没有符合条件的结果 |
|||
</div> : |
|||
peoplePro?.name ? ( |
|||
<div style={{ background: '#F4F5FC', border: '1px solid rgba(0, 90, 189, 0.2)', marginTop: 8, padding: '20px 83px 20px 50px' }}> |
|||
<div style={{ display: 'flex' }}> |
|||
{renderPeopleItem('姓名:', peoplePro.name)} |
|||
<div style={{ display: 'flex', flexBasis: '50%' }}> |
|||
<div style={{ width: 16, height: 16, marginRight: 9 }}> |
|||
<img src="/assets/images/hrImg/department.png" alt="" style={{ width: '100%', height: '100%' }} /> |
|||
</div> |
|||
<div style={{ color: 'rgba(0,0,0,0.6)', fontSize: 12 }}> |
|||
所属部门: |
|||
</div> |
|||
{getMultis(arrStr)} |
|||
</div> |
|||
</div> |
|||
<div style={{ display: 'flex' }}> |
|||
{renderPeopleItem('入职时间:', peoplePro.hireDate)} |
|||
{renderPeopleItem('岗位:', peoplePro.post)} |
|||
</div> <div style={{ display: 'flex' }}> |
|||
{renderPeopleItem('转正时间:', peoplePro.regularDate)} |
|||
{renderPeopleItem('工龄:', peoplePro.hireDate ? String(moment(new Date()).diff(peoplePro.hireDate, 'years', true)).substring(0, 3) + '年' : '-')} |
|||
</div> |
|||
</div> |
|||
) : ('') |
|||
} |
|||
</div> |
|||
} |
|||
|
|||
const renderPeopleItem = (label, value) => { |
|||
return <div style={{ display: 'flex', flexBasis: '50%' }}> |
|||
<div style={{ width: 16, height: 16, marginRight: 9 }}> |
|||
<img src="/assets/images/hrImg/department.png" style={{ width: '100%', height: '100%' }} /> |
|||
</div> |
|||
<div style={{ color: 'rgba(0,0,0,0.6)', fontSize: 12 }}> |
|||
{label} |
|||
</div> |
|||
<div style={{ color: '#4A4A4A', fontSize: 12 }}> |
|||
{value || '-'} |
|||
</div> |
|||
</div> |
|||
} |
|||
|
|||
const onClear = () => { |
|||
form.current.setValue('cities', []) |
|||
} |
|||
|
|||
return ( |
|||
<Modal title={dataToEdit?.pepUserId ? '修改销售人员信息' : '新增销售人员'} |
|||
visible={true} |
|||
destroyOnClose |
|||
okText='保存' width={800} |
|||
onOk={handleOk} |
|||
onCancel={onCancel}> |
|||
<Form getFormApi={(formApi) => (form.current = formApi)} |
|||
labelPosition={'left'} labelCol={{ span: 8 }} wrapperCol={{ span: 16 }}> |
|||
<Form.Input |
|||
field="userCode" |
|||
label='人员编号' |
|||
initValue={dataToEdit?.userCode || ""} |
|||
placeholder="请输入人员编号" |
|||
showClear |
|||
style={{ width: '100%' }} |
|||
rules={[{ required: true, message: "请输入人员编号" }]} |
|||
onChange={() => setPeoplePro({})} |
|||
addonAfter={<div style={{ margin: '0px 12px', color: '#005ABD', cursor: "pointer", fontSize: 14 }} onClick={() => { |
|||
let formList = form.current.getValues() |
|||
if (formList.userCode) { |
|||
memberSeach(formList.userCode) |
|||
} |
|||
}}> |
|||
搜索 |
|||
</div>} /> |
|||
|
|||
{peoplePro ? renderSimpleInfo() : ''} |
|||
<Form.Select |
|||
initValue={dataToEdit?.provinces?.split('、') || []} |
|||
label="销售区域(省/直辖市)" |
|||
field='provinces' |
|||
showClear |
|||
rules={[{ required: true, message: '请选择销售区域(省/直辖市)' }]} |
|||
placeholder='请选择销售区域(省/直辖市)' |
|||
multiple filter |
|||
style={{ width: '100%' }} |
|||
onClear={() => onClear()} |
|||
onChange={value => onChange(value)} |
|||
maxTagCount={5} |
|||
> |
|||
{options} |
|||
</Form.Select> |
|||
<Form.Select |
|||
initValue={dataToEdit?.cities ? dataToEdit?.cities?.split('、') : []} |
|||
label="销售区域(市)" |
|||
field='cities' |
|||
showClear |
|||
placeholder='请选择销售区域(市)' |
|||
multiple filter |
|||
style={{ width: '100%' }} |
|||
maxTagCount={5} |
|||
> |
|||
{cityOptions} |
|||
</Form.Select> |
|||
<Form.Select |
|||
initValue={dataToEdit?.businessLines ? dataToEdit?.businessLines?.split('、') : []} |
|||
label="业务线" |
|||
field='businessLines' |
|||
showClear |
|||
placeholder='请选择业务线' |
|||
multiple filter |
|||
style={{ width: '100%' }} |
|||
maxTagCount={5} |
|||
> |
|||
{lineOptions} |
|||
</Form.Select> |
|||
</Form> |
|||
</Modal> |
|||
) |
|||
} |
|||
|
|||
function mapStateToProps(state) { |
|||
const { auth, global, MemberList } = state; |
|||
return { |
|||
user: auth.user, |
|||
actions: global.actions, |
|||
apiRoot: global.apiRoot, |
|||
rzMembers: MemberList.data?.rows || [], |
|||
}; |
|||
} |
|||
|
|||
export default connect(mapStateToProps)(SalesMemberModal); |
@ -0,0 +1,8 @@ |
|||
'use strict'; |
|||
|
|||
export const UserAttribute = { |
|||
jobDataSource: ['普通员工', '中层', '高层'], |
|||
activeStatusDataSource: ['在职', '离职', '特殊状态-特殊账号'], |
|||
organizationDataSource: ['江西飞尚科技有限公司', '江西飞尚工程质量检测有限公司', |
|||
'江西飞尚科技有限公司江苏分公司', '江西汇派科技有限公司'], |
|||
}; |
Loading…
Reference in new issue