refactor(mes-qc): 优化 IPQC/IQC 质检模块

IPQC 优化:
  - 修复 TODO AI 项:模板检索方法、tool 字段重构、删除冗余字段
  - 新增废品数量字段(工废/料废/其他废品)及验证
  - 添加检测人员和物料的存在性验证

  IQC 优化:
  - 检测人员由后端自动设置改为前端传递
  - 添加检测人员存在性验证
pull/871/MERGE
YunaiV 2026-02-25 00:15:05 +08:00
parent a013f965aa
commit 7df0785818
4 changed files with 72 additions and 84 deletions

View File

@ -24,10 +24,10 @@ export interface QcIpqcVO {
itemName: string // 产品物料名称(关联查询)
itemSpecification: string // 规格型号(关联查询)
unitName: string // 单位名称(关联查询)
checkQuantity: number // 检测数量
qualifiedQuantity: number // 合格品数量
unqualifiedQuantity: number // 不合格品数量
laborScrapQuantity: number // 工废数量
checkQuantity?: number // 检测数量
qualifiedQuantity?: number // 合格品数量
unqualifiedQuantity?: number // 不合格品数量
laborScrapQuantity?: number // 工废数量
materialScrapQuantity: number // 料废数量
otherScrapQuantity: number // 其他废品数量
criticalRate: number // 致命缺陷率(%
@ -40,7 +40,7 @@ export interface QcIpqcVO {
inspectDate: Date // 检测日期
inspectorUserId: number // 检测人员用户 ID
inspectorNickname: string // 检测人员昵称(关联查询)
status: number // 状态
status?: number // 状态
remark: string // 备注
}

View File

@ -8,6 +8,7 @@
:rules="formRules"
label-width="120px"
v-loading="formLoading"
:disabled="isDetail"
>
<el-row :gutter="16">
<el-col :span="8">
@ -64,76 +65,86 @@
<el-divider content-position="left">检测情况</el-divider>
<el-row :gutter="16">
<el-col :span="6">
<el-col :span="8">
<el-form-item label="检测数量" prop="checkQuantity">
<el-input-number
v-model="formData.checkQuantity"
:min="0"
:precision="2"
placeholder="请输入"
placeholder="请输入检测数量"
class="!w-1/1"
/>
</el-form-item>
</el-col>
<el-col :span="6">
<el-col :span="8">
<el-form-item label="合格品数量" prop="qualifiedQuantity">
<el-input-number
v-model="formData.qualifiedQuantity"
:min="0"
:precision="2"
placeholder="请输入"
placeholder="请输入合格品数量"
class="!w-1/1"
/>
</el-form-item>
</el-col>
<el-col :span="6">
<el-col :span="8">
<el-form-item label="不合格品数量" prop="unqualifiedQuantity">
<el-input-number
v-model="formData.unqualifiedQuantity"
:min="0"
:precision="2"
placeholder="请输入"
placeholder="请输入不合格品数量"
class="!w-1/1"
/>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="16">
<el-col :span="6">
<!-- 废品数量当不合格数量大于 0 时显示 -->
<el-row :gutter="16" v-if="formData.unqualifiedQuantity && formData.unqualifiedQuantity > 0">
<el-col :span="8">
<el-form-item label="工废数量" prop="laborScrapQuantity">
<el-input-number
v-model="formData.laborScrapQuantity"
:min="0"
:precision="2"
placeholder="请输入"
placeholder="请输入工废数量"
class="!w-1/1"
/>
</el-form-item>
</el-col>
<el-col :span="6">
<el-col :span="8">
<el-form-item label="料废数量" prop="materialScrapQuantity">
<el-input-number
v-model="formData.materialScrapQuantity"
:min="0"
:precision="2"
placeholder="请输入"
placeholder="请输入料废数量"
class="!w-1/1"
/>
</el-form-item>
</el-col>
<el-col :span="6">
<el-col :span="8">
<el-form-item label="其他废品数量" prop="otherScrapQuantity">
<el-input-number
v-model="formData.otherScrapQuantity"
:min="0"
:precision="2"
placeholder="请输入"
placeholder="请输入其他废品数量"
class="!w-1/1"
/>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="16">
<el-col :span="8">
<el-form-item label="检测人员" prop="inspectorUserId">
<UserSelect
v-model="formData.inspectorUserId"
placeholder="请选择检测人员"
class="!w-1/1"
/>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="检测日期" prop="inspectDate">
<el-date-picker
@ -145,15 +156,6 @@
/>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="检测人员" prop="inspectorUserId">
<UserSelect
v-model="formData.inspectorUserId"
placeholder="请选择检测人员"
class="!w-1/1"
/>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="检测结果" prop="checkResult">
<el-select
@ -220,8 +222,8 @@
</template>
</el-form>
<!-- 子表标签页编辑模式下显示 -->
<template v-if="formType === 'update' && formData.id">
<!-- 子表标签页编辑/详情模式下显示 -->
<template v-if="(formType === 'update' || formType === 'detail') && formData.id">
<el-divider />
<el-tabs v-model="activeTab">
<el-tab-pane label="检验项" name="line">
@ -234,12 +236,7 @@
</template>
<template #footer>
<el-button
@click="submitForm"
type="primary"
:disabled="formLoading"
v-if="formData.status === 0"
>
<el-button @click="submitForm" type="primary" :disabled="formLoading" v-if="!isDetail">
</el-button>
<el-button @click="dialogVisible = false"> </el-button>
@ -264,10 +261,18 @@ const { t } = useI18n()
const message = useMessage()
const dialogVisible = ref(false)
const dialogTitle = ref('')
const formLoading = ref(false)
const formType = ref('')
const activeTab = ref('line')
const dialogTitle = computed(() => {
const titles = {
create: '新增过程检验单',
update: '修改过程检验单',
detail: '查看过程检验单'
}
return titles[formType.value] || t('action.' + formType.value)
})
const isDetail = computed(() => formType.value === 'detail')
const formData = ref({
id: undefined as number | undefined,
@ -277,7 +282,6 @@ const formData = ref({
templateId: undefined,
sourceDocId: undefined,
sourceDocType: undefined,
sourceDocCode: undefined,
sourceLineId: undefined,
workOrderId: undefined,
taskId: undefined,
@ -285,8 +289,8 @@ const formData = ref({
processId: undefined,
itemId: undefined,
checkQuantity: undefined,
qualifiedQuantity: 0,
unqualifiedQuantity: 0,
qualifiedQuantity: undefined,
unqualifiedQuantity: undefined,
laborScrapQuantity: 0,
materialScrapQuantity: 0,
otherScrapQuantity: 0,
@ -294,7 +298,6 @@ const formData = ref({
inspectDate: undefined,
inspectorUserId: undefined,
remark: undefined,
status: 0,
//
criticalRate: 0,
majorRate: 0,
@ -303,24 +306,30 @@ const formData = ref({
majorQuantity: 0,
minorQuantity: 0
})
// TODO @AI
const formRules = reactive({
code: [{ required: true, message: '检验单编号不能为空', trigger: 'blur' }],
name: [{ required: true, message: '检验单名称不能为空', trigger: 'blur' }],
type: [{ required: true, message: '检验类型不能为空', trigger: 'change' }],
workOrderId: [{ required: true, message: '生产工单不能为空', trigger: 'change' }],
workstationId: [{ required: true, message: '工位不能为空', trigger: 'change' }]
workstationId: [{ required: true, message: '工位不能为空', trigger: 'change' }],
checkQuantity: [{ required: true, message: '检测数量不能为空', trigger: 'blur' }],
qualifiedQuantity: [{ required: true, message: '合格品数量不能为空', trigger: 'blur' }],
unqualifiedQuantity: [{ required: true, message: '不合格品数量不能为空', trigger: 'blur' }],
laborScrapQuantity: [{ required: true, message: '工废数量不能为空', trigger: 'blur' }],
materialScrapQuantity: [{ required: true, message: '料废数量不能为空', trigger: 'blur' }],
otherScrapQuantity: [{ required: true, message: '其他废品数量不能为空', trigger: 'blur' }],
inspectorUserId: [{ required: true, message: '检测人员不能为空', trigger: 'change' }],
inspectDate: [{ required: true, message: '检测日期不能为空', trigger: 'change' }]
})
const formRef = ref()
/** 打开弹窗 */
const open = async (type: string, id?: number) => {
dialogVisible.value = true
dialogTitle.value = t('action.' + type)
formType.value = type
activeTab.value = 'line'
resetForm()
//
// /
if (id) {
formLoading.value = true
try {
@ -365,7 +374,6 @@ const resetForm = () => {
templateId: undefined,
sourceDocId: undefined,
sourceDocType: undefined,
sourceDocCode: undefined,
sourceLineId: undefined,
workOrderId: undefined,
taskId: undefined,
@ -373,8 +381,8 @@ const resetForm = () => {
processId: undefined,
itemId: undefined,
checkQuantity: undefined,
qualifiedQuantity: 0,
unqualifiedQuantity: 0,
qualifiedQuantity: undefined,
unqualifiedQuantity: undefined,
laborScrapQuantity: 0,
materialScrapQuantity: 0,
otherScrapQuantity: 0,
@ -382,7 +390,6 @@ const resetForm = () => {
inspectDate: undefined,
inspectorUserId: undefined,
remark: undefined,
status: 0,
criticalRate: 0,
majorRate: 0,
minorRate: 0,

View File

@ -41,15 +41,6 @@
class="!w-240px"
/>
</el-form-item>
<!-- TODO @AIworkstationId 前后端检索都去掉这个字段 -->
<el-form-item label="工位" prop="workstationId">
<MdWorkstationSelect
v-model="queryParams.workstationId"
placeholder="请选择工位"
clearable
class="!w-240px"
/>
</el-form-item>
<el-form-item label="产品物料" prop="itemId">
<MdItemSelect
v-model="queryParams.itemId"
@ -73,18 +64,6 @@
/>
</el-select>
</el-form-item>
<!-- TODO @AIinspectDate 前后端检索都去掉这个字段 -->
<el-form-item label="检测日期" prop="inspectDate">
<el-date-picker
v-model="queryParams.inspectDate"
value-format="YYYY-MM-DD HH:mm:ss"
type="daterange"
start-placeholder="开始日期"
end-placeholder="结束日期"
:default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
class="!w-240px"
/>
</el-form-item>
<el-form-item>
<el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
<el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
@ -112,8 +91,13 @@
<!-- 列表 -->
<ContentWrap>
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
<!-- TODO @AI需要有超链接 -->
<el-table-column label="检验单编号" align="center" prop="code" width="160" />
<el-table-column label="检验单编号" align="center" prop="code" width="160">
<template #default="scope">
<el-link type="primary" @click="openForm('detail', scope.row.id)">
{{ scope.row.code }}
</el-link>
</template>
</el-table-column>
<el-table-column label="检验单名称" align="center" prop="name" min-width="180" />
<el-table-column label="检验类型" align="center" prop="type" width="120">
<template #default="scope">
@ -196,7 +180,6 @@ import { QcIpqcApi, QcIpqcVO } from '@/api/mes/qc/ipqc'
import IpqcForm from './IpqcForm.vue'
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
import ProWorkOrderSelect from '@/views/mes/pro/workorder/components/ProWorkOrderSelect.vue'
import MdWorkstationSelect from '@/views/mes/md/workstation/components/MdWorkstationSelect.vue'
import MdItemSelect from '@/views/mes/md/item/components/MdItemSelect.vue'
import { MesOrderStatusEnum } from '@/views/mes/utils/constants'
@ -214,10 +197,8 @@ const queryParams = reactive({
code: undefined,
type: undefined,
workOrderId: undefined,
workstationId: undefined,
itemId: undefined,
checkResult: undefined,
inspectDate: undefined
checkResult: undefined
})
const queryFormRef = ref()
const exportLoading = ref(false)

View File

@ -95,14 +95,11 @@
</el-row>
<el-row :gutter="16">
<el-col :span="8">
<el-form-item label="来料日期" prop="receiveDate">
<el-date-picker
v-model="formData.receiveDate"
type="datetime"
value-format="YYYY-MM-DD HH:mm:ss"
placeholder="请选择来料日期"
<el-form-item label="检测人员" prop="inspectorUserId">
<UserSelect
v-model="formData.inspectorUserId"
placeholder="请选择检测人员"
class="!w-1/1"
:disabled="isFromPendingTask"
/>
</el-form-item>
</el-col>
@ -248,9 +245,10 @@ const formData = ref({
receivedQuantity: undefined,
qualifiedQuantity: undefined,
unqualifiedQuantity: undefined,
inspectorUserId: undefined,
inspectDate: undefined,
checkResult: undefined,
receiveDate: undefined,
inspectDate: undefined,
remark: undefined,
//
criticalRate: 0,
@ -269,6 +267,7 @@ const formRules = reactive({
qualifiedQuantity: [{ required: true, message: '合格品数量不能为空', trigger: 'blur' }],
unqualifiedQuantity: [{ required: true, message: '不合格品数量不能为空', trigger: 'blur' }],
receiveDate: [{ required: true, message: '来料日期不能为空', trigger: 'change' }],
inspectorUserId: [{ required: true, message: '检测人员不能为空', trigger: 'change' }],
inspectDate: [{ required: true, message: '检测日期不能为空', trigger: 'change' }]
})
const formRef = ref() // Ref
@ -342,9 +341,10 @@ const resetForm = () => {
receivedQuantity: undefined,
qualifiedQuantity: undefined,
unqualifiedQuantity: undefined,
inspectorUserId: undefined,
inspectDate: undefined,
checkResult: undefined,
receiveDate: undefined,
inspectDate: undefined,
remark: undefined,
criticalRate: 0,
majorRate: 0,