SourceTermAnalysisSystem_vue/src/pages/sjfx/three/index.vue
wangchengming 95736e18ad 提交
2026-07-13 20:43:17 +08:00

1226 lines
37 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<script setup lang='ts'>
import { Download, Search } from '@element-plus/icons-vue';
import * as echarts from 'echarts';
import { ElLoading, ElMessage, type FormInstance, type FormRules, type UploadProps } from 'element-plus';
import { ref } from 'vue';
import { useI18n } from 'vue-i18n';
import { getConcChartData, getSampleStat, getStationList, exportData } from '@/utils/axios/sjfx';
import { exportCharts } from '@/utils/index';
// 下载图表图片
const { t } = useI18n();
const sampleTypeList = ref<any>([
{ label: '颗粒物', value: 'P' },
{ label: '惰性气体', value: 'B' },
]);
const dataSourceList = ref<any>([
{ label: 'MDC自动处理', value: '1' },
{ label: 'MDC交互分析', value: '2' },
{ label: 'IDC自动处理', value: '3' },
{ label: 'IDC交互分析', value: '4' },
]);
const form = ref<any>({
// sampleType: 'P',
// station: 122,
// dataSource: '1',
// startDate: '2025-01-01',
// endDate: '2025-10-15',
// createTime: ['2025-01-01', '2025-10-15'],
});
const stationList = ref<any>([]);
function getStation() {
getStationList({ systemType: form.value.sampleType, dataSource: form.value.dataSource }, (res) => {
const list = res.result;
stationList.value = list.map((item: { stationCode: any; stationId: any }) => {
return {
label: item.stationCode,
value: item.stationId,
};
});
// console.log(res, 'res');
});
}
const clickPieItem = ref('');
const nuclideData = ref<any>({});
const yTitle = ref('uBq)');
const loading = ref(false);
watch(
() => form.value.sampleType,
() => {
if (form.value.sampleType === 'P') {
yTitle.value = `uBq`;
}
else {
yTitle.value = `mBq`;
}
},
);
// watch(
// () => clickPieItem.value,
// () => {
// const list = nuclideData.value[clickPieItem.value].sort((a: any, b: any) => {
// return new Date(a.collectStop) - new Date(b.collectStop);
// });
// setClickPieData(list);
// },
// );
const formRef = ref<FormInstance>();
const rules = {
dataSource: [{ required: true, message: '请选择数据来源', trigger: 'change' }],
station: [{ required: true, message: '请选择台站名称', trigger: 'change' }],
createTime: [{
required: true, message: '请选择创建时间范围', trigger: 'change', validator: (rule, value, callback) => {
if (!value || value.length === 0) {
callback(new Error('请选择创建时间范围'));
}
else {
callback();
}
}
}],
};
function setTime(val: any[]) {
form.value.startDate = val[0];
form.value.endDate = val[1];
console.log(val);
}
function handleSearch() {
getData();
}
function handleSampleTypeChange() {
if (!form.value.dataSource) {
ElMessage.warning('请选择数据来源!!!');
return;
}
form.value.station = '';
getStation();
initPieCharts();
initLineCharts();
}
// 饼图相关
const pieData = ref<any>([
]);
// 模拟数据
const lineData = ref<any>({
// xAxisData: [],
// seriesData: [
// ],
});
const pieRef = ref<HTMLDivElement | null>(null);
let pieChart: echarts.ECharts | null = null;
// 折线图相关
const lineRef = ref<HTMLDivElement | null>(null);
let lineChart: echarts.ECharts | null = null;
// 饼图点击
function handlePieClick(params: { componentType: string; seriesType: string; data: { name: string } }) {
if (params.componentType === 'series' && params.seriesType === 'pie') {
// console.log('饼图被点击了', params);
if (params.data.name === 'other') {
ElMessage.info('点击了其他核素类别,无法显示具体数据');
return;
}
clickPieItem.value = params.data.name;
console.log(clickPieItem.value, '点中的');
clickPieInfo();
}
}
// 查询饼图数据
async function getData() {
const formEl = formRef.value;
if (!formEl)
return;
await formEl.validate((valid, fields) => {
if (valid) {
form.value.stationIds = [form.value.station]
console.log(form.value, 'form');
getSampleStat(form.value, (res) => {
console.log(res, 'res');
const data = res.result;
nuclideData.value = data?.nuclideActConcIntvlList;
if (Object.entries(nuclideData.value).length === 0) {
pieData.value = [];
initPieCharts();
initLineCharts();
ElMessage.warning('当前未查询到相关核素数据');
return;
}
const stationList: { name: string; value: any[] }[] = [];
Object.entries(nuclideData.value).forEach(([key, value]) => {
// console.log(`Key: ${key}, Value: ${value.length}`);
stationList.push({
name: key,
value: value?.length || 0,
});
});
console.log(stationList, 'stationList');
if (stationList.length > 10) {
const firstList = stationList.slice(0, 10);
// 第二个数组从第10个开始到结束
const secondList = stationList.slice(10);
const secondListLength = secondList.reduce((sum, item) => sum + item.value, 0);
const newArr: any = { name: 'other', value: secondListLength };
const newStationList = firstList.concat(newArr);
pieData.value = newStationList;
}
else {
pieData.value = stationList;
}
renderPieChart();
});
}
// else {
// console.log('表单验证失败:', fields);
// ElMessage.error('搜索信息未填写');
// }
});
}
// --- 图表核心逻辑 ---
/**
* 提取通用的 Custom Series 渲染器
* @param color 颜色
* @param shapeType 中心的形状 'rect' | 'polygon' (三角形)
*/
function createErrorBarRenderItem(color: string, shapeType: 'rect' | 'triangle' | 'circle') {
return function (params: any, api: any) {
const xValue = api.value(0);
const yValue = api.value(1);
const errorTop = api.value(2); // 顶部误差
const errorBottom = api.value(3); // 底部误差
const point = api.coord([xValue, yValue]);
const top = api.coord([xValue, yValue + errorTop]);
const bottom = api.coord([xValue, yValue - errorBottom]);
const halfSize = 6; // 图形半径
const children: any[] = [
// 垂直线
{
type: 'line',
shape: { x1: point[0], y1: top[1], x2: point[0], y2: bottom[1] },
style: { stroke: color, lineWidth: 1 },
},
// 顶部横线
{
type: 'line',
shape: { x1: point[0] - 3, y1: top[1], x2: point[0] + 3, y2: top[1] },
style: { stroke: color, lineWidth: 1 },
},
// 底部横线
{
type: 'line',
shape: { x1: point[0] - 3, y1: bottom[1], x2: point[0] + 3, y2: bottom[1] },
style: { stroke: color, lineWidth: 1 },
},
];
// 中心图形
if (shapeType === 'rect') {
children.push({
type: 'rect',
shape: {
x: point[0] - halfSize,
y: point[1] - halfSize,
width: halfSize * 2,
height: halfSize * 2,
},
style: { fill: color },
});
}
else if (shapeType === 'triangle') {
children.push({
type: 'polygon',
shape: {
points: [
[point[0] - halfSize, point[1] + halfSize - 1], // 左下
[point[0] + halfSize, point[1] + halfSize - 1], // 右下
[point[0], point[1] - halfSize - 1], // 上
],
},
style: { fill: color },
});
}
else if (shapeType === 'circle') {
children.push({
type: 'circle',
shape: {
cx: point[0],
cy: point[1],
r: halfSize,
},
style: { fill: color },
});
}
return { type: 'group', children };
};
}
/**
* 生成系列数据配置
*/
function generateSeries(name: string, data: any[], color: string, shape: 'rect' | 'triangle' | 'circle') {
if (!data || data.length === 0)
return null;
// 按照时间排序
const sortedData = data
.sort((a: any, b: any) => new Date(a[0]).getTime() - new Date(b[0]).getTime())
.map((item: any) => [
item[0], // x: 时间
item[1], // y: 值
item[3] || 0.1, // errorTop (对应原代码 item[3])
item[2] || 0.1, // errorBottom (对应原代码 item[2], 注意顺序需匹配renderItem里的获取)
]);
return {
name: `${name}${data.length}`,
type: 'custom',
itemStyle: { color },
renderItem: createErrorBarRenderItem(color, shape),
data: sortedData,
z: 10,
};
}
// 简单的去重工具函数
function uniqueData(data: any[]) {
if (!data)
return [];
// 假设根据时间(item[0])去重,使用 Map 效率更高
const map = new Map();
data.forEach((item) => {
if (Array.isArray(item))
map.set(item[0], item);
});
return Array.from(map.values());
}
// 点击饼图详情
async function clickPieInfo() {
const formEl = formRef.value;
if (!formEl)
return;
await formEl.validate((valid, fields) => {
if (valid) {
// console.log(form.value, 'form');
getConcChartData({ ...form.value, nuclideName: clickPieItem.value }, (res) => {
console.log(res, 'res');
const data = res.result;
if (Object.entries(data).length === 0) {
lineData.value.xAxisData = [];
lineData.value.seriesData = [];
renderLineChart();
ElMessage.warning('当前核素无浓度数据');
return;
}
const mdcList = uniqueData(data.mdc);// mdc数据Y轴数据
const threshold = uniqueData(data.threshold);// 异常阀值Y轴数据
// 基础系列 (MDC 和 阈值)
const seriesArr: any[] = [
{
name: 'MDC',
type: 'line',
step: 'start',
symbolSize: 0,
data: mdcList,
},
{
name: '异常阀值',
type: 'line',
lineStyle: { color: 'red', width: 4, type: 'dashed' },
itemStyle: { color: 'red' },
data: threshold,
},
];
// 根据 sampleType 动态添加自定义系列
const levelsConfig = [];
if (form.value.sampleType === 'P') {
// 颗粒物Level 3 (黄, rect), Level 4 (红, triangle) - 原代码 Level4 是 triangle
levelsConfig.push(
{ name: '级别3', data: data.category3, color: 'yellow', shape: 'rect' },
{ name: '级别4', data: data.category4, color: 'red', shape: 'triangle' },
);
}
else {
// 惰性气体Level 1 (圆点), Level 2 (黄, rect), Level 3 (红, triangle)
levelsConfig.push(
{ name: '级别1', data: data.category1, color: 'green', shape: 'circle' },
{ name: '级别2', data: data.category2, color: 'yellow', shape: 'rect' },
{ name: '级别3', data: data.category3, color: 'red', shape: 'triangle' },
);
}
// 批量生成并过滤空数据
levelsConfig.forEach((conf) => {
const s = generateSeries(conf.name, conf.data, conf.color, conf.shape as any);
if (s)
seriesArr.push(s);
});
// 提取 X 轴 (取阈值的时间点,也可以合并所有数据的时间点)
const xAxisData = threshold.map((item: any) => item[0]);
console.log(xAxisData, seriesArr, 'xAxisData, seriesArr');
lineData.value.xAxisData = xAxisData;
lineData.value.seriesData = seriesArr;
renderLineChart(xAxisData, seriesArr);
});
}
// else {
// console.log('表单验证失败:', fields);
// ElMessage.error('搜索信息未填写');
// }
});
}
// async function getData() {
// const loading = ElLoading.service({
// background: 'rgba(0, 0, 0, 0.7)',
// fullscreen: true,
// text: 'Loading...',
// target: document.querySelector('.pie-container'),
// });
// const res = await fetch('../../../../public/mock/threeData.json');
// const list = await res.json();
// console.log(list, 'res');
// const data = list.result;
// nuclideData.value = data?.nuclideActConcIntvlList;
// // const threshold = data?.thresholdResultHisDataList; // 为空
// // const sampleLevel = data?.sampleLevelDataList;
// // 按照日期排序
// // const sortSampleLevelList = sampleLevel.sort((a: any, b: any) => {
// // return new Date(a.collectStop) - new Date(b.collectStop);
// // });
// const stationList: { name: string; value: any[] }[] = [];
// Object.entries(nuclideData.value).forEach(([key, value]) => {
// // console.log(`Key: ${key}, Value: ${value.length}`);
// stationList.push({
// name: key,
// value: value?.length || 0,
// });
// });
// console.log(stationList, 'stationList');
// if (stationList.length > 10) {
// const firstList = stationList.slice(0, 10);
// // 第二个数组从第10个开始到结束
// const secondList = stationList.slice(10);
// // console.log(firstList, 'newStationList');
// // console.log(secondList, 'newStationList2');
// const secondListLength = secondList.reduce((sum, item) => sum + item.value, 0);
// // console.log(secondListLength, 'secondListLength');
// const newArr = { name: 'other', value: secondListLength };
// const newStationList = firstList.concat(newArr);
// // console.log(newStationList, 'newStationListFinal');
// pieData.value = newStationList;
// }
// else {
// pieData.value = stationList;
// }
// // loading.close();
// renderPieChart();
// // renderLineChart();
// }
// async function clickPieInfo() {
// // const loading = ElLoading.service({
// // background: 'rgba(0, 0, 0, 0.7)',
// // fullscreen: true,
// // text: 'Loading...',
// // target: document.querySelector('.line-container'),
// // });
// const res = await fetch('../../../../public/mock/three1.json');
// const list = await res.json();
// console.log(list, 'res');
// const data = list.result;
// const mdcList = data.mdc; // mdc数据Y轴数据
// const threshold = data.threshold; // 异常阀值Y轴数据
// const level3List = data.category3; // 级别3浓度数据Y轴数据
// const level4List = data.category4; // 级别4浓度数据Y轴数据
// // const a = level4List
// // .sort((a: any, b: any) => new Date(a.x).getTime() - new Date(b.x).getTime())
// // .map((item: any, index: number) => [
// // item[0], // 使用索引作为x值
// // item[1], // y值
// // item[2] || 0.1, // 底部误差如果为0则给个最小值
// // item[3] || 0.1, // 顶部误差如果为0则给个最小值
// // ]);
// // console.log(a, 'a');
// lineData.value.xAxisData = [...new Set(threshold.map(item => JSON.stringify(item)))].map(str => JSON.parse(str)).map((item: any) => item[0]); ; // X轴数据
// // console.log(lineData.value.xAxisData, 'xAxisData');
// lineData.value.seriesData = [
// {
// name: 'MDC',
// type: 'line',
// step: 'start',
// symbolSize: 0,
// // lineStyle: {
// // color: '#878787',
// // },
// itemStyle: {
// // width: 0,
// // borderColor: 'red',
// // color: '#878787',
// },
// data: [...new Set(mdcList.map(item => JSON.stringify(item)))].map(str => JSON.parse(str)),
// },
// {
// name: '异常阀值',
// type: 'line',
// lineStyle: {
// color: 'red',
// width: 4,
// type: 'dashed',
// },
// itemStyle: {
// color: 'red',
// },
// data: [...new Set(threshold.map(item => JSON.stringify(item)))].map(str => JSON.parse(str)),
// },
// {
// name: `级别3${level3List.length}`,
// // type: 'line',
// symbol: 'circle',
// symbolSize: 10,
// itemStyle: {
// color: 'yellow',
// },
// type: 'custom',
// renderItem(params: any, api: any) {
// const xValue: any = api.value(0);
// const yValue: any = api.value(1);
// const error: any = api.value(2);
// const error2: any = api.value(3);
// const point: any = api.coord([xValue, yValue]);
// const top = api.coord([xValue, yValue + error]);
// const bottom = api.coord([xValue, yValue - error2]);
// // 正方形的大小
// const squareSize = 12;
// return {
// type: 'group',
// children: [
// // 完整垂直误差线(从顶部到底部)
// {
// type: 'line',
// shape: {
// x1: point[0],
// y1: top[1],
// x2: point[0],
// y2: bottom[1],
// },
// style: {
// stroke: 'yellow',
// lineWidth: 1,
// },
// },
// // 顶部标记(水平短线)
// {
// type: 'line',
// shape: {
// x1: point[0] - 3,
// y1: top[1],
// x2: point[0] + 3,
// y2: top[1],
// },
// style: {
// stroke: 'yellow',
// lineWidth: 1,
// },
// },
// // 底部标记(水平短线)
// {
// type: 'line',
// shape: {
// x1: point[0] - 3,
// y1: bottom[1],
// x2: point[0] + 3,
// y2: bottom[1],
// },
// style: {
// stroke: 'yellow',
// lineWidth: 1,
// },
// },
// // 正方形
// {
// type: 'rect',
// shape: {
// x: point[0] - squareSize / 2,
// y: point[1] - squareSize / 2,
// width: squareSize,
// height: squareSize,
// },
// style: {
// fill: 'yellow',
// // stroke: '#fff',
// lineWidth: 0.5,
// },
// },
// ],
// };
// },
// data: level3List
// .sort((a: any, b: any) => new Date(a.x).getTime() - new Date(b.x).getTime())
// .map((item: any) => [
// item[0], // 使用索引作为x值
// item[1], // y值
// item[2] || 0.1, // 底部误差如果为0则给个最小值
// item[3] || 0.1, // 顶部误差如果为0则给个最小值
// ]),
// z: 10,
// // data: level3List,
// },
// {
// name: `级别4${level4List.length}`,
// // type: 'line',
// symbol: 'circle',
// symbolSize: 10,
// itemStyle: {
// color: 'red',
// },
// type: 'custom',
// renderItem(params: any, api: any) {
// const xValue: any = api.value(0);
// const yValue: any = api.value(1);
// const error: any = api.value(2);
// const error2: any = api.value(3);
// const point: any = api.coord([xValue, yValue]);
// const top = api.coord([xValue, yValue + error]);
// const bottom = api.coord([xValue, yValue - error2]);
// return {
// type: 'group',
// children: [
// {
// type: 'line',
// shape: {
// x1: point[0],
// y1: top[1],
// x2: point[0],
// y2: bottom[1],
// },
// style: {
// stroke: 'red',
// lineWidth: 3,
// },
// },
// {
// type: 'line',
// shape: {
// x1: point[0] - 5,
// y1: top[1],
// x2: point[0] + 5,
// y2: top[1],
// },
// style: {
// stroke: 'red',
// lineWidth: 2,
// },
// },
// {
// type: 'line',
// shape: {
// x1: point[0] - 5,
// y1: bottom[1],
// x2: point[0] + 5,
// y2: bottom[1],
// },
// style: {
// stroke: 'red',
// lineWidth: 2,
// },
// },
// // 中心点正三角形
// {
// type: 'polygon',
// shape: {
// points: [
// [point[0] - 6, point[1] + 5], // 左下角
// [point[0] + 6, point[1] + 5], // 右下角
// [point[0], point[1] - 5], // 上顶点
// ],
// },
// style: {
// fill: 'red',
// // stroke: '#fff',
// lineWidth: 0.5,
// },
// },
// ],
// };
// },
// data: level4List
// .sort((a: any, b: any) => new Date(a.x).getTime() - new Date(b.x).getTime())
// .map((item: any) => [
// item[0], // 使用索引作为x值
// item[1], // y值
// item[2] || 0.1, // 底部误差如果为0则给个最小值
// item[3] || 0.1, // 顶部误差如果为0则给个最小值
// ]),
// z: 10,
// // data: level4List,
// },
// ];
// console.log(lineData.value.seriesData, 'seriesData');
// loading.value.close();
// renderLineChart();
// }
function renderPieChart() {
const option = {
tooltip: { trigger: 'item' },
legend: { type: 'scroll', orient: 'vertical', top: '20%', left: 'right', textStyle: { color: '#9ab1bc' }, icon: 'circle' },
series: [
{
type: 'pie',
radius: ['40%', '70%'],
data: pieData.value,
},
],
};
pieChart.setOption(option);
// 绑定点击事件
pieChart.on('click', handlePieClick);
}
function renderLineChart() {
const option = {
tooltip: {
trigger: 'axis', backgroundColor: '#03aa7f', borderColor: '#03aa7f', // 边框颜色
borderWidth: 1, // 边框宽度
borderRadius: 4, // 圆角
textStyle: {
color: '#fff', // 文字颜色
fontSize: 12,
}
},
toolbox: {
feature: {
dataZoom: {
show: true,
title: {
zoom: '区域缩放',
back: '区域缩放还原',
},
},
},
},
legend: {
type: 'plain',
// data: lineData.legendData,
top: 0,
textStyle: { color: '#9ab1bc' },
},
grid: {
left: '5%',
right: '2%',
bottom: '5%',
top: '10%',
containLabel: true,
},
xAxis: {
type: 'time',
name: '时间',
nameLocation: 'center',
nameTextStyle: { padding: 20, color: '#9ab1bc' },
axisLabel: {
color: '#9ab1bc',
formatter: (value: string | number) => {
const d = new Date(value as any);
if (isNaN(d.getTime()))
return String(value);
const y = d.getFullYear();
const m = (`0${d.getMonth() + 1}`).slice(-2);
const day = (`0${d.getDate()}`).slice(-2);
return `${y}-${m}-${day}`;
},
rotate: 0,
},
// data: lineData.value.xAxisData,
},
yAxis: {
type: 'value',
name: `浓度 (${yTitle.value}/m³)`,
nameLocation: 'center',
nameTextStyle: { padding: 60, color: '#9ab1bc' },
axisLabel: { color: '#9ab1bc' },
splitLine: {
show: true,
lineStyle: { color: '#406979', width: 1, type: 'solid' },
},
},
scale: true,
series: lineData.value.seriesData,
};
lineChart.setOption(option);
// 绑定点击事件
// lineChart.on('click', handlePieClick);
}
// 初始化图表
function initPieCharts() {
// 先销毁已存在的图表实例,防止内存泄漏
if (pieChart) {
pieChart.dispose();
}
nextTick(() => {
// 初始化柱状图
if (pieRef.value) {
pieChart = echarts.init(pieRef.value);
pieChart.setOption({
// tooltip: { trigger: 'item' },
// legend: { type: 'scroll', orient: 'vertical', top: '20%', left: 'right', textStyle: { color: '#9ab1bc' }, icon: 'circle' },
// series: [
// {
// type: 'pie',
// radius: ['40%', '70%'],
// data: pieData.value,
// },
// ],
});
}
});
}
// 初始化图表
function initLineCharts() {
// 先销毁已存在的图表实例,防止内存泄漏
if (lineChart) {
lineChart.dispose();
}
nextTick(() => {
// 初始化柱状图
if (lineRef.value) {
lineChart = echarts.init(lineRef.value);
lineChart.setOption({
// tooltip: { trigger: 'axis' },
// legend: {
// type: 'plain',
// // data: lineData.value.legendData,
// top: 0,
// textStyle: { color: '#9ab1bc' },
// },
// grid: {
// left: '5%',
// right: '2%',
// bottom: '5%',
// top: '10%',
// containLabel: true,
// },
// xAxis: {
// type: 'category',
// nameTextStyle: { color: '#9ab1bc' },
// axisLabel: { color: '#9ab1bc' },
// axisLine: {
// lineStyle: {
// color: '#406979', // 线条颜色
// width: 1, // 线条宽度
// type: 'solid', // 线条类型: solid, dashed, dotted
// },
// },
// // data: lineData.value.xAxisData,
// name: '时间',
// nameLocation: 'center',
// },
// yAxis: {
// type: 'value',
// name: '浓度(uBq/m3)',
// nameLocation: 'center',
// nameTextStyle: { color: '#9ab1bc' },
// axisLabel: { color: '#9ab1bc' },
// splitLine: {
// show: true,
// lineStyle: {
// color: '#406979', // 网格线颜色
// width: 1, // 网格线宽度
// type: 'solid', // 线条类型: solid, dashed, dotted
// },
// },
// },
// series: [
// {
// name: 'MDC',
// type: 'line',
// step: 'start',
// symbolSize: 0,
// lineStyle: {
// color: '#878787',
// },
// itemStyle: {
// // width: 0,
// // borderColor: 'red',
// color: '#878787',
// },
// // data: lineData.value.seriesData[0]?.data,
// },
// {
// name: '异常阀值',
// type: 'line',
// lineStyle: {
// color: 'red',
// width: 4,
// type: 'dashed',
// },
// itemStyle: {
// color: 'red',
// },
// // data: lineData.value.seriesData[1]?.data,
// },
// {
// name: '级别4(10)',
// type: 'line',
// // data: lineData.value.seriesData[2]?.data,
// symbol:
// 'path://M-0.4,-35 L0.4,-35 L0.4,-8 L-0.4,-8 Z M-0.4,8 L0.4,8 L0.4,35 L-0.4,35 Z M-5,6 L0,-6 L5,6 L0,6 Z',
// symbolSize: [20, 70],
// lineStyle: {
// type: 'dashed',
// color: 'red',
// width: 0,
// },
// itemStyle: {
// borderColor: 'red',
// color: 'red',
// },
// },
// {
// name: '级别3(40)',
// type: 'line',
// symbol:
// 'path://M-0.4,-35 L0.4,-35 L0.4,-8 L-0.4,-8 Z M-0.4,8 L0.4,8 L0.4,35 L-0.4,35 Z M-4,-6 L4,-6 L4,6 L-4,6 Z',
// symbolSize: [12, 70],
// // data: lineData.value.seriesData[3]?.data,
// lineStyle: {
// type: 'solid',
// color: 'yellow',
// width: 0,
// },
// itemStyle: {
// borderWidth: 0.5,
// borderColor: 'yellow',
// color: 'yellow',
// opacity: 1, // 透明度
// },
// },
// ],
});
}
});
}
const exportChartsData2 = async () => {
try {
console.log('开始下载...');
form.value.type = 'sampleStat'
const { filename, blob } = await exportData(form.value);
// const filename = '模版'
console.log('文件信息:', {
filename,
blob,
blobType: blob.type,
blobSize: `${blob.size} bytes`,
});
// 覆盖 Blob 类型为 Excel 标准 MIME 类型(避免后端返回类型错误)
const excelBlob = new Blob([blob], {
type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' // xlsx 标准类型
// 如果是 xls 格式,用:'application/vnd.ms-excel'
});
// 验证 Blob 是否有效
if (!blob || blob.size === 0) {
throw new Error('获取到的文件为空');
}
// 创建下载链接
const url = window.URL.createObjectURL(excelBlob);
const link = document.createElement('a');
link.href = url;
link.download = filename; // 使用从响应头获取的文件名
link.style.display = 'none';
document.body.appendChild(link);
link.click();
console.log('文件下载成功:', filename);
ElMessage.success('导出成功');
}
catch (error) {
console.error('文件下载失败:', error);
}
finally {
}
}
const exportChartsData = async () => {
try {
console.log('开始下载...');
form.value.type = 'nuclideTime'
const { filename, blob } = await exportData(form.value);
// const filename = '模版'
console.log('文件信息:', {
filename,
blob,
blobType: blob.type,
blobSize: `${blob.size} bytes`,
});
// 覆盖 Blob 类型为 Excel 标准 MIME 类型(避免后端返回类型错误)
const excelBlob = new Blob([blob], {
type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' // xlsx 标准类型
// 如果是 xls 格式,用:'application/vnd.ms-excel'
});
// 验证 Blob 是否有效
if (!blob || blob.size === 0) {
throw new Error('获取到的文件为空');
}
// 创建下载链接
const url = window.URL.createObjectURL(excelBlob);
const link = document.createElement('a');
link.href = url;
link.download = filename; // 使用从响应头获取的文件名
link.style.display = 'none';
document.body.appendChild(link);
link.click();
console.log('文件下载成功:', filename);
ElMessage.success('导出成功');
}
catch (error) {
console.error('文件下载失败:', error);
}
finally {
}
}
onMounted(async () => {
// getStation();
initPieCharts();
initLineCharts();
window.addEventListener('resize', () => {
pieChart?.resize();
lineChart.resize();
});
});
</script>
<template>
<div class="sjfx-two-content">
<div class="head">
<el-form ref="formRef" :model="form" label-width="100px" class="queryForm" :rules="rules">
<el-row :gutter="10">
<el-col :span="4">
<el-form-item :label="t('dataSource')" prop="dataSource">
<el-select v-model="form.dataSource" :placeholder="t('pleaseSelect')" style="width: 300px;">
<el-option v-for="item in dataSourceList" :key="item.value"
style="background-color: transparent; height: 30px;color: #fff;" :label="item.label"
:value="item.value" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="4">
<el-form-item :label="t('sampleType')">
<el-select v-model="form.sampleType" :placeholder="t('pleaseSelect')" style="width: 100%;"
popper-style="background-color:transparent;border: solid 1px #0da397;width:100px;overflow: hidden;border-radius: 0;"
@change="handleSampleTypeChange">
<el-option v-for="item in sampleTypeList" :key="item.value"
style="background-color: transparent; height: 30px;color: #fff;" :label="item.label"
:value="item.value" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="5">
<el-form-item :label="t('stationName')" prop="station">
<el-select-v2 v-model="form.station" :placeholder="t('pleaseSelect')" style="width: 160px;" filterable
:options="stationList" clearable />
</el-form-item>
</el-col>
<!-- <el-col :span="3">
<el-form-item :label="t('nuclideName')">
<el-input v-model="form.nuclideName" style="width: 100%;" />
</el-form-item>
</el-col> -->
<el-col :span="8">
<el-form-item :label="t('createTime')" prop="createTime">
<el-date-picker v-model="form.createTime" type="daterange" unlink-panels :default-time="new Date()"
style="width: 100%;" range-separator="--" :start-placeholder="t('startDate')"
:end-placeholder="t('endDate')" value-format="YYYY-MM-DD" @change="setTime" />
</el-form-item>
</el-col>
<el-col :span="2">
<el-form-item>
<el-button :icon="Search" @click="handleSearch">
{{ t('refresh') }}
</el-button>
</el-form-item>
</el-col>
</el-row>
</el-form>
</div>
<div class="echars">
<div class="pie">
<div class="echars-head">
<h3>{{ t('NuclearIsotopeStatisticalAnalysis') }}</h3>
<div style="flex: 1; text-align: right;">
<el-button :icon="Download" @click="exportChartsData2">
{{ t('exportData') }}
</el-button>
<el-button :icon="Download" @click="exportCharts(pieChart, '核素统计分析')">
{{ t('download') }}
</el-button>
</div>
</div>
<div class="chart-card">
<div ref="pieRef" class="pie-container" />
<!-- <button class="update-btn" @click="handleUpdatePie">
更新数据
</button> -->
</div>
</div>
<div class="line">
<div class="echars-head">
<h3>{{ t('IsotopeGradeTimeSeriesAnalysis') }}</h3>
<div style="flex: 1; text-align: right;">
<el-button :icon="Download" @click="exportChartsData">
{{ t('exportData') }}
</el-button>
<el-button :icon="Download" @click="exportCharts(lineChart, '核素等级时序分析')">
{{ t('download') }}
</el-button>
</div>
</div>
<div class="chart-card">
<div ref="lineRef" class="line-container" :loading="loading" />
</div>
</div>
</div>
</div>
</template>
<style scoped lang='less'>
.head {
width: 100%;
height: 50px;
line-height: 50px;
display: flex;
justify-content: space-between;
margin: 10px 0 0 0;
padding-top: 5px;
.queryForm {
display: flex;
align-items: center;
:deep(.el-form) {
padding-top: 20px;
}
:deep(.el-form-item__content) {
&:nth-last-child(1) {
margin-left: 0 !important;
}
}
:deep(.el-range-separator) {
color: #fff;
}
:deep(.el-select__wrapper),
:deep(.el-date-editor) {
background-color: rgba(3, 53, 63, 0.51) !important;
border-radius: 0%;
border: solid 1px #0b8c82 !important;
}
}
:deep(.el-button) {
width: 90px;
height: 32px;
margin: 0 5px;
background-color: #1397a3;
font-size: 16px;
font-weight: normal;
color: #ffffff;
border-radius: 0;
border: 1px solid #1397a3;
}
}
.echars-head {
position: relative;
display: flex;
justify-content: space-between;
align-items: center;
height: 42px;
// border-bottom: 2px solid #0cebc9;
border-top: 1px solid #0cebc9;
margin-bottom: 6px;
// &::before {
// content: '';
// position: absolute;
// top: 0;
// left: 0;
// width: 100%;
// height: 0.5px;
// width: 100%;
// transform: scaleY(0.5);
// // transform-origin: top;
// background-color: #0cebc9;
// }
&::after {
content: '';
position: absolute;
bottom: 0;
left: 0;
width: 100%;
height: 6px;
width: 100%;
transform: scaleY(0.5);
transform-origin: bottom;
background: rgba(12, 235, 201, 0.2);
}
h3 {
height: 40px;
padding-right: 20px;
font-size: 18px;
font-weight: bold;
font-stretch: normal;
line-height: 40px;
letter-spacing: 1px;
color: #0cebc9;
background: rgba(12, 235, 201, 0.05);
margin-left: 18px;
}
:deep(.el-button) {
width: 85px;
// height: 24px;
// line-height: 42px;
background-color: #097c42;
color: #fff;
border: none;
border-radius: 0;
}
}
.chart-card {
background-color: transparent;
border-radius: 8px;
padding: 16px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
.pie-container {
width: 100%;
height: 266px;
}
.line-container {
width: 100%;
height: 440px;
}
}
</style>