添加百度地图类型声明,更新 HTML 以加载百度地图 API,新增多个视图组件和路由配置以支持信号覆盖、网络质量、数据连接、热门应用和个人用户的统计展示。

This commit is contained in:
wangran
2026-01-09 16:51:16 +08:00
parent eef27d2bbc
commit a2379ccf2a
42 changed files with 7339 additions and 8 deletions

View File

@@ -0,0 +1,332 @@
<script setup lang="ts">
import { onMounted, ref, onUnmounted, nextTick } from 'vue'
import * as echarts from 'echarts'
interface ConnectionRateItem {
operator: string
connectionRate: number
totalCount: number
successCount: number
}
const chartContainer = ref<HTMLDivElement | null>(null)
const startDate = ref<string>('')
const endDate = ref<string>('')
const loading = ref<boolean>(false)
const chartInstance = ref<echarts.ECharts | null>(null)
// 运营商名称映射
const operatorMap: Record<string, string> = {
CMCC: '中国移动',
CUCC: '中国联通',
CTCC: '中国电信'
}
// 初始化图表
const initChart = () => {
if (!chartContainer.value) return
// 如果图表已存在,先销毁
if (chartInstance.value) {
chartInstance.value.dispose()
}
chartInstance.value = echarts.init(chartContainer.value)
}
// 更新图表数据
const updateChart = (data: ConnectionRateItem[]) => {
if (!chartInstance.value) return
// 按运营商代码排序,确保顺序一致
const sortedData = [...data].sort((a, b) => {
const order = ['CMCC', 'CUCC', 'CTCC']
return order.indexOf(a.operator) - order.indexOf(b.operator)
})
const operators = sortedData.map(item => operatorMap[item.operator] || item.operator)
const rates = sortedData.map(item => item.connectionRate)
const option = {
title: {
text: '各运营商数据连接率统计',
left: 'center',
textStyle: {
fontSize: 18,
fontWeight: 'bold'
}
},
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow'
},
formatter: (params: any) => {
const dataIndex = params[0].dataIndex
const item = sortedData[dataIndex]
return `
<div style="padding: 8px;">
<div><strong>${params[0].name}</strong></div>
<div>连接率: <strong>${item.connectionRate}%</strong></div>
<div>总数量: ${item.totalCount}</div>
<div>成功数量: ${item.successCount}</div>
</div>
`
}
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
containLabel: true
},
xAxis: {
type: 'category',
data: operators,
axisLabel: {
fontSize: 12
}
},
yAxis: {
type: 'value',
name: '连接率 (%)',
min: 0,
max: 100,
axisLabel: {
formatter: '{value}%'
}
},
series: [
{
name: '连接率',
type: 'bar',
data: rates,
itemStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: '#83bff6' },
{ offset: 0.5, color: '#188df0' },
{ offset: 1, color: '#188df0' }
])
},
emphasis: {
itemStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: '#2378f7' },
{ offset: 0.7, color: '#2378f7' },
{ offset: 1, color: '#83bff6' }
])
}
},
label: {
show: true,
position: 'top',
formatter: '{c}%',
fontSize: 12,
fontWeight: 'bold'
}
}
]
}
chartInstance.value.setOption(option)
}
// 获取数据
const fetchData = async () => {
loading.value = true
try {
const body: { startDate?: string; endDate?: string } = {}
if (startDate.value) body.startDate = startDate.value
if (endDate.value) body.endDate = endDate.value
const response = await fetch('http://localhost:8081/dataConnection/connectionRate', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(body)
})
if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`)
const data = await response.json() as ConnectionRateItem[]
// 输出返回的数据到控制台
console.log('返回的数据:', data)
await nextTick()
updateChart(data)
} catch (error) {
console.error('获取数据失败:', error)
// 清空图表
if (chartInstance.value) {
chartInstance.value.setOption({
series: [{ data: [] }]
})
}
} finally {
loading.value = false
}
}
// 监听窗口大小变化,自动调整图表大小
const handleResize = () => {
if (chartInstance.value) {
chartInstance.value.resize()
}
}
onMounted(async () => {
await nextTick()
initChart()
await fetchData()
window.addEventListener('resize', handleResize)
})
onUnmounted(() => {
window.removeEventListener('resize', handleResize)
if (chartInstance.value) {
chartInstance.value.dispose()
chartInstance.value = null
}
})
</script>
<template>
<div class="page">
<!-- 加载遮罩 -->
<div v-if="loading" class="loading-overlay">
<div class="loading-spinner">
<div class="spinner"></div>
<p class="loading-text">加载数据中...</p>
</div>
</div>
<h2>业务需求16数据链接-数据链接率统计</h2>
<div class="filter-bar">
<div class="filter-item">
<label for="startDate">开始日期:</label>
<input
id="startDate"
type="date"
v-model="startDate"
class="date-input"
:disabled="loading"
/>
</div>
<div class="filter-item">
<label for="endDate">结束日期:</label>
<input
id="endDate"
type="date"
v-model="endDate"
class="date-input"
:disabled="loading"
/>
</div>
<button @click="fetchData" class="refresh-btn" :disabled="loading">
{{ loading ? '加载中...' : '查询' }}
</button>
</div>
<div class="chart-container" ref="chartContainer"></div>
</div>
</template>
<style scoped>
.page {
padding: 16px;
height: 100%;
display: flex;
flex-direction: column;
position: relative; /* 让加载遮罩只覆盖当前页面,而不是全局 */
}
h2 {
margin: 0 0 16px 0;
font-size: 20px;
font-weight: 600;
}
.filter-bar {
display: flex;
align-items: center;
gap: 16px;
margin-bottom: 16px;
padding: 12px;
background: #f9fafb;
border-radius: 6px;
flex-wrap: wrap;
}
.filter-item {
display: flex;
align-items: center;
gap: 8px;
}
.date-input {
padding: 6px 12px;
border: 1px solid #d1d5db;
border-radius: 4px;
font-size: 14px;
}
.refresh-btn {
padding: 6px 16px;
background: #3b82f6;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 14px;
}
.refresh-btn:hover:not(:disabled) {
background: #2563eb;
}
.refresh-btn:disabled {
background: #9ca3af;
cursor: not-allowed;
}
.chart-container {
width: 100%;
height: calc(100vh - 200px);
min-height: 500px;
border: 1px solid #e5e7eb;
border-radius: 4px;
flex: 1;
}
.loading-overlay {
position: absolute;
inset: 0;
background: rgba(255, 255, 255, 0.9);
display: flex;
align-items: center;
justify-content: center;
z-index: 10;
}
.loading-spinner {
display: flex;
flex-direction: column;
align-items: center;
gap: 16px;
}
.spinner {
width: 48px;
height: 48px;
border: 4px solid #e5e7eb;
border-top-color: #3b82f6;
border-radius: 50%;
animation: spin 1s linear infinite;
}
@keyframes spin {
to {
transform: rotate(360deg);
}
}
.loading-text {
font-size: 16px;
color: #374151;
margin: 0;
}
</style>