重构文件系统

This commit is contained in:
宇阳
2025-01-04 19:26:44 +08:00
parent 8f36c1bc30
commit 573a69be9a
8 changed files with 82 additions and 40 deletions

10
package-lock.json generated
View File

@@ -28,6 +28,7 @@
"react-github-calendar": "^4.2.2", "react-github-calendar": "^4.2.2",
"react-hot-toast": "^2.4.1", "react-hot-toast": "^2.4.1",
"react-icons": "^4.12.0", "react-icons": "^4.12.0",
"react-masonry-css": "^1.0.16",
"react-router-dom": "^6.14.2", "react-router-dom": "^6.14.2",
"react-toastify": "^9.1.3", "react-toastify": "^9.1.3",
"rehype-callouts": "^1.4.1", "rehype-callouts": "^1.4.1",
@@ -7546,6 +7547,15 @@
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
}, },
"node_modules/react-masonry-css": {
"version": "1.0.16",
"resolved": "https://registry.npmmirror.com/react-masonry-css/-/react-masonry-css-1.0.16.tgz",
"integrity": "sha512-KSW0hR2VQmltt/qAa3eXOctQDyOu7+ZBevtKgpNDSzT7k5LA/0XntNa9z9HKCdz3QlxmJHglTZ18e4sX4V8zZQ==",
"license": "MIT",
"peerDependencies": {
"react": ">=16.0.0"
}
},
"node_modules/react-refresh": { "node_modules/react-refresh": {
"version": "0.14.0", "version": "0.14.0",
"resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.0.tgz", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.0.tgz",

View File

@@ -34,6 +34,7 @@
"react-github-calendar": "^4.2.2", "react-github-calendar": "^4.2.2",
"react-hot-toast": "^2.4.1", "react-hot-toast": "^2.4.1",
"react-icons": "^4.12.0", "react-icons": "^4.12.0",
"react-masonry-css": "^1.0.16",
"react-router-dom": "^6.14.2", "react-router-dom": "^6.14.2",
"react-toastify": "^9.1.3", "react-toastify": "^9.1.3",
"rehype-callouts": "^1.4.1", "rehype-callouts": "^1.4.1",

View File

@@ -1,28 +1,14 @@
import Request from '@/utils/request' import Request from '@/utils/request'
import { File } from '@/types/app/file' import { File, FileDir } from '@/types/app/file'
// 删除文件 // 删除文件
export const delFileDataAPI = (filePath: string) => Request<File>("DELETE", `/file?filePath=${filePath}`) export const delFileDataAPI = (filePath: string) => Request<File>("DELETE", `/file/plus?filePath=${filePath}`)
// 获取文件 // 获取文件
export const getFileDataAPI = (filePath: string) => Request<File>("GET", `/file/info?filePath=${filePath}`) export const getFileDataAPI = (filePath: string) => Request<File>("GET", `/file/plus/info?filePath=${filePath}`)
// 获取文件列表 // 获取文件列表
export const getFileListAPI = (data?: QueryData) => Request<File[]>("POST", `/file/list`, { export const getFileListAPI = (dir: string) => Request<File[]>("GET", `/file/plus/list?dir=${dir}`)
data: { ...data?.query },
params: {
dir: data?.dir
}
})
// 分页获取文件列表
export const getFilePagingAPI = (data?: QueryData) => Request<Paginate<File[]>>("POST", `/file/paging`, {
data: { ...data?.query },
params: {
dir: data?.dir,
...data?.pagination
}
})
// 获取目录列表 // 获取目录列表
export const getDirListAPI = () => Request<string[]>("GET", '/file/dir');; export const getDirListAPI = () => Request<FileDir[]>("GET", '/file/plus/dir')

View File

@@ -2,12 +2,12 @@ import { useRef, useState } from 'react';
import { InboxOutlined } from '@ant-design/icons'; import { InboxOutlined } from '@ant-design/icons';
import { message, Modal, Radio, Select, Spin } from 'antd'; import { message, Modal, Radio, Select, Spin } from 'antd';
import { useUserStore } from '@/stores'; import { useUserStore } from '@/stores';
import { FileDir } from '@/types/app/file'; import { DirList } from '@/types/app/file';
import { baseURL } from '@/utils/request'; import { baseURL } from '@/utils/request';
import Compressor from 'compressorjs'; import Compressor from 'compressorjs';
interface UploadFileProps { interface UploadFileProps {
dir: FileDir, dir: DirList,
open: boolean, open: boolean,
onSuccess: (urls: string[]) => void, onSuccess: (urls: string[]) => void,
onCancel: () => void onCancel: () => void
@@ -52,7 +52,7 @@ export default ({ dir, open, onCancel, onSuccess }: UploadFileProps) => {
} }
// 发起网络请求 // 发起网络请求
const res = await fetch(`${baseURL}/file`, { const res = await fetch(`${baseURL}/file/plus`, {
method: "POST", method: "POST",
body: formData, body: formData,
headers: { headers: {

View File

@@ -1,10 +1,12 @@
.FilePage { .FilePage {
.ant-image { .ant-image {
display: block;
width: 100%; width: 100%;
height: 100%;
.ant-image-img { .ant-image-img {
height: 100%; width: 100%;
height: auto;
display: block;
} }
} }
} }

View File

@@ -5,11 +5,19 @@ import FileUpload from '@/components/FileUpload'
import fileSvg from './image/file.svg' import fileSvg from './image/file.svg'
import { delFileDataAPI, getDirListAPI, getFileListAPI } from '@/api/File' import { delFileDataAPI, getDirListAPI, getFileListAPI } from '@/api/File'
import { File } from '@/types/app/file' import { File, FileDir } from '@/types/app/file'
import { PiKeyReturnFill } from "react-icons/pi"; import { PiKeyReturnFill } from "react-icons/pi";
import { DeleteOutlined, DownloadOutlined, RotateLeftOutlined, RotateRightOutlined, SwapOutlined, UndoOutlined, ZoomInOutlined, ZoomOutOutlined, } from '@ant-design/icons'; import { DeleteOutlined, DownloadOutlined, RotateLeftOutlined, RotateRightOutlined, SwapOutlined, UndoOutlined, ZoomInOutlined, ZoomOutOutlined, } from '@ant-design/icons';
import Masonry from "react-masonry-css";
import "./index.scss" import "./index.scss"
const breakpointColumnsObj = {
default: 4,
1100: 3,
700: 2,
500: 1
};
export default () => { export default () => {
const [loading, setLoading] = useState(false) const [loading, setLoading] = useState(false)
@@ -17,7 +25,7 @@ export default () => {
const [openFileInfoDrawer, setOpenFileInfoDrawer] = useState(false); const [openFileInfoDrawer, setOpenFileInfoDrawer] = useState(false);
const [openFilePreviewDrawer, setOpenFilePreviewDrawer] = useState(false); const [openFilePreviewDrawer, setOpenFilePreviewDrawer] = useState(false);
const [dirList, setDirList] = useState<string[]>([]) const [dirList, setDirList] = useState<FileDir[]>([])
const [fileList, setFileList] = useState<File[]>([]) const [fileList, setFileList] = useState<File[]>([])
const [dirName, setDirName] = useState("") const [dirName, setDirName] = useState("")
@@ -33,7 +41,7 @@ export default () => {
// 获取指定目录的文件列表 // 获取指定目录的文件列表
const getFileList = async (dir: string) => { const getFileList = async (dir: string) => {
const { data } = await getFileListAPI({ dir }) const { data } = await getFileListAPI(dir)
if (!fileList.length && !(data as File[]).length) message.error("该目录中没有文件") if (!fileList.length && !(data as File[]).length) message.error("该目录中没有文件")
@@ -44,7 +52,10 @@ export default () => {
// 删除图片 // 删除图片
const onDeleteImage = async (data: File) => { const onDeleteImage = async (data: File) => {
setLoading(true) setLoading(true)
await delFileDataAPI(`${dirName}/${data.name}`)
let filePath = data.url.replace(/^https?:\/\//, '');
await delFileDataAPI(filePath)
message.success("🎉 删除图片成功") message.success("🎉 删除图片成功")
getFileList(dirName) getFileList(dirName)
setFile({} as File) setFile({} as File)
@@ -107,22 +118,36 @@ export default () => {
{ {
fileList.length fileList.length
? ( ? (
fileList.map((item, index) => <Masonry
<div breakpointCols={breakpointColumnsObj}
key={index} className="masonry-grid"
className={`group relative overflow-hidden w-[21.625rem] h-44 p-[2px] flex flex-col items-center cursor-pointer m-4 border-2 ${file.url === item.url ? 'border-primary' : 'border-[#eee]'} rounded-md`} columnClassName="masonry-grid_column"
onClick={() => viewOpenFileInfo(item)}> >
<img src={item.url} alt="" className='rounded-md w-full h-full object-cover object-center' /> {
</div> fileList.map((item, index) =>
) <div
key={index}
className={`group relative overflow-hidden rounded-md cursor-pointer mb-4 border-2 border-[#eee] dark:border-transparent hover:!border-primary p-1 ${file.url === item.url ? 'border-primary' : 'border-gray-100'}`}
onClick={() => viewOpenFileInfo(item)}>
<Image
src={item.url}
alt=""
className='w-full rounded-md'
loading="lazy"
preview={false}
/>
</div>
)
}
</Masonry>
) )
: dirList.map((dir, index) => ( : dirList.map((item, index) => (
<div <div
key={index} key={index}
className='group w-25 flex flex-col items-center cursor-pointer mx-4 my-2' className='group w-25 flex flex-col items-center cursor-pointer mx-4 my-2'
onClick={() => openDir(dir)}> onClick={() => openDir(item.name)}>
<img src={fileSvg} alt="" /> <img src={fileSvg} alt="" />
<p className='group-hover:text-primary transition-colors'>{dir}</p> <p className='group-hover:text-primary transition-colors'>{item.name}</p>
</div> </div>
)) ))
} }

View File

@@ -38,4 +38,17 @@
border-radius: 100% 60% 60% 100%/100% 100% 60% 60%; border-radius: 100% 60% 60% 100%/100% 100% 60% 60%;
transform: translate3d(0, -3px, 0) rotate(.01deg) transform: translate3d(0, -3px, 0) rotate(.01deg)
} }
}
// 瀑布流布局
.masonry-grid {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
width: auto;
}
.masonry-grid_column {
padding-left: 10px;
background-clip: padding-box;
} }

View File

@@ -1,4 +1,4 @@
export type FileDir = ("default" | "article" | "swiper" | string) export type DirList = ("default" | "article" | "swiper" | string)
export interface File { export interface File {
name: string; name: string;
@@ -6,4 +6,9 @@ export interface File {
type: string; type: string;
url: string; url: string;
createTime: number; createTime: number;
}
export interface FileDir {
path: string,
name: string
} }