(proxy?.useDict('aqu_device_type', 'open_close', 'connect_voltage_type', 'yes_no', 'loop_type', 'is_linked_ctrl', 'interval_type'));
@@ -1700,6 +1704,73 @@ const historyTabConfig = {
battery: { label: '电量', unit: '%', color: '#9a60b4' }
};
+const historyExportColumns: Array<{ label: string; unit?: string; getValue: (item: DeviceSensorData) => unknown }> = [
+ { label: '监测时间', getValue: item => item.time },
+ { label: '塘口', getValue: item => (item as DeviceSensorData & { pondName?: string }).pondName || historyDevice.value?.pondName || '' },
+ { label: '设备名称', getValue: item => item.deviceName || historyDevice.value?.deviceName || '' },
+ { label: '设备编号', getValue: item => item.serialNum || historyDevice.value?.serialNum || historyQueryParams.serialNum || '' },
+ { label: '溶解氧', unit: 'Mg/L', getValue: item => item.dissolvedOxygen },
+ { label: '水温', unit: '℃', getValue: item => item.temperature },
+ { label: '饱和度', unit: '%', getValue: item => item.saturability }
+];
+
+const escapeHistoryExcelHtml = (value: unknown) => {
+ if (value === null || value === undefined) {
+ return '';
+ }
+ return String(value)
+ .replace(/&/g, '&')
+ .replace(//g, '>')
+ .replace(/"/g, '"');
+};
+
+const getHistoryExportFileName = () => {
+ const serialNum = historyQueryParams.serialNum || 'all';
+ const startTime = historyQueryParams.startTime || historyDateRange.value?.[0] || '';
+ const endTime = historyQueryParams.endTime || historyDateRange.value?.[1] || '';
+ const dateText = startTime && endTime ? `_${startTime}_${endTime}` : '';
+ return `监测历史记录_${serialNum}${dateText}_${new Date().getTime()}.xls`;
+};
+
+const getHistoryExportLimit = () => {
+ const intervalType = Number(historyQueryParams.intervalType || 1);
+ if (intervalType === 1) {
+ return { months: 1, message: '实时数据最多只能导出1个月内的数据' };
+ }
+ if (intervalType === 2 || intervalType === 3) {
+ return { months: 3, message: '10分钟和30分钟数据最多只能导出3个月内的数据' };
+ }
+ return { months: 6, message: '1小时以上数据最多只能导出6个月内的数据' };
+};
+
+const validateHistoryExportDateRange = () => {
+ const startTime = historyQueryParams.startTime || historyDateRange.value?.[0];
+ const endTime = historyQueryParams.endTime || historyDateRange.value?.[1];
+ if (!startTime || !endTime) {
+ proxy?.$modal.msgWarning('请先选择导出时间范围');
+ return false;
+ }
+
+ const startDate = new Date(`${startTime} 00:00:00`.replace(/-/g, '/'));
+ const endDate = new Date(`${endTime} 23:59:59`.replace(/-/g, '/'));
+ if (Number.isNaN(startDate.getTime()) || Number.isNaN(endDate.getTime()) || startDate > endDate) {
+ proxy?.$modal.msgWarning('导出时间范围不正确,请重新选择');
+ return false;
+ }
+
+ const limit = getHistoryExportLimit();
+ const maxEndDate = new Date(startDate);
+ maxEndDate.setMonth(maxEndDate.getMonth() + limit.months);
+ maxEndDate.setHours(23, 59, 59, 999);
+ if (endDate > maxEndDate) {
+ proxy?.$modal.msgWarning(limit.message);
+ return false;
+ }
+
+ return true;
+};
+
/** 查看历史曲线 */
const handleViewHistory = (row: DeviceVO) => {
historyDevice.value = row;
@@ -1843,6 +1914,61 @@ const downloadHistoryChart = () => {
link.click();
};
+/** 导出历史数据 */
+const exportHistoryExcel = () => {
+ if (historyData.value.length === 0) {
+ proxy?.$modal.msgWarning('暂无可导出的历史数据,请先查询');
+ return;
+ }
+ if (!validateHistoryExportDateRange()) {
+ return;
+ }
+
+ const headerHtml = historyExportColumns
+ .map(column => `| ${escapeHistoryExcelHtml(column.unit ? `${column.label}(${column.unit})` : column.label)} | `)
+ .join('');
+ const rowsHtml = historyData.value
+ .map(item => {
+ const cells = historyExportColumns
+ .map(column => `${escapeHistoryExcelHtml(column.getValue(item))} | `)
+ .join('');
+ return `${cells}
`;
+ })
+ .join('');
+ const worksheet = `
+
+
+
+
+
+
+
+
+ ${headerHtml}
+ ${rowsHtml}
+
+
+`;
+
+ const blob = new Blob([worksheet], { type: 'application/vnd.ms-excel;charset=utf-8' });
+ FileSaver.saveAs(blob, getHistoryExportFileName());
+};
+
/** 关闭历史曲线对话框 */
const closeHistoryDialog = () => {
historyDialogVisible.value = false;
diff --git a/src/views/fishery/monitorHistory/index.vue b/src/views/fishery/monitorHistory/index.vue
index 93fa1e8..254b02d 100644
--- a/src/views/fishery/monitorHistory/index.vue
+++ b/src/views/fishery/monitorHistory/index.vue
@@ -42,6 +42,7 @@
搜索
重置
+ 导出 Excel
@@ -184,6 +185,7 @@
import { ref, reactive, onMounted, nextTick, computed } from 'vue';
import * as echarts from 'echarts';
import type { EChartsOption } from 'echarts';
+import FileSaver from 'file-saver';
import { getHistoryData } from '@/api/fishery/monitorHistory';
import type { DeviceSensorDataQueryBo, DeviceSensorData } from '@/api/fishery/monitorHistory/types';
import { listDevice } from '@/api/fishery/device';
@@ -262,6 +264,73 @@ const tabConfig = {
battery: { label: '电量', unit: '%', color: '#9a60b4' }
};
+const exportColumns: Array<{ label: string; unit?: string; getValue: (item: DeviceSensorData) => unknown }> = [
+ { label: '监测时间', getValue: item => item.time },
+ { label: '塘口', getValue: item => (item as DeviceSensorData & { pondName?: string }).pondName || selectedDevice.value?.pondName || '' },
+ { label: '设备名称', getValue: item => item.deviceName || selectedDevice.value?.deviceName || '' },
+ { label: '设备编号', getValue: item => item.serialNum || selectedDevice.value?.serialNum || queryParams.value.serialNum || '' },
+ { label: '溶解氧', unit: 'Mg/L', getValue: item => item.dissolvedOxygen },
+ { label: '水温', unit: '℃', getValue: item => item.temperature },
+ { label: '饱和度', unit: '%', getValue: item => item.saturability }
+];
+
+const escapeHtml = (value: unknown) => {
+ if (value === null || value === undefined) {
+ return '';
+ }
+ return String(value)
+ .replace(/&/g, '&')
+ .replace(//g, '>')
+ .replace(/"/g, '"');
+};
+
+const getExportFileName = () => {
+ const serialNum = queryParams.value.serialNum || 'all';
+ const startTime = queryParams.value.startTime || dateRange.value?.[0] || '';
+ const endTime = queryParams.value.endTime || dateRange.value?.[1] || '';
+ const dateText = startTime && endTime ? `_${startTime}_${endTime}` : '';
+ return `监测历史记录_${serialNum}${dateText}_${new Date().getTime()}.xls`;
+};
+
+const getExportLimit = () => {
+ const intervalType = Number(queryParams.value.intervalType || 1);
+ if (intervalType === 1) {
+ return { months: 1, message: '实时数据最多只能导出1个月内的数据' };
+ }
+ if (intervalType === 2 || intervalType === 3) {
+ return { months: 3, message: '10分钟和30分钟数据最多只能导出3个月内的数据' };
+ }
+ return { months: 6, message: '1小时以上数据最多只能导出6个月内的数据' };
+};
+
+const validateExportDateRange = () => {
+ const startTime = queryParams.value.startTime || dateRange.value?.[0];
+ const endTime = queryParams.value.endTime || dateRange.value?.[1];
+ if (!startTime || !endTime) {
+ proxy?.$modal.msgWarning('请先选择导出时间范围');
+ return false;
+ }
+
+ const startDate = new Date(`${startTime} 00:00:00`.replace(/-/g, '/'));
+ const endDate = new Date(`${endTime} 23:59:59`.replace(/-/g, '/'));
+ if (Number.isNaN(startDate.getTime()) || Number.isNaN(endDate.getTime()) || startDate > endDate) {
+ proxy?.$modal.msgWarning('导出时间范围不正确,请重新选择');
+ return false;
+ }
+
+ const limit = getExportLimit();
+ const maxEndDate = new Date(startDate);
+ maxEndDate.setMonth(maxEndDate.getMonth() + limit.months);
+ maxEndDate.setHours(23, 59, 59, 999);
+ if (endDate > maxEndDate) {
+ proxy?.$modal.msgWarning(limit.message);
+ return false;
+ }
+
+ return true;
+};
+
/** 查询历史数据 */
const getHistoryList = async () => {
// 验证是否选择了设备
@@ -331,6 +400,63 @@ const resetQuery = () => {
endTime: '',
intervalType: '3'
};
+ historyData.value = [];
+ updateChart();
+};
+
+/** 导出查询结果 */
+const handleExport = () => {
+ if (historyData.value.length === 0) {
+ proxy?.$modal.msgWarning('暂无可导出的历史数据,请先查询');
+ return;
+ }
+ if (!validateExportDateRange()) {
+ return;
+ }
+
+ const headerHtml = exportColumns
+ .map(column => `${escapeHtml(column.unit ? `${column.label}(${column.unit})` : column.label)} | `)
+ .join('');
+ const rowsHtml = historyData.value
+ .map(item => {
+ const cells = exportColumns
+ .map(column => `${escapeHtml(column.getValue(item))} | `)
+ .join('');
+ return `${cells}
`;
+ })
+ .join('');
+ const worksheet = `
+
+
+
+
+
+
+
+
+ ${headerHtml}
+ ${rowsHtml}
+
+
+`;
+
+ const blob = new Blob([worksheet], { type: 'application/vnd.ms-excel;charset=utf-8' });
+ FileSaver.saveAs(blob, getExportFileName());
};
/** 标签页切换 */