轨迹图逻辑修改

This commit is contained in:
wangxk 2025-07-31 14:26:05 +08:00
parent 694e16240c
commit d76271da24
2 changed files with 251 additions and 92 deletions

View File

@ -1,12 +1,16 @@
<template>
<div class="map-container">
<svg ref="svgRef" class="trajectory-svg" width="800" height="600">
<!-- <image> 放入 zoom-container 使其与轨迹同步缩放 -->
<div class="map-container" :class="{ fullscreen: isFullscreen }">
<!-- 全屏按钮 -->
<button class="fullscreen-btn" @click="toggleFullscreen">
{{ isFullscreen ? '退出全屏' : '全屏' }}
</button>
<svg ref="svgRef" class="trajectory-svg" :width="svgWidth" :height="svgHeight">
<g class="zoom-container">
<image
:xlink:href="imageUrl"
width="800"
height="600"
:width="svgWidth"
:height="svgHeight"
/>
</g>
</svg>
@ -39,6 +43,13 @@ const props = defineProps({
// === Emits ===
const emit = defineEmits(['trajectoryComplete'])
// === ===
const isFullscreen = ref(false)
const svgWidth = ref(800)
const svgHeight = ref(600)
let resizeTimeout = null
const prevFullscreenState = ref(isFullscreen.value)
// === DOM ===
const svgRef = ref(null)
const xScale = ref(null)
@ -47,15 +58,22 @@ const zoomTransform = ref(d3.zoomIdentity)
// === ===
const initMap = () => {
//
const container = d3.select(svgRef.value).node()
if (container) {
svgWidth.value = container.clientWidth
svgHeight.value = container.clientHeight
}
const [minLng, minLat, maxLng, maxLat] = props.bounds
xScale.value = d3.scaleLinear()
.domain([minLng, maxLng])
.range([0, 800])
.range([0, svgWidth.value])
yScale.value = d3.scaleLinear()
.domain([minLat, maxLat])
.range([600, 0])
.range([svgHeight.value, 0])
initZoom()
updateTrajectory()
@ -65,7 +83,7 @@ const initMap = () => {
const initZoom = () => {
const mapZoom = d3.zoom()
.scaleExtent([0.5, 3]) //
.translateExtent([[-800, -600], [1600, 1200]]) //
.translateExtent([[-svgWidth.value, -svgHeight.value], [2*svgWidth.value, 2*svgHeight.value]])
.on("zoom", (event) => {
d3.select(svgRef.value).select('.zoom-container')
.attr("transform", event.transform)
@ -79,9 +97,8 @@ const initZoom = () => {
const updateTrajectory = () => {
if (!props.trajectory?.length || !xScale.value || !yScale.value) return
if (props.qvehuan) {
clearOldElements()
}
//
// clearOldElements()
if (shouldRedraw()) {
renderTrajectoryPath()
@ -160,12 +177,65 @@ const renderTrajectoryMarkers = () => {
//
const shouldRedraw = () => {
//
if (isFullscreen.value !== prevFullscreenState.value) {
prevFullscreenState.value = isFullscreen.value
return true
}
if (props.qvehuan && props.trajectory.length > 0) {
return true
}
return props.trajectory.length > 0
}
// === ===
const toggleFullscreen = () => {
if (!isFullscreen.value) {
//
const elem = document.documentElement
if (elem.requestFullscreen) {
elem.requestFullscreen()
} else if (elem.webkitRequestFullscreen) {
elem.webkitRequestFullscreen()
} else if (elem.mozRequestFullScreen) {
elem.mozRequestFullScreen()
}
} else {
// 退
if (document.exitFullscreen) {
document.exitFullscreen()
} else if (document.webkitExitFullscreen) {
document.webkitExitFullscreen()
} else if (document.mozCancelFullScreen) {
document.mozCancelFullScreen()
}
}
}
// === ===
const handleFullscreenChange = () => {
isFullscreen.value = !!document.fullscreenElement
//
clearOldElements()
// DOM
setTimeout(() => {
initMap()
updateTrajectory()
}, 500)
}
// === ===
const handleResize = () => {
if (resizeTimeout) clearTimeout(resizeTimeout)
resizeTimeout = setTimeout(() => {
initMap()
updateTrajectory()
}, 300)
}
// === ===
watch(() => props.trajectory, (newVal) => {
if (newVal.length) {
@ -180,6 +250,18 @@ watch(() => props.qvehuan, (newVal) => {
})
onMounted(() => {
//
document.addEventListener('fullscreenchange', handleFullscreenChange)
window.addEventListener('resize', handleResize)
// SVG
const container = d3.select(svgRef.value).node()
if (container) {
svgWidth.value = container.clientWidth
svgHeight.value = container.clientHeight
}
//
const imageElement = d3.select(svgRef.value).select('image')
if (imageElement) {
imageElement.on('load', initMap)
@ -187,6 +269,8 @@ onMounted(() => {
})
onBeforeUnmount(() => {
document.removeEventListener('fullscreenchange', handleFullscreenChange)
window.removeEventListener('resize', handleResize)
d3.select(svgRef.value).selectAll('*').remove()
})
</script>
@ -198,6 +282,36 @@ onBeforeUnmount(() => {
height: 600px;
overflow: hidden;
margin-top: 10px;
transition: all 0.3s ease;
}
.map-container.fullscreen {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
z-index: 9999;
margin: 0;
}
.fullscreen-btn {
position: absolute;
top: 10px;
right: 10px;
z-index: 10;
padding: 6px 12px;
background: rgba(255, 255, 255, 0.8);
border: none;
border-radius: 4px;
cursor: pointer;
transition: all 0.2s;
font-size: 14px;
}
.fullscreen-btn:hover {
background: #1890FF;
color: white;
}
.trajectory-svg {
@ -206,6 +320,9 @@ onBeforeUnmount(() => {
left: 0;
z-index: 1;
cursor: grab;
width: 100%;
height: 100%;
transition: all 0.3s ease;
}
.trajectory-svg:active {

View File

@ -1594,18 +1594,20 @@ const imgor = ref(true)
function openMap(row: any) {
pngobj.vlaue = {
pngurl: '',
conid: '',
textcontent: []
}
pngform.value = JSON.parse(JSON.stringify(row))
fredid.value = row.id
getSSELink()
getpngdata()
tabbas.value = '1'
mapTrajectory.value = true
maptime.value = 300
if (row.custom1) {
maptime.value = 60
console.log(JSON.parse(row.custom1))
if (JSON.parse(row.custom1).pngurl && JSON.parse(row.custom1).textcontent && JSON.parse(row.custom1).conid) {
ElMessageBox.confirm(
'当前轨迹文件已有关联图片,是否直接加载轨迹?',
'当前轨迹文件已有关联轨迹配置文件和图片,是否直接加载轨迹?',
'提示',
{
confirmButtonText: '确定',
@ -1615,13 +1617,18 @@ function openMap(row: any) {
)
.then(() => {
pngobj.value = JSON.parse(row.custom1)
configradio.value = pngobj.value.conid
imgor.value = false
getSSELink()
})
.catch(() => {
})
}
}
const pngArr = ref([])
const txtArr = ref([])
const conarr = ref([])
const pngloading = ref(false)
function getpngdata() {
pngloading.value = true
@ -1629,14 +1636,16 @@ function getpngdata() {
pngloading.value = false
pngArr.value = res.data.tsFilesListPng
txtArr.value = res.data.tsFilesListTxt
conarr.value = res.data.tsFilesListConfig
})
}
const pngobj: any = ref({
pngurl: '',
conid: '',
textcontent: []
})
function pngsure() {
if (!(pngradio.value && txtradio.value)) {
if (!(pngradio.value && txtradio.value && configradio.value)) {
ElMessage.warning('请选择底图或地理信息文件')
return
}
@ -1644,7 +1653,8 @@ function pngsure() {
//
const newPngobj = {
pngurl: '',
textcontent: []
textcontent: [],
conid: '',
}
// URL
@ -1660,6 +1670,11 @@ function pngsure() {
newPngobj.textcontent = JSON.parse(JSON.stringify(item.fileContent)).split(",")
}
})
conarr.value.forEach((item: any) => {
if (configradio.value == item.id) {
newPngobj.conid = item.id
}
})
// 使
pngobj.value = {
@ -1675,6 +1690,7 @@ function pngsure() {
imgor.value = false
}
function mapClose() {
configradio.value = null
txtradio.value = null
pngradio.value = null
imgor.value = true
@ -1687,6 +1703,18 @@ function mapClose() {
}
// 1s/10s/30s/1m/2m/5m
const options = ref([
{
name: '10秒钟'
, id: 10
},
{
name: '1分钟'
, id: 60
},
{
name: '2分钟'
, id: 120
},
{
name: '5分钟'
, id: 300
@ -1694,15 +1722,15 @@ const options = ref([
{
name: '10分钟'
, id: 600
},
}
])
const maptime: any = ref(300)
const maptime: any = ref(60)
//
const qvehuan: any = ref(false)
const qvehuan1: any = ref(false)
function frequency(row: any) {
startSimpleNavi({
samTimes: maptime.value, id: fredid.value, token: userStore.userId, taskId: projectId.value
samTimes: maptime.value, id: fredid.value, token: userStore.userId, taskId: projectId.value, configId: configradio.value
}).then((res: any) => {
if (res.code == '0' && row) {
ElMessage.success("切换成功")
@ -1813,6 +1841,7 @@ const formatFileSize = (size: number): string => {
const txtradio: any = ref(null)
const pngradio: any = ref(null)
const configradio: any = ref(null)
</script>
<template>
@ -2202,6 +2231,13 @@ const pngradio: any = ref(null)
<!-- 轨迹地图 -->
<el-dialog title="轨迹地图/图表" v-model="mapTrajectory" :close-on-click-modal="false" :before-close="mapClose"
top="30px" draggable destroy-on-close>
<div v-loading="pngloading">
<div class="map_select" style="margin-top: 0px;">
<div style="width: 220px;margin-top: 5px;">请选择配置文件(trj_config*.txt)</div>
<el-radio-group v-model="configradio">
<el-radio v-for="(item, index) in conarr" :value="item.id">{{ item.fileName }}</el-radio>
</el-radio-group>
</div>
<div style="margin:0px 0px 15px 0px ;">
<span>设定采样频率</span>
<span>
@ -2209,8 +2245,10 @@ const pngradio: any = ref(null)
style="width: 240px">
<el-option v-for="item in options" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
<el-button style="margin-left: 15px;" type="primary" @click="getSSELink()">确定</el-button>
</span>
</div>
<div class="mapbox">
<div @click="tabbas = '1'" :class="tabbas == '1' ? 'mapbox_border' : 'mapbox_border1'">载体运动轨迹</div>
<div @click="tabbas = '2'" :class="tabbas == '2' ? 'mapbox_border' : 'mapbox_border1'">高程变化动态图</div>
@ -2222,18 +2260,20 @@ const pngradio: any = ref(null)
<div v-show="tabbas == '2'" style="width: 800px;height:600px;overflow: hidden;">
<Echart :chart-data="lineData" />
</div>
<div v-show="tabbas == '3'" v-loading="pngloading">
<div v-if="imgor">
<div v-show="tabbas == '3'">
<div v-if="imgor" style="width: 800px;height:600px;overflow: hidden;margin: auto;">
<div class="map_select">
<div style="width: 210px;margin-top: 5px;">请选择底图(支持png/jpg格式)</div>
<el-radio-group v-model="pngradio">
<el-radio v-for="(item, index) in pngArr" :value="item.id">{{ item.fileName }}</el-radio>
<el-radio v-for="(item, index) in pngArr" :value="item.id">{{ item.fileName
}}</el-radio>
</el-radio-group>
</div>
<div class="map_select">
<div style="width: 220px;margin-top: 5px;">请选择地理信息文件(maps*.txt)</div>
<el-radio-group v-model="txtradio">
<el-radio v-for="(item, index) in txtArr" :value="item.id">{{ item.fileName }}</el-radio>
<el-radio v-for="(item, index) in txtArr" :value="item.id">{{ item.fileName
}}</el-radio>
</el-radio-group>
</div>
<div style="width: 100%;display: flex;">
@ -2246,6 +2286,8 @@ const pngradio: any = ref(null)
:qvehuan="qvehuan" @qvehuan1="handleCustomEvent" />
</div>
</div>
</div>
</el-dialog>
<!-- 组件预览 -->