feat(linkage): 鞍座改为预备生产位 + 在线改人工触发 + 计划删除
- 上卷鞍座=预备生产位(不生产);投入生产后离开鞍座、进入生产中并转入物料跟踪,鞍座随即空出
- 在线状态改为人工触发:移动到入口端才变在线,引擎不再自动上线(ensure_online 置空)
- 单卷在产:投入生产时若已有在产卷则拒绝
- 物料跟踪显示在产卷实时进度(客户端外推带头长度/进度条)
- 入口跟踪鞍座卡片改为预备生产展示(去掉进度)
- 计划管理新增删除按钮 + DELETE /plan/{id}(生产中不可删)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -19,6 +19,7 @@ export const updateProductionRecord = (id, data) => request.put(`/production/${i
|
||||
export const getPlans = params => request.get('/plan/', { params })
|
||||
export const createPlan = data => request.post('/plan/', data)
|
||||
export const updatePlan = (id, data) => request.put(`/plan/${id}`, data)
|
||||
export const deletePlan = id => request.delete(`/plan/${id}`)
|
||||
export const confirmPlan = id => request.patch(`/plan/${id}/confirm`)
|
||||
export const startProducing = id => request.patch(`/plan/${id}/start`) // 移动到上卷鞍座(兼容)
|
||||
export const moveToSaddle = id => request.patch(`/plan/${id}/start`)
|
||||
|
||||
@@ -5,9 +5,9 @@
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
入口跟踪
|
||||
<span class="ch-badge">{{ saddle ? '生产:' + statusLabel(saddle.status) : '鞍座空闲' }}</span>
|
||||
<span class="ch-badge">{{ saddle ? '上卷鞍座:预备生产' : '上卷鞍座:空闲' }}</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 v-if="saddle" class="btn btn-primary" @click="commit(saddle)">投入生产</button>
|
||||
<button class="btn btn-outline" @click="refreshAll">刷新</button>
|
||||
</span>
|
||||
</div>
|
||||
@@ -38,28 +38,24 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 下方:单个上卷鞍座(唯一进入生产的工位) -->
|
||||
<!-- 下方:单个上卷鞍座(预备生产位;投入生产后离开鞍座进入物料跟踪) -->
|
||||
<div :class="['pos-cell', 'saddle-cell', { filled: !!saddle }]">
|
||||
<div class="pos-title saddle-title">上卷鞍座<span class="st-tag">生产工位</span></div>
|
||||
<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.hot_coil_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 class="sb-stage">
|
||||
<span class="badge badge-yellow">预备生产</span>
|
||||
<span class="sb-hint">点击右上「投入生产」→ 进入生产中并转入物料跟踪</span>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="pos-empty">空闲 — 移动计划到「上卷鞍座」开始生产</div>
|
||||
<div v-else class="pos-empty">空闲 — 把在线计划移动到「上卷鞍座」预备生产</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -203,8 +199,8 @@ export default {
|
||||
async commit(p) {
|
||||
try {
|
||||
await commitProducing(p.id)
|
||||
this.$message.success('已投入生产')
|
||||
this.fetchSaddle()
|
||||
this.$message.success('已投入生产,转入物料跟踪')
|
||||
this.refreshAll()
|
||||
} catch (e) {
|
||||
this.$message.error(e?.response?.data?.detail || '操作失败')
|
||||
}
|
||||
@@ -252,12 +248,9 @@ export default {
|
||||
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; } }
|
||||
.sb-stage { display: flex; flex-direction: column; align-items: center; gap: 8px; justify-content: center;
|
||||
border-left: 1px solid $border; padding-left: 16px;
|
||||
.sb-hint { font-size: 11px; color: $text-muted; text-align: center; } }
|
||||
|
||||
.action-link { color: $accent-green; cursor: pointer; font-size: 12px; &:hover { text-decoration: underline; } }
|
||||
|
||||
|
||||
@@ -45,7 +45,12 @@
|
||||
<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>
|
||||
<span class="kv-label">线速度</span><span class="kv-value">{{ fmt(producingPlan.run_speed, 0) }}<span class="kv-unit"> m/min</span></span>
|
||||
<span class="kv-label">带头</span><span class="kv-value">{{ fmt(prodLength, 0) }}<span class="kv-unit"> / 2000 m</span></span>
|
||||
<div class="prog-bar-wrap" style="flex:1;min-width:120px;height:8px;">
|
||||
<div class="prog-bar-fill" :style="{ width: prodPct + '%', background: prodPct >= 100 ? 'var(--accent-green)' : 'var(--sms-teal)' }"></div>
|
||||
</div>
|
||||
<span class="kv-value">{{ prodPct.toFixed(0) }}%</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>
|
||||
@@ -380,6 +385,8 @@ export default {
|
||||
data() {
|
||||
return {
|
||||
l1Online: false,
|
||||
now: Date.now(),
|
||||
prodBase: null, // { id, len, at, speed } 用于客户端平滑外推生产进度
|
||||
current: { coil_no: '26053552', speed: 95.0 },
|
||||
prev_coil_no: '26053551',
|
||||
weld: { position: 0.08 },
|
||||
@@ -428,8 +435,18 @@ export default {
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
onlinePlans() { return this.plans.filter(p => p.status === 'online') },
|
||||
onlinePlans() { return this.plans.filter(p => p.status === 'online' && p.on_saddle !== 1) },
|
||||
producingPlan() { return this.plans.find(p => p.status === 'producing') || null },
|
||||
prodLength() {
|
||||
const p = this.producingPlan
|
||||
if (!p) return 0
|
||||
const b = this.prodBase
|
||||
if (b && b.id === p.id) {
|
||||
return Math.min(2000, b.len + b.speed / 60 * Math.max(0, (this.now - b.at) / 1000))
|
||||
}
|
||||
return p.run_length_m || 0
|
||||
},
|
||||
prodPct() { return this.producingPlan ? Math.max(0, Math.min(100, this.prodLength / 2000 * 100)) : 0 },
|
||||
equipments() {
|
||||
const n = EQUIPMENTS.length
|
||||
const xStart = 50, xEnd = 1850
|
||||
@@ -540,10 +557,10 @@ export default {
|
||||
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
|
||||
}
|
||||
// 把生产中的卷号同步到产线显示 + 记录进度外推基准
|
||||
const pp = this.producingPlan
|
||||
if (pp && pp.cold_coil_no) this.current.coil_no = pp.cold_coil_no
|
||||
this.prodBase = pp ? { id: pp.id, len: pp.run_length_m || 0, at: Date.now(), speed: pp.run_speed || 0 } : null
|
||||
} catch (e) { /* ignore */ }
|
||||
},
|
||||
async movePlan(p) {
|
||||
@@ -623,6 +640,7 @@ export default {
|
||||
return { coil, gap, speed, aux }
|
||||
},
|
||||
tick() {
|
||||
this.now = Date.now()
|
||||
this.weld.position = (this.weld.position + 0.012) % 1
|
||||
// 新一卷开始时滚动卷号
|
||||
if (this.weld.position < 0.012) {
|
||||
|
||||
@@ -78,8 +78,10 @@
|
||||
<td><span :class="['badge', statusBadge(row.status)]">{{ statusLabel(row.status) }}</span></td>
|
||||
<td @click.stop>
|
||||
<span class="action-link" @click="openDialog(row)">编辑</span>
|
||||
<span v-if="row.status === 'online' || row.status === 'ready'"
|
||||
<span v-if="row.status === 'ready' || row.status === 'online'"
|
||||
class="action-link" style="color:var(--accent-green)" @click="openMove(row)">移动</span>
|
||||
<span v-if="row.status !== 'producing'"
|
||||
class="action-link" style="color:var(--accent-red)" @click="removeRow(row)">删除</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-if="!tableData.length && !loading">
|
||||
@@ -268,7 +270,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getPlans, createPlan, updatePlan, confirmPlan as apiConfirm, movePlan, getLastPlanTemplate } from '@/api'
|
||||
import { getPlans, createPlan, updatePlan, deletePlan, confirmPlan as apiConfirm, movePlan, getLastPlanTemplate } from '@/api'
|
||||
|
||||
const SADDLE = '上卷鞍座'
|
||||
const POSITIONS = [
|
||||
@@ -381,6 +383,17 @@ export default {
|
||||
this.$message.success('已上线')
|
||||
this.fetchData()
|
||||
},
|
||||
async removeRow(row) {
|
||||
if (!confirm(`确认删除计划 ${row.cold_coil_no || row.plan_no}?`)) return
|
||||
try {
|
||||
await deletePlan(row.id)
|
||||
this.$message.success('已删除')
|
||||
if (this.selectedRow && this.selectedRow.id === row.id) this.selectedRow = null
|
||||
this.fetchData()
|
||||
} catch (e) {
|
||||
this.$message.error(e?.response?.data?.detail || '删除失败')
|
||||
}
|
||||
},
|
||||
openMove(row) {
|
||||
this.moveDialog = { visible: true, plan: row, target: row.position || '' }
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user