Bläddra i källkod

add preprocessed view details

allen 1 månad sedan
förälder
incheckning
2cc3a85180

+ 69 - 0
ips-admin/src/main/java/com/ips/system/controller/BizCommonController.java

@@ -0,0 +1,69 @@
+package com.ips.system.controller;
+
+import org.springframework.core.io.FileSystemResource;
+import org.springframework.core.io.Resource;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.io.File;
+
+@RestController
+@RequestMapping("/biz/common")
+public class BizCommonController {
+
+    @GetMapping("/getStatic")
+    public ResponseEntity<Resource> getStaticFile(@RequestParam String filePath) {
+        // 安全检查 - 防止路径遍历攻击
+        if (!isSafePath(filePath)) {
+            return ResponseEntity.badRequest().build();
+        }
+
+        File file = new File(filePath);
+        if (!file.exists()) {
+            return ResponseEntity.notFound().build();
+        }
+
+        Resource resource = new FileSystemResource(file);
+
+        // 设置Content-Type
+        String contentType = determineContentType(file.getName());
+
+        return ResponseEntity.ok()
+                .contentType(MediaType.parseMediaType(contentType))
+                .header(HttpHeaders.CONTENT_DISPOSITION, "inline; filename=\"" + file.getName() + "\"")
+                .body(resource);
+    }
+
+    private boolean isSafePath(String filePath) {
+        // 这里添加你的安全检查逻辑
+        // 例如:禁止相对路径、限制特定目录等
+        return true; // 临时返回true,实际使用时需要实现
+    }
+
+    private String determineContentType(String filename) {
+        // 简单的内容类型判断
+        if (filename.endsWith(".jpg") || filename.endsWith(".jpeg")) {
+            return "image/jpeg";
+        } else if (filename.endsWith(".png")) {
+            return "image/png";
+        } else if (filename.endsWith(".gif")) {
+            return "image/gif";
+        } else if (filename.endsWith(".pdf")) {
+            return "application/pdf";
+        } else if (filename.endsWith(".json")) {
+            return "application/json";
+        } else if (filename.endsWith(".csv")) {
+            return "text/csv";
+        } else if (filename.endsWith(".txt")) {
+            return "text/plain";
+        } else {
+            return "application/octet-stream";
+        }
+    }
+}
+

+ 11 - 0
ips-admin/src/main/java/com/ips/system/controller/PreprocessedController.java

@@ -115,4 +115,15 @@ public class PreprocessedController extends BaseController
             return error(errorMsg);
         }
     }
+
+    @PreAuthorize("@ss.hasPermi('biz:preprocessed:add')")
+    @GetMapping(value = "/getResultDetails/{id}")
+    public AjaxResult getResultDetails(@PathVariable("id") Long id) throws JsonProcessingException {
+        Preprocessed obj = preprocessedService.getResultDetails(id);
+        if(obj != null){
+            return success(obj);
+        } else {
+            return error("获取详情失败");
+        }
+    }
 }

+ 7 - 103
ips-admin/src/main/java/com/ips/system/domain/Preprocessed.java

@@ -1,11 +1,13 @@
 package com.ips.system.domain;
 
-import java.util.Date;
 import com.fasterxml.jackson.annotation.JsonFormat;
 import com.ips.common.annotation.Excel;
 import com.ips.common.core.domain.BaseEntity;
-import org.apache.commons.lang3.builder.ToStringBuilder;
-import org.apache.commons.lang3.builder.ToStringStyle;
+import com.ips.system.dto.FileInfoDTO;
+import lombok.Data;
+
+import java.util.Date;
+import java.util.List;
 
 /**
  * 数据预处理对象 biz_preprocessed
@@ -13,6 +15,7 @@ import org.apache.commons.lang3.builder.ToStringStyle;
  * @author Allen
  * @date 2025-05-21
  */
+@Data
 public class Preprocessed extends BaseEntity
 {
     private static final long serialVersionUID = 1L;
@@ -51,105 +54,6 @@ public class Preprocessed extends BaseEntity
     @Excel(name = "结束时间", width = 30, dateFormat = "yyyy-MM-dd")
     private Date endTime;
 
-    public void setId(Long id) 
-    {
-        this.id = id;
-    }
-
-    public Long getId() 
-    {
-        return id;
-    }
-    public void setTaskName(String taskName) 
-    {
-        this.taskName = taskName;
-    }
-
-    public String getTaskName() 
-    {
-        return taskName;
-    }
-    public void setAlgorithmId(Long algorithmId) 
-    {
-        this.algorithmId = algorithmId;
-    }
-
-    public Long getAlgorithmId() 
-    {
-        return algorithmId;
-    }
-    public void setInputPath(String inputPath) 
-    {
-        this.inputPath = inputPath;
-    }
-
-    public String getInputPath() 
-    {
-        return inputPath;
-    }
-    public void setOutputPath(String outputPath) 
-    {
-        this.outputPath = outputPath;
-    }
-
-    public String getOutputPath() 
-    {
-        return outputPath;
-    }
-    public void setAlgorithmParams(String algorithmParams) 
-    {
-        this.algorithmParams = algorithmParams;
-    }
-
-    public String getAlgorithmParams() 
-    {
-        return algorithmParams;
-    }
-    public void setStatus(String status) 
-    {
-        this.status = status;
-    }
-
-    public String getStatus() 
-    {
-        return status;
-    }
-    public void setStartTime(Date startTime) 
-    {
-        this.startTime = startTime;
-    }
-
-    public Date getStartTime() 
-    {
-        return startTime;
-    }
-    public void setEndTime(Date endTime) 
-    {
-        this.endTime = endTime;
-    }
-
-    public Date getEndTime() 
-    {
-        return endTime;
-    }
+    private List<FileInfoDTO> folderInfoDTO;
 
-    @Override
-    public String toString() {
-        return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE)
-            .append("id", getId())
-            .append("taskName", getTaskName())
-            .append("algorithmId", getAlgorithmId())
-            .append("inputPath", getInputPath())
-            .append("outputPath", getOutputPath())
-            .append("algorithmParams", getAlgorithmParams())
-            .append("status", getStatus())
-            .append("startTime", getStartTime())
-            .append("endTime", getEndTime())
-            .append("remark", getRemark())
-            .append("createBy", getCreateBy())
-            .append("createTime", getCreateTime())
-            .append("updateBy", getUpdateBy())
-            .append("updateTime", getUpdateTime())
-            .toString();
-    }
 }

+ 39 - 0
ips-admin/src/main/java/com/ips/system/dto/FileInfoDTO.java

@@ -0,0 +1,39 @@
+package com.ips.system.dto;
+
+import lombok.Data;
+
+// 文件对象
+@Data
+public class FileInfoDTO {
+    private String fileName;    // 不含后缀
+    private String fullName;   // 包含后缀
+    private String filePath;
+    private long fileSize;
+    private String extension;  // 文件扩展名(不带点)
+
+//    private PreprocessResultDto preprocessResult;
+
+    public FileInfoDTO(String fileName, String fullName, String filePath, long fileSize, String extension) {
+        this.fileName = fileName;
+        this.fullName = fullName;
+        this.filePath = filePath;
+        this.fileSize = fileSize;
+        this.extension = extension;
+//        this.preprocessResult = new PreprocessResultDto(fileName);
+    }
+
+//    public FileInfoDTO(String fileName, String filePath, long fileSize, String extension) {
+//        this.fileName = fileName;
+//        this.filePath = filePath;
+//        this.fileSize = fileSize;
+//        this.extension = extension;
+//    }
+
+    public FileInfoDTO(String fileName, String filePath, long fileSize) {
+        this.fileName = fileName;
+        this.filePath = filePath;
+        this.fileSize = fileSize;
+        this.extension = extension;
+    }
+
+}

+ 18 - 0
ips-admin/src/main/java/com/ips/system/dto/FolderInfoDTO.java

@@ -0,0 +1,18 @@
+package com.ips.system.dto;
+
+import lombok.Data;
+
+import java.util.ArrayList;
+import java.util.List;
+
+// 文件夹对象
+@Data
+public class FolderInfoDTO {
+    private String folderName;
+    private List<FileInfoDTO> files = new ArrayList<>();
+
+    public FolderInfoDTO(String folderName) {
+        this.folderName = folderName;
+    }
+}
+

+ 2 - 0
ips-admin/src/main/java/com/ips/system/service/IPreprocessedService.java

@@ -62,4 +62,6 @@ public interface IPreprocessedService
     public int deletePreprocessedById(Long id);
 
     String run(Long id) throws JsonProcessingException;
+
+    Preprocessed getResultDetails(Long id);
 }

+ 23 - 0
ips-admin/src/main/java/com/ips/system/service/impl/PreprocessedServiceImpl.java

@@ -1,5 +1,6 @@
 package com.ips.system.service.impl;
 
+import java.io.IOException;
 import java.util.*;
 
 import com.fasterxml.jackson.core.JsonProcessingException;
@@ -9,9 +10,14 @@ import com.ips.common.utils.DateUtils;
 import com.ips.common.utils.StringUtils;
 import com.ips.system.domain.AlgorithmConfig;
 import com.ips.system.dto.AlgorithmParamsDto;
+import com.ips.system.dto.FileInfoDTO;
+import com.ips.system.dto.FolderInfoDTO;
 import com.ips.system.service.IAlgorithmConfigService;
 import com.ips.system.utils.AlgorithmCaller;
+import com.ips.system.utils.CommonUtils;
 import org.apache.poi.ss.formula.functions.T;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.scheduling.annotation.Async;
 import org.springframework.stereotype.Service;
@@ -29,6 +35,8 @@ import org.springframework.transaction.annotation.Transactional;
 @Service
 public class PreprocessedServiceImpl implements IPreprocessedService 
 {
+
+    protected final Logger logger = LoggerFactory.getLogger(this.getClass());
     @Autowired
     private PreprocessedMapper preprocessedMapper;
 
@@ -153,4 +161,19 @@ public class PreprocessedServiceImpl implements IPreprocessedService
         this.updatePreprocessed(preprocessed);
         return errorMsg;
     }
+
+    @Override
+    public Preprocessed getResultDetails(Long id) {
+        Preprocessed preprocessed = this.selectPreprocessedById(id);
+        String inputPath = preprocessed.getInputPath();
+        try {
+            List<FileInfoDTO> fileInfoDTOList = CommonUtils.getSortedFiles(inputPath, "dat");
+            preprocessed.setFolderInfoDTO(fileInfoDTOList);
+        } catch (IOException e) {
+            logger.error("读取文件夹错误", e);
+            return null;
+        }
+        return preprocessed;
+    }
+
 }

+ 195 - 0
ips-admin/src/main/java/com/ips/system/utils/CommonUtils.java

@@ -0,0 +1,195 @@
+package com.ips.system.utils;
+
+import com.ips.system.dto.FileInfoDTO;
+import com.ips.system.dto.FolderInfoDTO;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Comparator;
+import java.util.List;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+public class CommonUtils {
+    public static List<FolderInfoDTO> scanAndSortWithStream(String rootPath, String extension) throws IOException {
+        Path rootDir = Paths.get(rootPath);
+
+        return Files.walk(rootDir)
+                .filter(p -> Files.isRegularFile(p) && p.toString().endsWith(extension))
+                .collect(Collectors.groupingBy(
+                        file -> file.getParent().getFileName().toString(),
+                        Collectors.mapping(
+                                file -> new FileInfoDTO(
+                                        file.getFileName().toString(),
+                                        file.toString(),
+                                        file.toFile().length()
+                                ),
+                                Collectors.toList()
+                        )
+                ))
+                .entrySet()
+                .stream()
+                .map(entry -> {
+                    FolderInfoDTO folder = new FolderInfoDTO(entry.getKey());
+                    // 对文件按fileName排序
+                    List<FileInfoDTO> sortedFiles = entry.getValue().stream()
+                            .sorted(Comparator.comparing(FileInfoDTO::getFileName))
+                            .collect(Collectors.toList());
+                    folder.getFiles().addAll(sortedFiles);
+                    return folder;
+                })
+                .sorted(Comparator.comparing(FolderInfoDTO::getFolderName)) // 对文件夹按folderName排序
+                .collect(Collectors.toList());
+    }
+//    public static void main(String[] args) throws IOException {
+//        String rootPath = "D:\\ips\\algrothm\\preprocess\\test";
+//        String extension = ".dat";
+//
+//        List<FolderInfoDTO> folders = scanAndSortWithStream(rootPath, extension);
+//
+//        // 打印结果
+//        folders.forEach(folder -> {
+//            System.out.println("文件夹: " + folder.getFolderName());
+//            folder.getFiles().forEach(file ->
+//                    System.out.println("  文件: " + file.getFileName() +
+//                            ", 大小: " + file.getFileSize() + " bytes")
+//            );
+//        });
+//    }
+
+    /**
+     * 获取文件夹下所有文件(fileName不含后缀),并按文件名(不含后缀)排序
+     * @param folderPath 文件夹路径
+     * @return 排序后的文件列表
+     * @throws IOException 如果读取文件出错
+     */
+    public static List<FileInfoDTO> getAllSortedFiles(String folderPath) throws IOException {
+        // 验证输入参数
+        if (folderPath == null) {
+            throw new IllegalArgumentException("文件夹路径不能为null");
+        }
+
+        Path dir = Paths.get(folderPath);
+        if (!Files.isDirectory(dir)) {
+            throw new IllegalArgumentException("提供的路径不是一个有效的文件夹: " + folderPath);
+        }
+
+        try (Stream<Path> paths = Files.walk(dir, 1)) {  // 参数1表示只查看当前目录
+            return paths
+                    .filter(Files::isRegularFile)  // 只处理普通文件
+                    .map(p -> {
+                        String fullName = p.getFileName().toString();
+                        return new FileInfoDTO(
+                                removeExtension(fullName),  // fileName不含后缀
+                                fullName,                   // fullName包含后缀
+                                p.toAbsolutePath().toString(),
+                                p.toFile().length(),
+                                getFileExtension(fullName)  // 扩展名(不带点)
+                        );
+                    })
+                    .sorted(Comparator.comparing(FileInfoDTO::getFileName))  // 按文件名(不含后缀)排序
+                    .collect(Collectors.toList());
+        }
+    }
+
+    /**
+     * 获取文件夹下指定扩展名的文件(fileName不含后缀),并按文件名(不含后缀)排序
+     * @param folderPath 文件夹路径
+     * @param extension 文件扩展名(可带点或不带点),null表示获取所有文件,""表示无后缀文件
+     * @return 排序后的文件列表
+     * @throws IOException 如果读取文件出错
+     */
+    public static List<FileInfoDTO> getSortedFiles(String folderPath, String extension) throws IOException {
+        if (extension == null) {
+            return getAllSortedFiles(folderPath);
+        }
+
+        // 验证输入参数
+        if (folderPath == null) {
+            throw new IllegalArgumentException("文件夹路径不能为null");
+        }
+
+        Path dir = Paths.get(folderPath);
+        if (!Files.isDirectory(dir)) {
+            throw new IllegalArgumentException("提供的路径不是一个有效的文件夹: " + folderPath);
+        }
+
+        // 处理扩展名格式
+        if (!extension.startsWith(".") && !extension.isEmpty()) {
+            extension = "." + extension;
+        }
+        final String finalExtension = extension.toLowerCase();
+
+        try (Stream<Path> paths = Files.walk(dir, 1)) {  // 参数1表示只查看当前目录
+            String finalExtension1 = extension;
+            return paths
+                    .filter(Files::isRegularFile)  // 只处理普通文件
+                    .filter(p -> {
+                        String filename = p.getFileName().toString().toLowerCase();
+                        // 匹配条件:1) 扩展名空字符串(无后缀文件) 2) 匹配指定扩展名
+                        return finalExtension1.isEmpty()
+                                ? !filename.contains(".")  // 无后缀文件
+                                : filename.endsWith(finalExtension);  // 有指定后缀
+                    })
+                    .map(p -> {
+                        String fullName = p.getFileName().toString();
+                        return new FileInfoDTO(
+                                removeExtension(fullName),  // fileName不含后缀
+                                fullName,                 // fullName包含后缀
+                                p.toAbsolutePath().toString(),
+                                p.toFile().length(),
+                                getFileExtension(fullName)  // 扩展名(不带点)
+                        );
+                    })
+                    .sorted(Comparator.comparing(FileInfoDTO::getFileName))  // 按文件名(不含后缀)排序
+                    .collect(Collectors.toList());
+        }
+    }
+
+    // 辅助方法:移除文件扩展名
+    private static String removeExtension(String filename) {
+        int dotIndex = filename.lastIndexOf('.');
+        return (dotIndex == -1) ? filename : filename.substring(0, dotIndex);
+    }
+
+    // 辅助方法:获取文件扩展名(不带点)
+    private static String getFileExtension(String filename) {
+        int dotIndex = filename.lastIndexOf('.');
+        return (dotIndex == -1) ? "" : filename.substring(dotIndex + 1);
+    }
+
+    // 测试方法
+    public static void main(String[] args) {
+        try {
+            String folderPath = "D:\\ips\\algrothm\\preprocess\\test\\test1";
+
+            // 1. 获取所有文件(fileName不含后缀)
+            System.out.println("所有文件(fileName不含后缀):");
+            List<FileInfoDTO> allFiles = getAllSortedFiles(folderPath);
+            allFiles.forEach(file -> System.out.printf("%-30s %-15s %10d bytes %s%n",
+                    file.getFileName(),
+                    "(" + file.getFullName() + ")",
+                    file.getFileSize(),
+                    file.getExtension().isEmpty() ? "[无后缀]" : "扩展名: " + file.getExtension()));
+
+            // 2. 获取PDF文件(fileName不含后缀)
+            System.out.println("\nPDF文件(fileName不含后缀):");
+            List<FileInfoDTO> pdfFiles = getSortedFiles(folderPath, "dat");
+            pdfFiles.forEach(file -> System.out.printf("%-30s %-15s %10d bytes %s%n",
+                    file.getFileName(),
+                    "(" + file.getFullName() + ")",
+                    file.getFileSize(),
+                    file.getExtension().isEmpty() ? "[无后缀]" : "扩展名: " + file.getExtension()));
+            // 3. 获取无后缀文件
+            System.out.println("\n无后缀文件:");
+            List<FileInfoDTO> noExtensionFiles = getSortedFiles(folderPath, "");
+            noExtensionFiles.forEach(file -> System.out.printf("%-30s %10d bytes%n",
+                    file.getFileName(), file.getFileSize()));
+
+        } catch (IOException e) {
+            System.err.println("处理文件时出错: " + e.getMessage());
+        }
+    }
+}

+ 1 - 1
ips-framework/src/main/java/com/ips/framework/config/SecurityConfig.java

@@ -113,7 +113,7 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter
                 // 对于登录login 注册register 验证码captchaImage 允许匿名访问
                 .antMatchers("/login", "/register", "/captchaImage").permitAll()
                 // 静态资源,可匿名访问
-                .antMatchers(HttpMethod.GET, "/", "/*.html", "/**/*.html", "/**/*.css", "/**/*.js", "/profile/**").permitAll()
+                .antMatchers(HttpMethod.GET, "/", "/*.html", "/**/*.html", "/**/*.css", "/**/*.js", "/profile/**", "/biz/common/getStatic").permitAll()
                 .antMatchers("/swagger-ui.html", "/swagger-resources/**", "/webjars/**", "/*/api-docs", "/druid/**").permitAll()
                 // 除上面外的所有请求全部需要鉴权认证
                 .anyRequest().authenticated()

+ 10 - 0
ips-ui/src/api/biz/common.js

@@ -0,0 +1,10 @@
+import request from '@/utils/request'
+
+// 查询算法配置详细
+export function getStatic(filePath) {
+  return request({
+    url: '/biz/common/getStatic',
+    method: 'get',
+    params: filePath
+  })
+}

+ 8 - 0
ips-ui/src/api/biz/preprocessed.js

@@ -50,3 +50,11 @@ export function run(id) {
     method: 'get'
   })
 }
+
+// 获取运行结果详情
+export function getResultDetails(id) {
+  return request({
+    url: '/biz/preprocessed/getResultDetails/' + id,
+    method: 'get'
+  })
+}

+ 421 - 60
ips-ui/src/views/biz/preprocessed/index.vue

@@ -1,6 +1,13 @@
 <template>
   <div class="app-container">
-    <el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
+    <el-form
+      :model="queryParams"
+      ref="queryForm"
+      size="small"
+      :inline="true"
+      v-show="showSearch"
+      label-width="68px"
+    >
       <el-form-item label="任务名称" prop="taskName">
         <el-input
           v-model="queryParams.taskName"
@@ -18,7 +25,11 @@
         />
       </el-form-item>
       <el-form-item label="状态" prop="status">
-        <el-select v-model="queryParams.status" placeholder="请选择状态" clearable>
+        <el-select
+          v-model="queryParams.status"
+          placeholder="请选择状态"
+          clearable
+        >
           <el-option
             v-for="dict in dict.type.biz_status"
             :key="dict.value"
@@ -28,24 +39,36 @@
         </el-select>
       </el-form-item>
       <el-form-item label="开始时间" prop="startTime">
-        <el-date-picker clearable
+        <el-date-picker
+          clearable
           v-model="queryParams.startTime"
           type="date"
           value-format="yyyy-MM-dd"
-          placeholder="请选择开始时间">
+          placeholder="请选择开始时间"
+        >
         </el-date-picker>
       </el-form-item>
       <el-form-item label="结束时间" prop="endTime">
-        <el-date-picker clearable
+        <el-date-picker
+          clearable
           v-model="queryParams.endTime"
           type="date"
           value-format="yyyy-MM-dd"
-          placeholder="请选择结束时间">
+          placeholder="请选择结束时间"
+        >
         </el-date-picker>
       </el-form-item>
       <el-form-item>
-        <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
-        <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
+        <el-button
+          type="primary"
+          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>
 
@@ -58,7 +81,8 @@
           size="mini"
           @click="handleAdd"
           v-hasPermi="['biz:preprocessed:add']"
-        >新增</el-button>
+          >新增</el-button
+        >
       </el-col>
       <el-col :span="1.5">
         <el-button
@@ -69,7 +93,8 @@
           :disabled="single"
           @click="handleUpdate"
           v-hasPermi="['biz:preprocessed:edit']"
-        >修改</el-button>
+          >修改</el-button
+        >
       </el-col>
       <el-col :span="1.5">
         <el-button
@@ -80,7 +105,8 @@
           :disabled="multiple"
           @click="handleDelete"
           v-hasPermi="['biz:preprocessed:remove']"
-        >删除</el-button>
+          >删除</el-button
+        >
       </el-col>
       <el-col :span="1.5">
         <el-button
@@ -90,33 +116,55 @@
           size="mini"
           @click="handleExport"
           v-hasPermi="['biz:preprocessed:export']"
-        >导出</el-button>
+          >导出</el-button
+        >
       </el-col>
-      <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
+      <right-toolbar
+        :showSearch.sync="showSearch"
+        @queryTable="getList"
+      ></right-toolbar>
     </el-row>
 
-    <el-table v-loading="loading" :data="preprocessedList" @selection-change="handleSelectionChange">
+    <el-table
+      v-loading="loading"
+      :data="preprocessedList"
+      @selection-change="handleSelectionChange"
+    >
       <el-table-column type="selection" width="55" align="center" />
       <el-table-column label="编号" align="center" prop="id" />
       <el-table-column label="任务名称" align="center" prop="taskName" />
       <el-table-column label="算法" align="center" prop="algorithmId" />
       <el-table-column label="状态" align="center" prop="status">
         <template slot-scope="scope">
-          <dict-tag :options="dict.type.biz_status" :value="scope.row.status"/>
+          <dict-tag :options="dict.type.biz_status" :value="scope.row.status" />
         </template>
       </el-table-column>
-      <el-table-column label="开始时间" align="center" prop="startTime" width="180">
+      <el-table-column
+        label="开始时间"
+        align="center"
+        prop="startTime"
+        width="180"
+      >
         <template slot-scope="scope">
-          <span>{{ parseTime(scope.row.startTime, '{y}-{m}-{d}') }}</span>
+          <span>{{ parseTime(scope.row.startTime, "{y}-{m}-{d}") }}</span>
         </template>
       </el-table-column>
-      <el-table-column label="结束时间" align="center" prop="endTime" width="180">
+      <el-table-column
+        label="结束时间"
+        align="center"
+        prop="endTime"
+        width="180"
+      >
         <template slot-scope="scope">
-          <span>{{ parseTime(scope.row.endTime, '{y}-{m}-{d}') }}</span>
+          <span>{{ parseTime(scope.row.endTime, "{y}-{m}-{d}") }}</span>
         </template>
       </el-table-column>
       <el-table-column label="备注" align="center" prop="remark" />
-      <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
+      <el-table-column
+        label="操作"
+        align="center"
+        class-name="small-padding fixed-width"
+      >
         <template slot-scope="scope">
           <el-button
             size="mini"
@@ -124,27 +172,38 @@
             icon="el-icon-edit"
             @click="handleRun(scope.row)"
             v-hasPermi="['biz:preprocessed:add']"
-          >运行</el-button>
+            >运行</el-button
+          >
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-edit"
+            @click="handleGetResult(scope.row)"
+            v-hasPermi="['biz:preprocessed:add']"
+            >查看结果</el-button
+          >
           <el-button
             size="mini"
             type="text"
             icon="el-icon-edit"
             @click="handleUpdate(scope.row)"
             v-hasPermi="['biz:preprocessed:edit']"
-          >修改</el-button>
+            >修改</el-button
+          >
           <el-button
             size="mini"
             type="text"
             icon="el-icon-delete"
             @click="handleDelete(scope.row)"
             v-hasPermi="['biz:preprocessed:remove']"
-          >删除</el-button>
+            >删除</el-button
+          >
         </template>
       </el-table-column>
     </el-table>
 
     <pagination
-      v-show="total>0"
+      v-show="total > 0"
       :total="total"
       :page.sync="queryParams.pageNum"
       :limit.sync="queryParams.pageSize"
@@ -158,7 +217,12 @@
           <el-input v-model="form.taskName" placeholder="请输入任务名称" />
         </el-form-item>
         <el-form-item label="算法" prop="algorithmId">
-          <el-select v-model="form.algorithmId" placeholder="请选择算法" @change="changeAlgorithmId" clearable>
+          <el-select
+            v-model="form.algorithmId"
+            placeholder="请选择算法"
+            @change="changeAlgorithmId"
+            clearable
+          >
             <el-option
               v-for="option in configOptions"
               :key="option.id"
@@ -174,10 +238,18 @@
           <el-input v-model="form.outputPath" placeholder="请输入输出路径" />
         </el-form-item>
         <el-form-item label="算法参数" prop="algorithmParams">
-          <el-input v-model="form.algorithmParams" type="textarea" placeholder="请输入内容" />
+          <el-input
+            v-model="form.algorithmParams"
+            type="textarea"
+            placeholder="请输入内容"
+          />
         </el-form-item>
         <el-form-item label="备注" prop="remark">
-          <el-input v-model="form.remark" type="textarea" placeholder="请输入内容" />
+          <el-input
+            v-model="form.remark"
+            type="textarea"
+            placeholder="请输入内容"
+          />
         </el-form-item>
       </el-form>
       <div slot="footer" class="dialog-footer">
@@ -185,17 +257,195 @@
         <el-button @click="cancel">取 消</el-button>
       </div>
     </el-dialog>
+    <!-- 查看数据预处理结果详情 -->
+    <el-dialog
+      title="查看结果详情"
+      :visible.sync="detailsOpen"
+      width="900px"
+      append-to-body
+    >
+      <el-row>
+        <el-col :span="6">
+          <el-table
+            :data="resultDetails.folderInfoDTO"
+            style="width: 100%"
+            border
+            stripe
+            @row-click="handleRowClick"
+          >
+            <!-- 文件名列 -->
+            <el-table-column prop="fullName" label="文件名"> </el-table-column>
+          </el-table>
+        </el-col>
+        <el-col v-if="selectFileName" :span="18">
+          <el-row>
+            <el-card shadow="hover" class="data-card">
+              <el-row
+                :gutter="20"
+                v-for="(value, key) in this.jsonResult"
+                :key="key"
+              >
+                <el-col :span="12" class="key-col">
+                  <div class="key-item">
+                    {{ key }}
+                  </div>
+                </el-col>
+                <el-col :span="12" class="value-col">
+                  <div class="value-item">
+                    {{ value }}
+                  </div>
+                </el-col>
+              </el-row>
+            </el-card>
+          </el-row>
+          <el-row>
+            <div class="image-dashboard">
+              <!-- 标题区域 -->
+              <!-- <el-card shadow="never" class="header-card">
+                <div class="header-title">
+                  <i class="el-icon-picture"></i>
+                  <span>频谱分析图像展示</span>
+                </div>
+              </el-card> -->
+
+              <!-- 四象限图片区域 -->
+              <div class="image-grid">
+                <!-- 第一行 -->
+                <div class="image-row">
+                  <!-- 左上图片 -->
+                  <el-card shadow="hover" class="image-card">
+                    <div slot="header" class="image-header">
+                      <span>原始数据波形图</span>
+                    </div>
+                    <el-image
+                      :src="
+                        encodeURI(
+                          staticsUrl +
+                            '?filePath=' +
+                            resultDetails.outputPath +
+                            '\\' +
+                            selectFileName +
+                            '_originaltime.jpg'
+                        )
+                      "
+                      fit="contain"
+                      class="image-content"
+                    >
+                      <div slot="error" class="image-error">
+                        <i class="el-icon-picture-outline"></i>
+                        <p>图片加载失败</p>
+                      </div>
+                    </el-image>
+                  </el-card>
+
+                  <!-- 右上图片 -->
+                  <el-card shadow="hover" class="image-card">
+                    <div slot="header" class="image-header">
+                      <span>原始数据FFT频谱图</span>
+                    </div>
+                    <el-image
+                      :src="
+                        encodeURI(
+                          staticsUrl +
+                            '?filePath=' +
+                            resultDetails.outputPath +
+                            '\\' +
+                            selectFileName +
+                            '_originalfft.jpg'
+                        )
+                      "
+                      fit="contain"
+                      class="image-content"
+                    >
+                      <div slot="error" class="image-error">
+                        <i class="el-icon-picture-outline"></i>
+                        <p>图片加载失败</p>
+                      </div>
+                    </el-image>
+                  </el-card>
+                </div>
+
+                <!-- 第二行 -->
+                <div class="image-row">
+                  <!-- 左下图片 -->
+                  <el-card shadow="hover" class="image-card">
+                    <div slot="header" class="image-header">
+                      <span>滤波数据波形图</span>
+                    </div>
+                    <el-image
+                      :src="
+                        encodeURI(
+                          staticsUrl +
+                            '?filePath=' +
+                            resultDetails.outputPath +
+                            '\\' +
+                            selectFileName +
+                            '_filteredtime.jpg'
+                        )
+                      "
+                      fit="contain"
+                      class="image-content"
+                    >
+                      <div slot="error" class="image-error">
+                        <i class="el-icon-picture-outline"></i>
+                        <p>图片加载失败</p>
+                      </div>
+                    </el-image>
+                  </el-card>
+
+                  <!-- 右下图片 -->
+                  <el-card shadow="hover" class="image-card">
+                    <div slot="header" class="image-header">
+                      <span>滤波数据FFT频谱图</span>
+                    </div>
+                    <el-image
+                      :src="
+                        encodeURI(
+                          staticsUrl +
+                            '?filePath=' +
+                            resultDetails.outputPath +
+                            '\\' +
+                            selectFileName +
+                            '_filteredfft.jpg'
+                        )
+                      "
+                      fit="contain"
+                      class="image-content"
+                    >
+                      <div slot="error" class="image-error">
+                        <i class="el-icon-picture-outline"></i>
+                        <p>图片加载失败</p>
+                      </div>
+                    </el-image>
+                  </el-card>
+                </div>
+              </div>
+            </div>
+          </el-row>
+        </el-col>
+      </el-row>
+    </el-dialog>
   </div>
 </template>
 
 <script>
-import { listPreprocessed, getPreprocessed, delPreprocessed, addPreprocessed, updatePreprocessed, run } from "@/api/biz/preprocessed";
+import {
+  listPreprocessed,
+  getPreprocessed,
+  delPreprocessed,
+  addPreprocessed,
+  updatePreprocessed,
+  run,
+  getResultDetails,
+} from "@/api/biz/preprocessed";
+import { getStatic } from "@/api/biz/common";
 import { getOptionsByType } from "@/api/biz/config";
 export default {
   name: "Preprocessed",
-  dicts: ['biz_status'],
+  dicts: ["biz_status"],
   data() {
     return {
+      staticsUrl: process.env.VUE_APP_BASE_API + "/biz/common/getStatic",
       // 遮罩层
       loading: true,
       // 选中数组
@@ -228,9 +478,12 @@ export default {
       // 表单参数
       form: {},
       // 表单校验
-      rules: {
-      },
-      configOptions:[],
+      rules: {},
+      configOptions: [],
+      resultDetails: {},
+      detailsOpen: false,
+      jsonResult: {},
+      selectFileName: "",
     };
   },
   created() {
@@ -241,7 +494,7 @@ export default {
     /** 查询数据预处理列表 */
     getList() {
       this.loading = true;
-      listPreprocessed(this.queryParams).then(response => {
+      listPreprocessed(this.queryParams).then((response) => {
         this.preprocessedList = response.rows;
         this.total = response.total;
         this.loading = false;
@@ -268,7 +521,7 @@ export default {
         createBy: null,
         createTime: null,
         updateBy: null,
-        updateTime: null
+        updateTime: null,
       };
       this.resetForm("form");
     },
@@ -284,9 +537,9 @@ export default {
     },
     // 多选框选中数据
     handleSelectionChange(selection) {
-      this.ids = selection.map(item => item.id)
-      this.single = selection.length!==1
-      this.multiple = !selection.length
+      this.ids = selection.map((item) => item.id);
+      this.single = selection.length !== 1;
+      this.multiple = !selection.length;
     },
     /** 新增按钮操作 */
     handleAdd() {
@@ -297,31 +550,31 @@ export default {
     /** 修改按钮操作 */
     handleUpdate(row) {
       this.reset();
-      const id = row.id || this.ids
-      getPreprocessed(id).then(response => {
+      const id = row.id || this.ids;
+      getPreprocessed(id).then((response) => {
         this.form = response.data;
         this.open = true;
         this.title = "修改数据预处理";
       });
     },
-    handleRun(row){
+    handleRun(row) {
       const id = row.id;
-      run(id).then(response => {
-        alert("任务开始运行!")
+      run(id).then((response) => {
+        alert("任务开始运行!");
       });
     },
     /** 提交按钮 */
     submitForm() {
-      this.$refs["form"].validate(valid => {
+      this.$refs["form"].validate((valid) => {
         if (valid) {
           if (this.form.id != null) {
-            updatePreprocessed(this.form).then(response => {
+            updatePreprocessed(this.form).then((response) => {
               this.$modal.msgSuccess("修改成功");
               this.open = false;
               this.getList();
             });
           } else {
-            addPreprocessed(this.form).then(response => {
+            addPreprocessed(this.form).then((response) => {
               this.$modal.msgSuccess("新增成功");
               this.open = false;
               this.getList();
@@ -333,33 +586,141 @@ export default {
     /** 删除按钮操作 */
     handleDelete(row) {
       const ids = row.id || this.ids;
-      this.$modal.confirm('是否确认删除数据预处理编号为"' + ids + '"的数据项?').then(function() {
-        return delPreprocessed(ids);
-      }).then(() => {
-        this.getList();
-        this.$modal.msgSuccess("删除成功");
-      }).catch(() => {});
+      this.$modal
+        .confirm('是否确认删除数据预处理编号为"' + ids + '"的数据项?')
+        .then(function () {
+          return delPreprocessed(ids);
+        })
+        .then(() => {
+          this.getList();
+          this.$modal.msgSuccess("删除成功");
+        })
+        .catch(() => {});
     },
     /** 导出按钮操作 */
     handleExport() {
-      this.download('biz/preprocessed/export', {
-        ...this.queryParams
-      }, `preprocessed_${new Date().getTime()}.xlsx`)
+      this.download(
+        "biz/preprocessed/export",
+        {
+          ...this.queryParams,
+        },
+        `preprocessed_${new Date().getTime()}.xlsx`
+      );
     },
     getOptions() {
-      getOptionsByType("1").then(response => {
+      getOptionsByType("1").then((response) => {
         this.configOptions = response.data;
       });
     },
-    changeAlgorithmId(){
+    changeAlgorithmId() {
       const algorithmId = this.form.algorithmId;
-      const config = this.configOptions.find(item => item.id === algorithmId);
+      const config = this.configOptions.find((item) => item.id === algorithmId);
       if (config && config.params) {
         this.form.algorithmParams = config.params;
       } else {
-        this.form.algorithmParams = '';
+        this.form.algorithmParams = "";
       }
-    }
-  }
+    },
+    handleGetResult(row) {
+      this.resultDetails = {};
+      this.selectFileName = "";
+      const id = row.id;
+      getResultDetails(id).then((response) => {
+        this.resultDetails = response.data;
+        this.detailsOpen = true;
+      });
+    },
+    handleRowClick(row) {
+      this.selectFileName = row.fileName;
+      let filePath = `${this.resultDetails.outputPath}/${row.fileName}_result.json`;
+      getStatic({ filePath }).then((response) => {
+        this.jsonResult = response;
+      });
+      //selectRowImage
+    },
+  },
 };
 </script>
+<style scoped>
+.image-dashboard {
+  padding: 20px;
+  background-color: #f5f7fa;
+}
+
+.header-card {
+  margin-bottom: 20px;
+  border: none;
+}
+
+.header-title {
+  display: flex;
+  align-items: center;
+  font-size: 18px;
+  font-weight: bold;
+}
+
+.header-title i {
+  margin-right: 10px;
+  font-size: 20px;
+  color: #409eff;
+}
+
+.image-grid {
+  display: flex;
+  flex-direction: column;
+  gap: 20px;
+}
+
+.image-row {
+  display: flex;
+  gap: 20px;
+}
+
+.image-card {
+  flex: 1;
+  border-radius: 8px;
+}
+
+.image-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  font-weight: bold;
+}
+
+.image-content {
+  width: 100%;
+  height: 300px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  background-color: #fafafa;
+  border-radius: 4px;
+}
+
+.image-error {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  color: #909399;
+}
+
+.image-error i {
+  font-size: 50px;
+  margin-bottom: 10px;
+}
+
+.dialog-footer {
+  text-align: center;
+}
+
+@media (max-width: 992px) {
+  .image-row {
+    flex-direction: column;
+  }
+
+  .image-content {
+    height: 250px;
+  }
+}
+</style>

+ 1 - 1
ips-ui/vue.config.js

@@ -30,7 +30,7 @@ module.exports = {
   devServer: {
     host: "0.0.0.0",
     port: port,
-    open: true,
+    open: false,
     proxy: {
       // detail: https://cli.vuejs.org/config/#devserver-proxy
       [process.env.VUE_APP_BASE_API]: {

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 1138 - 0
sql/nwpu-ips20250522.sql


Vissa filer visades inte eftersom för många filer har ändrats