<template>
|
<!-- <p>params: {{ $route.params }}</p> -->
|
<div class="main" ref="main">
|
<div class="container" ref="container">
|
<div
|
class="item"
|
v-for="item in windowList"
|
:ref="item + 'wrap'"
|
:key="'list_' + item"
|
>
|
<div class="bar">
|
<div class="title">{{ titles[item] }}</div>
|
<div class="btn-grp">
|
<div
|
:class="[
|
'btn',
|
'iconfont',
|
{
|
'icon-quanping': !fullScreen,
|
'icon-tuichuquanping': fullScreen,
|
},
|
]"
|
:title="fullScreen ? '还原' : '全屏'"
|
@click="toggleScreen(item)"
|
></div>
|
<div
|
class="btn iconfont icon-guanbi"
|
title="关闭"
|
@click="closeItem(item)"
|
></div>
|
</div>
|
</div>
|
<div class="content">
|
<normal-bar
|
v-if="item != 'tableVisiable'"
|
:ref="item"
|
:marks="marks[item]"
|
@contextmenu.native="(e) => chartContextClick(e, item)"
|
></normal-bar>
|
<div class="table-wrap" v-else>
|
<el-table
|
:data="tableData"
|
style="width: 100%"
|
ref="table"
|
:default-sort="{ prop: 'date', order: 'descending' }"
|
stripe
|
size="small"
|
height="100%"
|
class="tableCent"
|
tooltip-effect="light"
|
show-summary
|
:summary-method="summaryMethod"
|
>
|
<el-table-column
|
v-for="header in headers"
|
:key="header.prop"
|
:prop="header.prop"
|
:label="header.label"
|
:width="header.width"
|
:min-width="header.minWidth"
|
sortable
|
:sort-method="(a, b) => sortMethod(a, b, header.prop)"
|
show-overflow-tooltip
|
align="center"
|
></el-table-column>
|
</el-table>
|
</div>
|
</div>
|
</div>
|
</div>
|
<!-- 图表右键菜单 -->
|
<chart-context-menu
|
:getParentNode="getChartContextParentNode"
|
v-model="contextMenu.visible"
|
:context-data="contextMenu.node"
|
:style="{ left: contextMenu.x + 'px', top: contextMenu.y + 'px' }"
|
@toggleLabel="toggleLabel"
|
@updateChart="updateChart"
|
></chart-context-menu>
|
<!-- 文件属性 -->
|
<el-dialog
|
title="属性"
|
class="file-info"
|
:visible.sync="fileInfoVisible"
|
append-to-body
|
:close-on-click-modal="false"
|
:close-on-press-escape="false"
|
:show-close="false"
|
width="800px"
|
>
|
<file-info v-if="fileInfoVisible" :info="fileData" @ok="editOk" @quit="quit"></file-info>
|
</el-dialog>
|
</div>
|
</template>
|
|
<script>
|
import FileInfo from "@/components/fileInfo";
|
import NormalBar from "@/components/myCharts/NormalBar";
|
import ChartContextMenu from "@/components/chartContextMenu";
|
import offset from "@/assets/js/offset";
|
import { mapGetters } from "vuex";
|
import { Loading } from 'element-ui';
|
import {
|
getXmlValue,
|
testReport,
|
getInfoByFileId,
|
getParamByFileId,
|
updateFileParam,
|
} from "@/apis";
|
import { toFixed } from "@/assets/js/util";
|
// 保留三位小数
|
const BIT = 3;
|
|
export default {
|
name: "",
|
|
data() {
|
const titles = {
|
resVisiable: "内阻(mΩ)",
|
volVisiable: "电压(V)",
|
capVisiable: "容量(Ah)",
|
chainVisiable: "连接条(uΩ)",
|
condVisiable: "电导(S)",
|
tableVisiable: "数据表格",
|
};
|
const marks = {
|
resVisiable: [
|
{
|
name: "内阻告警",
|
y: 0,
|
type: "high",
|
color: "#ff0",
|
},
|
{
|
name: "内阻更换",
|
y: 0,
|
type: "high",
|
color: "#d9001b",
|
},
|
],
|
volVisiable: [
|
{
|
name: "高压告警",
|
y: 0,
|
type: "high",
|
color: "#ff0",
|
},
|
{
|
name: "低压告警",
|
y: 0,
|
type: "low",
|
color: "#d9001b",
|
},
|
],
|
chainVisiable: [
|
{
|
name: "连接条",
|
y: 0,
|
type: "high",
|
color: "#d9001b",
|
},
|
],
|
};
|
return {
|
fileInfoVisible: false,
|
fileData: {},
|
tmpParams: null,
|
stdDevBr: "",
|
stdDevBs: "",
|
stdDevBv: "",
|
stdDevCr: "",
|
resPic: "",
|
volPic: "",
|
fileParam: undefined,
|
stationInfo: undefined,
|
marks,
|
fullScreen: false,
|
titles,
|
list: [],
|
fileUrl: this.$route.query.url,
|
fileId: this.$route.query.fileId,
|
tableData: [],
|
headers: [
|
{
|
prop: "monNum",
|
label: "电池",
|
minWidth: 80,
|
},
|
{
|
prop: "bv",
|
label: "电压(V)",
|
minWidth: 100,
|
},
|
{
|
prop: "br",
|
label: "内阻(mΩ)",
|
minWidth: 100,
|
},
|
{
|
prop: "cr",
|
label: "连接条(μΩ)",
|
minWidth: 120,
|
},
|
{
|
prop: "bs",
|
label: "电导(S)",
|
minWidth: 100,
|
},
|
],
|
contextMenu: {
|
x: 0,
|
y: 0,
|
visible: false,
|
node: {},
|
},
|
windowList: [],
|
};
|
},
|
components: {
|
NormalBar,
|
ChartContextMenu,
|
FileInfo,
|
},
|
computed: {
|
...mapGetters("setting", [
|
"volVisiable",
|
"resVisiable",
|
"capVisiable",
|
"chainVisiable",
|
"condVisiable",
|
"tableVisiable",
|
"params",
|
]),
|
},
|
methods: {
|
formatData(data) {
|
let xLabel = [];
|
let resData = [],
|
volData = [],
|
condData = [],
|
capData = [],
|
chainData = [],
|
tableData = [];
|
data.forEach((v) => {
|
xLabel.push("#" + v.monNum);
|
resData.push(v.br);
|
volData.push(v.bv);
|
condData.push(v.bs);
|
// capData.push() TODO
|
chainData.push(v.cr);
|
tableData.push({ ...v, monNum: "#" + v.monNum });
|
});
|
return {
|
xLabel,
|
resData,
|
volData,
|
condData,
|
capData,
|
chainData,
|
tableData,
|
};
|
},
|
formatNum(n) {
|
return isNaN(n)
|
? ("" + n).indexOf("#") != -1
|
? ("" + n).replace("#", "") * 1
|
: 0
|
: n;
|
},
|
sortMethod(a, b, prop) {
|
// console.log(a, b);
|
a = this.formatNum(a[prop]);
|
b = this.formatNum(b[prop]);
|
return a - b;
|
},
|
summaryMethod(params) {
|
let { columns, data } = params;
|
let res = [];
|
// console.log(columns, data);
|
let len = data.length;
|
columns.forEach((v, i) => {
|
if (0 == i) {
|
res[i] = "均一性";
|
return;
|
}
|
let values = data.map((val) => Number(val[v.property]));
|
if (values.some((v) => isNaN(v))) {
|
res[i] = "N/A";
|
} else {
|
// 平均值
|
let average = values.reduce((total, v) => total + v, 0) / len;
|
// 差值平方和
|
let sum = values.reduce(
|
(total, v) => total + Math.pow(v - average, 2),
|
0
|
);
|
res[i] = Math.round(Math.sqrt(sum / len) * 10000) / 100 + "%";
|
}
|
});
|
return res;
|
},
|
toggleLabel(data) {
|
this.$refs[data][0].toggleLabelVisible();
|
},
|
updateChart(data) {
|
this.$refs[data.ref][0].setData(data.data);
|
},
|
getInfoByFileId() {
|
getInfoByFileId(this.fileId).then((res) => {
|
let { code, data, data2 } = res.data;
|
if (code) {
|
let fileParam = data.fileParam;
|
|
const {
|
chainResCoeK5,
|
resBadCoeK4,
|
resGoodCoeK3,
|
vol2HighCoeK2,
|
vol2LowCoeK1,
|
vol12HighCoeK2,
|
vol12LowCoeK1,
|
} = this.params;
|
|
this.marks.resVisiable[0].y = toFixed(
|
resGoodCoeK3 * fileParam.battRes / 100,
|
BIT
|
);
|
this.marks.resVisiable[1].y = toFixed(
|
resBadCoeK4 * fileParam.battRes / 100,
|
BIT
|
);
|
if (2 == fileParam.battVol) {
|
this.marks.volVisiable[0].y = toFixed(
|
vol2HighCoeK2,
|
BIT
|
);
|
this.marks.volVisiable[1].y = toFixed(
|
vol2LowCoeK1,
|
BIT
|
);
|
} else if (12 == fileParam.battVol) {
|
this.marks.volVisiable[0].y = toFixed(
|
vol12HighCoeK2,
|
BIT
|
);
|
this.marks.volVisiable[1].y = toFixed(
|
vol12LowCoeK1,
|
BIT
|
);
|
} else {
|
this.marks.volVisiable = [];
|
}
|
|
this.marks.chainVisiable[0].y = toFixed(
|
chainResCoeK5 * fileParam.chainRes / 100,
|
BIT
|
);
|
|
this.list = data.dataList;
|
|
this.stdDevBr = data2.stdDevBr;
|
this.stdDevBs = data2.stdDevBs;
|
this.stdDevBv = data2.stdDevBv;
|
this.stdDevCr = data2.stdDevCr;
|
this.fileParam = fileParam;
|
this.stationInfo = data.stationInfo;
|
this.$nextTick(() => {
|
this.initChart();
|
});
|
} else {
|
this.$message.error("查询数据失败");
|
}
|
});
|
},
|
getFileInfo() {
|
const params = {
|
filePath: this.fileUrl,
|
};
|
getXmlValue(params).then((res) => {
|
const { code, data, data2 } = res.data;
|
if (code && data) {
|
// console.log(data2, "====d");
|
this.fileData = this.tmpParams || data2.fileParam;
|
let fileParam = this.fileData;
|
|
const {
|
chainResCoeK5,
|
resBadCoeK4,
|
resGoodCoeK3,
|
vol2HighCoeK2,
|
vol2LowCoeK1,
|
vol12HighCoeK2,
|
vol12LowCoeK1,
|
} = this.params;
|
|
this.marks.resVisiable[0].y = toFixed(
|
resGoodCoeK3 * fileParam.battRes / 100,
|
BIT
|
);
|
this.marks.resVisiable[1].y = toFixed(
|
resBadCoeK4 * fileParam.battRes / 100,
|
BIT
|
);
|
if (2 == fileParam.battVol) {
|
this.marks.volVisiable[0].y = toFixed(
|
vol2HighCoeK2,
|
BIT
|
);
|
this.marks.volVisiable[1].y = toFixed(
|
vol2LowCoeK1,
|
BIT
|
);
|
} else if (12 == fileParam.battVol) {
|
this.marks.volVisiable[0].y = toFixed(
|
vol12HighCoeK2,
|
BIT
|
);
|
this.marks.volVisiable[1].y = toFixed(
|
vol12LowCoeK1,
|
BIT
|
);
|
} else {
|
this.marks.volVisiable = [];
|
}
|
|
this.marks.chainVisiable[0].y = toFixed(
|
chainResCoeK5 * fileParam.chainRes / 100,
|
BIT
|
);
|
|
this.list = data2.battInfoList[0].battDataList;
|
let columns = ["", "bv", "br", "cr", "bs"].map((v) => ({
|
property: v,
|
}));
|
let [a, stdDevBv, stdDevBr, stdDevCr, stdDevBs] = this.summaryMethod({
|
columns,
|
data: this.list,
|
});
|
this.stdDevBr = stdDevBr;
|
this.stdDevBs = stdDevBs;
|
this.stdDevBv = stdDevBv;
|
this.stdDevCr = stdDevCr;
|
this.fileParam = fileParam;
|
this.$nextTick(() => {
|
this.initChart();
|
});
|
} else {
|
this.$message.error("文件解析失败");
|
}
|
});
|
},
|
initChart() {
|
let {
|
xLabel,
|
resData,
|
volData,
|
condData,
|
capData,
|
chainData,
|
tableData,
|
} = this.formatData(this.list);
|
this.tableData = tableData;
|
const obj = {
|
resVisiable: resData,
|
volVisiable: volData,
|
capVisiable: capData,
|
chainVisiable: chainData,
|
condVisiable: condData,
|
};
|
this.windowList.forEach((v) => {
|
let chart = this.$refs[v];
|
// debugger;
|
if (!chart || !chart.length) {
|
return;
|
}
|
chart[0].setData({
|
xLabel,
|
sData: obj[v],
|
});
|
chart[0].resize();
|
|
if (v == "resVisiable") {
|
this.resPic = chart[0].getDataURL();
|
}
|
if (v == "volVisiable") {
|
this.volPic = chart[0].getDataURL();
|
}
|
});
|
this.$nextTick(() => {
|
if (this.$refs.table[0]) {
|
this.$refs.table[0].doLayout();
|
}
|
});
|
},
|
resize() {
|
this.$nextTick(() => {
|
this.windowList.forEach((v) => {
|
let chart = this.$refs[v];
|
if (!chart || !chart.length) {
|
return;
|
}
|
chart[0].resize();
|
});
|
if (this.$refs.table[0]) {
|
this.$refs.table[0].doLayout();
|
}
|
});
|
},
|
chartContextClick($e, item) {
|
const { clientX, clientY, target } = $e;
|
const container = this.$refs.container;
|
let { left, top } = offset(container);
|
if (this.fullScreen) {
|
left = 0;
|
top = 0;
|
}
|
let y = clientY - top;
|
let x = clientX - left;
|
let { clientHeight: bodyHeight, clientWidth: bodyWidth } = document.body;
|
if (clientY + 105 > bodyHeight) {
|
y = bodyHeight - 105 - top;
|
}
|
if (clientX + 120 > bodyWidth) {
|
x = bodyWidth - 120 - left;
|
}
|
let { xLabel, resData, volData, condData, capData, chainData } =
|
this.formatData(this.list);
|
const obj = {
|
resVisiable: resData,
|
volVisiable: volData,
|
capVisiable: capData,
|
chainVisiable: chainData,
|
condVisiable: condData,
|
};
|
this.contextMenu.x = x;
|
this.contextMenu.y = y;
|
this.contextMenu.node = { xLabel, data: obj[item], ref: item };
|
this.contextMenu.visible = true;
|
},
|
addListener() {
|
document.addEventListener("fullscreenchange", this.fullScreenListener);
|
document.addEventListener(
|
"webkitfullscreenchange",
|
this.fullScreenListener
|
);
|
},
|
removeListener() {
|
document.removeEventListener("fullscreenchange", this.fullScreenListener);
|
document.removeEventListener(
|
"webkitfullscreenchange",
|
this.fullScreenListener
|
);
|
},
|
fullScreenListener() {
|
// console.log(e);
|
this.fullScreen = !this.fullScreen;
|
this.resize();
|
},
|
toggleScreen(item) {
|
if (this.fullScreen) {
|
this.outFullScreen(item);
|
} else {
|
this.inFullScreen(item + "wrap");
|
}
|
},
|
inFullScreen(item) {
|
const el = this.$refs[item][0];
|
if (el.requestFullscreen) {
|
el.requestFullscreen();
|
return true;
|
} else if (el.webkitRequestFullScreen) {
|
el.webkitRequestFullScreen();
|
return true;
|
}
|
return false;
|
},
|
outFullScreen() {
|
if (document.exitFullscreen) {
|
document.exitFullscreen();
|
} else if (document.webkitCancelFullScreen) {
|
document.webkitCancelFullScreen();
|
} else if (document.mozCancelFullScreen) {
|
document.mozCancelFullScreen();
|
} else if (document.msExitFullscreen) {
|
document.msExitFullscreen();
|
}
|
},
|
getChartContextParentNode() {
|
const ref = this.contextMenu.node.ref + "wrap";
|
if (!this.fullScreen) {
|
return this.$refs.main;
|
} else {
|
return this.$refs[ref][0];
|
}
|
},
|
closeItem(item) {
|
if (this.fullScreen) {
|
this.toggleScreen(item);
|
}
|
if (this.windowList.length <= 1) {
|
this.$message.warning("最少保留一个模块");
|
return;
|
}
|
const idx = this.windowList.indexOf(item);
|
// console.log(idx)
|
this.windowList.splice(idx, 1);
|
this.$nextTick(() => {
|
this.resize();
|
});
|
},
|
getWindowList() {
|
this.windowList = [
|
"resVisiable",
|
"volVisiable",
|
"capVisiable",
|
"condVisiable",
|
"chainVisiable",
|
"tableVisiable",
|
].filter((v) => this[v]);
|
},
|
activeFN() {
|
this.$nextTick(() => {
|
this.resize();
|
});
|
},
|
editOk(data) {
|
// 临时文件不修改
|
if (!this.fileId) {
|
this.tmpParams = data;
|
this.refreshPage();
|
this.fileInfoVisible = false;
|
return false;
|
}
|
updateFileParam(this.fileId, data).then((res) => {
|
const { code, data } = res.data;
|
if (code && data) {
|
this.$message.success("修改成功");
|
this.refreshPage();
|
this.fileInfoVisible = false;
|
} else {
|
this.$message.error("修改失败");
|
}
|
});
|
},
|
quit() {
|
this.fileInfoVisible = false;
|
},
|
viewProp() {
|
if (this._isDestroyed || this._directInactive) {
|
return false;
|
}
|
if (!this.fileId) {
|
const params = {
|
filePath: this.fileUrl,
|
};
|
getXmlValue(params).then((res) => {
|
const { code, data, data2 } = res.data;
|
if (code && data) {
|
// console.log(data2, "====d");
|
this.fileData = data2.fileParam;
|
this.fileInfoVisible = true;
|
} else {
|
this.$message.error("读取文件失败");
|
}
|
});
|
return false;
|
}
|
getParamByFileId(this.fileId).then((res) => {
|
const { code, data, data2 } = res.data;
|
if (code && data) {
|
this.fileData = data2;
|
this.fileInfoVisible = true;
|
} else {
|
this.$message.error("获取参数失败");
|
}
|
});
|
},
|
refreshPage(force) {
|
if (!force && (this._isDestroyed || this._directInactive)) {
|
return false;
|
}
|
this.getWindowList();
|
if (this.fileId) {
|
this.getInfoByFileId();
|
} else {
|
this.getFileInfo();
|
}
|
},
|
exportReport() {
|
if (this._isDestroyed || this._directInactive) {
|
return false;
|
}
|
let loading = Loading.service({ fullscreen: true });
|
// console.log(111,this._isDestroyed, this._directInactive, 'export')
|
const {
|
resPic,
|
volPic,
|
list: dataList,
|
fileParam,
|
stationInfo,
|
stdDevBr,
|
stdDevBs,
|
stdDevBv,
|
stdDevCr,
|
} = this;
|
let params = {
|
dataList,
|
fileParam,
|
resPic,
|
volPic,
|
stationInfo,
|
stdDevBr,
|
stdDevBs,
|
stdDevBv,
|
stdDevCr,
|
};
|
testReport(params).then((res) => {
|
let { headers, data, status } = res;
|
loading.close();
|
if (200 == status && data) {
|
let url = window.URL.createObjectURL(data);
|
const matchRes = /filename=(.*)/.exec(headers["content-disposition"]);
|
const fileName = matchRes
|
? decodeURI(matchRes[1].trim())
|
: "未知文件名.xls";
|
|
let link = document.createElement("a");
|
link.style.display = "none";
|
link.href = url;
|
link.download = fileName;
|
document.body.appendChild(link);
|
link.click();
|
document.body.removeChild(link);
|
window.URL.revokeObjectURL(url);
|
} else {
|
this.$message.error("操作失败");
|
}
|
});
|
},
|
},
|
|
mounted() {
|
// console.log(this.params, "params");
|
this.getWindowList();
|
if (this.fileId) {
|
this.getInfoByFileId();
|
} else {
|
this.getFileInfo();
|
}
|
if (this.$route.query.params) {
|
this.tmpParams = this.$route.query.params;
|
}
|
this.addListener();
|
this.$bus.$on("checkScroll", this.resize);
|
this.$bus.$on("chartResize", this.resize);
|
this.$bus.$on("export", this.exportReport);
|
this.$bus.$on("viewProp", this.viewProp);
|
this.$bus.$on("paramsUpdated", () => {
|
this.refreshPage(true);
|
});
|
this.$bus.$on("windowConfigChanged", () => {
|
this.refreshPage(true);
|
});
|
},
|
};
|
</script>
|
|
<style lang="less" scoped>
|
.container {
|
width: 100%;
|
height: 100%;
|
display: flex;
|
flex-wrap: wrap;
|
}
|
.item {
|
display: flex;
|
flex-direction: column;
|
.bar {
|
display: flex;
|
justify-content: space-between;
|
background: #f0f0f0;
|
padding-left: 8px;
|
padding-right: 8px;
|
.btn-grp {
|
display: flex;
|
align-items: center;
|
.btn {
|
margin: 0 4px;
|
cursor: pointer;
|
&:hover {
|
background: rgba(200, 200, 200, 0.6);
|
}
|
}
|
}
|
}
|
.content {
|
flex: 1;
|
background: #0029a0;
|
position: relative;
|
.table-wrap {
|
position: absolute;
|
top: 0;
|
right: 0;
|
left: 0;
|
bottom: 0;
|
:deep(.el-table__footer) tr {
|
background: #fa8e34;
|
td {
|
background: transparent;
|
color: #fff;
|
}
|
}
|
}
|
}
|
}
|
.item:only-child {
|
width: 100%;
|
height: 100%;
|
}
|
.item ~ .item {
|
margin-left: 4px;
|
}
|
.item:first-child:nth-last-child(3) ~ .item:last-child,
|
.item:first-child:nth-last-child(4) ~ .item:nth-child(3),
|
.item:first-child:nth-last-child(5) ~ .item:nth-child(4),
|
.item:first-child:nth-last-child(6) ~ .item:nth-child(4) {
|
margin-left: 0;
|
}
|
|
.item:first-child:nth-last-child(2),
|
.item:first-child:nth-last-child(2) ~ .item {
|
width: calc(50% - 2px);
|
height: 100%;
|
}
|
.item:first-child:nth-last-child(3),
|
.item:first-child:nth-last-child(3) + .item {
|
width: calc(50% - 2px);
|
height: 50%;
|
}
|
.item:first-child:nth-last-child(3) ~ .item:last-child {
|
width: 100%;
|
height: 50%;
|
}
|
.item:first-child:nth-last-child(4),
|
.item:first-child:nth-last-child(4) ~ .item {
|
width: calc(50% - 2px);
|
height: 50%;
|
}
|
.item:first-child:nth-last-child(5),
|
.item:first-child:nth-last-child(5) ~ .item {
|
width: calc((100% - 4px * 2) / 3);
|
height: 50%;
|
}
|
.item:first-child:nth-last-child(5) ~ .item:nth-last-child(2),
|
.item:first-child:nth-last-child(5) ~ .item:last-child {
|
width: calc(50% - 2px);
|
height: 50%;
|
}
|
.item:first-child:nth-last-child(6),
|
.item:first-child:nth-last-child(6) ~ .item {
|
width: calc((100% - 4px * 2) / 3);
|
height: 50%;
|
}
|
</style>
|