From 5740eb93928f3cb8e17c3fa5f082f42072ebbc31 Mon Sep 17 00:00:00 2001
From: zhuguifei <312353457@qq.com>
Date: 星期四, 12 三月 2026 13:03:17 +0800
Subject: [PATCH] feat: 新增基础数据维护-判定规程维护界面和后端接口
---
ruoyi-plus-soybean/src/views/qm/std/index.vue | 229 ++++++++
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/qm/service/IQmStdService.java | 68 ++
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/qm/service/impl/QmStdServiceImpl.java | 139 +++++
ruoyi-plus-soybean/src/views/qm/std/modules/std-operate-drawer.vue | 179 ++++++
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/qm/mapper/QmStdMapper.java | 15
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/qm/domain/bo/QmStdBo.java | 97 +++
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/resources/mapper/qa/qm/QmStdMapper.xml | 6
ruoyi-plus-soybean/src/views/analy/store-silk/modules/store-silk-sankey.vue | 282 ++++++++++
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/qm/domain/QmStd.java | 97 +++
ruoyi-plus-soybean/src/service/api/qm/std.ts | 35 +
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/qm/domain/vo/QmStdVo.java | 120 ++++
ruoyi-plus-soybean/src/typings/api/qm.std.api.d.ts | 85 +++
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/qm/controller/QmStdController.java | 105 ++++
ruoyi-plus-soybean/src/views/qm/std/modules/std-search.vue | 96 +++
14 files changed, 1,553 insertions(+), 0 deletions(-)
diff --git a/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/qm/controller/QmStdController.java b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/qm/controller/QmStdController.java
new file mode 100644
index 0000000..1343e1c
--- /dev/null
+++ b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/qm/controller/QmStdController.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.dromara.qa.qm.domain.bo.QmStdBo;
+import org.dromara.qa.qm.domain.vo.QmStdVo;
+import org.dromara.qa.qm.service.IQmStdService;
+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.common.mybatis.core.page.TableDataInfo;
+
+/**
+ * 鍒ゅ畾瑙勭▼
+ *
+ * @author zhuguifei
+ * @date 2026-03-11
+ */
+@Validated
+@RequiredArgsConstructor
+@RestController
+@RequestMapping("/qm/std")
+public class QmStdController extends BaseController {
+
+ private final IQmStdService qmStdService;
+
+ /**
+ * 鏌ヨ鍒ゅ畾瑙勭▼鍒楄〃
+ */
+ @SaCheckPermission("qm:std:list")
+ @GetMapping("/list")
+ public TableDataInfo<QmStdVo> list(QmStdBo bo, PageQuery pageQuery) {
+ return qmStdService.queryPageList(bo, pageQuery);
+ }
+
+ /**
+ * 瀵煎嚭鍒ゅ畾瑙勭▼鍒楄〃
+ */
+ @SaCheckPermission("qm:std:export")
+ @Log(title = "鍒ゅ畾瑙勭▼", businessType = BusinessType.EXPORT)
+ @PostMapping("/export")
+ public void export(QmStdBo bo, HttpServletResponse response) {
+ List<QmStdVo> list = qmStdService.queryList(bo);
+ ExcelUtil.exportExcel(list, "鍒ゅ畾瑙勭▼", QmStdVo.class, response);
+ }
+
+ /**
+ * 鑾峰彇鍒ゅ畾瑙勭▼璇︾粏淇℃伅
+ *
+ * @param id 涓婚敭
+ */
+ @SaCheckPermission("qm:std:query")
+ @GetMapping("/{id}")
+ public R<QmStdVo> getInfo(@NotNull(message = "涓婚敭涓嶈兘涓虹┖")
+ @PathVariable String id) {
+ return R.ok(qmStdService.queryById(id));
+ }
+
+ /**
+ * 鏂板鍒ゅ畾瑙勭▼
+ */
+ @SaCheckPermission("qm:std:add")
+ @Log(title = "鍒ゅ畾瑙勭▼", businessType = BusinessType.INSERT)
+ @RepeatSubmit()
+ @PostMapping()
+ public R<Void> add(@Validated(AddGroup.class) @RequestBody QmStdBo bo) {
+ return toAjax(qmStdService.insertByBo(bo));
+ }
+
+ /**
+ * 淇敼鍒ゅ畾瑙勭▼
+ */
+ @SaCheckPermission("qm:std:edit")
+ @Log(title = "鍒ゅ畾瑙勭▼", businessType = BusinessType.UPDATE)
+ @RepeatSubmit()
+ @PutMapping()
+ public R<Void> edit(@Validated(EditGroup.class) @RequestBody QmStdBo bo) {
+ return toAjax(qmStdService.updateByBo(bo));
+ }
+
+ /**
+ * 鍒犻櫎鍒ゅ畾瑙勭▼
+ *
+ * @param ids 涓婚敭涓�
+ */
+ @SaCheckPermission("qm:std:remove")
+ @Log(title = "鍒ゅ畾瑙勭▼", businessType = BusinessType.DELETE)
+ @DeleteMapping("/{ids}")
+ public R<Void> remove(@NotEmpty(message = "涓婚敭涓嶈兘涓虹┖")
+ @PathVariable String[] ids) {
+ return toAjax(qmStdService.deleteWithValidByIds(List.of(ids), true));
+ }
+}
diff --git a/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/qm/domain/QmStd.java b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/qm/domain/QmStd.java
new file mode 100644
index 0000000..e542fd3
--- /dev/null
+++ b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/qm/domain/QmStd.java
@@ -0,0 +1,97 @@
+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_std
+ *
+ * @author zhuguifei
+ * @date 2026-03-11
+ */
+@Data
+@TableName("qm_std")
+public class QmStd {
+
+ @Serial
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * 缂栫爜
+ */
+ @TableId(value = "id")
+ private String id;
+
+ /**
+ * 瑙勭▼浠g爜
+ */
+ private String stdCode;
+
+ /**
+ * 瑙勭▼鍚嶇О
+ */
+ private String stdName;
+
+ /**
+ * 鍒涘缓鏃堕棿
+ */
+ private Date cdate;
+
+ /**
+ * 鐗堟湰鍙�
+ */
+ private Long ver;
+
+ /**
+ * 鍚敤鏍囪瘑
+ */
+ private Long enable;
+
+ /**
+ * 妫�楠岄」鐩甤ode
+ */
+ private String itemCod;
+
+ /**
+ * 妫�楠岄」鐩悕绉�
+ */
+ private String itemNam;
+
+ /**
+ * 鍒ゅ畾绫诲瀷 0-鎵嬪姩鍒ゅ畾,1-涓婁笅闄愬垽瀹�,2-骞冲潎鍊煎垽瀹�,3-SD鍊煎垽瀹�,4-CV鍊煎垽瀹�,5-瓒呮爣鏁板垽瀹�
+ */
+ private Long typ;
+
+ /**
+ * 澶囨敞
+ */
+ private String stdDes;
+
+ /**
+ * 妫�楠岀骇鍒�
+ */
+ private Long checkLevel;
+
+ /**
+ * 绫诲瀷 锛� 0-鎴愬搧 1-杈呮枡
+ */
+ private Long category;
+
+ /**
+ * $column.columnComment
+ */
+ private String matfltype;
+
+ /**
+ * $column.columnComment
+ */
+ private String matflname;
+
+
+}
diff --git a/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/qm/domain/bo/QmStdBo.java b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/qm/domain/bo/QmStdBo.java
new file mode 100644
index 0000000..455f462
--- /dev/null
+++ b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/qm/domain/bo/QmStdBo.java
@@ -0,0 +1,97 @@
+package org.dromara.qa.qm.domain.bo;
+
+import org.dromara.qa.qm.domain.QmStd;
+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_std
+ *
+ * @author zhuguifei
+ * @date 2026-03-11
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@AutoMapper(target = QmStd.class, reverseConvertGenerate = false)
+public class QmStdBo extends BaseEntity {
+
+ /**
+ * 缂栫爜
+ */
+ @NotBlank(message = "缂栫爜涓嶈兘涓虹┖", groups = { EditGroup.class })
+ private String id;
+
+ /**
+ * 瑙勭▼浠g爜
+ */
+ private String stdCode;
+
+ /**
+ * 瑙勭▼鍚嶇О
+ */
+ private String stdName;
+
+ /**
+ * 鍒涘缓鏃堕棿
+ */
+ private Date cdate;
+
+ /**
+ * 鐗堟湰鍙�
+ */
+ private Long ver;
+
+ /**
+ * 鍚敤鏍囪瘑
+ */
+ private Long enable;
+
+ /**
+ * 妫�楠岄」鐩甤ode
+ */
+ private String itemCod;
+
+ /**
+ * 妫�楠岄」鐩悕绉�
+ */
+ private String itemNam;
+
+ /**
+ * 鍒ゅ畾绫诲瀷 0-鎵嬪姩鍒ゅ畾,1-涓婁笅闄愬垽瀹�,2-骞冲潎鍊煎垽瀹�,3-SD鍊煎垽瀹�,4-CV鍊煎垽瀹�,5-瓒呮爣鏁板垽瀹�
+ */
+ private Long typ;
+
+ /**
+ * 澶囨敞
+ */
+ private String stdDes;
+
+ /**
+ * 妫�楠岀骇鍒�
+ */
+ private Long checkLevel;
+
+ /**
+ * 绫诲瀷 锛� 0-鎴愬搧 1-杈呮枡
+ */
+ private Long category;
+
+ /**
+ * $column.columnComment
+ */
+ private String matfltype;
+
+ /**
+ * $column.columnComment
+ */
+ private String matflname;
+
+
+}
diff --git a/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/qm/domain/vo/QmStdVo.java b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/qm/domain/vo/QmStdVo.java
new file mode 100644
index 0000000..dc70c62
--- /dev/null
+++ b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/qm/domain/vo/QmStdVo.java
@@ -0,0 +1,120 @@
+package org.dromara.qa.qm.domain.vo;
+
+import java.util.Date;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import org.dromara.qa.qm.domain.QmStd;
+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_std
+ *
+ * @author zhuguifei
+ * @date 2026-03-11
+ */
+@Data
+@ExcelIgnoreUnannotated
+@AutoMapper(target = QmStd.class)
+public class QmStdVo implements Serializable {
+
+ @Serial
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * 缂栫爜
+ */
+ @ExcelProperty(value = "缂栫爜")
+ private String id;
+
+ /**
+ * 瑙勭▼浠g爜
+ */
+ @ExcelProperty(value = "瑙勭▼浠g爜")
+ private String stdCode;
+
+ /**
+ * 瑙勭▼鍚嶇О
+ */
+ @ExcelProperty(value = "瑙勭▼鍚嶇О")
+ private String stdName;
+
+ /**
+ * 鍒涘缓鏃堕棿
+ */
+ @ExcelProperty(value = "鍒涘缓鏃堕棿")
+ private Date cdate;
+
+ /**
+ * 鐗堟湰鍙�
+ */
+ @ExcelProperty(value = "鐗堟湰鍙�")
+ private Long ver;
+
+ /**
+ * 鍚敤鏍囪瘑
+ */
+ @ExcelProperty(value = "鍚敤鏍囪瘑")
+ private Long enable;
+
+ /**
+ * 妫�楠岄」鐩甤ode
+ */
+ @ExcelProperty(value = "妫�楠岄」鐩甤ode")
+ private String itemCod;
+
+ /**
+ * 妫�楠岄」鐩悕绉�
+ */
+ @ExcelProperty(value = "妫�楠岄」鐩悕绉�")
+ private String itemNam;
+
+ /**
+ * 鍒ゅ畾绫诲瀷 0-鎵嬪姩鍒ゅ畾,1-涓婁笅闄愬垽瀹�,2-骞冲潎鍊煎垽瀹�,3-SD鍊煎垽瀹�,4-CV鍊煎垽瀹�,5-瓒呮爣鏁板垽瀹�
+ */
+ @ExcelProperty(value = "鍒ゅ畾绫诲瀷 0-鎵嬪姩鍒ゅ畾,1-涓婁笅闄愬垽瀹�,2-骞冲潎鍊煎垽瀹�,3-SD鍊煎垽瀹�,4-CV鍊煎垽瀹�,5-瓒呮爣鏁板垽瀹�")
+ private Long typ;
+
+ /**
+ * 澶囨敞
+ */
+ @ExcelProperty(value = "澶囨敞")
+ private String stdDes;
+
+ /**
+ * 妫�楠岀骇鍒�
+ */
+ @ExcelProperty(value = "妫�楠岀骇鍒�")
+ private Long checkLevel;
+
+ /**
+ * 绫诲瀷 锛� 0-鎴愬搧 1-杈呮枡
+ */
+ @ExcelProperty(value = "绫诲瀷 锛� 0-鎴愬搧 1-杈呮枡")
+ private Long category;
+
+ /**
+ * $column.columnComment
+ */
+ @ExcelProperty(value = "${comment}", converter = ExcelDictConvert.class)
+ @ExcelDictFormat(readConverterExp = "$column.readConverterExp()")
+ private String matfltype;
+
+ /**
+ * $column.columnComment
+ */
+ @ExcelProperty(value = "${comment}", converter = ExcelDictConvert.class)
+ @ExcelDictFormat(readConverterExp = "$column.readConverterExp()")
+ private String matflname;
+
+
+}
diff --git a/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/qm/mapper/QmStdMapper.java b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/qm/mapper/QmStdMapper.java
new file mode 100644
index 0000000..ba24c78
--- /dev/null
+++ b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/qm/mapper/QmStdMapper.java
@@ -0,0 +1,15 @@
+package org.dromara.qa.qm.mapper;
+
+import org.dromara.qa.qm.domain.QmStd;
+import org.dromara.qa.qm.domain.vo.QmStdVo;
+import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
+
+/**
+ * 鍒ゅ畾瑙勭▼Mapper鎺ュ彛
+ *
+ * @author zhuguifei
+ * @date 2026-03-11
+ */
+public interface QmStdMapper extends BaseMapperPlus<QmStd, QmStdVo> {
+
+}
diff --git a/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/qm/service/IQmStdService.java b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/qm/service/IQmStdService.java
new file mode 100644
index 0000000..18498cb
--- /dev/null
+++ b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/qm/service/IQmStdService.java
@@ -0,0 +1,68 @@
+package org.dromara.qa.qm.service;
+
+import org.dromara.qa.qm.domain.vo.QmStdVo;
+import org.dromara.qa.qm.domain.bo.QmStdBo;
+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-11
+ */
+public interface IQmStdService {
+
+ /**
+ * 鏌ヨ鍒ゅ畾瑙勭▼
+ *
+ * @param id 涓婚敭
+ * @return 鍒ゅ畾瑙勭▼
+ */
+ QmStdVo queryById(String id);
+
+ /**
+ * 鍒嗛〉鏌ヨ鍒ゅ畾瑙勭▼鍒楄〃
+ *
+ * @param bo 鏌ヨ鏉′欢
+ * @param pageQuery 鍒嗛〉鍙傛暟
+ * @return 鍒ゅ畾瑙勭▼鍒嗛〉鍒楄〃
+ */
+ TableDataInfo<QmStdVo> queryPageList(QmStdBo bo, PageQuery pageQuery);
+
+ /**
+ * 鏌ヨ绗﹀悎鏉′欢鐨勫垽瀹氳绋嬪垪琛�
+ *
+ * @param bo 鏌ヨ鏉′欢
+ * @return 鍒ゅ畾瑙勭▼鍒楄〃
+ */
+ List<QmStdVo> queryList(QmStdBo bo);
+
+ /**
+ * 鏂板鍒ゅ畾瑙勭▼
+ *
+ * @param bo 鍒ゅ畾瑙勭▼
+ * @return 鏄惁鏂板鎴愬姛
+ */
+ Boolean insertByBo(QmStdBo bo);
+
+ /**
+ * 淇敼鍒ゅ畾瑙勭▼
+ *
+ * @param bo 鍒ゅ畾瑙勭▼
+ * @return 鏄惁淇敼鎴愬姛
+ */
+ Boolean updateByBo(QmStdBo 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/QmStdServiceImpl.java b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/qm/service/impl/QmStdServiceImpl.java
new file mode 100644
index 0000000..db6d78f
--- /dev/null
+++ b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/qm/service/impl/QmStdServiceImpl.java
@@ -0,0 +1,139 @@
+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.QmStdBo;
+import org.dromara.qa.qm.domain.vo.QmStdVo;
+import org.dromara.qa.qm.domain.QmStd;
+import org.dromara.qa.qm.mapper.QmStdMapper;
+import org.dromara.qa.qm.service.IQmStdService;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Collection;
+
+/**
+ * 鍒ゅ畾瑙勭▼Service涓氬姟灞傚鐞�
+ *
+ * @author zhuguifei
+ * @date 2026-03-11
+ */
+@Slf4j
+@RequiredArgsConstructor
+@Service
+public class QmStdServiceImpl implements IQmStdService {
+
+ private final QmStdMapper baseMapper;
+
+ /**
+ * 鏌ヨ鍒ゅ畾瑙勭▼
+ *
+ * @param id 涓婚敭
+ * @return 鍒ゅ畾瑙勭▼
+ */
+ @Override
+ public QmStdVo queryById(String id){
+ return baseMapper.selectVoById(id);
+ }
+
+ /**
+ * 鍒嗛〉鏌ヨ鍒ゅ畾瑙勭▼鍒楄〃
+ *
+ * @param bo 鏌ヨ鏉′欢
+ * @param pageQuery 鍒嗛〉鍙傛暟
+ * @return 鍒ゅ畾瑙勭▼鍒嗛〉鍒楄〃
+ */
+ @Override
+ public TableDataInfo<QmStdVo> queryPageList(QmStdBo bo, PageQuery pageQuery) {
+ LambdaQueryWrapper<QmStd> lqw = buildQueryWrapper(bo);
+ Page<QmStdVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
+ return TableDataInfo.build(result);
+ }
+
+ /**
+ * 鏌ヨ绗﹀悎鏉′欢鐨勫垽瀹氳绋嬪垪琛�
+ *
+ * @param bo 鏌ヨ鏉′欢
+ * @return 鍒ゅ畾瑙勭▼鍒楄〃
+ */
+ @Override
+ public List<QmStdVo> queryList(QmStdBo bo) {
+ LambdaQueryWrapper<QmStd> lqw = buildQueryWrapper(bo);
+ return baseMapper.selectVoList(lqw);
+ }
+
+ private LambdaQueryWrapper<QmStd> buildQueryWrapper(QmStdBo bo) {
+ Map<String, Object> params = bo.getParams();
+ LambdaQueryWrapper<QmStd> lqw = Wrappers.lambdaQuery();
+ lqw.orderByAsc(QmStd::getId);
+ lqw.like(StringUtils.isNotBlank(bo.getStdCode()), QmStd::getStdCode, bo.getStdCode());
+ lqw.like(StringUtils.isNotBlank(bo.getStdName()), QmStd::getStdName, bo.getStdName());
+ lqw.eq(bo.getTyp() != null, QmStd::getTyp, bo.getTyp());
+ lqw.eq(StringUtils.isNotBlank(bo.getStdDes()), QmStd::getStdDes, bo.getStdDes());
+ lqw.eq(bo.getCheckLevel() != null, QmStd::getCheckLevel, bo.getCheckLevel());
+ lqw.eq(bo.getCategory() != null, QmStd::getCategory, bo.getCategory());
+ lqw.eq(StringUtils.isNotBlank(bo.getMatfltype()), QmStd::getMatfltype, bo.getMatfltype());
+ lqw.like(StringUtils.isNotBlank(bo.getMatflname()), QmStd::getMatflname, bo.getMatflname());
+ return lqw;
+ }
+
+ /**
+ * 鏂板鍒ゅ畾瑙勭▼
+ *
+ * @param bo 鍒ゅ畾瑙勭▼
+ * @return 鏄惁鏂板鎴愬姛
+ */
+ @Override
+ public Boolean insertByBo(QmStdBo bo) {
+ QmStd add = MapstructUtils.convert(bo, QmStd.class);
+ validEntityBeforeSave(add);
+ boolean flag = baseMapper.insert(add) > 0;
+ if (flag) {
+ bo.setId(add.getId());
+ }
+ return flag;
+ }
+
+ /**
+ * 淇敼鍒ゅ畾瑙勭▼
+ *
+ * @param bo 鍒ゅ畾瑙勭▼
+ * @return 鏄惁淇敼鎴愬姛
+ */
+ @Override
+ public Boolean updateByBo(QmStdBo bo) {
+ QmStd update = MapstructUtils.convert(bo, QmStd.class);
+ validEntityBeforeSave(update);
+ return baseMapper.updateById(update) > 0;
+ }
+
+ /**
+ * 淇濆瓨鍓嶇殑鏁版嵁鏍¢獙
+ */
+ private void validEntityBeforeSave(QmStd 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/qm/QmStdMapper.xml b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/resources/mapper/qa/qm/QmStdMapper.xml
new file mode 100644
index 0000000..22bbec6
--- /dev/null
+++ b/RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/resources/mapper/qa/qm/QmStdMapper.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.QmStdMapper">
+</mapper>
diff --git a/ruoyi-plus-soybean/src/service/api/qm/std.ts b/ruoyi-plus-soybean/src/service/api/qm/std.ts
new file mode 100644
index 0000000..e2de6de
--- /dev/null
+++ b/ruoyi-plus-soybean/src/service/api/qm/std.ts
@@ -0,0 +1,35 @@
+import { request } from '@/service/request';
+
+/** 鑾峰彇鍒ゅ畾瑙勭▼鍒楄〃 */
+export function fetchGetStdList (params?: Api.Qm.StdSearchParams) {
+ return request<Api.Qm.StdList>({
+ url: '/qm/std/list',
+ method: 'get',
+ params
+ });
+}
+/** 鏂板鍒ゅ畾瑙勭▼ */
+export function fetchCreateStd (data: Api.Qm.StdOperateParams) {
+ return request<boolean>({
+ url: '/qm/std',
+ method: 'post',
+ data
+ });
+}
+
+/** 淇敼鍒ゅ畾瑙勭▼ */
+export function fetchUpdateStd (data: Api.Qm.StdOperateParams) {
+ return request<boolean>({
+ url: '/qm/std',
+ method: 'put',
+ data
+ });
+}
+
+/** 鎵归噺鍒犻櫎鍒ゅ畾瑙勭▼ */
+export function fetchBatchDeleteStd (ids: CommonType.IdType[]) {
+ return request<boolean>({
+ url: `/qm/std/${ids.join(',')}`,
+ method: 'delete'
+ });
+}
diff --git a/ruoyi-plus-soybean/src/typings/api/qm.std.api.d.ts b/ruoyi-plus-soybean/src/typings/api/qm.std.api.d.ts
new file mode 100644
index 0000000..a043dfb
--- /dev/null
+++ b/ruoyi-plus-soybean/src/typings/api/qm.std.api.d.ts
@@ -0,0 +1,85 @@
+/**
+ * Namespace Api
+ *
+ * All backend api type
+ */
+declare namespace Api {
+ /**
+ * namespace Qm
+ *
+ * backend api module: "Qm"
+ */
+ namespace Qm {
+ /** std */
+ type Std = Common.CommonRecord<{
+ /** 缂栫爜 */
+ id: CommonType.IdType;
+ /** 瑙勭▼浠g爜 */
+ stdCode: string;
+ /** 瑙勭▼鍚嶇О */
+ stdName: string;
+ /** 鍒涘缓鏃堕棿 */
+ cdate: string;
+ /** 鐗堟湰鍙� */
+ ver: number;
+ /** 鍚敤鏍囪瘑 */
+ enable: number;
+ /** 妫�楠岄」鐩甤ode */
+ itemCod: string;
+ /** 妫�楠岄」鐩悕绉� */
+ itemNam: string;
+ /** 鍒ゅ畾绫诲瀷 0-鎵嬪姩鍒ゅ畾,1-涓婁笅闄愬垽瀹�,2-骞冲潎鍊煎垽瀹�,3-SD鍊煎垽瀹�,4-CV鍊煎垽瀹�,5-瓒呮爣鏁板垽瀹� */
+ typ: number;
+ /** 澶囨敞 */
+ stdDes: string;
+ /** 妫�楠岀骇鍒� */
+ checkLevel: number;
+ /** 绫诲瀷 锛� 0-鎴愬搧 1-杈呮枡 */
+ category: number;
+ /** $column.columnComment */
+ matfltype: string;
+ /** $column.columnComment */
+ matflname: string;
+ }>;
+
+ /** std search params */
+ type StdSearchParams = CommonType.RecordNullable<
+ Pick<
+ Api.Qm.Std,
+ | 'stdCode'
+ | 'stdName'
+ | 'typ'
+ | 'stdDes'
+ | 'checkLevel'
+ | 'category'
+ | 'matfltype'
+ | 'matflname'
+ > &
+ Api.Common.CommonSearchParams
+ >;
+
+ /** std operate params */
+ type StdOperateParams = CommonType.RecordNullable<
+ Pick<
+ Api.Qm.Std,
+ | 'id'
+ | 'stdCode'
+ | 'stdName'
+ | 'cdate'
+ | 'ver'
+ | 'enable'
+ | 'itemCod'
+ | 'itemNam'
+ | 'typ'
+ | 'stdDes'
+ | 'checkLevel'
+ | 'category'
+ | 'matfltype'
+ | 'matflname'
+ >
+ >;
+
+ /** std list */
+ type StdList = Api.Common.PaginatingQueryRecord<Std>;
+ }
+}
diff --git a/ruoyi-plus-soybean/src/views/analy/store-silk/modules/store-silk-sankey.vue b/ruoyi-plus-soybean/src/views/analy/store-silk/modules/store-silk-sankey.vue
new file mode 100644
index 0000000..4021450
--- /dev/null
+++ b/ruoyi-plus-soybean/src/views/analy/store-silk/modules/store-silk-sankey.vue
@@ -0,0 +1,282 @@
+<script setup lang="ts">
+import { computed, ref, watch } from 'vue';
+import { useEcharts } from '@/hooks/common/echarts';
+
+defineOptions({
+ name: 'StoreSilkSankey'
+});
+
+interface StoreSilkDetailVo {
+ fsNum?: string;
+ siloNum?: string;
+ equNo?: string;
+ output?: number | null;
+}
+
+interface Props {
+ rollerRecordList?: StoreSilkDetailVo[];
+ packerRecordList?: StoreSilkDetailVo[];
+ rows?: any[];
+}
+
+const props = withDefaults(defineProps<Props>(), {
+ rollerRecordList: () => [],
+ packerRecordList: () => [],
+ rows: () => []
+});
+
+type SankeyType = 'roller' | 'packer';
+
+const sankeyType = ref<SankeyType>('roller');
+
+function normalizeNumber(val: unknown) {
+ const v = Number(val);
+ if (!Number.isFinite(v)) return null;
+ return v;
+}
+
+function calcRollerBox(val: unknown) {
+ if (val === null || val === undefined) return null;
+ const v = Number(val) / 50;
+ if (!Number.isFinite(v)) return null;
+ return v;
+}
+
+function calcPackerBox(val: unknown) {
+ if (val === null || val === undefined) return null;
+ const v = Number(val) / 10 / 250;
+ if (!Number.isFinite(v)) return null;
+ return v;
+}
+
+function formatTime(val: unknown) {
+ if (!val) return '';
+ const s = String(val);
+ return s.length > 19 ? s.slice(0, 19) : s;
+}
+
+function buildChartData(list: StoreSilkDetailVo[], type: SankeyType) {
+ const nodeSet = new Set<string>();
+ const linkMap = new Map<string, { source: string; target: string; value: number }>();
+ const nodeInValue = new Map<string, number>();
+ const nodeOutValue = new Map<string, number>();
+ const nodeExtra = new Map<string, any>();
+ const rows = Array.isArray(props.rows) ? props.rows : [];
+
+ function addLink(source: string, target: string, value: number) {
+ if (value <= 0) return;
+ nodeSet.add(source);
+ nodeSet.add(target);
+ nodeInValue.set(target, (nodeInValue.get(target) ?? 0) + value);
+ nodeOutValue.set(source, (nodeOutValue.get(source) ?? 0) + value);
+ const key = `${source}__->__${target}`;
+ const existed = linkMap.get(key);
+ if (existed) {
+ existed.value += value;
+ return;
+ }
+ linkMap.set(key, { source, target, value });
+ }
+
+ for (const item of list) {
+ const val = normalizeNumber(item.output);
+ if (val === null) continue;
+ if (val <= 0) continue;
+
+ const fsBase = `鍠備笣鏈� ${item.fsNum || '-'}`;
+ const siloBase = `鍌ㄤ笣鏌� ${item.siloNum || '-'}`;
+ const equ = item.equNo ? `${item.equNo}#${type === 'roller' ? '鍗锋帴鏈�' : '鍖呰鏈�'}` : type === 'roller' ? '鍗锋帴鏈�' : '鍖呰鏈�';
+ const target = equ;
+ const fs = fsBase;
+ const silo = siloBase;
+
+ addLink(fs, silo, val);
+ addLink(silo, target, val);
+
+ const parentRow = rows.find((r: any) => {
+ const dList = type === 'roller' ? r?.rollerDetailList : r?.packerDetailList;
+ if (!Array.isArray(dList)) return false;
+ return dList.some((d: any) => d?.fsNum === item.fsNum && d?.siloNum === item.siloNum && d?.equNo === item.equNo);
+ });
+
+ if (parentRow) {
+ const materialname = String(parentRow.materialname ?? '');
+ const batchcode = String(parentRow.batchcode ?? parentRow.batchCode ?? '');
+ const jobinput = Number(parentRow.jobinput);
+ const distimebegin = formatTime(parentRow.distimebegin);
+ const distimeend = formatTime(parentRow.distimeend);
+
+ nodeExtra.set(fs, {
+ kind: 'fs',
+ displayName: fsBase,
+ meta: { materialname, batchcode, jobinput }
+ });
+ nodeExtra.set(silo, {
+ kind: 'silo',
+ displayName: siloBase,
+ meta: { distimebegin, distimeend }
+ });
+ }
+ }
+
+ const nodes = Array.from(nodeSet).map(name => {
+ const inV = nodeInValue.get(name) ?? 0;
+ const outV = nodeOutValue.get(name) ?? 0;
+ const value = inV > 0 ? inV : outV;
+ return { name, value, ...(nodeExtra.get(name) ?? {}) };
+ });
+ const links = Array.from(linkMap.values());
+
+ return { nodes, links };
+}
+
+const activeList = computed(() => {
+ return sankeyType.value === 'roller' ? props.rollerRecordList : props.packerRecordList;
+});
+
+watch(
+ () => [props.rollerRecordList, props.packerRecordList],
+ () => {
+ const rollerLen = Array.isArray(props.rollerRecordList) ? props.rollerRecordList.length : 0;
+ const packerLen = Array.isArray(props.packerRecordList) ? props.packerRecordList.length : 0;
+
+ if (sankeyType.value === 'roller' && rollerLen === 0 && packerLen > 0) {
+ sankeyType.value = 'packer';
+ return;
+ }
+
+ if (sankeyType.value === 'packer' && packerLen === 0 && rollerLen > 0) {
+ sankeyType.value = 'roller';
+ }
+ },
+ { deep: true, immediate: true }
+);
+
+const hasData = computed(() => {
+ return Boolean(activeList.value && activeList.value.length > 0);
+});
+
+const { domRef, updateOptions } = useEcharts(() => {
+ const { nodes, links } = buildChartData(activeList.value, sankeyType.value);
+
+ return {
+ tooltip: {
+ trigger: 'item'
+ },
+ series: [
+ {
+ type: 'sankey',
+ data: nodes,
+ links,
+ nodeAlign: 'justify',
+ layoutIterations: 32,
+ emphasis: {
+ focus: 'adjacency'
+ },
+ lineStyle: {
+ color: 'source',
+ curveness: 0.5,
+ opacity: 0.35
+ },
+ label: {
+ position: 'right',
+ formatter: (params: any) => {
+ const name = String(params?.data?.name ?? '');
+ const value = params?.data?.value;
+ const kind = params?.data?.kind;
+ const displayName = String(params?.data?.displayName ?? name);
+ const meta = params?.data?.meta;
+
+ if (kind === 'fs') {
+ const materialLine = meta?.materialname ? `鐗屽彿锛�${meta.materialname}` : '';
+ const batchLine = meta?.batchcode ? `鎵规锛�${meta.batchcode}` : '';
+ const jobLine =
+ Number.isFinite(Number(meta?.jobinput)) ? `鎶曟枡閲嶉噺锛�${Number(meta.jobinput).toFixed(2)}kg` : '';
+ const lines: string[] = [displayName];
+ lines.push('');
+ if (materialLine) lines.push(materialLine);
+ if (batchLine) lines.push(batchLine);
+ if (jobLine) {
+ lines.push('');
+ lines.push(jobLine);
+ }
+ return lines.join('\n');
+ }
+
+ if (kind === 'silo') {
+ const beginLine = meta?.distimebegin ? `寮�濮嬶細${meta.distimebegin}` : '';
+ const endLine = meta?.distimeend ? `缁撴潫锛�${meta.distimeend}` : '';
+ const valLine =
+ typeof value === 'number' && Number.isFinite(value) ? `浜ч噺锛�${value.toFixed(2)}绠盽 : '';
+ const lines: string[] = [displayName];
+ lines.push('');
+ if (beginLine) lines.push(beginLine);
+ if (endLine) lines.push(endLine);
+ if (valLine) {
+ lines.push('');
+ lines.push(valLine);
+ }
+ return lines.join('\n');
+ }
+ const showValue =
+ name.startsWith('鍌ㄤ笣鏌� ') ||
+ name.endsWith('#鍗锋帴鏈�') ||
+ name.endsWith('#鍖呰鏈�') ||
+ name === '鍗锋帴鏈�' ||
+ name === '鍖呰鏈�';
+
+ if (showValue && typeof value === 'number' && Number.isFinite(value)) {
+ return `${displayName}\n${value.toFixed(2)}绠盽;
+ }
+ return displayName;
+ }
+ }
+ },
+ ]
+ };
+});
+
+watch(
+ () => [props.rollerRecordList, props.packerRecordList, sankeyType.value],
+ () => {
+ updateOptions(opts => {
+ const { nodes, links } = buildChartData(activeList.value, sankeyType.value);
+ opts.series[0].data = nodes as any;
+ (opts.series[0] as any).links = links;
+ return opts;
+ });
+ },
+ { deep: true, immediate: true }
+);
+</script>
+
+<template>
+ <div class="h-full flex-col-stretch">
+ <div class="pb-8px">
+ <NSpace :size="8">
+ <NButton
+ size="small"
+ ghost
+ :type="sankeyType === 'roller' ? 'primary' : 'default'"
+ @click="sankeyType = 'roller'"
+ >
+ 鍗锋帴
+ </NButton>
+ <NButton
+ size="small"
+ ghost
+ :type="sankeyType === 'packer' ? 'primary' : 'default'"
+ @click="sankeyType = 'packer'"
+ >
+ 鍖呰
+ </NButton>
+ </NSpace>
+ </div>
+ <div v-if="!hasData" class="flex-center flex-1 text-gray-400">
+ 鏆傛棤{{ sankeyType === 'roller' ? '鍗锋帴' : '鍖呰' }}妗戝熀鍥炬暟鎹�
+ </div>
+ <div v-else ref="domRef" class="flex-1 min-h-360px overflow-hidden"></div>
+ </div>
+</template>
+
+<style scoped></style>
diff --git a/ruoyi-plus-soybean/src/views/qm/std/index.vue b/ruoyi-plus-soybean/src/views/qm/std/index.vue
new file mode 100644
index 0000000..f4f6b03
--- /dev/null
+++ b/ruoyi-plus-soybean/src/views/qm/std/index.vue
@@ -0,0 +1,229 @@
+<script setup lang="tsx">
+import { ref } from 'vue';
+import { NDivider } from 'naive-ui';
+import dayjs from 'dayjs';
+import { fetchBatchDeleteStd, fetchGetStdList } from '@/service/api/qm/std';
+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 StdOperateDrawer from './modules/std-operate-drawer.vue';
+import StdSearch from './modules/std-search.vue';
+
+defineOptions({
+ name: 'StdList'
+});
+
+
+const appStore = useAppStore();
+const { download } = useDownload();
+const { hasAuth } = useAuth();
+
+const searchParams = ref<Api.Qm.StdSearchParams>({
+ pageNum: 1,
+ pageSize: 10,
+ stdCode: null,
+ stdName: null,
+ typ: null,
+ stdDes: null,
+ checkLevel: null,
+ category: 0,
+ matfltype: null,
+ matflname: null,
+ params: {}
+});
+
+const { columns, columnChecks, data, getData, getDataByPage, loading, mobilePagination, scrollX } =
+ useNaivePaginatedTable({
+ api: () => fetchGetStdList(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: 'stdCode',
+ title: '瑙勭▼浠g爜',
+ align: 'center',
+ width: 200
+ },
+ {
+ key: 'stdName',
+ title: '瑙勭▼鍚嶇О',
+ align: 'center',
+ width: 300
+ },
+ {
+ key: 'category',
+ title: '鐗╂枡绫诲瀷',
+ align: 'center',
+ minWidth: 120,
+ render: row => {
+ const v = String(row?.category ?? '');
+ return v === '0' ? '鎴愬搧' : v === '1' ? '杈呮枡' : v;
+ }
+ },
+ {
+ key: 'cdate',
+ title: '鍒涘缓鏃堕棿',
+ align: 'center',
+ minWidth: 120,
+ render: row => (row.cdate ? dayjs(row.cdate).format('YYYY-MM-DD') : '')
+ },
+ {
+ key: 'ver',
+ title: '鐗堟湰鍙�',
+ align: 'center',
+ minWidth: 120
+ },
+ {
+ key: 'enable',
+ title: '鍚敤鏍囪瘑',
+ align: 'center',
+ minWidth: 120,
+ render: row => {
+ const v = String(row?.enable ?? '');
+ return v === '0' ? '鍋滅敤' : v === '1' ? '鍚敤' : v;
+ }
+ },
+ {
+ key: 'operate',
+ title: $t('common.operate'),
+ align: 'center',
+ fixed: 'right',
+ width: 130,
+ render: row => {
+ const divider = () => {
+ if (!hasAuth('qm:std:edit') || !hasAuth('qm:std:remove')) {
+ return null;
+ }
+ return <NDivider vertical />;
+ };
+
+ const editBtn = () => {
+ if (!hasAuth('qm:std: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:std: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 fetchBatchDeleteStd(checkedRowKeys.value);
+ if (error) return;
+ onBatchDeleted();
+}
+
+async function handleDelete(id: CommonType.IdType) {
+ // request
+ const { error } = await fetchBatchDeleteStd([id]);
+ if (error) return;
+ onDeleted();
+}
+
+function edit(id: CommonType.IdType) {
+ handleEdit(id);
+}
+
+function handleExport() {
+ download('/qm/std/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">
+ <StdSearch 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:std:add')"
+ :show-delete="hasAuth('qm:std:remove')"
+ :show-export="hasAuth('qm:std: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"
+ />
+ <StdOperateDrawer
+ 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/std/modules/std-operate-drawer.vue b/ruoyi-plus-soybean/src/views/qm/std/modules/std-operate-drawer.vue
new file mode 100644
index 0000000..6316192
--- /dev/null
+++ b/ruoyi-plus-soybean/src/views/qm/std/modules/std-operate-drawer.vue
@@ -0,0 +1,179 @@
+<script setup lang="ts">
+import { computed, ref, watch } from 'vue';
+import { jsonClone } from '@sa/utils';
+import { fetchCreateStd, fetchUpdateStd } from '@/service/api/qm/std';
+import { useFormRules, useNaiveForm } from '@/hooks/common/form';
+import { $t } from '@/locales';
+
+defineOptions({
+ name: 'StdOperateDrawer'
+});
+
+interface Props {
+ /** the type of operation */
+ operateType: NaiveUI.TableOperateType;
+ /** the edit row data */
+ rowData?: Api.Qm.Std | 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.StdOperateParams;
+
+const model = ref<Model>(createDefaultModel());
+
+function createDefaultModel(): Model {
+ return {
+ id: '',
+ stdCode: '',
+ stdName: '',
+ cdate: null,
+ ver: null,
+ enable: null,
+ itemCod: '',
+ itemNam: '',
+ typ: null,
+ stdDes: '',
+ checkLevel: null,
+ category: null,
+ matfltype: '',
+ matflname: ''
+ };
+}
+
+type RuleKey = Extract<
+ keyof Model,
+ | 'id'
+>;
+
+const rules: Record<RuleKey, App.Global.FormRule> = {
+ id: createRequiredRule('缂栫爜涓嶈兘涓虹┖'),
+};
+
+function handleUpdateModelWhenEdit() {
+ model.value = createDefaultModel();
+
+ if (props.operateType === 'edit' && props.rowData) {
+ Object.assign(model.value, jsonClone(props.rowData));
+ }
+}
+
+function closeDrawer() {
+ visible.value = false;
+}
+
+async function handleSubmit() {
+ await validate();
+
+ const { id, stdCode, stdName, cdate, ver, enable, itemCod, itemNam, typ, stdDes, checkLevel, category, matfltype, matflname } = model.value;
+
+ // request
+ if (props.operateType === 'add') {
+ const { error } = await fetchCreateStd({ stdCode, stdName, cdate, ver, enable, itemCod, itemNam, typ, stdDes, checkLevel, category, matfltype, matflname });
+ if (error) return;
+ }
+
+ if (props.operateType === 'edit') {
+ const { error } = await fetchUpdateStd({ id, stdCode, stdName, cdate, ver, enable, itemCod, itemNam, typ, stdDes, checkLevel, category, matfltype, matflname });
+ 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="stdCode">
+ <NInput v-model:value="model.stdCode" placeholder="璇疯緭鍏ヨ绋嬩唬鐮�" />
+ </NFormItem>
+ <NFormItem label="瑙勭▼鍚嶇О" path="stdName">
+ <NInput v-model:value="model.stdName" 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="ver">
+ <NInput v-model:value="model.ver" placeholder="璇疯緭鍏ョ増鏈彿" />
+ </NFormItem>
+ <NFormItem label="鍚敤鏍囪瘑" path="enable">
+ <NInput v-model:value="model.enable" placeholder="璇疯緭鍏ュ惎鐢ㄦ爣璇�" />
+ </NFormItem>
+ <NFormItem label="妫�楠岄」鐩甤ode" path="itemCod">
+ <NInput v-model:value="model.itemCod" placeholder="璇疯緭鍏ユ楠岄」鐩甤ode" />
+ </NFormItem>
+ <NFormItem label="妫�楠岄」鐩悕绉�" path="itemNam">
+ <NInput v-model:value="model.itemNam" placeholder="璇疯緭鍏ユ楠岄」鐩悕绉�" />
+ </NFormItem>
+ <NFormItem label="鍒ゅ畾绫诲瀷 0-鎵嬪姩鍒ゅ畾,1-涓婁笅闄愬垽瀹�,2-骞冲潎鍊煎垽瀹�,3-SD鍊煎垽瀹�,4-CV鍊煎垽瀹�,5-瓒呮爣鏁板垽瀹�" path="typ">
+ <NInput v-model:value="model.typ" placeholder="璇疯緭鍏ュ垽瀹氱被鍨� 0-鎵嬪姩鍒ゅ畾,1-涓婁笅闄愬垽瀹�,2-骞冲潎鍊煎垽瀹�,3-SD鍊煎垽瀹�,4-CV鍊煎垽瀹�,5-瓒呮爣鏁板垽瀹�" />
+ </NFormItem>
+ <NFormItem label="澶囨敞" path="stdDes">
+ <NInput v-model:value="model.stdDes" placeholder="璇疯緭鍏ュ娉�" />
+ </NFormItem>
+ <NFormItem label="妫�楠岀骇鍒�" path="checkLevel">
+ <NInput v-model:value="model.checkLevel" placeholder="璇疯緭鍏ユ楠岀骇鍒�" />
+ </NFormItem>
+ <NFormItem label="绫诲瀷 锛� 0-鎴愬搧 1-杈呮枡" path="category">
+ <NInput v-model:value="model.category" placeholder="璇疯緭鍏ョ被鍨� 锛� 0-鎴愬搧 1-杈呮枡" />
+ </NFormItem>
+ <NFormItem label="$column.columnComment" path="matfltype">
+ <NSelect
+ v-model:value="model.matfltype"
+ placeholder="璇烽�夋嫨$column.columnComment"
+ :options="[{ value: '0', label: '璇烽�夋嫨瀛楀吀鐢熸垚' }]"
+ clearable
+ />
+ </NFormItem>
+ <NFormItem label="$column.columnComment" path="matflname">
+ <NInput v-model:value="model.matflname" placeholder="璇疯緭鍏�$column.columnComment" />
+ </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/std/modules/std-search.vue b/ruoyi-plus-soybean/src/views/qm/std/modules/std-search.vue
new file mode 100644
index 0000000..cdd3bac
--- /dev/null
+++ b/ruoyi-plus-soybean/src/views/qm/std/modules/std-search.vue
@@ -0,0 +1,96 @@
+<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: 'StdSearch'
+});
+
+interface Emits {
+ (e: 'search'): void;
+}
+
+const emit = defineEmits<Emits>();
+
+const { formRef, validate, restoreValidation } = useNaiveForm();
+
+const model = defineModel<Api.Qm.StdSearchParams>('model', { required: true });
+
+const defaultModel = jsonClone(toRaw(model.value));
+
+// 鍒ゅ畾绫诲瀷閫夐」锛�0-鎵嬪姩鍒ゅ畾 1-涓婁笅闄愬垽瀹� 2-骞冲潎鍊煎垽瀹� 3-SD鍊煎垽瀹� 4-CV鍊煎垽瀹� 5-瓒呮爣鏁板垽瀹�
+const typOptions = [
+ { label: '鎵嬪姩鍒ゅ畾', value: 0 },
+ { label: '涓婁笅闄愬垽瀹�', value: 1 },
+ { label: '骞冲潎鍊煎垽瀹�', value: 2 },
+ { label: 'SD鍊煎垽瀹�', value: 3 },
+ { label: 'CV鍊煎垽瀹�', value: 4 },
+ { label: '瓒呮爣鏁板垽瀹�', value: 5 }
+];
+
+// 鐗╂枡绫诲瀷閫夐」锛�0-鎴愬搧 1-杈呮枡
+const categoryOptions = [
+ { label: '鎴愬搧', value: 0 },
+ { label: '杈呮枡', value: 1 }
+];
+
+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-std-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="stdCode" class="pr-24px">
+ <NInput v-model:value="model.stdCode" placeholder="璇疯緭鍏ヨ绋嬩唬鐮�" />
+ </NFormItemGi>
+ <NFormItemGi span="24 s:12 m:6" label="瑙勭▼鍚嶇О" label-width="auto" path="stdName" class="pr-24px">
+ <NInput v-model:value="model.stdName" placeholder="璇疯緭鍏ヨ绋嬪悕绉�" />
+ </NFormItemGi>
+ <NFormItemGi span="24 s:12 m:6" label="鍒ゅ畾绫诲瀷" label-width="auto" path="typ" class="pr-24px">
+ <NSelect v-model:value="model.typ" :options="typOptions" placeholder="璇烽�夋嫨鍒ゅ畾绫诲瀷" clearable />
+ </NFormItemGi>
+ <NFormItemGi span="24 s:12 m:6" label="鐗╂枡绫诲瀷" label-width="auto" path="category" class="pr-24px">
+ <NSelect v-model:value="model.category" :options="categoryOptions" placeholder="璇烽�夋嫨鐗╂枡绫诲瀷" clearable />
+ </NFormItemGi>
+ <NFormItemGi :show-feedback="false" span="24" class="pr-24px">
+ <NSpace class="w-full" justify="end">
+ <NButton @click="reset">
+ <template #icon>
+ <icon-ic-round-refresh class="text-icon" />
+ </template>
+ {{ $t('common.reset') }}
+ </NButton>
+ <NButton type="primary" ghost @click="search">
+ <template #icon>
+ <icon-ic-round-search class="text-icon" />
+ </template>
+ {{ $t('common.search') }}
+ </NButton>
+ </NSpace>
+ </NFormItemGi>
+ </NGrid>
+ </NForm>
+ </NCollapseItem>
+ </NCollapse>
+ </NCard>
+</template>
+
+<style scoped></style>
--
Gitblit v1.9.3