2025年04月30日18:18:19
This commit is contained in:
parent
dd4d7b2412
commit
65bcb4b974
36
src/api/index.d.ts
vendored
36
src/api/index.d.ts
vendored
@ -185,6 +185,7 @@ export namespace TDevice {
|
|||||||
battery: number
|
battery: number
|
||||||
otaFlag: number
|
otaFlag: number
|
||||||
adminUsername: string
|
adminUsername: string
|
||||||
|
adminName: string
|
||||||
adminPhone: string
|
adminPhone: string
|
||||||
userNumber: string
|
userNumber: string
|
||||||
adminType: string
|
adminType: string
|
||||||
@ -247,7 +248,7 @@ export namespace TDevice {
|
|||||||
createTime: string
|
createTime: string
|
||||||
}
|
}
|
||||||
export interface IWarningRecordRes {
|
export interface IWarningRecordRes {
|
||||||
id: number
|
id: number | string
|
||||||
userNumber: string
|
userNumber: string
|
||||||
deviceId: string
|
deviceId: string
|
||||||
warnType: number
|
warnType: number
|
||||||
@ -414,4 +415,37 @@ export namespace TRoleMenuList {
|
|||||||
menus: TMenus[]
|
menus: TMenus[]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
export namespace TDataRecord {
|
||||||
|
interface TMenus {
|
||||||
|
id: number
|
||||||
|
type: number
|
||||||
|
name: string
|
||||||
|
createTime: string
|
||||||
|
}
|
||||||
|
interface ThealthData {
|
||||||
|
temp: number
|
||||||
|
hr: number
|
||||||
|
time: number
|
||||||
|
bo: number
|
||||||
|
}
|
||||||
|
|
||||||
|
interface TReq {
|
||||||
|
useRecordId: number | string
|
||||||
|
}
|
||||||
|
interface TRes {
|
||||||
|
id: number
|
||||||
|
userNumber: number
|
||||||
|
adminName: string
|
||||||
|
username: number
|
||||||
|
adminPhone: number
|
||||||
|
deviceId: number
|
||||||
|
warnType: number
|
||||||
|
type: number
|
||||||
|
orgName: string
|
||||||
|
value: number
|
||||||
|
maxValue: number
|
||||||
|
minValue: number
|
||||||
|
healthData: ThealthData[]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import request from '../utils/request';
|
import request from '../utils/request';
|
||||||
import { TLogin, TAccount, IpagingRes, TDevice, TOrg, TRoleList, statisticsContentReq, statisticsContentRes, TStatisticsCount, TWarnRecord, TWarningDetail, TWarningConfirm, TDeviceConfigModify, TDeviceConfig, THealthLatestData, TLocateRecord, TSetUseStatus, TRoleMenuList, TRoleModify, TbindWeb, TstatisticsUseCount, TAccountSetStatus } from "./index.d";
|
import { TLogin, TAccount, IpagingRes, TDevice, TOrg, TRoleList, statisticsContentReq, statisticsContentRes, TStatisticsCount, TWarnRecord, TWarningDetail, TWarningConfirm, TDeviceConfigModify, TDeviceConfig, THealthLatestData, TLocateRecord, TSetUseStatus, TRoleMenuList, TRoleModify, TbindWeb, TstatisticsUseCount, TAccountSetStatus, TDataRecord } from "./index.d";
|
||||||
|
|
||||||
export const fetchLogin = (p: TLogin.Ireq): Promise<TLogin.IRes> => {
|
export const fetchLogin = (p: TLogin.Ireq): Promise<TLogin.IRes> => {
|
||||||
return request({
|
return request({
|
||||||
@ -378,4 +378,13 @@ export const roleMenuList = (): Promise<TRoleMenuList.TRes[]> => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 获取最新健康数据使用记录
|
||||||
|
export const dataRecord = (params: TDataRecord.TReq): Promise<TDataRecord.TRes> => {
|
||||||
|
return request({
|
||||||
|
url: '/v1/api/health/data/record',
|
||||||
|
method: 'get',
|
||||||
|
params
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
BIN
src/assets/img/fullScreen.png
Normal file
BIN
src/assets/img/fullScreen.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 294 B |
BIN
src/assets/img/narrow.png
Normal file
BIN
src/assets/img/narrow.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 304 B |
@ -60,11 +60,11 @@
|
|||||||
</template>
|
</template>
|
||||||
<template #default="{ row, column, $index }" v-if="!item.type">
|
<template #default="{ row, column, $index }" v-if="!item.type">
|
||||||
<slot :name="item.prop" :rows="row" :index="$index">
|
<slot :name="item.prop" :rows="row" :index="$index">
|
||||||
<template v-if="item.prop == 'operator'">
|
<!-- <template v-if="item.prop == 'operator'">
|
||||||
<el-button type="primary" size="small" :icon="Edit" @click="editFunc(row)"> 编辑 </el-button>
|
<el-button type="primary" size="small" :icon="Edit" @click="editFunc(row)"> 编辑 </el-button>
|
||||||
<el-button type="danger" size="small" :icon="Delete" @click="handleDelete(row)"> 删除 </el-button>
|
<el-button type="danger" size="small" :icon="Delete" @click="handleDelete(row)"> 删除 </el-button>
|
||||||
</template>
|
</template> -->
|
||||||
<span v-else-if="item.formatter">
|
<span v-if="item.formatter">
|
||||||
{{ item.formatter(row[item.prop]) }}
|
{{ item.formatter(row[item.prop]) }}
|
||||||
</span>
|
</span>
|
||||||
<span v-else>
|
<span v-else>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { computed } from "vue";
|
import { computed, onBeforeMount, onMounted, onUnmounted, ref } from "vue";
|
||||||
|
|
||||||
export function useVModel(props, propsName, emit) {
|
export function useVModel(props, propsName, emit) {
|
||||||
return computed({
|
return computed({
|
||||||
@ -18,4 +18,84 @@ export function useVModel(props, propsName, emit) {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
// 全屏
|
||||||
|
export function useFullScreen() {
|
||||||
|
const isFullScreen = ref(false)
|
||||||
|
const toggleFullScreen = (domId: string) => {
|
||||||
|
if (!isFullScreen.value) {
|
||||||
|
requestFullScreen(domId);
|
||||||
|
} else {
|
||||||
|
exitFull();
|
||||||
|
}
|
||||||
|
isFullScreen.value = !isFullScreen.value;
|
||||||
|
}
|
||||||
|
//全屏
|
||||||
|
const requestFullScreen = (el) => {
|
||||||
|
const element: any = document.querySelector(`#${el}`);
|
||||||
|
let win: any = window
|
||||||
|
var requestMethod =
|
||||||
|
element.requestFullScreen || //W3C
|
||||||
|
element.webkitRequestFullScreen || //Chrome等
|
||||||
|
element.mozRequestFullScreen || //FireFox
|
||||||
|
element.msRequestFullScreen; //IE11
|
||||||
|
|
||||||
|
if (requestMethod) {
|
||||||
|
requestMethod.call(element);
|
||||||
|
} else if ("ActiveXObject" in window) {
|
||||||
|
//for Internet Explorer
|
||||||
|
var wscript = win.ActiveXObject("WScript.Shell");
|
||||||
|
if (wscript !== null) {
|
||||||
|
wscript.SendKeys("{F11}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
//退出全屏
|
||||||
|
const exitFull = () => {
|
||||||
|
// 判断各种浏览
|
||||||
|
let doc: any = document
|
||||||
|
let win: any = window
|
||||||
|
var exitMethod =
|
||||||
|
doc.exitFullscreen || //W3C
|
||||||
|
doc.mozCancelFullScreen || //Chrome等
|
||||||
|
doc.webkitExitFullscreen; //FireFox
|
||||||
|
if (exitMethod) {
|
||||||
|
exitMethod.call(doc);
|
||||||
|
} else if (typeof win.ActiveXObject !== "undefined") {
|
||||||
|
var wscript = new win.ActiveXObject("WScript.Shell");
|
||||||
|
if (wscript !== null) {
|
||||||
|
wscript.SendKeys("{F11}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const checkFull = () => {
|
||||||
|
let doc: any = document
|
||||||
|
//判断浏览器是否处于全屏状态 (需要考虑兼容问题)
|
||||||
|
var flag = doc.mozFullScreen ||
|
||||||
|
doc.fullScreen ||
|
||||||
|
//谷歌浏览器及Webkit内核浏览器
|
||||||
|
doc.webkitIsFullScreen ||
|
||||||
|
doc.webkitRequestFullScreen ||
|
||||||
|
doc.mozRequestFullScreen ||
|
||||||
|
doc.msFullscreenEnabled
|
||||||
|
if (flag === undefined) {
|
||||||
|
isFullScreen.value = false
|
||||||
|
}
|
||||||
|
return flag;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const fullChange = () => {
|
||||||
|
if (!checkFull() && isFullScreen.value) {
|
||||||
|
isFullScreen.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onMounted(() => {
|
||||||
|
window.addEventListener('resize', fullChange)
|
||||||
|
});
|
||||||
|
onUnmounted(() => {
|
||||||
|
window.removeEventListener('resize', fullChange)
|
||||||
|
});
|
||||||
|
return { isFullScreen, toggleFullScreen };
|
||||||
}
|
}
|
@ -10,7 +10,7 @@ const showErrorNotification = throttle((message: string) => {
|
|||||||
|
|
||||||
const service: AxiosInstance = axios.create({
|
const service: AxiosInstance = axios.create({
|
||||||
baseURL: import.meta.env.VITE_APP_URL,
|
baseURL: import.meta.env.VITE_APP_URL,
|
||||||
timeout: 5000
|
timeout: 20000
|
||||||
});
|
});
|
||||||
|
|
||||||
service.interceptors.request.use(
|
service.interceptors.request.use(
|
||||||
|
@ -80,7 +80,9 @@ const handleSearch = () => {
|
|||||||
let columns = ref([
|
let columns = ref([
|
||||||
{ type: "index", label: "序号", width: 55, align: "center" },
|
{ type: "index", label: "序号", width: 55, align: "center" },
|
||||||
{ prop: "deviceId", label: "手铐IMEI号" },
|
{ prop: "deviceId", label: "手铐IMEI号" },
|
||||||
{ prop: "userNumber", label: "关联人员" },
|
{ prop: "adminName", label: "绑定用户" },
|
||||||
|
{ prop: "username", label: "警员号" },
|
||||||
|
{ prop: "userNumber", label: "佩戴者" },
|
||||||
{ prop: "warnType", label: "事件类型" },
|
{ prop: "warnType", label: "事件类型" },
|
||||||
{ prop: "createTime", label: "时间" },
|
{ prop: "createTime", label: "时间" },
|
||||||
{ prop: "status", label: "处理状态" },
|
{ prop: "status", label: "处理状态" },
|
||||||
|
@ -18,8 +18,9 @@
|
|||||||
<div class="info-text">
|
<div class="info-text">
|
||||||
告警类型:<span style="color: red">{{ warnTypeEnum[curData.warnType] }}</span>
|
告警类型:<span style="color: red">{{ warnTypeEnum[curData.warnType] }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="info-text">绑定关联人名称:{{ curData.username }}</div>
|
<div class="info-text">绑定用户:{{ curData.adminName }}</div>
|
||||||
<div class="info-text">绑定关联人警号:{{ curData.userNumber }}</div>
|
<div class="info-text">绑定警员号:{{ curData.username }}</div>
|
||||||
|
<div class="info-text">佩戴者:{{ curData.userNumber || "--" }}</div>
|
||||||
<div class="info-text">
|
<div class="info-text">
|
||||||
状态:
|
状态:
|
||||||
<el-tag :type="curData.status == 1 ? 'success' : 'danger'">
|
<el-tag :type="curData.status == 1 ? 'success' : 'danger'">
|
||||||
@ -385,7 +386,7 @@ onUnmounted(() => {
|
|||||||
|
|
||||||
.info-text {
|
.info-text {
|
||||||
color: #061451;
|
color: #061451;
|
||||||
font-size: 18px;
|
font-size: 16px;
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
}
|
}
|
||||||
.info-box {
|
.info-box {
|
||||||
|
@ -39,7 +39,11 @@
|
|||||||
{{ item.adminType }}
|
{{ item.adminType }}
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<span class="lable">警号:</span>
|
<span class="lable">用户名称:</span>
|
||||||
|
{{ item.adminName }}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span class="lable">警员号:</span>
|
||||||
{{ item.adminUsername }}
|
{{ item.adminUsername }}
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
|
@ -1,5 +1,10 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="monitoringMap" id="mapcontainer"></div>
|
<div class="monitoringMap" id="mapcontainer">
|
||||||
|
<div class="toolbox" @click="toggleFullScreen('mapcontainer')">
|
||||||
|
<img :src="fullScreen" alt="" v-if="!isFullScreen" />
|
||||||
|
<img :src="narrow" alt="" v-else />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div :style="{ display: 'none' }">
|
<div :style="{ display: 'none' }">
|
||||||
<InfoWindow class="infoBox" :value="locationInfo" @close="InfoWin.close()" />
|
<InfoWindow class="infoBox" :value="locationInfo" @close="InfoWin.close()" />
|
||||||
</div>
|
</div>
|
||||||
@ -13,11 +18,14 @@ import InfoWindow from "@/components/InfoWindow.vue";
|
|||||||
import ViaMarker from "@/assets/img/via-marker.png";
|
import ViaMarker from "@/assets/img/via-marker.png";
|
||||||
import endMarker from "@/assets/img/end-marker.png";
|
import endMarker from "@/assets/img/end-marker.png";
|
||||||
import startMarker from "@/assets/img/start-marker.png";
|
import startMarker from "@/assets/img/start-marker.png";
|
||||||
|
import fullScreen from "@/assets/img/fullScreen.png";
|
||||||
|
import narrow from "@/assets/img/narrow.png";
|
||||||
|
import { useFullScreen } from "@/utils/hooks";
|
||||||
|
|
||||||
let InfoWin = null;
|
let InfoWin = null;
|
||||||
let newMap = null;
|
let newMap = null;
|
||||||
let locationInfo = ref({});
|
let locationInfo = ref({});
|
||||||
|
const { isFullScreen, toggleFullScreen } = useFullScreen();
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
deviceId: {
|
deviceId: {
|
||||||
type: String || Number,
|
type: String || Number,
|
||||||
@ -100,10 +108,6 @@ const getLocateRecord = () => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const fn = () => {
|
|
||||||
// this.cluster.on("click", this.clusterClickEvent);
|
|
||||||
};
|
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
newMap = new MapCustom({ dom: "mapcontainer" });
|
newMap = new MapCustom({ dom: "mapcontainer" });
|
||||||
getLocateRecord();
|
getLocateRecord();
|
||||||
@ -114,5 +118,19 @@ onMounted(() => {
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
height: 400px;
|
height: 400px;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
|
position: relative;
|
||||||
|
.toolbox {
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
padding: 5px;
|
||||||
|
position: absolute;
|
||||||
|
right: 20px;
|
||||||
|
top: 20px;
|
||||||
|
// padding: 5px;
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 4px;
|
||||||
|
z-index: 2;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -22,6 +22,9 @@
|
|||||||
<template #warnType="{ rows }">
|
<template #warnType="{ rows }">
|
||||||
{{ warnTypeEnum[rows.warnType] }}
|
{{ warnTypeEnum[rows.warnType] }}
|
||||||
</template>
|
</template>
|
||||||
|
<template #operator="{ rows }">
|
||||||
|
<el-button type="primary" size="small" link @click="toIncidentDispose(rows.id)"> 处理事件 </el-button>
|
||||||
|
</template>
|
||||||
</TableCustom>
|
</TableCustom>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@ -30,6 +33,7 @@ import TableCustom from "@/components/table-custom.vue";
|
|||||||
import { warningRecord } from "@/api/index";
|
import { warningRecord } from "@/api/index";
|
||||||
import { ref, reactive, watch } from "vue";
|
import { ref, reactive, watch } from "vue";
|
||||||
import { TDevice } from "@/api/index.d";
|
import { TDevice } from "@/api/index.d";
|
||||||
|
import { useRouter } from "vue-router";
|
||||||
|
|
||||||
const statusColor = ["danger", "success"];
|
const statusColor = ["danger", "success"];
|
||||||
enum warningStatusEnum {
|
enum warningStatusEnum {
|
||||||
@ -45,6 +49,7 @@ enum warnTypeEnum {
|
|||||||
"血氧告警",
|
"血氧告警",
|
||||||
"体温告警",
|
"体温告警",
|
||||||
}
|
}
|
||||||
|
const router = useRouter();
|
||||||
const warnTypeList = ["SOS告警", "围栏告警", "破坏告警", "低电告警", "心率告警", "血氧告警", "体温告警"];
|
const warnTypeList = ["SOS告警", "围栏告警", "破坏告警", "低电告警", "心率告警", "血氧告警", "体温告警"];
|
||||||
const paging = reactive({
|
const paging = reactive({
|
||||||
page: 1,
|
page: 1,
|
||||||
@ -69,6 +74,7 @@ let columns = [
|
|||||||
{ prop: "warnType", label: "事件类型" },
|
{ prop: "warnType", label: "事件类型" },
|
||||||
{ prop: "status", label: "处理状态" },
|
{ prop: "status", label: "处理状态" },
|
||||||
{ prop: "createTime", label: "触发时间" },
|
{ prop: "createTime", label: "触发时间" },
|
||||||
|
{ prop: "operator", label: "操作" },
|
||||||
];
|
];
|
||||||
const getData = async () => {
|
const getData = async () => {
|
||||||
const res = await warningRecord(paging);
|
const res = await warningRecord(paging);
|
||||||
@ -95,6 +101,12 @@ watch(
|
|||||||
},
|
},
|
||||||
{ immediate: true } // 关键选项
|
{ immediate: true } // 关键选项
|
||||||
);
|
);
|
||||||
|
const toIncidentDispose = (id: string) => {
|
||||||
|
router.push({
|
||||||
|
path: "/incidentDispose",
|
||||||
|
query: { id },
|
||||||
|
});
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
<style scoped lang="less">
|
<style scoped lang="less">
|
||||||
.card {
|
.card {
|
||||||
|
@ -38,7 +38,6 @@ import DeviceLocationMap from "./deviceLocationMap.vue";
|
|||||||
import { deviceList, healthLatestData } from "@/api/index";
|
import { deviceList, healthLatestData } from "@/api/index";
|
||||||
import { TDevice, THealthLatestData } from "@/api/index.d";
|
import { TDevice, THealthLatestData } from "@/api/index.d";
|
||||||
import { onMounted, ref, reactive, onUnmounted } from "vue";
|
import { onMounted, ref, reactive, onUnmounted } from "vue";
|
||||||
import { format } from "@/utils";
|
|
||||||
import heart from "@/assets/img/heart.png";
|
import heart from "@/assets/img/heart.png";
|
||||||
import temperature from "@/assets/img/temperature.png";
|
import temperature from "@/assets/img/temperature.png";
|
||||||
import blood from "@/assets/img/blood.png";
|
import blood from "@/assets/img/blood.png";
|
||||||
|
@ -8,7 +8,12 @@
|
|||||||
</div>
|
</div>
|
||||||
<TableCustom :columns="columns" :tableData="tableData" :paging="page" :changePage="changePage">
|
<TableCustom :columns="columns" :tableData="tableData" :paging="page" :changePage="changePage">
|
||||||
<template #location="{ rows }">
|
<template #location="{ rows }">
|
||||||
<el-button type="success" link :icon="View" v-if="rows.warnType == 0 || rows.warnType == 1" @click="toIncidentDispose(rows.id)"> 查看 </el-button>
|
<el-button type="success" link v-if="rows.warnType == 0 || rows.warnType == 1" @click="toIncidentDispose(rows.id)"> 查看 </el-button>
|
||||||
|
<div v-else>--</div>
|
||||||
|
</template>
|
||||||
|
<template #operator="{ rows }">
|
||||||
|
<el-button type="primary" v-if="!rows.status" size="small" link @click="toIncidentDispose(rows.id)"> 处理事件 </el-button>
|
||||||
|
<div v-else>--</div>
|
||||||
</template>
|
</template>
|
||||||
<template #warnType="{ rows }">
|
<template #warnType="{ rows }">
|
||||||
{{ warnTypeEnum[rows.warnType] }}
|
{{ warnTypeEnum[rows.warnType] }}
|
||||||
@ -82,6 +87,7 @@ let columns = [
|
|||||||
{ prop: "createTime", label: "时间" },
|
{ prop: "createTime", label: "时间" },
|
||||||
{ prop: "location", label: "地理位置" },
|
{ prop: "location", label: "地理位置" },
|
||||||
{ prop: "status", label: "处理状态" },
|
{ prop: "status", label: "处理状态" },
|
||||||
|
{ prop: "operator", label: "操作" },
|
||||||
];
|
];
|
||||||
const handleInput = debounce((e) => {
|
const handleInput = debounce((e) => {
|
||||||
page.deviceId = e;
|
page.deviceId = e;
|
||||||
|
234
src/views/synthesizeManage/deviceInfo/deviceHistory.vue
Normal file
234
src/views/synthesizeManage/deviceInfo/deviceHistory.vue
Normal file
@ -0,0 +1,234 @@
|
|||||||
|
<template>
|
||||||
|
<el-dialog title="历史记录" v-model="visible" width="70%" destroy-on-close :close-on-click-modal="false" @close="visible = false">
|
||||||
|
<div class="monitoringMap" id="mapcontainer"></div>
|
||||||
|
<div ref="chartRef" class="chart"></div>
|
||||||
|
<div :style="{ display: 'none' }">
|
||||||
|
<InfoWindow class="infoBox" :value="locationInfo" @close="InfoWin.close()" />
|
||||||
|
</div>
|
||||||
|
</el-dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { debounce, format } from "@/utils";
|
||||||
|
import { MapCustom } from "@/utils/mapCustom";
|
||||||
|
import { nextTick, onMounted, onUnmounted, PropType, ref, watch } from "vue";
|
||||||
|
import * as echarts from "echarts";
|
||||||
|
import { dataRecord, locateRecord } from "@/api";
|
||||||
|
import { TDevice } from "@/api/index.d";
|
||||||
|
import ViaMarker from "@/assets/img/via-marker.png";
|
||||||
|
import endMarker from "@/assets/img/end-marker.png";
|
||||||
|
import startMarker from "@/assets/img/start-marker.png";
|
||||||
|
import InfoWindow from "@/components/InfoWindow.vue";
|
||||||
|
|
||||||
|
const chartRef = ref(null);
|
||||||
|
let myChart = null;
|
||||||
|
let InfoWin = null;
|
||||||
|
let newMap = null;
|
||||||
|
const visible = ref(false);
|
||||||
|
let locationInfo = ref({});
|
||||||
|
defineExpose({
|
||||||
|
visible,
|
||||||
|
});
|
||||||
|
const props = defineProps({
|
||||||
|
recordInofo: {
|
||||||
|
type: Object as PropType<TDevice.IUseRecordRes>,
|
||||||
|
default: () => {},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const options = {
|
||||||
|
tooltip: {
|
||||||
|
trigger: "axis",
|
||||||
|
formatter: function (params) {
|
||||||
|
let unit = { 0: "次/分", 1: "%", 2: "℃" };
|
||||||
|
var res = format(Number(params[0].name)) + "<br/>";
|
||||||
|
res += params
|
||||||
|
.map(function (param, index) {
|
||||||
|
return param.marker + param.seriesName + ":" + param.value + unit[index] + "<br/>";
|
||||||
|
})
|
||||||
|
.join("");
|
||||||
|
return res;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
legend: {
|
||||||
|
data: ["心率", "体表温度", "血氧"],
|
||||||
|
},
|
||||||
|
grid: {
|
||||||
|
left: "0%",
|
||||||
|
right: "4%",
|
||||||
|
bottom: "6%",
|
||||||
|
containLabel: true,
|
||||||
|
},
|
||||||
|
xAxis: {
|
||||||
|
type: "category",
|
||||||
|
boundaryGap: false,
|
||||||
|
data: [],
|
||||||
|
axisLabel: {
|
||||||
|
formatter: function (item) {
|
||||||
|
return format(Number(item), "HH:mm:ss");
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
type: "value",
|
||||||
|
},
|
||||||
|
dataZoom: [
|
||||||
|
{
|
||||||
|
type: "slider", //slider表示有滑动块的,inside表示内置的
|
||||||
|
start: 90, // 左边在 10% 的位置。
|
||||||
|
end: 100, // 右边在 60% 的位置。
|
||||||
|
show: true,
|
||||||
|
xAxisIndex: [0],
|
||||||
|
handleSize: 0, //滑动条的 左右2个滑动条的大小
|
||||||
|
height: 12, //组件高度
|
||||||
|
bottom: -2, //右边的距离
|
||||||
|
borderColor: "#eee",
|
||||||
|
fillerColor: "#E7E7E7",
|
||||||
|
backgroundColor: "#eee", //两边未选中的滑动条区域的颜色
|
||||||
|
showDataShadow: false, //是否显示数据阴影 默认auto
|
||||||
|
showDetail: false, //即拖拽时候是否显示详细数值信息 默认true
|
||||||
|
realtime: true, //是否实时更新
|
||||||
|
filterMode: "filter",
|
||||||
|
handleStyle: {
|
||||||
|
borderRadius: "20",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
itemStyle: {
|
||||||
|
color: "#FF4241",
|
||||||
|
},
|
||||||
|
name: "心率",
|
||||||
|
type: "line",
|
||||||
|
showSymbol: false,
|
||||||
|
data: [],
|
||||||
|
time: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
itemStyle: {
|
||||||
|
color: "#12CCA2",
|
||||||
|
},
|
||||||
|
name: "血氧",
|
||||||
|
type: "line",
|
||||||
|
showSymbol: false,
|
||||||
|
data: [],
|
||||||
|
time: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
itemStyle: {
|
||||||
|
color: "#FF7D00",
|
||||||
|
},
|
||||||
|
showSymbol: false,
|
||||||
|
name: "体表温度",
|
||||||
|
type: "line",
|
||||||
|
data: [],
|
||||||
|
time: [],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
watch(visible, (val) => {
|
||||||
|
if (val) {
|
||||||
|
nextTick(() => {
|
||||||
|
newMap = new MapCustom({ dom: "mapcontainer" });
|
||||||
|
myChart = echarts.init(chartRef.value);
|
||||||
|
getLocateRecord();
|
||||||
|
dataRecord({ useRecordId: props.recordInofo.id }).then((res) => {
|
||||||
|
if (res.healthData && res.healthData.length) {
|
||||||
|
res.healthData.forEach((item) => {
|
||||||
|
options.xAxis.data.push(item.time);
|
||||||
|
options.series[0].data.push(item.hr);
|
||||||
|
options.series[1].data.push(item.bo);
|
||||||
|
options.series[2].data.push(item.temp);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
myChart.setOption(options);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
myChart = null;
|
||||||
|
newMap = null;
|
||||||
|
window.removeEventListener("resize", handleResize);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const getLocateRecord = () => {
|
||||||
|
locateRecord({
|
||||||
|
deviceId: props.recordInofo.deviceId,
|
||||||
|
startDate: props.recordInofo.createTime,
|
||||||
|
endDate: props.recordInofo.updateTime,
|
||||||
|
}).then((res) => {
|
||||||
|
if (res && res.length) {
|
||||||
|
let list = res;
|
||||||
|
newMap.clearMap();
|
||||||
|
newMap.setCenter([list[0].lng, list[0].lat]);
|
||||||
|
newMap.polyline(list);
|
||||||
|
let startIcon = newMap.newIcon({
|
||||||
|
image: startMarker,
|
||||||
|
size: [20, 29],
|
||||||
|
});
|
||||||
|
let endIcon = newMap.newIcon({
|
||||||
|
image: endMarker,
|
||||||
|
size: [20, 29],
|
||||||
|
});
|
||||||
|
let ViaIcon = newMap.newIcon({
|
||||||
|
image: ViaMarker,
|
||||||
|
size: [20, 29],
|
||||||
|
});
|
||||||
|
let markers = [];
|
||||||
|
list.forEach((item, index) => {
|
||||||
|
if (list.length < 50) {
|
||||||
|
let marker: any = "";
|
||||||
|
if (index == 0) {
|
||||||
|
marker = newMap.marker({ icon: endIcon, position: [item.lng, item.lat], zIndex: 13 });
|
||||||
|
} else if (index == list.length - 1) {
|
||||||
|
marker = newMap.marker({ icon: startIcon, position: [item.lng, item.lat], zIndex: 13 });
|
||||||
|
} else {
|
||||||
|
marker = newMap.marker({ icon: ViaIcon, position: [item.lng, item.lat], zIndex: 12 });
|
||||||
|
}
|
||||||
|
marker.on("click", () => {
|
||||||
|
locationInfo.value = item;
|
||||||
|
InfoWin = newMap.infoWindow();
|
||||||
|
InfoWin.open(newMap.map, marker.getPosition());
|
||||||
|
});
|
||||||
|
markers.push(marker);
|
||||||
|
} else {
|
||||||
|
if (index % 5 == 0) {
|
||||||
|
let marker: any = "";
|
||||||
|
if (index == 0) {
|
||||||
|
marker = newMap.marker({ icon: endIcon, position: [item.lng, item.lat], zIndex: 13 });
|
||||||
|
} else if (index == list.length - 1) {
|
||||||
|
marker = newMap.marker({ icon: startIcon, position: [item.lng, item.lat], zIndex: 13 });
|
||||||
|
} else {
|
||||||
|
marker = newMap.marker({ icon: ViaIcon, position: [item.lng, item.lat], zIndex: 12 });
|
||||||
|
}
|
||||||
|
marker.on("click", () => {
|
||||||
|
locationInfo.value = item;
|
||||||
|
InfoWin = newMap.infoWindow();
|
||||||
|
InfoWin.open(newMap.map, marker.getPosition());
|
||||||
|
});
|
||||||
|
|
||||||
|
markers.push(marker);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
newMap.map.add(markers);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
const handleResize = debounce(() => {
|
||||||
|
myChart && myChart.resize();
|
||||||
|
}, 200);
|
||||||
|
onMounted(() => {
|
||||||
|
window.addEventListener("resize", handleResize);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="less">
|
||||||
|
.monitoringMap {
|
||||||
|
height: 300px;
|
||||||
|
}
|
||||||
|
.chart {
|
||||||
|
height: 300px;
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
</style>
|
@ -7,12 +7,12 @@
|
|||||||
<el-row :gutter="20">
|
<el-row :gutter="20">
|
||||||
<el-col :span="6">
|
<el-col :span="6">
|
||||||
<div class="item"><span>手铐序号:</span>{{ query.id }}</div>
|
<div class="item"><span>手铐序号:</span>{{ query.id }}</div>
|
||||||
<div class="item"><span>绑定管理员:</span>{{ query.adminName }}</div>
|
<div class="item"><span>绑定用户:</span>{{ query.adminName }}</div>
|
||||||
<div class="item"><span>设备状态:</span>{{ statusEnum[query.status as string] }}</div>
|
<div class="item"><span>设备状态:</span>{{ statusEnum[query.status as string] }}</div>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="6">
|
<el-col :span="6">
|
||||||
<div class="item"><span>IMEI号:</span>{{ query.deviceId }}</div>
|
<div class="item"><span>IMEI号:</span>{{ query.deviceId }}</div>
|
||||||
<div class="item"><span>绑定管理者账号:</span>{{ query.adminUsername }}</div>
|
<div class="item"><span>绑定警员号:</span>{{ query.adminUsername }}</div>
|
||||||
<div class="item"><span>当前电量:</span>{{ query.battery }}%</div>
|
<div class="item"><span>当前电量:</span>{{ query.battery }}%</div>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="6">
|
<el-col :span="6">
|
||||||
@ -33,9 +33,9 @@
|
|||||||
<template #status="{ rows }">
|
<template #status="{ rows }">
|
||||||
{{ recordStatusEnum[rows.status] }}
|
{{ recordStatusEnum[rows.status] }}
|
||||||
</template>
|
</template>
|
||||||
<template #operator="{ rows }">
|
<!-- <template #operator="{ rows }">
|
||||||
<el-button link type="primary" size="small"> 历史记录 </el-button>
|
<el-button link type="primary" size="small" @click="viewHistory(rows)" v-if="rows.updateTime"> 历史记录 </el-button>
|
||||||
</template>
|
</template> -->
|
||||||
</TableCustom>
|
</TableCustom>
|
||||||
</el-card>
|
</el-card>
|
||||||
<el-card class="baseInfo">
|
<el-card class="baseInfo">
|
||||||
@ -45,7 +45,6 @@
|
|||||||
<TableCustom :hasToolbar="false" :columns="columns" :tableData="warningTableData" :paging="paging1" :changePage="changeWarningPage">
|
<TableCustom :hasToolbar="false" :columns="columns" :tableData="warningTableData" :paging="paging1" :changePage="changeWarningPage">
|
||||||
<template #operator="{ rows }">
|
<template #operator="{ rows }">
|
||||||
<el-button link type="primary" size="small" @click="toIncidentDispose(rows.id)" v-if="rows.status == 0"> 处理事件 </el-button>
|
<el-button link type="primary" size="small" @click="toIncidentDispose(rows.id)" v-if="rows.status == 0"> 处理事件 </el-button>
|
||||||
<div v-else></div>
|
|
||||||
</template>
|
</template>
|
||||||
<template #status="{ rows }">
|
<template #status="{ rows }">
|
||||||
<el-tag :type="statusColor[rows.status]">{{ warningStatusEnum[rows.status] }}</el-tag>
|
<el-tag :type="statusColor[rows.status]">{{ warningStatusEnum[rows.status] }}</el-tag>
|
||||||
@ -55,6 +54,7 @@
|
|||||||
</template>
|
</template>
|
||||||
</TableCustom>
|
</TableCustom>
|
||||||
</el-card>
|
</el-card>
|
||||||
|
<DeviceHistory ref="deviceHistoryRef" :recordInofo="recordInofo" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -65,6 +65,8 @@ import TableCustom from "@/components/table-custom.vue";
|
|||||||
import { TableItem } from "@/types/table";
|
import { TableItem } from "@/types/table";
|
||||||
import { useRouter, useRoute } from "vue-router";
|
import { useRouter, useRoute } from "vue-router";
|
||||||
import { TDevice } from "@/api/index.d";
|
import { TDevice } from "@/api/index.d";
|
||||||
|
import DeviceHistory from "./deviceHistory.vue";
|
||||||
|
|
||||||
const { query } = useRoute();
|
const { query } = useRoute();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
@ -90,15 +92,18 @@ enum warnTypeEnum {
|
|||||||
"血氧告警",
|
"血氧告警",
|
||||||
"体温告警",
|
"体温告警",
|
||||||
}
|
}
|
||||||
|
const deviceHistoryRef = ref(null);
|
||||||
|
const recordInofo = ref<TDevice.IUseRecordRes>();
|
||||||
const statusColor = ["danger", "success"];
|
const statusColor = ["danger", "success"];
|
||||||
// 表格相关
|
// 表格相关
|
||||||
let record = ref([
|
let record = ref([
|
||||||
{ type: "index", label: "序号", width: 55, align: "center" },
|
{ type: "index", label: "序号", width: 55, align: "center" },
|
||||||
{ prop: "adminName", label: "管理员" },
|
{ prop: "adminName", label: "用户名称" },
|
||||||
|
{ prop: "username", label: "警员号" },
|
||||||
{ prop: "userNumber", label: "佩戴者" },
|
{ prop: "userNumber", label: "佩戴者" },
|
||||||
{ prop: "createTime", label: "开始使用时间" },
|
{ prop: "createTime", label: "开始使用时间" },
|
||||||
{ prop: "updateTime", label: "结束使用时间" },
|
{ prop: "updateTime", label: "结束使用时间" },
|
||||||
{ prop: "operator", label: "操作" },
|
// { prop: "operator", label: "操作" },
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// 表格相关
|
// 表格相关
|
||||||
@ -153,6 +158,10 @@ const toIncidentDispose = (id: string) => {
|
|||||||
query: { id },
|
query: { id },
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
const viewHistory = (row: TDevice.IUseRecordRes) => {
|
||||||
|
recordInofo.value = row;
|
||||||
|
deviceHistoryRef.value.visible = true;
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="less">
|
<style scoped lang="less">
|
||||||
|
@ -134,15 +134,15 @@ let columns = ref([
|
|||||||
{ type: "selection", reserveSelection: true },
|
{ type: "selection", reserveSelection: true },
|
||||||
{ type: "index", label: "序号", width: 55, align: "center" },
|
{ type: "index", label: "序号", width: 55, align: "center" },
|
||||||
{ prop: "deviceId", label: "IMEI", width: 120 },
|
{ prop: "deviceId", label: "IMEI", width: 120 },
|
||||||
{ prop: "adminName", label: "绑定警察名称", width: 180 },
|
{ prop: "adminName", label: "绑定用户名称", width: 180 },
|
||||||
{ prop: "adminUsername", label: "绑定警察账户", width: 120 },
|
{ prop: "adminUsername", label: "绑定警员号", width: 120 },
|
||||||
{ prop: "orgName", label: "关联辖区", width: 120 },
|
{ prop: "orgName", label: "关联辖区", width: 120 },
|
||||||
{ prop: "battery", label: "电量", width: 100 },
|
{ prop: "battery", label: "电量", width: 100 },
|
||||||
{ prop: "deviceVersion", label: "版本号", width: 100 },
|
{ prop: "deviceVersion", label: "版本号", width: 100 },
|
||||||
{ prop: "status", label: "设备状态", width: 100 },
|
{ prop: "status", label: "设备状态", width: 100 },
|
||||||
{ prop: "mode", label: "当前模式", width: 100 },
|
{ prop: "mode", label: "当前模式", width: 100 },
|
||||||
{ prop: "useStatus", label: "使用状态", width: 100 },
|
{ prop: "useStatus", label: "使用状态", width: 100 },
|
||||||
{ prop: "deviceSwitch", label: "启用开关", width: 100 },
|
// { prop: "deviceSwitch", label: "启用开关", width: 100 },
|
||||||
{ prop: "createTime", label: "最新通信时间", width: 180 },
|
{ prop: "createTime", label: "最新通信时间", width: 180 },
|
||||||
{ prop: "createTime", label: "创建时间", width: 180 },
|
{ prop: "createTime", label: "创建时间", width: 180 },
|
||||||
{ prop: "operator", label: "操作", width: 400, fixed: "right" },
|
{ prop: "operator", label: "操作", width: 400, fixed: "right" },
|
||||||
|
Loading…
x
Reference in New Issue
Block a user