mirror of
https://gitee.com/czh-dev/upload-hub
synced 2026-09-05 23:47:46 +08:00
feat:新增文件工具箱
- 本地查找 - 文件树状图 - Diff - 文件加密 - 批量修改文件名 - 文件格式转换
This commit is contained in:
@@ -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<String> drives) {
|
|
||||||
return Result.success(fileSearchService.buildIndex(drives));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 根据文件名称模糊查询
|
|
||||||
*/
|
|
||||||
@GetMapping("/search")
|
|
||||||
public Result<?> search(@RequestParam String keyword) {
|
|
||||||
return Result.success(fileSearchService.searchFiles(keyword));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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<String> 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));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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<FileNode> children; // 子节点
|
||||||
|
}
|
||||||
+4
-1
@@ -1,10 +1,11 @@
|
|||||||
package cn.czh.service;
|
package cn.czh.service;
|
||||||
|
|
||||||
|
import cn.czh.dto.FileNode;
|
||||||
import cn.czh.dto.IndexResult;
|
import cn.czh.dto.IndexResult;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public interface IFileSearchService {
|
public interface ILocalFileService {
|
||||||
|
|
||||||
List<String> getDrives();
|
List<String> getDrives();
|
||||||
|
|
||||||
@@ -12,4 +13,6 @@ public interface IFileSearchService {
|
|||||||
|
|
||||||
List<String> searchFiles(String keyword);
|
List<String> searchFiles(String keyword);
|
||||||
|
|
||||||
|
FileNode getFileTree(String path, boolean showFiles, boolean showFolders, int maxDepth);
|
||||||
|
|
||||||
}
|
}
|
||||||
+43
-2
@@ -1,7 +1,8 @@
|
|||||||
package cn.czh.service.impl;
|
package cn.czh.service.impl;
|
||||||
|
|
||||||
|
import cn.czh.dto.FileNode;
|
||||||
import cn.czh.dto.IndexResult;
|
import cn.czh.dto.IndexResult;
|
||||||
import cn.czh.service.IFileSearchService;
|
import cn.czh.service.ILocalFileService;
|
||||||
import cn.czh.utils.FileSearchUtil;
|
import cn.czh.utils.FileSearchUtil;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
@@ -14,7 +15,7 @@ import java.util.stream.Collectors;
|
|||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@Service
|
@Service
|
||||||
public class FileSearchServiceImpl implements IFileSearchService {
|
public class LocalFileServiceImpl implements ILocalFileService {
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private FileSearchUtil fileSearchUtil;
|
private FileSearchUtil fileSearchUtil;
|
||||||
@@ -76,4 +77,44 @@ public class FileSearchServiceImpl implements IFileSearchService {
|
|||||||
public List<String> searchFiles(String keyword) {
|
public List<String> searchFiles(String keyword) {
|
||||||
return fileSearchUtil.search(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<FileNode> 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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -11,12 +11,15 @@
|
|||||||
"axios": "^1.8.1",
|
"axios": "^1.8.1",
|
||||||
"axios-extra": "^0.0.8",
|
"axios-extra": "^0.0.8",
|
||||||
"core-js": "^3.8.3",
|
"core-js": "^3.8.3",
|
||||||
|
"diff-match-patch": "^1.0.5",
|
||||||
|
"diff2html": "^3.4.51",
|
||||||
"element-ui": "^2.15.14",
|
"element-ui": "^2.15.14",
|
||||||
"os-browserify": "^0.3.0",
|
"os-browserify": "^0.3.0",
|
||||||
"pdfjs-dist": "^5.0.375",
|
"pdfjs-dist": "^5.0.375",
|
||||||
"promise-queue-plus": "^1.2.2",
|
"promise-queue-plus": "^1.2.2",
|
||||||
"spark-md5": "^3.0.2",
|
"spark-md5": "^3.0.2",
|
||||||
"vue": "^2.6.14",
|
"vue": "^2.6.14",
|
||||||
|
"vue-diff": "^1.2.4",
|
||||||
"vue-router": "^3.5.1",
|
"vue-router": "^3.5.1",
|
||||||
"vue-virtual-scroller": "^1.1.2",
|
"vue-virtual-scroller": "^1.1.2",
|
||||||
"vuex": "^3.6.2"
|
"vuex": "^3.6.2"
|
||||||
|
|||||||
@@ -0,0 +1,164 @@
|
|||||||
|
<!-- Toolbox.vue -->
|
||||||
|
<template>
|
||||||
|
<div class="toolbox-container">
|
||||||
|
<el-row :gutter="20" class="tools-grid">
|
||||||
|
<el-col :span="6" v-for="(tool, index) in tools" :key="index">
|
||||||
|
<el-card
|
||||||
|
class="tool-card"
|
||||||
|
@click.native="openTool(tool)"
|
||||||
|
shadow="hover"
|
||||||
|
>
|
||||||
|
<div class="tool-item">
|
||||||
|
<i :class="tool.icon" class="tool-icon"></i>
|
||||||
|
<span class="tool-name">{{ tool.name }}</span>
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
|
<!-- 动态模态框 -->
|
||||||
|
<el-dialog
|
||||||
|
:title="currentTool ? currentTool.name : ''"
|
||||||
|
:visible.sync="dialogVisible"
|
||||||
|
width="60%"
|
||||||
|
:before-close="handleClose"
|
||||||
|
custom-class="tool-dialog"
|
||||||
|
>
|
||||||
|
<component v-if="currentTool" :is="currentTool.component" />
|
||||||
|
</el-dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import FileTree from './tools/FileTree.vue'
|
||||||
|
import FileDiff from './tools/FileDiff.vue'
|
||||||
|
import FileEncrypt from './tools/FileEncrypt.vue'
|
||||||
|
import BatchRename from './tools/BatchRename.vue'
|
||||||
|
import FormatConvert from './tools/FormatConvert.vue'
|
||||||
|
import LocalSearch from './tools/LocalSearch.vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'FileToolbox',
|
||||||
|
components: {
|
||||||
|
FileTree,
|
||||||
|
FileDiff,
|
||||||
|
FileEncrypt,
|
||||||
|
BatchRename,
|
||||||
|
FormatConvert,
|
||||||
|
LocalSearch,
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
tools: [
|
||||||
|
{ name: '本地查找', icon: 'el-icon-search', component: 'LocalSearch', color: '#7232A8' },
|
||||||
|
{ name: '文件树状图', icon: 'el-icon-folder-opened', component: 'FileTree', color: '#409EFF' },
|
||||||
|
{ name: 'Diff比较', icon: 'el-icon-document-copy', component: 'FileDiff', color: '#67C23A' },
|
||||||
|
{ name: '文件加密', icon: 'el-icon-lock', component: 'FileEncrypt', color: '#E6A23C' },
|
||||||
|
{ name: '批量修改文件名', icon: 'el-icon-edit', component: 'BatchRename', color: '#F56C6C' },
|
||||||
|
{ name: '文件格式转换', icon: 'el-icon-refresh', component: 'FormatConvert', color: '#909399' }
|
||||||
|
],
|
||||||
|
currentTool: null,
|
||||||
|
dialogVisible: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
openTool(tool) {
|
||||||
|
this.currentTool = tool
|
||||||
|
this.dialogVisible = true
|
||||||
|
},
|
||||||
|
handleClose(done) {
|
||||||
|
this.$confirm('确定关闭工具吗?')
|
||||||
|
.then(() => {
|
||||||
|
this.dialogVisible = false
|
||||||
|
this.currentTool = null
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
.catch(() => {})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.toolbox-container {
|
||||||
|
padding: 30px;
|
||||||
|
height: 400px;
|
||||||
|
background: #f5f7fa;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tools-grid {
|
||||||
|
max-width: 1200px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tool-card {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
cursor: pointer;
|
||||||
|
border-radius: 10px;
|
||||||
|
background: #ffffff;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tool-card:hover {
|
||||||
|
transform: translateY(-5px);
|
||||||
|
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tool-item {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
padding: 25px;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tool-icon {
|
||||||
|
font-size: 32px;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tool-card:hover .tool-icon {
|
||||||
|
transform: scale(1.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tool-name {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #303133;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tool-card::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 4px;
|
||||||
|
background: v-bind('tools[$index].color');
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 自定义模态框样式 */
|
||||||
|
:deep(.tool-dialog) {
|
||||||
|
border-radius: 10px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.tool-dialog .el-dialog__header) {
|
||||||
|
background: #f5f7fa;
|
||||||
|
padding: 15px 20px;
|
||||||
|
border-bottom: 1px solid #eee;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.tool-dialog .el-dialog__title) {
|
||||||
|
color: #303133;
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.tool-dialog .el-dialog__body) {
|
||||||
|
padding: 20px;
|
||||||
|
background: #ffffff;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
<template>
|
||||||
|
<div class="batch-rename">
|
||||||
|
<el-upload
|
||||||
|
drag
|
||||||
|
multiple
|
||||||
|
:auto-upload="false"
|
||||||
|
:on-change="handleFileChange"
|
||||||
|
>
|
||||||
|
<i class="el-icon-upload"></i>
|
||||||
|
<div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
|
||||||
|
</el-upload>
|
||||||
|
<el-input v-model="renameRule" placeholder="输入重命名规则" class="rename-input"></el-input>
|
||||||
|
<el-button type="primary" @click="rename" class="action-btn">执行重命名</el-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'BatchRename',
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
renameRule: ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
handleFileChange(file, fileList) {
|
||||||
|
console.log('文件列表:', fileList)
|
||||||
|
},
|
||||||
|
rename() {
|
||||||
|
this.$message.success('批量重命名功能待实现')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.batch-rename {
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
.rename-input {
|
||||||
|
margin: 15px 0;
|
||||||
|
}
|
||||||
|
.action-btn {
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
<template>
|
||||||
|
<div class="file-diff">
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-input type="textarea" v-model="text1" placeholder="输入第一个文件内容" :rows="8"></el-input>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-input type="textarea" v-model="text2" placeholder="输入第二个文件内容" :rows="8"></el-input>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
|
<el-button type="primary" @click="compare" class="action-btn">比较</el-button>
|
||||||
|
|
||||||
|
<div v-if="diffHtml" v-html="diffHtml" class="diff-container"></div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { createTwoFilesPatch } from 'diff';
|
||||||
|
import { Diff2Html } from 'diff2html';
|
||||||
|
import 'diff2html/bundles/css/diff2html.min.css';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'FileDiff',
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
text1: '',
|
||||||
|
text2: '',
|
||||||
|
diffHtml: ''
|
||||||
|
};
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
compare() {
|
||||||
|
// 生成 Diff
|
||||||
|
const diff = createTwoFilesPatch('File1', 'File2', this.text1, this.text2);
|
||||||
|
|
||||||
|
// 解析 Diff 并生成 HTML
|
||||||
|
this.diffHtml = Diff2Html.getPrettyHtml(diff, {
|
||||||
|
inputFormat: 'diff',
|
||||||
|
outputFormat: 'side-by-side', // 或 "line-by-line"
|
||||||
|
drawFileList: false
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.file-diff {
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
.action-btn {
|
||||||
|
margin-top: 15px;
|
||||||
|
}
|
||||||
|
.diff-container {
|
||||||
|
margin-top: 20px;
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
padding: 10px;
|
||||||
|
background: #f8f8f8;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
<template>
|
||||||
|
<div class="file-encrypt">
|
||||||
|
<el-form :model="form" label-width="100px">
|
||||||
|
<el-form-item label="文件内容">
|
||||||
|
<el-input v-model="form.content" type="textarea" :rows="5"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="密码">
|
||||||
|
<el-input v-model="form.password" type="password"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" @click="encrypt" class="action-btn">加密</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'FileEncrypt',
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
form: {
|
||||||
|
content: '',
|
||||||
|
password: ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
encrypt() {
|
||||||
|
this.$message.success('加密功能待实现')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.file-encrypt {
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
.action-btn {
|
||||||
|
margin-left: 100px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,275 @@
|
|||||||
|
<template>
|
||||||
|
<div class="file-tree-container">
|
||||||
|
<!-- 目录路径输入框和获取按钮 -->
|
||||||
|
<div class="input-section">
|
||||||
|
<el-input
|
||||||
|
v-model="directoryPath"
|
||||||
|
placeholder="请输入或选择目录路径"
|
||||||
|
style="width: 300px; margin-right: 10px;"
|
||||||
|
></el-input>
|
||||||
|
<el-input
|
||||||
|
v-model.number="maxDepth"
|
||||||
|
type="number"
|
||||||
|
placeholder="输入目录深度"
|
||||||
|
style="width: 120px; margin-right: 10px;"
|
||||||
|
:min="0"
|
||||||
|
></el-input>
|
||||||
|
<el-button type="primary" @click="fetchFileTree">获取文件树</el-button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 过滤选项 -->
|
||||||
|
<div class="filter-section" style="margin-top: 20px;">
|
||||||
|
<el-input
|
||||||
|
v-model="filterText"
|
||||||
|
placeholder="输入关键字过滤"
|
||||||
|
style="width: 200px; margin-right: 20px;"
|
||||||
|
></el-input>
|
||||||
|
<el-checkbox v-model="showFiles">显示文件</el-checkbox>
|
||||||
|
<el-checkbox v-model="showFolders">显示文件夹</el-checkbox>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 文件树和文本展示区域 -->
|
||||||
|
<div class="content-section" style="margin-top: 20px;">
|
||||||
|
<!-- 文件树 -->
|
||||||
|
<div class="tree-section">
|
||||||
|
<el-tree
|
||||||
|
:data="fileTreeData"
|
||||||
|
:props="defaultProps"
|
||||||
|
:filter-node-method="filterNode"
|
||||||
|
ref="tree"
|
||||||
|
node-key="path"
|
||||||
|
default-expand-all
|
||||||
|
class="tree-container"
|
||||||
|
>
|
||||||
|
<span slot-scope="{ node, data }" class="custom-tree-node">
|
||||||
|
<i :class="data.folder ? 'el-icon-folder' : 'el-icon-document'"></i>
|
||||||
|
<span style="margin-left: 8px;">{{ node.label }}</span>
|
||||||
|
</span>
|
||||||
|
</el-tree>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 文本树展示 -->
|
||||||
|
<div class="text-section">
|
||||||
|
<el-input
|
||||||
|
type="textarea"
|
||||||
|
v-model="textTree"
|
||||||
|
placeholder="文件树文本格式将显示在这里"
|
||||||
|
class="text-tree-input"
|
||||||
|
readonly
|
||||||
|
></el-input>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { getFileTree } from '@/utils/api';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'FileTreeComponent',
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
directoryPath: '',
|
||||||
|
filterText: '',
|
||||||
|
showFiles: true,
|
||||||
|
showFolders: true,
|
||||||
|
maxDepth: 3, // 默认深度为 0,表示无限制
|
||||||
|
fileTreeData: [],
|
||||||
|
textTree: '',
|
||||||
|
defaultProps: {
|
||||||
|
children: 'children',
|
||||||
|
label: 'name',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
filterText(val) {
|
||||||
|
this.$refs.tree.filter(val);
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.updateTextTree();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
showFiles() {
|
||||||
|
if (this.directoryPath) this.fetchFileTree();
|
||||||
|
},
|
||||||
|
showFolders() {
|
||||||
|
if (this.directoryPath) this.fetchFileTree();
|
||||||
|
},
|
||||||
|
maxDepth() {
|
||||||
|
if (this.directoryPath) this.fetchFileTree(); // 深度变化时重新获取
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async fetchFileTree() {
|
||||||
|
if (!this.directoryPath) {
|
||||||
|
this.$message.warning('请输入目录路径');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const res = await getFileTree(this.directoryPath, this.showFiles, this.showFolders, this.maxDepth);
|
||||||
|
if (res.code === 200) {
|
||||||
|
this.fileTreeData = [res.data];
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.updateTextTree();
|
||||||
|
});
|
||||||
|
this.$message.success('文件树获取成功');
|
||||||
|
} else {
|
||||||
|
this.$message.error(res.msg || '获取文件树失败');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
this.$message.error('请求失败,请检查网络或路径');
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
filterNode(value, data) {
|
||||||
|
if (!value) return true;
|
||||||
|
return data.name.toLowerCase().includes(value.toLowerCase());
|
||||||
|
},
|
||||||
|
updateTextTree() {
|
||||||
|
const visibleNodes = this.getVisibleNodes();
|
||||||
|
this.generateTextTreeFromVisibleNodes(visibleNodes);
|
||||||
|
},
|
||||||
|
getVisibleNodes() {
|
||||||
|
const tree = this.$refs.tree;
|
||||||
|
const allNodes = [];
|
||||||
|
|
||||||
|
const collectVisibleNodes = (node) => {
|
||||||
|
if (!node) return;
|
||||||
|
if (node.visible) {
|
||||||
|
const nodeData = { ...node.data };
|
||||||
|
if (node.childNodes && node.childNodes.length > 0) {
|
||||||
|
nodeData.children = [];
|
||||||
|
node.childNodes.forEach(child => {
|
||||||
|
if (child.visible) {
|
||||||
|
nodeData.children.push(collectVisibleNodes(child));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (nodeData.children.length === 0) delete nodeData.children;
|
||||||
|
}
|
||||||
|
return nodeData;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
tree.root.childNodes.forEach(rootNode => {
|
||||||
|
const visibleNode = collectVisibleNodes(rootNode);
|
||||||
|
if (visibleNode) allNodes.push(visibleNode);
|
||||||
|
});
|
||||||
|
|
||||||
|
return allNodes.length > 0 ? { name: '', children: allNodes } : { name: '' };
|
||||||
|
},
|
||||||
|
generateTextTreeFromVisibleNodes(node, level = 0, isLast = false, prefix = '') {
|
||||||
|
let result = '';
|
||||||
|
if (!node) return result;
|
||||||
|
|
||||||
|
if (level === 0 && !node.name) {
|
||||||
|
if (node.children && node.children.length > 0) {
|
||||||
|
node.children.forEach((child, index) => {
|
||||||
|
const isChildLast = index === node.children.length - 1;
|
||||||
|
result += this.generateTextTreeFromVisibleNodes(child, 0, isChildLast, '');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const indent = ' '.repeat(level * 2);
|
||||||
|
const connector = isLast ? '└── ' : '├── ';
|
||||||
|
result += `${prefix}${indent}${connector}${node.name}\n`;
|
||||||
|
|
||||||
|
if (node.children && node.children.length > 0) {
|
||||||
|
const newPrefix = prefix + indent + (isLast ? ' ' : '│ ');
|
||||||
|
node.children.forEach((child, index) => {
|
||||||
|
const isChildLast = index === node.children.length - 1;
|
||||||
|
result += this.generateTextTreeFromVisibleNodes(child, level + 1, isChildLast, newPrefix);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.textTree = result.trim();
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.file-tree-container {
|
||||||
|
padding: 20px;
|
||||||
|
height: 400px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-section,
|
||||||
|
.filter-section {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-section {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tree-section {
|
||||||
|
flex: 1;
|
||||||
|
overflow: hidden;
|
||||||
|
margin-right: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tree-container {
|
||||||
|
height: 100%;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-section {
|
||||||
|
flex: 1;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-tree-input {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-tree-input >>> .el-textarea__inner {
|
||||||
|
height: 100%;
|
||||||
|
resize: none;
|
||||||
|
font-family: 'Courier New', Courier, monospace;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 1.5;
|
||||||
|
padding: 10px;
|
||||||
|
background-color: #fff;
|
||||||
|
color: #303133;
|
||||||
|
border: 1px solid #dcdfe6;
|
||||||
|
border-radius: 4px;
|
||||||
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
|
||||||
|
transition: border-color 0.3s, box-shadow 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-tree-input >>> .el-textarea__inner:hover {
|
||||||
|
border-color: #c0c4cc;
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-tree-input >>> .el-textarea__inner::-webkit-scrollbar {
|
||||||
|
width: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-tree-input >>> .el-textarea__inner::-webkit-scrollbar-thumb {
|
||||||
|
background-color: #c1c1c1;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-tree-input >>> .el-textarea__inner::-webkit-scrollbar-thumb:hover {
|
||||||
|
background-color: #a8a8a8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-tree-input >>> .el-textarea__inner::-webkit-scrollbar-track {
|
||||||
|
background-color: #f1f1f1;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-tree-node {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
<template>
|
||||||
|
<div class="format-convert">
|
||||||
|
<el-upload
|
||||||
|
drag
|
||||||
|
:auto-upload="false"
|
||||||
|
:on-change="handleFileChange"
|
||||||
|
>
|
||||||
|
<i class="el-icon-upload"></i>
|
||||||
|
<div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
|
||||||
|
</el-upload>
|
||||||
|
<el-select v-model="targetFormat" placeholder="选择目标格式" class="format-select">
|
||||||
|
<el-option
|
||||||
|
v-for="format in formatOptions"
|
||||||
|
:key="format.value"
|
||||||
|
:label="format.label"
|
||||||
|
:value="format.value"
|
||||||
|
></el-option>
|
||||||
|
</el-select>
|
||||||
|
<el-button type="primary" @click="convert" class="action-btn">转换</el-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'FormatConvert',
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
targetFormat: '',
|
||||||
|
formatOptions: [
|
||||||
|
{ value: 'pdf', label: 'PDF' },
|
||||||
|
{ value: 'docx', label: 'Word' },
|
||||||
|
{ value: 'txt', label: 'TXT' }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
handleFileChange(file) {
|
||||||
|
console.log('待转换文件:', file)
|
||||||
|
},
|
||||||
|
convert() {
|
||||||
|
this.$message.success('格式转换功能待实现')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.format-convert {
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
.format-select {
|
||||||
|
margin: 15px 0;
|
||||||
|
width: 200px;
|
||||||
|
}
|
||||||
|
.action-btn {
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,181 @@
|
|||||||
|
<template>
|
||||||
|
<div class="local-search">
|
||||||
|
<el-form :model="searchForm" inline>
|
||||||
|
<el-form-item label="选择磁盘:">
|
||||||
|
<el-checkbox-group v-model="searchForm.drives">
|
||||||
|
<el-checkbox v-for="drive in availableDrives" :key="drive" :label="drive" />
|
||||||
|
</el-checkbox-group>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="关键字:" style="margin-left: 30px;">
|
||||||
|
<el-input v-model="searchForm.keyword" placeholder="输入关键字" clearable />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" @click="searchFiles" :loading="searching">查找</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
|
||||||
|
<!-- 搜索结果区域 -->
|
||||||
|
<div class="results-area">
|
||||||
|
<!-- 圆形进度条 -->
|
||||||
|
<div v-if="searching" class="progress-container">
|
||||||
|
<el-progress type="circle" :percentage="progress" :width="120" :stroke-width="8" color="#409EFF" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 文件列表 -->
|
||||||
|
<el-table v-else-if="searchResults.length > 0" :data="searchResults" style="width: 100%" max-height="400" stripe>
|
||||||
|
<el-table-column prop="name" label="文件名" width="200" />
|
||||||
|
<el-table-column prop="path" label="路径" />
|
||||||
|
<el-table-column label="操作" width="100">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-button type="text" @click="openLocalDir(scope.row.path)">位置</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
|
||||||
|
<!-- 无结果提示 -->
|
||||||
|
<div v-else-if="searched" class="no-results">
|
||||||
|
未找到匹配的文件
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { getLocalDrives, buildIndex, localFileSearch, openDir } from '@/utils/api'
|
||||||
|
export default {
|
||||||
|
name: 'LocalSearch',
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
searchForm: {
|
||||||
|
drives: [],
|
||||||
|
keyword: ''
|
||||||
|
},
|
||||||
|
availableDrives: ['C:'],
|
||||||
|
searchResults: [],
|
||||||
|
searching: false,
|
||||||
|
searched: false,
|
||||||
|
progress: 0,
|
||||||
|
progressTimer: null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.fetchAvailableDrives()
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async fetchAvailableDrives() {
|
||||||
|
const res = await getLocalDrives()
|
||||||
|
if (res.code === 200) {
|
||||||
|
this.availableDrives = res.data
|
||||||
|
} else {
|
||||||
|
this.$message.error('获取磁盘列表失败')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
},
|
||||||
|
searchFiles() {
|
||||||
|
if (!this.searchForm.drives.length) {
|
||||||
|
this.$message.warning('请至少选择一个磁盘')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (!this.searchForm.keyword.trim()) {
|
||||||
|
this.$message.warning('请输入关键字')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this.searching = true
|
||||||
|
this.searched = false
|
||||||
|
this.searchResults = []
|
||||||
|
this.progress = 0
|
||||||
|
|
||||||
|
// 开始进度条动画
|
||||||
|
this.startProgress()
|
||||||
|
buildIndex(this.searchForm.drives).then((res) => {
|
||||||
|
if (res.code === 200) {
|
||||||
|
localFileSearch(this.searchForm.keyword).then((res) => {
|
||||||
|
if (res.code === 200) {
|
||||||
|
this.searchResults = res.data
|
||||||
|
.filter(path => this.searchForm.drives.some(drive => path.startsWith(drive)))
|
||||||
|
.map(path => ({
|
||||||
|
name: path.split('\\').pop(), // 提取文件名
|
||||||
|
path: path
|
||||||
|
}))
|
||||||
|
this.searching = false
|
||||||
|
this.searched = true
|
||||||
|
} else {
|
||||||
|
this.$message.error('搜索失败')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}).finally(() => {
|
||||||
|
clearInterval(this.progressTimer)
|
||||||
|
this.progress = 100
|
||||||
|
})
|
||||||
|
},
|
||||||
|
startProgress() {
|
||||||
|
if (this.progressTimer) {
|
||||||
|
clearInterval(this.progressTimer)
|
||||||
|
}
|
||||||
|
|
||||||
|
let timeElapsed = 0
|
||||||
|
const totalTime = 60000 // 60秒
|
||||||
|
|
||||||
|
this.progressTimer = setInterval(() => {
|
||||||
|
timeElapsed += 500 // 每0.5秒更新一次
|
||||||
|
|
||||||
|
// 非匀速增长:前快后慢
|
||||||
|
const progressRatio = timeElapsed / totalTime
|
||||||
|
this.progress = Math.min(99, Math.floor(100 * (1 - Math.pow(1 - progressRatio, 2))))
|
||||||
|
|
||||||
|
if (timeElapsed >= totalTime) {
|
||||||
|
clearInterval(this.progressTimer)
|
||||||
|
}
|
||||||
|
}, 500)
|
||||||
|
},
|
||||||
|
openLocalDir(path) {
|
||||||
|
openDir(path)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
beforeDestroy() {
|
||||||
|
// 清理定时器
|
||||||
|
if (this.progressTimer) {
|
||||||
|
clearInterval(this.progressTimer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.local-search {
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-form-item {
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.results-area {
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress-container {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
min-height: 200px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-table {
|
||||||
|
margin-top: 15px;
|
||||||
|
border-radius: 5px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.no-results {
|
||||||
|
text-align: center;
|
||||||
|
color: #909399;
|
||||||
|
padding: 20px;
|
||||||
|
min-height: 200px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -271,6 +271,60 @@ const setPassword = (password) => {
|
|||||||
return http.post("/config/setPassword", formData);
|
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 {
|
export {
|
||||||
getUploadProgress,
|
getUploadProgress,
|
||||||
createMultipartUpload,
|
createMultipartUpload,
|
||||||
@@ -291,5 +345,10 @@ export {
|
|||||||
setPassword,
|
setPassword,
|
||||||
addSharedFile,
|
addSharedFile,
|
||||||
deleteFile,
|
deleteFile,
|
||||||
|
getLocalDrives,
|
||||||
|
buildIndex,
|
||||||
|
localFileSearch,
|
||||||
|
openDir,
|
||||||
|
getFileTree,
|
||||||
httpExtra
|
httpExtra
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -23,7 +23,7 @@
|
|||||||
<div class="tab-underline"></div>
|
<div class="tab-underline"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="activeTab !== 'gallery' && activeTab !== 'shared' && !isWipTab" class="upload-content">
|
<div v-if="activeTab !== 'gallery' && activeTab !== 'toolbox' && activeTab !== 'shared' && !isWipTab" class="upload-content">
|
||||||
<upload-file :file-list.sync="fileList" :accept="allowedFormats" :max-size="maxUploadSize" width="100%"
|
<upload-file :file-list.sync="fileList" :accept="allowedFormats" :max-size="maxUploadSize" width="100%"
|
||||||
height="400px" :storage-type="activeTab" />
|
height="400px" :storage-type="activeTab" />
|
||||||
</div>
|
</div>
|
||||||
@@ -36,6 +36,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<share-file v-else-if="activeTab === 'shared'" />
|
<share-file v-else-if="activeTab === 'shared'" />
|
||||||
|
<file-toolbox v-else-if="activeTab === 'toolbox'" />
|
||||||
<file-gallery v-else />
|
<file-gallery v-else />
|
||||||
<settings-modal :show="showSettings" :allowed-formats="allowedFormats" :max-upload-size="maxUploadSize"
|
<settings-modal :show="showSettings" :allowed-formats="allowedFormats" :max-upload-size="maxUploadSize"
|
||||||
@save="saveSettings" @close="closeSettings" />
|
@save="saveSettings" @close="closeSettings" />
|
||||||
@@ -54,10 +55,11 @@ import StorageConfigModal from '@/components/StorageConfigModal/StorageConfigMod
|
|||||||
import AboutModal from '@/components/AboutModal/AboutModal.vue';
|
import AboutModal from '@/components/AboutModal/AboutModal.vue';
|
||||||
import ShareFile from '@/components/ShareFile/ShareFile.vue';
|
import ShareFile from '@/components/ShareFile/ShareFile.vue';
|
||||||
import PasswordConfig from '@/components/PasswordConfig/PasswordConfig.vue';
|
import PasswordConfig from '@/components/PasswordConfig/PasswordConfig.vue';
|
||||||
|
import FileToolbox from '@/components/Toolbox/FileToolbox.vue';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'MainPage',
|
name: 'MainPage',
|
||||||
components: { UploadFile, FileGallery, SettingsModal, StorageConfigModal, AboutModal, ShareFile, PasswordConfig },
|
components: { UploadFile, FileGallery, SettingsModal, StorageConfigModal, AboutModal, ShareFile, PasswordConfig, FileToolbox },
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
fileList: [],
|
fileList: [],
|
||||||
@@ -87,6 +89,7 @@ export default {
|
|||||||
{ label: 'QiNiu', value: 'qiniu' },
|
{ label: 'QiNiu', value: 'qiniu' },
|
||||||
{ label: '共享文件', value: 'shared' },
|
{ label: '共享文件', value: 'shared' },
|
||||||
...(this.isAdmin ? [{ label: '已上传文件', value: 'gallery' }] : []),
|
...(this.isAdmin ? [{ label: '已上传文件', value: 'gallery' }] : []),
|
||||||
|
{ label: '工具箱', value: 'toolbox' },
|
||||||
];
|
];
|
||||||
},
|
},
|
||||||
isWipTab() {
|
isWipTab() {
|
||||||
|
|||||||
Reference in New Issue
Block a user