更新
This commit is contained in:
@@ -3,9 +3,9 @@ VITE_CLI_PORT = 8080
|
|||||||
VITE_SERVER_PORT = 8888
|
VITE_SERVER_PORT = 8888
|
||||||
VITE_BASE_API = /api
|
VITE_BASE_API = /api
|
||||||
VITE_FILE_API = /api
|
VITE_FILE_API = /api
|
||||||
# VITE_BASE_PATH = http://192.168.1.9:8888
|
VITE_BASE_PATH = http://192.168.1.9:8888
|
||||||
# VITE_BASE_PATH = http://192.168.110.98:8888
|
# VITE_BASE_PATH = http://192.168.110.98:8888
|
||||||
VITE_BASE_PATH = https://www.xingoil.com/api
|
# VITE_BASE_PATH = https://www.xingoil.com/api
|
||||||
VITE_POSITION = open
|
VITE_POSITION = open
|
||||||
VITE_EDITOR = code
|
VITE_EDITOR = code
|
||||||
// VITE_EDITOR = webstorm 如果使用webstorm开发且要使用dom定位到代码行功能 请先自定添加 webstorm到环境变量 再将VITE_EDITOR值修改为webstorm
|
// VITE_EDITOR = webstorm 如果使用webstorm开发且要使用dom定位到代码行功能 请先自定添加 webstorm到环境变量 再将VITE_EDITOR值修改为webstorm
|
||||||
|
|||||||
@@ -11,15 +11,14 @@
|
|||||||
"/src/view/dashboard/components/table.vue": "Table",
|
"/src/view/dashboard/components/table.vue": "Table",
|
||||||
"/src/view/dashboard/components/wiki.vue": "Wiki",
|
"/src/view/dashboard/components/wiki.vue": "Wiki",
|
||||||
"/src/view/dashboard/index.vue": "Dashboard",
|
"/src/view/dashboard/index.vue": "Dashboard",
|
||||||
"/src/view/equipment/alarmRecord/index.vue": "Index",
|
|
||||||
"/src/view/equipment/gateway/index.vue": "Index",
|
"/src/view/equipment/gateway/index.vue": "Index",
|
||||||
|
"/src/view/equipment/index.vue": "Equipment",
|
||||||
"/src/view/equipment/list/components/detail/components/alarm/index.vue": "Index",
|
"/src/view/equipment/list/components/detail/components/alarm/index.vue": "Index",
|
||||||
"/src/view/equipment/list/components/detail/components/info/index.vue": "Index",
|
"/src/view/equipment/list/components/detail/components/info/index.vue": "Index",
|
||||||
"/src/view/equipment/list/components/detail/components/trend/index.vue": "Index",
|
"/src/view/equipment/list/components/detail/components/trend/index.vue": "Index",
|
||||||
"/src/view/equipment/list/components/detail/index.vue": "Index",
|
"/src/view/equipment/list/components/detail/index.vue": "Index",
|
||||||
"/src/view/equipment/list/components/list/index.vue": "Index",
|
"/src/view/equipment/list/components/list/index.vue": "Index",
|
||||||
"/src/view/equipment/list/index.vue": "Index",
|
"/src/view/equipment/list/index.vue": "Index",
|
||||||
"/src/view/equipment/particulars/index.vue": "Index",
|
|
||||||
"/src/view/error/index.vue": "Error",
|
"/src/view/error/index.vue": "Error",
|
||||||
"/src/view/error/reload.vue": "Reload",
|
"/src/view/error/reload.vue": "Reload",
|
||||||
"/src/view/example/breakpoint/breakpoint.vue": "BreakPoint",
|
"/src/view/example/breakpoint/breakpoint.vue": "BreakPoint",
|
||||||
@@ -56,6 +55,8 @@
|
|||||||
"/src/view/login/index.vue": "Login",
|
"/src/view/login/index.vue": "Login",
|
||||||
"/src/view/person/person.vue": "Person",
|
"/src/view/person/person.vue": "Person",
|
||||||
"/src/view/routerHolder.vue": "RouterHolder",
|
"/src/view/routerHolder.vue": "RouterHolder",
|
||||||
|
"/src/view/securityControl/alarmList/index.vue": "Index",
|
||||||
|
"/src/view/securityControl/index.vue": "SecurityControl",
|
||||||
"/src/view/superAdmin/api/api.vue": "Api",
|
"/src/view/superAdmin/api/api.vue": "Api",
|
||||||
"/src/view/superAdmin/authority/authority.vue": "Authority",
|
"/src/view/superAdmin/authority/authority.vue": "Authority",
|
||||||
"/src/view/superAdmin/authority/components/apis.vue": "Apis",
|
"/src/view/superAdmin/authority/components/apis.vue": "Apis",
|
||||||
|
|||||||
@@ -1,114 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div>
|
|
||||||
<!-- <warning-bar title="注:右上角头像下拉可切换角色" /> -->
|
|
||||||
<div class="gva-search-box">
|
|
||||||
<el-form ref="searchForm" :inline="true" :model="searchInfo">
|
|
||||||
<el-form-item label="设备ID">
|
|
||||||
<el-input v-model="searchInfo.deviceId" placeholder="设备ID" />
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item>
|
|
||||||
<el-button type="primary" icon="search" @click="onSubmit"> 查询 </el-button>
|
|
||||||
<el-button icon="refresh" @click="onReset"> 重置 </el-button>
|
|
||||||
</el-form-item>
|
|
||||||
</el-form>
|
|
||||||
</div>
|
|
||||||
<div class="gva-table-box">
|
|
||||||
<div class="gva-btn-list">
|
|
||||||
<!-- <el-button type="primary" icon="plus" @click="addUser">新增用户</el-button> -->
|
|
||||||
</div>
|
|
||||||
<el-table :data="tableData">
|
|
||||||
<el-table-column align="left" label="网关ID" prop="gatewayId" width="200" />
|
|
||||||
<el-table-column align="left" label="设备号" prop="imei" width="200" />
|
|
||||||
<el-table-column align="left" label="网关mac" prop="gatewayMac" width="200" />
|
|
||||||
<el-table-column align="left" label="网关地址" prop="gatewayAddress" />
|
|
||||||
<el-table-column align="left" label="经纬度" width="250">
|
|
||||||
<template #default="scope">
|
|
||||||
<span>{{ scope.row.gatewayLong }}</span>
|
|
||||||
<el-divider direction="vertical" />
|
|
||||||
<span>{{ scope.row.gatewayLat }}</span>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
</el-table>
|
|
||||||
<div class="gva-pagination">
|
|
||||||
<el-pagination
|
|
||||||
:current-page="page"
|
|
||||||
:page-size="pageSize"
|
|
||||||
:page-sizes="[10, 30, 50, 100]"
|
|
||||||
:total="total"
|
|
||||||
layout="total, sizes, prev, pager, next, jumper"
|
|
||||||
@current-change="handleCurrentChange"
|
|
||||||
@size-change="handleSizeChange"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
import * as serve from '@/api/equipment/alarmRecord'
|
|
||||||
|
|
||||||
import { nextTick, ref, watch, onMounted } from 'vue'
|
|
||||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
|
||||||
// import { id } from 'element-plus/es/locale'
|
|
||||||
import { useAppStore } from '@/pinia'
|
|
||||||
|
|
||||||
const appStore = useAppStore()
|
|
||||||
|
|
||||||
const searchInfo = ref({
|
|
||||||
username: '',
|
|
||||||
nickname: '',
|
|
||||||
phone: '',
|
|
||||||
email: ''
|
|
||||||
})
|
|
||||||
|
|
||||||
const page = ref(1)
|
|
||||||
const total = ref(0)
|
|
||||||
const pageSize = ref(10)
|
|
||||||
const tableData = ref([])
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
getTableData()
|
|
||||||
})
|
|
||||||
|
|
||||||
// 分页
|
|
||||||
const handleSizeChange = (val) => {
|
|
||||||
pageSize.value = val
|
|
||||||
getTableData()
|
|
||||||
}
|
|
||||||
const handleCurrentChange = (val) => {
|
|
||||||
page.value = val
|
|
||||||
getTableData()
|
|
||||||
}
|
|
||||||
// 查询
|
|
||||||
const getTableData = async () => {
|
|
||||||
const table = await serve.getAlarmRecordListByPage({
|
|
||||||
page: page.value,
|
|
||||||
pageSize: pageSize.value,
|
|
||||||
...searchInfo.value
|
|
||||||
})
|
|
||||||
if (table.code === 0) {
|
|
||||||
tableData.value = table.data.list
|
|
||||||
total.value = table.data.total
|
|
||||||
page.value = table.data.page
|
|
||||||
pageSize.value = table.data.pageSize
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const onSubmit = () => {
|
|
||||||
page.value = 1
|
|
||||||
getTableData()
|
|
||||||
}
|
|
||||||
|
|
||||||
const onReset = () => {
|
|
||||||
searchInfo.value = {
|
|
||||||
deviceId: ''
|
|
||||||
}
|
|
||||||
getTableData()
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss">
|
|
||||||
.header-img-box {
|
|
||||||
@apply w-52 h-52 border border-solid border-gray-300 rounded-xl flex justify-center items-center cursor-pointer;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
20
src/view/equipment/index.vue
Normal file
20
src/view/equipment/index.vue
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<router-view v-slot="{ Component }">
|
||||||
|
<transition mode="out-in" name="el-fade-in-linear">
|
||||||
|
<keep-alive :include="routerStore.keepAliveRouters">
|
||||||
|
<component :is="Component" />
|
||||||
|
</keep-alive>
|
||||||
|
</transition>
|
||||||
|
</router-view>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { useRouterStore } from '@/pinia/modules/router'
|
||||||
|
const routerStore = useRouterStore()
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'Equipment'
|
||||||
|
})
|
||||||
|
</script>
|
||||||
@@ -1,156 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div>
|
|
||||||
<!-- <warning-bar title="注:右上角头像下拉可切换角色" /> -->
|
|
||||||
<div class="gva-search-box">
|
|
||||||
<el-form ref="searchForm" :inline="true" :model="searchInfo">
|
|
||||||
<el-form-item label="设备状态">
|
|
||||||
<!-- <el-select v-model="searchInfo.deviceStatus" placeholder="请选择">
|
|
||||||
<el-option label="合闸" :value="1" />
|
|
||||||
<el-option label="分闸" :value="0" />
|
|
||||||
</el-select> -->
|
|
||||||
</el-form-item>
|
|
||||||
|
|
||||||
<el-form-item>
|
|
||||||
<el-button type="primary" icon="search" @click="onSubmit"> 查询 </el-button>
|
|
||||||
<el-button icon="refresh" @click="onReset"> 重置 </el-button>
|
|
||||||
</el-form-item>
|
|
||||||
</el-form>
|
|
||||||
</div>
|
|
||||||
<div class="gva-table-box">
|
|
||||||
<div class="gva-btn-list">
|
|
||||||
<!-- <el-button type="primary" icon="plus" @click="addUser">新增用户</el-button> -->
|
|
||||||
</div>
|
|
||||||
<el-table :data="tableData" row-key="ID">
|
|
||||||
<el-table-column align="left" label="设备ID" prop="deviceId"> </el-table-column>
|
|
||||||
<el-table-column align="left" label="运行时长" prop="runtime">
|
|
||||||
<template #default="{ row }">
|
|
||||||
<span>{{ row.runtime || '' }}</span>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column align="left" label="信号质量" prop="signalQuality">
|
|
||||||
<template #default="{ row }">
|
|
||||||
{{ row.signalQuality || '' }}
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column align="left" label="电压(V)" prop="leakageCurrent">
|
|
||||||
<template #default="{ row }">
|
|
||||||
{{ row.leakageCurrent || '' }}
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column align="left" label="漏电流值(mA)" prop="leakageCurrent">
|
|
||||||
<template #default="{ row }">
|
|
||||||
{{ row.leakageCurrent || '' }}
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column align="left" label="累计用电量" prop="cumulativeElectricity">
|
|
||||||
<template #default="{ row }">
|
|
||||||
{{ row.cumulativeElectricity || '' }}
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column align="left" label="电流值(A)" prop="current">
|
|
||||||
<template #default="{ row }">
|
|
||||||
{{ row.current || '' }}
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column align="left" label="内部温度(℃)" prop="internalTemperature">
|
|
||||||
<template #default="{ row }">
|
|
||||||
{{ row.internalTemperature || '' }}
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column align="left" label="N相下端温度(℃)" prop="nLowerTemperature">
|
|
||||||
<template #default="{ row }">
|
|
||||||
{{ row.nLowerTemperature || '' }}
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column align="left" label="功率因数(%)" prop="powerFactor">
|
|
||||||
<template #default="{ row }">
|
|
||||||
{{ row.powerFactor || '' }}
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
|
|
||||||
<el-table-column label="操作" fixed="right">
|
|
||||||
<template #default="{ row }">
|
|
||||||
<!-- <el-button type="primary" link icon="delete" @click="deleteUserFunc(scope.row)">删除</el-button> -->
|
|
||||||
<!-- <el-button type="primary" link icon="Tickets" @click="openDetails(row)">查看</el-button> -->
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
</el-table>
|
|
||||||
<div class="gva-pagination">
|
|
||||||
<el-pagination
|
|
||||||
:current-page="page"
|
|
||||||
:page-size="pageSize"
|
|
||||||
:page-sizes="[10, 30, 50, 100]"
|
|
||||||
:total="total"
|
|
||||||
layout="total, sizes, prev, pager, next, jumper"
|
|
||||||
@current-change="handleCurrentChange"
|
|
||||||
@size-change="handleSizeChange"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<script setup>
|
|
||||||
import * as serve from '@/api/equipment/particulars'
|
|
||||||
|
|
||||||
import { nextTick, ref, watch, onMounted } from 'vue'
|
|
||||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
|
||||||
// import { id } from 'element-plus/es/locale'
|
|
||||||
import { useAppStore } from '@/pinia'
|
|
||||||
|
|
||||||
const appStore = useAppStore()
|
|
||||||
|
|
||||||
const searchInfo = ref({
|
|
||||||
deviceStatus: ''
|
|
||||||
})
|
|
||||||
|
|
||||||
const page = ref(1)
|
|
||||||
const total = ref(0)
|
|
||||||
const pageSize = ref(10)
|
|
||||||
const tableData = ref([])
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
getTableData()
|
|
||||||
})
|
|
||||||
|
|
||||||
// 分页
|
|
||||||
const handleSizeChange = (val) => {
|
|
||||||
pageSize.value = val
|
|
||||||
getTableData()
|
|
||||||
}
|
|
||||||
const handleCurrentChange = (val) => {
|
|
||||||
page.value = val
|
|
||||||
getTableData()
|
|
||||||
}
|
|
||||||
// 查询
|
|
||||||
const getTableData = async () => {
|
|
||||||
const table = await serve.getDeviceDetailsListByPage({
|
|
||||||
page: page.value,
|
|
||||||
pageSize: pageSize.value,
|
|
||||||
...searchInfo.value
|
|
||||||
})
|
|
||||||
if (table.code === 0) {
|
|
||||||
tableData.value = table.data.list
|
|
||||||
total.value = table.data.total
|
|
||||||
page.value = table.data.page
|
|
||||||
pageSize.value = table.data.pageSize
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const onSubmit = () => {
|
|
||||||
page.value = 1
|
|
||||||
getTableData()
|
|
||||||
}
|
|
||||||
|
|
||||||
const onReset = () => {
|
|
||||||
searchInfo.value = {
|
|
||||||
deviceStatus: ''
|
|
||||||
}
|
|
||||||
getTableData()
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss">
|
|
||||||
.header-img-box {
|
|
||||||
@apply w-52 h-52 border border-solid border-gray-300 rounded-xl flex justify-center items-center cursor-pointer;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
370
src/view/securityControl/alarmList/index.vue
Normal file
370
src/view/securityControl/alarmList/index.vue
Normal file
@@ -0,0 +1,370 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<!-- <warning-bar title="注:右上角头像下拉可切换角色" /> -->
|
||||||
|
<div class="p-4 bg-white text-slate-700 dark:text-slate-400 dark:bg-slate-900 rounded my-2">
|
||||||
|
<!-- <el-tabs v-model="activeName" class="demo-tabs" @tab-click="handleClick">
|
||||||
|
<el-tab-pane label="报警事件" name="first"></el-tab-pane>
|
||||||
|
<el-tab-pane label="预警事件" name="second"></el-tab-pane>
|
||||||
|
<el-tab-pane label="二级报警事件" name="third"></el-tab-pane>
|
||||||
|
</el-tabs> -->
|
||||||
|
<el-radio-group v-model="radio" size="large" fill="#409eff">
|
||||||
|
<el-radio-button label="报警事件" value="New York" />
|
||||||
|
<el-radio-button label="预警事件" value="Washington" />
|
||||||
|
<el-radio-button label="二级报警事件" value="Los Angeles" />
|
||||||
|
</el-radio-group>
|
||||||
|
</div>
|
||||||
|
<div class="gva-search-box">
|
||||||
|
<el-form ref="searchForm" :inline="true" :model="searchInfo">
|
||||||
|
<el-form-item label="设备ID">
|
||||||
|
<el-input v-model="searchInfo.deviceId" placeholder="设备ID" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" icon="search" @click="onSubmit"> 查询 </el-button>
|
||||||
|
<el-button icon="refresh" @click="onReset"> 重置 </el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</div>
|
||||||
|
<div class="gva-table-box">
|
||||||
|
<div class="gva-btn-list">
|
||||||
|
<!-- <el-button type="primary" icon="plus" @click="addUser">新增用户</el-button> -->
|
||||||
|
</div>
|
||||||
|
<div class="echarts-box">
|
||||||
|
<div class="item pie">
|
||||||
|
<p class="title">报警类型</p>
|
||||||
|
<v-chart :option="alarmTypeOption" autoresize class="alarm-type-chart" />
|
||||||
|
</div>
|
||||||
|
<div class="item broken-line">
|
||||||
|
<p class="title">报警数量</p>
|
||||||
|
<v-chart :option="alarmCountOption" autoresize class="alarm-count-chart" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<el-table :data="tableData">
|
||||||
|
<el-table-column align="left" label="网关ID" prop="gatewayId" width="200" />
|
||||||
|
</el-table>
|
||||||
|
<div class="gva-pagination">
|
||||||
|
<el-pagination
|
||||||
|
:current-page="page"
|
||||||
|
:page-size="pageSize"
|
||||||
|
:page-sizes="[10, 30, 50, 100]"
|
||||||
|
:total="total"
|
||||||
|
layout="total, sizes, prev, pager, next, jumper"
|
||||||
|
@current-change="handleCurrentChange"
|
||||||
|
@size-change="handleSizeChange"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { use } from 'echarts/core'
|
||||||
|
import { CanvasRenderer } from 'echarts/renderers'
|
||||||
|
import { PieChart, LineChart } from 'echarts/charts'
|
||||||
|
import { TitleComponent, TooltipComponent, LegendComponent, GridComponent } from 'echarts/components'
|
||||||
|
import VChart from 'vue-echarts'
|
||||||
|
import * as serve from '@/api/equipment/alarmRecord'
|
||||||
|
|
||||||
|
import { ref, watch, onMounted } from 'vue'
|
||||||
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||||
|
// import { id } from 'element-plus/es/locale'
|
||||||
|
import { useAppStore } from '@/pinia'
|
||||||
|
|
||||||
|
// 按需注册 ECharts 模块
|
||||||
|
use([CanvasRenderer, PieChart, LineChart, TitleComponent, TooltipComponent, LegendComponent, GridComponent])
|
||||||
|
|
||||||
|
const appStore = useAppStore()
|
||||||
|
|
||||||
|
const searchInfo = ref({
|
||||||
|
username: '',
|
||||||
|
nickname: '',
|
||||||
|
phone: '',
|
||||||
|
email: ''
|
||||||
|
})
|
||||||
|
const radio = ref('New York')
|
||||||
|
|
||||||
|
const page = ref(1)
|
||||||
|
const total = ref(0)
|
||||||
|
const pageSize = ref(10)
|
||||||
|
const tableData = ref([])
|
||||||
|
|
||||||
|
// 报警类型映射(与 alarm/index.vue 中的 typeMap 保持一致)
|
||||||
|
const alarmTypeMap = {
|
||||||
|
45: { label: '设备事件', color: '#409EFF' },
|
||||||
|
'4f': { label: '操作事件', color: '#67C23A' }
|
||||||
|
}
|
||||||
|
|
||||||
|
// 报警类型环形图配置(响应式 option 交给 v-chart 渲染)
|
||||||
|
const alarmTypeOption = ref(buildAlarmTypeOption([]))
|
||||||
|
|
||||||
|
// 报警数量折线图配置
|
||||||
|
const alarmCountOption = ref(buildAlarmCountOption([]))
|
||||||
|
|
||||||
|
// 构建报警类型环形图 option
|
||||||
|
function buildAlarmTypeOption(list) {
|
||||||
|
// 按 warnType 分组统计
|
||||||
|
const countMap = {}
|
||||||
|
list.forEach((item) => {
|
||||||
|
const key = String(item.warnType ?? 'unknown')
|
||||||
|
countMap[key] = (countMap[key] || 0) + 1
|
||||||
|
})
|
||||||
|
// 转成 ECharts 数据
|
||||||
|
const chartData = Object.keys(countMap).map((key) => {
|
||||||
|
// key 已经是 String,alarmTypeMap 的键也是字符串
|
||||||
|
const meta = alarmTypeMap[key] || { label: key || '未知', color: '#909399' }
|
||||||
|
return {
|
||||||
|
name: meta.label,
|
||||||
|
value: countMap[key],
|
||||||
|
itemStyle: { color: meta.color }
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const totalCount = chartData.reduce((sum, d) => sum + d.value, 0)
|
||||||
|
|
||||||
|
return {
|
||||||
|
tooltip: {
|
||||||
|
trigger: 'item',
|
||||||
|
formatter: (params) => `${params.name}<br/>数量:${params.value}(${params.percent}%)`
|
||||||
|
},
|
||||||
|
// 图例放右侧
|
||||||
|
legend: {
|
||||||
|
orient: 'vertical',
|
||||||
|
right: 8,
|
||||||
|
top: 'middle',
|
||||||
|
textStyle: { color: '#606266', fontSize: 12 },
|
||||||
|
itemWidth: 10,
|
||||||
|
itemHeight: 10
|
||||||
|
},
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
name: '报警类型',
|
||||||
|
type: 'pie',
|
||||||
|
radius: ['52%', '78%'],
|
||||||
|
center: ['38%', '52%'],
|
||||||
|
avoidLabelOverlap: false,
|
||||||
|
label: { show: false },
|
||||||
|
labelLine: { show: false },
|
||||||
|
itemStyle: {
|
||||||
|
borderRadius: 6,
|
||||||
|
borderColor: '#fff',
|
||||||
|
borderWidth: 2
|
||||||
|
},
|
||||||
|
// 中心文字
|
||||||
|
graphic: [
|
||||||
|
{
|
||||||
|
type: 'text',
|
||||||
|
left: '38%',
|
||||||
|
top: '46%',
|
||||||
|
style: {
|
||||||
|
text: totalCount,
|
||||||
|
textAlign: 'center',
|
||||||
|
fill: '#303133',
|
||||||
|
fontSize: 22,
|
||||||
|
fontWeight: 'bold'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'text',
|
||||||
|
left: '38%',
|
||||||
|
top: '58%',
|
||||||
|
style: {
|
||||||
|
text: '总数',
|
||||||
|
textAlign: 'center',
|
||||||
|
fill: '#909399',
|
||||||
|
fontSize: 12
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
data: chartData
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 拉取全部报警数据用于统计(不分页拿全量),同时刷新两个图表
|
||||||
|
const loadChartData = async () => {
|
||||||
|
const res = await serve.getAlarmRecordListByPage({
|
||||||
|
page: 1,
|
||||||
|
pageSize: 9999
|
||||||
|
})
|
||||||
|
if (res.code === 0) {
|
||||||
|
const list = res.data.list || []
|
||||||
|
alarmTypeOption.value = buildAlarmTypeOption(list)
|
||||||
|
alarmCountOption.value = buildAlarmCountOption(list)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 构建报警数量折线图 option(按日期统计最近 30 天)
|
||||||
|
function buildAlarmCountOption(list) {
|
||||||
|
// 按日期分组(YYYY-MM-DD)
|
||||||
|
const countMap = {}
|
||||||
|
list.forEach((item) => {
|
||||||
|
const date = formatDate(item.CreatedAt || item.createdAt || item.time)
|
||||||
|
if (!date) return
|
||||||
|
countMap[date] = (countMap[date] || 0) + 1
|
||||||
|
})
|
||||||
|
|
||||||
|
// 生成最近 30 天的 X 轴(缺失日期补 0)
|
||||||
|
const xAxisData = []
|
||||||
|
const seriesData = []
|
||||||
|
const today = new Date()
|
||||||
|
for (let i = 29; i >= 0; i--) {
|
||||||
|
const d = new Date(today)
|
||||||
|
d.setDate(today.getDate() - i)
|
||||||
|
const key = formatDateKey(d)
|
||||||
|
xAxisData.push(formatDateLabel(d))
|
||||||
|
seriesData.push(countMap[key] || 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
tooltip: {
|
||||||
|
trigger: 'axis',
|
||||||
|
backgroundColor: 'rgba(15, 23, 42, 0.9)',
|
||||||
|
borderColor: 'transparent',
|
||||||
|
textStyle: { color: '#fff' }
|
||||||
|
},
|
||||||
|
grid: {
|
||||||
|
left: 36,
|
||||||
|
right: 16,
|
||||||
|
top: 16,
|
||||||
|
bottom: 28
|
||||||
|
},
|
||||||
|
xAxis: {
|
||||||
|
type: 'category',
|
||||||
|
data: xAxisData,
|
||||||
|
boundaryGap: false,
|
||||||
|
axisLine: { lineStyle: { color: '#e4e7ed' } },
|
||||||
|
axisLabel: { color: '#909399', fontSize: 11 }
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
type: 'value',
|
||||||
|
minInterval: 1,
|
||||||
|
axisLine: { show: false },
|
||||||
|
axisTick: { show: false },
|
||||||
|
splitLine: { lineStyle: { color: '#f0f2f5' } },
|
||||||
|
axisLabel: { color: '#909399', fontSize: 11 }
|
||||||
|
},
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
name: '报警数量',
|
||||||
|
type: 'line',
|
||||||
|
data: seriesData,
|
||||||
|
symbol: 'none', // 不画数据点,全一条线
|
||||||
|
smooth: true,
|
||||||
|
lineStyle: {
|
||||||
|
color: '#409EFF',
|
||||||
|
width: 2
|
||||||
|
},
|
||||||
|
// 折线下面积渐变
|
||||||
|
areaStyle: {
|
||||||
|
color: {
|
||||||
|
type: 'linear',
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
x2: 0,
|
||||||
|
y2: 1,
|
||||||
|
colorStops: [
|
||||||
|
{ offset: 0, color: 'rgba(64, 158, 255, 0.35)' },
|
||||||
|
{ offset: 1, color: 'rgba(64, 158, 255, 0.02)' }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 把后端时间字段格式化成 YYYY-MM-DD
|
||||||
|
function formatDate(value) {
|
||||||
|
if (!value) return ''
|
||||||
|
const d = new Date(value)
|
||||||
|
if (isNaN(d.getTime())) return ''
|
||||||
|
return formatDateKey(d)
|
||||||
|
}
|
||||||
|
function formatDateKey(d) {
|
||||||
|
const y = d.getFullYear()
|
||||||
|
const m = String(d.getMonth() + 1).padStart(2, '0')
|
||||||
|
const day = String(d.getDate()).padStart(2, '0')
|
||||||
|
return `${y}-${m}-${day}`
|
||||||
|
}
|
||||||
|
// X 轴显示用 MM-DD
|
||||||
|
function formatDateLabel(d) {
|
||||||
|
const m = String(d.getMonth() + 1).padStart(2, '0')
|
||||||
|
const day = String(d.getDate()).padStart(2, '0')
|
||||||
|
return `${m}-${day}`
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
getTableData()
|
||||||
|
loadChartData()
|
||||||
|
})
|
||||||
|
|
||||||
|
// 分页
|
||||||
|
const handleSizeChange = (val) => {
|
||||||
|
pageSize.value = val
|
||||||
|
getTableData()
|
||||||
|
}
|
||||||
|
const handleCurrentChange = (val) => {
|
||||||
|
page.value = val
|
||||||
|
getTableData()
|
||||||
|
}
|
||||||
|
// 查询
|
||||||
|
const getTableData = async () => {
|
||||||
|
const table = await serve.getAlarmRecordListByPage({
|
||||||
|
page: page.value,
|
||||||
|
pageSize: pageSize.value,
|
||||||
|
...searchInfo.value
|
||||||
|
})
|
||||||
|
if (table.code === 0) {
|
||||||
|
tableData.value = table.data.list
|
||||||
|
total.value = table.data.total
|
||||||
|
page.value = table.data.page
|
||||||
|
pageSize.value = table.data.pageSize
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const onSubmit = () => {
|
||||||
|
page.value = 1
|
||||||
|
getTableData()
|
||||||
|
}
|
||||||
|
|
||||||
|
const onReset = () => {
|
||||||
|
searchInfo.value = {
|
||||||
|
deviceId: ''
|
||||||
|
}
|
||||||
|
getTableData()
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.header-img-box {
|
||||||
|
@apply w-52 h-52 border border-solid border-gray-300 rounded-xl flex justify-center items-center cursor-pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gva-table-box {
|
||||||
|
padding-top: 0 !important;
|
||||||
|
}
|
||||||
|
.echarts-box {
|
||||||
|
display: flex;
|
||||||
|
> .item {
|
||||||
|
height: 251px;
|
||||||
|
|
||||||
|
&.pie {
|
||||||
|
width: 370px;
|
||||||
|
}
|
||||||
|
&.broken-line {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
.title {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.alarm-type-chart {
|
||||||
|
width: 100%;
|
||||||
|
height: calc(100% - 30px);
|
||||||
|
}
|
||||||
|
.alarm-count-chart {
|
||||||
|
width: 100%;
|
||||||
|
height: calc(100% - 30px);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
20
src/view/securityControl/index.vue
Normal file
20
src/view/securityControl/index.vue
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<router-view v-slot="{ Component }">
|
||||||
|
<transition mode="out-in" name="el-fade-in-linear">
|
||||||
|
<keep-alive :include="routerStore.keepAliveRouters">
|
||||||
|
<component :is="Component" />
|
||||||
|
</keep-alive>
|
||||||
|
</transition>
|
||||||
|
</router-view>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { useRouterStore } from '@/pinia/modules/router'
|
||||||
|
const routerStore = useRouterStore()
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'SecurityControl'
|
||||||
|
})
|
||||||
|
</script>
|
||||||
Reference in New Issue
Block a user