peng.peng 2 years ago
parent
commit
9173ea2bf2
  1. 6
      api/Dockerfile
  2. 268
      api/app/lib/controllers/patrolManage/patrolRecord.js
  3. 2
      api/app/lib/controllers/patrolManage/patrolTemplate.js
  4. 6
      api/app/lib/index.js
  5. 15
      api/app/lib/routes/patrolManage/patrolRecord.js
  6. 33
      jenkinsfile_api
  7. 33
      jenkinsfile_web
  8. 5
      weapp/app.json
  9. 2
      weapp/custom-tab-bar/index.js
  10. 3
      weapp/images/arrow_right_blue.svg
  11. BIN
      weapp/images/calendar.png
  12. BIN
      weapp/images/calendar_card_bg.png
  13. BIN
      weapp/images/calendar_icon.png
  14. BIN
      weapp/images/device.png
  15. BIN
      weapp/images/down1.png
  16. BIN
      weapp/images/edit.png
  17. BIN
      weapp/images/fault_icon.png
  18. BIN
      weapp/images/right_bg.png
  19. BIN
      weapp/images/right_card_bg.png
  20. BIN
      weapp/images/right_icon.png
  21. BIN
      weapp/images/shape1.png
  22. BIN
      weapp/images/shape2.png
  23. BIN
      weapp/images/shape3.png
  24. 168
      weapp/package/deviceBigdataGraph/deviceBigdataGraph.js
  25. 11
      weapp/package/deviceBigdataGraph/deviceBigdataGraph.json
  26. 63
      weapp/package/deviceBigdataGraph/deviceBigdataGraph.wxml
  27. 91
      weapp/package/deviceBigdataGraph/deviceBigdataGraph.wxss
  28. 120
      weapp/package/deviceBigdataGraph/statusDetail/statusDetail.js
  29. 10
      weapp/package/deviceBigdataGraph/statusDetail/statusDetail.json
  30. 26
      weapp/package/deviceBigdataGraph/statusDetail/statusDetail.wxml
  31. 76
      weapp/package/deviceBigdataGraph/statusDetail/statusDetail.wxss
  32. 173
      weapp/package/inspectionInput/inspectionInput.js
  33. 121
      weapp/package/inspectionInput/inspectionInput.wxml
  34. 39
      weapp/package/polling/inspectionRecordDetail/inspectionRecordDetail.js
  35. 110
      weapp/package/polling/inspectionRecordDetail/inspectionRecordDetail.wxml
  36. 7
      weapp/package/polling/polling.js
  37. 4
      weapp/package/polling/polling.json
  38. 5
      weapp/package/polling/polling.wxml
  39. 543
      weapp/package/report/report.js
  40. 25
      weapp/package/report/report.json
  41. 96
      weapp/package/report/report.wxml
  42. 131
      weapp/package/report/report.wxss
  43. 94
      weapp/package/riskManagement/riskCalendar/riskCalendar.js
  44. 8
      weapp/package/riskManagement/riskCalendar/riskCalendar.json
  45. 78
      weapp/package/riskManagement/riskCalendar/riskCalendar.wxml
  46. 104
      weapp/package/riskManagement/riskCalendar/riskCalendar.wxss
  47. 76
      weapp/package/riskManagement/riskManagement.js
  48. 54
      weapp/package/riskManagement/riskManagement.wxml
  49. 45
      weapp/package/riskManagement/riskManagement.wxss
  50. 3
      weapp/package/subSystem/subSystem.js
  51. 46
      weapp/package/subSystem/subSystem.wxml
  52. 44
      weapp/package/subSystem/subSystem.wxss
  53. 53
      weapp/package/troubleshooting/shootingForm/index.js
  54. 11
      weapp/package/troubleshooting/shootingForm/index.wxml
  55. 8
      weapp/pages/home/home.js
  56. 4
      weapp/pages/home/home.wxml
  57. 4
      weapp/pages/home/home.wxss
  58. 29
      weapp/pages/overview/overview.js
  59. 4
      weapp/pages/workbench/workbench.js
  60. 5
      weapp/project.config.json
  61. 22
      weapp/utils/getApiUrl.js
  62. 8
      web/Dockerfile
  63. BIN
      web/client/assets/images/menu/device.png
  64. 4
      web/client/src/app.js
  65. 56
      web/client/src/sections/deviceManage/actions/device.js
  66. 7
      web/client/src/sections/deviceManage/actions/index.js
  67. 213
      web/client/src/sections/deviceManage/components/importDevicesModal.js
  68. 111
      web/client/src/sections/deviceManage/components/modelModal.js
  69. 0
      web/client/src/sections/deviceManage/constants/index.js
  70. 254
      web/client/src/sections/deviceManage/containers/deviceManage.js
  71. 5
      web/client/src/sections/deviceManage/containers/index.js
  72. 0
      web/client/src/sections/deviceManage/containers/style.css
  73. 0
      web/client/src/sections/deviceManage/containers/style.less
  74. 15
      web/client/src/sections/deviceManage/index.js
  75. 13
      web/client/src/sections/deviceManage/nav-item.js
  76. 5
      web/client/src/sections/deviceManage/reducers/index.js
  77. 13
      web/client/src/sections/deviceManage/routes.js
  78. 33
      web/client/src/sections/issueHandle/components/isuue-handle-mdal.js
  79. 2
      web/client/src/sections/issueHandle/containers/patrolRecord.js
  80. 89
      web/client/src/sections/patrolManage/containers/patrolRecord.js
  81. 12
      web/client/src/sections/projectRegime/components/pointModel.js
  82. 32
      web/client/src/sections/projectRegime/components/projectAddModel.js
  83. 7
      web/client/src/sections/projectRegime/containers/point.js
  84. 5
      web/client/src/utils/webapi.js

6
api/Dockerfile

@ -1,16 +1,16 @@
FROM registry.cn-hangzhou.aliyuncs.com/fs-devops/node:12-dev as builder FROM registry.cn-hangzhou.aliyuncs.com/fs-devops/node:12-dev as builder
COPY . /var/app COPY ./api/ /var/app
WORKDIR /var/app WORKDIR /var/app
EXPOSE 8080 EXPOSE 8080
RUN npm config set registry=http://10.8.30.22:7000 RUN npm config set registry=https://nexus.ngaiot.com/repository/fs-npm/
RUN echo "{\"time\":\"$BUILD_TIMESTAMP\",\"build\": \"$BUILD_NUMBER\",\"revision\": \"$SVN_REVISION_1\",\"URL\":\"$SVN_URL_1\"}" > version.json RUN echo "{\"time\":\"$BUILD_TIMESTAMP\",\"build\": \"$BUILD_NUMBER\",\"revision\": \"$SVN_REVISION_1\",\"URL\":\"$SVN_URL_1\"}" > version.json
RUN npm cache clean -f RUN npm cache clean -f
RUN rm -rf package-lock.json RUN rm -rf package-lock.json
RUN npm install --registry http://10.8.30.22:7000 RUN npm install --registry https://nexus.ngaiot.com/repository/fs-npm/
FROM registry.cn-hangzhou.aliyuncs.com/fs-devops/node:12 FROM registry.cn-hangzhou.aliyuncs.com/fs-devops/node:12

268
api/app/lib/controllers/patrolManage/patrolRecord.js

@ -2,6 +2,7 @@
const moment = require("moment"); const moment = require("moment");
async function findPatrolRecord(ctx, next) { async function findPatrolRecord(ctx, next) {
let rslt = []; let rslt = [];
let error = { name: 'FindError', message: '获取巡检记录失败' }; let error = { name: 'FindError', message: '获取巡检记录失败' };
@ -414,21 +415,188 @@ function editPatrolRecordIssueHandle(opts) {
} }
//查询子系统的当月的巡检和维修 //查询子系统的当月的巡检和维修
function getSubSystemPatrolAbout(opts) { function getSubSystemPatrolAbout(opts) {
return async function (ctx, next) {
try {
let rslt = []
const models = ctx.fs.dc.models;
const { STime, ETime, keywords } = ctx.query
let generalInclude = [{ model: models.PatrolRecordIssueHandle }, { model: models.Project, where: { subType: { $like: `%${keywords}%` } } }]
rslt = await models.PatrolRecord.findAll({
where: { inspectionTime: { $between: [STime, ETime] } },
include: generalInclude
})
let userInfo = ctx.fs.api.userInfo;
rslt = rslt.filter(f => f)
if (userInfo.username != 'SuperAdmin') {
if (userInfo.structure) {
rslt = rslt.filter(s => userInfo.structure.find(x => x == s.points.project.id))
} else {
rslt = []
}
}
ctx.status = 200;
ctx.body = rslt
} catch (error) {
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
ctx.status = 400;
ctx.body = { message: '子系统查询巡检记录失败' }
}
}
}
/**
* 查询故障风险统计
* @param structures {String} 结构物id, example: 1,2,3
*/
function getPatrolRecordStatistic(opts) {
return async function (ctx, next) {
try {
let rslt = {
monthAlarmCount: 0, // 本月上报风险
monthHandleCount: 0, // 本月处理风险
historyTrend: [], // 历史风险趋势
monthDeviceAlarm: [], // 设备故障统计
};
const models = ctx.fs.dc.models;
const sequelize = ctx.fs.dc.orm;
const { structures } = ctx.query;
const monthStartTime = moment().startOf("month").format('YYYY-MM-DD HH:mm:ss');
const historyStartTime = moment().startOf("month").subtract(11, 'months').format('YYYY-MM-DD HH:mm:ss');
const endTime = moment().endOf("month").format('YYYY-MM-DD HH:mm:ss');
const monthAlarm = await models.PatrolRecord.findAndCountAll({
where: {
inspectionTime: { $between: [monthStartTime, endTime] },
projectId: { $in: structures.split(',') },
alarm: true
},
include: [{
model: models.Project,
where: { type: '管廊' }
}],
})
rslt.monthAlarmCount = monthAlarm.count;
let abnormalDevice = [];
for (const r of monthAlarm.rows) {
if (Array.isArray(r.points.inspectContent)) {
for (const d of r.points.inspectContent) {
if (d.deviceName && d.alarm) {
let abnormalCount = 0, abnormalScore = 0, slight = 0, middle = 0, severity = 0, itemsCount = [];
const index = abnormalDevice.findIndex(e => e.deviceId === d.deviceId);
if (index !== -1) {
itemsCount = abnormalDevice[index].itemsCount;
slight = abnormalDevice[index].slight;
middle = abnormalDevice[index].middle;
severity = abnormalDevice[index].severity;
}
for (const item of d.checkItems) {
if (item.isNormal === false) {
abnormalCount += 1;
switch (item.level) {
case '轻微':
slight += 1;
abnormalScore += 1;
break;
case '中度':
middle += 1;
abnormalScore += 3;
break;
case '严重':
severity += 1;
abnormalScore += 5;
break;
default:
break;
}
const itemIndex = itemsCount.findIndex(i => i.name === item.name);
if (itemIndex === -1) {
itemsCount.push({ name: item.name, count: 1 });
} else {
itemsCount[itemIndex].count += 1;
}
}
}
if (index !== -1) {
abnormalDevice[index].abnormalCount += abnormalCount;
abnormalDevice[index].abnormalScore += abnormalScore;
abnormalDevice[index].itemsCount = itemsCount;
abnormalDevice[index].slight = slight;
abnormalDevice[index].middle = middle;
abnormalDevice[index].severity = severity;
} else {
abnormalDevice.push({
deviceId: d.deviceId, deviceName: d.deviceName, project: r.points.project.name,
abnormalCount, abnormalScore, itemsCount, slight, middle, severity,
})
}
}
}
}
}
rslt.monthDeviceAlarm = abnormalDevice;
rslt.monthHandleCount = await models.PatrolRecord.count({
where: {
inspectionTime: { $between: [monthStartTime, endTime] },
projectId: { $in: structures.split(',') },
alarm: true
},
include: [{
model: models.PatrolRecordIssueHandle,
where: { state: 6 } // 验收通过
}, {
model: models.Project,
where: { type: '管廊' }
}],
})
// rslt.historyTrend = await sequelize.query(
// `select to_char(inspection_time::DATE, 'YYYY-MM') as month, COUNT(*) as num from patrol_record INNER JOIN "project" ON "patrol_record"."project_id" = "project"."id" AND "project"."type" = '管廊' where inspection_time >= '${historyStartTime}' and inspection_time <= '${endTime}' group by month order by month;`
// );
const monthFn = sequelize.fn('to_char', sequelize.col('inspection_time'), 'YYYY-MM');
rslt.historyTrend = await models.PatrolRecord.findAll({
attributes: [
[monthFn, 'month'],
[sequelize.fn('COUNT', sequelize.col('*')), 'count'],
],
group: [monthFn],
order: [monthFn],
where: {
inspectionTime: { $between: [historyStartTime, endTime] },
projectId: { $in: structures.split(',') },
alarm: true
},
include: [{
attributes: [],
model: models.Project,
where: { type: '管廊' }
}],
})
ctx.status = 200;
ctx.body = rslt
} catch (error) {
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
ctx.status = 400;
ctx.body = { message: '查询故障风险统计失败' }
}
}
}
//根据子系统查询点位信息
function getPointInfo(opts) {
return async function (ctx, next){ return async function (ctx, next){
try{ try{
let rslt=[] let rslt=[]
const models = ctx.fs.dc.models; const models = ctx.fs.dc.models;
const {STime,ETime,keywords}=ctx.query const {keywords}=ctx.query
let generalInclude = [{model: models.PatrolRecordIssueHandle},{model:models.Project,where:{subType :{$like: `%${keywords}%`}}}] let generalInclude = [{model:models.Project,where:{subType :{$like: `%${keywords}%`}}},{model:models.Device}]
rslt=await models.PatrolRecord.findAll({ rslt=await models.Point.findAll({
where:{inspectionTime: { $between: [STime, ETime] }},
include:generalInclude include:generalInclude
}) })
let userInfo = ctx.fs.api.userInfo; let userInfo = ctx.fs.api.userInfo;
rslt = rslt.filter(f => f) rslt = rslt.filter(f => f)
if (userInfo.username != 'SuperAdmin') { if (userInfo.username != 'SuperAdmin') {
if (userInfo.structure) { if (userInfo.structure) {
rslt = rslt.filter(s => userInfo.structure.find(x => x == s.points.project.id)) rslt = rslt.filter(s => userInfo.structure.find(x => x == s.project.userId))
} else { } else {
rslt = [] rslt = []
} }
@ -438,11 +606,91 @@ function getSubSystemPatrolAbout(opts) {
}catch(error){ }catch(error){
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`); ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
ctx.status = 400; ctx.status = 400;
ctx.body = { message: '子系统查询巡检记录失败' } ctx.body = { message: '根据子系统查询点位信息失败' }
}
}
}
//根据结构物查询对应巡检计划的模板
function getTemplate(opts){
return async function (ctx, next){
try{
let rslt=[]
const models = ctx.fs.dc.models;
const {keywords}=ctx.query
rslt=await models.PatrolPlan.findAll({
include:[
{model:models.Project,
where:{subType:{$like: `%${keywords}%`}}},
{model:models.PatrolTemplate}]
})
let userInfo = ctx.fs.api.userInfo;
rslt = rslt.filter(f => f)
if (userInfo.username != 'SuperAdmin') {
if (userInfo.structure) {
rslt = rslt.filter(s => userInfo.structure.find(x => x == s.project.userId))
} else {
rslt = []
}
}
ctx.status = 200;
ctx.body = rslt
}catch(error){
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
ctx.status = 400;
ctx.body = { message: '根据结构物查询对应巡检计划的模板失败' }
} }
} }
} }
// //上报问题和发现问题接口
function reportQuest(opts){
return async function (ctx, next){
try{
const transaction = await ctx.fs.dc.orm.transaction();
const models = ctx.fs.dc.models;
const data = ctx.request.body;
let { patrolPlanId, inspectionTime, points, alarm, pointId, projectId } = data
const pointRecord = await models.PatrolRecord.findAll({
where: { pointId: pointId },
order: [['inspectionTime', 'desc']],
attributes: ['inspectionTime'],
});
const lastInspectionTime = pointRecord.length ? pointRecord[0].dataValues.inspectionTime : null;
const recordRes = await models.PatrolRecord.create(
// record
{
patrolPlanId: patrolPlanId,
lastInspectionTime,
inspectionTime,
points,
alarm,
pointId: pointId,
projectId
},{transaction}
);
if (alarm) {
await models.PatrolRecordIssueHandle.create({
patrolRecordId: recordRes.id,
state: 1,
}, { transaction });
}
await transaction.commit();
ctx.status = 204;
}catch(error){
await transaction.rollback();
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
ctx.status = 400;
ctx.body = { message: '上报问题失败' }
}
}
}
//根据子系统查询点位信息
// function getPointInfo(opts) {
// return async function (ctx, next){
// }
// }
Array.prototype.group = function (callback, thisArg = null) { Array.prototype.group = function (callback, thisArg = null) {
// 参数合法性判断 // 参数合法性判断
@ -473,5 +721,9 @@ module.exports = {
getPatrolRecordIssueHandleById, getPatrolRecordIssueHandleById,
addPatrolRecordIssueHandle, addPatrolRecordIssueHandle,
editPatrolRecordIssueHandle, editPatrolRecordIssueHandle,
getSubSystemPatrolAbout getSubSystemPatrolAbout,
getPatrolRecordStatistic,
getPointInfo,
getTemplate,
reportQuest
} }

2
api/app/lib/controllers/patrolManage/patrolTemplate.js

@ -16,7 +16,7 @@ async function getPatrolTemplate (ctx, next) {
model: models.CheckItems, model: models.CheckItems,
}, { }, {
model: models.PatrolPlan, model: models.PatrolPlan,
attributes: ['name'], attributes: ['structure_id','name'],
}] }]
}; };
if (id) { if (id) {

6
api/app/lib/index.js

@ -111,4 +111,10 @@ module.exports.models = function (dc) { // dc = { orm: Sequelize对象, ORM: Seq
PointDevice.belongsTo(Device, { foreignKey: 'deviceId', targetKey: 'id' }); PointDevice.belongsTo(Device, { foreignKey: 'deviceId', targetKey: 'id' });
Device.hasMany(PointDevice, { foreignKey: 'deviceId', sourceKey: 'id' }); Device.hasMany(PointDevice, { foreignKey: 'deviceId', sourceKey: 'id' });
Device.belongsToMany(Point,{ through: PointDevice, foreignKey: 'deviceId', otherKey: 'pointId' })
Point.belongsToMany(Device,{ through: PointDevice, foreignKey: 'pointId', otherKey: 'deviceId' })
}; };

15
api/app/lib/routes/patrolManage/patrolRecord.js

@ -34,4 +34,19 @@ module.exports = function (app, router, opts) {
//子系统巡检记录 //子系统巡检记录
app.fs.api.logAttr['GET/patrolRecord/subSystemPatrolAbout'] = { content: '子系统查询巡检记录', visible: true }; app.fs.api.logAttr['GET/patrolRecord/subSystemPatrolAbout'] = { content: '子系统查询巡检记录', visible: true };
router.get('/patrolRecord/subSystemPatrolAbout', patrolRecord.getSubSystemPatrolAbout(opts)) router.get('/patrolRecord/subSystemPatrolAbout', patrolRecord.getSubSystemPatrolAbout(opts))
//故障风险管理-统计接口
app.fs.api.logAttr['GET/patrolRecord/statistic'] = { content: '故障风险统计', visible: true };
router.get('/patrolRecord/statistic', patrolRecord.getPatrolRecordStatistic(opts))
//点位信息
app.fs.api.logAttr['GET/patrolRecord/pointInfo'] = { content: '点位信息', visible: true };
router.get('/patrolRecord/pointInfo', patrolRecord.getPointInfo(opts))
//查询模板
app.fs.api.logAttr['GET/patrolRecord/getTemplate'] = { content: '查询模板', visible: true };
router.get('/patrolRecord/getTemplate', patrolRecord.getTemplate(opts))
//查询模板
app.fs.api.logAttr['POST/patrolRecord/reportQuest'] = { content: '上报问题', visible: true };
router.post('/patrolRecord/reportQuest', patrolRecord.reportQuest(opts))
}; };

33
jenkinsfile_api

@ -1,18 +1,25 @@
pipeline { podTemplate {
agent { node('pod-templ-jenkins-slave-common') {
node{
label 'jnlp-slave' env.IMAGE_NAME = "${IOT_IMAGES_REGISTRY}/${IOT}/${JOB_NAME}"
} env.IMAGE_NAME_SHORT = "${IOT}/${JOB_NAME}"
} env.CODE_ADDR = "${GIT_ADDRESS}/free-sun/Inspection.git"
stage('Run shell') {
git branch: 'master', credentialsId: 'gitea-builder', url: "${CODE_ADDR}"
stages { container('image-builder') {
stage('巡检 api ......') { sh'''
steps { pwd
buildName "#${BUILD_NUMBER} ~/smartcity/${JOB_NAME}:${IMAGE_VERSION}" ls -al
buildDescription "harbor.anxinyun.cn/smartcity/${JOB_NAME}:${IMAGE_VERSION}"
sh 'nerdctl build -t harbor.anxinyun.cn/smartcity/${JOB_NAME}:${IMAGE_VERSION} ./api' /kaniko/executor --context=${BUILD_WORKSPACE} --dockerfile=./api/Dockerfile --destination=${IMAGE_NAME}:${IMAGE_VERSION} --cache=false --cleanup
sh 'nerdctl push harbor.anxinyun.cn/smartcity/${JOB_NAME}:${IMAGE_VERSION}'
'''
} }
buildName "${IMAGE_NAME_SHORT}:${IMAGE_VERSION}"
buildDescription "${IMAGE_NAME}:${IMAGE_VERSION}"
} }
} }
} }

33
jenkinsfile_web

@ -1,18 +1,25 @@
pipeline { podTemplate {
agent { node('pod-templ-jenkins-slave-common') {
node{
label 'jnlp-slave' env.IMAGE_NAME = "${IOT_IMAGES_REGISTRY}/${IOT}/${JOB_NAME}"
} env.IMAGE_NAME_SHORT = "${IOT}/${JOB_NAME}"
} env.CODE_ADDR = "${GIT_ADDRESS}/free-sun/Inspection.git"
stage('Run shell') {
git branch: 'master', credentialsId: 'gitea-builder', url: "${CODE_ADDR}"
stages { container('image-builder') {
stage('巡检 web ......') { sh'''
steps { pwd
buildName "#${BUILD_NUMBER} ~/smartcity/${JOB_NAME}:${IMAGE_VERSION}" ls -al
buildDescription "harbor.anxinyun.cn/smartcity/${JOB_NAME}:${IMAGE_VERSION}"
sh 'nerdctl build -t harbor.anxinyun.cn/smartcity/${JOB_NAME}:${IMAGE_VERSION} ./web' /kaniko/executor --context=${BUILD_WORKSPACE} --dockerfile=./web/Dockerfile --destination=${IMAGE_NAME}:${IMAGE_VERSION} --cache=false --cleanup
sh 'nerdctl push harbor.anxinyun.cn/smartcity/${JOB_NAME}:${IMAGE_VERSION}'
'''
} }
buildName "${IMAGE_NAME_SHORT}:${IMAGE_VERSION}"
buildDescription "${IMAGE_NAME}:${IMAGE_VERSION}"
} }
} }
} }

5
weapp/app.json

@ -23,7 +23,10 @@
"pointsStatus/pointsStatus", "pointsStatus/pointsStatus",
"subSystem/subSystem", "subSystem/subSystem",
"riskManagement/riskManagement", "riskManagement/riskManagement",
"riskManagement/riskCalendar/riskCalendar" "riskManagement/riskCalendar/riskCalendar",
"deviceBigdataGraph/deviceBigdataGraph",
"deviceBigdataGraph/statusDetail/statusDetail",
"report/report"
] ]
} }
], ],

2
weapp/custom-tab-bar/index.js

@ -9,7 +9,7 @@ Component({
let userRole = wx.getStorageSync('userRole'); let userRole = wx.getStorageSync('userRole');
// 0 表示普通用户 1表示管理员 // 0 表示普通用户 1表示管理员
console.log('userRole', userRole); console.log('userRole', userRole);
if (userRole && userRole.includes('管理')) { if (userRole && userRole.includes('巡检')) {
this.setData({ this.setData({
list: getApp().globalData.managerList list: getApp().globalData.managerList
}) })

3
weapp/images/arrow_right_blue.svg

@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 12 12" class="design-iconfont">
<path d="M8.918625,6.235875 C8.924625,6.22423828 8.93436328,6.21451172 8.939625,6.20248828 C9.04275,5.98798828 9.009,5.7255 8.826375,5.55298828 L4.68675,1.637625 C4.46098828,1.42423828 4.10513672,1.43436328 3.891375,1.65976172 C3.67798828,1.88551172 3.68775,2.24136328 3.91351172,2.45513672 L7.62788672,5.968875 L3.928125,9.53475 C3.704625,9.75036328 3.697875,10.1062383 3.91351172,10.330125 C4.02375,10.4448633 4.17113672,10.5022617 4.31851172,10.5022617 C4.45913672,10.5022617 4.59976172,10.4497617 4.70888672,10.3447617 L8.81101172,6.39113672 C8.81889844,6.38325 8.821875,6.37238672 8.82976172,6.36414844 C8.83577344,6.35852344 8.84213672,6.35438672 8.848125,6.34839844 C8.88,6.315 8.896875,6.27411328 8.918625,6.235875 L8.918625,6.235875 Z" fill-opacity=".65" fill="#1684FF" fill-rule="nonzero"/>
</svg>

After

Width:  |  Height:  |  Size: 895 B

BIN
weapp/images/calendar.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
weapp/images/calendar_card_bg.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 808 B

BIN
weapp/images/calendar_icon.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
weapp/images/device.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

BIN
weapp/images/down1.png

Binary file not shown.

BIN
weapp/images/edit.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
weapp/images/fault_icon.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 942 B

BIN
weapp/images/right_bg.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

BIN
weapp/images/right_card_bg.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

BIN
weapp/images/right_icon.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
weapp/images/shape1.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

BIN
weapp/images/shape2.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 808 B

BIN
weapp/images/shape3.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

168
weapp/package/deviceBigdataGraph/deviceBigdataGraph.js

@ -0,0 +1,168 @@
// package/riskManagement/riskCalendar/riskCalendar.js
import * as echarts from '../components/ec-canvas/echarts';
Page({
initECharts(option) {
this.ecComponent.init((canvas, width, height, dpr) => {
const chart = echarts.init(canvas, null, {
width: width,
height: height,
devicePixelRatio: dpr,
});
chart.setOption(option);
this.chart = chart;
return chart;
});
},
initDeviceECharts(option) {
this.ecDeviceComponent.init((canvas, width, height, dpr) => {
const chart = echarts.init(canvas, null, {
width: width,
height: height,
devicePixelRatio: dpr,
});
chart.setOption(option);
this.chart = chart;
return chart;
});
},
/**
* 页面的初始数据
*/
data: {
ec:{}
},
navigator(e) {
wx.navigateTo({
url: '/package/deviceBigdataGraph/statusDetail/statusDetail',
})
console.log('xxxxxx',e)
},
/**
* 生命周期函数--监听页面加载
*/
onLoad(options) {
const that = this
that.ecComponent = that.selectComponent('#mychart-dom-pie');
that.ecDeviceComponent=that.selectComponent('#mychart-device-pie');
var option = {
backgroundColor: "#ffffff",
legend: {
bottom: 10,
left: 'center',
},
series: [{
label: {
normal: {
fontSize: 14
}
},
type: 'pie',
center: ['50%', '50%'],
radius: ['20%', '40%'],
data: [{
name: '类型一',
value: 1
},
{
name: '类型二',
value: 2
},
{
name: '类型三',
value: 3
},
{
name: '类型四',
value: 4
}
]
}]
};
var optionDevice = {
backgroundColor: "#ffffff",
legend: {
bottom: 10,
left: 'center',
},
series: [{
label: {
normal: {
fontSize: 14
}
},
type: 'pie',
center: ['50%', '50%'],
radius: ['20%', '40%'],
data: [{
name: '正常',
value: 1
},
{
name: '未知',
value: 2
},
{
name: '异常',
value: 3
},
]
}]
};
that.initECharts(option);
that.initDeviceECharts(optionDevice);
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady() {
},
/**
* 生命周期函数--监听页面显示
*/
onShow() {
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide() {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload() {
},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh() {
},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom() {
},
/**
* 用户点击右上角分享
*/
onShareAppMessage() {
}
})

11
weapp/package/deviceBigdataGraph/deviceBigdataGraph.json

@ -0,0 +1,11 @@
{
"navigationBarBackgroundColor": "#1979ff",
"navigationBarTextStyle": "white",
"navigationBarTitleText": "设备大数据图谱",
"enablePullDownRefresh": false,
"usingComponents": {
"ec-canvas": "../components/ec-canvas/ec-canvas",
"van-button": "@vant/weapp/button/index",
"van-progress": "@vant/weapp/progress/index"
}
}

63
weapp/package/deviceBigdataGraph/deviceBigdataGraph.wxml

@ -0,0 +1,63 @@
<!--package/riskManagement/riskCalendar/riskCalendar.wxml-->
<view class="container">
<!--质保图谱-->
<view class="card">
<image src="/images/shape3.png" class="imgStyle"></image>
<view class="top">
<view style="display: flex; align-items: center;">
<image style="width: 30px; height: 30px;" src="/images/edit.png" />
<text class="fontStyle">质保图谱</text>
</view>
<view class="detailStyle">
<van-button type="info" round size="small">查看详情</van-button>
</view>
</view>
<view class="progress-container">
<text class="label">过保比率:</text>
<view class="progress-wrapper">
<progress class="progress" percent="50" color="#4E87FF"></progress>
<text>50%</text>
</view>
</view>
<view class="progress-container">
<text class="label">质保期比例:</text>
<view class="progress-wrapper">
<progress class="progress" percent="50"></progress>
<text>50%</text>
</view>
</view>
</view>
<!--设备状态-->
<view class="card">
<image src="/images/shape1.png" class="imgStyle"></image>
<view class="top">
<view style="display: flex; align-items: center;">
<image style="width: 30px; height: 30px;" src="/images/device.png" />
<text class="fontStyle">设备状态</text>
</view>
<view class="detailStyle">
<van-button type="info" round size="small" bindtap="navigator">查看详情</van-button>
</view>
</view>
<view style="height: 250px;">
<ec-canvas id="mychart-device-pie" canvas-id="mychart-device-pie" ec="{{ ec }}"></ec-canvas>
</view>
</view>
<!--设备类型-->
<view class="card">
<image src="/images/shape1.png" class="imgStyle"></image>
<view class="top">
<view style="display: flex; align-items: center;">
<image style="width: 30px; height: 30px;" src="/images/device.png" />
<text class="fontStyle">设备类型</text>
</view>
<view class="countStyle">
总数:
</view>
</view>
<view style="height: 250px;">
<ec-canvas id="mychart-dom-pie" canvas-id="mychart-pie" ec="{{ ec }}"></ec-canvas>
</view>
</view>
</view>

91
weapp/package/deviceBigdataGraph/deviceBigdataGraph.wxss

@ -0,0 +1,91 @@
/* package/riskManagement/riskCalendar/riskCalendar.wxss */
.container {
background-image: linear-gradient(179deg, #006BE3 0%, #4E87FF 16%, #4e87ff00 93%);
padding: 0 15px;
}
.card {
position: relative;
background-color: #fff;
border: 1px solid #ddd;
border-radius: 8px;
/* padding: 10px; */
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
margin-top: 12px;
width: 100%;
background-image: linear-gradient(0deg, #F3F7FF 84%, #DBE6FF 100%);
}
.top {
display: flex;
justify-content: space-between;
padding: 10px;
/* background-position: bottom; */
}
.fontStyle {
width: 64px;
height: 22px;
font-family: PingFangSC-Medium;
font-weight: 500;
font-size: 16px;
color: #000000d9;
letter-spacing: 0;
margin-left: 5px;
}
.imgStyle {
position: absolute;
width: 115px;
height: 67px;
right: 0;
}
.detailStyle {
z-index: 1;
}
.progress-container {
display: flex;
align-items: center;
width: 100%;
margin: 0px 0px 10px 10px;
}
.label {
margin-right: 10px;
width: 50%;
height: 20px;
font-family: PingFangSC-Regular;
font-weight: 400;
font-size: 14px;
color: #383A3B;
}
.progress-wrapper {
display: flex;
align-items: center;
width: 100%;
}
.percentage {
margin-right: 10px;
}
.progress {
width: 75%;
}
.countStyle {
width: 89px;
height: 24px;
font-family: PingFangSC-Medium;
font-weight: 500;
font-size: 17px;
color: #1684FF;
letter-spacing: 0;
}

120
weapp/package/deviceBigdataGraph/statusDetail/statusDetail.js

@ -0,0 +1,120 @@
// package/deviceBigdataGraph/detail/detail.js
import * as echarts from '../../components/ec-canvas/echarts';
function setOption(chart, data) {
const option = {
grid: {
top: '5%',
left: '3%',
right: '4%',
bottom: '3%',
containLabel: true
},
xAxis: {
type: 'category',
data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
},
yAxis: {
type: 'value'
},
series: [
{
data: data,
type: 'line'
}
]
};
chart.setOption(option);
}
Page({
/**
* 页面的初始数据
*/
data: {
ec: {
// onInit: initChart,
lazyLoad: true, // 将 lazyLoad 设为 true 后,需要手动初始化图表
},
isLoaded: false,
list: [1,2,3]
},
/**
* 生命周期函数--监听页面加载
*/
onLoad(options) {
setTimeout(() => {
this.initChart([250, 300, 100, 147, 260, 123, 311])
}, 1000)
},
initChart: function (data) {
this.ecComponent.init((canvas, width, height, dpr) => {
const chart = echarts.init(canvas, null, {
width: width,
height: height,
devicePixelRatio: dpr // new
});
setOption(chart, data);
// 将图表实例绑定到 this 上,可以在其他成员函数中访问
this.chart = chart;
this.setData({
isLoaded: true,
});
// 注意这里一定要返回 chart 实例,否则会影响事件处理等
return chart;
});
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady() {
this.ecComponent = this.selectComponent('#device-status-chart');
},
/**
* 生命周期函数--监听页面显示
*/
onShow() {
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide() {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload() {
},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh() {
},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom() {
},
/**
* 用户点击右上角分享
*/
onShareAppMessage() {
}
})

10
weapp/package/deviceBigdataGraph/statusDetail/statusDetail.json

@ -0,0 +1,10 @@
{
"navigationBarBackgroundColor": "#1979ff",
"navigationBarTextStyle": "white",
"navigationBarTitleText": "设备状态",
"enablePullDownRefresh": false,
"usingComponents": {
"ec-canvas": "../../components/ec-canvas/ec-canvas"
}
}

26
weapp/package/deviceBigdataGraph/statusDetail/statusDetail.wxml

@ -0,0 +1,26 @@
<!--package/deviceBigdataGraph/detail/detail.wxml-->
<view class="status-detail">
<view class="icon"><text class="icon-text">设备总数</text></view>
<view>300</view>
<view class="flex flex-around">
<view class="title-item flex flex-col">
<view>设备故障率</view>
<view><text class="title-num">{{86}}%</text></view>
</view>
<view class="title-item flex flex-col">
<view>完好率</view>
<view><text class="title-num">{{300}}%</text></view>
</view>
</view>
<view class="card">
<view class="flex flex-start">
<!-- <image src="" class="card-img" /> -->
<view class="card-img" style="background: blue;"></view>
<view class="card-title">历史风险趋势</view>
</view>
<view class="chart">
<ec-canvas id="device-status-chart" canvas-id="device-status-chart" ec="{{ ec }}"></ec-canvas>
</view>
</view>
</view>

76
weapp/package/deviceBigdataGraph/statusDetail/statusDetail.wxss

@ -0,0 +1,76 @@
/* package/deviceBigdataGraph/detail/detail.wxss */
.status-detail {
height: 100vh;
background-image: linear-gradient(179deg, #006BE3 0%, #4E87FF 16%, #4e87ff00 93%);
padding: 0 15px;
}
.icon {
width: 61px;
height: 31.86px;
background-image: linear-gradient(0deg, #EAF2FF 5%, #2578F0 100%);
box-shadow: 0 3px 4px 1px #bfdbfa4f;
}
.icon-text {
width: 48px;
height: 17px;
font-family: PingFangSC-Medium;
font-weight: 500;
font-size: 12px;
color: #FFFFFF;
}
.title-item {
width: 150px;
color: #ffffffd9;
}
.title-num {
font-size: 20px;
color: #FFFFFF;
}
.title-unit {
font-size: 10px;
color: #FFFFFE;
margin-left: 10px;
}
.card {
background: #FFFFFF;
box-shadow: 2px 2px 11px 0 #00000008, 0 0 4px 0 #00000012;
border-radius: 4px;
padding: 12px;
margin-top: 12px;
}
.card-img {
width: 18px;
height: 18px;
margin-right: 13px;
}
.card-title {
font-weight: 500;
font-size: 16px;
color: #383A3B;
}
.card-link {
font-weight: 500;
font-size: 14px;
color: #1684FF;
}
.chart {
width: 100%;
height: 195px;
margin-top: 20px;
}
.list {
margin-top: 10px;
padding: 10px 7px;
background-color: #F1F7FF;
}

173
weapp/package/inspectionInput/inspectionInput.js

@ -13,7 +13,7 @@ Page({
address: '', // 当前位置 address: '', // 当前位置
imgUrl: getApp().globalData.imgUrl, imgUrl: getApp().globalData.imgUrl,
checkItems: [], // 检查项 checkItems: [], // 检查项
inspectContent: {}, // 巡检内容 inspectContentArr: [], // 巡检内容
isCommitting: false, isCommitting: false,
/*** 扫码巡检 ***/ /*** 扫码巡检 ***/
planList: null, // 巡检计划列表 planList: null, // 巡检计划列表
@ -61,26 +61,46 @@ Page({
}, },
// 获取巡检模板 // 获取巡检模板
getPatrolTemplate (templateId) { getPatrolTemplate(templateId, pointDevices = []) {
Request.get(getPatrolTemplate(templateId)).then(res => { Request.get(getPatrolTemplate(templateId)).then(res => {
const checkItems = res.rows[0].checkItems; const checkItems = res.rows[0].checkItems;
const inspectContent = {}; let inspectContentArr = [];
for (const c of checkItems) { // 有绑定设备的点位,每个设备都要检查各个检查项
inspectContent[c.name] = { if (pointDevices.length) {
pointDevices.forEach(device => {
inspectContentArr.push({
deviceName: device.device.name,
deviceId: device.deviceId,
checkItems: checkItems.map(c => ({
id: `${device.deviceId}-${c.id}`,
name: c.name,
isNormal: null, isNormal: null,
msgInp: null, msgInp: null,
level: null, level: null,
imgs: [], imgs: [],
}; }))
})
});
} else {
inspectContentArr.push({
checkItems: checkItems.map(c => ({
id: c.id,
name: c.name,
isNormal: null,
msgInp: null,
level: null,
imgs: [],
}))
})
} }
this.setData({ this.setData({
checkItems, checkItems,
inspectContent, inspectContentArr: inspectContentArr,
}) })
}) })
}, },
onPickerChange (e) { onPickerChange(e) {
const { key } = e.currentTarget.dataset; const { key } = e.currentTarget.dataset;
const { value } = e.detail; const { value } = e.detail;
this.setData({ this.setData({
@ -90,26 +110,27 @@ Page({
}); });
const curPlan = this.data.planList[e.detail.columns[0].index]; const curPlan = this.data.planList[e.detail.columns[0].index];
const nextItemData = curPlan.points.find(p => p.id == this.data.scenePointId)
this.setData({ this.setData({
dataList: curPlan, dataList: curPlan,
itemData: curPlan.points.find(p => p.id == this.data.scenePointId) itemData: nextItemData
}); });
this.getPatrolTemplate(curPlan.templateId); this.getPatrolTemplate(curPlan.templateId, nextItemData.pointDevices);
}, },
onPickerCancel (e) { onPickerCancel(e) {
const { key } = e.currentTarget.dataset; const { key } = e.currentTarget.dataset;
this.setData({ this.setData({
[`${key}Visible`]: false, [`${key}Visible`]: false,
}); });
}, },
onPlanListPicker () { onPlanListPicker() {
this.setData({ planListVisible: true }); this.setData({ planListVisible: true });
}, },
// 获取当前位置 // 获取当前位置
selfLocation () { selfLocation() {
const that = this const that = this
wx.showLoading({ wx.showLoading({
title: '定位中', title: '定位中',
@ -140,42 +161,43 @@ Page({
}); });
}, },
handleChangeTwo (e) { handleChangeTwo(e) {
const isNormal = e.detail === 'normal'; const isNormal = e.detail === 'normal';
const inspectContent = this.data.inspectContent; const { deviceidx, itemidx } = e.currentTarget.dataset;
inspectContent[e.currentTarget.dataset.item].isNormal = isNormal; let nextInspectContentArr = this.data.inspectContentArr;
nextInspectContentArr[deviceidx].checkItems[itemidx].isNormal = isNormal;
if (isNormal) { // 清除异常数据 if (isNormal) { // 清除异常数据
inspectContent[e.currentTarget.dataset.item].msgInp = null; nextInspectContentArr[deviceidx].checkItems[itemidx].msgInp = null;
inspectContent[e.currentTarget.dataset.item].level = null; nextInspectContentArr[deviceidx].checkItems[itemidx].level = null;
inspectContent[e.currentTarget.dataset.item].imgs = []; nextInspectContentArr[deviceidx].checkItems[itemidx].imgs = [];
} }
this.setData({ this.setData({ inspectContentArr: nextInspectContentArr })
inspectContent,
})
}, },
handleChangeThree (e) { handleChangeThree(e) {
const inspectContent = this.data.inspectContent; const { deviceidx, itemidx } = e.currentTarget.dataset;
inspectContent[e.currentTarget.dataset.item].level = e.detail; let nextInspectContentArr = this.data.inspectContentArr;
this.setData({
inspectContent nextInspectContentArr[deviceidx].checkItems[itemidx].level = e.detail;
}) this.setData({ inspectContentArr: nextInspectContentArr })
}, },
// 巡查详情 // 巡查详情
bindInput: function (e) { bindInput: function (e) {
const inspectContent = this.data.inspectContent; const { deviceidx, itemidx } = e.currentTarget.dataset;
inspectContent[e.currentTarget.dataset.item].msgInp = e.detail.value; let nextInspectContentArr = this.data.inspectContentArr;
this.setData({
inspectContent nextInspectContentArr[deviceidx].checkItems[itemidx].msgInp = e.detail.value;
}) this.setData({ inspectContentArr: nextInspectContentArr })
}, },
// 上传图片 // 上传图片
chooseImg: function (e) { // 这里是选取图片的方法 chooseImg: function (e) { // 这里是选取图片的方法
const { deviceidx, itemidx } = e.currentTarget.dataset;
const that = this; const that = this;
let pics = []; let pics = [];
const detailPics = that.data.inspectContent[e.currentTarget.dataset.item].imgs; const detailPics = that.data.inspectContentArr[deviceidx].checkItems[itemidx].imgs;
if (detailPics.length >= 20) { if (detailPics.length >= 20) {
wx.showToast({ wx.showToast({
title: '最多选择20张图片上传', title: '最多选择20张图片上传',
@ -203,16 +225,16 @@ Page({
} }
pics.push(imgs[i].tempFilePath) pics.push(imgs[i].tempFilePath)
} }
that.uploadimg({ that.uploadImg({
url: getApp().globalData.webUrl + '_upload/attachments/project', // 图片上传的接口 url: getApp().globalData.webUrl + '_upload/attachments/project', // 图片上传的接口
path: pics, // 选取的图片的地址数组 path: pics, // 选取的图片的地址数组
}, e.currentTarget.dataset.item); }, deviceidx, itemidx);
}, },
}) })
}, },
//多张图片上传 //多张图片上传
uploadimg: function (data, itemName) { uploadImg: function (data, deviceidx, itemidx) {
wx.showLoading({ wx.showLoading({
title: '上传中...', title: '上传中...',
mask: true, mask: true,
@ -221,7 +243,7 @@ Page({
i = data.i ? data.i : 0, i = data.i ? data.i : 0,
success = data.success ? data.success : 0, success = data.success ? data.success : 0,
fail = data.fail ? data.fail : 0; fail = data.fail ? data.fail : 0;
let imgs = that.data.inspectContent[itemName].imgs; let imgs = that.data.inspectContentArr[deviceidx].checkItems[itemidx].imgs;
wx.uploadFile({ wx.uploadFile({
url: data.url, url: data.url,
filePath: data.path[i], filePath: data.path[i],
@ -232,19 +254,15 @@ Page({
let str = JSON.parse(resp.data) // 返回的结果,可能不同项目结果不一样 let str = JSON.parse(resp.data) // 返回的结果,可能不同项目结果不一样
str = str.uploaded str = str.uploaded
if (imgs.length >= 20) { if (imgs.length >= 20) {
const inspectContent = that.data.inspectContent; let nextInspectContentArr = that.data.inspectContentArr;
inspectContent[itemName].imgs = imgs; nextInspectContentArr[deviceidx].checkItems[itemidx].imgs = imgs;
that.setData({ that.setData({ inspectContentArr: nextInspectContentArr });
inspectContent,
});
return false; return false;
} else { } else {
imgs.push(str); imgs.push(str);
const inspectContent = that.data.inspectContent; let nextInspectContentArr = that.data.inspectContentArr;
inspectContent[itemName].imgs = imgs; nextInspectContentArr[deviceidx].checkItems[itemidx].imgs = imgs;
that.setData({ that.setData({ inspectContentArr: nextInspectContentArr });
inspectContent,
})
} }
}, },
fail: (res) => { fail: (res) => {
@ -260,7 +278,7 @@ Page({
data.i = i; data.i = i;
data.success = success; data.success = success;
data.fail = fail; data.fail = fail;
that.uploadimg(data, itemName); // 递归,回调自己 that.uploadImg(data, deviceidx, itemidx); // 递归,回调自己
} }
} }
}); });
@ -268,22 +286,19 @@ Page({
// 删除图片 // 删除图片
deleteImg: function (e) { deleteImg: function (e) {
let imgs = this.data.inspectContent[e.currentTarget.dataset.item].imgs; const { deviceidx, itemidx, index } = e.currentTarget.dataset;
const index = e.currentTarget.dataset.index; let imgs = this.data.inspectContentArr[deviceidx].checkItems[itemidx].imgs;
imgs.splice(index, 1); imgs.splice(index, 1);
const inspectContent = this.data.inspectContent; let nextInspectContentArr = this.data.inspectContentArr;
inspectContent[e.currentTarget.dataset.item].imgs = imgs; nextInspectContentArr[deviceidx].checkItems[itemidx].imgs = imgs;
this.setData({ this.setData({ inspectContentArr: nextInspectContentArr })
inspectContent
});
}, },
// 预览图片 // 预览图片
previewImg: function (e) { previewImg: function (e) {
// 获取当前图片的下标 const { deviceidx, itemidx, index } = e.currentTarget.dataset;
const index = e.currentTarget.dataset.index;
// 所有图片 // 所有图片
const imgs = this.data.inspectContent[e.currentTarget.dataset.item].imgs; const imgs = this.data.inspectContentArr[deviceidx].checkItems[itemidx].imgs;
const newImgs = imgs.map(i => this.data.imgUrl + i); const newImgs = imgs.map(i => this.data.imgUrl + i);
wx.previewImage({ wx.previewImage({
// 当前显示图片 // 当前显示图片
@ -293,7 +308,7 @@ Page({
}) })
}, },
bindCancel () { bindCancel() {
if (this.data.scenePointId) { if (this.data.scenePointId) {
wx.switchTab({ url: '/pages/index/index' }) wx.switchTab({ url: '/pages/index/index' })
} else { } else {
@ -307,12 +322,11 @@ Page({
if (that.data.isCommitting) { return } if (that.data.isCommitting) { return }
let { let {
itemData, itemData,
inspectContent, inspectContentArr,
dataList, dataList,
address address
} = that.data; } = that.data;
let alarm = false; let alarm = false;
if (!address) { if (!address) {
wx.showToast({ wx.showToast({
title: '请获取当前位置', title: '请获取当前位置',
@ -321,9 +335,10 @@ Page({
}) })
return; return;
} }
let reportArr = inspectContentArr.map(d => ({ ...d, alarm: false }));
for (const item in inspectContent) { for (const [index, device] of inspectContentArr.entries()) {
if (inspectContent[item].isNormal === null) { for (const item of device.checkItems) {
if (item.isNormal === null) {
wx.showToast({ wx.showToast({
title: '请填写完整', title: '请填写完整',
icon: 'none', icon: 'none',
@ -331,7 +346,7 @@ Page({
}) })
return; return;
} }
if ((!inspectContent[item].isNormal) && (!inspectContent[item].level || !inspectContent[item].msgInp)) { if ((!item.isNormal) && (!item.level || !item.msgInp)) {
wx.showToast({ wx.showToast({
title: '异常项必须输入巡查详情和选择严重等级', title: '异常项必须输入巡查详情和选择严重等级',
icon: 'none', icon: 'none',
@ -339,8 +354,10 @@ Page({
}) })
return; return;
} }
if (inspectContent[item].isNormal === false) { if (item.isNormal === false) {
alarm = true; alarm = true; // 巡检记录异常
reportArr[index].alarm = true; // 设备异常
}
} }
} }
const { id, name, departmentId, deptName } = wx.getStorageSync('userInfo'); const { id, name, departmentId, deptName } = wx.getStorageSync('userInfo');
@ -353,7 +370,7 @@ Page({
project: dataList.project, project: dataList.project,
frequency: dataList.frequency, frequency: dataList.frequency,
itemData: itemData, itemData: itemData,
inspectContent, inspectContent: reportArr,
address: address address: address
}, },
alarm, alarm,
@ -377,7 +394,7 @@ Page({
/** /**
* 生命周期函数--监听页面加载 * 生命周期函数--监听页面加载
*/ */
onLoad (options) { onLoad(options) {
const that = this; const that = this;
const scenePointId = options.scene; const scenePointId = options.scene;
if (scenePointId) { // 扫小程序码进入 if (scenePointId) { // 扫小程序码进入
@ -398,56 +415,56 @@ Page({
dataList, dataList,
itemData itemData
}) })
that.getPatrolTemplate(dataList.templateId); that.getPatrolTemplate(dataList.templateId, itemData.pointDevices);
} }
}, },
/** /**
* 生命周期函数--监听页面初次渲染完成 * 生命周期函数--监听页面初次渲染完成
*/ */
onReady () { onReady() {
}, },
/** /**
* 生命周期函数--监听页面显示 * 生命周期函数--监听页面显示
*/ */
onShow () { onShow() {
}, },
/** /**
* 生命周期函数--监听页面隐藏 * 生命周期函数--监听页面隐藏
*/ */
onHide () { onHide() {
}, },
/** /**
* 生命周期函数--监听页面卸载 * 生命周期函数--监听页面卸载
*/ */
onUnload () { onUnload() {
}, },
/** /**
* 页面相关事件处理函数--监听用户下拉动作 * 页面相关事件处理函数--监听用户下拉动作
*/ */
onPullDownRefresh () { onPullDownRefresh() {
}, },
/** /**
* 页面上拉触底事件的处理函数 * 页面上拉触底事件的处理函数
*/ */
onReachBottom () { onReachBottom() {
}, },
/** /**
* 用户点击右上角分享 * 用户点击右上角分享
*/ */
onShareAppMessage () { onShareAppMessage() {
} }
}) })

121
weapp/package/inspectionInput/inspectionInput.wxml

@ -1,8 +1,24 @@
<!-- package/inspectionInput/inspectionInput.wxml --> <!-- package/inspectionInput/inspectionInput.wxml -->
<view class="popBox"> <view class="popBox">
<view wx:if="{{planList}}"> <view wx:if="{{planList}}">
<t-cell class="block" title="选择巡检计划" arrow hover note="{{planListText}}" bind:click="onPlanListPicker" /> <t-cell
<t-picker visible="{{planListVisible}}" value="{{planListValue}}" data-key="planList" title="选择巡检计划" cancelBtn="取消" confirmBtn="确认" bindchange="onPickerChange" bindcancel="onPickerCancel"> class="block"
title="选择巡检计划"
arrow
hover
note="{{planListText}}"
bind:click="onPlanListPicker"
/>
<t-picker
visible="{{planListVisible}}"
value="{{planListValue}}"
data-key="planList"
title="选择巡检计划"
cancelBtn="取消"
confirmBtn="确认"
bindchange="onPickerChange"
bindcancel="onPickerCancel"
>
<t-picker-item options="{{planList}}" format></t-picker-item> <t-picker-item options="{{planList}}" format></t-picker-item>
</t-picker> </t-picker>
</view> </view>
@ -24,36 +40,113 @@
</view> </view>
</view> </view>
<view class="divider" /> <view class="divider" />
<view wx:for="{{checkItems}}" wx:key="id">
<!-- 渲染巡检内容 -->
<view
wx:for="{{inspectContentArr}}"
wx:key="deviceId"
wx:for-item="device"
wx:for-index="deviceidx"
>
<view wx:if="{{device.deviceName}}" class="flex flex-start" style="height: 40px"
>{{device.deviceName}}</view
>
<view wx:for="{{device.checkItems}}" wx:key="id" wx:for-index="itemidx">
<view class="flex-between"> <view class="flex-between">
<view class="item-name">{{item.name}}:</view> <view class="item-name">{{item.name}}:</view>
<van-radio-group style="padding:10px 15px;" data-item="{{item.name}}" bindchange="handleChangeTwo"> <van-radio-group
<van-radio style="margin-right: 20px;" class="radio-text" color="#1979ff" name="normal">正常</van-radio> style="padding:10px 15px;"
data-deviceidx="{{deviceidx}}"
data-itemidx="{{itemidx}}"
bindchange="handleChangeTwo"
>
<van-radio style="margin-right: 20px;" class="radio-text" color="#1979ff" name="normal"
>正常</van-radio
>
<van-radio class="radio-text" checked-color="#CC0000" name="abnormal">异常</van-radio> <van-radio class="radio-text" checked-color="#CC0000" name="abnormal">异常</van-radio>
</van-radio-group> </van-radio-group>
</view> </view>
<view class="divider" /> <view class="divider" />
<van-radio-group class="flex-end" style="padding:10px 15px;" data-item="{{item.name}}" bindchange="handleChangeThree" wx:if="{{inspectContent[item.name].isNormal === false}}"> <van-radio-group
<van-radio style="margin-right: 20px;" class="radio-text" checked-color="#FF9900" name="轻微">轻微</van-radio> class="flex-end"
<van-radio style="margin-right: 20px;" class="radio-text" checked-color="#FF3300" name="中度">中度</van-radio> style="padding:10px 15px;"
data-deviceidx="{{deviceidx}}"
data-itemidx="{{itemidx}}"
bindchange="handleChangeThree"
wx:if="{{item.isNormal === false}}"
>
<van-radio
style="margin-right: 20px;"
class="radio-text"
checked-color="#FF9900"
name="轻微"
>轻微</van-radio
>
<van-radio
style="margin-right: 20px;"
class="radio-text"
checked-color="#FF3300"
name="中度"
>中度</van-radio
>
<van-radio class="radio-text" checked-color="#990000" name="严重">严重</van-radio> <van-radio class="radio-text" checked-color="#990000" name="严重">严重</van-radio>
</van-radio-group> </van-radio-group>
<textarea class="textarea" placeholder="请输入巡查详情" maxlength="-1" wx:if="{{inspectContent[item.name].isNormal === false}}" data-item="{{item.name}}" bindinput="bindInput"></textarea> <textarea
<view class="weui-uploader" style="padding: 20rpx 30rpx;overflow-y:scroll;" wx:if="{{inspectContent[item.name].isNormal === false}}"> class="textarea"
placeholder="请输入巡查详情"
maxlength="-1"
wx:if="{{item.isNormal === false}}"
data-deviceidx="{{deviceidx}}"
data-itemidx="{{itemidx}}"
bindinput="bindInput"
/>
<view
class="weui-uploader"
style="padding: 20rpx 30rpx;overflow-y:scroll;"
wx:if="{{item.isNormal === false}}"
>
<view class="img-v weui-uploader__bd" style="overflow:hidden;"> <view class="img-v weui-uploader__bd" style="overflow:hidden;">
<view class='pic' wx:for="{{inspectContent[item.name].imgs}}" wx:for-item="img" wx:key="*this"> <view
<image class='weui-uploader__img showImg' src="{{imgUrl + img}}" data-index="{{index}}" data-item="{{item.name}}" mode="aspectFill" bindtap="previewImg"> class="pic"
<icon type='cancel' class="delete-btn" data-index="{{index}}" data-item="{{item.name}}" catchtap="deleteImg"></icon> wx:for="{{item.imgs}}"
wx:for-item="img"
wx:key="*this"
>
<image
class="weui-uploader__img showImg"
src="{{imgUrl + img}}"
data-index="{{index}}"
data-deviceidx="{{deviceidx}}"
data-itemidx="{{itemidx}}"
mode="aspectFill"
bindtap="previewImg"
>
<icon
type="cancel"
class="delete-btn"
data-index="{{index}}"
data-deviceidx="{{deviceidx}}"
data-itemidx="{{itemidx}}"
catchtap="deleteImg"
/>
</image> </image>
</view> </view>
<!-- 用来提示用户上传图片 --> <!-- 用来提示用户上传图片 -->
<view class="weui-uploader__input-box pic" data-item="{{item.name}}" bindtap="chooseImg"> <view
class="weui-uploader__input-box pic"
data-item="{{item.name}}"
data-deviceidx="{{deviceidx}}"
data-itemidx="{{itemidx}}"
bindtap="chooseImg"
>
<image class="upload" src="/images/upload.png" /> <image class="upload" src="/images/upload.png" />
</view> </view>
</view> </view>
</view> </view>
<view class="divider" /> <view class="divider" />
</view> </view>
</view>
<view class="btnBox"> <view class="btnBox">
<view class="cancel" bindtap="bindCancel">取消</view> <view class="cancel" bindtap="bindCancel">取消</view>
<view class="submit" bindtap="addPatrolRecord">提交</view> <view class="submit" bindtap="addPatrolRecord">提交</view>

39
weapp/package/polling/inspectionRecordDetail/inspectionRecordDetail.js

@ -1,4 +1,5 @@
// package/inspectionRecord/inspectionRecordDetail/inspectionRecordDetail.js // package/inspectionRecord/inspectionRecordDetail/inspectionRecordDetail.js
const moment = require("../../../utils/moment");
Page({ Page({
/** /**
@ -7,21 +8,34 @@ Page({
data: { data: {
dataList: '', dataList: '',
imgUrl: getApp().globalData.imgUrl, imgUrl: getApp().globalData.imgUrl,
activeValues: [] activeValues: [],
multiActiveValues: [],
// 1.1 版本(2023-10-18)之前 dataList.points.inspectContent 为 Object, 1.1版本因增加点位关联设备改为 Array
priorToV1_1: false,
}, },
handleChange(e) { handleCollapseChange(e) {
this.setData({ this.setData({ activeValues: e.detail });
activeValues: e.detail, },
});
handleMultiCollapseChange(e) {
const { deviceidx } = e.currentTarget.dataset;
let nextMultiActiveValues = this.data.multiActiveValues;
nextMultiActiveValues[deviceidx] = e.detail;
this.setData({ multiActiveValues: nextMultiActiveValues });
}, },
// 预览图片 // 预览图片
previewImg: function (e) { previewImg: function (e) {
// 获取当前图片的下标 // 获取当前图片的下标
const index = e.currentTarget.dataset.index; const { index, deviceidx, itemidx } = e.currentTarget.dataset;
// 所有图片 // 所有图片
const imgs = this.data.dataList.points.inspectContent[e.currentTarget.dataset.key].imgs; let imgs = []
if (this.data.priorToV1_1) {
imgs = this.data.dataList.points.inspectContent[e.currentTarget.dataset.key].imgs;
} else {
imgs = this.data.dataList.points.inspectContent[deviceidx].checkItems[itemidx].imgs;
}
const newImgs = imgs.map(i => this.data.imgUrl + i); const newImgs = imgs.map(i => this.data.imgUrl + i);
wx.previewImage({ wx.previewImage({
current: newImgs[index], current: newImgs[index],
@ -35,7 +49,16 @@ Page({
onLoad(options) { onLoad(options) {
const that = this; const that = this;
const data = JSON.parse(decodeURIComponent(options.data)) const data = JSON.parse(decodeURIComponent(options.data))
that.setData({ dataList: data }) const priorToV1_1 = moment(data.inspectionTime).isBefore(moment('2023-10-18'))
let nextMultiActiveValues = []
if (!priorToV1_1) {
nextMultiActiveValues = data.points.inspectContent.map(c => ([]))
}
that.setData({
dataList: data,
priorToV1_1,
nextMultiActiveValues,
})
}, },
/** /**

110
weapp/package/polling/inspectionRecordDetail/inspectionRecordDetail.wxml

@ -7,35 +7,119 @@
<van-cell size="large" title="巡检频次:" value="{{dataList.points.frequency}}" /> <van-cell size="large" title="巡检频次:" value="{{dataList.points.frequency}}" />
<van-cell size="large" title="上次巡检日期:" value="{{dataList.lastInspectionTime}}" /> <van-cell size="large" title="上次巡检日期:" value="{{dataList.lastInspectionTime}}" />
<van-cell size="large" title="本次巡检日期:" value="{{dataList.inspectionTime}}" /> <van-cell size="large" title="本次巡检日期:" value="{{dataList.inspectionTime}}" />
<van-cell size="large" title="巡检结果:" value="{{dataList.alarm ? '异常' : '正常'}}" style="--cell-value-color: {{dataList.alarm ? '#FF0000' : '#006BE3'}}" /> <van-cell
size="large"
title="巡检结果:"
value="{{dataList.alarm ? '异常' : '正常'}}"
style="--cell-value-color: {{dataList.alarm ? '#FF0000' : '#006BE3'}}"
/>
<van-cell size="large" title="当前点位:" value="{{dataList.points.itemData.name}}" /> <van-cell size="large" title="当前点位:" value="{{dataList.points.itemData.name}}" />
<van-cell size="large" title="当前位置:" value="{{dataList.points.address}}" title-width="80px" /> <van-cell
size="large"
title="当前位置:"
value="{{dataList.points.address}}"
title-width="80px"
/>
</van-cell-group> </van-cell-group>
<view style="margin-top: 16px"> <view style="margin-top: 16px">
<van-collapse value="{{ activeValues }}" bind:change="handleChange"> <block wx:if="{{ priorToV1_1 }}">
<view wx:for="{{dataList.points.inspectContent}}" wx:key="key" wx:for-index="key" wx:for-item="value"> <van-collapse value="{{ activeValues }}" bind:change="handleCollapseChange">
<view
wx:for="{{dataList.points.inspectContent}}"
wx:key="key"
wx:for-index="key"
wx:for-item="value"
>
<van-collapse-item <van-collapse-item
title="{{key}}" title="{{key}}"
value="{{value.isNormal ? '正常' : '异常'}}" value="{{value.isNormal ? '正常' : '异常'}}"
style="--cell-value-color: {{value.isNormal ? '#006BE3' : '#FF0000'}}; --collapse-item-title-disabled-color: #323233" style="--cell-value-color: {{value.isNormal ? '#006BE3' : '#FF0000'}}; --collapse-item-title-disabled-color: #323233"
disabled="{{value.isNormal}}" disabled="{{value.isNormal}}"
> >
<view class='content'> <view class="content">
<view class='title'>描述:</view> <view class="title">描述:</view>
<view class='value'>{{value.msgInp || '--'}}</view> <view class="value">{{value.msgInp || '--'}}</view>
</view> </view>
<view class='content'> <view class="content">
<view class='title'>异常等级:</view> <view class="title">异常等级:</view>
<view class='value'>{{value.level || '--'}}</view> <view class="value">{{value.level || '--'}}</view>
</view> </view>
<view class='content'>巡检图片:</view> <view class="content">巡检图片:</view>
<view class="img-v weui-uploader__bd" style="overflow:hidden;"> <view class="img-v weui-uploader__bd" style="overflow:hidden;">
<view class='pic' wx:for="{{value.imgs}}" wx:for-item="item" wx:key="*this"> <view class="pic" wx:for="{{value.imgs}}" wx:for-item="item" wx:key="*this">
<image class='weui-uploader__img showImg' src="{{imgUrl + item}}" data-img="{{imgUrl + item}}" data-index="{{index}}" data-key="{{key}}" mode="aspectFill" bindtap="previewImg"></image> <image
class="weui-uploader__img showImg"
src="{{imgUrl + item}}"
data-img="{{imgUrl + item}}"
data-index="{{index}}"
data-key="{{key}}"
mode="aspectFill"
bindtap="previewImg"
></image>
</view> </view>
</view> </view>
</van-collapse-item> </van-collapse-item>
</view> </view>
</van-collapse> </van-collapse>
</block>
<block wx:else>
<view
wx:for="{{dataList.points.inspectContent}}"
wx:for-item="device"
wx:for-index="deviceidx"
style="margin-top: 16px"
>
<van-collapse
value="{{ multiActiveValues[deviceidx] }}"
data-deviceidx="{{deviceidx}}"
bind:change="handleMultiCollapseChange"
>
<view
wx:if="{{device.deviceName}}"
class="flex flex-between"
style="padding: 8px 16px 0; background: #fff"
>
<view>{{device.deviceName}}</view>
<view style="color: {{device.alarm ? '#FF0000' : '#006BE3'}};">
{{device.alarm ? '异常': '正常'}}
</view>
</view>
<view wx:for="{{device.checkItems}}" wx:for-index="itemidx">
<van-collapse-item
title="{{item.name}}"
value="{{item.isNormal ? '正常' : '异常'}}"
style="--cell-value-color: {{item.isNormal ? '#006BE3' : '#FF0000'}}; --collapse-item-title-disabled-color: #323233"
disabled="{{item.isNormal}}"
>
<view class="content">
<view class="title">描述:</view>
<view class="value">{{item.msgInp || '--'}}</view>
</view>
<view class="content">
<view class="title">异常等级:</view>
<view class="value">{{item.level || '--'}}</view>
</view>
<view class="content">巡检图片:</view>
<view class="img-v weui-uploader__bd" style="overflow:hidden;">
<view class="pic" wx:for="{{item.imgs}}" wx:for-item="img" wx:key="*this">
<image
class="weui-uploader__img showImg"
src="{{imgUrl + img}}"
data-img="{{imgUrl + img}}"
data-index="{{index}}"
data-key="{{key}}"
data-deviceidx="{{deviceidx}}"
data-itemidx="{{itemidx}}"
mode="aspectFill"
bindtap="previewImg"
></image>
</view>
</view>
</van-collapse-item>
</view>
</van-collapse>
</view>
</block>
</view> </view>
</view> </view>

7
weapp/package/polling/polling.js

@ -48,7 +48,12 @@ Page({
}, },
recordHidden: true, recordHidden: true,
}, },
jumpToReport (options) {
const key='主动上报'
wx.navigateTo({
url: `/package/report/report?key=${key}`,
})
},
// 顶部tab切换 // 顶部tab切换
onChange(event) { onChange(event) {
this.setData({ this.setData({

4
weapp/package/polling/polling.json

@ -9,6 +9,8 @@
"van-tabs": "@vant/weapp/tabs/index", "van-tabs": "@vant/weapp/tabs/index",
"van-dropdown-menu": "@vant/weapp/dropdown-menu/index", "van-dropdown-menu": "@vant/weapp/dropdown-menu/index",
"van-dropdown-item": "@vant/weapp/dropdown-item/index", "van-dropdown-item": "@vant/weapp/dropdown-item/index",
"van-datetime-picker": "@vant/weapp/datetime-picker/index" "van-datetime-picker": "@vant/weapp/datetime-picker/index",
"van-icon": "@vant/weapp/icon/index"
} }
} }

5
weapp/package/polling/polling.wxml

@ -79,5 +79,10 @@
</view> </view>
</view> </view>
</van-tab> </van-tab>
<van-tab title='主动上报' >
<view>
<van-button type="info" round bindtap="jumpToReport"><van-icon name="plus" /></van-button>
</view>
</van-tab>
</van-tabs> </van-tabs>
</view> </view>

543
weapp/package/report/report.js

@ -0,0 +1,543 @@
// package/report/report.js
import { getPointList,getPatrolTemplate,getTemplates,reportQuest,getPatrolPlan } from "../../utils/getApiUrl";
import {Request} from "../../common";
const moment = require("../../utils/moment");
Page({
data: {
isPlanState: false,
structList: [{
id: 0,
name: '指挥中心'
}, {
id: 1,
name: '管廊'
},
{
id: 2,
name: '电梯系统'
}, {
id: 3,
name: '供配电系统'
}, {
id: 4,
name: '燃气仓'
},
{
id: 5,
name: '给水仓'
}, {
id: 6,
name: '防雷与接地系统'
}, {
id: 7,
name: '电气仓'
},
{
id: 8,
name: '高压电力仓'
}, {
id: 9,
name: '安防系统'
}
],
data:[],//巡检计划的数据(包括点位,设备等等)
structListIndex: undefined,//结构物id
pointList:[],//点位列表
pointIndex:undefined,//点位索引
devicesList:[],//设备列表
dataList: '', // 当前巡检计划
patrolTemplate:[],//巡检模板
templateData:[],//巡检模板总数居
// curPlanTemplateId:0,//当前巡检计划的模板id
patrolTemplateIndex:undefined,//巡检模板索引
itemData: '', // 点位
address: '', // 当前位置
imgUrl: getApp().globalData.imgUrl,
checkItems: [], // 检查项
inspectContentArr: [], // 巡检内容
isCommitting: false,
planList: null, // 巡检计划列表
structListVisible: true,
scenePointId: null, // 当前点位id
},
//巡检计划
getPatrolPlan: function (scenePointId) {
let that = this;
wx.showLoading({
title: '加载中',
})
Request.get(getPatrolPlan()).then(res => {
wx.hideLoading();
let pointPlan = res.rows.filter(plan => {
for (const point of plan.points) {
if (point.id == scenePointId) {
return true;
}
}
return false;
}).map(p => ({
label: p.name,
value: p.name,
...p
}))
that.setData({
planList: pointPlan
})
})
},
//点位改变函数
pointChange(e){
const that = this
that.getPatrolPlan(that.data.data[e.detail.value].id)
that.setData({
inspectContentArr:[],
pointIndex:e.detail.value,
devicesList:that.data.data[e.detail.value].devices,
scenePointId:that.data.data[e.detail.value].id
})
},
// 预览图片
previewImg: function (e) {
const { deviceidx, itemidx, index } = e.currentTarget.dataset;
// 所有图片
const imgs = this.data.inspectContentArr[deviceidx].checkItems[itemidx].imgs;
const newImgs = imgs.map(i => this.data.imgUrl + i);
wx.previewImage({
// 当前显示图片
current: newImgs[index],
// 所有图片
urls: newImgs
})
},
//结构物改变函数
structChange(event) {
const that = this
that.setData({
structListIndex: event.detail.value,
// isPlanState: true,
pointList:[],//选择结构物后先置空先前的点位列表
})
const keywords=that.data?.structList[event.detail.value]?.name
const query={keywords}
Request.get(getTemplates(query)).then(res=>{
if(res){
const rlst=res.map(item=>item.patrolTemplate)
that.setData({patrolTemplate:rlst,templateData:res})
}else{
}
})
Request.get(getPointList(query)).then(res => {
if(res){
const pointList=res.map(item=>{
return {
id:item.id,
name:item.name
}
})
that.setData({pointList:pointList,data:res})
}else {
wx.hideLoading();
}
})
},
//整理设备和检查项
getPatrolTemplate(templateId,pointDevices=[]) {
const that=this
Request.get(getPatrolTemplate(templateId)).then(res => {
const checkItems = res.rows[0].checkItems;
let inspectContentArr = [];
// 有绑定设备的点位,每个设备都要检查各个检查项
if (pointDevices.length) {
pointDevices.forEach(device => {
inspectContentArr.push({
deviceName: device.name,
deviceId: device.id,
checkItems: checkItems.map(c => ({
id: `${device.id}-${c.id}`,
name: c.name,
isNormal: null,
msgInp: null,
level: null,
imgs: [],
}))
})
});
} else {
inspectContentArr.push({
checkItems: checkItems.map(c => ({
id: c.id,
name: c.name,
isNormal: null,
msgInp: null,
level: null,
imgs: [],
}))
})
}
this.setData({
checkItems,
inspectContentArr: inspectContentArr,
})
})
},
//选择异常或者正常
handleChangeTwo(e) {
const isNormal = e.detail === 'normal';
const { deviceidx, itemidx } = e.currentTarget.dataset;
let nextInspectContentArr = this.data.inspectContentArr;
nextInspectContentArr[deviceidx].checkItems[itemidx].isNormal = isNormal;
if (isNormal) { // 清除异常数据
nextInspectContentArr[deviceidx].checkItems[itemidx].msgInp = null;
nextInspectContentArr[deviceidx].checkItems[itemidx].level = null;
nextInspectContentArr[deviceidx].checkItems[itemidx].imgs = [];
}
this.setData({ inspectContentArr: nextInspectContentArr })
},
//返回前一页
bindCancel() {
wx.navigateBack();
},
// 开始巡检录入
addPatrolRecord: function () {
const that = this;
if (that.data.isCommitting) { return }
let {
patrolTemplate,
patrolTemplateIndex,
structListIndex,
pointIndex,
pointList,
inspectContentArr,
dataList,
address,
data,
templateData
} = that.data;
let alarm = false;
if (!address) {
wx.showToast({
title: '请获取当前位置',
icon: 'none',
duration: 1500
})
return;
}
if (!structListIndex) {
wx.showToast({
title: '请选择结构物',
icon: 'none',
duration: 1500
})
return;
}
if (!patrolTemplateIndex) {
wx.showToast({
title: '请选择模板',
icon: 'none',
duration: 1500
})
return;
}
if (!pointIndex) {
wx.showToast({
title: '请选择点位',
icon: 'none',
duration: 1500
})
return;
}
let reportArr = inspectContentArr.map(d => ({ ...d, alarm: false }));
for (const [index, device] of inspectContentArr.entries()) {
for (const item of device.checkItems) {
if (item.isNormal === null) {
wx.showToast({
title: '请填写完整',
icon: 'none',
duration: 1500
})
return;
}
if ((!item.isNormal) && (!item.level || !item.msgInp)) {
wx.showToast({
title: '异常项必须输入巡查详情和选择严重等级',
icon: 'none',
duration: 2000
})
return;
}
if (item.isNormal === false) {
alarm = true; // 巡检记录异常
reportArr[index].alarm = true; // 设备异常
}
}
}
const { id, name, departmentId, deptName } = wx.getStorageSync('userInfo');
const curPlan = that.data.planList.find(item=>item.id=patrolTemplate[patrolTemplateIndex].id)
const nextItemData = curPlan.points.find(p => p.id == this.data.scenePointId)
const aboutSend=templateData.find(item=>item.patrolTemplate.id===patrolTemplate[patrolTemplateIndex].id)
let datas = {
patrolPlanId: -1,
pointId: pointList[pointIndex].id,
inspectionTime: moment().format('YYYY-MM-DD HH:mm:ss'),
points: {
user: { id, name, department: { id: departmentId, name: deptName } },
project: aboutSend.project,
frequency: aboutSend.frequency,
itemData:nextItemData,
inspectContent: reportArr,
address: address
},
alarm,
projectId: aboutSend.project.id
}
wx.showLoading({ title: '提交中...' });
that.setData({ isCommitting: true });
Request.post(reportQuest(), datas).then(res => {
wx.hideLoading();
that.setData({ isCommitting: false });
wx.showToast({
title: '提交成功',
icon: 'success'
})
setTimeout(() => {
that.bindCancel();
}, 1500)
})
},
//多张图片上传
uploadImg: function (data, deviceidx, itemidx) {
wx.showLoading({
title: '上传中...',
mask: true,
})
let that = this,
i = data.i ? data.i : 0,
success = data.success ? data.success : 0,
fail = data.fail ? data.fail : 0;
let imgs = that.data.inspectContentArr[deviceidx].checkItems[itemidx].imgs;
wx.uploadFile({
url: data.url,
filePath: data.path[i],
name: 'file',
success: (resp) => {
wx.hideLoading();
success++;
let str = JSON.parse(resp.data) // 返回的结果,可能不同项目结果不一样
str = str.uploaded
if (imgs.length >= 20) {
let nextInspectContentArr = that.data.inspectContentArr;
nextInspectContentArr[deviceidx].checkItems[itemidx].imgs = imgs;
that.setData({ inspectContentArr: nextInspectContentArr });
return false;
} else {
imgs.push(str);
let nextInspectContentArr = that.data.inspectContentArr;
nextInspectContentArr[deviceidx].checkItems[itemidx].imgs = imgs;
that.setData({ inspectContentArr: nextInspectContentArr });
}
},
fail: (res) => {
fail++;
console.log('fail:' + i + "fail:" + fail);
},
complete: () => {
i++;
if (i == data.path.length) { // 当图片传完时,停止调用
console.log('执行完毕');
console.log('成功:' + success + " 失败:" + fail);
} else { // 若图片还没有传完,则继续调用函数
data.i = i;
data.success = success;
data.fail = fail;
that.uploadImg(data, deviceidx, itemidx); // 递归,回调自己
}
}
});
},
// 上传图片
chooseImg: function (e) { // 这里是选取图片的方法
const { deviceidx, itemidx } = e.currentTarget.dataset;
const that = this;
let pics = [];
const detailPics = that.data.inspectContentArr[deviceidx].checkItems[itemidx].imgs;
if (detailPics.length >= 20) {
wx.showToast({
title: '最多选择20张图片上传',
icon: 'none'
});
return;
}
wx.chooseMedia({
count: 20, // 基础库2.25.0前,最多可支持9个文件,2.25.0及以后最多可支持20个文件
mediaType: ['image'], // 文件类型
sizeType: ['original', 'compressed'], // original 原图,compressed 压缩图,默认二者都有
sourceType: ['album', 'camera'], // album 从相册选图,camera 使用相机,默认二者都有
success: function (res) {
const imgs = res.tempFiles;
for (let i = 0; i < imgs.length; i++) {
if (res.tempFiles[i].size > 15728640) {
wx.showToast({ title: '图片大于15M,不可上传', icon: 'none' });
return;
}
const fileNameArr = res.tempFiles[i].tempFilePath.split('.');
const extension = res.tempFiles[i].tempFilePath.split('.')[fileNameArr.length - 1];
if (extension !== 'jpg' && extension !== 'png' && extension !== 'jpeg') {
wx.showToast({ title: '只能上传jpg、jpeg、png格式的图片', icon: 'none' });
return;
}
pics.push(imgs[i].tempFilePath)
}
that.uploadImg({
url: getApp().globalData.webUrl + '_upload/attachments/project', // 图片上传的接口
path: pics, // 选取的图片的地址数组
}, deviceidx, itemidx);
},
})
},
// 巡查详情
bindInput: function (e) {
const { deviceidx, itemidx } = e.currentTarget.dataset;
let nextInspectContentArr = this.data.inspectContentArr;
nextInspectContentArr[deviceidx].checkItems[itemidx].msgInp = e.detail.value;
this.setData({ inspectContentArr: nextInspectContentArr })
},
handleChangeThree(e) {
const { deviceidx, itemidx } = e.currentTarget.dataset;
let nextInspectContentArr = this.data.inspectContentArr;
nextInspectContentArr[deviceidx].checkItems[itemidx].level = e.detail;
this.setData({ inspectContentArr: nextInspectContentArr })
},
//巡检模板改变
patrolTemplateChange(e){
const that=this
that.getPatrolTemplate(that.data.patrolTemplate[e.detail.value].id,that.data.devicesList)
that.setData({
patrolTemplateIndex:e.detail.value
})
},
bindShowMsg() {
this.setData({
select: !this.data.select
})
},
mySelect(e) {
var name = e.currentTarget.dataset.name
this.setData({
tihuoWay: name,
select: false
})
},
/**
* 页面的初始数据
*/
// data: {
// },
/**
* 生命周期函数--监听页面加载
*/
onLoad(options) {
const that=this
wx.setNavigationBarTitle({
title: options.key,
});
},
onStructListPicker() {
this.setData({
structListVisible: true
});
},
// 获取当前位置
selfLocation() {
const that = this
wx.showLoading({
title: '定位中',
mask: true,
});
wx.getLocation({
type: 'wgs84',
success: (res) => {
wx.request({
url: `https://apis.map.qq.com/ws/geocoder/v1/?location=${res.latitude},${res.longitude}&key=${getApp().globalData.key}`,
success: function (res) {
wx.hideLoading();
// 根据自己项目需求获取res内容
that.setData({
address: res.data.result.address
})
}
})
},
fail: (res) => {
wx.hideLoading();
wx.showToast({
title: res.errMsg,
icon: 'none',
duration: 1000
});
}
});
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady() {
},
/**
* 生命周期函数--监听页面显示
*/
onShow() {
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide() {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload() {
},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh() {
},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom() {
},
/**
* 用户点击右上角分享
*/
onShareAppMessage() {
}
})

25
weapp/package/report/report.json

@ -0,0 +1,25 @@
{
"navigationBarBackgroundColor": "#1979ff",
"navigationBarTextStyle": "white",
"navigationBarTitleText": "发现问题",
"enablePullDownRefresh": false,
"componentFramework": "glass-easel",
"usingComponents": {
"van-button": "@vant/weapp/button/index",
"van-field": "@vant/weapp/field/index",
"van-cell": "@vant/weapp/cell/index",
"van-cell-group": "@vant/weapp/cell-group/index",
"van-picker": "@vant/weapp/picker/index",
"van-popup": "@vant/weapp/popup/index",
"van-icon": "@vant/weapp/icon/index",
"van-collapse": "@vant/weapp/collapse/index",
"van-collapse-item": "@vant/weapp/collapse-item/index",
"van-divider": "@vant/weapp/divider/index",
"t-cell-group": "tdesign-miniprogram/cell-group/cell-group",
"t-cell": "tdesign-miniprogram/cell/cell",
"t-picker": "tdesign-miniprogram/picker/picker",
"t-picker-item": "tdesign-miniprogram/picker-item/picker-item",
"van-radio": "@vant/weapp/radio/index",
"van-radio-group": "@vant/weapp/radio-group/index"
}
}

96
weapp/package/report/report.wxml

@ -0,0 +1,96 @@
<view class="popBox">
<view>
<van-cell-group class="mission-card">
<van-cell>
<view style="display:flex">
<view class="fs-cell-title" style="">结构物:</view>
<picker style="width:100%;text-align:left" bindchange="structChange" data-type='jiegouwu' value="{{0}}" range="{{structList}}" range-key="name">
<view class="fs-cell-content" style="width:100%">
{{structListIndex||structListIndex==0?structList[structListIndex].name:'请选择'}}
<van-icon name="arrow" style="float:right;position:relative; top:4px" />
</view>
</picker>
</view>
</van-cell>
<van-cell>
<view style="display:flex">
<view class="fs-cell-title" style="">当前点位:</view>
<picker style="width:100%;text-align:left" bindchange="pointChange" data-type='point' range="{{pointList}}" range-key="name">
<view class="fs-cell-content" style="width:100%">
{{pointIndex||pointIndex==0?pointList[pointIndex].name:'请选择'}}
<van-icon name="arrow" style="float:right;position:relative; top:4px" />
</view>
</picker>
</view>
</van-cell>
<van-cell>
<view style="overflow: hidden;">
<view style="float: left;" class="fs-cell-title">当前位置:</view>
<view style="float:right;" class="fs-cell-content" wx:if="{{address}}">
{{address}}
</view>
<view style="float:right;">
<image wx:if="{{!address}}" class="icon" src="../../images/landmark.svg" />
<view style="display: inline-block;" bindtap="selfLocation" wx:if="{{!address}}">
点击获取
</view>
</view>
</view>
</van-cell>
<van-cell>
<view style="display:flex">
<view class="fs-cell-title">巡检模板:</view>
<picker style="width:100%;text-align:left" bindchange="patrolTemplateChange" data-type='template' value="{{0}}" range="{{patrolTemplate}}" range-key="name">
<view class="fs-cell-content" style="width:100%">
{{patrolTemplateIndex||patrolTemplateIndex==0?patrolTemplate[patrolTemplateIndex].name:'请选择'}}
<van-icon name="arrow" style="float:right;position:relative; top:4px" />
</view>
</picker>
</view>
</van-cell>
</van-cell-group>
</view>
<!-- 渲染巡检内容 -->
<view wx:for="{{inspectContentArr}}" wx:key="id" wx:for-item="device" wx:for-index="deviceidx" >
<view wx:if="{{device.deviceName}}" class="flex flex-start" style="height: 40px">{{device.deviceName}}</view>
<view wx:for="{{device.checkItems}}" wx:key="id" wx:for-index="itemidx">
<view class="flex-between">
<view class="item-name">{{item.name}}:</view>
<van-radio-group style="padding:10px 15px;" data-deviceidx="{{deviceidx}}" data-itemidx="{{itemidx}}" bindchange="handleChangeTwo">
<van-radio style="margin-right: 20px;" class="radio-text" color="#1979ff" name="normal">正常</van-radio>
<van-radio class="radio-text" checked-color="#CC0000" name="abnormal">异常</van-radio>
</van-radio-group>
</view>
<view class="divider" />
<van-radio-group class="flex-end" style="padding:10px 15px;" data-deviceidx="{{deviceidx}}" data-itemidx="{{itemidx}}" bindchange="handleChangeThree" wx:if="{{item.isNormal === false}}">
<van-radio style="margin-right: 20px;" class="radio-text" checked-color="#FF9900" name="轻微">轻微</van-radio>
<van-radio style="margin-right: 20px;" class="radio-text" checked-color="#FF3300" name="中度">中度</van-radio>
<van-radio class="radio-text" checked-color="#990000" name="严重">严重</van-radio>
</van-radio-group>
<textarea class="textarea" placeholder="请输入巡查详情" maxlength="-1" wx:if="{{item.isNormal === false}}" data-deviceidx="{{deviceidx}}" data-itemidx="{{itemidx}}" bindinput="bindInput" />
<view class="weui-uploader" style="padding: 20rpx 30rpx;overflow-y:scroll;" wx:if="{{item.isNormal === false}}">
<view class="img-v weui-uploader__bd" style="overflow:hidden;">
<view class="pic" wx:for="{{item.imgs}}" wx:for-item="img" wx:key="*this">
<image class="weui-uploader__img showImg" src="{{imgUrl + img}}" data-index="{{index}}" data-deviceidx="{{deviceidx}}" data-itemidx="{{itemidx}}" mode="aspectFill" bindtap="bindInput">
<icon type="cancel" class="delete-btn" data-index="{{index}}" data-deviceidx="{{deviceidx}}" data-itemidx="{{itemidx}}" catchtap="deleteImg" />
</image>
</view>
<!-- 用来提示用户上传图片 -->
<view class="weui-uploader__input-box pic" data-item="{{item.name}}" data-deviceidx="{{deviceidx}}" data-itemidx="{{itemidx}}" bindtap="chooseImg">
<image class="upload" src="/images/upload.png" />
</view>
</view>
</view>
<view class="divider" />
</view>
</view>
<view class="btnBox">
<view class="cancel" bindtap="bindCancel">取消</view>
<view class="submit" bindtap="addPatrolRecord">提交</view>
</view>
</view>

131
weapp/package/report/report.wxss

@ -0,0 +1,131 @@
.divider {
width: 100%;
height: 0px;
border-top: 1px solid #F5F5F5;
}
.flex-between {
display: flex;
justify-content: space-between;
}
.flex-end {
display: flex;
justify-content: flex-end;
}
.popBox {
position: absolute;
left: 50%;
z-index: 1000;
background: #fff;
width: 95%;
margin-left: -356rpx;
padding: 20rpx 0;
}
.item-name {
margin: 20rpx 0 0 30rpx;
}
.btnBox {
padding: 50px 30rpx;
overflow: hidden;
font-size: 30rpx;
display: flex;
justify-content: space-between;
}
.cancel {
width: 38vw;
height: 42px;
line-height: 42px;
text-align: center;
background: #fff;
border: 1px solid #006BE3;
border-radius: 24px;
font-weight: 600;
font-size: 16px;
color: #1684FF;
}
.submit {
width: 38vw;
height: 42px;
line-height: 42px;
text-align: center;
background: #1684FF;
border: 1px solid #006BE3;
border-radius: 24px;
font-weight: 600;
font-size: 16px;
color: #FFFFFF;
}
.pic {
float: left;
position: relative;
margin-right: 8px;
margin-bottom: 8px;
}
.showImg {
width: 160rpx;
height: 160rpx;
}
.delete-btn {
position: absolute;
top: 0;
right: 0;
}
.upload {
width: 63px;
height: 63px;
}
.block {
display: block;
}
.icon {
width: 18px;
height: 18px;
margin-right: 5px;
}
.radio-text {
font-size: 14px;
color: #323233;
}
.van-radio-group {
display: flex;
}
.textarea {
width: 84%;
margin: 0 auto;
padding: 20rpx;
height: 120rpx;
border: 1px solid #61616166;
}
.mission-card-title {
background-color: #fff;
overflow: auto;
padding: 24rpx 16px;
display: flex;
align-items: center
}
.fs-cell-title {
max-width: 6.2em;
min-width: 6.2em;
margin-right: 12px;
text-align: left;
color: var(--field-label-color, #646566)
}
.fs-cell-content {
color: var(--field-input-text-color, #323233)
}

94
weapp/package/riskManagement/riskCalendar/riskCalendar.js

@ -1,18 +1,108 @@
// package/riskManagement/riskCalendar/riskCalendar.js // package/riskManagement/riskCalendar/riskCalendar.js
Page({ import { getPatrolRecord } from "../../../utils/getApiUrl";
import { Request } from "../../../common";
import moment from '../../../utils/moment';
Page({
/** /**
* 页面的初始数据 * 页面的初始数据
*/ */
data: { data: {
show: false,
level: ['全部', '轻微', '中度', '严重'],
curLevel: '全部',
minDate: moment().startOf('day').subtract(6, 'days').valueOf(),
maxDate: moment().endOf('day').valueOf(),
curDate: moment().format('YYYY-MM-DD'),
showList: [],
formatter(day) { return day; },
},
dayData: [], // 每天的异常数据
onDateSelect(e) {
this.setData({ curDate: moment(e.detail).format('YYYY-MM-DD') })
this.calcShowList(this.data.curLevel, moment(e.detail).format('YYYY-MM-DD'))
},
showPopup() {
this.setData({ show: true })
},
onCancel() {
this.setData({ show: false })
},
onConfirm(e) {
this.setData({ curLevel: e.detail.value, show: false })
this.calcShowList(e.detail.value, this.data.curDate)
},
calcShowList(level = this.data.curLevel, date = this.data.curDate) {
const { dayData } = this;
let nextShowList = []
for (const day of dayData) {
if (day.formatDate === date) {
nextShowList = day.data;
}
}
if (level !== '全部') {
nextShowList = nextShowList.filter(l => l.level === level)
}
this.setData({ showList: nextShowList })
},
// 查看详情
toDetail(e) {
let data = JSON.stringify(e.currentTarget.dataset.item);
wx.navigateTo({
url: '/package/polling/inspectionRecordDetail/inspectionRecordDetail?data=' + encodeURIComponent(data),
})
}, },
/** /**
* 生命周期函数--监听页面加载 * 生命周期函数--监听页面加载
*/ */
onLoad(options) { onLoad(options) {
const { minDate, maxDate } = this.data;
Request.get(getPatrolRecord('all', moment(minDate).format('YYYY-MM-DD HH:mm:ss'), moment(maxDate).format('YYYY-MM-DD HH:mm:ss'), true, 'null')).then(res => {
let dayData = new Array(7)
for (let i = 6; i >= 0; i--) {
const month = moment().subtract(i, 'day').month();
const date = moment().subtract(i, 'day').date();
let data = [];
res.forEach(d => {
if (moment(d.inspectionTime).date() === date) {
// 计算此记录的异常等级(检查项中异常最高等级),用于列表展示
let tempLevel = '轻微';
for (const device of d.points.inspectContent) {
if (device.alarm === true) {
for (const item of device.checkItems) {
if (item.level) { tempLevel = item.level; }
}
}
}
data.push({
...d, level: tempLevel,
inspectionTime: moment(d.inspectionTime).format('YYYY-MM-DD HH:mm:ss'),
lastInspectionTime: d.lastInspectionTime ? moment(d.lastInspectionTime).format('YYYY-MM-DD HH:mm:ss') : '--',
})
}
})
dayData[6 - i] = { month, date, formatDate: moment().subtract(i, 'day').format('YYYY-MM-DD'), data }
}
const formatter = (day) => {
const month = day.date.getMonth();
const date = day.date.getDate();
dayData.forEach(d => {
if (d.month === month && d.date === date && d.data.length) {
day.bottomInfo = '🔸'
}
})
return day;
}
this.setData({ formatter });
this.dayData = dayData;
this.calcShowList();
})
}, },
/** /**

8
weapp/package/riskManagement/riskCalendar/riskCalendar.json

@ -1,9 +1,11 @@
{ {
"navigationBarBackgroundColor": "#1979ff", "navigationBarBackgroundColor": "#006BE3",
"navigationBarTextStyle": "white", "navigationBarTextStyle": "white",
"navigationBarTitleText": "故障日历", "navigationBarTitleText": "故障风险管理",
"enablePullDownRefresh": false, "enablePullDownRefresh": false,
"usingComponents": { "usingComponents": {
"ec-canvas": "../../components/ec-canvas/ec-canvas" "van-calendar": "@vant/weapp/calendar/index",
"van-picker": "@vant/weapp/picker/index",
"van-popup": "@vant/weapp/popup/index"
} }
} }

78
weapp/package/riskManagement/riskCalendar/riskCalendar.wxml

@ -1,2 +1,78 @@
<!--package/riskManagement/riskCalendar/riskCalendar.wxml--> <!--package/riskManagement/riskCalendar/riskCalendar.wxml-->
<text>package/riskManagement/riskCalendar/riskCalendar.wxml</text> <view class="risk-calendar">
<view class="card">
<view class="card-top flex flex-between">
<view class="card-left flex">
<image class="card-icon" src="/images/calendar_icon.png" />
<view class="title">巡检日历</view>
</view>
<view class="card-right">{{curDate}}</view>
<image src="/images/calendar_card_bg.png" class="card-bg" />
</view>
<view class="calendar-box">
<van-calendar
show-mark="{{ false }}"
show-title="{{ false }}"
show-subtitle="{{ false }}"
poppable="{{ false }}"
show-confirm="{{ false }}"
min-date="{{ minDate }}"
max-date="{{ maxDate }}"
formatter="{{ formatter }}"
bind:select="onDateSelect"
row-height="48"
color="#1684FF"
class="calendar"
/>
</view>
</view>
<view class="title-box flex flex-between ">
<view class="title">异常等级</view>
<view class="flex" bind:tap="showPopup">
<view class="picker-text">{{curLevel}}</view>
<image class="arrow" src="/images/arrow_right_blue.svg" />
</view>
</view>
<van-popup show="{{ show }}" round position="bottom" close-on-click-overlay="{{false}}">
<van-picker
show-toolbar
columns="{{ level }}"
bind:cancel="onCancel"
bind:confirm="onConfirm"
/>
</van-popup>
<view wx:for="{{showList}}" class="card" style="margin-bottom: 10px">
<view class="card-top flex flex-between">
<view class="card-left flex">
<image class="card-icon" src="/images/right_icon.png" />
<view class="title">{{item.points.project.name}}</view>
</view>
<view class="card-right-btn flex" data-item="{{item}}" bindtap="toDetail">查看详情</view>
<image src="/images/right_card_bg.png" class="card-bg" />
</view>
<view class="card-content">
<view class="row flex flex-between">
<view class="content-left">本次巡检日期</view>
<view class="content-right">{{item.inspectionTime}}</view>
</view>
<view class="row flex flex-between">
<view class="content-left">点位</view>
<view class="content-right">{{item.points.itemData.name}}</view>
</view>
<view class="row flex flex-between">
<view class="content-left">巡检人</view>
<view class="content-right">{{item.points.user.name}}</view>
</view>
<view class="row flex flex-between">
<view class="content-left">巡检结果</view>
<view class="content-right" style="color: red;">异常</view>
</view>
<view class="row flex flex-between">
<view class="content-left">异常等级</view>
<view class="content-right" style="color: red;">{{item.level}}</view>
</view>
</view>
</view>
</view>

104
weapp/package/riskManagement/riskCalendar/riskCalendar.wxss

@ -1 +1,105 @@
/* package/riskManagement/riskCalendar/riskCalendar.wxss */ /* package/riskManagement/riskCalendar/riskCalendar.wxss */
.risk-calendar {
height: 100%;
background-image: linear-gradient(179deg, #006BE3 0%, #4E87FF 16%, #4e87ff00 93%);
padding: 0 15px;
}
.card {
box-sizing: border-box;
background: #FFFFFF;
box-shadow: 2px 2px 11px 0 #00000008, 0 0 4px 0 #00000012;
border-radius: 4px;
}
.card-top {
height: 68px;
background-image: linear-gradient(0deg, #F3F7FF 84%, #DBE6FF 100%);
border-radius: 4px;
position: relative;
}
.card-bg {
position: absolute;
top: 0;
right: 11px;
width: 115px;
height: 67px;
}
.card-left {
margin-left: 23px;
font-weight: 500;
font-size: 16px;
color: #000000d9;
}
.card-right {
margin-right: 18px;
color: #1684FF;
}
.card-right-btn {
width: 76px;
height: 26px;
margin-right: 18px;
background: #1684FF;
border-radius: 16px;
color: #fff;
font-size: 13px;
z-index: 99;
}
.card-icon {
width: 30px;
height: 30px;
margin-right: 8px;
}
.calendar-box {
padding-bottom: 12px;
}
.calendar {
--calendar-height: 300px;
}
.title-box {
margin: 10px 0;
}
.title {
font-weight: 600;
font-size: 16px;
color: #383A3B;
}
.picker-text {
font-weight: 600;
color: #1684FF;
text-align: right;
}
.arrow {
width: 12px;
height: 12px;
transform: rotate(90deg);
margin-left: 9px;
}
.card-content {
padding-bottom: 10px;
}
.row {
margin: 4px 26px;
color: #31373E;
}
.content-left {
font-size: 12px;
}
.content-right {
font-size: 14px;
}

76
weapp/package/riskManagement/riskManagement.js

@ -1,5 +1,8 @@
// package/riskManagement/riskManagement.js // package/riskManagement/riskManagement.js
import * as echarts from '../components/ec-canvas/echarts'; import * as echarts from '../components/ec-canvas/echarts';
import { Request } from "../../common";
import { getPatrolRecordStatistic } from '../../utils/getApiUrl';
import moment from '../../utils/moment';
function setOption(chart, data) { function setOption(chart, data) {
const option = { const option = {
@ -10,17 +13,42 @@ function setOption(chart, data) {
bottom: '3%', bottom: '3%',
containLabel: true containLabel: true
}, },
tooltip: {
trigger: 'axis',
},
xAxis: { xAxis: {
type: 'category', type: 'category',
data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'] data: data.map(d => d.month),
boundaryGap: false,
axisLabel: {
formatter: (value) => {
const temp = value.split('-');
return + temp[1] + '月' + '\n ' + temp[0];
}
},
}, },
yAxis: { yAxis: {
type: 'value' type: 'value',
splitLine: { // 网格线
lineStyle: {
type: 'dashed'
},
}
}, },
series: [ series: [
{ {
data: data, data: data.map(d => d.count),
type: 'line' type: 'line',
symbol: 'circle',
symbolSize: 5,
itemStyle: {
normal: {
color: '#008AFF',
lineStyle: {
color: '#008AFF',
}
}
},
} }
] ]
}; };
@ -38,7 +66,12 @@ Page({
lazyLoad: true, // 将 lazyLoad 设为 true 后,需要手动初始化图表 lazyLoad: true, // 将 lazyLoad 设为 true 后,需要手动初始化图表
}, },
isLoaded: false, isLoaded: false,
list: [1,2,3] list: [1, 2, 3, 4],
monthAlarmCount: 0,
monthHandleCount: 0,
historyTrend: [],
monthDeviceRank: [],
monthDeviceScoreRank: [],
}, },
// 初始化图表 // 初始化图表
@ -65,14 +98,41 @@ Page({
}); });
}, },
toCalendar() {
wx.navigateTo({
url: '/package/riskManagement/riskCalendar/riskCalendar',
})
},
/** /**
* 生命周期函数--监听页面加载 * 生命周期函数--监听页面加载
*/ */
onLoad(options) { onLoad(options) {
const userInfo = wx.getStorageSync("userInfo");
// 请求数据 // 请求数据
setTimeout(() => { wx.showLoading()
this.initChart([250, 300, 100, 147, 260, 123, 311]) Request.get(getPatrolRecordStatistic(userInfo.structure.join())).then(res => {
}, 1000) wx.hideLoading()
let historyTrend = new Array(12)
for (let i = 11; i >= 0; i--) {
const month = moment().subtract(i, 'month').format('YYYY-MM')
historyTrend[11 - i] = {
month: month,
count: res.historyTrend?.find(h => h.month === month)?.count || 0,
}
}
const monthDeviceRank = [...res.monthDeviceAlarm]
.sort((a, b) => b.abnormalCount - a.abnormalCount)
.map(d => ({ ...d, itemsCount: d.itemsCount.sort((a, b) => b.count - a.count) }))
this.setData({
monthAlarmCount: res.monthAlarmCount,
monthHandleCount: res.monthHandleCount,
historyTrend,
monthDeviceRank: monthDeviceRank,
monthDeviceScoreRank: [...res.monthDeviceAlarm].sort((a, b) => b.abnormalScore - a.abnormalScore),
})
this.initChart(historyTrend)
})
}, },
/** /**

54
weapp/package/riskManagement/riskManagement.wxml

@ -1,22 +1,23 @@
<!-- package / riskManagement / riskManagement.wxml --> <!-- package / riskManagement / riskManagement.wxml -->
<view class="risk-management"> <view class="risk-management">
<view class="icon"><text class="icon-text">故障统计</text></view> <image src="/images/right_bg.png" class="page-bg" />
<view class="icon flex"><text class="icon-text">故障统计</text></view>
<view class="flex flex-between"> <view class="flex flex-between">
<view class="title-item flex flex-col"> <view class="title-item flex flex-col">
<view>本月上报风险</view> <view>本月上报风险</view>
<view><text class="title-num">{{86}}</text><text class="title-unit">个</text></view> <view><text class="title-num">{{monthAlarmCount}}</text><text class="title-unit">个</text></view>
</view> </view>
<view class="title-item flex flex-col"> <view class="title-item flex flex-col">
<view>故障风险管理</view> <view>本月处理风险</view>
<view><text class="title-num">{{300}}</text><text class="title-unit">个</text></view> <view><text class="title-num">{{monthHandleCount}}</text><text class="title-unit">个</text></view>
</view> </view>
</view> </view>
<view class="card"> <view class="card">
<view class="flex flex-start"> <view class="flex flex-start">
<!-- <image src="" class="card-img" /> --> <image src="/images/fault_icon.png" class="card-img" />
<view class="card-img" style="background: blue;"></view>
<view class="card-title">历史风险趋势</view> <view class="card-title">历史风险趋势</view>
</view> </view>
<view class="chart"> <view class="chart">
@ -26,16 +27,45 @@
<view class="card"> <view class="card">
<view class="flex flex-between"> <view class="flex flex-between">
<!-- <image src="" class="card-img" /> -->
<view class="flex"> <view class="flex">
<view class="card-img" style="background: blue;"></view> <image src="/images/fault_icon.png" class="card-img" />
<view class="card-title">故障排行榜</view> <view class="card-title">故障排行榜</view>
</view> </view>
<view class="card-link">查看详情 ></view> <view class="card-link" bindtap="toCalendar">查看详情 ></view>
</view>
<view style="margin-top: 10px">【故障次数统计】</view>
<view class="list" wx:for="{{monthDeviceRank}}">
<view class="list-title">{{item.deviceName}}</view>
<view class="list-line" />
<view class="list-content flex flex-between">
<view class="content-item content-left">
<view>所属结构物</view>
<view>故障次数</view>
<view>问题概述</view>
</view>
<view class="content-item content-right">
<view>{{item.project}}</view>
<view>{{item.abnormalCount}}次</view>
<view>{{item.itemsCount[0].name}}</view>
</view>
</view>
</view>
<view style="margin-top: 10px">【故障评分统计】</view>
<view class="list" wx:for="{{monthDeviceScoreRank}}">
<view class="list-title">{{item.deviceName}}</view>
<view class="list-line" />
<view class="list-content flex flex-between">
<view class="content-item content-left">
<view>所属结构物</view>
<view>故障评分</view>
<view>等级分布</view>
</view>
<view class="content-item content-right">
<view>{{item.project}}</view>
<view>{{item.abnormalScore}}</view>
<view>严重:{{item.severity}}次,中度{{item.middle}}次,轻微{{item.slight}}次</view>
</view>
</view> </view>
<view>【故障次数统计】</view>
<view class="list" wx:for="{{list}}">
<view class="list-title">设备{{item}}</view>
</view> </view>
</view> </view>
</view> </view>

45
weapp/package/riskManagement/riskManagement.wxss

@ -1,10 +1,18 @@
/* package/riskManagement/riskManagement.wxss */ /* package/riskManagement/riskManagement.wxss */
.risk-management { .risk-management {
height: 100vh; height: 100%;
background-image: linear-gradient(179deg, #006BE3 0%, #4E87FF 16%, #4e87ff00 93%); background-image: linear-gradient(179deg, #006BE3 0%, #4E87FF 16%, #4e87ff00 93%);
padding: 0 15px; padding: 0 15px;
} }
.page-bg {
position: absolute;
width: 161px;
height: 174px;
opacity: 0.61;
right: 43px;
}
.icon { .icon {
width: 61px; width: 61px;
height: 31.86px; height: 31.86px;
@ -22,6 +30,7 @@
} }
.title-item { .title-item {
margin-top: 50px;
width: 150px; width: 150px;
color: #ffffffd9; color: #ffffffd9;
} }
@ -43,6 +52,7 @@
border-radius: 4px; border-radius: 4px;
padding: 12px; padding: 12px;
margin-top: 12px; margin-top: 12px;
position: relative;
} }
.card-img { .card-img {
@ -73,4 +83,37 @@
margin-top: 10px; margin-top: 10px;
padding: 10px 7px; padding: 10px 7px;
background-color: #F1F7FF; background-color: #F1F7FF;
border-radius: 4px;
}
.list-title {
font-weight: 500;
font-size: 15px;
color: #1684FF;
}
.list-line {
width: 100%;
border-top: 1px solid #DAE6F6;
margin: 5px 0;
}
.list-content {
color: #31373E;
}
.content-item {
height: 72px;
display: flex;
flex-direction: column;
justify-content: space-evenly;
}
.content-left {
font-size: 12px;
}
.content-right {
font-size: 14px;
text-align: right;
} }

3
weapp/package/subSystem/subSystem.js

@ -141,7 +141,7 @@ Page({
if (res) { if (res) {
//巡查内容 //巡查内容
that.setData({ that.setData({
currentRepairCount: res?.filter(i => i?.patrolRecordIssueHandles[0]?.yanshoushijian && parseInt(moment(i?.patrolRecordIssueHandles[0]?.yanshoushijian).format('YYYYMMDD')) === parseInt(moment().format('YYYYMMDD'))).length || 0, currentRepairCount: res?.filter(i =>i.patrolRecordIssueHandles.length>0 ).length || 0,
currentPatrolCount: res.length, currentPatrolCount: res.length,
level1Count: that.filterLevelCount(res,'轻微')||0, level1Count: that.filterLevelCount(res,'轻微')||0,
level2Count: that.filterLevelCount(res,'中度')||0, level2Count: that.filterLevelCount(res,'中度')||0,
@ -160,7 +160,6 @@ Page({
} }
}) })
that.ecComponent = that.selectComponent('#mychart-dom-pie'); that.ecComponent = that.selectComponent('#mychart-dom-pie');
console.log('that.level1Count',this.data.level2Count)
var option = { var option = {
backgroundColor: "#ffffff", backgroundColor: "#ffffff",
legend: { legend: {

46
weapp/package/subSystem/subSystem.wxml

@ -1,6 +1,7 @@
<!--package/subSystem/subSystem.wxml--> <!--package/subSystem/subSystem.wxml-->
<!--总览图--> <view style="background-image: linear-gradient(179deg, #006BE3 0%, #4E87FF 16%, #4e87ff00 93%); padding: 0 15px; ">
<view style="display: flex;justify-content: space-around;margin-bottom: 10px; padding-top: 10px; background-image: linear-gradient(179deg, #006BE3 0%, #4E87FF 16%, #4e87ff00 93%);"> <!--总览图-->
<view style="display: flex;justify-content: space-around;margin-bottom: 10px; padding-top: 10px; ">
<view> <view>
<view>本月巡检次数</view> <view>本月巡检次数</view>
<view class="number">{{currentPatrolCount}}</view> <view class="number">{{currentPatrolCount}}</view>
@ -9,27 +10,32 @@
<view>本月维修次数</view> <view>本月维修次数</view>
<view class="number">{{currentRepairCount}}</view> <view class="number">{{currentRepairCount}}</view>
</view> </view>
</view>
<!--饼图-->
<view class="card ">
<image src="/images/shape1.png" class="imgStyle"></image>
<view class="top">
<view style="display: flex; align-items: center;">
<image style="width: 30px; height: 30px;" src="/images/device.png" />
<text class="fontStyle">故障</text>
</view>
<view class="countStyle">
总数:{{currentPatrolCount}}
</view>
</view>
<view style="height: 300px"> <ec-canvas id="mychart-dom-pie" canvas-id="mychart-pie" ec="{{ ec }}"></ec-canvas>
</view> </view>
<!--饼图--> </view>
<view class="card" > <!--巡检日历-->
<ec-canvas id="mychart-dom-pie" canvas-id="mychart-pie" ec="{{ ec }}"></ec-canvas> <view class="card heightStyle">
</view>
<!--巡检日历-->
<view class="card heightStyle">
<view class="header"> <view class="header">
<view style="display:flex;align-items: center;">
<image style="width: 30px; height: 30px;" src="/images/calendar.png" />
<view class="xunjian">巡检日历</view> <view class="xunjian">巡检日历</view>
</view>
<image src="/images/shape2.png" class="imgStyle"></image>
<view class="yearMonth">{{currentYear+'年'+currentMonth+'月'}}</view> <view class="yearMonth">{{currentYear+'年'+currentMonth+'月'}}</view>
</view> </view>
<van-calendar <van-calendar max-date="{{lastDay}}" v-model="selectedDate" readonly show-title="{{false}}" show-subtitle="{{false}}" poppable="{{ false }}" show-confirm="{{ false }}" default-date="{{selectedDate}}" formatter="{{formatter}}" color="#0000FF" class="calendar" />
max-date="{{lastDay}}" </view>
v-model="selectedDate"
readonly
show-title="{{false}}"
show-subtitle="{{false}}"
poppable="{{ false }}"
show-confirm="{{ false }}"
default-date="{{selectedDate}}"
formatter="{{formatter}}"
color="#0000FF"
class="calendar" />
</view> </view>

44
weapp/package/subSystem/subSystem.wxss

@ -18,13 +18,14 @@
border: 1px solid #ddd; border: 1px solid #ddd;
border-radius: 8px; border-radius: 8px;
/* padding: 10px 10px; */ /* padding: 10px 10px; */
margin: 5px 5px; padding: 12px;
margin-top: 12px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
/* width: 300px; */ background-image: linear-gradient(0deg, #F3F7FF 84%, #DBE6FF 100%);
height: 300px;
} }
.heightStyle{ .heightStyle{
height: auto; height: 100%;
} }
.xunjian{ .xunjian{
@ -48,9 +49,12 @@
} }
.header{ .header{
display: flex; display: flex;
justify-content: space-around; justify-content: space-between;
margin-bottom: 10px; margin-bottom: 10px;
margin-top:10px; margin-top:10px;
align-items: center;
position: relative;
/* margin-left: -22px; */
} }
.yellowClass .van-calendar__bottom-info{ .yellowClass .van-calendar__bottom-info{
@ -62,8 +66,38 @@
font-size: 50px; font-size: 50px;
color:green; color:green;
} }
.imgStyle {
position: absolute;
width: 115px;
height: 67px;
right: 0;
}
.top{
display: flex;
justify-content: space-between;
padding: 10px;
}
.fontStyle {
width: 64px;
height: 22px;
font-family: PingFangSC-Medium;
font-weight: 500;
font-size: 16px;
color: #000000d9;
letter-spacing: 0;
margin-left: 5px;
}
.countStyle {
width: 89px;
height: 24px;
font-family: PingFangSC-Medium;
font-weight: 500;
font-size: 17px;
color: #1684FF;
letter-spacing: 0;
}

53
weapp/package/troubleshooting/shootingForm/index.js

@ -47,7 +47,7 @@ Page({
/** /**
* 生命周期函数--监听页面加载 * 生命周期函数--监听页面加载
*/ */
onLoad (options) { onLoad(options) {
const { shootingid, tabIndex } = options const { shootingid, tabIndex } = options
if (shootingid) { if (shootingid) {
this.setData({ this.setData({
@ -69,14 +69,20 @@ Page({
points: { points: {
...issue.PatrolRecord.points, ...issue.PatrolRecord.points,
inspectContent: (() => { inspectContent: (() => {
// 1.1 版本(2023-10-18)之前 dataList.points.inspectContent 为 Object, 1.1版本因增加点位关联设备改为 Array
const priorToV1_1 = moment(issue.PatrolRecord.inspectionTime).isBefore(moment('2023-10-18'))
if (priorToV1_1) {
let inspectContent = [] let inspectContent = []
for (let k in issue.PatrolRecord.points.inspectContent) { for (let k in issue.PatrolRecord.points.inspectContent) {
inspectContent.push({ inspectContent.push({
...issue.PatrolRecord.points.inspectContent[k], ...issue.PatrolRecord.points.inspectContent[k],
itemName: k name: k,
}) })
} }
return inspectContent return [{ checkItems: inspectContent }]
} else {
return issue.PatrolRecord.points.inspectContent
}
})() })()
} }
} }
@ -88,6 +94,7 @@ Page({
isRepair: tabIndex == 0 && (issue.state == 4 || issue.state == 7), isRepair: tabIndex == 0 && (issue.state == 4 || issue.state == 7),
isCheck: tabIndex == 0 && issue.state == 5, isCheck: tabIndex == 0 && issue.state == 5,
}) })
console.log(nextData, 'nextData')
this.setData({ this.setData({
strucFocusUser: { strucFocusUser: {
...strucUser, ...strucUser,
@ -135,13 +142,13 @@ Page({
} }
}, },
onPointItemCollapseActiveChange (event) { onPointItemCollapseActiveChange(event) {
this.setData({ this.setData({
pointItemCollapseActiveNames: event.detail, pointItemCollapseActiveNames: event.detail,
}); });
}, },
onMaintenancePersonPopupChange (e) { onMaintenancePersonPopupChange(e) {
if (e.target.dataset.type == 'zhijian') { if (e.target.dataset.type == 'zhijian') {
this.setData({ this.setData({
qualityPersonIndex: e.detail.value qualityPersonIndex: e.detail.value
@ -155,13 +162,13 @@ Page({
} }
}, },
showPlanStartTimePopup () { showPlanStartTimePopup() {
if (this.data.isPlanState) { if (this.data.isPlanState) {
this.setData({ planStartTimePopupShow: true }) this.setData({ planStartTimePopupShow: true })
} }
}, },
closePlanStartTimePopup (e) { closePlanStartTimePopup(e) {
this.setData({ planStartTimePopupShow: false }) this.setData({ planStartTimePopupShow: false })
if (e.target.dataset.option == 'cancel') { if (e.target.dataset.option == 'cancel') {
// this.setData({ planStartTime: '' }) // this.setData({ planStartTime: '' })
@ -172,19 +179,19 @@ Page({
} }
}, },
onPlanStartTimeChange (event) { onPlanStartTimeChange(event) {
this.setData({ this.setData({
planStartTime: event.detail, planStartTime: event.detail,
}) })
}, },
showPlanEndTimePopup () { showPlanEndTimePopup() {
if (this.data.isPlanState) { if (this.data.isPlanState) {
this.setData({ planEndTimePopupShow: true }) this.setData({ planEndTimePopupShow: true })
} }
}, },
closePlanEndTimePopup (e) { closePlanEndTimePopup(e) {
this.setData({ planEndTimePopupShow: false }) this.setData({ planEndTimePopupShow: false })
if (e.target.dataset.option == 'confirmed') { if (e.target.dataset.option == 'confirmed') {
this.setData({ this.setData({
@ -193,13 +200,13 @@ Page({
} }
}, },
onPlanEndTimeChange (event) { onPlanEndTimeChange(event) {
this.setData({ this.setData({
planEndTime: event.detail, planEndTime: event.detail,
}) })
}, },
onInputChange (e) { onInputChange(e) {
let value = e.detail.value let value = e.detail.value
if (e.target.dataset.type === 'cost' && !/^(\d?)+(\.\d{0,2})?$/.test(e.detail.value)) { if (e.target.dataset.type === 'cost' && !/^(\d?)+(\.\d{0,2})?$/.test(e.detail.value)) {
wx.showToast({ wx.showToast({
@ -215,8 +222,8 @@ Page({
// 预览图片 // 预览图片
previewImg: function (e) { previewImg: function (e) {
const { index, itemindex, type } = e.currentTarget.dataset const { index, itemindex, deviceidx, type } = e.currentTarget.dataset
const imgs = type == 'point' ? this.data.data.PatrolRecord.points.inspectContent[itemindex].imgs : this.data[type]; const imgs = type == 'point' ? this.data.data.PatrolRecord.points.inspectContent[deviceidx].checkItems[itemindex].imgs : this.data[type];
const newImgs = imgs.map(i => this.data.imgServer + i); const newImgs = imgs.map(i => this.data.imgServer + i);
wx.previewImage({ wx.previewImage({
current: newImgs[index], current: newImgs[index],
@ -324,7 +331,7 @@ Page({
}); });
}, },
async confirm (e) { async confirm(e) {
const { approve } = e.target.dataset const { approve } = e.target.dataset
const { state } = this.data.data const { state } = this.data.data
const { const {
@ -465,7 +472,7 @@ Page({
}) })
}, },
mustInput (field) { mustInput(field) {
let fieldMap = { let fieldMap = {
repairPerson: '维修人', repairPerson: '维修人',
checkPerson: '质检人', checkPerson: '质检人',
@ -488,49 +495,49 @@ Page({
/** /**
* 生命周期函数--监听页面初次渲染完成 * 生命周期函数--监听页面初次渲染完成
*/ */
onReady () { onReady() {
}, },
/** /**
* 生命周期函数--监听页面显示 * 生命周期函数--监听页面显示
*/ */
onShow () { onShow() {
}, },
/** /**
* 生命周期函数--监听页面隐藏 * 生命周期函数--监听页面隐藏
*/ */
onHide () { onHide() {
}, },
/** /**
* 生命周期函数--监听页面卸载 * 生命周期函数--监听页面卸载
*/ */
onUnload () { onUnload() {
}, },
/** /**
* 页面相关事件处理函数--监听用户下拉动作 * 页面相关事件处理函数--监听用户下拉动作
*/ */
onPullDownRefresh () { onPullDownRefresh() {
}, },
/** /**
* 页面上拉触底事件的处理函数 * 页面上拉触底事件的处理函数
*/ */
onReachBottom () { onReachBottom() {
}, },
/** /**
* 用户点击右上角分享 * 用户点击右上角分享
*/ */
onShareAppMessage () { onShareAppMessage() {
} }
}) })

11
weapp/package/troubleshooting/shootingForm/index.wxml

@ -15,10 +15,12 @@
</view> </view>
<van-field value="{{data.PatrolRecord.points.itemData.name}}" label="点位" readonly accordion /> <van-field value="{{data.PatrolRecord.points.itemData.name}}" label="点位" readonly accordion />
<van-collapse wx:if="{{data.PatrolRecord.points.inspectContent}}" value="{{ pointItemCollapseActiveNames }}" bind:change="onPointItemCollapseActiveChange" border="{{false}}"> <van-collapse wx:if="{{data.PatrolRecord.points.inspectContent}}" value="{{ pointItemCollapseActiveNames }}" bind:change="onPointItemCollapseActiveChange" border="{{false}}">
<block wx:for="{{data.PatrolRecord.points.inspectContent}}" wx:key="index"> <block wx:for="{{data.PatrolRecord.points.inspectContent}}" wx:key="index" wx:for-item="device" wx:for-index="deviceidx">
<van-collapse-item wx:if="{{!item.isNormal}}" name="{{item.itemName}}" border="{{false}}"> <van-field wx:if="{{device.deviceName && device.alarm}}" value="{{device.deviceName}}" label="设备" readonly accordion />
<block wx:for="{{device.checkItems}}">
<van-collapse-item wx:if="{{!item.isNormal}}" name="{{item.id}}" border="{{false}}">
<view slot="title"> <view slot="title">
<van-field value="{{item.itemName}}" label="检查项" readonly /> <van-field value="{{item.name}}" label="检查项" readonly />
</view> </view>
<view slot=""> <view slot="">
<van-field value="{{item.level}}" label="异常等级" readonly /> <van-field value="{{item.level}}" label="异常等级" readonly />
@ -28,7 +30,7 @@
<view class="fs-cell-title" style="">现场照片</view> <view class="fs-cell-title" style="">现场照片</view>
<view class="fs-cell-content" style=""> <view class="fs-cell-content" style="">
<block wx:for="{{item.imgs}}" wx:for-index="imgIndex" wx:for-item="imgUrl" wx:key="imgIndex"> <block wx:for="{{item.imgs}}" wx:for-index="imgIndex" wx:for-item="imgUrl" wx:key="imgIndex">
<image style="height:160rpx;width:160rpx;padding-right:12rpx;" src="{{imgServer+imgUrl}}" data-img="{{imgServer+imgUrl}}" data-index="{{imgIndex}}" data-key="{{imgIndex}}" data-itemindex="{{index}}" mode="aspectFill" bindtap="previewImg" data-type="point"></image> <image style="height:160rpx;width:160rpx;padding-right:12rpx;" src="{{imgServer+imgUrl}}" data-img="{{imgServer+imgUrl}}" data-index="{{imgIndex}}" data-key="{{imgIndex}}" data-itemindex="{{index}}" data-deviceidx="{{deviceidx}}" mode="aspectFill" bindtap="previewImg" data-type="point"></image>
</block> </block>
</view> </view>
</view> </view>
@ -36,6 +38,7 @@
</view> </view>
</van-collapse-item> </van-collapse-item>
</block> </block>
</block>
</van-collapse> </van-collapse>
<view class="mission-card-title mission-center-card-title"> <view class="mission-card-title mission-center-card-title">
<image class="icon" src="../../../images/title_icon.svg"></image> <image class="icon" src="../../../images/title_icon.svg"></image>

8
weapp/pages/home/home.js

@ -98,7 +98,7 @@ Page({
//过去七天的所有巡检记录 //过去七天的所有巡检记录
const sevenDaysList=list?.filter(item=> const sevenDaysList=list?.filter(item=>
{ {
const recordDate = moment(date, 'YYYY-MM-DD'); // 解析日期 const recordDate = moment(item?.inspectionTime, 'YYYY-MM-DD'); // 解析日期
const daysDifference = moment().diff(recordDate, 'days'); // 计算日期差 const daysDifference = moment().diff(recordDate, 'days'); // 计算日期差
// 返回近七天内的记录 // 返回近七天内的记录
return daysDifference >= 0 && daysDifference < 7; return daysDifference >= 0 && daysDifference < 7;
@ -106,10 +106,10 @@ Page({
)||[] )||[]
that.setData({ that.setData({
allCount:list.length || 0, allCount:list.length || 0,
allQuestionCount:list?.filter(i=>i.patrolRecordIssueHandles>0)?.length || 0, allQuestionCount:list?.filter(i=>i.patrolRecordIssueHandles.length>0)?.length || 0,
allHandleCount:list?.filter(i => i?.patrolRecordIssueHandles[0]?.yanshoushijian && parseInt(moment(i?.patrolRecordIssueHandles[0]?.yanshoushijian).format('YYYYMMDD')) === parseInt(moment().format('YYYYMMDD'))).length || 0, allHandleCount:list?.filter(i => i?.patrolRecordIssueHandles[0]?.yanshoushijian && parseInt(moment(i?.patrolRecordIssueHandles[0]?.yanshoushijian).format('YYYYMMDD')) === parseInt(moment().format('YYYYMMDD'))).length || 0,
sevenDaysCount:sevenDaysList.length||0, sevenDaysCount:sevenDaysList?.length||0,
sevenDaysQuestionCount:sevenDaysList?.filter(i=>i.patrolRecordIssueHandles>0)?.length || 0, sevenDaysQuestionCount:sevenDaysList?.filter(i=>i.patrolRecordIssueHandles.length>0)?.length || 0,
sevenDaysHandleCount:sevenDaysList?.filter(i => i?.patrolRecordIssueHandles[0]?.yanshoushijian && parseInt(moment(i?.patrolRecordIssueHandles[0]?.yanshoushijian).format('YYYYMMDD')) === parseInt(moment().format('YYYYMMDD'))).length || 0, sevenDaysHandleCount:sevenDaysList?.filter(i => i?.patrolRecordIssueHandles[0]?.yanshoushijian && parseInt(moment(i?.patrolRecordIssueHandles[0]?.yanshoushijian).format('YYYYMMDD')) === parseInt(moment().format('YYYYMMDD'))).length || 0,
pageHeight:pageHeight+'px', pageHeight:pageHeight+'px',
swiperData:data.datas swiperData:data.datas

4
weapp/pages/home/home.wxml

@ -1,9 +1,9 @@
<!--pages/home/home.wxml--> <!--pages/home/home.wxml-->
<view style="height:{{pageHeight}} ; overflow: auto;"> <view style="height:{{pageHeight}} ; overflow: auto; padding: 0 15px;">
<!--轮播图--> <!--轮播图-->
<view class="card"> <view class="card">
<swiper class="home-swiper" indicator-dots="true" autoplay="{{true}}" interval="{{2000}}" duration="{{500}}"> <swiper class="home-swiper" indicator-dots="true" autoplay="{{true}}" interval="{{2000}}" duration="{{500}}">
<block wx:for-items="{{swiperData}}"> <block wx:for-items="{{swiperData}}" wx:key="*this">
<swiper-item> <swiper-item>
<image src="{{item.imgurl}}" class="slide-image" /> <image src="{{item.imgurl}}" class="slide-image" />
</swiper-item> </swiper-item>

4
weapp/pages/home/home.wxss

@ -12,8 +12,8 @@
border-radius: 8px; border-radius: 8px;
/* padding: 10px; */ /* padding: 10px; */
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
margin: 10px; margin-top: 12px;
} }
.home-swiper { .home-swiper {
width: 95%; width: 95%;
/* height: 360rpx; */ /* height: 360rpx; */

29
weapp/pages/overview/overview.js

@ -9,19 +9,19 @@ Page({
}, },
// 巡检 // 巡检
bindPolling () { bindPolling() {
wx.navigateTo({ wx.navigateTo({
url: '/package/polling/polling', url: '/package/polling/polling',
}) })
}, },
bindTroubleshooting () { bindTroubleshooting() {
wx.navigateTo({ wx.navigateTo({
url: '/package/troubleshooting/index', url: '/package/troubleshooting/index',
}) })
}, },
bindInspectionReport () { bindInspectionReport() {
wx.navigateTo({ wx.navigateTo({
url: '/package/inspectionReport/inspectionReport', url: '/package/inspectionReport/inspectionReport',
}) })
@ -30,21 +30,28 @@ Page({
/** /**
* 生命周期函数--监听页面加载 * 生命周期函数--监听页面加载
*/ */
onLoad (options) { onLoad(options) {
}, },
/** /**
* 生命周期函数--监听页面初次渲染完成 * 生命周期函数--监听页面初次渲染完成
*/ */
onReady () { onReady() {
}, },
/** /**
* 生命周期函数--监听页面显示 * 生命周期函数--监听页面显示
*/ */
onShow () { onShow() {
const userRole = wx.getStorageSync('userRole');
if (userRole && userRole.includes('管理')) {
wx.reLaunch({
url: '/pages/workbench/workbench'
});
return
}
if (typeof this.getTabBar === 'function' && this.getTabBar()) { if (typeof this.getTabBar === 'function' && this.getTabBar()) {
this.getTabBar().setData({ this.getTabBar().setData({
selected: 1 selected: 1
@ -55,35 +62,35 @@ Page({
/** /**
* 生命周期函数--监听页面隐藏 * 生命周期函数--监听页面隐藏
*/ */
onHide () { onHide() {
}, },
/** /**
* 生命周期函数--监听页面卸载 * 生命周期函数--监听页面卸载
*/ */
onUnload () { onUnload() {
}, },
/** /**
* 页面相关事件处理函数--监听用户下拉动作 * 页面相关事件处理函数--监听用户下拉动作
*/ */
onPullDownRefresh () { onPullDownRefresh() {
}, },
/** /**
* 页面上拉触底事件的处理函数 * 页面上拉触底事件的处理函数
*/ */
onReachBottom () { onReachBottom() {
}, },
/** /**
* 用户点击右上角分享 * 用户点击右上角分享
*/ */
onShareAppMessage () { onShareAppMessage() {
} }
}) })

4
weapp/pages/workbench/workbench.js

@ -14,7 +14,7 @@ Page({
{ {
iconPath: '/images/workbench/report.png', iconPath: '/images/workbench/report.png',
text: '设备大数据图谱', text: '设备大数据图谱',
page: '/package/riskManagement/riskCalendar/riskCalendar' page: '/package/deviceBigdataGraph/deviceBigdataGraph'
}, },
{ {
iconPath: '/images/workbench/report.png', iconPath: '/images/workbench/report.png',
@ -34,7 +34,7 @@ Page({
{ {
iconPath: '/images/workbench/issues.png', iconPath: '/images/workbench/issues.png',
text: '发现问题', text: '发现问题',
page: '/package/riskManagement/riskManagement' page: '/package/report/report'
}, },
] ]
}, },

5
weapp/project.config.json

@ -43,11 +43,12 @@
"disablePlugins": [], "disablePlugins": [],
"outputPath": "" "outputPath": ""
}, },
"condition": false "condition": false,
"ignoreUploadUnusedFiles": true
}, },
"compileType": "miniprogram", "compileType": "miniprogram",
"libVersion": "2.19.4", "libVersion": "2.19.4",
"appid": "wxdd82ae635b22ccdb", "appid": "wx79ff58f03d17f24d",
"projectname": "miniprogram-92", "projectname": "miniprogram-92",
"condition": {}, "condition": {},
"editorSetting": { "editorSetting": {

22
weapp/utils/getApiUrl.js

@ -33,6 +33,11 @@ exports.getPatrolPlan = () => {
exports.addPatrolRecord = () => { exports.addPatrolRecord = () => {
return `/patrolRecord/add` return `/patrolRecord/add`
} }
//上报问题
exports.reportQuest = () => {
return `/patrolRecord/reportQuest`
}
// 获取巡检记录 // 获取巡检记录
exports.getPatrolRecord = (patrolPlanId, startTime, endTime, alarm, pointId) => { exports.getPatrolRecord = (patrolPlanId, startTime, endTime, alarm, pointId) => {
@ -44,6 +49,13 @@ exports.getSubSystemPatrolAbout = (query) => {
return `/patrolRecord/subSystemPatrolAbout?STime=${STime}&ETime=${ETime}&keywords=${keywords}` return `/patrolRecord/subSystemPatrolAbout?STime=${STime}&ETime=${ETime}&keywords=${keywords}`
} }
// 获取点位信息
exports.getPointList = (query) => {
const {keywords } = query;
return `/patrolRecord/pointInfo?keywords=${keywords}`
}
// 获取点位最新一条巡检记录 // 获取点位最新一条巡检记录
exports.getdPointCurPatrolRecord = (pointId) => { exports.getdPointCurPatrolRecord = (pointId) => {
return `/patrolRecord/${pointId}/cur` return `/patrolRecord/${pointId}/cur`
@ -53,6 +65,11 @@ exports.getdPointCurPatrolRecord = (pointId) => {
exports.getPatrolTemplate = (id) => { exports.getPatrolTemplate = (id) => {
return `/patrolTemplate?id=${id}` return `/patrolTemplate?id=${id}`
} }
//根据结构物获取巡检模板
exports.getTemplates = (query) => {
const {keywords } = query;
return `/patrolRecord/getTemplate?keywords=${keywords}`
}
exports.getPatrolRecordIssueHandle = () => { exports.getPatrolRecordIssueHandle = () => {
return `/patrolRecord/issue/handle` return `/patrolRecord/issue/handle`
@ -80,3 +97,8 @@ exports.getPatrolReport = (query) => {
const { projectId, startTime, endTime } = query; const { projectId, startTime, endTime } = query;
return `/patrolReport?projectId=${projectId}&startTime=${startTime}&endTime=${endTime}` return `/patrolReport?projectId=${projectId}&startTime=${startTime}&endTime=${endTime}`
} }
// 故障风险管理统计
exports.getPatrolRecordStatistic = (structures) => {
return `/patrolRecord/statistic?structures=${structures}`
}

8
web/Dockerfile

@ -1,6 +1,6 @@
#FROM repository.anxinyun.cn/base-images/nodejs12:20.10.12.2 #FROM repository.anxinyun.cn/base-images/nodejs12:20.10.12.2
FROM repository.anxinyun.cn/base-images/nodejs12:20.10.12.2 FROM repository.anxinyun.cn/base-images/nodejs12:20.10.12.2
COPY . /var/app COPY ./web/ /var/app
WORKDIR /var/app WORKDIR /var/app
EXPOSE 8080 EXPOSE 8080
@ -17,15 +17,15 @@ RUN apk update && apk add --no-cache \
giflib-dev \ giflib-dev \
python \ python \
; ;
RUN npm config set registry=http://10.8.30.22:7000 RUN npm config set registry=https://nexus.ngaiot.com/repository/fs-npm/
RUN npm cache clean -f RUN npm cache clean -f
#RUN npm install -g node-gyp #RUN npm install -g node-gyp
RUN rm -rf package-lock.json RUN rm -rf package-lock.json
RUN npm install --registry http://10.8.30.22:7000 RUN npm install --registry https://nexus.ngaiot.com/repository/fs-npm/
RUN npm run build RUN npm run build
RUN rm -rf client/src RUN rm -rf client/src
RUN rm -rf node_modules RUN rm -rf node_modules
RUN npm install --production --registry http://10.8.30.22:7000 RUN npm install --production --registry https://nexus.ngaiot.com/repository/fs-npm/
#RUN npm cache clean -f && npm install --production --force --registry http://10.8.30.22:7000 #RUN npm cache clean -f && npm install --production --force --registry http://10.8.30.22:7000
CMD ["-u", "http://localhost:8088"] CMD ["-u", "http://localhost:8088"]
ENTRYPOINT [ "node", "server.js" ] ENTRYPOINT [ "node", "server.js" ]

BIN
web/client/assets/images/menu/device.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 577 B

4
web/client/src/app.js

@ -9,6 +9,8 @@ import Organization from './sections/organization';
import PatrolManage from './sections/patrolManage'; import PatrolManage from './sections/patrolManage';
import IssueHandle from './sections/issueHandle' import IssueHandle from './sections/issueHandle'
import Shouye from './sections/shouye'; import Shouye from './sections/shouye';
import DeviceManage from './sections/deviceManage';
import { Func } from '$utils'; import { Func } from '$utils';
const App = props => { const App = props => {
const { projectName } = props const { projectName } = props
@ -20,7 +22,7 @@ const App = props => {
return ( return (
<Layout <Layout
title={projectName} title={projectName}
sections={[Auth,Shouye, ProjectRegime, Safetymanage, Organization, PatrolManage, IssueHandle]} sections={[Auth, Shouye, ProjectRegime, Safetymanage, Organization, PatrolManage, IssueHandle, DeviceManage]}
/> />
) )

56
web/client/src/sections/deviceManage/actions/device.js

@ -0,0 +1,56 @@
'use strict';
import { basicAction } from '@peace/utils'
import { ApiTable } from '$utils'
export function getDeviceList(query) {
return dispatch => basicAction({
type: 'get',
dispatch: dispatch,
query: query || {},
actionType: 'GET_Device_REPORT',
url: `${ApiTable.getDeviceList}`,
msg: { error: '获取设备列表失败' },
reducer: { name: 'device' }
});
}
export function addDevice(params) {
return (dispatch) => basicAction({
type: 'post',
data: params,
dispatch,
actionType: 'ADD_Device_REPORT',
url: ApiTable.addDevice,
msg: {
option: '设备新增',
},
});
}
export function deleteDevice(id) {
return (dispatch) => basicAction({
type: 'del',
dispatch,
actionType: 'DELETE_Device_REPORT',
url: ApiTable.modifyDevice.replace('{id}', id),
msg: {
option: '设备删除',
},
});
}
export function modifyDevice(id, params, msg) {
return (dispatch) => basicAction({
type: 'put',
data: params,
dispatch,
actionType: 'MODIFY_Device_REPORT',
url: ApiTable.modifyDevice.replace('{id}', id),
msg: {
option: msg || '设备编辑',
},
});
}

7
web/client/src/sections/deviceManage/actions/index.js

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

213
web/client/src/sections/deviceManage/components/importDevicesModal.js

@ -0,0 +1,213 @@
'use strict';
import React, { useState, useEffect } from 'react';
import { connect } from 'react-redux';
import moment from 'moment';
import { Modal, Form, Button, Upload, message } from 'antd';
import { DownloadOutlined } from '@ant-design/icons';
import XLSX from 'xlsx';
import { DEVICE_TYPES } from './modelModal';
// import porvince from './province.json'
const workerKeys = {
name: '设备名称',
type: '设备类型',
specification: '规格型号',
dateProduced: '生产日期',
dateInstall: '安装时间',
dateGuarantee: '质保期',
dateMainten: '维保期',
}
//下载模板和上传文件读取
const ImportDeviceModal = props => {
const { dispatch, actions, onCancel, onOk, devices } = props;
const { deviceManage } = actions;
const [msg, setMsg] = useState('');
const [loading, setLoading] = useState(false);
const [postData, setPostData] = useState([]);
//初始化
useEffect(() => {
}, []);
const confirm = () => {
if (postData.length) {
setLoading(true)
//导入明细接口通用
dispatch(deviceManage?.addDevice(postData)).then(res => {
if (res.success) {
onOk()
}
setLoading(false)
})
} else {
message.warning({ content: '没有数据可以提交,请上传数据文件', duration: 2 })
}
}
const dldCsvMb = () => {
//表头
let head = [];
Object.keys(workerKeys).map(key => {
head.push(workerKeys[key]);
})
head = head.join(',') + "\n";
//数据
//let data = 1 + ',' + 2 + ',' + 3 + ',' + 4 + ',' + 5
let templateCsv = "data:text/xls;charset=utf-8,\ufeff" + head;
//创建一个a标签
let link = document.createElement("a");
//为a标签设置属性
link.setAttribute("href", templateCsv);
link.setAttribute("download", `设备导入模板.xls`);
//点击a标签
link.click();
}
const download = () => {
dldCsvMb();
}
const judgeTimeValid = (v) => {
let valid = true;
if (v.split('/').length !== 3) {
valid = false;
} else {
let time = new Date(v);
// if (!time) {
// return valid;//可以不填
// }
const ymd = /^((19|20)[0-9]{2})[\/\-]((0[1-9])|(1[0-2]))[\/\-]((0[1-9])|((1|2)[0-9])|(3[0-1]))$/;//年月日
if (time instanceof Date) {
let timeStr = moment(time).format('YYYY/MM/DD');
if (!ymd.test(timeStr)) {
valid = false;
}
} else {
valid = false;
}
}
return valid;
}
return (<Modal
title={"导入设备信息"} visible={true}
onOk={confirm} width={520}
confirmLoading={loading}
onCancel={() => {
setMsg('')
setLoading(false)
setPostData([])
onCancel()
}}
>
<Form>
<Upload
action={'/'} accept={'.xls,.xlsx'}
maxCount={1}
onRemove={(currentFile, fileList, fileItem) => {
setMsg('');
setPostData([]);
}}
customRequest={async (data) => {
const { file, onSuccess, onError } = data
const error = (msg) => {
setMsg(msg)
onError({ message: msg })
}
let isLt = file.size / 1024 / 1024 < 200
if (!isLt) {
error(`文件最大不超过200M`);
return;
}
const reader = new FileReader();
reader.onload = function (e) {
const data = e.target.result;
const workbook = XLSX.read(data, { type: 'binary', codepage: 936 });
const firstSheetName = workbook.SheetNames[0];
const worksheet = workbook.Sheets[firstSheetName];
//XLSX.utils.sheet_to_json解析 ------把身份证号和手机号变成 字符串------
Object.keys(worksheet).forEach(k => {
worksheet[k].w ? worksheet[k].v = worksheet[k].w : ''
})
//------------------end------------------
const res = XLSX.utils.sheet_to_json(worksheet);
if (res.length > 1000) {
error('一次性上传数据行数应小于1000行,请分批上传')
return
}
if (!res.length) {
error('请填写至少一行数据')
return
}
let postData = []
for (let i = 0; i < res.length; i++) {
let d = res[i]
let obj = {};
Object.keys(workerKeys).map(key => {
obj[key] = d[workerKeys[key]] || null;
})
//必填项
let notNullKeys = ['name', 'type', 'specification', 'dateProduced', 'dateGuarantee', 'dateMainten', 'dateInstall']
for (let k = 0; k < notNullKeys.length; k++) {
let key = notNullKeys[k];
if (!obj[key]) {
error(`${i + 2}行【${workerKeys[key]}】不能为空`)
return
}
}
//判断设备类型
let ext = DEVICE_TYPES.find(m => m == obj.type);
if (!ext) {
error(`${i + 2}行的【设备类型】错误,请填写【${DEVICE_TYPES.toString()}】中的一种`)
return
}
let tValid = judgeTimeValid(obj.dateProduced) && judgeTimeValid(obj.dateGuarantee)
&& judgeTimeValid(obj.dateMainten) && judgeTimeValid(obj.dateInstall);
if (!tValid) {
error(`${i + 2}行【日期格式】错误,请填写yyyy/mm/dd格式`)
return;
}
let dateInstallValid = moment(obj.dateInstall).valueOf() > moment().startOf('d').add(1, 'd').valueOf();
if (dateInstallValid) {
error(`${i + 2}行【安装日期】不能填写今天之后的时间`)
return;
}
postData.push(obj)
}
setPostData(postData)
let msg = '文件解析完成,点击确定按钮上传保存!'
setMsg(msg)
onSuccess({ message: msg })
}
reader.readAsBinaryString(file);
}}>
<Button icon={<DownloadOutlined />} theme="light">
请选择文件
</Button>
</Upload>
<span>{msg}</span>
<div style={{ color: '#ccc', marginTop: 20 }}>最大不超过200M导入文件需与
<span onClick={() => download()} style={{ cursor: 'pointer', color: '#0066FF' }}>导入模板</span>
一致</div>
</Form>
</Modal>)
}
function mapStateToProps(state) {
const { auth, global } = state;
return {
user: auth.user,
actions: global.actions,
}
}
export default connect(mapStateToProps)(ImportDeviceModal);

111
web/client/src/sections/deviceManage/components/modelModal.js

@ -0,0 +1,111 @@
import React, { useRef } from 'react';
import { Button, Form } from 'antd';
import { InfoCircleOutlined } from '@ant-design/icons';
import {
ModalForm,
ProFormSelect,
ProFormText,
ProFormDatePicker
} from '@ant-design/pro-form';
import moment from 'moment';
export default (props) => {
const { title, triggerRender, editData = null, onFinish, devices } = props;
const formItemLayout = { labelCol: { span: 6 }, wrapperCol: { span: 16 } };
const initialValues = editData ? {
...editData,
} : {};
const [form] = Form.useForm();
const formRef = useRef();
const disabledDate = (value) => {
return value.valueOf() > moment().startOf('d').add(1, 'd').valueOf();
}
return (
<ModalForm
formRef={formRef}
title={title || ''}
initialValues={initialValues}
trigger={
triggerRender ? triggerRender : <Button type="primary" >
{title || ''}
</Button>
}
layout="horizontal"
grid={true}
{...formItemLayout}
modalProps={{
destroyOnClose: true,
onCancel: () => { },
}}
onFinish={async (values) => {
return onFinish && await onFinish(values, editData, form)
// return true;
}}
width={500}
>
<ProFormText
rules={[
{ required: true, message: '请输入设备名称' },
{ max: 255, message: '设备名称长度不能大于255个字符' },
]}
name="name"
label="设备名称"
/>
<ProFormSelect
rules={[{ required: true, message: '请选择设备类型' }]}
options={
DEVICE_TYPES
.map(s => {
return { label: s, value: s }
})
}
name="type"
label="设备类型"
/>
<ProFormText
rules={[
{ required: true, message: '请输入规格型号' },
{ max: 20, message: '规格型号长度不能大于20个字符' },
]}
name="specification"
label="规格型号"
/>
<ProFormDatePicker
rules={[{ required: true, message: '请输入生产日期' }]}
name="dateProduced"
label="生产日期"
fieldProps={
{ disabledDate: disabledDate }
}
/>
<ProFormDatePicker
rules={[{ required: true, message: '请输入安装日期' }]}
name="dateInstall"
label="安装日期"
fieldProps={
{ disabledDate: disabledDate }
}
/>
<ProFormDatePicker
rules={[{ required: true, message: '请输入质保期' }]}
name="dateGuarantee"
label="质保期"
/>
<ProFormDatePicker
rules={[{ required: true, message: '请输入维保期' }]}
name="dateMainten"
label="维保期"
/>
</ModalForm>
);
};
export const DEVICE_TYPES = ['安防系统', '厨房系统', '电梯', '供电系统', '空调', '排水系统', '水系统', '通道门禁',
'通风系统', '通信系统', '显示视频', '消防系统', '照明系统']

0
web/client/src/sections/deviceManage/constants/index.js

254
web/client/src/sections/deviceManage/containers/deviceManage.js

@ -0,0 +1,254 @@
import React, { useEffect, useState } from 'react'
import { Spin, Popconfirm, message, Button, Input } from 'antd';
import { connect } from 'react-redux';
import ProTable from '@ant-design/pro-table';
import DeviceModal from '../components/modelModal'
import moment from 'moment';
import ImportDeviceModal from '../components/importDevicesModal'
function DeviceManagement(props) {
const { loading, clientHeight, actions, dispatch, devices } = props;
const [pageSize, setPageSize] = useState(10);
const [currentPage, setCurrentPage] = useState(1);
const [rowSelected, setRowSelected] = useState([])
const [showImportModal, setShowImportModal] = useState(false);
const [name, setName] = useState();
const queryData = (search) => {
const query = {
limit: search ? 10 : pageSize || 10,
page: search ? 1 : currentPage || 1,
name: name
}
dispatch(actions.deviceManage.getDeviceList(query));
}
useEffect(() => {
queryData();
}, [pageSize, currentPage]);
const handleDelete = (id) => {
dispatch(actions.deviceManage.deleteDevice(id)).then(() => {
queryData();
setRowSelected([])
});
};
const onFinish = async (values, editData) => {
if (editData) {
const dataToSave = { ...values }
return dispatch(
actions.deviceManage.modifyDevice(editData.id, dataToSave, values?.msg || ''),
).then((res) => {
if (res.success) {
queryData();
return true;
} else {
return false;
}
});
}
return dispatch(actions.deviceManage.addDevice({
...values,
})).then(res => {
if (res.success) {
queryData();
return true;
} else {
return false;
}
});
};
const columns = [
{
title: '设备名称',
dataIndex: 'name',
ellipsis: true,
},
{
title: '设备类型',
dataIndex: 'type',
ellipsis: true,
},
{
title: '规格型号',
dataIndex: 'specification',
ellipsis: true,
},
{
title: '生产日期',
dataIndex: 'dateProduced',
ellipsis: true,
},
{
title: '安装时间',
dataIndex: 'dateInstall',
ellipsis: true,
},
{
title: '质保期',
dataIndex: 'dateGuarantee',
ellipsis: true,
search: false,
},
{
title: '维保期',
dataIndex: 'dateMainten',
ellipsis: true,
},
{
title: '设备投入使用时长',
dataIndex: 'length',
ellipsis: true,
render: (text, record) => {
const start = moment(record?.dateInstall);
const end = moment();
const days = end.diff(start, 'days');
return days + '天'
}
},
{
title: '操作',
width: 160,
key: 'option',
valueType: 'option',
render: (text, record) => {
const options = [];
options.push(<DeviceModal
triggerRender={<a>编辑</a>}
editData={record}
title="编辑设备"
onFinish={onFinish}
key="editModel"
/>)
options.push(
<Popconfirm
key="del"
placement="top"
title="是否确认删除该设备?"
onConfirm={() => handleDelete(record.id)}
okText="是"
cancelText="否"
>
<a>删除</a>
</Popconfirm>)
return options;
},
},
];
return <div id='patrol-record' className='global-main'>
<Spin spinning={loading}>
<div style={{ marginBottom: 19 }}>
<div className='top' style={{ marginBottom: 19 }}>
<div className='title'>
<span className='line'></span>
<span className='cn'>设备管理</span>
<span className='en'>&nbsp;DEVICE</span>
</div>
<div>
<DeviceModal
triggerRender={<Button type='primary'>新建</Button>}
title="新建设备"
onFinish={onFinish}
key="addModel"
/>
<Button type="primary" style={{ marginRight: 10, marginLeft: 10 }} onClick={() => { setShowImportModal(true) }}>批量新增</Button>
<Popconfirm title="确认删除?" onConfirm={() => {
rowSelected?.length > 0 ? handleDelete(rowSelected?.toString()) : message.warning('请先选择要删除的设备')
}}>
<Button>批量删除</Button>
</Popconfirm>
<Input onChange={e => setName(e?.target?.value)} style={{ width: '13vw', marginLeft: 20, marginRight: 10 }} />
<Button type="primary" onClick={() => {
setPageSize(10)
setCurrentPage(1)
queryData(true)
}}>查询</Button>
</div>
</div>
</div>
<ProTable
columns={columns}
rowKey="id"
dateFormatter="string"
scroll={
{
scrollToFirstRowOnChange: true,
y: clientHeight - 260
}
}
pagination={{
size: 'large',
total: devices?.count,
showSizeChanger: true,
// showQuickJumper: true,
current: currentPage,
pageSize: pageSize || 10,
pageSizeOptions: [10, 20, 50],
showTotal: (total) => {
return <span style={{ fontSize: 15 }}>{`${Math.ceil(total / pageSize)}页,${total}`}</span>
},
onShowSizeChange: (currentPage, pageSize) => {
setCurrentPage(currentPage);
setPageSize(pageSize);
},
onChange: (page, pageSize) => {
setCurrentPage(page);
setPageSize(pageSize);
}
}}
dataSource={devices?.rows || []}
rowSelection={{
selectedRowKeys: rowSelected,
onChange: (selectedRowKeys) => {
setRowSelected(selectedRowKeys);
},
getCheckboxProps: (record) => {
return {
disabled: record.username === 'SuperAdmin',
}
}
}}
options={false}
search={false}
/>
{showImportModal && <ImportDeviceModal
devices={devices?.rows || []}
onCancel={() => {
setShowImportModal(false)
}}
onOk={() => {
setShowImportModal(false)
queryData()
}}
/>}
</Spin>
</div>
}
function mapStateToProps(state) {
const {
auth, global, device
} = state;
return {
loading: device.isRequesting,
clientHeight: global.clientHeight,
actions: global.actions,
devices: device?.data || {}
};
}
export default connect(mapStateToProps)(DeviceManagement);

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

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

0
web/client/src/sections/deviceManage/containers/style.css

0
web/client/src/sections/deviceManage/containers/style.less

15
web/client/src/sections/deviceManage/index.js

@ -0,0 +1,15 @@
'use strict';
import reducers from './reducers';
import routes from './routes';
import actions from './actions';
import { getNavItem } from './nav-item';
export default {
key: 'deviceManage',
name: '设备管理',
reducers: reducers,
routes: routes,
actions: actions,
getNavItem: getNavItem
};

13
web/client/src/sections/deviceManage/nav-item.js

@ -0,0 +1,13 @@
import React from 'react';
import { Link } from 'react-router-dom';
import { Menu } from 'antd';
import { Func } from '$utils';
export function getNavItem(user, dispatch) {
return (
<Menu.Item icon={<img src='/assets/images/menu/device.png' style={{ width: 24, height: 24 }} />}
key="deviceManage">
<Link to="/deviceManage">设备管理</Link>
</Menu.Item>
);
}

5
web/client/src/sections/deviceManage/reducers/index.js

@ -0,0 +1,5 @@
'use strict';
export default {
}

13
web/client/src/sections/deviceManage/routes.js

@ -0,0 +1,13 @@
'use strict';
import { DeviceManage } from './containers';
export default [{
type: 'inner',
route: {
path: '/deviceManage',
key: 'deviceManage',
breadcrumb: '设备管理',
component: DeviceManage,
}
}];

33
web/client/src/sections/issueHandle/components/isuue-handle-mdal.js

@ -70,28 +70,38 @@ export default (props) => {
<div className="item-title">{"问题详情"}</div> <div className="item-title">{"问题详情"}</div>
<Row> <Row>
<Col span={6}> <Col span={12}>
<Form.Item label="点位名称:" {...formItemLayout}> <Form.Item label="点位名称:" {...formItemLayout}>
<Input value={editData?.points?.itemData?.name} readOnly /> <Input value={editData?.points?.itemData?.name} readOnly />
</Form.Item> </Form.Item>
</Col> </Col>
</Row>
<Col span={18}> <Row>
<Col span={24}>
{ {
editData?.points?.inspectContent && Object.keys(editData?.points?.inspectContent).map(key => { editData?.points?.inspectContent && Array.isArray(editData?.points?.inspectContent) &&
if (editData?.points?.inspectContent[key]?.isNormal == false) { editData?.points?.inspectContent?.map(s => {
if (s?.alarm == true) {
return <>
{s?.deviceName && <Col span={12}>
<Form.Item label="设备名称:" {...formItemLayout}>
<Input value={s?.deviceName} readOnly />
</Form.Item>
</Col>}
{
s?.checkItems?.map(k => {
return <Row style={{ marginBottom: 15 }}> return <Row style={{ marginBottom: 15 }}>
<Col span={12}> <Col span={12}>
<Form.Item label="检查项:" {...formItemLayout}> <Form.Item label="检查项:" {...formItemLayout}>
<Input value={key} readOnly /> <Input value={k.name} readOnly />
</Form.Item> </Form.Item>
<Form.Item label="异常等级:" {...formItemLayout}> <Form.Item label="异常等级:" {...formItemLayout}>
<Input value={editData?.points?.inspectContent[key]?.level} readOnly /> <Input value={k?.level} readOnly />
</Form.Item> </Form.Item>
</Col> </Col>
<Col span={12}> <Col span={12}>
<Form.Item label="问题描述:" {...formItemLayout}> <Form.Item label="问题描述:" {...formItemLayout}>
<Input value={editData?.points?.inspectContent[key]?.msgInp} readOnly title={222} /> <Input value={k?.msgInp} readOnly title={222} />
</Form.Item> </Form.Item>
<Form.Item label="现场图片:" {...formItemLayout}> <Form.Item label="现场图片:" {...formItemLayout}>
@ -106,7 +116,7 @@ export default (props) => {
defaultValue={ defaultValue={
(() => { (() => {
let nextV = [] let nextV = []
for (let s of (editData?.points?.inspectContent[key].imgs || [])) { for (let s of (k.imgs || [])) {
if (s) { if (s) {
nextV.push({ nextV.push({
storageUrl: s storageUrl: s
@ -121,6 +131,11 @@ export default (props) => {
</Form.Item> </Form.Item>
</Col> </Col>
</Row> </Row>
})
}
</>
} }
}) })
} }

2
web/client/src/sections/issueHandle/containers/patrolRecord.js

@ -119,7 +119,7 @@ const PatrolRecord = (props) => {
dataIndex: 'source', dataIndex: 'source',
key: 'source', key: 'source',
showInDetail: true, showInDetail: true,
render: (text, record, index) => '巡检上报' //暂定巡检上报 后续会增加平台录入 render: (text, record, index) => record?.patrolPlanId == -1 ? '主动上报' : '巡检上报' //暂定巡检上报 后续会增加平台录入
}, },
{ {
title: '严重等级', title: '严重等级',

89
web/client/src/sections/patrolManage/containers/patrolRecord.js

@ -43,7 +43,7 @@ const PatrolRecord = (props) => {
width: '10%', width: '10%',
showInDetail: true, showInDetail: true,
render: (text, record, index) => { render: (text, record, index) => {
return !record.points?.project ? '' : <div>{record.points.project.name}</div> return !record.points?.project ? '--' : <div>{record.points.project.name}</div>
} }
}, { }, {
title: '巡检计划', title: '巡检计划',
@ -52,7 +52,7 @@ const PatrolRecord = (props) => {
width: '10%', width: '10%',
showInDetail: true, showInDetail: true,
render: (text, record, index) => { render: (text, record, index) => {
return !record.patrolPlan ? '' : <div>{record.patrolPlan.name}</div> return !record.patrolPlan ? '--' : <div>{record?.patrolPlan?.name || '-'}</div>
} }
}, { }, {
title: '巡检点位', title: '巡检点位',
@ -61,7 +61,7 @@ const PatrolRecord = (props) => {
showInDetail: true, showInDetail: true,
width: '10%', width: '10%',
render: (text, record, index) => { render: (text, record, index) => {
return !record.points?.user ? '' : <div>{record.points.itemData.name}</div> return !record.points?.user ? '--' : <div>{record.points.itemData.name}</div>
} }
}, { }, {
title: '巡检人', title: '巡检人',
@ -70,7 +70,7 @@ const PatrolRecord = (props) => {
showInDetail: true, showInDetail: true,
width: '10%', width: '10%',
render: (text, record, index) => { render: (text, record, index) => {
return !record.points?.user ? '' : <div>{record.points.user.name}</div> return !record.points?.user ? '--' : <div>{record.points.user.name}</div>
} }
}, { }, {
title: '巡检单位', title: '巡检单位',
@ -79,7 +79,7 @@ const PatrolRecord = (props) => {
key: 'type', key: 'type',
width: '10%', width: '10%',
render: (text, record, index) => { render: (text, record, index) => {
return !record.points?.user ? '' : <div>{record.points.user.department.name}</div> return !record.points?.user ? '--' : <div>{record.points.user.department.name}</div>
} }
}, { }, {
title: '巡检频次', title: '巡检频次',
@ -88,7 +88,7 @@ const PatrolRecord = (props) => {
showInDetail: true, showInDetail: true,
width: '10%', width: '10%',
render: (text, record, index) => { render: (text, record, index) => {
return !record.points ? '' : <div>{record.points.frequency}</div> return !record.points ? '--' : <div>{record.points.frequency || '-'}</div>
} }
}, { }, {
title: '上次巡检日期', title: '上次巡检日期',
@ -207,22 +207,83 @@ const PatrolRecord = (props) => {
} }
}) })
if (modelData && modelData.points && modelData.points.inspectContent) { if (modelData && modelData.points && modelData.points.inspectContent) {
if (modelData?.points?.itemData?.pointDevices?.length > 0) {
let inspectContent = modelData.points.inspectContent let inspectContent = modelData.points.inspectContent
for (let k in inspectContent) { inspectContent?.map(s => {
dataArr.push( dataArr.push(
<> <>
<Row> <Row>
<Col span={5} style={{}}>{k} </Col> <Col span={5} style={{}}>{s?.deviceName} </Col>
<Col span={19}> <Col span={19}>
{ {
inspectContent[k].isNormal ? '正常' : !s.alarm ? '正常' :
<Collapse ghost style={{ padding: 0 }}>
<Panel header="异常" key="1" style={{ padding: 0 }}>
{s?.checkItems?.map(k => {
return <>
<Row style={{ fontWeight: 'bold' }}>{k.name}:</Row>
<Row style={{ marginBottom: 20 }}>
<Col span={4} style={{}}>巡查详情 </Col>
<Col span={20}>{k.msgInp}</Col>
<Col span={4} style={{}}>严重等级 </Col>
<Col span={20}>{k.level}</Col>
<Col span={4} style={{}}>现场照片 </Col>
<Col span={20}>
<Uploads
listType='picture-card'
uploadType='project'
maxFilesNum={1}
maxFileSize={10}
isQiniu={true}
disabled={true}
fileTypes={["png", "jpg"]}
defaultValue={
(() => {
let nextV = []
for (let s of (k.imgs || [])) {
if (s) {
nextV.push({
storageUrl: s
})
}
}
return nextV
})()
}
/>
</Col>
</Row>
</>
})}
</Panel>
</Collapse>
}
</Col>
</Row>
</>
)
})
} else {
let inspectContent = modelData.points.inspectContent
for (let v in inspectContent) {
inspectContent[v]?.checkItems?.map(k => {
dataArr.push(
<>
<Row>
<Col span={5} style={{}}>{k?.name} </Col>
<Col span={19}>
{
!inspectContent[v].alarm ? '正常' :
<Collapse ghost style={{ padding: 0 }}> <Collapse ghost style={{ padding: 0 }}>
<Panel header="异常" key="1" style={{ padding: 0 }}> <Panel header="异常" key="1" style={{ padding: 0 }}>
<Row> <Row>
<Col span={4} style={{}}>巡查详情 </Col> <Col span={4} style={{}}>巡查详情 </Col>
<Col span={20}>{inspectContent[k].msgInp}</Col> <Col span={20}>{k.msgInp}</Col>
<Col span={4} style={{}}>严重等级 </Col> <Col span={4} style={{}}>严重等级 </Col>
<Col span={20}>{inspectContent[k].level}</Col> <Col span={20}>{k.level}</Col>
<Col span={4} style={{}}>现场照片 </Col> <Col span={4} style={{}}>现场照片 </Col>
<Col span={20}> <Col span={20}>
<Uploads <Uploads
@ -236,7 +297,7 @@ const PatrolRecord = (props) => {
defaultValue={ defaultValue={
(() => { (() => {
let nextV = [] let nextV = []
for (let s of (inspectContent[k].imgs || [])) { for (let s of (k.imgs || [])) {
if (s) { if (s) {
nextV.push({ nextV.push({
storageUrl: s storageUrl: s
@ -256,7 +317,11 @@ const PatrolRecord = (props) => {
</Row> </Row>
</> </>
) )
})
}
} }
} }
return dataArr return dataArr
})()} })()}

12
web/client/src/sections/projectRegime/components/pointModel.js

@ -7,7 +7,7 @@ import Uploads from '$components/Uploads';
import { useEffect } from 'react'; import { useEffect } from 'react';
import moment from 'moment'; import moment from 'moment';
const ProjectAddModel = ({ dispatch, actions, user, modelData, close, success, qrCodeId }) => { const ProjectAddModel = ({ dispatch, actions, user, modelData, close, success, qrCodeId, devices }) => {
const { projectRegime } = actions const { projectRegime } = actions
const [showBaiduMap, setShowBaiduMap] = useState(false) const [showBaiduMap, setShowBaiduMap] = useState(false)
@ -172,6 +172,16 @@ const ProjectAddModel = ({ dispatch, actions, user, modelData, close, success, q
initialValue={modelData?.equipmentModel}> initialValue={modelData?.equipmentModel}>
<Input placeholder="请输入设备型号" allowClear /> <Input placeholder="请输入设备型号" allowClear />
</Form.Item> </Form.Item>
<Form.Item label='设备绑定' name="devices" style={{}}
initialValue={modelData?.pointDevices?.map(s => s?.deviceId) || []}>
<Select mode="multiple">
{
devices?.map(s => <Select.Option
disabled={s?.pointDevices?.length > 0 && !s?.pointDevices?.find(x => x.pointId == modelData?.id)}
value={s.id} >{s?.name}</Select.Option>)
}
</Select>
</Form.Item>
<Form.Item <Form.Item
label="点位图片" label="点位图片"
name='img' name='img'

32
web/client/src/sections/projectRegime/components/projectAddModel.js

@ -6,11 +6,15 @@ import { connect } from 'react-redux';
import Uploads from '$components/Uploads'; import Uploads from '$components/Uploads';
import { useEffect } from 'react'; import { useEffect } from 'react';
// import moment from 'moment'; // import moment from 'moment';
const type_options = [
{ value: '桥梁', label: '桥梁' },
{ value: '隧道', label: '隧道' },
{ value: '管廊', label: '管廊' }]
const ProjectAddModel = ({ dispatch, actions, user, modelData, close, success, firmList }) => { const ProjectAddModel = ({ dispatch, actions, user, modelData, close, success, firmList }) => {
const { projectRegime } = actions const { projectRegime } = actions
const [showBaiduMap, setShowBaiduMap] = useState(false) const [showBaiduMap, setShowBaiduMap] = useState(false)
const [type, setType] = useState(modelData?.type || type_options[0])
const [form] = Form.useForm(); const [form] = Form.useForm();
useEffect(() => { useEffect(() => {
@ -103,15 +107,33 @@ const ProjectAddModel = ({ dispatch, actions, user, modelData, close, success, f
// rules={[{ required: true, message: '请选择结构物类型' },]} // rules={[{ required: true, message: '请选择结构物类型' },]}
> >
<Select <Select
onChange={value => { setType(value) }}
bordered={false} bordered={false}
allowClear allowClear
options={[ options={type_options}
{ value: '桥梁', label: '桥梁' },
{ value: '隧道', label: '隧道' },
{ value: '管廊', label: '管廊' }]}
/> />
</Form.Item> </Form.Item>
</div> </div>
{type == '管廊' && <Form.Item label='子系统' name="subType"
initialValue={modelData?.subType || '指挥中心'}
rules={[{ required: true, message: '请选择子系统' }]}
>
<Select
bordered={false}
options={[
{ value: '指挥中心', label: '指挥中心' },
{ value: '管廊本体', label: '管廊本体' },
{ value: '电梯系统', label: '电梯系统' },
{ value: '供配电系统', label: '供配电系统' },
{ value: '防雷与接地系统', label: '防雷与接地系统' },
{ value: '燃气仓', label: '燃气仓' },
{ value: '给水仓', label: '给水仓' },
{ value: '电气仓', label: '电气仓' },
{ value: '安防系统', label: '安防系统' },
{ value: '高压电力仓', label: '高压电力仓' },
]}
/>
</Form.Item>}
<div style={{ position: 'relative' }}> <div style={{ position: 'relative' }}>
<Form.Item label="所在地区:" labelCol={{ span: 9 }} labelAlign='right' name="longitude" style={{ display: 'inline-block', width: 'calc(60% - 50px)', }} <Form.Item label="所在地区:" labelCol={{ span: 9 }} labelAlign='right' name="longitude" style={{ display: 'inline-block', width: 'calc(60% - 50px)', }}
rules={[{ required: true, message: '', }, { rules={[{ required: true, message: '', }, {

7
web/client/src/sections/projectRegime/containers/point.js

@ -5,7 +5,7 @@ import '../style.less';
import PointModel from '../components/pointModel' import PointModel from '../components/pointModel'
const Information = (props) => { const Information = (props) => {
const { dispatch, actions } = props const { dispatch, actions, devices } = props
const { projectRegime } = actions const { projectRegime } = actions
const [tableList, settableList] = useState([]) const [tableList, settableList] = useState([])
const [addModel, setAddModel] = useState(false) const [addModel, setAddModel] = useState(false)
@ -28,6 +28,7 @@ const Information = (props) => {
const projectList = (obj) => { const projectList = (obj) => {
const { limit, page } = obj const { limit, page } = obj
dispatch(actions.deviceManage.getDeviceList());
dispatch(projectRegime.positionList({ limit, page: 0, projectId: qrCodeId })).then(res => { dispatch(projectRegime.positionList({ limit, page: 0, projectId: qrCodeId })).then(res => {
if (res.success) { if (res.success) {
let data = [] let data = []
@ -166,6 +167,7 @@ const Information = (props) => {
<PointModel <PointModel
modelData={modelData} modelData={modelData}
qrCodeId={qrCodeId} qrCodeId={qrCodeId}
devices={devices}
close={() => { close={() => {
setAddModel(false) setAddModel(false)
setModelData({}) setModelData({})
@ -183,10 +185,11 @@ const Information = (props) => {
} }
function mapStateToProps(state) { function mapStateToProps(state) {
const { auth, global } = state; const { auth, global, device } = state;
return { return {
user: auth.user, user: auth.user,
actions: global.actions, actions: global.actions,
devices: device?.data?.rows || []
}; };
} }

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

@ -139,6 +139,11 @@ export const ApiTable = {
getProjectPoints: 'project/{projectId}/all/points', getProjectPoints: 'project/{projectId}/all/points',
getDeployPoints: 'picture/{pictureId}/deploy/points', getDeployPoints: 'picture/{pictureId}/deploy/points',
setDeployPoints: 'set/picture/{pictureId}/deploy/points', setDeployPoints: 'set/picture/{pictureId}/deploy/points',
//设备管理
getDeviceList: 'device',
addDevice: 'device',
modifyDevice: 'device/{id}',
}; };
export const RouteTable = { export const RouteTable = {

Loading…
Cancel
Save