Files
admin-govern/src/views/govern/device/control/moduleData.vue

677 lines
23 KiB
Vue
Raw Normal View History

<template>
<!-- 运行 运行(中断) 运行(故障) 离线 -->
2025-07-08 10:47:04 +08:00
<div :style="height" style="overflow-y: auto" v-loading="loading">
<div class="container">
<div class="tabs-container">
<!-- 左侧标签头 -->
2025-07-15 16:31:06 +08:00
<div class="tab-list" :style="titleHeight" style="overflow-y: auto" v-loading="loadingLeft">
2025-07-08 10:47:04 +08:00
<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">
2025-07-08 10:47:04 +08:00
{{ item.moduleState }}
</el-tag>
</button>
</div>
<!-- 右侧内容区域 -->
<div class="tab-content-container flex-1" :key="activeTab">
2025-07-18 16:27:39 +08:00
<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>
2025-07-08 10:47:04 +08:00
</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>
2025-07-08 10:47:04 +08:00
</el-collapse> -->
</div>
</template>
<script setup lang="ts">
2025-07-18 16:27:39 +08:00
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)
2025-07-15 16:31:06 +08:00
const loadingLeft = ref(false)
const height = ref(mainHeight(290))
const titleHeight = ref(mainHeight(302))
const echartHeight = ref(mainHeight(342))
2025-07-08 10:47:04 +08:00
const activeTab = ref(0)
const activeName = ref('0')
2025-07-08 10:47:04 +08:00
const echartList: any = ref({})
2025-07-18 16:27:39 +08:00
const dataType = ref([0])
const key = ref(0)
import * as echarts from 'echarts' // 全引入
2025-07-18 16:27:39 +08:00
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) => {
2025-07-18 16:27:39 +08:00
// activeTab.value = 0
2025-07-08 10:47:04 +08:00
// echartHeight.value = mainHeight(292 + data.length * 49, data.length)
data.forEach((item: any) => {
// console.log('🚀 ~ setData ~ data:', data)
if (item.dataList == null) return
2025-07-18 16:27:39 +08:00
item.dataList.map((k: any, i: any) => {
k.endtime = item.dataList[i + 1]?.time
})
item.options = {
2025-07-18 16:27:39 +08:00
// 鼠标提示
tooltip: {
2025-07-18 16:27:39 +08:00
axisPointer: {
type: 'cross',
label: {
color: '#fff',
fontSize: 16
}
2025-07-18 16:27:39 +08:00
},
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/>'
// }
}
}
2025-07-18 16:27:39 +08:00
return tip
}
},
legend: {
2025-07-18 16:27:39 +08:00
// data: ['运行', '停止', '离线','事件标记']
data: [
2025-07-18 16:27:39 +08:00
{ name: '运行' },
{ name: '停止' },
{ name: '离线' },
{
2025-07-18 16:27:39 +08:00
name: '事件标记',
icon: 'path://M0,10 L10,10 L5,0 Z', // 自定义三角形路径
textStyle: {
color: '#333',
2025-07-18 16:27:39 +08:00
fontSize: 14
}
}
]
},
xAxis: {
type: 'time',
name: '时间',
axisLabel: {
formatter: {
day: '{MM}-{dd}',
month: '{MM}',
year: '{yyyy}'
}
}
},
yAxis: {
2025-07-18 16:27:39 +08:00
show: false,
axisPointer: {
show: false // 隐藏Y轴上的指示器
}
},
2025-07-18 16:27:39 +08:00
color: ['#67c23a', '#e6a23c', '#f56c6c', '#ccc'],
series: [
{
name: '运行',
type: 'custom',
renderItem: renderItem,
2025-07-18 16:27:39 +08:00
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,
2025-07-18 16:27:39 +08:00
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,
2025-07-18 16:27:39 +08:00
encode: {
x: [1, 2],
y: 0
},
2025-07-18 16:27:39 +08:00
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
},
2025-07-18 16:27:39 +08:00
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 // 确保标记显示在甘特图上方
}
]
}
})
2025-07-08 10:47:04 +08:00
list.value = data
2025-07-08 10:47:04 +08:00
echartList.value = list.value[activeTab.value]
2025-07-18 16:27:39 +08:00
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, ...data2])
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) => {
2025-07-15 16:31:06 +08:00
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('刷新成功')
})
2025-07-15 16:31:06 +08:00
loadingLeft.value = false
}
2025-07-08 10:47:04 +08:00
const changeTab = (e: any) => {
echartList.value = list.value[e]
activeTab.value = e
2025-07-18 16:27:39 +08:00
changeDataType()
2025-07-08 10:47:04 +08:00
}
const getType = (type: string) => {
switch (type) {
case '运行':
return 'success'
case '离线':
return 'danger'
default:
return 'warning'
}
}
defineExpose({ setData, getModule })
onBeforeUnmount(() => {
echarts.disconnect('group')
})
</script>
2025-07-08 10:47:04 +08:00
<style lang="scss" scoped>
2025-07-08 10:47:04 +08:00
.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;
2025-07-08 10:47:04 +08:00
flex-direction: column;
height: 100%;
}
@media (min-width: 768px) {
.tabs-container {
flex-direction: row;
}
}
2025-07-08 10:47:04 +08:00
.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;
}
2025-07-18 16:27:39 +08:00
.select {
position: absolute;
top: 7px;
left: 150px;
}
</style>