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. 166
      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
} = ctx.request.body;
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 = {
name, pushWay, noticeWay, timing
}
if (configId) {
const configRes = await models.CameraStatusPushConfig.findOne({
where: {
id: configId
}
})
if (!configRes) {
if (!editDataExist) {
throw '参数错误'
} else {
// DO UPDATE
@ -89,7 +124,7 @@ async function get (ctx) {
try {
const models = ctx.fs.dc.models;
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;
let findOption = {
@ -104,13 +139,16 @@ async function get (ctx) {
if (page && limit) {
findOption.offset = page * limit
}
if (keyword) {
if (name) {
findOption.where['$or'] = {
name: {
$like: `%${keyword}%`
$like: `%${name}%`
},
}
}
if (pushWay) {
findOption.where.pushWay = pushWay
}
const configLimitRes = await models.CameraStatusPushConfig.findAll(findOption)
@ -131,6 +169,7 @@ async function get (ctx) {
'cameraStatusPushMonitors.id',
'cameraStatusPushLogs.id',
'cameraStatusPushReceivers.id',
'cameraStatusPushMonitors->camera.id',
]
findOption.order = [
[orderBy || 'id', orderDirection || 'DESC']
@ -141,6 +180,10 @@ async function get (ctx) {
attributes: ['cameraId'],
required: false,
duplicating: true,
include: [{
model: models.Camera,
attributes: ['name'],
}]
},
{
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: {} }
// 加载定义模型 历史写法
// 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) => {
@ -64,7 +55,7 @@ module.exports.models = function (dc) { // dc = { orm: Sequelize对象, ORM: Seq
const {
Nvr, Camera, CameraAbility, CameraAbilityBind, CameraKind, CameraRemark,
GbCamera, SecretYingshi, Vender, CameraStatus, CameraStatusResolve, CameraStatusLog,
CameraStatusPushConfig, CameraStatusPushMonitor, CameraStatusPushLog, CameraStatusPushReceiver,
CameraStatusPushConfig, CameraStatusPushMonitor, CameraStatusPushLog, CameraStatusPushReceiver, CameraStatusOfflineLog
} = dc.models;
// 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' });
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' });
CameraStatusPushConfig.hasMany(CameraStatusPushLog, { foreignKey: 'pushConfigId', sourceKey: 'id' });
CameraStatusPushReceiver.belongsTo(CameraStatusPushConfig, { foreignKey: 'configId', targetKey: '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;
};

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

@ -3,12 +3,174 @@ const moment = require('moment')
module.exports = function (app, opts) {
const cameraOnlinePush = app.fs.scheduleInit(
{
// interval: '* * 4 * * *',
interval: '*/15 * * * *',
interval: '* */15 * * * *',
// interval: '*/15 * * * * *',
immediate: false,
proRun: false,
},
async () => {
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')
} 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) {
const freshYingshiState = app.fs.scheduleInit(
{
// interval: '* * 4 * * *',
interval: '*/10 * * * *',
interval: '* */5 * * * *',
// interval: '*/10 * * * *',
immediate: false,
proRun: true,
},
async () => {
@ -49,22 +50,39 @@ module.exports = function (app, opts) {
if (existD.online != storageD.online) {
// 状态更新
if (storageD.online == 'ON' && !existD.playUrl) {
// 播放地址更新
const playUrlRes = await getYingshiPlayUrl({ deviceSerial: d.deviceSerial, token: tokenYingshi })
storageD.playUrl = playUrlRes
}
// 在离线状态更新
await models.GbCamera.update(storageD, {
where: {
id: existD.id
}
})
// 状态更新 END
// 状态推送
// 状态向前端页面推送
cameraStatePush({
gbId: existD.id,
online: storageD.online,
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 {
const yingshiRes = await models.GbCamera.create(storageD)
@ -99,6 +117,7 @@ module.exports = function (app, opts) {
// interval: '*/30 * * * *',
interval: '0 34 5 1 * *',
immediate: true,
proRun: true,
},
async () => {
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) {
const scheduleInit = ({
interval, immediate
interval, immediate, proRun,
}, callback) => {
const j = nodeSchedule.scheduleJob(interval, callback);
if (immediate && !opts.dev) {
if (immediate && (!proRun || (proRun && !opts.dev))) {
setTimeout(callback, 0)
}
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')
module.exports = function (app, opts) {
const pushBySms = async ({ phone = [] } = {}) => {
const pushBySms = async ({ phone = [], templateCode, templateParam } = {}) => {
try {
if (phone.length) {
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 {
let transporter = nodemailer.createTransport({
host: opts.email.host,
@ -44,7 +44,7 @@ module.exports = function (app, opts) {
// send mail with defined transport object
await transporter.sendMail({
from: 'grazing-stars@qq.com', // sender address
to: email, // list of receivers 逗号分隔字符串
to: email.join(','), // list of receivers 逗号分隔字符串
subject: title, // Subject line
text: text, // plain text 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 文件的目录
typesDir: 'models', // 指定输出 TypeScript 类型定义的文件目录,只有 TypeScript / Midway 等会有类型定义
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,则忽略改属性
tsNoCheck: false, // 是否添加 `@ts-nocheck` 注释到 models 文件中
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"
onValueChange={(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 = {}
setting.map((item) => {
if (values.hasOwnProperty(item)) {
@ -109,7 +110,8 @@ function fluoriteCamera ({ cRef, CameraKind, CameraAbility, cameraData, ashTrue,
cloudControl: cameraData.cloudControl || "",
voice: cameraData.voice || "",
serialNo: cameraData.serialNo || "",
rtmp: cameraData.rtmp || "",
// rtmp: cameraData.rtmp || "",
rtmp: '此处无效 自动生成',
}}
getFormApi={(formApi) => (form.current = formApi)}
>
@ -264,7 +266,7 @@ function fluoriteCamera ({ cRef, CameraKind, CameraAbility, cameraData, ashTrue,
label="设备类型:"
field="kindId"
placeholder="请选择摄像头类型"
style={{ width: 180 }}
style={{ width: 307 }}
rules={[{ required: true, message: "请选择摄像头类型" }]}
>
{CameraKind.map((item, index) => (
@ -274,21 +276,6 @@ function fluoriteCamera ({ cRef, CameraKind, CameraAbility, cameraData, ashTrue,
))}
</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 span={12}>
{/* 云台支持 */}
@ -473,7 +460,7 @@ function fluoriteCamera ({ cRef, CameraKind, CameraAbility, cameraData, ashTrue,
rules={[{ required: true, message: "请输入设备序列号" }]}
/>
{/* RTMP地址接入 */}
<TextArea
{/* <TextArea
style={{ width: 320, height: 90 }}
field="rtmp"
label="RTMP地址接入:"
@ -481,7 +468,22 @@ function fluoriteCamera ({ cRef, CameraKind, CameraAbility, cameraData, ashTrue,
disabled={cameraData.id ? true : false}
placeholder="请输入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>
</Row>
</Form>

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

@ -88,7 +88,8 @@ function ipcCamera ({
labelWidth="115px"
onValueChange={(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 = {}
setting.map((item) => {
if (values.hasOwnProperty(item)) {
@ -113,7 +114,8 @@ function ipcCamera ({
cloudControl: cameraData.cloudControl || "",
voice: cameraData.voice || "",
serialNo: cameraData.serialNo || "",
rtmp: cameraData.rtmp || "",
rtmp: "此处无效 自动生成",
// rtmp: cameraData.rtmp || "",
}}
getFormApi={(formApi) => (form.current = formApi)}
>
@ -190,7 +192,7 @@ function ipcCamera ({
label="设备类型:"
field="kindId"
placeholder="请选择摄像头类型"
style={{ width: 180 }}
style={{ width: 307 }}
rules={[{ required: true, message: "请选择摄像头类型" }]}
>
{CameraKind.map((item, index) => (
@ -199,22 +201,6 @@ function ipcCamera ({
</Form.Select.Option>
))}
</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 span={12}>
<Form.RadioGroup
@ -398,14 +384,29 @@ function ipcCamera ({
rules={[{ required: true, message: "请输入设备编号" }]}
/>
</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 }}
field="rtmp"
label="RTMP地址接入:"
disabled={cameraData.id ? true : false}
placeholder="请输入RTMP地址接入"
rules={[{ required: true, message: "请输入RTMP地址" }]}
/>
/> */}
</Col>
{/* <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,
})
).then((res) => {
if(res.success==false){
setstep("none");
setloading(false);
setstepp("none");
setloadingTip("获取中...");
return
}
let oneData = res.payload.data[0];
let modifyData = res.payload.data.find(
(item) => item.id == cameraData.gbId);
@ -92,6 +99,13 @@ function nvrCamera ({ dispatch, actions, nvrRef, cameraData, addNvr, nvrNumber,
setloading(true);
setstep("block");
dispatch(equipmentWarehouse.getVideoStreaming({ streamId: nvrNumber })).then((res) => {
if(res.success==false){
setstep("none");
setloading(false);
setstepp("none");
setloadingTip("获取中...");
return
}
let chooseList = [];
let data = res.payload.data.map((item) => {
item.change = false;
@ -159,6 +173,13 @@ function nvrCamera ({ dispatch, actions, nvrRef, cameraData, addNvr, nvrNumber,
setloading(true);
setstep("block");
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) => {
item.change = 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 { 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 './pushSideSheet.less'
import { push } from "react-router-redux";
function pushSideSheet (props) {
const {
close,
@ -11,20 +12,67 @@ function pushSideSheet (props) {
journal//
} = props;
const { offline } = actions;
const [notesValue, setNotesValue] = useState('');//
const [logList, setLogList] = useState([]);//
const [noticeTest, setNoticeTest] = useState('');//
const [equipmentTest, setEquipmentTest] = useState();//
//
useEffect(() => {
let timeList = []
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(
{
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){
let year = moment(date).year()+'年'
@ -47,16 +95,54 @@ function pushSideSheet (props) {
<Tabs type="line">
<TabPane tab="推送日志" itemKey="1">
<Collapse>
<Collapse.Panel header={<div style={{width:'100%',textAlign:'center'}}>11111111</div>} itemKey="1">
<p>Hi, bytedance dance dance. This is the docsite of Semi UI. </p>
{logList.map((item, index) => {
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>
</TabPane>
<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 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>
</Tabs>
</SideSheet>

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

@ -5,4 +5,48 @@
.semi-tabs-tab-active{
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 onClick={()=>{
dispatch(offline.getPushLog(row.id)).then((res) => {
// equipmentGetStatusPush();
setJournal(res.payload.data);
setSideSheetVisible(true);
setPushData(row);
setSideSheetVisible(true);
});
}} theme="borderless">
日志
查看
</Button>
</div>
);
@ -376,7 +375,7 @@ const Carrierpigeon = (props) => {
>
<Form.Select.Option value="">全部</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>
<div
@ -479,8 +478,7 @@ const Carrierpigeon = (props) => {
>
<Table
columns={setupp.filter((s) => s)}
// dataSource={StatusPushList.data}
dataSource={StatusPushList}
dataSource={StatusPushList.rows}
bordered={false}
empty="暂无数据"
style={{
@ -498,10 +496,10 @@ const Carrierpigeon = (props) => {
}}
>
<span style={{ lineHeight: "30px" }}>
{StatusPushList.total}条策略
{StatusPushList.count}条策略
</span>
<Pagination
total={StatusPushList.total}
total={StatusPushList.count}
showSizeChanger
currentPage={query.page + 1}
pageSizeOpts={[10, 20, 30, 40]}
@ -556,8 +554,7 @@ function mapStateToProps (state) {
user: auth.user,
actions: global.actions,
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',//获取全部状态码简略信息
getCameraListAll:'camera/listAll',//获取所有摄像头信息
getStatusPush:'status/push',//获取推送配置
putSasdtatusPush:'sasdtatus/push',//编辑推送配置
putSasdtatusPush:'status/push',//编辑推送配置
delPush:'status/push/{configId}',//删除推送配置
putPushBanned:'status/push/banned',//禁用推送配置
getPushCopy:'status/push/{configId}/copy',//复制推送配置

Loading…
Cancel
Save