Files
klp-oa/klp-ui/src/views/lines/acid/components/real-time-monitoring.vue
砂糖 212c2b16eb feat: 新增锌线生产监控模块及相关API和组件
refactor(auth): 增加锌线系统token管理功能
feat(api): 添加锌线停机记录、生产报表和设备快照API
feat(views): 实现锌线实时监控、生产统计和停机统计页面
feat(components): 开发锌线生产报表、停机统计和班组绩效组件
feat(utils): 新增锌线专用请求工具zinc1Request
chore(vue.config): 配置锌线API代理
2026-01-19 13:29:44 +08:00

837 lines
36 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 class="page-container">
<!-- 刷新按钮固定在右下角保留 -->
<div class="refresh-btn-fixed" @click="refreshData">
<span class="refresh-icon" :class="{ 'rotating': isRefreshing }"></span>
</div>
<!-- 快速导航菜单固定在左下角保留全部导航项 -->
<div class="nav-menu-fixed" :class="{ 'nav-expanded': navMenuExpanded }">
<div class="nav-toggle" @click="navMenuExpanded = !navMenuExpanded">
<span class="nav-toggle-icon">{{ navMenuExpanded ? '✕' : '☰' }}</span>
</div>
<div class="nav-items" v-if="navMenuExpanded">
<div class="nav-item" @click="scrollToSection('speed-monitor')">
<span class="nav-label">速度监控</span>
</div>
<div class="nav-item" @click="scrollToSection('exit-speed-chart')">
<span class="nav-label">出口速度趋势</span>
</div>
<div class="nav-item" @click="scrollToSection('temp-chart')">
<span class="nav-label">酸槽温度趋势</span>
</div>
<div class="nav-item" @click="scrollToSection('looper-status')">
<span class="nav-label">活套运行状态</span>
</div>
<div class="nav-item" @click="scrollToSection('tank-concentration')">
<span class="nav-label">酸槽浓度监控</span>
</div>
<div class="nav-item" @click="scrollToSection('force-chart')">
<span class="nav-label">轧制力趋势</span>
</div>
<div class="nav-item" @click="scrollToSection('process-params')">
<span class="nav-label">工艺参数</span>
</div>
<div class="nav-item" @click="scrollToSection('roll-speed')">
<span class="nav-label">轧辊速度监控</span>
</div>
<div class="nav-item" @click="scrollToSection('reduc-rate')">
<span class="nav-label">机架压下率</span>
</div>
<div class="nav-item" @click="scrollToSection('tension-monitor')">
<span class="nav-label">带钢张力监控</span>
</div>
<div class="nav-item" @click="scrollToSection('power-ratio')">
<span class="nav-label">机架功率百分比</span>
</div>
<div class="nav-item" @click="scrollToSection('paint-speed')">
<span class="nav-label">涂装速度监控</span>
</div>
<div class="nav-item" @click="scrollToSection('tlv-params')">
<span class="nav-label">拉矫参数</span>
</div>
<div class="nav-item" @click="scrollToSection('paint-temp-chart')">
<span class="nav-label">烘干温度趋势</span>
</div>
</div>
</div>
<!-- 核心纯实时监控滚动容器 移除tab判断直接展示 -->
<div class="scroll-container">
<!-- 顶部状态栏 -->
<div class="status-bar">
<div class="status-item">
<span class="status-label">网络状态</span>
<span class="status-value" :class="'status-' + webStatus[0].value">{{ webStatus[0].value }}</span>
</div>
<div class="status-divider"></div>
<div class="status-item">
<span class="status-label">当前班组</span>
<span class="status-value">{{ webStatus[1].value }}</span>
</div>
<div class="status-divider"></div>
<div class="status-item">
<span class="status-label">更新时间</span>
<span class="status-value status-time">{{ lastUpdateTime }}</span>
</div>
</div>
<!-- 速度监控 -->
<div class="section" id="speed-monitor">
<div class="section-title">速度监控</div>
<div class="metrics-grid-3">
<div class="metric-box" v-for="(item, index) in speedMetrics" :key="index">
<span class="metric-name">{{ item.label }}</span>
<span class="metric-value">{{ item.value }}</span>
<span class="metric-unit">{{ item.unit }}</span>
</div>
</div>
</div>
<!-- 出口速度趋势 -->
<div class="section" id="exit-speed-chart">
<div class="section-title">出口速度趋势</div>
<div class="chart-box" ref="exitSpeedChart" id="exitSpeedChart"></div>
</div>
<!-- 酸槽温度趋势 -->
<div class="section" id="temp-chart">
<div class="section-title">酸槽温度趋势</div>
<div class="chart-box" ref="tempChart" id="tempChart"></div>
</div>
<!-- 活套运行状态 -->
<div class="section" id="looper-status">
<div class="section-title">活套运行状态</div>
<div class="metrics-grid-3">
<div class="metric-box" v-for="(item, index) in looperMetrics" :key="index">
<span class="metric-name">{{ item.label }}</span>
<span class="metric-value">{{ item.value }}</span>
<span class="metric-unit">{{ item.unit }}</span>
</div>
</div>
</div>
<!-- 酸槽浓度监控 -->
<div class="section" id="tank-concentration">
<div class="section-title">酸槽浓度监控</div>
<div class="tank-grid">
<div class="tank-card" v-for="(tank, index) in tankConcentration" :key="index">
<div class="tank-header">{{ tank.name }}</div>
<div class="tank-data">
<div class="tank-row">
<span class="data-label">酸浓度</span>
<span class="data-value">{{ tank.hclCont }} <span class="data-unit">g/L</span></span>
</div>
<div class="tank-divider"></div>
<div class="tank-row">
<span class="data-label">铁盐浓度</span>
<span class="data-value">{{ tank.feCont }} <span class="data-unit">g/L</span></span>
</div>
</div>
</div>
</div>
</div>
<!-- 轧制力趋势 -->
<div class="section" id="force-chart">
<div class="section-title">轧制力趋势</div>
<div class="chart-box" ref="forceChart" id="forceChart"></div>
</div>
<!-- 工艺参数 -->
<div class="section" id="process-params">
<div class="section-title">工艺参数</div>
<div class="metrics-grid-2">
<div class="metric-box" v-for="(item, index) in processMetrics" :key="index">
<span class="metric-name">{{ item.label }}</span>
<span class="metric-value">{{ item.value }}</span>
<span class="metric-unit">{{ item.unit }}</span>
</div>
</div>
</div>
<!-- ============ 镀锌线数据 ============ -->
<div class="section-divider">
<span class="divider-text">镀锌线监控数据</span>
</div>
<!-- 轧辊速度监控 -->
<div class="section" id="roll-speed">
<div class="section-title">轧辊速度监控</div>
<div class="metrics-grid-3">
<div class="metric-box" v-for="(item, index) in rollSpeedMetrics" :key="index">
<span class="metric-name">{{ item.label }}</span>
<span class="metric-value">{{ item.value }}</span>
<span class="metric-unit">{{ item.unit }}</span>
</div>
</div>
</div>
<!-- 机架压下率 -->
<div class="section" id="reduc-rate">
<div class="section-title">机架压下率</div>
<div class="metrics-grid-3">
<div class="metric-box" v-for="(item, index) in reducMetrics" :key="index">
<span class="metric-name">{{ item.label }}</span>
<span class="metric-value">{{ item.value }}</span>
<span class="metric-unit">{{ item.unit }}</span>
</div>
</div>
</div>
<!-- 带钢张力监控 -->
<div class="section" id="tension-monitor">
<div class="section-title">带钢张力监控</div>
<div class="metrics-grid-3">
<div class="metric-box" v-for="(item, index) in tensionMetrics" :key="index">
<span class="metric-name">{{ item.label }}</span>
<span class="metric-value">{{ item.value }}</span>
<span class="metric-unit">{{ item.unit }}</span>
</div>
</div>
</div>
<!-- 机架功率百分比 -->
<div class="section" id="power-ratio">
<div class="section-title">机架功率百分比</div>
<div class="metrics-grid-3">
<div class="metric-box" v-for="(item, index) in powerMetrics" :key="index">
<span class="metric-name">{{ item.label }}</span>
<span class="metric-value">{{ item.value }}</span>
<span class="metric-unit">{{ item.unit }}</span>
</div>
</div>
</div>
<!-- ============ 涂装线数据 ============ -->
<div class="section-divider">
<span class="divider-text">涂装线监控数据</span>
</div>
<!-- 涂装速度监控 -->
<div class="section" id="paint-speed">
<div class="section-title">涂装速度监控</div>
<div class="metrics-grid-3">
<div class="metric-box" v-for="(item, index) in paintSpeedMetrics" :key="index">
<span class="metric-name">{{ item.label }}</span>
<span class="metric-value">{{ item.value }}</span>
<span class="metric-unit">{{ item.unit }}</span>
</div>
</div>
</div>
<!-- 拉矫参数 -->
<div class="section" id="tlv-params">
<div class="section-title">拉矫参数</div>
<div class="metrics-grid-2">
<div class="metric-box" v-for="(item, index) in tlvMetrics" :key="index">
<span class="metric-name">{{ item.label }}</span>
<span class="metric-value">{{ item.value }}</span>
<span class="metric-unit">{{ item.unit }}</span>
</div>
</div>
</div>
<!-- 烘干温度趋势 -->
<div class="section" id="paint-temp-chart">
<div class="section-title">烘干温度趋势</div>
<div class="chart-box" ref="paintTempChart" id="paintTempChart"></div>
</div>
</div>
</div>
</template>
<script>
// ✅ 原接口导入 完全未改动
import { getAllPlantStateDefines, listPlantStateHistory, getCurrentShift } from '@/api/pocket/plantState'
// ✅ 引入Echarts5.x 兼容Vue2
import * as echarts from 'echarts'
// ✅ 引入axios替换uni.request (PC端网络请求)
import axios from 'axios'
export default {
name: 'RealTimeMonitor',
data() {
return {
webStatus: [
{ label: '网络状态', value: '检测中...' },
{ label: '当前班组', value: '—' }
],
lastUpdateTime: '—', // 最后更新时间
isRefreshing: false, // 是否正在刷新
refreshTimer: null, // 定时器
navMenuExpanded: false, // 导航菜单是否展开
// ✅ 移除了 tabData/currentTab 无用属性
// 速度监控指标ID=1,2,3
speedMetrics: [
{ label: '出口带钢速度', value: '—', unit: 'm/min' },
{ label: '酸洗带钢速度', value: '—', unit: 'm/min' },
{ label: '圆盘剪速度', value: '—', unit: 'm/min' }
],
// 活套状态ID=8,9,10
looperMetrics: [
{ label: '入口活套', value: '—', unit: '%' },
{ label: '出口活套', value: '—', unit: '%' },
{ label: '联机活套', value: '—', unit: '%' }
],
// 酸槽浓度ID=11-16
tankConcentration: [
{ name: '1#酸槽', hclCont: '—', feCont: '—' },
{ name: '2#酸槽', hclCont: '—', feCont: '—' },
{ name: '3#酸槽', hclCont: '—', feCont: '—' }
],
// 其他工艺参数ID=7,17,18,19,20
processMetrics: [
{ label: '漂洗温度', value: '—', unit: '°C' },
{ label: '烘干温度', value: '—', unit: '°C' },
{ label: '漂洗电导率', value: '—', unit: 'g/L' },
{ label: '联机活套张力', value: '—', unit: 'kN' },
{ label: '拉矫机延伸率', value: '—', unit: '%' }
],
// 镀锌线数据
rollSpeedMetrics: [
{ label: '1#机架', value: '—', unit: 'm/min' },
{ label: '2#机架', value: '—', unit: 'm/min' },
{ label: '3#机架', value: '—', unit: 'm/min' },
{ label: '4#机架', value: '—', unit: 'm/min' },
{ label: '5#机架', value: '—', unit: 'm/min' },
{ label: '6#机架', value: '—', unit: 'm/min' }
],
reducMetrics: [
{ label: '1#机架', value: '—', unit: '%' },
{ label: '2#机架', value: '—', unit: '%' },
{ label: '3#机架', value: '—', unit: '%' },
{ label: '4#机架', value: '—', unit: '%' },
{ label: '5#机架', value: '—', unit: '%' },
{ label: '6#机架', value: '—', unit: '%' }
],
tensionMetrics: [
{ label: '0#张力', value: '—', unit: 'kN' },
{ label: '1#张力', value: '—', unit: 'kN' },
{ label: '2#张力', value: '—', unit: 'kN' },
{ label: '3#张力', value: '—', unit: 'kN' },
{ label: '4#张力', value: '—', unit: 'kN' },
{ label: '5#张力', value: '—', unit: 'kN' },
{ label: '6#张力', value: '—', unit: 'kN' }
],
powerMetrics: [
{ label: '1#机架', value: '—', unit: '%' },
{ label: '2#机架', value: '—', unit: '%' },
{ label: '3#机架', value: '—', unit: '%' },
{ label: '4#机架', value: '—', unit: '%' },
{ label: '5#机架', value: '—', unit: '%' },
{ label: '6#机架', value: '—', unit: '%' }
],
// 涂装线数据
paintSpeedMetrics: [
{ label: '出口带钢速度', value: '—', unit: 'm/min' },
{ label: '涂装带钢速度', value: '—', unit: 'm/min' },
{ label: '圆盘剪速度', value: '—', unit: 'm/min' }
],
tlvMetrics: [
{ label: '拉矫延伸率', value: '—', unit: '%' },
{ label: '破磷机插入量1', value: '—', unit: 'mm' },
{ label: '破磷机插入量2', value: '—', unit: 'mm' },
{ label: '破磷机插入量3', value: '—', unit: 'mm' }
],
// Echarts图表实例
exitSpeedChart: null,
tempChart: null,
forceChart: null,
paintTempChart: null,
// 图表配色与原qiun一致
lineColor: ["#0066cc", "#409eff", "#66b1ff"],
forceColor: ["#0066cc", "#409eff", "#66b1ff", "#a0cfff", "#d9ecff", "#ecf5ff"],
paintColor: ["#0066cc", "#409eff", "#66b1ff", "#a0cfff", "#d9ecff"],
plantStateDefines: [] // 缓存所有的状态定义
};
},
mounted() {
this.loadAllData() // 加载所有数据
this.startAutoRefresh() // 启动自动刷新
this.initEcharts() // 初始化图表
window.addEventListener('resize', this.resizeEcharts) // 窗口自适应
},
beforeDestroy() {
this.stopAutoRefresh() // 页面销毁时清除定时器
window.removeEventListener('resize', this.resizeEcharts)
this.disposeEcharts() // 销毁图表实例 防内存泄漏
},
methods: {
// ✅ 初始化Echarts图表
initEcharts() {
this.exitSpeedChart = echarts.init(document.getElementById('exitSpeedChart'))
this.tempChart = echarts.init(document.getElementById('tempChart'))
this.forceChart = echarts.init(document.getElementById('forceChart'))
this.paintTempChart = echarts.init(document.getElementById('paintTempChart'))
},
// ✅ 图表自适应
resizeEcharts() {
this.exitSpeedChart && this.exitSpeedChart.resize()
this.tempChart && this.tempChart.resize()
this.forceChart && this.forceChart.resize()
this.paintTempChart && this.paintTempChart.resize()
},
// ✅ 销毁图表实例
disposeEcharts() {
this.exitSpeedChart && this.exitSpeedChart.dispose()
this.tempChart && this.tempChart.dispose()
this.forceChart && this.forceChart.dispose()
this.paintTempChart && this.paintTempChart.dispose()
},
// ✅ 渲染折线图通用方法
renderLineChart(chart, xData, seriesData, color, yTitle) {
if(!chart) return
const option = {
color: color,
grid: { left: 30, right: 30, top: 40, bottom: 30 },
tooltip: { trigger: 'axis' },
legend: { top: 0, left: 'center', textStyle: { fontSize: 12 } },
xAxis: { type: 'category', data: xData, axisLine: { show: false } },
yAxis: {
type: 'value',
name: yTitle,
splitLine: { type: 'dashed', color: '#e4e7ed' }
},
series: seriesData.map(item => ({
name: item.name,
type: 'line',
data: item.data,
smooth: true,
lineStyle: { width: 2 },
symbol: 'none'
}))
}
chart.setOption(option, true)
},
// 启动自动刷新每30秒 逻辑完全未改
startAutoRefresh() {
this.refreshTimer = setInterval(() => {
console.log('自动刷新数据...')
this.refreshData(true) // 静默刷新
}, 30000) // 30秒刷新一次
},
// 停止自动刷新 逻辑完全未改
stopAutoRefresh() {
if (this.refreshTimer) {
clearInterval(this.refreshTimer)
this.refreshTimer = null
}
},
// 加载所有数据(初始化) 逻辑完全未改
loadAllData() {
this.checkNetworkStatus() // 检测网络状态
this.loadCurrentShift() // 加载当前班组
this.initPlantStateDefines() // 加载所有定义
this.updateLastTime() // 更新时间
},
// 刷新数据(手动或自动) ✅ 替换uni弹窗为Element弹窗
refreshData(isSilent = false) {
if (this.isRefreshing) return // 防止重复刷新
this.isRefreshing = true
let loading = null
if (!isSilent) {
loading = this.$loading({ lock: true, text: '刷新中', spinner: 'el-icon-loading' })
}
Promise.all([
this.checkNetworkStatus(),
this.loadCurrentShift(),
this.initPlantStateDefines(isSilent)
]).finally(() => {
this.isRefreshing = false
loading && loading.close()
if (!isSilent) {
this.$message.success('刷新成功')
}
this.updateLastTime()
})
},
// 更新最后刷新时间 逻辑完全未改
updateLastTime() {
const now = new Date()
const hour = String(now.getHours()).padStart(2, '0')
const minute = String(now.getMinutes()).padStart(2, '0')
const second = String(now.getSeconds()).padStart(2, '0')
this.lastUpdateTime = `${hour}:${minute}:${second}`
},
// 检测网络状态 ✅ 替换uni.request为axios
checkNetworkStatus() {
return new Promise((resolve) => {
const startTime = Date.now()
axios.get(`http://140.143.206.120:8080/pocket/proPlantStateDefine/allWithValues`, { timeout: 5000 })
.then(() => {
const responseTime = Date.now() - startTime
if (responseTime < 500) this.webStatus[0].value = '通畅'
else if (responseTime < 2000) this.webStatus[0].value = '卡顿'
else this.webStatus[0].value = '异常'
resolve()
}).catch(() => {
this.webStatus[0].value = '异常'
resolve()
})
})
},
// 加载当前班组信息 逻辑完全未改
loadCurrentShift() {
return getCurrentShift().then(response => {
if (response.code === 200 && response.data) {
const shiftData = response.data
const shiftName = this.getShiftName(shiftData.shift)
const crewName = this.getCrewName(shiftData.crew)
this.webStatus[1].value = `${crewName} / ${shiftName}`
}
}).catch(error => {
console.error('加载班组信息失败:', error)
})
},
// 获取班次名称 逻辑完全未改
getShiftName(shift) {
const shiftMap = { 'A': '早班', 'B': '中班', 'C': '晚班' }
return shiftMap[shift] || shift || '—'
},
// 获取班组名称 逻辑完全未改
getCrewName(crew) {
const crewMap = { 1: '甲', 2: '乙', 3: '丙', 4: '丁' }
return crewMap[crew] || crew || '—'
},
// 初始化:加载所有状态定义及其当前值 ✅ 替换uni弹窗为Element弹窗
initPlantStateDefines(isSilent = false) {
let loading = null
if (!isSilent) {
loading = this.$loading({ lock: true, text: '加载中', spinner: 'el-icon-loading' })
}
return getAllPlantStateDefines().then(response => {
if (response.code === 200 && response.data) {
this.plantStateDefines = response.data
this.updateCurrentMetrics()
return this.loadTempTrend(isSilent)
}
}).finally(() => {
loading && loading.close()
}).catch(error => {
loading && loading.close()
console.error('加载状态定义失败:', error)
})
},
// 更新所有实时指标 逻辑完全未改
updateCurrentMetrics() {
// 速度监控
const exitSpeed = this.getDefineById(1)
const plSpeed = this.getDefineById(2)
const trimSpeed = this.getDefineById(3)
this.speedMetrics = [
{ label: exitSpeed?.comments || '出口带钢速度', value: this.formatValue(exitSpeed?.currentValue), unit: exitSpeed?.units || 'm/min' },
{ label: plSpeed?.comments || '酸洗带钢速度', value: this.formatValue(plSpeed?.currentValue), unit: plSpeed?.units || 'm/min' },
{ label: trimSpeed?.comments || '圆盘剪速度', value: this.formatValue(trimSpeed?.currentValue), unit: trimSpeed?.units || 'm/min' }
]
// 活套状态
const celLooper = this.getDefineById(8)
const cxlLooper = this.getDefineById(9)
const telLooper = this.getDefineById(10)
this.looperMetrics = [
{ label: celLooper?.comments || '入口重套', value: this.formatValue(celLooper?.currentValue), unit: celLooper?.units || '%' },
{ label: cxlLooper?.comments || '出口活套', value: this.formatValue(cxlLooper?.currentValue), unit: cxlLooper?.units || '%' },
{ label: telLooper?.comments || '联机活套', value: this.formatValue(telLooper?.currentValue), unit: telLooper?.units || '%' }
]
// 酸槽浓度
this.tankConcentration = [
{ name: '1#酸槽', hclCont: this.formatValue(this.getDefineById(11)?.currentValue), feCont: this.formatValue(this.getDefineById(12)?.currentValue) },
{ name: '2#酸槽', hclCont: this.formatValue(this.getDefineById(13)?.currentValue), feCont: this.formatValue(this.getDefineById(14)?.currentValue) },
{ name: '3#酸槽', hclCont: this.formatValue(this.getDefineById(15)?.currentValue), feCont: this.formatValue(this.getDefineById(16)?.currentValue) }
]
// 工艺参数
const rinseTemp = this.getDefineById(7)
const windTemp = this.getDefineById(17)
const rinseFlow = this.getDefineById(18)
const telTension = this.getDefineById(19)
const tlvElong = this.getDefineById(20)
this.processMetrics = [
{ label: rinseTemp?.comments || '漂洗温度', value: this.formatValue(rinseTemp?.currentValue), unit: rinseTemp?.units || '°C' },
{ label: windTemp?.comments || '烘干温度', value: this.formatValue(windTemp?.currentValue), unit: windTemp?.units || '°C' },
{ label: rinseFlow?.comments || '漂洗电导率', value: this.formatValue(rinseFlow?.currentValue), unit: rinseFlow?.units || 'g/L' },
{ label: telTension?.comments || '联机活套张力', value: this.formatValue(telTension?.currentValue), unit: telTension?.units || 'kN' },
{ label: tlvElong?.comments || '拉矫机延伸率', value: this.formatValue(tlvElong?.currentValue), unit: tlvElong?.units || '%' }
]
// 镀锌线-轧辊速度
this.rollSpeedMetrics = [
{ label: '1#机架', value: this.formatValue(this.getDefineById(36)?.currentValue), unit: 'm/min' },
{ label: '2#机架', value: this.formatValue(this.getDefineById(37)?.currentValue), unit: 'm/min' },
{ label: '3#机架', value: this.formatValue(this.getDefineById(38)?.currentValue), unit: 'm/min' },
{ label: '4#机架', value: this.formatValue(this.getDefineById(39)?.currentValue), unit: 'm/min' },
{ label: '5#机架', value: this.formatValue(this.getDefineById(40)?.currentValue), unit: 'm/min' },
{ label: '6#机架', value: this.formatValue(this.getDefineById(41)?.currentValue), unit: 'm/min' }
]
// 镀锌线-压下率
this.reducMetrics = [
{ label: '1#机架', value: this.formatValue(this.getDefineById(24)?.currentValue), unit: '%' },
{ label: '2#机架', value: this.formatValue(this.getDefineById(25)?.currentValue), unit: '%' },
{ label: '3#机架', value: this.formatValue(this.getDefineById(26)?.currentValue), unit: '%' },
{ label: '4#机架', value: this.formatValue(this.getDefineById(27)?.currentValue), unit: '%' },
{ label: '5#机架', value: this.formatValue(this.getDefineById(28)?.currentValue), unit: '%' },
{ label: '6#机架', value: this.formatValue(this.getDefineById(29)?.currentValue), unit: '%' }
]
// 镀锌线-张力
this.tensionMetrics = [
{ label: '0#张力', value: this.formatValue(this.getDefineById(42)?.currentValue), unit: 'kN' },
{ label: '1#张力', value: this.formatValue(this.getDefineById(43)?.currentValue), unit: 'kN' },
{ label: '2#张力', value: this.formatValue(this.getDefineById(44)?.currentValue), unit: 'kN' },
{ label: '3#张力', value: this.formatValue(this.getDefineById(45)?.currentValue), unit: 'kN' },
{ label: '4#张力', value: this.formatValue(this.getDefineById(46)?.currentValue), unit: 'kN' },
{ label: '5#张力', value: this.formatValue(this.getDefineById(47)?.currentValue), unit: 'kN' },
{ label: '6#张力', value: this.formatValue(this.getDefineById(48)?.currentValue), unit: 'kN' }
]
// 镀锌线-功率
this.powerMetrics = [
{ label: '1#机架', value: this.formatValue(this.getDefineById(49)?.currentValue), unit: '%' },
{ label: '2#机架', value: this.formatValue(this.getDefineById(50)?.currentValue), unit: '%' },
{ label: '3#机架', value: this.formatValue(this.getDefineById(51)?.currentValue), unit: '%' },
{ label: '4#机架', value: this.formatValue(this.getDefineById(52)?.currentValue), unit: '%' },
{ label: '5#机架', value: this.formatValue(this.getDefineById(53)?.currentValue), unit: '%' },
{ label: '6#机架', value: this.formatValue(this.getDefineById(54)?.currentValue), unit: '%' }
]
// 涂装线-速度
this.paintSpeedMetrics = [
{ label: '出口带钢速度', value: this.formatValue(this.getDefineById(1)?.currentValue), unit: 'm/min' },
{ label: '涂装带钢速度', value: this.formatValue(this.getDefineById(2)?.currentValue), unit: 'm/min' },
{ label: '圆盘剪速度', value: this.formatValue(this.getDefineById(3)?.currentValue), unit: 'm/min' }
]
// 涂装线-拉矫参数
this.tlvMetrics = [
{ label: '拉矫延伸率', value: this.formatValue(this.getDefineById(20)?.currentValue), unit: '%' },
{ label: '破磷机插入量1', value: this.formatValue(this.getDefineById(21)?.currentValue), unit: 'mm' },
{ label: '破磷机插入量2', value: this.formatValue(this.getDefineById(22)?.currentValue), unit: 'mm' },
{ label: '破磷机插入量3', value: this.formatValue(this.getDefineById(23)?.currentValue), unit: 'mm' }
]
},
// 加载历史趋势图数据 ✅ 渲染Echarts图表
loadTempTrend(isSilent = false) {
return listPlantStateHistory({ pageNum: 1, pageSize: 30 }).then(response => {
if (response.code === 200 && response.rows && response.rows.length > 0) {
const categories = []
const tank1Data = [];const tank2Data = [];const tank3Data = [];
const exitSpeedData = [];
const force1Data = [];const force2Data = [];const force3Data = [];const force4Data = [];const force5Data = [];const force6Data = [];
const paintTemp1 = [];const paintTemp2 = [];const paintTemp3 = [];const paintTemp4 = [];const paintTemp5 = [];
response.rows.forEach(item => {
const dateStr = this.formatDate(item.insdate)
categories.push(dateStr)
tank1Data.push(Number(item.value4) || 0)
tank2Data.push(Number(item.value5) || 0)
tank3Data.push(Number(item.value6) || 0)
exitSpeedData.push(Number(item.value1) || 0)
force1Data.push(Number(item.value30) || 0)
force2Data.push(Number(item.value31) || 0)
force3Data.push(Number(item.value32) || 0)
force4Data.push(Number(item.value33) || 0)
force5Data.push(Number(item.value34) || 0)
force6Data.push(Number(item.value35) || 0)
paintTemp1.push(Number(item.value4) || 0)
paintTemp2.push(Number(item.value5) || 0)
paintTemp3.push(Number(item.value6) || 0)
paintTemp4.push(Number(item.value7) || 0)
paintTemp5.push(Number(item.value17) || 0)
})
const reverseCate = categories.reverse()
// 渲染出口速度趋势
this.renderLineChart(this.exitSpeedChart, reverseCate, [{name:'出口带钢速度',data:exitSpeedData.reverse()}], this.lineColor, 'm/min')
// 渲染酸槽温度趋势
this.renderLineChart(this.tempChart, reverseCate, [
{name:'1#酸槽温度',data:tank1Data.reverse()},
{name:'2#酸槽温度',data:tank2Data.reverse()},
{name:'3#酸槽温度',data:tank3Data.reverse()}
], this.lineColor, '°C')
// 渲染轧制力趋势
this.renderLineChart(this.forceChart, reverseCate, [
{name:'1#轧制力',data:force1Data.reverse()},
{name:'2#轧制力',data:force2Data.reverse()},
{name:'3#轧制力',data:force3Data.reverse()},
{name:'4#轧制力',data:force4Data.reverse()},
{name:'5#轧制力',data:force5Data.reverse()},
{name:'6#轧制力',data:force6Data.reverse()}
], this.forceColor, 'kN')
// 渲染烘干温度趋势
this.renderLineChart(this.paintTempChart, reverseCate, [
{name:'1#酸槽温度',data:paintTemp1.reverse()},
{name:'2#酸槽温度',data:paintTemp2.reverse()},
{name:'3#酸槽温度',data:paintTemp3.reverse()},
{name:'漂洗温度',data:paintTemp4.reverse()},
{name:'烘干温度',data:paintTemp5.reverse()}
], this.paintColor, '°C')
}
}).catch(error => {
console.error('加载历史趋势失败:', error)
})
},
// 根据ID获取Define对象 逻辑完全未改
getDefineById(id) {
return this.plantStateDefines.find(item => item.id == id)
},
// 格式化日期 逻辑完全未改
formatDate(dateStr) {
if (!dateStr) return ''
const date = new Date(dateStr)
const hour = String(date.getHours()).padStart(2, '0')
const minute = String(date.getMinutes()).padStart(2, '0')
return `${hour}:${minute}`
},
// 格式化数值 逻辑完全未改
formatValue(value) {
if (value === null || value === undefined || value === '') return '—'
const num = Number(value)
if (isNaN(num)) return '—'
return num.toFixed(2)
},
// 滚动到指定部分 ✅ PC端原生滚动适配
scrollToSection(sectionId) {
document.getElementById(sectionId).scrollIntoView({ behavior: 'smooth' })
this.navMenuExpanded = false // 点击后关闭菜单
}
}
};
</script>
<style scoped lang="scss">
// ✅ 所有rpx转px(1rpx=0.5px) PC端完美适配 | 移除tab相关样式
.page-container {
min-height: 100vh;
background: #f5f7fa;
position: relative;
}
/* 刷新按钮(固定在右下角) */
.refresh-btn-fixed {
position: fixed;
right: 16px;
bottom: 60px;
width: 48px;
height: 48px;
background: #0066cc;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 4px 10px rgba(0, 102, 204, 0.4);
z-index: 999;
cursor: pointer;
&:active { opacity: 0.8; transform: scale(0.95); }
}
.refresh-icon {
font-size: 24px;
color: #fff;
display: block;
line-height: 1;
&.rotating { animation: rotate 1s linear infinite; }
}
@keyframes rotate { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }
/* 快速导航菜单(固定在左下角) */
.nav-menu-fixed {
position: fixed;
left: 16px;
bottom: 60px;
z-index: 998;
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 6px;
}
.nav-toggle {
width: 48px;height: 48px;
background: #409eff;
border-radius: 50%;
display: flex;align-items: center;justify-content: center;
box-shadow: 0 4px 10px rgba(64, 158, 255, 0.4);
cursor: pointer;
transition: all 0.3s ease;
&:active { opacity: 0.8; transform: scale(0.95); }
}
.nav-toggle-icon { font-size: 24px; color: #fff; display: block; line-height: 1; }
.nav-items {
display: flex;flex-direction: column;gap:4px;
animation: slideUp 0.3s ease;
}
@keyframes slideUp { from { opacity:0; transform: translateY(10px); } to { opacity:1; transform: translateY(0); } }
.nav-item {
background: #fff;border:1px solid #409eff;border-radius:4px;
padding:8px 12px;cursor: pointer;
box-shadow: 0 2px 6px rgba(64,158,255,0.2);
transition: all 0.2s ease;
&:active { background: #f0f9ff; transform: scale(0.95); }
}
.nav-label { font-size:13px; color:#409eff; font-weight:500; white-space: nowrap; }
/* 滚动容器 - PC端原生滚动 核心修改 */
.scroll-container {
height: calc(100vh - 20px);
padding: 12px;
overflow-y: auto;
scroll-behavior: smooth;
}
/* 顶部状态栏 */
.status-bar {
display: flex;align-items: center;
background: #fff;padding:12px 16px;margin-bottom:12px;
border-radius:4px;border:1px solid #e4e7ed;
}
.status-item { flex:1; display:flex;align-items:center;justify-content:center;gap:8px; }
.status-label { font-size:13px;color:#909399; }
.status-value {
font-size:14px;font-weight:500;color:#303133;
&.status-通畅 { color: #67c23a; }
&.status-卡顿 { color: #e6a23c; }
&.status-异常 { color: #f56c6c; }
&.status-time { color:#909399;font-size:12px; }
}
.status-divider { width:1px;height:20px;background:#e4e7ed; }
/* 区块样式 */
.section { margin-bottom:12px; }
.section-title {
font-size:15px;font-weight:500;color:#303133;
margin-bottom:10px;padding-left:8px;border-left:2px solid #0066cc;
}
/* 分隔符 */
.section-divider {
display:flex;align-items:center;margin:20px 0 12px 0;padding:0 8px;
}
.divider-text {
font-size:14px;font-weight:600;color:#0066cc;
padding:0 6px;background:#f5f7fa;
border-left:2px solid #0066cc;padding-left:8px;
}
/* 指标卡片 - 3列布局 */
.metrics-grid-3 { display: grid; grid-template-columns: repeat(3, 1fr); gap:10px; }
/* 指标卡片 - 2列布局 */
.metrics-grid-2 { display: grid; grid-template-columns: repeat(2, 1fr); gap:10px; }
.metric-box {
background:#fff;border:1px solid #e4e7ed;border-radius:4px;
padding:14px 10px;text-align:center;
}
.metric-name { display:block;font-size:12px;color:#909399;margin-bottom:8px; }
.metric-value { display:block;font-size:24px;font-weight:600;color:#0066cc;margin-bottom:4px;line-height:1; }
.metric-unit { display:block;font-size:11px;color:#909399; }
/* 图表容器 */
.chart-box {
background:#fff;border:1px solid #e4e7ed;border-radius:4px;
padding:12px 8px;height:225px;width:100%;
}
/* 酸槽监控网格 */
.tank-grid { display:grid;grid-template-columns:repeat(3,1fr);gap:10px; }
.tank-card { background:#fff;border:2px solid #0066cc;border-radius:4px;overflow:hidden; }
.tank-header { background:#0066cc;color:#fff;font-size:14px;font-weight:500;padding:10px;text-align:center; }
.tank-data { padding:12px 8px; }
.tank-row { display:flex;flex-direction:column;align-items:center;padding:6px 0; }
.data-label { font-size:11px;color:#909399;margin-bottom:4px; }
.data-value { font-size:18px;font-weight:600;color:#303133; }
.data-unit { font-size:10px;font-weight:400;color:#909399;margin-left:2px; }
.tank-divider { height:1px;background:#e4e7ed;margin:6px 0; }
</style>