Browse Source

AI修改后的相对完美

wukai 5 months ago
parent
commit
e66ee08fa5
3 changed files with 2561 additions and 6 deletions
  1. 1268 0
      src/views/dye/hour/calc.vue
  2. 1243 0
      src/views/dye/line/calc.vue
  3. 50 6
      src/views/dyeing/gyfx/index.vue

+ 1268 - 0
src/views/dye/hour/calc.vue

@@ -0,0 +1,1268 @@
+<template>
+  <div class="app-container">
+    <!-- 头部操作区域 -->
+    <el-card style="margin-bottom: 15px;">
+      <el-row :gutter="15" align="middle">
+        <!-- 时间选择 -->
+        <el-col :span="8">
+          <div style="display: flex; align-items: center; justify-content: center;">
+            <el-button icon="ArrowLeft" @click="navigateDay(-1)" size="small" style="margin-right: 10px;"></el-button>
+            <el-date-picker clearable
+                            v-model="queryParams.dataDate"
+                            type="date"
+                            value-format="YYYY-MM-DD"
+                            placeholder="请选择日期"
+                            style="width: 120px"
+                            size="small"
+                            @change="handleQuery">
+            </el-date-picker>
+            <el-button icon="ArrowRight" @click="navigateDay(1)" size="small" style="margin-left: 10px;"></el-button>
+          </div>
+        </el-col>
+
+        <!-- 设备类型和参数选择 -->
+        <el-col :span="10">
+          <div style="display: flex; align-items: center; flex-wrap: nowrap; gap: 10px;">
+            <span style="white-space: nowrap;">设备类型:</span>
+            <el-select v-model="selectedEquipmentType" placeholder="设备类型" size="small" @change="handleEquipmentTypeChange" clearable style="flex: 1; min-width: 100px;">
+              <el-option
+                v-for="item in equipmentTypeOptions"
+                :key="item.typeId"
+                :label="item.typeName"
+                :value="item.typeId">
+              </el-option>
+            </el-select>
+
+            <span style="white-space: nowrap; margin-left: 10px;">参数:</span>
+            <el-select v-model="selectedEquipmentParam" placeholder="设备参数" size="small" @change="handleEquipmentParamChange" clearable style="flex: 1; min-width: 100px;">
+              <el-option
+                v-for="item in equipmentParamOptions"
+                :key="item.paraCode"
+                :label="item.paraName"
+                :value="item.paraCode">
+              </el-option>
+            </el-select>
+          </div>
+        </el-col>
+
+        <!-- 产线选择 -->
+        <el-col :span="6">
+          <div style="display: flex; align-items: center;">
+            <span style="white-space: nowrap; margin-right: 10px;">产线:</span>
+            <el-select
+              v-model="selectedLines"
+              multiple
+              collapse-tags
+              collapse-tags-tooltip
+              placeholder="请选择产线"
+              size="small"
+              @change="handleLineSelectionChange"
+              style="flex: 1;">
+              <el-option
+                v-for="line in allLines"
+                :key="line"
+                :label="line + '#产线'"
+                :value="line">
+              </el-option>
+            </el-select>
+          </div>
+        </el-col>
+      </el-row>
+    </el-card>
+
+    <!-- 产线和设备选择详情区域 -->
+    <el-card style="margin-bottom: 15px;" v-if="selectedLines.length > 0">
+      <!-- 同产线设备对比(选择一条产线时) -->
+      <div v-if="selectedLines.length === 1" style="display: flex; align-items: center; flex-wrap: wrap; gap: 15px; padding: 10px;">
+        <span style="font-weight: bold;">同产线设备对比:</span>
+        <span>{{ selectedLines[0] }}#产线</span>
+        <el-select
+          v-model="selectedMetrics[selectedLines[0] + '_0']"
+          placeholder="设备1"
+          size="small"
+          @change="updateQueryData"
+          style="width: 180px;"
+          clearable>
+          <el-option
+            v-for="item in equipmentList[selectedLines[0]]"
+            :key="item.id"
+            :label="item.name"
+            :value="item.id">
+          </el-option>
+        </el-select>
+        <span>VS</span>
+        <el-select
+          v-model="selectedMetrics[selectedLines[0] + '_1']"
+          placeholder="设备2"
+          size="small"
+          @change="updateQueryData"
+          style="width: 180px;"
+          clearable>
+          <el-option
+            v-for="item in equipmentList[selectedLines[0]]"
+            :key="item.id"
+            :label="item.name"
+            :value="item.id">
+          </el-option>
+        </el-select>
+      </div>
+
+      <!-- 跨产线设备对比(选择两条产线时) -->
+      <div v-if="selectedLines.length === 2" style="display: flex; align-items: center; flex-wrap: wrap; gap: 15px; padding: 10px;">
+        <template v-for="(line, index) in selectedLines" :key="line">
+          <div v-if="index > 0" style="width: 1px; height: 24px; background-color: #dcdfe6; margin: 0 10px;"></div>
+          <span style="font-weight: bold;">{{ index === 0 ? '产线A' : '产线B' }}:</span>
+          <span>{{ line }}#产线</span>
+          <el-select
+            v-model="selectedMetrics[line]"
+            placeholder="请选择设备"
+            size="small"
+            @change="updateQueryData"
+            style="width: 180px;"
+            clearable>
+            <el-option
+              v-for="item in equipmentList[line]"
+              :key="item.id"
+              :label="item.name"
+              :value="item.id">
+            </el-option>
+          </el-select>
+        </template>
+      </div>
+    </el-card>
+
+    <!-- 折线图和表格左右布局 -->
+    <el-row :gutter="15">
+      <!-- 左侧折线图 -->
+      <el-col :span="12">
+        <el-card style="margin-bottom: 15px;">
+          <div ref="chartRef0" style="width: 100%; height: 400px;"></div>
+        </el-card>
+         <el-card style="margin-bottom: 15px;">
+          <el-table :data="rzLineList" height="400" :span-method="spanMethod">
+            <el-table-column label="小时" align="center" prop="hour" width="60" />
+            <el-table-column label="设备" align="center" prop="deviceName" width="360" />
+            <el-table-column :label="rzLineList.length>0 ? rzLineList[0].paraName : ''" align="center" >
+              <template  #default="scope">
+                {{ scope.row.paraValue }}
+              </template>
+            </el-table-column>
+          </el-table>
+        </el-card>
+      </el-col>
+
+      <!-- 右侧表格 -->
+      <el-col :span="12">
+        <el-card style="margin-bottom: 15px;">
+          <div ref="chartRef1" style="width: 100%; height: 400px;"></div>
+        </el-card>
+         <el-card style="margin-bottom: 15px;">
+          <el-table :data="rzLineList1" height="400" :span-method="spanMethod">
+            <el-table-column label="小时" align="center" prop="hour" width="60" />
+            <el-table-column label="设备" align="center" prop="deviceName" width="360" />
+            <el-table-column :label="rzLineList.length>0 ? rzLineList[0].paraName : ''" align="center" >
+              <template  #default="scope">
+                {{ scope.row.paraValue }}
+              </template>
+            </el-table-column>
+          </el-table>
+        </el-card>
+      </el-col>
+    </el-row>
+
+    <!-- 分钟级数据弹窗 -->
+    <el-dialog
+      :title="minuteDialogTitle"
+      v-model="minuteDialogVisible"
+      width="80%"
+      :before-close="handleMinuteDialogClose"
+      append-to-body
+    >
+      <el-row :gutter="15">
+        <!-- 左侧分钟级折线图 -->
+        <el-col :span="12">
+          <el-card style="margin-bottom: 15px;">
+            <div ref="minuteChartRef0" style="width: 100%; height: 400px;"></div>
+          </el-card>
+          <el-card style="margin-bottom: 15px;">
+            <el-table :data="minuteTableData0" height="400">
+              <el-table-column prop="time" label="分钟" width="100" align="center"></el-table-column>
+              <el-table-column prop="value" :label="minuteDataLabel0" align="center"></el-table-column>
+            </el-table>
+          </el-card>
+        </el-col>
+
+        <!-- 右侧分钟级折线图 -->
+        <el-col :span="12">
+          <el-card style="margin-bottom: 15px;">
+            <div ref="minuteChartRef1" style="width: 100%; height: 400px;"></div>
+          </el-card>
+          <el-card style="margin-bottom: 15px;">
+            <el-table :data="minuteTableData1" height="400">
+              <el-table-column prop="time" label="分钟" width="100" align="center"></el-table-column>
+              <el-table-column prop="value" :label="minuteDataLabel1" align="center"></el-table-column>
+            </el-table>
+          </el-card>
+        </el-col>
+      </el-row>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup name="gyfx">
+import * as echarts from 'echarts';
+import { listRzLine, getRzLine, delRzLine, addRzLine, updateRzLine } from "@/api/dyeing/rzLine";
+import { listDeviceTypes } from "@/api/dyeing/gyfx"; // 添加导入listDeviceTypes接口
+import { listDevice } from "@/api/dye/device";
+import {listHour} from "@/api/dye/hour.js";
+import { ref, nextTick } from 'vue';
+
+const { proxy } = getCurrentInstance();
+
+const rzLineList = ref([]);
+const rzLineList1 = ref([]);
+const open = ref(false);
+const loading = ref(true);
+const showSearch = ref(true);
+const ids = ref([]);
+const single = ref(true);
+const multiple = ref(true);
+const total = ref(0);
+const title = ref("");
+const chartRef0 = ref(null);
+const chartRef1 = ref(null);
+let chartInstance0 = null;
+let chartInstance1 = null;
+
+// 分钟级数据相关变量
+const minuteDialogVisible = ref(false);
+const minuteDialogTitle = ref('');
+// 左侧分钟级数据
+const minuteChartRef0 = ref(null);
+let minuteChartInstance0 = null;
+const minuteTableData0 = ref([]);
+const minuteDataLabel0 = ref('');
+// 右侧分钟级数据
+const minuteChartRef1 = ref(null);
+let minuteChartInstance1 = null;
+const minuteTableData1 = ref([]);
+const minuteDataLabel1 = ref('');
+
+const data = reactive({
+  form: {},
+  queryParams: {
+    pageNum: 1,
+    pageSize: 10000,
+    dataDate: new Date(new Date().getTime() - 24 * 60 * 60 * 1000).Format('yyyy-MM-dd'),
+    hour: null,
+    lines: null,
+    openRate: null,
+    length: null,
+    tmp: null,
+    speed: null,
+    energy: null,
+    amp: null,
+    createdBy: null,
+    createdTime: null,
+    updatedBy: null,
+    updatedTime: null,
+    remark: null
+  },
+  rules: {
+  }
+});
+
+const selectedEquipmentType = ref('');
+const selectedEquipmentParam = ref('');
+const selectedMetrics = ref({});
+const selectedLines = ref([]);  // 多条产线选择
+const equipmentList = ref({});
+const equipmentTypeList = ref([]);
+const equipmentParamOptions = ref([]);
+const equipmentTypeOptions = ref([]);
+const allLines = ref(['1', '2', '3', '4', '5', '6', '7', '8']);
+const oldSelectLines = ref([]);
+
+const { queryParams, form, rules } = toRefs(data);
+
+// 按产线分组的数据
+const groupedRzLineList = computed(() => {
+  const grouped = {};
+  rzLineList.value.forEach(item => {
+    if (!grouped[item.line]) {
+      grouped[item.line] = [];
+    }
+    grouped[item.line].push(item);
+  });
+  // 按产线编号排序
+  const sortedGrouped = {};
+  Object.keys(grouped).sort().forEach(key => {
+    sortedGrouped[key] = grouped[key];
+  });
+  return sortedGrouped;
+});
+
+// 合并单元格处理函数
+const spanMethod = ({ row, column, rowIndex, columnIndex }) => {
+  if (columnIndex === 0) { // 小时列
+    // 获取当前行小时值
+    const currentHour = row.hour;
+
+    // 计算当前小时值第一次出现的位置
+    let firstIndex = -1;
+    for (let i = 0; i < rzLineList.value.length; i++) {
+      if (rzLineList.value[i].hour === currentHour) {
+        firstIndex = i;
+        break;
+      }
+    }
+
+    // 如果当前行是该小时值第一次出现的位置
+    if (firstIndex === rowIndex) {
+      // 计算该小时值连续出现的次数
+      let spanCount = 0;
+      for (let i = firstIndex; i < rzLineList.value.length; i++) {
+        if (rzLineList.value[i].hour === currentHour) {
+          spanCount++;
+        } else {
+          break;
+        }
+      }
+      return [spanCount, 1];
+    } else {
+      // 如果不是第一次出现,隐藏该单元格
+      return [0, 0];
+    }
+  }
+  return [1, 1]; // 其他列不合并
+};
+function navigateDay(offset) {
+  const currentDate = new Date(queryParams.value.dataDate);
+  currentDate.setDate(currentDate.getDate() + offset);
+  queryParams.value.dataDate = currentDate.toISOString().split('T')[0];
+  handleQuery();
+}
+/** 查询染整线产线小时统计数据列表 */
+function getList() {
+  // 先获取设备类型列表
+  listDeviceTypes({pageSize: 10000}).then(response => {
+    equipmentTypeList.value = response.rows;
+    // 提取设备类型选项
+    equipmentTypeOptions.value = response.rows.map(item => {
+      return {
+        typeId: item.typeId,
+        typeName: item.typeName
+      };
+    });
+
+    // 默认选择第一条设备类型
+    if (response.rows.length > 0 && !selectedEquipmentType.value) {
+      selectedEquipmentType.value = response.rows[0].typeId;
+      // 触发设备类型变化事件,加载对应的参数
+      handleEquipmentTypeChange(selectedEquipmentType.value);
+    }
+  });
+}
+
+function difference(arr1, arr2) {
+  const set2 = new Set(arr2);
+  return arr1.filter(item => !set2.has(item));
+}
+
+/**
+ * 获取设备列表
+ * @returns {void}
+ */
+function getDeviceList(){
+  // 检查是否选择了产线
+  if (selectedLines.value.length === 0) {
+    proxy.$modal.msgWarning("请至少选择一条产线进行对比");
+    return;
+  }
+  if (selectedLines.value.length > 2) {
+    let tmp = difference(selectedLines.value, oldSelectLines.value);
+    selectedLines.value = selectedLines.value.filter(item => !tmp.includes(item));
+    oldSelectLines.value = JSON.parse(JSON.stringify(selectedLines.value));
+    proxy.$modal.msgWarning("最多只能选择两条产线进行对比");
+    return;
+  }
+
+  // 处理取消选择产线的情况
+  if (selectedLines.value.length < oldSelectLines.value.length) {
+    // 取消了一条产线
+    let tmp = difference(oldSelectLines.value, selectedLines.value);
+    // 清除取消产线的相关设备选择
+    if (selectedMetrics.value[tmp[0]]) {
+      selectedMetrics.value[tmp[0]] = '';
+    }
+    // 如果是单一产线情况,清除相关的设备选择
+    if (selectedMetrics.value[tmp[0] + '_0']) selectedMetrics.value[tmp[0] + '_0'] = '';
+    if (selectedMetrics.value[tmp[0] + '_1']) selectedMetrics.value[tmp[0] + '_1'] = '';
+    updateQueryData();
+  }
+
+  // 检查是否选择了设备类型和参数
+  if (!selectedEquipmentType.value) {
+    proxy.$modal.msgError("请先选择设备类型");
+    return;
+  }
+  if (!selectedEquipmentParam.value) {
+    proxy.$modal.msgError("请先选择设备参数");
+    return;
+  }
+
+  // 为所有选中的产线获取设备列表
+  selectedLines.value.forEach(line => {
+    listDevice({
+      pageSize: 10000,
+      pageNum: 1,
+      paraCode: selectedEquipmentParam.value,
+      typeId: selectedEquipmentType.value,
+      line: line
+    }).then(response => {
+      // 提取设备参数选项
+      equipmentList.value[line] = response.rows.map(item => {
+        return {
+          id: item.deviceId,
+          name: item.deviceName
+        };
+      });
+      
+      // 默认选中第一台设备
+      let needUpdateData = false;
+      if (response.rows.length > 0) {
+        // 如果是单条产线情况,设置同产线设备对比的第一台设备
+        if (selectedLines.value.length === 1) {
+          const lineKey = selectedLines.value[0];
+          if (!selectedMetrics.value[lineKey + '_0']) {
+            selectedMetrics.value[lineKey + '_0'] = response.rows[0].deviceId;
+            needUpdateData = true;
+          }
+        } 
+        // 如果是两条产线情况,设置对应产线的第一台设备
+        else if (selectedLines.value.length === 2) {
+          if (!selectedMetrics.value[line]) {
+            selectedMetrics.value[line] = response.rows[0].deviceId;
+            needUpdateData = true;
+          }
+        }
+      }
+      
+      // 如果设置了默认设备,则更新数据
+      if (needUpdateData) {
+        updateQueryData();
+      }
+    });
+  });
+
+  oldSelectLines.value = JSON.parse(JSON.stringify(selectedLines.value));
+  // 如果选择了产线,更新图表
+  if (selectedLines.value.length >= 1 && Object.keys(selectedMetrics.value).length > 0) {
+    // 检查是否所有产线都已设置设备,如果是则更新数据
+    let allLinesHaveDevices = true;
+    if (selectedLines.value.length === 1) {
+      const lineKey = selectedLines.value[0];
+      if (!selectedMetrics.value[lineKey + '_0']) {
+        allLinesHaveDevices = false;
+      }
+    } else if (selectedLines.value.length === 2) {
+      for (const line of selectedLines.value) {
+        if (!selectedMetrics.value[line]) {
+          allLinesHaveDevices = false;
+          break;
+        }
+      }
+    }
+    
+    if (allLinesHaveDevices) {
+      updateQueryData();
+    }
+  }
+}
+
+function updateQueryData() {
+  initChart();
+  rzLineList.value = [];
+  rzLineList1.value = [];
+
+  let queryPara = {
+    paraCode: selectedEquipmentParam.value,
+    typeId: selectedEquipmentType.value,
+    dataDate: queryParams.value.dataDate,
+    pageSize: 10000,
+    pageNum: 1,
+  };
+
+  // 情况1:选择了一条产线,需要比较该产线下的两个设备
+  if (selectedLines.value.length == 1) {
+    const line = selectedLines.value[0];
+    // 检查是否选择了两个设备进行对比
+    if (selectedMetrics.value[line + '_0'] && selectedMetrics.value[line + '_1']) {
+      // 查询第一个设备的数据
+      queryPara['line'] = line;
+      queryPara['deviceId'] = selectedMetrics.value[line + '_0'];
+      listHour(queryPara).then(res => {
+        rzLineList.value = res.rows;
+        nextTick(() => {
+          updateChart();
+        });
+      });
+
+      // 查询第二个设备的数据
+      queryPara['deviceId'] = selectedMetrics.value[line + '_1'];
+      listHour(queryPara).then(res => {
+        rzLineList1.value = res.rows;
+        nextTick(() => {
+          updateChart1();
+        });
+      });
+    }
+  }
+  // 情况2:选择了两条产线,每条产线选择一个设备进行对比
+  else if (selectedLines.value.length == 2) {
+    // 查询第一条产线设备的数据
+    if (selectedMetrics.value[selectedLines.value[0]]) {
+      queryPara['line'] = selectedLines.value[0];
+      queryPara['deviceId'] = selectedMetrics.value[selectedLines.value[0]];
+      listHour(queryPara).then(res => {
+        rzLineList.value = res.rows;
+        nextTick(() => {
+          updateChart();
+        });
+      });
+    }
+
+    // 查询第二条产线设备的数据
+    if (selectedMetrics.value[selectedLines.value[1]]) {
+      queryPara['line'] = selectedLines.value[1];
+      queryPara['deviceId'] = selectedMetrics.value[selectedLines.value[1]];
+      listHour(queryPara).then(res => {
+        rzLineList1.value = res.rows;
+        nextTick(() => {
+          updateChart1();
+        });
+      });
+    }
+  }
+}
+
+// 初始化图表
+function initChart() {
+  if (chartInstance0) {
+    chartInstance0.dispose();
+  }
+  if (chartInstance1) {
+    chartInstance1.dispose();
+  }
+  if (chartRef0.value) {
+    chartInstance0 = echarts.init(chartRef0.value);
+  }
+  if (chartRef1.value) {
+    chartInstance1 = echarts.init(chartRef1.value);
+  }
+  //建立图表联动
+  if (chartInstance0 && chartInstance1) {
+    echarts.connect([
+      chartInstance0,
+      chartInstance1,
+    ]);
+  }
+}
+
+// 初始化分钟级数据图表
+function initMinuteChart0(minuteData, targetData) {
+  if (minuteChartInstance0) {
+    minuteChartInstance0.dispose();
+  }
+
+  if (minuteChartRef0.value) {
+    minuteChartInstance0 = echarts.init(minuteChartRef0.value);
+
+    // 准备分钟级数据
+    const timePoints = minuteData.map(item => item.time);
+    const values = minuteData.map(item => item.value);
+
+    const option = {
+      title: {
+        text: targetData.deviceName + ' ' + targetData.paraName,
+        left: 'center'
+      },
+      tooltip: {
+        trigger: 'axis',
+        formatter: function (params) {
+          let result = params[0].axisValue + '分<br/>';
+          params.forEach(param => {
+            let valueText = '';
+            valueText = param.value !== null ? param.value : '无数据';
+            result += `<div style="display:flex;align-items:center;">
+              <span style="display:inline-block;margin-right:5px;border-radius:10px;width:10px;height:10px;background-color:${param.color};"></span>
+              ${param.seriesName}: ${valueText}
+            </div>`;
+          });
+          return result;
+        }
+      },
+      legend:{
+        top: 40 ,
+        left: 'center',
+        textStyle: {
+          fontSize: 10
+        },
+        itemWidth: 10,
+        itemHeight: 10
+      },
+      grid: {
+        left: '3%',
+        right: '4%',
+        top: '25%',
+        bottom: 20,
+        containLabel: true
+      },
+      xAxis: {
+        type: 'category',
+        boundaryGap: false,
+        data: timePoints
+      },
+      yAxis: {
+        type: 'value',
+        name: targetData.paraName
+      },
+      series: [{
+        name: targetData.deviceName + '-' + targetData.paraName,
+        type: 'line',
+        data: values,
+        smooth: true,
+        showSymbol: false
+      }]
+    };
+
+    minuteChartInstance0.setOption(option);
+  }
+}
+
+// 初始化分钟级数据图表1
+function initMinuteChart1(minuteData, targetData) {
+  if (minuteChartInstance1) {
+    minuteChartInstance1.dispose();
+  }
+
+  if (minuteChartRef1.value) {
+    minuteChartInstance1 = echarts.init(minuteChartRef1.value);
+
+    // 准备分钟级数据
+    const timePoints = minuteData.map(item => item.time);
+    const values = minuteData.map(item => item.value);
+
+    const option = {
+      title: {
+        text: targetData.deviceName + ' ' + targetData.paraName,
+        left: 'center'
+      },
+      tooltip: {
+        trigger: 'axis',
+        formatter: function (params) {
+          let result = params[0].axisValue + '分<br/>';
+          params.forEach(param => {
+            let valueText = '';
+            valueText = param.value !== null ? param.value : '无数据';
+            result += `<div style="display:flex;align-items:center;">
+              <span style="display:inline-block;margin-right:5px;border-radius:10px;width:10px;height:10px;background-color:${param.color};"></span>
+              ${param.seriesName}: ${valueText}
+            </div>`;
+          });
+          return result;
+        }
+      },
+      legend:{
+        top: 40 ,
+        left: 'center',
+        textStyle: {
+          fontSize: 10
+        },
+        itemWidth: 10,
+        itemHeight: 10
+      },
+      grid: {
+        left: '3%',
+        right: '4%',
+        top: '25%',
+        bottom: 20,
+        containLabel: true
+      },
+      xAxis: {
+        type: 'category',
+        boundaryGap: false,
+        data: timePoints
+      },
+      yAxis: {
+        type: 'value',
+        name: targetData.paraName
+      },
+      series: [{
+        name: targetData.deviceName + '-' + targetData.paraName,
+        type: 'line',
+        data: values,
+        smooth: true,
+        showSymbol: false
+      }]
+    };
+
+    minuteChartInstance1.setOption(option);
+  }
+}
+
+// 更新图表0
+function updateChart() {
+  if (chartInstance0 && rzLineList.value.length > 0) {
+    const hours = rzLineList.value.map(item => item.hour.toString().padStart(2, '0') + ':00');
+    const values = rzLineList.value.map(item => item.paraValue);
+
+    const option = {
+      title: {
+        text: rzLineList.value[0].deviceName + ' ' + rzLineList.value[0].paraName,
+        left: 'center'
+      },
+      tooltip: {
+        trigger: 'axis',
+        formatter: function (params) {
+          let result = params[0].axisValue + '<br/>';
+          params.forEach(param => {
+            let valueText = '';
+            valueText = param.value !== null ? param.value : '无数据';
+            result += `<div style="display:flex;align-items:center;">
+              <span style="display:inline-block;margin-right:5px;border-radius:10px;width:10px;height:10px;background-color:${param.color};"></span>
+              ${param.seriesName}: ${valueText}
+            </div>`;
+          });
+          return result;
+        }
+      },
+      legend:{
+        top: 40 ,
+        left: 'center',
+        textStyle: {
+          fontSize: 10
+        },
+        itemWidth: 10,
+        itemHeight: 10
+      },
+      grid: {
+        left: '3%',
+        right: '4%',
+        top: '25%',
+        bottom: 20,
+        containLabel: true
+      },
+      xAxis: {
+        type: 'category',
+        boundaryGap: false,
+        data: hours
+      },
+      yAxis: {
+        type: 'value',
+        name: rzLineList.value[0].paraName
+      },
+      series: [{
+        name: rzLineList.value[0].deviceName,
+        type: 'line',
+        data: values,
+        smooth: true,
+        showSymbol: false
+      }]
+    };
+
+    chartInstance0.setOption(option);
+
+    // 先移除之前的点击事件监听,避免重复绑定
+    chartInstance0.off('click');
+    // 添加点击事件监听
+    chartInstance0.on('click', (params) => {
+      handleChartClick(params, 'chart0');
+    });
+  } else if (chartInstance0) {
+    // 如果没有数据,清空图表
+    chartInstance0.setOption({
+      title: {
+        text: '暂无数据',
+        left: 'center',
+        top: 'center'
+      },
+      xAxis: {
+        type: 'category',
+        data: []
+      },
+      yAxis: {
+        type: 'value'
+      },
+      series: []
+    });
+
+    // 移除点击事件监听
+    chartInstance0.off('click');
+  }
+}
+
+// 更新图表1
+function updateChart1() {
+  if (chartInstance1 && rzLineList1.value.length > 0) {
+    const hours = rzLineList1.value.map(item => item.hour.toString().padStart(2, '0') + ':00');
+    const values = rzLineList1.value.map(item => item.paraValue);
+
+    const option = {
+      title: {
+        text: rzLineList1.value[0].deviceName + ' ' + rzLineList1.value[0].paraName,
+        left: 'center'
+      },
+      tooltip: {
+        trigger: 'axis',
+        formatter: function (params) {
+          let result = params[0].axisValue + '<br/>';
+          params.forEach(param => {
+            let valueText = '';
+            valueText = param.value !== null ? param.value : '无数据';
+            result += `<div style="display:flex;align-items:center;">
+              <span style="display:inline-block;margin-right:5px;border-radius:10px;width:10px;height:10px;background-color:${param.color};"></span>
+              ${param.seriesName}: ${valueText}
+            </div>`;
+          });
+          return result;
+        }
+      },
+      legend:{
+        top: 40 ,
+        left: 'center',
+        textStyle: {
+          fontSize: 10
+        },
+        itemWidth: 10,
+        itemHeight: 10
+      },
+      grid: {
+        left: '3%',
+        right: '4%',
+        top: '25%',
+        bottom: 20,
+        containLabel: true
+      },
+      xAxis: {
+        type: 'category',
+        boundaryGap: false,
+        data: hours
+      },
+      yAxis: {
+        type: 'value',
+        name: rzLineList1.value[0].paraName
+      },
+      series: [{
+        name: rzLineList1.value[0].deviceName,
+        type: 'line',
+        data: values,
+        smooth: true,
+        showSymbol: false
+      }]
+    };
+
+    chartInstance1.setOption(option);
+
+    // 先移除之前的点击事件监听,避免重复绑定
+    chartInstance1.off('click');
+    // 添加点击事件监听
+    chartInstance1.on('click', (params) => {
+      handleChartClick(params, 'chart1');
+    });
+  } else if (chartInstance1) {
+    // 如果没有数据,清空图表
+    chartInstance1.setOption({
+      title: {
+        text: '暂无数据',
+        left: 'center',
+        top: 'center'
+      },
+      xAxis: {
+        type: 'category',
+        data: []
+      },
+      yAxis: {
+        type: 'value'
+      },
+      series: []
+    });
+
+    // 移除点击事件监听
+    chartInstance1.off('click');
+  }
+}
+
+// 图表点击事件处理函数
+function handleChartClick(params, chartType) {
+  // 获取点击的小时
+  const hour = params.name;
+
+  // 查找对应小时的数据(无论点击的是哪个图表,都要显示两个设备的数据)
+  // 尝试多种匹配方式来查找数据
+  let targetData0 = null;
+  let targetData1 = null;
+
+  // 对于第一个数据集
+  for (let i = 0; i < rzLineList.value.length; i++) {
+    const item = rzLineList.value[i];
+    const itemHour = item.hour.toString().padStart(2, '0');
+    const clickedHour = hour.replace(':00', ''); // 去掉可能的 ':00' 后缀
+
+    if (itemHour === clickedHour) {
+      targetData0 = item;
+      break;
+    }
+  }
+
+  // 对于第二个数据集
+  for (let i = 0; i < rzLineList1.value.length; i++) {
+    const item = rzLineList1.value[i];
+    const itemHour = item.hour.toString().padStart(2, '0');
+    const clickedHour = hour.replace(':00', ''); // 去掉可能的 ':00' 后缀
+
+    if (itemHour === clickedHour) {
+      targetData1 = item;
+      break;
+    }
+  }
+
+  // 检查两个设备是否都有对应小时的数据
+  if ((targetData0 && targetData0.paraMValue) || (targetData1 && targetData1.paraMValue)) {
+    try {
+      // 设置对话框标题
+      minuteDialogTitle.value = `小时 ${hour} 的分钟级数据对比`;
+
+      // 处理左侧设备的分钟级数据
+      if (targetData0 && targetData0.paraMValue) {
+        let minuteData0;
+        try {
+          minuteData0 = typeof targetData0.paraMValue === 'string'
+            ? JSON.parse(targetData0.paraMValue)
+            : targetData0.paraMValue;
+        } catch (e) {
+          // 尝试其他格式
+          if (Array.isArray(targetData0.paraMValue)) {
+            minuteData0 = targetData0.paraMValue;
+          } else {
+            minuteData0 = [];
+          }
+        }
+
+        // 设置左侧分钟级数据
+        minuteTableData0.value = minuteData0.map(item => ({
+          time: item.time,
+          value: item.value
+        }));
+        minuteDataLabel0.value = targetData0.paraName;
+
+        // 初始化左侧分钟级图表
+        nextTick(() => {
+          initMinuteChart0(minuteData0, targetData0);
+          // 建立分钟级图表联动
+          if (minuteChartInstance0 && minuteChartInstance1) {
+            echarts.connect([minuteChartInstance0, minuteChartInstance1]);
+          }
+        });
+      } else {
+        // 如果没有数据,清空左侧内容
+        minuteTableData0.value = [];
+        minuteDataLabel0.value = '';
+        if (minuteChartInstance0) {
+          minuteChartInstance0.dispose();
+          minuteChartInstance0 = null;
+        }
+      }
+
+      // 处理右侧设备的分钟级数据
+      if (targetData1 && targetData1.paraMValue) {
+        let minuteData1;
+        try {
+          minuteData1 = typeof targetData1.paraMValue === 'string'
+            ? JSON.parse(targetData1.paraMValue)
+            : targetData1.paraMValue;
+        } catch (e) {
+          // 尝试其他格式
+          if (Array.isArray(targetData1.paraMValue)) {
+            minuteData1 = targetData1.paraMValue;
+          } else {
+            minuteData1 = [];
+          }
+        }
+
+        // 设置右侧分钟级数据
+        minuteTableData1.value = minuteData1.map(item => ({
+          time: item.time,
+          value: item.value
+        }));
+        minuteDataLabel1.value = targetData1.paraName;
+
+        // 初始化右侧分钟级图表
+        nextTick(() => {
+          initMinuteChart1(minuteData1, targetData1);
+          // 建立分钟级图表联动
+          if (minuteChartInstance0 && minuteChartInstance1) {
+            echarts.connect([minuteChartInstance0, minuteChartInstance1]);
+          }
+        });
+      } else {
+        // 如果没有数据,清空右侧内容
+        minuteTableData1.value = [];
+        minuteDataLabel1.value = '';
+        if (minuteChartInstance1) {
+          minuteChartInstance1.dispose();
+          minuteChartInstance1 = null;
+        }
+      }
+
+      // 显示对话框
+      showMinuteDialog();
+    } catch (error) {
+      proxy.$modal.msgError("数据解析失败");
+    }
+  } else {
+    // 显示提示信息
+    proxy.$modal.msgWarning("该小时没有分钟级数据");
+  }
+}
+
+// 窗口大小改变时重置图表大小
+function resizeChart() {
+  if (chartInstance0) {
+    chartInstance0.resize();
+  }
+  if (chartInstance1) {
+    chartInstance1.resize();
+  }
+  if (minuteChartInstance0) {
+    minuteChartInstance0.resize();
+  }
+  if (minuteChartInstance1) {
+    minuteChartInstance1.resize();
+  }
+}
+
+// 关闭分钟级数据弹窗
+function handleMinuteDialogClose() {
+  minuteDialogVisible.value = false;
+}
+
+// 显示分钟级数据弹窗
+function showMinuteDialog() {
+  minuteDialogVisible.value = true;
+}
+
+onMounted(() => {
+  initChart();
+  window.addEventListener('resize', resizeChart);
+
+  // 默认选择所有产线
+  //selectedLines.value = [...allLines.value];
+
+  // 初始加载数据
+  getList();
+
+  // 添加一个微任务确保图表正确初始化
+  nextTick(() => {
+    if (!chartInstance0 && chartRef0.value) {
+      chartInstance0 = echarts.init(chartRef0.value);
+    }
+    if (!chartInstance1 && chartRef1.value) {
+      chartInstance1 = echarts.init(chartRef1.value);
+    }
+  });
+});
+
+onUnmounted(() => {
+  window.removeEventListener('resize', resizeChart);
+  if (chartInstance0) {
+    chartInstance0.dispose();
+  }
+  if (chartInstance1) {
+    chartInstance1.dispose();
+  }
+  if (minuteChartInstance0) {
+    minuteChartInstance0.dispose();
+  }
+  if (minuteChartInstance1) {
+    minuteChartInstance1.dispose();
+  }
+});
+
+onActivated(() => {
+  resizeChart();
+});
+
+// 取消按钮
+function cancel() {
+  open.value = false;
+  reset();
+}
+
+// 表单重置
+function reset() {
+  form.value = {
+    id: null,
+    dataDate: null,
+    hour: null,
+    line: null,
+    openRate: null,
+    length: null,
+    tmp: null,
+    speed: null,
+    energy: null,
+    amp: null,
+    createdBy: null,
+    createdTime: null,
+    updatedBy: null,
+    updatedTime: null,
+    remark: null
+  };
+  proxy.resetForm("rzLineRef");
+}
+
+/** 搜索按钮操作 */
+function handleQuery() {
+  queryParams.value.pageNum = 1;
+  // 检查查询条件
+  if (!queryParams.value.dataDate) {
+    proxy.$modal.msgWarning("请选择日期");
+    return;
+  }
+  // 不要重置已选择的产线和设备,仅更新数据
+  updateQueryData();
+}
+
+/** 重置按钮操作 */
+function resetQuery() {
+  proxy.resetForm("queryRef");
+  // 设置默认日期为当前日期
+  const now = new Date();
+  const year = now.getFullYear();
+  const month = (now.getMonth() + 1).toString().padStart(2, '0');
+  const day = now.getDate().toString().padStart(2, '0');
+  queryParams.value.dataDate = `${year}-${month}-${day}`;
+
+  // 重置产线和设备选择
+  selectedLines.value = [];
+  selectedMetrics.value = {};
+  equipmentList.value = {};
+
+  handleQuery();
+}
+// 处理设备类型变化
+function handleEquipmentTypeChange(val) {
+  // 清空已选的设备参数
+  selectedEquipmentParam.value = '';
+
+  // 如果有选择设备类型,则更新设备参数选项
+  if (val) {
+    // 在设备类型列表中找到当前选中的设备类型
+    const selectedType = equipmentTypeList.value.find(item => item.typeId === val);
+    if (selectedType && selectedType.dyeTypeParaList) {
+      // 提取设备参数选项
+      equipmentParamOptions.value = selectedType.dyeTypeParaList.map(item => {
+        return {
+          paraCode: item.paraCode,
+          paraName: item.paraName
+        };
+      });
+
+      // 默认选择第一条设备参数
+      if (selectedType.dyeTypeParaList.length > 0 && !selectedEquipmentParam.value) {
+        selectedEquipmentParam.value = selectedType.dyeTypeParaList[0].paraCode;
+        // 触发设备参数变化事件
+        handleEquipmentParamChange();
+      }
+    } else {
+      equipmentParamOptions.value = [];
+    }
+  } else {
+    // 如果没有选择设备类型,清空设备参数选项
+    equipmentParamOptions.value = [];
+  }
+}
+
+// 处理设备参数变化
+function handleEquipmentParamChange() {
+  // 清空设备列表和已选择的设备
+  equipmentList.value = {};
+  selectedMetrics.value = {};
+  
+  // 获取新设备列表
+  getDeviceList();
+}
+
+// 处理产线选择变化
+function handleLineSelectionChange(val) {
+  if (val.length > 2) {
+    // 限制最多选择两条产线
+    selectedLines.value = val.slice(0, 2);
+    proxy.$modal.msgWarning("最多只能选择两条产线进行对比");
+  }
+  // 当取消选择所有产线时,清空相关设备选择
+  if (val.length === 0) {
+    selectedMetrics.value = {};
+  }
+  getDeviceList();
+}
+
+/** 新增按钮操作 */
+function handleAdd() {
+  reset();
+  open.value = true;
+  title.value = "添加染整线产线小时统计数据";
+}
+
+/** 修改按钮操作 */
+function handleUpdate(row) {
+  reset();
+  const _id = row.id || ids.value
+  getRzLine(_id).then(response => {
+    form.value = response.data;
+    open.value = true;
+    title.value = "修改染整线产线小时统计数据";
+  });
+}
+
+/** 提交按钮 */
+function submitForm() {
+  proxy.$refs["rzLineRef"].validate(valid => {
+    if (valid) {
+      if (form.value.id != null) {
+        updateRzLine(form.value).then(response => {
+          proxy.$modal.msgSuccess("修改成功");
+          open.value = false;
+          getList();
+        });
+      } else {
+        addRzLine(form.value).then(response => {
+          proxy.$modal.msgSuccess("新增成功");
+          open.value = false;
+          getList();
+        });
+      }
+    }
+  });
+}
+
+/** 删除按钮操作 */
+function handleDelete(row) {
+  const _ids = row.id || ids.value;
+  proxy.$modal.confirm('是否确认删除染整线产线小时统计数据编号为"' + _ids + '"的数据项?').then(function() {
+    return delRzLine(_ids);
+  }).then(() => {
+    getList();
+    proxy.$modal.msgSuccess("删除成功");
+  }).catch(() => {});
+}
+
+/** 导出按钮操作 */
+function handleExport() {
+  proxy.download('dyeing/rzLine/export', {
+    ...queryParams.value
+  }, `rzLine_${new Date().getTime()}.xlsx`)
+}
+
+getList();
+</script>

+ 1243 - 0
src/views/dye/line/calc.vue

@@ -0,0 +1,1243 @@
+<template>
+  <div class="app-container">
+    <el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch" label-width="68px"
+             style="text-align: center;">
+      <el-form-item style="width: 208px; display: inline-block;">
+        <div style="display: flex; align-items: center; justify-content: center;">
+          <el-button icon="ArrowLeft" @click="navigateDay(-1)" size="small" style="margin-right: 15px;"></el-button>
+          <el-date-picker clearable
+                          v-model="queryParams.dataDate"
+                          type="date"
+                          value-format="YYYY-MM-DD"
+                          placeholder="请选择日期"
+                          style="width: 130px"
+                          @change="handleQuery">
+          </el-date-picker>
+          <el-button icon="ArrowRight" @click="navigateDay(1)" size="small" style="margin-left: 15px;"></el-button>
+        </div>
+      </el-form-item>
+    </el-form>
+
+    <!-- 产线选择 -->
+    <el-row :gutter="20" style="margin-bottom: 20px;">
+      <el-col :span="24">
+        <el-card>
+          <div style="display: flex; align-items: center; flex-wrap: wrap; gap: 10px; margin-bottom: 10px;">
+            <span>选择产线进行对比(最多2条):</span>
+            <el-button size="small" @click="selectAllLines">全选</el-button>
+            <el-button size="small" @click="invertSelection">反选</el-button>
+            <el-checkbox-group v-model="selectedLines" @change="handleLineSelectionChange" style="padding-left: 30px;">
+              <el-checkbox v-for="line in allLines" :key="line" :label="line">{{ line }}</el-checkbox>
+            </el-checkbox-group>
+          </div>
+          <div style="display: flex; align-items: center; flex-wrap: wrap; gap: 10px; margin-bottom: 10px;">
+            <span>选择指标:</span>
+            <el-button size="small" @click="selectAllMetrics">全选</el-button>
+            <el-button size="small" @click="invertMetrics">反选</el-button>
+            <el-checkbox-group v-model="selectedMetrics" @change="updateQueryData" style="padding-left: 30px;">
+              <el-checkbox label="速度">速度</el-checkbox>
+              <el-checkbox label="电量">电量</el-checkbox>
+              <el-checkbox label="电流">电流</el-checkbox>
+            </el-checkbox-group>
+          </div>
+        </el-card>
+      </el-col>
+    </el-row>
+
+    <!-- 折线图和表格左右布局 -->
+    <el-row :gutter="20">
+      <!-- 左侧产线图表和表格 -->
+      <el-col :span="12">
+        <el-card style="margin-bottom: 20px;">
+          <div ref="chartRef0" style="width: 100%; height: 400px;"></div>
+        </el-card>
+        <el-card style="margin-bottom: 20px;">
+          <el-table :data="lineDataList0" height="400">
+            <el-table-column label="小时" align="center" prop="hour" width="60"/>
+            <el-table-column label="产线" align="center" prop="line" width="60"/>
+            <el-table-column label="速度(米/min)" align="center" prop="speed"/>
+            <el-table-column label="电量(kW·h)" align="center" prop="energy"/>
+            <el-table-column label="电流(A)" align="center" prop="amp"/>
+          </el-table>
+        </el-card>
+      </el-col>
+
+      <!-- 右侧产线图表和表格 -->
+      <el-col :span="12">
+        <el-card style="margin-bottom: 20px;">
+          <div ref="chartRef1" style="width: 100%; height: 400px;"></div>
+        </el-card>
+        <el-card style="margin-bottom: 20px;">
+          <el-table :data="lineDataList1" height="400">
+            <el-table-column label="小时" align="center" prop="hour" width="60"/>
+            <el-table-column label="产线" align="center" prop="line" width="60"/>
+            <el-table-column label="速度(米/min)" align="center" prop="speed"/>
+            <el-table-column label="电量(kW·h)" align="center" prop="energy"/>
+            <el-table-column label="电流(A)" align="center" prop="amp"/>
+          </el-table>
+        </el-card>
+      </el-col>
+    </el-row>
+
+    <!-- 分钟级数据弹窗 -->
+    <el-dialog
+      :title="minuteDialogTitle"
+      v-model="minuteDialogVisible"
+      width="80%"
+      :before-close="handleMinuteDialogClose"
+      append-to-body
+    >
+      <el-row :gutter="15">
+        <!-- 左侧分钟级折线图 -->
+        <el-col :span="12">
+          <el-card style="margin-bottom: 15px;">
+            <div ref="minuteChartRef0" style="width: 100%; height: 400px;"></div>
+          </el-card>
+          <el-card style="margin-bottom: 15px;">
+            <el-table :data="minuteTableData0" height="400" ref="tableRef0" @scroll="handleTableScroll">
+              <el-table-column prop="time" label="分钟" width="100" align="center"></el-table-column>
+              <el-table-column prop="speed" label="速度(米/min)" align="center"></el-table-column>
+              <el-table-column prop="energy" label="电量(kW·h)" align="center"></el-table-column>
+              <el-table-column prop="amp" label="电流(A)" align="center"></el-table-column>
+            </el-table>
+          </el-card>
+        </el-col>
+
+        <!-- 右侧分钟级折线图 -->
+        <el-col :span="12">
+          <el-card style="margin-bottom: 15px;">
+            <div ref="minuteChartRef1" style="width: 100%; height: 400px;"></div>
+          </el-card>
+          <el-card style="margin-bottom: 15px;">
+            <el-table :data="minuteTableData1" height="400" ref="tableRef1" @scroll="handleTableScroll">
+              <el-table-column prop="time" label="分钟" width="100" align="center"></el-table-column>
+              <el-table-column prop="speed" label="速度(米/min)" align="center"></el-table-column>
+              <el-table-column prop="energy" label="电量(kW·h)" align="center"></el-table-column>
+              <el-table-column prop="amp" label="电流(A)" align="center"></el-table-column>
+            </el-table>
+          </el-card>
+        </el-col>
+      </el-row>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup name="lineCalc">
+import * as echarts from 'echarts';
+import {listLine} from "@/api/dye/line";
+
+const {proxy} = getCurrentInstance();
+
+const lineDataList0 = ref([]);
+const lineDataList1 = ref([]);
+const open = ref(false);
+const loading = ref(true);
+const showSearch = ref(true);
+const ids = ref([]);
+const single = ref(true);
+const multiple = ref(true);
+const total = ref(0);
+const title = ref("");
+const chartRef0 = ref(null);
+const chartRef1 = ref(null);
+let chartInstance0 = null;
+let chartInstance1 = null;
+
+// 分钟级数据相关变量
+const minuteDialogVisible = ref(false);
+const minuteDialogTitle = ref('');
+const minuteChartRef0 = ref(null);
+let minuteChartInstance0 = null;
+const minuteTableData0 = ref([]);
+const minuteDataLabel0 = ref(''); // 不再使用
+const minuteChartRef1 = ref(null);
+let minuteChartInstance1 = null;
+const minuteTableData1 = ref([]);
+const minuteDataLabel1 = ref(''); // 不再使用
+
+// 表格滚动同步相关变量
+const tableRef0 = ref(null);
+const tableRef1 = ref(null);
+let isSyncing = false; // 防止循环触发
+
+// 用于存储事件处理函数的变量,以便后续移除
+let leftTableScrollHandler = null;
+let rightTableScrollHandler = null;
+
+const data = reactive({
+  form: {},
+  queryParams: {
+    pageNum: 1,
+    pageSize: 10000,
+    dataDate: new Date(new Date().getTime() - 24 * 60 * 60 * 1000).Format('yyyy-MM-dd'),
+    hour: null,
+    lines: null,
+    openRate: null,
+    length: null,
+    tmp: null,
+    speed: null,
+    energy: null,
+    amp: null,
+    createdBy: null,
+    createdTime: null,
+    updatedBy: null,
+    updatedTime: null,
+    remark: null
+  },
+  rules: {}
+});
+
+const selectedMetrics = ref(['速度', '电量', '电流']);
+const selectedLines = ref(['5', '6']); // 默认选中5号和6号产线
+const allLines = ref(['1', '2', '3', '4', '5', '6', '7', '8']);
+const oldSelectLines = ref([]);
+
+const {queryParams, form, rules} = toRefs(data);
+
+// 按产线分组的数据
+const groupedRzLineList = computed(() => {
+  const grouped = {};
+  rzLineList.value.forEach(item => {
+    if (!grouped[item.line]) {
+      grouped[item.line] = [];
+    }
+    grouped[item.line].push(item);
+  });
+  // 按产线编号排序
+  const sortedGrouped = {};
+  Object.keys(grouped).sort().forEach(key => {
+    sortedGrouped[key] = grouped[key];
+  });
+  return sortedGrouped;
+});
+
+// 合并单元格处理函数
+const spanMethod = ({row, column, rowIndex, columnIndex}) => {
+  if (columnIndex === 0) { // 小时列
+    // 获取当前行小时值
+    const currentHour = row.hour;
+
+    // 计算当前小时值第一次出现的位置
+    let firstIndex = -1;
+    for (let i = 0; i < rzLineList.value.length; i++) {
+      if (rzLineList.value[i].hour === currentHour) {
+        firstIndex = i;
+        break;
+      }
+    }
+
+    // 如果当前行是该小时值第一次出现的位置
+    if (firstIndex === rowIndex) {
+      // 计算该小时值连续出现的次数
+      let spanCount = 0;
+      for (let i = firstIndex; i < rzLineList.value.length; i++) {
+        if (rzLineList.value[i].hour === currentHour) {
+          spanCount++;
+        } else {
+          break;
+        }
+      }
+      return [spanCount, 1];
+    } else {
+      // 如果不是第一次出现,隐藏该单元格
+      return [0, 0];
+    }
+  }
+  return [1, 1]; // 其他列不合并
+};
+
+function navigateDay(offset) {
+  const currentDate = new Date(queryParams.value.dataDate);
+  currentDate.setDate(currentDate.getDate() + offset);
+  queryParams.value.dataDate = currentDate.toISOString().split('T')[0];
+  handleQuery();
+}
+
+// 处理产线选择变化
+function handleLineSelectionChange(val) {
+  if (val.length > 2) {
+    // 限制最多选择两条产线
+    selectedLines.value = val.slice(0, 2);
+    proxy.$modal.msgWarning("最多只能选择两条产线进行对比");
+  }
+  // 当取消选择所有产线时,清空相关数据
+  if (val.length === 0) {
+    lineDataList0.value = [];
+    lineDataList1.value = [];
+  }
+  updateQueryData();
+}
+
+/** 查询染整线产线小时统计数据列表 */
+function getList() {
+  // 检查是否选择了产线
+  if (selectedLines.value.length === 0) {
+    proxy.$modal.msgWarning("请至少选择一条产线进行对比");
+    return;
+  }
+
+  loading.value = true;
+
+  // 清空之前的数据
+  lineDataList0.value = [];
+  lineDataList1.value = [];
+
+  // 限制最多只显示两条产线的数据
+  const linesToShow = selectedLines.value.slice(0, 2);
+
+  // 分别获取每条产线的数据
+  const promises = [];
+  linesToShow.forEach((line, index) => {
+    const queryPara = {
+      pageNum: 1,
+      pageSize: 10000,
+      dataDate: queryParams.value.dataDate,
+      line: line
+    };
+
+    promises.push(
+      listLine(queryPara).then(response => {
+        // 按小时排序
+        const sortedData = response.rows.sort((a, b) => a.hour - b.hour);
+
+        // 根据索引存储数据,确保第一条产线数据在左侧,第二条产线数据在右侧
+        if (index === 0) {
+          lineDataList0.value = sortedData.map(item => ({...item}));
+        } else if (index === 1) {
+          lineDataList1.value = sortedData.map(item => ({...item}));
+        }
+
+        return response;
+      })
+    );
+  });
+
+  Promise.all(promises).then(() => {
+    total.value = promises.length;
+    loading.value = false;
+
+    // 更新图表
+    nextTick(() => {
+      updateChart0();
+      updateChart1();
+    });
+  }).catch(error => {
+    console.error(error);
+    proxy.$modal.msgError("数据获取失败");
+    loading.value = false;
+  });
+}
+
+// 初始化图表
+function initChart() {
+  if (chartInstance0) {
+    chartInstance0.dispose();
+  }
+  if (chartInstance1) {
+    chartInstance1.dispose();
+  }
+  if (chartRef0.value) {
+    chartInstance0 = echarts.init(chartRef0.value);
+  }
+  if (chartRef1.value) {
+    chartInstance1 = echarts.init(chartRef1.value);
+  }
+  // 建立图表联动
+  if (chartInstance0 && chartInstance1) {
+    echarts.connect([
+      chartInstance0,
+      chartInstance1,
+    ]);
+  }
+}
+
+// 更新第一个图表
+function updateChart0() {
+  // 如果没有数据,则清空图表
+  if (!chartInstance0 || !lineDataList0.value.length) {
+    if (chartInstance0) {
+      chartInstance0.setOption({
+        title: {
+          text: '暂无数据',
+          left: 'center',
+          top: 'center'
+        },
+        xAxis: {
+          type: 'category',
+          data: []
+        },
+        yAxis: {
+          type: 'value'
+        },
+        series: []
+      });
+    }
+    return;
+  }
+
+  const lineData = lineDataList0.value;
+  const line = lineData[0].line;
+  
+  // 获取所有唯一的时间点,只显示小时
+  const allTimePoints = lineData.map(item => 
+    `${item.hour.toString().padStart(2, '0')}:00`
+  );
+
+  // 准备图表数据
+  const series = [];
+
+  const speedData = selectedMetrics.value.includes('速度') ? lineData.map(item => 
+    item.speed !== null ? parseFloat(item.speed) : null
+  ) : [];
+
+  const energyData = selectedMetrics.value.includes('电量') ? lineData.map(item => 
+    item.energy !== null ? parseFloat(item.energy) : null
+  ) : [];
+
+  const ampData = selectedMetrics.value.includes('电流') ? lineData.map(item => 
+    item.amp !== null ? parseFloat(item.amp) : null
+  ) : [];
+
+  // 添加指标的数据系列
+  if (selectedMetrics.value.includes('速度')) {
+    series.push({
+      name: `${line}-速度`,
+      type: 'line',
+      data: speedData,
+      smooth: true,
+      showSymbol: false
+    });
+  }
+
+  if (selectedMetrics.value.includes('电量')) {
+    series.push({
+      name: `${line}-电量`,
+      type: 'line',
+      data: energyData,
+      smooth: true,
+      showSymbol: false
+    });
+  }
+
+  if (selectedMetrics.value.includes('电流')) {
+    series.push({
+      name: `${line}-电流`,
+      type: 'line',
+      data: ampData,
+      smooth: true,
+      showSymbol: false
+    });
+  }
+
+  const option = {
+    title: {
+      text: `${line}号线数据对比`,
+      left: 'center'
+    },
+    tooltip: {
+      trigger: 'axis',
+      formatter: function (params) {
+        // 在提示框中只显示小时
+        let result = params[0].axisValue + '<br/>';
+        params.forEach(param => {
+          let valueText = '';
+          if (param.seriesName.includes('速度')) {
+            valueText = param.value !== null ? param.value + ' (米/min)' : '无数据';
+          } else if (param.seriesName.includes('电量')) {
+            valueText = param.value !== null ? param.value + ' (kW·h)' : '无数据';
+          } else if (param.seriesName.includes('电流')) {
+            valueText = param.value !== null ? param.value + ' (A)' : '无数据';
+          } else {
+            valueText = param.value !== null ? param.value : '无数据';
+          }
+          result += `<div style="display:flex;align-items:center;">
+            <span style="display:inline-block;margin-right:5px;border-radius:10px;width:10px;height:10px;background-color:${param.color};"></span>
+            ${param.seriesName}: ${valueText}
+          </div>`;
+        });
+        return result;
+      }
+    },
+    legend: {
+      data: series.map(s => s.name),
+      top: 40,
+      left: 'center',
+      textStyle: {
+        fontSize: 10
+      },
+      itemWidth: 10,
+      itemHeight: 10
+    },
+    grid: {
+      left: '3%',
+      right: '4%',
+      top: '25%',
+      bottom: 20,
+      containLabel: true
+    },
+    xAxis: {
+      type: 'category',
+      boundaryGap: false,
+      data: allTimePoints
+    },
+    yAxis: {
+      type: 'value'
+    },
+    series: series
+  };
+
+  chartInstance0.setOption(option, true);
+  
+  // 添加点击事件监听(仅数据点)
+  chartInstance0.on('click', (params) => {
+    if (params && params.componentType === 'series') {
+      handleChartClick(params, 'chart0');
+    }
+  });
+}
+
+// 更新第二个图表
+function updateChart1() {
+  // 如果没有数据,则清空图表
+  if (!chartInstance1 || !lineDataList1.value.length) {
+    if (chartInstance1) {
+      chartInstance1.setOption({
+        title: {
+          text: '暂无数据',
+          left: 'center',
+          top: 'center'
+        },
+        xAxis: {
+          type: 'category',
+          data: []
+        },
+        yAxis: {
+          type: 'value'
+        },
+        series: []
+      });
+    }
+    return;
+  }
+
+  const lineData = lineDataList1.value;
+  const line = lineData[0].line;
+  
+  // 获取所有唯一的时间点,只显示小时
+  const allTimePoints = lineData.map(item => 
+    `${item.hour.toString().padStart(2, '0')}:00`
+  );
+
+  // 准备图表数据
+  const series = [];
+
+  const speedData = selectedMetrics.value.includes('速度') ? lineData.map(item => 
+    item.speed !== null ? parseFloat(item.speed) : null
+  ) : [];
+
+  const energyData = selectedMetrics.value.includes('电量') ? lineData.map(item => 
+    item.energy !== null ? parseFloat(item.energy) : null
+  ) : [];
+
+  const ampData = selectedMetrics.value.includes('电流') ? lineData.map(item => 
+    item.amp !== null ? parseFloat(item.amp) : null
+  ) : [];
+
+  // 添加指标的数据系列
+  if (selectedMetrics.value.includes('速度')) {
+    series.push({
+      name: `${line}-速度`,
+      type: 'line',
+      data: speedData,
+      smooth: true,
+      showSymbol: false
+    });
+  }
+
+  if (selectedMetrics.value.includes('电量')) {
+    series.push({
+      name: `${line}-电量`,
+      type: 'line',
+      data: energyData,
+      smooth: true,
+      showSymbol: false
+    });
+  }
+
+  if (selectedMetrics.value.includes('电流')) {
+    series.push({
+      name: `${line}-电流`,
+      type: 'line',
+      data: ampData,
+      smooth: true,
+      showSymbol: false
+    });
+  }
+
+  const option = {
+    title: {
+      text: `${line}号线数据对比`,
+      left: 'center'
+    },
+    tooltip: {
+      trigger: 'axis',
+      formatter: function (params) {
+        // 在提示框中仍然显示完整的时间信息
+        const fullTime = lineData[params[0].dataIndex].dataDate + ' ' + 
+                         lineData[params[0].dataIndex].hour.toString().padStart(2, '0') + ':00';
+        let result = fullTime + '<br/>';
+        params.forEach(param => {
+          let valueText = '';
+          if (param.seriesName.includes('速度')) {
+            valueText = param.value !== null ? param.value + ' (米/min)' : '无数据';
+          } else if (param.seriesName.includes('电量')) {
+            valueText = param.value !== null ? param.value + ' (kW·h)' : '无数据';
+          } else if (param.seriesName.includes('电流')) {
+            valueText = param.value !== null ? param.value + ' (A)' : '无数据';
+          } else {
+            valueText = param.value !== null ? param.value : '无数据';
+          }
+          result += `<div style="display:flex;align-items:center;">
+            <span style="display:inline-block;margin-right:5px;border-radius:10px;width:10px;height:10px;background-color:${param.color};"></span>
+            ${param.seriesName}: ${valueText}
+          </div>`;
+        });
+        return result;
+      }
+    },
+    legend: {
+      data: series.map(s => s.name),
+      top: 40,
+      left: 'center',
+      textStyle: {
+        fontSize: 10
+      },
+      itemWidth: 10,
+      itemHeight: 10
+    },
+    grid: {
+      left: '3%',
+      right: '4%',
+      top: '25%',
+      bottom: 20,
+      containLabel: true
+    },
+    xAxis: {
+      type: 'category',
+      boundaryGap: false,
+      data: allTimePoints
+    },
+    yAxis: {
+      type: 'value'
+    },
+    series: series
+  };
+
+  chartInstance1.setOption(option, true);
+  
+  // 添加点击事件监听(仅数据点)
+  chartInstance1.on('click', (params) => {
+    console.log('点击图表1:', params); // 调试信息
+    if (params.componentType === 'series') {
+      handleChartClick(params, 'chart1');
+    }
+  });
+}
+
+// 更新数据
+function updateQueryData() {
+  initChart();
+  getList();
+}
+
+// 图表点击事件处理函数
+function handleChartClick(params, chartType) {
+  console.log('处理点击事件:', params, chartType); // 调试信息
+  
+  // 检查必要参数是否存在
+  if (!params || !params.seriesName || !params.name) {
+    console.log('缺少必要参数');
+    return;
+  }
+  
+  // 获取点击的数据点信息
+  const seriesName = params.seriesName;
+  const timePoint = params.name;
+  
+  // 从系列名称中提取产线和指标信息
+  const [line, metric] = seriesName.split('-');
+  
+  // 查找完整的时间信息
+  let fullDate = '';
+  let hour = '';
+  
+  // 获取对应图表的数据列表
+  let targetDataList = [];
+  if (chartType === 'chart0') {
+    targetDataList = lineDataList0.value;
+  } else if (chartType === 'chart1') {
+    targetDataList = lineDataList1.value;
+  }
+  
+  if (targetDataList.length > 0) {
+    // 在目标数据列表中查找匹配的数据
+    const matchedData = targetDataList.find(item => 
+      `${item.hour.toString().padStart(2, '0')}:00` === timePoint
+    );
+    
+    if (matchedData) {
+      fullDate = matchedData.dataDate;
+      hour = matchedData.hour.toString().padStart(2, '0');
+    }
+  }
+  
+  // 如果没有找到匹配的数据,使用原来的解析方法
+  if (!fullDate) {
+    // 从时间点中提取日期和小时
+    const parts = timePoint.split(' ');
+    if (parts.length >= 2) {
+      fullDate = parts[0];
+      hour = parts[1].split(':')[0];
+    } else {
+      // 尝试直接使用时间点作为小时
+      const timeParts = timePoint.split(':');
+      if (timeParts.length >= 1) {
+        hour = timeParts[0];
+        fullDate = new Date().toISOString().split('T')[0]; // 使用当前日期作为默认值
+      } else {
+        fullDate = '未知日期';
+        hour = '00';
+      }
+    }
+  }
+  
+  // 设置对话框标题
+  minuteDialogTitle.value = `${fullDate} ${hour}时 ${line}号线分钟级数据`;
+  
+  // 模拟分钟级数据(实际项目中应该从API获取)
+  // 为所有指标生成分钟级数据
+  const speedData = [];
+  const energyData = [];
+  const ampData = [];
+  
+  for (let i = 0; i < 60; i++) {
+    // 生成模拟数据,实际项目中应该从服务器获取真实数据
+    const speedValue = Math.random() * 100;
+    const energyValue = Math.random() * 50;
+    const ampValue = Math.random() * 30;
+    
+    speedData.push({
+      time: i.toString(),
+      value: parseFloat(speedValue.toFixed(2))
+    });
+    
+    energyData.push({
+      time: i.toString(),
+      value: parseFloat(energyValue.toFixed(2))
+    });
+    
+    ampData.push({
+      time: i.toString(),
+      value: parseFloat(ampValue.toFixed(2))
+    });
+  }
+  
+  // 根据点击的图表设置分钟级数据
+  if (chartType === 'chart0') {
+    minuteTableData0.value = speedData.map((item, index) => ({
+      time: item.time,
+      speed: item.value,
+      energy: energyData[index].value,
+      amp: ampData[index].value
+    }));
+    minuteTableData1.value = [...minuteTableData0.value];
+  } else {
+    minuteTableData1.value = speedData.map((item, index) => ({
+      time: item.time,
+      speed: item.value,
+      energy: energyData[index].value,
+      amp: ampData[index].value
+    }));
+    minuteTableData0.value = [...minuteTableData1.value];
+  }
+  
+  minuteDataLabel0.value = '';
+  minuteDataLabel1.value = '';
+  
+  // 显示对话框
+  minuteDialogVisible.value = true;
+  
+  // 初始化分钟级图表
+  nextTick(() => {
+    // 初始化左右两侧的图表,显示所有指标
+    initMinuteChart0(speedData, energyData, ampData, `${line}号线`);
+    initMinuteChart1(speedData, energyData, ampData, `${line}号线`);
+    
+    // 初始化表格滚动同步
+    initTableScrollSync();
+    
+    // 建立分钟级图表联动
+    if (minuteChartInstance0 && minuteChartInstance1) {
+      echarts.connect([
+        minuteChartInstance0,
+        minuteChartInstance1
+      ]);
+    }
+  });
+}
+
+// 初始化分钟级数据图表0
+function initMinuteChart0(speedData, energyData, ampData, lineName) {
+  if (minuteChartInstance0) {
+    minuteChartInstance0.dispose();
+  }
+  
+  if (minuteChartRef0.value) {
+    minuteChartInstance0 = echarts.init(minuteChartRef0.value);
+    
+    // 准备分钟级数据
+    const timePoints = speedData.map(item => item.time);
+    
+    const option = {
+      title: {
+        text: `${lineName}分钟级数据`,
+        left: 'center'
+      },
+      tooltip: {
+        trigger: 'axis',
+        formatter: function (params) {
+          let result = params[0].axisValue + '分<br/>';
+          params.forEach(param => {
+            let valueText = '';
+            if (param.seriesName.includes('速度')) {
+              valueText = param.value !== null ? param.value + ' (米/min)' : '无数据';
+            } else if (param.seriesName.includes('电量')) {
+              valueText = param.value !== null ? param.value + ' (kW·h)' : '无数据';
+            } else if (param.seriesName.includes('电流')) {
+              valueText = param.value !== null ? param.value + ' (A)' : '无数据';
+            } else {
+              valueText = param.value !== null ? param.value : '无数据';
+            }
+            result += `<div style="display:flex;align-items:center;">
+              <span style="display:inline-block;margin-right:5px;border-radius:10px;width:10px;height:10px;background-color:${param.color};"></span>
+              ${param.seriesName}: ${valueText}
+            </div>`;
+          });
+          return result;
+        }
+      },
+      legend:{
+        data: ['速度', '电量', '电流'],
+        top: 40 ,
+        left: 'center',
+        textStyle: {
+          fontSize: 10
+        },
+        itemWidth: 10,
+        itemHeight: 10
+      },
+      grid: {
+        left: '3%',
+        right: '4%',
+        top: '25%',
+        bottom: 20,
+        containLabel: true
+      },
+      xAxis: {
+        type: 'category',
+        boundaryGap: false,
+        data: timePoints
+      },
+      yAxis: {
+        type: 'value'
+      },
+      series: [
+        {
+          name: '速度',
+          type: 'line',
+          data: speedData.map(item => item.value),
+          smooth: true,
+          showSymbol: false
+        },
+        {
+          name: '电量',
+          type: 'line',
+          data: energyData.map(item => item.value),
+          smooth: true,
+          showSymbol: false
+        },
+        {
+          name: '电流',
+          type: 'line',
+          data: ampData.map(item => item.value),
+          smooth: true,
+          showSymbol: false
+        }
+      ]
+    };
+    
+    minuteChartInstance0.setOption(option);
+  }
+}
+
+// 初始化分钟级数据图表1
+function initMinuteChart1(speedData, energyData, ampData, lineName) {
+  if (minuteChartInstance1) {
+    minuteChartInstance1.dispose();
+  }
+  
+  if (minuteChartRef1.value) {
+    minuteChartInstance1 = echarts.init(minuteChartRef1.value);
+    
+    // 准备分钟级数据
+    const timePoints = speedData.map(item => item.time);
+    
+    const option = {
+      title: {
+        text: `${lineName}分钟级数据`,
+        left: 'center'
+      },
+      tooltip: {
+        trigger: 'axis',
+        formatter: function (params) {
+          let result = params[0].axisValue + '分<br/>';
+          params.forEach(param => {
+            let valueText = '';
+            if (param.seriesName.includes('速度')) {
+              valueText = param.value !== null ? param.value + ' (米/min)' : '无数据';
+            } else if (param.seriesName.includes('电量')) {
+              valueText = param.value !== null ? param.value + ' (kW·h)' : '无数据';
+            } else if (param.seriesName.includes('电流')) {
+              valueText = param.value !== null ? param.value + ' (A)' : '无数据';
+            } else {
+              valueText = param.value !== null ? param.value : '无数据';
+            }
+            result += `<div style="display:flex;align-items:center;">
+              <span style="display:inline-block;margin-right:5px;border-radius:10px;width:10px;height:10px;background-color:${param.color};"></span>
+              ${param.seriesName}: ${valueText}
+            </div>`;
+          });
+          return result;
+        }
+      },
+      legend:{
+        data: ['速度', '电量', '电流'],
+        top: 40 ,
+        left: 'center',
+        textStyle: {
+          fontSize: 10
+        },
+        itemWidth: 10,
+        itemHeight: 10
+      },
+      grid: {
+        left: '3%',
+        right: '4%',
+        top: '25%',
+        bottom: 20,
+        containLabel: true
+      },
+      xAxis: {
+        type: 'category',
+        boundaryGap: false,
+        data: timePoints
+      },
+      yAxis: {
+        type: 'value'
+      },
+      series: [
+        {
+          name: '速度',
+          type: 'line',
+          data: speedData.map(item => item.value),
+          smooth: true,
+          showSymbol: false
+        },
+        {
+          name: '电量',
+          type: 'line',
+          data: energyData.map(item => item.value),
+          smooth: true,
+          showSymbol: false
+        },
+        {
+          name: '电流',
+          type: 'line',
+          data: ampData.map(item => item.value),
+          smooth: true,
+          showSymbol: false
+        }
+      ]
+    };
+    
+    minuteChartInstance1.setOption(option);
+  }
+}
+
+// 关闭分钟级数据弹窗
+function handleMinuteDialogClose() {
+  // 清理表格滚动事件监听器
+  const leftTableWrap = tableRef0.value?.$el?.querySelector('.el-scrollbar__wrap');
+  const rightTableWrap = tableRef1.value?.$el?.querySelector('.el-scrollbar__wrap');
+  
+  if (leftTableWrap && leftTableScrollHandler) {
+    leftTableWrap.removeEventListener('scroll', leftTableScrollHandler);
+  }
+  
+  if (rightTableWrap && rightTableScrollHandler) {
+    rightTableWrap.removeEventListener('scroll', rightTableScrollHandler);
+  }
+  
+  minuteDialogVisible.value = false;
+}
+
+// 表格滚动同步处理函数
+function syncTableScroll(sourceTable, targetTable) {
+  // 防止循环触发
+  if (isSyncing) return;
+  
+  isSyncing = true;
+  
+  try {
+    // 同步滚动位置
+    targetTable.scrollTop = sourceTable.scrollTop;
+  } catch (error) {
+    console.error('表格滚动同步出错:', error);
+  } finally {
+    // 延迟重置标志位,确保同步完成
+    setTimeout(() => {
+      isSyncing = false;
+    }, 10);
+  }
+}
+
+// 初始化分钟级数据弹窗的滚动同步
+function initTableScrollSync() {
+  nextTick(() => {
+    const leftTableWrap = tableRef0.value?.$el?.querySelector('.el-scrollbar__wrap');
+    const rightTableWrap = tableRef1.value?.$el?.querySelector('.el-scrollbar__wrap');
+    
+    if (leftTableWrap && rightTableWrap) {
+      // 移除之前的事件监听器(如果有的话)
+      leftTableWrap.removeEventListener('scroll', leftTableScrollHandler);
+      rightTableWrap.removeEventListener('scroll', rightTableScrollHandler);
+      
+      // 定义事件处理函数
+      leftTableScrollHandler = function() {
+        syncTableScroll(leftTableWrap, rightTableWrap);
+      };
+      
+      rightTableScrollHandler = function() {
+        syncTableScroll(rightTableWrap, leftTableWrap);
+      };
+      
+      // 添加事件监听器
+      leftTableWrap.addEventListener('scroll', leftTableScrollHandler);
+      rightTableWrap.addEventListener('scroll', rightTableScrollHandler);
+    }
+  });
+}
+
+// 窗口大小改变时重置图表大小
+function resizeChart() {
+  if (chartInstance0) {
+    chartInstance0.resize();
+  }
+  if (chartInstance1) {
+    chartInstance1.resize();
+  }
+  if (minuteChartInstance0) {
+    minuteChartInstance0.resize();
+  }
+  if (minuteChartInstance1) {
+    minuteChartInstance1.resize();
+  }
+}
+
+onMounted(() => {
+  initChart();
+  window.addEventListener('resize', resizeChart);
+
+  // 初始加载数据
+  getList();
+});
+
+onUnmounted(() => {
+  window.removeEventListener('resize', resizeChart);
+  if (chartInstance0) {
+    chartInstance0.dispose();
+  }
+  if (chartInstance1) {
+    chartInstance1.dispose();
+  }
+  if (minuteChartInstance0) {
+    minuteChartInstance0.dispose();
+  }
+  if (minuteChartInstance1) {
+    minuteChartInstance1.dispose();
+  }
+});
+
+onActivated(() => {
+  resizeChart();
+});
+
+// 取消按钮
+function cancel() {
+  open.value = false;
+  reset();
+}
+
+// 表单重置
+function reset() {
+  form.value = {
+    id: null,
+    dataDate: null,
+    hour: null,
+    line: null,
+    openRate: null,
+    length: null,
+    tmp: null,
+    speed: null,
+    energy: null,
+    amp: null,
+    createdBy: null,
+    createdTime: null,
+    updatedBy: null,
+    updatedTime: null,
+    remark: null
+  };
+  proxy.resetForm("rzLineRef");
+}
+
+/** 搜索按钮操作 */
+function handleQuery() {
+  queryParams.value.pageNum = 1;
+  // 检查查询条件
+  if (!queryParams.value.dataDate) {
+    proxy.$modal.msgWarning("请选择日期");
+    return;
+  }
+  getList();
+}
+
+/** 重置按钮操作 */
+function resetQuery() {
+  proxy.resetForm("queryRef");
+  // 设置默认日期为当前日期
+  const now = new Date();
+  const year = now.getFullYear();
+  const month = (now.getMonth() + 1).toString().padStart(2, '0');
+  const day = now.getDate().toString().padStart(2, '0');
+  queryParams.value.dataDate = `${year}-${month}-${day}`;
+
+  // 默认选择5号和6号产线
+  selectedLines.value = ['5', '6'];
+
+  handleQuery();
+}
+
+// 全选产线
+function selectAllLines() {
+  selectedLines.value = [...allLines.value]; // 选择所有产线,但显示时只显示前两条数据
+  updateQueryData();
+}
+
+// 反选产线
+function invertSelection() {
+  const newSelectedLines = [];
+  allLines.value.forEach(line => {
+    if (!selectedLines.value.includes(line)) {
+      newSelectedLines.push(line);
+    }
+  });
+  selectedLines.value = newSelectedLines;
+  updateQueryData();
+}
+
+// 全选指标
+function selectAllMetrics() {
+  selectedMetrics.value = ['速度', '电量', '电流'];
+  updateQueryData();
+}
+
+// 反选指标
+function invertMetrics() {
+  const allMetrics = ['速度', '电量', '电流'];
+  const newSelectedMetrics = [];
+  allMetrics.forEach(metric => {
+    if (!selectedMetrics.value.includes(metric)) {
+      newSelectedMetrics.push(metric);
+    }
+  });
+  selectedMetrics.value = newSelectedMetrics;
+  updateQueryData();
+}
+
+// 多选框选中数据
+function handleSelectionChange(selection) {
+  ids.value = selection.map(item => item.id);
+  single.value = selection.length != 1;
+  multiple.value = !selection.length;
+}
+
+/** 新增按钮操作 */
+function handleAdd() {
+  reset();
+  open.value = true;
+  title.value = "添加染整线产线小时统计数据";
+}
+
+/** 修改按钮操作 */
+function handleUpdate(row) {
+  reset();
+  const _id = row.id || ids.value
+  getRzLine(_id).then(response => {
+    form.value = response.data;
+    open.value = true;
+    title.value = "修改染整线产线小时统计数据";
+  });
+}
+
+/** 提交按钮 */
+function submitForm() {
+  proxy.$refs["rzLineRef"].validate(valid => {
+    if (valid) {
+      if (form.value.id != null) {
+        updateRzLine(form.value).then(response => {
+          proxy.$modal.msgSuccess("修改成功");
+          open.value = false;
+          getList();
+        });
+      } else {
+        addRzLine(form.value).then(response => {
+          proxy.$modal.msgSuccess("新增成功");
+          open.value = false;
+          getList();
+        });
+      }
+    }
+  });
+}
+
+/** 删除按钮操作 */
+function handleDelete(row) {
+  const _ids = row.id || ids.value;
+  proxy.$modal.confirm('是否确认删除染整线产线小时统计数据编号为"' + _ids + '"的数据项?').then(function () {
+    return delRzLine(_ids);
+  }).then(() => {
+    getList();
+    proxy.$modal.msgSuccess("删除成功");
+  }).catch(() => {
+  });
+}
+
+/** 导出按钮操作 */
+function handleExport() {
+  proxy.download('dyeing/rzLine/export', {
+    ...queryParams.value
+  }, `rzLine_${new Date().getTime()}.xlsx`)
+}
+
+getList();
+</script>

+ 50 - 6
src/views/dyeing/gyfx/index.vue

@@ -427,13 +427,56 @@ function getDeviceList(){
           name: item.deviceName
         };
       });
+      
+      // 默认选中第一台设备
+      let needUpdateData = false;
+      if (response.rows.length > 0) {
+        // 如果是单条产线情况,设置同产线设备对比的第一台设备
+        if (selectedLines.value.length === 1) {
+          const lineKey = selectedLines.value[0];
+          if (!selectedMetrics.value[lineKey + '_0']) {
+            selectedMetrics.value[lineKey + '_0'] = response.rows[0].deviceId;
+            needUpdateData = true;
+          }
+        } 
+        // 如果是两条产线情况,设置对应产线的第一台设备
+        else if (selectedLines.value.length === 2) {
+          if (!selectedMetrics.value[line]) {
+            selectedMetrics.value[line] = response.rows[0].deviceId;
+            needUpdateData = true;
+          }
+        }
+      }
+      
+      // 如果设置了默认设备,则更新数据
+      if (needUpdateData) {
+        updateQueryData();
+      }
     });
   });
 
   oldSelectLines.value = JSON.parse(JSON.stringify(selectedLines.value));
   // 如果选择了产线,更新图表
-  if (selectedLines.value.length >= 1) {
-    updateQueryData();
+  if (selectedLines.value.length >= 1 && Object.keys(selectedMetrics.value).length > 0) {
+    // 检查是否所有产线都已设置设备,如果是则更新数据
+    let allLinesHaveDevices = true;
+    if (selectedLines.value.length === 1) {
+      const lineKey = selectedLines.value[0];
+      if (!selectedMetrics.value[lineKey + '_0']) {
+        allLinesHaveDevices = false;
+      }
+    } else if (selectedLines.value.length === 2) {
+      for (const line of selectedLines.value) {
+        if (!selectedMetrics.value[line]) {
+          allLinesHaveDevices = false;
+          break;
+        }
+      }
+    }
+    
+    if (allLinesHaveDevices) {
+      updateQueryData();
+    }
   }
 }
 
@@ -1142,11 +1185,12 @@ function handleEquipmentTypeChange(val) {
 
 // 处理设备参数变化
 function handleEquipmentParamChange() {
-  // 可以在这里添加设备参数变化时的处理逻辑
-  //let lst = equipmentParamOptions.value.filter(item => item.paraCode ==selectedEquipmentParam.value);
-  //console.log(lst);
+  // 清空设备列表和已选择的设备
+  equipmentList.value = {};
+  selectedMetrics.value = {};
+  
+  // 获取新设备列表
   getDeviceList();
-  updateQueryData();
 }
 
 // 处理产线选择变化