diff --git a/src/api/monitoring.js b/src/api/monitoring.js index 3f9fa3a..cdbc004 100644 --- a/src/api/monitoring.js +++ b/src/api/monitoring.js @@ -3,35 +3,40 @@ import request from '@/utils/request' /** * 按时间范围查询监测数据 * @param {Object} params - 查询参数 - * @param {string} params.start_date - 开始时间 - * @param {string} params.end_date - 结束时间 - * @param {Array} params.indicator_ids - 指标ID数组 - * @param {number} params.point_id - 监测点ID - * @param {number} params.device_id - 设备ID + * @param {string} [params.start_date] - 开始时间,格式:YYYY-MM-DD + * @param {string} [params.end_date] - 结束时间,格式:YYYY-MM-DD + * @param {string} [params.indicator_ids] - 指标ID,多个用逗号分隔 + * @param {string} [params.point_id] - 监测点ID + * @param {string} [params.device_id] - 设备ID */ export function getMonitoringData(params) { - return request.get('/api/monitoring/data', { params }) + return request.get('/api/monitoring/dataQuery', { params }) } /** * 获取最新数据 * @param {Object} params - 查询参数 - * @param {number} params.point_id - 监测点ID - * @param {Array} params.indicator_ids - 指标ID数组 - * @param {number} params.device_id - 设备ID + * @param {string} [params.point_id] - 监测点ID + * @param {string} [params.indicator_ids] - 指标ID,多个用逗号分隔 + * @param {string} [params.device_id] - 设备ID */ export function getLatestData(params) { - return request.get('/api/monitoring/data/latest', { params }) + // 确保 indicator_ids 是字符串格式 + const formattedParams = { + ...params, + indicator_ids: Array.isArray(params.indicator_ids) ? params.indicator_ids.join(',') : params.indicator_ids + } + return request.get('/api/monitoring/data/latest', { params: formattedParams }) } /** * 获取统计数据 * @param {Object} params - 查询参数 - * @param {string} params.start_date - 开始时间 - * @param {string} params.end_date - 结束时间 - * @param {number} params.indicator_id - 指标ID - * @param {number} params.point_id - 监测点ID - * @param {number} params.device_id - 设备ID + * @param {string} [params.start_date] - 开始时间,格式:YYYY-MM-DD + * @param {string} [params.end_date] - 结束时间,格式:YYYY-MM-DD + * @param {string} [params.indicator_id] - 指标ID + * @param {string} [params.point_id] - 监测点ID + * @param {string} [params.device_id] - 设备ID */ export function getStatistics(params) { return request.get('/api/monitoring/data/statistics', { params }) @@ -40,9 +45,9 @@ export function getStatistics(params) { /** * 获取数据质量统计 * @param {Object} params - 查询参数 - * @param {string} params.start_date - 开始时间 - * @param {string} params.end_date - 结束时间 - * @param {number} params.point_id - 监测点ID + * @param {string} [params.start_date] - 开始时间,格式:YYYY-MM-DD + * @param {string} [params.end_date] - 结束时间,格式:YYYY-MM-DD + * @param {string} [params.point_id] - 监测点ID */ export function getQualityStatistics(params) { return request.get('/api/monitoring/data/quality-statistics', { params }) diff --git a/src/views/dashboard/screen/components/CenterPanel/DataChart.vue b/src/views/dashboard/screen/components/CenterPanel/DataChart.vue index 2b52468..50ccb57 100644 --- a/src/views/dashboard/screen/components/CenterPanel/DataChart.vue +++ b/src/views/dashboard/screen/components/CenterPanel/DataChart.vue @@ -13,19 +13,109 @@ const fetchSpeciesData = async () => { try { const res = await request.get('/api/admin/species/statistics/overview') if (res.success && res.data) { - updateSpeciesChart(res.data) + // 中文名称映射 + const categoryNames = { + bird: '鸟类', + mammal: '哺乳类', + fish: '鱼类', + amphibian: '两栖类', + reptile: '爬行类', + insect: '昆虫类', + plant: '植物' + } + + // 将数据转换为饼图所需格式 + const chartData = Object.entries(res.data.categories).map(([key, value]) => ({ + name: categoryNames[key], + value: parseInt(value.total_count), + itemStyle: { + color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ + { offset: 0, color: getSpeciesColor(key)[0] }, + { offset: 1, color: getSpeciesColor(key)[1] } + ]) + } + })).sort((a, b) => b.value - a.value) // 按数量从大到小排序 + + speciesChart.setOption({ + series: [{ + data: chartData + }] + }) } } catch (error) { console.error('获取物种统计数据失败:', error) } } +// 获取物种类别的颜色 +const getSpeciesColor = (category) => { + const colors = { + bird: ['#36CFFF', '#2861F5'], + mammal: ['#FF36D9', '#C92AF5'], + fish: ['#FFB72C', '#F5612A'], + amphibian: ['#4EF568', '#2AB256'], + reptile: ['#36FFB0', '#2AF5A1'], + insect: ['#7636FF', '#2A3CF5'], + plant: ['#FF7636', '#F52A2A'] + } + return colors[category] || ['#36CFFF', '#2861F5'] +} + // 获取巡护统计数据 const fetchPatrolData = async () => { try { const res = await request.get('/api/admin/patrol/records/statistics/overview') if (res.success && res.data) { - updatePatrolChart(res.data) + const { overview } = res.data + const chartData = [ + { + name: '已完成', + value: Number(overview.completed_count) || 0, + itemStyle: { + color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ + { offset: 0, color: '#67C23A' }, + { offset: 1, color: '#67C23A99' } + ]) + } + }, + { + name: '进行中', + value: Number(overview.in_progress_count) || 0, + itemStyle: { + color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ + { offset: 0, color: '#409EFF' }, + { offset: 1, color: '#409EFF99' } + ]) + } + }, + { + name: '总任务', + value: Number(overview.total_count) || 0, + itemStyle: { + color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ + { offset: 0, color: '#909399' }, + { offset: 1, color: '#90939999' } + ]) + } + } + ] + + patrolChart.setOption({ + xAxis: { + data: chartData.map(item => item.name) + }, + series: [{ + data: chartData, + label: { + show: true, + position: 'top', + color: '#fff', + formatter: function(params) { + return Math.floor(params.value) + } + } + }] + }) } } catch (error) { console.error('获取巡护统计数据失败:', error) @@ -39,54 +129,50 @@ const initSpeciesChart = () => { 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}%)' + formatter: function(params) { + const percent = parseInt(params.percent) + return `${params.name}: ${parseInt(params.value)}种 (${percent}%)` + } }, legend: { - orient: 'vertical', - right: '5%', - top: 'middle', - textStyle: { - color: '#fff' - } + show: false }, series: [ { + name: '物种分布', type: 'pie', radius: ['40%', '70%'], - center: ['40%', '55%'], + center: ['50%', '50%'], avoidLabelOverlap: true, itemStyle: { borderRadius: 10, - borderColor: '#fff', + borderColor: 'rgba(0, 0, 0, 0.2)', borderWidth: 2 }, label: { show: true, + position: 'outside', + formatter: function(params) { + return `${params.name}\n${parseInt(params.value)}种` + }, color: '#fff', - formatter: '{b}\n{c}种' + fontSize: 12 }, - emphasis: { - label: { - show: true, - fontSize: 16, - fontWeight: 'bold' + labelLine: { + length: 15, + length2: 0, + maxSurfaceAngle: 80, + lineStyle: { + color: 'rgba(255, 255, 255, 0.3)' } }, data: [] } ] } + speciesChart.setOption(option) } @@ -97,19 +183,13 @@ const initPatrolChart = () => { 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' + }, + formatter: function(params) { + return `${params[0].name}: ${Math.floor(params[0].value)}` } }, grid: { @@ -121,7 +201,7 @@ const initPatrolChart = () => { }, xAxis: { type: 'category', - data: ['已完成', '进行中', '未开始', '已超时'], + data: ['已完成', '进行中', '总任务'], axisLine: { lineStyle: { color: 'rgba(255, 255, 255, 0.3)' @@ -134,6 +214,9 @@ const initPatrolChart = () => { }, yAxis: { type: 'value', + minInterval: 1, + splitNumber: 4, + min: 0, splitLine: { lineStyle: { color: 'rgba(255, 255, 255, 0.1)', @@ -141,7 +224,10 @@ const initPatrolChart = () => { } }, axisLabel: { - color: '#fff' + color: '#fff', + formatter: function(value) { + return Math.floor(value) + } } }, series: [ @@ -154,7 +240,10 @@ const initPatrolChart = () => { label: { show: true, position: 'top', - color: '#fff' + color: '#fff', + formatter: function(params) { + return parseInt(params.value || 0) + '个' + } }, data: [] } @@ -163,62 +252,6 @@ const initPatrolChart = () => { 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 = () => { @@ -295,10 +328,10 @@ onUnmounted(() => { border-radius: 4px; padding: 12px; - .chart-title { - font-size: 16px; + .chart-title { + font-size: 16px; font-weight: bold; - color: #fff; + color: #fff; text-align: center; margin-bottom: 12px; background: linear-gradient(to bottom, #ffffff, #3fa7dd); diff --git a/src/views/dashboard/screen/components/LeftPanel/MiddleCard.vue b/src/views/dashboard/screen/components/LeftPanel/MiddleCard.vue index 58537c6..d247829 100644 --- a/src/views/dashboard/screen/components/LeftPanel/MiddleCard.vue +++ b/src/views/dashboard/screen/components/LeftPanel/MiddleCard.vue @@ -1,17 +1,11 @@ @@ -307,36 +179,67 @@ onUnmounted(() => { .middle-card { height: 100%; padding: 16px; - background: rgba(0, 0, 0, 0.2); - border-radius: 8px; + box-sizing: border-box; + background: rgba(6, 30, 93, 0.5); + border-radius: 4px; - .card-title { - font-size: 16px; - font-weight: 500; - color: #fff; - margin-bottom: 16px; + .card-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 20px; + + .title { + font-size: 18px; + font-weight: bold; + background: linear-gradient(to bottom, #ffffff, #3fa7dd); + -webkit-background-clip: text; + color: transparent; + letter-spacing: 2px; + } + + .update-time { + font-size: 14px; + color: #3fa7dd; + opacity: 0.8; + position: relative; + padding-left: 20px; + + &::before { + content: ''; + position: absolute; + left: 0; + top: 50%; + transform: translateY(-50%); + width: 8px; + height: 8px; + background: #67C23A; + border-radius: 50%; + animation: blink 1s infinite; + } + } } .card-content { - height: calc(100% - 32px); - display: flex; - flex-direction: column; - gap: 16px; + height: calc(100% - 60px); - .indicator-chart { - flex: 1; - min-height: 180px; - } + .chart-item { + height: 100%; + background: rgba(0, 0, 0, 0.2); + border-radius: 4px; + padding: 12px; - .trend-chart { - flex: 2; - min-height: 200px; - } - - .quality-chart { - flex: 1; - min-height: 180px; + .chart { + height: 100%; + width: 100%; + } } } } + +@keyframes blink { + 0% { opacity: 0.2; } + 50% { opacity: 1; } + 100% { opacity: 0.2; } +} \ No newline at end of file