Files
admin-govern/src/views/govern/device/control/moduleData.vue
2026-01-16 15:54:16 +08:00

677 lines
24 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>
<!-- 运行 运行(中断) 运行(故障) 离线 -->
<div :style="height" style="overflow-y: auto" v-loading="loading">
<div class="container">
<div class="tabs-container">
<!-- 左侧标签头 -->
<div class="tab-list" :style="titleHeight" style="overflow-y: auto" v-loading="loadingLeft">
<button
v-for="(item, index) in list"
:key="index"
:class="{ 'tab-button active': activeTab === index, 'tab-button': activeTab !== index }"
@click="changeTab(index)"
>
<span>{{ item.moduleName }}</span>
<el-tag class="ml10" :type="getType(item.moduleState)" size="small" effect="dark">
{{ item.moduleState }}
</el-tag>
</button>
</div>
<!-- 右侧内容区域 -->
<div class="tab-content-container flex-1" :key="activeTab">
<div v-if="echartList?.dataList != null" style="position: relative">
<el-tabs v-model="activeName" type="card" @tab-change="changeDataType">
<el-tab-pane label="状态" name="0">
<div :style="echartHeight" class="pt10">
<MyEchart :options="echartList.options" />
</div>
</el-tab-pane>
<el-tab-pane label="数据" name="1">
<div :style="echartHeight" class="pt10">
<MyEchart style="height: 100%" :options="echartsData" :key="key" />
<!-- <MyEchart style="height: 50%" :options="echartList.loadList" />
<MyEchart style="height: 50%" :options="echartList.modOutList" /> -->
</div>
</el-tab-pane>
</el-tabs>
<el-select
v-if="activeName == '1'"
v-model="dataType"
class="select"
placeholder="请选择"
multiple
collapse-tags
style="width: 150px"
@change="changeDataType"
>
<el-option label="负载电流" :value="0" />
<el-option label="输入电流" :value="1" />
<el-option label="温度" :value="2" />
</el-select>
</div>
<el-empty description="暂无数据" style="width: 100%; height: 100%" v-else></el-empty>
</div>
</div>
</div>
<!-- <el-collapse v-model="activeNames" v-loading="loading">
<el-collapse-item v-for="(item, index) in list" :key="index" :name="index">
<template #title>
<div class="header">
{{ item.moduleName }}
<el-tag
class="ml10"
:type="item.moduleState == '离线' ? 'danger' : 'success'"
size="small"
effect="dark"
>
{{ item.moduleState }}
</el-tag>
</div>
</template>
<div :style="echartHeight" style="min-height: 150px">
<MyEchart :options="item.options" v-if="item.dataList != null" />
<el-empty description="暂无数据" style="height: 130px" v-else></el-empty>
</div>
</el-collapse-item>
</el-collapse> -->
</div>
</template>
<script setup lang="ts">
import { ref, reactive, onBeforeUnmount } from 'vue'
import { mainHeight } from '@/utils/layout'
import { WarnTriangleFilled } from '@element-plus/icons-vue'
import { getModuleState } from '@/api/cs-device-boot/EquipmentDelivery'
import MyEchart from '@/components/echarts/MyEchart.vue'
import { ElMessage } from 'element-plus'
import { yMethod } from '@/utils/echartMethod'
const list: any = ref([])
const loading = ref(false)
const loadingLeft = ref(false)
const height = ref(mainHeight(290))
const titleHeight = ref(mainHeight(302))
const echartHeight = ref(mainHeight(342))
const activeTab = ref(0)
const activeName = ref('0')
const echartList: any = ref({})
const dataType = ref([0])
const key = ref(0)
import * as echarts from 'echarts' // 全引入
import { max } from 'lodash'
const echartsData: any = ref({
title: {
show: false
},
xAxis: {
type: 'time',
name: '',
axisLabel: {
formatter: {
day: '{MM}-{dd}',
month: '{MM}',
year: '{yyyy}'
}
}
},
tooltip: {
axisPointer: {
type: 'cross',
label: {
color: '#fff',
fontSize: 16
}
},
textStyle: {
color: '#fff',
fontStyle: 'normal',
opacity: 0.35,
fontSize: 14
},
backgroundColor: 'rgba(0,0,0,0.55)',
borderWidth: 0,
formatter(params: any) {
const xname = params[0].value[0]
let str = `${xname}<br>`
params.forEach((el: any, index: any) => {
let marker = ''
if (el.value[3] == 'dashed') {
for (let i = 0; i < 3; i++) {
marker += `<span style="display:inline-block;border: 2px ${el.color} solid;margin-right:5px;width:10px;height:0px;background-color:#ffffff00;"></span>`
}
} else {
marker = `<span style="display:inline-block;border: 2px ${el.color} ${el.value[3]};margin-right:5px;width:40px;height:0px;background-color:#ffffff00;"></span>`
}
str += `${marker}${el.seriesName.split('(')[0]}${
el.value[1] != null ? el.value[1] + ' ' + (el.value[2] || '') : '-'
}<br>`
})
return str
}
},
legend: {
itemWidth: 20,
itemHeight: 20,
itemStyle: { opacity: 0 }, //去圆点
type: 'scroll', // 开启滚动分页
right: 70
},
grid: {
top: '80px',
right: '50px',
},
yAxis: [{}],
options: {
// yAxis: [{}],
series: []
}
})
const setData = (data: any) => {
// activeTab.value = 0
// echartHeight.value = mainHeight(292 + data.length * 49, data.length)
data.forEach((item: any) => {
// console.log('🚀 ~ setData ~ data:', data)
if (item.dataList == null) return
item.dataList.map((k: any, i: any) => {
k.endtime = item.dataList[i + 1]?.time
})
item.options = {
// 鼠标提示
tooltip: {
axisPointer: {
type: 'cross',
label: {
color: '#fff',
fontSize: 16
}
},
formatter: function (params) {
// console.log("🚀 ~ data.forEach ~ params:", params)
let tip = ''
for (let i = 0; i < params.length; i++) {
if (params[i].seriesName != '事件标记') {
tip +=
params[i].marker +
params[i].name +
'<br/>' +
'开始时间:' +
params[i].value[1] +
'<br/>' +
'结束时间:' +
(params[i].value[2] || '-') +
'<br/>'
if (params[i].value[3] == 1) {
tip += '事件:' + params[i].value[4] + '<br/>'
}
} else {
// if (params[i].value[3] == 1) {
// tip += '事件:' + params[i].value[1] + '<br/>'
// }
}
}
return tip
}
},
legend: {
// data: ['运行', '停止', '离线','事件标记']
data: [
{ name: '运行' },
{ name: '停止' },
{ name: '离线' },
{
name: '事件标记',
icon: 'path://M0,10 L10,10 L5,0 Z', // 自定义三角形路径
textStyle: {
color: '#333',
fontSize: 14
}
}
]
},
xAxis: {
type: 'time',
name: '时间',
axisLabel: {
formatter: {
day: '{MM}-{dd}',
month: '{MM}',
year: '{yyyy}'
}
}
},
yAxis: {
show: false,
axisPointer: {
show: false // 隐藏Y轴上的指示器
}
},
color: ['#67c23a', '#e6a23c', '#f56c6c', '#ccc'],
series: [
{
name: '运行',
type: 'custom',
renderItem: renderItem,
encode: {
x: [1, 2],
y: 0
},
data: item.dataList
.filter((k: any, i: number) => k.stateDesc == '运行')
.map((p: any) => {
return {
name: '运行',
value: [0, p.time, p.endtime, p.dataType, p.eventName],
itemStyle: {
normal: {
color: '#67c23a'
}
}
}
})
},
{
name: '停止',
type: 'custom',
renderItem: renderItem,
encode: {
x: [1, 2],
y: 0
},
data: item.dataList
.filter((k: any, i: number) => k.stateDesc != '离线' && k.stateDesc != '运行')
.map((p: any) => {
return {
name: '停止',
value: [0, p.time, p.endtime, p.dataType, p.eventName],
itemStyle: {
normal: {
color: '#e6a23c'
}
}
}
})
},
{
name: '离线',
type: 'custom',
renderItem: renderItem,
encode: {
x: [1, 2],
y: 0
},
data: item.dataList
.filter((k: any, i: number) => k.stateDesc == '离线')
.map((p: any) => {
return {
name: '离线',
value: [0, p.time, p.endtime, p.dataType, p.eventName],
itemStyle: {
normal: {
color: '#f56c6c'
}
}
}
})
// [
// {
// name: '离线',
// value: [0, '2025-07-16 01:03:00', '2025-07-16 01:06:00', 353249],
// itemStyle: {
// normal: {
// color: '#f56c6c'
// }
// }
// }
// ]
},
{
name: '事件标记',
type: 'custom',
renderItem: renderMarker,
encode: {
x: 0,
y: 1
},
data: item.dataList
.filter((k: any) => k.dataType === 1)
.map((k: any) => [
k.time,
k.eventName,
k.stateDesc == '离线' ? '#f56c6c' : k.stateDesc == '运行' ? '#67c23a' : '#e6a23c',
k.dataType
]),
// data: [['2025-07-16 01:00:00','故障发生','#bd6d6c']],
z: 10 // 确保标记显示在甘特图上方
}
]
}
})
list.value = data
echartList.value = list.value[activeTab.value]
changeDataType()
// echarts.connect('group')
}
const lineStyle = [{ type: 'solid' }, { type: 'dashed' }, { type: 'dotted' }]
const changeDataType = () => {
// loadList 负载电流
// modOutList 输入电流
// console.log('🚀 ~ changeDataType ~ val:', list.value[activeTab.value])
echartsData.value.yAxis = [{}]
echartsData.value.options.series = []
let flag = dataType.value.includes(0) || dataType.value.includes(1)
if (flag) {
let data1 = dataType.value.includes(0)
? list.value[activeTab.value]?.loadList.map(k => (k.data == 3.14159 ? 0 : k.data))
: [0]
let data2 = dataType.value.includes(1)
? list.value[activeTab.value]?.modOutList.map(k => (k.data == 3.14159 ? 0 : k.data))
: [0]
let [modOuMin, modOuMax] = yMethod([...data1||[0], ...data2||[0]])
console.log("🚀 ~ changeDataType ~ modOuMin:", modOuMin,modOuMax)
echartsData.value.yAxis[0] = {
name: 'A',
yAxisIndex: 0,
max: modOuMax,
min: modOuMin,
splitNumber: 5,
minInterval: 1
}
if (dataType.value.includes(0)) {
echartsData.value.options.series.push(
{
name: 'A相负载电流',
type: 'line',
data: list.value[activeTab.value].loadList
.filter((k: any) => k.phasicType == 'A')
.map((k: any) => [k.time, k.data == 3.14159 ? null : k.data, 'A', lineStyle[0].type]),
smooth: true, //让线变得平滑
symbol: 'none',
color: '#DAA520',
lineStyle: lineStyle[0],
yAxisIndex: 0
},
{
name: 'B相负载电流',
type: 'line',
data: list.value[activeTab.value].loadList
.filter((k: any) => k.phasicType == 'B')
.map((k: any) => [k.time, k.data == 3.14159 ? null : k.data, 'A', lineStyle[0].type]),
smooth: true, //让线变得平滑
symbol: 'none',
color: '#2E8B57',
lineStyle: lineStyle[0],
yAxisIndex: 0
},
{
name: 'C相负载电流',
type: 'line',
data: list.value[activeTab.value].loadList
.filter((k: any) => k.phasicType == 'C')
.map((k: any) => [k.time, k.data == 3.14159 ? null : k.data, 'A', lineStyle[0].type]),
smooth: true, //让线变得平滑
symbol: 'none',
color: '#A52a2a',
lineStyle: lineStyle[0],
yAxisIndex: 0
}
)
}
if (dataType.value.includes(1)) {
echartsData.value.options.series.push(
{
name: 'A相输入电流',
type: 'line',
data: list.value[activeTab.value].modOutList
.filter((k: any) => k.phasicType == 'A')
.map((k: any) => [k.time, k.data == 3.14159 ? null : k.data, 'A', lineStyle[1].type]),
smooth: true, //让线变得平滑
symbol: 'none',
color: '#DAA520',
lineStyle: lineStyle[1],
yAxisIndex: 0
},
{
name: 'B相输入电流',
type: 'line',
data: list.value[activeTab.value].modOutList
.filter((k: any) => k.phasicType == 'B')
.map((k: any) => [k.time, k.data == 3.14159 ? null : k.data, 'A', lineStyle[1].type]),
smooth: true, //让线变得平滑
symbol: 'none',
color: '#2E8B57',
lineStyle: lineStyle[1],
yAxisIndex: 0
},
{
name: 'C相输入电流',
type: 'line',
data: list.value[activeTab.value].modOutList
.filter((k: any) => k.phasicType == 'C')
.map((k: any) => [k.time, k.data == 3.14159 ? null : k.data, 'A', lineStyle[1].type]),
smooth: true, //让线变得平滑
symbol: 'none',
color: '#A52a2a',
lineStyle: lineStyle[1],
yAxisIndex: 0
}
)
}
}
if (dataType.value.includes(2)) {
let [temperatureMin, temperatureMax] = yMethod(
list.value[activeTab.value].temperatureList.map(k => (k.data == 3.14159 ? 0 : k.data))
)
echartsData.value.yAxis[flag ? 1 : 0] = {
name: '℃',
yAxisIndex: flag ? 1 : 0,
splitNumber: 5,
max: temperatureMax,
min: temperatureMin,
minInterval: 1
}
echartsData.value.options.series.push({
name: '温度',
type: 'line',
data: list.value[activeTab.value].temperatureList.map((k: any) => [
k.time,
k.data == 3.14159 ? null : k.data,
'℃',
lineStyle[2].type
]),
smooth: true, //让线变得平滑
symbol: 'none',
color: '#DAA520',
lineStyle: lineStyle[2],
yAxisIndex: flag ? 1 : 0
})
}
key.value += 1
}
function renderItem(params, api) {
var categoryIndex = api.value(0)
var start = api.coord([api.value(1), categoryIndex])
var end = api.coord([api.value(2), categoryIndex])
var height = api.size([0, 1])[1] * 0.9
var rectShape = echarts.graphic.clipRectByRect(
{
x: start[0],
y: start[1] - height,
width: end[0] - start[0],
height: height
},
{
x: params.coordSys.x,
y: params.coordSys.y,
width: params.coordSys.width,
height: params.coordSys.height - 50
}
)
return (
rectShape && {
type: 'rect',
shape: rectShape,
style: api.style()
}
)
}
// 三角形标记渲染函数
function renderMarker(params, api) {
var point = api.coord([api.value(0), 0])
var symbolSize = 8
var offsetY = 40
// 计算三角形的三个点坐标
var points = [
[point[0], point[1] - symbolSize - offsetY], // 上顶点
[point[0] - symbolSize, point[1] + symbolSize - offsetY], // 左下顶点
[point[0] + symbolSize, point[1] + symbolSize - offsetY] // 右下顶点
]
return {
type: 'polygon',
shape: {
points: points
},
style: {
fill: api.value(2),
stroke: '#fff',
lineWidth: 1
},
label: {
position: [point[0] + symbolSize + 5, point[1]],
formatter: api.value(1),
fontSize: 12
}
}
}
const getModule = async (id: string) => {
loadingLeft.value = true
await getModuleState({ id: id }).then(res => {
res.data.forEach((item: any) => {
list.value.forEach((k: any) => {
if (k.moduleName === item.moduleName) {
k.moduleState = item.moduleState
}
})
})
ElMessage.success('刷新成功')
})
loadingLeft.value = false
}
const changeTab = (e: any) => {
echartList.value = list.value[e]
activeTab.value = e
changeDataType()
}
const getType = (type: string) => {
switch (type) {
case '运行':
return 'success'
case '离线':
return 'danger'
default:
return 'warning'
}
}
defineExpose({ setData, getModule })
onBeforeUnmount(() => {
echarts.disconnect('group')
})
</script>
<style lang="scss" scoped>
.container {
margin: 10px 10px 0 10px;
background-color: white;
border-radius: 0.5rem;
// box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
overflow: hidden;
}
.tabs-container {
display: flex;
flex-direction: column;
height: 100%;
}
@media (min-width: 768px) {
.tabs-container {
flex-direction: row;
}
}
.tab-list {
width: 100%;
border-bottom: 1px solid #e2e8f0;
background-color: #f8fafc;
}
@media (min-width: 768px) {
.tab-list {
width: 140px;
border-right: 1px solid #e2e8f0;
border-bottom: none;
}
}
.tab-button {
display: flex;
align-items: center;
width: 100%;
padding: 15px 20px;
text-align: left;
border: none;
background: none;
cursor: pointer;
font-size: 14px;
color: #64748b;
border-right: 4px solid transparent;
transition: all 0.2s ease;
}
.tab-button:hover {
background-color: #f1f5f9;
color: #475569;
}
.tab-button.active {
background-color: #e2e8f0;
color: #000;
border-right-color: var(--el-color-primary);
font-weight: 700;
}
.tab-button i {
width: 1.25rem;
margin-right: 0.75rem;
}
.tab-content-container {
flex: 1;
padding: 0 10px;
}
.select {
position: absolute;
top: 7px;
left: 150px;
}
</style>