Browse Source

(+)权限控制

master
ww664853070 1 year ago
parent
commit
346b9ecdee
  1. 14
      api/.vscode/launch.json
  2. 42
      api/app/lib/controllers/attendance/index.js
  3. 50
      api/app/lib/controllers/auth/index.js
  4. 116
      api/app/lib/controllers/member/index.js
  5. 4
      api/app/lib/controllers/salesDistribution/index.js
  6. 20
      api/app/lib/middlewares/authenticator.js
  7. 70
      api/app/lib/models/resource.js
  8. 24
      api/app/lib/models/role.js
  9. 43
      api/app/lib/models/role_resource.js
  10. 23
      api/app/lib/models/user_role.js
  11. 43
      api/app/lib/models/vacate_remark.js
  12. 3
      api/app/lib/routes/attendance/index.js
  13. 206
      api/app/lib/schedule/attendance.js
  14. 52
      api/app/lib/utils/member.js
  15. 2
      doc/scripts/3.11.0/add_primary_key.sql
  16. 23
      doc/scripts/3.11.0/create_vacate_remark.sql
  17. 19
      doc/scripts/tools/v3.22.1_init_HRM_resource/.vscode/launch.json
  18. 1
      doc/scripts/tools/v3.22.1_init_HRM_resource/Answer.txt
  19. 0
      doc/scripts/tools/v3.22.1_init_HRM_resource/AnswerOld.txt
  20. 62
      doc/scripts/tools/v3.22.1_init_HRM_resource/AuthCode.txt
  21. 12
      doc/scripts/tools/v3.22.1_init_HRM_resource/Dockerfile
  22. 24
      doc/scripts/tools/v3.22.1_init_HRM_resource/config.js
  23. 425
      doc/scripts/tools/v3.22.1_init_HRM_resource/index.js
  24. 19
      doc/scripts/tools/v3.22.1_init_HRM_resource/jenkinsfile
  25. 19
      doc/scripts/tools/v3.22.1_init_HRM_resource/jenkinsfile-registry
  26. 21
      doc/scripts/tools/v3.22.1_init_HRM_resource/package.json
  27. 39
      doc/scripts/tools/v3.22.1_init_HRM_resource/readme.txt
  28. 22
      web/client/src/components/setup.jsx
  29. 4
      web/client/src/sections/humanAffairs/actions/index.js
  30. 15
      web/client/src/sections/humanAffairs/actions/vacateRemark.js
  31. 78
      web/client/src/sections/humanAffairs/containers/leaveStatistics.jsx
  32. 18
      web/client/src/sections/humanAffairs/containers/salersDistribution/personnelDistribution.jsx
  33. 48
      web/client/src/sections/humanAffairs/containers/vacateRemark.js
  34. 2
      web/client/src/utils/webapi.js

14
api/.vscode/launch.json

@ -17,16 +17,16 @@
"-f http://localhost:4700",
//
"-g postgres://postgres:123@10.8.30.166:5432/hr-dev",
"--redisHost 10.8.30.112",
"--redisHost localhost",
"--redisPort 6379",
"--apiEmisUrl http://10.8.30.112:14000",
"--apiEmisUrl http://10.8.30.103:14000",
//
// "--apiEmisUrl http://10.8.30.161:1111",
"--qnak XuDgkao6cL0HidoMAPnA5OB10Mc_Ew08mpIfRJK5",
"--qnsk yewcieZLzKZuDfig0wLZ9if9jKp2P_1jd3CMJPSa",
"--qnbkt dev-hr",
// "--qndmn http://resources.anxinyun.cn",
"--qndmn http://rjkwed13l.hn-bkt.clouddn.com",
"--qnak 5XrM4wEB9YU6RQwT64sPzzE6cYFKZgssdP5Kj3uu",
"--qnsk w6j2ixR_i-aelc6I7S3HotKIX-ukMzcKmDfH6-M5",
"--qnbkt anxinyun-test",
"--qndmn http://resources.anxinyun.cn",
// "--qndmn http://rjkwed13l.hn-bkt.clouddn.com",
// * 2
// "--clickHouseUser ",

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

@ -205,10 +205,14 @@ async function vacateStatistic(ctx) {
startDate, endDate, pepUserIds
})
const remarkList = await models.VacateRemark.findAll({}); //查询备注
returnD.forEach(u => {
let vacateStatistic = sumRes.filter(s => s.pepUserId == u.pepUserId)
let remarkData = remarkList.filter(e => e.pepUserId == u.pepUserId)
u.vacateDuration = vacateStatistic.reduce((sum, vs) => sum + vs.duration, 0)
u.vacateStatistic = vacateStatistic
u.remark = remarkData.length ? remarkData[0].remark : null
})
ctx.status = 200;
ctx.body = {
@ -255,6 +259,8 @@ async function exportVacateStatistic(ctx) {
startDate, endDate, pepUserIds
})
const remarkList = await models.VacateRemark.findAll({}); //查询备注
returnD.forEach(u => {
u.departmrnt = u.departmrnt.map(dep => dep.name).join('、')
u.role = u.role.map(r => r.name).join('、')
@ -269,6 +275,9 @@ async function exportVacateStatistic(ctx) {
}, 0)
}
u.vacateDayStatisticDuration = (u.vacateDayStatisticDuration || 0) / 3600
u.userActiveStatus = u.userActiveStatus == 1 ? '在职' : u.userActiveStatus == 2 ? '离职' : '特殊状态-特殊账号'
let remarkData = remarkList.filter(e => e.pepUserId == u.pepUserId)
u.remark = remarkData.length ? (remarkData[0].remark == '' ? '无' : remarkData[0].remark) : '无'
})
const header = [{
@ -283,6 +292,9 @@ async function exportVacateStatistic(ctx) {
}, {
title: '职位',
key: 'role',
}, {
title: '在职状态',
key: 'userActiveStatus',
},]
.concat(vacateTypeRes.map(v => {
return {
@ -302,6 +314,12 @@ async function exportVacateStatistic(ctx) {
defaultValue: '0',
}]
)
.concat(
[{
title: '备注',
key: 'remark',
}]
)
const fileName = `请假统计_${startDate ? moment(startDate).format('YYYY-MM-DD') : ''}${startDate && endDate ? '-' : ''}${endDate ? moment(endDate).format('YYYY-MM-DD') : ''}${startDate || endDate ? '_' : ''}${moment().format('YYYYMMDDHHmmss')}` + '.csv'
const filePath = await simpleExcelDown({ data: returnD, header, fileName: fileName })
@ -320,10 +338,34 @@ async function exportVacateStatistic(ctx) {
}
}
// 添加请假统计备注
async function vacateRemark(ctx) {
try {
const { models } = ctx.fs.dc;
const { remark, pepUserId } = ctx.query;
let oldData = await models.VacateRemark.findOne({ where: { pepUserId: pepUserId } });
if (oldData) {
await models.VacateRemark.update({ remark, pepUserId }, { where: { pepUserId: pepUserId } });
} else {
await models.VacateRemark.create({ remark, pepUserId });
}
ctx.status = 200;
ctx.body = '添加备注成功';
} catch (error) {
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
ctx.status = 400;
ctx.body = {
"message": "添加备注失败"
}
}
}
module.exports = {
overtimeStatistic,
exportOvertimeStatistic,
vacateType,
vacateStatistic,
exportVacateStatistic,
vacateRemark
};

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

@ -4,7 +4,52 @@ const MD5 = require('crypto-js/md5');
const moment = require('moment');
const uuid = require('uuid');
async function login (ctx, next) {
async function getDataRange(ctx) {
//null为所有权限,不加条件过滤
let userIds = null;
let departmentNames = null;
let userNames = null;
let dataRange = null;
const { adminHr, id, department, allDepartment } = ctx.fs.api.userInfo;
//人资管理-所有权限
if (adminHr.filter(admin => admin.id == id).length) {
dataRange = 1;
} else {
const models = ctx.fs.dc.models;
//获取用户对应角色的最高数据范围
let roleRes = await models.Role.findAll({
include: [{
model: models.UserRole,
where: { userId: id }
}]
})
if (roleRes.some(r => r.dataRange && r.dataRange === 1)) {
dataRange = 1;
} else {
const departments = allDepartment.departments;
dataRange = 2;
userIds = [];
userNames = [];
if (department && departments) {
let userDeps = department.map(d => d.id);
departmentNames = department.map(d => d.name);
let deps = departments.filter(d => userDeps.indexOf(d.id) > -1)
if (deps && deps.length > 0) {
deps.map(d => d.users.map(u => {
if (userIds.indexOf(u.id) < 0) {
userIds.push(u.id);
userNames.push(u.name);
}
}));
}
}
}
}
return { dataRange, userIds, departmentNames, userNames };
}
async function login(ctx, next) {
// const transaction = await ctx.fs.dc.orm.transaction();
try {
const models = ctx.fs.dc.models;
@ -58,7 +103,7 @@ async function login (ctx, next) {
}
}
async function logout (ctx) {
async function logout(ctx) {
try {
const models = ctx.fs.dc.models;
const params = ctx.request.body;
@ -81,4 +126,5 @@ async function logout (ctx) {
module.exports = {
login,
logout,
getDataRange
};

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

@ -1,6 +1,7 @@
'use strict';
const moment = require('moment')
const fs = require('fs');
const { getDataRange } = require('../auth/index')
async function add(ctx) {
try {
@ -283,12 +284,19 @@ async function list(ctx) {
hiredateStart, hiredateEnd, marital, native, workPlace,
orderBy, orderDirection,
} = ctx.query
let dataRange = await getDataRange(ctx);
if (dataRange.userIds && dataRange.userIds.length === 0) {
ctx.status = 200;
ctx.body = {
count: 0,
rows: []
}
} else {
const userRes = await memberList({
keywordTarget, keyword, limit, page, state,
hiredateStart, hiredateEnd, marital, native, workPlace,
orderBy, orderDirection,
nowAttendanceTime: true
nowAttendanceTime: true, userIds: dataRange.userIds
})
let { packageUser: returnD, pepUserIds } = await packageUserData(userRes, {
@ -300,6 +308,7 @@ async function list(ctx) {
count: userRes.count,
rows: returnD
}
}
} catch (error) {
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
ctx.status = 400;
@ -498,19 +507,65 @@ async function exportData(ctx) {
hiredateStart, hiredateEnd, marital, native, workPlace,
orderBy, orderDirection,
} = ctx.query
const tableAttributes = models['Member'].tableAttributes
const optionKeys = keys.split(',')
let exportD = null;
let dataRange = await getDataRange(ctx);
if (dataRange.userIds && dataRange.userIds.length === 0) {
exportD = [];
} else {
const userRes = await memberList({
keywordTarget, keyword, limit, page, state,
hiredateStart, hiredateEnd, marital, native, workPlace,
orderBy, orderDirection,
nowAttendanceTime: true
nowAttendanceTime: true, userIds: dataRange.userIds
})
const tableAttributes = models['Member'].tableAttributes
const optionKeys = keys.split(',')
let { packageUser, pepUserIds } = await packageUserData(userRes)
exportD = packageUser;
let { packageUser: exportD, pepUserIds } = await packageUserData(userRes)
// 查询累计加班次数及总时长
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.userJob = d.userJob ? UserAttribute.jobDataSource[d.userJob - 1] : '';
d.userActiveStatus = d.userActiveStatus ? UserAttribute.activeStatusDataSource[d.userActiveStatus - 1] : '';
d.userOrganization = d.userOrganization ? UserAttribute.organizationDataSource[d.userOrganization - 1] : '';
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
})
}
let preHeader = [{
title: '员工编号',
key: 'userCode',
@ -573,49 +628,6 @@ async function exportData(ctx) {
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.userJob = d.userJob ? UserAttribute.jobDataSource[d.userJob - 1] : '';
d.userActiveStatus = d.userActiveStatus ? UserAttribute.activeStatusDataSource[d.userActiveStatus - 1] : '';
d.userOrganization = d.userOrganization ? UserAttribute.organizationDataSource[d.userOrganization - 1] : '';
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);
@ -714,7 +726,9 @@ async function getPositionRating(ctx) {
findObj.limit = Number(limit);
findObj.offset = Number(page) * Number(limit);
}
let dataRange = await getDataRange(ctx);
if (dataRange.userIds)
findObj.where = { pepUserId: { $in: dataRange.userIds || [] } }
const list = await models.PositionRating.findAndCountAll(findObj);
if (list.rows.length) {
const userIds = list.rows.map(u => u.pepUserId);

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

@ -8,7 +8,7 @@ async function salesList(ctx) {
const {
keywordTarget, keyword, limit, page, state,
hiredateStart, hiredateEnd, marital, native, workPlace,
orderBy, orderDirection, placeSearch
orderBy, orderDirection, placeSearch,userActiveStatus
} = ctx.query
const userRes = await memberList({
@ -55,10 +55,12 @@ async function salesList(ctx) {
department: info.departmrnt,
hireDate: info.hiredate,//入职时间
regularDate: info.regularDate,//转正时间
userActiveStatus:info.userActiveStatus,
...d.dataValues
}
rslt.push(item);
})
rslt= rslt.filter(e=>e.userActiveStatus == userActiveStatus)
ctx.status = 200;
ctx.body = {
count: res.count,

20
api/app/lib/middlewares/authenticator.js

@ -72,32 +72,16 @@ let authorizeToken = async function (ctx, token) {
if (token && tokenFormatRegexp.test(token)) {
try {
const expired = await ctx.redis.hget(token, 'expired');
// const authorizeRes = await ctx.app.fs.emisRequest.get('authorize', {
// query: { token }
// })
// const { userInfo, expired } = authorizeRes;
// TODO 从项企 clickhouse 数据库中查 token 并更新
if (expired && moment().valueOf() <= moment(expired).valueOf()) {
const userInfo = JSON.parse(await ctx.redis.hmget(token, 'userInfo'));
const { pomsUserInfo: pomsUser } = userInfo
// const pomsUser = await ctx.app.fs.dc.models.User.findOne({
// where: {
// pepUserId: userInfo.id
// }
// }) || {}
rslt = {
'authorized': userInfo.authorized,
'resources': (userInfo || {}).resources || [],
};
ctx.fs.api.userId = pomsUser.id;
ctx.fs.api.userInfo = pomsUser;
ctx.fs.api.userId = userInfo.id;
ctx.fs.api.userInfo = userInfo;
ctx.fs.api.pepUserId = userInfo.id;
ctx.fs.api.pepUserInfo = userInfo;
ctx.fs.api.token = token;
}
} catch (err) {

70
api/app/lib/models/resource.js

@ -0,0 +1,70 @@
/* eslint-disable*/
'use strict';
module.exports = dc => {
const DataTypes = dc.ORM;
const sequelize = dc.orm;
const Resource = sequelize.define("resource", {
id: {
type: DataTypes.INTEGER,
allowNull: false,
defaultValue: null,
comment: null,
primaryKey: true,
field: "id",
autoIncrement: true
},
name: {
type: DataTypes.STRING,
allowNull: true,
defaultValue: null,
comment: "权限名称",
primaryKey: false,
field: "name",
autoIncrement: false
},
code: {
type: DataTypes.STRING,
allowNull: true,
defaultValue: null,
comment: "权限码",
primaryKey: false,
field: "code",
autoIncrement: false
},
parentId: {
type: DataTypes.INTEGER,
allowNull: true,
defaultValue: null,
comment: "父级id",
primaryKey: false,
field: "parent_id",
autoIncrement: false
},
createTime: {
type: DataTypes.DATE,
allowNull: true,
defaultValue: null,
comment: "创建时间",
primaryKey: false,
field: "create_time",
autoIncrement: false
},
order: {
type: DataTypes.INTEGER,
allowNull: true,
defaultValue: null,
comment: null,
primaryKey: false,
field: "order",
autoIncrement: false
}
}, {
tableName: "resource",
comment: "",
indexes: []
});
dc.models.Resource = Resource;
return Resource;
};

24
api/app/lib/models/role.js

@ -1,4 +1,5 @@
/* eslint-disable*/
'use strict';
module.exports = dc => {
@ -16,25 +17,36 @@ module.exports = dc => {
},
name: {
type: DataTypes.STRING,
allowNull: false,
allowNull: true,
defaultValue: null,
comment: null,
comment: "角色名称",
primaryKey: false,
field: "name",
autoIncrement: false
},
delete: {
type: DataTypes.BOOLEAN,
allowNull: false,
defaultValue: true,
comment: null,
defaultValue: null,
comment: "收否删除",
primaryKey: false,
field: "delete",
autoIncrement: false
},
dataRange: {
type: DataTypes.INTEGER,
allowNull: true,
defaultValue: null,
comment: "数据范围:1全公司 2本部门",
primaryKey: false,
field: "data_range",
autoIncrement: false
}
}, {
tableName: "role",
comment: "",
indexes: []
});
dc.models.Role = Role;
return Role;
};

43
api/app/lib/models/role_resource.js

@ -0,0 +1,43 @@
/* eslint-disable*/
'use strict';
module.exports = dc => {
const DataTypes = dc.ORM;
const sequelize = dc.orm;
const RoleResource = sequelize.define("roleResource", {
id: {
type: DataTypes.INTEGER,
allowNull: false,
defaultValue: null,
comment: null,
primaryKey: true,
field: "id",
autoIncrement: true
},
roleId: {
type: DataTypes.INTEGER,
allowNull: true,
defaultValue: null,
comment: "角色id",
primaryKey: false,
field: "role_id",
autoIncrement: false
},
resId: {
type: DataTypes.INTEGER,
allowNull: true,
defaultValue: null,
comment: "权限id",
primaryKey: false,
field: "res_id",
autoIncrement: false
}
}, {
tableName: "role_resource",
comment: "",
indexes: []
});
dc.models.RoleResource = RoleResource;
return RoleResource;
};

23
api/app/lib/models/user_role.js

@ -1,10 +1,11 @@
/* eslint-disable*/
'use strict';
module.exports = dc => {
const DataTypes = dc.ORM;
const sequelize = dc.orm;
const UserRole = sequelize.define("user_role", {
const UserRole = sequelize.define("userRole", {
id: {
type: DataTypes.INTEGER,
allowNull: false,
@ -16,23 +17,31 @@ module.exports = dc => {
},
roleId: {
type: DataTypes.INTEGER,
allowNull: false,
allowNull: true,
defaultValue: null,
comment: null,
comment: "角色id",
primaryKey: false,
field: "role_id",
autoIncrement: false
},
userId: {
type: DataTypes.INTEGER,
allowNull: false,
defaultValue: true,
comment: null,
allowNull: true,
defaultValue: null,
comment: "用户id",
primaryKey: false,
field: "user_id",
},
autoIncrement: false
}
}, {
tableName: "user_role",
comment: "",
indexes: []
});
dc.models.UserRole = UserRole;
const Role = dc.models.Role;
UserRole.belongsTo(Role, { foreignKey: 'roleId', targetKey: 'id' });
Role.hasMany(UserRole, { foreignKey: 'roleId', sourceKey: 'id' });
return UserRole;
};

43
api/app/lib/models/vacate_remark.js

@ -0,0 +1,43 @@
/* eslint-disable*/
'use strict';
module.exports = dc => {
const DataTypes = dc.ORM;
const sequelize = dc.orm;
const VacateRemark = sequelize.define("vacateRemark", {
id: {
type: DataTypes.INTEGER,
allowNull: false,
defaultValue: null,
comment: null,
primaryKey: true,
field: "id",
autoIncrement: true,
unique: "vacate_remark_id_uindex"
},
pepUserId: {
type: DataTypes.INTEGER,
allowNull: false,
defaultValue: null,
comment: 'pep用户id',
primaryKey: false,
field: "pep_user_id",
autoIncrement: false
},
remark: {
type: DataTypes.TEXT,
allowNull: true,
defaultValue: null,
comment: '备注',
primaryKey: false,
field: "remark",
autoIncrement: false
}
}, {
tableName: "vacate_remark",
comment: "",
indexes: []
});
dc.models.VacateRemark = VacateRemark;
return VacateRemark;
};

3
api/app/lib/routes/attendance/index.js

@ -17,4 +17,7 @@ module.exports = function (app, router, opts) {
app.fs.api.logAttr['GET/attendance/vacate/export'] = { content: '请假统计导出', visible: true };
router.get('/attendance/vacate/export', attendance.exportVacateStatistic);
app.fs.api.logAttr['PUT/attendance/vacate/creat/remark'] = { content: '添加请假统计备注', visible: true };
router.put('/attendance/vacate/creat/remark', attendance.vacateRemark);
};

206
api/app/lib/schedule/attendance.js

@ -65,68 +65,121 @@ module.exports = function (app, opts) {
keys: ['leaveType'],
require: true,
},
hrAffirmType: {
keyWord: ['核定请假类别'],
},
duration: {
keyWord: ['请假时长(小时)', '请假小时'],
keys: ['leaveTime']
},
hrAffirmType: {
keyWord: ['核定请假类别'],
},
hrAffirmDuration: {
keyWord: ['核定请假小时'],
},
hrAffirmItem: {
array: {
hrAffirmType: { keyWord: ['核定请假类别'] },
hrAffirmDuration: { keyWord: ['核定请假小时'] }
}
},
}
const schemaRecursionObj = ({
jsonSchema, keyWord, targetKeys = [], schemaPath, require
}) => {
let schemaPath_ = JSON.parse(JSON.stringify(schemaPath))
const schemaRecursionObj = ({ jsonSchema, keyWord, targetKeys = [], schemaPath, require, propKey, valueKey }) => {
let schemaPath_ = JSON.parse(JSON.stringify(schemaPath));
if (jsonSchema.properties) {
for (let prKey in jsonSchema.properties) {
if (
keyWord.includes(jsonSchema.properties[prKey].title)
|| targetKeys.includes(prKey)
) {
schemaPath_.push({
if (Array.isArray(keyWord)) { //分组vacateNeedData的keyWord传的是数值对象
if (keyWord.includes(jsonSchema.properties[prKey].title) || targetKeys.includes(prKey)) {
if (propKey) {//分组的请假事项和请假时长放在一起,目的多条数据取值不错乱
const propKeyIndex = schemaPath_.findIndex(i => i.prKey == propKey);
if (propKeyIndex > -1) {
schemaPath_[propKeyIndex].child.push({
key: valueKey,
prKey,
...jsonSchema.properties[prKey]
})
return schemaPath_
}
} else {//非分组场景
schemaPath_.push({
prKey,
...jsonSchema.properties[prKey]
});
}
return schemaPath_;
} else if (jsonSchema.properties[prKey].properties) {
schemaPath_.push({
prKey,
...jsonSchema.properties[prKey]
})
});
schemaPath_ = schemaRecursionObj({
jsonSchema: jsonSchema.properties[prKey],
keyWord,
targetKeys,
schemaPath: schemaPath_,
require,
})
});
if (!schemaPath_.length && require) {
console.warn('数据字段查找错误:', jsonSchema.properties);
}
if (schemaPath_.length > schemaPath.length) {
return schemaPath_
}
} else if (jsonSchema.properties[prKey].items && jsonSchema.properties[prKey].items.properties) {
if (valueKey) {
const propKeyObj = schemaPath_.findIndex(i => i.prKey == prKey);
if (-1 == propKeyObj) {
schemaPath_.push({
prKey,
child: []
})
}
schemaPath_ = schemaRecursionObj({
jsonSchema: jsonSchema.properties[prKey].items,
keyWord,
targetKeys,
schemaPath: schemaPath_,
require,
propKey: prKey, //分组请假类型按照数组嵌套多个属性方式取值
valueKey: valueKey
})
}
if (!schemaPath_.length && require) {
console.warn('数据字段查找错误:', jsonSchema.properties);
}
} else {
return schemaPath_
}
} else {//vacateNeedData中核定请假信息hrAffirmItem特殊处理
for (let item in keyWord) {
schemaPath_ = schemaRecursionObj({
jsonSchema: jsonSchema,
keyWord: keyWord[item].keyWord,
targetKeys,
schemaPath: schemaPath_,
require,
propKey: null,
valueKey: item //用于多条请假记录key,方便最后数组对象取值
});
}
return schemaPath_;
}
}
} else {
return schemaPath_;
}
}
const dataRecursionObj = (dataObj, index, needData, lastKeyObj, nd) => {
const keyObj = needData[nd].schemaPath[index]
const keyObj = needData[nd].schemaPath[index];
if (dataObj.hasOwnProperty(keyObj.prKey)) {
if (lastKeyObj.prKey == keyObj.prKey) {
if (keyObj.child) {//schemaRecursionObj()处理时对分组用child标记,此处取值特殊处理
return dataRecursionChildObj(dataObj[keyObj.prKey], keyObj.child);
}
let gotValue = dataObj[keyObj.prKey]
if (keyObj.enum) {
let vIndex = keyObj.enum.findIndex(ke => ke == gotValue)
gotValue = keyObj.enumNames[vIndex]
let vIndex = keyObj.enum.findIndex(ke => ke == gotValue);
gotValue = keyObj.enumNames[vIndex];
}
return gotValue
return gotValue;
} else {
return dataRecursionObj(
dataObj[keyObj.prKey],
@ -139,6 +192,22 @@ module.exports = function (app, opts) {
}
}
const dataRecursionChildObj = (dataObj, keyObj) => {
return dataObj.map(data => {
let retObj = {};
for (let item in data) {
const keyToData = keyObj.find(k => k.prKey == item);
let value = data[item];
if (keyToData.enum) {
let vIndex = keyToData.enum.findIndex(ke => ke == value);
value = keyToData.enumNames[vIndex];
}
retObj[keyToData.key] = value;
}
return retObj;
});
}
const getData = (applyDetail, needData) => {
for (let nd in needData) {
if (needData[nd].noProcess) {
@ -148,35 +217,25 @@ module.exports = function (app, opts) {
const { jsonSchema } = JSON.parse(applyDetail.formSchema)
needData[nd].schemaPath = schemaRecursionObj({
jsonSchema: jsonSchema || {},
keyWord: needData[nd]['keyWord'],
keyWord: needData[nd]['keyWord'] || needData[nd]['array'],
targetKeys: needData[nd]['keys'],
schemaPath: [],
require: nd.require
})
if (
needData[nd].schemaPath
&& needData[nd].schemaPath.length
) {
const lastKeyObj = needData[nd]
.schemaPath[needData[nd].schemaPath.length - 1]
if (needData[nd].schemaPath && needData[nd].schemaPath.length) {
const lastKeyObj = needData[nd].schemaPath[needData[nd].schemaPath.length - 1];
if (applyDetail.formData) {
const formData = JSON.parse(applyDetail.formData)
needData[nd].value = dataRecursionObj(
formData, 0, needData, lastKeyObj, nd
)
} else {
console.warn(
`表单数据缺失:[${nd}]`,
applyDetail
);
console.warn(`表单数据缺失:[${nd}]`, applyDetail);
}
} else {
if (needData[nd].require) {
// 记录错误 关键数据没找到
console.warn(
`数据字段查找错误:${nd.needData[nd]['keyWord'].join('、')}`,
jsonSchema
);
console.warn(`数据字段查找错误:${nd.needData[nd]['keyWord'].join('、')}`, jsonSchema);
}
}
}
@ -218,10 +277,11 @@ module.exports = function (app, opts) {
` +
`
${existOvertimeCount || existVacateCount ?
`WHERE story.create_at > '${moment().subtract(1, 'month').format('YYYY-MM-DD HH:mm:ss')}'`
`WHERE story.create_at > '2023-03-16 00:00:00'`
: ''}
`
).toPromise()
// `WHERE story.create_at > '${moment().subtract(1, 'month').format('YYYY-MM-DD HH:mm:ss')}'`
let insertCount = 0, updateCount = 0, invalidCount = 0, unCompletedCount = 0, unknowCount = 0
for (let a of attendanceRes) {
@ -229,29 +289,75 @@ module.exports = function (app, opts) {
if (a.processName) {
if ('COMPLETED' == a.state) {
if (a.processName.indexOf('请假') > -1) {
let needData = JSON.parse(JSON.stringify(vacateNeedData))
getData(a, needData)
const { begainTime, endTime, type, hrAffirmType, duration, hrAffirmDuration, reason } = needData
if (
begainTime.value && endTime.value && type.value
&& (duration.value || hrAffirmDuration.value)
) {
let durationSec = 0
let needData = JSON.parse(JSON.stringify(vacateNeedData));
getData(a, needData);
const vacateDurationSecDeal = (hrAffirmDuration, duration) => {
let durationSec = 0;
if (hrAffirmDuration.value) {
durationSec = parseFloat(hrAffirmDuration.value) * 3600
} else if (duration.value) {
durationSec = parseFloat(duration.value) * 3600
}
return durationSec;
};
if (
typeof durationSec != 'number'
|| isNaN(durationSec)
|| durationSec <= 0
) {
const { begainTime, endTime, type, hrAffirmItem, duration, hrAffirmDuration, reason } = needData;
if (begainTime.value && endTime.value && type.value && hrAffirmItem.value) {//核定请假分组,后面历史表单全部走完后,可把hrAffirmType、hrAffirmDuration取值的去掉
const existRes = await models.Vacate.findOne({ where: { pepProcessStoryId: a.historyId } });
if (existRes) {
updateCount++;
} else {
const storageD = [];
const packageDay = [];
for (let index in hrAffirmItem.value) {
const { hrAffirmDuration, hrAffirmType } = hrAffirmItem.value[index];
const durationSec = vacateDurationSecDeal({ value: hrAffirmDuration }, duration);
if (typeof durationSec != 'number' || isNaN(durationSec) || durationSec <= 0) {
console.warn('请假时长计算结果错误', hrAffirmDuration, duration);
invalidCount++
invalidCount++;
} else {
storageD.push({
pepUserId: a.pepUserId,
pepProcessStoryId: a.historyId,
startTime: moment(begainTime.value).format('YYYY-MM-DD HH:mm:ss'),
endTime: moment(endTime.value).format('YYYY-MM-DD HH:mm:ss'),
duration: durationSec,
type: hrAffirmType,
wfProcessState: a.state,
reason: reason.value
});
packageDay.push({
index: {
day: moment(begainTime.value).format('YYYY-MM-DD'),
duration: durationSec
}
});
}
}
if (storageD.length) {
let res = await models.Vacate.bulkCreate(storageD, { returning: true });
insertCount++;
for (let item of res) {
await models.VacateDay.create({
day: moment(item.startTime).format('YYYY-MM-DD'),
duration: item.duration,
vacateId: item.id
});
}
}
}
} else if (begainTime.value && endTime.value && type.value && (duration.value || hrAffirmDuration.value)) {//历史请假表单,无核定请假分组
const durationSec = vacateDurationSecDeal(hrAffirmDuration, duration);
if (typeof durationSec != 'number' || isNaN(durationSec) || durationSec <= 0) {
console.warn('请假时长计算结果错误', hrAffirmDuration, duration);
invalidCount++;
} else {
const { hrAffirmType } = needData;
// 计算每个工作日请了多少假
let begainTime_ = moment(begainTime.value)
let endTime_ = moment(endTime.value)

52
api/app/lib/utils/member.js

@ -4,7 +4,7 @@ const request = require('superagent');
module.exports = function (app, opts) {
async function memberList ({
async function memberList({
keywordTarget, keyword, limit, page, state,
hiredateStart, hiredateEnd, marital, native, workPlace,
orderBy, orderDirection,
@ -12,7 +12,8 @@ module.exports = function (app, opts) {
overtimeDayStatisticStartDate, overtimeDayStatisticendDate,
overtimeCountStatistic, overtimeCountStatisticStartDate, overtimeCountStatisticendDate,
vacateDayStatisticStartDate, vacateDayStatisticendDate,
vacateDurationStatistic, vacateCountStatistic, vacateCountStatisticStartDate, vacateCountStatisticendDate
vacateDurationStatistic, vacateCountStatistic, vacateCountStatisticStartDate, vacateCountStatisticendDate,
userIds
}) {
const { judgeHoliday } = app.fs.utils
const { clickHouse } = app.fs
@ -100,6 +101,8 @@ module.exports = function (app, opts) {
FROM member
INNER JOIN ${pepEmis}.user AS user
ON member.pep_user_id = user.id
${userIds && userIds.length ? `AND user.id in (${userIds.join(',')})` : ''}
${keywordTarget == 'number' && keyword ? `
AND user.people_code LIKE '%${keyword}%'
`: ''}
@ -189,7 +192,7 @@ module.exports = function (app, opts) {
LEFT JOIN (
SELECT
vacate.pep_user_id AS pepUserId,
count(pep_process_story_id) AS count
count(distinct pep_process_story_id) AS count
FROM vacate
${vacateCountStatisticWhere.length ? `
WHERE ${vacateCountStatisticWhere.join(' AND ')}
@ -279,6 +282,26 @@ module.exports = function (app, opts) {
member.*
${innerSelectQuery}
ORDER BY ${orderBy == 'code' ?
'user.people_code'
: orderBy == 'hiredate'
? 'member.hiredate'
: orderBy == 'age'
? 'member.birthday'
: orderBy == 'overtimeTakeRestSum'
|| orderBy == 'overtimePaySum'
|| orderBy == 'overtimeSum' ?
'overtimeDayStatisticDuration'
: orderBy == 'overtimeCount' ?
'overtimeCount'
: orderBy == 'vacateSum' ?
'vacateDayStatisticDuration'
: orderBy == 'vacateCount' ?
'vacateCount'
: 'user.people_code'}
${orderDirection || 'ASC'}
${limit ? `LIMIT ${limit}` : ''}
${limit && page ? 'OFFSET ' + parseInt(limit) * parseInt(page) : ''}
) AS hrMember
@ -296,24 +319,7 @@ module.exports = function (app, opts) {
LEFT JOIN ${pepEmis}.department AS department
ON department.id = department_user.department
${whereOption.length ? `WHERE ${whereOption.join(' AND ')}` : ''}
ORDER BY ${orderBy == 'code' ?
'user.people_code'
: orderBy == 'hiredate'
? 'hrMember."member.hiredate"'
: orderBy == 'age'
? 'hrMember."member.birthday"'
: orderBy == 'overtimeTakeRestSum'
|| orderBy == 'overtimePaySum'
|| orderBy == 'overtimeSum' ?
'hrMember.overtimeDayStatisticDuration'
: orderBy == 'overtimeCount' ?
'hrMember.overtimeCount'
: orderBy == 'vacateSum' ?
'hrMember.vacateDayStatisticDuration'
: orderBy == 'vacateCount' ?
'hrMember.vacateCount'
: 'user.people_code'}
${orderDirection || 'ASC'}
`).toPromise()
const countRes = await clickHouse.hr.query(`
@ -328,7 +334,7 @@ module.exports = function (app, opts) {
}
}
async function packageUserData (userRes, option = {}) {
async function packageUserData(userRes, option = {}) {
const { judgeHoliday, } = app.fs.utils
let workTime = false
@ -383,7 +389,7 @@ module.exports = function (app, opts) {
obj[nextKey] = u[k] == '1970-01-01 00:00:00.000000' || u[k] == '1970-01-01 08:00:00.000000' ? null : u[k]
}
pepUserIds.push(u.pepUserId)
console.log("查询到的用户信息:", obj);
// console.log("查询到的用户信息:", obj);
returnD.push({
...obj,
departmrnt: u.depId ? [{

2
doc/scripts/3.11.0/add_primary_key.sql

@ -0,0 +1,2 @@
ALTER TABLE "public"."vacate_remark"
ADD PRIMARY KEY ("id");

23
doc/scripts/3.11.0/create_vacate_remark.sql

@ -0,0 +1,23 @@
-- ----------------------------
-- Table structure for vacate_remark
-- ----------------------------
-- ----------------------------
-- 创建 vacate_remark 表自增序列
-- ----------------------------
CREATE SEQUENCE "public"."vacate_remark_id_seq"
INCREMENT 1
MINVALUE 1
MAXVALUE 9223372036854775807
START 1
CACHE 1;
DROP TABLE IF EXISTS "public"."vacate_remark";
CREATE TABLE "public"."vacate_remark" (
"id" int4 NOT NULL DEFAULT nextval('vacate_remark_id_seq'::regclass),
"pep_user_id" int4 NOT NULL,
"remark" text COLLATE "pg_catalog"."default"
)
;
COMMENT ON COLUMN "public"."vacate_remark"."pep_user_id" IS 'pep用户id';
COMMENT ON COLUMN "public"."vacate_remark"."remark" IS '备注';

19
doc/scripts/tools/v3.22.1_init_HRM_resource/.vscode/launch.json

@ -0,0 +1,19 @@
{
// 使 IntelliSense
//
// 访: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "launch",
"name": "启动程序",
"program": "${workspaceFolder}\\index.js",
"env": {
"NODE_ENV": "development",
"FS_ENV": "false",
"FS_ENV_TEST":"false"
}
}
]
}

1
doc/scripts/tools/v3.22.1_init_HRM_resource/Answer.txt

@ -0,0 +1 @@
{"人员档案管理":"PERSONNELFILEMANAGEMENT","人员档案":"PERSONNELFILES","新增档案":"ADDINGFILES","导入员工信息":"IMPORTEMPLOYEEINFORMATION","导出员工档案":"EXPORTEMPLOYEEPROFILE","编辑档案":"EDITPROFILE","删除档案":"DELETEFILES","员工信息":"EMPLOYEEINFORMATION","导出员工信息":"EXPORTEMPLOYEEINFORMATION","岗位评级":"JOBRATING","导入岗位评级":"IMPORTJOBRATINGS","部门档案管理":"DEPARTMENTARCHIVESMANAGEMENT","部门档案":"DEPARTMENTARCHIVES","假勤管理":"LEAVEMANAGEMENT","出勤统计":"ATTENDANCESTATISTICS","请假统计":"LEAVESTATISTICS","导出请假统计":"EXPORTLEAVESTATISTICS","加班统计":"OVERTIMESTATISTICS","导出加班统计":"EXPORTOVERTIMESTATISTICS","员工沟通":"EMPLOYEECOMMUNICATION","员工沟通统计":"EMPLOYEECOMMUNICATIONSTATISTICS","导出员工沟通统计":"EXPORTEMPLOYEECOMMUNICATIONSTATISTICS","招聘记录":"RECRUITMENTRECORDS","任用记录":"APPOINTMENTRECORD","销售统计":"SALESSTATISTICS","销售人员分布":"SALESPERSONNELDISTRIBUTION","新增销售人员":"ADDSALESPERSONNEL","导入销售人员分布":"IMPORTSALESPERSONNELDISTRIBUTION","培训档案":"TRAININGDOSSIER","培训资源储存库":"TRAININGRESOURCEREPOSITORY","文件夹管理":"FOLDERMANAGEMENT","上传培训资料":"UPLOADTRAININGMATERIALS","下载培训资料":"DOWNLOADTRAININGMATERIALS","删除培训资料":"DELETETRAININGMATERIALS","培训管理":"TRAININGMANAGEMENT","个人培训记录":"PERSONALTRAININGRECORDS","导入个人培训记录":"IMPORTPERSONALTRAININGRECORDS","部门培训记录":"DEPARTMENTTRAININGRECORDS","导入部门培训记录":"IMPORTDEPARTMENTTRAININGRECORDS","基本动作管理":"BASICACTIONMANAGEMENT","周报管理":"WEEKLYREPORTMANAGEMENT","导出周报":"EXPORTWEEKLYREPORT","销售日志":"SALESLOG","导出销售日志":"EXPORTSALESLOG","工程日志":"ENGINEERINGLOG","导出工程日志":"EXPORTENGINEERINGLOG","员工考核":"EMPLOYEEASSESSMENT","试用期员工考核":"EMPLOYEEASSESSMENTDURINGPROBATIONARYPERIOD","正式员工考核":"FORMALEMPLOYEEASSESSMENT","下载正式员工考核":"DOWNLOADOFFICIALEMPLOYEEASSESSMENT","中层考核":"MIDDLELEVELASSESSMENT","中层月度过程考核":"MIDDLELEVELMONTHLYPROCESSASSESSMENT","下载中层月度过程考核":"DOWNLOADMIDDLELEVELMONTHLYPROCESSASSESSMENT","中层季度考核":"MIDLEVELQUARTERLYASSESSMENT","下载中层季度考核":"DOWNLOADMIDLEVELQUARTERLYASSESSMENT","高层考核":"SENIORASSESSMENT","高层月度过程考核":"MONTHLYPROCESSASSESSMENTFORSENIORMANAGEMENT","下载高层月度过程考核":"DOWNLOADTHEMONTHLYPROCESSASSESSMENTFORSENIORMANAGEMENT","高层季度考核":"QUARTERLYASSESSMENTOFSENIORMANAGEMENT","下载高层季度考核":"DOWNLOADQUARTERLYASSESSMENTSFORSENIORMANAGEMENT","奖惩管理":"REWARDANDPUNISHMENTMANAGEMENT","奖惩信息":"REWARDANDPUNISHMENTINFORMATION"}

0
doc/scripts/tools/v3.22.1_init_HRM_resource/AnswerOld.txt

62
doc/scripts/tools/v3.22.1_init_HRM_resource/AuthCode.txt

@ -0,0 +1,62 @@
人员档案管理 : 功能 : PERSONNELFILEMANAGEMENT [1]
人员档案 : 功能 : PERSONNELFILES [1]
新增档案 : 按钮 : ADDINGFILES [1];
导入员工信息 : 按钮 : IMPORTEMPLOYEEINFORMATION [2];
导出员工档案 : 按钮 : EXPORTEMPLOYEEPROFILE [3];
编辑档案 : 按钮 : EDITPROFILE [4];
删除档案 : 按钮 : DELETEFILES [5];
员工信息 : 功能 : EMPLOYEEINFORMATION [2]
导出员工信息 : 按钮 : EXPORTEMPLOYEEINFORMATION [1];
岗位评级 : 功能 : JOBRATING [3]
导入岗位评级 : 按钮 : IMPORTJOBRATINGS [1];
部门档案管理 : 功能 : DEPARTMENTARCHIVESMANAGEMENT [2]
部门档案 : 功能 : DEPARTMENTARCHIVES [1];
假勤管理 : 功能 : LEAVEMANAGEMENT [3]
出勤统计 : 功能 : ATTENDANCESTATISTICS [1];
请假统计 : 功能 : LEAVESTATISTICS [2]
导出请假统计 : 按钮 : EXPORTLEAVESTATISTICS [1];
加班统计 : 功能 : OVERTIMESTATISTICS [3]
导出加班统计 : 按钮 : EXPORTOVERTIMESTATISTICS [1];
员工沟通 : 功能 : EMPLOYEECOMMUNICATION [4]
员工沟通统计 : 功能 : EMPLOYEECOMMUNICATIONSTATISTICS [1]
导出员工沟通统计 : 按钮 : EXPORTEMPLOYEECOMMUNICATIONSTATISTICS [1];
招聘记录 : 功能 : RECRUITMENTRECORDS [5]
任用记录 : 功能 : APPOINTMENTRECORD [1];
销售统计 : 功能 : SALESSTATISTICS [6]
销售人员分布 : 功能 : SALESPERSONNELDISTRIBUTION [1]
新增销售人员 : 按钮 : ADDSALESPERSONNEL [1];
导入销售人员分布 : 按钮 : IMPORTSALESPERSONNELDISTRIBUTION [2];
培训档案 : 功能 : TRAININGDOSSIER [7]
培训资源储存库 : 功能 : TRAININGRESOURCEREPOSITORY [1]
文件夹管理 : 按钮 : FOLDERMANAGEMENT [1];
上传培训资料 : 按钮 : UPLOADTRAININGMATERIALS [2];
下载培训资料 : 按钮 : DOWNLOADTRAININGMATERIALS [3];
删除培训资料 : 按钮 : DELETETRAININGMATERIALS [4];
培训管理 : 功能 : TRAININGMANAGEMENT [8]
个人培训记录 : 功能 : PERSONALTRAININGRECORDS [1]
导入个人培训记录 : 按钮 : IMPORTPERSONALTRAININGRECORDS [1];
部门培训记录 : 功能 : DEPARTMENTTRAININGRECORDS [2]
导入部门培训记录 : 按钮 : IMPORTDEPARTMENTTRAININGRECORDS [1];
基本动作管理 : 功能 : BASICACTIONMANAGEMENT [9]
周报管理 : 功能 : WEEKLYREPORTMANAGEMENT [1]
导出周报 : 按钮 : EXPORTWEEKLYREPORT [1];
销售日志 : 功能 : SALESLOG [2]
导出销售日志 : 按钮 : EXPORTSALESLOG [1];
工程日志 : 功能 : ENGINEERINGLOG [3]
导出工程日志 : 按钮 : EXPORTENGINEERINGLOG [1];
员工考核 : 功能 : EMPLOYEEASSESSMENT [10]
试用期员工考核 : 功能 : EMPLOYEEASSESSMENTDURINGPROBATIONARYPERIOD [1];
正式员工考核 : 功能 : FORMALEMPLOYEEASSESSMENT [2]
下载正式员工考核 : 按钮 : DOWNLOADOFFICIALEMPLOYEEASSESSMENT [1];
中层考核 : 功能 : MIDDLELEVELASSESSMENT [11]
中层月度过程考核 : 功能 : MIDDLELEVELMONTHLYPROCESSASSESSMENT [1]
下载中层月度过程考核 : 按钮 : DOWNLOADMIDDLELEVELMONTHLYPROCESSASSESSMENT [1];
中层季度考核 : 功能 : MIDLEVELQUARTERLYASSESSMENT [2]
下载中层季度考核 : 按钮 : DOWNLOADMIDLEVELQUARTERLYASSESSMENT [1];
高层考核 : 功能 : SENIORASSESSMENT [12]
高层月度过程考核 : 功能 : MONTHLYPROCESSASSESSMENTFORSENIORMANAGEMENT [1]
下载高层月度过程考核 : 按钮 : DOWNLOADTHEMONTHLYPROCESSASSESSMENTFORSENIORMANAGEMENT [1];
高层季度考核 : 功能 : QUARTERLYASSESSMENTOFSENIORMANAGEMENT [2]
下载高层季度考核 : 按钮 : DOWNLOADQUARTERLYASSESSMENTSFORSENIORMANAGEMENT [1];
奖惩管理 : 功能 : REWARDANDPUNISHMENTMANAGEMENT [13]
奖惩信息 : 功能 : REWARDANDPUNISHMENTINFORMATION [1];

12
doc/scripts/tools/v3.22.1_init_HRM_resource/Dockerfile

@ -0,0 +1,12 @@
FROM repository.anxinyun.cn/base-images/nodejs12:20.10.12.2
MAINTAINER liuxinyi "liu.xinyi@free-sun.com.cn"
COPY . /app
WORKDIR /app
RUN npm cache clean -f && npm install --production --force --registry http://10.8.30.22:7000
CMD [ "node", "index.js" ]

24
doc/scripts/tools/v3.22.1_init_HRM_resource/config.js

@ -0,0 +1,24 @@
const ENV_SY_POOL = { //商用数据库配置
user: 'FashionAdmin',
host: '10.8.40.210',
database: 'HRM',
password: '123456',
port: 5432
}
const ENV_CS_POOL = { //生产环境数据库配置
user: 'postgres',
host: '10.8.30.166',
database: 'HRM',
password: '123',
port: 5432
}
const PRO_POOL = { //研发自测用
user: 'postgres',
host: '10.8.30.166',
database: 'hr-dev',
password: '123',
port: 5432
}
module.exports = {
ENV_SY_POOL, ENV_CS_POOL, PRO_POOL
}

425
doc/scripts/tools/v3.22.1_init_HRM_resource/index.js

@ -0,0 +1,425 @@
try {
const { Pool, Client } = require('pg')
const Hex = require('crypto-js/enc-hex');
const MD5 = require('crypto-js/md5');
const request = require('superagent');
const fs = require("fs");
const args = require('args');
const moment = require('moment');
const { ENV_SY_POOL, ENV_CS_POOL, PRO_POOL } = require('./config.js')
args.option(['e', 'env'], '商用环境');
args.option(['t', 'test'], '测试环境');
args.option(['p', 'product'], '生产环境');
const flags = args.parse(process.argv);
console.log("process.env.FS_ENV:", process.env.FS_ENV);
console.log("process.env.FS_ENV_TEST:", process.env.FS_ENV_TEST);
console.log(" flags.env:", flags.env);
console.log("flags.test:", flags.test);
const FS_ENV = process.env.FS_ENV || flags.env || false;
const FS_ENV_TEST = process.env.FS_ENV_TEST || flags.test || false;
let poolSetting = PRO_POOL;
let readAnswer = true;
if (FS_ENV && 'true' == FS_ENV) {//商用
poolSetting = ENV_SY_POOL;
console.log("poolSetting:FS_ENV");
} else if (FS_ENV_TEST && 'true' == FS_ENV_TEST) {//测试
poolSetting = ENV_CS_POOL;
console.log("poolSetting:FS_ENV_TEST");
} else {//研发
poolSetting = PRO_POOL;
//对权限初始变动时,readAnswer需设为false,其他数据库执行更新时设为true
// readAnswer = false;
console.log("poolSetting:PRO_POOL");
}
const pool = new Pool(poolSetting);
const baiduInfo = {
appid: '20200917000567738',
key: 'xXm4jsuuD38JIkkhEcK6'
}
// 功能名称 alias翻译别名,show展示的名字,btn操作按钮,child子类,order排序
let resource = [
{
show: '人员档案',
alias: '人员档案管理',
order: 1,
child: [{
show: '人员档案',
child: [{
btn: '新增档案',
alias: '新增档案'
}, {
btn: '导入员工信息'
}, {
btn: '导出',
alias: '导出员工档案'
}, {
btn: '编辑档案'
}, {
btn: '删除档案'
}]
}, {
show: '员工信息',
child: [{
btn: '导出',
alias: '导出员工信息'
}]
}, {
show: '岗位评级',
child: [{
btn: '导入',
alias: '导入岗位评级'
}]
}]
}, {
show: '部门档案',
alias: '部门档案管理',
order: 2,
child: [{
show: '部门档案'
}]
}, {
show: '假勤管理',
order: 3,
child: [{
show: '出勤统计',
}, {
show: '请假统计',
child: [{
btn: '导出',
alias: '导出请假统计'
}]
}, {
show: '加班统计',
child: [{
btn: '导出',
alias: '导出加班统计'
}]
}]
}, {
show: '员工沟通',
order: 4,
child: [{
show: '员工沟通统计',
child: [{
btn: '导出',
alias: '导出员工沟通统计'
}]
}]
}, {
show: '招聘记录',
order: 5,
child: [{
show: '任用记录'
}]
}, {
show: '销售统计',
order: 6,
child: [{
show: '销售人员分布',
child: [{
btn: '新增',
alias: '新增销售人员'
}, {
btn: '导入',
alias: '导入销售人员分布'
}]
}]
}, {
show: '培训档案',
order: 7,
child: [{
show: '培训资源储存库',
child: [{
btn: '文件夹管理'
}, {
btn: '上传文件',
alias: '上传培训资料'
}, {
btn: '下载',
alias: '下载培训资料'
}, {
btn: '删除',
alias: '删除培训资料'
}]
}]
}, {
show: '培训管理',
order: 8,
child: [{
show: '个人培训记录',
child: [{
btn: '导入',
alias: '导入个人培训记录'
}]
}, {
show: '部门培训记录',
child: [{
btn: '导入',
alias: '导入部门培训记录'
}]
}]
}, {
show: '基本动作管理',
order: 9,
child: [{
show: '周报管理',
child: [{
btn: '导出',
alias: '导出周报'
}]
}, {
show: '销售日志',
child: [{
btn: '导出',
alias: '导出销售日志'
}]
}, {
show: '工程日志',
child: [{
btn: '导出',
alias: '导出工程日志'
}]
}]
}, {
show: '员工考核',
order: 10,
child: [{
show: '试用期员工考核'
}, {
show: '正式员工考核',
child: [{
btn: '下载',
alias: '下载正式员工考核'
}]
}]
}, {
show: '中层考核',
order: 11,
child: [{
show: '月度过程考核',
alias: '中层月度过程考核',
child: [{
btn: '下载',
alias: '下载中层月度过程考核'
}]
}, {
show: '季度考核',
alias: '中层季度考核',
child: [{
btn: '下载',
alias: '下载中层季度考核'
}]
}]
}, {
show: '高层考核',
order: 12,
child: [{
show: '月度过程考核',
alias: '高层月度过程考核',
child: [{
btn: '下载',
alias: '下载高层月度过程考核'
}]
}, {
show: '季度考核',
alias: '高层季度考核',
child: [{
btn: '下载',
alias: '下载高层季度考核'
}]
}]
}, {
show: '奖惩信息',
alias: '奖惩管理',
order: 13,
child: [{
show: '奖惩信息'
}]
}];
let transResult = '';
let answerObj = {};
const getAnswer = async (query) => {
let start = (new Date()).getTime();
let salt = start;
let str1 = baiduInfo.appid + query + salt + baiduInfo.key;
let sign = Hex.stringify(MD5(str1));
let answer = await request.get('http://api.fanyi.baidu.com/api/trans/vip/translate').timeout(1000 * 30).query({
q: query,
appid: baiduInfo.appid,
salt: salt,
from: 'zh',
to: 'en',
sign: sign
});
if (answer.body.error_code) {
console.warn(answer.body);
throw '百度不给力,快快debug'
}
let rslt = answer.body.trans_result;
// let upperCaseRslt = rslt[0].dst.toLowerCase().replace(/( |^)[a-z]/g, (L) => L.toUpperCase()).replace(/ /g, '');
let upperCaseRslt = rslt[0].dst.toUpperCase().replace(/ /g, '');
while (((new Date()).getTime() - start) < (1000 / 5)) {//每s只能调用10次
continue;
}
return upperCaseRslt
}
(async () => {
const client = await pool.connect()
try {
let oldAnswer = {};
let answers;
if (readAnswer) {
answers = fs.readFileSync('Answer.txt', 'utf-8')
answers = JSON.parse(answers);
fs.exists('./AnswerOld.txt', (exists) => {
if (exists) {
oldAnswer = fs.readFileSync('AnswerOld.txt', 'utf-8')
if (oldAnswer != '') {
oldAnswer = JSON.parse(oldAnswer)
} else {
oldAnswer = {}
}
} else {//文件不存在
oldAnswer = {}
}
});
} else {
oldAnswer = fs.readFileSync('Answer.txt', 'utf-8')
if ('' != oldAnswer)
oldAnswer = JSON.parse(oldAnswer)
else oldAnswer = {}
}
await client.query('BEGIN')
let answerArr = [];
const dealResource = async (resource, flag, parentId, childNum) => {
let index = 1;
for (let r of resource) {
let query = r.alias || r.show || r.btn
console.log(query);
let name = r.show || r.btn;
let answer = readAnswer ? answers[query] : await getAnswer(query)
if (answerArr.some(a => a == answer)) {
console.error(r);
console.error(`${query}译名重复了!`)
throw 'xxx'
} else {
answerArr.push(answer)
}
let res = {}
/**对比原来的code */
if (oldAnswer[query]) { //resource
res = await client.query(`SELECT * FROM resource where code = $1`, [oldAnswer[query]])
if (!res.rows.length) {
console.log(query, 'code码未找到对应值 :', oldAnswer[query])
throw `code码未找到对应值 : ${oldAnswer[query]}`;
}
if (res.rows[0].name != name || (r.order && res.rows[0].order != r.order) || res.rows[0].order != index) {
await client.query(`UPDATE resource SET name = $1 ,"order" = $2 WHERE id = $3`, [name, r.order || index, res.rows[0].id])
}
if (r.old && oldAnswer[r.old]) {//r如果原来存在
//name code互换
const { id } = res.rows[0];
res = await client.query(`SELECT * FROM resource where code = $1`, [oldAnswer[r.old]]);
await client.query(`UPDATE resource SET code = $1 , name = $2 WHERE id = $3`, [oldAnswer[r.old], r.old, id]);
if (res.rows)
await client.query(`UPDATE resource SET code = $1 , name = $2 WHERE id = $3`, [oldAnswer[query], name, res.rows[0].id])
}
if (oldAnswer[query] != answer) {//原code码和新翻译不一样
// await client.query(`UPDATE system_functions SET code = $1 WHERE code = $2`, [answer, oldAnswer[query]])
answer = oldAnswer[query]; //采用原翻译
}
if (parentId && parentId != res.rows[0].parentId) {//和库里的parent_id不一致
await client.query(`UPDATE resource SET parent_id = $1 WHERE id = $2`, [parentId, res.rows[0].id]);
}
console.log(oldAnswer[query]);
delete oldAnswer[query];
} else {
res = await client.query(`SELECT * FROM resource where code = $1`, [answer])
if (res && !res.rows.length) {
const queryText = `INSERT INTO resource(name, code, "order", create_time, parent_id)
VALUES( $1, $2, $3, $4, $5) RETURNING id`;
res = await client.query(queryText, [name, answer, r.order || index, moment(), parentId ? parentId : null]);
}
}
answerObj[query] = answer;
if (childNum) {
for (let i = 0; i < childNum; i++)
transResult += ' ';
}
transResult += `${query} : ${r.btn ? '按钮' : '功能'} : ${answer} [${r.order || index}]`
if (r.child) {
transResult += '\n';
let num = childNum || 0;
await dealResource(r.child, null, res.rows[0].id, ++num)
} else {
if (!flag)
transResult += ';\n'
}
index++;
}
}
await dealResource(resource, true, null, null)
console.log("oldAnswer", oldAnswer);
/**删除多余code */
for (let item in oldAnswer) {
if (oldAnswer[item]) {
const functionData = await client.query(`SELECT * FROM resource where code = $1`, [oldAnswer[item]]);
if (functionData.rows[0]) {
const dealChild = async (id) => {
/**判断当前的code是否是其它code的父值parent_id */
const childFunction = await client.query(`SELECT * FROM resource where parent_id = $1`, [id]);
if (childFunction.rows.length) {
const childIds = childFunction.rows.reduce((p, c) => p = p.concat(c.id), []);
for (let child of childIds) {
await dealChild(child);
await client.query(`DELETE FROM resource where id = $1`, [child]);
}
}
}
await dealChild(functionData.rows[0].id);
await client.query(`DELETE FROM resource where id = $1`, [functionData.rows[0].id]);
}
}
}
await client.query('COMMIT')
if (!readAnswer) {
const old = fs.readFileSync('Answer.txt', 'utf-8')
fs.writeFileSync('AnswerOld.txt', old, 'utf-8');
fs.writeFileSync('AuthCode.txt', transResult, 'utf-8');
fs.writeFileSync('Answer.txt', JSON.stringify(answerObj), 'utf-8');
console.log('文件生成完毕!')
}
console.log('执行完毕~')
} catch (e) {
await client.query('ROLLBACK')
console.log('执行错误~')
throw e
} finally {
client.release()
}
})().catch(e => console.error(e.stack))
} catch (error) {
console.error(error)
}

19
doc/scripts/tools/v3.22.1_init_HRM_resource/jenkinsfile

@ -0,0 +1,19 @@
pipeline {
agent {
node{
label 'jnlp-slave'
}
}
stages {
stage('Testing hrm......') {
steps {
buildName "#${BUILD_NUMBER} ~/pep/${JOB_NAME}:${IMAGE_VERSION}"
buildDescription "harbor.anxinyun.cn/pep/${JOB_NAME}:${IMAGE_VERSION}"
sh 'nerdctl build -t harbor.anxinyun.cn/pep/${JOB_NAME}:${IMAGE_VERSION} .'
sh 'nerdctl push harbor.anxinyun.cn/pep/${JOB_NAME}:${IMAGE_VERSION}'
}
}
}
}

19
doc/scripts/tools/v3.22.1_init_HRM_resource/jenkinsfile-registry

@ -0,0 +1,19 @@
pipeline {
agent {
node{
label 'jnlp-slave'
}
}
stages {
stage('Testing hrm......') {
steps {
buildName '#${BUILD_NUMBER} ~/pep/${JOB_NAME}:${IMAGE_VERSION}'
buildDescription 'registry.ngaiot.com/pep/${JOB_NAME}:${IMAGE_VERSION}'
sh 'pwd'
sh 'nerdctl build -t registry.ngaiot.com/pep/${JOB_NAME}:${IMAGE_VERSION} .'
sh 'nerdctl push registry.ngaiot.com/pep/${JOB_NAME}:${IMAGE_VERSION}'
}
}
}
}

21
doc/scripts/tools/v3.22.1_init_HRM_resource/package.json

@ -0,0 +1,21 @@
{
"name": "appkey-generator",
"version": "1.0.0",
"description": "tool",
"main": "index.js",
"scripts": {
"test": "mocha",
"start": "set NODE_ENV=development&&node index -e false -t false -p true"
},
"author": "liu",
"license": "ISC",
"dependencies": {
"args": "^3.0.7",
"crypto-js": "^3.3.0",
"pg": "^7.18.2",
"pg-hstore": "^2.3.2",
"sequelize": "^4.22.12",
"superagent": "^6.1.0",
"moment": "^2.29.4"
}
}

39
doc/scripts/tools/v3.22.1_init_HRM_resource/readme.txt

@ -0,0 +1,39 @@
//create by zhouxin on 2022.10.10
一、To 测试/研发小伙伴 (修改数据库配置、环境变量参数)
1.修改config.js数据库配置
todo-todo-todo:
1)测试:修改【ENV_SY_POOL】和【ENV_CS_POOL】定义的配置值;
2)研发:修改【PRO_POOL】定义的配置值
配置参数说明:
{
"host" : 数据库host, //默认值为 10.8.30.36"
"user" : 数据库user, //默认值为 FashionAdmin
"database" : 数据库database, // 默认值为 emis0630,
"password" : 数据库密码, //默认值为 123456",
"port" : 数据库端口号 //默认值为 5432"
}
2. 环境变量/启动参数 修改
todo-todo-todo: 根据场景设置对应环境参数的true/false, 判断顺序为:FS_ENV > FS_ENV_TEST
环境变量参数说明:
{
FS_ENV : false , //商用环境
FS_ENV_TEST : true , //测试环境
}
二、To 研发
//启动参数含义
配置参数说明:
{
e : false , //env 商用环境
t : false , //test 测试环境
p : true //product 研发环境
}
研发思路(同pm权限工具)answerOld.txt文件,用于和新更改的权限做对比
1.第一次执行时,测试需删除answeOld.text文件;研发看情况清空Answer.txt文件还是 readAnswer变量设置为true;
测试环境调用百度翻译接口有问题,更改权限码后需将answerOld.txt提交
2.开发注意:中文名称不变,两个 互换位置可以加old属性,仅限两两互换
存在bug:中文名称未更改只是换了位置的话,是不做更新的,如不换名字换位置,建议把名称调整下或者单独删除原来那条以及answeOld.text去除

22
web/client/src/components/setup.jsx

@ -5,7 +5,7 @@ import {
Checkbox,
} from "@douyinfe/semi-ui";
function Setup (props) {
function Setup(props) {
const {
close,
tableType,
@ -15,14 +15,13 @@ function Setup (props) {
const [check, setCheck] = useState([]);
const checkboxcss = { width: "25%", height: 16, margin: "0 0 20px 0" };
const checkItem = localStorage.getItem(tableType);
useEffect(() => {
//
const checkItem = localStorage.getItem(tableType);
setCheck(checkItem ? JSON.parse(checkItem) : [])
ischeck();
}, []);
function ischeck (value) {
function ischeck(value) {
if (check.length >= length) {
if (check.includes(value)) {
return false;
@ -73,7 +72,20 @@ function Setup (props) {
defaultValue={check}
aria-label="表格属性设置"
onChange={(check) => {
const flag = tableList[0].list.filter(t => t.disabled);
if (flag.length > 0) {
const some = check.filter((e) => !JSON.parse(checkItem).some((e2) => e2 === e))
let newArr = []
JSON.parse(checkItem).forEach((e, index) => {
if (index == JSON.parse(checkItem).length - 2) {
newArr = newArr.concat(some)
}
newArr.push(e)
})
setCheck(JSON.stringify(newArr) == checkItem ? check : newArr);
} else {
setCheck(check);
}
ischeck();
}}
>
@ -105,7 +117,7 @@ function Setup (props) {
key={itm.value}
value={itm.value}
style={checkboxcss}
disabled={ischeck(itm.value)}
disabled={itm.disabled ? itm.disabled : ischeck(itm.value)}
>
{itm.name}
</Checkbox>

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

@ -10,6 +10,7 @@ import * as employeeCommunication from './employeeCommunication'
import * as employeeAssessment from './employeeAssessment'
import * as role from './role'
import * as vacateRemark from './vacateRemark'
export default {
...personnelFiles,
@ -20,5 +21,6 @@ export default {
...resourceRepository,
...employeeCommunication,
...employeeAssessment,
...role
...role,
...vacateRemark
}

15
web/client/src/sections/humanAffairs/actions/vacateRemark.js

@ -0,0 +1,15 @@
'use strict';
import { ApiTable, basicAction } from '$utils'
export function createVacateRemark(query) {//添加请假统计备注
return (dispatch) => basicAction({
type: "put",
dispatch: dispatch,
query: query,
actionType: "PUT_VACATE_REMARK",
url: `${ApiTable.createVacateRemark}`,
msg: { option: '添加备注' },
reducer: {},
});
}

78
web/client/src/sections/humanAffairs/containers/leaveStatistics.jsx

@ -1,6 +1,7 @@
import React, { useEffect, useState, useRef, useMemo } from 'react';
import { connect } from 'react-redux';
import { Table, Button, Pagination, Skeleton, Form, Tooltip } from '@douyinfe/semi-ui';
import VacateRemark from './vacateRemark';
import { IconSearch } from '@douyinfe/semi-icons';
import { SkeletonScreen } from "$components";
import '../style.less'
@ -26,22 +27,27 @@ const leaveStatistics = (props) => {
const [downloadUrl, setDownloadUrl] = useState('')//pdf
const LEAVESTATISTICS = "leaveStatistics";
const page = useRef(query.page);//
const [modalV, setModalV] = useState(false);
const [remark, setRemark] = useState(null);
const [tableList, setTableList] = useState([{
title: '展示信息',
list: [
{ name: "姓名", value: "userName" },
{ name: "所属部门", value: "departmrnt" },
{ name: "职位", value: "roleName" },
{ name: "合计请假时长", value: "vacateDayStatisticDuration" },
{ name: "合计请假次数", value: "vacateCount" },
{ name: "姓名", value: "userName", disabled: true },
{ name: "所属部门", value: "departmrnt", disabled: true },
{ name: "职位", value: "roleName", disabled: true },
{ name: "在职状态", value: "inStatus", disabled: true },
{ name: "合计请假时长", value: "vacateDayStatisticDuration", disabled: true },
{ name: "合计请假次数", value: "vacateCount", disabled: true },
{ name: "备注", value: "remark", disabled: true },
{ name: "操作", value: "operate", disabled: true },
]
}]);//
useEffect(() => {
localStorage.getItem(LEAVESTATISTICS) == null
? localStorage.setItem(
LEAVESTATISTICS,
JSON.stringify(['userName', 'departmrnt', 'roleName', 'vacateCount', 'vacateDayStatisticDuration'])
JSON.stringify(['userName', 'departmrnt', 'roleName', 'inStatus', 'vacateCount', 'vacateDayStatisticDuration', 'remark', 'operate'])
)
: "";
getAttendanceVacateTypeList()
@ -89,6 +95,7 @@ const leaveStatistics = (props) => {
</span>
),
width: 200,
fixed: true,
dataIndex: "userCode",
key: "userCode",
sorter: (a, b) => { },
@ -111,6 +118,7 @@ const leaveStatistics = (props) => {
</div>
),
width: 100,
fixed: true,
dataIndex: "userName",
key: "userName",
render: (_, r, index) => {
@ -123,6 +131,7 @@ const leaveStatistics = (props) => {
</div>
),
width: 200,
fixed: true,
dataIndex: "departmrnt",
key: "departmrnt",
render: (_, r, index) => {
@ -165,6 +174,7 @@ const leaveStatistics = (props) => {
</div>
),
width: 150,
fixed: true,
dataIndex: "roleName",
key: "roleName",
render: (_, r, index) => {
@ -196,6 +206,19 @@ const leaveStatistics = (props) => {
}
</div>);
},
}, {
title: (
<div>
<img src="/assets/images/hrImg/V.png" alt="" style={{ width: 14, height: 14 }} /> 在职状态
</div>
),
width: 120,
fixed: true,
dataIndex: "inStatus",
key: "inStatus",
render: (_, r, index) => {
return (r.userActiveStatus == 1 ? '在职' : r.userActiveStatus == 2 ? '离职' : '特殊状态-特殊账号');
},
},
];
for (let j = 0; j < typeList.length; j++) {
@ -234,6 +257,35 @@ const leaveStatistics = (props) => {
return (r.vacateCount ? r.vacateCount : '0')
},
})
column.push({
title: '备注',
width: 160,
dataIndex: "remark",
key: "remark",
render: (_, r, index) => {
return (
r.remark?.length > 22 ? <Tooltip content={r.remark}>
{r.remark.slice(0, 21) + '...'}
</Tooltip> : (r.remark ? r.remark : '无')
)
},
})
column.push({
title: '操作',
width: 160,
dataIndex: "operate",
key: "operate",
render: (_, r, index) => {
return (
<div>
<span style={{ color: '#1890FF', cursor: 'pointer' }} onClick={() => {
setModalV(true);
setRemark({ pepUserId: r.pepUserId, remark: r.remark })
}}>添加备注</span>
</div>
)
},
})
for (let i = 0; i < arr.length; i++) {
let colum = column.filter((item) => {
return item.key === arr[i];
@ -255,6 +307,11 @@ const leaveStatistics = (props) => {
}
}
const scroll = useMemo(() => ({}), []);
const closeAndFetch = () => {
setModalV(false)
getAttendanceVacateList();
}
return (
<>
<div style={{ padding: '0px 12px' }}>
@ -417,12 +474,19 @@ const leaveStatistics = (props) => {
</div>
</div>
</div>
{
modalV ? <VacateRemark
remarkData={remark}
close={() => closeAndFetch()}
onCancel={() => setModalV(false)} /> : ''
}
</div>
{setup ? (
<Setup
tableType={LEAVESTATISTICS}
tableList={tableList}
length={5 + mytypeList.length}
length={8 + mytypeList.length}
close={() => {
setSetup(false);
attribute(mytypeList);

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

@ -12,7 +12,7 @@ const PersonnelDistribution = (props) => {
const { dispatch, actions } = props
const { humanAffairs } = actions;
const [keywordTarget, setKeywordTarget] = useState('dep');
const [job, setJob] = useState('in');
const [userActiveStatus, setuserActiveStatus] = useState(1);
const [keyword, setKeyword] = useState('');//
const [limits, setLimits] = useState()//
const [query, setQuery] = useState({ limit: 10, page: 0 }); //
@ -38,7 +38,7 @@ const PersonnelDistribution = (props) => {
let kt = keywordTarget == 'place' ? '' : keywordTarget;
let k = keywordTarget == 'place' ? '' : keyword;
let placeSearch = keywordTarget == 'place' ? keyword : '';
dispatch(humanAffairs.getSalesList({ keywordTarget: kt, keyword: k, job, placeSearch, ...query })).then(r => {
dispatch(humanAffairs.getSalesList({ keywordTarget: kt, keyword: k,userActiveStatus, placeSearch, ...query })).then(r => {
if (r.success) {
setTableData(r.payload?.data?.rows);
setLimits(r.payload?.data?.count)
@ -118,6 +118,12 @@ const PersonnelDistribution = (props) => {
let arrStr = text.map(t => t.name);
return getMultis(arrStr);
}
},{
title: '在职状态',
dataIndex: 'userActiveStatus',
key: 'userActiveStatus',
width: 200,
render: (text, r, index) => text == '1'?'在职':text == '2'?'离职':'特殊账号-特殊状态'
}, {
title: '销售区域(省/直辖市)',
dataIndex: 'provinces',
@ -229,10 +235,10 @@ const PersonnelDistribution = (props) => {
</Select>
</div>
<div>
<Select value={job} style={{ width: 170, marginLeft: 15 }} onChange={setJob} >
<Select.Option value='in'>在职</Select.Option>
<Select.Option value='out'>离职</Select.Option>
<Select.Option value='other'>特殊账号-特殊状态</Select.Option>
<Select value={userActiveStatus} style={{ width: 170, marginLeft: 15 }} onChange={setuserActiveStatus} >
<Select.Option value={1}>在职</Select.Option>
<Select.Option value={2}>离职</Select.Option>
<Select.Option value={3}>特殊账号-特殊状态</Select.Option>
</Select>
</div>
<div style={{ margin: '0px 18px' }}>

48
web/client/src/sections/humanAffairs/containers/vacateRemark.js

@ -0,0 +1,48 @@
import React, { useEffect, useRef, useState } from 'react';
import { connect } from "react-redux";
import { Modal, Form } from "@douyinfe/semi-ui";
const VacateRemark = (props) => {
const { dispatch, actions, onCancel, close, remarkData } = props;
console.log(actions, 'actionsactionsactions');
const { humanAffairs } = actions;
const form = useRef();//表单
//初始化
useEffect(() => { }, []);
function handleOk() {
form.current.validate().then((values) => {
dispatch(humanAffairs.createVacateRemark({ pepUserId: remarkData.pepUserId, remark: values.remark || '' })).then((res) => {
if (res.success) {
close();
}
})
})
}
return (
<Modal title='添加备注'
visible={true}
destroyOnClose
okText='保存' width={800}
onOk={handleOk}
onCancel={onCancel}>
<Form getFormApi={(formApi) => (form.current = formApi)} labelPosition={'left'}>
<Form.TextArea
field="remark"
label='备注'
initValue={remarkData?.remark || ""}
placeholder="请输入备注"
/>
</Form>
</Modal>
)
}
function mapStateToProps(state) {
const { global } = state;
return {
actions: global.actions,
};
}
export default connect(mapStateToProps)(VacateRemark);

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

@ -73,6 +73,8 @@ export const ApiTable = {
delRole:'role/del',
getUserRoleList:'roleUser/list',
addUserRole:'roleUser/add',
// 请假统计添加备注
createVacateRemark: 'attendance/vacate/creat/remark'
};
export const RouteTable = {

Loading…
Cancel
Save