解决冲突

This commit is contained in:
zhaotiantian
2023-12-08 18:28:19 +08:00
24 changed files with 856 additions and 243 deletions
+2
View File
@@ -13,8 +13,10 @@
"@amap/amap-jsapi-loader": "^1.0.1",
"axios": "^1.6.2",
"dayjs": "^1.11.10",
"mitt": "^3.0.1",
"path": "^0.12.7",
"prettier": "^3.1.0",
"qs": "^6.11.2",
"vue": "^3.3.4",
"vue-router": "^4.2.5",
"vue3-seamless-scroll": "^2.0.1"
+11 -1
View File
@@ -1,5 +1,4 @@
import axios from 'axios';
import requestURL from './url';
import { getAppEnvConfig } from '../utils/env.ts';
const { VITE_GLOB_API_URL } = getAppEnvConfig();
@@ -30,3 +29,14 @@ export const get = (url, params) => {
});
});
};
export const post = (url: string, params: any) =>
new Promise((resolve, reject) => {
service
.post(url, params)
.then((res: any) => {
resolve(res.data);
})
.catch((err: any) => {
reject(err);
});
});
Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

+59
View File
@@ -0,0 +1,59 @@
//高德信息弹窗
.amap-info-contentContainer {
.amap-content-body {
background: #00152B;
border: 1px solid #129BFF;
.amap-lib-infowindow {
background: #00152B;
.amap-lib-infowindow-title {
color: #fff;
border-bottom: 1px solid #129BFF;
padding: 4px 0;
}
.amap-lib-infowindow-content {
color: #fff;
padding: 4px 0;
}
}
}
}
/*地图元素样式---start*/
//marker
.amap-marker-label {
padding: 0;
}
.alarm-content {
background-color: #263d5e;
color: red;
font-size: 14px;
padding: 7px 10px;
border: 1px solid #1b7ef2;
position: relative;
.close-btn {
position: absolute;
top: -6px;
right: -8px;
width: 15px;
height: 15px;
font-size: 12px;
background: #ccc;
border-radius: 50%;
color: #fff;
text-align: center;
line-height: 15px;
box-shadow: -1px 1px 1px rgba(10, 10, 10, 0.2);
}
.close-btn:hover {
background: #666;
}
}
/*地图元素样式---end*/
+7 -2
View File
@@ -1,3 +1,5 @@
@import "./amap-item";
.type-time {
padding-right: 4%;
padding-top: 3%;
@@ -55,9 +57,12 @@
background-color: rgba(0, 0, 0, 0.3);
}
//直升机
.helicopter {
width: 60px;
height: 60px;
background: url('../img/helicopter.png') no-repeat;
height: 37px;
background: url("../img/helicopter.png") no-repeat;
background-size: 100% 100%;
margin-bottom: 4px;
}
+25 -3
View File
@@ -6,7 +6,7 @@ import { mapIcon1, mapIcon2, mapIcon3, mapIcon4 } from '/@/components/chinaMap/c
export function createIcon({ icon, iconSize, imageSize }) {
return new AMap.Icon({
// 图标尺寸
size: new AMap.Size(21, 44),
size: new AMap.Size(...iconSize),
// 图标的取图地址
image: icon,
// 图标所用图片大小
@@ -19,8 +19,8 @@ export function createIcon({ icon, iconSize, imageSize }) {
* @type 1:油田医院,2:合作医院,3:医疗点
*/
export function typeToIcon(type: string) {
const iconSize = [21, 44];
const imageSize = [21, 44];
const iconSize = [18, 44];
const imageSize = [18, 44];
if (type == '1') {
return createIcon({ icon: mapIcon1, iconSize, imageSize });
}
@@ -34,3 +34,25 @@ export function typeToIcon(type: string) {
return createIcon({ icon: mapIcon3, iconSize, imageSize });
}
}
/**
*
*/
export function createCircle(option) {
const { lon, lat } = option;
return new AMap.Circle({
center: [lon, lat],
radius: 100000, //半径
borderWeight: 3,
strokeColor: '#FF33FF',
// strokeWeight: 6,
strokeOpacity: 0.2,
fillOpacity: 0.4,
strokeStyle: 'dashed',
strokeDasharray: [10, 10],
// 线样式还支持 'dashed'
fillColor: '#1791fc',
zIndex: 50,
...option,
});
}
+133 -116
View File
@@ -1,19 +1,27 @@
import china from '/@/assets/lngLat/china.ts';
import chinaInner from '/@/assets/lngLat/chinaInner.ts';
import { typeToIcon } from '/@/assets/mapUtils/commonFun.ts';
import { helicopter, mapbg } from '/@/components/chinaMap/chinaHooks.ts';
import { createCircle, createIcon, typeToIcon } from '/@/assets/mapUtils/commonFun.ts';
import { aircraftContent, alarmIcon } from '/@/components/chinaMap/chinaHooks.ts';
export const defaultOption = {
center: [106.3, 37.87], //地图中心点
zoom: 4.3, //地图显示的缩放级别
mapStyle: 'darkblue', //地图样式
};
export const mapPlugin = ['AMap.ToolBar', 'AMap.Driving', 'AMap.GeoJSON', 'AMap.MarkerCluster'];
export const mapPlugin = ['AMap.ToolBar', 'AMap.Driving', 'AMap.GeoJSON', 'AMap.MarkerCluster', 'AMap.PlaceSearch'];
export const Map = {
map: null,
overlayGroup: null, // 地图中marker
aircraftGroup: null, //直升机marker
cluster: null,
circleGroup: null, //直升机背景圆
cluster: null, //直升机marker点聚合图层
airMarkerList: [], //直升机markerList
placeSearch: null, //地点查询
marker: null, // 健康终端报警
/**
* @desc 地图舒适化
* @param el dom元素 id选择器
* @param option 地图配置
*/
initMap({ el, option }) {
const { zoom, center, mapStyle } = { ...defaultOption, ...option };
//基本地图加载
@@ -28,8 +36,9 @@ export const Map = {
this.drawInnerLine();
this.drawOutLine();
});
},
// 中国外层边界线
} /**
* @desc 中国外层边界线
*/,
drawOutLine() {
let geojson = new AMap.GeoJSON({
geoJSON: china,
@@ -46,8 +55,9 @@ export const Map = {
},
});
this.map.add(geojson);
},
// 中国内层边界线
} /**
* @desc 中国内层边界线
*/,
drawInnerLine() {
let geojson = new AMap.GeoJSON({
geoJSON: chinaInner,
@@ -60,25 +70,39 @@ export const Map = {
},
});
this.map.add(geojson);
},
// 创建覆盖物组,用于批量处理点 (控制显隐,点击事件等)
createOverlay(result) {
}, // 清空当前地图上除了边界线的覆盖物
removeOverlayGroup() {
this.clearAircraft();
this.clearOverlay();
this.clearAirRange();
this.clearMarker(); //
if (this.airMarkerList.length > 0) {
for (let i = 0; i < this.airMarkerList.length; i++) {
this.airMarkerList[i].setMap(null);
}
}
// this.map.remove(this.airMarkerList);
}, //
/**
* @desc 创建覆盖物组,用于批量处理点 (控制显隐,点击事件等)
* @param result
*/
createOverlay(result) {
this.removeOverlayGroup();
this.overlayGroup = new AMap.OverlayGroup();
for (let i = 0; i < result.length; i++) {
let { type, lon, lat } = result[i];
if (!lon || !lat || !type) {
continue;
}
// 创建一个 Icon
let startIcon = typeToIcon(type);
// 将 icon 传入 marker
let startMarker = new AMap.Marker({
// 点的坐标
position: [lon, lat],
// 指定点的icon,也可以是个url ,但这样就不能对图片进行设置了
icon: startIcon,
//启用点击事件 如果需要点击事件,必须开启
clickable: true,
//点额外携带的数据,用户自定义属性,支持JavaScript API任意数据类型
position: [lon, lat], // 指定点的icon,也可以是个url ,但这样就不能对图片进行设置了
icon: startIcon, //启用点击事件 如果需要点击事件,必须开启
clickable: true, //点额外携带的数据,用户自定义属性,支持JavaScript API任意数据类型
extData: result[i],
});
// 可以调用setExtData()设置自定义属性
@@ -100,130 +124,123 @@ export const Map = {
clearOverlay() {
if (this.overlayGroup) {
this.map.remove(this.overlayGroup);
this.overlayGroup.setMap(null);
this.overlayGroup = null;
}
},
//创建直升机marker
/**
* @desc 创建直升机marker
* @param result
*/
createAircraft(result) {
this.clearAircraft();
this.clearOverlay();
// this.aircraftGroup = new AMap.OverlayGroup();
this.removeOverlayGroup();
this.clearActiveHospital();
if (!Array.isArray(result) && result.length == 0) return;
const content = `
<div style="
display: flex;
justify-content: center;
align-items: center;
">
<div
style="width:156px;height: 52px;text-align:center; !important;">
<div style="display: flex;
justify-content: center;">
<div class="helicopter"></div>
</div>
<div style='z-index:999;background-color:#0A8B18;height:auto; '>
<h5 style="overflow: hidden;font-size:18px;color:#fff;white-space: nowrap;height:26px; line-height: 26px;padding:2px 4px;">
4009-120-999
</h5>
</div>
</div>
`;
const markers = [];
for (let i = 0; i < result.length; i++) {
let { lon, lat } = result[i];
const marker = new AMap.Marker({
// 点的坐标
position: [lon, lat],
//启用点击事件 如果需要点击事件,必须开启
clickable: true,
title: `4009-120-999`,
content: content,
offset: new AMap.Pixel(-16, -30),
});
markers.push(marker);
}
// //将组 挂载到地图上
// this.aircraftGroup.setMap(this.map);
// //将需要批量控制的点放到一个组中,通过控制组,就可以操作组内所有所有覆盖物
// this.aircraftGroup.addOverlay(markers);
if (this.cluster) {
// this.cluster.setMap(null);
this.map.remove(this.cluster);
// this.cluster = null;
this.cluster.setMap(null);
}
//使用renderCluserMarker属性实现聚合点的完全自定义绘制
let sts = [
{
url: mapbg,
size: new AMap.Size(32, 32),
offset: new AMap.Pixel(-16, -30),
textColor: '#fff',
textSize: 20,
},
];
this.createAirRange(result);
let that = this;
let arr = result.map((item) => ({ weight: 4, lnglat: [item.lon, item.lat] }));
let arr = result.map((item) => ({ weight: 4, lnglat: [item.lon, item.lat], params: item.name }));
let count = 4;
this.cluster = new AMap.MarkerClusterer(
that.map, // 地图实例
arr,
{
renderMarker: (context) => {
var factor = Math.pow(context.count / count, 1 / 18);
var div = document.createElement('div');
var Hue = 180 - factor * 180;
var bgColor = 'hsla(' + Hue + ',100%,50%,0.7)';
var fontColor = 'hsla(' + Hue + ',100%,20%,1)';
var borderColor = 'hsla(' + Hue + ',100%,40%,1)';
var shadowColor = 'hsla(' + Hue + ',100%,50%,1)';
var size = Math.round(30 + Math.pow(context.count / count, 1 / 5) * 20);
let factor = Math.pow(context.count / count, 1 / 18);
let div = document.createElement('div');
div.className = 'render-air';
let Hue = 180 - factor * 180;
let size = Math.round(30 + Math.pow(context.count / count, 1 / 5) * 20);
div.style.width = div.style.height = size + 'px';
div.innerHTML = content || context.count;
div.style.lineHeight = size + 'px';
div.style.color = fontColor;
div.innerHTML = aircraftContent() || context.count;
div.style.color = '#fff';
div.style.fontSize = '14px';
div.style.textAlign = 'center';
context.marker.setOffset(new AMap.Pixel(-size / 2, -size / 2));
context.marker.setContent(div);
this.airMarkerList.push(context.marker);
context.marker.on('click', () => {
console.log('data', context);
});
},
}
);
console.log('xx', this.cluster);
this.cluster.setMap(this.map);
this.getRange(result);
},
clearAircraft() {
if (this.aircraftGroup) {
this.map.remove(this.aircraftGroup);
this.aircraftGroup = null;
if (this.cluster) {
this.map.remove(this.cluster);
this.cluster = null;
}
} /**
* @desc 飞机范围
* @param list
*/,
createAirRange(list) {
this.removeOverlayGroup();
this.circleGroup = new AMap.OverlayGroup();
for (let i = 0; i < list.length; i++) {
let circle = createCircle({ lon: list[i].lon, lat: list[i].lat });
this.circleGroup.setMap(this.map);
this.circleGroup.addOverlay(circle);
}
},
getRange(list) {
//绘制飞机的范围图
var array = new Array(list.length);
for (var i = 0; i < list.length; i++) {
var circle = new AMap.Circle({
center: [list[i].lon, list[i].lat],
radius: 100000, //半径
borderWeight: 3,
strokeColor: '#FF33FF',
strokeOpacity: 1,
strokeWeight: 6,
strokeOpacity: 0.2,
fillOpacity: 0.4,
strokeStyle: 'dashed',
strokeDasharray: [10, 10],
// 线样式还支持 'dashed'
fillColor: '#1791fc',
zIndex: 50,
clearAirRange() {
if (this.circleGroup) {
this.map.remove(this.circleGroup);
this.circleGroup = null;
}
} /**
* @desc 地方医院医疗点显示
* @param cityCode 城市code
* @param keywords 搜索名称
*/,
createActiveHospital(cityCode: string, keywords = '医院') {
this.removeOverlayGroup();
this.clearActiveHospital();
if (!cityCode) return;
this.placeSearch = new AMap.PlaceSearch({
pageSize: 30, // 单页显示结果条数
pageIndex: 1, // 页码
city: cityCode, // 兴趣点城市
citylimit: true, //是否强制限制在设置的城市内搜索
map: this.map, // 展现结果的地图实例
// panel: 'map_hospital', // 结果列表将在此容器中进行展示。
autoFitView: false, // 是否自动调整地图视野使绘制的 Marker点都处于视口的可见范围
});
circle.setMap(this.map);
array.push(circle);
//关键字查询
this.placeSearch.search(keywords);
},
clearActiveHospital() {
if (this.placeSearch) {
// debugger;
this.placeSearch.clear();
this.placeSearch = null;
}
},
createMarker(option, callbck: () => {}) {
this.clearMarker();
let { lon, lat, zoom = 12 } = option;
const icon = createIcon({ icon: alarmIcon, iconSize: [36, 42], imageSize: [36, 42] });
this.marker = new AMap.Marker({
position: [lon, lat],
icon: icon,
offset: new AMap.Pixel(0, 0), //设置偏移量
label: {
content: `<div class="alarm-content" >
<div>${option?.realName},${option?.eventType_dictText}</div>
</div>`,
offset: new AMap.Pixel(0, -9),
direction: 'top',
},
});
this.map.add(this.marker);
this.map.setZoomAndCenter(zoom, [lon, lat]);
this.marker.on('click', callbck);
},
clearMarker() {
if (this.marker) {
this.marker?.setMap(null);
this.marker = null;
}
return array;
},
};
@@ -1,6 +1,43 @@
import { ref } from 'vue';
import * as echarts from 'echarts';
export function useStatistics() {
const statistics = ref([
{
name: '最新呼入电话',
value: '',
key: 'gross',
},
{
name: '最新受理电话',
value: '',
key: 'alerdyreceive',
},
{
name: '突发重大伤病应急',
value: '',
key: 'jqqz',
},
{
name: '重大伤病应急就医',
value: '',
key: 'dbjy',
},
]);
function setStatistics(data = {}) {
statistics.value.forEach((item) => {
const { key } = item;
item.value = data[key];
});
}
return {
statistics,
setStatistics,
};
}
export const timeTab = [
{
name: '全部',
+11 -12
View File
@@ -12,9 +12,9 @@
>
</div>
<div class="server-container">
<div v-for="(item, index) in dataLists" :key="index" class="server-item">
<span>{{ item?.title }}</span>
<span>{{ item?.gross }}</span>
<div v-for="(item, index) in statistics" :key="index" class="server-item">
<span>{{ item?.name }}</span>
<span>{{ item?.value }}</span>
</div>
<a-spin :spinning="spinning" class="spin"></a-spin>
</div>
@@ -23,12 +23,13 @@
</template>
<script setup lang="ts">
import { onMounted, ref } from 'vue';
import bus from '/@/utils/bus.ts';
import PublicTitle from '../publicTitle.vue';
import { list } from './left.api';
import { timeTab, useSpin } from '/@/components/body-d-left/bodyLeftHooks.ts';
import { timeTab, useSpin, useStatistics } from '/@/components/body-d-left/bodyLeftHooks.ts';
const typeTime = ref(timeTab);
const dataLists = ref([]);
const { statistics, setStatistics } = useStatistics();
const selectIndex = ref(0);
const selectVal = ref('1');
const { setSpin, spinning } = useSpin();
@@ -42,10 +43,12 @@
try {
const { code, result } = (await list({ condition: selectVal.value })) || {};
setSpin(!spinning.value);
if (code != 200) {
if (code != 200 || !Array.isArray(result)) {
return;
}
dataLists.value = result?.map((item) => ({ ...item, title: '最新呼入电话' }));
setStatistics(result[0]);
let { alerdyreceive = '', totalacceptance = '' } = result[0];
bus.emit('alerdyreceive', { alerdyreceive, totalacceptance });
} catch {
setSpin(!spinning.value);
}
@@ -70,10 +73,6 @@
overflow-y: auto;
position: relative;
> :nth-child(1) {
padding-top: 0 !important;
}
.spin {
position: absolute;
left: 50%;
@@ -100,7 +99,7 @@
}
}
.server-item:nth-child(2n) {
.server-item:nth-child(odd) {
background-color: #0a0a0c;
}
}
+1 -2
View File
@@ -15,7 +15,7 @@
<a-spin :spinning="spinning" class="spin"></a-spin>
<div class="physical-item" v-for="(item, i) in hospitalList" :key="i" :class="[i < 3 ? 'b1' : 'b2']">
<div class="ct">
<div class="value">{{ countData[item.key] || '' }}</div>
<div class="value">{{ countData[item.key] || '/' }}</div>
<div class="label">{{ item.title }}</div>
</div>
</div>
@@ -55,7 +55,6 @@
return;
}
countData.value = result[0] || {};
console.log('result', result);
} catch {
setSpin(!spinning.value);
}
+61 -24
View File
@@ -4,11 +4,12 @@
<div class="inner">
<vue3-seamless-scroll :list="scrollList" class="scroll" :hover="true" :limitScrollNum="4" v-bind="classOption">
<div class="item" v-for="(item, i) in scrollList" :key="i">
<span>{{ item?.realName }}</span>
<span>{{ item?.eventType_dictText }}</span>
<span class="fixed-width" :title="item?.realName">{{ item?.realName }}</span>
<span class="fixed-width" :title="item?.eventType_dictText">{{ item?.eventType_dictText }}</span>
<div class="time-icon">
<span>{{ item?.warnTime }}</span>
<span class="terminal-icon" @click="handlePolice(item)"></span>
<span class="terminal-icon" @click="handlePolice(item)" title="健康报警员工信息"></span>
<span class="position" @click="sendPosition(item)" title="员工定位"></span>
</div>
</div>
</vue3-seamless-scroll>
@@ -25,27 +26,27 @@
<div class="police-con">
<div class="con-item">
<span>姓名</span>
<span>{{ userInfo.name }}</span>
<span>{{ userInfo?.realName }}</span>
</div>
<div class="con-item">
<span>二级单位</span>
<span>{{ userInfo.unit }}</span>
<span>{{ userInfo?.orgName1 }}</span>
</div>
<div class="con-item">
<span>手机号码</span>
<span>{{ userInfo.iphone }}</span>
<span>{{ userInfo?.phone }}</span>
</div>
<div class="con-item">
<span>异常事件</span>
<span>{{ userInfo.abnormal }}</span>
<span>{{ userInfo?.eventType_dictText }}</span>
</div>
<div class="con-item">
<span>发生时间</span>
<span>{{ userInfo.time }}</span>
<span>{{ userInfo?.warnTime }}</span>
</div>
<div class="con-item">
<span>发生地点</span>
<span>{{ userInfo.address }}</span>
<span>{{ userInfo?.address }}</span>
</div>
</div>
</template>
@@ -54,24 +55,26 @@
</template>
<script setup lang="ts">
import { onMounted, ref } from 'vue';
import $bus from '/@/utils/bus.ts';
import { columns } from '/@/components/body-d-right/bodyRightHooks.ts';
import PublicTitle from '/@/components/publicTitle.vue';
import Dialog from '/@/components/dialog/Dialog.vue';
import { Vue3SeamlessScroll } from 'vue3-seamless-scroll';
import { getMonitorList } from '/@/components/body-d-right/right.api.ts';
import { Map } from '/@/assets/mapUtils/map.ts';
const classOption = {
const classOption = ref({
step: 0.3, // 速度
};
});
const open = ref<boolean>(false);
const openInfor = ref<boolean>(false);
const userInfo = ref({
name: '王苗',
unit: '长庆油田',
iphone: '1897263745',
abnormal: '体温异常',
time: '2023-11-23 16:23',
address: '陕西省西安市',
realName: '',
orgName1: '',
phone: '',
eventType_dictText: '',
warnTime: '',
address: '',
});
const dataSource = ref([
@@ -91,12 +94,21 @@
open.value = false;
}
function handlePolice() {
/**
* @desc open员工信息
*/
function handlePolice(item) {
userInfo.value = item;
openInfor.value = true;
classOption.value.step = 0;
}
/**
* @desc close员工信息
*/
function handlePoliceClose() {
openInfor.value = false;
classOption.value.step = 0.3;
}
onMounted(() => {
@@ -109,12 +121,19 @@
const scrollList = ref([]);
async function getScrollList() {
const { code, result } = await getMonitorList({});
const { code, result } = await getMonitorList({ pageSize: 10, pageNo: 1 });
if (code == 200) {
scrollList.value = result;
scrollList.value = result.records;
}
}
console.log('dd', result);
function sendPosition(item) {
let { lon, lat } = item;
if (lon && lat) {
Map.createMarker(item, () => {
handlePolice(item);
});
}
}
</script>
<style scoped lang="less">
@@ -125,7 +144,7 @@
justify-content: center;
.scroll {
width: 90%;
width: 98%;
height: 88%;
overflow: hidden;
@@ -135,6 +154,14 @@
justify-content: space-between;
padding: 3% 0;
.fixed-width {
display: inline-flex;
width: 100px;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
}
span {
color: #ffffff;
font-size: 16px;
@@ -146,11 +173,21 @@
}
.terminal-icon {
margin-left: 20px;
margin-left: 15px;
display: inline-block;
width: 18px;
height: 18px;
background: url('../../assets/images/terminal.png') no-repeat;
background: url('/@/assets/images/terminal.png') no-repeat;
background-size: 100% 100%;
cursor: pointer;
}
.position {
margin-left: 5px;
display: inline-block;
width: 20px;
height: 20px;
background: url('/@/assets/img/position.png') no-repeat;
background-size: 100% 100%;
cursor: pointer;
}
+22
View File
@@ -7,6 +7,7 @@ export enum TabType {
yiLiaoDian = '3', //医疗点
jiuHuChe = '4', //救护车
zhiShengJi = '5', //直升机
diFangYiYuan = '6', //地方医院
}
export const tabList = [
@@ -32,6 +33,7 @@ export const tabList = [
},
{
name: '地方医院',
type: TabType.diFangYiYuan,
},
{
name: '直升机',
@@ -44,6 +46,7 @@ export const tabList = [
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 alarmIcon = new URL('/@/assets/img/alarm-icon.png', import.meta.url).href;
// 直升机位置数据
export const aircraft = [
@@ -77,6 +80,25 @@ export const aircraft = [
},
];
/**
* @desc 直升机文本
*/
export function aircraftContent() {
return `
<div style="display: flex;justify-content: center;align-items: center;">
<div>
<div style="display: flex;justify-content: center;">
<div class="helicopter"></div>
</div>
<div style="z-index:999;background-color:#0A8B18;">
<h5 style="overflow: hidden;font-size:18px;color:#fff;white-space: nowrap;padding:0 4px;">
4009-120-999
</h5>
</div>
</div>
</div>`;
}
export function mapApiSwitch(type, params: any) {
const basicParams = {
lat: 34, //纬度34
+71 -11
View File
@@ -19,7 +19,7 @@
<a-button class="search-btn" @click="searchRoute">路线规划</a-button>
</a-form-item>
</a-form>
<div class="line-container" v-show="showRoutePanel" v-click-outside="outClick">
<div v-show="showRoutePanel" v-click-outside="outClick" class="line-container">
<div v-show="routeList.length" class="has-data">
<div class="line-title"> {{ panelTitle }}</div>
<div class="line-content">
@@ -36,7 +36,9 @@
<div v-show="!routeList.length" class="empty-data"> 暂无数据</div>
</div>
</div>
<div class="mapContainer" id="mapContainer"></div>
<div style="flex: 1; overflow: hidden; margin: 0 0 10px">
<div id="mapContainer" class="mapContainer"></div>
</div>
<div v-show="false" id="container"></div>
<div v-show="false" id="panel"></div>
<div class="map-legend">
@@ -44,14 +46,15 @@
<div><img :src="item.img" alt="" class="legend-img" /> {{ item.text }}</div>
</div>
</div>
<div class="level">地图层级{{ level }}</div>
</div>
<div class="center-footer">
<div class="count-item">
<div class="num">1054</div>
<div class="count-item" @click="test">
<div class="num">{{ footerData?.alerdyreceive || '/' }}</div>
<div class="text">累计受理</div>
</div>
<div class="count-item">
<div class="num">1054</div>
<div class="num">{{ footerData?.totalacceptance || '/' }}</div>
<div class="text">累计处置</div>
</div>
</div>
@@ -59,9 +62,16 @@
</template>
<script lang="ts" setup>
import { onMounted, ref } from 'vue';
import $bus from '/@/utils/bus.ts';
import { message } from 'ant-design-vue';
import { Map } from '/@/assets/mapUtils/map';
import { tabList, legendList, getTimeStr, getKmStr, mapApiSwitch, TabType, aircraft } from '/@/components/chinaMap/chinaHooks';
import { aircraft, getKmStr, getTimeStr, legendList, mapApiSwitch, tabList, TabType } from '/@/components/chinaMap/chinaHooks';
function test() {
Map.clearActiveHospital();
console.log('清空了');
}
const formState = ref({
start: '西安北站',
@@ -74,27 +84,68 @@
// if (i == currentIndex.value) {
// return;
// }
currentIndex.value = i;
currentIndex.value = i; // 地方医院
if (TabType.diFangYiYuan == item.type) {
Map.clearActiveHospital();
Map.map.on('moveend', logMapinfo);
return;
} else {
Map.clearActiveHospital();
Map.map.off('moveend', logMapinfo);
}
// 直升机
if (TabType.zhiShengJi == item.type) {
Map.createAircraft(aircraft);
return;
}
getMapData(item.type, { type: item.type });
}
function logMapinfo() {
// let zoom = Map.map.getZoom();
Map.map.getCity(function (info) {
const { citycode } = info;
Map.createActiveHospital(citycode);
});
}
const level = ref(0);
function setLevel() {
//获取当前地图级别
level.value = Map.map.getZoom();
}
async function getMapData(type, params = {}) {
try {
const { code, result } = await mapApiSwitch(type, params);
if (code == 200 && Array.isArray(result) && result.length > 0) {
Map.createOverlay(result);
}
console.log('--->data', result);
} catch (e) {
console.log('error------------------------------');
console.warn(e);
}
1;
// console.log('--->data', result);
}
const footerData = ref({
alerdyreceive: '',
totalacceptance: '',
});
onMounted(() => {
console.log('reload');
initMap();
getMapData(tabList[0].type);
Map.map && Map.map.on('moveend', setLevel);
Map.map && Map.map.on('zoomend', setLevel);
$bus.on('alerdyreceive', (val) => {
footerData.value = val;
});
});
function initMap() {
@@ -221,7 +272,7 @@
background: #002e64;
border-radius: 6px;
position: absolute;
top: 40px;
top: 36px;
z-index: 999;
transition: all 0.3s;
@@ -343,14 +394,15 @@
.mapContainer {
flex: 1;
height: calc(100% + 22px);
margin: 10px 0;
//border: 1px solid red;
}
.map-legend {
position: absolute;
bottom: 20px;
left: 0;
bottom: 15px;
left: 10px;
width: 200px;
//border: 1px solid red;
display: flex;
@@ -368,6 +420,14 @@
}
}
.level {
position: absolute;
top: 50px;
left: 400px;
font-size: 18px;
color: #fff;
}
.center-footer {
display: flex;
justify-content: center;
+9 -1
View File
@@ -7,6 +7,7 @@
<script setup lang="ts">
import { defineProps, defineEmits } from 'vue';
const emit = defineEmits(['close']);
const props = defineProps({
width: {
@@ -19,6 +20,7 @@
type: String,
},
});
function handleCancel() {
emit('close');
}
@@ -31,30 +33,36 @@
display: flex;
align-items: center;
width: 100%;
height: 56px;
height: 40px;
background: url('../../assets/images/dialog.png') no-repeat;
background-size: 100% 100%;
color: #ffffff !important;
padding-left: 2%;
}
.ant-modal-header {
padding: 0 !important;
padding-bottom: 0;
}
.ant-modal-header,
.ant-modal-content {
background-color: transparent !important;
}
.ant-modal-body {
padding: 0 !important;
}
.ant-modal-close-x,
.anticon {
color: #999999 !important;
}
.ant-modal-footer {
border-top: none !important;
}
.ant-modal-wrap {
background-color: rgba(0, 0, 0, 0.6);
}
+5
View File
@@ -0,0 +1,5 @@
// 引入mitt插件 index.ts
import mitt from "mitt"
const $bus = mitt();
export default $bus
+56
View File
@@ -0,0 +1,56 @@
import { onMounted, onUnmounted, ref } from 'vue';
//健康监测工具报警
export const basicPath = 'ws://192.168.1.16:7098/websocket/watchMonitor';
//应急求助
export const emergencyPath = 'ws://192.168.1.16:7011/websocket/emergency';
const token =
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwiaWRlbnRpZmllciI6ImU5Y2EyM2Q2OGQ4ODRkNGViYjE5ZDA3ODg5NzI3ZGFlIiwiZXhwIjoxNzAyNjA0MDI3fQ.MYf9yUnXO0RTFL4u96pN1Z2mBwtXU5W-QlxDgoAsoOw';
export function useSocket(path = basicPath) {
const socket = ref();
let socketData = ref({});
function init() {
if (typeof WebSocket === 'undefined') {
alert('您的浏览器不支持socket');
} else {
// 实例化socket
socket.value = new WebSocket(path, [token]);
// 监听socket连接
socket.value.onopen = open;
// 监听socket错误信息
socket.value.onerror = error;
// 监听socket消息
socket.value.onmessage = getMessage;
}
}
function open() {
console.log('socket连接成功');
}
function error() {
console.log('socket连接错误');
}
function getMessage(msg) {
console.log('y', msg.data);
socketData.value = JSON.parse(msg.data);
}
function setSocketData(data) {
socketData.value = data;
}
function close() {
console.log('socket已经关闭');
}
onMounted(() => {
init();
});
onUnmounted(() => {
socket.value.onclose = close;
});
return { socket, socketData, setSocketData };
}
+25
View File
@@ -0,0 +1,25 @@
import * as dayjs from 'dayjs';
export function getZero(num: any) {
if (num < 10) {
return '0' + num;
}
return num;
}
export function useTopTime() {
const timeLeft = dayjs().format('YYYY年MM月DD日 HH时mm分ss秒');
const timeRight =
dayjs().diff(dayjs('2018-10-01'), 'day') +
'天' +
getZero(dayjs().diff(dayjs(dayjs().format('YYYY-MM-DD') + ' 00:00:00'), 'hours')) +
'时' +
getZero(dayjs().diff(dayjs(dayjs().format('YYYY-MM-DD HH') + ':00:00'), 'minutes')) +
'分' +
getZero(dayjs().diff(dayjs(dayjs().format('YYYY-MM-DD HH:mm') + ':00'), 'seconds')) +
'秒';
return {
timeLeft,
timeRight,
};
}
+20
View File
@@ -0,0 +1,20 @@
import { ref } from 'vue';
export function useDialog({ title }) {
const visible = ref(false);
function openDialog() {
visible.value = true;
}
function closeDialog() {
visible.value = false;
}
return {
visible,
title,
openDialog,
closeDialog,
};
}
+86
View File
@@ -0,0 +1,86 @@
<template>
<Dialog :openVis="visible" width="30%" :title="title" @close="closeDialog" :maskClosable="false">
<template #container>
<div class="police-con">
<div class="sub-text">有一个员工健康监测发生异常</div>
<div class="con-item">
<span>异常事件</span>
<span>{{ userInfo?.eventType_dictText }}</span>
</div>
<div class="con-item">
<span>姓名</span>
<span>{{ userInfo?.realName }}</span>
</div>
<div class="con-item">
<span>二级单位</span>
<span>{{ userInfo?.orgName }}</span>
</div>
<div class="con-item">
<span>手机号码</span>
<span>{{ userInfo?.phone }}</span>
</div>
<div class="con-item">
<span>发生时间</span>
<span>{{ userInfo?.warnTime }}</span>
</div>
<div class="con-item">
<span>发生地点</span>
<span>{{ userInfo?.address }}</span>
</div>
</div>
</template>
</Dialog>
</template>
<script setup lang="ts">
import Dialog from '/@/components/dialog/Dialog.vue';
import { useDialog } from '/@/views/components/dialogHooks.ts';
import { watch, ref } from 'vue';
const props = defineProps({
record: Object,
});
const userInfo = ref({});
watch(
() => props.record,
(nVal) => {
userInfo.value = nVal;
}
);
const { visible, title, closeDialog, openDialog } = useDialog({ title: '健康监测工具报警' });
defineExpose({
openDialog,
});
</script>
<style scoped lang="less">
.police-con {
color: #ffffff;
background-color: #00152b;
border: 1px solid #129bff;
padding: 2% 0 2% 5%;
.sub-text {
font-weight: bold;
margin-left: 8px;
}
.con-item {
padding: 1.3% 0;
display: flex;
span:nth-child(1) {
font-weight: bold;
text-align: right;
display: inline-flex;
justify-content: flex-end;
width: 80px;
}
span:nth-child(2) {
flex: 1;
padding-left: 2%;
padding-right: 2%;
}
}
}
</style>
+86
View File
@@ -0,0 +1,86 @@
<template>
<Dialog :openVis="visible" width="30%" :title="title" @close="closeDialog" :maskClosable="false">
<template #container>
<div class="police-con">
<div class="sub-text">有一个员工健康监测发生异常</div>
<div class="con-item">
<span>异常事件</span>
<span>{{ userInfo?.eventType_dictText }}</span>
</div>
<div class="con-item">
<span>姓名</span>
<span>{{ userInfo?.realName }}</span>
</div>
<div class="con-item">
<span>二级单位</span>
<span>{{ userInfo?.orgName }}</span>
</div>
<div class="con-item">
<span>手机号码</span>
<span>{{ userInfo?.phone }}</span>
</div>
<div class="con-item">
<span>发生时间</span>
<span>{{ userInfo?.warnTime }}</span>
</div>
<div class="con-item">
<span>发生地点</span>
<span>{{ userInfo?.address }}</span>
</div>
</div>
</template>
</Dialog>
</template>
<script setup lang="ts">
import Dialog from '/@/components/dialog/Dialog.vue';
import { useDialog } from '/@/views/components/dialogHooks.ts';
import { watch, ref } from 'vue';
const props = defineProps({
record: Object,
});
const userInfo = ref({});
watch(
() => props.record,
(nVal) => {
userInfo.value = nVal;
}
);
const { visible, title, closeDialog, openDialog } = useDialog({ title: '健康监测工具报警' });
defineExpose({
openDialog,
});
</script>
<style scoped lang="less">
.police-con {
color: #ffffff;
background-color: #00152b;
border: 1px solid #129bff;
padding: 2% 0 2% 5%;
.sub-text {
font-weight: bold;
margin-left: 8px;
}
.con-item {
padding: 1.3% 0;
display: flex;
span:nth-child(1) {
font-weight: bold;
text-align: right;
display: inline-flex;
justify-content: flex-end;
width: 80px;
}
span:nth-child(2) {
flex: 1;
padding-left: 2%;
padding-right: 2%;
}
}
}
</style>
+8
View File
@@ -0,0 +1,8 @@
import { get } from '/@/api/request';
export enum Api {
deleteWatchNo = 'health-watch/watch/watchMonitorData/delete',
}
//获取全部部门
export const deleteWatchNo = (params: any) => get(Api.deleteWatchNo, params);
+71 -21
View File
@@ -22,11 +22,13 @@
<three-r />
</div>
</div>
<div class="bottom-d"></div>
<div class="bottom-d" @click="test"></div>
<EmployeeDialog ref="employeeDialogRef" :record="socketData" />
<EmergencyDialog ref="emergencyDialogRef" :record="emergencyData" />
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { onUnmounted, ref, watch, nextTick } from 'vue';
import OneL from '../components/body-d-left/oneL.vue';
import TwoL from '../components/body-d-left/twoL.vue';
import ThreeL from '../components/body-d-left/threeL.vue';
@@ -34,39 +36,87 @@
import TwoR from '../components/body-d-right/twoR.vue';
import ThreeR from '../components/body-d-right/threeR.vue';
import ChinaMap from '../components/chinaMap/chinaMap.vue';
import * as dayjs from 'dayjs';
import EmployeeDialog from '/@/views/components/employeeDialog.vue';
import EmergencyDialog from '/@/views/components/emergencyDialog.vue';
import { useTopTime } from '/@/utils/utils.ts';
import { basicPath, emergencyPath, useSocket } from '/@/utils/socket.ts';
const dayTimeO = ref();
const dayTimeT = ref();
const interval = ref(1);
function init() {
getTime();
setInterval(() => {
interval.value = setInterval(() => {
getTime();
}, 1000);
}
function getTime() {
dayTimeO.value = dayjs().format('YYYY年MM月DD日 HH时mm分ss秒');
dayTimeT.value =
dayjs().diff(dayjs('2018-10-01'), 'day') +
'天' +
getZero(dayjs().diff(dayjs(dayjs().format('YYYY-MM-DD') + ' 00:00:00'), 'hours')) +
'时' +
getZero(dayjs().diff(dayjs(dayjs().format('YYYY-MM-DD HH') + ':00:00'), 'minutes')) +
'分' +
getZero(dayjs().diff(dayjs(dayjs().format('YYYY-MM-DD HH:mm') + ':00'), 'seconds')) +
'秒';
}
function getZero(num: any) {
if (num < 10) {
return '0' + num;
}
return num;
const { timeLeft, timeRight } = useTopTime();
dayTimeO.value = timeLeft;
dayTimeT.value = timeRight;
}
onUnmounted(() => {
clearInterval(interval.value);
});
init();
// 健康监测工具报警
const { socketData, setSocketData } = useSocket(basicPath);
const employeeDialogRef = ref();
watch(socketData, (nVal) => {
if (nVal) {
nextTick(() => {
setSocketData(nVal);
employeeDialogRef.value.openDialog();
});
}
});
//应急求助
const { socketData: emergencyData, setSocketData: setEmergencyData } = useSocket(emergencyPath);
const emergencyDialogRef = ref();
watch(emergencyData, (nVal) => {
if (nVal) {
nextTick(() => {
setEmergencyData(nVal);
emergencyDialogRef.value.openDialog;
});
}
});
function test() {
let nVal = {
watchNo: 'CRFTQ22C03000080',
eventType: 'heart_rate',
bindUserId: 'fd358031299d4816b5d304fd818ecc73',
createBy: null,
createDate: null,
dataValue: '999',
address:
'一段文字一段文字一段文字一段文字一段文字一段文字一段文字一段文字一段文字一段文字一段文字一段文字一段文字一段文字一段文字一段文字一段文字一段文字一段文字一段文字一段文字一段文字一段文字一段文字一段文字一段文字一段文字一段文字一段文字',
lon: 108.6666,
lat: 29.8888,
dataDate: '2023-12-07',
warnTime: '2023-12-07',
gpsErrorMsg: '',
lonGd: 108.67084314886117,
latGd: 29.88607705672868,
addressGd: null,
sendFlag: '0',
largeScreenFlag: '0',
orgCode: null,
orgName: '冯地坑采油作业区',
realName: '王志文',
phone: '15229020494',
};
nextTick(() => {
setSocketData(nVal);
employeeDialogRef.value.openDialog();
});
}
</script>
<style lang="less" scoped>
.outer {