feat(追踪系统): 添加手动设置起始钢卷功能

- 后端添加设置起始钢卷API和钢卷查询API
- 前端实现起始钢卷选择界面和状态管理
- 优化追踪逻辑自动加载后续钢卷
- 添加本地存储保持起始钢卷设置
This commit is contained in:
2026-04-30 11:45:42 +08:00
parent 0b07c2a2f1
commit 3ef502d737
3 changed files with 430 additions and 13 deletions

View File

@@ -1,11 +1,30 @@
<template>
<div>
<!-- 起始钢卷信息显示 -->
<div class="panel" style="background:#f8f9fa;border:1px solid #e9ecef">
<h3 style="margin:0 0 10px 0;color:#495057">起始钢卷信息</h3>
<div v-if="startCoilInfo.coilid" style="display:flex;align-items:center;gap:20px">
<div style="display:flex;align-items:center;gap:10px">
<span style="color:#dc3545;font-weight:bold;font-size:16px">起始钢卷:</span>
<span style="color:#dc3545;font-weight:bold;font-size:16px">{{ startCoilInfo.coilid }}</span>
<el-tag type="danger" size="small">顺序号: {{ startCoilInfo.sequencenb }}</el-tag>
</div>
<div style="color:#6c757d;font-size:14px">
<span>设置时间: {{ startCoilInfo.set_time || '未知' }}</span>
</div>
<div style="flex:1"></div>
<el-button size="small" type="warning" icon="el-icon-location" @click="openStartCoilDialog">重新设置</el-button>
</div>
<div v-else style="color:#6c757d;text-align:center;padding:20px">
<span>尚未设置起始钢卷</span>
<el-button size="small" type="primary" style="margin-left:10px" @click="openStartCoilDialog">设置起始钢卷</el-button>
</div>
</div>
<div class="panel" style="display:flex;align-items:center;gap:10px;flex-wrap:wrap">
<el-button size="small" @click="loadCoils">刷新</el-button>
<el-button size="small" type="primary" icon="el-icon-plus" @click="openAdd">新增钢卷</el-button>
<el-button size="small" type="danger" icon="el-icon-delete" @click="clearAll">清空全部</el-button>
<span style="flex:1"></span>
<el-button size="small" type="warning" @click="triggerSignal1">模拟信号1(入口)</el-button>
<el-button size="small" type="success" @click="triggerSignal2">模拟信号2(焊接完成)</el-button>
<span style="font-size:12px;color:#666">1.出口 2.酸洗 3.入口活套 4.开卷机 提示:(最小的先进产线)</span>
</div>
@@ -48,11 +67,80 @@
<el-button type="primary" size="small" @click="doSave" :loading="saving">保存</el-button>
</div>
</el-dialog>
<!-- 起始钢卷选择对话框 -->
<el-dialog title="选择起始钢卷" :visible.sync="startCoilDialogVisible" width="1400px">
<div style="margin-bottom:15px">
<el-row :gutter="10">
<el-col :span="8">
<el-input
v-model="searchCoilid"
placeholder="搜索钢卷号"
clearable
size="small"
@clear="searchAvailableCoils"
@keyup.enter.native="searchAvailableCoils"
>
<el-button slot="append" icon="el-icon-search" @click="searchAvailableCoils"></el-button>
</el-input>
</el-col>
<el-col :span="6">
<el-input
v-model="searchSequencenb"
placeholder="顺序号筛选"
clearable
size="small"
@clear="searchAvailableCoils"
@keyup.enter.native="searchAvailableCoils"
>
<el-button slot="append" icon="el-icon-search" @click="searchAvailableCoils"></el-button>
</el-input>
</el-col>
<el-col :span="4">
<el-button size="small" style="width:100%" @click="searchAvailableCoils">刷新</el-button>
</el-col>
</el-row>
</div>
<el-table :data="availableCoils" stripe border size="small" v-loading="searchLoading" element-loading-text="搜索中..." style="width:100%" height="400" :empty-text="'暂无数据'">
<el-table-column prop="sequencenb" label="顺序号" align="center" />
<el-table-column prop="coilid" label="钢卷号" />
<el-table-column prop="rollprogramnb" label="计划号" align="center" />
<el-table-column prop="steel_grade" label="钢种" width="150" />
<el-table-column prop="entry_coil_thickness" label="厚度(mm)" width="100" align="center" />
<el-table-column prop="entry_coil_width" label="宽度(mm)" width="100" align="center" />
<el-table-column prop="entry_coil_weight" label="重量(kg)" width="110" align="center" />
<el-table-column label="状态" width="90" align="center">
<template slot-scope="{row}">
<el-tag size="mini" type="success">计划中</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" width="110" align="center">
<template slot-scope="{row}">
<el-button type="primary" size="mini" @click="selectStartCoil(row)">选择</el-button>
</template>
</el-table-column>
</el-table>
<div style="margin-top:15px;text-align:center">
<el-pagination
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="searchParams.page"
:page-sizes="[10, 20, 50, 100]"
:page-size="searchParams.page_size"
layout="total, sizes, prev, pager, next, jumper"
:total="searchTotal">
</el-pagination>
</div>
<div slot="footer" class="dialog-footer">
<el-button @click="startCoilDialogVisible = false">取消</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import { trackApi } from '../api/index'
import axios from 'axios'
export default {
name: 'TrackCoil',
@@ -74,6 +162,22 @@ export default {
coilid: [
{ required: true, message: '钢卷号不能为空', trigger: 'blur' }
]
},
// 起始钢卷选择相关
startCoilDialogVisible: false,
availableCoils: [],
searchLoading: false,
searchCoilid: '',
searchSequencenb: '',
searchParams: {
page: 1,
page_size: 20
},
searchTotal: 0,
startCoilInfo: {
coilid: '',
sequencenb: 0,
set_time: ''
}
}
},
@@ -83,7 +187,18 @@ export default {
}
},
created() {
this.loadStartCoilFromStorage()
this.loadCoils()
this.loadTrackStatus()
// 定时刷新状态
this.statusTimer = setInterval(() => {
this.loadTrackStatus()
}, 2000)
},
beforeDestroy() {
if (this.statusTimer) {
clearInterval(this.statusTimer)
}
},
methods: {
async loadCoils() {
@@ -220,11 +335,152 @@ export default {
async triggerSignal2() {
try {
await trackApi.simulateSignal2()
this.$message.success('信号2已触发 - 已更新追踪表')
this.$message.success('信号2已触发 - 已读取钢卷并更新追踪表')
this.loadCoils()
} catch (e) {
this.$message.error(e.message)
}
},
// 起始钢卷持久化存储方法
loadStartCoilFromStorage() {
try {
const saved = localStorage.getItem('startCoilInfo')
if (saved) {
this.startCoilInfo = JSON.parse(saved)
}
} catch (e) {
console.error('Failed to load start coil from storage:', e)
}
},
saveStartCoilToStorage() {
try {
localStorage.setItem('startCoilInfo', JSON.stringify(this.startCoilInfo))
} catch (e) {
console.error('Failed to save start coil to storage:', e)
}
},
clearStartCoilFromStorage() {
try {
localStorage.removeItem('startCoilInfo')
} catch (e) {
console.error('Failed to clear start coil from storage:', e)
}
},
// 起始钢卷选择相关方法
async loadTrackStatus() {
try {
const res = await axios.get('/api/opc/status')
const tracking_info = res.data.tracking_info || {}
const first_coilid = tracking_info.first_coilid || ''
// 如果localStorage中有起始钢卷信息优先使用localStorage的
// 不再随后端状态变化而变化
if (this.startCoilInfo.coilid) {
// 有本地存储的起始钢卷,保持不变
return
}
// 只有在没有本地存储时,才检查后端是否有历史设置
if (first_coilid) {
try {
const params = { page: 1, page_size: 1, coilid: first_coilid }
const coilRes = await axios.get('/api/track/available-coils', { params })
if (coilRes.data.data && coilRes.data.data.length > 0) {
const coil = coilRes.data.data[0]
this.startCoilInfo = {
coilid: coil.coilid,
sequencenb: coil.sequencenb,
set_time: '历史设置' // 标记为历史设置,不是当前手动设置
}
// 保存到localStorage
this.saveStartCoilToStorage()
}
} catch (e) {
console.error('Failed to get start coil details:', e)
}
}
} catch (e) {
console.error('Failed to load track status:', e)
}
},
isStartCoil(coilid) {
return this.startCoilInfo.coilid === coilid
},
openStartCoilDialog() {
this.startCoilDialogVisible = true
this.searchAvailableCoils()
},
clearStartCoil() {
this.startCoilInfo = {
coilid: '',
sequencenb: 0,
set_time: ''
}
this.clearStartCoilFromStorage()
},
async searchAvailableCoils() {
this.searchLoading = true
try {
const params = {
page: this.searchParams.page,
page_size: this.searchParams.page_size,
coilid: this.searchCoilid || undefined
}
// 添加顺序号筛选
if (this.searchSequencenb) {
const seqNb = parseInt(this.searchSequencenb)
if (!isNaN(seqNb)) {
params.sequencenb_min = seqNb
params.sequencenb_max = seqNb
}
}
const res = await axios.get('/api/track/available-coils', { params })
this.availableCoils = res.data.data || []
this.searchTotal = res.data.total || 0
} catch (e) {
this.$message.error('搜索钢卷失败: ' + e.message)
} finally {
this.searchLoading = false
}
},
async selectStartCoil(row) {
this.$confirm(`确认设置钢卷 [${row.coilid}] 作为追踪起始点?\n设置后将自动加载该钢卷及后续3个钢卷到追踪表。`, '设置起始钢卷', {
confirmButtonText: '确认设置',
cancelButtonText: '取消',
type: 'warning'
}).then(async () => {
try {
// 设置起始钢卷
const res = await axios.post('/api/track/set-start-coil', { coilid: row.coilid })
this.$message.success(res.data.message)
// 更新起始钢卷信息并保存到localStorage
this.startCoilInfo = {
coilid: row.coilid,
sequencenb: row.sequencenb,
set_time: new Date().toLocaleString('zh-CN')
}
this.saveStartCoilToStorage()
// 重新加载钢卷列表
this.loadCoils()
this.startCoilDialogVisible = false
} catch (e) {
this.$message.error(e.response?.data?.detail || e.message)
}
}).catch(() => {})
},
handleSizeChange(val) {
this.searchParams.page_size = val
this.searchParams.page = 1
this.searchAvailableCoils()
},
handleCurrentChange(val) {
this.searchParams.page = val
this.searchAvailableCoils()
}
}
}