|
|
|
@ -126,6 +126,7 @@ |
|
|
|
:is-icon-edit-mode="isIconEditMode" |
|
|
|
:current-scale-config="scaleConfig" |
|
|
|
:map-drag-enabled="mapDragEnabled" |
|
|
|
:show-room-access-manage="roomAccessNavVisible" |
|
|
|
@select-nav="selectTopNav" |
|
|
|
@toggle-map-drag="mapDragEnabled = !mapDragEnabled" |
|
|
|
@set-k-time="openKTimeSetDialog" |
|
|
|
@ -178,6 +179,11 @@ |
|
|
|
@route-favorites="routeFavorites" |
|
|
|
@show-online-members="showOnlineMembersDialog" |
|
|
|
@edit-user-profile="openUserProfileDialog" |
|
|
|
@open-room-access-manage="showRoomAccessManageDialog = true" |
|
|
|
/> |
|
|
|
<room-access-manage-dialog |
|
|
|
v-model="showRoomAccessManageDialog" |
|
|
|
:room-id="currentRoomId" |
|
|
|
/> |
|
|
|
<!-- 左侧折叠菜单栏 - 蓝色主题 --> |
|
|
|
<left-menu |
|
|
|
@ -210,6 +216,7 @@ |
|
|
|
:selected-plan-details="selectedPlanDetails" |
|
|
|
:selected-route-id="selectedRouteId" |
|
|
|
:routes="routes" |
|
|
|
:peer-routes="peerRoutes" |
|
|
|
:active-route-ids="activeRouteIds" |
|
|
|
:route-locked="routeLocked" |
|
|
|
:route-locked-by="routeLockedBy" |
|
|
|
@ -238,7 +245,7 @@ |
|
|
|
@open-import-dialog="showImportDialog = true" |
|
|
|
/> |
|
|
|
<!-- 左下角工具面板(白板模式下隐藏,避免遮挡白板) --> |
|
|
|
<bottom-left-panel v-show="!screenshotMode && !showWhiteboardPanel" @bottom-panel-visible="handleBottomPanelVisible" @six-steps-overlay-visible="sixStepsOverlayVisible = $event" @open-whiteboard="toggleWhiteboardMode" :room-id="currentRoomId" :is-parent-room="!!(roomDetail && roomDetail.parentId == null)" /> |
|
|
|
<bottom-left-panel ref="bottomLeftPanel" v-show="!screenshotMode && !showWhiteboardPanel" @bottom-panel-visible="handleBottomPanelVisible" @six-steps-overlay-visible="sixStepsOverlayVisible = $event" @open-whiteboard="toggleWhiteboardMode" @six-steps-broadcast="onParentSixStepsBroadcast" :room-id="currentRoomId" :is-parent-room="!!(roomDetail && roomDetail.parentId == null)" /> |
|
|
|
<!-- 底部时间轴(最初版本的样式)- 蓝色主题 --> |
|
|
|
<div |
|
|
|
v-show="!screenshotMode" |
|
|
|
@ -549,6 +556,7 @@ import ExternalParamsDialog from '@/views/dialogs/ExternalParamsDialog' |
|
|
|
import PageLayoutDialog from '@/views/dialogs/PageLayoutDialog' |
|
|
|
import KTimeSetDialog from '@/views/dialogs/KTimeSetDialog' |
|
|
|
import UserProfileDialog from '@/views/dialogs/UserProfileDialog' |
|
|
|
import RoomAccessManageDialog from '@/views/dialogs/RoomAccessManageDialog' |
|
|
|
import GenerateAirspaceDialog from '@/views/dialogs/GenerateAirspaceDialog' |
|
|
|
import LeftMenu from './LeftMenu' |
|
|
|
import RightPanel from './RightPanel' |
|
|
|
@ -563,7 +571,7 @@ import { listScenario, addScenario, delScenario } from "@/api/system/scenario"; |
|
|
|
import { listRoutes, getRoutes, addRoutes, updateRoutes, delRoutes, getPlatformStyle, savePlatformStyle, getMissileParams, updateMissilePositions, saveMissileParams, deleteMissileParams } from "@/api/system/routes"; |
|
|
|
import { updateWaypoints, addWaypoints, delWaypoints } from "@/api/system/waypoints"; |
|
|
|
import { listLib,addLib,delLib} from "@/api/system/lib"; |
|
|
|
import { getRooms, updateRooms, listRooms } from "@/api/system/rooms"; |
|
|
|
import { getRooms, updateRooms, listRooms, checkRoomAccess, getRoomAccessManageContext, checkIsHost } from "@/api/system/rooms"; |
|
|
|
import { getMenuConfig, saveMenuConfig } from "@/api/system/userMenuConfig"; |
|
|
|
import { listByRoomId as listRoomPlatformIcons, addRoomPlatformIcon, updateRoomPlatformIcon, delRoomPlatformIcon } from "@/api/system/roomPlatformIcon"; |
|
|
|
import { |
|
|
|
@ -605,6 +613,7 @@ export default { |
|
|
|
PageLayoutDialog, |
|
|
|
KTimeSetDialog, |
|
|
|
UserProfileDialog, |
|
|
|
RoomAccessManageDialog, |
|
|
|
GenerateAirspaceDialog, |
|
|
|
LeftMenu, |
|
|
|
RightPanel, |
|
|
|
@ -626,6 +635,9 @@ export default { |
|
|
|
// 在线成员弹窗 |
|
|
|
showOnlineMembers: false, |
|
|
|
showUserProfileDialog: false, |
|
|
|
showRoomAccessManageDialog: false, |
|
|
|
roomAccessNavVisible: false, |
|
|
|
isCurrentRoomHost: false, |
|
|
|
// 编辑弹窗控制 |
|
|
|
showPlatformDialog: false, |
|
|
|
selectedPlatform: null, |
|
|
|
@ -688,6 +700,10 @@ export default { |
|
|
|
onlineCount: 0, |
|
|
|
wsOnlineMembers: [], |
|
|
|
wsConnection: null, |
|
|
|
/** 大房间 SYNC_SIX_STEPS 早于 roomDetail 返回时暂存,roomDetail 就绪后补应用 */ |
|
|
|
_pendingSyncSixSteps: null, |
|
|
|
/** 大房间:六步法已开但 WebSocket 尚未 connected 时待发送的开关 */ |
|
|
|
_pendingParentSixStepsSend: null, |
|
|
|
chatMessages: [], |
|
|
|
privateChatMessages: {}, |
|
|
|
combatTime: 'K+00:00:00', // 进入房间时固定作战时间,不随真实时间走 |
|
|
|
@ -810,6 +826,8 @@ export default { |
|
|
|
// 右侧面板 |
|
|
|
currentRoomId: null, |
|
|
|
plans: [], |
|
|
|
/** 同一父房间下兄弟小房间的航线列表(仅展示) */ |
|
|
|
peerRoutes: [], |
|
|
|
activeRightTab: 'plan', |
|
|
|
activeRouteIds: [], // 存储当前所有选中的航线ID |
|
|
|
/** 新加入时收到的房间可见航线 ID,若 routes 未加载则暂存,getList 完成后应用 */ |
|
|
|
@ -1319,6 +1337,7 @@ export default { |
|
|
|
if (response.code === 200 && response.data) { |
|
|
|
const routeData = response.data; |
|
|
|
// 构造航线对象,保持和右侧面板编辑时一致的格式 |
|
|
|
const listMeta = this.routes.find(r => r.id === routeData.id) || this.peerRoutes.find(r => r.id === routeData.id); |
|
|
|
const route = { |
|
|
|
id: routeData.id, |
|
|
|
name: routeData.callSign, |
|
|
|
@ -1326,8 +1345,11 @@ export default { |
|
|
|
platformId: routeData.platformId, |
|
|
|
platform: routeData.platform, |
|
|
|
attributes: routeData.attributes, |
|
|
|
creatorId: routeData.creatorId || (listMeta && listMeta.creatorId) || null, |
|
|
|
points: routeData.waypoints ? routeData.waypoints.length : 0, |
|
|
|
waypoints: routeData.waypoints || [] |
|
|
|
waypoints: routeData.waypoints || [], |
|
|
|
peerViewOnly: !!(listMeta && listMeta.peerViewOnly), |
|
|
|
sourceRoomId: listMeta && listMeta.sourceRoomId |
|
|
|
}; |
|
|
|
// 打开航线编辑弹窗 |
|
|
|
this.openRouteDialog(route); |
|
|
|
@ -2409,8 +2431,21 @@ export default { |
|
|
|
const newer = existing.filter(m => (m.timestamp || 0) > maxTs); |
|
|
|
this.$set(this.privateChatMessages, targetUserId, [...history, ...newer]); |
|
|
|
}, |
|
|
|
onSyncRouteVisibility: () => { |
|
|
|
// 小房间内每人展示各自内容,不应用他人的航线显隐变更 |
|
|
|
onSyncRouteVisibility: (rid, vis, senderSessionId, styleRoomId) => { |
|
|
|
if (this.isMySyncSession(senderSessionId)) return; |
|
|
|
this.applySyncRouteVisibility(rid, vis, styleRoomId); |
|
|
|
}, |
|
|
|
onSyncSixSteps: (open) => { |
|
|
|
// 大房间不跟自己的广播(仅子房间需要跟父房间) |
|
|
|
if (this.roomDetail && this.roomDetail.parentId == null) return; |
|
|
|
// 注意:子房间 MEMBER_LIST 会聚合父房间成员,同一用户的父房间 sessionId 也在 mySyncSessionIds 中, |
|
|
|
// 若此处用 isMySyncSession 会把「父房间发来的六步法同步」误判为「自己发的」而直接丢弃。 |
|
|
|
const wantOpen = !!open; |
|
|
|
if (!this.roomDetail) { |
|
|
|
this._pendingSyncSixSteps = wantOpen; |
|
|
|
return; |
|
|
|
} |
|
|
|
this.applyRemoteSixStepsFromParent(wantOpen); |
|
|
|
}, |
|
|
|
onSyncWaypoints: (routeId, senderSessionId) => { |
|
|
|
if (this.isMySyncSession(senderSessionId)) return; |
|
|
|
@ -2433,6 +2468,13 @@ export default { |
|
|
|
}, |
|
|
|
onConnected: () => { |
|
|
|
setTimeout(() => this.pushUserSelectionNow(), 200); |
|
|
|
if (this._pendingParentSixStepsSend != null && this.roomDetail && this.roomDetail.parentId == null) { |
|
|
|
const v = this._pendingParentSixStepsSend; |
|
|
|
this._pendingParentSixStepsSend = null; |
|
|
|
if (this.wsConnection && this.wsConnection.connected && this.wsConnection.sendSyncSixSteps) { |
|
|
|
this.wsConnection.sendSyncSixSteps(v); |
|
|
|
} |
|
|
|
} |
|
|
|
}, |
|
|
|
onDisconnected: () => { |
|
|
|
this.onlineCount = 0; |
|
|
|
@ -2453,6 +2495,7 @@ export default { |
|
|
|
this.wsConnection.disconnect(); |
|
|
|
this.wsConnection = null; |
|
|
|
} |
|
|
|
this._pendingParentSixStepsSend = null; |
|
|
|
this.roomStatePendingVisibleRouteIds = []; |
|
|
|
this.wsOnlineMembers = []; |
|
|
|
this.mySyncSessionIds = []; |
|
|
|
@ -2534,10 +2577,31 @@ export default { |
|
|
|
if (!senderSessionId) return false; |
|
|
|
return Array.isArray(this.mySyncSessionIds) && this.mySyncSessionIds.includes(senderSessionId); |
|
|
|
}, |
|
|
|
/** 收到其他设备的航线显隐同步:直接应用变更,不经过 selectRoute 的 toggle 逻辑,避免互相干扰 */ |
|
|
|
async applySyncRouteVisibility(routeId, visible) { |
|
|
|
const route = this.routes.find(r => r.id === routeId); |
|
|
|
/** 收到父子/兄弟房间的航线显隐同步(styleRoomId 用于 Redis 平台样式所在房间) */ |
|
|
|
async applySyncRouteVisibility(routeId, visible, styleRoomIdFromMsg) { |
|
|
|
let route = this.routes.find(r => r.id === routeId) || this.peerRoutes.find(r => r.id === routeId); |
|
|
|
if (!route && visible) { |
|
|
|
try { |
|
|
|
const res = await getRoutes(routeId); |
|
|
|
if (res.code !== 200 || !res.data) return; |
|
|
|
const d = res.data; |
|
|
|
route = { |
|
|
|
id: d.id, |
|
|
|
name: d.callSign, |
|
|
|
platformId: d.platformId, |
|
|
|
platform: d.platform, |
|
|
|
attributes: d.attributes, |
|
|
|
scenarioId: d.scenarioId, |
|
|
|
peerViewOnly: true, |
|
|
|
sourceRoomId: styleRoomIdFromMsg || null, |
|
|
|
points: (d.waypoints || []).length |
|
|
|
}; |
|
|
|
} catch (_) { |
|
|
|
return; |
|
|
|
} |
|
|
|
} |
|
|
|
if (!route) return; |
|
|
|
const styleRoomId = styleRoomIdFromMsg != null ? styleRoomIdFromMsg : this.resolveStyleRoomIdForRoute(route); |
|
|
|
if (visible) { |
|
|
|
if (this.activeRouteIds.includes(routeId)) return; |
|
|
|
try { |
|
|
|
@ -2558,11 +2622,14 @@ export default { |
|
|
|
if (routeIndex > -1) { |
|
|
|
this.$set(this.routes, routeIndex, { ...this.routes[routeIndex], waypoints }); |
|
|
|
} |
|
|
|
const pi = this.peerRoutes.findIndex(r => r.id === route.id); |
|
|
|
if (pi > -1) { |
|
|
|
this.$set(this.peerRoutes, pi, { ...this.peerRoutes[pi], waypoints }); |
|
|
|
} |
|
|
|
if (waypoints.length > 0 && this.$refs.cesiumMap) { |
|
|
|
const roomId = this.currentRoomId; |
|
|
|
if (roomId && route.platformId) { |
|
|
|
if (styleRoomId && route.platformId) { |
|
|
|
try { |
|
|
|
const styleRes = await getPlatformStyle({ roomId, routeId: route.id, platformId: route.platformId }); |
|
|
|
const styleRes = await getPlatformStyle({ roomId: styleRoomId, routeId: route.id, platformId: route.platformId }); |
|
|
|
if (styleRes.data) this.$refs.cesiumMap.setPlatformStyle(route.id, styleRes.data); |
|
|
|
} catch (_) {} |
|
|
|
} |
|
|
|
@ -2580,7 +2647,7 @@ export default { |
|
|
|
const lastId = this.activeRouteIds[this.activeRouteIds.length - 1]; |
|
|
|
const res = await getRoutes(lastId); |
|
|
|
if (res.code === 200 && res.data) { |
|
|
|
const fromList = this.routes.find(r => r.id === lastId); |
|
|
|
const fromList = this.routes.find(r => r.id === lastId) || this.peerRoutes.find(r => r.id === lastId); |
|
|
|
this.selectedRouteId = res.data.id; |
|
|
|
this.selectedRouteDetails = { |
|
|
|
id: res.data.id, |
|
|
|
@ -2605,7 +2672,8 @@ export default { |
|
|
|
const res = await getRoutes(routeId); |
|
|
|
if (res.code !== 200 || !res.data) return; |
|
|
|
const waypoints = res.data.waypoints || []; |
|
|
|
const route = this.routes.find(r => r.id === routeId); |
|
|
|
let route = this.routes.find(r => r.id === routeId); |
|
|
|
if (!route) route = this.peerRoutes.find(r => r.id === routeId); |
|
|
|
if (route) { |
|
|
|
this.$set(route, 'waypoints', waypoints); |
|
|
|
} |
|
|
|
@ -2613,7 +2681,7 @@ export default { |
|
|
|
this.selectedRouteDetails = { ...this.selectedRouteDetails, waypoints }; |
|
|
|
} |
|
|
|
if (this.activeRouteIds.includes(routeId) && this.$refs.cesiumMap && waypoints.length > 0) { |
|
|
|
const r = this.routes.find(rr => rr.id === routeId); |
|
|
|
const r = this.routes.find(rr => rr.id === routeId) || this.peerRoutes.find(rr => rr.id === routeId); |
|
|
|
if (r) { |
|
|
|
if (waypoints.some(wp => this.isHoldWaypoint(wp))) { |
|
|
|
const { minMinutes, maxMinutes } = this.getDeductionTimeRange(); |
|
|
|
@ -2824,8 +2892,79 @@ export default { |
|
|
|
} |
|
|
|
return false; |
|
|
|
}, |
|
|
|
onParentSixStepsBroadcast(open) { |
|
|
|
if (!(this.roomDetail && this.roomDetail.parentId == null)) { |
|
|
|
return; |
|
|
|
} |
|
|
|
const want = !!open; |
|
|
|
const ws = this.wsConnection; |
|
|
|
if (ws && ws.connected && typeof ws.sendSyncSixSteps === 'function') { |
|
|
|
ws.sendSyncSixSteps(want); |
|
|
|
this._pendingParentSixStepsSend = null; |
|
|
|
} else { |
|
|
|
this._pendingParentSixStepsSend = want; |
|
|
|
} |
|
|
|
}, |
|
|
|
/** 子房间应用大房间下发的六步法开关;ref 未就绪时写入待处理队列 */ |
|
|
|
applyRemoteSixStepsFromParent(open) { |
|
|
|
if (!(this.roomDetail && this.roomDetail.parentId != null)) return; |
|
|
|
const panel = this.$refs.bottomLeftPanel; |
|
|
|
if (panel && typeof panel.applyRemoteSixSteps === 'function') { |
|
|
|
panel.applyRemoteSixSteps(open); |
|
|
|
this._pendingSyncSixSteps = null; |
|
|
|
} else { |
|
|
|
this._pendingSyncSixSteps = open; |
|
|
|
this.$nextTick(() => { |
|
|
|
const p = this.$refs.bottomLeftPanel; |
|
|
|
if (p && typeof p.applyRemoteSixSteps === 'function' && this._pendingSyncSixSteps != null) { |
|
|
|
const o = this._pendingSyncSixSteps; |
|
|
|
this._pendingSyncSixSteps = null; |
|
|
|
p.applyRemoteSixSteps(o); |
|
|
|
} |
|
|
|
}); |
|
|
|
} |
|
|
|
}, |
|
|
|
flushPendingSyncSixStepsIfAny() { |
|
|
|
if (this._pendingSyncSixSteps == null) return; |
|
|
|
if (this.roomDetail && this.roomDetail.parentId == null) { |
|
|
|
this._pendingSyncSixSteps = null; |
|
|
|
return; |
|
|
|
} |
|
|
|
if (this.roomDetail && this.roomDetail.parentId != null) { |
|
|
|
const o = this._pendingSyncSixSteps; |
|
|
|
this._pendingSyncSixSteps = null; |
|
|
|
this.$nextTick(() => this.applyRemoteSixStepsFromParent(o)); |
|
|
|
} |
|
|
|
}, |
|
|
|
resolveStyleRoomIdForRoute(route) { |
|
|
|
if (!route) return this.currentRoomId; |
|
|
|
const isParentRoom = this.roomDetail && this.roomDetail.parentId == null; |
|
|
|
const plan = this.plans.find(p => p.id === route.scenarioId); |
|
|
|
if (isParentRoom && plan && plan.roomId) return plan.roomId; |
|
|
|
if (route.sourceRoomId) return route.sourceRoomId; |
|
|
|
return this.currentRoomId; |
|
|
|
}, |
|
|
|
routeEditDeniedReason(route) { |
|
|
|
if (!route) return '无效航线'; |
|
|
|
if (route.peerViewOnly) return '兄弟房间航线仅供展示与同步查看'; |
|
|
|
const isParent = this.roomDetail && this.roomDetail.parentId == null; |
|
|
|
const ul = String(this.$store.getters.userLevel || ''); |
|
|
|
const adminOk = this.isAdmin || ul === '1'; |
|
|
|
if (adminOk) return ''; |
|
|
|
if (isParent) return '大房间内仅管理员可修改航线'; |
|
|
|
if (this.isCurrentRoomHost) return ''; |
|
|
|
const myId = this.currentUserId; |
|
|
|
const isCreator = route.creatorId != null && myId != null && String(route.creatorId) === String(myId); |
|
|
|
if (isCreator) return ''; |
|
|
|
return '暂无权限修改该航线(仅管理员、房间主持人或航线创建者可修改)'; |
|
|
|
}, |
|
|
|
// 航线编辑弹窗相关方法 |
|
|
|
openRouteDialog(route) { |
|
|
|
const deny = this.routeEditDeniedReason(route); |
|
|
|
if (deny) { |
|
|
|
this.$message.warning(deny); |
|
|
|
return; |
|
|
|
} |
|
|
|
this.selectedRoute = route; |
|
|
|
this.routeEditInitialTab = null; |
|
|
|
this.showRouteDialog = true; |
|
|
|
@ -2958,6 +3097,10 @@ export default { |
|
|
|
} |
|
|
|
}, |
|
|
|
async handleDeleteRoute(route) { |
|
|
|
if (route && route.peerViewOnly) { |
|
|
|
this.$message.warning('兄弟房间航线仅展示在本地,不能从当前房间删除'); |
|
|
|
return; |
|
|
|
} |
|
|
|
if (this.isRouteLockedByOther(route.id)) { |
|
|
|
this.$message.warning('该航线正被其他成员编辑,无法删除'); |
|
|
|
return; |
|
|
|
@ -3036,6 +3179,7 @@ export default { |
|
|
|
platformId: item.platformId, |
|
|
|
platform: item.platform, |
|
|
|
attributes: item.attributes, |
|
|
|
creatorId: item.creatorId || null, |
|
|
|
points: item.waypoints ? item.waypoints.length : 0, |
|
|
|
waypoints: (item.waypoints || []).slice().sort((a, b) => { |
|
|
|
const saRaw = a.seq != null ? a.seq : a.Seq; |
|
|
|
@ -3057,9 +3201,60 @@ export default { |
|
|
|
|
|
|
|
this.routes = allRoutes; |
|
|
|
|
|
|
|
let peerRoutesAcc = []; |
|
|
|
if (!isParentRoom && this.roomDetail && this.roomDetail.parentId) { |
|
|
|
try { |
|
|
|
const sibRes = await listRooms({ pageNum: 1, pageSize: 999, parentId: this.roomDetail.parentId }); |
|
|
|
const siblings = (sibRes.rows || []).filter(r => String(r.id) !== String(roomId)); |
|
|
|
for (const sib of siblings) { |
|
|
|
const sibScen = await listScenario({ roomId: sib.id, pageNum: 1, pageSize: 9999 }); |
|
|
|
const srows = (sibScen.code === 200 && sibScen.rows) ? sibScen.rows : []; |
|
|
|
const pids = srows.map(p => p.id); |
|
|
|
const scenarioNameMap = {}; |
|
|
|
srows.forEach(p => { scenarioNameMap[p.id] = p.name || `方案${p.id}`; }); |
|
|
|
if (pids.length === 0) continue; |
|
|
|
const sibRt = await listRoutes({ scenarioIdsStr: pids.join(','), pageNum: 1, pageSize: 9999 }); |
|
|
|
let sibRows = (sibRt.code === 200 && sibRt.rows) ? sibRt.rows : []; |
|
|
|
sibRows = sibRows.filter(r => pids.includes(r.scenarioId)); |
|
|
|
sibRows.forEach(item => { |
|
|
|
peerRoutesAcc.push({ |
|
|
|
id: item.id, |
|
|
|
name: item.callSign, |
|
|
|
platformId: item.platformId, |
|
|
|
platform: item.platform, |
|
|
|
attributes: item.attributes, |
|
|
|
points: item.waypoints ? item.waypoints.length : 0, |
|
|
|
waypoints: (item.waypoints || []).slice().sort((a, b) => { |
|
|
|
const saRaw = a.seq != null ? a.seq : a.Seq; |
|
|
|
const sbRaw = b.seq != null ? b.seq : b.Seq; |
|
|
|
const saNum = Number(saRaw); |
|
|
|
const sbNum = Number(sbRaw); |
|
|
|
const sa = Number.isFinite(saNum) ? saNum : Number.POSITIVE_INFINITY; |
|
|
|
const sb = Number.isFinite(sbNum) ? sbNum : Number.POSITIVE_INFINITY; |
|
|
|
if (sa !== sb) return sa - sb; |
|
|
|
return (Number(a.id) || 0) - (Number(b.id) || 0); |
|
|
|
}), |
|
|
|
conflict: false, |
|
|
|
scenarioId: item.scenarioId, |
|
|
|
peerViewOnly: true, |
|
|
|
sourceRoomId: sib.id, |
|
|
|
sourceRoomName: sib.name || `房间${sib.id}`, |
|
|
|
sourcePlanName: scenarioNameMap[item.scenarioId] || `方案${item.scenarioId}` |
|
|
|
}); |
|
|
|
}); |
|
|
|
} |
|
|
|
} catch (e) { |
|
|
|
console.warn('load peer routes', e); |
|
|
|
} |
|
|
|
} |
|
|
|
this.peerRoutes = peerRoutesAcc; |
|
|
|
|
|
|
|
// 回滚/删除后后端可能不存在某些旧 routeId:右侧列表会消失,但地图实体如果不清理就会残留。 |
|
|
|
// 这里先把 activeRouteIds 与后端现存 routes 做一致性校正,并同步移除地图上“已消失的航线实体”。 |
|
|
|
const existingIdSet = new Set(allRoutes.map(r => String(r.id))); |
|
|
|
const existingIdSet = new Set([ |
|
|
|
...allRoutes.map(r => String(r.id)), |
|
|
|
...peerRoutesAcc.map(r => String(r.id)) |
|
|
|
]); |
|
|
|
const missingRouteIds = (this.activeRouteIds || []).filter(id => !existingIdSet.has(String(id))); |
|
|
|
if (missingRouteIds.length > 0 && this.$refs.cesiumMap) { |
|
|
|
missingRouteIds.forEach((routeId) => { |
|
|
|
@ -3446,26 +3641,67 @@ export default { |
|
|
|
const min = parseInt(m[3], 10); |
|
|
|
return sign * (h * 60 + min); |
|
|
|
}, |
|
|
|
refreshRoomAccessNav() { |
|
|
|
if (!this.currentRoomId) { |
|
|
|
this.roomAccessNavVisible = false; |
|
|
|
this.isCurrentRoomHost = false; |
|
|
|
return; |
|
|
|
} |
|
|
|
const ul = String(this.$store.getters.userLevel || ''); |
|
|
|
const isAdminUser = this.isAdmin || ul === '1'; |
|
|
|
if (isAdminUser) { |
|
|
|
this.roomAccessNavVisible = true; |
|
|
|
this.isCurrentRoomHost = false; |
|
|
|
return; |
|
|
|
} |
|
|
|
checkIsHost(this.currentRoomId).then(res => { |
|
|
|
const d = res.data != null ? res.data : res; |
|
|
|
this.isCurrentRoomHost = !!(d && d.isHost); |
|
|
|
this.roomAccessNavVisible = this.isCurrentRoomHost; |
|
|
|
}).catch(() => { |
|
|
|
this.isCurrentRoomHost = false; |
|
|
|
this.roomAccessNavVisible = false; |
|
|
|
}); |
|
|
|
}, |
|
|
|
getRoomDetail(callback) { |
|
|
|
if (!this.currentRoomId) { callback && callback(); return; } |
|
|
|
getRooms(this.currentRoomId).then(res => { |
|
|
|
if (res.code === 200 && res.data) { |
|
|
|
this.roomDetail = res.data; |
|
|
|
this.$nextTick(() => this.loadRoomDrawings()); |
|
|
|
if (res.data.parentId == null) { |
|
|
|
listRooms({ pageNum: 1, pageSize: 999, parentId: this.currentRoomId }).then(roomsRes => { |
|
|
|
const rows = roomsRes.rows || roomsRes || []; |
|
|
|
this.childRoomKTimes = rows |
|
|
|
.filter(r => r.kAnchorTime) |
|
|
|
.map(r => ({ name: r.name || `房间${r.id}`, kAnchorTime: r.kAnchorTime })); |
|
|
|
this.selectedChildKTimeIndex = 0; |
|
|
|
}).catch(() => { this.childRoomKTimes = []; }); |
|
|
|
} else { |
|
|
|
this.childRoomKTimes = []; |
|
|
|
if (!this.currentRoomId) { this.roomAccessNavVisible = false; callback && callback(); return; } |
|
|
|
const loadDetail = () => { |
|
|
|
getRooms(this.currentRoomId).then(res => { |
|
|
|
if (res.code === 200 && res.data) { |
|
|
|
this.roomDetail = res.data; |
|
|
|
this.refreshRoomAccessNav(); |
|
|
|
this.$nextTick(() => { |
|
|
|
this.loadRoomDrawings(); |
|
|
|
this.flushPendingSyncSixStepsIfAny(); |
|
|
|
if (res.data.parentId == null && this._pendingParentSixStepsSend != null && this.wsConnection && this.wsConnection.connected && this.wsConnection.sendSyncSixSteps) { |
|
|
|
const v = this._pendingParentSixStepsSend; |
|
|
|
this._pendingParentSixStepsSend = null; |
|
|
|
this.wsConnection.sendSyncSixSteps(v); |
|
|
|
} |
|
|
|
}); |
|
|
|
if (res.data.parentId == null) { |
|
|
|
listRooms({ pageNum: 1, pageSize: 999, parentId: this.currentRoomId }).then(roomsRes => { |
|
|
|
const rows = roomsRes.rows || roomsRes || []; |
|
|
|
this.childRoomKTimes = rows |
|
|
|
.filter(r => r.kAnchorTime) |
|
|
|
.map(r => ({ name: r.name || `房间${r.id}`, kAnchorTime: r.kAnchorTime })); |
|
|
|
this.selectedChildKTimeIndex = 0; |
|
|
|
}).catch(() => { this.childRoomKTimes = []; }); |
|
|
|
} else { |
|
|
|
this.childRoomKTimes = []; |
|
|
|
} |
|
|
|
} |
|
|
|
callback && callback(); |
|
|
|
}).catch(() => { callback && callback(); }); |
|
|
|
}; |
|
|
|
checkRoomAccess(this.currentRoomId).then(chk => { |
|
|
|
if (chk.code === 200 && chk.data && chk.data.allowed === false) { |
|
|
|
this.$message.warning('您暂无进入该房间的权限,请联系管理员或主持人授权'); |
|
|
|
this.$router.replace({ path: '/selectRoom' }).catch(() => {}); |
|
|
|
return; |
|
|
|
} |
|
|
|
callback && callback(); |
|
|
|
}).catch(() => { callback && callback(); }); |
|
|
|
loadDetail(); |
|
|
|
}).catch(() => loadDetail()); |
|
|
|
}, |
|
|
|
/** 加载当前房间的空域/威力区图形(与房间 ID 绑定,进入该房间即显示);大房间时合并所有子房间的 frontend_drawings */ |
|
|
|
async loadRoomDrawings() { |
|
|
|
@ -7210,7 +7446,7 @@ export default { |
|
|
|
attributes: route.attributes |
|
|
|
}; |
|
|
|
|
|
|
|
// 更新 routes 数组中对应航线的 waypoints 字段 |
|
|
|
// 更新 routes / peerRoutes 中对应航线的 waypoints 字段 |
|
|
|
const routeIndex = this.routes.findIndex(r => r.id === route.id); |
|
|
|
if (routeIndex > -1) { |
|
|
|
this.$set(this.routes, routeIndex, { |
|
|
|
@ -7218,13 +7454,20 @@ export default { |
|
|
|
waypoints: waypoints |
|
|
|
}); |
|
|
|
} |
|
|
|
const peerIx = this.peerRoutes.findIndex(r => r.id === route.id); |
|
|
|
if (peerIx > -1) { |
|
|
|
this.$set(this.peerRoutes, peerIx, { |
|
|
|
...this.peerRoutes[peerIx], |
|
|
|
waypoints: waypoints |
|
|
|
}); |
|
|
|
} |
|
|
|
|
|
|
|
if (waypoints.length > 0) { |
|
|
|
if (this.$refs.cesiumMap) { |
|
|
|
const roomId = this.currentRoomId; |
|
|
|
if (roomId && route.platformId) { |
|
|
|
const styleRoomId = this.resolveStyleRoomIdForRoute(route); |
|
|
|
if (styleRoomId && route.platformId) { |
|
|
|
try { |
|
|
|
const styleRes = await getPlatformStyle({ roomId, routeId: route.id, platformId: route.platformId }); |
|
|
|
const styleRes = await getPlatformStyle({ roomId: styleRoomId, routeId: route.id, platformId: route.platformId }); |
|
|
|
if (styleRes.data) this.$refs.cesiumMap.setPlatformStyle(route.id, styleRes.data); |
|
|
|
} catch (_) {} |
|
|
|
} |
|
|
|
@ -7363,13 +7606,12 @@ export default { |
|
|
|
if (this.$refs.cesiumMap) { |
|
|
|
this.$refs.cesiumMap.removeRouteById(route.id); |
|
|
|
} |
|
|
|
// 航线显隐仅本地生效,不同步给他人 |
|
|
|
if (this.selectedRouteDetails && this.selectedRouteDetails.id === route.id) { |
|
|
|
if (this.activeRouteIds.length > 0) { |
|
|
|
const lastId = this.activeRouteIds[this.activeRouteIds.length - 1]; |
|
|
|
getRoutes(lastId).then(res => { |
|
|
|
if (res.code === 200 && res.data) { |
|
|
|
const fromList = this.routes.find(r => r.id === lastId); |
|
|
|
const fromList = this.routes.find(r => r.id === lastId) || this.peerRoutes.find(r => r.id === lastId); |
|
|
|
this.selectedRouteId = res.data.id; |
|
|
|
this.selectedRouteDetails = { |
|
|
|
id: res.data.id, |
|
|
|
@ -7388,10 +7630,14 @@ export default { |
|
|
|
this.selectedRouteDetails = null; |
|
|
|
} |
|
|
|
} |
|
|
|
if (!fromPlanSwitch && this.wsConnection && this.wsConnection.sendSyncRouteVisibility) { |
|
|
|
this.wsConnection.sendSyncRouteVisibility(route.id, false, this.resolveStyleRoomIdForRoute(route)); |
|
|
|
} |
|
|
|
} else { |
|
|
|
// 航线已隐藏,显示它 |
|
|
|
await this.selectRoute(route); |
|
|
|
// 航线显隐仅本地生效,不同步给他人 |
|
|
|
if (!fromPlanSwitch && this.wsConnection && this.wsConnection.sendSyncRouteVisibility) { |
|
|
|
this.wsConnection.sendSyncRouteVisibility(route.id, true, this.resolveStyleRoomIdForRoute(route)); |
|
|
|
} |
|
|
|
} |
|
|
|
}, |
|
|
|
|
|
|
|
|