diff --git a/upload-file-backend/src/main/java/cn/czh/controller/FileSearchController.java b/upload-file-backend/src/main/java/cn/czh/controller/FileSearchController.java deleted file mode 100644 index 57a4dea..0000000 --- a/upload-file-backend/src/main/java/cn/czh/controller/FileSearchController.java +++ /dev/null @@ -1,42 +0,0 @@ -package cn.czh.controller; - -import cn.czh.base.Result; -import cn.czh.service.IFileSearchService; -import lombok.extern.slf4j.Slf4j; -import org.springframework.web.bind.annotation.*; - -import javax.annotation.Resource; -import java.util.List; - -@Slf4j -@RequestMapping("/fileSearch") -@RestController -public class FileSearchController { - - @Resource - private IFileSearchService fileSearchService; - - /** - * 获取系统磁盘列表 - */ - @GetMapping("/getDrives") - public Result getDrives() { - return Result.success(fileSearchService.getDrives()); - } - - /** - * 根据指定的磁盘列表构建文件索引 - */ - @PostMapping("/buildIndex") - public Result buildIndex(@RequestBody List drives) { - return Result.success(fileSearchService.buildIndex(drives)); - } - - /** - * 根据文件名称模糊查询 - */ - @GetMapping("/search") - public Result search(@RequestParam String keyword) { - return Result.success(fileSearchService.searchFiles(keyword)); - } -} diff --git a/upload-file-backend/src/main/java/cn/czh/controller/LocalSearchController.java b/upload-file-backend/src/main/java/cn/czh/controller/LocalSearchController.java new file mode 100644 index 0000000..fc55b89 --- /dev/null +++ b/upload-file-backend/src/main/java/cn/czh/controller/LocalSearchController.java @@ -0,0 +1,75 @@ +package cn.czh.controller; + +import cn.czh.base.Result; +import cn.czh.service.ILocalFileService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; +import java.util.List; + +@Slf4j +@RequestMapping("/localFile") +@RestController +public class LocalSearchController { + + @Resource + private ILocalFileService fileSearchService; + + /** + * 获取系统磁盘列表 + */ + @GetMapping("/getDrives") + public Result getDrives() { + return Result.success(fileSearchService.getDrives()); + } + + /** + * 根据指定的磁盘列表构建文件索引 + */ + @PostMapping("/buildIndex") + public Result buildIndex(@RequestBody List drives) { + return Result.success(fileSearchService.buildIndex(drives)); + } + + /** + * 根据文件名称模糊查询 + */ + @GetMapping("/search") + public Result search(@RequestParam String keyword) { + return Result.success(fileSearchService.searchFiles(keyword)); + } + + /** + * 根据文件地址打开目录 + */ + @PostMapping("/openDir") + public Result openDir(@RequestParam String dir) { + try { + Runtime.getRuntime().exec("explorer.exe /select," + dir); + return Result.success(); + } catch (Exception e) { + log.error("打开目录失败:{}", e.getMessage(), e); + return Result.error("打开目录失败"); + } + } + + /** + * 根据文件目录展示文件树 + */ + @GetMapping("/getFileTree") + public Result getFileTree( + @RequestParam String path, + @RequestParam(defaultValue = "true") boolean showFiles, + @RequestParam(defaultValue = "true") boolean showFolders, + @RequestParam(defaultValue = "3") int maxDepth + + ) { + // 避免直接输入磁盘进去全盘扫描 + if (path.length() == 2 && path.endsWith(":")) { + return Result.error("请选择目录"); + } + + return Result.success(fileSearchService.getFileTree(path, showFiles, showFolders, maxDepth)); + } +} diff --git a/upload-file-backend/src/main/java/cn/czh/dto/FileNode.java b/upload-file-backend/src/main/java/cn/czh/dto/FileNode.java new file mode 100644 index 0000000..d951b61 --- /dev/null +++ b/upload-file-backend/src/main/java/cn/czh/dto/FileNode.java @@ -0,0 +1,12 @@ +package cn.czh.dto; + +import lombok.Data; +import java.util.List; + +@Data +public class FileNode { + private String name; // 文件/文件夹名称 + private String path; // 绝对路径 + private boolean isFolder; // 是否是文件夹 + private List children; // 子节点 +} diff --git a/upload-file-backend/src/main/java/cn/czh/service/IFileSearchService.java b/upload-file-backend/src/main/java/cn/czh/service/ILocalFileService.java similarity index 57% rename from upload-file-backend/src/main/java/cn/czh/service/IFileSearchService.java rename to upload-file-backend/src/main/java/cn/czh/service/ILocalFileService.java index 6f8f8a7..bba76d7 100644 --- a/upload-file-backend/src/main/java/cn/czh/service/IFileSearchService.java +++ b/upload-file-backend/src/main/java/cn/czh/service/ILocalFileService.java @@ -1,10 +1,11 @@ package cn.czh.service; +import cn.czh.dto.FileNode; import cn.czh.dto.IndexResult; import java.util.List; -public interface IFileSearchService { +public interface ILocalFileService { List getDrives(); @@ -12,4 +13,6 @@ public interface IFileSearchService { List searchFiles(String keyword); + FileNode getFileTree(String path, boolean showFiles, boolean showFolders, int maxDepth); + } diff --git a/upload-file-backend/src/main/java/cn/czh/service/impl/FileSearchServiceImpl.java b/upload-file-backend/src/main/java/cn/czh/service/impl/LocalFileServiceImpl.java similarity index 61% rename from upload-file-backend/src/main/java/cn/czh/service/impl/FileSearchServiceImpl.java rename to upload-file-backend/src/main/java/cn/czh/service/impl/LocalFileServiceImpl.java index 4971ce7..b155099 100644 --- a/upload-file-backend/src/main/java/cn/czh/service/impl/FileSearchServiceImpl.java +++ b/upload-file-backend/src/main/java/cn/czh/service/impl/LocalFileServiceImpl.java @@ -1,7 +1,8 @@ package cn.czh.service.impl; +import cn.czh.dto.FileNode; import cn.czh.dto.IndexResult; -import cn.czh.service.IFileSearchService; +import cn.czh.service.ILocalFileService; import cn.czh.utils.FileSearchUtil; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; @@ -14,7 +15,7 @@ import java.util.stream.Collectors; @Slf4j @Service -public class FileSearchServiceImpl implements IFileSearchService { +public class LocalFileServiceImpl implements ILocalFileService { @Resource private FileSearchUtil fileSearchUtil; @@ -76,4 +77,44 @@ public class FileSearchServiceImpl implements IFileSearchService { public List searchFiles(String keyword) { return fileSearchUtil.search(keyword); } + + @Override + public FileNode getFileTree(String path, boolean showFiles, boolean showFolders, int maxDepth) { + File root = new File(path); + if (!root.exists() || !root.isDirectory()) { + return null; + } + return buildFileTree(root, showFiles, showFolders, 0, maxDepth); + } + + private FileNode buildFileTree(File file, boolean showFiles, boolean showFolders, int currentDepth, int maxDepth) { + // 判断是否匹配类型 + boolean matchesType = (file.isDirectory() && showFolders) || (!file.isDirectory() && showFiles); + + // 如果不匹配类型,则不包含此节点 + if (!matchesType) { + return null; + } + + FileNode node = new FileNode(); + node.setName(file.getName()); + node.setPath(file.getAbsolutePath()); + node.setFolder(file.isDirectory()); + + // 如果是目录且未达到最大深度,递归构建子节点 + if (file.isDirectory() && (maxDepth == 0 || currentDepth < maxDepth)) { + File[] files = file.listFiles(); + if (files != null) { + List children = new ArrayList<>(); + for (File f : files) { + FileNode child = buildFileTree(f, showFiles, showFolders, currentDepth + 1, maxDepth); + if (child != null) { + children.add(child); + } + } + node.setChildren(children.isEmpty() ? null : children); + } + } + return node; + } } diff --git a/upload-file-frontend/package.json b/upload-file-frontend/package.json index 586e67e..beb93b3 100644 --- a/upload-file-frontend/package.json +++ b/upload-file-frontend/package.json @@ -11,12 +11,15 @@ "axios": "^1.8.1", "axios-extra": "^0.0.8", "core-js": "^3.8.3", + "diff-match-patch": "^1.0.5", + "diff2html": "^3.4.51", "element-ui": "^2.15.14", "os-browserify": "^0.3.0", "pdfjs-dist": "^5.0.375", "promise-queue-plus": "^1.2.2", "spark-md5": "^3.0.2", "vue": "^2.6.14", + "vue-diff": "^1.2.4", "vue-router": "^3.5.1", "vue-virtual-scroller": "^1.1.2", "vuex": "^3.6.2" diff --git a/upload-file-frontend/src/components/Toolbox/FileToolbox.vue b/upload-file-frontend/src/components/Toolbox/FileToolbox.vue new file mode 100644 index 0000000..1c79a33 --- /dev/null +++ b/upload-file-frontend/src/components/Toolbox/FileToolbox.vue @@ -0,0 +1,164 @@ + + + + + + \ No newline at end of file diff --git a/upload-file-frontend/src/components/Toolbox/tools/BatchRename.vue b/upload-file-frontend/src/components/Toolbox/tools/BatchRename.vue new file mode 100644 index 0000000..4333b1e --- /dev/null +++ b/upload-file-frontend/src/components/Toolbox/tools/BatchRename.vue @@ -0,0 +1,46 @@ + + + + + \ No newline at end of file diff --git a/upload-file-frontend/src/components/Toolbox/tools/FileDiff.vue b/upload-file-frontend/src/components/Toolbox/tools/FileDiff.vue new file mode 100644 index 0000000..760fe40 --- /dev/null +++ b/upload-file-frontend/src/components/Toolbox/tools/FileDiff.vue @@ -0,0 +1,61 @@ + + + + + diff --git a/upload-file-frontend/src/components/Toolbox/tools/FileEncrypt.vue b/upload-file-frontend/src/components/Toolbox/tools/FileEncrypt.vue new file mode 100644 index 0000000..dd35667 --- /dev/null +++ b/upload-file-frontend/src/components/Toolbox/tools/FileEncrypt.vue @@ -0,0 +1,42 @@ + + + + \ No newline at end of file diff --git a/upload-file-frontend/src/components/Toolbox/tools/FileTree.vue b/upload-file-frontend/src/components/Toolbox/tools/FileTree.vue new file mode 100644 index 0000000..96e2516 --- /dev/null +++ b/upload-file-frontend/src/components/Toolbox/tools/FileTree.vue @@ -0,0 +1,275 @@ + + + + + \ No newline at end of file diff --git a/upload-file-frontend/src/components/Toolbox/tools/FormatConvert.vue b/upload-file-frontend/src/components/Toolbox/tools/FormatConvert.vue new file mode 100644 index 0000000..221f54d --- /dev/null +++ b/upload-file-frontend/src/components/Toolbox/tools/FormatConvert.vue @@ -0,0 +1,57 @@ + + + + \ No newline at end of file diff --git a/upload-file-frontend/src/components/Toolbox/tools/LocalSearch.vue b/upload-file-frontend/src/components/Toolbox/tools/LocalSearch.vue new file mode 100644 index 0000000..bb41b05 --- /dev/null +++ b/upload-file-frontend/src/components/Toolbox/tools/LocalSearch.vue @@ -0,0 +1,181 @@ + + + + + diff --git a/upload-file-frontend/src/utils/api.js b/upload-file-frontend/src/utils/api.js index b45a781..b556c96 100644 --- a/upload-file-frontend/src/utils/api.js +++ b/upload-file-frontend/src/utils/api.js @@ -271,6 +271,60 @@ const setPassword = (password) => { return http.post("/config/setPassword", formData); } +/** + * 获取磁盘列表 + * @returns + */ +const getLocalDrives = () => { + return http.get("/localFile/getDrives"); +} + +/** + * 构建本地文件索引 + * @param {array} drives + * @returns + */ +const buildIndex = (drives) => { + return http.post('/localFile/buildIndex', drives); +} + +/** + * 查找本地文件 + * @param {string} keyword + * @returns + */ +const localFileSearch = (keyword) => { + return http.get("/localFile/search", { + params: { + keyword + } + }); +} + +/** + * 打开文件资源管理器 + * @param {string} dir + * @returns + */ +const openDir = (dir) => { + const formData = new FormData(); + formData.append('dir', dir); + return http.post("/localFile/openDir", formData); +} + +/** + * 查询文件树 + * @param {string} path + * @param {boolean} showFiles + * @param {boolean} showFolders + * @returns + */ +const getFileTree = (path, showFiles, showFolders, maxDepth) => { + return http.get("/localFile/getFileTree", { + params: { path, showFiles, showFolders, maxDepth } + }); +} + export { getUploadProgress, createMultipartUpload, @@ -291,5 +345,10 @@ export { setPassword, addSharedFile, deleteFile, + getLocalDrives, + buildIndex, + localFileSearch, + openDir, + getFileTree, httpExtra }; diff --git a/upload-file-frontend/src/views/MainPage.vue b/upload-file-frontend/src/views/MainPage.vue index 0e1c4d9..d3fd058 100644 --- a/upload-file-frontend/src/views/MainPage.vue +++ b/upload-file-frontend/src/views/MainPage.vue @@ -23,7 +23,7 @@
-
+
@@ -36,6 +36,7 @@
+ @@ -54,10 +55,11 @@ import StorageConfigModal from '@/components/StorageConfigModal/StorageConfigMod import AboutModal from '@/components/AboutModal/AboutModal.vue'; import ShareFile from '@/components/ShareFile/ShareFile.vue'; import PasswordConfig from '@/components/PasswordConfig/PasswordConfig.vue'; +import FileToolbox from '@/components/Toolbox/FileToolbox.vue'; export default { name: 'MainPage', - components: { UploadFile, FileGallery, SettingsModal, StorageConfigModal, AboutModal, ShareFile, PasswordConfig }, + components: { UploadFile, FileGallery, SettingsModal, StorageConfigModal, AboutModal, ShareFile, PasswordConfig, FileToolbox }, data() { return { fileList: [], @@ -87,6 +89,7 @@ export default { { label: 'QiNiu', value: 'qiniu' }, { label: '共享文件', value: 'shared' }, ...(this.isAdmin ? [{ label: '已上传文件', value: 'gallery' }] : []), + { label: '工具箱', value: 'toolbox' }, ]; }, isWipTab() {