he wei
2024-11-09 5cc9eb2d302ffe6218221208a28b60f91b57e41e
src/views/datas/dataHis.vue
@@ -1,13 +1,551 @@
<script setup>
import { ref } from "vue";
<script setup name="dataHis">
import { ref, reactive, computed, watch, nextTick, onMounted } from "vue";
import useDevsRt from "@/hooks/useDevsRt.js";
import { getTestList, getRecordInf, exportRecord } from "./api";
import lineChart from "@/components/echarts/lineChart2.vue";
import bar from "@/components/echarts/bar1.vue";
import ycCard from "@/components/ycCard.vue";
import formatSeconds from "@/assets/js/tools/formatSeconds.js";
import getBarNum from "@/assets/js/tools/getBarNum.js";
import toFixed from "@/assets/js/tools/toFixed.js";
import useElement from "@/hooks/useElement.js";
const { $alert, $loading, $message, $confirm } = useElement();
const { list } = useDevsRt();
const devType = ref(1);
const keyWord = ref("");
const currentDevId = ref(0);
const testRecordCode = ref();
const monChart = ref();
const groupChart = ref();
const slideValue = ref(0);
const testDataFormated = reactive({
  labels: [],
  datas: [],
});
const testRecordList = ref([]);
const testDatas = ref([]);
const resList = computed(() => {
  let _list = list.value[devType.value];
  return _list.filter((v) => ("" + v.devIdcode).indexOf(keyWord.value) > -1);
});
const testObj = computed(() => {
  if (testRecordList.value) {
    let [type, record] = testRecordCode.value || [];
    if (!type) return {};
    let list = testRecordList.value.filter((v) => v.value == type)[0].children;
    return list.filter((v) => record == v.value)[0];
  } else {
    return {};
  }
});
function chooseDev(params) {
  currentDevId.value = params.devId;
  testRecordCode.value = undefined;
  testDatas.value = [];
  updateChart([]);
  getList();
}
const stopWatch = watch(resList, (newValue, oldValue) => {
  console.log("resList", newValue, oldValue);
  typeChange();
  stopWatch();
});
function typeChange() {
  chooseDev(resList.value[0]);
}
function getList() {
  getTestList(currentDevId.value)
    .then((res) => {
      let { code, data, data2 } = res.data;
      let _list = [];
      if (code && data) {
        console.log(data);
        _list = Object.keys(data2).map((v) => {
          let arr = data2[v];
          let type = "";
          switch (v) {
            case "chr":
              type = devType.value == 1 ? "充电测试" : "充电";
              break;
            case "dis":
              type = devType.value == 1 ? "放电测试" : "放电";
              break;
            case "jun":
              type = "均衡";
              break;
          }
          let label = type;
          let value = v;
          let children = [];
          let count = arr.length;
          if (devType.value == 1) {
            children = arr.map((vv) => ({
              ...vv,
              label: vv.testStarttime,
              value: vv.testRecordCount,
            }));
          } else {
            // 添加一级 区分是组1还是组2
            let _obj = {};
            for (let i = arr.length; i--; ) {
              let item = arr[i];
              let idx = item.battIdx;
              _obj[idx] = _obj[idx] || [];
              _obj[idx].push(item);
            }
            Object.keys(_obj).forEach((vv) => {
              let item = _obj[vv];
              let idx = `组${vv * 1 + 1}`;
              let _count = item.length;
              children.push({
                label: idx,
                  count: _count,
                value: `group_${vv}`,
                children: item.map((vvv) => ({
                  ...vvv,
                  label: vvv.testStarttime,
                  value: vvv.testRecordCount,
                })),
              });
            });
            // children = arr.map((vv) => ({
            //   ...vv,
            //   label: vv.testStarttime,
            //   value: vv.testRecordCount,
            // }));
          }
          return { label, value, count, disabled: !arr.length, children };
        });
      }
      testRecordList.value = _list;
    })
    .catch((err) => {
      console.log(err);
    });
}
const canSlide = computed(() => !!testDatas.value.length);
// const testInf = computed(() => {
//   if (testDatas.value.length) {
//     return testDatas.value[0];
//   } else {
//     return {};
//   }
// });
function updateChart(data) {
  let labels = [],
    groupVols = [],
    monVols = [];
  let recordTime = -1; // 记录时间
  let recordNum = -100; // 记录笔数
  data.forEach((item) => {
    let monNum = item.monNum;
    let testTimeLong = formatSeconds(item.testTimelong);
    // 获取组端电压,在线电压,组端电流的信息和开辟一个单体柱状图
    if (recordNum != item.recordNum) {
      recordTime = item.recordTime;
      recordNum = item.recordNum;
      labels.push(testTimeLong);
      groupVols.push(item.groupVol);
      // 开辟空间
      monVols.push({
        labels: [],
        datas: [],
      });
    }
    // 单体电压柱状图设置
    let monNum_text = "#" + monNum;
    let monBarVol = monVols[monVols.length - 1];
    // monBarVol.push([monNum_text, item.monVol]);
    monBarVol.labels.push(monNum_text);
    monBarVol.datas.push(item.monVol);
  });
  groupChart.value.updateChart(labels, groupVols);
  testDataFormated.datas = monVols;
  testDataFormated.labels = labels;
  setBarChart(monVols);
}
// 根据百分比获取显示的数据的笔数
function getDataIndex(num, percent) {
  if (percent <= 0) {
    return 0;
  }
  return Math.floor((num * percent) / 100) - 1;
}
function handleSlider(params) {
  // updateChart(testDatas.value);
  setBarChart(testDataFormated.datas);
}
// 设置柱状图
function setBarChart(dataList) {
  // if (!dataList.length) {
  //   return false;
  // }
  let unit = "V";
  let fixed = 3;
  let title = `最大值=0${unit}; 最小值=0${unit}; 平均值=0${unit};`;
  let index = getDataIndex(dataList.length, slideValue.value);
  let labels = [],
    datas = [];
  if (dataList.length && index != -1) {
    let data = dataList[index];
    let batNum = getBarNum(data);
    title =
      "最大值=" +
      batNum.max +
      unit +
      ";最小值=" +
      batNum.min +
      unit +
      ";平均值=" +
      toFixed(batNum.avg, fixed) +
      unit;
    labels = data.labels;
    datas = data.datas;
  }
  monChart.value.updateChart(title, labels, datas);
}
function selectRecord() {
  let res = testRecordCode.value[testRecordCode.value.length - 1];
  // console.log("res", res, "=============");
  getRecordInf(currentDevId.value, res)
    .then((res) => {
      let { code, data, data2 } = res.data;
      let _list = [];
      if (code && data) {
        console.log(data);
        _list = data2;
        updateChart(_list);
      }
      testDatas.value = _list;
    })
    .catch((err) => {
      console.log(err);
    });
}
function exportData() {
  if (!testRecordCode.value || !testRecordCode.value.length) {
    $message.error("请选择要导出的测试记录");
    return false;
  }
  exportRecord(currentDevId.value, testRecordCode.value[1])
    .then((res) => {
      let { data, status, headers } = res;
      const matchRes = /filename=(.*)/.exec(headers["content-disposition"]);
      const fileName = matchRes ? matchRes[1].trim() : "未知文件名.xls";
      if (200 == status && data) {
        let url = window.URL.createObjectURL(data);
        let link = document.createElement("a");
        link.style.display = "none";
        link.href = url;
        link.download = decodeURI(fileName);
        document.body.appendChild(link);
        link.click();
        document.body.removeChild(link);
        window.URL.revokeObjectURL(url);
        // downloadLog(parentModel, subModel);
      } else {
        $message.error("下载失败");
      }
    })
    .catch((err) => {
      console.log(err);
    });
}
function formatTooltip(params) {
  let testTimeLong = testDataFormated.labels;
  let index = getDataIndex(testTimeLong.length, params);
  let test_long = formatSeconds(0);
  if (index != -1) {
    // test_long = formatSeconds(testTimeLong[index]);
    test_long = testTimeLong[index];
  }
  return test_long;
}
onMounted(() => {
  if (resList.length) {
    typeChange();
  }
});
</script>
<template>
历史数据
  <div class="page">
    <div class="p-left">
      <div class="filter">
        <el-input
          v-model="keyWord"
          class="key-word"
          placeholder="请您输入您要查找的设备"
        >
          <template #suffix>
            <el-icon class="el-input__icon" color="#42f4b5"><search /></el-icon>
          </template>
        </el-input>
      </div>
      <div class="tabs">
        <el-radio-group
          v-model="devType"
          @change="typeChange"
          size="default"
          is-button
        >
          <el-radio-button :value="1">充放电测试仪</el-radio-button>
          <el-radio-button :value="2">均衡测试仪</el-radio-button>
        </el-radio-group>
      </div>
      <div class="list-wrap posR">
        <div class="list pos-full">
          <div
            :class="['item', { active: item.devId == currentDevId }]"
            v-for="item in resList"
            :key="item.devId"
            @click="chooseDev(item)"
          >
            {{ item.devIdcode }}
          </div>
        </div>
      </div>
    </div>
    <div class="p-content">
      <yc-card class="item item-left" title="测试记录">
        <el-cascader
          class="select"
          v-model="testRecordCode"
          :options="testRecordList"
          @change="selectRecord"
          ><template #default="{ node, data }">
            <span>{{ data.label }}</span>
            <span v-if="!node.isLeaf"> ({{ data.count }}) </span>
          </template></el-cascader
        >
        <div class="info-title">
          测试概况
          <div
            :class="['btn', { disabled: !testRecordCode }]"
            @click="exportData"
          >
            导出
          </div>
        </div>
        <div class="info">
          <div class="label">测试时间</div>
          <div class="content">{{ testObj.testStarttime }}</div>
          <div class="label">测试时长</div>
          <div class="content">{{ formatSeconds(testObj.testTimelong) }}</div>
          <div class="label">数据类型</div>
          <div class="content">
            {{
              { 2: "充电数据", 3: "放电数据", 4: "均衡数据" }[testObj.testType]
            }}
          </div>
          <div class="label">停止原因</div>
          <div class="content">{{ testObj.stopTypeReason }}</div>
          <!-- <div class="label">标称容量</div>
          <div class="content">4444444</div>
          <div class="label">单体数量</div>
          <div class="content">33333</div>
          <div class="label">测试电流</div>
          <div class="content">4444444</div>
          <div class="label">单体下限</div>
          <div class="content">4444444</div>
          <div class="label">测试时长</div>
          <div class="content">4444444</div>
          <div class="label">测试容量</div>
          <div class="content">4444444</div>
          <div class="label">停止原因</div>
          <div class="content">4444444</div> -->
        </div>
      </yc-card>
      <yc-card class="item" title="组端电压">
        <line-chart ref="groupChart" unit="V"></line-chart>
      </yc-card>
      <yc-card class="item" title="单体电压">
        <div class="card-content">
          <div class="chart-w">
            <bar ref="monChart" unit="V"></bar>
          </div>
          <el-slider
            class="slider"
            v-model="slideValue"
            :format-tooltip="formatTooltip"
            :disabled="!canSlide"
            @input="handleSlider"
          />
        </div>
      </yc-card>
    </div>
  </div>
</template>
<style scoped lang="less">
.page {
  height: 100%;
  padding: 8px;
  display: flex;
</style>
  .p-left {
    width: 340px;
    background: radial-gradient(#1a585d10, #1a585d80);
    display: flex;
    flex-direction: column;
    .filter {
      padding: 6px;
      .key-word {
        // margin: 6px;
      }
      :deep(.el-input__inner) {
        background: transparent;
        color: #fff;
        &::placeholder {
          color: #b0b7b4;
        }
      }
      :deep(.el-input__wrapper) {
        background-color: rgba(66, 244, 181, 0.1);
        box-shadow: 0 0 0 1px #42f4b5 inset;
      }
    }
    .tabs {
      display: flex;
      justify-content: center;
    }
    .list-wrap {
      flex: 1;
      margin: 6px;
      .list {
        border: 1px solid #0ff;
        overflow-y: auto;
        padding-left: 4em;
        padding-top: 6px;
        .item {
          height: 38px;
          display: flex;
          align-items: center;
          position: relative;
          color: #0ff;
          cursor: pointer;
          &.active {
            background: #007983;
          }
          &:hover {
            background: rgba(100, 216, 216, 0.3);
          }
          &::before {
            content: "";
            position: absolute;
            left: -2em;
            top: 50%;
            width: 1.8em;
            border-top: 1px solid #0ff;
          }
          &::after {
            content: "";
            position: absolute;
            top: 50%;
            left: -2em;
            height: 100%;
            border-left: 1px solid #0ff;
          }
          &:last-of-type {
            &::after {
              content: none;
            }
          }
        }
      }
    }
  }
  .p-content {
    flex: 1;
    margin-left: 10px;
    // display: flex;
    // flex-direction: column;
    display: grid;
    grid-template-columns: 300px 1fr;
    grid-template-rows: 1fr 1fr;
    grid-auto-flow: column;
    gap: 8px;
    // background: #1a585d;
    .item-left {
      grid-row: 1 e("/") 3;
      :deep(.c-content) {
        padding: 6px;
      }
      :deep(.select) {
        width: 100%;
        margin-bottom: 0.8em;
      }
      .info-title {
        color: #0ff;
        font-weight: bold;
        display: flex;
        justify-content: space-between;
        align-items: center;
        .btn {
          cursor: pointer;
          display: inline-block;
          background: #0ff;
          border-radius: 6px;
          padding: 0 6px;
          color: #000;
          &.disabled {
            background: #ccc;
          }
        }
      }
      .info {
        display: grid;
        grid-template-columns: 5em 1fr;
        padding-left: 0.6em;
        .label {
          &::after {
            content: ":";
          }
        }
      }
    }
    .item {
      // background: #000;
      background: radial-gradient(#1a585d10, #1a585d80);
    }
    .card-content {
      height: 100%;
      display: flex;
      flex-direction: column;
      padding: 10px;
      .chart-w {
        flex: 1;
      }
      .slider {
        padding-left: 10px;
      }
    }
  }
}
</style>