Files
leonpan-pc/app/view/components/file_card.py
Leonmmcoset ead93c1e49 feat(预览): 支持更多文本文件类型预览并增强URL处理
添加对js和html文件类型的文本预览支持,并改进预览组件的URL处理逻辑。现在能自动补全相对路径为完整URL,同时增加详细的日志记录以便调试。
2025-10-29 22:27:39 +08:00

363 lines
12 KiB
Python

# coding: utf-8
from loguru import logger
from PyQt6.QtCore import Qt, pyqtSignal
from PyQt6.QtGui import QPixmap
from PyQt6.QtWidgets import QHBoxLayout
from qfluentwidgets import Action, BodyLabel, CardWidget, ImageLabel, InfoBar, InfoBarPosition, MenuAnimationType, \
MessageBox, PushButton, RoundMenu, StrongBodyLabel
from qfluentwidgets import FluentIcon as FIF
from app.core import (DeleteFileThread, formatDate, formatSize, getFileIcon, lang, signalBus)
from app.view.widgets.share_file_messageBox import ShareFileMessageBox
class FileCard(CardWidget):
def __init__(self, _id, fileName, fileType, path, date, size, parent=None):
super().__init__(parent)
self._id = _id
self.fileName = fileName
self.fileSize = size
self.changeTime = date
self.fileType = fileType
self.filePath = path
self.setFixedHeight(50)
self.iconLabel = ImageLabel(self)
self.fileNameLabel = StrongBodyLabel(self.fileName, self)
self.fileSizeLabel = BodyLabel(formatSize(self.fileSize), self)
self.changeTimeLabel = BodyLabel(formatDate(self.changeTime), self)
self.hBoxLayout = QHBoxLayout(self)
self.hBoxLayout.addWidget(self.iconLabel)
self.hBoxLayout.addWidget(self.fileNameLabel)
self.hBoxLayout.addWidget(self.fileSizeLabel)
self.hBoxLayout.addWidget(self.changeTimeLabel)
self.hBoxLayout.setStretch(0, 1)
self.hBoxLayout.setStretch(1, 2)
self.hBoxLayout.setStretch(2, 1)
self.hBoxLayout.setContentsMargins(10, 10, 10, 10)
self.hBoxLayout.setSpacing(10)
self.loadIcon()
if self.fileType == "dir":
self.clicked.connect(self.dirClicked)
self.suffix = fileName.split(".")[-1].lower()
def mousePressEvent(self, event):
"""重写鼠标点击事件,区分左右键"""
if event.button() == Qt.MouseButton.RightButton:
# 使用globalPosition()获取全局位置并转换为适合菜单显示的坐标
global_pos = event.globalPosition().toPoint()
if self.fileType == "file":
self.showFileContextMenu(global_pos)
else:
self.showFolderContextMenu(global_pos)
else:
super().mousePressEvent(event)
def loadIcon(self):
icon_name = getFileIcon(self.fileType, self.fileName)
self.iconLabel.setImage(QPixmap(f":app/icons/{icon_name}"))
self.iconLabel.scaledToHeight(25)
self.iconLabel.scaledToWidth(25)
def dirClicked(self):
if self.filePath == "/":
paths = "/" + self.fileName
else:
paths = f"{self.filePath}/{self.fileName}"
signalBus.dirOpenSignal.emit(paths)
def selfPreview(self):
if self.fileType == "file" and self.suffix in [
"jpg",
"png",
"jpeg",
"bmp",
"gif",
]:
signalBus.imagePreviewSignal.emit(self._id)
if self.fileType == "file" and self.suffix in ["txt", "py", "md", "js", "html"]:
signalBus.txtPreviewSignal.emit(self._id)
def downloadFile(self):
if self.fileType == "file":
# 构建Cloudreve V4 API所需的正确路径格式
# 确保不会出现重复的前缀和文件名
if self.filePath == "/":
# 根目录情况
full_path = f"cloudreve://my/{self.fileName}"
else:
# 子目录情况,确保正确拼接路径
# 清理路径,避免重复的斜杠
clean_path = self.filePath.lstrip("/")
full_path = f"cloudreve://my/{clean_path}/{self.fileName}"
# 确保路径格式正确,没有重复的部分
full_path = full_path.replace("cloudreve://my/cloudreve://my", "cloudreve://my")
# 确保没有重复的文件名
if f"/{self.fileName}/{self.fileName}" in full_path:
full_path = full_path.replace(f"/{self.fileName}/{self.fileName}", f"/{self.fileName}")
signalBus.addDownloadFileTask.emit(
f"own.{self.suffix}", self.fileName, full_path
)
def showFileContextMenu(self, pos):
"""显示上下文菜单"""
menu = RoundMenu(parent=self)
menu.addActions(
[
Action(
FIF.DOWNLOAD, lang("下载"), triggered=lambda: self.downloadFile()
),
Action(
FIF.PROJECTOR, lang("预览"), triggered=lambda: self.selfPreview()
),
]
)
menu.addSeparator()
menu.addActions(
[
Action(FIF.DELETE, lang("删除"), triggered=lambda: self.deleteSelf()),
]
)
menu.exec(pos, aniType=MenuAnimationType.DROP_DOWN)
def showFolderContextMenu(self, pos):
"""显示上下文菜单"""
menu = RoundMenu(parent=self)
menu.addActions(
[
Action(FIF.DOWNLOAD, lang("进入"), triggered=lambda: self.dirClicked()),
]
)
menu.addSeparator()
menu.addActions(
[
Action(FIF.DELETE, lang("删除"), triggered=lambda: self.deleteSelf()),
]
)
menu.exec(pos, aniType=MenuAnimationType.DROP_DOWN)
def deleteSelf(self):
w = MessageBox(
"确认删除",
f"你确定要删除{self.fileName}吗?\n删除后不可恢复噢!",
parent=self.window(),
)
if w.exec():
self.deleteThread = DeleteFileThread(self._id, self.fileType)
self.deleteThread.successDelete.connect(self.deleteSuccess)
self.deleteThread.errorDelete.connect(self.deleteError)
self.deleteThread.start()
else:
InfoBar.info(
"提示",
"删除已取消",
Qt.Orientation.Horizontal,
True,
1000,
InfoBarPosition.TOP_RIGHT,
self.window(),
)
def deleteSuccess(self):
InfoBar.success(
"成功",
"成功删除",
Qt.Orientation.Horizontal,
True,
1000,
InfoBarPosition.TOP_RIGHT,
self.window(),
)
signalBus.refreshFolderListSignal.emit()
def deleteError(self, error_msg):
InfoBar.error(
"失败",
"删除失败",
Qt.Orientation.Horizontal,
True,
1000,
InfoBarPosition.TOP_RIGHT,
self.window(),
)
logger.error(f"删除文件失败:{error_msg}")
def contextMenuEvent(self, e):
"""重写上下文菜单事件,确保只有右键点击才会触发"""
pass
class ShareFileCard(CardWidget):
def __init__(self, data, parent=None):
super().__init__(parent)
self._id = data["key"]
self.fileName = data["source"]["name"]
self.fileSize = data["source"]["size"]
self.changeTime = data["create_date"]
self.fileType = "dir" if data["is_dir"] else "file"
self.preview = data["preview"]
self.passWord = data["password"]
self.remainDownloads = data["remain_downloads"]
self.downloads = data["downloads"]
self.score = data["score"]
self.views = data["views"]
self.expireTime = data["expire"]
self.setFixedHeight(50)
self.iconLabel = ImageLabel(self)
self.fileNameLabel = StrongBodyLabel(self.fileName, self)
self.changeTimeLabel = BodyLabel(formatDate(self.changeTime), self)
self.viewButton = PushButton("查看", self)
self.viewButton.clicked.connect(self.viewFile)
self.hBoxLayout = QHBoxLayout(self)
self.hBoxLayout.addWidget(self.iconLabel)
self.hBoxLayout.addWidget(self.fileNameLabel)
self.hBoxLayout.addWidget(self.changeTimeLabel)
self.hBoxLayout.addWidget(self.viewButton)
self.hBoxLayout.setStretch(0, 1)
self.hBoxLayout.setStretch(1, 2)
self.hBoxLayout.setStretch(2, 1)
self.hBoxLayout.setContentsMargins(10, 10, 10, 10)
self.hBoxLayout.setSpacing(10)
self.loadIcon()
self.suffix = self.fileName.split(".")[-1].lower()
def loadIcon(self):
icon_name = getFileIcon(self.fileType, self.fileName)
self.iconLabel.setImage(QPixmap(f":app/icons/{icon_name}"))
self.iconLabel.scaledToHeight(25)
self.iconLabel.scaledToWidth(25)
def viewFile(self):
if self.fileType == "file":
w = ShareFileMessageBox(
self._id, self.iconLabel.pixmap(), self.suffix, self.window()
)
if w.exec():
...
else:
signalBus.shareFolderViewSignal.emit(self._id)
class SharedFolderFileCard(CardWidget):
shareFileDownloadSignal = pyqtSignal() # 共享文件下载信号
def __init__(self, key, _id, fileName, fileType, path, date, size, parent=None):
super().__init__(parent)
self._id = _id
self.key = key
self.fileName = fileName
self.fileSize = size
self.changeTime = date
self.fileType = fileType
self.filePath = path
self.setFixedHeight(50)
self.iconLabel = ImageLabel(self)
self.fileNameLabel = StrongBodyLabel(self.fileName, self)
self.fileSizeLabel = BodyLabel(formatSize(self.fileSize), self)
self.changeTimeLabel = BodyLabel(formatDate(self.changeTime), self)
self.hBoxLayout = QHBoxLayout(self)
self.hBoxLayout.addWidget(self.iconLabel)
self.hBoxLayout.addWidget(self.fileNameLabel)
self.hBoxLayout.addWidget(self.fileSizeLabel)
self.hBoxLayout.addWidget(self.changeTimeLabel)
self.hBoxLayout.setStretch(0, 1)
self.hBoxLayout.setStretch(1, 2)
self.hBoxLayout.setStretch(2, 1)
self.hBoxLayout.setContentsMargins(10, 10, 10, 10)
self.hBoxLayout.setSpacing(10)
self.loadIcon()
self.suffix = self.fileName.split(".")[-1].lower()
if self.fileType == "dir":
# 连接左键点击信号
self.clicked.connect(self.dirClicked)
def mousePressEvent(self, event):
"""重写鼠标点击事件,区分左右键"""
if event.button() == Qt.MouseButton.RightButton:
# 右键点击,显示上下文菜单
if self.fileType == "file":
self.showFileContextMenu(event.globalPos())
else:
self.showFolderContextMenu(event.globalPos())
else:
# 左键或其他按钮点击,调用父类处理
super().mousePressEvent(event)
def loadIcon(self):
icon_name = getFileIcon(self.fileType, self.fileName)
self.iconLabel.setImage(QPixmap(f":app/icons/{icon_name}"))
self.iconLabel.scaledToHeight(25)
self.iconLabel.scaledToWidth(25)
def dirClicked(self):
if self.filePath == "/":
paths = "/" + self.fileName
else:
paths = f"{self.filePath}/{self.fileName}"
signalBus.shareDirOpenSignal.emit(paths)
def downloadFile(self):
if self.fileType == "file":
signalBus.addDownloadFileTask.emit(
f"share.{self.suffix}",
self.fileName,
f"{self.filePath}/{self.fileName}.{self.key}",
)
signalBus.shareFileDownloadSignal.emit()
def showFileContextMenu(self, pos):
"""显示上下文菜单"""
menu = RoundMenu(parent=self)
menu.addActions(
[
Action(
FIF.DOWNLOAD, lang("下载"), triggered=lambda: self.downloadFile()
),
]
)
menu.exec(pos, aniType=MenuAnimationType.DROP_DOWN)
def showFolderContextMenu(self, pos):
"""显示上下文菜单"""
menu = RoundMenu(parent=self)
menu.addActions(
[
Action(FIF.DOWNLOAD, lang("进入"), triggered=lambda: self.dirClicked()),
]
)
menu.exec(pos, aniType=MenuAnimationType.DROP_DOWN)