From bd4b0885a1e09a04286da94254efe9461aa4d8bb Mon Sep 17 00:00:00 2001
From: bietiaop <1527109126@qq.com>
Date: Tue, 4 Feb 2025 14:47:38 +0800
Subject: [PATCH] =?UTF-8?q?fix:=20=E9=A2=84=E8=A7=88?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
napcat.webui/package.json | 1 +
.../file_manage/file_preview_modal.tsx | 27 +-
.../src/components/file_manage/file_table.tsx | 313 ++++++++++--------
.../file_manage/image_name_button.tsx | 73 ++++
napcat.webui/src/main.tsx | 1 +
5 files changed, 265 insertions(+), 150 deletions(-)
create mode 100644 napcat.webui/src/components/file_manage/image_name_button.tsx
diff --git a/napcat.webui/package.json b/napcat.webui/package.json
index a5efece0..373b98ba 100644
--- a/napcat.webui/package.json
+++ b/napcat.webui/package.json
@@ -70,6 +70,7 @@
"react-hot-toast": "^2.4.1",
"react-icons": "^5.4.0",
"react-markdown": "^9.0.3",
+ "react-photo-view": "^1.2.7",
"react-redux": "^9.2.0",
"react-responsive": "^10.0.0",
"react-router-dom": "^7.1.4",
diff --git a/napcat.webui/src/components/file_manage/file_preview_modal.tsx b/napcat.webui/src/components/file_manage/file_preview_modal.tsx
index be18f696..792c127f 100644
--- a/napcat.webui/src/components/file_manage/file_preview_modal.tsx
+++ b/napcat.webui/src/components/file_manage/file_preview_modal.tsx
@@ -18,11 +18,10 @@ interface FilePreviewModalProps {
onClose: () => void
}
-const imageExts = ['.png', '.jpg', '.jpeg', '.gif', '.bmp']
-const videoExts = ['.mp4', '.webm']
-const audioExts = ['.mp3', '.wav']
+export const videoExts = ['.mp4', '.webm']
+export const audioExts = ['.mp3', '.wav']
-const supportedPreviewExts = [...imageExts, ...videoExts, ...audioExts]
+export const supportedPreviewExts = [...videoExts, ...audioExts]
export default function FilePreviewModal({
isOpen,
@@ -31,7 +30,7 @@ export default function FilePreviewModal({
}: FilePreviewModalProps) {
const ext = path.extname(filePath).toLowerCase()
const { data, loading, error, run } = useRequest(
- async (path: string) => FileManager.downloadToURL(path),
+ async () => FileManager.downloadToURL(filePath),
{
refreshDeps: [filePath],
refreshDepsAction: () => {
@@ -39,7 +38,7 @@ export default function FilePreviewModal({
if (!filePath || !supportedPreviewExts.includes(ext)) {
return
}
- run(filePath)
+ run()
}
}
)
@@ -55,20 +54,20 @@ export default function FilePreviewModal({
)
- } else if (imageExts.includes(ext)) {
- contentElement = (
-
- )
} else if (videoExts.includes(ext)) {
- contentElement = (
-
- )
+ contentElement =
} else if (audioExts.includes(ext)) {
contentElement =
+ } else {
+ contentElement = (
+
+
+
+ )
}
return (
-
+
文件预览
diff --git a/napcat.webui/src/components/file_manage/file_table.tsx b/napcat.webui/src/components/file_manage/file_table.tsx
index b8632202..8886f8d1 100644
--- a/napcat.webui/src/components/file_manage/file_table.tsx
+++ b/napcat.webui/src/components/file_manage/file_table.tsx
@@ -12,15 +12,19 @@ import {
TableRow
} from '@heroui/table'
import path from 'path-browserify'
-import { useState } from 'react'
+import { useCallback, useEffect, useState } from 'react'
import { BiRename } from 'react-icons/bi'
import { FiCopy, FiDownload, FiMove, FiTrash2 } from 'react-icons/fi'
+import { PhotoSlider } from 'react-photo-view'
import FileIcon from '@/components/file_icon'
import type { FileInfo } from '@/controllers/file_manager'
-interface FileTableProps {
+import { supportedPreviewExts } from './file_preview_modal'
+import ImageNameButton, { PreviewImage, imageExts } from './image_name_button'
+
+export interface FileTableProps {
files: FileInfo[]
currentPath: string
loading: boolean
@@ -62,143 +66,180 @@ export default function FileTable({
const start = (page - 1) * PAGE_SIZE
const end = start + PAGE_SIZE
const displayFiles = files.slice(start, end)
+ const [showImage, setShowImage] = useState(false)
+ const [previewIndex, setPreviewIndex] = useState(0)
+ const [previewImages, setPreviewImages] = useState([])
+
+ const addPreviewImage = useCallback((image: PreviewImage) => {
+ setPreviewImages((prev) => {
+ const exists = prev.some((p) => p.key === image.key)
+ if (exists) return prev
+ return [...prev, image]
+ })
+ }, [])
+
+ useEffect(() => {
+ setPreviewImages([])
+ setPreviewIndex(0)
+ setShowImage(false)
+ }, [files])
+
+ const onPreviewImage = (name: string, images: PreviewImage[]) => {
+ const index = images.findIndex((image) => image.key === name)
+ if (index === -1) {
+ return
+ }
+ setPreviewIndex(index)
+ setShowImage(true)
+ }
+
return (
-
- setPage(page)}
- />
-
- }
- >
-
-
- 名称
-
-
- 类型
-
-
- 大小
-
-
- 修改时间
-
- 操作
-
-
-
+ <>
+ setShowImage(false)}
+ index={previewIndex}
+ onIndexChange={setPreviewIndex}
+ />
+
+ setPage(page)}
+ />
}
- items={displayFiles}
>
- {(file: FileInfo) => {
- const filePath = path.join(currentPath, file.name)
- // 判断预览类型
- const ext = path.extname(file.name).toLowerCase()
- const previewable = [
- '.png',
- '.jpg',
- '.jpeg',
- '.gif',
- '.bmp',
- '.mp4',
- '.webm',
- '.mp3',
- '.wav'
- ].includes(ext)
- return (
-
-
-
-
- {file.isDirectory ? '目录' : '文件'}
-
- {isNaN(file.size) || file.isDirectory
- ? '-'
- : `${file.size} 字节`}
-
- {new Date(file.mtime).toLocaleString()}
-
-
-
-
-
-
-
-
-
-
- )
- }}
-
-
+
+
+ 名称
+
+
+ 类型
+
+
+ 大小
+
+
+ 修改时间
+
+ 操作
+
+
+
+
+ }
+ >
+ {displayFiles.map((file: FileInfo) => {
+ const filePath = path.join(currentPath, file.name)
+ const ext = path.extname(file.name).toLowerCase()
+ const previewable = supportedPreviewExts.includes(ext)
+ const images = previewImages
+ return (
+
+
+ {imageExts.includes(ext) ? (
+ onPreviewImage(file.name, images)}
+ onAddPreview={addPreviewImage}
+ />
+ ) : (
+
+ )}
+
+ {file.isDirectory ? '目录' : '文件'}
+
+ {isNaN(file.size) || file.isDirectory
+ ? '-'
+ : `${file.size} 字节`}
+
+ {new Date(file.mtime).toLocaleString()}
+
+
+
+
+
+
+
+
+
+
+ )
+ })}
+
+
+ >
)
}
diff --git a/napcat.webui/src/components/file_manage/image_name_button.tsx b/napcat.webui/src/components/file_manage/image_name_button.tsx
new file mode 100644
index 00000000..b39a2bb6
--- /dev/null
+++ b/napcat.webui/src/components/file_manage/image_name_button.tsx
@@ -0,0 +1,73 @@
+import { Button } from '@heroui/button'
+import { Image } from '@heroui/image'
+import { Spinner } from '@heroui/spinner'
+import { useRequest } from 'ahooks'
+import path from 'path-browserify'
+import { useEffect } from 'react'
+
+import FileManager from '@/controllers/file_manager'
+
+import FileIcon from '../file_icon'
+
+export interface PreviewImage {
+ key: string
+ src: string
+ alt: string
+}
+export const imageExts = ['.png', '.jpg', '.jpeg', '.gif', '.bmp']
+
+export interface ImageNameButtonProps {
+ name: string
+ filePath: string
+ onPreview: () => void
+ onAddPreview: (image: PreviewImage) => void
+}
+
+export default function ImageNameButton({
+ name,
+ filePath,
+ onPreview,
+ onAddPreview
+}: ImageNameButtonProps) {
+ const { data, loading, error, run } = useRequest(
+ async () => FileManager.downloadToURL(filePath),
+ {
+ refreshDeps: [filePath],
+ refreshDepsAction: () => {
+ const ext = path.extname(filePath).toLowerCase()
+ if (!filePath || !imageExts.includes(ext)) {
+ return
+ }
+ run()
+ }
+ }
+ )
+ useEffect(() => {
+ if (data) {
+ onAddPreview({
+ key: name,
+ src: data,
+ alt: name
+ })
+ }
+ }, [data, name, onAddPreview])
+
+ return (
+
+ ) : loading || !data ? (
+
+ ) : (
+
+ )
+ }
+ >
+ {name}
+
+ )
+}
diff --git a/napcat.webui/src/main.tsx b/napcat.webui/src/main.tsx
index daeeca56..0a549a81 100644
--- a/napcat.webui/src/main.tsx
+++ b/napcat.webui/src/main.tsx
@@ -1,4 +1,5 @@
import ReactDOM from 'react-dom/client'
+import 'react-photo-view/dist/react-photo-view.css'
import { BrowserRouter } from 'react-router-dom'
import App from '@/App.tsx'