318 lines
6.6 KiB
Vue

<script setup>
import { ref, onMounted, onUnmounted } from 'vue'
import * as echarts from 'echarts'
import request from '@/utils/request'
let speciesChart = null
let patrolChart = null
const speciesChartRef = ref(null)
const patrolChartRef = ref(null)
// 获取物种统计数据
const fetchSpeciesData = async () => {
try {
const res = await request.get('/api/admin/species/statistics/overview')
if (res.success && res.data) {
updateSpeciesChart(res.data)
}
} catch (error) {
console.error('获取物种统计数据失败:', error)
}
}
// 获取巡护统计数据
const fetchPatrolData = async () => {
try {
const res = await request.get('/api/admin/patrol/records/statistics/overview')
if (res.success && res.data) {
updatePatrolChart(res.data)
}
} catch (error) {
console.error('获取巡护统计数据失败:', error)
}
}
// 初始化物种统计图表
const initSpeciesChart = () => {
if (!speciesChartRef.value) return
speciesChart = echarts.init(speciesChartRef.value)
const option = {
backgroundColor: 'transparent',
title: {
text: '物种分布统计',
textStyle: {
color: '#fff',
fontSize: 16
},
left: 'center',
top: 0
},
tooltip: {
trigger: 'item',
formatter: '{b}: {c} ({d}%)'
},
legend: {
orient: 'vertical',
right: '5%',
top: 'middle',
textStyle: {
color: '#fff'
}
},
series: [
{
type: 'pie',
radius: ['40%', '70%'],
center: ['40%', '55%'],
avoidLabelOverlap: true,
itemStyle: {
borderRadius: 10,
borderColor: '#fff',
borderWidth: 2
},
label: {
show: true,
color: '#fff',
formatter: '{b}\n{c}种'
},
emphasis: {
label: {
show: true,
fontSize: 16,
fontWeight: 'bold'
}
},
data: []
}
]
}
speciesChart.setOption(option)
}
// 初始化巡护统计图表
const initPatrolChart = () => {
if (!patrolChartRef.value) return
patrolChart = echarts.init(patrolChartRef.value)
const option = {
backgroundColor: 'transparent',
title: {
text: '巡护任务统计',
textStyle: {
color: '#fff',
fontSize: 16
},
left: 'center',
top: 0
},
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow'
}
},
grid: {
top: '15%',
right: '5%',
bottom: '5%',
left: '15%',
containLabel: true
},
xAxis: {
type: 'category',
data: ['已完成', '进行中', '未开始', '已超时'],
axisLine: {
lineStyle: {
color: 'rgba(255, 255, 255, 0.3)'
}
},
axisLabel: {
color: '#fff',
interval: 0
}
},
yAxis: {
type: 'value',
splitLine: {
lineStyle: {
color: 'rgba(255, 255, 255, 0.1)',
type: 'dashed'
}
},
axisLabel: {
color: '#fff'
}
},
series: [
{
type: 'bar',
barWidth: '40%',
itemStyle: {
borderRadius: [4, 4, 0, 0]
},
label: {
show: true,
position: 'top',
color: '#fff'
},
data: []
}
]
}
patrolChart.setOption(option)
}
// 更新物种统计图表
const updateSpeciesChart = (data) => {
const colors = {
'鸟类': '#36CFFF',
'鱼类': '#FFB72C',
'两栖类': '#4EF568',
'爬行类': '#FF36D9',
'哺乳类': '#9E87FF'
}
const chartData = Object.entries(data.category_counts || {}).map(([name, value]) => ({
name,
value,
itemStyle: {
color: colors[name] || '#36CFFF'
}
}))
speciesChart.setOption({
series: [{
data: chartData
}]
})
}
// 更新巡护统计图表
const updatePatrolChart = (data) => {
const colors = {
'已完成': '#67C23A',
'进行中': '#409EFF',
'未开始': '#909399',
'已超时': '#F56C6C'
}
const chartData = [
{ name: '已完成', value: data.completed || 0 },
{ name: '进行中', value: data.in_progress || 0 },
{ name: '未开始', value: data.not_started || 0 },
{ name: '已超时', value: data.overdue || 0 }
].map(item => ({
value: item.value,
itemStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: colors[item.name] },
{ offset: 1, color: colors[item.name].replace('FF', '99') }
])
}
}))
patrolChart.setOption({
series: [{
data: chartData
}]
})
}
// 自动刷新数据
let timer = null
const startAutoRefresh = () => {
fetchSpeciesData()
fetchPatrolData()
timer = setInterval(() => {
fetchSpeciesData()
fetchPatrolData()
}, 60000) // 每分钟更新一次
}
// 处理窗口大小变化
const handleResize = () => {
speciesChart?.resize()
patrolChart?.resize()
}
onMounted(() => {
initSpeciesChart()
initPatrolChart()
startAutoRefresh()
window.addEventListener('resize', handleResize)
})
onUnmounted(() => {
if (timer) {
clearInterval(timer)
}
if (speciesChart) {
speciesChart.dispose()
speciesChart = null
}
if (patrolChart) {
patrolChart.dispose()
patrolChart = null
}
window.removeEventListener('resize', handleResize)
})
</script>
<template>
<div class="data-chart">
<div class="chart-container">
<div class="chart-item">
<div class="chart-title">物种分布统计</div>
<div ref="speciesChartRef" class="species-chart"></div>
</div>
<div class="chart-item">
<div class="chart-title">巡护任务统计</div>
<div ref="patrolChartRef" class="patrol-chart"></div>
</div>
</div>
</div>
</template>
<style lang="scss" scoped>
.data-chart {
height: 100%;
padding: 16px;
box-sizing: border-box;
background: rgba(6, 30, 93, 0.5);
border-radius: 4px;
.chart-container {
height: 100%;
display: flex;
gap: 16px;
.chart-item {
flex: 1;
display: flex;
flex-direction: column;
background: rgba(0, 0, 0, 0.2);
border-radius: 4px;
padding: 12px;
.chart-title {
font-size: 16px;
font-weight: bold;
color: #fff;
text-align: center;
margin-bottom: 12px;
background: linear-gradient(to bottom, #ffffff, #3fa7dd);
-webkit-background-clip: text;
color: transparent;
letter-spacing: 2px;
}
.species-chart,
.patrol-chart {
flex: 1;
min-height: 0;
}
}
}
}
</style>