Browse Source

Merge branch 'dev_trial' of https://gitea.anxinyun.cn/free-sun/FS-IOT into dev_trial

release_1.1.2
wenlele 3 years ago
parent
commit
634e10e64b
  1. 63
      code/VideoAccess-VCMP/api/app/lib/controllers/status/push.js
  2. 17
      code/VideoAccess-VCMP/api/app/lib/index.js
  3. 52
      code/VideoAccess-VCMP/api/app/lib/models/camera_status_offline_log.js
  4. 170
      code/VideoAccess-VCMP/api/app/lib/schedule/cameraStatePush.js
  5. 29
      code/VideoAccess-VCMP/api/app/lib/schedule/freshYingshiMsg.js
  6. 4
      code/VideoAccess-VCMP/api/app/lib/schedule/index.js
  7. 6
      code/VideoAccess-VCMP/api/app/lib/utils/push.js
  8. 2
      code/VideoAccess-VCMP/api/sequelize-automate.config.js
  9. BIN
      code/VideoAccess-VCMP/web/client/assets/images/background/icon_camera.png
  10. BIN
      code/VideoAccess-VCMP/web/client/assets/images/background/icon_email.png
  11. BIN
      code/VideoAccess-VCMP/web/client/assets/images/background/icon_phone.png
  12. 42
      code/VideoAccess-VCMP/web/client/src/sections/equipmentWarehouse/components/fluoriteCamera.jsx
  13. 43
      code/VideoAccess-VCMP/web/client/src/sections/equipmentWarehouse/components/ipcCamera.jsx
  14. 21
      code/VideoAccess-VCMP/web/client/src/sections/equipmentWarehouse/components/nvrCamera.jsx
  15. 102
      code/VideoAccess-VCMP/web/client/src/sections/offline/components/pushSideSheet.jsx
  16. 44
      code/VideoAccess-VCMP/web/client/src/sections/offline/components/pushSideSheet.less
  17. 17
      code/VideoAccess-VCMP/web/client/src/sections/offline/containers/carrierpigeon.jsx
  18. 2
      code/VideoAccess-VCMP/web/client/src/utils/webapi.js

63
code/VideoAccess-VCMP/api/app/lib/controllers/status/push.js

@ -11,20 +11,55 @@ async function edit (ctx) {
receiver, cameraId receiver, cameraId
} = ctx.request.body; } = ctx.request.body;
let configId_ = configId let configId_ = configId
let receiverSorted = receiver.sort()
let cameraIdSorted = cameraId.sort()
let noticeWaySorted = noticeWay.sort()
let editDataExist = false
// 判重 // 判重
const configAllRes = await models.CameraStatusPushConfig.findAll({
include: [{
model: models.CameraStatusPushMonitor,
attributes: ['cameraId'],
required: false,
duplicating: true,
}, {
model: models.CameraStatusPushReceiver,
attributes: ['receiver'],
duplicating: false,
required: false,
},]
})
for (let c of configAllRes) {
if (configId_ && c.id == configId_) {
editDataExist = true
continue
} else if (c.name == name) {
throw '已有同名称配置信息'
}
const cReceiver = c.cameraStatusPushReceivers.map(r => r.receiver)
const cCameraId = c.cameraStatusPushMonitors.map(m => m.cameraId)
const cReceiverSorted = cReceiver.sort()
const cCameraIdSorted = cCameraId.sort()
if (
receiverSorted.join(',') == cReceiverSorted.join(',') && cameraIdSorted.join(',') == cCameraIdSorted.join(',') &&
c.pushWay == pushWay &&
c.noticeWay.sort().join(',') == noticeWaySorted.join(',') &&
(
(!c.timing && !timing) ||
c.timing == timing
)
) {
throw '已有相同配置信息'
}
}
let config = { let config = {
name, pushWay, noticeWay, timing name, pushWay, noticeWay, timing
} }
if (configId) { if (configId) {
const configRes = await models.CameraStatusPushConfig.findOne({ if (!editDataExist) {
where: {
id: configId
}
})
if (!configRes) {
throw '参数错误' throw '参数错误'
} else { } else {
// DO UPDATE // DO UPDATE
@ -89,7 +124,7 @@ async function get (ctx) {
try { try {
const models = ctx.fs.dc.models; const models = ctx.fs.dc.models;
const { userId, token } = ctx.fs.api const { userId, token } = ctx.fs.api
const { limit, page, orderBy, orderDirection, keyword, pushWay } = ctx.query const { limit, page, orderBy, orderDirection, name, pushWay } = ctx.query
const sequelize = ctx.fs.dc.ORM; const sequelize = ctx.fs.dc.ORM;
let findOption = { let findOption = {
@ -104,13 +139,16 @@ async function get (ctx) {
if (page && limit) { if (page && limit) {
findOption.offset = page * limit findOption.offset = page * limit
} }
if (keyword) { if (name) {
findOption.where['$or'] = { findOption.where['$or'] = {
name: { name: {
$like: `%${keyword}%` $like: `%${name}%`
}, },
} }
} }
if (pushWay) {
findOption.where.pushWay = pushWay
}
const configLimitRes = await models.CameraStatusPushConfig.findAll(findOption) const configLimitRes = await models.CameraStatusPushConfig.findAll(findOption)
@ -131,6 +169,7 @@ async function get (ctx) {
'cameraStatusPushMonitors.id', 'cameraStatusPushMonitors.id',
'cameraStatusPushLogs.id', 'cameraStatusPushLogs.id',
'cameraStatusPushReceivers.id', 'cameraStatusPushReceivers.id',
'cameraStatusPushMonitors->camera.id',
] ]
findOption.order = [ findOption.order = [
[orderBy || 'id', orderDirection || 'DESC'] [orderBy || 'id', orderDirection || 'DESC']
@ -141,6 +180,10 @@ async function get (ctx) {
attributes: ['cameraId'], attributes: ['cameraId'],
required: false, required: false,
duplicating: true, duplicating: true,
include: [{
model: models.Camera,
attributes: ['name'],
}]
}, },
{ {
model: models.CameraStatusPushLog, model: models.CameraStatusPushLog,

17
code/VideoAccess-VCMP/api/app/lib/index.js

@ -46,15 +46,6 @@ module.exports.entry = function (app, router, opts) {
module.exports.models = function (dc) { // dc = { orm: Sequelize对象, ORM: Sequelize, models: {} } module.exports.models = function (dc) { // dc = { orm: Sequelize对象, ORM: Sequelize, models: {} }
// 加载定义模型 历史写法 // 加载定义模型 历史写法
// require('./models/nvr')(dc); // require('./models/nvr')(dc);
// require('./models/camera_ability')(dc);
// require('./models/camera_kind')(dc);
// require('./models/camera')(dc);
// require('./models/camera_ability_bind')(dc);
// require('./models/vender')(dc);
// require('./models/secret_yingshi')(dc);
// require('./models/gb_camera')(dc);
// require('./models/ax_project')(dc);
// require('./models/camera_remark')(dc);
// 模型关系摘出来 初始化之后再定义关系才行 // 模型关系摘出来 初始化之后再定义关系才行
fs.readdirSync(path.join(__dirname, '/models')).forEach((filename) => { fs.readdirSync(path.join(__dirname, '/models')).forEach((filename) => {
@ -64,7 +55,7 @@ module.exports.models = function (dc) { // dc = { orm: Sequelize对象, ORM: Seq
const { const {
Nvr, Camera, CameraAbility, CameraAbilityBind, CameraKind, CameraRemark, Nvr, Camera, CameraAbility, CameraAbilityBind, CameraKind, CameraRemark,
GbCamera, SecretYingshi, Vender, CameraStatus, CameraStatusResolve, CameraStatusLog, GbCamera, SecretYingshi, Vender, CameraStatus, CameraStatusResolve, CameraStatusLog,
CameraStatusPushConfig, CameraStatusPushMonitor, CameraStatusPushLog, CameraStatusPushReceiver, CameraStatusPushConfig, CameraStatusPushMonitor, CameraStatusPushLog, CameraStatusPushReceiver, CameraStatusOfflineLog
} = dc.models; } = dc.models;
// Nvr.belongsTo(User, { foreignKey: 'userId', targetKey: 'id' }); // Nvr.belongsTo(User, { foreignKey: 'userId', targetKey: 'id' });
@ -105,9 +96,15 @@ module.exports.models = function (dc) { // dc = { orm: Sequelize对象, ORM: Seq
CameraStatusPushMonitor.belongsTo(CameraStatusPushConfig, { foreignKey: 'configId', targetKey: 'id' }); CameraStatusPushMonitor.belongsTo(CameraStatusPushConfig, { foreignKey: 'configId', targetKey: 'id' });
CameraStatusPushConfig.hasMany(CameraStatusPushMonitor, { foreignKey: 'configId', sourceKey: 'id' }); CameraStatusPushConfig.hasMany(CameraStatusPushMonitor, { foreignKey: 'configId', sourceKey: 'id' });
CameraStatusPushMonitor.belongsTo(Camera, { foreignKey: 'cameraId', targetKey: 'id' });
Camera.hasMany(CameraStatusPushMonitor, { foreignKey: 'cameraId', sourceKey: 'id' });
CameraStatusPushLog.belongsTo(CameraStatusPushConfig, { foreignKey: 'pushConfigId', targetKey: 'id' }); CameraStatusPushLog.belongsTo(CameraStatusPushConfig, { foreignKey: 'pushConfigId', targetKey: 'id' });
CameraStatusPushConfig.hasMany(CameraStatusPushLog, { foreignKey: 'pushConfigId', sourceKey: 'id' }); CameraStatusPushConfig.hasMany(CameraStatusPushLog, { foreignKey: 'pushConfigId', sourceKey: 'id' });
CameraStatusPushReceiver.belongsTo(CameraStatusPushConfig, { foreignKey: 'configId', targetKey: 'id' }); CameraStatusPushReceiver.belongsTo(CameraStatusPushConfig, { foreignKey: 'configId', targetKey: 'id' });
CameraStatusPushConfig.hasMany(CameraStatusPushReceiver, { foreignKey: 'configId', sourceKey: 'id' }); CameraStatusPushConfig.hasMany(CameraStatusPushReceiver, { foreignKey: 'configId', sourceKey: 'id' });
CameraStatusOfflineLog.belongsTo(Camera, { foreignKey: 'cameraId', targetKey: 'id' });
Camera.hasMany(CameraStatusOfflineLog, { foreignKey: 'cameraId', sourceKey: 'id' });
}; };

52
code/VideoAccess-VCMP/api/app/lib/models/camera_status_offline_log.js

@ -0,0 +1,52 @@
/* eslint-disable*/
'use strict';
module.exports = dc => {
const DataTypes = dc.ORM;
const sequelize = dc.orm;
const CameraStatusOfflineLog = sequelize.define("cameraStatusOfflineLog", {
id: {
type: DataTypes.INTEGER,
allowNull: false,
defaultValue: null,
comment: null,
primaryKey: true,
field: "id",
autoIncrement: true,
unique: "camera_status_offline_log_id_uindex"
},
cameraId: {
type: DataTypes.INTEGER,
allowNull: false,
defaultValue: null,
comment: null,
primaryKey: false,
field: "camera_id",
autoIncrement: false
},
status: {
type: DataTypes.STRING,
allowNull: false,
defaultValue: null,
comment: null,
primaryKey: false,
field: "status",
autoIncrement: false
},
time: {
type: DataTypes.DATE,
allowNull: false,
defaultValue: null,
comment: null,
primaryKey: false,
field: "time",
autoIncrement: false
}
}, {
tableName: "camera_status_offline_log",
comment: "",
indexes: []
});
dc.models.CameraStatusOfflineLog = CameraStatusOfflineLog;
return CameraStatusOfflineLog;
};

170
code/VideoAccess-VCMP/api/app/lib/schedule/cameraStatePush.js

@ -3,16 +3,178 @@ const moment = require('moment')
module.exports = function (app, opts) { module.exports = function (app, opts) {
const cameraOnlinePush = app.fs.scheduleInit( const cameraOnlinePush = app.fs.scheduleInit(
{ {
// interval: '* * 4 * * *', interval: '* */15 * * * *',
interval: '*/15 * * * *', // interval: '*/15 * * * * *',
immediate: false, immediate: false,
proRun: false,
}, },
async () => { async () => {
try { try {
const { models } = app.fs.dc
const { pushBySms, pushByEmail } = app.fs.utils
const configRes = await models.CameraStatusPushConfig.findAll({
where: {
forbidden: false
},
include: [
{
model: models.CameraStatusPushMonitor,
attributes: ['cameraId'],
required: false,
duplicating: true
},
{
model: models.CameraStatusPushReceiver,
attributes: ['receiver'],
duplicating: false,
required: false,
},
],
})
const timeNow = moment().format()
for (let c of configRes) {
// 查配置信息所对应的摄像头15min内的在离线状态
const cameraIds = c.cameraStatusPushMonitors.map(m => m.cameraId)
const offlineStatusRes = await models.CameraStatusOfflineLog.findAll({
where: {
cameraId: { $in: cameraIds },
time: {
$between: [moment(timeNow).subtract(15, 'minutes').format(), timeNow]
}
},
include: [{
model: models.Camera,
attributes: ['name']
}],
order: [['time', 'ASC']],
})
if (offlineStatusRes.length) {
const cameraStatusMap = {}
// 当前逻辑
// 只要最后状态是离线 就做离线推送
// 只要出现一次上线 就做上线推送
for (let s of offlineStatusRes) {
if (cameraStatusMap[s.cameraId]) {
cameraStatusMap.status.push({
cameraId: s.cameraId,
status: s.status,
time: s.time,
})
} else {
cameraStatusMap[s.cameraId] = {
status: [{
cameraId: s.cameraId,
status: s.status,
time: s.time,
}],
name: s.camera.name,
}
}
}
let offArr = []
let onArr = []
for (let k in Object.keys(cameraStatusMap).sort()) {
const data = cameraStatusMap[k]
if (data.status[0].status == 'OFF') {
offArr.push({
name: data.name,
time: data.status[0].time,
})
}
const onLineIndex = data.status.findIndex(s => s.status == 'ON')
if (onLineIndex >= 0) {
const onLineLastOffIndex = data.status.findIndex((s, i) => s.status == 'OFF' && i > onLineIndex)
let onlineData = {
cameraId: data.status[onLineIndex].cameraId,
name: data.status[onLineIndex].name,
time: data.status[onLineIndex].time,
}
if (onLineLastOffIndex >= 0) {
onlineData.offTime = data.status[onLineLastOffIndex].time
}
onArr.push(onlineData)
}
}
// 查当前配置由谁配置
const corUser = await app.fs.authRequest.get(`user/${c.createUser}/message`, {
query: {
// TODO 这里拿不到 token 去鉴权 怎么能系统间鉴权呢
// 暂时取消了鉴权系统对这里的判断
token: ''
}
})
const receiver = c.cameraStatusPushReceivers.map(r => r.receiver)
// 离线推送
if (offArr.length && c.noticeWay && c.noticeWay.includes('offline')) {
if (c.pushWay == 'email') {
// 邮件
let text = `${corUser[0].namePresent}】账号下的设备,截止【${moment(timeNow).format('YYYY年HH时mm分')}】,有${offArr.length}个设备掉线:\n`
text += offArr.map(o => `${o.name}】于【${o.time}】掉线,`).join('\n')
text += `\n请及时处理!`
await pushByEmail({
email: receiver,
title: '',
text,
})
} else if (c.pushWay == 'phone') {
// 短信
let text = `${corUser[0].namePresent}】账号下的`
if (offArr.length == 1) {
text += `${offArr[0].name}】离线,请及时处理!【${moment(offArr[0].time).format('YYYY年HH时mm分')}`
} else {
text += offArr.map(o => `${o.name}`).join('')
if (text.length > 35) {
text = text.substring(0, 35) + '...'
text += `${offArr.length}个摄像头离线`
}
text += `,请及时处理!【${moment().format('MM月DD日HH时mm分')}-${moment(timeNow).subtract(15, 'minutes').format('MM月DD日HH时mm分')}`
}
await pushBySms({
phone: receiver,
templateCode: '',
templateParam: '',
text
})
}
}
// 上线推送
if (onArr.length && c.noticeWay && c.noticeWay.includes('online')) {
if (c.pushWay == 'email') {
// 邮件
// const outTimeCameraOff = onArr.filter(a => !a.offTime)
// if (outTimeCameraOff.length) {
// let cameraIds = outTimeCameraOff.map(a => a.cameraId)
// const lastOfflineRes = await models.CameraStatus.findAll({
// where: {
// cameraId: { $in: cameraIds },
// status: 'OFF',
// time: {
// $lt: moment(timeNow).subtract(15, 'minutes').format()
// }
// },
// group: ['cameraId'],
// attributes: ['cameraId', [sequelize.fn('max', sequelize.col('time')), 'time']],
// })
// }
} else if (c.pushWay == 'phone') {
// 短信
}
}
}
}
console.log('cameraOnlinePush') console.log('cameraOnlinePush')
} catch (error) { } catch (error) {
} }
} }
) )

29
code/VideoAccess-VCMP/api/app/lib/schedule/freshYingshiMsg.js

@ -4,9 +4,10 @@ const moment = require('moment')
module.exports = function (app, opts) { module.exports = function (app, opts) {
const freshYingshiState = app.fs.scheduleInit( const freshYingshiState = app.fs.scheduleInit(
{ {
// interval: '* * 4 * * *', interval: '* */5 * * * *',
interval: '*/10 * * * *', // interval: '*/10 * * * *',
immediate: false, immediate: false,
proRun: true,
}, },
async () => { async () => {
@ -49,22 +50,39 @@ module.exports = function (app, opts) {
if (existD.online != storageD.online) { if (existD.online != storageD.online) {
// 状态更新 // 状态更新
if (storageD.online == 'ON' && !existD.playUrl) { if (storageD.online == 'ON' && !existD.playUrl) {
// 播放地址更新
const playUrlRes = await getYingshiPlayUrl({ deviceSerial: d.deviceSerial, token: tokenYingshi }) const playUrlRes = await getYingshiPlayUrl({ deviceSerial: d.deviceSerial, token: tokenYingshi })
storageD.playUrl = playUrlRes storageD.playUrl = playUrlRes
} }
// 在离线状态更新
await models.GbCamera.update(storageD, { await models.GbCamera.update(storageD, {
where: { where: {
id: existD.id id: existD.id
} }
}) })
// 状态更新 END // 状态向前端页面推送
// 状态推送
cameraStatePush({ cameraStatePush({
gbId: existD.id, gbId: existD.id,
online: storageD.online, online: storageD.online,
ipctype: storageD.ipctype, ipctype: storageD.ipctype,
}) })
// 记录日志
const cameraRes = await models.Camera.findAll({
where: {
gbId: existD.id
}
})
const nowTime = moment().format()
let bulkCreateData = cameraRes.map(c => {
return {
cameraId: c.id,
status: storageD.online,
time: nowTime
}
})
if (bulkCreateData.length) {
await models.CameraStatusOfflineLog.bulkCreate(bulkCreateData)
}
} }
} else { } else {
const yingshiRes = await models.GbCamera.create(storageD) const yingshiRes = await models.GbCamera.create(storageD)
@ -99,6 +117,7 @@ module.exports = function (app, opts) {
// interval: '*/30 * * * *', // interval: '*/30 * * * *',
interval: '0 34 5 1 * *', interval: '0 34 5 1 * *',
immediate: true, immediate: true,
proRun: true,
}, },
async () => { async () => {
try { try {

4
code/VideoAccess-VCMP/api/app/lib/schedule/index.js

@ -7,10 +7,10 @@ const nodeSchedule = require('node-schedule');
module.exports = async function (app, opts) { module.exports = async function (app, opts) {
const scheduleInit = ({ const scheduleInit = ({
interval, immediate interval, immediate, proRun,
}, callback) => { }, callback) => {
const j = nodeSchedule.scheduleJob(interval, callback); const j = nodeSchedule.scheduleJob(interval, callback);
if (immediate && !opts.dev) { if (immediate && (!proRun || (proRun && !opts.dev))) {
setTimeout(callback, 0) setTimeout(callback, 0)
} }
return j; return j;

6
code/VideoAccess-VCMP/api/app/lib/utils/push.js

@ -5,7 +5,7 @@ const Core = require('@alicloud/pop-core');
const nodemailer = require('nodemailer') const nodemailer = require('nodemailer')
module.exports = function (app, opts) { module.exports = function (app, opts) {
const pushBySms = async ({ phone = [] } = {}) => { const pushBySms = async ({ phone = [], templateCode, templateParam } = {}) => {
try { try {
if (phone.length) { if (phone.length) {
const client = new Core({ const client = new Core({
@ -29,7 +29,7 @@ module.exports = function (app, opts) {
} }
} }
const pushByEmail = async () => { const pushByEmail = async ({ email = [], title, text = '', html = '', attachments = undefined } = {}) => {
try { try {
let transporter = nodemailer.createTransport({ let transporter = nodemailer.createTransport({
host: opts.email.host, host: opts.email.host,
@ -44,7 +44,7 @@ module.exports = function (app, opts) {
// send mail with defined transport object // send mail with defined transport object
await transporter.sendMail({ await transporter.sendMail({
from: 'grazing-stars@qq.com', // sender address from: 'grazing-stars@qq.com', // sender address
to: email, // list of receivers 逗号分隔字符串 to: email.join(','), // list of receivers 逗号分隔字符串
subject: title, // Subject line subject: title, // Subject line
text: text, // plain text body text: text, // plain text body
html: html, // html body html: html, // html body

2
code/VideoAccess-VCMP/api/sequelize-automate.config.js

@ -26,7 +26,7 @@ module.exports = {
dir: './app/lib/models', // 指定输出 models 文件的目录 dir: './app/lib/models', // 指定输出 models 文件的目录
typesDir: 'models', // 指定输出 TypeScript 类型定义的文件目录,只有 TypeScript / Midway 等会有类型定义 typesDir: 'models', // 指定输出 TypeScript 类型定义的文件目录,只有 TypeScript / Midway 等会有类型定义
emptyDir: false, // !!! 谨慎操作 生成 models 之前是否清空 `dir` 以及 `typesDir` emptyDir: false, // !!! 谨慎操作 生成 models 之前是否清空 `dir` 以及 `typesDir`
tables: ['camera_status_push_log'], // 指定生成哪些表的 models,如 ['user', 'user_post'];如果为 null,则忽略改属性 tables: ['camera_status_offline_log'], // 指定生成哪些表的 models,如 ['user', 'user_post'];如果为 null,则忽略改属性
skipTables: [], // 指定跳过哪些表的 models,如 ['user'];如果为 null,则忽略改属性 skipTables: [], // 指定跳过哪些表的 models,如 ['user'];如果为 null,则忽略改属性
tsNoCheck: false, // 是否添加 `@ts-nocheck` 注释到 models 文件中 tsNoCheck: false, // 是否添加 `@ts-nocheck` 注释到 models 文件中
ignorePrefix: [], // 生成的模型名称忽略的前缀,因为 项目中有以下表名是以 t_ 开头的,在实际模型中不需要, 可以添加多个 [ 't_data_', 't_',] ,长度较长的 前缀放前面 ignorePrefix: [], // 生成的模型名称忽略的前缀,因为 项目中有以下表名是以 t_ 开头的,在实际模型中不需要, 可以添加多个 [ 't_data_', 't_',] ,长度较长的 前缀放前面

BIN
code/VideoAccess-VCMP/web/client/assets/images/background/icon_camera.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

BIN
code/VideoAccess-VCMP/web/client/assets/images/background/icon_email.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

BIN
code/VideoAccess-VCMP/web/client/assets/images/background/icon_phone.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 561 B

42
code/VideoAccess-VCMP/web/client/src/sections/equipmentWarehouse/components/fluoriteCamera.jsx

@ -83,7 +83,8 @@ function fluoriteCamera ({ cRef, CameraKind, CameraAbility, cameraData, ashTrue,
labelWidth="115px" labelWidth="115px"
onValueChange={(values) => { onValueChange={(values) => {
console.log(values); console.log(values);
let setting = ["abilityId", "cloudControl", "highDefinition", "kindId", "name", "position", "rtmp", "serialNo",] // let setting = ["abilityId", "cloudControl", "highDefinition", "kindId", "name", "position", "rtmp", "serialNo",]
let setting = ["abilityId", "cloudControl", "highDefinition", "kindId", "name", "position", "serialNo",]
let b = {} let b = {}
setting.map((item) => { setting.map((item) => {
if (values.hasOwnProperty(item)) { if (values.hasOwnProperty(item)) {
@ -109,7 +110,8 @@ function fluoriteCamera ({ cRef, CameraKind, CameraAbility, cameraData, ashTrue,
cloudControl: cameraData.cloudControl || "", cloudControl: cameraData.cloudControl || "",
voice: cameraData.voice || "", voice: cameraData.voice || "",
serialNo: cameraData.serialNo || "", serialNo: cameraData.serialNo || "",
rtmp: cameraData.rtmp || "", // rtmp: cameraData.rtmp || "",
rtmp: '此处无效 自动生成',
}} }}
getFormApi={(formApi) => (form.current = formApi)} getFormApi={(formApi) => (form.current = formApi)}
> >
@ -264,7 +266,7 @@ function fluoriteCamera ({ cRef, CameraKind, CameraAbility, cameraData, ashTrue,
label="设备类型:" label="设备类型:"
field="kindId" field="kindId"
placeholder="请选择摄像头类型" placeholder="请选择摄像头类型"
style={{ width: 180 }} style={{ width: 307 }}
rules={[{ required: true, message: "请选择摄像头类型" }]} rules={[{ required: true, message: "请选择摄像头类型" }]}
> >
{CameraKind.map((item, index) => ( {CameraKind.map((item, index) => (
@ -274,21 +276,6 @@ function fluoriteCamera ({ cRef, CameraKind, CameraAbility, cameraData, ashTrue,
))} ))}
</Form.Select> </Form.Select>
<Form.Select
label="设备能力:"
multiple
maxTagCount={1}
field="abilityId"
placeholder="请选择能力"
style={{ width: 180 }}
rules={[{ required: true, message: "请选择能力" }]}
>
{CameraAbility.map((item, index) => (
<Form.Select.Option key={index} value={item.id}>
{item.ability}
</Form.Select.Option>
))}
</Form.Select>
</Col> </Col>
<Col span={12}> <Col span={12}>
{/* 云台支持 */} {/* 云台支持 */}
@ -473,7 +460,7 @@ function fluoriteCamera ({ cRef, CameraKind, CameraAbility, cameraData, ashTrue,
rules={[{ required: true, message: "请输入设备序列号" }]} rules={[{ required: true, message: "请输入设备序列号" }]}
/> />
{/* RTMP地址接入 */} {/* RTMP地址接入 */}
<TextArea {/* <TextArea
style={{ width: 320, height: 90 }} style={{ width: 320, height: 90 }}
field="rtmp" field="rtmp"
label="RTMP地址接入:" label="RTMP地址接入:"
@ -481,7 +468,22 @@ function fluoriteCamera ({ cRef, CameraKind, CameraAbility, cameraData, ashTrue,
disabled={cameraData.id ? true : false} disabled={cameraData.id ? true : false}
placeholder="请输入RTMP地址接入" placeholder="请输入RTMP地址接入"
rules={[{ required: true, message: "请输入RTMP地址接入" }]} rules={[{ required: true, message: "请输入RTMP地址接入" }]}
/> /> */}
<Form.Select
label="设备能力:"
multiple
maxTagCount={1}
field="abilityId"
placeholder="请选择能力"
style={{ width: 307 }}
rules={[{ required: true, message: "请选择能力" }]}
>
{CameraAbility.map((item, index) => (
<Form.Select.Option key={index} value={item.id}>
{item.ability}
</Form.Select.Option>
))}
</Form.Select>
</Col> </Col>
</Row> </Row>
</Form> </Form>

43
code/VideoAccess-VCMP/web/client/src/sections/equipmentWarehouse/components/ipcCamera.jsx

@ -88,7 +88,8 @@ function ipcCamera ({
labelWidth="115px" labelWidth="115px"
onValueChange={(values) => { onValueChange={(values) => {
console.log(values); console.log(values);
let setting = ["abilityId", "cloudControl", "kindId", "name", "position", "rtmp", "serialNo"] let setting = ["abilityId", "cloudControl", "kindId", "name", "position", "serialNo"]
// let setting = ["abilityId", "cloudControl", "kindId", "name", "position", "rtmp", "serialNo"]
let b = {} let b = {}
setting.map((item) => { setting.map((item) => {
if (values.hasOwnProperty(item)) { if (values.hasOwnProperty(item)) {
@ -113,7 +114,8 @@ function ipcCamera ({
cloudControl: cameraData.cloudControl || "", cloudControl: cameraData.cloudControl || "",
voice: cameraData.voice || "", voice: cameraData.voice || "",
serialNo: cameraData.serialNo || "", serialNo: cameraData.serialNo || "",
rtmp: cameraData.rtmp || "", rtmp: "此处无效 自动生成",
// rtmp: cameraData.rtmp || "",
}} }}
getFormApi={(formApi) => (form.current = formApi)} getFormApi={(formApi) => (form.current = formApi)}
> >
@ -190,7 +192,7 @@ function ipcCamera ({
label="设备类型:" label="设备类型:"
field="kindId" field="kindId"
placeholder="请选择摄像头类型" placeholder="请选择摄像头类型"
style={{ width: 180 }} style={{ width: 307 }}
rules={[{ required: true, message: "请选择摄像头类型" }]} rules={[{ required: true, message: "请选择摄像头类型" }]}
> >
{CameraKind.map((item, index) => ( {CameraKind.map((item, index) => (
@ -199,22 +201,6 @@ function ipcCamera ({
</Form.Select.Option> </Form.Select.Option>
))} ))}
</Form.Select> </Form.Select>
<Form.Select
label="设备能力:"
multiple
maxTagCount={1}
field="abilityId"
placeholder="请选择能力"
style={{ width: 180 }}
rules={[{ required: true, message: "请选择能力" }]}
>
{CameraAbility.map((item, index) => (
<Form.Select.Option key={index} value={item.id}>
{item.ability}
</Form.Select.Option>
))}
</Form.Select>
</Col> </Col>
<Col span={12}> <Col span={12}>
<Form.RadioGroup <Form.RadioGroup
@ -398,14 +384,29 @@ function ipcCamera ({
rules={[{ required: true, message: "请输入设备编号" }]} rules={[{ required: true, message: "请输入设备编号" }]}
/> />
</div> </div>
<TextArea <Form.Select
label="设备能力:"
multiple
maxTagCount={1}
field="abilityId"
placeholder="请选择能力"
style={{ width: 307 }}
rules={[{ required: true, message: "请选择能力" }]}
>
{CameraAbility.map((item, index) => (
<Form.Select.Option key={index} value={item.id}>
{item.ability}
</Form.Select.Option>
))}
</Form.Select>
{/* <TextArea
style={{ width: 320, height: 90 }} style={{ width: 320, height: 90 }}
field="rtmp" field="rtmp"
label="RTMP地址接入:" label="RTMP地址接入:"
disabled={cameraData.id ? true : false} disabled={cameraData.id ? true : false}
placeholder="请输入RTMP地址接入" placeholder="请输入RTMP地址接入"
rules={[{ required: true, message: "请输入RTMP地址" }]} rules={[{ required: true, message: "请输入RTMP地址" }]}
/> /> */}
</Col> </Col>
{/* <Col span={18}> {/* <Col span={18}>

21
code/VideoAccess-VCMP/web/client/src/sections/equipmentWarehouse/components/nvrCamera.jsx

@ -54,6 +54,13 @@ function nvrCamera ({ dispatch, actions, nvrRef, cameraData, addNvr, nvrNumber,
streamId: cameraDataNvr.serialNo, streamId: cameraDataNvr.serialNo,
}) })
).then((res) => { ).then((res) => {
if(res.success==false){
setstep("none");
setloading(false);
setstepp("none");
setloadingTip("获取中...");
return
}
let oneData = res.payload.data[0]; let oneData = res.payload.data[0];
let modifyData = res.payload.data.find( let modifyData = res.payload.data.find(
(item) => item.id == cameraData.gbId); (item) => item.id == cameraData.gbId);
@ -92,6 +99,13 @@ function nvrCamera ({ dispatch, actions, nvrRef, cameraData, addNvr, nvrNumber,
setloading(true); setloading(true);
setstep("block"); setstep("block");
dispatch(equipmentWarehouse.getVideoStreaming({ streamId: nvrNumber })).then((res) => { dispatch(equipmentWarehouse.getVideoStreaming({ streamId: nvrNumber })).then((res) => {
if(res.success==false){
setstep("none");
setloading(false);
setstepp("none");
setloadingTip("获取中...");
return
}
let chooseList = []; let chooseList = [];
let data = res.payload.data.map((item) => { let data = res.payload.data.map((item) => {
item.change = false; item.change = false;
@ -159,6 +173,13 @@ function nvrCamera ({ dispatch, actions, nvrRef, cameraData, addNvr, nvrNumber,
setloading(true); setloading(true);
setstep("block"); setstep("block");
dispatch(equipmentWarehouse.getVideoStreaming(valuess)).then((res) => { dispatch(equipmentWarehouse.getVideoStreaming(valuess)).then((res) => {
if(res.success==false){
setstep("none");
setloading(false);
setstepp("none");
setloadingTip("获取中...");
return
}
let data = res.payload.data.map((item) => { let data = res.payload.data.map((item) => {
item.change = false; item.change = false;
item.support = false; item.support = false;

102
code/VideoAccess-VCMP/web/client/src/sections/offline/components/pushSideSheet.jsx

@ -1,8 +1,9 @@
import React, { useState, useRef, useEffect } from "react"; import React, { useState, useRef, useEffect } from "react";
import { connect } from "react-redux"; import { connect } from "react-redux";
import { Modal, Spin,Input,Collapse ,SideSheet,Tabs, TabPane} from "@douyinfe/semi-ui"; import { Modal, Spin,Input,Collapse ,SideSheet,Tabs, TabPane,Popover} from "@douyinfe/semi-ui";
import moment from "moment"; import moment from "moment";
import './pushSideSheet.less' import './pushSideSheet.less'
import { push } from "react-router-redux";
function pushSideSheet (props) { function pushSideSheet (props) {
const { const {
close, close,
@ -11,20 +12,67 @@ function pushSideSheet (props) {
journal// journal//
} = props; } = props;
const { offline } = actions; const { offline } = actions;
const [notesValue, setNotesValue] = useState('');// const [logList, setLogList] = useState([]);//
const [noticeTest, setNoticeTest] = useState('');//
const [equipmentTest, setEquipmentTest] = useState();//
// //
useEffect(() => { useEffect(() => {
let timeList = [] let timeList = []
for (let index = 0; index < journal.length; index++) { for (let index = 0; index < journal.length; index++) {
let receiverTest=journal[index].receiver.join(';');
let camerTest=''
for (let j = 0; j < journal[index].camera.length; j++) {
camerTest=camerTest+journal[index].camera[j].name+';'
}
timeList.push( timeList.push(
{ {
title:getTimeTitle(journal[index].time), title:getTimeTitle(journal[index].time),
time:getTimeData(journal[index].time) time:getTimeData(journal[index].time),
cameraList:camerTest,
receiverList:receiverTest
} }
) )
} }
console.log('timeList',timeList); let yearList=[]
for (let index = 0; index < journal.length; index++) {
yearList.push(
{
title:getTimeTitle(journal[index].time),
}
)
}
let showList=[]
let obj = {};
for (let i = 0; i < yearList.length; i++) {
if (!obj[yearList[i].title]) {
showList.push(yearList[i]);
obj[yearList[i].title] = true;
}
}
for (let index = 0; index < showList.length; index++) {
showList[index].timeList=[]
}
for (let j = 0; j < showList.length; j++) {
for (let i = 0; i < timeList.length; i++) {
if(timeList[i].title==showList[j].title){
showList[j].timeList.push({time:timeList[i].time,cameraList:timeList[i].cameraList,receiverList:timeList[i].receiverList})
}
}
}
setLogList(showList)
let noticeList =''
for (let i = 0; i < pushData.cameraStatusPushReceivers.length; i++) {
noticeList=noticeList+pushData.cameraStatusPushReceivers[i].receiver+';'
}
setNoticeTest(noticeList)
let equipmentList=''
for (let i = 0; i < pushData.cameraStatusPushMonitors.length; i++) {
noticeList=noticeList+pushData.cameraStatusPushMonitors[i].camera.name+';'
}
setEquipmentTest(equipmentList)
console.log('pushData',pushData);
}, []); }, []);
function getTimeTitle(date){ function getTimeTitle(date){
let year = moment(date).year()+'年' let year = moment(date).year()+'年'
@ -47,16 +95,54 @@ function pushSideSheet (props) {
<Tabs type="line"> <Tabs type="line">
<TabPane tab="推送日志" itemKey="1"> <TabPane tab="推送日志" itemKey="1">
<Collapse> <Collapse>
<Collapse.Panel header={<div style={{width:'100%',textAlign:'center'}}>11111111</div>} itemKey="1"> {logList.map((item, index) => {
<p>Hi, bytedance dance dance. This is the docsite of Semi UI. </p> return (
<Collapse.Panel header={<div style={{width:'100%',textAlign:'center'}}>{item.title}</div>} itemKey={String(index)} key={index} >
{item.timeList.map((itm,idx)=>{
return(
<div style={{display:'flex',alignItems:'center',height:46,borderBottom: '1px solid #E8E8E8',fontSize:12,color:'rgba(0,0,0,0.6500)'}} key={idx}>
<div style={{margin:'0px 24px',width:74}}>{itm.time}</div>
<Popover content={
<div style={{ padding: 12,width:300,wordBreak:'break-all' }}>
{itm.receiverList}
</div>
}>
<div style={{marginRight:24,width:262,textOverflow:'ellipsis',whiteSpace:'nowrap',overflow:'hidden'}}>{itm.receiverList}</div>
</Popover>
<Popover content={
<div style={{ padding: 12,width:200,wordBreak:'break-all' }}>
{itm.cameraList}
</div>
}>
<div style={{marginRight:24,width:133,textOverflow:'ellipsis',whiteSpace:'nowrap',overflow:'hidden'}}>{itm.cameraList}</div>
</Popover>
<div style={{marginRight:25,width:83}} className='online' >上线及时通知</div>
</div>
)
})}
</Collapse.Panel> </Collapse.Panel>
)})}
</Collapse> </Collapse>
</TabPane> </TabPane>
<TabPane tab="监听范围" itemKey="2"> <TabPane tab="监听范围" itemKey="2">
22 <div style={{margin:'12px 36px',fontSize:12,color:'rgba(0,0,0,0.6500)'}}>
<img
src="/assets/images/background/icon_camera.png"
alt="设置"
style={{ width: 24, height: 24 }}
/>
<div>{equipmentTest}</div>
</div>
</TabPane> </TabPane>
<TabPane tab="接收信息" itemKey="3"> <TabPane tab="接收信息" itemKey="3">
33 <div style={{margin:'12px 36px',fontSize:12,color:'rgba(0,0,0,0.6500)'}}>
<img
src={pushData.pushWay=='email'?"/assets/images/background/icon_email.png":"/assets/images/background/icon_phone.png"}
alt="设置"
style={{ width: 24, height: 24 }}
/>
<div>{noticeTest}</div>
</div>
</TabPane> </TabPane>
</Tabs> </Tabs>
</SideSheet> </SideSheet>

44
code/VideoAccess-VCMP/web/client/src/sections/offline/components/pushSideSheet.less

@ -5,4 +5,48 @@
.semi-tabs-tab-active{ .semi-tabs-tab-active{
color: #1859C1; color: #1859C1;
} }
.semi-sidesheet-body{
padding:0px;
}
.semi-tabs-bar{
margin: 0px 24px;
border-bottom:none;
}
.semi-collapse-header{
background: #F7F8FA;
margin:0px;
}
.semi-tabs-content{
padding:0px;
}
.semi-collapse-content{
padding: 0px;
}
.online{
background: #E7F6EB;
color: #007B22;
display: flex;
align-items: center;
height: 22px;
justify-content: center;
border-radius: 3px;
}
.offline{
background:#FFF6D8;
color: #E8B500;
display: flex;
align-items: center;
height: 22px;
justify-content: center;
border-radius: 3px;
}
.timing{
background: #F0F6FF;
color: #1859C1;
display: flex;
align-items: center;
height: 22px;
justify-content: center;
border-radius: 3px;
}
} }

17
code/VideoAccess-VCMP/web/client/src/sections/offline/containers/carrierpigeon.jsx

@ -198,13 +198,12 @@ const Carrierpigeon = (props) => {
</Button> </Button>
<Button onClick={()=>{ <Button onClick={()=>{
dispatch(offline.getPushLog(row.id)).then((res) => { dispatch(offline.getPushLog(row.id)).then((res) => {
// equipmentGetStatusPush();
setJournal(res.payload.data); setJournal(res.payload.data);
setSideSheetVisible(true);
setPushData(row); setPushData(row);
setSideSheetVisible(true);
}); });
}} theme="borderless"> }} theme="borderless">
日志 查看
</Button> </Button>
</div> </div>
); );
@ -376,7 +375,7 @@ const Carrierpigeon = (props) => {
> >
<Form.Select.Option value="">全部</Form.Select.Option> <Form.Select.Option value="">全部</Form.Select.Option>
<Form.Select.Option value="phone">短信通知</Form.Select.Option> <Form.Select.Option value="phone">短信通知</Form.Select.Option>
<Form.Select.Option value="email ">邮件通知</Form.Select.Option> <Form.Select.Option value="email">邮件通知</Form.Select.Option>
</Form.Select> </Form.Select>
</Form> </Form>
<div <div
@ -479,8 +478,7 @@ const Carrierpigeon = (props) => {
> >
<Table <Table
columns={setupp.filter((s) => s)} columns={setupp.filter((s) => s)}
// dataSource={StatusPushList.data} dataSource={StatusPushList.rows}
dataSource={StatusPushList}
bordered={false} bordered={false}
empty="暂无数据" empty="暂无数据"
style={{ style={{
@ -498,10 +496,10 @@ const Carrierpigeon = (props) => {
}} }}
> >
<span style={{ lineHeight: "30px" }}> <span style={{ lineHeight: "30px" }}>
{StatusPushList.total}条策略 {StatusPushList.count}条策略
</span> </span>
<Pagination <Pagination
total={StatusPushList.total} total={StatusPushList.count}
showSizeChanger showSizeChanger
currentPage={query.page + 1} currentPage={query.page + 1}
pageSizeOpts={[10, 20, 30, 40]} pageSizeOpts={[10, 20, 30, 40]}
@ -556,8 +554,7 @@ function mapStateToProps (state) {
user: auth.user, user: auth.user,
actions: global.actions, actions: global.actions,
members: members.data, members: members.data,
StatusPushList: StatusPushList.data || [], StatusPushList: StatusPushList.data || {},
// StatusPushList: StatusPushList.data || {},
}; };
} }

2
code/VideoAccess-VCMP/web/client/src/utils/webapi.js

@ -47,7 +47,7 @@ export const ApiTable = {
getStatusSimpleAll:'status/simple_all',//获取全部状态码简略信息 getStatusSimpleAll:'status/simple_all',//获取全部状态码简略信息
getCameraListAll:'camera/listAll',//获取所有摄像头信息 getCameraListAll:'camera/listAll',//获取所有摄像头信息
getStatusPush:'status/push',//获取推送配置 getStatusPush:'status/push',//获取推送配置
putSasdtatusPush:'sasdtatus/push',//编辑推送配置 putSasdtatusPush:'status/push',//编辑推送配置
delPush:'status/push/{configId}',//删除推送配置 delPush:'status/push/{configId}',//删除推送配置
putPushBanned:'status/push/banned',//禁用推送配置 putPushBanned:'status/push/banned',//禁用推送配置
getPushCopy:'status/push/{configId}/copy',//复制推送配置 getPushCopy:'status/push/{configId}/copy',//复制推送配置

Loading…
Cancel
Save