Browse Source

头部 50%

master
wenlele 2 years ago
parent
commit
420f8f92a4
  1. 122
      api/app/lib/controllers/patrolPlan/patrolPlan.js
  2. 113
      api/app/lib/controllers/patrolRecord/patrolRecord.js
  3. 431
      api/app/lib/controllers/projectRegime/projectSituation.js
  4. 17
      api/app/lib/routes/patrolPlan/patrolPlan.js
  5. 12
      api/app/lib/routes/patrolRecord/patrolRecord.js
  6. 31
      api/app/lib/routes/projectRegime/index.js
  7. BIN
      web/client/assets/images/avatar/1.png
  8. BIN
      web/client/assets/images/avatar/10.png
  9. BIN
      web/client/assets/images/avatar/11.png
  10. BIN
      web/client/assets/images/avatar/12.png
  11. BIN
      web/client/assets/images/avatar/2.png
  12. BIN
      web/client/assets/images/avatar/3.png
  13. BIN
      web/client/assets/images/avatar/4.png
  14. BIN
      web/client/assets/images/avatar/5.png
  15. BIN
      web/client/assets/images/avatar/6.png
  16. BIN
      web/client/assets/images/avatar/7.png
  17. BIN
      web/client/assets/images/avatar/8.png
  18. BIN
      web/client/assets/images/avatar/9.png
  19. BIN
      web/client/assets/images/avatar/avatar.jpg
  20. BIN
      web/client/assets/images/homePage/close.png
  21. BIN
      web/client/assets/images/homePage/gis-infowindow-bg.png
  22. BIN
      web/client/assets/images/homePage/gis/marker.gif
  23. BIN
      web/client/assets/images/homePage/gis/mingchu_anquan.png
  24. BIN
      web/client/assets/images/homePage/gis/mingchu_weisheng.png
  25. BIN
      web/client/assets/images/homePage/gis/企业-中低风险.png
  26. BIN
      web/client/assets/images/homePage/gis/企业-中风险.png
  27. BIN
      web/client/assets/images/homePage/gis/企业-低风险.png
  28. BIN
      web/client/assets/images/homePage/gis/企业-高风险.png
  29. BIN
      web/client/assets/images/homePage/gis/特种设备企业.png
  30. BIN
      web/client/assets/images/homePage/gis/食堂.png
  31. BIN
      web/client/assets/images/homePage/u1025.png
  32. BIN
      web/client/assets/images/homePage/u1026.png
  33. BIN
      web/client/assets/images/homePage/u1031.png
  34. BIN
      web/client/assets/images/homePage/u1036.png
  35. BIN
      web/client/assets/images/homePage/u133.png
  36. BIN
      web/client/assets/images/homePage/u145.png
  37. BIN
      web/client/assets/images/homePage/u150.png
  38. BIN
      web/client/assets/images/homePage/u151.png
  39. BIN
      web/client/assets/images/homePage/u162.png
  40. BIN
      web/client/assets/images/homePage/u165.png
  41. BIN
      web/client/assets/images/homePage/u182.png
  42. BIN
      web/client/assets/images/homePage/u183.png
  43. BIN
      web/client/assets/images/homePage/u184.png
  44. BIN
      web/client/assets/images/homePage/u189.png
  45. BIN
      web/client/assets/images/login.png
  46. BIN
      web/client/assets/images/login_bg.png
  47. BIN
      web/client/assets/images/logo.png
  48. BIN
      web/client/assets/images/monitor/chose-none.png
  49. BIN
      web/client/assets/images/monitor/header-bg.png
  50. BIN
      web/client/assets/images/monitor/pitch-on.png
  51. BIN
      web/client/assets/images/monitor/screen-bg.png
  52. BIN
      web/client/assets/images/monitor/strip.png
  53. BIN
      web/client/assets/images/monitor/user.png
  54. 7
      web/client/src/app.js
  55. 0
      web/client/src/components/README.txt
  56. 316
      web/client/src/components/Upload/index.js
  57. 389
      web/client/src/components/Uploads/index.js
  58. 683
      web/client/src/components/export/index.js
  59. 13
      web/client/src/components/export/index.less
  60. 74
      web/client/src/components/flowRecordTable/index.js
  61. 0
      web/client/src/components/flowRecordTable/index.less
  62. 23
      web/client/src/components/index.js
  63. 70
      web/client/src/components/limit-textarea/index.js
  64. 10
      web/client/src/components/limit-textarea/index.less
  65. 21
      web/client/src/components/no-resource/index.js
  66. 217
      web/client/src/components/search/index.js
  67. 3
      web/client/src/components/search/index.less
  68. 82
      web/client/src/components/sketchColor/index.js
  69. 259
      web/client/src/components/table/index.js
  70. 34
      web/client/src/components/table/index.less
  71. 2
      web/client/src/index.js
  72. 2
      web/client/src/layout/components/header/index.js
  73. 14
      web/client/src/layout/components/sider/index.js
  74. 3
      web/client/src/sections/auth/containers/login.js
  75. 0
      web/client/src/sections/bigScreen/actions/authority.js
  76. 0
      web/client/src/sections/bigScreen/actions/index.js
  77. 0
      web/client/src/sections/bigScreen/actions/user.js
  78. 26
      web/client/src/sections/bigScreen/components/basis.js
  79. 26
      web/client/src/sections/bigScreen/components/capacity.js
  80. 27
      web/client/src/sections/bigScreen/components/electrical.js
  81. 75
      web/client/src/sections/bigScreen/components/header.js
  82. 26
      web/client/src/sections/bigScreen/components/realTime.js
  83. 5
      web/client/src/sections/bigScreen/containers/index.js
  84. 50
      web/client/src/sections/bigScreen/containers/systemManagement.js
  85. 0
      web/client/src/sections/bigScreen/index.js
  86. 22
      web/client/src/sections/bigScreen/nav-item.js
  87. 0
      web/client/src/sections/bigScreen/reducers/index.js
  88. 27
      web/client/src/sections/bigScreen/routes.js
  89. 88
      web/client/src/sections/organization/components/deptModal.js
  90. 74
      web/client/src/sections/organization/components/resetPwd.js
  91. 121
      web/client/src/sections/organization/components/resource.js
  92. 171
      web/client/src/sections/organization/components/userModal.js
  93. 149
      web/client/src/sections/organization/containers/authority.js
  94. 6
      web/client/src/sections/organization/containers/index.js
  95. 332
      web/client/src/sections/organization/containers/user.js
  96. 22
      web/client/src/sections/organization/nav-item.js
  97. 26
      web/client/src/sections/organization/routes.js
  98. 9
      web/client/src/sections/patrolManage/actions/index.js
  99. 80
      web/client/src/sections/patrolManage/actions/plan.js
  100. 17
      web/client/src/sections/patrolManage/actions/record.js

122
api/app/lib/controllers/patrolPlan/patrolPlan.js

@ -1,122 +0,0 @@
'use strict';
async function getPatrolPlan(ctx, next) {
try {
const models = ctx.fs.dc.models;
const { limit, page } = ctx.query;
let options = {
include: [{
required: true,
model: models.User,
attributes: ['id', 'name'],
include: [{
required: true,
model: models.Department,
attributes: ['id', 'name'],
}]
}, {
required: true,
model: models.Project,
attributes: ['id', 'name'],
}]
};
if (limit) {
options.limit = Number(limit);
}
if (page && limit) {
options.offset = Number(page) * Number(limit);
}
let res = await models.PatrolPlan.findAndCountAll(options);
ctx.status = 200;
ctx.body = res;
} catch (error) {
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
ctx.status = 400;
ctx.body = {
"message": "获取巡检计划失败"
}
}
}
async function createPatrolPlan(ctx, next) {
try {
const models = ctx.fs.dc.models;
const data = ctx.request.body;
const { name, way, structureId, startTime, endTime, frequency, points, userId } = data;
let plan = { name, way, structureId, startTime, endTime, frequency, points, userId };
await models.PatrolPlan.create(plan);
ctx.status = 204;
} catch (error) {
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
ctx.status = 400;
ctx.body = {
"message": '新增巡检计划失败'
}
}
}
async function updatePatrolPlan(ctx, next) {
try {
let errMsg = '修改巡检计划失败';
const models = ctx.fs.dc.models;
const data = ctx.request.body;
const { name, way, structureId, startTime, endTime, frequency, points, userId } = data;
let plan = { name, way, structureId, startTime, endTime, frequency, points, userId };
if (data && data.id) {
await models.PatrolPlan.update(plan, {
where: { id: data.id }
})
} else {
errMsg = '请传入巡检计划id';
throw errMsg;
}
ctx.status = 204;
} catch (error) {
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
ctx.status = 400;
ctx.body = {
"message": errMsg
}
}
}
async function delPatrolPlan(ctx, next) {
try {
let errMsg = '删除巡检计划失败';
const models = ctx.fs.dc.models;
const { id } = ctx.params;
const record = await models.PatrolRecord.findOne({
where: { patrolPlanId: id }
});
if (record) {
errMsg = '不能删除有巡检记录的计划';
throw errMsg;
}
await models.PatrolPlan.destroy({
where: { id }
})
ctx.status = 204;
} catch (error) {
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
ctx.status = 400;
ctx.body = { message: error }
}
}
module.exports = {
getPatrolPlan,
createPatrolPlan,
updatePatrolPlan,
delPatrolPlan,
}

113
api/app/lib/controllers/patrolRecord/patrolRecord.js

@ -1,113 +0,0 @@
'use strict';
async function findPatrolRecord(ctx, next) {
let rslt = [];
let error = { name: 'FindError', message: '获取巡检记录失败' };
try {
const models = ctx.fs.dc.models;
const { startTime, endTime, alarm, patrolPlanId, pointId } = ctx.params;
// patrolPlanId传all查所有
if (patrolPlanId == 'all') {
/* 如果有startTime && endTime,查询所有符合条件的数据 */
if (startTime !== 'null' && endTime !== 'null') {
if (pointId !== 'null') {
if (alarm == 'null') {
rslt = await models.PatrolRecord.findAll({
where: { inspectionTime: { $between: [startTime, endTime] }, pointId: { $in: pointId.split(',') } },
});
} else {
rslt = await models.PatrolRecord.findAll({
where: { alarm, inspectionTime: { $between: [startTime, endTime] }, pointId: { $in: pointId.split(',') } },
});
}
} else {
if (alarm == 'null') {
rslt = await models.PatrolRecord.findAll({
where: { inspectionTime: { $between: [startTime, endTime] } },
});
} else {
rslt = await models.PatrolRecord.findAll({
where: { alarm, inspectionTime: { $between: [startTime, endTime] } },
});
}
}
} else {
/* 如果没有startTime && endTime,查询每个点位最新一条符合条件的数据 */
let a = []
if (pointId !== 'null') {
a = await models.PatrolRecord.findAll({
where: { pointId: { $in: pointId.split(',') } },
});
}
rslt = pointId.split(',').map(i => {
return a.filter(t => t.pointId == i).sort((a, b) => b.id - a.id)[0] || null
})
}
} else {
if (startTime !== 'null' && endTime !== 'null') {
if (pointId !== 'null') {
rslt = await models.PatrolRecord.findAll({
where: { patrolPlanId: { $in: patrolPlanId.split(',') }, alarm, inspectionTime: { $between: [startTime, endTime] }, pointId: { $in: pointId.split(',') } },
});
} else {
rslt = await models.PatrolRecord.findAll({
where: { patrolPlanId: { $in: patrolPlanId.split(',') }, alarm, inspectionTime: { $between: [startTime, endTime] } },
});
}
} else {
let a = []
/* 如果没有startTime && endTime,查询每个点位最新一条符合条件的数据 */
if (pointId !== 'null') {
a = await models.PatrolRecord.findAll({
where: { patrolPlanId: { $in: patrolPlanId.split(',') }, pointId: { $in: pointId.split(',') } },
});
} else {
a = await models.PatrolRecord.findAll({
where: { patrolPlanId: { $in: patrolPlanId.split(',') } },
});
}
rslt = pointId.split(',').map(i => {
return a.filter(t => t.pointId == i).sort((a, b) => b.id - a.id)[0] || null
})
}
}
ctx.status = 200;
ctx.body = rslt;
error = null
} catch (error) {
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
ctx.status = 400;
ctx.body = {
"message": "获取巡检记录失败"
}
}
}
async function addPatrolRecord(ctx, next) {
let error = { name: 'addError', message: '新增巡检记录失败' };
try {
const models = ctx.fs.dc.models;
const data = ctx.request.body;
let { patrolPlanId, lastInspectionTime, inspectionTime, points, alarm, pointId } = data
let record = { patrolPlanId, lastInspectionTime, inspectionTime, points, alarm, pointId }
await models.PatrolRecord.create(record);
ctx.status = 204;
error = null
} catch (error) {
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
ctx.status = 400;
ctx.body = {
"message": '新增巡检计划失败'
}
}
}
module.exports = {
findPatrolRecord,
addPatrolRecord,
}

431
api/app/lib/controllers/projectRegime/projectSituation.js

@ -1,431 +0,0 @@
'use strict';
async function projectList (ctx, next) {
try {
const models = ctx.fs.dc.models;
let userInfo = ctx.fs.api.userInfo;
const { limit, page, name, justStructure } = ctx.query;
let options = {
where: {
},
// include: [{
// as: 'company',
// model: models.Company,
// attributes: ['id', 'name'],
// },],
}
if (limit) {
options.limit = Number(limit)
}
if (page && limit) {
options.offset = Number(page) * Number(limit)
}
if (name) {
options.where.name = { $like: `%${name}%` }
}
let res = []
if (justStructure) {
res = await models.Project.findAndCountAll({
attributes: ['id', 'name'],
})
} else {
res = await models.Project.findAndCountAll(options)
}
ctx.status = 200;
ctx.body = res
} catch (error) {
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
ctx.status = 400;
ctx.body = {
"message": "获取结构列表失败"
}
}
}
async function postAddProject (ctx, next) {
try {
const models = ctx.fs.dc.models;
let userInfo = ctx.fs.api.userInfo;
const data = ctx.request.body;
const { img, longitude, latitude, name, type, describe } = data
let errMsg = data.id ? '结构物编辑失败' : '结构物新增失败'
let project = { img, longitude, latitude, name, type, describe, userId: userInfo.id }
const alikeProject = await models.Project.findOne({
where: {
name: name,
}
})
if ((!data.id && alikeProject) || (alikeProject && alikeProject.id !== data.id)) {
errMsg = '已有相同结构物名称'
throw errMsg
}
if (data && data.id) {
await models.Project.update(project, {
where: {
id: data.id
}
})
} else {
await models.Project.create(project)
}
ctx.status = 204;
} catch (error) {
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`)
ctx.status = 400;
ctx.body = {
"message": errMsg
}
}
}
async function delProject (ctx, next) {
try {
const models = ctx.fs.dc.models;
let userInfo = ctx.fs.api.userInfo;
const { id } = ctx.params
//删除结构物
await models.Project.destroy({
where: {
id,
}
})
const pointId = []
const pointLIst = await models.Point.findAll({
where: {
projectId: id,
},
attributes: ['id'],
})
pointLIst.map(v => pointId.push(v.id))
//点位
await models.Point.destroy({
where: {
projectId: id
}
})
//巡检计划
const planId = []
const planLIst = await models.PatrolPlan.findAll({
where: {
structureId: id,
},
attributes: ['id'],
})
planLIst.map(v => planId.push(v.id))
await models.PatrolPlan.destroy({
where: {
structureId: id
}
})
//巡检记录
await models.PatrolRecord.destroy({
where: {
pointId: { $in: pointId },
patrolPlanId: { $in: planId }
}
})
ctx.status = 204;
} catch (error) {
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`)
ctx.status = 400;
ctx.body = {
"message": '删除结构物失败'
}
}
}
async function addPosition (ctx, next) {
try {
const models = ctx.fs.dc.models;
let userInfo = ctx.fs.api.userInfo;
const data = ctx.request.body;
const { longitude, latitude, name, describe, qrCode, projectId, } = data
let errMsg = data.id ? '点位编辑失败' : '点位新增失败'
let pointData = { longitude, latitude, name, describe, qrCode, projectId }
const alikeProject = await models.Point.findOne({
where: {
id: data.id,
}
})
if (data && data.id) {
if (qrCode) {
await models.Point.update({ ...alikeProject, qrCode }, {
where: {
id: data.id,
}
})
} else {
await models.Point.update({ pointData }, {
where: {
id: data.id,
}
})
}
} else {
await models.Point.create(pointData)
}
ctx.status = 204;
} catch (error) {
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`)
ctx.status = 400;
ctx.body = {
"message": errMsg
}
}
}
async function position (ctx, next) {
try {
const models = ctx.fs.dc.models;
let userInfo = ctx.fs.api.userInfo;
const { limit, page, projectId } = ctx.query;
let options = {
where: {
id: projectId
},
include: [{
model: models.Point,
},],
}
if (limit) {
options.limit = Number(limit)
}
if (page && limit) {
options.offset = Number(page) * Number(limit)
}
let res = await models.Project.findAndCountAll(options)
ctx.status = 200;
ctx.body = res
} catch (error) {
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
ctx.status = 400;
ctx.body = {
"message": "获取结构列表失败"
}
}
}
async function delPosition (ctx, next) {
try {
const models = ctx.fs.dc.models;
let userInfo = ctx.fs.api.userInfo;
const { id } = ctx.params
const pointOne = await models.Point.findOne({
where: {
id
},
attributes: ['projectId'],
})
if (pointOne) {
const patrolPlanLIst = await models.PatrolPlan.findAll({
where: {
structureId: pointOne.projectId,
},
})
for (var u of patrolPlanLIst) {
const points = []
u.points.map(r => {
if (r.id == id) {
} else {
points.push(r)
}
})
u.points = points
await models.PatrolRecord.destroy({
where: {
pointId: id,
patrolPlanId: u.id
}
})
if (points.length > 0) {
let data = {
name: u.dataValues.name,
way: u.dataValues.way,
structureId: u.dataValues.structureId,
startTime: u.dataValues.startTime,
endTime: u.dataValues.endTime,
frequency: u.dataValues.frequency,
points: u.dataValues.points,
userId: u.dataValues.userId,
patrolCount: u.dataValues.patrolCount
}
await models.PatrolPlan.update(data,{
where: {
id: u.dataValues.id
}
})
} else {
await models.PatrolPlan.destroy({
where: {
id: u.id
}
})
}
}
}
await models.Point.destroy({
where: {
id,
}
})
ctx.status = 204;
} catch (error) {
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`)
ctx.status = 400;
ctx.body = {
"message": '删除点位失败'
}
}
}
async function qrCodeShow (ctx, next) {
try {
const models = ctx.fs.dc.models;
let userInfo = ctx.fs.api.userInfo;
const { projectId, name } = ctx.query;
let options = {
where: {
qrCode: { $ne: null }
},
}
if (projectId) {
options.where.projectId = projectId
}
if (name) {
options.where.name = { $like: `%${name}%` }
}
let res = await models.Point.findAndCountAll(options)
ctx.status = 200;
ctx.body = res
} catch (error) {
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
ctx.status = 400;
ctx.body = {
"message": "获取二维码列表失败"
}
}
}
async function q (ctx) {
// let error = {
// name: 'FindError',
// message: "获取失败!"
// };
// const models = ctx.fs.dc.models;
// const { devices } = ctx.request.body
// const attachment = ctx.app.fs.qn_attachment
// try {
// if (!Array.isArray(devices)) {
// error = { name: 'paramsError', message: '参数不能为空' };
// ctx.throw(400);
// }
// const devicesArr = await models.Device.findAll({
// attributes: ['deviceNo', 'periodCode', 'qrSrc'],
// where: { deviceNo: { $in: devices } }
// })
// let ids = [], idsMap = {}, qnImages = []
// devicesArr.forEach(d => {
// const qrSrc = d.qrSrc
// const deviceNo = d.deviceNo
// const periodCode = d.periodCode
// if (qrSrc) {
// if (/^\d+$/.test(qrSrc)) {
// ids.push(qrSrc)
// idsMap[qrSrc] = { deviceNo, periodCode }
// } else {
// let domain = globalCache.getQnDomain()
// let imgUrl = `${domain}/${qrSrc}`
// qnImages.push({ src: imgUrl, deviceNo, periodCode })
// }
// }
// })
// const docs = await models.QrcodePng.findAll({
// where: {
// id: { $in: ids }
// },
// attributes: ["id", "base64"]
// })
// let pics = []
// if (docs.length > 0) {
// pics = docs.map((d) => {
// let { deviceNo, periodCode } = idsMap[d.id] || {}
// let base64 = d.base64.replace(/^data:image\/\w+;base64,/, '')
// return {
// url: Buffer.from(base64, 'base64'),
// name: deviceNo,
// periodCode
// }
// })
// }
// if (qnImages.length > 0) {
// let qns = await downloadImgsAsBase64(qnImages)
// pics = pics.concat(qns)
// }
// let fileUrl = await downLoadImageBiz(pics, { zipName: "二维码_" + moment().format("YYYY-MM-DD-HH-mm-ss"), attachment })
// add2CleanCache(fileUrl, attachment)
// ctx.status = 200
// ctx.body = { fileUrl }
// } catch (err) {
// ctx.fs.logger.error(err);
// ctx.status = 400;
// ctx.body = error;
// }
}
module.exports = {
projectList,
postAddProject,
delProject,
addPosition,
position,
delPosition,
qrCodeShow,
q
}

17
api/app/lib/routes/patrolPlan/patrolPlan.js

@ -1,17 +0,0 @@
'use strict';
const patrolPlan = require('../../controllers/patrolPlan/patrolPlan');
module.exports = function (app, router, opts) {
app.fs.api.logAttr['GET/patrolPlan'] = { content: '获取巡检计划', visible: false };
router.get('/patrolPlan', patrolPlan.getPatrolPlan);
app.fs.api.logAttr['POST/patrolPlan'] = { content: '新增巡检计划', visible: true };
router.post('/patrolPlan', patrolPlan.createPatrolPlan);
app.fs.api.logAttr['PUT/patrolPlan'] = { content: '修改巡检计划', visible: true };
router.put('/patrolPlan', patrolPlan.updatePatrolPlan);
app.fs.api.logAttr['DELETE/patrolPlan/:id'] = { content: '删除巡检计划', visible: true };
router.del('/patrolPlan/:id', patrolPlan.delPatrolPlan);
};

12
api/app/lib/routes/patrolRecord/patrolRecord.js

@ -1,12 +0,0 @@
'use strict';
const patrolRecord = require('../../controllers/patrolRecord/patrolRecord');
module.exports = function (app, router, opts) {
app.fs.api.logAttr['GET/patrolRecord/:patrolPlanId/:startTime/:endTime/:alarm/:pointId'] = { content: '获取巡检记录', visible: true };
// web端、小程序端查数据:patrolPlanId为all,不需要传pointId
// 小程序端查点位最新一条数据:startTime、endTime、alarm不传
router.get('/patrolRecord/:patrolPlanId/:startTime/:endTime/:alarm/:pointId', patrolRecord.findPatrolRecord);
app.fs.api.logAttr['POST/patrolRecord/add'] = { content: '新增巡检记录', visible: true }
router.post('/patrolRecord/add', patrolRecord.addPatrolRecord);
};

31
api/app/lib/routes/projectRegime/index.js

@ -1,31 +0,0 @@
'use strict';
const projectSituation = require('../../controllers/projectRegime/projectSituation');
module.exports = function (app, router, opts) {
app.fs.api.logAttr['GET/projectList'] = { content: '获取结构物列表', visible: false };
router.get('/projectList', projectSituation.projectList);
app.fs.api.logAttr['POST/addProject'] = { content: '新增修改结构物', visible: false };
router.post('/addProject', projectSituation.postAddProject);
app.fs.api.logAttr['DEL/delProject/:id'] = { content: '删除结构物', visible: false };
router.del('/delProject/:id', projectSituation.delProject);
app.fs.api.logAttr['POST/position'] = { content: '新增修改点位', visible: false };
router.post('/position', projectSituation.addPosition);
app.fs.api.logAttr['GET/position'] = { content: '获取点位列表', visible: false };
router.get('/position', projectSituation.position);
app.fs.api.logAttr['DEL/delPosition/:id'] = { content: '删除点位', visible: false };
router.del('/delPosition/:id', projectSituation.delPosition);
app.fs.api.logAttr['GET/qrCodeShow'] = { content: '获取二维码列表', visible: false };
router.get('/qrCodeShow', projectSituation.qrCodeShow);
app.fs.api.logAttr['GET/q'] = { content: '获取二维码列表', visible: false };
router.get('/q', projectSituation.q);
}

BIN
web/client/assets/images/avatar/1.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

BIN
web/client/assets/images/avatar/10.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

BIN
web/client/assets/images/avatar/11.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

BIN
web/client/assets/images/avatar/12.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

BIN
web/client/assets/images/avatar/2.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

BIN
web/client/assets/images/avatar/3.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

BIN
web/client/assets/images/avatar/4.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

BIN
web/client/assets/images/avatar/5.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

BIN
web/client/assets/images/avatar/6.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

BIN
web/client/assets/images/avatar/7.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

BIN
web/client/assets/images/avatar/8.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

BIN
web/client/assets/images/avatar/9.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

BIN
web/client/assets/images/avatar/avatar.jpg

Binary file not shown.

Before

Width:  |  Height:  |  Size: 202 KiB

BIN
web/client/assets/images/homePage/close.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 725 B

BIN
web/client/assets/images/homePage/gis-infowindow-bg.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

BIN
web/client/assets/images/homePage/gis/marker.gif

Binary file not shown.

Before

Width:  |  Height:  |  Size: 169 KiB

BIN
web/client/assets/images/homePage/gis/mingchu_anquan.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.7 KiB

BIN
web/client/assets/images/homePage/gis/mingchu_weisheng.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

BIN
web/client/assets/images/homePage/gis/企业-中低风险.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

BIN
web/client/assets/images/homePage/gis/企业-中风险.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

BIN
web/client/assets/images/homePage/gis/企业-低风险.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

BIN
web/client/assets/images/homePage/gis/企业-高风险.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

BIN
web/client/assets/images/homePage/gis/特种设备企业.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

BIN
web/client/assets/images/homePage/gis/食堂.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

BIN
web/client/assets/images/homePage/u1025.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

BIN
web/client/assets/images/homePage/u1026.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 524 B

BIN
web/client/assets/images/homePage/u1031.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

BIN
web/client/assets/images/homePage/u1036.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 873 B

BIN
web/client/assets/images/homePage/u133.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 206 KiB

BIN
web/client/assets/images/homePage/u145.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 150 B

BIN
web/client/assets/images/homePage/u150.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 950 B

BIN
web/client/assets/images/homePage/u151.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 873 B

BIN
web/client/assets/images/homePage/u162.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 815 B

BIN
web/client/assets/images/homePage/u165.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 193 B

BIN
web/client/assets/images/homePage/u182.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 147 B

BIN
web/client/assets/images/homePage/u183.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 999 B

BIN
web/client/assets/images/homePage/u184.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 648 B

BIN
web/client/assets/images/homePage/u189.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 932 B

BIN
web/client/assets/images/login.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 646 KiB

BIN
web/client/assets/images/login_bg.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 200 KiB

BIN
web/client/assets/images/logo.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.8 KiB

BIN
web/client/assets/images/monitor/chose-none.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

BIN
web/client/assets/images/monitor/header-bg.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 234 KiB

BIN
web/client/assets/images/monitor/pitch-on.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

BIN
web/client/assets/images/monitor/screen-bg.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

BIN
web/client/assets/images/monitor/strip.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

BIN
web/client/assets/images/monitor/user.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

7
web/client/src/app.js

@ -3,10 +3,7 @@
import React, { useEffect } from 'react';
import Layout from './layout';
import Auth from './sections/auth';
import Safetymanage from './sections/safetymanage';
import ProjectRegime from './sections/projectRegime';
import Organization from './sections/organization';
import PatrolManage from './sections/patrolManage';
import bigScreen from './sections/bigScreen';
const App = props => {
const { projectName } = props
@ -18,7 +15,7 @@ const App = props => {
return (
<Layout
title={projectName}
sections={[Auth, ProjectRegime, Safetymanage, Organization, PatrolManage]}
sections={[Auth, bigScreen]}
/>
)

0
web/client/src/components/README.txt

316
web/client/src/components/Upload/index.js

@ -1,316 +0,0 @@
'use strict';
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { Spin, Upload, message, Modal, Card, Button } from 'antd';
import moment from 'moment';
import { PlusOutlined, UploadOutlined, CloseOutlined } from '@ant-design/icons';
class Uploads extends Component {
constructor(props) {
super(props);
this.ApiRoot = localStorage.getItem('tyApiRoot')
this.state = {
fileUploading: false,
fileList: [],
curPreviewPic: '',
delPicIng: false,
removeFilesList: []
};
}
dealName = (uploaded) => {
let realName = uploaded.split('/')[2]
let x1 = realName.split('.')
let x2 = x1[0].split('_')
let showName = `${x2[0]}.${x1[1]}`
return showName
}
// setFileList = (value) => {
// let defaultFileList = [];
// defaultFileList = value.map((u, index) => {
// let fileUrl = `${this.ApiRoot}/${u.url}`;
// return {
// uid: -index - 1,
// name: this.dealName(u.url),
// status: 'done',
// storageUrl: u.url,
// url: fileUrl
// };
// });
// onChange(defaultFileList)
// this.setState({
// fileList: defaultFileList
// });
// };
componentDidMount() {
const { value } = this.props;
if (value) {
this.setState(value);
}
}
componentWillReceiveProps(np) {
const { dispatch, value: thisEditData, onChange } = this.props;
const { value: nextEditData } = np;
const setFileList = () => {
let defaultFileList = [];
defaultFileList = nextEditData.map((u, index) => {
let fileUrl = `${this.ApiRoot}/${u.storageUrl}`;
return {
uid: -index - 1,
name: this.dealName(u.storageUrl),
status: 'done',
storageUrl: u.storageUrl,
url: fileUrl,
size: u.size || -1
};
});
this.setState({
fileList: defaultFileList
});
};
if (nextEditData && nextEditData.length) {
if (!thisEditData || !this.state.fileList.length) {
setFileList();
} else if (nextEditData.length != thisEditData.length) {
setFileList();
} else {
let repeat = true;
for (let i = 0; i < thisEditData.length; i++) {
if (thisEditData[i] != nextEditData[i]) {
repeat = false;
break;
}
}
if (!repeat) {
setFileList();
}
}
}
// else{
// this.setState({
// fileList:[],
// })
// }
}
render() {
const UploadPath = {
project: ['txt', 'dwg', 'doc', 'docx', 'xls', 'xlsx', 'pdf', 'png', 'jpg', 'rar', 'zip'],
report: ['doc', 'docx', 'xls', 'xlsx', 'pdf'],
data: ['txt', 'xls', 'xlsx'],
image: ['png', 'jpg', 'svg', 'jpeg'],
three: ['js'],
video: ['mp4']
};
/**
* uploadType string 主要区别文件上传路径 以及类型 web/routes/attachment/index.js UploadPath key 值为准默认 project
* disabled boolean 上传是否可用
* maxFilesNum number 最大上传数量
* fileTypes array[string] 可允许上传的文件类型
* maxFileSize number 单个文件最大大小 M
* listType antd upload 组件的属性
* onChange function 文件数量变化时候回调 返回文件
* value array[obj] 编辑数据 [{url:'xxx', [size:999]}]
* onStateChange function 文件状态改变回调函数 上传中 return { uploading:true/false }
*/
const {
uploadType,
disabled,
maxFilesNum,
fileTypes,
maxFileSize,
listType,
onChange,
value,
showUploadList,
onStateChange
} = this.props;
const { fileList, curPreviewPic, delPicIng, removeFilesList } = this.state;
const that = this;
let uploadType_ = uploadType || 'project';
let maxFilesNum_ = maxFilesNum || 1;
let defaultFileTypes = fileTypes || UploadPath[uploadType_];
const uploadProps = {
name: 'checkFile_',
multiple: false,
showUploadList: showUploadList || true,
action: `${this.ApiRoot}/attachments/${uploadType_}`,
listType: listType || 'text',
disabled: disabled,
beforeUpload: (file) => {
if (fileList.length >= maxFilesNum_) {
message.warning(`最多选择${maxFilesNum_}个文件上传`);
return false;
}
if (file.name.length > 60) {
message.warning(`文件名过长(大于60字符),请修改后上传`);
return false;
}
const extNames = file.name.split('.');
var reg = /^[\.\s\u4e00-\u9fa5a-zA-Z0-9_-]{0,}$/;
if (!reg.exec(file.name)) {
message.warning(`文件名包含除字母、汉字、数字、中划线、下划线之外的字符,请修改后上传`);
return false;
}
let isDAE = false;
if (extNames.length > 0) {
let fileType = extNames[extNames.length - 1].toLowerCase();
isDAE = defaultFileTypes.some((f) => f == fileType);
}
if (!isDAE) {
message.error(`只能上传 ${defaultFileTypes.join()} 格式的文件!`);
return false;
}
const isLt = file.size / 1024 / 1024 < (maxFileSize || 3);
if (!isLt) {
message.error(`文件必须小于${maxFileSize || 3}MB!`);
return false;
}
this.setState({
fileUploading: true
});
if (onStateChange) {
onStateChange({ uploading: true });
}
},
onChange(info) {
const status = info.file.status;
if (status === 'uploading') {
that.setState({
fileList: info.fileList
});
}
if (status === 'done') {
let { uploaded, url } = info.file.response;
let size = info.file.size;
let nextFileList = fileList;
nextFileList[nextFileList.length - 1] = {
uid: -moment().unix(),
name: that.dealName(uploaded),
status: 'done',
storageUrl: uploaded,
url: url,
size: size
};
onChange(nextFileList);
that.setState({
fileUploading: false,
fileList: nextFileList
});
if (onStateChange) {
onStateChange({ uploading: false });
}
} else if (status === 'error') {
that.setState({
fileUploading: false
});
message.error(`${info.file.name} 上传失败,请重试`);
if (onStateChange) {
onStateChange({ uploading: false });
}
}
},
onRemove(file) {
let nextFileList = [];
fileList.map((f, i) => {
if (f.uid != file.uid) {
nextFileList.push(f);
}
});
let nextRemoveFiles = removeFilesList.concat([file.storageUrl]);
if (curPreviewPic == file.url) {
that.setState({
curPreviewPic: ''
});
}
onChange(nextFileList);
that.setState({
fileList: nextFileList,
removeFilesList: nextRemoveFiles
});
},
onPreview(file) {
let filePostfix = file.url.split('.').pop();
filePostfix = filePostfix.toLowerCase();
if (UploadPath.image.some((img) => img == filePostfix)) {
that.setState({
curPreviewPic: file.url
});
} else {
message.warn('仅支持图片预览');
}
}
};
let fileList_ = fileList
// .map(f => {
// if (f.storageUrl) {
// let realName = f.storageUrl.split('/').pop()
// if (f.name != realName) {
// f.name = realName
// }
// }
// return f
// })
return (
<div>
<Spin spinning={delPicIng}>
<Upload {...uploadProps} fileList={fileList_}>
{
disabled ? (
''
) :
listType == 'picture-card' ?
(
fileList.length >= maxFilesNum_ ? null : (
<div style={{}}>
<PlusOutlined />
<div>上传图片</div>
</div>
)
) : (
<Button disabled={fileList.length >= maxFilesNum_} icon={<UploadOutlined />}> 文件上传 </Button>
)
}
</Upload>
{
curPreviewPic ? (
<Card
bodyStyle={{
padding: 8
}}
>
<div style={{ marginBottom: 8 }} >
<span>文件预览</span>
<span
style={{ float: 'right' }}
onClick={() => { this.setState({ curPreviewPic: '' }); }}
>
<CloseOutlined style={{ fontSize: 20 }} />
</span>
</div>
<img style={{ width: '100%' }} src={curPreviewPic}></img>
</Card>
) : ''
}
</Spin>
</div>
);
}
}
function mapStateToProps(state) {
const { auth } = state
return {
user: auth.user
};
}
export default connect(mapStateToProps)(Uploads);

389
web/client/src/components/Uploads/index.js

@ -1,389 +0,0 @@
'use strict';
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { Spin, Upload, message, Modal, Card, Button } from 'antd';
import moment from 'moment';
import { PlusOutlined, UploadOutlined, CloseOutlined } from '@ant-design/icons';
class Uploads extends Component {
constructor(props) {
super(props);
this.ApiRoot = localStorage.getItem('tyApiRoot')
this.qnDomain = localStorage.getItem('qnDomain');
this.aliAdmin = localStorage.getItem('aliAdmin');
this.state = {
fileUploading: false,
fileList: [],
curPreviewPic: '',
curPreviewVideo: '',
delPicIng: false,
removeFilesList: []
};
}
dealName = (uploaded) => {
let realName = uploaded.split('/')[2]
// let x1 = realName.split('.')
// let postfix = x1.pop()
// let allName = x1.join('.')
// let x2 = allName.split('_')
// let showName = `${x2[0]}.${postfix}`
return realName
}
// setFileList = (value) => {
// let defaultFileList = [];
// defaultFileList = value.map((u, index) => {
// let fileUrl = `${this.ApiRoot}/${u.url}`;
// return {
// uid: -index - 1,
// name: this.dealName(u.url),
// status: 'done',
// storageUrl: u.url,
// url: fileUrl
// };
// });
// onChange(defaultFileList)
// this.setState({
// fileList: defaultFileList
// });
// };
setFileList = (nextEditData, isQiniu, isAli) => {
let defaultFileList = [];
defaultFileList = nextEditData.map((u, index) => {
let fileUrl =
isQiniu ? `/_file-server/${u.storageUrl}`
: isAli ? `/_file-ali-server/${u.storageUrl}`
: `${this.ApiRoot}/${u.storageUrl}`;
return {
uid: -index - 1,
name: this.dealName(u.storageUrl),
status: 'done',
storageUrl: u.storageUrl,
url: fileUrl,
size: u.size || -1
};
});
this.setState({
fileList: defaultFileList
});
};
componentDidMount() {
const { value, defaultValue, isQiniu, isAli } = this.props;
if (defaultValue) {
this.setFileList(defaultValue, isQiniu, isAli)
}
}
UNSAFE_componentWillReceiveProps(np) {
const { dispatch, value: thisEditData, onChange } = this.props;
const { value: nextEditData, isQiniu, isAli } = np;
// this.setFileList(nextEditData, isQiniu)
// const setFileList = () => {
// let defaultFileList = [];
// defaultFileList = nextEditData.map((u, index) => {
// let fileUrl = isQiniu ? `/_file-server/${u.storageUrl}` : `${this.ApiRoot}/${u.storageUrl}`;
// return {
// uid: -index - 1,
// name: this.dealName(u.storageUrl),
// status: 'done',
// storageUrl: u.storageUrl,
// url: fileUrl,
// size: u.size || -1
// };
// });
// this.setState({
// fileList: defaultFileList
// });
// };
if (nextEditData && nextEditData.length) {
if (!thisEditData || !this.state.fileList.length) {
this.setFileList(nextEditData, isQiniu, isAli);
} else if (nextEditData.length != thisEditData.length) {
this.setFileList(nextEditData, isQiniu, isAli);
} else {
let repeat = true;
for (let i = 0; i < thisEditData.length; i++) {
if (thisEditData[i] != nextEditData[i]) {
repeat = false;
break;
}
}
if (!repeat) {
this.setFileList(nextEditData, isQiniu, isAli);
}
}
}
// else{
// this.setState({
// fileList:[],
// })
// }
}
render() {
const UploadPath = {
project: ['txt', 'dwg', 'doc', 'docx', 'xls', 'xlsx', 'csv', 'pdf', 'pptx', 'png', 'jpg', 'svg', 'jpeg', 'rar', 'zip', 'jpeg', 'mp4'],
report: ['doc', 'docx', 'xls', 'xlsx', 'csv', 'pdf'],
data: ['txt', 'xls', 'xlsx', 'csv'],
image: ['png', 'jpg', 'svg', 'jpeg'],
three: ['js'],
video: ['mp4']
};
/**
* uploadType string 主要区别文件上传路径 以及类型 web/routes/attachment/index.js UploadPath key 值为准默认 project
* disabled boolean 上传是否可用
* maxFilesNum number 最大上传数量
* fileTypes array[string] 可允许上传的文件类型
* maxFileSize number 单个文件最大大小 M
* listType antd upload 组件的属性
* onChange function 文件数量变化时候回调 返回文件
* value array[obj] 编辑数据 [{url:'xxx', [size:999]}]
* onStateChange function 文件状态改变回调函数 上传中 return { uploading:true/false }
*/
const {
uploadType,
disabled,
maxFilesNum,
fileTypes,
maxFileSize,
listType,
onChange = () => { },
value,
showUploadList,
onStateChange,
isQiniu,
isAli,
} = this.props;
const { fileList, curPreviewPic, curPreviewVideo, delPicIng, removeFilesList } = this.state;
const that = this;
let uploadType_ = uploadType || 'project';
let maxFilesNum_ = maxFilesNum || 1;
let defaultFileTypes = fileTypes || UploadPath[uploadType_];
// debugger
const uploadProps = {
name: 'checkFile_',
multiple: false,
showUploadList: showUploadList || true,
action:
isQiniu ? `/_upload/attachments/${uploadType_}`
: isAli ? `/_upload/attachments/ali/${uploadType_}`
: `${this.ApiRoot}/attachments/${uploadType_}`,
listType: listType || 'text',
disabled: disabled,
beforeUpload: (file) => {
if (fileList.length >= maxFilesNum_) {
message.warning(`最多选择${maxFilesNum_}个文件上传`);
return false;
}
if (file.name.length > 60) {
message.warning(`文件名过长(大于60字符),请修改后上传`);
return false;
}
const extNames = file.name.split('.');
// var reg = /^[\.\s\u4e00-\u9fa5a-zA-Z0-9_-]{0,}$/;
// if (!reg.exec(file.name)) {
// message.warning(`文件名包含除字母、汉字、数字、中划线、下划线之外的字符,请修改后上传`);
// return false;
// }
let isDAE = false;
if (extNames.length > 0) {
let fileType = extNames[extNames.length - 1].toLowerCase();
isDAE = defaultFileTypes.some((f) => f == fileType);
}
if (!isDAE) {
message.error(`只能上传 ${defaultFileTypes.join()} 格式的文件!`);
return false;
}
const isLt = file.size / 1024 / 1024 < (maxFileSize || 3);
if (!isLt) {
message.error(`文件必须小于${maxFileSize || 3}MB!`);
return false;
}
this.setState({
fileUploading: true
});
if (onStateChange) {
onStateChange({ uploading: true });
}
},
onChange(info) {
const status = info.file.status;
if (status === 'uploading') {
that.setState({
fileList: info.fileList
});
}
if (status === 'done') {
let { uploaded, url } = info.file.response;
let size = info.file.size;
let nextFileList = fileList;
nextFileList[nextFileList.length - 1] = {
uid: -moment().unix(),
name: that.dealName(uploaded),
status: 'done',
storageUrl: uploaded,
url:
isQiniu ? '/_file-server/' + uploaded :
isAli ? `/_file-ali-server/${uploaded}` :
url,
size: size
};
onChange(nextFileList);
that.setState({
fileUploading: false,
fileList: nextFileList
});
if (onStateChange) {
onStateChange({ uploading: false });
}
} else if (status === 'error') {
that.setState({
fileUploading: false
});
message.error(`${info.file.name} 上传失败,请重试`);
if (onStateChange) {
onStateChange({ uploading: false });
}
}
},
onRemove(file) {
let nextFileList = [];
fileList.map((f, i) => {
if (f.uid != file.uid) {
nextFileList.push(f);
}
});
let nextRemoveFiles = removeFilesList.concat([file.storageUrl]);
if (curPreviewPic == file.url) {
that.setState({
curPreviewPic: ''
});
}
if (curPreviewVideo == file.url) {
that.setState({
curPreviewVideo: ''
});
}
onChange(nextFileList);
that.setState({
fileList: nextFileList,
removeFilesList: nextRemoveFiles
});
},
onPreview(file) {
let filePostfix = file.url.split('.').pop();
filePostfix = filePostfix.toLowerCase();
if (UploadPath.image.some((img) => img == filePostfix)) {
that.setState({
curPreviewPic: file.url
});
} else if (UploadPath.video.some((img) => img == filePostfix)) {
that.setState({
curPreviewVideo: file.url
});
} else {
//message.warn('仅支持图片预览');
preview(file.storageUrl)
}
}
};
const preview = (url) => {
let link = isQiniu ? encodeURI(`${this.qnDomain}/${url}`) :
isAli ? encodeURI(`${this.aliAdmin}/${url}`) : ''
if (link)
if (url.indexOf("pdf") !== -1 || url.indexOf("csv") !== -1) {
window.open(link)
} else {
window.open(`https://view.officeapps.live.com/op/view.aspx?src=${link}`)
}
}
let fileList_ = fileList
// .map(f => {
// if (f.storageUrl) {
// let realName = f.storageUrl.split('/').pop()
// if (f.name != realName) {
// f.name = realName
// }
// }
// return f
// })
//下载文件
const handleDownload = (file) => {
saveAs(file)
};
const saveAs = (file) => {
const link = document.createElement('a');
link.href = file.url;
link.download = file.name;
link.style.display = 'none';
link.click();
}
//自定义下载
return (
<div>
<Spin spinning={delPicIng}>
<Upload {...uploadProps} fileList={fileList_} showUploadList={{ showDownloadIcon: true }} onDownload={handleDownload}>
{
disabled ? (
''
) :
listType == 'picture-card' ?
(
fileList.length >= maxFilesNum_ ? null : (
<div style={{}}>
<PlusOutlined />
<div>添加附件</div>
</div>
)
) : (
<Button disabled={fileList.length >= maxFilesNum_} icon={<UploadOutlined />}> 文件上传 </Button>
)
}
</Upload>
{
curPreviewPic ? (
<Card bodyStyle={{ padding: 8 }}>
<div style={{ marginBottom: 8 }} >
<span>图片预览</span>
<span style={{ float: 'right' }} onClick={() => { this.setState({ curPreviewPic: '' }) }}>
<CloseOutlined style={{ fontSize: 20 }} />
</span>
</div>
<img style={{ width: '100%' }} src={curPreviewPic} />
</Card>
) : ''
}
{
curPreviewVideo ? (<Card bodyStyle={{ padding: 8 }}>
<div style={{ marginBottom: 8 }} >
<span>视频预览</span>
<span style={{ float: 'right' }} onClick={() => { this.setState({ curPreviewVideo: '' }) }}>
<CloseOutlined style={{ fontSize: 20 }} />
</span>
</div>
<video controls style={{ width: '100%' }}>
<source src={curPreviewVideo} type="video/mp4"></source>
</video>
</Card>) : ''
}
</Spin>
</div>
);
}
}
function mapStateToProps(state) {
const { auth } = state
return {
user: auth.user
};
}
export default connect(mapStateToProps)(Uploads);

683
web/client/src/components/export/index.js

@ -1,683 +0,0 @@
/**
* Created by Xumeng 2020/04/22.
*/
import React, { useState, useEffect } from 'react';
import PropTypes from 'prop-types';
import {
Row, Col, Space, Button, message, notification, Form, Input, Tooltip, Menu, Dropdown,
} from 'antd';
import moment from 'moment';
import XLSX from 'xlsx';
import { fromJS } from 'immutable';
import { Request } from '@peace/utils';
import FileSaver from 'file-saver';
import './index.less';
// 通用前端导入导出组件 使用方法查看底部propTypes
function ExportAndImport(props) {
const [form] = Form.useForm();
const [exportLoading, setExportLoading] = useState(false);
const [importLoading, setImportLoading] = useState(false);
const {
importDataCallback, onImportSucess, handelData, importMethod = 'post',
} = props;
useEffect(() => () => {
// 只有unmount 时调用
notification.close('import-notification');
});
const importExcel = (file, type) => {
setImportLoading(true);
// 获取上传的文件对象
const { files } = file.target;
// 判断xls、xlsx格式
if (files[0].type.indexOf('sheet') > -1 || files[0].type.indexOf('ms-excel') > -1) {
// 通过FileReader对象读取文件
const fileReader = new FileReader();
fileReader.onload = (event) => {
try {
const { importRequest = true, importUrl, importQuery } = props;
if (importRequest && !importUrl) {
message.error('获取导入接口失败!');
form.resetFields();
return;
}
const { result } = event.target;
// 以二进制流方式读取得到整份excel表格对象
const workbook = XLSX.read(result, { type: 'binary', cellDates: true });
let data = []; // 存储获取到的数据
// 遍历每张工作表进行读取(这里默认只读取第一张表)
for (const sheet in workbook.Sheets) {
if (workbook.Sheets.hasOwnProperty(sheet)) {
// 利用 sheet_to_json 方法将 excel 转成 json 数据
data = data.concat(XLSX.utils.sheet_to_json(workbook.Sheets[sheet]));
break; // 如果只取第一张表,就取消注释这行
}
}
if (data.length > 10000) {
message.error('一次最多导入10000条数据,请分批导入!');
form.resetFields();
setImportLoading(false);
return;
}
if (importRequest) {
message.success('获取文件数据成功,开始处理导入...');
const importData = handelData ? handelData(data) : data;
Request[importMethod](importUrl, { data: importData }, importQuery || {}).then((res) => {
message.success('导入数据成功');
form.resetFields();
notification.close('import-notification');
setImportLoading(false);
onImportSucess && onImportSucess();
}, (err) => {
if (err.status === 500) {
message.error('数据导入出错,导入失败');
} else if (err.status === 400) {
message.error(err.body.message || '数据验证出错,请检查数据格式是否正确');
} else {
message.error('导入失败');
}
form.resetFields();
setImportLoading(false);
});
} else {
form.resetFields();
setImportLoading(false);
importDataCallback && importDataCallback(data, type);
notification.close('import-notification');
}
} catch (e) {
console.log(e);
// 这里可以抛出文件类型错误不正确的相关提示
message.error('文件格式不正确!');
setImportLoading(false);
form.resetFields();
}
};
// fileReader.onloadend = (event) => {
// console.log(event)
// }
// 以二进制方式打开文件
fileReader.readAsBinaryString(files[0]);
} else {
message.error('文件格式不正确!');
form.resetFields();
setImportLoading(false);
}
};
const loop = (data, keypath, values) => { // deal with array
const dkey = keypath.slice(0, 1)[0];
console.log(dkey);
if (dkey) {
const dvalue = data[dkey];
const otherKeypath = keypath.slice(1);
if (Array.isArray(dvalue)) {
if (otherKeypath.length) {
const immutableData = fromJS(data);
for (let index = 0; index < dvalue.length; index++) {
const tmp = immutableData.getIn([dkey, index]).toJS();
loop(tmp, otherKeypath, values);
}
}
} else {
values.push(dvalue);
}
}
return values;
};
const getColumnData = (opts) => {
const {
data, keypath, render, spliter, rawdata,
} = opts;
let v = null;
const outer = data[keypath[0]];
if (Array.isArray(outer)) {
const values = loop(data, keypath, []);
v = rawdata ? values : values.join(spliter || ',');
} else {
v = fromJS(data).getIn(keypath);
}
// 处理render
if (render && typeof render === 'function') {
v = render(outer, data);
}
return v;
};
const getDataSource = (attrs, filterData) => {
// let token = JSON.parse(sessionStorage.getItem('user')).token;
const dataSource = filterData.map((item) => {
const record = {};
attrs.forEach((attr) => {
const {
key, dataIndex, render, child,
} = attr;
if (child) {
record[key] = getDataSource(child, item[key]);
} else {
const v = getColumnData({
data: item,
keypath: dataIndex || [key],
render: render || null,
});
record[key] = v;
}
});
return record;
});
return dataSource;
};
// 暂时只处理两层
const getFlatData = (attrs, filterData, dataToAoa, deep = 0) => {
filterData.map((item) => {
let cur = dataToAoa[deep];
if (!cur) {
cur = dataToAoa[deep] = [];
}
attrs.map((attr, index) => {
const { key, child } = attr;
if (child) {
if (Array.isArray(item[key])) {
// getFlatData(child,item[key],dataToAoa,deep)
item[key].map((s, i) => {
if (i == 0) {
child.map((c) => {
cur.push(s[c.key]);
});
} else {
deep++;
const childCur = dataToAoa[deep] = [];
pushNull(childCur, index);
child.map((c) => {
childCur.push(s[c.key]);
});
}
});
}
} else {
cur.push(item[key]);
}
});
deep++;
});
};
const getHeader = (headers, excelHeader, deep, perOffset) => {
let offset = 0;
let cur = excelHeader[deep];
if (!cur) {
cur = excelHeader[deep] = [];
}
pushNull(cur, perOffset - cur.length);
for (let i = 0; i < headers.length; i++) {
const head = headers[i];
cur.push(head.name);
if (head.hasOwnProperty('child') && Array.isArray(head.child) && head.child.length > 0) {
const childOffset = getHeader(head.child, excelHeader, deep + 1, cur.length - 1);
pushNull(cur, childOffset - 1);
offset += childOffset;
} else {
offset++;
}
}
return offset;
};
const pushNull = (arr, count) => {
for (let i = 0; i < count; i++) {
arr.push(null);
}
};
const fillNull = (arr) => {
const max = Math.max(...(arr.map((a) => a.length)));
arr.filter((e) => e.length < max).forEach((e) => pushNull(e, max - e.length));
};
const doMerges = (arr) => {
// 要么横向合并 要么纵向合并
const deep = arr.length;
const merges = [];
for (let y = 0; y < deep; y++) {
// 先处理横向合并
const row = arr[y];
let colSpan = 0;
for (let x = 0; x < row.length; x++) {
if (row[x] === null) {
colSpan++;
if (((x + 1) === row.length) && (colSpan > 0 && x > colSpan)) {
merges.push({ s: { r: y, c: x - colSpan }, e: { r: y, c: x } });
}
} else if (colSpan > 0 && x > colSpan) {
merges.push({ s: { r: y, c: x - colSpan - 1 }, e: { r: y, c: x - 1 } });
colSpan = 0;
} else {
colSpan = 0;
}
}
}
// 再处理纵向合并
const colLength = arr[0].length;
for (let x = 0; x < colLength; x++) {
let rowSpan = 0;
for (let y = 0; y < deep; y++) {
if (arr[y][x] != null) {
rowSpan = 0;
} else {
rowSpan++;
}
}
if (rowSpan > 0) {
merges.push({ s: { r: deep - rowSpan - 1, c: x }, e: { r: deep - 1, c: x } });
}
}
return merges;
};
// 内容暂只出了纵向合并
const doContetMerges = (arr, headerLength) => {
const deep = arr.length;
const merges = [];
// 处理纵向合并
const colLength = arr[0].length;
for (let x = 0; x < colLength; x++) {
let rowSpan = 0;
const mergY = 0;
for (let y = 0; y < deep; y++) {
if (rowSpan > 0) {
// 如果还有null 继续加
if (arr[y][x] === null) {
rowSpan += 1;
} else {
// 不为null 增加merge
merges.push({ s: { r: headerLength + (y - rowSpan - 1), c: x }, e: { r: headerLength + y - 1, c: x } });
rowSpan = 0;
}
} else if (arr[y][x] === null) {
rowSpan += 1;
}
}
if (rowSpan > 0) {
merges.push({ s: { r: headerLength + (deep - rowSpan - 1), c: x }, e: { r: headerLength + deep - 1, c: x } });
rowSpan = 0;
}
}
return merges;
};
const exportMergeExcel = async () => {
setExportLoading(true);
const {
column, data, fileName, exportUrl, exportQuery, exportBody, requestType, header, showYearMouth,
} = props || {};
let resultData = [];
if (exportUrl) {
resultData = requestType == 'post' ? await Request.post(exportUrl, exportBody || {}, exportQuery || {}).then((data) => {
// 数据接口返回的结果 如果是对象 必须把返回数组放入rows
if (typeof data === 'object' && data.rows) {
return data.rows;
}
return data;
}, (err) => {
message.error('获取数据失败,导出失败!');
}) : await Request.get(exportUrl, exportQuery || {}).then((data) => {
if (typeof data === 'object' && data.rows) {
return data.rows;
}
return data;
}, (err) => {
message.error('获取数据失败,导出失败!');
});
if (!resultData) {
return;
}
} else {
resultData = data;
}
const excelHeader = [];
getHeader(column, excelHeader, 0, 0);
fillNull(excelHeader);
// console.log(excelHeader);
const loopData = getDataSource(column, resultData);
// console.log(loopData)
const dataToAoa = [];
getFlatData(column, loopData, dataToAoa, 0);
fillNull(dataToAoa);
// console.log(dataToAoa);
const aoa = [].concat(excelHeader, dataToAoa);
// console.log(aoa)
const headerMerges = doMerges(excelHeader);
const contentMerages = doContetMerges(dataToAoa, excelHeader.length);
const merges = [].concat(headerMerges, contentMerages);
// console.log(contentMerages)
// let opts = {
// defaultCellStyle: {
// font: { name: "宋体", sz: 11, color: { auto: 1 } },
// border: {
// color: { auto: 1 }
// },
// alignment: {
// /// 自动换行
// wrapText: 1,
// // 居中
// horizontal: "center",
// vertical: "center",
// indent: 0
// }
// }
// }
const sheet = XLSX.utils.aoa_to_sheet(aoa);
// let newSheet = {};
// for (let [key, value] of Object.entries(sheet)) {
// if(key == '!ref'){
// newSheet[key] = value
// }else if(typeof value === 'object'){
// newSheet[key] = {
// ...value,
// s: opts.defaultCellStyle
// }
// }
// }
const wpx = column.map((c) => ({
wpx: Number.parseInt(c.wpx) ? Number.parseInt(c.wpx) : 100,
}));
sheet['!cols'] = wpx;
sheet['!merges'] = merges;
// 构建 workbook 对象
const workbook = XLSX.utils.book_new();
const time = moment().format('YYYY-MM-DD');
XLSX.utils.book_append_sheet(workbook, sheet, 'mySheet');
// 导出 Excel
XLSX.writeFile(workbook, fileName ? `${fileName}-${time}.xlsx` : '导出数据.xlsx');
setExportLoading(false);
// message.success(`成功导出了 ${loopData.length || 0} 条数据`);
};
const exportProExcel = async () => {
setExportLoading(true);
const {
column, data, fileName, exportUrl, exportQuery, exportBody, requestType, showYearMouth,
} = props || {};
let resultData = [];
if (exportUrl) {
resultData = requestType == 'post' ? await Request.post(exportUrl, exportBody || {}, exportQuery || {}).then((data) => {
// 数据接口返回的结果 如果是对象 必须把返回数组放入rows
if (typeof data === 'object') {
return data.data ? data.data : data.rows;
}
return data;
}, (err) => {
message.error('获取数据失败,导出失败!');
}) : await Request.get(exportUrl, exportQuery || {}).then((data) => {
if (showYearMouth) {
}
if (typeof data === 'object' && data.rows) {
return data.rows;
}
return data;
}, (err) => {
message.error('获取数据失败,导出失败!');
});
if (!resultData) {
return;
}
} else {
resultData = data;
}
const loopData = getDataSource(column, resultData);
let content = '';
let header = '<tr>';
// header += `<th><div>序号</div></th>`;
column.map((colum) => {
header += `<th><div>${colum.name}</div></th>`;
});
header += '</tr>';
loopData.map((data) => {
content += '<tr>';
column.map((c) => {
if (c.style) {
content += `<th style="${c.style}"><div>${data[c.dataIndex || c.key]}</div></th>`;
} else {
content += `<th><div>${data[c.dataIndex || c.key]}</div></th>`;
}
});
content += '</tr>';
});
const exportTable = `\uFEFF
<table style='text-alagin:center' border="1">
${header}
${content}
</table>
`;
const time = moment().format('YYYY-MM-DD');
const tempStrs = new Blob([exportTable], { type: 'text/xls' });
FileSaver.saveAs(tempStrs, fileName ? `${fileName}-${time}.xls` : '导出数据.xls');
setExportLoading(false);
// message.success(`成功导出了 ${loopData.length || 0} 条数据`);
};
const exportExcel = async () => {
setExportLoading(true);
const {
column, data, fileName, exportUrl, exportQuery, exportBody, requestType,
} = props || {};
const _headers = column
.map((item, i) => ({ key: item.key, title: item.name, position: String.fromCharCode(65 + i) + 1 }))
.reduce((prev, next) => ({ ...prev, [next.position]: { key: next.key, v: next.title } }), {});
let resultData = [];
if (exportUrl) {
resultData = requestType == 'post' ? await Request.post(exportUrl, exportBody || {}, exportQuery || {}).then((data) => {
// 数据接口返回的结果 如果是对象 必须把返回数组放入rows
if (typeof data === 'object' && (data.rows || data.data)) {
return data.data ? data.data : data.rows;
}
return data;
}, (err) => {
message.error('获取数据失败,导出失败!');
}) : await Request.get(exportUrl, exportQuery || {}).then((data) => {
if (typeof data === 'object' && data.rows) {
return data.rows;
}
return data;
}, (err) => {
message.error('获取数据失败,导出失败!');
});
if (!resultData) {
return;
}
} else {
resultData = data;
}
const loopDate = getDataSource(column, resultData);
const wpx = column.map((c) => ({
wpx: Number.parseInt(c.wpx) ? Number.parseInt(c.wpx) : 100,
}));
if (!(loopDate.length > 0)) {
setExportLoading(false);
return;
}
const _data = loopDate
.map((item, i) => column.map((key, j) => ({ content: item[key.key], position: String.fromCharCode(65 + j) + (i + 2) })))
// 对刚才的结果进行降维处理(二维数组变成一维数组)
.reduce((prev, next) => prev.concat(next))
// 转换成 worksheet 需要的结构
.reduce((prev, next) => ({ ...prev, [next.position]: { v: next.content } }), {});
// 合并 column 和 data
const output = { ..._headers, ..._data };
// 获取所有单元格的位置
const outputPos = Object.keys(output);
// 计算出范围 ,["A1",..., "H2"]
const ref = `${outputPos[0]}:${outputPos[outputPos.length - 1]}`;
// 构建 workbook 对象
const workbook = {
SheetNames: ['mySheet'],
Sheets: {
mySheet: {
...output,
'!ref': ref,
'!cols': wpx,
},
},
};
const time = moment().format('YYYY-MM-DD');
// 导出 Excel
XLSX.writeFile(workbook, fileName ? `${fileName}-${time}.xlsx` : '导出数据.xlsx');
setExportLoading(false);
// message.success(`成功导出了 ${loopDate.length || 0} 条数据`);
};
const exportTemplete = async () => {
const { importTemColumn, importTemData, fileName } = props || {};
const _headers = importTemColumn
.map((item, i) => {
let group = 0; // 用于处理Z1的时候,重计算AA1
if (parseInt(i / 26) > group) {
group = parseInt(i / 26);
}
if (group > 0) { // AA1 BA1 CA1
const position = String.fromCharCode(65 + (group - 1));
return { key: item.key, title: item.name, position: position + String.fromCharCode(65 + (i % 26)) + 1 };
} return { key: item.key, title: item.name, position: String.fromCharCode(65 + i) + 1 };
})
.reduce((prev, next) => ({ ...prev, [next.position]: { key: next.key, v: next.title } }), {});
const loopDate = getDataSource(importTemColumn, importTemData);
const wpx = importTemColumn.map((c) => ({
wpx: Number.parseInt(c.wpx) ? Number.parseInt(c.wpx) : 100,
}));
const _data = loopDate.length ? loopDate
.map((item, i) => importTemColumn.map((key, j) => ({ content: item[key.key], position: String.fromCharCode(65 + j) + (i + 2) })))
// 对刚才的结果进行降维处理(二维数组变成一维数组)
.reduce((prev, next) => prev.concat(next))
// 转换成 worksheet 需要的结构
.reduce((prev, next) => ({ ...prev, [next.position]: { v: next.content } }), {}) : [];
// 合并 column 和 data
const output = { ..._headers, ..._data };
// 获取所有单元格的位置
const outputPos = Object.keys(output);
// 计算出范围 ,["A1",..., "H2"]
const ref = `${outputPos[0]}:${outputPos[outputPos.length - 1]}`;
// 构建 workbook 对象
const workbook = {
SheetNames: ['mySheet'],
Sheets: {
mySheet: {
...output,
'!ref': ref,
'!cols': wpx,
},
},
};
// 导出 Excel
XLSX.writeFile(workbook, fileName ? `${fileName}-导入模板.xlsx` : '导入模板.xlsx');
};
const tips = (type) => {
const { tips, templeteBth = true } = props;
const description = (
<div className="export-import">
{tips && tips}
<Row gutter={16}>
<Col span={12}>
<Form form={form} initialValues={{}}>
<Form.Item name="import-file">
<Input className="file-uploader" type="file" accept=".xlsx, .xls" onChange={(e) => importExcel(e, type)} />
<Button style={props.btnStyle} className={props.btnClass} loading={importLoading}>
选择文件
</Button>
</Form.Item>
</Form>
</Col>
{templeteBth && (
<Col span={12}>
<Button style={props.btnStyle} className={props.btnClass} onClick={exportTemplete}>
模板下载
</Button>
</Col>
)}
</Row>
</div>
);
notification.info({
message: '支持 .xlsx、.xls 格式的文件',
description,
key: 'import-notification',
duration: null,
});
};
return (
<Space>
{
props.import && (
<Button style={props.btnStyle} className={props.btnClass} loading={importLoading} onClick={tips}>
{props.importBtnName || '导入'}
</Button>
)
}
{
props.export && (
<Tooltip placement="top" title={props.exportBtnTips || '默认导出所有数据'}>
<Button style={props.btnStyle} className={props.btnClass} loading={exportLoading} onClick={props.exportType === 'pro' ? exportProExcel : exportExcel}>
{props.exportBtnName || '导出'}
</Button>
</Tooltip>
)
}
</Space>
);
}
ExportAndImport.propTypes = {
export: PropTypes.bool, // 是否显示导出按钮
exportBtnName: PropTypes.string, // 导出按钮文字
importBtnName: PropTypes.string, // 导入按钮文字
import: PropTypes.bool, // 是否显示导入按钮
variedImport: PropTypes.bool, // 是否显示多样导入
variedImportDisable: PropTypes.bool, // 多样导入禁用
variedImportBtnName: PropTypes.string, // 多样导入文字
column: PropTypes.array, // 导出显示的header数组 兼容antd column 可直接拿table的column使用 注:column每列的属性wpx设置导出的execl每列的宽度值 默认 100
data: PropTypes.array, // 导出的数据 兼容antd table 数组嵌套处理
exportUrl: PropTypes.string, // 导出数据从接口获取的url地址 返回的数据1、数组必须支持column的设置 ,2、如果是对象,数组需放在rows属性上
exportBody: PropTypes.object, // 导出数据接口body参数
exportQuery: PropTypes.object, // 导出数据从接口获取的url地址上的参数
exportBtnTips: PropTypes.string, // 导出按钮tips文字提示
importUrl: PropTypes.string, // 导入接口url
importQuery: PropTypes.object, // 导入接口url地址上的参数
btnClass: PropTypes.string, // 按钮className
btnStyle: PropTypes.object, // 按钮style
tips: PropTypes.oneOfType([PropTypes.string, PropTypes.element]), // 上传文件提示的信息
onImportSucess: PropTypes.func, // 上传成功后 返回处理函数
importTemColumn: PropTypes.array, // 导入模板设置 头部字段数组
importTemData: PropTypes.array, // 导入模板默认数据
requestType: PropTypes.string, // 请求类型
importDataCallback: PropTypes.func, // 上传后数据返回
templeteBth: PropTypes.bool, // 模板按钮
importRequest: PropTypes.bool, // 请求导入接口,false时搭配importDataCallback,
exportType: PropTypes.string, // 导出执行的函数名
};
export default ExportAndImport;

13
web/client/src/components/export/index.less

@ -1,13 +0,0 @@
.export-import {
.file-uploader {
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
outline: none;
opacity: 0;
background-color: transparent;
z-index: 10;
}
}

74
web/client/src/components/flowRecordTable/index.js

@ -1,74 +0,0 @@
'use strict';
import React, { Component } from 'react';
import { Table } from 'antd';
import './index.less';
import moment from 'moment';
class FlowRecordTable extends Component {
constructor(props) {
super(props);
this.state = {
isRequesting: false,
pagination: {
showTotal: total => `${total}`,
responsive: true,
defaultPageSize: 10,
},
data: []
}
this.token = JSON.parse(sessionStorage.getItem('user')).token;
}
getPagination = () => {
const { pagination, data } = this.state;
const { actiPage } = this.props;
pagination.total = data.length > 0 ? data.length : 0;
pagination.current = actiPage;
return pagination;
};
onTableChange = (pagination, filters, sorter) => {
this.props.onPageChange(pagination.current)
}
componentDidMount() {
}
render() {
const { flowRecord } = this.props;
const tableColumnAttrs = [
{
title: '序号',
dataIndex: 'id',
width: '12%',
render: (text, record, index) => { return index + 1 }
},
{
title: '操作人',
dataIndex: 'processBy',
width: '25%',
},
{
title: '操作时间',
dataIndex: 'processAt',
width: '25%',
render: (text, record, index) => { return moment(text).format('YYYY-MM-DD HH:mm') }
}, {
title: '操作内容',
dataIndex: 'processContent',
width: '38%',
},
];
return (
<Table
rowKey="id"
className={'fs-table'}
dataSource={flowRecord}
columns={tableColumnAttrs}
onChange={this.onTableChange}
pagination={this.getPagination()}
/>
);
}
}
export default FlowRecordTable;

0
web/client/src/components/flowRecordTable/index.less

23
web/client/src/components/index.js

@ -1,27 +1,10 @@
'use strict';
import Upload from './Upload';
import Uploads from './Uploads';
import NoResource from './no-resource';
import LimitTextArea from './limit-textarea';
// import ProcessForm from './process_form'
import FlowRecordTable from './flowRecordTable'
import Table from './table'
import Search from './search'
import SketchColor from './sketchColor'
import ExportAndImport from './export'
// import Upload from './Upload';
export {
Upload,
Uploads,
NoResource,
LimitTextArea,
// ProcessForm,
FlowRecordTable,
Table,
Search,
SketchColor,
ExportAndImport,
};

70
web/client/src/components/limit-textarea/index.js

@ -1,70 +0,0 @@
import React, { PureComponent } from 'react';
import { Input } from 'antd';
const { TextArea } = Input;
import './index.less';
/***
* 显示最大输入字符数
* maxLength300默认
*/
class LimitTextArea extends PureComponent {
constructor(props) {
super(props)
this.state = {
len: 0,
maxLength: 300,
isvalid: true, // 是否显示最大字符数
}
// 若需要覆盖onChange时,value必填
if (props.onChange && !props.hasOwnProperty('value')) {
this.state.isvalid = false
console.warn('LimitTextArea:绑定onChange时,value属性必填,否则显示最大输入字符数将失效!')
}
}
// 若外部定义了onChange事件,handleChange将会被覆盖
handleChange = (e) => {
const { sep } = this.props
const val = e.target.value
const arr = (val || '').split(sep)
this.setState({
len: arr.length
})
}
render () {
const { maxLength: defaultMax, isvalid } = this.state
const { sep, maxLength, value, ...restProps } = this.props
const max = maxLength > 0 ? maxLength : defaultMax
/** form组件中,value有值 */
const arr = (value || '').split(sep)
let len = value ? arr.length : this.state.len
len = len > max ? max : len
/**截取最大字符串 */
const val = arr.slice(0, len).join(sep)
const n = val ? len : 0
const suffix = `${n}/${max}`
return isvalid ? (
<div className={'block'}>
<TextArea
onChange={ e => this.handleChange(e) }
value={val}
{ ...restProps }
/>
<span className={'counter'}>{suffix}</span>
</div>
) : <TextArea { ...this.props } />
}
}
LimitTextArea.defaultProps = {
/**
* 可以是个字符串'\n'
* 也可以是个正则表达式/\n\r/
*/
sep: ''
}
export default LimitTextArea;

10
web/client/src/components/limit-textarea/index.less

@ -1,10 +0,0 @@
.block {
position: relative;
.counter {
position: absolute;
bottom: 5px;
right: 15px;
color: #adadad;
z-index: 2;
}
}

21
web/client/src/components/no-resource/index.js

@ -1,21 +0,0 @@
'use strict';
import React from 'react';
import { Result} from 'antd';
import { MehOutlined } from '@ant-design/icons';
class NoResource extends React.Component {
constructor(props) {
super(props);
}
render() {
const title = this.props.title ? this.props.title : "抱歉,没有可访问的资源!"
return (
<Result
icon={<MehOutlined />}
title={title}
/>
);
}
}
export default NoResource;

217
web/client/src/components/search/index.js

@ -1,217 +0,0 @@
/**
* Created by Xumeng 2020/04/01.
*/
'use strict';
import React, { useState } from 'react';
import PropTypes from 'prop-types';
import { Space, Row, Col, Form, DatePicker, Input, Select, Button } from 'antd';
import { DownOutlined, UpOutlined } from '@ant-design/icons';
//import { fromJS } from 'immutable';
import { Constans } from '$utils';
import moment from 'moment';
import './index.less';
const FormItem = Form.Item;
const { RangePicker } = DatePicker;
//通用搜索栏组件 使用方法查看底部propTypes
const Search = (props) => {
const [expand, setExpand] = useState(false);
const [form] = Form.useForm();
const { colSpan } = props;
//初始化表单数据
const getinitialValues = () => {
const { formList } = props;
let obj = {};
formList.forEach((v) => {
const { field, initialValue } = v;
if (initialValue) {
obj[field] = initialValue;
}
});
return obj;
};
//获取表单项
const getFields = () => {
const formItemList = [];
const { formList, showNumber, offset } = props;
let showNum = showNumber ? showNumber : 3;
let offsetNum = offset ? offset : 0;
const span = Number.parseInt((24 - offsetNum) / showNum);
if (formList && formList.length > 0) {
formList.forEach((item, index) => {
const { label, field, type, placeholder, style, labelSpan, showTime, optionName, list, rules, itemProps } = item;
let num = 0;
if (index === 0 && offsetNum) {
num = offsetNum;
}
switch (type) {
case 'TIME':
const TIMES = (
<Col offset={num} span={labelSpan || span} key={`${field}-${index}`}>
<FormItem label={label || '选择日期'} name={field} rules={rules}>
<DatePicker
getPopupContainer={(triggerNode) => triggerNode.parentNode}
//showTime={showTime ? showTime : false}
style={style ? style : {}}
placeholder={placeholder || '选择日期'}
format='YYYY-MM-DD'
{...itemProps}
/>
</FormItem>
</Col>
);
formItemList.push(TIMES);
break;
case 'RANGETIME':
const RANGETIMES = (
<Col offset={num} span={labelSpan || span} key={`${field}-${index}`}>
<FormItem label={label || '选择日期时间段'} name={field} rules={rules}>
<RangePicker
getPopupContainer={(triggerNode) => triggerNode.parentNode}
showTime={showTime ? showTime : false}
style={style ? style : {}}
//placeholder={placeholder || '选择日期时间段'}
format={showTime ? 'YYYY-MM-DD HH:mm:ss' : 'YYYY-MM-DD'}
{...itemProps}
/>
</FormItem>
</Col>
);
formItemList.push(RANGETIMES);
break;
case 'SELECT':
const SELECT = (
<Col offset={num} span={labelSpan || span} key={`${field}-${index}`}>
<FormItem label={label || '请选择'} name={field} rules={rules}>
<Select
style={style ? style : {}}
getPopupContainer={(triggerNode) => triggerNode.parentNode}
placeholder={placeholder || '请选择'}
{...itemProps}
>
{getOptionList(list, optionName)}
</Select>
</FormItem>
</Col>
);
formItemList.push(SELECT);
break;
default:
const INPUT = (
<Col offset={num} span={labelSpan || span} key={`${field}-${index}`}>
<FormItem label={label || '关键字'} name={field} rules={rules}>
<Input style={style ? style : {}} placeholder={placeholder || '请输入关键字'} {...itemProps} />
</FormItem>
</Col>
);
formItemList.push(INPUT);
break;
}
});
}
//默认显示个数处理
return expand ? formItemList : formItemList.slice(0, showNum);
};
const onFinish = (values) => {
const { formList } = props;
let obj = Object.assign({}, values);
//处理时间
formList.forEach((v) => {
if (v.type == 'TIME' && obj.hasOwnProperty(v.field)) {
if (obj[v.field]) {
obj[v.field] = [
moment(obj[v.field]).startOf('day').format('YYYY-MM-DD HH:mm:ss'),
moment(obj[v.field]).endOf('day').format('YYYY-MM-DD HH:mm:ss')
];
}
}
if (v.type == 'RANGETIME' && obj.hasOwnProperty(v.field)) {
if (Array.isArray(obj[v.field])) {
obj[v.field] = v.showTime
? [moment(obj[v.field][0]).format('YYYY-MM-DD HH:mm:ss'), moment(obj[v.field][1]).format('YYYY-MM-DD HH:mm:ss')]
: [
moment(obj[v.field][0]).startOf('day').format('YYYY-MM-DD HH:mm:ss'),
moment(obj[v.field][1]).endOf('day').format('YYYY-MM-DD HH:mm:ss')
];
}
}
//处理undefind
if (obj[v.field] === undefined) {
obj[v.field] = null;
}
});
props.onSearch(obj);
};
const getOptionList = (data, name) => {
if (!data) {
return [];
}
return data.map((item) => (
<Select.Option disabled={item.disabled || false} value={item.value} key={item.key || item.value}>
{item[`${name}`]}
</Select.Option>
));
};
return (
<Form
form={form}
name='smart_seal_search'
className='smart-seal-search-form'
onFinish={onFinish}
initialValues={getinitialValues()}
validateMessages={Constans.defaultValidateMessages}
>
<Row gutter={16}>
<Col span={colSpan ? colSpan.label : 18}>
<Row gutter={16}>{getFields()}</Row>
</Col>
<Col span={colSpan ? colSpan.button : 6}>
<Space>
<Button htmlType='submit' className='smart-seal-default-btn'>
查询
</Button>
{props.showRest && (
<Button
onClick={() => {
form.resetFields();
}}
className='smart-seal-default-btn'
>
重置
</Button>
)}
{props.formList.length > props.showNumber && (
<a
style={{ marginLeft: 8, fontSize: 12 }}
onClick={() => {
setExpand(!expand);
}}
>
{expand ? <UpOutlined /> : <DownOutlined />} {expand ? '收起' : '展开'}
</a>
)}
</Space>
</Col>
</Row>
</Form>
);
};
Search.propTypes = {
//查询配置数组 [{label, field, type[TIME,RANGETIME,SELECT,INPUT], initialValue, placeholder, width, list(select使用) optionName(select使用) , showTime(是否显示时间) }]
formList: PropTypes.array.isRequired,
showNumber: PropTypes.number, //默认展示几个item ,其余展开按钮控制
offset: PropTypes.number, //设置第一个item的偏移值
onSearch: PropTypes.func.isRequired, //查询提交函数
showRest: PropTypes.bool, //是否显示重置按钮
colSpan: PropTypes.object // 搜索栏整体布局 默认 {label: 18 : button: 6}
};
export default Search;

3
web/client/src/components/search/index.less

@ -1,3 +0,0 @@
.ant-form-horizontal .ant-form-item-control {
flex: 1 1 0%;
}

82
web/client/src/components/sketchColor/index.js

@ -1,82 +0,0 @@
'use strict'
import React from 'react';
import { SketchPicker } from 'react-color';
class SketchColor extends React.Component {
constructor(props) {
super(props);
this.state = {
displayColorPicker: false,
color: "#fff",
};
}
componentDidMount() {
const { color } = this.props;
color && this.setState({
color: color
})
}
handleClick = () => {
this.setState({ displayColorPicker: !this.state.displayColorPicker })
};
handleClose = () => {
this.setState({ displayColorPicker: false })
};
handleChange = (color) => {
const {onChangeComplete} = this.props;
this.setState({ color: color.hex });
onChangeComplete && onChangeComplete(color)
};
render() {
const { color } = this.state;
const styles = {
color: {
width: '36px',
height: '14px',
borderRadius: '2px',
background: color,
},
swatch: {
padding: '5px',
background: '#fff',
borderRadius: '1px',
boxShadow: '0 0 0 1px rgba(0,0,0,.1)',
display: 'inline-block',
cursor: 'pointer',
},
popover: {
position: 'absolute',
zIndex: '2',
},
cover: {
position: 'fixed',
top: '0px',
right: '0px',
bottom: '0px',
left: '0px',
},
};
return (
<div>
<div style={ styles.swatch } onClick={ this.handleClick }>
<div style={ styles.color } />
</div>
{ this.state.displayColorPicker ? <div style={ styles.popover }>
<div style={ styles.cover } onClick={ this.handleClose }/>
<SketchPicker width={300} color={ color } onChange={ this.handleChange } />
</div> : null }
</div>
)
}
}
export default SketchColor

259
web/client/src/components/table/index.js

@ -1,259 +0,0 @@
'use strict';
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { Table as AntdTable, Divider, Dropdown, Menu, Spin, Popconfirm } from 'antd';
import { DownOutlined } from '@ant-design/icons';
import { fromJS } from 'immutable';
import './index.less';
const loop = (data, keypath, values) => {
// deal with array
let dkey = keypath.slice(0, 1)[0];
if (dkey) {
let dvalue = data[dkey];
let otherKeypath = keypath.slice(1);
if (Array.isArray(dvalue)) {
if (otherKeypath.length) {
let immutableData = fromJS(data);
for (let index = 0; index < dvalue.length; index++) {
let tmp = immutableData.getIn([dkey, index]).toJS();
loop(tmp, otherKeypath, values);
}
}
} else {
values.push(dvalue);
}
}
return values;
};
//通用table组件 使用方法查看底部propTypes
class Table extends Component {
constructor(props) {
super(props);
this.state = {
selectedRowKeys: [],
pagination: {
showTotal: (total) => `${total}`,
showSizeChanger: true,
showQuickJumper: true,
responsive: true,
pageSizeOptions: ['10', '20', '30', '40'],
defaultPageSize: 10
},
visible: {},
current: 1
};
this.token = JSON.parse(sessionStorage.getItem('user')).token;
}
handleMenuClick = (e) => {
//TODO 点击删除
};
UNSAFE_componentWillReceiveProps() {
this.setState({
visible: {}
});
}
handleVisibleChange = (flag, key) => {
let visible = this.state.visible;
visible[key] = flag;
this.setState({ visible });
};
getColumns = () => {
let columns = this.props.attrs.map((attr) => {
const { dataIndex, key, name, render, width, ellipsis, fixed } = attr;
let obj = {
title: name,
dataIndex: dataIndex || key,
key: key,
ellipsis: ellipsis == undefined ? true : ellipsis,
width: width
};
if (render) {
obj.render = render;
}
if (fixed) {
obj.fixed = fixed;
}
return obj;
});
if (!this.props.actions) {
return columns;
}
columns.push({
title: '操作',
key: 'action',
className: 'fs-table-column-action',
render: (text, record) => {
let actions = this.props.actions;
if (record && record.actions) actions = actions.filter((act) => record.actions.includes(act.key));
return (
<span style={{ whiteSpace: 'normal' }}>
{actions.reduce((p, c, i) => {
const { key, name, handler, dom, style, dropdown, hidden, popconfirm, getPopTitleFun, definedTitle } = c;
if (typeof hidden === 'function') {
let unVisiable = hidden(record);
if (unVisiable) return p;
}
if (dropdown && Array.isArray(dropdown)) {
// 操作按钮 下拉菜单处理
const menus = (
<Menu onClick={this.handleMenuClick}>
{dropdown.map((m) => {
if (m.popconfirm) {
return (
<Menu.Item key={m.key}>
<Popconfirm
placement='left'
title={m.title ? m.title : `确认${m.name}`}
onConfirm={() => {
m.handler && m.handler(record);
}}
>
<a onClick={(e) => e.preventDefault()}>{m.name}</a>
</Popconfirm>
</Menu.Item>
);
} else {
return (
<Menu.Item key={m.key}>
<a onClick={(e) => m.handler && m.handler(record)}>{m.name}</a>
</Menu.Item>
);
}
})}
</Menu>
);
p.push(
<Dropdown
overlay={menus}
onVisibleChange={(e) => this.handleVisibleChange(e, text.id)}
visible={this.state.visible ? this.state.visible[text.id] : false}
key={key}
>
<a style={style ? style : { display: 'inline-block' }} onClick={(e) => e.preventDefault()}>
{name} <DownOutlined />
</a>
</Dropdown>
);
} else {
if (dom) {
popconfirm
? p.push(
<Popconfirm placement='left' title={`确认${name}吗?`} onConfirm={() => handler(record)} key={key}>
<span
style={style ? style : { display: 'inline-block' }}
key={key}
onClick={(e) => e.preventDefault()}
>
{dom}
</span>
</Popconfirm>
)
: p.push(
<span style={style ? style : { display: 'inline-block' }} key={key} onClick={(e) => handler(record)}>
{dom}
</span>
);
} else {
popconfirm
? p.push(
<Popconfirm placement='left' title={definedTitle && getPopTitleFun ? getPopTitleFun(record) : `确认${name}吗?`} onConfirm={() => handler(record)} key={key}>
<a
style={style ? style : { display: 'inline-block' }}
key={key}
onClick={(e) => e.preventDefault()}
>
{name}
</a>
</Popconfirm>
)
: p.push(
<a style={style ? style : { display: 'inline-block' }} key={key} onClick={(e) => handler(record)}>
{name}
</a>
);
}
}
if (i < actions.length - 1) p.push(<Divider key={`divider${i}`} type='vertical' />);
return p;
}, [])}
</span>
);
}
});
return columns;
};
getColumnData = (opts) => {
const { data, keypath, spliter, rawdata } = opts;
let v = null;
let outer = data[keypath[0]];
if (Array.isArray(outer)) {
let values = loop(data, keypath, []);
v = rawdata ? values : values.join(spliter || ',');
} else {
v = fromJS(data).getIn(keypath);
}
return v;
};
getPagination = () => {
const { total, curpage } = this.props;
const { pagination, current } = this.state;
pagination.total = total ? total : 0;
pagination.current = curpage ? curpage : current;
return pagination;
};
//暂时只支持分页远程处理
handleTableChange = (pagination, filters, sorter) => {
this.setState({
current: pagination.current
});
if (this.props.onTableChange) {
let limit = Number.parseInt(pagination.pageSize);
let offset = limit * (Number.parseInt(pagination.current) - 1);
this.props.onTableChange(limit, offset, pagination.current);
}
};
render() {
const { scroll, rowSelection, data, isRequesting, showHeader, noShowPagination, rowKey } = this.props;
return (
<AntdTable
className={'fs-table'}
id='fs-smart-seal-table'
dataSource={data}
loading={isRequesting}
showHeader={showHeader}
scroll={scroll ? scroll : {}}
rowKey={rowKey ? rowKey : 'id'}
rowSelection={rowSelection ? rowSelection : null}
columns={this.getColumns()}
onChange={this.handleTableChange}
pagination={noShowPagination ? false : this.getPagination()}
/>
);
}
}
Table.propTypes = {
data: PropTypes.array.isRequired, //数据资源
attrs: PropTypes.array.isRequired, //属性数组用于colums {key,name,|render|isImg|nullable}
actions: PropTypes.array, //操作栏数组 { key,name,style,handler,dropdown,dom}
scroll: PropTypes.object, //同antd 用法
rowSelection: PropTypes.object, //表格行是否可选择 配置项同antd
onTableChange: PropTypes.func, //onChange触发函数
showHeader: PropTypes.bool, //是否显示表头
noShowPagination: PropTypes.bool, //是否显示分页器,
showLessItems: PropTypes.bool, //是否显示较少页面内容,
rowKey: PropTypes.string//表格记录的key
};
export default Table;

34
web/client/src/components/table/index.less

@ -1,34 +0,0 @@
//宽度小于1920px
@media screen and (max-width:1920px) {
.fs-table :global(.ant-table tbody > tr > td) {
border: none;
font-size: 14px;
font-weight: 400;
color: #666;
}
.fs-table :global(.ant-table thead > tr > th) {
border: none;
font-size: 14px;
font-weight: 600;
}
}
//宽度大于等于1920px
@media screen and (min-width:1920px) {
.fs-table :global(.ant-table tbody > tr > td) {
border: none;
font-size: 14px;
font-weight: 400;
color: #666;
}
.fs-table :global(.ant-table thead > tr > th) {
border: none;
font-size: 16px;
font-weight: 600;
}
}
// .fs-table :global(.fs-table-column-action) {
// font-size: 16px;
// }

2
web/client/src/index.js

@ -4,4 +4,4 @@ import React from 'react';
import { render } from 'react-dom';
import App from './app';
render((<App projectName="运维巡检平台" />), document.getElementById('App'));
render((<App projectName="泵站系统" />), document.getElementById('App'));

2
web/client/src/layout/components/header/index.js

@ -38,7 +38,7 @@ const Header = props => {
</span>
<div className={styles['header-title']} style={{}}>
{/* <img src='/assets/images/logo.png' style={{ margin: '0 12px 4px 12px', height: 42, borderRadius: 4 }} /> */}
运维巡检平台
泵站系统
</div>
</div>
<div id="nav" className={styles['header-nav']}>

14
web/client/src/layout/components/sider/index.js

@ -37,15 +37,15 @@ const Sider = (props) => {
let firstItem = items[0] || null
if (firstItem) {
let children = firstItem.props.children
let children = firstItem?.props?.children
if (Array.isArray(children)) {
selectedKeys = [children[0].key]
openKeys = [firstItem.key]
} else if (children.key) {
selectedKeys = [children.key]
openKeys = [firstItem.key]
selectedKeys = [children[0]?.key]
openKeys = [firstItem?.key]
} else if (children?.key) {
selectedKeys = [children?.key]
openKeys = [firstItem?.key]
} else {
selectedKeys = [firstItem.key]
selectedKeys = [firstItem?.key]
}
}
}

3
web/client/src/sections/auth/containers/login.js

@ -35,7 +35,7 @@ const Login = props => {
useEffect(() => {
if (user && user.authorized) {
dispatch(push('/projectRegime/information'));
dispatch(push('/systemManagement'));
}
}, [user])
@ -138,7 +138,6 @@ const Login = props => {
function mapStateToProps (state) {
const { auth } = state;
console.log(auth.error);
return {
user: auth.user,
error: auth.error,

0
web/client/src/sections/organization/actions/authority.js → web/client/src/sections/bigScreen/actions/authority.js

0
web/client/src/sections/organization/actions/index.js → web/client/src/sections/bigScreen/actions/index.js

0
web/client/src/sections/organization/actions/user.js → web/client/src/sections/bigScreen/actions/user.js

26
web/client/src/sections/bigScreen/components/basis.js

@ -0,0 +1,26 @@
import React from 'react';
import { connect } from 'react-redux';
import { Spin, Card, Modal, TreeSelect, message } from 'antd';
import ProForm, { ProFormText, ModalForm, ProFormSwitch, ProFormTreeSelect } from '@ant-design/pro-form';
const Basis = ({ user, module, setModule }) => {
return <div style={{ width: '100%', heigh: '100%' }}>
基础信息
</div >
}
function mapStateToProps (state) {
const { auth, global } = state;
return {
user: auth.user,
clientHeight: global.clientHeight,
};
}
export default connect(mapStateToProps)(Basis);

26
web/client/src/sections/bigScreen/components/capacity.js

@ -0,0 +1,26 @@
import React from 'react';
import { connect } from 'react-redux';
import { Spin, Card, Modal, TreeSelect, message } from 'antd';
import ProForm, { ProFormText, ModalForm, ProFormSwitch, ProFormTreeSelect } from '@ant-design/pro-form';
import Title from 'antd/lib/skeleton/Title';
const Capacity = ({ user, }) => {
return <div style={{ width: '100%', heigh: '100%' }}>
能耗监测
</div >
}
function mapStateToProps (state) {
const { auth, global } = state;
return {
user: auth.user,
clientHeight: global.clientHeight,
};
}
export default connect(mapStateToProps)(Capacity);

27
web/client/src/sections/bigScreen/components/electrical.js

@ -0,0 +1,27 @@
import React from 'react';
import { connect } from 'react-redux';
import { Spin, Card, Modal, TreeSelect, message } from 'antd';
import ProForm, { ProFormText, ModalForm, ProFormSwitch, ProFormTreeSelect } from '@ant-design/pro-form';
import Title from 'antd/lib/skeleton/Title';
const Electrical = ({ user, module, setModule }) => {
return <div style={{ width: '100%', heigh: '100%' }}>
电排远控
</div >
}
function mapStateToProps (state) {
const { auth, global } = state;
return {
user: auth.user,
clientHeight: global.clientHeight,
};
}
export default connect(mapStateToProps)(Electrical);

75
web/client/src/sections/bigScreen/components/header.js

@ -0,0 +1,75 @@
import React from 'react';
import { connect } from 'react-redux';
import { Spin, Card, Modal, TreeSelect, message } from 'antd';
import ProForm, { ProFormText, ModalForm, ProFormSwitch, ProFormTreeSelect } from '@ant-design/pro-form';
import Title from 'antd/lib/skeleton/Title';
const Header = ({ user, module, setModule }) => {
return <div style={{ width: '100%', heigh: '100%' }}>
<div style={{
width: '100%', height: 130,
backgroundImage: 'url(/assets/images/monitor/header-bg.png)',
backgroundSize: 'cover',
backgroundPosition: 'center',
display: 'flex',
justifyContent: 'space-between',
}}>
<div style={{ width: 140 }}>天气</div>
<div style={{
width: 424, lineHeight: '136px',
fontFamily: 'YouSheBiaoTiHei', fontSize: 43, color: '#E2F8FF', letterSpacing: 4, fontWeight: 600
}}>泵站自动化控制系统</div>
<div>
<div style={{
width: 140, height: 52,
backgroundImage: 'url(/assets/images/monitor/user.png)',
backgroundSize: 'cover',
backgroundPosition: 'center'
}}>{user?.name}
</div>
</div>
</div>
<div style={{ display: 'flex', justifyContent: 'center' }}>
<div style={{
width: '45%', height: 44, textAlign: 'center',
backgroundImage: 'url(/assets/images/monitor/strip.png)',
backgroundSize: '100% 25%',
backgroundPosition: 'center',
backgroundRepeat: 'no-repeat',
display: 'flex',
alignItems: 'flex-end', justifyContent: 'space-around',
}}>
{[{ title: '基础信息', key: 'basis' },
{ title: '能耗监测', key: 'capacity' },
{ title: '电排远控', key: 'electrical ' },
{ title: '实时监测', key: 'realTime' },].map(v => {
return <div key={v.key} style={{
width: 80, height: 30, lineHeight: '30px',
backgroundImage: `url(/assets/images/monitor/${module == v.key ? 'pitch-on' : 'chose-none'}.png)`,
backgroundSize: 'center',
backgroundPosition: 'center',
color: 'white',
}} onClick={() => setModule(v.key)}>{v.title}</div>
})}
</div>
</div>
</div >
}
function mapStateToProps (state) {
const { auth, global } = state;
return {
user: auth.user,
clientHeight: global.clientHeight,
};
}
export default connect(mapStateToProps)(Header);

26
web/client/src/sections/bigScreen/components/realTime.js

@ -0,0 +1,26 @@
import React from 'react';
import { connect } from 'react-redux';
import { Spin, Card, Modal, TreeSelect, message } from 'antd';
import ProForm, { ProFormText, ModalForm, ProFormSwitch, ProFormTreeSelect } from '@ant-design/pro-form';
import Title from 'antd/lib/skeleton/Title';
const RealTime = ({ user, module, setModule }) => {
return <div style={{ width: '100%', heigh: '100%' }}>
实时监测
</div >
}
function mapStateToProps (state) {
const { auth, global } = state;
return {
user: auth.user,
clientHeight: global.clientHeight,
};
}
export default connect(mapStateToProps)(RealTime);

5
web/client/src/sections/bigScreen/containers/index.js

@ -0,0 +1,5 @@
'use strict';
import SystemManagement from './systemManagement';
export { SystemManagement };

50
web/client/src/sections/bigScreen/containers/systemManagement.js

@ -0,0 +1,50 @@
import React, { useEffect, useState } from 'react';
import { connect } from 'react-redux';
import { FormOutlined, DeleteOutlined } from '@ant-design/icons';
import { Spin, Tooltip, Button, Popconfirm, Row, Col, Tree, Card, Switch } from 'antd';
import ProTable from '@ant-design/pro-table';
import { getDepMessage, getDepUser, createUser, updateUser, delUser, resetPwd, createDept, updateDept, delDept } from '../actions/user';
import Header from '../components/header';
import Basis from '../components/basis';
import Capacity from '../components/capacity';
import Electrical from '../components/electrical';
import RealTime from '../components/realTime';
const TreeNode = Tree.TreeNode;
const SystemManagement = ({ clientHeight, user }) => {
const [module, setModule] = useState('basis')
useEffect(() => {
}, [])
return (
<div style={{
width: '100vw', height: '100vh',
backgroundImage: 'url(/assets/images/monitor/screen-bg.png)',
backgroundSize: 'cover',
backgroundPosition: 'center'
}}>
<Header module={module} setModule={setModule} />
{module == 'basis' ? <Basis /> : ""}
{module == 'capacity' ? <Capacity /> : ""}
{module == 'electrical' ? <Electrical /> : ""}
{module == 'realTime' ? <RealTime /> : ""}
</div>
)
}
function mapStateToProps (state) {
const { auth, global } = state;
return {
user: auth.user,
clientHeight: global.clientHeight,
};
}
export default connect(mapStateToProps)(SystemManagement);

0
web/client/src/sections/organization/index.js → web/client/src/sections/bigScreen/index.js

22
web/client/src/sections/bigScreen/nav-item.js

@ -0,0 +1,22 @@
import React from 'react';
import { Link } from 'react-router-dom';
import { Menu } from 'antd';
import { SettingOutlined } from '@ant-design/icons';
const SubMenu = Menu.SubMenu;
export function getNavItem(user, dispatch) {
// if (!Func.isAuthorized("ORG_MANAGE")) {
// return null
// }
return (<></>
// <SubMenu key="organization" icon={<SettingOutlined />} title={'组织管理'}>
// <Menu.Item key="userManage">
// <Link to="/organization/user">部门成员</Link>
// </Menu.Item>
// <Menu.Item key="authority">
// <Link to="/organization/authority">权限配置</Link>
// </Menu.Item>
// </SubMenu>
);
}

0
web/client/src/sections/organization/reducers/index.js → web/client/src/sections/bigScreen/reducers/index.js

27
web/client/src/sections/bigScreen/routes.js

@ -0,0 +1,27 @@
'use strict';
import { SystemManagement } from './containers';
export default [{
type: 'outer',
route: {
path: '/systemManagement',
key: 'systemManagement',
breadcrumb: '泵站大屏',
component: SystemManagement,
// menuSelectKeys: ['userManage'],
// menuOpenKeys: ['organization'],
// childRoutes: [{
// path: '/user',
// key: 'userManage',
// menuSelectKeys: ['userManage'],
// component: UserManage,
// breadcrumb: '部门成员',
// }, {
// path: '/authority',
// key: 'authority',
// component: Authority,
// menuSelectKeys: ['authority'],
// breadcrumb: '权限配置',
// }]
}
}];

88
web/client/src/sections/organization/components/deptModal.js

@ -1,88 +0,0 @@
import React from 'react';
import { connect } from 'react-redux';
import { ProFormText, ModalForm, ProFormSelect } from '@ant-design/pro-form';
const DeptModal = (props) => {
const { visible, modalType, onVisibleChange, onConfirm, editData, depts } = props
let deptOptions = [], sonArr = [];
depts.map(d => {
deptOptions.push({
value: d.id,
label: d.name
});
d.subordinate.map(ds => {
sonArr.push({
value: ds.id,
label: ds.name
})
})
})
const onFinish = (values) => {
if (onConfirm) {
if (modalType === 'edit') {
values.contract.parentDeptId = values.contract.parentDeptId || null
onConfirm(values)
} else {
onConfirm(values);
}
}
}
const checkName = (rule, value, callback) => {
const list = modalType == 'edit' ? deptOptions.concat(sonArr).filter(g => g.value != editData.id) : deptOptions.concat(sonArr)
if (list.filter(s => s.label == value).length) {
callback('该名称已存在');
} else {
callback();
}
}
return (
<ModalForm
width={400}
title={modalType == 'edit' ? '编辑部门' : '新建部门'}
visible={visible}
onVisibleChange={onVisibleChange}
onFinish={onFinish}
destroyOnClose
initialValues={
modalType == 'edit' ?
{
contract: editData
} :
{
contract: {
enable: true
}
}
}
>
<ProFormText
name={['contract', 'name']}
maxLength={24}
width="md"
label="部门名称"
required
placeholder="请输入部门名称"
rules={[{ required: true, message: '请输入部门名称' }, { validator: checkName }]}
/>
<ProFormSelect
name={['contract', 'dependence']}
label="上级部门"
request={async () => {
let t = modalType === 'edit' ? deptOptions.filter(i => i.value !== editData.id) : deptOptions
return t
}}
disabled={modalType === 'edit' ? editData.subordinate?.length === 0 ? false : true : false}
/>
</ModalForm>
)
}
function mapStateToProps(state) {
return {
};
}
export default connect(mapStateToProps)(DeptModal);

74
web/client/src/sections/organization/components/resetPwd.js

@ -1,74 +0,0 @@
import React, { useRef, useState } from 'react';
import { connect } from 'react-redux';
import { Spin, Card, Modal, TreeSelect } from 'antd';
import ProForm, { ProFormText, ModalForm, ProFormSwitch, ProFormTreeSelect } from '@ant-design/pro-form';
const ResetPwd = (props) => {
const { visible, onVisibleChange, onConfirm } = props;
const formRef = useRef();
const onFinish = (values) => {
if (onConfirm) {
onConfirm(values);
}
}
return (
<Spin spinning={false}>
<ModalForm
title={'重置密码'}
visible={visible}
onVisibleChange={onVisibleChange}
onFinish={onFinish}
formRef={formRef}
destroyOnClose
>
<ProFormText.Password
name={['password']}
width="md"
label="新密码"
required
placeholder="请输入密码"
fieldProps={{
autocomplete: 'new-password'
}}
rules={[
{ required: true, message: '请填写密码' },
{ min: 6, message: '请填写至少6位密码' },
]}
/>
<ProFormText.Password
name={['checkPassword']}
width="md"
label="确认密码"
required
autocomplete='off'
placeholder="请输入密码"
rules={[
{ required: true, message: '请再次填写密码' },
{ min: 6, message: '请填写至少6位密码' },
{
validator: (rule, value, callback) => {
const pwd = formRef.current.getFieldValue('password');
if (!value) {
callback();
}
if (pwd == value) {
callback();
} else {
callback('两次输入的密码不一致');
}
}
}
]}
/>
</ModalForm>
</Spin>
)
}
function mapStateToProps(state) {
return {};
}
export default connect(mapStateToProps)(ResetPwd);

121
web/client/src/sections/organization/components/resource.js

@ -1,121 +0,0 @@
import React, { useEffect } from 'react';
import { Checkbox, Table } from 'antd';
import { useState } from 'react';
const CheckboxGroup = Checkbox.Group;
const Resource = props => {
const { roleData, userRole, userSelected, setResCode, userType } = props;
const [indeterminate, setIndeterminate] = useState({});
const [roleCheck, setRoleCheck] = useState({});//一级权限码
const [parentRoleCheck, setParentRoleCheck] = useState({}); //二级权限码
const roleDatas=roleData.slice(0)
useEffect(() => {
const check = {}
const parentCheck = {}
const initInd = {}
roleData && roleData.map && roleData.map(r => {
let currentInd = false;
let sum = 0;
if (r.resources) {
check[r.code] = []
r.resources.map(child => {
if (userRole.find(code => code.resourceId == child.code)) {
currentInd = true;
sum++;
check[r.code].push(child.code);
}
})
}
parentCheck[r.code] = r.resources.length === sum
initInd[r.code] = parentCheck[r.code] ? false : currentInd
});
setParentRoleCheck(parentCheck)
setRoleCheck(check);
setIndeterminate(initInd);
}, [userRole]);
const setResData = (role) => {
let codes = [];
// Object.keys(partRole).map(r => {
// if (partRole[r]) codes.push(r)
// })
Object.keys(role).map(r => {
if (role[r].length) {
codes.push(r);
}
codes = codes.concat(role[r])
})
setResCode(codes)
}
return (
<Table
bordered
pagination={false}
dataSource={roleDatas}
columns={[{
title: '功能',
key: 'name',
dataIndex: 'name',
render: (text, record) => {
const parentCode = record.code
return <Checkbox
indeterminate={indeterminate[parentCode]}
onChange={(e) => {
const currentParCheck = JSON.parse(JSON.stringify(parentRoleCheck));
currentParCheck[parentCode] = e.target.checked;
const currentCode = JSON.parse(JSON.stringify(roleCheck));
currentCode[parentCode] = e.target.checked ? roleData.find(r => r.code == parentCode).resources.map(r => r.code) : []
const currentInd = JSON.parse(JSON.stringify(indeterminate));
currentInd[parentCode] = false;
setParentRoleCheck(currentParCheck);
setRoleCheck(currentCode);
setIndeterminate(currentInd);
setResData(currentCode)
}}
checked={parentRoleCheck[parentCode] || false}
disabled={userSelected === "SuperAdmin" || userType === 4}
options={''}
>
{text}
</Checkbox>
}
}, {
title: '列表',
key: 'resources',
dataIndex: 'resources',
render: (text, record) => {
let data = [];
console.log()
text.map(s => { s.name !== "整治汇总编辑" ? data.push({ label: s.name, value: s.code }) : '' })
let parentCode = record.code;
return <CheckboxGroup
disabled={userSelected === "SuperAdmin" || userType === 4}
options={data}
value={roleCheck[parentCode] || []}
onChange={value => {
const checkArr = JSON.parse(JSON.stringify(roleCheck));
const parentCheck = JSON.parse(JSON.stringify(parentRoleCheck));
const ind = JSON.parse(JSON.stringify(indeterminate));
const currentCode = roleData.find(r => r.code == parentCode) || {}
checkArr[parentCode] = value;
ind[parentCode] = !!value.length && value.length < currentCode.resources.length
parentCheck[parentCode] = value.length === currentCode.resources.length
setRoleCheck(checkArr);
setIndeterminate(ind);
setParentRoleCheck(parentCheck);
setResData(checkArr)
}}
/>
}
}]}
></Table >
)
}
export default Resource

171
web/client/src/sections/organization/components/userModal.js

@ -1,171 +0,0 @@
import React from 'react';
import { connect } from 'react-redux';
import { Spin, Card, Modal, TreeSelect, message } from 'antd';
import ProForm, { ProFormText, ModalForm, ProFormSwitch, ProFormTreeSelect } from '@ant-design/pro-form';
const UserModal = (props) => {
const { visible, modalType, depData, onVisibleChange, onConfirm, editData } = props
const reg_tel = /^1([358][0-9]|4[579]|66|7[0135678]|9[89])[0-9]{8}$/;
const onFinish = (values) => {
if (onConfirm) {
onConfirm(values);
}
}
const mobile = (value) => {
if (reg_tel.test(value)) {
return
}
return message('请输入姓名')
}
return (
<Spin spinning={false}>
<ModalForm
title={modalType == 'edit' ? '编辑用户' : '新建用户'}
visible={visible}
onVisibleChange={onVisibleChange}
onFinish={onFinish}
destroyOnClose
initialValues={
modalType == 'edit' ?
{
contract: editData
} :
{
contract: {
enable: true
}
}
}
>
<ProForm.Group>
<ProFormText
name={['contract', 'name']}
maxLength={24}
width="md"
label="姓名"
required
placeholder="请输入姓名"
rules={[{ required: true, message: '请输入姓名' }]}
/>
<ProFormText
name={['contract', 'phone']}
width="md"
label="用户名(手机号)"
required
fieldProps={{
maxLength: 11,
}}
getValueFromEvent={(event) => {
return event.target.value.replace(/\D/g, '')
}}
placeholder="请输入用户名(手机号)"
rules={[
{ required: true, valueType: Number, max: 11 }, { pattern: /^1([358][0-9]|4[579]|66|7[0135678]|9[89])[0-9]{8}$/, message: "请输入正确的手机号" }
]}
/>
</ProForm.Group>
<ProForm.Group>
<ProFormTreeSelect
name={['contract', 'departmentId']}
placeholder="请选择所属部门"
width="md"
label="所属部门"
required
fieldNames={{
title: 'name',
key: 'id',
children: 'subordinate'
}}
onSelect={(selectedKeys, { selected, selectedNodes }) => {
if (selected) {
setDepSelectedKeys(selectedKeys)
setDepSelected(selectedNodes[0].name || "")
dispatch(getDepUser(selectedKeys[0]))
}
}}
fieldProps={{
fieldNames: {
label: 'title',
},
treeDefaultExpandAll: false,
}}
rules={[{ required: true, message: '请选择所属部门' }]}
request={async () => {
console.log(depData);
return depData
}}
expandedKeys={["title"]}
/>
< ProFormText
name={['contract', 'post']}
width="md"
label="职位"
// required
placeholder="请输入职位"
/>
</ProForm.Group>
<ProForm.Group>
<ProFormText
name={['contract', 'email']}
width="md"
label="邮箱"
// required
placeholder="请输入邮箱"
rules={[
// { required: true, message: '请输入邮箱' },
{ type: 'email', message: '请输入正确格式的邮箱' },
]}
/>
{modalType == 'edit' ? null : <ProFormText.Password
name={['contract', 'password']}
width="md"
label="密码"
required
placeholder="请输入密码"
fieldProps={{
autocomplete: 'new-password'
}}
rules={[
{ required: true, message: '请填写密码' },
{ min: 6, message: '请填写至少6位密码' },
]}
/>}
</ProForm.Group>
<ProForm.Group>
<ProFormSwitch
name={['contract', 'enable']}
width="md"
label="是否启用"
placeholder="请选择"
// defaultChecked
valuePropName="checked"
/>
</ProForm.Group>
</ModalForm>
</Spin>
)
}
function mapStateToProps(state) {
const { depMessage } = state;
const pakData = (dep) => {
// console.log(dep);
return dep.map((d) => {
return {
title: d.name,
value: d.id,
// key: d.id,
children: pakData(d.subordinate)
}
})
}
let depData = pakData(depMessage.data || [])
return {
loading: depMessage.isRequesting,
depData,
};
}
export default connect(mapStateToProps)(UserModal);

149
web/client/src/sections/organization/containers/authority.js

@ -1,149 +0,0 @@
import React, { useEffect, useState } from 'react';
import { connect } from 'react-redux';
import { Spin, Row, Col, Card, Button, Tree, Empty } from 'antd';
import { getDepMessage, getDepUser } from '../actions/user';
import { getResource, getUserResource, postUserRes } from '../actions/authority';
import Resource from '../components/resource';
const Authority = (props) => {
const { dispatch, loading, depMessage, depUser, resource, userResource, clientHeight } = props
const [depSelectedKeys, setDepSelectedKeys] = useState([])
const [userSelectedKeys, setUserSelectedKeys] = useState([])
const [depSelected, setDepSelected] = useState()
const [userSelected, setUserSelected] = useState()
const [resCode, setResCode] = useState({})
const [useName, setUseName] = useState()// 选中名字
const [userType, setUserType] = useState()
console.log(resource)
useEffect(() => {
dispatch(getResource())
if (!(depMessage && depMessage.length)) {
dispatch(getDepMessage())
}
}, [])
useEffect(() => {
if (depMessage.length) {
setDepSelectedKeys([depMessage[0].id])
setDepSelected([depMessage[0].name])
dispatch(getDepUser(depMessage[0].id))
}
}, [depMessage])
useEffect(() => {
if (depUser.length) {
setUserSelectedKeys([depUser[0].id])
setUserSelected(depUser[0].username)
dispatch(getUserResource(depUser[0].id))
setUseName(depUser[0].name)
}
console.log(depUser, 'depUser')
}, [depUser])
const handleSave = () => {
dispatch(postUserRes({ userId: userSelectedKeys[0], resCode: resCode })).then(res => {
if (res.success) {
dispatch(getUserResource(userSelectedKeys[0]))
}
})
}
return (
<Spin spinning={loading}>
<Row gutter={16}>
<Col span={4} style={{ height: '100%' }}>
<Card title="部门" bordered={false} bodyStyle={{ padding: 8, paddingTop: 24 }}>
{
depMessage.length ?
<Tree
height={clientHeight - 100}
defaultExpandedKeys={[depMessage[0].id]}
selectedKeys={depSelectedKeys}
onSelect={(selectedKeys, { selected, selectedNodes, node }) => {
setUserType(selectedNodes[0].type)
if (selected) {
setDepSelectedKeys(selectedKeys)
setDepSelected(selectedNodes[0].name || "")
dispatch(getDepUser(selectedKeys[0]))
}
}}
treeData={depMessage}
fieldNames={{
title: 'name',
key: 'id',
children: 'subordinate'
}}
/> : ''
}
</Card>
</Col>
<Col span={4} style={{ height: '100%', }}>
<Card title={(depSelected ? `[${depSelected}]` : "") + '用户列表'} bordered={false} bodyStyle={{ padding: 8, paddingTop: 24 }}>
{
depUser.length ?
<Tree
height={clientHeight - 100}
defaultSelectedKeys={[depUser[0].id]}
selectedKeys={userSelectedKeys}
onSelect={(selectedKeys, { selected, selectedNodes, node, event }) => {
const name = node.name
setUseName(name)
if (selected) {
setUserSelectedKeys(selectedKeys)
setUserSelected(selectedNodes[0].username || '')
dispatch(getUserResource(selectedKeys[0]))
}
}}
treeData={depUser}
fieldNames={{
title: 'name',
key: 'id'
}}
/> : <Empty />
}
</Card>
</Col>
<Col span={16} style={{ height: '100%', }}>
{depUser.length ?
<Card title={`[${useName ? useName : '管理员'}] 功能范围`} bordered={false} bodyStyle={{ padding: 8, paddingTop: 24 }}>
<Resource
userSelected={userSelected}
roleData={resource}
userRole={userResource}
setResCode={setResCode}
userType={userType}
/>
<Row type="flex" justify="center" style={{ marginBottom: 16, marginTop: 16, textAlign: 'center' }}>
<Col span="24">
<Button
disabled={userSelected === "SuperAdmin" || userType === 4}
onClick={handleSave}
style={{ width: '60%' }}
type='primary'>保存修改</Button>
</Col></Row>
</Card>
: <Card title={`[]功能范围`} bordered={false} bodyStyle={{ padding: 8, paddingTop: 24 }}>
<Empty />
</Card>
}
</Col>
</Row>
</Spin >
)
}
function mapStateToProps (state) {
const { userResource, resource, depMessage, depUser, global } = state;
return {
clientHeight: global.clientHeight,
loading: depMessage.isRequesting || depUser.isRequesting || resource.isRequesting,
userResource: userResource.data || [],
resource: resource.data || [],
depMessage: depMessage.data || [],
depUser: depUser.data || []
};
}
export default connect(mapStateToProps)(Authority);

6
web/client/src/sections/organization/containers/index.js

@ -1,6 +0,0 @@
'use strict';
import Authority from './authority';
import UserManage from './user';
export { Authority, UserManage };

332
web/client/src/sections/organization/containers/user.js

@ -1,332 +0,0 @@
import React, { useEffect, useState } from 'react';
import { connect } from 'react-redux';
import { FormOutlined, DeleteOutlined } from '@ant-design/icons';
import { Spin, Tooltip, Button, Popconfirm, Row, Col, Tree, Card, Switch } from 'antd';
import ProTable from '@ant-design/pro-table';
import { getDepMessage, getDepUser, createUser, updateUser, delUser, resetPwd, createDept, updateDept, delDept } from '../actions/user';
import UserModal from '../components/userModal';
import ResetPwd from '../components/resetPwd';
import DeptModal from '../components/deptModal';
const TreeNode = Tree.TreeNode;
const UserManage = (props) => {
const user = JSON.parse(sessionStorage.getItem('user'));
const { dispatch, loading, depMessage, depUser, clientHeight } = props;
// 部门
const [deptModalVisible, setDeptModalVisible] = useState(false);
const [deptModalType, setDeptModalType] = useState();
const [deptModalRecord, setDeptModalRecord] = useState();
// 成员
const [modalVisible, setModalVisible] = useState(false);
const [modalType, setModalType] = useState();
const [modalRecord, setModalRecord] = useState();
const [pwdModalVisible, setPwdModalVisible] = useState(false);
const [depSelectedKeys, setDepSelectedKeys] = useState([])
const [rowSelected, setRowSelected] = useState([])
useEffect(() => {
dispatch(getDepMessage())
}, [])
useEffect(() => {
if (depMessage.length) {
setDepSelectedKeys([depMessage[0].id])
dispatch(getDepUser(depMessage[0].id))
}
}, [depMessage])
const columns = [
{
title: '姓名',
dataIndex: 'name',
}, {
title: '用户名(手机号)',
dataIndex: 'username',
},
{
title: '职位',
dataIndex: 'post',
}, {
title: '邮箱',
dataIndex: 'email',
}, {
title: '启用状态',
dataIndex: 'enable',
render: (_, r) => {
return <Switch checkedChildren="启用" unCheckedChildren="禁用" disabled defaultChecked={r.enable} />
}
}, {
title: '操作',
dataIndex: 'action',
render: (dom, record) => {
return record.username == 'SuperAdmin' ? '' : [
<Button type="link" onClick={() => { openModal('edit', record) }}>编辑</Button>,
<Popconfirm
title="确认删除?"
onConfirm={() => {
delUsers([record.id])
}}
>
<Button type="link">删除</Button>
</Popconfirm>,
<Button
type="link"
onClick={() => {
setModalRecord(record);
setPwdModalVisible(true);
}}
>重置密码</Button>
]
},
},
];
//弹窗确认
const onConfirm = (values) => {
if (modalType == 'edit') {
dispatch(updateUser(modalRecord.id, values.contract)).then(res => {
if (res.success) {
setModalVisible(false);
dispatch(getDepUser(depSelectedKeys[0]));
}
});
} else {
dispatch(createUser(values.contract)).then(res => {
if (res.success) {
setModalVisible(false);
dispatch(getDepUser(depSelectedKeys[0]));
}
});
}
}
//打开弹窗
const openModal = (type, record) => {
setModalVisible(true);
setModalType(type);
if (type == 'edit') {
setModalRecord(record);
} else {
setModalRecord(null);
}
}
//删除用户
const delUsers = (ids, type) => {
dispatch(delUser(ids)).then(res => {
dispatch(getDepUser(depSelectedKeys[0]));
if (type == 'batch') {
setRowSelected([]);
}
});
}
//重置密码
const onPwdConfirm = (values) => {
dispatch(resetPwd(modalRecord.id, { password: values.password })).then(res => {
if (res.success) {
setPwdModalVisible(false);
dispatch(getDepUser(depSelectedKeys[0]));
}
});
}
const openDeptModal = (type, record) => {
console.log(type, record, 'type, record')
setDeptModalVisible(true);
setDeptModalType(type);
if (type === 'edit') {
setDeptModalRecord(record);
} else {
setDeptModalRecord(null);
}
}
const onDeptConfirm = (values) => {
if (deptModalType === 'edit') {
dispatch(updateDept(deptModalRecord.id, values.contract)).then(res => {
if (res.success) {
setDeptModalVisible(false);
dispatch(getDepMessage())
}
});
} else {
dispatch(createDept(values.contract)).then(res => {
if (res.success) {
setDeptModalVisible(false);
dispatch(getDepMessage())
}
});
}
}
const delDepartment = (id) => {
dispatch(delDept(id)).then(res => {
if (res.success) {
dispatch(getDepMessage())
}
});
}
const renderTree = (item, id) => {
// let cannotDel = item.users.length || item.subordinate?.filter(is => is.users.length).length;//自己下面有成员 或者下级部门下有成员 不能删除
return <div style={{ display: 'flex', width: '6vw', justifyContent: 'space-between' }}>
<Tooltip title={item.name}>
<div style={{ width: '70%', textOverflow: 'ellipsis', whiteSpace: 'nowrap', overflow: 'hidden' }}>{item.name}</div>
</Tooltip>
<div style={{ width: '30%' }} >
{
depSelectedKeys == id && user.username === "SuperAdmin" ?
<>
<FormOutlined onClick={() => {
setDeptModalRecord(item)
setDeptModalVisible(true)
setDeptModalType('edit')
}} />
{
<Popconfirm title='是否确认删除?' onConfirm={() => { delDepartment(id) }}>
<DeleteOutlined style={{ marginLeft: 5 }} />
</Popconfirm>
}
</> : null
}
</div>
</div>
}
return (<div >
<Spin spinning={loading} /* style={{ height: "calc(100vh - 70px)" }} */>
<Row gutter={16} /* style={{ overflow: "scroll" }} */>
<Col flex="260px" style={{ height: '100%' }}>
<Card title="部门" bordered={false} bodyStyle={{ padding: 8, paddingTop: 24, }}>
{
user.username === "SuperAdmin" && <Button
type="primary"
key="primary"
style={{ margin: '16px 0px' }}
onClick={() => openDeptModal('create')}
>新建部门</Button>
}
{
depMessage.length ?
<Tree
height={clientHeight - 95}
defaultExpandedKeys={[depMessage[0].id]}
selectedKeys={depSelectedKeys}
onSelect={(selectedKeys, e) => {
if (e.selected) {
setDepSelectedKeys(selectedKeys)
dispatch(getDepUser(selectedKeys[0]))
}
}}
// treeData={depMessage}
// fieldNames={{
// title: 'name',
// key: 'id',
// children: 'subordinate'
// }}
>
{
depMessage.map((s, index) => {
return <TreeNode title={renderTree(s, s.id)} key={s.id} >
{
s.subordinate.map(k => {
return <TreeNode title={renderTree(k, k.id)} key={k.id} onMouseOver={() => { setIShowIcon(k.id) }} onMouseOut={() => { setIShowIcon(null) }}>
</TreeNode>
})
}
</TreeNode>
})
}
</Tree> : ''
}
</Card>
</Col>
<Col /* flex="auto" */ style={{ width: "calc(100% - 260px)", height: '100%', display: "black" }}>
<Card title="用户" bordered={false} height={clientHeight} bodyStyle={{ padding: 8, paddingTop: 24, overflow: "hidden", width: "100%" }}>
<ProTable
columns={columns}
dataSource={depUser}
style={{ width: "100% ", height: clientHeight - 95, overflow: "auto" }}
rowSelection={{
selectedRowKeys: rowSelected,
onChange: (selectedRowKeys) => {
setRowSelected(selectedRowKeys);
},
getCheckboxProps: (record) => {
return {
disabled: record.username === 'SuperAdmin',
}
},
}}
options={false}
search={false}
rowKey="id"
toolBarRender={() => [
<span>
<Button
type="primary"
key="primary"
style={{ marginRight: 10 }}
onClick={() => openModal('create')}
>新建用户</Button>
<Button style={{ marginRight: 10 }} onClick={() => { dispatch(getDepUser(depSelectedKeys[0])); }}>刷新</Button>
<Popconfirm title="确认删除?" onConfirm={() => { delUsers(rowSelected, 'batch') }}>
<Button>批量删除</Button>
</Popconfirm>
</span>
]}
/>
</Card>
{
deptModalVisible ?
<DeptModal
visible={deptModalVisible}
onVisibleChange={setDeptModalVisible}
modalType={deptModalType}
onConfirm={onDeptConfirm}
editData={deptModalRecord}
depts={depMessage}
/>
: ''
}
{
depMessage.length && modalVisible ?
<UserModal
visible={modalVisible}
onVisibleChange={setModalVisible}
modalType={modalType}
onConfirm={onConfirm}
editData={modalRecord}
/>
: ''
}
{pwdModalVisible ? <ResetPwd visible={pwdModalVisible}
onVisibleChange={setPwdModalVisible}
onConfirm={onPwdConfirm} /> : ''}
</Col>
</Row>
</Spin>
</div>
)
}
function mapStateToProps(state) {
const { depMessage, depUser, global } = state;
return {
clientHeight: global.clientHeight,
loading: depMessage.isRequesting,
depMessage: depMessage.data || [],
depUser: depUser.data || []
};
}
export default connect(mapStateToProps)(UserManage);

22
web/client/src/sections/organization/nav-item.js

@ -1,22 +0,0 @@
import React from 'react';
import { Link } from 'react-router-dom';
import { Menu } from 'antd';
import { SettingOutlined } from '@ant-design/icons';
const SubMenu = Menu.SubMenu;
export function getNavItem(user, dispatch) {
// if (!Func.isAuthorized("ORG_MANAGE")) {
// return null
// }
return (
<SubMenu key="organization" icon={<SettingOutlined />} title={'组织管理'}>
<Menu.Item key="userManage">
<Link to="/organization/user">部门成员</Link>
</Menu.Item>
<Menu.Item key="authority">
<Link to="/organization/authority">权限配置</Link>
</Menu.Item>
</SubMenu>
);
}

26
web/client/src/sections/organization/routes.js

@ -1,26 +0,0 @@
'use strict';
import { UserManage, Authority } from './containers';
export default [{
type: 'inner',
route: {
path: '/organization',
key: 'organization',
breadcrumb: '组织管理',
menuSelectKeys: ['userManage'],
menuOpenKeys: ['organization'],
childRoutes: [{
path: '/user',
key: 'userManage',
menuSelectKeys: ['userManage'],
component: UserManage,
breadcrumb: '部门成员',
}, {
path: '/authority',
key: 'authority',
component: Authority,
menuSelectKeys: ['authority'],
breadcrumb: '权限配置',
}]
}
}];

9
web/client/src/sections/patrolManage/actions/index.js

@ -1,9 +0,0 @@
'use strict';
import * as plan from './plan'
import * as record from './record'
export default {
...plan,
...record,
}

80
web/client/src/sections/patrolManage/actions/plan.js

@ -1,80 +0,0 @@
'use strict';
import { basicAction } from '@peace/utils'
import { ApiTable } from '$utils'
export function getPatrolPlan() {
return dispatch => basicAction({
type: 'get',
dispatch: dispatch,
actionType: 'GET_PATROL_PLAN',
url: ApiTable.patrolPlan,
msg: { error: '获取巡检计划失败' },
});
}
export function createPatrolPlan(data) {
return dispatch => basicAction({
type: 'post',
data,
dispatch: dispatch,
actionType: 'CREATE_PATROL_PLAN',
url: ApiTable.patrolPlan,
msg: { error: '新增巡检计划失败' },
});
}
export function delPatrolPlan(id) {
return dispatch => basicAction({
type: 'del',
dispatch: dispatch,
actionType: 'DEL_PATROL_PLAN',
url: ApiTable.delPatrolPlan.replace('{id}', id),
msg: { error: '删除巡检计划失败' },
});
}
export function updatePatrolPlan(data) {
return dispatch => basicAction({
type: 'put',
data,
dispatch: dispatch,
actionType: 'UPDATE_PATROL_PLAN',
url: ApiTable.patrolPlan,
msg: { error: '修改巡检计划失败' },
});
}
export function getUserList() {
return dispatch => basicAction({
type: 'get',
dispatch: dispatch,
actionType: 'GET_USER_LIST',
url: ApiTable.getDepUser.replace('{depId}', null),
msg: { error: '获取人员列表失败' },
reducer: { name: 'userList' }
});
}
export function getProjectList(query) {
return (dispatch) => basicAction({
type: 'get',
query,
dispatch,
actionType: 'GET_PROJEECT_LIST',
url: ApiTable.getProjectList,
msg: { error: '获取结构物列表失败', },
reducer: { name: 'structureList' }
});
}
export function positionList(query) {
return (dispatch) => basicAction({
type: 'get',
query,
dispatch,
actionType: 'POSITION_LIST',
url: ApiTable.position,
msg: { error: '获取点位列表失败', },
});
}

17
web/client/src/sections/patrolManage/actions/record.js

@ -1,17 +0,0 @@
'use strict';
import { basicAction } from '@peace/utils'
export const GET_PATROL_RECORD_LIST = 'GET_PATROL_RECORD_LIST';
export const GET_PATROL_RECORD_LIST_SUCCESS = 'GET_PATROL_RECORD_LIST_SUCCESS';
export const GET_PATROL_RECORD_LIST_ERROR = 'GET_PATROL_RECORD_LIST_ERROR';
export function records(url) {
return (dispatch) => basicAction({
type: 'get',
dispatch,
actionType: GET_PATROL_RECORD_LIST,
url: url,
msg: { error: '获取巡检记录失败', },
reducer: { name: 'record' }
});
}

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save