From 92ddfac6cea39eb0b0a0a81b11294998787f4b9e Mon Sep 17 00:00:00 2001
From: zhuguifei <312353457@qq.com>
Date: 星期五, 17 四月 2026 15:23:46 +0800
Subject: [PATCH] feat: 1.新增物料、物料类型、材料检验统计等表维护功能 2.完善判定依据-判断依据明细 3.新增判断依据、物料管理等字典翻译类 4.成品物料批次-原始数据维护页面
---
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/qm/domain/QmMatcheck.java | 184 ++
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/sc/md/domain/vo/MdMatVo.java | 118 +
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/sc/md/mapper/MdMatTypeMapper.java | 15
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/sc/md/domain/vo/MdMatTypeVo.java | 106 +
ruoyi-plus-soybean/src/service/api/md/mat.ts | 35
ruoyi-plus-soybean/src/typings/api/md.mat-type.api.d.ts | 76
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/qm/mapper/QmMatcheckMapper.java | 38
ruoyi-plus-soybean/src/views/md/mat-type/index.vue | 241 ++
ruoyi-plus-soybean/src/views/md/mat/modules/mat-operate-drawer.vue | 184 ++
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/qm/domain/vo/QmMatcheckVo.java | 255 +++
ruoyi-plus-soybean/src/views/md/mat/index.vue | 283 +++
ruoyi-plus-soybean/src/typings/api/md.mat.api.d.ts | 83 +
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/sc/md/domain/bo/MdMatTypeBo.java | 87 +
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/sc/md/service/impl/MdMatServiceImpl.java | 147 +
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-translation/src/main/java/org/dromara/common/translation/core/impl/MatNameTranslationImpl.java | 28
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/sc/md/controller/MdMatController.java | 105 +
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/resources/mapper/qa/qm/QmMatcheckMapper.xml | 67
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/resources/mapper/sc/md/MdMatMapper.xml | 6
ruoyi-plus-soybean/src/views/md/mat-type/modules/mat-type-operate-drawer.vue | 171 ++
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/qm/service/IQmMatcheckService.java | 85 +
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/resources/mapper/sc/md/MdMatTypeMapper.xml | 6
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/qm/service/impl/QmMatcheckServiceImpl.java | 154 +
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/sc/md/service/impl/MdMatTypeServiceImpl.java | 138 +
ruoyi-plus-soybean/src/typings/api/qm.matcheck.api.d.ts | 165 +
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/sc/md/controller/MdMatTypeController.java | 105 +
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/sc/md/domain/MdMatType.java | 87 +
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/sc/md/domain/bo/MdMatBo.java | 97 +
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/qm/domain/bo/QmMatcheckBo.java | 183 ++
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/sc/md/mapper/MdMatMapper.java | 15
ruoyi-plus-soybean/src/views/md/mat/modules/mat-search.vue | 119 +
ruoyi-plus-soybean/src/views/qm/judge-details/modules/judge-details-sub-table.vue | 414 ++++
ruoyi-plus-soybean/src/views/qm/matcheck/index.vue | 268 +++
ruoyi-plus-soybean/src/service/api/qm/matcheck.ts | 57
ruoyi-plus-soybean/src/service/api/md/mat-type.ts | 35
ruoyi-plus-soybean/src/views/qm/matcheck/modules/matcheck-search.vue | 93 +
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/resources/mapper/sc/test | 0
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/sc/md/service/IMdMatService.java | 68
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/sc/md/domain/MdMat.java | 97 +
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/qm/controller/QmMatcheckController.java | 122 +
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/sc/md/service/IMdMatTypeService.java | 68
ruoyi-plus-soybean/src/views/md/mat-type/modules/mat-type-search.vue | 99 +
ruoyi-plus-soybean/src/views/qm/matcheck/modules/matcheck-operate-drawer.vue | 266 +++
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/sc/test | 0
43 files changed, 4,970 insertions(+), 0 deletions(-)
diff --git a/RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-translation/src/main/java/org/dromara/common/translation/core/impl/MatNameTranslationImpl.java b/RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-translation/src/main/java/org/dromara/common/translation/core/impl/MatNameTranslationImpl.java
new file mode 100755
index 0000000..39d48cf
--- /dev/null
+++ b/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());
+ }
+}
diff --git a/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/qm/controller/QmMatcheckController.java b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/qm/controller/QmMatcheckController.java
new file mode 100644
index 0000000..5bf78c1
--- /dev/null
+++ b/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));
+ }
+}
diff --git a/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/qm/domain/QmMatcheck.java b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/qm/domain/QmMatcheck.java
new file mode 100644
index 0000000..e3f73e4
--- /dev/null
+++ b/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;
+
+ /**
+ * 妫�楠岄」鐩瓹OD
+ */
+ 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-鏈笂浼爉es 1-宸蹭笂浼�
+ */
+ private String flag;
+
+ /**
+ * 涓婁紶mes鏃堕棿
+ */
+ private Date toMesTime;
+
+ /**
+ * 澶囨敞
+ */
+ private String chkDes;
+
+
+}
diff --git a/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/qm/domain/bo/QmMatcheckBo.java b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/qm/domain/bo/QmMatcheckBo.java
new file mode 100644
index 0000000..98378e5
--- /dev/null
+++ b/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;
+
+ /**
+ * 妫�楠岄」鐩瓹OD
+ */
+ 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-鏈笂浼爉es 1-宸蹭笂浼�
+ */
+ private String flag;
+
+ /**
+ * 涓婁紶mes鏃堕棿
+ */
+ private Date toMesTime;
+
+ /**
+ * 澶囨敞
+ */
+ private String chkDes;
+
+
+}
diff --git a/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/qm/domain/vo/QmMatcheckVo.java b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/qm/domain/vo/QmMatcheckVo.java
new file mode 100644
index 0000000..30a2376
--- /dev/null
+++ b/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;
+
+ /**
+ * 妫�楠岄」鐩瓹OD
+ */
+ @ExcelProperty(value = "妫�楠岄」鐩瓹OD")
+ 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-鏈笂浼爉es 1-宸蹭笂浼�
+ */
+ @ExcelProperty(value = "0-鏈笂浼爉es 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;
+
+
+}
diff --git a/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/qm/mapper/QmMatcheckMapper.java b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/qm/mapper/QmMatcheckMapper.java
new file mode 100644
index 0000000..6c6fbaa
--- /dev/null
+++ b/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;
+
+/**
+ * 鏉愭枡妫�楠岀粺璁apper鎺ュ彛
+ *
+ * @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);
+}
diff --git a/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/qm/service/IQmMatcheckService.java b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/qm/service/IQmMatcheckService.java
new file mode 100644
index 0000000..6f7f7da
--- /dev/null
+++ b/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;
+
+/**
+ * 鏉愭枡妫�楠岀粺璁ervice鎺ュ彛
+ *
+ * @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);
+}
diff --git a/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/qm/service/impl/QmMatcheckServiceImpl.java b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/qm/service/impl/QmMatcheckServiceImpl.java
new file mode 100644
index 0000000..721519d
--- /dev/null
+++ b/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;
+
+/**
+ * 鏉愭枡妫�楠岀粺璁ervice涓氬姟灞傚鐞�
+ *
+ * @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);
+ }
+}
diff --git a/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/sc/md/controller/MdMatController.java b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/sc/md/controller/MdMatController.java
new file mode 100644
index 0000000..038b934
--- /dev/null
+++ b/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));
+ }
+}
diff --git a/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/sc/md/controller/MdMatTypeController.java b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/sc/md/controller/MdMatTypeController.java
new file mode 100644
index 0000000..2729d63
--- /dev/null
+++ b/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));
+ }
+}
diff --git a/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/sc/md/domain/MdMat.java b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/sc/md/domain/MdMat.java
new file mode 100644
index 0000000..389590c
--- /dev/null
+++ b/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;
+
+ /**
+ * 鐗╂枡浠g爜
+ */
+ 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;
+
+
+}
diff --git a/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/sc/md/domain/MdMatType.java b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/sc/md/domain/MdMatType.java
new file mode 100644
index 0000000..273e37f
--- /dev/null
+++ b/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;
+
+
+}
diff --git a/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/sc/md/domain/bo/MdMatBo.java b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/sc/md/domain/bo/MdMatBo.java
new file mode 100644
index 0000000..8937469
--- /dev/null
+++ b/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;
+
+ /**
+ * 鐗╂枡浠g爜
+ */
+ 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;
+
+
+}
diff --git a/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/sc/md/domain/bo/MdMatTypeBo.java b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/sc/md/domain/bo/MdMatTypeBo.java
new file mode 100644
index 0000000..570defe
--- /dev/null
+++ b/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;
+
+
+}
diff --git a/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/sc/md/domain/vo/MdMatTypeVo.java b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/sc/md/domain/vo/MdMatTypeVo.java
new file mode 100644
index 0000000..e84d889
--- /dev/null
+++ b/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;
+
+
+}
diff --git a/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/sc/md/domain/vo/MdMatVo.java b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/sc/md/domain/vo/MdMatVo.java
new file mode 100644
index 0000000..56be849
--- /dev/null
+++ b/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;
+
+ /**
+ * 鐗╂枡浠g爜
+ */
+ @ExcelProperty(value = "鐗╂枡浠g爜")
+ 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;
+
+
+}
diff --git a/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/sc/md/mapper/MdMatMapper.java b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/sc/md/mapper/MdMatMapper.java
new file mode 100644
index 0000000..0c41b40
--- /dev/null
+++ b/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> {
+
+}
diff --git a/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/sc/md/mapper/MdMatTypeMapper.java b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/sc/md/mapper/MdMatTypeMapper.java
new file mode 100644
index 0000000..2e962df
--- /dev/null
+++ b/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> {
+
+}
diff --git a/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/sc/md/service/IMdMatService.java b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/sc/md/service/IMdMatService.java
new file mode 100644
index 0000000..0b9c397
--- /dev/null
+++ b/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);
+}
diff --git a/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/sc/md/service/IMdMatTypeService.java b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/sc/md/service/IMdMatTypeService.java
new file mode 100644
index 0000000..ac89e41
--- /dev/null
+++ b/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);
+}
diff --git a/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/sc/md/service/impl/MdMatServiceImpl.java b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/sc/md/service/impl/MdMatServiceImpl.java
new file mode 100644
index 0000000..1575845
--- /dev/null
+++ b/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();
+ }
+}
diff --git a/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/sc/md/service/impl/MdMatTypeServiceImpl.java b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/sc/md/service/impl/MdMatTypeServiceImpl.java
new file mode 100644
index 0000000..8a43315
--- /dev/null
+++ b/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;
+ }
+}
diff --git a/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/sc/test b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/sc/test
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/sc/test
diff --git a/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/resources/mapper/qa/qm/QmMatcheckMapper.xml b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/resources/mapper/qa/qm/QmMatcheckMapper.xml
new file mode 100644
index 0000000..180a1a7
--- /dev/null
+++ b/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>
diff --git a/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/resources/mapper/sc/md/MdMatMapper.xml b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/resources/mapper/sc/md/MdMatMapper.xml
new file mode 100644
index 0000000..afb20ab
--- /dev/null
+++ b/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>
diff --git a/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/resources/mapper/sc/md/MdMatTypeMapper.xml b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/resources/mapper/sc/md/MdMatTypeMapper.xml
new file mode 100644
index 0000000..5659381
--- /dev/null
+++ b/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>
diff --git a/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/resources/mapper/sc/test b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/resources/mapper/sc/test
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/resources/mapper/sc/test
diff --git a/ruoyi-plus-soybean/src/service/api/md/mat-type.ts b/ruoyi-plus-soybean/src/service/api/md/mat-type.ts
new file mode 100644
index 0000000..da2cdd3
--- /dev/null
+++ b/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'
+ });
+}
diff --git a/ruoyi-plus-soybean/src/service/api/md/mat.ts b/ruoyi-plus-soybean/src/service/api/md/mat.ts
new file mode 100644
index 0000000..dcd6389
--- /dev/null
+++ b/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'
+ });
+}
diff --git a/ruoyi-plus-soybean/src/service/api/qm/matcheck.ts b/ruoyi-plus-soybean/src/service/api/qm/matcheck.ts
new file mode 100644
index 0000000..8353c54
--- /dev/null
+++ b/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'
+ });
+}
diff --git a/ruoyi-plus-soybean/src/typings/api/md.mat-type.api.d.ts b/ruoyi-plus-soybean/src/typings/api/md.mat-type.api.d.ts
new file mode 100644
index 0000000..aa4ccc5
--- /dev/null
+++ b/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>;
+ }
+}
diff --git a/ruoyi-plus-soybean/src/typings/api/md.mat.api.d.ts b/ruoyi-plus-soybean/src/typings/api/md.mat.api.d.ts
new file mode 100644
index 0000000..5ed69f4
--- /dev/null
+++ b/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;
+ /** 鐗╂枡浠g爜 */
+ 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>;
+ }
+}
diff --git a/ruoyi-plus-soybean/src/typings/api/qm.matcheck.api.d.ts b/ruoyi-plus-soybean/src/typings/api/qm.matcheck.api.d.ts
new file mode 100644
index 0000000..c340104
--- /dev/null
+++ b/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;
+ /** 妫�楠岄」鐩瓹OD */
+ 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-鏈笂浼爉es 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 & {
+ /** 妫�楠岄」涓嬫媺绛夋帴鍙d娇鐢ㄧ殑鍒ゅ畾瑙勭▼ 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;
+ };
+ }
+}
diff --git a/ruoyi-plus-soybean/src/views/md/mat-type/index.vue b/ruoyi-plus-soybean/src/views/md/mat-type/index.vue
new file mode 100644
index 0000000..c3fdd16
--- /dev/null
+++ b/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>
diff --git a/ruoyi-plus-soybean/src/views/md/mat-type/modules/mat-type-operate-drawer.vue b/ruoyi-plus-soybean/src/views/md/mat-type/modules/mat-type-operate-drawer.vue
new file mode 100644
index 0000000..c46a545
--- /dev/null
+++ b/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="璇疯緭鍏es缂栫爜" />
+ </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>
diff --git a/ruoyi-plus-soybean/src/views/md/mat-type/modules/mat-type-search.vue b/ruoyi-plus-soybean/src/views/md/mat-type/modules/mat-type-search.vue
new file mode 100644
index 0000000..a55c423
--- /dev/null
+++ b/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="璇疯緭鍏es缂栫爜" />
+ </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>
diff --git a/ruoyi-plus-soybean/src/views/md/mat/index.vue b/ruoyi-plus-soybean/src/views/md/mat/index.vue
new file mode 100644
index 0000000..b292a2e
--- /dev/null
+++ b/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: '鐗╂枡浠g爜',
+ 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>
diff --git a/ruoyi-plus-soybean/src/views/md/mat/modules/mat-operate-drawer.vue b/ruoyi-plus-soybean/src/views/md/mat/modules/mat-operate-drawer.vue
new file mode 100644
index 0000000..4977e2b
--- /dev/null
+++ b/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="鐗╂枡浠g爜" 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>
diff --git a/ruoyi-plus-soybean/src/views/md/mat/modules/mat-search.vue b/ruoyi-plus-soybean/src/views/md/mat/modules/mat-search.vue
new file mode 100644
index 0000000..36f9d22
--- /dev/null
+++ b/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="鐗╂枡浠g爜" 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>
diff --git a/ruoyi-plus-soybean/src/views/qm/judge-details/modules/judge-details-sub-table.vue b/ruoyi-plus-soybean/src/views/qm/judge-details/modules/judge-details-sub-table.vue
new file mode 100644
index 0000000..67101a2
--- /dev/null
+++ b/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>
diff --git a/ruoyi-plus-soybean/src/views/qm/matcheck/index.vue b/ruoyi-plus-soybean/src/views/qm/matcheck/index.vue
new file mode 100644
index 0000000..6203c03
--- /dev/null
+++ b/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>
diff --git a/ruoyi-plus-soybean/src/views/qm/matcheck/modules/matcheck-operate-drawer.vue b/ruoyi-plus-soybean/src/views/qm/matcheck/modules/matcheck-operate-drawer.vue
new file mode 100644
index 0000000..8115cdf
--- /dev/null
+++ b/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="璇疯緭鍏D鍊�" class="w-full" />
+ </NFormItem>
+ <NFormItem label="CV鍊�" path="cvval">
+ <NInputNumber v-model:value="model.cvval" placeholder="璇疯緭鍏V鍊�" class="w-full" />
+ </NFormItem>
+ <NFormItem label="CPK鍊�" path="cpkval">
+ <NInputNumber v-model:value="model.cpkval" placeholder="璇疯緭鍏PK鍊�" 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>
diff --git a/ruoyi-plus-soybean/src/views/qm/matcheck/modules/matcheck-search.vue b/ruoyi-plus-soybean/src/views/qm/matcheck/modules/matcheck-search.vue
new file mode 100644
index 0000000..bc73db1
--- /dev/null
+++ b/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>
--
Gitblit v1.9.3