编辑 | blame | 历史 | 原始文档

不合格品处理单模块(QualityUnqualifiedOrder)

概述

不合格品处理单是正式的不合格品处置流程模块,替代旧的不合格管理(/quality/qualityUnqualified)中的处置功能。旧模块的不合格品发现(新增/列表/详情)继续使用,但**处置操作统一使用本模块**。

自动创建机制

当检验单(原材料/过程/出厂检验)提交时,如果**不合格数量 > 0**,系统在创建 QualityUnqualified 记录的同时,**自动创建一条不合格品处理单**,unqualifiedProcess(不合格工序)根据检验类别自动映射:

检验类别 inspectType unqualifiedProcess
原材料检验 0 1(来料)
过程检验 0 2(制程)
出厂检验 0 3(成品)

处理单初始状态为 0(草稿),后续可编辑处置方式并提交审批。

与旧模块的关系

旧:不合格管理 新:不合格品处理单
路径 /quality/qualityUnqualified /qualityUnqualifiedOrder
用途 不合格品首次记录、查看 正式处置流程(含审批)
处置方式 dealResult 自由文本 disposalMethod 结构化枚举
创建方式 检验单提交自动创建 / 手动新增 检验单提交自动创建 / 手动新增

涉及页面

  • 不合格品处理单列表页
  • 不合格品处理单新增/编辑页
  • 不合格品处理单详情页

API

1. 新增处理单

方法 路径 说明
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 }

2. 修改处理单

方法 路径 说明
PUT /qualityUnqualifiedOrder/update 修改不合格品处理单

请求参数: 与新增相同,额外需要 id 字段。

3. 删除处理单

方法 路径 说明
DELETE /qualityUnqualifiedOrder/delete 删除不合格品处理单

请求参数: [id1, id2, ...] — ID 数组

4. 分页查询

方法 路径 说明
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(附件列表)。

5. 详情查询

方法 路径 说明
GET /qualityUnqualifiedOrder/{id} 处理单详情

响应: 返回单条 QualityUnqualifiedOrder 全部字段,含附件。

6. 处理(处置)

方法 路径 说明
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 }

7. 导出处理单

方法 路径 说明
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}`);
  },
}

前端修改点

1. 处理单列表页

<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();
      });
    });
  },
}

2. 新增/编辑处理单页

<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(已完成)

注意事项

  • 检验单提交时,不合格数量 > 0 会自动创建处理单(状态为草稿),**无需手动新增**
  • 手动新增处理单时,选了处置方式直接完成,没选则保持草稿
  • 草稿状态的处理单通过 /qualityUnqualifiedOrder/deal 接口补充处置方式
  • 旧的不合格管理(/quality/qualityUnqualified)继续用于不合格品首次记录和列表查看
  • 处置操作统一使用本模块(/qualityUnqualifiedOrder
  • 选择"厂内维修"或"返厂维修"时,系统自动创建返修生产订单,无需手动操作
  • 返修生产订单的 disposalMethod 字段标记了处置方式,可在生产订单列表区分普通订单和返修订单
  • 处理单编号自动生成,前缀为 BHG
  • 附件通过 storageBlobDTOs 字段上传,查询时返回 storageBlobVOs