tplink摄像头物联管理
This commit is contained in:
parent
e275543bad
commit
bcb34ab8c8
|
@ -166,6 +166,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<script type="module" src="/src/main.ts"></script>
|
<script type="module" src="/src/main.ts"></script>
|
||||||
|
<script src="/static/tums-player/tums-player.umd.min.js"></script>
|
||||||
<!-- 百度统计 -->
|
<!-- 百度统计 -->
|
||||||
<script>
|
<script>
|
||||||
var _hmt = _hmt || [];
|
var _hmt = _hmt || [];
|
||||||
|
|
15388
pnpm-lock.yaml
15388
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
Binary file not shown.
|
@ -0,0 +1,129 @@
|
||||||
|
/*
|
||||||
|
* Author: Liu Ninggang
|
||||||
|
* File Created: Monday, 27th September 2021 10:28:13 am
|
||||||
|
* Last Modified: Wednesday, 29th September 2021 5:36:21 pm
|
||||||
|
* Modified By: Liu Ninggang
|
||||||
|
* Copyright (c) 2021 TP-LINK
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* eslint-disable */
|
||||||
|
|
||||||
|
let inputBuffer;
|
||||||
|
let outputBuffer;
|
||||||
|
let has_init = 0;
|
||||||
|
let width, height;
|
||||||
|
let dataSeq;
|
||||||
|
let decoderType;
|
||||||
|
let videoDecoder;
|
||||||
|
let dataParams = {};
|
||||||
|
var Module = {};
|
||||||
|
|
||||||
|
postMessage({ hasInstalled: true });
|
||||||
|
|
||||||
|
const initVideoDecoder = () => {
|
||||||
|
videoDecoder = new VideoDecoder({
|
||||||
|
output: async (frame) => {
|
||||||
|
let { timestamp, format, codedWidth, codedHeight } = frame;
|
||||||
|
let param = dataParams[timestamp] || {};
|
||||||
|
postMessage({ data: frame, width: codedWidth, height: codedHeight, timestamp, seq: param.seq, format }, [frame]);
|
||||||
|
frame.close();
|
||||||
|
delete dataParams[timestamp];
|
||||||
|
},
|
||||||
|
error: (err) => {
|
||||||
|
console.log('video decoder error: ', err);
|
||||||
|
postMessage({ decodeError: err, state: videoDecoder.state });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
has_init = 1;
|
||||||
|
};
|
||||||
|
|
||||||
|
const initWasmDecoder = (scripts = [], wasmBinary) => {
|
||||||
|
Module.wasmBinary = wasmBinary;
|
||||||
|
self.importScripts(...scripts);
|
||||||
|
|
||||||
|
Module.onRuntimeInitialized = function () {
|
||||||
|
Module._hevc_decoder_init();
|
||||||
|
this.bufferLen = 2304 * 1296 * 8;
|
||||||
|
inputBuffer = Module._malloc(this.bufferLen);
|
||||||
|
outputBuffer = Module._malloc(this.bufferLen);
|
||||||
|
width = Module._malloc(4);
|
||||||
|
height = Module._malloc(4);
|
||||||
|
has_init = 1;
|
||||||
|
postMessage({ hasInit: true });
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
this.onmessage = async function (event) {
|
||||||
|
let { cmd } = event.data;
|
||||||
|
if (cmd === 'init') {
|
||||||
|
decoderType = event.data.decoderType;
|
||||||
|
let { scripts, wasmBinary } = event.data;
|
||||||
|
|
||||||
|
if (decoderType === 'wasm') {
|
||||||
|
initWasmDecoder(scripts, wasmBinary);
|
||||||
|
} else if (decoderType === 'webcodecs') {
|
||||||
|
postMessage({ hasInit: true });
|
||||||
|
} else {
|
||||||
|
initVideoDecoder();
|
||||||
|
initWasmDecoder(scripts, wasmBinary);
|
||||||
|
}
|
||||||
|
} else if (cmd === 'webcodecs_config') {
|
||||||
|
initVideoDecoder();
|
||||||
|
|
||||||
|
let { data } = event;
|
||||||
|
let config = {
|
||||||
|
codec: data.codec,
|
||||||
|
codeWidth: data.width,
|
||||||
|
codeHeight: data.height
|
||||||
|
};
|
||||||
|
videoDecoder.configure(config);
|
||||||
|
width = data.width;
|
||||||
|
height = data.height;
|
||||||
|
postMessage({ hasWebcodecsConfig: true });
|
||||||
|
} else if (cmd === 'decode') {
|
||||||
|
if (!has_init) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let chunk = new Uint8Array(event.data.data);
|
||||||
|
let { code_type, pts, timestamp, seq, type } = event.data;
|
||||||
|
|
||||||
|
if (decoderType === 'webcodecs') {
|
||||||
|
if (videoDecoder.state === 'unconfigured' || videoDecoder.state === 'closed') return;
|
||||||
|
|
||||||
|
let encodedVideoChunk = new EncodedVideoChunk({
|
||||||
|
type,
|
||||||
|
timestamp,
|
||||||
|
data: chunk
|
||||||
|
});
|
||||||
|
videoDecoder.decode(encodedVideoChunk);
|
||||||
|
dataParams[timestamp] = { timestamp, seq, type };
|
||||||
|
} else {
|
||||||
|
Module.HEAP8.set(chunk, inputBuffer);
|
||||||
|
if (seq) {
|
||||||
|
dataSeq = seq;
|
||||||
|
}
|
||||||
|
|
||||||
|
let len = Module._hevc_to_yuv(code_type, inputBuffer, chunk.length, outputBuffer, width, height);
|
||||||
|
if (len > 0) {
|
||||||
|
let data1 = Module.HEAPU8.subarray(width, width + 4);
|
||||||
|
let result_width = data1[0] + data1[1] * 256;
|
||||||
|
let data2 = Module.HEAPU8.subarray(height, height + 4);
|
||||||
|
let result_height = data2[0] + data2[1] * 256;
|
||||||
|
|
||||||
|
let outArray = Module.HEAPU8.subarray(outputBuffer, outputBuffer + len);
|
||||||
|
let outputData = new Uint8Array(outArray);
|
||||||
|
if (dataSeq) {
|
||||||
|
seq = dataSeq;
|
||||||
|
dataSeq = 0;
|
||||||
|
}
|
||||||
|
postMessage({ data: outputData, pts: pts, width: result_width, height: result_height, timestamp, seq }, [outputData.buffer]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (cmd === 'close') {
|
||||||
|
if (videoDecoder && videoDecoder.state === 'configured') {
|
||||||
|
videoDecoder.close();
|
||||||
|
}
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
};
|
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
|
@ -0,0 +1,92 @@
|
||||||
|
/*
|
||||||
|
* Author: Liu Ninggang
|
||||||
|
* File Created: Monday, 27th September 2021 10:28:13 am
|
||||||
|
* Last Modified: Wednesday, 29th September 2021 5:36:21 pm
|
||||||
|
* Modified By: Liu Ninggang
|
||||||
|
* Copyright (c) 2021 TP-LINK
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* eslint-disable */
|
||||||
|
let inputBuffer;
|
||||||
|
let outputBuffer;
|
||||||
|
let finalInputBuffer;
|
||||||
|
let finalOutputBuffer;
|
||||||
|
let has_init = 0;
|
||||||
|
let isG726 = true;
|
||||||
|
var Module = {};
|
||||||
|
|
||||||
|
postMessage({ hasInstalled: true });
|
||||||
|
|
||||||
|
this.onmessage = function (event) {
|
||||||
|
let { cmd } = event.data;
|
||||||
|
if (cmd === 'init') {
|
||||||
|
let { scripts, wasmBinary } = event.data;
|
||||||
|
Module.wasmBinary = wasmBinary;
|
||||||
|
self.importScripts(...scripts);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!Module['asm']) return;
|
||||||
|
|
||||||
|
if (!has_init) {
|
||||||
|
Module._tp_ns2_init(event.data.samplerate || 8000);
|
||||||
|
has_init = 1;
|
||||||
|
inputBuffer = Module._malloc(1024 * 2);
|
||||||
|
outputBuffer = Module._malloc(1024 * 2);
|
||||||
|
finalOutputBuffer = Module._malloc(1024 * 2);
|
||||||
|
postMessage({ hasInit: true });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let { dts, pts, timestamp, seq, type, samplerate, bitCount, decoderType } = event.data;
|
||||||
|
let param = {
|
||||||
|
pts: pts,
|
||||||
|
dts: dts,
|
||||||
|
timestamp: timestamp,
|
||||||
|
seq: seq,
|
||||||
|
type: type,
|
||||||
|
samplerate: samplerate,
|
||||||
|
bitCount: bitCount
|
||||||
|
};
|
||||||
|
const chunk = event.data.data;
|
||||||
|
Module.HEAPU8.set(chunk, inputBuffer);
|
||||||
|
let chunk_num;
|
||||||
|
if (decoderType === '_decodeG726') {
|
||||||
|
if (isG726) {
|
||||||
|
Module._initG726State(0, bitCount);
|
||||||
|
isG726 = false;
|
||||||
|
}
|
||||||
|
chunk_num = Module._decodeG726(0, inputBuffer, chunk.length, outputBuffer);
|
||||||
|
if (chunk_num === 1) {
|
||||||
|
let getLength = ((chunk.length << 4) / bitCount) >>> 1;
|
||||||
|
Module._tp_ns2_process(outputBuffer, getLength, finalOutputBuffer);
|
||||||
|
let finalOutArray = Module.HEAP16.subarray(finalOutputBuffer >> 1, (finalOutputBuffer + getLength * 2) >> 1);
|
||||||
|
finalOutArray = new Int16Array(finalOutArray);
|
||||||
|
postMessage({
|
||||||
|
data: finalOutArray.buffer,
|
||||||
|
...param
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else if (decoderType === '_decodeAAC') {
|
||||||
|
var pcmLen = Module._decodeAAC(outputBuffer, inputBuffer, chunk.length);
|
||||||
|
|
||||||
|
if (pcmLen >= 0) {
|
||||||
|
Module._tp_ns2_process(outputBuffer, pcmLen, finalOutputBuffer);
|
||||||
|
let finalOutArray = Module.HEAP16.subarray(finalOutputBuffer >> 1, (finalOutputBuffer + pcmLen * 2) >> 1);
|
||||||
|
finalOutArray = new Int16Array(finalOutArray);
|
||||||
|
postMessage({
|
||||||
|
data: finalOutArray.buffer,
|
||||||
|
...param
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
chunk_num = Module[decoderType](outputBuffer, inputBuffer, chunk.length);
|
||||||
|
if (chunk_num === 1) {
|
||||||
|
Module._tp_ns2_process(outputBuffer, chunk.length, finalOutputBuffer);
|
||||||
|
let finalOutArray = Module.HEAP16.subarray(finalOutputBuffer >> 1, (finalOutputBuffer + chunk.length * 2) >> 1);
|
||||||
|
finalOutArray = new Int16Array(finalOutArray);
|
||||||
|
postMessage({
|
||||||
|
data: finalOutArray.buffer,
|
||||||
|
...param
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,42 @@
|
||||||
|
postMessage({ hasInstalled: true });
|
||||||
|
|
||||||
|
let offscreenCanvas;
|
||||||
|
let webgl;
|
||||||
|
let decoderType;
|
||||||
|
|
||||||
|
this.onmessage = (event) => {
|
||||||
|
const { data } = event;
|
||||||
|
const { cmd } = data;
|
||||||
|
|
||||||
|
if (cmd === 'init') {
|
||||||
|
const { canvas, options, scripts } = data;
|
||||||
|
|
||||||
|
self.importScripts(...scripts);
|
||||||
|
const WebGL = self.webgl.default;
|
||||||
|
|
||||||
|
offscreenCanvas = canvas;
|
||||||
|
webgl = new WebGL(canvas, options);
|
||||||
|
decoderType = options.decoderType;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cmd === 'render') {
|
||||||
|
const { frame, width, height, yLength, uvLength } = data;
|
||||||
|
|
||||||
|
offscreenCanvas.width = width;
|
||||||
|
offscreenCanvas.height = height;
|
||||||
|
|
||||||
|
if (decoderType === 'webcodecs') {
|
||||||
|
webgl && webgl.renderFrame(frame, width, height);
|
||||||
|
if (!webgl) frame.close();
|
||||||
|
} else {
|
||||||
|
webgl && webgl.renderFrame(frame, width, height, yLength, uvLength);
|
||||||
|
}
|
||||||
|
} else if (cmd === 'display') {
|
||||||
|
const { options } = data;
|
||||||
|
webgl && webgl.setDisplayInfo(options);
|
||||||
|
} else if (cmd === 'close') {
|
||||||
|
webgl && webgl.dispose();
|
||||||
|
this.close();
|
||||||
|
}
|
||||||
|
};
|
|
@ -0,0 +1,23 @@
|
||||||
|
import type { AppRouteRecordRaw } from '/@/router/types';
|
||||||
|
import { LAYOUT } from '/@/router/constant';
|
||||||
|
|
||||||
|
export const tplink: AppRouteRecordRaw = {
|
||||||
|
path: '/tplink',
|
||||||
|
name: 'ai-parent',
|
||||||
|
component: LAYOUT,
|
||||||
|
meta: {
|
||||||
|
title: 'tplink',
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: 'cameraConfig',
|
||||||
|
name: 'cameraConfig',
|
||||||
|
component: () => import('/@/views/iot/tplink/camera/components/CameraPictureConfig.vue'),
|
||||||
|
meta: {
|
||||||
|
title: '画面配置',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
export const tplinkList = [tplink];
|
|
@ -23,6 +23,7 @@ import { getBackMenuAndPerms } from '/@/api/sys/menu';
|
||||||
|
|
||||||
import { useMessage } from '/@/hooks/web/useMessage';
|
import { useMessage } from '/@/hooks/web/useMessage';
|
||||||
import { PageEnum } from '/@/enums/pageEnum';
|
import { PageEnum } from '/@/enums/pageEnum';
|
||||||
|
import { tplinkList } from "@/router/routes/tplink";
|
||||||
|
|
||||||
// 系统权限
|
// 系统权限
|
||||||
interface AuthItem {
|
interface AuthItem {
|
||||||
|
@ -279,7 +280,7 @@ export const usePermissionStore = defineStore({
|
||||||
|
|
||||||
routeList = flatMultiLevelRoutes(routeList);
|
routeList = flatMultiLevelRoutes(routeList);
|
||||||
// update-begin--author:liaozhiyang---date:20240529---for:【TV360X-522】ai助手路由写死在前端
|
// update-begin--author:liaozhiyang---date:20240529---for:【TV360X-522】ai助手路由写死在前端
|
||||||
routes = [PAGE_NOT_FOUND_ROUTE, ...routeList, ...staticRoutesList];
|
routes = [PAGE_NOT_FOUND_ROUTE, ...routeList, ...staticRoutesList, ...tplinkList];
|
||||||
// update-end--author:liaozhiyang---date:20240529---for:【TV360X-522】ai助手路由写死在前端
|
// update-end--author:liaozhiyang---date:20240529---for:【TV360X-522】ai助手路由写死在前端
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,268 @@
|
||||||
|
import { defHttp } from '/@/utils/http/axios';
|
||||||
|
|
||||||
|
enum Api {
|
||||||
|
queryProjectTreeSync = '/iot/projectInfo/queryRegionTreeSync',
|
||||||
|
queryRegionTreeSync = '/iot/regionInfo/queryRegionTreeSync',
|
||||||
|
syncProject = '/iot/projectInfo/sync',
|
||||||
|
syncRegion = '/iot/regionInfo/sync',
|
||||||
|
list = '/iot/cameraInfo/list',
|
||||||
|
ipcCapability = '/iot/cameraInfo/getIpcCapability',
|
||||||
|
nuList = '/iot/cameraInfo/nuList',
|
||||||
|
edit = '/iot/cameraInfo/edit',
|
||||||
|
rebootDevice = '/iot/cameraInfo/rebootDevice',
|
||||||
|
previewUrl = '/iot/cameraInfo/getPreviewUrl',
|
||||||
|
getImageCommon = '/iot/cameraInfo/getImageCommon',
|
||||||
|
setImageCommon = '/iot/cameraInfo/setImageCommon',
|
||||||
|
getOsdCapability = '/iot/cameraInfo/getOsdCapability',
|
||||||
|
getOsd = '/iot/cameraInfo/getOsd',
|
||||||
|
setOsd = '/iot/cameraInfo/setOsd',
|
||||||
|
getTamperDet = '/iot/cameraInfo/getTamperDet',
|
||||||
|
setTamperDet = '/iot/cameraInfo/setTamperDet',
|
||||||
|
getTamperNotif = '/iot/cameraInfo/getTamperNotif',
|
||||||
|
setTamperNotif = '/iot/cameraInfo/setTamperNotif',
|
||||||
|
testAudio = '/iot/cameraInfo/testAudio',
|
||||||
|
getAlarmInfo = '/iot/cameraInfo/getAlarmInfo',
|
||||||
|
setAlarmInfo = '/iot/cameraInfo/setAlarmInfo',
|
||||||
|
getAlarmPlan = '/iot/cameraInfo/getAlarmPlan',
|
||||||
|
setAlarmPlan = '/iot/cameraInfo/setAlarmPlan',
|
||||||
|
getVideoParams = '/iot/cameraInfo/getVideoParams',
|
||||||
|
setVideoParams = '/iot/cameraInfo/setVideoParams',
|
||||||
|
configRecovery = '/iot/cameraInfo/configRecovery',
|
||||||
|
searchVideo = '/iot/cameraInfo/searchVideo',
|
||||||
|
getPlaybackUrlList = '/iot/cameraInfo/getPlaybackUrlList',
|
||||||
|
deletePlaybackChn = '/iot/cameraInfo/deletePlaybackChn',
|
||||||
|
getMultitransUrl = '/iot/cameraInfo/getMultitransUrl',
|
||||||
|
getRecordCfgs = '/iot/cameraInfo/getRecordCfgs',
|
||||||
|
setRecordCfgs = '/iot/cameraInfo/setRecordCfgs',
|
||||||
|
getBatchProgress = '/iot/cameraInfo/getBatchProgress',
|
||||||
|
uploadToServer = '/iot/cameraInfo/uploadToServer',
|
||||||
|
stopUploadToServer = '/iot/cameraInfo/stopUploadToServer',
|
||||||
|
getUploadToServerProcess = '/iot/cameraInfo/getUploadToServerProcess',
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取项目树列表
|
||||||
|
* @param params
|
||||||
|
*/
|
||||||
|
export const queryProjectTreeSync = (params?) => defHttp.get({ url: Api.queryProjectTreeSync, params });
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取区域树列表
|
||||||
|
* @param params
|
||||||
|
*/
|
||||||
|
export const queryRegionTreeSync = (params?) => defHttp.get({ url: Api.queryRegionTreeSync, params });
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 同步项目
|
||||||
|
* @param params
|
||||||
|
*/
|
||||||
|
export const syncProject = (params?) => defHttp.get({ url: Api.syncProject, params });
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 同步区域
|
||||||
|
* @param params
|
||||||
|
*/
|
||||||
|
export const syncRegion = (params?) => defHttp.get({ url: Api.syncRegion, params });
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 列表接口
|
||||||
|
* @param params
|
||||||
|
*/
|
||||||
|
export const list = (params) => defHttp.get({ url: Api.list, params });
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取IPC能力集
|
||||||
|
* @param params
|
||||||
|
*/
|
||||||
|
export const getIpcCapability = (params) => defHttp.get({ url: Api.ipcCapability, params });
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取获取预览通道的url
|
||||||
|
* @param params
|
||||||
|
*/
|
||||||
|
export const getPreviewUrl = (params) => defHttp.get({ url: Api.previewUrl, params });
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 列表接口
|
||||||
|
* @param params
|
||||||
|
*/
|
||||||
|
export const nuList = (params) => defHttp.get({ url: Api.nuList, params });
|
||||||
|
/**
|
||||||
|
* 更新
|
||||||
|
* @param params
|
||||||
|
*/
|
||||||
|
export const update = (params) => {
|
||||||
|
return defHttp.post({ url: Api.edit, params });
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 重启
|
||||||
|
* @param params
|
||||||
|
*/
|
||||||
|
export const rebootDevice = (params) => {
|
||||||
|
return defHttp.post({ url: Api.rebootDevice, params });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取画面基本信息
|
||||||
|
* @param params
|
||||||
|
*/
|
||||||
|
export const getImageCommon = (params) => defHttp.post({ url: Api.getImageCommon, params });
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置画面基本信息
|
||||||
|
* @param params
|
||||||
|
*/
|
||||||
|
export const setImageCommon = (params) => defHttp.post({ url: Api.setImageCommon, params });
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取OSD能力集参数
|
||||||
|
* @param params
|
||||||
|
*/
|
||||||
|
export const getOsdCapability = (params) => defHttp.post({ url: Api.getOsdCapability, params });
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取OSD参数
|
||||||
|
* @param params
|
||||||
|
*/
|
||||||
|
export const getOsd = (params) => defHttp.post({ url: Api.getOsd, params });
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置OSD参数
|
||||||
|
* @param params
|
||||||
|
*/
|
||||||
|
export const setOsd = (params) => defHttp.post({ url: Api.setOsd, params });
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取镜头遮挡参数
|
||||||
|
* @param params
|
||||||
|
*/
|
||||||
|
export const getTamperDet = (params) => defHttp.post({ url: Api.getTamperDet, params });
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置镜头遮挡参数
|
||||||
|
* @param params
|
||||||
|
*/
|
||||||
|
export const setTamperDet = (params) => defHttp.post({ url: Api.setTamperDet, params });
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取镜头遮挡处理方式
|
||||||
|
* @param params
|
||||||
|
*/
|
||||||
|
export const getTamperNotif = (params) => defHttp.post({ url: Api.getTamperNotif, params });
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置镜头遮挡处理方式
|
||||||
|
* @param params
|
||||||
|
*/
|
||||||
|
export const setTamperNotif = (params) => defHttp.post({ url: Api.setTamperNotif, params });
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 报警声音试听
|
||||||
|
* @param params
|
||||||
|
*/
|
||||||
|
export const testAudio = (params) => defHttp.post({ url: Api.testAudio, params });
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取白光/声音告警参数
|
||||||
|
* @param params
|
||||||
|
*/
|
||||||
|
export const getAlarmInfo = (params) => defHttp.post({ url: Api.getAlarmInfo, params });
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置白光/声音告警参数
|
||||||
|
* @param params
|
||||||
|
*/
|
||||||
|
export const setAlarmInfo = (params) => defHttp.post({ url: Api.setAlarmInfo, params });
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取白光/声音告警布防时间
|
||||||
|
* @param params
|
||||||
|
*/
|
||||||
|
export const getAlarmPlan = (params) => defHttp.post({ url: Api.getAlarmPlan, params });
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置白光/声音告警布防时间
|
||||||
|
* @param params
|
||||||
|
*/
|
||||||
|
export const setAlarmPlan = (params) => defHttp.post({ url: Api.setAlarmPlan, params });
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取码率参数
|
||||||
|
* @param params
|
||||||
|
*/
|
||||||
|
export const getVideoParams = (params) => defHttp.post({ url: Api.getVideoParams, params });
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置码率参数
|
||||||
|
* @param params
|
||||||
|
*/
|
||||||
|
export const setVideoParams = (params) => defHttp.post({ url: Api.setVideoParams, params });
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 恢复画面默认值
|
||||||
|
* @param params
|
||||||
|
*/
|
||||||
|
export const configRecovery = (params) => defHttp.post({ url: Api.configRecovery, params });
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 列表接口
|
||||||
|
* @param params
|
||||||
|
*/
|
||||||
|
export const searchVideo = (params) => defHttp.get({ url: Api.searchVideo, params });
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取获取预览通道的url
|
||||||
|
* @param params
|
||||||
|
*/
|
||||||
|
export const getPlaybackUrlList = (params) => defHttp.get({ url: Api.getPlaybackUrlList, params });
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取获取预览通道的url
|
||||||
|
* @param params
|
||||||
|
*/
|
||||||
|
export const deletePlaybackChn = (params) => defHttp.get({ url: Api.deletePlaybackChn, params });
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取摄像头录像回放地址--通过功能集具备的Multitrans能力
|
||||||
|
* @param params
|
||||||
|
*/
|
||||||
|
export const getMultitransUrl = (params) => defHttp.get({ url: Api.getMultitransUrl, params });
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取录像配置--暂无用
|
||||||
|
* @param params
|
||||||
|
*/
|
||||||
|
export const getRecordCfgs = (params) => defHttp.get({ url: Api.getRecordCfgs, params });
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置录像计划--暂无用
|
||||||
|
* @param params
|
||||||
|
*/
|
||||||
|
export const setRecordCfgs = (params) => defHttp.get({ url: Api.setRecordCfgs, params });
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取批量操作录像计划进度--暂无用
|
||||||
|
* @param params
|
||||||
|
*/
|
||||||
|
export const getBatchProgress = (params) => defHttp.get({ url: Api.getBatchProgress, params });
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 回放上传
|
||||||
|
* @param params
|
||||||
|
*/
|
||||||
|
export const uploadToServer = (params) => defHttp.get({ url: Api.uploadToServer, params });
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 停止回放上传
|
||||||
|
* @param params
|
||||||
|
*/
|
||||||
|
export const stopUploadToServer = (params) => defHttp.get({ url: Api.stopUploadToServer, params });
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取回放上传进度
|
||||||
|
* @param params
|
||||||
|
*/
|
||||||
|
export const getUploadToServerProcess = (params) => defHttp.get({ url: Api.getUploadToServerProcess, params });
|
||||||
|
|
|
@ -0,0 +1,266 @@
|
||||||
|
import {BasicColumn} from '/@/components/Table';
|
||||||
|
import {FormSchema} from '/@/components/Table';
|
||||||
|
import dayjs from 'dayjs';
|
||||||
|
|
||||||
|
//列表数据
|
||||||
|
export const columns: BasicColumn[] = [
|
||||||
|
{
|
||||||
|
title: '设备序号',
|
||||||
|
align: "center",
|
||||||
|
dataIndex: 'deviceIndex'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '设备名称',
|
||||||
|
align: "center",
|
||||||
|
dataIndex: 'deviceName'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '设备状态',
|
||||||
|
align: "center",
|
||||||
|
dataIndex: 'deviceStatus_dictText'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '设备型号',
|
||||||
|
align: "center",
|
||||||
|
dataIndex: 'deviceModel'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'IP地址',
|
||||||
|
align: "center",
|
||||||
|
dataIndex: 'ip'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'MAC地址',
|
||||||
|
align: "center",
|
||||||
|
dataIndex: 'mac'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '区域名称',
|
||||||
|
align: "center",
|
||||||
|
dataIndex: 'regionName'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '父设备名称',
|
||||||
|
align: "center",
|
||||||
|
dataIndex: 'parentDeviceName'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '项目名称',
|
||||||
|
align: "center",
|
||||||
|
dataIndex: 'projectName'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '位置名称',
|
||||||
|
align: "center",
|
||||||
|
dataIndex: 'locationName'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '护理单元',
|
||||||
|
align: "center",
|
||||||
|
dataIndex: 'nuId_dictText',
|
||||||
|
},
|
||||||
|
// {
|
||||||
|
// title: '护理单元',
|
||||||
|
// align: "center",
|
||||||
|
// dataIndex: 'nuName'
|
||||||
|
// },
|
||||||
|
];
|
||||||
|
|
||||||
|
export const formSchema: FormSchema[] = [
|
||||||
|
{
|
||||||
|
label: '',
|
||||||
|
field: 'id',
|
||||||
|
component: 'Input',
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '设备序号',
|
||||||
|
field: 'deviceIndex',
|
||||||
|
component: 'Input',
|
||||||
|
// dynamicDisabled: ({value})=>{
|
||||||
|
// return true;
|
||||||
|
// }
|
||||||
|
dynamicDisabled: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '设备名称',
|
||||||
|
field: 'deviceName',
|
||||||
|
component: 'Input',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '设备状态',
|
||||||
|
field: 'deviceStatus',
|
||||||
|
component: 'JDictSelectTag',
|
||||||
|
componentProps: {
|
||||||
|
dictCode: 'tplink_status',
|
||||||
|
placeholder: '请选择设备状态',
|
||||||
|
},
|
||||||
|
dynamicDisabled: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '设备型号',
|
||||||
|
field: 'deviceModel',
|
||||||
|
component: 'Input',
|
||||||
|
dynamicDisabled: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'IP地址',
|
||||||
|
field: 'ip',
|
||||||
|
component: 'Input',
|
||||||
|
dynamicDisabled: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'MAC地址',
|
||||||
|
field: 'mac',
|
||||||
|
component: 'Input',
|
||||||
|
dynamicDisabled: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '区域名称',
|
||||||
|
field: 'regionName',
|
||||||
|
component: 'Input',
|
||||||
|
dynamicDisabled: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '父设备名称',
|
||||||
|
field: 'parentDeviceName',
|
||||||
|
component: 'Input',
|
||||||
|
dynamicDisabled: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '项目ID',
|
||||||
|
field: 'projectId',
|
||||||
|
component: 'Input',
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '项目名称',
|
||||||
|
field: 'projectName',
|
||||||
|
component: 'Input',
|
||||||
|
dynamicDisabled: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '位置名称',
|
||||||
|
field: 'locationName',
|
||||||
|
component: 'Input',
|
||||||
|
dynamicDisabled: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '护理单元',
|
||||||
|
field: 'nuId',
|
||||||
|
component: 'JSelectNu',
|
||||||
|
componentProps: {
|
||||||
|
rowKey: 'nuId',
|
||||||
|
labelKey: 'nuName',
|
||||||
|
selectType: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '设备重启',
|
||||||
|
field: 'id',
|
||||||
|
component: 'Input',
|
||||||
|
slot: 'customInput',
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
export const searchFormSchema: FormSchema[] = [
|
||||||
|
{
|
||||||
|
label: '设备状态',
|
||||||
|
field: 'deviceStatus',
|
||||||
|
component: 'JDictSelectTag',
|
||||||
|
componentProps: {
|
||||||
|
dictCode: 'tplink_status',
|
||||||
|
placeholder: '请选择状态',
|
||||||
|
stringToNumber: true,
|
||||||
|
},
|
||||||
|
//colProps: { span: 6 },
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
//列表数据
|
||||||
|
export const recordingColumns: BasicColumn[] = [
|
||||||
|
{
|
||||||
|
title: '开始时间',
|
||||||
|
align: "center",
|
||||||
|
dataIndex: 'startTimeFt'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '结束时间',
|
||||||
|
align: "center",
|
||||||
|
dataIndex: 'endTimeFt'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '时长',
|
||||||
|
align: "center",
|
||||||
|
dataIndex: 'duration'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '大小(MB)',
|
||||||
|
align: "center",
|
||||||
|
dataIndex: 'size'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '回放类型',
|
||||||
|
align: "center",
|
||||||
|
dataIndex: 'videoType',
|
||||||
|
customRender:({record})=>{
|
||||||
|
return record.videoType?(record.videoType=='1'?'定时录像':'移动侦测'):'';
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '错误状态',
|
||||||
|
align: "center",
|
||||||
|
dataIndex: 'errorMsg'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '设备序号',
|
||||||
|
align: "center",
|
||||||
|
dataIndex: 'videoDevId'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '存储设备ID',
|
||||||
|
align: "center",
|
||||||
|
dataIndex: 'storageDevId'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '双摄IPC通道ID',
|
||||||
|
align: "center",
|
||||||
|
dataIndex: 'channelId'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '所属NVS的ID',
|
||||||
|
align: "center",
|
||||||
|
dataIndex: 'nvsIdInPoolList'
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export const searchRecording: FormSchema[] = [
|
||||||
|
{
|
||||||
|
field: 'dataDate',
|
||||||
|
label: '录像日期',
|
||||||
|
component: 'DatePicker',
|
||||||
|
defaultValue: dayjs(new Date()).format('YYYY-MM-DD'),
|
||||||
|
componentProps: {
|
||||||
|
//日期格式化,页面上显示的值
|
||||||
|
format: 'YYYY-MM-DD',
|
||||||
|
//返回值格式化(绑定值的格式)
|
||||||
|
valueFormat: 'YYYY-MM-DD',
|
||||||
|
//是否显示今天按钮
|
||||||
|
showToday: true,
|
||||||
|
},
|
||||||
|
colProps: { span: 6 },
|
||||||
|
},
|
||||||
|
// {
|
||||||
|
// field: 'videoType',
|
||||||
|
// label: '回放类型',
|
||||||
|
// component: 'JDictSelectTag',
|
||||||
|
// componentProps: {
|
||||||
|
// placeholder: '请选择类型',
|
||||||
|
// options: [
|
||||||
|
// { label: '定时录像', value: '1' },
|
||||||
|
// { label: '移动侦测', value: '2' },
|
||||||
|
// ],
|
||||||
|
// },
|
||||||
|
// colProps: { span: 6 },
|
||||||
|
// },
|
||||||
|
];
|
|
@ -0,0 +1,300 @@
|
||||||
|
<template>
|
||||||
|
<a-spin :spinning="confirmLoading">
|
||||||
|
<JFormContainer :disabled="disabled">
|
||||||
|
<template #detail>
|
||||||
|
<a-row>
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-row>
|
||||||
|
<a-col :span="20">
|
||||||
|
<a-divider orientation="left">主码流</a-divider>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="20">
|
||||||
|
<a-row style="margin-bottom: 14px">
|
||||||
|
<a-col :span="6" class="labelText">
|
||||||
|
分辨率
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="16">
|
||||||
|
<a-select v-model:value="formData.main_resolution" :options="formData.mainResolutionArr"/>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="20">
|
||||||
|
<a-row style="margin-bottom: 14px">
|
||||||
|
<a-col :span="6" class="labelText">
|
||||||
|
视频帧率
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="16">
|
||||||
|
<a-select v-model:value="formData.main_frame_rate" :options="formData.mainFrameRatesArr"/>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="20">
|
||||||
|
<a-row style="margin-bottom: 14px">
|
||||||
|
<a-col :span="6" class="labelText">
|
||||||
|
码率类型
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="16">
|
||||||
|
<a-select v-model:value="formData.main_bitrate_type" :options="formData.mainBitrateTypeArr"/>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="20">
|
||||||
|
<a-row style="margin-bottom: 14px">
|
||||||
|
<a-col :span="6" class="labelText">
|
||||||
|
图像质量
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="16">
|
||||||
|
<a-select v-model:value="formData.main_quality" :options="formData.mainQualityArr"/>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="20">
|
||||||
|
<a-row style="margin-bottom: 10px">
|
||||||
|
<a-col :span="6" class="labelText">
|
||||||
|
码率上限
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="16">
|
||||||
|
<a-select v-model:value="formData.main_bitrate" :options="formData.mainBitrateArr"/>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="20">
|
||||||
|
<a-row style="margin-bottom: 10px">
|
||||||
|
<a-col :span="6" class="labelText">
|
||||||
|
视频编码
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="16">
|
||||||
|
<a-select v-model:value="formData.main_encode_type" :options="formData.mainEncodeTypeArr"/>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
<a-row style="margin-top: 14px">
|
||||||
|
<a-col :span="1"></a-col>
|
||||||
|
<a-col :span="20">
|
||||||
|
<a-button type="primary" preIcon="ant-design:save-outlined" @click="setParams">保存</a-button>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-row>
|
||||||
|
<a-col :span="20">
|
||||||
|
<a-divider orientation="left">子码流</a-divider>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="20">
|
||||||
|
<a-row style="margin-bottom: 14px">
|
||||||
|
<a-col :span="6" class="labelText">
|
||||||
|
分辨率
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="16">
|
||||||
|
<a-select v-model:value="formData.minor_resolution" :options="formData.minorResolutionArr"/>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="20">
|
||||||
|
<a-row style="margin-bottom: 14px">
|
||||||
|
<a-col :span="6" class="labelText">
|
||||||
|
视频帧率
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="16">
|
||||||
|
<a-select v-model:value="formData.minor_frame_rate" :options="formData.minorFrameRatesArr"/>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="20">
|
||||||
|
<a-row style="margin-bottom: 14px">
|
||||||
|
<a-col :span="6" class="labelText">
|
||||||
|
码率类型
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="16">
|
||||||
|
<a-select v-model:value="formData.minor_bitrate_type" :options="formData.minorBitrateTypeArr"/>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="20">
|
||||||
|
<a-row style="margin-bottom: 14px">
|
||||||
|
<a-col :span="6" class="labelText">
|
||||||
|
图像质量
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="16">
|
||||||
|
<a-select v-model:value="formData.minor_quality" :options="formData.minorQualityArr"/>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="20">
|
||||||
|
<a-row style="margin-bottom: 10px">
|
||||||
|
<a-col :span="6" class="labelText">
|
||||||
|
码率上限
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="16">
|
||||||
|
<a-select v-model:value="formData.minor_bitrate" :options="formData.minorBitrateArr"/>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="20">
|
||||||
|
<a-row style="margin-bottom: 10px">
|
||||||
|
<a-col :span="6" class="labelText">
|
||||||
|
视频编码
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="16">
|
||||||
|
<a-select v-model:value="formData.minor_encode_type" :options="formData.minorEncodeTypeArr"/>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</template>
|
||||||
|
</JFormContainer>
|
||||||
|
</a-spin>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { ref, reactive, defineProps, onMounted, watch,} from 'vue';
|
||||||
|
import { defHttp } from '/@/utils/http/axios';
|
||||||
|
import { useMessage } from '/@/hooks/web/useMessage';
|
||||||
|
import JDictSelectTag from '/@/components/Form/src/jeecg/components/JDictSelectTag.vue';
|
||||||
|
import { getValueType } from '/@/utils';
|
||||||
|
import { Form } from 'ant-design-vue';
|
||||||
|
import JFormContainer from '/@/components/Form/src/container/JFormContainer.vue';
|
||||||
|
import { getVideoParams,setVideoParams } from "@/views/iot/tplink/camera/camera.api";
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
data: { type: Object, default: () => ({}) },
|
||||||
|
});
|
||||||
|
|
||||||
|
const formData = reactive<Record<string, any>>({
|
||||||
|
deviceIndex: '', //设备索引
|
||||||
|
|
||||||
|
main_quality: '', //图像质量
|
||||||
|
mainQualityArr: [],
|
||||||
|
main_bitrate: '', //码率上限
|
||||||
|
mainBitrateArr: [],
|
||||||
|
main_frame_rate: '', //帧速率
|
||||||
|
mainFrameRatesArr: [],
|
||||||
|
main_encode_type: '', //视频编码
|
||||||
|
mainEncodeTypeArr: [],
|
||||||
|
main_resolution: '', //分辨率
|
||||||
|
mainResolutionArr: [],
|
||||||
|
main_bitrate_type: '', //码率类型
|
||||||
|
mainBitrateTypeArr: [],
|
||||||
|
|
||||||
|
minor_quality: '', //图像质量
|
||||||
|
minorQualityArr: [],
|
||||||
|
minor_bitrate: '', //码率上限
|
||||||
|
minorBitrateArr: [],
|
||||||
|
minor_frame_rate: '', //帧速率
|
||||||
|
minorFrameRatesArr: [],
|
||||||
|
minor_encode_type: '', //视频编码
|
||||||
|
minorEncodeTypeArr: [],
|
||||||
|
minor_resolution: '', //分辨率
|
||||||
|
minorResolutionArr: [],
|
||||||
|
minor_bitrate_type: '', //码率类型
|
||||||
|
minorBitrateTypeArr: [],
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
const confirmLoading = ref<boolean>(false);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取画面通用信息
|
||||||
|
*/
|
||||||
|
function getParams(deviceIndex){
|
||||||
|
if(deviceIndex==null){
|
||||||
|
return
|
||||||
|
}
|
||||||
|
getVideoParams({
|
||||||
|
"deviceIndex": deviceIndex
|
||||||
|
}).then(res=>{
|
||||||
|
let mainData = res.mainData;
|
||||||
|
let minorData = res.minorData;
|
||||||
|
|
||||||
|
formData.main_quality = mainData.quality;
|
||||||
|
formData.main_bitrate = mainData.bitrate;
|
||||||
|
formData.main_frame_rate = mainData.frame_rate;
|
||||||
|
formData.main_encode_type = mainData.encode_type;
|
||||||
|
formData.main_resolution = mainData.resolution;
|
||||||
|
formData.main_bitrate_type = mainData.bitrate_type;
|
||||||
|
|
||||||
|
formData.minor_quality = minorData.quality;
|
||||||
|
formData.minor_bitrate = minorData.bitrate;
|
||||||
|
formData.minor_frame_rate = minorData.frame_rate;
|
||||||
|
formData.minor_encode_type = minorData.encode_type;
|
||||||
|
formData.minor_resolution = minorData.resolution;
|
||||||
|
formData.minor_bitrate_type = minorData.bitrate_type;
|
||||||
|
|
||||||
|
formData.mainQualityArr = res.mainQualityArr;
|
||||||
|
formData.mainBitrateArr = res.mainBitrateArr;
|
||||||
|
formData.mainFrameRatesArr = res.mainFrameRatesArr;
|
||||||
|
formData.mainEncodeTypeArr = res.mainEncodeTypeArr;
|
||||||
|
formData.mainResolutionArr = res.mainResolutionArr;
|
||||||
|
formData.mainBitrateTypeArr = res.mainBitrateTypeArr;
|
||||||
|
|
||||||
|
formData.minorQualityArr = res.minorQualityArr;
|
||||||
|
formData.minorBitrateArr = res.minorBitrateArr;
|
||||||
|
formData.minorFrameRatesArr = res.minorFrameRatesArr;
|
||||||
|
formData.minorEncodeTypeArr = res.minorEncodeTypeArr;
|
||||||
|
formData.minorResolutionArr = res.minorResolutionArr;
|
||||||
|
formData.minorBitrateTypeArr = res.minorBitrateTypeArr;
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置画面通用信息
|
||||||
|
*/
|
||||||
|
function setParams(){
|
||||||
|
confirmLoading.value = true;
|
||||||
|
let params = {
|
||||||
|
"deviceIndex": formData.deviceIndex,
|
||||||
|
"main":{
|
||||||
|
"quality": formData.main_quality,
|
||||||
|
"bitrate": formData.main_bitrate,
|
||||||
|
"frame_rate": formData.main_frame_rate,
|
||||||
|
"encode_type": formData.main_encode_type,
|
||||||
|
"resolution": formData.main_resolution,
|
||||||
|
"bitrate_type": formData.main_bitrate_type,
|
||||||
|
},
|
||||||
|
"minor":{
|
||||||
|
"quality": formData.minor_quality,
|
||||||
|
"bitrate": formData.minor_bitrate,
|
||||||
|
"frame_rate": formData.minor_frame_rate,
|
||||||
|
"encode_type": formData.minor_encode_type,
|
||||||
|
"resolution": formData.minor_resolution,
|
||||||
|
"bitrate_type": formData.minor_bitrate_type,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
setVideoParams(params).then(res=>{
|
||||||
|
confirmLoading.value = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
watch(
|
||||||
|
() => props.data,
|
||||||
|
async () => {
|
||||||
|
formData.deviceIndex = props.data.deviceIndex;
|
||||||
|
getParams(formData.deviceIndex);
|
||||||
|
},
|
||||||
|
{ deep: true, immediate: true }
|
||||||
|
);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.antd-modal-form {
|
||||||
|
padding: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.labelText {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-end;
|
||||||
|
padding-right: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-select {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
|
@ -0,0 +1,305 @@
|
||||||
|
<template>
|
||||||
|
<a-spin :spinning="confirmLoading">
|
||||||
|
<JFormContainer :disabled="disabled">
|
||||||
|
<template #detail>
|
||||||
|
<a-row>
|
||||||
|
<a-col :span="24">
|
||||||
|
<a-row>
|
||||||
|
<a-col :span="20">
|
||||||
|
<a-divider orientation="left">当镜头被其它物体遮挡时,发生报警。</a-divider>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="20">
|
||||||
|
<a-row style="margin-bottom: 14px">
|
||||||
|
<a-col :span="6" class="labelText">
|
||||||
|
镜头遮挡
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="16">
|
||||||
|
<a-switch v-model:checked="formData.enabled" checked-children="已开启" un-checked-children="已关闭" />
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="20">
|
||||||
|
<a-row style="margin-bottom: 14px">
|
||||||
|
<a-col :span="6" class="labelText">
|
||||||
|
灵敏度
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="6">
|
||||||
|
<a-slider v-model:value="formData.digitalSensitivity" :disabled="!formData.enabled" :min="1" :max="100" @change="(value) => changeSensitivity(value)"/>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="4">
|
||||||
|
<a-input-number v-model:value="formData.digitalSensitivity" :disabled="!formData.enabled" :min="1" :max="100" style="margin-left: 12px" @change="(value) => changeSensitivity(value)"/>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="24">
|
||||||
|
<a-row>
|
||||||
|
<a-col :span="20">
|
||||||
|
<a-divider orientation="left">处理方式</a-divider>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="20">
|
||||||
|
<a-row style="margin-bottom: 14px">
|
||||||
|
<a-col :span="5" class="labelText">
|
||||||
|
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="16">
|
||||||
|
<a-checkbox v-model:checked="formData.msgPushEnabled" :disabled="!formData.enabled">消息提醒(触发事件后,设备会上传云端信息)</a-checkbox>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="20">
|
||||||
|
<a-row style="margin-bottom: 14px">
|
||||||
|
<a-col :span="5" class="labelText">
|
||||||
|
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="16">
|
||||||
|
<a-checkbox v-model:checked="formData.lightAlarmEnabled" :disabled="!formData.enabled">白光告警(触发事件后,设备会发出白光告警)</a-checkbox>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="20">
|
||||||
|
<a-row style="margin-bottom: 14px">
|
||||||
|
<a-col :span="5" class="labelText">
|
||||||
|
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="16">
|
||||||
|
<a-checkbox v-model:checked="formData.soundAlarmEnabled" :disabled="!formData.enabled">声音告警(触发事件后,设备会发出声音告警)</a-checkbox>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="20">
|
||||||
|
<a-row style="margin-bottom: 14px">
|
||||||
|
<a-col :span="7" class="labelText">
|
||||||
|
报警声音
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="5">
|
||||||
|
<a-select v-model:value="formData.soundAlarmType" :options="formData.soundAlarmTypeArr" :disabled="!formData.enabled"/>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="5" style="padding-left: 10px">
|
||||||
|
<a-button preIcon="ant-design:customer-service-outlined" @click="soundAlarm" :disabled="!formData.enabled">试听</a-button>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="20">
|
||||||
|
<a-row style="margin-bottom: 10px">
|
||||||
|
<a-col :span="7" class="labelText">
|
||||||
|
播放次数
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="5">
|
||||||
|
<a-select v-model:value="formData.soundAlarmTimes" :options="formData.soundAlarmTimesArr" :disabled="!formData.enabled"/>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="24">
|
||||||
|
<a-row style="margin-top: 14px">
|
||||||
|
<a-col :span="1"></a-col>
|
||||||
|
<a-col :span="20">
|
||||||
|
<a-button type="primary" preIcon="ant-design:save-outlined" @click="setTamper">保存</a-button>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</template>
|
||||||
|
</JFormContainer>
|
||||||
|
</a-spin>
|
||||||
|
<a-modal v-model:visible="visible" title="设备上试听报警声音" :footer="null" width="400px">
|
||||||
|
<ul>
|
||||||
|
<li class="li" v-for="item in formData.soundAlarmTypeArr">
|
||||||
|
<div style="display: flex;justify-content: space-between;">
|
||||||
|
<div>{{item.label}}</div>
|
||||||
|
<div><a-button preIcon="ant-design:customer-service-outlined" @click="playAudio(item.value)">试听</a-button></div>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</a-modal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { ref, reactive, defineProps, onMounted, watch,} from 'vue';
|
||||||
|
import { defHttp } from '/@/utils/http/axios';
|
||||||
|
import { useMessage } from '/@/hooks/web/useMessage';
|
||||||
|
import JDictSelectTag from '/@/components/Form/src/jeecg/components/JDictSelectTag.vue';
|
||||||
|
import { getValueType } from '/@/utils';
|
||||||
|
import { Form } from 'ant-design-vue';
|
||||||
|
import JFormContainer from '/@/components/Form/src/container/JFormContainer.vue';
|
||||||
|
import {
|
||||||
|
getTamperDet,
|
||||||
|
setTamperDet,
|
||||||
|
getTamperNotif,
|
||||||
|
setTamperNotif,
|
||||||
|
testAudio
|
||||||
|
} from "@/views/iot/tplink/camera/camera.api";
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
data: { type: Object, default: () => ({}) },
|
||||||
|
});
|
||||||
|
|
||||||
|
const formData = reactive<Record<string, any>>({
|
||||||
|
deviceIndex: '', //设备索引
|
||||||
|
enabled: false, //镜头遮挡
|
||||||
|
digitalSensitivity: '50', //灵敏度
|
||||||
|
msgPushEnabled: true, //消息提醒
|
||||||
|
lightAlarmEnabled: false, //白光告警
|
||||||
|
soundAlarmEnabled: false, //声音告警
|
||||||
|
soundAlarmType: '', //报警声音
|
||||||
|
soundAlarmTypeArr: [
|
||||||
|
{value:'0',label:'报警音'},
|
||||||
|
{value:'1',label:'提示音'},
|
||||||
|
{value:'2',label:'警戒区域,尽快离开'},
|
||||||
|
{value:'3',label:'危险区域,请勿靠近'},
|
||||||
|
{value:'4',label:'此区域禁止停车'},
|
||||||
|
{value:'5',label:'你已进入实时监控区域'},
|
||||||
|
{value:'6',label:'您好,欢迎光临'},
|
||||||
|
{value:'7',label:'贵重物品,请勿触摸'},
|
||||||
|
{value:'8',label:'私人领域, 禁止入内'},
|
||||||
|
{value:'9',label:'水深危险,注意安全'},
|
||||||
|
{value:'10',label:'高处危险,请勿攀爬'},
|
||||||
|
{value:'11',label:'垃圾请分类投放'},
|
||||||
|
], //报警声音
|
||||||
|
soundAlarmTimes: '', //播放次数
|
||||||
|
soundAlarmTimesArr:[{value:'1',label:'1次'},{value:'2',label:'2次'},{value:'3',label:'3次'},{value:'4',label:'4次'},{value:'5',label:'5次'}],
|
||||||
|
});
|
||||||
|
|
||||||
|
const confirmLoading = ref<boolean>(false);
|
||||||
|
const visible = ref<boolean>(false);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取镜头遮挡信息
|
||||||
|
*/
|
||||||
|
function getTamper(deviceIndex){
|
||||||
|
if(deviceIndex==null){
|
||||||
|
return
|
||||||
|
}
|
||||||
|
//获取是否开启
|
||||||
|
getTamperDet({
|
||||||
|
"deviceIndex": deviceIndex
|
||||||
|
}).then(res=>{
|
||||||
|
if(res.enabled == "on"){
|
||||||
|
formData.enabled = true;
|
||||||
|
}else{
|
||||||
|
formData.enabled = false;
|
||||||
|
}
|
||||||
|
formData.digitalSensitivity = res.digital_sensitivity;
|
||||||
|
});
|
||||||
|
//获取处理方式
|
||||||
|
getTamperNotif({
|
||||||
|
"deviceIndex": deviceIndex
|
||||||
|
}).then(res=>{
|
||||||
|
let tamper_notif_list = res.tamper_notif_list;
|
||||||
|
let sound_alarm_info = res.sound_alarm_info;
|
||||||
|
if(tamper_notif_list.msg_push_enabled == "on"){
|
||||||
|
formData.msgPushEnabled = true;
|
||||||
|
}else{
|
||||||
|
formData.msgPushEnabled = false;
|
||||||
|
}
|
||||||
|
if(tamper_notif_list.light_alarm_enabled == "on"){
|
||||||
|
formData.lightAlarmEnabled = true;
|
||||||
|
}else{
|
||||||
|
formData.lightAlarmEnabled = false;
|
||||||
|
}
|
||||||
|
if(tamper_notif_list.sound_alarm_enabled == "on"){
|
||||||
|
formData.soundAlarmEnabled = true;
|
||||||
|
}else{
|
||||||
|
formData.soundAlarmEnabled = false;
|
||||||
|
}
|
||||||
|
formData.soundAlarmType = sound_alarm_info.sound_alarm_type;
|
||||||
|
formData.soundAlarmTimes = sound_alarm_info.sound_alarm_times;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置镜头遮挡信息
|
||||||
|
*/
|
||||||
|
function setTamper(){
|
||||||
|
confirmLoading.value = true;
|
||||||
|
let params = {
|
||||||
|
"deviceIndex": formData.deviceIndex,
|
||||||
|
};
|
||||||
|
if(formData.enabled){
|
||||||
|
params["enabled"] = "on";
|
||||||
|
}else{
|
||||||
|
params["enabled"] = "off";
|
||||||
|
}
|
||||||
|
if(formData.enabled){
|
||||||
|
params["digitalSensitivity"] = formData.digitalSensitivity;
|
||||||
|
}
|
||||||
|
setTamperDet(params).then(res=>{
|
||||||
|
confirmLoading.value = false;
|
||||||
|
});
|
||||||
|
if(formData.enabled){
|
||||||
|
if(formData.msgPushEnabled){
|
||||||
|
params["msgPushEnabled"] = "on";
|
||||||
|
}else{
|
||||||
|
params["msgPushEnabled"] = "off";
|
||||||
|
}
|
||||||
|
if(formData.lightAlarmEnabled){
|
||||||
|
params["lightAlarmEnabled"] = "on";
|
||||||
|
}else{
|
||||||
|
params["lightAlarmEnabled"] = "off";
|
||||||
|
}
|
||||||
|
if(formData.soundAlarmEnabled){
|
||||||
|
params["soundAlarmEnabled"] = "on";
|
||||||
|
}else{
|
||||||
|
params["soundAlarmEnabled"] = "off";
|
||||||
|
}
|
||||||
|
params["soundAlarmType"] = formData.soundAlarmType;
|
||||||
|
params["soundAlarmTimes"] = formData.soundAlarmTimes;
|
||||||
|
setTamperNotif(params).then(res=>{
|
||||||
|
confirmLoading.value = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function soundAlarm(){
|
||||||
|
visible.value = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 播放告警声音
|
||||||
|
* @param id
|
||||||
|
*/
|
||||||
|
function playAudio(id){
|
||||||
|
let params = {
|
||||||
|
"deviceIndex": formData.deviceIndex,
|
||||||
|
"force": 1,
|
||||||
|
"id": id
|
||||||
|
};
|
||||||
|
testAudio(params);
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
watch(
|
||||||
|
() => props.data,
|
||||||
|
async () => {
|
||||||
|
formData.deviceIndex = props.data.deviceIndex;
|
||||||
|
getTamper(formData.deviceIndex);
|
||||||
|
},
|
||||||
|
{ deep: true, immediate: true }
|
||||||
|
);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.antd-modal-form {
|
||||||
|
padding: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.labelText {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-end;
|
||||||
|
padding-right: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-select {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.li {
|
||||||
|
padding: 12px 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
|
@ -0,0 +1,455 @@
|
||||||
|
<template>
|
||||||
|
<a-spin :spinning="confirmLoading">
|
||||||
|
<JFormContainer :disabled="disabled">
|
||||||
|
<template #detail>
|
||||||
|
<a-row>
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-row>
|
||||||
|
<a-col :span="20">
|
||||||
|
<a-divider orientation="left">设置视频呈现的效果</a-divider>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
<a-row>
|
||||||
|
<a-col :span="2"></a-col>
|
||||||
|
<a-col :span="20">
|
||||||
|
<div id="video-container" class="video-container"></div>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
<a-row style="margin-top: 14px">
|
||||||
|
<a-col :span="4" class="labelText">
|
||||||
|
画面镜像
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="8">
|
||||||
|
<a-select v-model:value="formData.flip_type" @change="(value) => changeSwitch('flip_type', value)">
|
||||||
|
<a-select-option value="off">关闭</a-select-option>
|
||||||
|
<a-select-option value="left_and_right">左右</a-select-option>
|
||||||
|
<a-select-option value="up_and_down">上下</a-select-option>
|
||||||
|
<a-select-option value="center">中心</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="4" class="labelText">
|
||||||
|
画面翻转
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="8">
|
||||||
|
<a-select v-model:value="formData.rotate_type" @change="(value) => changeSwitch('rotate_type', value)">
|
||||||
|
<a-select-option value="off">关闭</a-select-option>
|
||||||
|
<a-select-option value="anticlockwise_180">上下翻转</a-select-option>
|
||||||
|
<a-select-option value="anticlockwise_90">左翻转90°</a-select-option>
|
||||||
|
<a-select-option value="clockwise_90">右翻转90°</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
<a-row style="margin-top: 14px">
|
||||||
|
<a-col :span="1"></a-col>
|
||||||
|
<a-col :span="20">
|
||||||
|
<a-button preIcon="ant-design:rollback-outlined" @click="restoreDefault">恢复默认</a-button>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-row>
|
||||||
|
<a-col :span="20">
|
||||||
|
<a-divider orientation="left">画面调节</a-divider>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="20">
|
||||||
|
<a-row style="margin-bottom: 14px">
|
||||||
|
<a-col :span="6" class="labelText">
|
||||||
|
亮度
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-slider v-model:value="formData.luma" :min="1" :max="100" @change="(value) => changeCommon('luma', value)"/>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="4">
|
||||||
|
<a-input-number v-model:value="formData.luma" :min="1" :max="100" style="margin-left: 12px" @change="(value) => changeCommon('luma', value)"/>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="20">
|
||||||
|
<a-row style="margin-bottom: 14px">
|
||||||
|
<a-col :span="6" class="labelText">
|
||||||
|
对比度
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-slider v-model:value="formData.contrast" :min="1" :max="100" @change="(value) => changeCommon('contrast', value)"/>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="4">
|
||||||
|
<a-input-number v-model:value="formData.contrast" :min="1" :max="100" style="margin-left: 12px" @change="(value) => changeCommon('contrast', value)"/>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="20">
|
||||||
|
<a-row style="margin-bottom: 14px">
|
||||||
|
<a-col :span="6" class="labelText">
|
||||||
|
饱和度
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-slider v-model:value="formData.saturation" :min="1" :max="100" @change="(value) => changeCommon('saturation', value)"/>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="4">
|
||||||
|
<a-input-number v-model:value="formData.saturation" :min="1" :max="100" style="margin-left: 12px" @change="(value) => changeCommon('saturation', value)"/>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="20">
|
||||||
|
<a-row style="margin-bottom: 14px">
|
||||||
|
<a-col :span="6" class="labelText">
|
||||||
|
锐度
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-slider v-model:value="formData.sharpness" :min="1" :max="100" @change="(value) => changeCommon('sharpness', value)"/>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="4">
|
||||||
|
<a-input-number v-model:value="formData.sharpness" :min="1" :max="100" style="margin-left: 12px" @change="(value) => changeCommon('sharpness', value)"/>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="20">
|
||||||
|
<a-row style="margin-bottom: 10px">
|
||||||
|
<a-col :span="6" class="labelText">
|
||||||
|
宽动态
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="16">
|
||||||
|
<a-select v-model:value="formData.wide_dynamic" @change="(value) => changeCommon('wide_dynamic', value)">
|
||||||
|
<a-select-option value="off">关闭</a-select-option>
|
||||||
|
<a-select-option value="on">开启</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="20" v-show="formData.wide_dynamic=='on'">
|
||||||
|
<a-row style="margin-bottom: 14px">
|
||||||
|
<a-col :span="6" class="labelText"></a-col>
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-slider v-model:value="formData.wd_gain" :min="1" :max="100" @change="(value) => changeCommon('wd_gain', value)"/>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="4">
|
||||||
|
<a-input-number v-model:value="formData.wd_gain" :min="1" :max="100" style="margin-left: 12px" @change="(value) => changeCommon('wd_gain', value)"/>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
<a-row>
|
||||||
|
<a-col :span="20">
|
||||||
|
<a-divider orientation="left">照明设置</a-divider>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="20">
|
||||||
|
<a-row style="margin-bottom: 14px">
|
||||||
|
<a-col :span="6" class="labelText">
|
||||||
|
照明模式
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="16">
|
||||||
|
<a-select v-model:value="formData.night_vision_mode" @change="(value) => changeSwitch('night_vision_mode', value)">
|
||||||
|
<a-select-option value="wtl_night_vision">白光照明</a-select-option>
|
||||||
|
<a-select-option value="inf_night_vision">红外照明</a-select-option>
|
||||||
|
<a-select-option value="md_night_vision">移动侦测全彩</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="20" v-show="formData.night_vision_mode=='md_night_vision'">
|
||||||
|
<a-row style="margin-bottom: 14px">
|
||||||
|
<a-col :span="6" class="labelText">
|
||||||
|
防红外过曝
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="16">
|
||||||
|
<a-select v-model:value="formData.smartir" @change="(value) => changeCommon('smartir', value)">
|
||||||
|
<a-select-option value="auto_ir_ae">自动-增强模式</a-select-option>
|
||||||
|
<a-select-option value="auto_ir">自动-标准模式</a-select-option>
|
||||||
|
<a-select-option value="manual">手动</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="20" v-show="formData.night_vision_mode=='md_night_vision'&&formData.smartir=='manual'">
|
||||||
|
<a-row style="margin-bottom: 14px">
|
||||||
|
<a-col :span="6" class="labelText">
|
||||||
|
防过曝等级
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-slider v-model:value="formData.smartir_level" :min="1" :max="100" @change="(value) => changeCommon('smartir_level', value)"/>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="4">
|
||||||
|
<a-input-number v-model:value="formData.smartir_level" :min="1" :max="100" style="margin-left: 12px" @change="(value) => changeCommon('smartir_level', value)"/>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="20">
|
||||||
|
<a-row style="margin-bottom: 14px">
|
||||||
|
<a-col :span="6" class="labelText">
|
||||||
|
白光强度
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="16">
|
||||||
|
<a-select v-model:value="formData.smartwtl" @change="(value) => changeCommon('smartwtl', value)">
|
||||||
|
<a-select-option value="auto_wtl_ae">智能白光-柔和</a-select-option>
|
||||||
|
<a-select-option value="auto_wtl">智能白光-标准</a-select-option>
|
||||||
|
<a-select-option value="manual">手动</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="20" v-show="formData.smartwtl=='manual'">
|
||||||
|
<a-row style="margin-bottom: 14px">
|
||||||
|
<a-col :span="6" class="labelText">
|
||||||
|
白光等级
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-slider v-model:value="formData.smartwtl_digital_level" :min="1" :max="100" @change="(value) => changeCommon('smartwtl_digital_level', value)"/>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="4">
|
||||||
|
<a-input-number v-model:value="formData.smartwtl_digital_level" :min="1" :max="100" style="margin-left: 12px" @change="(value) => changeCommon('smartwtl_digital_level', value)"/>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</template>
|
||||||
|
</JFormContainer>
|
||||||
|
</a-spin>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import {
|
||||||
|
ref,
|
||||||
|
reactive,
|
||||||
|
defineProps,
|
||||||
|
onMounted,
|
||||||
|
watch,
|
||||||
|
} from 'vue';
|
||||||
|
import { defHttp } from '/@/utils/http/axios';
|
||||||
|
import { useMessage } from '/@/hooks/web/useMessage';
|
||||||
|
import JDictSelectTag from '/@/components/Form/src/jeecg/components/JDictSelectTag.vue';
|
||||||
|
import { getValueType } from '/@/utils';
|
||||||
|
import { Form } from 'ant-design-vue';
|
||||||
|
import JFormContainer from '/@/components/Form/src/container/JFormContainer.vue';
|
||||||
|
import {
|
||||||
|
getImageCommon,
|
||||||
|
setImageCommon,
|
||||||
|
configRecovery,
|
||||||
|
getPreviewUrl,
|
||||||
|
} from "@/views/iot/tplink/camera/camera.api";
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
data: { type: Object, default: () => ({}) },
|
||||||
|
});
|
||||||
|
const formRef = ref();
|
||||||
|
const useForm = Form.useForm;
|
||||||
|
const formData = reactive<Record<string, any>>({
|
||||||
|
deviceIndex: '',//设备索引
|
||||||
|
streamType: 0,//码流类型 0 代表主码流,1 代码子码流
|
||||||
|
//视频预览参数
|
||||||
|
url: '',//预览通道对应的URL
|
||||||
|
backupUrl: '',//备选URL,当流媒体服务器存在内外网IP配置时,该字段不为null
|
||||||
|
wsUrl: '',//用于建立ws连接传输视频帧信息
|
||||||
|
wssUrl: '',//用于建立wss接传输视频帧信息
|
||||||
|
|
||||||
|
chroma: "0", //色度
|
||||||
|
luma: "0", //亮度
|
||||||
|
sharpness: "0", //锐度
|
||||||
|
saturation: "0", //饱和度
|
||||||
|
contrast: "0", //对比度
|
||||||
|
wd_gain: "0", //宽动态度
|
||||||
|
smartir_level: "0",//防过曝等级
|
||||||
|
wide_dynamic: "off", //宽动态 "off"//关闭 "on"//开启
|
||||||
|
smartir: "auto_ir",//防红外过曝:"auto_ir_ae" //自动-增强模式 "auto_ir" //自动-标准模式 "manual"//手动——防过曝等级
|
||||||
|
smartwtl: "auto_wtl",//白光强度:"auto_wtl_ae" //智能白光-柔和 ;"auto_wtl" //智能白光-标准 ; "manual" //手动——出现白光等级
|
||||||
|
smartwtl_digital_level: "0",//白光等级
|
||||||
|
|
||||||
|
flip_type: "center",//画面镜像: "off"//关闭 "left_and_right"//左右 "up_and_down"//上下 "center"//中心
|
||||||
|
night_vision_mode: "md_night_vision",//照明模式:"wtl_night_vision"//白光照明 ;"inf_night_vision"//红外照明 ; "md_night_vision"//移动侦测全彩——出现白光强度
|
||||||
|
rotate_type: "off",//画面旋转 "off"//关闭 "anticlockwise_180"//逆时针旋转180 "clockwise_90"//顺时针旋转90 "anticlockwise_90"//逆时针旋转90
|
||||||
|
|
||||||
|
});
|
||||||
|
const player = ref();
|
||||||
|
const { createMessage } = useMessage();
|
||||||
|
const labelCol = ref<any>({ xs: { span: 24 }, sm: { span: 8 } });
|
||||||
|
const wrapperCol = ref<any>({ xs: { span: 24 }, sm: { span: 16 } });
|
||||||
|
const confirmLoading = ref<boolean>(false);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 恢复默认
|
||||||
|
function restoreDefault(){
|
||||||
|
if(formData.deviceIndex==''){
|
||||||
|
return
|
||||||
|
}
|
||||||
|
confirmLoading.value=true;
|
||||||
|
configRecovery({"deviceIndex":formData.deviceIndex}).then(res=>{
|
||||||
|
confirmLoading.value=false;
|
||||||
|
getCommon(formData.deviceIndex);
|
||||||
|
getSwitch(formData.deviceIndex);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 播放
|
||||||
|
*/
|
||||||
|
async function preview(deviceIndex,streamType,url) {
|
||||||
|
if(deviceIndex==null){
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (url!=''){
|
||||||
|
return
|
||||||
|
}
|
||||||
|
await getPreviewUrl({"deviceIndex":deviceIndex,"streamType":streamType}).then(res=>{
|
||||||
|
formData.url = res.url;
|
||||||
|
formData.backupUrl = res.backupUrl;
|
||||||
|
formData.wsUrl = res.wsUrl;
|
||||||
|
formData.wssUrl = res.wssUrl;
|
||||||
|
confirmLoading.value=false;
|
||||||
|
});
|
||||||
|
const TumsPlayer = window['tums-player'].default;
|
||||||
|
player.value = new TumsPlayer('video-container', {
|
||||||
|
type: "rtsp", // 协议类型,rtsp
|
||||||
|
url: formData.url, // 取流地址, getPreviewUrl接口获取
|
||||||
|
// url: formData.backupUrl, // 取流地址, getPreviewUrl接口获取
|
||||||
|
socket: formData.wssUrl, // websocket地址, getPreviewUrl接口获取
|
||||||
|
pluginPath: '/static', // 当sdk资源不在根路径下时,需配置pluginPath
|
||||||
|
});
|
||||||
|
let isPlaying = player.value.isPlaying();
|
||||||
|
if (!isPlaying) {
|
||||||
|
if (player.value.isInit) {
|
||||||
|
player.value.start();
|
||||||
|
} else {
|
||||||
|
player.value.play();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 销毁
|
||||||
|
*/
|
||||||
|
function destroy(player){
|
||||||
|
if (player){
|
||||||
|
player.value.destroy().then(() => {
|
||||||
|
}); // 销毁
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取画面通用信息
|
||||||
|
*/
|
||||||
|
function getCommon(deviceIndex){
|
||||||
|
if(deviceIndex==null){
|
||||||
|
return
|
||||||
|
}
|
||||||
|
getImageCommon({
|
||||||
|
"deviceIndex": deviceIndex,
|
||||||
|
"type": "common"
|
||||||
|
}).then(res=>{
|
||||||
|
formData.chroma = res.chroma; //色度
|
||||||
|
formData.luma = res.luma; //亮度
|
||||||
|
formData.sharpness = res.sharpness; //锐度
|
||||||
|
formData.saturation = res.saturation; //饱和度
|
||||||
|
formData.contrast = res.contrast; //对比度
|
||||||
|
formData.wd_gain = res.wd_gain; //宽动态度
|
||||||
|
formData.smartir_level = res.smartir_level; //防过曝等级
|
||||||
|
formData.wide_dynamic = res.wide_dynamic; //宽动态 "off"//关闭 "on"//开启
|
||||||
|
formData.smartir = res.smartir; //防红外过曝:"auto_ir_ae" //自动-增强模式 "auto_ir" //自动-标准模式 "manual"//手动——防过曝等级
|
||||||
|
formData.smartwtl = res.smartwtl; //白光强度:"auto_wtl_ae" //智能白光-柔和 ;"auto_wtl" //智能白光-标准 ; "manual" //手动——出现白光等级
|
||||||
|
formData.smartwtl_digital_level = res.smartwtl_digital_level; //白光等级
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取照明设置信息
|
||||||
|
*/
|
||||||
|
function getSwitch(deviceIndex){
|
||||||
|
if(deviceIndex==null){
|
||||||
|
return
|
||||||
|
}
|
||||||
|
getImageCommon({
|
||||||
|
"deviceIndex": deviceIndex,
|
||||||
|
"type": "switch"
|
||||||
|
}).then(res=>{
|
||||||
|
// console.log(res);
|
||||||
|
formData.flip_type = res.flip_type; //画面镜像
|
||||||
|
formData.night_vision_mode = res.night_vision_mode; //照明模式
|
||||||
|
formData.rotate_type = res.rotate_type; //画面旋转 貌似不好使,需要找厂家
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置画面信息
|
||||||
|
* @param paramKey
|
||||||
|
* @param paramValue
|
||||||
|
*/
|
||||||
|
function setCommon(paramKey, paramValue){
|
||||||
|
setImageCommon({
|
||||||
|
"deviceIndex": formData.deviceIndex,
|
||||||
|
"type": formData.deviceIndex,
|
||||||
|
paramKey: paramValue
|
||||||
|
}).then(res=>{ });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置照明设置信息
|
||||||
|
* @param paramObj
|
||||||
|
* @param type
|
||||||
|
*/
|
||||||
|
function setCommonSwitch(paramObj,type){
|
||||||
|
setImageCommon({
|
||||||
|
"deviceIndex": formData.deviceIndex,
|
||||||
|
"type": type,
|
||||||
|
"param": paramObj
|
||||||
|
}).then(res=>{ });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更改画面信息
|
||||||
|
* @param attr
|
||||||
|
* @param value
|
||||||
|
*/
|
||||||
|
function changeCommon(attr,value){
|
||||||
|
let param = {};
|
||||||
|
param[attr] = value;
|
||||||
|
setCommonSwitch(param,"common");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更改照明设置信息
|
||||||
|
* @param attr
|
||||||
|
* @param value
|
||||||
|
*/
|
||||||
|
function changeSwitch(attr,value){
|
||||||
|
let param = {};
|
||||||
|
param[attr] = value;
|
||||||
|
setCommonSwitch(param,"switch");
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
watch(
|
||||||
|
() => props.data,
|
||||||
|
async () => {
|
||||||
|
formData.deviceIndex = props.data.deviceIndex;
|
||||||
|
getCommon(formData.deviceIndex);
|
||||||
|
getSwitch(formData.deviceIndex);
|
||||||
|
preview(formData.deviceIndex,formData.streamType,formData.url);
|
||||||
|
},
|
||||||
|
{ deep: true, immediate: true }
|
||||||
|
);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.antd-modal-form {
|
||||||
|
padding: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.labelText {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-end;
|
||||||
|
padding-right: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-select {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.video-container {
|
||||||
|
height: 500px;
|
||||||
|
width: 500px;
|
||||||
|
background-color: #010917;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
|
@ -0,0 +1,124 @@
|
||||||
|
<template>
|
||||||
|
<BasicDrawer
|
||||||
|
v-bind="$attrs"
|
||||||
|
@register="registerDrawer"
|
||||||
|
:title="getTitle"
|
||||||
|
:width="adaptiveWidth"
|
||||||
|
@ok="handleSubmit"
|
||||||
|
:showFooter="showFooter"
|
||||||
|
destroyOnClose
|
||||||
|
>
|
||||||
|
<BasicForm @register="registerForm" >
|
||||||
|
<template #customInput="{ model, field }">
|
||||||
|
<a-button preIcon="ant-design:poweroff-outlined" @click="restartNow(model)">立即重启</a-button>
|
||||||
|
</template>
|
||||||
|
</BasicForm>
|
||||||
|
</BasicDrawer>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import {defineComponent, ref, computed, unref, useAttrs, createVNode, h} from 'vue';
|
||||||
|
import { BasicForm, useForm } from '/@/components/Form/index';
|
||||||
|
import { formSchema } from "@/views/iot/tplink/camera/camera.data";
|
||||||
|
import { update,rebootDevice } from '@/views/iot/tplink/camera/camera.api';
|
||||||
|
import { BasicDrawer, useDrawerInner } from '/@/components/Drawer';
|
||||||
|
import { useDrawerAdaptiveWidth } from '/@/hooks/jeecg/useAdaptiveWidth';
|
||||||
|
import { getTenantId } from "/@/utils/auth";
|
||||||
|
import {Modal} from "ant-design-vue";
|
||||||
|
import {ExclamationCircleOutlined} from "@ant-design/icons-vue";
|
||||||
|
|
||||||
|
// 声明Emits
|
||||||
|
const emit = defineEmits(['success', 'register']);
|
||||||
|
const attrs = useAttrs();
|
||||||
|
const isUpdate = ref(true);
|
||||||
|
const rowId = ref('');
|
||||||
|
const departOptions = ref([]);
|
||||||
|
let isFormDepartUser = false;
|
||||||
|
//表单配置
|
||||||
|
const [registerForm, { setProps, resetFields, setFieldsValue, validate, updateSchema }] = useForm({
|
||||||
|
labelWidth: 90,
|
||||||
|
schemas: formSchema,
|
||||||
|
showActionButtonGroup: false,
|
||||||
|
});
|
||||||
|
const showFooter = ref(true);
|
||||||
|
//表单赋值
|
||||||
|
const [registerDrawer, { setDrawerProps, closeDrawer }] = useDrawerInner(async (data) => {
|
||||||
|
await resetFields();
|
||||||
|
showFooter.value = data?.showFooter ?? true;
|
||||||
|
setDrawerProps({ confirmLoading: false, showFooter: showFooter.value });
|
||||||
|
isUpdate.value = !!data?.isUpdate;
|
||||||
|
// 无论新增还是编辑,都可以设置表单值
|
||||||
|
if (typeof data.record === 'object') {
|
||||||
|
setFieldsValue({
|
||||||
|
...data.record,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// 隐藏底部时禁用整个表单
|
||||||
|
setProps({ disabled: !showFooter.value });
|
||||||
|
});
|
||||||
|
//获取标题
|
||||||
|
const getTitle = computed(() => {
|
||||||
|
if (!unref(isUpdate)) {
|
||||||
|
return '新增摄像头';
|
||||||
|
} else {
|
||||||
|
return unref(showFooter) ? '编辑摄像头' : '摄像头详情';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const { adaptiveWidth } = useDrawerAdaptiveWidth();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 提交事件
|
||||||
|
*/
|
||||||
|
async function handleSubmit() {
|
||||||
|
try {
|
||||||
|
let values = await validate();
|
||||||
|
setDrawerProps({ confirmLoading: true });
|
||||||
|
let params = values;
|
||||||
|
await update(params);
|
||||||
|
//关闭弹窗
|
||||||
|
closeDrawer();
|
||||||
|
//刷新列表
|
||||||
|
emit('success');
|
||||||
|
} finally {
|
||||||
|
setDrawerProps({ confirmLoading: false });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设备重启
|
||||||
|
* @param record
|
||||||
|
*/
|
||||||
|
function restartNow(record){
|
||||||
|
Modal.confirm({
|
||||||
|
title: '设备重启',
|
||||||
|
width: '500px',
|
||||||
|
icon: createVNode(ExclamationCircleOutlined),
|
||||||
|
content: createVNode('div', { style: 'color:red;' }, '重启设备过程中,请勿插拔电源,以免损坏设备。确定要重启吗?'),
|
||||||
|
okText: '重启',
|
||||||
|
onOk() {
|
||||||
|
let params = {
|
||||||
|
deviceIndex: record.deviceIndex,
|
||||||
|
projectId: record.projectId,
|
||||||
|
};
|
||||||
|
rebootDevice(params);
|
||||||
|
Modal.success({
|
||||||
|
title: '重启IPC',
|
||||||
|
okText: '确定',
|
||||||
|
content: h('div', {}, [
|
||||||
|
h('p', '正在重启IPC,重启过程大约需要60秒钟'),
|
||||||
|
h('p', '重启后,请刷新表格以获取最新设备状态'),
|
||||||
|
]),
|
||||||
|
});
|
||||||
|
},
|
||||||
|
onCancel() {
|
||||||
|
// console.log('Cancel');
|
||||||
|
},
|
||||||
|
class: 'test',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
|
@ -0,0 +1,207 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<!--引用表格-->
|
||||||
|
<BasicTable @register="registerTable">
|
||||||
|
<!--插槽:table标题-->
|
||||||
|
<template #tableTitle>
|
||||||
|
</template>
|
||||||
|
<!--操作栏-->
|
||||||
|
<template #action="{ record }">
|
||||||
|
<TableAction :actions="getTableAction(record)"/>
|
||||||
|
</template>
|
||||||
|
<template v-slot:bodyCell="{ column, record, index, text }">
|
||||||
|
</template>
|
||||||
|
</BasicTable>
|
||||||
|
<!-- 表单区域 -->
|
||||||
|
<CameraPreviewModal ref="previewModal"></CameraPreviewModal>
|
||||||
|
<CameraInfoDrawer @register="registerDrawer" @success="handleSuccess" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" name="iot-nuIotCameraInfo" setup>
|
||||||
|
import {ref, reactive, createVNode, h, onMounted, watch, unref} from 'vue';
|
||||||
|
import { BasicTable, useTable, TableAction } from '/@/components/Table';
|
||||||
|
import { useListPage } from '/@/hooks/system/useListPage';
|
||||||
|
import { columns, searchFormSchema } from '@/views/iot/tplink/camera/camera.data';
|
||||||
|
import { list } from '@/views/iot/tplink/camera/camera.api';
|
||||||
|
import { useUserStore } from '/@/store/modules/user';
|
||||||
|
import { useDrawer } from "@/components/Drawer";
|
||||||
|
import { useRouter } from 'vue-router';
|
||||||
|
import CameraPreviewModal from './CameraPreviewModal.vue'
|
||||||
|
import CameraInfoDrawer from './CameraInfoDrawer.vue'
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
data: { type: Object, default: () => ({}) },
|
||||||
|
});
|
||||||
|
//注册drawer
|
||||||
|
const [registerDrawer, { openDrawer }] = useDrawer();
|
||||||
|
let router = useRouter();
|
||||||
|
const formRef = ref();
|
||||||
|
const queryParam = reactive<any>({});
|
||||||
|
const registerModal = ref();
|
||||||
|
const previewModal = ref();
|
||||||
|
const deviceModal = ref();
|
||||||
|
const userStore = useUserStore();
|
||||||
|
//注册table数据
|
||||||
|
const { prefixCls, tableContext, onExportXls, onImportXls } = useListPage({
|
||||||
|
tableProps: {
|
||||||
|
title: '护理单元-物联管理-摄像头信息',
|
||||||
|
api: list,
|
||||||
|
columns,
|
||||||
|
canResize: false,
|
||||||
|
formConfig: {
|
||||||
|
// labelWidth: 200,
|
||||||
|
schemas: searchFormSchema,
|
||||||
|
},
|
||||||
|
actionColumn: {
|
||||||
|
width: 180,
|
||||||
|
fixed: 'right',
|
||||||
|
},
|
||||||
|
beforeFetch: async (params) => {
|
||||||
|
return Object.assign(params, queryParam);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const [registerTable, { reload, collapseAll, updateTableDataRecord, findTableDataRecord, getDataSource }, { rowSelection, selectedRowKeys }] = tableContext;
|
||||||
|
const labelCol = reactive({
|
||||||
|
xs:24,
|
||||||
|
sm:4,
|
||||||
|
xl:6,
|
||||||
|
xxl:4
|
||||||
|
});
|
||||||
|
const wrapperCol = reactive({
|
||||||
|
xs: 24,
|
||||||
|
sm: 20,
|
||||||
|
});
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
watch(
|
||||||
|
() => props.data,
|
||||||
|
async () => {
|
||||||
|
console.log(props.data);
|
||||||
|
queryParam.projectId = props.data.projectId;
|
||||||
|
queryParam.regionId = props.data.regionId;
|
||||||
|
console.log(queryParam);
|
||||||
|
let record = unref(props.data);
|
||||||
|
if (typeof record !== 'object') {
|
||||||
|
record = {};
|
||||||
|
}
|
||||||
|
reload();
|
||||||
|
},
|
||||||
|
{ deep: true, immediate: true }
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 编辑
|
||||||
|
*/
|
||||||
|
function handleEdit(record: Recordable) {
|
||||||
|
openDrawer(true, {
|
||||||
|
record,
|
||||||
|
isUpdate: true,
|
||||||
|
showFooter: true,
|
||||||
|
tenantSaas: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 预览
|
||||||
|
*/
|
||||||
|
function handlePreview(record: Recordable) {
|
||||||
|
previewModal.value.disableSubmit = true;
|
||||||
|
previewModal.value.edit(record);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 成功回调
|
||||||
|
*/
|
||||||
|
function handleSuccess() {
|
||||||
|
reload();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 画面配置
|
||||||
|
*/
|
||||||
|
function handlePicConfig(record) {
|
||||||
|
router.push({
|
||||||
|
path:'/tplink/cameraConfig',
|
||||||
|
query: {
|
||||||
|
deviceIndex: record.deviceIndex,
|
||||||
|
parentId: record.parentId,
|
||||||
|
multitrans: record.multitrans,
|
||||||
|
projectId: record.projectId,
|
||||||
|
regionId: record.regionId,
|
||||||
|
ip: record.ip
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 操作栏
|
||||||
|
*/
|
||||||
|
function getTableAction(record) {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
label: '画面配置',
|
||||||
|
onClick: handlePicConfig.bind(null, record),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '编辑',
|
||||||
|
onClick: handleEdit.bind(null, record),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '预览',
|
||||||
|
onClick: handlePreview.bind(null, record),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 左侧树选择后触发
|
||||||
|
*/
|
||||||
|
function onTreeSelect(data) {
|
||||||
|
console.log('onTreeSelect: ', data);
|
||||||
|
// departData.value = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 左侧树rootTreeData触发
|
||||||
|
*/
|
||||||
|
function onRootTreeData(data) {
|
||||||
|
console.log('onRootTreeData: ', data);
|
||||||
|
// rootTreeData.value = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
|
||||||
|
.jeecg-basic-table-form-container {
|
||||||
|
padding: 0;
|
||||||
|
.table-page-search-submitButtons {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
.query-group-cust{
|
||||||
|
min-width: 100px !important;
|
||||||
|
}
|
||||||
|
.query-group-split-cust{
|
||||||
|
width: 30px;
|
||||||
|
display: inline-block;
|
||||||
|
text-align: center
|
||||||
|
}
|
||||||
|
.ant-form-item:not(.ant-form-item-with-help){
|
||||||
|
margin-bottom: 16px;
|
||||||
|
height: 32px;
|
||||||
|
}
|
||||||
|
:deep(.ant-picker),:deep(.ant-input-number){
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.p-2{
|
||||||
|
height: calc(100vh - 120px);
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
|
@ -0,0 +1,205 @@
|
||||||
|
<template>
|
||||||
|
<a-card :bordered="false" style="height: 100%">
|
||||||
|
<div class="j-table-operator" style="width: 100%">
|
||||||
|
<!-- <a-button preIcon="ant-design:sync-outlined" @click="loadRootTreeData">刷新</a-button>-->
|
||||||
|
<a-button type="primary" preIcon="ant-design:sync-outlined" @click="syncProjectInfo">同步项目</a-button>
|
||||||
|
<template v-if="currentRegion !=null">
|
||||||
|
<a-button preIcon="ant-design:sync-outlined" @click="syncRegionInfo">同步区域</a-button>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
<a-spin :spinning="loading">
|
||||||
|
<!--区域树-->
|
||||||
|
<template v-if="treeData.length > 0">
|
||||||
|
<a-tree
|
||||||
|
v-if="!treeReloading"
|
||||||
|
:clickRowToExpand="false"
|
||||||
|
:treeData="treeData"
|
||||||
|
:selectedKeys="selectedKeys"
|
||||||
|
:checkStrictly="checkStrictly"
|
||||||
|
:load-data="loadChildrenTreeData"
|
||||||
|
:checkedKeys="checkedKeys"
|
||||||
|
v-model:expandedKeys="expandedKeys"
|
||||||
|
@select="onSelect"
|
||||||
|
>
|
||||||
|
</a-tree>
|
||||||
|
</template>
|
||||||
|
<a-empty v-else description="暂无数据" />
|
||||||
|
</a-spin>
|
||||||
|
</a-card>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { nextTick, ref } from 'vue';
|
||||||
|
import { useModal } from '/@/components/Modal';
|
||||||
|
import { useMessage } from '/@/hooks/web/useMessage';
|
||||||
|
import { useMethods } from '/@/hooks/system/useMethods';
|
||||||
|
const { createMessage } = useMessage();
|
||||||
|
import { queryProjectTreeSync, queryRegionTreeSync, syncProject, syncRegion } from '@/views/iot/tplink/camera/camera.api';
|
||||||
|
|
||||||
|
const emit = defineEmits(['select', 'rootTreeData']);
|
||||||
|
const loading = ref<boolean>(false);
|
||||||
|
// 区域树列表数据
|
||||||
|
const treeData = ref<any[]>([]);
|
||||||
|
// 当前选中的项
|
||||||
|
const checkedKeys = ref<any[]>([]);
|
||||||
|
// 当前展开的项
|
||||||
|
const expandedKeys = ref<any[]>([]);
|
||||||
|
// 当前选中的项
|
||||||
|
const selectedKeys = ref<any[]>([]);
|
||||||
|
// 树组件重新加载
|
||||||
|
const treeReloading = ref<boolean>(false);
|
||||||
|
// 树父子是否关联
|
||||||
|
const checkStrictly = ref<boolean>(true);
|
||||||
|
// 当前选中的区域
|
||||||
|
const currentRegion = ref<any>(null);
|
||||||
|
|
||||||
|
// 加载顶级区域信息
|
||||||
|
async function loadRootTreeData() {
|
||||||
|
try {
|
||||||
|
loading.value = true;
|
||||||
|
treeData.value = [];
|
||||||
|
const result = await queryProjectTreeSync();
|
||||||
|
if (Array.isArray(result)) {
|
||||||
|
treeData.value = result;
|
||||||
|
}
|
||||||
|
if (expandedKeys.value.length === 0) {
|
||||||
|
autoExpandParentNode();
|
||||||
|
} else {
|
||||||
|
if (selectedKeys.value.length === 0) {
|
||||||
|
let item = treeData.value[0];
|
||||||
|
if (item) {
|
||||||
|
// 默认选中第一个
|
||||||
|
setSelectedKey(item.id, item);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
emit('select', currentRegion.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
emit('rootTreeData', treeData.value);
|
||||||
|
} finally {
|
||||||
|
loading.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
loadRootTreeData();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加载子级区域信息
|
||||||
|
*/
|
||||||
|
async function loadChildrenTreeData(treeNode) {
|
||||||
|
try {
|
||||||
|
const result = await queryRegionTreeSync({
|
||||||
|
parentId: treeNode.dataRef.id,
|
||||||
|
projectId: treeNode.dataRef.projectId,
|
||||||
|
});
|
||||||
|
if (result.length == 0) {
|
||||||
|
treeNode.dataRef.isLeaf = true;
|
||||||
|
} else {
|
||||||
|
treeNode.dataRef.children = result;
|
||||||
|
if (expandedKeys.value.length > 0) {
|
||||||
|
// 判断获取的子级是否有当前展开的项
|
||||||
|
let subKeys: any[] = [];
|
||||||
|
for (let key of expandedKeys.value) {
|
||||||
|
if (result.findIndex((item) => item.id === key) !== -1) {
|
||||||
|
subKeys.push(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (subKeys.length > 0) {
|
||||||
|
expandedKeys.value = [...expandedKeys.value];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
treeData.value = [...treeData.value];
|
||||||
|
emit('rootTreeData', treeData.value);
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自动展开父节点,只展开一级
|
||||||
|
*/
|
||||||
|
function autoExpandParentNode() {
|
||||||
|
let item = treeData.value[0];
|
||||||
|
if (item) {
|
||||||
|
if (!item.isLeaf) {
|
||||||
|
expandedKeys.value = [item.key];
|
||||||
|
}
|
||||||
|
// 默认选中第一个
|
||||||
|
setSelectedKey(item.id, item);
|
||||||
|
reloadTree();
|
||||||
|
} else {
|
||||||
|
emit('select', null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 重新加载树组件,防止无法默认展开数据
|
||||||
|
*/
|
||||||
|
async function reloadTree() {
|
||||||
|
await nextTick();
|
||||||
|
treeReloading.value = true;
|
||||||
|
await nextTick();
|
||||||
|
treeReloading.value = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置当前选中的行
|
||||||
|
*/
|
||||||
|
function setSelectedKey(key: string, data?: object) {
|
||||||
|
console.log('setSelectedKey: ', key);
|
||||||
|
selectedKeys.value = [key];
|
||||||
|
if (data) {
|
||||||
|
currentRegion.value = data;
|
||||||
|
emit('select', data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 树选择事件
|
||||||
|
*/
|
||||||
|
function onSelect(selKeys, event) {
|
||||||
|
console.log('onSelect: ', selKeys, event);
|
||||||
|
if (selKeys.length > 0 && selectedKeys.value[0] !== selKeys[0]) {
|
||||||
|
setSelectedKey(selKeys[0], event.selectedNodes[0]);
|
||||||
|
} else {
|
||||||
|
// 这样可以防止用户取消选择
|
||||||
|
setSelectedKey(selectedKeys.value[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 同步项目
|
||||||
|
*/
|
||||||
|
async function syncProjectInfo(){
|
||||||
|
await syncProject();
|
||||||
|
await loadRootTreeData();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 同步区域
|
||||||
|
*/
|
||||||
|
async function syncRegionInfo(){
|
||||||
|
let data = currentRegion.value;
|
||||||
|
if (data == null) {
|
||||||
|
createMessage.warning('请先选择一个区域');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const record = { projectId: data.projectId };
|
||||||
|
await syncRegion(record);
|
||||||
|
await loadRootTreeData();
|
||||||
|
}
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
loadRootTreeData,
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
|
||||||
|
:deep(.ant-card-body){
|
||||||
|
padding: 24px 0px 0px 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
|
@ -0,0 +1,713 @@
|
||||||
|
<template>
|
||||||
|
<a-spin :spinning="confirmLoading">
|
||||||
|
<JFormContainer :disabled="disabled">
|
||||||
|
<template #detail>
|
||||||
|
<a-row>
|
||||||
|
<a-col :span="24">
|
||||||
|
<a-row>
|
||||||
|
<a-col :span="20">
|
||||||
|
<a-divider orientation="left">白光报警</a-divider>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="20">
|
||||||
|
<a-row style="margin-bottom: 14px">
|
||||||
|
<a-col :span="4" class="labelText">
|
||||||
|
白光报警
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="16">
|
||||||
|
<a-switch v-model:checked="formData.enabled" checked-children="已开启" un-checked-children="已关闭" />
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="24">
|
||||||
|
<a-row>
|
||||||
|
<a-col :span="20">
|
||||||
|
<a-divider orientation="left">布防时间设置</a-divider>
|
||||||
|
</a-col>
|
||||||
|
<a-col style="width: 800px;">
|
||||||
|
<a-row style="margin-bottom: 14px">
|
||||||
|
<a-col :span="2" class="labelText">
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="22">
|
||||||
|
<div class="cleanTitle">
|
||||||
|
<div>仅在以下时间段进行白光报警</div>
|
||||||
|
<div>
|
||||||
|
<a-button preIcon="ant-design:delete-outlined" @click="cleanPlan" :disabled="!formData.enabled">清空计划</a-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-col>
|
||||||
|
<a-col style="width: 800px;">
|
||||||
|
<a-row style="margin-bottom: 14px">
|
||||||
|
<a-col :span="2" class="labelText">
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="22">
|
||||||
|
<div class="armingSchedule" id="armingSchedule" @mousedown="handleMouseDown">
|
||||||
|
<div id="recPlanGrid" class="test" style="overflow: hidden;">
|
||||||
|
<div class="cruiseDiv"><i class="iCruise displayNone" style="left: 373px;"></i></div>
|
||||||
|
<div class="psHourList">
|
||||||
|
<div class="psHourLi" v-for="(idx,index) in 25" :key="index">
|
||||||
|
<span>{{ index }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<ul class="psWeekList">
|
||||||
|
<li>星期一</li>
|
||||||
|
<li>星期二</li>
|
||||||
|
<li>星期三</li>
|
||||||
|
<li>星期四</li>
|
||||||
|
<li>星期五</li>
|
||||||
|
<li>星期六</li>
|
||||||
|
<li>星期日</li>
|
||||||
|
</ul>
|
||||||
|
<div class="tableDiv">
|
||||||
|
<div class="trDiv" v-for="weekDay in 7">
|
||||||
|
<div :class="getClass(index)" v-for="(idx,index) in 25" :key="index">
|
||||||
|
<div class="blank" style="width: 24px;" :id="getId(weekDay,index)" @click="divSelect($event,weekDay,index)" @mouseover="overDivSelect($event,weekDay,index)"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="editDiv"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="24">
|
||||||
|
<a-row style="margin-top: 14px">
|
||||||
|
<a-col :span="1"></a-col>
|
||||||
|
<a-col :span="20">
|
||||||
|
<a-button type="primary" preIcon="ant-design:save-outlined" @click="saveAlarm">保存</a-button>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</template>
|
||||||
|
</JFormContainer>
|
||||||
|
</a-spin>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { ref, reactive, defineProps, onMounted, watch,} from 'vue';
|
||||||
|
import { defHttp } from '/@/utils/http/axios';
|
||||||
|
import { useMessage } from '/@/hooks/web/useMessage';
|
||||||
|
import JDictSelectTag from '/@/components/Form/src/jeecg/components/JDictSelectTag.vue';
|
||||||
|
import { getValueType } from '/@/utils';
|
||||||
|
import { Form } from 'ant-design-vue';
|
||||||
|
import JFormContainer from '/@/components/Form/src/container/JFormContainer.vue';
|
||||||
|
import {
|
||||||
|
getAlarmInfo, setAlarmInfo,
|
||||||
|
getAlarmPlan, setAlarmPlan,
|
||||||
|
} from "@/views/iot/tplink/camera/camera.api";
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
data: { type: Object, default: () => ({}) },
|
||||||
|
});
|
||||||
|
|
||||||
|
const formData = reactive<Record<string, any>>({
|
||||||
|
deviceIndex: '', //设备索引
|
||||||
|
enabled: false, //镜头遮挡
|
||||||
|
});
|
||||||
|
|
||||||
|
const confirmLoading = ref<boolean>(false);
|
||||||
|
const planVisible = ref<boolean>(false);
|
||||||
|
const mondayArr = ref<string>([]);
|
||||||
|
const mondayMergeArr = ref<string>([]);
|
||||||
|
const tuesdayArr = ref<string>([]);
|
||||||
|
const tuesdayMergeArr = ref<string>([]);
|
||||||
|
const wednesdayArr = ref<string>([]);
|
||||||
|
const wednesdayMergeArr = ref<string>([]);
|
||||||
|
const thursdayArr = ref<string>([]);
|
||||||
|
const thursdayMergeArr = ref<string>([]);
|
||||||
|
const fridayArr = ref<string>([]);
|
||||||
|
const fridayMergeArr = ref<string>([]);
|
||||||
|
const saturdayArr = ref<string>([]);
|
||||||
|
const saturdayMergeArr = ref<string>([]);
|
||||||
|
const sundayArr = ref<string>([]);
|
||||||
|
const sundayMergeArr = ref<string>([]);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置单元格样式
|
||||||
|
*/
|
||||||
|
function getClass(index){
|
||||||
|
if(index == 0) {
|
||||||
|
return 'tdDiv firTd';
|
||||||
|
}else if(index == 23){
|
||||||
|
return 'tdDiv lastTd';
|
||||||
|
}else if(index > 23){
|
||||||
|
return 'editBtn';
|
||||||
|
}else{
|
||||||
|
return 'tdDiv';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置单元格ID
|
||||||
|
*/
|
||||||
|
function getId(weekDay,index){
|
||||||
|
let id = weekDay+'-'+index;
|
||||||
|
return id
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 格式化各星期数据
|
||||||
|
*/
|
||||||
|
function initPlan(armingScheduleLight){
|
||||||
|
let monday = decodeURIComponent(armingScheduleLight.monday);
|
||||||
|
initValue(1,monday);
|
||||||
|
mergeNumbers(1,1,-1);
|
||||||
|
let tuesday = decodeURIComponent(armingScheduleLight.tuesday);
|
||||||
|
initValue(2,tuesday);
|
||||||
|
mergeNumbers(1,2,-1);
|
||||||
|
let wednesday = decodeURIComponent(armingScheduleLight.wednesday);
|
||||||
|
initValue(3,wednesday);
|
||||||
|
mergeNumbers(1,3,-1);
|
||||||
|
let thursday = decodeURIComponent(armingScheduleLight.thursday);
|
||||||
|
initValue(4,thursday);
|
||||||
|
mergeNumbers(1,4,-1);
|
||||||
|
let friday = decodeURIComponent(armingScheduleLight.friday);
|
||||||
|
initValue(5,friday);
|
||||||
|
mergeNumbers(1,5,-1);
|
||||||
|
let saturday = decodeURIComponent(armingScheduleLight.saturday);
|
||||||
|
initValue(6,saturday);
|
||||||
|
mergeNumbers(1,6,-1);
|
||||||
|
let sunday = decodeURIComponent(armingScheduleLight.sunday);
|
||||||
|
initValue(7,sunday);
|
||||||
|
mergeNumbers(1,7,-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化各星期数据
|
||||||
|
*/
|
||||||
|
function initValue(weekDay,valueArr){
|
||||||
|
valueArr = valueArr.replaceAll('[','').replaceAll(']','').replaceAll('"','');
|
||||||
|
const arr = valueArr.split(",");
|
||||||
|
arr.forEach(item =>{
|
||||||
|
const hours = item.split("-");
|
||||||
|
const start = parseInt(hours[0].substring(0, 2));
|
||||||
|
const end = parseInt(hours[1].substring(0, 2));
|
||||||
|
if(start<end){
|
||||||
|
for(let i=start;i<end;i++){
|
||||||
|
const id = weekDay+'-'+i;
|
||||||
|
const element = document.getElementById(id);
|
||||||
|
element.className = 'timing';
|
||||||
|
switch (weekDay) {
|
||||||
|
case 1:
|
||||||
|
mondayArr.value.push(i);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
tuesdayArr.value.push(i);
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
wednesdayArr.value.push(i);
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
thursdayArr.value.push(i);
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
fridayArr.value.push(i);
|
||||||
|
break;
|
||||||
|
case 6:
|
||||||
|
saturdayArr.value.push(i);
|
||||||
|
break;
|
||||||
|
case 7:
|
||||||
|
sundayArr.value.push(i);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清除计划
|
||||||
|
*/
|
||||||
|
function cleanPlan(){
|
||||||
|
const elements = document.querySelectorAll('.timing');
|
||||||
|
elements.forEach(item =>{
|
||||||
|
item.className = 'blank';
|
||||||
|
})
|
||||||
|
mondayArr.value = [];
|
||||||
|
mondayMergeArr.value = [];
|
||||||
|
tuesdayArr.value = [];
|
||||||
|
tuesdayMergeArr.value = [];
|
||||||
|
wednesdayArr.value = [];
|
||||||
|
wednesdayMergeArr.value = [];
|
||||||
|
thursdayArr.value = [];
|
||||||
|
thursdayMergeArr.value = [];
|
||||||
|
fridayArr.value = [];
|
||||||
|
fridayMergeArr.value = [];
|
||||||
|
saturdayArr.value = [];
|
||||||
|
saturdayMergeArr.value = [];
|
||||||
|
sundayArr.value = [];
|
||||||
|
sundayMergeArr.value = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 合并各星期计划数组
|
||||||
|
*/
|
||||||
|
function mergeNumbers(type,weekDay,hourValue) {
|
||||||
|
let numbers = [];
|
||||||
|
switch (weekDay) {
|
||||||
|
case 1:
|
||||||
|
Object.assign(numbers, mondayArr.value);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
Object.assign(numbers, tuesdayArr.value);
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
Object.assign(numbers, wednesdayArr.value);
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
Object.assign(numbers, thursdayArr.value);
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
Object.assign(numbers, fridayArr.value);
|
||||||
|
break;
|
||||||
|
case 6:
|
||||||
|
Object.assign(numbers, saturdayArr.value);
|
||||||
|
break;
|
||||||
|
case 7:
|
||||||
|
Object.assign(numbers, sundayArr.value);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(hourValue!=-1){
|
||||||
|
if(type == 1) {
|
||||||
|
numbers.push(hourValue);
|
||||||
|
numbers.sort(function(a, b) {
|
||||||
|
return a - b;
|
||||||
|
});
|
||||||
|
}else {
|
||||||
|
let idx = numbers.indexOf(hourValue);
|
||||||
|
numbers.splice(idx,1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let start = numbers[0];
|
||||||
|
let end = null;
|
||||||
|
let mergedNumbers = [];
|
||||||
|
|
||||||
|
for (let i = 0; i < numbers.length; i++) {
|
||||||
|
if (i === 0) {
|
||||||
|
start = numbers[i];
|
||||||
|
end = numbers[i];
|
||||||
|
} else if (numbers[i] === numbers[i - 1] + 1) {
|
||||||
|
end = numbers[i];
|
||||||
|
} else {
|
||||||
|
if (start === end) {
|
||||||
|
mergedNumbers.push(start.toString());
|
||||||
|
} else {
|
||||||
|
mergedNumbers.push(`${start}-${end}`);
|
||||||
|
}
|
||||||
|
start = numbers[i];
|
||||||
|
end = numbers[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (start === end) {
|
||||||
|
mergedNumbers.push(start.toString());
|
||||||
|
} else {
|
||||||
|
mergedNumbers.push(`${start}-${end}`);
|
||||||
|
}
|
||||||
|
if(mergedNumbers.length>6){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
switch (weekDay) {
|
||||||
|
case 1:
|
||||||
|
mondayArr.value = [];
|
||||||
|
mondayMergeArr.value = [];
|
||||||
|
Object.assign(mondayMergeArr.value, mergedNumbers);
|
||||||
|
Object.assign(mondayArr.value, numbers);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
tuesdayArr.value = [];
|
||||||
|
tuesdayMergeArr.value = [];
|
||||||
|
Object.assign(tuesdayMergeArr.value, mergedNumbers);
|
||||||
|
Object.assign(tuesdayArr.value, numbers);
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
wednesdayArr.value = [];
|
||||||
|
wednesdayMergeArr.value = [];
|
||||||
|
Object.assign(wednesdayMergeArr.value, mergedNumbers);
|
||||||
|
Object.assign(wednesdayArr.value, numbers);
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
thursdayArr.value = [];
|
||||||
|
thursdayMergeArr.value = [];
|
||||||
|
Object.assign(thursdayMergeArr.value, mergedNumbers);
|
||||||
|
Object.assign(thursdayArr.value, numbers);
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
fridayArr.value = [];
|
||||||
|
fridayMergeArr.value = [];
|
||||||
|
Object.assign(fridayMergeArr.value, mergedNumbers);
|
||||||
|
Object.assign(fridayArr.value, numbers);
|
||||||
|
break;
|
||||||
|
case 6:
|
||||||
|
saturdayArr.value = [];
|
||||||
|
saturdayMergeArr.value = [];
|
||||||
|
Object.assign(saturdayMergeArr.value, mergedNumbers);
|
||||||
|
Object.assign(saturdayArr.value, numbers);
|
||||||
|
break;
|
||||||
|
case 7:
|
||||||
|
sundayArr.value = [];
|
||||||
|
sundayMergeArr.value = [];
|
||||||
|
Object.assign(sundayMergeArr.value, mergedNumbers);
|
||||||
|
Object.assign(sundayArr.value, numbers);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// console.log('星期一', mondayArr.value);
|
||||||
|
// console.log('星期二', tuesdayArr.value);
|
||||||
|
// console.log('星期三', wednesdayArr.value);
|
||||||
|
// console.log('星期四', thursdayArr.value);
|
||||||
|
// console.log('星期五', fridayArr.value);
|
||||||
|
// console.log('星期六', saturdayArr.value);
|
||||||
|
// console.log('星期七', sundayArr.value);
|
||||||
|
|
||||||
|
// console.log('星期一', mondayMergeArr.value);
|
||||||
|
// console.log('星期二', tuesdayMergeArr.value);
|
||||||
|
// console.log('星期三', wednesdayMergeArr.value);
|
||||||
|
// console.log('星期四', thursdayMergeArr.value);
|
||||||
|
// console.log('星期五', fridayMergeArr.value);
|
||||||
|
// console.log('星期六', saturdayMergeArr.value);
|
||||||
|
// console.log('星期七', sundayMergeArr.value);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 点击选择单元格
|
||||||
|
*/
|
||||||
|
function divSelect(event,weekDay,hourValue){
|
||||||
|
if(formData.enabled){
|
||||||
|
if(event.target.className == "blank"){
|
||||||
|
let visiable = mergeNumbers(1,weekDay,hourValue);
|
||||||
|
if(visiable){
|
||||||
|
event.target.className = "timing";
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
let visiable = mergeNumbers(2,weekDay,hourValue);
|
||||||
|
if(visiable){
|
||||||
|
event.target.className = "blank";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 悬浮选择单元格
|
||||||
|
*/
|
||||||
|
function overDivSelect(event,weekDay,hourValue){
|
||||||
|
if(formData.enabled&&planVisible.value){
|
||||||
|
if(event.target.className == "blank"){
|
||||||
|
let visiable = mergeNumbers(1,weekDay,hourValue);
|
||||||
|
if(visiable){
|
||||||
|
event.target.className = "timing";
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
let visiable = mergeNumbers(2,weekDay,hourValue);
|
||||||
|
if(visiable){
|
||||||
|
event.target.className = "blank";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取报警
|
||||||
|
*/
|
||||||
|
function getAlarm(deviceIndex){
|
||||||
|
if(deviceIndex==null){
|
||||||
|
return
|
||||||
|
}
|
||||||
|
//获取白光报警信息
|
||||||
|
getAlarmInfo({
|
||||||
|
"deviceIndex": deviceIndex
|
||||||
|
}).then(res=>{
|
||||||
|
if(res.light_alarm_enabled == "on"){
|
||||||
|
formData.enabled = true;
|
||||||
|
}else{
|
||||||
|
formData.enabled = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
//获取报警计划划信息
|
||||||
|
getAlarmPlan({
|
||||||
|
"deviceIndex": deviceIndex,
|
||||||
|
"type": "light"
|
||||||
|
}).then(res=>{
|
||||||
|
let armingScheduleLight = res.arming_schedule_light;
|
||||||
|
initPlan(armingScheduleLight);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 保存白光报警设置信息
|
||||||
|
*/
|
||||||
|
function saveAlarm(){
|
||||||
|
confirmLoading.value = true;
|
||||||
|
let params = {
|
||||||
|
"deviceIndex": formData.deviceIndex,
|
||||||
|
"type": "light"
|
||||||
|
};
|
||||||
|
if(formData.enabled){
|
||||||
|
params["enabled"] = "on";
|
||||||
|
}else{
|
||||||
|
params["enabled"] = "off";
|
||||||
|
}
|
||||||
|
setAlarmInfo(params).then(res=>{
|
||||||
|
confirmLoading.value = false;
|
||||||
|
});
|
||||||
|
if(formData.enabled){
|
||||||
|
params["alarmPlan"] = setAlarmPlanData();
|
||||||
|
setAlarmPlan(params).then(res=>{
|
||||||
|
confirmLoading.value = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取报警计划数据
|
||||||
|
*/
|
||||||
|
function setAlarmPlanData(){
|
||||||
|
const monday = getHoursEncode(mondayMergeArr.value);
|
||||||
|
const tuesday = getHoursEncode(tuesdayMergeArr.value);
|
||||||
|
const wednesday = getHoursEncode(wednesdayMergeArr.value);
|
||||||
|
const thursday = getHoursEncode(thursdayMergeArr.value);
|
||||||
|
const friday = getHoursEncode(fridayMergeArr.value);
|
||||||
|
const saturday = getHoursEncode(saturdayMergeArr.value);
|
||||||
|
const sunday = getHoursEncode(sundayMergeArr.value);
|
||||||
|
let alarmPlan = {
|
||||||
|
"monday": monday,
|
||||||
|
"tuesday": tuesday,
|
||||||
|
"wednesday": wednesday,
|
||||||
|
"thursday": thursday,
|
||||||
|
"friday": friday,
|
||||||
|
"saturday": saturday,
|
||||||
|
"sunday": sunday
|
||||||
|
}
|
||||||
|
return alarmPlan;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 格式化设置计划数据
|
||||||
|
* @param weekArr
|
||||||
|
*/
|
||||||
|
function getHoursEncode(weekArr){
|
||||||
|
let hoursEncode = "[";
|
||||||
|
if(weekArr.length>0){
|
||||||
|
let hourArrStr = "";
|
||||||
|
weekArr.forEach(item =>{
|
||||||
|
let hours = 0;
|
||||||
|
let end = 0;
|
||||||
|
const arr = item.split("-");
|
||||||
|
hours = parseInt(arr[0]);
|
||||||
|
if(arr.length>1){
|
||||||
|
end = parseInt(arr[1]) + 1;
|
||||||
|
}else{
|
||||||
|
end = hours + 1;
|
||||||
|
}
|
||||||
|
if(hours<10){
|
||||||
|
hours = "0" + hours;
|
||||||
|
}
|
||||||
|
hours = hours + "00-"
|
||||||
|
if(end<10){
|
||||||
|
hours = hours + "0" + end;
|
||||||
|
}else{
|
||||||
|
hours = hours + end;
|
||||||
|
}
|
||||||
|
hours = hours + "00";
|
||||||
|
hours = '"' + hours + '"';
|
||||||
|
hourArrStr = hourArrStr + hours + ",";
|
||||||
|
})
|
||||||
|
hoursEncode = "[" + hourArrStr.substring(0,hourArrStr.length-1) + "]";
|
||||||
|
}else{
|
||||||
|
hoursEncode = '["0000-0000"]';
|
||||||
|
}
|
||||||
|
return encodeURIComponent(hoursEncode);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 鼠标按下
|
||||||
|
*/
|
||||||
|
function handleMouseDown(){
|
||||||
|
// console.log('鼠标按下', event);
|
||||||
|
planVisible.value = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 鼠标抬起
|
||||||
|
*/
|
||||||
|
function handleMouseUp(){
|
||||||
|
// console.log('鼠标抬起', event);
|
||||||
|
planVisible.value = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
watch(
|
||||||
|
() => props.data,
|
||||||
|
async () => {
|
||||||
|
formData.deviceIndex = props.data.deviceIndex;
|
||||||
|
getAlarm(formData.deviceIndex);
|
||||||
|
},
|
||||||
|
{ deep: true, immediate: true }
|
||||||
|
);
|
||||||
|
// window.addEventListener('mousedown', handleMouseDown);
|
||||||
|
window.addEventListener('mouseup', handleMouseUp);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.antd-modal-form {
|
||||||
|
padding: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.labelText {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-end;
|
||||||
|
padding-right: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cleanTitle {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.armingSchedule {
|
||||||
|
*position: relative;
|
||||||
|
z-index: 0;
|
||||||
|
background: #ebeef0;
|
||||||
|
border: 1px solid #ced7e0;
|
||||||
|
height: 260px;
|
||||||
|
border-radius: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.cruiseDiv {
|
||||||
|
width: 100%;
|
||||||
|
height: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.cruiseDiv i.iCruise {
|
||||||
|
width: 8px;
|
||||||
|
height: 8px;
|
||||||
|
position: relative;
|
||||||
|
//background: url(../../web-static/images/pointDown.png) no-repeat;
|
||||||
|
display: inline-block;
|
||||||
|
*display: inline;
|
||||||
|
*zoom: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.psHourList {
|
||||||
|
color: #666;
|
||||||
|
margin-left: 63px;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.psHourList div.psHourLi {
|
||||||
|
width: 25px;
|
||||||
|
display: inline-block;
|
||||||
|
text-align: center;
|
||||||
|
*display: inline;
|
||||||
|
*zoom: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul.psWeekList {
|
||||||
|
float: left;
|
||||||
|
color: #333;
|
||||||
|
font-size: 13px;
|
||||||
|
list-style: none;
|
||||||
|
margin-right: 16px;
|
||||||
|
margin-left: 20px;
|
||||||
|
_width: 42px;
|
||||||
|
_margin-left: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul.psWeekList li {
|
||||||
|
height: 32px;
|
||||||
|
line-height: 32px;
|
||||||
|
_height: 34px;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.tableDiv {
|
||||||
|
z-index: 0;
|
||||||
|
*position: relative;
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.tableDiv div.trDiv {
|
||||||
|
width: 100%;
|
||||||
|
height: 24px;
|
||||||
|
margin-top: 4px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.tableDiv div.trDiv div.firTd {
|
||||||
|
border-left: solid 1px #ced7e0;
|
||||||
|
border-bottom-left-radius: 2px!important;
|
||||||
|
border-top-left-radius: 2px!important;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.tableDiv div.trDiv div.tdDiv {
|
||||||
|
height: 24px;
|
||||||
|
overflow: hidden;
|
||||||
|
vertical-align: middle;
|
||||||
|
border-right: solid 1px #ced7e0;
|
||||||
|
border-top: solid 1px #ced7e0;
|
||||||
|
border-bottom: solid 1px #ced7e0;
|
||||||
|
display: inline-block;
|
||||||
|
*display: inline;
|
||||||
|
*zoom: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.tableDiv div.trDiv div.editBtn {
|
||||||
|
//background-image: url(@/assets/loginmini/icon/jeecg_bg.png);
|
||||||
|
//background-image: url(@/assets/images/planEdit.png);
|
||||||
|
/* background-image: url(@/views/iot/tplink/camera/icons/planEdit.png);*/
|
||||||
|
background: url(../icons/plan_set.png) no-repeat;
|
||||||
|
background-size: cover;
|
||||||
|
//background: url(@/assets/images/planEdit.png) no-repeat;
|
||||||
|
//background: new URL('@/assets/images/planEdit.png', import.meta.url).href;
|
||||||
|
width: 20px;
|
||||||
|
vertical-align: middle;
|
||||||
|
height: 24px;
|
||||||
|
cursor: pointer;
|
||||||
|
margin-left: 8px;
|
||||||
|
margin-right: 8px;
|
||||||
|
display: none;
|
||||||
|
zoom: 1;
|
||||||
|
}
|
||||||
|
div.tableDiv div.trDiv:hover div.editBtn{
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
div.tableDiv div.trDiv div.lastTd {
|
||||||
|
border-bottom-right-radius: 2px;
|
||||||
|
border-top-right-radius: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.tableDiv div.trDiv div.tdDiv div.timing {
|
||||||
|
background-color: #5a92ff;
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.tableDiv div.trDiv div.tdDiv div.blank {
|
||||||
|
background-color: #ffffff;
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.displayNone {
|
||||||
|
display: none!important;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
|
@ -0,0 +1,195 @@
|
||||||
|
<template>
|
||||||
|
<a-spin :spinning="confirmLoading">
|
||||||
|
<a-row>
|
||||||
|
<a-col :span="24">
|
||||||
|
<div id="video-container-multitrans" v-if="initContainerMultitrans"></div>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="24" style="padding: 5px;">
|
||||||
|
<span style="margin-left: 15px;">回放倍速
|
||||||
|
<a-select
|
||||||
|
ref="select"
|
||||||
|
v-model:value="formData.scale"
|
||||||
|
style="width: 120px"
|
||||||
|
@focus="focus"
|
||||||
|
@change="changeScale"
|
||||||
|
>
|
||||||
|
<a-select-option value="0.0625">0.0625</a-select-option>
|
||||||
|
<a-select-option value="0.125">0.125</a-select-option>
|
||||||
|
<a-select-option value="0.25">0.25</a-select-option>
|
||||||
|
<a-select-option value="0.5">0.5</a-select-option>
|
||||||
|
<a-select-option value="1">1</a-select-option>
|
||||||
|
<a-select-option value="2">2</a-select-option>
|
||||||
|
<a-select-option value="4">4</a-select-option>
|
||||||
|
<a-select-option value="8">8</a-select-option>
|
||||||
|
<a-select-option value="16">16</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</span>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-spin>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { ref, reactive, defineExpose, nextTick, defineProps, computed, onMounted } from 'vue';
|
||||||
|
import { defHttp } from '/@/utils/http/axios';
|
||||||
|
import { useMessage } from '/@/hooks/web/useMessage';
|
||||||
|
import { getValueType } from '/@/utils';
|
||||||
|
import { Form } from 'ant-design-vue';
|
||||||
|
import { getMultitransUrl } from "@/views/iot/tplink/camera/camera.api";
|
||||||
|
|
||||||
|
const TumsPlayer = window['tums-player'].default;
|
||||||
|
const props = defineProps({
|
||||||
|
formDisabled: { type: Boolean, default: false },
|
||||||
|
formData: { type: Object, default: () => ({})},
|
||||||
|
formBpm: { type: Boolean, default: true }
|
||||||
|
});
|
||||||
|
|
||||||
|
const formRef = ref();
|
||||||
|
const player = ref();
|
||||||
|
const initContainerMultitrans = ref(true);
|
||||||
|
const useForm = Form.useForm;
|
||||||
|
const emit = defineEmits(['register', 'ok']);
|
||||||
|
const formData = reactive<Record<string, any>>({
|
||||||
|
//预览通道参数
|
||||||
|
videoDevId: '',//设备索引
|
||||||
|
multitrans: '',//IPC能力集中multitrans的能力
|
||||||
|
startTime: '',//回放开始时间
|
||||||
|
endTime: '',//回放结束时间
|
||||||
|
scale: 1,//回放速率
|
||||||
|
});
|
||||||
|
const { createMessage } = useMessage();
|
||||||
|
const labelCol = ref<any>({ xs: { span: 24 }, sm: { span: 5 } });
|
||||||
|
const wrapperCol = ref<any>({ xs: { span: 24 }, sm: { span: 16 } });
|
||||||
|
const confirmLoading = ref<boolean>(false);
|
||||||
|
|
||||||
|
//表单验证
|
||||||
|
const validatorRules = reactive({
|
||||||
|
});
|
||||||
|
const { resetFields, validate, validateInfos } = useForm(formData, validatorRules, { immediate: false });
|
||||||
|
|
||||||
|
// 表单禁用
|
||||||
|
const disabled = computed(()=>{
|
||||||
|
if(props.formBpm === true){
|
||||||
|
if(props.formData.disabled === false){
|
||||||
|
return false;
|
||||||
|
}else{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return props.formDisabled;
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 详情
|
||||||
|
*/
|
||||||
|
async function edit(record) {
|
||||||
|
await nextTick(() => {
|
||||||
|
confirmLoading.value=true;
|
||||||
|
resetFields();
|
||||||
|
const tmpData = {};
|
||||||
|
Object.keys(formData).forEach((key) => {
|
||||||
|
if(record.hasOwnProperty(key)){
|
||||||
|
tmpData[key] = record[key]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
//赋值
|
||||||
|
Object.assign(formData, tmpData);
|
||||||
|
});
|
||||||
|
//获取录像回放地址
|
||||||
|
await getMultitransUrl({
|
||||||
|
"deviceIndex":formData.videoDevId,
|
||||||
|
"startTime":formData.startTime,
|
||||||
|
"endTime":formData.endTime,
|
||||||
|
"scale":formData.scale
|
||||||
|
}).then(res=>{
|
||||||
|
confirmLoading.value=false;
|
||||||
|
if(res.success == false){
|
||||||
|
createMessage.error(res.message)
|
||||||
|
}else{
|
||||||
|
const list = res;
|
||||||
|
list.forEach(item => {
|
||||||
|
let startTime = item.startTime*1000;
|
||||||
|
let endTime = item.endTime*1000;
|
||||||
|
let duration = endTime - startTime;
|
||||||
|
// console.log(duration);
|
||||||
|
// console.log("url:"+item.url);
|
||||||
|
// console.log("socket:"+item.wssUrl);
|
||||||
|
// console.log("queryAddress:"+item.queryAddress);
|
||||||
|
// console.log("videoDevId:"+item.videoDevId);
|
||||||
|
// console.log("storageDevId:"+item.storageDevId);
|
||||||
|
// console.log("startTime:"+startTime);
|
||||||
|
// console.log("endTime:"+endTime);
|
||||||
|
// console.log("scale:"+item.scale);
|
||||||
|
player.value = new TumsPlayer('video-container-multitrans', {
|
||||||
|
"autoplay": true,
|
||||||
|
"resolution": "HD",
|
||||||
|
"streamType": "sdvod", // 回放类型
|
||||||
|
"url": item.url, // 该url为一次性 需要通过接口实时获取
|
||||||
|
"socket": item.wssUrl,
|
||||||
|
"type": "rtsp",
|
||||||
|
"decoderType": "wasm", // 硬软解类型
|
||||||
|
"queryAddress": item.queryAddress,
|
||||||
|
"videoSessionId": "",
|
||||||
|
"videoDevId": item.videoDevId, // 设备id
|
||||||
|
"useMultitrans": true,
|
||||||
|
"storageDevId": item.storageDevId,
|
||||||
|
"startTime": startTime, // 回放开始时间 13位
|
||||||
|
"endTime": endTime, // 回放结束时间 13位
|
||||||
|
"eventType": [1, 2], // 事件类型
|
||||||
|
"scale": item.scale // 回放倍速
|
||||||
|
});
|
||||||
|
setTimeout(()=>{stop(player)}, duration);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 切换回放倍
|
||||||
|
*/
|
||||||
|
function changeScale(){
|
||||||
|
createMessage.info('切换回放倍速至'+formData.scale+'倍');
|
||||||
|
let scale = parseInt(formData.scale) ;
|
||||||
|
player.value.setPlaybackConfig({"scale":scale});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 暂停播放
|
||||||
|
*/
|
||||||
|
function stop(player){
|
||||||
|
if (player){
|
||||||
|
player.value.stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 销毁
|
||||||
|
*/
|
||||||
|
function destroy(){
|
||||||
|
console.log("destroy");
|
||||||
|
if (player.value){
|
||||||
|
player.value.destroy().then(() => { }); // 回放销毁时自动调用接口,跨域导致失败
|
||||||
|
initContainerMultitrans.value = false
|
||||||
|
setTimeout(()=>{
|
||||||
|
initContainerMultitrans.value = true
|
||||||
|
},1000)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
edit,
|
||||||
|
destroy
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.antd-modal-form {
|
||||||
|
padding: 14px;
|
||||||
|
}
|
||||||
|
#video-container-multitrans {
|
||||||
|
padding: 0px 25px;
|
||||||
|
width: 600px;
|
||||||
|
height: 550px;
|
||||||
|
background: #1a1a1a;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,67 @@
|
||||||
|
<template>
|
||||||
|
<j-modal :title="title" :width="width" :visible="visible" @ok="handleOk" :okButtonProps="{ class: { 'jee-hidden': disableSubmit } }" @cancel="handleCancel" cancelText="关闭">
|
||||||
|
<CameraMultitransForm ref="registerForm" @ok="submitCallback" :formDisabled="disableSubmit" :formBpm="false"></CameraMultitransForm>
|
||||||
|
</j-modal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { ref, nextTick, defineExpose } from 'vue';
|
||||||
|
import CameraMultitransForm from './CameraMultitransForm.vue'
|
||||||
|
import JModal from '/@/components/Modal/src/JModal/JModal.vue';
|
||||||
|
|
||||||
|
const title = ref<string>('');
|
||||||
|
const width = ref<number>(600);
|
||||||
|
const visible = ref<boolean>(false);
|
||||||
|
const disableSubmit = ref<boolean>(false);
|
||||||
|
const registerForm = ref();
|
||||||
|
const emit = defineEmits(['register', 'success']);
|
||||||
|
|
||||||
|
// const player = ref();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 编辑
|
||||||
|
* @param record
|
||||||
|
*/
|
||||||
|
function edit(record) {
|
||||||
|
title.value = "录像回放";
|
||||||
|
visible.value = true;
|
||||||
|
nextTick(() => {
|
||||||
|
registerForm.value.edit(record);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 确定按钮点击事件
|
||||||
|
*/
|
||||||
|
function handleOk() {
|
||||||
|
registerForm.value.submitForm();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* form保存回调事件
|
||||||
|
*/
|
||||||
|
function submitCallback() {
|
||||||
|
handleCancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 取消按钮回调事件
|
||||||
|
*/
|
||||||
|
function handleCancel() {
|
||||||
|
registerForm.value.destroy();
|
||||||
|
visible.value = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
edit,
|
||||||
|
disableSubmit,
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less">
|
||||||
|
/**隐藏样式-modal确定按钮 */
|
||||||
|
.jee-hidden {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<style lang="less" scoped></style>
|
|
@ -0,0 +1,305 @@
|
||||||
|
<template>
|
||||||
|
<a-spin :spinning="confirmLoading">
|
||||||
|
<JFormContainer :disabled="disabled">
|
||||||
|
<template #detail>
|
||||||
|
<a-row>
|
||||||
|
<a-col :span="24">
|
||||||
|
<a-row>
|
||||||
|
<a-col :span="1"></a-col>
|
||||||
|
<a-col :span="22">
|
||||||
|
<a-divider orientation="left">设置视频呈现的效果</a-divider>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
<a-row>
|
||||||
|
<a-col :span="1"></a-col>
|
||||||
|
<a-col :span="20">
|
||||||
|
<div id="video-container" class="video-container">
|
||||||
|
<div class="osd-text-top">
|
||||||
|
<span v-show="formData.dateEnabled">{{formData.dateData}} </span>
|
||||||
|
<span v-show="formData.weekEnabled">{{formData.weekData}} </span>
|
||||||
|
<span v-show="formData.dateEnabled">{{formData.timeData}}</span>
|
||||||
|
</div>
|
||||||
|
<div class="osd-text-bottom" v-show="formData.labelEnabled">
|
||||||
|
{{formData.labelData}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
<a-row>
|
||||||
|
<a-col :span="1"></a-col>
|
||||||
|
<a-col :span="20">
|
||||||
|
<a-row style="margin: 14px 0px">
|
||||||
|
<a-col :span="3" class="label-text">
|
||||||
|
<a-checkbox v-model:checked="formData.dateEnabled">显示日期</a-checkbox>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="8">
|
||||||
|
{{formData.dateData}} {{formData.timeData}}
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
<a-row>
|
||||||
|
<a-col :span="1"></a-col>
|
||||||
|
<a-col :span="20">
|
||||||
|
<a-row style="margin-bottom: 14px">
|
||||||
|
<a-col :span="3" class="label-text">
|
||||||
|
<a-checkbox v-model:checked="formData.weekEnabled">显示星期</a-checkbox>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="8">
|
||||||
|
{{formData.weekData}}
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
<a-row>
|
||||||
|
<a-col :span="1"></a-col>
|
||||||
|
<a-col :span="20">
|
||||||
|
<a-row style="margin-bottom: 14px">
|
||||||
|
<a-col :span="3" class="label-text">
|
||||||
|
<a-checkbox v-model:checked="formData.labelEnabled">通道名称</a-checkbox>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="8">
|
||||||
|
<a-input v-model:value="formData.labelData" :disabled="!formData.labelEnabled"/>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
<a-row style="margin-top: 14px">
|
||||||
|
<a-col :span="1"></a-col>
|
||||||
|
<a-col :span="20">
|
||||||
|
<a-button type="primary" preIcon="ant-design:save-outlined" @click="setOsdData">保存</a-button>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</template>
|
||||||
|
</JFormContainer>
|
||||||
|
</a-spin>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { ref, reactive, defineProps, onMounted, watch,} from 'vue';
|
||||||
|
import { defHttp } from '/@/utils/http/axios';
|
||||||
|
import { useMessage } from '/@/hooks/web/useMessage';
|
||||||
|
import JDictSelectTag from '/@/components/Form/src/jeecg/components/JDictSelectTag.vue';
|
||||||
|
import { getValueType } from '/@/utils';
|
||||||
|
import { Form } from 'ant-design-vue';
|
||||||
|
import JFormContainer from '/@/components/Form/src/container/JFormContainer.vue';
|
||||||
|
import {
|
||||||
|
getOsdCapability,
|
||||||
|
getOsd,
|
||||||
|
setOsd,
|
||||||
|
getPreviewUrl
|
||||||
|
} from "@/views/iot/tplink/camera/camera.api";
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
data: { type: Object, default: () => ({}) },
|
||||||
|
});
|
||||||
|
|
||||||
|
const formData = reactive<Record<string, any>>({
|
||||||
|
deviceIndex: '', //设备索引
|
||||||
|
|
||||||
|
weekData:'',
|
||||||
|
dateData:'',
|
||||||
|
timeData:'',
|
||||||
|
labelData:'',
|
||||||
|
|
||||||
|
dateEnabled: true,
|
||||||
|
weekEnabled: true,
|
||||||
|
labelEnabled: true,
|
||||||
|
|
||||||
|
streamType: 0,//码流类型 0 代表主码流,1 代码子码流
|
||||||
|
//视频预览参数
|
||||||
|
url: '',//预览通道对应的URL
|
||||||
|
backupUrl: '',//备选URL,当流媒体服务器存在内外网IP配置时,该字段不为null
|
||||||
|
wsUrl: '',//用于建立ws连接传输视频帧信息
|
||||||
|
wssUrl: '',//用于建立wss接传输视频帧信息
|
||||||
|
|
||||||
|
});
|
||||||
|
const player = ref();
|
||||||
|
const confirmLoading = ref<boolean>(false);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前时间和星期
|
||||||
|
*/
|
||||||
|
function getCurrentTime(){
|
||||||
|
let days = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'];
|
||||||
|
let now = new Date();
|
||||||
|
formData.dateData = now.getFullYear()+"-"+(('0' + (now.getMonth() + 1)).slice(-2))+"-"+(('0' + (now.getDate() + 1)).slice(-2));
|
||||||
|
formData.timeData = (('0' + (now.getHours() + 1)).slice(-2))+":"+(('0' + (now.getMinutes() + 1)).slice(-2))+":"+(('0' + (now.getSeconds() + 1)).slice(-2))
|
||||||
|
formData.weekData = days[now.getDay()];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取OSD能力集
|
||||||
|
*/
|
||||||
|
// function getCapability(deviceIndex){
|
||||||
|
// if(deviceIndex==null){
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
// getOsdCapability({
|
||||||
|
// "deviceIndex": deviceIndex
|
||||||
|
// }).then(res=>{
|
||||||
|
//
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取OSD参数
|
||||||
|
*/
|
||||||
|
function getOsdData(deviceIndex){
|
||||||
|
if(deviceIndex==null){
|
||||||
|
return
|
||||||
|
}
|
||||||
|
getOsd({
|
||||||
|
"deviceIndex": deviceIndex
|
||||||
|
}).then(res=>{
|
||||||
|
if(res.date.enabled == "on"){
|
||||||
|
formData.dateEnabled = true;
|
||||||
|
}else{
|
||||||
|
formData.dateEnabled = false;
|
||||||
|
}
|
||||||
|
if(res.week.enabled == "on"){
|
||||||
|
formData.weekEnabled = true;
|
||||||
|
}else{
|
||||||
|
formData.weekEnabled = false;
|
||||||
|
}
|
||||||
|
if(res.label_info[0].label_info_1.enabled == "on"){
|
||||||
|
formData.labelEnabled = true;
|
||||||
|
}else{
|
||||||
|
formData.labelEnabled = false;
|
||||||
|
}
|
||||||
|
formData.labelData = res.label_info[0].label_info_1.text;
|
||||||
|
formData.labelData = formData.labelData.replaceAll("%20", " ")
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置OSD参数
|
||||||
|
*/
|
||||||
|
function setOsdData(){
|
||||||
|
confirmLoading.value = true;
|
||||||
|
let params = {
|
||||||
|
"deviceIndex": formData.deviceIndex,
|
||||||
|
"mainFontPixel": [256,256,256,256,256] //设置主字体像素,全景球机无效
|
||||||
|
};
|
||||||
|
if(formData.dateEnabled){
|
||||||
|
params["dateEnabled"] = "on";
|
||||||
|
}else{
|
||||||
|
params["dateEnabled"] = "off";
|
||||||
|
}
|
||||||
|
if(formData.weekEnabled){
|
||||||
|
params["weekEnabled"] = "on";
|
||||||
|
}else{
|
||||||
|
params["weekEnabled"] = "off";
|
||||||
|
}
|
||||||
|
if(formData.labelEnabled){
|
||||||
|
params["labelEnabled"] = "on";
|
||||||
|
}else{
|
||||||
|
params["labelEnabled"] = "off";
|
||||||
|
}
|
||||||
|
setOsd(params).then(res=>{
|
||||||
|
confirmLoading.value = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 播放
|
||||||
|
*/
|
||||||
|
async function preview(deviceIndex,streamType,url) {
|
||||||
|
if(deviceIndex==null){
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (url!=''){
|
||||||
|
return
|
||||||
|
}
|
||||||
|
await getPreviewUrl({"deviceIndex":deviceIndex,"streamType":streamType}).then(res=>{
|
||||||
|
formData.url = res.url;
|
||||||
|
formData.backupUrl = res.backupUrl;
|
||||||
|
formData.wsUrl = res.wsUrl;
|
||||||
|
formData.wssUrl = res.wssUrl;
|
||||||
|
confirmLoading.value=false;
|
||||||
|
});
|
||||||
|
const TumsPlayer = window['tums-player'].default;
|
||||||
|
player.value = new TumsPlayer('video-container', {
|
||||||
|
type: "rtsp", // 协议类型,rtsp
|
||||||
|
url: formData.url, // 取流地址, getPreviewUrl接口获取
|
||||||
|
// url: formData.backupUrl, // 取流地址, getPreviewUrl接口获取
|
||||||
|
socket: formData.wssUrl, // websocket地址, getPreviewUrl接口获取
|
||||||
|
pluginPath: '/static', // 当sdk资源不在根路径下时,需配置pluginPath
|
||||||
|
});
|
||||||
|
let isPlaying = player.value.isPlaying();
|
||||||
|
if (!isPlaying) {
|
||||||
|
if (player.value.isInit) {
|
||||||
|
player.value.start();
|
||||||
|
} else {
|
||||||
|
player.value.play();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 销毁
|
||||||
|
*/
|
||||||
|
function destroy(player){
|
||||||
|
if (player){
|
||||||
|
player.value.destroy().then(() => {
|
||||||
|
}); // 销毁
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
watch(
|
||||||
|
() => props.data,
|
||||||
|
async () => {
|
||||||
|
formData.deviceIndex = props.data.deviceIndex;
|
||||||
|
// getCapability(formData.deviceIndex);
|
||||||
|
getCurrentTime();
|
||||||
|
getOsdData(formData.deviceIndex);
|
||||||
|
preview(formData.deviceIndex,formData.streamType,formData.url);
|
||||||
|
},
|
||||||
|
{ deep: true, immediate: true }
|
||||||
|
);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.antd-modal-form {
|
||||||
|
padding: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label-text {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-select {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.video-container {
|
||||||
|
height: 500px;
|
||||||
|
width: 500px;
|
||||||
|
background-color: #010917;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.osd-text-top {
|
||||||
|
color: #40a9ff;
|
||||||
|
padding-left: 10px;
|
||||||
|
padding-top: 5px;
|
||||||
|
position: absolute;
|
||||||
|
top: 0px;
|
||||||
|
z-index: 999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.osd-text-bottom {
|
||||||
|
color: #40a9ff;
|
||||||
|
padding-left: 10px;
|
||||||
|
padding-bottom: 5px;
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0px;
|
||||||
|
z-index: 999;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
|
@ -0,0 +1,228 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<a-tabs
|
||||||
|
v-model:activeKey="activeKey"
|
||||||
|
tab-position="left"
|
||||||
|
:style="{ height: '100%' }"
|
||||||
|
@tabScroll="callback"
|
||||||
|
>
|
||||||
|
<a-tab-pane :key="1" tab="画面显示">
|
||||||
|
<a-tabs
|
||||||
|
v-model:activeKey="oneChildActiveKey"
|
||||||
|
tab-position="top"
|
||||||
|
:style="{ height: '100%' }"
|
||||||
|
@tabScroll="callback"
|
||||||
|
type="card"
|
||||||
|
>
|
||||||
|
<a-tab-pane :key="1" tab="视频显示">
|
||||||
|
<div class="scrollable" v-if="oneChildActiveKey==1">
|
||||||
|
<CameraCommonForm :data="cameraData"></CameraCommonForm>
|
||||||
|
</div>
|
||||||
|
</a-tab-pane>
|
||||||
|
<a-tab-pane :key="2" tab="OSD">
|
||||||
|
<div class="scrollable" v-if="oneChildActiveKey==2">
|
||||||
|
<CameraOsdForm :data="cameraData"></CameraOsdForm>
|
||||||
|
</div>
|
||||||
|
</a-tab-pane>
|
||||||
|
<a-tab-pane :key="3" tab="码流参数">
|
||||||
|
<div class="scrollable" v-if="oneChildActiveKey==3">
|
||||||
|
<CameraBitrateForm :data="cameraData"></CameraBitrateForm>
|
||||||
|
</div>
|
||||||
|
</a-tab-pane>
|
||||||
|
</a-tabs>
|
||||||
|
</a-tab-pane>
|
||||||
|
<a-tab-pane :key="2" tab="事件侦测">
|
||||||
|
<a-tabs
|
||||||
|
v-model:activeKey="twoChildActiveKey"
|
||||||
|
tab-position="top"
|
||||||
|
:style="{ height: '100%' }"
|
||||||
|
@tabScroll="callback"
|
||||||
|
type="card"
|
||||||
|
>
|
||||||
|
<a-tab-pane :key="1" tab="镜头遮挡">
|
||||||
|
<div class="scrollable">
|
||||||
|
<CameraBlockForm :data="cameraData"></CameraBlockForm>
|
||||||
|
</div>
|
||||||
|
</a-tab-pane>
|
||||||
|
<!-- <a-tab-pane :key="2" tab="移动侦测">-->
|
||||||
|
<!-- <div class="scrollable">-->
|
||||||
|
<!-- 21-->
|
||||||
|
<!-- </div>-->
|
||||||
|
<!-- </a-tab-pane>-->
|
||||||
|
|
||||||
|
<!-- <a-tab-pane :key="3" tab="区域覆盖">-->
|
||||||
|
<!-- <div class="scrollable">-->
|
||||||
|
<!-- 23-->
|
||||||
|
<!-- </div>-->
|
||||||
|
<!-- </a-tab-pane>-->
|
||||||
|
<!-- <a-tab-pane :key="4" tab="越界侦测">-->
|
||||||
|
<!-- <div class="scrollable">-->
|
||||||
|
<!-- 24-->
|
||||||
|
<!-- </div>-->
|
||||||
|
<!-- </a-tab-pane>-->
|
||||||
|
<!-- <a-tab-pane :key="5" tab="区域入侵">-->
|
||||||
|
<!-- <div class="scrollable">-->
|
||||||
|
<!-- 25-->
|
||||||
|
<!-- </div>-->
|
||||||
|
<!-- </a-tab-pane>-->
|
||||||
|
</a-tabs>
|
||||||
|
</a-tab-pane>
|
||||||
|
<a-tab-pane :key="3" tab="报警设备">
|
||||||
|
<a-tabs
|
||||||
|
v-model:activeKey="threeChildActiveKey"
|
||||||
|
tab-position="top"
|
||||||
|
:style="{ height: '100%' }"
|
||||||
|
@tabScroll="callback"
|
||||||
|
type="card"
|
||||||
|
>
|
||||||
|
<a-tab-pane :key="1" tab="白光报警">
|
||||||
|
<div class="scrollable" v-if="threeChildActiveKey==1">
|
||||||
|
<CameraLightAlarmForm :data="cameraData"></CameraLightAlarmForm>
|
||||||
|
</div>
|
||||||
|
</a-tab-pane>
|
||||||
|
<a-tab-pane :key="2" tab="声音报警">
|
||||||
|
<div class="scrollable" v-if="threeChildActiveKey==2">
|
||||||
|
<CameraSoundAlarmForm :data="cameraData"></CameraSoundAlarmForm>
|
||||||
|
</div>
|
||||||
|
</a-tab-pane>
|
||||||
|
</a-tabs>
|
||||||
|
</a-tab-pane>
|
||||||
|
<a-tab-pane :key="4" tab="录像管理">
|
||||||
|
<a-tabs
|
||||||
|
v-model:activeKey="fourChildActiveKey"
|
||||||
|
tab-position="top"
|
||||||
|
:style="{ height: '100%' }"
|
||||||
|
@tabScroll="callback"
|
||||||
|
type="card"
|
||||||
|
>
|
||||||
|
<a-tab-pane :key="1" tab="MP4转发FTP">
|
||||||
|
<div class="scrollable">
|
||||||
|
<CameraUploadForm :data="cameraData"></CameraUploadForm>
|
||||||
|
</div>
|
||||||
|
</a-tab-pane>
|
||||||
|
<a-tab-pane :key="2" tab="当天录像">
|
||||||
|
<div class="scrollable">
|
||||||
|
<CameraRecordList :data="cameraData" />
|
||||||
|
</div>
|
||||||
|
</a-tab-pane>
|
||||||
|
<!-- <a-tab-pane :key="3" tab="录像设置">
|
||||||
|
<div class="scrollable">
|
||||||
|
<a-button type="primary" preIcon="ant-design:search-outlined" @click="recordingConfig">获取录像配置</a-button>
|
||||||
|
<a-button type="primary" preIcon="ant-design:search-outlined" @click="setRecordingPlan">设置录像计划</a-button>
|
||||||
|
<a-button type="primary" preIcon="ant-design:search-outlined" @click="batchProgress">录像计划进度</a-button>
|
||||||
|
</div>
|
||||||
|
</a-tab-pane>-->
|
||||||
|
</a-tabs>
|
||||||
|
</a-tab-pane>
|
||||||
|
</a-tabs>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" name="iot-nuIotCameraInfo" setup>
|
||||||
|
import { ref, onMounted, reactive } from "vue";
|
||||||
|
import { useRouter } from 'vue-router';
|
||||||
|
import type { TabsProps } from 'ant-design-vue';
|
||||||
|
import { useListPage } from "@/hooks/system/useListPage";
|
||||||
|
import { getImageCommon,setImageCommon } from "@/views/iot/tplink/camera/camera.api";
|
||||||
|
import { recordingColumns,searchRecording } from "@/views/iot/tplink/camera/camera.data";
|
||||||
|
import CameraCommonForm from './CameraCommonForm.vue';//画面公共信息
|
||||||
|
import CameraOsdForm from './CameraOsdForm.vue';//OSD信息
|
||||||
|
import CameraBitrateForm from './CameraBitrateForm.vue';//码流参数
|
||||||
|
import CameraBlockForm from './CameraBlockForm.vue';//镜头遮挡
|
||||||
|
import CameraLightAlarmForm from './CameraLightAlarmForm.vue';//白光报警
|
||||||
|
import CameraSoundAlarmForm from './CameraSoundAlarmForm.vue';//声音报警
|
||||||
|
import CameraRecordList from './CameraRecordList.vue';//录像列表
|
||||||
|
import CameraUploadForm from './CameraUploadForm.vue';//上传FTP
|
||||||
|
import { useMessage } from "@/hooks/web/useMessage";
|
||||||
|
|
||||||
|
const { createMessage } = useMessage();
|
||||||
|
let router = useRouter();
|
||||||
|
const formData = reactive<Record<string, any>>({
|
||||||
|
//预览通道参数
|
||||||
|
deviceIndex: '',//设备索引
|
||||||
|
parentId: '',//存储设备(nvr,nvs)ID
|
||||||
|
multitrans: '',//multitrans功能标识
|
||||||
|
scale: 1 ,//回放速率
|
||||||
|
projectId: '' ,//项目ID
|
||||||
|
regionId: '' ,//区域ID
|
||||||
|
ip: '' ,//IP
|
||||||
|
});
|
||||||
|
const cameraData = ref({});
|
||||||
|
const activeKey = ref(1);
|
||||||
|
const oneChildActiveKey = ref(1);
|
||||||
|
const twoChildActiveKey = ref(1);
|
||||||
|
const threeChildActiveKey = ref(1);
|
||||||
|
|
||||||
|
const imageData = reactive<Record<string, any>>({
|
||||||
|
//画面显示配置
|
||||||
|
luma: "50", //亮度
|
||||||
|
contrast: '50', //对比度
|
||||||
|
saturation: '50', //饱和度
|
||||||
|
sharpness: '50', //锐度
|
||||||
|
wide_dynamic: 'off', //宽动态
|
||||||
|
wd_gain: '50', //宽动态度
|
||||||
|
});
|
||||||
|
|
||||||
|
// function recordingConfig(){
|
||||||
|
// getRecordCfgs({
|
||||||
|
// "deviceIndex": formData.deviceIndex,
|
||||||
|
// "parentId": formData.parentId,
|
||||||
|
// "projectId": formData.projectId,
|
||||||
|
// "regionId": formData.regionId,
|
||||||
|
// "ip": formData.ip
|
||||||
|
// }).then(res=>{
|
||||||
|
//
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// function setRecordingPlan(){
|
||||||
|
// setRecordCfgs({
|
||||||
|
// "deviceIndex": formData.deviceIndex,
|
||||||
|
// "parentId": formData.parentId,
|
||||||
|
// "recordSwitch": 0,
|
||||||
|
// "streamType": 0
|
||||||
|
// }).then(res=>{
|
||||||
|
//
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// function batchProgress(){
|
||||||
|
// getBatchProgress({}).then(res=>{});
|
||||||
|
// }
|
||||||
|
|
||||||
|
onMounted(()=>{
|
||||||
|
formData.deviceIndex = router.currentRoute.value.query.deviceIndex;
|
||||||
|
formData.parentId = router.currentRoute.value.query.parentId;
|
||||||
|
formData.multitrans = router.currentRoute.value.query.multitrans;
|
||||||
|
formData.projectId = router.currentRoute.value.query.projectId;
|
||||||
|
formData.regionId = router.currentRoute.value.query.regionId;
|
||||||
|
formData.ip = router.currentRoute.value.query.ip;
|
||||||
|
|
||||||
|
cameraData.value.deviceIndex = formData.deviceIndex;
|
||||||
|
cameraData.value.projectId = formData.projectId;
|
||||||
|
cameraData.value.regionId = formData.regionId;
|
||||||
|
cameraData.value.multitrans = formData.multitrans;
|
||||||
|
cameraData.value.scale = formData.scale;
|
||||||
|
})
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.scrollable {
|
||||||
|
width: 99.6%;
|
||||||
|
height: calc(100vh - 160px);
|
||||||
|
overflow: auto; /* 或者使用 scroll, hidden, visible */
|
||||||
|
//border: 1px solid #000;
|
||||||
|
background-color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.ant-tabs-nav){
|
||||||
|
margin-bottom: 3px;
|
||||||
|
margin-left: -1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.ant-tabs-left>.ant-tabs-content-holder>.ant-tabs-content>.ant-tabs-tabpane){
|
||||||
|
padding-left: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
|
@ -0,0 +1,197 @@
|
||||||
|
<template>
|
||||||
|
<a-spin :spinning="confirmLoading">
|
||||||
|
<a-row>
|
||||||
|
<a-col :span="24">
|
||||||
|
<div id="video-container-playback" v-if="initContainerPlayback"></div>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="24" style="padding: 5px;">
|
||||||
|
<span style="margin-left: 15px;">回放倍速
|
||||||
|
<a-select
|
||||||
|
ref="select"
|
||||||
|
v-model:value="formData.scale"
|
||||||
|
style="width: 120px"
|
||||||
|
@focus="focus"
|
||||||
|
@change="changeScale"
|
||||||
|
>
|
||||||
|
<a-select-option value="0.0625">0.0625</a-select-option>
|
||||||
|
<a-select-option value="0.125">0.125</a-select-option>
|
||||||
|
<a-select-option value="0.25">0.25</a-select-option>
|
||||||
|
<a-select-option value="0.5">0.5</a-select-option>
|
||||||
|
<a-select-option value="1">1</a-select-option>
|
||||||
|
<a-select-option value="2">2</a-select-option>
|
||||||
|
<a-select-option value="4">4</a-select-option>
|
||||||
|
<a-select-option value="8">8</a-select-option>
|
||||||
|
<a-select-option value="16">16</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</span>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-spin>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { ref, reactive, defineExpose, nextTick, defineProps, computed, onMounted } from 'vue';
|
||||||
|
import { defHttp } from '/@/utils/http/axios';
|
||||||
|
import { useMessage } from '/@/hooks/web/useMessage';
|
||||||
|
import { getValueType } from '/@/utils';
|
||||||
|
import { Form } from 'ant-design-vue';
|
||||||
|
import {
|
||||||
|
getPlaybackUrlList,
|
||||||
|
deletePlaybackChn
|
||||||
|
} from "@/views/iot/tplink/camera/camera.api";
|
||||||
|
|
||||||
|
const TumsPlayer = window['tums-player'].default;
|
||||||
|
const props = defineProps({
|
||||||
|
formDisabled: { type: Boolean, default: false },
|
||||||
|
formData: { type: Object, default: () => ({})},
|
||||||
|
formBpm: { type: Boolean, default: true }
|
||||||
|
});
|
||||||
|
|
||||||
|
const formRef = ref();
|
||||||
|
const player = ref();
|
||||||
|
const initContainerPlayback = ref(true);
|
||||||
|
const useForm = Form.useForm;
|
||||||
|
const emit = defineEmits(['register', 'ok']);
|
||||||
|
const formData = reactive<Record<string, any>>({
|
||||||
|
//预览通道参数
|
||||||
|
videoDevId: '',//设备索引
|
||||||
|
startTime: '',//回放开始时间
|
||||||
|
endTime: '',//回放结束时间
|
||||||
|
videoType: '',//回放录像类型
|
||||||
|
scale: 1,//回放速率
|
||||||
|
sessionId: ''//回放通道对应的sessionId
|
||||||
|
});
|
||||||
|
const { createMessage } = useMessage();
|
||||||
|
const labelCol = ref<any>({ xs: { span: 24 }, sm: { span: 5 } });
|
||||||
|
const wrapperCol = ref<any>({ xs: { span: 24 }, sm: { span: 16 } });
|
||||||
|
const confirmLoading = ref<boolean>(false);
|
||||||
|
|
||||||
|
//表单验证
|
||||||
|
const validatorRules = reactive({
|
||||||
|
});
|
||||||
|
const { resetFields, validate, validateInfos } = useForm(formData, validatorRules, { immediate: false });
|
||||||
|
|
||||||
|
// 表单禁用
|
||||||
|
const disabled = computed(()=>{
|
||||||
|
if(props.formBpm === true){
|
||||||
|
if(props.formData.disabled === false){
|
||||||
|
return false;
|
||||||
|
}else{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return props.formDisabled;
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 详情
|
||||||
|
*/
|
||||||
|
async function edit(record) {
|
||||||
|
await nextTick(() => {
|
||||||
|
confirmLoading.value=true;
|
||||||
|
resetFields();
|
||||||
|
const tmpData = {};
|
||||||
|
Object.keys(formData).forEach((key) => {
|
||||||
|
if(record.hasOwnProperty(key)){
|
||||||
|
tmpData[key] = record[key]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
//赋值
|
||||||
|
Object.assign(formData, tmpData);
|
||||||
|
});
|
||||||
|
//获取录像回放地址
|
||||||
|
await getPlaybackUrlList({
|
||||||
|
"deviceIndex":formData.videoDevId,
|
||||||
|
"startTime":formData.startTime,
|
||||||
|
"endTime":formData.endTime,
|
||||||
|
"videoType":formData.videoType,
|
||||||
|
"scale":formData.scale
|
||||||
|
}).then(res=>{
|
||||||
|
confirmLoading.value=false;
|
||||||
|
// console.log(res);
|
||||||
|
const error = res.error;
|
||||||
|
if(error==null||error==''){
|
||||||
|
formData.sessionId = '';
|
||||||
|
const list = res.data;
|
||||||
|
list.forEach(item => {
|
||||||
|
formData.sessionId += item.sessionId+","
|
||||||
|
let startTime = item.startTime*1000;
|
||||||
|
let endTime = item.endTime*1000;
|
||||||
|
// console.log("url:"+item.url);
|
||||||
|
// console.log("socket:"+item.wssUrl);
|
||||||
|
// console.log("queryAddress:"+item.queryAddress);
|
||||||
|
// console.log("videoSessionId:"+item.sessionId);
|
||||||
|
// console.log("videoDevId:"+item.videoDevId);
|
||||||
|
// console.log("storageDevId:"+item.storageDevId);
|
||||||
|
// console.log("startTime:"+startTime);
|
||||||
|
// console.log("endTime:"+endTime);
|
||||||
|
// console.log("scale:"+item.scale);
|
||||||
|
player.value = new TumsPlayer('video-container-playback', {
|
||||||
|
"autoplay": true,
|
||||||
|
"resolution": "HD",
|
||||||
|
"streamType": "sdvod", // 回放类型
|
||||||
|
"url": item.url, // 该url为一次性 需要通过接口实时获取
|
||||||
|
"socket": item.wssUrl,
|
||||||
|
"type": "rtsp",
|
||||||
|
"decoderType": "wasm", // 硬软解类型
|
||||||
|
"queryAddress": item.queryAddress,
|
||||||
|
"videoSessionId": item.sessionId,
|
||||||
|
"videoDevId": item.videoDevId, // 设备id
|
||||||
|
// "useMultitrans": true,
|
||||||
|
"storageDevId": item.storageDevId,
|
||||||
|
"startTime": startTime, // 回放开始时间 13位
|
||||||
|
"endTime": endTime, // 回放结束时间 13位
|
||||||
|
"eventType": [1, 2], // 事件类型
|
||||||
|
"scale": item.scale // 回放倍速
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 切换回放倍
|
||||||
|
*/
|
||||||
|
function changeScale(){
|
||||||
|
createMessage.info('切换回放倍速至'+formData.scale+'倍');
|
||||||
|
let scale = parseInt(formData.scale) ;
|
||||||
|
player.value.setPlaybackConfig({"scale":scale});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 销毁
|
||||||
|
*/
|
||||||
|
async function destroy(){
|
||||||
|
console.log("destroy");
|
||||||
|
if (player.value){
|
||||||
|
deletePlaybackChn({
|
||||||
|
"deviceIndex":formData.videoDevId,
|
||||||
|
"sessionId":formData.sessionId
|
||||||
|
}).then(res=>{
|
||||||
|
console.log(res)
|
||||||
|
});
|
||||||
|
// player.value.destroy().then(() => { }); // 回放销毁时自动调用接口,跨域导致失败
|
||||||
|
initContainerPlayback.value = false
|
||||||
|
setTimeout(()=>{
|
||||||
|
initContainerPlayback.value = true
|
||||||
|
},1000)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
edit,
|
||||||
|
destroy
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.antd-modal-form {
|
||||||
|
padding: 14px;
|
||||||
|
}
|
||||||
|
#video-container-playback {
|
||||||
|
padding: 0px 25px;
|
||||||
|
width: 600px;
|
||||||
|
height: 550px;
|
||||||
|
background: #1a1a1a;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,67 @@
|
||||||
|
<template>
|
||||||
|
<j-modal :title="title" :width="width" :visible="visible" @ok="handleOk" :okButtonProps="{ class: { 'jee-hidden': disableSubmit } }" @cancel="handleCancel" cancelText="关闭">
|
||||||
|
<CameraPlaybackForm ref="registerForm" @ok="submitCallback" :formDisabled="disableSubmit" :formBpm="false"></CameraPlaybackForm>
|
||||||
|
</j-modal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { ref, nextTick, defineExpose } from 'vue';
|
||||||
|
import CameraPlaybackForm from './CameraPlaybackForm.vue'
|
||||||
|
import JModal from '/@/components/Modal/src/JModal/JModal.vue';
|
||||||
|
|
||||||
|
const title = ref<string>('');
|
||||||
|
const width = ref<number>(600);
|
||||||
|
const visible = ref<boolean>(false);
|
||||||
|
const disableSubmit = ref<boolean>(false);
|
||||||
|
const registerForm = ref();
|
||||||
|
const emit = defineEmits(['register', 'success']);
|
||||||
|
|
||||||
|
// const player = ref();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 编辑
|
||||||
|
* @param record
|
||||||
|
*/
|
||||||
|
function edit(record) {
|
||||||
|
title.value = "录像回放";;
|
||||||
|
visible.value = true;
|
||||||
|
nextTick(() => {
|
||||||
|
registerForm.value.edit(record);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 确定按钮点击事件
|
||||||
|
*/
|
||||||
|
function handleOk() {
|
||||||
|
registerForm.value.submitForm();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* form保存回调事件
|
||||||
|
*/
|
||||||
|
function submitCallback() {
|
||||||
|
handleCancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 取消按钮回调事件
|
||||||
|
*/
|
||||||
|
function handleCancel() {
|
||||||
|
registerForm.value.destroy();
|
||||||
|
visible.value = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
edit,
|
||||||
|
disableSubmit,
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less">
|
||||||
|
/**隐藏样式-modal确定按钮 */
|
||||||
|
.jee-hidden {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<style lang="less" scoped></style>
|
|
@ -0,0 +1,318 @@
|
||||||
|
<template>
|
||||||
|
<a-spin :spinning="confirmLoading">
|
||||||
|
<a-row>
|
||||||
|
<a-col :span="24">
|
||||||
|
<div id="video-container"></div>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="24" style="padding: 5px;">
|
||||||
|
<span style="margin-left: 5px;" v-show="!izPlaying">
|
||||||
|
<a-button preIcon="ant-design:play-circle-outlined" @click="play">播放</a-button>
|
||||||
|
</span>
|
||||||
|
<span style="margin-left: 5px;" v-show="izPlaying">
|
||||||
|
<a-button preIcon="ant-design:pause-circle-outlined" @click="pause">暂停</a-button>
|
||||||
|
</span>
|
||||||
|
<span style="margin-left: 5px;">
|
||||||
|
<a-button preIcon="ant-design:swap-outlined" @click="switchResolution">{{ resolution }}</a-button>
|
||||||
|
</span>
|
||||||
|
<span style="margin-left: 5px;">
|
||||||
|
<a-button preIcon="ant-design:phone-outlined" @click="screenshot">巡航*</a-button>
|
||||||
|
</span>
|
||||||
|
<span style="margin-left: 5px;">
|
||||||
|
<a-button preIcon="ant-design:phone-outlined" @click="screenshot">电话*</a-button>
|
||||||
|
</span>
|
||||||
|
<span style="margin-left: 15px;">分屏
|
||||||
|
<a-select
|
||||||
|
ref="select"
|
||||||
|
v-model:value="fishEyeDisplayMode"
|
||||||
|
style="width: 120px"
|
||||||
|
@focus="focus"
|
||||||
|
@change="setFishEyeDisplayMode"
|
||||||
|
>
|
||||||
|
<a-select-option value="ORIGIN">原图</a-select-option>
|
||||||
|
<a-select-option value="FISHEYE_360D">360全景</a-select-option>
|
||||||
|
<a-select-option value="FISHEYE_180D">180全景</a-select-option>
|
||||||
|
<a-select-option value="FISHEYE_WIN_PLANE_TOP_QUAD">四分屏</a-select-option>
|
||||||
|
<a-select-option value="FISHEYE_LONGITUDE">全景拉伸</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</span>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="24" style="padding: 5px;">
|
||||||
|
<span style="margin-left: 5px;">
|
||||||
|
<a-button preIcon="ant-design:picture-outlined" @click="screenshot">截图</a-button>
|
||||||
|
</span>
|
||||||
|
<span style="margin-left: 5px;" v-show="!izRecording">
|
||||||
|
<a-button preIcon="ant-design:video-camera-outlined" @click="recordingStart">录制</a-button>
|
||||||
|
</span>
|
||||||
|
<span style="margin-left: 5px;" v-show="izRecording">
|
||||||
|
<a-button type="primary" danger ghost preIcon="ant-design:video-camera-outlined" @click="recordingEnd">录制</a-button>
|
||||||
|
</span>
|
||||||
|
<!-- <span style="margin-left: 15px;">画面翻转
|
||||||
|
<a-select
|
||||||
|
ref="select"
|
||||||
|
v-model:value="rotateType"
|
||||||
|
style="width: 120px"
|
||||||
|
@focus="focus"
|
||||||
|
@change="saveImageSwitch"
|
||||||
|
>
|
||||||
|
<a-select-option value="off">不翻转</a-select-option>
|
||||||
|
<a-select-option value="anticlockwise_180">上下翻转</a-select-option>
|
||||||
|
<a-select-option value="clockwise_90">右翻转90°</a-select-option>
|
||||||
|
<a-select-option value="anticlockwise_90">左翻转90°</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</span>-->
|
||||||
|
<span style="margin-left: 5px;">
|
||||||
|
<a-button preIcon="ant-design:alert-outlined" @click="manualAlarm">报警</a-button>
|
||||||
|
</span>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-spin>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { ref, reactive, defineExpose, nextTick, defineProps, computed, onMounted } from 'vue';
|
||||||
|
import { defHttp } from '/@/utils/http/axios';
|
||||||
|
import { useMessage } from '/@/hooks/web/useMessage';
|
||||||
|
import { getValueType } from '/@/utils';
|
||||||
|
import { Form } from 'ant-design-vue';
|
||||||
|
import { getPreviewUrl, testAudio } from "@/views/iot/tplink/camera/camera.api";
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
formDisabled: { type: Boolean, default: false },
|
||||||
|
formData: { type: Object, default: () => ({})},
|
||||||
|
formBpm: { type: Boolean, default: true }
|
||||||
|
});
|
||||||
|
|
||||||
|
const formRef = ref();
|
||||||
|
const player = ref();
|
||||||
|
const resolution = ref<string>('超清');
|
||||||
|
const izPlaying = ref<boolean>(true);
|
||||||
|
const izRecording = ref<boolean>(false);
|
||||||
|
const fishEyeDisplayMode = ref<string>('ORIGIN');
|
||||||
|
const rotateType = ref<string>('off');
|
||||||
|
const useForm = Form.useForm;
|
||||||
|
const emit = defineEmits(['register', 'ok']);
|
||||||
|
const formData = reactive<Record<string, any>>({
|
||||||
|
|
||||||
|
//预览通道参数
|
||||||
|
deviceIndex: '',//设备索引
|
||||||
|
streamType: 0,//码流类型 0 代表主码流,1 代码子码流
|
||||||
|
|
||||||
|
//视频预览参数
|
||||||
|
url: '',//预览通道对应的URL
|
||||||
|
backupUrl: '',//备选URL,当流媒体服务器存在内外网IP配置时,该字段不为null
|
||||||
|
wsUrl: '',//用于建立ws连接传输视频帧信息
|
||||||
|
wssUrl: '',//用于建立wss接传输视频帧信息
|
||||||
|
|
||||||
|
});
|
||||||
|
const { createMessage } = useMessage();
|
||||||
|
const labelCol = ref<any>({ xs: { span: 24 }, sm: { span: 5 } });
|
||||||
|
const wrapperCol = ref<any>({ xs: { span: 24 }, sm: { span: 16 } });
|
||||||
|
const confirmLoading = ref<boolean>(false);
|
||||||
|
|
||||||
|
//表单验证
|
||||||
|
const validatorRules = reactive({
|
||||||
|
});
|
||||||
|
const { resetFields, validate, validateInfos } = useForm(formData, validatorRules, { immediate: false });
|
||||||
|
|
||||||
|
// 表单禁用
|
||||||
|
const disabled = computed(()=>{
|
||||||
|
if(props.formBpm === true){
|
||||||
|
if(props.formData.disabled === false){
|
||||||
|
return false;
|
||||||
|
}else{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return props.formDisabled;
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 详情
|
||||||
|
*/
|
||||||
|
async function edit(record) {
|
||||||
|
await nextTick(() => {
|
||||||
|
confirmLoading.value=true;
|
||||||
|
resetFields();
|
||||||
|
const tmpData = {};
|
||||||
|
Object.keys(formData).forEach((key) => {
|
||||||
|
if(record.hasOwnProperty(key)){
|
||||||
|
tmpData[key] = record[key]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
//赋值
|
||||||
|
Object.assign(formData, tmpData);
|
||||||
|
});
|
||||||
|
|
||||||
|
createPreview();
|
||||||
|
|
||||||
|
/* await getIpcCapability({"deviceIndex":formData.deviceIndex}).then(res=>{
|
||||||
|
console.log(res);
|
||||||
|
});*/
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建预览
|
||||||
|
*/
|
||||||
|
async function createPreview(){
|
||||||
|
await getPreviewUrl({"deviceIndex":formData.deviceIndex,"streamType":formData.streamType}).then(res=>{
|
||||||
|
formData.url = res.url;
|
||||||
|
formData.backupUrl = res.backupUrl;
|
||||||
|
formData.wsUrl = res.wsUrl;
|
||||||
|
formData.wssUrl = res.wssUrl;
|
||||||
|
confirmLoading.value=false;
|
||||||
|
});
|
||||||
|
if (player.value){
|
||||||
|
player.value.destroy().then(() => {
|
||||||
|
}); // 销毁
|
||||||
|
}
|
||||||
|
const TumsPlayer = window['tums-player'].default;
|
||||||
|
player.value = new TumsPlayer('video-container', {
|
||||||
|
type: "rtsp", // 协议类型,rtsp
|
||||||
|
url: formData.url, // 取流地址, getPreviewUrl接口获取
|
||||||
|
// url: formData.backupUrl, // 取流地址, getPreviewUrl接口获取
|
||||||
|
socket: formData.wssUrl, // websocket地址, getPreviewUrl接口获取
|
||||||
|
pluginPath: '/static', // 当sdk资源不在根路径下时,需配置pluginPath
|
||||||
|
});
|
||||||
|
|
||||||
|
let isPlaying = player.value.isPlaying();
|
||||||
|
if (!isPlaying) {
|
||||||
|
if (player.value.isInit) {
|
||||||
|
player.value.start();
|
||||||
|
} else {
|
||||||
|
player.value.play();
|
||||||
|
}
|
||||||
|
izPlaying.value = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 切换超清/流程
|
||||||
|
*/
|
||||||
|
function switchResolution(){
|
||||||
|
if(formData.streamType == 0){
|
||||||
|
resolution.value = '流畅';
|
||||||
|
formData.streamType = 1;
|
||||||
|
}else{
|
||||||
|
resolution.value = '超清';
|
||||||
|
formData.streamType = 0;
|
||||||
|
}
|
||||||
|
createMessage.info('正在切换至'+resolution.value);
|
||||||
|
createPreview();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 播放
|
||||||
|
*/
|
||||||
|
function play(){
|
||||||
|
izPlaying.value = true;
|
||||||
|
player.value.play();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 暂停
|
||||||
|
*/
|
||||||
|
function pause(){
|
||||||
|
izPlaying.value = false;
|
||||||
|
player.value.pause();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 截屏
|
||||||
|
*/
|
||||||
|
function screenshot(){
|
||||||
|
player.value.screenshot();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 鱼眼画面显示模式
|
||||||
|
*/
|
||||||
|
function setFishEyeDisplayMode(){
|
||||||
|
player.value.setFishEyeDisplayMode(fishEyeDisplayMode.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 画面翻转
|
||||||
|
*/
|
||||||
|
function saveImageSwitch(){
|
||||||
|
player.value.saveImageSwitch({rotate_type:1}).then(() => {
|
||||||
|
// 设置成功,实际画面需要0.5-2s进行响应
|
||||||
|
createMessage.success('设置成功,实际画面需要0.5-2s进行响应');
|
||||||
|
}).catch((errData) => {
|
||||||
|
// 设置失败,包括网络错误以及接口调用报错
|
||||||
|
// 具体错误见errData.error_code
|
||||||
|
console.log(errData);
|
||||||
|
createMessage.error('设置错误,'+errData.msg);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 手动报警
|
||||||
|
*/
|
||||||
|
function manualAlarm(){
|
||||||
|
let params = {
|
||||||
|
"deviceIndex": formData.deviceIndex,
|
||||||
|
"force": 1,
|
||||||
|
"id": '0'
|
||||||
|
};
|
||||||
|
testAudio(params);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 开始录制
|
||||||
|
*/
|
||||||
|
function recordingStart(){
|
||||||
|
izRecording.value = true;
|
||||||
|
player.value.startRecording({micStream:true}).then((res) => {
|
||||||
|
// 设置成功,返回resolve
|
||||||
|
console.log(res);
|
||||||
|
}).catch((errData) => {
|
||||||
|
// 设置失败,包括网络错误以及接口调用报错
|
||||||
|
// 具体错误见errData.error_code
|
||||||
|
createMessage.error('录制错误,'+errData.msg);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 结束录制
|
||||||
|
*/
|
||||||
|
function recordingEnd(){
|
||||||
|
izRecording.value = false;
|
||||||
|
let fileName = formData.deviceIndex+'-'+(new Date().getTime());
|
||||||
|
player.value.stopRecording(fileName, true).then((res) => {
|
||||||
|
// 设置成功,返回resolve
|
||||||
|
console.log(res);
|
||||||
|
}).catch((errData) => {
|
||||||
|
// 设置失败,包括网络错误以及接口调用报错
|
||||||
|
// 具体错误见errData.error_code
|
||||||
|
createMessage.error('录制错误,'+errData.msg);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 销毁
|
||||||
|
*/
|
||||||
|
function destroy(){
|
||||||
|
console.log("destroy");
|
||||||
|
if (player){
|
||||||
|
player.value.destroy().then(() => {
|
||||||
|
}); // 销毁
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
edit,
|
||||||
|
destroy
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.antd-modal-form {
|
||||||
|
padding: 14px;
|
||||||
|
}
|
||||||
|
#video-container {
|
||||||
|
padding: 0px 25px;
|
||||||
|
width: 600px;
|
||||||
|
height: 500px;
|
||||||
|
background: #1a1a1a;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,67 @@
|
||||||
|
<template>
|
||||||
|
<j-modal :title="title" :width="width" :visible="visible" @ok="handleOk" :okButtonProps="{ class: { 'jee-hidden': disableSubmit } }" @cancel="handleCancel" cancelText="关闭">
|
||||||
|
<CameraPreviewForm ref="registerForm" @ok="submitCallback" :formDisabled="disableSubmit" :formBpm="false"></CameraPreviewForm>
|
||||||
|
</j-modal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { ref, nextTick, defineExpose } from 'vue';
|
||||||
|
import CameraPreviewForm from './CameraPreviewForm.vue'
|
||||||
|
import JModal from '/@/components/Modal/src/JModal/JModal.vue';
|
||||||
|
|
||||||
|
const title = ref<string>('');
|
||||||
|
const width = ref<number>(600);
|
||||||
|
const visible = ref<boolean>(false);
|
||||||
|
const disableSubmit = ref<boolean>(false);
|
||||||
|
const registerForm = ref();
|
||||||
|
const emit = defineEmits(['register', 'success']);
|
||||||
|
|
||||||
|
// const player = ref();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 编辑
|
||||||
|
* @param record
|
||||||
|
*/
|
||||||
|
function edit(record) {
|
||||||
|
title.value = record.deviceName;
|
||||||
|
visible.value = true;
|
||||||
|
nextTick(() => {
|
||||||
|
registerForm.value.edit(record);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 确定按钮点击事件
|
||||||
|
*/
|
||||||
|
function handleOk() {
|
||||||
|
registerForm.value.submitForm();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* form保存回调事件
|
||||||
|
*/
|
||||||
|
function submitCallback() {
|
||||||
|
handleCancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 取消按钮回调事件
|
||||||
|
*/
|
||||||
|
function handleCancel() {
|
||||||
|
registerForm.value.destroy();
|
||||||
|
visible.value = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
edit,
|
||||||
|
disableSubmit,
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less">
|
||||||
|
/**隐藏样式-modal确定按钮 */
|
||||||
|
.jee-hidden {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<style lang="less" scoped></style>
|
|
@ -0,0 +1,133 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<!--引用表格-->
|
||||||
|
<BasicTable @register="registerTable">
|
||||||
|
<!--插槽:table标题-->
|
||||||
|
<template #tableTitle>
|
||||||
|
</template>
|
||||||
|
<!--操作栏-->
|
||||||
|
<template #action="{ record }">
|
||||||
|
<TableAction :actions="getTableAction(record)"/>
|
||||||
|
</template>
|
||||||
|
<template v-slot:bodyCell="{ column, record, index, text }">
|
||||||
|
</template>
|
||||||
|
</BasicTable>
|
||||||
|
</div>
|
||||||
|
<CameraPlaybackModal ref="playbackModal"></CameraPlaybackModal>
|
||||||
|
<CameraMultitransModal ref="multitransModal"></CameraMultitransModal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" name="iot-nuIotCameraInfo" setup>
|
||||||
|
import {ref, reactive, createVNode, h, onMounted, watch, unref} from 'vue';
|
||||||
|
import { BasicTable, useTable, TableAction } from '/@/components/Table';
|
||||||
|
import { useListPage } from '/@/hooks/system/useListPage';
|
||||||
|
import { recordingColumns,searchRecording } from '@/views/iot/tplink/camera/camera.data';
|
||||||
|
import { searchVideo } from '@/views/iot/tplink/camera/camera.api';
|
||||||
|
import { useUserStore } from '/@/store/modules/user';
|
||||||
|
import { useDrawer } from "@/components/Drawer";
|
||||||
|
import { useRouter } from 'vue-router';
|
||||||
|
import CameraPlaybackModal from './CameraPlaybackModal.vue';//普通回放
|
||||||
|
import CameraMultitransModal from './CameraMultitransModal.vue';//Multitrans模式回放
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
data: { type: Object, default: () => ({}) },
|
||||||
|
});
|
||||||
|
//注册drawer
|
||||||
|
const [registerDrawer, { openDrawer }] = useDrawer();
|
||||||
|
let router = useRouter();
|
||||||
|
const formRef = ref();
|
||||||
|
const queryParam = reactive<any>({});
|
||||||
|
const registerModal = ref();
|
||||||
|
const userStore = useUserStore();
|
||||||
|
const playbackModal = ref();
|
||||||
|
const multitransModal = ref();
|
||||||
|
//注册table数据
|
||||||
|
const { prefixCls, tableContext, onExportXls, onImportXls } = useListPage({
|
||||||
|
tableProps: {
|
||||||
|
title: '护理单元-物联管理-录像信息',
|
||||||
|
api: searchVideo,
|
||||||
|
columns: recordingColumns,
|
||||||
|
canResize: false,
|
||||||
|
showIndexColumn: true,
|
||||||
|
actionColumn: {
|
||||||
|
width: 180,
|
||||||
|
fixed: 'right',
|
||||||
|
},
|
||||||
|
formConfig: {
|
||||||
|
schemas: searchRecording,
|
||||||
|
},
|
||||||
|
beforeFetch: async (params) => {
|
||||||
|
return Object.assign(params, queryParam);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const [registerTable, { reload, collapseAll, updateTableDataRecord, findTableDataRecord, getDataSource }, { rowSelection, selectedRowKeys }] = tableContext;
|
||||||
|
const labelCol = reactive({
|
||||||
|
xs:24,
|
||||||
|
sm:4,
|
||||||
|
xl:6,
|
||||||
|
xxl:4
|
||||||
|
});
|
||||||
|
const wrapperCol = reactive({
|
||||||
|
xs: 24,
|
||||||
|
sm: 20,
|
||||||
|
});
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
watch(
|
||||||
|
() => props.data,
|
||||||
|
async () => {
|
||||||
|
queryParam.deviceIndex = props.data.deviceIndex;
|
||||||
|
queryParam.projectId = props.data.projectId;
|
||||||
|
queryParam.regionId = props.data.regionId;
|
||||||
|
queryParam.multitrans = props.data.multitrans;
|
||||||
|
queryParam.scale = props.data.scale;
|
||||||
|
// let record = unref(props.data);
|
||||||
|
// if (typeof record !== 'object') {
|
||||||
|
// record = {};
|
||||||
|
// }
|
||||||
|
reload();
|
||||||
|
},
|
||||||
|
{ deep: true, immediate: true }
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 播放
|
||||||
|
*/
|
||||||
|
function handlePlayback(record: Recordable) {
|
||||||
|
record.scale = props.data.scale;
|
||||||
|
record.multitrans = props.data.multitrans;
|
||||||
|
if(record.multitrans=="1"){
|
||||||
|
multitransModal.value.disableSubmit = true;
|
||||||
|
multitransModal.value.edit(record);
|
||||||
|
}else{
|
||||||
|
playbackModal.value.disableSubmit = true;
|
||||||
|
playbackModal.value.edit(record);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 成功回调
|
||||||
|
*/
|
||||||
|
function handleSuccess() {
|
||||||
|
reload();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 操作栏
|
||||||
|
*/
|
||||||
|
function getTableAction(record) {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
label: '播放',
|
||||||
|
onClick: handlePlayback.bind(null, record),
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
|
||||||
|
</style>
|
|
@ -0,0 +1,713 @@
|
||||||
|
<template>
|
||||||
|
<a-spin :spinning="confirmLoading">
|
||||||
|
<JFormContainer :disabled="disabled">
|
||||||
|
<template #detail>
|
||||||
|
<a-row>
|
||||||
|
<a-col :span="24">
|
||||||
|
<a-row>
|
||||||
|
<a-col :span="20">
|
||||||
|
<a-divider orientation="left">声音报警</a-divider>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="20">
|
||||||
|
<a-row style="margin-bottom: 14px">
|
||||||
|
<a-col :span="4" class="labelText">
|
||||||
|
声音报警
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="16">
|
||||||
|
<a-switch v-model:checked="formData.enabled" checked-children="已开启" un-checked-children="已关闭" />
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="24">
|
||||||
|
<a-row>
|
||||||
|
<a-col :span="20">
|
||||||
|
<a-divider orientation="left">布防时间设置</a-divider>
|
||||||
|
</a-col>
|
||||||
|
<a-col style="width: 800px;">
|
||||||
|
<a-row style="margin-bottom: 14px">
|
||||||
|
<a-col :span="2" class="labelText">
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="22">
|
||||||
|
<div class="cleanTitle">
|
||||||
|
<div>仅在以下时间段进行声音报警</div>
|
||||||
|
<div>
|
||||||
|
<a-button preIcon="ant-design:delete-outlined" @click="cleanPlan" :disabled="!formData.enabled">清空计划</a-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-col>
|
||||||
|
<a-col style="width: 800px;">
|
||||||
|
<a-row style="margin-bottom: 14px">
|
||||||
|
<a-col :span="2" class="labelText">
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="22">
|
||||||
|
<div class="armingSchedule" id="armingSchedule" @mousedown="handleMouseDown">
|
||||||
|
<div id="recPlanGrid" class="test" style="overflow: hidden;">
|
||||||
|
<div class="cruiseDiv"><i class="iCruise displayNone" style="left: 373px;"></i></div>
|
||||||
|
<div class="psHourList">
|
||||||
|
<div class="psHourLi" v-for="(idx,index) in 25" :key="index">
|
||||||
|
<span>{{ index }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<ul class="psWeekList">
|
||||||
|
<li>星期一</li>
|
||||||
|
<li>星期二</li>
|
||||||
|
<li>星期三</li>
|
||||||
|
<li>星期四</li>
|
||||||
|
<li>星期五</li>
|
||||||
|
<li>星期六</li>
|
||||||
|
<li>星期日</li>
|
||||||
|
</ul>
|
||||||
|
<div class="tableDiv">
|
||||||
|
<div class="trDiv" v-for="weekDay in 7">
|
||||||
|
<div :class="getClass(index)" v-for="(idx,index) in 25" :key="index">
|
||||||
|
<div class="blank" style="width: 24px;" :id="getId(weekDay,index)" @click="divSelect($event,weekDay,index)" @mouseover="overDivSelect($event,weekDay,index)"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="editDiv"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="24">
|
||||||
|
<a-row style="margin-top: 14px">
|
||||||
|
<a-col :span="1"></a-col>
|
||||||
|
<a-col :span="20">
|
||||||
|
<a-button type="primary" preIcon="ant-design:save-outlined" @click="saveAlarm">保存</a-button>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</template>
|
||||||
|
</JFormContainer>
|
||||||
|
</a-spin>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { ref, reactive, defineProps, onMounted, watch,} from 'vue';
|
||||||
|
import { defHttp } from '/@/utils/http/axios';
|
||||||
|
import { useMessage } from '/@/hooks/web/useMessage';
|
||||||
|
import JDictSelectTag from '/@/components/Form/src/jeecg/components/JDictSelectTag.vue';
|
||||||
|
import { getValueType } from '/@/utils';
|
||||||
|
import { Form } from 'ant-design-vue';
|
||||||
|
import JFormContainer from '/@/components/Form/src/container/JFormContainer.vue';
|
||||||
|
import {
|
||||||
|
getAlarmInfo, setAlarmInfo,
|
||||||
|
getAlarmPlan, setAlarmPlan,
|
||||||
|
} from "@/views/iot/tplink/camera/camera.api";
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
data: { type: Object, default: () => ({}) },
|
||||||
|
});
|
||||||
|
|
||||||
|
const formData = reactive<Record<string, any>>({
|
||||||
|
deviceIndex: '', //设备索引
|
||||||
|
enabled: false, //镜头遮挡
|
||||||
|
});
|
||||||
|
|
||||||
|
const confirmLoading = ref<boolean>(false);
|
||||||
|
const planVisible = ref<boolean>(false);
|
||||||
|
const mondayArr = ref<string>([]);
|
||||||
|
const mondayMergeArr = ref<string>([]);
|
||||||
|
const tuesdayArr = ref<string>([]);
|
||||||
|
const tuesdayMergeArr = ref<string>([]);
|
||||||
|
const wednesdayArr = ref<string>([]);
|
||||||
|
const wednesdayMergeArr = ref<string>([]);
|
||||||
|
const thursdayArr = ref<string>([]);
|
||||||
|
const thursdayMergeArr = ref<string>([]);
|
||||||
|
const fridayArr = ref<string>([]);
|
||||||
|
const fridayMergeArr = ref<string>([]);
|
||||||
|
const saturdayArr = ref<string>([]);
|
||||||
|
const saturdayMergeArr = ref<string>([]);
|
||||||
|
const sundayArr = ref<string>([]);
|
||||||
|
const sundayMergeArr = ref<string>([]);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置单元格样式
|
||||||
|
*/
|
||||||
|
function getClass(index){
|
||||||
|
if(index == 0) {
|
||||||
|
return 'tdDiv firTd';
|
||||||
|
}else if(index == 23){
|
||||||
|
return 'tdDiv lastTd';
|
||||||
|
}else if(index > 23){
|
||||||
|
return 'editBtn';
|
||||||
|
}else{
|
||||||
|
return 'tdDiv';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置单元格ID
|
||||||
|
*/
|
||||||
|
function getId(weekDay,index){
|
||||||
|
let id = weekDay+'-'+index;
|
||||||
|
return id
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 格式化各星期数据
|
||||||
|
*/
|
||||||
|
function initPlan(armingScheduleSound){
|
||||||
|
let monday = decodeURIComponent(armingScheduleSound.monday);
|
||||||
|
initValue(1,monday);
|
||||||
|
mergeNumbers(1,1,-1);
|
||||||
|
let tuesday = decodeURIComponent(armingScheduleSound.tuesday);
|
||||||
|
initValue(2,tuesday);
|
||||||
|
mergeNumbers(1,2,-1);
|
||||||
|
let wednesday = decodeURIComponent(armingScheduleSound.wednesday);
|
||||||
|
initValue(3,wednesday);
|
||||||
|
mergeNumbers(1,3,-1);
|
||||||
|
let thursday = decodeURIComponent(armingScheduleSound.thursday);
|
||||||
|
initValue(4,thursday);
|
||||||
|
mergeNumbers(1,4,-1);
|
||||||
|
let friday = decodeURIComponent(armingScheduleSound.friday);
|
||||||
|
initValue(5,friday);
|
||||||
|
mergeNumbers(1,5,-1);
|
||||||
|
let saturday = decodeURIComponent(armingScheduleSound.saturday);
|
||||||
|
initValue(6,saturday);
|
||||||
|
mergeNumbers(1,6,-1);
|
||||||
|
let sunday = decodeURIComponent(armingScheduleSound.sunday);
|
||||||
|
initValue(7,sunday);
|
||||||
|
mergeNumbers(1,7,-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化各星期数据
|
||||||
|
*/
|
||||||
|
function initValue(weekDay,valueArr){
|
||||||
|
valueArr = valueArr.replaceAll('[','').replaceAll(']','').replaceAll('"','');
|
||||||
|
const arr = valueArr.split(",");
|
||||||
|
arr.forEach(item =>{
|
||||||
|
const hours = item.split("-");
|
||||||
|
const start = parseInt(hours[0].substring(0, 2));
|
||||||
|
const end = parseInt(hours[1].substring(0, 2));
|
||||||
|
if(start<end){
|
||||||
|
for(let i=start;i<end;i++){
|
||||||
|
const id = weekDay+'-'+i;
|
||||||
|
const element = document.getElementById(id);
|
||||||
|
element.className = 'timing';
|
||||||
|
switch (weekDay) {
|
||||||
|
case 1:
|
||||||
|
mondayArr.value.push(i);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
tuesdayArr.value.push(i);
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
wednesdayArr.value.push(i);
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
thursdayArr.value.push(i);
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
fridayArr.value.push(i);
|
||||||
|
break;
|
||||||
|
case 6:
|
||||||
|
saturdayArr.value.push(i);
|
||||||
|
break;
|
||||||
|
case 7:
|
||||||
|
sundayArr.value.push(i);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清除计划
|
||||||
|
*/
|
||||||
|
function cleanPlan(){
|
||||||
|
const elements = document.querySelectorAll('.timing');
|
||||||
|
elements.forEach(item =>{
|
||||||
|
item.className = 'blank';
|
||||||
|
})
|
||||||
|
mondayArr.value = [];
|
||||||
|
mondayMergeArr.value = [];
|
||||||
|
tuesdayArr.value = [];
|
||||||
|
tuesdayMergeArr.value = [];
|
||||||
|
wednesdayArr.value = [];
|
||||||
|
wednesdayMergeArr.value = [];
|
||||||
|
thursdayArr.value = [];
|
||||||
|
thursdayMergeArr.value = [];
|
||||||
|
fridayArr.value = [];
|
||||||
|
fridayMergeArr.value = [];
|
||||||
|
saturdayArr.value = [];
|
||||||
|
saturdayMergeArr.value = [];
|
||||||
|
sundayArr.value = [];
|
||||||
|
sundayMergeArr.value = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 合并各星期计划数组
|
||||||
|
*/
|
||||||
|
function mergeNumbers(type,weekDay,hourValue) {
|
||||||
|
let numbers = [];
|
||||||
|
switch (weekDay) {
|
||||||
|
case 1:
|
||||||
|
Object.assign(numbers, mondayArr.value);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
Object.assign(numbers, tuesdayArr.value);
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
Object.assign(numbers, wednesdayArr.value);
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
Object.assign(numbers, thursdayArr.value);
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
Object.assign(numbers, fridayArr.value);
|
||||||
|
break;
|
||||||
|
case 6:
|
||||||
|
Object.assign(numbers, saturdayArr.value);
|
||||||
|
break;
|
||||||
|
case 7:
|
||||||
|
Object.assign(numbers, sundayArr.value);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(hourValue!=-1){
|
||||||
|
if(type == 1) {
|
||||||
|
numbers.push(hourValue);
|
||||||
|
numbers.sort(function(a, b) {
|
||||||
|
return a - b;
|
||||||
|
});
|
||||||
|
}else {
|
||||||
|
let idx = numbers.indexOf(hourValue);
|
||||||
|
numbers.splice(idx,1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let start = numbers[0];
|
||||||
|
let end = null;
|
||||||
|
let mergedNumbers = [];
|
||||||
|
|
||||||
|
for (let i = 0; i < numbers.length; i++) {
|
||||||
|
if (i === 0) {
|
||||||
|
start = numbers[i];
|
||||||
|
end = numbers[i];
|
||||||
|
} else if (numbers[i] === numbers[i - 1] + 1) {
|
||||||
|
end = numbers[i];
|
||||||
|
} else {
|
||||||
|
if (start === end) {
|
||||||
|
mergedNumbers.push(start.toString());
|
||||||
|
} else {
|
||||||
|
mergedNumbers.push(`${start}-${end}`);
|
||||||
|
}
|
||||||
|
start = numbers[i];
|
||||||
|
end = numbers[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (start === end) {
|
||||||
|
mergedNumbers.push(start.toString());
|
||||||
|
} else {
|
||||||
|
mergedNumbers.push(`${start}-${end}`);
|
||||||
|
}
|
||||||
|
if(mergedNumbers.length>6){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
switch (weekDay) {
|
||||||
|
case 1:
|
||||||
|
mondayArr.value = [];
|
||||||
|
mondayMergeArr.value = [];
|
||||||
|
Object.assign(mondayMergeArr.value, mergedNumbers);
|
||||||
|
Object.assign(mondayArr.value, numbers);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
tuesdayArr.value = [];
|
||||||
|
tuesdayMergeArr.value = [];
|
||||||
|
Object.assign(tuesdayMergeArr.value, mergedNumbers);
|
||||||
|
Object.assign(tuesdayArr.value, numbers);
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
wednesdayArr.value = [];
|
||||||
|
wednesdayMergeArr.value = [];
|
||||||
|
Object.assign(wednesdayMergeArr.value, mergedNumbers);
|
||||||
|
Object.assign(wednesdayArr.value, numbers);
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
thursdayArr.value = [];
|
||||||
|
thursdayMergeArr.value = [];
|
||||||
|
Object.assign(thursdayMergeArr.value, mergedNumbers);
|
||||||
|
Object.assign(thursdayArr.value, numbers);
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
fridayArr.value = [];
|
||||||
|
fridayMergeArr.value = [];
|
||||||
|
Object.assign(fridayMergeArr.value, mergedNumbers);
|
||||||
|
Object.assign(fridayArr.value, numbers);
|
||||||
|
break;
|
||||||
|
case 6:
|
||||||
|
saturdayArr.value = [];
|
||||||
|
saturdayMergeArr.value = [];
|
||||||
|
Object.assign(saturdayMergeArr.value, mergedNumbers);
|
||||||
|
Object.assign(saturdayArr.value, numbers);
|
||||||
|
break;
|
||||||
|
case 7:
|
||||||
|
sundayArr.value = [];
|
||||||
|
sundayMergeArr.value = [];
|
||||||
|
Object.assign(sundayMergeArr.value, mergedNumbers);
|
||||||
|
Object.assign(sundayArr.value, numbers);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// console.log('星期一', mondayArr.value);
|
||||||
|
// console.log('星期二', tuesdayArr.value);
|
||||||
|
// console.log('星期三', wednesdayArr.value);
|
||||||
|
// console.log('星期四', thursdayArr.value);
|
||||||
|
// console.log('星期五', fridayArr.value);
|
||||||
|
// console.log('星期六', saturdayArr.value);
|
||||||
|
// console.log('星期七', sundayArr.value);
|
||||||
|
|
||||||
|
// console.log('星期一', mondayMergeArr.value);
|
||||||
|
// console.log('星期二', tuesdayMergeArr.value);
|
||||||
|
// console.log('星期三', wednesdayMergeArr.value);
|
||||||
|
// console.log('星期四', thursdayMergeArr.value);
|
||||||
|
// console.log('星期五', fridayMergeArr.value);
|
||||||
|
// console.log('星期六', saturdayMergeArr.value);
|
||||||
|
// console.log('星期七', sundayMergeArr.value);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 点击选择单元格
|
||||||
|
*/
|
||||||
|
function divSelect(event,weekDay,hourValue){
|
||||||
|
if(formData.enabled){
|
||||||
|
if(event.target.className == "blank"){
|
||||||
|
let visiable = mergeNumbers(1,weekDay,hourValue);
|
||||||
|
if(visiable){
|
||||||
|
event.target.className = "timing";
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
let visiable = mergeNumbers(2,weekDay,hourValue);
|
||||||
|
if(visiable){
|
||||||
|
event.target.className = "blank";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 悬浮选择单元格
|
||||||
|
*/
|
||||||
|
function overDivSelect(event,weekDay,hourValue){
|
||||||
|
if(formData.enabled&&planVisible.value){
|
||||||
|
if(event.target.className == "blank"){
|
||||||
|
let visiable = mergeNumbers(1,weekDay,hourValue);
|
||||||
|
if(visiable){
|
||||||
|
event.target.className = "timing";
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
let visiable = mergeNumbers(2,weekDay,hourValue);
|
||||||
|
if(visiable){
|
||||||
|
event.target.className = "blank";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取报警
|
||||||
|
*/
|
||||||
|
function getAlarm(deviceIndex){
|
||||||
|
if(deviceIndex==null){
|
||||||
|
return
|
||||||
|
}
|
||||||
|
//获取声音报警信息
|
||||||
|
getAlarmInfo({
|
||||||
|
"deviceIndex": deviceIndex
|
||||||
|
}).then(res=>{
|
||||||
|
if(res.sound_alarm_enabled == "on"){
|
||||||
|
formData.enabled = true;
|
||||||
|
}else{
|
||||||
|
formData.enabled = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
//获取报警计划划信息
|
||||||
|
getAlarmPlan({
|
||||||
|
"deviceIndex": deviceIndex,
|
||||||
|
"type": "sound"
|
||||||
|
}).then(res=>{
|
||||||
|
let armingScheduleSound = res.arming_schedule_sound;
|
||||||
|
initPlan(armingScheduleSound);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 保存声音报警设置信息
|
||||||
|
*/
|
||||||
|
function saveAlarm(){
|
||||||
|
confirmLoading.value = true;
|
||||||
|
let params = {
|
||||||
|
"deviceIndex": formData.deviceIndex,
|
||||||
|
"type": "sound"
|
||||||
|
};
|
||||||
|
if(formData.enabled){
|
||||||
|
params["enabled"] = "on";
|
||||||
|
}else{
|
||||||
|
params["enabled"] = "off";
|
||||||
|
}
|
||||||
|
setAlarmInfo(params).then(res=>{
|
||||||
|
confirmLoading.value = false;
|
||||||
|
});
|
||||||
|
if(formData.enabled){
|
||||||
|
params["alarmPlan"] = setAlarmPlanData();
|
||||||
|
setAlarmPlan(params).then(res=>{
|
||||||
|
confirmLoading.value = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取报警计划数据
|
||||||
|
*/
|
||||||
|
function setAlarmPlanData(){
|
||||||
|
const monday = getHoursEncode(mondayMergeArr.value);
|
||||||
|
const tuesday = getHoursEncode(tuesdayMergeArr.value);
|
||||||
|
const wednesday = getHoursEncode(wednesdayMergeArr.value);
|
||||||
|
const thursday = getHoursEncode(thursdayMergeArr.value);
|
||||||
|
const friday = getHoursEncode(fridayMergeArr.value);
|
||||||
|
const saturday = getHoursEncode(saturdayMergeArr.value);
|
||||||
|
const sunday = getHoursEncode(sundayMergeArr.value);
|
||||||
|
let alarmPlan = {
|
||||||
|
"monday": monday,
|
||||||
|
"tuesday": tuesday,
|
||||||
|
"wednesday": wednesday,
|
||||||
|
"thursday": thursday,
|
||||||
|
"friday": friday,
|
||||||
|
"saturday": saturday,
|
||||||
|
"sunday": sunday
|
||||||
|
}
|
||||||
|
return alarmPlan;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 格式化设置计划数据
|
||||||
|
* @param weekArr
|
||||||
|
*/
|
||||||
|
function getHoursEncode(weekArr){
|
||||||
|
let hoursEncode = "[";
|
||||||
|
if(weekArr.length>0){
|
||||||
|
let hourArrStr = "";
|
||||||
|
weekArr.forEach(item =>{
|
||||||
|
let hours = 0;
|
||||||
|
let end = 0;
|
||||||
|
const arr = item.split("-");
|
||||||
|
hours = parseInt(arr[0]);
|
||||||
|
if(arr.length>1){
|
||||||
|
end = parseInt(arr[1]) + 1;
|
||||||
|
}else{
|
||||||
|
end = hours + 1;
|
||||||
|
}
|
||||||
|
if(hours<10){
|
||||||
|
hours = "0" + hours;
|
||||||
|
}
|
||||||
|
hours = hours + "00-"
|
||||||
|
if(end<10){
|
||||||
|
hours = hours + "0" + end;
|
||||||
|
}else{
|
||||||
|
hours = hours + end;
|
||||||
|
}
|
||||||
|
hours = hours + "00";
|
||||||
|
hours = '"' + hours + '"';
|
||||||
|
hourArrStr = hourArrStr + hours + ",";
|
||||||
|
})
|
||||||
|
hoursEncode = "[" + hourArrStr.substring(0,hourArrStr.length-1) + "]";
|
||||||
|
}else{
|
||||||
|
hoursEncode = '["0000-0000"]';
|
||||||
|
}
|
||||||
|
return encodeURIComponent(hoursEncode);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 鼠标按下
|
||||||
|
*/
|
||||||
|
function handleMouseDown(){
|
||||||
|
// console.log('鼠标按下', event);
|
||||||
|
planVisible.value = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 鼠标抬起
|
||||||
|
*/
|
||||||
|
function handleMouseUp(){
|
||||||
|
// console.log('鼠标抬起', event);
|
||||||
|
planVisible.value = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
watch(
|
||||||
|
() => props.data,
|
||||||
|
async () => {
|
||||||
|
formData.deviceIndex = props.data.deviceIndex;
|
||||||
|
getAlarm(formData.deviceIndex);
|
||||||
|
},
|
||||||
|
{ deep: true, immediate: true }
|
||||||
|
);
|
||||||
|
// window.addEventListener('mousedown', handleMouseDown);
|
||||||
|
window.addEventListener('mouseup', handleMouseUp);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.antd-modal-form {
|
||||||
|
padding: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.labelText {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-end;
|
||||||
|
padding-right: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cleanTitle {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.armingSchedule {
|
||||||
|
*position: relative;
|
||||||
|
z-index: 0;
|
||||||
|
background: #ebeef0;
|
||||||
|
border: 1px solid #ced7e0;
|
||||||
|
height: 260px;
|
||||||
|
border-radius: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.cruiseDiv {
|
||||||
|
width: 100%;
|
||||||
|
height: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.cruiseDiv i.iCruise {
|
||||||
|
width: 8px;
|
||||||
|
height: 8px;
|
||||||
|
position: relative;
|
||||||
|
//background: url(../../web-static/images/pointDown.png) no-repeat;
|
||||||
|
display: inline-block;
|
||||||
|
*display: inline;
|
||||||
|
*zoom: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.psHourList {
|
||||||
|
color: #666;
|
||||||
|
margin-left: 63px;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.psHourList div.psHourLi {
|
||||||
|
width: 25px;
|
||||||
|
display: inline-block;
|
||||||
|
text-align: center;
|
||||||
|
*display: inline;
|
||||||
|
*zoom: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul.psWeekList {
|
||||||
|
float: left;
|
||||||
|
color: #333;
|
||||||
|
font-size: 13px;
|
||||||
|
list-style: none;
|
||||||
|
margin-right: 16px;
|
||||||
|
margin-left: 20px;
|
||||||
|
_width: 42px;
|
||||||
|
_margin-left: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul.psWeekList li {
|
||||||
|
height: 32px;
|
||||||
|
line-height: 32px;
|
||||||
|
_height: 34px;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.tableDiv {
|
||||||
|
z-index: 0;
|
||||||
|
*position: relative;
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.tableDiv div.trDiv {
|
||||||
|
width: 100%;
|
||||||
|
height: 24px;
|
||||||
|
margin-top: 4px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.tableDiv div.trDiv div.firTd {
|
||||||
|
border-left: solid 1px #ced7e0;
|
||||||
|
border-bottom-left-radius: 2px!important;
|
||||||
|
border-top-left-radius: 2px!important;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.tableDiv div.trDiv div.tdDiv {
|
||||||
|
height: 24px;
|
||||||
|
overflow: hidden;
|
||||||
|
vertical-align: middle;
|
||||||
|
border-right: solid 1px #ced7e0;
|
||||||
|
border-top: solid 1px #ced7e0;
|
||||||
|
border-bottom: solid 1px #ced7e0;
|
||||||
|
display: inline-block;
|
||||||
|
*display: inline;
|
||||||
|
*zoom: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.tableDiv div.trDiv div.editBtn {
|
||||||
|
//background-image: url(@/assets/loginmini/icon/jeecg_bg.png);
|
||||||
|
//background-image: url(@/assets/images/planEdit.png);
|
||||||
|
/* background-image: url(@/views/iot/tplink/camera/icons/planEdit.png);*/
|
||||||
|
background: url(../icons/plan_set.png) no-repeat;
|
||||||
|
background-size: cover;
|
||||||
|
//background: url(@/assets/images/planEdit.png) no-repeat;
|
||||||
|
//background: new URL('@/assets/images/planEdit.png', import.meta.url).href;
|
||||||
|
width: 20px;
|
||||||
|
vertical-align: middle;
|
||||||
|
height: 24px;
|
||||||
|
cursor: pointer;
|
||||||
|
margin-left: 8px;
|
||||||
|
margin-right: 8px;
|
||||||
|
display: none;
|
||||||
|
zoom: 1;
|
||||||
|
}
|
||||||
|
div.tableDiv div.trDiv:hover div.editBtn{
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
div.tableDiv div.trDiv div.lastTd {
|
||||||
|
border-bottom-right-radius: 2px;
|
||||||
|
border-top-right-radius: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.tableDiv div.trDiv div.tdDiv div.timing {
|
||||||
|
background-color: #5a92ff;
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.tableDiv div.trDiv div.tdDiv div.blank {
|
||||||
|
background-color: #ffffff;
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.displayNone {
|
||||||
|
display: none!important;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
|
@ -0,0 +1,210 @@
|
||||||
|
<template>
|
||||||
|
<a-spin :spinning="confirmLoading">
|
||||||
|
<JFormContainer :disabled="disabled">
|
||||||
|
<template #detail>
|
||||||
|
<a-form ref="formRef" class="antd-modal-form" :labelCol="labelCol" :wrapperCol="wrapperCol" name="NuIotCameraUploadForm">
|
||||||
|
<a-row>
|
||||||
|
<a-col :span="24">
|
||||||
|
<a-form-item label="录像类型" v-bind="validateInfos.videoType" id="NuIotCameraUploadForm-videoType" name="videoType">
|
||||||
|
<a-select v-model:value="formData.videoType" placeholder="请选择设备状态">
|
||||||
|
<a-select-option value="1">定时录像</a-select-option>
|
||||||
|
<a-select-option value="2">移动侦测录像</a-select-option>
|
||||||
|
<a-select-option value="3">全部</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="24">
|
||||||
|
<a-form-item label="开始时间" v-bind="validateInfos.startTime" id="NuIotCameraUploadForm-startTime" name="startTime">
|
||||||
|
<a-date-picker show-time placeholder="请选择开始时间" v-model:value="formData.startTime"/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="24">
|
||||||
|
<a-form-item label="结束时间" v-bind="validateInfos.endTime" id="NuIotCameraUploadForm-endTime" name="endTime">
|
||||||
|
<a-date-picker show-time placeholder="请选择结束时间" v-model:value="formData.endTime"/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="24">
|
||||||
|
<a-form-item label="文件名称" id="NuIotCameraUploadForm-fileName" name="fileName">
|
||||||
|
<a-input v-model:value="formData.fileName" placeholder="请输入文件名称" allow-clear ></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="24" style="text-align: center">
|
||||||
|
<a-button type="primary" preIcon="ant-design:cloud-upload-outlined" @click="uploadSummit" v-show="canUpload">回放上传</a-button>
|
||||||
|
<a-button type="primary" preIcon="ant-design:disconnect-outlined" @click="uploadStop" v-show="!canUpload">停止上传</a-button>
|
||||||
|
<!-- <a-button type="primary" preIcon="ant-design:search-outlined" @click="getProgress">查询进度</a-button>-->
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-form>
|
||||||
|
<a-row style="margin-top: 50px">
|
||||||
|
<a-col :span="2">
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="20">
|
||||||
|
<a-progress :percent="percentRate" v-if="showPercent"/>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</template>
|
||||||
|
</JFormContainer>
|
||||||
|
</a-spin>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import {
|
||||||
|
ref,
|
||||||
|
reactive,
|
||||||
|
defineExpose,
|
||||||
|
nextTick,
|
||||||
|
defineProps,
|
||||||
|
computed,
|
||||||
|
onMounted,
|
||||||
|
watch,
|
||||||
|
unref
|
||||||
|
} from 'vue';
|
||||||
|
import { defHttp } from '/@/utils/http/axios';
|
||||||
|
import { useMessage } from '/@/hooks/web/useMessage';
|
||||||
|
import JDictSelectTag from '/@/components/Form/src/jeecg/components/JDictSelectTag.vue';
|
||||||
|
import { getValueType } from '/@/utils';
|
||||||
|
import { Form } from 'ant-design-vue';
|
||||||
|
import JFormContainer from '/@/components/Form/src/container/JFormContainer.vue';
|
||||||
|
import {uploadToServer,stopUploadToServer,getUploadToServerProcess} from "@/views/iot/tplink/camera/camera.api";
|
||||||
|
const props = defineProps({
|
||||||
|
data: { type: Object, default: () => ({}) },
|
||||||
|
});
|
||||||
|
const formRef = ref();
|
||||||
|
const useForm = Form.useForm;
|
||||||
|
const emit = defineEmits(['register', 'ok']);
|
||||||
|
const formData = reactive<Record<string, any>>({
|
||||||
|
deviceIndex: '',
|
||||||
|
parentId: '',
|
||||||
|
videoType: '',
|
||||||
|
storageType: '0',
|
||||||
|
fileName: '',
|
||||||
|
startTime: '',
|
||||||
|
endTime: '',
|
||||||
|
taskId: '',
|
||||||
|
});
|
||||||
|
const percentRate = ref(0);
|
||||||
|
const showPercent = ref(false);
|
||||||
|
const timerRef = ref();
|
||||||
|
const canUpload = ref(true);
|
||||||
|
const { createMessage } = useMessage();
|
||||||
|
const labelCol = ref<any>({ xs: { span: 24 }, sm: { span: 5 } });
|
||||||
|
const wrapperCol = ref<any>({ xs: { span: 24 }, sm: { span: 16 } });
|
||||||
|
const confirmLoading = ref<boolean>(false);
|
||||||
|
//表单验证
|
||||||
|
const validatorRules = {
|
||||||
|
videoType: [{ required: true, message: '请选择录像类型!' }],
|
||||||
|
startTime: [{ required: true, message: '请选择开始时间!' }],
|
||||||
|
endTime: [{ required: true, message: '请选择结束时间!' }],
|
||||||
|
};
|
||||||
|
const { resetFields, validate, validateInfos } = useForm(formData, validatorRules, { immediate: false });
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 上传提交
|
||||||
|
*/
|
||||||
|
async function uploadSummit(){
|
||||||
|
await validate();
|
||||||
|
let startTime = new Date(formData.startTime).getTime();
|
||||||
|
startTime = Math.floor(startTime/1000);
|
||||||
|
let endTime = new Date(formData.endTime).getTime();
|
||||||
|
endTime = Math.floor(endTime/1000);
|
||||||
|
let params = {
|
||||||
|
videoType : formData.videoType,
|
||||||
|
fileName : formData.fileName,
|
||||||
|
startTime: startTime,
|
||||||
|
endTime: endTime,
|
||||||
|
deviceIndex: formData.deviceIndex,
|
||||||
|
parentId: formData.parentId,
|
||||||
|
storageType: formData.storageType
|
||||||
|
}
|
||||||
|
confirmLoading.value=true;
|
||||||
|
await uploadToServer(params).then(res=>{
|
||||||
|
confirmLoading.value=false;
|
||||||
|
canUpload.value = false;
|
||||||
|
const taskId = res.taskId;
|
||||||
|
formData.taskId = taskId;
|
||||||
|
percentRate.value = 0;
|
||||||
|
showPercent.value = true;
|
||||||
|
timerRef.value = setInterval(()=>{
|
||||||
|
uploadProgress()
|
||||||
|
},3000 );
|
||||||
|
}).catch(res=>{
|
||||||
|
console.log(res);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 上传暂定
|
||||||
|
*/
|
||||||
|
function uploadStop(){
|
||||||
|
let params = {
|
||||||
|
taskId : formData.taskId
|
||||||
|
}
|
||||||
|
stopUploadToServer(params).then(res=>{
|
||||||
|
clearInterval(timerRef.value);
|
||||||
|
canUpload.value = true;
|
||||||
|
}).catch(res=>{
|
||||||
|
console.log(res);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// function getProgress(){
|
||||||
|
// let params = {
|
||||||
|
// taskId : formData.fileName
|
||||||
|
// }
|
||||||
|
// getUploadToServerProcess(params).then(res=>{
|
||||||
|
// const process = res.process;
|
||||||
|
// percentRate.value = Math.round(process/10);
|
||||||
|
// }).catch(res=>{
|
||||||
|
// console.log(res);
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 上传进度
|
||||||
|
*/
|
||||||
|
function uploadProgress(){
|
||||||
|
if(showPercent.value){
|
||||||
|
let params = {
|
||||||
|
taskId : formData.taskId
|
||||||
|
}
|
||||||
|
getUploadToServerProcess(params).then(res=>{
|
||||||
|
const process = res.process;
|
||||||
|
percentRate.value = Math.round(process / 10);
|
||||||
|
if (percentRate.value >= 100) {
|
||||||
|
clearInterval(timerRef.value);
|
||||||
|
canUpload.value = true;
|
||||||
|
}
|
||||||
|
}).catch(res=>{
|
||||||
|
console.log(res);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 销毁
|
||||||
|
*/
|
||||||
|
function destroy(){
|
||||||
|
clearInterval(timerRef.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
watch(
|
||||||
|
() => props.data,
|
||||||
|
async () => {
|
||||||
|
formData.deviceIndex = props.data.deviceIndex;
|
||||||
|
formData.projectId = props.data.projectId;
|
||||||
|
formData.regionId = props.data.regionId;
|
||||||
|
formData.multitrans = props.data.multitrans;
|
||||||
|
formData.scale = props.data.scale;
|
||||||
|
console.log(formData);
|
||||||
|
},
|
||||||
|
{ deep: true, immediate: true }
|
||||||
|
);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.antd-modal-form {
|
||||||
|
padding: 14px;
|
||||||
|
}
|
||||||
|
</style>
|
Binary file not shown.
After Width: | Height: | Size: 2.8 KiB |
|
@ -0,0 +1,47 @@
|
||||||
|
<template>
|
||||||
|
|
||||||
|
<a-row class="p-2" type="flex" :gutter="10">
|
||||||
|
<a-col :xl="4" :lg="24" :md="24" style="margin-bottom: 10px">
|
||||||
|
<CameraLeftTree ref="leftTree" @select="onTreeSelect" @rootTreeData="onRootTreeData" />
|
||||||
|
</a-col>
|
||||||
|
<a-col :xl="20" :lg="24" :md="24" style="margin-bottom: 10px">
|
||||||
|
<div v-show="cameraData != null">
|
||||||
|
<CameraInfoList :data="cameraData" />
|
||||||
|
</div>
|
||||||
|
<div v-show="cameraData == null" style="padding-top: 40px">
|
||||||
|
<a-empty description="请选择区域" />
|
||||||
|
</div>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" name="iot-nuIotCameraInfo" setup>
|
||||||
|
import { ref } from 'vue';
|
||||||
|
import { useDesign } from '/@/hooks/web/useDesign';
|
||||||
|
import CameraLeftTree from './components/CameraLeftTree.vue'
|
||||||
|
import CameraInfoList from "@/views/iot/tplink/camera/components/CameraInfoList.vue";
|
||||||
|
|
||||||
|
// 给子组件定义一个ref变量
|
||||||
|
const leftTree = ref();
|
||||||
|
// 当前选中的区域信息
|
||||||
|
const cameraData = ref({});
|
||||||
|
const rootTreeData = ref<any[]>([]);
|
||||||
|
// 左侧树选择后触发
|
||||||
|
function onTreeSelect(data) {
|
||||||
|
console.log('onTreeSelect: ', data);
|
||||||
|
cameraData.value = data;
|
||||||
|
}
|
||||||
|
// 左侧树rootTreeData触发
|
||||||
|
function onRootTreeData(data) {
|
||||||
|
rootTreeData.value = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
|
||||||
|
.p-2{
|
||||||
|
height: calc(100vh - 120px);
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
|
@ -0,0 +1,18 @@
|
||||||
|
import { defHttp } from '/@/utils/http/axios';
|
||||||
|
|
||||||
|
enum Api {
|
||||||
|
list = '/iot/projectInfo/list',
|
||||||
|
sync = '/iot/projectInfo/sync',
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 列表接口
|
||||||
|
* @param params
|
||||||
|
*/
|
||||||
|
export const list = (params) => defHttp.get({ url: Api.list, params });
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 项目信息同步
|
||||||
|
* @param params
|
||||||
|
*/
|
||||||
|
export const sync = (params) => defHttp.get({ url: Api.sync, params });
|
|
@ -0,0 +1,130 @@
|
||||||
|
import {BasicColumn} from '/@/components/Table';
|
||||||
|
import {FormSchema} from '/@/components/Table';
|
||||||
|
|
||||||
|
//列表数据
|
||||||
|
export const columns: BasicColumn[] = [
|
||||||
|
{
|
||||||
|
title: '项目序号',
|
||||||
|
align: "center",
|
||||||
|
dataIndex: 'projectId'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '项目名称',
|
||||||
|
align: "center",
|
||||||
|
dataIndex: 'projectName'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '创建时间',
|
||||||
|
align: "center",
|
||||||
|
dataIndex: 'createTimeStr'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '设备数量',
|
||||||
|
align: "center",
|
||||||
|
dataIndex: 'deviceNum'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '离线设备数',
|
||||||
|
align: "center",
|
||||||
|
dataIndex: 'offlineNum'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '异常设备数',
|
||||||
|
align: "center",
|
||||||
|
dataIndex: 'abnormalNum'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '运行天数',
|
||||||
|
align: "center",
|
||||||
|
dataIndex: 'runningTimeStr'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '状态',
|
||||||
|
align: "center",
|
||||||
|
dataIndex: 'status',
|
||||||
|
customRender:({record})=>{
|
||||||
|
return record.status?(record.status=='1'?'正常':'冻结'):'';
|
||||||
|
},
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
export const searchFormSchema: FormSchema[] = [
|
||||||
|
{
|
||||||
|
label: '名称',
|
||||||
|
field: 'projectName',
|
||||||
|
component: 'Input',
|
||||||
|
//colProps: { span: 6 },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '状态',
|
||||||
|
field: 'status',
|
||||||
|
component: 'JDictSelectTag',
|
||||||
|
componentProps: {
|
||||||
|
placeholder: '请选择状态',
|
||||||
|
options: [
|
||||||
|
{ label: '正常', value: '1' },
|
||||||
|
{ label: '冻结', value: '2' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
//colProps: { span: 6 },
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
export const formSchema: FormSchema[] = [
|
||||||
|
{
|
||||||
|
label: '',
|
||||||
|
field: 'id',
|
||||||
|
component: 'Input',
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '项目序号',
|
||||||
|
field: 'projectId',
|
||||||
|
component: 'Input',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '项目名称',
|
||||||
|
field: 'projectName',
|
||||||
|
component: 'Input',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '创建时间',
|
||||||
|
field: 'createTimeStr',
|
||||||
|
component: 'Input',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '设备数量',
|
||||||
|
field: 'deviceNum',
|
||||||
|
component: 'Input',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '离线设备数',
|
||||||
|
field: 'offlineNum',
|
||||||
|
component: 'Input',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '异常设备数',
|
||||||
|
field: 'abnormalNum',
|
||||||
|
component: 'Input',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '运行天数',
|
||||||
|
field: 'runningTimeStr',
|
||||||
|
component: 'Input',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '状态',
|
||||||
|
field: 'status',
|
||||||
|
component: 'JDictSelectTag',
|
||||||
|
defaultValue: 1,
|
||||||
|
componentProps: ({}) => {
|
||||||
|
return {
|
||||||
|
options: [
|
||||||
|
{ label: '正常', value: 1, key: '1' },
|
||||||
|
{ label: '冻结', value: 2, key: '2' },
|
||||||
|
]
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
|
@ -0,0 +1,153 @@
|
||||||
|
<template>
|
||||||
|
<a-spin :spinning="syncoading">
|
||||||
|
<div class="p-2">
|
||||||
|
<!--引用表格-->
|
||||||
|
<BasicTable @register="registerTable">
|
||||||
|
<!--插槽:table标题-->
|
||||||
|
<template #tableTitle>
|
||||||
|
<a-button type="primary" preIcon="ant-design:sync-outlined" @click="handleSync"> 同步</a-button>
|
||||||
|
</template>
|
||||||
|
<!--操作栏-->
|
||||||
|
<template #action="{ record }">
|
||||||
|
<TableAction :actions="getTableAction(record)"/>
|
||||||
|
</template>
|
||||||
|
<template v-slot:bodyCell="{ column, record, index, text }">
|
||||||
|
</template>
|
||||||
|
</BasicTable>
|
||||||
|
<!-- 表单区域 -->
|
||||||
|
<ProjectInfoDrawer @register="registerDrawer" @success="handleSuccess" />
|
||||||
|
</div>
|
||||||
|
</a-spin>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" name="iot-nuIotCameraInfo" setup>
|
||||||
|
import {ref, reactive, createVNode, h} from 'vue';
|
||||||
|
import { BasicTable, useTable, TableAction } from '/@/components/Table';
|
||||||
|
import { useListPage } from '/@/hooks/system/useListPage';
|
||||||
|
import { columns,searchFormSchema } from './ProjectInfo.data';
|
||||||
|
import { list, sync } from './ProjectInfo.api';
|
||||||
|
import { useUserStore } from '/@/store/modules/user';
|
||||||
|
import { useRouter } from 'vue-router';
|
||||||
|
import { useDrawer } from "@/components/Drawer";
|
||||||
|
import ProjectInfoDrawer from './components/ProjectInfoDrawer.vue';
|
||||||
|
//注册drawer
|
||||||
|
const [registerDrawer, { openDrawer }] = useDrawer();
|
||||||
|
let router = useRouter();
|
||||||
|
const formRef = ref();
|
||||||
|
const syncoading = ref<boolean>(false);
|
||||||
|
const queryParam = reactive<any>({});
|
||||||
|
const userStore = useUserStore();
|
||||||
|
//注册table数据
|
||||||
|
const { prefixCls, tableContext, onExportXls, onImportXls } = useListPage({
|
||||||
|
tableProps: {
|
||||||
|
title: '护理单元-物联管理-项目信息',
|
||||||
|
api: list,
|
||||||
|
columns,
|
||||||
|
canResize:false,
|
||||||
|
formConfig: {
|
||||||
|
// labelWidth: 200,
|
||||||
|
schemas: searchFormSchema,
|
||||||
|
},
|
||||||
|
actionColumn: {
|
||||||
|
width: 120,
|
||||||
|
fixed: 'right',
|
||||||
|
},
|
||||||
|
beforeFetch: async (params) => {
|
||||||
|
return Object.assign(params, queryParam);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const [registerTable, { reload, collapseAll, updateTableDataRecord, findTableDataRecord, getDataSource }, { rowSelection, selectedRowKeys }] = tableContext;
|
||||||
|
const labelCol = reactive({
|
||||||
|
xs:24,
|
||||||
|
sm:4,
|
||||||
|
xl:6,
|
||||||
|
xxl:4
|
||||||
|
});
|
||||||
|
const wrapperCol = reactive({
|
||||||
|
xs: 24,
|
||||||
|
sm: 20,
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 详情
|
||||||
|
*/
|
||||||
|
function handleDetail(record: Recordable) {
|
||||||
|
openDrawer(true, {
|
||||||
|
record,
|
||||||
|
isUpdate: true,
|
||||||
|
showFooter: false,
|
||||||
|
tenantSaas: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 操作栏
|
||||||
|
*/
|
||||||
|
function getTableAction(record) {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
label: '详情',
|
||||||
|
onClick: handleDetail.bind(null, record),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询
|
||||||
|
*/
|
||||||
|
function searchQuery() {
|
||||||
|
reload();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 重置
|
||||||
|
*/
|
||||||
|
function searchReset() {
|
||||||
|
formRef.value.resetFields();
|
||||||
|
selectedRowKeys.value = [];
|
||||||
|
//刷新数据
|
||||||
|
reload();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 信息同步
|
||||||
|
*/
|
||||||
|
function handleSync(){
|
||||||
|
syncoading.value = true;
|
||||||
|
sync({}).then(res=>{
|
||||||
|
syncoading.value = false;
|
||||||
|
//刷新数据
|
||||||
|
reload();
|
||||||
|
}).catch(res=>{
|
||||||
|
syncoading.value = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.jeecg-basic-table-form-container {
|
||||||
|
padding: 0;
|
||||||
|
.table-page-search-submitButtons {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
.query-group-cust{
|
||||||
|
min-width: 100px !important;
|
||||||
|
}
|
||||||
|
.query-group-split-cust{
|
||||||
|
width: 30px;
|
||||||
|
display: inline-block;
|
||||||
|
text-align: center
|
||||||
|
}
|
||||||
|
.ant-form-item:not(.ant-form-item-with-help){
|
||||||
|
margin-bottom: 16px;
|
||||||
|
height: 32px;
|
||||||
|
}
|
||||||
|
:deep(.ant-picker),:deep(.ant-input-number){
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,77 @@
|
||||||
|
<template>
|
||||||
|
<BasicDrawer
|
||||||
|
v-bind="$attrs"
|
||||||
|
@register="registerDrawer"
|
||||||
|
:title="getTitle"
|
||||||
|
:width="adaptiveWidth"
|
||||||
|
@ok="handleSubmit"
|
||||||
|
:showFooter="showFooter"
|
||||||
|
destroyOnClose
|
||||||
|
>
|
||||||
|
<BasicForm @register="registerForm" />
|
||||||
|
</BasicDrawer>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { defineComponent, ref, computed, unref, useAttrs } from 'vue';
|
||||||
|
import { BasicForm, useForm } from '/@/components/Form/index';
|
||||||
|
import { formSchema } from "@/views/iot/tplink/project/ProjectInfo.data";
|
||||||
|
import { BasicDrawer, useDrawerInner } from '/@/components/Drawer';
|
||||||
|
import { useDrawerAdaptiveWidth } from '/@/hooks/jeecg/useAdaptiveWidth';
|
||||||
|
import { getTenantId } from "/@/utils/auth";
|
||||||
|
|
||||||
|
// 声明Emits
|
||||||
|
const emit = defineEmits(['success', 'register']);
|
||||||
|
const attrs = useAttrs();
|
||||||
|
const isUpdate = ref(true);
|
||||||
|
const rowId = ref('');
|
||||||
|
const departOptions = ref([]);
|
||||||
|
let isFormDepartUser = false;
|
||||||
|
//表单配置
|
||||||
|
const [registerForm, { setProps, resetFields, setFieldsValue, validate, updateSchema }] = useForm({
|
||||||
|
labelWidth: 90,
|
||||||
|
schemas: formSchema,
|
||||||
|
showActionButtonGroup: false,
|
||||||
|
});
|
||||||
|
const showFooter = ref(true);
|
||||||
|
//表单赋值
|
||||||
|
const [registerDrawer, { setDrawerProps, closeDrawer }] = useDrawerInner(async (data) => {
|
||||||
|
await resetFields();
|
||||||
|
showFooter.value = data?.showFooter ?? true;
|
||||||
|
setDrawerProps({ confirmLoading: false, showFooter: showFooter.value });
|
||||||
|
isUpdate.value = !!data?.isUpdate;
|
||||||
|
// 无论新增还是编辑,都可以设置表单值
|
||||||
|
if (typeof data.record === 'object') {
|
||||||
|
setFieldsValue({
|
||||||
|
...data.record,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// 隐藏底部时禁用整个表单
|
||||||
|
setProps({ disabled: !showFooter.value });
|
||||||
|
});
|
||||||
|
//获取标题
|
||||||
|
const getTitle = computed(() => {
|
||||||
|
if (!unref(isUpdate)) {
|
||||||
|
return '新增项目';
|
||||||
|
} else {
|
||||||
|
return unref(showFooter) ? '编辑项目' : '项目详情';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const { adaptiveWidth } = useDrawerAdaptiveWidth();
|
||||||
|
|
||||||
|
//提交事件
|
||||||
|
async function handleSubmit() {
|
||||||
|
try {
|
||||||
|
//关闭弹窗
|
||||||
|
closeDrawer();
|
||||||
|
//刷新列表
|
||||||
|
emit('success');
|
||||||
|
} finally {
|
||||||
|
setDrawerProps({ confirmLoading: false });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
|
@ -0,0 +1,18 @@
|
||||||
|
import { defHttp } from '/@/utils/http/axios';
|
||||||
|
|
||||||
|
enum Api {
|
||||||
|
list = '/iot/regionInfo/list',
|
||||||
|
sync = '/iot/regionInfo/sync',
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 列表接口
|
||||||
|
* @param params
|
||||||
|
*/
|
||||||
|
export const list = (params) => defHttp.get({ url: Api.list, params });
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 项目信息同步
|
||||||
|
* @param params
|
||||||
|
*/
|
||||||
|
export const sync = (params) => defHttp.get({ url: Api.sync, params });
|
|
@ -0,0 +1,108 @@
|
||||||
|
import {BasicColumn} from '/@/components/Table';
|
||||||
|
import {FormSchema} from '/@/components/Table';
|
||||||
|
|
||||||
|
//列表数据
|
||||||
|
export const columns: BasicColumn[] = [
|
||||||
|
{
|
||||||
|
title: '区域序号',
|
||||||
|
align: "center",
|
||||||
|
dataIndex: 'regionId'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '区域名称',
|
||||||
|
align: "center",
|
||||||
|
dataIndex: 'regionName'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '区域层级',
|
||||||
|
align: "center",
|
||||||
|
dataIndex: 'regionLevel'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '项目名称',
|
||||||
|
align: "center",
|
||||||
|
dataIndex: 'projectName'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '更新时间',
|
||||||
|
align: "center",
|
||||||
|
dataIndex: 'updateTime'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '区域次序',
|
||||||
|
align: "center",
|
||||||
|
dataIndex: 'sort'
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export const searchFormSchema: FormSchema[] = [
|
||||||
|
{
|
||||||
|
label: '项目',
|
||||||
|
field: 'projectId',
|
||||||
|
component: 'JDictSelectTag',
|
||||||
|
componentProps: {
|
||||||
|
dictCode: 'nu_iot_tplink_project,project_name,project_id',
|
||||||
|
placeholder: '请选择项目',
|
||||||
|
},
|
||||||
|
//colProps: { span: 6 },
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export const formSchema: FormSchema[] = [
|
||||||
|
{
|
||||||
|
label: '',
|
||||||
|
field: 'id',
|
||||||
|
component: 'Input',
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '项目序号',
|
||||||
|
field: 'projectId',
|
||||||
|
component: 'Input'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '项目名称',
|
||||||
|
field: 'projectName',
|
||||||
|
component: 'Input',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '创建时间',
|
||||||
|
field: 'createTimeStr',
|
||||||
|
component: 'Input',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '设备数量',
|
||||||
|
field: 'deviceNum',
|
||||||
|
component: 'Input',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '离线设备数',
|
||||||
|
field: 'offlineNum',
|
||||||
|
component: 'Input',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '异常设备数',
|
||||||
|
field: 'abnormalNum',
|
||||||
|
component: 'Input',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '运行天数',
|
||||||
|
field: 'runningTimeStr',
|
||||||
|
component: 'Input',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '状态',
|
||||||
|
field: 'status',
|
||||||
|
component: 'JDictSelectTag',
|
||||||
|
defaultValue: 1,
|
||||||
|
componentProps: ({}) => {
|
||||||
|
return {
|
||||||
|
options: [
|
||||||
|
{ label: '正常', value: 1, key: '1' },
|
||||||
|
{ label: '冻结', value: 2, key: '2' },
|
||||||
|
]
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
|
@ -0,0 +1,161 @@
|
||||||
|
<template>
|
||||||
|
<a-spin :spinning="syncoading">
|
||||||
|
<div class="p-2">
|
||||||
|
<!--引用表格-->
|
||||||
|
<BasicTable @register="registerTable">
|
||||||
|
<!--插槽:table标题-->
|
||||||
|
<template #tableTitle>
|
||||||
|
<a-button type="primary" preIcon="ant-design:sync-outlined" @click="handleSync"> 同步</a-button>
|
||||||
|
</template>
|
||||||
|
<!--操作栏-->
|
||||||
|
<template #action="{ record }">
|
||||||
|
<TableAction :actions="getTableAction(record)"/>
|
||||||
|
</template>
|
||||||
|
<template v-slot:bodyCell="{ column, record, index, text }">
|
||||||
|
</template>
|
||||||
|
</BasicTable>
|
||||||
|
<!-- 表单区域 -->
|
||||||
|
</div>
|
||||||
|
</a-spin>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" name="iot-nuIotCameraInfo" setup>
|
||||||
|
import {ref, reactive, createVNode, h} from 'vue';
|
||||||
|
import { BasicTable, useTable, TableAction } from '/@/components/Table';
|
||||||
|
import { useListPage } from '/@/hooks/system/useListPage';
|
||||||
|
import { columns,searchFormSchema } from './RegionInfo.data';
|
||||||
|
import { list, sync } from './RegionInfo.api';
|
||||||
|
import { useUserStore } from '/@/store/modules/user';
|
||||||
|
import { useRouter } from 'vue-router';
|
||||||
|
import { useDrawer } from "@/components/Drawer";
|
||||||
|
import { useMessage } from "@/hooks/web/useMessage";
|
||||||
|
const { createMessage } = useMessage();
|
||||||
|
//注册drawer
|
||||||
|
const [registerDrawer, { openDrawer }] = useDrawer();
|
||||||
|
let router = useRouter();
|
||||||
|
const formRef = ref();
|
||||||
|
const syncoading = ref<boolean>(false);
|
||||||
|
const queryParam = reactive<any>({});
|
||||||
|
const userStore = useUserStore();
|
||||||
|
//注册table数据
|
||||||
|
const { prefixCls, tableContext, onExportXls, onImportXls } = useListPage({
|
||||||
|
tableProps: {
|
||||||
|
title: '护理单元-物联管理-区域信息',
|
||||||
|
api: list,
|
||||||
|
columns,
|
||||||
|
canResize:false,
|
||||||
|
// 是否显示操作列
|
||||||
|
showActionColumn: false,
|
||||||
|
formConfig: {
|
||||||
|
// labelWidth: 200,
|
||||||
|
schemas: searchFormSchema,
|
||||||
|
},
|
||||||
|
actionColumn: {
|
||||||
|
width: 120,
|
||||||
|
fixed: 'right',
|
||||||
|
},
|
||||||
|
beforeFetch: async (params) => {
|
||||||
|
return Object.assign(params, queryParam);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const [registerTable, { reload, getForm, collapseAll, updateTableDataRecord, findTableDataRecord, getDataSource }, { rowSelection, selectedRowKeys }] = tableContext;
|
||||||
|
const labelCol = reactive({
|
||||||
|
xs:24,
|
||||||
|
sm:4,
|
||||||
|
xl:6,
|
||||||
|
xxl:4
|
||||||
|
});
|
||||||
|
const wrapperCol = reactive({
|
||||||
|
xs: 24,
|
||||||
|
sm: 20,
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 详情
|
||||||
|
*/
|
||||||
|
function handleDetail(record: Recordable) {
|
||||||
|
openDrawer(true, {
|
||||||
|
record,
|
||||||
|
isUpdate: true,
|
||||||
|
showFooter: false,
|
||||||
|
tenantSaas: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 操作栏
|
||||||
|
*/
|
||||||
|
function getTableAction(record) {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
label: '详情',
|
||||||
|
onClick: handleDetail.bind(null, record),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询
|
||||||
|
*/
|
||||||
|
function searchQuery() {
|
||||||
|
reload();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 重置
|
||||||
|
*/
|
||||||
|
function searchReset() {
|
||||||
|
formRef.value.resetFields();
|
||||||
|
selectedRowKeys.value = [];
|
||||||
|
//刷新数据
|
||||||
|
reload();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 信息同步
|
||||||
|
*/
|
||||||
|
function handleSync(){
|
||||||
|
let { getFieldsValue } = getForm();
|
||||||
|
let params = getFieldsValue();
|
||||||
|
if(params.projectId==null||params.projectId==''){
|
||||||
|
createMessage.error("请先择一个项目");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
syncoading.value = true;
|
||||||
|
sync(params).then(res=>{
|
||||||
|
syncoading.value = false;
|
||||||
|
//刷新数据
|
||||||
|
reload();
|
||||||
|
}).catch(res=>{
|
||||||
|
syncoading.value = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.jeecg-basic-table-form-container {
|
||||||
|
padding: 0;
|
||||||
|
.table-page-search-submitButtons {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
.query-group-cust{
|
||||||
|
min-width: 100px !important;
|
||||||
|
}
|
||||||
|
.query-group-split-cust{
|
||||||
|
width: 30px;
|
||||||
|
display: inline-block;
|
||||||
|
text-align: center
|
||||||
|
}
|
||||||
|
.ant-form-item:not(.ant-form-item-with-help){
|
||||||
|
margin-bottom: 16px;
|
||||||
|
height: 32px;
|
||||||
|
}
|
||||||
|
:deep(.ant-picker),:deep(.ant-input-number){
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
Loading…
Reference in New Issue