Compare commits
11 Commits
092d1d1505
...
develop-zm
| Author | SHA1 | Date | |
|---|---|---|---|
| 3a264bc1ab | |||
| ecf0e4add9 | |||
| b6bfb92c6e | |||
| a8b781d48a | |||
| 0bd3629190 | |||
| d303a04986 | |||
| 5a5752b113 | |||
| fdefb92540 | |||
|
|
6de279cc85 | ||
| afe078dd1d | |||
| 5f52ac4956 |
@@ -3,11 +3,8 @@ NODE_ENV = 'development'
|
||||
# 渔船点位
|
||||
VITE_WS_BASE_URL ='ws://220.185.188.222:8055/api/gisWs'
|
||||
|
||||
# VITE_APP_BASE_URL = 'http://125.124.131.105:6811/api'
|
||||
# 成彬本地
|
||||
# VITE_APP_BASE_URL = 'http://100.95.157.241:6061/api'
|
||||
VITE_APP_BASE_URL = 'http://100.95.236.218:6061/api'
|
||||
# VITE_APP_BASE_URL = 'http://119.167.138.11:6061/video-service'
|
||||
VITE_APP_BASE_URL = 'http://100.95.196.8:6061/api'
|
||||
|
||||
# 智能体访问地址
|
||||
# 宋凯忠本地
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# 生产环境
|
||||
NODE_ENV = 'production'
|
||||
# 渔船点位
|
||||
VITE_WS_BASE_URL ='ws://100.95.225.221:6810/api/gisWs'
|
||||
VITE_WS_BASE_URL ='ws://198.16.74.211:6060/video-service/SowWs/123'
|
||||
|
||||
VITE_APP_BASE_URL = 'http://198.16.74.211:7284/api'
|
||||
# 智能体访问地址
|
||||
|
||||
Binary file not shown.
@@ -6,14 +6,50 @@ import request from '@/utils/request'
|
||||
* @param data
|
||||
* @returns {*}
|
||||
*/
|
||||
export const videoCameraFindPage = (data) => {
|
||||
export const dsVideoPage = (data) => {
|
||||
return request({
|
||||
url: '/videoCamera/findPage',
|
||||
url: '/fishingPort/dsVideo/page',
|
||||
method: 'post',
|
||||
data: data,
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded'
|
||||
}
|
||||
data: data
|
||||
})
|
||||
}
|
||||
/**
|
||||
* 分页查询监控设备列表
|
||||
* 新增
|
||||
* @param data
|
||||
* @returns {*}
|
||||
*/
|
||||
export const dsVideoSave = (data) => {
|
||||
return request({
|
||||
url: '/fishingPort/dsVideo/save',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
/**
|
||||
* 分页查询监控设备列表
|
||||
* 修改
|
||||
* @param data
|
||||
* @returns {*}
|
||||
*/
|
||||
export const dsVideoUpdate = (data) => {
|
||||
return request({
|
||||
url: '/fishingPort/dsVideo/update',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
/**
|
||||
* 分页查询监控设备列表
|
||||
* 删除
|
||||
* @param data
|
||||
* @returns {*}
|
||||
*/
|
||||
export const dsVideoDelete = (data) => {
|
||||
return request({
|
||||
url: '/fishingPort/dsVideo/delete',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
/**
|
||||
@@ -73,3 +109,20 @@ export const findEnvPage = (data) => {
|
||||
data: data
|
||||
})
|
||||
}
|
||||
// 视频监控详情信息
|
||||
export const getVideoInfo = (data) => {
|
||||
return request({
|
||||
url: '/fishingPort/dsVideo/getVideoInfo',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
// 巡检报告导出
|
||||
export const cctvExport = (data) => {
|
||||
return request({
|
||||
url: '/pollExport/cctvExport',
|
||||
method: 'post',
|
||||
data: data,
|
||||
responseType: 'blob'
|
||||
})
|
||||
}
|
||||
BIN
src/assets/images/common/icon_suffix.png
Normal file
BIN
src/assets/images/common/icon_suffix.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 204 B |
BIN
src/assets/images/map/devices/icon_monitor_bayonet.png
Normal file
BIN
src/assets/images/map/devices/icon_monitor_bayonet.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.0 KiB |
@@ -1,110 +0,0 @@
|
||||
<template>
|
||||
<!-- flv视频流播放 -->
|
||||
<div>
|
||||
<video
|
||||
v-if="show"
|
||||
id="videoElement"
|
||||
:controls="true"
|
||||
autoplay
|
||||
style="width: 100%; height: 100%; object-fit: fill;">
|
||||
</video>
|
||||
<el-empty v-else :image="getAssetsFile('lbtmenu/icon-UAV-inner.png')" description="无人机暂未起飞" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {
|
||||
ref, defineExpose, nextTick
|
||||
} from 'vue'
|
||||
import flvjs from 'flv.js'
|
||||
import { getAssetsFile } from '@/utils/common.js'
|
||||
|
||||
const emit = defineEmits([ 'toggleShow' ])
|
||||
let flvPlayer = null
|
||||
const show = ref(false)
|
||||
const clear = () => {
|
||||
if (flvPlayer) {
|
||||
flvPlayer.unload()
|
||||
flvPlayer.detachMediaElement()
|
||||
flvPlayer = null
|
||||
}
|
||||
}
|
||||
const onPlay = (url) => {
|
||||
show.value = true
|
||||
nextTick(() => {
|
||||
if (flvjs.isSupported()) {
|
||||
const videoElement = document.getElementById('videoElement')
|
||||
clear()
|
||||
flvPlayer = flvjs.createPlayer(
|
||||
{
|
||||
type: 'flv',
|
||||
isLive: true,
|
||||
hasAudio: false,
|
||||
url
|
||||
},
|
||||
{
|
||||
// 启用IO隐藏缓冲区
|
||||
// 如果需要实时(最小延迟)来进行实时流播放,则设置为false
|
||||
// 但是如果网络抖动,则可能会停顿
|
||||
enableStashBuffer: false,
|
||||
// 之时IO暂存缓冲区的初始大小,默认值为384kb,指出合适的尺寸可以改善视频负载/搜索时间
|
||||
stashInitialSize: 128
|
||||
}
|
||||
)
|
||||
flvPlayer.attachMediaElement(videoElement)
|
||||
flvPlayer.load()
|
||||
flvPlayer.play()
|
||||
emit('toggleShow', show.value)
|
||||
flvPlayer.on('error', () => {
|
||||
console.log('errorrrrrrrrrrrrrrrrrrrrrrrr')
|
||||
show.value = false
|
||||
flvPlayer.pause()
|
||||
if (flvPlayer) {
|
||||
flvPlayer = null
|
||||
}
|
||||
emit('toggleShow', show.value)
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
defineExpose({
|
||||
onPlay
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
:deep .el-empty__image{
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
}
|
||||
:deep .el-empty__description p{
|
||||
color:#fff;
|
||||
}
|
||||
/* 播放按钮 */
|
||||
video::-webkit-media-controls-play-button {
|
||||
display: none;
|
||||
}
|
||||
/* 进度条 */
|
||||
video::-webkit-media-controls-timeline {
|
||||
display: none;
|
||||
}
|
||||
/* 观看的当前时间 */
|
||||
video::-webkit-media-controls-current-time-display{
|
||||
display: none;
|
||||
}
|
||||
/* 剩余时间 */
|
||||
video::-webkit-media-controls-time-remaining-display {
|
||||
display: none;
|
||||
}
|
||||
/* 音量按钮 */
|
||||
video::-webkit-media-controls-mute-button {
|
||||
display: none;
|
||||
}
|
||||
video::-webkit-media-controls-toggle-closed-captions-button {
|
||||
display: none;
|
||||
}
|
||||
/* 音量的控制条 */
|
||||
video::-webkit-media-controls-volume-slider {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
@@ -1,332 +0,0 @@
|
||||
<template>
|
||||
|
||||
<!-- 绑定父组件class:videoWindow -->
|
||||
<div
|
||||
:id="playWndId"
|
||||
ref="video-preview">
|
||||
</div>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
/* eslint-disable */
|
||||
|
||||
export default {
|
||||
name: 'video-preview',
|
||||
data() {
|
||||
return {
|
||||
width: 0,
|
||||
height: 0,
|
||||
secret: '',
|
||||
oWebControl: null,
|
||||
initCount: 0,
|
||||
appkey: import.meta.env.VITE_APP_HAIKANG_APPKEY,
|
||||
ip: import.meta.env.VITE_APP_HAIKANG_IP,
|
||||
port: parseInt(import.meta.env.VITE_APP_HAIKANG_PORT),
|
||||
playMode: 1,
|
||||
pubKey: '',
|
||||
isInit: false
|
||||
}
|
||||
},
|
||||
props: {
|
||||
layout: {
|
||||
type: String,
|
||||
default: () => '1x1'
|
||||
},
|
||||
cameraIndexCode: {},
|
||||
timeOut: {
|
||||
type: Number,
|
||||
default: () => 500
|
||||
},
|
||||
videoWindowClassName: {
|
||||
type: String,
|
||||
default: "videoWindow",
|
||||
required: false
|
||||
},
|
||||
playWndId: {
|
||||
type: String,
|
||||
default: "playWnd",
|
||||
required: false
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
var that = this
|
||||
this.$nextTick(function() {
|
||||
that.windowChange()
|
||||
setTimeout(() => {
|
||||
that.initPlugin()
|
||||
}, that.timeOut)
|
||||
window.addEventListener('resize', function() {
|
||||
that.onResize()
|
||||
})
|
||||
})
|
||||
},
|
||||
destroyed() {
|
||||
this.uninit()
|
||||
},
|
||||
methods: {
|
||||
onResize(change=true){
|
||||
let that = this
|
||||
if (that.oWebControl) {
|
||||
if(change){
|
||||
that.windowChange()
|
||||
}
|
||||
that.oWebControl.JS_Resize(that.width, that.height)
|
||||
}
|
||||
},
|
||||
wait (fn, timeout, tick) {
|
||||
timeout = timeout || 5000;
|
||||
tick = tick || 250;
|
||||
var timeoutTimer = null;
|
||||
var execTimer = null;
|
||||
|
||||
return new Promise(function(resolve, reject) {
|
||||
|
||||
timeoutTimer = setTimeout(function() {
|
||||
clearTimeout(execTimer);
|
||||
reject(new Error('polling fail because timeout'));
|
||||
}, timeout);
|
||||
|
||||
tickHandler(fn);
|
||||
|
||||
function tickHandler(fn) {
|
||||
var ret = fn();
|
||||
if (!ret) {
|
||||
execTimer = setTimeout(function() {
|
||||
tickHandler(fn);
|
||||
}, tick)
|
||||
} else {
|
||||
clearTimeout(timeoutTimer);
|
||||
resolve();
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
// 初始化plugin
|
||||
initPlugin() {
|
||||
let that = this
|
||||
this.oWebControl = new WebControl({
|
||||
szPluginContainer: this.playWndId,
|
||||
iServicePortStart: 15900,
|
||||
iServicePortEnd: 15909,
|
||||
szClassId: '23BF3B0A-2C56-4D97-9C03-0CB103AA8F11', // 用于IE10使用ActiveX的clsid
|
||||
cbConnectSuccess: function() {
|
||||
that.setCallbacks();
|
||||
//实例创建成功后需要启动服务
|
||||
that.oWebControl
|
||||
.JS_StartService("window", {
|
||||
dllPath: "./VideoPluginConnect.dll"
|
||||
})
|
||||
.then(
|
||||
function() {
|
||||
that.oWebControl
|
||||
.JS_CreateWnd(that.playWndId, that.width, that.height)
|
||||
.then(function() {
|
||||
//JS_CreateWnd创建视频播放窗口,宽高可设定
|
||||
that.init(); //创建播放实例成功后初始化
|
||||
});
|
||||
},
|
||||
function() {}
|
||||
);
|
||||
},
|
||||
cbConnectError: function() {
|
||||
that.oWebControl = null;
|
||||
$("#" + that.playWndId).html("插件未启动,正在尝试启动,请稍候...");
|
||||
WebControl.JS_WakeUp("VideoWebPlugin://"); //程序未启动时执行error函数,采用wakeup来启动程序
|
||||
initCount++;
|
||||
if (initCount < 3) {
|
||||
setTimeout(function() {
|
||||
that.initPlugin();
|
||||
}, 2000);
|
||||
} else {
|
||||
$("#" + that.playWndId).html("插件启动失败,请检查插件是否安装!");
|
||||
}
|
||||
},
|
||||
cbConnectClose: function(bNormalClose) {
|
||||
that.oWebControl = null
|
||||
}
|
||||
})
|
||||
},
|
||||
init() {
|
||||
|
||||
let that = this;
|
||||
that.getPubKey(function() {
|
||||
////////////////////////////////// 请自行修改以下变量值 ////////////////////////////////////
|
||||
var snapDir = "d:\\SnapDir"; //抓图存储路径
|
||||
var videoDir = "d:\\VideoDir"; //紧急录像或录像剪辑存储路径
|
||||
var layout = "1x1"; //playMode指定模式的布局
|
||||
var enableHTTPS = 1; //是否启用HTTPS协议与综合安防管理平台交互,是为1,否为0
|
||||
var encryptedFields = "secret"; //加密字段,默认加密领域为secret
|
||||
var showToolbar = 1; //是否显示工具栏,0-不显示,非0-显示
|
||||
var showSmart = 1; //是否显示智能信息(如配置移动侦测后画面上的线框),0-不显示,非0-显示
|
||||
var buttonIDs = "0,16,256,257,258,259,260,512,515,516,517,768,769"; //自定义工具条按钮
|
||||
////////////////////////////////// 请自行修改以上变量值 ////////////////////////////////////
|
||||
that.secret = that.setEncrypt(import.meta.env.VITE_APP_HAIKANG_SECRET);
|
||||
that.oWebControl
|
||||
.JS_RequestInterface({
|
||||
funcName: "init",
|
||||
argument: JSON.stringify({
|
||||
appkey: that.appkey, //API网关提供的appkey
|
||||
secret: that.secret, //API网关提供的secret
|
||||
ip: that.ip, //API网关IP地址
|
||||
playMode: that.playMode, //播放模式(决定显示预览还是回放界面)
|
||||
port: that.port, //端口
|
||||
snapDir: snapDir, //抓图存储路径
|
||||
videoDir: videoDir, //紧急录像或录像剪辑存储路径
|
||||
layout: layout, //布局
|
||||
enableHTTPS: enableHTTPS, //是否启用HTTPS协议
|
||||
encryptedFields: encryptedFields, //加密字段
|
||||
showToolbar: showToolbar, //是否显示工具栏
|
||||
showSmart: showSmart, //是否显示智能信息
|
||||
buttonIDs: buttonIDs //自定义工具条按钮
|
||||
})
|
||||
})
|
||||
.then(function(oData) {
|
||||
that.oWebControl.JS_Resize(that.width, that.height); // 初始化后resize一次,规避firefox下首次显示窗口后插件窗口未与DIV窗口重合问题
|
||||
});
|
||||
});
|
||||
},
|
||||
// 获取公钥
|
||||
getPubKey(callback) {
|
||||
var that = this
|
||||
this.oWebControl.JS_RequestInterface({
|
||||
funcName: 'getRSAPubKey',
|
||||
argument: JSON.stringify({
|
||||
keyLength: 1024
|
||||
})
|
||||
}).then(function(oData) {
|
||||
if (oData.responseMsg.data) {
|
||||
that.pubKey = oData.responseMsg.data
|
||||
that.isInit = true
|
||||
callback()
|
||||
}
|
||||
})
|
||||
},
|
||||
// 设置窗口控制回调
|
||||
setCallbacks() {
|
||||
let that = this;
|
||||
that.oWebControl.JS_SetWindowControlCallback({
|
||||
cbIntegrationCallBack: function (oData) {
|
||||
if(oData.responseMsg.msg.result === 816){
|
||||
that.$emit('close')
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
// RSA加密
|
||||
setEncrypt(value) {
|
||||
let that = this;
|
||||
var encrypt = new JSEncrypt();
|
||||
encrypt.setPublicKey(that.pubKey);
|
||||
return encrypt.encrypt(value);
|
||||
},
|
||||
ready() {
|
||||
let that = this;
|
||||
return new Promise((resolve, reject) => {
|
||||
this.wait(
|
||||
function() {
|
||||
return that.isInit;
|
||||
},
|
||||
6000 + that.timeOut || 500,
|
||||
100
|
||||
)
|
||||
.then(function() {
|
||||
resolve();
|
||||
})
|
||||
.catch(function(err) {
|
||||
// that.$Message.info({
|
||||
// content: "视频控件加载超时,请检查:" + err,
|
||||
// duration: 3
|
||||
// });
|
||||
// reject("视频控件加载超时,请检查:" + err);
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
playBack(codes, startTime, endTime) {
|
||||
let that = this;
|
||||
const startTimeStamp = this.timrStr2Stamp(startTime) + "";
|
||||
const endTimeStamp = this.timrStr2Stamp(endTime) + "";
|
||||
var recordLocation = 0; //录像存储位置:0-中心存储,1-设备存储
|
||||
var transMode = 1; //传输协议:0-UDP,1-TCP
|
||||
var gpuMode = 0; //是否启用GPU硬解,0-不启用,1-启用
|
||||
var wndId = -1; //播放窗口序号(在2x2以上布局下可指定播放窗口)
|
||||
that.oWebControl
|
||||
.JS_RequestInterface({
|
||||
funcName: "startPlayback",
|
||||
argument: JSON.stringify({
|
||||
cameraIndexCode: codes, //监控点编号
|
||||
startTimeStamp: startTimeStamp, //录像查询开始时间戳,单位:秒
|
||||
endTimeStamp: endTimeStamp, //录像结束开始时间戳,单位:秒
|
||||
recordLocation: recordLocation, //录像存储类型:0-中心存储,1-设备存储
|
||||
transMode: transMode, //传输协议:0-UDP,1-TCP
|
||||
gpuMode: gpuMode, //是否启用GPU硬解,0-不启用,1-启用
|
||||
wndId: wndId //可指定播放窗口
|
||||
})
|
||||
})
|
||||
.then(res => {
|
||||
console.log("回放的参数", res);
|
||||
});
|
||||
},
|
||||
timrStr2Stamp(str) {
|
||||
let date = new Date(str);
|
||||
let time = date.getTime();
|
||||
return time / 1000;
|
||||
},
|
||||
stopPlayBack() {
|
||||
this.oWebControl.JS_RequestInterface({
|
||||
funcName: "stopAllPlayback"
|
||||
});
|
||||
},
|
||||
uninit() {
|
||||
console.log(999);
|
||||
|
||||
let that = this;
|
||||
if (that.oWebControl != null) {
|
||||
that.oWebControl.JS_RequestInterface({
|
||||
funcName: "stopAllPreview"
|
||||
});
|
||||
that.oWebControl.JS_HideWnd(); // 先让窗口隐藏,规避可能的插件窗口滞后于浏览器消失问题
|
||||
that.oWebControl.JS_Disconnect().then(
|
||||
function() {
|
||||
// 断开与插件服务连接成功
|
||||
},
|
||||
function() {
|
||||
// 断开与插件服务连接失败
|
||||
console.log("oWebControl close error");
|
||||
}
|
||||
);
|
||||
that.oWebControl = null;
|
||||
}
|
||||
},
|
||||
windowChange() {
|
||||
|
||||
//列表选项在下方
|
||||
this.width = document.getElementsByClassName(
|
||||
this.videoWindowClassName
|
||||
)[0].scrollWidth;
|
||||
var btnHeight = 0;
|
||||
if (document.getElementsByClassName("playBtn").length > 0) {
|
||||
btnHeight = document.getElementsByClassName("playBtn")[0].clientHeight;
|
||||
this.height =
|
||||
document.getElementsByClassName(this.videoWindowClassName)[0]
|
||||
.scrollHeight -
|
||||
btnHeight -
|
||||
10;
|
||||
} else {
|
||||
this.height = document.getElementsByClassName(
|
||||
this.videoWindowClassName
|
||||
)[0].scrollHeight;
|
||||
}
|
||||
if (document.getElementById(this.playWndId)) {
|
||||
document.getElementById(this.playWndId).style.height = this.height + "px";
|
||||
document.getElementById(this.playWndId).style.width = this.width + "px";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -1,397 +0,0 @@
|
||||
<template>
|
||||
|
||||
<!-- 绑定父组件class:videoWindow -->
|
||||
<div
|
||||
:id="`playWnd${item}`"
|
||||
ref="video-preview">
|
||||
</div>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
/* eslint-disable */
|
||||
|
||||
export default {
|
||||
name: 'video-preview',
|
||||
data() {
|
||||
return {
|
||||
index: 0,
|
||||
width: 0,
|
||||
height: 0,
|
||||
secret: import.meta.env.VITE_APP_HAIKANG_SECRET,
|
||||
streamMode: 0, // 主子码流标识 1:子码流 0:主码流
|
||||
transMode: 1, // 传输协议 1:TCP 0:UDP
|
||||
gpuMode: 0, // 是否启用GPU硬解
|
||||
oWebControl: {},
|
||||
initCount: 0,
|
||||
appkey: import.meta.env.VITE_APP_HAIKANG_APPKEY,
|
||||
ip: import.meta.env.VITE_APP_HAIKANG_IP,
|
||||
port: parseInt(import.meta.env.VITE_APP_HAIKANG_PORT),
|
||||
snapDir: 'D:\\SnapDir',
|
||||
videoDir: 'D:\\VideoDir',
|
||||
playMode: 0,
|
||||
szShowToolbar: 1, // 显示工具栏,
|
||||
szShowSmart: 1, // 显示智能信息=
|
||||
btIds: '0,16,256,257,258,259,260,512,513,514,515,516,517,768,769', // 工具条按钮ID集
|
||||
pubKey: '',
|
||||
enableHttps: 1, // 是否启用https
|
||||
showToolbar: 1,
|
||||
showSmart: 1,
|
||||
specialCodes: [
|
||||
],
|
||||
encryptedFields: 'secret',
|
||||
isInit: false
|
||||
}
|
||||
},
|
||||
props: {
|
||||
layout: {
|
||||
type: String,
|
||||
default: () => '1x1'
|
||||
},
|
||||
cameraIndexCode: {},
|
||||
timeOut: {
|
||||
type: Number,
|
||||
default: () => 500
|
||||
},
|
||||
item:{
|
||||
type: String,
|
||||
default: () => '0'
|
||||
},
|
||||
iframe:{
|
||||
type: Object,
|
||||
default: () => {}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
var that = this
|
||||
this.$nextTick(function() {
|
||||
that.windowChange()
|
||||
setTimeout(() => {
|
||||
that.initPlugin()
|
||||
}, that.timeOut)
|
||||
window.addEventListener('resize', function() {
|
||||
that.onResize()
|
||||
})
|
||||
})
|
||||
},
|
||||
destroyed() {
|
||||
this.uninit()
|
||||
},
|
||||
methods: {
|
||||
onResize(change=true){
|
||||
let that = this
|
||||
if (that.oWebControl) {
|
||||
if(change){
|
||||
that.windowChange()
|
||||
}
|
||||
that.oWebControl.JS_Resize(that.width, that.height)
|
||||
}
|
||||
},
|
||||
wait (fn, timeout, tick) {
|
||||
timeout = timeout || 5000;
|
||||
tick = tick || 250;
|
||||
var timeoutTimer = null;
|
||||
var execTimer = null;
|
||||
|
||||
return new Promise(function(resolve, reject) {
|
||||
|
||||
timeoutTimer = setTimeout(function() {
|
||||
clearTimeout(execTimer);
|
||||
reject(new Error('polling fail because timeout'));
|
||||
}, timeout);
|
||||
|
||||
tickHandler(fn);
|
||||
|
||||
function tickHandler(fn) {
|
||||
var ret = fn();
|
||||
if (!ret) {
|
||||
execTimer = setTimeout(function() {
|
||||
tickHandler(fn);
|
||||
}, tick)
|
||||
} else {
|
||||
clearTimeout(timeoutTimer);
|
||||
resolve();
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
setLayout(layout = '1x1') {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!this.isInit) {
|
||||
reject('未完成视频插件初始化')
|
||||
}
|
||||
this.oWebControl.JS_RequestInterface({
|
||||
funcName: 'setLayout',
|
||||
argument: JSON.stringify({
|
||||
'layout': layout
|
||||
})
|
||||
}).then(function(oData) {
|
||||
resolve()
|
||||
})
|
||||
})
|
||||
},
|
||||
// 初始化plugin
|
||||
initPlugin() {
|
||||
let that = this
|
||||
this.oWebControl = new WebControl({
|
||||
szPluginContainer: `playWnd${that.item}`,
|
||||
iServicePortStart: 15900,
|
||||
iServicePortEnd: 15909,
|
||||
szClassId: '23BF3B0A-2C56-4D97-9C03-0CB103AA8F11', // 用于IE10使用ActiveX的clsid
|
||||
cbConnectSuccess: function() {
|
||||
that.oWebControl.JS_SetWindowControlCallback({
|
||||
cbIntegrationCallBack: function (oData) {
|
||||
if(oData.responseMsg.msg.result === 1024){
|
||||
that.oWebControl.JS_HideWnd();
|
||||
}
|
||||
if(oData.responseMsg.msg.result === 1025){
|
||||
that.oWebControl.JS_ShowWnd();
|
||||
}
|
||||
if(oData.responseMsg.msg.result === 816){
|
||||
that.$emit('close',oData,that.item)
|
||||
}
|
||||
}
|
||||
});
|
||||
that.oWebControl.JS_StartService('window', {
|
||||
dllPath: './VideoPluginConnect.dll'
|
||||
}).then(function() {
|
||||
that.oWebControl.JS_CreateWnd(`playWnd${that.item}`, that.width, that.height).then(function() {
|
||||
console.log('视频plugin创建成功,进行interface初始化')
|
||||
// ue嵌入页面,视频窗口偏移
|
||||
if(that.iframe){
|
||||
that.oWebControl.JS_SetDocOffset ({
|
||||
left: that.iframe.left,
|
||||
top: that.iframe.top
|
||||
})
|
||||
}
|
||||
that.initInterface()
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
},
|
||||
cbConnectError: function() {
|
||||
console.log('cbConnectError')
|
||||
this.oWebControl = null
|
||||
WebControl.JS_WakeUp('VideoWebPlugin://')
|
||||
that.initCount++
|
||||
if (that.initCount < 3) {
|
||||
setTimeout(that.initPlugin, 2000)
|
||||
} else {
|
||||
that.isInit = false
|
||||
that.$message.error('插件启动失败,请检查VideoWebPlugin.exe插件是否安装!')
|
||||
}
|
||||
},
|
||||
cbConnectClose: function(bNormalClose) {
|
||||
console.log('cbConnectClose')
|
||||
that.isInit = false
|
||||
that.oWebControl = null
|
||||
}
|
||||
})
|
||||
},
|
||||
setEncrypt(value) {
|
||||
// RSA加密
|
||||
var encrypt = new JSEncrypt()
|
||||
encrypt.setPublicKey(this.pubKey)
|
||||
return encrypt.encrypt(value)
|
||||
},
|
||||
//初始化interface
|
||||
initInterface() {
|
||||
let that = this
|
||||
this.btIds = '0,16,256,257,258,259,260,512,513,514,515,516,517,768,769'
|
||||
this.getPubKey(function() {
|
||||
that.oWebControl.JS_RequestInterface({
|
||||
funcName: 'init',
|
||||
argument: JSON.stringify({
|
||||
appkey: that.appkey,
|
||||
secret: that.secret,
|
||||
ip: that.ip,
|
||||
playMode: that.playMode, // 预览
|
||||
port: that.port,
|
||||
snapDir: that.snapDir,
|
||||
videoDir: that.videoDir,
|
||||
layout: that.layout,
|
||||
enableHTTPS: that.enableHttps,
|
||||
showToolbar: that.showToolbar,
|
||||
showSmart: that.showSmart,
|
||||
buttonIDs: that.btIds,
|
||||
//encryptedFields: that.encryptedFields
|
||||
})
|
||||
}).then(function(oData) {
|
||||
that.isInit = true
|
||||
that.oWebControl.JS_Resize(that.width, that.height) // 初始化后resize一次,规避firefox下首次显示窗口后插件窗口未与DIV窗口重合问题
|
||||
that.handlePreview(that.cameraIndexCode)
|
||||
})
|
||||
})
|
||||
},
|
||||
// 获取公钥
|
||||
getPubKey(callback) {
|
||||
var that = this
|
||||
this.oWebControl.JS_RequestInterface({
|
||||
funcName: 'getRSAPubKey',
|
||||
argument: JSON.stringify({
|
||||
keyLength: 1024
|
||||
})
|
||||
}).then(function(oData) {
|
||||
if (oData.responseMsg.data) {
|
||||
that.pubKey = oData.responseMsg.data
|
||||
callback()
|
||||
}
|
||||
})
|
||||
},
|
||||
multiVideos(videos) {
|
||||
let that = this
|
||||
let last = videos[videos.length - 1]
|
||||
return new Promise((resole, reject) => {
|
||||
let intervalVideo = (clips) => {
|
||||
if (clips instanceof Array && clips.length > 0) {
|
||||
let clipLength = clips.length
|
||||
let clip = clips[0]
|
||||
|
||||
// 子码流特殊处理
|
||||
let streamMode = that.streamMode
|
||||
if(that.specialCodes.indexOf(clip) > -1) streamMode = 1
|
||||
console.log(clip)
|
||||
|
||||
that.oWebControl.JS_RequestInterface({
|
||||
funcName: 'startPreview',
|
||||
argument: JSON.stringify({
|
||||
cameraIndexCode: clip,
|
||||
streamMode: streamMode,
|
||||
transMode: that.transMode,
|
||||
gpuMode: that.gpuMode,
|
||||
wndId: -1
|
||||
})
|
||||
}).then(function(oData) {
|
||||
clips.shift()
|
||||
if (oData.responseMsg.code == 0) {
|
||||
if (clips.length) {
|
||||
setTimeout(() => {
|
||||
intervalVideo(clips)
|
||||
}, 1000)
|
||||
}
|
||||
}else{
|
||||
console.log(oData)
|
||||
}
|
||||
})
|
||||
} else {
|
||||
resole
|
||||
}
|
||||
}
|
||||
|
||||
intervalVideo(videos)
|
||||
})
|
||||
},
|
||||
handlePreview(codes, startIdx = 1) { //startIdx 如果是多个 从第几个窗口开始加载
|
||||
console.log(codes)
|
||||
let that = this
|
||||
if (!codes || codes=='' || codes.length==0)
|
||||
return
|
||||
if (Array.isArray(codes) && codes.length > 0) {
|
||||
// let confs = codes.map((code, index) => {
|
||||
// return {
|
||||
// cameraIndexCode: code,
|
||||
// streamMode: that.streamMode,
|
||||
// transMode: that.transMode,
|
||||
// gpuMode: that.gpuMode,
|
||||
// wndId: startIdx + index //设置不对会报错
|
||||
// }
|
||||
// })
|
||||
// that.oWebControl.JS_RequestInterface({
|
||||
// funcName: 'startMultiPreviewByCameraIndexCode',
|
||||
// argument: JSON.stringify({
|
||||
// list: confs
|
||||
// })
|
||||
// })
|
||||
this.onLoading = true
|
||||
this.multiVideos(codes).then(()=>{
|
||||
this.onLoading = false
|
||||
})
|
||||
} else if (typeof codes == 'string') {
|
||||
// 子码流特殊处理
|
||||
let streamMode = that.streamMode
|
||||
if(that.specialCodes.indexOf(codes)>-1) streamMode = 1
|
||||
|
||||
that.oWebControl.JS_RequestInterface({
|
||||
funcName: 'startPreview',
|
||||
argument: JSON.stringify({
|
||||
cameraIndexCode: codes,
|
||||
streamMode: streamMode,
|
||||
transMode: that.transMode,
|
||||
gpuMode: that.gpuMode,
|
||||
wndId: -1
|
||||
})
|
||||
})
|
||||
}
|
||||
},
|
||||
uninit() {
|
||||
let oWebControl = this.oWebControl
|
||||
if (oWebControl != null) {
|
||||
oWebControl.JS_RequestInterface({
|
||||
funcName: 'stopAllPreview'
|
||||
})
|
||||
oWebControl.JS_HideWnd() // 先让窗口隐藏,规避可能的插件窗口滞后于浏览器消失问题
|
||||
oWebControl.JS_Disconnect()
|
||||
.then(function() {
|
||||
// 断开与插件服务连接成功
|
||||
},
|
||||
function() { // 断开与插件服务连接失败
|
||||
console.log('oWebControl close error')
|
||||
})
|
||||
oWebControl = null
|
||||
}
|
||||
},
|
||||
windowChange() {
|
||||
//列表选项在左侧
|
||||
// this.height = document.getElementsByClassName('videoWindow')[0].scrollHeight
|
||||
// var btnWidth = 0
|
||||
// if (document.getElementsByClassName('playBtn').length > 0) {
|
||||
// btnWidth = document.getElementsByClassName('playBtn')[0].clientWidth
|
||||
// this.width = document.getElementsByClassName('videoWindow')[0].scrollWidth - btnWidth - 5
|
||||
// } else {
|
||||
// this.width = document.getElementsByClassName('videoWindow')[0].scrollWidth
|
||||
// }
|
||||
//列表选项在下方
|
||||
this.width = document.getElementsByClassName(`video-window${this.item}`)[0].scrollWidth
|
||||
var btnHeight = 0
|
||||
if (document.getElementsByClassName('playBtn').length > 0) {
|
||||
btnHeight = document.getElementsByClassName('playBtn')[0].clientHeight
|
||||
this.height = document.getElementsByClassName(`video-window${this.item}`)[0].scrollHeight - btnHeight - 10
|
||||
} else {
|
||||
this.height = document.getElementsByClassName(`video-window${this.item}`)[0].scrollHeight
|
||||
}
|
||||
|
||||
if (document.getElementById(`playWnd${this.item}`)) {
|
||||
document.getElementById(`playWnd${this.item}`).style.height = this.height + 'px'
|
||||
document.getElementById(`playWnd${this.item}`).style.width = this.width + 'px'
|
||||
}
|
||||
},
|
||||
hidePlugin() {
|
||||
this.oWebControl.JS_HideWnd()
|
||||
},
|
||||
showPlugin() {
|
||||
this.oWebControl.JS_ShowWnd()
|
||||
},
|
||||
ready() {
|
||||
let that = this
|
||||
return new Promise((resolve, reject) => {
|
||||
this.wait(function() {
|
||||
return that.isInit
|
||||
}, 6000 + that.timeOut || 500, 100).then(function() {
|
||||
resolve()
|
||||
}).catch(function(err) {
|
||||
that.$message.error({
|
||||
content: '视频控件加载超时,请检查',
|
||||
duration: 3
|
||||
})
|
||||
reject('视频控件加载超时,请检查')
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -11,7 +11,7 @@
|
||||
v-model="model[item.prop]"
|
||||
:placeholder="item.placeholder ? item.placeholder : '请输入'"
|
||||
:clearable="item.clearable !== false"
|
||||
style="width: 110px">
|
||||
:style="{width: item.width || '110px'}">
|
||||
</el-input>
|
||||
<el-select
|
||||
v-if="item.type === 'select'"
|
||||
@@ -23,7 +23,7 @@
|
||||
:collapse-tags="item.collapseTags || false"
|
||||
:collapse-tags-tooltip="item.collapseTagsTooltip || false"
|
||||
@change="(val) => handle(item.event,val)"
|
||||
style="width: 90px"
|
||||
:style="{width: item.width || '90px'}"
|
||||
poper-class="filter-select"
|
||||
:empty-values="item.emptyValues || [ '', undefined]"
|
||||
:value-on-clear="item.valueOnClear || ''">
|
||||
@@ -158,8 +158,10 @@ defineExpose({
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
.el-form{
|
||||
width: 1060px;
|
||||
text-align: left;
|
||||
}
|
||||
.buttons{
|
||||
min-width: 140px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
462
src/components/Filter/index2.vue
Normal file
462
src/components/Filter/index2.vue
Normal file
@@ -0,0 +1,462 @@
|
||||
<template>
|
||||
<el-form :class="theme" :inline="true" :model="model">
|
||||
<el-form-item
|
||||
v-for="(item, index) in filterItems"
|
||||
:key="index"
|
||||
:label="item.label">
|
||||
|
||||
<el-input
|
||||
v-if="item.type === 'input'"
|
||||
v-model="model[item.prop]"
|
||||
:placeholder="item.placeholder ? item.placeholder : ''"
|
||||
:clearable="item.clearable !== false"
|
||||
style="width: 200px">
|
||||
</el-input>
|
||||
<template v-if="item.type === 'inputrange'">
|
||||
<div>
|
||||
<el-input
|
||||
v-model="model[item.prop1]"
|
||||
:placeholder="item.placeholder ? item.placeholder[0] : ''"
|
||||
:clearable="item.clearable !== false"
|
||||
style="width: 80px">
|
||||
</el-input>
|
||||
<span class="range">至</span>
|
||||
<el-input
|
||||
v-model="model[item.prop2]"
|
||||
:placeholder="item.placeholder ? item.placeholder[1] : ''"
|
||||
:clearable="item.clearable !== false"
|
||||
style="width: 80px">
|
||||
</el-input>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<el-select
|
||||
v-if="item.type === 'select'"
|
||||
v-model="model[item.prop]"
|
||||
:clearable="item.clearable !== false"
|
||||
:filterable="item.filterable || false"
|
||||
:placeholder="item.placeholder ? item.placeholder : ''"
|
||||
:multiple="item.multiple || false"
|
||||
:collapse-tags="item.collapseTags || false"
|
||||
:collapse-tags-tooltip="item.collapseTagsTooltip || false"
|
||||
@change="(val) => handle(item.event,val)"
|
||||
style="width: 200px">
|
||||
<el-checkbox v-if="item.checkbox || false" v-model="checked" @change='(val)=>selectAll(val,item)'>全选</el-checkbox>
|
||||
<el-option
|
||||
v-for="(opt, index) in item.options"
|
||||
:key="index"
|
||||
:label="opt.label"
|
||||
:value="opt.value">
|
||||
</el-option>
|
||||
</el-select>
|
||||
<el-select
|
||||
v-if="item.type === 'selectRemote'"
|
||||
v-model="model[item.prop]"
|
||||
filterable
|
||||
remote
|
||||
reserve-keyword
|
||||
:clearable="item.clearable !== false"
|
||||
:placeholder="item.placeholder ? item.placeholder : ''"
|
||||
:remote-method="(val) => emit('remote', val,item.prop)"
|
||||
:loading="false"
|
||||
style="width: 200px">
|
||||
<el-option v-for="(opt, index) in item.options" :key="index" :label="opt.label" :value="opt.value">
|
||||
</el-option>
|
||||
</el-select>
|
||||
<el-tree-select
|
||||
v-if="item.type === 'treeselect'"
|
||||
:clearable="item.clearable !== false"
|
||||
v-model="model[item.prop]"
|
||||
:data="item.options"
|
||||
:props="{ value: 'areaCode', label: 'areaName', children: 'childArea' }"
|
||||
value-key="areaCode"
|
||||
:render-after-expand="false"
|
||||
check-strictly
|
||||
style="width: 200px"
|
||||
:load="load"
|
||||
lazy
|
||||
/>
|
||||
<el-date-picker
|
||||
v-if="item.type === 'year'"
|
||||
v-model="model[item.prop]"
|
||||
type="year"
|
||||
:placeholder="item.placeholder ? item.placeholder : '请选择年份'"
|
||||
value-format="YYYY"
|
||||
:clearable="item.clearable !== false"
|
||||
style="width: 200px">
|
||||
</el-date-picker>
|
||||
<el-date-picker
|
||||
v-if="item.type === 'date'"
|
||||
v-model="model[item.prop]"
|
||||
type="date"
|
||||
:placeholder="item.placeholder ? item.placeholder : '请选择日期'"
|
||||
value-format="YYYY-MM-DD"
|
||||
:clearable="item.clearable !== false"
|
||||
style="width: 200px">
|
||||
</el-date-picker>
|
||||
|
||||
<el-date-picker
|
||||
v-if="item.type === 'datetime'"
|
||||
v-model="model[item.prop]"
|
||||
type="datetime"
|
||||
:placeholder="item.placeholder ? item.placeholder : '请选择时间'"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
:clearable="item.clearable !== false"
|
||||
style="width: 200px">
|
||||
</el-date-picker>
|
||||
|
||||
<el-date-picker
|
||||
v-if="item.type === 'daterange'"
|
||||
v-model="model[item.prop]"
|
||||
type="daterange"
|
||||
range-separator="-"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
value-format="YYYY-MM-DD"
|
||||
:clearable="item.clearable !== false"
|
||||
style="width: 280px"
|
||||
@change="changeDate">
|
||||
</el-date-picker>
|
||||
|
||||
<el-date-picker
|
||||
v-if="item.type === 'datetimerange'"
|
||||
v-model="model[item.prop]"
|
||||
type="datetimerange"
|
||||
range-separator="-"
|
||||
start-placeholder="开始时间"
|
||||
end-placeholder="结束时间"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
:clearable="item.clearable !== false"
|
||||
:disabled-date="disabledDate"
|
||||
@calendar-change="calendarChange"
|
||||
style="width: 360px">
|
||||
</el-date-picker>
|
||||
<el-time-picker
|
||||
is-range
|
||||
v-if="item.type === 'timePicker'"
|
||||
value-format="HH:mm:ss"
|
||||
format="HH:mm:ss"
|
||||
v-model="model[item.prop]"
|
||||
range-separator="至"
|
||||
start-placeholder="开始时间"
|
||||
end-placeholder="结束时间"
|
||||
placeholder="选择时间范围">
|
||||
</el-time-picker>
|
||||
|
||||
<el-button
|
||||
v-if="item.type === 'button'"
|
||||
:type="item.theme"
|
||||
v-hasPermi="item.permission"
|
||||
@click="handle(item.prop)">{{ item.name }}
|
||||
</el-button>
|
||||
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</template>
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue'
|
||||
|
||||
const emit = defineEmits([ 'handle', 'change', 'remote', 'changeDate' ])
|
||||
|
||||
const props = defineProps({
|
||||
'filter-items': {
|
||||
type: Array,
|
||||
required: false,
|
||||
default: () => {
|
||||
return []
|
||||
}
|
||||
},
|
||||
'filter-model': {
|
||||
type: Object,
|
||||
required: false,
|
||||
default: () => {
|
||||
return {}
|
||||
}
|
||||
},
|
||||
theme: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: 'dark'
|
||||
},
|
||||
disabledDate: {
|
||||
type: Function,
|
||||
required: false
|
||||
},
|
||||
calendarChange: {
|
||||
type: Function,
|
||||
required: false
|
||||
}
|
||||
})
|
||||
|
||||
const model = ref({})
|
||||
const checked = ref(false)
|
||||
|
||||
// 多选下拉框 全选操作
|
||||
const selectAll = (val, item) => {
|
||||
model.value[item.prop] = []
|
||||
if (checked.value) {
|
||||
model.value[item.prop] = item.options.map(i => i.value)
|
||||
} else {
|
||||
model.value[item.prop] = []
|
||||
}
|
||||
}
|
||||
// 修改多选下拉框全选选中状态
|
||||
const changeChecked = (flag) => {
|
||||
checked.value = flag
|
||||
}
|
||||
|
||||
/**
|
||||
* 点击操作按钮
|
||||
* @param type 操作类型
|
||||
*/
|
||||
const handle = (type) => {
|
||||
emit('handle', type, model.value)
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置表单数据
|
||||
*/
|
||||
const reset = () => {
|
||||
model.value = { ...props.filterModel }
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
model.value = { ...props.filterModel }
|
||||
})
|
||||
|
||||
const initModel = () => {
|
||||
model.value = { ...props.filterModel }
|
||||
}
|
||||
|
||||
const changeDate = (e) => {
|
||||
emit('changeDate', e)
|
||||
}
|
||||
const load = async(node, resolve) => {
|
||||
if (node.data && node.data.childArea) {
|
||||
resolve(node.data.childArea)
|
||||
}
|
||||
}
|
||||
defineExpose({
|
||||
reset,
|
||||
initModel,
|
||||
changeChecked
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.dark {
|
||||
:deep(.el-form-item) {
|
||||
margin-right: 20px;
|
||||
|
||||
.el-form-item__label,.range {
|
||||
color: #fff;
|
||||
}
|
||||
.range{
|
||||
margin: 0 10px;
|
||||
}
|
||||
|
||||
.el-input__wrapper {
|
||||
background-color: #00889b00;
|
||||
box-shadow: 0 0 0 1px #296dff99;
|
||||
|
||||
&:has(.el-input__inner:focus){
|
||||
box-shadow: 0 0 0 1px #296dff;
|
||||
}
|
||||
|
||||
.el-input__inner {
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
.el-select {
|
||||
|
||||
.el-select__wrapper {
|
||||
background-color: #00889b1f;
|
||||
box-shadow: 0 0 0 1px #296dff99;
|
||||
|
||||
.el-select__selected-item,
|
||||
.el-select__icon {
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.el-date-editor {
|
||||
.el-icon {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.el-range-input {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.el-range-separator {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.el-picker__popper {
|
||||
border-color: #005e5eb2;
|
||||
.el-picker-panel {
|
||||
background-color: #0f1E31;
|
||||
|
||||
.el-picker-panel__body-wrapper {
|
||||
.el-date-range-picker__time-header{
|
||||
border-bottom: 1px solid #0acccc1a;
|
||||
|
||||
}
|
||||
|
||||
.el-date-range-picker__content.is-left{
|
||||
border-right: 1px solid #0acccc1a;
|
||||
}
|
||||
|
||||
.el-date-range-picker__header {
|
||||
color: #fff;
|
||||
border-bottom: 1px solid #0acccc1a;
|
||||
|
||||
.el-picker-panel__icon-btn {
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
th {
|
||||
color: #fff;
|
||||
border-bottom: 1px solid #0acccc1a;
|
||||
}
|
||||
|
||||
td {
|
||||
&.in-range .el-date-table-cell {
|
||||
background-color: #0c424E;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
&.start-date .el-date-table-cell {
|
||||
border-radius: unset;
|
||||
|
||||
.el-date-table-cell__text {
|
||||
border-radius: unset;
|
||||
background-color: #0acccc;
|
||||
}
|
||||
}
|
||||
|
||||
&.end-date .el-date-table-cell {
|
||||
border-radius: unset;
|
||||
padding: 0;
|
||||
|
||||
.el-date-table-cell__text {
|
||||
border-radius: unset;
|
||||
background-color: #0acccc;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
}
|
||||
}
|
||||
|
||||
&.available:hover{
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
&.today{
|
||||
.el-date-table-cell__text{
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.el-picker-panel__footer {
|
||||
background-color: #0f1E31;
|
||||
border-top: 1px solid #0acccc1a;
|
||||
|
||||
.el-button {
|
||||
background-color: transparent;
|
||||
color: #0acccc;
|
||||
|
||||
&:hover {
|
||||
background-color: #0acccc1a;
|
||||
}
|
||||
|
||||
&.is-plain {
|
||||
background-color: #0d646E;
|
||||
border: 1px solid #0acccc;
|
||||
color: #fff;
|
||||
|
||||
&:hover {
|
||||
background-color: #0acccc71;
|
||||
}
|
||||
}
|
||||
|
||||
&:active {
|
||||
opacity: .8;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.el-popper__arrow::before{
|
||||
border-color: #0acccc;
|
||||
background-color: #07224c;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// .el-button {
|
||||
|
||||
// &.el-button--primary {
|
||||
// background-color: #409eff33;
|
||||
// border: 1px solid #409eff;
|
||||
// color: #409eff;
|
||||
|
||||
// &:hover {
|
||||
// background-color: #409eff71;
|
||||
// }
|
||||
// }
|
||||
|
||||
// &.el-button--success {
|
||||
// background-color: #67c23a33;
|
||||
// border: 1px solid #67c23a;
|
||||
// color: #67c23a;
|
||||
|
||||
// &:hover {
|
||||
// background-color: #67c23a71;
|
||||
// }
|
||||
// }
|
||||
|
||||
// &.el-button--info {
|
||||
// background-color: #f4f4f533;
|
||||
// border: 1px solid #f4f4f5;
|
||||
// color: #f4f4f5;
|
||||
|
||||
// &:hover {
|
||||
// background-color: #f4f4f571;
|
||||
// }
|
||||
// }
|
||||
|
||||
// &.el-button--warning {
|
||||
// background-color: #e6a23c33;
|
||||
// border: 1px solid #e6a23c;
|
||||
// color: #e6a23c;
|
||||
|
||||
// &:hover {
|
||||
// background-color: #e6a23c71;
|
||||
// }
|
||||
// }
|
||||
|
||||
// &.el-button--danger {
|
||||
// background-color: #f56c6c33;
|
||||
// border: 1px solid #f56c6c;
|
||||
// color: #f56c6c;
|
||||
|
||||
// &:hover {
|
||||
// background-color: #f56c6c71;
|
||||
// }
|
||||
// }
|
||||
|
||||
// &:active {
|
||||
// opacity: .8;
|
||||
// }
|
||||
// }
|
||||
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -24,6 +24,7 @@ const props = defineProps({
|
||||
let player = null
|
||||
let flag = 0
|
||||
let retryCount = 0
|
||||
let timeoutId = null
|
||||
const MAX_RETRIES = 30
|
||||
const initPlayer = () => {
|
||||
if (flvjs.isSupported()) {
|
||||
@@ -45,35 +46,28 @@ const initPlayer = () => {
|
||||
url: url
|
||||
},
|
||||
{
|
||||
// // 启用IO隐藏缓冲区
|
||||
// // 如果需要实时(最小延迟)来进行实时流播放,则设置为false
|
||||
// // 但是如果网络抖动,则可能会停顿
|
||||
// enableStashBuffer: false,
|
||||
// // 之时IO暂存缓冲区的初始大小,默认值为384kb,指出合适的尺寸可以改善视频负载/搜索时间
|
||||
// stashInitialSize: 128
|
||||
|
||||
// 启用缓冲区优化
|
||||
enableStashBuffer: true, // 改为true启用缓冲
|
||||
stashInitialSize: 1024 * 1024, // 设置初始缓冲大小
|
||||
maxBufferLength: 30 // 最大缓冲时长(秒)
|
||||
// 启用IO隐藏缓冲区
|
||||
// 如果需要实时(最小延迟)来进行实时流播放,则设置为false
|
||||
// 但是如果网络抖动,则可能会停顿
|
||||
enableStashBuffer: false,
|
||||
// 之时IO暂存缓冲区的初始大小,默认值为384kb,指出合适的尺寸可以改善视频负载/搜索时间
|
||||
stashInitialSize: 128
|
||||
}
|
||||
)
|
||||
|
||||
player.attachMediaElement(videoElement)
|
||||
player.load()
|
||||
player.play()
|
||||
console.log('player play')
|
||||
flag += 1
|
||||
|
||||
player.on('error', () => {
|
||||
console.log('errorrrrrrrrrrrrrrrrrrrrrrrr')
|
||||
clear()
|
||||
// 重试次数增加,延长时间指数增长,不超过10s
|
||||
const retryDelay = Math.min(10000, 1000 * Math.pow(2, retryCount))
|
||||
retryCount++
|
||||
|
||||
setTimeout(() => {
|
||||
console.log(retryCount, retryDelay, '重连次数......')
|
||||
timeoutId = setTimeout(() => {
|
||||
timeoutId = null
|
||||
if (retryCount <= MAX_RETRIES) {
|
||||
initPlayer() // 重新初始化播放器
|
||||
} else {
|
||||
@@ -81,7 +75,6 @@ const initPlayer = () => {
|
||||
if (player) {
|
||||
player = null
|
||||
}
|
||||
console.error('重试超过最大次数')
|
||||
}
|
||||
}, retryDelay)
|
||||
})
|
||||
@@ -90,6 +83,10 @@ const initPlayer = () => {
|
||||
}
|
||||
|
||||
const clear = () => {
|
||||
if (timeoutId) {
|
||||
clearTimeout(timeoutId)
|
||||
timeoutId = null
|
||||
}
|
||||
if (player) {
|
||||
player.pause()
|
||||
player.destroy()
|
||||
|
||||
@@ -69,11 +69,11 @@
|
||||
]
|
||||
|
||||
const navRight = [
|
||||
// {
|
||||
// label: '识别记录',
|
||||
// path: '/screen/identification',
|
||||
// prop: 'identification'
|
||||
// },
|
||||
{
|
||||
label: '业务配置',
|
||||
path: '/setting',
|
||||
prop: 'identification'
|
||||
},
|
||||
{
|
||||
label: '插件下载',
|
||||
path: '',
|
||||
@@ -101,6 +101,8 @@
|
||||
const toggle = (nav) => {
|
||||
if(nav.prop === 'monitor') {
|
||||
window.open('/plugin/VideoWebPlugin.exe', '_blank')
|
||||
}else{
|
||||
router.push(nav.path)
|
||||
}
|
||||
}
|
||||
// 右上角时间
|
||||
@@ -250,7 +252,7 @@
|
||||
}
|
||||
.datetime{
|
||||
display: flex;
|
||||
gap: 14px;
|
||||
gap: 12px;
|
||||
color: #FFFFFF;
|
||||
text-align: left;
|
||||
height: 29px;
|
||||
@@ -284,7 +286,7 @@
|
||||
margin-left: 61px;
|
||||
}
|
||||
.right{
|
||||
gap:20px;
|
||||
gap:15px;
|
||||
margin-right: 40px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
@@ -1,130 +0,0 @@
|
||||
<template>
|
||||
<div :class="[{ 'expend-only': expend }, 'screen-map-server',toolBarStore.expand ? 'expand' : '']" @mouseenter="moveEnter" @mouseleave="moveLeave">
|
||||
|
||||
<img alt="ICON_MAP" class="icon-cover" :src="getAssetsFile(`icon-${active}-active.png`)">
|
||||
|
||||
<div v-for="(item, index) in list" class="screen-map-server-item" :key="index" :style="getStyle(item, index)"
|
||||
@click="toggle(item, index)">
|
||||
|
||||
<img v-show="(!expend && !index) || expend" alt="ICON_MAP" class="icon-map"
|
||||
:src="getAssetsFile(`icon-${item.prop}${item.prop == active ? '-active' : ''}.png`)">
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import { getAssetsFile } from '@/utils/common'
|
||||
import useToolBarStore from '@/store/modules/toolbar'
|
||||
|
||||
const toolBarStore = useToolBarStore()
|
||||
const emit = defineEmits([ 'toggle' ])
|
||||
|
||||
const active = ref('satellite')
|
||||
const expend = ref(false)
|
||||
|
||||
const list = [
|
||||
{
|
||||
label: '遥感',
|
||||
prop: 'satellite'
|
||||
},
|
||||
{
|
||||
label: '海图',
|
||||
prop: 'sea'
|
||||
},
|
||||
{
|
||||
label: '浅色',
|
||||
prop: 'light'
|
||||
}
|
||||
]
|
||||
|
||||
/**
|
||||
* 动态改变当前图例的定位
|
||||
* @param e 当前图例信息
|
||||
* @param index 当前图例下标
|
||||
*/
|
||||
const getStyle = (e, index) => ({
|
||||
'background-color': !expend.value && e.color ? e.color : 'transparent', // 未展开样式
|
||||
'border-radius': expend.value ? '0px' : '5px', // 未展开样式
|
||||
right: expend.value ? `${index * 40 + (index + 1) * 4}px` : `${(index + 1) * 4}px`,
|
||||
'transition-duration': expend.value ? `${index * 0.2}s` : '0s',
|
||||
'z-index': e.index
|
||||
})
|
||||
|
||||
const moveEnter = () => {
|
||||
expend.value = true
|
||||
}
|
||||
|
||||
const moveLeave = () => {
|
||||
expend.value = false
|
||||
}
|
||||
|
||||
/**
|
||||
* 底图切换
|
||||
* @param e 当前图例信息
|
||||
*/
|
||||
const toggle = (e) => {
|
||||
active.value = e.prop
|
||||
emit('toggle', 'toggle-base', e.prop)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.screen-map-server {
|
||||
position: absolute;
|
||||
right: 30px;
|
||||
bottom: 112px;
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
padding: 4px;
|
||||
border-radius: 5px;
|
||||
box-sizing: border-box;
|
||||
background-color: transparent;
|
||||
transition-duration: .3s;
|
||||
cursor: pointer;
|
||||
pointer-events: auto;
|
||||
|
||||
&.expend-only {
|
||||
height: 48px;
|
||||
width: 135px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: rgba(77, 151, 255, 0.5);
|
||||
|
||||
.icon-cover {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* COVER */
|
||||
.icon-cover {
|
||||
display: block;
|
||||
position: absolute;
|
||||
right: 4px;
|
||||
top: 4px;
|
||||
z-index: 9;
|
||||
}
|
||||
|
||||
/* ITEM */
|
||||
.screen-map-server-item {
|
||||
border-radius: 0;
|
||||
height: 40px;
|
||||
position: absolute;
|
||||
top: 4px;
|
||||
width: 40px;
|
||||
z-index: 1;
|
||||
|
||||
.icon-map {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
.expand{
|
||||
right: 390px;
|
||||
}
|
||||
</style>
|
||||
@@ -13,38 +13,31 @@
|
||||
<TrawlerInfoWindowComponent/>
|
||||
</template>
|
||||
<script setup>
|
||||
import { computed, onMounted, onUnmounted, watch } from 'vue'
|
||||
import { computed, nextTick, onMounted, onUnmounted, watch } from 'vue'
|
||||
import * as maptalks from 'maptalks'
|
||||
import GlobalMap from './js/GlobalMap'
|
||||
import useMapStore from '@/store/modules/map'
|
||||
import { getAssetsFile } from '@/utils/common'
|
||||
import * as BoatUtil from './lbtbox/boatTerminal'
|
||||
import * as $configs from './map-config.js'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { monitors, uavs, stations, environmentals, fences, detailFences } from './js/mock.js'
|
||||
import InfoWindowComponent from '@/components/Map/window/index.vue'
|
||||
import TrawlerInfoWindowComponent from '@/components/Map/window/trawler.vue'
|
||||
import { videoCameraFindPage, findUavPage, findEnvPage, dsVideoList } from '@/api/device.js'
|
||||
import { dsVideoList, findUavPage, getVideoInfo } from '@/api/device.js'
|
||||
import { useRouter } from 'vue-router'
|
||||
|
||||
const router = useRouter()
|
||||
const mapStore = useMapStore()
|
||||
const UAV = computed(() => mapStore.legend.UAV)
|
||||
const monitor = computed(() => mapStore.legend.monitor)
|
||||
const origin_monitor = computed(() => mapStore.legend.origin_monitor)
|
||||
const ais_station = computed(() => mapStore.legend.ais_station)
|
||||
const environmental = computed(() => mapStore.legend.environmental)
|
||||
const fence = computed(() => mapStore.legend.fence)
|
||||
const sector = computed(() => mapStore.sector)
|
||||
const legend = computed(() => mapStore.legend)
|
||||
const locateData = computed(() => mapStore.locate.data)
|
||||
let globalMap = null
|
||||
let vector = {}
|
||||
const geography = {
|
||||
monitor: [], // 监控数据
|
||||
origin_monitor: [], // 原始监控数据
|
||||
UAV: [], // 无人机
|
||||
ais_station: [], // ais基站
|
||||
environmental: [], // 环境监测
|
||||
fence: [] // 电子围栏
|
||||
}
|
||||
let sectorLayer = null
|
||||
const initMap = () => {
|
||||
const mapDom = document.getElementById('map')
|
||||
globalMap = new GlobalMap(mapDom)//, { seamlessZoom: false })
|
||||
@@ -112,42 +105,53 @@ const addMonitorToMap = () => {
|
||||
},
|
||||
{
|
||||
id: item.id,
|
||||
symbol: $configs.getDevicePointSymbol('_monitor', { ...item, name: item.videoName }),
|
||||
symbol: $configs.getDevicePointSymbol('_monitor' + (item.beBayonet === 1 ? '_bayonet' : ''), { ...item, name: item.videoName }), // beBayonet 是否卡口1是 0否
|
||||
properties: item,
|
||||
zIndex: 2
|
||||
}
|
||||
)
|
||||
marker.addTo(vector.monitor)
|
||||
drawSector('_monitor', [ item.longitude, item.latitude ], 5 * 1000, item.id, 30)
|
||||
const visionDistance = item.visionDistance * 1000 || 5 * 1000
|
||||
const pvalue = item.ptzcfg?.pValue || 0
|
||||
drawSector('_monitor', [ item.longitude, item.latitude ], visionDistance, item.id, (90 - pvalue) % 360)
|
||||
marker.on('click', (evt) => {
|
||||
// 先隐藏所有扇形
|
||||
changeSectorsInLayer('sectors_monitor', false)
|
||||
|
||||
// 确保扇形图层是可见的
|
||||
if (!vector.sectors_monitor.isVisible()) {
|
||||
vector.sectors_monitor.show()
|
||||
}
|
||||
|
||||
// 显示当前点击项的所有相关扇形(圆、椭圆、线)
|
||||
const baseId = `sector__monitor${item.id}`
|
||||
const circleGeometry = vector.sectors_monitor.getGeometryById(baseId + '_circle')
|
||||
const ellipseGeometry = vector.sectors_monitor.getGeometryById(baseId + '_ellipse')
|
||||
const lineGeometry = vector.sectors_monitor.getGeometryById(baseId + '_line')
|
||||
|
||||
if (circleGeometry) {
|
||||
circleGeometry.show()
|
||||
}
|
||||
if (ellipseGeometry) {
|
||||
ellipseGeometry.show()
|
||||
}
|
||||
if (lineGeometry) {
|
||||
lineGeometry.show()
|
||||
}
|
||||
mapStore.updateWindowInfo({ visible: true, type: '_monitor', data: { ...item } })
|
||||
mapStore.updateLocate({ ...item }) // 视频定位
|
||||
mapStore.updateWindowInfo({ visible: true, type: '_monitor', data: { ...item } }) // 打开监控视频弹窗
|
||||
})
|
||||
}
|
||||
})
|
||||
vector.monitor.show()
|
||||
}
|
||||
// 要素/视频定位
|
||||
const locateMoitor = (data) => {
|
||||
initLayerToMap('locate-monitor')
|
||||
const monitors = vector.monitor
|
||||
const marker = monitors.getGeometryById(data.id)
|
||||
if (marker) {
|
||||
geography.monitor.forEach((item) => {
|
||||
if (item.id === data.id) {
|
||||
// 先隐藏所有扇形
|
||||
changeSectorsInLayer('sectors_monitor', false)
|
||||
getVideoInfo({ id: item.id }).then(res => {
|
||||
const visionDistance = item.visionDistance * 1000 || 5 * 1000 // 视角距离
|
||||
const pvalue = res.data.ptzcfg?.pValue || 0 // 旋转角度
|
||||
drawSector('_monitor', [ item.longitude, item.latitude ], visionDistance, item.id, (90 - pvalue) % 360)
|
||||
|
||||
// 确保扇形图层是可见的
|
||||
if (!vector.sectors_monitor.isVisible()) {
|
||||
vector.sectors_monitor.show()
|
||||
}
|
||||
|
||||
// 显示当前点击项的所有相关扇形(圆、椭圆、线)
|
||||
const baseId = `sector__monitor${item.id}`
|
||||
vector.sectors_monitor.getGeometryById(baseId + '_circle')?.show()
|
||||
vector.sectors_monitor.getGeometryById(baseId + '_ellipse')?.show()
|
||||
vector.sectors_monitor.getGeometryById(baseId + '_line')?.show()
|
||||
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 叠加无人机数据
|
||||
@@ -189,7 +193,7 @@ const addUAVToMap = () => {
|
||||
if (circleGeometry) {
|
||||
circleGeometry.show()
|
||||
}
|
||||
if(item.sourceType) {
|
||||
if(item.sourceType === '1' || item.sourceType === '2') {
|
||||
mapStore.updateWindowInfo({ visible: true, type: '_UAV', data: { ...item } })
|
||||
}
|
||||
})
|
||||
@@ -212,18 +216,29 @@ const changeSectorsInLayer = (layerName, show) => {
|
||||
}
|
||||
// 需要监控的起始角度和结束角度,修改视野范围可以传递经纬度坐标
|
||||
const drawSector = (type, center, radius, id, angle) => {
|
||||
const sectorLayerName = 'sectors' + type
|
||||
|
||||
// 检查是否存在扇形图层,不存在则初始化
|
||||
if (!vector[sectorLayerName]) {
|
||||
initLayerToMap(sectorLayerName.replace('sectors', 'sectors_')) // 例如 sectors_monitor -> sectors_monitor
|
||||
}
|
||||
|
||||
const sectorId = `sector_${type}${id}`
|
||||
|
||||
// 如果已存在同ID的扇形,则先移除
|
||||
const existingSector = vector['sectors' + type].getGeometryById(sectorId)
|
||||
if (existingSector) {
|
||||
vector['sectors' + type].removeGeometry(existingSector)
|
||||
}
|
||||
const existingCircle = vector[sectorLayerName].getGeometryById(sectorId + '_circle')
|
||||
const existingEllipse = vector[sectorLayerName].getGeometryById(sectorId + '_ellipse')
|
||||
const existingLine = vector[sectorLayerName].getGeometryById(sectorId + '_line')
|
||||
|
||||
// 如果已有该监控点的扇形图层,则先移除
|
||||
if (globalMap.map.getLayer(sectorId)) {
|
||||
globalMap.map.getLayer(sectorId).remove()
|
||||
if (existingCircle) {
|
||||
vector[sectorLayerName].removeGeometry(existingCircle)
|
||||
}
|
||||
if (existingEllipse) {
|
||||
vector[sectorLayerName].removeGeometry(existingEllipse)
|
||||
}
|
||||
if (existingLine) {
|
||||
vector[sectorLayerName].removeGeometry(existingLine)
|
||||
}
|
||||
|
||||
let circle = new maptalks.Circle(center, radius, {
|
||||
id: sectorId + '_circle',
|
||||
symbol: {
|
||||
@@ -234,15 +249,15 @@ const drawSector = (type, center, radius, id, angle) => {
|
||||
polygonOpacity: 0.16
|
||||
}
|
||||
})
|
||||
if(angle) {
|
||||
let ellipse = new maptalks.Sector(center, radius - 1 * 1000, angle - 30, angle + 30, {
|
||||
if(typeof angle === 'number' && !isNaN(angle)) {
|
||||
let ellipse = new maptalks.Sector(center, radius, angle - 10, angle + 10, {
|
||||
id: sectorId + '_ellipse',
|
||||
symbol: {
|
||||
lineColor: '#FF8D1C',
|
||||
polygonFill: '#ff8d1c29'
|
||||
}
|
||||
})
|
||||
let line = new maptalks.Sector(center, radius + 1 * 1000, angle, angle, {
|
||||
let line = new maptalks.Sector(center, radius, angle, angle, {
|
||||
id: sectorId + '_line',
|
||||
symbol: {
|
||||
lineColor: '#FF8D1C',
|
||||
@@ -256,7 +271,6 @@ const drawSector = (type, center, radius, id, angle) => {
|
||||
}
|
||||
// 添加到可视域图层
|
||||
changeSectorsInLayer('sectors' + type, false)
|
||||
// vector['sectors' + type].hide()
|
||||
}
|
||||
/**
|
||||
* 叠加ais基站数据
|
||||
@@ -280,8 +294,6 @@ const addAisStationToMap = () => {
|
||||
}
|
||||
)
|
||||
marker.addTo(vector.ais_station)
|
||||
marker.on('click', (evt) => {
|
||||
})
|
||||
}
|
||||
})
|
||||
vector.ais_station.show()
|
||||
@@ -308,8 +320,6 @@ const addEnvironmentalToMap = () => {
|
||||
}
|
||||
)
|
||||
marker.addTo(vector.environmental)
|
||||
marker.on('click', (evt) => {
|
||||
})
|
||||
}
|
||||
})
|
||||
vector.environmental.show()
|
||||
@@ -326,20 +336,13 @@ const initUAV = () => {
|
||||
addUAVToMap()
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
const initMonitor = () => {
|
||||
const params = {
|
||||
pageNo: 1,
|
||||
pageSize: 9999
|
||||
}
|
||||
videoCameraFindPage(params).then(res => {
|
||||
if (res.success) {
|
||||
geography.monitor = res.result.records
|
||||
addMonitorToMap()
|
||||
|
||||
}
|
||||
})
|
||||
const params = {}
|
||||
dsVideoList(params).then(res => {
|
||||
geography.monitor = res.data
|
||||
addMonitorToMap()
|
||||
})
|
||||
}
|
||||
const initAisStation = () => {
|
||||
geography.ais_station = stations
|
||||
@@ -357,78 +360,63 @@ const initFence = () => {
|
||||
addFenceToMap('detailFence')
|
||||
}
|
||||
const toModel = () => {
|
||||
mapStore.updateLargeModel(true)
|
||||
router.push('/largeModel')
|
||||
}
|
||||
onMounted(() => {
|
||||
initMap()
|
||||
initFence()
|
||||
initUAV()
|
||||
initMonitor()
|
||||
initAisStation()
|
||||
initEnvironmental()
|
||||
// 监控设备图层初始化
|
||||
Object.values(layerMehods).forEach(initFunc => initFunc())
|
||||
// 渔船链接
|
||||
BoatUtil.init(mapStore)
|
||||
BoatUtil.getShip()
|
||||
// 轨迹图层
|
||||
initLayerToMap('track')
|
||||
})
|
||||
watch(() => UAV.value, () => {
|
||||
if(UAV.value) {
|
||||
initUAV()
|
||||
}else{
|
||||
vector.UAV?.hide()
|
||||
}
|
||||
})
|
||||
watch(() => monitor.value, () => {
|
||||
if(monitor.value) {
|
||||
initMonitor()
|
||||
}else{
|
||||
vector.monitor?.hide()
|
||||
const layerMehods = {
|
||||
UAV: initUAV,
|
||||
monitor: initMonitor,
|
||||
ais_station: initAisStation,
|
||||
environmental: initEnvironmental,
|
||||
fence: initFence
|
||||
}
|
||||
watch(() => Object.keys(layerMehods).reduce((obj, key) => {
|
||||
obj[key] = legend.value[key]
|
||||
return obj
|
||||
}, {}), (newVal, oldVal) => {
|
||||
Object.keys(layerMehods).forEach(key => {
|
||||
// 监听值变化进行更改
|
||||
if (newVal[key] !== oldVal[key]) {
|
||||
if (newVal[key]) {
|
||||
layerMehods[key]()
|
||||
} else {
|
||||
vector[key]?.hide()
|
||||
if (key === 'UAV' || key === 'monitor') { // 无人机和监控可视区域隐藏
|
||||
changeSectorsInLayer('sectors_' + key, false)
|
||||
} else if (key === 'fence') {
|
||||
vector.detailFence?.hide()
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}, { deep: true })
|
||||
const watchKeys = [ 'sectors_monitor', 'sectors_UAV' ]
|
||||
watch(() => ({ sectors_UAV: legend.value.sectors_UAV, sectors_monitor: legend.value.sectors_monitor }), (newVal, oldVal) => {
|
||||
watchKeys.forEach(key => {
|
||||
// 监听值变化进行更改
|
||||
if (newVal[key] !== oldVal[key]) {
|
||||
if (newVal[key]) {
|
||||
changeSectorsInLayer(key, true)
|
||||
} else {
|
||||
changeSectorsInLayer(key, false)
|
||||
}
|
||||
}
|
||||
})
|
||||
watch(() => ais_station.value, () => {
|
||||
if(ais_station.value) {
|
||||
initAisStation()
|
||||
}else{
|
||||
vector.ais_station?.hide()
|
||||
}
|
||||
})
|
||||
watch(() => environmental.value, () => {
|
||||
if(environmental.value) {
|
||||
initEnvironmental()
|
||||
}else{
|
||||
vector.environmental?.hide()
|
||||
}
|
||||
})
|
||||
watch(() => fence.value, () => {
|
||||
if(fence.value) {
|
||||
initFence()
|
||||
}else{
|
||||
vector.fence?.hide()
|
||||
vector.detailFence?.hide()
|
||||
}
|
||||
})
|
||||
watch(() => sector.value.monitor, (newVal) => {
|
||||
if(vector.sectors_monitor) {
|
||||
if(newVal) {
|
||||
// vector.sectors_monitor.show()
|
||||
changeSectorsInLayer('sectors_monitor', true)
|
||||
} else {
|
||||
// vector.sectors_monitor.hide()
|
||||
changeSectorsInLayer('sectors_monitor', false)
|
||||
}
|
||||
}
|
||||
})
|
||||
watch(() => sector.value.UAV, (newVal) => {
|
||||
if(vector.sectors_UAV) {
|
||||
if(newVal) {
|
||||
// vector.sectors_UAV.show()
|
||||
changeSectorsInLayer('sectors_UAV', true)
|
||||
} else {
|
||||
// vector.sectors_UAV.hide()
|
||||
changeSectorsInLayer('sectors_UAV', false)
|
||||
}
|
||||
}
|
||||
})
|
||||
}, { deep: true })
|
||||
// 定位数据变化
|
||||
watch(() => locateData.value.videoCode, () => {
|
||||
nextTick(() => {
|
||||
locateMoitor(locateData.value)
|
||||
})
|
||||
})
|
||||
onUnmounted(() => {
|
||||
globalMap.destroy()
|
||||
|
||||
@@ -402,8 +402,7 @@ const fences = [ {
|
||||
'lineColor': '#00FDF0FF',
|
||||
'lineWidth': '3',
|
||||
'longitude': '120.731175',
|
||||
'positionInfo':
|
||||
'120.51471127064674,28.13957549396699;120.51890739842734,28.13587527906429;120.53762858391006,28.139006238454016;120.54795751383156,28.139006238454016;120.55247642067222,28.137867718353533;120.55538143221263,28.132459582652743;120.55376753691239,28.124489200957314;120.55441309503249,28.11566415847298;120.55893200187315,28.10797723934722;120.56667869931427,28.105984244460032;120.57345705957525,28.107123103215212;120.57958986171613,28.10655367534892;120.58604544291708,28.10342176805678;120.58927323351755,28.097157679200365;120.5931465822381,28.0834893071102;120.58991879163763,28.06924956919361;120.58895045445749,28.051019950559496;120.5931465822381,28.041903981997688;120.59766548907875,28.034781593968088;120.61186776772081,28.026803959632165;120.62058280234207,28.02651903318312;120.63123451132361,28.024524526921667;120.64511401090562,28.02651903318312;120.66093018484791,28.0282285805638;120.66835410322899,28.028798423656696;120.68126526563086,28.024524526921667;120.6948219861528,28.021675168156403;120.71160649727524,28.021675168156403;120.72839100839767,28.01141685220725;120.73742882207898,27.999162583129444;120.73871993831916,27.989187149187458;120.76163725158248,27.977215409958912;120.77616230928459,27.97265439781902;120.78713679732618,27.97265439781902;120.79262404134698,27.970088743745798;120.80876299434931,27.968948433462458;120.81747802897057,27.967808111127816;120.83038919137243,27.957259558282324;120.84168645847407,27.952127457120493;120.84555980719463,27.996312553354493;120.83200308667267,27.988332069036062;120.82135137769113,27.986051822143104;120.81102244776964,27.987762011834025;120.79456071570725,27.99488751020321;120.78326344860561,28.019680572148907;120.76099169346239,28.0282285805638;120.73290991523832,28.03079285072198;120.72128986907664,28.03563630542758;120.69256253273248,28.034781593968088;120.67448690536987,28.040479542114323;120.66254408014814,28.03791550279268;120.6580251733075,28.03592120773863;120.64479123184557,28.038200399068412;120.62736116260305,28.03734570797788;120.61735501174161,28.03307215068109;120.60831719806029,28.033926875719207;120.60057050061918,28.039055083369046;120.59831104719885,28.05757157564859;120.60057050061918,28.072952082651348;120.60250717497945,28.087191329724334;120.59540603565843,28.097442418444963;120.58539988479698,28.108831368678235;120.57474817581544,28.113101913313074;120.56764703649439,28.11139371586372;120.56086867623343,28.113956001835078;120.55731810657291,28.12904377736259;120.557963664693,28.14042937156471;120.5434386069909,28.145837104956886;120.53085022364908,28.144983270467232;120.51567960782688,28.14014474645503;120.51471127064674,28.13957549396699'
|
||||
'positionInfo': '120.38200628800818,28.153871588450777;120.40161934924632,28.16665224935052;120.41526321793371,28.161013910606457;120.43146531199999,28.161013910606457;120.43999272992963,28.15838258420298;120.44681466427332,28.15199195078788;120.45448934040998,28.143721153079422;120.45917942027128,28.140713431912022;120.4655749837185,28.137329644652915;120.4813507068883,28.141841337246788;120.48945175392144,28.144097112287035;120.4992582845405,28.139585514701242;120.50991755695254,28.139209539658587;120.52697239281179,28.142969230705212;120.5401898906027,28.145600935920818;120.5517019048077,28.143721153079422;120.55937658094436,28.139585514701242;120.56193480632325,28.132441763265888;120.5610820645303,28.124169456077574;120.5610820645303,28.117024677005077;120.56577214439159,28.110255499765515;120.57685778770009,28.112135869634514;120.58495883473324,28.10912726202057;120.5930598817664,28.100853155967197;120.59860270342064,28.092578411842236;120.6037191541784,28.08467919733773;120.60116092879952,28.071512548408652;120.59945544521361,28.063988024404832;120.59860270342064,28.05081883965576;120.60116092879952,28.039529683128116;120.60840923403971,28.034261004599962;120.62247947362359,28.033131968492132;120.62844866617432,28.037271709637253;120.64763535651598,28.039906007435775;120.66000011251393,28.036142705120728;120.66767478865059,28.04705591914302;120.67065938492597,28.054581628489878;120.67449672299429,28.051947690132785;120.67236486851188,28.043292866963252;120.68430325361335,28.038777030563462;120.69965260588668,28.038777030563462;120.71500195816,28.038400702306493;120.72480848877908,28.037271709637253;120.74314243732776,28.031626568587686;120.75891816049757,28.02823934180657;120.77597299635681,28.021840955926812;120.78705863966533,28.013936542452893;120.79132234863015,28.003772872605627;120.79857065387033,27.99549065486699;120.80880355538586,27.98909032338784;120.83012210020993,27.98909032338784;120.85271975772343,27.997373032975464;120.87702289882287,27.99925537820563;120.89620958916451,27.995867133118892;120.91880724667803,27.98946682399441;120.94268401688097,27.98268961190924;120.95206417660356,27.95708296714755;120.93373022805487,27.966874460666574;120.92221821384989,27.97365266654279;120.91454353771323,27.97553542593344;120.89407773468211,27.979677380918556;120.8829920913736,27.98043044655396;120.86764273910028,27.978547772606063;120.84845604875862,27.976665065794414;120.84419233979382,27.974029221049765;120.86295265923899,27.964614964082905;120.88427120406304,27.950680356532175;120.90089966902582,27.93787399639349;120.91411716681674,27.92845658653934;120.90644249068008,27.899445808853542;120.88469757495953,27.914894074727723;120.87275918985806,27.92883329869066;120.85399887041288,27.94465402325662;120.83438580917475,27.95519988579228;120.82244742407327,27.964238376719482;120.81136178076476,27.971769874292207;120.8015552501457,27.9706401831697;120.77810485083923,27.973276110721436;120.77085654559905,27.97214643537107;120.75337533884331,27.980053914393565;120.7401578410524,27.987207800631985;120.73290953581223,28.006031548779006;120.72480848877908,28.01581859829085;120.71883929622834,28.020335398097348;120.71244373278111,28.022970110482113;120.6881405916817,28.022217342094482;120.67407035209781,28.025604758386503;120.66341107968577,28.028992068081955;120.64166616396524,28.02522838406238;120.62375858631303,28.024475631466153;120.60116092879952,28.031250215321077;120.58836980190509,28.038777030563462;120.58879617280158,28.049689977327688;120.58751706011213,28.062106811075683;120.58836980190509,28.071136334723224;120.59007528549101,28.077907979485328;120.5926335108699,28.08129364177948;120.58964891459453,28.09333068767149;120.58666431831915,28.101229265540294;120.58367972204378,28.10423809466379;120.57557867501066,28.107622926567224;120.56449303170214,28.10423809466379;120.55426013018659,28.11100765166906;120.55298101749715,28.117024677005077;120.55255464660067,28.125297535562492;120.55255464660067,28.129057714770116;120.5538337592901,28.13544971666095;120.55042279211827,28.13845758561478;120.5401898906027,28.13845758561478;120.53166247267309,28.13770562629301;120.5222823129505,28.13845758561478;120.51972408757162,28.136953661693376;120.50906481515958,28.13394575051819;120.50608021888421,28.132441763265888;120.49670005916163,28.131689761723855;120.4770869979235,28.13319375953069;120.46429587102905,28.13469773622831;120.45576845309942,28.13770562629301;120.45107837323813,28.141465370121423;120.44553555158389,28.146352839819542;120.43871361724018,28.15349566355806;120.4335971664824,28.156878940072858;120.42848071572463,28.158006675150574;120.42506974855277,28.1561271100874;120.41696870151965,28.156503025740147;120.4088676544865,28.15763076477806;120.39565015669558,28.15499935520908;120.38371177159411,28.14936040262422;120.38200628800818,28.153871588450777'
|
||||
} ]
|
||||
const detailFences = [ {
|
||||
'diaphaneity': '0.5',
|
||||
|
||||
@@ -184,7 +184,6 @@ const cleanupExpiredBoats = () => {
|
||||
|
||||
const getShip = () => {
|
||||
findByCurrent().then(res => {
|
||||
console.log('初始获取')
|
||||
addBoats(res.result)
|
||||
})
|
||||
// ----------船只数据------------------
|
||||
@@ -192,7 +191,6 @@ const getShip = () => {
|
||||
dynamicBoatInfoWebSocket.onopen(event => {
|
||||
})
|
||||
dynamicBoatInfoWebSocket.onmessage(event => {
|
||||
console.log('接收数据:', JSON.parse(event.data))
|
||||
if(Array.isArray(JSON.parse(event.data))) {
|
||||
addBoats(JSON.parse(event.data))
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
|
||||
<div :class="['screen-legend-left',isFold? 'fold':'']">
|
||||
<div :class="['screen-legend-left',isFold? 'fold':'',retract?'retract':'']">
|
||||
<div class="header" @click="toggleExpand">
|
||||
<span>图例 </span>
|
||||
<img src="@/assets/images/map/legend/icon-suffix.png" alt="">
|
||||
@@ -24,6 +24,12 @@
|
||||
|
||||
const mapStore = useMapStore()
|
||||
|
||||
const props = defineProps({
|
||||
retract: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
})
|
||||
const isFold = ref(true)
|
||||
|
||||
const list = ref([
|
||||
@@ -69,7 +75,7 @@
|
||||
return
|
||||
}
|
||||
item.sector = !item.sector
|
||||
mapStore.updateSector(item.prop, item.sector)
|
||||
mapStore.updateLegend('sectors_' + item.prop, item.sector)
|
||||
}
|
||||
const toggleExpand = () => {
|
||||
isFold.value = !isFold.value
|
||||
@@ -80,7 +86,7 @@
|
||||
mapStore.updateLegend(item.prop, item.checked)
|
||||
if(!item.checked && (item.prop === 'monitor' || item.prop === 'UAV')) {
|
||||
item.sector = false
|
||||
mapStore.updateSector(item.prop, item.sector)
|
||||
mapStore.updateLegend('sectors_' + item.prop, item.sector)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -152,6 +158,9 @@
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
&.retract{
|
||||
left: 40px;
|
||||
}
|
||||
}
|
||||
.screen-legend-left::before {
|
||||
content: '';
|
||||
|
||||
@@ -92,8 +92,8 @@ const getLocationSymbol = () => [
|
||||
}
|
||||
]
|
||||
const baseConfig = {
|
||||
center: [ 120.67, 28.01 ],
|
||||
zoom: 12,
|
||||
center: [ 120.68, 28.01 ],
|
||||
zoom: 10.5,
|
||||
maxZoom: 20,
|
||||
minZoom: 5,
|
||||
attribution: '',
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<DialogComponent @mousedown="drag" @resize="handleResize" :style="{ resize: 'both', overflow: 'auto' }" v-if="visible && option[type]" :title="option[type].title" :width="type==='_UAV' ? 1800:395" :draggable="true" :modal="false" @close="close">
|
||||
<DialogComponent @mousedown="drag" @resize="handleResize" :style="{ resize: 'both', overflow: 'auto' }" v-if="visible && option[type]" :title="title" :width="type==='_UAV' ? 1800:395" :draggable="true" :modal="false" @close="close">
|
||||
<component :is="option[type].component" ref="component"/>
|
||||
</DialogComponent>
|
||||
</template>
|
||||
@@ -33,6 +33,12 @@ const option = computed(() => ({
|
||||
component: MeteorologyComponent
|
||||
}
|
||||
}))
|
||||
const title = computed(() => {
|
||||
if(type.value === '_UAV') {
|
||||
return data.value.videoName + '--' + option.value[type.value].title
|
||||
}
|
||||
return option.value[type.value].title
|
||||
})
|
||||
const component = ref(null)
|
||||
const close = () => {
|
||||
mapStore.updateWindowInfo(false)
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed, nextTick, onMounted, ref } from 'vue'
|
||||
import { computed, nextTick, ref, watch } from 'vue'
|
||||
import useMapStore from '@/store/modules/map'
|
||||
import HikPlayerComponent from '@/components/Player/HikPlayer.vue'
|
||||
|
||||
@@ -16,11 +16,12 @@ const visible = ref(false)
|
||||
const data = computed(() => mapStore.windowInfo.data)
|
||||
const HikCCTV = ref(null)
|
||||
|
||||
onMounted(() => {
|
||||
watch(() => data.value.videoCode, () => {
|
||||
visible.value = false
|
||||
nextTick(() => {
|
||||
visible.value = true
|
||||
})
|
||||
})
|
||||
}, { immediate: true })
|
||||
defineExpose({
|
||||
HikCCTV
|
||||
})
|
||||
|
||||
@@ -31,10 +31,13 @@ import CockpitCom from '@/views/business/drone/cockpit.vue'
|
||||
import { getVideoStream, doStartOrStopUavAlgorithm } from '@/api/uav'
|
||||
import { ElMessage } from 'element-plus'
|
||||
|
||||
// sourceType1 的是接道通的视频流
|
||||
// sourceType2 的是擎天云的识别的视频流
|
||||
const mapStore = useMapStore()
|
||||
const data = computed(() => mapStore.windowInfo.data)
|
||||
const url = computed(() => `http://198.16.74.210:3456/embed?projectId=4bd996b8-5201-4e5d-82b1-6879be360c20&authInfoId=eyJhbGciOiJIUzUxMiIsImNyaXQiOlsidHlwIiwiYWxnIiwia2lkIl0sImtpZCI6IjU3YmQyNmEwLTYyMDktNGE5My1hNjg4LWY4NzUyYmU1ZDE5MSIsInR5cCI6IkpXVCJ9.eyJhY2NvdW50IjoiIiwiZXhwIjoyMDc1OTQ3NzIyLCJuYmYiOjE3NjA0MTQ5MjIsIm9yZ2FuaXphdGlvbl91dWlkIjoiOWRmMjlmYTgtNGI5OS00MThlLWJhMmQtMGY5ZWY5ZWVlMzkyIiwicHJvamVjdF91dWlkIjoiIiwic3ViIjoiZmgyIiwidXNlcl9pZCI6IjE3NjA0MTQxMDkzNTcwMDI0MjkifQ.DC_aS37W2fkqOjCtfvysDfhTn-4XVn3_IrXBnPD9rICGyrIBKBG3oPldeW_pqele5H_gCn1EgM0KXcbDgvq-dw&
|
||||
deviceSn=${data.value.droneSn}&deviceType=drone&token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoxLCJleHAiOjE3NjcxNzA1NTIsImlhdCI6MTc2NjU2NTc1Mn0._YhukLexaErvTc3QDIAV5MuOa6cqglYUfsixNCit3us`)
|
||||
const url = 'http://198.16.74.210:3456/embed?projectId=2&authInfoId=3&deviceSn=7601839813836800&deviceType=drone&token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoxLCJleHAiOjE3NjgxODQ5NDUsImlhdCI6MTc2NzU4MDE0NX0.AuUfl9Cbi_KZsIHrwRzwpLcpiMYB70NcXb0IYlha_5s'
|
||||
// const url = computed(() => `http://198.16.74.210:3456/embed?projectId=4bd996b8-5201-4e5d-82b1-6879be360c20&authInfoId=eyJhbGciOiJIUzUxMiIsImNyaXQiOlsidHlwIiwiYWxnIiwia2lkIl0sImtpZCI6IjU3YmQyNmEwLTYyMDktNGE5My1hNjg4LWY4NzUyYmU1ZDE5MSIsInR5cCI6IkpXVCJ9.eyJhY2NvdW50IjoiIiwiZXhwIjoyMDc1OTQ3NzIyLCJuYmYiOjE3NjA0MTQ5MjIsIm9yZ2FuaXphdGlvbl91dWlkIjoiOWRmMjlmYTgtNGI5OS00MThlLWJhMmQtMGY5ZWY5ZWVlMzkyIiwicHJvamVjdF91dWlkIjoiIiwic3ViIjoiZmgyIiwidXNlcl9pZCI6IjE3NjA0MTQxMDkzNTcwMDI0MjkifQ.DC_aS37W2fkqOjCtfvysDfhTn-4XVn3_IrXBnPD9rICGyrIBKBG3oPldeW_pqele5H_gCn1EgM0KXcbDgvq-dw&
|
||||
// deviceSn=${data.value.droneSn}&deviceType=drone&token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoxLCJleHAiOjE3NjcxNzA1NTIsImlhdCI6MTc2NjU2NTc1Mn0._YhukLexaErvTc3QDIAV5MuOa6cqglYUfsixNCit3us`)
|
||||
// 算法开启关闭状态
|
||||
const algorithmStatus = ref(false)
|
||||
const algorithms = [
|
||||
@@ -60,18 +63,20 @@ const toggleFly = () => {
|
||||
dialog.visible = true
|
||||
dialog.url = ''
|
||||
setTimeout(() => {
|
||||
const params = {
|
||||
if(data.value.droneSn) {
|
||||
const params = {
|
||||
status: 'stop',
|
||||
sn: data.value.droneSn,
|
||||
enable_orc: false
|
||||
}
|
||||
getVideoStream(params).then(res => {
|
||||
}
|
||||
getVideoStream(params).then(res => {
|
||||
if(res.success) {
|
||||
setTimeout(() => {
|
||||
dialog.url = res.result.httpUrl
|
||||
}, 10000)
|
||||
}
|
||||
})
|
||||
}
|
||||
}, 20000)
|
||||
}
|
||||
// 开启/关闭算法
|
||||
@@ -161,7 +166,7 @@ const stopResize = () => {
|
||||
document.removeEventListener('mouseup', stopResize)
|
||||
}
|
||||
watch(() => data.value.sourceType, (val) => {
|
||||
if(val === '2') {
|
||||
if(val === '2' && data.value.droneSn) {
|
||||
toggleFly()
|
||||
}
|
||||
}, { immediate: true })
|
||||
@@ -177,7 +182,7 @@ onUnmounted(() => {
|
||||
.content-wrapper{
|
||||
display: flex;
|
||||
position: relative;
|
||||
height: 850px;
|
||||
height: 800px;
|
||||
iframe{
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
@@ -196,10 +201,20 @@ onUnmounted(() => {
|
||||
//1音量的控制条
|
||||
video::-webkit-media-controls-volume-slider{display: none;}
|
||||
video::-webkit-media-controls-enclosuret{display: none;}
|
||||
.uav-button{
|
||||
width: fit-content;
|
||||
cursor: pointer;
|
||||
color: #00c0ff;
|
||||
padding: 6px 10px;
|
||||
background: rgba(22, 119, 255, 0.21);
|
||||
border-radius: 2px 2px 2px 2px;
|
||||
border: 1px solid #236ACE;
|
||||
color: rgba(255, 255, 255, 0.9);
|
||||
}
|
||||
.left-wrapper{
|
||||
// width: 60%;
|
||||
width: v-bind(leftWidth);
|
||||
height: 700px;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
@@ -241,7 +256,7 @@ onUnmounted(() => {
|
||||
.right-wrapper{
|
||||
// width: 40%;
|
||||
width: v-bind(rightWidth);
|
||||
height: 700px;
|
||||
height: 100%;
|
||||
// position: absolute;
|
||||
// right: 0;
|
||||
flex: 1;
|
||||
|
||||
@@ -283,8 +283,6 @@ export default {
|
||||
});
|
||||
},
|
||||
uninit() {
|
||||
console.log(999);
|
||||
|
||||
let that = this;
|
||||
if (that.oWebControl != null) {
|
||||
that.oWebControl.JS_RequestInterface({
|
||||
|
||||
@@ -14,9 +14,7 @@ const routes = [
|
||||
auth: false,
|
||||
title: '首页'
|
||||
},
|
||||
component: () => import('@/views/business/index.vue'),
|
||||
children: [
|
||||
]
|
||||
component: () => import('@/views/business/index.vue')
|
||||
},
|
||||
{
|
||||
path: '/login',
|
||||
@@ -71,7 +69,7 @@ router.beforeEach((to, from, next) => {
|
||||
// 没有token
|
||||
if (whiteList.indexOf(to.path) !== -1) {
|
||||
// 在免登录白名单,直接进入
|
||||
next()
|
||||
next()
|
||||
} else {
|
||||
// next(`/login?redirect=${to.fullPath}`) // 否则全部重定向到登录页
|
||||
next('/login') // 否则全部重定向到登录页
|
||||
|
||||
@@ -6,33 +6,29 @@ const useMapStore = defineStore(
|
||||
state: () => ({
|
||||
legend: {
|
||||
UAV: true,
|
||||
sectors_UAV: false, // 无人机可视域开关
|
||||
monitor: true,
|
||||
origin_monitor: false,
|
||||
sectors_monitor: false, // 监控可视域开关
|
||||
ais_station: true,
|
||||
environmental: true,
|
||||
fence: true
|
||||
},
|
||||
windowInfo: {
|
||||
visible: false,
|
||||
type: '',
|
||||
type: '', // 弹窗类型 _monitor/_UAV/_trawler_dynamic
|
||||
data: {}
|
||||
},
|
||||
sector: {
|
||||
'UAV': false, // 无人机可视域开关
|
||||
'monitor': false // 监控可视域开关
|
||||
},
|
||||
largeModel: false, // 智能体
|
||||
hik: { // 海康插件设备层级
|
||||
level: 1,
|
||||
data: {}
|
||||
},
|
||||
dialog: {
|
||||
visible: false,
|
||||
type: '' // 弹窗类型 alarm/CCTV/UAV
|
||||
},
|
||||
// 无人机信息
|
||||
uavs: {
|
||||
data: {}
|
||||
},
|
||||
// cctv轮询定位
|
||||
locate: {
|
||||
data: {}
|
||||
}
|
||||
}),
|
||||
actions: {
|
||||
@@ -45,24 +41,16 @@ const useMapStore = defineStore(
|
||||
this.windowInfo.type = type
|
||||
this.windowInfo.data = data
|
||||
},
|
||||
updateSector(type, checked) {
|
||||
this.sector[type] = checked
|
||||
},
|
||||
updateLargeModel(option) {
|
||||
this.largeModel = option
|
||||
},
|
||||
updateHik(option) {
|
||||
const { level = 1, data = {} } = option
|
||||
this.hik.level = level
|
||||
this.hik.data = data
|
||||
},
|
||||
updateDialog(option) {
|
||||
const { visible = false, type = '' } = option
|
||||
this.dialog.visible = visible
|
||||
this.dialog.type = type
|
||||
},
|
||||
updateUavData(option) {
|
||||
this.uavs.data = option
|
||||
},
|
||||
updateLocate(option) {
|
||||
this.locate.data = option
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
import { defineStore } from 'pinia'
|
||||
|
||||
const useToolBarStore = defineStore(
|
||||
'toolbar',
|
||||
{
|
||||
state: () => ({
|
||||
expand: false, //地图图例位置
|
||||
trawler: {
|
||||
visible: false // 渔船动态详情收缩框显示
|
||||
}
|
||||
}),
|
||||
actions: {
|
||||
toogleExpand(expand) {
|
||||
this.expand = expand
|
||||
},
|
||||
toggleTrawlerVisible(visible) {
|
||||
this.trawler.visible = visible
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
export default useToolBarStore
|
||||
@@ -5,11 +5,11 @@
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import useMapStore from '@/store/modules/map'
|
||||
import { useRouter } from 'vue-router'
|
||||
|
||||
const mapStore = useMapStore()
|
||||
const router = useRouter()
|
||||
const handle = (type) => {
|
||||
mapStore.updateDialog({ visible: true, type })
|
||||
router.push({ path: '/wall', query: { type } })
|
||||
}
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<template>
|
||||
<ListCom/>
|
||||
<ListCom @toggle-fold="toggle"/>
|
||||
<DeviceCom/>
|
||||
<MonitorCom/>
|
||||
<LegendCom/>
|
||||
<LegendCom :retract="retract"/>
|
||||
|
||||
</template>
|
||||
<script setup>
|
||||
@@ -10,7 +10,12 @@ import ListCom from './list.vue'
|
||||
import DeviceCom from './device.vue'
|
||||
import MonitorCom from './monitor.vue'
|
||||
import LegendCom from '@/components/Map/legend.vue'
|
||||
import { ref } from 'vue'
|
||||
|
||||
const retract = ref(null)
|
||||
const toggle = (value) => {
|
||||
retract.value = value
|
||||
}
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
.screen-legend-left{
|
||||
|
||||
@@ -61,11 +61,12 @@
|
||||
</template>
|
||||
<script setup>
|
||||
import SubtitleComponent from '@/components/SubtItle/index.vue'
|
||||
import { reactive, ref } from 'vue'
|
||||
import useMapStore from '@/store/modules/map'
|
||||
import { ref } from 'vue'
|
||||
import { videoIdentificationPage } from '@/api/identification.js'
|
||||
import { useRouter } from 'vue-router'
|
||||
|
||||
const mapStore = useMapStore()
|
||||
const router = useRouter()
|
||||
const emit = defineEmits([ 'toggle-fold' ])
|
||||
const tabs = [
|
||||
{
|
||||
label: '无船号',
|
||||
@@ -109,6 +110,7 @@ const initData = () => {
|
||||
}
|
||||
const toggleFold = () => {
|
||||
isFold.value = !isFold.value
|
||||
emit('toggle-fold', isFold.value)
|
||||
}
|
||||
const toggle = (index) => {
|
||||
current.value = index
|
||||
@@ -118,7 +120,7 @@ const toggle = (index) => {
|
||||
const handle = (type, item) => {
|
||||
switch (type) {
|
||||
case 'more':
|
||||
mapStore.updateDialog({ visible: true, type: 'alarm' })
|
||||
router.push({ path: '/identification', query: { isHasBoatName: current.value == 1 ? 1 : 2 } })
|
||||
break
|
||||
default:
|
||||
break
|
||||
|
||||
@@ -3,46 +3,118 @@
|
||||
<div class="tab">
|
||||
<div v-for="(item,index) in tabs"
|
||||
:key="index"
|
||||
:class="['tab-item',active === index ? 'active' : '']"
|
||||
:class="['tab-item']"
|
||||
@click="handle(index)">{{item}}</div>
|
||||
</div>
|
||||
<div class="resize" :style="{ resize: 'both', overflow: 'auto' }"></div>
|
||||
<!-- <div class="flv-container"><FlvPlayerComponent :url="uavDialog.url"/></div> -->
|
||||
<div class="flv-container"><FlvPlayerComponent v-if="videoUrl" :url="videoUrl"/></div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import { computed, nextTick, onMounted, onUnmounted, ref } from 'vue'
|
||||
import { dragEvent, resizeEvent } from '@/utils/common'
|
||||
import FlvPlayerComponent from '@/components/FlvPlayer/index.vue'
|
||||
import { getAssetsFile } from '@/utils/common'
|
||||
import { dsVideoList, cctvExport } from '@/api/device.js'
|
||||
import useMapStore from '@/store/modules/map'
|
||||
import { ElMessage } from 'element-plus'
|
||||
|
||||
const mapStore = useMapStore()
|
||||
const videoData = computed(() => mapStore.locate.data)
|
||||
const tabs = [
|
||||
'重新轮询', '巡检报告'
|
||||
'锁定轮询', '重新轮询', '巡检报告'
|
||||
]
|
||||
const active = ref(0)
|
||||
const data = ref([])
|
||||
const lock = ref(false)
|
||||
let timer = null
|
||||
let monitorIndex = 0
|
||||
const videoUrl = ref('')
|
||||
const time = ref(10000)
|
||||
|
||||
const handle = (index) => {
|
||||
active.value = index
|
||||
if(index === 0) {
|
||||
lock.value = true
|
||||
ElMessage.success('锁定成功')
|
||||
}else if(index === 1) {
|
||||
lock.value = false
|
||||
ElMessage.success('成功轮询')
|
||||
}else if(index === 2) { // 巡检报告下载
|
||||
cctvExport({}).then(res => {
|
||||
const blob = new Blob([ res ], { type: 'application/pdf' })
|
||||
const pdfUrl = URL.createObjectURL(blob)
|
||||
window.open(pdfUrl, '_blank')
|
||||
})
|
||||
}
|
||||
}
|
||||
const clearTimer = () => {
|
||||
if (timer) {
|
||||
clearInterval(timer)
|
||||
timer = null
|
||||
}
|
||||
}
|
||||
const initIndex = () => {
|
||||
monitorIndex = 0
|
||||
}
|
||||
const init = () => {
|
||||
const params = {}
|
||||
videoUrl.value = ''
|
||||
dsVideoList(params).then(res => {
|
||||
data.value = res.data.filter(i => i.videoUrl && i.bePlay === 1).sort((a, b) => a.playIndex - b.playIndex)
|
||||
// 启动轮播定时器
|
||||
startPolling()
|
||||
})
|
||||
}
|
||||
|
||||
// 启动轮播
|
||||
const startPolling = () => {
|
||||
clearTimer()
|
||||
const runPolling = () => {
|
||||
if (!lock.value) {
|
||||
switchToNextVideo()
|
||||
}
|
||||
timer = setTimeout(runPolling, time.value)
|
||||
}
|
||||
runPolling()
|
||||
}
|
||||
|
||||
// 切换到下一个视频
|
||||
const switchToNextVideo = () => {
|
||||
videoUrl.value = ''
|
||||
time.value = data.value[monitorIndex].playTime * 1000 || 10 * 1000
|
||||
mapStore.updateLocate(data.value[monitorIndex])
|
||||
nextTick(() => {
|
||||
videoUrl.value = data.value[monitorIndex].videoUrl
|
||||
// 更新播放时间
|
||||
if (monitorIndex >= data.value.length - 1) {
|
||||
monitorIndex = 0 // 回到第一个
|
||||
} else {
|
||||
monitorIndex++
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 拖拽事件
|
||||
* @param e
|
||||
*
|
||||
*/
|
||||
const drag = (e) => {
|
||||
const drag = (e) => {
|
||||
if(e.target.className === 'resize') {
|
||||
resizeEvent(e, 'resize', (i) => {
|
||||
document.querySelector('.monitor-container').style.height = i.height + 'px'
|
||||
document.querySelector('.monitor-container').style.width = i.width + 'px'
|
||||
resizeEvent(e, 'resize', (i) => {
|
||||
document.querySelector('.monitor-container').style.height = i.height + 'px'
|
||||
document.querySelector('.monitor-container').style.width = i.width + 'px'
|
||||
})
|
||||
}else{
|
||||
dragEvent(e, 'monitor-container', () => {
|
||||
})
|
||||
dragEvent(e, 'monitor-container', () => { /* empty */ })
|
||||
}
|
||||
|
||||
}
|
||||
const resize = (e) => {
|
||||
|
||||
}
|
||||
onMounted(() => {
|
||||
// // 查询当前轮询到第几个
|
||||
// initIndex()
|
||||
init()
|
||||
})
|
||||
onUnmounted(() => {
|
||||
clearTimer()
|
||||
})
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
|
||||
@@ -82,7 +154,8 @@ const resize = (e) => {
|
||||
text-align: center;
|
||||
line-height: 36px;
|
||||
box-sizing: border-box;
|
||||
&.active{
|
||||
cursor: pointer;
|
||||
&:active{
|
||||
background: linear-gradient( 180deg, #2EA4F0 0%, rgba(46,164,240,0.35) 100%);
|
||||
box-shadow: inset 0px 2px 4px 0px rgba(83,203,255,0.4);
|
||||
// border-radius: 2px 2px 0px 0px;
|
||||
@@ -94,6 +167,7 @@ const resize = (e) => {
|
||||
}
|
||||
.flv-container{
|
||||
width: 100%;
|
||||
height: calc(100% - 50px);
|
||||
margin-top: 10px;
|
||||
}
|
||||
.resize{
|
||||
@@ -101,7 +175,7 @@ const resize = (e) => {
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
width: 524px;
|
||||
height: 386px;
|
||||
height: 386px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -22,11 +22,10 @@
|
||||
</template>
|
||||
<script setup>
|
||||
import { computed, nextTick, onMounted, ref } from 'vue'
|
||||
// import { useStore } from 'vuex'
|
||||
|
||||
import useMapStore from '@/store/modules/map'
|
||||
const mapStore = useMapStore()
|
||||
const Detail = ref(null)
|
||||
// const store = useStore()
|
||||
// const data = computed(() => store.state.mapStore.uavs.data)
|
||||
const data = computed(() => mapStore.windowInfo.data)
|
||||
const ip = '198.16.74.210'
|
||||
|
||||
onMounted(() => {
|
||||
@@ -46,154 +45,8 @@ onMounted(() => {
|
||||
drone_sn: data.value.droneSn // 飞机 sn,按实际传入
|
||||
})
|
||||
}
|
||||
// // 使用轮询方式等待内容加载完成后再滚动
|
||||
// waitForContentAndScroll()
|
||||
})
|
||||
const waitForContentAndScroll = () => {
|
||||
let attempts = 0
|
||||
let previousScrollWidth = 0
|
||||
let stableCount = 0
|
||||
const maxAttempts = 30
|
||||
const checkAndScroll = () => {
|
||||
attempts++
|
||||
|
||||
if (Detail.value) {
|
||||
// 检查目标容器是否已加载内容
|
||||
const mapContainer = document.getElementById('map-app-global')
|
||||
|
||||
// 判断内容是否加载完成
|
||||
const isContentLoaded = mapContainer && mapContainer.children.length > 0
|
||||
// console.log('Content loaded:', isContentLoaded, mapContainer)
|
||||
// 获取当前尺寸
|
||||
const currentScrollWidth = Detail.value.scrollWidth
|
||||
const clientWidth = Detail.value.clientWidth
|
||||
|
||||
// console.log('Size check:', {
|
||||
// attempt: attempts,
|
||||
// scrollWidth: currentScrollWidth,
|
||||
// clientWidth: clientWidth,
|
||||
// isContentLoaded: isContentLoaded
|
||||
// })
|
||||
|
||||
// 检查尺寸是否稳定(连续几次尺寸相同)
|
||||
if (currentScrollWidth === previousScrollWidth && currentScrollWidth > clientWidth) {
|
||||
stableCount++
|
||||
} else {
|
||||
stableCount = 0
|
||||
}
|
||||
previousScrollWidth = currentScrollWidth
|
||||
// 当内容加载完成且尺寸稳定,或者达到最大尝试次数时执行滚动
|
||||
if (isContentLoaded && stableCount >= 2 && currentScrollWidth > clientWidth ||
|
||||
attempts >= maxAttempts) {
|
||||
|
||||
// 强制重新计算布局
|
||||
forceLayoutRecalculation(() => {
|
||||
executeScroll()
|
||||
})
|
||||
} else {
|
||||
// 内容未加载完成或尺寸不稳定,继续等待
|
||||
setTimeout(checkAndScroll, 300)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 开始检查
|
||||
setTimeout(checkAndScroll, 300)
|
||||
}
|
||||
const forceLayoutRecalculation = (callback) => {
|
||||
if (!Detail.value) {
|
||||
if (callback) {
|
||||
callback()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// 方法1: 强制重排
|
||||
Detail.value.style.display = 'none'
|
||||
void Detail.value.offsetHeight // 强制重排
|
||||
Detail.value.style.display = 'flex'
|
||||
|
||||
// 方法2: 临时修改overflow触发重新计算
|
||||
const originalOverflow = Detail.value.style.overflow
|
||||
Detail.value.style.overflow = 'hidden'
|
||||
void Detail.value.offsetWidth
|
||||
Detail.value.style.overflow = originalOverflow
|
||||
|
||||
setTimeout(() => {
|
||||
if (callback) {
|
||||
callback()
|
||||
}
|
||||
}, 100)
|
||||
}
|
||||
|
||||
const executeScroll = () => {
|
||||
if (!Detail.value) {
|
||||
return
|
||||
}
|
||||
|
||||
// 重新获取准确的尺寸
|
||||
const scrollWidth = Detail.value.scrollWidth
|
||||
const clientWidth = Detail.value.clientWidth
|
||||
const maxScroll = scrollWidth - clientWidth
|
||||
|
||||
// console.log('Final size calculation:', {
|
||||
// scrollWidth,
|
||||
// clientWidth,
|
||||
// maxScroll
|
||||
// })
|
||||
|
||||
if (maxScroll > 0) {
|
||||
const targetPosition = 1388
|
||||
const scrollPosition = targetPosition > maxScroll ? maxScroll : targetPosition
|
||||
|
||||
// 修复:正确处理 scrollPosition 为 0 的情况
|
||||
const finalPosition = typeof scrollPosition === 'number' ? scrollPosition : 645
|
||||
|
||||
// 激活滚动机制后再执行滚动
|
||||
activateScrollMechanism(() => {
|
||||
Detail.value.scrollTo({
|
||||
left: finalPosition,
|
||||
behavior: 'smooth'
|
||||
})
|
||||
|
||||
// console.log('Scrolled to position:', finalPosition,
|
||||
// 'Max scroll:', maxScroll,
|
||||
// 'Container width:', clientWidth,
|
||||
// 'Scroll width:', scrollWidth)
|
||||
})
|
||||
} else {
|
||||
// console.log('No scrollable content, but content is loaded')
|
||||
// 即使没有滚动内容,也尝试激活滚动机制
|
||||
activateScrollMechanism(() => {
|
||||
Detail.value.scrollTo({
|
||||
left: 645,
|
||||
behavior: 'smooth'
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const activateScrollMechanism = (callback) => {
|
||||
if (!Detail.value) {
|
||||
if (callback) {
|
||||
callback()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// 预激活滚动机制
|
||||
Detail.value.scrollTo({ left: 1, behavior: 'instant' })
|
||||
|
||||
setTimeout(() => {
|
||||
Detail.value.scrollTo({ left: 0, behavior: 'instant' })
|
||||
|
||||
setTimeout(() => {
|
||||
if (callback) {
|
||||
callback()
|
||||
}
|
||||
}, 50)
|
||||
}, 20)
|
||||
}
|
||||
</script>
|
||||
<style lang="scss">
|
||||
.fh2-container {
|
||||
|
||||
@@ -203,7 +203,6 @@ const rules = {
|
||||
model[key] = props.data[key]
|
||||
if(key === 'illegalType') {
|
||||
model[key] = props.data[key].map(i => i.label)
|
||||
console.log(model[key])
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -5,14 +5,6 @@
|
||||
<FilterCom ref="Filter" :filter-buttons="filterButtons" :filter-items="items" :filter-model="model" @handle="handle"/>
|
||||
<!-- 多选框筛选 -->
|
||||
<div class="Form">
|
||||
<!-- 暂时只支持单个类型的筛选 -->
|
||||
<!-- <el-checkbox-group v-model="illegalType" :max="1">
|
||||
<el-checkbox
|
||||
v-for="item in illegalTypes"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value" />
|
||||
</el-checkbox-group> -->
|
||||
<el-radio-group v-model="illegalType" @change="changeRadio">
|
||||
<el-radio v-for="item in illegalTypes"
|
||||
:key="item.value"
|
||||
@@ -255,7 +247,6 @@ const columns = ref([
|
||||
])
|
||||
const config = {
|
||||
label: '操作',
|
||||
// width: 60
|
||||
width: 150
|
||||
}
|
||||
const operate = [
|
||||
@@ -341,7 +332,6 @@ const initData = () => {
|
||||
const params = new FormData()
|
||||
const obj = {
|
||||
...model,
|
||||
// illegalType: illegalType.value.join(','),
|
||||
illegalType: illegalType.value,
|
||||
beginTime: model.time && model.time[0] ? model.time[0] : '',
|
||||
endTime: model.time && model.time[1] ? model.time[1] : '',
|
||||
@@ -349,9 +339,6 @@ const initData = () => {
|
||||
pageSize: pagination.size
|
||||
}
|
||||
delete obj.time
|
||||
// Object.keys(obj).forEach((key) => {
|
||||
// params.append(key, obj[key])
|
||||
// })
|
||||
videoIdentificationPage(obj).then(res => {
|
||||
if (res.success) {
|
||||
tableData.value = res.result.records.map(i => {
|
||||
@@ -375,63 +362,6 @@ const initData = () => {
|
||||
}).finally(() => {
|
||||
loading.value = false
|
||||
})
|
||||
// tableData.value = [
|
||||
// {
|
||||
// 'aisStatus': '未开启',
|
||||
// 'belongPort': '',
|
||||
// 'boatCodePath': '',
|
||||
// 'boatName': '浙周田货0998',
|
||||
// 'boatNameEn': 'WUCHUANHAO',
|
||||
// 'cog': '0',
|
||||
// 'createAt': '2025-12-01 11:04:44',
|
||||
// 'createBy': '',
|
||||
// 'crossLineTime': null,
|
||||
// 'delFlag': 0,
|
||||
// 'distance': null,
|
||||
// 'draftMarks': null,
|
||||
// 'entryOut': '',
|
||||
// 'height': 0.00000000,
|
||||
// 'heightRange': '',
|
||||
// 'hkResult': '',
|
||||
// 'id': 76168,
|
||||
// 'identificationType': '',
|
||||
// 'illegalType': '未穿救生衣预警,未封舱预警,未悬挂国旗',
|
||||
// 'isCloseDoor': '否',
|
||||
// 'isHasAis': '否',
|
||||
// 'jacketStatus': '',
|
||||
// 'latitude': null,
|
||||
// 'length': 0.00000000,
|
||||
// 'longitude': null,
|
||||
// 'mmsi': '413823183',
|
||||
// 'shipType': '集装箱船',
|
||||
// 'sourcePicPath': 'http://198.16.74.209:6060/pic/2025-12-01/198.16.74.187/20251201_110559/ship_1_20251201_110559_644.jpg',
|
||||
// 'speed': null,
|
||||
// 'streetName': '',
|
||||
// 'sysShipName': '',
|
||||
// 'sysUpdateName': '',
|
||||
// 'systemResult': '',
|
||||
// 'takeTime': '2025-12-23 11:06:08',
|
||||
// 'takeType': '卡口',
|
||||
// 'trackerPicPath': 'http://198.16.74.209:6060/pic/2025-12-01/198.16.74.187/20251201_110559/ship_1_20251201_110559_644.jpg,http://198.16.74.209:6060/pic/2025-12-01/198.16.74.187/20251201_110559/ship_2_20251201_110603_161.jpg,http://198.16.74.209:6060/pic/2025-12-01/198.16.74.187/20251201_110559/ship_3_20251201_110605_535.jpg,http://198.16.74.209:6060/pic/2025-12-01/198.16.74.187/20251201_110559/ship_4_20251201_110608_298.jpg',
|
||||
// 'updateAt': '2025-12-01 11:04:44',
|
||||
// 'updateBy': '',
|
||||
// 'videoCode': 'fd3b45e1429a4e47bba873af602a9bed',
|
||||
// 'videoName': '卧旗--雷云一体机',
|
||||
// 'videoUrl': '',
|
||||
// 'width': 0.00000000
|
||||
// }, {}
|
||||
// ].map(i => {
|
||||
// return {
|
||||
// ...i,
|
||||
// illegalType: i.illegalType?.split(',').map(j => {
|
||||
// return {
|
||||
// value: illegalTypes.find(type => type.value === j)?.prop,
|
||||
// label: j
|
||||
// }
|
||||
// })
|
||||
// }
|
||||
// })
|
||||
// handle('check', tableData.value.length > 0 ? tableData.value[0] : {})
|
||||
}
|
||||
const changeRadio = () => {
|
||||
nextTick(() => {
|
||||
@@ -523,15 +453,7 @@ const closeDetail = () => {
|
||||
initData()
|
||||
}
|
||||
onMounted(() => {
|
||||
if(Object.keys(route.query).length > 0 && route.query.type === 'alarm') {
|
||||
model.takeType = route.query.takeType
|
||||
illegalType.value = route.query.illegalType
|
||||
nextTick(() => {
|
||||
if (Filter.value && Filter.value.model) {
|
||||
Filter.value.model.takeType = route.query.takeType
|
||||
}
|
||||
})
|
||||
}
|
||||
model.isHasBoatName = route.query.isHasBoatName
|
||||
|
||||
initData()
|
||||
})
|
||||
@@ -550,7 +472,7 @@ defineExpose({
|
||||
gap: 20px;
|
||||
height: 812px;
|
||||
.identificate-index{
|
||||
width: 1200px;
|
||||
width: 67.95%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
@@ -637,7 +559,7 @@ defineExpose({
|
||||
}
|
||||
}
|
||||
.identificate-detail{
|
||||
width: 545px;
|
||||
width: 30.8%;
|
||||
height: 802px;
|
||||
overflow: auto;
|
||||
// 单个设备抓拍详情
|
||||
|
||||
@@ -1,22 +1,18 @@
|
||||
<template>
|
||||
<!-- 识别记录页面 -->
|
||||
<DialogComponent :style="{ resize: 'both', overflow: 'auto' }" v-if="type === 'alarm'" title="识别记录" width="1800" :draggable="true" :modal="false" @close="close">
|
||||
<DialogComponent :style="{ resize: 'both', overflow: 'auto' }" title="识别记录" width="1800" :draggable="true" :modal="false" @close="close">
|
||||
<AlarmCom/>
|
||||
</DialogComponent>
|
||||
</template>
|
||||
<script setup>
|
||||
import { computed } from 'vue'
|
||||
import DialogComponent from '@/components/Dialog/screen.vue'
|
||||
import AlarmCom from './alarm/index.vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import useMapStore from '@/store/modules/map'
|
||||
|
||||
const mapStore = useMapStore()
|
||||
const router = useRouter()
|
||||
const type = computed(() => mapStore.dialog.type)
|
||||
|
||||
const close = () => {
|
||||
mapStore.updateDialog(false)
|
||||
router.push('/')
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
@@ -6,27 +6,13 @@
|
||||
<router-view></router-view>
|
||||
</div>
|
||||
<Alarm/>
|
||||
<LargeModelCom v-if="largeModel"/>
|
||||
<IdentificationCom v-if="visible && type === 'alarm'"/>
|
||||
<WallCom v-if="visible && (type==='CCTV'|| type==='UAV')"/>
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import HeaderMenuComponent from '@/components/HeaderMenu/index.vue'
|
||||
import MapComponent from '@/components/Map/index.vue'
|
||||
import Alarm from '@/views/business/alarm/index.vue'
|
||||
import LargeModelCom from '@/views/business/largeModel/index.vue'
|
||||
import IdentificationCom from '@/views/business/identification/index.vue'
|
||||
import WallCom from '@/views/business/wall/index.vue'
|
||||
import useMapStore from '@/store/modules/map'
|
||||
import { computed } from 'vue'
|
||||
|
||||
const mapStore = useMapStore()
|
||||
const largeModel = computed(() => {
|
||||
return mapStore.largeModel
|
||||
})
|
||||
const visible = computed(() => mapStore.dialog.visible)
|
||||
const type = computed(() => mapStore.dialog.type)
|
||||
window.name = 'business_window'
|
||||
</script>
|
||||
|
||||
@@ -205,7 +191,7 @@ window.name = 'business_window'
|
||||
position: absolute;
|
||||
right: 7px;
|
||||
i:not(.el-select__clear) {
|
||||
// background: url("@/assets/images/common/icon_suffix.png") center center no-repeat;
|
||||
background: url("@/assets/images/common/icon_suffix.png") center center no-repeat;
|
||||
background-size: 8px 6px;
|
||||
width: 8px;
|
||||
height: 6px;
|
||||
@@ -233,7 +219,7 @@ window.name = 'business_window'
|
||||
right: 7px;
|
||||
top: 13px;
|
||||
.el-input__suffix-inner{
|
||||
// background: url("@/assets/images/common/icon_suffix.png") center center no-repeat;
|
||||
background: url("@/assets/images/common/icon_suffix.png") center center no-repeat;
|
||||
background-size: 8px 6px;
|
||||
width: 8px;
|
||||
height: 6px;
|
||||
|
||||
@@ -256,13 +256,11 @@ const send = () => {
|
||||
additionalContent += '\n' + data.mp4_url.join(' ') + '\n'
|
||||
}
|
||||
if(data.rtsp_url && Array.isArray(data.rtsp_url)) {
|
||||
console.log('data.rtsp_url', data.rtsp_url)
|
||||
data.rtsp_url.forEach(item => {
|
||||
additionalContent += '\n' + JSON.stringify(item) + '\n'
|
||||
})
|
||||
}
|
||||
if(data.rtsp_data && Array.isArray(data.rtsp_data)) {
|
||||
console.log('data.rtsp_data', data.rtsp_data)
|
||||
data.rtsp_data.forEach(item => {
|
||||
additionalContent += '\n' + JSON.stringify(item) + '\n'
|
||||
})
|
||||
|
||||
@@ -11,13 +11,13 @@
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="border">
|
||||
|
||||
</div>
|
||||
<div :class="['border',show ? 'history-collapse' : 'history-expand']">
|
||||
<img
|
||||
:class="[show ? 'history-collapse' : 'history-expand']"
|
||||
src="@/assets/images/largeModel/icon-collapse.png" alt=""
|
||||
@click="show = !show">
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
@@ -174,24 +174,17 @@ defineExpose({
|
||||
right: 0;
|
||||
}
|
||||
}
|
||||
.border{
|
||||
width: 20px;
|
||||
.history-collapse{
|
||||
position: absolute;
|
||||
right: 160px;
|
||||
bottom: 40px;
|
||||
.history-collapse{
|
||||
display: none;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease-in-out;
|
||||
transform: translateX(0);
|
||||
transform: rotateY(180deg);
|
||||
}
|
||||
.history-expand{
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
bottom: 20px;
|
||||
width: 20px;
|
||||
cursor: pointer;
|
||||
z-index: 2;
|
||||
transition: all 0.3s ease-in-out;
|
||||
transform: translateX(0);
|
||||
transform: rotateY(180deg);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -54,7 +54,9 @@ import { dragEvent } from '@/utils/common'
|
||||
import { getVideoStream, doStartOrStopUavAlgorithm } from '@/api/uav'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import useMapStore from '@/store/modules/map'
|
||||
import { useRouter } from 'vue-router'
|
||||
|
||||
const router = useRouter()
|
||||
const mapStore = useMapStore()
|
||||
const uavData = computed(() => mapStore.uavs.data)
|
||||
const ModuleLeft = ref(null)
|
||||
@@ -105,7 +107,7 @@ const initList = () => {
|
||||
History.value.initList()
|
||||
}
|
||||
const close = () => {
|
||||
mapStore.updateLargeModel(false)
|
||||
router.push('/')
|
||||
}
|
||||
const openVideo = (data) => {
|
||||
visible.value = data.visible
|
||||
|
||||
@@ -70,7 +70,6 @@ const renderMedia = (url, text) => {
|
||||
if (!url) {
|
||||
return ''
|
||||
}
|
||||
console.log(url, 'url')
|
||||
// 监控
|
||||
if (url.startsWith('{') && url.includes('rtsp_url')) {
|
||||
const videoNameMatch = JSON.parse(url).video_name
|
||||
@@ -193,7 +192,29 @@ return ''
|
||||
// 监控视频查看
|
||||
const openVideoHandler = (index) => {
|
||||
const data = videoArr.value[index]
|
||||
codes.value = [ data.videoCode ]
|
||||
// 雷云一体机一个视频
|
||||
if(data.videoName.indexOf('雷云') !== -1) {
|
||||
const videos = videoArr.value.filter(i => i.videoName.indexOf('雷云') !== -1).sort((a, b) => {
|
||||
const index1 = a.videoName[a.videoName.length - 1]
|
||||
const index2 = b.videoName[b.videoName.length - 1]
|
||||
return index1 - index2
|
||||
})
|
||||
|
||||
codes.value = [ videos[0].videoCode ]
|
||||
}else{
|
||||
if(data.videoName.indexOf('球机') !== -1) {
|
||||
const videos = videoArr.value.filter(i => i.videoName.indexOf('球机') !== -1).sort((a, b) => {
|
||||
const index1 = a.videoName[a.videoName.length - 1]
|
||||
const index2 = b.videoName[b.videoName.length - 1]
|
||||
return index1 - index2
|
||||
})
|
||||
codes.value = videos.map((i, index) => {
|
||||
return i.previewUrl
|
||||
})
|
||||
}else{
|
||||
codes.value = [ data.videoCode ]
|
||||
}
|
||||
}
|
||||
nextTick(() => {
|
||||
setTimeout(() => {
|
||||
visible.value = true
|
||||
|
||||
85
src/views/business/setting/index.vue
Normal file
85
src/views/business/setting/index.vue
Normal file
@@ -0,0 +1,85 @@
|
||||
<template>
|
||||
<!-- 业务配置页面 -->
|
||||
<DialogComponent :style="{ resize: 'both', overflow: 'auto' }" title="业务配置" width="1800" :draggable="true" :modal="false" @close="close">
|
||||
<div class="tabs-container">
|
||||
<el-tabs v-model="type" tab-position="left" class="tabs">
|
||||
<el-tab-pane v-for="item in tabs"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:name="item.value">
|
||||
<template #label>
|
||||
<span class="custom-tabs-label">
|
||||
<span>{{item.label}}</span>
|
||||
<el-icon v-if="type == item.value"><CaretRight /></el-icon>
|
||||
</span>
|
||||
</template>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
<CCTVComponent v-if="type === 'cctv'"/>
|
||||
</div>
|
||||
</DialogComponent>
|
||||
</template>
|
||||
<script setup>
|
||||
import { CaretRight } from '@element-plus/icons-vue'
|
||||
import DialogComponent from '@/components/Dialog/screen.vue'
|
||||
import CCTVComponent from './monitor/index.vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { ref } from 'vue'
|
||||
|
||||
const router = useRouter()
|
||||
const tabs = ref([
|
||||
{
|
||||
value: 'cctv',
|
||||
label: 'CCTV'
|
||||
},
|
||||
{
|
||||
value: 'UAV',
|
||||
label: '无人机'
|
||||
}
|
||||
])
|
||||
const type = ref('cctv')
|
||||
|
||||
const close = () => {
|
||||
router.push('/')
|
||||
}
|
||||
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
.tabs-container{
|
||||
display: flex;
|
||||
width: 100%;
|
||||
}
|
||||
.el-tabs {
|
||||
box-sizing: border-box;
|
||||
:deep(.el-tabs__header){
|
||||
&.is-left{
|
||||
margin-right: 20px !important;
|
||||
}
|
||||
.el-tabs__nav-wrap::after {
|
||||
background-color: rgba(0, 192, 255, 0.2) !important;
|
||||
}
|
||||
.el-tabs__item {
|
||||
width: 200px;
|
||||
color: rgba(68, 165, 255, 1);
|
||||
justify-content: center;
|
||||
&.is-active{
|
||||
color: #00C0FF;
|
||||
background: rgba(10, 169, 255, 0.15);
|
||||
.el-icon{
|
||||
position: absolute;
|
||||
right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
.el-tabs__item.is-disabled {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
.el-tabs__active-bar {
|
||||
background-color: #00C0FF;
|
||||
}
|
||||
}
|
||||
}
|
||||
.content{
|
||||
width: calc(100% - 220px);
|
||||
}
|
||||
</style>
|
||||
262
src/views/business/setting/monitor/detail.vue
Normal file
262
src/views/business/setting/monitor/detail.vue
Normal file
@@ -0,0 +1,262 @@
|
||||
<template>
|
||||
<el-form class="detail-form" ref="form" :model="model" label-width="auto" :rules="rules" label-suffix=":">
|
||||
<el-form-item v-for="(item,index) in items" :key="index" :label="item.label" :prop="item.prop" :style="`grid-column-start: span ${item.width || '1'};`">
|
||||
|
||||
<el-input v-if="item.type === 'input'" v-model="model[item.prop]" :readonly="type==='check'"/>
|
||||
<el-input v-if="item.type === 'textarea'" autosize v-model="model[item.prop]" :rows="item.rows || 2" type="textarea" :readonly="type==='check'"/>
|
||||
<el-input-number v-if="item.type === 'number'" v-model="model[item.prop]" :min="item.min || 0" style="width: 100%;" :readonly="type==='check'"/>
|
||||
|
||||
<el-select v-if="item.type === 'select'" v-model="model[item.prop]" :clearable="item.clearable" :disabled="type==='check'">
|
||||
<el-option v-for="(opt, index) in item.options" :key="index" :label="opt.label" :value="opt.value">
|
||||
</el-option>
|
||||
</el-select>
|
||||
|
||||
<el-date-picker v-if="item.type === 'datetime'" v-model="model[item.prop]" type="datetime"
|
||||
:placeholder="item.placeholder ? item.placeholder : '请选择时间'" value-format="YYYY-MM-DD HH:mm:ss"
|
||||
:clearable="item.clearable" style="width:100%" :readonly="type==='check'">
|
||||
</el-date-picker>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div class="button-wrapper" v-if="type !== 'check'">
|
||||
<el-button type="primary" @click="validate">提交</el-button>
|
||||
<el-button type="primary" @click="$emit('close')">取消</el-button>
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import { ref, reactive } from 'vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { dsVideoSave, dsVideoUpdate } from '@/api/device'
|
||||
|
||||
const emit = defineEmits([ 'close' ])
|
||||
const props = defineProps({
|
||||
data: {
|
||||
default() {
|
||||
return {}
|
||||
},
|
||||
required: false,
|
||||
type: Object
|
||||
},
|
||||
type: {
|
||||
default: '',
|
||||
required: false,
|
||||
type: String
|
||||
}
|
||||
})
|
||||
|
||||
const form = ref(null)
|
||||
|
||||
const model = reactive({
|
||||
videoCode: '',
|
||||
videoName: '',
|
||||
longitude: '',
|
||||
latitude: '',
|
||||
height: 0,
|
||||
visionDistance: 5,
|
||||
videoIp: '',
|
||||
videoPort: '',
|
||||
videoAccount: '',
|
||||
videoPassword: '',
|
||||
channelNo: '0',
|
||||
videoType: '云台',
|
||||
belongUnit: '',
|
||||
runStatus: '正常',
|
||||
beBayonet: 0,
|
||||
bePlay: 0,
|
||||
playIndex: 1,
|
||||
playTime: 5
|
||||
})
|
||||
|
||||
const items = ref([
|
||||
{
|
||||
label: '监控编号',
|
||||
prop: 'videoCode',
|
||||
type: 'input'
|
||||
},
|
||||
{
|
||||
label: '监控名称',
|
||||
prop: 'videoName',
|
||||
type: 'input'
|
||||
},
|
||||
{
|
||||
label: '经度',
|
||||
prop: 'longitude',
|
||||
type: 'input'
|
||||
},
|
||||
{
|
||||
label: '纬度',
|
||||
prop: 'latitude',
|
||||
type: 'input'
|
||||
},
|
||||
{
|
||||
label: '监控安装高度(米)',
|
||||
prop: 'height',
|
||||
type: 'number'
|
||||
},
|
||||
{
|
||||
label: '可视距离(公里)',
|
||||
prop: 'visionDistance',
|
||||
type: 'number'
|
||||
},
|
||||
{
|
||||
label: '设备ip',
|
||||
prop: 'videoIp',
|
||||
type: 'input'
|
||||
},
|
||||
{
|
||||
label: '设备端口号',
|
||||
prop: 'videoPort',
|
||||
type: 'input'
|
||||
},
|
||||
{
|
||||
label: '设备账号',
|
||||
prop: 'videoAccount',
|
||||
type: 'input'
|
||||
},
|
||||
{
|
||||
label: '设备密码',
|
||||
prop: 'videoPassword',
|
||||
type: 'input'
|
||||
},
|
||||
{
|
||||
label: '监控类型',
|
||||
prop: 'videoType',
|
||||
type: 'select',
|
||||
options: [
|
||||
{ value: '球机', label: '球机' },
|
||||
{ value: '云台', label: '云台' }
|
||||
]
|
||||
},
|
||||
{
|
||||
label: '运行状态',
|
||||
prop: 'runStatus',
|
||||
type: 'select',
|
||||
options: [
|
||||
{ value: '正常', label: '正常' },
|
||||
{ value: '故障', label: '故障' }
|
||||
]
|
||||
},
|
||||
{
|
||||
label: '是否卡口',
|
||||
prop: 'beBayonet',
|
||||
type: 'select',
|
||||
options: [
|
||||
{ value: 1, label: '是' },
|
||||
{ value: 0, label: '否' }
|
||||
]
|
||||
},
|
||||
{
|
||||
label: '是否轮询',
|
||||
prop: 'bePlay',
|
||||
type: 'select',
|
||||
options: [
|
||||
{
|
||||
value: 1,
|
||||
label: '是'
|
||||
},
|
||||
{
|
||||
value: 0,
|
||||
label: '否'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: '轮询顺序',
|
||||
prop: 'playIndex',
|
||||
type: 'number'
|
||||
},
|
||||
{
|
||||
label: '轮询时间(s)',
|
||||
prop: 'playTime',
|
||||
type: 'number'
|
||||
},
|
||||
{
|
||||
label: '通道号',
|
||||
prop: 'channelNo',
|
||||
type: 'input'
|
||||
},
|
||||
{
|
||||
label: '监管所属单位',
|
||||
prop: 'belongUnit',
|
||||
type: 'input'
|
||||
}
|
||||
])
|
||||
|
||||
const rules = {
|
||||
videoCode: [ { required: true, message: '监控编号 未填写', trigger: 'blur' } ],
|
||||
videoName: [ { required: true, message: '监控名称 未填写', trigger: 'blur' } ],
|
||||
longitude: [ { required: true, message: '经度 未填写', trigger: 'blur' } ],
|
||||
latitude: [ { required: true, message: '纬度 未填写', trigger: 'blur' } ],
|
||||
height: [ { required: true, message: '监控安装高度 未填写', trigger: 'blur' } ],
|
||||
visionDistance: [ { required: true, message: '可视距离 未填写', trigger: 'blur' } ],
|
||||
videoIp: [ { required: true, message: '设备ip 未填写', trigger: 'blur' } ],
|
||||
videoPort: [ { required: true, message: '设备端口号 未填写', trigger: 'blur' } ],
|
||||
videoAccount: [ { required: true, message: '设备账号 未填写', trigger: 'blur' } ],
|
||||
videoPassword: [ { required: true, message: '设备密码 未填写', trigger: 'blur' } ],
|
||||
videoType: [ { required: true, message: '监控类型 未填写', trigger: 'blur' } ],
|
||||
runStatus: [ { required: true, message: '运行状态 未填写', trigger: 'blur' } ],
|
||||
beBayonet: [ { required: true, message: '是否卡口 未填写', trigger: 'change' } ],
|
||||
bePlay: [ { required: true, message: '是否轮询 未填写', trigger: 'change' } ]
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化赋值
|
||||
*/
|
||||
const initData = () => {
|
||||
if (props.type !== 'add') {
|
||||
Object.keys(model).forEach((key) => {
|
||||
model[key] = props.data[key]
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 表单校验
|
||||
*/
|
||||
const validate = () => {
|
||||
form.value.validate().then((valid) => {
|
||||
if (valid) {
|
||||
submit()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 表单校验
|
||||
*/
|
||||
const submit = () => {
|
||||
const params = {
|
||||
...model
|
||||
}
|
||||
if(props.type === 'add') {
|
||||
dsVideoSave(params).then(() => {
|
||||
ElMessage.success('提交成功!')
|
||||
emit('close', true)
|
||||
})
|
||||
}else{
|
||||
dsVideoUpdate({ ...params, id: props.data.id }).then(() => {
|
||||
ElMessage.success('提交成功!')
|
||||
emit('close', true)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
initData()
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.detail-form{
|
||||
display: grid;
|
||||
box-sizing: border-box;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
column-gap: 20px;
|
||||
}
|
||||
|
||||
.button-wrapper{
|
||||
width: 100%;
|
||||
margin-top: 20px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 20px;
|
||||
}
|
||||
</style>
|
||||
263
src/views/business/setting/monitor/index.vue
Normal file
263
src/views/business/setting/monitor/index.vue
Normal file
@@ -0,0 +1,263 @@
|
||||
<template>
|
||||
<div class="content">
|
||||
<FilterComponent :filter-buttons="filterButtons" :filter-items="items" :filter-model="model" @handle="handle"/>
|
||||
<TableComponent style="height: 650px;" :loading="loading" :data="tableData" :columns="columns" :config="config" :operate="operate" @handle="handle"/>
|
||||
<el-pagination v-model:current-page="pagination.current" v-model:page-size="pagination.size"
|
||||
:total="pagination.total" :page-sizes="[10, 15, 20, 50]" layout="total, sizes, prev, pager, next, jumper"
|
||||
@size-change="initData" @current-change="initData"
|
||||
style="margin-top: 10px; justify-content: center;" />
|
||||
<DialogComponent v-if="detail.visible" :title="detail.title[detail.type]" :modal="true" width="800"
|
||||
@close="closeDetail">
|
||||
<DetailComponent :data="detail.data" :type="detail.type" @close="closeDetail" />
|
||||
</DialogComponent>
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import { reactive, ref } from 'vue'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import FilterComponent from '@/components/Filter/index.vue'
|
||||
import TableComponent from '@/components/Table/index2.vue'
|
||||
import DialogComponent from '@/components/Dialog/screen.vue'
|
||||
import DetailComponent from './detail.vue'
|
||||
import { dsVideoPage, dsVideoDelete } from '@/api/device'
|
||||
|
||||
const items = [
|
||||
{
|
||||
label: '监控名称',
|
||||
prop: 'videoName',
|
||||
width: '180px',
|
||||
type: 'input'
|
||||
},
|
||||
{
|
||||
label: '监控编号',
|
||||
prop: 'videoCode',
|
||||
width: '180px',
|
||||
type: 'input'
|
||||
},
|
||||
{
|
||||
label: '是否卡口',
|
||||
prop: 'beBayonet',
|
||||
type: 'select',
|
||||
width: '180px',
|
||||
options: [
|
||||
{ value: 1, label: '是' },
|
||||
{ value: 0, label: '否' }
|
||||
]
|
||||
},
|
||||
{
|
||||
label: '是否轮询',
|
||||
prop: 'bePlay',
|
||||
type: 'select',
|
||||
width: '180px',
|
||||
options: [
|
||||
{ value: 1, label: '是' },
|
||||
{ value: 0, label: '否' }
|
||||
]
|
||||
}
|
||||
]
|
||||
const filterButtons = ref([
|
||||
{
|
||||
name: '检索',
|
||||
prop: 'query',
|
||||
theme: 'primary',
|
||||
type: 'button'
|
||||
},
|
||||
{
|
||||
name: '新增',
|
||||
prop: 'add',
|
||||
theme: 'primary',
|
||||
type: 'button'
|
||||
}
|
||||
])
|
||||
let model = {
|
||||
videoName: '',
|
||||
videoCode: '',
|
||||
beBayonet: '',
|
||||
bePlay: ''
|
||||
}
|
||||
|
||||
const loading = ref(false)
|
||||
const tableData = ref([])
|
||||
|
||||
const columns = [
|
||||
{
|
||||
label: '监控编号',
|
||||
prop: 'videoCode',
|
||||
width: 290
|
||||
},
|
||||
{
|
||||
label: '监控名称',
|
||||
prop: 'videoName',
|
||||
width: 250
|
||||
},
|
||||
{
|
||||
label: '经度',
|
||||
prop: 'longitude',
|
||||
width: 100
|
||||
},
|
||||
{
|
||||
label: '纬度',
|
||||
prop: 'latitude',
|
||||
width: 100
|
||||
},
|
||||
|
||||
{
|
||||
label: '设备ip',
|
||||
prop: 'videoIp',
|
||||
width: 120
|
||||
},
|
||||
{
|
||||
label: '设备端口号',
|
||||
prop: 'videoPort',
|
||||
width: 90
|
||||
},
|
||||
{
|
||||
label: '通道号',
|
||||
prop: 'channelNo',
|
||||
width: 60
|
||||
},
|
||||
{
|
||||
label: '监控类型',
|
||||
prop: 'videoType',
|
||||
width: 80
|
||||
},
|
||||
{
|
||||
label: '运行状态',
|
||||
prop: 'runStatus',
|
||||
width: 80
|
||||
},
|
||||
{
|
||||
label: '是否卡口',
|
||||
prop: 'beBayonetF',
|
||||
width: 80
|
||||
},
|
||||
{
|
||||
label: '是否轮询',
|
||||
prop: 'bePlayF',
|
||||
width: 80
|
||||
}
|
||||
]
|
||||
|
||||
const config = {
|
||||
type: 'index',
|
||||
width: 110,
|
||||
align: 'center'
|
||||
}
|
||||
|
||||
const operate = [
|
||||
{
|
||||
label: '编辑',
|
||||
prop: 'edit',
|
||||
theme: 'success'
|
||||
},
|
||||
{
|
||||
label: '删除',
|
||||
prop: 'remove',
|
||||
theme: 'danger'
|
||||
}
|
||||
]
|
||||
|
||||
const pagination = {
|
||||
current: 1,
|
||||
size: 15,
|
||||
total: 0
|
||||
}
|
||||
|
||||
const detail = reactive({
|
||||
visible: false,
|
||||
title: {
|
||||
add: '监控信息新增',
|
||||
check: '监控信息查看',
|
||||
edit: '监控信息编辑'
|
||||
},
|
||||
type: 'add',
|
||||
data: {}
|
||||
})
|
||||
|
||||
const initData = () => {
|
||||
loading.value = true
|
||||
const data = {
|
||||
...model,
|
||||
pageNum: pagination.current,
|
||||
pageSize: pagination.size
|
||||
}
|
||||
dsVideoPage(data).then(res => {
|
||||
tableData.value = res.rows.map(i => {
|
||||
return {
|
||||
...i,
|
||||
beBayonetF: i.beBayonet === 1 ? '是' : '否',
|
||||
bePlayF: i.bePlay === 1 ? '是' : '否'
|
||||
}
|
||||
})
|
||||
pagination.total = res.total
|
||||
}).finally(() => {
|
||||
loading.value = false
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 按钮操作
|
||||
* @param type 操作类型
|
||||
* @param data 数据
|
||||
*/
|
||||
const handle = (type, data) => {
|
||||
|
||||
switch (type) {
|
||||
case 'query':
|
||||
model = { ...data }
|
||||
pagination.current = 1
|
||||
initData()
|
||||
break
|
||||
case 'add':
|
||||
detail.visible = true
|
||||
detail.type = 'add'
|
||||
detail.data = {}
|
||||
break
|
||||
case 'check':
|
||||
case 'edit':
|
||||
detail.visible = true
|
||||
detail.type = type
|
||||
detail.data = { ...data }
|
||||
break
|
||||
case 'remove': {
|
||||
ElMessageBox.confirm(
|
||||
'是否移除该条数据?',
|
||||
'提示',
|
||||
{
|
||||
confirmButtonText: '确认',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}
|
||||
).then(() => {
|
||||
const params = {
|
||||
id: data.id
|
||||
}
|
||||
dsVideoDelete(params).then(() => {
|
||||
ElMessage.success('移除成功!')
|
||||
initData()
|
||||
})
|
||||
}).catch(() => { /* empty */ })
|
||||
break
|
||||
}
|
||||
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 关闭详情弹窗
|
||||
* @param refresh 是否刷新数据
|
||||
*/
|
||||
const closeDetail = (refresh) => {
|
||||
detail.visible = false
|
||||
if (refresh) {
|
||||
initData()
|
||||
}
|
||||
}
|
||||
initData()
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
</style>
|
||||
@@ -20,14 +20,14 @@
|
||||
<div style="width: 100%;height: 100%;" :class="[`video-window${item}`]">
|
||||
<!-- 视频预览组件 -->
|
||||
<HikPlayerComponent
|
||||
v-if="deviceType === 'CCTV' && haikang[item-1]?.codes"
|
||||
v-if="videoType === 'CCTV' && haikang[item-1]?.codes"
|
||||
ref="videoPreview"
|
||||
:id="item"
|
||||
:cameraIndexCode="haikang[item-1]?.codes"
|
||||
:layout="haikang[item-1]?.layout"
|
||||
@close="closeVideo">
|
||||
</HikPlayerComponent>
|
||||
<FlvPlayerComponent v-if="deviceType === 'UAV' && haikang[item-1]?.codes" :id="'uav'+item" :url="haikang[item-1]?.codes"/>
|
||||
<FlvPlayerComponent v-if="videoType === 'UAV' && haikang[item-1]?.codes" :id="'uav'+item" :url="haikang[item-1]?.codes"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -39,7 +39,7 @@
|
||||
import { nextTick, onUnmounted, ref } from 'vue'
|
||||
import HikPlayerComponent from '@/components/Player/HikPlayer.vue'
|
||||
import FlvPlayerComponent from '@/components/FlvPlayer/index.vue'
|
||||
import { getVideoStream, doStartOrStopUavAlgorithm } from '@/api/uav'
|
||||
import { getVideoStream } from '@/api/uav'
|
||||
|
||||
const control = ref([
|
||||
{ label: '1X1', value: 1 },
|
||||
@@ -51,7 +51,7 @@ const active = ref(4)
|
||||
const haikang = ref([])
|
||||
const videoPreview = ref(null)
|
||||
const props = defineProps({
|
||||
deviceType: {
|
||||
videoType: {
|
||||
type: String,
|
||||
default: () => ''
|
||||
}
|
||||
@@ -91,14 +91,13 @@ const closeVideo = (data, item) => {
|
||||
}
|
||||
// 点击视频预览
|
||||
const handleNodeClick = (node) => {
|
||||
if(props.deviceType === 'CCTV' && node.videoCode) {
|
||||
if(props.videoType === 'CCTV' && node.videoCode) {
|
||||
let index = haikang.value.findIndex(i => !i)
|
||||
console.log(index, haikang.value, 'value')
|
||||
if(index !== -1) {
|
||||
haikang.value[index] = { codes: node.videoCode }
|
||||
}
|
||||
}
|
||||
if(props.deviceType === 'UAV' && node.droneSn) {
|
||||
if(props.videoType === 'UAV' && node.droneSn) {
|
||||
toggleFly(node)
|
||||
}
|
||||
}
|
||||
@@ -113,7 +112,10 @@ const toggleFly = (data) => {
|
||||
if(res.success) {
|
||||
let index = haikang.value.findIndex(i => !i)
|
||||
if(index !== -1) {
|
||||
haikang.value[index] = { codes: res.result.httpUrl }
|
||||
setTimeout(() => {
|
||||
haikang.value[index] = { codes: res.result.httpUrl }
|
||||
}, 10000)
|
||||
h
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -12,13 +12,12 @@ import { computed, nextTick, ref, watch } from 'vue'
|
||||
import DialogComponent from '@/components/Dialog/screen.vue'
|
||||
import TreeCom from './tree.vue'
|
||||
import GridCom from './grid.vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import useMapStore from '@/store/modules/map'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { dragEvent, resizeEvent } from '@/utils/common'
|
||||
|
||||
const mapStore = useMapStore()
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const type = computed(() => mapStore.dialog.type)
|
||||
const type = computed(() => route.query.type)
|
||||
const Tree = ref(null)
|
||||
const Grid = ref(null)
|
||||
|
||||
@@ -32,7 +31,7 @@ const handle = (node) => {
|
||||
Grid.value.handleNodeClick(node)
|
||||
}
|
||||
const close = () => {
|
||||
mapStore.updateDialog(false)
|
||||
router.push('/')
|
||||
}
|
||||
watch(() => type.value, () => {
|
||||
if(type.value) {
|
||||
@@ -48,8 +47,8 @@ watch(() => type.value, () => {
|
||||
*
|
||||
*/
|
||||
const drag = (e) => {
|
||||
if(e.target.className.includes('el-dialog-title')) {
|
||||
dragEvent(e, 'el-dialog-title', () => {
|
||||
if(e.target.className.includes('el-dialog-title') || e.target.className.includes('el-dialog__title')) {
|
||||
dragEvent(e, 'el-dialog', () => {
|
||||
if(Grid.value.videoPreview) {
|
||||
Grid.value.videoPreview.forEach(i => {
|
||||
i.initResize(false)
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
@node-click="handleNodeClick">
|
||||
<template v-slot="{ node, data }">
|
||||
<!-- <img src="@/assets/images/livePreview/icon-monitor.png" alt=""> -->
|
||||
<span :class="(!data.sourceType && treeType === 'UAV') ? 'offline': ''">{{ node.label }}</span>
|
||||
<span :class="(data.sourceType!=='2' && treeType === 'UAV') ? 'offline': ''">{{ node.label }}</span>
|
||||
</template>
|
||||
</el-tree>
|
||||
</div>
|
||||
@@ -16,8 +16,8 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted } from 'vue'
|
||||
import { findVideoLevelList, findUavPage, findEnvPage, dsVideoList } from '@/api/device.js'
|
||||
import { ref } from 'vue'
|
||||
import { findVideoLevelList, findUavPage } from '@/api/device.js'
|
||||
|
||||
const emit = defineEmits([ 'handle' ])
|
||||
|
||||
@@ -29,34 +29,11 @@ const refresh = (type) => {
|
||||
if(type === 'CCTV') {
|
||||
const params = {}
|
||||
findVideoLevelList(params).then(res => {
|
||||
if (res.success) {
|
||||
const data = res.result.records
|
||||
const groupedData = {}
|
||||
data.forEach(item => {
|
||||
const belong = item.videoBelong
|
||||
if (!groupedData[belong]) {
|
||||
groupedData[belong] = []
|
||||
}
|
||||
groupedData[belong].push(item)
|
||||
})
|
||||
// 转换为树形结构
|
||||
const result = Object.keys(groupedData).map(belong => {
|
||||
const children = groupedData[belong]
|
||||
if (children.length === 1) {
|
||||
// 只有一个子元素,直接返回该子元素
|
||||
return children[0]
|
||||
}
|
||||
// 有多个子元素,返回分组节点
|
||||
return {
|
||||
videoName: belong,
|
||||
longitude: children[0].longitude,
|
||||
latitude: children[0].latitude,
|
||||
children: children
|
||||
}
|
||||
|
||||
})
|
||||
tableData.value = result
|
||||
}
|
||||
const arr = []
|
||||
Object.keys(res.data).forEach(i => {
|
||||
arr.push({ videoName: i, children: res.data[i] })
|
||||
})
|
||||
tableData.value = arr
|
||||
})
|
||||
}else if(type === 'UAV') {
|
||||
const params = {
|
||||
@@ -97,10 +74,10 @@ const refresh = (type) => {
|
||||
|
||||
}
|
||||
const handleNodeClick = (data, node) => {
|
||||
if(!data.sourceType) {
|
||||
return false
|
||||
}
|
||||
if(!data.children || data.children.length === 0) {
|
||||
if(treeType.value === 'UAV' && !data.sourceType) {
|
||||
return false
|
||||
}
|
||||
emit('handle', node.data)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,7 +74,7 @@ const model = reactive({
|
||||
leader: '',
|
||||
phone: '',
|
||||
email: '',
|
||||
areaCodes: '',
|
||||
// areaCodes: '',
|
||||
orderNum: ''
|
||||
})
|
||||
|
||||
@@ -92,11 +92,11 @@ const items = [
|
||||
return model.parentId !== 0
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '数据来源',
|
||||
prop: 'areaCodes',
|
||||
type: 'tree'
|
||||
},
|
||||
// {
|
||||
// label: '数据来源',
|
||||
// prop: 'areaCodes',
|
||||
// type: 'tree'
|
||||
// },
|
||||
{
|
||||
label: '负责人',
|
||||
prop: 'leader',
|
||||
@@ -140,16 +140,16 @@ const getAllCheckedKeys = () => {
|
||||
* 初始化赋值
|
||||
*/
|
||||
const initData = () => {
|
||||
getAreaTree({ id: 331000 }).then(res => {
|
||||
areaOptions.value = res.data
|
||||
if (props.data.areaCodes && props.type !== 'add') {
|
||||
props.data.areaCodes.split(',').forEach(item => {
|
||||
nextTick(() => {
|
||||
areaRef.value[0].setChecked(item, true, false)
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
||||
// getAreaTree({ id: 331000 }).then(res => {
|
||||
// areaOptions.value = res.data
|
||||
// if (props.data.areaCodes && props.type !== 'add') {
|
||||
// props.data.areaCodes.split(',').forEach(item => {
|
||||
// nextTick(() => {
|
||||
// areaRef.value[0].setChecked(item, true, false)
|
||||
// })
|
||||
// })
|
||||
// }
|
||||
// })
|
||||
if (props.type !== 'add') {
|
||||
Object.keys(model).forEach((key) => {
|
||||
model[key] = props.data[key]
|
||||
@@ -161,7 +161,7 @@ const initData = () => {
|
||||
* 表单校验
|
||||
*/
|
||||
const validate = () => {
|
||||
model.areaCodes = getAllCheckedKeys()
|
||||
// model.areaCodes = getAllCheckedKeys()
|
||||
form.value.validate().then((valid) => {
|
||||
if (valid) {
|
||||
submit()
|
||||
|
||||
@@ -12,7 +12,7 @@ import { ref, reactive } from 'vue'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import { getDept, delDept, listDeptExcludeChild } from '@/api/system'
|
||||
import { handleTree } from '@/utils/common'
|
||||
import FilterComponent from '@/components/Filter/index.vue'
|
||||
import FilterComponent from '@/components/Filter/index2.vue'
|
||||
import TableComponent from '@/components/Table/index.vue'
|
||||
import DialogComponent from '@/components/Dialog/index.vue'
|
||||
import DetailComponent from './detail.vue'
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
</el-breadcrumb>
|
||||
<div class="title">千帆智瞰</div>
|
||||
<div class="user">
|
||||
<!-- <el-button :icon="Monitor" @click="goBusiness">业务系统</el-button> -->
|
||||
<el-button :icon="Monitor" @click="goHome">返回门户</el-button>
|
||||
<el-avatar :icon="UserFilled" :size="28"/>
|
||||
<el-dropdown @command="handle">
|
||||
@@ -150,14 +149,8 @@ const validate = () => {
|
||||
}
|
||||
})
|
||||
}
|
||||
/**
|
||||
* 跳转到系统设置
|
||||
*/
|
||||
const goBusiness = () => {
|
||||
window.open('/', 'business_window')
|
||||
}
|
||||
const goHome = () => {
|
||||
window.open('/data-center', 'business_window')
|
||||
window.open('/', 'business_window')
|
||||
}
|
||||
watch(
|
||||
route,
|
||||
|
||||
@@ -17,7 +17,7 @@ import { ref, reactive } from 'vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { loginLogPage, exportLoginLog } from '@/api/system'
|
||||
import { addDateRange, downloadFile } from '@/utils/common'
|
||||
import FilterComponent from '@/components/Filter/index.vue'
|
||||
import FilterComponent from '@/components/Filter/index2.vue'
|
||||
import TableComponent from '@/components/Table/index.vue'
|
||||
|
||||
const items = [
|
||||
@@ -158,7 +158,7 @@ const initData = () => {
|
||||
loginLogPage(params).then(res => {
|
||||
if(res.code === 200) {
|
||||
const statusArr = [ '成功', '失败' ]
|
||||
data.value = res.data.map(item => {
|
||||
data.value = res.rows.map(item => {
|
||||
return {
|
||||
...item,
|
||||
statusStr: statusArr[item.status]
|
||||
|
||||
@@ -17,7 +17,7 @@ import { ref, reactive } from 'vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { operateLogPage, exportOperateLog } from '@/api/system'
|
||||
import { addDateRange, downloadFile } from '@/utils/common'
|
||||
import FilterComponent from '@/components/Filter/index.vue'
|
||||
import FilterComponent from '@/components/Filter/index2.vue'
|
||||
import TableComponent from '@/components/Table/index.vue'
|
||||
|
||||
const items = [
|
||||
@@ -144,7 +144,7 @@ const initData = () => {
|
||||
operateLogPage(params).then(res => {
|
||||
if(res.code === 200) {
|
||||
const statusArr = [ '成功', '失败' ]
|
||||
data.value = res.data.map(item => {
|
||||
data.value = res.rows.map(item => {
|
||||
return {
|
||||
...item,
|
||||
statusStr: statusArr[item.status],
|
||||
|
||||
@@ -12,7 +12,7 @@ import { ref, reactive } from 'vue'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import { getMenu, delMenu } from '@/api/system'
|
||||
import { handleTree } from '@/utils/common'
|
||||
import FilterComponent from '@/components/Filter/index.vue'
|
||||
import FilterComponent from '@/components/Filter/index2.vue'
|
||||
import TableComponent from '@/components/Table/index.vue'
|
||||
import DialogComponent from '@/components/Dialog/index.vue'
|
||||
import DetailComponent from './detail.vue'
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
import { ref, reactive } from 'vue'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import { getRolePage, delRole } from '@/api/system'
|
||||
import FilterComponent from '@/components/Filter/index.vue'
|
||||
import FilterComponent from '@/components/Filter/index2.vue'
|
||||
import TableComponent from '@/components/Table/index.vue'
|
||||
import DialogComponent from '@/components/Dialog/index.vue'
|
||||
import DetailComponent from './detail.vue'
|
||||
@@ -117,7 +117,7 @@ const detail = reactive({
|
||||
}
|
||||
getRolePage(params).then(res => {
|
||||
if(res.code === 200) {
|
||||
data.value = res.data
|
||||
data.value = res.rows
|
||||
pagination.total = res.total
|
||||
}else{
|
||||
ElMessage.error(res.msg || '查询失败!')
|
||||
|
||||
@@ -32,20 +32,6 @@
|
||||
check-strictly
|
||||
:render-after-expand="false"
|
||||
/>
|
||||
<el-tree-select
|
||||
v-if="item.type === 'treeselect' && item.prop === 'areaCode'"
|
||||
:disabled="type==='check'"
|
||||
@change="(val)=>changeArea(val,item.prop)"
|
||||
v-model="model[item.prop]"
|
||||
:data="item.options"
|
||||
:props="item.props"
|
||||
:value-key="item.key"
|
||||
:render-after-expand="false"
|
||||
check-strictly
|
||||
:load="load"
|
||||
lazy
|
||||
/>
|
||||
|
||||
<el-radio-group v-if="item.type === 'radio'" v-model="model[item.prop]">
|
||||
<el-radio v-for="(opt, index) in item.options" :key="index" :value="opt.value">{{opt.label}}</el-radio>
|
||||
</el-radio-group>
|
||||
@@ -89,8 +75,6 @@ const props = defineProps({
|
||||
const form = ref(null)
|
||||
|
||||
const model = reactive({
|
||||
areaCode: '',
|
||||
areaName: '',
|
||||
userName: '',
|
||||
password: '',
|
||||
confirmPassword: '',
|
||||
@@ -104,14 +88,6 @@ const model = reactive({
|
||||
})
|
||||
|
||||
const items = ref([
|
||||
{
|
||||
label: '行政区域',
|
||||
prop: 'areaCode',
|
||||
type: 'treeselect',
|
||||
options: [],
|
||||
key: 'areaCode',
|
||||
props: { value: 'areaCode', label: 'areaName', children: 'childArea' }
|
||||
},
|
||||
{
|
||||
label: '用户账号',
|
||||
prop: 'userName',
|
||||
@@ -180,7 +156,6 @@ const items = ref([
|
||||
])
|
||||
|
||||
const rules = {
|
||||
areaCode: [ { required: true, message: '行政区域 未填写', type: 'error' } ],
|
||||
userName: [ { required: true, message: '用户账号 未填写', type: 'error' } ],
|
||||
nickName: [ { required: true, message: '用户名称 未填写', type: 'error' } ],
|
||||
password: [
|
||||
@@ -218,23 +193,6 @@ const rules = {
|
||||
// idCard: [ { required: true, message: '身份证 未填写', type: 'error' } ]
|
||||
}
|
||||
|
||||
const load = async (node, resolve) => {
|
||||
if(node.data && node.data.childArea) {
|
||||
resolve(node.data.childArea)
|
||||
}
|
||||
}
|
||||
const initArea = () => {
|
||||
let index = items.value.findIndex(i => i.prop === 'areaCode')
|
||||
getAreaTree({ id: 0 }).then(res => {
|
||||
items.value[index].options = res.data
|
||||
})
|
||||
}
|
||||
const changeArea = (val, prop) => {
|
||||
if(prop === 'areaCode') {
|
||||
let index = items.value.findIndex(i => i.prop === 'areaCode')
|
||||
findName(val, items.value[index].options)
|
||||
}
|
||||
}
|
||||
const findName = (val, list) => {
|
||||
for (const i of list) {
|
||||
if (i.areaCode === val) {
|
||||
@@ -300,7 +258,6 @@ const submit = () => {
|
||||
}
|
||||
|
||||
initData()
|
||||
initArea()
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@@ -14,10 +14,11 @@
|
||||
import { ref, reactive } from 'vue'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import { getUserPage, delUser, deptTreeSelect, resetUserPwd } from '@/api/system'
|
||||
import FilterComponent from '@/components/Filter/index.vue'
|
||||
import FilterComponent from '@/components/Filter/index2.vue'
|
||||
import TableComponent from '@/components/Table/index.vue'
|
||||
import DialogComponent from '@/components/Dialog/index.vue'
|
||||
import DetailComponent from './detail.vue'
|
||||
import { it } from 'element-plus/es/locale/index.mjs'
|
||||
|
||||
const items = [
|
||||
{
|
||||
@@ -127,7 +128,12 @@ const initData = () => {
|
||||
}
|
||||
getUserPage(params).then(res => {
|
||||
if (res.code === 200) {
|
||||
data.value = res.data
|
||||
data.value = res.rows.map(item => {
|
||||
return{
|
||||
...item,
|
||||
deptName: item.dept ? item.dept.deptName : ''
|
||||
}
|
||||
})
|
||||
pagination.total = res.total
|
||||
} else {
|
||||
ElMessage.error(res.msg || '查询失败!')
|
||||
|
||||
Reference in New Issue
Block a user