整合前端
This commit is contained in:
186
ruoyi-ui/src/components/FileManager/components/header/index.vue
Normal file
186
ruoyi-ui/src/components/FileManager/components/header/index.vue
Normal file
@@ -0,0 +1,186 @@
|
||||
<!-- FileManagerHeader.vue -->
|
||||
<template>
|
||||
<div class="action-bar">
|
||||
<div class="left-actions">
|
||||
<el-input
|
||||
:value="searchText"
|
||||
@input="updateSearchText"
|
||||
placeholder="搜索文件..."
|
||||
clearable
|
||||
prefix-icon="el-icon-search"
|
||||
style="width: 300px"
|
||||
/>
|
||||
|
||||
<el-select
|
||||
:value="filterType"
|
||||
placeholder="文件类型"
|
||||
@change="updateFilterType"
|
||||
clearable
|
||||
style="width: 120px"
|
||||
>
|
||||
<el-option label="全部文件" value="all" />
|
||||
<el-option label="图片" value="image" />
|
||||
<el-option label="文档" value="doc" />
|
||||
<el-option label="视频" value="video" />
|
||||
</el-select>
|
||||
</div>
|
||||
|
||||
<div class="right-actions">
|
||||
<el-radio-group :value="viewMode" @input="updateViewMode">
|
||||
<el-radio-button label="grid">
|
||||
<el-icon class="el-icon-menu" />
|
||||
</el-radio-button>
|
||||
<el-radio-button label="list">
|
||||
<el-icon class="el-icon-s-unfold" />
|
||||
</el-radio-button>
|
||||
</el-radio-group>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
searchText: String,
|
||||
filterType: String,
|
||||
viewMode: {
|
||||
type: String,
|
||||
default: "grid",
|
||||
},
|
||||
},
|
||||
emits: [
|
||||
"update:searchText",
|
||||
"update:filterType",
|
||||
"update:viewMode",
|
||||
"new-file",
|
||||
],
|
||||
methods: {
|
||||
updateSearchText(e) {
|
||||
console.log(e);
|
||||
this.$emit("update:searchText", e);
|
||||
},
|
||||
updateFilterType(e) {
|
||||
console.log(e);
|
||||
this.$emit("update:filterType", e);
|
||||
},
|
||||
updateViewMode(e) {
|
||||
console.log(e);
|
||||
this.$emit("update:viewMode", e);
|
||||
},
|
||||
newFile() {
|
||||
this.$emit("new-file");
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.file-manager {
|
||||
position: relative;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.view-toggle button {
|
||||
margin-right: 10px;
|
||||
padding: 5px 10px;
|
||||
}
|
||||
|
||||
.view-toggle button.active {
|
||||
background: #0078d4;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.file-container.grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.file-container.list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.file-item {
|
||||
padding: 10px;
|
||||
border: 1px solid #ddd;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.file-item:hover {
|
||||
background: #f0f0f0;
|
||||
}
|
||||
|
||||
.file-item.selected {
|
||||
background: #cce8ff;
|
||||
border-color: #0078d4;
|
||||
}
|
||||
|
||||
.file-icon img {
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
}
|
||||
|
||||
.list .file-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.list .file-icon img {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.context-menu {
|
||||
position: fixed;
|
||||
background: white;
|
||||
border: 1px solid #ddd;
|
||||
box-shadow: 2px 2px 5px rgba(0, 0, 0, 0.2);
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.menu-item {
|
||||
padding: 8px 20px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.menu-item:hover {
|
||||
background: #f0f0f0;
|
||||
}
|
||||
|
||||
.menu-item.danger {
|
||||
color: #dc3545;
|
||||
}
|
||||
|
||||
.action-bar {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
padding: 10px;
|
||||
background: #f5f7fa;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.left-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.right-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.el-button-group {
|
||||
margin-left: 15px;
|
||||
}
|
||||
|
||||
.new-btn {
|
||||
margin-left: 10px;
|
||||
}
|
||||
</style>
|
||||
273
ruoyi-ui/src/components/FileManager/components/list/index.vue
Normal file
273
ruoyi-ui/src/components/FileManager/components/list/index.vue
Normal file
@@ -0,0 +1,273 @@
|
||||
<!-- FileList.vue -->
|
||||
<template>
|
||||
<div
|
||||
class="file-container"
|
||||
@keydown="handleKeydown"
|
||||
:class="viewMode"
|
||||
tabindex="0"
|
||||
>
|
||||
<div
|
||||
v-for="file in files"
|
||||
:key="file.id"
|
||||
:class="['file-item', { selected: isSelected(file) }]"
|
||||
@click.stop="selectFile($event, file)"
|
||||
@contextmenu.prevent="showContextMenu($event, file)"
|
||||
>
|
||||
<div class="file-icon">
|
||||
<img :src="ext2Icon(file.ext)" alt="file icon" />
|
||||
</div>
|
||||
<div class="file-name">{{ file.name }}</div>
|
||||
</div>
|
||||
|
||||
<!-- 右键菜单 -->
|
||||
<div
|
||||
v-show="contextMenu.visible"
|
||||
class="context-menu"
|
||||
:style="{ top: contextMenu.top + 'px', left: contextMenu.left + 'px' }"
|
||||
>
|
||||
<div class="menu-item" @click="handlePreview">预览</div>
|
||||
<div class="menu-item" @click="handleDownload">下载</div>
|
||||
<div class="menu-item danger" @click="handleDelete">删除</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ext2Icon } from "@/utils";
|
||||
|
||||
export default {
|
||||
props: {
|
||||
files: Array,
|
||||
viewMode: {
|
||||
type: String,
|
||||
default: "grid",
|
||||
},
|
||||
selectedFiles: Array,
|
||||
},
|
||||
emits: ["select", "preview", "download", "delete"],
|
||||
data() {
|
||||
return {
|
||||
contextMenu: {
|
||||
visible: false,
|
||||
top: 0,
|
||||
left: 0,
|
||||
selectedFile: null,
|
||||
},
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
isSelected(file) {
|
||||
return this.selectedFiles.some((f) => f.id === file.id);
|
||||
},
|
||||
selectFile(event, file) {
|
||||
const newSelection = event.ctrlKey
|
||||
? [...this.selectedFiles, file]
|
||||
: [file];
|
||||
this.$emit("select", newSelection);
|
||||
},
|
||||
showContextMenu(event, file) {
|
||||
if (!this.isSelected(file)) {
|
||||
this.$emit("select", [file]);
|
||||
}
|
||||
|
||||
this.contextMenu = {
|
||||
visible: true,
|
||||
top: event.clientY,
|
||||
left: event.clientX,
|
||||
selectedFile: file,
|
||||
};
|
||||
},
|
||||
handlePreview() {
|
||||
this.$emit("preview", this.selectedFiles);
|
||||
this.contextMenu.visible = false;
|
||||
console.log(this.files);
|
||||
},
|
||||
handleDownload() {
|
||||
this.$emit("download", this.selectedFiles);
|
||||
this.contextMenu.visible = false;
|
||||
},
|
||||
handleDelete() {
|
||||
this.$emit("delete", this.selectedFiles);
|
||||
this.contextMenu.visible = false;
|
||||
},
|
||||
ext2Icon,
|
||||
handleKeydown(event) {
|
||||
console.log(event);
|
||||
if (document.activeElement.tagName === "INPUT") return;
|
||||
|
||||
switch (event.key) {
|
||||
case "Enter":
|
||||
this.handleDownload();
|
||||
break;
|
||||
case " ":
|
||||
event.preventDefault();
|
||||
this.handlePreview();
|
||||
break;
|
||||
case "Delete":
|
||||
this.handleDelete();
|
||||
break;
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.file-manager {
|
||||
position: relative;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.view-toggle button {
|
||||
margin-right: 10px;
|
||||
padding: 5px 10px;
|
||||
}
|
||||
|
||||
.view-toggle button.active {
|
||||
background: #0078d4;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.file-container.grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.file-container.list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.file-item {
|
||||
padding: 10px;
|
||||
border: 1px solid #ddd;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.file-item:hover {
|
||||
background: #f0f0f0;
|
||||
}
|
||||
|
||||
.file-item.selected {
|
||||
background: #cce8ff;
|
||||
border-color: #0078d4;
|
||||
}
|
||||
|
||||
.file-icon img {
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
}
|
||||
|
||||
.list .file-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.list .file-icon img {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.context-menu {
|
||||
position: fixed;
|
||||
background: white;
|
||||
border: 1px solid #ddd;
|
||||
box-shadow: 2px 2px 5px rgba(0, 0, 0, 0.2);
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.menu-item {
|
||||
padding: 8px 20px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.menu-item:hover {
|
||||
background: #f0f0f0;
|
||||
}
|
||||
|
||||
.menu-item.danger {
|
||||
color: #dc3545;
|
||||
}
|
||||
|
||||
.action-bar {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
padding: 10px;
|
||||
background: #f5f7fa;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.left-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.right-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.el-button-group {
|
||||
margin-left: 15px;
|
||||
}
|
||||
|
||||
.new-btn {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
/* 修改 grid 模式下的文件项布局 */
|
||||
.file-container.grid .file-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 12px;
|
||||
min-height: 120px; /* 保持统一高度 */
|
||||
}
|
||||
|
||||
/* 图标区域样式 */
|
||||
.file-container.grid .file-icon {
|
||||
flex: none;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.file-container.grid .file-icon img {
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
object-fit: contain; /* 保持图标比例 */
|
||||
}
|
||||
|
||||
/* 文件名样式 */
|
||||
.file-container.grid .file-name {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
font-size: 12px;
|
||||
line-height: 1.4;
|
||||
max-height: 2.8em; /* 双行高度 */
|
||||
overflow: hidden;
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: 2; /* 限制两行 */
|
||||
word-break: break-all; /* 长单词换行 */
|
||||
padding: 0 4px;
|
||||
}
|
||||
|
||||
/* 保持 list 模式原有样式不变 */
|
||||
.list .file-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.list .file-icon img {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,42 @@
|
||||
<!-- FileManagerPager.vue -->
|
||||
<template>
|
||||
<div class="file-pager">
|
||||
<el-pagination
|
||||
:current-page="pageNum"
|
||||
:page-sizes="[20, 50, 100]"
|
||||
:page-size="pageSize"
|
||||
:total="total"
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
@size-change="handleSizeChange"
|
||||
@current-change="handlePageChange"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
pageNum: Number,
|
||||
pageSize: Number,
|
||||
total: Number,
|
||||
},
|
||||
emits: ["update:pageNum", "update:pageSize"],
|
||||
methods: {
|
||||
handleSizeChange(size) {
|
||||
console.log(size);
|
||||
this.$emit("update:pageSize", size);
|
||||
},
|
||||
handlePageChange(page) {
|
||||
console.log(page);
|
||||
this.$emit("update:pageNum", page);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.file-pager {
|
||||
margin-top: 20px;
|
||||
text-align: right;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,103 @@
|
||||
<!-- 文件预览器,用来预览不同类型的文件,使用统一入口 -->
|
||||
<template>
|
||||
<div class="file-previewer">
|
||||
<!-- 图片预览 -->
|
||||
<div v-if="isImage" class="preview-container image-preview">
|
||||
<img :src="fileUrl" alt="文件预览" class="preview-content" />
|
||||
</div>
|
||||
|
||||
<!-- PDF预览 -->
|
||||
<div v-else-if="isPDF" class="preview-container pdf-preview">
|
||||
<iframe
|
||||
:src="fileUrl"
|
||||
class="preview-content"
|
||||
frameborder="0"
|
||||
type="application/pdf"
|
||||
></iframe>
|
||||
</div>
|
||||
|
||||
<!-- 不支持的类型 -->
|
||||
<div v-else class="unsupported-type">
|
||||
<el-icon class="el-icon-pear"></el-icon>
|
||||
<div>暂不支持此文件类型预览</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
fileUrl: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
// 提取文件后缀(处理带查询参数的URL)
|
||||
fileExtension() {
|
||||
console.log(this.fileUrl);
|
||||
const cleanUrl = this.fileUrl.split("?")[0]; // 移除查询参数
|
||||
const parts = cleanUrl.split(".");
|
||||
return parts.length > 1 ? parts.pop().toLowerCase() : "";
|
||||
},
|
||||
isImage() {
|
||||
const imageTypes = ["jpg", "jpeg", "png", "gif", "webp"];
|
||||
return imageTypes.includes(this.fileExtension);
|
||||
},
|
||||
isPDF() {
|
||||
return this.fileExtension === "pdf";
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.file-previewer {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.preview-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background: #f5f7fa;
|
||||
}
|
||||
|
||||
.image-preview {
|
||||
max-width: 1000px;
|
||||
max-height: 80vh;
|
||||
}
|
||||
|
||||
.image-preview img {
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
object-fit: contain;
|
||||
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.pdf-preview {
|
||||
height: 80vh;
|
||||
}
|
||||
|
||||
.pdf-preview iframe {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.unsupported-type {
|
||||
padding: 40px;
|
||||
text-align: center;
|
||||
color: #606266;
|
||||
|
||||
.warning-icon {
|
||||
font-size: 48px;
|
||||
color: #e6a23c;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
178
ruoyi-ui/src/components/FileManager/index.vue
Normal file
178
ruoyi-ui/src/components/FileManager/index.vue
Normal file
@@ -0,0 +1,178 @@
|
||||
<!-- FileManager.vue -->
|
||||
<template>
|
||||
<div class="file-manager">
|
||||
<FileManagerHeader
|
||||
:config="headerConfig"
|
||||
:search-text.sync="searchText"
|
||||
:filter-type.sync="filterType"
|
||||
:view-mode.sync="viewMode"
|
||||
@new-file="$emit('new-file')"
|
||||
/>
|
||||
|
||||
<FileList
|
||||
:files="currentFiles"
|
||||
:view-mode="viewMode"
|
||||
:selected-files="selectedFiles"
|
||||
@select="handleSelect"
|
||||
@preview="handlePreview"
|
||||
@download="handleDownload"
|
||||
@delete="$emit('delete', $event)"
|
||||
/>
|
||||
|
||||
<FileManagerPager
|
||||
v-if="pagerConfig.enable"
|
||||
:page-num.sync="pager.pageNum"
|
||||
:page-size.sync="pager.pageSize"
|
||||
:total="totalFiles"
|
||||
/>
|
||||
|
||||
<el-dialog
|
||||
:visible.sync="previwFileVisible"
|
||||
title="文件预览"
|
||||
@close="previewFileVisible = false"
|
||||
>
|
||||
<FilePreviewer :fileUrl="previewFileUrl"></FilePreviewer>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import FileManagerHeader from "./components/header/index.vue";
|
||||
import FileList from "./components/list/index.vue";
|
||||
import FileManagerPager from "./components/pager/index.vue";
|
||||
import FilePreviewer from "./components/previewer/index.vue";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
FileManagerHeader,
|
||||
FileList,
|
||||
FileManagerPager,
|
||||
FilePreviewer,
|
||||
},
|
||||
props: {
|
||||
files: Array,
|
||||
pagerConfig: {
|
||||
default: () => ({
|
||||
enable: true,
|
||||
// 分页实现方式:前端实现 / 后端实现
|
||||
type: "front",
|
||||
}),
|
||||
type: Object,
|
||||
},
|
||||
headerConfig: {
|
||||
default: () => ({
|
||||
search: false,
|
||||
filter: false,
|
||||
view: true,
|
||||
// 搜索实现方式
|
||||
searchType: "front",
|
||||
}),
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
searchText: "",
|
||||
filterType: "all",
|
||||
viewMode: "grid",
|
||||
selectedFiles: [],
|
||||
previwFileVisible: false,
|
||||
previewFileUrl: "",
|
||||
pager: {
|
||||
pageNum: 1,
|
||||
pageSize: 20,
|
||||
},
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
totalFiles() {
|
||||
return this.filteredFiles.length;
|
||||
},
|
||||
filteredFiles() {
|
||||
return this.pipe([this.searchTextPipe], this.files);
|
||||
},
|
||||
currentFiles() {
|
||||
return this.pagerPipe(this.filteredFiles);
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
pager: {
|
||||
handler(newVal) {
|
||||
this.$emit("pagerChange", newVal);
|
||||
},
|
||||
deep: true,
|
||||
},
|
||||
searchText(newVal) {
|
||||
this.$emit("searchChange", newVal);
|
||||
},
|
||||
filterType(newVal) {
|
||||
this.$emit("filterChange", newVal);
|
||||
},
|
||||
viewMode(newVal) {
|
||||
this.$emit("viewChange", newVal);
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
// 搜索管道,文件一次经过文件名(搜索文本)过滤、文件类型过滤和分页过滤
|
||||
// 管道组装函数
|
||||
pipe(pipes, files) {
|
||||
return pipes.reduce((prev, curr) => curr(prev), files);
|
||||
},
|
||||
searchTextPipe(files) {
|
||||
if (this.searchText === "") {
|
||||
return files;
|
||||
}
|
||||
console.log(files, this.searchText);
|
||||
return files.filter((file) => {
|
||||
return file.name.includes(this.searchText);
|
||||
});
|
||||
},
|
||||
pagerPipe(files) {
|
||||
if (!this.pagerConfig.enable) {
|
||||
return files;
|
||||
}
|
||||
if (this.pagerConfig.type === "front") {
|
||||
const start = (this.pager.pageNum - 1) * this.pager.pageSize;
|
||||
const end = start + this.pager.pageSize;
|
||||
return files.slice(start, end);
|
||||
}
|
||||
return files;
|
||||
},
|
||||
handleSelect(files) {
|
||||
this.selectedFiles = files;
|
||||
},
|
||||
handlePreview(files) {
|
||||
if (files.length === 0) {
|
||||
this.$message.warning("请选择进行预览");
|
||||
return;
|
||||
}
|
||||
if (files.length > 1) {
|
||||
this.$message.warning("一次只能预览一个文件");
|
||||
return;
|
||||
}
|
||||
if (this.previwFileVisible) {
|
||||
this.previwFileVisible = false;
|
||||
return;
|
||||
}
|
||||
console.log(files, "文件预览");
|
||||
this.previwFileVisible = true;
|
||||
this.previewFileUrl = files[0].url;
|
||||
},
|
||||
handleDownload(files) {
|
||||
const a = document.createElement("a");
|
||||
a.href = files[0].url;
|
||||
a.download = files[0].name;
|
||||
a.click();
|
||||
a.remove();
|
||||
console.log(files, "文件下载");
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.file-manager {
|
||||
position: relative;
|
||||
padding: 20px;
|
||||
}
|
||||
</style>
|
||||
81
ruoyi-ui/src/components/FileManager/utils/index.js
Normal file
81
ruoyi-ui/src/components/FileManager/utils/index.js
Normal file
@@ -0,0 +1,81 @@
|
||||
// 使用静态导入确保正确获取资源路径
|
||||
import xlsxIcon from "../assets/icons/Excel.png";
|
||||
import gifIcon from "../assets/icons/Gif.png";
|
||||
import pdfIcon from "../assets/icons/Pdf.png";
|
||||
import pptIcon from "../assets/icons/PPT.png";
|
||||
import imageIcon from "../assets/icons/tupian.png";
|
||||
import otherIcon from "../assets/icons/weizhiwenjian.png";
|
||||
import docIcon from "../assets/icons/Word.png";
|
||||
import zipIcon from "../assets/icons/yasuobao.png";
|
||||
|
||||
// 智能扩展名映射配置
|
||||
export const extensionMap = {
|
||||
// 文档类型
|
||||
doc: docIcon,
|
||||
docx: docIcon,
|
||||
dot: docIcon,
|
||||
|
||||
// 电子表格
|
||||
xls: xlsxIcon,
|
||||
xlsx: xlsxIcon,
|
||||
csv: xlsxIcon,
|
||||
|
||||
// 演示文稿
|
||||
ppt: pptIcon,
|
||||
pptx: pptIcon,
|
||||
pot: pptIcon,
|
||||
|
||||
// PDF文档
|
||||
pdf: pdfIcon,
|
||||
|
||||
// 图像类型
|
||||
jpg: imageIcon,
|
||||
jpeg: imageIcon,
|
||||
png: imageIcon,
|
||||
webp: imageIcon,
|
||||
bmp: imageIcon,
|
||||
tiff: imageIcon,
|
||||
|
||||
// 动图
|
||||
gif: gifIcon,
|
||||
|
||||
// 压缩文件
|
||||
zip: zipIcon,
|
||||
rar: zipIcon,
|
||||
"7z": zipIcon,
|
||||
tar: zipIcon,
|
||||
gz: zipIcon,
|
||||
};
|
||||
|
||||
// 带缓存机制的图标获取函数
|
||||
const iconCache = new Map();
|
||||
|
||||
export const ext2Icon = (ext) => {
|
||||
// 防御性处理
|
||||
if (!ext || typeof ext !== "string") return otherIcon;
|
||||
|
||||
// 清洗和标准化扩展名
|
||||
const cleanExt = ext
|
||||
.split(".")
|
||||
.pop() // 取最后一个扩展段
|
||||
.replace(/[^a-z0-9]/gi, "") // 移除非字母数字字符
|
||||
.toLowerCase();
|
||||
|
||||
// 缓存检查
|
||||
if (iconCache.has(cleanExt)) {
|
||||
return iconCache.get(cleanExt);
|
||||
}
|
||||
|
||||
// 获取图标路径
|
||||
const result = extensionMap[cleanExt] || otherIcon;
|
||||
|
||||
// 更新缓存
|
||||
iconCache.set(cleanExt, result);
|
||||
|
||||
// 调试日志(生产环境可移除)
|
||||
if (process.env.NODE_ENV !== "production") {
|
||||
console.log(`[ext2Icon] ${ext} → ${cleanExt} → ${result}`);
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
Reference in New Issue
Block a user