Files
klp-mono/apps/hand-factory/components/lines/paint.vue

596 lines
16 KiB
Vue
Raw Normal View History

2025-10-27 13:21:43 +08:00
<template>
2025-10-31 19:10:08 +08:00
<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">
2025-12-07 12:54:39 +08:00
<!-- 未启用提示 -->
<view class="disabled-container">
<view class="disabled-icon">🔒</view>
<view class="disabled-title">该功能未启用</view>
<view class="disabled-desc">涂装线实时监控功能暂未启用请联系管理员</view>
2025-10-31 19:10:08 +08:00
</view>
</scroll-view>
2025-10-27 13:21:43 +08:00
2025-10-31 19:10:08 +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 19:10:08 +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 19:10:08 +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 19:10:08 +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 {
2025-10-31 19:10:08 +08:00
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-31 19:10:08 +08:00
webStatus: [
{ label: '网络状态', value: '检测中...' },
{ label: '当前班组', value: '—' }
2025-10-27 13:21:43 +08:00
],
2025-10-31 19:10:08 +08:00
lastUpdateTime: '—',
isRefreshing: false,
refreshTimer: null,
// 涂装速度ID=1-3
speedMetrics: [
{ label: '出口带钢速度', value: '—', unit: 'm/min' },
{ label: '涂装带钢速度', value: '—', unit: 'm/min' },
{ label: '圆盘剪速度', value: '—', unit: 'm/min' }
],
// 活套状态ID=8-10
looperMetrics: [
{ label: '入口活套', value: '—', unit: '%' },
{ label: '出口活套', value: '—', unit: '%' },
{ label: '联机活套', value: '—', unit: '%' }
],
// 工艺参数ID=7, 17-19
processMetrics: [
{ label: '漂洗温度', value: '—', unit: '°C' },
{ label: '烘干温度', value: '—', unit: '°C' },
{ label: '漂洗电导率', value: '—', unit: 'g/L' },
{ label: '联机活套张力', value: '—', unit: 'kN' }
],
// 拉矫参数ID=20-23
tlvMetrics: [
{ label: '拉矫延伸率', value: '—', unit: '%' },
{ label: '破磷机插入量1', value: '—', unit: 'mm' },
{ label: '破磷机插入量2', value: '—', unit: 'mm' },
{ label: '破磷机插入量3', value: '—', unit: 'mm' }
],
// 温度趋势图ID=4-7, 17
tempChartData: {},
lineChartOpts: {
color: ["#0066cc", "#409eff", "#66b1ff", "#a0cfff", "#d9ecff"],
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: "温度(°C)" }]
},
extra: {
line: {
type: "curve",
width: 2,
activeType: "hollow"
}
}
},
plantStateDefines: []
};
2025-10-27 13:21:43 +08:00
},
2025-10-31 19:10:08 +08:00
2025-10-27 13:21:43 +08:00
mounted() {
2025-10-31 19:10:08 +08:00
this.loadAllData()
this.startAutoRefresh()
2025-10-27 13:21:43 +08:00
},
2025-10-31 19:10:08 +08:00
beforeDestroy() {
this.stopAutoRefresh()
},
2025-10-27 13:21:43 +08:00
methods: {
2025-10-31 19:10:08 +08:00
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() {
2025-10-31 19:10:08 +08:00
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()
}
2025-10-31 19:10:08 +08:00
})
})
},
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
}
2025-10-31 19:10:08 +08:00
}).catch(error => {
console.error('加载班组信息失败:', error)
2025-10-31 14:50:19 +08:00
})
},
2025-10-31 19:10:08 +08:00
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
2025-10-31 19:10:08 +08:00
if (!isSilent) console.log('彩涂线状态定义已加载:', this.plantStateDefines)
this.updateCurrentMetrics()
return this.loadTempTrend(isSilent)
2025-10-31 14:50:19 +08:00
} else {
2025-10-31 19:10:08 +08:00
if (!isSilent) uni.hideLoading()
2025-10-27 13:21:43 +08:00
}
2025-10-31 14:50:19 +08:00
}).catch(error => {
2025-10-31 19:10:08 +08:00
if (!isSilent) uni.hideLoading()
console.error('加载状态定义失败:', error)
2025-10-31 14:50:19 +08:00
})
},
2025-10-31 19:10:08 +08:00
updateCurrentMetrics() {
// 1. 速度监控ID=1-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-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=7, 17-19
const rinseTemp = this.getDefineById(7)
const windTemp = this.getDefineById(17)
const rinseFlow = this.getDefineById(18)
const telTension = this.getDefineById(19)
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' }
]
// 4. 拉矫参数ID=20-23
const tlvElong = this.getDefineById(20)
const tlvMesh1 = this.getDefineById(21)
const tlvMesh2 = this.getDefineById(22)
const tlvMesh3 = this.getDefineById(23)
this.tlvMetrics = [
{ label: tlvElong?.comments || '拉矫延伸率', value: this.formatValue(tlvElong?.currentValue), unit: tlvElong?.units || '%' },
{ label: tlvMesh1?.comments || '破磷机插入量1', value: this.formatValue(tlvMesh1?.currentValue), unit: tlvMesh1?.units || 'mm' },
{ label: tlvMesh2?.comments || '破磷机插入量2', value: this.formatValue(tlvMesh2?.currentValue), unit: tlvMesh2?.units || 'mm' },
{ label: tlvMesh3?.comments || '破磷机插入量3', value: this.formatValue(tlvMesh3?.currentValue), unit: tlvMesh3?.units || 'mm' }
]
2025-10-31 14:50:19 +08:00
},
2025-10-31 19:10:08 +08:00
loadTempTrend(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 temp1Data = []
const temp2Data = []
const temp3Data = []
const temp4Data = []
const temp5Data = []
response.rows.forEach(item => {
const dateStr = this.formatDate(item.insdate)
categories.push(dateStr)
temp1Data.push(Number(item.value4) || 0) // 1#酸槽温度
temp2Data.push(Number(item.value5) || 0) // 2#酸槽温度
temp3Data.push(Number(item.value6) || 0) // 3#酸槽温度
temp4Data.push(Number(item.value7) || 0) // 漂洗温度
temp5Data.push(Number(item.value17) || 0) // 烘干温度
})
this.tempChartData = {
categories: categories.reverse(),
series: [
{ name: '1#酸槽温度', data: temp1Data.reverse() },
{ name: '2#酸槽温度', data: temp2Data.reverse() },
{ name: '3#酸槽温度', data: temp3Data.reverse() },
{ name: '漂洗温度', data: temp4Data.reverse() },
{ name: '烘干温度', data: temp5Data.reverse() }
]
}
}
}).catch(error => {
if (!isSilent) uni.hideLoading()
console.error('加载温度趋势失败:', error)
})
},
getDefineById(id) {
return this.plantStateDefines.find(item => item.id == id)
},
2025-10-31 14:50:19 +08:00
formatDate(dateStr) {
if (!dateStr) return ''
const date = new Date(dateStr)
2025-10-31 19:10:08 +08:00
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)
2025-10-27 13:21:43 +08:00
}
}
2025-10-31 19:10:08 +08:00
};
2025-10-27 13:21:43 +08:00
</script>
<style scoped lang="scss">
2025-10-31 19:10:08 +08:00
/* 页面容器 */
.page-container {
min-height: 100vh;
background: #f5f7fa;
2025-10-27 13:21:43 +08:00
}
2025-10-31 19:10:08 +08:00
/* 标签栏 */
2025-10-29 15:38:20 +08:00
.tab-container {
display: flex;
background: #fff;
2025-10-31 19:10:08 +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 19:10:08 +08:00
padding: 28rpx 0;
2025-10-29 15:38:20 +08:00
position: relative;
2025-10-31 19:10:08 +08:00
2025-10-29 15:38:20 +08:00
.tab-label {
font-size: 28rpx;
2025-10-31 19:10:08 +08:00
color: #606266;
2025-10-29 15:38:20 +08:00
font-weight: 400;
}
2025-10-31 19:10:08 +08:00
2025-10-29 15:38:20 +08:00
&.tab-active {
.tab-label {
2025-10-31 19:10:08 +08:00
color: #0066cc;
font-weight: 500;
2025-10-29 15:38:20 +08:00
}
}
2025-10-31 19:10:08 +08:00
2025-10-29 15:38:20 +08:00
.tab-indicator {
position: absolute;
bottom: 0;
left: 50%;
transform: translateX(-50%);
2025-10-31 19:10:08 +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 19:10:08 +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 19:10:08 +08:00
.refresh-icon {
font-size: 48rpx;
color: #fff;
display: block;
line-height: 1;
&.rotating {
animation: rotate 1s linear infinite;
}
}
2025-10-31 19:10:08 +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 19:10:08 +08:00
align-items: center;
2025-10-29 15:38:20 +08:00
background: #fff;
2025-10-31 19:10:08 +08:00
padding: 24rpx 32rpx;
margin-bottom: 24rpx;
border-radius: 8rpx;
border: 1rpx solid #e4e7ed;
2025-10-27 13:21:43 +08:00
}
2025-10-31 19:10:08 +08:00
.status-item {
2025-10-27 13:21:43 +08:00
flex: 1;
2025-10-31 19:10:08 +08:00
display: flex;
align-items: center;
justify-content: center;
gap: 16rpx;
2025-10-27 13:21:43 +08:00
}
2025-10-31 19:10:08 +08:00
.status-label {
font-size: 26rpx;
color: #909399;
2025-10-27 13:21:43 +08:00
}
2025-10-31 19:10:08 +08:00
.status-value {
2025-10-27 13:21:43 +08:00
font-size: 28rpx;
2025-10-31 19:10:08 +08:00
font-weight: 500;
color: #303133;
&.status-通畅 { color: #67c23a; }
&.status-卡顿 { color: #e6a23c; }
&.status-异常 { color: #f56c6c; }
&.status-time {
color: #909399;
font-size: 24rpx;
}
2025-10-27 13:21:43 +08:00
}
2025-10-31 19:10:08 +08:00
.status-divider {
width: 1rpx;
height: 40rpx;
background: #e4e7ed;
2025-10-27 13:21:43 +08:00
}
2025-10-31 19:10:08 +08:00
/* 区块样式 */
.section {
2025-10-27 13:21:43 +08:00
margin-bottom: 24rpx;
2025-10-31 19:10:08 +08:00
}
2025-10-27 13:21:43 +08:00
2025-10-31 19:10:08 +08:00
.section-title {
font-size: 30rpx;
font-weight: 500;
color: #303133;
margin-bottom: 20rpx;
padding-left: 16rpx;
border-left: 4rpx solid #0066cc;
2025-10-27 13:21:43 +08:00
}
2025-10-31 19:10:08 +08:00
/* 指标卡片 - 3列布局 */
.metrics-grid-3 {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 20rpx;
}
2025-10-31 19:10:08 +08:00
/* 指标卡片 - 2列布局 */
.metrics-grid-2 {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 20rpx;
2025-10-27 13:21:43 +08:00
}
2025-10-31 19:10:08 +08:00
.metric-box {
background: #fff;
border: 1rpx solid #e4e7ed;
border-radius: 8rpx;
padding: 28rpx 20rpx;
text-align: center;
2025-10-27 13:21:43 +08:00
}
2025-10-31 19:10:08 +08:00
.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;
2025-10-27 13:21:43 +08:00
}
2025-12-07 12:54:39 +08:00
/* 未启用容器 */
.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>