Преглед изворни кода

feat: 增加历史数据的分页

yanglzh пре 7 месеци
родитељ
комит
683f62c3da
1 измењених фајлова са 301 додато и 288 уклоњено
  1. 301 288
      src/views/iot/device/instance/component/chart.vue

+ 301 - 288
src/views/iot/device/instance/component/chart.vue

@@ -1,356 +1,369 @@
 <template>
-  <div class="system-edit-dic-container">
-    <el-dialog v-model="isShowDialog" :title="data.name + `(${data.key})`" width="850px">
-      <!-- 添加 tab 切换 -->
-      <el-tabs v-model="activeTab">
-        <el-tab-pane label="趋势图" name="trend">
-          <!-- 这里是 echarts 线图 -->
-          <div id="lineChart" ref="chartRef" class="chart-container"></div>
-        </el-tab-pane>
-        <el-tab-pane label="历史数据" name="history">
-          <!-- 历史日志数据表格 -->
-          <div class="history-container">
-            <div class="date-picker-container">
-              <el-date-picker v-model="historyDateRange" type="datetimerange" range-separator="至" start-placeholder="开始日期" end-placeholder="结束日期" format="YYYY-MM-DD HH:mm:ss" value-format="YYYY-MM-DD HH:mm:ss" @change="fetchHistoryData" />
-              <el-button type="primary" @click="exportData">导出</el-button>
-            </div>
-            <el-table :data="historyData" border style="width: 100%" v-loading="historyLoading">
-              <el-table-column prop="dataTime" label="时间" align="center" />
-              <el-table-column prop="dataValue" label="属性值" align="center" />
-              <el-table-column prop="unit" label="数据单位" align="center">
-                <template #default>{{ data.unit }}</template>
-              </el-table-column>
-            </el-table>
-            <div class="pagination-container">
-              <el-pagination
-                v-model:current-page="currentPage"
-                v-model:page-size="pageSize"
-                :page-sizes="[10, 20, 50, 100]"
-                layout="total, sizes, prev, pager, next, jumper"
-                :total="totalItems"
-                @size-change="handleSizeChange"
-                @current-change="handleCurrentChange"
-              />
-            </div>
-          </div>
-        </el-tab-pane>
-      </el-tabs>
-    </el-dialog>
-  </div>
+	<div class="system-edit-dic-container">
+		<el-dialog v-model="isShowDialog" :title="data.name + `(${data.key})`" width="850px">
+			<!-- 添加 tab 切换 -->
+			<el-tabs v-model="activeTab">
+				<el-tab-pane label="趋势图" name="trend">
+					<!-- 这里是 echarts 线图 -->
+					<div id="lineChart" ref="chartRef" class="chart-container"></div>
+				</el-tab-pane>
+				<el-tab-pane label="历史数据" name="history">
+					<!-- 历史日志数据表格 -->
+					<div class="history-container">
+						<div class="date-picker-container">
+							<el-date-picker
+								v-model="historyDateRange"
+								type="datetimerange"
+								range-separator="至"
+								start-placeholder="开始日期"
+								end-placeholder="结束日期"
+								format="YYYY-MM-DD HH:mm:ss"
+								value-format="YYYY-MM-DD HH:mm:ss"
+								@change="fetchHistoryData"
+							/>
+							<el-button type="primary" @click="exportData">导出</el-button>
+						</div>
+						<el-table :data="historyData" border style="width: 100%" v-loading="historyLoading">
+							<el-table-column prop="ts" label="时间" align="center" />
+							<el-table-column prop="value" label="属性值" align="center" />
+							<el-table-column prop="unit" label="数据单位" align="center">
+								<template #default>{{ data.unit }}</template>
+							</el-table-column>
+						</el-table>
+						<div class="pagination-container">
+							<el-pagination
+								v-model:current-page="currentPage"
+								v-model:page-size="pageSize"
+								:page-sizes="[10, 20, 50, 100]"
+								layout="total, sizes, prev, pager, next, jumper"
+								:total="totalItems"
+								@size-change="handleSizeChange"
+								@current-change="handleCurrentChange"
+							/>
+						</div>
+					</div>
+				</el-tab-pane>
+			</el-tabs>
+		</el-dialog>
+	</div>
 </template>
 
 <script lang="ts" setup>
-import { ref, reactive, watch, nextTick } from "vue";
-import api from "/@/api/device";
-import * as echarts from "echarts";
-import dayjs from "dayjs";
-import { ElMessage } from "element-plus";
+import { ref, reactive, watch, nextTick } from 'vue'
+import api from '/@/api/device'
+import * as echarts from 'echarts'
+import dayjs from 'dayjs'
+import { ElMessage } from 'element-plus'
 
-const data = ref({ name: "", key: "", unit: "" });
-const loading = ref(false);
-const isShowDialog = ref(false);
-const chartRef = ref<HTMLElement | null>(null);
-let chartInstance: echarts.ECharts | null = null;
-const lineData = ref([]);
+const data = ref({ name: '', key: '', unit: '' })
+const loading = ref(false)
+const isShowDialog = ref(false)
+const chartRef = ref<HTMLElement | null>(null)
+let chartInstance: echarts.ECharts | null = null
+const lineData = ref([])
 
 // 新增 Tab 相关状态
-const activeTab = ref("trend");
-const historyData = ref([]);
-const historyLoading = ref(false);
-const historyDateRange = ref([dayjs().subtract(1, "hour").format("YYYY-MM-DD HH:mm:ss"), dayjs().format("YYYY-MM-DD HH:mm:ss")]);
-const currentPage = ref(1);
-const pageSize = ref(10);
-const totalItems = ref(0);
+const activeTab = ref('trend')
+const historyData = ref([])
+const historyLoading = ref(false)
+const historyDateRange = ref([dayjs().subtract(1, 'hour').format('YYYY-MM-DD HH:mm:ss'), dayjs().format('YYYY-MM-DD HH:mm:ss')])
+const currentPage = ref(1)
+const pageSize = ref(10)
+const totalItems = ref(0)
 
 const params = reactive({
-  productKey: "",
-  deviceKey: "",
-  properties: "",
-  dateRange: [dayjs().subtract(1, "hour").format("YYYY-MM-DD HH:mm:ss"), dayjs().format("YYYY-MM-DD HH:mm:ss")],
-});
+	productKey: '',
+	deviceKey: '',
+	properties: '',
+	dateRange: [dayjs().subtract(1, 'hour').format('YYYY-MM-DD HH:mm:ss'), dayjs().format('YYYY-MM-DD HH:mm:ss')],
+})
 
 // 初始化图表
 const initChart = () => {
-  if (chartRef.value) {
-    // 如果已有实例先销毁
-    if (chartInstance) {
-      chartInstance.dispose();
-    }
-    // 创建图表实例
-    chartInstance = echarts.init(chartRef.value);
-    // 设置加载状态
-    if (loading.value) {
-      chartInstance.showLoading();
-    } else {
-      chartInstance.hideLoading();
-    }
-    // 更新图表
-    updateChart();
-  }
-};
+	if (chartRef.value) {
+		// 如果已有实例先销毁
+		if (chartInstance) {
+			chartInstance.dispose()
+		}
+		// 创建图表实例
+		chartInstance = echarts.init(chartRef.value)
+		// 设置加载状态
+		if (loading.value) {
+			chartInstance.showLoading()
+		} else {
+			chartInstance.hideLoading()
+		}
+		// 更新图表
+		updateChart()
+	}
+}
 
 // 更新图表数据
 const updateChart = () => {
-  if (!chartInstance) return;
+	if (!chartInstance) return
 
-  // 从 lineData 中提取数据
-  const xData = lineData.value.map((item: any) => item.ts?.slice(10)).reverse();
-  const yData = lineData.value.map((item: any) => item.value).reverse();
+	// 从 lineData 中提取数据
+	const xData = lineData.value.map((item: any) => item.ts?.slice(10)).reverse()
+	const yData = lineData.value.map((item: any) => item.value).reverse()
 
-  // 配置图表选项
-  const option = {
-    tooltip: {
-      trigger: "axis",
-      formatter: "{b}<br />{a}: {c}",
-    },
-    grid: {
-      top: 15,
-      left: 40,
-      right: 30,
-      bottom: 50, // 增加底部空间,为 dataZoom 留出位置
-      containLabel: true,
-    },
-    dataZoom: [
-      {
-        type: "slider", // 滑动条型数据区域缩放组件
-        show: true,
-        start: 0,
-        end: 100,
-        height: 20,
-        bottom: 10,
-        borderColor: "transparent",
-        backgroundColor: "#f5f5f5",
-        fillerColor: "rgba(167, 183, 204, 0.4)",
-        handleIcon: "M10.7,11.9v-1.3H9.3v1.3c-4.9,0.3-8.8,4.4-8.8,9.4c0,5,3.9,9.1,8.8,9.4v1.3h1.3v-1.3c4.9-0.3,8.8-4.4,8.8-9.4C19.5,16.3,15.6,12.2,10.7,11.9z M13.3,24.4H6.7V23h6.6V24.4z M13.3,19.6H6.7v-1.4h6.6V19.6z",
-        handleSize: "80%",
-        handleStyle: {
-          color: "#fff",
-          shadowBlur: 3,
-          shadowColor: "rgba(0, 0, 0, 0.6)",
-          shadowOffsetX: 2,
-          shadowOffsetY: 2,
-        },
-      },
-      {
-        type: "inside", // 内置型数据区域缩放组件,允许鼠标滚轮或触摸板缩放
-        start: 0,
-        end: 100,
-      },
-    ],
-    xAxis: {
-      type: "category",
-      data: xData,
-      axisLabel: {
-        rotate: 0,
-      },
-    },
-    yAxis: {
-      type: "value",
-      splitLine: {
-        show: true,
-        lineStyle: {
-          color: "rgba(88,88,88,0.5)",
-        },
-      },
-    },
-    series: [
-      {
-        name: "数值",
-        type: "line",
-        data: yData,
-        smooth: true,
-        lineStyle: {
-          width: 2,
-        },
-        symbolSize: 4,
-      },
-    ],
-  };
+	// 配置图表选项
+	const option = {
+		tooltip: {
+			trigger: 'axis',
+			formatter: '{b}<br />{a}: {c}',
+		},
+		grid: {
+			top: 15,
+			left: 40,
+			right: 30,
+			bottom: 50, // 增加底部空间,为 dataZoom 留出位置
+			containLabel: true,
+		},
+		dataZoom: [
+			{
+				type: 'slider', // 滑动条型数据区域缩放组件
+				show: true,
+				start: 0,
+				end: 100,
+				height: 20,
+				bottom: 10,
+				borderColor: 'transparent',
+				backgroundColor: '#f5f5f5',
+				fillerColor: 'rgba(167, 183, 204, 0.4)',
+				handleIcon:
+					'M10.7,11.9v-1.3H9.3v1.3c-4.9,0.3-8.8,4.4-8.8,9.4c0,5,3.9,9.1,8.8,9.4v1.3h1.3v-1.3c4.9-0.3,8.8-4.4,8.8-9.4C19.5,16.3,15.6,12.2,10.7,11.9z M13.3,24.4H6.7V23h6.6V24.4z M13.3,19.6H6.7v-1.4h6.6V19.6z',
+				handleSize: '80%',
+				handleStyle: {
+					color: '#fff',
+					shadowBlur: 3,
+					shadowColor: 'rgba(0, 0, 0, 0.6)',
+					shadowOffsetX: 2,
+					shadowOffsetY: 2,
+				},
+			},
+			{
+				type: 'inside', // 内置型数据区域缩放组件,允许鼠标滚轮或触摸板缩放
+				start: 0,
+				end: 100,
+			},
+		],
+		xAxis: {
+			type: 'category',
+			data: xData,
+			axisLabel: {
+				rotate: 0,
+			},
+		},
+		yAxis: {
+			type: 'value',
+			splitLine: {
+				show: true,
+				lineStyle: {
+					color: 'rgba(88,88,88,0.5)',
+				},
+			},
+		},
+		series: [
+			{
+				name: '数值',
+				type: 'line',
+				data: yData,
+				smooth: true,
+				lineStyle: {
+					width: 2,
+				},
+				symbolSize: 4,
+			},
+		],
+	}
 
-  // 设置图表配置
-  chartInstance.setOption(option);
-};
+	// 设置图表配置
+	chartInstance.setOption(option)
+}
 
 // 获取历史数据
 const fetchHistoryData = () => {
-  if (!params.deviceKey || !params.properties) return;
+	if (!params.deviceKey || !params.properties) return
 
-  historyLoading.value = true;
+	historyLoading.value = true
 
-  const historyParams = {
-    productKey: params.productKey,
-    deviceKey: params.deviceKey,
-    properties: params.properties,
-    dateRange: historyDateRange.value,
-    page: currentPage.value,
-    pageSize: pageSize.value,
-  };
+	const historyParams = {
+		productKey: params.productKey,
+		deviceKey: params.deviceKey,
+		propertyKey: params.properties,
+		dateRange: historyDateRange.value,
+		pageNum: currentPage.value,
+		pageSize: pageSize.value,
+	}
 
-  api.analysis
-    .deviceIndicatorTrend(historyParams)
-    .then((res: any) => {
-      historyData.value = res;
-      totalItems.value = res.length;
-    })
-    .catch((error) => {
-      console.error("获取历史数据失败:", error);
-      ElMessage.error("获取历史数据失败");
-    })
-    .finally(() => {
-      historyLoading.value = false;
-    });
-};
+	api.instance
+		.getLogDetail(historyParams)
+		.then((res: any) => {
+			historyData.value = res.List
+			totalItems.value = res.Total
+		})
+		.catch(() => {
+			historyData.value = []
+			totalItems.value = 0
+		})
+		.finally(() => (historyLoading.value = false))
+}
 
 // 处理分页大小变化
 const handleSizeChange = (size: number) => {
-  pageSize.value = size;
-  fetchHistoryData();
-};
+	pageSize.value = size
+	fetchHistoryData()
+}
 
 // 处理页码变化
 const handleCurrentChange = (page: number) => {
-  currentPage.value = page;
-  fetchHistoryData();
-};
+	currentPage.value = page
+	fetchHistoryData()
+}
 
 // 导出数据
 const exportData = () => {
-  if (historyData.value.length === 0) {
-    ElMessage.warning("没有数据可导出");
-    return;
-  }
+	const historyParams = {
+		productKey: params.productKey,
+		deviceKey: params.deviceKey,
+		propertyKey: params.properties,
+		dateRange: historyDateRange.value,
+		pageNum: currentPage.value,
+		pageSize: pageSize.value,
+	}
 
-  // 创建CSV内容
-  let csvContent = "时间,属性值";
-  if (data.value.unit) {
-    csvContent += ",数据单位";
-  }
-  csvContent += "\n";
+	api.instance.getLogDetail(historyParams).then((res: any) => {
+		// 创建CSV内容
+		let csvContent = '时间,属性值'
+		if (data.value.unit) {
+			csvContent += ',数据单位'
+		}
+		csvContent += '\n'
 
-  historyData.value.forEach((item: any) => {
-    csvContent += `${item.ts},${item.value}`;
-    if (data.value.unit) {
-      csvContent += `,${data.value.unit}`;
-    }
-    csvContent += "\n";
-  });
+		res.List.forEach((item: any) => {
+			csvContent += `${item.ts},${item.value}`
+			if (data.value.unit) {
+				csvContent += `,${data.value.unit}`
+			}
+			csvContent += '\n'
+		})
 
-  // 创建Blob对象
-  const blob = new Blob([csvContent], { type: "text/csv;charset=utf-8;" });
-  const url = URL.createObjectURL(blob);
+		// 创建Blob对象
+		const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' })
+		const url = URL.createObjectURL(blob)
 
-  // 创建下载链接
-  const link = document.createElement("a");
-  link.setAttribute("href", url);
-  link.setAttribute("download", `${data.value.name}_历史数据_${dayjs().format("YYYYMMDD_HHmmss")}.csv`);
-  link.style.visibility = "hidden";
-  document.body.appendChild(link);
-  link.click();
-  document.body.removeChild(link);
-};
+		// 创建下载链接
+		const link = document.createElement('a')
+		link.setAttribute('href', url)
+		link.setAttribute('download', `${data.value.name}_历史数据_${dayjs().format('YYYYMMDD_HHmmss')}.csv`)
+		link.style.visibility = 'hidden'
+		document.body.appendChild(link)
+		link.click()
+		document.body.removeChild(link)
+	})
+}
 
 // 监听数据变化,更新图表
 watch(
-  () => lineData.value,
-  () => {
-    nextTick(() => {
-      updateChart();
-    });
-  },
-  { deep: true }
-);
+	() => lineData.value,
+	() => {
+		nextTick(() => {
+			updateChart()
+		})
+	},
+	{ deep: true }
+)
 
 // 监听 loading 状态变化
 watch(
-  () => loading.value,
-  (newVal) => {
-    if (chartInstance) {
-      if (newVal) {
-        chartInstance.showLoading();
-      } else {
-        chartInstance.hideLoading();
-      }
-    }
-  }
-);
+	() => loading.value,
+	(newVal) => {
+		if (chartInstance) {
+			if (newVal) {
+				chartInstance.showLoading()
+			} else {
+				chartInstance.hideLoading()
+			}
+		}
+	}
+)
 
 // 监听弹窗显示状态
 watch(
-  () => isShowDialog.value,
-  (val) => {
-    if (val) {
-      nextTick(() => {
-        initChart();
-        // 重置分页
-        currentPage.value = 1;
-        // 如果是历史日志标签页,加载历史数据
-        if (activeTab.value === "history") {
-          fetchHistoryData();
-        }
-      });
-    }
-  }
-);
+	() => isShowDialog.value,
+	(val) => {
+		if (val) {
+			nextTick(() => {
+				initChart()
+				// 重置分页
+				currentPage.value = 1
+				// 如果是历史日志标签页,加载历史数据
+				if (activeTab.value === 'history') {
+					fetchHistoryData()
+				}
+			})
+		}
+	}
+)
 
 // 监听标签页切换
 watch(
-  () => activeTab.value,
-  (val) => {
-    if (val === "history" && isShowDialog.value) {
-      fetchHistoryData();
-    } else if (val === "trend" && isShowDialog.value) {
-      nextTick(() => {
-        initChart();
-      });
-    }
-  }
-);
+	() => activeTab.value,
+	(val) => {
+		if (val === 'history' && isShowDialog.value) {
+			fetchHistoryData()
+		} else if (val === 'trend' && isShowDialog.value) {
+			nextTick(() => {
+				initChart()
+			})
+		}
+	}
+)
 
 // 打开弹窗
 const openDialog = (row: any, deviceKey: string, productKey: string) => {
-  params.productKey = productKey;
-  params.deviceKey = deviceKey;
-  params.properties = row.key;
+	params.productKey = productKey
+	params.deviceKey = deviceKey
+	params.properties = row.key
 
-  // 重置日期范围
-  historyDateRange.value = [dayjs().subtract(1, "hour").format("YYYY-MM-DD HH:mm:ss"), dayjs().format("YYYY-MM-DD HH:mm:ss")];
-  console.log(row);
-  data.value = row;
-  isShowDialog.value = true;
-  loading.value = true;
-  api.instance
-    .getLogDetail({
-      deviceKey: deviceKey,
-      propertyKey: row.key,
-      pageSize: 100,
-    })
-    .then((res: any) => {
-      lineData.value = res.List;
-    })
-    .finally(() => (loading.value = false));
-};
+	// 重置日期范围
+	historyDateRange.value = [dayjs().subtract(1, 'hour').format('YYYY-MM-DD HH:mm:ss'), dayjs().format('YYYY-MM-DD HH:mm:ss')]
+	data.value = row
+	isShowDialog.value = true
+	loading.value = true
+	api.instance
+		.getLogDetail({
+			deviceKey: deviceKey,
+			propertyKey: row.key,
+			pageSize: 100,
+		})
+		.then((res: any) => {
+			lineData.value = res.List
+		})
+		.finally(() => (loading.value = false))
+}
 
-defineExpose({ openDialog });
+defineExpose({ openDialog })
 </script>
 <style scoped>
 .chart-container {
-  width: 100%;
-  height: 400px;
+	width: 100%;
+	height: 400px;
 }
 
 .history-container {
-  width: 100%;
+	width: 100%;
 }
 
 .date-picker-container {
-  display: flex;
-  justify-content: space-between;
-  margin-bottom: 15px;
+	display: flex;
+	justify-content: space-between;
+	margin-bottom: 15px;
 }
 
 .pagination-container {
-  margin-top: 15px;
-  display: flex;
-  justify-content: flex-end;
+	margin-top: 15px;
+	display: flex;
+	justify-content: flex-end;
 }
 </style>