<script setup name="hisTest">
|
import { ref, reactive, onMounted, watch, nextTick, computed } from "vue";
|
import lineChart from '@/components/echarts/line4.vue';
|
import barChart from '@/components/echarts/bar7.vue';
|
|
import formatSeconds from '@/utils/formatSeconds';
|
import useElement from "@/hooks/useElement.js";
|
const { $loading, $message, $confirm } = useElement();
|
|
import * as echarts from 'echarts';
|
|
import {
|
toFixed,
|
digits,
|
} from '@/utils/toFixed';
|
|
import {
|
getBattTinf,
|
getTinfDataWithTestRecordCount,
|
} from "@/api/history.js";
|
import { format } from "echarts";
|
|
const props = defineProps({
|
battgroupId: {
|
type: [String, Number],
|
},
|
num: {
|
type: [String, Number],
|
}
|
});
|
|
const testTypes = [
|
{
|
prop: 'hrfd',
|
label: '核容放电'
|
},
|
{
|
prop: 'hrcd',
|
label: '核容充电'
|
},
|
{
|
prop: 'jkfd',
|
label: '监测放电'
|
},
|
{
|
prop: 'jkcd',
|
label: '监测充电'
|
},
|
{
|
prop: 'tdfd',
|
label: '停电放电'
|
}
|
];
|
|
const testRecordList = reactive({
|
hrfd: [],
|
hrcd: [],
|
jkfd: [],
|
jkcd: [],
|
tdfd: [],
|
});
|
|
const selectValues = reactive({
|
hrfd: '',
|
hrcd: '',
|
jkfd: '',
|
jkcd: '',
|
tdfd: '',
|
});
|
|
const slider = ref(100);
|
const chart0 = ref(null);
|
const chart1 = ref(null);
|
const chart2 = ref(null);
|
const chart3 = ref(null);
|
|
const statusList = reactive([
|
{ label: '电池状态', prop: 'battStateName', unit: '' },
|
{ label: '在线电压', prop: 'onlineVol', unit: 'V' },
|
{ label: '电池电流', prop: 'groupCurr', unit: 'A' },
|
{ label: '测试时长', prop: 'testTimeLongStr', unit: '' },
|
{ label: '测试时间', prop: 'testStarttime', unit: '' },
|
{ label: '测试容量', prop: 'testCap', unit: 'Ah' },
|
{ label: '预估容量', prop: 'realCap', unit: 'Ah' },
|
{ label: '预估续航', prop: 'restTimeStr', unit: '' },
|
]);
|
|
const infoList = reactive([
|
{ label: '标称容量', prop: 'moncapstd', unit: 'Ah' },
|
{ label: '蓄电池数量', prop: 'monCount', unit: '' },
|
{ label: '标称电压', prop: 'monvolstd', unit: 'V' },
|
{ label: '浮充电压', prop: 'floatchartVol', unit: 'V' },
|
])
|
|
const monBarTitle = ref("最大值=0V;最小值=0V;平均值=0V");
|
const chartType = ref("vol");
|
const chartTypes = [
|
{
|
label: "单体电压",
|
value: "vol",
|
unit: "V",
|
fixed: digits.VOL,
|
},
|
{
|
label: "单体温度",
|
value: "temp",
|
unit: "℃",
|
fixed: digits.TEMP,
|
},
|
{
|
label: "单体实际容量",
|
value: "realCap",
|
unit: "AH",
|
fixed: digits.CAP,
|
},
|
{
|
label: "单体剩余容量",
|
value: "resCap",
|
unit: "AH",
|
fixed: digits.CAP,
|
},
|
{
|
label: "单体容量百分比",
|
value: "preCap",
|
unit: "%",
|
fixed: 0,
|
},
|
];
|
|
const chartData = reactive({
|
vol: [],
|
temp: [],
|
realCap: [],
|
resCap: [],
|
preCap: [],
|
});
|
|
const testRecordCount = ref('');
|
const currProp = ref('');
|
function filterChange(prop) {
|
testTypes.forEach(v => {
|
if (v.prop !== prop) {
|
selectValues[v.prop] = '';
|
}
|
});
|
|
testRecordCount.value = selectValues[prop];
|
currProp.value = prop;
|
getTestInfo();
|
}
|
|
const battStatus = computed(() => {
|
return currProp.value ? testTypes.filter(v => v.prop == currProp.value)[0].label : '--';
|
});
|
|
const testInfo = computed(() => {
|
let obj = currProp.value ? testRecordList[currProp.value].filter(v => v.value == testRecordCount.value)[0] : {};
|
obj.battStateName = battStatus.value;
|
console.log('obj', obj, '=============');
|
|
return obj;
|
});
|
|
|
// 历史测试记录
|
async function getLists() {
|
let res = await getBattTinf(props.battgroupId);
|
let { code, data, data2 } = res;
|
if (code && data) {
|
testRecordList.hrfd = data2['核容放电'].map(v => ({
|
...v,
|
testCap: toFixed(v.testCap, digits.CAP),
|
restTimeStr: formatSeconds(v.restTime),
|
testTimeLongStr: formatSeconds(v.testTimeLong),
|
value: `${v.testRecordCount}-${v.recordNum}`,
|
label: v.testStarttime
|
}));
|
testRecordList.hrfd = data2['核容放电'].map(v => ({
|
...v,
|
testCap: toFixed(v.testCap, digits.CAP),
|
restTimeStr: formatSeconds(v.restTime),
|
testTimeLongStr: formatSeconds(v.testTimeLong),
|
value: `${v.testRecordCount}-${v.recordNum}`,
|
label: v.testStarttime
|
}));
|
testRecordList.hrfd = data2['核容放电'].map(v => ({
|
...v,
|
testCap: toFixed(v.testCap, digits.CAP),
|
restTimeStr: formatSeconds(v.restTime),
|
testTimeLongStr: formatSeconds(v.testTimeLong),
|
value: `${v.testRecordCount}-${v.recordNum}`,
|
label: v.testStarttime
|
}));
|
testRecordList.hrfd = data2['核容放电'].map(v => ({
|
...v,
|
testCap: toFixed(v.testCap, digits.CAP),
|
restTimeStr: formatSeconds(v.restTime),
|
testTimeLongStr: formatSeconds(v.testTimeLong),
|
value: `${v.testRecordCount}-${v.recordNum}`,
|
label: v.testStarttime
|
}));
|
testRecordList.hrfd = data2['核容放电'].map(v => ({
|
...v,
|
testCap: toFixed(v.testCap, digits.CAP),
|
restTimeStr: formatSeconds(v.restTime),
|
testTimeLongStr: formatSeconds(v.testTimeLong),
|
value: `${v.testRecordCount}-${v.recordNum}`,
|
label: v.testStarttime
|
}));
|
}
|
}
|
|
// 查询测试信息
|
async function getTestInfo() {
|
let [recordCount, recordNum] = testRecordCount.value.split('-');
|
let loading = $loading();
|
let res = await getTinfDataWithTestRecordCount(props.battgroupId, props.num, recordNum, recordCount);
|
loading.close();
|
let { code, data, data2 } = res;
|
if (code && data) {
|
// testInfo.value = data2;
|
formatData(data2);
|
}
|
}
|
|
watch(
|
() => props.num,
|
() => {
|
if (!currProp.value) {
|
return false;
|
}
|
getTestInfo();
|
}
|
);
|
|
const chartOptions = reactive({
|
chart0: {
|
color: ["#0081ff", "#df9d02"],
|
title: {
|
show: false,
|
text: "端电压折线图(V)",
|
x: "left",
|
textStyle: {
|
fontSize: "14",
|
},
|
},
|
legend: {
|
show: false,
|
data: ["在线电压", "组端电压"],
|
right: 0,
|
orient: "vertical",
|
},
|
series: [
|
{
|
name: "在线电压",
|
data: [],
|
},
|
{
|
name: "组端电压",
|
data: [],
|
},
|
],
|
},
|
chart1: {
|
name: '',
|
data: [],
|
},
|
chart2: {
|
title: {
|
show: false,
|
text: "电池电流折线图(A)",
|
x: "center",
|
textStyle: {
|
fontSize: "14",
|
},
|
},
|
legend: {
|
show: false,
|
},
|
series: [
|
{
|
name: "电池电流",
|
areaStyle: {
|
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
{
|
offset: 0,
|
color: "#00feff",
|
},
|
{
|
offset: 1,
|
color: "transparent",
|
},
|
]),
|
},
|
data: [],
|
},
|
],
|
},
|
chart3: {
|
title: {
|
show: false,
|
text: "单体电压(V)",
|
x: "center",
|
textStyle: {
|
fontSize: "14",
|
},
|
},
|
legend: {
|
show: false,
|
},
|
series: [],
|
},
|
});
|
|
const testTLong = ref([]);
|
const barDatas = ref({});
|
const bgDatas = ref({});
|
const monNames = ref([]);
|
|
// 格式化数据
|
function formatData(data) {
|
let recordNum = -1;
|
let chartData = {
|
groupVol: [],
|
onlineVol: [],
|
testCurr: [],
|
monVolLine: [],
|
};
|
let monBarData = {
|
vol: [],
|
temp: [],
|
realCap: [],
|
resCap: [],
|
preCap: [],
|
};
|
let xLabels = [];
|
let firestData = {
|
vol: [],
|
temp: [],
|
realCap: [],
|
resCap: [],
|
preCap: [],
|
};
|
let firstRecordNum = -1;
|
for (let i = 0, len = data.length; i < len; i++) {
|
let item = data[i];
|
let monNum = item.monNum;
|
let testTimeLong = formatSeconds(item.testTimelong);
|
if (recordNum != item.recordNum) {
|
recordNum = item.recordNum;
|
if (firstRecordNum == -1) {
|
firstRecordNum = item.recordNum;
|
}
|
chartData.groupVol.push([testTimeLong, item.groupVol]);
|
chartData.onlineVol.push([testTimeLong, item.onlineVol]);
|
chartData.testCurr.push([testTimeLong, item.testCurr]);
|
monBarData.vol.push([]);
|
monBarData.temp.push([]);
|
monBarData.realCap.push([]);
|
monBarData.resCap.push([]);
|
monBarData.preCap.push([]);
|
testTLong.value.push(testTimeLong);
|
}
|
let monName;
|
if (firstRecordNum == recordNum) {
|
monName = '#' + monNum;
|
firestData.vol.push(item.monVol);
|
firestData.temp.push(item.monTmp);
|
firestData.realCap.push(item.realCap);
|
firestData.resCap.push(item.restCap);
|
firestData.preCap.push(item.percentCap);
|
xLabels.push(monName);
|
}
|
monBarData.vol[monBarData.vol.length - 1].push(item.monVol);
|
monBarData.temp[monBarData.temp.length - 1].push(item.monTmp);
|
monBarData.realCap[monBarData.realCap.length - 1].push(item.realCap);
|
monBarData.resCap[monBarData.resCap.length - 1].push(item.restCap);
|
monBarData.preCap[monBarData.preCap.length - 1].push(item.percentCap);
|
|
let monIdx = monNum - 1;
|
chartData.monVolLine[monIdx] = chartData.monVolLine[monIdx] || [];
|
chartData.monVolLine[monIdx].push([testTimeLong, item.monVol]);
|
}
|
// return { chartData, monBarData, xLabels };
|
|
chartOptions.chart0.series[0].data = chartData.onlineVol;
|
chartOptions.chart0.series[1].data = chartData.groupVol;
|
|
chartOptions.chart2.series[0].data = chartData.testCurr;
|
chartOptions.chart3.series = chartData.monVolLine.map((item, idx) => ({
|
name: '#' + (idx + 1),
|
data: item
|
}));
|
|
barDatas.value = monBarData;
|
bgDatas.value = firestData;
|
monNames.value = xLabels;
|
updateChart();
|
}
|
|
function updateChart() {
|
chart0.value.updateChart(chartOptions.chart0);
|
updateBarChart();
|
chart2.value.updateChart(chartOptions.chart2);
|
chart3.value.updateChart(chartOptions.chart3);
|
// chart2.value.updateChart(xLabels, monBarData.vol, monBarData.temp, monBarData.realCap, monBarData.resCap, monBarData.preCap);
|
}
|
|
function getBarNum(data) {
|
let sum = 0;
|
data.map(v => v * 1).forEach((item) => {
|
sum += item;
|
});
|
let max = data.length > 0 ? Math.max(...data) : 0;
|
let min = data.length > 0 ? Math.min(...data) : 0;
|
let avg = data.length > 0 ? sum / data.length : 0;
|
return {
|
max,
|
min,
|
avg,
|
};
|
}
|
|
function updateBarChart() {
|
let dataList = barDatas.value[chartType.value];
|
let opt = chartTypes.find((item) => item.value == chartType.value);
|
let fixed = opt.fixed;
|
let unit = opt.unit;
|
let name = opt.label;
|
let index = getDataIndex(dataList.length, slider.value);
|
let data = [];
|
if (index != -1) {
|
data = dataList[index];
|
let batNum = getBarNum(data);
|
monBarTitle.value =
|
"最大值=" +
|
batNum.max +
|
unit +
|
";最小值=" +
|
batNum.min +
|
unit +
|
";平均值=" +
|
toFixed(batNum.avg, fixed) +
|
unit;
|
} else {
|
console.log("未获取到值");
|
}
|
|
// 设置柱状图
|
chart1.value.updateChart(monNames.value, { name, data }, bgDatas.value[chartType.value]);
|
}
|
|
|
// 格式化滑块显示的内容
|
function formatTooltip(value) {
|
let index = getDataIndex(testTLong.value.length, value);
|
let test_long = formatSeconds(0);
|
if (index != -1) {
|
test_long = formatSeconds(testTLong[index]);
|
}
|
return test_long;
|
}
|
// 拖动滑块时触发
|
function sliderInput() {
|
updateBarChart();
|
}
|
// 根据百分比获取显示的数据的笔数
|
function getDataIndex(num, percent) {
|
if (percent <= 0) {
|
return 0;
|
}
|
return Math.floor((num * percent) / 100) - 1;
|
}
|
|
function changeChartType() {
|
updateBarChart();
|
}
|
|
onMounted(() => {
|
if (chart0.value && chart2.value && chart3.value) {
|
echarts.connect([chart0.value.getChart(), chart2.value.getChart(), chart3.value.getChart()]);
|
|
}
|
getLists();
|
});
|
</script>
|
|
<template>
|
<div class="page-his">
|
<div class="filter page-filter">
|
<div class="item" v-for="(item, idx) in testTypes" :key="'list0_' + idx">
|
<div class="label">{{ item.label }}</div>
|
<div class="value">
|
<el-select v-model="selectValues[item.prop]" size="small" placeholder="请选择" @change="filterChange(item.prop)">
|
<el-option v-for="(item, idx) in testRecordList[item.prop]" :key="'list5_' + idx" :label="item.label"
|
:value="item.value">
|
</el-option>
|
</el-select>
|
</div>
|
</div>
|
</div>
|
<div class="info">
|
<div :class="['status-item',]" v-for="(item, index) in statusList" :key="'status_' + index">
|
<div class="item-value" v-if="item.prop == 'captestTimelong' || item.prop == 'restTime'">
|
{{ testInfo[item.prop] ?
|
formatSeconds(testInfo[item.prop]) : '--' }}
|
</div>
|
<div class="item-value time" v-else-if="item.prop == 'testStarttime'">
|
{{ testInfo[item.prop] || '--' }}
|
</div>
|
<div class="item-value" v-else>
|
{{ testInfo[item.prop]}}{{ item.unit }}
|
</div>
|
<div class="item-name">{{ item.label }}</div>
|
</div>
|
<div class="info-item" v-for="(item, index) in infoList" :key="'info_' + index">
|
<div class="label">{{ item.label }}({{ item.unit }})</div>
|
<div class="value">{{ testInfo[item.prop] }}</div>
|
</div>
|
</div>
|
<div class="main">
|
<div class="grid">
|
<div class="g-item">
|
<card title="端电压折线图(V)">
|
<line-chart ref="chart0"></line-chart>
|
</card>
|
</div>
|
<div class="g-item">
|
<card :title="monBarTitle">
|
<template #tools>
|
<el-select v-model="chartType" :disabled="!currProp" size="small" @change="changeChartType">
|
<el-option v-for="item in chartTypes" :key="item.value" :label="item.label"
|
:value="item.value"></el-option>
|
</el-select>
|
</template>
|
<bar-chart ref="chart1"></bar-chart>
|
</card>
|
</div>
|
<div class="g-item">
|
<card title="电池电流折线图(A)">
|
<line-chart ref="chart2"></line-chart>
|
</card>
|
</div>
|
<div class="g-item">
|
<card title="单体电压(V)">
|
<line-chart ref="chart3"></line-chart>
|
</card>
|
</div>
|
</div>
|
</div>
|
<div class="footer">
|
<el-slider v-model="slider" size="small" :format-tooltip="formatTooltip" @input="sliderInput" />
|
</div>
|
</div>
|
</template>
|
|
<style scoped lang="less">
|
.page-his {
|
height: 100%;
|
display: flex;
|
flex-direction: column;
|
}
|
|
.filter {
|
background: #073451;
|
display: flex;
|
// flex-wrap: wrap;
|
align-items: center;
|
padding: 8px;
|
font-size: 16px;
|
color: #45beea;
|
|
.item {
|
flex: 1;
|
display: flex;
|
|
&~.item {
|
margin-left: 10px;
|
}
|
|
.label {
|
margin-right: 10px;
|
|
&::after {
|
content: ":";
|
}
|
}
|
|
.value {
|
flex: 1;
|
}
|
}
|
}
|
|
.info {
|
height: 72px;
|
display: flex;
|
flex-direction: row;
|
|
.status-item {
|
flex: 1;
|
font-size: 14px;
|
font-weight: 700;
|
color: #50c7f1;
|
// border: 1px solid #0F6B79;
|
border-radius: 6px;
|
margin: 4px 2px;
|
display: flex;
|
flex-direction: column;
|
justify-content: center;
|
align-items: center;
|
box-shadow: inset 0 0 30px -10px #0F6B79;
|
background: radial-gradient(circle at -10% center, #1F6571 5%, transparent 20%) no-repeat,
|
radial-gradient(circle at 110% center, #1F6571 5%, transparent 20%) no-repeat,
|
radial-gradient(circle at center -72%, #1F6571 12%, transparent 50%) no-repeat,
|
radial-gradient(circle at center 138%, #1F6571 12%, transparent 50%) no-repeat;
|
|
.item-value {
|
margin-bottom: 6px;
|
color: #ff0;
|
font-size: 20px;
|
|
&.time {
|
white-space: nowrap;
|
font-size: 12px;
|
}
|
}
|
|
.item-name {
|
color: #6DCCE6;
|
}
|
}
|
|
.info-item {
|
flex: 1.2;
|
display: flex;
|
align-items: center;
|
margin-left: 2px;
|
|
.label {
|
color: #6DCCE6;
|
font-size: 12px;
|
margin-bottom: 4px;
|
|
&::after {
|
content: ":";
|
}
|
}
|
|
.value {
|
color: #6DCCE6;
|
font-size: 14px;
|
background: #134263;
|
flex: 1;
|
border-radius: 4px;
|
padding-left: 6px;
|
margin-left: 4px;
|
margin-right: 6px;
|
min-height: 1.8em;
|
display: flex;
|
align-items: center;
|
}
|
}
|
}
|
|
.main {
|
flex: 1;
|
padding: 8px;
|
|
.grid {
|
height: 100%;
|
display: grid;
|
grid-template-columns: repeat(2, 1fr);
|
grid-template-rows: repeat(2, 1fr);
|
grid-gap: 10px 8px;
|
place-content: stretch;
|
|
// place-items: center stretch;
|
// justify-items: stretch;
|
// align-items: stretch;
|
.g-item {
|
display: flex;
|
align-items: center;
|
justify-content: center;
|
|
:deep(.card) {
|
width: 100%;
|
|
.el-select {
|
width: 140px;
|
}
|
}
|
}
|
}
|
}
|
|
.footer {
|
padding: 0 8px;
|
}
|
</style>
|