Browse Source

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

release_1.3.0
巴林闲侠 3 years ago
parent
commit
fdbd9690b6
  1. 132
      code/VideoAccess-VCMP/api/app/lib/controllers/application/index.js
  2. 15
      code/VideoAccess-VCMP/api/app/lib/routes/application/index.js
  3. BIN
      code/VideoAccess-VCMP/web/client/assets/images/imageImg/copy.png
  4. BIN
      code/VideoAccess-VCMP/web/client/assets/images/imageImg/del.png
  5. BIN
      code/VideoAccess-VCMP/web/client/assets/images/imageImg/detail_icon.png
  6. BIN
      code/VideoAccess-VCMP/web/client/assets/images/imageImg/idCopy.png
  7. BIN
      code/VideoAccess-VCMP/web/client/assets/images/imageImg/nextStep.png
  8. BIN
      code/VideoAccess-VCMP/web/client/assets/images/imageImg/question.png
  9. BIN
      code/VideoAccess-VCMP/web/client/assets/images/imageImg/release.png
  10. BIN
      code/VideoAccess-VCMP/web/client/assets/images/imageImg/stepfour.png
  11. BIN
      code/VideoAccess-VCMP/web/client/assets/images/imageImg/stepone.png
  12. BIN
      code/VideoAccess-VCMP/web/client/assets/images/imageImg/stepthree.png
  13. BIN
      code/VideoAccess-VCMP/web/client/assets/images/imageImg/steptwo.png
  14. BIN
      code/VideoAccess-VCMP/web/client/assets/images/imageImg/text.png
  15. BIN
      code/VideoAccess-VCMP/web/client/assets/images/imageImg/图科技蓝_小@2x.png
  16. 54
      code/VideoAccess-VCMP/web/client/src/sections/application/actions/application.js
  17. 5
      code/VideoAccess-VCMP/web/client/src/sections/application/actions/index.js
  18. 36
      code/VideoAccess-VCMP/web/client/src/sections/application/components/applyModal.jsx
  19. 110
      code/VideoAccess-VCMP/web/client/src/sections/application/containers/applicationCenter.jsx
  20. 1
      code/VideoAccess-VCMP/web/client/src/sections/equipmentWarehouse/components/nvrModal.jsx
  21. 134
      code/VideoAccess-VCMP/web/client/src/sections/offline/components/pushSideSheet.jsx
  22. 48
      code/VideoAccess-VCMP/web/client/src/sections/offline/containers/carrierpigeon.jsx
  23. 34
      code/VideoAccess-VCMP/web/client/src/sections/offline/containers/statuscode.jsx
  24. 256
      code/VideoAccess-VCMP/web/client/src/sections/openness/containers/mirroring.jsx
  25. 29
      code/VideoAccess-VCMP/web/client/src/utils/webapi.js
  26. 2
      code/VideoAccess-VCMP/web/package.json

132
code/VideoAccess-VCMP/api/app/lib/controllers/application/index.js

@ -29,19 +29,29 @@ async function check (ctx) {
}
async function edit (ctx, next) {
let errMsg = '创建应用失败'
const transaction = await ctx.fs.dc.orm.transaction();
try {
const { models } = ctx.fs.dc;
const { userId } = ctx.fs.api
const data = ctx.request.body;
if (data.id) {
let findOption = { where: { name: data.name } }
if (data.appId) {
findOption.where.id = { $ne: data.appId }
}
const applicationRes = await models.Application.findOne(findOption)
if (applicationRes) {
throw '已有相同应用名称'
}
if (data.appId) {
// 修改
const storageData = Object.assign({}, data,)
await models.Application.update(storageData, {
where: {
id: data.id
id: data.appId
},
transaction
})
@ -66,13 +76,125 @@ async function edit (ctx, next) {
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
ctx.status = 400;
ctx.body = {
message: errMsg
message: typeof error == 'string' ? error : undefined
}
}
}
async function get (ctx) {
try {
const models = ctx.fs.dc.models;
const { userId, token } = ctx.fs.api
const { limit, page, orderBy, orderDirection } = ctx.query
let findOption = {
where: {
// createUserId: userId,
},
order: [
[orderBy || 'id', orderDirection || 'DESC'] //查询排序
],
}
if (limit) {
findOption.limit = limit
}
if (page && limit) {
findOption.offset = page * limit
}
const applicationRes = await models.Application.findAndCountAll(findOption)
let createUserIds = new Set()
let cameraIds = []
for (let c of applicationRes.rows) {
cameraIds.push(c.id)
createUserIds.add(c.createUserId)
}
// 查用户信息
const createUserRes = await ctx.app.fs.authRequest.get(`user/${[...createUserIds].join(',') || -1}/message`, { query: { token } })
for (let { dataValues: n } of applicationRes.rows) {
const corCreateUser = createUserRes.find(u => u.id == n.createUserId)
n.createUser = {
name: corCreateUser ? corCreateUser.username : ''
}
}
ctx.status = 200;
ctx.body = {
total: applicationRes.count,
data: applicationRes.rows
}
} catch (error) {
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
ctx.status = 400;
ctx.body = {}
}
}
async function put (ctx) {
try {
const { models } = ctx.fs.dc;
const data = ctx.request.body;
// TODO 向视频服务发送通知
// 库记录
await models.Application.update({
forbidden: data.forbidden
}, {
where: {
id: data.appId
}
})
ctx.status = 204;
} catch (error) {
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
ctx.status = 400;
ctx.body = {}
}
}
async function del (ctx, next) {
const transaction = await ctx.fs.dc.orm.transaction();
try {
const models = ctx.fs.dc.models;
const { token } = ctx.fs.api
const { appId } = ctx.params
const { appKey } = await models.Application.findOne({
where: {
id: appId
},
}) || {}
await models.Application.destroy({
where: {
id: appId
},
transaction
})
await ctx.app.fs.authRequest.delete(`oauth2/token/invalidate_all`, {
query: { token, appKey }
})
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 = {
check,
edit,
get,
put,
del,
check,
};

15
code/VideoAccess-VCMP/api/app/lib/routes/application/index.js

@ -5,12 +5,21 @@ const application = require('../../controllers/application');
module.exports = function (app, router, opts) {
app.fs.api.logAttr['GET/application'] = { content: '获取应用信息', visible: false };
router.get('/application', application.get);
app.fs.api.logAttr['POST/application'] = { content: '创建/修改应用', visible: false };
router.post('/application', application.edit);
app.fs.api.logAttr['PUT/application'] = { content: '禁用应用', visible: false }
router.put('/application', application.put);
app.fs.api.logAttr['DEL/application/:appId'] = { content: '删除应用', visible: false };
router.del('/application/:appId', application.del);
app.fs.api.logAttr['GET/application/check'] = { content: '检查应用状态', visible: false };
router.get('/application/check', application.check);
// app.fs.api.logAttr['GET/application'] = { content: '获取应用信息', visible: false };
// router.get('/application', application.get);
app.fs.api.logAttr['POST/application'] = { content: '创建/修改应用', visible: false };
router.post('/application', application.edit);

BIN
code/VideoAccess-VCMP/web/client/assets/images/imageImg/copy.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 823 B

BIN
code/VideoAccess-VCMP/web/client/assets/images/imageImg/del.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 742 B

BIN
code/VideoAccess-VCMP/web/client/assets/images/imageImg/detail_icon.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 915 B

BIN
code/VideoAccess-VCMP/web/client/assets/images/imageImg/idCopy.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 696 B

BIN
code/VideoAccess-VCMP/web/client/assets/images/imageImg/nextStep.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 704 B

BIN
code/VideoAccess-VCMP/web/client/assets/images/imageImg/question.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 697 B

BIN
code/VideoAccess-VCMP/web/client/assets/images/imageImg/release.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 719 B

BIN
code/VideoAccess-VCMP/web/client/assets/images/imageImg/stepfour.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

BIN
code/VideoAccess-VCMP/web/client/assets/images/imageImg/stepone.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

BIN
code/VideoAccess-VCMP/web/client/assets/images/imageImg/stepthree.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

BIN
code/VideoAccess-VCMP/web/client/assets/images/imageImg/steptwo.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

BIN
code/VideoAccess-VCMP/web/client/assets/images/imageImg/text.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

BIN
code/VideoAccess-VCMP/web/client/assets/images/imageImg/图科技蓝_小@2x.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 323 KiB

54
code/VideoAccess-VCMP/web/client/src/sections/application/actions/application.js

@ -0,0 +1,54 @@
"use strict";
import { basicAction } from "@peace/utils";
import { ApiTable } from "$utils";
export function getApplication (query) {
return (dispatch) =>
basicAction({
type: "get",
dispatch: dispatch,
actionType: "GET_APPLICATION",
query: query,
url: `${ApiTable.getApplication}`,
msg: { option: "获取应用信息" },
reducer: { name: "applicationData", params: { noClear: true } },
});
}
export function putApplication (data) {
return (dispatch) =>
basicAction({
type: "put",
dispatch: dispatch,
actionType: "PUT_APPLICATION",
data,
url: `${ApiTable.putApplication}`,
msg: { option: data?.forbidden ? "禁用" : "启用" }, //禁用摄像头
reducer: {},
});
}
export function delApplication (orgId) {
return (dispatch) =>
basicAction({
type: "del",
dispatch: dispatch,
actionType: "DEL_APPLICATION",
url: `${ApiTable.delApplication.replace("{appId}", orgId)}`,
msg: { option: "删除" }, //删除应用
reducer: {},
});
}
export function postApplication (data) {
return (dispatch) =>
basicAction({
type: "post",
dispatch: dispatch,
data,
actionType: "POST_CHANGE_NVR",
msg: { option: data?.appId ? "修改" : "添加" },
url: `${ApiTable.postApplication}`,
});
}

5
code/VideoAccess-VCMP/web/client/src/sections/application/actions/index.js

@ -1,5 +1,8 @@
'use strict';
export default {
import * as application from './application'
export default {
...application
}

36
code/VideoAccess-VCMP/web/client/src/sections/application/components/applyModal.jsx

@ -2,25 +2,33 @@ import React, { useState, useEffect, useRef } from "react";
import { connect } from "react-redux";
import { Button, Form, Modal, } from "@douyinfe/semi-ui";
const ApplyModal = ({ close, modalName, visible }) => {
const ApplyModal = ({ dispatch, actions, close, modalName, visible, appData }) => {
const { applicationCenter } = actions;
const appDatas = appData || {}
const form = useRef();
const handleOk = () => {
form.current
.validate()
.then((values) => {
console.log(values);
// close()
if (appDatas?.id) {
values.appId = appDatas?.id
}
dispatch(applicationCenter.postApplication(values)).then((res) => {
console.log(res);
if (res.success) {
close()
form.current.reset()
}
})
})
}
return <Modal
title={modalName ? "修改应用" : "创建应用"}
visible={visible}
width={494}
onCancel={() => close()}
onCancel={() => { close(); form.current.reset() }}
onOk={handleOk}
>
<Form
@ -28,16 +36,14 @@ const ApplyModal = ({ close, modalName, visible }) => {
labelPosition="left"
labelAlign="left"
labelWidth="90px"
onValueChange={(values) => console.log(values)}
initValues={{ name: appDatas?.name || '', type: appDatas?.type || '' }}
getFormApi={(formApi) => (form.current = formApi)}
>
<Form.Input
maxLength="36"
maxLength="15"
field="name"
label="应用名称:"
allowEmpty={false}
initValue={'' || ""}
placeholder="建议命名方式: [应用名 + 应用场景] 不超过15个字符"
style={{ width: 350 }}
rules={[
@ -48,16 +54,16 @@ const ApplyModal = ({ close, modalName, visible }) => {
]}
/>
<Form.Select
label="设备厂家:"
field="venderId"
initValue={'' || null}
label="应用类型:"
field="type"
multiple
placeholder="请选择应用类型"
allowEmpty={false}
style={{ width: 350 }}
rules={[{ required: true, message: "请选择应用类型" }]}
>
{[{ name: 'web', id: 'web' }, { name: 'app', id: 'app' }, { name: '小程序', id: '小程序' }, { name: '其他', id: '其他' }].map((item, index) => (
<Form.Select.Option key={index} value={item.id}>
{[{ name: 'web', value: 'web' }, { name: 'app', value: 'app' }, { name: '小程序', value: 'wxapp' }, { name: '其他', value: 'other' }].map((item, index) => (
<Form.Select.Option key={index} value={item.value}>
{item.name}
</Form.Select.Option>
))}

110
code/VideoAccess-VCMP/web/client/src/sections/application/containers/applicationCenter.jsx

@ -22,35 +22,17 @@ import ApplyModal from "../components/applyModal";
import '../style.less'
const ApplicationCenter = (props) => {
const { dispatch, actions, user, loading, equipmentWarehouseCamera } = props;
// const { equipmentWarehouse } = actions;
const [cameraModal, setCameraModal] = useState(false);
const [remarksModal, setRemarksModal] = useState(false);
const [videoPlay, setVideoPlay] = useState(false);
const { dispatch, actions, user, loading, applicationData } = props;
const { applicationCenter } = actions;
const [modalName, setModalName] = useState(false); //
const [setup, setSetup] = useState(false); //
const [applyModal, setApplyModal] = useState(false);
const [cameraSetup, setcameraSetup] = useState(false);
const [setupp, setSetupp] = useState([]);
const [venderList, setvenderList] = useState([]); //
const [query, setQuery] = useState({ limit: 10, page: 0 }); //
const [search, setSearch] = useState({}); //
const [rowId, setRowId] = useState(); //id
const [cameraData, setCameraData] = useState({}); //
const [modify, setModify] = useState(false); //
const [parentCamera, setParentCamera] = useState(""); //
const [addNvr, setAddNvr] = useState(false); //nvrNVR
const [nvrNumber, setNvrNumber] = useState();
const [videoObj, setVideoObj] = useState(); //
const [axyData, setAxyData] = useState();
const [cameraRemarks, setCameraRemarks] = useState([]);//
const api = useRef();
const searchData = useRef({})
const [appData, setAppData] = useState(null); //id
const APPLICATION = 'application'
const pageLimit = useRef({ limit: 10, page: 0 });
const limits = useRef(); //
const page = useRef(query.page);
const deviceClickb = useRef(true)
const APPLICATION = "application";
const columns = [
@ -65,16 +47,28 @@ const ApplicationCenter = (props) => {
title: "应用名称",
dataIndex: "name",
key: "name",
render: (text, r, index) => {
return r?.name.length > 8 ? <Popover
position='top'
content={
<article style={{ padding: 12 }}>
{r?.name}
</article>
}
>{
`${r?.name.substr(0, 8)}...`}
</Popover> : r?.name
},
},
{
title: "APPID",
dataIndex: "appId",
dataIndex: "appKey",
key: "appId",
},
{
title: "Secret Key",
dataIndex: "secretKey",
dataIndex: "appSecret",
key: "secretKey",
},
@ -90,45 +84,55 @@ const ApplicationCenter = (props) => {
onClick={() => {
setApplyModal(true)
setModalName(true)
setAppData(row)
}}
>
修改
</Button>
{row.forbidden ? (
{row?.forbidden ? (
<Button
theme="borderless"
onClick={() => {
dispatch(applicationCenter.putApplication({ appId: row?.id, forbidden: !row?.forbidden })).then(() => details())
}}
>
启用
</Button>
) : (
<Popconfirm
title="禁用后,应用系统引入的页面及能力将会暂时失效,请谨慎操作。"
title={<div style={{ width: 200 }}>禁用后应用系统引入的页面及能力将会暂时失效请谨慎操作</div>}
arrowPointAtCenter={false}
showArrow={true}
position="topRight"
onConfirm={() => {
dispatch(applicationCenter.putApplication({ appId: row?.id, forbidden: !row?.forbidden })).then(() => {
setQuery({ limit: pageLimit.current.limit, page: pageLimit.current.page })
})
}}
>
<Button theme="borderless">禁用</Button>
</Popconfirm>
)}
<Popconfirm
title="删除后,应用系统引入的页面及能力将会永久失效,请谨慎操作。"
title={<div style={{ width: 200 }}>删除后应用系统引入的页面及能力将会永久失效请谨慎操作</div>}
arrowPointAtCenter={false}
width={300}
showArrow={true}
position="topRight"
onConfirm={() => {
dispatch(applicationCenter.delApplication(row?.id)).then(() => {
if (pageLimit.current.page > 0 && limits.current < 2) {
setQuery({ limit: pageLimit.current.limit, page: pageLimit.current.page - 1 })
} else {
setQuery({ limit: pageLimit.current.limit, page: pageLimit.current.page })
}
})
}}
>
<Button theme="borderless">删除</Button>
</Popconfirm>
</div>
</div >
);
},
}
@ -145,19 +149,28 @@ const ApplicationCenter = (props) => {
title: "创建时间",
dataIndex: "createTime",
key: "createTime",
render: (_, r, index) => {
return moment(r.createTime).format("YYYY-MM-DD HH:mm:ss");
},
},
{
title: "创建账号",
dataIndex: "account",
dataIndex: "createUserId",
key: "account",
render: (_, r, index) => {
return r?.createUser?.name
},
},
{
title: "应用类型",
dataIndex: "applicationType",
dataIndex: "type",
key: "applicationType",
render: (_, r, index) => {
const types = { web: 'web', app: 'app', wxapp: '小程序', other: '其他' }
return r?.type?.map((item, index) => types[item] + ';')
},
},
];
for (let i = 0; i < arr.length; i++) {
@ -179,6 +192,17 @@ const ApplicationCenter = (props) => {
},
];
//
const details = (data) => {
pageLimit.current = query
dispatch(applicationCenter.getApplication(pageLimit.current)).then((res) => {
limits.current = res.payload.data.data.length
});
}
useEffect(() => {
details()
}, [query])
useEffect(() => {
//
localStorage.getItem(APPLICATION) == null
@ -295,7 +319,7 @@ const ApplicationCenter = (props) => {
>
<Table
columns={setupp.filter((s) => s)}
dataSource={[{ name: 'csadca', }]}
dataSource={applicationData.data}
bordered={false}
empty="暂无数据"
style={{
@ -312,17 +336,17 @@ const ApplicationCenter = (props) => {
}}
>
<span style={{ lineHeight: "30px" }}>
{100}个设备
{applicationData.total}个设备
</span>
<Pagination
className="22"
total={100}
total={applicationData.total}
showSizeChanger
currentPage={query.page + 1}
pageSizeOpts={[10, 20, 30, 40]}
onChange={(currentPage, pageSize) => {
setQuery({ limit: pageSize, page: currentPage - 1 });
page.current = currentPage - 1
pageLimit.current = { limit: pageSize, page: currentPage - 1 }
}}
/>
</div>
@ -347,9 +371,12 @@ const ApplicationCenter = (props) => {
<ApplyModal
visible={true}
modalName={modalName}
appData={appData}
close={() => {
setApplyModal(false)
setModalName(false)
setAppData(null)
details()
}}
/>
) : (
@ -361,9 +388,10 @@ const ApplicationCenter = (props) => {
}
function mapStateToProps (state) {
const { auth } = state;
const { global, applicationData } = state;
return {
user: auth.user,
actions: global.actions,
applicationData: applicationData.data || {}
};
}

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

@ -43,7 +43,6 @@ function nvrModal(props) {
.validate()
.then((values) => {
//
console.log(values)
let valuesObj = JSON.parse(JSON.stringify(values));
valuesObj.longitude = values.position.split(",")[0];
valuesObj.latitude = values.position.split(",")[1];

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

@ -1,6 +1,6 @@
import React, { useState, useRef, useEffect } from "react";
import { connect } from "react-redux";
import { Modal, Spin,Tag, Space,Collapse ,SideSheet,Tabs, TabPane,Popover} from "@douyinfe/semi-ui";
import { Modal, Spin, Tag, Space, Collapse, SideSheet, Tabs, TabPane, Popover } from "@douyinfe/semi-ui";
import moment from "moment";
import './pushSideSheet.less'
import PerfectScrollbar from "perfect-scrollbar";
@ -13,41 +13,53 @@ function pushSideSheet (props) {
close,
actions,
pushData,//
journal//
rowId,
dispatch
} = props;
const { offline } = actions;
const [logList, setLogList] = useState([]);//
const [noticeTest, setNoticeTest] = useState([]);//
const [equipmentTest, setEquipmentTest] = useState([]);//
const journal = useRef([]);
const [isloading, setloading] = useState(false);
//
useEffect(() => {
setloading(true)
setTimeout(() => {
dispatch(offline.getPushLog(rowId)).then((res) => {
if (res.success) {
journal.current=res.payload.data
}
else {
journal.current=[]
}
let timeList = []
for (let index = 0; index < journal.length; index++) {
let receiverTest=journal[index].receiver.join(';');
let camerTest=''
for (let j = 0; j < journal[index].camera.length; j++) {
camerTest=camerTest+journal[index].camera[j].name+';'
for (let index = 0; index < journal.current.length; index++) {
let receiverTest = journal.current[index].receiver.join(';');
let camerTest = ''
for (let j = 0; j < journal.current[index].camera.length; j++) {
camerTest = camerTest + journal.current[index].camera[j].name + ';'
}
timeList.push(
{
title:getTimeTitle(journal[index].time),
time:getTimeData(journal[index].time),
cameraList:camerTest,
receiverList:receiverTest,
noticeWay:journal[index].noticeWay[0],
title: getTimeTitle(journal.current[index].time),
time: getTimeData(journal.current[index].time),
cameraList: camerTest,
receiverList: receiverTest,
noticeWay: journal.current[index].noticeWay[0],
}
)
}
let yearList=[]
for (let index = 0; index < journal.length; index++) {
let yearList = []
for (let index = 0; index < journal.current.length; index++) {
yearList.push(
{
title:getTimeTitle(journal[index].time),
title: getTimeTitle(journal.current[index].time),
}
)
}
let showList=[]
let showList = []
let obj = {};
for (let i = 0; i < yearList.length; i++) {
if (!obj[yearList[i].title]) {
@ -56,31 +68,32 @@ function pushSideSheet (props) {
}
}
for (let index = 0; index < showList.length; index++) {
showList[index].timeList=[]
showList[index].timeList = []
}
for (let j = 0; j < showList.length; j++) {
for (let i = 0; i < timeList.length; i++) {
if(timeList[i].title==showList[j].title){
showList[j].timeList.push({time:timeList[i].time,cameraList:timeList[i].cameraList,receiverList:timeList[i].receiverList,noticeWay:timeList[i].noticeWay,})
if (timeList[i].title == showList[j].title) {
showList[j].timeList.push({ time: timeList[i].time, cameraList: timeList[i].cameraList, receiverList: timeList[i].receiverList, noticeWay: timeList[i].noticeWay, })
}
}
}
setLogList(showList)
let noticeList =[]
let noticeList = []
for (let i = 0; i < pushData.cameraStatusPushReceivers.length; i++) {
noticeList.push(pushData.cameraStatusPushReceivers[i].receiver)
}
setNoticeTest(noticeList)
let equipmentList=[]
let equipmentList = []
for (let i = 0; i < pushData.cameraStatusPushMonitors.length; i++) {
equipmentList.push(pushData.cameraStatusPushMonitors[i].camera.name)
}
setEquipmentTest(equipmentList)
setTimeout(() => {
equipmentScrollbar = new PerfectScrollbar("#collapse_test", {
suppressScrollX: true,
});
}, 100);
setloading(false)
});
}, 0);
}, []);
useEffect(() => {
const videoStreaming = document.getElementById("collapse_test");
@ -88,19 +101,19 @@ function pushSideSheet (props) {
equipmentScrollbar.update();
}
});
function getTimeTitle(date){
let year = moment(date).year()+'年'
let month = (moment(date).month()+1)>10?(moment(date).month()+1)+'月':'0'+(moment(date).month()+1)+'月'
return year+month
}
function getTimeData(date){
let day=moment(date).date()>10?moment(date).date()+'日':'0'+moment(date).date()+'日';
let hour=moment(date).hour()>10?moment(date).hour():'0'+moment(date).hour();
let minute=moment(date).minute()>10?moment(date).minute():'0'+moment(date).minute();
let second=moment(date).second()>10?moment(date).second():'0'+moment(date).second();
return day+' '+hour+':'+minute+':'+second
}
function sideSheetChange(){
function getTimeTitle (date) {
let year = moment(date).year() + '年'
let month = (moment(date).month() + 1) > 10 ? (moment(date).month() + 1) + '月' : '0' + (moment(date).month() + 1) + '月'
return year + month
}
function getTimeData (date) {
let day = moment(date).date() > 10 ? moment(date).date() + '日' : '0' + moment(date).date() + '日';
let hour = moment(date).hour() > 10 ? moment(date).hour() : '0' + moment(date).hour();
let minute = moment(date).minute() > 10 ? moment(date).minute() : '0' + moment(date).minute();
let second = moment(date).second() > 10 ? moment(date).second() : '0' + moment(date).second();
return day + ' ' + hour + ':' + minute + ':' + second
}
function sideSheetChange () {
close()
}
return (
@ -108,6 +121,14 @@ function pushSideSheet (props) {
<SideSheet title={pushData.name} className='sideSheet' width={670} visible={true} onCancel={sideSheetChange}>
<Tabs type="line">
<TabPane tab="推送日志" itemKey="1">
<Spin tip="数据加载中..." spinning={isloading}>
{
isloading?(
<div style={{height:300}}>
</div>
):''
}
</Spin>
<div id="collapse_test"
style={{
height: document.body.clientHeight - 122,
@ -117,46 +138,47 @@ function pushSideSheet (props) {
<Collapse defaultActiveKey="0">
{logList.map((item, index) => {
return (
<Collapse.Panel header={<div style={{width:'100%',textAlign:'center'}}>{item.title}</div>} itemKey={String(index)} key={index}>
{item.timeList.map((itm,idx)=>{
return(
<div style={{display:'flex',alignItems:'center',height:46,borderBottom: '1px solid #E8E8E8',fontSize:12,color:'rgba(0,0,0,0.6500)'}} key={idx}>
<div style={{margin:'0px 24px',width:74}}>{itm.time}</div>
<Collapse.Panel header={<div style={{ width: '100%', textAlign: 'center' }}>{item.title}</div>} itemKey={String(index)} key={index}>
{item.timeList.map((itm, idx) => {
return (
<div style={{ display: 'flex', alignItems: 'center', height: 46, borderBottom: '1px solid #E8E8E8', fontSize: 12, color: 'rgba(0,0,0,0.6500)' }} key={idx}>
<div style={{ margin: '0px 24px', width: 74 }}>{itm.time}</div>
<Popover content={
<div style={{ padding: 12,width:300,wordBreak:'break-all' }}>
<div style={{ padding: 12, width: 300, wordBreak: 'break-all' }}>
{itm.receiverList}
</div>
}>
<div style={{marginRight:24,width:262,textOverflow:'ellipsis',whiteSpace:'nowrap',overflow:'hidden'}}>{itm.receiverList}</div>
<div style={{ marginRight: 24, width: 262, textOverflow: 'ellipsis', whiteSpace: 'nowrap', overflow: 'hidden' }}>{itm.receiverList}</div>
</Popover>
<Popover content={
<div style={{ padding: 12,width:200,wordBreak:'break-all' }}>
<div style={{ padding: 12, width: 200, wordBreak: 'break-all' }}>
{itm.cameraList}
</div>
}>
<div style={{marginRight:24,width:133,textOverflow:'ellipsis',whiteSpace:'nowrap',overflow:'hidden'}}>{itm.cameraList}</div>
<div style={{ marginRight: 24, width: 133, textOverflow: 'ellipsis', whiteSpace: 'nowrap', overflow: 'hidden' }}>{itm.cameraList}</div>
</Popover>
<div style={{marginRight:25,width:83}} className={itm.noticeWay=='online'?'online':itm.noticeWay=='offline'?'offline':'timing'}>{itm.noticeWay=='online'?'上线及时通知':itm.noticeWay=='offline'?'离线即时通知':'24时定时统计'}</div>
<div style={{ marginRight: 25, width: 83 }} className={itm.noticeWay == 'online' ? 'online' : itm.noticeWay == 'offline' ? 'offline' : 'timing'}>{itm.noticeWay == 'online' ? '上线及时通知' : itm.noticeWay == 'offline' ? '离线即时通知' : '24时定时统计'}</div>
</div>
)
})}
</Collapse.Panel>
)})}
)
})}
</Collapse>
</div>
</TabPane>
<TabPane tab="监听范围" itemKey="2">
<div style={{margin:'12px 36px',fontSize:12,color:'rgba(0,0,0,0.6500)'}}>
<div style={{ margin: '12px 36px', fontSize: 12, color: 'rgba(0,0,0,0.6500)' }}>
<img
src="/assets/images/background/icon_camera.png"
alt="设置"
style={{ width: 26, height: 26 }}
/>
<div style={{marginTop:1,background: '#F1F5FF',padding:14,minHeight:139}}>
<div style={{ marginTop: 1, background: '#F1F5FF', padding: 14, minHeight: 139 }}>
<Space wrap>
{equipmentTest.map((item, index) => {
return(
<Tag color='blue' style={{height:33}} type='solid' key={index}>{item}</Tag>
return (
<Tag color='blue' style={{ height: 33 }} type='solid' key={index}>{item}</Tag>
)
})}
</Space>
@ -164,17 +186,17 @@ function pushSideSheet (props) {
</div>
</TabPane>
<TabPane tab="接收信息" itemKey="3">
<div style={{margin:'12px 36px',fontSize:12,color:'rgba(0,0,0,0.6500)'}}>
<div style={{ margin: '12px 36px', fontSize: 12, color: 'rgba(0,0,0,0.6500)' }}>
<img
src={pushData.pushWay=='email'?"/assets/images/background/icon_email.png":"/assets/images/background/icon_phone.png"}
src={pushData.pushWay == 'email' ? "/assets/images/background/icon_email.png" : "/assets/images/background/icon_phone.png"}
alt="设置"
style={{ width: 26, height: 26 }}
/>
<div style={{marginTop:1,background: '#F1F5FF',padding:14,minHeight:139}}>
<div style={{ marginTop: 1, background: '#F1F5FF', padding: 14, minHeight: 139 }}>
<Space wrap>
{noticeTest.map((item, index) => {
return(
<Tag color='blue' style={{height:33}} type='solid' key={index}>{item}</Tag>
return (
<Tag color='blue' style={{ height: 33 }} type='solid' key={index}>{item}</Tag>
)
})}
</Space>

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

@ -34,7 +34,6 @@ const Carrierpigeon = (props) => {
const cameraList = useRef([]);//
const [sideSheetVisible, setSideSheetVisible] = useState(false);
const [pushData, setPushData] = useState({});
const [journal, setJournal] = useState([]);
const tableList = [//
{
title: '推送信息',
@ -97,6 +96,11 @@ const Carrierpigeon = (props) => {
limits.current = res.payload.data.length
});
}
function banned(row){
dispatch(offline.putPushBanned({configId: row.id,forbidden: !row.forbidden})).then(() => {
dispatch(offline.getStatusPush(searchData.current))
});
}
const columns = [
{
title: "序号",
@ -133,19 +137,7 @@ const Carrierpigeon = (props) => {
{row.forbidden ? (
<Button
theme="borderless"
onClick={() => {
dispatch(
offline.putPushBanned(
{
configId: row.id,
forbidden: !row.forbidden,
},
row.forbidden
)
).then(() => {
equipmentGetStatusPush();
});
}}
onClick={() => banned(row)}
>
启用
</Button>
@ -155,19 +147,7 @@ const Carrierpigeon = (props) => {
arrowPointAtCenter={false}
showArrow={true}
position="topRight"
onConfirm={() => {
dispatch(
offline.putPushBanned(
{
configId: row.id,
forbidden: !row.forbidden,
},
row.forbidden
)
).then(() => {
equipmentGetStatusPush();
});
}}
onConfirm={() =>banned(row)}
>
<Button theme="borderless">禁用</Button>
</Popconfirm>
@ -197,16 +177,9 @@ const Carrierpigeon = (props) => {
复制
</Button>
<Button onClick={()=>{
dispatch(offline.getPushLog(row.id)).then((res) => {
if(res.success){
setJournal(res.payload.data);
}
else{
setJournal([]);
}
setRowId(row.id);
setPushData(row);
setSideSheetVisible(true);
});
}} theme="borderless">
查看
</Button>
@ -540,10 +513,11 @@ const Carrierpigeon = (props) => {
/>}
{sideSheetVisible&&<PushSideSheet
pushData={pushData}
journal={journal}
rowId={rowId}
close={() => {
setSideSheetVisible(false);
setPushData({})
setPushData({});
setRowId();
}}
/>}
</div>

34
code/VideoAccess-VCMP/web/client/src/sections/offline/containers/statuscode.jsx

@ -62,7 +62,11 @@ const Statuscode = (props) => {
const codegetStatus = () => {
searchData.current = { ...query, ...search }
dispatch(offline.getStatus(searchData.current)).then((res) => {
});
}
function banned(row){
dispatch(offline.putStatueBanned({statusId: row.id,forbidden: !row.forbidden,})).then(() => {
dispatch(offline.getStatus(searchData.current))
});
}
const columns = [
@ -114,19 +118,7 @@ const Statuscode = (props) => {
{row.forbidden ? (
<Button
theme="borderless"
onClick={() => {
dispatch(
offline.putStatueBanned(
{
statusId: row.id,
forbidden: !row.forbidden,
},
row.forbidden
)
).then(() => {
codegetStatus();
});
}}
onClick={() => banned(row)}
>
启用
</Button>
@ -136,19 +128,7 @@ const Statuscode = (props) => {
arrowPointAtCenter={false}
showArrow={true}
position="topRight"
onConfirm={() => {
dispatch(
offline.putStatueBanned(
{
statusId: row.id,
forbidden: !row.forbidden,
},
row.forbidden
)
).then(() => {
codegetStatus();
});
}}
onConfirm={() => banned(row)}
>
<Button theme="borderless">禁用</Button>
</Popconfirm>

256
code/VideoAccess-VCMP/web/client/src/sections/openness/containers/mirroring.jsx

@ -1,12 +1,262 @@
import React, { useEffect } from 'react';
import React, { useEffect, useState, useRef } from 'react';
import { connect } from 'react-redux';
import { Coming } from '$components'
import '../style.less'
const Mirroring = (props) => {
const detailList = useRef([
{
title: '科技蓝',
img: '/assets/images/imageImg/text.png',
color: 'blue'
}, {
title: '白',
img: '/assets/images/imageImg/text.png',
color: 'blue'
}, {
title: '黑色(深灰)',
img: '/assets/images/imageImg/text.png',
color: 'blue'
}, {
title: '灰色',
img: '/assets/images/imageImg/text.png',
color: 'blue'
},
]);
const myCustomList = useRef([
{
title: '智慧小蓝视频融合中心实例实例实例实例',
img: '/assets/images/imageImg/text.png',
color: 'blue',
lastEditTime:'2022-12-22 16:00:57',
lastReleaseTime:'2022-12-22 19:20:57',
typeId:'1511444S4545'
}, {
title: '智慧小蓝视频融合',
img: '/assets/images/imageImg/text.png',
color: 'blue',
lastEditTime:'2022-12-22 16:00:57',
lastReleaseTime:'2022-12-22 19:20:57',
typeId:'1511444S4545'
}, {
title: '智慧小蓝视频融合小蓝视频融合',
img: '/assets/images/imageImg/text.png',
color: 'blue',
lastEditTime:'2022-12-22 16:00:57',
lastReleaseTime:'2022-12-22 19:20:57',
typeId:'1511444S4545'
}, {
title: '智慧小蓝视频融合小蓝视频融合是的阿三打阿三打',
img: '/assets/images/imageImg/text.png',
color: 'blue',
lastEditTime:'2022-12-22 16:00:57',
lastReleaseTime:'2022-12-22 19:20:57',
typeId:'1511444S4545'
},
]);
const [maskNum, setMaskNum] = useState(100);
function mouseOver (index) {
setMaskNum(index)
}
function mouseOut () {
setMaskNum(100)
}
return (
<>
<div style={{ height: 171, background: '#FFFFFF' }}>
<div style={{ padding: '18px 0px 0px 20px', color: '#34383E', fontSize: 20, fontWeight: 600 }}>创建步骤</div>
<div style={{ marginTop: 2, display: 'flex', width: "100%", alignItems: 'center' }}>
<div style={{ width: '4.513%', marginLeft: '5.549%' }}>
<img
src="/assets/images/imageImg/stepone.png"
alt="设置"
style={{ width: '100%' }}
/>
</div>
<div style={{ marginLeft: '0.854%', marginTop: 16, width: '7.683%' }}>
<div style={{ fontSize: 16, color: '#1859C1', fontWeight: 600 }}>创建镜像模板</div>
<div style={{ marginTop: 2, fontSize: 14, color: 'rgba(0,0,0,0.4500)' }}>选择样式模板</div>
</div>
<div style={{ width: '6.281%' }}>
<img
src="/assets/images/imageImg/nextStep.png"
alt="设置"
style={{ width: '100%' }}
/>
</div>
<div style={{ width: '4.513%', marginLeft: '4.086%' }}>
<img
src="/assets/images/imageImg/steptwo.png"
alt="设置"
style={{ width: '100%' }}
/>
</div>
<div style={{ marginLeft: '0.854%', marginTop: 16, width: '7.683%' }}>
<div style={{ fontSize: 16, color: '#1859C1', fontWeight: 600 }}>节点配置</div>
<div style={{ marginTop: 2, fontSize: 14, color: 'rgba(0,0,0,0.4500)' }}>创建节点及设备树</div>
</div>
<div style={{ width: '6.281%' }}>
<img
src="/assets/images/imageImg/nextStep.png"
alt="设置"
style={{ width: '100%' }}
/>
</div>
<div style={{ width: '4.513%', marginLeft: '5.183%' }}>
<img
src="/assets/images/imageImg/stepthree.png"
alt="设置"
style={{ width: '100%' }}
/>
</div>
<div style={{ marginLeft: '0.854%', marginTop: 16, width: '11.464%' }}>
<div style={{ fontSize: 16, color: '#1859C1', fontWeight: 600 }}>发布镜像</div>
<div style={{ marginTop: 2, fontSize: 14, color: 'rgba(0,0,0,0.4500)' }}>发布成功后获取镜像服务ID</div>
</div>
<div style={{ width: '6.281%' }}>
<img
src="/assets/images/imageImg/nextStep.png"
alt="设置"
style={{ width: '100%' }}
/>
</div>
<div style={{ width: '4.513%', marginLeft: '5.549%' }}>
<img
src="/assets/images/imageImg/stepfour.png"
alt="设置"
style={{ width: '100%' }}
/>
</div>
<div style={{ marginLeft: '0.854%', marginTop: 16 }}>
<div style={{ fontSize: 16, color: '#1859C1', fontWeight: 600 }}>调用服务</div>
<div style={{ marginTop: 2, fontSize: 14, color: 'rgba(0,0,0,0.4500)' }}>通过服务ID调用镜像服务</div>
<div style={{ marginTop: 3, fontSize: 12, color: '#1859C1', display: 'flex', alignItems: 'center' }}>API调用镜像服务方法
<img
src="/assets/images/imageImg/question.png"
alt="设置"
style={{ width: 11, height: 11, marginLeft: 3 }}
/>
</div>
</div>
</div>
</div>
<div style={{ background: '#FFFFFF', marginTop: 20 }}>
<div style={{ padding: '18px 0px 0px 20px', color: '#34383E', fontSize: 20, fontWeight: 600 }}>应用详情</div>
<div style={{ display: 'flex', padding: '34px 64px 18px 64px', flexWrap: 'wrap', justifyContent: 'space-between' }}>
{detailList.current.map((item, index) => {
return (
<Coming />
<div key={index}>
<div style={{ position: 'relative', width: '306px', height: 142, boxShadow: '0px 2px 4px 0px rgba(231,231,231,0.5)', }} onMouseOver={() => mouseOver(index)}>
<img
src={item.img}
alt="设置"
style={{ width: '306px', height: 142 }}
/>
{maskNum == index ? (<div onMouseOut={() => mouseOut()} style={{ position: 'absolute', width: '100%', height: '100%', background: 'rgba(0,0,0,0.3)', top: 0, display: 'flex', justifyContent: 'center', alignItems: 'center', cursor: 'pointer' }}>
<div style={{ width: 127, height: 33, border: '1px solid #FFFFFF', borderRadius: 4, color: '#ffffff', fontSize: 14, display: 'flex', justifyContent: 'center', alignItems: 'center' }}>
使用该模版
</div>
</div>) : ''}
</div>
<div style={{ marginTop: 11, display: 'flex', alignItems: 'center' }}>
<div style={{ marginRight: 4 }}>
<img
src="/assets/images/imageImg/detail_icon.png"
alt="设置"
style={{ width: 26, height: 26 }}
/>
</div>
<div style={{ color: 'rgba(0,0,0,0.65)', fontSize: 16 }}>
{item.title}
</div>
</div>
</div>
)
})}
</div>
</div>
<div style={{ background: '#FFFFFF', marginTop: 27 }}>
<div style={{ padding: '18px 0px 0px 20px', display: 'flex', alignItems: 'center' }}>
<div style={{ color: '#34383E', fontSize: 20, fontWeight: 600 }}>我的自定义镜像服务</div>
<div style={{ height: 20, border: '1px solid #1859C1', padding: '0px 12px', fontSize: 14, color: '#1859C1', marginLeft: 4, borderRadius: 2, display: 'flex', alignItems: 'center' }}>
共9份
</div>
</div>
<div style={{ display: 'flex', padding: '19px 46px 79px 64px', flexWrap: 'wrap', justifyContent: 'space-between' }}>
{myCustomList.current.map((item,index)=>{
return(
<div style={{ height: 334, width: 362, boxShadow: '1px 2px 12px 1px rgba(217,217,217,0.7)', padding: 16 }}>
<img
src={item.img}
alt="设置"
style={{ width: 330, height: 153 }}
/>
<div style={{ color: '#2F2F2F', fontSize: 16, width: 300, textOverflow: 'ellipsis', whiteSpace: 'nowrap', overflow: 'hidden' }}>
{item.title}
</div>
<div style={{ display: 'flex', justifyContent: 'flex-end', alignItems: 'center', marginTop: 10 }}>
<div style={{ display: 'flex', cursor: 'pointer' }}>
<div style={{ display: 'flex', alignItems: 'center' }}>
<img
src="/assets/images/imageImg/release.png"
alt="设置"
style={{ width: 16, height: 16 }}
/>
</div>
<div style={{ color: '#1859C1', fontSize: 14, margin: '0px 10px' }}>
发布
</div>
</div>
<div style={{ display: 'flex', cursor: 'pointer' }}>
<div style={{ display: 'flex', alignItems: 'center' }}>
<img
src="/assets/images/imageImg/copy.png"
alt="设置"
style={{ width: 16, height: 16 }}
/>
</div>
<div style={{ color: '#1859C1', fontSize: 14, margin: '0px 10px' }}>
复制
</div>
</div>
<div style={{ display: 'flex', cursor: 'pointer' }}>
<div style={{ display: 'flex', alignItems: 'center' }}>
<img
src="/assets/images/imageImg/del.png"
alt="设置"
style={{ width: 16, height: 16 }}
/>
</div>
<div style={{ color: '#1859C1', fontSize: 14, marginLeft: 10 }}>
删除
</div>
</div>
</div>
<div style={{ color: 'rgba(0,0,0,0.45)', marginTop: 10, fontSize: 12 }}>
最后编辑于{item.lastEditTime}
</div>
<div style={{ color: 'rgba(0,0,0,0.45)', marginTop: 8, fontSize: 12 }}>
最后发布于{item.lastReleaseTime}
</div>
<div style={{ display: 'flex', justifyContent: 'flex-end', alignItems: 'center', marginTop: 12 }}>
<div style={{ display: 'flex', alignItems: 'center' }}>
<img
src="/assets/images/imageImg/idCopy.png"
alt="设置"
style={{ width: 20, height: 20 }}
/>
</div>
<div style={{marginLeft:4,background:'#1859C1',padding:'2px 8px',color:'#FFFFFF',fontSize:12,borderRadius:2}}>
ID:{item.typeId}
</div>
</div>
</div>
)
})}
</div>
</div>
</>
)
}

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

@ -41,17 +41,24 @@ export const ApiTable = {
postCameraRemark: 'camera/remark',//编辑摄像头备注
//获取状态码
getStatus: 'status',//获取状态码
putStatueBanned:'status/banned',//禁用状态码自定义
postStatusResolve:'status/resolve',//编辑解决方案
postStatusCustom:'status/custom',//自定义状态码释义
getStatusSimpleAll:'status/simple_all',//获取全部状态码简略信息
getCameraListAll:'camera/listAll',//获取所有摄像头信息
getStatusPush:'status/push',//获取推送配置
putSasdtatusPush:'status/push',//编辑推送配置
delPush:'status/push/{configId}',//删除推送配置
putPushBanned:'status/push/banned',//禁用推送配置
getPushCopy:'status/push/{configId}/copy',//复制推送配置
getPushLog:'/status/push/{configId}/log',//获取推送记录
putStatueBanned: 'status/banned',//禁用状态码自定义
postStatusResolve: 'status/resolve',//编辑解决方案
postStatusCustom: 'status/custom',//自定义状态码释义
getStatusSimpleAll: 'status/simple_all',//获取全部状态码简略信息
getCameraListAll: 'camera/listAll',//获取所有摄像头信息
getStatusPush: 'status/push',//获取推送配置
putSasdtatusPush: 'status/push',//编辑推送配置
delPush: 'status/push/{configId}',//删除推送配置
putPushBanned: 'status/push/banned',//禁用推送配置
getPushCopy: 'status/push/{configId}/copy',//复制推送配置
getPushLog: '/status/push/{configId}/log',//获取推送记录
//应用管理
getApplication: '/application', //获取应用信息
putApplication: '/application', //禁用应用
delApplication: '/application/{appId}', //删除应用
postApplication: '/application', //创建/修改应用
};
export const VideoServeApi = {

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

@ -7,7 +7,7 @@
"test": "mocha",
"start-vite": "cross-env NODE_ENV=developmentVite npm run start-params",
"start": "cross-env NODE_ENV=development npm run start-params",
"start-params": "node server -p 5000 -u http://localhost:4000 --apiVcmpUrl http://localhost:4000 --apiAuthUrl http://localhost:4200 --apiAnxinyunUrl http://localhost:4100 --iotAuthWeb http://localhost:5200 --iotVideoServer http://221.230.55.27:8081",
"start-params": "node server -p 5000 -u http://10.8.30.112:4000 --apiVcmpUrl http://localhost:4000 --apiAuthUrl http://10.8.30.112:4200 --apiAnxinyunUrl http://10.8.30.112:4100 --iotAuthWeb http://localhost:5200 --iotVideoServer http://221.230.55.27:8081",
"deploy": "export NODE_ENV=production&& npm run build && node server",
"build-dev": "cross-env NODE_ENV=development&&webpack --config webpack.config.js",
"build": "cross-env NODE_ENV=production&&webpack --config webpack.config.prod.js"

Loading…
Cancel
Save