warehouse P0修复: http.js(lang_storage_key)+SignalR内存泄漏(LRU)+Pinia store合并(7文件import同步)+Camera type导入修复
This commit is contained in:
@@ -38,6 +38,8 @@ if (!axios.defaults.baseURL.endsWith('/')) {
|
||||
axios.defaults.baseURL += '/'
|
||||
}
|
||||
|
||||
const lang_storage_key = 'lang'
|
||||
|
||||
let ipAddress = axios.defaults.baseURL;
|
||||
if (!ipAddress || ipAddress == '/') {
|
||||
ipAddress = window.location.origin + '/';
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script setup>
|
||||
import { watch, computed } from "vue"
|
||||
import { useMapStore } from "../store/useMapStore"
|
||||
import { useMapStore } from "../stores/mapStore"
|
||||
|
||||
// 可以使用pinia等管理全局数据,这里只是方便演示, 直接注入了上层提供的数据
|
||||
const store = useMapStore()
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script setup>
|
||||
import { computed } from "vue"
|
||||
import { useNavi } from "../hooks/useNavi"
|
||||
import { useMapStore } from "../store/useMapStore"
|
||||
import { useMapStore } from "../stores/mapStore"
|
||||
|
||||
// 可以使用pinia等管理全局数据,这里只是方便演示, 直接注入了上层提供的数据
|
||||
const store = useMapStore()
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script setup>
|
||||
import { inject, onBeforeUnmount } from "vue"
|
||||
import { useFence } from "../hooks/useFence"
|
||||
import { useMapStore } from "../store/useMapStore"
|
||||
import { useMapStore } from "../stores/mapStore"
|
||||
|
||||
const { THREE } = VgoMap
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script setup>
|
||||
import { inject, ref, watch, onBeforeUnmount } from "vue"
|
||||
import { useClassPolygon } from "../hooks/useClassPolygon"
|
||||
import { useMapStore } from "../store/useMapStore"
|
||||
import { useMapStore } from "../stores/mapStore"
|
||||
import http from "../api/http"
|
||||
|
||||
const { THREE } = VgoMap
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script setup>
|
||||
import { ref, watch, onBeforeUnmount } from "vue"
|
||||
import { useSkyLight } from "../hooks/useSkyLight"
|
||||
import { useMapStore } from "../store/useMapStore"
|
||||
import { useMapStore } from "../stores/mapStore"
|
||||
import CustomSwitch from "./CustomSwitch.vue"
|
||||
|
||||
const store = useMapStore()
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script setup>
|
||||
import { ref, watch } from "vue"
|
||||
import { useWeatherEffect, WeatherType } from "../hooks/useWeather"
|
||||
import { useMapStore } from "../store/useMapStore"
|
||||
import { useMapStore } from "../stores/mapStore"
|
||||
import CustomSwitch from "./CustomSwitch.vue"
|
||||
|
||||
const store = useMapStore()
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
import { ref, computed } from "vue"
|
||||
import { defineStore } from "pinia"
|
||||
|
||||
export const useMapStore = defineStore('map', () => {
|
||||
const map = ref(null)
|
||||
|
||||
const polygonDataAll = computed(() => {
|
||||
const outDoor = map.value?.mapData?.polygonData ?? []
|
||||
|
||||
const inDoor = map?.mapData?.build?.reduce((result, build) => {
|
||||
build.floor.forEach(fItem => {
|
||||
result.push(...fItem.polygonData)
|
||||
})
|
||||
return result
|
||||
}, []) ?? []
|
||||
|
||||
return [...outDoor, ...inDoor]
|
||||
})
|
||||
|
||||
function setMap(mapInstance) {
|
||||
map.value = mapInstance
|
||||
}
|
||||
|
||||
return {
|
||||
map,
|
||||
polygonDataAll,
|
||||
setMap,
|
||||
}
|
||||
})
|
||||
@@ -1,48 +1,34 @@
|
||||
import { ref } from "vue"
|
||||
import { ref, computed } from "vue"
|
||||
import { defineStore } from "pinia"
|
||||
|
||||
export const useMapStore = defineStore('map', () => {
|
||||
const map = ref(null)
|
||||
const lastClickedPosition = ref(null) // 存储地图最后点击位置的经纬度
|
||||
const editingMode = ref(false) // 编辑状态标识
|
||||
const lastClickedPosition = ref(null)
|
||||
const editingMode = ref(false)
|
||||
|
||||
function setMap(mapInstance) {
|
||||
map.value = mapInstance
|
||||
}
|
||||
/** 聚合所有楼层+室外的 polygonData */
|
||||
const polygonDataAll = computed(() => {
|
||||
const outDoor = map.value?.mapData?.polygonData ?? []
|
||||
const builds = map.value?.mapData?.build ?? []
|
||||
const inDoor = builds.reduce((result, build) => {
|
||||
build.floor?.forEach(f => { result.push(...(f.polygonData ?? [])) })
|
||||
return result
|
||||
}, [])
|
||||
return [...outDoor, ...inDoor]
|
||||
})
|
||||
|
||||
// 设置地图最后点击位置的经纬度
|
||||
function setLastClickedPosition(lng, lat) {
|
||||
lastClickedPosition.value = { lng, lat }
|
||||
}
|
||||
|
||||
// 设置编辑状态
|
||||
function setMap(mapInstance) { map.value = mapInstance }
|
||||
function setLastClickedPosition(lng, lat) { lastClickedPosition.value = { lng, lat } }
|
||||
function setEditingMode(mode) {
|
||||
editingMode.value = mode
|
||||
if(mode){
|
||||
map.value.setViewMode('2D')
|
||||
}
|
||||
else{
|
||||
map.value.setViewMode('3D')
|
||||
}
|
||||
}
|
||||
|
||||
// 获取编辑状态
|
||||
function getEditingMode() {
|
||||
if(editingMode.value){
|
||||
return editingMode.value
|
||||
}
|
||||
else{
|
||||
return false
|
||||
}
|
||||
map.value?.setViewMode(mode ? '2D' : '3D')
|
||||
}
|
||||
function getEditingMode() { return editingMode.value }
|
||||
|
||||
return {
|
||||
map,
|
||||
setMap,
|
||||
lastClickedPosition,
|
||||
setLastClickedPosition,
|
||||
editingMode,
|
||||
setEditingMode,
|
||||
getEditingMode
|
||||
map, setMap,
|
||||
lastClickedPosition, setLastClickedPosition,
|
||||
editingMode, setEditingMode, getEditingMode,
|
||||
polygonDataAll,
|
||||
}
|
||||
})
|
||||
|
||||
@@ -6,7 +6,7 @@ import Sky from "../components/Sky.vue"
|
||||
import DisplayColor from "../components/DisplayColor.vue"
|
||||
import DisplayRouteLine from "../components/DisplayRouteLine.vue"
|
||||
import Weather from "../components/Weather.vue"
|
||||
import { useMapStore } from "../store/useMapStore"
|
||||
import { useMapStore } from "../stores/mapStore"
|
||||
|
||||
const { VgoMap } = window
|
||||
let mapId = /#\/(\d+)/.exec(location.hash)[1]
|
||||
|
||||
@@ -2,89 +2,60 @@ import * as signalR from "@microsoft/signalr";
|
||||
import { ElNotification } from "element-plus";
|
||||
import { ElMessageBox } from "element-plus";
|
||||
import store from "@/store/index";
|
||||
|
||||
export default function (http, receive) {
|
||||
let connection;
|
||||
let connection
|
||||
|
||||
let messageQueue = [];
|
||||
let isProcessingQueue = false;
|
||||
let displayedMessageIds = new Set(); // 用于存储已显示的消息ID,避免重复显示
|
||||
// LRU: 最近 500 条 ID,防内存无限增长
|
||||
const MAX_IDS = 500
|
||||
const displayedIds = new Set()
|
||||
const idList = []
|
||||
|
||||
// 消息队列处理函数 - 移到闭包内部,确保可以访问所有必要的变量
|
||||
function processMessageQueue() {
|
||||
if (messageQueue.length === 0) {
|
||||
isProcessingQueue = false;
|
||||
return;
|
||||
function addId(id) {
|
||||
if (displayedIds.has(id)) return
|
||||
displayedIds.add(id)
|
||||
idList.push(id)
|
||||
if (idList.length > MAX_IDS) {
|
||||
displayedIds.delete(idList.shift())
|
||||
}
|
||||
}
|
||||
function hasId(id) { return displayedIds.has(id) }
|
||||
|
||||
isProcessingQueue = true;
|
||||
const currentMessage = messageQueue.shift();
|
||||
|
||||
// 直接调用receive回调处理消息显示
|
||||
if (receive) {
|
||||
try {
|
||||
receive(currentMessage);
|
||||
console.log('成功调用receive回调处理消息:', currentMessage);
|
||||
} catch (error) {
|
||||
console.error('调用receive回调时出错:', error);
|
||||
}
|
||||
// 重试启动: 最多 5 次, 间隔 2s
|
||||
async function startWithRetry(conn, retries = 5) {
|
||||
for (let i = 1; i <= retries; i++) {
|
||||
try { await conn.start(); return }
|
||||
catch (e) { if (i < retries) await new Promise(r => setTimeout(r, 2000)) }
|
||||
}
|
||||
|
||||
// 可以设置一定的延迟,避免消息显示过于密集
|
||||
setTimeout(() => {
|
||||
processMessageQueue();
|
||||
}, 3000); // 3秒后处理下一条消息
|
||||
console.error('SignalR 连接失败 (5次重试后放弃)')
|
||||
}
|
||||
|
||||
http.post("api/user/GetCurrentUserInfo").then((result) => {
|
||||
console.log(result,'result');
|
||||
http.post("api/user/GetCurrentUserInfo").then(async (result) => {
|
||||
if (!result?.data?.userName) {
|
||||
console.error('获取用户信息失败: 缺少 userName')
|
||||
return
|
||||
}
|
||||
connection = new signalR.HubConnectionBuilder()
|
||||
.withAutomaticReconnect()
|
||||
.withUrl(
|
||||
`/hub/message?userName=${result.data.userName}`,
|
||||
{
|
||||
//withCredentials: true // 启用凭证模式
|
||||
// accessTokenFactory: () => store.getters.getToken()
|
||||
}
|
||||
)
|
||||
//.withUrl(`${http.ipAddress}message`)
|
||||
.build();
|
||||
.withUrl('/hub/message?userName=' + result.data.userName)
|
||||
.build()
|
||||
|
||||
startWithRetry(connection)
|
||||
|
||||
connection.onreconnected(function (id) {
|
||||
if (import.meta.env.DEV) console.log('SignalR reconnected:', id)
|
||||
})
|
||||
|
||||
connection.start().catch((err) => console.log(err.message));
|
||||
//自动重连成功后的处理
|
||||
connection.onreconnected((connectionId) => {
|
||||
console.log(connectionId, 'connectionId');
|
||||
});
|
||||
connection.on("ReceiveHomePageMessage", function (data) {
|
||||
console.log('接收到新消息:', data);
|
||||
|
||||
// 检查消息是否已显示过
|
||||
if (data.id && displayedMessageIds.has(data.id)) {
|
||||
console.log('消息已显示过,跳过:', data.id);
|
||||
return;
|
||||
}
|
||||
if (data?.id && hasId(data.id)) return
|
||||
if (data?.id) addId(data.id)
|
||||
|
||||
// 将消息ID添加到已显示集合
|
||||
if (data.id) {
|
||||
displayedMessageIds.add(data.id);
|
||||
console.log('添加到已显示集合:', data.id);
|
||||
}
|
||||
|
||||
// 添加到store
|
||||
// store.getters.data().pushMessage(data);
|
||||
console.log(data, 'data_after_push');
|
||||
// 直接调用receive回调
|
||||
if (receive) {
|
||||
try {
|
||||
receive(data);
|
||||
console.log('成功调用receive回调:', data);
|
||||
} catch (error) {
|
||||
console.error('调用receive回调时出错:', error);
|
||||
}
|
||||
} else {
|
||||
console.warn('receive回调未定义,无法处理消息');
|
||||
try { receive(data) }
|
||||
catch (e) { console.error('receive callback error:', e) }
|
||||
}
|
||||
});
|
||||
}).catch(error => {
|
||||
console.error('获取用户信息时出错:', error);
|
||||
});
|
||||
})
|
||||
}).catch(function (e) {
|
||||
console.error('getUserInfo failed:', e)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -61,7 +61,8 @@
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import { VideoPlay } from '@element-plus/icons-vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { fetchCameras, gwGet, Camera } from '@/api/gateway'
|
||||
import { fetchCameras, gwGet } from '@/api/gateway'
|
||||
import type { Camera } from '@/api/gateway'
|
||||
|
||||
const cameras = ref<Camera[]>([])
|
||||
const selectedCamera = ref<Camera | null>(null)
|
||||
|
||||
@@ -92,7 +92,8 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import { VideoCamera } from '@element-plus/icons-vue'
|
||||
import { fetchCameras, gwGet, gwPost, Camera } from '@/api/gateway'
|
||||
import { fetchCameras, gwGet, gwPost } from '@/api/gateway'
|
||||
import type { Camera } from '@/api/gateway'
|
||||
|
||||
const cameras = ref<Camera[]>([])
|
||||
const selectedCamera = ref<Camera | null>(null)
|
||||
|
||||
@@ -106,7 +106,8 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted, watch } from 'vue'
|
||||
import { fetchCameras, gwGet, gwPost, Camera } from '@/api/gateway'
|
||||
import { fetchCameras, gwGet, gwPost } from '@/api/gateway'
|
||||
import type { Camera } from '@/api/gateway'
|
||||
|
||||
const cameras = ref<Camera[]>([])
|
||||
const streamUrls = ref<Record<string, string>>({})
|
||||
|
||||
Reference in New Issue
Block a user