style(entry/material): 鞍座移至8工位下方全宽行+固定高度;物料跟踪仅显示在线计划

- 入口跟踪:8个设备工位(2行4列)固定高度176px,下方单个上卷鞍座全宽行(132px)
  显示生产信息/速度/带头长度/进度,去掉顶部独立鞍座卡片
- 物料跟踪「在线计划(入口队列)」仅显示在线计划(不再混入49条准备好)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-29 14:58:03 +08:00
parent 630395226e
commit 2b7273cdae
2 changed files with 68 additions and 83 deletions

View File

@@ -1,59 +1,19 @@
<template>
<div class="entry-page">
<!-- 上卷鞍座唯一进入生产的工位 -->
<!-- 入口跟踪8 个设备工位 + 下方单个上卷鞍座 -->
<div class="card">
<div class="card-header">
上卷鞍座
<span class="ch-badge">{{ saddle ? statusLabel(saddle.status) : '空闲' }}</span>
入口跟踪
<span class="ch-badge">{{ saddle ? '生产:' + statusLabel(saddle.status) : '鞍座空闲' }}</span>
<span style="margin-left:auto;display:flex;gap:8px;align-items:center;">
<button v-if="saddle && saddle.status !== 'producing'" class="btn btn-primary" @click="commit(saddle)">投入生产</button>
<button class="btn btn-outline" @click="refreshAll">刷新</button>
</span>
</div>
<div class="card-body">
<div v-if="saddle" class="saddle-station">
<div class="saddle-info">
<div class="si-row"><span class="si-k">冷卷号</span><span class="si-v hl">{{ saddle.cold_coil_no || saddle.plan_no }}</span></div>
<div class="si-row"><span class="si-k">热卷号</span><span class="si-v">{{ saddle.hot_coil_no || '—' }}</span></div>
<div class="si-row"><span class="si-k">钢种</span><span class="si-v">{{ saddle.steel_grade || '—' }}</span></div>
<div class="si-row"><span class="si-k">规格(×)</span><span class="si-v">{{ fmt(saddle.product_thickness, 2) }} × {{ fmt(saddle.product_width, 0) }}</span></div>
<div class="si-row"><span class="si-k">来料重量[t]</span><span class="si-v">{{ fmt(saddle.incoming_weight, 3) }}</span></div>
<div class="si-row"><span class="si-k">轧制模式</span><span class="si-v">{{ saddle.rolling_mode || '—' }}</span></div>
</div>
<div class="saddle-run">
<div class="metric-box">
<span class="mb-label">线速度</span>
<span class="mb-value">{{ fmt(saddle.run_speed, 0) }}</span>
<span class="mb-unit">m/min</span>
</div>
<div class="metric-box">
<span class="mb-label">带头长度 / 目标</span>
<span class="mb-value">{{ fmt(saddle.run_length_m, 0) }}</span>
<span class="mb-unit">/ {{ TARGET }} m</span>
</div>
<div class="run-prog">
<div class="rp-head">
<span>生产进度</span>
<span class="rp-pct">{{ progPct(saddle).toFixed(1) }}%</span>
</div>
<div class="prog-bar-wrap" style="height:10px;">
<div class="prog-bar-fill" :style="{ width: progPct(saddle) + '%', background: progColor(saddle) }"></div>
</div>
<div class="rp-tip">带头到达 {{ TARGET }} m 后自动产出实绩并完成下一卷需从队列再次移动到鞍座</div>
</div>
</div>
</div>
<div v-else class="saddle-empty">
上卷鞍座空闲 点击计划移动并选择上卷鞍座即可上卷生产
</div>
</div>
</div>
<!-- 入口跟踪设备位置 -->
<div class="card">
<div class="card-header">入口跟踪 <span class="ch-badge">{{ equipCols.length }} × 2 工位</span></div>
<div class="entry-grid">
<!-- 8 个设备工位 -->
<div v-for="(row, ri) in equipRows" :key="ri" class="entry-row">
<div
v-for="pos in row"
@@ -61,24 +21,46 @@
:class="['pos-cell', { filled: !!occupantOf(pos) }]"
>
<div class="pos-title">{{ pos }}</div>
<template v-if="occupantOf(pos)">
<table class="pos-table">
<tbody>
<tr><td class="k">卷号</td><td class="v">{{ occ(pos,'cold_coil_no') }}</td></tr>
<tr><td class="k">热卷号</td><td class="v">{{ occ(pos,'hot_coil_no') }}</td></tr>
<tr><td class="k">钢种</td><td class="v">{{ occ(pos,'steel_grade') }}</td></tr>
<tr><td class="k">来料[mm]</td><td class="v">{{ occNum(pos,'incoming_thickness',2) }}</td></tr>
<tr><td class="k">成品[mm]</td><td class="v">{{ occNum(pos,'product_thickness',2) }}</td></tr>
<tr><td class="k">成品宽[mm]</td><td class="v">{{ occNum(pos,'product_width',0) }}</td></tr>
<tr><td class="k">来料重[t]</td><td class="v">{{ occNum(pos,'incoming_weight',3) }}</td></tr>
<tr><td class="k">轧制模式</td><td class="v">{{ occ(pos,'rolling_mode') }}</td></tr>
</tbody>
</table>
<div class="pos-act"><span class="action-link" @click="openMove(occupantOf(pos))">移动</span></div>
</template>
<table v-if="occupantOf(pos)" class="pos-table">
<tbody>
<tr><td class="k">冷卷号</td><td class="v">{{ occ(pos,'cold_coil_no') }}</td></tr>
<tr><td class="k">卷号</td><td class="v">{{ occ(pos,'hot_coil_no') }}</td></tr>
<tr><td class="k">钢种</td><td class="v">{{ occ(pos,'steel_grade') }}</td></tr>
<tr><td class="k">来料厚[mm]</td><td class="v">{{ occNum(pos,'incoming_thickness',2) }}</td></tr>
<tr><td class="k">成品[mm]</td><td class="v">{{ occNum(pos,'product_thickness',2) }}</td></tr>
<tr><td class="k">成品[mm]</td><td class="v">{{ occNum(pos,'product_width',0) }}</td></tr>
<tr><td class="k">来料重[t]</td><td class="v">{{ occNum(pos,'incoming_weight',3) }}</td></tr>
<tr><td class="k">轧制模式</td><td class="v">{{ occ(pos,'rolling_mode') }}</td></tr>
</tbody>
</table>
<div v-else class="pos-empty">空闲</div>
<div v-if="occupantOf(pos)" class="pos-act"><span class="action-link" @click="openMove(occupantOf(pos))">移动</span></div>
</div>
</div>
<!-- 下方单个上卷鞍座唯一进入生产的工位 -->
<div :class="['pos-cell', 'saddle-cell', { filled: !!saddle }]">
<div class="pos-title saddle-title">上卷鞍座<span class="st-tag">生产工位</span></div>
<div v-if="saddle" class="saddle-body">
<div class="sb-info">
<span><i>冷卷号</i>{{ saddle.cold_coil_no || saddle.plan_no }}</span>
<span><i>钢种</i>{{ saddle.steel_grade || '—' }}</span>
<span><i>规格</i>{{ fmt(saddle.product_thickness, 2) }}×{{ fmt(saddle.product_width, 0) }}</span>
<span><i>来料重[t]</i>{{ fmt(saddle.incoming_weight, 3) }}</span>
<span><i>轧制模式</i>{{ saddle.rolling_mode || '—' }}</span>
<span><i>状态</i><b :class="badgeOf(saddle.status)" style="padding:0 6px;border-radius:2px;">{{ statusLabel(saddle.status) }}</b></span>
</div>
<div class="sb-run">
<div class="sb-metric"><span class="m-v">{{ fmt(saddle.run_speed, 0) }}</span><span class="m-u">m/min</span></div>
<div class="sb-metric"><span class="m-v">{{ fmt(saddle.run_length_m, 0) }}</span><span class="m-u">/ {{ TARGET }} m</span></div>
<div class="sb-prog">
<div class="prog-bar-wrap" style="height:9px;"><div class="prog-bar-fill" :style="{ width: progPct(saddle) + '%', background: progColor(saddle) }"></div></div>
<span class="sb-pct">{{ progPct(saddle).toFixed(1) }}%</span>
</div>
</div>
</div>
<div v-else class="pos-empty">空闲 移动计划到上卷鞍座开始生产</div>
</div>
</div>
</div>
@@ -162,7 +144,7 @@ export default {
data() {
return {
plans: [], saddle: null, TARGET, SADDLE,
positions: POSITIONS, equipRows: EQUIP_ROWS, equipCols: EQUIP_ROWS[0],
positions: POSITIONS, equipRows: EQUIP_ROWS,
timer: null, fastTimer: null, moving: false,
moveDialog: { visible: false, plan: null, target: '' },
}
@@ -236,29 +218,13 @@ export default {
.entry-page { display: flex; flex-direction: column; gap: 12px; }
// ── 鞍座工位 ──
.saddle-station { display: grid; grid-template-columns: 1fr 2fr; gap: 16px; }
.saddle-info {
display: grid; grid-template-columns: 1fr 1fr; gap: 6px 18px; align-content: start;
border-right: 1px solid $border; padding-right: 16px;
}
.si-row { display: flex; justify-content: space-between; gap: 10px; font-size: 12px; padding: 3px 0; }
.si-k { color: $text-muted; }
.si-v { color: $text-primary; font-family: $font-mono; font-weight: 600; &.hl { color: $sms-teal; } }
.saddle-run { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; align-content: start; }
.saddle-run .metric-box { min-width: 0; }
.run-prog { grid-column: 1 / -1; }
.rp-head { display: flex; justify-content: space-between; font-size: 12px; color: $text-secondary; margin-bottom: 5px; }
.rp-pct { font-family: $font-mono; font-weight: 700; color: $sms-teal; }
.rp-tip { font-size: 11px; color: $text-muted; margin-top: 6px; }
.saddle-empty { text-align: center; padding: 30px; color: $text-muted; font-size: 13px; }
// ── 入口设备网格恢复原布局2 行)──
// ── 入口设备网格 ──
.entry-grid { padding: 8px; display: flex; flex-direction: column; gap: 6px; overflow-x: auto; }
.entry-row { display: grid; grid-template-columns: repeat(4, minmax(140px, 1fr)); gap: 6px; }
.pos-cell {
background: $bg-panel; border: 1px solid $border; border-radius: 4px; padding: 4px 8px 5px;
display: flex; flex-direction: column;
height: 176px; overflow: hidden; display: flex; flex-direction: column;
&.filled { border-color: $sms-teal; background: rgba($sms-teal, .04); }
}
.pos-title {
@@ -266,13 +232,32 @@ export default {
padding: 2px 0 4px; border-bottom: 1px dashed $border; margin-bottom: 3px; letter-spacing: .3px;
}
.pos-table {
width: 100%; border-collapse: collapse; font-size: 10.5px; line-height: 1.35;
width: 100%; border-collapse: collapse; font-size: 10.5px; line-height: 1.4;
td { padding: 0 0 1px; vertical-align: baseline; white-space: nowrap; }
td.k { color: $text-muted; text-align: left; font-size: 10px; padding-right: 6px; }
td.v { color: $sms-teal; text-align: right; font-family: $font-mono; font-weight: 600; width: 100%; }
}
.pos-act { text-align: right; padding-top: 3px; margin-top: 2px; border-top: 1px dashed $border; }
.pos-empty { flex: 1; min-height: 56px; display: flex; align-items: center; justify-content: center; color: $text-muted; font-size: 11px; }
.pos-act { margin-top: auto; text-align: right; padding-top: 3px; border-top: 1px dashed $border; }
.pos-empty { flex: 1; display: flex; align-items: center; justify-content: center; color: $text-muted; font-size: 11px; }
// ── 下方单个上卷鞍座(全宽) ──
.saddle-cell {
height: 132px;
&.filled { border-color: $accent-yellow; background: rgba($accent-yellow, .05); }
}
.saddle-title { border-bottom-color: rgba($accent-yellow, .4); display: flex; align-items: center; justify-content: center; gap: 8px;
.st-tag { font-size: 9px; color: $accent-yellow; border: 1px solid rgba($accent-yellow, .5); border-radius: 2px; padding: 0 5px; font-weight: 600; } }
.saddle-body { flex: 1; display: grid; grid-template-columns: 3fr 2fr; gap: 16px; align-items: center; }
.sb-info { display: grid; grid-template-columns: repeat(3, 1fr); gap: 6px 18px;
span { font-size: 12px; color: $text-primary; font-family: $font-mono; font-weight: 600; display: flex; gap: 6px;
i { color: $text-muted; font-style: normal; font-family: $font-main; font-weight: 400; min-width: 56px; } }
}
.sb-run { display: flex; align-items: center; gap: 16px; border-left: 1px solid $border; padding-left: 16px; }
.sb-metric { display: flex; flex-direction: column; line-height: 1.1;
.m-v { font-size: 22px; font-family: $font-mono; font-weight: 700; color: $sms-teal; }
.m-u { font-size: 10px; color: $text-muted; } }
.sb-prog { flex: 1; display: flex; flex-direction: column; gap: 5px;
.sb-pct { font-size: 11px; font-family: $font-mono; font-weight: 700; color: $sms-teal; text-align: right; } }
.action-link { color: $accent-green; cursor: pointer; font-size: 12px; &:hover { text-decoration: underline; } }

View File

@@ -428,7 +428,7 @@ export default {
}
},
computed: {
onlinePlans() { return this.plans.filter(p => p.status === 'online' || p.status === 'ready') },
onlinePlans() { return this.plans.filter(p => p.status === 'online') },
producingPlan() { return this.plans.find(p => p.status === 'producing') || null },
equipments() {
const n = EQUIPMENTS.length