Browse Source

Merge pull request 'dev_trial' (#3) from dev_trial into release_0.0.1

Reviewed-on: https://gitea.free-sun.vip/free-sun/FS-IOT/pulls/3
release_0.0.1
殷伟文 3 years ago
parent
commit
89d485d276
  1. 5
      code/VideoAccess-VCMP/api/.vscode/launch.json
  2. 159
      code/VideoAccess-VCMP/api/app/lib/controllers/camera/index.js
  3. 197
      code/VideoAccess-VCMP/api/app/lib/controllers/nvr/index.js
  4. 8
      code/VideoAccess-VCMP/api/app/lib/index.js
  5. 2
      code/VideoAccess-VCMP/api/app/lib/middlewares/api-log.js
  6. 1
      code/VideoAccess-VCMP/api/app/lib/middlewares/authenticator.js
  7. 50
      code/VideoAccess-VCMP/api/app/lib/middlewares/business-rest.js
  8. 511
      code/VideoAccess-VCMP/api/app/lib/models/camera.js
  9. 6
      code/VideoAccess-VCMP/api/app/lib/models/vender.js
  10. 17
      code/VideoAccess-VCMP/api/app/lib/routes/camera/index.js
  11. 66
      code/VideoAccess-VCMP/api/app/lib/service/paasRequest.js
  12. 1
      code/VideoAccess-VCMP/api/app/lib/service/redis.js
  13. 169
      code/VideoAccess-VCMP/api/config.js
  14. 5
      code/VideoAccess-VCMP/web/.vscode/extensions.json
  15. BIN
      code/VideoAccess-VCMP/web/client/assets/images/background/backGround.png
  16. BIN
      code/VideoAccess-VCMP/web/client/assets/images/background/camera.png
  17. BIN
      code/VideoAccess-VCMP/web/client/assets/images/background/copy1.png
  18. BIN
      code/VideoAccess-VCMP/web/client/assets/images/background/copy2.png
  19. BIN
      code/VideoAccess-VCMP/web/client/assets/images/background/header.png
  20. BIN
      code/VideoAccess-VCMP/web/client/assets/images/background/logo.png
  21. BIN
      code/VideoAccess-VCMP/web/client/assets/images/background/notice.png
  22. BIN
      code/VideoAccess-VCMP/web/client/assets/images/background/projectIcon0.png
  23. BIN
      code/VideoAccess-VCMP/web/client/assets/images/background/projectIcon1.png
  24. BIN
      code/VideoAccess-VCMP/web/client/assets/images/background/projectIcon2.png
  25. BIN
      code/VideoAccess-VCMP/web/client/assets/images/background/projectIcon3.png
  26. BIN
      code/VideoAccess-VCMP/web/client/assets/images/background/sewage_camera1.png
  27. BIN
      code/VideoAccess-VCMP/web/client/assets/images/background/sewage_camera2.png
  28. BIN
      code/VideoAccess-VCMP/web/client/assets/images/background/store1.png
  29. BIN
      code/VideoAccess-VCMP/web/client/assets/images/background/store2.png
  30. BIN
      code/VideoAccess-VCMP/web/client/assets/images/background/straightline.png
  31. 120
      code/VideoAccess-VCMP/web/client/src/layout/components/header/index.jsx
  32. 1
      code/VideoAccess-VCMP/web/client/src/sections/equipmentWarehouse/actions/nvr.js
  33. 292
      code/VideoAccess-VCMP/web/client/src/sections/equipmentWarehouse/components/cameraModal.jsx
  34. 3
      code/VideoAccess-VCMP/web/client/src/sections/equipmentWarehouse/components/cameraModal.less
  35. 192
      code/VideoAccess-VCMP/web/client/src/sections/equipmentWarehouse/components/cascadeCamera.jsx
  36. 262
      code/VideoAccess-VCMP/web/client/src/sections/equipmentWarehouse/components/fluoriteCamera.jsx
  37. 224
      code/VideoAccess-VCMP/web/client/src/sections/equipmentWarehouse/components/ipcCamera.jsx
  38. 182
      code/VideoAccess-VCMP/web/client/src/sections/equipmentWarehouse/components/nvrCamera.jsx
  39. 5
      code/VideoAccess-VCMP/web/client/src/sections/equipmentWarehouse/components/nvrModal.jsx
  40. 8
      code/VideoAccess-VCMP/web/client/src/sections/equipmentWarehouse/components/setup.jsx
  41. 447
      code/VideoAccess-VCMP/web/client/src/sections/equipmentWarehouse/components/sideSheet.jsx
  42. 23
      code/VideoAccess-VCMP/web/client/src/sections/equipmentWarehouse/containers/camera.jsx
  43. 22
      code/VideoAccess-VCMP/web/client/src/sections/equipmentWarehouse/containers/nvr.jsx
  44. 1
      code/VideoAccess-VCMP/web/package.json
  45. 118
      code/VideoAccess-VCMP/web/webpack.config.js

5
code/VideoAccess-VCMP/api/.vscode/launch.json

@ -17,7 +17,10 @@
"-f http://localhost:4000",
"-g postgres://postgres:123@10.8.30.32:5432/video_access",
"--redisHost 127.0.0.1",
"--redisPort 6379"
"--redisPort 6379",
"--axyApiUrl http://127.0.0.1:4100",
"--godUrl https://restapi.amap.com/v3",
"--godKey 21c2d970e1646bb9a795900dd00093ce"
]
},
{

159
code/VideoAccess-VCMP/api/app/lib/controllers/camera/index.js

@ -0,0 +1,159 @@
'use strict';
async function getCameraProject (ctx, next) {
try {
const models = ctx.fs.dc.models;
const { limit, page, orderBy, orderDirection, keyword, abilityId, type, venderId } = ctx.query
const { userId, token } = ctx.fs.api
let findOption = {
attributes: { exclude: ['delete', 'recycleTime',] },
where: {
createUserId: userId,
recycleTime: null,
delete: false
},
order: [
[orderBy || 'id', orderDirection || 'DESC']
],
include: [{
model: models.CameraAbility
}, {
model: models.CameraKind
}]
}
if (limit) {
findOption.limit = limit
}
if (page && limit) {
findOption.offset = page * limit
}
if (keyword) {
findOption.where.$or = [{
name: { $like: `%${keyword}%` }
}, {
serialNo: { $like: `%${keyword}%` }
}]
}
if (type) {
findOption.where.type = type
}
if (abilityId) {
findOption.where.abilityId = abilityId
}
if (venderId) {
findOption.where.venderId = venderId
}
const cameraRes = await models.Camera.findAll(findOption)
const total = await models.Camera.count({
where: findOption.where
})
// 查在安心云绑定的数据
const cameraIds = cameraRes.map(c => {
return c.dataValues.id
})
const axbindCameraRes = await ctx.app.fs.axyRequest.get('vcmp/camera/project', { query: { token, cameraId: cameraIds.join(',') } })
for (let { dataValues: camera } of cameraRes) {
const corBindCamera = axbindCameraRes.find(b => b.cameraId == camera.id)
if (corBindCamera) {
camera.station = corBindCamera.stations
} else {
camera.station = []
}
}
ctx.status = 200;
ctx.body = {
total: total,
data: cameraRes
}
} catch (error) {
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
ctx.status = 400;
ctx.body = {}
}
}
async function getCamera (ctx) {
try {
const { models } = ctx.fs.dc;
const { cameraId } = ctx.query
const cameraRes = await models.Camera.findAll({
attributes: { exclude: ['delete', 'recycleTime',] },
where: {
id: { $in: cameraId.split(',') }
},
include: [{
model: models.CameraAbility
}, {
model: models.CameraKind
}, {
model: models.Vender
}]
})
ctx.status = 200;
ctx.body = cameraRes
} catch (error) {
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
ctx.status = 400;
ctx.body = {}
}
}
async function banned (ctx) {
try {
const { models } = ctx.fs.dc;
const data = ctx.request.body;
// 向视频服务发送通知
// 库记录
await models.Camera.update({
forbidden: data.forbidden
}, {
where: {
id: data.cameraId
}
})
ctx.status = 204;
} catch (error) {
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
ctx.status = 400;
ctx.body = {}
}
}
async function del (ctx) {
try {
const { models } = ctx.fs.dc;
const { cameraId } = ctx.query
const { token } = ctx.fs.api
await models.cameraId.destroy({
where: {
id: cameraId
}
})
await ctx.app.fs.axyRequest.delete('vcmp/camera/project', { query: { token, cameraId: cameraId.join(',') } })
ctx.status = 204;
} catch (error) {
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
ctx.status = 400;
ctx.body = {}
}
}
module.exports = {
getCameraProject,
getCamera,
banned,
del,
};

197
code/VideoAccess-VCMP/api/app/lib/controllers/nvr/index.js

@ -2,103 +2,134 @@
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 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,
}
// 或取其他服务信息
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 })
}
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 = {}
}
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 get (ctx) {
const models = ctx.fs.dc.models;
try {
const { limit, page, orderBy, orderDirection } = ctx.query
let findOption = {
attributes: { exclude: ['delete'] },
where: {
delete: false,
},
order: [
[orderBy || 'id', orderDirection || 'DESC']
]
}
if (limit) {
findOption.limit = limit
}
if (page && limit) {
findOption.offset = page * limit
}
const models = ctx.fs.dc.models;
try {
const { userId, token } = ctx.fs.api
const { limit, page, orderBy, orderDirection, keyword, venderId } = ctx.query
let findOption = {
attributes: { exclude: ['delete'] },
where: {
createUserId: userId,
delete: false,
},
order: [
[orderBy || 'id', orderDirection || 'DESC']
]
}
if (limit) {
findOption.limit = limit
}
if (page && limit) {
findOption.offset = page * limit
}
if (keyword) {
findOption.where.name = { $like: `%${keyword}%` }
}
if (venderId) {
findOption.where.venderId = venderId
}
const res = await models.Nvr.findAll(findOption)
const total = await models.Nvr.count()
const res = await models.Nvr.findAll(findOption)
const total = await models.Nvr.count({
where: findOption.where
})
ctx.status = 200;
ctx.body = {
total: total,
data: res
}
} catch (error) {
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
ctx.status = 400;
ctx.body = {}
}
ctx.status = 200;
ctx.body = {
total: total,
data: res
}
} catch (error) {
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
const transaction = await ctx.fs.dc.orm.transaction();
try {
const models = ctx.fs.dc.models;
const { userId, token } = ctx.fs.api
const { nvrId } = ctx.params
await models.Nvr.destroy({
where: {
id: nvrId
}
})
await models.Nvr.destroy({
where: {
id: nvrId
},
transaction
})
const cameraRes = await models.Camera.findAll({
where: {
nvrId
}
})
const cameraIds = cameraRes.map(c => c.id)
await models.Camera.destroy({
where: {
nvrId,
}
})
await ctx.app.fs.axyRequest.delete('vcmp/camera/project', { query: { token, cameraId: cameraIds.join(',') } })
ctx.status = 204;
} catch (error) {
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
ctx.status = 400;
ctx.body = {}
}
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 = {}
}
}
module.exports = {
edit,
get,
del,
edit,
get,
del,
};

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

@ -3,9 +3,9 @@
const routes = require('./routes');
const redisConnect = require('./service/redis')
const socketConect = require('./service/socket')
const paasRequest = require('./service/paasRequest');
const authenticator = require('./middlewares/authenticator');
// const apiLog = require('./middlewares/api-log');
const businessRest = require('./middlewares/business-rest');
module.exports.entry = function (app, router, opts) {
app.fs.logger.log('info', '[FS-AUTH]', 'Inject auth and api mv into router.');
@ -18,8 +18,12 @@ module.exports.entry = function (app, router, opts) {
redisConnect(app, opts)
socketConect(app, opts)
// 实例其他平台请求方法
paasRequest(app, opts)
router.use(authenticator(app, opts));
// router.use(businessRest(app, router, opts));
// 日志记录
// router.use(apiLog(app, opts));
router = routes(app, router, opts);

2
code/VideoAccess-VCMP/api/app/lib/middlewares/api-log.js

@ -71,7 +71,7 @@ function factory(app, opts) {
partition: 0
}];
await sendToEsAsync(producer, payloads);
// await sendToEsAsync(producer, payloads);
} catch (e) {
ctx.fs.logger.error(`日志记录失败: ${e}`);

1
code/VideoAccess-VCMP/api/app/lib/middlewares/authenticator.js

@ -94,7 +94,6 @@ let isResourceAvailable = function (resources, options) {
let authCode = null;
// authorize user by authorization attribute
const { authAttr, method, path } = options;
console.log(resources, options)
for (let prop in authAttr) {
let keys = [];
let re = pathToRegexp(prop.replace(/\:[A-Za-z_\-]+\b/g, '(\\d+)'), keys);

50
code/VideoAccess-VCMP/api/app/lib/middlewares/business-rest.js

@ -1,50 +0,0 @@
'use strict';
const request = require('superagent');
const buildUrl = (url,token) => {
let connector = url.indexOf('?') === -1 ? '?' : '&';
return `${url}${connector}token=${token}`;
};
function factory(app, router, opts) {
return async function (ctx, next) {
const token = ctx.fs.api.token;
//console.log(username,password)
const req = {
get: (url, query) => {
return request
.get(buildUrl(url,token))
.query(query)
},
post: (url, data, query) => {
return request
.post(buildUrl(url,token))
.query(query)
//.set('Content-Type', 'application/json')
.send(data);
},
put: (url, data) => {
return request
.put(buildUrl(url,token))
//.set('Content-Type', 'application/json')
.send(data);
},
delete: (url) => {
return request
.del(buildUrl(url,token))
},
};
app.business = app.business || {};
app.business.request = req;
await next();
};
}
module.exports = factory;

511
code/VideoAccess-VCMP/api/app/lib/models/camera.js

@ -2,256 +2,265 @@
'use strict';
module.exports = dc => {
const DataTypes = dc.ORM;
const sequelize = dc.orm;
const Camera = sequelize.define("camera", {
id: {
type: DataTypes.INTEGER,
allowNull: false,
defaultValue: null,
comment: null,
primaryKey: true,
field: "id",
autoIncrement: true,
unique: "camera_id_uindex"
},
type: {
type: DataTypes.STRING,
allowNull: false,
defaultValue: null,
comment: "设备类型:yingshi - 萤石;nvr - NVR摄像头;ipc - IPC 网络摄像头;cascade - 级联摄像头",
primaryKey: false,
field: "type",
autoIncrement: false
},
name: {
type: DataTypes.STRING,
allowNull: true,
defaultValue: null,
comment: "设备名称/安装位置",
primaryKey: false,
field: "name",
autoIncrement: false
},
channelName: {
type: DataTypes.STRING,
allowNull: true,
defaultValue: null,
comment: "通道名称",
primaryKey: false,
field: "channel_name",
autoIncrement: false
},
externalDomain: {
type: DataTypes.STRING,
allowNull: true,
defaultValue: null,
comment: "外域名称",
primaryKey: false,
field: "external_domain",
autoIncrement: false
},
rtmp: {
type: DataTypes.STRING,
allowNull: true,
defaultValue: null,
comment: null,
primaryKey: false,
field: "rtmp",
autoIncrement: false
},
serialNo: {
type: DataTypes.STRING,
allowNull: true,
defaultValue: null,
comment: "设备编号",
primaryKey: false,
field: "serial_no",
autoIncrement: false
},
cloudControl: {
type: DataTypes.BOOLEAN,
allowNull: true,
defaultValue: null,
comment: "云台控制",
primaryKey: false,
field: "cloud_control",
autoIncrement: false
},
highDefinition: {
type: DataTypes.BOOLEAN,
allowNull: true,
defaultValue: null,
comment: "高清支持",
primaryKey: false,
field: "high_definition",
autoIncrement: false
},
voice: {
type: DataTypes.BOOLEAN,
allowNull: true,
defaultValue: null,
comment: "语音对讲支持",
primaryKey: false,
field: "voice",
autoIncrement: false
},
memoryCard: {
type: DataTypes.STRING,
allowNull: true,
defaultValue: null,
comment: "内存卡容量",
primaryKey: false,
field: "memory_card",
autoIncrement: false
},
venderId: {
type: DataTypes.INTEGER,
allowNull: true,
defaultValue: null,
comment: "设备厂商id",
primaryKey: false,
field: "vender_id",
autoIncrement: false,
references: {
key: "id",
model: "vender"
const DataTypes = dc.ORM;
const sequelize = dc.orm;
const Camera = sequelize.define("camera", {
id: {
type: DataTypes.INTEGER,
allowNull: false,
defaultValue: null,
comment: null,
primaryKey: true,
field: "id",
autoIncrement: true,
unique: "camera_id_uindex"
},
type: {
type: DataTypes.STRING,
allowNull: false,
defaultValue: null,
comment: "设备类型:yingshi - 萤石;nvr - NVR摄像头;ipc - IPC 网络摄像头;cascade - 级联摄像头",
primaryKey: false,
field: "type",
autoIncrement: false
},
name: {
type: DataTypes.STRING,
allowNull: true,
defaultValue: null,
comment: "设备名称/安装位置",
primaryKey: false,
field: "name",
autoIncrement: false
},
channelName: {
type: DataTypes.STRING,
allowNull: true,
defaultValue: null,
comment: "通道名称",
primaryKey: false,
field: "channel_name",
autoIncrement: false
},
externalDomain: {
type: DataTypes.STRING,
allowNull: true,
defaultValue: null,
comment: "外域名称",
primaryKey: false,
field: "external_domain",
autoIncrement: false
},
rtmp: {
type: DataTypes.STRING,
allowNull: true,
defaultValue: null,
comment: null,
primaryKey: false,
field: "rtmp",
autoIncrement: false
},
serialNo: {
type: DataTypes.STRING,
allowNull: true,
defaultValue: null,
comment: "设备编号",
primaryKey: false,
field: "serial_no",
autoIncrement: false
},
cloudControl: {
type: DataTypes.BOOLEAN,
allowNull: true,
defaultValue: null,
comment: "云台控制",
primaryKey: false,
field: "cloud_control",
autoIncrement: false
},
highDefinition: {
type: DataTypes.BOOLEAN,
allowNull: true,
defaultValue: null,
comment: "高清支持",
primaryKey: false,
field: "high_definition",
autoIncrement: false
},
voice: {
type: DataTypes.BOOLEAN,
allowNull: true,
defaultValue: null,
comment: "语音对讲支持",
primaryKey: false,
field: "voice",
autoIncrement: false
},
memoryCard: {
type: DataTypes.STRING,
allowNull: true,
defaultValue: null,
comment: "内存卡容量",
primaryKey: false,
field: "memory_card",
autoIncrement: false
},
venderId: {
type: DataTypes.INTEGER,
allowNull: true,
defaultValue: null,
comment: "设备厂商id",
primaryKey: false,
field: "vender_id",
autoIncrement: false,
references: {
key: "id",
model: "vender"
}
},
cascadeType: {
type: DataTypes.STRING,
allowNull: true,
defaultValue: null,
comment: "级联方式:up - 上级联;down - 下级联",
primaryKey: false,
field: "cascade_type",
autoIncrement: false
},
sip: {
type: DataTypes.STRING,
allowNull: true,
defaultValue: null,
comment: null,
primaryKey: false,
field: "sip",
autoIncrement: false
},
longitude: {
type: DataTypes.DOUBLE,
allowNull: true,
defaultValue: null,
comment: "经度",
primaryKey: false,
field: "longitude",
autoIncrement: false
},
latitude: {
type: DataTypes.DOUBLE,
allowNull: true,
defaultValue: null,
comment: "维度",
primaryKey: false,
field: "latitude",
autoIncrement: false
},
forbidden: {
type: DataTypes.BOOLEAN,
allowNull: false,
defaultValue: null,
comment: "是否禁用",
primaryKey: false,
field: "forbidden",
autoIncrement: false
},
createTime: {
type: DataTypes.DATE,
allowNull: false,
defaultValue: null,
comment: null,
primaryKey: false,
field: "create_time",
autoIncrement: false
},
recycleTime: {
type: DataTypes.DATE,
allowNull: true,
defaultValue: null,
comment: "放入回收站时间",
primaryKey: false,
field: "recycle_time",
autoIncrement: false
},
delete: {
type: DataTypes.BOOLEAN,
allowNull: false,
defaultValue: null,
comment: "是否彻底删除",
primaryKey: false,
field: "delete",
autoIncrement: false
},
createUserId: {
type: DataTypes.INTEGER,
allowNull: false,
defaultValue: null,
comment: null,
primaryKey: false,
field: "create_user_id",
autoIncrement: false
},
nvrId: {
type: DataTypes.INTEGER,
allowNull: true,
defaultValue: null,
comment: null,
primaryKey: false,
field: "nvr_id",
autoIncrement: false,
references: {
key: "id",
model: "nvr"
}
},
model: {
type: DataTypes.STRING,
allowNull: true,
defaultValue: null,
comment: "型号",
primaryKey: false,
field: "model",
autoIncrement: false
},
kindId: {
type: DataTypes.INTEGER,
allowNull: true,
defaultValue: null,
comment: null,
primaryKey: false,
field: "kind_id",
autoIncrement: false,
references: {
key: "id",
model: "cameraKind"
}
},
abilityId: {
type: DataTypes.INTEGER,
allowNull: true,
defaultValue: null,
comment: null,
primaryKey: false,
field: "ability_id",
autoIncrement: false,
references: {
key: "id",
model: "cameraAbility"
}
}
},
cascadeType: {
type: DataTypes.STRING,
allowNull: true,
defaultValue: null,
comment: "级联方式:up - 上级联;down - 下级联",
primaryKey: false,
field: "cascade_type",
autoIncrement: false
},
sip: {
type: DataTypes.STRING,
allowNull: true,
defaultValue: null,
comment: null,
primaryKey: false,
field: "sip",
autoIncrement: false
},
longitude: {
type: DataTypes.DOUBLE,
allowNull: true,
defaultValue: null,
comment: "经度",
primaryKey: false,
field: "longitude",
autoIncrement: false
},
latitude: {
type: DataTypes.DOUBLE,
allowNull: true,
defaultValue: null,
comment: "维度",
primaryKey: false,
field: "latitude",
autoIncrement: false
},
forbidden: {
type: DataTypes.BOOLEAN,
allowNull: false,
defaultValue: null,
comment: "是否禁用",
primaryKey: false,
field: "forbidden",
autoIncrement: false
},
createTime: {
type: DataTypes.DATE,
allowNull: false,
defaultValue: null,
comment: null,
primaryKey: false,
field: "create_time",
autoIncrement: false
},
recycleTime: {
type: DataTypes.DATE,
allowNull: true,
defaultValue: null,
comment: "放入回收站时间",
primaryKey: false,
field: "recycle_time",
autoIncrement: false
},
delete: {
type: DataTypes.BOOLEAN,
allowNull: false,
defaultValue: null,
comment: "是否彻底删除",
primaryKey: false,
field: "delete",
autoIncrement: false
},
createUserId: {
type: DataTypes.INTEGER,
allowNull: false,
defaultValue: null,
comment: null,
primaryKey: false,
field: "create_user_id",
autoIncrement: false
},
nvrId: {
type: DataTypes.INTEGER,
allowNull: true,
defaultValue: null,
comment: null,
primaryKey: false,
field: "nvr_id",
autoIncrement: false,
references: {
key: "id",
model: "nvr"
}
},
model: {
type: DataTypes.STRING,
allowNull: true,
defaultValue: null,
comment: "型号",
primaryKey: false,
field: "model",
autoIncrement: false
},
kindId: {
type: DataTypes.INTEGER,
allowNull: true,
defaultValue: null,
comment: null,
primaryKey: false,
field: "kind_id",
autoIncrement: false,
references: {
key: "id",
model: "cameraKind"
}
},
abilityId: {
type: DataTypes.INTEGER,
allowNull: true,
defaultValue: null,
comment: null,
primaryKey: false,
field: "ability_id",
autoIncrement: false,
references: {
key: "id",
model: "cameraAbility"
}
}
}, {
tableName: "camera",
comment: "",
indexes: []
});
dc.models.Camera = Camera;
return Camera;
}, {
tableName: "camera",
comment: "",
indexes: []
});
dc.models.Camera = Camera;
const CameraKind = dc.models.CameraKind;
Camera.belongsTo(CameraKind, { foreignKey: 'kindId', targetKey: 'id' });
CameraKind.hasMany(Camera, { foreignKey: 'kindId', sourceKey: 'id' });
const CameraAbility = dc.models.CameraAbility;
Camera.belongsTo(CameraAbility, { foreignKey: 'abilityId', targetKey: 'id' });
CameraAbility.hasMany(Camera, { foreignKey: 'abilityId', sourceKey: 'id' });
return Camera;
};

6
code/VideoAccess-VCMP/api/app/lib/models/vender.js

@ -29,6 +29,12 @@ module.exports = dc => {
comment: "",
indexes: []
});
const Camera = dc.models.Camera;
Camera.belongsTo(Vender, { foreignKey: 'venderId', targetKey: 'id' });
Vender.hasMany(Camera, { foreignKey: 'venderId', sourceKey: 'id' });
dc.models.Vender = Vender;
return Vender;
};

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

@ -0,0 +1,17 @@
'use strict';
const camera = require('../../controllers/camera');
module.exports = function (app, router, opts) {
app.fs.api.logAttr['GET/camera/project'] = { content: '获取摄像头列表及项目绑定信息', visible: false };
router.get('/camera/project', camera.getCameraProject);
app.fs.api.logAttr['GET/camera'] = { content: '获取摄像头信息', visible: false };
router.get('/camera', camera.getCamera);
app.fs.api.logAttr['PUT/camera/banned'] = { content: '禁用摄像头', visible: false };
router.put('/camera/banned', camera.banned);
app.fs.api.logAttr['DEL/camera'] = { content: '删除摄像头', visible: false };
router.delete('/camera', camera.del);
};

66
code/VideoAccess-VCMP/api/app/lib/service/paasRequest.js

@ -0,0 +1,66 @@
'use strict';
const request = require('superagent')
class paasRequest {
constructor(root, { query = {} } = {}) {
this.root = root;
this.query = query
}
#buildUrl = (url) => {
return `${this.root}/${url}`;
}
#resultHandler = (resolve, reject) => {
return (err, res) => {
if (err) {
reject(err);
} else {
resolve(res.body);
}
};
}
get = (url, { query = {}, header = {} } = {}) => {
return new Promise((resolve, reject) => {
request.get(this.#buildUrl(url)).set(header).query(Object.assign(query, this.query)).end(this.#resultHandler(resolve, reject));
})
}
post = (url, { data = {}, query = {}, header = {} } = {}) => {
return new Promise((resolve, reject) => {
request.post(this.#buildUrl(url)).set(header).query(Object.assign(query, this.query)).send(data).end(this.#resultHandler(resolve, reject));
})
}
put = (url, { data = {}, header = {}, query = {}, } = {}) => {
return new Promise((resolve, reject) => {
request.put(this.#buildUrl(url)).set(header).query(Object.assign(query, this.query)).send(data).end(this.#resultHandler(resolve, reject));
})
}
delete = (url, { header = {}, query = {} } = {}) => {
return new Promise((resolve, reject) => {
request.delete(this.#buildUrl(url)).set(header).query(Object.assign(query, this.query)).end(this.#resultHandler(resolve, reject));
})
}
}
function factory (app, opts) {
if (opts.pssaRequest) {
try {
for (let r of opts.pssaRequest) {
if (r.name && r.root) {
app.fs[r.name] = new paasRequest(r.root, { ...(r.params || {}) })
} else {
throw 'opts.pssaRequest 参数错误!'
}
}
} catch (error) {
console.error(error)
process.exit(-1);
}
}
}
module.exports = factory;

1
code/VideoAccess-VCMP/api/app/lib/service/redis.js

@ -1,7 +1,6 @@
'use strict';
// https://github.com/luin/ioredis
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);

169
code/VideoAccess-VCMP/api/config.js

@ -14,6 +14,9 @@ args.option(['f', 'fileHost'], '文件中心本地化存储: WebApi 服务器地
args.option('redisHost', 'redisHost');
args.option('redisPort', 'redisPort');
args.option('redisPswd', 'redisPassword');
args.option('axyApiUrl', 'axyApiUrl'); // 安心云 api
args.option('godUrl', '高德地图API请求地址');
args.option('godKey', '高德地图API key');
const flags = args.parse(process.argv);
@ -24,92 +27,108 @@ const IOTA_REDIS_SERVER_HOST = process.env.IOTA_REDIS_SERVER_HOST || flags.redis
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);
const AXY_API_URL = process.env.AXY_API_URL || flags.axyApiUrl;
const GOD_URL = process.env.GOD_URL || flags.godUrl || 'https://restapi.amap.com/v3';
const GOD_KEY = process.env.GOD_KEY || flags.godKey;
if (!IOT_VIDEO_ACCESS_DB || !IOTA_REDIS_SERVER_HOST || !IOTA_REDIS_SERVER_PORT || !GOD_KEY) {
console.log('缺少启动参数,异常退出');
args.showHelp();
process.exit(-1);
}
const product = {
port: flags.port || 8080,
staticDirs: ['static'],
mws: [
{
entry: require('@fs/attachment').entry,
opts: {
local: {
origin: IOT_VIDEO_ACCESS_LOCAL_SVR_ORIGIN || `http://localhost:${flags.port || 8080}`,
rootPath: 'static',
childPath: 'upload',
},
maxSize: 104857600, // 100M
}
}, {
entry: require('./app').entry,
opts: {
exclude: [], // 不做认证的路由,也可以使用 exclude: ["*"] 跳过所有路由
redis: {
host: IOTA_REDIS_SERVER_HOST,
port: IOTA_REDIS_SERVER_PORT,
pwd: IOTA_REDIS_SERVER_PWD
},
}
}
],
dc: {
url: IOT_VIDEO_ACCESS_DB,
opts: {
pool: {
max: 80,
min: 10,
idle: 10000
port: flags.port || 8080,
staticDirs: ['static'],
mws: [
{
entry: require('@fs/attachment').entry,
opts: {
local: {
origin: IOT_VIDEO_ACCESS_LOCAL_SVR_ORIGIN || `http://localhost:${flags.port || 8080}`,
rootPath: 'static',
childPath: 'upload',
},
define: {
freezeTableName: true, // 固定表名
timestamps: false // 不含列 "createAt"/"updateAt"/"DeleteAt"
maxSize: 104857600, // 100M
}
}, {
entry: require('./app').entry,
opts: {
exclude: [], // 不做认证的路由,也可以使用 exclude: ["*"] 跳过所有路由
redis: {
host: IOTA_REDIS_SERVER_HOST,
port: IOTA_REDIS_SERVER_PORT,
pwd: IOTA_REDIS_SERVER_PWD
},
timezone: '+08:00',
logging: false
},
models: [require('./app').models]
},
logger: {
level: 'info',
json: false,
filename: path.join(__dirname, 'log', 'runtime.log'),
colorize: false,
maxsize: 1024 * 1024 * 5,
rotationFormat: false,
zippedArchive: true,
maxFiles: 10,
prettyPrint: true,
label: '',
timestamp: () => moment().format('YYYY-MM-DD HH:mm:ss.SSS'),
eol: os.EOL,
tailable: true,
depth: null,
showLevel: true,
maxRetries: 1
}
pssaRequest: [{ // name 会作为一个 request 出现在 ctx.app.fs
name: 'axyRequest',
root: AXY_API_URL
}, {
name: 'godRequest',
root: GOD_URL,
params: {
query: {
key: GOD_KEY
}
}
},]
}
}
],
dc: {
url: IOT_VIDEO_ACCESS_DB,
opts: {
pool: {
max: 80,
min: 10,
idle: 10000
},
define: {
freezeTableName: true, // 固定表名
timestamps: false // 不含列 "createAt"/"updateAt"/"DeleteAt"
},
timezone: '+08:00',
logging: false
},
models: [require('./app').models]
},
logger: {
level: 'info',
json: false,
filename: path.join(__dirname, 'log', 'runtime.log'),
colorize: false,
maxsize: 1024 * 1024 * 5,
rotationFormat: false,
zippedArchive: true,
maxFiles: 10,
prettyPrint: true,
label: '',
timestamp: () => moment().format('YYYY-MM-DD HH:mm:ss.SSS'),
eol: os.EOL,
tailable: true,
depth: null,
showLevel: true,
maxRetries: 1
}
};
const development = {
port: product.port,
staticDirs: product.staticDirs,
mws: product.mws,
dc: product.dc,
logger: product.logger
port: product.port,
staticDirs: product.staticDirs,
mws: product.mws,
dc: product.dc,
logger: product.logger
};
if (dev) {
// mws
for (let mw of development.mws) {
// if (mw.opts.exclude) mw.opts.exclude = ['*']; // 使用 ['*'] 跳过所有路由
}
// logger
development.logger.filename = path.join(__dirname, 'log', 'development.log');
development.logger.level = 'debug';
development.dc.opts.logging = console.log;
// mws
for (let mw of development.mws) {
// if (mw.opts.exclude) mw.opts.exclude = ['*']; // 使用 ['*'] 跳过所有路由
}
// logger
development.logger.filename = path.join(__dirname, 'log', 'development.log');
development.logger.level = 'debug';
development.dc.opts.logging = console.log;
}
module.exports = dev ? development : product;

5
code/VideoAccess-VCMP/web/.vscode/extensions.json

@ -0,0 +1,5 @@
{
"recommendations": [
"formulahendry.code-runner"
]
}

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 4.0 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 712 B

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 173 B

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 169 B

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 226 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 576 B

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 434 B

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 258 B

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 453 B

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 300 B

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 853 B

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

120
code/VideoAccess-VCMP/web/client/src/layout/components/header/index.jsx

@ -1,47 +1,85 @@
'use strict';
import React from 'react';
import { connect } from 'react-redux';
import { Nav } from '@douyinfe/semi-ui';
"use strict";
import React from "react";
import { connect } from "react-redux";
import { Nav, Avatar, Dropdown } from "@douyinfe/semi-ui";
const Header = props => {
const { dispatch, history, user, actions, socket } = props
const Header = (props) => {
const { dispatch, history, user, actions, socket } = props;
return (
<div style={{ position: 'relative', height: 60, minWidth: 520 }}>
<div style={{ float: 'left', paddingLeft: 32, fontSize: 16 }}>
<div style={{
lineHeight: '60px', display: 'inline-block', fontSize: 20, textShadow: '0 4px 3px rgba(0, 0, 0, 0.2)',
userSelect: 'none'
}}>
飞尚物联
</div>
</div>
<div id="nav" style={{ float: 'right' }}>
<Nav mode={'horizontal'} onClick={({ itemKey }) => {
if (itemKey == 'logout') {
dispatch(actions.auth.logout(user));
if (socket) {
socket.disconnect()
}
history.push(`/signin`);
}
}}>
<Nav.Sub itemKey={'user'} text={<div style={{ display: 'inline-block' }}>{user && user.namePresent}</div>}>
<Nav.Item itemKey={'logout'} text={'退出'} />
</Nav.Sub>
</Nav>
</div>
</div>
)
return (
<>
<Nav
mode={"horizontal"}
onClick={({ itemKey }) => {
if (itemKey == "logout") {
dispatch(actions.auth.logout(user));
if (socket) {
socket.disconnect();
}
history.push(`/signin`);
}
}}
style={{
height: 60,
minWidth: 520,
background: "url(/assets/images/background/header.png)",
backgroundSize: "100% 100%",
color: "white",
}}
header={{
logo: (
<img
src="/assets/images/background/logo.png"
style={{ display: "inline-block", width: 280, height: 52}}
/>
),
text: "",
}}
footer={
<Nav.Sub
itemKey={"user"}
text={
<div
style={{
marginLeft: 20,
display: "inline-block",
color: "white",
}}
>
<img
src="/assets/images/background/notice.png"
style={{
display: "inline-block",
width: 18,
height: 18,
position: "relative",
top: 6,
left: -10,
}}
/>
<Avatar size="small" color="light-blue" style={{ margin: 4 }}>
<img src="/assets/images/avatar/6.png" />
</Avatar>
{user && user.namePresent}
</div>
}
>
<Nav.Item itemKey={"logout"} text={"退出"} />
</Nav.Sub>
}
/>
</>
);
};
function mapStateToProps (state) {
const { global, auth, webSocket } = state;
return {
actions: global.actions,
user: auth.user,
socket: webSocket.socket
};
function mapStateToProps(state) {
const { global, auth, webSocket } = state;
return {
actions: global.actions,
user: auth.user,
socket: webSocket.socket,
};
}
export default connect(mapStateToProps)(Header);
export default connect(mapStateToProps)(Header);

1
code/VideoAccess-VCMP/web/client/src/sections/equipmentWarehouse/actions/nvr.js

@ -45,6 +45,7 @@ export function addchangeNvr(data) {
dispatch: dispatch,
data,
actionType: "ADD_CHANGE_NVR",
msg: { option: "添加/修改" },
url: `${ApiTable.nvr}`,
});
}

292
code/VideoAccess-VCMP/web/client/src/sections/equipmentWarehouse/components/cameraModal.jsx

@ -1,20 +1,21 @@
import React, { useState ,useRef} from 'react'
import React, { useState ,useRef,useEffect} from 'react'
import { connect } from "react-redux";
import { Modal,Form,Row,Col,Spin,Notification,Button } from '@douyinfe/semi-ui';
import { Modal,Spin } from '@douyinfe/semi-ui';
import { IconChevronLeft,IconChevronRight } from '@douyinfe/semi-icons';
import FluoriteCamera from "./fluoriteCamera";
import NvrCamera from './nvrCamera';
import IpcCamera from './ipcCamera';
import CascadeCamera from './cascadeCamera';
import "./cameraModal.less";
function cameraModal(props){
const { TextArea } = Form;
const {modalName,visible,close}=props
const form = useRef();
// const [visible, setVisible] = useState(false);//
const fluoriteRef = useRef();
const ipcRef = useRef();
const cascadeRef = useRef();
const [isloading,setloading] = useState(false);//loading
const [loadingTip,setloadingTip] = useState('获取中...请稍后...');//loading tip
const [step,setstep] = useState(0)//
const [okText,setokText] = useState('确定')//oktext
const [cancelText,setcancelText] = useState('取消')//text
const [cloud,setcloud] = useState('')//
const [voice,setvoice] = useState('')//
const opts ={//
title:'Hi',
content:'添加成功',
@ -44,67 +45,103 @@ function cameraModal(props){
text:'通过GB/T28181协议级联的平台摄像头,常用于平台对接推送'
},
]
const [showcameraList,setcameraList]=useState(cameraList.slice(0,3));
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();
if(clickNum==1){
console.log('1111111111111');
}else if(clickNum==2){
console.log('22222222222222');
}else if(clickNum==3){
console.log('33333333333333');
}else if(clickNum==4){
console.log('44444444444444');
}
// Notification.success(opts)
// close();
}
function handleAfterClose(){//
setstep(0);
setokText('测试校验');
setcancelText('取消');
}
function handleCancel() {//
if(step==0){
// setVisible(false);
close();
}
else{
setstep(0);
setokText('测试校验');
setcancelText('取消');
}
}
function handleLocation(){//
window.open('https://lbs.amap.com/tools/picker','_blank')
close();
}
function handleChoose(id){//
setclickNum(id);
}
function turnLift(){
function turnLift(){//
setcameraList(cameraList.slice(0,3))
}
function turnRight(){
function turnRight(){//
setcameraList(cameraList.slice(1,4))
}
function onReset(){
if(clickNum==1){
fluoriteRef.current.resetFluoriteCamera()
}else if(clickNum==3){
ipcRef.current.resetIpcCamera()
}else if(clickNum==4){
}
}
function toTest(){
if(clickNum==1){
fluoriteRef.current.fluoriteCameraForm().then(values=>{//
console.log('111111111',values);
})
.catch(errors=>{//
console.log('errors',errors);
})
}else if(clickNum==3){
ipcRef.current.ipcCameraForm().then(values=>{//
console.log('111111111',values);
})
.catch(errors=>{//
console.log('errors',errors);
})
}else if(clickNum==4){
cascadeRef.current.cascadeCameraForm()
.then(values=>{//
let chooseList=[]
let nvrCameraList=[{
id:10,
name:'南昌县1',
number:'111111111111111111',
support:false,
change:false,
},{
id:20,
name:'南昌县2',
number:'222222222222222222',
support:false,
change:false,
},{
id:30,
name:'南昌县3',
number:'333333333333333333',
support:false,
change:false,
},{
id:40,
name:'南昌县4',
number:'444444444444444444',
support:false,
change:false,
}]
cascadeRef.current.setNVRcameraList(nvrCameraList)
for (let index = 0; index < nvrCameraList.length; index++) {
chooseList.push(nvrCameraList[index].id)
}
cascadeRef.current.setNvrCheckList(chooseList)
cascadeRef.current.setIsAllChoose(true)
})
.catch(errors=>{//
console.log('errors',errors);
})
}
}
return (
<>
{/* <div onClick={showDialog}>{modalName=='add'?'添加NVR':'修改'}</div> */}
<Modal
title={modalName=='add'?'添加摄像头':'修改摄像头'}
okText={okText}
@ -164,9 +201,9 @@ function cameraModal(props){
onClick={turnRight}/>
</div>
</div>
<div style={{marginLeft:'-24px',marginRight:'-24px',marginTop:48,display:'flex',alignItems: 'center',justifyContent: 'space-between'}}>
<div style={{height:30,marginLeft:'-24px',marginRight:'-24px',marginTop:48,display:'flex',alignItems: 'center',justifyContent: 'space-between'}}>
<div style={{marginLeft:29,color:'#1859C1',fontSize:14,fontWeight:500}}>配置属性</div>
<div style={{display:'flex',marginRight:43,}}>
{clickNum!==2?<div style={{display:'flex',marginRight:43,}}>
<div style={{
height:30,
width:64,
@ -178,7 +215,7 @@ function cameraModal(props){
alignItems: 'center',
cursor: "pointer",
marginRight:16
}}>
}} onClick={onReset}>
<img src="/assets/images/background/Reset.png" alt="1" style={{marginRight:4}}/>
重置
</div>
@ -192,145 +229,20 @@ function cameraModal(props){
justifyContent: 'center',
alignItems: 'center',
cursor: "pointer",
}}>
}} onClick={toTest}>
<img src="/assets/images/background/test.png" alt="1" style={{marginRight:4}} />
测试
</div>
</div>
</div>:''}
</div>
<div>
<Form
labelPosition='left'
labelAlign='left'
labelWidth= '115px'
onValueChange={values=>console.log(values)}
getFormApi={formApi => form.current = formApi}>
<Row>
<Col span={12}>
<Form.Input field='UserName' label='设备名称:' initValue={''} placeholder='请输入设备名称、常用项目或位置定义' style={{ width:307 }}
rules={[
{ required: true, message: '请输入设备名称' }
]}/>
</Col>
<Col span={12}>
<Form.RadioGroup
label="云台支持:"
field='role'
type='pureCard'
direction='horizontal'
style={{padding:0}}
rules={[
{ required: true, message: '请选择云台支持' }
]}
onChange={(checked) => {
console.log(checked.target.value);
if(checked.target.value=='yes'){
setcloud('yes')
}
else{
setcloud('no')
}
}}>
<Form.Radio value="yes" style={{width:58,height:30,padding:0,margin:0,background:'#F9F9F9'}}>
<div className='cloud' style={{width:58,height:30,textAlign:'center',lineHeight:'30px'}}>
支持
</div>
{cloud=='yes'?<div style={{position: 'absolute', top: '-2px', right: '-1px'}}>
<img src="/assets/images/background/formchoose.png" alt="1" />
</div>:''}
</Form.Radio>
<Form.Radio value="no" style={{width:58,height:30,padding:0,margin:0,marginLeft:18,background:'#F9F9F9'}}>
<div className='cloud' style={{width:58,height:30,textAlign:'center',lineHeight:'30px'}}>
不支持
</div>
{cloud=='no'?<div style={{position: 'absolute', top: '-2px', right: '-1px'}}>
<img src="/assets/images/background/formchoose.png" alt="1" />
</div>:''}
</Form.Radio>
</Form.RadioGroup>
</Col>
<Col span={12}>
<Form.Input field='Use11rName11' label='设备厂家:' placeholder='请选择设备厂家' style={{ width:307 }}/>
</Col>
<Col span={12}>
<div style={{display:'flex'}}>
<Form.Select label="内存:" field='business2' placeholder='未安装' style={{ width:92 }}>
<Form.Select.Option value="abc">Semi</Form.Select.Option>
<Form.Select.Option value="ulikeCam">轻颜相机</Form.Select.Option>
<Form.Select.Option value="toutiao">今日头条</Form.Select.Option>
</Form.Select>
<div style={{marginLeft:18}}>
<Form.RadioGroup
labelWidth= '76px'
label="语音支持:"
field='role2'
type='pureCard'
direction='horizontal'
style={{padding:0}}
onChange={(checked) => {
console.log(checked.target.value);
if(checked.target.value=='yes'){
setvoice('yes')
}
else{
setvoice('no')
}
}}>
<Form.Radio value="yes" style={{width:58,height:30,padding:0,margin:0,background:'#F9F9F9'}}>
<div className='voice' style={{width:58,height:30,textAlign:'center',lineHeight:'30px'}}>
支持
</div>
{voice=='yes'?<div style={{position: 'absolute', top: '-2px', right: '-1px'}}>
<img src="/assets/images/background/formchoose.png" alt="1" />
</div>:''}
</Form.Radio>
<Form.Radio value="no" style={{width:58,height:30,padding:0,margin:0,marginLeft:18,background:'#F9F9F9'}}>
<div className='voice' style={{width:58,height:30,textAlign:'center',lineHeight:'30px'}}>
不支持
</div>
{voice=='no'?<div style={{position: 'absolute', top: '-2px', right: '-1px'}}>
<img src="/assets/images/background/formchoose.png" alt="1" />
</div>:''}
</Form.Radio>
</Form.RadioGroup>
</div>
</div>
</Col>
<Col span={12}>
<div style={{display:'flex'}}>
<Form.Input field='Use11rName1312' label='安装位置:' placeholder='请输入或拾取高德经纬度坐标' style={{ width:270 }}
rules={[
{ required: true, message: '请输入或拾取高德经纬度坐标' }
]}/>
<div style={{
width:32,
height:32,
background:"#1859C1",
marginLeft:4,
display:'flex',
justifyContent: 'center',
alignItems: 'center',
cursor: "pointer",
marginTop:12,
borderRadius: 3+'px'}}
onClick={handleLocation}>
<img src="../../../assets/images/background/location.png" width={16} height={20}/>
</div>
</div>
<div>
<Form.Input field='UserName11' label='设备编号接入:' initValue={''} placeholder='请输入设备编号' style={{ width:307 }}/>
</div>
</Col>
<Col span={12}>
<TextArea
style={{ width:320, height: 90 }}
field='description'
label='RTMP地址接入:'
placeholder='请输入RTMP地址接入'
/>
</Col>
</Row>
</Form>
{clickNum==1?
<FluoriteCamera cRef={fluoriteRef}/>
:clickNum==2?
<NvrCamera/>
:clickNum==3?
<IpcCamera aRef={ipcRef} />
:<CascadeCamera dRef={cascadeRef}/>}
</div>
</Spin>
</Modal>

3
code/VideoAccess-VCMP/web/client/src/sections/equipmentWarehouse/components/cameraModal.less

@ -3,4 +3,7 @@
}
.semi-radio-cardRadioGroup_checked .voice{
color: #1859C1;
}
.semi-radio-cardRadioGroup_checked .switching{
color: #1859C1;
}

192
code/VideoAccess-VCMP/web/client/src/sections/equipmentWarehouse/components/cascadeCamera.jsx

@ -0,0 +1,192 @@
import React, { useState ,useRef,useEffect,useImperativeHandle} from 'react'
import { connect } from "react-redux";
import { Form,Row,Col,CheckboxGroup, Checkbox,Radio,Input } from '@douyinfe/semi-ui';
import { IconEdit,IconPlayCircle } from '@douyinfe/semi-icons';
import "./cameraModal.less";
function cascadeCamera({dRef}){
const form = useRef();
const [memoryList,setMemoryList] = useState([
{
id:1,
value:'8g'
},{
id:2,
value:'16g'
},{
id:3,
value:'32g'
},{
id:4,
value:'64g'
},{
id:5,
value:'128g'
},{
id:6,
value:'256g'
},{
id:7,
value:'>256g'
}
])//
const [nvrCheckList, setNvrCheckList] = useState([]);//nvr
const [NVRcameraList,setNVRcameraList]=useState([])//nvr
const [isAllChoose,setIsAllChoose]=useState(false)//
const [equipmentNum,setEquipmentNum]=useState('')//nvr
function NvrChangeName(e,index){//nvr
let NvrchangeList = JSON.parse(JSON.stringify(NVRcameraList))
NvrchangeList[index].change=true
setNVRcameraList(NvrchangeList)
e.stopPropagation()
}
function nvronBlur(index){//nvr
let NvrchangeList = JSON.parse(JSON.stringify(NVRcameraList))
NvrchangeList[index].change=false
setNVRcameraList(NvrchangeList)
}
function inputchange(e,index){//nvr
let NvrchangeList = JSON.parse(JSON.stringify(NVRcameraList))
NvrchangeList[index].name=e
setNVRcameraList(NvrchangeList)
}
function toggle(e,index){//nvr
let NvrchangeList = JSON.parse(JSON.stringify(NVRcameraList))
NvrchangeList[index].support=e.target.checked
setNVRcameraList(NvrchangeList)
e.stopPropagation()
}
function allChoose(e){///
let chooseList=[]
if(NVRcameraList.length==nvrCheckList.length){
setNvrCheckList([])
setIsAllChoose(false)
}
else{
for (let index = 0; index < NVRcameraList.length; index++) {
chooseList.push(NVRcameraList[index].id)
}
setNvrCheckList(chooseList)
setIsAllChoose(true)
}
}
function playVideo(e) {//nvr
console.log('22222222222222222');
e.stopPropagation()
}
useImperativeHandle(dRef,() => ({//
//
cascadeCameraForm : form.current.validate,
resetCascadeCamera : form.current.reset,
setNVRcameraList : setNVRcameraList,
setNvrCheckList : setNvrCheckList,
setIsAllChoose : setIsAllChoose,
}))
return (
<>
<Form
allowEmpty
labelPosition='left'
labelAlign='left'
labelWidth= '115px'
onValueChange={values=>{console.log(values);setEquipmentNum(values.equipmentNum)}}
getFormApi={formApi => form.current = formApi}>
<Row>
<Col span={12}>
<Form.Input field='foreignDomainName' label='外域名称:' initValue={''} placeholder='请输入外域名称' style={{ width:307 }}
rules={[
{ required: true, message: '请输入外域名称' }
]}/>
</Col>
<Col span={12}>
<Form.Select label="级联方式:" field='cascadeMode' placeholder='请选择级联方式' style={{ width:307 }}
rules={[
{ required: true, message: '请选择输入级联方式' }
]}>
{memoryList.map((item,index)=>(
<Form.Select.Option key={index} value={item.id}>{item.value}</Form.Select.Option>
))}
</Form.Select>
</Col>
<Col span={12}>
<Form.Select label="SIP编号:" field='sipNum' placeholder='请选择SIP编号' style={{ width:307 }}
rules={[
{ required: true, message: '请选择SIP编号' }
]}>
{memoryList.map((item,index)=>(
<Form.Select.Option key={index} value={item.id}>{item.value}</Form.Select.Option>
))}
</Form.Select>
</Col>
<Col span={24}>
{NVRcameraList.length>0?<div style={{display: 'flex',alignItems: 'center',justifyContent: 'flex-end',marginRight:19}}>
<Radio
checked={isAllChoose}
mode="advanced"
onChange={e=>allChoose(e)}
aria-label="全选">
全选
</Radio>
</div>:''}
</Col>
</Row>
<Row>
<CheckboxGroup type='pureCard' direction='vertical' aria-label="视频流获取"
value={nvrCheckList}
onChange={(nvrCheck) => {
setNvrCheckList(nvrCheck);
console.log('11111111111',nvrCheck);
if(NVRcameraList.length==nvrCheck.length){
setIsAllChoose(true)
}
else{
setIsAllChoose(false)
}
}}>
{NVRcameraList.length>0?NVRcameraList.map((item,index)=>(
<Col key={index} span={12} style={{display:'flex',justifyContent:'center',marginTop:12}}>
<Checkbox value={item.id}
extra={
<div>
<div style={{display:'flex',alignItems:'center',justifyContent:'space-between',height:30}}>
<div>通道名称{item.change?<Input autofocus style={{width:100}} value={item.name} onChange={e=>inputchange(e,index)} onBlur={()=>nvronBlur(index)}></Input>:item.name}</div>
<div style={{display:'flex',alignItems:'center'}}>
<IconEdit
style={{fontSize:16,marginLeft:18,cursor: "pointer",color:'#1859C1'}}
onClick={e=>NvrChangeName(e,index)}/>
</div>
</div>
<div style={{marginTop:8,width:246}}>设备编号{item.number}</div>
<div style={{marginTop:12,display:'flex',justifyContent:'space-between',alignItems:'center'}}>
<IconPlayCircle size='extra-large' style={{color:'#1859C1',}} onClick={e=>playVideo(e)}/>
<Radio
checked={item.support}
mode="advanced"
onChange={e=>toggle(e,index)}
aria-label="单选"
>
云台支持
</Radio>
</div>
</div>
}
style={{width:280,border:'1px solid #F9F9F9',}}>
</Checkbox>
</Col>
)):''}
</CheckboxGroup>
</Row>
</Form>
</>
);
}
function mapStateToProps(state) {
const { auth, global, members } = state;
return {
loading: members.isRequesting,
user: auth.user,
actions: global.actions,
members: members.data,
};
}
export default connect(mapStateToProps)(cascadeCamera);

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

@ -0,0 +1,262 @@
import React, { useState ,useRef,useEffect,useImperativeHandle} from 'react'
import { connect } from "react-redux";
import { Form,Row,Col} from '@douyinfe/semi-ui';
import "./cameraModal.less";
function fluoriteCamera({cRef}){
const { TextArea } = Form;
const form = useRef();
const [cloud,setcloud] = useState('')//
const [voice,setvoice] = useState('')//
const [switching,setSwitching] = useState('')//
const [memoryList,setMemoryList] = useState([
{
id:1,
value:'8g'
},{
id:2,
value:'16g'
},{
id:3,
value:'32g'
},{
id:4,
value:'64g'
},{
id:5,
value:'128g'
},{
id:6,
value:'256g'
},{
id:7,
value:'>256g'
}
])//
function handleLocation(){//
window.open('https://lbs.amap.com/tools/picker','_blank')
}
function positionForm(val){//
let zz = /^(-?\d+)(\.\d+)?$/
if(!val){
return '请输入或拾取高德经纬度坐标'
}
else if(val.split(',').length!=2){
return '请输入格式为116.354169,39.835452的经纬度坐标'
}
else if(!zz.test(val.split(',')[0])){
return '只能填写数字'
}
else if(!zz.test(val.split(',')[1])){
return '只能填写数字'
}
else{
return ''
}
}
useImperativeHandle(cRef,() => ({//
//
fluoriteCameraForm : form.current.validate,
resetFluoriteCamera : form.current.reset
}))
return (
<>
<Form
labelPosition='left'
labelAlign='left'
labelWidth= '115px'
onValueChange={values=>console.log(values)}
getFormApi={formApi => form.current = formApi}>
<Row>
<Col span={12}>
{/* 设备名称 */}
<Form.Input field='UserName' label='设备名称:' initValue={''} placeholder='请输入设备名称、常用项目或位置定义' style={{ width:307 }}
rules={[
{ required: true, message: '请输入设备名称' }
]}/>
{/* 高清切换 */}
<Form.RadioGroup
label="高清切换:"
field='hdSwitching'
type='pureCard'
direction='horizontal'
style={{padding:0,paddingTop:1,paddingBottom:1}}
rules={[
{ required: true, message: '请选择高清切换' }
]}
onChange={(checked) => {
console.log(checked.target.value);
if(checked.target.value=='yes'){
setSwitching('yes')
}
else{
setSwitching('no')
}
}}>
<Form.Radio value="yes" style={{width:58,height:30,padding:0,margin:0,background:'#F9F9F9'}}>
<div className='switching' style={{width:58,height:30,textAlign:'center',lineHeight:'30px'}}>
支持
</div>
{switching=='yes'?<div style={{position: 'absolute', top: '-2px', right: '-1px'}}>
<img src="/assets/images/background/formchoose.png" alt="1" />
</div>:''}
</Form.Radio>
<Form.Radio value="no" style={{width:58,height:30,padding:0,margin:0,marginLeft:18,background:'#F9F9F9'}}>
<div className='switching' style={{width:58,height:30,textAlign:'center',lineHeight:'30px'}}>
不支持
</div>
{switching=='no'?<div style={{position: 'absolute', top: '-2px', right: '-1px'}}>
<img src="/assets/images/background/formchoose.png" alt="1" />
</div>:''}
</Form.Radio>
</Form.RadioGroup>
{/* 安装位置 */}
<div style={{display:'flex'}}>
<Form.Input field='Use11rName1312' label='安装位置:' placeholder='请输入或拾取高德经纬度坐标' style={{ width:270 }}
validate={positionForm}
rules={[
{ required: true, message: '请输入或拾取高德经纬度坐标' }
]}/>
<div style={{
width:32,
height:32,
background:"#1859C1",
marginLeft:4,
display:'flex',
justifyContent: 'center',
alignItems: 'center',
cursor: "pointer",
marginTop:12,
borderRadius: 3+'px'}}
onClick={handleLocation}>
<img src="../../../assets/images/background/location.png" width={16} height={20}/>
</div>
</div>
{/* 设备类型 */}
<div style={{display:'flex',}}>
<div>
<Form.Select label="设备类型:" field='business23' placeholder='请选择摄像头类型' style={{ width:160 }}
rules={[
{ required: true, message: '请选择摄像头类型' }
]}>
{memoryList.map((item,index)=>(
<Form.Select.Option key={index} value={item.id}>{item.value}</Form.Select.Option>
))}
</Form.Select>
</div>
<div style={{marginLeft:7}}>
<Form.Select noLabel='true' field='business244' placeholder='请选择能力' style={{ width:140 }}
rules={[
{ required: true, message: '请选择能力' }
]}>
{memoryList.map((item,index)=>(
<Form.Select.Option key={index} value={item.id}>{item.value}</Form.Select.Option>
))}
</Form.Select>
</div>
</div>
</Col>
<Col span={12}>
{/* 云台支持 */}
<Form.RadioGroup
label="云台支持:"
field='role'
type='pureCard'
direction='horizontal'
style={{padding:0,paddingTop:1,paddingBottom:1}}
rules={[
{ required: true, message: '请选择云台支持' }
]}
onChange={(checked) => {
console.log(checked.target.value);
if(checked.target.value=='yes'){
setcloud('yes')
}
else{
setcloud('no')
}
}}>
<Form.Radio value="yes" style={{width:58,height:30,padding:0,margin:0,background:'#F9F9F9'}}>
<div className='cloud' style={{width:58,height:30,textAlign:'center',lineHeight:'30px'}}>
支持
</div>
{cloud=='yes'?<div style={{position: 'absolute', top: '-2px', right: '-1px'}}>
<img src="/assets/images/background/formchoose.png" alt="1" />
</div>:''}
</Form.Radio>
<Form.Radio value="no" style={{width:58,height:30,padding:0,margin:0,marginLeft:18,background:'#F9F9F9'}}>
<div className='cloud' style={{width:58,height:30,textAlign:'center',lineHeight:'30px'}}>
不支持
</div>
{cloud=='no'?<div style={{position: 'absolute', top: '-2px', right: '-1px'}}>
<img src="/assets/images/background/formchoose.png" alt="1" />
</div>:''}
</Form.Radio>
</Form.RadioGroup>
{/* 内存 */}
<div style={{display:'flex'}}>
<Form.Select label="内存:" field='business2' placeholder='未安装' style={{ width:92 }}>
{memoryList.map((item,index)=>(
<Form.Select.Option key={index} value={item.id}>{item.value}</Form.Select.Option>
))}
</Form.Select>
{/* 语音支持 */}
<div style={{marginLeft:18}}>
<Form.RadioGroup
labelWidth= '76px'
label="语音支持:"
field='role2'
type='pureCard'
direction='horizontal'
style={{padding:0,paddingTop:1,paddingBottom:1}}
onChange={(checked) => {
console.log(checked.target.value);
if(checked.target.value=='yes'){
setvoice('yes')
}
else{
setvoice('no')
}
}}>
<Form.Radio value="yes" style={{width:58,height:30,padding:0,margin:0,background:'#F9F9F9'}}>
<div className='voice' style={{width:58,height:30,textAlign:'center',lineHeight:'30px'}}>
支持
</div>
{voice=='yes'?<div style={{position: 'absolute', top: '-2px', right: '-1px'}}>
<img src="/assets/images/background/formchoose.png" alt="1" />
</div>:''}
</Form.Radio>
<Form.Radio value="no" style={{width:58,height:30,padding:0,margin:0,marginLeft:18,background:'#F9F9F9'}}>
<div className='voice' style={{width:58,height:30,textAlign:'center',lineHeight:'30px'}}>
不支持
</div>
{voice=='no'?<div style={{position: 'absolute', top: '-2px', right: '-1px'}}>
<img src="/assets/images/background/formchoose.png" alt="1" />
</div>:''}
</Form.Radio>
</Form.RadioGroup>
</div>
</div>
{/* RTMP地址接入 */}
<TextArea
style={{ width:320, height: 90 }}
field='description'
label='RTMP地址接入:'
placeholder='请输入RTMP地址接入'
/>
</Col>
</Row>
</Form>
</>
);
}
function mapStateToProps(state) {
const { auth, global, members } = state;
return {
loading: members.isRequesting,
user: auth.user,
actions: global.actions,
members: members.data,
};
}
export default connect(mapStateToProps)(fluoriteCamera);

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

@ -0,0 +1,224 @@
import React, { useState ,useRef,useEffect,useImperativeHandle} from 'react'
import { connect } from "react-redux";
import { Form,Row,Col } from '@douyinfe/semi-ui';
import "./cameraModal.less";
function ipcCamera({aRef}){
const { TextArea } = Form;
const form = useRef();
const [cloud,setcloud] = useState('')//
const [voice,setvoice] = useState('')//
const [memoryList,setMemoryList] = useState([
{
id:1,
value:'8g'
},{
id:2,
value:'16g'
},{
id:3,
value:'32g'
},{
id:4,
value:'64g'
},{
id:5,
value:'128g'
},{
id:6,
value:'256g'
},{
id:7,
value:'>256g'
}
])//
function handleLocation(){//
window.open('https://lbs.amap.com/tools/picker','_blank')
}
function positionForm(val){//
let zz = /^(-?\d+)(\.\d+)?$/
if(!val){
return '请输入或拾取高德经纬度坐标'
}
else if(val.split(',').length!=2){
return '请输入格式为116.354169,39.835452的经纬度坐标'
}
else if(!zz.test(val.split(',')[0])){
return '只能填写数字'
}
else if(!zz.test(val.split(',')[1])){
return '只能填写数字'
}
else{
return ''
}
}
useImperativeHandle(aRef,() => ({//
//
ipcCameraForm : form.current.validate,
resetIpcCamera : form.current.reset
}))
return (
<>
<Form
labelPosition='left'
labelAlign='left'
labelWidth= '115px'
onValueChange={values=>console.log(values)}
getFormApi={formApi => form.current = formApi}>
<Row>
<Col span={12}>
<Form.Input field='UserName' label='设备名称:' initValue={''} placeholder='请输入设备名称、常用项目或位置定义' style={{ width:307 }}
rules={[
{ required: true, message: '请输入设备名称' }
]}/>
<Form.Input field='Use11rName11' label='设备厂家:' placeholder='请选择设备厂家' style={{ width:307 }}/>
<div style={{display:'flex'}}>
<Form.Input field='Use11rName1312' label='安装位置:' placeholder='请输入或拾取高德经纬度坐标' style={{ width:270 }}
validate={positionForm}
rules={[
{ required: true, message: '请输入或拾取高德经纬度坐标' }
]}/>
<div style={{
width:32,
height:32,
background:"#1859C1",
marginLeft:4,
display:'flex',
justifyContent: 'center',
alignItems: 'center',
cursor: "pointer",
marginTop:12,
borderRadius: 3+'px'}}
onClick={handleLocation}>
<img src="../../../assets/images/background/location.png" width={16} height={20}/>
</div>
</div>
<div>
<Form.Input field='UserName11' label='设备编号接入:' initValue={''} placeholder='请输入设备编号' style={{ width:307 }}/>
</div>
</Col>
<Col span={12}>
<Form.RadioGroup
label="云台支持:"
field='role'
type='pureCard'
direction='horizontal'
style={{padding:0,paddingTop:1,paddingBottom:1}}
rules={[
{ required: true, message: '请选择云台支持' }
]}
onChange={(checked) => {
console.log(checked.target.value);
if(checked.target.value=='yes'){
setcloud('yes')
}
else{
setcloud('no')
}
}}>
<Form.Radio value="yes" style={{width:58,height:30,padding:0,margin:0,background:'#F9F9F9'}}>
<div className='cloud' style={{width:58,height:30,textAlign:'center',lineHeight:'30px'}}>
支持
</div>
{cloud=='yes'?<div style={{position: 'absolute', top: '-2px', right: '-1px'}}>
<img src="/assets/images/background/formchoose.png" alt="1" />
</div>:''}
</Form.Radio>
<Form.Radio value="no" style={{width:58,height:30,padding:0,margin:0,marginLeft:18,background:'#F9F9F9'}}>
<div className='cloud' style={{width:58,height:30,textAlign:'center',lineHeight:'30px'}}>
不支持
</div>
{cloud=='no'?<div style={{position: 'absolute', top: '-2px', right: '-1px'}}>
<img src="/assets/images/background/formchoose.png" alt="1" />
</div>:''}
</Form.Radio>
</Form.RadioGroup>
<div style={{display:'flex'}}>
<Form.Select label="内存:" field='business2' placeholder='未安装' style={{ width:92 }}>
{memoryList.map((item,index)=>(
<Form.Select.Option key={index} value={item.id}>{item.value}</Form.Select.Option>
))}
</Form.Select>
<div style={{marginLeft:18}}>
<Form.RadioGroup
labelWidth= '76px'
label="语音支持:"
field='role2'
type='pureCard'
direction='horizontal'
style={{padding:0}}
onChange={(checked) => {
console.log(checked.target.value);
if(checked.target.value=='yes'){
setvoice('yes')
}
else{
setvoice('no')
}
}}>
<Form.Radio value="yes" style={{width:58,height:30,padding:0,margin:0,background:'#F9F9F9'}}>
<div className='voice' style={{width:58,height:30,textAlign:'center',lineHeight:'30px'}}>
支持
</div>
{voice=='yes'?<div style={{position: 'absolute', top: '-2px', right: '-1px'}}>
<img src="/assets/images/background/formchoose.png" alt="1" />
</div>:''}
</Form.Radio>
<Form.Radio value="no" style={{width:58,height:30,padding:0,margin:0,marginLeft:18,background:'#F9F9F9'}}>
<div className='voice' style={{width:58,height:30,textAlign:'center',lineHeight:'30px'}}>
不支持
</div>
{voice=='no'?<div style={{position: 'absolute', top: '-2px', right: '-1px'}}>
<img src="/assets/images/background/formchoose.png" alt="1" />
</div>:''}
</Form.Radio>
</Form.RadioGroup>
</div>
</div>
<TextArea
style={{ width:320, height: 90 }}
field='description'
label='RTMP地址接入:'
placeholder='请输入RTMP地址接入'
/>
</Col>
<Col span={18}>
<div style={{display:'flex',}}>
<div>
<Form.Select label="设备类型:" field='business23' placeholder='请选择摄像头类型' style={{ width:160 }}
rules={[
{ required: true, message: '请选择摄像头类型' }
]}>
{memoryList.map((item,index)=>(
<Form.Select.Option key={index} value={item.id}>{item.value}</Form.Select.Option>
))}
</Form.Select>
</div>
<div style={{marginLeft:7}}>
<Form.Select noLabel='true' field='business244' placeholder='请选择能力' style={{ width:140 }}
rules={[
{ required: true, message: '请选择能力' }
]}>
{memoryList.map((item,index)=>(
<Form.Select.Option key={index} value={item.id}>{item.value}</Form.Select.Option>
))}
</Form.Select>
</div>
</div>
</Col>
</Row>
</Form>
</>
);
}
function mapStateToProps(state) {
const { auth, global, members } = state;
return {
loading: members.isRequesting,
user: auth.user,
actions: global.actions,
members: members.data,
};
}
export default connect(mapStateToProps)(ipcCamera);

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

@ -0,0 +1,182 @@
import React, { useState ,useRef,useEffect,useImperativeHandle} from 'react'
import { connect } from "react-redux";
import { Form,Row,Col,Button,CheckboxGroup, Checkbox,Radio,Input } from '@douyinfe/semi-ui';
import { IconEdit,IconPlayCircle } from '@douyinfe/semi-icons';
import "./cameraModal.less";
function nvrCamera({cRef}){
const form = useRef();
const [nvrCheckList, setNvrCheckList] = useState([]);//nvr
const [NVRcameraList,setNVRcameraList]=useState([])//nvr
const [isAllChoose,setIsAllChoose]=useState(false)//
const [equipmentNum,setEquipmentNum]=useState('')//nvr
function NvrChangeName(e,index){//nvr
let NvrchangeList = JSON.parse(JSON.stringify(NVRcameraList))
NvrchangeList[index].change=true
setNVRcameraList(NvrchangeList)
e.stopPropagation()
}
function nvronBlur(index){//nvr
let NvrchangeList = JSON.parse(JSON.stringify(NVRcameraList))
NvrchangeList[index].change=false
setNVRcameraList(NvrchangeList)
}
function inputchange(e,index){//nvr
let NvrchangeList = JSON.parse(JSON.stringify(NVRcameraList))
NvrchangeList[index].name=e
setNVRcameraList(NvrchangeList)
}
function toggle(e,index){//nvr
let NvrchangeList = JSON.parse(JSON.stringify(NVRcameraList))
NvrchangeList[index].support=e.target.checked
setNVRcameraList(NvrchangeList)
e.stopPropagation()
}
function getVideoList(){
form.current.validate().then(values=>{//
let chooseList=[]
let nvrCameraList=[{
id:10,
name:'南昌县1',
number:'111111111111111111',
support:false,
change:false,
},{
id:20,
name:'南昌县2',
number:'222222222222222222',
support:false,
change:false,
},{
id:30,
name:'南昌县3',
number:'333333333333333333',
support:false,
change:false,
},{
id:40,
name:'南昌县4',
number:'444444444444444444',
support:false,
change:false,
}]
setNVRcameraList(nvrCameraList)
for (let index = 0; index < nvrCameraList.length; index++) {
chooseList.push(nvrCameraList[index].id)
}
setNvrCheckList(chooseList)
setIsAllChoose(true)
})
.catch(errors=>{//
console.log('errors',errors);
})
}
function allChoose(e){///
let chooseList=[]
if(NVRcameraList.length==nvrCheckList.length){
setNvrCheckList([])
setIsAllChoose(false)
}
else{
for (let index = 0; index < NVRcameraList.length; index++) {
chooseList.push(NVRcameraList[index].id)
}
setNvrCheckList(chooseList)
setIsAllChoose(true)
}
}
function playVideo(e) {//nvr
console.log('22222222222222222');
e.stopPropagation()
}
// useImperativeHandle(cRef,() => ({//
// //aa
// getDate : form.current.validate,
// // resetFluoriteCamera : form.current.reset
// }))
return (
<>
<Form
allowEmpty
labelPosition='left'
labelAlign='left'
labelWidth= '115px'
onValueChange={values=>{setEquipmentNum(values.equipmentNum)}}
getFormApi={formApi => form.current = formApi}>
<div style={{display:'flex'}}>
<Form.Input field='equipmentNum' maxLength='39' label='设备编号:' initValue={''} placeholder='请输入设备编号' style={{ width:307 }}
rules={[
{ required: true, message: '请输入设备编号' },
{ pattern: '^[A-Za-z0-9]+$', message: '只能输入数字或者字母' }
]}/>
<Button disabled={!equipmentNum.length>0} theme='solid' type='primary' onClick={()=>{getVideoList()}} style={{ marginLeft: 8,marginTop:12 }}>视频流获取</Button>
{NVRcameraList.length>0?<div style={{display: 'flex',alignItems: 'center',marginLeft: 211}}>
<Radio
checked={isAllChoose}
mode="advanced"
onChange={e=>allChoose(e)}
aria-label="全选">
全选
</Radio>
</div>:''}
</div>
<Row>
<CheckboxGroup type='pureCard' direction='vertical' aria-label="视频流获取"
value={nvrCheckList}
onChange={(nvrCheck) => {
setNvrCheckList(nvrCheck);
// console.log('11111111111',nvrCheck);
if(NVRcameraList.length==nvrCheck.length){
setIsAllChoose(true)
}
else{
setIsAllChoose(false)
}
}}>
{NVRcameraList.length>0?NVRcameraList.map((item,index)=>(
<Col key={index} span={12} style={{display:'flex',justifyContent:'center',marginTop:12}}>
<Checkbox value={item.id}
extra={
<div>
<div style={{display:'flex',alignItems:'center',justifyContent:'space-between',height:30}}>
<div>通道名称{item.change?<Input autofocus style={{width:100}} value={item.name} onChange={e=>inputchange(e,index)} onBlur={()=>nvronBlur(index)}></Input>:item.name}</div>
<div style={{display:'flex',alignItems:'center'}}>
<IconEdit
style={{fontSize:16,marginLeft:18,cursor: "pointer",color:'#1859C1'}}
onClick={e=>NvrChangeName(e,index)}/>
</div>
</div>
<div style={{marginTop:8,width:246}}>设备编号{item.number}</div>
<div style={{marginTop:12,display:'flex',justifyContent:'space-between',alignItems:'center'}}>
<IconPlayCircle size='extra-large' style={{color:'#1859C1',}} onClick={e=>playVideo(e)}/>
<Radio
checked={item.support}
mode="advanced"
onChange={e=>toggle(e,index)}
aria-label="单选"
>
云台支持
</Radio>
</div>
</div>
}
style={{width:280,border:'1px solid #F9F9F9',}}>
</Checkbox>
</Col>
)):''}
</CheckboxGroup>
</Row>
</Form>
</>
);
}
function mapStateToProps(state) {
const { auth, global, members } = state;
return {
loading: members.isRequesting,
user: auth.user,
actions: global.actions,
members: members.data,
};
}
export default connect(mapStateToProps)(nvrCamera);

5
code/VideoAccess-VCMP/web/client/src/sections/equipmentWarehouse/components/nvrModal.jsx

@ -135,7 +135,10 @@ function nvrModal(props){
]}/>
</Col>
<Col span={24}>
<Form.Select label="设备厂家:" field='venderId' initValue={nvrData.venderId||''} placeholder='请选择设备厂家' style={{ width: 421 }}>
<Form.Select label="设备厂家:" field='venderId' initValue={nvrData.venderId||null} placeholder='请选择设备厂家' style={{ width: 421 }}
rules={[
{ required: true, message: '请选择设备厂家' }
]}>
{vender.map((item,index)=>(
<Form.Select.Option key={index} value={item.id}>{item.name}</Form.Select.Option>
))}

8
code/VideoAccess-VCMP/web/client/src/sections/equipmentWarehouse/components/setup.jsx

@ -24,12 +24,12 @@ function Setup(props) {
useEffect(() => {
//
const ISgetItem1 = localStorage.getItem(SETUPS);
const ISgetItem2 = localStorage.getItem(CAMERAS);
const nvrItem = localStorage.getItem(SETUPS);
const cameraItem = localStorage.getItem(CAMERAS);
if (cameraSetup) {
setCheck(ISgetItem2 ? JSON.parse(ISgetItem2) : []);
setCheck(cameraItem ? JSON.parse(cameraItem) : []);
} else {
setCheck(ISgetItem1 ? JSON.parse(ISgetItem1) : []);
setCheck(nvrItem ? JSON.parse(nvrItem) : []);
}
ischeck();
}, []);

447
code/VideoAccess-VCMP/web/client/src/sections/equipmentWarehouse/components/sideSheet.jsx

@ -1,71 +1,134 @@
import React, { useState, useEffect } from "react";
import { SideSheet, Tabs, TabPane } from "@douyinfe/semi-ui";
import { SideSheet, Tabs, TabPane, Button } from "@douyinfe/semi-ui";
import copy from "copy-to-clipboard";
function SideSheets(props) {
const { dispatch, actions, user, loading, visible, close, SETUPS } = props;
const [checkeds, setCheckeds] = useState([]);
const [check, setCheck] = useState([]);
const {
dispatch,
actions,
user,
loading,
visible,
close,
SETUPS,
cameraSetup,
} = props;
const [clickStyle, setclickStyle] = useState();
const list = [
{
name: "项目名称",
a: "南昌县智慧环保",
b: "南昌县智慧环保",
c: "南昌市市政隧道综合管理平台",
c: "南昌市市政隧道综合管理 哦哦哦哦哦 哦哦哦哦 哦哦哦哦 哦哦哦哦哦哦哦哦哦 哦",
d: "C",
},
{ name: "关联结构物", a: "a", b: "B", c: "C", d: "C" },
{ name: "关联测点", a: "a", b: "B", c: "C", d: "C" },
{ name: "关联监测因素", a: "a", b: "B", c: "C", d: "C" },
];
const IFname = [
{ name: "设备名称:", value: "" },
{ name: "设备编号:", value: "" },
{ name: "接入方式:", value: "" },
{ name: "厂商:", value: "" },
{ name: "添加账号:", value: "" },
{ name: "添加时间:", value: "" },
{ name: "行政区别:", value: "" },
{ name: "设备安装位置:", value: "" },
{ name: "SIP服务编号:", value: "" },
{ name: "SIP域:", value: "" },
{ name: "SIP端口号:", value: "" },
{ name: "通道数量:", value: "" },
{ name: "心跳周期:", value: "" },
{ name: "最大心跳次数:", value: "" },
{ name: "注册密码:", value: "" },
{ name: "注册有效期::", value: "" },
{ name: "接入识别模块:", value: "" },
];
function pp() {
let str = [];
console.log(IFname.length);
for (let i = 0; i < IFname.length; i++) {
if (i === 0) {
str.push(<div>基础</div>);
}
if (0 <= i && i < 6) {
console.log(IFname[i].name);
str.push(
<div>
<span>{IFname[i].name}</span>
<span>{IFname[i].value}</span>
</div>
);
}
}
return str;
}
console.log(IFname);
const information = {
nvr: [
{
name: "基础信息",
basics: [
{ name: "设备名称:", value: "消火栓呼呼呼呼" },
{ name: "设备编号:", value: "D50F2049010B" },
{ name: "接入方式:", value: "NJBJ858G68H" },
{ name: "厂商:", value: "哇哇哇哇哇" },
{ name: "添加账号:", value: "Superadmin" },
{ name: "添加时间:", value: "2022-09-09" },
{
name: "行政区别:",
value: "江西省-南昌市-南昌县",
difference: "difference",
},
{
name: "设备安装位置:",
value: "江西省南昌县小蓝经开区江西飞尚科技有限公司",
difference: "line",
},
],
},
{
name: "接入信息",
access: [
{ name: "SIP服务编号:", value: "1111111111" },
{ name: "SIP域:", value: "KGU876J87" },
{ name: "SIP端口号:", value: "KGU876J87" },
{ name: "通道数量:", value: "16通道" },
{ name: "心跳周期:", value: "3600s" },
{ name: "最大心跳次数:", value: "3次" },
{ name: "注册密码:", value: "**********" },
{ name: "注册有效期::", value: "3600s" },
{ name: "接入识别模块:", value: "sssss" },
],
},
{
name: "摄像头信息",
CameraInformation: [
{
name: "通道1",
value1: "145641201564-1",
value2: "哦哦哦我我我喔噢喔喔我",
},
{
name: "通道2",
value1: "145641201564-2",
value2: "哦哦哦哦哦噢噢欧欧哦",
},
],
},
],
camera: [
{
name: "基础信息",
basics: [
{ name: "设备名称:", value: "消火栓呼呼呼呼" },
{ name: "SIP编号/设备编号:", value: "D50F2049010B" },
{ name: "接入方式:", value: "NJBJ858G68H" },
{ name: "厂商:", value: "哇哇哇哇哇" },
{ name: "添加账号:", value: "Superadmin" },
{ name: "添加时间:", value: "2022-09-09" },
{
name: "设备安装位置:",
value: "江西省南昌县小蓝经开区江西飞尚科技有限公司",
difference: "line",
},
],
},
{
name: "复制直播地址",
liveBroadcast: [
{ name: "HLS地址", value: "" },
{ name: "FLV地址", value: "" },
{ name: "RTMP地址", value: "" },
{ name: "EZOPEN地址", value: "" },
{ name: "ONVIF地址", value: "" },
],
},
{
name: "复制回收地址",
recovery: [
{ name: "云储存地址", value: "" },
{ name: "本地储存地址", value: "" },
],
},
],
};
const styles = {
width: 180,
height: 64,
textAlign: "center",
// background: "#FFFFFF",
background: "url(/assets/images/background/backGround.png)",
backgroundSize: "100% 100%",
padding: "12px 17px",
padding: "12px 17px 12px 17px",
margin: "30px 0 0 10px",
lineHeight: "20px",
position: "relative",
lineHeight: "24px",
overflow: "hidden",
textOverflow: "ellipsis",
display: "-webkit-box",
WebkitLineClamp: "2",
WebkitBoxOrient: "vertical",
zIndex: 5,
};
@ -74,7 +137,7 @@ function SideSheets(props) {
return (
<SideSheet
visible={visible}
title="智慧设备NVR"
title={cameraSetup ? "污水管理出出口" : "智慧设备NVR"}
style={{ background: "#F9FBFF" }}
size="large"
onCancel={() => {
@ -84,7 +147,7 @@ function SideSheets(props) {
<Tabs type="line">
<TabPane tab="项目信息" itemKey="1">
<div style={{ display: "flex", justifyContent: "space-evenly" }}>
{list.map((item) => {
{list.map((item, index) => {
return (
<div
key={item.name}
@ -97,10 +160,15 @@ function SideSheets(props) {
style={{
lineHeight: "32px",
background: "#1859C1",
color: "#FFFFFF",
borderRadius: 3,
textAlign: "center",
}}
>
<img
src={`/assets/images/background/projectIcon${index}.png`}
style={{ marginRight: 10 }}
/>
{item.name}
</div>
<div style={styles}>{item.a}</div>
@ -113,7 +181,284 @@ function SideSheets(props) {
</div>
</TabPane>
<TabPane tab="设备信息" itemKey="2">
{pp()}
{(cameraSetup ? information.camera : information.nvr).map(
(item, index) => {
let str = [];
if (index > 0) {
str.push(
<img
src="/assets/images/background/straightline.png"
key={"img" + index}
alt="无法显示"
style={{ width: 872, display: "block", marginTop: 24 }}
/>
);
}
str.push(
<div
key={"name" + index}
style={{
fontWeight: "600",
color: "#1859C1",
margin: "15px 0 12px 40px",
}}
>
{item.name}
{cameraSetup ? (
index == 1 ? (
<div
style={{
display: "inline-block",
float: "right",
marginRight: 20,
}}
>
<Button style={{ marginRight: 20 }} theme="solid">
高清
</Button>
<Button>标清</Button>
</div>
) : (
""
)
) : (
""
)}
</div>
);
//
if (index == 0) {
item.basics.map((item, index) => {
if (item.difference == "line") {
str.push(
<div key={"basics" + index} style={{ marginTop: 14 }}>
<span
style={{
width: 150,
display: "inline-block",
textAlign: "right",
fontWeight: "600",
color: "rgba(0, 0, 0, 0.45)",
}}
>
{item.name}
</span>
<span style={{ fontWeight: "600", color: " #34383E" }}>
{item.value}
</span>
</div>
);
} else {
str.push(
<div
key={"basics" + index}
style={{
width: "50%",
display: "inline-block",
marginTop: 20,
}}
>
<span
style={{
width: 150,
display: "inline-block",
textAlign: "right",
fontWeight: "600",
color: "rgba(0, 0, 0, 0.45)",
}}
>
{item.name}
</span>
<span style={{ fontWeight: "600", color: " #34383E" }}>
{item.value}
</span>
</div>
);
}
return str;
});
}
///
if (index == 1) {
(cameraSetup ? item.liveBroadcast : item.access).map(
(item, index) => {
if (cameraSetup) {
str.push(
<div
key={"liveBroadcast" + index}
style={{
width: 120,
height: 130,
display: "inline-block",
borderRadius: 4,
border: "1px solid #D9D9D9",
margin: "20px 0 0 40px",
textAlign: "center",
color: clickStyle == item.name ? "white" : "",
background:
clickStyle == item.name ? "#1859C1" : "",
}}
onClick={() => {
copy(item.name);
// alert("");
setclickStyle(item.name);
}}
>
<img
src={`/assets/images/background/${
clickStyle == item.name
? "sewage_camera2"
: "sewage_camera1"
}.png`}
style={{ margin: "20px 0 8px 0" }}
/>
<div>
{item.name}
<img
src={`/assets/images/background/${
clickStyle == item.name ? "copy2" : "copy1"
}.png`}
style={{
paddingBottom: 10,
display: "inline-block",
width: 10,
height: 20,
}}
/>
</div>
</div>
);
} else {
str.push(
<div
key={"access" + index}
style={{
width: "50%",
display: "inline-block",
marginTop: 20,
}}
>
<span
style={{
width: 150,
display: "inline-block",
textAlign: "right",
fontWeight: "600",
color: "rgba(0, 0, 0, 0.45)",
}}
>
{item.name}
</span>
<span
style={{ fontWeight: "600", color: " #34383E" }}
>
{item.value}
</span>
</div>
);
}
return str;
}
);
}
///
if (index == 2) {
(cameraSetup ? item.recovery : item.CameraInformation).map(
(item, index) => {
if (cameraSetup) {
str.push(
<div
key={"liveBroadcast" + index}
style={{
width: 120,
height: 130,
display: "inline-block",
borderRadius: 4,
border: "1px solid #D9D9D9",
margin: "20px 0 0 40px",
textAlign: "center",
color: clickStyle == item.name ? "white" : "",
background:
clickStyle == item.name ? "#1859C1" : "",
}}
onClick={() => {
copy(item.name);
// alert("");
setclickStyle(item.name);
}}
>
<img
src={`/assets/images/background/${
clickStyle == item.name ? "store2" : "store1"
}.png`}
style={{ margin: "20px 0 8px 0" }}
/>
<div>
{item.name}
<img
src={`/assets/images/background/${
clickStyle == item.name ? "copy2" : "copy1"
}.png`}
style={{
paddingBottom: 10,
display: "inline-block",
width: 10,
height: 20,
}}
/>
</div>
</div>
);
} else {
str.push(
<div
key={"access" + index}
style={{
width: "40%",
display: "inline-block",
borderRadius: 4,
border: "1px solid rgba(151, 151, 151, 0.2)",
margin: "16px 0 0 36px",
color: "rgba(0, 0, 0, 0.85)",
fontWeight: "600",
}}
>
<div
style={{
padding: "8px 10px",
borderBottom:
"1px solid rgba(151, 151, 151, 0.2)",
}}
>
<img
src={`/assets/images/background/camera.png`}
style={{ marginRight: 10 }}
/>
<div
style={{
display: "inline-block",
}}
>
{item.name}
</div>
</div>
<p style={{ margin: "16px 0 0 30px " }}>
{item.value1}
</p>
<p style={{ margin: "16px 0 20px 30px " }}>
{item.value2}
</p>
</div>
);
}
return str;
}
);
}
return str;
}
)}
</TabPane>
</Tabs>
</SideSheet>

23
code/VideoAccess-VCMP/web/client/src/sections/equipmentWarehouse/containers/camera.jsx

@ -26,10 +26,12 @@ const CameraHeader = (props) => {
setvenderList(res.payload.data);
});
//
localStorage.setItem(
CAMERAS,
JSON.stringify(["state", "type", "manufactor"])
);
localStorage.getItem(CAMERAS) == null
? localStorage.setItem(
CAMERAS,
JSON.stringify(["state", "type", "manufactor"])
)
: "";
attribute();
}, []);
@ -76,7 +78,7 @@ const CameraHeader = (props) => {
render: (_, row) => {
// console.log(row);
return (
<>
<div style={{ display: "flex" }}>
<Button theme="borderless">
<NvrModal
nvrData={row}
@ -91,11 +93,12 @@ const CameraHeader = (props) => {
theme="borderless"
onClick={() => {
setSideSheet(true);
setcameraSetup(true);
}}
>
查看
</Button>
<Button theme="borderless" size="large">播放</Button>
<Button theme="borderless">播放</Button>
<Button theme="borderless">禁用</Button>
<Button
theme="borderless"
@ -106,7 +109,7 @@ const CameraHeader = (props) => {
>
删除
</Button>
</>
</div>
);
},
},
@ -323,7 +326,7 @@ const CameraHeader = (props) => {
<Form.Select.Option value="yes">在线</Form.Select.Option>
<Form.Select.Option value="no">离线</Form.Select.Option>
</Form.Select>
<Form.Select
{/* <Form.Select
label="关联项目:"
labelPosition="left"
field="type3"
@ -336,7 +339,7 @@ const CameraHeader = (props) => {
<Form.Select.Option value="智慧水务">
飞尚科技2
</Form.Select.Option>
</Form.Select>
</Form.Select> */}
</Form>
<div
style={{
@ -498,8 +501,10 @@ const CameraHeader = (props) => {
{sideSheet ? (
<SideSheets
visible={true}
cameraSetup={cameraSetup}
close={() => {
setSideSheet(false);
setcameraSetup(false);
}}
/>
) : (

22
code/VideoAccess-VCMP/web/client/src/sections/equipmentWarehouse/containers/nvr.jsx

@ -1,6 +1,6 @@
import React, { useState, useEffect, useRef } from "react";
import { connect } from "react-redux";
import moment from 'moment'
import moment from "moment";
import { Button, Form, Input, Row, Table, Pagination } from "@douyinfe/semi-ui";
import "../style.less";
import NvrModal from "../components/nvrModal";
@ -23,10 +23,12 @@ const NvrHeader = (props) => {
setvenderList(res.payload.data);
});
//
localStorage.setItem(SETUPS, JSON.stringify(["a", "c", "d", "e"]));
localStorage.getItem(SETUPS) == null
? localStorage.setItem(SETUPS, JSON.stringify(["a", "c", "d", "e"]))
: "";
// ;
attribute();
}, []);
useEffect(() => {
equipmentGetNvr();
}, [query]);
@ -73,7 +75,7 @@ const NvrHeader = (props) => {
render: (_, row) => {
// console.log(row);
return (
<>
<div style={{ display: "flex" }}>
<Button theme="borderless">
<NvrModal
nvrData={row}
@ -101,7 +103,7 @@ const NvrHeader = (props) => {
>
删除
</Button>
</>
</div>
);
},
},
@ -143,8 +145,8 @@ const NvrHeader = (props) => {
title: "创建时间",
dataIndex: "createTime",
key: "f",
render: (text, r, index) => {
return moment(r.createTime).format('YYYY-MM-DD HH:MM:SS')
render: (text, r, index) => {
return moment(r.createTime).format("YYYY-MM-DD HH:MM:SS");
},
},
{
@ -297,7 +299,7 @@ const NvrHeader = (props) => {
<Form.Select.Option value="yes">在线</Form.Select.Option>
<Form.Select.Option value="no">离线</Form.Select.Option>
</Form.Select>
<Form.Select
{/* <Form.Select
label="关联项目:"
labelPosition="left"
field="type3"
@ -310,7 +312,7 @@ const NvrHeader = (props) => {
<Form.Select.Option value="智慧水务">
飞尚科技2
</Form.Select.Option>
</Form.Select>
</Form.Select> */}
</Form>
<div
style={{
@ -339,7 +341,6 @@ const NvrHeader = (props) => {
item.name.indexOf(values.name) > -1 &&
item.venderId === values.venderId
);
})
);
});
@ -442,7 +443,6 @@ const NvrHeader = (props) => {
{equipmentWarehouseNvr.total}个设备
</span>
<Pagination
className="22"
total={equipmentWarehouseNvr.total}
showSizeChanger
currentPage={query.page + 1}

1
code/VideoAccess-VCMP/web/package.json

@ -60,6 +60,7 @@
"@vitejs/plugin-react": "^1.3.1",
"@vitejs/plugin-react-refresh": "^1.3.6",
"args": "^5.0.1",
"copy-to-clipboard": "^3.3.1",
"cross-env": "^7.0.3",
"fs-web-server-scaffold": "^1.0.6",
"koa-better-http-proxy": "^0.2.5",

118
code/VideoAccess-VCMP/web/webpack.config.js

@ -3,65 +3,67 @@ const webpack = require('webpack');
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
const PATHS = {
app: path.join(__dirname, 'client/src'),
build: path.join(__dirname, 'client/build')
app: path.join(__dirname, 'client/src'),
build: path.join(__dirname, 'client/build')
};
module.exports = {
mode: "development",
devtool: 'source-map',
devServer: {
historyApiFallback: true,
},
entry: {
app: ["@babel/polyfill", PATHS.app]
},
output: {
publicPath: '/client/build/',
path: PATHS.build,
filename: '[name].js'
},
resolve: {
modules: [path.resolve(__dirname, 'client/src'), path.resolve(__dirname, 'node_modules')],
extensions: ['.js', '.jsx'],
alias: {
crypto: false,
$utils: path.resolve(__dirname, 'client/src/utils/'),
$components: path.resolve(__dirname, 'client/src/components/'),
}
},
plugins: [
new webpack.HotModuleReplacementPlugin(),
new BundleAnalyzerPlugin(),
],
module: {
rules: [{
test: /\.css$/,
use: ['style-loader', {
loader: 'css-loader',
options: {
modules: true
}
}]
},
{
test: /\.less$/,
use: ['style-loader', 'css-loader', {
loader: 'less-loader',
options: {
lessOptions: {
javascriptEnabled: true
}
}
}]
},
{
test: /\.(js|jsx)$/,
use: 'babel-loader',
include: [PATHS.app, path.resolve(__dirname, 'node_modules', '@peace')],
}, {
test: /\.(eot|woff|woff2|svg|ttf)([\?]?.*)$/,
loader: "file-loader"
}]
}
mode: "development",
devtool: 'source-map',
devServer: {
historyApiFallback: true,
},
entry: {
app: ["@babel/polyfill", PATHS.app]
},
output: {
publicPath: '/client/build/',
path: PATHS.build,
filename: '[name].js'
},
resolve: {
modules: [path.resolve(__dirname, 'client/src'), path.resolve(__dirname, 'node_modules')],
extensions: ['.js', '.jsx'],
alias: {
crypto: false,
$utils: path.resolve(__dirname, 'client/src/utils/'),
$components: path.resolve(__dirname, 'client/src/components/'),
}
},
plugins: [
new webpack.HotModuleReplacementPlugin(),
new BundleAnalyzerPlugin(),
],
module: {
rules: [{
test: /\.css$/,
use: ['style-loader', {
loader: 'css-loader',
options: {
modules: true
}
}]
},
{
test: /\.less$/,
use: ['style-loader', 'css-loader', {
loader: 'less-loader',
options: {
lessOptions: {
javascriptEnabled: true
}
}
}]
},
{
test: /\.(js|jsx)$/,
use: 'babel-loader',
include: [PATHS.app, path.resolve(__dirname, 'node_modules', '@peace')],
},
{
test: /\.(eot|woff|woff2|svg|ttf)([\?]?.*)$/,
loader: "file-loader"
}
]
}
};
Loading…
Cancel
Save