wenlele 2 years ago
parent
commit
469474ac67
  1. 4
      api/.vscode/launch.json
  2. 8
      api/app/lib/controllers/attendance/index.js
  3. 15
      api/app/lib/controllers/auth/index.js
  4. 27
      api/app/lib/controllers/salesDistribution/index.js
  5. 9
      api/app/lib/models/sales_distribution.js
  6. 4
      doc/scripts/PEP V3.0.0/schema/1.sales_distribution_modify.sql
  7. 4
      web/client/src/sections/humanAffairs/containers/salersDistribution/constans.jsx
  8. 25
      web/client/src/sections/humanAffairs/containers/salersDistribution/importSalersModal.js
  9. 35
      web/client/src/sections/humanAffairs/containers/salersDistribution/personnelDistribution.jsx
  10. 45
      web/client/src/sections/humanAffairs/containers/salersDistribution/salesMemberModal.js

4
api/.vscode/launch.json

@ -19,9 +19,9 @@
"-g postgres://postgres:123@10.8.30.166:5432/hr-dev", "-g postgres://postgres:123@10.8.30.166:5432/hr-dev",
"--redisHost 10.8.30.112", "--redisHost 10.8.30.112",
"--redisPort 6379", "--redisPort 6379",
// "--apiEmisUrl http://10.8.30.112:14000", "--apiEmisUrl http://10.8.30.112:14000",
// //
"--apiEmisUrl http://10.8.30.161:1111", // "--apiEmisUrl http://10.8.30.161:1111",
"--qnak XuDgkao6cL0HidoMAPnA5OB10Mc_Ew08mpIfRJK5", "--qnak XuDgkao6cL0HidoMAPnA5OB10Mc_Ew08mpIfRJK5",
"--qnsk yewcieZLzKZuDfig0wLZ9if9jKp2P_1jd3CMJPSa", "--qnsk yewcieZLzKZuDfig0wLZ9if9jKp2P_1jd3CMJPSa",
"--qnbkt dev-hr", "--qnbkt dev-hr",

8
api/app/lib/controllers/attendance/index.js

@ -29,7 +29,9 @@ async function overtimeStatistic (ctx) {
}) })
returnD.forEach(u => { returnD.forEach(u => {
u.overtimeStatistic = sumRes.filter(s => s.pepUserId == u.pepUserId) let overtimeStatistic = sumRes.filter(s => s.pepUserId == u.pepUserId)
u.overtimeDuration = overtimeStatistic.reduce((sum, os) => sum + os.duration, 0)
u.overtimeStatistic = overtimeStatistic
}) })
ctx.status = 200; ctx.status = 200;
ctx.body = { ctx.body = {
@ -204,7 +206,9 @@ async function vacateStatistic (ctx) {
}) })
returnD.forEach(u => { returnD.forEach(u => {
u.vacateStatistic = sumRes.filter(s => s.pepUserId == u.pepUserId) let vacateStatistic = sumRes.filter(s => s.pepUserId == u.pepUserId)
u.vacateDuration = vacateStatistic.reduce((sum, vs) => sum + vs.duration, 0)
u.vacateStatistic = vacateStatistic
}) })
ctx.status = 200; ctx.status = 200;
ctx.body = { ctx.body = {

15
api/app/lib/controllers/auth/index.js

@ -10,9 +10,18 @@ async function login (ctx, next) {
const models = ctx.fs.dc.models; const models = ctx.fs.dc.models;
const params = ctx.request.body; const params = ctx.request.body;
const emisLoginRes = await ctx.app.fs.emisRequest.post('login', { let emisLoginRes = null
data: params if (params.username && params.password) {
}) emisLoginRes = await ctx.app.fs.emisRequest.post('login', {
data: { ...params, code: 'HR' }
})
} else if (params.token) {
emisLoginRes = await ctx.app.fs.emisRequest.get('user-info', {
query: {
token: params.token, code: 'HR'
}
})
}
if (!emisLoginRes) { if (!emisLoginRes) {
throw "无此用户,请使用正确的登录信息" throw "无此用户,请使用正确的登录信息"

27
api/app/lib/controllers/salesDistribution/index.js

@ -47,20 +47,7 @@ async function salesList(ctx) {
let rslt = [] let rslt = []
res.rows.map(d => { res.rows.map(d => {
//let valid = false;
let info = members.find(m => m.pepUserId == d.dataValues.pepUserId); let info = members.find(m => m.pepUserId == d.dataValues.pepUserId);
// if (info) {
// if (placeSearch) {
// let exist1 = d.dataValues.provinces.join(',').indexOf(placeSearch) != -1
// let exist2 = d.dataValues.cities.join(',').indexOf(placeSearch) != -1
// if (exist1 || exist2) {
// valid = true;
// }
// } else {
// valid = true;
// }
// }
//if (valid) {
let item = { let item = {
name: info.userName, name: info.userName,
userCode: info.userCode, userCode: info.userCode,
@ -71,10 +58,7 @@ async function salesList(ctx) {
...d.dataValues ...d.dataValues
} }
rslt.push(item); rslt.push(item);
//}
}) })
// let end = Number(page) * Number(limit) + Number(limit)
// let arr = rslt.slice(Number(page) * Number(limit), end)
ctx.status = 200; ctx.status = 200;
ctx.body = { ctx.body = {
count: res.count, count: res.count,
@ -92,7 +76,7 @@ async function salesList(ctx) {
async function add(ctx) { async function add(ctx) {
try { try {
const { models } = ctx.fs.dc; const { models } = ctx.fs.dc;
const { pepUserId, provinces, cities } = ctx.request.body const { pepUserId, provinces, cities, businessLines } = ctx.request.body
const existRes = await models.SalesDistribution.findOne({ const existRes = await models.SalesDistribution.findOne({
where: { pepUserId } where: { pepUserId }
@ -102,7 +86,7 @@ async function add(ctx) {
throw '当前销售人员信息已存在' throw '当前销售人员信息已存在'
} }
let storageData = { pepUserId, provinces, cities, del: false } let storageData = { pepUserId, provinces, cities, businessLines, del: false }
if (existRes && existRes.del) { if (existRes && existRes.del) {
await models.SalesDistribution.update(storageData, { await models.SalesDistribution.update(storageData, {
where: { pepUserId } where: { pepUserId }
@ -123,7 +107,7 @@ async function add(ctx) {
async function edit(ctx) { async function edit(ctx) {
try { try {
const { models } = ctx.fs.dc; const { models } = ctx.fs.dc;
const { pepUserId, provinces, cities } = ctx.request.body const { pepUserId, provinces, cities, businessLines } = ctx.request.body
const existRes = await models.SalesDistribution.findOne({ const existRes = await models.SalesDistribution.findOne({
where: { pepUserId } where: { pepUserId }
@ -133,7 +117,7 @@ async function edit(ctx) {
throw '当前销售人员信息不存在' throw '当前销售人员信息不存在'
} }
let storageData = { pepUserId, provinces, cities, del: false } let storageData = { pepUserId, provinces, cities, businessLines, del: false }
await models.SalesDistribution.update(storageData, { await models.SalesDistribution.update(storageData, {
where: { where: {
@ -321,11 +305,12 @@ async function addSalesMemberBulk(ctx) {
//处理编辑的 //处理编辑的
if (editArr.length) { if (editArr.length) {
for (let i in editArr) { for (let i in editArr) {
let { pepUserId, provinces, cities, del = false } = editArr[i]; let { pepUserId, provinces, cities, businessLines, del = false } = editArr[i];
let dataToUpdate = { let dataToUpdate = {
provinces, provinces,
cities, cities,
businessLines,
del del
} }
await models.SalesDistribution.update(dataToUpdate, { where: { pepUserId: pepUserId } }); await models.SalesDistribution.update(dataToUpdate, { where: { pepUserId: pepUserId } });

9
api/app/lib/models/sales_distribution.js

@ -51,6 +51,15 @@ module.exports = dc => {
primaryKey: false, primaryKey: false,
field: "del", field: "del",
autoIncrement: false autoIncrement: false
},
businessLines: {
type: DataTypes.STRING,
allowNull: true,
defaultValue: null,
comment: null,
primaryKey: false,
field: "business_lines",
autoIncrement: false
} }
}, { }, {
tableName: "sales_distribution", tableName: "sales_distribution",

4
doc/scripts/PEP V3.0.0/schema/1.sales_distribution_modify.sql

@ -0,0 +1,4 @@
alter table sales_distribution
add "business_lines" text;

4
web/client/src/sections/humanAffairs/containers/salersDistribution/constans.jsx

@ -0,0 +1,4 @@
export const businessLinesConst = ['市政', '地灾', '水利', '智慧城市', '工地',
'环保', '安防', '产品投标', '交通', '矿山', '产品线']

25
web/client/src/sections/humanAffairs/containers/salersDistribution/importSalersModal.js

@ -4,6 +4,7 @@ import { connect } from 'react-redux';
import { Modal, Form, Button, Notification } from '@douyinfe/semi-ui'; import { Modal, Form, Button, Notification } from '@douyinfe/semi-ui';
import { IconUpload } from '@douyinfe/semi-icons'; import { IconUpload } from '@douyinfe/semi-icons';
import cityData from '../../components/city.json'; import cityData from '../../components/city.json';
import { businessLinesConst } from './constans'
import XLSX from 'xlsx' import XLSX from 'xlsx'
//下载模板和上传文件读取 //下载模板和上传文件读取
const ImportSalersModal = props => { const ImportSalersModal = props => {
@ -38,7 +39,7 @@ const ImportSalersModal = props => {
const dldCsvMb = () => { const dldCsvMb = () => {
//表头 //表头
let head = "员工编号,姓名,销售区域(省/直辖市),销售区域(市)\n" let head = "员工编号,姓名,销售区域(省/直辖市),销售区域(市),业务线\n"
//数据 //数据
//let data = 1 + ',' + 2 + ',' + 3 + ',' + 4 + ',' + 5 //let data = 1 + ',' + 2 + ',' + 3 + ',' + 4 + ',' + 5
let templateCsv = "data:text/csv;charset=utf-8,\ufeff" + head; let templateCsv = "data:text/csv;charset=utf-8,\ufeff" + head;
@ -142,6 +143,19 @@ const ImportSalersModal = props => {
return !noMark; return !noMark;
} }
const judgeLines = (businessLines) => {
if (!businessLines) {//可以不填
return true;
}
let noMark = 0;
businessLines?.split('、')?.map(p => {
if (businessLinesConst.indexOf(p) == -1) {
noMark++
}
})
return !noMark;
}
const judgeNull = (value) => { const judgeNull = (value) => {
return value ? String(value).trim().replace(/\s*/g, "") : null; return value ? String(value).trim().replace(/\s*/g, "") : null;
} }
@ -191,6 +205,7 @@ const ImportSalersModal = props => {
let name = judgeNull(d['姓名']); let name = judgeNull(d['姓名']);
let provinces = judgeNull(d['销售区域(省/直辖市)']); let provinces = judgeNull(d['销售区域(省/直辖市)']);
let cities = judgeNull(d['销售区域(市)']); let cities = judgeNull(d['销售区域(市)']);
let businessLines = judgeNull(d['业务线']);
if (!number) {//人员编号不为空,唯一,字母和数字 if (!number) {//人员编号不为空,唯一,字母和数字
error(`${i + 2}行人员编号为空,请填写`) error(`${i + 2}行人员编号为空,请填写`)
return return
@ -226,9 +241,14 @@ const ImportSalersModal = props => {
error(`${i + 2}行销售区域(市)错误`) error(`${i + 2}行销售区域(市)错误`)
return return
} }
let bValid = judgeLines(businessLines);
if (!bValid) {
error(`${i + 2}行业务线错误`)
return
}
postData.push({ postData.push({
pepUserId: rzExist.pepUserId, name, number, pepUserId: rzExist.pepUserId, name, number,
provinces: provinces || '', cities: cities || '', provinces: provinces || '', cities: cities || '', businessLines: businessLines || '',
del: false del: false
}) })
} }
@ -252,6 +272,7 @@ const ImportSalersModal = props => {
<div>姓名必填若与员工编号对应的项企用户姓名不同将以项企数据为准</div> <div>姓名必填若与员工编号对应的项企用户姓名不同将以项企数据为准</div>
<div>销售区域/直辖市必填省或直辖市顿号隔开北京市江西省江苏省</div> <div>销售区域/直辖市必填省或直辖市顿号隔开北京市江西省江苏省</div>
<div>销售区域非必填归属所填省的地级市顿号隔开南昌市镇江市</div> <div>销售区域非必填归属所填省的地级市顿号隔开南昌市镇江市</div>
<div>业务线非必填顿号隔开智慧城市工地</div>
</div> </div>
</Form> </Form>
</Modal > </Modal >

35
web/client/src/sections/humanAffairs/containers/salersDistribution/personnelDistribution.jsx

@ -1,4 +1,4 @@
import React, { useEffect, useRef, useState } from 'react'; import React, { useEffect, useRef, useState, useMemo } from 'react';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import moment from 'moment' import moment from 'moment'
import { Select, Input, Button, Popconfirm, Radio, Tooltip, Table, Pagination, Skeleton } from '@douyinfe/semi-ui'; import { Select, Input, Button, Popconfirm, Radio, Tooltip, Table, Pagination, Skeleton } from '@douyinfe/semi-ui';
@ -100,19 +100,19 @@ const PersonnelDistribution = (props) => {
title: '序号', title: '序号',
dataIndex: 'id', dataIndex: 'id',
key: 'id', key: 'id',
width: '5%', width: 60,
render: (text, record, index) => index + 1 render: (text, record, index) => index + 1
}, { }, {
title: starHeader('姓名'), title: starHeader('姓名'),
dataIndex: 'name', dataIndex: 'name',
key: 'name', key: 'name',
width: '5%' width: 80
}, },
{ {
title: starHeader('部门名称'), title: starHeader('部门名称'),
dataIndex: 'department', dataIndex: 'department',
key: 'department', key: 'department',
width: '18%', width: 200,
render: (text, r, index) => { render: (text, r, index) => {
let arrStr = text.map(t => t.name); let arrStr = text.map(t => t.name);
return getMultis(arrStr); return getMultis(arrStr);
@ -121,7 +121,7 @@ const PersonnelDistribution = (props) => {
title: '销售区域(省/直辖市)', title: '销售区域(省/直辖市)',
dataIndex: 'provinces', dataIndex: 'provinces',
key: 'provinces', key: 'provinces',
width: '15%', width: 160,
render: (text, record, index) => { render: (text, record, index) => {
return getMultis(text?.split('、') || []); return getMultis(text?.split('、') || []);
} }
@ -129,40 +129,48 @@ const PersonnelDistribution = (props) => {
title: '销售区域(市)', title: '销售区域(市)',
dataIndex: 'cities', dataIndex: 'cities',
key: 'cities', key: 'cities',
width: '15%', width: 160,
render: (text, record, index) => { render: (text, record, index) => {
return getMultis(text?.split('、') || []); return text ? getMultis(text?.split('、') || []) : '-';
}
}, {
title: '业务线',
dataIndex: 'businessLines',
key: 'businessLines',
width: 140,
render: (text, record, index) => {
return text ? getMultis(text?.split('、') || []) : '-';
} }
}, { }, {
title: starHeader('岗位'), title: starHeader('岗位'),
dataIndex: 'post', dataIndex: 'post',
key: 'post', key: 'post',
width: '11%', width: 120,
render: (text, record) => <span>{text || '-'}</span> render: (text, record) => <span>{text || '-'}</span>
}, { }, {
title: starHeader('入职时间'), title: starHeader('入职时间'),
dataIndex: 'hireDate', dataIndex: 'hireDate',
key: 'hireDate', key: 'hireDate',
width: '8%', width: 120,
render: (text, record) => <span>{text || '-'}</span> render: (text, record) => <span>{text || '-'}</span>
}, { }, {
title: starHeader('转正时间'), title: starHeader('转正时间'),
dataIndex: 'regularDate', dataIndex: 'regularDate',
key: 'regularDate', key: 'regularDate',
width: '8%', width: 120,
render: (text, record) => <span>{text || '-'}</span> render: (text, record) => <span>{text || '-'}</span>
}, { }, {
title: starHeader('工龄'), title: starHeader('工龄'),
dataIndex: 'workYears', dataIndex: 'workYears',
key: 'workYears', key: 'workYears',
width: '5%', width: 120,
render: (_, r, index) => { render: (_, r, index) => {
return (r.hireDate ? <span style={{ color: '#1890FF' }}>{String(moment(new Date()).diff(r.hireDate, 'years', true)).substring(0, 3) + '年'}</span> : '-') return (r.hireDate ? <span style={{ color: '#1890FF' }}>{String(moment(new Date()).diff(r.hireDate, 'years', true)).substring(0, 3) + '年'}</span> : '-')
}, },
}, { }, {
title: '操作', title: '操作',
dataIndex: 'action', dataIndex: 'action',
width: '10%', width: 120,
render: (text, record) => { render: (text, record) => {
return <div> return <div>
<span style={{ color: '#1890FF', cursor: 'pointer' }} onClick={() => onEdit(record)}>编辑</span>&nbsp;&nbsp; <span style={{ color: '#1890FF', cursor: 'pointer' }} onClick={() => onEdit(record)}>编辑</span>&nbsp;&nbsp;
@ -186,7 +194,7 @@ const PersonnelDistribution = (props) => {
} }
}); });
} }
const scroll = useMemo(() => ({}), []);
return (<div style={{ padding: '0px 12px' }}> return (<div style={{ padding: '0px 12px' }}>
<div style={{ display: 'flex' }}> <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 }}>招聘</div>
@ -294,6 +302,7 @@ const PersonnelDistribution = (props) => {
justifyContent: "space-between", justifyContent: "space-between",
padding: "20px 20px", padding: "20px 20px",
}}> }}>
<div></div>
<div style={{ display: 'flex', }}> <div style={{ display: 'flex', }}>
<span style={{ lineHeight: "30px", fontSize: 13, color: 'rgba(0,90,189,0.8)' }}> <span style={{ lineHeight: "30px", fontSize: 13, color: 'rgba(0,90,189,0.8)' }}>
{limits}条信息 {limits}条信息

45
web/client/src/sections/humanAffairs/containers/salersDistribution/salesMemberModal.js

@ -3,10 +3,12 @@ import moment from 'moment';
import { connect } from "react-redux"; import { connect } from "react-redux";
import { Select, Modal, Form, Notification } from "@douyinfe/semi-ui"; import { Select, Modal, Form, Notification } from "@douyinfe/semi-ui";
import cityData from '../../components/city.json'; import cityData from '../../components/city.json';
import { businessLinesConst } from './constans'
const SalesMemberModal = (props) => { const SalesMemberModal = (props) => {
const { dispatch, actions, user, meetingList, onConfirm, getMultis, onCancel, close, rzMembers, dataToEdit } = props; const { dispatch, actions, user, meetingList, onConfirm, getMultis, onCancel, close, rzMembers, dataToEdit } = props;
const { humanAffairs } = actions; const { humanAffairs } = actions;
const form = useRef();//表单 const form = useRef();//表单
const [lineOptions, setLineOptions] = useState([]);
const [options, setOptions] = useState([]); const [options, setOptions] = useState([]);
const [cityOptions, setCityOptions] = useState([]); const [cityOptions, setCityOptions] = useState([]);
const [peoplePro, setPeoplePro] = useState({}); //人员信息 const [peoplePro, setPeoplePro] = useState({}); //人员信息
@ -18,6 +20,14 @@ const SalesMemberModal = (props) => {
</Select.Option> </Select.Option>
}) })
setOptions(optionItems); setOptions(optionItems);
let lineOptions = businessLinesConst.map((l, index) => {
return <Select.Option value={l} key={index}>
{l}
</Select.Option>
})
setLineOptions(lineOptions);
if (dataToEdit) { if (dataToEdit) {
setPeoplePro(dataToEdit); setPeoplePro(dataToEdit);
onChange(dataToEdit.provinces?.split('、') || []);//市options onChange(dataToEdit.provinces?.split('、') || []);//市options
@ -59,6 +69,7 @@ const SalesMemberModal = (props) => {
if (values.userCode == peoplePro.userCode) { if (values.userCode == peoplePro.userCode) {
values.provinces = values.provinces.join('、') values.provinces = values.provinces.join('、')
values.cities = values.cities.join('、') values.cities = values.cities.join('、')
values.businessLines = values.businessLines.join('、')
if (dataToEdit) { if (dataToEdit) {
dispatch(humanAffairs.editSalesMember({ pepUserId: peoplePro.pepUserId, msg: '编辑销售人员信息', ...values })).then((res) => { dispatch(humanAffairs.editSalesMember({ pepUserId: peoplePro.pepUserId, msg: '编辑销售人员信息', ...values })).then((res) => {
if (res.success) { if (res.success) {
@ -163,14 +174,6 @@ const SalesMemberModal = (props) => {
</div> </div>
} }
// const handleDeselect = (value) => {//删除
// let ranges = cityData.find(td => td.name == value)?.children || []
// if (ranges) {
// let formList = form.current.getValues().cities;
// }
// }
const onClear = () => { const onClear = () => {
form.current.setValue('cities', []) form.current.setValue('cities', [])
} }
@ -212,36 +215,36 @@ const SalesMemberModal = (props) => {
placeholder='请选择销售区域(省/直辖市)' placeholder='请选择销售区域(省/直辖市)'
multiple filter multiple filter
style={{ width: '100%' }} style={{ width: '100%' }}
// optionFilterProp='children'
// getPopupContainer={triggerNode => triggerNode.parentNode}
// filterOption={(input, option) => option.props.children
// .toLowerCase().indexOf(input.toLowerCase()) >= 0}
// value={selectedKeys || []}
onClear={() => onClear()} onClear={() => onClear()}
onChange={value => onChange(value)} onChange={value => onChange(value)}
//onDeselect={value => handleDeselect(value)}
maxTagCount={5} maxTagCount={5}
> >
{options} {options}
</Form.Select> </Form.Select>
<Form.Select <Form.Select
initValue={dataToEdit?.cities || []} initValue={dataToEdit?.cities ? dataToEdit?.cities?.split('、') : []}
label="销售区域(市)" label="销售区域(市)"
field='cities' field='cities'
showClear showClear
placeholder='请选择销售区域(市)' placeholder='请选择销售区域(市)'
multiple filter multiple filter
style={{ width: '100%' }} style={{ width: '100%' }}
// optionFilterProp='children'
// getPopupContainer={triggerNode => triggerNode.parentNode}
// filterOption={(input, option) => option.props.children
// .toLowerCase().indexOf(input.toLowerCase()) >= 0}
// value={selectedKeys || []}
//onDeselect={value => handleDeselect(value)}
maxTagCount={5} maxTagCount={5}
> >
{cityOptions} {cityOptions}
</Form.Select> </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> </Form>
</Modal> </Modal>
) )

Loading…
Cancel
Save