Browse Source

feat:巡检1.9代码相关代码

master
zhaobing 10 months ago
parent
commit
3d53743bc2
  1. 4
      api/app/lib/controllers/auth/index.js
  2. 355
      api/app/lib/controllers/bigScreen/leader.js
  3. 28
      api/app/lib/routes/bigScreen/leader.js
  4. BIN
      web-screen/client/assets/bigScreen/arrow.png
  5. BIN
      web-screen/client/assets/bigScreen/bg.png
  6. BIN
      web-screen/client/assets/bigScreen/bigImg.png
  7. BIN
      web-screen/client/assets/bigScreen/blue.png
  8. BIN
      web-screen/client/assets/bigScreen/cardHeader.png
  9. BIN
      web-screen/client/assets/bigScreen/center.webp
  10. BIN
      web-screen/client/assets/bigScreen/daic.png
  11. BIN
      web-screen/client/assets/bigScreen/daij.png
  12. BIN
      web-screen/client/assets/bigScreen/doneRate.png
  13. BIN
      web-screen/client/assets/bigScreen/left.png
  14. BIN
      web-screen/client/assets/bigScreen/plan.png
  15. BIN
      web-screen/client/assets/bigScreen/qc.png
  16. BIN
      web-screen/client/assets/bigScreen/right.png
  17. BIN
      web-screen/client/assets/bigScreen/runHeaderSelect.png
  18. BIN
      web-screen/client/assets/bigScreen/runHeaderSelected.png
  19. BIN
      web-screen/client/assets/bigScreen/selected-btn.png
  20. BIN
      web-screen/client/assets/bigScreen/top.png
  21. BIN
      web-screen/client/assets/bigScreen/yellow.png
  22. BIN
      web-screen/client/assets/bigScreen/yic.png
  23. BIN
      web-screen/client/assets/bigScreen/yij.png
  24. BIN
      web-screen/client/assets/bigScreen/ying.png
  25. BIN
      web-screen/client/assets/images/weather/baoxue.png
  26. BIN
      web-screen/client/assets/images/weather/dayu.png
  27. BIN
      web-screen/client/assets/images/weather/duoyun.png
  28. BIN
      web-screen/client/assets/images/weather/fucheng.png
  29. BIN
      web-screen/client/assets/images/weather/leizhengyu.png
  30. BIN
      web-screen/client/assets/images/weather/mai.png
  31. BIN
      web-screen/client/assets/images/weather/qiangshachengbao.png
  32. BIN
      web-screen/client/assets/images/weather/qing.png
  33. BIN
      web-screen/client/assets/images/weather/shachengbao.png
  34. BIN
      web-screen/client/assets/images/weather/tedabaoyu.png
  35. BIN
      web-screen/client/assets/images/weather/weizhi.png
  36. BIN
      web-screen/client/assets/images/weather/wu.png
  37. BIN
      web-screen/client/assets/images/weather/xiaoxue.png
  38. BIN
      web-screen/client/assets/images/weather/xiaoyu.png
  39. BIN
      web-screen/client/assets/images/weather/yangsha.png
  40. BIN
      web-screen/client/assets/images/weather/yingtian.png
  41. BIN
      web-screen/client/assets/images/weather/yujiaxue.png
  42. BIN
      web-screen/client/assets/images/weather/zhenyu.png
  43. BIN
      web-screen/client/assets/images/weather/zhongxue.png
  44. BIN
      web-screen/client/assets/images/weather/zhongyu.png
  45. 5
      web-screen/client/src/app.js
  46. 2
      web-screen/client/src/index.js
  47. 8
      web-screen/client/src/layout/actions/global.js
  48. 49
      web-screen/client/src/layout/components/fullScreen/index.js
  49. 310
      web-screen/client/src/layout/components/fullScreen/style.less
  50. 386
      web-screen/client/src/layout/components/header/index.js
  51. 38
      web-screen/client/src/layout/components/header/showTime.js
  52. 44
      web-screen/client/src/layout/components/header/style.css
  53. 33
      web-screen/client/src/layout/components/sider/index.js
  54. 89
      web-screen/client/src/layout/constants/weather.js
  55. 102
      web-screen/client/src/layout/containers/layout/index.js
  56. 21
      web-screen/client/src/layout/containers/layout/index.less
  57. 5
      web-screen/client/src/layout/reducers/index.js
  58. 24
      web-screen/client/src/layout/reducers/tab.js
  59. 2
      web-screen/client/src/sections/auth/containers/login.js
  60. 6
      web-screen/client/src/sections/bigScreen/actions/index.js
  61. 111
      web-screen/client/src/sections/bigScreen/actions/leader.js
  62. 140
      web-screen/client/src/sections/bigScreen/components/AutoRollComponent.js
  63. 40
      web-screen/client/src/sections/bigScreen/components/error/bottom.js
  64. 38
      web-screen/client/src/sections/bigScreen/components/error/index.js
  65. 21
      web-screen/client/src/sections/bigScreen/components/error/style.less
  66. 49
      web-screen/client/src/sections/bigScreen/components/error/top.js
  67. 50
      web-screen/client/src/sections/bigScreen/components/homePages.js
  68. 61
      web-screen/client/src/sections/bigScreen/components/leader/index.js
  69. 473
      web-screen/client/src/sections/bigScreen/components/leader/left.js
  70. 317
      web-screen/client/src/sections/bigScreen/components/leader/right.js
  71. 72
      web-screen/client/src/sections/bigScreen/components/run/index.js
  72. 213
      web-screen/client/src/sections/bigScreen/components/run/left.js
  73. 242
      web-screen/client/src/sections/bigScreen/components/run/right.js
  74. 232
      web-screen/client/src/sections/bigScreen/components/run/style.less
  75. 464
      web-screen/client/src/sections/bigScreen/components/style.less
  76. 30
      web-screen/client/src/sections/bigScreen/containers/bigScreen.js
  77. 13
      web-screen/client/src/sections/bigScreen/index.js
  78. 0
      web-screen/client/src/sections/bigScreen/reducers/index.js
  79. 13
      web-screen/client/src/sections/bigScreen/routes.js
  80. 49
      web-screen/client/src/utils/fs.js
  81. 22
      web-screen/client/src/utils/someStores.js
  82. 11
      web-screen/client/src/utils/webapi.js
  83. 4
      web-screen/package.json
  84. 76
      web-screen/routes/weather/index.js
  85. 2
      web/client/src/sections/projectManagement/components/projectModel.js
  86. 2
      web/webpack.config.js

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

@ -201,9 +201,9 @@ async function screenlogin (ctx, next) {
authorized: true,
token: token,
});
await models.ProjectUserToken.create({
await models.UserToken.create({
token: token,
projectUserInfo: userRslt,
userInfo: userRslt,
expired: moment().add(30, 'days').format()
});

355
api/app/lib/controllers/bigScreen/leader.js

@ -0,0 +1,355 @@
'use strict';
const moment = require("moment");
async function findPatrolRecords(ctx, next) {
const sequelize = ctx.fs.dc.orm;
try {
let rslt = { planCount: 0, done: 0 }
const models = ctx.fs.dc.models
const { projectId, startTime, endTime } = ctx.query
const plan = await models.PatrolPlan.findAll({
where: {
structureId: { $in: projectId.split(',') },
$or: [{
startTime: { $lte: startTime },
endTime: { $gte: endTime }
},
{
startTime: { $gt: startTime },
endTime: { $lt: endTime }
},
{
startTime: { $between: [startTime, endTime] },
},
{
endTime: { $between: [startTime, endTime] }
},
]
}
})
if (plan && plan.length) {
for (let { dataValues: c } of plan) {
let frequency = Number(c.frequency.split('次')[0]) || 0
let points = c.points.length || 0
let done = 0
//
if (moment(c.startTime).isSameOrBefore(moment(startTime)) && moment(c.endTime).isSameOrAfter(moment(endTime))) {
done = await models.PatrolRecord.count({
where: {
inspectionTime: { $between: [c.startTime, endTime] },
patrolPlanId: c.id
}
})
} else if (moment(c.startTime).isSameOrAfter(moment(startTime)) && moment(c.startTime).isSameOrBefore(moment(endTime))) {
done = await models.PatrolRecord.count({
where: {
inspectionTime: { $between: [c.startTime, endTime] },
patrolPlanId: c.id
}
})
} else if (moment(c.endTime).isSameOrAfter(moment(startTime)) && moment(c.endTime).isSameOrBefore(moment(endTime))) {
done = await models.PatrolRecord.count({
where: {
inspectionTime: { $between: [c.startTime, c.endTime] },
patrolPlanId: c.id
}
})
} else if (moment(c.startTime).isAfter(moment(startTime)) && moment(c.endTime).isBefore(moment(endTime))) {
done = await models.PatrolRecord.count({
where: {
inspectionTime: { $between: [c.startTime, c.endTime] },
patrolPlanId: c.id
}
})
}
let dones = await models.PatrolRecord.count({
where: {
inspectionTime: { $between: [startTime, endTime] },
patrolPlanId: c.id
}
})
rslt.planCount += frequency * points - done
rslt.done += dones
}
}
ctx.status = 200
ctx.body = rslt
} catch (error) {
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
ctx.status = 400;
ctx.body = { message: '查询巡检记录失败' }
}
}
async function findPatrolRate(ctx) {
try {
const models = ctx.fs.dc.models
const sequelize = ctx.fs.dc.orm;
const { projectId, startTime, endTime } = ctx.query
const res = await models.PatrolRecord.findAll({
attributes: ['id'],
where: {
projectId: { $in: projectId.split(',') },
},
include: [
{
model: models.PatrolRecordIssueHandle,
required: true,
where: {
createTime: { $between: [startTime, endTime] },
},
attributes: ['createTime', 'state'],
},
],
});
ctx.body = res
ctx.status = 200
} catch (error) {
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
ctx.status = 400;
ctx.body = { message: '查询巡检完成率失败' }
}
}
async function countByProject(ctx) {
try {
const models = ctx.fs.dc.models
const sequelize = ctx.fs.dc.orm;
const { projectId, } = ctx.query
const res = await models.Project.findAll({
attributes: [
'id',
'name',
[sequelize.fn('COUNT', sequelize.literal('"PatrolRecords"."id"')), 'patrolRecordCount'],
[sequelize.fn('COUNT', sequelize.literal('"PatrolRecords->patrolRecordIssueHandles"."id"')), 'issueHandleCount'],
],
include: [
{
model: models.PatrolRecord,
// as: 'PatrolRecords',
attributes: [],
include: [
{
model: models.PatrolRecordIssueHandle,
attributes: [],
// as: 'PatrolRecordIssueHandles',
},
],
},
],
where: {
id: { $in: projectId.split(',') },
},
group: ['project.id', 'project.name'],
order: [
[sequelize.fn('COUNT', sequelize.literal('"PatrolRecords"."id"')), 'DESC'],
],
});
ctx.body = res
ctx.status = 200
} catch (error) {
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
ctx.status = 400;
ctx.body = { message: '根据结构物汇集失败' }
}
}
async function devicesGuarantee(ctx) {
try {
let rslt = {}
const models = ctx.fs.dc.models
const sequelize = ctx.fs.dc.orm;
const { projectId, } = ctx.query
let option = {
// where: searchWhere,
order: [["id", "desc"]],
include: [{
model: models.PointDevice,
include: {
model: models.Point,
where: { projectId: { $in: projectId.split(',') } }
}
}]
}
const res = await models.Device.findAll(option);
rslt.count = res.length
let underGuarantee = 0
for (let { dataValues: c } of res) {
if (moment(c.dateGuarantee).isAfter(moment())) {
underGuarantee += 1
}
}
rslt.underGuarantee = underGuarantee
const rs = await sequelize.query(`
SELECT d.type, COUNT(d.id) as count
FROM device d
LEFT JOIN point_device pd
ON d.id = pd.device_id
LEFT JOIN point p
ON pd.point_id = p.id
WHERE p.project_id IN (${projectId})
GROUP by d.type`)
rslt.rs = rs[0]
ctx.body = rslt
ctx.status = 200
} catch (error) {
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
ctx.status = 400;
ctx.body = { message: '查询设备失败' }
}
}
async function getHistoricalFaults(ctx) {
try {
const models = ctx.fs.dc.models
const sequelize = ctx.fs.dc.orm;
const { projectId, startTime, endTime } = ctx.query
const rslt = await sequelize.query(`
select p.name,
count(1) as count,
count(case when prih.state=6 then 1 end) as bCount
from project p
left join patrol_record pr
on p.id = pr.project_id
left join patrol_record_issue_handle prih
on pr.id = prih.patrol_record_id
and prih.create_time
between '${startTime}'
and '${endTime}'
where p.id in (${projectId})
group by p.name
`)
ctx.body = rslt[0]
ctx.status = 200
} catch (error) {
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
ctx.status = 400;
ctx.body = { message: '查询历史故障失败' }
}
}
async function getFaultsRank(ctx) {
try {
let rslt = []
const models = ctx.fs.dc.models
const sequelize = ctx.fs.dc.orm;
const { projectId, } = ctx.query
const rs = await models.PatrolRecord.findAll(
{
where:
{ projectId: { $in: projectId.split(',') } }
}
)
for (let { dataValues: c } of rs) {
if (c.points.inspectContent && c.points.inspectContent.length) {
for (let p of c.points.inspectContent) {
if (p.deviceId) {
if(p.alarm){
const isExist = rslt.find(item => item.id === p.deviceId)
if (!isExist) {
rslt.push({ id: p.deviceId, name: p.deviceName, count: 1 })
} else {
isExist.count += 1
}
}
}
}
}
}
rslt.sort((a, b) => b.count - a.count)
ctx.body = rslt
ctx.status = 200
} catch (error) {
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
ctx.status = 400;
ctx.body = { message: '查询故障排名失败' }
}
}
async function getCenterData(ctx) {
try{
let rslt = {}
const models = ctx.fs.dc.models
const sequelize = ctx.fs.dc.orm;
const { projectId, } = ctx.query
let option = {
// where: searchWhere,
order: [["id", "desc"]],
include: [{
model: models.PointDevice,
include: {
model: models.Point,
where: { projectId: { $in: projectId.split(',') } }
}
}]
}
const res = await models.Device.findAll(option)
const rs = await sequelize.query(`
select
count(1) as count,
count(case when prih.state=6 then 1 end) as bCount
from project p
left join patrol_record pr
on p.id = pr.project_id
left join patrol_record_issue_handle prih
on pr.id = prih.patrol_record_id
where p.id in (${projectId})
`)
const record=await models.PatrolRecord.count({where:{projectId:{$in:projectId.split(',')}}})
const reportCount=await models.ReportInfo.count({
where:{
$or:[ {projectId:{$in:projectId.split(',') }},
{
structureIds:{$contains:projectId.split(',').map(item=>parseInt(item))}
}
]
}
})
rslt.deviceCount=res.length
rslt.questions=rs[0][0]?Number(rs[0][0].count):0
rslt.handleCount=rs[0][0]?Number(rs[0][0].count):0
rslt.records=record
rslt.reportCount=reportCount
ctx.body = rslt
ctx.status = 200
}catch(error){
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
ctx.status = 400;
ctx.body = { message: '领导驾驶舱中间数据查询失败' }
}
}
module.exports = {
findPatrolRecords,
findPatrolRate,
countByProject,
devicesGuarantee,
getHistoricalFaults,
getFaultsRank,
getCenterData
}

28
api/app/lib/routes/bigScreen/leader.js

@ -0,0 +1,28 @@
'use strict';
const leader = require('../../controllers/bigScreen/leader');
module.exports = function (app, router, opts) {
app.fs.api.logAttr['GET/bigScreen/patrolRecord'] = { content: '查询对应结构物下的结构物相关的', visible: false };
router.get('/bigScreen/patrolRecord', leader.findPatrolRecords)
app.fs.api.logAttr['GET/bigScreen/patrolRate'] = { content: '查询巡检率', visible: false };
router.get('/bigScreen/patrolRate', leader.findPatrolRate)
app.fs.api.logAttr['GET/bigScreen/patrolCount'] = { content: '根据结构物汇集巡检', visible: false };
router.get('/bigScreen/patrolCount', leader.countByProject)
app.fs.api.logAttr['GET/bigScreen/devices/guarantee'] = { content: '查询设备寿命相关', visible: false };
router.get('/bigScreen/devices/guarantee', leader.devicesGuarantee)
app.fs.api.logAttr['GET/bigScreen/historicalFaults'] = { content: '查询历史错误', visible: false };
router.get('/bigScreen/historicalFaults', leader.getHistoricalFaults)
app.fs.api.logAttr['GET/bigScreen/faultsRank'] = { content: '查询故障排名', visible: false };
router.get('/bigScreen/faultsRank', leader.getFaultsRank)
app.fs.api.logAttr['GET/bigScreen/centerData'] = { content: '领导驾驶仓中间数据', visible: false };
router.get('/bigScreen/centerData', leader.getCenterData)
}

BIN
web-screen/client/assets/bigScreen/arrow.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.9 KiB

BIN
web-screen/client/assets/bigScreen/bg.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

BIN
web-screen/client/assets/bigScreen/bigImg.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 862 KiB

BIN
web-screen/client/assets/bigScreen/blue.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 KiB

BIN
web-screen/client/assets/bigScreen/cardHeader.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

BIN
web-screen/client/assets/bigScreen/center.webp

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 MiB

BIN
web-screen/client/assets/bigScreen/daic.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

BIN
web-screen/client/assets/bigScreen/daij.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

BIN
web-screen/client/assets/bigScreen/doneRate.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

BIN
web-screen/client/assets/bigScreen/left.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 918 B

BIN
web-screen/client/assets/bigScreen/plan.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
web-screen/client/assets/bigScreen/qc.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

BIN
web-screen/client/assets/bigScreen/right.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 923 B

BIN
web-screen/client/assets/bigScreen/runHeaderSelect.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

BIN
web-screen/client/assets/bigScreen/runHeaderSelected.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

BIN
web-screen/client/assets/bigScreen/selected-btn.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

BIN
web-screen/client/assets/bigScreen/top.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 890 KiB

BIN
web-screen/client/assets/bigScreen/yellow.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

BIN
web-screen/client/assets/bigScreen/yic.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

BIN
web-screen/client/assets/bigScreen/yij.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

BIN
web-screen/client/assets/bigScreen/ying.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

BIN
web-screen/client/assets/images/weather/baoxue.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

BIN
web-screen/client/assets/images/weather/dayu.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

BIN
web-screen/client/assets/images/weather/duoyun.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

BIN
web-screen/client/assets/images/weather/fucheng.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

BIN
web-screen/client/assets/images/weather/leizhengyu.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

BIN
web-screen/client/assets/images/weather/mai.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

BIN
web-screen/client/assets/images/weather/qiangshachengbao.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

BIN
web-screen/client/assets/images/weather/qing.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

BIN
web-screen/client/assets/images/weather/shachengbao.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

BIN
web-screen/client/assets/images/weather/tedabaoyu.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

BIN
web-screen/client/assets/images/weather/weizhi.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
web-screen/client/assets/images/weather/wu.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 665 B

BIN
web-screen/client/assets/images/weather/xiaoxue.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
web-screen/client/assets/images/weather/xiaoyu.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
web-screen/client/assets/images/weather/yangsha.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

BIN
web-screen/client/assets/images/weather/yingtian.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

BIN
web-screen/client/assets/images/weather/yujiaxue.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

BIN
web-screen/client/assets/images/weather/zhenyu.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

BIN
web-screen/client/assets/images/weather/zhongxue.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

BIN
web-screen/client/assets/images/weather/zhongyu.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

5
web-screen/client/src/app.js

@ -3,7 +3,8 @@
import React, { useEffect } from 'react';
import Layout from './layout';
import Auth from './sections/auth';
import DeviceManage from './sections/deviceManage';
import BigScreen from './sections/bigScreen';
import { Func } from '$utils';
const App = props => {
@ -16,7 +17,7 @@ const App = props => {
return (
<Layout
title={projectName}
sections={[Auth, DeviceManage]}
sections={[Auth,BigScreen]}
/>
)

2
web-screen/client/src/index.js

@ -1,5 +1,5 @@
'use strict';
import './utils/fs'
import React from 'react';
import { render } from 'react-dom';
import App from './app';

8
web-screen/client/src/layout/actions/global.js

@ -26,6 +26,14 @@ export function resize (clientHeight, clientWidth) {
}
}
export const INIT_PAGE_HEADER_DETAILS = 'INIT_PAGE_HEADER_DETAILS';
export function initPageHeaderDetails(component) {
return{
type:INIT_PAGE_HEADER_DETAILS,
payload:{component}
}
}
export const INIT_API_ROOT = 'INIT_API_ROOT';
export function initApiRoot () {
return dispatch => {

49
web-screen/client/src/layout/components/fullScreen/index.js

@ -0,0 +1,49 @@
import React, { useLayoutEffect, forwardRef } from 'react';
import { useAutoResize } from '@jiaminghi/data-view-react';
import './style.less';
const FullScreenContainer = forwardRef(({
children, className, style, designWidth = 1920, designHeight = 1080, onlyAutoSize = false, divRef, dispatch,
}, ref) => {
const { domRef } = useAutoResize(ref);
useLayoutEffect(() => {
const bodyWidth = Math.max(document.documentElement.clientWidth, document.body.clientWidth);
const bodyHeight = Math.max(document.documentElement.clientHeight, document.body.clientHeight);
const w = bodyWidth / designWidth;
const h = bodyHeight / designHeight;
const scale = w < h ? w : h;
const divRect = divRef?.current?.getBoundingClientRect();
if (onlyAutoSize) {
Object.assign(domRef.current.style, {
width: `${divRect?.width || 0}px`,
height: `${divRect?.height || 0}px`,
position: 'fixed',
// zIndex: 999,
top: `${divRect?.top || 0}px`,
left: `${divRect?.left || 0}px`,
});
} else {
Object.assign(domRef.current.style, {
width: `${designWidth}px`,
height: `${designHeight}px`,
});
domRef.current.style.transform = `scale(${scale}) translate(-50%, -50%)`;
}
});
return (
<div
id={onlyAutoSize ? 'dv-screen-container' : 'custom-dv-full-screen-container'}
className={className}
style={style}
ref={domRef}
>
{children}
</div>
);
});
export default FullScreenContainer;

310
web-screen/client/src/layout/components/fullScreen/style.less

@ -0,0 +1,310 @@
#custom-dv-full-screen-container {
position: fixed;
top: 50%;
left: 50%;
overflow: hidden;
transform-origin: left top;
// padding: 14px 8px 28px 8px;
transition: 0.3s;
}
#super-screen-container {
width: 100%;
height: 100vh;
background-image: url('/assets/bigScreen/bg.png');
overflow: hidden;
color: #fff;
}
#emergency-command-modal {
color: #fff;
.onpoint {
cursor: pointer;
}
.close {
display: flex;
justify-content: flex-end;
margin-bottom: 10px;
}
.top {
display: flex;
justify-content: flex-end;
margin-bottom: 10px;
.cell-active {
background-image: url("/assets/images/screen/cockpit/modal/cell_bg.png");
background-size: 100% 100%;
}
.cell {
padding: 6px 18px;
margin: 0 6px;
border: 1px solid #051043;
display: flex;
align-items: center;
}
}
.content {
.left {
height: 95%;
display: flex;
justify-content: center;
align-items: center;
img {
height: 100%;
}
}
.right {
height: 95%;
font-size: 20px;
overflow-y: auto;
.task-item {
background: #061A2F;
height: 38px;
padding: 4px 8px;
margin: 8px 0;
font-size: 18px;
text-align: center;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
width: 96%;
}
.task-item-active {
background-image: url("/assets/images/screen/cockpit/modal/task_bg.png");
background-size: 100% 100%;
text-align: center;
height: 38px;
padding: 4px 8px;
margin: 8px 0;
font-size: 18px;
text-align: center;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
width: 96%;
}
.right-content {
background-image: url("/assets/images/screen/cockpit/modal/text_bg.png");
background-size: 100% 100%;
height: 100%;
width: 90%;
padding: 5%;
.time {
border-bottom: 2px solid #174683;
padding-bottom: 6px;
margin-bottom: 5%;
color: #c1c1c1;
font-weight: bold;
}
.content-text {
letter-spacing: 0.2rem;
word-break: break-all;
}
}
.yuntai {
height: 100%;
display: flex;
flex-direction: column;
justify-content: center;
.label {
background-image: url("/assets/images/screen/cockpit/modal/yuntai_label.png");
background-size: 100% 100%;
background-repeat: no-repeat;
width: fit-content;
padding: 6px 10px;
margin-bottom: 4px;
}
.control {
border: 1px solid #174683;
height: 85%;
width: 90%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
.btn-group {
background-image: linear-gradient(0deg,
#16299e,
#245cd0 50%,
#3389ff);
border-radius: 50%;
width: 150px;
height: 150px;
margin: 10px 0 30px 0;
display: flex;
flex-direction: column;
justify-content: space-between;
align-items: center;
.control-icon {
color: #000;
font-size: 28px;
}
.left-right {
width: 100%;
display: flex;
justify-content: space-between;
align-items: center;
.control-center {
background-color: #000000;
padding: 8px;
border-radius: 50%;
.circle {
width: 40px;
height: 40px;
border-radius: 50%;
background-image: linear-gradient(0deg,
#16299e,
#245cd0 50%,
#3389ff);
}
}
}
}
.add-or-minus {
width: 40%;
display: flex;
justify-content: space-around;
margin: 20px 0;
font-size: 18px;
}
}
}
}
.right::-webkit-scrollbar {
/* 滚动条整体样式 */
width: 11px;
/* 高宽分别对应横竖滚动条的尺寸 */
height: 5px;
}
.right::-webkit-scrollbar-thumb {
/* 滚动条里面小方块 */
background: #2d7fe2;
}
.right::-webkit-scrollbar-track {
/* 滚动条里面轨道 */
background: #154780;
}
}
}
#alarm-confirm {
height: 100%;
.title {
font-size: 22px;
display: flex;
align-items: center;
.icon {
color: #fff;
margin-right: 5px;
}
}
.content {
margin-left: 50px;
margin-top: 30px;
.confirm-input {
width: 350px;
margin-top: 30px;
color: #0057C9;
font-size: 16px;
}
}
.button-group {
position: absolute;
bottom: 70px;
right: 70px;
font-size: 16px;
.confirm {
color: #fff;
background-color: #0057C9;
border: none;
}
.cancel {
margin-right: 10px;
border: none;
background-color: #fff;
color: black;
}
}
}
.auto-size-container {
overflow: hidden;
transform-origin: left top;
transition: 0.3s;
position: fixed;
// left: 50%;
}
.gradient-text-one {
margin-top: -10px;
font-size: 16px;
background-image: -webkit-linear-gradient(bottom,
#fff,
red,
);
-webkit-background-clip: text;
-webkit-text-fill-color:
transparent;
}
// 超载车数量文字渐变
.gradient-text-number {
font-weight: Italic;
font-style: italic;
font-size: 25px;
margin-top: -15px;
background-image: -webkit-linear-gradient(bottom,
#fff,
red,
);
-webkit-background-clip: text;
-webkit-text-fill-color:
transparent;
}
.gradient-text-numbercolor {
font-weight: Italic;
font-style: italic;
font-size: 25px;
margin-top: -15px;
background-image: -webkit-linear-gradient(bottom,
#fff,
#0E88D5,
);
-webkit-background-clip: text;
-webkit-text-fill-color:
transparent;
}

386
web-screen/client/src/layout/components/header/index.js

@ -1,74 +1,364 @@
'use strict';
import React from 'react';
import { Menu } from 'antd';
import { Link } from 'react-router-dom';
import { connect } from 'react-redux';
import styles from './style.css';
import {
MenuFoldOutlined, MenuUnfoldOutlined, UserOutlined, LogoutOutlined
} from '@ant-design/icons';
'use strict'
import React, { useState, useEffect, useMemo } from 'react'
import { Menu } from 'antd'
import { Link } from 'react-router-dom'
import { connect } from 'react-redux'
import styles from './style.css'
import { setCurrentTab } from '$utils/someStores'
import { WEATHERMARGIN, ICONSMAP } from '../../constants/weather'
import { RouteRequest } from '@peace/utils'
import moment from 'moment'
import ShowTime from './showTime'
import { MenuFoldOutlined, MenuUnfoldOutlined, UserOutlined, LogoutOutlined } from '@ant-design/icons'
const Header = props => {
const { dispatch, history, user, pathname, toggleCollapsed, collapsed, actions } = props
const { dispatch, history, user, pathname, actions, clientWidth=100, toggleCollapsed, globalTab, tabChange } = props
const [tabs, setTabs] = useState({
patrolManage: [
{ name: '巡检管理', value: 'manage' },
{ name: '巡检项', value: 'item' },
{ name: '巡检日历', value: 'calendar' },
{ name: '巡检报告', value: 'report' },
],
deviceManageTabs: [
{ name: '设备统计', value: 'statistics' },
{ name: '设备台账', value: 'list' },
],
})
const [patrolManageVisible, setPatrolManageVisible] = useState(false)
const [deviceManageTabsVisible, setDeviceManageTabsVisible] = useState(false)
const [currentSubMenuTab, setCurrentSubMenuTab] = useState('')
const [tab, setTab] = useState('error')
const [weather, setWeather] = useState([])
console.log('globalTab', globalTab)
let headerTitleStyle = {
position: 'absolute',
left: '3.125rem',
top: '.125rem',
fontSize: '1.875rem',
color: ' #FFFFFF',
fontWeight: 600,
fontFamily: 'PangMenZhengDao',
letterSpacing: '.5231rem',
}
let headerTabStyle = {
width: '4.375rem',
height: '1.125rem',
fontFamily: 'PangMenZhengDao',
fontSize: '1.125rem',
color: '#CCE6FF',
letterSpacing: '.0231rem'
}
const handelClick = item => {
if (item.key == 'logout') {
dispatch(actions.auth.logout(user));
dispatch({ type: 'CLEAR_GLOBAL_SITE_LIST' });//清空用户关注工地列表
history.push(`/signin`);
useEffect(() => {
queryWeather()
const timeUpdate = setInterval(() => {
queryWeather()
}, WEATHERMARGIN)
return () => {
clearInterval(timeUpdate)
}
}
}, [])
let current = pathname;
if (pathname == '/' || pathname == '') {
current = 'default';
} else if (pathname.charAt(0) == '/') {
current = pathname.substring(1);
const queryWeather = () => {
// RouteRequest.get(`/query/weather/3d?location=101240101`).then(res => {
// if (res?.daily?.length === 3) {
// setWeather(res.daily)
// }
// })
}
if (current.indexOf('/') != -1) {
current = current.substring(0, current.indexOf('/'));
const iconSrc = useMemo(() => {
if (weather.length === 3) {
const icon = [ICONSMAP[weather[0].textDay], ICONSMAP[weather[1].textDay], ICONSMAP[weather[2].textDay]]
return icon
}
return []
}, [weather])
const handelClick = item => {
if (item.key == 'logout') {
dispatch(actions.auth.logout(user))
dispatch({ type: 'CLEAR_GLOBAL_SITE_LIST' }) //清空用户关注工地列表
history.push(`/signin`)
}
}
const onClick1 = tab => {
setTab(tab)
if (tab == 'patrolManage') {
setPatrolManageVisible(true)
setDeviceManageTabsVisible(false)
} else if (tab == 'device') {
setDeviceManageTabsVisible(true)
setPatrolManageVisible(false)
} else {
setPatrolManageVisible(false)
setDeviceManageTabsVisible(false)
setCurrentSubMenuTab('')
// dispatch({ type: 'TAB-CHANGE', data: tab })
}
tabChange(tab)
}
return (
<div className={styles.header}>
<div className={styles.header} >
<div className={styles['header-fold']}>
{/* <span onClick={toggleCollapsed} style={{ marginRight: 20 }}>
{collapsed ? <MenuUnfoldOutlined /> : <MenuFoldOutlined />}
</span> */}
<img src='/assets/images/logo.svg' style={{ margin: '-14px 12px 0 0', height: 24 }} />
{/* <img src='/assets/images/logo.svg' style={{ margin: '-14px 12px 0 0', height: 24 }} /> */}
<div className={styles['header-title']} style={{}}>
<div className={styles['title-cn']}>运维巡检平台</div>
<div className={styles['title-en']}>RUNNING MANAGEMENT SYSTEM</div>
<span style={headerTitleStyle}>河州路河州路河州路河州路河州路</span>
</div>
</div>
<div
style={{
color: '#FFFFFF',
fontSize: '1rem',
fontWeight: 400,
position: 'absolute',
left: '48.25rem',
top: '.4375rem',
}}>
<a
onClick={() => {
onClick1('leader')
}}
style={{
...headerTabStyle,
// color: tab == 'leader' ? '#E9CF14' : '#FFFFFF',
// fontWeight: tab == 'leader' ? 500 : 400,
position: 'relative',
marginRight: '1.375rem',
}}>
{/* <img style={{ marginRight: 4, display: tab == 'leader' ? 'inline-block' : 'none' }} src="/assets/bigScreen/left.png" /> */}
领导驾驶舱
{/* <img style={{ marginLeft: 4, display: tab == 'leader' ? 'inline-block' : 'none' }} src="/assets/bigScreen/right.png" /> */}
<img
style={{width:'8.3125rem',height:'5.625rem', position: 'absolute', left: '-1.1875rem', top: '-1.5625rem', display: tab == 'leader' ? 'inline-block' : 'none' }}
src='/assets/bigscreen/selected-btn.png'
/>
</a>
<a
onClick={() => {
onClick1('run')
}}
style={{
...headerTabStyle,
// color: tab == 'run' ? '#E9CF14' : '#FFFFFF',
position: 'relative', marginRight: '1.375rem'
}}>
{/* <img style={{ marginRight: 4, display: tab == 'run' ? 'inline-block' : 'none' }} src="/assets/bigscreen/left.png" /> */}
运行监控
{/* <img style={{ marginLeft: 4, display: tab == 'run' ? 'inline-block' : 'none' }} src="/assets/bigscreen/right.png" /> */}
<img
style={{width:'8.3125rem',height:'5.625rem', position: 'absolute', left: '-1.75rem', top: '-1.5625rem', display: tab == 'run' ? 'inline-block' : 'none' }}
src='/assets/bigscreen/selected-btn.png'
/>
</a>
<a
onClick={() => {
onClick1('error')
}}
style={{
...headerTabStyle,
// color: tab == 'error' ? '#E9CF14' : '#FFFFFF',
position: 'relative', marginRight: '1.375rem'
}}>
{/* <img style={{ marginRight: 4, display: tab == 'error' ? 'inline-block' : 'none' }} src="/assets/bigscreen/left.png" /> */}
故障管理
{/* <img style={{ marginLeft: 4, display: tab == 'error' ? 'inline-block' : 'none' }} src="/assets/bigscreen/right.png" /> */}
<img
style={{ width:'8.3125rem',height:'5.625rem',position: 'absolute', left: '-1.75rem', top: '-1.5625rem', display: tab == 'error' ? 'inline-block' : 'none' }}
src='/assets/bigscreen/selected-btn.png'
/>
</a>
<a
onClick={() => {
onClick1('inspection')
}}
style={{
...headerTabStyle,
// color: tab == 'inspection' ? '#E9CF14' : '#FFFFFF',
position: 'relative', marginRight: '1.375rem' }}>
{/* <img style={{ marginRight: 4, display: tab == 'inspection' ? 'inline-block' : 'none' }} src="/assets/bigscreen/left.png" /> */}
巡检管理
{/* <img style={{ marginLeft: 4, display: tab == 'inspection' ? 'inline-block' : 'none' }} src="/assets/bigscreen/right.png" /> */}
<img
style={{
width:'8.3125rem',height:'5.625rem',
position: 'absolute',
left: '-1.75rem', top: '-1.5625rem',
display: tab == 'inspection' ? 'inline-block' : 'none',
}}
src='/assets/bigscreen/selected-btn.png'
/>
</a>
<a
onClick={() => {
onClick1('aiot')
}}
style={{
...headerTabStyle,
// color: tab == 'aiot' ? '#E9CF14' : '#FFFFFF',
position: 'relative',marginRight: '1.375rem' }}>
{/* <img style={{ marginRight: 4, display: tab == 'aiot' ? 'inline-block' : 'none' }} src="/assets/bigscreen/left.png" /> */}
AIOT运维
{/* <img style={{ marginLeft: 4, display: tab == 'aiot' ? 'inline-block' : 'none' }} src="/assets/bigscreen/right.png" /> */}
<img
style={{width:'8.3125rem',height:'5.625rem', position: 'absolute', left: '-1.75rem', top: '-1.5625rem', display: tab == 'aiot' ? 'inline-block' : 'none' }}
src='/assets/bigscreen/selected-btn.png'
/>
</a>
<a
onClick={() => {
onClick1('device')
}}
style={{
...headerTabStyle,
// color: tab == 'device' ? '#E9CF14' : '#FFFFFF',
position: 'relative', marginRight: '1.375rem' }}>
{/* <img style={{ marginRight: 4, display: tab == 'device' ? 'inline-block' : 'none' }} src="/assets/bigscreen/left.png" /> */}
设备管理
{/* <img style={{ marginLeft: 4, display: tab == 'device' ? 'inline-block' : 'none' }} src="/assets/bigscreen/right.png" /> */}
<img
style={{width:'8.3125rem',height:'5.625rem', position: 'absolute',left: '-1.75rem', top: '-1.5625rem', display: tab == 'device' ? 'inline-block' : 'none' }}
src='/assets/bigscreen/selected-btn.png'
/>
</a>
<a
onClick={() => {
onClick1('expert')
}}
style={{
// color: tab == 'expert' ? '#E9CF14' : '#FFFFFF',
...headerTabStyle,
position: 'relative', marginRight: '1.375rem' }}>
{/* <img style={{ marginRight: 4, display: tab == 'expert' ? 'inline-block' : 'none' }} src="/assets/bigscreen/left.png" /> */}
运维专家
{/* <img style={{ marginLeft: 4, display: tab == 'expert' ? 'inline-block' : 'none' }} src="/assets/bigscreen/right.png" /> */}
<img
style={{width:'8.3125rem',height:'5.625rem', position: 'absolute', left: '-1.75rem', top: '-1.5625rem', display: tab == 'expert' ? 'inline-block' : 'none' }}
src='/assets/bigscreen/selected-btn.png'
/>
</a>
<a
onClick={() => {
onClick1('info')
}}
style={{
...headerTabStyle,
// color: tab == 'info' ? '#E9CF14' : '#FFFFFF',
position: 'relative', marginRight: '1.375rem' }}>
{/* <img style={{ marginRight: 4, display: tab == 'info' ? 'inline-block' : 'none' }} src="/assets/bigscreen/left.png" /> */}
知识库
{/* <img style={{ marginLeft: 4, display: tab == 'info' ? 'inline-block' : 'none' }} src="/assets/bigscreen/right.png" /> */}
<img
style={{width:'8.3125rem',height:'5.625rem', position: 'absolute', left: '-1.75rem', top: '-1.5625rem', display: tab == 'info' ? 'inline-block' : 'none' }}
src='/assets/bigscreen/selected-btn.png'
/>
</a>
<a
onClick={() => {
onClick1('task')
}}
style={{
...headerTabStyle,
// color: tab == 'task' ? '#E9CF14' : '#FFFFFF',
position: 'relative', marginRight:'2.1875rem' }}>
{/* <img style={{ marginRight: 4, display: tab == 'task' ? 'inline-block' : 'none' }} src="/assets/bigscreen/left.png" /> */}
任务统计
{/* <img style={{ marginLeft: 4, display: tab == 'task' ? 'inline-block' : 'none' }} src="/assets/bigscreen/right.png" /> */}
<img
style={{width:'8.3125rem',height:'5.625rem', position: 'absolute', left: '-1.75rem', top: '-1.5625rem', display: tab == 'task' ? 'inline-block' : 'none' }}
src='/assets/bigscreen/selected-btn.png'
/>
</a>
<ShowTime marginRight={'0.075rem'} />
<div style={{ float: 'right', lineHeight: '.1875rem', marginTop: '.5rem' , marginRight: '.875rem' }}>
<div style={{ display: 'flex', alignItems: 'center' }}>
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center' }}>
<div style={{ color: '#FFF' }}>{weather[0]?.textDay}</div>
{/* <div style={{ color: '#24DCF7' }}>{weather[0]?.tempMin}-{weather[0]?.tempMax}℃</div> */}
</div>
<img src={iconSrc[0] || ICONSMAP.未知} alt='icon' style={{ width: '2.6875rem', height: '2.375rem', margin: '.29rem .3125rem' }} />
</div>
</div>
</div>
<div id="nav" className={styles['header-nav']}>
{patrolManageVisible || deviceManageTabsVisible ? (
<div
style={{
position: 'absolute',
// width: '29%',
height: '3.125rem',
top: tab == 'oprations' ? '4.125rem' : '4.8125rem',
left: patrolManageVisible ? '28%' : '36%',
display: 'flex',
// justifyContent: 'space-between',
justifyContent: 'center',
padding: '.75rem',
}}>
{(patrolManageVisible ? tabs.patrolManage : tabs.deviceManageTabs).map((tab, index) => (
<div>
<a
onClick={() => this.clickSubMenuTab(tab.value)}
style={{
color: currentSubMenuTab == tab.value ? '#E9CF14' : 'white',
fontSize: '1rem',
margin: '0 .625rem',
}}>
{tab.name}
</a>
<span
style={{
display:
index < (patrolManageVisible ? tabs.patrolManage : tabs.deviceManageTabs).length - 1
? 'inline-block'
: 'none',
}}>
<img src={`/assets/images/bigscreen/boring_${currentSubMenuTab == tab.value ? 1 : 0}.png`} />
</span>
</div>
))}
</div>
) : (
''
)}
<div id='nav' className={styles['header-nav']}>
<Menu
mode='horizontal'
selectedKeys={[current]}
style={{ border: 0, background: '#004093' }}
style={{ border: 0 }}
onClick={handelClick}
theme={'light'}
items={[{
label: <span style={{ color: 'aliceblue' }}>{user.displayName}</span>,
key: "user",
icon: <img className={styles['header-nav-user-img']} src={`/assets/images/avatar/5.png`} />,
children: [{
label: '退出', key: 'logout'
}],
}]}
items={[
{
label: <span style={{ color: 'aliceblue' }}>{user.displayName}</span>,
key: 'user',
icon: <img className={styles['header-nav-user-img']} src={`/assets/images/avatar/5.png`} />,
children: [
{
label: '退出',
key: 'logout',
},
],
},
]}
/>
</div>
</div>
);
};
)
}
function mapStateToProps (state) {
const { global, auth } = state;
function mapStateToProps(state) {
const { global, auth, globalTab } = state
return {
actions: global.actions,
user: auth.user
};
user: auth.user,
clientWidth: global.clientWidth,
clientHeight:global.clientHeight,
globalTab: globalTab.tab,
}
}
export default connect(mapStateToProps)(Header);
export default connect(mapStateToProps)(Header)

38
web-screen/client/src/layout/components/header/showTime.js

@ -0,0 +1,38 @@
'use strict';
import React, { useState, useEffect, useMemo } from 'react';
import { connect } from 'react-redux';
import moment from 'moment'
const ShowTime = props => {
const {marginRight}=props
const [date, setDate] = useState(moment());
const setTime = () => {
setDate(moment());
setTimeout(() => {
setTime()
}, 1000);
}
useEffect(()=>{
setTime()
},[])
return ( <span style={{ color: '#C3E6FF', fontSize: '.9rem',marginRight:marginRight }}>{date.format('YYYY-MM-DD HH:mm:ss')}</span>
)
}
function mapStateToProps(state) {
return {
};
}
export default connect(mapStateToProps)(ShowTime)

44
web-screen/client/src/layout/components/header/style.css

@ -1,54 +1,60 @@
.header {
position: relative;
height: 65px;
min-width: 520px;
background-color: #004093;
height: 5.75rem;
min-width: 32.5rem;
/* background-color: #004093; */
background-image: url('/assets/bigScreen/top.png');
}
.header-fold {
float: left;
padding-left: 32px;
font-size: 16px;
padding-left: 2rem;
font-size: 1rem;
}
.header-title {
margin-top: 19px;
height: 60px;
margin-top: 1.1875rem;
height: 3.75rem;
display: inline-block;
color: #fff;
text-shadow: 0 4px 3px rgba(54, 77, 108, 0.20);
text-shadow: 0 .25rem .1875rem rgba(54, 77, 108, 0.20);
}
.header-title .title-cn {
font-family: YouSheBiaoTiHei;
font-size: 18px;
line-height: 18px;
letter-spacing: 3px;
font-size: 1.125rem;
line-height: 1.125rem;
letter-spacing: .1875rem;
}
.header-title .title-en {
font-family: D-DINExp-Italic;
line-height: 8px;
font-size: 8px;
line-height: .5rem;
font-size: .5rem;
}
.header-nav {
float: right;
margin-top:.4375rem;
margin-right:0px;
}
.header-nav-notification {
/* color : #666; */
font-size: 16px;
color : #666;
font-size: 1rem;
}
.header-nav-user-img-wrapper {
display: inline;
margin: 14px 8px;
margin: .875rem .5rem;
}
.header-nav-user-img {
width: 36px;
height: 36px;
width: 2.25rem;
height: 2.25rem;
position: relative;
bottom: 2px;
bottom: .125rem;
}
.ant-menu{
background: transparent;
}

33
web-screen/client/src/layout/components/sider/index.js

@ -57,22 +57,23 @@ const Sider = (props) => {
return (
<Menu id="sider" mode="inline"
theme={'dark'}
selectedKeys={selectedKeys}
openKeys={openKeys}
onSelect={(e) => {
const { selectedKeys } = e;
setSelectedKeys(selectedKeys)
localStorage.setItem('xunjian_selected_sider', JSON.stringify(selectedKeys))
}}
onOpenChange={(openKeys) => {
setOpenKeys(openKeys)
localStorage.setItem('xunjian_open_sider', JSON.stringify(openKeys))
}}
>
{items}
</Menu>
<></>
// <Menu id="sider" mode="inline"
// theme={'dark'}
// selectedKeys={selectedKeys}
// openKeys={openKeys}
// onSelect={(e) => {
// const { selectedKeys } = e;
// setSelectedKeys(selectedKeys)
// localStorage.setItem('xunjian_selected_sider', JSON.stringify(selectedKeys))
// }}
// onOpenChange={(openKeys) => {
// setOpenKeys(openKeys)
// localStorage.setItem('xunjian_open_sider', JSON.stringify(openKeys))
// }}
// >
// {items}
// </Menu>
)
}

89
web-screen/client/src/layout/constants/weather.js

@ -0,0 +1,89 @@
export const WEATHERArr = [
{
lbl: '未知',
key: 'weizhi',
},
{
lbl: '暴雪',
key: 'baoxue',
},
{
lbl: '多云',
key: 'duoyun',
},
{
lbl: '浮沉',
key: 'fucheng',
},
{
lbl: '雷阵雨',
key: 'leizhengyu',
},
{
lbl: '霾',
key: 'mai',
},
{
lbl: '强沙尘暴',
key: 'qiangshachengbao',
},
{
lbl: '晴',
key: 'qing',
},
{
lbl: '沙尘暴',
key: 'shachengbao',
},
{
lbl: '特大暴雨',
key: 'tedabaoyu',
},
{
lbl: '雾',
key: 'wu',
},
{
lbl: '小雪',
key: 'xiaoxue',
},
{
lbl: '小雨',
key: 'xiaoyu',
},
{
lbl: '扬沙',
key: 'yangsha',
},
{
lbl: '阴天',
key: 'yingtian',
},
{
lbl: '雨夹雪',
key: 'yujiaxue',
},
{
lbl: '阵雨',
key: 'zhenyu',
},
{
lbl: '中雪',
key: 'zhongxue',
},
{
lbl: '中雨',
key: 'zhongyu',
},
{
lbl: '大雨',
key: 'dayu',
},
];
export const WEATHERMARGIN = 1 * 60 * 60 * 1000;
export let ICONSMAP = {};
WEATHERArr.forEach(({ lbl, key }) => {
const icon = `/assets/images/weather/${key}.png`;
ICONSMAP[lbl] = icon;
});

102
web-screen/client/src/layout/containers/layout/index.js

@ -12,6 +12,9 @@ import { resize } from '../../actions/global';
import * as NProgress from 'nprogress';
import PerfectScrollbar from 'perfect-scrollbar';
import { getUserSiteList } from '../../actions/site';
import BigScreen from '../../../sections/bigScreen/containers/bigScreen'
import { setCurrentTab } from "../../../utils/someStores";
NProgress.configure({
template: `
<div class="bar" style="height:2px" role="bar">
@ -27,14 +30,13 @@ const footerHeight = 0
let scrollbar
const LayoutContainer = props => {
const { dispatch, msg, user, copyright, children, sections, clientWidth, clientHeight, location, match, routes, history, sites } = props
const { dispatch, msg, user, copyright, children, sections, clientWidth, clientHeight, location, match, routes, history, sites,globalTab} = props
const [collapsed, setCollapsed] = useState(false)
NProgress.start();
const resize_ = (collapsed) => {
const extraHeight = headerHeight + footerHeight;
console.log(document.body.clientHeight);
dispatch(resize(
document.body.clientHeight - extraHeight - 12,
document.body.clientWidth - (collapsed ? 120 : 220)
@ -43,10 +45,16 @@ const LayoutContainer = props => {
useEffect(() => {
resize_(collapsed)
scrollbar = new PerfectScrollbar('#page-content', { suppressScrollX: true });
// scrollbar = new PerfectScrollbar('#page-content', { suppressScrollX: true });
}, [])
const tabChange = (tab) => {
setCurrentTab(tab);
dispatch({ type: 'TAB-CHANGE', data: tab })
}
useEffect(() => {
NProgress.done();
if (!user || !user.authorized) {
@ -61,63 +69,48 @@ const LayoutContainer = props => {
message.error(msg.error);
}
}
const dom = document.getElementById('page-content');
if (dom) {
scrollbar.update();
dom.scrollTop = 0;
}
// const dom = document.getElementById('page-content');
// if (dom) {
// scrollbar.update();
// dom.scrollTop = 0;
// }
})
return (
<Layout id="layout">
<Layout.Header style={{ padding: 0 }}>
<Header
user={user}
pathname={location.pathname}
toggleCollapsed={() => {
setCollapsed(!collapsed);
resize_(!collapsed)
}}
collapsed={collapsed}
history={history}
/>
</Layout.Header>
<Layout>
<Layout.Sider trigger={null} collapsible collapsed={collapsed} theme={'dark'}>
<Sider
sections={sections}
dispatch={dispatch}
user={user}
pathname={location.pathname}
collapsed={collapsed}
/>
</Layout.Sider>
<Layout.Content id="page-content" style={{
position: 'relative',
marginTop: '12px',
padding: '0px 20px 20px',
height: clientHeight,
background: '#F2F3F5'
}}>
<div style={{ minWidth: 520 }}>
<div style={{ padding: '0px 16px 4px' }}>
<Breadcrumbs routes={routes} />
</div>
<div>
{children}
</div>
</div>
</Layout.Content>
{/* <Layout.Footer {...footerProps}>
{copyright}
</Layout.Footer> */}
</Layout>
</Layout>
<div id="super-screen-container">
<Layout id="layout">
<Layout.Header style={{ padding: 0,zIndex:1}}>
<Header
tabChange={tabChange}
user={user}
pathname={location.pathname}
toggleCollapsed={() => {
setCollapsed(!collapsed);
resize_(!collapsed)
}}
collapsed={collapsed}
history={history}
/>
</Layout.Header>
<Layout>
<Layout.Content id="page-content" style={{
position: 'relative',
marginTop: '12px',
padding: '0px 20px 20px',
height: clientHeight,
background: 'transparent',
}}>
<BigScreen globalTab={globalTab} />
</Layout.Content>
</Layout>
</Layout>
</div>
)
}
function mapStateToProps(state) {
const { global, auth, ajaxResponse } = state;
const { global, auth, ajaxResponse,globalTab } = state;
return {
title: global.title,
copyright: global.copyright,
@ -127,7 +120,8 @@ function mapStateToProps(state) {
clientHeight: global.clientHeight,
msg: ajaxResponse.msg,
user: auth.user,
sites: global.sites
sites: global.sites,
globalTab:globalTab?.tab
};
}

21
web-screen/client/src/layout/containers/layout/index.less

@ -120,4 +120,25 @@
border: none !important;
background-color: #F9F9F9 !important;
}
}
#layout {
width: 100%;
height: 100vh;
background-image: url('/assets/bigScreen/bg.png');
overflow: hidden;
color: #fff;
// height: 100%;
background-size: 100% 100%;
background-repeat: no-repeat;
}
.ant-layout {
background-color: transparent !important;
}
#nav {
.ant-menu-overflow {
background: transparent !important;
}
}

5
web-screen/client/src/layout/reducers/index.js

@ -8,8 +8,9 @@
import global from './global';
import ajaxResponse from './ajaxResponse';
import globalTab from './tab'
export default {
global,
ajaxResponse
ajaxResponse,
globalTab
};

24
web-screen/client/src/layout/reducers/tab.js

@ -0,0 +1,24 @@
'use strict';
import Immutable from 'immutable';
const initState = {
tab: 'error',
showCG: true
};
export default function globalTab(state = initState, action) {
switch (action.type) {
case 'TAB-CHANGE':
return Immutable.fromJS(state).merge({
tab: action.data
}).toJS();
case 'SHOW-CG-CLOSE':
return Immutable.fromJS(state).merge({
showCG: false
}).toJS();
default:
return state;
}
}

2
web-screen/client/src/sections/auth/containers/login.js

@ -33,7 +33,7 @@ const Login = props => {
const [form] = Form.useForm();
const tourl = () => {
return '/shouye'
return '/bigScreen'
if (Func.isAuthorized("STRU_INFO_CONFIG")) {
return '/projectRegime/information'
}

6
web-screen/client/src/sections/bigScreen/actions/index.js

@ -0,0 +1,6 @@
'use strict';
import leader from './leader'
export default {
...leader,
}

111
web-screen/client/src/sections/bigScreen/actions/leader.js

@ -0,0 +1,111 @@
'use strict';
import { basicAction } from '@peace/utils'
import { ApiTable } from '$utils'
export function findPatrolRecords(query) {
return dispatch => basicAction({
type: 'get',
query,
dispatch: dispatch,
actionType: 'FIND_PATROL_RECORDS',
url: `${ApiTable.findPatrolRecords}`,
msg: { error: '获取巡检记录数' },
reducer: { name: 'patrolRecords'}
});
}
export function findPatrolRate(query) {
return dispatch => basicAction({
type: 'get',
query,
dispatch: dispatch,
actionType: 'FIND_PATROL_RATE',
url: `${ApiTable.findPatrolRate}`,
msg: { error: '获取巡检率失败' },
reducer: { name: 'patrolRate'}
});
}
export function countByProject(query) {
return dispatch => basicAction({
type: 'get',
query,
dispatch: dispatch,
actionType: 'COUNT_BY_PROJECT',
url: `${ApiTable.countByProject}`,
msg: { error: '获取巡检排名失败' },
reducer: { name: 'patrolRank'}
});
}
export function getDevices(query) {
return dispatch => basicAction({
type: 'get',
query,
dispatch: dispatch,
actionType: 'GET_DEVICES',
url: `${ApiTable.getDevices}`,
msg: { error: '获取设备寿命相关' },
reducer: { name: 'devices'}
});
}
export function getHistoricalFaults(query) {
return dispatch => basicAction({
type: 'get',
query,
dispatch: dispatch,
actionType: 'GET_HISTORICAL_FAULTS',
url: `${ApiTable.getHistoricalFaults}`,
msg: { error: '获取历史故障统计' },
reducer: { name: 'historyFaults'}
});
}
export function getFaultsRank(query) {
return dispatch => basicAction({
type: 'get',
query,
dispatch: dispatch,
actionType: 'GET_FAULTS_RANK',
url: `${ApiTable.getFaultsRank}`,
msg: { error: '获取故障排名' },
reducer: { name: 'faultsRank'}
});
}
export function getCenterData(query) {
return dispatch => basicAction({
type: 'get',
query,
dispatch: dispatch,
actionType: 'GET_CENTER_DATA',
url: `${ApiTable.getCenterData}`,
msg: { error: '获取大屏中心数据' },
reducer: { name: 'centerData'}
});
}
export default{
findPatrolRecords,
findPatrolRate,
countByProject,
getDevices,
getHistoricalFaults,
getFaultsRank,
getCenterData
}

140
web-screen/client/src/sections/bigScreen/components/AutoRollComponent.js

@ -0,0 +1,140 @@
'use strict'
import React, { Component } from 'react';
import { Row, Col } from 'antd';
export default class AutoRollComponent extends Component {
constructor(props) {
super(props);
this.scrollElem = null;
this.stopscroll = false;
this.preTop = 0;
this.cloneEle = null;
this.currentTop = 0;
this.marqueesHeight = 0;
this.interval = null;
this.state = {
enabledScroll: false
}
}
get enabledScroll() {
let scrollElem = document.getElementById(this.props.divId);
let fatherElem = scrollElem?.parentNode || null;
if (scrollElem && fatherElem) {
return scrollElem.scrollHeight > fatherElem.scrollHeight
}
return false;
}
marque = (height) => {
try {
this.scrollElem = document.getElementById(this.props.divId);
this.marqueesHeight = height;
if (this.scrollElem) {
this.scrollElem.style.height = this.marqueesHeight;
console.log('marqueesHeight',this.marqueesHeight)
this.scrollElem.style.overflow = 'hidden';
}
this.repeat();
} catch (e) { console.log(e) }
}
repeat = () => {
this.scrollElem.scrollTop = 0;
let offset = 1.5
this.interval = setInterval(() => {
if (this.stopscroll) return;
this.currentTop = this.currentTop + offset;
this.preTop = this.scrollElem.scrollTop;
this.scrollElem.scrollTop = this.scrollElem.scrollTop + offset;
// console.log(`this.scrollElem.scrollTop:${this.scrollElem.scrollTop} === this.preTop:${this.preTop}`);
if (this.preTop === this.scrollElem.scrollTop) {
this.scrollElem.scrollTop = this.marqueesHeight;
this.scrollElem.scrollTop = this.scrollElem.scrollTop + offset;
}
}, 100);
}
componentWillUnmount() {
clearInterval(this.interval);
}
componentWillReceiveProps(nextProps) {
requestAnimationFrame(() => {
if (this.enabledScroll) {
if (!this.state.enabledScroll) {
this.setState({ enabledScroll: true }, () => {
this.marque(10)
})
}
}
})
}
componentDidMount() {
if (this.enabledScroll) {
this.setState({ enabledScroll: true }, () => {
this.marque(10)
})
}
}
onMouseOver = () => {
this.stopscroll = true;
}
onMouseOut = () => {
this.stopscroll = false;
}
render() {
const { changeStyleCol, heads, spans, data, divId, divHeight, content, containerStyle = {} } = this.props;
return (
<div style={{ ...containerStyle, textAlign: 'left' }}>
{
heads ?
<Row style={{ lineHeight: '40px', height: 40 }}>
{heads.map((c, index) => {
return <Col span={spans[index]} key={index}>{c}</Col>
})
}
</Row> : ''
}
<div id={divId} style={{ overflow: 'hidden', height: divHeight }} onMouseOver={this.onMouseOver} onMouseOut={this.onMouseOut}>
<div>
{content ? content : ''}
{this.state.enabledScroll && content ? content : ''}
{
data ?
data.map((q, idx) => {
return (
<div key={idx}>
<Row gutter={16} style={{ borderBottom: '0px solid grey' }}>
{heads.map((c, index) => {
return <Col span={spans[index]} key={index} style={{ paddingTop: 8, textAlign: 'left', wordBreak: 'break-word', color: `${c === changeStyleCol ? q.changeColor : ''}` }}>
{index == 1 ? q.data[index] == -1 ? "-" : q.data[index] : index == 2 ? q.data[1] == -1 ? '-' : q.data[index] : q.data[index]}</Col>
})
}
</Row>
</div>
)
}) : ''
}
<div style={{ margin: 16 }}></div>
</div>
</div>
</div >
)
}
}

40
web-screen/client/src/sections/bigScreen/components/error/bottom.js

@ -0,0 +1,40 @@
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 moment from 'moment'
import ReactEcharts from 'echarts-for-react'
import PerfectScrollbar from 'perfect-scrollbar'
import '../style.less'
const Bottom = props => {
const { dispatch, clientHeight, clientWidth, actions } = props
return (
<>
</>
)
}
function mapStateToProps(state) {
const { auth, global } = state
return {
clientHeight: global.clientHeight,
clientWidth: global.clientWidth,
actions: global.actions,
}
}
export default connect(mapStateToProps)(Bottom)

38
web-screen/client/src/sections/bigScreen/components/error/index.js

@ -0,0 +1,38 @@
//故障管理
'use strict';
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 moment from 'moment';
import Bottom from './bottom';
import Top from './top';
import '../style.less'
const Error = (props) => {
const {actions,dispatch, globalTab, } = props
return <>
<Top/>
<Bottom/>
</>
}
function mapStateToProps(state) {
const { auth, global, device } = state;
return {
clientHeight: global.clientHeight,
actions: global.actions,
};
}
export default connect(mapStateToProps)(Error)

21
web-screen/client/src/sections/bigScreen/components/error/style.less

@ -0,0 +1,21 @@
.errorContainer {
display: flex;
width: 33.625rem;
height: 20.3125rem;
margin-top: 2.6875rem;
background-image: linear-gradient(180deg, #0080ff00 3%, #0080ff1a 54%, #0080ff3d 100%);
.topHeader {
// width: 33.375rem;
// height: 2.6875rem;
background: url(/assets/bigScreen/cardHeader.png) no-repeat;
background-size: 100% 100%;
height: 2.6875rem;
font-family: PangMenZhengDao;
font-size: 1.125rem;
color: #CCE6FF;
letter-spacing: .0231rem;
display: flex;
padding-left: 2.375rem;
justify-content: space-between,
}
}

49
web-screen/client/src/sections/bigScreen/components/error/top.js

@ -0,0 +1,49 @@
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 moment from 'moment'
import ReactEcharts from 'echarts-for-react'
import PerfectScrollbar from 'perfect-scrollbar'
import './style.less'
const Top = props => {
const { dispatch, clientHeight, clientWidth, actions } = props
return (
<>
<div style={{display:'flex'}}>
{/* 上1 */}
<div style={{ position: 'relative' }} className='errorContainer'>
<div className='topHeader'>
<div>故障等级分布</div>
</div>
</div>
{/* 上2 */}
<div style={{ position: 'relative' }} className='errorContainer'>
<div className='topHeader'>
<div>问题发现趋势30</div>
</div>
</div>
</div>
</>
)
}
function mapStateToProps(state) {
const { auth, global } = state
return {
clientHeight: global.clientHeight,
clientWidth: global.clientWidth,
actions: global.actions,
}
}
export default connect(mapStateToProps)(Top)

50
web-screen/client/src/sections/bigScreen/components/homePages.js

@ -0,0 +1,50 @@
import React, { Component } from 'react';
// import './style.less';
import { push } from 'react-router-redux';
import Leader from './leader';
import Run from './run'
import Error from './error';
import { Modal, Form } from 'antd';
// const { RouteTable, RouteRequest, ApiTable, Request } = WebAPI;
// const model = {
// 'leader': Leader,
// 'run':Run,
// 'error':Error,
// 'inspection':Inspection,//巡检
// 'item':Item,//巡检项
// 'Calendar':Calendar,//日历
// 'report':Report,//报告
// 'aiot':Aiot,
// // 'device':Device,
// 'statistics':Statistics,//设备统计
// 'list':List,//设备台账
// 'expert':Expert,
// 'info':Info,
// 'task':Task
// };
// console.log('Leader',Leader)
const HomePage=(props)=>{
const{globalTab}=props
return <>
{globalTab === 'leader' && <Leader />}
{globalTab === 'run' && <Run />}
{globalTab === 'error' && <Error />}
{/* {globalTab === 'run' && <Run />}
{globalTab === 'error' && <Error />}
{globalTab === 'inspection' && <Inspection />} */}
</>
}
export default HomePage;

61
web-screen/client/src/sections/bigScreen/components/leader/index.js

@ -0,0 +1,61 @@
//领导驾驶舱
'use strict';
import Left from './left';
import Right from './right';
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 moment from 'moment';
import '../style.less'
const Leader = (props) => {
const {actions,dispatch, globalTab, } = props
const {bigScreen}=actions
const [centerData,setCenterData]=useState({})
const fontStyle={
fontSize:'1.2rem',
}
useEffect(()=>{
const structArr = JSON.parse(sessionStorage.getItem('user')).monitorObject
dispatch(bigScreen.getCenterData({projectId:structArr.toString()})).then(res=>{
if(res.success){
console.log('res.payload.data',res.payload.data)
setCenterData(res.payload.data)
}
})
},[])
return <>
<div style={{ display: 'flex', justifyContent: 'space-between',position:'relative' }}>
<Left></Left>
<Right></Right>
</div>
<div style={{position:'absolute',top:'-10rem',left:'27rem',}}>
<img src='/assets/bigScreen/center.webp' style={{display:'inline-block' ,width: '64.47rem', height: '62.3125rem'}}></img>
</div>
<div style={{position:'absolute',top:'15.6rem',left:'38rem',}}><span>设备总数</span><span>{centerData.deviceCount}</span></div>
<div style={{position:'absolute',top:'12rem',left:'45rem',}}><span>发现问题</span><span>{centerData.questions}</span></div>
<div style={{position:'absolute',top:'15rem',left:'57.5rem',}}><div>{centerData.handleCount}</div> <span></span></div>
<div style={{position:'absolute',top:'12rem',left:'68rem',}}><span>出具报告数</span><span>{centerData.records}</span></div>
<div style={{position:'absolute',top:'15.6rem',left:'75rem',}}><span>处理故障数</span><span>{centerData.reportCount}</span></div>
</>
}
function mapStateToProps(state) {
const { auth, global, device } = state;
return {
clientHeight: global.clientHeight,
actions: global.actions,
};
}
export default connect(mapStateToProps)(Leader)

473
web-screen/client/src/sections/bigScreen/components/leader/left.js

@ -0,0 +1,473 @@
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 moment from 'moment'
import ReactEcharts from 'echarts-for-react'
import PerfectScrollbar from 'perfect-scrollbar'
import '../style.less'
import AutoRollComponent from '../AutoRollComponent'
let scrollbarLeft
let problems
const Left = props => {
const { dispatch, clientHeight, clientWidth, actions } = props
const [startTime, setStartTime] = useState(moment().startOf('week').format('YYYY-MM-DD HH:mm:ss'))
const [endTime, setEndTime] = useState(moment().endOf('week').format('YYYY-MM-DD HH:mm:ss'))
const [plan, setPlan] = useState(0)
const [done, setDone] = useState(0)
const [cRate, setCRate] = useState([]) //完成率
const [rRate, setRRate] = useState([]) //修复率
const [months, setMonths] = useState([])
const [period, setPeriod] = useState('week')
const [rank,setRank]=useState([])
const [state, setState] = useState(0)
const [rankCopy,setRankCopy]=useState([])//用于排序(巡检次数)
const { bigScreen } = actions
// const questFontColor = { color: '#8f7a49' } //有问题的颜色
const normalFontColor = { color: 'yellow' } //正常的颜色
useEffect(() => {
scrollbarLeft = new PerfectScrollbar('#left1', { suppressScrollX: true })
const dom = document.getElementById('left1')
if (dom) {
scrollbarLeft.update()
// dom.scrollTop = 0
}
// getData()
queryData()
const months = generateMonthNames()
setMonths(months)
}, [])
useEffect(() => {
getData()
}, [startTime])
const queryData = async () => {
let repair = []
const structArr = JSON.parse(sessionStorage.getItem('user')).monitorObject
const sTime = moment().subtract(6, 'months').format('YYYY-MM-DD HH:mm:ss')
const eTime = moment().format('YYYY-MM-DD HH:mm:ss')
// let count={}
let cRate = []
for (let i = 5; i >= 0; i--) {
const currentTime = moment()
const startOfMonth = currentTime.subtract(i, 'months').startOf('month')
const startTime = startOfMonth.format('YYYY-MM-DD HH:mm:ss')
const endTime = startOfMonth.endOf('month').format('YYYY-MM-DD HH:mm:ss')
await dispatch(bigScreen.findPatrolRecords({ projectId: structArr.toString(), startTime, endTime })).then(res => {
if (res.success) {
const data = res.payload.data
// count[i] = { plan: data.planCount, done: data.done, rate:((data.done/data.planCount+data.done)*100).toFixed(2) }
cRate.push(
Number(data.planCount + data.done > 0 ? ((data.done / (data.planCount + data.done)) * 100).toFixed(2) : 0)
)
}
})
}
setCRate(cRate)
dispatch(bigScreen.findPatrolRate({ projectId: structArr.toString(), startTime: sTime, endTime: eTime })).then(
res => {
if (res.success) {
const data = res.payload.data
if (data && data.length) {
const repairObject = {}
for (let i = 0; i < 6; i++) {
const currentDate = moment().subtract(i, 'months')
const month = currentDate.month() + 1
const year = currentDate.year()
repairObject[`${year}-${month}`] = { done: 0, quests: 0, ratio: 0 }
}
data.forEach(item => {
const createTime = moment(item.patrolRecordIssueHandles[0].createTime)
const month = createTime.month()+1
const year = createTime.year()
const state = item.patrolRecordIssueHandles[0].state
if (state === 6) {
repairObject[`${year}-${month}`].done++
}
repairObject[`${year}-${month}`].quests++
})
//计算比例
Object.values(repairObject).forEach(item => {
repair.push(item.quests === 0 ? 0 : Number((item.done / item.quests * 100).toFixed(2)));
});
setRRate(repair)
}
}
}
)
dispatch(bigScreen.countByProject({ projectId: structArr.toString()})).then(res=>{
if(res.success){
//[{},{},{},{},{},{},{},{}]||
// [{id:1,issueHandleCount:1,patrolRecordCount:2},
// {id:1,issueHandleCount:2,patrolRecordCount:3},
// {id:1,issueHandleCount:3,patrolRecordCount:2},
// {id:1,issueHandleCount:6,patrolRecordCount:4},
// {id:1,issueHandleCount:7,patrolRecordCount:5},
// {id:1,issueHandleCount:10,patrolRecordCount:6},
// {id:1,issueHandleCount:11,patrolRecordCount:7},
// {id:1,issueHandleCount:19,patrolRecordCount:9}]
const data=res.payload.data
if(data.length>2){
let problemstop = 0
let problemsId = document.getElementById('left1');
if (problems) clearInterval(problems)
if (problemsId) {
function problemstart () {
problems = setInterval(() => {
problemstop += 5
problemsId.scrollTop = problemstop
if (problemsId.scrollTop >= problemsId.scrollHeight / 2) problemstop = 0, problemsId.scrollTop = problemstop
}, 500);
problemsId.onmouseover = () => clearInterval(problems)
}
problemsId.onmouseout = () => problemstart()
setTimeout(problemstart(), 1000);
}
}
if(data.length>10){
setRank(data.slice(0,10))
}else{
setRank(data)
}
setRankCopy(data)
}
})
}
function generateMonthNames() {
const currentTime = moment()
const monthNames = []
for (let i = 0; i < 6; i++) {
const currentMonth = currentTime.clone().subtract(i, 'months').format('MMMM')
monthNames.unshift(currentMonth)
}
return monthNames
}
const getData = () => {
const structArr = JSON.parse(sessionStorage.getItem('user')).monitorObject
dispatch(bigScreen.findPatrolRecords({ projectId: structArr.toString(), startTime, endTime })).then(res => {
if (res.success) {
const data = res.payload.data
setPlan(data.planCount)
setDone(data.done)
}
})
}
const selectTime = time => {
if (time === 'week') {
setPeriod('week')
setStartTime(moment().startOf('week').format('YYYY-MM-DD HH:mm:ss'))
setEndTime(moment().endOf('week').format('YYYY-MM-DD HH:mm:ss'))
} else {
setPeriod('month')
setStartTime(moment().startOf('month').format('YYYY-MM-DD HH:mm:ss'))
setEndTime(moment().endOf('month').format('YYYY-MM-DD HH:mm:ss'))
}
}
const topClick=(e)=>{
switch (e) {
case 'perBottom':
const rs=rankCopy.sort((a,b)=>b.patrolRecordCount-a.patrolRecordCount)
if(rs.length>10){
setRank(rs.slice(0,10))
}else{
setRank(rs)
}
setState(1)
break;
case 'perTop':
const rs1=rankCopy.sort((a,b)=>a.patrolRecordCount-b.patrolRecordCount)
if(rs1.length>10){
setRank(rs1.slice(0,10))
}else{
setRank(rs1)
}
setState(0)
break;
case 'timeBottom':
const rs2=rankCopy.sort((a,b)=>b.issueHandleCount-a.issueHandleCount)
if(rs2.length>10){
setRank(rs2.slice(0,10))
}else{
setRank(rs2)
}
setState(3)
break;
case 'timeTop':
const rs3=rankCopy.sort((a,b)=>a.issueHandleCount-b.issueHandleCount)
if(rs3.length>10){
setRank(rs3.slice(0,10))
}else{
setRank(rs3)
}
setState(2)
break;
default:
return
}
}
return (
<>
{/* 左一 */}
{/**backgroundImage: 'linear-gradient(269deg, #000080ff 0%, #1a0080ff 58%, #3d0080ff 100%)' */}
<div>
<div style={{ position: 'relative' }} className='contanier'>
<div
style={{
display: 'flex',
justifyContent: 'space-between',
height: '2.6875rem',
background: 'url(/assets/bigScreen/cardHeader.png)',
backgroundSize: '100% 100%',
backgroundRepeat: 'no-repeat',
}}>
<div className='title'>巡检数据统计</div>
<div className='monthOrWeek'>
<span
style={{ color: period === 'week' ? '#6EECE9' : '#8FCFFF' }}
onClick={() => {
selectTime('week')
}}>
</span>
&nbsp; | &nbsp;
<span
style={{ color: period === 'month' ? '#6EECE9' : '#8FCFFF' }}
onClick={() => {
selectTime('month')
}}>
</span>
</div>
</div>
<div
style={{
width: '26.3125rem',
height: '9.5625rem',
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
}}>
<img src='/assets/bigScreen/doneRate.png'></img>
</div>
<div className='rate' style={{ bottom: '6.5125rem', left: '11.875rem' }}>
{`${plan > 0 ? ((done / done + plan) * 100).toFixed(2) : 0}%`}
</div>
<div style={{ display: 'flex', justifyContent: 'center', marginTop: '.5rem' }}>当前巡检完成率</div>
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
<div
style={{
background: 'url(/assets/bigScreen/plan.png)',
width: '10.375rem',
height: '3.625rem',
position: 'relative',
marginLeft: '.7rem',
backgroundSize: '100% 100%',
}}>
<div
style={{
position: 'absolute',
bottom: '1.7rem',
right: '2.35rem',
width: '3.75rem',
height: '.75rem',
textAlign: 'center',
}}
className='plan'>
计划巡检数
</div>
<div
style={{
position: 'absolute',
bottom: '.1044rem',
right: '2.35rem',
width: '3.75rem',
height: '1.375rem',
textAlign: 'center',
}}
className='number'>
{plan}
</div>
</div>
<div
style={{
background: 'url(/assets/bigScreen/plan.png)',
width: '10.375rem',
height: '3.625rem',
position: 'relative',
marginLeft: '.7rem',
backgroundSize: '100% 100%',
marginRight: '.625rem',
}}>
<div
style={{
position: 'absolute',
bottom: '1.7rem',
right: '1.75rem',
width: '4.75rem',
height: '.75rem',
textAlign: 'center',
}}
className='plan'>
已完成巡检数
</div>
<div
style={{
position: 'absolute',
bottom: '.1044rem',
right: '1.75rem',
width: '4.75rem',
height: '1.375rem',
textAlign: 'center',
}}
className='number'>
{done}
</div>
</div>
</div>
</div>
{/* 左二 */}
<div className='contanier' style={{ height: '17.8125rem' }}>
<div
style={{
display: 'flex',
justifyContent: 'space-between',
height: '2.5rem',
background: 'url(/assets/bigScreen/cardHeader.png)',
backgroundSize: '100% 100%',
backgroundRepeat: 'no-repeat',
}}>
<div className='title'>巡检成效评估</div>
{/* <div className='monthOrWeek'>{'更多' + '>>'}</div> */}
</div>
<ReactEcharts
style={{ height: '13.4375rem', width: '25.2662rem' }}
option={{
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow',
},
},
legend: {
right: 0,
textStyle: { color: '#CCE6FF' },
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
containLabel: true,
},
xAxis: [
{
type: 'category',
data: months,
},
],
yAxis: [
{
type: 'value',
name: '单位%',
},
],
series: [
{
name: '巡检完成率',
nameTextStyle: {
color: ' #CCE6FF',
fontSize: 14,
fontFamily: 'SourceHanSansCN-Medium',
},
type: 'bar',
stack: 'Search Engine',
// emphasis: {
// focus: 'series'
// },
data: cRate,
},
{
name: '故障修复率',
type: 'bar',
stack: 'Search Engine',
// emphasis: {
// focus: 'series'
// },
data: rRate,
},
],
}}></ReactEcharts>
</div>
{/* 左三 */}
<div className='contanier'>
<div
style={{
display: 'flex',
justifyContent: 'space-between',
height: '2.5rem',
background: 'url(/assets/bigScreen/cardHeader.png)',
backgroundSize: '100% 100%',
backgroundRepeat: 'no-repeat',
}}>
<div className='title'>巡检次数排名</div>
</div>
<div className='header' style={{ width: '24.3125rem' }}>
<div className='threeHeaderFontStyle'>序号</div>
<div className='threeHeaderFontStyle'>结构物</div>
<div className='threeHeaderFontStyle' style={{position:'relative'}}>巡检次数
<i class="angle_top" id={state==0?'angleSelected':null} onClick={()=>topClick('perTop')}></i>
<i id={state==1?'angleSelected':null} class="angle_bottom" onClick={()=>topClick('perBottom')}></i>
</div>
<div className='threeHeaderFontStyle'style={{position:'relative'}}>问题个数
<i class="angle_top" id={state==2?'angleSelected':null} onClick={()=>topClick('timeTop')}></i>
<i id={state==3?'angleSelected':null} class="angle_bottom" onClick={()=>topClick('timeBottom')}></i>
</div>
</div>
<div id='left1' style={{ width: '25.8125rem', height: '12rem', position: 'relative' }}>
{rank.map((item,index)=>(
<div className='thridCardItem' style={{ width: '24.3125rem' }}>
<div className='index1'>
<div>{index+1}</div>
</div>
<div className='name1'>{item.name}</div>
<div className='num1'>{item.patrolRecordCount}</div>
<div className='quests1' style={{ ...normalFontColor }}>
{item.issueHandleCount}
</div>
</div>
))}
</div>
</div>
</div>
</>
)
}
function mapStateToProps(state) {
const { auth, global } = state
return {
clientHeight: global.clientHeight,
clientWidth: global.clientWidth,
actions: global.actions,
}
}
export default connect(mapStateToProps)(Left)

317
web-screen/client/src/sections/bigScreen/components/leader/right.js

@ -0,0 +1,317 @@
import React, { useEffect, useState } from 'react'
import { Spin, Popconfirm, message, Button, Input, Progress } from 'antd'
import { connect } from 'react-redux'
import ProTable from '@ant-design/pro-table'
import moment from 'moment'
import ReactEcharts from 'echarts-for-react'
import '../style.less'
import AutoRollComponent from '../AutoRollComponent'
import PerfectScrollbar from 'perfect-scrollbar'
let scrollbar
let diviceScrollbar
let rank
let device
const Right = props => {
const { dispatch, clientHeight, clientWidth, actions } = props
const { bigScreen } = actions
const questFontColor = { color: '#8f7a49' } //有问题的颜色
const normalFontColor = { color: 'rgba(33, 106, 167)' } //正常的颜色
const [deviceLists, setDeviceLists] = useState([])
const [underGuarantee, setUnderGuarantee] = useState(0)
const [devicescount, setDevicesCount] = useState(0)
const [period, setPeriod] = useState('threeMonths')
const [startTime, setStartTime] = useState(moment().subtract(3, 'months').format('YYYY-MM-DD HH:mm:ss'))
const [endTime, setEndTime] = useState(moment().format('YYYY-MM-DD HH:mm:ss'))
const [projectName, setProjectName] = useState([])
const [historyFaults, setHistoryFaults] = useState([])
const [historyFaultsDone, setHistoryFaultsDone] = useState([])
const [rankData, setRankData] = useState([])
//历史故障切换时间变化
useEffect(() => {
const structArr = JSON.parse(sessionStorage.getItem('user')).monitorObject
dispatch(bigScreen.getHistoricalFaults({ projectId: structArr.toString(), startTime, endTime })).then(res => {
if (res.success) {
const data = res.payload.data
let nameArr = []
let historyFaults = []
let historyFaultsDone = []
data.map(item => {
nameArr.push(item.name)
historyFaults.push(Number(item.count))
historyFaultsDone.push(Number(item.bcount))
})
setProjectName(nameArr)
setHistoryFaults(historyFaults)
setHistoryFaultsDone(historyFaultsDone)
}
})
}, [period])
useEffect(() => {
scrollbar = new PerfectScrollbar('#right3', { suppressScrollX: true })
const dom = document.getElementById('right3')
if (dom) {
scrollbar.update()
// dom.scrollTop = 0
}
diviceScrollbar = new PerfectScrollbar('#right1', { suppressScrollX: true })
const doms = document.getElementById('right1')
if (doms) {
diviceScrollbar.update()
// dom.scrollTop = 0
}
getData()
}, [])
const getData = () => {
const structArr = JSON.parse(sessionStorage.getItem('user')).monitorObject
dispatch(bigScreen.getDevices({ projectId: structArr.toString() })).then(res => {
if (res.success) {
const data = res.payload.data
setDeviceLists(data.rs)
setUnderGuarantee(data.underGuarantee)
setDevicesCount(data.count)
if(data.length>2){
let problemstops = 0
let problemsId = document.getElementById('right1');
if (device) clearInterval(device)
if (problemsId) {
function problemstart () {
device = setInterval(() => {
problemstops += 5
problemsId.scrollTop = problemstops
if (problemsId.scrollTop >= problemsId.scrollHeight / 2) problemstops = 0, problemsId.scrollTop = problemstops
}, 500);
problemsId.onmouseover = () => clearInterval(device)
}
problemsId.onmouseout = () => problemstart()
setTimeout(problemstart(), 1000);
}
}
}
})
dispatch(bigScreen.getFaultsRank({ projectId: structArr.toString() })).then(res => {
if (res.success) {
//[{},{},{},{},{},{},{},{},{}] ||
const data = res.payload.data
setRankData(data)
if(data.length>2){
let problemstop = 0
let problemsId = document.getElementById('right3');
if (rank) clearInterval(rank)
if (problemsId) {
function problemstart () {
rank = setInterval(() => {
problemstop += 5
problemsId.scrollTop = problemstop
if (problemsId.scrollTop >= problemsId.scrollHeight / 2) problemstop = 0, problemsId.scrollTop = problemstop
}, 500);
problemsId.onmouseover = () => clearInterval(rank)
}
problemsId.onmouseout = () => problemstart()
setTimeout(problemstart(), 1000);
}
}
}
})
}
console.log('rank', rankData)
const selectTime = time => {
if (time === 'threeMonths') {
setPeriod('threeMonths')
setStartTime(moment().subtract(3, 'months').format('YYYY-MM-DD HH:mm:ss'))
} else if (time === 'halfYear') {
setPeriod('halfYear')
setStartTime(moment().subtract(6, 'months').format('YYYY-MM-DD HH:mm:ss'))
} else {
setPeriod('oneYear')
setStartTime(moment().subtract(12, 'months').format('YYYY-MM-DD HH:mm:ss'))
}
}
return (
<>
<div>
{/* 右一 */}
<div className='contanier1'>
<div className='cardHeader'>
<div className='title'>设备数据总览</div>
<div className='monthOrWeek'>{'更多' + '>>'}</div>
</div>
<div className='right1bg'>
<img src='/assets/bigScreen/qc.png'></img>
</div>
<div className='content'>
<div>
<div className='qcNum numFontStyle'>
{underGuarantee}&nbsp;<span className='ge'></span>
</div>
<div className='qcDescribe describeFontStyle'>质保数</div>
</div>
<div>
<div className='centerNum'>
{(((devicescount - underGuarantee) / devicescount) * 100).toFixed(0) || 0}%
</div>
<div className='pDescribe pLocation'>质保期比例</div>
</div>
<div>
<div className='deviceNum numFontStyle'>
{devicescount}&nbsp;<span className='ge'></span>
</div>
<div className='deviceDescribe describeFontStyle'>设备总数</div>
</div>
</div>
<div id='right1' style={{width:'25.25rem',height:'9.2875rem',position:'relative'}}>
{deviceLists?.map(item => (
<div className='percent'>
{/* <img src='/assets/bigScreen/arrow.png'></img> */}
<div className='describe'> {item.type}</div>
<Progress
format={percent => (percent === 100 ? '100%' : `${percent}`)}
strokeColor={'#6EECE9'}
trailColor={'#0B4A7A'}
percent={(item.count / devicescount).toFixed(2) * 100}
/>
</div>
))}
</div>
</div>
{/* 右2 */}
<div className='contanier' style={{ height: '17.8125rem' }}>
<div className='cardHeader'>
<div className='title'>历史故障统计</div>
<div className='monthOrWeek'>
<span
style={{ color: period === 'threeMonths' ? '#6EECE9' : '#8FCFFF' }}
onClick={() => {
selectTime('threeMonths')
}}>
三个月
</span>
&nbsp; | &nbsp;
<span
style={{ color: period === 'halfYear' ? '#6EECE9' : '#8FCFFF' }}
onClick={() => {
selectTime('halfYear')
}}>
{' '}
半年
</span>
&nbsp; | &nbsp;
<span
style={{ color: period === 'oneYear' ? '#6EECE9' : '#8FCFFF' }}
onClick={() => {
selectTime('oneYear')
}}>
{' '}
一年
</span>
</div>
</div>
{projectName.length ? (
<ReactEcharts
style={{ height: '13.4375rem', width: '25.2662rem' }}
option={{
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow',
},
},
legend: {
right: 0,
textStyle: { color: '#CCE6FF' },
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
containLabel: true,
},
xAxis: [
{
type: 'category',
data: projectName,
},
],
yAxis: [
{
type: 'value',
name: '单位次',
},
],
series: [
{
name: '故障数',
nameTextStyle: {
color: ' #CCE6FF',
fontSize: 14,
fontFamily: 'SourceHanSansCN-Medium',
},
type: 'line',
stack: 'Search Engine',
// emphasis: {
// focus: 'series'
// },
data: historyFaults,
},
{
name: '已闭环',
type: 'line',
stack: 'Search Engine',
// emphasis: {
// focus: 'series'
// },
data: historyFaultsDone,
},
],
}}></ReactEcharts>
) : (
'暂无数据'
)}
</div>
{/*右三 */}
<div className='contanier'>
<div className='cardHeader'>
<div className='title'>设备故障排名</div>
</div>
<div className='header1'>
<div className='threeHeaderFontStyle'>序号</div>
<div className='threeHeaderFontStyle'>设备名称</div>
<div className='threeHeaderFontStyle'>故障发生次数</div>
</div>
<div id='right3' style={{ width: '25.8125rem', height: '12rem', position: 'relative' }}>
{rankData.map((item, index) => (
<div className='thridCardItem' style={{ width: '24.3125rem' }}>
<div className='index'>
<div>{index + 1}</div>
</div>
<div className='name'>{item.name}</div>
<div className='quests' style={{ ...normalFontColor }}>
{item.count}
</div>
</div>
))}
</div>
</div>
</div>
</>
)
}
function mapStateToProps(state) {
const { auth, global, device } = state
return {
clientHeight: global.clientHeight,
clientWidth: global.clientWidth,
actions: global.actions,
}
}
export default connect(mapStateToProps)(Right)

72
web-screen/client/src/sections/bigScreen/components/run/index.js

@ -0,0 +1,72 @@
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 moment from 'moment'
import ReactEcharts from 'echarts-for-react'
import PerfectScrollbar from 'perfect-scrollbar';
import '../style.less'
import AutoRollComponent from '../AutoRollComponent'
import Left from './left';
import Right from './right';
let scrollbar
const Run = props => {
const { clientHeight, clientWidth } = props
const questFontColor = { color: '#8f7a49' }//有问题的颜色
const normalFontColor = { color: 'rgba(33, 106, 167)' }//正常的颜色
const [beginHeight, setBeginHeight] = useState(window.innerHeight); //
const [isFullScreen, setIsFullScreen] = useState(false); //是否全屏
window.onresize = () => {
console.log('x1111111',)
// 全屏
if (beginHeight!==window.innerHeight) {
console.log('全屏')
setIsFullScreen(true)
}
// 不是全屏
else {
console.log('不全屏')
setIsFullScreen(false)
}
}
useEffect(() => {
// scrollbar = new PerfectScrollbar('#left1', { suppressScrollX: true });
// const dom = document.getElementById('left1');
// console.log('dom', dom)
// if (dom) {
// scrollbar.update();
// dom.scrollTop = 0;
// }
}, [])
// useEffect(()=>{
// })
return (
<>
<div style={{display:'flex',}}>
<Left isFullScreen={isFullScreen}></Left>
<Right isFullScreen={isFullScreen}></Right>
</div>
</>
)
}
function mapStateToProps(state) {
const { auth, global, device } = state
return {
clientHeight: global.clientHeight,
clientWidth: global.clientWidth,
actions: global.actions,
}
}
export default connect(mapStateToProps)(Run)

213
web-screen/client/src/sections/bigScreen/components/run/left.js

@ -0,0 +1,213 @@
import React, { useEffect, useState } from 'react'
import { Spin, Popconfirm, message, Button, Input, Calendar, Col, Radio, Row, Select, Typography } from 'antd'
import { connect } from 'react-redux'
import ProTable from '@ant-design/pro-table'
import moment from 'moment'
import ReactEcharts from 'echarts-for-react'
import PerfectScrollbar from 'perfect-scrollbar';
import './style.less'
import AutoRollComponent from '../AutoRollComponent'
let scrollbar
const Left = props => {
const { clientHeight, clientWidth,isFullScreen} = props
const questFontColor = { color: '#8f7a49' }//有问题的颜色
const normalFontColor = { color: 'rgba(33, 106, 167)' }//正常的颜色
// useEffect(()=>{
// })
const onPanelChange = (value, mode) => {
console.log('value1', value);
};
const defaultDate = moment({ year: moment().year(), month: moment().month() });
return (
<>
<div >
{/* 左一 */}
<div className='left1Container'>
<Calendar
validRange={[moment(moment()).startOf('month'), moment(moment()).endOf('month')]}
fullscreen={false}
headerRender={({ value, type, onChange, onTypeChange }) => {
const start = 0;
const end = 12;
const monthOptions = [];
const current = value.clone();
const localeData = value.localeData();
const months = [];
for (let i = 0; i < 12; i++) {
current.month(i);
months.push(localeData.monthsShort(current));
}
for (let i = start; i < end; i++) {
monthOptions.push(
<Select.Option key={i} value={i} className="month-item">
{months[i]}
</Select.Option>,
);
}
const year = value.year();
const month = value.month();
const options = [];
for (let i = year - 10; i < year + 10; i += 1) {
// for (let j = 0; j < 12; j++) {
options.push(
<Select.Option key={i} value={i} className="year-item">
{`${i}`}
</Select.Option>)
// }
}
return (
<div className='calendarHeader'>
<div className='cardHeader'>
<div className='title' >巡检数据统计</div>
</div>
{/* <Typography.Title level={4}>巡检数据统计</Typography.Title> */}
<Row gutter={8}>
<Col>
<Select
size="small"
dropdownMatchSelectWidth={false}
className="my-year-select"
value={moment().format('yyy-MM')}
onChange={(newYear) => {
console.log('x11112', newYear)
const now = value.clone().year(newYear);
onChange(now);
}}
>
{options}
</Select>
</Col>
</Row>
</div>
);
}}
onPanelChange={onPanelChange}
/>
</div>
{/* 左二 */}
<div className='left1Container'>
<div className='cardHeader'>
<div className='title' >本周巡检统计</div>
</div>
<div className='row'>
<div >
<div className='smallP'>
<div style={{background:'url(/assets/bigScreen/ying.png) 0% 0% / 100% 100% no-repeat'}}></div>
<div className='describe1'>本周应检</div>
</div>
<span className='numbers'>100</span><span></span>
</div>
<div>
<div className='smallP'>
<div style={{background:'url(/assets/bigScreen/yij.png) 0% 0% / 100% 100% no-repeat',}}></div>
<div className='describe1'>本周已检</div>
</div>
<span className='numbers'>100</span><span></span>
</div>
</div>
<div className='row'>
<div >
<div className='smallP'>
<div style={{background:'url(/assets/bigScreen/daij.png) 0% 0% / 100% 100% no-repeat',}}></div>
<div className='describe1'>本周待验</div>
</div>
<span className='numbers'>100</span><span></span>
</div>
<div>
<div className='smallP'>
<div style={{background:'url(/assets/bigScreen/daic.png) 0% 0% / 100% 100% no-repeat',}}></div>
<div className='describe1'>待处理问题</div>
</div>
<span className='numbers'>100</span><span></span>
</div>
</div>
<div className='row'>
<div >
<div className='smallP'>
<div style={{background:'url(/assets/bigScreen/yic.png) 0% 0% / 100% 100% no-repeat',}}></div>
<div className='describe1'>已处理问题</div>
</div>
<span className='numbers'>100</span><span></span>
</div>
<div className='last'>
<div className='smallP'>
<div></div>
<div className='describe1'>待处理问题</div>
</div>
<span className='numbers'>100</span><span></span>
</div>
</div>
</div>
{/* 左三 */}
<div className='left1Container'>
<div className='cardHeader'>
<div className='title' >已处理问题</div>
</div>
<div className={'pieChartfs'}>
<ReactEcharts
style={{ height: !isFullScreen?'13.4375rem':'18.4375rem', width: '25.2662rem' }}
option={{
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow',
},
},
// legend: {
// right: 0,
// textStyle: { color: '#CCE6FF' }
// },
grid: {
left: '3%',
right: '4%',
bottom: '3%',
containLabel: true,
},
series: [
{
name: 'Access From',
type: 'pie',
radius: '50%',
data: [
{ value: 1048, name: '严重' },
{ value: 735, name: '中等' },
{ value: 580, name: '轻微' },
],
emphasis: {
itemStyle: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: 'rgba(0, 0, 0, 0.5)'
}
}
}
],
}}></ReactEcharts>
</div>
</div>
</div>
</>
)
}
function mapStateToProps(state) {
const { auth, global, device } = state
return {
clientHeight: global.clientHeight,
clientWidth: global.clientWidth,
actions: global.actions,
}
}
export default connect(mapStateToProps)(Left)

242
web-screen/client/src/sections/bigScreen/components/run/right.js

@ -0,0 +1,242 @@
import React, { useEffect, useState } from 'react'
import { Spin, Popconfirm, message, Button, Input, Progress } from 'antd';
import { connect } from 'react-redux';
import ProTable from '@ant-design/pro-table';
import moment from 'moment';
import PerfectScrollbar from 'perfect-scrollbar';
import ReactEcharts from 'echarts-for-react'
import '../style.less'
let scrollbar
const Right = (props) => {
const { clientHeight, clientWidth,isFullScreen } = props
const [beginHeight, setBeginHeight] = useState(window.innerHeight); //
// const [isFullScreen, setIsFullScreen] = useState(false); //是否全屏
const [index,setIndex]=useState('1')//默认第一个tab
useEffect(() => {
}, [])
useEffect(()=>{
scrollbar = new PerfectScrollbar('#redcordContent', { suppressScrollX: true });
const dom = document.getElementById('redcordContent');
console.log('dom', dom)
if (dom) {
scrollbar.update();
dom.scrollTop = 0;
}
},[window.innerHeight])
let header = [
{
name: '指挥中心'
},
{
name: '管廊本体'
},
{
name: '电梯系统'
},
{
name: '供配电系统'
},
{
name: '防雷与接地系统'
},
{
name: '燃气仓'
},
{
name: '给水仓'
},
{
name: '安防系统'
},
{
name: '高压电力仓'
},
]
//巡检记录表头
let recordHeader=[
{name:'结构物名称'},
{name:'上报人'},
{name:'上报时间'},
{name:'点位名称'},
{name:'问题来源'},
{name:'严重等级'},
{name:'当前状态'},
]
const ck=(index)=>{
setIndex(index)
}
return <>
<div>
{/* 右边的抬头 */}
<div className='runHeader'>
{header.map(item => {
switch (item.name) {
case '指挥中心':
return <div className={index === '1' ? 'bgSelectedStyle' : 'bgUnselectedStyle'}
onClick={()=>{ck('1')}}>{item.name+`(${1})`}</div>
case '管廊本体':
return <div className={index === '2' ? 'bgSelectedStyle' : 'bgUnselectedStyle'}
onClick={()=>{ck('2')}}>{item.name+`(${1})`}</div>
case '电梯系统':
return <div className={index === '3' ? 'bgSelectedStyle' : 'bgUnselectedStyle'}
onClick={()=>{ck('3')}}>{item.name+`(${1})`}</div>
case '供配电系统':
return <div className={index === '4' ? 'bgSelectedStyle' : 'bgUnselectedStyle'}
onClick={()=>{ck('4')}}>{item.name+`(${1})`}</div>
case '防雷与接地系统':
return <div className={index === '5' ? 'bgSelectedStyle' : 'bgUnselectedStyle'}
onClick={()=>{ck('5')}}>{item.name+`(${1})`}</div>
case '燃气仓':
return <div className={index === '6' ? 'bgSelectedStyle' : 'bgUnselectedStyle'}
onClick={()=>{ck('6')}}>{item.name+`(${1})`}</div>
case '给水仓':
return <div className={index === '7' ? 'bgSelectedStyle' : 'bgUnselectedStyle'}
onClick={()=>{ck('7')}}>{item.name+`(${1})`}</div>
case '安防系统':
return <div className={index === '8' ? 'bgSelectedStyle' : 'bgUnselectedStyle'}
onClick={()=>{ck('8')}}>{item.name+`(${1})`}</div>
case '管廊本体':
return <div className={index === '9' ? 'bgSelectedStyle' : 'bgUnselectedStyle'}
onClick={()=>{ck('9')}}>{item.name+`(${1})`}</div>
case '高压电力仓':
return <div className={index === '10' ? 'bgSelectedStyle' : 'bgUnselectedStyle'}
onClick={()=>{ck('10')}}>{item.name+`(${1})`}</div>
default:
break;
}
})}
</div>
{/* 中间的图片 */}
<div className='bigiImg'>
<img src='/assets/bigScreen/bigImg.png' className='bigiImg'></img>
</div>
{/* 巡检记录 */}
<div className='recordHeader'>
{recordHeader.map(item=>(<div className='recordTitle'>{item.name}</div>))}
</div>
<div id='redcordContent' className={isFullScreen?'fullStyle':'noFullStyle'} style={{ width: '88.6875rem', position: 'relative' }}>
<div className='redcordContent'>
<div>指挥中心</div>
<div>李一一</div>
<div>2023-11-02 09:23:35</div>
<div>燃气仓6仓</div>
<div>巡检上报</div>
<div>轻微</div>
<div>待制定计划</div>
</div>
<div className='redcordContent'>
<div>指挥中心</div>
<div>李一一</div>
<div>2023-11-02 09:23:35</div>
<div>燃气仓6仓</div>
<div>巡检上报</div>
<div>轻微</div>
<div>待制定计划</div>
</div>
<div className='redcordContent'>
<div>指挥中心</div>
<div>李一一</div>
<div>2023-11-02 09:23:35</div>
<div>燃气仓6仓</div>
<div>巡检上报</div>
<div>轻微</div>
<div>待制定计划</div>
</div>
<div className='redcordContent'>
<div>指挥中心</div>
<div>李一一</div>
<div>2023-11-02 09:23:35</div>
<div>燃气仓6仓</div>
<div>巡检上报</div>
<div>轻微</div>
<div>待制定计划</div>
</div>
<div className='redcordContent'>
<div>指挥中心</div>
<div>李一一</div>
<div>2023-11-02 09:23:35</div>
<div>燃气仓6仓</div>
<div>巡检上报</div>
<div>轻微</div>
<div>待制定计划</div>
</div>
<div className='redcordContent'>
<div>指挥中心</div>
<div>李一一</div>
<div>2023-11-02 09:23:35</div>
<div>燃气仓6仓</div>
<div>巡检上报</div>
<div>轻微</div>
<div>待制定计划</div>
</div>
<div className='redcordContent'>
<div>指挥中心</div>
<div>李一一</div>
<div>2023-11-02 09:23:35</div>
<div>燃气仓6仓</div>
<div>巡检上报</div>
<div>轻微</div>
<div>待制定计划</div>
</div>
<div className='redcordContent'>
<div>指挥中心</div>
<div>李一一</div>
<div>2023-11-02 09:23:35</div>
<div>燃气仓6仓</div>
<div>巡检上报</div>
<div>轻微</div>
<div>待制定计划</div>
</div>
<div className='redcordContent'>
<div>指挥中心</div>
<div>李一一</div>
<div>2023-11-02 09:23:35</div>
<div>燃气仓6仓</div>
<div>巡检上报</div>
<div>轻微</div>
<div>待制定计划</div>
</div>
<div className='redcordContent'>
<div>指挥中心最后</div>
<div>李一一</div>
<div>2023-11-02 09:23:35</div>
<div>燃气仓6仓</div>
<div>巡检上报</div>
<div>轻微</div>
<div>待制定计划</div>
</div>
</div>
</div>
</>
}
function mapStateToProps(state) {
const { auth, global, device } = state;
return {
clientHeight: global.clientHeight,
clientWidth: global.clientWidth,
actions: global.actions,
};
}
export default connect(mapStateToProps)(Right)

232
web-screen/client/src/sections/bigScreen/components/run/style.less

@ -0,0 +1,232 @@
.left1Container {
width: 26.3125rem;
height: 17.8125rem;
margin-top: 1.5rem;
.pieChartfs{
text-align: center;
height: 17.8125rem;
line-height: 17.8125rem
}
}
.cardHeader {
display: flex;
justify-content: space-between;
height: 2.5rem;
}
.title {
// width: 122px;
height: 1.3125rem;
font-family: PangMenZhengDao;
font-size: 1.3125rem;
color: #ffffff;
// letter-spacing: 0.95px;
margin-top: 0.4375rem;
margin-left: 2.375rem;
line-height: 1.5rem;
}
.calendarHeader {
background: url(/assets/bigScreen/cardHeader.png);
background-size: 100% 100%;
background-repeat: no-repeat;
display: flex;
justify-content: space-between;
}
.ant-row {
background: transparent !important;
// background: url(/assets/bigScreen/cardHeader.png) !important;
// background-size: 100% 100%;
background-repeat: no-repeat;
line-height: 2.5rem;
margin-right: 0.5rem !important;
}
.ant-picker-calendar {
background: transparent !important;
}
.ant-picker-calendar {
.ant-picker-panel {
// width: 3.4194rem;
// height: 2.3531rem;
background: #93c6f824 !important;
border-radius: 0 0 5rem 4.75rem 0;
}
}
.ant-picker-content th {
color: #ffffff !important;
}
.ant-picker-cell {
.ant-picker-cell-inner {
.ant-picker-calendar-date-value {
color: #ffffff !important;
}
}
}
.smallP {
display: flex;
align-items: flex-end;
}
.describe1 {
width: 6.1875rem;
height: 1.5rem;
font-family: SourceHanSansSC-Regular;
font-weight: 400;
font-size: 1rem;
color: #add7ff;
letter-spacing: 0.0456rem;
margin-left: 0.5rem;
}
.smallP :nth-child(1) {
background: url(/assets/bigScreen/ying.png);
background-size: 100% 100%;
background-repeat: no-repeat;
width: 2.0625rem;
height: 2.0625rem;
margin-top: 1rem;
}
.row {
display: flex;
justify-content: space-around;
}
.numbers {
height: 1rem;
font-family: D-DIN-Bold;
font-weight: 700;
font-size: 1rem;
color: #0fffee;
letter-spacing: 0.0456rem;
padding-left: 3.5rem;
}
.numbers+span {
padding-left: 3.5rem;
}
.last {
visibility: hidden;
}
.runHeader {
display: flex;
justify-content: space-around;
padding-top: 2rem;
padding-left: 2rem;
}
.bgUnselectedStyle {
background: url(/assets/bigScreen/runHeaderSelect.png);
background-repeat: no-repeat;
background-size: 100% 100%;
width: 10rem;
height: 4rem;
padding-left: 2.4rem;
line-height: 3.5rem;
cursor: pointer;
font-family: SourceHanSansSC-Regular;
font-size: 0.8rem;
color: #ADD7FF;
letter-spacing: .0231rem;
}
.bgSelectedStyle {
background: url(/assets/bigScreen/runHeaderSelected.png);
background-repeat: no-repeat;
background-size: 100% 100%;
width: 10rem;
height: 4rem;
padding-left: 2.4rem;
line-height: 3.5rem;
cursor: pointer;
font-family: SourceHanSansSC-Regular;
font-size: 0.8rem;
color: #FFFFFF;
letter-spacing: .0231rem;
}
.bigiImg {
width: 88.6594rem;
height: 33.5rem;
// padding-top: 0.5rem;
margin-left: 2rem;
// color: red;
// background: url(/assets/bigScreen/bigImg.png) no-repeat;
// background-size: 100% 100%;
}
.recordHeader {
width: 88.6875rem;
height: 2.5rem;
background: url(/assets/bigScreen/cardHeader.png) no-repeat;
background-size: 100% 100%;
margin-top: 1rem;
margin-left: 2rem;
display: flex;
padding-left: 3.375rem;
line-height: 2.5rem;
font-family: PangMenZhengDao-Regular;
font-weight: 400;
font-size: 1rem;
color: #FFFFFF;
letter-spacing: .0594rem;
// background-image: linear-gradient(180deg, #0080ff3d 0%, #0080ff1a 58%, #0080ff3d 100%);
}
.recordTitle {
text-align: start;
width:calc((100% - 2rem)/7)
}
.redcordContent{
width: 88.6875rem;
height: 2rem;
line-height: 2rem;
display: flex;
padding-left: 3.375rem;
margin-left: 2rem;
margin-top: .5rem;
background: url(/assets/bigScreen/blue.png) no-repeat;
background-size: 100% 100%;
// div:first-child{
// margin-left: 2rem;
// }
div{
// flex:1;
text-align: start;
width:calc((100% - 2rem)/7);
}
}
.redcordContent:hover{
background: url(/assets/bigScreen/yellow.png) no-repeat;
background-size: 100% 100%;
}
.fullStyle{
height: 19rem;
}
.noFullStyle{
height: 11rem;
}

464
web-screen/client/src/sections/bigScreen/components/style.less

@ -0,0 +1,464 @@
//计划巡检数和已完成数字体样式
.plan {
font-family: SourceHanSansCN-Bold;
font-weight: 700;
font-size: .75rem;
color: #dceff9;
letter-spacing: 0;
}
//数字样式
.number {
font-family: DIN-Medium;
font-weight: 500;
font-size: 1.125rem;
color: #6eece9;
letter-spacing: 0;
}
.rate {
width: 2.5rem;
height: 1.5rem;
font-family: DIN-Medium;
font-weight: 500;
font-size: 1.25rem;
color: #f4dba2;
letter-spacing: .0256rem;
position: absolute;
bottom: 5.8125rem;
left: 11.875rem;
}
.title {
// width: 122px;
height: 1.3125rem;
font-family: PangMenZhengDao;
font-size: 1.3125rem;
color: #ffffff;
// letter-spacing: 0.95px;
margin-top: .4375rem;
margin-left: 2.375rem;
line-height: 1.5rem;
}
.monthOrWeek {
height: .875rem;
font-family: SourceHanSansCN-Regular;
font-weight: 400;
font-size: .875rem;
// color: #6EECE9;//选中
// color: #8FCFFF;//未选中
letter-spacing: .0181rem;
margin-top: .75rem;
margin-right: 1.875rem;
line-height: .875rem;
cursor: pointer;
// color: #8FCFFF;
}
.header1 {
width: 24.3125rem;
height: 1.4rem;
display: flex;
justify-content: space-between;
}
.header1 div:first-child {
margin-left: .5rem;
width: 2.8125rem;
height: .875rem;
}
.header1> :nth-child(2) {
width: calc((100% - 3.625rem - .5rem - 1.8125rem)/2);
// height: .875rem;
}
.header1> :nth-child(3) {
width: calc((100% - 3.625rem - .5rem - 1.8125rem)/2);
// width: 3.625rem;
// height: .875rem;
}
.index {
margin-left: 0.5rem;
width: 2.8125rem;
// width: 33.3%;
padding-left: 0.5rem;
height: 1.1875rem;
// border-radius: 50%;
// margin-left: .5rem;
}
.index div {
height: 1.1875rem;
width: 1.1875rem;
border-radius: 50%;
background: rgba(33, 106, 167);
text-align: center;
}
.name {
width: calc((100% - 3.625rem - 0.5rem - 1.8125rem)/2);
// height: .875rem;
font-family: SourceHanSansCN-Regular;
font-weight: 400;
font-size: .875rem;
line-height: 1.1875rem;
color: #CCE6FF;
letter-spacing: .0181rem;
text-align: center;
}
.quests {
width: calc((100% - 3.625rem - 1.3rem - 1.8125rem)/2);
text-align: center;
// width: 3.625rem;
// height: .875rem;
line-height: .875rem;
text-align: center;
font-family: DIN-Regular;
font-weight: 400;
font-size: 1rem;
color: rgba(64, 110, 152);
// letter-spacing: .0206rem;
}
.header {
width: 23.3125rem;
height: 1.4rem;
display: flex;
justify-content: space-between;
}
.header div:first-child {
margin-left: .5rem;
width: 2.8125rem;
height: .875rem;
}
.header> :nth-child(2) {
width: calc((100% - 3.625rem - .5rem - 1.8125rem)/3);
// height: .875rem;
}
.header> :nth-child(3) {
width: calc((100% - 3.625rem - .5rem - 1.8125rem)/3);
// width: 3.625rem;
// height: .875rem;
}
.header> :nth-child(4) {
width: calc((100% - 3.625rem - .5rem - 1.8125rem)/3);
// width: 3.625rem;
// height: .875rem;
}
.index1 {
margin-left: 0.5rem;
width: 2.8125rem;
// width: 33.3%;
padding-left: 0.5rem;
height: 1.1875rem;
// border-radius: 50%;
// margin-left: .5rem;
}
.index1 div {
height: 1.1875rem;
width: 1.1875rem;
border-radius: 50%;
background: rgba(33, 106, 167);
text-align: center;
}
.name1 {
width: calc((100% - 3.625rem - .5rem - 1.8125rem)/3);
// height: .875rem;
font-family: SourceHanSansCN-Regular;
font-weight: 400;
font-size: .875rem;
line-height: 1.1875rem;
color: #CCE6FF;
letter-spacing: .0181rem;
text-align: center;
}
.num1 {
width: calc((100% - 3.625rem - .5rem - 1.8125rem)/3);
// height: .875rem;
font-family: DIN-Regular;
font-weight: 400;
font-size: 1rem;
text-align: center;
color: #6EECE9;
line-height: .875rem;
letter-spacing: .0206rem;
}
.quests1 {
width: calc((100% - 3.625rem - .5rem - 1.8125rem)/3);
text-align: center;
// width: 3.625rem;
// height: .875rem;
line-height: .875rem;
text-align: center;
font-family: DIN-Regular;
font-weight: 400;
font-size: 1rem;
color: rgba(64, 110, 152);
// letter-spacing: .0206rem;
}
.threeHeaderFontStyle {
// width: 33.3%;
height: .875rem;
font-family: SourceHanSansCN-Regular;
font-weight: 400;
font-size: .875rem;
color: #8FCFFF;
letter-spacing: .0181rem;
text-align: center;
}
.thridCardItem {
width: 23.3125rem;
height: 2.1875rem;
display: flex;
justify-content: space-between;
align-items: center;
background: rgba(1, 43, 96);
margin-top: .875rem;
overflow: hidden;
}
.thirdCardItem>div {
text-align: center;
}
.thridCardItem:hover {
background: #8f7a49;
}
//右边
.contanier1 {
width: 26.3125rem;
height: 18.8125rem;
margin-top: 2.6875rem;
position: relative;
background-image: linear-gradient(-45deg, #0080ff00 0%, #0080ff1a 58%, #0080ff3d 100%);
// margin-bottom: 0.5rem
}
;
.cardHeader {
display: flex;
justify-content: space-between;
height: 2.5rem;
background: url(/assets/bigScreen/cardHeader.png);
background-size: 100% 100%;
background-repeat: no-repeat,
}
.right1bg {
width: 26.3125rem;
height: 7.1875rem;
display: flex;
justify-content: center;
align-items: center;
margin-top: 1rem;
}
.content {
display: flex;
justify-content: center;
}
.qcNum {
position: absolute;
top: 4.7rem;
left: 5rem;
// width: 1.375rem;
// height: 1.5rem;
}
.numFontStyle {
width: 3.625rem;
height: 1.5rem;
font-family: DIN-Medium;
font-weight: 500;
font-size: 1.25rem;
color: #6EECE9;
letter-spacing: 0;
text-align: center;
}
.ge {
width: .75rem;
height: .75rem;
font-family: SourceHanSansCN-Regular;
font-weight: 400;
font-size: .75rem;
color: #6EECE9;
letter-spacing: 0;
}
.qcDescribe {
position: absolute;
top: 7.8rem;
left: 5rem;
}
.describeFontStyle {
width: 3.625rem;
height: .875rem;
font-family: SourceHanSansCN-Regular;
font-weight: 400;
font-size: .875rem;
color: #CCE6FF;
letter-spacing: 0;
text-align: center;
}
.deviceNum {
position: absolute;
top: 4.7rem;
right: 5rem;
}
.deviceDescribe {
position: absolute;
top: 7.8rem;
right: 5rem;
}
.centerNum {
position: absolute;
top: 5.6rem;
right: 11.3rem;
width: 3.5rem;
height: 31px;
font-family: DIN-Medium;
font-weight: 500;
font-size: 1.625rem;
color: #6EECE9;
letter-spacing: -0.1163rem;
text-align: center;
}
.pDescribe {
width: 3.5rem;
height: .75rem;
font-family: SourceHanSansCN-Medium;
font-weight: 500;
font-size: .75rem;
color: #CCE6FF;
letter-spacing: -0.0537rem;
position: absolute;
top: 7.9rem;
right: 11.3rem;
text-align: center;
}
.percent {
margin-top: 1rem;
width: 23.25rem;
height: 2.875rem;
display: flex;
background: url(/assets/bigScreen/arrow.png);
background-repeat: no-repeat;
background-size: 100% 100%;
justify-content: space-between;
align-items: center;
}
.describe {
width: 8rem;
height: .875rem;
font-family: SourceHanSansCN-Regular;
font-weight: 400;
font-size: .875rem;
color: #CCE6FF;
letter-spacing: 0;
margin-left: 2rem;
margin-top: -1rem;
}
.ant-progress-line {
margin-top: -0.5rem !important;
}
.ant-progress-text {
color: #6EECE9 !important;
}
.contanier {
width: 26.3125rem;
height: 18.8125rem;
margin-top: 2.6875rem;
background-image: linear-gradient(-45deg, #0080ff00 0%, #0080ff1a 58%, #0080ff3d 100%);
}
.angle_top {
content: '';
width: 0;
height: 0;
display: block;
border-style: solid;
border-width: 0 .375rem .375rem;
// border-color: transparent transparent #5e5e5e;
position: absolute;
transform: rotate(180deg);
bottom: -0.3187rem;
right: 0.425rem;
}
.angle_bottom {
content: '';
width: 0;
height: 0;
display: block;
border-style: solid;
border-width: 0 .375rem .375rem;
// border-color: transparent transparent #5e5e5e;
position: absolute;
top: 0.325rem;
right:0.425rem;
}
.angle_bottom,.angle_top:first-child{
border-color: transparent transparent #5e5e5e;
}
.angle_bottom,.angle_top:nth-child(2){
border-color: transparent transparent #5e5e5e;
}
.angle_bottom,.angle_top:nth-child(3){
border-color: transparent transparent #5e5e5e;
}
#angleSelected {
/* 添加选中时的样式 */
border-color: transparent transparent blue
}

30
web-screen/client/src/sections/bigScreen/containers/bigScreen.js

@ -0,0 +1,30 @@
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 moment from 'moment';
import HomePage from '../components/homePages';
const BigScreen=(props)=> {
const {globalTab,}=props
return (
<HomePage globalTab={globalTab}/>
)
}
function mapStateToProps(state) {
const {
auth, global, device
} = state;
return {
clientHeight: global.clientHeight,
actions: global.actions,
};
}
export default connect(mapStateToProps)(BigScreen);

13
web-screen/client/src/sections/bigScreen/index.js

@ -0,0 +1,13 @@
'use strict';
import reducers from './reducers';
import routes from './routes';
import actions from './actions';
export default {
key: 'bigScreen',
name: '首页',
reducers: reducers,
routes: routes,
actions: actions,
};

0
web-screen/client/src/sections/bigScreen/reducers/index.js

13
web-screen/client/src/sections/bigScreen/routes.js

@ -0,0 +1,13 @@
'use strict';
import BigScreen from './containers/bigScreen';
export default [{
type: 'inner',
route: {
path: '/bigScreen',
key: 'bigScreen',
// breadcrumb: '首页',
component: BigScreen,
}
}];

49
web-screen/client/src/utils/fs.js

@ -0,0 +1,49 @@
(function flexible(window, document) {
var docEl = document.documentElement;
// 获取当前显示设备的物理像素分辨率与CSS像素分辨率之比;
var dpr = window.devicePixelRatio || 1;
//根据分辨率调整全局字体大小
function setBodyFontSize() {
// html已完成加载,则立即调整字体大小,否则等待html加载完成再调整字体大小
if (document.body) {
document.body.style.fontSize = 12 * dpr + "px";
} else {
// 监听DOMContentLoaded 事件——当初始的 HTML 文档被完全加载和解析完成之后触发,无需等待样式表
document.addEventListener("DOMContentLoaded", setBodyFontSize);
}
}
setBodyFontSize();
// 根据屏幕宽度,重置1rem的长度为当前屏幕宽度的1/10
function setRemUnit() {
var rem = docEl.clientWidth / 120
// 1rem的值永远为根元素的字体大小,所以此处通过调整全局字体大小来重置rem
docEl.style.fontSize = rem + "px";
}
setRemUnit();
// 监听resize事件——屏幕大小发生变化时触发
window.addEventListener("resize", setRemUnit);
// 监听pageshow事件——显示页面时触发
window.addEventListener("pageshow", function(e) {
// 若是浏览器中点击后退时显示页面,则重置rem
if (e.persisted) {
setRemUnit();
}
});
// 检测是否支持0.5px
if (dpr >= 2) {
var fakeBody = document.createElement("body");
var testElement = document.createElement("div");
testElement.style.border = ".5px solid transparent";
fakeBody.appendChild(testElement);
docEl.appendChild(fakeBody);
if (testElement.offsetHeight === 1) {
docEl.classList.add("hairlines");
}
docEl.removeChild(fakeBody);
}
})(window, document);

22
web-screen/client/src/utils/someStores.js

@ -0,0 +1,22 @@
import * as echarts from 'echarts';
const TB = Symbol("当前选中的tab");
export function setCurrentTab(tab) {
window[TB] = tab
}
export function getCurrentTab(tab) {
return window[TB]
}
const EC = Symbol("echarts symbol");
if (!window[EC]) {
window[EC] = echarts;
}
export function getEcharts() {
return window[EC];
}

11
web-screen/client/src/utils/webapi.js

@ -169,7 +169,18 @@ export const ApiTable = {
getNetworks:'network',
addOrUpdateNetwork:'network',
delNetwork:'network/{id}',
findPatrolRecords:'bigScreen/patrolRecord',
findPatrolRate:'bigScreen/patrolRate',
countByProject:'bigScreen/patrolCount',
getDevices:'bigScreen/devices/guarantee',
getHistoricalFaults:'bigScreen/historicalFaults',
getFaultsRank:'bigScreen/faultsRank',
getCenterData:'bigScreen/centerData',
};
//
export const RouteTable = {
apiRoot: '/api/root',

4
web-screen/package.json

@ -67,6 +67,7 @@
"@ant-design/pro-table": "^2.48.0",
"@antv/g6": "^4.2.5",
"@fs/attachment": "^1.0.0",
"@jiaminghi/data-view-react": "^1.2.5",
"@peace/components": "0.0.35",
"@peace/utils": "0.0.37",
"ahooks": "^3.7.4",
@ -82,6 +83,7 @@
"cross-env": "^7.0.3",
"crypto-js": "^4.1.1",
"echarts": "^5.4.1",
"echarts-for-react": "^3.0.2",
"file-saver": "^2.0.5",
"form-data": "^3.0.0",
"fs-attachment": "^1.0.0",
@ -96,9 +98,9 @@
"npm": "^7.20.6",
"qrcode": "^1.5.1",
"qs": "^6.10.1",
"react-color": "^2.19.3",
"react-dnd": "^7",
"react-dnd-html5-backend": "^7",
"react-color": "^2.19.3",
"react-router-breadcrumbs-hoc": "^4.0.1",
"react-sortable-hoc": "^2.0.0",
"shortid": "^2.2.16",

76
web-screen/routes/weather/index.js

@ -0,0 +1,76 @@
const request = require('superagent');
let _weatherUrl = '';
const qweatherUrl = 'https://api.qweather.com/v7/weather/3d'
const qweatherKey = '8f5704332b95425c987dca82b5ea0a5e'
module.exports.entry = function (app, router, opts) {
const { weatherUrl } = opts;
_weatherUrl = weatherUrl;
// _weatherUrl = "https://weatherssj.anxinyun.cn/weatherApp/weather/getImmeData"
router.get('/query/weather', weather);
router.get('/query/weather/3d', weather3day);
};
async function weather(ctx) {
try {
const { cname } = ctx.query;
const reg = /.+?(省|市|自治区|自治州|县|区)/g;
const arr = cname.match(reg);
if (Array.isArray(arr)) {
let cityName = arr[0];
if (arr[1]) {
cityName = arr[1];
}
const weatherRes = await request.post(_weatherUrl).send({ cityName });
const { body } = weatherRes;
if (body && body.data) {
ctx.status = 200;
ctx.body = { ...body.data, cname, texta: _weatherUrl };
} else {
ctx.status = 400;
ctx.body = { msg: '获取天气错误' };
}
} else {
ctx.status = 400;
ctx.body = { msg: '地址解析错误' };
}
} catch (error) {
console.log('[*err]', error);
ctx.status = 400;
ctx.body = error;
}
}
async function weather3day(ctx) {
try {
const { location } = ctx.query;
const weatherRes = await request.get(`${qweatherUrl}?key=${qweatherKey}&location=${location}`);
const { body } = weatherRes;
if (body) {
ctx.status = 200;
ctx.body = { ...body };
} else {
ctx.status = 400;
ctx.body = { msg: '获取天气错误' };
}
} catch (error) {
console.log('[*err]', error);
ctx.status = 400;
ctx.body = error;
}
}
// ip地址识别库
// 淘宝IP地址查询接口:http://ip.taobao.com/service/getIpInfo.php?ip=1.1.35.57
// 腾讯IP地址查询接口:http://fw.qq.com/ipaddress
// 新浪的IP地址查询接口:http://int.dpool.sina.com.cn/iplookup/iplookup.php?format=js
// 新浪多地域测试方法:http://int.dpool.sina.com.cn/iplookup/iplookup.php?format=js&ip=218.192.3.42
// 搜狐IP地址查询接口(默认GBK):http://pv.sohu.com/cityjson
// 搜狐IP地址查询接口(可设置编码):http://pv.sohu.com/cityjson?ie=utf-8
// 搜狐另外的IP地址查询接口:http://txt.go.sohu.com/ip/soip
// 谷歌IP地址查询接口:http://j.maxmind.com/app/geoip.js
// 1616 IP地址查询接口:http://w.1616.net/chaxun/iptolocal.php
// 126 http://ip.ws.126.net/ipquery hao123
// http://app.hao123.com/ipquery/getcity.php?rtype=2
// 太平洋电脑网 http://whois.pconline.com.cn/

2
web/client/src/sections/projectManagement/components/projectModel.js

@ -59,7 +59,7 @@ const ProjectModel = ({ dispatch, actions, user, modelData, close, success, proj
<Form.Item label='项目名称' name='projectName' initialValue={modelData?.projectName}
rules={[{ required: true, message: '请输入项目名称' },]}
>
<Input style={{ width: 210 }} placeholder='请输入项目名称' />
<Input style={{ width: 210 }} placeholder='请输入项目名称' maxLength={15}/>
</Form.Item>
<Form.Item name='projectDescribe' label="项目描述" initialValue={modelData?.projectDescribe}
rules={[{ required: true, message: '请输入项目描述' },]}

2
web/webpack.config.js

@ -32,7 +32,7 @@ module.exports = {
},
plugins: [
new webpack.HotModuleReplacementPlugin(),
new BundleAnalyzerPlugin({analyzerPort:5900}),
new BundleAnalyzerPlugin({analyzerPort:5901}),
],
module: {
rules: [{

Loading…
Cancel
Save