WholeProcessPlatform/frontend/src/views/shengTaiDiaoCha/shuiShengShengTaiDiaoCha/SSSTDCDB.vue

442 lines
10 KiB
Vue
Raw Normal View History

<script setup lang="ts">
import {
ref,
reactive,
watch,
onMounted,
onBeforeUnmount,
nextTick
} from 'vue';
import * as echarts from 'echarts';
import type { ECharts } from 'echarts';
import { getFishtKendoList, getCompareBar } from '@/api/stdc';
import BasicTable from '@/components/BasicTable/index.vue';
const props = defineProps<{
currentItem: any;
}>();
// ==================== 搜索状态 ====================
const weLis = ref<any[]>([]);
const pcList = ref<any[]>([]);
const searchParam = reactive({
dcpc: '',
dcpcCompare: '',
stcd: ''
});
// ==================== 图表状态 ====================
const chartRef = ref<HTMLElement | null>(null);
let chartInstance: ECharts | null = null;
const chartList = ref<any[]>([]);
const chartLoading = ref(true);
// ==================== 表格状态 ====================
const tabLoading = ref(false);
const tableRef = ref();
const column = ref([
{
key: 'ftp',
title: '鱼种类',
dataIndex: 'ftp',
fixed: 'left' as const,
width: 150,
ellipsis: true
},
{
key: 'genus',
title: '科属',
dataIndex: 'genus',
fixed: 'left' as const,
width: 150,
ellipsis: true
},
{
key: 'fcnt',
title: '第一批次',
dataIndex: 'fcnt',
width: 150,
ellipsis: true
},
{
key: 'fcnt2',
title: '对比批次',
dataIndex: 'fcnt2',
width: 150,
ellipsis: true
}
]);
// ==================== 构建表格过滤条件 ====================
const buildTableFilter = () => {
const filters: any[] = [];
if (searchParam.stcd) {
filters.push({ field: 'stcd', operator: 'eq', value: searchParam.stcd });
}
if (searchParam.dcpc) {
filters.push({ field: 'dcpc', operator: 'eq', value: searchParam.dcpc });
}
if (searchParam.dcpcCompare) {
filters.push({
field: 'dcpc2',
operator: 'eq',
value: searchParam.dcpcCompare
});
}
// 动态更新列标题
column.value[2].title = searchParam.dcpc || '第一批次';
column.value[3].title = searchParam.dcpcCompare || '对比批次';
return {
logic: 'and',
filters
};
};
// ==================== 获取断面和批次列表 ====================
const fetchPcData = async () => {
const baseid = props.currentItem?.wbsCode || props.currentItem?.baseid;
const params = {
filter: {
filters:
baseid === 'all'
? []
: [
{
field: 'baseId',
operator: 'eq',
value: baseid
}
]
},
group: [
{ dir: 'asc', field: 'stcd' },
{ dir: 'asc', field: 'stnm' },
{ dir: 'asc', field: 'dcpc' }
]
};
const res = await getFishtKendoList(params);
if (res?.data?.data?.length) {
const options: any[] = [];
res.data.data.forEach((el: any) => {
if (el?.items?.[0]?.items?.length > 1) {
options.push({
label: el?.items?.[0]?.key,
value: el?.key,
dcpc: el?.items?.[0]?.items?.map((item: any) => ({
label: item.key,
value: item.key
}))
});
}
});
weLis.value = options;
pcList.value = options?.[0]?.dcpc ?? [];
searchParam.stcd = options[0]?.value || '';
searchParam.dcpc = options[0]?.dcpc?.[0]?.value || '';
searchParam.dcpcCompare = options[0]?.dcpc?.[1]?.value || '';
tabLoading.value = true;
} else {
weLis.value = [];
pcList.value = [];
tabLoading.value = true;
}
};
// ==================== 断面切换 ====================
const handleStcdChange = (stcd: string) => {
if (!stcd) return;
const found = weLis.value.find((el: any) => el.value === stcd);
if (found) {
pcList.value = found.dcpc;
searchParam.stcd = found.value;
searchParam.dcpc = found.dcpc?.[0]?.value || '';
searchParam.dcpcCompare = found.dcpc?.[1]?.value || '';
}
};
// ==================== 获取图表数据 ====================
const fetchChartData = async () => {
chartLoading.value = true;
try {
const params = {
filter: {
logic: 'and',
filters: [
{
field: 'stcd',
operator: 'eq',
dataType: 'string',
value: searchParam.stcd
},
{
field: 'dcpc',
operator: 'eq',
dataType: 'string',
value: searchParam.dcpc
},
{
field: 'dcpc2',
operator: 'eq',
dataType: 'string',
value: searchParam.dcpcCompare
}
]
}
};
const res: any = await getCompareBar(params);
// debugger
if (res?.data?.data?.[0]?.dataSource) {
chartList.value = res.data.data[0].dataSource;
} else {
chartList.value = [];
}
} catch (error) {
console.error('获取图表数据失败:', error);
chartList.value = [];
} finally {
chartLoading.value = false;
}
};
// ==================== 初始化图表 ====================
const initChart = () => {
if (!chartRef.value) return;
if (!chartInstance) {
chartInstance = echarts.init(chartRef.value);
}
if (!chartList.value || chartList.value.length < 1) {
chartInstance.setOption({
title: { text: '', subtext: '' },
tooltip: { trigger: 'axis' },
xAxis: { type: 'category', data: [] },
yAxis: { type: 'value' },
series: []
});
return;
}
const option = {
title: { text: '', subtext: '' },
tooltip: {
trigger: 'axis',
axisPointer: { type: 'shadow' }
},
legend: {
data: [searchParam.dcpc, searchParam.dcpcCompare],
left: 'center',
show: true,
itemWidth: 12,
itemHeight: 8,
textStyle: { fontSize: 14 }
},
grid: {
top: '20%',
left: '5%',
right: 0,
bottom: 0,
containLabel: true
},
xAxis: {
type: 'category',
axisLabel: { rotate: 45 },
axisTick: { show: false },
data: chartList.value.map((e: any) => e.ftp)
},
yAxis: {
type: 'value',
name: '数量(尾)'
},
series: [
{
name: searchParam.dcpc,
type: 'bar',
barWidth: 5,
stack: 'dcpc',
data: chartList.value.map((e: any) => e.fcnt)
},
{
name: searchParam.dcpcCompare,
type: 'bar',
barWidth: 5,
stack: 'dcpcCompare',
data: chartList.value.map((e: any) => e.fcnt2)
}
]
};
chartInstance.setOption(option, true);
};
// ==================== 表格数据转换 ====================
const customTransform = (res: any) => {
return {
records: res?.data?.data?.[0].dataSource || [],
total: res?.data?.total || 0
};
};
// ==================== 刷新表格 ====================
const refreshTable = () => {
const filter = buildTableFilter();
tableRef.value?.getList(filter);
};
// ==================== 查询按钮 ====================
const handleSearch = () => {
fetchChartData();
nextTick(() => {
initChart();
refreshTable();
});
};
// ==================== 监听批次变化自动刷新图表 ====================
watch(
() => [searchParam.dcpc, searchParam.dcpcCompare],
() => {
fetchChartData();
nextTick(() => {
initChart();
});
}
);
// ==================== 监听图表数据变化 ====================
watch(
() => chartList.value,
() => {
nextTick(() => {
initChart();
});
}
);
// ==================== 窗口resize ====================
const handleResize = () => {
chartInstance?.resize();
};
// ==================== 生命周期 ====================
onMounted(async () => {
await fetchPcData();
await nextTick();
fetchChartData();
await nextTick();
initChart();
await nextTick();
refreshTable();
window.addEventListener('resize', handleResize);
});
onBeforeUnmount(() => {
window.removeEventListener('resize', handleResize);
chartInstance?.dispose();
chartInstance = null;
});
</script>
<template>
<div class="ssstdcdb-page">
<!-- 搜索表单 -->
<div class="search-form">
<a-form-item label="调查断面">
<a-select
v-model:value="searchParam.stcd"
placeholder="请选择调查断面"
style="width: 200px; margin-right: 12px"
:options="weLis"
@change="handleStcdChange"
/>
</a-form-item>
<a-form-item label="调查批次">
<a-select
v-model:value="searchParam.dcpc"
placeholder="请选择调查批次"
style="width: 200px; margin-right: 12px"
:options="pcList"
/>
</a-form-item>
<a-form-item label="对比批次">
<a-select
v-model:value="searchParam.dcpcCompare"
placeholder="请选择对比批次"
style="width: 200px; margin-right: 12px"
:options="pcList"
/>
</a-form-item>
<a-button type="primary" @click="handleSearch">查询</a-button>
</div>
<!-- 图表 + 表格 -->
<div class="content-area">
<!-- 图表 -->
<div class="chart-section">
<a-spin :spinning="chartLoading">
<div ref="chartRef" class="chart-container" style="height: 500px"></div>
<a-empty
v-if="!chartLoading && chartList.length === 0"
class="chart-empty"
description="暂无数据"
/>
</a-spin>
</div>
<!-- 表格 -->
<BasicTable
ref="tableRef"
:columns="column"
:scroll-y="400"
:scroll-x="300"
:list-url="tabLoading ? getCompareBar : undefined"
:search-params="{ sort: [] }"
:transform-data="customTransform"
/>
</div>
</div>
</template>
<style scoped lang="scss">
.ssstdcdb-page {
// padding: 16px;
.search-form {
padding: 16px 0;
display: flex;
}
.content-area {
display: flex;
gap: 16px;
.chart-section {
width: 40%;
height: 500px;
position: relative;
}
.chart-empty {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
z-index: 1;
}
.table-section {
width: 60%;
height: 500px;
}
}
}
</style>