Files
smart_storage_app/pages/tabbar/components/topology/standard.vue

647 lines
14 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<view class="warp">
<topoCanvas ref="topoCanvasRef" :cId="canvasId" :width="'100%'" :height="'100%'" :canvas-data="canvasData" :noloading="noloading" />
</view>
</template>
<script>
import topoCanvas from '@/components/new-canvas/index.vue'
export default {
components: {
topoCanvas
},
data() {
return {
noloading: false,
canvasData: [],
canvasId:'canvas',
// 文字
textCanvasData: [
// 并网柜--0
{
type: "text",
coord: [
[145, 65]
],
font: [{
text: this.$t("homePage.home.gridCab"),
size: 12,
color: "#333333",
width: 50
}]
},
//1# 直流舱--1
{
type: "text",
coord: [
[15, 65]
],
font: [{
text: this.$t("homePage.home.grid"),
size: 12,
color: "#333333",
width: 50
}]
},
// //1#PCS升压一体舱--2
{
type: "text",
coord: [
[260, 65]
],
font: [{
text: this.$t("homePage.home.esiCab"),
size: 12,
color: "#333333",
width: 50
}]
},
//---3
{
type: "text",
coord: [
[190, 155]
],
font: [{
text: this.$t("homePage.home.workStatus"),
size: 12,
color: "#666666",
width: 50
},
{
text: "",
size: 12,
color: "#333333",
left: 55
}
]
},
//---4
{
type: "text",
coord: [
[187, 170]
],
font: [{
text: this.$t("homePage.home.activePower"),
size: 12,
color: "#666666",
width: 50
},
{
text: "",
size: 12,
color: "#333333",
left: 80
}
]
},
//---5
{
type: "text",
coord: [
[225, 185]
],
font: [{
text: "SOC(%)",
size: 12,
color: "#666666",
width: 50
},
{
text: "",
size: 12,
color: "#333333",
left: 50
}
]
},
// //---6
{
type: "text",
coord: [
[225, 200]
],
font: [{
text: "SOH(%)",
size: 12,
color: "#666666",
width: 50
},
{
text: "",
size: 12,
color: "#333333",
left: 50
}
]
},
],
// 图片
imageCanvasData: [{
//电网
type: "image",
url: "/static/topology/dianwang.png",
coord: [
[10, 80],
[40, 50]
]
},
{
//一体柜
type: "image",
url: "/static/topology/yitigui.png",
coord: [
[140, 80],
[40, 50]
]
},
{
//一体舱
type: "image",
url: "/static/topology/yiticang.png",
coord: [
[270, 80],
[40, 50]
]
},
],
// 线
lineCanvasData: [
// 电网---并网柜
{
type: "line",
coord: [
[50, 110],
[140, 110]
],
color: "#19875c",
width: 2,
dash: [10, 5]
},
// 1直流舱--并网柜
{
type: "line",
coord: [
[260, 110],
[185, 110]
],
color: "#19875c",
width: 2,
dash: [10, 5]
},
],
// 点
circleCanvasData: [
{
type: "circle",
coord: [
[50, 110]
],
color: "#3AECA1",
r: 3
},
{
type: "circle",
coord: [
[260, 110]
],
color: "#3AECA1",
r: 3
},
],
animationId: null, // requestAnimationFrame ID
movingPoints: [
// 点1左上角 → 向右移动
{
x: 50,
y: 110,
path: [{
x: 50,
y: 110
}, // 起点
{
x: 140,
y: 110
} // 终点
],
currentStep: 0, // 当前在路径的哪一步
isActive: false, // 标识动画是否激活
id: 'point-1'
},
// 点2左上角 → 向右移动
{
x: 185,
y: 110,
path: [{
x: 185,
y: 110
}, // 起点
{
x: 260,
y: 110
} // 终点
],
currentStep: 0, // 当前在路径的哪一步
isActive: false, // 标识动画是否激活
id: 'point-2'
},
// 点3左上角 → 向右移动
{
x: 260,
y: 110,
path: [{
x: 260,
y: 110
}, // 起点
{
x: 185,
y: 110
} // 终点
],
currentStep: 0, // 当前在路径的哪一步
isActive: false, // 标识动画是否激活
id: 'point-3'
}
],
pointSpeed: 1.5, // 移动速度(像素/帧)
kWValues: {
right: null // 存储 getRightPcs 的 kW 值
}
}
},
computed: {
currentStation() {
return this.vuex_currentStation;
},
},
beforeDestroy() {
// 强制取消动画帧
if (this.animationId) {
if (typeof requestAnimationFrame !== 'undefined') {
cancelAnimationFrame(this.animationId);
} else {
clearTimeout(this.animationId);
}
this.animationId = null; // 重置ID
}
// 重置所有动态点状态
this.movingPoints.forEach(point => {
point.isActive = false;
point.currentStep = 0;
point.x = point.path[0].x;
point.y = point.path[0].y;
});
// 清空canvasData
this.canvasData = [];
},
watch:{
stationId(newVal,oldVal){
console.log(newVal,oldVal);
if (newVal) {
this.getData(newVal);
}
}
},
methods: {
// 启动动画
startAnimation() {
const canvasRef = this.$refs.topoCanvasRef;
if (!canvasRef) return;
// 清除旧动画
if (this.animationId !== null) {
if (requestAnimationFrame && this.animationId) {
cancelAnimationFrame(this.animationId);
} else {
clearTimeout(this.animationId);
}
this.animationId = null;
}
// 直接启动新动画
this.animate();
},
animate() {
const canvasRef = this.$refs.topoCanvasRef;
if (!canvasRef) return;
const animateFrame = () => {
// 1. 更新移动点的位置
this.movingPoints.forEach(point => {
if (!point.isActive) return;
const currentIndex = point.currentStep;
const nextTarget = point.path[currentIndex + 1];
if (!nextTarget) {
point.x = point.path[0].x;
point.y = point.path[0].y;
point.currentStep = 0;
return;
}
const dx = nextTarget.x - point.x;
const dy = nextTarget.y - point.y;
const distance = Math.sqrt(dx * dx + dy * dy);
if (distance < this.pointSpeed) {
point.x = nextTarget.x;
point.y = nextTarget.y;
point.currentStep++;
} else {
point.x += (dx / distance) * this.pointSpeed;
point.y += (dy / distance) * this.pointSpeed;
}
});
// 2. 更新 canvasData 中的动态点
this.updateMovingPoints();
// 3. 清除并重绘
canvasRef.clear();
canvasRef.draw(null);
// 4. 继续下一帧(✅ 安全判断)
if (typeof requestAnimationFrame !== 'undefined') {
this.animationId = requestAnimationFrame(animateFrame);
} else {
this.animationId = setTimeout(animateFrame, 16);
}
};
// 启动第一帧(✅ 安全判断)
if (typeof requestAnimationFrame !== 'undefined') {
requestAnimationFrame(animateFrame);
} else {
setTimeout(animateFrame, 16);
}
},
updateMovingPoints() {
this.movingPoints.forEach(point => {
const dataIndex = this.canvasData.findIndex(item => item.id === point.id);
if (dataIndex !== -1) {
this.canvasData[dataIndex].coord = [
[point.x, point.y]
];
}
});
},
changeEnglish() {
this.textCanvasData = [
// 并网柜--0
{
type: "text",
coord: [
[145, 65]
],
font: [{
text: this.$t("homePage.home.gridCab"),
size: 12,
color: "#333333",
width: 50
}]
},
//1# 直流舱--1
{
type: "text",
coord: [
[15, 65]
],
font: [{
text: this.$t("homePage.home.grid"),
size: 12,
color: "#333333",
width: 50
}]
},
// //1#PCS升压一体舱--2
{
type: "text",
coord: [
[260, 65]
],
font: [{
text: this.$t("homePage.home.esiCab"),
size: 12,
color: "#333333",
width: 50
}]
},
//---3
{
type: "text",
coord: [
[190, 155]
],
font: [{
text: this.$t("homePage.home.workStatus"),
size: 12,
color: "#666666",
width: 50
},
{
text: "",
size: 12,
color: "#333333",
left: 55
}
]
},
//---4
{
type: "text",
coord: [
[187, 170]
],
font: [{
text: this.$t("homePage.home.activePower"),
size: 12,
color: "#666666",
width: 50
},
{
text: "",
size: 12,
color: "#333333",
left: 80
}
]
},
//---5
{
type: "text",
coord: [
[225, 185]
],
font: [{
text: "SOC(%)",
size: 12,
color: "#666666",
width: 50
},
{
text: "",
size: 12,
color: "#333333",
left: 50
}
]
},
// //---6
{
type: "text",
coord: [
[225, 200]
],
font: [{
text: "SOH(%)",
size: 12,
color: "#666666",
width: 50
},
{
text: "",
size: 12,
color: "#333333",
left: 50
}
]
},
]
},
getData(val) {
// 彻底重置画布数据
this.canvasData = [];
// 重置所有点状态
this.movingPoints.forEach(point => (point.isActive = false,point.isMove = false,point.currentStep = 0,point.x = point.path[0].x,point.y = point.path[0].y));
this.stationId = val
this.clearData()
const api = [
this.getMiddleData()
];
Promise.all(api).finally((result) => {
// 重置所有点(确保干净)
this.movingPoints.forEach(p => (p.isActive = false));
this.movingPoints.find(p => p.id === 'point-1').isActive = true;
// 再设置点2-3的显示逻辑
this.updatePcsPoints();
this.canvasData = [...this.textCanvasData, ...this.imageCanvasData, ...this
.lineCanvasData, ...this.circleCanvasData
]
// 动态添加 movingPoints
this.movingPoints.forEach(point => {
this.canvasData.push({
type: "circle",
id: point.id,
coord: [
[point.x, point.y]
],
color: "#3AECA1",
r: 3,
isMove: true
});
});
this.noloading = true
this.$nextTick(() => {
this.startAnimation();
});
});
},
workStatus(val, data) {
if (data === 1) {
if (+val > +1) {
return this.$t("homePage.home.charge")
}
if (+val < -1) {
return this.$t("homePage.home.disCharge")
}
if (val < 1 || +val.abs < 1) {
return this.$t("homePage.home.standing")
}
} else {
if (+val > +1) {
return this.$t("homePage.home.disCharge")
}
if (+val < -1) {
return this.$t("homePage.home.charge")
}
if (val < 1 || +val.abs < 1) {
return this.$t("homePage.home.standing")
}
}
},
clearData() {
this.textCanvasData[3].font[1].text = ''
this.textCanvasData[4].font[1].text = ''
this.textCanvasData[5].font[1].text = ''
this.textCanvasData[6].font[1].text = ''
},
getMiddleData() {
let self = this
return new Promise((resolve, reject) => {
self.$u.api.homePageData
.GetbozhouTuopuStatus({
stationId: this.stationId
})
.then(res => {
if (res.data) {
const value = parseFloat(res.data.activePowerPCS);
this.kWValues.right = value;
this.textCanvasData[3].font[1].text = this.workStatus(res.data.activePowerPCS, res
.data.flowDirection)
this.textCanvasData[4].font[1].text = res.data.activePowerPCS !== null ? res.data
.activePowerPCS + '' : ''
this.textCanvasData[5].font[1].text = res.data.soc !== null ? res.data.soc + '' :
''
this.textCanvasData[6].font[1].text = res.data.soh !== null ? res.data.soh + '' :
''
} else {
this.kWValues.right = 0;
}
resolve()
}).catch(() => {
this.kWValues.right = 0;
resolve();
})
})
},
updatePcsPoints() {
const { right } = this.kWValues;
// 处理右边点5 和 点9
const point2 = this.movingPoints.find(p => p.id === 'point-2');
const point3 = this.movingPoints.find(p => p.id === 'point-3');
if (right === 0) {
point2.isActive = false;
point3.isActive = false;
} else if (right > 0) {
point2.isActive = true;
point3.isActive = false;
} else if (right < 0) {
point2.isActive = false;
point3.isActive = true;
}
}
}
}
</script>
<style lang="scss" scoped>
.warp {
width: 650rpx;
height: 550rpx;
position: relative;
}
</style>