|
|
|
|
@ -0,0 +1,420 @@
|
|
|
|
|
<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>
|
|
|
|
|
<!-- 修复1:Vue中title绑定语法错误,改为v-bind:title -->
|
|
|
|
|
<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 {getProductionLineData} from "@/api/kanban/Packagingline";
|
|
|
|
|
|
|
|
|
|
export default {
|
|
|
|
|
name: 'ProductionBoard',
|
|
|
|
|
data() {
|
|
|
|
|
return {
|
|
|
|
|
productionLines: [],
|
|
|
|
|
currentTime: '',
|
|
|
|
|
planTotal:0,
|
|
|
|
|
completeTotal: 0,
|
|
|
|
|
completeRate: 0
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
created() {
|
|
|
|
|
this.initProductionData();
|
|
|
|
|
|
|
|
|
|
setInterval(() => {
|
|
|
|
|
this.initProductionData();
|
|
|
|
|
}, 5000);
|
|
|
|
|
|
|
|
|
|
setInterval(() => {
|
|
|
|
|
this.updateCurrentTime();
|
|
|
|
|
}, 1000);
|
|
|
|
|
},
|
|
|
|
|
methods: {
|
|
|
|
|
// 初始化产线数据:添加产量完成率(保留1位小数)
|
|
|
|
|
initProductionData() {
|
|
|
|
|
getProductionLineData().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
|
|
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
},
|
|
|
|
|
// 更新看板当前时间(格式:YYYY-MM-DD HH:mm:ss)
|
|
|
|
|
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>
|
|
|
|
|
/* 全局容器:修复高度100%继承,配合v-scale-screen满屏展示,无溢出 */
|
|
|
|
|
.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 保留原有样式,无需修改 */
|
|
|
|
|
.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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 标题字体放大:28px → 36px(醒目且不挤压) */
|
|
|
|
|
.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);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 时间字体同步放大:16px → 20px(与标题比例协调) */
|
|
|
|
|
.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); /* 6列3行完美适配18条产线 */
|
|
|
|
|
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); /* 5列4行完美适配18条产线 */
|
|
|
|
|
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>
|