723第一次提交
This commit is contained in:
commit
283eedbcaa
|
@ -0,0 +1,14 @@
|
||||||
|
/* eslint-env node */
|
||||||
|
require('@rushstack/eslint-patch/modern-module-resolution')
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
root: true,
|
||||||
|
'extends': [
|
||||||
|
'plugin:vue/vue3-essential',
|
||||||
|
'eslint:recommended',
|
||||||
|
'@vue/eslint-config-typescript'
|
||||||
|
],
|
||||||
|
parserOptions: {
|
||||||
|
ecmaVersion: 'latest'
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
pnpm-debug.log*
|
||||||
|
lerna-debug.log*
|
||||||
|
|
||||||
|
node_modules
|
||||||
|
.DS_Store
|
||||||
|
dist
|
||||||
|
dist-ssr
|
||||||
|
coverage
|
||||||
|
*.local
|
||||||
|
|
||||||
|
/cypress/videos/
|
||||||
|
/cypress/screenshots/
|
||||||
|
|
||||||
|
# Editor directories and files
|
||||||
|
.vscode/*
|
||||||
|
!.vscode/extensions.json
|
||||||
|
.idea
|
||||||
|
*.suo
|
||||||
|
*.ntvs*
|
||||||
|
*.njsproj
|
||||||
|
*.sln
|
||||||
|
*.sw?
|
||||||
|
|
||||||
|
*.tsbuildinfo
|
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"recommendations": [
|
||||||
|
"Vue.volar",
|
||||||
|
"dbaeumer.vscode-eslint"
|
||||||
|
]
|
||||||
|
}
|
|
@ -0,0 +1,39 @@
|
||||||
|
# vue_one
|
||||||
|
|
||||||
|
This template should help get you started developing with Vue 3 in Vite.
|
||||||
|
|
||||||
|
## Recommended IDE Setup
|
||||||
|
|
||||||
|
[VSCode](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur).
|
||||||
|
|
||||||
|
## Type Support for `.vue` Imports in TS
|
||||||
|
|
||||||
|
TypeScript cannot handle type information for `.vue` imports by default, so we replace the `tsc` CLI with `vue-tsc` for type checking. In editors, we need [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) to make the TypeScript language service aware of `.vue` types.
|
||||||
|
|
||||||
|
## Customize configuration
|
||||||
|
|
||||||
|
See [Vite Configuration Reference](https://vitejs.dev/config/).
|
||||||
|
|
||||||
|
## Project Setup
|
||||||
|
|
||||||
|
```sh
|
||||||
|
npm install
|
||||||
|
```
|
||||||
|
|
||||||
|
### Compile and Hot-Reload for Development
|
||||||
|
|
||||||
|
```sh
|
||||||
|
npm run dev
|
||||||
|
```
|
||||||
|
|
||||||
|
### Type-Check, Compile and Minify for Production
|
||||||
|
|
||||||
|
```sh
|
||||||
|
npm run build
|
||||||
|
```
|
||||||
|
|
||||||
|
### Lint with [ESLint](https://eslint.org/)
|
||||||
|
|
||||||
|
```sh
|
||||||
|
npm run lint
|
||||||
|
```
|
|
@ -0,0 +1,8 @@
|
||||||
|
// vue3 报错提示 找不到模块“./XXX.vue”或其相应的类型声明
|
||||||
|
// 报错原因:typescript 只能理解 .ts 文件,无法理解 .vue文件
|
||||||
|
declare module '*.vue' {
|
||||||
|
import type { DefineComponent } from 'vue'
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types
|
||||||
|
const component: DefineComponent<{}, {}, any>
|
||||||
|
export default component
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<link rel="icon" href="/favicon.ico">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Vite App</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="app"></div>
|
||||||
|
<script type="module" src="/src/main.ts"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,37 @@
|
||||||
|
{
|
||||||
|
"name": "vue_one",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"private": true,
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "vite",
|
||||||
|
"build": "run-p type-check \"build-only {@}\" --",
|
||||||
|
"preview": "vite preview",
|
||||||
|
"build-only": "vite build",
|
||||||
|
"type-check": "vue-tsc --build --force",
|
||||||
|
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"axios": "^1.7.2",
|
||||||
|
"echarts": "^5.5.1",
|
||||||
|
"element-plus": "^2.7.6",
|
||||||
|
"http-proxy-middleware": "^3.0.0",
|
||||||
|
"vue": "^3.4.29",
|
||||||
|
"vue-router": "^4.4.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@rushstack/eslint-patch": "^1.8.0",
|
||||||
|
"@tsconfig/node20": "^20.1.4",
|
||||||
|
"@types/node": "^20.14.10",
|
||||||
|
"@vitejs/plugin-vue": "^5.0.5",
|
||||||
|
"@vue/eslint-config-typescript": "^13.0.0",
|
||||||
|
"@vue/tsconfig": "^0.5.1",
|
||||||
|
"eslint": "^8.57.0",
|
||||||
|
"eslint-plugin-vue": "^9.23.0",
|
||||||
|
"npm-run-all2": "^6.2.0",
|
||||||
|
"typescript": "~5.4.0",
|
||||||
|
"vite": "^5.3.1",
|
||||||
|
"vite-plugin-vue-setup-extend": "^0.4.0",
|
||||||
|
"vue-tsc": "^2.0.21"
|
||||||
|
}
|
||||||
|
}
|
Binary file not shown.
After Width: | Height: | Size: 4.2 KiB |
|
@ -0,0 +1,105 @@
|
||||||
|
<template>
|
||||||
|
<div id="app">
|
||||||
|
<el-header>
|
||||||
|
<div class="logo">
|
||||||
|
<span>管理平台</span>
|
||||||
|
</div>
|
||||||
|
<div class="nav-menu">
|
||||||
|
<el-menu
|
||||||
|
:default-active="activeIndex"
|
||||||
|
class="el-menu-demo"
|
||||||
|
mode="horizontal"
|
||||||
|
router
|
||||||
|
>
|
||||||
|
<el-menu-item index="/">首页</el-menu-item>
|
||||||
|
<el-menu-item index="/alert-management">告警管理</el-menu-item>
|
||||||
|
<el-menu-item index="/real-time-monitoring">实时监测</el-menu-item>
|
||||||
|
<el-menu-item index="/statistics-analysis">统计分析</el-menu-item>
|
||||||
|
<!-- <el-menu-item index="/system-settings">系统设置</el-menu-item> -->
|
||||||
|
<el-menu-item index="/test">系统设置</el-menu-item>
|
||||||
|
</el-menu>
|
||||||
|
</div>
|
||||||
|
<div class="user-info">
|
||||||
|
<span>线上轮巡</span>
|
||||||
|
<span>|</span>
|
||||||
|
<span>English</span>
|
||||||
|
<span>|</span>
|
||||||
|
<span>多云 35°C</span>
|
||||||
|
<span>|</span>
|
||||||
|
<el-dropdown>
|
||||||
|
<span class="el-dropdown-link">
|
||||||
|
test@qqqq.cn<i class="el-icon-arrow-down el-icon--right"></i>
|
||||||
|
</span>
|
||||||
|
<el-dropdown-menu>
|
||||||
|
<el-dropdown-item>Profile</el-dropdown-item>
|
||||||
|
<el-dropdown-item>Logout</el-dropdown-item>
|
||||||
|
</el-dropdown-menu>
|
||||||
|
</el-dropdown>
|
||||||
|
</div>
|
||||||
|
</el-header>
|
||||||
|
<router-view />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
activeIndex: '/'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
$route(to) {
|
||||||
|
this.activeIndex = to.path
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.el-header {
|
||||||
|
background-color: #1f2d3d;
|
||||||
|
color: #fff;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0 20px;
|
||||||
|
}
|
||||||
|
.logo {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-right: 10px; /* 添加适当的右边距 */
|
||||||
|
}
|
||||||
|
.logo img {
|
||||||
|
height: 40px;
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
.nav-menu {
|
||||||
|
flex-grow: 1;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
margin-left: 110px; /* 添加适当的左边距 */
|
||||||
|
margin-right: 100px; /* 添加适当的右边距 */
|
||||||
|
}
|
||||||
|
.el-menu-demo {
|
||||||
|
background-color: transparent;
|
||||||
|
color: #fff;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between; /* 菜单项之间均匀分布 */
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
.el-menu-demo .el-menu-item {
|
||||||
|
padding: 0 20px; /* 减少 padding 以增加菜单项之间的间隔 */
|
||||||
|
text-align: center;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
.user-info {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
margin-left: 10px; /* 添加适当的左边距 */
|
||||||
|
}
|
||||||
|
.user-info span {
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,20 @@
|
||||||
|
<template>
|
||||||
|
<div class="about">
|
||||||
|
<h1>关于</h1>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<scrip setup lang="ts" name="about">
|
||||||
|
|
||||||
|
</scrip>
|
||||||
|
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.about{
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
|
@ -0,0 +1,95 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<el-card class="alert-card">
|
||||||
|
<div class="alert-header">告警趋势</div>
|
||||||
|
<el-tabs v-model="activeName" @tab-click="handleClick" class="alert-tabs">
|
||||||
|
<el-tab-pane label="24小时" name="first">24小时数据展示</el-tab-pane>
|
||||||
|
<el-tab-pane label="30天" name="second">30天数据展示</el-tab-pane>
|
||||||
|
<el-tab-pane label="12月" name="third">12月数据展示</el-tab-pane>
|
||||||
|
</el-tabs>
|
||||||
|
<div id="alert-chart" class="chart-container"></div>
|
||||||
|
</el-card>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import * as echarts from 'echarts';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'AlertChart',
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
activeName: 'first'
|
||||||
|
};
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.initChart();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
handleClick(tab, event) {
|
||||||
|
console.log(tab, event);
|
||||||
|
// 根据选中的标签刷新图表数据
|
||||||
|
this.initChart();
|
||||||
|
},
|
||||||
|
initChart() {
|
||||||
|
var chartDom = document.getElementById('alert-chart');
|
||||||
|
var myChart = echarts.init(chartDom);
|
||||||
|
var option;
|
||||||
|
|
||||||
|
option = {
|
||||||
|
xAxis: {
|
||||||
|
type: 'category',
|
||||||
|
boundaryGap: false,
|
||||||
|
data: ['2024-07-08 00:00', '2024-07-08 08:00', '2024-07-08 16:00', '2024-07-09 00:00', '2024-07-09 08:00', '2024-07-09 16:00']
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
type: 'value'
|
||||||
|
},
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
data: [10, 22, 28, 43, 49, 62],
|
||||||
|
type: 'line',
|
||||||
|
areaStyle: {}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
option && myChart.setOption(option);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.alert-card {
|
||||||
|
background-color: #2a3f54;
|
||||||
|
color: #fff;
|
||||||
|
padding: 20px;
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.alert-header {
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
border-bottom: 1px solid #3a4b5c;
|
||||||
|
padding-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.alert-tabs {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chart-container {
|
||||||
|
width: 100%;
|
||||||
|
height: 400px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-tabs__item {
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-tabs__active-bar {
|
||||||
|
background-color: #409EFF;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,252 @@
|
||||||
|
<template>
|
||||||
|
<div class="alert-management">
|
||||||
|
<div class="breadcrumb">
|
||||||
|
<el-breadcrumb separator=">">
|
||||||
|
<el-breadcrumb-item>告警管理</el-breadcrumb-item>
|
||||||
|
<el-breadcrumb-item>实时告警</el-breadcrumb-item>
|
||||||
|
</el-breadcrumb>
|
||||||
|
</div>
|
||||||
|
<div class="alert-summary">
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col :span="8">
|
||||||
|
<div class="alert-card alert-pending">
|
||||||
|
<div class="alert-card-header">告警数(待处理)</div>
|
||||||
|
<div class="alert-card-content">{{ alerts.pending }}</div>
|
||||||
|
</div>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="8">
|
||||||
|
<div class="alert-card alert-processing">
|
||||||
|
<div class="alert-card-header">告警数(处理中)</div>
|
||||||
|
<div class="alert-card-content">{{ alerts.processing }}</div>
|
||||||
|
</div>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="8">
|
||||||
|
<div class="alert-card alert-completed">
|
||||||
|
<div class="alert-card-header">告警数(已完成)</div>
|
||||||
|
<div class="alert-card-content">{{ alerts.completed }}</div>
|
||||||
|
</div>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</div>
|
||||||
|
<div class="region-overview">
|
||||||
|
<el-card>
|
||||||
|
<div slot="header" class="card-header">
|
||||||
|
<span>区域概况</span>
|
||||||
|
</div>
|
||||||
|
<el-table
|
||||||
|
:data="tableData.slice((currentPage - 1) * pageSize, currentPage * pageSize)"
|
||||||
|
style="width: 100%;"
|
||||||
|
row-key="id"
|
||||||
|
:tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
|
||||||
|
>
|
||||||
|
<el-table-column prop="id" label="序号" width="50"></el-table-column>
|
||||||
|
<el-table-column prop="region" label="区域名称" width="200"></el-table-column>
|
||||||
|
<el-table-column prop="siteCount" label="站点数量" width="100"></el-table-column>
|
||||||
|
<el-table-column prop="siteName" label="站点名称">
|
||||||
|
<template v-slot="scope">
|
||||||
|
<span v-if="scope.row.children"></span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="status" label="状态" width="100">
|
||||||
|
<template v-slot="scope">
|
||||||
|
<el-tag v-if="scope.row.status === '在线'" type="success">在线</el-tag>
|
||||||
|
<el-tag v-else type="info">离线</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="cameraCount" label="摄像头数量" width="120"></el-table-column>
|
||||||
|
<el-table-column prop="alertCount" label="告警数量" width="120"></el-table-column>
|
||||||
|
<el-table-column label="操作" width="100">
|
||||||
|
<template v-slot="scope">
|
||||||
|
<el-button v-if="!scope.row.children" type="text" size="small">查看告警</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
<div class="pagination">
|
||||||
|
<el-pagination
|
||||||
|
@size-change="handleSizeChange"
|
||||||
|
@current-change="handleCurrentChange"
|
||||||
|
:current-page="currentPage"
|
||||||
|
:page-sizes="[5, 10, 20, 30]"
|
||||||
|
:page-size="pageSize"
|
||||||
|
layout="total, sizes, prev, pager, next, jumper"
|
||||||
|
:total="total"
|
||||||
|
></el-pagination>
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'AlertManagement',
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
alerts: {
|
||||||
|
pending: 1460,
|
||||||
|
processing: 1,
|
||||||
|
completed: 123830
|
||||||
|
},
|
||||||
|
tableData: [
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
region: '安徽区域',
|
||||||
|
siteCount: 2,
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
id: 11,
|
||||||
|
siteName: '天长吾悦广场',
|
||||||
|
status: '在线',
|
||||||
|
cameraCount: 20,
|
||||||
|
alertCount: 6059
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 12,
|
||||||
|
siteName: '颖上吾悦广场',
|
||||||
|
status: '在线',
|
||||||
|
cameraCount: 20,
|
||||||
|
alertCount: 617
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
region: '东北区域',
|
||||||
|
siteCount: 2,
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
id: 21,
|
||||||
|
siteName: '东北站点1',
|
||||||
|
status: '在线',
|
||||||
|
cameraCount: 15,
|
||||||
|
alertCount: 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 22,
|
||||||
|
siteName: '东北站点2',
|
||||||
|
status: '离线',
|
||||||
|
cameraCount: 18,
|
||||||
|
alertCount: 150
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 3,
|
||||||
|
region: '沪苏区域',
|
||||||
|
siteCount: 1,
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
id: 31,
|
||||||
|
siteName: '沪苏站点',
|
||||||
|
status: '在线',
|
||||||
|
cameraCount: 10,
|
||||||
|
alertCount: 300
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 4,
|
||||||
|
region: '华北区域',
|
||||||
|
siteCount: 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 5,
|
||||||
|
region: '华南区域',
|
||||||
|
siteCount: 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 6,
|
||||||
|
region: '华中区域',
|
||||||
|
siteCount: 10
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 7,
|
||||||
|
region: '闽浙区域',
|
||||||
|
siteCount: 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 8,
|
||||||
|
region: '南京区域',
|
||||||
|
siteCount: 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 9,
|
||||||
|
region: '山东区域',
|
||||||
|
siteCount: 7
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 10,
|
||||||
|
region: '苏北区域',
|
||||||
|
siteCount: 3
|
||||||
|
}
|
||||||
|
],
|
||||||
|
currentPage: 1,
|
||||||
|
pageSize: 5,
|
||||||
|
total: 10
|
||||||
|
};
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
handleSizeChange(val) {
|
||||||
|
this.pageSize = val;
|
||||||
|
},
|
||||||
|
handleCurrentChange(val) {
|
||||||
|
this.currentPage = val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.alert-management {
|
||||||
|
padding: 40px;
|
||||||
|
background-color: #f5f7fa;
|
||||||
|
}
|
||||||
|
.breadcrumb {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
.alert-summary {
|
||||||
|
background-color: #fff;
|
||||||
|
padding: 20px;
|
||||||
|
border-radius: 10px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
.alert-card {
|
||||||
|
background-color: #fff;
|
||||||
|
border-radius: 10px;
|
||||||
|
text-align: center;
|
||||||
|
padding: 20px;
|
||||||
|
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
.alert-card-header {
|
||||||
|
font-size: 16px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
.alert-card-content {
|
||||||
|
font-size: 28px;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
.alert-pending {
|
||||||
|
background: linear-gradient(135deg, #ff9a9e 0%, #fecfef 100%);
|
||||||
|
}
|
||||||
|
.alert-processing {
|
||||||
|
background: linear-gradient(135deg, #a1c4fd 0%, #c2e9fb 100%);
|
||||||
|
}
|
||||||
|
.alert-completed {
|
||||||
|
background: linear-gradient(135deg, #a18cd1 0%, #fbc2eb 100%);
|
||||||
|
}
|
||||||
|
.region-overview {
|
||||||
|
background-color: #fff;
|
||||||
|
padding: 20px;
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
||||||
|
.card-header {
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
.pagination {
|
||||||
|
margin-top: 20px;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,17 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<h2>摄像头管理</h2>
|
||||||
|
<!-- 模拟静态数据内容 -->
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: "CameraManagement",
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
/* 添加需要的样式 */
|
||||||
|
</style>
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<h2>设备管理</h2>
|
||||||
|
<!-- 模拟静态数据内容 -->
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: "DeviceManagement",
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
/* 添加需要的样式 */
|
||||||
|
</style>
|
||||||
|
|
|
@ -0,0 +1,288 @@
|
||||||
|
<template>
|
||||||
|
<div class="home">
|
||||||
|
<div class="breadcrumb">
|
||||||
|
<el-breadcrumb separator=">">
|
||||||
|
<el-breadcrumb-item>主页</el-breadcrumb-item>
|
||||||
|
</el-breadcrumb>
|
||||||
|
</div>
|
||||||
|
<el-row>
|
||||||
|
<el-col :span="16">
|
||||||
|
<div class="table-container">
|
||||||
|
<el-table :data="paginatedData">
|
||||||
|
<el-table-column prop="id" label="告警编号" width="140"></el-table-column>
|
||||||
|
<el-table-column label="告警类型" width="140">
|
||||||
|
<template v-slot="scope">
|
||||||
|
<!-- {{ typeMapping[scope.row.types] || codenameTranslate(scope.row.types) }} -->
|
||||||
|
{{ typeMapping[scope.row.types] }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="camera.name" label="告警位置" width="180"></el-table-column>
|
||||||
|
<el-table-column label="告警时间" width="180">
|
||||||
|
<template v-slot="scope">
|
||||||
|
{{ formatDateTime(scope.row.started_at) }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="status" label="告警状态" width="140">
|
||||||
|
<template v-slot="scope">
|
||||||
|
<el-tag :type="scope.row.status === '已处理' ? 'success' : 'warning'">{{ scope.row.status }}</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="操作" width="140">
|
||||||
|
<template v-slot="scope">
|
||||||
|
<el-button type="text" @click="handleView(scope.row)">查看</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
</div>
|
||||||
|
<div class="pagination-container">
|
||||||
|
<el-pagination background layout="total, sizes, prev, pager, next, jumper" :total="tableData.length"
|
||||||
|
:page-size="pageSize" :current-page.sync="currentPage" @current-change="handlePageChange"
|
||||||
|
@size-change="handleSizeChange">
|
||||||
|
</el-pagination>
|
||||||
|
</div>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
<el-dialog v-model="dialogVisible" title="告警详情" width="80%">
|
||||||
|
<div>
|
||||||
|
<el-row>
|
||||||
|
<el-col :span="6">
|
||||||
|
<p>告警编号: {{ selectedRow.id }}</p>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="6">
|
||||||
|
<!-- <p>告警类型: {{ typeMapping[selectedRow.types] || codenameTranslate(selectedRow.types) }}</p> -->
|
||||||
|
<p>告警类型: {{ typeMapping[selectedRow.types] }}</p>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="6">
|
||||||
|
<p>告警位置: {{ selectedRow.camera.name }}</p>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="6">
|
||||||
|
<p>告警时间: {{ selectedRow.formatted_started_at }}</p>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
<el-row>
|
||||||
|
<el-col :span="6">
|
||||||
|
<p>告警状态: <el-tag :type="selectedRow.status === '已处理' ? 'success' : 'warning'">{{ selectedRow.status }}</el-tag></p>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="6">
|
||||||
|
<p>摄像头编号: {{ selectedRow.camera_id }}</p>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="6">
|
||||||
|
<p>持续时间: {{ duration }}</p>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="6">
|
||||||
|
<p>备注: {{ selectedRow.note }}</p>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
<div class="event-media">
|
||||||
|
<div v-for="medium in selectedRow.mediums" :key="medium.id" class="media-container">
|
||||||
|
<div v-if="medium.name === 'video'" class="media-item video-item">
|
||||||
|
<p>告警关联视频</p>
|
||||||
|
<video :src="medium.file" controls></video>
|
||||||
|
</div>
|
||||||
|
<div v-if="medium.name === 'snapshot'" class="media-item image-item">
|
||||||
|
<p>告警关联图片</p>
|
||||||
|
<el-image :src="medium.file" fit="contain"></el-image>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<span slot="footer" class="dialog-footer">
|
||||||
|
<div class=""></div>
|
||||||
|
</span>
|
||||||
|
</el-dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import * as echarts from 'echarts';
|
||||||
|
import Statistics from '../components/Statistics.vue';
|
||||||
|
import AlertChart from '../components/AlertChart.vue';
|
||||||
|
import { login, getEvents, initCodeNameMap, codenameTranslate } from '../../superbox.js';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'Home',
|
||||||
|
components: {
|
||||||
|
Statistics,
|
||||||
|
AlertChart,
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
tableData: [],
|
||||||
|
dialogVisible: false,
|
||||||
|
selectedRow: {},
|
||||||
|
mediums: {},
|
||||||
|
duration: '',
|
||||||
|
typeMapping: {
|
||||||
|
'abnormal:525': '黑屏检测',
|
||||||
|
'car_blocking:512': '车辆拥塞',
|
||||||
|
'car_blocking:514': '违规停车',
|
||||||
|
'escalator_status:518': '扶梯运行状态',
|
||||||
|
'gathering:520': '人员密集',
|
||||||
|
'gathering:521': '保洁点名',
|
||||||
|
'intrude:513': '入侵检测',
|
||||||
|
'long_term_door_status:526': '长期门状态检测',
|
||||||
|
'lying_down:527': '人员倒地',
|
||||||
|
'minizawu:531': '杂物堆积',
|
||||||
|
'minizawu:532': '杂物堆积',
|
||||||
|
'personnel_stay:535': '人员逗留',
|
||||||
|
'vacant:524': '人员离岗',
|
||||||
|
'zawu:516': '饮料垃圾检测',
|
||||||
|
'zawu:517': '垃圾桶满溢',
|
||||||
|
'zawu:519': '违规放置',
|
||||||
|
'zawu:523': '绿化带垃圾检测',
|
||||||
|
},
|
||||||
|
statusMapping: {
|
||||||
|
'': '待处理',
|
||||||
|
'': '处理中',
|
||||||
|
'': '已处理'
|
||||||
|
},
|
||||||
|
currentPage: 1,
|
||||||
|
pageSize: 20,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async created() {
|
||||||
|
try {
|
||||||
|
const token = await login('turingvideo', '1234qwer!');
|
||||||
|
// await initCodeNameMap(token);
|
||||||
|
this.tableData = await getEvents(token);
|
||||||
|
console.log(this.tableData);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error fetching events:", error);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
paginatedData() {
|
||||||
|
const start = (this.currentPage - 1) * this.pageSize;
|
||||||
|
const end = start + this.pageSize;
|
||||||
|
return this.tableData.slice(start, end);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
handleView(row) {
|
||||||
|
this.selectedRow = row;
|
||||||
|
this.duration = this.calculateDuration(row.started_at, row.ended_at);
|
||||||
|
row.formatted_started_at = this.formatDateTime(row.started_at);
|
||||||
|
|
||||||
|
// console.log("duration: ", row.started_at);
|
||||||
|
// console.log("duration: ", row.ended_at);
|
||||||
|
// console.log("duration: ", this.duration);
|
||||||
|
this.dialogVisible = true;
|
||||||
|
this.mediums = row.mediums || {};
|
||||||
|
if (Array.isArray(row.mediums)) {
|
||||||
|
row.mediums.forEach(medium => {
|
||||||
|
// console.log("medium: ", medium.file);
|
||||||
|
// console.log("medium: ", medium.id);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// console.log("No mediums available");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handlePageChange(page) {
|
||||||
|
this.currentPage = page;
|
||||||
|
},
|
||||||
|
handleSizeChange(size) {
|
||||||
|
this.pageSize = size;
|
||||||
|
},
|
||||||
|
calculateDuration(started_at, ended_at) {
|
||||||
|
const start = new Date(started_at);
|
||||||
|
const end = new Date(ended_at);
|
||||||
|
const duration = end - start;
|
||||||
|
const minutes = Math.floor(duration / 60000);
|
||||||
|
const seconds = ((duration % 60000) / 1000).toFixed(0);
|
||||||
|
return `${minutes}分${(seconds < 10 ? '0' : '')}${seconds}秒`;
|
||||||
|
},
|
||||||
|
formatDateTime(datetime) {
|
||||||
|
const date = new Date(datetime);
|
||||||
|
const year = date.getFullYear();
|
||||||
|
const month = (date.getMonth() + 1).toString().padStart(2, '0');
|
||||||
|
const day = date.getDate().toString().padStart(2, '0');
|
||||||
|
const hours = date.getHours().toString().padStart(2, '0');
|
||||||
|
const minutes = date.getMinutes().toString().padStart(2, '0');
|
||||||
|
const seconds = date.getSeconds().toString().padStart(2, '0');
|
||||||
|
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.home {
|
||||||
|
padding: 40px;
|
||||||
|
background-color: #f5f7fa;
|
||||||
|
}
|
||||||
|
|
||||||
|
.breadcrumb {
|
||||||
|
margin-bottom: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.top-scroll {
|
||||||
|
width: 100%;
|
||||||
|
overflow-x: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.top-scroll-content {
|
||||||
|
/* 高度滚动条显示出来默认值 */
|
||||||
|
height: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-container {
|
||||||
|
width: 80%;
|
||||||
|
max-height: 1000px;
|
||||||
|
overflow-x: auto;
|
||||||
|
padding-left: 7%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 弹窗> 显示左右等分*/
|
||||||
|
.event-media {
|
||||||
|
/* display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 10px;
|
||||||
|
margin-top: 20px; */
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
/* 添加这个属性来左右等分 */
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.media-container {
|
||||||
|
flex: 0 0 48%;
|
||||||
|
/* 确保每块区域占据一行的48% */
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
/* 使内容在区域内居中 */
|
||||||
|
margin-bottom: 20px;
|
||||||
|
/* 添加底部间距,使多个元素之间有间隔 */
|
||||||
|
box-sizing: border-box;
|
||||||
|
/* 确保padding和border不会影响大小 */
|
||||||
|
padding: 10px;
|
||||||
|
/* 添加内边距,使内容不靠近边框 */
|
||||||
|
}
|
||||||
|
|
||||||
|
.video-item video,
|
||||||
|
.image-item img {
|
||||||
|
width: 100%;
|
||||||
|
/* 保证视频和图片在容器内等宽 */
|
||||||
|
height: auto;
|
||||||
|
/* 保证视频和图片比例正常 */
|
||||||
|
border-radius: 8px;
|
||||||
|
/* 圆润效果 */
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||||
|
/* 阴影效果 */
|
||||||
|
}
|
||||||
|
|
||||||
|
.video-item p,
|
||||||
|
.image-item p {
|
||||||
|
margin-bottom: 8px;
|
||||||
|
text-align: center;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-container {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
margin-top: 20px;
|
||||||
|
margin-right: 150px;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,17 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<h2>层级列表</h2>
|
||||||
|
<!-- 模拟静态数据内容 -->
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: "LevelList",
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
/* 添加需要的样式 */
|
||||||
|
</style>
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<h2>层级树</h2>
|
||||||
|
<!-- 模拟静态数据内容 -->
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: "LevelTree",
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
/* 添加需要的样式 */
|
||||||
|
</style>
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<el-card>
|
||||||
|
<h2>当前位置:中国</h2>
|
||||||
|
<!-- Add map implementation here -->
|
||||||
|
</el-card>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'LocationMap'
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
/* Add your styles here */
|
||||||
|
</style>
|
|
@ -0,0 +1,83 @@
|
||||||
|
<!-- 测试 -->
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<button @click="loginAndGetToken">Login and Get Token</button>
|
||||||
|
<button @click="withTokenGetCameras">Get Cameras with Token</button>
|
||||||
|
<pre>Token: {{ token }}</pre>
|
||||||
|
<pre>Cameras: {{ cameras }}</pre>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import axios from 'axios';
|
||||||
|
import { ref } from 'vue';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'App',
|
||||||
|
setup() {
|
||||||
|
const token = ref('');
|
||||||
|
const cameras = ref([]);
|
||||||
|
|
||||||
|
const api_base = "/api/v1";
|
||||||
|
const api_login = api_base + "/auth/login";
|
||||||
|
const api_cameras = api_base + "/camera/cameras";
|
||||||
|
const username = "turingvideo";
|
||||||
|
const password = "1234qwer!";
|
||||||
|
|
||||||
|
const loginAndGetToken = async () => {
|
||||||
|
try {
|
||||||
|
const res = await axios.post(api_login,
|
||||||
|
{
|
||||||
|
'username': username,
|
||||||
|
'password': password,
|
||||||
|
'cookieless': 'True'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
'Content-type': 'application/json'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
console.log(res.data)
|
||||||
|
token.value = res.data.ret.token;
|
||||||
|
console.log('Token:', token.value);
|
||||||
|
} catch (e) {
|
||||||
|
console.log(e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const withTokenGetCameras = async () => {
|
||||||
|
try {
|
||||||
|
if (!token.value) {
|
||||||
|
console.error('No token available. Please login first.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const res = await axios.get(api_cameras, {
|
||||||
|
headers: {
|
||||||
|
'Content-type': 'application/json',
|
||||||
|
'Authorization': `Bearer ${token.value}`,
|
||||||
|
'Cookie': `jwt=${token.value}; sessionid=27iy45x18cq2uio2k2gnsl4a8s4si935`
|
||||||
|
}
|
||||||
|
});
|
||||||
|
cameras.value = res.data;
|
||||||
|
console.log('Cameras:', cameras.value);
|
||||||
|
} catch (e) {
|
||||||
|
console.log(e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
loginAndGetToken,
|
||||||
|
withTokenGetCameras,
|
||||||
|
token,
|
||||||
|
cameras
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
/* Add your styles here */
|
||||||
|
</style>
|
|
@ -0,0 +1,313 @@
|
||||||
|
<template>
|
||||||
|
<div class="real-time-monitoring">
|
||||||
|
<div class="breadcrumb">
|
||||||
|
<el-breadcrumb separator=">">
|
||||||
|
<el-breadcrumb-item>实时监测</el-breadcrumb-item>
|
||||||
|
<el-breadcrumb-item>实时监测数据</el-breadcrumb-item>
|
||||||
|
</el-breadcrumb>
|
||||||
|
</div>
|
||||||
|
<div class="alert-summary">
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col :span="12">
|
||||||
|
<div class="chart-container">
|
||||||
|
<div class="chart-header">告警情况汇总</div>
|
||||||
|
<div ref="cameraChart" class="chart"></div>
|
||||||
|
<div class="chart-info">
|
||||||
|
<div>摄像头数量</div>
|
||||||
|
<div>
|
||||||
|
<span class="status online">• 在线</span> 1885
|
||||||
|
<span class="status offline">• 离线</span> 115
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<div class="chart-container">
|
||||||
|
<div class="chart-header">站点数量</div>
|
||||||
|
<div ref="siteChart" class="chart"></div>
|
||||||
|
<div class="chart-info">
|
||||||
|
<div>编制总数</div>
|
||||||
|
<div>
|
||||||
|
<span class="status online">• 在线</span> 47
|
||||||
|
<span class="status offline">• 离线</span> 7
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</div>
|
||||||
|
<div class="device-status">
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col :span="8">
|
||||||
|
<div class="status-card pending">
|
||||||
|
<div class="status-header">待处理告警摄像头</div>
|
||||||
|
<div class="status-content">{{ deviceStatus.pending }}</div>
|
||||||
|
</div>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="8">
|
||||||
|
<div class="status-card total">
|
||||||
|
<div class="status-header">累计产生告警摄像头</div>
|
||||||
|
<div class="status-content">{{ deviceStatus.total }}</div>
|
||||||
|
<div class="status-footer">
|
||||||
|
<div>近7天产生告警摄像头 {{ deviceStatus.last7Days }}</div>
|
||||||
|
<div>近30天产生告警摄像头 {{ deviceStatus.last30Days }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="8">
|
||||||
|
<div class="status-card inactive">
|
||||||
|
<div class="status-header">非活跃摄像头(累计90天未产生告警)</div>
|
||||||
|
<div class="status-content">{{ deviceStatus.inactive }}</div>
|
||||||
|
</div>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</div>
|
||||||
|
<div class="region-overview">
|
||||||
|
<el-card>
|
||||||
|
<div slot="header" class="card-header">
|
||||||
|
<span>区域概况</span>
|
||||||
|
</div>
|
||||||
|
<el-table
|
||||||
|
:data="pagedData"
|
||||||
|
style="width: 100%;"
|
||||||
|
row-key="id"
|
||||||
|
height="400"
|
||||||
|
>
|
||||||
|
<el-table-column prop="id" label="序号" width="50"></el-table-column>
|
||||||
|
<el-table-column prop="region" label="区域名称" width="200"></el-table-column>
|
||||||
|
<el-table-column prop="siteCount" label="站点数量" width="100"></el-table-column>
|
||||||
|
<el-table-column prop="cameraCount" label="摄像头数量" width="120"></el-table-column>
|
||||||
|
<el-table-column label="操作" width="100">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-button type="text" size="small">查看站点</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
<div class="pagination">
|
||||||
|
<el-pagination
|
||||||
|
@size-change="handleSizeChange"
|
||||||
|
@current-change="handleCurrentChange"
|
||||||
|
:current-page="currentPage"
|
||||||
|
:page-sizes="[5, 10, 20, 30]"
|
||||||
|
:page-size="pageSize"
|
||||||
|
layout="total, sizes, prev, pager, next, jumper"
|
||||||
|
:total="tableData.length"
|
||||||
|
></el-pagination>
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import * as echarts from 'echarts';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'RealTimeMonitoring',
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
deviceStatus: {
|
||||||
|
pending: 100,
|
||||||
|
total: 1104,
|
||||||
|
last7Days: 125,
|
||||||
|
last30Days: 185,
|
||||||
|
inactive: 1738
|
||||||
|
},
|
||||||
|
tableData: [
|
||||||
|
{ id: 1, region: '安徽区域', siteCount: 2, cameraCount: 40 },
|
||||||
|
{ id: 2, region: '东北区域', siteCount: 2, cameraCount: 40 },
|
||||||
|
{ id: 3, region: '沪苏区域', siteCount: 1, cameraCount: 311 },
|
||||||
|
{ id: 4, region: '华北区域', siteCount: 5, cameraCount: 120 },
|
||||||
|
{ id: 5, region: '华南区域', siteCount: 2, cameraCount: 60 },
|
||||||
|
{ id: 6, region: '华中区域', siteCount: 10, cameraCount: 279 },
|
||||||
|
{ id: 7, region: '闽浙区域', siteCount: 2, cameraCount: 20 },
|
||||||
|
{ id: 8, region: '南京区域', siteCount: 1, cameraCount: 377 },
|
||||||
|
{ id: 9, region: '山东区域', siteCount: 7, cameraCount: 193 },
|
||||||
|
{ id: 10, region: '苏北区域', siteCount: 3, cameraCount: 100 }
|
||||||
|
],
|
||||||
|
currentPage: 1,
|
||||||
|
pageSize: 5
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
pagedData() {
|
||||||
|
const start = (this.currentPage - 1) * this.pageSize;
|
||||||
|
const end = this.currentPage * this.pageSize;
|
||||||
|
return this.tableData.slice(start, end);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.initCameraChart();
|
||||||
|
this.initSiteChart();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
initCameraChart() {
|
||||||
|
const cameraChart = echarts.init(this.$refs.cameraChart);
|
||||||
|
const cameraOptions = {
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
name: '摄像头数量',
|
||||||
|
type: 'pie',
|
||||||
|
radius: ['50%', '70%'],
|
||||||
|
avoidLabelOverlap: false,
|
||||||
|
label: {
|
||||||
|
show: false,
|
||||||
|
position: 'center'
|
||||||
|
},
|
||||||
|
emphasis: {
|
||||||
|
label: {
|
||||||
|
show: true,
|
||||||
|
fontSize: '30',
|
||||||
|
fontWeight: 'bold'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
labelLine: {
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
data: [
|
||||||
|
{ value: 1885, name: '在线' },
|
||||||
|
{ value: 115, name: '离线' }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
cameraChart.setOption(cameraOptions);
|
||||||
|
},
|
||||||
|
initSiteChart() {
|
||||||
|
const siteChart = echarts.init(this.$refs.siteChart);
|
||||||
|
const siteOptions = {
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
name: '站点数量',
|
||||||
|
type: 'pie',
|
||||||
|
radius: ['50%', '70%'],
|
||||||
|
avoidLabelOverlap: false,
|
||||||
|
label: {
|
||||||
|
show: false,
|
||||||
|
position: 'center'
|
||||||
|
},
|
||||||
|
emphasis: {
|
||||||
|
label: {
|
||||||
|
show: true,
|
||||||
|
fontSize: '30',
|
||||||
|
fontWeight: 'bold'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
labelLine: {
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
data: [
|
||||||
|
{ value: 47, name: '在线' },
|
||||||
|
{ value: 7, name: '离线' }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
siteChart.setOption(siteOptions);
|
||||||
|
},
|
||||||
|
handleSizeChange(val) {
|
||||||
|
this.pageSize = val;
|
||||||
|
},
|
||||||
|
handleCurrentChange(val) {
|
||||||
|
this.currentPage = val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.real-time-monitoring {
|
||||||
|
padding: 20px;
|
||||||
|
background-color: #f5f7fa;
|
||||||
|
}
|
||||||
|
.breadcrumb {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
.alert-summary {
|
||||||
|
background-color: #fff;
|
||||||
|
padding: 20px;
|
||||||
|
border-radius: 10px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
.chart-container {
|
||||||
|
background-color: #fff;
|
||||||
|
padding: 20px;
|
||||||
|
border-radius: 10px;
|
||||||
|
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.chart-header {
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
.chart {
|
||||||
|
width: 100%;
|
||||||
|
height: 300px;
|
||||||
|
}
|
||||||
|
.chart-info {
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
.status {
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
.status.online {
|
||||||
|
color: #67c23a;
|
||||||
|
}
|
||||||
|
.status.offline {
|
||||||
|
color: #f56c6c;
|
||||||
|
}
|
||||||
|
.device-status {
|
||||||
|
background-color: #fff;
|
||||||
|
padding: 20px;
|
||||||
|
border-radius: 10px;
|
||||||
|
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
.status-card {
|
||||||
|
padding: 20px;
|
||||||
|
border-radius: 10px;
|
||||||
|
text-align: center;
|
||||||
|
color: #fff;
|
||||||
|
height: 200px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
.status-card.pending {
|
||||||
|
background: linear-gradient(135deg, #ff9a9e 0%, #fecfef 100%);
|
||||||
|
}
|
||||||
|
.status-card.total {
|
||||||
|
background: linear-gradient(135deg, #a1c4fd 0%, #c2e9fb 100%);
|
||||||
|
}
|
||||||
|
.status-card.inactive {
|
||||||
|
background: linear-gradient(135deg, #a18cd1 0%, #fbc2eb 100%);
|
||||||
|
}
|
||||||
|
.status-header {
|
||||||
|
font-size: 16px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
.status-content {
|
||||||
|
font-size: 28px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
.status-footer {
|
||||||
|
margin-top: 10px;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
.region-overview {
|
||||||
|
background-color: #fff;
|
||||||
|
padding: 20px;
|
||||||
|
border-radius: 10px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
.card-header {
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
.pagination {
|
||||||
|
margin-top: 20px;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<h2>区域1</h2>
|
||||||
|
<!-- 模拟静态数据内容 -->
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: "Region1",
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
/* 添加需要的样式 */
|
||||||
|
</style>
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<h2>区域2</h2>
|
||||||
|
<!-- 模拟静态数据内容 -->
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: "Region2",
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
/* 添加需要的样式 */
|
||||||
|
</style>
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<h2>服务器管理</h2>
|
||||||
|
<!-- 模拟静态数据内容 -->
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: "ServerManagement",
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
/* 添加需要的样式 */
|
||||||
|
</style>
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<h2>站点管理</h2>
|
||||||
|
<!-- 模拟静态数据内容 -->
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: "SiteManagement",
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
/* 添加需要的样式 */
|
||||||
|
</style>
|
||||||
|
|
|
@ -0,0 +1,101 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<el-card class="stats-card">
|
||||||
|
<div class="stats-header">布控情况</div>
|
||||||
|
<el-row :gutter="20" class="stats-row">
|
||||||
|
<el-col :span="8">
|
||||||
|
<div class="stats-item">
|
||||||
|
<div class="stats-title">布控区域数</div>
|
||||||
|
<div class="stats-value">15</div>
|
||||||
|
</div>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="8">
|
||||||
|
<div class="stats-item">
|
||||||
|
<div class="stats-title">布控站点数</div>
|
||||||
|
<div class="stats-value">54</div>
|
||||||
|
<div class="stats-subvalue">7 异常站点</div>
|
||||||
|
</div>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="8">
|
||||||
|
<div class="stats-item">
|
||||||
|
<div class="stats-title">布控摄像头数</div>
|
||||||
|
<div class="stats-value">2000</div>
|
||||||
|
</div>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
<div class="stats-divider"></div>
|
||||||
|
<div class="stats-header">告警情况</div>
|
||||||
|
<el-row :gutter="20" class="stats-row">
|
||||||
|
<el-col :span="8">
|
||||||
|
<div class="stats-item">
|
||||||
|
<div class="stats-title">告警站点</div>
|
||||||
|
<div class="stats-value">25</div>
|
||||||
|
</div>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="8">
|
||||||
|
<div class="stats-item">
|
||||||
|
<div class="stats-title">未完成告警</div>
|
||||||
|
<div class="stats-value">1511</div>
|
||||||
|
</div>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="8">
|
||||||
|
<div class="stats-item">
|
||||||
|
<div class="stats-title">已完成告警</div>
|
||||||
|
<div class="stats-value">123916</div>
|
||||||
|
</div>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</el-card>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'Statistics'
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.stats-card {
|
||||||
|
background-color: #2a3f54;
|
||||||
|
color: #fff;
|
||||||
|
padding: 20px;
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stats-header {
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
border-bottom: 1px solid #3a4b5c;
|
||||||
|
padding-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stats-row {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stats-item {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stats-title {
|
||||||
|
font-size: 16px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stats-value {
|
||||||
|
font-size: 24px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stats-subvalue {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #f39c12;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stats-divider {
|
||||||
|
border-top: 1px solid #3a4b5c;
|
||||||
|
margin: 20px 0;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,376 @@
|
||||||
|
<template>
|
||||||
|
<div class="statistics-analysis">
|
||||||
|
<div class="breadcrumb">
|
||||||
|
<el-breadcrumb separator=">">
|
||||||
|
<el-breadcrumb-item>统计分析</el-breadcrumb-item>
|
||||||
|
</el-breadcrumb>
|
||||||
|
</div>
|
||||||
|
<div class="filter-form">
|
||||||
|
<el-card>
|
||||||
|
<div slot="header" class="card-header">
|
||||||
|
<span>筛选条件</span>
|
||||||
|
</div>
|
||||||
|
<el-form :inline="true" :model="filterForm" class="demo-form-inline">
|
||||||
|
<el-form-item label="所属站点:">
|
||||||
|
<el-select v-model="filterForm.site" placeholder="全部">
|
||||||
|
<el-option label="全部" value="all"></el-option>
|
||||||
|
<el-option label="站点一" value="site1"></el-option>
|
||||||
|
<el-option label="站点二" value="site2"></el-option>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="开始时间:">
|
||||||
|
<el-date-picker v-model="filterForm.startDate" type="date" placeholder="选择日期"></el-date-picker>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="结束时间:">
|
||||||
|
<el-date-picker v-model="filterForm.endDate" type="date" placeholder="选择日期"></el-date-picker>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" @click="onSearch">查询</el-button>
|
||||||
|
<el-button @click="onReset">清空</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</el-card>
|
||||||
|
</div>
|
||||||
|
<div class="device-management">
|
||||||
|
<el-card>
|
||||||
|
<div slot="header" class="card-header">
|
||||||
|
<span>设备管理</span>
|
||||||
|
</div>
|
||||||
|
<el-row :gutter="10">
|
||||||
|
<el-col :span="8">
|
||||||
|
<div class="status-info">
|
||||||
|
<div class="status-title">摄像头总数</div>
|
||||||
|
<div class="status-value">{{ deviceData.totalCameras }}</div>
|
||||||
|
</div>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="8">
|
||||||
|
<div class="status-info">
|
||||||
|
<div class="status-title">在线</div>
|
||||||
|
<div class="status-value">{{ deviceData.online }}</div>
|
||||||
|
</div>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="8">
|
||||||
|
<div class="status-info">
|
||||||
|
<div class="status-title">异常</div>
|
||||||
|
<div class="status-value">{{ deviceData.abnormal }}</div>
|
||||||
|
</div>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
<el-row :gutter="10" class="chart-row">
|
||||||
|
<el-col :span="12">
|
||||||
|
<div ref="statusPieChart" class="chart"></div>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<div class="status-percentage">
|
||||||
|
<div><span class="dot online"></span> 在线 {{ deviceData.onlinePercentage }}%</div>
|
||||||
|
<div><span class="dot abnormal"></span> 异常 {{ deviceData.abnormalPercentage }}%</div>
|
||||||
|
</div>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
<div class="chart-container">
|
||||||
|
<div ref="distributionBarChart" class="chart"></div>
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
</div>
|
||||||
|
<div class="alert-management">
|
||||||
|
<el-card>
|
||||||
|
<div slot="header" class="card-header">
|
||||||
|
<span>告警管理</span>
|
||||||
|
</div>
|
||||||
|
<el-row :gutter="10">
|
||||||
|
<el-col :span="8">
|
||||||
|
<div class="alert-info">
|
||||||
|
<div class="alert-title">总告警数</div>
|
||||||
|
<div class="alert-value">{{ alertData.totalAlerts }}</div>
|
||||||
|
</div>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="4">
|
||||||
|
<div class="alert-info pending">
|
||||||
|
<div class="alert-title">待处理</div>
|
||||||
|
<div class="alert-value">{{ alertData.pending }}</div>
|
||||||
|
</div>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="4">
|
||||||
|
<div class="alert-info processing">
|
||||||
|
<div class="alert-title">处理中</div>
|
||||||
|
<div class="alert-value">{{ alertData.processing }}</div>
|
||||||
|
</div>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="4">
|
||||||
|
<div class="alert-info completed">
|
||||||
|
<div class="alert-title">已完成</div>
|
||||||
|
<div class="alert-value">{{ alertData.completed }}</div>
|
||||||
|
</div>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
<el-row :gutter="10" class="chart-row">
|
||||||
|
<el-col :span="12">
|
||||||
|
<div ref="alertPieChart" class="chart"></div>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<div ref="alertTrendChart" class="chart"></div>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</el-card>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import * as echarts from 'echarts';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'StatisticsAnalysis',
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
filterForm: {
|
||||||
|
site: 'all',
|
||||||
|
startDate: '',
|
||||||
|
endDate: ''
|
||||||
|
},
|
||||||
|
deviceData: {
|
||||||
|
totalCameras: 2000,
|
||||||
|
online: 1885,
|
||||||
|
abnormal: 115,
|
||||||
|
onlinePercentage: 94,
|
||||||
|
abnormalPercentage: 6,
|
||||||
|
distribution: [
|
||||||
|
{ name: '杂物堆积', value: 600 },
|
||||||
|
{ name: '车辆拥堵', value: 500 },
|
||||||
|
{ name: '违规停车', value: 400 },
|
||||||
|
{ name: '饮料垃圾检测', value: 300 },
|
||||||
|
{ name: '垃圾桶满溢', value: 200 },
|
||||||
|
{ name: '入侵检测', value: 150 },
|
||||||
|
{ name: '扶梯运行状态', value: 100 },
|
||||||
|
{ name: '违规放置', value: 80 },
|
||||||
|
{ name: '长期门状态检测', value: 70 },
|
||||||
|
{ name: '人员密集', value: 60 },
|
||||||
|
{ name: '保洁点名', value: 50 }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
alertData: {
|
||||||
|
totalAlerts: 0,
|
||||||
|
pending: 0,
|
||||||
|
processing: 0,
|
||||||
|
completed: 0
|
||||||
|
}
|
||||||
|
};
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.initStatusPieChart();
|
||||||
|
this.initDistributionBarChart();
|
||||||
|
this.initAlertPieChart();
|
||||||
|
this.initAlertTrendChart();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
onSearch() {
|
||||||
|
// 查询逻辑
|
||||||
|
console.log('查询参数:', this.filterForm);
|
||||||
|
},
|
||||||
|
onReset() {
|
||||||
|
this.filterForm = {
|
||||||
|
site: 'all',
|
||||||
|
startDate: '',
|
||||||
|
endDate: ''
|
||||||
|
};
|
||||||
|
},
|
||||||
|
initStatusPieChart() {
|
||||||
|
const statusPieChart = echarts.init(this.$refs.statusPieChart);
|
||||||
|
const options = {
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
name: '设备状态分布',
|
||||||
|
type: 'pie',
|
||||||
|
radius: '50%',
|
||||||
|
data: [
|
||||||
|
{ value: this.deviceData.online, name: '在线' },
|
||||||
|
{ value: this.deviceData.abnormal, name: '异常' }
|
||||||
|
],
|
||||||
|
label: {
|
||||||
|
formatter: '{b}: {d}%'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
statusPieChart.setOption(options);
|
||||||
|
},
|
||||||
|
initDistributionBarChart() {
|
||||||
|
const distributionBarChart = echarts.init(this.$refs.distributionBarChart);
|
||||||
|
const options = {
|
||||||
|
xAxis: {
|
||||||
|
type: 'category',
|
||||||
|
data: this.deviceData.distribution.map(item => item.name)
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
type: 'value'
|
||||||
|
},
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
data: this.deviceData.distribution.map(item => item.value),
|
||||||
|
type: 'bar'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
distributionBarChart.setOption(options);
|
||||||
|
},
|
||||||
|
initAlertPieChart() {
|
||||||
|
const alertPieChart = echarts.init(this.$refs.alertPieChart);
|
||||||
|
const options = {
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
name: '告警事件分布',
|
||||||
|
type: 'pie',
|
||||||
|
radius: '50%',
|
||||||
|
data: [
|
||||||
|
{ value: this.alertData.pending, name: '待处理' },
|
||||||
|
{ value: this.alertData.processing, name: '处理中' },
|
||||||
|
{ value: this.alertData.completed, name: '已完成' }
|
||||||
|
],
|
||||||
|
label: {
|
||||||
|
formatter: '{b}: {d}%'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
alertPieChart.setOption(options);
|
||||||
|
},
|
||||||
|
initAlertTrendChart() {
|
||||||
|
const alertTrendChart = echarts.init(this.$refs.alertTrendChart);
|
||||||
|
const options = {
|
||||||
|
xAxis: {
|
||||||
|
type: 'category',
|
||||||
|
data: ['2024-07-16', '2024-07-17', '2024-07-18', '2024-07-19', '2024-07-20', '2024-07-21', '2024-07-22']
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
type: 'value'
|
||||||
|
},
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
name: '待处理',
|
||||||
|
data: [0, 0, 0, 0, 0, 0, 0],
|
||||||
|
type: 'line',
|
||||||
|
smooth: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '处理中',
|
||||||
|
data: [0, 0, 0, 0, 0, 0, 0],
|
||||||
|
type: 'line',
|
||||||
|
smooth: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '已完成',
|
||||||
|
data: [0, 0, 0, 0, 0, 0, 0],
|
||||||
|
type: 'line',
|
||||||
|
smooth: true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
alertTrendChart.setOption(options);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.statistics-analysis {
|
||||||
|
padding: 20px;
|
||||||
|
background-color: #f5f7fa;
|
||||||
|
}
|
||||||
|
.breadcrumb {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
.filter-form {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
.card-header {
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
.demo-form-inline .el-form-item {
|
||||||
|
margin-right: 20px;
|
||||||
|
}
|
||||||
|
.el-form-item .el-select,
|
||||||
|
.el-form-item .el-date-picker {
|
||||||
|
width: 200px;
|
||||||
|
}
|
||||||
|
.el-button {
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
.device-management {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
.status-info {
|
||||||
|
background-color: #fff;
|
||||||
|
padding: 10px;
|
||||||
|
border-radius: 10px;
|
||||||
|
text-align: center;
|
||||||
|
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
||||||
|
height: 100px;
|
||||||
|
}
|
||||||
|
.status-title {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
.status-value {
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-top: 5px;
|
||||||
|
}
|
||||||
|
.chart-row {
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
.chart {
|
||||||
|
width: 100%;
|
||||||
|
height: 200px;
|
||||||
|
}
|
||||||
|
.status-percentage {
|
||||||
|
margin-top: 20px;
|
||||||
|
font-size: 14px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.status-percentage .dot {
|
||||||
|
display: inline-block;
|
||||||
|
width: 10px;
|
||||||
|
height: 10px;
|
||||||
|
border-radius: 50%;
|
||||||
|
margin-right: 5px;
|
||||||
|
}
|
||||||
|
.status-percentage .online {
|
||||||
|
background-color: #67c23a;
|
||||||
|
}
|
||||||
|
.status-percentage .abnormal {
|
||||||
|
background-color: #f56c6c;
|
||||||
|
}
|
||||||
|
.chart-container {
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
.alert-management {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
.alert-info {
|
||||||
|
background-color: #fff;
|
||||||
|
padding: 20px;
|
||||||
|
border-radius: 10px;
|
||||||
|
text-align: center;
|
||||||
|
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
.alert-info.pending {
|
||||||
|
background: linear-gradient(135deg, #64b5f6 0%, #bbdefb 100%);
|
||||||
|
}
|
||||||
|
.alert-info.processing {
|
||||||
|
background: linear-gradient(135deg, #9575cd 0%, #d1c4e9 100%);
|
||||||
|
}
|
||||||
|
.alert-info.completed {
|
||||||
|
background: linear-gradient(135deg, #81c784 0%, #c8e6c9 100%);
|
||||||
|
}
|
||||||
|
.alert-title {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
.alert-value {
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-top: 5px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
|
@ -0,0 +1,71 @@
|
||||||
|
<template>
|
||||||
|
<div class="system-setting">
|
||||||
|
<el-row>
|
||||||
|
<el-col :span="3">
|
||||||
|
<el-menu
|
||||||
|
:default-openeds="['1', '2', '3', '4']"
|
||||||
|
router
|
||||||
|
class="el-menu-vertical-demo"
|
||||||
|
background-color="#1f2d3d"
|
||||||
|
text-color="#fff"
|
||||||
|
active-text-color="#ffd04b"
|
||||||
|
>
|
||||||
|
<el-submenu index="1">
|
||||||
|
<template slot="title">
|
||||||
|
<i class="el-icon-menu"></i>
|
||||||
|
<span>层级管理</span>
|
||||||
|
</template>
|
||||||
|
<el-menu-item index="/levelList">层级列表</el-menu-item>
|
||||||
|
<el-menu-item index="/levelTree">层级树</el-menu-item>
|
||||||
|
</el-submenu>
|
||||||
|
<el-menu-item index="/siteManagement">
|
||||||
|
<i class="el-icon-s-platform"></i>
|
||||||
|
<span slot="title">站点管理</span>
|
||||||
|
</el-menu-item>
|
||||||
|
<el-submenu index="3">
|
||||||
|
<template slot="title">
|
||||||
|
<i class="el-icon-setting"></i>
|
||||||
|
<span>设备管理</span>
|
||||||
|
</template>
|
||||||
|
<el-menu-item index="/serverManagement">服务器管理</el-menu-item>
|
||||||
|
<el-menu-item index="/cameraManagement">摄像头管理</el-menu-item>
|
||||||
|
</el-submenu>
|
||||||
|
<el-menu-item index="/userManagement">
|
||||||
|
<i class="el-icon-user"></i>
|
||||||
|
<span slot="title">用户管理</span>
|
||||||
|
</el-menu-item>
|
||||||
|
</el-menu>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="21">
|
||||||
|
<div class="router-view-container">
|
||||||
|
<router-view></router-view>
|
||||||
|
</div>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: "SystemSetting",
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.system-setting {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
.el-menu-vertical-demo {
|
||||||
|
height: 100%;
|
||||||
|
border-right: none;
|
||||||
|
}
|
||||||
|
.router-view-container {
|
||||||
|
margin: 20px 30px 20px 10px;
|
||||||
|
padding: 20px;
|
||||||
|
background-color: #fff;
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||||
|
border-radius: 8px;
|
||||||
|
height: calc(100vh - 100px); /* 调整高度以适应页面 */
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<h2>用户管理</h2>
|
||||||
|
<!-- 模拟静态数据内容 -->
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: "UserManagement",
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
/* 添加需要的样式 */
|
||||||
|
</style>
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
|
||||||
|
import {reactive} from "vue"
|
||||||
|
import axios from "axios"
|
||||||
|
|
||||||
|
export default function (){
|
||||||
|
|
||||||
|
let dogList = reactive([
|
||||||
|
'https://images.dog.ceo/breeds/pembroke/n02113023_1774.jpg'
|
||||||
|
])
|
||||||
|
async function addDog(){
|
||||||
|
try {
|
||||||
|
let result = await axios.get('https://dog.ceo/api/breeds/image/random')
|
||||||
|
console.log(result.data.message)
|
||||||
|
dogList.push(result.data.message)
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return {dogList,addDog}
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
import {ref,onMounted} from "vue"
|
||||||
|
import axios from "axios"
|
||||||
|
|
||||||
|
export default function (){
|
||||||
|
|
||||||
|
let sum = ref(0)
|
||||||
|
|
||||||
|
function changeSum(){
|
||||||
|
sum.value++
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
changeSum()
|
||||||
|
})
|
||||||
|
|
||||||
|
return {sum,changeSum}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
import { createApp } from "vue";
|
||||||
|
import App from "./App.vue";
|
||||||
|
import router from "./router";
|
||||||
|
import ElementPlus from 'element-plus'
|
||||||
|
import 'element-plus/dist/index.css'
|
||||||
|
import * as echarts from 'echarts'
|
||||||
|
import axios from 'axios';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const app = createApp(App)
|
||||||
|
|
||||||
|
app.use(ElementPlus)
|
||||||
|
app.use(router)
|
||||||
|
app.config.globalProperties.$echarts = echarts
|
||||||
|
app.mount("#app");
|
|
@ -0,0 +1,75 @@
|
||||||
|
// 第一步,引入createRouter
|
||||||
|
import { createRouter, createWebHistory,createWebHashHistory} from 'vue-router'
|
||||||
|
|
||||||
|
// 引入路由组件
|
||||||
|
import Home from '@/components/Home.vue'
|
||||||
|
import About from '@/components/About.vue'
|
||||||
|
import News from '@/components/News.vue'
|
||||||
|
import AlertManagement from '@/components/AlertManagement.vue'
|
||||||
|
import RealTimeMonitoring from '@/components/RealTimeMonitoring.vue'
|
||||||
|
import StatisticsAnalysis from '@/components/StatisticsAnalysis.vue'
|
||||||
|
import SystemSettings from '@/components/SystemSettings.vue'
|
||||||
|
import LevelList from '@/components/LevelList.vue'
|
||||||
|
import LevelTree from '@/components/LevelTree.vue'
|
||||||
|
import Region1 from '@/components/Region1.vue'
|
||||||
|
import Region2 from '@/components/Region2.vue'
|
||||||
|
import SiteManagement from '@/components/SiteManagement.vue'
|
||||||
|
import DeviceManagement from '@/components/DeviceManagement.vue'
|
||||||
|
import ServerManagement from '@/components/ServerManagement.vue'
|
||||||
|
import CameraManagement from '@/components/CameraManagement.vue'
|
||||||
|
import UserManagement from '@/components/UserManagement.vue'
|
||||||
|
|
||||||
|
|
||||||
|
// 第二步,创建路由求
|
||||||
|
const router = createRouter({
|
||||||
|
history: createWebHashHistory(),
|
||||||
|
routes: [
|
||||||
|
{ path: '/', component: Home },
|
||||||
|
{ path: '/news', component: News },
|
||||||
|
{ path: '/about', component: About },
|
||||||
|
{ path: '/alert-management', name: 'AlertManagement', component: AlertManagement },
|
||||||
|
{ path: '/real-time-monitoring', name: 'RealTimeMonitoring', component: RealTimeMonitoring },
|
||||||
|
{ path: '/statistics-analysis', name: 'StatisticsAnalysis', component: StatisticsAnalysis },
|
||||||
|
{ path: '/system-settings', name: 'SystemSettings', component: SystemSettings },
|
||||||
|
{ path: '/levelList', name: 'LevelList', component: LevelList },
|
||||||
|
{ path: '/region1', name: 'Region1', component: Region1 },
|
||||||
|
{ path: '/region2', name: 'Region2', component: Region2 },
|
||||||
|
{ path: '/siteManagement', name: 'SiteManagement', component: SiteManagement },
|
||||||
|
{ path: '/deviceManagement', name: 'DeviceManagement', component: DeviceManagement },
|
||||||
|
{ path: '/serverManagement', name: 'ServerManagement', component: ServerManagement },
|
||||||
|
{ path: '/cameraManagement', name: 'CameraManagement', component: CameraManagement },
|
||||||
|
{ path: '/userManagement', name: 'UserManagement', component: UserManagement },
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
|
// const routes = [
|
||||||
|
// { path: '/', component: Home },
|
||||||
|
// { path: '/news', component: News },
|
||||||
|
// { path: '/about', component: About },
|
||||||
|
// { path: '/alert-management', name: 'AlertManagement', component: AlertManagement },
|
||||||
|
// { path: '/real-time-monitoring', name: 'RealTimeMonitoring', component: RealTimeMonitoring },
|
||||||
|
// { path: '/statistics-analysis', name: 'StatisticsAnalysis', component: StatisticsAnalysis },
|
||||||
|
// { path: '/system-settings', name: 'SystemSettings', component: SystemSettings },
|
||||||
|
// { path: '/levelList', name: 'LevelList', component: LevelList },
|
||||||
|
// { path: '/region1', name: 'Region1', component: Region1 },
|
||||||
|
// { path: '/region2', name: 'Region2', component: Region2 },
|
||||||
|
// { path: '/siteManagement', name: 'SiteManagement', component: SiteManagement },
|
||||||
|
// { path: '/deviceManagement', name: 'DeviceManagement', component: DeviceManagement },
|
||||||
|
// { path: '/serverManagement', name: 'ServerManagement', component: ServerManagement },
|
||||||
|
// { path: '/cameraManagement', name: 'CameraManagement', component: CameraManagement },
|
||||||
|
// { path: '/userManagement', name: 'UserManagement', component: UserManagement },
|
||||||
|
|
||||||
|
// ]
|
||||||
|
|
||||||
|
// const router = createRouter({
|
||||||
|
// history: createWebHistory(),
|
||||||
|
// routes
|
||||||
|
// })
|
||||||
|
|
||||||
|
|
||||||
|
// const routes = createRouter({
|
||||||
|
// history: createWebHistory(process.env.BASE_URL),
|
||||||
|
// router
|
||||||
|
// })
|
||||||
|
// 暴漏出去router
|
||||||
|
export default router
|
|
@ -0,0 +1,158 @@
|
||||||
|
import axios from "axios";
|
||||||
|
|
||||||
|
export const getSuperboxApiConfig = (address) => {
|
||||||
|
const PORT = 8000;
|
||||||
|
const BASE_ROUTE = "/api/v1";
|
||||||
|
const LOGIN_ROUTE = BASE_ROUTE + "/auth/login";
|
||||||
|
const LOGOUT_ROUTE = BASE_ROUTE + "/auth/logout";
|
||||||
|
const CAMERA_ROUTE = BASE_ROUTE + "/camera/cameras";
|
||||||
|
const EVENTS_ROUTE = BASE_ROUTE + "/event/events";
|
||||||
|
const ALGORITHM_ROUTE = BASE_ROUTE + "/algorithms";
|
||||||
|
|
||||||
|
let addr = address;
|
||||||
|
if (!addr) {
|
||||||
|
addr = window.location.host.replace(/:\d+/, "");
|
||||||
|
}
|
||||||
|
const superboxAddress = "http://" + addr + ":" + PORT.toString();
|
||||||
|
|
||||||
|
return {
|
||||||
|
base: superboxAddress + BASE_ROUTE,
|
||||||
|
login: superboxAddress + LOGIN_ROUTE,
|
||||||
|
logout: superboxAddress + LOGOUT_ROUTE,
|
||||||
|
cameras: superboxAddress + CAMERA_ROUTE,
|
||||||
|
events: superboxAddress + EVENTS_ROUTE,
|
||||||
|
algorithms: superboxAddress + ALGORITHM_ROUTE,
|
||||||
|
};
|
||||||
|
/*return {
|
||||||
|
base: "/api/v1",
|
||||||
|
login: "/api/v1/auth/login",
|
||||||
|
logout: "/api/v1/auth/logout",
|
||||||
|
cameras: "/api/v1/camera/cameras",
|
||||||
|
events: "/api/v1/event/events",
|
||||||
|
};*/
|
||||||
|
};
|
||||||
|
|
||||||
|
axios.defaults.withCredentials = true;
|
||||||
|
export const login = (username, password, address = null) => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const api = getSuperboxApiConfig(address);
|
||||||
|
axios
|
||||||
|
.post(
|
||||||
|
api.login,
|
||||||
|
{
|
||||||
|
username: username,
|
||||||
|
password: password,
|
||||||
|
cookieless: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.then((res) => {
|
||||||
|
if (res.data.err.ec === 0) {
|
||||||
|
resolve(res.data.ret.token);
|
||||||
|
}
|
||||||
|
reject(res.data.err);
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
reject(err);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getCameras = (token, address = null) => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const api = getSuperboxApiConfig(address);
|
||||||
|
axios
|
||||||
|
.get(api.cameras, {
|
||||||
|
withCredentials: true,
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
Authorization: `Bearer ${token}`,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.then((res) => {
|
||||||
|
if (res.data.err.ec === 0) {
|
||||||
|
resolve(res.data.ret.results);
|
||||||
|
}
|
||||||
|
reject(res.data.err);
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
reject(err);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getEvents = (token, address = null) => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const api = getSuperboxApiConfig(address);
|
||||||
|
axios
|
||||||
|
.get(api.events, {
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
Authorization: `Bearer ${token}`,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.then((res) => {
|
||||||
|
if (res.data.err.ec === 0) {
|
||||||
|
resolve(res.data.ret.results);
|
||||||
|
}
|
||||||
|
reject(res.data.err);
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
reject(err);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getAlgorithms = (token, address = null) => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const api = getSuperboxApiConfig(address);
|
||||||
|
axios
|
||||||
|
.get(api.algorithms, {
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
Authorization: `Bearer ${token}`,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.then((res) => {
|
||||||
|
if (res.data.err.ec === 0) {
|
||||||
|
resolve(res.data.ret);
|
||||||
|
}
|
||||||
|
reject(res.data.err);
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
reject(err);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
var codeNameMap = {};
|
||||||
|
|
||||||
|
export const initCodeNameMap = async (token, address = null) => {
|
||||||
|
try {
|
||||||
|
const algorithms = await getAlgorithms(token, address);
|
||||||
|
algorithms.forEach((algorithm) => {
|
||||||
|
codeNameMap[algorithm.code_name] = algorithm.name;
|
||||||
|
});
|
||||||
|
return true;
|
||||||
|
} catch (err) {
|
||||||
|
console.log(err);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const codenameTranslate = (codeName) => {
|
||||||
|
return codeNameMap[codeName];
|
||||||
|
};
|
||||||
|
|
||||||
|
export default {
|
||||||
|
login,
|
||||||
|
getCameras,
|
||||||
|
getEvents,
|
||||||
|
getSuperboxApiConfig,
|
||||||
|
initCodeNameMap,
|
||||||
|
codenameTranslate,
|
||||||
|
};
|
|
@ -0,0 +1,14 @@
|
||||||
|
{
|
||||||
|
"extends": "@vue/tsconfig/tsconfig.dom.json",
|
||||||
|
"include": ["env.d.ts", "src/**/*", "src/**/*.vue"],
|
||||||
|
"exclude": ["src/**/__tests__/*"],
|
||||||
|
"compilerOptions": {
|
||||||
|
"composite": true,
|
||||||
|
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
|
||||||
|
|
||||||
|
"baseUrl": ".",
|
||||||
|
"paths": {
|
||||||
|
"@/*": ["./src/*"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
{
|
||||||
|
"files": [],
|
||||||
|
"references": [
|
||||||
|
{
|
||||||
|
"path": "./tsconfig.node.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "./tsconfig.app.json"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"compilerOptions": {
|
||||||
|
"types": ["element-plus/global"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
{
|
||||||
|
"extends": "@tsconfig/node20/tsconfig.json",
|
||||||
|
"include": [
|
||||||
|
"vite.config.*",
|
||||||
|
"vitest.config.*",
|
||||||
|
"cypress.config.*",
|
||||||
|
"nightwatch.conf.*",
|
||||||
|
"playwright.config.*"
|
||||||
|
],
|
||||||
|
"compilerOptions": {
|
||||||
|
"composite": true,
|
||||||
|
"noEmit": true,
|
||||||
|
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
|
||||||
|
|
||||||
|
"module": "ESNext",
|
||||||
|
"moduleResolution": "Bundler",
|
||||||
|
"types": ["node"]
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,37 @@
|
||||||
|
import { defineConfig } from 'vite'
|
||||||
|
import vue from '@vitejs/plugin-vue'
|
||||||
|
import vueSetupExtend from 'vite-plugin-vue-setup-extend'
|
||||||
|
import { fileURLToPath, URL } from 'node:url'
|
||||||
|
import { createProxyMiddleware } from 'http-proxy-middleware'
|
||||||
|
|
||||||
|
|
||||||
|
// https://vitejs.dev/config/
|
||||||
|
export default defineConfig({
|
||||||
|
plugins: [
|
||||||
|
vue(),
|
||||||
|
vueSetupExtend(),
|
||||||
|
],
|
||||||
|
resolve: {
|
||||||
|
alias: {
|
||||||
|
'@': fileURLToPath(new URL('./src', import.meta.url))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
server:{
|
||||||
|
// host:'192.168.28.44',
|
||||||
|
// host:'127.0.0.1',
|
||||||
|
// host: '192.168.28.32',
|
||||||
|
port: 8090,
|
||||||
|
open:true,
|
||||||
|
cors: true,
|
||||||
|
// proxy: {
|
||||||
|
// '/api': "192.168.28.44:8000"
|
||||||
|
// }
|
||||||
|
proxy: {
|
||||||
|
'/api': {
|
||||||
|
target: 'http://127.0.0.1:8000', // 目标 API 地址
|
||||||
|
changeOrigin: true, // 开启跨域
|
||||||
|
rewrite: path => path.replace('^/api/', '')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
|
@ -0,0 +1,21 @@
|
||||||
|
// vite.config.ts
|
||||||
|
import { fileURLToPath, URL } from "node:url";
|
||||||
|
import { defineConfig } from "file:///D:/java/demo/turing/vue_one/node_modules/vite/dist/node/index.js";
|
||||||
|
import vue from "file:///D:/java/demo/turing/vue_one/node_modules/@vitejs/plugin-vue/dist/index.mjs";
|
||||||
|
import vueSetupExtend from "file:///D:/java/demo/turing/vue_one/node_modules/vite-plugin-vue-setup-extend/dist/index.mjs";
|
||||||
|
var __vite_injected_original_import_meta_url = "file:///D:/java/demo/turing/vue_one/vite.config.ts";
|
||||||
|
var vite_config_default = defineConfig({
|
||||||
|
plugins: [
|
||||||
|
vue(),
|
||||||
|
vueSetupExtend()
|
||||||
|
],
|
||||||
|
resolve: {
|
||||||
|
alias: {
|
||||||
|
"@": fileURLToPath(new URL("./src", __vite_injected_original_import_meta_url))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
export {
|
||||||
|
vite_config_default as default
|
||||||
|
};
|
||||||
|
//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsidml0ZS5jb25maWcudHMiXSwKICAic291cmNlc0NvbnRlbnQiOiBbImNvbnN0IF9fdml0ZV9pbmplY3RlZF9vcmlnaW5hbF9kaXJuYW1lID0gXCJEOlxcXFxqYXZhXFxcXGRlbW9cXFxcdHVyaW5nXFxcXHZ1ZV9vbmVcIjtjb25zdCBfX3ZpdGVfaW5qZWN0ZWRfb3JpZ2luYWxfZmlsZW5hbWUgPSBcIkQ6XFxcXGphdmFcXFxcZGVtb1xcXFx0dXJpbmdcXFxcdnVlX29uZVxcXFx2aXRlLmNvbmZpZy50c1wiO2NvbnN0IF9fdml0ZV9pbmplY3RlZF9vcmlnaW5hbF9pbXBvcnRfbWV0YV91cmwgPSBcImZpbGU6Ly8vRDovamF2YS9kZW1vL3R1cmluZy92dWVfb25lL3ZpdGUuY29uZmlnLnRzXCI7aW1wb3J0IHsgZmlsZVVSTFRvUGF0aCwgVVJMIH0gZnJvbSAnbm9kZTp1cmwnXG5cbmltcG9ydCB7IGRlZmluZUNvbmZpZyB9IGZyb20gJ3ZpdGUnXG5pbXBvcnQgdnVlIGZyb20gJ0B2aXRlanMvcGx1Z2luLXZ1ZSdcbmltcG9ydCB2dWVTZXR1cEV4dGVuZCBmcm9tICd2aXRlLXBsdWdpbi12dWUtc2V0dXAtZXh0ZW5kJ1xuXG4vLyBodHRwczovL3ZpdGVqcy5kZXYvY29uZmlnL1xuZXhwb3J0IGRlZmF1bHQgZGVmaW5lQ29uZmlnKHtcbiAgcGx1Z2luczogW1xuICAgIHZ1ZSgpLFxuICAgIHZ1ZVNldHVwRXh0ZW5kKCksXG4gIF0sXG4gIHJlc29sdmU6IHtcbiAgICBhbGlhczoge1xuICAgICAgJ0AnOiBmaWxlVVJMVG9QYXRoKG5ldyBVUkwoJy4vc3JjJywgaW1wb3J0Lm1ldGEudXJsKSlcbiAgICB9XG4gIH1cbn0pXG4iXSwKICAibWFwcGluZ3MiOiAiO0FBQTZRLFNBQVMsZUFBZSxXQUFXO0FBRWhULFNBQVMsb0JBQW9CO0FBQzdCLE9BQU8sU0FBUztBQUNoQixPQUFPLG9CQUFvQjtBQUo0SSxJQUFNLDJDQUEyQztBQU94TixJQUFPLHNCQUFRLGFBQWE7QUFBQSxFQUMxQixTQUFTO0FBQUEsSUFDUCxJQUFJO0FBQUEsSUFDSixlQUFlO0FBQUEsRUFDakI7QUFBQSxFQUNBLFNBQVM7QUFBQSxJQUNQLE9BQU87QUFBQSxNQUNMLEtBQUssY0FBYyxJQUFJLElBQUksU0FBUyx3Q0FBZSxDQUFDO0FBQUEsSUFDdEQ7QUFBQSxFQUNGO0FBQ0YsQ0FBQzsiLAogICJuYW1lcyI6IFtdCn0K
|
|
@ -0,0 +1,21 @@
|
||||||
|
// vite.config.ts
|
||||||
|
import { fileURLToPath, URL } from "node:url";
|
||||||
|
import { defineConfig } from "file:///D:/java/demo/turing/vue_one/node_modules/vite/dist/node/index.js";
|
||||||
|
import vue from "file:///D:/java/demo/turing/vue_one/node_modules/@vitejs/plugin-vue/dist/index.mjs";
|
||||||
|
import vueSetupExtend from "file:///D:/java/demo/turing/vue_one/node_modules/vite-plugin-vue-setup-extend/dist/index.mjs";
|
||||||
|
var __vite_injected_original_import_meta_url = "file:///D:/java/demo/turing/vue_one/vite.config.ts";
|
||||||
|
var vite_config_default = defineConfig({
|
||||||
|
plugins: [
|
||||||
|
vue(),
|
||||||
|
vueSetupExtend()
|
||||||
|
],
|
||||||
|
resolve: {
|
||||||
|
alias: {
|
||||||
|
"@": fileURLToPath(new URL("./src", __vite_injected_original_import_meta_url))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
export {
|
||||||
|
vite_config_default as default
|
||||||
|
};
|
||||||
|
//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsidml0ZS5jb25maWcudHMiXSwKICAic291cmNlc0NvbnRlbnQiOiBbImNvbnN0IF9fdml0ZV9pbmplY3RlZF9vcmlnaW5hbF9kaXJuYW1lID0gXCJEOlxcXFxqYXZhXFxcXGRlbW9cXFxcdHVyaW5nXFxcXHZ1ZV9vbmVcIjtjb25zdCBfX3ZpdGVfaW5qZWN0ZWRfb3JpZ2luYWxfZmlsZW5hbWUgPSBcIkQ6XFxcXGphdmFcXFxcZGVtb1xcXFx0dXJpbmdcXFxcdnVlX29uZVxcXFx2aXRlLmNvbmZpZy50c1wiO2NvbnN0IF9fdml0ZV9pbmplY3RlZF9vcmlnaW5hbF9pbXBvcnRfbWV0YV91cmwgPSBcImZpbGU6Ly8vRDovamF2YS9kZW1vL3R1cmluZy92dWVfb25lL3ZpdGUuY29uZmlnLnRzXCI7aW1wb3J0IHsgZmlsZVVSTFRvUGF0aCwgVVJMIH0gZnJvbSAnbm9kZTp1cmwnXG5cbmltcG9ydCB7IGRlZmluZUNvbmZpZyB9IGZyb20gJ3ZpdGUnXG5pbXBvcnQgdnVlIGZyb20gJ0B2aXRlanMvcGx1Z2luLXZ1ZSdcbmltcG9ydCB2dWVTZXR1cEV4dGVuZCBmcm9tICd2aXRlLXBsdWdpbi12dWUtc2V0dXAtZXh0ZW5kJ1xuXG4vLyBodHRwczovL3ZpdGVqcy5kZXYvY29uZmlnL1xuZXhwb3J0IGRlZmF1bHQgZGVmaW5lQ29uZmlnKHtcbiAgcGx1Z2luczogW1xuICAgIHZ1ZSgpLFxuICAgIHZ1ZVNldHVwRXh0ZW5kKCksXG4gIF0sXG4gIHJlc29sdmU6IHtcbiAgICBhbGlhczoge1xuICAgICAgJ0AnOiBmaWxlVVJMVG9QYXRoKG5ldyBVUkwoJy4vc3JjJywgaW1wb3J0Lm1ldGEudXJsKSlcbiAgICB9XG4gIH1cbn0pXG4iXSwKICAibWFwcGluZ3MiOiAiO0FBQTZRLFNBQVMsZUFBZSxXQUFXO0FBRWhULFNBQVMsb0JBQW9CO0FBQzdCLE9BQU8sU0FBUztBQUNoQixPQUFPLG9CQUFvQjtBQUo0SSxJQUFNLDJDQUEyQztBQU94TixJQUFPLHNCQUFRLGFBQWE7QUFBQSxFQUMxQixTQUFTO0FBQUEsSUFDUCxJQUFJO0FBQUEsSUFDSixlQUFlO0FBQUEsRUFDakI7QUFBQSxFQUNBLFNBQVM7QUFBQSxJQUNQLE9BQU87QUFBQSxNQUNMLEtBQUssY0FBYyxJQUFJLElBQUksU0FBUyx3Q0FBZSxDQUFDO0FBQUEsSUFDdEQ7QUFBQSxFQUNGO0FBQ0YsQ0FBQzsiLAogICJuYW1lcyI6IFtdCn0K
|
|
@ -0,0 +1,26 @@
|
||||||
|
// vite.config.ts
|
||||||
|
import { fileURLToPath, URL } from "node:url";
|
||||||
|
import { defineConfig } from "file:///D:/java/demo/turing/vue_one/node_modules/vite/dist/node/index.js";
|
||||||
|
import vue from "file:///D:/java/demo/turing/vue_one/node_modules/@vitejs/plugin-vue/dist/index.mjs";
|
||||||
|
import vueSetupExtend from "file:///D:/java/demo/turing/vue_one/node_modules/vite-plugin-vue-setup-extend/dist/index.mjs";
|
||||||
|
var __vite_injected_original_import_meta_url = "file:///D:/java/demo/turing/vue_one/vite.config.ts";
|
||||||
|
var vite_config_default = defineConfig({
|
||||||
|
plugins: [
|
||||||
|
vue(),
|
||||||
|
vueSetupExtend()
|
||||||
|
],
|
||||||
|
resolve: {
|
||||||
|
alias: {
|
||||||
|
"@": fileURLToPath(new URL("./src", __vite_injected_original_import_meta_url))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
server: {
|
||||||
|
host: "127.0.0.1",
|
||||||
|
port: 5137,
|
||||||
|
open: true
|
||||||
|
}
|
||||||
|
});
|
||||||
|
export {
|
||||||
|
vite_config_default as default
|
||||||
|
};
|
||||||
|
//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsidml0ZS5jb25maWcudHMiXSwKICAic291cmNlc0NvbnRlbnQiOiBbImNvbnN0IF9fdml0ZV9pbmplY3RlZF9vcmlnaW5hbF9kaXJuYW1lID0gXCJEOlxcXFxqYXZhXFxcXGRlbW9cXFxcdHVyaW5nXFxcXHZ1ZV9vbmVcIjtjb25zdCBfX3ZpdGVfaW5qZWN0ZWRfb3JpZ2luYWxfZmlsZW5hbWUgPSBcIkQ6XFxcXGphdmFcXFxcZGVtb1xcXFx0dXJpbmdcXFxcdnVlX29uZVxcXFx2aXRlLmNvbmZpZy50c1wiO2NvbnN0IF9fdml0ZV9pbmplY3RlZF9vcmlnaW5hbF9pbXBvcnRfbWV0YV91cmwgPSBcImZpbGU6Ly8vRDovamF2YS9kZW1vL3R1cmluZy92dWVfb25lL3ZpdGUuY29uZmlnLnRzXCI7aW1wb3J0IHsgZmlsZVVSTFRvUGF0aCwgVVJMIH0gZnJvbSAnbm9kZTp1cmwnXG5cbmltcG9ydCB7IGRlZmluZUNvbmZpZyB9IGZyb20gJ3ZpdGUnXG5pbXBvcnQgdnVlIGZyb20gJ0B2aXRlanMvcGx1Z2luLXZ1ZSdcbmltcG9ydCB2dWVTZXR1cEV4dGVuZCBmcm9tICd2aXRlLXBsdWdpbi12dWUtc2V0dXAtZXh0ZW5kJ1xuXG4vLyBodHRwczovL3ZpdGVqcy5kZXYvY29uZmlnL1xuZXhwb3J0IGRlZmF1bHQgZGVmaW5lQ29uZmlnKHtcbiAgcGx1Z2luczogW1xuICAgIHZ1ZSgpLFxuICAgIHZ1ZVNldHVwRXh0ZW5kKCksXG4gIF0sXG4gIHJlc29sdmU6IHtcbiAgICBhbGlhczoge1xuICAgICAgJ0AnOiBmaWxlVVJMVG9QYXRoKG5ldyBVUkwoJy4vc3JjJywgaW1wb3J0Lm1ldGEudXJsKSlcbiAgICB9XG4gIH0sXG4gIHNlcnZlcjp7XG4gICAgaG9zdDonMTI3LjAuMC4xJyxcbiAgICBwb3J0OjUxMzcsXG4gICAgb3Blbjp0cnVlXG4gIH1cbn0pXG4iXSwKICAibWFwcGluZ3MiOiAiO0FBQTZRLFNBQVMsZUFBZSxXQUFXO0FBRWhULFNBQVMsb0JBQW9CO0FBQzdCLE9BQU8sU0FBUztBQUNoQixPQUFPLG9CQUFvQjtBQUo0SSxJQUFNLDJDQUEyQztBQU94TixJQUFPLHNCQUFRLGFBQWE7QUFBQSxFQUMxQixTQUFTO0FBQUEsSUFDUCxJQUFJO0FBQUEsSUFDSixlQUFlO0FBQUEsRUFDakI7QUFBQSxFQUNBLFNBQVM7QUFBQSxJQUNQLE9BQU87QUFBQSxNQUNMLEtBQUssY0FBYyxJQUFJLElBQUksU0FBUyx3Q0FBZSxDQUFDO0FBQUEsSUFDdEQ7QUFBQSxFQUNGO0FBQUEsRUFDQSxRQUFPO0FBQUEsSUFDTCxNQUFLO0FBQUEsSUFDTCxNQUFLO0FBQUEsSUFDTCxNQUFLO0FBQUEsRUFDUDtBQUNGLENBQUM7IiwKICAibmFtZXMiOiBbXQp9Cg==
|
Loading…
Reference in New Issue