feat: 新增质量评审流程全量功能

- 新增质量评审相关的实体、Mapper、Service、Controller接口与实现
- 新增前端页面与API接口,支持评审单增删改查、提交送审、审批驳回、改判执行
- 新增数据库初始化脚本与字典数据
- 修复vue.config热加载监视系统文件导致的EBUSY错误
- 清理HRM模块API导出注释
This commit is contained in:
王文昊
2026-06-30 17:58:21 +08:00
parent c997a4a1a0
commit 450fca0c45
25 changed files with 2816 additions and 1 deletions

View File

@@ -0,0 +1,153 @@
-- =============================================================
-- 质量评审流程 - 安装脚本
-- 用法: mysql -h 140.143.206.120 -P 13306 -u klp -p 数据库名 --default-character-set=utf8mb4 < 本文件
-- =============================================================
SET NAMES utf8mb4;
-- ========== 1. 彻底清理所有质量评审相关数据 ==========
-- 删除角色-菜单关联
DELETE FROM sys_role_menu WHERE menu_id IN (SELECT menu_id FROM sys_menu WHERE perms LIKE '%qualityReview%');
DELETE FROM sys_role_menu WHERE menu_id IN (SELECT menu_id FROM sys_menu WHERE menu_name = '质量评审' OR menu_name = '质量评审待审批');
-- 删除菜单本身
DELETE FROM sys_menu WHERE perms LIKE '%qualityReview%';
DELETE FROM sys_menu WHERE menu_name = '质量评审';
DELETE FROM sys_menu WHERE menu_name = '质量评审待审批';
-- 删除字典
DELETE FROM sys_dict_data WHERE dict_type = 'quality_review_status';
DELETE FROM sys_dict_data WHERE dict_type = 'regrade_quality_type';
DELETE FROM sys_dict_type WHERE dict_type = 'quality_review_status';
DELETE FROM sys_dict_type WHERE dict_type = 'regrade_quality_type';
-- 删表
DROP TABLE IF EXISTS qc_quality_review_log;
DROP TABLE IF EXISTS qc_quality_review_coil;
DROP TABLE IF EXISTS qc_quality_review;
-- ========== 2. 建表 ==========
CREATE TABLE IF NOT EXISTS `qc_quality_review` (
`review_id` bigint NOT NULL AUTO_INCREMENT COMMENT '评审单主键',
`review_no` varchar(50) NOT NULL COMMENT '评审单编号',
`product_name` varchar(100) DEFAULT NULL COMMENT '产品名称',
`transmit_dept` varchar(50) DEFAULT NULL COMMENT '传递部门',
`transmit_user` varchar(50) DEFAULT NULL COMMENT '传递人',
`transmit_date` date DEFAULT NULL COMMENT '传递日期',
`prod_date_range` varchar(50) DEFAULT NULL COMMENT '生产日期范围',
`flow_status` tinyint(1) DEFAULT '1' COMMENT '流程状态: 1=待提交 2=待审批 3=已通过 4=已驳回',
`dept_opinion` text COMMENT '品质部评审意见',
`dept_sign` varchar(50) DEFAULT NULL COMMENT '品质部签字人',
`dept_sign_date` date DEFAULT NULL COMMENT '品质部签字日期',
`leader_opinion` text COMMENT '领导审批意见',
`leader_sign` varchar(50) DEFAULT NULL COMMENT '领导签字人',
`leader_sign_date` date DEFAULT NULL COMMENT '领导签字日期',
`reject_reason` varchar(500) DEFAULT NULL COMMENT '驳回原因',
`remark` varchar(500) DEFAULT NULL COMMENT '备注',
`del_flag` tinyint(1) DEFAULT '0' COMMENT '删除标志',
`create_by` varchar(50) DEFAULT NULL,
`create_time` datetime DEFAULT CURRENT_TIMESTAMP,
`update_by` varchar(50) DEFAULT NULL,
`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`review_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='质量评审单';
CREATE TABLE IF NOT EXISTS `qc_quality_review_coil` (
`detail_id` bigint NOT NULL AUTO_INCREMENT COMMENT '明细主键',
`review_id` bigint NOT NULL COMMENT '关联评审单ID',
`group_seq` int DEFAULT NULL COMMENT '序号',
`group_remark` varchar(200) DEFAULT NULL COMMENT '组备注',
`coil_id` bigint DEFAULT NULL COMMENT '钢卷ID',
`current_coil_no` varchar(50) DEFAULT NULL COMMENT '产品卷号',
`supplier_coil_no` varchar(50) DEFAULT NULL COMMENT '原料卷号',
`spec` varchar(50) DEFAULT NULL COMMENT '规格',
`net_weight` decimal(10,3) DEFAULT NULL COMMENT '卷重(t)',
`defect_desc` text COMMENT '缺陷描述',
`before_quality` varchar(20) DEFAULT NULL COMMENT '改判前质量等级',
`regrade_quality` varchar(50) DEFAULT NULL COMMENT '改判后质量状态',
`execute_status` tinyint(1) DEFAULT '0' COMMENT '执行状态: 0=待执行 1=已执行',
`execute_time` datetime DEFAULT NULL COMMENT '执行时间',
`remark` varchar(500) DEFAULT NULL,
`del_flag` tinyint(1) DEFAULT '0',
`create_by` varchar(50) DEFAULT NULL,
`create_time` datetime DEFAULT CURRENT_TIMESTAMP,
`update_by` varchar(50) DEFAULT NULL,
`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`detail_id`),
KEY `idx_review_id` (`review_id`),
KEY `idx_coil_id` (`coil_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='质量评审钢卷明细';
CREATE TABLE IF NOT EXISTS `qc_quality_review_log` (
`log_id` bigint NOT NULL AUTO_INCREMENT COMMENT '日志主键',
`review_id` bigint NOT NULL COMMENT '关联评审单ID',
`action` varchar(20) NOT NULL COMMENT '动作: submit/approve/reject',
`operator` varchar(50) DEFAULT NULL COMMENT '操作人',
`opinion` text COMMENT '审批意见',
`operate_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '操作时间',
`create_by` varchar(50) DEFAULT NULL,
`create_time` datetime DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`log_id`),
KEY `idx_review_id` (`review_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='质量评审审批日志';
-- ========== 3. 字典 ==========
INSERT IGNORE INTO sys_dict_type (dict_name, dict_type, status, create_by, create_time, update_by, update_time, remark)
VALUES ('Quality Review Status', 'quality_review_status', '0', 'admin', NOW(), 'admin', NOW(), '');
INSERT IGNORE INTO sys_dict_type (dict_name, dict_type, status, create_by, create_time, update_by, update_time, remark)
VALUES ('Regrade Quality Type', 'regrade_quality_type', '0', 'admin', NOW(), 'admin', NOW(), '');
INSERT IGNORE INTO sys_dict_data (dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, status, create_by, create_time) VALUES (1, '待提交', '1', 'quality_review_status', '', 'info', 'Y', '0', 'admin', NOW());
INSERT IGNORE INTO sys_dict_data (dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, status, create_by, create_time) VALUES (2, '待审批', '2', 'quality_review_status', '', 'warning', 'Y', '0', 'admin', NOW());
INSERT IGNORE INTO sys_dict_data (dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, status, create_by, create_time) VALUES (3, '已通过', '3', 'quality_review_status', '', 'success', 'Y', '0', 'admin', NOW());
INSERT IGNORE INTO sys_dict_data (dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, status, create_by, create_time) VALUES (4, '已驳回', '4', 'quality_review_status', '', 'danger', 'Y', '0', 'admin', NOW());
INSERT IGNORE INTO sys_dict_data (dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, status, create_by, create_time) VALUES (1, '协议销售', 'protocol_sale', 'regrade_quality_type', '', '', 'Y', '0', 'admin', NOW());
INSERT IGNORE INTO sys_dict_data (dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, status, create_by, create_time) VALUES (2, '转分剪', 'to_slitting', 'regrade_quality_type', '', '', 'Y', '0', 'admin', NOW());
INSERT IGNORE INTO sys_dict_data (dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, status, create_by, create_time) VALUES (3, '降级', 'downgrade', 'regrade_quality_type', '', '', 'Y', '0', 'admin', NOW());
INSERT IGNORE INTO sys_dict_data (dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, status, create_by, create_time) VALUES (4, '返修', 'rework', 'regrade_quality_type', '', '', 'Y', '0', 'admin', NOW());
INSERT IGNORE INTO sys_dict_data (dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, status, create_by, create_time) VALUES (5, '报废', 'scrap', 'regrade_quality_type', '', '', 'Y', '0', 'admin', NOW());
-- ========== 4. 创建菜单 ==========
-- 父菜单: 质量模块 (path=quality, menu_id=2068954238598967297)
SET @parent_id = 2068954238598967297;
SET @max_order = (SELECT COALESCE(MAX(order_num), 0) FROM sys_menu WHERE parent_id = @parent_id);
SET @max_id = (SELECT COALESCE(MAX(menu_id), 0) FROM sys_menu);
SET @id1 = @max_id + 1;
SET @id2 = @max_id + 2;
SET @id3 = @max_id + 3;
SET @id4 = @max_id + 4;
SET @id5 = @max_id + 5;
SET @id6 = @max_id + 6;
SET @id7 = @max_id + 7;
SET @id8 = @max_id + 8;
-- 质量评审菜单 (type=C 菜单)
INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time)
VALUES (@id1, '质量评审', @parent_id, @max_order + 1, 'qualityReview', 'mes/qc/qualityReview/index', 1, 0, 'C', '0', '0', 'qc:qualityReview:list', 'guide', 'admin', NOW());
-- 质量评审待审批 (type=C 隐藏)
INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time)
VALUES (@id2, '质量评审待审批', @parent_id, @max_order + 2, 'qualityReviewTodo', 'mes/qc/qualityReview/todo', 1, 0, 'C', '1', '0', 'qc:qualityReview:approve', 'checkbox', 'admin', NOW());
-- 按钮权限 (type=F)
INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, menu_type, visible, status, perms, create_by, create_time)
VALUES (@id3, '质量评审新增', @id1, 1, 'F', '0', '0', 'qc:qualityReview:add', 'admin', NOW());
INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, menu_type, visible, status, perms, create_by, create_time)
VALUES (@id4, '质量评审修改', @id1, 2, 'F', '0', '0', 'qc:qualityReview:edit', 'admin', NOW());
INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, menu_type, visible, status, perms, create_by, create_time)
VALUES (@id5, '质量评审删除', @id1, 3, 'F', '0', '0', 'qc:qualityReview:delete', 'admin', NOW());
INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, menu_type, visible, status, perms, create_by, create_time)
VALUES (@id6, '质量评审提交送审', @id1, 4, 'F', '0', '0', 'qc:qualityReview:submit', 'admin', NOW());
INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, menu_type, visible, status, perms, create_by, create_time)
VALUES (@id7, '质量评审审批', @id1, 5, 'F', '0', '0', 'qc:qualityReview:approve', 'admin', NOW());
INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, menu_type, visible, status, perms, create_by, create_time)
VALUES (@id8, '质量评审执行改判', @id1, 6, 'F', '0', '0', 'qc:qualityReview:execute', 'admin', NOW());
-- ========== 5. 授权 admin 角色 ==========
INSERT IGNORE INTO sys_role_menu (role_id, menu_id) VALUES
(1, @parent_id), (1, @id1), (1, @id2),
(1, @id3), (1, @id4), (1, @id5),
(1, @id6), (1, @id7), (1, @id8);
-- ========== 6. 验证 ==========
SELECT CONCAT('Tables: ', COUNT(*)) AS result FROM information_schema.TABLES WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME LIKE 'qc_quality_review%';
SELECT CONCAT('Dict: ', dict_type) AS result FROM sys_dict_type WHERE dict_type = 'quality_review_status';
SELECT CONCAT('Menu: ', menu_name, ' -> parent_id=', parent_id) AS result FROM sys_menu WHERE perms = 'qc:qualityReview:list';

View File

@@ -0,0 +1,105 @@
-- ------------------------------------------------------------
-- 质量评审流程 - 异常产品评审审批单
-- V15__qc_quality_review.sql
-- ------------------------------------------------------------
-- 1. 字典类型
INSERT INTO `sys_dict_type` VALUES (NULL, '质量评审状态', 'quality_review_status', '0', 'admin', NOW(), '', NULL, '质量评审单流程状态');
INSERT INTO `sys_dict_type` VALUES (NULL, '改判后质量状态', 'regrade_quality_type', '0', 'admin', NOW(), '', NULL, '领导审批时指定的改判结果');
-- 2. 字典数据 - 质量评审状态
INSERT INTO `sys_dict_data` VALUES (NULL, 1, '待提交', '1', 'quality_review_status', '', 'info', 'Y', '0', 'admin', NOW(), '', NULL, '');
INSERT INTO `sys_dict_data` VALUES (NULL, 2, '待审批', '2', 'quality_review_status', '', 'warning', 'Y', '0', 'admin', NOW(), '', NULL, '');
INSERT INTO `sys_dict_data` VALUES (NULL, 3, '已通过', '3', 'quality_review_status', '', 'success', 'Y', '0', 'admin', NOW(), '', NULL, '');
INSERT INTO `sys_dict_data` VALUES (NULL, 4, '已驳回', '4', 'quality_review_status', '', 'danger', 'Y', '0', 'admin', NOW(), '', NULL, '');
-- 3. 字典数据 - 改判后质量状态
INSERT INTO `sys_dict_data` VALUES (NULL, 1, '协议销售', 'protocol_sale', 'regrade_quality_type', '', '', 'Y', '0', 'admin', NOW(), '', NULL, '');
INSERT INTO `sys_dict_data` VALUES (NULL, 2, '转分剪', 'to_slitting', 'regrade_quality_type', '', '', 'Y', '0', 'admin', NOW(), '', NULL, '');
INSERT INTO `sys_dict_data` VALUES (NULL, 3, '降级', 'downgrade', 'regrade_quality_type', '', '', 'Y', '0', 'admin', NOW(), '', NULL, '');
INSERT INTO `sys_dict_data` VALUES (NULL, 4, '返修', 'rework', 'regrade_quality_type', '', '', 'Y', '0', 'admin', NOW(), '', NULL, '');
INSERT INTO `sys_dict_data` VALUES (NULL, 5, '报废', 'scrap', 'regrade_quality_type', '', '', 'Y', '0', 'admin', NOW(), '', NULL, '');
-- 4. 质量评审主表
CREATE TABLE `qc_quality_review` (
`review_id` bigint NOT NULL AUTO_INCREMENT COMMENT '评审单主键',
`review_no` varchar(50) NOT NULL COMMENT '评审单编号QR-2026-XXXX',
`product_name` varchar(100) DEFAULT NULL COMMENT '产品名称',
`transmit_dept` varchar(50) DEFAULT '品质部' COMMENT '传递部门',
`transmit_user` varchar(50) DEFAULT NULL COMMENT '传递人',
`transmit_date` date DEFAULT NULL COMMENT '传递日期',
`prod_date_range` varchar(50) DEFAULT NULL COMMENT '生产日期范围',
-- 流程状态
`flow_status` tinyint(1) DEFAULT '1' COMMENT '流程状态1=待提交 2=待审批 3=已通过 4=已驳回',
-- 品质部评审
`dept_opinion` text COMMENT '品质部评审意见',
`dept_sign` varchar(50) DEFAULT NULL COMMENT '品质部签字人',
`dept_sign_date` date DEFAULT NULL COMMENT '品质部签字日期',
-- 领导审批
`leader_opinion` text COMMENT '领导审批意见',
`leader_sign` varchar(50) DEFAULT NULL COMMENT '领导签字人',
`leader_sign_date` date DEFAULT NULL COMMENT '领导签字日期',
`reject_reason` varchar(500) DEFAULT NULL COMMENT '驳回原因',
-- 公共基础字段
`remark` varchar(500) DEFAULT NULL COMMENT '备注',
`del_flag` tinyint(1) DEFAULT '0' COMMENT '删除标志0=正常1=已删除)',
`create_by` varchar(50) DEFAULT '' COMMENT '创建人',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_by` varchar(50) DEFAULT '' COMMENT '更新人',
`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`review_id`) USING BTREE,
UNIQUE KEY `uk_review_no` (`review_no`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='质量评审单';
-- 5. 质量评审钢卷明细表
CREATE TABLE `qc_quality_review_coil` (
`detail_id` bigint NOT NULL AUTO_INCREMENT COMMENT '明细主键',
`review_id` bigint NOT NULL COMMENT '关联评审单ID',
-- 钢卷分组信息
`group_seq` int DEFAULT NULL COMMENT '序号1,2,3...',
`group_remark` varchar(200) DEFAULT NULL COMMENT '组备注(如"含3个钢卷"',
-- 钢卷信息
`coil_id` bigint DEFAULT NULL COMMENT '钢卷ID关联wms_material_coil',
`current_coil_no` varchar(50) DEFAULT NULL COMMENT '产品卷号',
`supplier_coil_no` varchar(50) DEFAULT NULL COMMENT '原料卷号',
`spec` varchar(50) DEFAULT NULL COMMENT '规格如1.35×1250',
`net_weight` decimal(10,3) DEFAULT NULL COMMENT '卷重(t)',
-- 缺陷与改判
`defect_desc` text COMMENT '缺陷描述',
`before_quality` varchar(20) DEFAULT NULL COMMENT '改判前质量等级(系统自动带出)',
`regrade_quality` varchar(50) DEFAULT NULL COMMENT '改判后质量状态字典regrade_quality_type',
`execute_status` tinyint(1) DEFAULT '0' COMMENT '执行状态0=待执行 1=已执行',
`execute_time` datetime DEFAULT NULL COMMENT '执行时间',
-- 公共基础字段
`remark` varchar(500) DEFAULT NULL,
`del_flag` tinyint(1) DEFAULT '0',
`create_by` varchar(50) DEFAULT '',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP,
`update_by` varchar(50) DEFAULT '',
`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`detail_id`) USING BTREE,
KEY `idx_review_id` (`review_id`) USING BTREE,
KEY `idx_coil_id` (`coil_id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='质量评审钢卷明细';
-- 6. 质量评审审批日志表
CREATE TABLE `qc_quality_review_log` (
`log_id` bigint NOT NULL AUTO_INCREMENT COMMENT '日志主键',
`review_id` bigint NOT NULL COMMENT '关联评审单ID',
`action` varchar(20) NOT NULL COMMENT '动作submit=提交 approve=通过 reject=驳回',
`operator` varchar(50) DEFAULT NULL COMMENT '操作人',
`opinion` text COMMENT '审批意见',
`operate_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '操作时间',
`create_by` varchar(50) DEFAULT '',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`log_id`) USING BTREE,
KEY `idx_review_id` (`review_id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='质量评审审批日志';

View File

@@ -0,0 +1,159 @@
package com.klp.mes.qc.controller;
import com.klp.common.annotation.Log;
import com.klp.common.annotation.RepeatSubmit;
import com.klp.common.core.controller.BaseController;
import com.klp.common.core.domain.PageQuery;
import com.klp.common.core.domain.R;
import com.klp.common.core.page.TableDataInfo;
import com.klp.common.core.validate.AddGroup;
import com.klp.common.core.validate.EditGroup;
import com.klp.common.enums.BusinessType;
import com.klp.common.utils.poi.ExcelUtil;
import com.klp.mes.qc.domain.bo.QcQualityReviewApproveBo;
import com.klp.mes.qc.domain.bo.QcQualityReviewBo;
import com.klp.mes.qc.domain.vo.QcQualityReviewCoilVo;
import com.klp.mes.qc.domain.vo.QcQualityReviewLogVo;
import com.klp.mes.qc.domain.vo.QcQualityReviewVo;
import com.klp.mes.qc.service.IQcQualityReviewService;
import lombok.RequiredArgsConstructor;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletResponse;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.util.Arrays;
import java.util.List;
/**
* 质量评审单Controller
*
* @author klp
* @date 2026-06-30
*/
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/qc/qualityReview")
public class QcQualityReviewController extends BaseController {
private final IQcQualityReviewService iQcQualityReviewService;
/**
* 查询质量评审单列表
*/
@GetMapping("/list")
public TableDataInfo<QcQualityReviewVo> list(QcQualityReviewBo bo, PageQuery pageQuery) {
return iQcQualityReviewService.queryPageList(bo, pageQuery);
}
/**
* 导出质量评审单列表
*/
@Log(title = "质量评审单", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(QcQualityReviewBo bo, HttpServletResponse response) {
List<QcQualityReviewVo> list = iQcQualityReviewService.queryList(bo);
ExcelUtil.exportExcel(list, "质量评审单", QcQualityReviewVo.class, response);
}
/**
* 获取质量评审单详细信息
*/
@GetMapping("/{reviewId}")
public R<QcQualityReviewVo> getInfo(@NotNull(message = "主键不能为空")
@PathVariable Long reviewId) {
return R.ok(iQcQualityReviewService.queryById(reviewId));
}
/**
* 新增质量评审单
*/
@Log(title = "质量评审单", businessType = BusinessType.INSERT)
@RepeatSubmit()
@PostMapping()
public R<Void> add(@Validated(AddGroup.class) @RequestBody QcQualityReviewBo bo) {
return toAjax(iQcQualityReviewService.insertByBo(bo));
}
/**
* 修改质量评审单
*/
@Log(title = "质量评审单", businessType = BusinessType.UPDATE)
@RepeatSubmit()
@PutMapping()
public R<Void> edit(@Validated(EditGroup.class) @RequestBody QcQualityReviewBo bo) {
return toAjax(iQcQualityReviewService.updateByBo(bo));
}
/**
* 删除质量评审单
*/
@Log(title = "质量评审单", businessType = BusinessType.DELETE)
@DeleteMapping("/{reviewIds}")
public R<Void> remove(@NotEmpty(message = "主键不能为空")
@PathVariable Long[] reviewIds) {
return toAjax(iQcQualityReviewService.deleteWithValidByIds(Arrays.asList(reviewIds), true));
}
// ==================== 业务操作 ====================
/**
* 提交送审
*/
@Log(title = "质量评审单", businessType = BusinessType.UPDATE)
@PostMapping("/submit/{reviewId}")
public R<Void> submit(@NotNull(message = "主键不能为空")
@PathVariable Long reviewId) {
return toAjax(iQcQualityReviewService.submit(reviewId));
}
/**
* 审批通过
*/
@Log(title = "质量评审单", businessType = BusinessType.UPDATE)
@PostMapping("/approve")
public R<Void> approve(@Validated @RequestBody QcQualityReviewApproveBo bo) {
return toAjax(iQcQualityReviewService.approve(bo));
}
/**
* 驳回
*/
@Log(title = "质量评审单", businessType = BusinessType.UPDATE)
@PostMapping("/reject/{reviewId}")
public R<Void> reject(@NotNull(message = "主键不能为空")
@PathVariable Long reviewId,
@RequestParam String reason) {
return toAjax(iQcQualityReviewService.reject(reviewId, reason));
}
/**
* 执行改判
*/
@Log(title = "质量评审单", businessType = BusinessType.UPDATE)
@PostMapping("/execute/{reviewId}")
public R<Void> execute(@NotNull(message = "主键不能为空")
@PathVariable Long reviewId) {
return toAjax(iQcQualityReviewService.execute(reviewId));
}
/**
* 查询钢卷明细列表
*/
@GetMapping("/coilList/{reviewId}")
public R<List<QcQualityReviewCoilVo>> coilList(@NotNull(message = "主键不能为空")
@PathVariable Long reviewId) {
return R.ok(iQcQualityReviewService.queryCoilListByReviewId(reviewId));
}
/**
* 查询审批日志列表
*/
@GetMapping("/logList/{reviewId}")
public R<List<QcQualityReviewLogVo>> logList(@NotNull(message = "主键不能为空")
@PathVariable Long reviewId) {
return R.ok(iQcQualityReviewService.queryLogListByReviewId(reviewId));
}
}

View File

@@ -0,0 +1,76 @@
package com.klp.mes.qc.domain;
import com.baomidou.mybatisplus.annotation.*;
import com.klp.common.core.domain.BaseEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.Date;
/**
* 质量评审单对象 qc_quality_review
*
* @author klp
* @date 2026-06-30
*/
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("qc_quality_review")
public class QcQualityReview extends BaseEntity {
private static final long serialVersionUID = 1L;
/** 评审单主键 */
@TableId(value = "review_id")
private Long reviewId;
/** 评审单编号 */
private String reviewNo;
/** 产品名称 */
private String productName;
/** 传递部门 */
private String transmitDept;
/** 传递人 */
private String transmitUser;
/** 传递日期 */
private Date transmitDate;
/** 生产日期范围 */
private String prodDateRange;
/** 流程状态1=待提交 2=待审批 3=已通过 4=已驳回 */
private Long flowStatus;
/** 品质部评审意见 */
private String deptOpinion;
/** 品质部签字人 */
private String deptSign;
/** 品质部签字日期 */
private Date deptSignDate;
/** 领导审批意见 */
private String leaderOpinion;
/** 领导签字人 */
private String leaderSign;
/** 领导签字日期 */
private Date leaderSignDate;
/** 驳回原因 */
private String rejectReason;
/** 备注 */
private String remark;
/** 删除标志0=正常1=已删除) */
@TableLogic
private Integer delFlag;
}

View File

@@ -0,0 +1,74 @@
package com.klp.mes.qc.domain;
import com.baomidou.mybatisplus.annotation.*;
import com.klp.common.core.domain.BaseEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.math.BigDecimal;
import java.util.Date;
/**
* 质量评审钢卷明细对象 qc_quality_review_coil
*
* @author klp
* @date 2026-06-30
*/
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("qc_quality_review_coil")
public class QcQualityReviewCoil extends BaseEntity {
private static final long serialVersionUID = 1L;
/** 明细主键 */
@TableId(value = "detail_id")
private Long detailId;
/** 关联评审单ID */
private Long reviewId;
/** 序号 */
private Integer groupSeq;
/** 组备注 */
private String groupRemark;
/** 钢卷ID */
private Long coilId;
/** 产品卷号 */
private String currentCoilNo;
/** 原料卷号 */
private String supplierCoilNo;
/** 规格 */
private String spec;
/** 卷重(t) */
private BigDecimal netWeight;
/** 缺陷描述 */
private String defectDesc;
/** 改判前质量等级 */
private String beforeQuality;
/** 改判后质量状态字典regrade_quality_type */
private String regradeQuality;
/** 执行状态0=待执行 1=已执行 */
private Long executeStatus;
/** 执行时间 */
private Date executeTime;
/** 备注 */
private String remark;
/** 删除标志0=正常1=已删除) */
@TableLogic
private Integer delFlag;
}

View File

@@ -0,0 +1,45 @@
package com.klp.mes.qc.domain;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import java.util.Date;
/**
* 质量评审审批日志对象 qc_quality_review_log
*
* @author klp
* @date 2026-06-30
*/
@Data
@TableName("qc_quality_review_log")
public class QcQualityReviewLog {
private static final long serialVersionUID = 1L;
/** 日志主键 */
@TableId(value = "log_id")
private Long logId;
/** 关联评审单ID */
private Long reviewId;
/** 动作submit=提交 approve=通过 reject=驳回 */
private String action;
/** 操作人 */
private String operator;
/** 审批意见 */
private String opinion;
/** 操作时间 */
private Date operateTime;
/** 创建人 */
private String createBy;
/** 创建时间 */
private Date createTime;
}

View File

@@ -0,0 +1,41 @@
package com.klp.mes.qc.domain.bo;
import lombok.Data;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.util.List;
/**
* 质量评审审批参数(领导审批时使用)
*
* @author klp
* @date 2026-06-30
*/
@Data
public class QcQualityReviewApproveBo {
/** 评审单ID */
@NotNull(message = "评审单ID不能为空")
private Long reviewId;
/** 领导审批意见 */
private String leaderOpinion;
/** 领导签字人 */
private String leaderSign;
/** 各钢卷的改判状态 */
private List<CoilRegradeBo> coilRegradeList;
@Data
public static class CoilRegradeBo {
/** 明细ID */
@NotNull(message = "钢卷明细ID不能为空")
private Long detailId;
/** 改判后质量状态 */
@NotBlank(message = "改判后质量状态不能为空")
private String regradeQuality;
}
}

View File

@@ -0,0 +1,85 @@
package com.klp.mes.qc.domain.bo;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.klp.common.core.domain.BaseEntity;
import com.klp.common.core.validate.AddGroup;
import com.klp.common.core.validate.EditGroup;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.springframework.format.annotation.DateTimeFormat;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.util.Date;
import java.util.List;
/**
* 质量评审单业务对象 qc_quality_review
*
* @author klp
* @date 2026-06-30
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class QcQualityReviewBo extends BaseEntity {
/** 评审单主键 */
@NotNull(message = "主键不能为空", groups = {EditGroup.class})
private Long reviewId;
/** 评审单编号 */
private String reviewNo;
/** 产品名称 */
@NotBlank(message = "产品名称不能为空", groups = {AddGroup.class})
private String productName;
/** 传递部门 */
private String transmitDept;
/** 传递人 */
private String transmitUser;
/** 传递日期 */
@JsonFormat(pattern = "yyyy-MM-dd")
@DateTimeFormat(pattern = "yyyy-MM-dd")
private Date transmitDate;
/** 生产日期范围 */
private String prodDateRange;
/** 流程状态 */
private Long flowStatus;
/** 品质部评审意见 */
private String deptOpinion;
/** 品质部签字人 */
private String deptSign;
/** 品质部签字日期 */
@JsonFormat(pattern = "yyyy-MM-dd")
@DateTimeFormat(pattern = "yyyy-MM-dd")
private Date deptSignDate;
/** 领导审批意见 */
private String leaderOpinion;
/** 领导签字人 */
private String leaderSign;
/** 领导签字日期 */
@JsonFormat(pattern = "yyyy-MM-dd")
@DateTimeFormat(pattern = "yyyy-MM-dd")
private Date leaderSignDate;
/** 驳回原因 */
private String rejectReason;
/** 备注 */
private String remark;
/** 钢卷明细列表 */
private List<QcQualityReviewCoilBo> coilList;
}

View File

@@ -0,0 +1,67 @@
package com.klp.mes.qc.domain.bo;
import com.klp.common.core.domain.BaseEntity;
import com.klp.common.core.validate.AddGroup;
import com.klp.common.core.validate.EditGroup;
import lombok.Data;
import lombok.EqualsAndHashCode;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.math.BigDecimal;
/**
* 质量评审钢卷明细业务对象 qc_quality_review_coil
*
* @author klp
* @date 2026-06-30
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class QcQualityReviewCoilBo extends BaseEntity {
/** 明细主键 */
@NotNull(message = "主键不能为空", groups = {EditGroup.class})
private Long detailId;
/** 关联评审单ID */
private Long reviewId;
/** 序号 */
private Integer groupSeq;
/** 组备注 */
private String groupRemark;
/** 钢卷ID */
@NotNull(message = "钢卷不能为空", groups = {AddGroup.class})
private Long coilId;
/** 产品卷号 */
private String currentCoilNo;
/** 原料卷号 */
private String supplierCoilNo;
/** 规格 */
private String spec;
/** 卷重(t) */
private BigDecimal netWeight;
/** 缺陷描述 */
private String defectDesc;
/** 改判前质量等级 */
private String beforeQuality;
/** 改判后质量状态 */
private String regradeQuality;
/** 执行状态 */
private Long executeStatus;
/** 备注 */
private String remark;
}

View File

@@ -0,0 +1,72 @@
package com.klp.mes.qc.domain.vo;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.klp.common.core.domain.BaseEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.math.BigDecimal;
import java.util.Date;
/**
* 质量评审钢卷明细视图对象 qc_quality_review_coil
*
* @author klp
* @date 2026-06-30
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class QcQualityReviewCoilVo extends BaseEntity {
private static final long serialVersionUID = 1L;
/** 明细主键 */
private Long detailId;
/** 关联评审单ID */
private Long reviewId;
/** 序号 */
private Integer groupSeq;
/** 组备注 */
private String groupRemark;
/** 钢卷ID */
private Long coilId;
/** 产品卷号 */
private String currentCoilNo;
/** 原料卷号 */
private String supplierCoilNo;
/** 规格 */
private String spec;
/** 卷重(t) */
private BigDecimal netWeight;
/** 缺陷描述 */
private String defectDesc;
/** 改判前质量等级 */
private String beforeQuality;
/** 改判后质量状态 */
private String regradeQuality;
/** 执行状态 */
private Long executeStatus;
/** 执行时间 */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date executeTime;
/** 备注 */
private String remark;
/** 删除标志 */
private Integer delFlag;
}

View File

@@ -0,0 +1,40 @@
package com.klp.mes.qc.domain.vo;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import java.util.Date;
/**
* 质量评审审批日志视图对象
*
* @author klp
* @date 2026-06-30
*/
@Data
public class QcQualityReviewLogVo {
/** 日志主键 */
private Long logId;
/** 关联评审单ID */
private Long reviewId;
/** 动作submit=提交 approve=通过 reject=驳回 */
private String action;
/** 操作人 */
private String operator;
/** 审批意见 */
private String opinion;
/** 操作时间 */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date operateTime;
/** 创建时间 */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date createTime;
}

View File

@@ -0,0 +1,83 @@
package com.klp.mes.qc.domain.vo;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.klp.common.core.domain.BaseEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.Date;
import java.util.List;
/**
* 质量评审单视图对象 qc_quality_review
*
* @author klp
* @date 2026-06-30
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class QcQualityReviewVo extends BaseEntity {
private static final long serialVersionUID = 1L;
/** 评审单主键 */
private Long reviewId;
/** 评审单编号 */
private String reviewNo;
/** 产品名称 */
private String productName;
/** 传递部门 */
private String transmitDept;
/** 传递人 */
private String transmitUser;
/** 传递日期 */
@JsonFormat(pattern = "yyyy-MM-dd")
private Date transmitDate;
/** 生产日期范围 */
private String prodDateRange;
/** 流程状态 */
private Long flowStatus;
/** 品质部评审意见 */
private String deptOpinion;
/** 品质部签字人 */
private String deptSign;
/** 品质部签字日期 */
@JsonFormat(pattern = "yyyy-MM-dd")
private Date deptSignDate;
/** 领导审批意见 */
private String leaderOpinion;
/** 领导签字人 */
private String leaderSign;
/** 领导签字日期 */
@JsonFormat(pattern = "yyyy-MM-dd")
private Date leaderSignDate;
/** 驳回原因 */
private String rejectReason;
/** 备注 */
private String remark;
/** 删除标志 */
private Integer delFlag;
/** 钢卷明细列表 */
private List<QcQualityReviewCoilVo> coilList;
/** 审批日志列表 */
private List<QcQualityReviewLogVo> logList;
}

View File

@@ -0,0 +1,22 @@
package com.klp.mes.qc.mapper;
import com.klp.mes.qc.domain.QcQualityReviewCoil;
import com.klp.mes.qc.domain.vo.QcQualityReviewCoilVo;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import java.util.List;
/**
* 质量评审钢卷明细Mapper接口
*
* @author klp
* @date 2026-06-30
*/
public interface QcQualityReviewCoilMapper extends BaseMapper<QcQualityReviewCoil> {
/**
* 根据评审单ID查询钢卷明细列表
*/
List<QcQualityReviewCoilVo> selectVoListByReviewId(Long reviewId);
}

View File

@@ -0,0 +1,22 @@
package com.klp.mes.qc.mapper;
import com.klp.mes.qc.domain.QcQualityReviewLog;
import com.klp.mes.qc.domain.vo.QcQualityReviewLogVo;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import java.util.List;
/**
* 质量评审审批日志Mapper接口
*
* @author klp
* @date 2026-06-30
*/
public interface QcQualityReviewLogMapper extends BaseMapper<QcQualityReviewLog> {
/**
* 根据评审单ID查询审批日志列表
*/
List<QcQualityReviewLogVo> selectVoListByReviewId(Long reviewId);
}

View File

@@ -0,0 +1,35 @@
package com.klp.mes.qc.mapper;
import com.klp.mes.qc.domain.QcQualityReview;
import com.klp.mes.qc.domain.vo.QcQualityReviewVo;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* 质量评审单Mapper接口
*
* @author klp
* @date 2026-06-30
*/
public interface QcQualityReviewMapper extends BaseMapper<QcQualityReview> {
/**
* 分页查询评审单列表(含状态字典值)
*/
Page<QcQualityReviewVo> selectVoPage(IPage<?> page, @Param("vo") QcQualityReviewVo vo);
/**
* 查询评审单详情
*/
QcQualityReviewVo selectVoById(@Param("reviewId") Long reviewId);
/**
* 查询评审单列表
*/
List<QcQualityReviewVo> selectVoList(@Param("vo") QcQualityReviewVo vo);
}

View File

@@ -0,0 +1,82 @@
package com.klp.mes.qc.service;
import com.klp.common.core.domain.PageQuery;
import com.klp.common.core.page.TableDataInfo;
import com.klp.mes.qc.domain.bo.QcQualityReviewApproveBo;
import com.klp.mes.qc.domain.bo.QcQualityReviewBo;
import com.klp.mes.qc.domain.vo.QcQualityReviewVo;
import com.klp.mes.qc.domain.vo.QcQualityReviewCoilVo;
import com.klp.mes.qc.domain.vo.QcQualityReviewLogVo;
import java.util.Collection;
import java.util.List;
/**
* 质量评审单Service接口
*
* @author klp
* @date 2026-06-30
*/
public interface IQcQualityReviewService {
/**
* 查询评审单详情
*/
QcQualityReviewVo queryById(Long reviewId);
/**
* 分页查询评审单列表
*/
TableDataInfo<QcQualityReviewVo> queryPageList(QcQualityReviewBo bo, PageQuery pageQuery);
/**
* 查询评审单列表
*/
List<QcQualityReviewVo> queryList(QcQualityReviewBo bo);
/**
* 创建评审单
*/
Boolean insertByBo(QcQualityReviewBo bo);
/**
* 修改评审单
*/
Boolean updateByBo(QcQualityReviewBo bo);
/**
* 批量删除评审单
*/
Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
/**
* 提交送审flowStatus: 1→2
*/
Boolean submit(Long reviewId);
/**
* 审批通过flowStatus: 2→3
*/
Boolean approve(QcQualityReviewApproveBo bo);
/**
* 驳回flowStatus: 2→4
*/
Boolean reject(Long reviewId, String reason);
/**
* 执行改判(写改判记录 + 更新钢卷状态)
*/
Boolean execute(Long reviewId);
/**
* 查询钢卷明细列表
*/
List<QcQualityReviewCoilVo> queryCoilListByReviewId(Long reviewId);
/**
* 查询审批日志列表
*/
List<QcQualityReviewLogVo> queryLogListByReviewId(Long reviewId);
}

View File

@@ -0,0 +1,389 @@
package com.klp.mes.qc.service.impl;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.klp.common.core.domain.PageQuery;
import com.klp.common.core.page.TableDataInfo;
import com.klp.common.utils.StringUtils;
import com.klp.domain.WmsCoilQualityRejudge;
import com.klp.domain.WmsMaterialCoil;
import com.klp.mapper.WmsCoilQualityRejudgeMapper;
import com.klp.mapper.WmsMaterialCoilMapper;
import com.klp.mes.qc.domain.QcQualityReview;
import com.klp.mes.qc.domain.QcQualityReviewCoil;
import com.klp.mes.qc.domain.QcQualityReviewLog;
import com.klp.mes.qc.domain.bo.QcQualityReviewApproveBo;
import com.klp.mes.qc.domain.bo.QcQualityReviewBo;
import com.klp.mes.qc.domain.bo.QcQualityReviewCoilBo;
import com.klp.mes.qc.domain.vo.QcQualityReviewCoilVo;
import com.klp.mes.qc.domain.vo.QcQualityReviewLogVo;
import com.klp.mes.qc.domain.vo.QcQualityReviewVo;
import com.klp.mes.qc.mapper.QcQualityReviewCoilMapper;
import com.klp.mes.qc.mapper.QcQualityReviewLogMapper;
import com.klp.mes.qc.mapper.QcQualityReviewMapper;
import com.klp.mes.qc.service.IQcQualityReviewService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.*;
import java.util.stream.Collectors;
/**
* 质量评审单Service业务层处理
*
* @author klp
* @date 2026-06-30
*/
@Slf4j
@RequiredArgsConstructor
@Service
public class QcQualityReviewServiceImpl implements IQcQualityReviewService {
private final QcQualityReviewMapper baseMapper;
private final QcQualityReviewCoilMapper coilMapper;
private final QcQualityReviewLogMapper logMapper;
private final WmsMaterialCoilMapper wmsMaterialCoilMapper;
private final WmsCoilQualityRejudgeMapper wmsCoilQualityRejudgeMapper;
/**
* 查询评审单详情(含钢卷明细和审批日志)
*/
@Override
public QcQualityReviewVo queryById(Long reviewId) {
QcQualityReviewVo vo = baseMapper.selectVoById(reviewId);
if (vo != null) {
vo.setCoilList(coilMapper.selectVoListByReviewId(reviewId));
vo.setLogList(logMapper.selectVoListByReviewId(reviewId));
}
return vo;
}
/**
* 分页查询评审单列表
*/
@Override
public TableDataInfo<QcQualityReviewVo> queryPageList(QcQualityReviewBo bo, PageQuery pageQuery) {
QcQualityReviewVo queryVo = new QcQualityReviewVo();
BeanUtil.copyProperties(bo, queryVo);
Page<QcQualityReviewVo> result = baseMapper.selectVoPage(pageQuery.build(), queryVo);
return TableDataInfo.build(result);
}
/**
* 查询评审单列表
*/
@Override
public List<QcQualityReviewVo> queryList(QcQualityReviewBo bo) {
QcQualityReviewVo queryVo = new QcQualityReviewVo();
BeanUtil.copyProperties(bo, queryVo);
return baseMapper.selectVoList(queryVo);
}
/**
* 创建评审单(草稿状态)
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean insertByBo(QcQualityReviewBo bo) {
// 1. 保存主表
QcQualityReview add = BeanUtil.toBean(bo, QcQualityReview.class);
// 设置默认值
add.setFlowStatus(1L); // 1=待提交
add.setReviewNo(generateReviewNo());
if (StringUtils.isBlank(add.getTransmitDept())) {
add.setTransmitDept("品质部");
}
validEntityBeforeSave(add);
boolean flag = baseMapper.insert(add) > 0;
if (flag) {
bo.setReviewId(add.getReviewId());
// 2. 保存钢卷明细
if (CollUtil.isNotEmpty(bo.getCoilList())) {
saveCoilList(add.getReviewId(), bo.getCoilList());
}
}
return flag;
}
/**
* 修改评审单(仅草稿状态可修改)
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean updateByBo(QcQualityReviewBo bo) {
QcQualityReview update = BeanUtil.toBean(bo, QcQualityReview.class);
validEntityBeforeSave(update);
boolean flag = baseMapper.updateById(update) > 0;
if (flag) {
// 先删除原有明细,再重新插入
coilMapper.delete(Wrappers.<QcQualityReviewCoil>lambdaQuery()
.eq(QcQualityReviewCoil::getReviewId, bo.getReviewId()));
if (CollUtil.isNotEmpty(bo.getCoilList())) {
saveCoilList(bo.getReviewId(), bo.getCoilList());
}
}
return flag;
}
/**
* 批量删除评审单
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
if (isValid) {
// 校验:只能删除待提交状态的
for (Long id : ids) {
QcQualityReview review = baseMapper.selectById(id);
if (review != null && !Long.valueOf(1L).equals(review.getFlowStatus())) {
throw new RuntimeException("评审单【" + review.getReviewNo() + "】不是待提交状态,无法删除");
}
}
}
// 级联删除钢卷明细
coilMapper.delete(Wrappers.<QcQualityReviewCoil>lambdaQuery()
.in(QcQualityReviewCoil::getReviewId, ids));
// 删除审批日志
logMapper.delete(Wrappers.<QcQualityReviewLog>lambdaQuery()
.in(QcQualityReviewLog::getReviewId, ids));
// 删除主表
return baseMapper.deleteBatchIds(ids) > 0;
}
// ==================== 业务操作 ====================
/**
* 提交送审(允许待提交→待审批,已驳回→待审批)
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean submit(Long reviewId) {
QcQualityReview review = baseMapper.selectById(reviewId);
if (review == null) {
throw new RuntimeException("评审单不存在");
}
Long status = review.getFlowStatus();
if (!Long.valueOf(1L).equals(status) && !Long.valueOf(4L).equals(status)) {
throw new RuntimeException("当前状态不允许送审");
}
// 校验:必须有品质部意见
if (StringUtils.isBlank(review.getDeptOpinion())) {
throw new RuntimeException("请先填写品质部评审意见");
}
// 校验:至少有一个钢卷
Long coilCount = coilMapper.selectCount(
Wrappers.<QcQualityReviewCoil>lambdaQuery()
.eq(QcQualityReviewCoil::getReviewId, reviewId)
.eq(QcQualityReviewCoil::getDelFlag, 0));
if (coilCount == null || coilCount == 0) {
throw new RuntimeException("请至少添加一个钢卷");
}
// 更新流程状态为待审批,清除驳回原因
baseMapper.update(null, Wrappers.<QcQualityReview>lambdaUpdate()
.eq(QcQualityReview::getReviewId, reviewId)
.set(QcQualityReview::getFlowStatus, 2L)
.set(QcQualityReview::getRejectReason, null));
// 记录审批日志
addLog(reviewId, "submit", "提交送审", null);
return true;
}
/**
* 审批通过
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean approve(QcQualityReviewApproveBo bo) {
QcQualityReview review = baseMapper.selectById(bo.getReviewId());
if (review == null) {
throw new RuntimeException("评审单不存在");
}
if (!Long.valueOf(2L).equals(review.getFlowStatus())) {
throw new RuntimeException("只有待审批状态的评审单才能审批");
}
if (CollUtil.isEmpty(bo.getCoilRegradeList())) {
throw new RuntimeException("请为每个钢卷指定改判后质量状态");
}
// 1. 更新主表流程状态
baseMapper.update(null, Wrappers.<QcQualityReview>lambdaUpdate()
.eq(QcQualityReview::getReviewId, bo.getReviewId())
.set(QcQualityReview::getFlowStatus, 3L)
.set(QcQualityReview::getLeaderOpinion, bo.getLeaderOpinion())
.set(QcQualityReview::getLeaderSign, bo.getLeaderSign())
.set(QcQualityReview::getLeaderSignDate, new java.sql.Date(System.currentTimeMillis())));
// 2. 更新每个钢卷的改判状态
for (QcQualityReviewApproveBo.CoilRegradeBo regrade : bo.getCoilRegradeList()) {
coilMapper.update(null, Wrappers.<QcQualityReviewCoil>lambdaUpdate()
.eq(QcQualityReviewCoil::getDetailId, regrade.getDetailId())
.set(QcQualityReviewCoil::getRegradeQuality, regrade.getRegradeQuality()));
}
// 3. 记录审批日志
addLog(bo.getReviewId(), "approve", bo.getLeaderOpinion(), bo.getLeaderSign());
return true;
}
/**
* 驳回
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean reject(Long reviewId, String reason) {
QcQualityReview review = baseMapper.selectById(reviewId);
if (review == null) {
throw new RuntimeException("评审单不存在");
}
if (!Long.valueOf(2L).equals(review.getFlowStatus())) {
throw new RuntimeException("只有待审批状态的评审单才能驳回");
}
if (StringUtils.isBlank(reason)) {
throw new RuntimeException("请填写驳回原因");
}
// 更新流程状态为已驳回
baseMapper.update(null, Wrappers.<QcQualityReview>lambdaUpdate()
.eq(QcQualityReview::getReviewId, reviewId)
.set(QcQualityReview::getFlowStatus, 4L)
.set(QcQualityReview::getRejectReason, reason));
// 记录审批日志
addLog(reviewId, "reject", reason, null);
return true;
}
/**
* 执行改判
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean execute(Long reviewId) {
QcQualityReview review = baseMapper.selectById(reviewId);
if (review == null) {
throw new RuntimeException("评审单不存在");
}
if (!Long.valueOf(3L).equals(review.getFlowStatus())) {
throw new RuntimeException("只有已通过的评审单才能执行改判");
}
// 查询待执行的钢卷明细
List<QcQualityReviewCoil> coilList = coilMapper.selectList(
Wrappers.<QcQualityReviewCoil>lambdaQuery()
.eq(QcQualityReviewCoil::getReviewId, reviewId)
.eq(QcQualityReviewCoil::getDelFlag, 0)
.eq(QcQualityReviewCoil::getExecuteStatus, 0));
if (CollUtil.isEmpty(coilList)) {
throw new RuntimeException("没有待执行的钢卷");
}
Date now = new Date();
for (QcQualityReviewCoil coil : coilList) {
// 1. 写入 wms_coil_quality_rejudge 改判记录
WmsCoilQualityRejudge rejudge = new WmsCoilQualityRejudge();
rejudge.setCoilId(coil.getCoilId());
rejudge.setBeforeQuality(coil.getBeforeQuality());
rejudge.setAfterQuality(coil.getRegradeQuality());
rejudge.setRejudgeReason("异常产品评审改判,评审单号:" + review.getReviewNo());
wmsCoilQualityRejudgeMapper.insert(rejudge);
// 2. 更新钢卷的 qualityStatus 为改判后的值
if (coil.getCoilId() != null && StringUtils.isNotBlank(coil.getRegradeQuality())) {
wmsMaterialCoilMapper.update(null, Wrappers.<WmsMaterialCoil>lambdaUpdate()
.eq(WmsMaterialCoil::getCoilId, coil.getCoilId())
.set(WmsMaterialCoil::getQualityStatus, coil.getRegradeQuality()));
}
// 3. 更新明细执行状态
coilMapper.update(null, Wrappers.<QcQualityReviewCoil>lambdaUpdate()
.eq(QcQualityReviewCoil::getDetailId, coil.getDetailId())
.set(QcQualityReviewCoil::getExecuteStatus, 1L)
.set(QcQualityReviewCoil::getExecuteTime, now));
}
return true;
}
// ==================== 查询方法 ====================
@Override
public List<QcQualityReviewCoilVo> queryCoilListByReviewId(Long reviewId) {
return coilMapper.selectVoListByReviewId(reviewId);
}
@Override
public List<QcQualityReviewLogVo> queryLogListByReviewId(Long reviewId) {
return logMapper.selectVoListByReviewId(reviewId);
}
// ==================== 私有方法 ====================
/**
* 保存钢卷明细列表
*/
private void saveCoilList(Long reviewId, List<QcQualityReviewCoilBo> coilBoList) {
int seq = 1;
for (QcQualityReviewCoilBo coilBo : coilBoList) {
QcQualityReviewCoil coil = BeanUtil.toBean(coilBo, QcQualityReviewCoil.class);
coil.setReviewId(reviewId);
coil.setExecuteStatus(0L);
if (coil.getGroupSeq() == null) {
coil.setGroupSeq(seq);
}
// 若未传改判前质量等级,从钢卷表获取
if (StringUtils.isBlank(coil.getBeforeQuality()) && coil.getCoilId() != null) {
WmsMaterialCoil wmsCoil = wmsMaterialCoilMapper.selectById(coil.getCoilId());
if (wmsCoil != null) {
coil.setBeforeQuality(wmsCoil.getQualityStatus());
if (StringUtils.isBlank(coil.getCurrentCoilNo())) {
coil.setCurrentCoilNo(wmsCoil.getCurrentCoilNo());
}
if (StringUtils.isBlank(coil.getSupplierCoilNo())) {
coil.setSupplierCoilNo(wmsCoil.getSupplierCoilNo());
}
if (coil.getNetWeight() == null) {
coil.setNetWeight(wmsCoil.getNetWeight());
}
}
}
coilMapper.insert(coil);
seq++;
}
}
/**
* 添加审批日志
*/
private void addLog(Long reviewId, String action, String opinion, String operator) {
QcQualityReviewLog log = new QcQualityReviewLog();
log.setReviewId(reviewId);
log.setAction(action);
log.setOpinion(opinion);
log.setOperator(operator);
log.setOperateTime(new Date());
logMapper.insert(log);
}
/**
* 生成评审单编号: QR-20260630-0001
*/
private String generateReviewNo() {
String dateStr = DateUtil.format(new Date(), "yyyyMMdd");
Long count = baseMapper.selectCount(Wrappers.<QcQualityReview>lambdaQuery()
.likeRight(QcQualityReview::getReviewNo, "QR-" + dateStr));
return "QR-" + dateStr + "-" + String.format("%04d", count + 1);
}
private void validEntityBeforeSave(QcQualityReview entity) {
// TODO 数据校验
}
}

View File

@@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.klp.mes.qc.mapper.QcQualityReviewCoilMapper">
<resultMap type="com.klp.mes.qc.domain.vo.QcQualityReviewCoilVo" id="QcQualityReviewCoilVo">
<id property="detailId" column="detail_id"/>
<result property="reviewId" column="review_id"/>
<result property="groupSeq" column="group_seq"/>
<result property="groupRemark" column="group_remark"/>
<result property="coilId" column="coil_id"/>
<result property="currentCoilNo" column="current_coil_no"/>
<result property="supplierCoilNo" column="supplier_coil_no"/>
<result property="spec" column="spec"/>
<result property="netWeight" column="net_weight"/>
<result property="defectDesc" column="defect_desc"/>
<result property="beforeQuality" column="before_quality"/>
<result property="regradeQuality" column="regrade_quality"/>
<result property="executeStatus" column="execute_status"/>
<result property="executeTime" column="execute_time"/>
<result property="remark" column="remark"/>
<result property="createBy" column="create_by"/>
<result property="createTime" column="create_time"/>
<result property="updateBy" column="update_by"/>
<result property="updateTime" column="update_time"/>
</resultMap>
<select id="selectVoListByReviewId" resultMap="QcQualityReviewCoilVo">
SELECT * FROM qc_quality_review_coil
WHERE review_id = #{reviewId} AND del_flag = 0
ORDER BY group_seq ASC, detail_id ASC
</select>
</mapper>

View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.klp.mes.qc.mapper.QcQualityReviewLogMapper">
<resultMap type="com.klp.mes.qc.domain.vo.QcQualityReviewLogVo" id="QcQualityReviewLogVo">
<id property="logId" column="log_id"/>
<result property="reviewId" column="review_id"/>
<result property="action" column="action"/>
<result property="operator" column="operator"/>
<result property="opinion" column="opinion"/>
<result property="operateTime" column="operate_time"/>
<result property="createTime" column="create_time"/>
</resultMap>
<select id="selectVoListByReviewId" resultMap="QcQualityReviewLogVo">
SELECT * FROM qc_quality_review_log
WHERE review_id = #{reviewId}
ORDER BY operate_time ASC
</select>
</mapper>

View File

@@ -0,0 +1,75 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.klp.mes.qc.mapper.QcQualityReviewMapper">
<resultMap type="com.klp.mes.qc.domain.vo.QcQualityReviewVo" id="QcQualityReviewVo">
<id property="reviewId" column="review_id"/>
<result property="reviewNo" column="review_no"/>
<result property="productName" column="product_name"/>
<result property="transmitDept" column="transmit_dept"/>
<result property="transmitUser" column="transmit_user"/>
<result property="transmitDate" column="transmit_date"/>
<result property="prodDateRange" column="prod_date_range"/>
<result property="flowStatus" column="flow_status"/>
<result property="deptOpinion" column="dept_opinion"/>
<result property="deptSign" column="dept_sign"/>
<result property="deptSignDate" column="dept_sign_date"/>
<result property="leaderOpinion" column="leader_opinion"/>
<result property="leaderSign" column="leader_sign"/>
<result property="leaderSignDate" column="leader_sign_date"/>
<result property="rejectReason" column="reject_reason"/>
<result property="remark" column="remark"/>
<result property="delFlag" column="del_flag"/>
<result property="createBy" column="create_by"/>
<result property="createTime" column="create_time"/>
<result property="updateBy" column="update_by"/>
<result property="updateTime" column="update_time"/>
</resultMap>
<select id="selectVoPage" resultMap="QcQualityReviewVo">
SELECT * FROM qc_quality_review
<where>
del_flag = 0
<if test="vo.reviewNo != null and vo.reviewNo != ''">
AND review_no LIKE CONCAT('%', #{vo.reviewNo}, '%')
</if>
<if test="vo.productName != null and vo.productName != ''">
AND product_name LIKE CONCAT('%', #{vo.productName}, '%')
</if>
<if test="vo.flowStatus != null">
AND flow_status = #{vo.flowStatus}
</if>
<if test="vo.transmitUser != null and vo.transmitUser != ''">
AND transmit_user LIKE CONCAT('%', #{vo.transmitUser}, '%')
</if>
<if test="vo.params != null and vo.params.beginTime != null and vo.params.beginTime != ''">
AND create_time &gt;= #{vo.params.beginTime}
</if>
<if test="vo.params != null and vo.params.endTime != null and vo.params.endTime != ''">
AND create_time &lt;= #{vo.params.endTime}
</if>
</where>
ORDER BY create_time DESC
</select>
<select id="selectVoById" resultMap="QcQualityReviewVo">
SELECT * FROM qc_quality_review WHERE review_id = #{reviewId} AND del_flag = 0
</select>
<select id="selectVoList" resultMap="QcQualityReviewVo">
SELECT * FROM qc_quality_review
<where>
del_flag = 0
<if test="vo.reviewNo != null and vo.reviewNo != ''">
AND review_no LIKE CONCAT('%', #{vo.reviewNo}, '%')
</if>
<if test="vo.flowStatus != null">
AND flow_status = #{vo.flowStatus}
</if>
</where>
ORDER BY create_time DESC
</select>
</mapper>

View File

@@ -1,4 +1,3 @@
// 导出所有 HRM 模块 API
export * from './employee'
export * from './org'
export * from './certContract'

View File

@@ -0,0 +1,94 @@
import request from '@/utils/request'
// 查询质量评审单列表
export function listQualityReview(query) {
return request({
url: '/qc/qualityReview/list',
method: 'get',
params: query
})
}
// 查询质量评审单详细
export function getQualityReview(reviewId) {
return request({
url: '/qc/qualityReview/' + reviewId,
method: 'get'
})
}
// 新增质量评审单
export function addQualityReview(data) {
return request({
url: '/qc/qualityReview',
method: 'post',
data: data
})
}
// 修改质量评审单
export function updateQualityReview(data) {
return request({
url: '/qc/qualityReview',
method: 'put',
data: data
})
}
// 删除质量评审单
export function delQualityReview(reviewIds) {
return request({
url: '/qc/qualityReview/' + reviewIds,
method: 'delete'
})
}
// 提交送审
export function submitQualityReview(reviewId) {
return request({
url: '/qc/qualityReview/submit/' + reviewId,
method: 'post'
})
}
// 审批通过
export function approveQualityReview(data) {
return request({
url: '/qc/qualityReview/approve',
method: 'post',
data: data
})
}
// 驳回
export function rejectQualityReview(reviewId, reason) {
return request({
url: '/qc/qualityReview/reject/' + reviewId,
method: 'post',
params: { reason }
})
}
// 执行改判
export function executeQualityReview(reviewId) {
return request({
url: '/qc/qualityReview/execute/' + reviewId,
method: 'post'
})
}
// 查询钢卷明细
export function listQualityReviewCoil(reviewId) {
return request({
url: '/qc/qualityReview/coilList/' + reviewId,
method: 'get'
})
}
// 查询审批日志
export function listQualityReviewLog(reviewId) {
return request({
url: '/qc/qualityReview/logList/' + reviewId,
method: 'get'
})
}

View File

@@ -0,0 +1,815 @@
<template>
<div class="app-container quality-review-container">
<div class="left-panel">
<div class="panel-header">
<div class="header-title">
<i class="el-icon-document-checked"></i>
<span>质量评审单</span>
<el-button size="mini" type="text" icon="el-icon-refresh" @click="getList" style="margin-left:4px;" title="刷新列表"></el-button>
</div>
<el-select v-model="queryParams.flowStatus" placeholder="全部状态" clearable size="mini" @change="handleQuery" class="header-filter">
<el-option v-for="item in dict.type.quality_review_status" :key="item.value" :label="item.label" :value="parseInt(item.value)" />
</el-select>
</div>
<div class="search-row">
<el-input v-model="queryParams.reviewNo" placeholder="搜索评审单编号..." clearable prefix-icon="el-icon-search" size="small" @keyup.enter.native="handleQuery" @clear="handleQuery" />
<el-button type="primary" size="small" @click="handleAdd" v-hasPermi="['qc:qualityReview:add']">
<i class="el-icon-plus"></i>
</el-button>
</div>
<div v-loading="loading" class="list-body">
<div v-for="item in dataList" :key="item.reviewId" class="list-item"
:class="{ active: currentRow && currentRow.reviewId === item.reviewId }" @click="handleRowClick(item)">
<div class="item-main">
<span class="item-title">{{ item.reviewNo }}</span>
<span class="item-sub">{{ item.transmitUser }} · {{ item.productName }}</span>
</div>
<div class="item-meta">
<el-tag v-if="item.flowStatus === 1" type="info" size="mini">待提交</el-tag>
<el-tag v-else-if="item.flowStatus === 2" type="warning" size="mini">待审批</el-tag>
<el-tag v-else-if="item.flowStatus === 3" type="success" size="mini">已通过</el-tag>
<el-tag v-else-if="item.flowStatus === 4" type="danger" size="mini">已驳回</el-tag>
</div>
<div class="item-actions">
<el-button size="mini" type="text" icon="el-icon-edit" @click.stop="handleUpdate(item)" v-hasPermi="['qc:qualityReview:edit']"></el-button>
<el-button size="mini" type="text" icon="el-icon-delete" @click.stop="handleDelete(item)" v-hasPermi="['qc:qualityReview:delete']"></el-button>
</div>
</div>
<div v-if="dataList.length === 0 && !loading" class="list-empty">
<i class="el-icon-folder-opened"></i>
<span>暂无质量评审单</span>
</div>
</div>
<div class="list-footer">
<pagination :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="getList" />
</div>
</div>
<!-- 右侧详情 -->
<div class="right-panel">
<div v-if="!currentRow" class="empty-tip">
<i class="el-icon-info"></i>
<span>请在左侧列表中选择一条评审单查看详情</span>
</div>
<div v-else v-loading="detailLoading" class="detail-content">
<!-- ===== 顶部操作栏 ===== -->
<div class="detail-header">
<div class="detail-header-left">
<span class="detail-title">{{ currentRow.reviewNo }}</span>
<el-tag v-if="currentRow.flowStatus === 1" type="info" size="small">待提交</el-tag>
<el-tag v-else-if="currentRow.flowStatus === 2" type="warning" size="small">待审批</el-tag>
<el-tag v-else-if="currentRow.flowStatus === 3" type="success" size="small">已通过</el-tag>
<el-tag v-else-if="currentRow.flowStatus === 4" type="danger" size="small">已驳回</el-tag>
</div>
<div class="detail-header-actions">
<!-- 待提交提交送审 -->
<el-button v-if="currentRow.flowStatus === 1" size="mini" type="primary" plain icon="el-icon-s-promotion"
@click="handleSubmit" v-hasPermi="['qc:qualityReview:submit']">提交送审</el-button>
<!-- 待审批审批/驳回 -->
<el-button v-if="currentRow.flowStatus === 2" size="mini" type="success" plain icon="el-icon-check"
@click="handleApprove" v-hasPermi="['qc:qualityReview:approve']">审批通过</el-button>
<el-button v-if="currentRow.flowStatus === 2" size="mini" type="danger" plain icon="el-icon-close"
@click="handleReject" v-hasPermi="['qc:qualityReview:approve']">驳回</el-button>
<!-- 已通过执行改判 -->
<el-button v-if="currentRow.flowStatus === 3" size="mini" type="warning" plain icon="el-icon-setting"
@click="handleExecute" v-hasPermi="['qc:qualityReview:execute']">执行改判</el-button>
<!-- 已驳回重新提交 -->
<el-button v-if="currentRow.flowStatus === 4" size="mini" type="primary" plain icon="el-icon-s-promotion"
@click="handleReSubmit" v-hasPermi="['qc:qualityReview:submit']">重新提交</el-button>
<!-- 通用 -->
<el-button size="mini" type="text" icon="el-icon-refresh" @click="handleRefreshDetail">刷新</el-button>
<el-button v-if="currentRow.flowStatus === 1 || currentRow.flowStatus === 4" size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(currentRow)">编辑</el-button>
<el-button v-if="currentRow.flowStatus === 1 || currentRow.flowStatus === 4" size="mini" type="text" icon="el-icon-delete" @click="handleDelete(currentRow)">删除</el-button>
</div>
</div>
<!-- ===== 基本信息 ===== -->
<el-card class="detail-section" shadow="never">
<div slot="header"><span><i class="el-icon-info"></i> 基本信息</span></div>
<el-form :model="currentRow" label-width="100px" size="small" class="basic-form">
<el-row :gutter="20">
<el-col :span="8">
<el-form-item label="评审单编号">{{ currentRow.reviewNo }}</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="产品名称">{{ currentRow.productName }}</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="传递部门">{{ currentRow.transmitDept }}</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="8">
<el-form-item label="传递人">{{ currentRow.transmitUser }}</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="传递日期">{{ currentRow.transmitDate }}</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="生产日期">{{ currentRow.prodDateRange }}</el-form-item>
</el-col>
</el-row>
</el-form>
</el-card>
<!-- ===== 钢卷明细表详情只读 ===== -->
<el-card class="detail-section" shadow="never">
<div slot="header">
<span><i class="el-icon-s-management"></i> 问题钢卷明细</span>
</div>
<el-table :data="coilList" border style="width: 100%" size="small" max-height="400">
<el-table-column type="index" label="序号" width="60" align="center" />
<el-table-column prop="currentCoilNo" label="产品卷号" min-width="140" />
<el-table-column prop="supplierCoilNo" label="原料卷号" min-width="140" />
<el-table-column prop="spec" label="规格(mm)" width="110" />
<el-table-column prop="netWeight" label="卷重(t)" width="90" align="right" />
<el-table-column prop="defectDesc" label="缺陷描述" min-width="200" show-overflow-tooltip />
<el-table-column prop="beforeQuality" label="改判前" width="80" align="center">
<template slot-scope="scope">
<el-tag size="mini" type="danger">{{ scope.row.beforeQuality }}</el-tag>
</template>
</el-table-column>
<el-table-column prop="regradeQuality" label="改判后" width="110" align="center">
<template slot-scope="scope">
<el-tag v-if="scope.row.regradeQuality" size="mini" type="warning">{{ scope.row.regradeQuality }}</el-tag>
<span v-else class="text-muted">待指定</span>
</template>
</el-table-column>
</el-table>
</el-card>
<!-- ===== 品质部评审意见 ===== -->
<el-card class="detail-section" shadow="never">
<div slot="header"><span><i class="el-icon-edit-outline"></i> 品质部评审意见</span></div>
<div v-if="editing" class="section-editor">
<el-input type="textarea" :rows="3" v-model="editForm.deptOpinion" placeholder="请填写品质部评审意见..." />
<div style="margin-top:8px; display:flex; gap:8px;">
<el-input v-model="editForm.deptSign" placeholder="签字人" style="width:160px;" size="small" />
<el-date-picker v-model="editForm.deptSignDate" type="date" placeholder="签字日期" size="small" />
</div>
<el-button size="small" type="primary" @click="saveOpinion" style="margin-top:8px;">保存意见</el-button>
</div>
<div v-else class="section-display">
<div class="opinion-text">{{ currentRow.deptOpinion || '暂未填写' }}</div>
<div class="opinion-footer" v-if="currentRow.deptOpinion">
<span>签字{{ currentRow.deptSign || '-' }}</span>
<span>日期{{ currentRow.deptSignDate || '-' }}</span>
</div>
</div>
</el-card>
<!-- ===== 领导审批意见 ===== -->
<el-card class="detail-section" shadow="never">
<div slot="header"><span><i class="el-icon-s-check"></i> 领导审批意见</span></div>
<!-- flowStatus=2时显示审批表单权限由后端API控制前端不做限制 -->
<div v-if="currentRow.flowStatus === 2" class="section-editor">
<!-- 审批通过模式 -->
<el-radio-group v-model="approveAction" style="margin-bottom:12px;">
<el-radio-button label="approve">通过</el-radio-button>
<el-radio-button label="reject">驳回</el-radio-button>
</el-radio-group>
<template v-if="approveAction === 'approve'">
<el-input type="textarea" :rows="3" v-model="approveForm.leaderOpinion" placeholder="请输入领导审批意见..." />
<div style="margin:8px 0;">
<el-input v-model="approveForm.leaderSign" placeholder="签字人" style="width:160px;" size="small" />
</div>
<div class="approve-coil-section">
<div class="approve-coil-title">请为每个钢卷指定改判后质量状态</div>
<div v-for="coil in coilList" :key="coil.detailId" class="approve-coil-row">
<span class="approve-coil-label">{{ coil.currentCoilNo }}{{ coil.spec }}</span>
<el-select v-model="approveCoilMap[coil.detailId]" placeholder="请选择改判后状态" size="small" style="width:180px;">
<el-option v-for="item in regradeOptions" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</div>
</div>
<el-button size="small" type="success" @click="doApprove">确认通过</el-button>
</template>
<template v-else>
<el-input type="textarea" :rows="3" v-model="rejectReason" placeholder="请输入驳回原因..." />
<el-button size="small" type="danger" @click="doReject" style="margin-top:8px;">确认驳回</el-button>
</template>
</div>
<div v-else class="section-display">
<div class="opinion-text">{{ currentRow.leaderOpinion || '暂无审批意见' }}</div>
<div class="opinion-footer" v-if="currentRow.leaderOpinion">
<span>签字{{ currentRow.leaderSign || '-' }}</span>
<span>日期{{ currentRow.leaderSignDate || '-' }}</span>
</div>
<div v-if="currentRow.flowStatus === 4 && currentRow.rejectReason" class="reject-reason">
<el-alert :title="'驳回原因:' + currentRow.rejectReason" type="error" show-icon :closable="false" />
</div>
</div>
</el-card>
<!-- ===== 审批日志 ===== -->
<el-card class="detail-section" shadow="never">
<div slot="header"><span><i class="el-icon-time"></i> 审批记录</span></div>
<div v-if="logList.length === 0" class="text-muted" style="padding:12px;">暂无审批记录</div>
<el-timeline v-else>
<el-timeline-item v-for="log in logList" :key="log.logId"
:timestamp="log.createTime"
:type="log.action === 'submit' ? 'primary' : (log.action === 'approve' ? 'success' : 'danger')"
:icon="log.action === 'submit' ? 'el-icon-s-promotion' : (log.action === 'approve' ? 'el-icon-check' : 'el-icon-close')">
<div>
<strong>{{ log.action === 'submit' ? '提交送审' : (log.action === 'approve' ? '审批通过' : '驳回') }}</strong>
<span v-if="log.operator"> {{ log.operator }}</span>
</div>
<div v-if="log.opinion" class="timeline-opinion">{{ log.opinion }}</div>
</el-timeline-item>
</el-timeline>
</el-card>
</div>
</div>
<!-- 编辑弹窗 -->
<el-dialog :title="dialogTitle" :visible.sync="dialogVisible" width="900px" :close-on-click-modal="false" append-to-body>
<el-form ref="form" :model="editForm" :rules="rules" label-width="100px" size="small">
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="产品名称" prop="productName">
<el-input v-model="editForm.productName" placeholder="如:镀锌产品" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="传递部门" prop="transmitDept">
<el-input v-model="editForm.transmitDept" placeholder="品质部" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="8">
<el-form-item label="传递人" prop="transmitUser">
<el-input v-model="editForm.transmitUser" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="传递日期" prop="transmitDate">
<el-date-picker v-model="editForm.transmitDate" type="date" placeholder="选择日期" style="width:100%;" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="生产日期" prop="prodDateRange">
<el-input v-model="editForm.prodDateRange" placeholder="如14-15" />
</el-form-item>
</el-col>
</el-row>
<!-- ===== 钢卷选择 ===== -->
<div class="dialog-section">
<div class="dialog-section-header">
<span>问题钢卷</span>
<el-button size="mini" type="primary" plain icon="el-icon-plus" @click="showCoilSelector = true">选择O级钢卷</el-button>
</div>
<el-table :data="editForm.coilList" border style="width:100%" size="small" max-height="250">
<el-table-column type="index" label="序号" width="60" align="center" />
<el-table-column prop="currentCoilNo" label="产品卷号" min-width="130" />
<el-table-column prop="supplierCoilNo" label="原料卷号" min-width="130" />
<el-table-column prop="spec" label="规格(mm)" width="100" />
<el-table-column prop="netWeight" label="卷重(t)" width="80" align="right" />
<el-table-column prop="defectDesc" label="缺陷描述" min-width="160">
<template slot-scope="scope">
<el-input v-model="scope.row.defectDesc" size="mini" placeholder="输入缺陷描述" />
</template>
</el-table-column>
<el-table-column label="操作" width="60" align="center">
<template slot-scope="scope">
<el-button size="mini" type="text" icon="el-icon-delete" @click="editForm.coilList.splice(scope.$index, 1)"></el-button>
</template>
</el-table-column>
</el-table>
<div v-if="editForm.coilList.length === 0" class="dialog-table-empty">请点击上方按钮选择O级钢卷</div>
</div>
<!-- ===== 钢卷选择器嵌套在弹窗内 ===== -->
<CoilSelector
:visible.sync="showCoilSelector"
:multiple="true"
:filters="{ qualityStatusCsv: 'O', status: 0 }"
@confirm="onDialogCoilConfirm" />
<el-divider />
<el-row :gutter="20">
<el-col :span="24">
<el-form-item label="品质部意见" prop="deptOpinion">
<el-input type="textarea" :rows="3" v-model="editForm.deptOpinion" placeholder="请填写品质部评审意见" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="8">
<el-form-item label="签字人">
<el-input v-model="editForm.deptSign" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="签字日期">
<el-date-picker v-model="editForm.deptSignDate" type="date" placeholder="选择日期" style="width:100%;" />
</el-form-item>
</el-col>
</el-row>
</el-form>
<span slot="footer">
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="saveForm">确定</el-button>
</span>
</el-dialog>
</div>
</template>
<script>
import { listQualityReview, getQualityReview, addQualityReview, updateQualityReview, delQualityReview,
submitQualityReview, approveQualityReview, rejectQualityReview, executeQualityReview,
listQualityReviewCoil, listQualityReviewLog } from '@/api/mes/qc/qualityReview'
import CoilSelector from '@/components/CoilSelector'
export default {
name: 'QualityReview',
components: { CoilSelector },
dicts: ['quality_review_status', 'regrade_quality_type'],
data() {
return {
// 列表
loading: false,
dataList: [],
total: 0,
queryParams: {
pageNum: 1,
pageSize: 20,
reviewNo: undefined,
flowStatus: undefined
},
currentRow: null,
// 详情
detailLoading: false,
coilList: [],
logList: [],
// 钢卷选择
showCoilSelector: false,
// 编辑弹窗
dialogVisible: false,
dialogTitle: '',
editing: false,
editForm: {
reviewId: undefined,
productName: '',
transmitDept: '品质部',
transmitUser: '',
transmitDate: undefined,
prodDateRange: '',
deptOpinion: '',
deptSign: '',
deptSignDate: undefined,
coilList: []
},
rules: {
productName: [{ required: true, message: '请输入产品名称', trigger: 'blur' }]
},
// 改判状态选项(硬编码后备,优先使用字典)
regradeOptions: [
{ label: '协议销售', value: 'protocol_sale' },
{ label: '转分剪', value: 'to_slitting' },
{ label: '降级', value: 'downgrade' },
{ label: '返修', value: 'rework' },
{ label: '报废', value: 'scrap' }
],
// 审批
approveAction: 'approve',
approveForm: {
reviewId: undefined,
leaderOpinion: '',
leaderSign: ''
},
approveCoilMap: {},
rejectReason: ''
}
},
created() {
this.getList()
},
methods: {
// ===== 列表 =====
handleQuery() {
this.queryParams.pageNum = 1
this.getList()
},
getList() {
this.loading = true
listQualityReview(this.queryParams).then(res => {
this.dataList = res.rows || []
this.total = res.total || 0
this.loading = false
}).catch(() => { this.loading = false })
},
handleRowClick(row) {
this.currentRow = row
this.loadDetail(row.reviewId)
},
// ===== 详情 =====
loadDetail(reviewId) {
this.detailLoading = true
getQualityReview(reviewId).then(res => {
const data = res.data
this.currentRow = data
this.coilList = data.coilList || []
this.logList = data.logList || []
// 初始化审批表单数据先构建完整对象再赋值解决Vue2响应式问题
const map = {}
this.coilList.forEach(c => {
map[c.detailId] = c.regradeQuality || ''
})
this.approveCoilMap = map
this.approveForm.reviewId = reviewId
this.approveForm.leaderOpinion = ''
this.approveForm.leaderSign = ''
this.rejectReason = ''
this.detailLoading = false
}).catch(() => { this.detailLoading = false })
},
handleRefreshDetail() {
if (this.currentRow) {
this.loadDetail(this.currentRow.reviewId)
this.getList()
}
},
// ===== 新增/编辑 =====
handleAdd() {
this.dialogTitle = '新建质量评审单'
this.editForm = {
reviewId: undefined,
productName: '',
transmitDept: '品质部',
transmitUser: '',
transmitDate: undefined,
prodDateRange: '',
deptOpinion: '',
deptSign: '',
deptSignDate: undefined,
coilList: []
}
this.editing = true
this.dialogVisible = true
},
handleUpdate(row) {
this.dialogTitle = '编辑质量评审单'
this.editForm = {
reviewId: row.reviewId,
productName: row.productName,
transmitDept: row.transmitDept,
transmitUser: row.transmitUser,
transmitDate: row.transmitDate,
prodDateRange: row.prodDateRange,
deptOpinion: row.deptOpinion,
deptSign: row.deptSign,
deptSignDate: row.deptSignDate,
coilList: this.coilList.map(c => ({ ...c }))
}
this.editing = true
this.dialogVisible = true
},
saveForm() {
this.$refs.form.validate(valid => {
if (!valid) return
const data = { ...this.editForm, coilList: this.editForm.coilList || [] }
if (this.editForm.reviewId) {
updateQualityReview(data).then(() => {
this.$modal.msgSuccess('修改成功')
this.dialogVisible = false
this.handleRefreshDetail()
})
} else {
addQualityReview(data).then(() => {
this.$modal.msgSuccess('创建成功')
this.dialogVisible = false
this.getList()
})
}
})
},
// ===== 钢卷选择(弹窗内) =====
onDialogCoilConfirm(selected) {
if (!selected || selected.length === 0) return
const startIdx = this.editForm.coilList.length || 0
const newCoils = selected.map((coil, idx) => ({
coilId: coil.coilId,
currentCoilNo: coil.currentCoilNo,
supplierCoilNo: coil.supplierCoilNo || coil.enterCoilNo,
spec: coil.spec,
netWeight: coil.netWeight,
defectDesc: '',
groupSeq: startIdx + idx + 1,
beforeQuality: coil.qualityStatus || 'O'
}))
this.editForm.coilList = [...(this.editForm.coilList || []), ...newCoils]
},
handleRemoveCoil(index) {
this.coilList.splice(index, 1)
},
// ===== 提交送审 =====
handleSubmit() {
this.$confirm('确认提交送审?', '提示', { type: 'warning' }).then(() => {
submitQualityReview(this.currentRow.reviewId).then(() => {
this.$modal.msgSuccess('送审成功')
this.handleRefreshDetail()
})
}).catch(() => {})
},
handleReSubmit() {
this.$confirm('确认重新提交?提交后将进入待审批状态。', '提示', { type: 'warning' }).then(() => {
submitQualityReview(this.currentRow.reviewId).then(() => {
this.$modal.msgSuccess('送审成功')
this.handleRefreshDetail()
})
}).catch(() => {})
},
// ===== 审批 =====
handleApprove() {
this.approveAction = 'approve'
this.approveForm = {
reviewId: this.currentRow.reviewId,
leaderOpinion: '',
leaderSign: ''
}
// 先构建完整对象再赋值
const map = {}
this.coilList.forEach(c => {
map[c.detailId] = c.regradeQuality || ''
})
this.approveCoilMap = map
},
doApprove() {
if (!this.approveForm.leaderOpinion) {
this.$modal.msgError('请输入审批意见')
return
}
const coilRegradeList = this.coilList.map(c => ({
detailId: c.detailId,
regradeQuality: this.approveCoilMap[c.detailId]
}))
const invalid = coilRegradeList.some(r => !r.regradeQuality)
if (invalid) {
this.$modal.msgError('请为每个钢卷指定改判后质量状态')
return
}
this.$confirm('确认审批通过?', '提示', { type: 'success' }).then(() => {
approveQualityReview({
reviewId: this.currentRow.reviewId,
leaderOpinion: this.approveForm.leaderOpinion,
leaderSign: this.approveForm.leaderSign,
coilRegradeList
}).then(() => {
this.$modal.msgSuccess('审批通过')
this.handleRefreshDetail()
})
}).catch(() => {})
},
handleReject() {
this.approveAction = 'reject'
this.rejectReason = ''
},
doReject() {
if (!this.rejectReason) {
this.$modal.msgError('请输入驳回原因')
return
}
this.$confirm('确认驳回该评审单?', '提示', { type: 'warning' }).then(() => {
rejectQualityReview(this.currentRow.reviewId, this.rejectReason).then(() => {
this.$modal.msgSuccess('已驳回')
this.handleRefreshDetail()
})
}).catch(() => {})
},
// ===== 执行改判 =====
handleExecute() {
this.$confirm('确认执行改判?执行后钢卷质量状态将被更新。', '提示', { type: 'warning' }).then(() => {
executeQualityReview(this.currentRow.reviewId).then(() => {
this.$modal.msgSuccess('改判执行成功')
this.handleRefreshDetail()
})
}).catch(() => {})
},
// ===== 删除 =====
handleDelete(row) {
this.$confirm('确认删除评审单【' + (row.reviewNo || row.reviewId) + '】?', '提示', { type: 'warning' }).then(() => {
delQualityReview(row.reviewId).then(() => {
this.$modal.msgSuccess('删除成功')
if (this.currentRow && this.currentRow.reviewId === row.reviewId) {
this.currentRow = null
this.coilList = []
this.logList = []
}
this.getList()
})
}).catch(() => {})
},
// ===== 辅助 =====
hasPermi(permi) {
return this.$store.getters.permissions && this.$store.getters.permissions.includes(permi)
},
saveOpinion() {
if (!this.currentRow) return
updateQualityReview({
reviewId: this.currentRow.reviewId,
deptOpinion: this.editForm.deptOpinion,
deptSign: this.editForm.deptSign,
deptSignDate: this.editForm.deptSignDate
}).then(() => {
this.$modal.msgSuccess('保存成功')
this.handleRefreshDetail()
})
}
}
}
</script>
<style lang="scss">
.quality-review-container {
display: flex;
height: calc(100vh - 84px);
gap: 12px;
padding: 0;
.left-panel {
width: 320px;
min-width: 320px;
display: flex;
flex-direction: column;
background: #fff;
border-radius: 4px;
border: 1px solid #e4e7ed;
.panel-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 12px 12px 0;
.header-title {
font-weight: 600;
font-size: 15px;
display: flex;
align-items: center;
gap: 6px;
}
.header-filter { width: 130px; }
}
.search-row {
display: flex;
padding: 8px 12px;
gap: 6px;
.el-input { flex: 1; }
}
.list-body {
flex: 1;
overflow-y: auto;
padding: 0 8px;
}
.list-item {
padding: 10px 12px;
border-bottom: 1px solid #f0f0f0;
cursor: pointer;
border-radius: 4px;
margin: 2px 0;
&:hover { background: #f5f7fa; }
&.active { background: #ecf5ff; }
.item-main {
display: flex;
justify-content: space-between;
align-items: center;
.item-title { font-weight: 500; font-size: 13px; }
.item-sub { font-size: 12px; color: #909399; }
}
.item-meta { margin-top: 4px; }
.item-actions {
margin-top: 4px;
text-align: right;
}
}
.list-empty {
text-align: center;
padding: 40px 0;
color: #c0c4cc;
i { font-size: 36px; display: block; margin-bottom: 8px; }
}
.list-footer { padding: 8px; }
}
.right-panel {
flex: 1;
background: #fff;
border-radius: 4px;
border: 1px solid #e4e7ed;
overflow-y: auto;
padding: 16px;
.empty-tip {
text-align: center;
padding: 80px 0;
color: #c0c4cc;
i { font-size: 48px; display: block; margin-bottom: 12px; }
}
.detail-header {
display: flex;
justify-content: space-between;
align-items: center;
padding-bottom: 12px;
border-bottom: 1px solid #e4e7ed;
margin-bottom: 12px;
.detail-header-left {
display: flex;
align-items: center;
gap: 8px;
.detail-title { font-size: 16px; font-weight: 600; }
}
.detail-header-actions { display: flex; gap: 4px; flex-wrap: wrap; }
}
.detail-section {
margin-bottom: 12px;
.basic-form .el-form-item { margin-bottom: 0; }
}
.section-editor {
.approve-coil-section {
background: #fafafa;
padding: 12px;
border-radius: 4px;
margin: 8px 0;
.approve-coil-title { font-weight: 500; margin-bottom: 8px; }
.approve-coil-row {
display: flex;
align-items: center;
gap: 12px;
margin-bottom: 6px;
.approve-coil-label { min-width: 200px; font-size: 13px; }
}
}
}
.section-display {
.opinion-text {
background: #fafafa;
padding: 12px;
border-radius: 4px;
line-height: 1.6;
white-space: pre-wrap;
}
.opinion-footer {
margin-top: 8px;
display: flex;
gap: 20px;
color: #909399;
font-size: 13px;
}
.reject-reason { margin-top: 12px; }
}
.dialog-section {
border: 1px solid #e4e7ed;
border-radius: 4px;
padding: 12px;
margin-bottom: 16px;
.dialog-section-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 8px;
font-weight: 600;
font-size: 14px;
}
.dialog-table-empty {
text-align: center;
padding: 20px;
color: #c0c4cc;
font-size: 13px;
}
}
.text-muted { color: #c0c4cc; }
.timeline-opinion {
font-size: 13px;
color: #606266;
margin-top: 4px;
background: #f5f7fa;
padding: 6px 10px;
border-radius: 4px;
}
}
}
</style>

View File

@@ -0,0 +1,220 @@
<template>
<div class="app-container">
<div class="todo-header">
<h3><i class="el-icon-s-promotion"></i> 待审批质量评审单</h3>
</div>
<el-table :data="todoList" v-loading="loading" border stripe @row-click="handleRowClick" style="cursor:pointer;">
<el-table-column label="评审单编号" prop="reviewNo" width="180" />
<el-table-column label="产品名称" prop="productName" width="160" />
<el-table-column label="传递人" prop="transmitUser" width="100" />
<el-table-column label="传递日期" prop="transmitDate" width="120" />
<el-table-column label="生产日期" prop="prodDateRange" width="120" />
<el-table-column label="品质部意见" prop="deptOpinion" min-width="200" show-overflow-tooltip />
<el-table-column label="创建时间" prop="createTime" width="160" />
<el-table-column label="操作" width="200" align="center" fixed="right">
<template slot-scope="scope">
<el-button size="mini" type="success" plain @click.stop="handleQuickApprove(scope.row)">通过</el-button>
<el-button size="mini" type="danger" plain @click.stop="handleQuickReject(scope.row)">驳回</el-button>
</template>
</el-table-column>
</el-table>
<pagination :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="getList" />
<!-- 快速审批弹窗 -->
<el-dialog title="审批质量评审单" :visible.sync="approveDialog.visible" width="700px" :close-on-click-modal="false" append-to-body>
<div v-if="approveDialog.review">
<div class="approve-info">
<el-descriptions :column="2" size="small" border>
<el-descriptions-item label="评审单编号">{{ approveDialog.review.reviewNo }}</el-descriptions-item>
<el-descriptions-item label="产品名称">{{ approveDialog.review.productName }}</el-descriptions-item>
<el-descriptions-item label="传递人">{{ approveDialog.review.transmitUser }}</el-descriptions-item>
<el-descriptions-item label="传递日期">{{ approveDialog.review.transmitDate }}</el-descriptions-item>
</el-descriptions>
<div class="approve-opinion-box">
<label>品质部意见</label>
<div class="opinion-content">{{ approveDialog.review.deptOpinion }}</div>
</div>
</div>
<el-divider />
<el-radio-group v-model="approveAction" style="margin-bottom:12px;">
<el-radio-button label="approve">通过</el-radio-button>
<el-radio-button label="reject">驳回</el-radio-button>
</el-radio-group>
<template v-if="approveAction === 'approve'">
<el-input type="textarea" :rows="3" v-model="approveForm.leaderOpinion" placeholder="请输入领导审批意见..." style="margin-bottom:12px;" />
<el-input v-model="approveForm.leaderSign" placeholder="签字人" style="width:160px; margin-bottom:12px;" size="small" />
<div class="coil-regrade-section">
<div class="coil-regrade-title">请为以下钢卷指定改判后质量状态</div>
<el-table :data="approveCoilList" size="small" border max-height="300">
<el-table-column prop="currentCoilNo" label="产品卷号" width="140" />
<el-table-column prop="spec" label="规格" width="100" />
<el-table-column prop="beforeQuality" label="当前等级" width="80">
<template slot-scope="s"><el-tag size="mini" type="danger">{{ s.row.beforeQuality }}</el-tag></template>
</el-table-column>
<el-table-column label="改判后状态" width="160">
<template slot-scope="s">
<el-select v-model="approveCoilMap[s.row.detailId]" placeholder="请选择" size="small">
<el-option v-for="item in regradeOptions" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</template>
</el-table-column>
</el-table>
</div>
<div style="margin-top:12px;">
<el-button type="success" @click="doApprove" :loading="approveLoading">确认通过</el-button>
</div>
</template>
<template v-else>
<el-input type="textarea" :rows="3" v-model="rejectReason" placeholder="请输入驳回原因..." />
<el-button type="danger" @click="doReject" :loading="approveLoading" style="margin-top:12px;">确认驳回</el-button>
</template>
</div>
</el-dialog>
</div>
</template>
<script>
import { listQualityReview, approveQualityReview, rejectQualityReview, getQualityReview } from '@/api/mes/qc/qualityReview'
export default {
name: 'QualityReviewTodo',
dicts: ['quality_review_status', 'regrade_quality_type'],
data() {
return {
loading: false,
todoList: [],
total: 0,
queryParams: {
pageNum: 1,
pageSize: 20,
flowStatus: 2
},
regradeOptions: [
{ label: '协议销售', value: 'protocol_sale' },
{ label: '转分剪', value: 'to_slitting' },
{ label: '降级', value: 'downgrade' },
{ label: '返修', value: 'rework' },
{ label: '报废', value: 'scrap' }
],
approveDialog: {
visible: false,
review: null
},
approveAction: 'approve',
approveForm: {
reviewId: undefined,
leaderOpinion: '',
leaderSign: ''
},
approveCoilList: [],
approveCoilMap: {},
approveLoading: false,
rejectReason: ''
}
},
created() {
this.getList()
},
methods: {
getList() {
this.loading = true
listQualityReview(this.queryParams).then(res => {
this.todoList = res.rows || []
this.total = res.total || 0
this.loading = false
}).catch(() => { this.loading = false })
},
handleRowClick(row) {
this.handleQuickApprove(row)
},
handleQuickApprove(row) {
this.approveAction = 'approve'
this.approveDialog.review = row
this.approveForm = { reviewId: row.reviewId, leaderOpinion: '', leaderSign: '' }
this.rejectReason = ''
this.approveDialog.visible = true
// 加载明细
getQualityReview(row.reviewId).then(res => {
const data = res.data
this.approveCoilList = data.coilList || []
const map = {}
;(data.coilList || []).forEach(c => {
map[c.detailId] = c.regradeQuality || ''
})
this.approveCoilMap = map
})
},
handleQuickReject(row) {
this.$prompt('请输入驳回原因', '驳回评审单', { inputType: 'textarea', inputPattern: /.+/, inputErrorMessage: '驳回原因不能为空' }).then(({ value }) => {
rejectQualityReview(row.reviewId, value).then(() => {
this.$modal.msgSuccess('已驳回')
this.getList()
})
}).catch(() => {})
},
doApprove() {
if (!this.approveForm.leaderOpinion) {
this.$modal.msgError('请输入审批意见')
return
}
const coilRegradeList = this.approveCoilList.map(c => ({
detailId: c.detailId,
regradeQuality: this.approveCoilMap[c.detailId]
}))
if (coilRegradeList.some(r => !r.regradeQuality)) {
this.$modal.msgError('请为每个钢卷指定改判后质量状态')
return
}
this.approveLoading = true
approveQualityReview({
reviewId: this.approveDialog.review.reviewId,
leaderOpinion: this.approveForm.leaderOpinion,
leaderSign: this.approveForm.leaderSign,
coilRegradeList
}).then(() => {
this.$modal.msgSuccess('审批通过')
this.approveDialog.visible = false
this.approveLoading = false
this.getList()
}).catch(() => { this.approveLoading = false })
},
doReject() {
if (!this.rejectReason) {
this.$modal.msgError('请输入驳回原因')
return
}
this.approveLoading = true
rejectQualityReview(this.approveDialog.review.reviewId, this.rejectReason).then(() => {
this.$modal.msgSuccess('已驳回')
this.approveDialog.visible = false
this.approveLoading = false
this.getList()
}).catch(() => { this.approveLoading = false })
}
}
}
</script>
<style lang="scss">
.todo-header h3 { margin: 0 0 16px; i { margin-right: 6px; } }
.approve-info { margin-bottom: 8px; }
.approve-opinion-box {
margin-top: 12px;
label { font-weight: 600; display: block; margin-bottom: 6px; }
.opinion-content {
background: #f5f7fa;
padding: 10px;
border-radius: 4px;
line-height: 1.6;
white-space: pre-wrap;
}
}
.coil-regrade-section {
margin-top: 12px;
.coil-regrade-title { font-weight: 500; margin-bottom: 8px; }
}
</style>

View File

@@ -86,6 +86,10 @@ module.exports = {
'@': resolve('src')
}
},
// 解决 chokidar 监视系统文件 (pagefile.sys) 导致的 EBUSY 错误
watchOptions: {
ignored: ['node_modules', '\.git', /pagefile\.sys$/, /swapfile\.sys$/]
},
plugins: [
// http://doc.klp.vip/klp-vue/other/faq.html#使用gzip解压缩静态文件
new CompressionPlugin({