2026-08-28 15:06:01 +08:00
|
|
|
|
<template>
|
|
|
|
|
|
<el-card>
|
|
|
|
|
|
<el-form inline>
|
|
|
|
|
|
<el-form-item label="工件SN">
|
|
|
|
|
|
<el-input v-model="sn" style="width:220px" placeholder="输入SN查询追溯" @keyup.enter="search" />
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
<el-form-item><el-button type="primary" @click="search">追溯查询</el-button></el-form-item>
|
2026-08-31 08:06:55 +08:00
|
|
|
|
<el-form-item>
|
|
|
|
|
|
<el-button type="warning" :disabled="!sn" @click="printCard">打印流程卡</el-button>
|
|
|
|
|
|
</el-form-item>
|
2026-08-28 15:06:01 +08:00
|
|
|
|
</el-form>
|
|
|
|
|
|
|
|
|
|
|
|
<el-alert v-if="error" :title="error" type="error" show-icon closable style="margin-bottom:10px" />
|
|
|
|
|
|
|
|
|
|
|
|
<template v-if="data">
|
|
|
|
|
|
<el-descriptions :column="4" border style="margin-bottom:12px">
|
|
|
|
|
|
<el-descriptions-item label="SN">{{ data.workpiece?.sn }}</el-descriptions-item>
|
|
|
|
|
|
<el-descriptions-item label="工单号">{{ data.workpiece?.orderNo }}</el-descriptions-item>
|
|
|
|
|
|
<el-descriptions-item label="状态">{{ data.workpiece?.status }}</el-descriptions-item>
|
2026-08-31 12:58:28 +08:00
|
|
|
|
<el-descriptions-item label="当前工位">{{ data.workpiece?.currentProcess }}</el-descriptions-item>
|
2026-08-28 15:06:01 +08:00
|
|
|
|
<el-descriptions-item label="进线时间">{{ fmt(data.workpiece?.onlineAt) }}</el-descriptions-item>
|
|
|
|
|
|
<el-descriptions-item label="完工时间">{{ fmt(data.workpiece?.doneAt) }}</el-descriptions-item>
|
|
|
|
|
|
</el-descriptions>
|
|
|
|
|
|
|
|
|
|
|
|
<el-tabs>
|
2026-08-31 12:58:28 +08:00
|
|
|
|
<el-tab-pane label="工位时间线">
|
2026-08-28 15:06:01 +08:00
|
|
|
|
<el-timeline>
|
|
|
|
|
|
<el-timeline-item v-for="p in (data.processTimeline||[])" :key="p.id"
|
|
|
|
|
|
:timestamp="p.operator ? (p.operator+' · '+fmt(p.endedAt)) : fmt(p.endedAt)"
|
|
|
|
|
|
:type="p.result==='NG'?'danger':'success'">
|
2026-08-31 12:58:28 +08:00
|
|
|
|
{{ p.processName }}(工位 {{ p.stationNo || p.processCode }})— {{ p.result }}
|
2026-08-28 15:06:01 +08:00
|
|
|
|
</el-timeline-item>
|
|
|
|
|
|
</el-timeline>
|
|
|
|
|
|
</el-tab-pane>
|
|
|
|
|
|
<el-tab-pane label="步骤考核数据">
|
|
|
|
|
|
<el-table :data="data.stepData||[]" border size="small">
|
|
|
|
|
|
<el-table-column prop="stepName" label="步骤" min-width="110" />
|
|
|
|
|
|
<el-table-column prop="criterionName" label="考核项" min-width="110" />
|
|
|
|
|
|
<el-table-column prop="value" label="数值" width="100" />
|
|
|
|
|
|
<el-table-column prop="isOK" label="合格" width="80">
|
|
|
|
|
|
<template #default="{row}"><el-tag :type="row.isOK?'success':'danger'">{{ row.isOK?'OK':'NG' }}</el-tag></template>
|
|
|
|
|
|
</el-table-column>
|
|
|
|
|
|
<el-table-column prop="operator" label="操作人" width="90" />
|
|
|
|
|
|
</el-table>
|
|
|
|
|
|
</el-tab-pane>
|
|
|
|
|
|
<el-tab-pane label="拧紧数据">
|
|
|
|
|
|
<el-table :data="data.torqueRecords||[]" border size="small">
|
|
|
|
|
|
<el-table-column prop="stationNo" label="工位" width="80" />
|
|
|
|
|
|
<el-table-column prop="torque" label="扭矩" width="100" />
|
|
|
|
|
|
<el-table-column prop="angle" label="角度" width="90" />
|
|
|
|
|
|
<el-table-column prop="result" label="结果" width="80" />
|
|
|
|
|
|
<el-table-column prop="operator" label="操作人" width="90" />
|
|
|
|
|
|
<el-table-column prop="time" label="时间" min-width="160" />
|
|
|
|
|
|
</el-table>
|
|
|
|
|
|
</el-tab-pane>
|
|
|
|
|
|
<el-tab-pane label="成品关联">
|
|
|
|
|
|
<p>批次号:{{ (data.associationTrace?.batchItems||[]).join(', ') || '无' }}</p>
|
|
|
|
|
|
<p>精密件SN:{{ (data.associationTrace?.serialItems||[]).join(', ') || '无' }}</p>
|
|
|
|
|
|
</el-tab-pane>
|
2026-09-07 11:58:19 +08:00
|
|
|
|
<el-tab-pane label="装机绑定">
|
|
|
|
|
|
<el-alert :type="data.bindTrace?.complete ? 'success' : 'warning'" :closable="false" style="margin-bottom:10px"
|
|
|
|
|
|
:title="data.bindTrace?.complete ? '装配物料已全部绑齐 ✓' : ('装配物料未绑齐:' + (data.bindTrace?.missing || ''))" show-icon />
|
|
|
|
|
|
<el-table :data="data.bindTrace?.binds||[]" border size="small">
|
|
|
|
|
|
<el-table-column prop="processCode" label="工序" width="70" />
|
|
|
|
|
|
<el-table-column prop="materialCode" label="物料编码" min-width="110" />
|
|
|
|
|
|
<el-table-column prop="materialName" label="物料名称" min-width="110" />
|
|
|
|
|
|
<el-table-column prop="spec" label="规格" min-width="90" />
|
|
|
|
|
|
<el-table-column prop="bindType" label="类型" width="70">
|
|
|
|
|
|
<template #default="{row}">{{ row.bindType === 'SN' ? '精密件SN' : '批次' }}</template>
|
|
|
|
|
|
</el-table-column>
|
|
|
|
|
|
<el-table-column prop="bindValue" label="绑定值(SN/批次)" min-width="140" />
|
|
|
|
|
|
<el-table-column prop="operator" label="绑定人" width="90" />
|
|
|
|
|
|
<el-table-column prop="createdAt" label="绑定时间" min-width="165">
|
|
|
|
|
|
<template #default="{row}">{{ fmt(row.createdAt) }}</template>
|
|
|
|
|
|
</el-table-column>
|
|
|
|
|
|
</el-table>
|
|
|
|
|
|
</el-tab-pane>
|
2026-08-28 15:06:01 +08:00
|
|
|
|
</el-tabs>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</el-card>
|
2026-09-07 11:58:19 +08:00
|
|
|
|
<PageHelp :data="helpTrace" page="Trace" />
|
2026-08-28 15:06:01 +08:00
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
<script setup>
|
|
|
|
|
|
import { ref } from 'vue'
|
2026-09-04 14:18:53 +08:00
|
|
|
|
import { ElMessageBox } from 'element-plus'
|
2026-08-28 15:06:01 +08:00
|
|
|
|
import request from '../utils/request'
|
2026-08-28 17:23:44 +08:00
|
|
|
|
import PageHelp from '../components/PageHelp.vue'
|
|
|
|
|
|
import { helpTrace } from '../help'
|
2026-08-28 15:06:01 +08:00
|
|
|
|
|
|
|
|
|
|
const sn = ref('')
|
|
|
|
|
|
const data = ref(null)
|
|
|
|
|
|
const error = ref('')
|
|
|
|
|
|
|
|
|
|
|
|
async function search() {
|
|
|
|
|
|
if (!sn.value) return
|
|
|
|
|
|
error.value = ''
|
|
|
|
|
|
data.value = null
|
|
|
|
|
|
try {
|
|
|
|
|
|
data.value = await request.get('/trace', { params: { sn: sn.value } })
|
|
|
|
|
|
} catch (e) {
|
|
|
|
|
|
error.value = e.message || '查询失败'
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-09-04 14:18:53 +08:00
|
|
|
|
async function printCard() {
|
2026-08-31 08:06:55 +08:00
|
|
|
|
if (!sn.value) return
|
2026-09-04 14:18:53 +08:00
|
|
|
|
// 打印前数据完整性预检
|
|
|
|
|
|
let missing = []
|
|
|
|
|
|
try {
|
|
|
|
|
|
const check = await request.get('/process-card', { params: { sn: sn.value, check: 1 } })
|
|
|
|
|
|
missing = check?.missing || []
|
|
|
|
|
|
} catch {
|
|
|
|
|
|
/* 预检失败不阻塞打印 */
|
|
|
|
|
|
}
|
|
|
|
|
|
if (missing.length) {
|
|
|
|
|
|
try {
|
|
|
|
|
|
await ElMessageBox.confirm(
|
|
|
|
|
|
`以下数据不完整,是否继续打印?\n\n${missing.map((m) => '· ' + m).join('\n')}`,
|
|
|
|
|
|
'打印流程卡', {
|
|
|
|
|
|
type: 'warning',
|
|
|
|
|
|
confirmButtonText: '继续打印',
|
|
|
|
|
|
cancelButtonText: '取消'
|
|
|
|
|
|
})
|
|
|
|
|
|
} catch {
|
|
|
|
|
|
return // 用户取消
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-08-31 08:06:55 +08:00
|
|
|
|
window.open('/api/v1/process-card?sn=' + encodeURIComponent(sn.value), '_blank')
|
|
|
|
|
|
}
|
2026-08-28 15:06:01 +08:00
|
|
|
|
function fmt(v) { return v ? String(v).replace('T', ' ').slice(0, 19) : '-' }
|
|
|
|
|
|
</script>
|