|
|
|
|
@ -0,0 +1,398 @@
|
|
|
|
|
<template>
|
|
|
|
|
<!-- 屏幕适配容器:基于1920*1080设计稿,防溢出+完整适配 -->
|
|
|
|
|
<v-scale-screen width="1920" height="1080" :fullScreen="false">
|
|
|
|
|
<div class="app-container home">
|
|
|
|
|
<!-- 头部区域:标题+看板时间 -->
|
|
|
|
|
<div class="head">
|
|
|
|
|
<div class="head-content">
|
|
|
|
|
<div class="title">产线生产看板</div>
|
|
|
|
|
<div class="board-time">{{ currentTime }}</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<!-- 产线统计概览:核心数据总览 -->
|
|
|
|
|
<div class="overview-card">
|
|
|
|
|
<div class="overview-item">
|
|
|
|
|
<span class="overview-label">总产线数</span>
|
|
|
|
|
<span class="overview-value">18</span>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="overview-item">
|
|
|
|
|
<span class="overview-label">生产产线</span>
|
|
|
|
|
<span class="overview-value success">{{ productionLines.length }}</span>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="overview-item">
|
|
|
|
|
<span class="overview-label">计划总产量</span>
|
|
|
|
|
<span class="overview-value">{{ planTotal }}</span>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="overview-item">
|
|
|
|
|
<span class="overview-label">实际完成产量</span>
|
|
|
|
|
<span class="overview-value" :class="{ 'overview-warning': completeRate < 80 }">{{ completeTotal }}</span>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="overview-item">
|
|
|
|
|
<span class="overview-label">完成比例</span>
|
|
|
|
|
<span class="overview-value" :class="{ 'overview-warning': completeRate < 80 }">{{ completeRate }} %</span>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<!-- 产线网格:6列3行适配18条产线,优化内边距避免挤压 -->
|
|
|
|
|
<div class="production-grid" :class="{ 'production-grid-mini': productionLines.length < 13 }">
|
|
|
|
|
<div
|
|
|
|
|
class="production-card"
|
|
|
|
|
v-for="line in productionLines"
|
|
|
|
|
:key="line.lineNo"
|
|
|
|
|
:class="{ 'line-warning': line.completionRate < 80 }"
|
|
|
|
|
>
|
|
|
|
|
<div class="line-number"> {{ line.equipmentName }}</div>
|
|
|
|
|
<div class="product-name" >{{ line.productName }}</div>
|
|
|
|
|
<div class="data-group">
|
|
|
|
|
<div class="data-item">
|
|
|
|
|
<span class="label">计划产量:</span>
|
|
|
|
|
<span class="value plan-value">{{ line.planTotal }}</span>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="data-item">
|
|
|
|
|
<span class="label">当前产量:</span>
|
|
|
|
|
<span class="value current-value" :class="{ 'reach': line.currentOutput >= line.planOutput }">
|
|
|
|
|
{{ line.completTotal || 0 }}
|
|
|
|
|
</span>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="data-item efficiency-item">
|
|
|
|
|
<div class="efficiency-top">
|
|
|
|
|
<span class="label">产量完成率:</span>
|
|
|
|
|
<span class="value efficiency-value" :class="{ 'reach': line.completionRate >= 80 }">
|
|
|
|
|
{{ line.completionRate }}%
|
|
|
|
|
</span>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="efficiency-progress">
|
|
|
|
|
<div
|
|
|
|
|
class="progress-bar"
|
|
|
|
|
:style="{ width: line.completionRate + '%' }"
|
|
|
|
|
:class="{ 'progress-reach': line.completionRate >= 80 }"
|
|
|
|
|
></div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</v-scale-screen>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<script>
|
|
|
|
|
import {getProductionLineDataPublic} from "@/api/kanban/Packagingline";
|
|
|
|
|
|
|
|
|
|
export default {
|
|
|
|
|
name: 'ProductionBoardPublic',
|
|
|
|
|
data() {
|
|
|
|
|
return {
|
|
|
|
|
productionLines: [],
|
|
|
|
|
currentTime: '',
|
|
|
|
|
planTotal:0,
|
|
|
|
|
completeTotal: 0,
|
|
|
|
|
completeRate: 0
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
created() {
|
|
|
|
|
this.initProductionData();
|
|
|
|
|
|
|
|
|
|
setInterval(() => {
|
|
|
|
|
this.initProductionData();
|
|
|
|
|
}, 5000);
|
|
|
|
|
|
|
|
|
|
setInterval(() => {
|
|
|
|
|
this.updateCurrentTime();
|
|
|
|
|
}, 1000);
|
|
|
|
|
},
|
|
|
|
|
methods: {
|
|
|
|
|
initProductionData() {
|
|
|
|
|
getProductionLineDataPublic().then(res=>{
|
|
|
|
|
let planTotal = 0
|
|
|
|
|
let completeTotal = 0
|
|
|
|
|
for (const line of res.data){
|
|
|
|
|
if (line.planTotal){
|
|
|
|
|
planTotal = planTotal + line.planTotal
|
|
|
|
|
}
|
|
|
|
|
if (line.completTotal){
|
|
|
|
|
completeTotal = completeTotal + line.completTotal
|
|
|
|
|
line.completionRate = (line.completTotal / line.planTotal * 100).toFixed(1)
|
|
|
|
|
}else {
|
|
|
|
|
line.completionRate = 0
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let completeRate = ((completeTotal/planTotal) * 100 ).toFixed(1)
|
|
|
|
|
|
|
|
|
|
this.planTotal = planTotal
|
|
|
|
|
this.completeTotal = completeTotal
|
|
|
|
|
this.completeRate = completeRate
|
|
|
|
|
this.productionLines = res.data
|
|
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
},
|
|
|
|
|
updateCurrentTime() {
|
|
|
|
|
const now = new Date()
|
|
|
|
|
this.currentTime = now.toLocaleString('zh-CN', {
|
|
|
|
|
year: 'numeric',
|
|
|
|
|
month: '2-digit',
|
|
|
|
|
day: '2-digit',
|
|
|
|
|
hour: '2-digit',
|
|
|
|
|
minute: '2-digit',
|
|
|
|
|
second: '2-digit',
|
|
|
|
|
hour12: false
|
|
|
|
|
}).replace(/\//g, '-')
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<style scoped>
|
|
|
|
|
.app-container {
|
|
|
|
|
padding: 0;
|
|
|
|
|
width: 100%;
|
|
|
|
|
box-sizing: border-box;
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.home {
|
|
|
|
|
width: 100%;
|
|
|
|
|
background: url("../../../assets/images/bg1.jpg") no-repeat center center;
|
|
|
|
|
background-size: cover;
|
|
|
|
|
background-color: #050711;
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
gap: 16px;
|
|
|
|
|
padding: 16px 24px;
|
|
|
|
|
box-sizing: border-box;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.head {
|
|
|
|
|
width: 100%;
|
|
|
|
|
height: 74px;
|
|
|
|
|
position: relative;
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.head-content {
|
|
|
|
|
width: 100%;
|
|
|
|
|
height: 100%;
|
|
|
|
|
background: url("../../../assets/images/bg-head.png") no-repeat center center;
|
|
|
|
|
display: flex;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
align-items: center;
|
|
|
|
|
box-sizing: border-box;
|
|
|
|
|
position: relative;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.title {
|
|
|
|
|
position: absolute;
|
|
|
|
|
font-size: 36px;
|
|
|
|
|
color: #ffffff;
|
|
|
|
|
font-weight: 500;
|
|
|
|
|
margin: 0;
|
|
|
|
|
top: 0;
|
|
|
|
|
text-shadow: 0 1px 3px rgba(0,0,0,0.3);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.board-time {
|
|
|
|
|
position: absolute;
|
|
|
|
|
right: 20px;
|
|
|
|
|
top: 70%;
|
|
|
|
|
transform: translateY(-50%);
|
|
|
|
|
font-size: 20px;
|
|
|
|
|
color: #e0e0e0;
|
|
|
|
|
margin: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.overview-card {
|
|
|
|
|
width: 100%;
|
|
|
|
|
display: flex;
|
|
|
|
|
justify-content: space-around;
|
|
|
|
|
align-items: center;
|
|
|
|
|
border-radius: 8px;
|
|
|
|
|
padding: 20px 0;
|
|
|
|
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
height: 120px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.overview-item {
|
|
|
|
|
text-align: center;
|
|
|
|
|
padding: 0 16px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.overview-label {
|
|
|
|
|
display: block;
|
|
|
|
|
font-size: 24px;
|
|
|
|
|
color: #95a5a6;
|
|
|
|
|
margin-bottom: 4px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.overview-value {
|
|
|
|
|
font-size: 26px;
|
|
|
|
|
color: #ffffff;
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
line-height: 1.2;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.overview-value.success {
|
|
|
|
|
color: #2ecc71;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.overview-warning{
|
|
|
|
|
color: #f39c12;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.production-grid {
|
|
|
|
|
display: grid;
|
|
|
|
|
grid-template-columns: repeat(6, 1fr);
|
|
|
|
|
grid-template-rows: repeat(3, 1fr);
|
|
|
|
|
gap: 20px;
|
|
|
|
|
flex: 1;
|
|
|
|
|
box-sizing: border-box;
|
|
|
|
|
padding: 0;
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.production-grid-mini{
|
|
|
|
|
grid-template-columns: repeat(4, 1fr);
|
|
|
|
|
grid-template-rows: repeat(4, 1fr);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.production-card {
|
|
|
|
|
background: rgba(14, 28, 56, 0.9);
|
|
|
|
|
border-radius: 6px;
|
|
|
|
|
padding: 10px 12px;
|
|
|
|
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
|
|
|
|
|
border: 1px solid rgba(255, 255, 255, 0.1);
|
|
|
|
|
transition: all 0.3s ease;
|
|
|
|
|
box-sizing: border-box;
|
|
|
|
|
width: 100%;
|
|
|
|
|
height: 100%;
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
gap: 6px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.production-card:hover {
|
|
|
|
|
box-shadow: 0 6px 16px rgba(0, 183, 255, 0.2);
|
|
|
|
|
transform: translateY(-2px);
|
|
|
|
|
border-color: rgba(0, 183, 255, 0.4);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.line-warning {
|
|
|
|
|
border-left: 3px solid #f39c12;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.line-number {
|
|
|
|
|
font-size: 24px;
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
color: #ffffff;
|
|
|
|
|
padding-bottom: 4px;
|
|
|
|
|
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
|
|
|
|
|
line-height: 1.2;
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
margin: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.product-name {
|
|
|
|
|
font-size: 22px;
|
|
|
|
|
color: #a0b4c8;
|
|
|
|
|
word-wrap: break-word !important;
|
|
|
|
|
word-break: break-all !important;
|
|
|
|
|
white-space: normal !important;
|
|
|
|
|
min-height: 24px;
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
cursor: help;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.data-group {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
gap: 8px;
|
|
|
|
|
flex: 1;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
box-sizing: border-box;
|
|
|
|
|
margin: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.data-item {
|
|
|
|
|
display: flex;
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
align-items: center;
|
|
|
|
|
font-size: 20px;
|
|
|
|
|
width: 100%;
|
|
|
|
|
line-height: 1.3;
|
|
|
|
|
margin: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.label {
|
|
|
|
|
color: #8fa4b8;
|
|
|
|
|
font-weight: 400;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.value {
|
|
|
|
|
color: #e0efff;
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.plan-value {
|
|
|
|
|
color: #4fc3f7;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.current-value.reach,
|
|
|
|
|
.completion-rate.reach,
|
|
|
|
|
.efficiency-value.reach {
|
|
|
|
|
color: #2ecc71;
|
|
|
|
|
}
|
|
|
|
|
.current-value,
|
|
|
|
|
.completion-rate {
|
|
|
|
|
color: #ffb74d;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.efficiency-item {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
align-items: flex-start;
|
|
|
|
|
gap: 4px;
|
|
|
|
|
width: 100%;
|
|
|
|
|
margin: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.efficiency-top {
|
|
|
|
|
display: flex;
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
width: 100%;
|
|
|
|
|
align-items: center;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.efficiency-value {
|
|
|
|
|
font-size: 20px !important;
|
|
|
|
|
color: #ffb74d;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.efficiency-progress {
|
|
|
|
|
width: 100%;
|
|
|
|
|
height: 5px;
|
|
|
|
|
background: rgba(255, 255, 255, 0.1);
|
|
|
|
|
border-radius: 2px;
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
box-sizing: border-box;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.progress-bar {
|
|
|
|
|
height: 100%;
|
|
|
|
|
background: #f39c12;
|
|
|
|
|
border-radius: 2px;
|
|
|
|
|
transition: width 0.8s ease-in-out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.progress-bar.progress-reach {
|
|
|
|
|
background: #2ecc71;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
</style>
|