增加地图元素清空功能,完成附近医院路线规划功能
This commit is contained in:
@@ -92,5 +92,3 @@ export function getCenter(list = []) {
|
|||||||
let centerObj = turf.center(features);
|
let centerObj = turf.center(features);
|
||||||
return centerObj?.geometry?.coordinates;
|
return centerObj?.geometry?.coordinates;
|
||||||
}
|
}
|
||||||
|
|
||||||
// export function
|
|
||||||
+149
-2
@@ -2,7 +2,7 @@ import AMapLoader from '@amap/amap-jsapi-loader';
|
|||||||
import china from '/@/assets/lngLat/china.ts';
|
import china from '/@/assets/lngLat/china.ts';
|
||||||
import chinaInner from '/@/assets/lngLat/chinaInner.ts';
|
import chinaInner from '/@/assets/lngLat/chinaInner.ts';
|
||||||
import { createCircle, createIcon, getCenter, getLonLat, typeToIcon } from '/@/assets/mapUtils/commonFun.ts';
|
import { createCircle, createIcon, getCenter, getLonLat, typeToIcon } from '/@/assets/mapUtils/commonFun.ts';
|
||||||
import { aircraftContent, alarmIcon } from '/@/components/chinaMap/chinaHooks.ts';
|
import { aircraftContent, alarmIcon, female, male } from '/@/components/chinaMap/chinaHooks.ts';
|
||||||
import { isFunction } from '/@/utils/is.ts';
|
import { isFunction } from '/@/utils/is.ts';
|
||||||
import { Callback, ExtDataType, InitMap, MarkerOption, ResultType } from '/@/assets/mapUtils/mapFunTypes.ts';
|
import { Callback, ExtDataType, InitMap, MarkerOption, ResultType } from '/@/assets/mapUtils/mapFunTypes.ts';
|
||||||
import { defaultOption, getZoom, mapKey, mapPlugin } from '/@/assets/mapUtils/mapConstant.ts';
|
import { defaultOption, getZoom, mapKey, mapPlugin } from '/@/assets/mapUtils/mapConstant.ts';
|
||||||
@@ -17,6 +17,8 @@ export const Map = {
|
|||||||
placeSearch: null, //地点查询
|
placeSearch: null, //地点查询
|
||||||
marker: null, // 健康终端报警
|
marker: null, // 健康终端报警
|
||||||
infoWindow: null, //地图弹窗
|
infoWindow: null, //地图弹窗
|
||||||
|
driveLine: null, // 导航轨迹线
|
||||||
|
emergencyMarker: null, // 应急人员marker
|
||||||
/**
|
/**
|
||||||
* @desc 地图舒适化
|
* @desc 地图舒适化
|
||||||
* @param el dom元素 id选择器
|
* @param el dom元素 id选择器
|
||||||
@@ -31,7 +33,6 @@ export const Map = {
|
|||||||
}).then((res) => {
|
}).then((res) => {
|
||||||
AMap = res;
|
AMap = res;
|
||||||
const { zoom, center, mapStyle } = { ...defaultOption, ...option };
|
const { zoom, center, mapStyle } = { ...defaultOption, ...option };
|
||||||
console.log(zoom, center);
|
|
||||||
//基本地图加载
|
//基本地图加载
|
||||||
this.map = new AMap.Map(el, {
|
this.map = new AMap.Map(el, {
|
||||||
resizeEnable: true,
|
resizeEnable: true,
|
||||||
@@ -314,4 +315,150 @@ export const Map = {
|
|||||||
this.infoWindow = null;
|
this.infoWindow = null;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
* @desc 计算两个坐标点的距离
|
||||||
|
* @param {Array} listA 坐标点数组
|
||||||
|
* @param {Array} listB 坐标点数组
|
||||||
|
* @return {Number} 距离
|
||||||
|
*/
|
||||||
|
getDistance(listA: number[], listB: number[]) {
|
||||||
|
let lnglatA = new AMap.LngLat(listA[0], listA[1]);
|
||||||
|
let lnglatB = new AMap.LngLat(listB[0], listB[1]);
|
||||||
|
return lnglatA.distance(lnglatB);
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @desc 计算两个坐标点的驾车时间
|
||||||
|
* @param {Array} listA 坐标点数组
|
||||||
|
* @param {Array} listB 坐标点数组
|
||||||
|
* @param {Function} callback 回调
|
||||||
|
* @return {Number} 时间
|
||||||
|
*/
|
||||||
|
getDriveTime(listA: number[], listB: number[], callback?: Callback) {
|
||||||
|
let lnglatA = new AMap.LngLat(listA[0], listA[1]);
|
||||||
|
let lnglatB = new AMap.LngLat(listB[0], listB[1]);
|
||||||
|
const drivingOptions = {
|
||||||
|
policy: AMap.DrivingPolicy.LEAST_TIME,
|
||||||
|
};
|
||||||
|
let driving = new AMap.Driving(drivingOptions);
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
driving.search(lnglatA, lnglatB, (status, result) => {
|
||||||
|
if (status === 'complete') {
|
||||||
|
// console.log('result', result);
|
||||||
|
if (callback && isFunction(callback)) {
|
||||||
|
callback(result);
|
||||||
|
}
|
||||||
|
return resolve(result.routes[0]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @desc 创建导航轨迹线路
|
||||||
|
* @param {Array} listA 坐标点数组
|
||||||
|
* @param {Array} listB 坐标点数组
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
createNaviLine(listA: number[], listB: number[]) {
|
||||||
|
this.clearNaviLine();
|
||||||
|
let lnglatA = new AMap.LngLat(listA[0], listA[1]);
|
||||||
|
let lnglatB = new AMap.LngLat(listB[0], listB[1]);
|
||||||
|
const drivingOptions = {
|
||||||
|
policy: AMap.DrivingPolicy.LEAST_TIME,
|
||||||
|
map: this.map,
|
||||||
|
autoFitView: true,
|
||||||
|
// panel: 'driving-panel',
|
||||||
|
};
|
||||||
|
this.driveLine = new AMap.Driving(drivingOptions);
|
||||||
|
this.driveLine.search(lnglatA, lnglatB, (status, result) => {
|
||||||
|
if (status === 'complete') {
|
||||||
|
// console.log('result', result);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.driveLine.on('error', function (result) {
|
||||||
|
let str = '定位失败:';
|
||||||
|
console.log('error', result);
|
||||||
|
switch (result.info) {
|
||||||
|
case 'PERMISSION_DENIED':
|
||||||
|
str += '浏览器阻止了定位操作';
|
||||||
|
break;
|
||||||
|
case 'POSITION_UNAVAILBLE':
|
||||||
|
str += '无法获得当前位置';
|
||||||
|
break;
|
||||||
|
case 'TIMEOUT':
|
||||||
|
str += '定位超时';
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
str += '未知错误';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
console.log(str);
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @desc 清除导航轨迹线路
|
||||||
|
*/
|
||||||
|
clearNaviLine() {
|
||||||
|
if (this.driveLine) {
|
||||||
|
this.driveLine.clear();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @desc 创建应急人员Marker
|
||||||
|
*/
|
||||||
|
createEmergencyMarker(option) {
|
||||||
|
this.clearEmergencyMarker();
|
||||||
|
const { sex, address, name, position } = option;
|
||||||
|
const icon = sex == '1' ? female : male;
|
||||||
|
this.emergencyMarker = new AMap.Marker({
|
||||||
|
position: position,
|
||||||
|
icon: createIcon({ icon: icon, iconSize: [36, 36], imageSize: [36, 36] }),
|
||||||
|
offset: new AMap.Pixel(0, 0), //设置偏移量
|
||||||
|
label: {
|
||||||
|
content: `<div class="alarm-content" style="width: 180px" >
|
||||||
|
<div style="text-align: center;padding-bottom: 6px">${name}</div>
|
||||||
|
<div style="text-wrap: wrap">${address}</div>
|
||||||
|
</div>`,
|
||||||
|
offset: new AMap.Pixel(-100, 55),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
this.map.add(this.emergencyMarker);
|
||||||
|
this.setZoomCenter(position);
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @desc 清除应急人员Marker
|
||||||
|
*/
|
||||||
|
clearEmergencyMarker() {
|
||||||
|
if (this.emergencyMarker) {
|
||||||
|
this.emergencyMarker?.setMap(null);
|
||||||
|
this.emergencyMarker = null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @desc 根据坐标获取地址信息
|
||||||
|
* @param {Array} list 坐标点数组
|
||||||
|
*/
|
||||||
|
getAddressByPoint(list: number[]) {
|
||||||
|
const geocoder = new AMap.Geocoder({
|
||||||
|
city: '全国',
|
||||||
|
radius: 1000,
|
||||||
|
});
|
||||||
|
let lnglat = new AMap.LngLat(list[0], list[1]);
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
geocoder.getAddress(lnglat, (status, result) => {
|
||||||
|
if (status === 'complete' && result.regeocode) {
|
||||||
|
return resolve(result.regeocode.formattedAddress);
|
||||||
|
}
|
||||||
|
return reject(result);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @desc 清除导航元素
|
||||||
|
*/
|
||||||
|
clearNavigationElements() {
|
||||||
|
this.clearNaviLine();
|
||||||
|
this.clearEmergencyMarker();
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// 西安地图默认配置
|
// 地图默认配置
|
||||||
export const defaultOption = {
|
export const defaultOption = {
|
||||||
center: [108.660704, 36.386058], //地图中心点
|
center: [108.660704, 36.386058], //地图中心点
|
||||||
zoom: 6.5, //地图显示的缩放级别
|
zoom: 6.5, //地图显示的缩放级别
|
||||||
@@ -12,7 +12,8 @@ export const YinChuanOption = {
|
|||||||
|
|
||||||
// 地图key
|
// 地图key
|
||||||
export const mapKey = '4bbcb216b889f2d612cbed05ae6979c8';
|
export const mapKey = '4bbcb216b889f2d612cbed05ae6979c8';
|
||||||
export const mapPlugin = ['AMap.ToolBar', 'AMap.Driving', 'AMap.GeoJSON', 'AMap.MarkerCluster', 'AMap.PlaceSearch'];
|
// 地图插件
|
||||||
|
export const mapPlugin = ['AMap.ToolBar', 'AMap.Driving', 'AMap.GeoJSON', 'AMap.MarkerCluster', 'AMap.PlaceSearch', 'AMap.Driving', 'AMap.Geocoder'];
|
||||||
|
|
||||||
//根据数据量设置地图层级
|
//根据数据量设置地图层级
|
||||||
export function getZoom(list = []) {
|
export function getZoom(list = []) {
|
||||||
|
|||||||
@@ -47,7 +47,8 @@ export const tabList = [
|
|||||||
export const helicopter = new URL('/@/assets/img/helicopter.png', import.meta.url).href;
|
export const helicopter = new URL('/@/assets/img/helicopter.png', import.meta.url).href;
|
||||||
export const mapbg = new URL('/@/assets/img/helicopter.png', import.meta.url).href;
|
export const mapbg = new URL('/@/assets/img/helicopter.png', import.meta.url).href;
|
||||||
export const alarmIcon = new URL('/@/assets/img/alarm-icon.png', import.meta.url).href;
|
export const alarmIcon = new URL('/@/assets/img/alarm-icon.png', import.meta.url).href;
|
||||||
|
export const male = new URL('/@/assets/img/male.png', import.meta.url).href;
|
||||||
|
export const female = new URL('/@/assets/img/female.png', import.meta.url).href;
|
||||||
// 直升机位置数据
|
// 直升机位置数据
|
||||||
export const aircraft = [
|
export const aircraft = [
|
||||||
{
|
{
|
||||||
@@ -171,4 +172,4 @@ export function getKmStr(distance: number): string {
|
|||||||
if (!distance) return '';
|
if (!distance) return '';
|
||||||
let d = convertMetersToKilometers(distance);
|
let d = convertMetersToKilometers(distance);
|
||||||
return d >= 1 ? d + '公里' : d * 100 + '米';
|
return d >= 1 ? d + '公里' : d * 100 + '米';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,6 +36,16 @@
|
|||||||
<div v-show="!routeList.length" class="empty-data"> 暂无数据</div>
|
<div v-show="!routeList.length" class="empty-data"> 暂无数据</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div v-show="showHospitalList" class="reset-map" @click="resetMap">
|
||||||
|
<RedoOutlined title="重置地图" />
|
||||||
|
</div>
|
||||||
|
<RoutePanel
|
||||||
|
class="driving-route-panel"
|
||||||
|
v-show="showDrivingPanel"
|
||||||
|
:drivingRoute="drivingRoute"
|
||||||
|
:drivingTitle="drivingTitle"
|
||||||
|
@out-click="showDrivingPanel = false"
|
||||||
|
/>
|
||||||
<div style="flex: 1; overflow: hidden; margin: 0 0 10px">
|
<div style="flex: 1; overflow: hidden; margin: 0 0 10px">
|
||||||
<div id="mapContainer" class="mapContainer"></div>
|
<div id="mapContainer" class="mapContainer"></div>
|
||||||
</div>
|
</div>
|
||||||
@@ -58,35 +68,62 @@
|
|||||||
<div class="text">累计处置</div>
|
<div class="text">累计处置</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<!-- 附近医院、医疗点 -->
|
||||||
|
<div class="hospital-list" v-if="showHospitalList">
|
||||||
|
<div class="hospital-item" v-for="(item, index) in hospitalList" :key="index" @click="showDrivingLine(item)">
|
||||||
|
<div>{{ item.name }}</div>
|
||||||
|
<div>相距{{ item.distance }}</div>
|
||||||
|
<div>驾车大约{{ item.driveTime }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { onMounted, ref } from 'vue';
|
import { nextTick, onMounted, ref } from 'vue';
|
||||||
import { message } from 'ant-design-vue';
|
import { message } from 'ant-design-vue';
|
||||||
|
import { RedoOutlined } from '@ant-design/icons-vue';
|
||||||
import { Map } from '/@/assets/mapUtils/map';
|
import { Map } from '/@/assets/mapUtils/map';
|
||||||
import { aircraft, getKmStr, getTimeStr, legendList, mapApiSwitch, tabList, TabType } from '/@/components/chinaMap/chinaHooks';
|
import { aircraft, getKmStr, getTimeStr, legendList, mapApiSwitch, tabList, TabType } from '/@/components/chinaMap/chinaHooks';
|
||||||
import { useDetailStore } from '/@/store/modules/detailData.ts';
|
import { useDetailStore } from '/@/store/modules/detailData.ts';
|
||||||
|
import RoutePanel from '/@/components/chinaMap/components/routePanel.vue';
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
|
//是否展示底部统计
|
||||||
showFooter: {
|
showFooter: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: () => {
|
default: () => {
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
//地图是否按照接口数据的中心点定位
|
||||||
computedCenter: {
|
computedCenter: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: () => {
|
default: () => {
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
// 地图配置
|
||||||
mapOptions: {
|
mapOptions: {
|
||||||
type: Object,
|
type: Object,
|
||||||
default: () => {
|
default: () => {
|
||||||
return {};
|
return {};
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
//是否展示最近的三个医院
|
||||||
|
showHospitalList: {
|
||||||
|
type: Boolean,
|
||||||
|
default: () => {
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
hospitalList: {
|
||||||
|
type: Array,
|
||||||
|
default: () => {
|
||||||
|
return [];
|
||||||
|
},
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
const emits = defineEmits(['clearHospitalList']);
|
||||||
const useDetail = useDetailStore();
|
const useDetail = useDetailStore();
|
||||||
const formState = ref({
|
const formState = ref({
|
||||||
start: '',
|
start: '',
|
||||||
@@ -98,6 +135,7 @@
|
|||||||
function changeTab(item, i: number) {
|
function changeTab(item, i: number) {
|
||||||
currentIndex.value = i; // 地方医院
|
currentIndex.value = i; // 地方医院
|
||||||
Map.clearActiveHospital();
|
Map.clearActiveHospital();
|
||||||
|
closeDrivingElement();
|
||||||
Map.map.off('moveend', logMapinfo);
|
Map.map.off('moveend', logMapinfo);
|
||||||
if (TabType.diFangYiYuan == item.type) {
|
if (TabType.diFangYiYuan == item.type) {
|
||||||
Map.removeOverlayGroup();
|
Map.removeOverlayGroup();
|
||||||
@@ -237,6 +275,36 @@
|
|||||||
formState.value.start = '';
|
formState.value.start = '';
|
||||||
formState.value.end = '';
|
formState.value.end = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 驾车路线
|
||||||
|
const drivingRoute = ref([]);
|
||||||
|
const drivingTitle = ref('');
|
||||||
|
const showDrivingPanel = ref(false);
|
||||||
|
|
||||||
|
function showDrivingLine(item) {
|
||||||
|
nextTick(() => {
|
||||||
|
drivingTitle.value = '全程' + item.driveTime + item.distance;
|
||||||
|
drivingRoute.value = item.routeList?.map((item) => item?.instruction);
|
||||||
|
showDrivingPanel.value = true;
|
||||||
|
if (item.lon) {
|
||||||
|
Map.clearNaviLine();
|
||||||
|
Map.createNaviLine(item.userLocation, [item.lon, item.lat]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeDrivingElement() {
|
||||||
|
Map.clearNavigationElements();
|
||||||
|
formState.value.start = '';
|
||||||
|
formState.value.end = '';
|
||||||
|
showDrivingPanel.value = false;
|
||||||
|
emits('clearHospitalList');
|
||||||
|
}
|
||||||
|
|
||||||
|
function resetMap() {
|
||||||
|
closeDrivingElement();
|
||||||
|
changeTab(list.value[currentIndex.value], currentIndex.value);
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
.center-map {
|
.center-map {
|
||||||
@@ -409,6 +477,34 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.reset-map {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 50px;
|
||||||
|
z-index: 99;
|
||||||
|
font-size: 20px;
|
||||||
|
color: #05d5ff;
|
||||||
|
cursor: pointer;
|
||||||
|
background: #243b5d;
|
||||||
|
padding: 0px 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
//驾驶路线导航面板
|
||||||
|
.driving-route-panel {
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
top: 86px;
|
||||||
|
width: 288px;
|
||||||
|
padding: 9px 18px;
|
||||||
|
min-height: 200px;
|
||||||
|
max-height: 473px;
|
||||||
|
overflow: auto;
|
||||||
|
background: #2a4164;
|
||||||
|
border-radius: 2px;
|
||||||
|
border: 1px solid #1b7ef2;
|
||||||
|
z-index: 99;
|
||||||
|
}
|
||||||
|
|
||||||
.mapContainer {
|
.mapContainer {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
height: calc(100% + 22px);
|
height: calc(100% + 22px);
|
||||||
@@ -472,5 +568,38 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.hospital-list {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 200px;
|
||||||
|
width: 800px;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
.hospital-item {
|
||||||
|
position: relative;
|
||||||
|
flex: 1;
|
||||||
|
gap: 20px;
|
||||||
|
min-width: 230px;
|
||||||
|
max-width: 230px;
|
||||||
|
height: 110px;
|
||||||
|
text-align: center;
|
||||||
|
padding: 10px;
|
||||||
|
margin: 0 10px;
|
||||||
|
color: #fff;
|
||||||
|
font-size: 16px;
|
||||||
|
background-color: #00152b;
|
||||||
|
border: 1px solid #1b7ef2;
|
||||||
|
border-radius: 12px;
|
||||||
|
box-shadow: 0 1px 11px 0 rgba(194, 189, 189, 0.5);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hospital-item:hover {
|
||||||
|
background-color: #192a46;
|
||||||
|
box-shadow: inset 0px 0px 16px 3px #2562a9;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -0,0 +1,120 @@
|
|||||||
|
<template>
|
||||||
|
<div class="route-panel">
|
||||||
|
<div v-show="drivingRoute.length" class="has-data">
|
||||||
|
<div class="line-title">
|
||||||
|
<div>{{ drivingTitle }}</div>
|
||||||
|
<div class="close-icon">
|
||||||
|
<CloseCircleOutlined style="font-size: 16px" @click="outClick" />
|
||||||
|
</div>
|
||||||
|
</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 drivingRoute" :key="i">{{ item }}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="end">终点</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-show="!drivingRoute.length" class="empty-data"> 暂无数据</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { CloseCircleOutlined } from '@ant-design/icons-vue';
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
drivingRoute: {
|
||||||
|
type: Array,
|
||||||
|
default: () => [],
|
||||||
|
},
|
||||||
|
drivingTitle: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const emit = defineEmits(['outClick']);
|
||||||
|
|
||||||
|
function outClick() {
|
||||||
|
emit('outClick', false);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style scoped lang="less">
|
||||||
|
.route-panel {
|
||||||
|
.line-title {
|
||||||
|
position: relative;
|
||||||
|
margin-bottom: 9px;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: rgba(255, 255, 255, 0.8);
|
||||||
|
|
||||||
|
.close-icon {
|
||||||
|
position: absolute;
|
||||||
|
top: 1px;
|
||||||
|
right: -13px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.line-content {
|
||||||
|
font-size: 12px;
|
||||||
|
|
||||||
|
.start {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: '起';
|
||||||
|
color: #fff;
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
margin-right: 7px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: #1478f5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.end {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: '终';
|
||||||
|
color: #fff;
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
margin-right: 7px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: #1478f5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.line-body {
|
||||||
|
display: flex;
|
||||||
|
//max-height: 370px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.line {
|
||||||
|
width: 1px;
|
||||||
|
//max-height: 370px;
|
||||||
|
background-color: #1478f5;
|
||||||
|
margin: 5px 19px 5px 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.line-sesc {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-data {
|
||||||
|
margin-top: 30%;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -1,12 +1,10 @@
|
|||||||
import { LOGOUT_CURRENT_PATH } from '/@/enum/cacheEnum.ts';
|
import { LOGOUT_CURRENT_PATH } from '/@/enum/cacheEnum.ts';
|
||||||
|
|
||||||
export enum PageEnum {
|
export enum PageEnum {
|
||||||
// basic login path
|
BASE_LOGIN = '/login',
|
||||||
BASE_LOGIN = '/login', //西安登录页
|
BASE_HOME = '/home',
|
||||||
BASE_HOME = '/home', //西安首页
|
BASE_LOGIN_YINCHUAN = '/screenLogin',
|
||||||
BASE_LOGIN_YINCHUAN = '/screenLogin', //银川登录页
|
YINCHUAN_HOME = '/yinChuanHome',
|
||||||
YINCHUAN_HOME = '/yinChuanHome', //银川首页
|
|
||||||
//other
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function setBaseLogin(baseLogin) {
|
export function setBaseLogin(baseLogin) {
|
||||||
@@ -19,9 +17,8 @@ export function getBaseLogin() {
|
|||||||
|
|
||||||
//根据当前路径跳转登录页
|
//根据当前路径跳转登录页
|
||||||
export function toLoginByNowPath(nowPath: string) {
|
export function toLoginByNowPath(nowPath: string) {
|
||||||
//西安的页面路径
|
|
||||||
const xianPath = ['/home', '/secondScreen', '/'];
|
const xianPath = ['/home', '/secondScreen', '/'];
|
||||||
//银川的页面路径
|
|
||||||
const yinchuanPath = ['/yinChuanHome'];
|
const yinchuanPath = ['/yinChuanHome'];
|
||||||
if (xianPath.includes(nowPath)) {
|
if (xianPath.includes(nowPath)) {
|
||||||
return PageEnum.BASE_LOGIN;
|
return PageEnum.BASE_LOGIN;
|
||||||
|
|||||||
+5
-5
@@ -47,10 +47,10 @@ export function useSocket(p = basicPath) {
|
|||||||
//这里发送一个心跳,后端收到后,返回一个心跳消息,
|
//这里发送一个心跳,后端收到后,返回一个心跳消息,
|
||||||
//onmessage拿到返回的心跳就说明连接正常
|
//onmessage拿到返回的心跳就说明连接正常
|
||||||
if (socket.value.readyState === WebSocket.OPEN) {
|
if (socket.value.readyState === WebSocket.OPEN) {
|
||||||
console.log('连接正常' + new Date().toLocaleString());
|
//console.log('连接正常' + new Date().toLocaleString());
|
||||||
socket.value.send('ping');
|
socket.value.send('ping');
|
||||||
} else {
|
} else {
|
||||||
console.log('连接断开');
|
//console.log('连接断开');
|
||||||
init();
|
init();
|
||||||
}
|
}
|
||||||
self.serverTimeoutObj = setTimeout(function () {
|
self.serverTimeoutObj = setTimeout(function () {
|
||||||
@@ -74,7 +74,7 @@ export function useSocket(p = basicPath) {
|
|||||||
|
|
||||||
socket.value.onclose = function () {
|
socket.value.onclose = function () {
|
||||||
reconnect(path.value);
|
reconnect(path.value);
|
||||||
// console.log('连接关闭', new Date().toLocaleString());
|
// //console.log('连接关闭', new Date().toLocaleString());
|
||||||
};
|
};
|
||||||
socket.value.onerror = function () {
|
socket.value.onerror = function () {
|
||||||
reconnect(path.value);
|
reconnect(path.value);
|
||||||
@@ -82,12 +82,12 @@ export function useSocket(p = basicPath) {
|
|||||||
};
|
};
|
||||||
socket.value.onopen = function () {
|
socket.value.onopen = function () {
|
||||||
heartCheck.reset().start(); //心跳检测重置
|
heartCheck.reset().start(); //心跳检测重置
|
||||||
// console.log('连接成功:' + new Date().toLocaleString());
|
// //console.log('连接成功:' + new Date().toLocaleString());
|
||||||
};
|
};
|
||||||
socket.value.onmessage = function (event) {
|
socket.value.onmessage = function (event) {
|
||||||
//如果获取到消息,心跳检测重置
|
//如果获取到消息,心跳检测重置
|
||||||
heartCheck.reset().start(); //拿到任何消息都说明当前连接是正常的
|
heartCheck.reset().start(); //拿到任何消息都说明当前连接是正常的
|
||||||
console.log('收到消息:', event.data);
|
//console.log('收到消息:', event.data);
|
||||||
if (event.data != 'pong') {
|
if (event.data != 'pong') {
|
||||||
let data = JSON.parse(event.data);
|
let data = JSON.parse(event.data);
|
||||||
socketData.value = data;
|
socketData.value = data;
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<Dialog :openVis="visible" width="30%" :title="title" @close="closeDialog" :maskClosable="false">
|
<Dialog :openVis="visible" width="30%" :title="title" @close="closeDialog" :maskClosable="false">
|
||||||
<template #container>
|
<template #container>
|
||||||
<div class="police-con">
|
<div class="police-con">
|
||||||
<div class="sub-text">有一个员工刚刚发起应急求助,请马上处理!!</div>
|
<div class="sub-text"> 有一个员工刚刚发起应急求助,请马上处理!</div>
|
||||||
<div class="con-item">
|
<div class="con-item">
|
||||||
<span>姓名:</span>
|
<span>姓名:</span>
|
||||||
<span>{{ userInfo?.realName }}</span>
|
<span>{{ userInfo?.realName }}</span>
|
||||||
@@ -13,25 +13,41 @@
|
|||||||
<span>{{ userInfo?.age }}</span>
|
<span>{{ userInfo?.age }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="con-item">
|
<div class="con-item">
|
||||||
<span>二级单位:</span>
|
<span>单位:</span>
|
||||||
|
<span>{{ userInfo?.departName }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="con-item">
|
||||||
|
<span>部门:</span>
|
||||||
<span>{{ userInfo?.orgName }}</span>
|
<span>{{ userInfo?.orgName }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="btn-box">
|
||||||
|
<a-button type="primary" @click="confirmHelp">确认</a-button>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
</template>
|
</template>
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import Dialog from '/@/components/dialog/Dialog.vue';
|
import Dialog from '/@/components/dialog/Dialog.vue';
|
||||||
import { useDialog } from '/@/views/components/dialogHooks.ts';
|
import { useDialog } from '/@/views/components/dialogHooks.ts';
|
||||||
import { watch, ref } from 'vue';
|
import { watch, ref, Ref } from 'vue';
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
record: Object,
|
record: Object,
|
||||||
});
|
});
|
||||||
const userInfo = ref({});
|
const emit = defineEmits(['confirmHelp']);
|
||||||
|
|
||||||
|
interface UserInfo {
|
||||||
|
realName?: string;
|
||||||
|
age?: string;
|
||||||
|
departName?: string;
|
||||||
|
orgName?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const userInfo: Ref<UserInfo> = ref({});
|
||||||
watch(
|
watch(
|
||||||
() => props.record,
|
() => props.record,
|
||||||
(nVal) => {
|
(nVal: UserInfo) => {
|
||||||
userInfo.value = nVal;
|
userInfo.value = nVal;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@@ -40,6 +56,12 @@
|
|||||||
defineExpose({
|
defineExpose({
|
||||||
openDialog,
|
openDialog,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
//确认帮助
|
||||||
|
function confirmHelp() {
|
||||||
|
closeDialog();
|
||||||
|
emit('confirmHelp', userInfo.value);
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
<style scoped lang="less">
|
<style scoped lang="less">
|
||||||
.police-con {
|
.police-con {
|
||||||
@@ -51,6 +73,7 @@
|
|||||||
.sub-text {
|
.sub-text {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
margin-left: 8px;
|
margin-left: 8px;
|
||||||
|
font-size: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.con-item {
|
.con-item {
|
||||||
@@ -72,4 +95,9 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.btn-box {
|
||||||
|
text-align: center;
|
||||||
|
margin-top: 2%;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
+152
-4
@@ -14,7 +14,7 @@
|
|||||||
<three-l :key="tabState" :setting="setting" :refreshState="refreshState" />
|
<three-l :key="tabState" :setting="setting" :refreshState="refreshState" />
|
||||||
</div>
|
</div>
|
||||||
<div class="body-d-middle">
|
<div class="body-d-middle">
|
||||||
<china-map />
|
<china-map :showHospitalList="showHospitalList" :hospitalList="hospitalList" @clearHospitalList="clearHospitalList"></china-map>
|
||||||
</div>
|
</div>
|
||||||
<div class="body-d-right">
|
<div class="body-d-right">
|
||||||
<one-r :setting="setting" :refreshState="refreshState" />
|
<one-r :setting="setting" :refreshState="refreshState" />
|
||||||
@@ -24,12 +24,12 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="bottom-d"></div>
|
<div class="bottom-d"></div>
|
||||||
<EmployeeDialog ref="employeeDialogRef" :record="socketData" />
|
<EmployeeDialog ref="employeeDialogRef" :record="socketData" />
|
||||||
<EmergencyDialog ref="emergencyDialogRef" :record="emergencyData" />
|
<EmergencyDialog ref="emergencyDialogRef" :record="emergencyData" @confirmHelp="confirmHelp" />
|
||||||
<PhoneDialog ref="phoneDialogRef" :title="phoneDialogTitle" :useForm="true" :setting="setting" :phoneType="phoneType" />
|
<PhoneDialog ref="phoneDialogRef" :title="phoneDialogTitle" :useForm="true" :setting="setting" :phoneType="phoneType" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { onUnmounted, ref, watch, nextTick, onMounted } from 'vue';
|
import { nextTick, onMounted, onUnmounted, ref, watch } from 'vue';
|
||||||
import OneL from '/@/components/body-d-left/oneL.vue';
|
import OneL from '/@/components/body-d-left/oneL.vue';
|
||||||
import TwoL from '/@/components/body-d-left/twoL.vue';
|
import TwoL from '/@/components/body-d-left/twoL.vue';
|
||||||
import ThreeL from '/@/components/body-d-left/threeL.vue';
|
import ThreeL from '/@/components/body-d-left/threeL.vue';
|
||||||
@@ -46,6 +46,7 @@
|
|||||||
import { DataType, getSettings } from '/@/utils/settings.ts';
|
import { DataType, getSettings } from '/@/utils/settings.ts';
|
||||||
import { ServerDetailType } from '/@/components/body-d-left/bodyLeftHooks.ts';
|
import { ServerDetailType } from '/@/components/body-d-left/bodyLeftHooks.ts';
|
||||||
import PhoneDialog from '/@/views/components/phoneDialog/phoneDialog.vue';
|
import PhoneDialog from '/@/views/components/phoneDialog/phoneDialog.vue';
|
||||||
|
import { Map } from '/@/assets/mapUtils/map.ts';
|
||||||
|
|
||||||
const { refreshState } = useRefresh(DEFAULT_TIME);
|
const { refreshState } = useRefresh(DEFAULT_TIME);
|
||||||
const dayTimeO = ref();
|
const dayTimeO = ref();
|
||||||
@@ -85,6 +86,60 @@
|
|||||||
// 健康监测工具报警
|
// 健康监测工具报警
|
||||||
const { socketData, setSocketData, closeSocket } = useSocket(basicPath + `${DataType.xian}`);
|
const { socketData, setSocketData, closeSocket } = useSocket(basicPath + `${DataType.xian}`);
|
||||||
const employeeDialogRef = ref();
|
const employeeDialogRef = ref();
|
||||||
|
|
||||||
|
//模拟开启
|
||||||
|
function employeeDialogOpen() {
|
||||||
|
setEmergencyData({
|
||||||
|
userId: '001a9878fdaf401c844a03e2fc7c370b',
|
||||||
|
realName: '曹燕',
|
||||||
|
lon: 108.872442,
|
||||||
|
lat: 34.189956,
|
||||||
|
sex: 2,
|
||||||
|
age: 40,
|
||||||
|
orgCode: 'A01A33A05',
|
||||||
|
orgName: '地质研究所',
|
||||||
|
hospitalList: [
|
||||||
|
{
|
||||||
|
id: null,
|
||||||
|
name: '交大一附院',
|
||||||
|
lat: 34.22,
|
||||||
|
lon: 108.94,
|
||||||
|
type: null,
|
||||||
|
ambulance: null,
|
||||||
|
docNum: null,
|
||||||
|
nurseNum: null,
|
||||||
|
expertNum: null,
|
||||||
|
distance: null,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: null,
|
||||||
|
name: '高新医院',
|
||||||
|
lat: 34.23,
|
||||||
|
lon: 108.88,
|
||||||
|
type: null,
|
||||||
|
ambulance: null,
|
||||||
|
docNum: null,
|
||||||
|
nurseNum: null,
|
||||||
|
expertNum: null,
|
||||||
|
distance: null,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: null,
|
||||||
|
name: '人民医院',
|
||||||
|
lat: 34.24,
|
||||||
|
lon: 108.93,
|
||||||
|
type: null,
|
||||||
|
ambulance: null,
|
||||||
|
docNum: null,
|
||||||
|
nurseNum: null,
|
||||||
|
expertNum: null,
|
||||||
|
distance: null,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
emergencyDialogRef.value.openDialog();
|
||||||
|
}
|
||||||
|
|
||||||
watch(socketData, (nVal) => {
|
watch(socketData, (nVal) => {
|
||||||
if (nVal) {
|
if (nVal) {
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
@@ -94,7 +149,7 @@
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
//应急求助 + `/${DataType.xian}`
|
//应急求助
|
||||||
const { socketData: emergencyData, setSocketData: setEmergencyData, closeSocket: closeSocket2 } = useSocket(emergencyPath + `${DataType.xian}`);
|
const { socketData: emergencyData, setSocketData: setEmergencyData, closeSocket: closeSocket2 } = useSocket(emergencyPath + `${DataType.xian}`);
|
||||||
const emergencyDialogRef = ref();
|
const emergencyDialogRef = ref();
|
||||||
|
|
||||||
@@ -137,6 +192,99 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const hospitalList = ref([]); //医院列表
|
||||||
|
const showHospitalList = ref(false);
|
||||||
|
|
||||||
|
//确认应急求助
|
||||||
|
async function confirmHelp(data) {
|
||||||
|
if (!data.lon) return;
|
||||||
|
hospitalList.value = [];
|
||||||
|
//用户的坐标信息
|
||||||
|
let point = [data.lon, data.lat];
|
||||||
|
if (data.hospitalList.length > 0) {
|
||||||
|
try {
|
||||||
|
hospitalList.value = await getHospitalList(data.hospitalList, point);
|
||||||
|
showHospitalList.value = true;
|
||||||
|
} catch (error) {
|
||||||
|
console.log('获取医院列表时出错:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Map.getAddressByPoint(point).then((res) => {
|
||||||
|
const option = {
|
||||||
|
lon: data.lon,
|
||||||
|
lat: data.lat,
|
||||||
|
position: point,
|
||||||
|
name: data.realName,
|
||||||
|
address: res,
|
||||||
|
sex: data.sex,
|
||||||
|
};
|
||||||
|
Map.createEmergencyMarker(option);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getHospitalList(hospitalList, userLocation) {
|
||||||
|
const hospitalPromises = hospitalList.map(async (item) => {
|
||||||
|
item.distance = getDistance(userLocation, [item.lon, item.lat]);
|
||||||
|
const { time, routeList } = await getDriveTime(userLocation, [item.lon, item.lat]);
|
||||||
|
item.driveTime = time;
|
||||||
|
item.routeList = routeList;
|
||||||
|
item.userLocation = userLocation;
|
||||||
|
return item; // 返回更新后的医院条目
|
||||||
|
});
|
||||||
|
return Promise.all(hospitalPromises);
|
||||||
|
}
|
||||||
|
|
||||||
|
// function getHospitalList(hospitalList, userLocation) {
|
||||||
|
// hospitalList.forEach(async (item) => {
|
||||||
|
// item.distance = getDistance(userLocation, [item.lon, item.lat]);
|
||||||
|
// let { time, routeList } = await getDriveTime(userLocation, [item.lon, item.lat]);
|
||||||
|
// item.driveTime = time;
|
||||||
|
// item.routeList = routeList;
|
||||||
|
// item.userLocation = userLocation;
|
||||||
|
// console.log('time', item.driveTime);
|
||||||
|
// });
|
||||||
|
// return hospitalList;
|
||||||
|
// }
|
||||||
|
|
||||||
|
function getDistance(userLocation, destination) {
|
||||||
|
const value = Map.getDistance(userLocation, destination);
|
||||||
|
return value > 1000 ? (value / 1000).toFixed(2) + 'km' : value + 'm';
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getDriveTime(userLocation, destination) {
|
||||||
|
let { time, steps } = await Map.getDriveTime(userLocation, destination);
|
||||||
|
let h,
|
||||||
|
m,
|
||||||
|
s = 0;
|
||||||
|
if (time <= 60) {
|
||||||
|
return {
|
||||||
|
time: `${s}秒`,
|
||||||
|
routeList: steps,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (time > 60 && time < 3600) {
|
||||||
|
m = Math.floor(time / 60);
|
||||||
|
return {
|
||||||
|
time: `${m}分`,
|
||||||
|
routeList: steps,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (time >= 3600) {
|
||||||
|
h = Math.floor(time / 3600);
|
||||||
|
m = Math.floor((time - h * 3600) / 60);
|
||||||
|
return {
|
||||||
|
time: `${h}小时${m}分`,
|
||||||
|
routeList: steps,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearHospitalList() {
|
||||||
|
hospitalList.value = [];
|
||||||
|
showHospitalList.value = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//地图上绘制
|
||||||
// function test() {
|
// function test() {
|
||||||
// let nVal = {
|
// let nVal = {
|
||||||
// watchNo: 'CRFTQ22C03000080',
|
// watchNo: 'CRFTQ22C03000080',
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
export interface HospitalList {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
lat: number;
|
||||||
|
lon: number;
|
||||||
|
distance: string;
|
||||||
|
time: string;
|
||||||
|
routeList: any[];
|
||||||
|
|
||||||
|
[key: string]: any;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user