不合格品处理单是正式的不合格品处置流程模块,替代旧的不合格管理(/quality/qualityUnqualified)中的处置功能。旧模块的不合格品发现(新增/列表/详情)继续使用,但**处置操作统一使用本模块**。
当检验单(原材料/过程/出厂检验)提交时,如果**不合格数量 > 0**,系统在创建 QualityUnqualified 记录的同时,**自动创建一条不合格品处理单**,unqualifiedProcess(不合格工序)根据检验类别自动映射:
| 检验类别 | inspectType | unqualifiedProcess |
|---|---|---|
| 原材料检验 | 0 | 1(来料) |
| 过程检验 | 0 | 2(制程) |
| 出厂检验 | 0 | 3(成品) |
处理单初始状态为 0(草稿),后续可编辑处置方式并提交审批。
| 旧:不合格管理 | 新:不合格品处理单 | |
|---|---|---|
| 路径 | /quality/qualityUnqualified |
/qualityUnqualifiedOrder |
| 用途 | 不合格品首次记录、查看 | 正式处置流程(含审批) |
| 处置方式 | dealResult 自由文本 |
disposalMethod 结构化枚举 |
| 创建方式 | 检验单提交自动创建 / 手动新增 | 检验单提交自动创建 / 手动新增 |
| 方法 | 路径 | 说明 |
|---|---|---|
| POST | /qualityUnqualifiedOrder/save | 新增不合格品处理单 |
请求参数:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| unqualifiedId | Long | 是 | 关联不合格品ID |
| projectName | String | 否 | 项目名称 |
| projectNo | String | 否 | 项目编号 |
| equipmentId | Long | 否 | 关联设备ID |
| equipmentName | String | 否 | 设备名称 |
| equipmentDrawingNo | String | 否 | 设备图号 |
| materialName | String | 否 | 物料/部件名称 |
| productModelId | Long | 否 | 关联产品型号ID |
| materialDrawingNo | String | 否 | 物料图号 |
| specificationModel | String | 否 | 型号规格 |
| materialQuality | String | 否 | 材质 |
| quantity | BigDecimal | 否 | 总数量 |
| unqualifiedQuantity | BigDecimal | 否 | 不合格数量 |
| unqualifiedProcess | Integer | 否 | 不合格工序:1=来料, 2=制程, 3=成品 |
| supplierName | String | 否 | 供应商名称 |
| inspectorName | String | 否 | 检验员 |
| inspectDate | Date | 否 | 检验日期 (yyyy-MM-dd) |
| responsiblePerson | String | 否 | 责任人 |
| responsibleDept | String | 否 | 责任部门 |
| problemDescription | String | 否 | 问题描述 |
| reasonAnalysis | String | 否 | 原因分析及建议 |
| correctionAction | String | 否 | 纠正措施 |
| disposalMethod | Integer | 是 | 处置方式:1=让步接收, 2=厂内维修, 3=返厂维修, 4=换货, 5=退货, 6=报废 |
| repairEvaluation | String | 否 | 厂内/返厂维修评估 |
| preventiveAction | String | 否 | 预防措施 |
| remark | String | 否 | 备注 |
| deptOpinion | String | 否 | 责任部门主管意见 |
| companyDecision | String | 否 | 公司处理决定 |
| generalManagerOpinion | String | 否 | 总经理意见 |
| storageBlobDTOs | List | 否 | 附件列表 |
自动行为:
- 自动生成处理单编号(BHGyyMMdd+流水号)
- 手动新增时:选了处置方式 → 状态自动为 3(已完成);没选 → 状态为 0(草稿)
- 质检自动创建时:状态初始为 0(草稿),需通过处理接口补充处置方式
- 当 disposalMethod 选择 2(厂内维修)或 3(返厂维修)时,自动创建返修生产订单
响应: { "code": 200, "msg": "操作成功", "data": true }
| 方法 | 路径 | 说明 |
|---|---|---|
| PUT | /qualityUnqualifiedOrder/update | 修改不合格品处理单 |
请求参数: 与新增相同,额外需要 id 字段。
| 方法 | 路径 | 说明 |
|---|---|---|
| DELETE | /qualityUnqualifiedOrder/delete | 删除不合格品处理单 |
请求参数: [id1, id2, ...] — ID 数组
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | /qualityUnqualifiedOrder/listPage | 分页查询 |
查询参数:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| page | int | 否 | 页码(默认1) |
| size | int | 否 | 每页条数(默认10) |
| status | Integer | 否 | 状态:0=草稿, 1=待审批, 2=审批中, 3=已完成, 4=已驳回 |
| projectName | String | 否 | 项目名称(模糊匹配) |
| orderNo | String | 否 | 处理单编号(模糊匹配) |
| entryDateStart | String | 否 | 创建时间起 (yyyy-MM-dd) |
| entryDateEnd | String | 否 | 创建时间止 (yyyy-MM-dd) |
响应字段: 返回 QualityUnqualifiedOrder 全部字段,含 storageBlobVOs(附件列表)。
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | /qualityUnqualifiedOrder/{id} | 处理单详情 |
响应: 返回单条 QualityUnqualifiedOrder 全部字段,含附件。
| 方法 | 路径 | 说明 |
|---|---|---|
| POST | /qualityUnqualifiedOrder/deal | 对草稿状态的处理单补充处置方式并完成 |
请求参数:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| id | Long | 是 | 处理单ID |
| disposalMethod | Integer | 是 | 处置方式:1=让步接收, 2=厂内维修, 3=返厂维修, 4=换货, 5=退货, 6=报废 |
| repairEvaluation | String | 否 | 厂内/返厂维修评估 |
| reasonAnalysis | String | 否 | 原因分析及建议 |
| correctionAction | String | 否 | 纠正措施 |
| preventiveAction | String | 否 | 预防措施 |
| remark | String | 否 | 备注 |
| deptOpinion | String | 否 | 责任部门主管意见 |
| companyDecision | String | 否 | 公司处理决定 |
| generalManagerOpinion | String | 否 | 总经理意见 |
自动行为:
- 状态更新为 3(已完成)
- 处置方式为 2(厂内维修)或 3(返厂维修)时,自动创建返修生产订单
响应: { "code": 200, "msg": "操作成功", "data": true }
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | /qualityUnqualifiedOrder/export/{id} | 导出不合格品处理单为 Excel |
请求参数: id — 处理单ID(路径参数)
响应: 文件流,Content-Type: application/vnd.ms-excel,文件名 不合格品处理单_{编号}.xls
模板字段映射:
| 模板位置 | 字段 |
|---|---|
| 项目名称 | projectName |
| 项目编号 | projectNo |
| 设备名称 | equipmentName |
| 设备图号 | equipmentDrawingNo |
| 物料名称 | materialName |
| 物料图号 | materialDrawingNo |
| 型号规格 | specificationModel |
| 材质 | materialQuality |
| 总数量 | quantity |
| 不合格数 | unqualifiedQuantity |
| 不合格工序 | unqualifiedProcess(勾选来料/制程/成品) |
| 供货商名称 | supplierName |
| 检验员 | inspectorName |
| 检验日期 | inspectDate |
| 责任人 | responsiblePerson |
| 责任部门 | responsibleDept |
| 问题描述 | problemDescription |
| 原因分析及建议 | reasonAnalysis |
| 纠正措施 | correctionAction |
| 处置方式 | disposalMethod(勾选对应选项) |
| 厂内/返厂维修评估 | repairEvaluation |
| 预防措施 | preventiveAction |
| 责任部门主管意见 | deptOpinion |
| 公司处理决定 | companyDecision |
| 总经理意见 | generalManagerOpinion |
前端调用示例:
// 列表页操作列增加导出按钮
<el-button text type="primary" @click="handleExport(row.id)">导出</el-button>
// 详情页增加导出按钮
<el-button type="primary" @click="handleExport">导出</el-button>
methods: {
handleExport(id) {
window.open(`/api/qualityUnqualifiedOrder/export/${id}`);
},
}
<template>
<div class="app-container">
<!-- 搜索栏 -->
<el-form :model="queryParams" :inline="true">
<el-form-item label="处理单编号">
<el-input v-model="queryParams.orderNo" placeholder="输入编号" />
</el-form-item>
<el-form-item label="项目名称">
<el-input v-model="queryParams.projectName" placeholder="输入项目" />
</el-form-item>
<el-form-item label="状态">
<el-select v-model="queryParams.status" placeholder="全部" clearable>
<el-option label="草稿" :value="0" />
<el-option label="待审批" :value="1" />
<el-option label="审批中" :value="2" />
<el-option label="已完成" :value="3" />
<el-option label="已驳回" :value="4" />
</el-select>
</el-form-item>
<el-form-item label="创建时间">
<el-date-picker v-model="dateRange" type="daterange" value-format="yyyy-MM-dd"
start-placeholder="开始" end-placeholder="结束" />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleQuery">查询</el-button>
<el-button @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<!-- 操作按钮 -->
<el-row :gutter="10" class="mb8">
<el-button type="primary" @click="handleAdd">新增处理单</el-button>
<el-button type="danger" :disabled="!selectedIds.length" @click="handleDelete">删除</el-button>
</el-row>
<!-- 表格 -->
<el-table :data="list" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" />
<el-table-column label="处理单编号" prop="orderNo" width="160" />
<el-table-column label="项目名称" prop="projectName" />
<el-table-column label="型号规格" prop="specificationModel" width="120" />
<el-table-column label="不合格数量" prop="unqualifiedQuantity" width="100" />
<el-table-column label="处置方式" width="110">
<template #default="{ row }">
{{ disposalMethodMap[row.disposalMethod] }}
</template>
</el-table-column>
<el-table-column label="状态" width="80">
<template #default="{ row }">
<el-tag :type="statusTagType(row.status)">{{ statusMap[row.status] }}</el-tag>
</template>
</el-table-column>
<el-table-column label="检验员" prop="inspectorName" width="100" />
<el-table-column label="检验日期" prop="inspectDate" width="110" />
<el-table-column label="创建时间" prop="createTime" width="160" />
<el-table-column label="操作" width="120" fixed="right">
<template #default="{ row }">
<el-button text type="primary" @click="handleDetail(row.id)">详情</el-button>
<el-button text type="primary" @click="handleEdit(row)">编辑</el-button>
</template>
</el-table-column>
</el-table>
<pagination :total="total" v-model:page="page" v-model:limit="size"
@pagination="loadList" />
</div>
</template>
data() {
return {
list: [],
total: 0,
page: 1,
size: 10,
selectedIds: [],
dateRange: [],
queryParams: {
orderNo: '',
projectName: '',
status: null,
},
disposalMethodMap: { 1: '让步接收', 2: '厂内维修', 3: '返厂维修', 4: '换货', 5: '退货', 6: '报废' },
statusMap: { 0: '草稿', 1: '待审批', 2: '审批中', 3: '已完成', 4: '已驳回' },
}
},
methods: {
statusTagType(status) {
const map = { 0: 'info', 1: 'warning', 2: '', 3: 'success', 4: 'danger' };
return map[status] || 'info';
},
loadList() {
const params = { ...this.queryParams, page: this.page, size: this.size };
if (this.dateRange && this.dateRange.length === 2) {
params.entryDateStart = this.dateRange[0];
params.entryDateEnd = this.dateRange[1];
}
listPage(params).then(res => {
this.list = res.rows;
this.total = res.total;
});
},
handleQuery() { this.page = 1; this.loadList(); },
resetQuery() {
this.queryParams = { orderNo: '', projectName: '', status: null };
this.dateRange = [];
this.handleQuery();
},
handleSelectionChange(selection) { this.selectedIds = selection.map(i => i.id); },
handleAdd() { this.$router.push('/quality/unqualified-order/add'); },
handleEdit(row) { this.$router.push({ path: '/quality/unqualified-order/edit', query: { id: row.id } }); },
handleDetail(id) { this.$router.push({ path: '/quality/unqualified-order/detail', query: { id } }); },
handleDelete() {
this.$confirm('确认删除选中的处理单?').then(() => {
deleteOrders(this.selectedIds).then(() => {
this.$message.success('删除成功');
this.loadList();
});
});
},
}
<template>
<div class="app-container">
<el-form ref="form" :model="form" :rules="rules" label-width="120px">
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="关联不合格品" prop="unqualifiedId">
<el-select v-model="form.unqualifiedId" placeholder="选择不合格品" filterable>
<el-option v-for="item in unqualifiedList" :key="item.id"
:label="item.productName + ' ' + item.model + ' (' + item.quantity + ')'"
:value="item.id" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="不合格工序" prop="unqualifiedProcess">
<el-select v-model="form.unqualifiedProcess">
<el-option label="来料" :value="1" />
<el-option label="制程" :value="2" />
<el-option label="成品" :value="3" />
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-divider>基本信息</el-divider>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="项目名称" prop="projectName">
<el-input v-model="form.projectName" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="项目编号" prop="projectNo">
<el-input v-model="form.projectNo" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="物料/部件名称" prop="materialName">
<el-input v-model="form.materialName" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="物料图号" prop="materialDrawingNo">
<el-input v-model="form.materialDrawingNo" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="8">
<el-form-item label="型号规格" prop="specificationModel">
<el-input v-model="form.specificationModel" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="材质" prop="materialQuality">
<el-input v-model="form.materialQuality" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="供应商" prop="supplierName">
<el-input v-model="form.supplierName" />
</el-form-item>
</el-col>
</el-row>
<el-divider>不合格信息</el-divider>
<el-row :gutter="20">
<el-col :span="8">
<el-form-item label="总数量" prop="quantity">
<el-input-number v-model="form.quantity" :min="0" :precision="2" style="width:100%" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="不合格数量" prop="unqualifiedQuantity">
<el-input-number v-model="form.unqualifiedQuantity" :min="0" :precision="2" style="width:100%" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="检验日期" prop="inspectDate">
<el-date-picker v-model="form.inspectDate" type="date" value-format="yyyy-MM-dd" style="width:100%" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="8">
<el-form-item label="检验员" prop="inspectorName">
<el-input v-model="form.inspectorName" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="责任人" prop="responsiblePerson">
<el-input v-model="form.responsiblePerson" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="责任部门" prop="responsibleDept">
<el-input v-model="form.responsibleDept" />
</el-form-item>
</el-col>
</el-row>
<el-form-item label="问题描述" prop="problemDescription">
<el-input v-model="form.problemDescription" type="textarea" :rows="2" />
</el-form-item>
<el-divider>处置决策</el-divider>
<el-form-item label="处置方式" prop="disposalMethod">
<el-radio-group v-model="form.disposalMethod">
<el-radio :value="1">让步接收</el-radio>
<el-radio :value="2">厂内维修</el-radio>
<el-radio :value="3">返厂维修</el-radio>
<el-radio :value="4">换货</el-radio>
<el-radio :value="5">退货</el-radio>
<el-radio :value="6">报废</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item v-if="form.disposalMethod === 2 || form.disposalMethod === 3" label="维修评估" prop="repairEvaluation">
<el-input v-model="form.repairEvaluation" type="textarea" :rows="3"
placeholder="评估维修可行性、所需工时、物料等" />
</el-form-item>
<el-form-item label="原因分析及建议" prop="reasonAnalysis">
<el-input v-model="form.reasonAnalysis" type="textarea" :rows="3" />
</el-form-item>
<el-form-item label="纠正措施" prop="correctionAction">
<el-input v-model="form.correctionAction" type="textarea" :rows="3" />
</el-form-item>
<el-form-item label="预防措施" prop="preventiveAction">
<el-input v-model="form.preventiveAction" type="textarea" :rows="3" />
</el-form-item>
<el-divider>审批意见</el-divider>
<el-form-item label="责任部门主管意见" prop="deptOpinion">
<el-input v-model="form.deptOpinion" type="textarea" :rows="2" />
</el-form-item>
<el-form-item label="公司处理决定" prop="companyDecision">
<el-input v-model="form.companyDecision" type="textarea" :rows="2" />
</el-form-item>
<el-form-item label="总经理意见" prop="generalManagerOpinion">
<el-input v-model="form.generalManagerOpinion" type="textarea" :rows="2" />
</el-form-item>
<el-divider>附件</el-divider>
<el-form-item label="附件">
<file-upload v-model="form.storageBlobDTOs" />
</el-form-item>
</el-form>
<div class="text-center">
<el-button type="primary" @click="handleSubmit">提交</el-button>
<el-button @click="handleCancel">取消</el-button>
</div>
</div>
</template>
data() {
return {
form: {
unqualifiedId: null,
disposalMethod: null,
},
rules: {
unqualifiedId: [{ required: true, message: '请选择关联不合格品', trigger: 'change' }],
disposalMethod: [{ required: true, message: '请选择处置方式', trigger: 'change' }],
},
unqualifiedList: [],
}
},
mounted() {
// 加载待处理的不合格品列表
loadUnqualifiedList({ inspectState: 0 }).then(res => this.unqualifiedList = res.rows);
if (this.$route.query.id) {
getDetail(this.$route.query.id).then(res => this.form = res.data);
}
},
methods: {
handleSubmit() {
this.$refs.form.validate(valid => {
if (!valid) return;
const api = this.form.id ? updateOrder : saveOrder;
api(this.form).then(() => {
this.$message.success(this.form.id ? '修改成功' : '新增成功');
this.$router.back();
});
});
},
handleCancel() { this.$router.back(); },
}
| 值 | 含义 | 系统自动行为 |
|---|---|---|
| 1 | 让步接收 | 无自动操作,需人工后续处理 |
| 2 | 厂内维修 | 自动创建返修生产订单(FG 开头 NPS 编号),克隆原工序路线 |
| 3 | 返厂维修 | 自动创建返修生产订单(FG 开头 NPS 编号),克隆原工序路线 |
| 4 | 换货 | 无自动操作 |
| 5 | 退货 | 无自动操作 |
| 6 | 报废 | 无自动操作(库存扣减由旧模块 /quality/qualityUnqualified/deal 处理) |
质检自动创建 ──> 0(草稿) ──> 调用 /deal 选择处置方式 ──> 3(已完成)
手动新增(没选处置方式) ──> 0(草稿) ──> 调用 /deal ──> 3(已完成)
手动新增(选了处置方式) ──> 3(已完成)
/qualityUnqualifiedOrder/deal 接口补充处置方式/quality/qualityUnqualified)继续用于不合格品首次记录和列表查看/qualityUnqualifiedOrder)disposalMethod 字段标记了处置方式,可在生产订单列表区分普通订单和返修订单BHGstorageBlobDTOs 字段上传,查询时返回 storageBlobVOs