feat(entry-tracking): 新增入口跟踪独立页面(11×2 位置网格)
- 新增 /entry-tracking 路由与导航入口 - 11 列 × 2 行位置卡(上卷小车/称重位/地辊/上卷鞍座/倒卷小车) - 1#地辊 绑生产中卷,2#地辊 绑在线队首 - 底部入口队列表保留「移动」按钮(计划→生产中)
This commit is contained in:
@@ -22,6 +22,12 @@ const routes = [
|
||||
component: () => import('@/views/Material.vue'),
|
||||
meta: { title: '物料跟踪', icon: 'el-icon-box', requiresAuth: true }
|
||||
},
|
||||
{
|
||||
path: 'entry-tracking',
|
||||
name: 'EntryTracking',
|
||||
component: () => import('@/views/EntryTracking.vue'),
|
||||
meta: { title: '入口跟踪', icon: 'el-icon-position', requiresAuth: true }
|
||||
},
|
||||
{
|
||||
path: 'production',
|
||||
name: 'Production',
|
||||
|
||||
207
frontend/src/views/EntryTracking.vue
Normal file
207
frontend/src/views/EntryTracking.vue
Normal file
@@ -0,0 +1,207 @@
|
||||
<template>
|
||||
<div class="entry-page">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
入口跟踪
|
||||
<span class="ch-badge">在线 {{ onlinePlans.length }} / 生产中 {{ producingPlan ? 1 : 0 }}</span>
|
||||
<span style="margin-left:auto;display:flex;gap:8px;align-items:center;">
|
||||
<button class="btn btn-outline" @click="fetchPlans">刷新</button>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="entry-grid">
|
||||
<div v-for="row in rows" :key="row.key" class="entry-row">
|
||||
<div
|
||||
v-for="pos in row.positions"
|
||||
:key="pos.name"
|
||||
:class="['pos-cell', { filled: !!pos.plan, highlight: pos.highlight }]"
|
||||
>
|
||||
<div class="pos-title">{{ pos.name }}</div>
|
||||
<table class="pos-table">
|
||||
<tbody>
|
||||
<tr><td class="k">冷卷号</td><td class="v">{{ pos.plan ? (pos.plan.cold_coil_no || '—') : '' }}</td></tr>
|
||||
<tr><td class="k">热卷号</td><td class="v">{{ pos.plan ? (pos.plan.hot_coil_no || '—') : '' }}</td></tr>
|
||||
<tr><td class="k">钢种</td><td class="v">{{ pos.plan ? (pos.plan.steel_grade || '—') : '' }}</td></tr>
|
||||
<tr><td class="k">来料厚度[mm]</td><td class="v">{{ pos.plan ? fmt(pos.plan.incoming_thickness, 2) : '' }}</td></tr>
|
||||
<tr><td class="k">成品厚度[mm]</td><td class="v">{{ pos.plan ? fmt(pos.plan.product_thickness, 2) : '' }}</td></tr>
|
||||
<tr><td class="k">厚差范围[mm]</td><td class="v">{{ pos.plan ? devRange(pos.plan) : '' }}</td></tr>
|
||||
<tr><td class="k">来料宽度[mm]</td><td class="v">{{ pos.plan ? fmt(pos.plan.incoming_width, 0) : '' }}</td></tr>
|
||||
<tr><td class="k">成品宽度[mm]</td><td class="v">{{ pos.plan ? fmt(pos.plan.product_width, 0) : '' }}</td></tr>
|
||||
<tr><td class="k">来料重量[t]</td><td class="v">{{ pos.plan ? fmt(pos.plan.incoming_weight, 4) : '' }}</td></tr>
|
||||
<tr><td class="k">轧制模式</td><td class="v">{{ pos.plan ? (pos.plan.rolling_mode || '—') : '' }}</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
入口队列(点击移动可将计划推到入口并开始生产)
|
||||
<span class="ch-badge">{{ onlinePlans.length }} 条</span>
|
||||
</div>
|
||||
<div class="table-scroll">
|
||||
<table class="data-table compact">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>冷卷号</th><th>热卷号</th><th>钢种</th>
|
||||
<th>规格(厚×宽)</th><th>来料重量</th><th>轧制模式</th><th>状态</th><th>操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="p in onlinePlans" :key="p.id">
|
||||
<td class="td-num">{{ p.cold_coil_no || p.plan_no }}</td>
|
||||
<td class="td-num">{{ p.hot_coil_no || '—' }}</td>
|
||||
<td>{{ p.steel_grade || '—' }}</td>
|
||||
<td class="td-num">{{ fmt(p.product_thickness, 2) }} × {{ fmt(p.product_width, 0) }}</td>
|
||||
<td class="td-num">{{ fmt(p.incoming_weight, 2) }}</td>
|
||||
<td>{{ p.rolling_mode || '—' }}</td>
|
||||
<td><span :class="['badge', p.status === 'online' ? 'badge-green' : 'badge-gray']">{{ p.status === 'online' ? '在线' : '准备好' }}</span></td>
|
||||
<td><span class="action-link" @click="moveToProducing(p)">移动</span></td>
|
||||
</tr>
|
||||
<tr v-if="!onlinePlans.length">
|
||||
<td colspan="8" class="td-muted" style="text-align:center;padding:14px;">暂无在线/准备好的计划</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getPlans, startProducing } from '@/api'
|
||||
|
||||
const ROW1 = ['1#上卷小车','1#称重位','1#地辊','13#上卷鞍座','11#上卷鞍座','9#上卷鞍座','7#上卷鞍座','1#倒卷小车','5#上卷鞍座','3#上卷鞍座','1#上卷鞍座']
|
||||
const ROW2 = ['2#上卷小车','2#称重位','2#地辊','14#上卷鞍座','12#上卷鞍座','10#上卷鞍座','8#上卷鞍座','2#倒卷小车','6#上卷鞍座','4#上卷鞍座','2#上卷鞍座']
|
||||
|
||||
export default {
|
||||
name: 'EntryTracking',
|
||||
data() {
|
||||
return { plans: [], timer: null }
|
||||
},
|
||||
computed: {
|
||||
onlinePlans() { return this.plans.filter(p => p.status === 'online' || p.status === 'ready') },
|
||||
producingPlan() { return this.plans.find(p => p.status === 'producing') || null },
|
||||
rows() {
|
||||
// 主辊位填充:1#地辊 = 生产中卷; 2#地辊 = 在线队首
|
||||
const onl = this.onlinePlans
|
||||
const map1 = { '1#地辊': this.producingPlan }
|
||||
const map2 = { '2#地辊': onl[0] || null }
|
||||
const build = (names, map) => names.map(n => ({
|
||||
name: n,
|
||||
plan: map[n] || null,
|
||||
highlight: n === '1#地辊' || n === '2#地辊',
|
||||
}))
|
||||
return [
|
||||
{ key: 'r1', positions: build(ROW1, map1) },
|
||||
{ key: 'r2', positions: build(ROW2, map2) },
|
||||
]
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.fetchPlans()
|
||||
this.timer = setInterval(this.fetchPlans, 5000)
|
||||
},
|
||||
beforeDestroy() { clearInterval(this.timer) },
|
||||
methods: {
|
||||
async fetchPlans() {
|
||||
try {
|
||||
const res = await getPlans({ page: 1, page_size: 50 })
|
||||
this.plans = res.data.items || []
|
||||
} catch (e) { /* ignore */ }
|
||||
},
|
||||
fmt(v, n = 2) { return v != null && v !== '' ? Number(v).toFixed(n) : '—' },
|
||||
devRange(p) {
|
||||
const u = p.deviation_upper, l = p.deviation_lower
|
||||
if (u == null && l == null) return '—'
|
||||
return `${l != null ? Number(l).toFixed(3) : '—'} / ${u != null ? Number(u).toFixed(3) : '—'}`
|
||||
},
|
||||
async moveToProducing(p) {
|
||||
if (!confirm(`将计划 ${p.cold_coil_no || p.plan_no} 移动到入口并开始生产?`)) return
|
||||
try {
|
||||
await startProducing(p.id)
|
||||
this.$message.success('已开始生产')
|
||||
this.fetchPlans()
|
||||
} catch (e) {
|
||||
this.$message.error(e?.response?.data?.detail || '操作失败')
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '@/assets/styles/variables';
|
||||
|
||||
.entry-page { display: flex; flex-direction: column; gap: 12px; }
|
||||
|
||||
.entry-grid {
|
||||
padding: 10px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.entry-row {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(11, minmax(118px, 1fr));
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.pos-cell {
|
||||
background: $bg-panel;
|
||||
border: 1px solid $border;
|
||||
border-radius: 3px;
|
||||
padding: 4px 5px 6px;
|
||||
min-height: 220px;
|
||||
|
||||
&.filled { border-color: $sms-highlight; background: rgba($sms-highlight, .04); }
|
||||
&.highlight { box-shadow: 0 0 0 1px rgba($sms-highlight, .35) inset; }
|
||||
}
|
||||
|
||||
.pos-title {
|
||||
text-align: center;
|
||||
font-size: 11px;
|
||||
font-weight: 700;
|
||||
color: $text-primary;
|
||||
padding: 3px 0 5px;
|
||||
border-bottom: 1px dashed $border;
|
||||
margin-bottom: 4px;
|
||||
letter-spacing: .3px;
|
||||
}
|
||||
|
||||
.pos-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
font-size: 10.5px;
|
||||
line-height: 1.45;
|
||||
|
||||
td {
|
||||
padding: 1px 2px;
|
||||
vertical-align: top;
|
||||
white-space: nowrap;
|
||||
}
|
||||
td.k {
|
||||
color: $text-muted;
|
||||
text-align: right;
|
||||
width: 56%;
|
||||
font-size: 10px;
|
||||
}
|
||||
td.v {
|
||||
color: $sms-highlight;
|
||||
text-align: right;
|
||||
font-family: $font-mono;
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
|
||||
.action-link {
|
||||
color: $accent-green;
|
||||
cursor: pointer;
|
||||
font-size: 12px;
|
||||
&:hover { text-decoration: underline; }
|
||||
}
|
||||
</style>
|
||||
@@ -71,6 +71,7 @@ const IC = {
|
||||
|
||||
const MENU = [
|
||||
{ path: '/plan', title: '计划管理', icon: IC.plan },
|
||||
{ path: '/entry-tracking',title: '入口跟踪', icon: IC.material },
|
||||
{ path: '/material', title: '物料跟踪', icon: IC.material },
|
||||
{ path: '/production', title: '实绩管理', icon: IC.production },
|
||||
{ path: '/process-model', title: '工艺段模型', icon: IC.process },
|
||||
|
||||
Reference in New Issue
Block a user