2026-06-18 09:29:15 +08:00
|
|
|
<template>
|
|
|
|
|
<SidePanelItem
|
|
|
|
|
title="鱼类变化情况"
|
|
|
|
|
:datetimePicker="datetimePicker"
|
|
|
|
|
@update-values="handlePanelChange"
|
|
|
|
|
>
|
2026-07-07 13:50:21 +08:00
|
|
|
<a-spin :spinning="loading" style="width: 100%">
|
|
|
|
|
<div
|
|
|
|
|
ref="chartContainer"
|
|
|
|
|
class="chart-container"
|
|
|
|
|
v-show="sourceData.length > 0"
|
|
|
|
|
></div>
|
|
|
|
|
<a-empty
|
|
|
|
|
v-show="sourceData.length === 0"
|
|
|
|
|
description="暂无数据"
|
|
|
|
|
class="empty-container"
|
|
|
|
|
/>
|
2026-06-18 09:29:15 +08:00
|
|
|
</a-spin>
|
|
|
|
|
</SidePanelItem>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<script lang="ts" setup>
|
|
|
|
|
import { ref, computed, watch, onMounted, onUnmounted, nextTick } from 'vue';
|
|
|
|
|
import * as echarts from 'echarts';
|
2026-06-24 11:26:19 +08:00
|
|
|
import type { ECharts } from 'echarts';
|
2026-06-18 09:29:15 +08:00
|
|
|
import type { EChartsOption } from 'echarts';
|
|
|
|
|
import SidePanelItem from '@/components/SidePanelItem/index.vue';
|
|
|
|
|
import { sdFprdRGetFtpChangeInfo } from '@/api/yldc';
|
2026-06-24 11:26:19 +08:00
|
|
|
import { useJidiSelectEventStore } from '@/store/modules/jidiSelectEvent';
|
2026-06-18 09:29:15 +08:00
|
|
|
defineOptions({
|
|
|
|
|
name: 'FishChangeTu'
|
|
|
|
|
});
|
|
|
|
|
const JidiSelectEventStore = useJidiSelectEventStore();
|
|
|
|
|
// const BASE_ID = '07';
|
|
|
|
|
|
|
|
|
|
// 日期选择器配置
|
|
|
|
|
const datetimePicker = ref({
|
|
|
|
|
show: true,
|
|
|
|
|
value: `${new Date().getFullYear()}`,
|
|
|
|
|
format: 'YYYY',
|
|
|
|
|
picker: 'year' as const,
|
|
|
|
|
options: []
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 数据状态
|
|
|
|
|
const sourceData = ref<
|
|
|
|
|
Array<{
|
|
|
|
|
year: number;
|
|
|
|
|
month: number;
|
|
|
|
|
ftpCount: number;
|
|
|
|
|
fishCount: number;
|
|
|
|
|
}>
|
|
|
|
|
>([]);
|
|
|
|
|
const loading = ref(false);
|
|
|
|
|
|
|
|
|
|
// 图表实例
|
|
|
|
|
const chartContainer = ref<HTMLDivElement | null>(null);
|
2026-06-24 11:26:19 +08:00
|
|
|
let chartInstance: ECharts | null = null;
|
2026-06-18 09:29:15 +08:00
|
|
|
|
|
|
|
|
// 生成从1月到当前月份的月份列表
|
|
|
|
|
function getMonthsUntilNow(year: number) {
|
|
|
|
|
const now = new Date();
|
|
|
|
|
const currentYear = now.getFullYear();
|
|
|
|
|
const currentMonth = now.getMonth() + 1;
|
|
|
|
|
const months: Array<{ name: string; value: number }> = [];
|
|
|
|
|
|
|
|
|
|
const maxMonth = year >= currentYear ? currentMonth : 12;
|
|
|
|
|
for (let i = 1; i <= maxMonth; i++) {
|
|
|
|
|
months.push({
|
|
|
|
|
name: i + '月',
|
|
|
|
|
value: i
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return months;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 处理图表数据
|
|
|
|
|
const fishData = computed(() => {
|
|
|
|
|
const dates = getMonthsUntilNow(parseInt(datetimePicker.value.value));
|
|
|
|
|
if (sourceData.value.length > 0) {
|
|
|
|
|
const speciesCount = dates.map(item => {
|
|
|
|
|
const info = sourceData.value.find(v => v.month === item.value);
|
|
|
|
|
return info ? Number(info.ftpCount) : 0;
|
|
|
|
|
});
|
|
|
|
|
const fishCount = dates.map(item => {
|
|
|
|
|
const info = sourceData.value.find(v => v.month === item.value);
|
|
|
|
|
return info ? Number(info.fishCount) : 0;
|
|
|
|
|
});
|
|
|
|
|
return {
|
|
|
|
|
dates: dates.map(item => item.name),
|
|
|
|
|
speciesCount,
|
|
|
|
|
fishCount
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
return {
|
|
|
|
|
dates: dates.map(item => item.name),
|
|
|
|
|
speciesCount: [],
|
|
|
|
|
fishCount: []
|
|
|
|
|
};
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 图表配置
|
|
|
|
|
const option = computed<EChartsOption>(() => {
|
|
|
|
|
const data = fishData.value;
|
|
|
|
|
const baseFontSize = 12;
|
|
|
|
|
|
|
|
|
|
// 动态计算Y轴范围
|
|
|
|
|
const speciesMax = Math.max(...data.speciesCount, 1);
|
|
|
|
|
const fishMax = Math.max(...data.fishCount, 1);
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
yAxisSplit: 5,
|
|
|
|
|
tooltip: {
|
|
|
|
|
trigger: 'axis',
|
|
|
|
|
axisPointer: { type: 'cross' },
|
|
|
|
|
backgroundColor: 'rgba(50,50,50,0.9)',
|
|
|
|
|
textStyle: { fontSize: baseFontSize, color: '#fff' }
|
|
|
|
|
},
|
|
|
|
|
legend: {
|
|
|
|
|
data: ['种类', '总数'],
|
|
|
|
|
top: 0,
|
|
|
|
|
textStyle: { fontSize: baseFontSize }
|
|
|
|
|
},
|
|
|
|
|
grid: {
|
2026-06-24 11:26:19 +08:00
|
|
|
left: 50,
|
|
|
|
|
right: 60,
|
|
|
|
|
top: 45,
|
|
|
|
|
bottom: 40
|
2026-06-18 09:29:15 +08:00
|
|
|
},
|
|
|
|
|
xAxis: {
|
|
|
|
|
type: 'category',
|
|
|
|
|
data: data.dates,
|
|
|
|
|
axisLine: { lineStyle: { color: '#ccc' } },
|
|
|
|
|
splitLine: { show: true },
|
|
|
|
|
axisLabel: {
|
|
|
|
|
color: '#666',
|
|
|
|
|
fontSize: baseFontSize,
|
|
|
|
|
rotate: 0,
|
|
|
|
|
interval: 0
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
yAxis: [
|
|
|
|
|
{
|
|
|
|
|
type: 'value',
|
|
|
|
|
name: '种类',
|
|
|
|
|
nameTextStyle: { fontSize: baseFontSize },
|
|
|
|
|
min: 0,
|
|
|
|
|
max: Math.ceil(speciesMax * 1.2),
|
|
|
|
|
axisLine: { lineStyle: { color: '#165DFF' } },
|
|
|
|
|
axisLabel: {
|
|
|
|
|
color: '#666',
|
|
|
|
|
fontSize: baseFontSize,
|
|
|
|
|
formatter: (value: number) => Math.floor(value).toString()
|
|
|
|
|
},
|
|
|
|
|
splitLine: { lineStyle: { color: '#eee' } }
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
type: 'value',
|
|
|
|
|
name: '总数',
|
|
|
|
|
nameTextStyle: { fontSize: baseFontSize },
|
|
|
|
|
min: 0,
|
|
|
|
|
max: Math.ceil(fishMax * 1.2),
|
|
|
|
|
axisLine: { lineStyle: { color: '#36CFC9' } },
|
|
|
|
|
axisLabel: {
|
|
|
|
|
color: '#666',
|
|
|
|
|
fontSize: baseFontSize,
|
|
|
|
|
formatter: (value: number) => Math.floor(value).toString()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
],
|
|
|
|
|
series: [
|
|
|
|
|
{
|
|
|
|
|
name: '种类',
|
|
|
|
|
type: 'line',
|
|
|
|
|
data: data.speciesCount,
|
|
|
|
|
yAxisIndex: 0,
|
|
|
|
|
color: '#165DFF',
|
|
|
|
|
smooth: true,
|
|
|
|
|
symbol: 'circle',
|
|
|
|
|
symbolSize: 8,
|
|
|
|
|
label: {
|
|
|
|
|
show: true,
|
|
|
|
|
position: 'top',
|
|
|
|
|
fontSize: baseFontSize - 2
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: '总数',
|
|
|
|
|
type: 'line',
|
|
|
|
|
data: data.fishCount,
|
|
|
|
|
yAxisIndex: 1,
|
|
|
|
|
color: '#36CFC9',
|
|
|
|
|
smooth: true,
|
|
|
|
|
symbol: 'circle',
|
|
|
|
|
symbolSize: 8,
|
|
|
|
|
label: {
|
|
|
|
|
show: true,
|
|
|
|
|
position: 'top',
|
|
|
|
|
fontSize: baseFontSize - 2
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
};
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 获取数据
|
|
|
|
|
const getData = async () => {
|
|
|
|
|
try {
|
|
|
|
|
loading.value = true;
|
|
|
|
|
const year = datetimePicker.value.value;
|
|
|
|
|
const res = await sdFprdRGetFtpChangeInfo({
|
|
|
|
|
baseId: baseid.value,
|
|
|
|
|
year
|
|
|
|
|
});
|
|
|
|
|
if (res && (res as any).data) {
|
|
|
|
|
sourceData.value = (res as any).data;
|
|
|
|
|
} else {
|
|
|
|
|
sourceData.value = [];
|
|
|
|
|
}
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('获取鱼类变化数据失败:', error);
|
|
|
|
|
sourceData.value = [];
|
|
|
|
|
} finally {
|
|
|
|
|
loading.value = false;
|
|
|
|
|
}
|
2026-06-24 11:26:19 +08:00
|
|
|
nextTick(() => {
|
|
|
|
|
if (sourceData.value.length > 0) {
|
|
|
|
|
initChart();
|
|
|
|
|
}
|
|
|
|
|
});
|
2026-06-18 09:29:15 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 初始化图表
|
|
|
|
|
const initChart = () => {
|
|
|
|
|
if (!chartContainer.value) return;
|
|
|
|
|
|
|
|
|
|
if (chartInstance) {
|
|
|
|
|
chartInstance.dispose();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
chartInstance = echarts.init(chartContainer.value);
|
|
|
|
|
chartInstance.setOption(option.value, true);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 更新图表
|
|
|
|
|
const updateChart = () => {
|
|
|
|
|
if (chartInstance) {
|
|
|
|
|
chartInstance.setOption(option.value, true);
|
|
|
|
|
chartInstance.resize();
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 处理面板变化
|
|
|
|
|
const handlePanelChange = (data: any) => {
|
|
|
|
|
if (data.datetime) {
|
|
|
|
|
datetimePicker.value.value = data.datetime;
|
|
|
|
|
getData();
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 窗口大小变化时重新调整图表
|
|
|
|
|
const handleResize = () => {
|
2026-06-24 11:26:19 +08:00
|
|
|
chartInstance?.resize();
|
2026-06-18 09:29:15 +08:00
|
|
|
};
|
|
|
|
|
let baseid = ref('');
|
|
|
|
|
watch(
|
|
|
|
|
() => JidiSelectEventStore.selectedItem,
|
|
|
|
|
newVal => {
|
|
|
|
|
if (newVal && newVal.wbsCode) {
|
|
|
|
|
baseid.value = newVal.wbsCode;
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
{ deep: true, immediate: true }
|
|
|
|
|
);
|
2026-06-24 11:26:19 +08:00
|
|
|
|
2026-06-18 09:29:15 +08:00
|
|
|
onMounted(() => {
|
2026-06-24 11:26:19 +08:00
|
|
|
nextTick(() => {
|
|
|
|
|
// 延时确保容器尺寸已完全渲染
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
if (chartContainer.value) {
|
|
|
|
|
chartInstance = echarts.init(chartContainer.value);
|
|
|
|
|
chartInstance.setOption(option.value);
|
|
|
|
|
}
|
|
|
|
|
// 监听窗口大小变化
|
|
|
|
|
window.addEventListener('resize', handleResize);
|
|
|
|
|
}, 50);
|
|
|
|
|
});
|
2026-06-18 09:29:15 +08:00
|
|
|
getData();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
onUnmounted(() => {
|
|
|
|
|
window.removeEventListener('resize', handleResize);
|
2026-06-24 11:26:19 +08:00
|
|
|
chartInstance?.dispose();
|
|
|
|
|
chartInstance = null;
|
2026-06-18 09:29:15 +08:00
|
|
|
});
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<style lang="scss" scoped>
|
|
|
|
|
.chart-container {
|
|
|
|
|
width: 100%;
|
|
|
|
|
height: 200px;
|
2026-06-24 11:26:19 +08:00
|
|
|
min-height: 200px;
|
|
|
|
|
}
|
|
|
|
|
.empty-container {
|
|
|
|
|
display: flex;
|
2026-07-07 13:50:21 +08:00
|
|
|
flex-direction: column;
|
2026-06-24 11:26:19 +08:00
|
|
|
justify-content: center;
|
|
|
|
|
align-items: center;
|
|
|
|
|
height: 200px;
|
2026-07-07 13:50:21 +08:00
|
|
|
p{
|
|
|
|
|
text-indent:0px !important;
|
|
|
|
|
}
|
2026-06-18 09:29:15 +08:00
|
|
|
}
|
|
|
|
|
</style>
|