Files
xj-screen/src/views/indexSun/centerMap.vue
T
weixin 5589f92e72 update
1.修改规划路线时,路程计算问题
2025-12-04 16:25:56 +08:00

938 lines
31 KiB
Vue

<template>
<div class="sun-center-map">
<div class="map-box">
<div class="tab-list">
<div v-for="(item, i) in list" :class="[currentIndex == i ? 'active' : '']" class="tab" @click="changeTab(item, i)" :key="`tab-${i}`">
{{ item.name }}
</div>
</div>
<div v-show="currentIndex == list.length - 1" class="route-navigation">
<a-form :model="formState" layout="inline">
<a-form-item class="form-item-self" label="起点">
<a-input v-model:value="formState.start" autocomplete="off" placeholder="请输入起点" id="startInput" />
</a-form-item>
<a-form-item class="form-item-self" label="终点">
<a-input v-model:value="formState.end" autocomplete="off" placeholder="请输入终点" id="endInput" />
</a-form-item>
<a-form-item>
<a-button class="reset-btn" @click="removeInput">清空</a-button>
<a-button class="search-btn" @click="searchRoute" :loading="searchFlag" :disabled="searchFlag"> 路线规划 </a-button>
</a-form-item>
</a-form>
</div>
<div class="reset-map" :class="[themeType]" @click="resetMap">
<RedoOutlined title="重置地图" />
</div>
<!-- <div v-show="showRoutePanel" v-click-outside="outClick" class="line-container" :class="['no-top']">-->
<div v-show="showRoutePanel" class="line-container" :class="[currentIndex !== list.length - 1 ? 'no-top' : '']">
<div v-show="routeList.length > 0" class="has-data">
<div class="line-title"> {{ panelTitle }}</div>
<div class="line-content">
<div class="start">起点</div>
<div class="line-body">
<div class="line"></div>
<div class="line-desc">
<p v-for="(item, i) in routeList" :key="i">{{ item }}</p>
</div>
</div>
<div class="end">终点</div>
</div>
</div>
<div v-show="routeList.length === 0" class="empty-data"> 暂无数据</div>
</div>
<div class="map">
<div style="flex: 1; overflow: hidden">
<div id="xianmapContainer" class="xianmapContainer"></div>
</div>
</div>
<div v-show="false" id="container"></div>
<div v-show="false" id="panel"></div>
<div class="map-legend">
<div v-for="(item, i) in legendList" :key="i" class="legend-item">
<div :style="{ color: item.color }"><img :src="item.img" alt="" class="legend-img" /> {{ item.text }}({{ numList[i] }}) </div>
</div>
</div>
</div>
<div class="center-footer" v-if="showFooter">
<div class="count-item">
<div class="num">{{ useDetail?.alerdyreceive || '0' }}</div>
<div class="text">累计受理</div>
</div>
<div class="count-item">
<div class="num">{{ useDetail?.totalacceptance || '0' }}</div>
<div class="text">累计处置</div>
</div>
</div>
<div class="hospital-list" :class="[themeType]" v-if="showMapBottom">
<div class="map-bottom">
<div>
<img :src="noLocationPng" alt="" />
</div>
<div> 未获取到当前求助信息的位置信息 </div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { nextTick, onMounted, reactive, ref, watch } from 'vue';
import { infoContent, infoContent1, infoContent4, legendList, tabList } from '/@/views/indexSun/indexSun.ts';
import { message } from 'ant-design-vue';
import { innerLineLight, outerLineOption } from '/@/assets/mapUtils/mapConstant.ts';
import { Map } from '/@/assets/mapUtils/map.ts';
import { getKmStr, getTimeStr, mapApiSwitch } from '/@/components/xianMap/xianMapHooks.ts';
import { useDetailStore } from '/@/store/modules/detailData.ts';
import $bus from '/@/utils/bus.ts';
import { earlyWarning } from '/@/utils/busConstant.ts';
import { useTheme } from '/@/hooks/theme/useTheme.ts';
import { RedoOutlined } from '@ant-design/icons-vue';
import { useUserStore } from '/@/store/modules/user.ts';
import noLocationPng from '/@/assets/images/map/noLocation.png';
const useDetail = useDetailStore();
const useStore = useUserStore();
const { themeType } = useTheme();
const list = ref(tabList);
const currentIndex = ref(0);
let searchFlag = ref(false);
const formState = ref({
start: '',
end: '',
startPosition: [],
endPosition: [],
});
// 1 aed 2 健康小屋 3 油田医院 4 救护车
const numList = reactive([useStore.getTypeThree, useStore.getTypeOne, useStore.getTypeFive, useStore.getTypeFour]);
const props = defineProps({
//是否展示底部统计
showFooter: {
type: Boolean,
default: () => {
return true;
},
},
//是否展示最近的三个医院
showHospitalList: {
type: Boolean,
default: () => false,
},
showRestB: {
type: Boolean,
default: () => false,
},
hospitalList: {
type: Array,
default: () => {
return [];
},
},
showMapBottom: {
type: Boolean,
default: () => false,
},
userData: {
type: Object,
default: () => {},
},
});
const showRoutePanel = ref(false);
const panelTitle = ref();
const level = ref(0);
const routeList = ref<any[]>([]);
const emits = defineEmits(['clearHospitalList', 'showRestB', 'handleDetail']);
let placeSearch: any = null;
let sListen: any = null;
let eListen: any = null;
watch(
() => props.showHospitalList,
(v) => {
if (v) {
watchInfoChangeTab(list.value[0], 0);
}
}
);
onMounted(() => {
const op = {
el: 'xianmapContainer',
innerLineOption: innerLineLight,
outerLineOption: outerLineOption,
};
Map.initMap(op).then(async () => {
await getMapData(tabList[0].type);
Map.map && Map.map.on('zoomend', setLevel);
Map.map && Map.map.on('zoomend', setLevel);
await initPlaceSearch();
Map.map.on('moveend', async () => {
// 获取当前地图的中心点[citation:2][citation:6]
await initPlaceSearch();
});
});
$bus.on(earlyWarning, (params) => {
drawMarker(params);
});
document.addEventListener('click', (e: any) => {
let t = e.target.closest('.viewBtn');
if (t) {
if (t.id) {
handleClickDetail(JSON.parse(t.id));
}
}
});
});
async function initPlaceSearch() {
placeSearch = null;
let city = {};
if (Map.map) {
const { lng, lat } = Map.map.getCenter();
city = await Map.getInfoByPoint([lng, lat]);
} else {
city = {};
}
// 确保 AutoComplete 加载完成后再使用
const startAuto = new Map.aMap.AutoComplete({ input: 'startInput', city, citylimit: true, closeResultOnScroll: false });
const endAuto = new Map.aMap.AutoComplete({ input: 'endInput', city, citylimit: true, closeResultOnScroll: false });
placeSearch = new Map.aMap.PlaceSearch({
city,
extensions: 'all',
pageIndex: 1,
pageSize: 30,
showMarker: false,
});
// const lnglat = Map.aMap.LngLat(87, 43);
// placeSearch.searchNearBy(lnglat, 1000);
sListen && startAuto.off(sListen);
eListen && endAuto.off(eListen);
sListen = startAuto.on('select', startSelect);
eListen = endAuto.on('select', endSelect);
}
function startSelect(e: any) {
placeSearch.search(e.poi.name, function (status: any, result: any) {
if (status === 'complete' && result.poiList.pois.length > 0) {
const poi = result.poiList.pois[0]; // 取第一个结果
const { lat, lng } = poi.location;
formState.value.start = poi.name;
formState.value.startPosition = [lng, lat];
Map.createSMarker([lng, lat]);
Map.map.setZoom(15);
}
});
}
function endSelect(e: any) {
placeSearch.search(e.poi.name, function (status: any, result: any) {
if (status === 'complete' && result.poiList.pois.length > 0) {
const poi = result.poiList.pois[0]; // 取第一个结果
const { lat, lng } = poi.location;
formState.value.end = poi.name;
formState.value.endPosition = [lng, lat];
Map.createEMarker([lng, lat]);
Map.map.setZoom(15);
}
});
}
function showDrivingLine(item: any) {
nextTick(() => {
panelTitle.value = '全程' + item.driveTime + item.distance;
routeList.value = item.routeList?.map((item: any) => item?.instruction);
if (item.lon) {
Map.clearNaviLine();
Map.createNaviLine(item.userLocation, [item.lon, item.lat]);
}
showRoutePanel.value = true;
});
}
function setLevel() {
//获取当前地图级别
level.value = Map.map.getZoom();
}
function removeInput() {
formState.value.start = '';
formState.value.startPosition = [];
formState.value.end = '';
formState.value.endPosition = [];
searchFlag.value = false;
Map.startMarker && Map.startMarker?.setMap(null);
Map.endMarker && Map.endMarker?.setMap(null);
Map.driving && Map.driving.clear();
Map.walking && Map.walking.clear();
}
function changeTab(item: any, i: number) {
currentIndex.value = i;
closeDrivingElement();
tab(item);
if (item.hasOwnProperty('type')) {
formState.value.startPosition = [];
formState.value.endPosition = [];
emits('showRestB');
} else {
setInfoByUserData(props.userData);
}
}
function setInfoByUserData(data: any) {
if (data?.address) {
formState.value.start = data.address;
formState.value.startPosition = [data?.lon, data?.lat];
} else {
if (data?.lon && data?.lat) {
formState.value.startPosition = [data?.lon, data?.lat];
Map.getAddressByPoint([data?.lon, data?.lat])
.then((res: any) => {
formState.value.start = res;
})
.catch((err) => {
console.log(err);
formState.value.start = '';
});
}
}
}
function tab(item: any) {
Map.clearActiveHospital();
Map.map.off('moveend', logMapinfo);
getMapData(item.type, { type: item.type });
}
function watchInfoChangeTab(item: any) {
currentIndex.value = 0;
tab(item);
}
async function searchRoute(callback: ((this: MediaQueryList, ev: MediaQueryListEvent) => any) | null) {
showRoutePanel.value = false;
panelTitle.value = '';
if (!formState.value.start) {
return message.warn('请输入起点');
}
if (!formState.value.end) {
return message.warn('请输入终点');
}
getSearch(formState.value);
}
function getSearch({ startPosition, endPosition, start, end }) {
if (Map.mapDriverOrWalkLoading) return message.warn('路线规划中,请稍等');
Map.mapDriverOrWalkLoading = true;
Map.driving && Map.driving.clear();
Map.walking && Map.walking.clear();
const distance = Map.getDistance(startPosition, endPosition);
console.log(startPosition.join(','), endPosition.join(','));
searchFlag.value = true;
const startArr = new Map.aMap.LngLat(startPosition[0], startPosition[1]);
const endtArr = new Map.aMap.LngLat(endPosition[0], endPosition[1]);
// let map = new Map.aMap.Map('container', {
// resizeEnable: true,
// center: [116.397428, 39.90923], //地图中心点
// zoom: 13, //地图显示的缩放级别
// });
try {
if (distance > 2000) {
Map.driving = new Map.aMap.Driving({
map: Map.map,
panel: 'panel',
});
Map.driving.search(startArr, endtArr, function (status: any) {
// result 即是对应的驾车导航信息,相关数据结构文档请参考 https://lbs.amap.com/api/javascript-api/reference/route-search#m_DrivingResult
if (status === 'complete') {
// console.log('绘制驾车路线完成');
searchFlag.value = false;
} else {
searchFlag.value = false;
}
});
Map.aMap.Event.addListener(Map.driving, 'complete', (res: any) => {
const data = JSON.parse(JSON.stringify(res));
searchFlag.value = false;
if (data.info == 'OK') {
const { routes } = data;
setTitle(routes[0]);
let arr = [];
arr.push(data.originName);
routes[0].steps.map((item: any) => {
arr.push(item.instruction);
});
arr.push(data.destinationName);
showRoutePanel.value = true;
routeList.value = arr;
Map.mapDriverOrWalkLoading = false;
}
});
} else {
Map.walking = new Map.aMap.Walking({
map: Map.map,
panel: 'panel',
});
Map.walking.search(startArr, endtArr, function (status: any) {
// result 即是对应的步行导航信息,相关数据结构文档请参考 https://lbs.amap.com/api/javascript-api/reference/route-search#m_DrivingResult
if (status === 'complete') {
// console.log('绘制步行路线完成');
searchFlag.value = false;
} else {
searchFlag.value = false;
}
});
Map.aMap.Event.addListener(Map.walking, 'complete', (res: any) => {
const data = JSON.parse(JSON.stringify(res));
searchFlag.value = false;
if (data.info == 'ok') {
const { routes } = data;
setTitle(routes[0]);
let arr = [];
arr.push(data.originName);
routes[0].steps.map((item: any) => {
arr.push(item.instruction);
});
arr.push(data.destinationName);
showRoutePanel.value = true;
routeList.value = arr;
Map.mapDriverOrWalkLoading = false;
}
});
}
} catch (e) {
console.log(e);
Map.mapDriverOrWalkLoading = false;
}
}
async function getMapData(type: any, params = { type: '' }) {
try {
const { code, result } = await mapApiSwitch(type, params);
if (!params.type) {
// aed
useStore.setTypeFour(result ? result.filter((item: any) => item?.type === '4').length : 0);
// 健康小屋
useStore.setTypeThree(result ? result.filter((item: any) => item?.type === '3').length : 0);
// 油田医院
useStore.setTypeOne(result ? result.filter((item: any) => item?.type === '1').length : 0);
// 救护车
useStore.setTypeFive(result ? result.filter((item: any) => item?.type === '5').length : 0);
}
if (code == 200) {
if (Array.isArray(result) && result.length > 0) {
Map.createOverlay(
result,
{
content: (val) => {
return `<div style="
width: 21px;
height: 44px;
position: absolute;
left: -20px;
top: -20px;" title="${val?.name}">
</div>`;
},
},
async (val) => {
if (val.type == '3') {
Map.createInfoModal({ option: val, content: infoContent(val) });
} else if (val.type == '4') {
Map.createInfoModal({ option: val, content: infoContent4(val) });
} else {
Map.createInfoModal({ option: val, content: infoContent1(val) });
}
},
true
);
} else {
Map.removeOverlayGroup();
}
}
} catch (e) {
console.warn(e);
}
}
function closeDrivingElement() {
Map.driving && Map.driving.clear();
Map.walking && Map.walking.clear();
if (currentIndex.value !== list.value.length - 1) {
Map.clearNavigationElements();
} else {
Map.clearNaviLine();
}
formState.value.start = '';
formState.value.end = '';
showRoutePanel.value = false;
emits('clearHospitalList');
}
function handleClickDetail(res: any) {
emits('handleDetail', res);
}
function setTitle({ time, distance }) {
let str = '全程';
str += getTimeStr(time);
// str += getKmStr(distance);
str += distance < 1000 ? distance + '米' : parseFloat(parseFloat(distance / 1000).toFixed(1)) + '公里';
panelTitle.value = str;
}
function outClick() {
showRoutePanel.value = false;
}
const currentCityCode = ref('');
function logMapinfo() {
Map.map.getCity(function (info) {
const { citycode } = info;
if (currentCityCode.value != citycode) {
Map.createActiveHospital(citycode);
currentCityCode.value = citycode;
}
});
}
function drawMarker(data: any) {
const { lon, lat } = data;
if (!lon || !lat) {
Map.clearMarker();
return;
}
Map.createMarker({ ...data, realName: data.userName });
}
function resetMap() {
Map.clearSOrE();
closeDrivingElement();
removeInput();
changeTab(list.value[currentIndex.value === 4 ? 0 : currentIndex.value], currentIndex.value);
emits('showRestB');
}
function newResetMap() {
Map.clearSOrE();
closeDrivingElement();
removeInput();
tab(currentIndex.value);
}
function setStartPosition(data: any) {
setInfoByUserData(data);
}
defineExpose({
resetMap,
newResetMap,
setStartPosition,
getSearch,
});
</script>
<style lang="less" scoped>
.sun-center-map {
flex: 1;
color: #fff;
display: flex;
flex-direction: column;
align-content: space-between;
position: relative;
.map-box {
flex: 1;
display: flex;
flex-direction: column;
position: relative;
.tab-list {
display: flex;
flex-wrap: wrap;
justify-content: space-around;
font-size: 16px;
letter-spacing: 1px;
.tab {
width: calc(100% / 8);
height: 40px;
display: flex;
justify-content: center;
align-items: center;
color: #ffffff;
background: #082147;
border-radius: 8px;
cursor: pointer;
transition: all 0.3s;
&.active {
color: #00ccff;
background: #073063;
font-weight: bold;
}
}
}
.route-navigation {
--fontColor: #607ddb;
background: #002e64;
margin: 12px 0;
width: 100%;
height: 46px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 6px;
position: absolute;
top: 36px;
z-index: 100;
transition: all 0.3s;
:deep(.form-item-self) {
label {
color: var(--fontColor) !important;
}
.ant-input {
width: 280px;
border-radius: 2px 2px 2px 2px;
color: var(--fontColor);
background: #d7ddf6;
border: 1px solid #a8bcff;
&::placeholder {
color: var(--fontColor);
}
}
}
.reset-btn {
width: 64px;
height: 32px;
border-radius: 5px;
color: var(--fontColor);
background: #c7d4ff;
border: 1px solid #7092ff;
}
.search-btn {
margin-left: 10px;
width: 120px;
height: 32px;
color: #fff;
background: #6d8bf1;
border-radius: 5px;
}
}
.map {
display: flex;
flex: 1;
.xianmapContainer {
flex: 1;
height: calc(100% + 22px);
margin: 10px 0;
}
}
.map-legend {
position: absolute;
bottom: 20px;
left: 10px;
padding: 0 20px 0 10px;
display: flex;
border-radius: 6px;
flex-direction: column;
.legend-item {
width: 100%;
font-size: 14px;
font-weight: 400;
margin: 5px 0;
color: #607ddb;
letter-spacing: 2px;
.legend-img {
width: 20px;
margin: -4px 5px 0 0;
}
}
}
}
.center-footer {
position: absolute;
left: 50%;
bottom: 20px;
transform: translateX(-50%);
display: flex;
justify-content: center;
margin-bottom: 10px;
gap: 20px;
> div {
position: relative;
&:before {
position: absolute;
top: 10px;
left: 10px;
background: #000001;
content: '';
width: 220px;
height: 100px;
z-index: -1;
}
}
> :nth-child(1) {
background: url('/@/assets/indexSun/footerBlue.png') no-repeat;
background-size: 100% 100%;
color: #22e8ff;
margin-right: 80px;
}
> :nth-child(2) {
background: url('/@/assets/indexSun/footerGreen.png') no-repeat;
background-size: 100% 100%;
color: #45ffa1;
}
.count-item {
width: 240px;
height: 120px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
//background: url('/@/assets/images/footer-num.png') no-repeat;
//border-radius: 14px;
.num {
font-size: 24px;
font-weight: 700;
}
.text {
font-size: 16px;
font-weight: 400;
}
}
}
:deep(.modal-box) {
width: 330px;
padding: 10px 20px;
display: flex;
flex-direction: column;
justify-content: space-around;
.top-left-a-right {
width: 48px;
height: 2px;
background: linear-gradient(90deg, #02fafc 0%, rgba(2, 250, 252, 0) 100%);
border-radius: 1px;
}
.modal-item {
display: flex;
justify-content: space-between;
.item-one {
width: 48%;
}
.item-two {
color: #00ccff;
text-align: right;
}
}
.modal-btn {
display: flex;
justify-content: center;
.view-btn {
padding: 5px;
width: 60%;
text-align: center;
color: #00ccff;
border-radius: 5px;
cursor: pointer;
border: 1px solid #00ccff;
}
}
}
:deep(.modal-box-normal) {
width: 330px;
height: 150px;
}
:deep(.modal-box-normal-1) {
width: 330px;
height: 110px;
}
.hospital-list {
position: absolute;
bottom: 160px;
left: 50%;
transform: translateX(-50%);
display: flex;
.hospital-item {
background: #0a1d36;
border: 1px solid #0a1d36;
box-shadow: 0 0 3px 3px #0a1d36;
position: relative;
flex: 1;
gap: 20px;
min-width: 200px;
max-width: 200px;
text-align: center;
padding: 10px;
margin: 0 10px;
font-size: 16px;
border-radius: 12px;
cursor: pointer;
}
}
.line-container {
z-index: 99;
position: absolute;
right: 0;
top: 100px;
width: 288px;
padding: 9px 18px;
min-height: 200px;
max-height: 473px;
overflow: hidden;
background: #0a1d36;
border-radius: 2px;
display: flex;
flex-direction: column;
//border: 1px solid #1b7ef2;
.has-data {
height: 100%;
display: flex;
flex-direction: column;
overflow: auto;
}
.line-title {
margin-bottom: 9px;
font-size: 14px;
font-weight: 700;
background: #0a1d36 !important;
color: var(--cardFontColor);
}
.line-content {
flex: 1;
overflow: auto;
font-size: 12px;
color: var(--cardFontColor);
.start {
display: flex;
align-items: center;
&::before {
content: '起';
color: var(--cardFontFFF);
width: 20px;
height: 20px;
margin-right: 7px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 50%;
background: var(--circelBg);
}
}
.end {
display: flex;
align-items: center;
&::before {
content: '终';
color: var(--cardFontFFF);
width: 20px;
height: 20px;
margin-right: 7px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 50%;
background: var(--circelBg);
}
}
.line-body {
display: flex;
color: var(--cardFontColor);
}
.line {
width: 1px;
background-color: var(--circelBg);
margin: 5px 19px 5px 8px;
}
.line-desc {
flex: 1;
}
}
.empty-data {
margin-top: 30%;
text-align: center;
}
}
}
.reset-map {
position: absolute;
left: 0;
top: 50px;
z-index: 101;
font-size: 20px;
cursor: pointer;
padding: 0 6px;
}
:deep(.amap-sug-result) {
background: #002e64 !important;
}
.map-bottom {
border-radius: 10px;
width: 570px;
padding: 5px 0;
> div {
width: 100%;
&:nth-child(1) {
display: flex;
justify-content: center;
padding: 0 0 12px;
img {
width: 100px;
height: 70px;
}
}
&:nth-child(2) {
height: 60px;
line-height: 60px;
text-align: center;
background: rgba(242, 68, 57, 0.3);
color: #f24439;
font-size: 18px;
font-weight: bold;
}
}
}
.no-top {
top: 50px !important;
}
</style>