diff --git a/api/Dockerfile b/api/Dockerfile index 10fc786..acd0358 100644 --- a/api/Dockerfile +++ b/api/Dockerfile @@ -1,16 +1,16 @@ FROM registry.cn-hangzhou.aliyuncs.com/fs-devops/node:12-dev as builder -COPY . /var/app +COPY ./api/ /var/app WORKDIR /var/app 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 npm cache clean -f 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 diff --git a/api/app/lib/controllers/patrolManage/patrolRecord.js b/api/app/lib/controllers/patrolManage/patrolRecord.js index 2b5ac30..3129d0f 100644 --- a/api/app/lib/controllers/patrolManage/patrolRecord.js +++ b/api/app/lib/controllers/patrolManage/patrolRecord.js @@ -2,6 +2,7 @@ const moment = require("moment"); + async function findPatrolRecord(ctx, next) { let rslt = []; let error = { name: 'FindError', message: '获取巡检记录失败' }; @@ -414,21 +415,188 @@ function editPatrolRecordIssueHandle(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){ 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] }}, + const {keywords}=ctx.query + let generalInclude = [{model:models.Project,where:{subType :{$like: `%${keywords}%`}}},{model:models.Device}] + rslt=await models.Point.findAll({ 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)) + rslt = rslt.filter(s => userInfo.structure.find(x => x == s.project.userId)) } else { rslt = [] } @@ -438,11 +606,91 @@ function getSubSystemPatrolAbout(opts) { }catch(error){ ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`); 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) { // 参数合法性判断 @@ -473,5 +721,9 @@ module.exports = { getPatrolRecordIssueHandleById, addPatrolRecordIssueHandle, editPatrolRecordIssueHandle, - getSubSystemPatrolAbout + getSubSystemPatrolAbout, + getPatrolRecordStatistic, + getPointInfo, + getTemplate, + reportQuest } \ No newline at end of file diff --git a/api/app/lib/controllers/patrolManage/patrolTemplate.js b/api/app/lib/controllers/patrolManage/patrolTemplate.js index 37ff08b..cebc30a 100644 --- a/api/app/lib/controllers/patrolManage/patrolTemplate.js +++ b/api/app/lib/controllers/patrolManage/patrolTemplate.js @@ -16,7 +16,7 @@ async function getPatrolTemplate (ctx, next) { model: models.CheckItems, }, { model: models.PatrolPlan, - attributes: ['name'], + attributes: ['structure_id','name'], }] }; if (id) { diff --git a/api/app/lib/index.js b/api/app/lib/index.js index 7a63112..7d02a88 100644 --- a/api/app/lib/index.js +++ b/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' }); 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' }) + + + }; diff --git a/api/app/lib/routes/patrolManage/patrolRecord.js b/api/app/lib/routes/patrolManage/patrolRecord.js index 82facc5..efffabd 100644 --- a/api/app/lib/routes/patrolManage/patrolRecord.js +++ b/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 }; 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)) }; \ No newline at end of file diff --git a/jenkinsfile_api b/jenkinsfile_api index ef6c939..4d8761a 100644 --- a/jenkinsfile_api +++ b/jenkinsfile_api @@ -1,18 +1,25 @@ -pipeline { - agent { - node{ - label 'jnlp-slave' - } - } - - stages { - stage('巡检 api ......') { - steps { - buildName "#${BUILD_NUMBER} ~/smartcity/${JOB_NAME}:${IMAGE_VERSION}" - buildDescription "harbor.anxinyun.cn/smartcity/${JOB_NAME}:${IMAGE_VERSION}" - sh 'nerdctl build -t harbor.anxinyun.cn/smartcity/${JOB_NAME}:${IMAGE_VERSION} ./api' - sh 'nerdctl push harbor.anxinyun.cn/smartcity/${JOB_NAME}:${IMAGE_VERSION}' - } - } - } +podTemplate { + node('pod-templ-jenkins-slave-common') { + + 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}" + + container('image-builder') { + sh''' + pwd + ls -al + + /kaniko/executor --context=${BUILD_WORKSPACE} --dockerfile=./api/Dockerfile --destination=${IMAGE_NAME}:${IMAGE_VERSION} --cache=false --cleanup + + ''' + } + + buildName "${IMAGE_NAME_SHORT}:${IMAGE_VERSION}" + buildDescription "${IMAGE_NAME}:${IMAGE_VERSION}" + } + } } \ No newline at end of file diff --git a/jenkinsfile_web b/jenkinsfile_web index 8840491..34dd5f8 100644 --- a/jenkinsfile_web +++ b/jenkinsfile_web @@ -1,18 +1,25 @@ -pipeline { - agent { - node{ - label 'jnlp-slave' - } - } - - stages { - stage('巡检 web ......') { - steps { - buildName "#${BUILD_NUMBER} ~/smartcity/${JOB_NAME}:${IMAGE_VERSION}" - buildDescription "harbor.anxinyun.cn/smartcity/${JOB_NAME}:${IMAGE_VERSION}" - sh 'nerdctl build -t harbor.anxinyun.cn/smartcity/${JOB_NAME}:${IMAGE_VERSION} ./web' - sh 'nerdctl push harbor.anxinyun.cn/smartcity/${JOB_NAME}:${IMAGE_VERSION}' - } - } - } +podTemplate { + node('pod-templ-jenkins-slave-common') { + + 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}" + + container('image-builder') { + sh''' + pwd + ls -al + + /kaniko/executor --context=${BUILD_WORKSPACE} --dockerfile=./web/Dockerfile --destination=${IMAGE_NAME}:${IMAGE_VERSION} --cache=false --cleanup + + ''' + } + + buildName "${IMAGE_NAME_SHORT}:${IMAGE_VERSION}" + buildDescription "${IMAGE_NAME}:${IMAGE_VERSION}" + } + } } \ No newline at end of file diff --git a/weapp/app.json b/weapp/app.json index 2323d4d..25c523a 100644 --- a/weapp/app.json +++ b/weapp/app.json @@ -23,7 +23,10 @@ "pointsStatus/pointsStatus", "subSystem/subSystem", "riskManagement/riskManagement", - "riskManagement/riskCalendar/riskCalendar" + "riskManagement/riskCalendar/riskCalendar", + "deviceBigdataGraph/deviceBigdataGraph", + "deviceBigdataGraph/statusDetail/statusDetail", + "report/report" ] } ], diff --git a/weapp/custom-tab-bar/index.js b/weapp/custom-tab-bar/index.js index 6fa7319..96ddf2c 100644 --- a/weapp/custom-tab-bar/index.js +++ b/weapp/custom-tab-bar/index.js @@ -9,7 +9,7 @@ Component({ let userRole = wx.getStorageSync('userRole'); // 0 表示普通用户 1表示管理员 console.log('userRole', userRole); - if (userRole && userRole.includes('管理')) { + if (userRole && userRole.includes('巡检')) { this.setData({ list: getApp().globalData.managerList }) diff --git a/weapp/images/arrow_right_blue.svg b/weapp/images/arrow_right_blue.svg new file mode 100644 index 0000000..1ab8c04 --- /dev/null +++ b/weapp/images/arrow_right_blue.svg @@ -0,0 +1,3 @@ + + + diff --git a/weapp/images/calendar.png b/weapp/images/calendar.png new file mode 100644 index 0000000..798e6ec Binary files /dev/null and b/weapp/images/calendar.png differ diff --git a/weapp/images/calendar_card_bg.png b/weapp/images/calendar_card_bg.png new file mode 100644 index 0000000..d8eef34 Binary files /dev/null and b/weapp/images/calendar_card_bg.png differ diff --git a/weapp/images/calendar_icon.png b/weapp/images/calendar_icon.png new file mode 100644 index 0000000..798e6ec Binary files /dev/null and b/weapp/images/calendar_icon.png differ diff --git a/weapp/images/device.png b/weapp/images/device.png new file mode 100644 index 0000000..4e14a39 Binary files /dev/null and b/weapp/images/device.png differ diff --git a/weapp/images/down1.png b/weapp/images/down1.png new file mode 100644 index 0000000..28bc27a Binary files /dev/null and b/weapp/images/down1.png differ diff --git a/weapp/images/edit.png b/weapp/images/edit.png new file mode 100644 index 0000000..a4c5467 Binary files /dev/null and b/weapp/images/edit.png differ diff --git a/weapp/images/fault_icon.png b/weapp/images/fault_icon.png new file mode 100644 index 0000000..0b0b509 Binary files /dev/null and b/weapp/images/fault_icon.png differ diff --git a/weapp/images/right_bg.png b/weapp/images/right_bg.png new file mode 100644 index 0000000..9ed3e1f Binary files /dev/null and b/weapp/images/right_bg.png differ diff --git a/weapp/images/right_card_bg.png b/weapp/images/right_card_bg.png new file mode 100644 index 0000000..e9a3cac Binary files /dev/null and b/weapp/images/right_card_bg.png differ diff --git a/weapp/images/right_icon.png b/weapp/images/right_icon.png new file mode 100644 index 0000000..a4c5467 Binary files /dev/null and b/weapp/images/right_icon.png differ diff --git a/weapp/images/shape1.png b/weapp/images/shape1.png new file mode 100644 index 0000000..e86c222 Binary files /dev/null and b/weapp/images/shape1.png differ diff --git a/weapp/images/shape2.png b/weapp/images/shape2.png new file mode 100644 index 0000000..d8eef34 Binary files /dev/null and b/weapp/images/shape2.png differ diff --git a/weapp/images/shape3.png b/weapp/images/shape3.png new file mode 100644 index 0000000..e9a3cac Binary files /dev/null and b/weapp/images/shape3.png differ diff --git a/weapp/package/deviceBigdataGraph/deviceBigdataGraph.js b/weapp/package/deviceBigdataGraph/deviceBigdataGraph.js new file mode 100644 index 0000000..579c700 --- /dev/null +++ b/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() { + + } +}) \ No newline at end of file diff --git a/weapp/package/deviceBigdataGraph/deviceBigdataGraph.json b/weapp/package/deviceBigdataGraph/deviceBigdataGraph.json new file mode 100644 index 0000000..2d686f4 --- /dev/null +++ b/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" + } + } \ No newline at end of file diff --git a/weapp/package/deviceBigdataGraph/deviceBigdataGraph.wxml b/weapp/package/deviceBigdataGraph/deviceBigdataGraph.wxml new file mode 100644 index 0000000..e932950 --- /dev/null +++ b/weapp/package/deviceBigdataGraph/deviceBigdataGraph.wxml @@ -0,0 +1,63 @@ + + + + + + + + + 质保图谱 + + + 查看详情 + + + + 过保比率: + + + 50% + + + + 质保期比例: + + + 50% + + + + + + + + + + + 设备状态 + + + 查看详情 + + + + + + + + + + + + + 设备类型 + + + 总数: + + + + + + + \ No newline at end of file diff --git a/weapp/package/deviceBigdataGraph/deviceBigdataGraph.wxss b/weapp/package/deviceBigdataGraph/deviceBigdataGraph.wxss new file mode 100644 index 0000000..73a1649 --- /dev/null +++ b/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; +} \ No newline at end of file diff --git a/weapp/package/deviceBigdataGraph/statusDetail/statusDetail.js b/weapp/package/deviceBigdataGraph/statusDetail/statusDetail.js new file mode 100644 index 0000000..ca20416 --- /dev/null +++ b/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() { + + } +}) \ No newline at end of file diff --git a/weapp/package/deviceBigdataGraph/statusDetail/statusDetail.json b/weapp/package/deviceBigdataGraph/statusDetail/statusDetail.json new file mode 100644 index 0000000..a4a2a67 --- /dev/null +++ b/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" + + } +} \ No newline at end of file diff --git a/weapp/package/deviceBigdataGraph/statusDetail/statusDetail.wxml b/weapp/package/deviceBigdataGraph/statusDetail/statusDetail.wxml new file mode 100644 index 0000000..6f3320e --- /dev/null +++ b/weapp/package/deviceBigdataGraph/statusDetail/statusDetail.wxml @@ -0,0 +1,26 @@ + + + 设备总数 + 300 + + + 设备故障率 + {{86}}% + + + 完好率 + {{300}}% + + + + + + + + 历史风险趋势 + + + + + + diff --git a/weapp/package/deviceBigdataGraph/statusDetail/statusDetail.wxss b/weapp/package/deviceBigdataGraph/statusDetail/statusDetail.wxss new file mode 100644 index 0000000..241dadd --- /dev/null +++ b/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; + } \ No newline at end of file diff --git a/weapp/package/inspectionInput/inspectionInput.js b/weapp/package/inspectionInput/inspectionInput.js index 149cbed..0ed90e5 100644 --- a/weapp/package/inspectionInput/inspectionInput.js +++ b/weapp/package/inspectionInput/inspectionInput.js @@ -13,7 +13,7 @@ Page({ address: '', // 当前位置 imgUrl: getApp().globalData.imgUrl, checkItems: [], // 检查项 - inspectContent: {}, // 巡检内容 + inspectContentArr: [], // 巡检内容 isCommitting: false, /*** 扫码巡检 ***/ planList: null, // 巡检计划列表 @@ -61,26 +61,46 @@ Page({ }, // 获取巡检模板 - getPatrolTemplate (templateId) { + getPatrolTemplate(templateId, pointDevices = []) { Request.get(getPatrolTemplate(templateId)).then(res => { const checkItems = res.rows[0].checkItems; - const inspectContent = {}; - for (const c of checkItems) { - inspectContent[c.name] = { - isNormal: null, - msgInp: null, - level: null, - imgs: [], - }; + let inspectContentArr = []; + // 有绑定设备的点位,每个设备都要检查各个检查项 + 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, + 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, - inspectContent, + inspectContentArr: inspectContentArr, }) }) }, - onPickerChange (e) { + onPickerChange(e) { const { key } = e.currentTarget.dataset; const { value } = e.detail; this.setData({ @@ -90,26 +110,27 @@ Page({ }); const curPlan = this.data.planList[e.detail.columns[0].index]; + const nextItemData = curPlan.points.find(p => p.id == this.data.scenePointId) this.setData({ 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; this.setData({ [`${key}Visible`]: false, }); }, - onPlanListPicker () { + onPlanListPicker() { this.setData({ planListVisible: true }); }, // 获取当前位置 - selfLocation () { + selfLocation() { const that = this wx.showLoading({ title: '定位中', @@ -140,42 +161,43 @@ Page({ }); }, - handleChangeTwo (e) { + handleChangeTwo(e) { const isNormal = e.detail === 'normal'; - const inspectContent = this.data.inspectContent; - inspectContent[e.currentTarget.dataset.item].isNormal = isNormal; + const { deviceidx, itemidx } = e.currentTarget.dataset; + let nextInspectContentArr = this.data.inspectContentArr; + + nextInspectContentArr[deviceidx].checkItems[itemidx].isNormal = isNormal; if (isNormal) { // 清除异常数据 - inspectContent[e.currentTarget.dataset.item].msgInp = null; - inspectContent[e.currentTarget.dataset.item].level = null; - inspectContent[e.currentTarget.dataset.item].imgs = []; + nextInspectContentArr[deviceidx].checkItems[itemidx].msgInp = null; + nextInspectContentArr[deviceidx].checkItems[itemidx].level = null; + nextInspectContentArr[deviceidx].checkItems[itemidx].imgs = []; } - this.setData({ - inspectContent, - }) + this.setData({ inspectContentArr: nextInspectContentArr }) }, - handleChangeThree (e) { - const inspectContent = this.data.inspectContent; - inspectContent[e.currentTarget.dataset.item].level = e.detail; - this.setData({ - inspectContent - }) + handleChangeThree(e) { + const { deviceidx, itemidx } = e.currentTarget.dataset; + let nextInspectContentArr = this.data.inspectContentArr; + + nextInspectContentArr[deviceidx].checkItems[itemidx].level = e.detail; + this.setData({ inspectContentArr: nextInspectContentArr }) }, // 巡查详情 bindInput: function (e) { - const inspectContent = this.data.inspectContent; - inspectContent[e.currentTarget.dataset.item].msgInp = e.detail.value; - this.setData({ - inspectContent - }) + const { deviceidx, itemidx } = e.currentTarget.dataset; + let nextInspectContentArr = this.data.inspectContentArr; + + nextInspectContentArr[deviceidx].checkItems[itemidx].msgInp = e.detail.value; + this.setData({ inspectContentArr: nextInspectContentArr }) }, // 上传图片 chooseImg: function (e) { // 这里是选取图片的方法 + const { deviceidx, itemidx } = e.currentTarget.dataset; const that = this; 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) { wx.showToast({ title: '最多选择20张图片上传', @@ -203,16 +225,16 @@ Page({ } pics.push(imgs[i].tempFilePath) } - that.uploadimg({ + that.uploadImg({ url: getApp().globalData.webUrl + '_upload/attachments/project', // 图片上传的接口 path: pics, // 选取的图片的地址数组 - }, e.currentTarget.dataset.item); + }, deviceidx, itemidx); }, }) }, //多张图片上传 - uploadimg: function (data, itemName) { + uploadImg: function (data, deviceidx, itemidx) { wx.showLoading({ title: '上传中...', mask: true, @@ -221,7 +243,7 @@ Page({ i = data.i ? data.i : 0, success = data.success ? data.success : 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({ url: data.url, filePath: data.path[i], @@ -232,19 +254,15 @@ Page({ let str = JSON.parse(resp.data) // 返回的结果,可能不同项目结果不一样 str = str.uploaded if (imgs.length >= 20) { - const inspectContent = that.data.inspectContent; - inspectContent[itemName].imgs = imgs; - that.setData({ - inspectContent, - }); + let nextInspectContentArr = that.data.inspectContentArr; + nextInspectContentArr[deviceidx].checkItems[itemidx].imgs = imgs; + that.setData({ inspectContentArr: nextInspectContentArr }); return false; } else { imgs.push(str); - const inspectContent = that.data.inspectContent; - inspectContent[itemName].imgs = imgs; - that.setData({ - inspectContent, - }) + let nextInspectContentArr = that.data.inspectContentArr; + nextInspectContentArr[deviceidx].checkItems[itemidx].imgs = imgs; + that.setData({ inspectContentArr: nextInspectContentArr }); } }, fail: (res) => { @@ -260,7 +278,7 @@ Page({ data.i = i; data.success = success; data.fail = fail; - that.uploadimg(data, itemName); // 递归,回调自己 + that.uploadImg(data, deviceidx, itemidx); // 递归,回调自己 } } }); @@ -268,22 +286,19 @@ Page({ // 删除图片 deleteImg: function (e) { - let imgs = this.data.inspectContent[e.currentTarget.dataset.item].imgs; - const index = e.currentTarget.dataset.index; + const { deviceidx, itemidx, index } = e.currentTarget.dataset; + let imgs = this.data.inspectContentArr[deviceidx].checkItems[itemidx].imgs; imgs.splice(index, 1); - const inspectContent = this.data.inspectContent; - inspectContent[e.currentTarget.dataset.item].imgs = imgs; - this.setData({ - inspectContent - }); + let nextInspectContentArr = this.data.inspectContentArr; + nextInspectContentArr[deviceidx].checkItems[itemidx].imgs = imgs; + this.setData({ inspectContentArr: nextInspectContentArr }) }, // 预览图片 previewImg: function (e) { - // 获取当前图片的下标 - const index = e.currentTarget.dataset.index; + const { deviceidx, itemidx, index } = e.currentTarget.dataset; // 所有图片 - 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); wx.previewImage({ // 当前显示图片 @@ -293,7 +308,7 @@ Page({ }) }, - bindCancel () { + bindCancel() { if (this.data.scenePointId) { wx.switchTab({ url: '/pages/index/index' }) } else { @@ -307,12 +322,11 @@ Page({ if (that.data.isCommitting) { return } let { itemData, - inspectContent, + inspectContentArr, dataList, address } = that.data; let alarm = false; - if (!address) { wx.showToast({ title: '请获取当前位置', @@ -321,26 +335,29 @@ Page({ }) return; } - - for (const item in inspectContent) { - if (inspectContent[item].isNormal === null) { - wx.showToast({ - title: '请填写完整', - icon: 'none', - duration: 1500 - }) - return; - } - if ((!inspectContent[item].isNormal) && (!inspectContent[item].level || !inspectContent[item].msgInp)) { - wx.showToast({ - title: '异常项必须输入巡查详情和选择严重等级', - icon: 'none', - duration: 2000 - }) - return; - } - if (inspectContent[item].isNormal === false) { - alarm = true; + 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'); @@ -353,7 +370,7 @@ Page({ project: dataList.project, frequency: dataList.frequency, itemData: itemData, - inspectContent, + inspectContent: reportArr, address: address }, alarm, @@ -377,7 +394,7 @@ Page({ /** * 生命周期函数--监听页面加载 */ - onLoad (options) { + onLoad(options) { const that = this; const scenePointId = options.scene; if (scenePointId) { // 扫小程序码进入 @@ -398,56 +415,56 @@ Page({ dataList, 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() { } }) \ No newline at end of file diff --git a/weapp/package/inspectionInput/inspectionInput.wxml b/weapp/package/inspectionInput/inspectionInput.wxml index abe4bd7..32adf1f 100644 --- a/weapp/package/inspectionInput/inspectionInput.wxml +++ b/weapp/package/inspectionInput/inspectionInput.wxml @@ -1,8 +1,24 @@ - - + + @@ -24,38 +40,115 @@ - - - {{item.name}}: - - 正常 - 异常 + + + + {{device.deviceName}} + + + {{item.name}}: + + 正常 + 异常 + + + + + 轻微 + 中度 + 严重 - - - - 轻微 - 中度 - 严重 - - - - - - - - - - - - +