feat(plan): 计划详细面板 + 来料重量/外径/分卷重量 + 在线/生产中状态机 + 入口移动 + 上次模板回填
- backend: plan 增加 incoming_weight/incoming_od/split_weights(JSON) 字段及迁移
- backend: GET /plan/last-template 返回最近一条计划的工艺字段用于新增回填(多端共享)
- backend: PATCH /plan/{id}/start 设为 producing,强制单卷在产(其他 producing 回退 online)
- backend: 生成实绩时按卷号自动把对应计划状态置为 produced
- frontend: 新增计划默认状态 online;新增时调用 last-template 自动回填
- frontend: Plan 表格行点击展开 计划详细 面板(按截图布局)
- frontend: Plan 行操作增加「移动」(ready/online → producing)
- frontend: 物料跟踪页加 在线计划队列 + 入口移动按钮,显示当前生产中卷
- frontend: 计划弹窗新增 轧制模式/来料重量/来料外径/1-6#分卷重量
This commit is contained in:
@@ -32,6 +32,41 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 在线计划队列 + 入口移动 -->
|
||||
<div class="card" style="margin-bottom:8px;">
|
||||
<div class="card-header">
|
||||
在线计划(入口队列)
|
||||
<span class="ch-badge">在线 {{ onlinePlans.length }} / 生产中 {{ producingPlan ? 1 : 0 }}</span>
|
||||
<span style="margin-left:auto;font-size:11px;color:var(--text-muted);">点击「移动」把队列卷推到入口并开始生产</span>
|
||||
</div>
|
||||
<div style="padding:8px 14px;">
|
||||
<div v-if="producingPlan" class="producing-row">
|
||||
<span class="badge badge-yellow">生产中</span>
|
||||
<span class="kv-label">冷卷号</span><span class="kv-value">{{ producingPlan.cold_coil_no || producingPlan.plan_no }}</span>
|
||||
<span class="kv-label">钢种</span><span class="kv-value">{{ producingPlan.steel_grade || '—' }}</span>
|
||||
<span class="kv-label">规格</span><span class="kv-value">{{ fmt(producingPlan.product_thickness) }}×{{ fmt(producingPlan.product_width, 0) }}</span>
|
||||
<span class="kv-label">分卷</span><span class="kv-value">{{ producingPlan.split_count || 1 }}</span>
|
||||
</div>
|
||||
<table class="data-table compact" v-if="onlinePlans.length">
|
||||
<thead><tr><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>{{ p.steel_grade || '—' }}</td>
|
||||
<td class="td-num">{{ fmt(p.product_thickness) }}</td>
|
||||
<td class="td-num">{{ fmt(p.product_width, 0) }}</td>
|
||||
<td class="td-num">{{ p.split_count || 1 }}</td>
|
||||
<td class="td-muted">{{ fmtTime(p.plan_date) }}</td>
|
||||
<td>
|
||||
<button class="btn btn-primary btn-sm" :disabled="moving" @click="movePlan(p)">移动 →</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div v-else-if="!producingPlan" class="td-muted" style="text-align:center;padding:10px;font-size:12px;">暂无在线计划</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 产线总图 -->
|
||||
<div class="line-wrap card">
|
||||
<div class="card-header">推拉酸洗线 - 物料跟踪总图</div>
|
||||
@@ -288,6 +323,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getPlans, startProducing } from '@/api'
|
||||
function rnd(base, amp) { return base + (Math.random() - 0.5) * amp }
|
||||
function fix(v, n = 1) { return Number(v).toFixed(n) }
|
||||
|
||||
@@ -368,9 +404,14 @@ export default {
|
||||
dryer: { t1: 145, t2: 168, t3: 152 },
|
||||
|
||||
_timer: null,
|
||||
_plansTimer: null,
|
||||
plans: [],
|
||||
moving: false,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
onlinePlans() { return this.plans.filter(p => p.status === 'online') },
|
||||
producingPlan() { return this.plans.find(p => p.status === 'producing') || null },
|
||||
equipments() {
|
||||
const n = EQUIPMENTS.length
|
||||
const xStart = 50, xEnd = 1850
|
||||
@@ -456,6 +497,31 @@ export default {
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
fmt(v, n = 2) { return v != null && v !== '' ? Number(v).toFixed(n) : '—' },
|
||||
fmtTime(t) { return t ? t.slice(0, 16).replace('T', ' ') : '—' },
|
||||
async loadPlans() {
|
||||
try {
|
||||
const res = await getPlans({ page: 1, page_size: 50 })
|
||||
this.plans = res.data.items || []
|
||||
// 把生产中的卷号同步到产线显示
|
||||
if (this.producingPlan && this.producingPlan.cold_coil_no) {
|
||||
this.current.coil_no = this.producingPlan.cold_coil_no
|
||||
}
|
||||
} catch (e) { /* ignore */ }
|
||||
},
|
||||
async movePlan(p) {
|
||||
if (this.moving) return
|
||||
this.moving = true
|
||||
try {
|
||||
await startProducing(p.id)
|
||||
this.$message && this.$message.success(`已开始生产 ${p.cold_coil_no || p.plan_no}`)
|
||||
await this.loadPlans()
|
||||
} catch (e) {
|
||||
this.$message && this.$message.error('移动失败')
|
||||
} finally {
|
||||
this.moving = false
|
||||
}
|
||||
},
|
||||
// 一行的展示数据:根据设备状态决定卷号/速度/辊缝/辅助列
|
||||
rowOf(eq, i) {
|
||||
const curIdx = this.currentEquipment.idx
|
||||
@@ -561,9 +627,12 @@ export default {
|
||||
created() {
|
||||
this.tick()
|
||||
this._timer = setInterval(this.tick, 2000)
|
||||
this.loadPlans()
|
||||
this._plansTimer = setInterval(this.loadPlans, 10000)
|
||||
},
|
||||
beforeDestroy() {
|
||||
if (this._timer) clearInterval(this._timer)
|
||||
if (this._plansTimer) clearInterval(this._plansTimer)
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@@ -593,6 +662,12 @@ export default {
|
||||
|
||||
.track-scroll { max-height: 640px; overflow-y: auto; }
|
||||
|
||||
.producing-row { display: flex; align-items: center; gap: 10px; padding: 6px 4px 10px; font-size: 12px; border-bottom: 1px dashed $border; margin-bottom: 6px;
|
||||
.kv-label { color: $text-muted; font-size: 11px; margin-left: 6px; }
|
||||
.kv-value { color: $sms-highlight; font-weight: 600; }
|
||||
}
|
||||
.btn-sm { padding: 2px 10px; font-size: 11px; }
|
||||
|
||||
.hd-cnt { font-size: 11px; color: #6b7c8d; margin-left: 8px; font-weight: 400; }
|
||||
|
||||
.sec-body { padding: 10px 14px; background: #161d24; }
|
||||
|
||||
Reference in New Issue
Block a user