广丰卷烟厂数采质量分析系统
baoshiwei
4 小时以前 e97b55310155a2dd691bd698a10295a4d867f60c
Merge remote-tracking branch 'origin/master'
已添加46个文件
已修改25个文件
6301 ■■■■■ 文件已修改
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/service/JudgeService.java 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/service/MatService.java 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-translation/src/main/java/org/dromara/common/translation/constant/TransConstant.java 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-translation/src/main/java/org/dromara/common/translation/core/impl/JudgeNameTranslationImpl.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-translation/src/main/java/org/dromara/common/translation/core/impl/MatNameTranslationImpl.java 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-translation/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/qm/controller/QmJudgeDetailsController.java 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/qm/controller/QmMatcheckController.java 122 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/qm/domain/QmJudgeDetails.java 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/qm/domain/QmMatcheck.java 184 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/qm/domain/bo/QmJudgeDetailsBo.java 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/qm/domain/bo/QmMatcheckBo.java 183 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/qm/domain/vo/QmBatchVo.java 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/qm/domain/vo/QmJudgeDetailsVo.java 54 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/qm/domain/vo/QmJudgeVo.java 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/qm/domain/vo/QmMatcheckVo.java 255 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/qm/mapper/QmMatcheckMapper.java 38 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/qm/service/IQmJudgeDetailsService.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/qm/service/IQmMatcheckService.java 85 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/qm/service/impl/QmJudgeDetailsServiceImpl.java 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/qm/service/impl/QmJudgeServiceImpl.java 61 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/qm/service/impl/QmMatcheckServiceImpl.java 154 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/sc/md/controller/MdMatController.java 105 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/sc/md/controller/MdMatTypeController.java 105 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/sc/md/domain/MdMat.java 97 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/sc/md/domain/MdMatType.java 87 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/sc/md/domain/bo/MdMatBo.java 97 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/sc/md/domain/bo/MdMatTypeBo.java 87 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/sc/md/domain/vo/MdMatTypeVo.java 106 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/sc/md/domain/vo/MdMatVo.java 118 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/sc/md/mapper/MdMatMapper.java 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/sc/md/mapper/MdMatTypeMapper.java 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/sc/md/service/IMdMatService.java 68 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/sc/md/service/IMdMatTypeService.java 68 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/sc/md/service/impl/MdMatServiceImpl.java 147 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/sc/md/service/impl/MdMatTypeServiceImpl.java 138 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/sc/test 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/resources/mapper/qa/qm/QmMatcheckMapper.xml 67 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/resources/mapper/sc/md/MdMatMapper.xml 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/resources/mapper/sc/md/MdMatTypeMapper.xml 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/resources/mapper/sc/test 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/router/elegant/imports.ts 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/router/elegant/routes.ts 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/router/elegant/transform.ts 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/service/api/md/mat-type.ts 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/service/api/md/mat.ts 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/service/api/qm/judge-details.ts 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/service/api/qm/matcheck.ts 57 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/typings/api/md.mat-type.api.d.ts 76 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/typings/api/md.mat.api.d.ts 83 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/typings/api/qm.batch.api.d.ts 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/typings/api/qm.judge-details.api.d.ts 40 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/typings/api/qm.matcheck.api.d.ts 165 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/typings/elegant-router.d.ts 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/views/md/mat-type/index.vue 241 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/views/md/mat-type/modules/mat-type-operate-drawer.vue 171 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/views/md/mat-type/modules/mat-type-search.vue 99 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/views/md/mat/index.vue 283 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/views/md/mat/modules/mat-operate-drawer.vue 184 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/views/md/mat/modules/mat-search.vue 119 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/views/qm/batch/index.vue 231 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/views/qm/batch/modules/batch-operate-drawer.vue 280 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/views/qm/batch/modules/batch-search.vue 166 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/views/qm/judge-details/modules/judge-details-operate-drawer.vue 70 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/views/qm/judge-details/modules/judge-details-sub-table.vue 414 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/views/qm/judge/index.vue 103 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/views/qm/judge/modules/judge-operate-drawer.vue 59 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/views/qm/judge/modules/judge-search.vue 54 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/views/qm/matcheck/index.vue 268 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/views/qm/matcheck/modules/matcheck-operate-drawer.vue 266 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/views/qm/matcheck/modules/matcheck-search.vue 93 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/service/JudgeService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,13 @@
package org.dromara.common.core.service;
/**
 * åˆ¤å®šä¾æ®
 */
public interface JudgeService {
    /**
     * åˆ¤å®šä¾æ®code转name
     * @param judgeCode
     * @return
     */
    String selectJudgeNameByCode(String judgeCode);
}
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/service/MatService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,13 @@
package org.dromara.common.core.service;
/**
 * ç‰©æ–™
 */
public interface MatService {
    /**
     * ç‰©æ–™code转name
     * @param matCode
     * @return
     */
    String selectMatNameByCode(String matCode);
}
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-translation/src/main/java/org/dromara/common/translation/constant/TransConstant.java
@@ -32,4 +32,15 @@
     */
    String OSS_ID_TO_URL = "oss_id_to_url";
    /**
     * ç‰©æ–™code转name
     */
    String MAT_CODE_TO_NAME = "mat_code_to_name";
    /**
     * åˆ¤å®šä¾æ®code转name
     */
    String JUDGE_CODE_TO_NAME = "judge_code_to_name";
}
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-translation/src/main/java/org/dromara/common/translation/core/impl/JudgeNameTranslationImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,26 @@
package org.dromara.common.translation.core.impl;
import lombok.AllArgsConstructor;
import org.dromara.common.core.service.JudgeService;
import org.dromara.common.core.service.MatService;
import org.dromara.common.translation.annotation.TranslationType;
import org.dromara.common.translation.constant.TransConstant;
import org.dromara.common.translation.core.TranslationInterface;
/**
 * judgeName翻译实现
 *
 * @author zhuguifei
 */
@AllArgsConstructor
@TranslationType(type = TransConstant.JUDGE_CODE_TO_NAME)
public class JudgeNameTranslationImpl implements TranslationInterface<String> {
    private final JudgeService judgeService;
    @Override
    public String translation(Object key, String other) {
        if(key == null) return  "";
        return judgeService.selectJudgeNameByCode(key.toString());
    }
}
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-translation/src/main/java/org/dromara/common/translation/core/impl/MatNameTranslationImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,28 @@
package org.dromara.common.translation.core.impl;
import cn.hutool.core.convert.Convert;
import lombok.AllArgsConstructor;
import org.dromara.common.core.service.MatService;
import org.dromara.common.core.service.UserService;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.translation.annotation.TranslationType;
import org.dromara.common.translation.constant.TransConstant;
import org.dromara.common.translation.core.TranslationInterface;
/**
 * matName翻译实现
 *
 * @author zhuguifei
 */
@AllArgsConstructor
@TranslationType(type = TransConstant.MAT_CODE_TO_NAME)
public class MatNameTranslationImpl implements TranslationInterface<String> {
    private final MatService matService;
    @Override
    public String translation(Object key, String other) {
        if(key == null) return  "";
        return matService.selectMatNameByCode(key.toString());
    }
}
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-translation/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
@@ -4,3 +4,5 @@
org.dromara.common.translation.core.impl.OssUrlTranslationImpl
org.dromara.common.translation.core.impl.UserNameTranslationImpl
org.dromara.common.translation.core.impl.NicknameTranslationImpl
org.dromara.common.translation.core.impl.MatNameTranslationImpl
org.dromara.common.translation.core.impl.JudgeNameTranslationImpl
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/qm/controller/QmJudgeDetailsController.java
@@ -46,6 +46,16 @@
    }
    /**
     * æŸ¥è¯¢åˆ¤å®šæ¨¡æ¿æ˜Žç»†æ ‘形结构列表
     */
    @SaCheckPermission("qm:judgeDetails:list")
    @GetMapping("/tree")
    public TableDataInfo<QmJudgeDetailsVo> tree(@RequestParam @NotBlank(message = "judgeId不能为空") String judgeId) {
        List<QmJudgeDetailsVo> list = qmJudgeDetailsService.queryTreeListByJudgeId(judgeId);
        return TableDataInfo.build(list);
    }
    /**
     * å¯¼å‡ºåˆ¤å®šæ¨¡æ¿æ˜Žç»†åˆ—表
     */
    @SaCheckPermission("qm:judgeDetails:export")
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/qm/controller/QmMatcheckController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,122 @@
package org.dromara.qa.qm.controller;
import java.util.List;
import java.util.Map;
import lombok.RequiredArgsConstructor;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.constraints.*;
import cn.dev33.satoken.annotation.SaCheckPermission;
import org.springframework.web.bind.annotation.*;
import org.springframework.validation.annotation.Validated;
import org.dromara.common.idempotent.annotation.RepeatSubmit;
import org.dromara.common.log.annotation.Log;
import org.dromara.common.web.core.BaseController;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.core.domain.R;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup;
import org.dromara.common.log.enums.BusinessType;
import org.dromara.common.excel.utils.ExcelUtil;
import org.dromara.qa.qm.domain.vo.QmMatcheckVo;
import org.dromara.qa.qm.domain.bo.QmMatcheckBo;
import org.dromara.qa.qm.service.IQmMatcheckService;
import org.dromara.common.mybatis.core.page.TableDataInfo;
/**
 * ææ–™æ£€éªŒç»Ÿè®¡
 *
 * @author zhuguifei
 * @date 2026-04-15
 */
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/qm/matcheck")
public class QmMatcheckController extends BaseController {
    private final IQmMatcheckService qmMatcheckService;
    /**
     * æŸ¥è¯¢ææ–™æ£€éªŒç»Ÿè®¡åˆ—表
     */
    @SaCheckPermission("qm:matcheck:list")
    @GetMapping("/list")
    public TableDataInfo<QmMatcheckVo> list(QmMatcheckBo bo, PageQuery pageQuery) {
        return qmMatcheckService.queryPageList(bo, pageQuery);
    }
    @SaCheckPermission("qm:matcheck:list")
    @GetMapping("/listCheckItem")
    public R<List<Map<String, Object>>> listCheckItem(@RequestParam String judgeId) {
        return R.ok(qmMatcheckService.getQmCheckItem(judgeId));
    }
    /**
     * æŒ‰æ‰¹æ¬¡å’Œç‰Œå·æŸ¥è¯¢ææ–™æ£€éªŒç»Ÿè®¡(多表)
     */
    @SaCheckPermission("qm:matcheck:list")
    @GetMapping("/listQmMatcheck")
    public TableDataInfo<QmMatcheckVo> listQmMatcheck(QmMatcheckBo bo, PageQuery pageQuery) {
        return TableDataInfo.build(qmMatcheckService.listQmMatcheck(bo), pageQuery.build());
    }
    /**
     * å¯¼å‡ºææ–™æ£€éªŒç»Ÿè®¡åˆ—表
     */
    @SaCheckPermission("qm:matcheck:export")
    @Log(title = "材料检验统计", businessType = BusinessType.EXPORT)
    @PostMapping("/export")
    public void export(QmMatcheckBo bo, HttpServletResponse response) {
        List<QmMatcheckVo> list = qmMatcheckService.queryList(bo);
        ExcelUtil.exportExcel(list, "材料检验统计", QmMatcheckVo.class, response);
    }
    /**
     * èŽ·å–ææ–™æ£€éªŒç»Ÿè®¡è¯¦ç»†ä¿¡æ¯
     *
     * @param id ä¸»é”®
     */
    @SaCheckPermission("qm:matcheck:query")
    @GetMapping("/{id}")
    public R<QmMatcheckVo> getInfo(@NotNull(message = "主键不能为空")
                                     @PathVariable String id) {
        return R.ok(qmMatcheckService.queryById(id));
    }
    /**
     * æ–°å¢žææ–™æ£€éªŒç»Ÿè®¡
     */
    @SaCheckPermission("qm:matcheck:add")
    @Log(title = "材料检验统计", businessType = BusinessType.INSERT)
    @RepeatSubmit()
    @PostMapping()
    public R<Void> add(@Validated(AddGroup.class) @RequestBody QmMatcheckBo bo) {
        return toAjax(qmMatcheckService.insertByBo(bo));
    }
    /**
     * ä¿®æ”¹ææ–™æ£€éªŒç»Ÿè®¡
     */
    @SaCheckPermission("qm:matcheck:edit")
    @Log(title = "材料检验统计", businessType = BusinessType.UPDATE)
    @RepeatSubmit()
    @PutMapping()
    public R<Void> edit(@Validated(EditGroup.class) @RequestBody QmMatcheckBo bo) {
        return toAjax(qmMatcheckService.updateByBo(bo));
    }
    /**
     * åˆ é™¤ææ–™æ£€éªŒç»Ÿè®¡
     *
     * @param ids ä¸»é”®ä¸²
     */
    @SaCheckPermission("qm:matcheck:remove")
    @Log(title = "材料检验统计", businessType = BusinessType.DELETE)
    @DeleteMapping("/{ids}")
    public R<Void> remove(@NotEmpty(message = "主键不能为空")
                          @PathVariable String[] ids) {
        return toAjax(qmMatcheckService.deleteWithValidByIds(List.of(ids), true));
    }
}
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/qm/domain/QmJudgeDetails.java
@@ -44,17 +44,17 @@
    /**
     * æ ‡å‡†å€¼
     */
    private Long value3;
    private Double value3;
    /**
     * åˆ¤å®šå€¼1
     */
    private Long value1;
    private Double value1;
    /**
     * åˆ¤å®šå€¼2
     */
    private Long value2;
    private Double value2;
    /**
     * ç¼ºé™·ä½ç½®
@@ -69,7 +69,7 @@
    /**
     * åˆ†å€¼æ ‡å‡† (扣分标准,得分标准),比如不合格一次扣多少分
     */
    private Long stdscore;
    private Double stdscore;
    /**
     * æ ‡è®°æ­¤é¡¹æ˜¯å¦ä¸ºåˆæˆé¡¹ç›®ï¼Œæ¯”如外观,实际上关联了很多子项目
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/qm/domain/QmMatcheck.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,184 @@
package org.dromara.qa.qm.domain;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import java.io.Serial;
import java.util.Date;
/**
 * ææ–™æ£€éªŒç»Ÿè®¡å¯¹è±¡ qm_matcheck
 *
 * @author zhuguifei
 * @date 2026-04-15
 */
@Data
@TableName("qm_matcheck")
public class QmMatcheck  {
    @Serial
    private static final long serialVersionUID = 1L;
    /**
     * id
     */
    @TableId(value = "id")
    private String id;
    /**
     * æ±‡æ€»è¡¨ID
     */
    private String pid;
    /**
     * æ£€éªŒæ‰¹æ¬¡
     */
    private String batchCode;
    /**
     * ç‰Œå·
     */
    private String matCode;
    /**
     * ä»ªå™¨ç¼–号
     */
    private String instrumentCode;
    /**
     * æŠ€æœ¯è¦æ±‚
     */
    private String techReq;
    /**
     * æ£€éªŒä¾æ®
     */
    private String checkStd;
    /**
     * æµ‹è¯•环境
     */
    private String testEnv;
    /**
     * æ£€éªŒé¡¹ç›®COD
     */
    private String itemCode;
    /**
     * ç›˜å·
     */
    private String subBatchCode;
    /**
     * æ¯ç›˜æ£€éªŒæ•°é‡
     */
    private Long sampleNumber;
    /**
     * å–样类型
     */
    private Long sampleType;
    /**
     * æ£€éªŒå‘˜
     */
    private String checkName;
    /**
     * æ£€éªŒæ—¶é—´
     */
    private Date checkTime;
    /**
     * å¤æ ¸å‘˜
     */
    private String reviewName;
    /**
     * å¤æ ¸æ—¶é—´
     */
    private Date reviewTime;
    /**
     * æœ€å¤§å€¼
     */
    private Double maxval;
    /**
     * æœ€å°å€¼
     */
    private Double minval;
    /**
     * å¹³å‡å€¼
     */
    private Double avgval;
    /**
     * SD值
     */
    private Double sdval;
    /**
     * CV值
     */
    private Double cvval;
    /**
     * CPK值
     */
    private Double cpkval;
    /**
     * è¶…标数
     */
    private Double badval;
    /**
     * åˆ¤å®š
     */
    private String judge;
    /**
     * å•项判定
     */
    private String singlejudge;
    /**
     * ç‰ˆæœ¬åç§°
     */
    private String verName;
    /**
     * ç‰ˆæœ¬ç¼–号
     */
    private String verCode;
    /**
     * ä¿å­˜æœŸ
     */
    private String archDate;
    /**
     * åˆ é™¤æ ‡å¿—
     */
    private Long del;
    /**
     * 0-未上传mes   1-已上传
     */
    private String flag;
    /**
     * ä¸Šä¼ mes时间
     */
    private Date toMesTime;
    /**
     * å¤‡æ³¨
     */
    private String chkDes;
}
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/qm/domain/bo/QmJudgeDetailsBo.java
@@ -43,17 +43,17 @@
    /**
     * æ ‡å‡†å€¼
     */
    private Long value3;
    private Double value3;
    /**
     * åˆ¤å®šå€¼1
     */
    private Long value1;
    private Double value1;
    /**
     * åˆ¤å®šå€¼2
     */
    private Long value2;
    private Double value2;
    /**
     * ç¼ºé™·ä½ç½®
@@ -68,7 +68,7 @@
    /**
     * åˆ†å€¼æ ‡å‡† (扣分标准,得分标准),比如不合格一次扣多少分
     */
    private Long stdscore;
    private Double stdscore;
    /**
     * æ ‡è®°æ­¤é¡¹æ˜¯å¦ä¸ºåˆæˆé¡¹ç›®ï¼Œæ¯”如外观,实际上关联了很多子项目
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/qm/domain/bo/QmMatcheckBo.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,183 @@
package org.dromara.qa.qm.domain.bo;
import org.dromara.qa.qm.domain.QmMatcheck;
import org.dromara.common.mybatis.core.domain.BaseEntity;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.Date;
/**
 * ææ–™æ£€éªŒç»Ÿè®¡ä¸šåŠ¡å¯¹è±¡ qm_matcheck
 *
 * @author zhuguifei
 * @date 2026-04-15
 */
@Data
@EqualsAndHashCode(callSuper = true)
@AutoMapper(target = QmMatcheck.class, reverseConvertGenerate = false)
public class QmMatcheckBo extends BaseEntity {
    /**
     * id
     */
    private String id;
    /**
     * æ±‡æ€»è¡¨ID
     */
    private String pid;
    /**
     * æ£€éªŒæ‰¹æ¬¡
     */
    private String batchCode;
    /**
     * ç‰Œå·
     */
    private String matCode;
    /**
     * ä»ªå™¨ç¼–号
     */
    private String instrumentCode;
    /**
     * æŠ€æœ¯è¦æ±‚
     */
    private String techReq;
    /**
     * æ£€éªŒä¾æ®
     */
    private String checkStd;
    /**
     * æµ‹è¯•环境
     */
    private String testEnv;
    /**
     * æ£€éªŒé¡¹ç›®COD
     */
    private String itemCode;
    /**
     * ç›˜å·
     */
    private String subBatchCode;
    /**
     * æ¯ç›˜æ£€éªŒæ•°é‡
     */
    private Long sampleNumber;
    /**
     * å–样类型
     */
    private Long sampleType;
    /**
     * æ£€éªŒå‘˜
     */
    private String checkName;
    /**
     * æ£€éªŒæ—¶é—´
     */
    private Date checkTime;
    /**
     * å¤æ ¸å‘˜
     */
    private String reviewName;
    /**
     * å¤æ ¸æ—¶é—´
     */
    private Date reviewTime;
    /**
     * æœ€å¤§å€¼
     */
    private Double maxval;
    /**
     * æœ€å°å€¼
     */
    private Double minval;
    /**
     * å¹³å‡å€¼
     */
    private Double avgval;
    /**
     * SD值
     */
    private Double sdval;
    /**
     * CV值
     */
    private Double cvval;
    /**
     * CPK值
     */
    private Double cpkval;
    /**
     * è¶…标数
     */
    private Double badval;
    /**
     * åˆ¤å®š
     */
    private String judge;
    /**
     * å•项判定
     */
    private String singlejudge;
    /**
     * ç‰ˆæœ¬åç§°
     */
    private String verName;
    /**
     * ç‰ˆæœ¬ç¼–号
     */
    private String verCode;
    /**
     * ä¿å­˜æœŸ
     */
    private String archDate;
    /**
     * åˆ é™¤æ ‡å¿—
     */
    private Long del;
    /**
     * 0-未上传mes   1-已上传
     */
    private String flag;
    /**
     * ä¸Šä¼ mes时间
     */
    private Date toMesTime;
    /**
     * å¤‡æ³¨
     */
    private String chkDes;
}
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/qm/domain/vo/QmBatchVo.java
@@ -2,6 +2,8 @@
import java.util.Date;
import org.dromara.common.translation.annotation.Translation;
import org.dromara.common.translation.constant.TransConstant;
import org.dromara.qa.qm.domain.QmBatch;
import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
import cn.idev.excel.annotation.ExcelProperty;
@@ -59,8 +61,15 @@
    /**
     * ç‰Œå·
     */
    @ExcelProperty(value = "牌号")
    @ExcelProperty(value = "牌号code")
    private String matCode;
    /**
     * ç‰Œå·
     */
    @ExcelProperty(value = "牌号")
    @Translation(type = TransConstant.MAT_CODE_TO_NAME, mapper = "matCode")
    private String matName;
    /**
     * åˆ¤å®šä¾æ®ä»£ç 
@@ -68,6 +77,10 @@
    @ExcelProperty(value = "判定依据代码")
    private String judgeCode;
    @ExcelProperty(value = "判定依据")
    @Translation(type = TransConstant.JUDGE_CODE_TO_NAME, mapper = "judgeCode")
    private String judgeName;
    /**
     * æ‰¹æ¬¡ç”Ÿæˆæ—¥æœŸ
     */
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/qm/domain/vo/QmJudgeDetailsVo.java
@@ -11,7 +11,7 @@
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
import java.util.List;
/**
@@ -27,6 +27,8 @@
    @Serial
    private static final long serialVersionUID = 1L;
    private String id;
    /**
     * åˆ¤å®šä¸»æ ‡è¯†
@@ -50,19 +52,19 @@
     * æ ‡å‡†å€¼
     */
    @ExcelProperty(value = "标准值")
    private Long value3;
    private Double value3;
    /**
     * åˆ¤å®šå€¼1
     */
    @ExcelProperty(value = "判定值1")
    private Long value1;
    private Double value1;
    /**
     * åˆ¤å®šå€¼2
     */
    @ExcelProperty(value = "判定值2")
    private Long value2;
    private Double value2;
    /**
     * ç¼ºé™·ä½ç½®
@@ -80,7 +82,7 @@
     * åˆ†å€¼æ ‡å‡† (扣分标准,得分标准),比如不合格一次扣多少分
     */
    @ExcelProperty(value = "分值标准 (扣分标准,得分标准),比如不合格一次扣多少分")
    private Long stdscore;
    private Double stdscore;
    /**
     * æ ‡è®°æ­¤é¡¹æ˜¯å¦ä¸ºåˆæˆé¡¹ç›®ï¼Œæ¯”如外观,实际上关联了很多子项目
@@ -113,4 +115,46 @@
    private String updateUser;
    /**
     * å­æ‰¹æ¬¡ç 
     */
    @ExcelProperty(value = "子批次码")
    private String subBatchCode;
    /**
     * ä¸è‰¯å€¼
     */
    @ExcelProperty(value = "不良值")
    private Long badVal;
    /**
     * æ‰¹æ¬¡ç 
     */
    @ExcelProperty(value = "批次码")
    private String batchCode;
    /**
     * ç‰©æ–™ç 
     */
    @ExcelProperty(value = "物料码")
    private String matCode;
    /**
     * è®¾å¤‡
     */
    @ExcelProperty(value = "设备")
    private String eqp;
    /**
     * æ›´æ–°æ—¶é—´
     */
    @ExcelProperty(value = "更新时间")
    private Date updateTime;
    /**
     * å­é¡¹åˆ—表(用于树形结构)
     */
    private List<QmJudgeDetailsVo> children;
}
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/qm/domain/vo/QmJudgeVo.java
@@ -48,6 +48,7 @@
     * ç‰©æ–™ç‰Œå·
     */
    @ExcelProperty(value = "物料牌号")
    @Translation(type = TransConstant.MAT_CODE_TO_NAME, mapper = "matCode")
    private String matName;
    /**
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/qm/domain/vo/QmMatcheckVo.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,255 @@
package org.dromara.qa.qm.domain.vo;
import com.fasterxml.jackson.annotation.JsonFormat;
import org.dromara.qa.qm.domain.QmMatcheck;
import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
import cn.idev.excel.annotation.ExcelProperty;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
/**
 * ææ–™æ£€éªŒç»Ÿè®¡è§†å›¾å¯¹è±¡ qm_matcheck
 *
 * @author zhuguifei
 * @date 2026-04-15
 */
@Data
@ExcelIgnoreUnannotated
@AutoMapper(target = QmMatcheck.class)
public class QmMatcheckVo implements Serializable {
    @Serial
    private static final long serialVersionUID = 1L;
    /**
     * id
     */
    @ExcelProperty(value = "id")
    private String id;
    /**
     * æ±‡æ€»è¡¨ID
     */
    @ExcelProperty(value = "汇总表ID")
    private String pid;
    /**
     * æ£€éªŒæ‰¹æ¬¡
     */
    @ExcelProperty(value = "检验批次")
    private String batchCode;
    /**
     * ç‰Œå·
     */
    @ExcelProperty(value = "牌号")
    private String matCode;
    /**
     * ä»ªå™¨ç¼–号
     */
    @ExcelProperty(value = "仪器编号")
    private String instrumentCode;
    /**
     * æŠ€æœ¯è¦æ±‚
     */
    @ExcelProperty(value = "技术要求")
    private String techReq;
    /**
     * æ£€éªŒä¾æ®
     */
    @ExcelProperty(value = "检验依据")
    private String checkStd;
    /**
     * æµ‹è¯•环境
     */
    @ExcelProperty(value = "测试环境")
    private String testEnv;
    /**
     * æ£€éªŒé¡¹ç›®COD
     */
    @ExcelProperty(value = "检验项目COD")
    private String itemCode;
    /**
     * æ£€éªŒé¡¹ç›®åç§°
     */
    private String itemName;
    /**
     * è®¾å¤‡
     */
    private String eqp;
    /**
     * ç›˜å·
     */
    @ExcelProperty(value = "盘号")
    private String subBatchCode;
    /**
     * æ¯ç›˜æ£€éªŒæ•°é‡
     */
    @ExcelProperty(value = "每盘检验数量")
    private Long sampleNumber;
    /**
     * å–样类型
     */
    @ExcelProperty(value = "取样类型")
    private Long sampleType;
    /**
     * æ£€éªŒå‘˜
     */
    @ExcelProperty(value = "检验员")
    private String checkName;
    /**
     * æ£€éªŒæ—¶é—´
     */
    @ExcelProperty(value = "检验时间")
    @JsonFormat(pattern = "yyyy-MM-dd")
    private Date checkTime;
    /**
     * å¤æ ¸å‘˜
     */
    @ExcelProperty(value = "复核员")
    private String reviewName;
    /**
     * å¤æ ¸æ—¶é—´
     */
    @ExcelProperty(value = "复核时间")
    @JsonFormat(pattern = "yyyy-MM-dd")
    private Date reviewTime;
    /**
     * æœ€å¤§å€¼
     */
    @ExcelProperty(value = "最大值")
    private Double maxval;
    /**
     * æœ€å°å€¼
     */
    @ExcelProperty(value = "最小值")
    private Double minval;
    /**
     * å¹³å‡å€¼
     */
    @ExcelProperty(value = "平均值")
    private Double avgval;
    /**
     * SD值
     */
    @ExcelProperty(value = "SD值")
    private Double sdval;
    /**
     * CV值
     */
    @ExcelProperty(value = "CV值")
    private Double cvval;
    /**
     * CPK值
     */
    @ExcelProperty(value = "CPK值")
    private Double cpkval;
    /**
     * è¶…标数
     */
    @ExcelProperty(value = "超标数")
    private Double badval;
    /**
     * åˆ¤å®š
     */
    @ExcelProperty(value = "判定")
    private String judge;
    /**
     * å•项判定
     */
    @ExcelProperty(value = "单项判定")
    private String singlejudge;
    /**
     * ç‰ˆæœ¬åç§°
     */
    @ExcelProperty(value = "版本名称")
    private String verName;
    /**
     * ç‰ˆæœ¬ç¼–号
     */
    @ExcelProperty(value = "版本编号")
    private String verCode;
    /**
     * ä¿å­˜æœŸ
     */
    @ExcelProperty(value = "保存期")
    private String archDate;
    /**
     * åˆ é™¤æ ‡å¿—
     */
    @ExcelProperty(value = "删除标志")
    private Long del;
    /**
     * 0-未上传mes   1-已上传
     */
    @ExcelProperty(value = "0-未上传mes   1-已上传")
    private String flag;
    /**
     * ä¸Šä¼ mes时间
     */
    @ExcelProperty(value = "上传mes时间")
    private Date toMesTime;
    /**
     * å¤‡æ³¨
     */
    @ExcelProperty(value = "备注")
    private String chkDes;
    /**
     * æ£€éªŒäººå±•示名
     */
    private String checker;
    /**
     * åˆ¤å®šè§„程code(非数据库字段)
     */
    private String judgeCode;
    /**
     * æ˜¯å¦ç”Ÿæˆ30支烟明细(非数据库字段)
     */
    private String generateDetails;
    /**
     * æ£€æµ‹ç±»åž‹(非数据库字段)
     */
    private String checkType;
}
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/qm/mapper/QmMatcheckMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,38 @@
package org.dromara.qa.qm.mapper;
import org.apache.ibatis.annotations.Result;
import org.apache.ibatis.annotations.Results;
import org.dromara.qa.qm.domain.bo.QmMatcheckBo;
import org.dromara.qa.qm.domain.QmMatcheck;
import org.dromara.qa.qm.domain.vo.QmMatcheckVo;
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import java.util.List;
import java.util.Map;
/**
 * ææ–™æ£€éªŒç»Ÿè®¡Mapper接口
 *
 * @author zhuguifei
 * @date 2026-04-15
 */
public interface QmMatcheckMapper extends BaseMapperPlus<QmMatcheck, QmMatcheckVo> {
    @Results(id = "qmCheckItemMap", value = {
            @Result(property = "itemCode", column = "item_code"),
            @Result(property = "itemName", column = "item_name")
    })
    @Select("SELECT b.item_code, a.item_name FROM QM_JUDGE_DETAILS a " +
            "JOIN QM_CHECKITEM b ON a.item_cod = b.id " +
            "WHERE a.judge_id = #{judgeId} AND a.rid IS NULL AND a.category IN (0)")
    List<Map<String, Object>> getQmCheckItem(@Param("judgeId") String judgeId);
    /**
     * æŒ‰æ‰¹æ¬¡å’Œç‰Œå·æŸ¥è¯¢ææ–™æ£€éªŒç»Ÿè®¡(多表)
     *
     * @param bo æŸ¥è¯¢æ¡ä»¶
     * @return ææ–™æ£€éªŒç»Ÿè®¡åˆ—表
     */
    List<QmMatcheckVo> listQmMatcheck(@Param("bo") QmMatcheckBo bo);
}
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/qm/service/IQmJudgeDetailsService.java
@@ -42,6 +42,14 @@
    List<QmJudgeDetailsVo> queryList(QmJudgeDetailsBo bo);
    /**
     * æŸ¥è¯¢æ ‘形结构数据
     *
     * @param judgeId åˆ¤å®šä¸»æ ‡è¯†
     * @return æ ‘形结构列表
     */
    List<QmJudgeDetailsVo> queryTreeListByJudgeId(String judgeId);
    /**
     * æ–°å¢žåˆ¤å®šæ¨¡æ¿æ˜Žç»†
     *
     * @param bo åˆ¤å®šæ¨¡æ¿æ˜Žç»†
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/qm/service/IQmMatcheckService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,85 @@
package org.dromara.qa.qm.service;
import org.dromara.qa.qm.domain.vo.QmMatcheckVo;
import org.dromara.qa.qm.domain.bo.QmMatcheckBo;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.mybatis.core.page.PageQuery;
import java.util.Collection;
import java.util.List;
import java.util.Map;
/**
 * ææ–™æ£€éªŒç»Ÿè®¡Service接口
 *
 * @author zhuguifei
 * @date 2026-04-15
 */
public interface IQmMatcheckService {
    /**
     * æŸ¥è¯¢ææ–™æ£€éªŒç»Ÿè®¡
     *
     * @param id ä¸»é”®
     * @return ææ–™æ£€éªŒç»Ÿè®¡
     */
    QmMatcheckVo queryById(String id);
    /**
     * åˆ†é¡µæŸ¥è¯¢ææ–™æ£€éªŒç»Ÿè®¡åˆ—表
     *
     * @param bo        æŸ¥è¯¢æ¡ä»¶
     * @param pageQuery åˆ†é¡µå‚æ•°
     * @return ææ–™æ£€éªŒç»Ÿè®¡åˆ†é¡µåˆ—表
     */
    TableDataInfo<QmMatcheckVo> queryPageList(QmMatcheckBo bo, PageQuery pageQuery);
    /**
     * æŸ¥è¯¢ç¬¦åˆæ¡ä»¶çš„ææ–™æ£€éªŒç»Ÿè®¡åˆ—表
     *
     * @param bo æŸ¥è¯¢æ¡ä»¶
     * @return ææ–™æ£€éªŒç»Ÿè®¡åˆ—表
     */
    List<QmMatcheckVo> queryList(QmMatcheckBo bo);
    /**
     * æ–°å¢žææ–™æ£€éªŒç»Ÿè®¡
     *
     * @param bo ææ–™æ£€éªŒç»Ÿè®¡
     * @return æ˜¯å¦æ–°å¢žæˆåŠŸ
     */
    Boolean insertByBo(QmMatcheckBo bo);
    /**
     * ä¿®æ”¹ææ–™æ£€éªŒç»Ÿè®¡
     *
     * @param bo ææ–™æ£€éªŒç»Ÿè®¡
     * @return æ˜¯å¦ä¿®æ”¹æˆåŠŸ
     */
    Boolean updateByBo(QmMatcheckBo bo);
    /**
     * æ ¡éªŒå¹¶æ‰¹é‡åˆ é™¤ææ–™æ£€éªŒç»Ÿè®¡ä¿¡æ¯
     *
     * @param ids     å¾…删除的主键集合
     * @param isValid æ˜¯å¦è¿›è¡Œæœ‰æ•ˆæ€§æ ¡éªŒ
     * @return æ˜¯å¦åˆ é™¤æˆåŠŸ
     */
    Boolean deleteWithValidByIds(Collection<String> ids, Boolean isValid);
    /**
     * èŽ·å–æ£€éªŒé¡¹ç›®
     *
     * @param judgeId åˆ¤å®šID
     * @return æ£€éªŒé¡¹ç›®åˆ—表
     */
    List<Map<String, Object>> getQmCheckItem(String judgeId);
    /**
     * æŒ‰æ‰¹æ¬¡å’Œç‰Œå·æŸ¥è¯¢ææ–™æ£€éªŒç»Ÿè®¡(多表)
     *
     * @param bo æŸ¥è¯¢æ¡ä»¶
     * @return ææ–™æ£€éªŒç»Ÿè®¡åˆ—表
     */
    List<QmMatcheckVo> listQmMatcheck(QmMatcheckBo bo);
}
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/qm/service/impl/QmJudgeDetailsServiceImpl.java
@@ -70,6 +70,51 @@
        return baseMapper.selectVoList(lqw);
    }
    /**
     * æŸ¥è¯¢æ ‘形结构数据
     *
     * @param judgeId åˆ¤å®šä¸»æ ‡è¯†
     * @return æ ‘形结构列表
     */
    @Override
    public List<QmJudgeDetailsVo> queryTreeListByJudgeId(String judgeId) {
        if (StringUtils.isBlank(judgeId)) {
            return new java.util.ArrayList<>();
        }
        // 1. æŸ¥è¯¢æ ¹èŠ‚ç‚¹ (rid is null)
        List<QmJudgeDetailsVo> roots = selectTreeNodes(judgeId, null);
        for (QmJudgeDetailsVo root : roots) {
            // 2. æŸ¥è¯¢ç¬¬ä¸€çº§å­èŠ‚ç‚¹ (rid = root.itemCod)
            List<QmJudgeDetailsVo> children = selectTreeNodes(judgeId, root.getItemCod());
            for (QmJudgeDetailsVo child : children) {
                // 3. æŸ¥è¯¢ç¬¬äºŒçº§å­èŠ‚ç‚¹ (rid = child.itemCod)
                List<QmJudgeDetailsVo> grandchildren = selectTreeNodes(judgeId, child.getItemCod());
                child.setChildren(grandchildren);
            }
            root.setChildren(children);
        }
        return roots;
    }
    /**
     * æŸ¥è¯¢æ ‘节点
     *
     * @param judgeId åˆ¤å®šä¸»æ ‡è¯†
     * @param rid     å…³è”项ID
     * @return èŠ‚ç‚¹åˆ—è¡¨
     */
    private List<QmJudgeDetailsVo> selectTreeNodes(String judgeId, String rid) {
        LambdaQueryWrapper<QmJudgeDetails> lqw = Wrappers.lambdaQuery();
        lqw.eq(QmJudgeDetails::getJudgeId, judgeId);
        if (rid == null) {
            lqw.isNull(QmJudgeDetails::getRid);
        } else {
            lqw.eq(QmJudgeDetails::getRid, rid);
        }
        lqw.orderByAsc(QmJudgeDetails::getId);
        return baseMapper.selectVoList(lqw);
    }
    private LambdaQueryWrapper<QmJudgeDetails> buildQueryWrapper(QmJudgeDetailsBo bo) {
        Map<String, Object> params = bo.getParams();
        LambdaQueryWrapper<QmJudgeDetails> lqw = Wrappers.lambdaQuery();
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/qm/service/impl/QmJudgeServiceImpl.java
@@ -1,5 +1,6 @@
package org.dromara.qa.qm.service.impl;
import org.dromara.common.core.service.JudgeService;
import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.mybatis.core.page.TableDataInfo;
@@ -19,6 +20,13 @@
import org.dromara.qa.qm.service.IQmStdService;
import org.dromara.qa.qm.mapper.QmStdMapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import org.dromara.qa.qm.domain.QmCheckitem;
import org.dromara.qa.qm.domain.QmJudgeDetails;
import org.dromara.qa.qm.mapper.QmCheckitemMapper;
import org.dromara.qa.qm.mapper.QmJudgeDetailsMapper;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import java.util.Map;
import java.util.Collection;
@@ -35,10 +43,12 @@
@Slf4j
@RequiredArgsConstructor
@Service
public class QmJudgeServiceImpl implements IQmJudgeService {
public class QmJudgeServiceImpl implements IQmJudgeService, JudgeService {
    private final QmJudgeMapper baseMapper;
    private final QmStdMapper qmStdMapper;
    private final QmCheckitemMapper qmCheckitemMapper;
    private final QmJudgeDetailsMapper qmJudgeDetailsMapper;
    /**
     * æŸ¥è¯¢åˆ¤å®šä¾æ®
@@ -127,12 +137,52 @@
     * @return æ˜¯å¦æ–°å¢žæˆåŠŸ
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean insertByBo(QmJudgeBo bo) {
        QmJudge add = MapstructUtils.convert(bo, QmJudge.class);
        if (add.getCdate() == null) {
            add.setCdate(new java.util.Date());
        }
        validEntityBeforeSave(add);
        boolean flag = baseMapper.insert(add) > 0;
        if (flag) {
            bo.setId(add.getId());
            // 1. æ ¹æ® stdCod æŸ¥è¯¢å¯ç”¨çš„规程检验项目
            LambdaQueryWrapper<QmCheckitem> itemLqw = Wrappers.lambdaQuery();
            itemLqw.eq(QmCheckitem::getStdCode, bo.getStdCod());
            itemLqw.eq(QmCheckitem::getEnable, 1L);
            itemLqw.ne(QmCheckitem::getDel, 1L);
            List<QmCheckitem> checkitems = qmCheckitemMapper.selectList(itemLqw);
            if (!checkitems.isEmpty()) {
                List<QmJudgeDetails> detailsList = checkitems.stream().map(item -> {
                    QmJudgeDetails detail = new QmJudgeDetails();
                    detail.setJudgeId(add.getId());
                    detail.setItemCod(item.getId());
                    detail.setItemName(item.getItemName());
                    detail.setValue3(0.0);
                    detail.setValue1(0.0);
                    detail.setValue2(0.0);
                    detail.setLocation(item.getLocation());
                    detail.setCls(item.getCheckLevel());
                    detail.setStdscore(item.getScore() != null ? item.getScore() : 0.0);
                    detail.setIsmix(item.getIsmix() != null ? item.getIsmix() : 0L);
                    detail.setRid(item.getRid());
                    detail.setCategory(item.getCategory());
                    detail.setDecisionDes(item.getItemDes());
                    return detail;
                }).collect(Collectors.toList());
                qmJudgeDetailsMapper.insertBatch(detailsList);
            }
            // 2. å°†åŽŸæ¥å­˜åœ¨çš„ç›¸åŒç‰©æ–™ç‰Œå·å’Œç±»åž‹çš„ä¾æ®è®¾ç½®ä¸ºç¦ç”¨
            LambdaUpdateWrapper<QmJudge> updateWrapper = Wrappers.lambdaUpdate();
            updateWrapper.set(QmJudge::getStatus, 0L)
                .eq(QmJudge::getMatCode, bo.getMatCode())
                .eq(QmJudge::getCategory, bo.getCategory())
                .ne(QmJudge::getId, add.getId());
            baseMapper.update(null, updateWrapper);
        }
        return flag;
    }
@@ -171,4 +221,13 @@
        }
        return baseMapper.deleteByIds(ids) > 0;
    }
    @Override
    public String selectJudgeNameByCode(String judgeCode) {
        QmJudgeVo qmJudgeVo = baseMapper.selectVoById(judgeCode);
        if(qmJudgeVo!=null){
            return qmJudgeVo.getJudgeName();
        }
        return null;
    }
}
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/qm/service/impl/QmMatcheckServiceImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,154 @@
package org.dromara.qa.qm.service.impl;
import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.mybatis.core.page.PageQuery;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.dromara.qa.qm.domain.bo.QmMatcheckBo;
import org.dromara.qa.qm.domain.vo.QmMatcheckVo;
import org.dromara.qa.qm.domain.QmMatcheck;
import org.dromara.qa.qm.mapper.QmMatcheckMapper;
import org.dromara.qa.qm.service.IQmMatcheckService;
import java.util.List;
import java.util.Map;
import java.util.Collection;
/**
 * ææ–™æ£€éªŒç»Ÿè®¡Service业务层处理
 *
 * @author zhuguifei
 * @date 2026-04-15
 */
@Slf4j
@RequiredArgsConstructor
@Service
public class QmMatcheckServiceImpl implements IQmMatcheckService {
    private final QmMatcheckMapper baseMapper;
    /**
     * æŸ¥è¯¢ææ–™æ£€éªŒç»Ÿè®¡
     *
     * @param id ä¸»é”®
     * @return ææ–™æ£€éªŒç»Ÿè®¡
     */
    @Override
    public QmMatcheckVo queryById(String id){
        return baseMapper.selectVoById(id);
    }
    /**
     * åˆ†é¡µæŸ¥è¯¢ææ–™æ£€éªŒç»Ÿè®¡åˆ—表
     *
     * @param bo        æŸ¥è¯¢æ¡ä»¶
     * @param pageQuery åˆ†é¡µå‚æ•°
     * @return ææ–™æ£€éªŒç»Ÿè®¡åˆ†é¡µåˆ—表
     */
    @Override
    public TableDataInfo<QmMatcheckVo> queryPageList(QmMatcheckBo bo, PageQuery pageQuery) {
        LambdaQueryWrapper<QmMatcheck> lqw = buildQueryWrapper(bo);
        Page<QmMatcheckVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
        return TableDataInfo.build(result);
    }
    /**
     * æŸ¥è¯¢ç¬¦åˆæ¡ä»¶çš„ææ–™æ£€éªŒç»Ÿè®¡åˆ—表
     *
     * @param bo æŸ¥è¯¢æ¡ä»¶
     * @return ææ–™æ£€éªŒç»Ÿè®¡åˆ—表
     */
    @Override
    public List<QmMatcheckVo> queryList(QmMatcheckBo bo) {
        LambdaQueryWrapper<QmMatcheck> lqw = buildQueryWrapper(bo);
        return baseMapper.selectVoList(lqw);
    }
    private LambdaQueryWrapper<QmMatcheck> buildQueryWrapper(QmMatcheckBo bo) {
        LambdaQueryWrapper<QmMatcheck> lqw = Wrappers.lambdaQuery();
        lqw.like(StringUtils.isNotBlank(bo.getBatchCode()), QmMatcheck::getBatchCode, bo.getBatchCode());
        lqw.eq(StringUtils.isNotBlank(bo.getMatCode()), QmMatcheck::getMatCode, bo.getMatCode());
        lqw.like(StringUtils.isNotBlank(bo.getCheckName()), QmMatcheck::getCheckName, bo.getCheckName());
        return lqw;
    }
    /**
     * æ–°å¢žææ–™æ£€éªŒç»Ÿè®¡
     *
     * @param bo ææ–™æ£€éªŒç»Ÿè®¡
     * @return æ˜¯å¦æ–°å¢žæˆåŠŸ
     */
    @Override
    public Boolean insertByBo(QmMatcheckBo bo) {
        QmMatcheck add = MapstructUtils.convert(bo, QmMatcheck.class);
        validEntityBeforeSave(add);
        boolean flag = baseMapper.insert(add) > 0;
        if (flag) {
            bo.setId(add.getId());
        }
        return flag;
    }
    /**
     * ä¿®æ”¹ææ–™æ£€éªŒç»Ÿè®¡
     *
     * @param bo ææ–™æ£€éªŒç»Ÿè®¡
     * @return æ˜¯å¦ä¿®æ”¹æˆåŠŸ
     */
    @Override
    public Boolean updateByBo(QmMatcheckBo bo) {
        QmMatcheck update = MapstructUtils.convert(bo, QmMatcheck.class);
        validEntityBeforeSave(update);
        return baseMapper.updateById(update) > 0;
    }
    /**
     * ä¿å­˜å‰çš„æ•°æ®æ ¡éªŒ
     */
    private void validEntityBeforeSave(QmMatcheck entity){
        //TODO åšä¸€äº›æ•°æ®æ ¡éªŒ,如唯一约束
    }
    /**
     * æ ¡éªŒå¹¶æ‰¹é‡åˆ é™¤ææ–™æ£€éªŒç»Ÿè®¡ä¿¡æ¯
     *
     * @param ids     å¾…删除的主键集合
     * @param isValid æ˜¯å¦è¿›è¡Œæœ‰æ•ˆæ€§æ ¡éªŒ
     * @return æ˜¯å¦åˆ é™¤æˆåŠŸ
     */
    @Override
    public Boolean deleteWithValidByIds(Collection<String> ids, Boolean isValid) {
        if(isValid){
            //TODO åšä¸€äº›ä¸šåŠ¡ä¸Šçš„æ ¡éªŒ,判断是否需要校验
        }
        return baseMapper.deleteByIds(ids) > 0;
    }
    /**
     * èŽ·å–æ£€éªŒé¡¹ç›®
     *
     * @param judgeId åˆ¤å®šID
     * @return æ£€éªŒé¡¹ç›®åˆ—表
     */
    @Override
    public List<Map<String, Object>> getQmCheckItem(String judgeId) {
        return baseMapper.getQmCheckItem(judgeId);
    }
    /**
     * æŒ‰æ‰¹æ¬¡å’Œç‰Œå·æŸ¥è¯¢ææ–™æ£€éªŒç»Ÿè®¡(多表)
     *
     * @param bo æŸ¥è¯¢æ¡ä»¶
     * @return ææ–™æ£€éªŒç»Ÿè®¡åˆ—表
     */
    @Override
    public List<QmMatcheckVo> listQmMatcheck(QmMatcheckBo bo) {
        return baseMapper.listQmMatcheck(bo);
    }
}
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/sc/md/controller/MdMatController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,105 @@
package org.dromara.sc.md.controller;
import java.util.List;
import lombok.RequiredArgsConstructor;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.constraints.*;
import cn.dev33.satoken.annotation.SaCheckPermission;
import org.springframework.web.bind.annotation.*;
import org.springframework.validation.annotation.Validated;
import org.dromara.common.idempotent.annotation.RepeatSubmit;
import org.dromara.common.log.annotation.Log;
import org.dromara.common.web.core.BaseController;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.core.domain.R;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup;
import org.dromara.common.log.enums.BusinessType;
import org.dromara.common.excel.utils.ExcelUtil;
import org.dromara.sc.md.domain.vo.MdMatVo;
import org.dromara.sc.md.domain.bo.MdMatBo;
import org.dromara.sc.md.service.IMdMatService;
import org.dromara.common.mybatis.core.page.TableDataInfo;
/**
 * ç‰©æ–™
 *
 * @author zhuguifei
 * @date 2026-04-13
 */
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/md/mat")
public class MdMatController extends BaseController {
    private final IMdMatService mdMatService;
    /**
     * æŸ¥è¯¢ç‰©æ–™åˆ—表
     */
    @SaCheckPermission("md:mat:list")
    @GetMapping("/list")
    public TableDataInfo<MdMatVo> list(MdMatBo bo, PageQuery pageQuery) {
        return mdMatService.queryPageList(bo, pageQuery);
    }
    /**
     * å¯¼å‡ºç‰©æ–™åˆ—表
     */
    @SaCheckPermission("md:mat:export")
    @Log(title = "物料", businessType = BusinessType.EXPORT)
    @PostMapping("/export")
    public void export(MdMatBo bo, HttpServletResponse response) {
        List<MdMatVo> list = mdMatService.queryList(bo);
        ExcelUtil.exportExcel(list, "物料", MdMatVo.class, response);
    }
    /**
     * èŽ·å–ç‰©æ–™è¯¦ç»†ä¿¡æ¯
     *
     * @param id ä¸»é”®
     */
    @SaCheckPermission("md:mat:query")
    @GetMapping("/{id}")
    public R<MdMatVo> getInfo(@NotNull(message = "主键不能为空")
                                     @PathVariable String id) {
        return R.ok(mdMatService.queryById(id));
    }
    /**
     * æ–°å¢žç‰©æ–™
     */
    @SaCheckPermission("md:mat:add")
    @Log(title = "物料", businessType = BusinessType.INSERT)
    @RepeatSubmit()
    @PostMapping()
    public R<Void> add(@Validated(AddGroup.class) @RequestBody MdMatBo bo) {
        return toAjax(mdMatService.insertByBo(bo));
    }
    /**
     * ä¿®æ”¹ç‰©æ–™
     */
    @SaCheckPermission("md:mat:edit")
    @Log(title = "物料", businessType = BusinessType.UPDATE)
    @RepeatSubmit()
    @PutMapping()
    public R<Void> edit(@Validated(EditGroup.class) @RequestBody MdMatBo bo) {
        return toAjax(mdMatService.updateByBo(bo));
    }
    /**
     * åˆ é™¤ç‰©æ–™
     *
     * @param ids ä¸»é”®ä¸²
     */
    @SaCheckPermission("md:mat:remove")
    @Log(title = "物料", businessType = BusinessType.DELETE)
    @DeleteMapping("/{ids}")
    public R<Void> remove(@NotEmpty(message = "主键不能为空")
                          @PathVariable String[] ids) {
        return toAjax(mdMatService.deleteWithValidByIds(List.of(ids), true));
    }
}
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/sc/md/controller/MdMatTypeController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,105 @@
package org.dromara.sc.md.controller;
import java.util.List;
import lombok.RequiredArgsConstructor;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.constraints.*;
import cn.dev33.satoken.annotation.SaCheckPermission;
import org.springframework.web.bind.annotation.*;
import org.springframework.validation.annotation.Validated;
import org.dromara.common.idempotent.annotation.RepeatSubmit;
import org.dromara.common.log.annotation.Log;
import org.dromara.common.web.core.BaseController;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.core.domain.R;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup;
import org.dromara.common.log.enums.BusinessType;
import org.dromara.common.excel.utils.ExcelUtil;
import org.dromara.sc.md.domain.vo.MdMatTypeVo;
import org.dromara.sc.md.domain.bo.MdMatTypeBo;
import org.dromara.sc.md.service.IMdMatTypeService;
import org.dromara.common.mybatis.core.page.TableDataInfo;
/**
 * ç‰©æ–™ç±»åž‹
 *
 * @author zhuguifei
 * @date 2026-04-13
 */
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/md/matType")
public class MdMatTypeController extends BaseController {
    private final IMdMatTypeService mdMatTypeService;
    /**
     * æŸ¥è¯¢ç‰©æ–™ç±»åž‹åˆ—表
     */
    @SaCheckPermission("md:matType:list")
    @GetMapping("/list")
    public TableDataInfo<MdMatTypeVo> list(MdMatTypeBo bo, PageQuery pageQuery) {
        return mdMatTypeService.queryPageList(bo, pageQuery);
    }
    /**
     * å¯¼å‡ºç‰©æ–™ç±»åž‹åˆ—表
     */
    @SaCheckPermission("md:matType:export")
    @Log(title = "物料类型", businessType = BusinessType.EXPORT)
    @PostMapping("/export")
    public void export(MdMatTypeBo bo, HttpServletResponse response) {
        List<MdMatTypeVo> list = mdMatTypeService.queryList(bo);
        ExcelUtil.exportExcel(list, "物料类型", MdMatTypeVo.class, response);
    }
    /**
     * èŽ·å–ç‰©æ–™ç±»åž‹è¯¦ç»†ä¿¡æ¯
     *
     * @param id ä¸»é”®
     */
    @SaCheckPermission("md:matType:query")
    @GetMapping("/{id}")
    public R<MdMatTypeVo> getInfo(@NotNull(message = "主键不能为空")
                                     @PathVariable String id) {
        return R.ok(mdMatTypeService.queryById(id));
    }
    /**
     * æ–°å¢žç‰©æ–™ç±»åž‹
     */
    @SaCheckPermission("md:matType:add")
    @Log(title = "物料类型", businessType = BusinessType.INSERT)
    @RepeatSubmit()
    @PostMapping()
    public R<Void> add(@Validated(AddGroup.class) @RequestBody MdMatTypeBo bo) {
        return toAjax(mdMatTypeService.insertByBo(bo));
    }
    /**
     * ä¿®æ”¹ç‰©æ–™ç±»åž‹
     */
    @SaCheckPermission("md:matType:edit")
    @Log(title = "物料类型", businessType = BusinessType.UPDATE)
    @RepeatSubmit()
    @PutMapping()
    public R<Void> edit(@Validated(EditGroup.class) @RequestBody MdMatTypeBo bo) {
        return toAjax(mdMatTypeService.updateByBo(bo));
    }
    /**
     * åˆ é™¤ç‰©æ–™ç±»åž‹
     *
     * @param ids ä¸»é”®ä¸²
     */
    @SaCheckPermission("md:matType:remove")
    @Log(title = "物料类型", businessType = BusinessType.DELETE)
    @DeleteMapping("/{ids}")
    public R<Void> remove(@NotEmpty(message = "主键不能为空")
                          @PathVariable String[] ids) {
        return toAjax(mdMatTypeService.deleteWithValidByIds(List.of(ids), true));
    }
}
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/sc/md/domain/MdMat.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,97 @@
package org.dromara.sc.md.domain;
import org.dromara.common.mybatis.core.domain.BaseEntity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.Date;
import com.fasterxml.jackson.annotation.JsonFormat;
import java.io.Serial;
/**
 * ç‰©æ–™å¯¹è±¡ MD_MAT
 *
 * @author zhuguifei
 * @date 2026-04-13
 */
@Data
@TableName("MD_MAT")
public class MdMat {
    @Serial
    private static final long serialVersionUID = 1L;
    /**
     * id
     */
    @TableId(value = "ID")
    private String id;
    /**
     * ç‰©æ–™ä»£ç 
     */
    private String code;
    /**
     * å…¨ç§°
     */
    private String name;
    /**
     * ç®€ç§°
     */
    private String simpleName;
    /**
     * æè¿°
     */
    private String des;
    /**
     * å•位
     */
    private String uid;
    /**
     * æœ€åŽæ›´æ–°æ—¶é—´
     */
    private Date lastUpdateTime;
    /**
     * å¯ç”¨
     */
    private Long enable;
    /**
     * åˆ é™¤
     */
    private String del;
    /**
     * ç‰©æ–™ç±»åž‹
     */
    private String tid;
    /**
     * åˆ›å»ºäºº
     */
    private String createUserName;
    /**
     * åˆ›å»ºæ—¶é—´
     */
    private Date createUserTime;
    /**
     * æ›´æ–°äºº
     */
    private String updateUserName;
    /**
     * æ›´æ–°æ—¶é—´
     */
    private Date updateUserTime;
}
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/sc/md/domain/MdMatType.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,87 @@
package org.dromara.sc.md.domain;
import org.dromara.common.mybatis.core.domain.BaseEntity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.Date;
import com.fasterxml.jackson.annotation.JsonFormat;
import java.io.Serial;
/**
 * ç‰©æ–™ç±»åž‹å¯¹è±¡ MD_MAT_TYPE
 *
 * @author zhuguifei
 * @date 2026-04-13
 */
@Data
@TableName("MD_MAT_TYPE")
public class MdMatType {
    @Serial
    private static final long serialVersionUID = 1L;
    /**
     * id
     */
    @TableId(value = "ID")
    private String id;
    /**
     * ç‰©æ–™ç»„外键
     */
    private String gid;
    /**
     * ç¼–码
     */
    private String code;
    /**
     * åç§°
     */
    private String name;
    /**
     * æè¿°
     */
    private String des;
    /**
     * å¯ç”¨
     */
    private Long enable;
    /**
     * åˆ é™¤
     */
    private String del;
    /**
     * mes编码
     */
    private String mesCode;
    /**
     * åˆ›å»ºäºº
     */
    private String createUserName;
    /**
     * åˆ›å»ºæ—¶é—´
     */
    private Date createUserTime;
    /**
     * æ›´æ–°äºº
     */
    private String updateUserName;
    /**
     * æ›´æ–°æ—¶é—´
     */
    private Date updateUserTime;
}
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/sc/md/domain/bo/MdMatBo.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,97 @@
package org.dromara.sc.md.domain.bo;
import org.dromara.sc.md.domain.MdMat;
import org.dromara.common.mybatis.core.domain.BaseEntity;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import lombok.EqualsAndHashCode;
import jakarta.validation.constraints.*;
import java.util.Date;
import com.fasterxml.jackson.annotation.JsonFormat;
/**
 * ç‰©æ–™ä¸šåŠ¡å¯¹è±¡ MD_MAT
 *
 * @author zhuguifei
 * @date 2026-04-13
 */
@Data
@EqualsAndHashCode(callSuper = true)
@AutoMapper(target = MdMat.class, reverseConvertGenerate = false)
public class MdMatBo extends BaseEntity {
    /**
     * id
     */
    @NotBlank(message = "id不能为空", groups = { EditGroup.class })
    private String id;
    /**
     * ç‰©æ–™ä»£ç 
     */
    private String code;
    /**
     * å…¨ç§°
     */
    private String name;
    /**
     * ç®€ç§°
     */
    private String simpleName;
    /**
     * æè¿°
     */
    private String des;
    /**
     * å•位
     */
    private String uid;
    /**
     * æœ€åŽæ›´æ–°æ—¶é—´
     */
    private Date lastUpdateTime;
    /**
     * å¯ç”¨
     */
    private Long enable;
    /**
     * åˆ é™¤
     */
    private String del;
    /**
     * ç‰©æ–™ç±»åž‹
     */
    private String tid;
    /**
     * åˆ›å»ºäºº
     */
    private String createUserName;
    /**
     * åˆ›å»ºæ—¶é—´
     */
    private Date createUserTime;
    /**
     * æ›´æ–°äºº
     */
    private String updateUserName;
    /**
     * æ›´æ–°æ—¶é—´
     */
    private Date updateUserTime;
}
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/sc/md/domain/bo/MdMatTypeBo.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,87 @@
package org.dromara.sc.md.domain.bo;
import org.dromara.sc.md.domain.MdMatType;
import org.dromara.common.mybatis.core.domain.BaseEntity;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import lombok.EqualsAndHashCode;
import jakarta.validation.constraints.*;
import java.util.Date;
import com.fasterxml.jackson.annotation.JsonFormat;
/**
 * ç‰©æ–™ç±»åž‹ä¸šåŠ¡å¯¹è±¡ MD_MAT_TYPE
 *
 * @author zhuguifei
 * @date 2026-04-13
 */
@Data
@EqualsAndHashCode(callSuper = true)
@AutoMapper(target = MdMatType.class, reverseConvertGenerate = false)
public class MdMatTypeBo extends BaseEntity {
    /**
     * id
     */
    @NotBlank(message = "id不能为空", groups = { EditGroup.class })
    private String id;
    /**
     * ç‰©æ–™ç»„外键
     */
    private String gid;
    /**
     * ç¼–码
     */
    private String code;
    /**
     * åç§°
     */
    private String name;
    /**
     * æè¿°
     */
    private String des;
    /**
     * å¯ç”¨
     */
    private Long enable;
    /**
     * åˆ é™¤
     */
    private String del;
    /**
     * mes编码
     */
    private String mesCode;
    /**
     * åˆ›å»ºäºº
     */
    private String createUserName;
    /**
     * åˆ›å»ºæ—¶é—´
     */
    private Date createUserTime;
    /**
     * æ›´æ–°äºº
     */
    private String updateUserName;
    /**
     * æ›´æ–°æ—¶é—´
     */
    private Date updateUserTime;
}
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/sc/md/domain/vo/MdMatTypeVo.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,106 @@
package org.dromara.sc.md.domain.vo;
import java.util.Date;
import com.fasterxml.jackson.annotation.JsonFormat;
import org.dromara.sc.md.domain.MdMatType;
import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
import cn.idev.excel.annotation.ExcelProperty;
import org.dromara.common.excel.annotation.ExcelDictFormat;
import org.dromara.common.excel.convert.ExcelDictConvert;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
/**
 * ç‰©æ–™ç±»åž‹è§†å›¾å¯¹è±¡ MD_MAT_TYPE
 *
 * @author zhuguifei
 * @date 2026-04-13
 */
@Data
@ExcelIgnoreUnannotated
@AutoMapper(target = MdMatType.class)
public class MdMatTypeVo implements Serializable {
    @Serial
    private static final long serialVersionUID = 1L;
    /**
     * id
     */
    @ExcelProperty(value = "id")
    private String id;
    /**
     * ç‰©æ–™ç»„外键
     */
    @ExcelProperty(value = "物料组外键")
    private String gid;
    /**
     * ç¼–码
     */
    @ExcelProperty(value = "编码")
    private String code;
    /**
     * åç§°
     */
    @ExcelProperty(value = "名称")
    private String name;
    /**
     * æè¿°
     */
    @ExcelProperty(value = "描述")
    private String des;
    /**
     * å¯ç”¨
     */
    @ExcelProperty(value = "启用")
    private Long enable;
    /**
     * åˆ é™¤
     */
    @ExcelProperty(value = "删除")
    private String del;
    /**
     * mes编码
     */
    @ExcelProperty(value = "mes编码")
    private String mesCode;
    /**
     * åˆ›å»ºäºº
     */
    @ExcelProperty(value = "创建人")
    private String createUserName;
    /**
     * åˆ›å»ºæ—¶é—´
     */
    @ExcelProperty(value = "创建时间")
    private Date createUserTime;
    /**
     * æ›´æ–°äºº
     */
    @ExcelProperty(value = "更新人")
    private String updateUserName;
    /**
     * æ›´æ–°æ—¶é—´
     */
    @ExcelProperty(value = "更新时间")
    private Date updateUserTime;
}
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/sc/md/domain/vo/MdMatVo.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,118 @@
package org.dromara.sc.md.domain.vo;
import java.util.Date;
import com.fasterxml.jackson.annotation.JsonFormat;
import org.dromara.sc.md.domain.MdMat;
import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
import cn.idev.excel.annotation.ExcelProperty;
import org.dromara.common.excel.annotation.ExcelDictFormat;
import org.dromara.common.excel.convert.ExcelDictConvert;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
/**
 * ç‰©æ–™è§†å›¾å¯¹è±¡ MD_MAT
 *
 * @author zhuguifei
 * @date 2026-04-13
 */
@Data
@ExcelIgnoreUnannotated
@AutoMapper(target = MdMat.class)
public class MdMatVo implements Serializable {
    @Serial
    private static final long serialVersionUID = 1L;
    /**
     * id
     */
    @ExcelProperty(value = "id")
    private String id;
    /**
     * ç‰©æ–™ä»£ç 
     */
    @ExcelProperty(value = "物料代码")
    private String code;
    /**
     * å…¨ç§°
     */
    @ExcelProperty(value = "全称")
    private String name;
    /**
     * ç®€ç§°
     */
    @ExcelProperty(value = "简称")
    private String simpleName;
    /**
     * æè¿°
     */
    @ExcelProperty(value = "描述")
    private String des;
    /**
     * å•位
     */
    @ExcelProperty(value = "单位")
    private String uid;
    /**
     * æœ€åŽæ›´æ–°æ—¶é—´
     */
    @ExcelProperty(value = "最后更新时间")
    private Date lastUpdateTime;
    /**
     * å¯ç”¨
     */
    @ExcelProperty(value = "启用")
    private Long enable;
    /**
     * åˆ é™¤
     */
    @ExcelProperty(value = "删除")
    private String del;
    /**
     * ç‰©æ–™ç±»åž‹
     */
    @ExcelProperty(value = "物料类型")
    private String tid;
    /**
     * åˆ›å»ºäºº
     */
    @ExcelProperty(value = "创建人")
    private String createUserName;
    /**
     * åˆ›å»ºæ—¶é—´
     */
    @ExcelProperty(value = "创建时间")
    private Date createUserTime;
    /**
     * æ›´æ–°äºº
     */
    @ExcelProperty(value = "更新人")
    private String updateUserName;
    /**
     * æ›´æ–°æ—¶é—´
     */
    @ExcelProperty(value = "更新时间")
    private Date updateUserTime;
}
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/sc/md/mapper/MdMatMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,15 @@
package org.dromara.sc.md.mapper;
import org.dromara.sc.md.domain.MdMat;
import org.dromara.sc.md.domain.vo.MdMatVo;
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
/**
 * ç‰©æ–™Mapper接口
 *
 * @author zhuguifei
 * @date 2026-04-13
 */
public interface MdMatMapper extends BaseMapperPlus<MdMat, MdMatVo> {
}
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/sc/md/mapper/MdMatTypeMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,15 @@
package org.dromara.sc.md.mapper;
import org.dromara.sc.md.domain.MdMatType;
import org.dromara.sc.md.domain.vo.MdMatTypeVo;
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
/**
 * ç‰©æ–™ç±»åž‹Mapper接口
 *
 * @author zhuguifei
 * @date 2026-04-13
 */
public interface MdMatTypeMapper extends BaseMapperPlus<MdMatType, MdMatTypeVo> {
}
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/sc/md/service/IMdMatService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,68 @@
package org.dromara.sc.md.service;
import org.dromara.sc.md.domain.vo.MdMatVo;
import org.dromara.sc.md.domain.bo.MdMatBo;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.mybatis.core.page.PageQuery;
import java.util.Collection;
import java.util.List;
/**
 * ç‰©æ–™Service接口
 *
 * @author zhuguifei
 * @date 2026-04-13
 */
public interface IMdMatService {
    /**
     * æŸ¥è¯¢ç‰©æ–™
     *
     * @param id ä¸»é”®
     * @return ç‰©æ–™
     */
    MdMatVo queryById(String id);
    /**
     * åˆ†é¡µæŸ¥è¯¢ç‰©æ–™åˆ—表
     *
     * @param bo        æŸ¥è¯¢æ¡ä»¶
     * @param pageQuery åˆ†é¡µå‚æ•°
     * @return ç‰©æ–™åˆ†é¡µåˆ—表
     */
    TableDataInfo<MdMatVo> queryPageList(MdMatBo bo, PageQuery pageQuery);
    /**
     * æŸ¥è¯¢ç¬¦åˆæ¡ä»¶çš„物料列表
     *
     * @param bo æŸ¥è¯¢æ¡ä»¶
     * @return ç‰©æ–™åˆ—表
     */
    List<MdMatVo> queryList(MdMatBo bo);
    /**
     * æ–°å¢žç‰©æ–™
     *
     * @param bo ç‰©æ–™
     * @return æ˜¯å¦æ–°å¢žæˆåŠŸ
     */
    Boolean insertByBo(MdMatBo bo);
    /**
     * ä¿®æ”¹ç‰©æ–™
     *
     * @param bo ç‰©æ–™
     * @return æ˜¯å¦ä¿®æ”¹æˆåŠŸ
     */
    Boolean updateByBo(MdMatBo bo);
    /**
     * æ ¡éªŒå¹¶æ‰¹é‡åˆ é™¤ç‰©æ–™ä¿¡æ¯
     *
     * @param ids     å¾…删除的主键集合
     * @param isValid æ˜¯å¦è¿›è¡Œæœ‰æ•ˆæ€§æ ¡éªŒ
     * @return æ˜¯å¦åˆ é™¤æˆåŠŸ
     */
    Boolean deleteWithValidByIds(Collection<String> ids, Boolean isValid);
}
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/sc/md/service/IMdMatTypeService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,68 @@
package org.dromara.sc.md.service;
import org.dromara.sc.md.domain.vo.MdMatTypeVo;
import org.dromara.sc.md.domain.bo.MdMatTypeBo;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.mybatis.core.page.PageQuery;
import java.util.Collection;
import java.util.List;
/**
 * ç‰©æ–™ç±»åž‹Service接口
 *
 * @author zhuguifei
 * @date 2026-04-13
 */
public interface IMdMatTypeService {
    /**
     * æŸ¥è¯¢ç‰©æ–™ç±»åž‹
     *
     * @param id ä¸»é”®
     * @return ç‰©æ–™ç±»åž‹
     */
    MdMatTypeVo queryById(String id);
    /**
     * åˆ†é¡µæŸ¥è¯¢ç‰©æ–™ç±»åž‹åˆ—表
     *
     * @param bo        æŸ¥è¯¢æ¡ä»¶
     * @param pageQuery åˆ†é¡µå‚æ•°
     * @return ç‰©æ–™ç±»åž‹åˆ†é¡µåˆ—表
     */
    TableDataInfo<MdMatTypeVo> queryPageList(MdMatTypeBo bo, PageQuery pageQuery);
    /**
     * æŸ¥è¯¢ç¬¦åˆæ¡ä»¶çš„物料类型列表
     *
     * @param bo æŸ¥è¯¢æ¡ä»¶
     * @return ç‰©æ–™ç±»åž‹åˆ—表
     */
    List<MdMatTypeVo> queryList(MdMatTypeBo bo);
    /**
     * æ–°å¢žç‰©æ–™ç±»åž‹
     *
     * @param bo ç‰©æ–™ç±»åž‹
     * @return æ˜¯å¦æ–°å¢žæˆåŠŸ
     */
    Boolean insertByBo(MdMatTypeBo bo);
    /**
     * ä¿®æ”¹ç‰©æ–™ç±»åž‹
     *
     * @param bo ç‰©æ–™ç±»åž‹
     * @return æ˜¯å¦ä¿®æ”¹æˆåŠŸ
     */
    Boolean updateByBo(MdMatTypeBo bo);
    /**
     * æ ¡éªŒå¹¶æ‰¹é‡åˆ é™¤ç‰©æ–™ç±»åž‹ä¿¡æ¯
     *
     * @param ids     å¾…删除的主键集合
     * @param isValid æ˜¯å¦è¿›è¡Œæœ‰æ•ˆæ€§æ ¡éªŒ
     * @return æ˜¯å¦åˆ é™¤æˆåŠŸ
     */
    Boolean deleteWithValidByIds(Collection<String> ids, Boolean isValid);
}
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/sc/md/service/impl/MdMatServiceImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,147 @@
package org.dromara.sc.md.service.impl;
import com.baomidou.dynamic.datasource.annotation.DS;
import org.dromara.common.core.service.MatService;
import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.mybatis.core.page.PageQuery;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.dromara.sc.md.domain.bo.MdMatBo;
import org.dromara.sc.md.domain.vo.MdMatVo;
import org.dromara.sc.md.domain.MdMat;
import org.dromara.sc.md.mapper.MdMatMapper;
import org.dromara.sc.md.service.IMdMatService;
import java.util.List;
import java.util.Map;
import java.util.Collection;
/**
 * ç‰©æ–™Service业务层处理
 *
 * @author zhuguifei
 * @date 2026-04-13
 */
@DS("oracle_sc")
@Slf4j
@RequiredArgsConstructor
@Service
public class MdMatServiceImpl implements IMdMatService, MatService {
    private final MdMatMapper baseMapper;
    /**
     * æŸ¥è¯¢ç‰©æ–™
     *
     * @param id ä¸»é”®
     * @return ç‰©æ–™
     */
    @Override
    public MdMatVo queryById(String id){
        return baseMapper.selectVoById(id);
    }
    /**
     * åˆ†é¡µæŸ¥è¯¢ç‰©æ–™åˆ—表
     *
     * @param bo        æŸ¥è¯¢æ¡ä»¶
     * @param pageQuery åˆ†é¡µå‚æ•°
     * @return ç‰©æ–™åˆ†é¡µåˆ—表
     */
    @Override
    public TableDataInfo<MdMatVo> queryPageList(MdMatBo bo, PageQuery pageQuery) {
        LambdaQueryWrapper<MdMat> lqw = buildQueryWrapper(bo);
        Page<MdMatVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
        return TableDataInfo.build(result);
    }
    /**
     * æŸ¥è¯¢ç¬¦åˆæ¡ä»¶çš„物料列表
     *
     * @param bo æŸ¥è¯¢æ¡ä»¶
     * @return ç‰©æ–™åˆ—表
     */
    @Override
    public List<MdMatVo> queryList(MdMatBo bo) {
        LambdaQueryWrapper<MdMat> lqw = buildQueryWrapper(bo);
        return baseMapper.selectVoList(lqw);
    }
    private LambdaQueryWrapper<MdMat> buildQueryWrapper(MdMatBo bo) {
        Map<String, Object> params = bo.getParams();
        LambdaQueryWrapper<MdMat> lqw = Wrappers.lambdaQuery();
        lqw.orderByAsc(MdMat::getId);
        lqw.eq(StringUtils.isNotBlank(bo.getCode()), MdMat::getCode, bo.getCode());
        lqw.like(StringUtils.isNotBlank(bo.getName()), MdMat::getName, bo.getName());
        lqw.like(StringUtils.isNotBlank(bo.getSimpleName()), MdMat::getSimpleName, bo.getSimpleName());
        lqw.eq(bo.getEnable() != null, MdMat::getEnable, bo.getEnable());
        lqw.eq(StringUtils.isNotBlank(bo.getTid()), MdMat::getTid, bo.getTid());
        return lqw;
    }
    /**
     * æ–°å¢žç‰©æ–™
     *
     * @param bo ç‰©æ–™
     * @return æ˜¯å¦æ–°å¢žæˆåŠŸ
     */
    @Override
    public Boolean insertByBo(MdMatBo bo) {
        MdMat add = MapstructUtils.convert(bo, MdMat.class);
        validEntityBeforeSave(add);
        boolean flag = baseMapper.insert(add) > 0;
        if (flag) {
            bo.setId(add.getId());
        }
        return flag;
    }
    /**
     * ä¿®æ”¹ç‰©æ–™
     *
     * @param bo ç‰©æ–™
     * @return æ˜¯å¦ä¿®æ”¹æˆåŠŸ
     */
    @Override
    public Boolean updateByBo(MdMatBo bo) {
        MdMat update = MapstructUtils.convert(bo, MdMat.class);
        validEntityBeforeSave(update);
        return baseMapper.updateById(update) > 0;
    }
    /**
     * ä¿å­˜å‰çš„æ•°æ®æ ¡éªŒ
     */
    private void validEntityBeforeSave(MdMat entity){
        //TODO åšä¸€äº›æ•°æ®æ ¡éªŒ,如唯一约束
    }
    /**
     * æ ¡éªŒå¹¶æ‰¹é‡åˆ é™¤ç‰©æ–™ä¿¡æ¯
     *
     * @param ids     å¾…删除的主键集合
     * @param isValid æ˜¯å¦è¿›è¡Œæœ‰æ•ˆæ€§æ ¡éªŒ
     * @return æ˜¯å¦åˆ é™¤æˆåŠŸ
     */
    @Override
    public Boolean deleteWithValidByIds(Collection<String> ids, Boolean isValid) {
        if(isValid){
            //TODO åšä¸€äº›ä¸šåŠ¡ä¸Šçš„æ ¡éªŒ,判断是否需要校验
        }
        return baseMapper.deleteByIds(ids) > 0;
    }
    @Override
    public String selectMatNameByCode(String matCode) {
        LambdaQueryWrapper<MdMat> lqw = Wrappers.lambdaQuery();
        lqw.eq(MdMat::getCode,matCode);
        MdMatVo mdMatVo = baseMapper.selectVoOne(lqw);
        return mdMatVo.getName();
    }
}
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/sc/md/service/impl/MdMatTypeServiceImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,138 @@
package org.dromara.sc.md.service.impl;
import com.baomidou.dynamic.datasource.annotation.DS;
import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.mybatis.core.page.PageQuery;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.dromara.sc.md.domain.bo.MdMatTypeBo;
import org.dromara.sc.md.domain.vo.MdMatTypeVo;
import org.dromara.sc.md.domain.MdMatType;
import org.dromara.sc.md.mapper.MdMatTypeMapper;
import org.dromara.sc.md.service.IMdMatTypeService;
import java.util.List;
import java.util.Map;
import java.util.Collection;
/**
 * ç‰©æ–™ç±»åž‹Service业务层处理
 *
 * @author zhuguifei
 * @date 2026-04-13
 */
@DS("oracle_sc")
@Slf4j
@RequiredArgsConstructor
@Service
public class MdMatTypeServiceImpl implements IMdMatTypeService {
    private final MdMatTypeMapper baseMapper;
    /**
     * æŸ¥è¯¢ç‰©æ–™ç±»åž‹
     *
     * @param id ä¸»é”®
     * @return ç‰©æ–™ç±»åž‹
     */
    @Override
    public MdMatTypeVo queryById(String id){
        return baseMapper.selectVoById(id);
    }
    /**
     * åˆ†é¡µæŸ¥è¯¢ç‰©æ–™ç±»åž‹åˆ—表
     *
     * @param bo        æŸ¥è¯¢æ¡ä»¶
     * @param pageQuery åˆ†é¡µå‚æ•°
     * @return ç‰©æ–™ç±»åž‹åˆ†é¡µåˆ—表
     */
    @Override
    public TableDataInfo<MdMatTypeVo> queryPageList(MdMatTypeBo bo, PageQuery pageQuery) {
        LambdaQueryWrapper<MdMatType> lqw = buildQueryWrapper(bo);
        Page<MdMatTypeVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
        return TableDataInfo.build(result);
    }
    /**
     * æŸ¥è¯¢ç¬¦åˆæ¡ä»¶çš„物料类型列表
     *
     * @param bo æŸ¥è¯¢æ¡ä»¶
     * @return ç‰©æ–™ç±»åž‹åˆ—表
     */
    @Override
    public List<MdMatTypeVo> queryList(MdMatTypeBo bo) {
        LambdaQueryWrapper<MdMatType> lqw = buildQueryWrapper(bo);
        return baseMapper.selectVoList(lqw);
    }
    private LambdaQueryWrapper<MdMatType> buildQueryWrapper(MdMatTypeBo bo) {
        Map<String, Object> params = bo.getParams();
        LambdaQueryWrapper<MdMatType> lqw = Wrappers.lambdaQuery();
        lqw.orderByAsc(MdMatType::getId);
        lqw.like(StringUtils.isNotBlank(bo.getCode()), MdMatType::getCode, bo.getCode());
        lqw.like(StringUtils.isNotBlank(bo.getName()), MdMatType::getName, bo.getName());
        lqw.eq(bo.getEnable() != null, MdMatType::getEnable, bo.getEnable());
        lqw.eq(StringUtils.isNotBlank(bo.getDel()), MdMatType::getDel, bo.getDel());
        lqw.like(StringUtils.isNotBlank(bo.getMesCode()), MdMatType::getMesCode, bo.getMesCode());
        return lqw;
    }
    /**
     * æ–°å¢žç‰©æ–™ç±»åž‹
     *
     * @param bo ç‰©æ–™ç±»åž‹
     * @return æ˜¯å¦æ–°å¢žæˆåŠŸ
     */
    @Override
    public Boolean insertByBo(MdMatTypeBo bo) {
        MdMatType add = MapstructUtils.convert(bo, MdMatType.class);
        validEntityBeforeSave(add);
        boolean flag = baseMapper.insert(add) > 0;
        if (flag) {
            bo.setId(add.getId());
        }
        return flag;
    }
    /**
     * ä¿®æ”¹ç‰©æ–™ç±»åž‹
     *
     * @param bo ç‰©æ–™ç±»åž‹
     * @return æ˜¯å¦ä¿®æ”¹æˆåŠŸ
     */
    @Override
    public Boolean updateByBo(MdMatTypeBo bo) {
        MdMatType update = MapstructUtils.convert(bo, MdMatType.class);
        validEntityBeforeSave(update);
        return baseMapper.updateById(update) > 0;
    }
    /**
     * ä¿å­˜å‰çš„æ•°æ®æ ¡éªŒ
     */
    private void validEntityBeforeSave(MdMatType entity){
        //TODO åšä¸€äº›æ•°æ®æ ¡éªŒ,如唯一约束
    }
    /**
     * æ ¡éªŒå¹¶æ‰¹é‡åˆ é™¤ç‰©æ–™ç±»åž‹ä¿¡æ¯
     *
     * @param ids     å¾…删除的主键集合
     * @param isValid æ˜¯å¦è¿›è¡Œæœ‰æ•ˆæ€§æ ¡éªŒ
     * @return æ˜¯å¦åˆ é™¤æˆåŠŸ
     */
    @Override
    public Boolean deleteWithValidByIds(Collection<String> ids, Boolean isValid) {
        if(isValid){
            //TODO åšä¸€äº›ä¸šåŠ¡ä¸Šçš„æ ¡éªŒ,判断是否需要校验
        }
        return baseMapper.deleteByIds(ids) > 0;
    }
}
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/sc/test
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/resources/mapper/qa/qm/QmMatcheckMapper.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,67 @@
<?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="org.dromara.qa.qm.mapper.QmMatcheckMapper">
    <select id="listQmMatcheck" resultType="org.dromara.qa.qm.domain.vo.QmMatcheckVo">
        SELECT
            a.ID AS id,
            a.PID AS pid,
            a.BATCH_CODE AS batchCode,
            a.MAT_CODE AS matCode,
            a.INSTRUMENT_CODE AS instrumentCode,
            a.TECH_REQ AS techReq,
            a.CHECK_STD AS checkStd,
            a.TEST_ENV AS testEnv,
            a.ITEM_CODE AS itemCode,
            d.ITEM_NAME AS itemName,
            CAST(NULL AS VARCHAR(100)) AS eqp,
            a.SUB_BATCH_CODE AS subBatchCode,
            a.SAMPLE_NUMBER AS sampleNumber,
            a.SAMPLE_TYPE AS sampleType,
            a.CHECK_NAME AS checkName,
            a.CHECK_TIME AS checkTime,
            a.REVIEW_NAME AS reviewName,
            a.REVIEW_TIME AS reviewTime,
            a.MAXVAL AS maxval,
            a.MINVAL AS minval,
            a.AVGVAL AS avgval,
            a.SDVAL AS sdval,
            a.CVVAL AS cvval,
            a.CPKVAL AS cpkval,
            a.BADVAL AS badval,
            a.JUDGE AS judge,
            a.SINGLEJUDGE AS singlejudge,
            a.VER_NAME AS verName,
            a.VER_CODE AS verCode,
            a.ARCH_DATE AS archDate,
            a.DEL AS del,
            a.FLAG AS flag,
            a.TO_MES_TIME AS toMesTime,
            a.CHK_DES AS chkDes,
            CAST(NULL AS VARCHAR(200)) AS checker,
            b.JUDGE_CODE AS judgeCode,
            CAST(NULL AS VARCHAR(10)) AS generateDetails,
            CAST(NULL AS VARCHAR(10)) AS checkType
        FROM QM_MATCHECK a
            JOIN QM_BATCH b ON b.BATCH_CODE = a.BATCH_CODE
                AND b.MAT_CODE = a.MAT_CODE
            JOIN QM_JUDGE c ON b.JUDGE_CODE = c.ID
            JOIN QM_CHECKITEM d ON d.STD_CODE = c.STD_COD
                AND d.ITEM_CODE = a.ITEM_CODE
                AND d.RID IS NULL
                AND d.DEL != 1
        <where>
            <if test="bo.batchCode != null and bo.batchCode != ''">
                AND a.BATCH_CODE = #{bo.batchCode}
            </if>
            <if test="bo.matCode != null and bo.matCode != ''">
                AND a.MAT_CODE = #{bo.matCode}
            </if>
            AND (a.DEL = 0 OR a.DEL IS NULL)
            AND b.DELETED != '1'
        </where>
        ORDER BY a.ITEM_CODE, a.SUB_BATCH_CODE
    </select>
</mapper>
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/resources/mapper/sc/md/MdMatMapper.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,6 @@
<?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="org.dromara.sc.md.mapper.MdMatMapper">
</mapper>
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/resources/mapper/sc/md/MdMatTypeMapper.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,6 @@
<?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="org.dromara.sc.md.mapper.MdMatTypeMapper">
</mapper>
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/resources/mapper/sc/test
ruoyi-plus-soybean/src/router/elegant/imports.ts
@@ -33,6 +33,8 @@
  demo_tree: () => import("@/views/demo/tree/index.vue"),
  home: () => import("@/views/home/index.vue"),
  md_instrument: () => import("@/views/md/instrument/index.vue"),
  "md_mat-type": () => import("@/views/md/mat-type/index.vue"),
  md_mat: () => import("@/views/md/mat/index.vue"),
  md_shift: () => import("@/views/md/shift/index.vue"),
  "md_weighing-box": () => import("@/views/md/weighing-box/index.vue"),
  monitor_cache: () => import("@/views/monitor/cache/index.vue"),
@@ -42,6 +44,7 @@
  qm_batch: () => import("@/views/qm/batch/index.vue"),
  "qm_judge-details": () => import("@/views/qm/judge-details/index.vue"),
  qm_judge: () => import("@/views/qm/judge/index.vue"),
  qm_matcheck: () => import("@/views/qm/matcheck/index.vue"),
  qm_std: () => import("@/views/qm/std/index.vue"),
  report_demo: () => import("@/views/report/demo/index.vue"),
  "report_silk-storage-output": () => import("@/views/report/silk-storage-output/index.vue"),
ruoyi-plus-soybean/src/router/elegant/routes.ts
@@ -199,6 +199,24 @@
        }
      },
      {
        name: 'md_mat',
        path: '/md/mat',
        component: 'view.md_mat',
        meta: {
          title: 'md_mat',
          i18nKey: 'route.md_mat'
        }
      },
      {
        name: 'md_mat-type',
        path: '/md/mat-type',
        component: 'view.md_mat-type',
        meta: {
          title: 'md_mat-type',
          i18nKey: 'route.md_mat-type'
        }
      },
      {
        name: 'md_shift',
        path: '/md/shift',
        component: 'view.md_shift',
@@ -302,6 +320,15 @@
        }
      },
      {
        name: 'qm_matcheck',
        path: '/qm/matcheck',
        component: 'view.qm_matcheck',
        meta: {
          title: 'qm_matcheck',
          i18nKey: 'route.qm_matcheck'
        }
      },
      {
        name: 'qm_std',
        path: '/qm/std',
        component: 'view.qm_std',
ruoyi-plus-soybean/src/router/elegant/transform.ts
@@ -186,6 +186,8 @@
  "login": "/login/:module(pwd-login|code-login|register|reset-pwd|bind-wechat)?",
  "md": "/md",
  "md_instrument": "/md/instrument",
  "md_mat": "/md/mat",
  "md_mat-type": "/md/mat-type",
  "md_shift": "/md/shift",
  "md_weighing-box": "/md/weighing-box",
  "monitor": "/monitor",
@@ -197,6 +199,7 @@
  "qm_batch": "/qm/batch",
  "qm_judge": "/qm/judge",
  "qm_judge-details": "/qm/judge-details",
  "qm_matcheck": "/qm/matcheck",
  "qm_std": "/qm/std",
  "report": "/report",
  "report_demo": "/report/demo",
ruoyi-plus-soybean/src/service/api/md/mat-type.ts
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,35 @@
import { request } from '@/service/request';
/** èŽ·å–ç‰©æ–™ç±»åž‹åˆ—è¡¨ */
export function fetchGetMatTypeList (params?: Api.Md.MatTypeSearchParams) {
    return request<Api.Md.MatTypeList>({
        url: '/md/matType/list',
        method: 'get',
        params
    });
}
/** æ–°å¢žç‰©æ–™ç±»åž‹ */
export function fetchCreateMatType (data: Api.Md.MatTypeOperateParams) {
    return request<boolean>({
        url: '/md/matType',
        method: 'post',
        data
    });
}
/** ä¿®æ”¹ç‰©æ–™ç±»åž‹ */
export function fetchUpdateMatType (data: Api.Md.MatTypeOperateParams) {
    return request<boolean>({
        url: '/md/matType',
        method: 'put',
        data
    });
}
/** æ‰¹é‡åˆ é™¤ç‰©æ–™ç±»åž‹ */
export function fetchBatchDeleteMatType (ids: CommonType.IdType[]) {
    return request<boolean>({
        url: `/md/matType/${ids.join(',')}`,
        method: 'delete'
    });
}
ruoyi-plus-soybean/src/service/api/md/mat.ts
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,35 @@
import { request } from '@/service/request';
/** èŽ·å–ç‰©æ–™åˆ—è¡¨ */
export function fetchGetMatList (params?: Api.Md.MatSearchParams) {
    return request<Api.Md.MatList>({
        url: '/md/mat/list',
        method: 'get',
        params
    });
}
/** æ–°å¢žç‰©æ–™ */
export function fetchCreateMat (data: Api.Md.MatOperateParams) {
    return request<boolean>({
        url: '/md/mat',
        method: 'post',
        data
    });
}
/** ä¿®æ”¹ç‰©æ–™ */
export function fetchUpdateMat (data: Api.Md.MatOperateParams) {
    return request<boolean>({
        url: '/md/mat',
        method: 'put',
        data
    });
}
/** æ‰¹é‡åˆ é™¤ç‰©æ–™ */
export function fetchBatchDeleteMat (ids: CommonType.IdType[]) {
    return request<boolean>({
        url: `/md/mat/${ids.join(',')}`,
        method: 'delete'
    });
}
ruoyi-plus-soybean/src/service/api/qm/judge-details.ts
@@ -8,6 +8,16 @@
    params
  });
}
/** èŽ·å–åˆ¤å®šæ¨¡æ¿æ˜Žç»†æ ‘å½¢åˆ—è¡¨ */
export function fetchGetJudgeDetailsTree(params?: Api.Qm.JudgeDetailsSearchParams) {
  return request<Api.Qm.JudgeDetailsList>({
    url: '/qm/judgeDetails/tree',
    method: 'get',
    params
  });
}
/** æ–°å¢žåˆ¤å®šæ¨¡æ¿æ˜Žç»† */
export function fetchCreateJudgeDetails(data: Api.Qm.JudgeDetailsOperateParams) {
  return request<boolean>({
ruoyi-plus-soybean/src/service/api/qm/matcheck.ts
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,57 @@
import { request } from '@/service/request';
/** èŽ·å–ææ–™æ£€éªŒç»Ÿè®¡åˆ—è¡¨ */
export function fetchGetMatcheckList (params?: Api.Qm.MatcheckSearchParams) {
    return request<Api.Qm.MatcheckList>({
        url: '/qm/matcheck/list',
        method: 'get',
        params
    });
}
export function fetchCheckItemList (params?: Api.Qm.MatcheckSearchParams) {
  return request<Api.Qm.MatcheckCustomItem[]>({
    url: '/qm/matcheck/listCheckItem',
    method: 'get',
    params
  });
}
/**
 * å¤šè¡¨æŸ¥è¯¢
 * @param params
 */
export function fetchGetQmMatcheckList (params?: Api.Qm.MatcheckSearchParams) {
  return request<Api.Qm.MatcheckList[]>({
    url: '/qm/matcheck/listQmMatcheck',
    method: 'get',
    params
  });
}
/** æ–°å¢žææ–™æ£€éªŒç»Ÿè®¡ */
export function fetchCreateMatcheck (data: Api.Qm.MatcheckOperateParams) {
    return request<boolean>({
        url: '/qm/matcheck',
        method: 'post',
        data
    });
}
/** ä¿®æ”¹ææ–™æ£€éªŒç»Ÿè®¡ */
export function fetchUpdateMatcheck (data: Api.Qm.MatcheckOperateParams) {
    return request<boolean>({
        url: '/qm/matcheck',
        method: 'put',
        data
    });
}
/** æ‰¹é‡åˆ é™¤ææ–™æ£€éªŒç»Ÿè®¡ */
export function fetchBatchDeleteMatcheck (ids: CommonType.IdType[]) {
    return request<boolean>({
        url: `/qm/matcheck/${ids.join(',')}`,
        method: 'delete'
    });
}
ruoyi-plus-soybean/src/typings/api/md.mat-type.api.d.ts
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,76 @@
/**
 * Namespace Api
 *
 * All backend api type
 */
declare namespace Api {
    /**
     * namespace Md
     *
     * backend api module: "Md"
     */
    namespace Md {
        /** mat type */
        type MatType = Common.CommonRecord<{
            /** id */
                id: CommonType.IdType;
            /** ç‰©æ–™ç»„外键 */
                gid: CommonType.IdType;
            /** ç¼–码 */
                code: string;
            /** åç§° */
                name: string;
            /** æè¿° */
                des: string;
            /** å¯ç”¨ */
                enable: number;
            /** åˆ é™¤ */
                del: string;
            /** mes编码 */
                mesCode: string;
            /** åˆ›å»ºäºº */
                createUserName: string;
            /** åˆ›å»ºæ—¶é—´ */
                createUserTime: string;
            /** æ›´æ–°äºº */
                updateUserName: string;
            /** æ›´æ–°æ—¶é—´ */
                updateUserTime: string;
        }>;
        /** mat type search params */
        type MatTypeSearchParams = CommonType.RecordNullable<
            Pick<
                Api.Md.MatType,
                        | 'code'
                        | 'name'
                        | 'enable'
                        | 'del'
                        | 'mesCode'
            > &
            Api.Common.CommonSearchParams
        >;
        /** mat type operate params */
        type MatTypeOperateParams = CommonType.RecordNullable<
            Pick<
                Api.Md.MatType,
                        | 'id'
                        | 'gid'
                        | 'code'
                        | 'name'
                        | 'des'
                        | 'enable'
                        | 'del'
                        | 'mesCode'
                        | 'createUserName'
                        | 'createUserTime'
                        | 'updateUserName'
                        | 'updateUserTime'
            >
        >;
        /** mat type list */
        type MatTypeList = Api.Common.PaginatingQueryRecord<MatType>;
    }
}
ruoyi-plus-soybean/src/typings/api/md.mat.api.d.ts
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,83 @@
/**
 * Namespace Api
 *
 * All backend api type
 */
declare namespace Api {
    /**
     * namespace Md
     *
     * backend api module: "Md"
     */
    namespace Md {
        /** mat */
        type Mat = Common.CommonRecord<{
            /** id */
                id: CommonType.IdType;
            /** ç‰©æ–™ä»£ç  */
                code: string;
            /** å…¨ç§° */
                name: string;
            /** ç®€ç§° */
                simpleName: string;
            /** æè¿° */
                des: string;
            /** å•位 */
                uid: CommonType.IdType;
            /** æœ€åŽæ›´æ–°æ—¶é—´ */
                lastUpdateTime: string;
            /** å¯ç”¨ */
                enable: number;
            /** åˆ é™¤ */
                del: string;
            /** ç‰©æ–™ç±»åž‹ */
                tid: CommonType.IdType;
            /** åˆ›å»ºäºº */
                createUserName: string;
            /** åˆ›å»ºæ—¶é—´ */
                createUserTime: string;
            /** æ›´æ–°äºº */
                updateUserName: string;
            /** æ›´æ–°æ—¶é—´ */
                updateUserTime: string;
        }>;
        /** mat search params */
        type MatSearchParams = CommonType.RecordNullable<
            Pick<
                Api.Md.Mat,
                        | 'code'
                        | 'name'
                        | 'simpleName'
                        | 'enable'
                        | 'del'
                        | 'tid'
            > &
            Api.Common.CommonSearchParams
        >;
        /** mat operate params */
        type MatOperateParams = CommonType.RecordNullable<
            Pick<
                Api.Md.Mat,
                        | 'id'
                        | 'code'
                        | 'name'
                        | 'simpleName'
                        | 'des'
                        | 'uid'
                        | 'lastUpdateTime'
                        | 'enable'
                        | 'del'
                        | 'tid'
                        | 'createUserName'
                        | 'createUserTime'
                        | 'updateUserName'
                        | 'updateUserTime'
            >
        >;
        /** mat list */
        type MatList = Api.Common.PaginatingQueryRecord<Mat>;
    }
}
ruoyi-plus-soybean/src/typings/api/qm.batch.api.d.ts
@@ -24,6 +24,8 @@
      eqpCode: string;
      /** ç‰Œå· */
      matCode: string;
      /** ç‰Œå· */
      matName: string;
      /** åˆ¤å®šä¾æ®ä»£ç  */
      judgeCode: string;
      /** æ‰¹æ¬¡ç”Ÿæˆæ—¥æœŸ */
@@ -113,6 +115,7 @@
      Pick<
        Api.Qm.Batch,
        | 'batchCode'
        | 'isflag'
        | 'typ'
        | 'eqpCode'
        | 'matCode'
@@ -120,6 +123,7 @@
        | 'flag'
        | 'toMesDate'
        | 'fromMesDate'
        | 'enabled'
        | 'deleted'
        | 'category'
        | 'state'
ruoyi-plus-soybean/src/typings/api/qm.judge-details.api.d.ts
@@ -16,32 +16,54 @@
      id: CommonType.IdType;
      /** åˆ¤å®šä¸»æ ‡è¯† */
      judgeId: CommonType.IdType;
      /** åˆ¤å®šé¡¹ITEM */
      /** æ£€éªŒé¡¹ç›®ä»£ç  */
      itemCode: string;
      /** åˆ¤å®šé¡¹ITEM (兼容旧字段) */
      itemCod: string;
      /** åˆ¤å®šé¡¹NAME */
      /** æ£€éªŒé¡¹ç›®åç§° */
      itemName: string;
      /** å•位 */
      unit: string;
      /** å¯ç”¨ */
      enable: number;
      /** åˆ é™¤ */
      del: number;
      /** æ£€éªŒé¡¹æè¿° */
      itemDes: string;
      /** è§„程代码 */
      stdCode: string;
      /** ä»ªå™¨æè¿° */
      instrumentDes: string;
      /** æ ‡å‡†å€¼ */
      value3: number;
      /** åˆ¤å®šå€¼1 */
      value1: number;
      /** åˆ¤å®šå€¼2 */
      value2: number;
      /** ç¼ºé™·ä½ç½® */
      /** ç¼ºé™·ä½ç½®-外观用 */
      location: string;
      /** åˆ¤å®šçº§åˆ« (A,B,C,D) */
      /** åˆ†A,B,C,D四个级别 */
      checkLevel: string;
      /** åˆ¤å®šçº§åˆ« (A,B,C,D) (兼容旧字段) */
      cls: string;
      /** åˆ†å€¼æ ‡å‡† (扣分标准,得分标准),比如不合格一次扣多少分 */
      /** åˆ†å€¼ */
      score: number;
      /** åˆ†å€¼æ ‡å‡† (扣分标准,得分标准) (兼容旧字段) */
      stdscore: number;
      /** æ ‡è®°æ­¤é¡¹æ˜¯å¦ä¸ºåˆæˆé¡¹ç›®ï¼Œæ¯”如外观,实际上关联了很多子项目 */
      /** æ˜¯å¦åˆæˆé¡¹ */
      ismix: number;
      /** è‹¥æ­¤å­—段有UUID值,表明它可能为其他项目的子项,比如“空头”,它为烟支外观项目的子项 */
      /** å…³è”项ID */
      rid: CommonType.IdType;
      /** èŒƒå›´-备用 */
      /** ç±»åˆ« 0:成品 1辅料 */
      category: number;
      /** å¤‡æ³¨ */
      /** ä»ªå™¨ç¼–码 */
      instrumentCode: string;
      /** å¤‡æ³¨ (兼容旧字段) */
      decisionDes: string;
      /** ä¿®æ”¹äºº */
      updateUser: string;
      /** å­èŠ‚ç‚¹ */
      children?: Api.Qm.JudgeDetails[];
    }>;
    /** judge details search params */
ruoyi-plus-soybean/src/typings/api/qm.matcheck.api.d.ts
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,165 @@
/**
 * Namespace Api
 *
 * All backend api type
 */
declare namespace Api {
    /**
     * namespace Qm
     *
     * backend api module: "Qm"
     */
    namespace Qm {
        /** matcheck */
        type Matcheck = Common.CommonRecord<{
            /** id */
                id: CommonType.IdType;
            /** æ±‡æ€»è¡¨ID */
                pid: CommonType.IdType;
            /** æ£€éªŒæ‰¹æ¬¡ */
                batchCode: string;
            /** ç‰Œå· */
                matCode: string;
            /** ä»ªå™¨ç¼–号 */
                instrumentCode: string;
            /** æŠ€æœ¯è¦æ±‚ */
                techReq: string;
            /** æ£€éªŒä¾æ® */
                checkStd: string;
            /** æµ‹è¯•环境 */
                testEnv: string;
            /** æ£€éªŒé¡¹ç›®COD */
                itemCode: string;
            /** æ£€éªŒé¡¹ç›®åç§° */
                itemName: string;
            /** è®¾å¤‡ */
                eqp: string;
            /** ç›˜å· */
                subBatchCode: string;
            /** æ¯ç›˜æ£€éªŒæ•°é‡ */
                sampleNumber: number;
            /** å–样类型 */
                sampleType: number;
            /** æ£€éªŒå‘˜ */
                checkName: string;
            /** æ£€éªŒæ—¶é—´ */
                checkTime: string;
            /** å¤æ ¸å‘˜ */
                reviewName: string;
            /** å¤æ ¸æ—¶é—´ */
                reviewTime: string;
            /** æœ€å¤§å€¼ */
                maxval: number;
            /** æœ€å°å€¼ */
                minval: number;
            /** å¹³å‡å€¼ */
                avgval: number;
            /** SD值 */
                sdval: number;
            /** CV值 */
                cvval: number;
            /** CPK值 */
                cpkval: number;
            /** è¶…标数 */
                badval: number;
            /** åˆ¤å®š */
                judge: string;
            /** å•项判定 */
                singlejudge: string;
            /** ç‰ˆæœ¬åç§° */
                verName: string;
            /** ç‰ˆæœ¬ç¼–号 */
                verCode: string;
            /** ä¿å­˜æœŸ */
                archDate: string;
            /** åˆ é™¤æ ‡å¿— */
                del: number;
            /** 0-未上传mes 1-已上传 */
                flag: string;
            /** ä¸Šä¼ MES时间 */
                toMesTime: string;
            /** å¤‡æ³¨ */
                chkDes: string;
            /** æ£€éªŒäººå±•示名 */
                checker: string;
            /** åˆ¤å®šè§„程 code(非数据库字段) */
                judgeCode: string;
            /** æ˜¯å¦ç”Ÿæˆ 30 æ”¯çƒŸæ˜Žç»†ï¼ˆéžæ•°æ®åº“字段) */
                generateDetails: string;
            /** æ£€æµ‹ç±»åž‹ï¼ˆéžæ•°æ®åº“字段) */
                checkType: string;
        }>;
        /** matcheck search params */
        type MatcheckSearchParams = CommonType.RecordNullable<
            Pick<
                Api.Qm.Matcheck,
                        | 'batchCode'
                        | 'judgeCode'
                        | 'matCode'
                        | 'checkName'
                        | 'checkTime'
                        | 'reviewName'
                        | 'reviewTime'
            > &
            Api.Common.CommonSearchParams & {
                /** æ£€éªŒé¡¹ä¸‹æ‹‰ç­‰æŽ¥å£ä½¿ç”¨çš„判定规程 id */
                judgeId?: string;
            }
        >;
        /** matcheck operate params */
        type MatcheckOperateParams = CommonType.RecordNullable<
            Pick<
                Api.Qm.Matcheck,
                        | 'id'
                        | 'pid'
                        | 'batchCode'
                        | 'matCode'
                        | 'instrumentCode'
                        | 'techReq'
                        | 'checkStd'
                        | 'testEnv'
                        | 'itemCode'
                        | 'itemName'
                        | 'eqp'
                        | 'subBatchCode'
                        | 'sampleNumber'
                        | 'sampleType'
                        | 'checkName'
                        | 'checkTime'
                        | 'reviewName'
                        | 'reviewTime'
                        | 'maxval'
                        | 'minval'
                        | 'avgval'
                        | 'sdval'
                        | 'cvval'
                        | 'cpkval'
                        | 'badval'
                        | 'judge'
                        | 'singlejudge'
                        | 'verName'
                        | 'verCode'
                        | 'archDate'
                        | 'del'
                        | 'flag'
                        | 'toMesTime'
                        | 'chkDes'
                        | 'checker'
                        | 'judgeCode'
                        | 'generateDetails'
                        | 'checkType'
            >
        >;
        /** matcheck list */
        type MatcheckList = Api.Common.PaginatingQueryRecord<Matcheck>;
        /** custom matcheck item */
        type MatcheckCustomItem = {
            itemCode: string;
            itemName: string;
        };
    }
}
ruoyi-plus-soybean/src/typings/elegant-router.d.ts
@@ -40,6 +40,8 @@
    "login": "/login/:module(pwd-login|code-login|register|reset-pwd|bind-wechat)?";
    "md": "/md";
    "md_instrument": "/md/instrument";
    "md_mat": "/md/mat";
    "md_mat-type": "/md/mat-type";
    "md_shift": "/md/shift";
    "md_weighing-box": "/md/weighing-box";
    "monitor": "/monitor";
@@ -51,6 +53,7 @@
    "qm_batch": "/qm/batch";
    "qm_judge": "/qm/judge";
    "qm_judge-details": "/qm/judge-details";
    "qm_matcheck": "/qm/matcheck";
    "qm_std": "/qm/std";
    "report": "/report";
    "report_demo": "/report/demo";
@@ -161,6 +164,8 @@
    | "demo_tree"
    | "home"
    | "md_instrument"
    | "md_mat-type"
    | "md_mat"
    | "md_shift"
    | "md_weighing-box"
    | "monitor_cache"
@@ -170,6 +175,7 @@
    | "qm_batch"
    | "qm_judge-details"
    | "qm_judge"
    | "qm_matcheck"
    | "qm_std"
    | "report_demo"
    | "report_silk-storage-output"
ruoyi-plus-soybean/src/views/md/mat-type/index.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,241 @@
<script setup lang="tsx">
import { ref } from 'vue';
import { NDivider } from 'naive-ui';
import { fetchBatchDeleteMatType, fetchGetMatTypeList } from '@/service/api/md/mat-type';
import { useAppStore } from '@/store/modules/app';
import { useAuth } from '@/hooks/business/auth';
import { useDownload } from '@/hooks/business/download';
import { defaultTransform, useNaivePaginatedTable, useTableOperate } from '@/hooks/common/table';
import { $t } from '@/locales';
import ButtonIcon from '@/components/custom/button-icon.vue';
import MatTypeOperateDrawer from './modules/mat-type-operate-drawer.vue';
import MatTypeSearch from './modules/mat-type-search.vue';
defineOptions({
  name: 'MatTypeList'
});
const appStore = useAppStore();
const { download } = useDownload();
const { hasAuth } = useAuth();
const searchParams = ref<Api.Md.MatTypeSearchParams>({
  pageNum: 1,
  pageSize: 10,
  code: null,
  name: null,
  enable: 1,
  del: '0',
  mesCode: null,
  params: {}
});
const { columns, columnChecks, data, getData, getDataByPage, loading, mobilePagination, scrollX } =
  useNaivePaginatedTable({
  api: () => fetchGetMatTypeList(searchParams.value),
  transform: response => defaultTransform(response),
  onPaginationParamsChange: params => {
    searchParams.value.pageNum = params.page;
    searchParams.value.pageSize = params.pageSize;
  },
  columns: () => [
    {
      type: 'selection',
      align: 'center',
      width: 48
    },
    {
      key: 'index',
      title: $t('common.index'),
      align: 'center',
      width: 64,
      render: (_, index) => index + 1
    },
    {
      key: 'code',
      title: '编码',
      align: 'center',
      minWidth: 120
    },
    {
      key: 'name',
      title: '名称',
      align: 'center',
      minWidth: 120
    },
    {
      key: 'des',
      title: '描述',
      align: 'center',
      minWidth: 120
    },
    {
      key: 'enable',
      title: '启用',
      align: 'center',
      minWidth: 120,
      render: row => (row.enable == 1 ? '是' : '否')
    },
    {
      key: 'del',
      title: '删除',
      align: 'center',
      minWidth: 120,
      render: row => (row.del == 1 ? '是' : '否')
    },
    {
      key: 'mesCode',
      title: 'mes编码',
      align: 'center',
      minWidth: 120
    },
    {
      key: 'createUserName',
      title: '创建人',
      align: 'center',
      minWidth: 120
    },
    {
      key: 'createUserTime',
      title: '创建时间',
      align: 'center',
      minWidth: 120
    },
    {
      key: 'updateUserName',
      title: '更新人',
      align: 'center',
      minWidth: 120
    },
    {
      key: 'updateUserTime',
      title: '更新时间',
      align: 'center',
      minWidth: 120
    },
    {
      key: 'operate',
      title: $t('common.operate'),
      align: 'center',
      fixed: 'right',
      width: 130,
      render: row => {
        const divider = () => {
          if (!hasAuth('md:matType:edit') || !hasAuth('md:matType:remove')) {
            return null;
          }
          return <NDivider vertical />;
        };
        const editBtn = () => {
          if (!hasAuth('md:matType:edit')) {
            return null;
          }
          return (
            <ButtonIcon
              text
              type="primary"
              icon="material-symbols:drive-file-rename-outline-outline"
              tooltipContent={$t('common.edit')}
              onClick={() => edit(row.id)}
            />
          );
        };
        const deleteBtn = () => {
          if (!hasAuth('md:matType:remove')) {
            return null;
          }
          return (
            <ButtonIcon
              text
              type="error"
              icon="material-symbols:delete-outline"
              tooltipContent={$t('common.delete')}
              popconfirmContent={$t('common.confirmDelete')}
              onPositiveClick={() => handleDelete(row.id)}
            />
          );
        };
        return (
          <div class="flex-center gap-8px">
            {editBtn()}
            {divider()}
            {deleteBtn()}
          </div>
        );
      }
    }
  ]
});
const { drawerVisible, operateType, editingData, handleAdd, handleEdit, checkedRowKeys, onBatchDeleted, onDeleted } =
  useTableOperate(data, 'id', getData);
async function handleBatchDelete() {
  // request
  const { error } = await fetchBatchDeleteMatType(checkedRowKeys.value);
  if (error) return;
  onBatchDeleted();
}
async function handleDelete(id: CommonType.IdType) {
  // request
  const { error } = await fetchBatchDeleteMatType([id]);
  if (error) return;
  onDeleted();
}
function edit(id: CommonType.IdType) {
  handleEdit(id);
}
function handleExport() {
  download('/md/matType/export', searchParams.value, `物料类型_${new Date().getTime()}.xlsx`);
}
</script>
<template>
  <div class="min-h-500px flex-col-stretch gap-16px overflow-hidden lt-sm:overflow-auto">
    <MatTypeSearch v-model:model="searchParams" @search="getDataByPage" />
    <NCard title="物料类型列表" :bordered="false" size="small" class="card-wrapper sm:flex-1-hidden">
      <template #header-extra>
        <TableHeaderOperation
          v-model:columns="columnChecks"
          :disabled-delete="checkedRowKeys.length === 0"
          :loading="loading"
          :show-add="hasAuth('md:matType:add')"
          :show-delete="hasAuth('md:matType:remove')"
          :show-export="hasAuth('md:matType:export')"
          @add="handleAdd"
          @delete="handleBatchDelete"
          @export="handleExport"
          @refresh="getData"
        />
      </template>
      <NDataTable
        v-model:checked-row-keys="checkedRowKeys"
        :columns="columns"
        :data="data"
        size="small"
        :flex-height="!appStore.isMobile"
        :scroll-x="scrollX"
        :loading="loading"
        remote
        :row-key="row => row.id"
        :pagination="mobilePagination"
        class="sm:h-full"
      />
      <MatTypeOperateDrawer
        v-model:visible="drawerVisible"
        :operate-type="operateType"
        :row-data="editingData"
        @submitted="getDataByPage"
      />
    </NCard>
  </div>
</template>
<style scoped></style>
ruoyi-plus-soybean/src/views/md/mat-type/modules/mat-type-operate-drawer.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,171 @@
<script setup lang="ts">
import { computed, ref, watch } from 'vue';
import { jsonClone } from '@sa/utils';
import { fetchCreateMatType, fetchUpdateMatType } from '@/service/api/md/mat-type';
import { useFormRules, useNaiveForm } from '@/hooks/common/form';
import { $t } from '@/locales';
defineOptions({
  name: 'MatTypeOperateDrawer'
});
interface Props {
  /** the type of operation */
  operateType: NaiveUI.TableOperateType;
  /** the edit row data */
  rowData?: Api.Md.MatType | null;
}
const props = defineProps<Props>();
interface Emits {
  (e: 'submitted'): void;
}
const emit = defineEmits<Emits>();
const visible = defineModel<boolean>('visible', {
  default: false
});
const { formRef, validate, restoreValidation } = useNaiveForm();
const { createRequiredRule } = useFormRules();
const title = computed(() => {
  const titles: Record<NaiveUI.TableOperateType, string> = {
    add: '新增物料类型',
    edit: '编辑物料类型'
  };
  return titles[props.operateType];
});
type Model = Api.Md.MatTypeOperateParams;
const model = ref<Model>(createDefaultModel());
function createDefaultModel(): Model {
  return {
      id: '',
      gid: '',
      code: '',
      name: '',
      des: '',
      enable: null,
      del: '',
      mesCode: '',
      createUserName: '',
      createUserTime: null,
      updateUserName: '',
      updateUserTime: null
  };
}
type RuleKey = Extract<
  keyof Model,
  | 'id'
>;
const rules: Record<RuleKey, App.Global.FormRule> = {
  id: createRequiredRule('id不能为空'),
};
function handleUpdateModelWhenEdit() {
  model.value = createDefaultModel();
  if (props.operateType === 'edit' && props.rowData) {
    Object.assign(model.value, jsonClone(props.rowData));
  }
}
function closeDrawer() {
  visible.value = false;
}
async function handleSubmit() {
  await validate();
  const { id, gid, code, name, des, enable, del, mesCode, createUserName, createUserTime, updateUserName, updateUserTime } = model.value;
  // request
  if (props.operateType === 'add') {
    const { error } = await fetchCreateMatType({ gid, code, name, des, enable, del, mesCode, createUserName, createUserTime, updateUserName, updateUserTime });
    if (error) return;
  }
  if (props.operateType === 'edit') {
    const { error } = await fetchUpdateMatType({ id, gid, code, name, des, enable, del, mesCode, createUserName, createUserTime, updateUserName, updateUserTime });
    if (error) return;
  }
  window.$message?.success($t('common.updateSuccess'));
  closeDrawer();
  emit('submitted');
}
watch(visible, () => {
  if (visible.value) {
    handleUpdateModelWhenEdit();
    restoreValidation();
  }
});
</script>
<template>
  <NDrawer v-model:show="visible" :title="title" display-directive="show" :width="800" class="max-w-90%">
    <NDrawerContent :title="title" :native-scrollbar="false" closable>
      <NForm ref="formRef" :model="model" :rules="rules">
        <NFormItem label="物料组外键" path="gid">
          <NInput v-model:value="model.gid" placeholder="请输入物料组外键" />
        </NFormItem>
        <NFormItem label="编码" path="code">
          <NInput v-model:value="model.code" placeholder="请输入编码" />
        </NFormItem>
        <NFormItem label="名称" path="name">
          <NInput v-model:value="model.name" placeholder="请输入名称" />
        </NFormItem>
        <NFormItem label="描述" path="des">
          <NInput v-model:value="model.des" placeholder="请输入描述" />
        </NFormItem>
        <NFormItem label="启用" path="enable">
          <NInput v-model:value="model.enable" placeholder="请输入启用" />
        </NFormItem>
        <NFormItem label="删除" path="del">
          <NInput v-model:value="model.del" placeholder="请输入删除" />
        </NFormItem>
        <NFormItem label="mes编码" path="mesCode">
          <NInput v-model:value="model.mesCode" placeholder="请输入mes编码" />
        </NFormItem>
        <NFormItem label="创建人" path="createUserName">
          <NInput v-model:value="model.createUserName" placeholder="请输入创建人" />
        </NFormItem>
        <NFormItem label="创建时间" path="createUserTime">
          <NDatePicker
            v-model:formatted-value="model.createUserTime"
            type="datetime"
            value-format="yyyy-MM-dd HH:mm:ss"
            clearable
          />
        </NFormItem>
        <NFormItem label="更新人" path="updateUserName">
          <NInput v-model:value="model.updateUserName" placeholder="请输入更新人" />
        </NFormItem>
        <NFormItem label="更新时间" path="updateUserTime">
          <NDatePicker
            v-model:formatted-value="model.updateUserTime"
            type="datetime"
            value-format="yyyy-MM-dd HH:mm:ss"
            clearable
          />
        </NFormItem>
      </NForm>
      <template #footer>
        <NSpace :size="16">
          <NButton @click="closeDrawer">{{ $t('common.cancel') }}</NButton>
          <NButton type="primary" @click="handleSubmit">{{ $t('common.confirm') }}</NButton>
        </NSpace>
      </template>
    </NDrawerContent>
  </NDrawer>
</template>
<style scoped></style>
ruoyi-plus-soybean/src/views/md/mat-type/modules/mat-type-search.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,99 @@
<script setup lang="ts">
import { toRaw } from 'vue';
import { jsonClone } from '@sa/utils';
import { useNaiveForm } from '@/hooks/common/form';
import { $t } from '@/locales';
defineOptions({
  name: 'MatTypeSearch'
});
interface Emits {
  (e: 'search'): void;
}
const emit = defineEmits<Emits>();
const { formRef, validate, restoreValidation } = useNaiveForm();
const model = defineModel<Api.Md.MatTypeSearchParams>('model', { required: true });
const defaultModel = jsonClone(toRaw(model.value));
function resetModel() {
  Object.assign(model.value, defaultModel);
}
async function reset() {
  await restoreValidation();
  resetModel();
  emit('search');
}
async function search() {
  await validate();
  emit('search');
}
</script>
<template>
  <NCard :bordered="false" size="small" class="card-wrapper">
    <NCollapse>
      <NCollapseItem :title="$t('common.search')" name="md-mat-type-search">
        <NForm ref="formRef" :model="model" label-placement="left" :label-width="80">
          <NGrid responsive="screen" item-responsive>
            <NFormItemGi span="24 s:12 m:6" label="编码" label-width="auto" path="code" class="pr-24px">
              <NInput v-model:value="model.code" placeholder="请输入编码" />
            </NFormItemGi>
            <NFormItemGi span="24 s:12 m:6" label="名称" label-width="auto" path="name" class="pr-24px">
              <NInput v-model:value="model.name" placeholder="请输入名称" />
            </NFormItemGi>
            <NFormItemGi span="24 s:12 m:6" label="启用" label-width="auto" path="enable" class="pr-24px">
              <NSelect
                v-model:value="model.enable"
                placeholder="请选择启用"
                :options="[
                  { label: '是', value: 1 },
                  { label: '否', value: 0 }
                ]"
                clearable
              />
            </NFormItemGi>
            <NFormItemGi span="24 s:12 m:6" label="删除" label-width="auto" path="del" class="pr-24px">
              <NSelect
                v-model:value="model.del"
                placeholder="请选择删除"
                :options="[
                  { label: '是', value: '1' },
                  { label: '否', value: '0' }
                ]"
                clearable
              />
            </NFormItemGi>
            <NFormItemGi span="24 s:12 m:6" label="mes编码" label-width="auto" path="mesCode" class="pr-24px">
              <NInput v-model:value="model.mesCode" placeholder="请输入mes编码" />
            </NFormItemGi>
            <NFormItemGi :show-feedback="false" span="24" class="pr-24px">
              <NSpace class="w-full" justify="end">
                <NButton @click="reset">
                  <template #icon>
                    <icon-ic-round-refresh class="text-icon" />
                  </template>
                  {{ $t('common.reset') }}
                </NButton>
                <NButton type="primary" ghost @click="search">
                  <template #icon>
                    <icon-ic-round-search class="text-icon" />
                  </template>
                  {{ $t('common.search') }}
                </NButton>
              </NSpace>
            </NFormItemGi>
          </NGrid>
        </NForm>
      </NCollapseItem>
    </NCollapse>
  </NCard>
</template>
<style scoped></style>
ruoyi-plus-soybean/src/views/md/mat/index.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,283 @@
<script setup lang="tsx">
import { onMounted, ref } from 'vue';
import { NDivider } from 'naive-ui';
import { fetchBatchDeleteMat, fetchGetMatList } from '@/service/api/md/mat';
import { fetchGetMatTypeList } from '@/service/api/md/mat-type';
import { useAppStore } from '@/store/modules/app';
import { useAuth } from '@/hooks/business/auth';
import { useDownload } from '@/hooks/business/download';
import { defaultTransform, useNaivePaginatedTable, useTableOperate } from '@/hooks/common/table';
import { $t } from '@/locales';
import ButtonIcon from '@/components/custom/button-icon.vue';
import MatOperateDrawer from './modules/mat-operate-drawer.vue';
import MatSearch from './modules/mat-search.vue';
defineOptions({
  name: 'MatList'
});
const appStore = useAppStore();
const { download } = useDownload();
const { hasAuth } = useAuth();
const matTypeOptions = ref<CommonType.Option[]>([]);
async function getMatTypeOptions() {
  const { data: typeData } = await fetchGetMatTypeList();
  if (typeData) {
    matTypeOptions.value = typeData.rows.map(item => ({
      label: item.name,
      value: item.code
    }));
  }
}
onMounted(() => {
  getMatTypeOptions();
});
const searchParams = ref<Api.Md.MatSearchParams>({
  pageNum: 1,
  pageSize: 10,
  code: null,
  name: null,
  simpleName: null,
  enable: 1,
  del: '0',
  tid: null,
  params: {}
});
const { columns, columnChecks, data, getData, getDataByPage, loading, mobilePagination, scrollX } =
  useNaivePaginatedTable({
  api: () => fetchGetMatList(searchParams.value),
  transform: response => defaultTransform(response),
  onPaginationParamsChange: params => {
    searchParams.value.pageNum = params.page;
    searchParams.value.pageSize = params.pageSize;
  },
  columns: () => [
    {
      type: 'selection',
      align: 'center',
      width: 48
    },
    {
      key: 'index',
      title: $t('common.index'),
      align: 'center',
      width: 64,
      render: (_, index) => index + 1
    },
    {
      key: 'code',
      title: '物料代码',
      align: 'center',
      minWidth: 120
    },
    {
      key: 'name',
      title: '全称',
      align: 'center',
      minWidth: 120
    },
    {
      key: 'simpleName',
      title: '简称',
      align: 'center',
      minWidth: 120
    },
    {
      key: 'des',
      title: '描述',
      align: 'center',
      minWidth: 120
    },
    {
      key: 'uid',
      title: '单位',
      align: 'center',
      minWidth: 120
    },
    {
      key: 'lastUpdateTime',
      title: '最后更新时间',
      align: 'center',
      minWidth: 120
    },
    {
      key: 'enable',
      title: '启用',
      align: 'center',
      minWidth: 120,
      render: row => (row.enable === 1 ? '是' : '否')
    },
    {
      key: 'del',
      title: '删除',
      align: 'center',
      minWidth: 120,
      render: row => (row.del === '1' ? '是' : '否')
    },
    {
      key: 'tid',
      title: '物料类型',
      align: 'center',
      minWidth: 120,
      render: row => {
        if (row.tid) {
          return matTypeOptions.value.find(item => String(item.value) === String(row.tid))?.label || row.tid;
        }
        return '';
      }
    },
    {
      key: 'createUserName',
      title: '创建人',
      align: 'center',
      minWidth: 120
    },
    {
      key: 'createUserTime',
      title: '创建时间',
      align: 'center',
      minWidth: 120
    },
    {
      key: 'updateUserName',
      title: '更新人',
      align: 'center',
      minWidth: 120
    },
    {
      key: 'updateUserTime',
      title: '更新时间',
      align: 'center',
      minWidth: 120
    },
    {
      key: 'operate',
      title: $t('common.operate'),
      align: 'center',
      fixed: 'right',
      width: 130,
      render: row => {
        const divider = () => {
          if (!hasAuth('md:mat:edit') || !hasAuth('md:mat:remove')) {
            return null;
          }
          return <NDivider vertical />;
        };
        const editBtn = () => {
          if (!hasAuth('md:mat:edit')) {
            return null;
          }
          return (
            <ButtonIcon
              text
              type="primary"
              icon="material-symbols:drive-file-rename-outline-outline"
              tooltipContent={$t('common.edit')}
              onClick={() => edit(row.id)}
            />
          );
        };
        const deleteBtn = () => {
          if (!hasAuth('md:mat:remove')) {
            return null;
          }
          return (
            <ButtonIcon
              text
              type="error"
              icon="material-symbols:delete-outline"
              tooltipContent={$t('common.delete')}
              popconfirmContent={$t('common.confirmDelete')}
              onPositiveClick={() => handleDelete(row.id)}
            />
          );
        };
        return (
          <div class="flex-center gap-8px">
            {editBtn()}
            {divider()}
            {deleteBtn()}
          </div>
        );
      }
    }
  ]
});
const { drawerVisible, operateType, editingData, handleAdd, handleEdit, checkedRowKeys, onBatchDeleted, onDeleted } =
  useTableOperate(data, 'id', getData);
async function handleBatchDelete() {
  // request
  const { error } = await fetchBatchDeleteMat(checkedRowKeys.value);
  if (error) return;
  onBatchDeleted();
}
async function handleDelete(id: CommonType.IdType) {
  // request
  const { error } = await fetchBatchDeleteMat([id]);
  if (error) return;
  onDeleted();
}
function edit(id: CommonType.IdType) {
  handleEdit(id);
}
function handleExport() {
  download('/md/mat/export', searchParams.value, `物料_${new Date().getTime()}.xlsx`);
}
</script>
<template>
  <div class="min-h-500px flex-col-stretch gap-16px overflow-hidden lt-sm:overflow-auto">
    <MatSearch v-model:model="searchParams" @search="getDataByPage" />
    <NCard title="物料列表" :bordered="false" size="small" class="card-wrapper sm:flex-1-hidden">
      <template #header-extra>
        <TableHeaderOperation
          v-model:columns="columnChecks"
          :disabled-delete="checkedRowKeys.length === 0"
          :loading="loading"
          :show-add="hasAuth('md:mat:add')"
          :show-delete="hasAuth('md:mat:remove')"
          :show-export="hasAuth('md:mat:export')"
          @add="handleAdd"
          @delete="handleBatchDelete"
          @export="handleExport"
          @refresh="getData"
        />
      </template>
      <NDataTable
        v-model:checked-row-keys="checkedRowKeys"
        :columns="columns"
        :data="data"
        size="small"
        :flex-height="!appStore.isMobile"
        :scroll-x="scrollX"
        :loading="loading"
        remote
        :row-key="row => row.id"
        :pagination="mobilePagination"
        class="sm:h-full"
      />
      <MatOperateDrawer
        v-model:visible="drawerVisible"
        :operate-type="operateType"
        :row-data="editingData"
        @submitted="getDataByPage"
      />
    </NCard>
  </div>
</template>
<style scoped></style>
ruoyi-plus-soybean/src/views/md/mat/modules/mat-operate-drawer.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,184 @@
<script setup lang="ts">
import { computed, ref, watch } from 'vue';
import { jsonClone } from '@sa/utils';
import { fetchCreateMat, fetchUpdateMat } from '@/service/api/md/mat';
import { useFormRules, useNaiveForm } from '@/hooks/common/form';
import { $t } from '@/locales';
defineOptions({
  name: 'MatOperateDrawer'
});
interface Props {
  /** the type of operation */
  operateType: NaiveUI.TableOperateType;
  /** the edit row data */
  rowData?: Api.Md.Mat | null;
}
const props = defineProps<Props>();
interface Emits {
  (e: 'submitted'): void;
}
const emit = defineEmits<Emits>();
const visible = defineModel<boolean>('visible', {
  default: false
});
const { formRef, validate, restoreValidation } = useNaiveForm();
const { createRequiredRule } = useFormRules();
const title = computed(() => {
  const titles: Record<NaiveUI.TableOperateType, string> = {
    add: '新增物料',
    edit: '编辑物料'
  };
  return titles[props.operateType];
});
type Model = Api.Md.MatOperateParams;
const model = ref<Model>(createDefaultModel());
function createDefaultModel(): Model {
  return {
      id: '',
      code: '',
      name: '',
      simpleName: '',
      des: '',
      uid: '',
      lastUpdateTime: null,
      enable: null,
      del: '',
      tid: '',
      createUserName: '',
      createUserTime: null,
      updateUserName: '',
      updateUserTime: null
  };
}
type RuleKey = Extract<
  keyof Model,
  | 'id'
>;
const rules: Record<RuleKey, App.Global.FormRule> = {
  id: createRequiredRule('id不能为空'),
};
function handleUpdateModelWhenEdit() {
  model.value = createDefaultModel();
  if (props.operateType === 'edit' && props.rowData) {
    Object.assign(model.value, jsonClone(props.rowData));
  }
}
function closeDrawer() {
  visible.value = false;
}
async function handleSubmit() {
  await validate();
  const { id, code, name, simpleName, des, uid, lastUpdateTime, enable, del, tid, createUserName, createUserTime, updateUserName, updateUserTime } = model.value;
  // request
  if (props.operateType === 'add') {
    const { error } = await fetchCreateMat({ code, name, simpleName, des, uid, lastUpdateTime, enable, del, tid, createUserName, createUserTime, updateUserName, updateUserTime });
    if (error) return;
  }
  if (props.operateType === 'edit') {
    const { error } = await fetchUpdateMat({ id, code, name, simpleName, des, uid, lastUpdateTime, enable, del, tid, createUserName, createUserTime, updateUserName, updateUserTime });
    if (error) return;
  }
  window.$message?.success($t('common.updateSuccess'));
  closeDrawer();
  emit('submitted');
}
watch(visible, () => {
  if (visible.value) {
    handleUpdateModelWhenEdit();
    restoreValidation();
  }
});
</script>
<template>
  <NDrawer v-model:show="visible" :title="title" display-directive="show" :width="800" class="max-w-90%">
    <NDrawerContent :title="title" :native-scrollbar="false" closable>
      <NForm ref="formRef" :model="model" :rules="rules">
        <NFormItem label="物料代码" path="code">
          <NInput v-model:value="model.code" placeholder="请输入物料代码" />
        </NFormItem>
        <NFormItem label="全称" path="name">
          <NInput v-model:value="model.name" placeholder="请输入全称" />
        </NFormItem>
        <NFormItem label="简称" path="simpleName">
          <NInput v-model:value="model.simpleName" placeholder="请输入简称" />
        </NFormItem>
        <NFormItem label="描述" path="des">
          <NInput v-model:value="model.des" placeholder="请输入描述" />
        </NFormItem>
        <NFormItem label="单位" path="uid">
          <NInput v-model:value="model.uid" placeholder="请输入单位" />
        </NFormItem>
        <NFormItem label="最后更新时间" path="lastUpdateTime">
          <NDatePicker
            v-model:formatted-value="model.lastUpdateTime"
            type="datetime"
            value-format="yyyy-MM-dd HH:mm:ss"
            clearable
          />
        </NFormItem>
        <NFormItem label="启用" path="enable">
          <NInput v-model:value="model.enable" placeholder="请输入启用" />
        </NFormItem>
        <NFormItem label="删除" path="del">
          <NInput v-model:value="model.del" placeholder="请输入删除" />
        </NFormItem>
        <NFormItem label="物料类型" path="tid">
          <NInput v-model:value="model.tid" placeholder="请输入物料类型" />
        </NFormItem>
        <NFormItem label="创建人" path="createUserName">
          <NInput v-model:value="model.createUserName" placeholder="请输入创建人" />
        </NFormItem>
        <NFormItem label="创建时间" path="createUserTime">
          <NDatePicker
            v-model:formatted-value="model.createUserTime"
            type="datetime"
            value-format="yyyy-MM-dd HH:mm:ss"
            clearable
          />
        </NFormItem>
        <NFormItem label="更新人" path="updateUserName">
          <NInput v-model:value="model.updateUserName" placeholder="请输入更新人" />
        </NFormItem>
        <NFormItem label="更新时间" path="updateUserTime">
          <NDatePicker
            v-model:formatted-value="model.updateUserTime"
            type="datetime"
            value-format="yyyy-MM-dd HH:mm:ss"
            clearable
          />
        </NFormItem>
      </NForm>
      <template #footer>
        <NSpace :size="16">
          <NButton @click="closeDrawer">{{ $t('common.cancel') }}</NButton>
          <NButton type="primary" @click="handleSubmit">{{ $t('common.confirm') }}</NButton>
        </NSpace>
      </template>
    </NDrawerContent>
  </NDrawer>
</template>
<style scoped></style>
ruoyi-plus-soybean/src/views/md/mat/modules/mat-search.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,119 @@
<script setup lang="ts">
import { onMounted, ref, toRaw } from 'vue';
import { jsonClone } from '@sa/utils';
import { fetchGetMatTypeList } from '@/service/api/md/mat-type';
import { useNaiveForm } from '@/hooks/common/form';
import { $t } from '@/locales';
defineOptions({
  name: 'MatSearch'
});
interface Emits {
  (e: 'search'): void;
}
const emit = defineEmits<Emits>();
const { formRef, validate, restoreValidation } = useNaiveForm();
const model = defineModel<Api.Md.MatSearchParams>('model', { required: true });
const defaultModel = jsonClone(toRaw(model.value));
const matTypeOptions = ref<CommonType.Option[]>([]);
async function getMatTypeOptions() {
  const { data } = await fetchGetMatTypeList();
  if (data) {
    matTypeOptions.value = data.rows.map(item => ({
      label: item.name,
      value: item.code
    }));
  }
}
onMounted(() => {
  getMatTypeOptions();
});
function resetModel() {
  Object.assign(model.value, defaultModel);
}
async function reset() {
  await restoreValidation();
  resetModel();
  emit('search');
}
async function search() {
  await validate();
  emit('search');
}
</script>
<template>
  <NCard :bordered="false" size="small" class="card-wrapper">
    <NCollapse>
      <NCollapseItem :title="$t('common.search')" name="md-mat-search">
        <NForm ref="formRef" :model="model" label-placement="left" :label-width="80">
          <NGrid responsive="screen" item-responsive>
            <NFormItemGi span="24 s:12 m:6" label="物料代码" label-width="auto" path="code" class="pr-24px">
              <NInput v-model:value="model.code" placeholder="请输入物料代码" />
            </NFormItemGi>
            <NFormItemGi span="24 s:12 m:6" label="全称" label-width="auto" path="name" class="pr-24px">
              <NInput v-model:value="model.name" placeholder="请输入全称" />
            </NFormItemGi>
            <NFormItemGi span="24 s:12 m:6" label="简称" label-width="auto" path="simpleName" class="pr-24px">
              <NInput v-model:value="model.simpleName" placeholder="请输入简称" />
            </NFormItemGi>
            <NFormItemGi span="24 s:12 m:6" label="启用" label-width="auto" path="enable" class="pr-24px">
              <NSelect
                v-model:value="model.enable"
                placeholder="请选择启用"
                :options="[
                  { label: '是', value: 1 },
                  { label: '否', value: 0 }
                ]"
                clearable
              />
            </NFormItemGi>
            <NFormItemGi span="24 s:12 m:6" label="删除" label-width="auto" path="del" class="pr-24px">
              <NSelect
                v-model:value="model.del"
                placeholder="请选择删除"
                :options="[
                  { label: '是', value: '1' },
                  { label: '否', value: '0' }
                ]"
                clearable
              />
            </NFormItemGi>
            <NFormItemGi span="24 s:12 m:6" label="物料类型" label-width="auto" path="tid" class="pr-24px">
              <NSelect v-model:value="model.tid" placeholder="请选择物料类型" :options="matTypeOptions" clearable />
            </NFormItemGi>
            <NFormItemGi :show-feedback="false" span="24" class="pr-24px">
              <NSpace class="w-full" justify="end">
                <NButton @click="reset">
                  <template #icon>
                    <icon-ic-round-refresh class="text-icon" />
                  </template>
                  {{ $t('common.reset') }}
                </NButton>
                <NButton type="primary" ghost @click="search">
                  <template #icon>
                    <icon-ic-round-search class="text-icon" />
                  </template>
                  {{ $t('common.search') }}
                </NButton>
              </NSpace>
            </NFormItemGi>
          </NGrid>
        </NForm>
      </NCollapseItem>
    </NCollapse>
  </NCard>
</template>
<style scoped></style>
ruoyi-plus-soybean/src/views/qm/batch/index.vue
@@ -1,12 +1,14 @@
<script setup lang="tsx">
import { ref } from 'vue';
import { NDivider } from 'naive-ui';
import { fetchBatchDeleteBatch, fetchGetBatchList } from '@/service/api/qm/batch';
import { useAppStore } from '@/store/modules/app';
import { useAuth } from '@/hooks/business/auth';
import { useDownload } from '@/hooks/business/download';
import { defaultTransform, useNaivePaginatedTable, useTableOperate } from '@/hooks/common/table';
import { $t } from '@/locales';
import {nextTick, ref} from 'vue';
import {useRouter} from 'vue-router';
import {NDivider, NDropdown} from 'naive-ui';
import {fetchBatchDeleteBatch, fetchGetBatchList} from '@/service/api/qm/batch';
import {useAppStore} from '@/store/modules/app';
import {useAuth} from '@/hooks/business/auth';
import {useDownload} from '@/hooks/business/download';
import {useSvgIcon} from '@/hooks/common/icon';
import {defaultTransform, useNaivePaginatedTable, useTableOperate} from '@/hooks/common/table';
import {$t} from '@/locales';
import ButtonIcon from '@/components/custom/button-icon.vue';
import BatchOperateDrawer from './modules/batch-operate-drawer.vue';
import BatchSearch from './modules/batch-search.vue';
@@ -16,18 +18,145 @@
});
const appStore = useAppStore();
const { download } = useDownload();
const { hasAuth } = useAuth();
const router = useRouter();
const {download} = useDownload();
const {hasAuth} = useAuth();
const {SvgIconVNode} = useSvgIcon();
// å³é”®èœå•相关
const showDropdown = ref(false);
const x = ref(0);
const y = ref(0);
const currentRow = ref<Api.Qm.Batch | null>(null);
const dropdownOptions = [
  {
    label: '原始数据维护',
    key: 'raw-data-maintenance',
    icon: SvgIconVNode({icon: 'mdi:database-edit-outline', fontSize: 18})
  },
  {
    label: '综合测试台数据维护',
    key: 'test-bench-maintenance',
    icon: SvgIconVNode({icon: 'mdi:monitor-dashboard', fontSize: 18})
  },
  {
    label: '新建复检批次',
    key: 'new-recheck-batch',
    icon: SvgIconVNode({icon: 'mdi:plus-circle-outline', fontSize: 18})
  },
  {
    type: 'divider',
    key: 'd1'
  },
  {
    label: '卷包卷制检验结果报告',
    key: 'report-rolling',
    icon: SvgIconVNode({icon: 'mdi:file-document-outline', fontSize: 18})
  },
  {
    label: '包装标识检验原始记录',
    key: 'record-packaging',
    icon: SvgIconVNode({icon: 'mdi:barcode-scan', fontSize: 18})
  },
  {
    label: '熄火、含水率、含末率原始记录',
    key: 'record-quality',
    icon: SvgIconVNode({icon: 'mdi:water-percent', fontSize: 18})
  },
  {
    label: '端部落丝原始记录',
    key: 'record-silk',
    icon: SvgIconVNode({icon: 'mdi:format-list-bulleted-type', fontSize: 18})
  },
  {
    label: '综合测试台原始记录',
    key: 'record-bench',
    icon: SvgIconVNode({icon: 'mdi:chart-line', fontSize: 18})
  },
  {
    label: '外观检验原始记录',
    key: 'record-appearance',
    icon: SvgIconVNode({icon: 'mdi:eye-outline', fontSize: 18})
  },
  {
    type: 'divider',
    key: 'd2'
  },
  {
    label: $t('common.edit'),
    key: 'edit',
    icon: SvgIconVNode({icon: 'material-symbols:drive-file-rename-outline-outline', fontSize: 18}),
    show: hasAuth('qm:batch:edit')
  },
  {
    label: $t('common.delete'),
    key: 'delete',
    icon: SvgIconVNode({icon: 'material-symbols:delete-outline', fontSize: 18}),
    show: hasAuth('qm:batch:remove')
  }
];
function handleSelect(key: string) {
  showDropdown.value = false;
  if (!currentRow.value) return;
  if (key === 'edit') {
    edit(currentRow.value.id);
  } else if (key === 'delete') {
    window.$dialog?.error({
      title: $t('common.confirmDelete'),
      content: $t('common.confirmDelete'),
      positiveText: $t('common.confirm'),
      negativeText: $t('common.cancel'),
      onPositiveClick: () => {
        handleDelete(currentRow.value!.id);
      }
    });
  } else if (key === 'raw-data-maintenance') {
    // è·³è½¬åˆ° matcheck é¡µé¢
    if (!currentRow.value.judgeCode) {
      window.$message?.warning('该批次没有判定依据');
      return;
    }
    router.push({
      path: '/qm/matcheck',
      query: {
        judgeCode: currentRow.value.judgeCode,
        batchCode: currentRow.value.batchCode,
        matCode: currentRow.value.matCode
      }
    });
  } else {
    // å¤„理其他业务操作
    window.$message?.info(`点击了: ${key}`);
  }
}
function handleRowProps(row: Api.Qm.Batch) {
  return {
    onContextmenu: (e: MouseEvent) => {
      e.preventDefault();
      showDropdown.value = false;
      nextTick().then(() => {
        currentRow.value = row;
        x.value = e.clientX;
        y.value = e.clientY;
        showDropdown.value = true;
      });
    }
  };
}
// ç±»åž‹/反馈MES/类别的 value->label æ˜ å°„(用于表格显示)
const TYP_MAP: Record<string, string> = { A: '制丝', B: '成型', C: '卷包', D: '封箱', E: '糖香料' };
const FLAG_MAP: Record<string, string> = { '0': '未上传mes', '1': '已上传', '3': '从MES下载' };
const CATEGORY_MAP: Record<string, string> = { '0': '成品', '1': '辅材' };
const FLAG_MAP: Record<string, string> = {'0': '未上传mes', '1': '已上传', '3': '从MES下载'};
const CATEGORY_MAP: Record<string, string> = {'0': '成品', '1': '辅材'};
const searchParams = ref<Api.Qm.BatchSearchParams>({
  pageNum: 1,
  pageSize: 10,
  batchCode: null,
  isflag: '1',
  typ: null,
  eqpCode: null,
  matCode: null,
@@ -35,13 +164,17 @@
  flag: null,
  toMesDate: null,
  fromMesDate: null,
  deleted: null,
  category: null,
  enabled: '1',
  deleted: 0,
  category: '0',
  state: null,
  params: {}
  params: {
    beginBatchDate: `${new Date().getFullYear()}-${String(new Date().getMonth() + 1).padStart(2, '0')}-${String(new Date().getDate()).padStart(2, '0')} 00:00:00`,
    endBatchDate: `${new Date().getFullYear()}-${String(new Date().getMonth() + 1).padStart(2, '0')}-${String(new Date().getDate()).padStart(2, '0')} 23:59:59`
  }
});
const { columns, columnChecks, data, getData, getDataByPage, loading, mobilePagination, scrollX } =
const {columns, columnChecks, data, getData, getDataByPage, loading, mobilePagination, scrollX} =
  useNaivePaginatedTable({
    api: () => fetchGetBatchList(searchParams.value),
    transform: response => defaultTransform(response),
@@ -63,45 +196,20 @@
        render: (_, index) => index + 1
      },
      {
        key: 'id',
        title: '编码',
        align: 'center',
        minWidth: 120
      },
      {
        key: 'batchCode',
        title: '批次代码',
        title: '批次号',
        align: 'center',
        minWidth: 120
      },
      {
        key: 'batchName',
        title: '批次名称',
        align: 'center',
        minWidth: 120
      },
      {
        key: 'typ',
        title: '类型',
        align: 'center',
        minWidth: 120,
        render: row => TYP_MAP[row.typ] ?? row.typ
      },
      {
        key: 'eqpCode',
        title: '机台代码',
        align: 'center',
        minWidth: 120
      },
      {
        key: 'matCode',
        key: 'matName',
        title: '牌号',
        align: 'center',
        minWidth: 120
      },
      {
        key: 'judgeCode',
        title: '判定依据代码',
        key: 'judgeName',
        title: '判定依据',
        align: 'center',
        minWidth: 120
      },
@@ -115,13 +223,15 @@
        key: 'isflag',
        title: '使用标志',
        align: 'center',
        minWidth: 120
        minWidth: 120,
        render: row => (row.isflag == '1' ? '是' : '否')
      },
      {
        key: 'enabled',
        title: '启用标志',
        align: 'center',
        minWidth: 120
        minWidth: 120,
        render: row => (row.enabled == '1' ? '是' : '否')
      },
      {
        key: 'totalNum',
@@ -200,7 +310,8 @@
        key: 'deleted',
        title: '删除标志',
        align: 'center',
        minWidth: 120
        minWidth: 120,
        render: row => (row.deleted == 1 ? '是' : '否')
      },
      {
        key: 'batchDes',
@@ -350,6 +461,7 @@
      {
        key: 'operate',
        title: $t('common.operate'),
        fixed: 'right',
        align: 'center',
        width: 130,
        render: row => {
@@ -357,7 +469,7 @@
            if (!hasAuth('qm:batch:edit') || !hasAuth('qm:batch:remove')) {
              return null;
            }
            return <NDivider vertical />;
            return <NDivider vertical/>;
          };
          const editBtn = () => {
@@ -403,19 +515,19 @@
    ]
  });
const { drawerVisible, operateType, editingData, handleAdd, handleEdit, checkedRowKeys, onBatchDeleted, onDeleted } =
const {drawerVisible, operateType, editingData, handleAdd, handleEdit, checkedRowKeys, onBatchDeleted, onDeleted} =
  useTableOperate(data, 'id', getData);
async function handleBatchDelete() {
  // request
  const { error } = await fetchBatchDeleteBatch(checkedRowKeys.value);
  const {error} = await fetchBatchDeleteBatch(checkedRowKeys.value);
  if (error) return;
  onBatchDeleted();
}
async function handleDelete(id: CommonType.IdType) {
  // request
  const { error } = await fetchBatchDeleteBatch([id]);
  const {error} = await fetchBatchDeleteBatch([id]);
  if (error) return;
  onDeleted();
}
@@ -431,7 +543,7 @@
<template>
  <div class="min-h-500px flex-col-stretch gap-16px overflow-hidden lt-sm:overflow-auto">
    <BatchSearch v-model:model="searchParams" @search="getDataByPage" />
    <BatchSearch v-model:model="searchParams" @search="getDataByPage"/>
    <NCard title="检验批次列表" :bordered="false" size="small" class="card-wrapper sm:flex-1-hidden">
      <template #header-extra>
        <TableHeaderOperation
@@ -458,8 +570,19 @@
        remote
        :row-key="row => row.id"
        :pagination="mobilePagination"
        :row-props="handleRowProps"
        class="sm:h-full"
      />
      <NDropdown
        placement="bottom-start"
        trigger="manual"
        :x="x"
        :y="y"
        :options="dropdownOptions"
        :show="showDropdown"
        :on-clickoutside="() => (showDropdown = false)"
        @select="handleSelect"
      />
      <BatchOperateDrawer
        v-model:visible="drawerVisible"
        :operate-type="operateType"
ruoyi-plus-soybean/src/views/qm/batch/modules/batch-operate-drawer.vue
@@ -1,7 +1,9 @@
<script setup lang="ts">
import { computed, ref, watch } from 'vue';
import { computed, onMounted, ref, watch } from 'vue';
import { jsonClone } from '@sa/utils';
import { fetchCreateBatch, fetchUpdateBatch } from '@/service/api/qm/batch';
import { fetchGetMatList } from '@/service/api/md/mat';
import { fetchGetJudgeList } from '@/service/api/qm/judge';
import { useFormRules, useNaiveForm } from '@/hooks/common/form';
import { $t } from '@/locales';
@@ -31,27 +33,41 @@
const { formRef, validate, restoreValidation } = useNaiveForm();
const { createRequiredRule } = useFormRules();
// ç±»åž‹é€‰é¡¹ï¼šA-制丝 B-成型 C-卷包 D-封箱 E-糖香料
const typOptions = [
  { label: '制丝', value: 'A' },
  { label: '成型', value: 'B' },
  { label: '卷包', value: 'C' },
  { label: '封箱', value: 'D' },
  { label: '糖香料', value: 'E' }
const matOptions = ref<CommonType.Option[]>([]);
const judgeOptions = ref<CommonType.Option[]>([]);
const stateOptions = [
  { label: '正常', value: '0' },
  { label: '停用', value: '1' }
];
// åé¦ˆMES选项:0-未上传mes 1-已上传 3-从MES下载
const flagOptions = [
  { label: '未上传mes', value: '0' },
  { label: '已上传', value: '1' },
  { label: '从MES下载', value: '3' }
];
async function getMatOptions() {
  const { data, error } = await fetchGetMatList({ tid: '1' as any, pageSize: 1000 });
  if (!error) {
    matOptions.value = data.rows.map(item => ({
      label: item.name,
      value: item.code
    }));
  }
}
// ç±»åˆ«é€‰é¡¹ï¼š0-成品 1-辅材
const categoryOptions = [
  { label: '成品', value: '0' },
  { label: '辅材', value: '1' }
];
async function getJudgeOptions(matCode?: string | null) {
  if (!matCode) {
    judgeOptions.value = [];
    return;
  }
  const params: Api.Qm.JudgeSearchParams = { category: 0, pageSize: 1000, matCode };
  const { data, error } = await fetchGetJudgeList(params);
  if (!error) {
    judgeOptions.value = data.rows.map(item => ({
      label: item.judgeName,
      value: String(item.id)
    })) as CommonType.Option[];
  }
}
onMounted(() => {
  getMatOptions();
});
const title = computed(() => {
  const titles: Record<NaiveUI.TableOperateType, string> = {
@@ -70,13 +86,13 @@
    id: '',
    batchCode: '',
    batchName: '',
    typ: '',
    typ: '0',
    eqpCode: '',
    matCode: '',
    matCode: null,
    judgeCode: '',
    batchDate: null,
    isflag: '',
    enabled: '',
    isflag: '0',
    enabled: '1',
    totalNum: null,
    results: '',
    approver: '',
@@ -86,19 +102,19 @@
    verName: '',
    verCode: '',
    archDate: '',
    flag: '',
    flag: '0',
    toMesDate: null,
    fromMesDate: null,
    deleted: null,
    deleted: 0,
    batchDes: '',
    category: '',
    category: '0',
    makeno: '',
    shifteqpno: '',
    boxno: '',
    pid: '',
    reviewer: '',
    rvcount: null,
    state: '',
    state: '0',
    reviewTime: null,
    auditTime: null,
    spec: '',
@@ -117,10 +133,11 @@
  };
}
type RuleKey = Extract<keyof Model, 'id'>;
type RuleKey = Extract<keyof Model, 'batchCode' | 'matCode'>;
const rules: Record<RuleKey, App.Global.FormRule> = {
  id: createRequiredRule('编码不能为空')
  batchCode: createRequiredRule('批次号不能为空'),
  matCode: createRequiredRule('牌号不能为空')
};
function handleUpdateModelWhenEdit() {
@@ -303,201 +320,54 @@
  if (visible.value) {
    handleUpdateModelWhenEdit();
    restoreValidation();
    getJudgeOptions(model.value.matCode);
  }
});
watch(
  () => model.value.matCode,
  newVal => {
    if (visible.value) {
      model.value.judgeCode = '';
      getJudgeOptions(newVal);
    }
  }
);
</script>
<template>
  <NDrawer v-model:show="visible" :title="title" display-directive="show" :width="800" class="max-w-90%">
    <NDrawerContent :title="title" :native-scrollbar="false" closable>
      <NForm ref="formRef" :model="model" :rules="rules">
        <NFormItem label="批次代码" path="batchCode">
          <NInput v-model:value="model.batchCode" placeholder="请输入批次代码" />
        </NFormItem>
        <NFormItem label="批次名称" path="batchName">
          <NInput v-model:value="model.batchName" placeholder="请输入批次名称" />
        </NFormItem>
        <NFormItem label="类型" path="typ">
          <NSelect v-model:value="model.typ" :options="typOptions" placeholder="请选择类型" clearable />
        </NFormItem>
        <NFormItem label="机台代码" path="eqpCode">
          <NInput v-model:value="model.eqpCode" placeholder="请输入机台代码" />
      <NForm ref="formRef" :model="model" :rules="rules" label-placement="left" :label-width="100">
        <NFormItem label="批次号" path="batchCode">
          <NInput v-model:value="model.batchCode" placeholder="请输入批次号" />
        </NFormItem>
        <NFormItem label="牌号" path="matCode">
          <NInput v-model:value="model.matCode" placeholder="请输入牌号" />
          <NSelect v-model:value="model.matCode" :options="matOptions" placeholder="请选择牌号" clearable filterable />
        </NFormItem>
        <NFormItem label="判定依据代码" path="judgeCode">
          <NInput v-model:value="model.judgeCode" placeholder="请输入判定依据代码" />
        </NFormItem>
        <NFormItem label="批次生成日期" path="batchDate">
          <NDatePicker
            v-model:formatted-value="model.batchDate"
            type="datetime"
            value-format="yyyy-MM-dd HH:mm:ss"
        <NFormItem label="判定依据" path="judgeCode">
          <NSelect
            v-model:value="model.judgeCode"
            :options="judgeOptions"
            placeholder="请选择判定依据"
            clearable
          />
        </NFormItem>
        <NFormItem label="使用标志" path="isflag">
          <NInput v-model:value="model.isflag" placeholder="请输入使用标志" />
        </NFormItem>
        <NFormItem label="启用标志" path="enabled">
          <NInput v-model:value="model.enabled" placeholder="请输入启用标志" />
        </NFormItem>
        <NFormItem label="到货总量" path="totalNum">
          <NInputNumber v-model:value="model.totalNum" placeholder="请输入到货总量" class="w-full" />
        </NFormItem>
        <NFormItem label="综合判定" path="results">
          <NInput v-model:value="model.results" placeholder="请输入综合判定" />
        </NFormItem>
        <NFormItem label="批准人" path="approver">
          <NInput v-model:value="model.approver" placeholder="请输入批准人" />
        </NFormItem>
        <NFormItem label="审核人" path="auditor">
          <NInput v-model:value="model.auditor" placeholder="请输入审核人" />
        </NFormItem>
        <NFormItem label="创建人" path="creater">
          <NInput v-model:value="model.creater" placeholder="请输入创建人" />
        </NFormItem>
        <NFormItem label="制表日期" path="tabDate">
          <NDatePicker
            v-model:formatted-value="model.tabDate"
            type="datetime"
            value-format="yyyy-MM-dd HH:mm:ss"
            clearable
          />
        </NFormItem>
        <NFormItem label="版本名称" path="verName">
          <NInput v-model:value="model.verName" placeholder="请输入版本名称" />
        </NFormItem>
        <NFormItem label="版本编号" path="verCode">
          <NInput v-model:value="model.verCode" placeholder="请输入版本编号" />
        </NFormItem>
        <NFormItem label="保存期" path="archDate">
          <NInput v-model:value="model.archDate" placeholder="请输入保存期" />
        </NFormItem>
        <NFormItem label="反馈MES" path="flag">
          <NSelect v-model:value="model.flag" :options="flagOptions" placeholder="请选择反馈MES" clearable />
        </NFormItem>
        <NFormItem label="上传MES时间" path="toMesDate">
          <NDatePicker
            v-model:formatted-value="model.toMesDate"
            type="datetime"
            value-format="yyyy-MM-dd HH:mm:ss"
            clearable
          />
        </NFormItem>
        <NFormItem label="从MES时间下载" path="fromMesDate">
          <NDatePicker
            v-model:formatted-value="model.fromMesDate"
            type="datetime"
            value-format="yyyy-MM-dd HH:mm:ss"
            clearable
          />
        </NFormItem>
        <NFormItem label="删除标志" path="deleted">
          <NInputNumber v-model:value="model.deleted" placeholder="请输入删除标志" class="w-full" />
        </NFormItem>
        <NFormItem label="批次描述" path="batchDes">
          <NInput v-model:value="model.batchDes" placeholder="请输入批次描述" />
        </NFormItem>
        <NFormItem label="类别" path="category">
          <NSelect v-model:value="model.category" :options="categoryOptions" placeholder="请选择类别" clearable />
        </NFormItem>
        <NFormItem label="卷制工号" path="makeno">
          <NInput v-model:value="model.makeno" placeholder="请输入卷制工号" />
        </NFormItem>
        <NFormItem label="班次机号" path="shifteqpno">
          <NInput v-model:value="model.shifteqpno" placeholder="请输入班次机号" />
        </NFormItem>
        <NFormItem label="装箱号" path="boxno">
          <NInput v-model:value="model.boxno" placeholder="请输入装箱号" />
        </NFormItem>
        <NFormItem label="父批次号" path="pid">
          <NInput v-model:value="model.pid as any" placeholder="请输入父批次号" />
        </NFormItem>
        <NFormItem label="复核人" path="reviewer">
          <NInput v-model:value="model.reviewer" placeholder="请输入复核人" />
        </NFormItem>
        <NFormItem label="复检次数" path="rvcount">
          <NInputNumber v-model:value="model.rvcount" placeholder="请输入复检次数" class="w-full" />
        </NFormItem>
        <NFormItem label="批次状态" path="state">
          <NInput v-model:value="model.state" placeholder="请输入批次状态" />
        </NFormItem>
        <NFormItem label="复核日期" path="reviewTime">
          <NDatePicker
            v-model:formatted-value="model.reviewTime"
            type="datetime"
            value-format="yyyy-MM-dd HH:mm:ss"
            clearable
          />
        </NFormItem>
        <NFormItem label="审核日期" path="auditTime">
          <NDatePicker
            v-model:formatted-value="model.auditTime"
            type="datetime"
            value-format="yyyy-MM-dd HH:mm:ss"
            clearable
            filterable
          />
        </NFormItem>
        <NFormItem label="规格" path="spec">
          <NInput v-model:value="model.spec" placeholder="请输入规格" />
        </NFormItem>
        <NFormItem label="批准时间" path="approveTime">
          <NDatePicker
            v-model:formatted-value="model.approveTime"
            type="datetime"
            value-format="yyyy-MM-dd HH:mm:ss"
            clearable
          />
        <NFormItem label="装箱号" path="boxno">
          <NInput v-model:value="model.boxno" placeholder="请输入装箱号" />
        </NFormItem>
        <NFormItem label="到货单位" path="unit">
          <NInput v-model:value="model.unit" placeholder="请输入到货单位" />
        <NFormItem label="班次机号" path="shifteqpno">
          <NInput v-model:value="model.shifteqpno" placeholder="请输入班次机号" />
        </NFormItem>
        <NFormItem label="到货日期" path="arrivalTime">
          <NDatePicker
            v-model:formatted-value="model.arrivalTime"
            type="datetime"
            value-format="yyyy-MM-dd HH:mm:ss"
            clearable
          />
        <NFormItem label="描述" path="batchDes">
          <NInput v-model:value="model.batchDes" placeholder="请输入描述" />
        </NFormItem>
        <NFormItem label="存放地点" path="storagePlace">
          <NInput v-model:value="model.storagePlace" placeholder="请输入存放地点" />
        </NFormItem>
        <NFormItem label="检验员" path="checker">
          <NInput v-model:value="model.checker" placeholder="请输入检验员" />
        </NFormItem>
        <NFormItem label="接单日期" path="receiveTime">
          <NDatePicker
            v-model:formatted-value="model.receiveTime"
            type="datetime"
            value-format="yyyy-MM-dd HH:mm:ss"
            clearable
          />
        </NFormItem>
        <NFormItem label="报检日期" path="inspTime">
          <NDatePicker
            v-model:formatted-value="model.inspTime"
            type="datetime"
            value-format="yyyy-MM-dd HH:mm:ss"
            clearable
          />
        </NFormItem>
        <NFormItem label="仓库保管员" path="storer">
          <NInput v-model:value="model.storer" placeholder="请输入仓库保管员" />
        </NFormItem>
        <NFormItem label="是否验证" path="isverify">
          <NInput v-model:value="model.isverify" placeholder="请输入是否验证" />
        </NFormItem>
        <NFormItem label="是否检验" path="ischk">
          <NInput v-model:value="model.ischk" placeholder="请输入是否检验" />
        </NFormItem>
        <NFormItem label="备用1" path="bak1">
          <NInput v-model:value="model.bak1" placeholder="请输入备用1" />
        </NFormItem>
        <NFormItem label="备用2" path="bak2">
          <NInput v-model:value="model.bak2" placeholder="请输入备用2" />
        <NFormItem label="状态" path="state">
          <NSelect v-model:value="model.state" :options="stateOptions" placeholder="请选择状态" clearable />
        </NFormItem>
      </NForm>
      <template #footer>
ruoyi-plus-soybean/src/views/qm/batch/modules/batch-search.vue
@@ -1,6 +1,7 @@
<script setup lang="ts">
import { ref, toRaw } from 'vue';
import { onMounted, ref, toRaw, watch } from 'vue';
import { jsonClone } from '@sa/utils';
import { fetchGetMatList } from '@/service/api/md/mat';
import { useNaiveForm } from '@/hooks/common/form';
import { $t } from '@/locales';
@@ -16,38 +17,53 @@
const { formRef, validate, restoreValidation } = useNaiveForm();
const dateRangeToMesDate = ref<[string, string] | null>(null);
const dateRangeFromMesDate = ref<[string, string] | null>(null);
const model = defineModel<Api.Qm.BatchSearchParams>('model', { required: true });
const dateRangeBatchDate = ref<[string, string] | null>(null);
const matOptions = ref<CommonType.Option[]>([]);
async function getMatOptions() {
  const { data } = await fetchGetMatList({ tid: '1' as any, pageSize: 1000 });
  if (data) {
    matOptions.value = data.rows.map(item => ({
      label: item.name,
      value: item.code
    }));
  }
}
onMounted(() => {
  getMatOptions();
});
// sync model params to dateRangeBatchDate
watch(
  () => model.value.params,
  val => {
    if (val?.beginBatchDate && val?.endBatchDate) {
      dateRangeBatchDate.value = [val.beginBatchDate, val.endBatchDate];
    } else {
      dateRangeBatchDate.value = null;
    }
  },
  { immediate: true, deep: true }
);
const defaultModel = jsonClone(toRaw(model.value));
// ç±»åž‹é€‰é¡¹ï¼šA-制丝 B-成型 C-卷包 D-封箱 E-糖香料
const typOptions = [
  { label: '制丝', value: 'A' },
  { label: '成型', value: 'B' },
  { label: '卷包', value: 'C' },
  { label: '封箱', value: 'D' },
  { label: '糖香料', value: 'E' }
];
// åé¦ˆMES选项:0-未上传mes 1-已上传 3-从MES下载
const flagOptions = [
  { label: '未上传mes', value: '0' },
  { label: '已上传', value: '1' },
  { label: '从MES下载', value: '3' }
];
// ç±»åˆ«é€‰é¡¹ï¼š0-成品 1-辅材
const categoryOptions = [
  { label: '成品', value: '0' },
  { label: '辅材', value: '1' }
];
function onDateRangeBatchDateUpdate(value: [string, string] | null) {
  if (value && value.length === 2) {
    model.value.params!.beginBatchDate = value[0];
    model.value.params!.endBatchDate = value[1];
  } else {
    model.value.params!.beginBatchDate = undefined;
    model.value.params!.endBatchDate = undefined;
  }
}
function resetModel() {
  dateRangeToMesDate.value = null;
  dateRangeFromMesDate.value = null;
  Object.assign(model.value, defaultModel);
  Object.assign(model.value, jsonClone(defaultModel));
}
async function reset() {
@@ -58,14 +74,6 @@
async function search() {
  await validate();
  if (dateRangeToMesDate.value?.length) {
    model.value.params!.beginToMesDate = dateRangeToMesDate.value[0];
    model.value.params!.endToMesDate = dateRangeToMesDate.value[1];
  }
  if (dateRangeFromMesDate.value?.length) {
    model.value.params!.beginFromMesDate = dateRangeFromMesDate.value[0];
    model.value.params!.endFromMesDate = dateRangeFromMesDate.value[1];
  }
  emit('search');
}
</script>
@@ -74,70 +82,68 @@
  <NCard :bordered="false" size="small" class="card-wrapper">
    <NCollapse>
      <NCollapseItem :title="$t('common.search')" name="qm-batch-search">
        <NForm ref="formRef" :model="model" label-placement="left" :label-width="80">
        <NForm ref="formRef" :model="model" label-placement="left" :label-width="110">
          <NGrid responsive="screen" item-responsive>
            <NFormItemGi span="24 s:12 m:8" label="批次代码" label-width="auto" path="batchCode" class="pr-24px">
              <NInput v-model:value="model.batchCode" placeholder="请输入批次代码" />
            </NFormItemGi>
            <NFormItemGi span="24 s:12 m:8" label="类型" label-width="auto" path="typ" class="pr-24px">
              <NSelect v-model:value="model.typ" :options="typOptions" placeholder="请选择类型" clearable />
            </NFormItemGi>
            <NFormItemGi span="24 s:12 m:8" label="机台代码" label-width="auto" path="eqpCode" class="pr-24px">
              <NInput v-model:value="model.eqpCode" placeholder="请输入机台代码" />
            </NFormItemGi>
            <NFormItemGi span="24 s:12 m:8" label="牌号" label-width="auto" path="matCode" class="pr-24px">
              <NInput v-model:value="model.matCode" placeholder="请输入牌号" />
            </NFormItemGi>
            <NFormItemGi span="24 s:12 m:8" label="批次生成日期" label-width="auto" path="batchDate" class="pr-24px">
              <NDatePicker
                v-model:formatted-value="model.batchDate"
                type="datetime"
                value-format="yyyy-MM-dd HH:mm:ss"
                clearable
              />
            </NFormItemGi>
            <NFormItemGi span="24 s:12 m:8" label="反馈MES" label-width="auto" path="flag" class="pr-24px">
              <NSelect v-model:value="model.flag" :options="flagOptions" placeholder="请选择反馈MES" clearable />
            </NFormItemGi>
            <NFormItemGi span="24 s:12 m:8" label="上传MES时间" label-width="auto" path="toMesDate" class="pr-24px">
              <NDatePicker
                v-model:formatted-value="dateRangeToMesDate"
                v-model:formatted-value="dateRangeBatchDate"
                type="datetimerange"
                value-format="yyyy-MM-dd HH:mm:ss"
                clearable
                :default-time="['00:00:00', '23:59:59']"
                @update:formatted-value="onDateRangeBatchDateUpdate"
              />
            </NFormItemGi>
            <NFormItemGi span="24 s:12 m:8" label="从MES时间下载" label-width="auto" path="fromMesDate" class="pr-24px">
              <NDatePicker
                v-model:formatted-value="dateRangeFromMesDate"
                type="datetimerange"
                value-format="yyyy-MM-dd HH:mm:ss"
            <NFormItemGi span="24 s:12 m:8" label="牌号" label-width="auto" path="matCode" class="pr-24px">
              <NSelect v-model:value="model.matCode" placeholder="请选择牌号" :options="matOptions" clearable />
            </NFormItemGi>
            <NFormItemGi span="24 s:12 m:8" label="使用标志" label-width="auto" path="isflag" class="pr-24px">
              <NSelect
                v-model:value="model.isflag"
                placeholder="请选择使用标志"
                :options="[
                  { label: '是', value: '1' },
                  { label: '否', value: '0' }
                ]"
                clearable
              />
            </NFormItemGi>
            <NFormItemGi span="24 s:12 m:8" label="启用标志" label-width="auto" path="enabled" class="pr-24px">
              <NSelect
                v-model:value="model.enabled"
                placeholder="请选择启用标志"
                :options="[
                  { label: '是', value: '1' },
                  { label: '否', value: '0' }
                ]"
                clearable
              />
            </NFormItemGi>
            <NFormItemGi span="24 s:12 m:8" label="删除标志" label-width="auto" path="deleted" class="pr-24px">
              <NInputNumber v-model:value="model.deleted" placeholder="请输入删除标志" class="w-full" />
              <NSelect
                v-model:value="model.deleted"
                placeholder="请选择删除标志"
                :options="[
                  { label: '是', value: 1 },
                  { label: '否', value: 0 }
                ]"
                clearable
              />
            </NFormItemGi>
            <NFormItemGi span="24 s:12 m:8" label="类别" label-width="auto" path="category" class="pr-24px">
              <NSelect v-model:value="model.category" :options="categoryOptions" placeholder="请选择类别" clearable />
            </NFormItemGi>
            <NFormItemGi span="24 s:12 m:8" label="批次状态" label-width="auto" path="state" class="pr-24px">
              <NInput v-model:value="model.state" placeholder="请输入批次状态" />
            </NFormItemGi>
            <NFormItemGi :show-feedback="false" span="24" class="pr-24px">
              <NSpace class="w-full" justify="end">
                <NButton @click="reset">
                  <template #icon>
                    <icon-ic-round-refresh class="text-icon" />
                  </template>
                  {{ $t('common.reset') }}
                </NButton>
            <NFormItemGi :show-feedback="false" span="24 s:12 m:8" class="pr-24px">
              <NSpace class="w-full" justify="start">
                <NButton type="primary" ghost @click="search">
                  <template #icon>
                    <icon-ic-round-search class="text-icon" />
                  </template>
                  {{ $t('common.search') }}
                </NButton>
                <NButton @click="reset">
                  <template #icon>
                    <icon-ic-round-refresh class="text-icon" />
                  </template>
                  {{ $t('common.reset') }}
                </NButton>
              </NSpace>
            </NFormItemGi>
          </NGrid>
ruoyi-plus-soybean/src/views/qm/judge-details/modules/judge-details-operate-drawer.vue
@@ -39,6 +39,13 @@
  return titles[props.operateType];
});
const clsOptions = [
  { label: 'A', value: 'A' },
  { label: 'B', value: 'B' },
  { label: 'C', value: 'C' },
  { label: 'D', value: 'D' }
];
type Model = Api.Qm.JudgeDetailsOperateParams;
const model = ref<Model>(createDefaultModel());
@@ -65,12 +72,14 @@
type RuleKey = Extract<keyof Model, 'id'>;
const rules: Record<RuleKey, App.Global.FormRule> = {};
const rules: Record<RuleKey, App.Global.FormRule | App.Global.FormRule[]> = {
  id: createRequiredRule('请输入编码')
};
function handleUpdateModelWhenEdit() {
  model.value = createDefaultModel();
  if (props.operateType === 'edit' && props.rowData) {
  if (props.rowData) {
    Object.assign(model.value, jsonClone(props.rowData));
  }
}
@@ -160,59 +169,26 @@
  <NDrawer v-model:show="visible" :title="title" display-directive="show" :width="800" class="max-w-90%">
    <NDrawerContent :title="title" :native-scrollbar="false" closable>
      <NForm ref="formRef" :model="model" :rules="rules">
        <NFormItem label="编码" path="id">
          <NInput v-model:value="model.id" placeholder="请输入编码" />
        </NFormItem>
        <NFormItem label="判定主标识" path="judgeId">
          <NInput v-model:value="model.judgeId" placeholder="请输入判定主标识" />
        </NFormItem>
        <NFormItem label="判定项ITEM" path="itemCod">
          <NInput v-model:value="model.itemCod" placeholder="请输入判定项ITEM" />
        </NFormItem>
        <NFormItem label="判定项NAME" path="itemName">
          <NInput v-model:value="model.itemName" placeholder="请输入判定项NAME" />
        <NFormItem label="判定项" path="itemName">
          <NInput v-model:value="(model.itemName as any)" placeholder="请输入判定项" :disabled="operateType === 'edit'" />
        </NFormItem>
        <NFormItem label="标准值" path="value3">
          <NInput v-model:value="model.value3" placeholder="请输入标准值" />
          <NInputNumber v-model:value="(model.value3 as any)" placeholder="请输入标准值" class="w-full" />
        </NFormItem>
        <NFormItem label="判定值1" path="value1">
          <NInput v-model:value="model.value1" placeholder="请输入判定值1" />
        <NFormItem label="下限值" path="value1">
          <NInputNumber v-model:value="(model.value1 as any)" placeholder="请输入下限值" class="w-full" />
        </NFormItem>
        <NFormItem label="判定值2" path="value2">
          <NInput v-model:value="model.value2" placeholder="请输入判定值2" />
        <NFormItem label="上限值" path="value2">
          <NInputNumber v-model:value="(model.value2 as any)" placeholder="请输入上限值" class="w-full" />
        </NFormItem>
        <NFormItem label="缺陷位置" path="location">
          <NInput v-model:value="model.location" placeholder="请输入缺陷位置" />
        <NFormItem label="分值" path="stdscore">
          <NInputNumber v-model:value="(model.stdscore as any)" placeholder="请输入分值" class="w-full" />
        </NFormItem>
        <NFormItem label="判定级别 (A,B,C,D)" path="cls">
          <NInput v-model:value="model.cls" placeholder="请输入判定级别 (A,B,C,D)" />
        </NFormItem>
        <NFormItem label="分值标准 (扣分标准,得分标准),比如不合格一次扣多少分" path="stdscore">
          <NInput
            v-model:value="model.stdscore"
            placeholder="请输入分值标准 (扣分标准,得分标准),比如不合格一次扣多少分"
          />
        </NFormItem>
        <NFormItem label="标记此项是否为合成项目,比如外观,实际上关联了很多子项目" path="ismix">
          <NInput
            v-model:value="model.ismix"
            placeholder="请输入标记此项是否为合成项目,比如外观,实际上关联了很多子项目"
          />
        </NFormItem>
        <NFormItem label="若此字段有UUID值,表明它可能为其他项目的子项,比如“空头”,它为烟支外观项目的子项" path="rid">
          <NInput
            v-model:value="model.rid"
            placeholder="请输入若此字段有UUID值,表明它可能为其他项目的子项,比如“空头”,它为烟支外观项目的子项"
          />
        </NFormItem>
        <NFormItem label="范围-备用" path="category">
          <NInput v-model:value="model.category" placeholder="请输入范围-备用" />
        <NFormItem label="判定级别" path="cls">
          <NSelect v-model:value="(model.cls as any)" :options="clsOptions" placeholder="请选择判定级别" />
        </NFormItem>
        <NFormItem label="备注" path="decisionDes">
          <NInput v-model:value="model.decisionDes" placeholder="请输入备注" />
        </NFormItem>
        <NFormItem label="修改人" path="updateUser">
          <NInput v-model:value="model.updateUser" placeholder="请输入修改人" />
          <NInput v-model:value="(model.decisionDes as any)" type="textarea" placeholder="请输入备注" />
        </NFormItem>
      </NForm>
      <template #footer>
ruoyi-plus-soybean/src/views/qm/judge-details/modules/judge-details-sub-table.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,414 @@
<script setup lang="tsx">
import { computed, onBeforeUnmount, onMounted, ref, watch } from 'vue';
import type { DataTableRowKey } from 'naive-ui';
import { NDivider } from 'naive-ui';
import { jsonClone } from '@sa/utils';
import { fetchBatchDeleteJudgeDetails, fetchGetJudgeDetailsTree } from '@/service/api/qm/judge-details';
import { useAuth } from '@/hooks/business/auth';
import { useNaiveTable } from '@/hooks/common/table';
import { $t } from '@/locales';
import ButtonIcon from '@/components/custom/button-icon.vue';
import JudgeDetailsOperateDrawer from './judge-details-operate-drawer.vue';
defineOptions({
  name: 'JudgeDetailsSubTable'
});
interface Props {
  judgeId?: CommonType.IdType | null;
}
const props = withDefaults(defineProps<Props>(), {
  judgeId: null
});
interface JudgeDetailsSubRow extends Api.Qm.JudgeDetails {
  children?: JudgeDetailsSubRow[];
}
const { hasAuth } = useAuth();
const checkedRowKeys = ref<CommonType.IdType[]>([]);
const drawerVisible = ref(false);
const operateType = ref<NaiveUI.TableOperateType>('add');
const editingData = ref<Api.Qm.JudgeDetails | null>(null);
const showFullscreen = ref(false);
const fullscreenStyle = ref<Record<string, string>>({});
function handleAdd() {
  if (!props.judgeId) {
    window.$message?.warning('请先选择判定依据');
    return;
  }
  operateType.value = 'add';
  editingData.value = { judgeId: props.judgeId } as any;
  drawerVisible.value = true;
}
function handleEdit(row: JudgeDetailsSubRow) {
  operateType.value = 'edit';
  editingData.value = jsonClone(row);
  drawerVisible.value = true;
}
async function handleBatchDelete() {
  if (checkedRowKeys.value.length === 0) return;
  const { error } = await fetchBatchDeleteJudgeDetails(checkedRowKeys.value);
  if (error) return;
  window.$message?.success($t('common.deleteSuccess'));
  checkedRowKeys.value = [];
  await getData();
}
async function handleDelete(id: CommonType.IdType) {
  const { error } = await fetchBatchDeleteJudgeDetails([id]);
  if (error) return;
  window.$message?.success($t('common.deleteSuccess'));
  await getData();
}
const {
  columns,
  columnChecks,
  data: rows,
  loading,
  getData,
  scrollX
} = useNaiveTable<Api.Common.PaginatingQueryRecord<Api.Qm.JudgeDetails>, Api.Qm.JudgeDetails>({
  api: async () => {
    if (!props.judgeId) return { rows: [], total: 0, pageNum: 1 } as any;
    const { data } = await fetchGetJudgeDetailsTree({
      pageNum: 1,
      pageSize: 9999,
      judgeId: props.judgeId
    });
    return data;
  },
  columns: () => [
    {
      type: 'selection',
      align: 'center',
      width: 48
    },
    {
      key: 'index',
      title: $t('common.index'),
      align: 'center',
      width: 56,
      render: (_: any, index: number) => index + 1
    },
    {
      key: 'itemName',
      title: '判定项',
      align: 'left',
      width: 250,
      tree: true
    } as any,
    {
      key: 'value3',
      title: '标准值',
      align: 'center',
      width: 100
    },
    {
      key: 'value1',
      title: '下限值',
      align: 'center',
      width: 100
    },
    {
      key: 'value2',
      title: '上限值',
      align: 'center',
      width: 100
    },
    {
      key: 'cls',
      title: '级别',
      align: 'center',
      width: 80
    },
    {
      key: 'stdscore',
      title: '分值',
      align: 'center',
      minWidth: 150
    },
    {
      key: 'decisionDes',
      title: '备注',
      align: 'center',
      minWidth: 150
    },
    {
      key: 'updateUser',
      title: '修改人',
      align: 'center',
      minWidth: 150
    },
    {
      key: 'operate',
      title: $t('common.operate'),
      fixed: 'right',
      align: 'center',
      width: 130,
      render: (row: any) => {
        const divider = () => {
          if (!hasAuth('qm:judgeDetails:edit') || !hasAuth('qm:judgeDetails:remove')) {
            return null;
          }
          return <NDivider vertical />;
        };
        const editBtn = () => {
          if (!hasAuth('qm:judgeDetails:edit')) {
            return null;
          }
          return (
            <ButtonIcon
              text
              type="primary"
              icon="material-symbols:drive-file-rename-outline-outline"
              tooltipContent={$t('common.edit')}
              onClick={() => handleEdit(row)}
            />
          );
        };
        const deleteBtn = () => {
          if (!hasAuth('qm:judgeDetails:remove')) {
            return null;
          }
          return (
            <ButtonIcon
              text
              type="error"
              icon="material-symbols:delete-outline"
              tooltipContent={$t('common.delete')}
              popconfirmContent={$t('common.confirmDelete')}
              onPositiveClick={() => handleDelete(row.id)}
            />
          );
        };
        return (
          <div class="flex-center gap-8px">
            {editBtn()}
            {divider()}
            {deleteBtn()}
          </div>
        );
      }
    }
  ],
  transform: response => {
    return response?.rows || [];
  }
});
const onFullscreenKeydown = (event: KeyboardEvent) => {
  if (event.key === 'Escape') {
    showFullscreen.value = false;
  }
};
const cardTitle = computed(() => `判定明细维护`);
watch(
  () => props.judgeId,
  async () => {
    checkedRowKeys.value = [];
    await getData();
  },
  { immediate: true }
);
watch(
  showFullscreen,
  visible => {
    if (visible) {
      updateFullscreenStyle();
      window.addEventListener('keydown', onFullscreenKeydown);
      window.addEventListener('resize', updateFullscreenStyle);
      window.addEventListener('scroll', updateFullscreenStyle, true);
      return;
    }
    window.removeEventListener('keydown', onFullscreenKeydown);
    window.removeEventListener('resize', updateFullscreenStyle);
    window.removeEventListener('scroll', updateFullscreenStyle, true);
  },
  { immediate: true }
);
onBeforeUnmount(() => {
  window.removeEventListener('keydown', onFullscreenKeydown);
  window.removeEventListener('resize', updateFullscreenStyle);
  window.removeEventListener('scroll', updateFullscreenStyle, true);
});
onMounted(() => {
  updateFullscreenStyle();
});
function updateFullscreenStyle() {
  const container = document.querySelector<HTMLElement>('.judge-content-area');
  if (!container) {
    fullscreenStyle.value = {
      position: 'fixed',
      inset: '0',
      zIndex: '20'
    };
    return;
  }
  const rect = container.getBoundingClientRect();
  fullscreenStyle.value = {
    position: 'fixed',
    left: `${rect.left}px`,
    top: `${rect.top}px`,
    width: `${rect.width}px`,
    height: `${rect.height}px`,
    zIndex: '20'
  };
}
function rowKey(row: JudgeDetailsSubRow): DataTableRowKey {
  return String(row.id);
}
</script>
<template>
  <div class="flex-col-stretch">
    <NCard
      :title="cardTitle"
      :bordered="false"
      size="small"
      class="flex-col-stretch card-wrapper flex-1-hidden"
      :content-style="{ flex: 1, overflow: 'hidden', display: 'flex', flexDirection: 'column' }"
    >
      <template #header-extra>
        <NSpace align="center">
          <TableHeaderOperation
            v-model:columns="columnChecks"
            :disabled-delete="checkedRowKeys.length === 0"
            :loading="loading"
            :show-add="hasAuth('qm:judgeDetails:add')"
            :show-delete="hasAuth('qm:judgeDetails:remove')"
            :show-export="false"
            @add="handleAdd"
            @delete="handleBatchDelete"
            @refresh="getData"
          />
          <NButton size="small" @click="showFullscreen = true">
            <template #icon>
              <icon-mdi-fullscreen class="text-icon" />
            </template>
            å…¨å±
          </NButton>
        </NSpace>
      </template>
      <NSpin :show="loading" class="h-full" content-class="h-full flex-col-stretch flex-1-hidden">
        <div v-if="!props.judgeId" class="h-full flex-center text-gray-400">请点击上方表格行查看明细</div>
        <NDataTable
          v-else
          v-model:checked-row-keys="checkedRowKeys"
          :columns="columns"
          :data="rows"
          size="small"
          flex-height
          children-key="children"
          :row-key="rowKey"
          default-expand-all
          striped
          class="flex-1-hidden"
        />
      </NSpin>
    </NCard>
    <Teleport to="body">
      <div v-if="showFullscreen" class="fullscreen-mask" :style="fullscreenStyle" @click.self="showFullscreen = false">
        <NCard
          :title="cardTitle"
          :bordered="false"
          size="small"
          class="fullscreen-card flex-col-stretch"
          :content-style="{ flex: 1, overflow: 'hidden', display: 'flex', flexDirection: 'column' }"
        >
          <template #header-extra>
            <NSpace align="center">
              <TableHeaderOperation
                v-model:columns="columnChecks"
                :disabled-delete="checkedRowKeys.length === 0"
                :loading="loading"
                :show-add="hasAuth('qm:judgeDetails:add')"
                :show-delete="hasAuth('qm:judgeDetails:remove')"
                :show-export="false"
                @add="handleAdd"
                @delete="handleBatchDelete"
                @refresh="getData"
              />
              <NButton size="small" ghost type="error" @click="showFullscreen = false">
                <template #icon>
                  <icon-mdi-close class="text-icon" />
                </template>
                å…³é—­
              </NButton>
            </NSpace>
          </template>
          <NSpin :show="loading" class="h-full" content-class="h-full">
            <div v-if="!props.judgeId" class="h-full flex-center text-gray-400">请点击上方表格行查看明细</div>
            <NDataTable
              v-else
              v-model:checked-row-keys="checkedRowKeys"
              :columns="columns"
              :data="rows"
              size="small"
              flex-height
              children-key="children"
              :row-key="rowKey"
              striped
              class="fullscreen-table"
            />
          </NSpin>
        </NCard>
      </div>
    </Teleport>
    <JudgeDetailsOperateDrawer
      v-model:visible="drawerVisible"
      :operate-type="operateType"
      :row-data="editingData"
      @submitted="getData"
    />
  </div>
</template>
<style scoped>
:deep(.n-card__content) {
  padding: 8px 12px;
}
:deep(.n-data-table-th),
:deep(.n-data-table-td) {
  padding: 4px 6px;
}
.fullscreen-mask {
  position: absolute;
  inset: 0;
  z-index: 20;
  background: #fff;
  padding: 0;
  display: flex;
}
.fullscreen-card {
  width: 100%;
  height: 100%;
}
.fullscreen-table {
  height: 100%;
}
</style>
ruoyi-plus-soybean/src/views/qm/judge/index.vue
@@ -10,6 +10,8 @@
import ButtonIcon from '@/components/custom/button-icon.vue';
import JudgeOperateDrawer from './modules/judge-operate-drawer.vue';
import JudgeSearch from './modules/judge-search.vue';
import JudgeDetailsSubTable from '../judge-details/modules/judge-details-sub-table.vue';
import StdSubTable from "@/views/qm/std/modules/std-sub-table.vue";
defineOptions({
  name: 'JudgeList'
@@ -19,6 +21,8 @@
const { download } = useDownload();
const { hasAuth } = useAuth();
const selectedJudgeId = ref<CommonType.IdType | null>(null);
const searchParams = ref<Api.Qm.JudgeSearchParams>({
  pageNum: 1,
  pageSize: 10,
@@ -26,7 +30,7 @@
  matName: null,
  judgeName: null,
  category: 0, // é»˜è®¤é€‰æ‹©æˆå“
  status: -1, // é»˜è®¤é€‰æ‹©å…¨éƒ¨
  status: 1, // é»˜è®¤æŸ¥è¯¢å¯ç”¨
  params: {}
});
@@ -276,47 +280,74 @@
function handleExport() {
  download('/qm/judge/export', searchParams.value, `判定依据_${new Date().getTime()}.xlsx`);
}
function handleRowClick(row: any) {
  return {
    onClick: (e: MouseEvent) => {
      const target = e.target as HTMLElement | null;
      if (target?.closest('.n-checkbox') || target?.closest('.n-button') || target?.closest('a')) return;
      selectedJudgeId.value = row.id;
    },
    style: 'cursor: pointer;'
  };
}
</script>
<template>
  <div class="min-h-500px flex-col-stretch gap-16px overflow-hidden lt-sm:overflow-auto">
    <JudgeSearch v-model:model="searchParams" @search="getDataByPage" />
    <NCard title="判定依据列表" :bordered="false" size="small" class="card-wrapper sm:flex-1-hidden">
      <template #header-extra>
        <TableHeaderOperation
          v-model:columns="columnChecks"
          :disabled-delete="checkedRowKeys.length === 0"
          :loading="loading"
          :show-add="hasAuth('qm:judge:add')"
          :show-delete="hasAuth('qm:judge:remove')"
          :show-export="hasAuth('qm:judge:export')"
          @add="handleAdd"
          @delete="handleBatchDelete"
          @export="handleExport"
          @refresh="getData"
        />
      </template>
      <NDataTable
        v-model:checked-row-keys="checkedRowKeys"
        :columns="columns"
        :data="data"
    <div class="judge-content-area relative flex-col-stretch gap-16px sm:flex-1-hidden">
      <NCard
        title="判定依据列表"
        :bordered="false"
        size="small"
        :flex-height="!appStore.isMobile"
        :scroll-x="scrollX"
        :loading="loading"
        remote
        :row-key="row => row.id"
        :pagination="mobilePagination"
        class="sm:h-full"
      />
      <JudgeOperateDrawer
        v-model:visible="drawerVisible"
        :operate-type="operateType"
        :row-data="editingData"
        @submitted="getDataByPage"
      />
    </NCard>
        class="flex-col-stretch card-wrapper sm:flex-1-hidden"
        :content-style="{ flex: 1, overflow: 'hidden', display: 'flex', flexDirection: 'column' }"
      >
        <template #header-extra>
          <TableHeaderOperation
            v-model:columns="columnChecks"
            :disabled-delete="checkedRowKeys.length === 0"
            :loading="loading"
            :show-add="hasAuth('qm:judge:add')"
            :show-delete="hasAuth('qm:judge:remove')"
            :show-export="hasAuth('qm:judge:export')"
            @add="handleAdd"
            @delete="handleBatchDelete"
            @export="handleExport"
            @refresh="getData"
          />
        </template>
        <NDataTable
          v-model:checked-row-keys="checkedRowKeys"
          :columns="columns"
          :data="data"
          size="small"
          :flex-height="!appStore.isMobile"
          :scroll-x="scrollX"
          :loading="loading"
          remote
          :row-key="row => row.id"
          :pagination="mobilePagination"
          :row-props="handleRowClick"
          class="flex-1-hidden"
        />
        <JudgeOperateDrawer
          v-model:visible="drawerVisible"
          :operate-type="operateType"
          :row-data="editingData"
          @submitted="getDataByPage"
        />
      </NCard>
      <JudgeDetailsSubTable :judge-id="selectedJudgeId" class="sm:flex-1-hidden"  />
    </div>
  </div>
</template>
<style scoped></style>
<style scoped>
:deep(.n-data-table-th),
:deep(.n-data-table-td) {
  padding: 4px 6px;
  overflow: hidden;
}
</style>
ruoyi-plus-soybean/src/views/qm/judge/modules/judge-operate-drawer.vue
@@ -1,10 +1,11 @@
<script setup lang="ts">
import { computed, onMounted, ref, watch } from 'vue';
import { computed, ref, watch } from 'vue';
import type { SelectOption } from 'naive-ui';
import { NSelect } from 'naive-ui';
import { jsonClone } from '@sa/utils';
import { fetchCreateJudge, fetchUpdateJudge } from '@/service/api/qm/judge';
import { fetchGetStdList } from '@/service/api/qm/std';
import { fetchGetMatList } from '@/service/api/md/mat';
import { useFormRules, useNaiveForm } from '@/hooks/common/form';
import { $t } from '@/locales';
@@ -53,6 +54,7 @@
];
const stdOptions = ref<SelectOption[]>([]);
const matOptions = ref<SelectOption[]>([]);
type Model = Api.Qm.JudgeOperateParams;
@@ -102,6 +104,20 @@
  }
}
async function fetchMatOptions() {
  if (model.value.category === 1) {
    matOptions.value = [];
    return;
  }
  const { data } = await fetchGetMatList({ tid: 1, pageSize: 1000 });
  if (data) {
    matOptions.value = data.rows.map(item => ({
      label: item.name,
      value: item.code
    }));
  }
}
async function handleSubmit() {
  await validate();
@@ -147,20 +163,39 @@
  emit('submitted');
}
watch(visible, () => {
watch(visible, async () => {
  if (visible.value) {
    handleUpdateModelWhenEdit();
    restoreValidation();
    // Fetch stdOptions when the drawer becomes visible
    fetchStdOptions();
    await fetchStdOptions();
    await fetchMatOptions();
  }
});
watch(
  () => model.value.category,
  () => {
  async () => {
    fetchStdOptions();
    model.value.matName = '';
    model.value.matCode = '';
    await fetchMatOptions();
  }
);
watch(
  () => model.value.matCode,
  val => {
    if (val) {
      const selectedOption = matOptions.value.find(opt => opt.value === val);
      if (selectedOption && typeof selectedOption.label === 'string') {
        const name = selectedOption.label as string;
        model.value.matName = name;
        model.value.judgeName = name;
      }
    } else {
      model.value.matName = '';
    }
  }
);
</script>
@@ -172,15 +207,17 @@
        <NFormItem label="判定名称" path="judgeName">
          <NInput v-model:value="model.judgeName" placeholder="请输入判定名称" />
        </NFormItem>
        <NFormItem label="物料牌号代码" path="matCode">
          <NInput v-model:value="model.matCode" placeholder="请输入物料牌号代码" />
        </NFormItem>
        <NFormItem label="物料牌号" path="matName">
          <NInput v-model:value="model.matName" placeholder="请输入物料牌号" />
        </NFormItem>
        <NFormItem label="物料类型" path="category">
          <NSelect v-model:value="model.category" :options="categoryOptions" placeholder="请选择物料类型" />
        </NFormItem>
        <NFormItem label="物料牌号" path="matName">
          <NSelect
            v-model:value="model.matCode"
            :options="matOptions"
            placeholder="请选择物料牌号"
            :disabled="model.category === 1"
          />
        </NFormItem>
        <NFormItem label="状态" path="status">
          <NSelect v-model:value="model.status" :options="statusOptions" placeholder="请选择状态" />
        </NFormItem>
ruoyi-plus-soybean/src/views/qm/judge/modules/judge-search.vue
@@ -1,6 +1,7 @@
<script setup lang="ts">
import { toRaw } from 'vue';
import { onMounted, ref, toRaw, watch } from 'vue';
import { jsonClone } from '@sa/utils';
import { fetchGetMatList } from '@/service/api/md/mat';
import { useNaiveForm } from '@/hooks/common/form';
import { $t } from '@/locales';
@@ -19,6 +20,43 @@
const model = defineModel<Api.Qm.JudgeSearchParams>('model', { required: true });
const defaultModel = jsonClone(toRaw(model.value));
const matOptions = ref<CommonType.Option[]>([]);
async function getMatOptions() {
  if (model.value.category !== 0) {
    matOptions.value = [];
    model.value.matName = null;
    model.value.matCode = null;
    return;
  }
  const { data } = await fetchGetMatList({ tid: 1, pageSize: 1000 });
  if (data) {
    matOptions.value = data.rows.map(item => ({
      label: item.name,
      value: item.code
    }));
  }
}
watch(
  () => model.value.category,
  newVal => {
    if (newVal === 0) {
      getMatOptions();
    } else {
      matOptions.value = [];
      model.value.matName = null;
      model.value.matCode = null;
    }
  }
);
onMounted(() => {
  if (model.value.category === 0) {
    getMatOptions();
  }
});
function resetModel() {
  Object.assign(model.value, defaultModel);
@@ -53,11 +91,15 @@
              />
            </NFormItemGi>
            <NFormItemGi span="24 s:12 m:6" label="物料牌号代码" label-width="auto" path="matCode" class="pr-24px">
              <NInput v-model:value="model.matCode" placeholder="请输入物料牌号代码" />
            </NFormItemGi>
            <NFormItemGi span="24 s:12 m:6" label="物料牌号" label-width="auto" path="matName" class="pr-24px">
              <NInput v-model:value="model.matName" placeholder="请输入物料牌号" />
            <NFormItemGi span="24 s:12 m:6" label="物料牌号" label-width="auto" path="matCode" class="pr-24px">
              <NSelect
                v-model:value="model.matCode"
                :options="matOptions"
                placeholder="请选择物料牌号"
                :disabled="model.category !== 0"
                clearable
                filterable
              />
            </NFormItemGi>
            <NFormItemGi span="24 s:12 m:6" label="判定名称" label-width="auto" path="judgeName" class="pr-24px">
              <NInput v-model:value="model.judgeName" placeholder="请输入判定名称" />
ruoyi-plus-soybean/src/views/qm/matcheck/index.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,268 @@
<script setup lang="tsx">
import { ref } from 'vue';
import { NDivider } from 'naive-ui';
import { fetchBatchDeleteMatcheck, fetchGetQmMatcheckList } from '@/service/api/qm/matcheck';
import { useAppStore } from '@/store/modules/app';
import { useAuth } from '@/hooks/business/auth';
import { useDownload } from '@/hooks/business/download';
import { defaultTransform, useNaivePaginatedTable, useTableOperate } from '@/hooks/common/table';
import { $t } from '@/locales';
import ButtonIcon from '@/components/custom/button-icon.vue';
import MatcheckOperateDrawer from './modules/matcheck-operate-drawer.vue';
import { useRoute } from 'vue-router';
defineOptions({
  name: 'MatcheckList'
});
const appStore = useAppStore();
const { download } = useDownload();
const { hasAuth } = useAuth();
const route = useRoute();
function getSingleQueryValue(query: string | string[] | null | undefined) {
  if (Array.isArray(query)) return query[0] || '';
  return query || '';
}
const judgeCode = getSingleQueryValue(route.query.judgeCode);
const batchCode = getSingleQueryValue(route.query.batchCode);
const matCode = getSingleQueryValue(route.query.matCode);
const searchParams = ref<Api.Qm.MatcheckSearchParams>({
  pageNum: 1,
  pageSize: 10,
  batchCode,
  judgeCode,
  matCode,
  checkName: null,
  checkTime: null,
  reviewName: null,
  reviewTiem: null,
  params: {}
});
const { columns, columnChecks, data, getData, getDataByPage, loading, mobilePagination, scrollX } =
  useNaivePaginatedTable({
  api: () => fetchGetQmMatcheckList(searchParams.value),
  transform: response => defaultTransform(response),
  onPaginationParamsChange: params => {
    searchParams.value.pageNum = params.page;
    searchParams.value.pageSize = params.pageSize;
  },
  columns: () => [
    {
      type: 'selection',
      align: 'center',
      width: 48
    },
    {
      key: 'index',
      title: $t('common.index'),
      align: 'center',
      width: 64,
      render: (_, index) => index + 1
    },
    {
      key: 'itemName',
      title: '检验项',
      align: 'center',
      minWidth: 120
    },
    {
      key: 'maxval',
      title: '最大值/烟丝质量(含末率)',
      align: 'center',
      minWidth: 170
    },
    {
      key: 'minval',
      title: '最小值/烟末质量(含末率)',
      align: 'center',
      minWidth: 170
    },
    {
      key: 'avgval',
      title: '平均值/含末率',
      align: 'center',
      minWidth: 130
    },
    {
      key: 'sdval',
      title: 'SD值',
      align: 'center',
      minWidth: 100
    },
    {
      key: 'cvval',
      title: 'CV值',
      align: 'center',
      minWidth: 100
    },
    {
      key: 'cpkval',
      title: 'CPK值',
      align: 'center',
      minWidth: 100
    },
    {
      key: 'badval',
      title: '超标数',
      align: 'center',
      minWidth: 100
    },
    {
      key: 'checker',
      title: '检验员',
      align: 'center',
      minWidth: 100
    },
    {
      key: 'checkTime',
      title: '检验时间',
      align: 'center',
      minWidth: 120
    },
    {
      key: 'reviewName',
      title: '复检员',
      align: 'center',
      minWidth: 100
    },
    {
      key: 'reviewTime',
      title: '复检时间',
      align: 'center',
      minWidth: 120
    },
    {
      key: 'chkDes',
      title: '描述',
      align: 'center',
      minWidth: 120
    },
    {
      key: 'operate',
      title: $t('common.operate'),
      align: 'center',
      fixed: 'right',
      width: 130,
      render: row => {
        const divider = () => {
          if (!hasAuth('qm:matcheck:edit') || !hasAuth('qm:matcheck:remove')) {
            return null;
          }
          return <NDivider vertical />;
        };
        const editBtn = () => {
          if (!hasAuth('qm:matcheck:edit')) {
            return null;
          }
          return (
            <ButtonIcon
              text
              type="primary"
              icon="material-symbols:drive-file-rename-outline-outline"
              tooltipContent={$t('common.edit')}
              onClick={() => edit(row.id)}
            />
          );
        };
        const deleteBtn = () => {
          if (!hasAuth('qm:matcheck:remove')) {
            return null;
          }
          return (
            <ButtonIcon
              text
              type="error"
              icon="material-symbols:delete-outline"
              tooltipContent={$t('common.delete')}
              popconfirmContent={$t('common.confirmDelete')}
              onPositiveClick={() => handleDelete(row.id)}
            />
          );
        };
        return (
          <div class="flex-center gap-8px">
            {editBtn()}
            {divider()}
            {deleteBtn()}
          </div>
        );
      }
    }
  ]
});
const { drawerVisible, operateType, editingData, handleAdd, handleEdit, checkedRowKeys, onBatchDeleted, onDeleted } =
  useTableOperate(data, 'id', getData);
async function handleBatchDelete() {
  // request
  const { error } = await fetchBatchDeleteMatcheck(checkedRowKeys.value);
  if (error) return;
  onBatchDeleted();
}
async function handleDelete(id: CommonType.IdType) {
  // request
  const { error } = await fetchBatchDeleteMatcheck([id]);
  if (error) return;
  onDeleted();
}
function edit(id: CommonType.IdType) {
  handleEdit(id);
}
function handleExport() {
  download('/qm/matcheck/export', searchParams.value, `材料检验统计_${new Date().getTime()}.xlsx`);
}
</script>
<template>
  <div class="min-h-500px flex-col-stretch gap-16px overflow-hidden lt-sm:overflow-auto">
    <NCard title="材料检验统计列表" :bordered="false" size="small" class="card-wrapper sm:flex-1-hidden">
      <template #header-extra>
        <TableHeaderOperation
          v-model:columns="columnChecks"
          :disabled-delete="checkedRowKeys.length === 0"
          :loading="loading"
          :show-add="hasAuth('qm:matcheck:add')"
          :show-delete="hasAuth('qm:matcheck:remove')"
          :show-export="hasAuth('qm:matcheck:export')"
          @add="handleAdd"
          @delete="handleBatchDelete"
          @export="handleExport"
          @refresh="getData"
        />
      </template>
      <NDataTable
        v-model:checked-row-keys="checkedRowKeys"
        :columns="columns"
        :data="data"
        size="small"
        :flex-height="!appStore.isMobile"
        :scroll-x="scrollX"
        :loading="loading"
        remote
        :row-key="row => row.id"
        :pagination="mobilePagination"
        class="sm:h-full"
      />
      <MatcheckOperateDrawer
        v-model:visible="drawerVisible"
        :operate-type="operateType"
        :row-data="editingData"
        @submitted="getDataByPage"
      />
    </NCard>
  </div>
</template>
<style scoped></style>
ruoyi-plus-soybean/src/views/qm/matcheck/modules/matcheck-operate-drawer.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,266 @@
<script setup lang="ts">
import { computed, onMounted, ref, watch } from 'vue';
import { jsonClone } from '@sa/utils';
import { fetchCreateMatcheck, fetchUpdateMatcheck, fetchCheckItemList } from '@/service/api/qm/matcheck';
import { useFormRules, useNaiveForm } from '@/hooks/common/form';
import { $t } from '@/locales';
import { useRoute } from 'vue-router';
defineOptions({
  name: 'MatcheckOperateDrawer'
});
interface Props {
  /** the type of operation */
  operateType: NaiveUI.TableOperateType;
  /** the edit row data */
  rowData?: Api.Qm.Matcheck | null;
}
const props = defineProps<Props>();
interface Emits {
  (e: 'submitted'): void;
}
const emit = defineEmits<Emits>();
const visible = defineModel<boolean>('visible', {
  default: false
});
const { formRef, validate, restoreValidation } = useNaiveForm();
const { createRequiredRule } = useFormRules();
const route = useRoute();
// æ£€éªŒé¡¹é€‰é¡¹
const checkItemOptions = ref<CommonType.Option[]>([]);
// èŽ·å–æ£€éªŒé¡¹é€‰é¡¹
async function getCheckItemOptions() {
  const judgeCode = Array.isArray(route.query.judgeCode) ? route.query.judgeCode[0] : route.query.judgeCode;
  if (!judgeCode) {
    checkItemOptions.value = [];
    return;
  }
  const { data, error } = await fetchCheckItemList({ judgeId: judgeCode });
  if (!error && data) {
    checkItemOptions.value = data.map(item => ({
      label: item.itemName, // æŽ¥å£è¿”回的字段是 item_name
      value: item.itemCode // æŽ¥å£è¿”回的字段是 item_code
    }));
  }
}
const title = computed(() => {
  const titles: Record<NaiveUI.TableOperateType, string> = {
    add: '新增材料检验统计',
    edit: '编辑材料检验统计'
  };
  return titles[props.operateType];
});
type Model = Api.Qm.MatcheckOperateParams;
const model = ref<Model>(createDefaultModel());
const generateDetailsChecked = computed<string | number | boolean | undefined>({
  get() {
    return model.value.generateDetails ?? '0';
  },
  set(value) {
    model.value.generateDetails = String(value ?? '0');
  }
});
function createDefaultModel(): Model {
  // èŽ·å–å½“å‰æ—¥æœŸï¼Œæ ¼å¼ä¸º yyyy-MM-dd
  const today = new Date().toISOString().split('T')[0];
  const matCode = (route.query.matCode as string) || '';
  return {
      id: '',
      pid: '',
      batchCode: route.query.batchCode as string || '',
      matCode,
      instrumentCode: '',
      techReq: '',
      checkStd: '',
      testEnv: '',
      itemCode: '',
      subBatchCode: '',
      sampleNumber: null,
      sampleType: null,
      checkName: '',
      checkTime: today,
      reviewName: '',
      reviewTime: null,
      maxval: null,
      minval: null,
      avgval: null,
      sdval: null,
      cvval: null,
      cpkval: null,
      badval: null,
      judge: '',
      singlejudge: '',
      verName: '',
      verCode: '',
      archDate: '',
      del: null,
      flag: '',
      toMesTime: null,
      chkDes: '',
      generateDetails: '0'
  };
}
type RuleKey = Extract<
  keyof Model,
  | 'id'
>;
const rules: Record<RuleKey, App.Global.FormRule> = {
  id: createRequiredRule('主键,编码不能为空'),
};
function handleUpdateModelWhenEdit() {
  model.value = createDefaultModel();
  if (props.operateType === 'edit' && props.rowData) {
    Object.assign(model.value, jsonClone(props.rowData));
    model.value.generateDetails = model.value.generateDetails ?? '0';
  }
}
function closeDrawer() {
  visible.value = false;
}
async function handleSubmit() {
  await validate();
  const { id, pid, batchCode, matCode, instrumentCode, techReq, checkStd, testEnv, itemCode, subBatchCode, sampleNumber, sampleType, checkName, checkTime, reviewName, reviewTime, maxval, minval, avgval, sdval, cvval, cpkval, badval, judge, singlejudge, verName, verCode, archDate, del, flag, toMesTime, chkDes, generateDetails } = model.value;
  // request
  if (props.operateType === 'add') {
    const { error } = await fetchCreateMatcheck({ id, pid, batchCode, matCode, instrumentCode, techReq, checkStd, testEnv, itemCode, subBatchCode, sampleNumber, sampleType, checkName, checkTime, reviewName, reviewTime, maxval, minval, avgval, sdval, cvval, cpkval, badval, judge, singlejudge, verName, verCode, archDate, del, flag, toMesTime, chkDes, generateDetails });
    if (error) return;
  }
  if (props.operateType === 'edit') {
    const { error } = await fetchUpdateMatcheck({ id, pid, batchCode, matCode, instrumentCode, techReq, checkStd, testEnv, itemCode, subBatchCode, sampleNumber, sampleType, checkName, checkTime, reviewName, reviewTime, maxval, minval, avgval, sdval, cvval, cpkval, badval, judge, singlejudge, verName, verCode, archDate, del, flag, toMesTime, chkDes, generateDetails });
    if (error) return;
  }
  window.$message?.success($t('common.updateSuccess'));
  closeDrawer();
  emit('submitted');
}
onMounted(() => {
  getCheckItemOptions();
});
watch(visible, () => {
  if (visible.value) {
    handleUpdateModelWhenEdit();
    restoreValidation();
    getCheckItemOptions();
  }
});
</script>
<template>
  <NDrawer v-model:show="visible" :title="title" display-directive="show" :width="800" class="max-w-90%">
    <NDrawerContent :title="title" :native-scrollbar="false" closable>
      <NForm ref="formRef" :model="model" :rules="rules" label-placement="left" :label-width="180">
        <NFormItem label="批次号" path="batchCode">
          <NInput v-model:value="model.batchCode" placeholder="批次号" :disabled="true" />
        </NFormItem>
        <NFormItem label="检验项" path="itemCode">
          <NSelect
            v-model:value="model.itemCode"
            placeholder="请选择检验项"
            :options="checkItemOptions"
            clearable
            filterable
          />
        </NFormItem>
        <NFormItem label="盘号" path="subBatchCode">
          <NInput v-model:value="model.subBatchCode" placeholder="请输入盘号" />
        </NFormItem>
        <NFormItem path="maxval">
          <template #label>
            æœ€å¤§å€¼/<br>
            çƒŸä¸è´¨é‡/<br>
            (含末率)
          </template>
          <NInputNumber v-model:value="model.maxval" placeholder="请输入最大值" class="w-full" />
        </NFormItem>
        <NFormItem path="minval">
          <template #label>
            æœ€å°å€¼/<br>
            çƒŸæœ«è´¨é‡/<br>
            (含末率)
          </template>
          <NInputNumber v-model:value="model.minval" placeholder="请输入最小值" class="w-full" />
        </NFormItem>
        <NFormItem path="avgval">
          <template #label>
            å¹³å‡å€¼/<br>
            å«æœ«çއ
          </template>
          <NInputNumber v-model:value="model.avgval" placeholder="请输入平均值" class="w-full" />
        </NFormItem>
        <NFormItem label="SD值" path="sdval">
          <NInputNumber v-model:value="model.sdval" placeholder="请输入SD值" class="w-full" />
        </NFormItem>
        <NFormItem label="CV值" path="cvval">
          <NInputNumber v-model:value="model.cvval" placeholder="请输入CV值" class="w-full" />
        </NFormItem>
        <NFormItem label="CPK值" path="cpkval">
          <NInputNumber v-model:value="model.cpkval" placeholder="请输入CPK值" class="w-full" />
        </NFormItem>
        <NFormItem label="超标数" path="badval">
          <NInputNumber v-model:value="model.badval" placeholder="请输入超标数" class="w-full" />
        </NFormItem>
        <NFormItem label="检验日期" path="checkTime">
          <NDatePicker
            v-model:formatted-value="model.checkTime"
            type="date"
            value-format="yyyy-MM-dd"
            clearable
          />
        </NFormItem>
        <NFormItem label="判定" path="judge">
          <NInput v-model:value="model.judge" placeholder="请输入判定" />
        </NFormItem>
        <NFormItem label="单项判定" path="singlejudge">
          <NInput v-model:value="model.singlejudge" placeholder="请输入单项判定" />
        </NFormItem>
        <NFormItem label="描述" path="chkDes">
          <NInput v-model:value="model.chkDes" placeholder="请输入描述" />
        </NFormItem>
        <NFormItem label="生成明细" path="generateDetails">
          <NCheckbox v-model:checked="generateDetailsChecked" checked-value="1" unchecked-value="0" />
        </NFormItem>
      </NForm>
      <template #footer>
        <NSpace :size="16">
          <NButton @click="closeDrawer">{{ $t('common.cancel') }}</NButton>
          <NButton type="primary" @click="handleSubmit">{{ $t('common.confirm') }}</NButton>
        </NSpace>
      </template>
    </NDrawerContent>
  </NDrawer>
</template>
<style scoped>
.n-form-item-label {
  white-space: pre-line;
  line-height: 1.4;
}
</style>
ruoyi-plus-soybean/src/views/qm/matcheck/modules/matcheck-search.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,93 @@
<script setup lang="ts">
import { toRaw } from 'vue';
import { jsonClone } from '@sa/utils';
import { useNaiveForm } from '@/hooks/common/form';
import { $t } from '@/locales';
defineOptions({
  name: 'MatcheckSearch'
});
interface Emits {
  (e: 'search'): void;
}
const emit = defineEmits<Emits>();
const { formRef, validate, restoreValidation } = useNaiveForm();
const model = defineModel<Api.Qm.MatcheckSearchParams>('model', { required: true });
const defaultModel = jsonClone(toRaw(model.value));
function resetModel() {
  Object.assign(model.value, defaultModel);
}
async function reset() {
  await restoreValidation();
  resetModel();
  emit('search');
}
async function search() {
  await validate();
  emit('search');
}
</script>
<template>
  <NCard :bordered="false" size="small" class="card-wrapper">
    <NCollapse>
      <NCollapseItem :title="$t('common.search')" name="qm-matcheck-search">
        <NForm ref="formRef" :model="model" label-placement="left" :label-width="80">
          <NGrid responsive="screen" item-responsive>
            <NFormItemGi span="24 s:12 m:6" label="牌号" label-width="auto" path="matCode" class="pr-24px">
              <NInput v-model:value="model.matCode" placeholder="请输入牌号" />
            </NFormItemGi>
            <NFormItemGi span="24 s:12 m:6" label="检验员" label-width="auto" path="checkName" class="pr-24px">
              <NInput v-model:value="model.checkName" placeholder="请输入检验员" />
            </NFormItemGi>
            <NFormItemGi span="24 s:12 m:6" label="检验时间" label-width="auto" path="checkTime" class="pr-24px">
              <NDatePicker
                v-model:formatted-value="model.checkTime"
                type="datetime"
                value-format="yyyy-MM-dd HH:mm:ss"
                clearable
              />
            </NFormItemGi>
            <NFormItemGi span="24 s:12 m:6" label="复核员" label-width="auto" path="reviewName" class="pr-24px">
              <NInput v-model:value="model.reviewName" placeholder="请输入复核员" />
            </NFormItemGi>
            <NFormItemGi span="24 s:12 m:6" label="复核时间" label-width="auto" path="reviewTiem" class="pr-24px">
              <NDatePicker
                v-model:formatted-value="model.reviewTiem"
                type="datetime"
                value-format="yyyy-MM-dd HH:mm:ss"
                clearable
              />
            </NFormItemGi>
            <NFormItemGi :show-feedback="false" span="24" class="pr-24px">
              <NSpace class="w-full" justify="end">
                <NButton @click="reset">
                  <template #icon>
                    <icon-ic-round-refresh class="text-icon" />
                  </template>
                  {{ $t('common.reset') }}
                </NButton>
                <NButton type="primary" ghost @click="search">
                  <template #icon>
                    <icon-ic-round-search class="text-icon" />
                  </template>
                  {{ $t('common.search') }}
                </NButton>
              </NSpace>
            </NFormItemGi>
          </NGrid>
        </NForm>
      </NCollapseItem>
    </NCollapse>
  </NCard>
</template>
<style scoped></style>