完善首页的页面
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>
|
<script setup>
|
||||||
import { ref, onMounted } from "vue";
|
import { ref, onMounted, onUnmounted } from "vue";
|
||||||
import * as echarts from "echarts";
|
import * as echarts from "echarts";
|
||||||
import { markRaw } from 'vue'
|
import { markRaw } from 'vue'
|
||||||
import {
|
import {
|
||||||
@ -7,9 +7,9 @@ import {
|
|||||||
DataAnalysis,
|
DataAnalysis,
|
||||||
Location,
|
Location,
|
||||||
Document,
|
Document,
|
||||||
Timer,
|
Warning
|
||||||
Bell
|
|
||||||
} from "@element-plus/icons-vue";
|
} from "@element-plus/icons-vue";
|
||||||
|
import { getSpeciesStatistics, getPatrolStatistics, getDeviceList } from '@/api/dashboard'
|
||||||
|
|
||||||
// 使用 markRaw 包装图标组件
|
// 使用 markRaw 包装图标组件
|
||||||
const icons = {
|
const icons = {
|
||||||
@ -17,41 +17,275 @@ const icons = {
|
|||||||
DataAnalysis: markRaw(DataAnalysis),
|
DataAnalysis: markRaw(DataAnalysis),
|
||||||
Location: markRaw(Location),
|
Location: markRaw(Location),
|
||||||
Document: markRaw(Document),
|
Document: markRaw(Document),
|
||||||
Timer: markRaw(Timer),
|
Warning: markRaw(Warning)
|
||||||
Bell: markRaw(Bell)
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// 统计数据
|
// 物种类别选项
|
||||||
const statistics = ref({
|
const categoryOptions = [
|
||||||
species: {
|
{ label: '鸟类', value: 'bird' },
|
||||||
total: 128,
|
{ label: '哺乳类', value: 'mammal' },
|
||||||
today: 12,
|
{ label: '鱼类', value: 'fish' },
|
||||||
trend: "+8%",
|
{ label: '两栖类', value: 'amphibian' },
|
||||||
type: "success",
|
{ label: '爬行类', value: 'reptile' },
|
||||||
icon: "Histogram",
|
{ 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,
|
title: '环境监测',
|
||||||
abnormal: 2,
|
icon: icons.DataAnalysis,
|
||||||
trend: "normal",
|
value: '2',
|
||||||
type: "warning",
|
unit: '点',
|
||||||
icon: "Monitor",
|
change: { value: '2', label: '异常' },
|
||||||
|
color: '#F5222D',
|
||||||
|
bgColor: 'linear-gradient(120deg, #FF416C 0%, #FF4B2B 100%)',
|
||||||
|
features: ['水质监测', '空气监测', '土壤监测', '气象监测']
|
||||||
},
|
},
|
||||||
patrol: {
|
{
|
||||||
total: 12,
|
title: '巡护任务',
|
||||||
completed: 8,
|
icon: icons.Location,
|
||||||
progress: "66%",
|
value: '0',
|
||||||
type: "primary",
|
unit: '个',
|
||||||
icon: "Location",
|
change: { value: '0%', label: '完成率' },
|
||||||
|
color: '#52C41A',
|
||||||
|
bgColor: 'linear-gradient(120deg, #00B09B 0%, #96C93D 100%)',
|
||||||
|
features: ['智能派单', '轨迹记录', '实时通讯', '数据采集']
|
||||||
},
|
},
|
||||||
devices: {
|
{
|
||||||
total: 36,
|
title: '设备状态',
|
||||||
online: 32,
|
icon: icons.Monitor,
|
||||||
rate: "88.9%",
|
value: '0',
|
||||||
type: "info",
|
unit: '台',
|
||||||
icon: "Connection",
|
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 = () => {
|
const initTrendChart = () => {
|
||||||
@ -300,78 +534,52 @@ const initDistributionChart = () => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// 最新动态
|
// 获取统计数据
|
||||||
const activities = ref([
|
const fetchStatisticsData = async () => {
|
||||||
{
|
try {
|
||||||
title: "系统更新",
|
const res = await getSpeciesStatistics()
|
||||||
desc: "系统版本更新到 v2.0",
|
if (res.success && res.data) {
|
||||||
time: "刚刚",
|
// 更新物种总数卡片
|
||||||
type: "primary",
|
const totalSpecies = Object.values(res.data.categories).reduce(
|
||||||
icon: icons.Timer
|
(sum, item) => sum + (parseInt(item.total_count) || 0), 0
|
||||||
},
|
)
|
||||||
{
|
const newSpecies = Object.values(res.data.categories).reduce(
|
||||||
icon: icons.Bell,
|
(sum, item) => sum + (parseInt(item.today_count) || 0), 0
|
||||||
type: "warning",
|
)
|
||||||
title: "环境预警",
|
statsCards.value[0].value = String(totalSpecies || 0)
|
||||||
desc: "B区水质监测点位出现异常数据",
|
statsCards.value[0].change.value = `+${newSpecies || 0}`
|
||||||
time: "30分钟前",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
icon: icons.Document,
|
|
||||||
type: "success",
|
|
||||||
title: "日报生成",
|
|
||||||
desc: "系统自动生成了昨日监测报告",
|
|
||||||
time: "1小时前",
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
|
|
||||||
const statsCards = ref([
|
// 更新物种分布图表
|
||||||
{
|
updateCategoryChart(res.data)
|
||||||
title: '物种监测',
|
}
|
||||||
icon: icons.Monitor,
|
} catch (error) {
|
||||||
value: '128',
|
console.error('获取统计数据失败:', error)
|
||||||
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: ['状态监控', '故障预警', '维护管理', '性能分析']
|
|
||||||
}
|
}
|
||||||
]);
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
|
initData()
|
||||||
|
startAutoRefresh()
|
||||||
initTrendChart();
|
initTrendChart();
|
||||||
initDistributionChart();
|
initDistributionChart();
|
||||||
|
initCategoryChart()
|
||||||
|
window.addEventListener('resize', () => {
|
||||||
|
categoryChart?.resize()
|
||||||
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
if (timer) {
|
||||||
|
clearInterval(timer)
|
||||||
|
}
|
||||||
|
if (categoryChart) {
|
||||||
|
categoryChart.dispose()
|
||||||
|
categoryChart = null
|
||||||
|
}
|
||||||
|
window.removeEventListener('resize', () => {
|
||||||
|
categoryChart?.resize()
|
||||||
|
})
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@ -412,50 +620,16 @@ onMounted(() => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 图表区域 -->
|
<!-- 图表区域 -->
|
||||||
<el-row :gutter="20" class="mb-20">
|
<div class="charts-container">
|
||||||
<el-col :span="16">
|
<div class="chart-item">
|
||||||
<el-card class="chart-card" shadow="hover">
|
<div class="chart-title">物种类别统计</div>
|
||||||
<div id="trendChart" style="height: 400px"></div>
|
<div ref="categoryChartRef" class="chart-content"></div>
|
||||||
</el-card>
|
</div>
|
||||||
</el-col>
|
<div class="chart-item">
|
||||||
<el-col :span="8">
|
<div class="chart-title">趋势统计</div>
|
||||||
<el-card class="chart-card" shadow="hover">
|
<div id="trendChart" class="chart-content"></div>
|
||||||
<div id="distributionChart" style="height: 400px"></div>
|
</div>
|
||||||
</el-card>
|
</div>
|
||||||
</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>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -616,76 +790,23 @@ onMounted(() => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.activity-card {
|
.charts-container {
|
||||||
border: none;
|
display: flex;
|
||||||
border-radius: 8px;
|
gap: 20px;
|
||||||
transition: all 0.3s;
|
margin-bottom: 20px;
|
||||||
|
|
||||||
&:hover {
|
.chart-item {
|
||||||
transform: translateY(-2px);
|
flex: 1;
|
||||||
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.08);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.activity-list {
|
.chart-title {
|
||||||
.activity-item {
|
font-size: 16px;
|
||||||
display: flex;
|
font-weight: 500;
|
||||||
align-items: flex-start;
|
color: v.$text-primary;
|
||||||
padding: 16px 0;
|
margin-bottom: 8px;
|
||||||
|
|
||||||
&.with-border {
|
|
||||||
border-bottom: 1px solid #f0f2f5;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.activity-icon {
|
.chart-content {
|
||||||
width: 32px;
|
height: 400px;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -100,9 +100,7 @@ const statistics = ref({
|
|||||||
})
|
})
|
||||||
|
|
||||||
// 图表实例
|
// 图表实例
|
||||||
const categoryChartRef = ref(null)
|
|
||||||
const protectionChartRef = ref(null)
|
const protectionChartRef = ref(null)
|
||||||
let categoryChart = null
|
|
||||||
let protectionChart = null
|
let protectionChart = null
|
||||||
|
|
||||||
// 基础URL
|
// 基础URL
|
||||||
@ -126,9 +124,6 @@ const getFullImageUrl = (url) => {
|
|||||||
|
|
||||||
// 初始化图表
|
// 初始化图表
|
||||||
const initCharts = () => {
|
const initCharts = () => {
|
||||||
if (categoryChartRef.value) {
|
|
||||||
categoryChart = echarts.init(categoryChartRef.value)
|
|
||||||
}
|
|
||||||
if (protectionChartRef.value) {
|
if (protectionChartRef.value) {
|
||||||
protectionChart = echarts.init(protectionChartRef.value)
|
protectionChart = echarts.init(protectionChartRef.value)
|
||||||
}
|
}
|
||||||
@ -136,75 +131,14 @@ const initCharts = () => {
|
|||||||
|
|
||||||
// 更新图表数据
|
// 更新图表数据
|
||||||
const updateCharts = () => {
|
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(
|
const protectionData = Object.entries(statistics.value.protection_levels)
|
||||||
Object.entries(statistics.value.protection_levels)
|
.filter(([_, count]) => count > 0)
|
||||||
.filter(([_, count]) => count > 0)
|
.map(([level, count]) => ({
|
||||||
.map(([level, count]) => ({
|
name: protectionLevelOptions.find(item => item.value === level)?.label || level,
|
||||||
name: protectionLevelOptions.find(item => item.value === level)?.label || level,
|
value: count
|
||||||
value: count
|
}))
|
||||||
}))
|
.sort((a, b) => b.value - a.value)
|
||||||
.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
|
|
||||||
}
|
|
||||||
]
|
|
||||||
})
|
|
||||||
|
|
||||||
// 设置保护等级图表
|
// 设置保护等级图表
|
||||||
protectionChart?.setOption({
|
protectionChart?.setOption({
|
||||||
@ -265,7 +199,6 @@ const updateCharts = () => {
|
|||||||
|
|
||||||
// 监听窗口大小变化
|
// 监听窗口大小变化
|
||||||
const handleResize = () => {
|
const handleResize = () => {
|
||||||
categoryChart?.resize()
|
|
||||||
protectionChart?.resize()
|
protectionChart?.resize()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -520,7 +453,6 @@ onMounted(() => {
|
|||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
// 销毁图表实例
|
// 销毁图表实例
|
||||||
categoryChart?.dispose()
|
|
||||||
protectionChart?.dispose()
|
protectionChart?.dispose()
|
||||||
window.removeEventListener('resize', handleResize)
|
window.removeEventListener('resize', handleResize)
|
||||||
})
|
})
|
||||||
@ -530,12 +462,7 @@ onUnmounted(() => {
|
|||||||
<div class="app-container">
|
<div class="app-container">
|
||||||
<!-- 统计信息展示 -->
|
<!-- 统计信息展示 -->
|
||||||
<el-row :gutter="20" class="statistics-container">
|
<el-row :gutter="20" class="statistics-container">
|
||||||
<el-col :span="12">
|
<el-col :span="24">
|
||||||
<el-card>
|
|
||||||
<div ref="categoryChartRef" style="height: 400px"></div>
|
|
||||||
</el-card>
|
|
||||||
</el-col>
|
|
||||||
<el-col :span="12">
|
|
||||||
<el-card>
|
<el-card>
|
||||||
<div ref="protectionChartRef" style="height: 400px"></div>
|
<div ref="protectionChartRef" style="height: 400px"></div>
|
||||||
</el-card>
|
</el-card>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user