564 lines
21 KiB
Vue
564 lines
21 KiB
Vue
<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>
|