Date: Fri, 29 Apr 2022 15:23:26 +0800
Subject: [PATCH 2/4] =?UTF-8?q?NVR=20=E5=A2=9E=E5=88=A0=E6=8E=A5=E5=8F=A3?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
code/VideoAccess-VCMP/api/.vscode/launch.json | 11 ++-
.../api/app/lib/controllers/nvr/index.js | 68 +++++++++++++++++++
code/VideoAccess-VCMP/api/app/lib/index.js | 5 +-
.../api/app/lib/middlewares/authenticator.js | 25 +++----
.../api/app/lib/routes/nvr/index.js | 11 +++
.../api/app/lib/service/redis.js | 29 ++++++++
code/VideoAccess-VCMP/api/config.js | 14 +++-
code/VideoAccess-VCMP/api/package.json | 4 +-
8 files changed, 142 insertions(+), 25 deletions(-)
create mode 100644 code/VideoAccess-VCMP/api/app/lib/controllers/nvr/index.js
create mode 100644 code/VideoAccess-VCMP/api/app/lib/routes/nvr/index.js
create mode 100644 code/VideoAccess-VCMP/api/app/lib/service/redis.js
diff --git a/code/VideoAccess-VCMP/api/.vscode/launch.json b/code/VideoAccess-VCMP/api/.vscode/launch.json
index e8d5ae1..5bef1ea 100644
--- a/code/VideoAccess-VCMP/api/.vscode/launch.json
+++ b/code/VideoAccess-VCMP/api/.vscode/launch.json
@@ -13,12 +13,11 @@
"NODE_ENV": "development"
},
"args": [
- "-p 14000",
- "-f http://localhost:14000",
- // "-g postgres://postgres:123@10.8.30.32:5432/yinjiguanli",
- // "-g postgres://postgres:123456@221.230.55.27:5432/yinjiguanli",
- // "-g postgres://FashionAdmin:123456@10.8.30.156:5432/SmartEmergency",
- "-g postgres://postgres:Mantis1921@116.63.50.139:54327/smartYingji"
+ "-p 4000",
+ "-f http://localhost:4000",
+ "-g postgres://postgres:123@10.8.30.32:5432/video_access",
+ "--redisHost 127.0.0.1",
+ "--redisPort 6379"
]
},
{
diff --git a/code/VideoAccess-VCMP/api/app/lib/controllers/nvr/index.js b/code/VideoAccess-VCMP/api/app/lib/controllers/nvr/index.js
new file mode 100644
index 0000000..5bb4373
--- /dev/null
+++ b/code/VideoAccess-VCMP/api/app/lib/controllers/nvr/index.js
@@ -0,0 +1,68 @@
+'use strict';
+const moment = require('moment')
+
+async function edit (ctx, next) {
+ const transaction = await ctx.fs.dc.orm.transaction();
+ try {
+ const models = ctx.fs.dc.models;
+ const { userId } = ctx.fs.api
+ const data = ctx.request.body;
+
+ // 或取其他服务信息
+ const nvrData = {
+ channelCount: 8,
+ port: 8080,
+ }
+
+ if (data.id) {
+ // 修改
+ const storageData = Object.assign({}, data, nvrData)
+ await models.Nvr.update(storageData, {
+ where: {
+ id: data.id
+ },
+ transaction
+ })
+ } else {
+ // 添加
+ const storageData = Object.assign({}, data, nvrData, {
+ createTime: moment().format(),
+ createUserId: userId,
+ delete: false,
+ })
+ await models.Nvr.create(storageData, { transaction })
+ }
+
+ await transaction.commit();
+ ctx.status = 204;
+ } catch (error) {
+ await transaction.rollback();
+ ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
+ ctx.status = 400;
+ ctx.body = {}
+ }
+}
+
+async function del (ctx, next) {
+ try {
+ const models = ctx.fs.dc.models;
+ const { nvrId } = ctx.params
+
+ await models.Nvr.destroy({
+ where: {
+ id: nvrId
+ }
+ })
+
+ ctx.status = 204;
+ } catch (error) {
+ ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
+ ctx.status = 400;
+ ctx.body = {}
+ }
+}
+
+module.exports = {
+ edit,
+ del,
+};
\ No newline at end of file
diff --git a/code/VideoAccess-VCMP/api/app/lib/index.js b/code/VideoAccess-VCMP/api/app/lib/index.js
index 0fd17b8..d5668f6 100644
--- a/code/VideoAccess-VCMP/api/app/lib/index.js
+++ b/code/VideoAccess-VCMP/api/app/lib/index.js
@@ -1,6 +1,7 @@
'use strict';
const routes = require('./routes');
+const redisConnect = require('./service/redis')
const authenticator = require('./middlewares/authenticator');
// const apiLog = require('./middlewares/api-log');
const businessRest = require('./middlewares/business-rest');
@@ -12,8 +13,10 @@ module.exports.entry = function (app, router, opts) {
app.fs.api.authAttr = app.fs.api.authAttr || {};
app.fs.api.logAttr = app.fs.api.logAttr || {};
+ redisConnect(app, opts)
+
router.use(authenticator(app, opts));
- router.use(businessRest(app, router, opts));
+ // router.use(businessRest(app, router, opts));
// router.use(apiLog(app, opts));
router = routes(app, router, opts);
diff --git a/code/VideoAccess-VCMP/api/app/lib/middlewares/authenticator.js b/code/VideoAccess-VCMP/api/app/lib/middlewares/authenticator.js
index cdc5caf..8399456 100644
--- a/code/VideoAccess-VCMP/api/app/lib/middlewares/authenticator.js
+++ b/code/VideoAccess-VCMP/api/app/lib/middlewares/authenticator.js
@@ -13,13 +13,13 @@ class ExcludesUrls {
this.reload(opts);
}
- sanitizePath(path) {
+ sanitizePath (path) {
if (!path) return '/';
const p = '/' + path.replace(/^\/+/i, '').replace(/\/+$/, '').replace(/\/{2,}/, '/');
return p;
}
- reload(opts) {
+ reload (opts) {
// load all url
if (!this.allUrls) {
this.allUrls = opts;
@@ -37,7 +37,7 @@ class ExcludesUrls {
}
}
- isExcluded(path, method) {
+ isExcluded (path, method) {
return this.allUrls.some(function (url) {
return !url.auth
&& url.pregexp.test(path)
@@ -58,9 +58,7 @@ let isPathExcluded = function (opts, path, method) {
if (!excludeAll) {
let excludeOpts = opts.exclude || [];
excludeOpts.push({ p: '/login', o: 'POST' });
- excludeOpts.push({ p: '/wxLogin', o: 'POST' });
excludeOpts.push({ p: '/logout', o: 'PUT' });
- excludeOpts.push({ p: '/wxLogout', o: 'PUT' });
excludes = new ExcludesUrls(excludeOpts);
}
let excluded = excludeAll || excludes.isExcluded(path, method);
@@ -72,14 +70,10 @@ let authorizeToken = async function (ctx, token) {
const tokenFormatRegexp = /^(\{{0,1}([0-9a-fA-F]){8}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){12}\}{0,1})$/g;
if (token && tokenFormatRegexp.test(token)) {
try {
- const axyRes = await ctx.fs.dc.models.UserToken.findOne({
- where: {
- token: token,
- expired: { $gte: moment().format('YYYY-MM-DD HH:mm:ss') }
- }
- });
- const { userInfo, expired } = axyRes;
- if (!expired || moment().valueOf() <= moment(expired).valueOf()) {
+ const expired = await ctx.redis.hget(token, 'expired');
+
+ if (expired && moment().valueOf() <= moment(expired).valueOf()) {
+ const userInfo = JSON.parse(await ctx.redis.hget(token, 'userInfo'));
rslt = {
'authorized': userInfo.authorized,
'resources': (userInfo || {}).resources || [],
@@ -112,13 +106,14 @@ let isResourceAvailable = function (resources, options) {
return !authCode || (resources || []).some(code => code === authCode);
};
-function factory(app, opts) {
- return async function auth(ctx, next) {
+function factory (app, opts) {
+ return async function auth (ctx, next) {
const { path, method, header, query } = ctx;
ctx.fs.logger.log('[AUTH] start', path, method);
ctx.fs.api = ctx.fs.api || {};
ctx.fs.port = opts.port;
ctx.redis = app.redis;
+ ctx.redisTools = app.redisTools;
let error = null;
if (path) {
if (!isPathExcluded(opts, path, method)) {
diff --git a/code/VideoAccess-VCMP/api/app/lib/routes/nvr/index.js b/code/VideoAccess-VCMP/api/app/lib/routes/nvr/index.js
new file mode 100644
index 0000000..ed6654c
--- /dev/null
+++ b/code/VideoAccess-VCMP/api/app/lib/routes/nvr/index.js
@@ -0,0 +1,11 @@
+'use strict';
+
+const nvr = require('../../controllers/nvr');
+
+module.exports = function (app, router, opts) {
+ app.fs.api.logAttr['POST/nvr'] = { content: '添加/修改nvr', visible: false };
+ router.post('/nvr', nvr.edit);
+
+ app.fs.api.logAttr['DEL/nvr'] = { content: '删除nvr', visible: false };
+ router.del('/nvr/:nvrId', nvr.del);
+};
diff --git a/code/VideoAccess-VCMP/api/app/lib/service/redis.js b/code/VideoAccess-VCMP/api/app/lib/service/redis.js
new file mode 100644
index 0000000..5e38a29
--- /dev/null
+++ b/code/VideoAccess-VCMP/api/app/lib/service/redis.js
@@ -0,0 +1,29 @@
+'use strict';
+const redis = require("ioredis")
+const moment = require('moment')
+
+module.exports = async function factory (app, opts) {
+ let client = new redis(opts.redis.port, opts.redis.host);
+
+ client.on("error", function (err) {
+ app.fs.logger.error('info', '[FS-AUTH-REDIS]', 'redis connect error.');
+ console.error("Error :", err);
+ process.exit(-1);
+ });
+
+ client.on('connect', function () {
+ console.log(`redis connect success ${opts.redis.host + ':' + opts.redis.port}`);
+ })
+
+ // 自定义方法
+ async function hdelall (key) {
+ const obj = await client.hgetall(key);
+ const hkeys = Object.keys(obj)
+ await client.hdel(key, hkeys)
+ }
+
+ app.redis = client
+ app.redisTools = {
+ hdelall,
+ }
+}
diff --git a/code/VideoAccess-VCMP/api/config.js b/code/VideoAccess-VCMP/api/config.js
index 5537608..084a131 100644
--- a/code/VideoAccess-VCMP/api/config.js
+++ b/code/VideoAccess-VCMP/api/config.js
@@ -11,13 +11,20 @@ const dev = process.env.NODE_ENV == 'development';
args.option(['p', 'port'], '启动端口');
args.option(['g', 'pg'], 'postgre服务URL');
args.option(['f', 'fileHost'], '文件中心本地化存储: WebApi 服务器地址(必填), 该服务器提供文件上传Web服务');
+args.option('redisHost', 'redisHost');
+args.option('redisPort', 'redisPort');
+args.option('redisPswd', 'redisPassword');
const flags = args.parse(process.argv);
const IOT_VIDEO_ACCESS_DB = process.env.IOT_VIDEO_ACCESS_DB || flags.pg;
const IOT_VIDEO_ACCESS_LOCAL_SVR_ORIGIN = process.env.IOT_VIDEO_ACCESS_LOCAL_SVR_ORIGIN || flags.fileHost;
-if (!IOT_VIDEO_ACCESS_DB) {
+const IOTA_REDIS_SERVER_HOST = process.env.IOTA_REDIS_SERVER_HOST || flags.redisHost || "localhost";//redis IP
+const IOTA_REDIS_SERVER_PORT = process.env.IOTA_REDIS_SERVER_PORT || flags.redisPort || "6379";//redis 端口
+const IOTA_REDIS_SERVER_PWD = process.env.IOTA_REDIS_SERVER_PWD || flags.redisPswd || "";//redis 密码
+
+if (!IOT_VIDEO_ACCESS_DB || !IOTA_REDIS_SERVER_HOST || !IOTA_REDIS_SERVER_PORT) {
console.log('缺少启动参数,异常退出');
args.showHelp();
process.exit(-1);
@@ -41,6 +48,11 @@ const product = {
entry: require('./app').entry,
opts: {
exclude: [], // 不做认证的路由,也可以使用 exclude: ["*"] 跳过所有路由
+ redis: {
+ host: IOTA_REDIS_SERVER_HOST,
+ port: IOTA_REDIS_SERVER_PORT,
+ pwd: IOTA_REDIS_SERVER_PWD
+ },
}
}
],
diff --git a/code/VideoAccess-VCMP/api/package.json b/code/VideoAccess-VCMP/api/package.json
index 43c97b7..c4d0945 100644
--- a/code/VideoAccess-VCMP/api/package.json
+++ b/code/VideoAccess-VCMP/api/package.json
@@ -5,7 +5,7 @@
"main": "server.js",
"scripts": {
"test": "set DEBUG=true&&\"node_modules/.bin/mocha\" --harmony --reporter spec app/test/*.test.js",
- "start": "set NODE_ENV=development&&node server -p 14000 -g postgres://postgres:123@10.8.30.32:5432/yinjiguanli -f http://localhost:14000",
+ "start": "set NODE_ENV=development&&node server -p 4000 -g postgres://postgres:123@10.8.30.32:5432/video_access -f http://localhost:4000",
"start:linux": "export NODE_ENV=development&&node server -p 4000 -g postgres://FashionAdmin:123456@10.8.30.39:5432/pm1",
"automate": "sequelize-automate -c sequelize-automate.config.js"
},
@@ -18,7 +18,7 @@
"crypto-js": "^4.0.0",
"file-saver": "^2.0.2",
"fs-web-server-scaffold": "^2.0.2",
- "ioredis": "^4.19.4",
+ "ioredis": "^5.0.4",
"koa-convert": "^1.2.0",
"koa-proxy": "^0.9.0",
"moment": "^2.24.0",
From 4168be60d73ae94a10af6dbf8397160318899dc8 Mon Sep 17 00:00:00 2001
From: yuan_yi <1650192445@qq.com>
Date: Fri, 29 Apr 2022 15:24:03 +0800
Subject: [PATCH 3/4] =?UTF-8?q?=E5=88=A0=E9=99=A4=20AUTH?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../api/app/lib/controllers/auth/index.js | 189 ------------------
.../api/app/lib/routes/auth/index.js | 32 ---
2 files changed, 221 deletions(-)
delete mode 100644 code/VideoAccess-VCMP/api/app/lib/controllers/auth/index.js
delete mode 100644 code/VideoAccess-VCMP/api/app/lib/routes/auth/index.js
diff --git a/code/VideoAccess-VCMP/api/app/lib/controllers/auth/index.js b/code/VideoAccess-VCMP/api/app/lib/controllers/auth/index.js
deleted file mode 100644
index 00040ce..0000000
--- a/code/VideoAccess-VCMP/api/app/lib/controllers/auth/index.js
+++ /dev/null
@@ -1,189 +0,0 @@
-'use strict';
-const Hex = require('crypto-js/enc-hex');
-const MD5 = require('crypto-js/md5');
-const moment = require('moment');
-const uuid = require('uuid');
-
-async function login(ctx, next) {
- const transaction = await ctx.fs.dc.orm.transaction();
- try {
- const models = ctx.fs.dc.models;
- const params = ctx.request.body;
- let password = Hex.stringify(MD5(params.password));
-
- const userRes = await models.User.findOne({
- where: {
- username: params.username,
- password: password,
- delete: false,
- },
- attributes: { exclude: ['password'] },
- include: [{
- attributes: ["resourceId"],
- model: models.UserResource
- }]
- });
-
- if (!userRes) {
- ctx.status = 400;
- ctx.body = {
- "message": "账号或密码错误"
- }
- } else if (!userRes.enable) {
- ctx.status = 400;
- ctx.body = { message: "该用户已被禁用" }
- } else {
- const token = uuid.v4();
-
- let userRslt = Object.assign(userRes.dataValues, {
- authorized: true,
- token: token,
- userResources: userRes.userResources.map(r => r.resourceId),
- });
-
- await models.UserToken.create({
- token: token,
- userInfo: userRslt,
- expired: moment().add(30, 'days').format()
- });
-
- ctx.status = 200;
- ctx.body = userRslt;
- }
- await transaction.commit();
- } catch (error) {
- await transaction.rollback();
- ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
- ctx.status = 400;
- ctx.body = {
- "message": "登录失败"
- }
- }
-}
-
-/**
- * 微信小程序登录
- * @@requires.body {phone-手机号, password-密码} ctx
- */
-async function wxLogin(ctx, next) {
- const transaction = await ctx.fs.dc.orm.transaction();
- try {
- const models = ctx.fs.dc.models;
- const params = ctx.request.body;
- let password = Hex.stringify(MD5(params.password));
- const userRes = await models.User.findOne({
- where: {
- phone: params.phone,
- password: password,
- delete: false,
- },
- attributes: { exclude: ['password'] }
- });
- if (!userRes) {
- ctx.status = 400;
- ctx.body = { message: "手机号或密码错误" }
- } else if (!userRes.enable) {
- ctx.status = 400;
- ctx.body = { message: "该用户已被禁用" }
- } else {
- const token = uuid.v4();
- //获取用户关注区域信息
- const departmentRes = await models.Department.findOne({ where: { id: userRes.departmentId } });
- let attentionRegion = departmentRes;
- while (attentionRegion.dependence && attentionRegion.type != 1) {
- const departmentParent = await models.Department.findOne({ where: { id: attentionRegion.dependence } });
- attentionRegion = {
- ...departmentParent.dataValues,
- nextRegin: attentionRegion
- }
- }
- //获取用户权限信息
- const resourceRes = await models.UserResource.findAll({
- where: {
- userId: userRes.id
- },
- include: [{
- model: models.Resource,
- attributes: ['code', 'name'],
- }],
- attributes: []
- });
- let userRslt = Object.assign({
- authorized: true,
- token: token,
- ...userRes.dataValues
- });
- await models.UserToken.create({
- token: token,
- userInfo: userRslt,
- expired: moment().add(30, 'day').format('YYYY-MM-DD HH:mm:ss')
- }, { transaction: transaction });
- ctx.status = 200;
- ctx.body = Object.assign({
- ...userRslt,
- userRegionType: departmentRes.type,//1-市级,2-区县级,3-乡镇级,4-村级
- attentionRegion: attentionRegion,
- resources: resourceRes.map(r => r.resource)
- });
- }
- await transaction.commit();
- } catch (error) {
- await transaction.rollback();
- ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
- ctx.status = 400;
- ctx.body = {
- "message": "登录失败"
- }
- }
-}
-
-async function logout(ctx) {
- try {
- const { token, code } = ctx.request.body;
- const models = ctx.fs.dc.models;
-
- await models.UserToken.destroy({
- where: {
- token: token,
- },
- });
-
- ctx.status = 204;
- } catch (error) {
- ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
- ctx.status = 400;
- ctx.body = {
- "message": "登出失败"
- }
- }
-}
-
-/**
- * 微信小程序登出
- * @request.body {token-用户登录Token} ctx
- */
-async function wxLogout(ctx) {
- try {
- const { token } = ctx.request.body;
- const models = ctx.fs.dc.models;
- await models.UserToken.destroy({
- where: {
- token: token,
- },
- });
- ctx.status = 204;
- } catch (error) {
- ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
- ctx.status = 400;
- ctx.body = {
- "message": "登出失败"
- }
- }
-}
-
-module.exports = {
- login,
- wxLogin,
- logout,
- wxLogout
-};
\ No newline at end of file
diff --git a/code/VideoAccess-VCMP/api/app/lib/routes/auth/index.js b/code/VideoAccess-VCMP/api/app/lib/routes/auth/index.js
deleted file mode 100644
index b0de650..0000000
--- a/code/VideoAccess-VCMP/api/app/lib/routes/auth/index.js
+++ /dev/null
@@ -1,32 +0,0 @@
-'use strict';
-
-const auth = require('../../controllers/auth');
-
-module.exports = function (app, router, opts) {
- /**
- * @api {Post} login 登录.
- * @apiVersion 1.0.0
- * @apiGroup Auth
- */
- app.fs.api.logAttr['POST/login'] = { content: '登录', visible: true };
- router.post('/login', auth.login);
-
- /**
- * @api {POST} wxLogin 微信小程序登录.(使用手机号、密码登录)
- * @apiVersion 1.0.0
- * @apiGroup Auth
- */
- app.fs.api.logAttr['POST/wxLogin'] = { content: '微信小程序登录', visible: true };
- router.post('/wxLogin', auth.wxLogin);
-
- app.fs.api.logAttr['PUT/logout'] = { content: '登出', visible: false };
- router.put('/logout', auth.logout);
-
- /**
- * @api {PUT} wxLogout 微信小程序登出
- * @apiVersion 1.0.0
- * @apiGroup Auth
- */
- app.fs.api.logAttr['PUT/wxLogout'] = { content: '登出', visible: false };
- router.put('/wxLogout', auth.wxLogout);
-};
From c3a82d8979bc68ecbce71eee65bbb0316f23ac6d Mon Sep 17 00:00:00 2001
From: deartibers <947466799@qq.com>
Date: Fri, 29 Apr 2022 16:56:17 +0800
Subject: [PATCH 4/4] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=91=84=E5=83=8F?=
=?UTF-8?q?=E5=A4=B4=E9=A1=B5=E9=9D=A2?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../client/assets/images/background/Reset.png | Bin 0 -> 350 bytes
.../assets/images/background/cascade.png | Bin 0 -> 4350 bytes
.../assets/images/background/formchoose.png | Bin 0 -> 354 bytes
.../client/assets/images/background/ipc.png | Bin 0 -> 3721 bytes
.../client/assets/images/background/nvr.png | Bin 0 -> 6172 bytes
.../client/assets/images/background/test.png | Bin 0 -> 125 bytes
.../assets/images/background/topchoose.png | Bin 0 -> 1907 bytes
.../client/assets/images/background/ysy.png | Bin 0 -> 5150 bytes
.../components/cameraModal.jsx | 202 ++++++++++++++++++
.../equipmentWarehouse/containers/camera.jsx | 45 ++--
10 files changed, 232 insertions(+), 15 deletions(-)
create mode 100644 code/VideoAccess-VCMP/web/client/assets/images/background/Reset.png
create mode 100644 code/VideoAccess-VCMP/web/client/assets/images/background/cascade.png
create mode 100644 code/VideoAccess-VCMP/web/client/assets/images/background/formchoose.png
create mode 100644 code/VideoAccess-VCMP/web/client/assets/images/background/ipc.png
create mode 100644 code/VideoAccess-VCMP/web/client/assets/images/background/nvr.png
create mode 100644 code/VideoAccess-VCMP/web/client/assets/images/background/test.png
create mode 100644 code/VideoAccess-VCMP/web/client/assets/images/background/topchoose.png
create mode 100644 code/VideoAccess-VCMP/web/client/assets/images/background/ysy.png
create mode 100644 code/VideoAccess-VCMP/web/client/src/sections/equipmentWarehouse/components/cameraModal.jsx
diff --git a/code/VideoAccess-VCMP/web/client/assets/images/background/Reset.png b/code/VideoAccess-VCMP/web/client/assets/images/background/Reset.png
new file mode 100644
index 0000000000000000000000000000000000000000..daa37b85591dab1173bc48d6be6a42457995be3a
GIT binary patch
literal 350
zcmV-k0iphhP)
Px$7)eAyR5(wS(!DE&VHC&l&r`_D#9;6;2rESv|AGnm1B5b|%nD&K8zdGJ$)JqN
zX7rMk3}PUQqA1eQb?5Hxd3th|`{kVP`JL-rzmmV`#Vx+kh*p&FgtuJX?`tF?YH)&m
zv|$hpDff;G?BFhIkb(6$MH9|(gzHq)j}f$E8E1LL4qW0HyLiD?ZoJwAMU%Nu&J-iD
zhu2gb&v|?m;BDMvAp?tNuk<~JX-ovzoaEQ>QKdrOV
literal 0
HcmV?d00001
diff --git a/code/VideoAccess-VCMP/web/client/assets/images/background/cascade.png b/code/VideoAccess-VCMP/web/client/assets/images/background/cascade.png
new file mode 100644
index 0000000000000000000000000000000000000000..4ea4de5a0507153c5bdc5de6040e2ba3f93a4665
GIT binary patch
literal 4350
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
literal 0
HcmV?d00001
diff --git a/code/VideoAccess-VCMP/web/client/assets/images/background/nvr.png b/code/VideoAccess-VCMP/web/client/assets/images/background/nvr.png
new file mode 100644
index 0000000000000000000000000000000000000000..d9cb1e3ec45b4fe3d749920fb3296fd2bb57c2c2
GIT binary patch
literal 6172
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
literal 0
HcmV?d00001
diff --git a/code/VideoAccess-VCMP/web/client/assets/images/background/topchoose.png b/code/VideoAccess-VCMP/web/client/assets/images/background/topchoose.png
new file mode 100644
index 0000000000000000000000000000000000000000..44c81652e4b242600f880d82b2262a2a8af13529
GIT binary patch
literal 1907
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
M07*qoM6N<$f-su=82|tP
literal 0
HcmV?d00001
diff --git a/code/VideoAccess-VCMP/web/client/src/sections/equipmentWarehouse/components/cameraModal.jsx b/code/VideoAccess-VCMP/web/client/src/sections/equipmentWarehouse/components/cameraModal.jsx
new file mode 100644
index 0000000..0d19d87
--- /dev/null
+++ b/code/VideoAccess-VCMP/web/client/src/sections/equipmentWarehouse/components/cameraModal.jsx
@@ -0,0 +1,202 @@
+import React, { useState ,useRef} from 'react'
+import { Modal,Form,Row,Col,Spin,Notification,Button } from '@douyinfe/semi-ui';
+import { IconChevronLeft,IconChevronRight } from '@douyinfe/semi-icons';
+function nvrModal(props){
+ const {modalName,visible,close}=props
+ const form = useRef();
+ // const [visible, setVisible] = useState(false);//是否显示弹框
+ const [isloading,setloading] = useState(false);//是否显示loading
+ const [loadingTip,setloadingTip] = useState('获取中...请稍后...');//loading tip的值
+ const [step,setstep] = useState(0)//第几步
+ const [okText,setokText] = useState('测试校验')//ok弹框text 右边
+ const [cancelText,setcancelText] = useState('取消')//取消弹框text 左边
+ const opts ={//添加完成确认后通知
+ title:'Hi',
+ content:'添加成功',
+ duration:3
+ }
+ const [clickNum,setclickNum] = useState(1);//点击的第几个
+ const cameraList=[//循环摄像头列表
+ {
+ id:1,
+ img:'/assets/images/background/ysy.png',
+ title:'萤石云平台摄像头',
+ text:'通过萤石云平台rtmp地址配置完成推流的平台摄像头。'
+ },{
+ id:2,
+ img:'/assets/images/background/nvr.png',
+ title:'NVR摄像头',
+ text:'通过连接NVR(网络硬盘录像机)进行视频流推送的摄像头'
+ },{
+ id:3,
+ img:'/assets/images/background/ipc.png',
+ title:'IPC网络摄像头',
+ text:'通过网络与监控设备直连完成视频流推送的摄像头设备'
+ },{
+ id:4,
+ img:'/assets/images/background/cascade.png',
+ title:'级联摄像头',
+ text:'通过GB/T28181协议级联的平台摄像头,常用于平台对接推送'
+ },
+ ]
+ const [showcameraList,setcameraList]=useState(cameraList.slice(0,3));
+ function handleOk() {//点击弹框确定 右边按钮
+ if(step==0){
+ form.current.validate()
+ .then(values=>{//表单校验成功
+ setloading(true);
+ setTimeout(() => {
+ setloadingTip('...接受成功')
+ setTimeout(()=>{
+ setloadingTip('已完成')
+ setTimeout(() => {
+ setstep(1);
+ setokText('确认');
+ setcancelText('上一步');
+ setloading(false);
+ }, 2000);
+ },2000)
+ }, 2000);
+ })
+ .catch(errors=>{//表单校验失败
+ console.log('errors',errors);
+ })
+
+ }
+ else{
+ Notification.success(opts)
+ // setVisible(false);
+ close();
+ }
+ }
+ function handleAfterClose(){//在关闭之后
+ setstep(0);
+ setokText('测试校验');
+ setcancelText('取消');
+ }
+ function handleCancel() {//点击弹框取消 左边按钮
+ if(step==0){
+ // setVisible(false);
+ close();
+ }
+ else{
+ setstep(0);
+ setokText('测试校验');
+ setcancelText('取消');
+ }
+ }
+ function handleLocation(){//高德经纬度
+ console.log('handleLocationhandleLocation');
+ }
+ function handleChoose(id){//选择摄像头接入类型
+ setclickNum(id);
+ }
+ function turnLift(){
+ setcameraList(cameraList.slice(0,3))
+ }
+ function turnRight(){
+ setcameraList(cameraList.slice(1,4))
+ }
+ return (
+ <>
+ {/* {modalName=='add'?'添加NVR':'修改'}
*/}
+
+
+
+
接入类型
+
+
+
+ {showcameraList.map((item,index)=>(
+
handleChoose(item.id)}>
+
+

+
+
{item.title}
+
{item.text}
+ {clickNum===item.id?
+

+
:''}
+
+ ))}
+
+
+
+
+
+
配置属性
+
+
+

+ 重置
+
+
+

+ 测试
+
+
+
+
+
+ >
+ );
+}
+
+export default nvrModal
\ No newline at end of file
diff --git a/code/VideoAccess-VCMP/web/client/src/sections/equipmentWarehouse/containers/camera.jsx b/code/VideoAccess-VCMP/web/client/src/sections/equipmentWarehouse/containers/camera.jsx
index b735465..050cba6 100644
--- a/code/VideoAccess-VCMP/web/client/src/sections/equipmentWarehouse/containers/camera.jsx
+++ b/code/VideoAccess-VCMP/web/client/src/sections/equipmentWarehouse/containers/camera.jsx
@@ -2,57 +2,72 @@ import React, { useState, useEffect } from "react";
import { connect } from "react-redux";
import { Button, Form, Input, Row, Table } from "@douyinfe/semi-ui";
import "../style.less";
+import CameraModal from "../components/cameraModal";
const CameraHeader = (props) => {
+ const [cameraModal,setCameraModal] = useState(false)
+ const [modalName,setModalName] = useState('')
return (
<>
摄像头管理
- 对NVR(网络硬盘录像机)设备节点的管理
+ 对监控摄像设备设备添加、修改、删除的硬件管理页面。
{
+ setModalName('add')
+ setCameraModal(true);
+ }}
>
- {/* */}
+ 添加摄像头
+ {cameraModal?
+ {
+ setCameraModal(false);
+ // setEditData(null)
+ }}
+ modalName={modalName} />:''}
>
);
};