完善首页的页面
This commit is contained in:
parent
4a5e37b5d9
commit
d62b8267a8
25
src/api/dashboard/index.js
Normal file
25
src/api/dashboard/index.js
Normal file
@ -0,0 +1,25 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
/**
|
||||
* 获取物种统计信息
|
||||
* @returns {Promise} 返回物种统计数据
|
||||
*/
|
||||
export function getSpeciesStatistics() {
|
||||
return request.get('/api/admin/species/statistics/overview')
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取巡护任务统计信息
|
||||
* @returns {Promise} 返回巡护任务统计数据
|
||||
*/
|
||||
export function getPatrolStatistics() {
|
||||
return request.get('/api/admin/patrol/records/statistics/overview')
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取设备列表信息
|
||||
* @returns {Promise} 返回设备列表数据
|
||||
*/
|
||||
export function getDeviceList() {
|
||||
return request.get('/api/device/list')
|
||||
}
|
||||
@ -1,5 +1,5 @@
|
||||
<script setup>
|
||||
import { ref, onMounted } from "vue";
|
||||
import { ref, onMounted, onUnmounted } from "vue";
|
||||
import * as echarts from "echarts";
|
||||
import { markRaw } from 'vue'
|
||||
import {
|
||||
@ -7,9 +7,9 @@ import {
|
||||
DataAnalysis,
|
||||
Location,
|
||||
Document,
|
||||
Timer,
|
||||
Bell
|
||||
Warning
|
||||
} from "@element-plus/icons-vue";
|
||||
import { getSpeciesStatistics, getPatrolStatistics, getDeviceList } from '@/api/dashboard'
|
||||
|
||||
// 使用 markRaw 包装图标组件
|
||||
const icons = {
|
||||
@ -17,41 +17,275 @@ const icons = {
|
||||
DataAnalysis: markRaw(DataAnalysis),
|
||||
Location: markRaw(Location),
|
||||
Document: markRaw(Document),
|
||||
Timer: markRaw(Timer),
|
||||
Bell: markRaw(Bell)
|
||||
Warning: markRaw(Warning)
|
||||
};
|
||||
|
||||
// 统计数据
|
||||
const statistics = ref({
|
||||
species: {
|
||||
total: 128,
|
||||
today: 12,
|
||||
trend: "+8%",
|
||||
type: "success",
|
||||
icon: "Histogram",
|
||||
// 物种类别选项
|
||||
const categoryOptions = [
|
||||
{ label: '鸟类', value: 'bird' },
|
||||
{ label: '哺乳类', value: 'mammal' },
|
||||
{ label: '鱼类', value: 'fish' },
|
||||
{ label: '两栖类', value: 'amphibian' },
|
||||
{ label: '爬行类', value: 'reptile' },
|
||||
{ label: '昆虫类', value: 'insect' },
|
||||
{ label: '植物', value: 'plant' }
|
||||
]
|
||||
|
||||
// 图表实例
|
||||
const categoryChartRef = ref(null)
|
||||
let categoryChart = null
|
||||
|
||||
// 统计卡片数据
|
||||
const statsCards = ref([
|
||||
{
|
||||
title: '物种监测',
|
||||
icon: icons.Monitor,
|
||||
value: '0',
|
||||
unit: '种',
|
||||
change: { value: '0', label: '今日新增' },
|
||||
color: '#1890FF',
|
||||
bgColor: 'linear-gradient(120deg, #0072FF 0%, #00C6FF 100%)',
|
||||
features: ['实时监测', '智能识别', '行为分析', '分布追踪']
|
||||
},
|
||||
environment: {
|
||||
normal: 22,
|
||||
abnormal: 2,
|
||||
trend: "normal",
|
||||
type: "warning",
|
||||
icon: "Monitor",
|
||||
{
|
||||
title: '环境监测',
|
||||
icon: icons.DataAnalysis,
|
||||
value: '2',
|
||||
unit: '点',
|
||||
change: { value: '2', label: '异常' },
|
||||
color: '#F5222D',
|
||||
bgColor: 'linear-gradient(120deg, #FF416C 0%, #FF4B2B 100%)',
|
||||
features: ['水质监测', '空气监测', '土壤监测', '气象监测']
|
||||
},
|
||||
patrol: {
|
||||
total: 12,
|
||||
completed: 8,
|
||||
progress: "66%",
|
||||
type: "primary",
|
||||
icon: "Location",
|
||||
{
|
||||
title: '巡护任务',
|
||||
icon: icons.Location,
|
||||
value: '0',
|
||||
unit: '个',
|
||||
change: { value: '0%', label: '完成率' },
|
||||
color: '#52C41A',
|
||||
bgColor: 'linear-gradient(120deg, #00B09B 0%, #96C93D 100%)',
|
||||
features: ['智能派单', '轨迹记录', '实时通讯', '数据采集']
|
||||
},
|
||||
devices: {
|
||||
total: 36,
|
||||
online: 32,
|
||||
rate: "88.9%",
|
||||
type: "info",
|
||||
icon: "Connection",
|
||||
},
|
||||
});
|
||||
{
|
||||
title: '设备状态',
|
||||
icon: icons.Monitor,
|
||||
value: '0',
|
||||
unit: '台',
|
||||
change: { value: '0%', label: '在线率' },
|
||||
color: '#722ED1',
|
||||
bgColor: 'linear-gradient(120deg, #7F00FF 0%, #E100FF 100%)',
|
||||
features: ['状态监控', '故障预警', '维护管理', '性能分析']
|
||||
}
|
||||
]);
|
||||
|
||||
// 设备状态统计数据
|
||||
const deviceData = ref({
|
||||
total: 0,
|
||||
online: 0
|
||||
})
|
||||
|
||||
// 初始化物种类别图表
|
||||
const initCategoryChart = () => {
|
||||
if (!categoryChartRef.value) return
|
||||
|
||||
categoryChart = echarts.init(categoryChartRef.value)
|
||||
const option = {
|
||||
tooltip: {
|
||||
trigger: 'item',
|
||||
formatter: '{b}: {c}种 ({d}%)'
|
||||
},
|
||||
legend: {
|
||||
orient: 'vertical',
|
||||
left: 'left',
|
||||
top: 'middle',
|
||||
textStyle: {
|
||||
color: '#303133'
|
||||
}
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '物种数量',
|
||||
type: 'pie',
|
||||
radius: ['40%', '70%'],
|
||||
center: ['60%', '50%'],
|
||||
avoidLabelOverlap: true,
|
||||
itemStyle: {
|
||||
borderRadius: 10,
|
||||
borderColor: '#fff',
|
||||
borderWidth: 2
|
||||
},
|
||||
label: {
|
||||
show: true,
|
||||
formatter: '{b}: {c}种'
|
||||
},
|
||||
emphasis: {
|
||||
label: {
|
||||
show: true,
|
||||
fontSize: 14,
|
||||
fontWeight: 'bold'
|
||||
},
|
||||
itemStyle: {
|
||||
shadowBlur: 10,
|
||||
shadowOffsetX: 0,
|
||||
shadowColor: 'rgba(0, 0, 0, 0.5)'
|
||||
}
|
||||
},
|
||||
data: []
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
categoryChart.setOption(option)
|
||||
}
|
||||
|
||||
// 更新图表数据
|
||||
const updateCategoryChart = (data) => {
|
||||
if (!categoryChart) return
|
||||
|
||||
// 物种类别图表数据
|
||||
const categoryData = Object.entries(data.categories)
|
||||
.filter(([_, count]) => count.total_count > 0)
|
||||
.map(([category, count]) => ({
|
||||
name: categoryOptions.find(item => item.value === category)?.label || category,
|
||||
value: parseInt(count.total_count)
|
||||
}))
|
||||
.sort((a, b) => b.value - a.value)
|
||||
|
||||
categoryChart.setOption({
|
||||
series: [{
|
||||
data: categoryData
|
||||
}]
|
||||
})
|
||||
}
|
||||
|
||||
// 获取物种统计数据
|
||||
const fetchSpeciesData = async () => {
|
||||
try {
|
||||
const res = await getSpeciesStatistics()
|
||||
if (res.success && res.data) {
|
||||
// 计算总物种数和今日新增数
|
||||
const totalSpecies = Object.values(res.data.categories).reduce((sum, category) =>
|
||||
sum + (parseInt(category.total_count) || 0), 0)
|
||||
const todayNew = Object.values(res.data.categories).reduce((sum, category) =>
|
||||
sum + (parseInt(category.today_count) || 0), 0)
|
||||
|
||||
// 更新物种监测卡片
|
||||
statsCards.value[0].value = String(totalSpecies || 0)
|
||||
statsCards.value[0].change.value = `+${todayNew || 0}`
|
||||
|
||||
// 更新物种分布图表
|
||||
updateCategoryChart(res.data)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取物种统计数据失败:', error)
|
||||
}
|
||||
}
|
||||
|
||||
// 获取巡护任务统计数据
|
||||
const fetchPatrolData = async () => {
|
||||
try {
|
||||
const res = await getPatrolStatistics()
|
||||
if (res.success && res.data) {
|
||||
const { overview } = res.data
|
||||
const progress = ((overview.completed_count / overview.total_count) * 100).toFixed(1)
|
||||
|
||||
// 更新巡护任务卡片
|
||||
statsCards.value[2].value = String(overview.total_count)
|
||||
statsCards.value[2].change.value = `${progress}%`
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取巡护任务统计数据失败:', error)
|
||||
}
|
||||
}
|
||||
|
||||
// 获取设备列表数据
|
||||
const fetchDeviceData = async () => {
|
||||
try {
|
||||
const res = await getDeviceList()
|
||||
if (res.success && res.data?.list) {
|
||||
const deviceList = res.data.list
|
||||
deviceData.value = {
|
||||
total: deviceList.length,
|
||||
online: deviceList.filter(device => device.status?.code === 1).length
|
||||
}
|
||||
|
||||
// 更新设备状态卡片
|
||||
statsCards.value[3].value = String(deviceData.value.total)
|
||||
statsCards.value[3].change.value = `${((deviceData.value.online / deviceData.value.total) * 100).toFixed(1)}%`
|
||||
// 更新设备状态特性
|
||||
statsCards.value[3].features = [
|
||||
`在线: ${deviceData.value.online}台`,
|
||||
`离线: ${deviceData.value.total - deviceData.value.online}台`,
|
||||
'故障预警',
|
||||
'性能分析'
|
||||
]
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取设备列表数据失败:', error)
|
||||
}
|
||||
}
|
||||
|
||||
// 获取物种类别中文名称
|
||||
const getCategoryName = (key) => {
|
||||
const categoryNames = {
|
||||
bird: '鸟类',
|
||||
mammal: '哺乳类',
|
||||
fish: '鱼类',
|
||||
amphibian: '两栖类',
|
||||
reptile: '爬行类',
|
||||
insect: '昆虫类',
|
||||
plant: '植物'
|
||||
}
|
||||
return categoryNames[key] || key
|
||||
}
|
||||
|
||||
// 获取物种类别颜色
|
||||
const getCategoryColor = (key) => {
|
||||
const categoryColors = {
|
||||
bird: '#409EFF',
|
||||
mammal: '#67C23A',
|
||||
fish: '#E6A23C',
|
||||
amphibian: '#F56C6C',
|
||||
reptile: '#909399',
|
||||
insect: '#9B59B6',
|
||||
plant: '#2ECC71'
|
||||
}
|
||||
return categoryColors[key] || '#409EFF'
|
||||
}
|
||||
|
||||
// 更新物种分布图表
|
||||
const updateDistributionChart = (data) => {
|
||||
const chartDom = document.getElementById("distributionChart");
|
||||
if (!chartDom) return;
|
||||
|
||||
const myChart = echarts.init(chartDom);
|
||||
myChart.setOption({
|
||||
series: [{
|
||||
data: data
|
||||
}]
|
||||
})
|
||||
}
|
||||
|
||||
// 初始化数据
|
||||
const initData = async () => {
|
||||
try {
|
||||
await Promise.all([
|
||||
fetchSpeciesData(),
|
||||
fetchPatrolData(),
|
||||
fetchDeviceData()
|
||||
])
|
||||
} catch (error) {
|
||||
console.error('初始化数据失败:', error)
|
||||
}
|
||||
}
|
||||
|
||||
// 自动刷新数据
|
||||
let timer = null
|
||||
const startAutoRefresh = () => {
|
||||
fetchStatisticsData()
|
||||
timer = setInterval(fetchStatisticsData, 60000) // 每分钟更新一次
|
||||
}
|
||||
|
||||
// 初始化趋势图表
|
||||
const initTrendChart = () => {
|
||||
@ -300,78 +534,52 @@ const initDistributionChart = () => {
|
||||
});
|
||||
};
|
||||
|
||||
// 最新动态
|
||||
const activities = ref([
|
||||
{
|
||||
title: "系统更新",
|
||||
desc: "系统版本更新到 v2.0",
|
||||
time: "刚刚",
|
||||
type: "primary",
|
||||
icon: icons.Timer
|
||||
},
|
||||
{
|
||||
icon: icons.Bell,
|
||||
type: "warning",
|
||||
title: "环境预警",
|
||||
desc: "B区水质监测点位出现异常数据",
|
||||
time: "30分钟前",
|
||||
},
|
||||
{
|
||||
icon: icons.Document,
|
||||
type: "success",
|
||||
title: "日报生成",
|
||||
desc: "系统自动生成了昨日监测报告",
|
||||
time: "1小时前",
|
||||
},
|
||||
]);
|
||||
// 获取统计数据
|
||||
const fetchStatisticsData = async () => {
|
||||
try {
|
||||
const res = await getSpeciesStatistics()
|
||||
if (res.success && res.data) {
|
||||
// 更新物种总数卡片
|
||||
const totalSpecies = Object.values(res.data.categories).reduce(
|
||||
(sum, item) => sum + (parseInt(item.total_count) || 0), 0
|
||||
)
|
||||
const newSpecies = Object.values(res.data.categories).reduce(
|
||||
(sum, item) => sum + (parseInt(item.today_count) || 0), 0
|
||||
)
|
||||
statsCards.value[0].value = String(totalSpecies || 0)
|
||||
statsCards.value[0].change.value = `+${newSpecies || 0}`
|
||||
|
||||
const statsCards = ref([
|
||||
{
|
||||
title: '物种监测',
|
||||
icon: icons.Monitor,
|
||||
value: '128',
|
||||
unit: '种',
|
||||
change: { value: '+12', label: '今日新增' },
|
||||
color: '#1890FF',
|
||||
bgColor: 'linear-gradient(120deg, #0072FF 0%, #00C6FF 100%)',
|
||||
features: ['实时监测', '智能识别', '行为分析', '分布追踪']
|
||||
},
|
||||
{
|
||||
title: '环境监测',
|
||||
icon: icons.DataAnalysis,
|
||||
value: '22',
|
||||
unit: '点',
|
||||
change: { value: '2', label: '异常' },
|
||||
color: '#F5222D',
|
||||
bgColor: 'linear-gradient(120deg, #FF416C 0%, #FF4B2B 100%)',
|
||||
features: ['水质监测', '空气监测', '土壤监测', '气象监测']
|
||||
},
|
||||
{
|
||||
title: '巡护任务',
|
||||
icon: icons.Location,
|
||||
value: '8',
|
||||
unit: '个',
|
||||
change: { value: '66%', label: '完成率' },
|
||||
color: '#52C41A',
|
||||
bgColor: 'linear-gradient(120deg, #00B09B 0%, #96C93D 100%)',
|
||||
features: ['智能派单', '轨迹记录', '实时通讯', '数据采集']
|
||||
},
|
||||
{
|
||||
title: '设备状态',
|
||||
icon: icons.Connection,
|
||||
value: '32',
|
||||
unit: '台',
|
||||
change: { value: '88.9%', label: '在线率' },
|
||||
color: '#722ED1',
|
||||
bgColor: 'linear-gradient(120deg, #7F00FF 0%, #E100FF 100%)',
|
||||
features: ['状态监控', '故障预警', '维护管理', '性能分析']
|
||||
// 更新物种分布图表
|
||||
updateCategoryChart(res.data)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取统计数据失败:', error)
|
||||
}
|
||||
]);
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
initData()
|
||||
startAutoRefresh()
|
||||
initTrendChart();
|
||||
initDistributionChart();
|
||||
initCategoryChart()
|
||||
window.addEventListener('resize', () => {
|
||||
categoryChart?.resize()
|
||||
})
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
if (timer) {
|
||||
clearInterval(timer)
|
||||
}
|
||||
if (categoryChart) {
|
||||
categoryChart.dispose()
|
||||
categoryChart = null
|
||||
}
|
||||
window.removeEventListener('resize', () => {
|
||||
categoryChart?.resize()
|
||||
})
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@ -412,50 +620,16 @@ onMounted(() => {
|
||||
</div>
|
||||
|
||||
<!-- 图表区域 -->
|
||||
<el-row :gutter="20" class="mb-20">
|
||||
<el-col :span="16">
|
||||
<el-card class="chart-card" shadow="hover">
|
||||
<div id="trendChart" style="height: 400px"></div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-card class="chart-card" shadow="hover">
|
||||
<div id="distributionChart" style="height: 400px"></div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- 动态信息区域 -->
|
||||
<el-row>
|
||||
<el-col :span="24">
|
||||
<el-card class="activity-card" shadow="hover">
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span>动态信息</span>
|
||||
</div>
|
||||
</template>
|
||||
<div class="activity-list">
|
||||
<div
|
||||
v-for="(item, index) in activities"
|
||||
:key="index"
|
||||
class="activity-item"
|
||||
:class="{ 'with-border': index !== activities.length - 1 }"
|
||||
>
|
||||
<div class="activity-icon" :class="item.type">
|
||||
<el-icon>
|
||||
<component :is="item.icon" />
|
||||
</el-icon>
|
||||
</div>
|
||||
<div class="activity-content">
|
||||
<div class="activity-title">{{ item.title }}</div>
|
||||
<div class="activity-desc">{{ item.desc }}</div>
|
||||
</div>
|
||||
<div class="activity-time">{{ item.time }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<div class="charts-container">
|
||||
<div class="chart-item">
|
||||
<div class="chart-title">物种类别统计</div>
|
||||
<div ref="categoryChartRef" class="chart-content"></div>
|
||||
</div>
|
||||
<div class="chart-item">
|
||||
<div class="chart-title">趋势统计</div>
|
||||
<div id="trendChart" class="chart-content"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -616,76 +790,23 @@ onMounted(() => {
|
||||
}
|
||||
}
|
||||
|
||||
.activity-card {
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
transition: all 0.3s;
|
||||
.charts-container {
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
margin-bottom: 20px;
|
||||
|
||||
&:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
}
|
||||
.chart-item {
|
||||
flex: 1;
|
||||
|
||||
.activity-list {
|
||||
.activity-item {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
padding: 16px 0;
|
||||
|
||||
&.with-border {
|
||||
border-bottom: 1px solid #f0f2f5;
|
||||
.chart-title {
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
color: v.$text-primary;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.activity-icon {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 6px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-right: 16px;
|
||||
|
||||
&.primary {
|
||||
background: rgba(64, 158, 255, 0.1);
|
||||
color: v.$primary-color;
|
||||
}
|
||||
|
||||
&.warning {
|
||||
background: rgba(230, 162, 60, 0.1);
|
||||
color: v.$warning-color;
|
||||
}
|
||||
|
||||
&.success {
|
||||
background: rgba(103, 194, 58, 0.1);
|
||||
color: v.$success-color;
|
||||
}
|
||||
|
||||
.el-icon {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.activity-content {
|
||||
flex: 1;
|
||||
|
||||
.activity-title {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: v.$text-primary;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.activity-desc {
|
||||
font-size: 13px;
|
||||
color: v.$text-secondary;
|
||||
}
|
||||
}
|
||||
|
||||
.activity-time {
|
||||
font-size: 12px;
|
||||
color: v.$text-secondary;
|
||||
margin-left: 16px;
|
||||
.chart-content {
|
||||
height: 400px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -100,9 +100,7 @@ const statistics = ref({
|
||||
})
|
||||
|
||||
// 图表实例
|
||||
const categoryChartRef = ref(null)
|
||||
const protectionChartRef = ref(null)
|
||||
let categoryChart = null
|
||||
let protectionChart = null
|
||||
|
||||
// 基础URL
|
||||
@ -126,9 +124,6 @@ const getFullImageUrl = (url) => {
|
||||
|
||||
// 初始化图表
|
||||
const initCharts = () => {
|
||||
if (categoryChartRef.value) {
|
||||
categoryChart = echarts.init(categoryChartRef.value)
|
||||
}
|
||||
if (protectionChartRef.value) {
|
||||
protectionChart = echarts.init(protectionChartRef.value)
|
||||
}
|
||||
@ -136,75 +131,14 @@ const initCharts = () => {
|
||||
|
||||
// 更新图表数据
|
||||
const updateCharts = () => {
|
||||
// 物种类别图表数据
|
||||
const categoryData = reverseArray(
|
||||
Object.entries(statistics.value.categories)
|
||||
.filter(([_, count]) => count.total_count > 0)
|
||||
.map(([category, count]) => ({
|
||||
name: categoryOptions.find(item => item.value === category)?.label || category,
|
||||
value: count.total_count
|
||||
}))
|
||||
.sort((a, b) => a.value - b.value)
|
||||
)
|
||||
|
||||
// 保护等级图表数据
|
||||
const protectionData = reverseArray(
|
||||
Object.entries(statistics.value.protection_levels)
|
||||
.filter(([_, count]) => count > 0)
|
||||
.map(([level, count]) => ({
|
||||
name: protectionLevelOptions.find(item => item.value === level)?.label || level,
|
||||
value: count
|
||||
}))
|
||||
.sort((a, b) => a.value - b.value)
|
||||
)
|
||||
|
||||
// 设置物种类别图表
|
||||
categoryChart?.setOption({
|
||||
title: {
|
||||
text: '物种类别统计',
|
||||
left: 'center'
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'item',
|
||||
formatter: '{b}: {c}种'
|
||||
},
|
||||
legend: {
|
||||
orient: 'vertical',
|
||||
left: 'left',
|
||||
top: 'middle'
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '物种数量',
|
||||
type: 'pie',
|
||||
radius: ['40%', '70%'],
|
||||
center: ['60%', '50%'],
|
||||
avoidLabelOverlap: true,
|
||||
itemStyle: {
|
||||
borderRadius: 10,
|
||||
borderColor: '#fff',
|
||||
borderWidth: 2
|
||||
},
|
||||
label: {
|
||||
show: true,
|
||||
formatter: '{b}: {c}种'
|
||||
},
|
||||
emphasis: {
|
||||
label: {
|
||||
show: true,
|
||||
fontSize: 14,
|
||||
fontWeight: 'bold'
|
||||
},
|
||||
itemStyle: {
|
||||
shadowBlur: 10,
|
||||
shadowOffsetX: 0,
|
||||
shadowColor: 'rgba(0, 0, 0, 0.5)'
|
||||
}
|
||||
},
|
||||
data: categoryData
|
||||
}
|
||||
]
|
||||
})
|
||||
const protectionData = Object.entries(statistics.value.protection_levels)
|
||||
.filter(([_, count]) => count > 0)
|
||||
.map(([level, count]) => ({
|
||||
name: protectionLevelOptions.find(item => item.value === level)?.label || level,
|
||||
value: count
|
||||
}))
|
||||
.sort((a, b) => b.value - a.value)
|
||||
|
||||
// 设置保护等级图表
|
||||
protectionChart?.setOption({
|
||||
@ -265,7 +199,6 @@ const updateCharts = () => {
|
||||
|
||||
// 监听窗口大小变化
|
||||
const handleResize = () => {
|
||||
categoryChart?.resize()
|
||||
protectionChart?.resize()
|
||||
}
|
||||
|
||||
@ -520,7 +453,6 @@ onMounted(() => {
|
||||
|
||||
onUnmounted(() => {
|
||||
// 销毁图表实例
|
||||
categoryChart?.dispose()
|
||||
protectionChart?.dispose()
|
||||
window.removeEventListener('resize', handleResize)
|
||||
})
|
||||
@ -530,12 +462,7 @@ onUnmounted(() => {
|
||||
<div class="app-container">
|
||||
<!-- 统计信息展示 -->
|
||||
<el-row :gutter="20" class="statistics-container">
|
||||
<el-col :span="12">
|
||||
<el-card>
|
||||
<div ref="categoryChartRef" style="height: 400px"></div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-col :span="24">
|
||||
<el-card>
|
||||
<div ref="protectionChartRef" style="height: 400px"></div>
|
||||
</el-card>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user