Commit 568600c8 by 吴春元

对接:设备接入数量、系统分类统计、设备分类占比、设备告警统计、近 7 天告警统计、已部署系统、下级单位设备统计

parent 0768b0b5
......@@ -10,13 +10,24 @@ declare module 'vue' {
export interface GlobalComponents {
AsyncMap3D: typeof import('./src/components/ThreeMap/AsyncMap3D.vue')['default']
ContainerWrap: typeof import('./src/components/ContainerWrap/index.vue')['default']
ElButton: typeof import('element-plus/es')['ElButton']
ElCascader: typeof import('element-plus/es')['ElCascader']
ElCheckbox: typeof import('element-plus/es')['ElCheckbox']
ElCol: typeof import('element-plus/es')['ElCol']
ElDialog: typeof import('element-plus/es')['ElDialog']
ElEmpty: typeof import('element-plus/es')['ElEmpty']
ElForm: typeof import('element-plus/es')['ElForm']
ElFormItem: typeof import('element-plus/es')['ElFormItem']
ElIcon: typeof import('element-plus/es')['ElIcon']
ElImage: typeof import('element-plus/es')['ElImage']
ElInput: typeof import('element-plus/es')['ElInput']
ElOption: typeof import('element-plus/es')['ElOption']
ElRadioButton: typeof import('element-plus/es')['ElRadioButton']
ElRadioGroup: typeof import('element-plus/es')['ElRadioGroup']
ElRow: typeof import('element-plus/es')['ElRow']
ElSelect: typeof import('element-plus/es')['ElSelect']
ElTable: typeof import('element-plus/es')['ElTable']
ElTableColumn: typeof import('element-plus/es')['ElTableColumn']
HelloWorld: typeof import('./src/components/HelloWorld.vue')['default']
ItemWrap: typeof import('./src/components/ItemWrap/index.vue')['default']
ItemWrap2: typeof import('./src/components/ItemWrap2/index.vue')['default']
......
......@@ -35,6 +35,12 @@
<script type="text/javascript"
src="https://api.map.baidu.com/api?v=1.0&&type=webgl&ak=i8RgXE4a6vzGgbFs5s4ynlyUh6YnUQIB">
</script>
<script type="text/javascript"
src="https://webapi.amap.com/maps?v=2.0&key=fee919a8e608c39d1d528ec662a2ca17&plugin=AMap.DistrictSearch"></script>
<script src="https://a.amap.com/jsapi_demos/static/demo-center/js/demoutils.js"></script>
<!-- <link rel="stylesheet" href="https://a.amap.com/jsapi_demos/static/demo-center/css/demo-center.css" /> -->
</body>
</html>
\ No newline at end of file
......@@ -2,159 +2,348 @@
"hash": "3d95d7cd",
"configHash": "beb1c48d",
"lockfileHash": "2e2ef206",
"browserHash": "c584cccb",
"browserHash": "80dbb247",
"optimized": {
"vue": {
"src": "../../vue/dist/vue.runtime.esm-bundler.js",
"file": "vue.js",
"fileHash": "04ed1a8d",
"fileHash": "27726b81",
"needsInterop": false
},
"vue-router": {
"src": "../../vue-router/dist/vue-router.mjs",
"file": "vue-router.js",
"fileHash": "d0d63c13",
"fileHash": "0e4a94be",
"needsInterop": false
},
"element-plus": {
"src": "../../element-plus/es/index.mjs",
"file": "element-plus.js",
"fileHash": "0023de30",
"fileHash": "d9930fe7",
"needsInterop": false
},
"@element-plus/icons-vue": {
"src": "../../@element-plus/icons-vue/dist/index.js",
"file": "@element-plus_icons-vue.js",
"fileHash": "b1b62eda",
"fileHash": "2651facd",
"needsInterop": false
},
"nprogress": {
"src": "../../nprogress/nprogress.js",
"file": "nprogress.js",
"fileHash": "592ef64d",
"fileHash": "17e27234",
"needsInterop": true
},
"pinia": {
"src": "../../pinia/dist/pinia.mjs",
"file": "pinia.js",
"fileHash": "35bb8462",
"fileHash": "25cdbaf2",
"needsInterop": false
},
"js-cookie": {
"src": "../../js-cookie/dist/js.cookie.mjs",
"file": "js-cookie.js",
"fileHash": "809f3851",
"fileHash": "3e9ef2cb",
"needsInterop": false
},
"axios": {
"src": "../../axios/index.js",
"file": "axios.js",
"fileHash": "93780ea1",
"fileHash": "4d97a917",
"needsInterop": false
},
"element-plus/es": {
"src": "../../element-plus/es/index.mjs",
"file": "element-plus_es.js",
"fileHash": "3cd56c8a",
"fileHash": "eff4ad91",
"needsInterop": false
},
"element-plus/es/components/base/style/index": {
"src": "../../element-plus/es/components/base/style/index.mjs",
"file": "element-plus_es_components_base_style_index.js",
"fileHash": "3ad3246e",
"fileHash": "bb275ed1",
"needsInterop": false
},
"element-plus/es/components/select/style/index": {
"src": "../../element-plus/es/components/select/style/index.mjs",
"file": "element-plus_es_components_select_style_index.js",
"fileHash": "14ec8502",
"fileHash": "a7c12d1a",
"needsInterop": false
},
"element-plus/es/components/option/style/index": {
"src": "../../element-plus/es/components/option/style/index.mjs",
"file": "element-plus_es_components_option_style_index.js",
"fileHash": "4bbdcb43",
"fileHash": "f0a4374a",
"needsInterop": false
},
"element-plus/es/components/cascader/style/index": {
"src": "../../element-plus/es/components/cascader/style/index.mjs",
"file": "element-plus_es_components_cascader_style_index.js",
"fileHash": "9fe4e813",
"fileHash": "f3c25105",
"needsInterop": false
},
"mitt": {
"src": "../../mitt/dist/mitt.mjs",
"file": "mitt.js",
"fileHash": "1128ae26",
"fileHash": "27983b13",
"needsInterop": false
},
"element-plus/es/components/dialog/style/index": {
"src": "../../element-plus/es/components/dialog/style/index.mjs",
"file": "element-plus_es_components_dialog_style_index.js",
"fileHash": "32c6577a",
"fileHash": "250cc421",
"needsInterop": false
},
"element-plus/es/components/icon/style/index": {
"src": "../../element-plus/es/components/icon/style/index.mjs",
"file": "element-plus_es_components_icon_style_index.js",
"fileHash": "ac36d8d2",
"fileHash": "ffaccf8e",
"needsInterop": false
},
"element-plus/es/components/row/style/index": {
"src": "../../element-plus/es/components/row/style/index.mjs",
"file": "element-plus_es_components_row_style_index.js",
"fileHash": "bdb7445c",
"fileHash": "8146d606",
"needsInterop": false
},
"element-plus/es/components/col/style/index": {
"src": "../../element-plus/es/components/col/style/index.mjs",
"file": "element-plus_es_components_col_style_index.js",
"fileHash": "9d3f3730",
"fileHash": "0537d086",
"needsInterop": false
},
"@bmapgl-plugin/cluster": {
"src": "../../@bmapgl-plugin/cluster/index.js",
"file": "@bmapgl-plugin_cluster.js",
"fileHash": "7dcbcecf",
"fileHash": "76122e4a",
"needsInterop": true
},
"echarts": {
"src": "../../echarts/index.js",
"file": "echarts.js",
"fileHash": "458581f4",
"fileHash": "1c4eb8d2",
"needsInterop": false
},
"lodash-es": {
"src": "../../lodash-es/lodash.js",
"file": "lodash-es.js",
"fileHash": "bf9153cd",
"fileHash": "810dae55",
"needsInterop": false
},
"element-plus/es/components/form/style/index": {
"src": "../../element-plus/es/components/form/style/index.mjs",
"file": "element-plus_es_components_form_style_index.js",
"fileHash": "970d9f3d",
"needsInterop": false
},
"element-plus/es/components/button/style/index": {
"src": "../../element-plus/es/components/button/style/index.mjs",
"file": "element-plus_es_components_button_style_index.js",
"fileHash": "9f2dfef8",
"needsInterop": false
},
"element-plus/es/components/checkbox/style/index": {
"src": "../../element-plus/es/components/checkbox/style/index.mjs",
"file": "element-plus_es_components_checkbox_style_index.js",
"fileHash": "9af6d3a5",
"needsInterop": false
},
"element-plus/es/components/form-item/style/index": {
"src": "../../element-plus/es/components/form-item/style/index.mjs",
"file": "element-plus_es_components_form-item_style_index.js",
"fileHash": "796b51f1",
"needsInterop": false
},
"element-plus/es/components/input/style/index": {
"src": "../../element-plus/es/components/input/style/index.mjs",
"file": "element-plus_es_components_input_style_index.js",
"fileHash": "6810a81c",
"needsInterop": false
},
"jsencrypt": {
"src": "../../jsencrypt/lib/index.js",
"file": "jsencrypt.js",
"fileHash": "0700df96",
"needsInterop": false
},
"gsap": {
"src": "../../gsap/index.js",
"file": "gsap.js",
"fileHash": "93435ea0",
"needsInterop": false
},
"element-plus/es/components/image/style/index": {
"src": "../../element-plus/es/components/image/style/index.mjs",
"file": "element-plus_es_components_image_style_index.js",
"fileHash": "ade67ad6",
"needsInterop": false
},
"element-plus/es/components/radio-group/style/index": {
"src": "../../element-plus/es/components/radio-group/style/index.mjs",
"file": "element-plus_es_components_radio-group_style_index.js",
"fileHash": "2230c661",
"needsInterop": false
},
"element-plus/es/components/radio-button/style/index": {
"src": "../../element-plus/es/components/radio-button/style/index.mjs",
"file": "element-plus_es_components_radio-button_style_index.js",
"fileHash": "215535d2",
"needsInterop": false
},
"element-plus/es/components/table/style/index": {
"src": "../../element-plus/es/components/table/style/index.mjs",
"file": "element-plus_es_components_table_style_index.js",
"fileHash": "8fc19294",
"needsInterop": false
},
"element-plus/es/components/table-column/style/index": {
"src": "../../element-plus/es/components/table-column/style/index.mjs",
"file": "element-plus_es_components_table-column_style_index.js",
"fileHash": "0e0deee5",
"needsInterop": false
},
"element-plus/es/components/empty/style/index": {
"src": "../../element-plus/es/components/empty/style/index.mjs",
"file": "element-plus_es_components_empty_style_index.js",
"fileHash": "193464b9",
"needsInterop": false
},
"three": {
"src": "../../three/build/three.module.js",
"file": "three.js",
"fileHash": "a2a26ed0",
"needsInterop": false
},
"three/examples/jsm/controls/OrbitControls.js": {
"src": "../../three/examples/jsm/controls/OrbitControls.js",
"file": "three_examples_jsm_controls_OrbitControls__js.js",
"fileHash": "34cf4c25",
"needsInterop": false
},
"three/examples/jsm/renderers/CSS2DRenderer.js": {
"src": "../../three/examples/jsm/renderers/CSS2DRenderer.js",
"file": "three_examples_jsm_renderers_CSS2DRenderer__js.js",
"fileHash": "7468204b",
"needsInterop": false
},
"three/addons/renderers/CSS3DRenderer.js": {
"src": "../../three/examples/jsm/renderers/CSS3DRenderer.js",
"file": "three_addons_renderers_CSS3DRenderer__js.js",
"fileHash": "7bef1bf6",
"needsInterop": false
},
"three/addons/postprocessing/EffectComposer.js": {
"src": "../../three/examples/jsm/postprocessing/EffectComposer.js",
"file": "three_addons_postprocessing_EffectComposer__js.js",
"fileHash": "9ef4409f",
"needsInterop": false
},
"three/addons/postprocessing/RenderPass.js": {
"src": "../../three/examples/jsm/postprocessing/RenderPass.js",
"file": "three_addons_postprocessing_RenderPass__js.js",
"fileHash": "b83c3a33",
"needsInterop": false
},
"three/addons/postprocessing/OutlinePass.js": {
"src": "../../three/examples/jsm/postprocessing/OutlinePass.js",
"file": "three_addons_postprocessing_OutlinePass__js.js",
"fileHash": "6e02a652",
"needsInterop": false
},
"d3": {
"src": "../../d3/src/index.js",
"file": "d3.js",
"fileHash": "8a875b0d",
"needsInterop": false
},
"three/examples/jsm/lines/Line2.js": {
"src": "../../three/examples/jsm/lines/Line2.js",
"file": "three_examples_jsm_lines_Line2__js.js",
"fileHash": "b09fa5aa",
"needsInterop": false
},
"three/examples/jsm/lines/LineGeometry.js": {
"src": "../../three/examples/jsm/lines/LineGeometry.js",
"file": "three_examples_jsm_lines_LineGeometry__js.js",
"fileHash": "2853a7f4",
"needsInterop": false
},
"three/examples/jsm/lines/LineMaterial.js": {
"src": "../../three/examples/jsm/lines/LineMaterial.js",
"file": "three_examples_jsm_lines_LineMaterial__js.js",
"fileHash": "a4ea8742",
"needsInterop": false
},
"three/examples/jsm/renderers/CSS3DRenderer.js": {
"src": "../../three/examples/jsm/renderers/CSS3DRenderer.js",
"file": "three_examples_jsm_renderers_CSS3DRenderer__js.js",
"fileHash": "6deae146",
"needsInterop": false
},
"element-plus/es/locales.mjs": {
"src": "../../element-plus/es/locales.mjs",
"file": "element-plus_es_locales__mjs.js",
"fileHash": "61130207",
"needsInterop": false
}
},
"chunks": {
"chunk-QOFRX7VY": {
"file": "chunk-QOFRX7VY.js"
"chunk-IYMVL6BZ": {
"file": "chunk-IYMVL6BZ.js"
},
"chunk-S4WGN4LU": {
"file": "chunk-S4WGN4LU.js"
"chunk-NY4Z4LNU": {
"file": "chunk-NY4Z4LNU.js"
},
"chunk-TKM7ANES": {
"file": "chunk-TKM7ANES.js"
"chunk-FEBRQDH5": {
"file": "chunk-FEBRQDH5.js"
},
"chunk-JDSL2H4L": {
"file": "chunk-JDSL2H4L.js"
"chunk-ZYSY3OGM": {
"file": "chunk-ZYSY3OGM.js"
},
"chunk-MFBDIXYG": {
"file": "chunk-MFBDIXYG.js"
},
"chunk-ZS2WBNEI": {
"file": "chunk-ZS2WBNEI.js"
},
"chunk-4GXJ2MCF": {
"file": "chunk-4GXJ2MCF.js"
},
"chunk-HNP5HSLD": {
"file": "chunk-HNP5HSLD.js"
},
"chunk-RG7S6HTO": {
"file": "chunk-RG7S6HTO.js"
},
"chunk-TZOTQKIH": {
"file": "chunk-TZOTQKIH.js"
},
"chunk-EYASHUGA": {
"file": "chunk-EYASHUGA.js"
"chunk-372N3PK6": {
"file": "chunk-372N3PK6.js"
},
"chunk-G3PMV62Z": {
"file": "chunk-G3PMV62Z.js"
"chunk-PCQ6BLF2": {
"file": "chunk-PCQ6BLF2.js"
},
"chunk-IQCPA24U": {
"file": "chunk-IQCPA24U.js"
},
"chunk-TKM7ANES": {
"file": "chunk-TKM7ANES.js"
},
"chunk-S4WGN4LU": {
"file": "chunk-S4WGN4LU.js"
},
"chunk-3EK6V4TN": {
"file": "chunk-3EK6V4TN.js"
},
"chunk-5HXAQIWW": {
"file": "chunk-5HXAQIWW.js"
},
"chunk-JDSL2H4L": {
"file": "chunk-JDSL2H4L.js"
},
"chunk-G3PMV62Z": {
"file": "chunk-G3PMV62Z.js"
}
}
}
\ No newline at end of file
......@@ -510,9 +510,10 @@ import {
virtualizedScrollbarProps,
watermarkProps,
zIndexContextKey
} from "./chunk-QOFRX7VY.js";
import "./chunk-S4WGN4LU.js";
} from "./chunk-IQCPA24U.js";
import "./chunk-TKM7ANES.js";
import "./chunk-S4WGN4LU.js";
import "./chunk-3EK6V4TN.js";
import "./chunk-JDSL2H4L.js";
import "./chunk-G3PMV62Z.js";
var export_dayjs = import_dayjs.default;
......
import request from "@/utils/request";
// 获取截止时间
export function nowTime() {
return request({
url: "/system/dept/getNow",
method: "get",
});
}
// 获取设备接入数量(在线、离线)
export function allDevice(deptId: number) {
return request({
url: "/device/baseDevice/getAllDevice?deptId=" + deptId,
method: "get",
});
}
// 获取设备分类占比
export function deviceCountByType(deptId: number, grade: String) {
return request({
url:
"/device/baseDevice/getDeviceCountByType?deptId=" +
deptId +
"&grade=" +
grade,
method: "get",
});
}
// 获取设备接入数量(在线、离线)
export function deviceCountByChild(deptId: number) {
return request({
url: "/device/baseDevice/getDeviceCountByChild?deptId=" + deptId,
method: "get",
});
}
// 设备告警统计
export function alarmNumCount(deptId: number) {
return request({
url: "/device/alarmInfo/alarmNumCount?deptId=" + deptId,
method: "get",
});
}
// 近7天告警统计
export function countByDay(data: any) {
return request({
url: "/device/alarmInfo/countByDay",
method: "post",
data: data,
});
}
// 已部署系统
export function countBySysType(deptId: number) {
return request({
url: "/system/dept/countBySysType?deptId=" + deptId,
method: "get",
});
}
......@@ -71,6 +71,7 @@ $blue-hover: #40a9ff; // 悬停蓝色
.el-input__inner {
color: #{$white-text};
}
}
......
<template>
<div class="user flex items-center cursor-pointer">
<el-icon color="#fff" size="24"><SwitchButton /></el-icon>
</div>
<div class="user flex cursor-pointer mt-4 mr-3" @click="logout">
<el-icon color="#fff" size="24"><SwitchButton /></el-icon>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { SwitchButton } from '@element-plus/icons-vue'
import { ref } from "vue";
import { SwitchButton } from "@element-plus/icons-vue";
import useUserStore from "@/store/modules/user";
import { ElMessageBox } from "element-plus";
const user = ref({
name: '张三',
avatar: 'https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif',
})
</script>
\ No newline at end of file
function logout() {
ElMessageBox.confirm("确定注销并退出系统吗?", "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
})
.then(() => {
useUserStore()
.logOut()
.then(() => {
location.href = "/";
});
})
.catch(() => {});
}
</script>
......@@ -45,7 +45,9 @@
</el-select>
</div>
</div>
<User class=""></User>
</div>
<div class="content relative">
<router-view></router-view>
</div>
......@@ -55,6 +57,7 @@
<script setup lang="ts">
import { AppSetting } from "@/setting";
import Menu from "./components/Menu.vue";
import User from "./components/User.vue";
import { ref } from "vue";
import { eventBus } from "@/eventBus";
......
......@@ -4,18 +4,18 @@
<!-- 第一列(3个均匀分布的框) -->
<el-col :span="7" class="column">
<div class="grid-content tall-box">
<ContainerWrap title="设备接入数量" endDate="2025-10-18">
<DeviceNumber />
<ContainerWrap title="设备接入数量" :endDate="nowTimes">
<DeviceNumber :dept-id="selectedOffice[0]" />
</ContainerWrap>
</div>
<div class="grid-content tall-box">
<ContainerWrap title="系统分类统计" endDate="2025-10-18">
<SysClassification />
<ContainerWrap title="系统分类统计" :endDate="nowTimes">
<SysClassification :dept-id="selectedOffice[0]" />
</ContainerWrap>
</div>
<div class="grid-content tall-box">
<ContainerWrap title="设备分类占比" endDate="2025-10-18">
<DeviceClassification />
<ContainerWrap title="设备分类占比" :endDate="nowTimes">
<DeviceClassification :dept-id="selectedOffice[0]" />
</ContainerWrap>
</div>
</el-col>
......@@ -23,13 +23,13 @@
<!-- 第二列(第一个框占2/3,第二个框占1/3) -->
<el-col :span="10" class="column">
<div class="grid-content wide-box" style="flex: 2">
<ContainerWrap title="设备分布图" endDate="2025-10-18">
<MapEcharts />
<ContainerWrap title="设备分布图" :endDate="nowTimes">
<MapEcharts :dept-id="selectedOffice[0]" />
</ContainerWrap>
</div>
<div class="grid-content wide-box" style="flex: 1">
<ContainerWrap title="各分子公司设备统计" endDate="2025-10-18">
<SubsidiaryCompanyEquipment />
<ContainerWrap title="下级单位设备统计" :endDate="nowTimes">
<SubsidiaryCompanyEquipment :dept-id="selectedOffice[0]" />
</ContainerWrap>
</div>
</el-col>
......@@ -37,18 +37,18 @@
<!-- 第三列(3个均匀分布的框) -->
<el-col :span="7" class="column">
<div class="grid-content tall-box">
<ContainerWrap title="设备告警统计" endDate="2025-10-18">
<EquipmentAlarm />
<ContainerWrap title="设备告警统计" :endDate="nowTimes">
<EquipmentAlarm :dept-id="selectedOffice[0]" />
</ContainerWrap>
</div>
<div class="grid-content tall-box">
<ContainerWrap title="近7天告警统计" endDate="2025-10-18">
<Alert7Days />
<ContainerWrap title="近7天告警统计" :endDate="nowTimes">
<Alert7Days :dept-id="selectedOffice[0]" />
</ContainerWrap>
</div>
<div class="grid-content tall-box">
<ContainerWrap title="已部署系统" endDate="2025-10-18">
<DeployProject />
<ContainerWrap title="已部署系统" :endDate="nowTimes">
<DeployProject :dept-id="selectedOffice[0]" />
</ContainerWrap>
</div>
</el-col>
......@@ -126,7 +126,7 @@ import SysClassification from "./components/SysClassification.vue";
import DeviceClassification from "./components/DeviceClassification.vue";
import EquipmentAlarm from "./components/EquipmentAlarm.vue";
import Alert7Days from "./components/Alert7Days.vue";
import MapEcharts from "./components/MapEcharts.vue";
import MapEcharts from "./components/Map1.vue";
import DeployProject from "./components/DeployProject.vue";
import SubsidiaryCompanyEquipment from "./components/SubsidiaryCompanyEquipment.vue";
import ContainerWrap from "@/components/ContainerWrap/index.vue";
......@@ -134,8 +134,10 @@ import ContainerWrap from "@/components/ContainerWrap/index.vue";
import { ref, onMounted, onUnmounted } from "vue";
import { eventBus } from "@/eventBus";
import { nowTime } from "@/api/iot/home";
const nowTimes = ref("");
const dialogVisible = ref(false);
const selectedOffice = ref([]);
const selectedOffice = ref([100]);
const officeTree = ref([
{
value: "1",
......@@ -251,12 +253,26 @@ onMounted(() => {
});
// 监听机构变化事件
eventBus.on("officeChange", (office) => {});
eventBus.on("officeChange", (office) => {
selectedOffice.value = office;
});
});
onUnmounted(() => {
eventBus.off("sceneChange"); // 避免内存泄漏
});
//获取截止时间
function getNowTime() {
nowTime().then((res: any) => {
if (res.code === 200) {
//将res.data时间戳转换为时间格式
nowTimes.value = new Date(res.data).toLocaleString();
}
});
}
getNowTime();
</script>
<style scoped>
......
......@@ -6,21 +6,70 @@
<script setup lang="ts">
import { ref, onMounted, onBeforeUnmount } from "vue";
import * as echarts from "echarts";
import { countByDay } from "@/api/iot/home";
import { de } from "element-plus/es/locales.mjs";
const chartRef = ref<HTMLDivElement>();
const chartContainer = ref<HTMLDivElement>();
const chartInstance = ref<echarts.ECharts | null>(null);
let resizeObserver: ResizeObserver | null = null;
const yData = ref([2, 1, 1, 1, 3, 2, 2]);
const wData = ref([0, 0, 0, 1, 1, 1, 0]);
const xData = ref<any[]>([
{
time: "",
timestamp: 0,
haveHandle: 0, //已处理
notHandle: 0, //未处理
haveIgnore: 0, //已忽略
},
{
time: "",
timestamp: 0,
haveHandle: 0, //已处理
notHandle: 0, //未处理
haveIgnore: 0, //已忽略
},
{
time: "",
timestamp: 0,
haveHandle: 0, //已处理
notHandle: 0, //未处理
haveIgnore: 0, //已忽略
},
{
time: "",
timestamp: 0,
haveHandle: 0, //已处理
notHandle: 0, //未处理
haveIgnore: 0, //已忽略
},
{
time: "",
timestamp: 0,
haveHandle: 0, //已处理
notHandle: 0, //未处理
haveIgnore: 0, //已忽略
},
{
time: "",
timestamp: 0,
haveHandle: 0, //已处理
notHandle: 0, //未处理
haveIgnore: 0, //已忽略
},
{
time: "",
timestamp: 0,
haveHandle: 0, //已处理
notHandle: 0, //未处理
haveIgnore: 0, //已忽略
},
]);
const initChart = () => {
if (!chartRef.value) return;
// 销毁旧实例
chartInstance.value?.dispose();
chartInstance.value = echarts.init(chartRef.value);
const option = {
......@@ -31,7 +80,7 @@ const initChart = () => {
},
},
legend: {
data: ["已处理", "未处理"],
data: ["已处理", "未处理", "已忽略"],
top: 0,
right: 7,
itemWidth: 15,
......@@ -42,18 +91,11 @@ const initChart = () => {
},
},
xAxis: {
data: [
"2025-10-13",
"2025-10-14",
"2025-10-15",
"2025-10-16",
"2025-10-17",
"2025-10-18",
"2025-10-19",
],
data: xData.value.map((item) => item.time.substring(0, 10)),
axisLabel: {
color: "#66FFFF",
fontSize: 11,
rotate: 15,
},
},
yAxis: {
......@@ -73,7 +115,7 @@ const initChart = () => {
{
name: "已处理",
type: "bar",
barWidth: 16,
barWidth: 13,
label: {
show: true,
position: "top",
......@@ -88,14 +130,14 @@ const initChart = () => {
{ offset: 1, color: "rgba(146, 225, 255, 1)" },
]),
},
data: yData.value,
data: xData.value.map((item) => item.haveHandle),
z: 10,
zlevel: 0,
},
{
name: "未处理",
type: "bar",
barWidth: 16,
barWidth: 13,
label: {
show: true,
position: "top",
......@@ -110,7 +152,31 @@ const initChart = () => {
{ offset: 1, color: "rgba(250, 76, 102, 1)" },
]),
},
data: wData.value,
data: xData.value.map((item) => item.notHandle),
z: 10,
zlevel: 0,
},
{
name: "已忽略",
type: "bar",
barWidth: 13,
label: {
show: true,
position: "top",
textStyle: {
color: "#fff",
fontSize: 10,
},
},
itemStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
// 已忽略灰色
// 已忽略灰色
{ offset: 1, color: "rgba(128, 128, 128, 1)" },
{ offset: 0, color: "rgba(255, 255, 255, 1)" },
]),
},
data: xData.value.map((item) => item.haveIgnore),
z: 10,
zlevel: 0,
},
......@@ -124,10 +190,10 @@ const initChart = () => {
symbolMargin: 2,
symbol: "rect",
symbolClip: true,
symbolSize: [18, 2],
symbolSize: [14, 2],
symbolPosition: "start",
symbolOffset: [-9, 1],
data: yData.value,
symbolOffset: [-14, 1],
data: xData.value.map((item) => item.haveHandle),
width: 2,
z: 0,
zlevel: 1,
......@@ -142,20 +208,38 @@ const initChart = () => {
symbolMargin: 2,
symbol: "rect",
symbolClip: true,
symbolSize: [18, 2],
symbolSize: [13, 2],
symbolPosition: "start",
symbolOffset: [9, 1],
data: wData.value,
symbolOffset: [0, 1],
data: xData.value.map((item) => item.notHandle),
width: 2,
z: 0,
zlevel: 1,
},
{
// 已忽略分隔
type: "pictorialBar",
itemStyle: {
color: "#0F375F",
},
symbolRepeat: "fixed",
symbolMargin: 2,
symbol: "rect",
symbolClip: true,
symbolSize: [14, 2],
symbolPosition: "start",
symbolOffset: [14, 1],
data: xData.value.map((item) => item.haveIgnore),
width: 2,
z: 0,
zlevel: 1,
},
],
color: ["#4C9FFF", "#FF6E76"],
color: ["#4C9FFF", "#FF6E76", "#FF0000"],
barWidth: "20%",
grid: {
left: "4%",
right: "5%",
left: "7%",
right: "3%",
bottom: "5%",
top: "20%",
containLabel: true,
......@@ -176,7 +260,69 @@ onMounted(() => {
chartContainer.value.style.height = "100%";
}
initChart();
// 构建请求参数 beginTime(年月日时分秒)7天前 endTime(年月日时分秒)今天
const data = {
beginTime:
new Date(new Date().getTime() - 7 * 24 * 60 * 60 * 1000)
.toISOString()
.substring(0, 10) + " 00:00:00",
endTime: new Date().toISOString().substring(0, 10) + " 23:59:59",
};
// 获取近7天告警统计
countByDay(data).then((res) => {
const haveHandle = res.data.haveHandle; //已处理
const notHandle = res.data.notHandle; //未处理
const haveIgnore = res.data.haveIgnore; //已忽略
// 将近7天的日期添加到xData.time中,格式为yyyy-MM-dd 00:00:00
xData.value.forEach((item, index) => {
item.time = `${new Date(
new Date().getTime() - (6 - index) * 24 * 60 * 60 * 1000
).getFullYear()}-${
new Date(
new Date().getTime() - (6 - index) * 24 * 60 * 60 * 1000
).getMonth() + 1
}-${new Date(
new Date().getTime() - (6 - index) * 24 * 60 * 60 * 1000
).getDate()} 00:00:00`;
});
// 将近7天的时间戳添加到xData.timestamp中
xData.value.forEach((item, index) => {
item.timestamp = new Date(item.time).getTime();
});
if (haveHandle) {
//将haveHandle的 key 和 xData.timestamp 进行匹配,将对应的值添加到xData.haveHandle中
for (let key in haveHandle) {
for (let item of xData.value) {
if (item.timestamp === Number(key)) {
item.haveHandle = haveHandle[key];
}
}
}
}
if (notHandle) {
//将notHandle的 key 和 xData.timestamp 进行匹配,将对应的值添加到xData.notHandle中
for (let key in notHandle) {
for (let item of xData.value) {
if (item.timestamp === Number(key)) {
item.notHandle = notHandle[key];
}
}
}
}
if (haveIgnore) {
//将haveIgnore的 key 和 xData.timestamp 进行匹配,将对应的值添加到xData.haveIgnore中
for (let key in haveIgnore) {
for (let item of xData.value) {
if (item.timestamp === Number(key)) {
item.haveIgnore = haveIgnore[key];
}
}
}
}
initChart();
});
// 使用 ResizeObserver 监听容器尺寸变化
if (chartRef.value) {
......
......@@ -8,7 +8,7 @@
class="flex flex-1 w-full h-full"
>
<div class="flex flex-col justify-center">
<img :src="item.imageUrl" width="90px" />
<!-- <img :src="item.imageUrl" width="90px" /> -->
<div class="bg-container flex flex-col items-center">
<div class="text-[#66FFFF] text-[14px]">
{{ item.name }}
......@@ -29,28 +29,68 @@ import sysBg1 from "@/assets/imgs/sys-bg1.png";
import sysBg2 from "@/assets/imgs/sys-bg2.png";
import sysBg3 from "@/assets/imgs/sys-bg3.png";
import sysBg4 from "@/assets/imgs/sys-bg4.png";
import { countBySysType } from "@/api/iot/home";
const props = defineProps({
deptId: {
type: Number,
default: 0,
},
});
const projectList = ref([
{
type: 1,
name: "智慧隧道",
imageUrl: sysBg1,
num: "3",
num: 0,
},
{
type: 2,
name: "幸福小镇",
imageUrl: sysBg2,
num: "2",
num: 0,
},
{
type: 3,
name: "智慧园区",
imageUrl: sysBg3,
num: 0,
},
{
type: 4,
name: "拌合站",
imageUrl: sysBg3,
num: 0,
},
{
type: 5,
name: "视频监控",
imageUrl: sysBg3,
num: "5",
num: 0,
},
{
name: "智慧园区",
type: 6,
name: "钢筋场",
imageUrl: sysBg4,
num: "7",
num: 0,
},
]);
function getCountBySysType() {
countBySysType(props.deptId).then((res: any) => {
if (res.code === 200) {
res.data.map((item: any) => {
projectList.value.map((i: any) => {
if (i.type === item.type) {
i.num = item.count || 0;
}
});
});
}
});
}
getCountBySysType();
</script>
<style scoped lang="scss">
.bg-container {
......
......@@ -7,11 +7,22 @@
<script setup lang="ts">
import { ref, onMounted, onBeforeUnmount } from "vue";
import * as echarts from "echarts";
import { deviceCountByType } from "@/api/iot/home";
const chartContainer = ref<HTMLElement | null>(null);
const chartInstance = ref<echarts.ECharts | null>(null);
let resizeObserver: ResizeObserver | null = null;
const props = defineProps({
deptId: {
type: Number,
default: 0,
},
});
const datas = ref([]);
const totalCount = ref(0);
const initChart = () => {
if (!chartContainer.value) return;
// 销毁旧实例
......@@ -19,16 +30,6 @@ const initChart = () => {
// 初始化图表
chartInstance.value = echarts.init(chartContainer.value);
const datas = [
{ value: 1048, name: "视频监控" },
{ value: 735, name: "人车识别" },
{ value: 580, name: "水电监测" },
{ value: 484, name: "消防监测" },
{ value: 300, name: "人员定位监测" },
{ value: 300, name: "环境气体监测" },
{ value: 300, name: "车辆定位监测" },
];
const chartPieColors = [
[
{ offset: 0, color: "#F5C815" },
......@@ -64,7 +65,7 @@ const initChart = () => {
],
];
const seriesData = datas.map((item, index) => ({
const seriesData = datas.value.map((item: any, index: number) => ({
value: item.value,
name: item.name,
itemStyle: {
......@@ -115,18 +116,19 @@ const initChart = () => {
right: 150,
radius: ["50%", "70%"],
center: ["53%", "48%"],
avoidLabelOverlap: false,
avoidLabelOverlap: true,
label: {
show: false,
position: "outside",
formatter: "{a|{b}:{d}%}\n{hr|}",
rich: {
hr: {
backgroundColor: "t",
borderRadius: 3,
width: 3,
height: 3,
padding: [3, 3, 0, -12],
width: 1,
height: 1,
padding: [2, 1, 0, -0],
},
a: {
padding: [-30, 15, -20, 15],
......@@ -161,9 +163,11 @@ const initChart = () => {
label: {
show: true,
position: "center",
formatter: "37800",
//item.value的设备总数
formatter: "" + totalCount.value,
// formatter: "",
color: "#FEBC22",
fontSize: 23,
fontSize: 22,
fontWeight: "bold",
fontFamily: "Arial",
},
......@@ -178,7 +182,6 @@ const initChart = () => {
},
],
};
chartInstance.value.setOption(option);
};
......@@ -187,7 +190,21 @@ const handleResize = () => {
};
onMounted(() => {
initChart();
deviceCountByType(props.deptId, "1").then((res: any) => {
if (res.code === 200) {
//将res.dcbt中的数据赋值给datas.value
datas.value = res.dcbt.map((item: any) => ({
value: item.count,
name: item.deviceType.name,
}));
// 计算总设备数量
totalCount.value = res.dcbt.reduce(
(acc: number, item: any) => acc + item.count,
0
);
initChart();
}
});
// 使用 ResizeObserver 监听容器大小变化
if (chartContainer.value) {
......
......@@ -4,7 +4,7 @@
<!-- 数量 -->
<div class="gap-2 flex">
<div
v-for="(digit, index) in digitsNumber"
v-for="(digit, index) in sumCount"
:key="index"
class="card-container flex-1"
>
......@@ -40,7 +40,7 @@
在线数量
<text
class="text-[#66FFFF] xl:text-[14px] 2xl:text-[18px] pl-1 font-[YouSheBiaoTiHei]"
>{{ onlineNumber }}</text
>{{ onLineCount }}</text
>
</div>
......@@ -51,7 +51,7 @@
>
<text
class="text-[#F01111] xl:text-[14px] 2xl:text-[18px] pr-1 font-[YouSheBiaoTiHei]"
>{{ offlineNumber }}</text
>{{ noLineCount }}</text
>
离线数量
</div>
......@@ -61,27 +61,51 @@
<script setup lang="ts">
import { ref } from "vue";
import { computed } from "vue";
import { allDevice } from "@/api/iot/home";
var originalNumber = ref("1610");
//将数字转换为字符串并补零到9位,并将其拆分为单个数字数组
var digitsNumber = originalNumber.value.padStart(9, "0").split("");
const props = defineProps({
deptId: {
type: Number,
default: 0,
},
});
// 设备接入数量(在线、离线)
var sumCount = ref([]);
//在线数量
var onlineNumber = ref(1500);
var onLineCount = ref(0);
//离线数量
var offlineNumber = ref(110);
var noLineCount = ref(0);
//计算在线和离线百分比
const totalDevices = computed(() => onlineNumber.value + offlineNumber.value);
const totalDevices = computed(() => onLineCount.value + noLineCount.value);
const onlinePercent = computed(() => {
return totalDevices.value > 0
? Math.round((onlineNumber.value / totalDevices.value) * 100)
? Math.round((onLineCount.value / totalDevices.value) * 100)
: 0;
});
const offlinePercent = computed(() => {
return totalDevices.value > 0
? Math.round((offlineNumber.value / totalDevices.value) * 100)
? Math.round((noLineCount.value / totalDevices.value) * 100)
: 0;
});
// 获取设备接入数量(在线、离线)
function getAllDevice() {
allDevice(props.deptId).then((res: any) => {
if (res.code === 200) {
// 将数字转换为字符串并补零到9位,并将其拆分为单个数字数组
sumCount.value = (res.numMap?.sumCount?.toString() || "0")
.padStart(9, "0")
.split("");
// 在线数量
onLineCount.value = res.numMap?.onLineCount || 0;
// 离线数量
noLineCount.value = res.numMap?.noLineCount || 0;
}
});
}
getAllDevice();
</script>
<style scoped lang="scss">
......
......@@ -5,7 +5,7 @@
<div
class="text-center font-bold text-[34px] text-[#66FFFF] font-[YouSheBiaoTiHei]"
>
10
{{ alarmTotal }}
</div>
<div class="text-center text-[12px] text-[#66FFFF] mt-0">告警总数</div>
</div>
......@@ -16,7 +16,7 @@
<div
class="font-medium text-[28px] text-[#66FFFF] font-[YouSheBiaoTiHei]"
>
0
{{ unprocessedCount }}
</div>
<img
src="@/assets/imgs/equ-right-icon.png"
......@@ -31,7 +31,7 @@
<div
class="font-medium text-[28px] text-[#66FFFF] font-[YouSheBiaoTiHei]"
>
8
{{ processedCount }}
</div>
<img
src="@/assets/imgs/equ-right-icon.png"
......@@ -40,12 +40,57 @@
/>
<div class="text-[12px] text-white mt-1">已处理</div>
</div>
<!-- 已处理统计 -->
<div class="flex flex-col items-center">
<div
class="font-medium text-[28px] text-[#66FFFF] font-[YouSheBiaoTiHei]"
>
{{ processedCount }}
</div>
<img
src="@/assets/imgs/equ-right-icon.png"
class="w-[45px] h-[50px] my-1"
alt="已处理图标"
/>
<div class="text-[12px] text-white mt-1">已忽略</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
// 可以保留空的script setup,或者移除如果不需要
import { alarmNumCount } from "@/api/iot/home";
import { onMounted, ref } from "vue";
// 告警总数
let alarmTotal = ref(0);
// 未处理统计
let unprocessedCount = ref(0);
// 已处理统计
let processedCount = ref(0);
// 已忽略统计
let ignoredCount = ref(0);
const props = defineProps({
deptId: {
type: Number,
default: 0,
},
});
// 获取设备告警统计
function getAlarmNumCount() {
alarmNumCount(props.deptId).then((res: any) => {
if (res.code === 200) {
alarmTotal.value = res.data.count;
unprocessedCount.value = res.data.notHandle;
processedCount.value = res.data.haveHandle;
ignoredCount.value = res.data.haveIgnore;
}
});
}
getAlarmNumCount();
</script>
<style scoped lang="scss">
......
<template>
<div id="map-container" ref="mapContainer" class="w-full h-full"></div>
</template>
<script>
import { ref, onMounted, onBeforeUnmount } from "vue";
onMounted(() => {
initMap();
});
function initMap() {
//创建地图对象
this.map = new AMap.Map("map", {
center: [113.280637, 23.125178],
resizeEnable: true,
zoom: 7,
});
//获取边界坐标点
AMap.plugin("AMap.DistrictSearch", () => {
var districtSearch = new AMap.DistrictSearch({
// 关键字对应的行政区级别,共有5种级别
level: "province",
// 是否显示下级行政区级数,1表示返回下一级行政区
subdistrict: 0,
// 返回行政区边界坐标点
extensions: "all",
});
// 搜索所有省/直辖市信息
districtSearch.search("广东", (status, result) => {
// 查询成功时,result即为对应的行政区信息
this.handlePolygon(result);
});
});
}
// function handlePolygon(result) {
// let bounds = result.districtList[0].boundaries
// if (bounds) {
// for (let i = 0, l = bounds.length; i < l; i++) {
// //生成行政区划polygon
// let polygon = new AMap.Polygon({
// map: this.map, // 指定地图对象
// strokeWeight: 1, // 轮廓线宽度
// path: bounds[i], //轮廓线的节点坐标数组
// fillOpacity: 0.15, //透明度
// fillColor: '#256edc', //填充颜色
// strokeColor: '#256edc', //线条颜色
// })
// polygon.on('click', (e) => {
// // 点击绘制的区域时执行其他交互
// // ......
// })
// }
// // 地图自适应
// this.map.setFitView()
// }
</script>
<style scoped></style>
......@@ -7,13 +7,21 @@
import { ref, onMounted, onBeforeUnmount } from "vue";
import * as echarts from "echarts";
import { debounce } from "lodash-es"; // 或使用自定义防抖函数
import { deviceCountByChild } from "@/api/iot/home";
import { de } from "element-plus/es/locales.mjs";
const chartRef = ref<HTMLDivElement>();
const chart = ref<echarts.ECharts | null>(null);
let resizeObserver: ResizeObserver | null = null;
const yData = ref<any[]>([]);
const xData = ref<any[]>([]);
const yData = ref([120, 200, 150, 190, 170, 110, 130, 130, 130, 130, 130]);
const props = defineProps({
deptId: {
type: Number,
default: 0,
},
});
// 防抖的重绘函数
const handleResize = debounce(() => {
chart.value?.resize();
......@@ -22,12 +30,9 @@ const handleResize = debounce(() => {
// 初始化图表
const initChart = () => {
if (!chartRef.value) return;
// 如果已有图表实例,先销毁
chart.value?.dispose();
chart.value = echarts.init(chartRef.value);
const option = {
tooltip: {
trigger: "axis",
......@@ -39,19 +44,7 @@ const initChart = () => {
show: false,
},
xAxis: {
data: [
"路桥一公司",
"路桥二公司",
"路桥三公司",
"路桥四公司",
"路桥五公司",
"路桥七公司",
"路桥八公司",
"路桥九公司",
"浙江分公司",
"市政公司",
"特种分公司",
],
data: xData.value,
axisLabel: {
color: "#66FFFF",
fontSize: 11,
......@@ -108,8 +101,24 @@ const initChart = () => {
};
onMounted(() => {
initChart();
deviceCountByChild(props.deptId).then((res: any) => {
if (res.code === 200) {
const childDeptList = res.result?.childDeptList || [];
const numMap = res.result?.numMap || [];
//numMap中的 key 和childDeptList.deptId 进行匹配
Object.entries(numMap).forEach(([key, value]) => {
console.log(`Key: ${key}, Value: ${value}`);
yData.value.push(value);
// 匹配到后,将对应的部门名称添加到 xData 中
for (let i = 0; i < childDeptList.length; i++) {
if (childDeptList[i].deptId === Number(key)) {
xData.value.push(childDeptList[i].deptName);
}
}
});
initChart();
}
});
// 使用 ResizeObserver 监听容器尺寸变化
if (chartRef.value) {
resizeObserver = new ResizeObserver(() => {
......@@ -117,7 +126,6 @@ onMounted(() => {
});
resizeObserver.observe(chartRef.value);
}
// 仍然监听窗口大小变化作为后备
window.addEventListener("resize", handleResize);
});
......@@ -128,7 +136,6 @@ onBeforeUnmount(() => {
resizeObserver.disconnect();
resizeObserver = null;
}
window.removeEventListener("resize", handleResize);
chart.value?.dispose();
chart.value = null;
......
<template>
<div class="login flex-col pb-10">
<h3 class="text-5xl font-bold text-center mb-15">
<h3 class="text-6xl font-bold text-center mb-15 text-[#fff]">
{{ AppSetting.projectName }}
</h3>
......@@ -81,7 +81,10 @@
</el-form>
<!-- 底部 -->
<div class="el-login-footer">
<span>Copyright © 2018-2024 ruoyi.vip All Rights Reserved.</span>
<span
>Copyright © 2025 {{ AppSetting.projectName }} All Rights
Reserved.</span
>
</div>
</div>
</template>
......@@ -267,23 +270,22 @@ getCookie();
// display: flex;
// // --font-size: 14px;
// margin-bottom: 13px;
// }
:deep(.el-input .el-input__inner) {
color: #fff;
// background: yellow !important;
// border: 0px solid rgba(255, 255, 255, 0.3);
}
:deep(.el-input--large .el-input__wrapper) {
// padding: 1px 15px;
background-color: #163054;
// box-shadow: 0 0 0 1px #2261ba inset !important; /* 可选:修改边框颜色 */
box-shadow: 0px 2px 4px rgba(79, 151, 214, 0.33);
// border: 0px solid rgba(255, 255, 255, 0.3);
}
// :deep(.el-input .el-input__inner) {
// color: #fff;
// background-color: #163054;
// border: 0px solid rgba(255, 255, 255, 0.3);
// }
// :deep(.el-input--large .el-input__wrapper) {
// // padding: 1px 15px;
// background-color: #163054;
// // box-shadow: 0 0 0 1px #2261ba inset !important; /* 可选:修改边框颜色 */
// box-shadow: 0px 2px 4px rgba(79, 151, 214, 0.33);
// // border: 0px solid rgba(255, 255, 255, 0.3);
// }
:deep(.el-input__inner) {
// background-color: rgba(22, 48, 84, 0.6);
}
// :deep(.el-input__inner) {
// background-color: rgba(22, 48, 84, 0.6);
// }
</style>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment