巴林闲侠 2 years ago
parent
commit
228f165742
  1. 6
      api/.vscode/launch.json
  2. 28
      api/app/lib/controllers/alarm/app.js
  3. 3
      api/app/lib/controllers/alarm/data.js
  4. 33
      api/app/lib/controllers/alarm/video.js
  5. 115
      api/app/lib/controllers/console/toolLink.js
  6. 90
      api/app/lib/controllers/control/analysis.js
  7. 195
      api/app/lib/controllers/control/data.js
  8. 367
      api/app/lib/controllers/control/toolLink.js
  9. 25
      api/app/lib/index.js
  10. 62
      api/app/lib/models/alarm_appear_record.js
  11. 71
      api/app/lib/models/alarm_confirm_log.js
  12. 62
      api/app/lib/models/email_send_log.js
  13. 80
      api/app/lib/models/latest_dynamic_list.js
  14. 14
      api/app/lib/routes/console/index.js
  15. 36
      api/app/lib/routes/control/index.js
  16. 7
      api/app/lib/utils/dataRange.js
  17. 1
      api/package.json
  18. BIN
      web/client/assets/images/problem/shield.png
  19. 4
      web/client/src/app.jsx
  20. 6
      web/client/src/layout/components/header/index.jsx
  21. 2
      web/client/src/sections/auth/containers/login.jsx
  22. 36
      web/client/src/sections/console/actions/console.js
  23. 7
      web/client/src/sections/console/actions/index.js
  24. 6
      web/client/src/sections/console/containers/index.js
  25. 48
      web/client/src/sections/control/actions/control.js
  26. 8
      web/client/src/sections/control/actions/index.js
  27. 228
      web/client/src/sections/control/containers/control.jsx
  28. 6
      web/client/src/sections/control/containers/index.js
  29. 0
      web/client/src/sections/control/containers/userCenter.jsx
  30. 2
      web/client/src/sections/control/index.js
  31. 4
      web/client/src/sections/control/nav-item.jsx
  32. 0
      web/client/src/sections/control/reducers/index.js
  33. 8
      web/client/src/sections/control/routes.js
  34. 0
      web/client/src/sections/control/style.less
  35. 15
      web/client/src/sections/problem/components/tableData.jsx
  36. 38
      web/client/src/sections/problem/containers/dataAlarm.jsx
  37. 2
      web/client/src/utils/webapi.js

6
api/.vscode/launch.json

@ -16,9 +16,9 @@
"-p 4600",
"-f http://localhost:4600",
//
// "-g postgres://postgres:123@10.8.30.32:5432/orational_service",
"-g postgres://postgres:123@10.8.30.32:5432/orational_service",
//
"-g postgres://FashionAdmin:123456@10.8.30.156:5432/POMS",
//"-g postgres://FashionAdmin:123456@10.8.30.156:5432/POMS",
"-k node35:6667,node36:6667,node37:6667",
"--iotaProxy http://10.8.30.157:17007",
"--redisHost 10.8.30.112",
@ -56,7 +56,7 @@
// "--clickHouseDataAlarm default",
//
"--clickHouseAnxincloud Anxinyun13",
"--clickHouseAnxincloud Anxinyun21",
"--clickHousePepEmis pepca8",
"--clickHouseProjectManage peppm8",
"--clickHouseVcmp video_access_dev",

28
api/app/lib/controllers/alarm/app.js

@ -327,8 +327,8 @@ async function apiErrorList (ctx) {
async function confirmApiError(ctx) {
try {
const models = ctx.fs.dc.models;
const { confirm, appAlarmId = [] } = ctx.request.body
const { confirm, appAlarmId = [], confirmPost } = ctx.request.body
const { pepUserId, projectCorrelationIds, alarmInfo } = confirmPost
await models.AppAlarm.update({
confirm,
confirmTime: moment().format()
@ -338,6 +338,30 @@ async function confirmApiError (ctx) {
}
})
//存日志
let logDatas = projectCorrelationIds.map(id => {
return {
pepUserId,
projectCorrelationId: id,
alarmInfo,//包含告警id,type,source
confirmTime: moment().format(),
confirmContent: confirm
}
})
let rslt = await models.AlarmConfirmLog.bulkCreate(logDatas, { returning: true });
//存最新动态
let dynamics = rslt.map(r => {
return {
time: r.confirmTime,
alarmConfirmId: r.id,
projectCorrelationId: r.projectCorrelationId,
type: 4//告警确认
}
})
await models.LatestDynamicList.bulkCreate(dynamics);
ctx.status = 204;
} catch (error) {
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);

3
api/app/lib/controllers/alarm/data.js

@ -247,7 +247,8 @@ function confirm (opts) {
const { models } = ctx.fs.dc;
const { utils: { kfkSendAsync } } = ctx.app.fs
const { clickHouse } = ctx.app.fs
const { content = '', alarmId } = ctx.request.body
const { content = '', alarmId, confirmPost } = ctx.request.body;
const { pepUserId, projectCorrelationIds, alarmInfo } = confirmPost;
// 发送告警恢复通知
// Topic: alarm
/*

33
api/app/lib/controllers/alarm/video.js

@ -21,7 +21,7 @@ async function deviceType (ctx) {
}
}
async function alarmList (ctx) {
async function alarmList(ctx, agg) {
try {
const { models } = ctx.fs.dc;
const { clickHouse } = ctx.app.fs
@ -249,8 +249,12 @@ async function alarmList (ctx) {
}
}
if (agg == 'day') {//控制台 按日聚集
return returnD
} else {
ctx.status = 200;
ctx.body = returnD
}
} catch (error) {
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
ctx.status = 400;
@ -263,8 +267,8 @@ async function alarmList (ctx) {
async function confirm (ctx) {
try {
const { models } = ctx.fs.dc;
const { alarmId, content } = ctx.request.body;
const { alarmId, content, confirmPost } = ctx.request.body;
const { pepUserId, projectCorrelationIds, alarmInfo } = confirmPost;
// TODO: 以视频·应用的秘钥进行鉴权
await ctx.app.fs.vcmpRequest.put('status/alarm/confirm', {
data: {
@ -272,6 +276,29 @@ async function confirm (ctx) {
}
})
//存日志
let logDatas = projectCorrelationIds.map(id => {
return {
pepUserId,
projectCorrelationId: id,
alarmInfo,//包含告警id,type,source
confirmTime: moment().format(),
confirmContent: content
}
})
let rslt = await models.AlarmConfirmLog.bulkCreate(logDatas, { returning: true });
//存最新动态
let dynamics = rslt.map(r => {
return {
time: r.confirmTime,
alarmConfirmId: r.id,
projectCorrelationId: r.projectCorrelationId,
type: 4//告警确认
}
})
await models.LatestDynamicList.bulkCreate(dynamics);
ctx.status = 204;
} catch (error) {
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);

115
api/app/lib/controllers/console/toolLink.js

@ -1,115 +0,0 @@
'use strict';
async function list (ctx) {
try {
const { models } = ctx.fs.dc;
const { userId, pepUserId } = ctx.fs.api
const linkListRes = await models.QuickLink.findAll({
attributes: { exclude: ['userId'] },
where: {
userId,
}
})
ctx.status = 200;
ctx.body = linkListRes
} catch (error) {
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
ctx.status = 400;
ctx.body = {
message: typeof error == 'string' ? error : undefined
}
}
}
async function edit (ctx) {
try {
const { models } = ctx.fs.dc;
const { userId, pepUserId } = ctx.fs.api
const { linkId, name, link } = ctx.request.body
if (!name || !link) {
throw '请将参数填写完整'
}
let findOption = {
where: {
userId: userId,
$or: [{
name,
}, {
link,
}]
}
}
if (linkId) {
findOption.where.id = { $ne: linkId }
}
const existRes = await models.QuickLink.findOne({
where: {
userId: userId,
$or: [{
name,
}, {
link,
}]
}
})
if (existRes) {
throw '已有相同名称/地址的工具'
}
if (linkId) {
await models.QuickLink.update({
name,
link,
}, {
where: {
id: linkId
}
})
} else {
await models.QuickLink.create({
userId,
name,
link,
})
}
ctx.status = 204;
} catch (error) {
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
ctx.status = 400;
ctx.body = {
message: typeof error == 'string' ? error : undefined
}
}
}
async function del (ctx) {
try {
const { models } = ctx.fs.dc;
const { userId, pepUserId } = ctx.fs.api
const { linkId } = ctx.params
await models.QuickLink.destroy({
where: {
id: linkId,
userId,
}
})
ctx.status = 204;
} catch (error) {
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
ctx.status = 400;
ctx.body = {
message: typeof error == 'string' ? error : undefined
}
}
}
module.exports = {
list, edit, del,
};

90
api/app/lib/controllers/control/analysis.js

@ -0,0 +1,90 @@
'use strict';
const moment = require('moment');
async function dataList (ctx) {
try {
const { models } = ctx.fs.dc;
const { userId, pepUserId, userInfo = {}, pepUserInfo } = ctx.fs.api
const { clickHouse } = ctx.app.fs
const { utils: { judgeSuper, anxinStrucIdRange, pomsProjectRange } } = ctx.app.fs
const { database: anxinyun } = clickHouse.anxinyun.opts.config
const { pepProjectId } = ctx.request.query
let anxinStruc = await anxinStrucIdRange({
ctx, pepProjectId
})
let pomsProject = await pomsProjectRange({
ctx, pepProjectId,
})
const pomsProjectIds = pomsProject.map(p => p.id)
if (anxinStruc.length) {
const anxinStrucIds = anxinStruc.map(a => a.strucId) || []
const dataAlarm = await clickHouse.dataAlarm.query(`
SELECT
formatDateTime(alarmData.StartTime,'%F %H') hours, count(AlarmId) count
FROM
( SELECT
AlarmId,State,StartTime
FROM
alarms
WHERE
alarms.StructureId IN (${anxinStrucIds.join(",")})
AND
AlarmGroup = 3) AS alarmData
GROUP BY hours
`).toPromise();
// const confirmedAlarm = dataAlarm
// // TODO: 开发临时注释
// .filter(ar => ar.State && ar.State > 2)
// .map(ar => "'" + ar.AlarmId + "'")
// // formatDateTime(Time,'%F %H') hours, count(AlarmId) count
// const dataConfirme = confirmedAlarm.length ?
// await clickHouse.dataAlarm.query(`
// SELECT
// max(Time) AS Time, AlarmId
// FROM
// alarm_details
// WHERE
// AlarmId IN (${confirmedAlarm.join(',')})
// GROUP BY AlarmId
// `).toPromise() :
// [];
// dataAlarm.forEach(ar => {
// ar.confirme =
// dataConfirme.find(as => as.AlarmId == ar.AlarmId) || {}
// })
ctx.status = 200
ctx.body = dataAlarm
} else {
ctx.status = 200
ctx.body = {
dataAlarm: 0,
}
}
} catch (error) {
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
ctx.status = 400;
ctx.body = {
message: typeof error == 'string' ? error : undefined
}
}
}
module.exports = {
dataList,
};

195
api/app/lib/controllers/control/data.js

@ -0,0 +1,195 @@
'use strict';
const moment = require('moment');
const { alarmList } = require('../alarm/video');
//工作台
async function getWorkbench(ctx) {
try {
const { models } = ctx.fs.dc;
const { clickHouse } = ctx.app.fs
const { alarmId, limit, page } = ctx.query
ctx.status = 200;
ctx.body = []
} catch (error) {
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
ctx.status = 400;
ctx.body = {
message: typeof error == 'string' ? error : undefined
}
}
}
//项目概览
async function getProjectsInfo(ctx) {
try {
const { models } = ctx.fs.dc;
const { clickHouse, utils: { judgeSuper, anxinStrucIdRange } } = ctx.app.fs
const { database: anxinyun } = clickHouse.anxinyun.opts.config
const { alarmId, limit, page, projectCorrelationId, pepProjectId, keywordTarget, keyword } = ctx.query;
const { userInfo } = ctx.fs.api;
// let where = {}
// if (!userInfo.role.includes('SuperAdmin') && !userInfo.role.includes('admin')) {
// where.projectCorrelationId = { $in: userInfo.correlationProject }
// }
// if (projectCorrelationId) {//查指定项目,控制台全局切换
// where.projectCorrelationId = projectCorrelationId
// }
let anxinStruc = await anxinStrucIdRange({
ctx, pepProjectId, keywordTarget, keyword
})
const anxinStrucIds = anxinStruc.map(a => a.strucId);
//先查全部的摄像头
const videoList = anxinStrucIds.length ? await clickHouse.vcmp.query(
`select camera.id,
camera.name,
camera.serial_no from camera where camera.delete=false and camera.recycle_time is null
LEFT JOIN ${anxinyun}.t_video_ipc AS anxinIpc
ON toString(anxinIpc.channel_no) = cameraAlarm.cameraChannelNo
AND anxinIpc.serial_no = cameraAlarm.cameraSerialNo
LEFT JOIN ${anxinyun}.t_structure AS anxinStruc
ON anxinStruc.id = anxinIpc.structure
AND anxinStruc.id IN (${anxinStrucIds.join(',')})
LEFT JOIN ${anxinyun}.t_video_ipc_station AS anxinIpcStation
ON anxinIpcStation.ipc = anxinIpc.id
LEFT JOIN ${anxinyun}.t_sensor AS anxinStation
ON anxinStation.id = anxinIpcStation.station`
).toPromise() : []
ctx.status = 200;
ctx.body = []
} catch (error) {
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
ctx.status = 400;
ctx.body = {
message: typeof error == 'string' ? error : undefined
}
}
}
//BI分析
async function getVideoAlarmsAggDay(ctx) {
try {
let videoAlarms = await alarmList(ctx, 'day');
let aggDayMap = [];
for (let a of videoAlarms) {
let exist = aggDayMap.find(ad => ad.day == moment(a.createTime).format('YYYY-MM-DD'));
if (exist) {
exist.total++;//总数
if (a.confirmTime || a.autoRestore) {
exist.done++;//已恢复
}
} else {
aggDayMap.push({ day: moment(a.createTime).format('YYYY-MM-DD'), total: 1, done: a.confirmTime || a.autoRestore ? 1 : 0 });
}
}
ctx.status = 200;
ctx.body = aggDayMap;
} catch (error) {
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
ctx.status = 400;
ctx.body = {
message: typeof error == 'string' ? error : undefined
}
}
}
//最新动态
async function getLatestDynamic(ctx) {
try {
const { models } = ctx.fs.dc;
const { limit, page, projectCorrelationId, types } = ctx.query;
const { userInfo } = ctx.fs.api;
const { clickHouse } = ctx.app.fs;
let where = { type: { $in: types.split(',') } }//传类型选择
if (!userInfo.role.includes('SuperAdmin') && !userInfo.role.includes('admin')) {
where.projectCorrelationId = { $in: userInfo.correlationProject }
}
if (projectCorrelationId) {//查指定项目,控制台全局切换
where.projectCorrelationId = projectCorrelationId
}
let news = await models.LatestDynamicList.findAll({//最新动态
include: [{
model: models.ProjectCorrelation,
where: { del: false },
attributes: ['id', 'name', 'pepProjectId'],
}, {
model: models.AlarmAppearRecord
}, {
model: models.EmailSendLog
}, {
model: models.AlarmConfirmLog
}],
where: where,
offset: Number(page) * Number(limit),
limit: Number(limit),
order: [['time', 'desc']],
});
//查项目名称 查用户名
let pepPojectIds = new Set(), notedUserIds = new Set();
for (let p of news) {
pepPojectIds.add(p.projectCorrelation.pepProjectId);
if (p.emailSendLog) {
notedUserIds.add(p.emailSendLog.toPepUserId);//通知 接收人
}
if (p.alarmConfirmLog && p.alarmConfirmLog.pepUserId) {
notedUserIds.add(p.alarmConfirmLog.pepUserId);//确认 操作者
}
}
let pepProjects = pepPojectIds.size ? await clickHouse.projectManage.query(`
SELECT id, project_name FROM t_pim_project WHERE id IN (${[...pepPojectIds]})
`).toPromise() : [];
let userPepRes = notedUserIds.size ? await clickHouse.pepEmis.query(
`SELECT DISTINCT user.id AS id, "user"."name" AS name FROM user WHERE user.id IN (${[...notedUserIds].join(',')})
`).toPromise() : []
let appear = [], notice = [], confirm = [];
news.map(d => {
let projectName = d.projectCorrelation.name || pepProjects.find(pp => pp.id == d.projectCorrelation.pepProjectId).project_name;
if (d.alarmAppearId) {
appear.push({
projectName,
...d.alarmAppearRecord
});
}
if (d.emailSendId) {
notice.push({
userName: userPepRes.find(u => u.id == d.emailSendLog.toPepUserId).name,
projectName,
...d.emailSendLog
});
}
if (d.alarmConfirmId) {
confirm.push({
userName: d.alarmConfirmLog.pepUserId ? userPepRes.find(u => u.id == d.alarmConfirmLog.pepUserId).name : '自动恢复',
projectName,
...d.alarmConfirmLog.dataValues
});
}
})
ctx.status = 200;
ctx.body = {
appear,//发现
notice,//通知
confirm//确认
};
} catch (error) {
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
ctx.status = 400;
ctx.body = {
message: typeof error == 'string' ? error : undefined
}
}
}
module.exports = {
getWorkbench,
getProjectsInfo,
getVideoAlarmsAggDay,
getLatestDynamic
};

367
api/app/lib/controllers/control/toolLink.js

@ -0,0 +1,367 @@
'use strict';
const moment = require('moment');
async function list (ctx) {
try {
const { models } = ctx.fs.dc;
const { userId, pepUserId } = ctx.fs.api
const linkListRes = await models.QuickLink.findAll({
attributes: { exclude: ['userId'] },
where: {
userId,
}
})
ctx.status = 200;
ctx.body = linkListRes
} catch (error) {
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
ctx.status = 400;
ctx.body = {
message: typeof error == 'string' ? error : undefined
}
}
}
async function edit (ctx) {
try {
const { models } = ctx.fs.dc;
const { userId, pepUserId } = ctx.fs.api
const { linkId, name, link } = ctx.request.body
if (!name || !link) {
throw '请将参数填写完整'
}
let findOption = {
where: {
userId: userId,
$or: [{
name,
}, {
link,
}]
}
}
if (linkId) {
findOption.where.id = { $ne: linkId }
}
const existRes = await models.QuickLink.findOne({
where: {
userId: userId,
$or: [{
name,
}, {
link,
}]
}
})
if (existRes) {
throw '已有相同名称/地址的工具'
}
if (linkId) {
await models.QuickLink.update({
name,
link,
}, {
where: {
id: linkId
}
})
} else {
await models.QuickLink.create({
userId,
name,
link,
})
}
ctx.status = 204;
} catch (error) {
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
ctx.status = 400;
ctx.body = {
message: typeof error == 'string' ? error : undefined
}
}
}
async function del (ctx) {
try {
const { models } = ctx.fs.dc;
const { userId, pepUserId } = ctx.fs.api
const { linkId } = ctx.params
await models.QuickLink.destroy({
where: {
id: linkId,
userId,
}
})
ctx.status = 204;
} catch (error) {
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
ctx.status = 400;
ctx.body = {
message: typeof error == 'string' ? error : undefined
}
}
}
async function count (ctx) {
try {
const { models } = ctx.fs.dc;
const { userId, pepUserId, userInfo = {}, pepUserInfo } = ctx.fs.api
const { clickHouse } = ctx.app.fs
const { utils: { judgeSuper, anxinStrucIdRange, pomsProjectRange } } = ctx.app.fs
const { database: anxinyun } = clickHouse.anxinyun.opts.config
const { pepProjectId } = ctx.request.query
let anxinStruc = await anxinStrucIdRange({
ctx, pepProjectId
})
let pomsProject = await pomsProjectRange({
ctx, pepProjectId,
})
const pomsProjectIds = pomsProject.map(p => p.id)
if (anxinStruc.length) {
const anxinStrucIds = anxinStruc.map(a => a.strucId) || []
const dataAlarm = await clickHouse.dataAlarm.query(`
SELECT
AlarmId,State,StartTime,AlarmGroup
FROM
alarms
WHERE
alarms.StructureId IN (${anxinStrucIds.join(",")})
`).toPromise();
const confirmedAlarm = dataAlarm
// TODO: 开发临时注释
.filter(ar => ar.State && ar.State > 2)
.map(ar => "'" + ar.AlarmId + "'")
//剩余数据告警
const dataSurplus = dataAlarm.filter(ar => ar.State && ar.State < 3 && ar.AlarmGroup < 4).length || 0
//剩余设备告警
const toolSurplus = dataAlarm.filter(ar => ar.State && ar.State < 3 && ar.AlarmGroup > 3).length || 0
//今日新增数据告警
const dataNewAdd = dataAlarm.filter(r => moment(moment().startOf('day').format('YYYY-MM-DD HH:mm:ss')).isBefore(r.StartTime) && moment(r.createTime).isBefore(moment().endOf('day').format('YYYY-MM-DD HH:mm:ss')) && r.AlarmGroup < 4).length || 0
const toolNewAdd = dataAlarm.filter(r => moment(moment().startOf('day').format('YYYY-MM-DD HH:mm:ss')).isBefore(r.StartTime) && moment(r.createTime).isBefore(moment().endOf('day').format('YYYY-MM-DD HH:mm:ss')) && r.AlarmGroup > 3).length || 0
//今日确认数据告警
const dataConfirme = confirmedAlarm.length ?
await clickHouse.dataAlarm.query(`
SELECT
max(Time) AS Time, AlarmId , max(Content) AS Content,
alarms.AlarmGroup AS AlarmGroup
FROM
alarm_details
LEFT JOIN alarms
ON alarm_details.AlarmId=alarms.AlarmId
WHERE
AlarmId IN (${confirmedAlarm.join(',')})
AND
alarm_details.Time >= '${moment().startOf('day').format('YYYY-MM-DD HH:mm:ss')}'
AND
alarm_details.Time <= '${moment().endOf('day').format('YYYY-MM-DD HH:mm:ss')}'
GROUP BY AlarmId,AlarmGroup
`).toPromise() :
[];
let findOption = {
where: {
'$app->projectCorrelations.id$': {
$in: pomsProjectIds
}
},
attributes: ['createTime', 'confirmTime'],
include: [{
model: models.App,
where: {
},
attributes: ['id', 'name'],
include: [{
model: models.ProjectCorrelation,
where: {
},
attributes: ['id'],
}]
}]
}
//应用总告警
const listRes = await models.AppAlarm.findAndCountAll(findOption)
//剩余应用告警
const appSurplus = listRes.rows.filter(r => !r.confirmTime).length || 0
//今日新增应用告警
const appNewAdd = listRes.rows.filter(r => moment(moment().startOf('day').format('YYYY-MM-DD HH:mm:ss')).isBefore(r.createTime) && moment(r.createTime).isBefore(moment().endOf('day').format('YYYY-MM-DD HH:mm:ss'))).length || 0
//今日确认应用告警
const appConfirme = listRes.rows.filter(r => moment(moment().startOf('day').format('YYYY-MM-DD HH:mm:ss')).isBefore(r.confirmTime) && moment(r.confirmTime).isBefore(moment().endOf('day').format('YYYY-MM-DD HH:mm:ss'))).length || 0
const alarmRes = anxinStrucIds.length ? await clickHouse.vcmp.query(
`
SELECT
cameraAlarm.cameraId AS cameraId,
cameraAlarm.cameraName AS cameraName,
cameraAlarm.cameraSerialNo AS cameraSerialNo,
cameraAlarm.cameraChannelNo AS cameraChannelNo,
cameraAlarm.alarmId AS alarmId,
cameraAlarm.createTime AS createTime,
cameraAlarm.platform AS platform,
cameraAlarm.confirmTime AS confirmTime,
camera_status_resolve.id AS resolveId,
camera_status.describe AS statusDescribe,
camera_status_resolve.resolve AS resolve,
"gbCamera".online AS cameraOnline,
anxinIpc.t_video_ipc.name AS anxinIpcPosition,
anxinStation.id AS anxinStationId,
anxinStation.name AS anxinStationName,
anxinStruc.name AS strucName,
anxinStruc.id AS strucId
FROM
(
SELECT
camera.id AS cameraId,
camera.gb_id AS gbId,
camera.name AS cameraName,
camera_status_alarm.id AS alarmId,
camera_status_alarm.create_time AS createTime,
camera_status_alarm.platform AS platform,
camera_status_alarm.status_id AS statusId,
camera_status_alarm.serial_no AS cameraSerialNo,
camera_status_alarm.channel_no AS cameraChannelNo,
camera_status_alarm.confirm_time AS confirmTime
FROM camera_status_alarm
INNER JOIN camera
ON camera.serial_no = camera_status_alarm.serial_no
AND camera.channel_no = camera_status_alarm.channel_no
LEFT JOIN vender
ON vender.id = camera.vender_id
WHERE
camera.delete = false
AND camera.recycle_time is null
AND alarmId IN (
SELECT camera_status_alarm.id AS alarmId
FROM camera_status_alarm
RIGHT JOIN ${anxinyun}.t_video_ipc
ON toString(${anxinyun}.t_video_ipc.channel_no) = camera_status_alarm.channel_no
AND ${anxinyun}.t_video_ipc.serial_no = camera_status_alarm.serial_no
${`WHERE ${anxinyun}.t_video_ipc.structure IN (${anxinStrucIds.join(',')})`
}
)
) AS cameraAlarm
LEFT JOIN camera_status
ON cameraAlarm.platform = camera_status.platform
AND cameraAlarm.statusId = camera_status.id
LEFT JOIN camera_status_resolve
ON camera_status_resolve.status_id = camera_status.id
LEFT JOIN "gbCamera"
ON "gbCamera".id = cameraAlarm.gbId
LEFT JOIN ${anxinyun}.t_video_ipc AS anxinIpc
ON toString(anxinIpc.channel_no) = cameraAlarm.cameraChannelNo
AND anxinIpc.serial_no = cameraAlarm.cameraSerialNo
LEFT JOIN ${anxinyun}.t_structure AS anxinStruc
ON anxinStruc.id = anxinIpc.structure
AND anxinStruc.id IN (${anxinStrucIds.join(',')})
LEFT JOIN ${anxinyun}.t_video_ipc_station AS anxinIpcStation
ON anxinIpcStation.ipc = anxinIpc.id
LEFT JOIN ${anxinyun}.t_sensor AS anxinStation
ON anxinStation.id = anxinIpcStation.station
`
).toPromise() : []
let returnD = []
let positionD = {}
// 每个设备一个告警
for (let a of alarmRes) {
if (positionD[a.cameraId]) {
let curD = returnD[positionD[a.cameraId].positionReturnD]
} else {
let d = {
cameraId: a.cameraId,
cameraName: a.cameraName,
createTime: a.createTime,
alarmId: a.alarmId,
confirmTime: a.confirmTime,
}
returnD.push(d)
positionD[a.cameraId] = {
positionReturnD: returnD.length - 1
}
}
}
//剩余视频告警
const videoSurplus = returnD.filter(r => !r.confirmTime).length || 0
//今日新增视频告警
const videoNewAdd = returnD.filter(r => moment(moment().startOf('day').format('YYYY-MM-DD HH:mm:ss')).isBefore(r.createTime) && moment(r.createTime).isBefore(moment().endOf('day').format('YYYY-MM-DD HH:mm:ss'))).length || 0
//今日确认视频告警
const videoConfirme = returnD.filter(r => moment(moment().startOf('day').format('YYYY-MM-DD HH:mm:ss')).isBefore(r.confirmTime) && moment(r.confirmTime).isBefore(moment().endOf('day').format('YYYY-MM-DD HH:mm:ss'))).length || 0
let findOptions = {
where: {
del: false
},
attributes: []
}
if (!userInfo.role.includes('SuperAdmin') && !userInfo.role.includes('admin')) {
findOptions.where.id = { $in: userInfo.correlationProject }
}
const projects = await models.ProjectCorrelation.findAndCountAll(findOptions)
ctx.status = 200;
ctx.body = {
dataSurplus: dataSurplus + videoSurplus,
dataNewAdd: dataNewAdd + videoNewAdd,
dataConfirme: appConfirme + dataConfirme.filter(r => r.AlarmGroup < 4).length,
toolSurplus: toolSurplus,
toolNewAdd: toolNewAdd,
toolConfirme: dataConfirme.filter(r => r.AlarmGroup > 3).length,
appSurplus: appSurplus,
appNewAdd: appNewAdd,
appConfirme: appConfirme,
projects: projects.count,
}
} else {
ctx.body = {
dataAlarm: 0,
}
}
ctx.status = 200;
} catch (error) {
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
ctx.status = 400;
ctx.body = {
message: typeof error == 'string' ? error : undefined
}
}
}
module.exports = {
list, edit, del, count
};

25
api/app/lib/index.js

@ -58,7 +58,7 @@ module.exports.models = function (dc) { // dc = { orm: Sequelize对象, ORM: Seq
});
const {
AppInspection, ProjectApp, ProjectCorrelation, AppAlarm, App
AppInspection, ProjectApp, ProjectCorrelation, AppAlarm, App, AlarmAppearRecord, AlarmConfirmLog, EmailSendLog, LatestDynamicList
} = dc.models;
AppInspection.belongsTo(App, { foreignKey: 'projectAppId', targetKey: 'id' });
@ -76,4 +76,27 @@ module.exports.models = function (dc) { // dc = { orm: Sequelize对象, ORM: Seq
AppAlarm.belongsTo(App, { foreignKey: 'projectAppId', targetKey: 'id' });
App.hasMany(AppAlarm, { foreignKey: 'projectAppId', sourceKey: 'id' });
AlarmAppearRecord.belongsTo(ProjectCorrelation, { foreignKey: 'projectCorrelationId', targetKey: 'id' });
ProjectCorrelation.hasMany(AlarmAppearRecord, { foreignKey: 'projectCorrelationId', sourceKey: 'id' });
AlarmConfirmLog.belongsTo(ProjectCorrelation, { foreignKey: 'projectCorrelationId', targetKey: 'id' });
ProjectCorrelation.hasMany(AlarmConfirmLog, { foreignKey: 'projectCorrelationId', sourceKey: 'id' });
EmailSendLog.belongsTo(ProjectCorrelation, { foreignKey: 'projectCorrelationId', targetKey: 'id' });
ProjectCorrelation.hasMany(EmailSendLog, { foreignKey: 'projectCorrelationId', sourceKey: 'id' });
LatestDynamicList.belongsTo(AlarmAppearRecord, { foreignKey: 'alarmAppearId', targetKey: 'id' });
AlarmAppearRecord.hasMany(LatestDynamicList, { foreignKey: 'alarmAppearId', sourceKey: 'id' });
LatestDynamicList.belongsTo(EmailSendLog, { foreignKey: 'emailSendId', targetKey: 'id' });
EmailSendLog.hasMany(LatestDynamicList, { foreignKey: 'emailSendId', sourceKey: 'id' });
LatestDynamicList.belongsTo(AlarmConfirmLog, { foreignKey: 'alarmConfirmId', targetKey: 'id' });
AlarmConfirmLog.hasMany(LatestDynamicList, { foreignKey: 'alarmConfirmId', sourceKey: 'id' });
LatestDynamicList.belongsTo(ProjectCorrelation, { foreignKey: 'projectCorrelationId', targetKey: 'id' });
ProjectCorrelation.hasMany(LatestDynamicList, { foreignKey: 'projectCorrelationId', sourceKey: 'id' });
};

62
api/app/lib/models/alarm_appear_record.js

@ -0,0 +1,62 @@
/* eslint-disable*/
'use strict';
module.exports = dc => {
const DataTypes = dc.ORM;
const sequelize = dc.orm;
const AlarmAppearRecord = sequelize.define("alarmAppearRecord", {
id: {
type: DataTypes.INTEGER,
allowNull: false,
defaultValue: null,
comment: null,
primaryKey: true,
field: "id",
autoIncrement: true,
unique: "alarm_appear_record_id_uindex"
},
projectCorrelationId: {
type: DataTypes.INTEGER,
allowNull: false,
defaultValue: null,
comment: null,
primaryKey: false,
field: "project_correlation_id",
autoIncrement: false
},
alarmInfo: {
type: DataTypes.JSON,
allowNull: false,
defaultValue: null,
comment: null,
primaryKey: false,
field: "alarm_info",
autoIncrement: false
},
time: {
type: DataTypes.DATE,
allowNull: true,
defaultValue: null,
comment: null,
primaryKey: false,
field: "time",
autoIncrement: false
},
type: {
type: DataTypes.STRING,
allowNull: true,
defaultValue: null,
comment: "数据告警:data,设备告警:device,应用告警:application",
primaryKey: false,
field: "type",
autoIncrement: false
}
}, {
tableName: "alarm_appear_record",
comment: "",
indexes: []
});
dc.models.AlarmAppearRecord = AlarmAppearRecord;
return AlarmAppearRecord;
};

71
api/app/lib/models/alarm_confirm_log.js

@ -0,0 +1,71 @@
/* eslint-disable*/
'use strict';
module.exports = dc => {
const DataTypes = dc.ORM;
const sequelize = dc.orm;
const AlarmConfirmLog = sequelize.define("alarmConfirmLog", {
id: {
type: DataTypes.INTEGER,
allowNull: false,
defaultValue: null,
comment: null,
primaryKey: true,
field: "id",
autoIncrement: true,
unique: "alarm_confirm_log_id_uindex"
},
pepUserId: {
type: DataTypes.INTEGER,
allowNull: true,
defaultValue: null,
comment: null,
primaryKey: false,
field: "pep_user_id",
autoIncrement: false
},
projectCorrelationId: {
type: DataTypes.INTEGER,
allowNull: false,
defaultValue: null,
comment: null,
primaryKey: false,
field: "project_correlation_id",
autoIncrement: false
},
alarmInfo: {
type: DataTypes.JSON,
allowNull: false,
defaultValue: null,
comment: null,
primaryKey: false,
field: "alarm_info",
autoIncrement: false
},
confirmTime: {
type: DataTypes.DATE,
allowNull: false,
defaultValue: null,
comment: null,
primaryKey: false,
field: "confirm_time",
autoIncrement: false
},
confirmContent: {
type: DataTypes.STRING,
allowNull: false,
defaultValue: null,
comment: null,
primaryKey: false,
field: "confirm_content",
autoIncrement: false
}
}, {
tableName: "alarm_confirm_log",
comment: "",
indexes: []
});
dc.models.AlarmConfirmLog = AlarmConfirmLog;
return AlarmConfirmLog;
};

62
api/app/lib/models/email_send_log.js

@ -0,0 +1,62 @@
/* eslint-disable*/
'use strict';
module.exports = dc => {
const DataTypes = dc.ORM;
const sequelize = dc.orm;
const EmailSendLog = sequelize.define("emailSendLog", {
id: {
type: DataTypes.INTEGER,
allowNull: false,
defaultValue: null,
comment: null,
primaryKey: true,
field: "id",
autoIncrement: true,
unique: "email_send_log_id_uindex"
},
projectCorrelationId: {
type: DataTypes.INTEGER,
allowNull: false,
defaultValue: null,
comment: null,
primaryKey: false,
field: "project_correlation_id",
autoIncrement: false
},
toPepUserId: {
type: DataTypes.INTEGER,
allowNull: false,
defaultValue: null,
comment: null,
primaryKey: false,
field: "to_pep_user_id",
autoIncrement: false
},
by: {
type: DataTypes.STRING,
allowNull: true,
defaultValue: null,
comment: null,
primaryKey: false,
field: "by",
autoIncrement: false
},
time: {
type: DataTypes.DATE,
allowNull: false,
defaultValue: null,
comment: null,
primaryKey: false,
field: "time",
autoIncrement: false
}
}, {
tableName: "email_send_log",
comment: "",
indexes: []
});
dc.models.EmailSendLog = EmailSendLog;
return EmailSendLog;
};

80
api/app/lib/models/latest_dynamic_list.js

@ -0,0 +1,80 @@
/* eslint-disable*/
'use strict';
module.exports = dc => {
const DataTypes = dc.ORM;
const sequelize = dc.orm;
const LatestDynamicList = sequelize.define("latestDynamicList", {
id: {
type: DataTypes.INTEGER,
allowNull: false,
defaultValue: null,
comment: null,
primaryKey: true,
field: "id",
autoIncrement: true,
unique: "latest_dynamic_list_id_uindex"
},
time: {
type: DataTypes.DATE,
allowNull: false,
defaultValue: null,
comment: null,
primaryKey: false,
field: "time",
autoIncrement: false
},
alarmAppearId: {
type: DataTypes.INTEGER,
allowNull: true,
defaultValue: null,
comment: null,
primaryKey: false,
field: "alarm_appear_id",
autoIncrement: false
},
emailSendId: {
type: DataTypes.INTEGER,
allowNull: true,
defaultValue: null,
comment: null,
primaryKey: false,
field: "email_send_id",
autoIncrement: false
},
alarmConfirmId: {
type: DataTypes.INTEGER,
allowNull: true,
defaultValue: null,
comment: null,
primaryKey: false,
field: "alarm_confirm_id",
autoIncrement: false
},
projectCorrelationId: {
type: DataTypes.INTEGER,
allowNull: false,
defaultValue: null,
comment: null,
primaryKey: false,
field: "project_correlation_id",
autoIncrement: false
},
type: {
type: DataTypes.INTEGER,
allowNull: true,
defaultValue: null,
comment: '1:发现,2:通知,3:处置,4:确认',
primaryKey: false,
field: "type",
autoIncrement: false
}
}, {
tableName: "latest_dynamic_list",
comment: "",
indexes: []
});
dc.models.LatestDynamicList = LatestDynamicList;
return LatestDynamicList;
};

14
api/app/lib/routes/console/index.js

@ -1,14 +0,0 @@
'use strict';
const toolLink = require('../../controllers/console/toolLink');
module.exports = function (app, router, opts) {
app.fs.api.logAttr['GET/console/toollink'] = { content: '获取常用工具', visible: true };
router.get('/console/toollink', toolLink.list);
app.fs.api.logAttr['PUT/console/toollink'] = { content: '编辑常用工具', visible: true };
router.put('/console/toollink', toolLink.edit);
app.fs.api.logAttr['DEL/console/toollink'] = { content: '删除常用工具', visible: true };
router.del('/console/toollink/:linkId', toolLink.del);
};

36
api/app/lib/routes/control/index.js

@ -0,0 +1,36 @@
'use strict';
const toolLink = require('../../controllers/control/toolLink');
const analysis = require('../../controllers/control/analysis');
const csData = require('../../controllers/control/data');
module.exports = function (app, router, opts) {
app.fs.api.logAttr['GET/console/toollink'] = { content: '获取常用工具', visible: true };
router.get('/console/toollink', toolLink.list);
app.fs.api.logAttr['PUT/console/toollink'] = { content: '编辑常用工具', visible: true };
router.put('/console/toollink', toolLink.edit);
app.fs.api.logAttr['DEL/console/toollink'] = { content: '删除常用工具', visible: true };
router.del('/console/toollink/:linkId', toolLink.del);
app.fs.api.logAttr['GET/console/count'] = { content: '查询告警数量', visible: true };
router.get('/console/count', toolLink.count);
app.fs.api.logAttr['GET/analysis/dataList'] = { content: '查询数据告警产生,确认数量', visible: true };
router.get('/analysis/dataList', analysis.dataList);
//项目概览
app.fs.api.logAttr['GET/projects/info'] = { content: '查询项目概览', visible: false };
router.get('/projects/info', csData.getProjectsInfo);
//BI分析模块
app.fs.api.logAttr['GET/video/alarms/agg/day'] = { content: '查询BI分析数据-视频异常', visible: false };
router.get('/video/alarms/agg/day', csData.getVideoAlarmsAggDay);
//最新动态
app.fs.api.logAttr['GET/latest/dynamic'] = { content: '查询最新动态', visible: false };
router.get('/latest/dynamic', csData.getLatestDynamic);
};

7
api/app/lib/utils/dataRange.js

@ -136,10 +136,17 @@ module.exports = function (app, opts) {
LEFT JOIN
t_structuregroup_structure
ON t_structuregroup_structure.structuregroup = t_project_structuregroup.structuregroup
LEFT JOIN
t_project_construction
ON t_project_construction.project = t_project.id
LEFT JOIN
t_structure_site
ON t_structure_site.siteid = t_project_construction.construction
RIGHT JOIN
t_structure
ON t_structure.id = t_project_structure.structure
OR t_structure.id = t_structuregroup_structure.structure
OR t_structure.id = t_structure_site.structid
WHERE
project_state != -1
AND

1
api/package.json

@ -34,6 +34,7 @@
"pg": "^7.9.0",
"redis": "^3.1.2",
"request": "^2.88.2",
"sequelize-automate-freesun": "^1.2.2",
"superagent": "^3.5.2",
"uuid": "^3.3.2"
},

BIN
web/client/assets/images/problem/shield.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 868 KiB

4
web/client/src/app.jsx

@ -8,7 +8,7 @@ import Analysis from './sections/analysis';
import Install from './sections/install';
import Problem from './sections/problem';
import NoMatch from './sections/noMatch';
import Console from './sections/console';
import Control from './sections/control';
import Facility from './sections/facility';
import Service from './sections/service';
import WorkOrder from './sections/workOrder';
@ -40,7 +40,7 @@ const App = props => {
title={projectName}
sections={[//Example,
Analysis, Install, Data, Facility, Service, Problem, WorkOrder,Means,
Auth, NoMatch, Console
Auth, NoMatch, Control
]}
/>
)

6
web/client/src/layout/components/header/index.jsx

@ -97,12 +97,13 @@ const Header = (props) => {
setScrollbar(!Scrollbar)
setKeyword('')
}}
clickToHide={true}
render={
<Dropdown.Menu style={{ minWidth: 270, maxWidth: 714, padding: 20, fontSize: 12 }}>
<Tabs type="button">
<TabPane tab="项目" itemKey="项目">
<div style={{ width: '100%', height: 1, background: "#d5cfcf8c", margin: "10px 0" }}></div>
<Input suffix={<IconSearch />} onChange={(v) => setKeyword(v)} showClear></Input>
<Input suffix={<IconSearch />} onChange={(v) => setKeyword(v)} showClear onClick={(e)=>e.stopPropagation()}></Input>
<div id='overall' style={{ width: '100%', height: 260, position: "relative", marginTop: 10 }}>
{pomsList?.length > 0 ?
pomsList.filter(u => u.pepProjectName?.includes(keyword))?.map(v => {
@ -114,9 +115,8 @@ const Header = (props) => {
v.pepProjectName?.length > 15 ? <Tooltip content={<div>{v.pepProjectName}</div>}>
<div style={{}} >
<div onClick={() => {
console.log(v.pepProjectId);
setPomsName(v.pepProjectName)
setPepProjectId(v.id)
setPepProjectId(v.pepProjectId)
}}>
{v.pepProjectName?.length > 15 ? `${v.pepProjectName?.substr(0, 15)}` : v.pepProjectName}
</div>

2
web/client/src/sections/auth/containers/login.jsx

@ -20,7 +20,7 @@ const Login = props => {
useEffect(() => {
if (user && user.authorized) {
dispatch(push('/console'));
dispatch(push('/control'));
localStorage.setItem('poms_open_sider', JSON.stringify([]))
localStorage.removeItem('poms_selected_sider')
}

36
web/client/src/sections/console/actions/console.js

@ -1,36 +0,0 @@
'use strict';
import { ApiTable ,basicAction} from '$utils'
export function ConsoleToollink () { //获取常用工具
return dispatch => basicAction({
type: 'get',
dispatch: dispatch,
actionType: 'GET_CONSLE_TOOLLINK',
url: `${ApiTable.consoleToollink}`,
msg: { option: '获取常用工具' },
reducer: { name: '' }
});
}
export function putConsoleToollink (data) { //编辑常用工具
return dispatch => basicAction({
type: 'put',
dispatch: dispatch,
data,
actionType: 'PUT_CONSLE_TOOLLINK',
url: `${ApiTable.consoleToollink}`,
msg: { option: '编辑常用工具' },
reducer: { name: '' }
});
}
export function deleteConsoleToollink (orgId) { //删除常用工具
return dispatch => basicAction({
type: 'delete',
dispatch: dispatch,
actionType: 'DELETE_CONSLE_TOOLLINK',
url: `${ApiTable.consoleToollink.replace('{linkId}', orgId)}`,
msg: { option: '删除常用工具' },
reducer: { name: '' }
});
}

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

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

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

@ -1,6 +0,0 @@
'use strict';
import Console from './console';
import UserCenter from './userCenter';
export { Console, UserCenter };

48
web/client/src/sections/control/actions/control.js

@ -0,0 +1,48 @@
'use strict';
import { ApiTable, basicAction } from '$utils'
export function getConsoleToollink () { //获取常用工具
return dispatch => basicAction({
type: 'get',
dispatch: dispatch,
actionType: 'GET_CONSLE_TOOLLINK',
url: `${ApiTable.consoleToollink}`,
msg: { option: '获取常用工具' },
reducer: { name: '' }
});
}
export function putConsoleToollink (data) { //编辑常用工具
return dispatch => basicAction({
type: 'put',
dispatch: dispatch,
data,
actionType: 'PUT_CONSLE_TOOLLINK',
url: `${ApiTable.consoleToollink}`,
msg: { option: '编辑常用工具' },
reducer: { name: '' }
});
}
export function deleteConsoleToollink (orgId) { //删除常用工具
return dispatch => basicAction({
type: 'delete',
dispatch: dispatch,
actionType: 'DELETE_CONSLE_TOOLLINK',
url: `${ApiTable.deleteConsoleToollink.replace('{linkId}', orgId)}`,
msg: { option: '删除常用工具' },
reducer: { name: '' }
});
}
export function geteteConsoleCount (query) { //工作台数量查询
return dispatch => basicAction({
type: 'get',
dispatch: dispatch,
query,
actionType: 'GET_CONSLE_COUNT',
url: `${ApiTable.geteteConsoleCount}`,
msg: { option: '工作台数量查询' },
reducer: { name: '' }
});
}

8
web/client/src/sections/control/actions/index.js

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

228
web/client/src/sections/console/containers/console.jsx → web/client/src/sections/control/containers/control.jsx

@ -1,6 +1,6 @@
import React, { useEffect, useState } from 'react';
import React, { useEffect, useState, useRef } from 'react';
import { connect } from 'react-redux';
import { Timeline, Card, Button } from '@douyinfe/semi-ui';
import { Timeline, Card, Button, Modal, Form } from '@douyinfe/semi-ui';
import { push } from 'react-router-redux';
import '../style.less'
import PerfectScrollbar from "perfect-scrollbar";
@ -17,8 +17,9 @@ let problemsScrollbar;
let alarmScrollbar;
const Console = (props) => {
const { dispatch, actions, user, loading, socket } = props
const Control = (props) => {
const { dispatch, actions, user, loading, socket ,pepProjectId} = props
const { control } = actions
const stationList = [
'url(/assets/images/console/lan_1.png)',
'url(/assets/images/console/lv_1.png)',
@ -36,12 +37,37 @@ const Console = (props) => {
const [problemsList, setProblemsList] = useState(['', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', ''])//&
const [setup, setSetup] = useState(false); //
const [tableType, setTableType] = useState(''); //localStorage
const [tool, setTool] = useState(false); //
const [alter, setAlter] = useState(false); //
const [compile, setCompile] = useState({}); //
const [toolShow, setToolShow] = useState([]); //
const [tableSetup, setTableSetup] = useState([]); //
const [exhibition, setExhibition] = useState({ workbench: [] }); //
const [workData, setWorkData] = useState({}); //
const FormApi = useRef()
useEffect(() => {
consoleToollink()
//
let data = ['workbench']
data.map(v => {
localStorage.getItem(v) == null
? localStorage.setItem(v, JSON.stringify(show[v]))
: "";
attribute(v)
})
}, [])
useEffect(() => {
dispatch(control.geteteConsoleCount({pepProjectId:pepProjectId})).then(res => {
if (res.success) setWorkData(res.payload.data)
})
}, [pepProjectId])
useEffect(() => {
newScrollbar = new PerfectScrollbar("#news", {
suppressScrollX: true,
@ -97,6 +123,13 @@ const Console = (props) => {
// dispatch(actions.example.getMembers(user.orgId))
})
const consoleToollink = () => {
dispatch(control.getConsoleToollink()).then(res => {
if (res.success) setToolShow(res.payload.data)
})
}
let Select = {
workbench: ['project', 'data', 'app', 'device'],
statistical: [],
@ -111,40 +144,36 @@ const Console = (props) => {
}
let listAll = [
{ name: '关注的项目', sort: 1, key: 'project', data: [], img: 'url(/assets/images/console/lan_1.png)' },
{ name: '数据告警', sort: 2, key: 'data', data: [], img: 'url(/assets/images/console/lv_1.png)' },
{ name: '应用告警', sort: 2, key: 'app', data: [], img: 'url(/assets/images/console/hong_1.png)' },
{ name: '设备告警', sort: 2, key: 'device', data: [], img: 'url(/assets/images/console/hong_1.png)' },
{ name: '关注的项目', sort: 1, key: 'project', data: workData?.projects, img: 'url(/assets/images/console/lan_1.png)' },
{ name: '数据告警', sort: 2, key: 'data', data: workData?.dataSurplus, img: 'url(/assets/images/console/lv_1.png)' },
{ name: '应用告警', sort: 2, key: 'app', data: workData?.appSurplus, img: 'url(/assets/images/console/hong_1.png)' },
{ name: '设备告警', sort: 2, key: 'device', data: workData?.toolSurplus, img: 'url(/assets/images/console/hong_1.png)' },
]
console.log(workData);
console.log(listAll);
useEffect(() => {
attribute('workbench')
}, [workData])
const attribute = (title) => {
let take = localStorage.getItem(title)
? JSON.parse(localStorage.getItem(title))
: [];
let data = Select[title].map(v => {
let dataTitle = listAll.find(vv => v == vv.key) || {}
return { name: dataTitle.name, value: dataTitle.key }
})
let TableDisplay = take?.map(v => listAll?.find(vv => v == vv.key))
TableDisplay.sort((a, b) => a.sort - b.sort)
setExhibition({ ...exhibition, [title]: TableDisplay })
setTableSetup([{ list: data }])
// const attribute = (name, route) => {
// let arr = localStorage.getItem(name)
// ? JSON.parse(localStorage.getItem(name))
// : [];
// // console.log(arr);
// if (route) {
// let setup = tableList[route].map(v => columnAll.find(vv => v == vv.value))
// let data = []
// data.splice(1, 0, ...arr, 'text')
// let TableDisplay = data?.map(v => {
// let datas = columnAll?.find(vv => v == vv.value)
// if (datas) {
// return { title: datas.name, sort: datas.sort, dataIndex: datas.value, rowKey: datas.value, render: datas.render }
// }
// })
// TableDisplay.sort((a, b) => a.sort - b.sort)
// setExhibition(TableDisplay)
// setTableSetup([{ list: setup }])
// }
// }
}
return (
// 11 ? <img src="/assets/images/install/watting.png" alt="" style={{ width: 'calc(100% + 16px)', position: "relative", top: -12, left: -8, }} /> :
<>
<div style={{ padding: '0px 40px', width: '100%' }} className='console'>
{/* 头部 */}
@ -188,7 +217,7 @@ const Console = (props) => {
剩余问题:
</div>
<div style={{ fontSize: 16, color: '#4A4A4A', width: 91 }}>
122
{(workData?.appSurplus + workData?.dataSurplus + workData?.toolSurplus) || 0}
</div>
</div>
<div style={{ display: 'flex', alignItems: 'center', marginTop: 26 }}>
@ -197,7 +226,7 @@ const Console = (props) => {
</div>
<div style={{ fontSize: 16, color: '#4A4A4A', width: 91, display: 'flex' }}>
<div>
12223
{(workData?.appNewAdd + workData?.dataNewAdd + workData?.toolNewAdd) || 0}
</div>
<div style={{ display: 'flex', alignItems: 'center', marginLeft: 4 }}>
<img title='设置' src="/assets/images/console/icon_up.png" style={{ width: 18, height: 18 }} />
@ -209,23 +238,23 @@ const Console = (props) => {
今日处理:
</div>
<div style={{ fontSize: 16, color: '#4A4A4A', width: 91 }}>
3
{(workData?.appConfirme + workData?.dataConfirme + workData?.toolConfirme) || 0}
</div>
</div>
</div>
{/* 循环类型 */}
<div id='alarm' style={{ width: 'calc(100% - 200px)', position: 'relative', whiteSpace: 'nowrap', }}>
{stationList.map((item, index) => {
{exhibition['workbench']?.map((item, index) => {
return (
<div key={item} style={{ background: item, backgroundSize: "100% 100%", display: "inline-block", width: 270, height: 135, marginRight: 26 }}>
<div key={item.name} style={{ background: item.img, backgroundSize: "100% 100%", display: "inline-block", width: 270, height: 135, marginRight: 26 }}>
<div style={{ margin: '35px 0px 0px 134px' }}>
<div>
<span style={{ fontSize: 14, color: '#4A4A4A' }}>关注的项目</span>
<span style={{ fontSize: 12, color: '#4A4A4A' }}>()</span>
<span style={{ fontSize: 14, color: '#4A4A4A', fontWeight: 500, fontFamily: 'PingFangSC-Medium, PingFang SC' }}>{item.name}</span>
<span style={{ fontSize: 12, color: '#4A4A4A' }}>{item.name == '关注的项目' ? ' ( 个 )' : ' ( 条 )'}</span>
</div>
<div style={{ marginTop: 15, display: 'flex', alignItems: 'center' }}>
<div style={{ fontSize: 32, color: index == 0 ? '#0F7EFB' : index == 1 ? '#0091A1' : index == 2 ? '#FE9812' : '#FF7575', fontFamily: 'YouSheBiaoTiHei' }}>112</div>
<div style={{ fontSize: 12, color: '#969799', marginLeft: 4 }}>进行中</div>
<div style={{ fontSize: 32, color: index == 0 ? '#0F7EFB' : index == 1 ? '#0091A1' : index == 2 ? '#FE9812' : '#FF7575', fontFamily: 'YouSheBiaoTiHei' }}>{item.data}</div>
{item.name == '关注的项目' ? '' : <div style={{ fontSize: 12, color: '#969799', marginLeft: 4 }}>待处理</div>}
</div>
</div>
</div>
@ -461,7 +490,7 @@ const Console = (props) => {
</div>
</div>
{/* 我常用的工具 */}
<div style={{ marginTop: 20, background: '#FFFFFF', boxShadow: '0px 0px 12px 2px rgba(220,222,224,0.2)', borderRadius: 2, paddingTop: 20, paddingLeft: 21, height: 303 }}>
<div style={{ marginTop: 20, background: '#FFFFFF', boxShadow: '0px 0px 12px 2px rgba(220,222,224,0.2)', borderRadius: 2, padding: 20 }}>
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
<div style={{ display: 'flex', alignItems: 'center' }}>
<div style={{ width: 0, height: 20, borderLeft: '3px solid #005ABD', borderTop: '3px solid transparent', borderBottom: '3px solid transparent' }}></div>
@ -469,66 +498,140 @@ const Console = (props) => {
<div style={{ marginLeft: 6, fontSize: 12, color: '#969799', fontFamily: "DINExp", }}>MY USUAL TOOLS</div>
</div>
</div>
{toolShow.length > 0 ?
toolShow?.map(v =>
<div
onMouseLeave={() => {
console.log(111111);
document.getElementById('aa').style.display = 'none'
document.getElementById(v.id + 'name').style.display = 'none'
}}
style={{ marginTop: 24, position: 'relative', display: "inline-block" }}>
id={v.id + v.name}
style={{ marginTop: 24, position: 'relative', display: "inline-block", cursor: 'pointer' }}>
<a href={v.link} target="_blank">
<Button
theme="solid"
type="primary"
style={{
width: 94, height: 29, borderRadius: 2, marginRight: 10, marginBottom: 15,
height: 29, borderRadius: 2, marginRight: 10, marginBottom: 15,
background: '#0F7EFB', color: '#FFFFFF', border: '1px solid #0F7EFB',
}}
onContextMenu={(e) => {
e.preventDefault();
document.getElementById('aa').style.display = 'block'
}}
onClick={(e) => {
// console.log(e);
document.getElementById(v.id + 'name').style.display = 'block'
}}
>
工单管理
{v.name}
</Button>
<div id='aa'
</a>
<div id={v.id + 'name'}
style={{
position: 'absolute', top: 12, right: -10, background: 'rgb(208 208 223 / 100%)', fontSize: 13,
width: 50, textAlign: "center", borderRadius: 4, padding: '0 10px', display: "none"
}}>
<div style={{ lineHeight: '30px' }}
onClick={() => {
setTool(true)
setAlter(true)
setCompile({ id: v.id, name: v.name, link: v.link, })
}}
>编辑</div>
<div style={{ lineHeight: '30px' }}
onClick={() => {
dispatch(control.deleteConsoleToollink(v.id)).then(res => {
if (res.success) consoleToollink()
})
}}
>删除</div>
</div>
</div>
</div>) : ""}
<Button
theme="solid"
type="primary"
style={{
width: '100%',
height: 40,
borderRadius: 2,
marginTop: 20,
background: '#F2F3F5',
color: '#0F7EFB',
border: '1px solid #F2F3F5'
}}
onClick={() => {
setTool(true)
}}
>
添加
</Button>
</div>
</div>75
</div>
</div>
{setup ? (
</div>
{/* 页面各个设置弹窗 */}
{
setup ? (
<Setup
tableType={tableType}
tableList={tableSetup}
close={() => {
setSetup(false);
attribute(tableType[route], route);
attribute(tableType);
setTableType('')
}}
/>
) : (
""
)}
)
}
{/* 工具添加修改弹窗 */}
{tool ? <Modal
title={alter ? '编辑' : "添加"}
visible={true}
width={600}
onCancel={() => {
setTool(false)
setAlter(false)
setCompile({})
}}
onOk={() => {
FormApi.current.validate().then((v) => {
console.log(v);
dispatch(control.putConsoleToollink({ id: compile?.id, name: v.name, link: v.link })).then(res => {
if (res.success) {
setTool(false)
setAlter(false)
setCompile({})
consoleToollink()
}
})
})
}}
>
<div style={{ paddingLeft: 20 }}>
<Form
onSubmit={(values) => console.log(values)}
getFormApi={(formApi) => (FormApi.current = formApi)}
>
<Form.Input
label='工具名称'
labelPosition="left"
rules={[{ required: true, message: "请输入工具名称,1~10个字符,支持中英文,数字及常见符号-_ /." }]}
field='name'
initValue={compile?.name || ''}
placeholder='请输入工具名称'
/>
<Form.TextArea maxCount={50} showClear
label='地址链接'
labelPosition="left"
placeholder='请输入URL'
initValue={compile?.link || ''}
rules={[{ required: true, message: "请输入地址链接" }]}
field='link'
/>
</Form>
</div>
</Modal> : ""
}
</>
)
}
@ -538,10 +641,11 @@ function mapStateToProps (state) {
return {
// loading: members.isRequesting,
user: auth.user,
// actions: global.actions,
actions: global.actions,
pepProjectId: global.pepProjectId,
// members: members.data,
// socket: webSocket.socket
};
}
export default connect(mapStateToProps)(Console);
export default connect(mapStateToProps)(Control);

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

@ -0,0 +1,6 @@
'use strict';
import Control from './control';
import UserCenter from './userCenter';
export { Control, UserCenter };

0
web/client/src/sections/console/containers/userCenter.jsx → web/client/src/sections/control/containers/userCenter.jsx

2
web/client/src/sections/console/index.js → web/client/src/sections/control/index.js

@ -6,7 +6,7 @@ import actions from './actions';
import { getNavItem } from './nav-item';
export default {
key: 'console',
key: 'control',
name: '控制台',
reducers: reducers,
routes: routes,

4
web/client/src/sections/console/nav-item.jsx → web/client/src/sections/control/nav-item.jsx

@ -5,9 +5,9 @@ export function getNavItem (user, dispatch) {
return (
[
{
itemKey: 'console',
itemKey: 'control',
text: '控制台',
to: '/console',
to: '/control',
// icon: <IconCode />,
},
]

0
web/client/src/sections/console/reducers/index.js → web/client/src/sections/control/reducers/index.js

8
web/client/src/sections/console/routes.js → web/client/src/sections/control/routes.js

@ -1,13 +1,13 @@
'use strict';
import { Console,UserCenter } from './containers';
import { Control,UserCenter } from './containers';
export default [{
type: 'inner',
route: {
path: '/console',
key: 'console',
path: '/control',
key: 'control',
breadcrumb: '控制台',
component: Console,
component: Control,
// 不设置 component 则面包屑禁止跳转
}
}, {

0
web/client/src/sections/console/style.less → web/client/src/sections/control/style.less

15
web/client/src/sections/problem/components/tableData.jsx

@ -26,8 +26,8 @@ const TableData = ({ route, dispatch, actions, collectData, setSetup, exhibition
let typeData = { element: "元素异常", apiError: "接口报错 ", timeout: "加载超时" }
let tableDatas = res.payload.data?.rows.map(v => ({
key: v.id,
projectName: v.app?.projectCorrelations?.map(r => (r.name ? { name: r.name, state: 'PMOS' } : {
name: r.pepProject?.projectName, state: r.pepProject?.constructionStatus
projectName: v.app?.projectCorrelations?.map(r => (r.name ? { id: r.id, name: r.name, state: 'PMOS' } : {
id: r.id, name: r.pepProject?.projectName, state: r.pepProject?.constructionStatus
}))?.filter(c => c),
appName: v.app?.name,
url: v.app?.url,
@ -59,8 +59,8 @@ const TableData = ({ route, dispatch, actions, collectData, setSetup, exhibition
let tableDatas = res.payload.data?.map(v => ({
key: v.alarmId,
StructureName: v.struc,
projectName: v.pomsProject?.map(r => (r.name ? { name: r.name, state: 'PMOS' } : {
name: r.pepProject?.projectName, state: r.pepProject?.constructionStatus
projectName: v.pomsProject?.map(r => (r.name ? { id: r.id, name: r.name, state: 'PMOS' } : {
id: r.id, name: r.pepProject?.projectName, state: r.pepProject?.constructionStatus
}))?.filter(c => c),
createTime: v.createTime ? moment(v.createTime).format("YYYY-MM-DD HH:mm:ss") : "",
updateTime: v.updateTime ? moment(v.updateTime).format("YYYY-MM-DD HH:mm:ss") : "",
@ -107,8 +107,8 @@ const TableData = ({ route, dispatch, actions, collectData, setSetup, exhibition
let tableDatas = res.payload.data?.rows?.map(v => ({
key: v.AlarmId,
StructureName: v.StructureName,
projectName: v.pomsProject?.map(r => (r.name ? { name: r.name, state: 'PMOS' } : {
name: r.pepProject?.projectName, state: r.pepProject?.constructionStatus
projectName: v.pomsProject?.map(r => (r.name ? { id: r.id, name: r.name, state: 'PMOS' } : {
id: r.id, name: r.pepProject?.projectName, state: r.pepProject?.constructionStatus
}))?.filter(c => c),
createTime: v.StartTime ? moment(v.StartTime).format("YYYY-MM-DD HH:mm:ss") : "",
updateTime: v.EndTime ? moment(v.EndTime).format("YYYY-MM-DD HH:mm:ss") : "",
@ -187,6 +187,7 @@ const TableData = ({ route, dispatch, actions, collectData, setSetup, exhibition
labelPosition="left"
field={v.field}
key={v.field}
maxLength="10"
style={{ width: 116, marginRight: 16, color: "#F9F9F9", }}
placeholder="全部"
filter
@ -263,7 +264,7 @@ const TableData = ({ route, dispatch, actions, collectData, setSetup, exhibition
columns={exhibition}
dataSource={route == 'useAbnormal' || route == 'videoAbnormal' ? tableData.slice(query.page * query.limit, (query.page + 1) * query.limit) || [] : tableData}
bordered={false}
empty="暂无数据"
empty={<div><img src="/assets/images/problem/shield.png" style={{ width: 20 }} />暂无告警数据</div>}
style={{}}
pagination={false}
onRow={(record, index) => {

38
web/client/src/sections/problem/containers/dataAlarm.jsx

@ -37,7 +37,7 @@ const DataAlarm = ({ match, dispatch, actions, user, loading, socket, iotVcmpWeb
const [videoModal, setVideoModal] = useState(false) //
const [videoData, setVideoData] = useState({}) //
const [videoToken, setVideoToken] = useState() //token
const [alarmToConfirm, setAlarmToConfirm] = useState(null) //
const TextAreaApi = useRef('')
@ -136,10 +136,10 @@ const DataAlarm = ({ match, dispatch, actions, user, loading, socket, iotVcmpWeb
}],
deviceAbnormal: [ // (deviceAbnormal)
{ name: '搜索', field: '1' },
{
name: '设备类型', field: 'groupUnitId',
data: genre
},
// {
// name: '', field: 'groupUnitId',
// data: genre
// },
{
name: '异常状态', field: 'state',
data: [
@ -163,9 +163,9 @@ const DataAlarm = ({ match, dispatch, actions, user, loading, socket, iotVcmpWeb
dataLnterrupt: ['index', 'projectName', 'StructureName', 'SourceName', 'AlarmGroupUnit', 'AlarmCodeName', 'sustainTime', 'createTime', 'AlarmContent', 'CurrentLevel', 'updateTime', 'detailCount', 'confirm', 'confirmTime',],
dataAbnormal: ['index', 'projectName', 'StructureName', 'SourceName', 'type', 'createTime', 'sustainTime', 'AlarmContent', 'CurrentLevel', 'updateTime', 'detailCount', 'confirm', 'confirmTime'],
strategyHit: ['index', 'projectName', 'StructureName', 'SourceName', 'Strategy', 'State', 'createTime', 'sustainTime', 'AlarmContent', 'CurrentLevel', 'updateTime', 'detailCount', 'confirm', 'confirmTime'],
videoAbnormal: ['index', 'projectName', 'StructureName', 'SourceName', 'station', 'cameraKindId', 'sustainTime', 'venderName', 'point', 'cameraSerialNo', 'cameraChannelNo', 'platform', 'AlarmContent', 'resolve', 'createTime', 'updateTime', 'confirm', 'confirmTime', 'camerOnline'],
videoAbnormal: ['index', 'projectName', 'StructureName', 'SourceName', 'station', 'cameraKindId', 'sustainTime', 'venderName', 'point', 'cameraSerialNo', 'cameraChannelNo', 'platform', 'AlarmContent', 'resolve', 'createTime', 'updateTime', 'confirm', 'confirmTime',],
useAbnormal: ['index', 'projectName', 'appName', 'url', 'type', 'alarmContent', 'createTime', 'sustainTime', 'updateTime', 'confirm', 'confirmTime'],
deviceAbnormal: ['index', 'projectName', 'StructureName', 'SourceName', 'station', 'type', 'cameraKindId', 'sustainTime', 'venderName', 'AlarmContent', 'AlarmCodeName', 'createTime', 'updateTime', 'confirm', 'confirmTime'],
deviceAbnormal: ['index', 'projectName', 'StructureName', 'SourceName', 'station', 'sustainTime', 'venderName', 'AlarmContent', 'AlarmCodeName', 'createTime', 'updateTime', 'confirm', 'confirmTime'],
}
//
const columns = {
@ -309,6 +309,7 @@ const DataAlarm = ({ match, dispatch, actions, user, loading, socket, iotVcmpWeb
<Button theme='borderless' style={{ width: 65 }} onClick={() => {
setConfirm(true)
setSelected([r.key])
setAlarmToConfirm(r)
}}>确认</Button>
: r.State == 3 || r.autoRestore || r.confirmAuto ?
<Button theme='borderless' style={{ width: 65 }} disabled>自动恢复</Button> :
@ -383,7 +384,19 @@ const DataAlarm = ({ match, dispatch, actions, user, loading, socket, iotVcmpWeb
// console.log(selected);
const getAlarmConfirmItem = () => {
let source = route == 'useAbnormal' ? alarmToConfirm.appName : alarmToConfirm.SourceName;
let type = route == 'useAbnormal' ? alarmToConfirm.type : route == 'videoAbnormal' ? alarmToConfirm.AlarmContent : alarmToConfirm.AlarmGroupUnit;
return {
pepUserId: user.pomsUserInfo.pepUserId,
projectCorrelationIds: alarmToConfirm?.projectName?.map(p => p.id),
alarmInfo: {
id: alarmToConfirm.key,
source: source,//
type: type,//
}
};
}
return (
// route=='dataLnterrupt'|| route=='dataAbnormal'|| route=='strategyHit'?
@ -444,10 +457,10 @@ const DataAlarm = ({ match, dispatch, actions, user, loading, socket, iotVcmpWeb
width={600}
onCancel={() => setConfirm(false)}
onOk={() => {
let confirmPost = getAlarmConfirmItem();
if (route == 'useAbnormal') {
TextAreaApi.current.validate().then((v) => {
dispatch(problem.postApiConfirm({ appAlarmId: selected, confirm: content })).then(res => {
dispatch(problem.postApiConfirm({ appAlarmId: selected, confirm: content, confirmPost })).then(res => {
if (res.success) {
setConfirm(false)
setSelected([])
@ -457,7 +470,7 @@ const DataAlarm = ({ match, dispatch, actions, user, loading, socket, iotVcmpWeb
})
} else if (route == 'videoAbnormal') {
TextAreaApi.current.validate().then((v) => {
dispatch(problem.putAlarmVideoConfirm({ alarmId: selected, content: content })).then(res => {
dispatch(problem.putAlarmVideoConfirm({ alarmId: selected, content: content, confirmPost })).then(res => {
if (res.success) {
setConfirm(false)
setSelected([])
@ -476,7 +489,7 @@ const DataAlarm = ({ match, dispatch, actions, user, loading, socket, iotVcmpWeb
})
} else {
TextAreaApi.current.validate().then((v) => {
dispatch(problem.putAlarmdataConfirm({ alarmId: selected, content: content })).then(res => {
dispatch(problem.putAlarmdataConfirm({ alarmId: selected, content: content, confirmPost })).then(res => {
if (res.success) {
setConfirm(false)
let data = tableData?.map(v => {
@ -524,6 +537,7 @@ const DataAlarm = ({ match, dispatch, actions, user, loading, socket, iotVcmpWeb
</div>
</Modal> : ""}
{/* 视频的播放 */}
{videoModal ? <Modal
visible={true}
header={null}

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

@ -53,6 +53,8 @@ export const ApiTable = {
//控制台
consoleToollink: 'console/toollink', //常用工具
deleteConsoleToollink: 'console/toollink/{linkId}', //删除常用工具
geteteConsoleCount: 'console/count', //工作台数量查询
};

Loading…
Cancel
Save