From 3471290659516cf21db3211a9053daff5f283e03 Mon Sep 17 00:00:00 2001
From: zhuguifei <312353457@qq.com>
Date: 星期五, 20 三月 2026 15:50:18 +0800
Subject: [PATCH] feat: 基础数据仪器管理、判定依据、判定依据明细
---
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/qm/domain/QmJudgeDetails.java | 100 +
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/qm/mapper/QmJudgeMapper.java | 15
ruoyi-plus-soybean/src/router/elegant/routes.ts | 27
ruoyi-plus-soybean/src/typings/api/qm.judge-details.api.d.ts | 85 +
ruoyi-plus-soybean/src/typings/api/qm.judge.api.d.ts | 77 +
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/md/service/impl/MdInstrumentServiceImpl.java | 133 ++
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/qm/domain/bo/QmJudgeDetailsBo.java | 99 +
ruoyi-plus-soybean/src/service/api/md/instrument.ts | 35
ruoyi-plus-soybean/src/service/api/qm/judge-details.ts | 35
ruoyi-plus-soybean/src/views/qm/judge/index.vue | 323 ++++
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/md/domain/MdInstrument.java | 69 +
ruoyi-plus-soybean/src/views/md/instrument/index.vue | 247 +++
ruoyi-plus-soybean/src/service/api/qm/judge.ts | 35
ruoyi-plus-soybean/.gitignore | 2
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/md/controller/MdInstrumentController.java | 105 +
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/qm/service/impl/QmJudgeServiceImpl.java | 174 ++
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/resources/mapper/qa/qm/QmJudgeDetailsMapper.xml | 6
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/qm/domain/vo/QmJudgeVo.java | 114 +
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/md/mapper/MdInstrumentMapper.java | 15
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/md/domain/vo/MdInstrumentVo.java | 87 +
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/qm/controller/QmJudgeDetailsController.java | 105 +
ruoyi-plus-soybean/src/views/qm/judge/modules/judge-operate-drawer.vue | 212 +++
ruoyi-plus-soybean/src/views/qm/judge-details/modules/judge-details-operate-drawer.vue | 175 ++
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/qm/domain/vo/QmJudgeDetailsVo.java | 116 +
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/qm/service/IQmJudgeService.java | 68 +
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/resources/mapper/qa/md/MdInstrumentMapper.xml | 6
ruoyi-plus-soybean/src/views/qm/judge/modules/judge-search.vue | 98 +
ruoyi-plus-soybean/src/views/qm/judge-details/modules/judge-details-search.vue | 83 +
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/md/domain/bo/MdInstrumentBo.java | 69 +
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/qm/controller/QmJudgeController.java | 105 +
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/qm/mapper/QmJudgeDetailsMapper.java | 15
ruoyi-plus-soybean/src/views/md/instrument/modules/instrument-search.vue | 82 +
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/qm/service/IQmJudgeDetailsService.java | 68 +
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/qm/domain/bo/QmJudgeBo.java | 81 +
ruoyi-plus-soybean/src/router/elegant/imports.ts | 3
ruoyi-plus-soybean/src/typings/elegant-router.d.ts | 6
ruoyi-plus-soybean/src/views/qm/judge-details/index.vue | 262 ++++
ruoyi-plus-soybean/src/typings/api/md.instrument.api.d.ts | 65 +
ruoyi-plus-soybean/src/views/qm/checkitem/modules/checkitem-operate-drawer.vue | 36
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/md/service/IMdInstrumentService.java | 68 +
ruoyi-plus-soybean/src/views/md/instrument/modules/instrument-operate-drawer.vue | 157 ++
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/qm/domain/QmJudge.java | 92 +
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/qm/service/impl/QmJudgeDetailsServiceImpl.java | 135 ++
ruoyi-plus-soybean/src/router/elegant/transform.ts | 3
44 files changed, 3,892 insertions(+), 1 deletions(-)
diff --git a/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/md/controller/MdInstrumentController.java b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/md/controller/MdInstrumentController.java
new file mode 100644
index 0000000..df68e72
--- /dev/null
+++ b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/md/controller/MdInstrumentController.java
@@ -0,0 +1,105 @@
+package org.dromara.qa.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.qa.md.domain.vo.MdInstrumentVo;
+import org.dromara.qa.md.domain.bo.MdInstrumentBo;
+import org.dromara.qa.md.service.IMdInstrumentService;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+
+/**
+ * 娴嬮噺浠櫒
+ *
+ * @author zhuguifei
+ * @date 2026-03-18
+ */
+@Validated
+@RequiredArgsConstructor
+@RestController
+@RequestMapping("/md/instrument")
+public class MdInstrumentController extends BaseController {
+
+ private final IMdInstrumentService mdInstrumentService;
+
+ /**
+ * 鏌ヨ娴嬮噺浠櫒鍒楄〃
+ */
+ @SaCheckPermission("md:instrument:list")
+ @GetMapping("/list")
+ public TableDataInfo<MdInstrumentVo> list(MdInstrumentBo bo, PageQuery pageQuery) {
+ return mdInstrumentService.queryPageList(bo, pageQuery);
+ }
+
+ /**
+ * 瀵煎嚭娴嬮噺浠櫒鍒楄〃
+ */
+ @SaCheckPermission("md:instrument:export")
+ @Log(title = "娴嬮噺浠櫒", businessType = BusinessType.EXPORT)
+ @PostMapping("/export")
+ public void export(MdInstrumentBo bo, HttpServletResponse response) {
+ List<MdInstrumentVo> list = mdInstrumentService.queryList(bo);
+ ExcelUtil.exportExcel(list, "娴嬮噺浠櫒", MdInstrumentVo.class, response);
+ }
+
+ /**
+ * 鑾峰彇娴嬮噺浠櫒璇︾粏淇℃伅
+ *
+ * @param id 涓婚敭
+ */
+ @SaCheckPermission("md:instrument:query")
+ @GetMapping("/{id}")
+ public R<MdInstrumentVo> getInfo(@NotNull(message = "涓婚敭涓嶈兘涓虹┖")
+ @PathVariable String id) {
+ return R.ok(mdInstrumentService.queryById(id));
+ }
+
+ /**
+ * 鏂板娴嬮噺浠櫒
+ */
+ @SaCheckPermission("md:instrument:add")
+ @Log(title = "娴嬮噺浠櫒", businessType = BusinessType.INSERT)
+ @RepeatSubmit()
+ @PostMapping()
+ public R<Void> add(@Validated(AddGroup.class) @RequestBody MdInstrumentBo bo) {
+ return toAjax(mdInstrumentService.insertByBo(bo));
+ }
+
+ /**
+ * 淇敼娴嬮噺浠櫒
+ */
+ @SaCheckPermission("md:instrument:edit")
+ @Log(title = "娴嬮噺浠櫒", businessType = BusinessType.UPDATE)
+ @RepeatSubmit()
+ @PutMapping()
+ public R<Void> edit(@Validated(EditGroup.class) @RequestBody MdInstrumentBo bo) {
+ return toAjax(mdInstrumentService.updateByBo(bo));
+ }
+
+ /**
+ * 鍒犻櫎娴嬮噺浠櫒
+ *
+ * @param ids 涓婚敭涓�
+ */
+ @SaCheckPermission("md:instrument:remove")
+ @Log(title = "娴嬮噺浠櫒", businessType = BusinessType.DELETE)
+ @DeleteMapping("/{ids}")
+ public R<Void> remove(@NotEmpty(message = "涓婚敭涓嶈兘涓虹┖")
+ @PathVariable String[] ids) {
+ return toAjax(mdInstrumentService.deleteWithValidByIds(List.of(ids), true));
+ }
+}
diff --git a/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/md/domain/MdInstrument.java b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/md/domain/MdInstrument.java
new file mode 100644
index 0000000..7ee4aae
--- /dev/null
+++ b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/md/domain/MdInstrument.java
@@ -0,0 +1,69 @@
+package org.dromara.qa.md.domain;
+
+import org.dromara.common.mybatis.core.domain.BaseEntity;
+import com.baomidou.mybatisplus.annotation.*;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.io.Serial;
+
+/**
+ * 娴嬮噺浠櫒瀵硅薄 md_instrument
+ *
+ * @author zhuguifei
+ * @date 2026-03-18
+ */
+@Data
+@TableName("md_instrument")
+public class MdInstrument {
+
+ @Serial
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * 缂栫爜
+ */
+ private String id;
+
+ /**
+ * 浠櫒浠g爜
+ */
+ private String instrumentCode;
+
+ /**
+ * 浠櫒鍚嶇О
+ */
+ private String instrumentName;
+
+ /**
+ * 鎺ュ彛鏍囧噯
+ */
+ private String ifStd;
+
+ /**
+ * 鎺ュ彛鏍囧噯浠g爜
+ */
+ private Long isc;
+
+ /**
+ * 鎵�灞炶溅闂�
+ */
+ private String workShop;
+
+ /**
+ * 鍚敤
+ */
+ private Long enable;
+
+ /**
+ * 鍒犻櫎
+ */
+ private Long del;
+
+ /**
+ * 澶囨敞
+ */
+ private String instrumentDes;
+
+
+}
diff --git a/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/md/domain/bo/MdInstrumentBo.java b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/md/domain/bo/MdInstrumentBo.java
new file mode 100644
index 0000000..02607d4
--- /dev/null
+++ b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/md/domain/bo/MdInstrumentBo.java
@@ -0,0 +1,69 @@
+package org.dromara.qa.md.domain.bo;
+
+import org.dromara.qa.md.domain.MdInstrument;
+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.*;
+
+/**
+ * 娴嬮噺浠櫒涓氬姟瀵硅薄 md_instrument
+ *
+ * @author zhuguifei
+ * @date 2026-03-18
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@AutoMapper(target = MdInstrument.class, reverseConvertGenerate = false)
+public class MdInstrumentBo extends BaseEntity {
+
+ /**
+ * 缂栫爜
+ */
+ private String id;
+
+ /**
+ * 浠櫒浠g爜
+ */
+ private String instrumentCode;
+
+ /**
+ * 浠櫒鍚嶇О
+ */
+ private String instrumentName;
+
+ /**
+ * 鎺ュ彛鏍囧噯
+ */
+ private String ifStd;
+
+ /**
+ * 鎺ュ彛鏍囧噯浠g爜
+ */
+ private Long isc;
+
+ /**
+ * 鎵�灞炶溅闂�
+ */
+ private String workShop;
+
+ /**
+ * 鍚敤
+ */
+ private Long enable;
+
+ /**
+ * 鍒犻櫎
+ */
+ private Long del;
+
+ /**
+ * 澶囨敞
+ */
+ private String instrumentDes;
+
+
+}
diff --git a/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/md/domain/vo/MdInstrumentVo.java b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/md/domain/vo/MdInstrumentVo.java
new file mode 100644
index 0000000..ea2e7fa
--- /dev/null
+++ b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/md/domain/vo/MdInstrumentVo.java
@@ -0,0 +1,87 @@
+package org.dromara.qa.md.domain.vo;
+
+import org.dromara.qa.md.domain.MdInstrument;
+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_instrument
+ *
+ * @author zhuguifei
+ * @date 2026-03-18
+ */
+@Data
+@ExcelIgnoreUnannotated
+@AutoMapper(target = MdInstrument.class)
+public class MdInstrumentVo implements Serializable {
+
+ @Serial
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * 缂栫爜
+ */
+ @ExcelProperty(value = "缂栫爜")
+ private String id;
+
+ /**
+ * 浠櫒浠g爜
+ */
+ @ExcelProperty(value = "浠櫒浠g爜")
+ private String instrumentCode;
+
+ /**
+ * 浠櫒鍚嶇О
+ */
+ @ExcelProperty(value = "浠櫒鍚嶇О")
+ private String instrumentName;
+
+ /**
+ * 鎺ュ彛鏍囧噯
+ */
+ @ExcelProperty(value = "鎺ュ彛鏍囧噯")
+ private String ifStd;
+
+ /**
+ * 鎺ュ彛鏍囧噯浠g爜
+ */
+ @ExcelProperty(value = "鎺ュ彛鏍囧噯浠g爜")
+ private Long isc;
+
+ /**
+ * 鎵�灞炶溅闂�
+ */
+ @ExcelProperty(value = "鎵�灞炶溅闂�")
+ private String workShop;
+
+ /**
+ * 鍚敤
+ */
+ @ExcelProperty(value = "鍚敤", converter = ExcelDictConvert.class)
+ @ExcelDictFormat(dictType = "sys_yes_no")
+ private Long enable;
+
+ /**
+ * 鍒犻櫎
+ */
+ @ExcelProperty(value = "鍒犻櫎")
+ private Long del;
+
+ /**
+ * 澶囨敞
+ */
+ @ExcelProperty(value = "澶囨敞")
+ private String instrumentDes;
+
+
+}
diff --git a/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/md/mapper/MdInstrumentMapper.java b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/md/mapper/MdInstrumentMapper.java
new file mode 100644
index 0000000..36f5d42
--- /dev/null
+++ b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/md/mapper/MdInstrumentMapper.java
@@ -0,0 +1,15 @@
+package org.dromara.qa.md.mapper;
+
+import org.dromara.qa.md.domain.MdInstrument;
+import org.dromara.qa.md.domain.vo.MdInstrumentVo;
+import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
+
+/**
+ * 娴嬮噺浠櫒Mapper鎺ュ彛
+ *
+ * @author zhuguifei
+ * @date 2026-03-18
+ */
+public interface MdInstrumentMapper extends BaseMapperPlus<MdInstrument, MdInstrumentVo> {
+
+}
diff --git a/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/md/service/IMdInstrumentService.java b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/md/service/IMdInstrumentService.java
new file mode 100644
index 0000000..bd3ce83
--- /dev/null
+++ b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/md/service/IMdInstrumentService.java
@@ -0,0 +1,68 @@
+package org.dromara.qa.md.service;
+
+import org.dromara.qa.md.domain.vo.MdInstrumentVo;
+import org.dromara.qa.md.domain.bo.MdInstrumentBo;
+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-03-18
+ */
+public interface IMdInstrumentService {
+
+ /**
+ * 鏌ヨ娴嬮噺浠櫒
+ *
+ * @param id 涓婚敭
+ * @return 娴嬮噺浠櫒
+ */
+ MdInstrumentVo queryById(String id);
+
+ /**
+ * 鍒嗛〉鏌ヨ娴嬮噺浠櫒鍒楄〃
+ *
+ * @param bo 鏌ヨ鏉′欢
+ * @param pageQuery 鍒嗛〉鍙傛暟
+ * @return 娴嬮噺浠櫒鍒嗛〉鍒楄〃
+ */
+ TableDataInfo<MdInstrumentVo> queryPageList(MdInstrumentBo bo, PageQuery pageQuery);
+
+ /**
+ * 鏌ヨ绗﹀悎鏉′欢鐨勬祴閲忎华鍣ㄥ垪琛�
+ *
+ * @param bo 鏌ヨ鏉′欢
+ * @return 娴嬮噺浠櫒鍒楄〃
+ */
+ List<MdInstrumentVo> queryList(MdInstrumentBo bo);
+
+ /**
+ * 鏂板娴嬮噺浠櫒
+ *
+ * @param bo 娴嬮噺浠櫒
+ * @return 鏄惁鏂板鎴愬姛
+ */
+ Boolean insertByBo(MdInstrumentBo bo);
+
+ /**
+ * 淇敼娴嬮噺浠櫒
+ *
+ * @param bo 娴嬮噺浠櫒
+ * @return 鏄惁淇敼鎴愬姛
+ */
+ Boolean updateByBo(MdInstrumentBo 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/qa/md/service/impl/MdInstrumentServiceImpl.java b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/md/service/impl/MdInstrumentServiceImpl.java
new file mode 100644
index 0000000..03c917e
--- /dev/null
+++ b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/md/service/impl/MdInstrumentServiceImpl.java
@@ -0,0 +1,133 @@
+package org.dromara.qa.md.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.md.domain.bo.MdInstrumentBo;
+import org.dromara.qa.md.domain.vo.MdInstrumentVo;
+import org.dromara.qa.md.domain.MdInstrument;
+import org.dromara.qa.md.mapper.MdInstrumentMapper;
+import org.dromara.qa.md.service.IMdInstrumentService;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Collection;
+
+/**
+ * 娴嬮噺浠櫒Service涓氬姟灞傚鐞�
+ *
+ * @author zhuguifei
+ * @date 2026-03-18
+ */
+@Slf4j
+@RequiredArgsConstructor
+@Service
+public class MdInstrumentServiceImpl implements IMdInstrumentService {
+
+ private final MdInstrumentMapper baseMapper;
+
+ /**
+ * 鏌ヨ娴嬮噺浠櫒
+ *
+ * @param id 涓婚敭
+ * @return 娴嬮噺浠櫒
+ */
+ @Override
+ public MdInstrumentVo queryById(String id){
+ return baseMapper.selectVoById(id);
+ }
+
+ /**
+ * 鍒嗛〉鏌ヨ娴嬮噺浠櫒鍒楄〃
+ *
+ * @param bo 鏌ヨ鏉′欢
+ * @param pageQuery 鍒嗛〉鍙傛暟
+ * @return 娴嬮噺浠櫒鍒嗛〉鍒楄〃
+ */
+ @Override
+ public TableDataInfo<MdInstrumentVo> queryPageList(MdInstrumentBo bo, PageQuery pageQuery) {
+ LambdaQueryWrapper<MdInstrument> lqw = buildQueryWrapper(bo);
+ Page<MdInstrumentVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
+ return TableDataInfo.build(result);
+ }
+
+ /**
+ * 鏌ヨ绗﹀悎鏉′欢鐨勬祴閲忎华鍣ㄥ垪琛�
+ *
+ * @param bo 鏌ヨ鏉′欢
+ * @return 娴嬮噺浠櫒鍒楄〃
+ */
+ @Override
+ public List<MdInstrumentVo> queryList(MdInstrumentBo bo) {
+ LambdaQueryWrapper<MdInstrument> lqw = buildQueryWrapper(bo);
+ return baseMapper.selectVoList(lqw);
+ }
+
+ private LambdaQueryWrapper<MdInstrument> buildQueryWrapper(MdInstrumentBo bo) {
+ Map<String, Object> params = bo.getParams();
+ LambdaQueryWrapper<MdInstrument> lqw = Wrappers.lambdaQuery();
+ lqw.like(StringUtils.isNotBlank(bo.getInstrumentCode()), MdInstrument::getInstrumentCode, bo.getInstrumentCode());
+ lqw.like(StringUtils.isNotBlank(bo.getInstrumentName()), MdInstrument::getInstrumentName, bo.getInstrumentName());
+ lqw.eq(bo.getEnable() != null, MdInstrument::getEnable, bo.getEnable());
+ return lqw;
+ }
+
+ /**
+ * 鏂板娴嬮噺浠櫒
+ *
+ * @param bo 娴嬮噺浠櫒
+ * @return 鏄惁鏂板鎴愬姛
+ */
+ @Override
+ public Boolean insertByBo(MdInstrumentBo bo) {
+ MdInstrument add = MapstructUtils.convert(bo, MdInstrument.class);
+ validEntityBeforeSave(add);
+ boolean flag = baseMapper.insert(add) > 0;
+ if (flag) {
+ bo.setId(add.getId());
+ }
+ return flag;
+ }
+
+ /**
+ * 淇敼娴嬮噺浠櫒
+ *
+ * @param bo 娴嬮噺浠櫒
+ * @return 鏄惁淇敼鎴愬姛
+ */
+ @Override
+ public Boolean updateByBo(MdInstrumentBo bo) {
+ MdInstrument update = MapstructUtils.convert(bo, MdInstrument.class);
+ validEntityBeforeSave(update);
+ return baseMapper.updateById(update) > 0;
+ }
+
+ /**
+ * 淇濆瓨鍓嶇殑鏁版嵁鏍¢獙
+ */
+ private void validEntityBeforeSave(MdInstrument 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/qa/qm/controller/QmJudgeController.java b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/qm/controller/QmJudgeController.java
new file mode 100644
index 0000000..ec936c9
--- /dev/null
+++ b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/qm/controller/QmJudgeController.java
@@ -0,0 +1,105 @@
+package org.dromara.qa.qm.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.qa.qm.domain.vo.QmJudgeVo;
+import org.dromara.qa.qm.domain.bo.QmJudgeBo;
+import org.dromara.qa.qm.service.IQmJudgeService;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+
+/**
+ * 鍒ゅ畾渚濇嵁
+ *
+ * @author zhuguifei
+ * @date 2026-03-18
+ */
+@Validated
+@RequiredArgsConstructor
+@RestController
+@RequestMapping("/qm/judge")
+public class QmJudgeController extends BaseController {
+
+ private final IQmJudgeService qmJudgeService;
+
+ /**
+ * 鏌ヨ鍒ゅ畾渚濇嵁鍒楄〃
+ */
+ @SaCheckPermission("qm:judge:list")
+ @GetMapping("/list")
+ public TableDataInfo<QmJudgeVo> list(QmJudgeBo bo, PageQuery pageQuery) {
+ return qmJudgeService.queryPageList(bo, pageQuery);
+ }
+
+ /**
+ * 瀵煎嚭鍒ゅ畾渚濇嵁鍒楄〃
+ */
+ @SaCheckPermission("qm:judge:export")
+ @Log(title = "鍒ゅ畾渚濇嵁", businessType = BusinessType.EXPORT)
+ @PostMapping("/export")
+ public void export(QmJudgeBo bo, HttpServletResponse response) {
+ List<QmJudgeVo> list = qmJudgeService.queryList(bo);
+ ExcelUtil.exportExcel(list, "鍒ゅ畾渚濇嵁", QmJudgeVo.class, response);
+ }
+
+ /**
+ * 鑾峰彇鍒ゅ畾渚濇嵁璇︾粏淇℃伅
+ *
+ * @param id 涓婚敭
+ */
+ @SaCheckPermission("qm:judge:query")
+ @GetMapping("/{id}")
+ public R<QmJudgeVo> getInfo(@NotNull(message = "涓婚敭涓嶈兘涓虹┖")
+ @PathVariable String id) {
+ return R.ok(qmJudgeService.queryById(id));
+ }
+
+ /**
+ * 鏂板鍒ゅ畾渚濇嵁
+ */
+ @SaCheckPermission("qm:judge:add")
+ @Log(title = "鍒ゅ畾渚濇嵁", businessType = BusinessType.INSERT)
+ @RepeatSubmit()
+ @PostMapping()
+ public R<Void> add(@Validated(AddGroup.class) @RequestBody QmJudgeBo bo) {
+ return toAjax(qmJudgeService.insertByBo(bo));
+ }
+
+ /**
+ * 淇敼鍒ゅ畾渚濇嵁
+ */
+ @SaCheckPermission("qm:judge:edit")
+ @Log(title = "鍒ゅ畾渚濇嵁", businessType = BusinessType.UPDATE)
+ @RepeatSubmit()
+ @PutMapping()
+ public R<Void> edit(@Validated(EditGroup.class) @RequestBody QmJudgeBo bo) {
+ return toAjax(qmJudgeService.updateByBo(bo));
+ }
+
+ /**
+ * 鍒犻櫎鍒ゅ畾渚濇嵁
+ *
+ * @param ids 涓婚敭涓�
+ */
+ @SaCheckPermission("qm:judge:remove")
+ @Log(title = "鍒ゅ畾渚濇嵁", businessType = BusinessType.DELETE)
+ @DeleteMapping("/{ids}")
+ public R<Void> remove(@NotEmpty(message = "涓婚敭涓嶈兘涓虹┖")
+ @PathVariable String[] ids) {
+ return toAjax(qmJudgeService.deleteWithValidByIds(List.of(ids), true));
+ }
+}
diff --git a/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/qm/controller/QmJudgeDetailsController.java b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/qm/controller/QmJudgeDetailsController.java
new file mode 100644
index 0000000..c58d4d6
--- /dev/null
+++ b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/qm/controller/QmJudgeDetailsController.java
@@ -0,0 +1,105 @@
+package org.dromara.qa.qm.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.qa.qm.domain.vo.QmJudgeDetailsVo;
+import org.dromara.qa.qm.domain.bo.QmJudgeDetailsBo;
+import org.dromara.qa.qm.service.IQmJudgeDetailsService;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+
+/**
+ * 鍒ゅ畾妯℃澘鏄庣粏
+ *
+ * @author 鏈辨椋�
+ * @date 2026-03-20
+ */
+@Validated
+@RequiredArgsConstructor
+@RestController
+@RequestMapping("/qm/judgeDetails")
+public class QmJudgeDetailsController extends BaseController {
+
+ private final IQmJudgeDetailsService qmJudgeDetailsService;
+
+ /**
+ * 鏌ヨ鍒ゅ畾妯℃澘鏄庣粏鍒楄〃
+ */
+ @SaCheckPermission("qm:judgeDetails:list")
+ @GetMapping("/list")
+ public TableDataInfo<QmJudgeDetailsVo> list(QmJudgeDetailsBo bo, PageQuery pageQuery) {
+ return qmJudgeDetailsService.queryPageList(bo, pageQuery);
+ }
+
+ /**
+ * 瀵煎嚭鍒ゅ畾妯℃澘鏄庣粏鍒楄〃
+ */
+ @SaCheckPermission("qm:judgeDetails:export")
+ @Log(title = "鍒ゅ畾妯℃澘鏄庣粏", businessType = BusinessType.EXPORT)
+ @PostMapping("/export")
+ public void export(QmJudgeDetailsBo bo, HttpServletResponse response) {
+ List<QmJudgeDetailsVo> list = qmJudgeDetailsService.queryList(bo);
+ ExcelUtil.exportExcel(list, "鍒ゅ畾妯℃澘鏄庣粏", QmJudgeDetailsVo.class, response);
+ }
+
+ /**
+ * 鑾峰彇鍒ゅ畾妯℃澘鏄庣粏璇︾粏淇℃伅
+ *
+ * @param id 涓婚敭
+ */
+ @SaCheckPermission("qm:judgeDetails:query")
+ @GetMapping("/{id}")
+ public R<QmJudgeDetailsVo> getInfo(@NotNull(message = "涓婚敭涓嶈兘涓虹┖")
+ @PathVariable String id) {
+ return R.ok(qmJudgeDetailsService.queryById(id));
+ }
+
+ /**
+ * 鏂板鍒ゅ畾妯℃澘鏄庣粏
+ */
+ @SaCheckPermission("qm:judgeDetails:add")
+ @Log(title = "鍒ゅ畾妯℃澘鏄庣粏", businessType = BusinessType.INSERT)
+ @RepeatSubmit()
+ @PostMapping()
+ public R<Void> add(@Validated(AddGroup.class) @RequestBody QmJudgeDetailsBo bo) {
+ return toAjax(qmJudgeDetailsService.insertByBo(bo));
+ }
+
+ /**
+ * 淇敼鍒ゅ畾妯℃澘鏄庣粏
+ */
+ @SaCheckPermission("qm:judgeDetails:edit")
+ @Log(title = "鍒ゅ畾妯℃澘鏄庣粏", businessType = BusinessType.UPDATE)
+ @RepeatSubmit()
+ @PutMapping()
+ public R<Void> edit(@Validated(EditGroup.class) @RequestBody QmJudgeDetailsBo bo) {
+ return toAjax(qmJudgeDetailsService.updateByBo(bo));
+ }
+
+ /**
+ * 鍒犻櫎鍒ゅ畾妯℃澘鏄庣粏
+ *
+ * @param ids 涓婚敭涓�
+ */
+ @SaCheckPermission("qm:judgeDetails:remove")
+ @Log(title = "鍒ゅ畾妯℃澘鏄庣粏", businessType = BusinessType.DELETE)
+ @DeleteMapping("/{ids}")
+ public R<Void> remove(@NotEmpty(message = "涓婚敭涓嶈兘涓虹┖")
+ @PathVariable String[] ids) {
+ return toAjax(qmJudgeDetailsService.deleteWithValidByIds(List.of(ids), true));
+ }
+}
diff --git a/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/qm/domain/QmJudge.java b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/qm/domain/QmJudge.java
new file mode 100644
index 0000000..0363a1d
--- /dev/null
+++ b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/qm/domain/QmJudge.java
@@ -0,0 +1,92 @@
+package org.dromara.qa.qm.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;
+
+/**
+ * 鍒ゅ畾渚濇嵁瀵硅薄 qm_judge
+ *
+ * @author zhuguifei
+ * @date 2026-03-18
+ */
+@Data
+@TableName("qm_judge")
+public class QmJudge {
+
+ @Serial
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * 缂栫爜
+ */
+ private String id;
+
+ /**
+ * 鐗╂枡鐗屽彿浠g爜
+ */
+ private String matCode;
+
+ /**
+ * 鐗╂枡鐗屽彿
+ */
+ private String matName;
+
+ /**
+ * 鐗堟湰鍙�
+ */
+ @Version
+ private Long version;
+
+ /**
+ * 鐘舵��
+ */
+ private Long status;
+
+ /**
+ * 鍒ゅ畾瑙勭▼COD
+ */
+ private String stdCod;
+
+ /**
+ * 鍒涘缓鏃堕棿
+ */
+ private Date cdate;
+
+ /**
+ * 鐢熸晥浜�/鎿嶄綔浜哄憳
+ */
+ private String oper;
+
+ /**
+ * 澶囨敞
+ */
+ private String des;
+
+ /**
+ * 鐗╂枡绫诲瀷
+ */
+ private Long category;
+
+ /**
+ * 杈呮枡绫诲瀷缂栫爜
+ */
+ private String typeCode;
+
+ /**
+ * 杈呮枡绫诲瀷鍚嶇О
+ */
+ private String typeName;
+
+ /**
+ * 鍒ゅ畾鍚嶇О
+ */
+ private String judgeName;
+
+
+}
diff --git a/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/qm/domain/QmJudgeDetails.java b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/qm/domain/QmJudgeDetails.java
new file mode 100644
index 0000000..1b07cdf
--- /dev/null
+++ b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/qm/domain/QmJudgeDetails.java
@@ -0,0 +1,100 @@
+package org.dromara.qa.qm.domain;
+
+import org.dromara.common.mybatis.core.domain.BaseEntity;
+import com.baomidou.mybatisplus.annotation.*;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.io.Serial;
+
+/**
+ * 鍒ゅ畾妯℃澘鏄庣粏瀵硅薄 qm_judge_details
+ *
+ * @author 鏈辨椋�
+ * @date 2026-03-20
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@TableName("qm_judge_details")
+public class QmJudgeDetails extends BaseEntity {
+
+ @Serial
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * 缂栫爜
+ */
+ private String id;
+
+ /**
+ * 鍒ゅ畾涓绘爣璇�
+ */
+ private String judgeId;
+
+ /**
+ * 鍒ゅ畾椤笽TEM
+ */
+ private String itemCod;
+
+ /**
+ * 鍒ゅ畾椤筃AME
+ */
+ private String itemName;
+
+ /**
+ * 鏍囧噯鍊�
+ */
+ private Long value3;
+
+ /**
+ * 鍒ゅ畾鍊�1
+ */
+ private Long value1;
+
+ /**
+ * 鍒ゅ畾鍊�2
+ */
+ private Long value2;
+
+ /**
+ * 缂洪櫡浣嶇疆
+ */
+ private String location;
+
+ /**
+ * 鍒ゅ畾绾у埆 (A,B,C,D)
+ */
+ private String cls;
+
+ /**
+ * 鍒嗗�兼爣鍑� (鎵e垎鏍囧噯锛屽緱鍒嗘爣鍑�),姣斿涓嶅悎鏍间竴娆℃墸澶氬皯鍒�
+ */
+ private Long stdscore;
+
+ /**
+ * 鏍囪姝ら」鏄惁涓哄悎鎴愰」鐩紝姣斿澶栬锛屽疄闄呬笂鍏宠仈浜嗗緢澶氬瓙椤圭洰
+ */
+ private Long ismix;
+
+ /**
+ * 鑻ユ瀛楁鏈塙UID鍊硷紝琛ㄦ槑瀹冨彲鑳戒负鍏朵粬椤圭洰鐨勫瓙椤癸紝姣斿鈥滅┖澶粹��,瀹冧负鐑熸敮澶栬椤圭洰鐨勫瓙椤�
+ */
+ private String rid;
+
+ /**
+ * 鑼冨洿-澶囩敤
+ */
+ private Long category;
+
+ /**
+ * 澶囨敞
+ */
+ private String decisionDes;
+
+ /**
+ * 淇敼浜�
+ */
+ private String updateUser;
+
+
+}
diff --git a/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/qm/domain/bo/QmJudgeBo.java b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/qm/domain/bo/QmJudgeBo.java
new file mode 100644
index 0000000..95b597b
--- /dev/null
+++ b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/qm/domain/bo/QmJudgeBo.java
@@ -0,0 +1,81 @@
+package org.dromara.qa.qm.domain.bo;
+
+import org.dromara.qa.qm.domain.QmJudge;
+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;
+
+/**
+ * 鍒ゅ畾渚濇嵁涓氬姟瀵硅薄 qm_judge
+ *
+ * @author zhuguifei
+ * @date 2026-03-18
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@AutoMapper(target = QmJudge.class, reverseConvertGenerate = false)
+public class QmJudgeBo extends BaseEntity {
+ private String id;
+ /**
+ * 鐗╂枡鐗屽彿浠g爜
+ */
+ private String matCode;
+
+ /**
+ * 鐗╂枡鐗屽彿
+ */
+ private String matName;
+
+ /**
+ * 鐘舵��
+ */
+ private Long status;
+
+ /**
+ * 鍒ゅ畾瑙勭▼COD
+ */
+ private String stdCod;
+
+ /**
+ * 鍒涘缓鏃堕棿
+ */
+ private Date cdate;
+
+ /**
+ * 鐢熸晥浜�/鎿嶄綔浜哄憳
+ */
+ private String oper;
+
+ /**
+ * 澶囨敞
+ */
+ private String des;
+
+ /**
+ * 鐗╂枡绫诲瀷
+ */
+ private Long category;
+
+ /**
+ * 杈呮枡绫诲瀷缂栫爜
+ */
+ private String typeCode;
+
+ /**
+ * 杈呮枡绫诲瀷鍚嶇О
+ */
+ private String typeName;
+
+ /**
+ * 鍒ゅ畾鍚嶇О
+ */
+ private String judgeName;
+
+
+}
diff --git a/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/qm/domain/bo/QmJudgeDetailsBo.java b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/qm/domain/bo/QmJudgeDetailsBo.java
new file mode 100644
index 0000000..9fd1f63
--- /dev/null
+++ b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/qm/domain/bo/QmJudgeDetailsBo.java
@@ -0,0 +1,99 @@
+package org.dromara.qa.qm.domain.bo;
+
+import org.dromara.qa.qm.domain.QmJudgeDetails;
+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.*;
+
+/**
+ * 鍒ゅ畾妯℃澘鏄庣粏涓氬姟瀵硅薄 qm_judge_details
+ *
+ * @author 鏈辨椋�
+ * @date 2026-03-20
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@AutoMapper(target = QmJudgeDetails.class, reverseConvertGenerate = false)
+public class QmJudgeDetailsBo extends BaseEntity {
+
+ /**
+ * 缂栫爜
+ */
+ private String id;
+
+ /**
+ * 鍒ゅ畾涓绘爣璇�
+ */
+ private String judgeId;
+
+ /**
+ * 鍒ゅ畾椤笽TEM
+ */
+ private String itemCod;
+
+ /**
+ * 鍒ゅ畾椤筃AME
+ */
+ private String itemName;
+
+ /**
+ * 鏍囧噯鍊�
+ */
+ private Long value3;
+
+ /**
+ * 鍒ゅ畾鍊�1
+ */
+ private Long value1;
+
+ /**
+ * 鍒ゅ畾鍊�2
+ */
+ private Long value2;
+
+ /**
+ * 缂洪櫡浣嶇疆
+ */
+ private String location;
+
+ /**
+ * 鍒ゅ畾绾у埆 (A,B,C,D)
+ */
+ private String cls;
+
+ /**
+ * 鍒嗗�兼爣鍑� (鎵e垎鏍囧噯锛屽緱鍒嗘爣鍑�),姣斿涓嶅悎鏍间竴娆℃墸澶氬皯鍒�
+ */
+ private Long stdscore;
+
+ /**
+ * 鏍囪姝ら」鏄惁涓哄悎鎴愰」鐩紝姣斿澶栬锛屽疄闄呬笂鍏宠仈浜嗗緢澶氬瓙椤圭洰
+ */
+ private Long ismix;
+
+ /**
+ * 鑻ユ瀛楁鏈塙UID鍊硷紝琛ㄦ槑瀹冨彲鑳戒负鍏朵粬椤圭洰鐨勫瓙椤癸紝姣斿鈥滅┖澶粹��,瀹冧负鐑熸敮澶栬椤圭洰鐨勫瓙椤�
+ */
+ private String rid;
+
+ /**
+ * 鑼冨洿-澶囩敤
+ */
+ private Long category;
+
+ /**
+ * 澶囨敞
+ */
+ private String decisionDes;
+
+ /**
+ * 淇敼浜�
+ */
+ private String updateUser;
+
+
+}
diff --git a/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/qm/domain/vo/QmJudgeDetailsVo.java b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/qm/domain/vo/QmJudgeDetailsVo.java
new file mode 100644
index 0000000..f005ed1
--- /dev/null
+++ b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/qm/domain/vo/QmJudgeDetailsVo.java
@@ -0,0 +1,116 @@
+package org.dromara.qa.qm.domain.vo;
+
+import org.dromara.qa.qm.domain.QmJudgeDetails;
+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;
+
+
+
+/**
+ * 鍒ゅ畾妯℃澘鏄庣粏瑙嗗浘瀵硅薄 qm_judge_details
+ *
+ * @author 鏈辨椋�
+ * @date 2026-03-20
+ */
+@Data
+@ExcelIgnoreUnannotated
+@AutoMapper(target = QmJudgeDetails.class)
+public class QmJudgeDetailsVo implements Serializable {
+
+ @Serial
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * 鍒ゅ畾涓绘爣璇�
+ */
+ @ExcelProperty(value = "鍒ゅ畾涓绘爣璇�")
+ private String judgeId;
+
+ /**
+ * 鍒ゅ畾椤笽TEM
+ */
+ @ExcelProperty(value = "鍒ゅ畾椤笽TEM")
+ private String itemCod;
+
+ /**
+ * 鍒ゅ畾椤筃AME
+ */
+ @ExcelProperty(value = "鍒ゅ畾椤筃AME")
+ private String itemName;
+
+ /**
+ * 鏍囧噯鍊�
+ */
+ @ExcelProperty(value = "鏍囧噯鍊�")
+ private Long value3;
+
+ /**
+ * 鍒ゅ畾鍊�1
+ */
+ @ExcelProperty(value = "鍒ゅ畾鍊�1")
+ private Long value1;
+
+ /**
+ * 鍒ゅ畾鍊�2
+ */
+ @ExcelProperty(value = "鍒ゅ畾鍊�2")
+ private Long value2;
+
+ /**
+ * 缂洪櫡浣嶇疆
+ */
+ @ExcelProperty(value = "缂洪櫡浣嶇疆")
+ private String location;
+
+ /**
+ * 鍒ゅ畾绾у埆 (A,B,C,D)
+ */
+ @ExcelProperty(value = "鍒ゅ畾绾у埆 (A,B,C,D)")
+ private String cls;
+
+ /**
+ * 鍒嗗�兼爣鍑� (鎵e垎鏍囧噯锛屽緱鍒嗘爣鍑�),姣斿涓嶅悎鏍间竴娆℃墸澶氬皯鍒�
+ */
+ @ExcelProperty(value = "鍒嗗�兼爣鍑� (鎵e垎鏍囧噯锛屽緱鍒嗘爣鍑�),姣斿涓嶅悎鏍间竴娆℃墸澶氬皯鍒�")
+ private Long stdscore;
+
+ /**
+ * 鏍囪姝ら」鏄惁涓哄悎鎴愰」鐩紝姣斿澶栬锛屽疄闄呬笂鍏宠仈浜嗗緢澶氬瓙椤圭洰
+ */
+ @ExcelProperty(value = "鏍囪姝ら」鏄惁涓哄悎鎴愰」鐩紝姣斿澶栬锛屽疄闄呬笂鍏宠仈浜嗗緢澶氬瓙椤圭洰")
+ private Long ismix;
+
+ /**
+ * 鑻ユ瀛楁鏈塙UID鍊硷紝琛ㄦ槑瀹冨彲鑳戒负鍏朵粬椤圭洰鐨勫瓙椤癸紝姣斿鈥滅┖澶粹��,瀹冧负鐑熸敮澶栬椤圭洰鐨勫瓙椤�
+ */
+ @ExcelProperty(value = "鑻ユ瀛楁鏈塙UID鍊硷紝琛ㄦ槑瀹冨彲鑳戒负鍏朵粬椤圭洰鐨勫瓙椤癸紝姣斿鈥滅┖澶粹��,瀹冧负鐑熸敮澶栬椤圭洰鐨勫瓙椤�")
+ private String rid;
+
+ /**
+ * 鑼冨洿-澶囩敤
+ */
+ @ExcelProperty(value = "鑼冨洿-澶囩敤")
+ private Long category;
+
+ /**
+ * 澶囨敞
+ */
+ @ExcelProperty(value = "澶囨敞")
+ private String decisionDes;
+
+ /**
+ * 淇敼浜�
+ */
+ @ExcelProperty(value = "淇敼浜�")
+ private String updateUser;
+
+
+}
diff --git a/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/qm/domain/vo/QmJudgeVo.java b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/qm/domain/vo/QmJudgeVo.java
new file mode 100644
index 0000000..e0fd2d8
--- /dev/null
+++ b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/qm/domain/vo/QmJudgeVo.java
@@ -0,0 +1,114 @@
+package org.dromara.qa.qm.domain.vo;
+
+import java.util.Date;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import org.dromara.common.translation.annotation.Translation;
+import org.dromara.common.translation.constant.TransConstant;
+import org.dromara.qa.qm.domain.QmJudge;
+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;
+
+
+
+/**
+ * 鍒ゅ畾渚濇嵁瑙嗗浘瀵硅薄 qm_judge
+ *
+ * @author zhuguifei
+ * @date 2026-03-18
+ */
+@Data
+@ExcelIgnoreUnannotated
+@AutoMapper(target = QmJudge.class)
+public class QmJudgeVo implements Serializable {
+
+ @Serial
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * 缂栫爜
+ */
+ @ExcelProperty(value = "缂栫爜")
+ private String id;
+
+ /**
+ * 鐗╂枡鐗屽彿浠g爜
+ */
+ @ExcelProperty(value = "鐗╂枡鐗屽彿浠g爜")
+ private String matCode;
+
+ /**
+ * 鐗╂枡鐗屽彿
+ */
+ @ExcelProperty(value = "鐗╂枡鐗屽彿")
+ private String matName;
+
+ /**
+ * 鐘舵��
+ */
+ @ExcelProperty(value = "鐘舵��")
+ private Long status;
+
+ /**
+ * 鍒ゅ畾瑙勭▼COD
+ */
+ @ExcelProperty(value = "鍒ゅ畾瑙勭▼COD")
+ private String stdCod;
+
+ /**
+ * 鍒ゅ畾瑙勭▼鍚嶇О
+ */
+ @ExcelProperty(value = "鍒ゅ畾瑙勭▼鍚嶇О")
+ private String stdName;
+
+ /**
+ * 鍒涘缓鏃堕棿
+ */
+ @ExcelProperty(value = "鍒涘缓鏃堕棿")
+ private Date cdate;
+
+ /**
+ * 鐢熸晥浜�/鎿嶄綔浜哄憳
+ */
+ @ExcelProperty(value = "鐢熸晥浜�/鎿嶄綔浜哄憳")
+ private String oper;
+
+ /**
+ * 澶囨敞
+ */
+ @ExcelProperty(value = "澶囨敞")
+ private String des;
+
+ /**
+ * 鐗╂枡绫诲瀷
+ */
+ @ExcelProperty(value = "鐗╂枡绫诲瀷")
+ private Long category;
+
+ /**
+ * 杈呮枡绫诲瀷缂栫爜
+ */
+ @ExcelProperty(value = "杈呮枡绫诲瀷缂栫爜")
+ private String typeCode;
+
+ /**
+ * 杈呮枡绫诲瀷鍚嶇О
+ */
+ @ExcelProperty(value = "杈呮枡绫诲瀷鍚嶇О")
+ private String typeName;
+
+ /**
+ * 鍒ゅ畾鍚嶇О
+ */
+ @ExcelProperty(value = "鍒ゅ畾鍚嶇О")
+ private String judgeName;
+
+
+}
diff --git a/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/qm/mapper/QmJudgeDetailsMapper.java b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/qm/mapper/QmJudgeDetailsMapper.java
new file mode 100644
index 0000000..8cae68a
--- /dev/null
+++ b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/qm/mapper/QmJudgeDetailsMapper.java
@@ -0,0 +1,15 @@
+package org.dromara.qa.qm.mapper;
+
+import org.dromara.qa.qm.domain.QmJudgeDetails;
+import org.dromara.qa.qm.domain.vo.QmJudgeDetailsVo;
+import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
+
+/**
+ * 鍒ゅ畾妯℃澘鏄庣粏Mapper鎺ュ彛
+ *
+ * @author 鏈辨椋�
+ * @date 2026-03-20
+ */
+public interface QmJudgeDetailsMapper extends BaseMapperPlus<QmJudgeDetails, QmJudgeDetailsVo> {
+
+}
diff --git a/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/qm/mapper/QmJudgeMapper.java b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/qm/mapper/QmJudgeMapper.java
new file mode 100644
index 0000000..221df76
--- /dev/null
+++ b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/qm/mapper/QmJudgeMapper.java
@@ -0,0 +1,15 @@
+package org.dromara.qa.qm.mapper;
+
+import org.dromara.qa.qm.domain.QmJudge;
+import org.dromara.qa.qm.domain.vo.QmJudgeVo;
+import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
+
+/**
+ * 鍒ゅ畾渚濇嵁Mapper鎺ュ彛
+ *
+ * @author zhuguifei
+ * @date 2026-03-18
+ */
+public interface QmJudgeMapper extends BaseMapperPlus<QmJudge, QmJudgeVo> {
+
+}
diff --git a/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/qm/service/IQmJudgeDetailsService.java b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/qm/service/IQmJudgeDetailsService.java
new file mode 100644
index 0000000..d6506ed
--- /dev/null
+++ b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/qm/service/IQmJudgeDetailsService.java
@@ -0,0 +1,68 @@
+package org.dromara.qa.qm.service;
+
+import org.dromara.qa.qm.domain.vo.QmJudgeDetailsVo;
+import org.dromara.qa.qm.domain.bo.QmJudgeDetailsBo;
+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 鏈辨椋�
+ * @date 2026-03-20
+ */
+public interface IQmJudgeDetailsService {
+
+ /**
+ * 鏌ヨ鍒ゅ畾妯℃澘鏄庣粏
+ *
+ * @param id 涓婚敭
+ * @return 鍒ゅ畾妯℃澘鏄庣粏
+ */
+ QmJudgeDetailsVo queryById(String id);
+
+ /**
+ * 鍒嗛〉鏌ヨ鍒ゅ畾妯℃澘鏄庣粏鍒楄〃
+ *
+ * @param bo 鏌ヨ鏉′欢
+ * @param pageQuery 鍒嗛〉鍙傛暟
+ * @return 鍒ゅ畾妯℃澘鏄庣粏鍒嗛〉鍒楄〃
+ */
+ TableDataInfo<QmJudgeDetailsVo> queryPageList(QmJudgeDetailsBo bo, PageQuery pageQuery);
+
+ /**
+ * 鏌ヨ绗﹀悎鏉′欢鐨勫垽瀹氭ā鏉挎槑缁嗗垪琛�
+ *
+ * @param bo 鏌ヨ鏉′欢
+ * @return 鍒ゅ畾妯℃澘鏄庣粏鍒楄〃
+ */
+ List<QmJudgeDetailsVo> queryList(QmJudgeDetailsBo bo);
+
+ /**
+ * 鏂板鍒ゅ畾妯℃澘鏄庣粏
+ *
+ * @param bo 鍒ゅ畾妯℃澘鏄庣粏
+ * @return 鏄惁鏂板鎴愬姛
+ */
+ Boolean insertByBo(QmJudgeDetailsBo bo);
+
+ /**
+ * 淇敼鍒ゅ畾妯℃澘鏄庣粏
+ *
+ * @param bo 鍒ゅ畾妯℃澘鏄庣粏
+ * @return 鏄惁淇敼鎴愬姛
+ */
+ Boolean updateByBo(QmJudgeDetailsBo 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/qa/qm/service/IQmJudgeService.java b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/qm/service/IQmJudgeService.java
new file mode 100644
index 0000000..9748a94
--- /dev/null
+++ b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/qm/service/IQmJudgeService.java
@@ -0,0 +1,68 @@
+package org.dromara.qa.qm.service;
+
+import org.dromara.qa.qm.domain.vo.QmJudgeVo;
+import org.dromara.qa.qm.domain.bo.QmJudgeBo;
+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-03-18
+ */
+public interface IQmJudgeService {
+
+ /**
+ * 鏌ヨ鍒ゅ畾渚濇嵁
+ *
+ * @param id 涓婚敭
+ * @return 鍒ゅ畾渚濇嵁
+ */
+ QmJudgeVo queryById(String id);
+
+ /**
+ * 鍒嗛〉鏌ヨ鍒ゅ畾渚濇嵁鍒楄〃
+ *
+ * @param bo 鏌ヨ鏉′欢
+ * @param pageQuery 鍒嗛〉鍙傛暟
+ * @return 鍒ゅ畾渚濇嵁鍒嗛〉鍒楄〃
+ */
+ TableDataInfo<QmJudgeVo> queryPageList(QmJudgeBo bo, PageQuery pageQuery);
+
+ /**
+ * 鏌ヨ绗﹀悎鏉′欢鐨勫垽瀹氫緷鎹垪琛�
+ *
+ * @param bo 鏌ヨ鏉′欢
+ * @return 鍒ゅ畾渚濇嵁鍒楄〃
+ */
+ List<QmJudgeVo> queryList(QmJudgeBo bo);
+
+ /**
+ * 鏂板鍒ゅ畾渚濇嵁
+ *
+ * @param bo 鍒ゅ畾渚濇嵁
+ * @return 鏄惁鏂板鎴愬姛
+ */
+ Boolean insertByBo(QmJudgeBo bo);
+
+ /**
+ * 淇敼鍒ゅ畾渚濇嵁
+ *
+ * @param bo 鍒ゅ畾渚濇嵁
+ * @return 鏄惁淇敼鎴愬姛
+ */
+ Boolean updateByBo(QmJudgeBo 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/qa/qm/service/impl/QmJudgeDetailsServiceImpl.java b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/qm/service/impl/QmJudgeDetailsServiceImpl.java
new file mode 100644
index 0000000..ccfb91d
--- /dev/null
+++ b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/qm/service/impl/QmJudgeDetailsServiceImpl.java
@@ -0,0 +1,135 @@
+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.QmJudgeDetailsBo;
+import org.dromara.qa.qm.domain.vo.QmJudgeDetailsVo;
+import org.dromara.qa.qm.domain.QmJudgeDetails;
+import org.dromara.qa.qm.mapper.QmJudgeDetailsMapper;
+import org.dromara.qa.qm.service.IQmJudgeDetailsService;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Collection;
+
+/**
+ * 鍒ゅ畾妯℃澘鏄庣粏Service涓氬姟灞傚鐞�
+ *
+ * @author 鏈辨椋�
+ * @date 2026-03-20
+ */
+@Slf4j
+@RequiredArgsConstructor
+@Service
+public class QmJudgeDetailsServiceImpl implements IQmJudgeDetailsService {
+
+ private final QmJudgeDetailsMapper baseMapper;
+
+ /**
+ * 鏌ヨ鍒ゅ畾妯℃澘鏄庣粏
+ *
+ * @param id 涓婚敭
+ * @return 鍒ゅ畾妯℃澘鏄庣粏
+ */
+ @Override
+ public QmJudgeDetailsVo queryById(String id){
+ return baseMapper.selectVoById(id);
+ }
+
+ /**
+ * 鍒嗛〉鏌ヨ鍒ゅ畾妯℃澘鏄庣粏鍒楄〃
+ *
+ * @param bo 鏌ヨ鏉′欢
+ * @param pageQuery 鍒嗛〉鍙傛暟
+ * @return 鍒ゅ畾妯℃澘鏄庣粏鍒嗛〉鍒楄〃
+ */
+ @Override
+ public TableDataInfo<QmJudgeDetailsVo> queryPageList(QmJudgeDetailsBo bo, PageQuery pageQuery) {
+ LambdaQueryWrapper<QmJudgeDetails> lqw = buildQueryWrapper(bo);
+ Page<QmJudgeDetailsVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
+ return TableDataInfo.build(result);
+ }
+
+ /**
+ * 鏌ヨ绗﹀悎鏉′欢鐨勫垽瀹氭ā鏉挎槑缁嗗垪琛�
+ *
+ * @param bo 鏌ヨ鏉′欢
+ * @return 鍒ゅ畾妯℃澘鏄庣粏鍒楄〃
+ */
+ @Override
+ public List<QmJudgeDetailsVo> queryList(QmJudgeDetailsBo bo) {
+ LambdaQueryWrapper<QmJudgeDetails> lqw = buildQueryWrapper(bo);
+ return baseMapper.selectVoList(lqw);
+ }
+
+ private LambdaQueryWrapper<QmJudgeDetails> buildQueryWrapper(QmJudgeDetailsBo bo) {
+ Map<String, Object> params = bo.getParams();
+ LambdaQueryWrapper<QmJudgeDetails> lqw = Wrappers.lambdaQuery();
+ lqw.eq(StringUtils.isNotBlank(bo.getJudgeId()), QmJudgeDetails::getJudgeId, bo.getJudgeId());
+ lqw.eq(StringUtils.isNotBlank(bo.getItemCod()), QmJudgeDetails::getItemCod, bo.getItemCod());
+ lqw.like(StringUtils.isNotBlank(bo.getItemName()), QmJudgeDetails::getItemName, bo.getItemName());
+ lqw.eq(StringUtils.isNotBlank(bo.getCls()), QmJudgeDetails::getCls, bo.getCls());
+ lqw.eq(bo.getIsmix() != null, QmJudgeDetails::getIsmix, bo.getIsmix());
+ return lqw;
+ }
+
+ /**
+ * 鏂板鍒ゅ畾妯℃澘鏄庣粏
+ *
+ * @param bo 鍒ゅ畾妯℃澘鏄庣粏
+ * @return 鏄惁鏂板鎴愬姛
+ */
+ @Override
+ public Boolean insertByBo(QmJudgeDetailsBo bo) {
+ QmJudgeDetails add = MapstructUtils.convert(bo, QmJudgeDetails.class);
+ validEntityBeforeSave(add);
+ boolean flag = baseMapper.insert(add) > 0;
+ if (flag) {
+ bo.setId(add.getId());
+ }
+ return flag;
+ }
+
+ /**
+ * 淇敼鍒ゅ畾妯℃澘鏄庣粏
+ *
+ * @param bo 鍒ゅ畾妯℃澘鏄庣粏
+ * @return 鏄惁淇敼鎴愬姛
+ */
+ @Override
+ public Boolean updateByBo(QmJudgeDetailsBo bo) {
+ QmJudgeDetails update = MapstructUtils.convert(bo, QmJudgeDetails.class);
+ validEntityBeforeSave(update);
+ return baseMapper.updateById(update) > 0;
+ }
+
+ /**
+ * 淇濆瓨鍓嶇殑鏁版嵁鏍¢獙
+ */
+ private void validEntityBeforeSave(QmJudgeDetails 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/qa/qm/service/impl/QmJudgeServiceImpl.java b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/qm/service/impl/QmJudgeServiceImpl.java
new file mode 100644
index 0000000..55b949c
--- /dev/null
+++ b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/qm/service/impl/QmJudgeServiceImpl.java
@@ -0,0 +1,174 @@
+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.dromara.qa.qm.domain.QmStd;
+import org.springframework.stereotype.Service;
+import org.dromara.qa.qm.domain.bo.QmJudgeBo;
+import org.dromara.qa.qm.domain.vo.QmJudgeVo;
+import org.dromara.qa.qm.domain.QmJudge;
+import org.dromara.qa.qm.mapper.QmJudgeMapper;
+import org.dromara.qa.qm.service.IQmJudgeService;
+import org.dromara.qa.qm.service.IQmStdService;
+import org.dromara.qa.qm.mapper.QmStdMapper;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Collection;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.HashMap;
+
+/**
+ * 鍒ゅ畾渚濇嵁Service涓氬姟灞傚鐞�
+ *
+ * @author zhuguifei
+ * @date 2026-03-18
+ */
+@Slf4j
+@RequiredArgsConstructor
+@Service
+public class QmJudgeServiceImpl implements IQmJudgeService {
+
+ private final QmJudgeMapper baseMapper;
+ private final QmStdMapper qmStdMapper;
+
+ /**
+ * 鏌ヨ鍒ゅ畾渚濇嵁
+ *
+ * @param id 涓婚敭
+ * @return 鍒ゅ畾渚濇嵁
+ */
+ @Override
+ public QmJudgeVo queryById(String id){
+ return baseMapper.selectVoById(id);
+ }
+
+ /**
+ * 鍒嗛〉鏌ヨ鍒ゅ畾渚濇嵁鍒楄〃
+ *
+ * @param bo 鏌ヨ鏉′欢
+ * @param pageQuery 鍒嗛〉鍙傛暟
+ * @return 鍒ゅ畾渚濇嵁鍒嗛〉鍒楄〃
+ */
+ @Override
+ public TableDataInfo<QmJudgeVo> queryPageList(QmJudgeBo bo, PageQuery pageQuery) {
+ LambdaQueryWrapper<QmJudge> lqw = buildQueryWrapper(bo);
+ Page<QmJudgeVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
+ queryQmStd(result);
+ return TableDataInfo.build(result);
+ }
+
+ private void queryQmStd(Page<QmJudgeVo> result) {
+ if (result == null || result.getRecords() == null || result.getRecords().isEmpty()) {
+ return;
+ }
+ List<QmJudgeVo> records = result.getRecords();
+
+ // 1. Collect all unique stdCode values
+ Set<String> stdCodes = records.stream()
+ .map(QmJudgeVo::getStdCod)
+ .filter(StringUtils::isNotBlank)
+ .collect(Collectors.toSet());
+
+ if (stdCodes.isEmpty()) {
+ return;
+ }
+
+ // 2. Query QmStd entities based on the collected stdCode values
+ LambdaQueryWrapper<QmStd> stdLqw = Wrappers.lambdaQuery();
+ stdLqw.in(QmStd::getId, stdCodes);
+ List<QmStd> qmStds = qmStdMapper.selectList(stdLqw);
+
+ // 3. Create a map from stdCode to stdName for easy lookup
+ Map<String, String> stdCodeToNameMap = qmStds.stream()
+ .collect(Collectors.toMap(QmStd::getId, QmStd::getStdName, (oldValue, newValue) -> oldValue));
+
+ // 4. Populate stdName for each QmJudgeVo
+ records.forEach(judgeVo ->
+ judgeVo.setStdName(stdCodeToNameMap.getOrDefault(judgeVo.getStdCod(), null))
+ );
+ }
+
+ /**
+ * 鏌ヨ绗﹀悎鏉′欢鐨勫垽瀹氫緷鎹垪琛�
+ *
+ * @param bo 鏌ヨ鏉′欢
+ * @return 鍒ゅ畾渚濇嵁鍒楄〃
+ */
+ @Override
+ public List<QmJudgeVo> queryList(QmJudgeBo bo) {
+ LambdaQueryWrapper<QmJudge> lqw = buildQueryWrapper(bo);
+ return baseMapper.selectVoList(lqw);
+ }
+
+ private LambdaQueryWrapper<QmJudge> buildQueryWrapper(QmJudgeBo bo) {
+ Map<String, Object> params = bo.getParams();
+ LambdaQueryWrapper<QmJudge> lqw = Wrappers.lambdaQuery();
+ lqw.like(StringUtils.isNotBlank(bo.getMatCode()), QmJudge::getMatCode, bo.getMatCode());
+ lqw.like(StringUtils.isNotBlank(bo.getMatName()), QmJudge::getMatName, bo.getMatName());
+ lqw.like(StringUtils.isNotBlank(bo.getJudgeName()), QmJudge::getJudgeName, bo.getJudgeName());
+ lqw.eq(bo.getStatus()!=null,QmJudge::getStatus, bo.getStatus());
+ lqw.eq(bo.getCategory()!=null,QmJudge::getCategory, bo.getCategory());
+ return lqw;
+ }
+
+ /**
+ * 鏂板鍒ゅ畾渚濇嵁
+ *
+ * @param bo 鍒ゅ畾渚濇嵁
+ * @return 鏄惁鏂板鎴愬姛
+ */
+ @Override
+ public Boolean insertByBo(QmJudgeBo bo) {
+ QmJudge add = MapstructUtils.convert(bo, QmJudge.class);
+ validEntityBeforeSave(add);
+ boolean flag = baseMapper.insert(add) > 0;
+ if (flag) {
+ bo.setId(add.getId());
+ }
+ return flag;
+ }
+
+ /**
+ * 淇敼鍒ゅ畾渚濇嵁
+ *
+ * @param bo 鍒ゅ畾渚濇嵁
+ * @return 鏄惁淇敼鎴愬姛
+ */
+ @Override
+ public Boolean updateByBo(QmJudgeBo bo) {
+ QmJudge update = MapstructUtils.convert(bo, QmJudge.class);
+ validEntityBeforeSave(update);
+ return baseMapper.updateById(update) > 0;
+ }
+
+ /**
+ * 淇濆瓨鍓嶇殑鏁版嵁鏍¢獙
+ */
+ private void validEntityBeforeSave(QmJudge 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/resources/mapper/qa/md/MdInstrumentMapper.xml b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/resources/mapper/qa/md/MdInstrumentMapper.xml
new file mode 100644
index 0000000..783ea3f
--- /dev/null
+++ b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/resources/mapper/qa/md/MdInstrumentMapper.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.qa.md.mapper.MdInstrumentMapper">
+</mapper>
diff --git a/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/resources/mapper/qa/qm/QmJudgeDetailsMapper.xml b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/resources/mapper/qa/qm/QmJudgeDetailsMapper.xml
new file mode 100644
index 0000000..5564d04
--- /dev/null
+++ b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/resources/mapper/qa/qm/QmJudgeDetailsMapper.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.qa.qm.mapper.QmJudgeDetailsMapper">
+</mapper>
diff --git a/ruoyi-plus-soybean/.gitignore b/ruoyi-plus-soybean/.gitignore
index fdd159a..92f30a5 100755
--- a/ruoyi-plus-soybean/.gitignore
+++ b/ruoyi-plus-soybean/.gitignore
@@ -33,3 +33,5 @@
yarn.lock
.VSCodeCounter
+
+/pms/
diff --git a/ruoyi-plus-soybean/src/router/elegant/imports.ts b/ruoyi-plus-soybean/src/router/elegant/imports.ts
index 600d23d..56a0110 100755
--- a/ruoyi-plus-soybean/src/router/elegant/imports.ts
+++ b/ruoyi-plus-soybean/src/router/elegant/imports.ts
@@ -32,12 +32,15 @@
demo_demo: () => import("@/views/demo/demo/index.vue"),
demo_tree: () => import("@/views/demo/tree/index.vue"),
home: () => import("@/views/home/index.vue"),
+ md_instrument: () => import("@/views/md/instrument/index.vue"),
md_shift: () => import("@/views/md/shift/index.vue"),
monitor_cache: () => import("@/views/monitor/cache/index.vue"),
monitor_logininfor: () => import("@/views/monitor/logininfor/index.vue"),
monitor_online: () => import("@/views/monitor/online/index.vue"),
monitor_operlog: () => import("@/views/monitor/operlog/index.vue"),
qm_batch: () => import("@/views/qm/batch/index.vue"),
+ "qm_judge-details": () => import("@/views/qm/judge-details/index.vue"),
+ qm_judge: () => import("@/views/qm/judge/index.vue"),
qm_std: () => import("@/views/qm/std/index.vue"),
report_demo: () => import("@/views/report/demo/index.vue"),
"report_silk-storage-output": () => import("@/views/report/silk-storage-output/index.vue"),
diff --git a/ruoyi-plus-soybean/src/router/elegant/routes.ts b/ruoyi-plus-soybean/src/router/elegant/routes.ts
index 193441e..1a339b1 100755
--- a/ruoyi-plus-soybean/src/router/elegant/routes.ts
+++ b/ruoyi-plus-soybean/src/router/elegant/routes.ts
@@ -190,6 +190,15 @@
},
children: [
{
+ name: 'md_instrument',
+ path: '/md/instrument',
+ component: 'view.md_instrument',
+ meta: {
+ title: 'md_instrument',
+ i18nKey: 'route.md_instrument'
+ }
+ },
+ {
name: 'md_shift',
path: '/md/shift',
component: 'view.md_shift',
@@ -266,6 +275,24 @@
}
},
{
+ name: 'qm_judge',
+ path: '/qm/judge',
+ component: 'view.qm_judge',
+ meta: {
+ title: 'qm_judge',
+ i18nKey: 'route.qm_judge'
+ }
+ },
+ {
+ name: 'qm_judge-details',
+ path: '/qm/judge-details',
+ component: 'view.qm_judge-details',
+ meta: {
+ title: 'qm_judge-details',
+ i18nKey: 'route.qm_judge-details'
+ }
+ },
+ {
name: 'qm_std',
path: '/qm/std',
component: 'view.qm_std',
diff --git a/ruoyi-plus-soybean/src/router/elegant/transform.ts b/ruoyi-plus-soybean/src/router/elegant/transform.ts
index 5667807..edd1a94 100755
--- a/ruoyi-plus-soybean/src/router/elegant/transform.ts
+++ b/ruoyi-plus-soybean/src/router/elegant/transform.ts
@@ -185,6 +185,7 @@
"iframe-page": "/iframe-page/:url",
"login": "/login/:module(pwd-login|code-login|register|reset-pwd|bind-wechat)?",
"md": "/md",
+ "md_instrument": "/md/instrument",
"md_shift": "/md/shift",
"monitor": "/monitor",
"monitor_cache": "/monitor/cache",
@@ -193,6 +194,8 @@
"monitor_operlog": "/monitor/operlog",
"qm": "/qm",
"qm_batch": "/qm/batch",
+ "qm_judge": "/qm/judge",
+ "qm_judge-details": "/qm/judge-details",
"qm_std": "/qm/std",
"report": "/report",
"report_demo": "/report/demo",
diff --git a/ruoyi-plus-soybean/src/service/api/md/instrument.ts b/ruoyi-plus-soybean/src/service/api/md/instrument.ts
new file mode 100644
index 0000000..10a7afd
--- /dev/null
+++ b/ruoyi-plus-soybean/src/service/api/md/instrument.ts
@@ -0,0 +1,35 @@
+import { request } from '@/service/request';
+
+/** 鑾峰彇娴嬮噺浠櫒鍒楄〃 */
+export function fetchGetInstrumentList (params?: Api.Md.InstrumentSearchParams) {
+ return request<Api.Md.InstrumentList>({
+ url: '/md/instrument/list',
+ method: 'get',
+ params
+ });
+}
+/** 鏂板娴嬮噺浠櫒 */
+export function fetchCreateInstrument (data: Api.Md.InstrumentOperateParams) {
+ return request<boolean>({
+ url: '/md/instrument',
+ method: 'post',
+ data
+ });
+}
+
+/** 淇敼娴嬮噺浠櫒 */
+export function fetchUpdateInstrument (data: Api.Md.InstrumentOperateParams) {
+ return request<boolean>({
+ url: '/md/instrument',
+ method: 'put',
+ data
+ });
+}
+
+/** 鎵归噺鍒犻櫎娴嬮噺浠櫒 */
+export function fetchBatchDeleteInstrument (ids: CommonType.IdType[]) {
+ return request<boolean>({
+ url: `/md/instrument/${ids.join(',')}`,
+ method: 'delete'
+ });
+}
diff --git a/ruoyi-plus-soybean/src/service/api/qm/judge-details.ts b/ruoyi-plus-soybean/src/service/api/qm/judge-details.ts
new file mode 100644
index 0000000..1a319fe
--- /dev/null
+++ b/ruoyi-plus-soybean/src/service/api/qm/judge-details.ts
@@ -0,0 +1,35 @@
+import { request } from '@/service/request';
+
+/** 鑾峰彇鍒ゅ畾妯℃澘鏄庣粏鍒楄〃 */
+export function fetchGetJudgeDetailsList (params?: Api.Qm.JudgeDetailsSearchParams) {
+ return request<Api.Qm.JudgeDetailsList>({
+ url: '/qm/judgeDetails/list',
+ method: 'get',
+ params
+ });
+}
+/** 鏂板鍒ゅ畾妯℃澘鏄庣粏 */
+export function fetchCreateJudgeDetails (data: Api.Qm.JudgeDetailsOperateParams) {
+ return request<boolean>({
+ url: '/qm/judgeDetails',
+ method: 'post',
+ data
+ });
+}
+
+/** 淇敼鍒ゅ畾妯℃澘鏄庣粏 */
+export function fetchUpdateJudgeDetails (data: Api.Qm.JudgeDetailsOperateParams) {
+ return request<boolean>({
+ url: '/qm/judgeDetails',
+ method: 'put',
+ data
+ });
+}
+
+/** 鎵归噺鍒犻櫎鍒ゅ畾妯℃澘鏄庣粏 */
+export function fetchBatchDeleteJudgeDetails (ids: CommonType.IdType[]) {
+ return request<boolean>({
+ url: `/qm/judgeDetails/${ids.join(',')}`,
+ method: 'delete'
+ });
+}
diff --git a/ruoyi-plus-soybean/src/service/api/qm/judge.ts b/ruoyi-plus-soybean/src/service/api/qm/judge.ts
new file mode 100644
index 0000000..2b4a94f
--- /dev/null
+++ b/ruoyi-plus-soybean/src/service/api/qm/judge.ts
@@ -0,0 +1,35 @@
+import { request } from '@/service/request';
+
+/** 鑾峰彇鍒ゅ畾渚濇嵁鍒楄〃 */
+export function fetchGetJudgeList (params?: Api.Qm.JudgeSearchParams) {
+ return request<Api.Qm.JudgeList>({
+ url: '/qm/judge/list',
+ method: 'get',
+ params
+ });
+}
+/** 鏂板鍒ゅ畾渚濇嵁 */
+export function fetchCreateJudge (data: Api.Qm.JudgeOperateParams) {
+ return request<boolean>({
+ url: '/qm/judge',
+ method: 'post',
+ data
+ });
+}
+
+/** 淇敼鍒ゅ畾渚濇嵁 */
+export function fetchUpdateJudge (data: Api.Qm.JudgeOperateParams) {
+ return request<boolean>({
+ url: '/qm/judge',
+ method: 'put',
+ data
+ });
+}
+
+/** 鎵归噺鍒犻櫎鍒ゅ畾渚濇嵁 */
+export function fetchBatchDeleteJudge (ids: CommonType.IdType[]) {
+ return request<boolean>({
+ url: `/qm/judge/${ids.join(',')}`,
+ method: 'delete'
+ });
+}
diff --git a/ruoyi-plus-soybean/src/typings/api/md.instrument.api.d.ts b/ruoyi-plus-soybean/src/typings/api/md.instrument.api.d.ts
new file mode 100644
index 0000000..f913592
--- /dev/null
+++ b/ruoyi-plus-soybean/src/typings/api/md.instrument.api.d.ts
@@ -0,0 +1,65 @@
+/**
+ * Namespace Api
+ *
+ * All backend api type
+ */
+declare namespace Api {
+ /**
+ * namespace Md
+ *
+ * backend api module: "Md"
+ */
+ namespace Md {
+ /** instrument */
+ type Instrument = Common.CommonRecord<{
+ /** 缂栫爜 */
+ id: CommonType.IdType;
+ /** 浠櫒浠g爜 */
+ instrumentCode: string;
+ /** 浠櫒鍚嶇О */
+ instrumentName: string;
+ /** 鎺ュ彛鏍囧噯 */
+ ifStd: string;
+ /** 鎺ュ彛鏍囧噯浠g爜 */
+ isc: number;
+ /** 鎵�灞炶溅闂� */
+ workShop: string;
+ /** 鍚敤 */
+ enable: number;
+ /** 鍒犻櫎 */
+ del: number;
+ /** 澶囨敞 */
+ instrumentDes: string;
+ }>;
+
+ /** instrument search params */
+ type InstrumentSearchParams = CommonType.RecordNullable<
+ Pick<
+ Api.Md.Instrument,
+ | 'instrumentCode'
+ | 'instrumentName'
+ | 'enable'
+ > &
+ Api.Common.CommonSearchParams
+ >;
+
+ /** instrument operate params */
+ type InstrumentOperateParams = CommonType.RecordNullable<
+ Pick<
+ Api.Md.Instrument,
+ | 'id'
+ | 'instrumentCode'
+ | 'instrumentName'
+ | 'ifStd'
+ | 'isc'
+ | 'workShop'
+ | 'enable'
+ | 'del'
+ | 'instrumentDes'
+ >
+ >;
+
+ /** instrument list */
+ type InstrumentList = Api.Common.PaginatingQueryRecord<Instrument>;
+ }
+}
diff --git a/ruoyi-plus-soybean/src/typings/api/qm.judge-details.api.d.ts b/ruoyi-plus-soybean/src/typings/api/qm.judge-details.api.d.ts
new file mode 100644
index 0000000..1a542ce
--- /dev/null
+++ b/ruoyi-plus-soybean/src/typings/api/qm.judge-details.api.d.ts
@@ -0,0 +1,85 @@
+/**
+ * Namespace Api
+ *
+ * All backend api type
+ */
+declare namespace Api {
+ /**
+ * namespace Qm
+ *
+ * backend api module: "Qm"
+ */
+ namespace Qm {
+ /** judge details */
+ type JudgeDetails = Common.CommonRecord<{
+ /** 缂栫爜 */
+ id: CommonType.IdType;
+ /** 鍒ゅ畾涓绘爣璇� */
+ judgeId: CommonType.IdType;
+ /** 鍒ゅ畾椤笽TEM */
+ itemCod: string;
+ /** 鍒ゅ畾椤筃AME */
+ itemName: string;
+ /** 鏍囧噯鍊� */
+ value3: number;
+ /** 鍒ゅ畾鍊�1 */
+ value1: number;
+ /** 鍒ゅ畾鍊�2 */
+ value2: number;
+ /** 缂洪櫡浣嶇疆 */
+ location: string;
+ /** 鍒ゅ畾绾у埆 (A,B,C,D) */
+ cls: string;
+ /** 鍒嗗�兼爣鍑� (鎵e垎鏍囧噯锛屽緱鍒嗘爣鍑�),姣斿涓嶅悎鏍间竴娆℃墸澶氬皯鍒� */
+ stdscore: number;
+ /** 鏍囪姝ら」鏄惁涓哄悎鎴愰」鐩紝姣斿澶栬锛屽疄闄呬笂鍏宠仈浜嗗緢澶氬瓙椤圭洰 */
+ ismix: number;
+ /** 鑻ユ瀛楁鏈塙UID鍊硷紝琛ㄦ槑瀹冨彲鑳戒负鍏朵粬椤圭洰鐨勫瓙椤癸紝姣斿鈥滅┖澶粹��,瀹冧负鐑熸敮澶栬椤圭洰鐨勫瓙椤� */
+ rid: CommonType.IdType;
+ /** 鑼冨洿-澶囩敤 */
+ category: number;
+ /** 澶囨敞 */
+ decisionDes: string;
+ /** 淇敼浜� */
+ updateUser: string;
+ }>;
+
+ /** judge details search params */
+ type JudgeDetailsSearchParams = CommonType.RecordNullable<
+ Pick<
+ Api.Qm.JudgeDetails,
+ | 'judgeId'
+ | 'itemCod'
+ | 'itemName'
+ | 'cls'
+ | 'ismix'
+ > &
+ Api.Common.CommonSearchParams
+ >;
+
+ /** judge details operate params */
+ type JudgeDetailsOperateParams = CommonType.RecordNullable<
+ Pick<
+ Api.Qm.JudgeDetails,
+ | 'id'
+ | 'judgeId'
+ | 'itemCod'
+ | 'itemName'
+ | 'value3'
+ | 'value1'
+ | 'value2'
+ | 'location'
+ | 'cls'
+ | 'stdscore'
+ | 'ismix'
+ | 'rid'
+ | 'category'
+ | 'decisionDes'
+ | 'updateUser'
+ >
+ >;
+
+ /** judge details list */
+ type JudgeDetailsList = Api.Common.PaginatingQueryRecord<JudgeDetails>;
+ }
+}
diff --git a/ruoyi-plus-soybean/src/typings/api/qm.judge.api.d.ts b/ruoyi-plus-soybean/src/typings/api/qm.judge.api.d.ts
new file mode 100644
index 0000000..04807e4
--- /dev/null
+++ b/ruoyi-plus-soybean/src/typings/api/qm.judge.api.d.ts
@@ -0,0 +1,77 @@
+/**
+ * Namespace Api
+ *
+ * All backend api type
+ */
+declare namespace Api {
+ /**
+ * namespace Qm
+ *
+ * backend api module: "Qm"
+ */
+ namespace Qm {
+ /** judge */
+ type Judge = Common.CommonRecord<{
+ /** 缂栫爜 */
+ id: CommonType.IdType;
+ /** 鐗╂枡鐗屽彿浠g爜 */
+ matCode: string;
+ /** 鐗╂枡鐗屽彿 */
+ matName: string;
+ /** 鐗堟湰鍙� */
+ version: number;
+ /** 鐘舵�� */
+ status: number;
+ /** 鍒ゅ畾瑙勭▼COD */
+ stdCod: string;
+ /** 鍒涘缓鏃堕棿 */
+ cdate: string;
+ /** 鐢熸晥浜�/鎿嶄綔浜哄憳 */
+ oper: string;
+ /** 澶囨敞 */
+ des: string;
+ /** 鐗╂枡绫诲瀷 */
+ category: number;
+ /** 杈呮枡绫诲瀷缂栫爜 */
+ typeCode: string;
+ /** 杈呮枡绫诲瀷鍚嶇О */
+ typeName: string;
+ /** 鍒ゅ畾鍚嶇О */
+ judgeName: string;
+ }>;
+
+ /** judge search params */
+ type JudgeSearchParams = CommonType.RecordNullable<
+ Pick<
+ Api.Qm.Judge,
+ | 'matCode'
+ | 'matName'
+ | 'judgeName'
+ | 'category'
+ | 'status'
+ > &
+ Api.Common.CommonSearchParams
+ >;
+
+ /** judge operate params */
+ type JudgeOperateParams = CommonType.RecordNullable<
+ Pick<
+ Api.Qm.Judge,
+ | 'matCode'
+ | 'matName'
+ | 'status'
+ | 'stdCod'
+ | 'cdate'
+ | 'oper'
+ | 'des'
+ | 'category'
+ | 'typeCode'
+ | 'typeName'
+ | 'judgeName'
+ >
+ >;
+
+ /** judge list */
+ type JudgeList = Api.Common.PaginatingQueryRecord<Judge>;
+ }
+}
diff --git a/ruoyi-plus-soybean/src/typings/elegant-router.d.ts b/ruoyi-plus-soybean/src/typings/elegant-router.d.ts
index 767e732..f9e689d 100755
--- a/ruoyi-plus-soybean/src/typings/elegant-router.d.ts
+++ b/ruoyi-plus-soybean/src/typings/elegant-router.d.ts
@@ -39,6 +39,7 @@
"iframe-page": "/iframe-page/:url";
"login": "/login/:module(pwd-login|code-login|register|reset-pwd|bind-wechat)?";
"md": "/md";
+ "md_instrument": "/md/instrument";
"md_shift": "/md/shift";
"monitor": "/monitor";
"monitor_cache": "/monitor/cache";
@@ -47,6 +48,8 @@
"monitor_operlog": "/monitor/operlog";
"qm": "/qm";
"qm_batch": "/qm/batch";
+ "qm_judge": "/qm/judge";
+ "qm_judge-details": "/qm/judge-details";
"qm_std": "/qm/std";
"report": "/report";
"report_demo": "/report/demo";
@@ -156,12 +159,15 @@
| "demo_demo"
| "demo_tree"
| "home"
+ | "md_instrument"
| "md_shift"
| "monitor_cache"
| "monitor_logininfor"
| "monitor_online"
| "monitor_operlog"
| "qm_batch"
+ | "qm_judge-details"
+ | "qm_judge"
| "qm_std"
| "report_demo"
| "report_silk-storage-output"
diff --git a/ruoyi-plus-soybean/src/views/md/instrument/index.vue b/ruoyi-plus-soybean/src/views/md/instrument/index.vue
new file mode 100644
index 0000000..9910a7d
--- /dev/null
+++ b/ruoyi-plus-soybean/src/views/md/instrument/index.vue
@@ -0,0 +1,247 @@
+<script setup lang="tsx">
+import { ref } from 'vue';
+import { NDivider, NTooltip } from 'naive-ui';
+import { fetchBatchDeleteInstrument, fetchGetInstrumentList } from '@/service/api/md/instrument';
+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 { useDict } from '@/hooks/business/dict';
+import { $t } from '@/locales';
+import ButtonIcon from '@/components/custom/button-icon.vue';
+import InstrumentOperateDrawer from './modules/instrument-operate-drawer.vue';
+import InstrumentSearch from './modules/instrument-search.vue';
+
+defineOptions({
+ name: 'InstrumentList'
+});
+
+useDict('sys_yes_no');
+
+const appStore = useAppStore();
+const { download } = useDownload();
+const { hasAuth } = useAuth();
+
+const searchParams = ref<Api.Md.InstrumentSearchParams>({
+ pageNum: 1,
+ pageSize: 10,
+ instrumentCode: null,
+ instrumentName: null,
+ enable: null,
+ params: {}
+});
+
+const { columns, columnChecks, data, getData, getDataByPage, loading, mobilePagination, scrollX } =
+ useNaivePaginatedTable({
+ api: () => fetchGetInstrumentList(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: 'instrumentCode',
+ title: '浠櫒浠g爜',
+ align: 'center',
+ width: 120
+ },
+ {
+ key: 'instrumentName',
+ title: '浠櫒鍚嶇О',
+ align: 'center',
+ width: 200
+ },
+ {
+ key: 'ifStd',
+ title: '鎺ュ彛鏍囧噯',
+ align: 'center',
+ width: 120
+ },
+ {
+ key: 'isc',
+ title: '鎺ュ彛鏍囧噯浠g爜',
+ align: 'center',
+ width: 100
+ },
+ {
+ key: 'workShop',
+ title: '鎵�灞炶溅闂�',
+ align: 'center',
+ width: 120
+ },
+ {
+ key: 'enable',
+ title: '鍚敤',
+ align: 'center',
+ width: 120,
+ render(row) {
+ return <DictTag value={row.enable} dictCode="sys_yes_no" />;
+ }
+ },
+ {
+ key: 'del',
+ title: '鍒犻櫎',
+ align: 'center',
+ width: 120
+ },
+ {
+ key: 'instrumentDes',
+ title: '澶囨敞',
+ align: 'center',
+ width: 300,
+ render: row => (
+ <NTooltip trigger="hover">
+ {{
+ trigger: () => (
+ <div
+ style={{
+ whiteSpace: 'nowrap',
+ overflow: 'hidden',
+ textOverflow: 'ellipsis'
+ }}
+ >
+ {row.instrumentDes}
+ </div>
+ ),
+ default: () => row.instrumentDes
+ }}
+ </NTooltip>
+ )
+ },
+ {
+ key: 'operate',
+ title: $t('common.operate'),
+ align: 'center',
+ width: 130,
+ render: row => {
+ const divider = () => {
+ if (!hasAuth('md:instrument:edit') || !hasAuth('md:instrument:remove')) {
+ return null;
+ }
+ return <NDivider vertical />;
+ };
+
+ const editBtn = () => {
+ if (!hasAuth('md:instrument: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:instrument: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 fetchBatchDeleteInstrument(checkedRowKeys.value);
+ if (error) return;
+ onBatchDeleted();
+}
+
+async function handleDelete(id: CommonType.IdType) {
+ // request
+ const { error } = await fetchBatchDeleteInstrument([id]);
+ if (error) return;
+ onDeleted();
+}
+
+function edit(id: CommonType.IdType) {
+ handleEdit(id);
+}
+
+function handleExport() {
+ download('/md/instrument/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">
+ <InstrumentSearch 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:instrument:add')"
+ :show-delete="hasAuth('md:instrument:remove')"
+ :show-export="hasAuth('md:instrument: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"
+ />
+ <InstrumentOperateDrawer
+ 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/instrument/modules/instrument-operate-drawer.vue b/ruoyi-plus-soybean/src/views/md/instrument/modules/instrument-operate-drawer.vue
new file mode 100644
index 0000000..c607874
--- /dev/null
+++ b/ruoyi-plus-soybean/src/views/md/instrument/modules/instrument-operate-drawer.vue
@@ -0,0 +1,157 @@
+<script setup lang="ts">
+import { computed, ref, watch } from 'vue';
+import { NSelect } from 'naive-ui';
+import { jsonClone } from '@sa/utils';
+import { fetchCreateInstrument, fetchUpdateInstrument } from '@/service/api/md/instrument';
+import { useFormRules, useNaiveForm } from '@/hooks/common/form';
+import { $t } from '@/locales';
+
+defineOptions({
+ name: 'InstrumentOperateDrawer'
+});
+
+interface Props {
+ /** the type of operation */
+ operateType: NaiveUI.TableOperateType;
+ /** the edit row data */
+ rowData?: Api.Md.Instrument | 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];
+});
+
+const enableOptions = [
+ { label: '鍚敤', value: 1 },
+ { label: '鍋滅敤', value: 0 }
+];
+
+const delOptions = [
+ { label: '姝e父', value: 0 },
+ { label: '鍒犻櫎', value: 1 }
+];
+
+type Model = Api.Md.InstrumentOperateParams;
+
+const model = ref<Model>(createDefaultModel());
+
+function createDefaultModel(): Model {
+ return {
+ id: '',
+ instrumentCode: '',
+ instrumentName: '',
+ ifStd: '',
+ isc: null,
+ workShop: '',
+ enable: 1,
+ del: 0,
+ instrumentDes: ''
+ };
+}
+
+type RuleKey = Extract<keyof Model, 'id'>;
+
+const rules: Record<RuleKey, App.Global.FormRule> = {
+ id: createRequiredRule('ud涓嶈兘涓虹┖')
+};
+
+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, instrumentCode, instrumentName, ifStd, isc, workShop, enable, del, instrumentDes } = model.value;
+
+ // request
+ if (props.operateType === 'add') {
+ const { error } = await fetchCreateInstrument({ id, instrumentCode, instrumentName, ifStd, isc, workShop, enable, del, instrumentDes });
+ if (error) return;
+ }
+
+ if (props.operateType === 'edit') {
+ const { error } = await fetchUpdateInstrument({ id, instrumentCode, instrumentName, ifStd, isc, workShop, enable, del, instrumentDes });
+ 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="instrumentCode">
+ <NInput v-model:value="model.instrumentCode" placeholder="璇疯緭鍏ヤ华鍣ㄤ唬鐮�" />
+ </NFormItem>
+ <NFormItem label="浠櫒鍚嶇О" path="instrumentName">
+ <NInput v-model:value="model.instrumentName" placeholder="璇疯緭鍏ヤ华鍣ㄥ悕绉�" />
+ </NFormItem>
+ <NFormItem label="鎺ュ彛鏍囧噯" path="ifStd">
+ <NInput v-model:value="model.ifStd" placeholder="璇疯緭鍏ユ帴鍙f爣鍑�" />
+ </NFormItem>
+ <NFormItem label="鎺ュ彛鏍囧噯浠g爜" path="isc">
+ <NInput v-model:value="model.isc" placeholder="璇疯緭鍏ユ帴鍙f爣鍑嗕唬鐮�" />
+ </NFormItem>
+ <NFormItem label="鎵�灞炶溅闂�" path="workShop">
+ <NInput v-model:value="model.workShop" placeholder="璇疯緭鍏ユ墍灞炶溅闂�" />
+ </NFormItem>
+ <NFormItem label="鍚敤" path="enable">
+ <NSelect v-model:value="model.enable as number" :options="enableOptions" placeholder="璇烽�夋嫨鍚敤" clearable />
+ </NFormItem>
+ <NFormItem label="鍒犻櫎" path="del">
+ <NSelect v-model:value="model.del as number" :options="delOptions" placeholder="璇烽�夋嫨鍒犻櫎" clearable />
+ </NFormItem>
+ <NFormItem label="澶囨敞" path="instrumentDes">
+ <NInput v-model:value="model.instrumentDes" placeholder="璇疯緭鍏ュ娉�" />
+ </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/instrument/modules/instrument-search.vue b/ruoyi-plus-soybean/src/views/md/instrument/modules/instrument-search.vue
new file mode 100644
index 0000000..dde8682
--- /dev/null
+++ b/ruoyi-plus-soybean/src/views/md/instrument/modules/instrument-search.vue
@@ -0,0 +1,82 @@
+<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: 'InstrumentSearch'
+});
+
+interface Emits {
+ (e: 'search'): void;
+}
+
+const emit = defineEmits<Emits>();
+
+const { formRef, validate, restoreValidation } = useNaiveForm();
+
+const model = defineModel<Api.Md.InstrumentSearchParams>('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-instrument-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="instrumentCode" class="pr-24px">
+ <NInput v-model:value="model.instrumentCode" placeholder="璇疯緭鍏ヤ华鍣ㄤ唬鐮�" />
+ </NFormItemGi>
+ <NFormItemGi span="24 s:12 m:6" label="浠櫒鍚嶇О" label-width="auto" path="instrumentName" class="pr-24px">
+ <NInput v-model:value="model.instrumentName" placeholder="璇疯緭鍏ヤ华鍣ㄥ悕绉�" />
+ </NFormItemGi>
+ <NFormItemGi span="24 s:12 m:6" label="鍚敤" label-width="auto" path="enable" class="pr-24px">
+ <DictSelect
+ v-model:value="model.enable"
+ placeholder="璇烽�夋嫨鍚敤"
+ dict-code="sys_yes_no"
+ 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/checkitem/modules/checkitem-operate-drawer.vue b/ruoyi-plus-soybean/src/views/qm/checkitem/modules/checkitem-operate-drawer.vue
index 4037161..e4af1c5 100755
--- a/ruoyi-plus-soybean/src/views/qm/checkitem/modules/checkitem-operate-drawer.vue
+++ b/ruoyi-plus-soybean/src/views/qm/checkitem/modules/checkitem-operate-drawer.vue
@@ -6,6 +6,7 @@
fetchGetRid,
fetchUpdateCheckitem
} from '@/service/api/qm/checkitem';
+import { fetchGetInstrumentList } from '@/service/api/md/instrument';
import { useFormRules, useNaiveForm } from '@/hooks/common/form';
import { $t } from '@/locales';
@@ -76,6 +77,7 @@
const model = ref<Model>(createDefaultModel());
const ridOptions = ref<{ label: string; value: string | number }[]>([]);
+const instrumentOptions = ref<{ label: string; value: string | number }[]>([]);
async function fetchRidOptions(searchText?: string) {
const params: any = {};
@@ -96,6 +98,26 @@
function handleRidSelectSearch(query: string) {
fetchRidOptions(query);
+}
+
+async function fetchInstrumentOptions(searchText?: string) {
+ const params: any = {};
+ if (searchText) {
+ params.instrumentName = searchText;
+ }
+ const { data } = await fetchGetInstrumentList(params);
+ if (data && data.rows) {
+ instrumentOptions.value = data.rows.map(item => ({
+ label: item.instrumentName,
+ value: item.instrumentCode
+ }));
+ } else {
+ instrumentOptions.value = [];
+ }
+}
+
+function handleInstrumentSelectSearch(query: string) {
+ fetchInstrumentOptions(query);
}
@@ -139,6 +161,8 @@
// Fetch options for rid
await fetchRidOptions();
+ // Fetch options for instrument
+ await fetchInstrumentOptions();
if (props.operateType === 'add' && props.stdCode) {
model.value.stdCode = props.stdCode;
@@ -202,7 +226,17 @@
<NFormItem label="妫�楠岄」鎻忚堪" path="itemDes">
<NInput v-model:value="model.itemDes" placeholder="璇疯緭鍏ユ楠岄」鎻忚堪" />
</NFormItem>
-
+ <NFormItem label="妫�娴嬩华鍣�" path="instrumentCode">
+ <NSelect
+ v-model:value="model.instrumentCode"
+ :options="instrumentOptions"
+ placeholder="璇烽�夋嫨妫�娴嬩华鍣�"
+ clearable
+ filterable
+ @search="handleInstrumentSelectSearch"
+ @focus="() => fetchInstrumentOptions()"
+ />
+ </NFormItem>
<NFormItem label="浠櫒鎻忚堪" path="instrumentDes">
<NInput v-model:value="model.instrumentDes" placeholder="璇疯緭鍏ヤ华鍣ㄦ弿杩�" />
</NFormItem>
diff --git a/ruoyi-plus-soybean/src/views/qm/judge-details/index.vue b/ruoyi-plus-soybean/src/views/qm/judge-details/index.vue
new file mode 100644
index 0000000..a7aa061
--- /dev/null
+++ b/ruoyi-plus-soybean/src/views/qm/judge-details/index.vue
@@ -0,0 +1,262 @@
+<script setup lang="tsx">
+import { ref } from 'vue';
+import { NDivider } from 'naive-ui';
+import { fetchBatchDeleteJudgeDetails, fetchGetJudgeDetailsList } from '@/service/api/qm/judge-details';
+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 JudgeDetailsOperateDrawer from './modules/judge-details-operate-drawer.vue';
+import JudgeDetailsSearch from './modules/judge-details-search.vue';
+
+defineOptions({
+ name: 'JudgeDetailsList'
+});
+
+
+const appStore = useAppStore();
+const { download } = useDownload();
+const { hasAuth } = useAuth();
+
+const searchParams = ref<Api.Qm.JudgeDetailsSearchParams>({
+ pageNum: 1,
+ pageSize: 10,
+ judgeId: null,
+ itemCod: null,
+ itemName: null,
+ cls: null,
+ ismix: null,
+ params: {}
+});
+
+const { columns, columnChecks, data, getData, getDataByPage, loading, mobilePagination, scrollX } =
+ useNaivePaginatedTable({
+ api: () => fetchGetJudgeDetailsList(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: 'judgeId',
+ title: '鍒ゅ畾涓绘爣璇�',
+ align: 'center',
+ minWidth: 120
+ },
+ {
+ key: 'itemCod',
+ title: '鍒ゅ畾椤笽TEM',
+ align: 'center',
+ minWidth: 120
+ },
+ {
+ key: 'itemName',
+ title: '鍒ゅ畾椤筃AME',
+ align: 'center',
+ minWidth: 120
+ },
+ {
+ key: 'value3',
+ title: '鏍囧噯鍊�',
+ align: 'center',
+ minWidth: 120
+ },
+ {
+ key: 'value1',
+ title: '鍒ゅ畾鍊�1',
+ align: 'center',
+ minWidth: 120
+ },
+ {
+ key: 'value2',
+ title: '鍒ゅ畾鍊�2',
+ align: 'center',
+ minWidth: 120
+ },
+ {
+ key: 'location',
+ title: '缂洪櫡浣嶇疆',
+ align: 'center',
+ minWidth: 120
+ },
+ {
+ key: 'cls',
+ title: '鍒ゅ畾绾у埆 (A,B,C,D)',
+ align: 'center',
+ minWidth: 120
+ },
+ {
+ key: 'stdscore',
+ title: '鍒嗗�兼爣鍑� (鎵e垎鏍囧噯锛屽緱鍒嗘爣鍑�),姣斿涓嶅悎鏍间竴娆℃墸澶氬皯鍒�',
+ align: 'center',
+ minWidth: 120
+ },
+ {
+ key: 'ismix',
+ title: '鏍囪姝ら」鏄惁涓哄悎鎴愰」鐩紝姣斿澶栬锛屽疄闄呬笂鍏宠仈浜嗗緢澶氬瓙椤圭洰',
+ align: 'center',
+ minWidth: 120
+ },
+ {
+ key: 'rid',
+ title: '鑻ユ瀛楁鏈塙UID鍊硷紝琛ㄦ槑瀹冨彲鑳戒负鍏朵粬椤圭洰鐨勫瓙椤癸紝姣斿鈥滅┖澶粹��,瀹冧负鐑熸敮澶栬椤圭洰鐨勫瓙椤�',
+ align: 'center',
+ minWidth: 120
+ },
+ {
+ key: 'category',
+ title: '鑼冨洿-澶囩敤',
+ align: 'center',
+ minWidth: 120
+ },
+ {
+ key: 'decisionDes',
+ title: '澶囨敞',
+ align: 'center',
+ minWidth: 120
+ },
+ {
+ key: 'updateUser',
+ title: '淇敼浜�',
+ align: 'center',
+ minWidth: 120
+ },
+ {
+ key: 'operate',
+ title: $t('common.operate'),
+ align: 'center',
+ width: 130,
+ render: row => {
+ 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={() => edit(row.id)}
+ />
+ );
+ };
+
+ 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>
+ );
+ }
+ }
+ ]
+});
+
+const { drawerVisible, operateType, editingData, handleAdd, handleEdit, checkedRowKeys, onBatchDeleted, onDeleted } =
+ useTableOperate(data, 'id', getData);
+
+async function handleBatchDelete() {
+ // request
+ const { error } = await fetchBatchDeleteJudgeDetails(checkedRowKeys.value);
+ if (error) return;
+ onBatchDeleted();
+}
+
+async function handleDelete(id: CommonType.IdType) {
+ // request
+ const { error } = await fetchBatchDeleteJudgeDetails([id]);
+ if (error) return;
+ onDeleted();
+}
+
+function edit(id: CommonType.IdType) {
+ handleEdit(id);
+}
+
+function handleExport() {
+ download('/qm/judgeDetails/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">
+ <JudgeDetailsSearch v-model:model="searchParams" @search="getDataByPage" />
+ <NCard title="鍒ゅ畾妯℃澘鏄庣粏鍒楄〃" :bordered="false" size="small" class="card-wrapper sm:flex-1-hidden">
+ <template #header-extra>
+ <TableHeaderOperation
+ v-model:columns="columnChecks"
+ :disabled-delete="checkedRowKeys.length === 0"
+ :loading="loading"
+ :show-add="hasAuth('qm:judgeDetails:add')"
+ :show-delete="hasAuth('qm:judgeDetails:remove')"
+ :show-export="hasAuth('qm:judgeDetails: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"
+ />
+ <JudgeDetailsOperateDrawer
+ 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/judge-details/modules/judge-details-operate-drawer.vue b/ruoyi-plus-soybean/src/views/qm/judge-details/modules/judge-details-operate-drawer.vue
new file mode 100644
index 0000000..fee65a3
--- /dev/null
+++ b/ruoyi-plus-soybean/src/views/qm/judge-details/modules/judge-details-operate-drawer.vue
@@ -0,0 +1,175 @@
+<script setup lang="ts">
+import { computed, ref, watch } from 'vue';
+import { jsonClone } from '@sa/utils';
+import { fetchCreateJudgeDetails, fetchUpdateJudgeDetails } from '@/service/api/qm/judge-details';
+import { useFormRules, useNaiveForm } from '@/hooks/common/form';
+import { $t } from '@/locales';
+
+defineOptions({
+ name: 'JudgeDetailsOperateDrawer'
+});
+
+interface Props {
+ /** the type of operation */
+ operateType: NaiveUI.TableOperateType;
+ /** the edit row data */
+ rowData?: Api.Qm.JudgeDetails | 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.Qm.JudgeDetailsOperateParams;
+
+const model = ref<Model>(createDefaultModel());
+
+function createDefaultModel(): Model {
+ return {
+ id: '',
+ judgeId: '',
+ itemCod: '',
+ itemName: '',
+ value3: null,
+ value1: null,
+ value2: null,
+ location: '',
+ cls: '',
+ stdscore: null,
+ ismix: null,
+ rid: '',
+ category: null,
+ decisionDes: '',
+ updateUser: '',
+ };
+}
+
+type RuleKey = Extract<
+ keyof Model,
+ | 'id'
+>;
+
+const rules: Record<RuleKey, App.Global.FormRule> = {
+};
+
+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, judgeId, itemCod, itemName, value3, value1, value2, location, cls, stdscore, ismix, rid, category, decisionDes, updateUser } = model.value;
+
+ // request
+ if (props.operateType === 'add') {
+ const { error } = await fetchCreateJudgeDetails({ id, judgeId, itemCod, itemName, value3, value1, value2, location, cls, stdscore, ismix, rid, category, decisionDes, updateUser });
+ if (error) return;
+ }
+
+ if (props.operateType === 'edit') {
+ const { error } = await fetchUpdateJudgeDetails({ id, judgeId, itemCod, itemName, value3, value1, value2, location, cls, stdscore, ismix, rid, category, decisionDes, updateUser });
+ 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="id">
+ <NInput v-model:value="model.id" placeholder="璇疯緭鍏ョ紪鐮�" />
+ </NFormItem>
+ <NFormItem label="鍒ゅ畾涓绘爣璇�" path="judgeId">
+ <NInput v-model:value="model.judgeId" placeholder="璇疯緭鍏ュ垽瀹氫富鏍囪瘑" />
+ </NFormItem>
+ <NFormItem label="鍒ゅ畾椤笽TEM" path="itemCod">
+ <NInput v-model:value="model.itemCod" placeholder="璇疯緭鍏ュ垽瀹氶」ITEM" />
+ </NFormItem>
+ <NFormItem label="鍒ゅ畾椤筃AME" path="itemName">
+ <NInput v-model:value="model.itemName" placeholder="璇疯緭鍏ュ垽瀹氶」NAME" />
+ </NFormItem>
+ <NFormItem label="鏍囧噯鍊�" path="value3">
+ <NInput v-model:value="model.value3" placeholder="璇疯緭鍏ユ爣鍑嗗��" />
+ </NFormItem>
+ <NFormItem label="鍒ゅ畾鍊�1" path="value1">
+ <NInput v-model:value="model.value1" placeholder="璇疯緭鍏ュ垽瀹氬��1" />
+ </NFormItem>
+ <NFormItem label="鍒ゅ畾鍊�2" path="value2">
+ <NInput v-model:value="model.value2" placeholder="璇疯緭鍏ュ垽瀹氬��2" />
+ </NFormItem>
+ <NFormItem label="缂洪櫡浣嶇疆" path="location">
+ <NInput v-model:value="model.location" placeholder="璇疯緭鍏ョ己闄蜂綅缃�" />
+ </NFormItem>
+ <NFormItem label="鍒ゅ畾绾у埆 (A,B,C,D)" path="cls">
+ <NInput v-model:value="model.cls" placeholder="璇疯緭鍏ュ垽瀹氱骇鍒� (A,B,C,D)" />
+ </NFormItem>
+ <NFormItem label="鍒嗗�兼爣鍑� (鎵e垎鏍囧噯锛屽緱鍒嗘爣鍑�),姣斿涓嶅悎鏍间竴娆℃墸澶氬皯鍒�" path="stdscore">
+ <NInput v-model:value="model.stdscore" placeholder="璇疯緭鍏ュ垎鍊兼爣鍑� (鎵e垎鏍囧噯锛屽緱鍒嗘爣鍑�),姣斿涓嶅悎鏍间竴娆℃墸澶氬皯鍒�" />
+ </NFormItem>
+ <NFormItem label="鏍囪姝ら」鏄惁涓哄悎鎴愰」鐩紝姣斿澶栬锛屽疄闄呬笂鍏宠仈浜嗗緢澶氬瓙椤圭洰" path="ismix">
+ <NInput v-model:value="model.ismix" placeholder="璇疯緭鍏ユ爣璁版椤规槸鍚︿负鍚堟垚椤圭洰锛屾瘮濡傚瑙傦紝瀹為檯涓婂叧鑱斾簡寰堝瀛愰」鐩�" />
+ </NFormItem>
+ <NFormItem label="鑻ユ瀛楁鏈塙UID鍊硷紝琛ㄦ槑瀹冨彲鑳戒负鍏朵粬椤圭洰鐨勫瓙椤癸紝姣斿鈥滅┖澶粹��,瀹冧负鐑熸敮澶栬椤圭洰鐨勫瓙椤�" path="rid">
+ <NInput v-model:value="model.rid" placeholder="璇疯緭鍏ヨ嫢姝ゅ瓧娈垫湁UUID鍊硷紝琛ㄦ槑瀹冨彲鑳戒负鍏朵粬椤圭洰鐨勫瓙椤癸紝姣斿鈥滅┖澶粹��,瀹冧负鐑熸敮澶栬椤圭洰鐨勫瓙椤�" />
+ </NFormItem>
+ <NFormItem label="鑼冨洿-澶囩敤" path="category">
+ <NInput v-model:value="model.category" placeholder="璇疯緭鍏ヨ寖鍥�-澶囩敤" />
+ </NFormItem>
+ <NFormItem label="澶囨敞" path="decisionDes">
+ <NInput v-model:value="model.decisionDes" placeholder="璇疯緭鍏ュ娉�" />
+ </NFormItem>
+ <NFormItem label="淇敼浜�" path="updateUser">
+ <NInput v-model:value="model.updateUser" placeholder="璇疯緭鍏ヤ慨鏀逛汉" />
+ </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/qm/judge-details/modules/judge-details-search.vue b/ruoyi-plus-soybean/src/views/qm/judge-details/modules/judge-details-search.vue
new file mode 100644
index 0000000..df74c16
--- /dev/null
+++ b/ruoyi-plus-soybean/src/views/qm/judge-details/modules/judge-details-search.vue
@@ -0,0 +1,83 @@
+<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: 'JudgeDetailsSearch'
+});
+
+interface Emits {
+ (e: 'search'): void;
+}
+
+const emit = defineEmits<Emits>();
+
+const { formRef, validate, restoreValidation } = useNaiveForm();
+
+const model = defineModel<Api.Qm.JudgeDetailsSearchParams>('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-judge-details-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="judgeId" class="pr-24px">
+ <NInput v-model:value="model.judgeId" placeholder="璇疯緭鍏ュ垽瀹氫富鏍囪瘑" />
+ </NFormItemGi>
+ <NFormItemGi span="24 s:12 m:6" label="鍒ゅ畾椤笽TEM" label-width="auto" path="itemCod" class="pr-24px">
+ <NInput v-model:value="model.itemCod" placeholder="璇疯緭鍏ュ垽瀹氶」ITEM" />
+ </NFormItemGi>
+ <NFormItemGi span="24 s:12 m:6" label="鍒ゅ畾椤筃AME" label-width="auto" path="itemName" class="pr-24px">
+ <NInput v-model:value="model.itemName" placeholder="璇疯緭鍏ュ垽瀹氶」NAME" />
+ </NFormItemGi>
+ <NFormItemGi span="24 s:12 m:6" label="鍒ゅ畾绾у埆 (A,B,C,D)" label-width="auto" path="cls" class="pr-24px">
+ <NInput v-model:value="model.cls" placeholder="璇疯緭鍏ュ垽瀹氱骇鍒� (A,B,C,D)" />
+ </NFormItemGi>
+ <NFormItemGi span="24 s:12 m:6" label="鏍囪姝ら」鏄惁涓哄悎鎴愰」鐩紝姣斿澶栬锛屽疄闄呬笂鍏宠仈浜嗗緢澶氬瓙椤圭洰" label-width="auto" path="ismix" class="pr-24px">
+ <NInput v-model:value="model.ismix" placeholder="璇疯緭鍏ユ爣璁版椤规槸鍚︿负鍚堟垚椤圭洰锛屾瘮濡傚瑙傦紝瀹為檯涓婂叧鑱斾簡寰堝瀛愰」鐩�" />
+ </NFormItemGi>
+ <NFormItemGi :show-feedback="false" span="24" class="pr-24px">
+ <NSpace class="w-full" justify="end">
+ <NButton @click="reset">
+ <template #icon>
+ <icon-ic-round-refresh class="text-icon" />
+ </template>
+ {{ $t('common.reset') }}
+ </NButton>
+ <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/index.vue b/ruoyi-plus-soybean/src/views/qm/judge/index.vue
new file mode 100644
index 0000000..80ff821
--- /dev/null
+++ b/ruoyi-plus-soybean/src/views/qm/judge/index.vue
@@ -0,0 +1,323 @@
+<script setup lang="tsx">
+import { ref } from 'vue';
+import { NDivider, NTooltip } from 'naive-ui';
+import { fetchBatchDeleteJudge, fetchGetJudgeList } from '@/service/api/qm/judge';
+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 JudgeOperateDrawer from './modules/judge-operate-drawer.vue';
+import JudgeSearch from './modules/judge-search.vue';
+
+defineOptions({
+ name: 'JudgeList'
+});
+
+
+const appStore = useAppStore();
+const { download } = useDownload();
+const { hasAuth } = useAuth();
+
+const searchParams = ref<Api.Qm.JudgeSearchParams>({
+ pageNum: 1,
+ pageSize: 10,
+ matCode: null,
+ matName: null,
+ judgeName: null,
+ category: 0, // 榛樿閫夋嫨鎴愬搧
+ status: -1, // 榛樿閫夋嫨鍏ㄩ儴
+ params: {}
+});
+
+const { columns, columnChecks, data, getData, getDataByPage, loading, mobilePagination, scrollX } =
+ useNaivePaginatedTable({
+ api: () => {
+ const params = { ...searchParams.value };
+ if (params.status === -1) {
+ delete params.status;
+ }
+ return fetchGetJudgeList(params);
+ },
+ 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: 'judgeName',
+ title: '鍒ゅ畾鍚嶇О',
+ align: 'center',
+ width: 200,
+ render: row => (
+ <NTooltip trigger="hover">
+ {{
+ trigger: () => (
+ <div
+ style={{
+ whiteSpace: 'nowrap',
+ overflow: 'hidden',
+ textOverflow: 'ellipsis'
+ }}
+ >
+ {row.judgeName}
+ </div>
+ ),
+ default: () => row.judgeName
+ }}
+ </NTooltip>
+ )
+ },
+ {
+ key: 'category',
+ title: '鐗╂枡绫诲瀷',
+ align: 'center',
+ width: 100,
+ render: row => {
+ if (row.category === 0) {
+ return '鎴愬搧';
+ }
+ if (row.category === 1) {
+ return '杈呮枡';
+ }
+ return String(row.category);
+ }
+ },
+ {
+ key: 'typeName',
+ title: '杈呮枡绫诲瀷',
+ align: 'center',
+ width: 100,
+ render: row => (
+ <NTooltip trigger="hover">
+ {{
+ trigger: () => (
+ <div
+ style={{
+ whiteSpace: 'nowrap',
+ overflow: 'hidden',
+ textOverflow: 'ellipsis'
+ }}
+ >
+ {row.typeName}
+ </div>
+ ),
+ default: () => row.typeName
+ }}
+ </NTooltip>
+ )
+ },
+ {
+ key: 'matName',
+ title: '鐗╂枡鐗屽彿',
+ align: 'center',
+ width: 180,
+ render: row => (
+ <NTooltip trigger="hover">
+ {{
+ trigger: () => (
+ <div
+ style={{
+ whiteSpace: 'nowrap',
+ overflow: 'hidden',
+ textOverflow: 'ellipsis'
+ }}
+ >
+ {row.matName}
+ </div>
+ ),
+ default: () => row.matName
+ }}
+ </NTooltip>
+ )
+ },
+ {
+ key: 'matCode',
+ title: '鐗╂枡鐗屽彿浠g爜',
+ align: 'center',
+ width: 150
+ },
+ {
+ key: 'version',
+ title: '鐗堟湰鍙�',
+ align: 'center',
+ width: 100
+ },
+ {
+ key: 'status',
+ title: '鐘舵��',
+ align: 'center',
+ width: 100,
+ render: row => {
+ if (row.status === 1) {
+ return '鍚敤';
+ }
+ if (row.status === 0) {
+ return '褰掓。';
+ }
+ return String(row.status);
+ }
+ },
+ {
+ key: 'stdName',
+ title: '鍒ゅ畾瑙勭▼',
+ align: 'center',
+ width: 200
+ },
+ {
+ key: 'cdate',
+ title: '鍒涘缓鏃堕棿',
+ align: 'center',
+ width: 180
+ },
+ {
+ key: 'oper',
+ title: '鎿嶄綔浜�',
+ align: 'center',
+ width: 100
+ },
+ {
+ key: 'des',
+ title: '澶囨敞',
+ align: 'center',
+ width: 200
+ },
+ {
+ key: 'operate',
+ title: $t('common.operate'),
+ align: 'center',
+ fixed: 'right',
+ width: 130,
+ render: row => {
+ const divider = () => {
+ if (!hasAuth('qm:judge:edit') || !hasAuth('qm:judge:remove')) {
+ return null;
+ }
+ return <NDivider vertical />;
+ };
+
+ const editBtn = () => {
+ if (!hasAuth('qm:judge: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:judge: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 fetchBatchDeleteJudge(checkedRowKeys.value);
+ if (error) return;
+ onBatchDeleted();
+}
+
+async function handleDelete(id: CommonType.IdType) {
+ // request
+ const { error } = await fetchBatchDeleteJudge([id]);
+ if (error) return;
+ onDeleted();
+}
+
+function edit(id: CommonType.IdType) {
+ handleEdit(id);
+}
+
+function handleExport() {
+ download('/qm/judge/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">
+ <JudgeSearch v-model:model="searchParams" @search="getDataByPage" />
+ <NCard title="鍒ゅ畾渚濇嵁鍒楄〃" :bordered="false" size="small" class="card-wrapper sm:flex-1-hidden">
+ <template #header-extra>
+ <TableHeaderOperation
+ v-model:columns="columnChecks"
+ :disabled-delete="checkedRowKeys.length === 0"
+ :loading="loading"
+ :show-add="hasAuth('qm:judge:add')"
+ :show-delete="hasAuth('qm:judge:remove')"
+ :show-export="hasAuth('qm:judge:export')"
+ @add="handleAdd"
+ @delete="handleBatchDelete"
+ @export="handleExport"
+ @refresh="getData"
+ />
+ </template>
+ <NDataTable
+ v-model:checked-row-keys="checkedRowKeys"
+ :columns="columns"
+ :data="data"
+ size="small"
+ :flex-height="!appStore.isMobile"
+ :scroll-x="scrollX"
+ :loading="loading"
+ remote
+ :row-key="row => row.id"
+ :pagination="mobilePagination"
+ class="sm:h-full"
+ />
+ <JudgeOperateDrawer
+ v-model:visible="drawerVisible"
+ :operate-type="operateType"
+ :row-data="editingData"
+ @submitted="getDataByPage"
+ />
+ </NCard>
+ </div>
+</template>
+
+<style scoped></style>
diff --git a/ruoyi-plus-soybean/src/views/qm/judge/modules/judge-operate-drawer.vue b/ruoyi-plus-soybean/src/views/qm/judge/modules/judge-operate-drawer.vue
new file mode 100644
index 0000000..2eb4cbb
--- /dev/null
+++ b/ruoyi-plus-soybean/src/views/qm/judge/modules/judge-operate-drawer.vue
@@ -0,0 +1,212 @@
+<script setup lang="ts">
+import { computed, ref, watch, onMounted } from 'vue';
+import { NSelect, SelectOption } from 'naive-ui';
+import { jsonClone } from '@sa/utils';
+import { fetchCreateJudge, fetchUpdateJudge } from '@/service/api/qm/judge';
+import { fetchGetStdList } from '@/service/api/qm/std';
+
+import { useFormRules, useNaiveForm } from '@/hooks/common/form';
+import { $t } from '@/locales';
+
+defineOptions({
+ name: 'JudgeOperateDrawer'
+});
+
+interface Props {
+ /** the type of operation */
+ operateType: NaiveUI.TableOperateType;
+ /** the edit row data */
+ rowData?: Api.Qm.Judge | 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];
+});
+
+const statusOptions: SelectOption[] = [
+ { label: '鍚敤', value: 1 },
+ { label: '褰掓。', value: 0 }
+];
+
+const categoryOptions: SelectOption[] = [
+ { label: '鎴愬搧', value: 0 },
+ { label: '杈呮枡', value: 1 }
+];
+
+
+const stdOptions = ref<SelectOption[]>([]);
+
+type Model = Api.Qm.JudgeOperateParams;
+
+const model = ref<Model>(createDefaultModel());
+
+function createDefaultModel(): Model {
+ return {
+ matCode: '',
+ matName: '',
+ status: null,
+ stdCod: '',
+ cdate: null,
+ oper: '',
+ des: '',
+ category: null,
+ typeCode: '',
+ typeName: '',
+ judgeName: ''
+ };
+}
+
+type RuleKey = Extract<
+ keyof Model,
+ | 'id'
+>;
+
+const rules: Record<RuleKey, App.Global.FormRule> = {
+};
+
+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 fetchStdOptions() {
+ const response = await fetchGetStdList({ category: model.value.category });
+ const stdListData = response.data?.rows || [];
+
+ if (stdListData.length > 0) {
+ stdOptions.value = stdListData.map((item: Api.Qm.Std) => ({
+ label: item.stdName + item.stdCode,
+ value: String(item.id)
+ }));
+ }
+}
+
+async function handleSubmit() {
+ await validate();
+
+ const { matCode, matName, status, stdCod, cdate, oper, des, category, typeCode, typeName, judgeName } = model.value;
+
+ // request
+ if (props.operateType === 'add') {
+ const { error } = await fetchCreateJudge({ matCode, matName, status, stdCod, cdate, oper, des, category, typeCode, typeName, judgeName });
+ if (error) return;
+ }
+
+ if (props.operateType === 'edit') {
+ const { error } = await fetchUpdateJudge({ matCode, matName, status, stdCod, cdate, oper, des, category, typeCode, typeName, judgeName });
+ if (error) return;
+ }
+
+ window.$message?.success($t('common.updateSuccess'));
+ closeDrawer();
+ emit('submitted');
+}
+
+watch(visible, () => {
+ if (visible.value) {
+ handleUpdateModelWhenEdit();
+ restoreValidation();
+
+ // Fetch stdOptions when the drawer becomes visible
+ fetchStdOptions();
+ }
+});
+
+watch(() => model.value.category, () => {
+ fetchStdOptions();
+});
+</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="judgeName">
+ <NInput v-model:value="model.judgeName" placeholder="璇疯緭鍏ュ垽瀹氬悕绉�" />
+ </NFormItem>
+ <NFormItem label="鐗╂枡鐗屽彿浠g爜" path="matCode">
+ <NInput v-model:value="model.matCode" placeholder="璇疯緭鍏ョ墿鏂欑墝鍙蜂唬鐮�" />
+ </NFormItem>
+ <NFormItem label="鐗╂枡鐗屽彿" path="matName">
+ <NInput v-model:value="model.matName" placeholder="璇疯緭鍏ョ墿鏂欑墝鍙�" />
+ </NFormItem>
+ <NFormItem label="鐗╂枡绫诲瀷" path="category">
+ <NSelect
+ v-model:value="model.category"
+ :options="categoryOptions"
+ placeholder="璇烽�夋嫨鐗╂枡绫诲瀷"
+ />
+ </NFormItem>
+ <NFormItem label="鐘舵��" path="status">
+ <NSelect
+ v-model:value="model.status"
+ :options="statusOptions"
+ placeholder="璇烽�夋嫨鐘舵��"
+ />
+ </NFormItem>
+ <NFormItem label="鍒ゅ畾瑙勭▼" path="stdCod">
+ <NSelect
+ v-model:value="model.stdCod"
+ :options="stdOptions"
+ placeholder="璇烽�夋嫨鍒ゅ畾瑙勭▼"
+ />
+ </NFormItem>
+ <NFormItem label="鍒涘缓鏃堕棿" path="cdate">
+ <NDatePicker
+ v-model:formatted-value="model.cdate"
+ type="datetime"
+ value-format="yyyy-MM-dd HH:mm:ss"
+ clearable
+ />
+ </NFormItem>
+ <NFormItem label="鐢熸晥浜�/鎿嶄綔浜哄憳" path="oper">
+ <NInput v-model:value="model.oper" placeholder="璇疯緭鍏ョ敓鏁堜汉/鎿嶄綔浜哄憳" />
+ </NFormItem>
+ <NFormItem label="澶囨敞" path="des">
+ <NInput v-model:value="model.des" placeholder="璇疯緭鍏ュ娉�" />
+ </NFormItem>
+
+ <NFormItem label="杈呮枡绫诲瀷缂栫爜" path="typeCode">
+ <NInput v-model:value="model.typeCode" placeholder="璇疯緭鍏ヨ緟鏂欑被鍨嬬紪鐮�" />
+ </NFormItem>
+ <NFormItem label="杈呮枡绫诲瀷鍚嶇О" path="typeName">
+ <NInput v-model:value="model.typeName" placeholder="璇疯緭鍏ヨ緟鏂欑被鍨嬪悕绉�" />
+ </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/qm/judge/modules/judge-search.vue b/ruoyi-plus-soybean/src/views/qm/judge/modules/judge-search.vue
new file mode 100644
index 0000000..235b8a3
--- /dev/null
+++ b/ruoyi-plus-soybean/src/views/qm/judge/modules/judge-search.vue
@@ -0,0 +1,98 @@
+<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: 'JudgeSearch'
+});
+
+interface Emits {
+ (e: 'search'): void;
+}
+
+const emit = defineEmits<Emits>();
+
+const { formRef, validate, restoreValidation } = useNaiveForm();
+
+const model = defineModel<Api.Qm.JudgeSearchParams>('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-judge-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="category" class="pr-24px">
+ <NSelect
+ v-model:value="model.category"
+ :options="[
+ { label: '鎴愬搧', value: 0 },
+ { label: '杈呮枡', value: 1 }
+ ]"
+ placeholder="璇烽�夋嫨鐗╂枡绫诲瀷"
+ />
+ </NFormItemGi>
+
+ <NFormItemGi span="24 s:12 m:6" label="鐗╂枡鐗屽彿浠g爜" label-width="auto" path="matCode" class="pr-24px">
+ <NInput v-model:value="model.matCode" placeholder="璇疯緭鍏ョ墿鏂欑墝鍙蜂唬鐮�" />
+ </NFormItemGi>
+ <NFormItemGi span="24 s:12 m:6" label="鐗╂枡鐗屽彿" label-width="auto" path="matName" class="pr-24px">
+ <NInput v-model:value="model.matName" placeholder="璇疯緭鍏ョ墿鏂欑墝鍙�" />
+ </NFormItemGi>
+ <NFormItemGi span="24 s:12 m:6" label="鍒ゅ畾鍚嶇О" label-width="auto" path="judgeName" class="pr-24px">
+ <NInput v-model:value="model.judgeName" placeholder="璇疯緭鍏ュ垽瀹氬悕绉�" />
+ </NFormItemGi>
+ <NFormItemGi span="24 s:12 m:6" label="鏄惁鍚敤" label-width="auto" path="status" class="pr-24px">
+ <NSelect
+ v-model:value="model.status"
+ :options="[
+ { label: '鍏ㄩ儴', value: -1 },
+ { label: '鍚敤', value: 1 },
+ { label: '褰掓。', value: 0 }
+ ]"
+ />
+ </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