2026-08-28 15:06:01 +08:00
|
|
|
|
<script setup>
|
2026-09-03 14:20:37 +08:00
|
|
|
|
import { computed, nextTick, onMounted, reactive, ref } from 'vue'
|
2026-08-28 15:06:01 +08:00
|
|
|
|
import { ElMessage } from 'element-plus'
|
|
|
|
|
|
import request from '../utils/request'
|
|
|
|
|
|
import { getRealName } from '../utils/auth'
|
2026-08-28 17:23:44 +08:00
|
|
|
|
import PageHelp from '../components/PageHelp.vue'
|
|
|
|
|
|
import { helpInspection } from '../help'
|
2026-08-28 15:06:01 +08:00
|
|
|
|
|
|
|
|
|
|
const targetType = ref('BATCH')
|
|
|
|
|
|
const status = ref('合格')
|
|
|
|
|
|
const inspector = ref(getRealName())
|
2026-09-03 14:20:37 +08:00
|
|
|
|
const submitting = ref(false)
|
2026-08-28 15:06:01 +08:00
|
|
|
|
|
2026-09-03 14:20:37 +08:00
|
|
|
|
// 检验量化字段
|
|
|
|
|
|
const inspectQty = ref(0)
|
|
|
|
|
|
const passQty = ref(0)
|
|
|
|
|
|
const checkDesc = ref('')
|
|
|
|
|
|
const failReason = ref('')
|
2026-08-28 15:06:01 +08:00
|
|
|
|
|
2026-09-03 14:20:37 +08:00
|
|
|
|
// 待检编号列表(合并单/批量,统一一次提交)
|
|
|
|
|
|
const targetList = ref([])
|
|
|
|
|
|
const scanInput = ref('')
|
|
|
|
|
|
const pickerValue = ref('')
|
2026-08-28 15:06:01 +08:00
|
|
|
|
|
2026-09-03 14:20:37 +08:00
|
|
|
|
// 输入框标题/占位随目标类型变化(而非仅一行小字提示)
|
|
|
|
|
|
const inputLabel = computed(() => (targetType.value === 'BATCH' ? '批次号' : 'SN 序列号'))
|
|
|
|
|
|
const inputPlaceholder = computed(() =>
|
|
|
|
|
|
targetType.value === 'BATCH'
|
|
|
|
|
|
? '扫入或粘贴批次号,可用 逗号/分号/空格/回车 分隔,一次可录多条'
|
|
|
|
|
|
: '扫入或粘贴 SN 序列号,可用 逗号/分号/空格/回车 分隔,一次可录多条'
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
// 库存拾取器:直接选已存在的批次号/SN,避免手输(垃圾设计)
|
|
|
|
|
|
const pickerOptions = ref([])
|
|
|
|
|
|
async function loadPicker() {
|
2026-08-28 15:06:01 +08:00
|
|
|
|
try {
|
2026-09-03 14:20:37 +08:00
|
|
|
|
const mm = targetType.value === 'BATCH' ? 1 : 2
|
|
|
|
|
|
const data = await request.get('/stock/query', {
|
|
|
|
|
|
params: { manageMode: mm, page: 1, pageSize: 500 }
|
2026-08-28 15:06:01 +08:00
|
|
|
|
})
|
2026-09-03 14:20:37 +08:00
|
|
|
|
pickerOptions.value = data?.list || []
|
|
|
|
|
|
} catch (e) {
|
|
|
|
|
|
pickerOptions.value = []
|
2026-08-28 15:06:01 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-09-03 14:20:37 +08:00
|
|
|
|
onMounted(loadPicker)
|
2026-08-28 15:06:01 +08:00
|
|
|
|
|
2026-09-03 14:20:37 +08:00
|
|
|
|
function pickerLabel(opt) {
|
|
|
|
|
|
const id = targetType.value === 'BATCH' ? opt.batchNo : opt.snCode
|
|
|
|
|
|
return `${opt.materialCode || ''}|${id || ''}|质量:${opt.qualityStatus || '未检'}`
|
|
|
|
|
|
}
|
2026-08-28 15:06:01 +08:00
|
|
|
|
|
2026-09-03 14:20:37 +08:00
|
|
|
|
function addFromInput() {
|
|
|
|
|
|
const arr = String(scanInput.value)
|
|
|
|
|
|
.split(/[,,.。;;、\s]+/)
|
|
|
|
|
|
.map((s) => s.trim())
|
|
|
|
|
|
.filter(Boolean)
|
|
|
|
|
|
for (const v of arr) {
|
|
|
|
|
|
if (!targetList.value.includes(v)) targetList.value.push(v)
|
|
|
|
|
|
}
|
|
|
|
|
|
scanInput.value = ''
|
|
|
|
|
|
}
|
|
|
|
|
|
function addFromPicker() {
|
|
|
|
|
|
if (!pickerValue.value) return
|
|
|
|
|
|
if (!targetList.value.includes(pickerValue.value)) targetList.value.push(pickerValue.value)
|
|
|
|
|
|
pickerValue.value = ''
|
|
|
|
|
|
}
|
|
|
|
|
|
function removeAt(i) {
|
|
|
|
|
|
targetList.value.splice(i, 1)
|
|
|
|
|
|
}
|
|
|
|
|
|
function clearAll() {
|
|
|
|
|
|
targetList.value = []
|
2026-08-28 15:06:01 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-09-03 14:20:37 +08:00
|
|
|
|
async function submitInspection() {
|
|
|
|
|
|
if (!targetList.value.length) {
|
|
|
|
|
|
ElMessage.warning('请先录入或选择待检编号')
|
2026-08-28 15:06:01 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
if (!inspector.value.trim()) {
|
|
|
|
|
|
ElMessage.warning('请填写检验员')
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
2026-09-03 14:20:37 +08:00
|
|
|
|
submitting.value = true
|
2026-08-28 15:06:01 +08:00
|
|
|
|
try {
|
|
|
|
|
|
const data = await request.post('/inspection/batch-flip', {
|
|
|
|
|
|
targetType: targetType.value,
|
2026-09-03 14:20:37 +08:00
|
|
|
|
targetIds: [...targetList.value],
|
2026-08-28 15:06:01 +08:00
|
|
|
|
status: status.value,
|
2026-09-03 14:20:37 +08:00
|
|
|
|
inspectQty: Number(inspectQty.value) || 0,
|
|
|
|
|
|
passQty: Number(passQty.value) || 0,
|
|
|
|
|
|
checkDesc: checkDesc.value.trim(),
|
|
|
|
|
|
failReason: failReason.value.trim(),
|
2026-08-28 15:06:01 +08:00
|
|
|
|
inspector: inspector.value.trim()
|
|
|
|
|
|
})
|
2026-09-03 14:20:37 +08:00
|
|
|
|
const n = data?.flipped ?? targetList.value.length
|
|
|
|
|
|
ElMessage.success(`检验已记录:共 ${n} 条 → ${status.value}`)
|
|
|
|
|
|
clearAll()
|
|
|
|
|
|
inspectQty.value = 0
|
|
|
|
|
|
passQty.value = 0
|
|
|
|
|
|
checkDesc.value = ''
|
|
|
|
|
|
failReason.value = ''
|
2026-08-28 15:06:01 +08:00
|
|
|
|
} finally {
|
2026-09-03 14:20:37 +08:00
|
|
|
|
submitting.value = false
|
2026-08-28 15:06:01 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
|
|
<template>
|
2026-09-03 14:20:37 +08:00
|
|
|
|
<el-card shadow="never" style="max-width:860px;margin:0 auto">
|
|
|
|
|
|
<el-form label-width="96px">
|
|
|
|
|
|
<!-- 目标类型直接改变输入框标题与占位,直观体现当前录入对象 -->
|
2026-08-28 15:06:01 +08:00
|
|
|
|
<el-form-item label="目标类型">
|
2026-09-03 14:20:37 +08:00
|
|
|
|
<el-radio-group v-model="targetType" @change="loadPicker">
|
|
|
|
|
|
<el-radio-button value="BATCH">批次 (结构件)</el-radio-button>
|
|
|
|
|
|
<el-radio-button value="SN">序列号 SN (精密件)</el-radio-button>
|
2026-08-28 15:06:01 +08:00
|
|
|
|
</el-radio-group>
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
<el-form-item label="检验结论">
|
|
|
|
|
|
<el-radio-group v-model="status">
|
|
|
|
|
|
<el-radio value="合格">合格</el-radio>
|
|
|
|
|
|
<el-radio value="不合格">不合格</el-radio>
|
|
|
|
|
|
</el-radio-group>
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
<el-form-item label="检验员">
|
|
|
|
|
|
<el-input v-model="inspector" placeholder="检验员姓名" clearable style="width:260px" />
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
|
2026-09-03 14:20:37 +08:00
|
|
|
|
<el-divider content-position="left">检验量化(可空)</el-divider>
|
|
|
|
|
|
<el-form-item label="检验数量">
|
|
|
|
|
|
<el-input-number v-model="inspectQty" :min="0" :max="999999" placeholder="抽检件数" style="width:160px" />
|
2026-08-28 15:06:01 +08:00
|
|
|
|
</el-form-item>
|
2026-09-03 14:20:37 +08:00
|
|
|
|
<el-form-item label="合格数量">
|
|
|
|
|
|
<el-input-number v-model="passQty" :min="0" :max="999999" placeholder="合格件数(≤检验数量)" style="width:160px" />
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
<el-form-item label="检测说明">
|
|
|
|
|
|
<el-input v-model="checkDesc" placeholder="检验项目/方法/标准(可空)" clearable style="width:420px" />
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
<el-form-item v-if="status === '不合格'" label="不合格原因">
|
|
|
|
|
|
<el-input v-model="failReason" type="textarea" :rows="2" placeholder="不合格原因(可空)" style="width:420px" />
|
2026-08-28 15:06:01 +08:00
|
|
|
|
</el-form-item>
|
|
|
|
|
|
|
2026-09-03 14:20:37 +08:00
|
|
|
|
<el-divider content-position="left">待检编号(可扫/选多条,统一提交)</el-divider>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 从库存直接拾取,避免手输批次号 -->
|
|
|
|
|
|
<el-form-item :label="`从库存选${inputLabel}`">
|
|
|
|
|
|
<el-select
|
|
|
|
|
|
v-model="pickerValue"
|
|
|
|
|
|
filterable
|
|
|
|
|
|
clearable
|
|
|
|
|
|
:placeholder="`选择已存在的${inputLabel}(来自库存)`"
|
|
|
|
|
|
style="width:100%"
|
|
|
|
|
|
@change="addFromPicker"
|
|
|
|
|
|
>
|
|
|
|
|
|
<el-option v-for="opt in pickerOptions" :key="targetType === 'BATCH' ? opt.batchNo : opt.snCode"
|
|
|
|
|
|
:value="targetType === 'BATCH' ? opt.batchNo : opt.snCode"
|
|
|
|
|
|
:label="pickerLabel(opt)" />
|
|
|
|
|
|
</el-select>
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 批量/扫码录入:单条也走这里,无需单独的"单个录入" -->
|
|
|
|
|
|
<el-form-item :label="`录入${inputLabel}`">
|
|
|
|
|
|
<div style="width:100%">
|
|
|
|
|
|
<el-input v-model="scanInput" type="textarea" :rows="4"
|
|
|
|
|
|
:placeholder="inputPlaceholder" />
|
|
|
|
|
|
<el-button type="primary" plain style="margin-top:6px" :disabled="!scanInput.trim()"
|
|
|
|
|
|
@click="addFromInput">录入到列表</el-button>
|
|
|
|
|
|
<span style="margin-left:8px;color:#909399;font-size:12px">支持扫码枪/粘贴,多编号用逗号、分号、空格或回车分隔</span>
|
|
|
|
|
|
</div>
|
2026-08-28 15:06:01 +08:00
|
|
|
|
</el-form-item>
|
2026-09-03 14:20:37 +08:00
|
|
|
|
|
|
|
|
|
|
<!-- 已选清单 -->
|
|
|
|
|
|
<el-form-item label="已选清单">
|
|
|
|
|
|
<div style="width:100%">
|
|
|
|
|
|
<el-tag v-for="(v, i) in targetList" :key="v + i" closable type="info"
|
|
|
|
|
|
style="margin:0 8px 8px 0" @close="removeAt(i)">{{ v }}</el-tag>
|
|
|
|
|
|
<span v-if="!targetList.length" style="color:#909399;font-size:12px">尚未选择任何待检编号</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
|
2026-08-28 15:06:01 +08:00
|
|
|
|
<el-form-item>
|
2026-09-03 14:20:37 +08:00
|
|
|
|
<el-button type="primary" :loading="submitting" @click="submitInspection">提交检验</el-button>
|
|
|
|
|
|
<el-button :disabled="!targetList.length" @click="clearAll">清空</el-button>
|
2026-08-28 15:06:01 +08:00
|
|
|
|
</el-form-item>
|
|
|
|
|
|
</el-form>
|
|
|
|
|
|
</el-card>
|
2026-08-28 17:23:44 +08:00
|
|
|
|
<PageHelp :data="helpInspection" />
|
2026-08-28 15:06:01 +08:00
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
<style scoped>
|
|
|
|
|
|
.tip { margin-left: 10px; color: #909399; font-size: 12px; }
|
2026-09-03 14:20:37 +08:00
|
|
|
|
</style>
|