HistoricalRecord.vue 11.3 KB
<template>
  <el-container class="history-container">
    <el-main class="layout-main">
      <splitpanes class="default-theme splitpanes-container">

        <pane :size="20" :min-size="15" :max-size="40" class="aside-pane">
          <div class="tree-wrapper">
            <vehicle-list
              ref="vehicleList"
              @tree-loaded="handleTreeLoaded"
              @node-click="nodeClick"
            />
          </div>
        </pane>

        <pane :size="80" class="main-pane">
          <div
            class="content-main"
            v-loading="loading"
            element-loading-text="拼命加载中"
            element-loading-spinner="el-icon-loading"
            element-loading-background="rgba(255, 255, 255, 0.7)"
          >
            <div class="header-section">
              <historical-record-form
                style="text-align:left"
                :inline="true"
                v-show="showSearch"
                :query-params="queryParams"
                @handleQuery="handleQuery"
              />
              <el-row v-if="deviceData || channelData" :gutter="10" class="mb8" style="margin-bottom: 10px;">
                <el-col :span="24">
                  <el-tag effect="dark" type="success">
                    <i class="el-icon-video-camera"></i> {{ deviceTitle }}
                  </el-tag>
                </el-col>
              </el-row>
            </div>

            <div class="table-section">
              <history-search-table
                ref="historySearchTable"
                style="width: 100%; height: 100%;"
                :table-data="historyData"
                @playHistoryVideo="clickHistoricalPlay"
                @uploadHistoryVideo="uploadHistoryVideo"
              />
            </div>

            <history-play-dialog ref="historyPlayDialog" />
          </div>
        </pane>
      </splitpanes>
    </el-main>
  </el-container>
</template>

<script>
// 1. 引入 VehicleList (替换 Device1078Tree)
import VehicleList from "./JT1078Components/deviceList/VehicleList.vue";
import HistoryPlayDialog from "./JT1078Components/HistoryPlayDialog.vue";
import HistoricalRecordForm from "./JT1078Components/HistoryRecordFrom.vue";
import HistorySearchTable from "./JT1078Components/HistorySearchTable.vue";
import { Splitpanes, Pane } from 'splitpanes'
import 'splitpanes/dist/splitpanes.css'
import {parseTime} from "../../utils/ruoyi";

export default {
  name: "HistoricalRecord",
  components: {
    VehicleList, // 注册组件
    HistoryPlayDialog,
    HistorySearchTable,
    HistoricalRecordForm,
    Splitpanes,
    Pane
  },
  data() {
    return {
      //列表定时器
      timer: null,
      //历史视频列表定时器
      historyTimer: null,
      historyData: [],
      //源列表数据
      sourceValue: [],
      //遮罩层
      loading: false,
      //sim号和通道号
      sim_channel: null,
      channelData:  null,
      nodeChannelData: null,
      deviceData: null,
      deviceTitle: '',
      showSearch: true,
      queryParams: {
        time: this.getTodayRange(),
        pageNum: 1,
        pageSize: 10
      },
      videoUrl: [],
      deviceNode: null,
    };
  },
  watch: {
    deviceNode(val) {
      this.deviceNode = val
      if (this.$refs.historySearchTable) {
        this.$refs.historySearchTable.deviceNode = val
      }
    }
  },
  created() {
    // 移除 getCarInfoBuffer() 调用,VehicleList 会自动加载
  },
  destroyed() {
    clearInterval(this.timer)
    clearInterval(this.historyTimer)
  },
  methods: {
    getTodayRange() {
      const startOfToday = new Date()
      startOfToday.setHours(0, 0, 0, 0)
      const endOfToday = new Date()
      endOfToday.setHours(23, 59, 59, 999)
      return [startOfToday, endOfToday]
    },

    /**
     * 接收 VehicleList 加载完成的数据
     */
    handleTreeLoaded(data) {
      this.sourceValue = data;
    },

    /**
     * 树点击事件 (逻辑保持不变,适配 VehicleList 的数据结构)
     */
    nodeClick(data, node) {
      if (data) {
        // VehicleList 生成的 ID 格式通常为 deviceId_sim_channel
        let split = data.id.split("_");
        this.deviceNode = node
        this.nodeChannelData = {};
        let nodeChannelDataList = [];

        // 判断是否为通道节点 (根据你的逻辑,长度为3代表是通道)
        if (split.length === 3) {
          this.sim_channel = split[1] + '_' + split[2]
          this.channelData = data
          this.deviceTitle = `车辆:${data.pid} 通道:${data.name}` // data.pid 是 VehicleList 处理好的父级ID

          // 获取同级所有通道用于显示
          let children = node.parent.data.children;
          if (children) {
            for (let i in children) {
              const nodeChannelData = children[i];
              let ids = nodeChannelData.id.split("_");
              if (ids.length === 3) {
                nodeChannelData.deviceId = ids[0];
                nodeChannelData.channelId = ids[2];
                nodeChannelDataList.push(nodeChannelData)
              }
            }
            this.nodeChannelData = nodeChannelDataList.reduce((map, item) => {
              map[item.channelId] = item;
              return map;
            }, {});
          }
        } else {
          // 点击了父设备节点
          this.deviceData = data;
          this.deviceTitle = `车辆:${data.name}`;
          // 清空通道选择,或者默认选择第一个通道,视业务需求而定
          this.sim_channel = null;
        }
      }
    },

    handleQuery(queryParams) {
      this.queryParams = {...this.queryParams, ...queryParams};
      this.searchHistoryList()
    },

    // --- 【删除】所有手动获取和处理树数据的方法 ---
    // 删除 getCarInfoBuffer, getCarInfo, statisticsOnline, processingTreeData
    // 删除 addChannels, disableItemsByName, processingSimList
    // 理由:VehicleList 组件内部已经完成了这些工作

    clickHistoricalPlay(data) {
      this.playHistoryItem(data)
    },

    uploadHistoryVideo(data) {
      this.loading = true
      this.$axios({
        method: 'get',
        url: '/api/jt1078/query/history/uploading/' + data.name
      }).then(res => {
        this.$message.success("视频开始上传,请等待")
        this.searchHistoryList()
        this.loading = false
      }).catch(err => {
        this.$message.error("视频上传失败")
        this.loading = false
      })
    },

    searchHistoryList() {
      let simChannel = this.sim_channel;
      if (this.isEmpty(simChannel)) {
        return this.$message.error('请先点击左侧选择车辆通道');
      }

      let split = simChannel.split('_');
      let sim = split[0];
      let channel = split[1];

      if (!this.queryParams.time) {
        return this.$message.error('请选择开始和结束时间');
      }

      this.loading = true;
      this.$axios({
        method: 'get',
        url: '/api/jt1078/query/history/list/' + sim + '/' + channel + "/" + parseTime(this.queryParams.time[0], '{y}-{m}-{d} {h}:{i}:{s}') + "/" + parseTime(this.queryParams.time[1], '{y}-{m}-{d} {h}:{i}:{s}')
      }).then(res => {
        let items = res.data.data.obj.data.items;
        if (items) {
          for (let i in items) {
            items[i].disabled = false;
            items[i].countdown = 10;
            items[i].channelName = this.nodeChannelData[items[i].channel] ? this.nodeChannelData[items[i].channel].name : `通道${Number(items[i].channel)}`
            items[i].deviceId = this.deviceData ? this.deviceData.name : this.channelData.pid
          }
          this.historyData = items;
        } else {
          this.historyData = [];
          this.$message.warning("搜索历史列表为空");
        }
        this.loading = false
      }).catch(error => {
        this.loading = false
        this.$message.error("发送历史视频列表指令异常");
      })
    },

    playHistoryItem(e) {
      this.videoUrl = [];
      this.loading = true
      this.$axios({
        method: 'get',
        url: '/api/jt1078/query/send/request/io/history/' + e.sim + '/' + e.channel + "/" + e.startTime + "/" + e.endTime + "/" + e.channelMapping
      }).then(res => {
        if (res.data && res.data.data && res.data.data.data) {
          let videoUrl1 = (location.protocol === "https:") ? res.data.data.data.wss_flv : res.data.data.data.ws_flv;

          this.$refs.historyPlayDialog.updateOpen(true)
          this.$refs.historyPlayDialog.data = {
            videoUrl: videoUrl1,
            startTime: e.startTime,
            endTime: e.endTime,
            deviceId: e.deviceId,
            channelName: e.channelName,
            channel: e.channel,
            sim: e.sim
          }
        } else {
          this.$message.error(res.data.msg || "获取播放地址失败");
        }
        this.loading = false
      })
    },

    setPlayUrl(url, idx) {
      this.$set(this.videoUrl, idx, url)
    },

    isEmpty(val) {
      return null == val || undefined == val || "" == val;
    }
  }
};
</script>

<style scoped>
/* 1. 全局容器 */
.history-container {
  height: 100%;
  width: 100%;
  overflow: hidden;
}

.layout-main {
  padding: 0;
  height: 100%;
  overflow: hidden;
}

/* 2. SplitPanes 容器背景统一 */
.splitpanes-container {
  height: 100%;
  background-color: #ffffff; /* 【修改】统一为白色 */
}

/* 3. 左侧侧边栏 (保持之前的优化) */
.aside-pane {
  background-color: #ffffff;
  border-right: 1px solid #dcdfe6;
  height: 100%;
  overflow: hidden;
  display: flex;
  flex-direction: column;
}

.tree-wrapper {
  height: 100%;
  width: 100%;
  overflow-y: auto;
  overflow-x: hidden;
  background-color: #ffffff;
  padding: 10px 5px;
  box-sizing: border-box;
}

/* 样式穿透修复 DeviceList (保持之前的优化) */
.tree-wrapper ::v-deep .head-container { background-color: transparent !important; padding: 0 !important; margin: 0 !important; }
.tree-wrapper ::v-deep .head-container .el-row { margin: 0 !important; display: block; }
.tree-wrapper ::v-deep .head-container .el-col { width: 100% !important; padding: 0 !important; float: none !important; }
.tree-wrapper ::v-deep .el-input__inner { border-radius: 4px; }
.tree-wrapper ::v-deep .vue-easy-tree, .tree-wrapper ::v-deep .filter-tree { margin-top: 10px !important; padding-left: 5px !important; background-color: #ffffff !important; }

/* ============================================================
   【核心修改区】右侧样式统一
   ============================================================ */

/* 4. 右侧主面板 */
.main-pane {
  background-color: #ffffff; /* 【修改】背景纯白 */
  padding: 0;
  overflow: hidden;
}

/* 5. 内容包裹层 */
.content-main {
  height: 100%;
  width: 100%;
  display: flex;
  flex-direction: column;
  background-color: #ffffff; /* 【修改】背景纯白 */

  /* 【关键修改】移除外边距和圆角,填满整个区域 */
  margin: 0;
  border-radius: 0;

  /* 内部保留间距 */
  padding: 20px;
  box-sizing: border-box;
}

/* 6. 顶部搜索区 */
.header-section {
  flex-shrink: 0;
  margin-bottom: 15px;
  border-bottom: 1px solid #EBEEF5; /* 保留底部分割线 */
  padding-bottom: 10px;
}

/* 7. 表格区域 */
.table-section {
  flex: 1; /* 撑满剩余高度 */
  overflow: hidden;
  background: #ffffff;
  position: relative;
}

/* 修复搜索表单 Label 颜色 */
::v-deep .el-form-item__label {
  color: #606266;
  font-weight: 500;
}
</style>