Files
xj-admin/src/views/emergency/aed/deviceMap/index.vue
T
weixin 98a2680b63 update
1.应急就医-aed组网迁移
2025-11-27 13:30:19 +08:00

564 lines
21 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 style="width: 100%; height: 100%; overflow: hidden">
<div class="devive-map">
<div style="position: absolute; top: 20px; left: 20px; z-index: 10">
<div class="map-search-box">
<div>
<a-select
v-model:value="intValue"
allow-clear
show-search
label-in-value
placeholder="请输入位置信息"
class="search-select"
style="width: 400px"
:default-active-first-option="false"
:show-arrow="true"
:filter-option="false"
:not-found-content="null"
@search="onSearch"
:options="panelList"
@change="handleChange"
/>
<!-- <a-button type="primary" @click="onButtonSearch"> 搜索 </a-button>-->
<span style="cursor: pointer; margin-left: 5px" @click="onButtonSearch">搜索</span>
</div>
</div>
<div style="margin-top: 5px; display: flex">
<div class="d-num">设备总数量{{ pointerArray.length }}</div>
</div>
</div>
<div id="panel" style="display: none"></div>
<div class="list" id="list"> </div>
<div class="map" id="container"></div>
<a-button class="recently" type="primary" @click="findRecently"> 查找距离最近 </a-button>
<div class="right-win" v-if="showWindow">
<Icon
icon="ant-design:close-circle-outlined"
style="position: absolute; right: 40px; cursor: pointer"
@click="() => (showWindow = false)"
/>
<div class="bottom-top">
<div class="top-title"> <img :src="aedP" alt="" /> {{ showWindowInfo?.name }} </div>
<div class="top-title-bottom"> 距离{{ showWindowInfo?.dis ? showWindowInfo?.dis : 0 }}m </div>
<div class="top-title-describe"> {{ showWindowInfo?.installAddress ? showWindowInfo?.installAddress : '无' }} </div>
<div class="table-list">
<div class="label-d"> 设备厂家 </div>
<div class="value-d"> {{ showWindowInfo?.mfrsName || '-' }} </div>
</div>
<div class="table-list">
<div class="label-d"> 设备型号 </div>
<div class="value-d">
{{ showWindowInfo?.mfrsName || '-' }}
</div>
</div>
<div class="table-list">
<div class="label-d"> 设备状态 </div>
<div class="value-d"> {{ showWindowInfo?.aedStatus }} </div>
</div>
<div class="table-list">
<div class="label-d"> 所属部门 </div>
<div class="value-d"> {{ showWindowInfo?.departName }} </div>
</div>
<div class="table-list">
<div class="label-d"> 管理单位 </div>
<div class="value-d"> {{ showWindowInfo?.controlOrgName }} </div>
</div>
<div class="table-list">
<div class="label-d"> 管理人 </div>
<div class="value-d"> {{ showWindowInfo?.manageUserName }} </div>
</div>
<div class="table-list">
<div class="label-d"> 管理人电话 </div>
<div class="value-d"> {{ showWindowInfo?.manageUserMobile }} </div>
</div>
<div class="table-list">
<div class="label-d"> 负责人1 </div>
<div class="value-d"> {{ showWindowInfo?.chargeFirst }} </div>
</div>
<div class="table-list">
<div class="label-d"> 负责人1电话 </div>
<div class="value-d"> {{ showWindowInfo?.chargeFirstMobile }} </div>
</div>
<div class="table-list">
<div class="label-d"> 负责人2 </div>
<div class="value-d"> {{ showWindowInfo?.chargeSecond || '-' }} </div>
</div>
<div class="table-list">
<div class="label-d"> 负责人2电话 </div>
<div class="value-d"> {{ showWindowInfo?.chargeSecondMobile || '-' }} </div>
</div>
<div class="table-list">
<div class="label-d"> 覆盖人数 </div>
<div class="value-d"> {{ showWindowInfo?.coverUserNum }} </div>
</div>
<div class="table-list">
<div class="label-d"> 设备状态 </div>
<div class="value-d"> {{ showWindowInfo?.electrodeIsOk_dictText || '-' }} </div>
</div>
<!-- <div class="table-list">-->
<!-- <div class="label-d"> 设备电量 </div>-->
<!-- <div class="value-d"> {{ showWindowInfo?.electricQuantity }} </div>-->
<!-- </div>-->
<!-- <div class="table-list" v-if="showWindowInfo.departCode.indexOf('A01A09') !== -1">-->
<!-- <div class="label-d"> 更新日期 </div>-->
<!-- <div class="value-d"> {{ showWindowInfo?.electrodeSheetValidTime }} </div>-->
<!-- </div>-->
</div>
<div class="bottom-bottom">
<a-button type="primary" @click="showPDF(showWindowInfo?.disclaimer)">免责声明</a-button>
<a-button type="primary" @click="showAny('1')">操作视频</a-button>
<a-button type="primary" @click="showAny('2')">操作图片</a-button>
<!-- <a-button-->
<!-- type="primary"-->
<!-- :disabled="-->
<!-- !(-->
<!-- (showWindowInfo?.cameraNum || showWindowInfo?.cameraNum === 0) &&-->
<!-- (showWindowInfo.cameraCode || showWindowInfo.cameraCode === 0)-->
<!-- )-->
<!-- "-->
<!-- @click="showAny('3')"-->
<!-- >摄像头监控</a-button-->
<!-- >-->
</div>
</div>
</div>
<Image
:width="200"
:style="{ display: 'none' }"
:preview="{
visible,
onVisibleChange: setVisible,
}"
:src="getFileAccessHttpUrl(showWindowInfo?.operateInstruction)"
/>
<VideoPreview :url="getFileAccessHttpUrl(showWindowInfo?.disclaimer)" ref="vp1" />
<VideoPreview :url="getFileAccessHttpUrl(showWindowInfo?.operateVideo)" ref="vp2" />
<location-modal @register="registerModal" />
</div>
</template>
<script setup name="aed-map" lang="ts">
import { onMounted, ref } from 'vue';
import AMapLoader from '@amap/amap-jsapi-loader';
import { debounce } from 'lodash-es';
import aedP from '/@/assets/images/aed/aedp.png';
import positionIcon from '/@/assets/images/aed/aed.png';
import MarkersIcon from '/@/assets/images/aed/point.png';
import { listApi } from '/@/views/emergency/aed/deviceMap/index';
import mapKey from '/@/utils/mapKey';
import { getShort } from '/@/utils/mapUtils';
import { Image, message } from 'ant-design-vue';
import { getFileAccessHttpUrl } from '/@/utils/common/compUtils';
import VideoPreview from '/@/components/Video/VideoPreview.vue';
import { useModal } from '/@/components/Modal';
import LocationModal from '/@/views/emergency/aed/location/compoents/locationModal.vue';
import { Dict } from '/@/utils/cache/dict';
let SelfMap = null;
let BasicMap = null;
let overlayGroup = null;
const clickMapLngLat = ref([]);
const showWindow = ref(false);
const showWindowInfo = ref({});
const intValue = ref('');
let placeSearch = ref();
let panelList = ref([]);
let pointerArray = ref([]);
let defaultMarker = null;
const [registerModal, { openModal }] = useModal();
onMounted(() => {
initMap();
});
const visible = ref<boolean>(false);
const setVisible = (value): void => {
visible.value = value;
};
const showIframe = async () => {
openModal(true, {
record: { ...showWindowInfo.value, orgName: showWindowInfo.value.departName },
type: '1',
});
};
function initMap() {
AMapLoader.reset();
AMapLoader.load({
key: mapKey,
version: '2.0',
plugins: ['AMap.Geocoder'], // 需要使用的的插件列表,如比例尺'AMap.Scale'等
})
.then((AMap) => {
SelfMap = AMap;
//设置地图容器id
BasicMap = new AMap.Map('container', {
viewMode: '3D', //是否为3D地图模式
// zoom: 11, //初始化地图级别
// center: [108.95, 34.33], //初始化地图中心点位置
resizeEnable: true,
});
BasicMap.on('click', clickMap);
// 注册搜索插件
bindSearch(AMap);
handleMarker({});
})
.catch((e) => {
console.log(e);
});
}
function clickMap(e) {
clickMapLngLat.value = [e.lnglat.lng, e.lnglat.lat];
addDefault(e.lnglat.lng, e.lnglat.lat, false, false);
intValue.value = '';
}
function setShowInfoDis(longitude: any, latitude: any) {
if (clickMapLngLat.value.length === 0) return (showWindowInfo.value['dis'] = 0);
const result = parseInt(SelfMap.GeometryUtil.distance(clickMapLngLat.value, [longitude, latitude]));
showWindowInfo.value['dis'] = Math.abs(result);
}
/*
** 获取区域对角线的两点坐标,即这个区域内的最小坐标值和最大坐标值
* @param pointerArray [[a,b],[c,d]]* @return Array {min:number[a,b], max:number[c,d]}
* */
function getMaxBoundsPointer(pointerArray) {
let lngArray = pointerArray.map((item) => item[0]);
let latArray = pointerArray.map((item) => item[1]);
return { min: [Math.min(...lngArray), Math.min(...latArray)], max: [Math.max(...lngArray), Math.max(...latArray)] };
}
/**
* @Description:输入框查询位置
* @date 2023/7/8
*/
function onSearch(value: string) {
//关键字查询
if (value) {
intValue.value = value;
searchRes(value);
}
}
const searchRes = debounce((value) => {
placeSearch.value.search(value, (status: string, result: any) => {
if (status === 'complete') {
panelList.value = result.poiList.pois.map((item) => ({ label: item.name, value: item.name, ...item }));
}
});
}, 500);
function onButtonSearch() {
onSearch(intValue.value);
}
/**
* @Description下拉框选择事件
* @date 2023/8/30
*/
function handleChange(value: any, option: any) {
if (option) {
setMapZoom(16);
let params = {
longitude: option.location.lng, // 经度
latitude: option.location.lat, // 纬度
radiusRange: '50', // 半径范围
};
setShowInfoDis(option.location.lng, option.location.lat);
addDefault(option.location.lng, option.location.lat);
handleMarker(params);
} else {
handleMarker({});
setMapZoom(12);
removeDefault();
}
}
function handleMarker(params) {
// pointerArray.value = dataData;
// let maxLocations = getMaxBoundsPointer(dataData.map((item) => [item.longitude, item.latitude]));
// hanleBounds(maxLocations);
// addMarker(dataData);
listApi(params).then((res) => {
// res = dataData;
if (res.length > 0) {
pointerArray.value = res.filter((item) => !!item.longitude && !!item.latitude);
let maxLocations = getMaxBoundsPointer(pointerArray.value.map((item) => [item.longitude, item.latitude]));
hanleBounds(maxLocations);
addMarker(pointerArray.value);
} else {
removeMarker();
// message.warn('该位置附近暂无设备!');
}
});
}
function hanleBounds(maxLocations) {
let lngGap = (maxLocations.max[0] - maxLocations.min[0]) / 4;
let latGap = (maxLocations.max[1] - maxLocations.min[1]) / 4;
let min = new SelfMap.LngLat(maxLocations.min[0] - lngGap, maxLocations.min[1] - latGap);
let max = new SelfMap.LngLat(maxLocations.max[0] + lngGap, maxLocations.max[1] + latGap);
if (pointerArray.value.length > 1) {
let bounds = new SelfMap.Bounds(min, max);
BasicMap.setBounds(bounds);
}
// 2. 一个点时,将其作为中心点
else if (pointerArray.value.length === 1) {
let pointValue = pointerArray.value;
let centerLngLat = new SelfMap.LngLat(pointValue[0].longitude, pointValue[0].latitude);
BasicMap.setCenter(centerLngLat); // 设置地图中心点坐标
}
}
// 加载默认图标
function addDefault(longitude, latitude, setCenter = true, cheap = true) {
removeDefault();
let defaultIcon = new SelfMap.Icon({
image: MarkersIcon,
size: new SelfMap.Size(50, 58), //图标大小
imageSize: new SelfMap.Size(50, 58),
});
defaultMarker = new SelfMap.Marker({
icon: defaultIcon,
position: [longitude, latitude],
offset: cheap ? new SelfMap.Pixel(-18, -32) : new SelfMap.Pixel(-26, -29),
// offset: cheap ? [-18, -32] : [-24, -30],
// maxZoom: 14,
});
defaultMarker.setMap(BasicMap);
if (setCenter) BasicMap.setCenter([longitude, latitude], true);
}
function addMarker(position) {
removeMarker();
if (position.length === 0) {
return;
}
const icon = new SelfMap.Icon({
image: positionIcon,
size: new SelfMap.Size(50, 58), //图标大小
imageSize: new SelfMap.Size(50, 58),
});
overlayGroup = new SelfMap.OverlayGroup();
position.map((item) => {
let marker = new SelfMap.Marker({
icon: icon,
position: [item.longitude, item.latitude],
offset: new SelfMap.Pixel(-18, -32),
maxZoom: 14,
});
marker.on('click', () => {
showWindow.value = true;
showWindowInfo.value = item;
setShowInfoDis(item.longitude, item.latitude);
});
overlayGroup.setMap(BasicMap);
overlayGroup.addOverlay(marker);
});
}
function removeDefault() {
defaultMarker && BasicMap.remove(defaultMarker);
}
function removeMarker() {
if (overlayGroup) {
BasicMap.remove(overlayGroup);
overlayGroup.setMap(null);
overlayGroup = null;
}
}
function setMapZoom(zoom: number) {
if (zoom) BasicMap && (BasicMap as any).setZoom(zoom);
}
// 搜索插件
function bindSearch(AMap) {
AMap.plugin(['AMap.PlaceSearch'], function () {
//构造地点查询类
placeSearch.value = new AMap.PlaceSearch({
pageSize: 20, // 单页显示结果条数
pageIndex: 1, // 页码
panel: 'panel', // 结果列表将在此容器中进行展示。
autoFitView: false, // 是否自动调整地图视野使绘制的 Marker点都处于视口的可见范围
});
});
}
function findRecently() {
if (clickMapLngLat.value.length === 0) return message.info('请先选择当前所在位置');
const item = getShort(SelfMap, clickMapLngLat.value, pointerArray.value);
showWindowInfo.value = item;
setShowInfoDis(item.longitude, item.latitude);
BasicMap.setCenter([item.longitude, item.latitude]); // 设置地图中心点坐标
showWindow.value = true;
}
const vp1 = ref();
const vp2 = ref();
function showAny(type: string) {
switch (type) {
case '0':
// vp1.value.showPreview();
break;
case '1':
vp2.value.showPreview();
break;
case '2':
setVisible(true);
break;
case '3':
showIframe();
break;
}
}
function showPDF(v) {
window.open(getFileAccessHttpUrl(v), '_blank');
}
function getName(val, list) {
if (!val && val !== 0) return '';
for (let i = 0; i < list.length; i++) {
if (list[i].value == val) {
return list[i].label;
}
}
return '-';
}
</script>
<style scoped lang="less">
.devive-map {
width: 100%;
height: calc(100% + 80px);
.map {
width: 100%;
height: 100%;
}
.map-search-box {
//position: absolute;
//top: 20px;
//left: 20px;
//z-index: 10;
box-shadow: 1px 2px 1px rgba(0, 0, 0, 0.15);
padding: 5px 10px;
background-color: #ffffff;
}
.panel-list {
position: absolute;
background-color: white;
max-height: 90%;
overflow-y: auto;
top: 70px;
left: 13px;
width: 280px;
z-index: 9;
.panel-item {
padding: 10px 5px;
color: #999;
line-height: 20px;
}
}
}
.table-list {
display: flex;
padding: 5px;
justify-content: space-between;
}
.recently {
position: absolute;
right: 50px;
top: 20px;
}
.right-win {
position: absolute;
width: 420px;
height: calc(100% - 100px);
top: 70px;
right: 50px;
background-color: #ffffff;
padding: 10px 20px;
}
.top-title {
display: flex;
align-items: flex-end;
padding-bottom: 5px;
font-weight: bold;
img {
width: 30px;
height: 30px;
margin-right: 5px;
}
}
.top-title-bottom {
padding-bottom: 5px;
font-weight: bold;
}
.top-title-describe {
position: relative;
padding-bottom: 10px;
color: #6f6f6f;
&:after {
position: absolute;
width: 100%;
height: 1px;
content: '';
bottom: 0;
left: 0;
background: #c3c3c3;
}
}
.bottom-top {
padding-top: 10px;
height: calc(100% - 50px);
overflow: auto;
}
.bottom-bottom {
height: 50px;
display: flex;
justify-content: space-around;
align-items: center;
}
.label-d {
color: #6f6f6f;
}
.value-d {
font-weight: bold;
}
:deep(.ant-select-selector) {
border-radius: 20px !important;
background-color: #f7f7f9 !important;
outline: none;
}
.d-num {
background-color: rgba(4, 4, 4, 0.41);
padding: 3px 10px;
border-radius: 10px;
color: #ffffff;
font-size: 12px;
}
</style>
<style lang="less">
.dialog-conMap {
padding: 10px;
width: 380px;
.item {
padding: 8px 0;
display: flex;
span:nth-child(1) {
display: inline-block;
width: 30%;
text-align: right;
}
span:nth-child(2) {
display: inline-block;
width: 70%;
text-align: left;
}
}
}
</style>