feat: 更细一次

This commit is contained in:
2025-12-13 20:34:12 +08:00
parent 5e0c88b70e
commit 6c3134656d
5 changed files with 695 additions and 0 deletions

32
src/api/dataMonitor.js Normal file
View File

@ -0,0 +1,32 @@
import request from '@/utils/request'
const api = "/mosty-api/mosty-gsxt";
/**
* 数据监控相关API接口
*/
/**
* 获取数据监测统计数据
* 接口地址:api+ /gsxt/sjjc/jrsjtj
* @returns {Promise} 统计数据
*/
export function getStatisticsData() {
return request({
url: api + '/gsxt/sjjc/jrsjtj',
method: 'get'
})
}
/**
* 获取数据接入状态列表
* 接口地址:api+ /gsxt/sjjc/selectList
* @param {string} jklx 监控类型01 公安内网数据、02 政务数据、03 社会数据、04 非结构化数据
* @returns {Promise} 监控数据列表
*/
export function getMonitorList(params) {
return request({
url: api + '/gsxt/sjjc/selectList',
method: 'get',
params: params
})
}

View File

@ -485,6 +485,31 @@ export const publicRoutes = [
} }
] ]
}, },
{
path: "/dataMonitor",
name: "dataMonitor",
meta: { title: "数据监控", icon: "article" },
children: [
{
path: "/resourceMonitoring",
name: "resourceMonitoring",
component: () => import("@/views/backOfficeSystem/dataMonitor/resourceMonitoring/index.vue"),
meta: {
title: "数据资源检测",
icon: "article"
}
},
{
path: "/onlineUserMonitoring",
name: "onlineUserMonitoring",
component: () => import("@/views/backOfficeSystem/dataMonitor/onlineUserMonitoring/index.vue"),
meta: {
title: "在线用户监控",
icon: "article"
}
}
]
},
{ {
path: "/IntelligentControl", path: "/IntelligentControl",
name: "IntelligentControl", name: "IntelligentControl",

View File

@ -0,0 +1,297 @@
<template>
<div class="data-table-card" :class="`${type}-table`">
<div class="table-header">
<div class="header-left">
<span class="circle-point" :style="{ backgroundColor: color }"></span>
<span class="table-title">{{ title }}</span>
<!-- <el-tag size="small" type="success">{{ total }} </el-tag> -->
</div>
<div class="header-right">
<el-button type="text" size="small" @click="handleRefresh">
<el-icon>
<Refresh />
</el-icon>
</el-button>
</div>
</div>
<div class="table-container">
<el-table :data="tableData" style="width: 100%" :loading="loading" height="280" :row-class-name="getRowClassName">
<el-table-column prop="jklx" label="监控类型">
<template #default="{ row }">
<el-tag size="small" type="info">{{ jklxCname[row.jklx] }}</el-tag>
</template>
</el-table-column>
<el-table-column prop="sjly" label="数据来源" show-overflow-tooltip />
<el-table-column prop="gxsj" label="最近更新时间">
<template #default="{ row }">
<div class="time-info">
<el-icon>
<Clock />
</el-icon>
<span>{{ row.gxsj || '' }}</span>
</div>
</template>
</el-table-column>
<el-table-column prop="online" label="通道状态" align="center">
<template #default="{ row }">
<div class="status-badge" :class="{ online: row.online }">
<div class="status-dot"></div>
<span>{{ row.online ? '在线' : '离线' }}</span>
</div>
</template>
</el-table-column>
</el-table>
</div>
</div>
</template>
<script setup>
import { ref, computed, onMounted } from 'vue'
import { Refresh, Clock } from '@element-plus/icons-vue'
import { ElMessage } from 'element-plus'
import { getMonitorList } from '@/api/dataMonitor'
// Props
const props = defineProps({
title: {
type: String,
required: true
},
type: {
type: String,
required: true,
validator: (value) => ['police', 'government', 'social', 'unstructured'].includes(value)
},
/** 颜色标题数组 */
opt: {
type: Array,
default: () => []
}
})
// Emits
const emit = defineEmits(['refresh'])
const color = computed(() => {
const opt = props.opt.find(item => item.type === props.type)
console.log('opt: ', opt);
return opt ? opt.color : '#409EFF'
})
// 组件内部状态
const loading = ref(false)
const data = ref([])
// 类型到监控类型的映射
const typeToJklx = {
/** 01 公安内网数据 */
police: '01',
/** 02 政务数据 */
government: '02',
/** 03 社会数据 */
social: '03',
/** 04 非结构化数据 */
unstructured: '04'
}
const jklxCname = {
'01': '公安内网数据',
'02': '政务数据',
'03': '社会数据',
'04': '非结构化数据'
}
// 获取数据
const fetchData = async () => {
try {
loading.value = true
const jklx = typeToJklx[props.type]
const params = {
pageCurrent: 1,
pageSize: 40,
jklx: jklx || '01'
}
const res = await getMonitorList(params) || {}
// 通道是否在线0 不在线 、1 在线
const onlineStatus = {
0: '不在线',
1: '在线'
}
// 处理数据,添加在线状态
data.value = (res || []).map(item => ({
...item,
online: onlineStatus[item.sfzx]
}))
} catch (error) {
ElMessage.error(`获取${props.title}数据失败`)
} finally {
loading.value = false
}
}
// 计算属性:表格数据
const tableData = computed(() => data.value)
// 总数
const total = ref(0)
// 获取表格行类名
const getRowClassName = ({ row }) => {
return row.online == 1 ? 'online-row' : 'offline-row'
}
// 刷新处理
const handleRefresh = async() => {
await fetchData()
ElMessage.success(`数据刷新成功`)
}
// 组件挂载时获取数据
onMounted(() => {
fetchData()
})
</script>
<style scoped lang="scss">
.data-table-card {
background: white;
border: 1px solid #e4e7ed;
border-radius: 4px;
margin-bottom: 16px;
}
.table-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 12px 16px;
border-bottom: 1px solid #e4e7ed;
background: #f5f7fa;
.header-left {
display: flex;
align-items: center;
gap: 8px;
.circle-point {
width: 12px;
height: 12px;
border-radius: 50%;
position: relative;
&::before {
content: '';
position: absolute;
top: -2px;
left: -2px;
right: -2px;
bottom: -2px;
border-radius: 50%;
background: inherit;
opacity: 0.3;
animation: pulse-light 2s ease-in-out infinite;
}
&::after {
content: '';
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 4px;
height: 4px;
border-radius: 50%;
background: white;
}
}
.table-title {
font-size: 14px;
font-weight: 500;
color: #303133;
}
}
.header-right {
.el-button {
color: #909399;
&:hover {
color: #409eff;
}
}
}
}
.table-container {
padding: 16px;
}
// 状态徽章样式
.status-badge {
display: flex;
align-items: center;
justify-content: center;
gap: 4px;
// padding: 2px 8px;
border-radius: 4px;
font-size: 12px;
&.online {
color: #67c23a;
background: #f0f9ff;
}
&:not(.online) {
color: #f56c6c;
background: #fef0f0;
}
.status-dot {
width: 6px;
height: 6px;
border-radius: 50%;
background: currentColor;
}
}
// 时间信息样式
.time-info {
display: flex;
align-items: center;
gap: 4px;
color: #606266;
font-size: 12px;
.el-icon {
color: #909399;
}
}
// 光圈动画
@keyframes pulse-light {
0% {
opacity: 0.3;
transform: scale(1);
}
50% {
opacity: 0.1;
transform: scale(1.3);
}
100% {
opacity: 0.3;
transform: scale(1);
}
}
// 表格行状态
:deep(.el-table) {
.online-row {
background-color: #f0f9ff;
}
.offline-row {
background-color: #fef0f0;
}
}
</style>

View File

@ -0,0 +1,5 @@
<template>
<div>
开发中
</div>
</template>

View File

@ -0,0 +1,336 @@
<template>
<div class="data-monitor-container">
<!-- 第一行统计数据展示 -->
<div class="statistics-section">
<el-row :gutter="24">
<el-col :span="6" v-for="item in statisticsData" :key="item.type">
<div class="statistics-card" :class="item.type" :style="{ backgroundColor: item.color }">
<div class="card-background">
<div class="bg-pattern"></div>
</div>
<div class="statistics-content">
<div class="statistics-icon">
<el-icon size="48">
<component :is="item.icon" />
</el-icon>
</div>
<div class="statistics-info">
<div class="statistics-title">{{ item.title }}</div>
<div class="statistics-value">{{ item.value }}</div>
</div>
</div>
</div>
</el-col>
</el-row>
</div>
<!-- 第二行公安内网数据和政务数据 -->
<div class="table-section">
<el-row :gutter="24">
<el-col :span="12">
<DataTableCard :opt="statisticsData" title="公安内网数据" type="police" />
</el-col>
<el-col :span="12">
<DataTableCard :opt="statisticsData" title="政务数据" type="government" />
</el-col>
</el-row>
</div>
<!-- 第三行社会数据和非结构化数据 -->
<div class="table-section">
<el-row :gutter="24">
<el-col :span="12">
<DataTableCard :opt="statisticsData" title="社会数据" type="social" />
</el-col>
<el-col :span="12">
<DataTableCard :opt="statisticsData" title="非结构化数据" type="unstructured" />
</el-col>
</el-row>
</div>
</div>
</template>
<script setup>
import { ref, reactive, onMounted } from 'vue'
import { ElMessage } from 'element-plus'
import { Monitor, DataLine, User, Document } from '@element-plus/icons-vue'
import { getStatisticsData } from '@/api/dataMonitor'
import DataTableCard from '../components/DataTableCard.vue'
// 统计数据
const statisticsData = ref([
{
type: 'police',
title: '公安内网数据',
value: 0,
icon: Monitor,
color: '#409EFF',
},
{
type: 'government',
title: '政务数据',
value: 0,
icon: DataLine,
color: '#67C23A',
},
{
type: 'social',
title: '社会数据',
value: 0,
icon: User,
color: '#E6A23C',
},
{
type: 'unstructured',
title: '非结构化数据',
value: 0,
icon: Document,
color: '#F56C6C',
}
])
// 获取统计数据
const fetchStatisticsData = async () => {
try {
const res = await getStatisticsData()
console.log('res: ', res)
if (res) {
statisticsData.value[0].value = res.ganwsj || 0 // 公安内网数据
statisticsData.value[1].value = res.zwsj || 0 // 政务数据
statisticsData.value[2].value = res.shsj || 0 // 社会数据
statisticsData.value[3].value = res.fjghsj || 0 // 非结构化数据
}
} catch (error) {
console.error('获取统计数据失败:', error)
ElMessage.error('获取统计数据失败')
}
}
// 处理表格刷新事件
const handleTableRefresh = (type) => {
ElMessage.success('数据已刷新')
}
// 页面加载时获取统计数据
onMounted(() => {
fetchStatisticsData()
})
</script>
<style scoped lang="scss">
.data-monitor-container {
height: calc(100vh - 125px);
padding: 20px;
background: #fff;
overflow: auto;
}
// 页面头部
.page-header {
background: white;
border-radius: 12px;
padding: 24px;
margin-bottom: 24px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
backdrop-filter: blur(10px);
.header-content {
display: flex;
justify-content: space-between;
align-items: center;
}
.page-title {
display: flex;
align-items: center;
margin: 0;
font-size: 28px;
font-weight: 700;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
.title-icon {
margin-right: 12px;
font-size: 32px;
color: #667eea;
}
}
.header-actions {
display: flex;
gap: 12px;
}
}
// 统计卡片样式
.statistics-section {
margin-bottom: 32px;
}
.statistics-card {
position: relative;
background: white;
border-radius: 16px;
padding: 24px;
// cursor: pointer;
transition: all 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275);
// overflow: hidden;
box-shadow: 0 8px 32px rgba(31, 38, 135, 0.15);
backdrop-filter: blur(4px);
border: 1px solid rgba(255, 255, 255, 0.18);
&:hover {
transform: translateY(-8px) scale(1.02);
box-shadow: 0 16px 48px rgba(31, 38, 135, 0.25);
}
&.police {
.statistics-icon {
color: #fff;
}
}
&.government {
.statistics-icon {
color: #fff;
}
}
&.social {
.statistics-icon {
color: #fff;
}
}
&.unstructured {
.statistics-icon {
color: #fff;
}
}
}
.card-background {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
overflow: hidden;
opacity: 0.1;
.bg-pattern {
position: absolute;
width: 200px;
height: 200px;
background: radial-gradient(circle, white 1px, transparent 1px);
background-size: 20px 20px;
border-radius: 50%;
top: -100px;
right: -100px;
animation: float 6s ease-in-out infinite;
}
}
.statistics-content {
position: relative;
z-index: 2;
display: flex;
align-items: center;
}
.statistics-icon {
margin-right: 20px;
padding: 16px;
border-radius: 12px;
background: rgba(255, 255, 255, 0.2);
backdrop-filter: blur(10px);
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.1);
}
.statistics-info {
flex: 1;
color: white;
}
.statistics-title {
font-size: 16px;
font-weight: 500;
margin-bottom: 8px;
opacity: 0.9;
}
.statistics-value {
font-size: 36px;
font-weight: 700;
margin-bottom: 8px;
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
}
// 表格区域样式
.table-section {
margin-bottom: 24px;
}
// 响应式设计
@media (max-width: 1200px) {
.statistics-card {
margin-bottom: 16px;
}
}
@media (max-width: 768px) {
.data-monitor-container {
padding: 16px;
overflow: auto;
}
.page-header {
padding: 16px;
.header-content {
flex-direction: column;
gap: 16px;
align-items: flex-start;
}
.page-title {
font-size: 22px;
}
}
.statistics-card {
padding: 16px;
.statistics-content {
flex-direction: column;
text-align: center;
}
.statistics-icon {
margin-right: 0;
margin-bottom: 12px;
}
.statistics-value {
font-size: 28px;
}
}
.table-header {
padding: 12px 16px;
flex-direction: column;
gap: 8px;
align-items: flex-start;
}
.table-container {
padding: 8px;
}
}
</style>