2025年05月06日14:19:38

This commit is contained in:
luojiayi 2025-05-06 14:19:39 +08:00
parent 65bcb4b974
commit 5bf5098349
9 changed files with 262 additions and 287 deletions

View File

@ -1,4 +1,5 @@
import { computed, onBeforeMount, onMounted, onUnmounted, ref } from "vue"; import { computed, onBeforeMount, onMounted, onUnmounted, ref } from "vue";
import { debounce } from "@/utils/index";
export function useVModel(props, propsName, emit) { export function useVModel(props, propsName, emit) {
return computed({ return computed({
@ -98,4 +99,15 @@ export function useFullScreen() {
window.removeEventListener('resize', fullChange) window.removeEventListener('resize', fullChange)
}); });
return { isFullScreen, toggleFullScreen }; return { isFullScreen, toggleFullScreen };
}
// 监听窗口大小
export function useResize(fn = () => { }) {
const handleResize = debounce(fn, 200);
onMounted(() => {
window.addEventListener("resize", handleResize);
});
onUnmounted(() => {
window.removeEventListener("resize", handleResize);
});
} }

View File

@ -94,6 +94,7 @@ import { TWarningConfirm, TWarningDetail } from "@/api/index.d";
import { useRoute } from "vue-router"; import { useRoute } from "vue-router";
import { ElMessage, FormInstance, FormRules } from "element-plus"; import { ElMessage, FormInstance, FormRules } from "element-plus";
import { debounce, format } from "@/utils"; import { debounce, format } from "@/utils";
import { useResize } from "@/utils/hooks";
// //
enum unitEnum { enum unitEnum {
@ -307,9 +308,9 @@ const submitForm = async (formEl: FormInstance | undefined) => {
}); });
}; };
const handleResize = debounce(() => { useResize(() => {
myChart.resize(); myChart.resize();
}, 200); });
onMounted(() => { onMounted(() => {
getData(); getData();
try { try {
@ -318,12 +319,8 @@ onMounted(() => {
if (chartRef.value) { if (chartRef.value) {
myChart = echarts.init(chartRef.value); myChart = echarts.init(chartRef.value);
myChart.setOption(options); myChart.setOption(options);
window.addEventListener("resize", handleResize);
} }
}); });
onUnmounted(() => {
window.removeEventListener("resize", handleResize);
});
</script> </script>
<style scoped lang="less"> <style scoped lang="less">

View File

@ -15,6 +15,7 @@
import { debounce, format } from "@/utils"; import { debounce, format } from "@/utils";
import { onMounted, onUnmounted, ref } from "vue"; import { onMounted, onUnmounted, ref } from "vue";
import * as echarts from "echarts"; import * as echarts from "echarts";
import { useResize } from "@/utils/hooks";
const radio = ref("1"); const radio = ref("1");
const chartRef = ref(null); const chartRef = ref(null);
@ -100,19 +101,14 @@ const getOptionsData = (list: { time: string; value: number }[], name: string, c
myChart.setOption(options); myChart.setOption(options);
}; };
const handleResize = debounce(() => {
myChart.resize();
}, 200);
defineExpose({ radio, getOptionsData }); defineExpose({ radio, getOptionsData });
useResize(() => {
myChart.resize();
});
onMounted(() => { onMounted(() => {
myChart = echarts.init(chartRef.value); myChart = echarts.init(chartRef.value);
window.addEventListener("resize", handleResize);
});
onUnmounted(() => {
window.removeEventListener("resize", handleResize);
}); });
const change = (e) => { const change = (e) => {
emit("change", e); emit("change", e);
}; };

View File

@ -2,19 +2,93 @@
<div class="deviceStatistics card"> <div class="deviceStatistics card">
<div class="card-head"> <div class="card-head">
<div class="title">内容数据</div> <div class="title">内容数据</div>
<SectionDate @change="api" /> <SectionDate @change="getStatisticsContent" />
</div> </div>
<slot name="chart"></slot> <div ref="chartRef" style="flex: 1"></div>
</div> </div>
</template> </template>
<script setup> <script setup>
import SectionDate from "@/components/sectionDate.vue"; import SectionDate from "@/components/sectionDate.vue";
import { statisticsContent } from "@/api/index";
import { onMounted, ref } from "vue";
import { format } from "@/utils";
import * as echarts from "echarts";
import { useResize } from "@/utils/hooks";
const props = defineProps({ const d = new Date();
api: { const chartRef = ref(null);
type: Function, let myChart = null;
required: true,
const option = ref({
tooltip: {
trigger: "axis",
}, },
grid: {
left: "3%",
right: "4%",
bottom: "3%",
containLabel: true,
},
xAxis: {
type: "category",
boundaryGap: false,
data: [],
axisLabel: {
formatter: function (item) {
return format(item, "MM-DD");
},
},
},
yAxis: {
type: "value",
},
series: [],
});
const getStatisticsContent = (req) => {
statisticsContent(req).then((res) => {
if (res.times && res.times.length) {
option.value.xAxis.data = res.times.map((item) => (req.type == "hour" ? format(item) : format(item, "YYYY-MM-DD")));
option.value.series = [
{
name: "SOS预警数组",
data: res?.sosArr,
type: "line",
},
{
name: "围栏预警数组",
data: res?.railArr,
type: "line",
},
{
name: "破坏预警数组",
data: res?.destroyArr,
type: "line",
},
{
name: "生理预警数组",
data: res?.healthArr,
type: "line",
},
];
myChart.setOption(option.value);
}
});
};
useResize(() => {
myChart.resize();
});
onMounted(() => {
if (chartRef.value) {
myChart = echarts.init(chartRef.value);
myChart.setOption(option.value);
}
getStatisticsContent({
startDate: `${format(d, "YYYY-MM-DD")} 00:00:00`,
endDate: `${format(d, "YYYY-MM-DD")} 23:59:59`,
type: "hour",
});
}); });
</script> </script>
<style scoped lang="less"> <style scoped lang="less">

View File

@ -6,7 +6,7 @@
<el-input style="width: 240px" v-model="search" placeholder="搜索设备IMEI号" :suffix-icon="Search" @input="handleInput" /> <el-input style="width: 240px" v-model="search" placeholder="搜索设备IMEI号" :suffix-icon="Search" @input="handleInput" />
</div> </div>
</div> </div>
<TableCustom :columns="columns" :tableData="tableData" :paging="page" :changePage="changePage"> <TableCustom :columns="columns" :tableData="tableData" :paging="paging" :changePage="changePage">
<template #location="{ rows }"> <template #location="{ rows }">
<el-button type="success" link 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> <div v-else>--</div>
@ -27,19 +27,13 @@
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { PropType, ref } from "vue";
import { Search, View } from "@element-plus/icons-vue"; import { Search, View } from "@element-plus/icons-vue";
import TableCustom from "@/components/table-custom.vue"; import TableCustom from "@/components/table-custom.vue";
import { debounce } from "@/utils";
import { useRouter } from "vue-router"; import { useRouter } from "vue-router";
import { TWarnRecord } from "@/api/index.d"; import { TWarnRecord } from "@/api/index.d";
import { onMounted, ref, reactive } from "vue";
interface TPaging { import { warnRecord } from "@/api/index";
page: number; import { debounce, format } from "@/utils";
size: number;
total: number;
deviceId?: number | string;
}
const router = useRouter(); const router = useRouter();
enum statusEnum { enum statusEnum {
@ -57,28 +51,32 @@ enum warnTypeEnum {
} }
const search = ref(""); const search = ref("");
const { tableData, page, getData, changePage } = defineProps({ let d = new Date();
tableData: {
type: Array, const paging = reactive({
default: () => [], page: 1,
}, size: 5,
page: { total: 0,
type: Object as PropType<TPaging>, deviceId: null,
default: () => ({ startDate: `${format(d, "YYYY-MM-DD")} 00:00:00`,
page: 1, endDate: `${format(d, "YYYY-MM-DD")} 23:59:59`,
size: 10,
total: 0,
}),
},
getData: {
type: Function,
default: () => {},
},
changePage: {
type: Function,
default: () => {},
},
}); });
const tableData = ref<TWarnRecord.IListRes[]>([]);
const getData = async () => {
try {
const res = await warnRecord(paging);
tableData.value = res.records;
paging.total = res.total;
} catch (error) {}
};
const changePage = (val: number) => {
paging.page = val;
getData();
};
// //
let columns = [ let columns = [
{ type: "index", label: "序号", width: 55, align: "center" }, { type: "index", label: "序号", width: 55, align: "center" },
@ -90,7 +88,7 @@ let columns = [
{ prop: "operator", label: "操作" }, { prop: "operator", label: "操作" },
]; ];
const handleInput = debounce((e) => { const handleInput = debounce((e) => {
page.deviceId = e; paging.deviceId = e;
getData(); getData();
}, 500); }, 500);
@ -100,6 +98,10 @@ const toIncidentDispose = (id: string) => {
query: { id }, query: { id },
}); });
}; };
onMounted(() => {
getData();
});
</script> </script>
<style scoped lang="less"> <style scoped lang="less">
.card { .card {

View File

@ -1,32 +1,20 @@
<template> <template>
<div class="container scrollbar"> <div class="container scrollbar">
<MonitoringTop :list="funcList" /> <MonitoringTop />
<el-row :gutter="20"> <el-row :gutter="20">
<el-col class="elcol" :span="16" :xs="24" :sm="24" :md="24" :lg="16" :xl="16"> <el-col class="elcol" :span="16" :xs="24" :sm="24" :md="24" :lg="16" :xl="16">
<DeviceHistory :api="getStatisticsContent"> <DeviceHistory />
<template #chart>
<div ref="chartRef" style="flex: 1"></div>
</template>
</DeviceHistory>
</el-col> </el-col>
<el-col class="elcol" :span="8" :xs="24" :sm="24" :md="24" :lg="8" :xl="8"> <el-col class="elcol" :span="8" :xs="24" :sm="24" :md="24" :lg="8" :xl="8">
<OnLineStatistics> <OnLineStatistics />
<template #chart>
<div ref="chartRef1" style="width: 100%; height: 100%"></div>
</template>
</OnLineStatistics>
</el-col> </el-col>
</el-row> </el-row>
<el-row :gutter="20"> <el-row :gutter="20">
<el-col class="elcol" :span="16" :xs="24" :sm="24" :md="24" :lg="16" :xl="16"> <el-col class="elcol" :span="16" :xs="24" :sm="24" :md="24" :lg="16" :xl="16">
<EmergencyList :tableData="tableData" :page="paging" :getData="getData" :changePage="changePage"></EmergencyList> <EmergencyList />
</el-col> </el-col>
<el-col class="elcol" :span="8" :xs="24" :sm="24" :md="24" :lg="8" :xl="8"> <el-col class="elcol" :span="8" :xs="24" :sm="24" :md="24" :lg="8" :xl="8">
<StatisticsWarning> <StatisticsWarning />
<template #chart>
<div ref="chartRef2" style="width: 100%; height: 100%"></div>
</template>
</StatisticsWarning>
</el-col> </el-col>
</el-row> </el-row>
</div> </div>
@ -37,218 +25,13 @@ import DeviceHistory from "./deviceHistory.vue";
import OnLineStatistics from "./onLineStatistics.vue"; import OnLineStatistics from "./onLineStatistics.vue";
import StatisticsWarning from "./statisticsWarning.vue"; import StatisticsWarning from "./statisticsWarning.vue";
import EmergencyList from "./emergencyList.vue"; import EmergencyList from "./emergencyList.vue";
import * as echarts from "echarts";
import { MapCustom } from "@/utils/mapCustom"; import { MapCustom } from "@/utils/mapCustom";
import { statisticsContent, statisticsCount, statisticsWarningapi, warnRecord, statisticsUseCount } from "@/api/index"; import { onMounted } from "vue";
import { onMounted, ref, reactive, onDeactivated, onUnmounted } from "vue";
import { debounce, format } from "@/utils";
import { TWarnRecord } from "@/api/index.d";
import handcuffs from "@/assets/img/handcuffs.png";
import onLine from "@/assets/img/onLine.png";
import report from "@/assets/img/report.png";
import newly from "@/assets/img/newly.png";
let d = new Date();
const chartRef = ref(null);
const chartRef1 = ref(null);
const chartRef2 = ref(null);
let myChart = null;
let myChart1 = null;
let myChart2 = null;
let funcList = ref([
{ title: "手铐总数", en: "SHOUKAOZONGSHU", icon: handcuffs, num: 0, color: "##71DDF9" },
{ title: "在线数量", en: "ZAIXIANSHULIANG", icon: onLine, num: 0, color: "##71DDF9" },
{ title: "告警数量", en: "GAOJINGSHULIANG", icon: report, num: 0, color: "#8B51FD" },
{ title: "较昨日新增", en: "JIAOZUORIXINZENG", icon: newly, num: 0, color: "#FF6905" },
]);
const option = ref({
tooltip: {
trigger: "axis",
},
grid: {
left: "3%",
right: "4%",
bottom: "3%",
containLabel: true,
},
xAxis: {
type: "category",
boundaryGap: false,
data: [],
axisLabel: {
formatter: function (item) {
return format(item, "MM-DD");
},
},
},
yAxis: {
type: "value",
},
series: [],
});
let option1 = ref({
tooltip: {
trigger: "item",
},
legend: {
right: "left",
},
series: {
type: "pie",
radius: ["20%", "50%"],
data: [
{ value: 0, name: "常规模式" },
{ value: 0, name: "户外押送" },
{ value: 0, name: "审讯模式" },
],
},
});
let option2 = ref({
tooltip: {
trigger: "item",
},
legend: {
right: "left",
},
series: {
type: "pie",
radius: ["20%", "50%"],
data: [
{ value: 0, name: "SOS告警" },
{ value: 0, name: "围栏告警" },
{ value: 0, name: "破坏告警" },
{ value: 0, name: "心率告警" },
{ value: 0, name: "血氧告警" },
{ value: 0, name: "体温告警" },
],
},
});
const paging = reactive({
page: 1,
size: 5,
total: 0,
deviceId: null,
startDate: `${format(d, "YYYY-MM-DD")} 00:00:00`,
endDate: `${format(d, "YYYY-MM-DD")} 23:59:59`,
});
const tableData = ref<TWarnRecord.IListRes[]>([]);
const getData = async () => {
try {
const res = await warnRecord(paging);
tableData.value = res.records;
paging.total = res.total;
} catch (error) {}
};
const getStatisticsWarningApi = () => {
statisticsWarningapi().then((res) => {
option2.value.series.data = [
{ value: res?.sosCount, name: "SOS告警" },
{ value: res?.railCount, name: "围栏告警" },
{ value: res?.destroyCount, name: "破坏告警" },
{ value: res?.heartRateCount, name: "心率告警" },
{ value: res?.bloodOxygenCount, name: "血氧告警" },
{ value: res?.tempCount, name: "体温告警" },
];
myChart2.setOption(option2.value);
});
};
const getStatisticsUseCount = () => {
statisticsUseCount().then((res) => {
option1.value.series.data = [
{ value: res.defaultCount, name: "常规模式" },
{ value: res.outsideCount, name: "户外押送" },
{ value: res.monitorCount, name: "审讯模式" },
];
myChart1.setOption(option1.value);
});
};
const getStatisticsContent = (req) => {
statisticsContent(req).then((res) => {
if (res.times && res.times.length) {
option.value.xAxis.data = res.times.map((item) => (req.type == "hour" ? format(item) : format(item, "YYYY-MM-DD")));
option.value.series = [
{
name: "SOS预警数组",
data: res?.sosArr,
type: "line",
},
{
name: "围栏预警数组",
data: res?.railArr,
type: "line",
},
{
name: "破坏预警数组",
data: res?.destroyArr,
type: "line",
},
{
name: "生理预警数组",
data: res?.healthArr,
type: "line",
},
];
myChart.setOption(option.value);
}
});
};
const getStatisticsCount = () => {
statisticsCount().then((res) => {
funcList.value[0].num = res?.deviceTotal || 0;
funcList.value[1].num = res?.onlineCount || 0;
funcList.value[2].num = res?.warnCount || 0;
funcList.value[3].num = res?.addCount || 0;
});
};
const changePage = (val: number) => {
paging.page = val;
getData();
};
const handleResize = debounce(() => {
myChart.resize();
myChart1.resize();
myChart2.resize();
}, 200);
onMounted(() => { onMounted(() => {
try { try {
new MapCustom({ dom: "mapcontainer" }); new MapCustom({ dom: "mapcontainer" });
} catch (error) {} } catch (error) {}
if (chartRef.value) {
myChart = echarts.init(chartRef.value);
myChart.setOption(option.value);
myChart1 = echarts.init(chartRef1.value);
myChart1.setOption(option1);
myChart2 = echarts.init(chartRef2.value);
myChart2.setOption(option2.value);
window.addEventListener("resize", handleResize);
}
getData();
getStatisticsCount();
getStatisticsWarningApi();
getStatisticsUseCount();
getStatisticsContent({
startDate: `${format(d, "YYYY-MM-DD")} 00:00:00`,
endDate: `${format(d, "YYYY-MM-DD")} 23:59:59`,
type: "hour",
});
});
onUnmounted(() => {
window.removeEventListener("resize", handleResize);
}); });
</script> </script>
<style lang="less" scoped> <style lang="less" scoped>

View File

@ -1,6 +1,6 @@
<template> <template>
<el-row :gutter="20" class="monitoring-top"> <el-row :gutter="20" class="monitoring-top">
<el-col :span="24" :xs="24" :sm="12" :md="12" :lg="6" :xl="6" v-for="item in list"> <el-col :span="24" :xs="24" :sm="12" :md="12" :lg="6" :xl="6" v-for="(item, index) in funcList" :key="index">
<div class="item"> <div class="item">
<div class="item-left"> <div class="item-left">
<div class="item-left-head"> <div class="item-left-head">
@ -20,12 +20,30 @@
</el-row> </el-row>
</template> </template>
<script setup> <script setup>
const { list } = defineProps({ import { statisticsCount } from "@/api/index";
// import { onMounted, ref } from "vue";
list: { import handcuffs from "@/assets/img/handcuffs.png";
type: Array, import onLine from "@/assets/img/onLine.png";
default: () => [], import report from "@/assets/img/report.png";
}, import newly from "@/assets/img/newly.png";
let funcList = ref([
{ title: "手铐总数", en: "SHOUKAOZONGSHU", icon: handcuffs, num: 0, color: "##71DDF9" },
{ title: "在线数量", en: "ZAIXIANSHULIANG", icon: onLine, num: 0, color: "##71DDF9" },
{ title: "告警数量", en: "GAOJINGSHULIANG", icon: report, num: 0, color: "#8B51FD" },
{ title: "较昨日新增", en: "JIAOZUORIXINZENG", icon: newly, num: 0, color: "#FF6905" },
]);
const getStatisticsCount = () => {
statisticsCount().then((res) => {
funcList.value[0].num = res?.deviceTotal || 0;
funcList.value[1].num = res?.onlineCount || 0;
funcList.value[2].num = res?.warnCount || 0;
funcList.value[3].num = res?.addCount || 0;
});
};
onMounted(() => {
getStatisticsCount();
}); });
</script> </script>
<style scoped lang="less"> <style scoped lang="less">

View File

@ -4,11 +4,55 @@
<div class="title">使用设备统计</div> <div class="title">使用设备统计</div>
</div> </div>
<div class="card-chart"> <div class="card-chart">
<slot name="chart"></slot> <div ref="chartRef1" style="width: 100%; height: 100%"></div>
</div> </div>
</div> </div>
</template> </template>
<script setup></script> <script setup>
import { statisticsUseCount } from "@/api/index";
import { onMounted, ref } from "vue";
import * as echarts from "echarts";
import { useResize } from "@/utils/hooks";
const chartRef1 = ref(null);
let myChart = null;
let option = ref({
tooltip: {
trigger: "item",
},
legend: {
right: "left",
},
series: {
type: "pie",
radius: ["20%", "50%"],
data: [
{ value: 0, name: "常规模式" },
{ value: 0, name: "户外押送" },
{ value: 0, name: "审讯模式" },
],
},
});
const getStatisticsUseCount = () => {
statisticsUseCount().then((res) => {
option.value.series.data = [
{ value: res.defaultCount, name: "常规模式" },
{ value: res.outsideCount, name: "户外押送" },
{ value: res.monitorCount, name: "审讯模式" },
];
myChart.setOption(option.value);
});
};
useResize(() => {
myChart.resize();
});
onMounted(() => {
myChart = echarts.init(chartRef1.value);
myChart.setOption(option);
getStatisticsUseCount();
});
</script>
<style scoped lang="less"> <style scoped lang="less">
.card { .card {
height: 100%; height: 100%;

View File

@ -4,11 +4,60 @@
<div class="title">告警占比</div> <div class="title">告警占比</div>
</div> </div>
<div class="card-chart"> <div class="card-chart">
<slot name="chart"></slot> <div ref="chartRef2" style="width: 100%; height: 100%"></div>
</div> </div>
</div> </div>
</template> </template>
<script setup></script> <script setup>
import { statisticsWarningapi } from "@/api/index";
import { onMounted, ref } from "vue";
import * as echarts from "echarts";
import { useResize } from "@/utils/hooks";
const chartRef2 = ref(null);
let myChart = null;
let option = ref({
tooltip: {
trigger: "item",
},
legend: {
right: "left",
},
series: {
type: "pie",
radius: ["20%", "50%"],
data: [
{ value: 0, name: "SOS告警" },
{ value: 0, name: "围栏告警" },
{ value: 0, name: "破坏告警" },
{ value: 0, name: "心率告警" },
{ value: 0, name: "血氧告警" },
{ value: 0, name: "体温告警" },
],
},
});
const getStatisticsWarningApi = () => {
statisticsWarningapi().then((res) => {
option.value.series.data = [
{ value: res?.sosCount, name: "SOS告警" },
{ value: res?.railCount, name: "围栏告警" },
{ value: res?.destroyCount, name: "破坏告警" },
{ value: res?.heartRateCount, name: "心率告警" },
{ value: res?.bloodOxygenCount, name: "血氧告警" },
{ value: res?.tempCount, name: "体温告警" },
];
myChart.setOption(option.value);
});
};
useResize(() => {
myChart.resize();
});
onMounted(() => {
myChart = echarts.init(chartRef2.value);
myChart.setOption(option.value);
getStatisticsWarningApi();
});
</script>
<style scoped lang="less"> <style scoped lang="less">
.card { .card {
height: 100%; height: 100%;