Files
fengjie-datascreen/src/views/scenic/components/big-map.vue
duanliang cc43098ca7 422
2025-04-23 02:00:16 +08:00

644 lines
16 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<div class="dialog">
<el-dialog v-model="modelValue" align-center :modal="false" :show-close="false">
<img class="close" src="@/assets/images/close.png" @click="handleClose" />
<div class="flex">
<div class="list-box">
<title2 title="车船" />
<el-input
v-model="keyword"
clearable
placeholder="车牌号"
@input="onInput"
>
</el-input>
<ul class="tabs">
<li
class="tab-item"
:class="{ 'tab-item__active': tabsIndex == index }"
v-for="(item, index) in tabs"
:key="index"
@click="handleTab(index)"
>
{{ item.label }}({{ item.leng }})
</li>
</ul>
<ul class="list">
<li
class="item"
:class="{ item__active: current === index }"
v-for="(item, index) in listTabs"
:key="index"
@click="handleItem(item, index)"
>
<div class="child-box">
<p class="item__label">{{ item.licenseNumber }}</p>
<span v-if="item.type == 0" class="label_img">
</span>
<span v-if="item.type == 2" class="label_img">
直通车
</span>
<span class="label_img label_img_2" v-if="item.type == 1" >
</span>
</div>
<div class="item__value"
>{{ item.speed }}km/h
<img v-if="item.accStatus == 1" src="@/assets/images/engine.png" alt="" />
<img v-else src="@/assets/images/unengine.png" alt="" />
<div
:class="{
'item__hot--primary': item.status == '行驶',
'item__hot--info': item.status != '行驶',
'item__hot--active': current === index
}"
>
</div>
</div>
</li>
</ul>
</div>
<div id="big-car-ship" class="big-car-ship" />
</div>
</el-dialog>
</div>
</template>
<script setup>
import { getGpsListApi, getGpsStatusListApi } from '@/api/scenic'
import closeIcon from '@/assets/images/close.png'
import carIcon from '@/assets/images/car.png'
import carStopIcon from '@/assets/images/car-stop.png'
import carOfflineIcon from '@/assets/images/car-offline.png'
import shipIcon from '@/assets/images/ship.png'
import shipStopIcon from '@/assets/images/ship-stop.png'
import shipOfflineIcon from '@/assets/images/ship-offline.png'
import icon13 from '@/assets/images/icon-13.png'
import icon14 from '@/assets/images/icon-14.png'
import icon15 from '@/assets/images/icon-15.png'
import icon16 from '@/assets/images/icon-16.png'
import icon17 from '@/assets/images/icon-17.png'
import icon18 from '@/assets/images/icon-18.png'
import icon19 from '@/assets/images/icon-19.png'
import icon20 from '@/assets/images/icon-20.png'
import { useMap } from '@/hooks/map'
import { debounce } from 'lodash'
import { useScenicStore } from '@/stores/scenic'
const scenicStore = useScenicStore()
const { map, marker, initMap, addMarker } = useMap()
let props = defineProps({
scenicSpotId: {
type: String,
default: ''
},
carList: {
type: Array,
default: () => []
},
shipList: {
type: Array,
default: () => []
}
})
let modelValue = defineModel()
let lat = ref('')
let lng = ref('')
let scenicSpotId = ref('')
let keyword = ref('')
let active = ref(0)
let current = ref('')
let scenicChange = null
let list = ref([])
let carOverlays = ref([])
let shipOverlays = ref([])
let lastInfoBox = ref(null)
let infoBox = ref(null)
let currentMarker = ref()
let tabs = ref([
{
label: '所有',
value: '',
number: 0
},
{
label: '运行',
value: '运行',
number: 0
},
{
label: '静止',
value: '静止',
number: 0
},
// {
// label: '离线',
// value: '离线',
// number: 0
// }
])
let listTabs = ref([])
let tabsIndex = ref(0)
watch(
() => modelValue.value,
(val) => {
if (val) {
setTimeout(() => {
initMap('big-car-ship', lng.value, lat.value, 15)
}, 1000)
}
},
{ immediate: true }
)
watch(
() => [scenicStore.vehicleData,tabsIndex.value,map.value],
(val) => {
// console.log(scenicStore.vehicleData,'scenicStore.vehicleDatascenicStore.vehicleDatascenicStore.vehicleData')
list.value = val[0].map
if(val[0].map){
tabs.value[0].leng = val[0].all.length
tabs.value[1].leng = val[0].moving.length
tabs.value[2].leng = val[0].still.length
// tabs.value[3].leng = val[0].offline.length
}
if(val[1]==0){
listTabs.value = val[0].all
}
if(val[1]==1){
listTabs.value = val[0].moving
}
if(val[1]==2){
listTabs.value = val[0].still
}
if(val[1]==3){
listTabs.value = val[0].offline
}
return false
},
{ immediate: true }
)
watch(
() => [list.value,map.value],
(val) => {
if (val[0]&&val[1]) {
if(carOverlays.value.length){
for(let i=0;i<carOverlays.value.length;i++){
for(let j=0;j<list.value.length;j++){
let carOverlay = carOverlays.value[i]
let car = list.value[j]
if (carOverlay && car && carOverlays.value[i].sim == car.sim) {
carOverlays.value[i].setPosition({
lng: car.lng,
lat: car.lat
})
continue
}
}
}
// 更新车辆infobox位置和内容
for (let i = 0; i < list.value.length; i++) {
if (
currentMarker.value &&
infoBox.value &&
list.value[i].sim == currentMarker.value.sim
) {
console.log('变化了')
let { lng, lat } = list.value[i]
infoBox.value.setContent(setHtml(list.value[i]))
infoBox.value.setPosition(new BMapGL.Point(lng, lat))
currentMarker.value.setPosition(new BMapGL.Point(lng, lat))
// map.value.centerAndZoom(new BMapGL.Point(lng, lat), 15)
break
}
}
}else{
list.value.map((item, i) => {
console.log(item,'item')
if (item.lng && item.lat) {
if (item.status == '行驶') {
if(item.type==1){
addMarker(shipIcon, [item.lng, item.lat], [36, 50])
}else{
addMarker(carIcon, [item.lng, item.lat], [36, 50])
}
}
if (item.status == '离线') {
if(item.type==1){
addMarker(shipOfflineIcon, [item.lng, item.lat], [36, 50])
}else{
addMarker(carOfflineIcon, [item.lng, item.lat], [36, 50])
}
}
if (item.status == '静止') {
if(item.type==1){
addMarker(shipStopIcon, [item.lng, item.lat], [36, 50])
}else{
addMarker(carStopIcon, [item.lng, item.lat], [36, 50])
}
}
marker.value.addEventListener('click', (e) => {
e.sim = e.target.sim
currentMarker.value = carOverlays.value.find(
(item) => item && item.sim == e.target.sim
)
let obj = scenicStore.vehicleData.all.find((item) => item.sim == e.target.sim)
setInfoBox(e.latLng, obj)
})
marker.value.sim = item.sim
carOverlays.value[i] = marker.value
}
})
}
}
},
{ immediate: true }
)
const timestampToYMD = (timestamp) => {
const date = new Date(timestamp * 1000)
const year = date.getFullYear()
const month = date.getMonth() + 1
const day = date.getDate()
return `${year}-${month}-${day}`
}
const setHtml = (data) => {
return `<div class='marker-box'>
<p class='marker-title'> ${data?.licenseNumber} </p>
<div class='marker-header'>
<span class='marker-sim'> ${data?.imei} </span>
<div class='marker-tag' style="color:#fff;">
<div></div>
${data?.status} ${data?.statusTimeDesc}
</div>
</div>
<div class='marker-line'> </div>
<div class='marker-row'>
<div class='marker-col'>
<img src='${icon13}' />
<p> ${data?.contactUser} </p> </div>
<div class='marker-col'>
<img src='${icon14}' /> <p> ${data?.contactTel} </p>
</div>
</div>
<div class='marker-row'>
<div class='marker-col'>
<img src='${icon15}' />
<p> ${timestampToYMD(data?.gpsTime)} </p> </div>
</div>
<div class='marker-row'>
<div class='marker-col'> <img src='${icon17}' /> <p> ${data?.speed}KM/H </p> </div>
</div>
<div class='marker-row'>
<div class='marker-col'>
<img src='${icon19}' /> <p> ${data?.lng},${data?.lat} </p> </div>
</div>
<div class='marker-row'>
<div class='marker-col' > <img src='${icon20}' /> <p style="line-height:1.4;"> ${data?.address} </p> </div>
</div>
</div>`
}
const setInfoBox = (e, data) => {
if (!infoBox.value) {
infoBox.value = new BMapGLLib.InfoBox(map.value, setHtml(data), {
boxStyle: {},
closeIconMargin: '',
closeIconUrl: closeIcon,
enableAutoPan: true,
align: INFOBOX_AT_TOP,
offset: new BMapGL.Size(0, 20)
})
infoBox.value.addEventListener('close', () => {
currentMarker.value = null
})
} else {
if (lastInfoBox.value) lastInfoBox.value?.close()
infoBox.value.setContent(setHtml(data))
lastInfoBox.value = infoBox.value
}
infoBox.value.open(e)
map.value.centerAndZoom(e, 15)
}
const handleItem = (e, index) => {
current.value = index
currentMarker.value = carOverlays.value.find((item) => item && item.sim == e.sim)
setInfoBox(new BMapGL.Point(e.lng, e.lat), e)
}
const handleTab = (index) => {
if (tabsIndex.value == index) return
tabsIndex.value = index
}
const handleClose = () => {
carOverlays.value = []
shipOverlays.value = []
map.value = null
current.value = ''
infoBox.value = null
lastInfoBox.value = null
modelValue.value = false
}
const getGpsList = async () => {
let res = await getGpsListApi({
keyword: keyword.value,
scenicSpotId: scenicSpotId.value,
status: tabs.value[active.value].value
})
list.value = res.data.list
let statusRes = await getGpsStatusListApi({
scenicSpotId: scenicSpotId.value
})
tabs.value[0].number = statusRes.data['全部']
tabs.value[1].number = statusRes.data['行驶']
tabs.value[2].number = statusRes.data['静止']
tabs.value[3].number = statusRes.data['离线']
}
const onInput = debounce((e) => {
PubSub.publish('keywordChange', e)
}, 500)
onMounted(() => {
scenicChange = PubSub.subscribe('scenicChange', (msg, data) => {
scenicSpotId.value = data.scenicSpotId
lat.value = data.lat
lng.value = data.lng
// getGpsList()
})
})
onUnmounted(() => {
PubSub.unsubscribe(scenicChange)
})
</script>
<style lang="scss">
.child-box{
display:flex;
flex:1;
}
.item__label{
min-width:vw(110);
display:flex;
align-items: center;
}
.label_img{
background: #0063ff;
padding:vh(5) vw(10);
border-radius:vh(2);
color:#fff;
width:vw(90);
text-align:center;
}
.label_img_2{
background: rgba(2, 249, 250, 0.5);
}
.infoBox {
> img {
width: vw(40) !important;
height: auto;
z-index: 9999;
}
}
.marker {
&-box {
position: relative;
width: vw(700);
border: 1px solid #0096ff;
background: linear-gradient(180deg, #0a4190 0%, #0e51b1 100%);
}
&-close {
position: absolute;
right: vw(20);
top: vh(10);
width: vw(30);
height: auto;
}
&-title {
width: 100%;
height: vh(36);
padding-left: vw(16);
font-weight: 600;
font-size: vw(16);
color: #ffffff;
display: flex;
align-items: center;
background-image: url('@/assets/images/marker-title.png');
background-size: 100% 100%;
}
&-header {
padding: vh(12) vw(14);
display: flex;
align-items: center;
justify-content: space-between;
}
&-sim {
font-weight: 600;
font-size: vw(16);
color: #ffffff;
}
&-tag {
padding: vh(5) vw(8);
font-weight: 400;
font-size: vw(13);
color: #0096ff;
display: flex;
align-items: center;
justify-content: center;
background-color: #0b4599;
border-radius: vw(23);
border: vw(1) solid #0096ff;
}
&-line {
width: 100%;
height: vw(1);
background-color: #134fa4;
}
&-row {
padding: vh(10) vw(16);
display: flex;
align-items: center;
border-bottom: 1px solid #134fa4;
}
&-col {
flex: 1;
display: flex;
align-items: flex-start;
> img {
width: vw(32);
height: vw(32);
margin-right: vw(10);
}
> p {
flex: 1;
font-weight: 400;
font-size: vw(24);
color: #ffffff;
line-height: vh(20);
}
}
}
</style>
<style scoped lang="scss">
:deep(.el-input__inner) {
height: vh(32);
color: #fff;
}
:deep(.el-input__wrapper) {
margin-top: vh(8);
font-size: vw(16);
border-radius: vw(2);
border: vw(1) solid #0096ff;
box-shadow: none !important;
background: rgba(217, 217, 217, 0);
}
.tabs {
display: flex;
border-bottom: vw(1) solid #0a4190;
.tab-item {
cursor: pointer;
position: relative;
flex: 1;
height: vh(44);
font-weight: 600;
font-size: vw(14);
color: #fff;
display: flex;
align-items: center;
justify-content: center;
}
.tab-item__active {
color: #00d0ff;
&::before {
content: '';
position: absolute;
bottom: 0;
width: vw(40);
height: vh(2);
background: #00d0ff;
}
}
}
:deep(.BMap_cpyCtrl) {
display: none;
}
:deep(.anchorBL) {
display: none;
}
.dialog {
:deep(.el-dialog) {
width: vw(2540);
padding: vw(8);
background-image: url('@/assets/images/dialog-bg.png') !important;
background-size: 100% 100%;
}
:deep(.el-dialog__header) {
padding-bottom: 0 !important;
}
}
.list-box {
margin-right: vw(10);
background-image: url('@/assets/images/bg-3.png');
background-size: 100% 100%;
}
.list {
width: vw(330);
height: vh(690);
padding-top: vh(10);
overflow-y: auto;
box-sizing: border-box;
/* 滚动条整体样式 */
&::-webkit-scrollbar {
width: vw(10); /* 滚动条的宽度 */
}
/* 滚动条轨道 */
&::-webkit-scrollbar-track {
background: 'transparent'; /* 轨道的背景色 */
}
/* 滚动条滑块 */
&::-webkit-scrollbar-thumb {
background: rgba(0, 150, 255, 0.63); /* 滑块的背景色 */
border-radius: 5px; /* 滑块的圆角 */
}
.item {
cursor: pointer;
font-weight: 400;
font-size: vw(14);
height: vh(30);
padding: 0 vw(20);
color: #0096ff;
display: flex;
align-items: center;
justify-content: space-between;
&__value {
display: flex;
align-items: center;
> img {
width: vw(16);
height: auto;
margin: 0 vw(10);
}
}
&__hot {
width: vw(8);
height: vw(8);
border-radius: 50%;
&--primary {
@extend .item__hot;
background-color: #02f9fa;
}
&--info {
@extend .item__hot;
background-color: #055498;
}
&--active {
@extend .item__hot;
// background-color: #ffffff !important;
}
}
&__active {
color: #fff !important;
background-color: #055498;
}
}
}
.big-car-ship {
flex: 1;
height: vh(820);
}
.close {
cursor: pointer;
position: absolute;
right: vw(20);
top: vw(20);
width: vw(60);
z-index: 9999;
}
</style>