1.应急就医-aed组网迁移
This commit is contained in:
2025-11-27 13:30:19 +08:00
parent ef852d13d6
commit 98a2680b63
21 changed files with 3022 additions and 2145 deletions
+563
View File
@@ -0,0 +1,563 @@
<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>