1.新需求2025.9.19新需求:
去掉"健康监测"副屏, 修改后健康终端报警点击查看详情弹出告警记录分页列表;
去掉【体检人数统计】【体检数据】【健康打卡数据】,增加【心血管监测】模块功能,【心血管监测】功能将原心血管监测大屏上的内容整理并移动到急救服务平台大屏中;
员工求助时,地图中推荐两个医院、两个健康小屋、两个AED坐标信息,并按距离排序,体现距离远近顺序;
地图中油田医院、健康小屋等各个图例,显示总数信息;
救护车坐标信息维护,并在大屏上标记;
右上角"增加【救助信息】板块, 当出现应急弹窗后或从今日应急选中应急事件 , 该板块需展示应急事件的相关信息(开发中);
This commit is contained in:
2025-09-24 20:53:28 +08:00
parent a97d9c64aa
commit c7abcf1f67
29 changed files with 1783 additions and 175 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

+29
View File
@@ -0,0 +1,29 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
width="72.000000pt" height="176.000000pt" viewBox="0 0 72.000000 176.000000"
preserveAspectRatio="xMidYMid meet">
<g transform="translate(0.000000,176.000000) scale(0.100000,-0.100000)"
fill="#000000" stroke="none">
<path d="M269 1715 c-107 -34 -194 -124 -225 -233 -43 -146 37 -312 185 -380
45 -22 71 -27 131 -27 130 1 234 66 292 183 63 127 39 271 -62 372 -82 82
-218 118 -321 85z m151 -190 c5 -38 7 -40 45 -43 22 -2 43 -6 47 -10 4 -4 8
-51 9 -105 1 -53 5 -100 10 -103 5 -3 9 -14 9 -25 0 -18 -9 -19 -165 -20 -91
-1 -171 -2 -177 -3 -23 -2 -21 41 1 47 19 5 21 13 21 95 0 49 3 117 6 151 l7
62 91 -3 91 -3 5 -40z"/>
<path d="M298 1536 c-24 -18 -23 -39 3 -62 22 -19 43 -13 63 19 8 12 5 21 -8
36 -22 24 -34 26 -58 7z"/>
<path d="M431 1366 c2 -102 18 -126 21 -32 1 36 2 76 2 88 1 12 -5 23 -12 26
-10 3 -13 -15 -11 -82z"/>
<path d="M267 1424 c-14 -15 6 -24 54 -24 36 0 49 4 47 13 -5 13 -89 23 -101
11z"/>
<path d="M272 1358 c5 -15 87 -18 91 -3 1 6 -20 11 -47 13 -36 2 -47 0 -44
-10z"/>
<path d="M283 1313 c-7 -2 -13 -9 -13 -14 0 -12 87 -12 94 0 11 17 -49 27 -81
14z"/>
<path d="M345 718 c-8 -308 -5 -393 15 -393 12 0 16 16 18 75 5 114 -9 560
-18 560 -4 0 -11 -109 -15 -242z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.4 KiB

After

Width:  |  Height:  |  Size: 8.8 KiB

+18 -13
View File
@@ -1,4 +1,4 @@
import { mapIcon1, mapIcon11, mapIcon4, mapIcon41, mapIcon5, mapIcon51, watchIconR } from '/@/components/chinaMap/chinaHooks.ts';
import { mapIcon1, mapIcon11, mapIcon3, mapIcon4, mapIcon41, mapIcon5, mapIcon51, watchIconR } from '/@/components/chinaMap/chinaHooks.ts';
import * as turf from '@turf/turf';
/**
@@ -20,22 +20,27 @@ export function createIcon({ icon, iconSize, imageSize }) {
* @type 1:油田医院,2:健康小屋,4:AED
*/
export function typeToIcon(type: string) {
const iconSize = [28, 49];
const imageSize = [28, 49];
// const iconSize = [28, 49];
// const imageSize = [28, 49];
if (type == '1') {
return createIcon({ icon: mapIcon1, iconSize, imageSize });
}
if (type == '2') {
return createIcon({ icon: mapIcon5, iconSize, imageSize });
// return createIcon({ icon: mapIcon1, iconSize, imageSize });
return mapIcon1;
}
if (type == '3') {
return createIcon({ icon: mapIcon5, iconSize, imageSize });
// return createIcon({ icon: mapIcon5, iconSize, imageSize });
return mapIcon5;
}
if (type == '4') {
return createIcon({ icon: mapIcon4, iconSize, imageSize });
// return createIcon({ icon: mapIcon4, iconSize, imageSize });
return mapIcon4;
}
if (type == '5') {
// return createIcon({ icon: mapIcon3, iconSize, imageSize });
return mapIcon3;
}
if (type === 'user') {
return createIcon({ icon: watchIconR, iconSize, imageSize });
// return createIcon({ icon: watchIconR, iconSize, imageSize });
return watchIconR;
}
}
export function typeToIcon1(type: string) {
@@ -44,15 +49,15 @@ export function typeToIcon1(type: string) {
if (type == '1') {
return createIcon({ icon: mapIcon11, iconSize, imageSize });
}
if (type == '2') {
return createIcon({ icon: mapIcon51, iconSize, imageSize });
}
if (type == '3') {
return createIcon({ icon: mapIcon51, iconSize, imageSize });
}
if (type == '4') {
return createIcon({ icon: mapIcon41, iconSize, imageSize });
}
if (type == '5') {
return createIcon({ icon: mapIcon3, iconSize, imageSize });
}
if (type === 'user') {
const iconS = [36, 88];
const imageS = [36, 88];
+111 -4
View File
@@ -22,7 +22,6 @@ export const Map = {
emergencyMarker: null, // 应急人员marker
startMarker: null,
endMarker: null,
clearSOrE: null,
/**
* @desc 地图舒适化
* @param el dom元素 id选择器
@@ -140,6 +139,70 @@ export const Map = {
* @param computedCenter 是否计算中心点
*/
createOverlay(result: ResultType[], extData?: ExtDataType, callback?: Callback, computedCenter?: boolean, iconType?: string) {
this.removeOverlayGroup();
this.overlayGroup = new AMap.OverlayGroup();
// const list: any[] = getLonLat(result);
// const center = getCenter(list);
// const zoom = getZoom(list);
let arr: any[] = [];
for (let i = 0; i < result.length; i++) {
let { type, lon, lat, longitude, latitude, lng, centerResource } = result[i];
if (longitude) {
lon = longitude;
}
if (latitude) {
lat = latitude;
}
if (lng) {
lon = lng;
}
if (!lon || !lat || !type) {
continue;
}
// 创建一个 Icon
let startIcon = typeToIcon1(type);
// 将 icon 传入 marker
let startMarker = new AMap.Marker({
// 点的坐标
position: [lon, lat], // 指定点的icon,也可以是个url ,但这样就不能对图片进行设置了
icon: startIcon, //启用点击事件 如果需要点击事件,必须开启
clickable: true, //点额外携带的数据,用户自定义属性,支持JavaScript API任意数据类型
offset: [-18, -32],
maxZoom: 14,
extData: result[i],
label: {
content: isFunction(extData?.content) && extData?.content(result[i]),
},
...extData?.extOption,
});
arr.push(startMarker);
// 可以调用setExtData()设置自定义属性
//将组 挂载到地图上
this.overlayGroup && this.overlayGroup.setMap(this.map);
//将需要批量控制的点放到一个组中,通过控制组,就可以操作组内所有所有覆盖物
this.overlayGroup && this.overlayGroup.addOverlay(startMarker);
}
this.overlayGroup &&
this.overlayGroup.on('click', function (e: any) {
//拿到点中携带的数据
if (!isFunction(callback)) return;
callback(e.target.getExtData());
});
if (computedCenter) {
this.map && this.map.setFitView(arr, false, [50, 50, 0, 50]);
}
return arr;
},
/**
* @desc 创建覆盖物组,用于批量处理点 (控制显隐,点击事件等)
* @param result
* @param extData
* @param callback
* @param computedCenter 是否计算中心点
*/
async createOverlay1(result: ResultType[], extData?: ExtDataType, callback?: Callback, computedCenter?: boolean, iconType?: string) {
this.removeOverlayGroup();
this.overlayGroup = new AMap.OverlayGroup();
const list: any[] = getLonLat(result);
@@ -148,6 +211,49 @@ export const Map = {
if (computedCenter) {
this.setZoomCenter(center, zoom);
}
function createTextOverlayIcon(originalIconUrl, text, options = {}) {
return new Promise((resolve) => {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
const img = new Image();
img.crossOrigin = 'anonymous';
img.onload = function () {
// 设置画布大小(比原图稍大以容纳文字)
canvas.width = 28;
canvas.height = 49 + 15; // 为文字留出空间
// 绘制原始图标
ctx.drawImage(img, 0, 10, 28, 49);
// 绘制圆形文字背景
const textX = 22; // 文字X坐标(居右对齐)
const textY = 11; // 文字Y坐标
const textWidth = ctx.measureText(text).width;
const circleRadius = Math.max(textWidth / 2 + 1, 6); // 根据文字宽度计算半径
ctx.beginPath();
ctx.arc(textX, textY, circleRadius, 0, Math.PI * 2);
ctx.fillStyle = 'red';
ctx.fill();
// 绘制文字背景(可选)
// ctx.fillStyle = 'red';
// ctx.fillRect(0, 0, 28, 15);
// 绘制文字
ctx.font = `bold 10px Arial`;
ctx.textAlign = 'right';
ctx.textBaseline = 'middle';
ctx.fillStyle = '#ffffff';
ctx.fillText(text, 25, 10);
resolve(canvas.toDataURL());
};
img.src = originalIconUrl;
});
}
let arr = [];
for (let i = 0; i < result.length; i++) {
let { type, lon, lat, longitude, latitude, lng, centerResource } = result[i];
@@ -165,12 +271,13 @@ export const Map = {
continue;
}
// 创建一个 Icon
let startIcon = iconType === '1' ? typeToIcon(type) : typeToIcon1(type);
const startIcon: any = typeToIcon(type);
const newIconUrl = await createTextOverlayIcon(startIcon, i + 1 + '');
// 将 icon 传入 marker
let startMarker = new AMap.Marker({
const startMarker = new AMap.Marker({
// 点的坐标
position: [lon, lat], // 指定点的icon,也可以是个url ,但这样就不能对图片进行设置了
icon: startIcon, //启用点击事件 如果需要点击事件,必须开启
icon: newIconUrl, //启用点击事件 如果需要点击事件,必须开启
clickable: true, //点额外携带的数据,用户自定义属性,支持JavaScript API任意数据类型
offset: [-18, -32],
maxZoom: 14,
@@ -15,6 +15,7 @@
:style="{
cursor: item.key == 'allotDeviceNum' ? 'pointer' : 'default',
}"
:key="`footer-list-${item.key}`"
class="col-item nowrap"
@click="openWatchList(item)"
>
@@ -35,10 +36,10 @@
class="footer-dialog-con"
style="--moveUp: 20px; --moveDown: -180px"
>
<FooterDialog :detailLeft="detailLeft" :detailRight="detailRight" @closeDialog="closeDialog"></FooterDialog>
<FooterDialog :detailLeft="detailLeft" :detailRight="detailRight" @closeDialog="closeDialog" />
</div>
</div>
<WatchListDialog ref="watchDialogRef" @close="closeWatch"></WatchListDialog>
<WatchListDialog ref="watchDialogRef" @close="closeWatch" />
</div>
</template>
<script lang="ts" setup>
+32
View File
@@ -23,6 +23,10 @@ export const useUserStore = defineStore({
centerId: '', //中心点id
centerInfo: null, //应急中心信息
count: 0,
typeFour: 0, // aed
typeThree: 0, // 健康小屋
typeOne: 0, // 油田医院
typeFive: 0, // 救护车
}),
getters: {
getUserInfo(): UserInfo {
@@ -58,6 +62,18 @@ export const useUserStore = defineStore({
getCount(): any {
return this.count || getAuthCache('count');
},
getTypeOne(): any {
return this.typeOne || getAuthCache('typeOne');
},
getTypeFive(): any {
return this.typeFive || getAuthCache('typeTwo');
},
getTypeThree(): any {
return this.typeThree || getAuthCache('typeThree');
},
getTypeFour(): any {
return this.typeFour || getAuthCache('typeFour');
},
},
actions: {
setToken(info: string | undefined) {
@@ -103,6 +119,22 @@ export const useUserStore = defineStore({
this.count++;
setAuthCache('count', this.count);
},
setTypeOne(info: number) {
this.typeOne = info;
setAuthCache('typeOne', this.typeOne);
},
setTypeFive(info: number) {
this.typeFive = info;
setAuthCache('typeTwo', this.typeFive);
},
setTypeThree(info: number) {
this.typeThree = info;
setAuthCache('typeThree', this.typeThree);
},
setTypeFour(info: number) {
this.typeFour = info;
setAuthCache('typeFour', this.typeFour);
},
/**
* 登录事件
*/
+115 -94
View File
@@ -19,24 +19,26 @@
<twoL @handle-view="handleTwoLClick" :center-id="centerId" :bKey="`two-l-${count}`" />
</div>
<div class="sun-body-left-three">
<three-left-new :bKey="`three-l-n-${count}`" :center-id="centerId" />
<!-- <threeL />-->
<threeNewL :bKey="`three-l-${count}`" :center-id="centerId" />
</div>
<div class="sun-body-left-four" style="margin-bottom: 0">
<threeR @handle-click="handleThreeRClick" :center-id="centerId" :bKey="`four-l-${count}`" />
<!-- <threeNewL :bKey="`three-l-${count}`" :center-id="centerId" />-->
</div>
<!-- <div class="sun-body-left-four" style="margin-bottom: 0">-->
<!-- <threeR @handle-click="handleThreeRClick" :center-id="centerId" :bKey="`four-l-${count}`" />-->
<!-- </div>-->
</div>
<div class="sun-body-center">
<CenterMap
ref="centerMapRef"
:showHospitalList="showHospitalList"
:hospitalList="hospitalList"
@clear-hospital-list="clearHospitalList"
:show-rest-b="showRestB"
@handle-detail="handleCenterMapClick"
:map-bottom-info="mapBottom"
:show-map-bottom="showMapBottom"
@show-rest-b="
() => {
userData = {};
showRestB = false;
showMapBottom = false;
}
@@ -44,7 +46,10 @@
/>
</div>
<div class="sun-body-right">
<div class="sun-body-right-one" style="height: calc((100% - 350px) / 2 - 10px); margin-bottom: 10px">
<div class="sun-body-right-one">
<one-right-new :user-data="userData" @handle-view="handleOneRightView" @handle-archives="handleArchives" />
</div>
<div class="sun-body-right-two">
<oneR
@handle-view="handleOneRClick"
@show-reset="
@@ -55,11 +60,8 @@
:bKey="`one-r-${count}`"
/>
</div>
<div class="sun-body-right-two" style="height: calc((100% - 350px) / 2 - 10px); margin-bottom: 10px">
<rwo-new-r :center-id="centerId" :bKey="`two-n-r-${count}`" @handle-view="handleTwoNewRClick" />
</div>
<div class="sun-body-right-three" style="height: 350px">
<twoR @handle-click="handleTwoRClick" :center-id="centerId" :bKey="`two-r-${count}`" />
<div class="sun-body-right-three">
<two-new-r :center-id="centerId" :bKey="`two-n-r-${count}`" @handle-view="handleTwoNewRClick" />
</div>
</div>
</div>
@@ -79,16 +81,18 @@
:firstForm="firstFormOne"
:need-dept="needDept"
:twoROnly="twoROnly"
/>
>
<template #bodyCell="{ column, record }">
<template v-if="column.dataIndex === 'cz'">
<a-button type="link" @click="forHelp(record)">救助信息</a-button>
</template>
</template>
</IndexModal>
</template>
<script setup lang="ts">
// import threeL from '/@/views/indexSun/threeL.vue';
import OneL from '/@/views/indexSun/oneL.vue';
import twoL from '/@/views/indexSun/twoL.vue';
import threeNewL from '/@/views/indexSun/threeNewL.vue';
import oneR from '/@/views/indexSun/oneR.vue';
import twoR from '/@/views/indexSun/twoR.vue';
import threeR from '/@/views/indexSun/threeR.vue';
import CenterMap from '/@/views/indexSun/centerMap.vue';
import { ServerDetailType } from '/@/components/body-d-left/bodyLeftHooks.ts';
import {
@@ -97,6 +101,7 @@
columns,
fromLists,
infoContentSpecial,
oneRNewColumns,
threeRModalColumns,
threeRModalFromList,
towRcolumns,
@@ -120,9 +125,12 @@
const { getCenterInfo, setCount } = useUserStore();
import {
healthArchivesApi,
nearResourcesApi,
oneLApi,
roomDetailApi,
threeRModalApi,
todayEmergencyListApi,
twoLModalApi,
twoRModal1Api,
twoRModal2Api,
@@ -130,7 +138,9 @@
twoRNApi,
} from '/@/views/indexSun/indexSun.api.ts';
import dayjs from 'dayjs';
import RwoNewR from '/@/views/indexSun/rwoNewR.vue';
import ThreeLeftNew from '/@/views/indexSun/threeLeftNew.vue';
import OneRightNew from '/@/views/indexSun/oneRightNew.vue';
import TwoNewR from '/@/views/indexSun/twoNewR.vue';
//0:处置 1:处理 2:呼入 3:全部
const phoneType = ref('');
const phoneDialogTitle = ref('呼入电话');
@@ -152,8 +162,9 @@
const qhInterval = ref();
const cInterval = ref();
const initDate = ref(startTime);
const mapBottom = ref({});
const showMapBottom = ref(false);
const userData = ref<object>({});
const centerMapRef = ref();
onMounted(() => {
getTime();
@@ -185,16 +196,40 @@
}
);
async function forHelp(record: any) {
showMapBottom.value = false;
indexModalRef.value.closeDialog();
record['lat'] = record?.latitude;
record['lon'] = record?.longitude;
if (record['lat']) {
let list: any = [];
const res: any = await nearResourcesApi({ longitude: record?.lon, latitude: record?.lat });
if (res.code === 200) {
list = res.result || [];
}
record['hospitalList'] = list.map((item: any) => {
item['latitude'] = item['lat'];
item['longitude'] = item['lon'];
return item;
});
} else {
centerMapRef.value.resetMap();
showRestB.value = false;
userData.value = record;
}
confirmHelp(record);
}
function toOther() {
window.open('http://aygc.jy.pc.iosp.ydpt.tech/envShow/envFullscreen', '_blank');
}
function handleOneLeftClick(item) {
function handleOneLeftClick(item: any) {
const { name, key } = item;
//呼入电话
//受理电话
if (key == 'alerdyreceive' || key == 'gross') {
phoneType.value = ServerDetailType[key];
phoneType.value = (ServerDetailType as any)[key];
indexModalTitle.value = name;
firstFormOne.value = fromLists[0];
indexColumn.value = columns[0];
@@ -247,56 +282,6 @@
orgName: '硬件部',
departCode: 'A01A01',
departName: '软硬件开发部',
hospitalList: [
{
id: null,
name: '吉祥作业区',
lat: 44.000833,
lon: 89.214722,
type: null,
centerResource: false,
manageName: null,
managePhone: null,
address: null,
ambulance: null,
docNum: null,
nurseNum: null,
expertNum: null,
distance: '383km',
},
{
id: null,
name: '吉祥作业区',
lat: 44.000833,
lon: 89.214722,
type: null,
centerResource: false,
manageName: null,
managePhone: null,
address: null,
ambulance: null,
docNum: null,
nurseNum: null,
expertNum: null,
distance: '383km',
},
{
id: null,
name: '页岩油作业区',
lat: 44.233333,
lon: 89.105278,
type: null,
centerResource: false,
manageName: null,
managePhone: null,
address: null,
ambulance: null,
docNum: null,
nurseNum: null,
expertNum: null,
distance: '363km',
},
],
type: '0',
centerId: '1940693902422519809',
popType: 1,
@@ -341,8 +326,8 @@
const hospitalList = ref([]); //医院列表
const showHospitalList = ref(false);
async function confirmHelp(data) {
if (!data.lon) return;
async function confirmHelp(data: any) {
if (!data.lon) return (showMapBottom.value = true);
Map.removeOverlayGroup();
hospitalList.value = data.hospitalList;
// showHospitalList.value = true;
@@ -350,7 +335,8 @@
let point = [data.lon, data.lat];
// if (data.hospitalList && data.hospitalList.length > 0) {
// try {
hospitalList.value = await getHospitalList(data.hospitalList, point);
const a = ((await getHospitalList(data.hospitalList, point)) as any) || [];
hospitalList.value = a.sort((a: any, b: any) => a.distance - b.distance);
// showHospitalList.value = true;
// } catch (error) {
// console.log('获取医院列表时出错:', error);
@@ -358,13 +344,8 @@
// } else {
// console.log('没有医院list');
// }
mapBottom.value = data;
// mapBottom.value['careLiaisonName'] = '1';
// mapBottom.value['careLiaisonPhone'] = '1';
// mapBottom.value['lifeguardName'] = '1';
// mapBottom.value['lifeguardPhone'] = '1';
showMapBottom.value = true;
const arr = Map.createOverlay(
// showMapBottom.value = true;
const arr = await Map.createOverlay1(
[...hospitalList.value, { latitude: data.lat, longitude: data.lon }],
{
content: (val) => {
@@ -410,9 +391,9 @@
Map.createEmergencyMarker(option);
Map.map.setFitView([...arr, Map.emergencyMarker], false, [180, 180, 180, 180]);
})
.catch((err) => {
Map.map.setFitView(arr, false, [180, 180, 180, 180]);
});
.catch((err) => {});
userData.value = data;
}
async function getHospitalList(hospitalList, userLocation) {
@@ -566,6 +547,34 @@
indexModalRef.value.openDialog();
});
}
function handleOneRightView() {
indexColumn.value = oneRNewColumns;
firstFormOne.value = [];
const params = {
centerId: centerId.value,
startDate: '2000-01-01',
endDate: '2025-09-25',
};
indexModalTitle.value = '今日应急';
modalApi.value = todayEmergencyListApi;
apiParams.value = params;
nextTick(() => {
indexModalRef.value.openDialog();
});
}
function handleArchives(userId: string) {
indexColumn.value = oneRNewColumns;
firstFormOne.value = [];
const params = {
userId: userId,
};
indexModalTitle.value = '今日应急';
modalApi.value = healthArchivesApi;
apiParams.value = params;
nextTick(() => {
indexModalRef.value.openDialog();
});
}
function handleCenterMapClick(res: any) {
indexColumn.value = centerMapColumns;
@@ -632,7 +641,7 @@
width: 22%;
display: flex;
flex-direction: column;
justify-content: space-between;
//justify-content: space-between;
}
.sun-body-center {
@@ -663,25 +672,29 @@
}
.sun-body-left-two {
height: 34%;
flex: 1;
}
.sun-body-left-three {
height: 28%;
}
.sun-body-left-four {
height: 18%;
//height: 45%;
}
//.sun-body-left-four {
// height: 18%;
//}
}
.sun-body-right {
display: flex;
flex-direction: column;
.sun-body-right-one,
.sun-body-right-two,
.sun-body-right-three {
//display: flex;
//flex-direction: column;
> div {
border: 1px solid #00ccff;
border-radius: 14px;
overflow: hidden;
overflow: hidden;
&:not(:last-child) {
margin-bottom: 10px;
}
.one-r-box,
.two-r-box,
@@ -689,6 +702,14 @@
height: 100%;
}
}
.sun-body-right-one {
}
.sun-body-right-two {
flex: 1;
}
.sun-body-right-three {
flex: 1;
}
}
}
}
+43 -53
View File
@@ -48,7 +48,7 @@
<div v-show="false" id="panel"></div>
<div class="map-legend">
<div v-for="(item, i) in legendList" :key="i" class="legend-item">
<div :style="{ color: item.color }"><img :src="item.img" alt="" class="legend-img" /> {{ item.text }} </div>
<div :style="{ color: item.color }"><img :src="item.img" alt="" class="legend-img" /> {{ item.text }}({{ numList[i] }}) </div>
</div>
</div>
</div>
@@ -64,38 +64,17 @@
</div>
<div class="hospital-list" :class="[themeType]" v-if="showMapBottom">
<!-- <div class="hospital-item" v-for="(item, index) in hospitalList" :key="index" @click.stop="showDrivingLine(item)">-->
<!-- <div>{{ item?.name }}</div>-->
<!-- <div>相距{{ item?.distance }}</div>-->
<!-- <div>驾车大约{{ item?.driveTime }}</div>-->
<!-- </div>-->
<div class="map-bottom">
<div>
<div class="title">关爱联络员</div>
<template v-if="mapBottomInfo?.careLiaisonName && mapBottomInfo?.careLiaisonPhone">
<div>{{ mapBottomInfo?.careLiaisonName }}</div>
<div>{{ mapBottomInfo?.careLiaisonPhone }}</div>
</template>
<template v-else>
<div class="no-data"> 该员工暂未设置关爱联络员 <img :src="pointPng" alt="" /></div>
</template>
</div>
<div>
<div class="title">急救人员</div>
<template v-if="mapBottomInfo?.lifeguardName && mapBottomInfo?.lifeguardPhone">
<div>{{ mapBottomInfo?.lifeguardName }}</div>
<div>{{ mapBottomInfo?.lifeguardPhone }}</div>
</template>
<template v-else>
<div class="no-data"> 该员工暂未设置急救人员 <img :src="pointPng" alt="" /></div>
</template>
<img :src="noLocationPng" alt="" />
</div>
<div> 未获取到当前救助人员的位置信息 </div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { nextTick, onMounted, ref, watch } from 'vue';
import { nextTick, onMounted, reactive, ref, watch } from 'vue';
import { infoContent, infoContent1, infoContent4, legendList, tabList } from '/@/views/indexSun/indexSun.ts';
import { message } from 'ant-design-vue';
import { innerLineLight, outerLineOption } from '/@/assets/mapUtils/mapConstant.ts';
@@ -106,8 +85,11 @@
import { earlyWarning } from '/@/utils/busConstant.ts';
import { useTheme } from '/@/hooks/theme/useTheme.ts';
import { RedoOutlined } from '@ant-design/icons-vue';
import pointPng from '/@/assets/indexSun/point.png';
import { useUserStore } from '/@/store/modules/user.ts';
import noLocationPng from '/@/assets/images/map/noLocation.png';
const useDetail = useDetailStore();
const useStore = useUserStore();
const { themeType } = useTheme();
const list = ref(tabList);
const currentIndex = ref(0);
@@ -116,6 +98,8 @@
start: '',
end: '',
});
// 1 aed 2 健康小屋 3 油田医院 4 救护车
const numList = reactive([useStore.getTypeThree, useStore.getTypeOne, useStore.getTypeFive, useStore.getTypeFour]);
const props = defineProps({
//是否展示底部统计
showFooter: {
@@ -139,16 +123,11 @@
return [];
},
},
mapBottomInfo: {
type: Object,
default: () => {},
},
showMapBottom: {
type: Boolean,
default: () => false,
},
});
const useDetail = useDetailStore();
const showRoutePanel = ref(false);
const panelTitle = ref();
const level = ref(0);
@@ -312,6 +291,16 @@
async function getMapData(type, params = { type: '' }) {
try {
const { code, result } = await mapApiSwitch(type, params);
if (!params.type) {
// aed
useStore.setTypeFour(result ? result.filter((item: any) => item?.type === '4').length : 0);
// 健康小屋
useStore.setTypeThree(result ? result.filter((item: any) => item?.type === '3').length : 0);
// 油田医院
useStore.setTypeOne(result ? result.filter((item: any) => item?.type === '1').length : 0);
// 救护车
useStore.setTypeFive(result ? result.filter((item: any) => item?.type === '5').length : 0);
}
if (code == 200) {
if (Array.isArray(result) && result.length > 0) {
Map.createOverlay(
@@ -403,6 +392,10 @@
changeTab(list.value[currentIndex.value === 4 ? 0 : currentIndex.value], currentIndex.value);
emits('showRestB');
}
defineExpose({
resetMap,
});
</script>
<style lang="less" scoped>
@@ -767,31 +760,28 @@
.map-bottom {
border-radius: 10px;
background: #073148;
width: 570px;
height: 100px;
padding: 5px 0;
display: flex;
line-height: 30px;
> div {
width: 50%;
text-align: center;
}
.title {
color: #45ffa1;
}
.no-data {
display: flex;
flex: 1;
justify-content: center;
align-items: center;
height: 60px;
line-height: 60px;
img {
width: 20px;
height: 20px;
width: 100%;
&:nth-child(1) {
display: flex;
justify-content: center;
padding: 0 0 12px;
img {
width: 100px;
height: 70px;
}
}
&:nth-child(2) {
height: 60px;
line-height: 60px;
text-align: center;
background: rgba(242, 68, 57, 0.3);
color: #f24439;
font-size: 18px;
font-weight: bold;
}
}
}
+14 -8
View File
@@ -1,16 +1,16 @@
<template>
<Dialog class="index-modal" :openVis="visible" width="60%" :title="dialogTitle" @close="handleClose" :maskClosable="false">
<template #container>
<div v-if="useForm" class="form-container">
<div v-if="useForm && firstForm.length > 0" class="form-container">
<Form :model="formRecord" v-bind="formItemLayout">
<slot name="searchFirst"></slot>
<a-form-item v-for="(item, index) in firstForm" :label="item.label" :key="index">
<a-input :placeholder="item.placeholder" v-if="item.type == 'input'" v-model:value="formRecord[item.field]" />
<a-form-item v-for="(item, index) in firstForm" :label="item?.label" :key="index">
<a-input :placeholder="item?.placeholder" v-if="item?.type == 'input'" v-model:value="formRecord[item?.field]" />
<a-select
v-if="item.type == 'select'"
:placeholder="item.placeholder"
v-model:value="formRecord[item.field]"
:options="item.options"
v-if="item?.type == 'select'"
:placeholder="item?.placeholder"
v-model:value="formRecord[item?.field]"
:options="item?.options"
/>
</a-form-item>
<a-form-item label="单位" v-if="needDept">
@@ -48,7 +48,11 @@
:key="visible"
:columns="props.column"
:apiParams="formState"
/>
>
<template #bodyCell="{ column, record, index, text }">
<slot name="bodyCell" v-bind="{ column, record, index, text } || {}"></slot>
</template>
</BasicTable>
</template>
</Dialog>
</template>
@@ -60,6 +64,7 @@
import BasicTable from '/@/components/secondaryScreen/secondMap/components/basicTable/BasicTable.vue';
import { allSecondaryDepartsApi, getThreeDepartByOrgCodeApi } from '/@/views/indexSun/indexSun.api.ts';
import dayjs from 'dayjs';
import { isFunction } from '/@/utils/is.ts';
const props = defineProps({
title: {
@@ -307,6 +312,7 @@
}
defineExpose({
openDialog,
closeDialog,
});
</script>
<style lang="less" scoped>
+4
View File
@@ -19,6 +19,8 @@ enum Api {
getSecondaryDepartment = '/sys/sysDepart/allSecondaryDeparts',
twoRN = '/health-emergency/emergency/LargeScreenNew/loveTrend',
nearResources = '/health-emergency/emergency/LargeScreenNew/nearResources',
todayEmergencyList = '/health-emergency/emergency/LargeScreenNew/todayEmergencyList',
healthArchives = '/health-emergency/emergency/LargeScreenNew/healthArchives',
}
export const statisticsNewApi = (params: any) => post(Api.statisticsNew, params);
@@ -31,6 +33,8 @@ export const threeRApi = (params: any) => post(Api.threeR, params);
export const twoRApi = (params: any) => post(Api.twoR, params);
export const twoRNApi = (params: any) => get(Api.twoRN, params);
export const nearResourcesApi = (params: any) => get(Api.nearResources, params);
export const todayEmergencyListApi = (params: any) => post(Api.todayEmergencyList, params);
export const healthArchivesApi = (params: any) => get(Api.healthArchives, params);
export const threeRModalApi = (params: any) => post(Api.threeRModal, params);
export const twoRModal1Api = (params: any) => post(Api.twoRModal1, params);
+59
View File
@@ -816,3 +816,62 @@ export const twoRNewForm = [
defaultValue: '',
},
];
export const oneRNewColumns = [
{
title: '单位',
dataIndex: 'departName',
key: 'departName',
},
{
title: '部门',
dataIndex: 'orgName',
key: 'orgName',
},
{
title: '姓名',
dataIndex: 'realName',
key: 'name',
},
{
title: '性别',
dataIndex: 'sex',
key: 'sex',
customRender: ({ text }) => {
if (text == '1') {
return '女';
} else {
return '男';
}
},
},
{
title: '年龄',
dataIndex: 'age',
key: 'age',
},
{
title: '应急方式',
dataIndex: 'emergencyType',
key: 'emergencyType',
customRender: ({ text }) => {
switch (text) {
case '0':
return '120';
case '1':
return '紧急求助';
case '2':
return '云坐席';
case '3':
return '终端报警';
default:
return '-';
}
},
},
{
title: '操作',
dataIndex: 'cz',
key: 'cz',
},
];
+323
View File
@@ -0,0 +1,323 @@
<template>
<div class="one-r-box">
<div class="title">
<div>救助信息</div>
<!-- <div class="top-button-d">-->
<a-button size="small" class="title-button title-button-one" @click="handleView">今日应急</a-button>
<!-- </div>-->
</div>
<template v-if="props.userData && Object.keys(props.userData).length > 0">
<div class="basic-info">
<span>{{ userData.realName }}</span>
<a-button v-if="props.userData && Object.keys(props.userData).length > 0" size="small" class="title-button" @click="handleArchives"
>健康档案</a-button
>
</div>
<div class="emer-top-d-outer">
<div class="emer-top-d">
<div style="display: flex; color: #ffffff">
<div class="emer-item-d">
<div>性别/年龄</div>
<div class="emer-item-d-t">{{ getSex(userData.sex) }}/{{ userData.age || '-' }}</div>
</div>
<div class="emer-item-d">
<div>应急方式</div>
<div class="emer-item-d-t">{{ getTypeName(userData.emergencyType) }}</div>
</div>
</div>
<div style="display: flex; color: #ffffff">
<div class="emer-item-d">
<div>单位</div>
<div class="emer-item-d-t">{{ userData.departName }}</div>
</div>
<div class="emer-item-d">
<div>手机号码</div>
<div class="emer-item-d-t">{{ userData.phone }}</div>
</div>
</div>
<div class="emer-item-d">
<div>当前位置</div>
<div class="emer-item-d-t" :style="{ color: !userData?.lon || !userData?.lat ? '#F24439' : '' }">
{{ !userData?.lon || !userData?.lat ? '未获取到位置信息' : locationInfo }}
</div>
</div>
</div>
</div>
<div class="title title-two">
<span>联系人</span>
<a-button size="small" class="title-button">紧急联系人</a-button>
</div>
<div class="emer-info-outer">
<div class="emer-info">
<div>
<div class="emer-info-title">关爱联络员</div>
<template v-if="props.userData?.careLiaisonName && props.userData?.careLiaisonPhone">
<div style="color: #ffffff">{{ props.userData?.careLiaisonName }}</div>
<div style="color: #ffffff">{{ props.userData?.careLiaisonPhone }}</div>
</template>
<template v-else>
<div class="no-data"> 该员工暂未设置关爱联络员 <img :src="pointPng" alt="" /></div>
</template>
</div>
<div>
<div class="emer-info-title">急救人员</div>
<template v-if="props.userData?.lifeguardName && props.userData?.lifeguardPhone">
<div style="color: #ffffff">{{ props.userData?.lifeguardName }}</div>
<div style="color: #ffffff">{{ props.userData?.lifeguardPhone }}</div>
</template>
<template v-else>
<div class="no-data"> 该员工暂未设置急救人员 <img :src="pointPng" alt="" /></div>
</template>
</div>
</div>
</div>
</template>
<template v-else>
<Empty class="empty-d" :description="'暂无救助人员'" />
</template>
<Dialog :openVis="openInfo" width="30%" title="健康报警员工信息" @close="handlePoliceClose">
<template #container>
<div class="police-con" :class="[themeType]">
<Row style="font-size: 14px; font-weight: bold"> 个人信息 </Row>
<Row>
<Col :span="8">姓名{{ archives?.realName || '--' }}</Col>
<Col :span="8">性别{{ archives?.sex_dictText || '--' }}</Col>
<Col :span="8">年龄{{ archives?.age || '--' }}</Col>
</Row>
<Row>
<Col :span="8">二级单位{{ archives?.orgName2 || '--' }}</Col>
<Col :span="8">部门{{ archives?.orgName || '--' }}</Col>
<Col :span="8">手机号码{{ archives?.phone || '--' }}</Col>
</Row>
<Row style="font-size: 14px; font-weight: bold"> 健康信息 </Row>
<Row> 异常事件{{ archives?.eventType_dictText || '--' }} </Row>
<Row>
<Col :span="8">异常值{{ archives?.dataValue || '--' }}</Col>
<Col :span="16">发生时间{{ archives?.warnTime || '--' }}</Col>
</Row>
</div>
</template>
</Dialog>
</div>
</template>
<script setup lang="ts">
import pointPng from '/@/assets/indexSun/point.png';
import { Col, Empty, Row } from 'ant-design-vue';
import { Map } from '/@/assets/mapUtils/map.ts';
import { ref, watch } from 'vue';
import Dialog from '/@/components/dialog/Dialog.vue';
import { useTheme } from '/@/hooks/theme/useTheme.ts';
import { healthArchivesApi } from '/@/views/indexSun/indexSun.api.ts';
const props = defineProps({
userData: { type: Object, default: () => {} },
});
const { themeType } = useTheme();
const locationInfo = ref<string>('');
const emit = defineEmits(['handleView']);
const openInfo = ref<boolean>(false);
const archives = ref<object>({});
watch(
() => props.userData,
(v) => {
locationInfo.value = '';
if (v && v?.lat && v?.lat) {
getAddress();
}
}
);
function handleView() {
emit('handleView');
}
function handleArchives() {
healthArchivesApi({ userId: props.userData?.userId })
.then((res) => {})
.catch((err) => {});
}
function handlePoliceClose() {
archives.value = {};
}
function getTypeName(type: string) {
switch (type) {
case '0':
return '120';
case '1':
return '紧急求助';
case '2':
return '云坐席';
case '3':
return '终端报警';
default:
return '-';
}
}
function getSex(type: string) {
switch (type) {
case '1':
return '女';
case '2':
return '男';
default:
return '-';
}
}
function getAddress() {
Map.getAddressByPoint([props.userData?.lon, props.userData?.lat])
.then((res: string) => {
locationInfo.value = res;
})
.catch((err) => {
console.log(err);
locationInfo.value = '';
});
}
</script>
<style lang="less" scoped>
.one-r-box {
background: rgba(8, 75, 100, 0.2);
display: flex;
flex-direction: column;
letter-spacing: 0px;
.title {
height: 40px;
border-radius: 14px 14px 0 0;
font-weight: bold;
font-size: 20px;
letter-spacing: 2px;
color: #ffffff;
line-height: 40px;
padding: 0 20px;
display: flex;
align-items: center;
justify-content: space-between;
background: linear-gradient(to right, rgba(8, 75, 100, 0.5) 0%, rgba(255, 255, 255, 0) 100%);
}
}
.title-two {
border-radius: 0 !important;
}
.title-button {
color: #ffffff;
font-weight: bold;
font-size: 10px !important;
padding: 2px 10px;
background: radial-gradient(ellipse at center, #082147 0%, #08254c 55%, #085faa 100%);
border-radius: 8px;
border: 1px solid #55d3ff;
}
.title-button-one {
margin-right: 10px;
}
//.top-button-d {
// display: flex;
// align-items: center;
// button {
// &:nth-child(1) {
// margin-right: 10px;
// }
// }
//}
.basic-info {
color: #ffffff;
display: flex;
padding: 3px 30px 0;
justify-content: space-between;
span {
color: #c9daff;
font-size: 18px;
font-weight: bold;
}
}
.emer-top-d-outer {
padding: 10px 20px;
}
.emer-top-d {
color: #ffffff;
padding: 10px 10px;
border-radius: 2px;
background: #152657;
.emer-item-d {
display: flex;
flex: 1;
padding: 2px 0;
> div {
&:nth-child(1) {
color: #82a9ef;
}
&:nth-child(2) {
color: #c9daff;
}
}
.emer-item-d-t {
white-space: wrap;
word-break: break-all;
flex: 1;
padding-right: 10px;
}
}
}
.emer-info-outer {
padding: 10px 20px;
}
.emer-info {
border-radius: 2px;
background: #152657;
height: 100px;
padding: 5px 0;
display: flex;
line-height: 30px;
> div {
width: 50%;
text-align: center;
}
.emer-info-title {
color: #45ffa1;
}
.no-data {
display: flex;
flex: 1;
justify-content: center;
align-items: center;
height: 60px;
line-height: 60px;
color: #ffffff;
img {
width: 20px;
height: 20px;
}
}
}
.empty-d {
padding: 10px 0;
:deep(.ant-empty-image) {
margin-top: 5px;
height: 30px;
}
:deep(.ant-empty-description) {
margin-bottom: 5px;
}
}
</style>
+468
View File
@@ -0,0 +1,468 @@
<template>
<div class="two-l-box">
<div class="title">
<span>心血管监测</span>
</div>
<div style="flex: 1" class="content">
<div class="content-search content-search-select">
<a-select v-model:value="selectValue" class="type-select" :options="selectOptions" @change="getData">
<a-select-option value="全部数据">全部数据</a-select-option>
</a-select>
<a-select v-model:value="selectValue1" class="type-select" :options="selectOption1" @change="getData" />
</div>
<div class="content-search content-search-new">
<div class="type-time">
<div
v-for="(item, index) in tabList"
@click="clickChooseType(item.value)"
:class="[chooseType === item.value ? 'selected' : 'no-selected']"
:key="`tab-lef-list-key-${index}`"
>
{{ item.label }}
<div>
<div v-if="chooseType === item.value" class="triangle"></div>
</div>
</div>
</div>
</div>
<div style="display: flex; flex-direction: column; height: 100%; padding: 5px; justify-content: center">
<div class="item-title" style="width: 99%">
<div class="title chart-title" style="display: flex; font-weight: normal !important">
<div> <img :src="imgList[chooseType]" alt="" />{{ tabList[chooseType].label }}</div>
<div v-if="[0, 1].includes(chooseType)"> 参考范围: {{ tabList[chooseType].tips }} </div>
</div>
<div style="height: 210px">
<div style="height: 100%" class="content-chart">
<template v-if="[0, 1, 2, 3].includes(chooseType)">
<three-left-new-item :choose-type="chooseType" />
</template>
<template v-if="[4].includes(chooseType)">
<sleep-item :orgCode="selectValue" :type="selectValue1" style="height: 100%" />
</template>
<template v-if="[5].includes(chooseType)">
<step-item :orgCode="selectValue" :type="selectValue1" style="height: 100%" />
</template>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted, watch, reactive, onBeforeUnmount, nextTick } from 'vue';
import * as echarts from 'echarts';
import { getClassCharts } from '/@/views/indexSun/indexSun.ts';
import { allSecondaryDepartsApi, twoLApi } from '/@/views/indexSun/indexSun.api.ts';
import { useUserStore } from '/@/store/modules/user.ts';
import ThreeLeftNewItem from '/src/views/indexSun/threeLeftNewItem/threeLeftNewItem.vue';
import SleepItem from '/@/views/indexSun/threeLeftNewItem/sleepItem.vue';
import StepItem from '/@/views/indexSun/threeLeftNewItem/stepItem.vue';
import item0Png from '/@/assets/images/threeLeftNew/item0.png';
import item1Png from '/@/assets/images/threeLeftNew/item1.png';
import item2Png from '/@/assets/images/threeLeftNew/item2.png';
import item3Png from '/@/assets/images/threeLeftNew/item3.png';
import item4Png from '/@/assets/images/threeLeftNew/item4.png';
import item5Png from '/@/assets/images/threeLeftNew/item5.png';
import item6Png from '/@/assets/images/threeLeftNew/item6.png';
const { getCenterInfo } = useUserStore();
const props = defineProps({
bKey: {
type: String,
default: () => '',
},
});
const imgList = reactive([item0Png, item1Png, item2Png, item3Png, item4Png, item5Png, item6Png]);
const tabList = reactive([
{
label: '心率',
value: 0,
tips: '正常范围60~100次/分',
},
{
label: '血氧',
value: 1,
tips: '血氧饱和度范围95%~100%',
},
{
label: '血压',
value: 2,
},
{
label: '体温',
value: 3,
},
{
label: '睡眠',
value: 4,
},
{
label: '步数',
value: 5,
},
]);
const chooseType = ref(0);
const selectValue = ref<string>('');
const timer = ref<any>(null);
const selectOptions = ref([{ label: '全部数据', value: '' }]);
const selectValue1 = ref('week');
const selectOption1 = ref([
{ label: '近一周', value: 'week' },
{ label: '近一月', value: 'month' },
{ label: '近一年', value: 'year' },
]);
const newChart = ref<HTMLElement>();
function clickChooseType(type: any) {
if (chooseType.value === type) return;
chooseType.value = type;
getData();
}
onMounted(() => {
if (selectOptions.value.length === 1) {
getAllDepart();
}
setTimer();
});
watch(props, () => {
selectValue.value = '';
getData();
});
function setTimer() {
if (timer.value) {
clearInterval(timer.value);
}
chooseType.value = 0;
getData();
timer.value = setInterval(() => {
if (chooseType.value !== 5) {
chooseType.value += 1;
} else {
chooseType.value = 0;
}
getData();
}, 5000);
}
onBeforeUnmount(() => {
timer.value && clearInterval(timer.value);
});
function getAllDepart() {
allSecondaryDepartsApi({})
.then((res: any) => {
if (res.code === 200) {
const op: any[] = res?.result
? res.result.map((it: any) => {
return {
label: it.departName,
value: it.orgCode,
};
})
: [];
selectOptions.value = selectOptions.value.concat(op);
} else {
}
})
.catch((err) => {
console.log(err);
});
}
const myChart = ref();
function getData() {
twoLApi({
sex: chooseType.value,
depart: selectValue.value,
centerId: getCenterInfo?.id,
})
.then((res: any) => {
if (res.code == 200) {
initChart(res.result);
}
})
.catch((err) => {
console.log(err);
});
}
function initChart(value: any) {
nextTick(() => {
myChart.value = echarts.init(newChart.value!);
const dx =
value.map((item: any) => {
return item.name;
}) || [];
const dy =
value.map((item: any) => {
return item.count;
}) || [];
const max =
value.sort((a: any, b: any) => b.count - a.count).length > 0 ? value.sort((a: any, b: any) => b.count - a.count)[0].count : [];
const m = max % 25 === 0 ? max : (parseInt(max / 25) + 1) * 25;
let option = getClassCharts(dx, dy, m);
myChart.value.setOption(option);
window.addEventListener('resize', () => {
myChart.value.resize();
});
});
}
const emit = defineEmits(['handleView']);
function handleView() {
emit('handleView');
}
</script>
<style lang="less" scoped>
.two-l-box {
display: flex;
flex-direction: column;
background: rgba(8, 75, 100, 0.2);
.title {
height: 40px;
border-radius: 25px 25px 0 0;
font-weight: bold;
font-size: 20px;
letter-spacing: 2px;
color: #ffffff;
line-height: 40px;
padding: 0 20px;
display: flex;
align-items: center;
justify-content: space-between;
background: linear-gradient(to right, rgba(8, 75, 100, 0.5) 0%, rgba(255, 255, 255, 0) 100%);
.look-view {
font-size: 14px;
cursor: pointer;
}
img {
width: 28px;
height: 24px;
margin: 0 5px 2px 0;
}
}
.content {
height: calc(100% - 40px);
display: flex;
flex-direction: column;
.content-search {
display: flex;
justify-content: space-between;
align-items: center;
margin: 5px 10px;
.type-select {
width: 200px;
}
}
.content-search-new {
display: inline;
.type-time {
justify-content: space-between;
margin: 7px 0 !important;
font-size: 12px;
> div {
padding: 3px 10px;
}
}
}
.content-search-select {
> div {
margin-top: 5px;
&:nth-child(1) {
flex: 1;
}
&:nth-child(2) {
width: 100px;
margin-left: 20px;
}
}
}
.type-time {
font-size: 14px;
display: flex;
justify-content: space-between;
> div {
cursor: pointer;
padding: 3px 15px;
display: flex;
align-items: center;
justify-content: center;
//width: 60px;
}
.selected {
color: #00ccff;
background: rgba(0, 204, 255, 0.2);
position: relative;
&:before {
content: '';
position: absolute;
top: 0;
left: 0;
width: 8px; /* 线条宽度 */
height: 8px; /* 线条高度 */
border-top: 3px solid #00ccff; /* 顶部线条 */
border-left: 3px solid #00ccff; /* 左侧线条 */
}
&:after {
content: '';
position: absolute;
bottom: 0;
right: 0;
width: 8px; /* 线条宽度 */
height: 8px; /* 线条高度 */
border-bottom: 3px solid #00ccff; /* 顶部线条 */
border-right: 3px solid #00ccff; /* 左侧线条 */
}
> div {
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
&:before {
content: '';
position: absolute;
top: 0;
right: 0;
width: 8px; /* 线条宽度 */
height: 8px; /* 线条高度 */
border-top: 3px solid #00ccff; /* 顶部线条 */
border-right: 3px solid #00ccff; /* 左侧线条 */
}
&:after {
content: '';
position: absolute;
bottom: 0;
left: 0;
width: 8px; /* 线条宽度 */
height: 8px; /* 线条高度 */
border-bottom: 3px solid #00ccff; /* 顶部线条 */
border-left: 3px solid #00ccff; /* 左侧线条 */
}
}
}
.no-selected {
color: #b6b6b6;
background: rgba(26, 69, 126, 0.2);
position: relative;
&:before {
content: '';
position: absolute;
top: 0;
left: 0;
width: 8px; /* 线条宽度 */
height: 8px; /* 线条高度 */
border-top: 3px solid #1a457e; /* 顶部线条 */
border-left: 3px solid #1a457e; /* 左侧线条 */
}
&:after {
content: '';
position: absolute;
bottom: 0;
right: 0;
width: 8px; /* 线条宽度 */
height: 8px; /* 线条高度 */
border-bottom: 3px solid #1a457e; /* 顶部线条 */
border-right: 3px solid #1a457e; /* 左侧线条 */
}
> div {
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
&:before {
content: '';
position: absolute;
top: 0;
right: 0;
width: 8px; /* 线条宽度 */
height: 8px; /* 线条高度 */
border-top: 3px solid #1a457e; /* 顶部线条 */
border-right: 3px solid #1a457e; /* 左侧线条 */
}
&:after {
content: '';
position: absolute;
bottom: 0;
left: 0;
width: 8px; /* 线条宽度 */
height: 8px; /* 线条高度 */
border-bottom: 3px solid #1a457e; /* 顶部线条 */
border-left: 3px solid #1a457e; /* 左侧线条 */
}
}
}
.triangle {
position: absolute;
left: 50%;
bottom: 0;
transform: translateX(-50%);
width: 0;
height: 0;
border-left: 5px solid transparent; /* 左边透明 */
border-right: 5px solid transparent; /* 右边透明 */
border-bottom: 6px solid #00ccff; /* 底部为黑色,形成三角形 */
}
}
.server-container {
color: #fff;
flex: 1;
display: flex;
flex-direction: column;
justify-content: space-around;
overflow-y: auto;
position: relative;
font-size: 16px;
font-weight: bold;
letter-spacing: 2px;
margin-top: 10px;
padding-bottom: 10px;
.spin {
position: absolute;
left: 50%;
top: 50%;
}
}
.server-item {
// font-size: 16px;
padding: 5px 3% 5px 5%;
&.hasCursor {
cursor: pointer;
}
display: flex;
justify-content: space-between;
}
.content-chart {
width: 100%;
margin: 0 auto;
flex: 1;
.charts {
height: 100%;
}
}
}
}
.item-title {
background: url('/@/assets/images/threeLeftNew/item-bg.png') no-repeat !important;
background-size: 100% 100% !important;
}
.chart-title {
font-size: 16px !important;
}
</style>
@@ -0,0 +1,120 @@
<template>
<div class="left-pie" style="height: 100%">
<div class="inner" ref="healthChart"></div>
<div class="count">
<div class="t">总人数</div>
<div class="v" style="text-align: center">{{ userCount }}</div>
</div>
</div>
</template>
<script setup lang="ts">
import { circleRing } from '/src/views/indexSun/threeLeftNewItem/pieCharts.ts';
import { nextTick, onMounted, ref, watch } from 'vue';
import * as echarts from 'echarts';
const props = defineProps({
title: {
type: String,
default: () => '',
},
data: {
type: Array,
default: () => [],
},
color: {
type: Array,
default: () => [],
},
tips: {
type: String,
default: () => '',
},
userCount: {
type: Number,
},
themeType: {
type: String,
default: () => '',
},
});
const healthChart = ref<HTMLElement>();
onMounted(() => {
init();
});
watch(
() => props.data,
() => {
init();
},
{
deep: true,
}
);
async function init() {
initChart(props.data, props.userCount, props.color);
}
function initChart(chartData, total, color) {
nextTick(() => {
let myChart = echarts.init(healthChart.value);
let options = circleRing(chartData, total, color);
myChart.setOption(options);
window.addEventListener('resize', () => {
myChart.resize();
});
});
}
</script>
<style scoped lang="less">
.inner {
width: 100%;
margin: 0 auto;
height: 100%;
}
.inner::after {
//content: '';
position: absolute;
left: 0;
top: 0;
width: 20px;
height: 20px;
background-color: #051a2b;
}
.left-pie {
position: relative;
padding-bottom: 0 !important;
}
.count {
position: absolute;
//left: 41%;
//top: 30%;
bottom: 0;
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
color: #fff;
font-size: 22px;
font-weight: bold;
z-index: 1;
pointer-events: none;
> div {
width: 80px;
text-align: center;
z-index: 0;
}
.t {
font-size: 14px;
font-weight: normal;
}
}
</style>
@@ -0,0 +1,97 @@
// 步数
import { visibleChartLabel } from '/@/utils/utils.ts';
export function screenPie(chartData) {
const data = visibleChartLabel(chartData).map((item) => {
// item = { ...item, label: { show: item.value > 0 }, labelLine: { show: item.value > 0 } };
return item;
});
return {
color: ['#B750BE', '#6C63F0', '#3AACFF', '#ED589D', '#FB466C'],
tooltip: {
trigger: 'item',
show: true,
textStyle: {
color: '#fff',
},
backgroundColor: 'rgba(50,50,50,0.7)',
borderColor: '#1b7ef2',
},
series: [
{
type: 'pie',
radius: ['50%', '72%'],
center: ['50%', '50%'],
avoidLabelOverlap: false,
label: {
show: true,
position: 'outside',
formatter: function (params: any) {
return `${params.name}\n占比:${params.percent}%\n人次:${params.value}`;
},
color: '#ffffff',
fontSize: 14,
},
labelLine: {
show: true,
length: 2,
length2: 30,
minTurnAngle: 120,
},
data: data,
},
],
};
}
//左侧圆
export function circleRing(chartData, total, color) {
const data = visibleChartLabel(chartData);
return {
color: color,
tooltip: {
trigger: 'item',
show: true,
textStyle: {
color: '#fff',
},
backgroundColor: 'rgba(50,50,50,0.7)',
borderColor: '#1b7ef2',
},
series: [
{
type: 'pie',
radius: ['50%', '75%'],
center: ['50%', '50%'],
avoidLabelOverlap: false,
// label: {
// show: true,
// // position: 'inner',
// formatter: '{d}%',
// color: '#ffffff',
// fontSize: 14,
// },
// labelLine: {
// show: false,
// },
label: {
show: true,
position: 'outside',
// formatter: '{a}{d}%\n{c}人次',
formatter: function (params: any) {
return `${params.name}:${params.percent}%\n${params.value}`;
},
color: '#ffffff',
fontSize: 14,
},
labelLine: {
show: true,
length: 2,
length2: 30,
minTurnAngle: 120,
},
data: data,
},
],
};
}
@@ -0,0 +1,112 @@
<template>
<div class="terminal-container self">
<div class="inner">
<div class="title">
<span class="lightSleep">平均睡眠 {{ nums.avg || 0 }} 深睡 {{ nums.avgLong || 0 }} 浅睡 {{ nums.avgShort || 0 }}</span>
</div>
<div class="sleepChart" ref="sleepChart"></div>
</div>
</div>
</template>
<script setup lang="ts">
import * as echarts from 'echarts';
import { ref, watch } from 'vue';
import { formatStr, sleepCharts } from '/@/components/secondaryScreen/screen-right/screenRightHooks';
import { getSleep } from '/@/components/secondaryScreen/commonApi.ts';
import { useSession, caching } from '/@/hooks/autoRefresh/caching.ts';
const { setSession, getSession } = useSession();
const { PAGE2_RIGHT1 } = caching;
const props = defineProps({
orgCode: String,
type: String,
themeType: String,
});
const sleepChart = ref<HTMLElement>();
watch(
props,
() => {
init();
},
{ immediate: true, deep: true }
);
const nums = ref({
avgShort: '',
avgLong: '',
avg: '',
});
async function init() {
try {
const params = {
orgCode: props.orgCode,
type: props.type,
};
const { code, result } = await getSleep(params);
if (code == 200 && result.length > 0) {
setSession(PAGE2_RIGHT1, JSON.stringify(result));
setValue(result);
}
} catch {
const result = JSON.parse(getSession(PAGE2_RIGHT1));
setValue(result);
}
}
function setValue(result: any) {
let time = result?.map((item: any) => item.dataDate);
let short = result?.map((item: any) => item.avgShortDuration);
let long = result?.map((item: any) => item.avgLongDuration);
let awake = result?.map((item: any) => item.awakeDuration);
let avg = result?.map((item: any) => item.averageDuration);
initChart({ time, short, long, awake, type: props.type });
const len = time.length;
if (!len > 0) {
return;
}
nums.value = {
avgShort: formatStr({ list: short, len }),
avgLong: formatStr({ list: long, len }),
avg: formatStr({ list: avg, len }), //平均
};
}
function initChart({ time, short, long, awake, type }) {
let myChart = echarts.init(sleepChart.value);
let options = sleepCharts({ time, short, long, awake, type });
myChart.setOption(options);
window.addEventListener('resize', () => {
myChart.resize();
});
}
</script>
<style scoped lang="less">
.terminal-container.self {
position: relative;
padding-bottom: 0 !important;
}
.inner {
height: 100%;
.title {
color: #cfcfcf;
text-align: left;
height: 30px;
line-height: 30px;
.count {
font-size: 28px;
font-weight: bold;
}
.lightSleep {
padding: 3px 0 3px 2%;
}
}
.sleepChart {
height: 90%;
}
}
</style>
@@ -0,0 +1,105 @@
<template>
<div class="pie-container self">
<div class="inner" ref="healthChart"></div>
<div class="count">
<div class="t">总人数</div>
<div class="v">{{ total }}</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, watch } from 'vue';
import * as echarts from 'echarts';
import { useStep } from '/@/components/secondaryScreen/screen-right/screenRightHooks.ts';
import { screenPie } from '/@/views/indexSun/threeLeftNewItem/pieCharts.ts';
import { getStepNew } from '/@/components/secondaryScreen/commonApi.ts';
import { caching, useSession } from '/@/hooks/autoRefresh/caching.ts';
const props = defineProps({
orgCode: String,
type: String,
themeType: String,
});
const { setSession, getSession } = useSession();
const { PAGE2_RIGHT2 } = caching;
watch(
props,
() => {
init();
},
{ immediate: true, deep: true }
);
const healthChart = ref<HTMLElement>();
const { total, setStep, getStepData } = useStep();
async function init() {
try {
const params = {
orgCode: props.orgCode,
type: props.type,
};
// const { code, result } = await getStep(params);
const { code, result } = await getStepNew(params);
if (code == 200) {
setValue(result);
setSession(PAGE2_RIGHT2, JSON.stringify(result));
}
} catch {
const result = JSON.parse(getSession(PAGE2_RIGHT2));
setValue(result);
}
}
function setValue(result: any) {
setStep(result);
initChart(getStepData());
}
function initChart(chartData: any) {
let myChart = echarts.init(healthChart.value);
let options = screenPie(chartData);
myChart.setOption(options);
window.addEventListener('resize', () => {
myChart.resize();
});
}
</script>
<style scoped lang="less">
.inner {
width: 100%;
margin: 0 auto;
height: 100%;
}
.pie-container.self {
position: relative;
padding-bottom: 0 !important;
}
.count {
position: absolute;
bottom: 0;
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
color: #fff;
font-size: 22px;
font-weight: bold;
z-index: 1;
pointer-events: none;
> div {
width: 80px;
text-align: center;
z-index: 0;
}
.t {
font-size: 14px;
font-weight: normal;
}
}
</style>
@@ -0,0 +1,129 @@
<template>
<item-d
:themeType="themeType"
:keu="spinning"
:title="dataList[props.chooseType].title"
:data="dataList[props.chooseType].data"
:tips="dataList[props.chooseType].tips"
:color="dataList[props.chooseType].color"
:user-count="dataList[props.chooseType].userCount"
/>
</template>
r
<script setup lang="ts">
import { onMounted, ref, watch } from 'vue';
import { cloneDeep } from 'lodash-es';
import { getWatchDataByOrgCodeNew } from '/@/components/secondaryScreen/commonApi.ts';
import { useSpin } from '/@/components/body-d-left/bodyLeftHooks.ts';
import ItemD from '/src/views/indexSun/threeLeftNewItem/item-d.vue';
const { spinning, setSpin } = useSpin();
const dataList = ref([
{
title: '心率',
data: [
{ name: '正常', value: 0, key: 'normalCount' },
{ name: '警戒', value: 0, key: 'warnCount' },
{ name: '危险', value: 0, key: 'riskCount' },
],
type: '1',
color: ['#0098FA', '#FF6648', '#FB466C'],
tips: '正常范围60~100次/分',
userCount: 0,
},
{
title: '血氧',
data: [
{ name: '正常', value: 0, key: 'normalCount' },
{ name: '警戒', value: 0, key: 'warnCount' },
{ name: '危险', value: 0, key: 'riskCount' },
],
type: '2',
color: ['#0098FA', '#FF6648', '#FB466C'],
tips: '血氧饱和度范围95%~100%',
userCount: 0,
},
{
title: '压力',
data: [
{ name: '正常', value: 0, key: 'normalCount' },
{ name: '放松', value: 0, key: 'unwindCount' },
{ name: '中等', value: 0, key: 'warnCount' },
{ name: '偏高', value: 0, key: 'riskCount' },
],
type: '3',
color: ['#1DCC79', '#0098FA', '#0CD9B5', '#6C63F0'],
tips: '',
userCount: 0,
},
{
title: '体温',
data: [
{ name: '<37.2', value: 0, key: 'normalCount' },
{ name: '37.2-38', value: 0, key: 'warnCount' },
{ name: '>38', value: 0, key: 'riskCount' },
],
type: '5',
color: ['#0098FA', '#0CD9B5', '#3B72AD'],
tips: '',
userCount: 0,
},
]);
const props = defineProps({
orgCode: String,
type: String,
themeType: String,
refreshState: {
type: Boolean,
default: () => false,
},
chooseType: {
type: Number,
default: () => 0,
},
});
onMounted(() => {
init();
});
watch(props, () => {
init();
});
async function init() {
setSpin(true);
try {
// const { code, result } = await getWatchDataByOrgCode(props);
const { code, result } = await getWatchDataByOrgCodeNew(props);
if (code == 200) {
setValue(result);
// setSession(PAGE2_LEFT, JSON.stringify(result));
}
} catch {
// const result = JSON.parse(getSession(PAGE2_LEFT));
// setValue(result);
}
}
function setValue(result) {
setSpin(false);
let data = cloneDeep(dataList.value);
data.forEach((item) => {
let resData = result.find((v) => v.type == item.type);
item.userCount = resData.userCount;
item.data = item.data.map((val) => {
val.value = resData[val.key];
val = { ...val, label: { show: val.value > 0 }, labelLine: { show: val.value > 0 } };
return val;
});
});
dataList.value = data;
}
defineExpose({
init,
});
</script>
<style scoped></style>
@@ -56,7 +56,7 @@
import { twoRNApi } from '/@/views/indexSun/indexSun.api.ts';
import dayjs from 'dayjs';
import { Row, Col } from 'ant-design-vue';
import { getName, list } from '/@/views/indexSun/rowNewR.data.ts';
import { getName, list } from '/@/views/indexSun/twoNewR.data.ts';
const { themeType } = useTheme();
const step = ref(0.2);