Compare commits
28 Commits
7f9d9ed8bd
...
dev
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5144bc9a7d | ||
|
|
2d2f46b6f1 | ||
|
|
e4d014cf8c | ||
|
|
80a360fa0e | ||
|
|
7d2c70a19f | ||
|
|
a248e3b234 | ||
|
|
70ab453d94 | ||
|
|
36f0cedc62 | ||
|
|
df52a3e343 | ||
|
|
8d27df05d6 | ||
|
|
e47b8d354e | ||
|
|
ced12d5805 | ||
|
|
dd54ce489e | ||
|
|
00efe08873 | ||
|
|
c3420640a7 | ||
|
|
4c882ac309 | ||
|
|
334787f2cf | ||
|
|
de48277991 | ||
|
|
753f84ec26 | ||
|
|
3d58c73a88 | ||
|
|
457d0c3888 | ||
|
|
a8ea3746f4 | ||
|
|
55f0cd2192 | ||
|
|
2492b8ff5a | ||
|
|
13b00ac8de | ||
|
|
89d0dd2a1d | ||
|
|
47b272441c | ||
|
|
159d53080a |
@@ -24,3 +24,9 @@ export interface DemoQuery extends PageQuery {
|
||||
beginTime?: string;
|
||||
endTime?: string;
|
||||
}
|
||||
|
||||
/** 告警列表响应结构 */
|
||||
export interface WarningListResult {
|
||||
rows: DemoVO[];
|
||||
total: number;
|
||||
}
|
||||
|
||||
@@ -37,9 +37,16 @@
|
||||
<!-- 消息 -->
|
||||
<el-tooltip content="警报" effect="dark" placement="bottom">
|
||||
<div style="display: flex; align-items: center" class="mr-[30px]">
|
||||
<el-popover placement="bottom" trigger="click" transition="el-zoom-in-top" :width="400" :persistent="false">
|
||||
<el-popover
|
||||
placement="bottom"
|
||||
transition="el-zoom-in-top"
|
||||
:width="400"
|
||||
:persistent="false"
|
||||
trigger="click"
|
||||
v-model:visible="popoverVisible"
|
||||
>
|
||||
<template #reference>
|
||||
<el-badge :value="warningListStore.warningListTotal > 0 ? warningListStore.warningListTotal : ''" :max="999">
|
||||
<el-badge :value="infoData" :max="999">
|
||||
<div class="right-menu-item hover-effect" @click="goToWarningList">
|
||||
<el-icon><BellFilled /></el-icon>
|
||||
</div>
|
||||
@@ -123,6 +130,9 @@ const noticeStore = storeToRefs(useNoticeStore());
|
||||
const newNotice = ref(<number>0);
|
||||
const { warningList, warningListTotal } = storeToRefs(warningListStore);
|
||||
|
||||
const infoData = ref(warningListStore.warningListTotal ?? 0);
|
||||
const popoverVisible = ref(false);
|
||||
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
|
||||
const userId = ref(userStore.userId);
|
||||
@@ -213,8 +223,14 @@ const handleCommand = (command: string) => {
|
||||
//用深度监听 消息
|
||||
watch(
|
||||
() => noticeStore.state.value.notices,
|
||||
(newVal) => {
|
||||
async (newVal) => {
|
||||
newNotice.value = newVal.filter((item: any) => !item.read).length;
|
||||
//监听到消息变化,刷新告警列表
|
||||
await warningListStore.getWarningList({
|
||||
status: 0,
|
||||
pageNum: 1,
|
||||
pageSize: 300
|
||||
});
|
||||
},
|
||||
{ deep: true }
|
||||
);
|
||||
@@ -239,8 +255,38 @@ onMounted(() => {
|
||||
});
|
||||
});
|
||||
onMounted(() => {
|
||||
warningListStore.getWarningList();
|
||||
warningListStore.getWarningList({
|
||||
status: 0,
|
||||
pageNum: 1,
|
||||
pageSize: 300
|
||||
});
|
||||
});
|
||||
|
||||
//监听数据变化
|
||||
watch(
|
||||
() => warningListStore.warningListTotal,
|
||||
(newVal) => {
|
||||
infoData.value = newVal;
|
||||
},
|
||||
{ deep: true }
|
||||
);
|
||||
// 告警数据变化时自动弹出弹窗
|
||||
// 当 isNotice 变为 true 时弹出弹窗,并重置为 false
|
||||
// let noticeFirstLoad = true; //解决初始化时弹出弹窗问题
|
||||
watch(
|
||||
() => noticeStore.state.value.isNotice,
|
||||
(newVal) => {
|
||||
// if (noticeFirstLoad) {
|
||||
// noticeFirstLoad = false;
|
||||
// return;
|
||||
// }
|
||||
if (newVal) {
|
||||
noticeStore.state.value.isNotice = false;
|
||||
popoverVisible.value = true;
|
||||
}
|
||||
},
|
||||
{ deep: true }
|
||||
);
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@@ -95,10 +95,13 @@ const isLastView = () => {
|
||||
};
|
||||
const filterAffixTags = (routes: RouteRecordRaw[], basePath = '') => {
|
||||
let tags: RouteLocationNormalized[] = [];
|
||||
const seen = new Set<string>();
|
||||
|
||||
routes.forEach((route) => {
|
||||
if (route.meta && route.meta.affix) {
|
||||
const tagPath = getNormalPath(basePath + '/' + route.path);
|
||||
if (!seen.has(tagPath)) {
|
||||
seen.add(tagPath);
|
||||
tags.push({
|
||||
hash: '',
|
||||
matched: [],
|
||||
@@ -111,6 +114,7 @@ const filterAffixTags = (routes: RouteRecordRaw[], basePath = '') => {
|
||||
meta: { ...route.meta }
|
||||
});
|
||||
}
|
||||
}
|
||||
if (route.children) {
|
||||
const tempTags = filterAffixTags(route.children, route.path);
|
||||
if (tempTags.length >= 1) {
|
||||
@@ -124,7 +128,6 @@ const initTags = () => {
|
||||
const res = filterAffixTags(routes.value);
|
||||
affixTags.value = res;
|
||||
for (const tag of res) {
|
||||
// Must have tag name
|
||||
if (tag.name) {
|
||||
useTagsViewStore().addVisitedView(tag);
|
||||
}
|
||||
@@ -136,6 +139,10 @@ const addTags = () => {
|
||||
route.meta.title = route.query.title as string;
|
||||
}
|
||||
if (name) {
|
||||
// 如果 visitedViews 中已有同 title 的固定标签(affix),不再添加普通标签
|
||||
if (visitedViews.value.some((v) => v.meta?.affix && v.title === route.meta?.title)) return;
|
||||
// 常规去重:已有同 path 或 fullPath 的标签则跳过
|
||||
if (visitedViews.value.some((v) => v.path === route.path || v.fullPath === route.fullPath)) return;
|
||||
useTagsViewStore().addView(route as any);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
>
|
||||
<template #header>
|
||||
<div class="flex w-full items-center justify-between gap-2 min-w-0 pr-8">
|
||||
<span class="truncate">实时监控</span>
|
||||
<span class="truncate">实时监控-{{ monitoringArea }}监控</span>
|
||||
</div>
|
||||
</template>
|
||||
<div class="monitor-video-wrap">
|
||||
@@ -52,25 +52,39 @@ import { listNotice } from '@/api/system/notice';
|
||||
import { initHls, destroyHls } from '@/utils/video';
|
||||
import { onBeforeRouteLeave } from 'vue-router';
|
||||
import { useWarningListStore } from '@/store/modules/warnningList';
|
||||
import type { WarningListResult } from '@/api/WarningList/types';
|
||||
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
|
||||
import { nextTick } from 'vue';
|
||||
import { useVideosStore } from '@/store/modules/video';
|
||||
const videosStore = useVideosStore();
|
||||
const monitoringArea = ref<string>('');
|
||||
|
||||
const warningListStore = useWarningListStore();
|
||||
const noticeStore = useNoticeStore();
|
||||
const { getWarningList, handleAction } = warningListStore;
|
||||
const { warningListTotal, warningList } = storeToRefs(warningListStore);
|
||||
|
||||
const props = defineProps<{
|
||||
warningList: any;
|
||||
}>();
|
||||
const { warningList } = toRefs(props);
|
||||
// const props = defineProps<{
|
||||
// warningList?: WarningListResult;
|
||||
// }>();
|
||||
const warningListRows = computed(() => warningList.value?.rows || []);
|
||||
// console.log('666666666666666666_warningListRows', warningListRows.value);
|
||||
const { readAll } = useNoticeStore();
|
||||
const monitorVideoRef = ref<HTMLVideoElement>();
|
||||
|
||||
const getList = async () => {
|
||||
try {
|
||||
await getWarningList({
|
||||
status: 0,
|
||||
pageNum: 1,
|
||||
pageSize: 300
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('获取消息列表失败', error);
|
||||
}
|
||||
};
|
||||
|
||||
// 定义变量内容
|
||||
const state = reactive({
|
||||
loading: false
|
||||
@@ -96,20 +110,23 @@ const parseNoticeItem = (notice: any) => {
|
||||
|
||||
//查看画面
|
||||
const handleSee = async (row: any) => {
|
||||
const videoId = row?.videoId;
|
||||
console.log('查看画面', row, 'videoId', videoId);
|
||||
const videoId = row?.deviceId;
|
||||
console.log('查看画面', row, 'deviceId', videoId);
|
||||
if (!videoId) {
|
||||
proxy?.$modal.msgError('视频ID不存在');
|
||||
return;
|
||||
}
|
||||
await videosStore.getVideos(videoId);
|
||||
monitorVideoUrl.value = videosStore.hisUrl;
|
||||
monitoringArea.value = row.monitoringArea;
|
||||
dialog.visible = true;
|
||||
await nextTick();
|
||||
initHls(monitorVideoUrl, monitorVideoRef);
|
||||
await handleAction(row.id);
|
||||
await getList();
|
||||
};
|
||||
|
||||
const handleMonitorDialogClose = (done: () => void) => {
|
||||
const handleMonitorDialogClose = async (done: () => void) => {
|
||||
// 卸载 Hls 与清理 video
|
||||
destroyHls(monitorVideoRef);
|
||||
done();
|
||||
@@ -194,9 +211,11 @@ onBeforeRouteLeave(() => {
|
||||
dialog.visible = false;
|
||||
monitorVideoUrl.value = '';
|
||||
});
|
||||
// onMounted(() => {
|
||||
// warningListStore.getWarningList();
|
||||
// });
|
||||
|
||||
onMounted(() => {
|
||||
getList();
|
||||
console.log('111111111111111111111_warningList.value', warningList.value);
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@@ -10,7 +10,8 @@ interface NoticeItem {
|
||||
|
||||
export const useNoticeStore = defineStore('notice', () => {
|
||||
const state = reactive({
|
||||
notices: [] as NoticeItem[]
|
||||
notices: [] as NoticeItem[],
|
||||
isNotice: false as boolean
|
||||
});
|
||||
|
||||
const addNotice = (notice: NoticeItem) => {
|
||||
|
||||
@@ -37,7 +37,12 @@ export const useTagsViewStore = defineStore('tagsView', () => {
|
||||
});
|
||||
};
|
||||
const addVisitedView = (view: RouteLocationNormalized): void => {
|
||||
if (visitedViews.value.some((v: RouteLocationNormalized) => v.path === view.path)) return;
|
||||
// 同时通过 path、fullPath、name 去重
|
||||
if (visitedViews.value.some((v: RouteLocationNormalized) =>
|
||||
v.path === view.path ||
|
||||
v.fullPath === view.fullPath ||
|
||||
(v.name && view.name && v.name === view.name)
|
||||
)) return;
|
||||
visitedViews.value.push(
|
||||
Object.assign({}, view, {
|
||||
title: view.meta?.title || 'no-name'
|
||||
|
||||
@@ -1,36 +1,45 @@
|
||||
/** 此数据源跟表列不一致,(后端接口重复使用因逻辑复杂, 经商定不动后端接口, 设置此数据源) 适用于消息 */
|
||||
/** 抽取公用代码更加复杂模糊-暂存数据源 */
|
||||
import { listDemo, updateDemo } from '@/api/WarningList/index';
|
||||
import { defineStore } from 'pinia';
|
||||
import { ref, computed } from 'vue';
|
||||
import { any } from 'vue-types';
|
||||
|
||||
export const useWarningListStore = defineStore('warningListStore', () => {
|
||||
// --- State (状态) ---
|
||||
// 相当于 Vuex 中的 state,用 ref 或 reactive 定义
|
||||
const warningList = ref([]);
|
||||
const warningList = ref();
|
||||
const warningListTotal = ref(0);
|
||||
// --- Getters (计算属性) ---
|
||||
// 相当于 Vuex 中的 getters,直接用 computed 定义
|
||||
|
||||
// --- Actions (动作) ---
|
||||
// 相当于 Vuex 中的 actions/mutations,直接写同步或异步函数
|
||||
async function getWarningList(deviceId?: string | number) {
|
||||
async function getWarningList(query?: any) {
|
||||
try {
|
||||
const response = await listDemo({
|
||||
deviceId,
|
||||
pageNum: 1,
|
||||
pageSize: 10
|
||||
...query
|
||||
// deviceId,
|
||||
// status: 0,
|
||||
// pageNum: 1,
|
||||
// pageSize: 10
|
||||
});
|
||||
// console.log('6666666666666666666', response);
|
||||
warningList.value = response.data || [];
|
||||
warningList.value = response.data;
|
||||
warningListTotal.value = response.data.total || 0;
|
||||
} catch (error) {
|
||||
console.error('获取消息列表失败', error);
|
||||
}
|
||||
}
|
||||
async function handleAction(ids: string | number | Array<string | number>) {
|
||||
try {
|
||||
await updateDemo(ids);
|
||||
} catch (error) {
|
||||
console.error('处理告警失败', error);
|
||||
}
|
||||
}
|
||||
|
||||
// 核心点:必须把需要暴露出去的变量和方法 return 出去!
|
||||
return {
|
||||
warningList,
|
||||
warningListTotal,
|
||||
getWarningList
|
||||
getWarningList,
|
||||
handleAction
|
||||
};
|
||||
});
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
import { getToken } from '@/utils/auth';
|
||||
import { ElNotification } from 'element-plus';
|
||||
import { useNoticeStore } from '@/store/modules/notice';
|
||||
import { toRefs } from 'vue';
|
||||
|
||||
// 初始化
|
||||
export const initSSE = (url: any) => {
|
||||
const noticeStore = useNoticeStore();
|
||||
const { isNotice, notices } = toRefs(noticeStore.state);
|
||||
if (import.meta.env.VITE_APP_SSE === 'false') {
|
||||
return;
|
||||
}
|
||||
@@ -40,12 +43,13 @@ export const initSSE = (url: any) => {
|
||||
time: new Date().toLocaleString()
|
||||
});
|
||||
const displayMessage = data.value.split('|')[0].trim();
|
||||
ElNotification({
|
||||
title: '消息',
|
||||
message: displayMessage,
|
||||
type: 'success',
|
||||
duration: 3000
|
||||
});
|
||||
// ElNotification({
|
||||
// title: '消息',
|
||||
// message: displayMessage,
|
||||
// type: 'success',
|
||||
// duration: 3000
|
||||
// });
|
||||
noticeStore.state.isNotice = true;
|
||||
data.value = null;
|
||||
});
|
||||
};
|
||||
|
||||
@@ -39,19 +39,18 @@
|
||||
<template #header>
|
||||
<el-row :gutter="10" class="flex justify-end mb8">
|
||||
<el-col :span="1.5">
|
||||
<el-button v-hasPermi="['demo:demo:edit']" type="success" plain icon="Edit" :disabled="multiple" @click="handleBatchProcess"
|
||||
>批量处理</el-button
|
||||
>
|
||||
<el-button type="success" plain icon="Edit" :disabled="multiple" @click="handleBatchProcess">批量处理</el-button>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</template>
|
||||
|
||||
<el-table v-loading="loading" border :data="demoList" @selection-change="handleSelectionChange">
|
||||
<el-table-column type="selection" width="55" align="center" />
|
||||
<el-table-column type="selection" width="55" align="center" :selectable="checkSelectable" />
|
||||
<el-table-column label="报警编号" align="center" prop="alarmCode" width="180" />
|
||||
<el-table-column label="报警时间" align="center" prop="alarmTime" min-width="170" />
|
||||
<el-table-column label="所属设备" align="center" prop="deviceName" min-width="120" show-overflow-tooltip />
|
||||
<el-table-column label="报警区域" align="center" prop="monitoringArea" min-width="120" show-overflow-tooltip />
|
||||
<!-- pay content -->
|
||||
<el-table-column label="报警图片" align="center" prop="alarmImage" min-width="120">
|
||||
<template #default="scope">
|
||||
<el-image
|
||||
@@ -133,6 +132,11 @@ import { listDemo, updateDemo } from '@/api/WarningList/index';
|
||||
//import { DemoVO, DemoQuery, DemoForm } from '@/api/WarningList/types';
|
||||
import { useDeviceStore } from '@/store/modules/equipmentList';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useWarningListStore } from '@/store/modules/warnningList';
|
||||
|
||||
const warningListStore = useWarningListStore();
|
||||
const { getWarningList, handleAction } = warningListStore;
|
||||
const { warningListTotal, warningList } = storeToRefs(warningListStore);
|
||||
|
||||
const deviceStore = useDeviceStore();
|
||||
const { deviceList } = storeToRefs(deviceStore);
|
||||
@@ -168,6 +172,7 @@ const initFormData: any = {
|
||||
processingTime: undefined
|
||||
};
|
||||
/* ?id=&deviceId=&pageSize=1&pageNum=1&beginTime=&endTime= */
|
||||
/** 查询参数 */
|
||||
const data = reactive({
|
||||
form: { ...initFormData },
|
||||
queryParams: {
|
||||
@@ -188,27 +193,35 @@ const data = reactive({
|
||||
|
||||
const { queryParams, form, rules, queryTime } = toRefs(data);
|
||||
|
||||
/** 获取列表 */
|
||||
const getList = async () => {
|
||||
loading.value = true;
|
||||
try {
|
||||
const res = await listDemo(queryParams.value);
|
||||
demoList.value = res.data.rows;
|
||||
total.value = res.data.total;
|
||||
await getWarningList({
|
||||
status: 0,
|
||||
pageNum: 1,
|
||||
pageSize: 300
|
||||
});
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
/** 取消按钮 */
|
||||
const cancel = () => {
|
||||
reset();
|
||||
dialog.visible = false;
|
||||
};
|
||||
|
||||
/** 重置按钮 */
|
||||
const reset = () => {
|
||||
form.value = { ...initFormData };
|
||||
demoFormRef.value?.resetFields();
|
||||
};
|
||||
|
||||
/** 查询按钮 */
|
||||
const handleQuery = () => {
|
||||
queryParams.value.pageNum = 1;
|
||||
queryParams.value.beginTime = queryTime.value[0];
|
||||
@@ -216,12 +229,13 @@ const handleQuery = () => {
|
||||
getList();
|
||||
};
|
||||
|
||||
/** 重置按钮 */
|
||||
const resetQuery = () => {
|
||||
queryFormRef.value?.resetFields();
|
||||
queryTime.value = [];
|
||||
handleQuery();
|
||||
};
|
||||
|
||||
/** 多选框选中数据 */
|
||||
const handleSelectionChange = (selection: any) => {
|
||||
ids.value = selection.map((item) => item.id as string | number);
|
||||
multiple.value = !selection.length;
|
||||
@@ -233,7 +247,7 @@ const handleUpdate = async (row: any) => {
|
||||
|
||||
loading.value = true;
|
||||
try {
|
||||
await updateDemo(row.id);
|
||||
await handleAction(row.id);
|
||||
proxy?.$modal.msgSuccess('处理成功');
|
||||
await getList();
|
||||
} finally {
|
||||
@@ -241,6 +255,11 @@ const handleUpdate = async (row: any) => {
|
||||
}
|
||||
};
|
||||
|
||||
/** 检查行是否可选(已处理不可选) */
|
||||
const checkSelectable = (row: any) => {
|
||||
return row.status !== 1;
|
||||
};
|
||||
|
||||
/** 批量:确认后直接提交 */
|
||||
const handleBatchProcess = async () => {
|
||||
if (!ids.value.length) {
|
||||
@@ -250,7 +269,7 @@ const handleBatchProcess = async () => {
|
||||
await proxy?.$modal.confirm('是否确认批量处理已选中的报警?');
|
||||
loading.value = true;
|
||||
try {
|
||||
await updateDemo(ids.value);
|
||||
await handleAction(ids.value);
|
||||
proxy?.$modal.msgSuccess('处理成功');
|
||||
await getList();
|
||||
} finally {
|
||||
@@ -258,6 +277,7 @@ const handleBatchProcess = async () => {
|
||||
}
|
||||
};
|
||||
|
||||
/** 提交按钮 */
|
||||
const submitForm = async () => {
|
||||
if (form.value.id == null) {
|
||||
proxy?.$modal.msgWarning('数据异常');
|
||||
@@ -291,4 +311,11 @@ onMounted(() => {
|
||||
getList();
|
||||
getDeviceName();
|
||||
});
|
||||
|
||||
//监听数据源变化,自动刷新列表
|
||||
watch(warningListTotal, async (newVal) => {
|
||||
//console.log('监听到报警列表变化,自动刷新列表', newVal);
|
||||
//数据源不同, 接口复用,请求多了一遍(不属于bug)
|
||||
await getList();
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -65,10 +65,19 @@
|
||||
<el-tag v-else>{{ scope.row.status ?? '-' }}</el-tag>
|
||||
</template> -->
|
||||
</el-table-column>
|
||||
<el-table-column label="电池电量" align="center" prop="batteryLevel">
|
||||
<el-table-column label="电池电量" align="center" prop="batteryLevel1" width="150">
|
||||
<template #default="scope">
|
||||
<el-tag v-if="scope.row.batteryLevel">{{ scope.row.batteryLevel + '%' }}</el-tag>
|
||||
<el-tag v-else type="info"> 未获取 </el-tag>
|
||||
<!-- <el-button type="primary" v-if="scope.row.batteryLevel1" plain size="small">{{ scope.row.batteryLevel1 + '%' }}</el-button>
|
||||
<el-button type="primary" v-if="scope.row.batteryLevel2" plain size="small">
|
||||
{{ scope.row.batteryLevel2 + '%' }}
|
||||
</el-button> -->
|
||||
<!-- <el-button type="primary" v-else plain size="small"> 未获取 </el-button> -->
|
||||
<el-tag v-if="scope.row.batteryLevel1">{{ scope.row.batteryLevel1 + '%' }}</el-tag>
|
||||
<el-tag v-if="scope.row.batteryLevel2" :style="scope.row.batteryLevel1 && scope.row.batteryLevel2 ? 'margin-left: 10px' : ''">
|
||||
{{ scope.row.batteryLevel2 + '%' }}
|
||||
</el-tag>
|
||||
<!-- <el-tag v-else type="info"> 未获取 </el-tag> -->
|
||||
<el-tag v-if="!scope.row.batteryLevel1 && !scope.row.batteryLevel2"> 未获取 </el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="设备型号" align="center" prop="model" />
|
||||
@@ -123,7 +132,7 @@
|
||||
<el-dialog v-model="dialog1.visible" class="monitor-dialog" :before-close="handleMonitorDialogClose" fullscreen destroy-on-close>
|
||||
<template #header>
|
||||
<div class="flex w-full items-center justify-between gap-2 min-w-0 pr-8">
|
||||
<span class="truncate">实时监控</span>
|
||||
<span class="truncate">{{ videoName }}-{{ monitoringArea }}监控</span>
|
||||
<div>
|
||||
<el-button v-if="!seeSee" type="primary" class="shrink-0" @click="drawImage()">绘制区域</el-button>
|
||||
<el-button v-if="seeSee" type="primary" class="shrink-0" @click="clearImage()">清空区域</el-button>
|
||||
@@ -165,6 +174,7 @@ const multiple = ref(true);
|
||||
const total = ref(0);
|
||||
const seeSee = ref(0);
|
||||
const haveCode = ref(false);
|
||||
const videoName = ref<string>('实时监控');
|
||||
|
||||
const queryFormRef = ref<ElFormInstance>();
|
||||
const demoFormRef = ref<ElFormInstance>();
|
||||
@@ -181,6 +191,8 @@ const dialog1 = reactive<DialogOption>({
|
||||
const monitorVideoUrl = ref('');
|
||||
const monitorVideoRef = ref<HTMLVideoElement>();
|
||||
let hlsInstance: Hls | null = null;
|
||||
let visibilityHandler: (() => void) | null = null;
|
||||
const monitoringArea = ref<string>('');
|
||||
|
||||
const initHls = () => {
|
||||
const url = monitorVideoUrl.value;
|
||||
@@ -400,6 +412,7 @@ const handleSee = async (row?: any) => {
|
||||
reset();
|
||||
resetMonitorDraw();
|
||||
seeSee.value = row?.isDrawArea === 1 ? 1 : 0;
|
||||
monitoringArea.value = row?.monitoringArea;
|
||||
const _id = row?.id || ids.value[0];
|
||||
const res = await getDeviceInfo(_id);
|
||||
Object.assign(form.value, res.data);
|
||||
@@ -454,6 +467,18 @@ const handleSee = async (row?: any) => {
|
||||
console.error('[查看监控] 解析 drawArea 失败:', error);
|
||||
}
|
||||
}
|
||||
// 后台运行的时候恢复播放
|
||||
cleanupVisibilityChange();
|
||||
const handleVisibilityChange = () => {
|
||||
if (!document.hidden) {
|
||||
const video = monitorVideoRef.value;
|
||||
if (video && video.paused) {
|
||||
video.play().catch((e) => console.warn('恢复播放失败:', e));
|
||||
}
|
||||
}
|
||||
};
|
||||
document.addEventListener('visibilitychange', handleVisibilityChange);
|
||||
visibilityHandler = handleVisibilityChange;
|
||||
};
|
||||
|
||||
// 提取显示报警区域的逻辑为独立函数
|
||||
@@ -530,6 +555,13 @@ const destroyVideoResizeObserver = () => {
|
||||
videoResizeObserver.value = null;
|
||||
}
|
||||
};
|
||||
// 清理 visibilitychange 事件监听
|
||||
const cleanupVisibilityChange = () => {
|
||||
if (visibilityHandler) {
|
||||
document.removeEventListener('visibilitychange', visibilityHandler);
|
||||
visibilityHandler = null;
|
||||
}
|
||||
};
|
||||
|
||||
const handleMonitorDialogClose = (done: () => void) => {
|
||||
// 卸载 Hls 与清理 video
|
||||
@@ -537,6 +569,7 @@ const handleMonitorDialogClose = (done: () => void) => {
|
||||
monitorVideoUrl.value = '';
|
||||
destroyVideoResizeObserver();
|
||||
resetMonitorDraw();
|
||||
cleanupVisibilityChange();
|
||||
done();
|
||||
//刷新列表
|
||||
getList();
|
||||
@@ -580,6 +613,7 @@ const payload = ref<AlarmAreaData[]>([]);
|
||||
const drawImage = () => {
|
||||
resetMonitorDraw();
|
||||
monitorDrawCollecting.value = true;
|
||||
videoName.value = '绘制区域';
|
||||
proxy?.$modal.msgSuccess('请在视频画面上连续点击两个点,生成报警区域');
|
||||
};
|
||||
|
||||
@@ -706,6 +740,13 @@ const handleExport = () => {
|
||||
onMounted(() => {
|
||||
getList();
|
||||
});
|
||||
|
||||
watch(
|
||||
() => seeSee.value,
|
||||
(newValue) => {
|
||||
videoName.value = newValue === 1 ? '绘制区域' : '实时监控';
|
||||
}
|
||||
);
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@@ -790,4 +831,19 @@ onMounted(() => {
|
||||
padding: 2px 6px;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
/* 隐藏视频进度条,保留其他控制按钮 */
|
||||
.monitor-video::-webkit-media-controls-timeline {
|
||||
display: none !important;
|
||||
}
|
||||
.monitor-video::-webkit-media-controls-current-time-display,
|
||||
.monitor-video::-webkit-media-controls-time-remaining-display {
|
||||
display: none !important;
|
||||
}
|
||||
.monitor-video::-moz-range-track {
|
||||
display: none !important;
|
||||
}
|
||||
.monitor-video::-moz-progress-bar {
|
||||
display: none !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -74,7 +74,7 @@
|
||||
<el-dialog v-model="dialog.visible" :title="dialog.title" width="500px" class="monitor-dialog" fullscreen destroy-on-close>
|
||||
<template #header>
|
||||
<div class="flex w-full items-center justify-between gap-2 min-w-0 pr-8">
|
||||
<span class="truncate">实时监控-北门出入门监控</span>
|
||||
<span class="truncate">播放-{{ videoName }}</span>
|
||||
</div>
|
||||
</template>
|
||||
<div class="monitor-video-wrap">
|
||||
@@ -109,6 +109,7 @@ const multiple = ref(true);
|
||||
const total = ref(0);
|
||||
const monitorVideoUrl = ref();
|
||||
const downloadUrl = ref();
|
||||
const videoName = ref<string>('录像文件');
|
||||
|
||||
const queryFormRef = ref<ElFormInstance>();
|
||||
const demoFormRef = ref<ElFormInstance>();
|
||||
@@ -218,6 +219,7 @@ const handlePlay = async (row?: any) => {
|
||||
return;
|
||||
}
|
||||
monitorVideoUrl.value = row.videoUrl;
|
||||
videoName.value = row.videoName;
|
||||
dialog.visible = true;
|
||||
};
|
||||
const handleDown = async (row?: any) => {
|
||||
|
||||
@@ -24,7 +24,8 @@ export default defineConfig(({ mode, command }) => {
|
||||
open: true,
|
||||
proxy: {
|
||||
[env.VITE_APP_BASE_API]: {
|
||||
target: 'http://47.104.208.94:8089',
|
||||
// target: 'http://47.104.208.94:8089',
|
||||
target: 'http://192.168.0.214:8089',
|
||||
changeOrigin: true,
|
||||
ws: true,
|
||||
rewrite: (path) => path.replace(new RegExp('^' + env.VITE_APP_BASE_API), '')
|
||||
|
||||
Reference in New Issue
Block a user