diff --git a/code/VideoAccess-VCMP/api/.vscode/launch.json b/code/VideoAccess-VCMP/api/.vscode/launch.json index bfa084a..d2b588d 100644 --- a/code/VideoAccess-VCMP/api/.vscode/launch.json +++ b/code/VideoAccess-VCMP/api/.vscode/launch.json @@ -20,10 +20,11 @@ "--redisPort 6379", "--axyApiUrl http://127.0.0.1:4100", "--iotAuthApi http://127.0.0.1:4200", - "--iotVideoServerUrl http://221.230.55.27:8081", "--godUrl https://restapi.amap.com/v3", "--godKey 21c2d970e1646bb9a795900dd00093ce", "--mqttVideoServer mqtt://10.8.30.71:30883", + "--iotVideoServerUrl http://221.230.55.27:8081", + // "--iotVideoServerUrl http://10.8.30.59:8080", "--cameraPlayWsHost ws://221.230.55.27:8081", "--cameraPlayHttpFlvHost http://221.230.55.27:2020", "--cameraPlayHlsHost http://221.230.55.27:8081", diff --git a/code/VideoAccess-VCMP/api/app/lib/controllers/camera/create.js b/code/VideoAccess-VCMP/api/app/lib/controllers/camera/create.js index e3e19d1..f6394c9 100644 --- a/code/VideoAccess-VCMP/api/app/lib/controllers/camera/create.js +++ b/code/VideoAccess-VCMP/api/app/lib/controllers/camera/create.js @@ -120,7 +120,11 @@ async function getNvrSteam (ctx) { attributes: ['id', 'name', 'serialNo', 'cloudControl'], where: { nvrId: nvrRes.id - } + }, + include: [{ + model: models.CameraRemark, + attributes: ['remark'] + }], }) const cameraRes = await getGbCameraLevel3ByStreamId({ streamId }) @@ -304,8 +308,8 @@ async function createIpcCamera (ctx) { const gbCameraRes = await verifyIpcInfo({ serialNo }) - storageData.sip = gbCameraRes.dataValues.sipip - storageData.gbId = gbCameraRes.dataValues.id + storageData.sip = gbCameraRes.sipip + storageData.gbId = gbCameraRes.id if (handleCameraId) { await models.Camera.update(storageData, { @@ -417,7 +421,11 @@ async function getCascadeSteam (ctx) { attributes: ['id', 'name', 'serialNo'], where: { serialNo: { $in: allStreamid } - } + }, + include: [{ + model: models.CameraRemark, + attributes: ['remark'] + }], }) for (let c of cameraRes) { diff --git a/code/VideoAccess-VCMP/api/app/lib/controllers/camera/index.js b/code/VideoAccess-VCMP/api/app/lib/controllers/camera/index.js index 0e8840c..f8492cb 100644 --- a/code/VideoAccess-VCMP/api/app/lib/controllers/camera/index.js +++ b/code/VideoAccess-VCMP/api/app/lib/controllers/camera/index.js @@ -31,6 +31,9 @@ async function getCameraProject (ctx, next) { }, { model: models.SecretYingshi, attributes: ['token'] + }, { + model: models.CameraRemark, + attributes: ['remark'] }], distinct: true } @@ -307,6 +310,13 @@ async function del (ctx) { transaction }) + await models.CameraRemark.destroy({ + where: { + cameraId: cameraId + }, + transaction + }) + await models.Camera.destroy({ where: { id: cameraId @@ -490,6 +500,50 @@ async function getKind (ctx) { } } +async function remark (ctx) { + const transaction = await ctx.fs.dc.orm.transaction(); + let errMsg = undefined + try { + const { models } = ctx.fs.dc + const { cameraId, remark, } = ctx.request.body; + + const cameraRes = await models.Camera.findOne({ + where: { + id: cameraId + } + }) + + if (!cameraRes) { + errMsg = '摄像头不存在' + } + + await models.CameraRemark.destroy({ + where: { + cameraId + }, + transaction + }) + await models.CameraRemark.bulkCreate(remark.map(r => { + return { + cameraId, + remark: r + } + }), { + 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 = { + message: errMsg + } + } +} + module.exports = { getCameraProject, getCamera, @@ -500,4 +554,5 @@ module.exports = { cameraExport, getAbility, getKind, + remark, }; \ No newline at end of file diff --git a/code/VideoAccess-VCMP/api/app/lib/controllers/nvr/index.js b/code/VideoAccess-VCMP/api/app/lib/controllers/nvr/index.js index 5751102..7af7c58 100644 --- a/code/VideoAccess-VCMP/api/app/lib/controllers/nvr/index.js +++ b/code/VideoAccess-VCMP/api/app/lib/controllers/nvr/index.js @@ -299,7 +299,8 @@ async function detail (ctx) { } const corUser = await ctx.app.fs.authRequest.get(`user/${nvrRes.createUserId}/message`, { query: { token } }) - const serverDRes = (await ctx.app.fs.videoServerRequest.post(`gateway/plugins`) || '') + const serverDRes = (await ctx.app.fs.videoServerRequest.post(`gb28181/api/plugins`) || '') + // const serverDRes = (await ctx.app.fs.videoServerRequest.post(`gateway/plugins`) || '') const serverDArr = JSON.parse(serverDRes.replace(/'/g, '"')) || [] const serverDConfig = serverDArr.find(s => s.Name == 'GB28181') let serveD = {} diff --git a/code/VideoAccess-VCMP/api/app/lib/index.js b/code/VideoAccess-VCMP/api/app/lib/index.js index 98d7560..b8a05ed 100644 --- a/code/VideoAccess-VCMP/api/app/lib/index.js +++ b/code/VideoAccess-VCMP/api/app/lib/index.js @@ -1,5 +1,7 @@ 'use strict'; +const fs = require('fs'); +const path = require('path'); const utils = require('./utils') const routes = require('./routes'); const redisConnect = require('./service/redis') @@ -22,7 +24,7 @@ module.exports.entry = function (app, router, opts) { redisConnect(app, opts) socketConect(app, opts) mqttVideoServer(app, opts) - + // 实例其他平台请求方法 paasRequest(app, opts) @@ -31,7 +33,7 @@ module.exports.entry = function (app, router, opts) { // 定时任务 schedule(app, opts) - + // 鉴权中间件 router.use(authenticator(app, opts)); @@ -51,4 +53,10 @@ module.exports.models = function (dc) { // dc = { orm: Sequelize对象, ORM: Seq require('./models/secret_yingshi')(dc); require('./models/gb_camera')(dc); require('./models/ax_project')(dc); + require('./models/camera_remark')(dc); + + // TODO 模型关系摘出来 初始化之后再定义关系才行 + // fs.readdirSync(path.join(__dirname, '/models')).forEach((filename) => { + // require(`./models/${filename}`)(dc) + // }); }; diff --git a/code/VideoAccess-VCMP/api/app/lib/models/camera_remark.js b/code/VideoAccess-VCMP/api/app/lib/models/camera_remark.js new file mode 100644 index 0000000..7c3a994 --- /dev/null +++ b/code/VideoAccess-VCMP/api/app/lib/models/camera_remark.js @@ -0,0 +1,52 @@ +/* eslint-disable*/ +'use strict'; + +module.exports = dc => { + const DataTypes = dc.ORM; + const sequelize = dc.orm; + const CameraRemark = sequelize.define("cameraRemark", { + id: { + type: DataTypes.INTEGER, + allowNull: false, + defaultValue: null, + comment: null, + primaryKey: true, + field: "id", + autoIncrement: true, + unique: "camera_remark_id_uindex" + }, + cameraId: { + type: DataTypes.INTEGER, + allowNull: false, + defaultValue: null, + comment: null, + primaryKey: false, + field: "camera_id", + autoIncrement: false, + references: { + key: "id", + model: "camera" + } + }, + remark: { + type: DataTypes.STRING, + allowNull: false, + defaultValue: null, + comment: null, + primaryKey: false, + field: "remark", + autoIncrement: false + } + }, { + tableName: "camera_remark", + comment: "", + indexes: [] + }); + dc.models.CameraRemark = CameraRemark; + + const Camera = dc.models.Camera; + CameraRemark.belongsTo(Camera, { foreignKey: 'cameraId', targetKey: 'id' }); + Camera.hasMany(CameraRemark, { foreignKey: 'cameraId', sourceKey: 'id' }); + + return CameraRemark; +}; \ No newline at end of file diff --git a/code/VideoAccess-VCMP/api/app/lib/routes/camera/index.js b/code/VideoAccess-VCMP/api/app/lib/routes/camera/index.js index f7450e4..2a3ce05 100644 --- a/code/VideoAccess-VCMP/api/app/lib/routes/camera/index.js +++ b/code/VideoAccess-VCMP/api/app/lib/routes/camera/index.js @@ -70,4 +70,7 @@ module.exports = function (app, router, opts) { app.fs.api.logAttr['GET/camera/kind'] = { content: '获取摄像头种类列表', visible: false }; router.get('/camera/kind', camera.getKind); + + app.fs.api.logAttr['POST/camera/remark'] = { content: '编辑摄像头备注', visible: false }; + router.post('/camera/remark', camera.remark); }; diff --git a/code/VideoAccess-VCMP/api/app/lib/routes/index.js b/code/VideoAccess-VCMP/api/app/lib/routes/index.js index 2d6a9f8..39cbc75 100644 --- a/code/VideoAccess-VCMP/api/app/lib/routes/index.js +++ b/code/VideoAccess-VCMP/api/app/lib/routes/index.js @@ -4,14 +4,14 @@ const path = require('path'); const fs = require('fs'); module.exports = function (app, router, opts) { - fs.readdirSync(__dirname).forEach((filename) => { - if (filename.indexOf('.') !== 0 &&fs.lstatSync(path.join(__dirname, filename)).isDirectory()) { - fs.readdirSync(path.join(__dirname, filename)).forEach((api) => { - if (api.indexOf('.') == 0 || api.indexOf('.js') == -1) return; - require(`./${filename}/${api}`)(app, router, opts); - }); - } - }); + fs.readdirSync(__dirname).forEach((filename) => { + if (filename.indexOf('.') !== 0 && fs.lstatSync(path.join(__dirname, filename)).isDirectory()) { + fs.readdirSync(path.join(__dirname, filename)).forEach((api) => { + if (api.indexOf('.') == 0 || api.indexOf('.js') == -1) return; + require(`./${filename}/${api}`)(app, router, opts); + }); + } + }); - return router; + return router; }; diff --git a/code/VideoAccess-VCMP/api/app/lib/schedule/freshYingshiMsg.js b/code/VideoAccess-VCMP/api/app/lib/schedule/freshYingshiMsg.js index 72945c6..099dc6a 100644 --- a/code/VideoAccess-VCMP/api/app/lib/schedule/freshYingshiMsg.js +++ b/code/VideoAccess-VCMP/api/app/lib/schedule/freshYingshiMsg.js @@ -97,11 +97,12 @@ module.exports = function (app, opts) { { // interval: '0 0 4 */1 *', // interval: '*/30 * * * *', - interval: '0 34 5 * * *', + interval: '0 34 5 1 * *', immediate: true, }, async () => { try { + const startTime = moment() const { models } = app.fs.dc const { token4yingshi, getYingshiPlayUrl } = app.fs.utils const secretRes = await models.SecretYingshi.findAll() @@ -137,6 +138,7 @@ module.exports = function (app, opts) { } } } + console.info(`萤石设备 ${deviceList.length} 播放地址更新查询用时 ${moment().diff(startTime, 'seconds')} s`) } catch (error) { app.fs.logger.error(`sechedule: freshYingshiPlayUrl, error: ${error}`); } diff --git a/code/VideoAccess-VCMP/api/app/lib/schedule/index.js b/code/VideoAccess-VCMP/api/app/lib/schedule/index.js index e97a1fc..d28db8c 100644 --- a/code/VideoAccess-VCMP/api/app/lib/schedule/index.js +++ b/code/VideoAccess-VCMP/api/app/lib/schedule/index.js @@ -10,7 +10,7 @@ module.exports = async function (app, opts) { interval, immediate }, callback) => { const j = nodeSchedule.scheduleJob(interval, callback); - if (immediate) { + if (immediate && !opts.dev) { setTimeout(callback, 0) } return j; diff --git a/code/VideoAccess-VCMP/api/app/lib/utils/token4yingshi.js b/code/VideoAccess-VCMP/api/app/lib/utils/token4yingshi.js index 0bacd53..7852da4 100644 --- a/code/VideoAccess-VCMP/api/app/lib/utils/token4yingshi.js +++ b/code/VideoAccess-VCMP/api/app/lib/utils/token4yingshi.js @@ -81,8 +81,8 @@ module.exports = function (app, opts) { } const typeMap = { // live: 1, - cloud: 2, - local: 3 + local: 2, + cloud: 3 } let playUrl = { diff --git a/code/VideoAccess-VCMP/api/config.js b/code/VideoAccess-VCMP/api/config.js index 9c4befd..f23c796 100644 --- a/code/VideoAccess-VCMP/api/config.js +++ b/code/VideoAccess-VCMP/api/config.js @@ -81,6 +81,7 @@ const product = { }, { entry: require('./app').entry, opts: { + dev, exclude: [], // 不做认证的路由,也可以使用 exclude: ["*"] 跳过所有路由 redis: { host: IOTA_REDIS_SERVER_HOST, diff --git a/code/VideoAccess-VCMP/api/sequelize-automate.config.js b/code/VideoAccess-VCMP/api/sequelize-automate.config.js index 185e650..497337f 100644 --- a/code/VideoAccess-VCMP/api/sequelize-automate.config.js +++ b/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_ability_bind'], // 指定生成哪些表的 models,如 ['user', 'user_post'];如果为 null,则忽略改属性 + tables: ['camera_remark'], // 指定生成哪些表的 models,如 ['user', 'user_post'];如果为 null,则忽略改属性 skipTables: [], // 指定跳过哪些表的 models,如 ['user'];如果为 null,则忽略改属性 tsNoCheck: false, // 是否添加 `@ts-nocheck` 注释到 models 文件中 ignorePrefix: [], // 生成的模型名称忽略的前缀,因为 项目中有以下表名是以 t_ 开头的,在实际模型中不需要, 可以添加多个 [ 't_data_', 't_',] ,长度较长的 前缀放前面 diff --git a/code/VideoAccess-VCMP/script/0.0.1/0_init_db.sql b/code/VideoAccess-VCMP/script/0.0.1/0_init_db.sql index 7506eb6..d8d335c 100644 --- a/code/VideoAccess-VCMP/script/0.0.1/0_init_db.sql +++ b/code/VideoAccess-VCMP/script/0.0.1/0_init_db.sql @@ -12,7 +12,7 @@ Target Server Version : 90515 File Encoding : 65001 - Date: 20/06/2022 14:41:32 + Date: 06/07/2022 11:45:25 */ @@ -38,6 +38,17 @@ MAXVALUE 9223372036854775807 START 1 CACHE 1; +-- ---------------------------- +-- Sequence structure for camera_remark_id_seq +-- ---------------------------- +DROP SEQUENCE IF EXISTS "public"."camera_remark_id_seq"; +CREATE SEQUENCE "public"."camera_remark_id_seq" +INCREMENT 1 +MINVALUE 1 +MAXVALUE 9223372036854775807 +START 1 +CACHE 1; + -- ---------------------------- -- Sequence structure for gbCamera_id_seq -- ---------------------------- @@ -151,7 +162,8 @@ CREATE TABLE "public"."camera" ( "model" varchar(128) COLLATE "pg_catalog"."default", "kind_id" int4, "yingshi_secret_id" int4, - "gb_id" int4 + "gb_id" int4, + "top_serial_no" varchar(128) COLLATE "pg_catalog"."default" ) ; COMMENT ON COLUMN "public"."camera"."type" IS '设备类型:yingshi - 萤石;nvr - NVR摄像头;ipc - IPC 网络摄像头;cascade - 级联摄像头'; @@ -171,6 +183,7 @@ COMMENT ON COLUMN "public"."camera"."forbidden" IS '是否禁用'; COMMENT ON COLUMN "public"."camera"."recycle_time" IS '放入回收站时间'; COMMENT ON COLUMN "public"."camera"."delete" IS '是否彻底删除'; COMMENT ON COLUMN "public"."camera"."model" IS '型号'; +COMMENT ON COLUMN "public"."camera"."top_serial_no" IS 'gb设备 level=0 的 steamid '; -- ---------------------------- -- Table structure for camera_ability @@ -221,6 +234,17 @@ INSERT INTO "public"."camera_kind" VALUES (1, '枪机'); INSERT INTO "public"."camera_kind" VALUES (2, '球机'); INSERT INTO "public"."camera_kind" VALUES (1314, '其他'); +-- ---------------------------- +-- Table structure for camera_remark +-- ---------------------------- +DROP TABLE IF EXISTS "public"."camera_remark"; +CREATE TABLE "public"."camera_remark" ( + "id" int4 NOT NULL DEFAULT nextval('camera_remark_id_seq'::regclass), + "camera_id" int4 NOT NULL, + "remark" varchar(256) COLLATE "pg_catalog"."default" NOT NULL +) +; + -- ---------------------------- -- Table structure for gbCamera -- ---------------------------- @@ -240,9 +264,11 @@ CREATE TABLE "public"."gbCamera" ( "Sipip" varchar(255) COLLATE "pg_catalog"."default", "ipctype" varchar(255) COLLATE "pg_catalog"."default", "registerTime" varchar(255) COLLATE "pg_catalog"."default" DEFAULT ''::character varying, - "updateTime" varchar(255) COLLATE "pg_catalog"."default" + "updateTime" varchar(255) COLLATE "pg_catalog"."default", + "playUrl" jsonb ) ; +COMMENT ON COLUMN "public"."gbCamera"."playUrl" IS '播放地址集合'; -- ---------------------------- -- Table structure for nvr @@ -286,9 +312,9 @@ CREATE TABLE "public"."secret_yingshi" ( -- ---------------------------- -- Records of secret_yingshi -- ---------------------------- -INSERT INTO "public"."secret_yingshi" VALUES (3, 'd0704fb9d5d14a6682c1c1d592c12512', '93d023269495b86be62cdfdcf34a6cd1', 'at.67z1cfi515oyuu8p4wtgnr6e8dd7qwe4-6bg4tycrsw-0evcx34-xh1ks80et', '1656293969344'); -INSERT INTO "public"."secret_yingshi" VALUES (1, '5d16a667e1c2423d9d0d634f781810b4', '0cc4e1ec4e6a53ea3dabeb09cd5f468b', 'at.4p2f551n58eb9cjdd8vr4uuh1j4ozdjn-9gvmswjpa7-1y5juuk-gochbasv0', '1656294553279'); -INSERT INTO "public"."secret_yingshi" VALUES (2, '3ea2b502f6804d64b43e4cb3d135665c', '331c85c5b7ce76179f6eb7dccb8aeb27', 'at.9q4woxj71nkmckb9d2xe1yoycc81fd4l-2a45fk76s4-0qvuhi2-j5bvk57lp', '1656293899796'); +INSERT INTO "public"."secret_yingshi" VALUES (2, '3ea2b502f6804d64b43e4cb3d135665c', '331c85c5b7ce76179f6eb7dccb8aeb27', 'at.culo424j9drz8atx9uikeq5e2mzkhnhx-490csubqrh-1o3hcaz-szuvfax6f', '1657681218109'); +INSERT INTO "public"."secret_yingshi" VALUES (3, 'd0704fb9d5d14a6682c1c1d592c12512', '93d023269495b86be62cdfdcf34a6cd1', 'at.1y2mk5fh2myswbkp017pmvdi0v1zbdrw-5j39z78wsp-0x4vi2j-a3mxjlt32', '1657683606281'); +INSERT INTO "public"."secret_yingshi" VALUES (1, '5d16a667e1c2423d9d0d634f781810b4', '0cc4e1ec4e6a53ea3dabeb09cd5f468b', 'at.9gq3xwwbazkk33y177ucqtk936bmd0sz-41x4xf24sh-1cn7x82-4lf7davve', '1657681803201'); -- ---------------------------- -- Table structure for t_upload_comm_http @@ -326,14 +352,18 @@ INSERT INTO "public"."vender" VALUES (1314, '其他'); -- ---------------------------- ALTER SEQUENCE "public"."camera_ability_bind_id_seq" OWNED BY "public"."camera_ability_bind"."id"; -SELECT setval('"public"."camera_ability_bind_id_seq"', 35, true); -- ---------------------------- -- Alter sequences owned by -- ---------------------------- ALTER SEQUENCE "public"."camera_id_seq" OWNED BY "public"."camera"."id"; -SELECT setval('"public"."camera_id_seq"', 314, true); + +-- ---------------------------- +-- Alter sequences owned by +-- ---------------------------- +ALTER SEQUENCE "public"."camera_remark_id_seq" +OWNED BY "public"."camera_remark"."id"; -- ---------------------------- -- Alter sequences owned by @@ -343,14 +373,12 @@ SELECT setval('"public"."gbCamera_id_seq"', 2, false); -- ---------------------------- -- Alter sequences owned by -- ---------------------------- -SELECT setval('"public"."gb_id_seq"', 38, true); -- ---------------------------- -- Alter sequences owned by -- ---------------------------- ALTER SEQUENCE "public"."nvr_id_seq" OWNED BY "public"."nvr"."id"; -SELECT setval('"public"."nvr_id_seq"', 69, true); -- ---------------------------- -- Alter sequences owned by @@ -431,10 +459,27 @@ CREATE UNIQUE INDEX "camera_kind_id_uindex" ON "public"."camera_kind" USING btre -- ---------------------------- ALTER TABLE "public"."camera_kind" ADD CONSTRAINT "camera_kind_pk" PRIMARY KEY ("id"); +-- ---------------------------- +-- Indexes structure for table camera_remark +-- ---------------------------- +CREATE UNIQUE INDEX "camera_remark_id_uindex" ON "public"."camera_remark" USING btree ( + "id" "pg_catalog"."int4_ops" ASC NULLS LAST +); + +-- ---------------------------- +-- Primary Key structure for table camera_remark +-- ---------------------------- +ALTER TABLE "public"."camera_remark" ADD CONSTRAINT "camera_remark_pk" PRIMARY KEY ("id"); + -- ---------------------------- -- Primary Key structure for table gbCamera -- ---------------------------- -ALTER TABLE "public"."gbCamera" ADD CONSTRAINT "gbCamera_pkey" PRIMARY KEY ("id"); +ALTER TABLE "public"."gbCamera" ADD CONSTRAINT "gbCamera1_copy1_pkey" PRIMARY KEY ("id"); + +-- ---------------------------- +-- Primary Key structure for table gbCamera1 +-- ---------------------------- +ALTER TABLE "public"."gbCamera1" ADD CONSTRAINT "gbCamera_pkey" PRIMARY KEY ("id"); -- ---------------------------- -- Indexes structure for table nvr @@ -491,6 +536,11 @@ ALTER TABLE "public"."camera" ADD CONSTRAINT "camera_vender_id_fk" FOREIGN KEY ( ALTER TABLE "public"."camera_ability_bind" ADD CONSTRAINT "camera_ability_bind_camera_ability_id_fk" FOREIGN KEY ("ability_id") REFERENCES "public"."camera_ability" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION; ALTER TABLE "public"."camera_ability_bind" ADD CONSTRAINT "camera_ability_bind_camera_id_fk" FOREIGN KEY ("camera_id") REFERENCES "public"."camera" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION; +-- ---------------------------- +-- Foreign Keys structure for table camera_remark +-- ---------------------------- +ALTER TABLE "public"."camera_remark" ADD CONSTRAINT "camera_remark_camera_id_fk" FOREIGN KEY ("camera_id") REFERENCES "public"."camera" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION; + -- ---------------------------- -- Foreign Keys structure for table nvr -- ---------------------------- diff --git a/code/VideoAccess-VCMP/web/client/assets/video/remarks.mp4 b/code/VideoAccess-VCMP/web/client/assets/video/remarks.mp4 new file mode 100644 index 0000000..4c11720 Binary files /dev/null and b/code/VideoAccess-VCMP/web/client/assets/video/remarks.mp4 differ diff --git a/code/VideoAccess-VCMP/web/client/index.ejs b/code/VideoAccess-VCMP/web/client/index.ejs index 3f13bdb..ee1e2d7 100644 --- a/code/VideoAccess-VCMP/web/client/index.ejs +++ b/code/VideoAccess-VCMP/web/client/index.ejs @@ -6,6 +6,7 @@ + diff --git a/code/VideoAccess-VCMP/web/client/src/components/textScroll.jsx b/code/VideoAccess-VCMP/web/client/src/components/textScroll.jsx index e8e017e..2a4d2af 100644 --- a/code/VideoAccess-VCMP/web/client/src/components/textScroll.jsx +++ b/code/VideoAccess-VCMP/web/client/src/components/textScroll.jsx @@ -3,19 +3,40 @@ import moment from 'moment' import './textScroll.less' function TextScroll (props) { - const { content, duration } = props - const [showContent, setShowContent] = useState('1231231') - const [timer, setTimer] = useState(null) + const { content, duration,roll } = props + const [showContent, setShowContent] = useState('') const showIndex = useRef(0) - console.log(content); - + const initialization = useRef(false) + const cancel = useRef(false) useEffect(() => { if (content.length) { - let repeatTime = moment() - let refreshTime = moment() - const scroll = () => { + if(roll){ + let contentParent = document.getElementById('marquee_box') + document.getElementById('contentPMakeUp').style.width = contentParent.clientWidth + 'px' + const contentP = document.getElementById('contentP') + contentP.style.visibility = 'visible' + setShowContent(content[0]) + window.cancelAnimationFrame(cancel.current) + contentParent.scrollLeft = 0 + initialization.current=false + showIndex.current = 0 + } + else{ + let repeatTime = moment() + let refreshTime = moment() + const scroll = () => { let contentParent = document.getElementById('marquee_box') document.getElementById('contentPMakeUp').style.width = contentParent.clientWidth + 'px' + //初始化 + // if(!showContent&&!initialization.current){ + if(!initialization.current){ + const contentP = document.getElementById('contentP') + contentParent.scrollLeft = 0 + setShowContent(content[showIndex.current]) + showIndex.current = (showIndex.current + 1) % content.length + contentP.style.visibility = 'visible' + initialization.current=true + } // 控制频率 if (moment().diff(refreshTime) > 1000 / 60) { const contentP = document.getElementById('contentP') @@ -37,14 +58,16 @@ function TextScroll (props) { } refreshTime = moment() } - window.requestAnimationFrame(scroll) + let text = null + text=window.requestAnimationFrame(scroll) + cancel.current=text } - window.requestAnimationFrame(scroll) + window.requestAnimationFrame(scroll) + } } - }, []) - + }, [content,roll]) return ( -
+

{showContent}

diff --git a/code/VideoAccess-VCMP/web/client/src/sections/equipmentWarehouse/actions/camera.js b/code/VideoAccess-VCMP/web/client/src/sections/equipmentWarehouse/actions/camera.js index fa0beda..cfface6 100644 --- a/code/VideoAccess-VCMP/web/client/src/sections/equipmentWarehouse/actions/camera.js +++ b/code/VideoAccess-VCMP/web/client/src/sections/equipmentWarehouse/actions/camera.js @@ -207,4 +207,15 @@ export function postVerifyYingshi(data) { url: `${ApiTable.getCascadeStream}`, msg: { option: "" }, //获取级联视频流 }); + } + export function postCameraRemark(data) { + return (dispatch) => + basicAction({ + type: "post", + dispatch: dispatch, + data, + actionType: "POST_CAMERA_REMARK", + url: `${ApiTable.postCameraRemark}`, + msg: { option: "摄像头备注" }, //编辑摄像头备注 + }); } \ No newline at end of file diff --git a/code/VideoAccess-VCMP/web/client/src/sections/equipmentWarehouse/components/remarksModal.jsx b/code/VideoAccess-VCMP/web/client/src/sections/equipmentWarehouse/components/remarksModal.jsx new file mode 100644 index 0000000..cae0297 --- /dev/null +++ b/code/VideoAccess-VCMP/web/client/src/sections/equipmentWarehouse/components/remarksModal.jsx @@ -0,0 +1,137 @@ +import React, { useState, useRef, useEffect } from "react"; +import { connect } from "react-redux"; +import { Modal, Spin,Input } from "@douyinfe/semi-ui"; +import TextScroll from "../../../components/textScroll"; + +function remarksModal (props) { + const { + close, + visible, + rowId, + dispatch, + actions + } = props; + const { equipmentWarehouse } = actions; + const [ScrollList, setScrollList] = useState([{num:'01',value:''},{num:'02',value:''},{num:'03',value:''}]); //显示 + const [showScrollList, setShowScrollList] = useState([]); //显示 + const [roll,setroll] = useState(false); + const [showValueNum,setShowValueNum] = useState(0); + const valueNum = useRef(0); + + // useEffect(() => { + // setShowScrollList([ + // '周杰伦7月6日出专辑,请大家多多捧场备注', + // '111111111111111111111111111111111111111111111111111111' + // ]) + // }, []); + + function handleOk () { + //点击弹框确定 右边按钮 + dispatch( + equipmentWarehouse.postCameraRemark({ + cameraId:rowId, + remark:showScrollList + }) + ).then((res) => { + close(); + }) + } + function handleAfterClose () { + //在关闭之后 + } + function handleCancel () { + close(); + //点击弹框取消 左边按钮 + } + function onChange(value,num){ + valueNum.current=0 + let myScrollList=ScrollList + myScrollList[num].value=value + setScrollList(myScrollList); + setShowScrollList([value]) + for (let index = 0; index < myScrollList.length; index++) { + if(myScrollList[index].value){ + valueNum.current=valueNum.current+1 + } + } + setShowValueNum(valueNum.current); + } + function onFocus(num){ + setShowScrollList([ScrollList[num].value]) + setroll(true) + } + function onBlur(){ + setShowScrollList([]) + let myScrollList = [] + for (let index = 0; index < ScrollList.length; index++) { + if(ScrollList[index].value){ + myScrollList.push(ScrollList[index].value) + } + } + setShowScrollList(myScrollList) + setroll(false) + } + return ( + <> + +
+
+
+
请添加备注信息
+
({showValueNum}/3)
+
+
+ {ScrollList.map((item, index) => ( +
+ {item.num}
} + value={item.value} + onChange={(value)=>onChange(value,index)} + onFocus={()=>onFocus(index)} + onBlur={onBlur} + maxLength={25} + showClear> +
+ ))} +
+
+
+
+ +
+
+
+ + + ); +} +function mapStateToProps (state) { + const { auth, global, members, CameraKind, CameraAbility } = state; + return { + loading: members.isRequesting, + user: auth.user, + actions: global.actions, + CameraKind: CameraKind.data || [], + CameraAbility: CameraAbility.data || [], + }; +} + +export default connect(mapStateToProps)(remarksModal); 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 c9ea168..3298f75 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 @@ -16,6 +16,7 @@ import { import { SimpleFileDownButton, VideoPlayModal } from "$components"; import "../style.less"; import CameraModal from "../components/cameraModal"; +import RemarksModal from "../components/remarksModal"; import Setup from "../components/setup"; import SideSheets from "../components/sideSheet"; import { skeletonScreen } from "../components/skeletonScreen"; @@ -24,6 +25,7 @@ import { accessType } from "./nvr"; const CameraHeader = (props) => { const { dispatch, actions, user, loading, equipmentWarehouseCamera } = props; const [cameraModal, setCameraModal] = useState(false); + const [remarksModal,setRemarksModal] = useState(false); const [videoPlay, setVideoPlay] = useState(false); const [modalName, setModalName] = useState(""); const [setup, setSetup] = useState(false); @@ -279,6 +281,22 @@ const CameraHeader = (props) => { > +
); }, @@ -822,6 +840,17 @@ const CameraHeader = (props) => { }} /> : "" } + { + remarksModal? + { + setRemarksModal(false) + setRowId() + }} > + :'' + } ); }; diff --git a/code/VideoAccess-VCMP/web/client/src/utils/webapi.js b/code/VideoAccess-VCMP/web/client/src/utils/webapi.js index d5783d3..34bf1d5 100644 --- a/code/VideoAccess-VCMP/web/client/src/utils/webapi.js +++ b/code/VideoAccess-VCMP/web/client/src/utils/webapi.js @@ -25,7 +25,7 @@ export const ApiTable = { delCamera: "camera/{cameraId}", //删除摄像头 getCameraDetails: "camera/{cameraId}/detail", //获取摄像头详情 getCameraKind: "camera/kind", //获取摄像头种类列表 - getAbility: "/camera/ability", //获取摄像头能力列表 + getAbility: "camera/ability", //获取摄像头能力列表 postCameraYingshi: "camera/create/yingshi", //创建萤石摄像头 postCameraIpc: "camera/create/ipc", //创建IPC摄像头 getVideoStreaming: "camera/nvr_stream", //获取NVR视频流 @@ -36,8 +36,9 @@ export const ApiTable = { postVerifyYingshi: "camera/verify/yingshi", //验证萤石摄像头信息 postVerifyIpc: "camera/verify/ipc", //验证IPC摄像头信息 postVerifyCascade: "camera/verify/cascade", //验证级联摄像头信息 - getCascadeStream: "/camera/cascade_stream", //获取级联视频流 + getCascadeStream: "camera/cascade_stream", //获取级联视频流 uploadYingshiVoice: 'camera/yingshi_voice/upload', //上传萤石语音 + postCameraRemark: 'camera/remark',//编辑摄像头备注 }; export const VideoServeApi = { diff --git a/code/VideoAccess-VCMP/web/webpack.config.prod.js b/code/VideoAccess-VCMP/web/webpack.config.prod.js index af8ef36..fb9a046 100644 --- a/code/VideoAccess-VCMP/web/webpack.config.prod.js +++ b/code/VideoAccess-VCMP/web/webpack.config.prod.js @@ -1,6 +1,7 @@ var path = require('path'); var webpack = require('webpack'); var HtmlWebpackPlugin = require('html-webpack-plugin'); +const SemiWebpackPlugin = require('@douyinfe/semi-webpack-plugin').default; const PATHS = { app: path.join(__dirname, 'client/src'), @@ -30,7 +31,13 @@ module.exports = { new HtmlWebpackPlugin({ filename: '../index.html', template: './client/index.ejs' - }) + }), + new SemiWebpackPlugin({ + theme: { + name: '@semi-bot/semi-theme-fscamera', + include: '~@semi-bot/semi-theme-fscamera/scss/local.scss' + } + }), ], optimization: { splitChunks: {