2025-10-27 13:21:43 +08:00
|
|
|
|
<template>
|
2025-10-31 17:18:30 +08:00
|
|
|
|
<view class="page-container">
|
2025-10-29 15:38:20 +08:00
|
|
|
|
<!-- 简洁标签栏 -->
|
|
|
|
|
|
<view class="tab-container">
|
2025-10-31 17:18:30 +08:00
|
|
|
|
<view
|
|
|
|
|
|
v-for="item in tabData"
|
2025-10-29 15:38:20 +08:00
|
|
|
|
:key="item.value"
|
|
|
|
|
|
@click="currentTab = item.value"
|
|
|
|
|
|
class="tab-item"
|
|
|
|
|
|
:class="{ 'tab-active': currentTab === item.value }"
|
|
|
|
|
|
>
|
|
|
|
|
|
<text class="tab-label">{{ item.text }}</text>
|
|
|
|
|
|
<view class="tab-indicator" v-if="currentTab === item.value"></view>
|
|
|
|
|
|
</view>
|
2025-10-27 13:21:43 +08:00
|
|
|
|
</view>
|
2025-10-31 17:18:30 +08:00
|
|
|
|
|
|
|
|
|
|
<!-- 刷新按钮(固定在右下角,所有tab都显示) -->
|
|
|
|
|
|
<view class="refresh-btn-fixed" @click="refreshData">
|
|
|
|
|
|
<text class="refresh-icon" :class="{ 'rotating': isRefreshing }">⟳</text>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
|
|
|
|
|
|
<scroll-view scroll-y class="scroll-container" v-if="currentTab === 1">
|
|
|
|
|
|
<!-- 顶部状态栏 -->
|
|
|
|
|
|
<view class="status-bar">
|
|
|
|
|
|
<view class="status-item">
|
|
|
|
|
|
<text class="status-label">网络状态</text>
|
|
|
|
|
|
<text class="status-value" :class="'status-' + webStatus[0].value">{{ webStatus[0].value }}</text>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
<view class="status-divider"></view>
|
|
|
|
|
|
<view class="status-item">
|
|
|
|
|
|
<text class="status-label">当前班组</text>
|
|
|
|
|
|
<text class="status-value">{{ webStatus[1].value }}</text>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
<view class="status-divider"></view>
|
|
|
|
|
|
<view class="status-item">
|
|
|
|
|
|
<text class="status-label">更新时间</text>
|
|
|
|
|
|
<text class="status-value status-time">{{ lastUpdateTime }}</text>
|
2025-10-27 13:21:43 +08:00
|
|
|
|
</view>
|
2025-10-31 17:18:30 +08:00
|
|
|
|
</view>
|
2025-10-27 13:21:43 +08:00
|
|
|
|
|
2025-10-31 17:18:30 +08:00
|
|
|
|
<!-- 速度监控 -->
|
|
|
|
|
|
<view class="section">
|
|
|
|
|
|
<view class="section-title">速度监控</view>
|
|
|
|
|
|
<view class="metrics-grid-3">
|
|
|
|
|
|
<view class="metric-box" v-for="(item, index) in speedMetrics" :key="index">
|
|
|
|
|
|
<text class="metric-name">{{ item.label }}</text>
|
|
|
|
|
|
<text class="metric-value">{{ item.value }}</text>
|
|
|
|
|
|
<text class="metric-unit">{{ item.unit }}</text>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 酸槽温度趋势 -->
|
|
|
|
|
|
<view class="section">
|
|
|
|
|
|
<view class="section-title">酸槽温度趋势</view>
|
|
|
|
|
|
<view class="chart-box">
|
|
|
|
|
|
<qiun-data-charts type="line" :chartData="tempChartData" :opts="lineChartOpts" />
|
2025-10-27 13:21:43 +08:00
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
|
2025-10-31 17:18:30 +08:00
|
|
|
|
<!-- 活套运行状态 -->
|
|
|
|
|
|
<view class="section">
|
|
|
|
|
|
<view class="section-title">活套运行状态</view>
|
|
|
|
|
|
<view class="metrics-grid-3">
|
|
|
|
|
|
<view class="metric-box" v-for="(item, index) in looperMetrics" :key="index">
|
|
|
|
|
|
<text class="metric-name">{{ item.label }}</text>
|
|
|
|
|
|
<text class="metric-value">{{ item.value }}</text>
|
|
|
|
|
|
<text class="metric-unit">{{ item.unit }}</text>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
2025-10-27 13:21:43 +08:00
|
|
|
|
</view>
|
|
|
|
|
|
|
2025-10-31 17:18:30 +08:00
|
|
|
|
<!-- 酸槽浓度监控 -->
|
|
|
|
|
|
<view class="section">
|
|
|
|
|
|
<view class="section-title">酸槽浓度监控</view>
|
|
|
|
|
|
<view class="tank-grid">
|
|
|
|
|
|
<view class="tank-card" v-for="(tank, index) in tankConcentration" :key="index">
|
|
|
|
|
|
<view class="tank-header">{{ tank.name }}</view>
|
|
|
|
|
|
<view class="tank-data">
|
|
|
|
|
|
<view class="tank-row">
|
|
|
|
|
|
<text class="data-label">酸浓度</text>
|
|
|
|
|
|
<text class="data-value">{{ tank.hclCont }} <text class="data-unit">g/L</text></text>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
<view class="tank-divider"></view>
|
|
|
|
|
|
<view class="tank-row">
|
|
|
|
|
|
<text class="data-label">铁盐浓度</text>
|
|
|
|
|
|
<text class="data-value">{{ tank.feCont }} <text class="data-unit">g/L</text></text>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
2025-10-27 13:21:43 +08:00
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
|
2025-10-31 17:18:30 +08:00
|
|
|
|
<!-- 工艺参数 -->
|
|
|
|
|
|
<view class="section">
|
|
|
|
|
|
<view class="section-title">工艺参数</view>
|
|
|
|
|
|
<view class="metrics-grid-2">
|
|
|
|
|
|
<view class="metric-box" v-for="(item, index) in processMetrics" :key="index">
|
|
|
|
|
|
<text class="metric-name">{{ item.label }}</text>
|
|
|
|
|
|
<text class="metric-value">{{ item.value }}</text>
|
|
|
|
|
|
<text class="metric-unit">{{ item.unit }}</text>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
2025-10-27 13:21:43 +08:00
|
|
|
|
</view>
|
|
|
|
|
|
</scroll-view>
|
|
|
|
|
|
|
2025-10-31 17:18:30 +08:00
|
|
|
|
<scroll-view scroll-y class="scroll-container" v-if="currentTab == 2">
|
2025-10-27 13:21:43 +08:00
|
|
|
|
<klp-product-statistic></klp-product-statistic>
|
|
|
|
|
|
</scroll-view>
|
|
|
|
|
|
|
2025-10-31 17:18:30 +08:00
|
|
|
|
<scroll-view scroll-y class="scroll-container" v-if="currentTab == 3">
|
2025-10-27 13:21:43 +08:00
|
|
|
|
<klp-shutdown-statistic></klp-shutdown-statistic>
|
|
|
|
|
|
</scroll-view>
|
|
|
|
|
|
|
2025-10-31 17:18:30 +08:00
|
|
|
|
<scroll-view scroll-y class="scroll-container" v-if="currentTab == 4">
|
2025-10-27 13:21:43 +08:00
|
|
|
|
<klp-team-performance></klp-team-performance>
|
|
|
|
|
|
</scroll-view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
<script>
|
2025-10-31 17:18:30 +08:00
|
|
|
|
import { getAllPlantStateDefines, listPlantStateHistory, getCurrentShift } from '@/api/pocket/plantState'
|
|
|
|
|
|
import config from '@/config'
|
2025-10-31 14:50:19 +08:00
|
|
|
|
|
2025-10-27 13:21:43 +08:00
|
|
|
|
export default {
|
|
|
|
|
|
data() {
|
|
|
|
|
|
return {
|
|
|
|
|
|
currentTab: 1,
|
2025-10-29 15:38:20 +08:00
|
|
|
|
tabData: [
|
|
|
|
|
|
{ text: "实时监控", value: 1 },
|
|
|
|
|
|
{ text: "生产统计", value: 2 },
|
|
|
|
|
|
{ text: "停机统计", value: 3 },
|
|
|
|
|
|
{ text: "班组绩效", value: 4 }
|
|
|
|
|
|
],
|
2025-10-27 13:21:43 +08:00
|
|
|
|
webStatus: [
|
2025-10-31 17:18:30 +08:00
|
|
|
|
{ label: '网络状态', value: '检测中...' },
|
|
|
|
|
|
{ label: '当前班组', value: '—' }
|
2025-10-27 13:21:43 +08:00
|
|
|
|
],
|
2025-10-31 17:18:30 +08:00
|
|
|
|
lastUpdateTime: '—', // 最后更新时间
|
|
|
|
|
|
isRefreshing: false, // 是否正在刷新
|
|
|
|
|
|
refreshTimer: null, // 定时器
|
|
|
|
|
|
// 速度监控指标(ID=1,2,3)
|
|
|
|
|
|
speedMetrics: [
|
|
|
|
|
|
{ label: '出口带钢速度', value: '—', unit: 'm/min' },
|
|
|
|
|
|
{ label: '酸洗带钢速度', value: '—', unit: 'm/min' },
|
|
|
|
|
|
{ label: '圆盘剪速度', value: '—', unit: 'm/min' }
|
2025-10-27 13:21:43 +08:00
|
|
|
|
],
|
2025-10-31 17:18:30 +08:00
|
|
|
|
// 活套状态(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: '%' }
|
|
|
|
|
|
],
|
|
|
|
|
|
// 温度趋势图数据(ID=4,5,6)
|
|
|
|
|
|
tempChartData: {},
|
|
|
|
|
|
lineChartOpts: {
|
|
|
|
|
|
color: ["#0066cc", "#409eff", "#66b1ff"],
|
|
|
|
|
|
padding: [15, 15, 0, 15],
|
|
|
|
|
|
enableScroll: false,
|
2025-10-31 19:10:08 +08:00
|
|
|
|
legend: {
|
|
|
|
|
|
show: true,
|
|
|
|
|
|
position: "top",
|
|
|
|
|
|
fontSize: 10,
|
|
|
|
|
|
lineHeight: 14,
|
|
|
|
|
|
itemGap: 6
|
|
|
|
|
|
},
|
|
|
|
|
|
dataLabel: false, // 隐藏数据标签
|
|
|
|
|
|
dataPointShape: false, // 隐藏数据点
|
2025-10-31 17:18:30 +08:00
|
|
|
|
xAxis: {
|
|
|
|
|
|
disableGrid: true,
|
2025-10-31 19:10:08 +08:00
|
|
|
|
rotateLabel: true,
|
|
|
|
|
|
itemCount: 5, // 减少标签数量
|
|
|
|
|
|
labelCount: 5,
|
|
|
|
|
|
fontSize: 10
|
2025-10-27 13:21:43 +08:00
|
|
|
|
},
|
2025-10-31 17:18:30 +08:00
|
|
|
|
yAxis: {
|
|
|
|
|
|
gridType: "dash",
|
|
|
|
|
|
dashLength: 4,
|
|
|
|
|
|
gridColor: "#e4e7ed",
|
|
|
|
|
|
showTitle: true,
|
2025-10-31 19:10:08 +08:00
|
|
|
|
fontSize: 10,
|
2025-10-31 17:18:30 +08:00
|
|
|
|
data: [{ min: 0, title: "温度(°C)" }]
|
2025-10-31 19:10:08 +08:00
|
|
|
|
},
|
|
|
|
|
|
extra: {
|
|
|
|
|
|
line: {
|
|
|
|
|
|
type: "curve",
|
|
|
|
|
|
width: 2,
|
|
|
|
|
|
activeType: "hollow"
|
|
|
|
|
|
}
|
2025-10-27 13:21:43 +08:00
|
|
|
|
}
|
2025-10-31 17:18:30 +08:00
|
|
|
|
},
|
|
|
|
|
|
plantStateDefines: [] // 缓存所有的状态定义
|
2025-10-27 13:21:43 +08:00
|
|
|
|
};
|
|
|
|
|
|
},
|
|
|
|
|
|
mounted() {
|
2025-10-31 17:18:30 +08:00
|
|
|
|
this.loadAllData() // 加载所有数据
|
|
|
|
|
|
this.startAutoRefresh() // 启动自动刷新
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
beforeDestroy() {
|
|
|
|
|
|
this.stopAutoRefresh() // 页面销毁时清除定时器
|
2025-10-27 13:21:43 +08:00
|
|
|
|
},
|
|
|
|
|
|
methods: {
|
2025-10-31 17:18:30 +08:00
|
|
|
|
// 启动自动刷新(每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() // 更新时间
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
// 刷新数据(手动或自动)
|
|
|
|
|
|
refreshData(isSilent = false) {
|
|
|
|
|
|
if (this.isRefreshing) return // 防止重复刷新
|
|
|
|
|
|
|
|
|
|
|
|
this.isRefreshing = true
|
|
|
|
|
|
|
|
|
|
|
|
if (!isSilent) {
|
|
|
|
|
|
uni.showLoading({ title: '刷新中' })
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 依次刷新各个数据
|
|
|
|
|
|
Promise.all([
|
|
|
|
|
|
this.checkNetworkStatus(),
|
|
|
|
|
|
this.loadCurrentShift(),
|
|
|
|
|
|
this.initPlantStateDefines(isSilent)
|
|
|
|
|
|
]).finally(() => {
|
|
|
|
|
|
this.isRefreshing = false
|
|
|
|
|
|
if (!isSilent) {
|
|
|
|
|
|
uni.hideLoading()
|
|
|
|
|
|
uni.showToast({ title: '刷新成功', icon: 'success', duration: 1500 })
|
|
|
|
|
|
}
|
|
|
|
|
|
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}`
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
// 检测网络状态
|
|
|
|
|
|
checkNetworkStatus() {
|
|
|
|
|
|
return new Promise((resolve) => {
|
|
|
|
|
|
const startTime = Date.now()
|
|
|
|
|
|
|
|
|
|
|
|
uni.request({
|
|
|
|
|
|
url: config.baseUrl + '/pocket/proPlantStateDefine/allWithValues',
|
|
|
|
|
|
method: 'GET',
|
|
|
|
|
|
timeout: 5000,
|
|
|
|
|
|
success: (res) => {
|
|
|
|
|
|
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()
|
|
|
|
|
|
},
|
|
|
|
|
|
fail: () => {
|
|
|
|
|
|
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}`
|
2025-10-31 14:50:19 +08:00
|
|
|
|
}
|
|
|
|
|
|
}).catch(error => {
|
2025-10-31 17:18:30 +08:00
|
|
|
|
console.error('加载班组信息失败:', error)
|
2025-10-31 14:50:19 +08:00
|
|
|
|
})
|
|
|
|
|
|
},
|
2025-10-31 17:18:30 +08:00
|
|
|
|
|
|
|
|
|
|
// 获取班次名称
|
|
|
|
|
|
getShiftName(shift) {
|
|
|
|
|
|
const shiftMap = {
|
|
|
|
|
|
'A': '早班',
|
|
|
|
|
|
'B': '中班',
|
|
|
|
|
|
'C': '晚班'
|
|
|
|
|
|
}
|
|
|
|
|
|
return shiftMap[shift] || shift || '—'
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
// 获取班组名称
|
|
|
|
|
|
getCrewName(crew) {
|
|
|
|
|
|
const crewMap = {
|
|
|
|
|
|
1: '甲',
|
|
|
|
|
|
2: '乙',
|
|
|
|
|
|
3: '丙',
|
|
|
|
|
|
4: '丁'
|
2025-10-31 14:50:19 +08:00
|
|
|
|
}
|
2025-10-31 17:18:30 +08:00
|
|
|
|
return crewMap[crew] || crew || '—'
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
// 初始化:加载所有状态定义及其当前值
|
|
|
|
|
|
initPlantStateDefines(isSilent = false) {
|
|
|
|
|
|
if (!isSilent) {
|
|
|
|
|
|
uni.showLoading({ title: '加载中' })
|
2025-10-31 14:50:19 +08:00
|
|
|
|
}
|
2025-10-31 17:18:30 +08:00
|
|
|
|
|
|
|
|
|
|
return getAllPlantStateDefines().then(response => {
|
|
|
|
|
|
if (response.code === 200 && response.data) {
|
|
|
|
|
|
this.plantStateDefines = response.data
|
|
|
|
|
|
|
|
|
|
|
|
if (!isSilent) {
|
|
|
|
|
|
console.log('状态定义已加载:', this.plantStateDefines)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 更新所有实时指标
|
|
|
|
|
|
this.updateCurrentMetrics()
|
|
|
|
|
|
|
|
|
|
|
|
// 加载温度趋势图
|
|
|
|
|
|
return this.loadTempTrend(isSilent)
|
|
|
|
|
|
} else {
|
|
|
|
|
|
if (!isSilent) {
|
|
|
|
|
|
uni.hideLoading()
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}).catch(error => {
|
|
|
|
|
|
if (!isSilent) {
|
|
|
|
|
|
uni.hideLoading()
|
|
|
|
|
|
}
|
|
|
|
|
|
console.error('加载状态定义失败:', error)
|
|
|
|
|
|
})
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
// 更新所有实时指标
|
|
|
|
|
|
updateCurrentMetrics() {
|
|
|
|
|
|
// 1. 速度监控(ID=1,2,3)
|
|
|
|
|
|
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'
|
|
|
|
|
|
}
|
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
// 2. 活套状态(ID=8,9,10)
|
|
|
|
|
|
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 || '%'
|
|
|
|
|
|
}
|
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
// 3. 酸槽浓度(ID=11-16)
|
|
|
|
|
|
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)
|
|
|
|
|
|
}
|
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
// 4. 其他工艺参数(ID=7,17,18,19,20)
|
|
|
|
|
|
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 || '%'
|
|
|
|
|
|
}
|
|
|
|
|
|
]
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
// 加载温度趋势图(3个酸槽温度:ID=4,5,6)
|
|
|
|
|
|
loadTempTrend(isSilent = false) {
|
|
|
|
|
|
return listPlantStateHistory({ pageNum: 1, pageSize: 30 }).then(response => {
|
|
|
|
|
|
if (!isSilent) {
|
|
|
|
|
|
uni.hideLoading()
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-31 14:50:19 +08:00
|
|
|
|
if (response.code === 200 && response.rows && response.rows.length > 0) {
|
|
|
|
|
|
const categories = []
|
2025-10-31 17:18:30 +08:00
|
|
|
|
const tank1Data = []
|
|
|
|
|
|
const tank2Data = []
|
|
|
|
|
|
const tank3Data = []
|
|
|
|
|
|
|
|
|
|
|
|
// 获取温度定义
|
|
|
|
|
|
const tank1Temp = this.getDefineById(4)
|
|
|
|
|
|
const tank2Temp = this.getDefineById(5)
|
|
|
|
|
|
const tank3Temp = this.getDefineById(6)
|
|
|
|
|
|
|
2025-10-31 14:50:19 +08:00
|
|
|
|
response.rows.forEach(item => {
|
|
|
|
|
|
const dateStr = this.formatDate(item.insdate)
|
|
|
|
|
|
categories.push(dateStr)
|
2025-10-31 17:18:30 +08:00
|
|
|
|
tank1Data.push(Number(item.value4) || 0) // ID=4 对应 value4
|
|
|
|
|
|
tank2Data.push(Number(item.value5) || 0) // ID=5 对应 value5
|
|
|
|
|
|
tank3Data.push(Number(item.value6) || 0) // ID=6 对应 value6
|
2025-10-31 14:50:19 +08:00
|
|
|
|
})
|
2025-10-31 17:18:30 +08:00
|
|
|
|
|
|
|
|
|
|
this.tempChartData = {
|
|
|
|
|
|
categories: categories.reverse(),
|
2025-10-31 14:50:19 +08:00
|
|
|
|
series: [
|
2025-10-31 17:18:30 +08:00
|
|
|
|
{ name: tank1Temp?.comments || '1#酸槽温度', data: tank1Data.reverse() },
|
|
|
|
|
|
{ name: tank2Temp?.comments || '2#酸槽温度', data: tank2Data.reverse() },
|
|
|
|
|
|
{ name: tank3Temp?.comments || '3#酸槽温度', data: tank3Data.reverse() }
|
2025-10-31 14:50:19 +08:00
|
|
|
|
]
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}).catch(error => {
|
2025-10-31 17:18:30 +08:00
|
|
|
|
if (!isSilent) {
|
|
|
|
|
|
uni.hideLoading()
|
|
|
|
|
|
}
|
|
|
|
|
|
console.error('加载温度趋势失败:', error)
|
|
|
|
|
|
})
|
2025-10-31 14:50:19 +08:00
|
|
|
|
},
|
2025-10-31 17:18:30 +08:00
|
|
|
|
|
|
|
|
|
|
// 根据ID获取Define对象
|
|
|
|
|
|
getDefineById(id) {
|
|
|
|
|
|
return this.plantStateDefines.find(item => item.id == id)
|
2025-10-31 14:50:19 +08:00
|
|
|
|
},
|
2025-10-31 17:18:30 +08:00
|
|
|
|
|
2025-10-31 14:50:19 +08:00
|
|
|
|
// 格式化日期
|
|
|
|
|
|
formatDate(dateStr) {
|
|
|
|
|
|
if (!dateStr) return ''
|
|
|
|
|
|
const date = new Date(dateStr)
|
2025-10-31 17:18:30 +08:00
|
|
|
|
const hour = String(date.getHours()).padStart(2, '0')
|
|
|
|
|
|
const minute = String(date.getMinutes()).padStart(2, '0')
|
|
|
|
|
|
return `${hour}:${minute}`
|
2025-10-27 13:21:43 +08:00
|
|
|
|
},
|
2025-10-31 17:18:30 +08:00
|
|
|
|
|
|
|
|
|
|
// 格式化数值(保留2位小数)
|
|
|
|
|
|
formatValue(value) {
|
|
|
|
|
|
if (value === null || value === undefined || value === '') return '—'
|
|
|
|
|
|
const num = Number(value)
|
|
|
|
|
|
if (isNaN(num)) return '—'
|
|
|
|
|
|
return num.toFixed(2)
|
2025-10-27 13:21:43 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
|
|
<style scoped lang="scss">
|
2025-10-31 17:18:30 +08:00
|
|
|
|
/* 页面容器 */
|
|
|
|
|
|
.page-container {
|
|
|
|
|
|
min-height: 100vh;
|
|
|
|
|
|
background: #f5f7fa;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* 标签栏 */
|
2025-10-29 15:38:20 +08:00
|
|
|
|
.tab-container {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
background: #fff;
|
2025-10-31 17:18:30 +08:00
|
|
|
|
border-bottom: 2rpx solid #e4e7ed;
|
2025-10-27 13:21:43 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-29 15:38:20 +08:00
|
|
|
|
.tab-item {
|
|
|
|
|
|
flex: 1;
|
|
|
|
|
|
text-align: center;
|
2025-10-31 17:18:30 +08:00
|
|
|
|
padding: 28rpx 0;
|
2025-10-29 15:38:20 +08:00
|
|
|
|
position: relative;
|
2025-10-31 17:18:30 +08:00
|
|
|
|
|
2025-10-29 15:38:20 +08:00
|
|
|
|
.tab-label {
|
|
|
|
|
|
font-size: 28rpx;
|
2025-10-31 17:18:30 +08:00
|
|
|
|
color: #606266;
|
2025-10-29 15:38:20 +08:00
|
|
|
|
font-weight: 400;
|
|
|
|
|
|
}
|
2025-10-31 17:18:30 +08:00
|
|
|
|
|
2025-10-29 15:38:20 +08:00
|
|
|
|
&.tab-active {
|
|
|
|
|
|
.tab-label {
|
2025-10-31 17:18:30 +08:00
|
|
|
|
color: #0066cc;
|
|
|
|
|
|
font-weight: 500;
|
2025-10-29 15:38:20 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-10-31 17:18:30 +08:00
|
|
|
|
|
2025-10-29 15:38:20 +08:00
|
|
|
|
.tab-indicator {
|
|
|
|
|
|
position: absolute;
|
|
|
|
|
|
bottom: 0;
|
|
|
|
|
|
left: 50%;
|
|
|
|
|
|
transform: translateX(-50%);
|
2025-10-31 17:18:30 +08:00
|
|
|
|
width: 60rpx;
|
|
|
|
|
|
height: 3rpx;
|
|
|
|
|
|
background: #0066cc;
|
2025-10-29 15:38:20 +08:00
|
|
|
|
}
|
2025-10-27 13:21:43 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-31 17:18:30 +08:00
|
|
|
|
/* 刷新按钮(固定在右下角) */
|
|
|
|
|
|
.refresh-btn-fixed {
|
|
|
|
|
|
position: fixed;
|
|
|
|
|
|
right: 32rpx;
|
|
|
|
|
|
bottom: 120rpx;
|
|
|
|
|
|
width: 96rpx;
|
|
|
|
|
|
height: 96rpx;
|
|
|
|
|
|
background: #0066cc;
|
|
|
|
|
|
border-radius: 50%;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
box-shadow: 0 8rpx 20rpx rgba(0, 102, 204, 0.4);
|
|
|
|
|
|
z-index: 999;
|
|
|
|
|
|
|
|
|
|
|
|
&:active {
|
|
|
|
|
|
opacity: 0.8;
|
|
|
|
|
|
transform: scale(0.95);
|
|
|
|
|
|
}
|
2025-10-27 13:21:43 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-31 17:18:30 +08:00
|
|
|
|
.refresh-icon {
|
|
|
|
|
|
font-size: 48rpx;
|
|
|
|
|
|
color: #fff;
|
|
|
|
|
|
display: block;
|
|
|
|
|
|
line-height: 1;
|
|
|
|
|
|
|
|
|
|
|
|
&.rotating {
|
|
|
|
|
|
animation: rotate 1s linear infinite;
|
|
|
|
|
|
}
|
2025-10-27 13:21:43 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-31 17:18:30 +08:00
|
|
|
|
@keyframes rotate {
|
|
|
|
|
|
from {
|
|
|
|
|
|
transform: rotate(0deg);
|
|
|
|
|
|
}
|
|
|
|
|
|
to {
|
|
|
|
|
|
transform: rotate(360deg);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* 滚动容器 */
|
|
|
|
|
|
.scroll-container {
|
|
|
|
|
|
height: calc(100vh - 96rpx);
|
|
|
|
|
|
padding: 24rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* 顶部状态栏 */
|
|
|
|
|
|
.status-bar {
|
2025-10-27 13:21:43 +08:00
|
|
|
|
display: flex;
|
2025-10-31 17:18:30 +08:00
|
|
|
|
align-items: center;
|
|
|
|
|
|
background: #fff;
|
|
|
|
|
|
padding: 24rpx 32rpx;
|
2025-10-27 13:21:43 +08:00
|
|
|
|
margin-bottom: 24rpx;
|
2025-10-31 17:18:30 +08:00
|
|
|
|
border-radius: 8rpx;
|
|
|
|
|
|
border: 1rpx solid #e4e7ed;
|
2025-10-27 13:21:43 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-31 17:18:30 +08:00
|
|
|
|
.status-item {
|
|
|
|
|
|
flex: 1;
|
2025-10-27 13:21:43 +08:00
|
|
|
|
display: flex;
|
2025-10-31 17:18:30 +08:00
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
gap: 16rpx;
|
2025-10-27 13:21:43 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-31 17:18:30 +08:00
|
|
|
|
.status-label {
|
2025-10-29 15:38:20 +08:00
|
|
|
|
font-size: 26rpx;
|
2025-10-31 17:18:30 +08:00
|
|
|
|
color: #909399;
|
2025-10-27 13:21:43 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-31 17:18:30 +08:00
|
|
|
|
.status-value {
|
2025-10-29 15:38:20 +08:00
|
|
|
|
font-size: 28rpx;
|
2025-10-27 13:21:43 +08:00
|
|
|
|
font-weight: 500;
|
2025-10-31 17:18:30 +08:00
|
|
|
|
color: #303133;
|
2025-10-27 13:21:43 +08:00
|
|
|
|
|
2025-10-31 17:18:30 +08:00
|
|
|
|
&.status-通畅 {
|
|
|
|
|
|
color: #67c23a;
|
2025-10-29 15:38:20 +08:00
|
|
|
|
}
|
2025-10-31 17:18:30 +08:00
|
|
|
|
|
|
|
|
|
|
&.status-卡顿 {
|
|
|
|
|
|
color: #e6a23c;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
&.status-异常 {
|
|
|
|
|
|
color: #f56c6c;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
&.status-time {
|
|
|
|
|
|
color: #909399;
|
2025-10-27 13:21:43 +08:00
|
|
|
|
font-size: 24rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-31 17:18:30 +08:00
|
|
|
|
.status-divider {
|
|
|
|
|
|
width: 1rpx;
|
|
|
|
|
|
height: 40rpx;
|
|
|
|
|
|
background: #e4e7ed;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* 区块样式 */
|
|
|
|
|
|
.section {
|
|
|
|
|
|
margin-bottom: 24rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.section-title {
|
|
|
|
|
|
font-size: 30rpx;
|
|
|
|
|
|
font-weight: 500;
|
|
|
|
|
|
color: #303133;
|
|
|
|
|
|
margin-bottom: 20rpx;
|
|
|
|
|
|
padding-left: 16rpx;
|
|
|
|
|
|
border-left: 4rpx solid #0066cc;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* 指标卡片 - 3列布局 */
|
|
|
|
|
|
.metrics-grid-3 {
|
|
|
|
|
|
display: grid;
|
|
|
|
|
|
grid-template-columns: repeat(3, 1fr);
|
|
|
|
|
|
gap: 20rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* 指标卡片 - 2列布局 */
|
|
|
|
|
|
.metrics-grid-2 {
|
|
|
|
|
|
display: grid;
|
|
|
|
|
|
grid-template-columns: repeat(2, 1fr);
|
|
|
|
|
|
gap: 20rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.metric-box {
|
|
|
|
|
|
background: #fff;
|
|
|
|
|
|
border: 1rpx solid #e4e7ed;
|
|
|
|
|
|
border-radius: 8rpx;
|
|
|
|
|
|
padding: 28rpx 20rpx;
|
|
|
|
|
|
text-align: center;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.metric-name {
|
|
|
|
|
|
display: block;
|
|
|
|
|
|
font-size: 24rpx;
|
|
|
|
|
|
color: #909399;
|
|
|
|
|
|
margin-bottom: 16rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.metric-value {
|
|
|
|
|
|
display: block;
|
|
|
|
|
|
font-size: 48rpx;
|
2025-10-29 15:38:20 +08:00
|
|
|
|
font-weight: 600;
|
2025-10-31 17:18:30 +08:00
|
|
|
|
color: #0066cc;
|
|
|
|
|
|
margin-bottom: 8rpx;
|
|
|
|
|
|
line-height: 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.metric-unit {
|
|
|
|
|
|
display: block;
|
|
|
|
|
|
font-size: 22rpx;
|
|
|
|
|
|
color: #909399;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* 图表容器 */
|
|
|
|
|
|
.chart-box {
|
|
|
|
|
|
background: #fff;
|
|
|
|
|
|
border: 1rpx solid #e4e7ed;
|
|
|
|
|
|
border-radius: 8rpx;
|
|
|
|
|
|
padding: 24rpx 16rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* 酸槽监控网格 */
|
|
|
|
|
|
.tank-grid {
|
|
|
|
|
|
display: grid;
|
|
|
|
|
|
grid-template-columns: repeat(3, 1fr);
|
|
|
|
|
|
gap: 20rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.tank-card {
|
|
|
|
|
|
background: #fff;
|
|
|
|
|
|
border: 2rpx solid #0066cc;
|
|
|
|
|
|
border-radius: 8rpx;
|
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.tank-header {
|
|
|
|
|
|
background: #0066cc;
|
|
|
|
|
|
color: #fff;
|
|
|
|
|
|
font-size: 28rpx;
|
|
|
|
|
|
font-weight: 500;
|
|
|
|
|
|
padding: 20rpx;
|
|
|
|
|
|
text-align: center;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.tank-data {
|
|
|
|
|
|
padding: 24rpx 16rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.tank-row {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
padding: 12rpx 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.data-label {
|
|
|
|
|
|
font-size: 22rpx;
|
|
|
|
|
|
color: #909399;
|
|
|
|
|
|
margin-bottom: 8rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.data-value {
|
|
|
|
|
|
font-size: 36rpx;
|
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
color: #303133;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.data-unit {
|
|
|
|
|
|
font-size: 20rpx;
|
|
|
|
|
|
font-weight: 400;
|
|
|
|
|
|
color: #909399;
|
|
|
|
|
|
margin-left: 4rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.tank-divider {
|
|
|
|
|
|
height: 1rpx;
|
|
|
|
|
|
background: #e4e7ed;
|
|
|
|
|
|
margin: 12rpx 0;
|
2025-10-27 13:21:43 +08:00
|
|
|
|
}
|
|
|
|
|
|
</style>
|