This commit is contained in:
2025-11-03 22:28:58 +08:00
parent c2aa65193d
commit 5645870954
13 changed files with 522 additions and 141 deletions

View File

@@ -98,6 +98,19 @@
"预览": "preview",
"进入": "enter",
"刷新当前": "refresh current",
"我的分享": "My Shares",
"分享名称": "Share Name",
"分享链接": "Share Link",
"访问次数": "Visit Count",
"操作": "Actions",
"暂无分享": "No Shares Yet",
"你还没有任何分享,分享文件可以让他人便捷地获取你的资料": "You don't have any shares yet. Sharing files allows others to conveniently access your materials.",
"确认删除": "Confirm Deletion",
"确定要删除这个分享链接吗?删除后将无法恢复。": "Are you sure you want to delete this share link? It cannot be recovered after deletion.",
"分享删除成功": "Share deleted successfully",
"删除分享失败": "Failed to delete share",
"加载分享列表失败": "Failed to load share list",
"获取分享列表成功": "Successfully retrieved share list",
"上传文件": "upload file",
"设置存储策略": "Set Storage Strategy"
}

View File

@@ -98,6 +98,19 @@
"预览": "预览",
"进入": "进入",
"刷新当前": "刷新当前",
"我的分享": "我的分享",
"分享名称": "分享名称",
"分享链接": "分享链接",
"访问次数": "访问次数",
"操作": "操作",
"暂无分享": "暂无分享",
"你还没有任何分享,分享文件可以让他人便捷地获取你的资料": "你还没有任何分享,分享文件可以让他人便捷地获取你的资料",
"确认删除": "确认删除",
"确定要删除这个分享链接吗?删除后将无法恢复。": "确定要删除这个分享链接吗?删除后将无法恢复。",
"分享删除成功": "分享删除成功",
"删除分享失败": "删除分享失败",
"加载分享列表失败": "加载分享列表失败",
"获取分享列表成功": "获取分享列表成功",
"上传文件": "上传文件",
"设置存储策略": "设置存储策略"
}

View File

@@ -487,83 +487,130 @@ class MiaoStarsBasicApi:
searchType: Literal["keyword", "internalTag", "externalTag"],
):
"""搜索文件 (Cloudreve V4 API)"""
# 根据Cloudreve V4 API使用/file端点并添加搜索参数
# 根据Cloudreve V4 API使用/file端点并在URI中添加搜索条件
url = "/file"
# 使用Cloudreve V4的URI格式
uri = "cloudreve://my/"
# 构建搜索URI根据Cloudreve V4的搜索规范
# 基础URI指向用户根目录
base_uri = "cloudreve://my/"
# 构建搜索参数
# 根据搜索类型和内容构建查询参数
# 注意Cloudreve V4将搜索条件作为URI的查询部分
search_query = []
if searchType == "keyword" and searchContent:
# 文件名搜索
search_query.append(f"name={quote_plus(searchContent)}")
elif searchType == "internalTag" and searchContent:
# 内部标签搜索
search_query.append(f"tag:internal={quote_plus(searchContent)}")
elif searchType == "externalTag" and searchContent:
# 外部标签搜索
search_query.append(f"tag:external={quote_plus(searchContent)}")
# 构建完整URI
if search_query:
uri = f"{base_uri}?{ '&'.join(search_query) }"
else:
uri = base_uri
# 构建请求参数
params = {
"uri": uri,
"keyword": searchContent,
"page": 0,
"page_size": 100
"page": 0, # 从第一页开始
"page_size": 100, # 使用合理的默认值
"order_by": "updated_at", # 默认按更新时间排序
"order_direction": "desc" # 默认降序
}
# 根据搜索类型调整参数
if searchType == "internalTag":
params["type"] = "internal"
elif searchType == "externalTag":
params["type"] = "tag"
logger.debug(f"发送文件搜索请求: URI={uri}, 搜索类型={searchType}, 搜索内容={searchContent}")
logger.debug(f"发送文件搜索请求: 关键词={searchContent}, 类型={searchType}")
r = self.request("GET", url, params=params)
# 转换响应格式以保持向后兼容
if isinstance(r, dict):
if "data" in r:
if "files" in r["data"]:
# Cloudreve V4 API 格式
return {"code": 0, "data": {"objects": r["data"]["files"]}}
elif isinstance(r["data"], list):
# 如果data是列表直接使用
return {"code": 0, "data": {"objects": r["data"]}}
try:
r = self.request("GET", url, params=params)
logger.warning(f"搜索响应格式不正确: {r}")
return {"code": 0, "data": {"objects": []}}
# 转换响应格式以保持向后兼容
if isinstance(r, dict):
if r.get("code") == 0 and "data" in r:
if "files" in r["data"]:
# Cloudreve V4 API 标准格式
logger.success(f"搜索成功,找到 {len(r['data']['files'])} 个文件")
return {"code": 0, "data": {"objects": r["data"]["files"]}}
elif isinstance(r["data"], list):
# 兼容列表格式响应
return {"code": 0, "data": {"objects": r["data"]}}
else:
logger.warning(f"搜索响应缺少files字段: {r}")
return {"code": 0, "data": {"objects": []}}
else:
# API返回错误
error_msg = r.get("msg", r.get("error", "搜索失败"))
logger.error(f"搜索API返回错误: {error_msg}")
return {"code": -1, "msg": error_msg}
else:
logger.error(f"搜索响应格式无效: {type(r).__name__}")
return {"code": -1, "msg": "搜索响应格式无效"}
except Exception as e:
logger.exception(f"搜索请求异常: {str(e)}")
return {"code": -1, "msg": f"搜索请求异常: {str(e)}"}
def shareSearch(self, keyword, orderBy, order, page):
"""搜索分享 (Cloudreve V4 API)"""
# 使用正确的API端点 - Cloudreve V4使用/share端点获取分享列表
url = "/share"
params = {
"page": page,
"page_size": 50, # 添加默认页面大小以避免"PageSize cannot be empty"错误
"order_by": orderBy,
"order": order,
"keyword": keyword, # 根据实际API支持情况调整
}
r = self.request("GET", url, params=params)
return r
# def shareSearch(self, keyword, orderBy, order, page):
# """搜索分享功能已移除"""
# return {"code": 0, "data": {"objects": []}}
def deleteTag(self, tagId):
"""删除标签"""
"""删除标签 (Cloudreve V4 API)"""
url = f"/tag/{tagId}"
r = self.request("DELETE", url)
# 转换响应格式
if r is None or (isinstance(r, dict) and not r.get("error")):
return {"code": 0, "msg": "标签删除成功"}
# Cloudreve V4 响应格式:{code: 0} 表示成功
if isinstance(r, dict):
if r.get("code") == 0:
return {"code": 0, "msg": "标签删除成功"}
else:
error_msg = r.get("msg", "标签删除失败")
return {"code": -1, "msg": error_msg}
else:
return {"code": -1, "msg": r.get("error", "标签删除失败")}
return {"code": -1, "msg": "标签删除失败:响应格式错误"}
# 重写为不支持添加标签功能
def addTag(self, name, expression):
"""添加标签"""
url = "/tag/filter"
jsons = {
"expression": expression,
"name": name,
"color": "#ff9800",
"icon": "Circle",
}
r = self.request("POST", url, json=jsons)
"""添加标签功能已禁用"""
logger.warning("添加标签功能已禁用")
return {"code": -1, "msg": "添加标签功能已禁用"}
def getMyShares(self, page=1, pageSize=10):
"""获取我的分享列表 (Cloudreve V4 API - GET /user/shares/{user-id})
# 转换响应格式
if r.get("id"):
return {"code": 0, "msg": "标签添加成功", "data": r}
Args:
page: 页码
pageSize: 每页数量
Returns:
分享列表数据
"""
# 使用"me"作为user-id表示当前用户
url = f"/user/shares/me"
params = {
"page": page,
"page_size": pageSize
}
r = self.request("GET", url, params=params)
# Cloudreve V4 响应格式处理
if isinstance(r, dict):
if r.get("code") == 0:
return {
"code": 0,
"msg": "获取分享列表成功",
"data": r.get("data", {})
}
else:
error_msg = r.get("msg", "获取分享列表失败")
return {"code": -1, "msg": error_msg}
else:
return {"code": -1, "msg": r.get("error", "标签添加失败")}
return {"code": -1, "msg": "获取分享列表失败:响应格式错误"}
def getShareFileInfo(self, shareId):
"""获取分享文件信息"""
@@ -589,6 +636,28 @@ class MiaoStarsBasicApi:
return {"code": 0, "msg": "文件内容更新成功"}
else:
return {"code": -1, "msg": r.get("error", "文件内容更新失败")}
def deleteShare(self, shareId):
"""删除分享链接 (Cloudreve V4 API)
Args:
shareId: 分享链接ID
Returns:
操作结果
"""
url = f"/share/{shareId}"
r = self.request("DELETE", url)
# Cloudreve V4 响应格式处理
if isinstance(r, dict):
if r.get("code") == 0:
return {"code": 0, "msg": "删除分享成功"}
else:
error_msg = r.get("msg", "删除分享失败")
return {"code": -1, "msg": error_msg}
else:
return {"code": -1, "msg": "删除分享失败:响应格式错误"}
def renameFile(self, fileUri, newName, fileType="file"):
"""重命名文件 (Cloudreve V4 API)"""

View File

@@ -60,19 +60,32 @@ class ListSearchThread(QThread):
def run(self):
"""执行API请求"""
try:
logger.info(f"开始API请求")
logger.info(f"开始搜索API请求")
response = miaoStarsBasicApi.wareSearch(self.searchContent, self.searchType)
if response["code"] == 0:
logger.success(f"API请求成功")
self.listDictSignal.emit(response)
if isinstance(response, dict):
if response.get("code") == 0:
# 确保数据格式正确
if "data" not in response:
response["data"] = {"objects": []}
elif "objects" not in response["data"]:
response["data"]["objects"] = []
logger.success(f"搜索API请求成功, 找到 {len(response['data']['objects'])} 个文件")
self.listDictSignal.emit(response)
else:
# 处理API返回的错误
error_msg = response.get("msg", "搜索失败")
logger.error(f"搜索API请求失败: {error_msg}")
self.errorSignal.emit(error_msg)
else:
logger.error(f"API请求失败, 错误: {response['msg']}")
self.errorSignal.emit(response["msg"])
# 处理响应格式错误
logger.error(f"搜索API返回无效响应格式: {type(response).__name__}")
self.errorSignal.emit(f"搜索失败: 返回数据格式错误")
except Exception as e:
logger.exception(f"API请求异常, 异常信息: {str(e)}")
self.errorSignal.emit(str(e))
logger.exception(f"搜索API请求异常: {str(e)}")
self.errorSignal.emit(f"搜索过程中发生错误: {str(e)}")
class ListShareThread(QThread):

View File

@@ -70,7 +70,7 @@ class Config(QConfig):
customBackground = ConfigItem(
"Background",
"CustomBackground",
"app\\resource\\images\\bg0.png",
"_internal\\backgrounds\\bg0.png",
)
customOpactity = ConfigItem("Background", "Opactity", 0.2)

View File

@@ -3,4 +3,4 @@
Version information for the application
"""
version = "Alpha 0.1"
version = "Alpha 0.2"

View File

@@ -13,6 +13,7 @@ from app.view.ownFiled_interface import OwnFiledInterface
from app.view.setting_interface import SettingInterface
from app.view.storagespace_interface import StoragespaceInterface
from app.view.task_interface import TaskInterface
from app.view.share_interface import ShareInterface
from app.view.widgets.custom_fluent_window import CustomFluentWindow
from app.view.widgets.preview_box import OptimizedPreviewBox, PreviewTextBox
from app.view.widgets.share_folder_messageBox import ShareFolderMessageBox
@@ -28,6 +29,7 @@ class MainWindow(CustomFluentWindow):
self.ownFiledInterface = OwnFiledInterface(self)
self.storagespaceInterface = StoragespaceInterface(self)
self.taskInterface = TaskInterface(self)
self.shareInterface = ShareInterface(self)
self.appInfoInterface = AppInfoInterface(self)
self.connectSignalToSlot()
@@ -60,6 +62,7 @@ class MainWindow(CustomFluentWindow):
self.storagespaceInterface.objectName(), lang("存储配额")
)
self.navigationInterface.setItemText(self.taskInterface.objectName(), lang("任务管理"))
self.navigationInterface.setItemText(self.shareInterface.objectName(), lang("我的分享"))
self.navigationInterface.setItemText(self.appInfoInterface.objectName(), lang("应用信息"))
def initNavigation(self):
@@ -85,6 +88,13 @@ class MainWindow(CustomFluentWindow):
lang("任务管理"),
position=NavigationItemPosition.TOP,
)
self.addSubInterface(
self.shareInterface,
FluentIcon.SHARE,
lang("我的分享"),
position=NavigationItemPosition.TOP,
)
self.addSubInterface(
self.appInfoInterface,

View File

@@ -12,7 +12,7 @@ from qfluentwidgets import (
from app.core import signalBus
from app.view.widgets.ownfile_scroll_widget import OwnFileScrollWidget
from app.view.widgets.ownFiled_widgets import SearchWidget, TagWidget
from app.view.widgets.share_search_widgets import ShareSearchScrollWidget
from app.view.widgets.ware_search_widgets import WareSearchScrollWidget
@@ -35,8 +35,7 @@ class OwnFiledInterface(QWidget):
self.wareSearchScrollWidget = WareSearchScrollWidget(self)
self.wareSearchScrollWidget.hide()
self.shareSearchScrollWidget = ShareSearchScrollWidget(self)
self.shareSearchScrollWidget.hide()
self.setupUi()
self.connectSignals()
@@ -75,7 +74,6 @@ class OwnFiledInterface(QWidget):
self.vBoxLayout.addLayout(self.topLayout)
self.vBoxLayout.addWidget(self.ownFileScrollWidget)
self.vBoxLayout.addWidget(self.wareSearchScrollWidget)
self.vBoxLayout.addWidget(self.shareSearchScrollWidget)
def tagSearch(self, types, keyword):
self.wareSearchScrollWidget.show()
@@ -84,7 +82,6 @@ class OwnFiledInterface(QWidget):
def search(self):
keyword = self.searchWidget.searchLineEdit.text()
searchType = self.searchWidget.searchButton.text()
if keyword == "" or keyword == ".":
InfoBar.warning(
"注意",
@@ -96,22 +93,14 @@ class OwnFiledInterface(QWidget):
self.window(),
)
return
if searchType == "仓内搜索":
self.wareSearchScrollWidget.show()
self.ownFileScrollWidget.hide()
self.shareSearchScrollWidget.hide()
self.wareSearchScrollWidget.wareSearch("keyword", keyword)
self.tagWidget.tagScrollArea.clearChecked()
elif searchType == "站内搜索":
self.wareSearchScrollWidget.hide()
self.ownFileScrollWidget.hide()
self.shareSearchScrollWidget.show()
self.shareSearchScrollWidget.shareSearch(keyword, 1)
self.tagWidget.tagScrollArea.clearChecked()
# 只保留仓内搜索功能
self.wareSearchScrollWidget.show()
self.ownFileScrollWidget.hide()
self.wareSearchScrollWidget.wareSearch("keyword", keyword)
self.tagWidget.tagScrollArea.clearChecked()
def returnLinkageSwitchingPage(self):
self.wareSearchScrollWidget.hide()
self.shareSearchScrollWidget.hide()
self.ownFileScrollWidget.show()
self.tagWidget.tagScrollArea.clearChecked()
self.searchWidget.searchLineEdit.clear()
@@ -131,8 +120,5 @@ class OwnFiledInterface(QWidget):
self.wareSearchScrollWidget.returnSignal.connect(
self.returnLinkageSwitchingPage
)
self.shareSearchScrollWidget.returnSignal.connect(
self.returnLinkageSwitchingPage
)
self.searchWidget.searchButton.clicked.connect(self.search)
self.tagWidget.tagScrollArea.tagClicked.connect(self.tagSearch)

View File

@@ -183,7 +183,7 @@ class ThemeSettingWidget(GroupHeaderCardWidget):
w = CustomBgMessageBox(self.window())
if w.exec():
index = w.returnImage()
qconfig.set(cfg.customBackground, f"app\\resource\\images\\bg{index}.png")
qconfig.set(cfg.customBackground, f"_internal\\backgrounds\\bg{index}.png")
signalBus.backgroundChanged.emit()
def customBackground(self):

320
app/view/share_interface.py Normal file
View File

@@ -0,0 +1,320 @@
from loguru import logger
from PyQt6.QtCore import Qt, QThread, pyqtSignal
from PyQt6.QtWidgets import QVBoxLayout, QWidget, QTableWidget, QTableWidgetItem, QHeaderView, QAbstractItemView, QMenu, QLabel, QFrame, QHBoxLayout
from PyQt6.QtGui import QAction
from qfluentwidgets import (CardWidget, ToolButton,
LineEdit, PrimaryPushButton, InfoBar, InfoBarPosition,
Dialog)
from app.core.api import miaoStarsBasicApi as basicApi
from app.core import lang
class ShareThread(QThread):
"""获取分享列表的线程"""
successSignal = pyqtSignal(dict)
errorSignal = pyqtSignal(str)
def __init__(self, page=1, pageSize=10, nextToken=None):
super().__init__()
self.page = page
self.pageSize = pageSize
self.nextToken = nextToken
def run(self):
try:
api = basicApi
# 注意getMyShares方法当前只支持page和pageSize参数
# 未来如果API支持next_token参数这里需要更新调用方式
result = api.getMyShares(self.page, self.pageSize)
if result['code'] == 0:
self.successSignal.emit(result)
else:
self.errorSignal.emit(result['msg'])
except Exception as e:
logger.error(f"获取分享列表失败: {e}")
self.errorSignal.emit(str(e))
class DeleteShareThread(QThread):
"""删除分享的线程"""
successSignal = pyqtSignal(str)
errorSignal = pyqtSignal(str)
def __init__(self, shareId):
super().__init__()
self.shareId = shareId
def run(self):
try:
api = basicApi
result = api.deleteShare(self.shareId)
if result['code'] == 0:
self.successSignal.emit(self.shareId)
else:
self.errorSignal.emit(result['msg'])
except Exception as e:
logger.error(f"删除分享失败: {e}")
self.errorSignal.emit(str(e))
class ShareWidget(QWidget):
"""分享页面组件"""
def __init__(self, parent=None):
super().__init__(parent)
self.page = 1
self.pageSize = 10
self.total = 0
self.nextToken = None
self.setupUi()
self.connectSignals()
self.loadShares()
def setupUi(self):
"""初始化UI"""
self.mainLayout = QVBoxLayout(self)
self.mainLayout.setSpacing(20)
# 标题
titleCard = CardWidget(self)
titleLayout = QVBoxLayout(titleCard)
self.titleLabel = QLabel(lang("我的分享"), parent=titleCard)
font = self.titleLabel.font()
font.setBold(True)
self.titleLabel.setFont(font)
self.titleLabel.setStyleSheet("font-size: 20px;")
titleLayout.addWidget(self.titleLabel, alignment=Qt.AlignmentFlag.AlignLeft)
self.mainLayout.addWidget(titleCard)
# 表格
self.tableWidget = QTableWidget(self)
self.tableWidget.setColumnCount(5)
self.tableWidget.setHorizontalHeaderLabels([
lang("分享名称"),
lang("分享链接"),
lang("创建时间"),
lang("访问次数"),
lang("操作")
])
# 设置表格属性
self.tableWidget.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeMode.Stretch)
self.tableWidget.horizontalHeader().setSectionResizeMode(0, QHeaderView.ResizeMode.ResizeToContents)
self.tableWidget.horizontalHeader().setSectionResizeMode(4, QHeaderView.ResizeMode.Fixed)
self.tableWidget.setColumnWidth(4, 100)
self.tableWidget.setEditTriggers(QAbstractItemView.EditTrigger.NoEditTriggers)
self.tableWidget.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu)
self.mainLayout.addWidget(self.tableWidget)
# 空状态
self.emptyWidget = QFrame(self)
self.emptyWidget.setFixedSize(400, 250)
self.emptyWidget.setStyleSheet("background-color: transparent;")
emptyLayout = QVBoxLayout(self.emptyWidget)
# 图标标签(使用占位文本代替图标)
self.iconLabel = QLabel("☁️", self.emptyWidget)
self.iconLabel.setStyleSheet("font-size: 48px;")
self.iconLabel.setAlignment(Qt.AlignmentFlag.AlignCenter)
# 标题
self.emptyTitleLabel = QLabel(lang("暂无分享"), self.emptyWidget)
self.emptyTitleLabel.setStyleSheet("font-size: 18px; font-weight: bold;")
self.emptyTitleLabel.setAlignment(Qt.AlignmentFlag.AlignCenter)
# 描述
self.emptyDescLabel = QLabel(lang("你还没有任何分享,分享文件可以让他人便捷地获取你的资料"), self.emptyWidget)
self.emptyDescLabel.setStyleSheet("font-size: 14px; color: #666;")
self.emptyDescLabel.setAlignment(Qt.AlignmentFlag.AlignCenter)
self.emptyDescLabel.setWordWrap(True)
emptyLayout.addWidget(self.iconLabel)
emptyLayout.addWidget(self.emptyTitleLabel, 0, Qt.AlignmentFlag.AlignCenter)
emptyLayout.addWidget(self.emptyDescLabel, 0, Qt.AlignmentFlag.AlignCenter)
self.emptyWidget.setVisible(False)
self.mainLayout.addWidget(self.emptyWidget, 1, Qt.AlignmentFlag.AlignCenter)
# 自定义分页组件
self.paginationWidget = QWidget(self)
paginationLayout = QHBoxLayout(self.paginationWidget)
paginationLayout.setContentsMargins(0, 0, 0, 0)
# 页码显示
self.currentPage = 1
self.totalPages = 1
self.pageLabel = QLabel("1/1", self.paginationWidget)
paginationLayout.addWidget(self.pageLabel)
self.mainLayout.addWidget(self.paginationWidget, alignment=Qt.AlignmentFlag.AlignRight)
def connectSignals(self):
"""连接信号与槽"""
self.tableWidget.customContextMenuRequested.connect(self.showContextMenu)
def loadShares(self):
"""加载分享列表"""
self.tableWidget.setRowCount(0)
self.emptyWidget.setVisible(False)
self.shareThread = ShareThread(self.page, self.pageSize, self.nextToken)
self.shareThread.successSignal.connect(self.onSharesLoaded)
self.shareThread.errorSignal.connect(self.onSharesLoadError)
self.shareThread.start()
def onSharesLoaded(self, result):
"""处理分享列表加载成功"""
data = result.get('data', {})
shares = data.get('shares', [])
pagination = data.get('pagination', {})
# 更新表格
self.tableWidget.setRowCount(len(shares))
for row, share in enumerate(shares):
# 分享名称
nameItem = QTableWidgetItem(share.get('name', '-'))
nameItem.setData(Qt.ItemDataRole.UserRole, share)
nameItem.setTextAlignment(Qt.AlignmentFlag.AlignVCenter)
self.tableWidget.setItem(row, 0, nameItem)
# 分享链接
shareUrl = share.get('url', '')
urlItem = QTableWidgetItem(shareUrl)
urlItem.setTextAlignment(Qt.AlignmentFlag.AlignVCenter)
self.tableWidget.setItem(row, 1, urlItem)
# 创建时间
createTime = share.get('created_at', '-')
timeItem = QTableWidgetItem(createTime)
timeItem.setTextAlignment(Qt.AlignmentFlag.AlignVCenter)
self.tableWidget.setItem(row, 2, timeItem)
# 访问次数
visits = share.get('visited', 0)
visitItem = QTableWidgetItem(str(visits))
visitItem.setTextAlignment(Qt.AlignmentFlag.AlignVCenter)
self.tableWidget.setItem(row, 3, visitItem)
# 操作按钮
actionWidget = QWidget()
actionLayout = QVBoxLayout(actionWidget)
actionLayout.setContentsMargins(0, 0, 0, 0)
deleteButton = ToolButton()
deleteButton.setText(lang("删除"))
deleteButton.setProperty("shareId", share.get('id'))
deleteButton.clicked.connect(lambda checked, shareId=share.get('id'): self.deleteShare(shareId))
actionLayout.addWidget(deleteButton, alignment=Qt.AlignmentFlag.AlignCenter)
actionWidget.setLayout(actionLayout)
self.tableWidget.setCellWidget(row, 4, actionWidget)
# 更新分页显示
self.nextToken = pagination.get('next_token')
# 对于基于token的分页我们仍然显示页码信息以保持UI一致性
pageSize = pagination.get('page_size', self.pageSize)
# 估算总页数(基于当前获取的数量和每页大小)
estimatedTotalPages = (len(shares) + pageSize - 1) // pageSize
self.totalPages = max(1, estimatedTotalPages)
self.pageLabel.setText(f"{self.currentPage}/{self.totalPages}")
# 显示空状态
if not shares:
self.emptyWidget.setVisible(True)
def onSharesLoadError(self, errorMsg):
"""处理分享列表加载失败"""
logger.error(f"加载分享列表失败: {errorMsg}")
InfoBar.error(
lang("失败"),
f"{lang('加载分享列表失败')}: {errorMsg}",
parent=self.window()
)
self.emptyWidget.setVisible(True)
def showContextMenu(self, pos):
"""显示右键菜单"""
item = self.tableWidget.itemAt(pos)
if not item:
return
share = item.data(Qt.ItemDataRole.UserRole)
if not share:
return
menu = QMenu(self)
deleteAction = QAction(lang("删除"), self)
deleteAction.triggered.connect(lambda: self.deleteShare(share.get('id')))
menu.addAction(deleteAction)
menu.exec(self.tableWidget.mapToGlobal(pos))
def deleteShare(self, shareId):
"""删除分享"""
# 确认对话框
from qfluentwidgets import FluentIcon as FIF
from qfluentwidgets import MessageBox
dialog = MessageBox(
lang("确认删除"),
lang("确定要删除这个分享链接吗?删除后将无法恢复。"),
self.window()
)
if dialog.exec() == MessageBox.DialogCode.Accepted:
self.deleteShareThread = DeleteShareThread(shareId)
self.deleteShareThread.successSignal.connect(self.onShareDeleted)
self.deleteShareThread.errorSignal.connect(self.onShareDeleteError)
self.deleteShareThread.start()
def onShareDeleted(self, shareId):
"""处理分享删除成功"""
logger.success(f"分享删除成功: {shareId}")
InfoBar.success(
lang("成功"),
lang("分享删除成功"),
parent=self.window()
)
# 重新加载当前页
self.loadShares()
def onShareDeleteError(self, errorMsg):
"""处理分享删除失败"""
logger.error(f"删除分享失败: {errorMsg}")
InfoBar.error(
lang("失败"),
f"{lang('删除分享失败')}: {errorMsg}",
parent=self.window()
)
def onPageChanged(self, page):
"""处理页码变化"""
self.page = page
self.loadShares()
class ShareInterface(QWidget):
"""分享页面接口"""
def __init__(self, parent=None):
super().__init__(parent)
self.setObjectName("shareInterface")
self.setupUi()
def setupUi(self):
"""初始化UI"""
self.mainLayout = QVBoxLayout(self)
self.mainLayout.setContentsMargins(30, 30, 30, 30)
self.shareWidget = ShareWidget(self)
self.mainLayout.addWidget(self.shareWidget)
def refresh(self):
"""刷新页面"""
if hasattr(self, 'shareWidget'):
self.shareWidget.loadShares()

View File

@@ -13,7 +13,7 @@ class CustomBgMessageBox(MessageBoxBase):
self.imageChoice = HorizontalFlipView(parent=self)
self.imageChoice.setBorderRadius(8)
for i in range(0, 6):
self.imageChoice.addImage(f"app\\resource\\images\\bg{i}.png")
self.imageChoice.addImage(f"_internal\\backgrounds\\bg{i}.png")
self.viewLayout.addWidget(self.imageChoice)
self.yesButton.setText("确定")

View File

@@ -8,7 +8,6 @@ from qfluentwidgets import (Action, InfoBar, InfoBarPosition, LineEdit, MenuAnim
PrimarySplitPushButton, PushButton, RoundMenu, ScrollArea)
from app.core import DeleteTagThread, userConfig, lang
from app.view.widgets.add_tag_messageBox import AddTagMessageBox
class TagsScrollArea(ScrollArea):
@@ -140,7 +139,7 @@ class TagsScrollArea(ScrollArea):
def deleteTag(self, tagId):
self.deleteTagThread = DeleteTagThread(tagId)
self.deleteTagThread.successDeleteSignal.connect(
lambda: self._onTagDeleteError(tagId)
lambda: self._onTagDeleteSuccess(tagId)
)
self.deleteTagThread.errorSignal.connect(self._onTagDeleteError)
self.deleteTagThread.start()
@@ -156,7 +155,7 @@ class TagsScrollArea(ScrollArea):
self.window(),
)
def _onTagDeleteError(self, tagId):
def _onTagDeleteSuccess(self, tagId):
self.removeTag(tagId)
InfoBar.success(
"成功",
@@ -214,43 +213,20 @@ class TagWidget(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.tagScrollArea = TagsScrollArea(self)
self.addPushButton = PushButton(lang("添加标签"), self)
# 移除添加标签按钮
logger.debug("初始化标签管理组件")
self.setupUi()
self.connectSignals()
# 移除信号连接
def setupUi(self):
"""初始化UI"""
self.hBoxLayout = QHBoxLayout(self)
self.hBoxLayout.addWidget(self.tagScrollArea)
self.hBoxLayout.addSpacing(10)
self.hBoxLayout.addWidget(self.addPushButton, Qt.AlignmentFlag.AlignRight)
def connectSignals(self):
"""连接信号与槽"""
logger.debug("连接标签管理组件信号")
self.addPushButton.clicked.connect(self.addTag)
def addTag(self):
w = AddTagMessageBox(self.window())
w.successAddTagSignal.connect(self.onAddTagSuccess)
if w.exec():
...
def onAddTagSuccess(self, name, result):
"""处理标签添加成功事件"""
InfoBar.success(
"成功",
"标签添加成功",
Qt.Orientation.Horizontal,
True,
1000,
InfoBarPosition.TOP_RIGHT,
self.window(),
)
self.tagScrollArea.addTag(result["data"], name)
# 移除添加标签按钮的布局设置
# 确保标签区域能够充分利用空间
self.hBoxLayout.addStretch()
class SearchWidget(QWidget):
@@ -262,31 +238,12 @@ class SearchWidget(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.searchLineEdit = LineEdit(self)
self.searchButton = PrimarySplitPushButton(lang("仓内搜索"), self)
self.menu = RoundMenu(parent=self)
self.menu.addAction(
Action(
lang("仓内搜索"),
triggered=lambda: self.changeButtonText(lang("仓内搜索")),
)
)
self.menu.addAction(
Action(
lang("站内搜索"),
triggered=lambda: self.changeButtonText(lang("站内搜索")),
)
)
self.searchButton.setFlyout(self.menu)
self.searchButton = PillPushButton(lang("搜索"), self) # 使用已导入的PillPushButton
logger.debug("初始化搜索组件")
self.setupUi()
def changeButtonText(self, text):
self.searchButton.setText(text)
def setupUi(self):
"""初始化UI"""
self.searchLineEdit.setPlaceholderText(lang("搜索文件"))

View File

@@ -137,7 +137,7 @@ def build_app():
"PyInstaller",
MAIN_SCRIPT,
"--name", build_name,
"--onefile", # 构建单个可执行文件
"--onedir", # 构建单个可执行文件
]
# 设置控制台选项