| | |
| | | <template> |
| | | <div :id="mapId" class="common-cesium-map" @contextmenu.prevent></div> |
| | | <div :id="mapId" class="common-cesium-map" @contextmenu.prevent> |
| | | <div class="card"> |
| | | <div class="card-content"> |
| | | <div class="card-item" v-for="(item, index) in props.list" @click="clickWeekTime(item, index)"> |
| | | <!-- <div class="time-top" :class="index === selectedImgIndex ? 'active' : ''"> |
| | | {{ item.createTime }} |
| | | </div> --> |
| | | <div class="time-point"> |
| | | <img |
| | | class="active" |
| | | v-if="index === selectedImgIndex" |
| | | src="@/assets/images/common/point-active.png" |
| | | alt="" |
| | | /> |
| | | <img v-else src="@/assets/images/common/point.png" alt="" /> |
| | | </div> |
| | | <div class="time-bottom">{{ item.createTime.slice(11) }}</div> |
| | | </div> |
| | | |
| | | </div> |
| | | </div> |
| | | <div class="time-line"></div> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | |
| | | import { onBeforeUnmount, onMounted, watch } from 'vue' |
| | | import { PublicCesium } from '@/utils/cesium/publicCesium' |
| | | import { loadJaAdminBoundary, removeJaAdminBoundary } from '@/utils/cesium/adminBoundary' |
| | | import positionIcon from '@/assets/images/dataCenter/positionicon.png' |
| | | |
| | | const props = defineProps({ |
| | | active: { |
| | |
| | | type: Number, |
| | | default: 10000, |
| | | }, |
| | | list: { |
| | | type: Array, |
| | | default: () => [], |
| | | }, |
| | | }) |
| | | |
| | | const emit = defineEmits(['ready', 'height-change', 'stage-change']) |
| | | const emit = defineEmits(['ready', 'height-change', 'stage-change', 'point-change']) |
| | | const mapId = props.domId || `common-cesium-map-${Math.random().toString(36).slice(2, 10)}` |
| | | let viewInstance = null |
| | | let viewer = null |
| | |
| | | removeCameraListener = () => viewer?.camera?.changed?.removeEventListener(onCameraChanged) |
| | | } |
| | | emit('ready', { viewer, publicCesium: viewInstance }) |
| | | // 地图就绪时,若列表已加载则默认标记选中项(首个) |
| | | showSelectedMarker() |
| | | } |
| | | |
| | | const selectedImgIndex = ref(0) |
| | | // 航线高度(与 TrajectoryDiaLog 里 MOCK_FLIGHT_HEIGHT 保持一致),让标记悬浮在轨迹线上 |
| | | const FLIGHT_HEIGHT = 120 |
| | | // 当前经纬度位置标记 |
| | | let positionEntity = null |
| | | // 点击周期 |
| | | const clickWeekTime = (item, index) => { |
| | | // clickImgSrc.value = item.url |
| | | selectedImgIndex.value = index |
| | | showPositionMarker(item) |
| | | // 通知父组件当前选中的轨迹点,用于右侧表格同步高亮 |
| | | emit('point-change', { item, index }) |
| | | } |
| | | |
| | | // 在地图上通过经纬度显示位置图标 |
| | | const showPositionMarker = item => { |
| | | if (!viewer) return |
| | | const longitude = Number(item?.longitude) |
| | | const latitude = Number(item?.latitude) |
| | | if (Number.isNaN(longitude) || Number.isNaN(latitude)) return |
| | | |
| | | // 与航线相同的高度,让标记悬浮在轨迹线上,而不是贴地 |
| | | const position = Cesium.Cartesian3.fromDegrees(longitude, latitude, FLIGHT_HEIGHT) |
| | | if (positionEntity) { |
| | | // 已存在则移动到新的位置 |
| | | positionEntity.position = position |
| | | } else { |
| | | positionEntity = viewer.entities.add({ |
| | | position, |
| | | billboard: { |
| | | image: positionIcon, |
| | | width: 32, |
| | | height: 40, |
| | | verticalOrigin: Cesium.VerticalOrigin.BOTTOM, |
| | | disableDepthTestDistance: Number.POSITIVE_INFINITY, |
| | | }, |
| | | }) |
| | | } |
| | | } |
| | | |
| | | // 显示当前选中项的标记(默认首个),地图就绪和列表加载后都会调用 |
| | | const showSelectedMarker = () => { |
| | | const item = props.list?.[selectedImgIndex.value] |
| | | if (item) showPositionMarker(item) |
| | | } |
| | | |
| | | // 列表数据到位后,默认选中并标记首个点 |
| | | watch( |
| | | () => props.list, |
| | | list => { |
| | | if (list && list.length) { |
| | | selectedImgIndex.value = 0 |
| | | showSelectedMarker() |
| | | // 默认选中首个,同步通知父组件高亮表格首行 |
| | | emit('point-change', { item: list[0], index: 0 }) |
| | | } |
| | | } |
| | | ) |
| | | |
| | | watch( |
| | | () => props.active, |
| | |
| | | if (removeCameraListener) removeCameraListener() |
| | | removeJaAdminBoundary(viewer, adminBoundarySources) |
| | | adminBoundarySources = null |
| | | positionEntity = null |
| | | viewInstance?.viewerDestroy?.() |
| | | }) |
| | | |
| | |
| | | .common-cesium-map { |
| | | width: 100%; |
| | | height: 100%; |
| | | position: relative; |
| | | .card { |
| | | position: absolute; |
| | | bottom: 0px; |
| | | width: 100%; |
| | | height: 67px; |
| | | background: rgba(0, 0, 0, 0.42); |
| | | border-radius: 0px 0px 20px 20px; |
| | | padding-left: 6px; |
| | | // padding-right: 6px; |
| | | // border: 1px solid red; |
| | | |
| | | color: #d5d5d5; |
| | | font-size: 14px; |
| | | |
| | | .card-content { |
| | | display: flex; |
| | | justify-content: center; |
| | | position: absolute; |
| | | |
| | | width: calc(100% - 6px - 6px); |
| | | height: 100%; |
| | | overflow-x: scroll; |
| | | overflow-y: hidden; |
| | | white-space: nowrap; |
| | | .card-item { |
| | | cursor: pointer; |
| | | } |
| | | |
| | | & > div { |
| | | position: relative; |
| | | } |
| | | |
| | | .time-top { |
| | | margin-top: 10px; |
| | | line-height: 1; |
| | | text-align: center; |
| | | cursor: pointer; |
| | | } |
| | | |
| | | .active { |
| | | font-weight: 400; |
| | | color: #ffffff; |
| | | } |
| | | |
| | | .time-point { |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | position: absolute; |
| | | top: 22px; |
| | | left: 50%; |
| | | width: 18px; |
| | | height: 18px; |
| | | z-index: 1; |
| | | transform: translate(-50%, 0); |
| | | |
| | | img { |
| | | width: 11.2px; |
| | | height: 11.2px; |
| | | } |
| | | |
| | | img.active { |
| | | width: 16.89px; |
| | | height: 16.89px; |
| | | } |
| | | } |
| | | |
| | | .time-bottom { |
| | | // border: 1px solid red; |
| | | margin-top: 44px; |
| | | margin-left: 8px; |
| | | &:first-child { |
| | | margin-left: 0; |
| | | } |
| | | } |
| | | |
| | | // .time-line { |
| | | // // position: absolute; |
| | | // // top: 35px; |
| | | // // width: 100%; |
| | | // position: fixed; |
| | | // bottom: 96px; |
| | | // width: 94%; |
| | | // border: 1px solid rgba(237, 237, 237, 0.6); |
| | | // z-index: 0; |
| | | // } |
| | | } |
| | | |
| | | } |
| | | .time-line { |
| | | position: absolute; |
| | | // top: 35px; |
| | | width: 100%; |
| | | // position: fixed; |
| | | bottom: 36px; |
| | | width: 100%; |
| | | border: 1px solid rgba(237, 237, 237, 0.6); |
| | | z-index: 0; |
| | | } |
| | | } |
| | | </style> |