596 lines
18 KiB
Vue
596 lines
18 KiB
Vue
<template>
|
||
<view class="page-container">
|
||
<!-- 简洁标签栏 -->
|
||
<view class="tab-container">
|
||
<view
|
||
v-for="item in tabData"
|
||
: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>
|
||
</view>
|
||
|
||
<!-- 刷新按钮 -->
|
||
<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="disabled-container">
|
||
<view class="disabled-icon">🔒</view>
|
||
<view class="disabled-title">该功能未启用</view>
|
||
<view class="disabled-desc">镀锌线2实时监控功能暂未启用,请联系管理员</view>
|
||
</view>
|
||
</scroll-view>
|
||
|
||
<scroll-view scroll-y class="scroll-container" v-if="currentTab == 2">
|
||
<klp-product-statistic></klp-product-statistic>
|
||
</scroll-view>
|
||
|
||
<scroll-view scroll-y class="scroll-container" v-if="currentTab == 3">
|
||
<klp-shutdown-statistic></klp-shutdown-statistic>
|
||
</scroll-view>
|
||
|
||
<scroll-view scroll-y class="scroll-container" v-if="currentTab == 4">
|
||
<klp-team-performance></klp-team-performance>
|
||
</scroll-view>
|
||
</view>
|
||
</template>
|
||
|
||
<script>
|
||
import { getAllPlantStateDefines, listPlantStateHistory, getCurrentShift } from '@/api/pocket/plantState'
|
||
import config from '@/config'
|
||
|
||
export default {
|
||
data() {
|
||
return {
|
||
currentTab: 1,
|
||
tabData: [
|
||
{ text: "实时监控", value: 1 },
|
||
{ text: "生产统计", value: 2 },
|
||
{ text: "停机统计", value: 3 },
|
||
{ text: "班组绩效", value: 4 }
|
||
],
|
||
webStatus: [
|
||
{ label: '网络状态', value: '检测中...' },
|
||
{ label: '当前班组', value: '—' }
|
||
],
|
||
lastUpdateTime: '—',
|
||
isRefreshing: false,
|
||
refreshTimer: null,
|
||
// 轧辊速度(ID=36-41)
|
||
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' }
|
||
],
|
||
// 机架压下率(ID=24-29)
|
||
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: '%' }
|
||
],
|
||
// 带钢张力(ID=42-48)
|
||
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' }
|
||
],
|
||
// 功率百分比(ID=49-54)
|
||
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: '%' }
|
||
],
|
||
// 轧制力趋势图(ID=30-35)
|
||
forceChartData: {},
|
||
lineChartOpts: {
|
||
color: ["#0066cc", "#409eff", "#66b1ff", "#a0cfff", "#d9ecff", "#ecf5ff"],
|
||
padding: [15, 15, 0, 15],
|
||
enableScroll: false,
|
||
legend: {
|
||
show: true,
|
||
position: "top",
|
||
fontSize: 10,
|
||
lineHeight: 14,
|
||
itemGap: 6
|
||
},
|
||
dataLabel: false, // 隐藏数据标签
|
||
dataPointShape: false, // 隐藏数据点
|
||
xAxis: {
|
||
disableGrid: true,
|
||
rotateLabel: true,
|
||
itemCount: 5, // 减少标签数量
|
||
labelCount: 5,
|
||
fontSize: 10
|
||
},
|
||
yAxis: {
|
||
gridType: "dash",
|
||
dashLength: 4,
|
||
gridColor: "#e4e7ed",
|
||
showTitle: true,
|
||
fontSize: 10,
|
||
data: [{ min: 0, title: "轧制力(kN)" }]
|
||
},
|
||
extra: {
|
||
line: {
|
||
type: "curve", // 曲线平滑
|
||
width: 2,
|
||
activeType: "hollow" // 点击时显示空心点
|
||
}
|
||
}
|
||
},
|
||
plantStateDefines: []
|
||
};
|
||
},
|
||
|
||
mounted() {
|
||
this.loadAllData()
|
||
this.startAutoRefresh()
|
||
},
|
||
|
||
beforeDestroy() {
|
||
this.stopAutoRefresh()
|
||
},
|
||
|
||
methods: {
|
||
startAutoRefresh() {
|
||
this.refreshTimer = setInterval(() => {
|
||
this.refreshData(true)
|
||
}, 30000)
|
||
},
|
||
|
||
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}`
|
||
}
|
||
}).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 || '—'
|
||
},
|
||
|
||
initPlantStateDefines(isSilent = false) {
|
||
if (!isSilent) uni.showLoading({ title: '加载中' })
|
||
|
||
return getAllPlantStateDefines().then(response => {
|
||
if (response.code === 200 && response.data) {
|
||
this.plantStateDefines = response.data
|
||
if (!isSilent) console.log('镀锌线1状态定义已加载:', this.plantStateDefines)
|
||
this.updateCurrentMetrics()
|
||
return this.loadForceHistory(isSilent)
|
||
} else {
|
||
if (!isSilent) uni.hideLoading()
|
||
}
|
||
}).catch(error => {
|
||
if (!isSilent) uni.hideLoading()
|
||
console.error('加载状态定义失败:', error)
|
||
})
|
||
},
|
||
|
||
updateCurrentMetrics() {
|
||
// 1. 轧辊速度(ID=36-41:rollSpeed1-6)
|
||
this.rollSpeedMetrics = [
|
||
{ label: '1#机架', value: this.formatValue(this.getDefineById(36)?.currentValue), unit: this.getDefineById(36)?.units || 'm/min' },
|
||
{ label: '2#机架', value: this.formatValue(this.getDefineById(37)?.currentValue), unit: this.getDefineById(37)?.units || 'm/min' },
|
||
{ label: '3#机架', value: this.formatValue(this.getDefineById(38)?.currentValue), unit: this.getDefineById(38)?.units || 'm/min' },
|
||
{ label: '4#机架', value: this.formatValue(this.getDefineById(39)?.currentValue), unit: this.getDefineById(39)?.units || 'm/min' },
|
||
{ label: '5#机架', value: this.formatValue(this.getDefineById(40)?.currentValue), unit: this.getDefineById(40)?.units || 'm/min' },
|
||
{ label: '6#机架', value: this.formatValue(this.getDefineById(41)?.currentValue), unit: this.getDefineById(41)?.units || 'm/min' }
|
||
]
|
||
|
||
// 2. 机架压下率(ID=24-29:reduc1-6)
|
||
this.reducMetrics = [
|
||
{ label: '1#机架', value: this.formatValue(this.getDefineById(24)?.currentValue), unit: this.getDefineById(24)?.units || '%' },
|
||
{ label: '2#机架', value: this.formatValue(this.getDefineById(25)?.currentValue), unit: this.getDefineById(25)?.units || '%' },
|
||
{ label: '3#机架', value: this.formatValue(this.getDefineById(26)?.currentValue), unit: this.getDefineById(26)?.units || '%' },
|
||
{ label: '4#机架', value: this.formatValue(this.getDefineById(27)?.currentValue), unit: this.getDefineById(27)?.units || '%' },
|
||
{ label: '5#机架', value: this.formatValue(this.getDefineById(28)?.currentValue), unit: this.getDefineById(28)?.units || '%' },
|
||
{ label: '6#机架', value: this.formatValue(this.getDefineById(29)?.currentValue), unit: this.getDefineById(29)?.units || '%' }
|
||
]
|
||
|
||
// 3. 带钢张力(ID=42-48:tensionForce0-6)
|
||
this.tensionMetrics = [
|
||
{ label: '0#张力', value: this.formatValue(this.getDefineById(42)?.currentValue), unit: this.getDefineById(42)?.units || 'kN' },
|
||
{ label: '1#张力', value: this.formatValue(this.getDefineById(43)?.currentValue), unit: this.getDefineById(43)?.units || 'kN' },
|
||
{ label: '2#张力', value: this.formatValue(this.getDefineById(44)?.currentValue), unit: this.getDefineById(44)?.units || 'kN' },
|
||
{ label: '3#张力', value: this.formatValue(this.getDefineById(45)?.currentValue), unit: this.getDefineById(45)?.units || 'kN' },
|
||
{ label: '4#张力', value: this.formatValue(this.getDefineById(46)?.currentValue), unit: this.getDefineById(46)?.units || 'kN' },
|
||
{ label: '5#张力', value: this.formatValue(this.getDefineById(47)?.currentValue), unit: this.getDefineById(47)?.units || 'kN' },
|
||
{ label: '6#张力', value: this.formatValue(this.getDefineById(48)?.currentValue), unit: this.getDefineById(48)?.units || 'kN' }
|
||
]
|
||
|
||
// 4. 功率百分比(ID=49-54:powerRatio1-6)
|
||
this.powerMetrics = [
|
||
{ label: '1#机架', value: this.formatValue(this.getDefineById(49)?.currentValue), unit: this.getDefineById(49)?.units || '%' },
|
||
{ label: '2#机架', value: this.formatValue(this.getDefineById(50)?.currentValue), unit: this.getDefineById(50)?.units || '%' },
|
||
{ label: '3#机架', value: this.formatValue(this.getDefineById(51)?.currentValue), unit: this.getDefineById(51)?.units || '%' },
|
||
{ label: '4#机架', value: this.formatValue(this.getDefineById(52)?.currentValue), unit: this.getDefineById(52)?.units || '%' },
|
||
{ label: '5#机架', value: this.formatValue(this.getDefineById(53)?.currentValue), unit: this.getDefineById(53)?.units || '%' },
|
||
{ label: '6#机架', value: this.formatValue(this.getDefineById(54)?.currentValue), unit: this.getDefineById(54)?.units || '%' }
|
||
]
|
||
},
|
||
|
||
loadForceHistory(isSilent = false) {
|
||
return listPlantStateHistory({ pageNum: 1, pageSize: 30 }).then(response => {
|
||
if (!isSilent) uni.hideLoading()
|
||
|
||
if (response.code === 200 && response.rows && response.rows.length > 0) {
|
||
const categories = []
|
||
const force1Data = []
|
||
const force2Data = []
|
||
const force3Data = []
|
||
const force4Data = []
|
||
const force5Data = []
|
||
const force6Data = []
|
||
|
||
response.rows.forEach(item => {
|
||
const dateStr = this.formatDate(item.insdate)
|
||
categories.push(dateStr)
|
||
force1Data.push(Number(item.value30) || 0) // ID=30
|
||
force2Data.push(Number(item.value31) || 0) // ID=31
|
||
force3Data.push(Number(item.value32) || 0) // ID=32
|
||
force4Data.push(Number(item.value33) || 0) // ID=33
|
||
force5Data.push(Number(item.value34) || 0) // ID=34
|
||
force6Data.push(Number(item.value35) || 0) // ID=35
|
||
})
|
||
|
||
this.forceChartData = {
|
||
categories: categories.reverse(),
|
||
series: [
|
||
{ 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() }
|
||
]
|
||
}
|
||
}
|
||
}).catch(error => {
|
||
if (!isSilent) uni.hideLoading()
|
||
console.error('加载轧制力历史失败:', error)
|
||
})
|
||
},
|
||
|
||
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)
|
||
}
|
||
}
|
||
};
|
||
</script>
|
||
|
||
<style scoped lang="scss">
|
||
/* 页面容器 */
|
||
.page-container {
|
||
min-height: 100vh;
|
||
background: #f5f7fa;
|
||
}
|
||
|
||
/* 标签栏 */
|
||
.tab-container {
|
||
display: flex;
|
||
background: #fff;
|
||
border-bottom: 2rpx solid #e4e7ed;
|
||
}
|
||
|
||
.tab-item {
|
||
flex: 1;
|
||
text-align: center;
|
||
padding: 28rpx 0;
|
||
position: relative;
|
||
|
||
.tab-label {
|
||
font-size: 28rpx;
|
||
color: #606266;
|
||
font-weight: 400;
|
||
}
|
||
|
||
&.tab-active {
|
||
.tab-label {
|
||
color: #0066cc;
|
||
font-weight: 500;
|
||
}
|
||
}
|
||
|
||
.tab-indicator {
|
||
position: absolute;
|
||
bottom: 0;
|
||
left: 50%;
|
||
transform: translateX(-50%);
|
||
width: 60rpx;
|
||
height: 3rpx;
|
||
background: #0066cc;
|
||
}
|
||
}
|
||
|
||
/* 刷新按钮 */
|
||
.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);
|
||
}
|
||
}
|
||
|
||
.refresh-icon {
|
||
font-size: 48rpx;
|
||
color: #fff;
|
||
display: block;
|
||
line-height: 1;
|
||
|
||
&.rotating {
|
||
animation: rotate 1s linear infinite;
|
||
}
|
||
}
|
||
|
||
@keyframes rotate {
|
||
from { transform: rotate(0deg); }
|
||
to { transform: rotate(360deg); }
|
||
}
|
||
|
||
/* 滚动容器 */
|
||
.scroll-container {
|
||
height: calc(100vh - 96rpx);
|
||
padding: 24rpx;
|
||
}
|
||
|
||
/* 顶部状态栏 */
|
||
.status-bar {
|
||
display: flex;
|
||
align-items: center;
|
||
background: #fff;
|
||
padding: 24rpx 32rpx;
|
||
margin-bottom: 24rpx;
|
||
border-radius: 8rpx;
|
||
border: 1rpx solid #e4e7ed;
|
||
}
|
||
|
||
.status-item {
|
||
flex: 1;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
gap: 16rpx;
|
||
}
|
||
|
||
.status-label {
|
||
font-size: 26rpx;
|
||
color: #909399;
|
||
}
|
||
|
||
.status-value {
|
||
font-size: 28rpx;
|
||
font-weight: 500;
|
||
color: #303133;
|
||
|
||
&.status-通畅 { color: #67c23a; }
|
||
&.status-卡顿 { color: #e6a23c; }
|
||
&.status-异常 { color: #f56c6c; }
|
||
&.status-time {
|
||
color: #909399;
|
||
font-size: 24rpx;
|
||
}
|
||
}
|
||
|
||
.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;
|
||
}
|
||
|
||
.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;
|
||
font-weight: 600;
|
||
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;
|
||
}
|
||
|
||
/* 未启用容器 */
|
||
.disabled-container {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
height: calc(100vh - 200rpx);
|
||
padding: 40rpx;
|
||
text-align: center;
|
||
}
|
||
|
||
.disabled-icon {
|
||
font-size: 120rpx;
|
||
margin-bottom: 32rpx;
|
||
opacity: 0.6;
|
||
}
|
||
|
||
.disabled-title {
|
||
font-size: 32rpx;
|
||
font-weight: 600;
|
||
color: #303133;
|
||
margin-bottom: 16rpx;
|
||
}
|
||
|
||
.disabled-desc {
|
||
font-size: 26rpx;
|
||
color: #909399;
|
||
line-height: 1.6;
|
||
}
|
||
</style>
|