feat(linkage): 移动改为任意入口位置选择,仅上卷鞍座触发生产
- 计划新增 position 字段;新增 /plan/{id}/move?position=… 与 /plan/positions/all
- line_service.place_at_position:放到任意位置(位置唯一占用),上卷鞍座单独触发生产联动
- 入口跟踪:新增入口位置图(单一鞍座)显示占位;移动按钮弹出位置选择框
- 计划管理:移动按钮同样弹出位置选择框
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -78,8 +78,8 @@
|
||||
<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'"
|
||||
class="action-link" style="color:var(--accent-green)" @click="moveToProducing(row)">移动</span>
|
||||
<span v-if="row.status === 'online' || row.status === 'ready'"
|
||||
class="action-link" style="color:var(--accent-green)" @click="openMove(row)">移动</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-if="!tableData.length && !loading">
|
||||
@@ -239,11 +239,42 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 移动-位置选择弹窗 -->
|
||||
<div v-if="moveDialog.visible" class="modal-mask" @click.self="moveDialog.visible=false">
|
||||
<div class="modal-box" style="width:480px;">
|
||||
<div class="modal-header">
|
||||
移动计划 — {{ moveDialog.plan && (moveDialog.plan.cold_coil_no || moveDialog.plan.plan_no) }}
|
||||
<span class="modal-close" @click="moveDialog.visible=false">✕</span>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="kv-label" style="margin-bottom:8px;">选择目标位置(只有「上卷鞍座」会触发生产)</div>
|
||||
<div class="pos-pick">
|
||||
<span
|
||||
v-for="pos in positions"
|
||||
:key="pos"
|
||||
:class="['pick-item', { active: moveDialog.target === pos, saddle: pos === '上卷鞍座' }]"
|
||||
@click="moveDialog.target = pos"
|
||||
>{{ pos }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn btn-outline" @click="moveDialog.visible=false">取消</button>
|
||||
<button class="btn btn-primary" :disabled="!moveDialog.target || moving" @click="confirmMove">{{ moving ? '移动中...' : '确定' }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getPlans, createPlan, updatePlan, confirmPlan as apiConfirm, startProducing, getLastPlanTemplate } from '@/api'
|
||||
import { getPlans, createPlan, updatePlan, confirmPlan as apiConfirm, movePlan, getLastPlanTemplate } from '@/api'
|
||||
|
||||
const SADDLE = '上卷鞍座'
|
||||
const POSITIONS = [
|
||||
'1#上卷小车', '2#上卷小车', '1#称重位', '2#称重位',
|
||||
'1#地辊', '2#地辊', '1#倒卷小车', '2#倒卷小车', SADDLE,
|
||||
]
|
||||
|
||||
const STATUS_MAP = {
|
||||
ready: { label: '准备好', badge: 'badge-gray' },
|
||||
@@ -269,6 +300,8 @@ export default {
|
||||
dialogVisible: false, editRow: null,
|
||||
form: this.emptyForm(),
|
||||
selectedRow: null,
|
||||
positions: POSITIONS, moving: false,
|
||||
moveDialog: { visible: false, plan: null, target: '' },
|
||||
}
|
||||
},
|
||||
created() { this.fetchData() },
|
||||
@@ -348,15 +381,21 @@ export default {
|
||||
this.$message.success('已上线')
|
||||
this.fetchData()
|
||||
},
|
||||
async moveToProducing(row) {
|
||||
if (!confirm(`将计划 ${row.cold_coil_no || row.plan_no} 移动到上卷鞍座?`)) return
|
||||
openMove(row) {
|
||||
this.moveDialog = { visible: true, plan: row, target: row.position || '' }
|
||||
},
|
||||
async confirmMove() {
|
||||
const { plan, target } = this.moveDialog
|
||||
if (!target) return
|
||||
this.moving = true
|
||||
try {
|
||||
await startProducing(row.id)
|
||||
this.$message.success('已移动到上卷鞍座')
|
||||
await movePlan(plan.id, target)
|
||||
this.$message.success(target === SADDLE ? '已移动到上卷鞍座' : `已移动到 ${target}`)
|
||||
this.moveDialog.visible = false
|
||||
this.fetchData()
|
||||
} catch (e) {
|
||||
this.$message.error(e?.response?.data?.detail || '操作失败')
|
||||
}
|
||||
} finally { this.moving = false }
|
||||
},
|
||||
async save() {
|
||||
if (!this.form.plan_no) { this.$message.error('计划号不能为空'); return }
|
||||
@@ -405,4 +444,13 @@ export default {
|
||||
.modal-header { display: flex; align-items: center; justify-content: space-between; padding: 12px 16px; background: $bg-panel; border-bottom: 1px solid $border; font-size: 13px; font-weight: 600; color: $sms-highlight; .modal-close { cursor: pointer; color: $text-muted; &:hover { color: $text-primary; } } }
|
||||
.modal-body { padding: 16px; overflow-y: auto; }
|
||||
.modal-footer { padding: 10px 16px; background: $bg-panel; border-top: 1px solid $border; display: flex; justify-content: flex-end; gap: 10px; }
|
||||
.pos-pick { display: grid; grid-template-columns: repeat(2, 1fr); gap: 8px; }
|
||||
.pick-item {
|
||||
padding: 9px 12px; font-size: 12px; text-align: center; border-radius: 6px; cursor: pointer;
|
||||
border: 1px solid $border; color: $text-secondary; background: $bg-card;
|
||||
&:hover { border-color: $sms-teal; color: $sms-teal; }
|
||||
&.active { color: #fff; background: $sms-teal; border-color: $sms-teal; }
|
||||
&.saddle { grid-column: 1 / -1; border-style: dashed; border-color: $accent-yellow; color: $accent-yellow;
|
||||
&.active { color: #fff; background: $accent-yellow; border-style: solid; } }
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user