Browse Source

ly bcc培训 图片压缩

ly 2 ngày trước cách đây
mục cha
commit
65efe272c0
33 tập tin đã thay đổi với 2151 bổ sung63 xóa
  1. 8 0
      master/pom.xml
  2. 443 0
      master/src/main/java/com/ruoyi/common/utils/image/ImageCompressor.java
  3. 61 0
      master/src/main/java/com/ruoyi/framework/task/ImageCompressionTask.java
  4. 46 10
      master/src/main/java/com/ruoyi/project/document/controller/TPlantproglistController.java
  5. 8 0
      master/src/main/java/com/ruoyi/project/plant/mapper/TStaffmgrMapper.java
  6. 103 0
      master/src/main/java/com/ruoyi/project/production/controller/TLimsDataController.java
  7. 266 0
      master/src/main/java/com/ruoyi/project/production/domain/TLimsData.java
  8. 63 0
      master/src/main/java/com/ruoyi/project/production/mapper/TLimsDataMapper.java
  9. 61 0
      master/src/main/java/com/ruoyi/project/production/service/ITLimsDataService.java
  10. 93 0
      master/src/main/java/com/ruoyi/project/production/service/impl/TLimsDataServiceImpl.java
  11. 8 10
      master/src/main/java/com/ruoyi/project/training/controller/TTrainingMatrixController.java
  12. 1 1
      master/src/main/resources/application.yml
  13. 15 0
      master/src/main/resources/mybatis/plant/TStaffmgrMapper.xml
  14. 141 0
      master/src/main/resources/mybatis/production/TLimsDataMapper.xml
  15. 2 1
      master/src/main/resources/mybatis/training/TTrainingbccDeviceMapper.xml
  16. BIN
      master/src/main/resources/static/template/training/trainingMatrix.xlsx
  17. 53 0
      ui/src/api/production/limsdata.js
  18. 28 5
      ui/src/assets/iconfont/demo_index.html
  19. 7 3
      ui/src/assets/iconfont/iconfont.css
  20. 0 0
      ui/src/assets/iconfont/iconfont.js
  21. 7 0
      ui/src/assets/iconfont/iconfont.json
  22. BIN
      ui/src/assets/iconfont/iconfont.ttf
  23. BIN
      ui/src/assets/iconfont/iconfont.woff
  24. BIN
      ui/src/assets/iconfont/iconfont.woff2
  25. 1 0
      ui/src/assets/icons/svg/peixun.svg
  26. 5 0
      ui/src/views/plant/organization/branch.vue
  27. 9 2
      ui/src/views/plant/organization/index.vue
  28. 608 0
      ui/src/views/production/limsdata/index.vue
  29. 2 1
      ui/src/views/training/bccdevice/index.vue
  30. 29 7
      ui/src/views/training/bccregular/regular.vue
  31. 29 7
      ui/src/views/training/matrix/index.vue
  32. 29 7
      ui/src/views/training/regular/index.vue
  33. 25 9
      ui/src/views/training/trainingbcc/index.vue

+ 8 - 0
master/pom.xml

@@ -510,6 +510,14 @@
             <artifactId>dysmsapi20170525</artifactId>
             <version>3.1.1</version>
         </dependency>
+
+        <!-- 图片压缩库 -->
+        <dependency>
+            <groupId>net.coobird</groupId>
+            <artifactId>thumbnailator</artifactId>
+            <version>0.4.17</version>
+        </dependency>
+
 <!--        <dependency>-->
 <!--            <groupId>com.aliyun</groupId>-->
 <!--            <artifactId>aliyun-java-sdk-core</artifactId>-->

+ 443 - 0
master/src/main/java/com/ruoyi/common/utils/image/ImageCompressor.java

@@ -0,0 +1,443 @@
+package com.ruoyi.common.utils.image;
+
+import net.coobird.thumbnailator.Thumbnails;
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.FilenameUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.imageio.ImageIO;
+import java.awt.image.BufferedImage;
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.*;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
+
+/**
+ * 图片批量压缩工具类
+ * 支持批量处理多个文件夹
+ */
+public class ImageCompressor {
+
+    private static final Logger logger = LoggerFactory.getLogger(ImageCompressor.class);
+
+    // 默认配置
+    private static final String[] DEFAULT_SUPPORTED_FORMATS = {"jpg", "jpeg", "png", "bmp", "gif"};
+    private static final long DEFAULT_TARGET_SIZE_KB = 200; // 默认目标文件大小:200KB
+    private static final int DEFAULT_THREAD_POOL_SIZE = 1;
+    private static final int DEFAULT_BATCH_SIZE = 100;
+
+    /**
+     * 压缩结果统计
+     */
+    public static class CompressionResult {
+        private final int totalFiles;
+        private final int successCount;
+        private final int failCount;
+        private final int skipCount;
+        private final long totalOriginalSize;
+        private final long totalCompressedSize;
+        private final double compressionRatio;
+
+        public CompressionResult(int totalFiles, int successCount, int failCount, int skipCount,
+                               long totalOriginalSize, long totalCompressedSize) {
+            this.totalFiles = totalFiles;
+            this.successCount = successCount;
+            this.failCount = failCount;
+            this.skipCount = skipCount;
+            this.totalOriginalSize = totalOriginalSize;
+            this.totalCompressedSize = totalCompressedSize;
+            this.compressionRatio = totalOriginalSize > 0 ?
+                (totalOriginalSize - totalCompressedSize) / (double) totalOriginalSize * 100 : 0;
+        }
+
+        // Getters
+        public int getTotalFiles() { return totalFiles; }
+        public int getSuccessCount() { return successCount; }
+        public int getFailCount() { return failCount; }
+        public int getSkipCount() { return skipCount; }
+        public long getTotalOriginalSize() { return totalOriginalSize; }
+        public long getTotalCompressedSize() { return totalCompressedSize; }
+        public double getCompressionRatio() { return compressionRatio; }
+    }
+
+    /**
+     * 批量压缩多个文件夹中的图片
+     *
+     * @param directoryPaths 文件夹路径列表
+     * @return 压缩结果统计
+     */
+    public static CompressionResult compressDirectories(List<String> directoryPaths) {
+        return compressDirectories(directoryPaths, DEFAULT_TARGET_SIZE_KB, DEFAULT_THREAD_POOL_SIZE, DEFAULT_BATCH_SIZE, DEFAULT_SUPPORTED_FORMATS, false);
+    }
+
+    /**
+     * 批量压缩多个文件夹中的图片(带参数)
+     *
+     * @param directoryPaths 文件夹路径列表
+     * @param targetSizeKB 目标文件大小(KB)
+     * @param threadPoolSize 线程池大小
+     * @param batchSize 批处理大小
+     * @param supportedFormats 支持的图片格式
+     * @param enableDetailedLog 是否启用详细日志
+     * @return 压缩结果统计
+     */
+    public static CompressionResult compressDirectories(List<String> directoryPaths, long targetSizeKB, int threadPoolSize, int batchSize, String[] supportedFormats, boolean enableDetailedLog) {
+        if (directoryPaths == null || directoryPaths.isEmpty()) {
+            throw new IllegalArgumentException("文件夹路径列表不能为空");
+        }
+
+        long targetSizeBytes = targetSizeKB * 1024;
+        long minSizeToCompress = targetSizeBytes;
+
+        if (enableDetailedLog) {
+            logger.info("🖼️ 图片批量压缩工具启动");
+            logger.info("目标文件夹数量: {}", directoryPaths.size());
+            logger.info("目标文件大小: {}KB", targetSizeKB);
+            logger.info("多线程配置: {} 个线程", threadPoolSize);
+            logger.info("批处理大小: {} 个文件/批", batchSize);
+            logger.info("支持的格式: {}", String.join(", ", supportedFormats));
+            logger.info("==========================================");
+        }
+
+        try {
+            // 收集所有图片文件
+            List<File> allImageFiles = new ArrayList<>();
+            List<String> validDirectories = new ArrayList<>();
+
+            for (String directoryPath : directoryPaths) {
+                Path path = Paths.get(directoryPath);
+                if (!Files.exists(path) || !Files.isDirectory(path)) {
+                    if (enableDetailedLog) {
+                        logger.warn("⚠️ 跳过无效路径: {}", directoryPath);
+                    }
+                    continue;
+                }
+
+                validDirectories.add(directoryPath);
+                List<File> imageFiles = findImageFiles(path.toFile(), supportedFormats);
+                allImageFiles.addAll(imageFiles);
+
+                if (enableDetailedLog) {
+                    logger.info("📁 {} - 找到 {} 个图片文件", directoryPath, imageFiles.size());
+                }
+            }
+
+            if (allImageFiles.isEmpty()) {
+                if (enableDetailedLog) {
+                    logger.warn("⚠️ 在指定路径下未找到支持的图片文件");
+                }
+                return new CompressionResult(0, 0, 0, 0, 0, 0);
+            }
+
+            if (enableDetailedLog) {
+                logger.info("📁 总计找到 {} 个图片文件", allImageFiles.size());
+                logger.info("🚀 启动多线程压缩处理...");
+            }
+
+            // 开始多线程压缩
+            return processImageFiles(allImageFiles, targetSizeKB, targetSizeBytes, minSizeToCompress, threadPoolSize, batchSize, enableDetailedLog);
+
+        } catch (Exception e) {
+            logger.error("❌ 压缩过程中发生错误: {}", e.getMessage(), e);
+            throw new RuntimeException("图片压缩失败", e);
+        }
+    }
+
+    /**
+     * 处理图片文件列表
+     */
+    private static CompressionResult processImageFiles(List<File> imageFiles, long targetSizeKB, long targetSizeBytes, long minSizeToCompress, int threadPoolSize, int batchSize, boolean enableDetailedLog) {
+        // 创建线程池
+        ExecutorService executor = Executors.newFixedThreadPool(threadPoolSize);
+
+        // 使用原子变量来保证线程安全
+        AtomicLong totalOriginalSize = new AtomicLong(0);
+        AtomicLong totalCompressedSize = new AtomicLong(0);
+        AtomicInteger successCount = new AtomicInteger(0);
+        AtomicInteger failCount = new AtomicInteger(0);
+        AtomicInteger skipCount = new AtomicInteger(0);
+        AtomicInteger processedCount = new AtomicInteger(0);
+
+        try {
+            // 分批处理文件
+            List<Future<?>> futures = new ArrayList<>();
+
+            for (int i = 0; i < imageFiles.size(); i += batchSize) {
+                int endIndex = Math.min(i + batchSize, imageFiles.size());
+                List<File> batch = imageFiles.subList(i, endIndex);
+
+                Future<?> future = executor.submit(() -> processBatch(batch,
+                    totalOriginalSize, totalCompressedSize, successCount, failCount, skipCount, processedCount, targetSizeKB, targetSizeBytes, minSizeToCompress, enableDetailedLog));
+                futures.add(future);
+            }
+
+            // 等待所有任务完成
+            for (Future<?> future : futures) {
+                try {
+                    future.get();
+                } catch (Exception e) {
+                    logger.error("❌ 批处理任务执行失败: {}", e.getMessage());
+                }
+            }
+
+        } finally {
+            // 关闭线程池
+            executor.shutdown();
+
+            try {
+                if (!executor.awaitTermination(60, TimeUnit.SECONDS)) {
+                    executor.shutdownNow();
+                }
+            } catch (InterruptedException e) {
+                executor.shutdownNow();
+                Thread.currentThread().interrupt();
+            }
+        }
+
+        // 输出最终结果
+        CompressionResult result = new CompressionResult(
+            imageFiles.size(), successCount.get(), failCount.get(), skipCount.get(),
+            totalOriginalSize.get(), totalCompressedSize.get()
+        );
+
+        if (enableDetailedLog) {
+            logger.info("\n==========================================");
+            logger.info("🎉 压缩完成!");
+            logger.info("📊 统计信息:");
+            logger.info("   总文件数: {}", result.getTotalFiles());
+            logger.info("   跳过数量: {} (小于{}KB)", result.getSkipCount(), targetSizeKB);
+            logger.info("   成功数量: {}", result.getSuccessCount());
+            logger.info("   失败数量: {}", result.getFailCount());
+            logger.info("   原始总大小: {}", formatFileSize(result.getTotalOriginalSize()));
+            logger.info("   压缩后总大小: {}", formatFileSize(result.getTotalCompressedSize()));
+            logger.info("   压缩比例: {}%", String.format("%.1f", result.getCompressionRatio()));
+            logger.info("==========================================");
+        }
+
+        return result;
+    }
+
+    /**
+     * 批量处理图片文件(线程安全)
+     */
+    private static void processBatch(List<File> batch,
+                            AtomicLong totalOriginalSize,
+                            AtomicLong totalCompressedSize,
+                            AtomicInteger successCount,
+                            AtomicInteger failCount,
+                            AtomicInteger skipCount,
+                            AtomicInteger processedCount,
+                            long targetSizeKB,
+                            long targetSizeBytes,
+                            long minSizeToCompress,
+                            boolean enableDetailedLog) {
+
+        for (File imageFile : batch) {
+            try {
+                long originalSize = imageFile.length();
+                totalOriginalSize.addAndGet(originalSize);
+
+                // 检查文件是否太小,不需要压缩
+                if (originalSize < minSizeToCompress) {
+                    skipCount.incrementAndGet();
+                    if (enableDetailedLog) {
+                        logger.info("⏭️ 跳过小文件: {} ({} KB) - 小于{}KB,无需压缩",
+                            imageFile.getName(),
+                            String.format("%.2f", originalSize / 1024.0),
+                            targetSizeKB);
+                    }
+                    processedCount.incrementAndGet();
+                    continue;
+                }
+
+                // 压缩图片
+                File compressedFile = compressImage(imageFile, targetSizeKB, targetSizeBytes, enableDetailedLog);
+
+                if (compressedFile != null && compressedFile.exists()) {
+                    long compressedSize = compressedFile.length();
+                    totalCompressedSize.addAndGet(compressedSize);
+
+                    // 检查压缩比例是否达到要求
+                    if (compressedSize <= targetSizeBytes) {
+                        // 覆盖原文件
+                        FileUtils.copyFile(compressedFile, imageFile);
+                        successCount.incrementAndGet();
+                        if (enableDetailedLog) {
+                            logger.info("✅ 压缩成功: {} ({} KB -> {} KB)",
+                                imageFile.getName(),
+                                String.format("%.2f", originalSize / 1024.0),
+                                String.format("%.2f", compressedSize / 1024.0));
+                        }
+                    } else {
+                        // 压缩效果不理想,删除临时文件
+                        compressedFile.delete();
+                        failCount.incrementAndGet();
+                        if (enableDetailedLog) {
+                            logger.warn("⚠️ 压缩效果不理想: {} (目标: {} KB, 实际: {} KB)",
+                                imageFile.getName(),
+                                targetSizeKB,
+                                String.format("%.2f", compressedSize / 1024.0));
+                        }
+                    }
+                } else {
+                    failCount.incrementAndGet();
+                    if (enableDetailedLog) {
+                        logger.error("❌ 压缩失败: {}", imageFile.getName());
+                    }
+                }
+
+            } catch (Exception e) {
+                failCount.incrementAndGet();
+                logger.error("❌ 处理文件失败: {} - {}", imageFile.getName(), e.getMessage());
+            } finally {
+                processedCount.incrementAndGet();
+            }
+        }
+    }
+
+    /**
+     * 递归查找所有图片文件
+     */
+    private static List<File> findImageFiles(File directory, String[] supportedFormats) {
+        List<File> imageFiles = new ArrayList<>();
+
+        if (directory.isDirectory()) {
+            File[] files = directory.listFiles();
+            if (files != null) {
+                for (File file : files) {
+                    if (file.isDirectory()) {
+                        imageFiles.addAll(findImageFiles(file, supportedFormats));
+                    } else if (isImageFile(file, supportedFormats)) {
+                        imageFiles.add(file);
+                    }
+                }
+            }
+        }
+
+        return imageFiles;
+    }
+
+    /**
+     * 判断文件是否为支持的图片格式
+     */
+    private static boolean isImageFile(File file, String[] supportedFormats) {
+        String extension = FilenameUtils.getExtension(file.getName()).toLowerCase();
+        for (String format : supportedFormats) {
+            if (format.equals(extension)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * 压缩单个图片文件
+     */
+    private static File compressImage(File originalFile, long targetSizeKB, long targetSizeBytes, boolean enableDetailedLog) throws IOException {
+        String extension = FilenameUtils.getExtension(originalFile.getName()).toLowerCase();
+
+        // 创建临时文件
+        File tempFile = File.createTempFile("compressed_", "." + extension);
+        tempFile.deleteOnExit();
+
+        BufferedImage originalImage = ImageIO.read(originalFile);
+        if (originalImage == null) {
+            return null;
+        }
+
+        // 获取原始尺寸
+        int originalWidth = originalImage.getWidth();
+        int originalHeight = originalImage.getHeight();
+
+        // 渐进式压缩参数
+        double quality = 0.9;
+        double scale = 1.0;
+        int maxAttempts = 20; // 增加尝试次数
+        int attempt = 0;
+
+        while (attempt < maxAttempts) {
+            // 计算目标尺寸
+            int targetWidth = (int) (originalWidth * scale);
+            int targetHeight = (int) (originalHeight * scale);
+
+            // 确保最小尺寸(降低最小尺寸限制)
+            if (targetWidth < 50 || targetHeight < 50) {
+                targetWidth = Math.max(50, targetWidth);
+                targetHeight = Math.max(50, targetHeight);
+            }
+
+            // 使用Thumbnailator进行压缩
+            Thumbnails.of(originalImage)
+                    .size(targetWidth, targetHeight)
+                    .outputQuality(quality)
+                    .toFile(tempFile);
+
+            // 检查文件大小
+            long fileSize = tempFile.length();
+
+            // 如果文件大小已经达到目标,则停止压缩
+            if (fileSize <= targetSizeBytes) {
+                if (enableDetailedLog) {
+                    logger.info("🎯 达到目标大小: {} ({} KB) - 尝试次数: {}",
+                        originalFile.getName(), String.format("%.2f", fileSize / 1024.0), attempt + 1);
+                }
+                break;
+            }
+
+            // 如果文件仍然太大,继续压缩
+            attempt++;
+
+            // 更精细的压缩策略
+            if (attempt <= 5) {
+                // 前5次主要调整质量,步长更小
+                quality = Math.max(0.2, quality - 0.1);
+            } else if (attempt <= 10) {
+                // 中间5次同时调整质量和尺寸
+                quality = Math.max(0.1, quality - 0.05);
+                scale = Math.max(0.2, scale - 0.1);
+            } else {
+                // 最后10次主要调整尺寸,步长更小
+                scale = Math.max(0.1, scale - 0.05);
+                quality = Math.max(0.05, quality - 0.02);
+            }
+
+            if (enableDetailedLog) {
+                logger.info("🔄 压缩尝试 {}: {} ({} KB) - 质量: {}, 尺寸: {}",
+                    attempt, originalFile.getName(), String.format("%.2f", fileSize / 1024.0),
+                    String.format("%.2f", quality), String.format("%.2f", scale));
+            }
+        }
+
+        // 最终检查
+        long finalSize = tempFile.length();
+        if (finalSize > targetSizeBytes && enableDetailedLog) {
+            logger.warn("⚠️ 无法压缩到目标大小: {} (最终: {} KB, 目标: {} KB)",
+                originalFile.getName(), String.format("%.2f", finalSize / 1024.0), targetSizeKB);
+        }
+
+        return tempFile;
+    }
+
+    /**
+     * 格式化文件大小显示
+     */
+    private static String formatFileSize(long size) {
+        if (size < 1024) {
+            return size + " B";
+        } else if (size < 1024 * 1024) {
+            return String.format("%.2f KB", size / 1024.0);
+        } else if (size < 1024 * 1024 * 1024) {
+            return String.format("%.2f MB", size / (1024.0 * 1024.0));
+        } else {
+            return String.format("%.2f GB", size / (1024.0 * 1024.0 * 1024.0));
+        }
+    }
+
+}

+ 61 - 0
master/src/main/java/com/ruoyi/framework/task/ImageCompressionTask.java

@@ -0,0 +1,61 @@
+package com.ruoyi.framework.task;
+
+import com.ruoyi.common.utils.image.ImageCompressor;
+import com.ruoyi.framework.web.controller.BaseController;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * 图片压缩定时任务
+ * 用于系统定时任务管理
+ *
+ * @author ruoyi
+ */
+@Component("imageCompressionTask")
+public class ImageCompressionTask extends BaseController {
+
+    private static final Logger logger = LoggerFactory.getLogger(ImageCompressionTask.class);
+
+    /**
+     * 图片压缩任务
+     */
+    public void imageCompressionTask() {
+        logger.info("🖼️ 开始执行图片压缩任务");
+
+        try {
+            // 需要压缩的文件夹路径列表
+            List<String> directories = Arrays.asList(
+                "D:\\ruoyi",
+                "/u03/photos"
+                // 在这里添加更多需要压缩的文件夹路径
+            );
+
+            if (directories.isEmpty()) {
+                logger.warn("⚠️ 未配置需要压缩的文件夹路径");
+                return;
+            }
+
+            // 执行压缩任务(使用默认配置:200KB目标大小,启用详细日志)
+            logger.info("📁 开始压缩文件夹: {}", directories);
+            ImageCompressor.CompressionResult result = ImageCompressor.compressDirectories(
+                directories, 200, 1, 100,
+                new String[]{"jpg", "jpeg", "png", "bmp", "gif"},
+                true  // 启用详细日志
+            );
+
+            // 记录压缩结果
+            logger.info("🎉 图片压缩任务完成");
+            logger.info("📊 压缩统计: 总文件数={}, 成功={}, 失败={}, 跳过={}, 压缩比例={:.1f}%",
+                    result.getTotalFiles(), result.getSuccessCount(), result.getFailCount(),
+                    result.getSkipCount(), result.getCompressionRatio());
+
+        } catch (Exception e) {
+            logger.error("❌ 图片压缩任务执行失败", e);
+        }
+    }
+}

+ 46 - 10
master/src/main/java/com/ruoyi/project/document/controller/TPlantproglistController.java

@@ -18,6 +18,7 @@ import com.ruoyi.project.document.domain.TPlantproglist;
 import com.ruoyi.project.document.mapper.TPlantproglistMapper;
 import com.ruoyi.project.document.service.ITPlantproglistService;
 import com.ruoyi.project.plant.domain.TStaffmgr;
+import com.ruoyi.project.plant.mapper.TStaffmgrMapper;
 import com.ruoyi.project.plant.service.ITStaffmgrService;
 import com.ruoyi.project.system.domain.SysDept;
 import com.ruoyi.project.system.domain.SysDictData;
@@ -74,6 +75,8 @@ public class TPlantproglistController extends BaseController
     private ITTrainingbccDeviceService trainingbccDeviceService;
     @Autowired
     private ITStaffmgrService tStaffmgrService;
+    @Resource
+    private TStaffmgrMapper tStaffmgrMapper;
     @Autowired
     private ITCommonfileService tCommonfileService;
     /**
@@ -182,10 +185,10 @@ public class TPlantproglistController extends BaseController
     }
 
     /**
-     * 培训装置程序清单
+     * 添加到培训装置程序清单
      */
     @PreAuthorize("@ss.hasPermi('document:plantproglist:remove')")
-    @Log(title = "装置程序清单", businessType = BusinessType.OTHER)
+    @Log(title = "添加到装置程序清单", businessType = BusinessType.OTHER)
     @DeleteMapping("/addTraining/{id}")
     public AjaxResult addTraining(@PathVariable Long id)
     {
@@ -218,41 +221,74 @@ public class TPlantproglistController extends BaseController
             tTrainingRegular.setInvolvedMoc("false");
             tTrainingRegular.setNotPlan("false");
             tTrainingRegular.setYear(String.valueOf(LocalDate.now().getYear()));
-            
+
             // 设置培训开始时间为发布时日期(当前时间)
             Date currentDate = new Date();
             tTrainingRegular.setPlanTrainingdate(currentDate);
-            
+
             // 设置授课人为程序的"更新负责人"
             tTrainingRegular.setLecturer(tPlantproglist.getResponsibility());
-            
+
             logger.info("regular:" + tTrainingRegular);
             tTrainingRegular.setDeptId(103l); //bcc专用
-            //新增培训计划定期类
+            //新增培训跟踪
             TTrainingbcc tTraining = new TTrainingbcc();
             tTraining.setTrainingType("10");
             tTraining.setYears(tTrainingRegular.getYear());
             tTraining.setCourse(tTrainingRegular.getItem());
             tTraining.setCourseid(tTrainingRegular.getCourseCode());
             tTraining.setDuration(tTrainingRegular.getHour());
-            tTraining.setTrainer(tTrainingRegular.getLecturer());
             tTraining.setPosition(tTrainingRegular.getActualpostId());
             tTraining.setIsfinish(Long.parseLong("0"));
             tTraining.setDeptId(tTrainingRegular.getDeptId());
             tTraining.setRegularId(tTrainingRegular.getId());
+            tTraining.setPeriod(matrix.getFrequency());
             tTraining.setFileUrl(fileUrl);
             tTraining.setFileName(fileName);
-            tTraining.setTrainer(tPlantproglist.getResponsibility());
+            tTraining.setContent(fileName);
+            // 通过人名查找staffmgr,获取员工编号来赋值trainer
+            String trainerName = tPlantproglist.getResponsibility();
+            if (StringUtils.isNotEmpty(trainerName)) {
+                TStaffmgr staffmgrParam = new TStaffmgr();
+                staffmgrParam.setName(trainerName);
+                staffmgrParam.setDeptId(tTrainingRegular.getDeptId());
+                staffmgrParam.setDelFlag(0L); // 未删除的员工
+                TStaffmgr staffmgr = tStaffmgrMapper.selectStaffmgrSingle(staffmgrParam);
+                if (staffmgr != null && StringUtils.isNotEmpty(staffmgr.getStaffid())) {
+                    tTraining.setTrainer(staffmgr.getStaffid());
+                } else {
+                    // 如果找不到对应的员工,使用原值
+                    tTraining.setTrainer(trainerName);
+                }
+            } else {
+                tTraining.setTrainer(trainerName);
+            }
             // 设置培训开始时间为发布时日期
             tTraining.setCourseStartdate(currentDate);
-            
+
             // 设置培训结束时间为发布后一个月
             Calendar calendar = Calendar.getInstance();
             calendar.setTime(currentDate);
             calendar.add(Calendar.MONTH, 1);
             Date endDate = calendar.getTime();
             tTraining.setCourseEnddate(endDate);
-            
+
+            //添加学习时间
+            String hourText = matrix.getHour();
+            if (hourText != null && !hourText.trim().isEmpty() && !hourText.contains("按需")) {
+                try {
+                    // 解析课时文本,支持 "1.5h"、"1.5小时"、"1.5" 等格式
+                    String cleanText = hourText.toLowerCase().replaceAll("[^0-9.]", "");
+                    if (!cleanText.isEmpty()) {
+                        double hours = Double.parseDouble(cleanText);
+                        int minutes = (int) Math.round(hours * 60); // 转换为分钟
+                        tTraining.setTimerNeed((long) minutes);
+                    }
+                } catch (NumberFormatException e) {
+                    logger.warn("无法解析课时: " + hourText);
+                }
+            }
+
             trainingbccService.insertTTrainingbcc(tTraining);
             Long trainingId = tTraining.getId();
             //新增人员-装置级关系

+ 8 - 0
master/src/main/java/com/ruoyi/project/plant/mapper/TStaffmgrMapper.java

@@ -177,5 +177,13 @@ public interface TStaffmgrMapper
 
     int deleteRetireTStaffmgrByIds(Long id);
 
+    /**
+     * 通过人名、部门ID、删除标志查询单个员工信息
+     *
+     * @param tStaffmgr 查询参数(包含name、deptId、delFlag)
+     * @return 员工信息
+     */
+    TStaffmgr selectStaffmgrSingle(TStaffmgr tStaffmgr);
+
 
 }

+ 103 - 0
master/src/main/java/com/ruoyi/project/production/controller/TLimsDataController.java

@@ -0,0 +1,103 @@
+package com.ruoyi.project.production.controller;
+
+import java.util.List;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import com.ruoyi.framework.aspectj.lang.annotation.Log;
+import com.ruoyi.framework.aspectj.lang.enums.BusinessType;
+import com.ruoyi.project.production.domain.TLimsData;
+import com.ruoyi.project.production.service.ITLimsDataService;
+import com.ruoyi.framework.web.controller.BaseController;
+import com.ruoyi.framework.web.domain.AjaxResult;
+import com.ruoyi.common.utils.poi.ExcelUtil;
+import com.ruoyi.framework.web.page.TableDataInfo;
+
+/**
+ * LIMS数据Controller
+ *
+ * @author ssy
+ * @date 2025-09-12
+ */
+@RestController
+@RequestMapping("/production/limsdata")
+public class TLimsDataController extends BaseController
+{
+    @Autowired
+    private ITLimsDataService tLimsDataService;
+
+    /**
+     * 查询LIMS数据列表
+     */
+    @PreAuthorize("@ss.hasPermi('production:limsdata:list')")
+    @GetMapping("/list")
+    public TableDataInfo list(TLimsData tLimsData)
+    {
+        startPage();
+        List<TLimsData> list = tLimsDataService.selectTLimsDataList(tLimsData);
+        return getDataTable(list);
+    }
+
+    /**
+     * 导出LIMS数据列表
+     */
+    @PreAuthorize("@ss.hasPermi('production:limsdata:export')")
+    @Log(title = "LIMS数据", businessType = BusinessType.EXPORT)
+    @GetMapping("/export")
+    public AjaxResult export(TLimsData tLimsData)
+    {
+        List<TLimsData> list = tLimsDataService.selectTLimsDataList(tLimsData);
+        ExcelUtil<TLimsData> util = new ExcelUtil<TLimsData>(TLimsData.class);
+        return util.exportExcel(list, "limsdata");
+    }
+
+    /**
+     * 获取LIMS数据详细信息
+     */
+    @PreAuthorize("@ss.hasPermi('production:limsdata:query')")
+    @GetMapping(value = "/{id}")
+    public AjaxResult getInfo(@PathVariable("id") Long id)
+    {
+        return AjaxResult.success(tLimsDataService.selectTLimsDataById(id));
+    }
+
+    /**
+     * 新增LIMS数据
+     */
+    @PreAuthorize("@ss.hasPermi('production:limsdata:add')")
+    @Log(title = "LIMS数据", businessType = BusinessType.INSERT)
+    @PostMapping
+    public AjaxResult add(@RequestBody TLimsData tLimsData)
+    {
+        return toAjax(tLimsDataService.insertTLimsData(tLimsData));
+    }
+
+    /**
+     * 修改LIMS数据
+     */
+    @PreAuthorize("@ss.hasPermi('production:limsdata:edit')")
+    @Log(title = "LIMS数据", businessType = BusinessType.UPDATE)
+    @PutMapping
+    public AjaxResult edit(@RequestBody TLimsData tLimsData)
+    {
+        return toAjax(tLimsDataService.updateTLimsData(tLimsData));
+    }
+
+    /**
+     * 删除LIMS数据
+     */
+    @PreAuthorize("@ss.hasPermi('production:limsdata:remove')")
+    @Log(title = "LIMS数据", businessType = BusinessType.DELETE)
+	@DeleteMapping("/{ids}")
+    public AjaxResult remove(@PathVariable Long[] ids)
+    {
+        return toAjax(tLimsDataService.deleteTLimsDataByIds(ids));
+    }
+}

+ 266 - 0
master/src/main/java/com/ruoyi/project/production/domain/TLimsData.java

@@ -0,0 +1,266 @@
+package com.ruoyi.project.production.domain;
+
+import java.util.Date;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.ruoyi.framework.aspectj.lang.annotation.Excel;
+import com.ruoyi.framework.web.domain.BaseEntity;
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+
+/**
+ * LIMS数据对象 t_lims_data
+ *
+ * @author ssy
+ * @date 2025-09-12
+ */
+public class TLimsData extends BaseEntity
+{
+    private static final long serialVersionUID = 1L;
+
+    /** 主键id */
+    private Long id;
+
+    /** 采样日期 */
+    @JsonFormat(pattern = "yyyy-MM-dd" , timezone = "GMT+8")
+    @Excel(name = "采样日期", width = 30, dateFormat = "yyyy-MM-dd")
+    private Date sampledDate;
+
+    /** 装置 */
+    @Excel(name = "装置")
+    private String plant;
+
+    /** 采样点 */
+    @Excel(name = "采样点")
+    private String samplingPoint;
+
+    /** 采样编号 */
+    @Excel(name = "采样编号")
+    private String sampId;
+
+    /** 检测项目 */
+    @Excel(name = "检测项目")
+    private String rName;
+
+    /** 单位 */
+    @Excel(name = "单位")
+    private String units;
+
+    /** 技术指标 */
+    @Excel(name = "技术指标")
+    private String mvRange;
+
+    /** 检测结果值 */
+    @Excel(name = "检测结果值")
+    private String rText;
+
+    /** 审核时间 */
+    @JsonFormat(pattern = "yyyy-MM-dd" , timezone = "GMT+8")
+    @Excel(name = "审核时间", width = 30, dateFormat = "yyyy-MM-dd")
+    private Date authorised;
+
+    /** 检测结果 */
+    @Excel(name = "检测结果")
+    private String result;
+
+    /** 状态 0 :正常 ;-1:删除 */
+    private Long delFlag;
+
+    /** 创建人 */
+    @Excel(name = "创建人")
+    private Long createrCode;
+
+    /** 创建时间 */
+    @JsonFormat(pattern = "yyyy-MM-dd" , timezone = "GMT+8")
+    @Excel(name = "创建时间", width = 30, dateFormat = "yyyy-MM-dd")
+    private Date createdate;
+
+    /** 修改人 */
+    @Excel(name = "修改人")
+    private Long updaterCode;
+
+    /** 修改时间 */
+    @JsonFormat(pattern = "yyyy-MM-dd" , timezone = "GMT+8")
+    @Excel(name = "修改时间", width = 30, dateFormat = "yyyy-MM-dd")
+    private Date updatedate;
+
+    /** 备注 */
+    @Excel(name = "备注")
+    private String remarks;
+
+    public void setId(Long id)
+    {
+        this.id = id;
+    }
+
+    public Long getId()
+    {
+        return id;
+    }
+    public void setSampledDate(Date sampledDate)
+    {
+        this.sampledDate = sampledDate;
+    }
+
+    public Date getSampledDate()
+    {
+        return sampledDate;
+    }
+    public void setPlant(String plant)
+    {
+        this.plant = plant;
+    }
+
+    public String getPlant()
+    {
+        return plant;
+    }
+    public void setSamplingPoint(String samplingPoint)
+    {
+        this.samplingPoint = samplingPoint;
+    }
+
+    public String getSamplingPoint()
+    {
+        return samplingPoint;
+    }
+    public void setSampId(String sampId)
+    {
+        this.sampId = sampId;
+    }
+
+    public String getSampId()
+    {
+        return sampId;
+    }
+    public void setrName(String rName)
+    {
+        this.rName = rName;
+    }
+
+    public String getrName()
+    {
+        return rName;
+    }
+    public void setUnits(String units)
+    {
+        this.units = units;
+    }
+
+    public String getUnits()
+    {
+        return units;
+    }
+    public void setMvRange(String mvRange)
+    {
+        this.mvRange = mvRange;
+    }
+
+    public String getMvRange()
+    {
+        return mvRange;
+    }
+    public void setrText(String rText)
+    {
+        this.rText = rText;
+    }
+
+    public String getrText()
+    {
+        return rText;
+    }
+    public void setAuthorised(Date authorised)
+    {
+        this.authorised = authorised;
+    }
+
+    public Date getAuthorised()
+    {
+        return authorised;
+    }
+    public void setResult(String result)
+    {
+        this.result = result;
+    }
+
+    public String getResult()
+    {
+        return result;
+    }
+    public void setDelFlag(Long delFlag)
+    {
+        this.delFlag = delFlag;
+    }
+
+    public Long getDelFlag()
+    {
+        return delFlag;
+    }
+    public void setCreaterCode(Long createrCode)
+    {
+        this.createrCode = createrCode;
+    }
+
+    public Long getCreaterCode()
+    {
+        return createrCode;
+    }
+    public void setCreatedate(Date createdate)
+    {
+        this.createdate = createdate;
+    }
+
+    public Date getCreatedate()
+    {
+        return createdate;
+    }
+    public void setUpdaterCode(Long updaterCode)
+    {
+        this.updaterCode = updaterCode;
+    }
+
+    public Long getUpdaterCode()
+    {
+        return updaterCode;
+    }
+    public void setUpdatedate(Date updatedate)
+    {
+        this.updatedate = updatedate;
+    }
+
+    public Date getUpdatedate()
+    {
+        return updatedate;
+    }
+    public void setRemarks(String remarks)
+    {
+        this.remarks = remarks;
+    }
+
+    public String getRemarks()
+    {
+        return remarks;
+    }
+
+    @Override
+    public String toString() {
+        return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
+            .append("id", getId())
+            .append("sampledDate", getSampledDate())
+            .append("plant", getPlant())
+            .append("samplingPoint", getSamplingPoint())
+            .append("sampId", getSampId())
+            .append("rName", getrName())
+            .append("units", getUnits())
+            .append("mvRange", getMvRange())
+            .append("rText", getrText())
+            .append("authorised", getAuthorised())
+            .append("result", getResult())
+            .append("delFlag", getDelFlag())
+            .append("createrCode", getCreaterCode())
+            .append("createdate", getCreatedate())
+            .append("updaterCode", getUpdaterCode())
+            .append("updatedate", getUpdatedate())
+            .append("remarks", getRemarks())
+            .toString();
+    }
+}

+ 63 - 0
master/src/main/java/com/ruoyi/project/production/mapper/TLimsDataMapper.java

@@ -0,0 +1,63 @@
+package com.ruoyi.project.production.mapper;
+
+import java.util.List;
+import com.ruoyi.framework.aspectj.lang.annotation.DataScope;
+import com.ruoyi.project.production.domain.TLimsData;
+
+/**
+ * LIMS数据Mapper接口
+ *
+ * @author ssy
+ * @date 2025-09-12
+ */
+public interface TLimsDataMapper
+{
+    /**
+     * 查询LIMS数据
+     *
+     * @param id LIMS数据ID
+     * @return LIMS数据
+     */
+    public TLimsData selectTLimsDataById(Long id);
+
+    /**
+     * 查询LIMS数据列表
+     *
+     * @param tLimsData LIMS数据
+     * @return LIMS数据集合
+     */
+    @DataScope(deptAlias = "d")
+    public List<TLimsData> selectTLimsDataList(TLimsData tLimsData);
+
+    /**
+     * 新增LIMS数据
+     *
+     * @param tLimsData LIMS数据
+     * @return 结果
+     */
+    public int insertTLimsData(TLimsData tLimsData);
+
+    /**
+     * 修改LIMS数据
+     *
+     * @param tLimsData LIMS数据
+     * @return 结果
+     */
+    public int updateTLimsData(TLimsData tLimsData);
+
+    /**
+     * 删除LIMS数据
+     *
+     * @param id LIMS数据ID
+     * @return 结果
+     */
+    public int deleteTLimsDataById(Long id);
+
+    /**
+     * 批量删除LIMS数据
+     *
+     * @param ids 需要删除的数据ID
+     * @return 结果
+     */
+    public int deleteTLimsDataByIds(Long[] ids);
+}

+ 61 - 0
master/src/main/java/com/ruoyi/project/production/service/ITLimsDataService.java

@@ -0,0 +1,61 @@
+package com.ruoyi.project.production.service;
+
+import java.util.List;
+import com.ruoyi.project.production.domain.TLimsData;
+
+/**
+ * LIMS数据Service接口
+ *
+ * @author ssy
+ * @date 2025-09-12
+ */
+public interface ITLimsDataService
+{
+    /**
+     * 查询LIMS数据
+     *
+     * @param id LIMS数据ID
+     * @return LIMS数据
+     */
+    public TLimsData selectTLimsDataById(Long id);
+
+    /**
+     * 查询LIMS数据列表
+     *
+     * @param tLimsData LIMS数据
+     * @return LIMS数据集合
+     */
+    public List<TLimsData> selectTLimsDataList(TLimsData tLimsData);
+
+    /**
+     * 新增LIMS数据
+     *
+     * @param tLimsData LIMS数据
+     * @return 结果
+     */
+    public int insertTLimsData(TLimsData tLimsData);
+
+    /**
+     * 修改LIMS数据
+     *
+     * @param tLimsData LIMS数据
+     * @return 结果
+     */
+    public int updateTLimsData(TLimsData tLimsData);
+
+    /**
+     * 批量删除LIMS数据
+     *
+     * @param ids 需要删除的LIMS数据ID
+     * @return 结果
+     */
+    public int deleteTLimsDataByIds(Long[] ids);
+
+    /**
+     * 删除LIMS数据信息
+     *
+     * @param id LIMS数据ID
+     * @return 结果
+     */
+    public int deleteTLimsDataById(Long id);
+}

+ 93 - 0
master/src/main/java/com/ruoyi/project/production/service/impl/TLimsDataServiceImpl.java

@@ -0,0 +1,93 @@
+package com.ruoyi.project.production.service.impl;
+
+import java.util.List;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import com.ruoyi.project.production.mapper.TLimsDataMapper;
+import com.ruoyi.project.production.domain.TLimsData;
+import com.ruoyi.project.production.service.ITLimsDataService;
+
+/**
+ * LIMS数据Service业务层处理
+ *
+ * @author ssy
+ * @date 2025-09-12
+ */
+@Service
+public class TLimsDataServiceImpl implements ITLimsDataService
+{
+    @Autowired
+    private TLimsDataMapper tLimsDataMapper;
+
+    /**
+     * 查询LIMS数据
+     *
+     * @param id LIMS数据ID
+     * @return LIMS数据
+     */
+    @Override
+    public TLimsData selectTLimsDataById(Long id)
+    {
+        return tLimsDataMapper.selectTLimsDataById(id);
+    }
+
+    /**
+     * 查询LIMS数据列表
+     *
+     * @param tLimsData LIMS数据
+     * @return LIMS数据
+     */
+    @Override
+    public List<TLimsData> selectTLimsDataList(TLimsData tLimsData)
+    {
+        return tLimsDataMapper.selectTLimsDataList(tLimsData);
+    }
+
+    /**
+     * 新增LIMS数据
+     *
+     * @param tLimsData LIMS数据
+     * @return 结果
+     */
+    @Override
+    public int insertTLimsData(TLimsData tLimsData)
+    {
+        return tLimsDataMapper.insertTLimsData(tLimsData);
+    }
+
+    /**
+     * 修改LIMS数据
+     *
+     * @param tLimsData LIMS数据
+     * @return 结果
+     */
+    @Override
+    public int updateTLimsData(TLimsData tLimsData)
+    {
+        return tLimsDataMapper.updateTLimsData(tLimsData);
+    }
+
+    /**
+     * 批量删除LIMS数据
+     *
+     * @param ids 需要删除的LIMS数据ID
+     * @return 结果
+     */
+    @Override
+    public int deleteTLimsDataByIds(Long[] ids)
+    {
+        return tLimsDataMapper.deleteTLimsDataByIds(ids);
+    }
+
+    /**
+     * 删除LIMS数据信息
+     *
+     * @param id LIMS数据ID
+     * @return 结果
+     */
+    @Override
+    public int deleteTLimsDataById(Long id)
+    {
+        return tLimsDataMapper.deleteTLimsDataById(id);
+    }
+}

+ 8 - 10
master/src/main/java/com/ruoyi/project/training/controller/TTrainingMatrixController.java

@@ -105,14 +105,12 @@ public class TTrainingMatrixController extends BaseController {
                 if (t.getDesignatedOther() != null) {
                     String[] designatedOther = t.getDesignatedOther().split(",");
                     for (String d : designatedOther) {
-                        if (d.equals("28")) {
-                            postStatu.set(postStatu.size() - 6, "(M)");
-                        }
-                        if (d.equals("30")) {
-                            postStatu.set(postStatu.size() - 4, "(M)");
-                        }
-                        if (d.equals("32")) {
-                            postStatu.set(postStatu.size() - 1, "(M)");
+                        // 根据岗位字典值找到对应的索引位置
+                        for (int i = 0; i < actualpost.size(); i++) {
+                            if (actualpost.get(i).getDictValue().equals(d)) {
+                                postStatu.set(i, "(M)");
+                                break;
+                            }
                         }
                     }
                 }
@@ -399,7 +397,7 @@ public class TTrainingMatrixController extends BaseController {
                         entity.setHour(cellValue);
                     } else if (j == 7) {
                         for (SysDictData actualpost : actualposts) {
-                            if (actualpost.getDictLabel().equals("装置经理")) {
+                            if (actualpost.getDictLabel().equals("首席经理")) {
                                 if ("M".equals(cellValue) || "X".equals(cellValue) || "m".equals(cellValue) || "x".equals(cellValue)) {
                                     actualpostId.append(actualpost.getDictValue()).append(",");
                                 } else if ("(M)".equals(cellValue) || "(X)".equals(cellValue) || "(m)".equals(cellValue) || "(x)".equals(cellValue)||"(M)".equals(cellValue) || "(X)".equals(cellValue) || "(m)".equals(cellValue) || "(x)".equals(cellValue)) {
@@ -411,7 +409,7 @@ public class TTrainingMatrixController extends BaseController {
                         }
                     } else if (j == 8) {
                         for (SysDictData actualpost : actualposts) {
-                            if (actualpost.getDictLabel().equals("装置经理")) {
+                            if (actualpost.getDictLabel().equals("装置经理")) {
                                 if ("M".equals(cellValue) || "X".equals(cellValue) || "m".equals(cellValue) || "x".equals(cellValue)) {
                                     actualpostId.append(actualpost.getDictValue()).append(",");
                                 } else if ("(M)".equals(cellValue) || "(X)".equals(cellValue) || "(m)".equals(cellValue) || "(x)".equals(cellValue)||"(M)".equals(cellValue) || "(X)".equals(cellValue) || "(m)".equals(cellValue) || "(x)".equals(cellValue)) {

+ 1 - 1
master/src/main/resources/application.yml

@@ -203,7 +203,7 @@ gen:
   # 作者
   author: ssy
   # 默认生成包路径 system 需改成自己的模块名称 如 system monitor tool
-  packageName: com.ruoyi.project.sems # 自动去除表前缀,默认是true
+  packageName: com.ruoyi.project.production # 自动去除表前缀,默认是true
   autoRemovePre: false
   # 表前缀(生成类名不会包含表前缀,多个用逗号分隔)
   tablePrefix: sys_

+ 15 - 0
master/src/main/resources/mybatis/plant/TStaffmgrMapper.xml

@@ -856,4 +856,19 @@
         or s1.DICT_LABEL like '%工长%'
         )
     </select>
+
+    <!-- 通过人名、部门ID、删除标志查询单个员工信息 -->
+    <select id="selectStaffmgrSingle" parameterType="TStaffmgr" resultMap="TStaffmgrResult">
+        <include refid="selectTStaffmgrVo"/>
+        where 2 > ROWNUM
+        <if test="name != null and name != ''">
+            AND d.name = #{name}
+        </if>
+        <if test="deptId != null and deptId != 0">
+            AND d.dept_id = #{deptId}
+        </if>
+        <if test="delFlag != null">
+            AND d.del_flag = #{delFlag}
+        </if>
+    </select>
 </mapper>

+ 141 - 0
master/src/main/resources/mybatis/production/TLimsDataMapper.xml

@@ -0,0 +1,141 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ruoyi.project.production.mapper.TLimsDataMapper">
+
+    <resultMap type="TLimsData" id="TLimsDataResult">
+        <result property="id"    column="id"    />
+        <result property="sampledDate"    column="sampled_date"    />
+        <result property="plant"    column="plant"    />
+        <result property="samplingPoint"    column="sampling_point"    />
+        <result property="sampId"    column="samp_id"    />
+        <result property="rName"    column="r_name"    />
+        <result property="units"    column="units"    />
+        <result property="mvRange"    column="mv_range"    />
+        <result property="rText"    column="r_text"    />
+        <result property="authorised"    column="authorised"    />
+        <result property="result"    column="result"    />
+        <result property="delFlag"    column="del_flag"    />
+        <result property="createrCode"    column="creater_code"    />
+        <result property="createdate"    column="createdate"    />
+        <result property="updaterCode"    column="updater_code"    />
+        <result property="updatedate"    column="updatedate"    />
+        <result property="remarks"    column="remarks"    />
+        <result property="deptName" column="dept_name" />
+    </resultMap>
+
+    <sql id="selectTLimsDataVo">
+        select d.id, d.sampled_date, d.plant, d.sampling_point, d.samp_id, d.r_name, d.units, d.mv_range, d.r_text, d.authorised, d.result, d.del_flag, d.creater_code, d.createdate, d.updater_code, d.updatedate, d.remarks ,s.dept_name from t_lims_data d
+      left join sys_dept s on s.dept_id = d.dept_id
+    </sql>
+
+    <select id="selectTLimsDataList" parameterType="TLimsData" resultMap="TLimsDataResult">
+        <include refid="selectTLimsDataVo"/>
+        <where>
+            <if test="sampledDate != null "> and sampled_date = #{sampledDate}</if>
+            <if test="plant != null  and plant != ''"> and plant = #{plant}</if>
+            <if test="samplingPoint != null  and samplingPoint != ''"> and sampling_point = #{samplingPoint}</if>
+            <if test="sampId != null  and sampId != ''"> and samp_id = #{sampId}</if>
+            <if test="rName != null  and rName != ''"> and r_name like concat(concat('%', #{rName}), '%')</if>
+            <if test="units != null  and units != ''"> and units = #{units}</if>
+            <if test="mvRange != null  and mvRange != ''"> and mv_range = #{mvRange}</if>
+            <if test="rText != null  and rText != ''"> and r_text = #{rText}</if>
+            <if test="authorised != null "> and authorised = #{authorised}</if>
+            <if test="result != null  and result != ''"> and result = #{result}</if>
+            <if test="createrCode != null "> and creater_code = #{createrCode}</if>
+            <if test="createdate != null "> and createdate = #{createdate}</if>
+            <if test="updaterCode != null "> and updater_code = #{updaterCode}</if>
+            <if test="updatedate != null "> and updatedate = #{updatedate}</if>
+            <if test="remarks != null  and remarks != ''"> and remarks = #{remarks}</if>
+            and d.del_flag = 0
+        </where>
+        <!-- 数据范围过滤 -->
+        ${params.dataScope}
+    </select>
+
+    <select id="selectTLimsDataById" parameterType="Long" resultMap="TLimsDataResult">
+        <include refid="selectTLimsDataVo"/>
+        where id = #{id}
+    </select>
+
+    <insert id="insertTLimsData" parameterType="TLimsData">
+        <selectKey keyProperty="id" resultType="long" order="BEFORE">
+            SELECT seq_t_lims_data.NEXTVAL as id FROM DUAL
+        </selectKey>
+        insert into t_lims_data
+        <trim prefix="(" suffix=")" suffixOverrides=",">
+            <if test="id != null">id,</if>
+            <if test="sampledDate != null">sampled_date,</if>
+            <if test="plant != null">plant,</if>
+            <if test="samplingPoint != null">sampling_point,</if>
+            <if test="sampId != null">samp_id,</if>
+            <if test="rName != null">r_name,</if>
+            <if test="units != null">units,</if>
+            <if test="mvRange != null">mv_range,</if>
+            <if test="rText != null">r_text,</if>
+            <if test="authorised != null">authorised,</if>
+            <if test="result != null">result,</if>
+            <if test="delFlag != null">del_flag,</if>
+            <if test="createrCode != null">creater_code,</if>
+            <if test="createdate != null">createdate,</if>
+            <if test="updaterCode != null">updater_code,</if>
+            <if test="updatedate != null">updatedate,</if>
+            <if test="remarks != null">remarks,</if>
+         </trim>
+        <trim prefix="values (" suffix=")" suffixOverrides=",">
+            <if test="id != null">#{id},</if>
+            <if test="sampledDate != null">#{sampledDate},</if>
+            <if test="plant != null">#{plant},</if>
+            <if test="samplingPoint != null">#{samplingPoint},</if>
+            <if test="sampId != null">#{sampId},</if>
+            <if test="rName != null">#{rName},</if>
+            <if test="units != null">#{units},</if>
+            <if test="mvRange != null">#{mvRange},</if>
+            <if test="rText != null">#{rText},</if>
+            <if test="authorised != null">#{authorised},</if>
+            <if test="result != null">#{result},</if>
+            <if test="delFlag != null">#{delFlag},</if>
+            <if test="createrCode != null">#{createrCode},</if>
+            <if test="createdate != null">#{createdate},</if>
+            <if test="updaterCode != null">#{updaterCode},</if>
+            <if test="updatedate != null">#{updatedate},</if>
+            <if test="remarks != null">#{remarks},</if>
+         </trim>
+    </insert>
+
+    <update id="updateTLimsData" parameterType="TLimsData">
+        update t_lims_data
+        <trim prefix="SET" suffixOverrides=",">
+            <if test="sampledDate != null">sampled_date = #{sampledDate},</if>
+            <if test="plant != null">plant = #{plant},</if>
+            <if test="samplingPoint != null">sampling_point = #{samplingPoint},</if>
+            <if test="sampId != null">samp_id = #{sampId},</if>
+            <if test="rName != null">r_name = #{rName},</if>
+            <if test="units != null">units = #{units},</if>
+            <if test="mvRange != null">mv_range = #{mvRange},</if>
+            <if test="rText != null">r_text = #{rText},</if>
+            <if test="authorised != null">authorised = #{authorised},</if>
+            <if test="result != null">result = #{result},</if>
+            <if test="delFlag != null">del_flag = #{delFlag},</if>
+            <if test="createrCode != null">creater_code = #{createrCode},</if>
+            <if test="createdate != null">createdate = #{createdate},</if>
+            <if test="updaterCode != null">updater_code = #{updaterCode},</if>
+            <if test="updatedate != null">updatedate = #{updatedate},</if>
+            <if test="remarks != null">remarks = #{remarks},</if>
+        </trim>
+        where id = #{id}
+    </update>
+
+    <update id="deleteTLimsDataById" parameterType="Long">
+        update t_lims_data set del_flag = 2 where id = #{id}
+    </update>
+
+    <update id="deleteTLimsDataByIds" parameterType="String">
+        update t_lims_data set del_flag = 2 where id in
+        <foreach item="id" collection="array" open="(" separator="," close=")">
+            #{id}
+        </foreach>
+    </update>
+
+</mapper>

+ 2 - 1
master/src/main/resources/mybatis/training/TTrainingbccDeviceMapper.xml

@@ -51,7 +51,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="examState != null "> and d.exam_state = #{examState}</if>
             <if test="examId != null "> and d.exam_id = #{examId}</if>
             <if test="learnTime != null "> and d.learn_time = #{learnTime}</if>
-            <if test="trainingType != null and trainingType != ''">and t.training_type = #{trainingType}</if>
+            <if test="trainingType != null and trainingType != '' and trainingType != -1">and t.training_type = #{trainingType}</if>
+            <if test="trainingType != null and trainingType == -1">and t.training_type not in ('10','12','14','16','20')</if>
             and d.del_flag = 0
         </where>
         order by d.exam_state, d.start_date

BIN
master/src/main/resources/static/template/training/trainingMatrix.xlsx


+ 53 - 0
ui/src/api/production/limsdata.js

@@ -0,0 +1,53 @@
+import request from '@/utils/request'
+
+// 查询LIMS数据列表
+export function listLimsdata(query) {
+  return request({
+    url: '/production/limsdata/list',
+    method: 'get',
+    params: query
+  })
+}
+
+// 查询LIMS数据详细
+export function getLimsdata(id) {
+  return request({
+    url: '/production/limsdata/' + id,
+    method: 'get'
+  })
+}
+
+// 新增LIMS数据
+export function addLimsdata(data) {
+  return request({
+    url: '/production/limsdata',
+    method: 'post',
+    data: data
+  })
+}
+
+// 修改LIMS数据
+export function updateLimsdata(data) {
+  return request({
+    url: '/production/limsdata',
+    method: 'put',
+    data: data
+  })
+}
+
+// 删除LIMS数据
+export function delLimsdata(id) {
+  return request({
+    url: '/production/limsdata/' + id,
+    method: 'delete'
+  })
+}
+
+// 导出LIMS数据
+export function exportLimsdata(query) {
+  return request({
+    url: '/production/limsdata/export',
+    method: 'get',
+    params: query
+  })
+}

+ 28 - 5
ui/src/assets/iconfont/demo_index.html

@@ -3,8 +3,8 @@
 <head>
   <meta charset="utf-8"/>
   <title>iconfont Demo</title>
-  <link rel="shortcut icon" href="//img.alicdn.com/imgextra/i2/O1CN01ZyAlrn1MwaMhqz36G_!!6000000001499-73-tps-64-64.ico" type="image/x-icon"/>
-  <link rel="icon" type="image/svg+xml" href="//img.alicdn.com/imgextra/i4/O1CN01EYTRnJ297D6vehehJ_!!6000000008020-55-tps-64-64.svg"/>
+  <link rel="shortcut icon" href="//img.alicdn.com/imgextra/i4/O1CN01Z5paLz1O0zuCC7osS_!!6000000001644-55-tps-83-82.svg" type="image/x-icon"/>
+  <link rel="icon" type="image/svg+xml" href="//img.alicdn.com/imgextra/i4/O1CN01Z5paLz1O0zuCC7osS_!!6000000001644-55-tps-83-82.svg"/>
   <link rel="stylesheet" href="https://g.alicdn.com/thx/cube/1.3.2/cube.min.css">
   <link rel="stylesheet" href="demo.css">
   <link rel="stylesheet" href="iconfont.css">
@@ -54,6 +54,12 @@
       <div class="content unicode" style="display: block;">
           <ul class="icon_lists dib-box">
 
+            <li class="dib">
+              <span class="icon iconfont">&#xe6c4;</span>
+                <div class="name">BAI-培训</div>
+                <div class="code-name">&amp;#xe6c4;</div>
+              </li>
+
             <li class="dib">
               <span class="icon iconfont">&#xe62c;</span>
                 <div class="name">设备图标_阀门</div>
@@ -120,9 +126,9 @@
 <pre><code class="language-css"
 >@font-face {
   font-family: 'iconfont';
-  src: url('iconfont.woff2?t=1638751065452') format('woff2'),
-       url('iconfont.woff?t=1638751065452') format('woff'),
-       url('iconfont.ttf?t=1638751065452') format('truetype');
+  src: url('iconfont.woff2?t=1757579908171') format('woff2'),
+       url('iconfont.woff?t=1757579908171') format('woff'),
+       url('iconfont.ttf?t=1757579908171') format('truetype');
 }
 </code></pre>
           <h3 id="-iconfont-">第二步:定义使用 iconfont 的样式</h3>
@@ -148,6 +154,15 @@
       <div class="content font-class">
         <ul class="icon_lists dib-box">
 
+          <li class="dib">
+            <span class="icon iconfont icon-BAI-peixun"></span>
+            <div class="name">
+              BAI-培训
+            </div>
+            <div class="code-name">.icon-BAI-peixun
+            </div>
+          </li>
+
           <li class="dib">
             <span class="icon iconfont icon-shebeitubiao_famen"></span>
             <div class="name">
@@ -247,6 +262,14 @@
       <div class="content symbol">
           <ul class="icon_lists dib-box">
 
+            <li class="dib">
+                <svg class="icon svg-icon" aria-hidden="true">
+                  <use xlink:href="#icon-BAI-peixun"></use>
+                </svg>
+                <div class="name">BAI-培训</div>
+                <div class="code-name">#icon-BAI-peixun</div>
+            </li>
+
             <li class="dib">
                 <svg class="icon svg-icon" aria-hidden="true">
                   <use xlink:href="#icon-shebeitubiao_famen"></use>

+ 7 - 3
ui/src/assets/iconfont/iconfont.css

@@ -1,8 +1,8 @@
 @font-face {
   font-family: "iconfont"; /* Project id 2877683 */
-  src: url('iconfont.woff2?t=1638751065452') format('woff2'),
-       url('iconfont.woff?t=1638751065452') format('woff'),
-       url('iconfont.ttf?t=1638751065452') format('truetype');
+  src: url('iconfont.woff2?t=1757579908171') format('woff2'),
+       url('iconfont.woff?t=1757579908171') format('woff'),
+       url('iconfont.ttf?t=1757579908171') format('truetype');
 }
 
 .iconfont {
@@ -13,6 +13,10 @@
   -moz-osx-font-smoothing: grayscale;
 }
 
+.icon-BAI-peixun:before {
+  content: "\e6c4";
+}
+
 .icon-shebeitubiao_famen:before {
   content: "\e62c";
 }

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 0 - 0
ui/src/assets/iconfont/iconfont.js


+ 7 - 0
ui/src/assets/iconfont/iconfont.json

@@ -5,6 +5,13 @@
   "css_prefix_text": "icon-",
   "description": "",
   "glyphs": [
+    {
+      "icon_id": "2513049",
+      "name": "BAI-培训",
+      "font_class": "BAI-peixun",
+      "unicode": "e6c4",
+      "unicode_decimal": 59076
+    },
     {
       "icon_id": "4652163",
       "name": "设备图标_阀门",

BIN
ui/src/assets/iconfont/iconfont.ttf


BIN
ui/src/assets/iconfont/iconfont.woff


BIN
ui/src/assets/iconfont/iconfont.woff2


+ 1 - 0
ui/src/assets/icons/svg/peixun.svg

@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1757578392881" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1600" xmlns:xlink="http://www.w3.org/1999/xlink" width="128" height="128"><path d="M862.697 355.871a19.577 19.577 0 0 0-0.868-0.345l-268.836-99.563c-25.771-10.871-53.016-16.381-80.993-16.381s-55.223 5.51-80.993 16.381l-268.835 99.563c-0.292 0.108-0.582 0.224-0.869 0.345-16.397 6.96-26.992 22.968-26.992 40.781s10.595 33.821 26.992 40.781c0.287 0.122 0.577 0.237 0.869 0.345l100.184 37.103v199.391c0 19.197 9.399 37.114 25.142 47.927 27.108 18.618 90.592 62.22 223.588 62.22 132.709 0 197.265-43.534 224.832-62.124l0.137-0.092c16.024-10.808 25.591-28.839 25.591-48.235V474.882l100.185-37.103c0.292-0.108 0.581-0.224 0.868-0.345 16.397-6.96 26.993-22.968 26.993-40.781s-10.597-33.822-26.995-40.782zM721.645 673.968c0 6.078-2.975 11.712-7.955 15.071l-0.138 0.093c-24.532 16.544-81.983 55.287-202.466 55.287-120.583 0-176.896-38.677-200.943-55.192-4.876-3.349-7.788-8.939-7.788-14.954V489.695l128.652 47.646c25.77 10.871 53.016 16.381 80.993 16.381s55.223-5.51 80.993-16.381l128.651-47.646v184.273z m125.699-273.48l-268.668 99.501c-0.292 0.108-0.582 0.224-0.868 0.345-20.93 8.884-43.07 13.389-65.808 13.389-22.737 0-44.878-4.504-65.807-13.389a19.616 19.616 0 0 0-0.869-0.345l-268.668-99.501c-1.557-0.759-2.346-2.048-2.346-3.835s0.789-3.076 2.346-3.835l268.668-99.501c0.292-0.108 0.582-0.224 0.869-0.345 20.929-8.884 43.07-13.389 65.807-13.389 22.737 0 44.878 4.504 65.808 13.389 0.286 0.122 0.576 0.237 0.868 0.345l268.668 99.501c1.558 0.759 2.347 2.048 2.347 3.836s-0.79 3.075-2.347 3.834z" fill="#d81e06" p-id="1601"></path></svg>

+ 5 - 0
ui/src/views/plant/organization/branch.vue

@@ -17,6 +17,7 @@
             <i v-if="item.bz6" class="iconfont icon-round"></i>
             <i v-if="item.bz7" class="iconfont icon-yuanjiaoliujiaoxing"></i>
             <i v-if="item.bz8" class="iconfont icon-shebeitubiao_famen"></i>
+            <i v-if="item.bz9" class="iconfont icon-BAI-peixun"></i>
           </div>
         </div>
         <!-- 平级 -->
@@ -33,6 +34,7 @@
             <i v-if="item.bz6" class="iconfont icon-round"></i>
             <i v-if="item.bz7" class="iconfont icon-yuanjiaoliujiaoxing"></i>
             <i v-if="item.bz8" class="iconfont icon-shebeitubiao_famen"></i>
+            <i v-if="item.bz9" class="iconfont icon-BAI-peixun"></i>
           </div>
         </div>
         <template v-if="item.secretary">
@@ -52,6 +54,7 @@
                     <i v-if="child.bz6" class="iconfont icon-round"></i>
                     <i v-if="child.bz7" class="iconfont icon-yuanjiaoliujiaoxing"></i>
                     <i v-if="child.bz8" class="iconfont icon-shebeitubiao_famen"></i>
+                    <i v-if="child.bz9" class="iconfont icon-BAI-peixun"></i>
                   </div>
                 </div>
                 <ul :style="{'width': ulWidth + 'px'}">
@@ -69,6 +72,7 @@
                         <i v-if="schild.bz6" class="iconfont icon-round"></i>
                         <i v-if="schild.bz7" class="iconfont icon-yuanjiaoliujiaoxing"></i>
                         <i v-if="schild.bz8" class="iconfont icon-shebeitubiao_famen"></i>
+                        <i v-if="schild.bz9" class="iconfont icon-BAI-peixun"></i>
                       </div>
                     </div>
                   </li>
@@ -88,6 +92,7 @@
                     <i v-if="child.bz6" class="iconfont icon-round"></i>
                     <i v-if="child.bz7" class="iconfont icon-yuanjiaoliujiaoxing"></i>
                     <i v-if="child.bz8" class="iconfont icon-shebeitubiao_famen"></i>
+                    <i v-if="child.bz9" class="iconfont icon-BAI-peixun"></i>
                   </div>
                 </div>
                 <div :style="{width: ulWidth + 'px'}"></div>

+ 9 - 2
ui/src/views/plant/organization/index.vue

@@ -52,6 +52,7 @@
             <i  class="iconfont icon-round"></i>志愿消防员
             <i  class="iconfont icon-yuanjiaoliujiaoxing"></i>安委会成员
             <i  class="iconfont icon-shebeitubiao_famen"></i>TDS负责人
+            <i  class="iconfont icon-BAI-peixun"></i>VRT培训师
           </div>
         </el-col>
         <right-toolbar :showSearch.sync="showSearch" @queryTable="getChartData"></right-toolbar>
@@ -593,7 +594,8 @@ export default {
                   bz5: false,
                   bz6: false,
                   bz7: false,
-                  bz8: false
+                  bz8: false,
+                  bz9: false
                 }
                 if (this.staffmgrList[i].specialDuty) {
                   // console.log(this.staffmgrList[i].specialDuty)
@@ -615,6 +617,8 @@ export default {
                       nodeData.bz8 = true
                     } else if (dutyArr[i] == "24") {
                       nodeData.bz7 = true
+                    }else if (dutyArr[i] == "30") {
+                      nodeData.bz9 = true
                     }
                   }
                 }
@@ -634,7 +638,8 @@ export default {
                   bz5: false,
                   bz6: false,
                   bz7: false,
-                  bz8: false
+                  bz8: false,
+                  bz9: false
                 }
                 if (this.staffmgrList[i].specialDuty) {
                   // console.log(this.staffmgrList[i].specialDuty)
@@ -656,6 +661,8 @@ export default {
                       nodeData.bz8 = true
                     } else if (dutyArr[i] == "24") {
                       nodeData.bz7 = true
+                    }else if (dutyArr[i] == "30") {
+                      nodeData.bz9 = true
                     }
                   }
                 }

+ 608 - 0
ui/src/views/production/limsdata/index.vue

@@ -0,0 +1,608 @@
+<template>
+  <div class="app-container">
+    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
+      <el-form-item label="采样日期" prop="sampledDate">
+        <el-date-picker clearable size="small" style="width: 200px"
+          v-model="queryParams.sampledDate"
+          type="date"
+          value-format="yyyy-MM-dd"
+          placeholder="选择采样日期">
+        </el-date-picker>
+      </el-form-item>
+      <el-form-item label="装置" prop="plant">
+        <el-input
+          v-model="queryParams.plant"
+          placeholder="请输入装置"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="采样点" prop="samplingPoint">
+        <el-input
+          v-model="queryParams.samplingPoint"
+          placeholder="请输入采样点"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="采样编号" prop="sampId">
+        <el-input
+          v-model="queryParams.sampId"
+          placeholder="请输入采样编号"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="检测项目" prop="rName">
+        <el-input
+          v-model="queryParams.rName"
+          placeholder="请输入检测项目"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="单位" prop="units">
+        <el-input
+          v-model="queryParams.units"
+          placeholder="请输入单位"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="技术指标" prop="mvRange">
+        <el-input
+          v-model="queryParams.mvRange"
+          placeholder="请输入技术指标"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="检测结果值" prop="rText">
+        <el-input
+          v-model="queryParams.rText"
+          placeholder="请输入检测结果值"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="审核时间" prop="authorised">
+        <el-date-picker clearable size="small" style="width: 200px"
+          v-model="queryParams.authorised"
+          type="date"
+          value-format="yyyy-MM-dd"
+          placeholder="选择审核时间">
+        </el-date-picker>
+      </el-form-item>
+      <el-form-item label="检测结果" prop="result">
+        <el-input
+          v-model="queryParams.result"
+          placeholder="请输入检测结果"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="创建人" prop="createrCode">
+        <el-input
+          v-model="queryParams.createrCode"
+          placeholder="请输入创建人"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="创建时间" prop="createdate">
+        <el-date-picker clearable size="small" style="width: 200px"
+          v-model="queryParams.createdate"
+          type="date"
+          value-format="yyyy-MM-dd"
+          placeholder="选择创建时间">
+        </el-date-picker>
+      </el-form-item>
+      <el-form-item label="修改人" prop="updaterCode">
+        <el-input
+          v-model="queryParams.updaterCode"
+          placeholder="请输入修改人"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="修改时间" prop="updatedate">
+        <el-date-picker clearable size="small" style="width: 200px"
+          v-model="queryParams.updatedate"
+          type="date"
+          value-format="yyyy-MM-dd"
+          placeholder="选择修改时间">
+        </el-date-picker>
+      </el-form-item>
+      <el-form-item label="备注" prop="remarks">
+        <el-input
+          v-model="queryParams.remarks"
+          placeholder="请输入备注"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item>
+        <el-button type="cyan" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
+        <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
+      </el-form-item>
+    </el-form>
+
+    <el-row :gutter="10" class="mb8">
+      <el-col :span="1.5">
+        <el-button
+          type="primary"
+          icon="el-icon-plus"
+          size="mini"
+          @click="handleAdd"
+          v-hasPermi="['production:limsdata:add']"
+        >新增</el-button>
+      </el-col>
+      <el-col :span="1.5">
+        <el-button
+          type="success"
+          icon="el-icon-edit"
+          size="mini"
+          :disabled="single"
+          @click="handleUpdate"
+          v-hasPermi="['production:limsdata:edit']"
+        >修改</el-button>
+      </el-col>
+      <el-col :span="1.5">
+        <el-button
+          type="danger"
+          icon="el-icon-delete"
+          size="mini"
+          :disabled="multiple"
+          @click="handleDelete"
+          v-hasPermi="['production:limsdata:remove']"
+        >删除</el-button>
+      </el-col>
+        <el-col :span="1.5">
+            <el-button
+                    type="info"
+                    icon="el-icon-upload2"
+                    size="mini"
+                    @click="handleImport"
+                    v-hasPermi="['production:limsdata:edit']"
+            >导入</el-button>
+        </el-col>
+      <el-col :span="1.5">
+        <el-button
+          type="warning"
+          icon="el-icon-download"
+          size="mini"
+          @click="handleExport"
+          v-hasPermi="['production:limsdata:export']"
+        >导出</el-button>
+      </el-col>
+	  <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
+    </el-row>
+
+    <el-table v-loading="loading" :data="limsdataList" @selection-change="handleSelectionChange" :height="clientHeight" border>
+      <el-table-column type="selection" width="55" align="center" />
+      <el-table-column label="主键id" align="center" prop="id" :show-overflow-tooltip="true"/>
+      <el-table-column label="采样日期" align="center" prop="sampledDate" width="100">
+        <template slot-scope="scope">
+          <span>{{ parseTime(scope.row.sampledDate, '{y}-{m}-{d}') }}</span>
+        </template>
+      </el-table-column>
+      <el-table-column label="装置" align="center" prop="plant" :show-overflow-tooltip="true"/>
+      <el-table-column label="采样点" align="center" prop="samplingPoint" :show-overflow-tooltip="true"/>
+      <el-table-column label="采样编号" align="center" prop="sampId" :show-overflow-tooltip="true"/>
+      <el-table-column label="检测项目" align="center" prop="rName" :show-overflow-tooltip="true"/>
+      <el-table-column label="单位" align="center" prop="units" :show-overflow-tooltip="true"/>
+      <el-table-column label="技术指标" align="center" prop="mvRange" :show-overflow-tooltip="true"/>
+      <el-table-column label="检测结果值" align="center" prop="rText" :show-overflow-tooltip="true"/>
+      <el-table-column label="审核时间" align="center" prop="authorised" width="100">
+        <template slot-scope="scope">
+          <span>{{ parseTime(scope.row.authorised, '{y}-{m}-{d}') }}</span>
+        </template>
+      </el-table-column>
+      <el-table-column label="检测结果" align="center" prop="result" :show-overflow-tooltip="true"/>
+      <el-table-column label="创建人" align="center" prop="createrCode" :show-overflow-tooltip="true"/>
+      <el-table-column label="创建时间" align="center" prop="createdate" width="100">
+        <template slot-scope="scope">
+          <span>{{ parseTime(scope.row.createdate, '{y}-{m}-{d}') }}</span>
+        </template>
+      </el-table-column>
+      <el-table-column label="修改人" align="center" prop="updaterCode" :show-overflow-tooltip="true"/>
+      <el-table-column label="修改时间" align="center" prop="updatedate" width="100">
+        <template slot-scope="scope">
+          <span>{{ parseTime(scope.row.updatedate, '{y}-{m}-{d}') }}</span>
+        </template>
+      </el-table-column>
+      <el-table-column label="备注" align="center" prop="remarks" :show-overflow-tooltip="true"/>
+      <el-table-column label="操作" align="center" fixed="right" width="120" class-name="small-padding fixed-width">
+        <template slot-scope="scope">
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-edit"
+            @click="handleUpdate(scope.row)"
+            v-hasPermi="['production:limsdata:edit']"
+          >修改</el-button>
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-delete"
+            @click="handleDelete(scope.row)"
+            v-hasPermi="['production:limsdata:remove']"
+          >删除</el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+
+    <pagination
+      v-show="total>0"
+      :total="total"
+      :page.sync="queryParams.pageNum"
+      :limit.sync="queryParams.pageSize"
+      @pagination="getList"
+    />
+
+    <!-- 添加或修改LIMS数据对话框 -->
+    <el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
+      <el-form ref="form" :model="form" :rules="rules" label-width="80px">
+        <el-form-item label="采样日期" prop="sampledDate">
+          <el-date-picker clearable size="small" style="width: 200px"
+            v-model="form.sampledDate"
+            type="date"
+            value-format="yyyy-MM-dd"
+            placeholder="选择采样日期">
+          </el-date-picker>
+        </el-form-item>
+        <el-form-item label="装置" prop="plant">
+          <el-input v-model="form.plant" placeholder="请输入装置" />
+        </el-form-item>
+        <el-form-item label="采样点" prop="samplingPoint">
+          <el-input v-model="form.samplingPoint" placeholder="请输入采样点" />
+        </el-form-item>
+        <el-form-item label="采样编号" prop="sampId">
+          <el-input v-model="form.sampId" placeholder="请输入采样编号" />
+        </el-form-item>
+        <el-form-item label="检测项目" prop="rName">
+          <el-input v-model="form.rName" placeholder="请输入检测项目" />
+        </el-form-item>
+        <el-form-item label="单位" prop="units">
+          <el-input v-model="form.units" placeholder="请输入单位" />
+        </el-form-item>
+        <el-form-item label="技术指标" prop="mvRange">
+          <el-input v-model="form.mvRange" placeholder="请输入技术指标" />
+        </el-form-item>
+        <el-form-item label="检测结果值" prop="rText">
+          <el-input v-model="form.rText" placeholder="请输入检测结果值" />
+        </el-form-item>
+        <el-form-item label="审核时间" prop="authorised">
+          <el-date-picker clearable size="small" style="width: 200px"
+            v-model="form.authorised"
+            type="date"
+            value-format="yyyy-MM-dd"
+            placeholder="选择审核时间">
+          </el-date-picker>
+        </el-form-item>
+        <el-form-item label="检测结果" prop="result">
+          <el-input v-model="form.result" placeholder="请输入检测结果" />
+        </el-form-item>
+        <el-form-item label="状态 0 :正常 ;-1:删除" prop="delFlag">
+          <el-input v-model="form.delFlag" placeholder="请输入状态 0 :正常 ;-1:删除" />
+        </el-form-item>
+        <el-form-item label="创建人" prop="createrCode">
+          <el-input v-model="form.createrCode" placeholder="请输入创建人" />
+        </el-form-item>
+        <el-form-item label="创建时间" prop="createdate">
+          <el-date-picker clearable size="small" style="width: 200px"
+            v-model="form.createdate"
+            type="date"
+            value-format="yyyy-MM-dd"
+            placeholder="选择创建时间">
+          </el-date-picker>
+        </el-form-item>
+        <el-form-item label="修改人" prop="updaterCode">
+          <el-input v-model="form.updaterCode" placeholder="请输入修改人" />
+        </el-form-item>
+        <el-form-item label="修改时间" prop="updatedate">
+          <el-date-picker clearable size="small" style="width: 200px"
+            v-model="form.updatedate"
+            type="date"
+            value-format="yyyy-MM-dd"
+            placeholder="选择修改时间">
+          </el-date-picker>
+        </el-form-item>
+        <el-form-item label="备注" prop="remarks">
+          <el-input v-model="form.remarks" placeholder="请输入备注" />
+        </el-form-item>
+          <el-form-item label="归属部门" prop="deptId">
+              <treeselect v-model="form.deptId" :options="deptOptions" :show-count="true" placeholder="请选择归属部门" />
+          </el-form-item>
+      </el-form>
+      <div slot="footer" class="dialog-footer">
+        <el-button type="primary" @click="submitForm">确 定</el-button>
+        <el-button @click="cancel">取 消</el-button>
+      </div>
+    </el-dialog>
+      <!-- 用户导入对话框 -->
+      <el-dialog :title="upload.title" :visible.sync="upload.open" width="400px" append-to-body>
+          <el-upload
+                  ref="upload"
+                  :limit="1"
+                  accept=".xlsx, .xls"
+                  :headers="upload.headers"
+                  :action="upload.url + '?updateSupport=' + upload.updateSupport"
+                  :disabled="upload.isUploading"
+                  :on-progress="handleFileUploadProgress"
+                  :on-success="handleFileSuccess"
+                  :auto-upload="false"
+                  drag
+          >
+              <i class="el-icon-upload"></i>
+              <div class="el-upload__text">
+                  将文件拖到此处,或
+                  <em>点击上传</em>
+              </div>
+              <div class="el-upload__tip" slot="tip">
+                  <el-checkbox v-model="upload.updateSupport" />是否更新已经存在的用户数据
+                  <el-link type="info" style="font-size:12px" @click="importTemplate">下载模板</el-link>
+              </div>
+              <div class="el-upload__tip" style="color:red" slot="tip">提示:仅允许导入“xls”或“xlsx”格式文件!</div>
+          </el-upload>
+          <div slot="footer" class="dialog-footer">
+              <el-button type="primary" @click="submitFileForm">确 定</el-button>
+              <el-button @click="upload.open = false">取 消</el-button>
+          </div>
+      </el-dialog>
+  </div>
+</template>
+
+<script>
+import { listLimsdata, getLimsdata, delLimsdata, addLimsdata, updateLimsdata, exportLimsdata, importTemplate} from "@/api/production/limsdata";
+import { treeselect } from "@/api/system/dept";
+import { getToken } from "@/utils/auth";
+import Treeselect from "@riophae/vue-treeselect";
+import "@riophae/vue-treeselect/dist/vue-treeselect.css";
+
+export default {
+  name: "Limsdata",
+  components: { Treeselect },
+  data() {
+    return {
+      // 遮罩层
+      loading: true,
+      // 选中数组
+      ids: [],
+      // 非单个禁用
+      single: true,
+      // 非多个禁用
+      multiple: true,
+      // 显示搜索条件
+      showSearch: false,
+      // 总条数
+      total: 0,
+      // LIMS数据表格数据
+      limsdataList: [],
+      // 弹出层标题
+      title: "",
+      // 部门树选项
+      deptOptions: undefined,
+      clientHeight:300,
+      // 是否显示弹出层
+      open: false,
+        // 用户导入参数
+        upload: {
+            // 是否显示弹出层(用户导入)
+            open: false,
+            // 弹出层标题(用户导入)
+            title: "",
+            // 是否禁用上传
+            isUploading: false,
+            // 是否更新已经存在的用户数据
+            updateSupport: 0,
+            // 设置上传的请求头部
+            headers: { Authorization: "Bearer " + getToken() },
+            // 上传的地址
+            url: process.env.VUE_APP_BASE_API + "/production/limsdata/importData"
+        },
+      // 查询参数
+      queryParams: {
+        pageNum: 1,
+        pageSize: 20,
+        sampledDate: null,
+        plant: null,
+        samplingPoint: null,
+        sampId: null,
+        rName: null,
+        units: null,
+        mvRange: null,
+        rText: null,
+        authorised: null,
+        result: null,
+        createrCode: null,
+        createdate: null,
+        updaterCode: null,
+        updatedate: null,
+        remarks: null
+      },
+      // 表单参数
+      form: {},
+      // 表单校验
+      rules: {
+      }
+    };
+  },
+  watch: {
+        // 根据名称筛选部门树
+        deptName(val) {
+            this.$refs.tree.filter(val);
+        }
+   },
+  created() {
+      //设置表格高度对应屏幕高度
+      this.$nextTick(() => {
+          this.clientHeight = document.body.clientHeight -250
+      })
+    this.getList();
+    this.getTreeselect();
+  },
+  methods: {
+    /** 查询LIMS数据列表 */
+    getList() {
+      this.loading = true;
+      listLimsdata(this.queryParams).then(response => {
+        this.limsdataList = response.rows;
+        this.total = response.total;
+        this.loading = false;
+      });
+    },
+     /** 查询部门下拉树结构 */
+     getTreeselect() {
+          treeselect().then(response => {
+              this.deptOptions = response.data;
+          });
+     },
+    // 取消按钮
+    cancel() {
+      this.open = false;
+      this.reset();
+    },
+    // 表单重置
+    reset() {
+      this.form = {
+        id: null,
+        sampledDate: null,
+        plant: null,
+        samplingPoint: null,
+        sampId: null,
+        rName: null,
+        units: null,
+        mvRange: null,
+        rText: null,
+        authorised: null,
+        result: null,
+        delFlag: null,
+        createrCode: null,
+        createdate: null,
+        updaterCode: null,
+        updatedate: null,
+        remarks: null
+      };
+      this.resetForm("form");
+    },
+    /** 搜索按钮操作 */
+    handleQuery() {
+      this.queryParams.pageNum = 1;
+      this.getList();
+    },
+    /** 重置按钮操作 */
+    resetQuery() {
+      this.resetForm("queryForm");
+      this.handleQuery();
+    },
+    // 多选框选中数据
+    handleSelectionChange(selection) {
+      this.ids = selection.map(item => item.id)
+      this.single = selection.length!==1
+      this.multiple = !selection.length
+    },
+    /** 新增按钮操作 */
+    handleAdd() {
+      this.reset();
+      this.open = true;
+      this.title = "添加LIMS数据";
+    },
+    /** 修改按钮操作 */
+    handleUpdate(row) {
+      this.reset();
+      const id = row.id || this.ids
+      getLimsdata(id).then(response => {
+        this.form = response.data;
+        this.open = true;
+        this.title = "修改LIMS数据";
+      });
+    },
+    /** 提交按钮 */
+    submitForm() {
+      this.$refs["form"].validate(valid => {
+        if (valid) {
+          if (this.form.id != null) {
+            updateLimsdata(this.form).then(response => {
+              this.msgSuccess("修改成功");
+              this.open = false;
+              this.getList();
+            });
+          } else {
+            addLimsdata(this.form).then(response => {
+              this.msgSuccess("新增成功");
+              this.open = false;
+              this.getList();
+            });
+          }
+        }
+      });
+    },
+    /** 删除按钮操作 */
+    handleDelete(row) {
+      const ids = row.id || this.ids;
+      this.$confirm('是否确认删除?', "警告", {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning"
+        }).then(function() {
+          return delLimsdata(ids);
+        }).then(() => {
+          this.getList();
+          this.msgSuccess("删除成功");
+        })
+    },
+    /** 导出按钮操作 */
+    handleExport() {
+      const queryParams = this.queryParams;
+      this.$confirm('是否确认导出所有LIMS数据数据项?', "警告", {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning"
+        }).then(function() {
+          return exportLimsdata(queryParams);
+        }).then(response => {
+          this.download(response.msg);
+        })
+    },
+      /** 导入按钮操作 */
+      handleImport() {
+          this.upload.title = "用户导入";
+          this.upload.open = true;
+      },
+      /** 下载模板操作 */
+      importTemplate() {
+          importTemplate().then(response => {
+              this.download(response.msg);
+          });
+      },
+      // 文件上传中处理
+      handleFileUploadProgress(event, file, fileList) {
+          this.upload.isUploading = true;
+      },
+      // 文件上传成功处理
+      handleFileSuccess(response, file, fileList) {
+          this.upload.open = false;
+          this.upload.isUploading = false;
+          this.$refs.upload.clearFiles();
+          this.$alert(response.msg, "导入结果", { dangerouslyUseHTMLString: true });
+          this.getList();
+      },
+      // 提交上传文件
+      submitFileForm() {
+          this.$refs.upload.submit();
+      }
+  }
+};
+</script>

+ 2 - 1
ui/src/views/training/bccdevice/index.vue

@@ -318,7 +318,8 @@ export default {
         learnState: null,
         examState: null,
         examId: null,
-        learnTime: null
+        learnTime: null,
+        trainingType: null,
       },
       // 表单参数
       form: {},

+ 29 - 7
ui/src/views/training/bccregular/regular.vue

@@ -104,9 +104,14 @@
       </el-table-column>
       <el-table-column :label="$t('授课人')" align="center" prop="lecturerName" width="230" :show-overflow-tooltip="true" />
       <el-table-column :label="$t('课时')" align="center" prop="hour" :show-overflow-tooltip="true"/>
-      <el-table-column v-for="(item, index) in actualpostIdOptions" width="150" :label="item.dictLabel" :key="index" align="center">
+      <el-table-column v-for="(item, index) in actualpostIdOptions"
+                       v-if="!shouldHideColumn(item.dictLabel)"
+                       width="150"
+                       :label="item.dictLabel"
+                       :key="index"
+                       align="center">
         <template slot-scope="scope">
-          {{scope.row.actualpost[index]}}
+          {{scope.row.actualpost[getOriginalIndex(item)]}}
         </template>
       </el-table-column>
       <el-table-column :label="$t('备注')" align="center" prop="remarks" width="300" :show-overflow-tooltip="true"/>
@@ -443,13 +448,30 @@ export default {
         this.lecturerOptions = response.rows;
       });
     },
+    // 判断是否需要隐藏列
+    shouldHideColumn(columnLabel) {
+      const hideColumns = ['工程师', '见习生', '机械维修经理', '电仪维修经理'];
+      return hideColumns.some(hideLabel => columnLabel.includes(hideLabel));
+    },
+    // 获取岗位在原始数组中的索引
+    getOriginalIndex(item) {
+      return this.actualpostIdOptions.findIndex(originalItem =>
+        originalItem.dictValue === item.dictValue
+      );
+    },
     //根据分数显示颜色提示
     tableCellClassName({ row, column, rowIndex, columnIndex }) {
-      var postNum = this.actualpostIdOptions.length;
-      for (var i = 0; i < postNum; i++) {
-        if (columnIndex == 7 + i){
-          return this.changeColor(row.actualpost[i])
-        }
+      // 计算实际显示的岗位列数(排除隐藏的列)
+      const visibleColumns = this.actualpostIdOptions.filter(item =>
+        !this.shouldHideColumn(item.dictLabel)
+      );
+
+      // 检查是否是岗位列(从第7列开始)
+      if (columnIndex >= 7 && columnIndex < 7 + visibleColumns.length) {
+        const visibleIndex = columnIndex - 7;
+        const visibleItem = visibleColumns[visibleIndex];
+        const originalIndex = this.getOriginalIndex(visibleItem);
+        return this.changeColor(row.actualpost[originalIndex]);
       }
     },
     changeColor (value) {

+ 29 - 7
ui/src/views/training/matrix/index.vue

@@ -136,9 +136,14 @@
       </el-table-column>      <el-table-column :label="$t('责任部门')" align="center" prop="responsDept" width="120" min-width="120" :formatter="responsDeptFormat" />
       <el-table-column :label="$t('授课人')" align="center" prop="lecturer" width="100" min-width="100" :formatter="lecturerFormat"/>
       <el-table-column :label="$t('课时')" align="center" prop="hour" width="70" min-width="70"  :show-overflow-tooltip="true"/>
-      <el-table-column v-for="(item, index) in actualpostIdOptions" width="150" min-width="150" :label="item.dictLabel" :key="index" align="center">
+      <el-table-column v-for="(item, index) in actualpostIdOptions"
+                       v-if="!shouldHideColumn(item.dictLabel)"
+                       width="150" min-width="150"
+                       :label="item.dictLabel"
+                       :key="index"
+                       align="center">
         <template slot-scope="scope">
-          {{scope.row.actualpost[index]}}
+          {{scope.row.actualpost[getOriginalIndex(item)]}}
         </template>
       </el-table-column>
       <el-table-column :label="$t('备注')" align="center" prop="remarks" width="300" min-width="300" :show-overflow-tooltip="true"/>
@@ -479,13 +484,30 @@ export default {
           this.deptOptions = response.data;
       });
      },
+    // 判断是否需要隐藏列
+    shouldHideColumn(columnLabel) {
+      const hideColumns = ['工程师', '见习生', '机械维修经理', '电仪维修经理'];
+      return hideColumns.some(hideLabel => columnLabel.includes(hideLabel));
+    },
+    // 获取岗位在原始数组中的索引
+    getOriginalIndex(item) {
+      return this.actualpostIdOptions.findIndex(originalItem =>
+        originalItem.dictValue === item.dictValue
+      );
+    },
     //根据分数显示颜色提示
     tableCellClassName({ row, column, rowIndex, columnIndex }) {
-      var postNum = this.actualpostIdOptions.length;
-      for (var i = 0; i < postNum; i++) {
-        if (columnIndex == 8 + i){
-          return this.changeColor(row.actualpost[i])
-        }
+      // 计算实际显示的岗位列数(排除隐藏的列)
+      const visibleColumns = this.actualpostIdOptions.filter(item =>
+        !this.shouldHideColumn(item.dictLabel)
+      );
+
+      // 检查是否是岗位列(从第8列开始)
+      if (columnIndex >= 8 && columnIndex < 8 + visibleColumns.length) {
+        const visibleIndex = columnIndex - 8;
+        const visibleItem = visibleColumns[visibleIndex];
+        const originalIndex = this.getOriginalIndex(visibleItem);
+        return this.changeColor(row.actualpost[originalIndex]);
       }
     },
     changeColor (value) {

+ 29 - 7
ui/src/views/training/regular/index.vue

@@ -95,9 +95,14 @@
       </el-table-column>
       <el-table-column :label="$t('授课人')" align="center" prop="lecturerName" width="230" :show-overflow-tooltip="true" />
       <el-table-column :label="$t('课时')" align="center" prop="hour" :show-overflow-tooltip="true"/>
-      <el-table-column v-for="(item, index) in actualpostIdOptions" width="150" :label="item.dictLabel" :key="index" align="center">
+      <el-table-column v-for="(item, index) in actualpostIdOptions"
+                       v-if="!shouldHideColumn(item.dictLabel)"
+                       width="150"
+                       :label="item.dictLabel"
+                       :key="index"
+                       align="center">
         <template slot-scope="scope">
-          {{scope.row.actualpost[index]}}
+          {{scope.row.actualpost[getOriginalIndex(item)]}}
         </template>
       </el-table-column>
       <el-table-column :label="$t('备注')" align="center" prop="remarks" width="300" :show-overflow-tooltip="true"/>
@@ -466,13 +471,30 @@
           this.lecturerOptions = response.rows;
         });
       },
+      // 判断是否需要隐藏列
+      shouldHideColumn(columnLabel) {
+        const hideColumns = ['工程师', '见习生', '机械维修经理', '电仪维修经理'];
+        return hideColumns.some(hideLabel => columnLabel.includes(hideLabel));
+      },
+      // 获取岗位在原始数组中的索引
+      getOriginalIndex(item) {
+        return this.actualpostIdOptions.findIndex(originalItem =>
+          originalItem.dictValue === item.dictValue
+        );
+      },
       //根据分数显示颜色提示
       tableCellClassName({ row, column, rowIndex, columnIndex }) {
-        var postNum = this.actualpostIdOptions.length;
-        for (var i = 0; i < postNum; i++) {
-          if (columnIndex == 7 + i){
-            return this.changeColor(row.actualpost[i])
-          }
+        // 计算实际显示的岗位列数(排除隐藏的列)
+        const visibleColumns = this.actualpostIdOptions.filter(item =>
+          !this.shouldHideColumn(item.dictLabel)
+        );
+
+        // 检查是否是岗位列(从第7列开始)
+        if (columnIndex >= 7 && columnIndex < 7 + visibleColumns.length) {
+          const visibleIndex = columnIndex - 7;
+          const visibleItem = visibleColumns[visibleIndex];
+          const originalIndex = this.getOriginalIndex(visibleItem);
+          return this.changeColor(row.actualpost[originalIndex]);
         }
       },
       changeColor (value) {

+ 25 - 9
ui/src/views/training/trainingbcc/index.vue

@@ -171,19 +171,35 @@
           <span > ({{ scope.row.haveTraining }}/{{ scope.row.mustTraining }})</span>
         </template>
       </el-table-column>
-      <el-table-column :label="$t('培训周期')" align="center" prop="period" :show-overflow-tooltip="true"/>
+      <el-table-column :label="$t('培训频率')" align="center" prop="period" :show-overflow-tooltip="true"/>
       <el-table-column label="学习时长(min)" align="center" prop="timerNeed" :show-overflow-tooltip="true"/>
       <el-table-column :label="$t('课时')" align="center" prop="duration" :show-overflow-tooltip="true"/>
       <el-table-column :label="$t('培训人')" align="center" prop="trainer" :show-overflow-tooltip="true"/>
-      <el-table-column label="课件" align="center">
+      <el-table-column label="课件" align="center" width="120">
         <template slot-scope="scope">
-          <el-button
-            size="mini"
-            type="text"
-            icon="el-icon-folder"
-            @click="handleDoc(scope.row)"
-          >上传课件
-          </el-button>
+          <div v-if="scope.row.fileName && scope.row.fileName.trim() !== ''">
+            <el-tag type="success" size="mini">
+              <i class="el-icon-document"></i>
+            </el-tag>
+            <el-button
+              size="mini"
+              type="text"
+              icon="el-icon-edit"
+              @click="handleDoc(scope.row)"
+              style="margin-left: 5px;"
+            >管理
+            </el-button>
+          </div>
+          <div v-else>
+            <el-button
+              size="mini"
+              type="text"
+              icon="el-icon-upload"
+              @click="handleDoc(scope.row)"
+              style="margin-left: 5px;"
+            >上传
+            </el-button>
+          </div>
         </template>
       </el-table-column>
       <el-table-column :label="$t('培训岗位')" align="center" prop="position" width="250" :show-overflow-tooltip="true" />

Một số tệp đã không được hiển thị bởi vì quá nhiều tập tin thay đổi trong này khác