From 5645870954e80a9f6e0bcf3963e25995673c9807 Mon Sep 17 00:00:00 2001 From: Leonmmcoset Date: Mon, 3 Nov 2025 22:28:58 +0800 Subject: [PATCH] sus --- _internal/lang/en.json | 13 + _internal/lang/zh.json | 13 + app/core/api/basicApi.py | 181 +++++++--- app/core/services/file_thread.py | 29 +- app/core/utils/config.py | 2 +- app/core/utils/version.py | 2 +- app/view/main_window.py | 10 + app/view/ownFiled_interface.py | 28 +- app/view/setting_interface.py | 2 +- app/view/share_interface.py | 320 ++++++++++++++++++ .../widgets/custom_background_messageBox.py | 2 +- app/view/widgets/ownFiled_widgets.py | 59 +--- build.py | 2 +- 13 files changed, 522 insertions(+), 141 deletions(-) create mode 100644 app/view/share_interface.py diff --git a/_internal/lang/en.json b/_internal/lang/en.json index 7eb93d0..c48c339 100644 --- a/_internal/lang/en.json +++ b/_internal/lang/en.json @@ -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" } \ No newline at end of file diff --git a/_internal/lang/zh.json b/_internal/lang/zh.json index e50771b..5c35979 100644 --- a/_internal/lang/zh.json +++ b/_internal/lang/zh.json @@ -98,6 +98,19 @@ "预览": "预览", "进入": "进入", "刷新当前": "刷新当前", + "我的分享": "我的分享", + "分享名称": "分享名称", + "分享链接": "分享链接", + "访问次数": "访问次数", + "操作": "操作", + "暂无分享": "暂无分享", + "你还没有任何分享,分享文件可以让他人便捷地获取你的资料": "你还没有任何分享,分享文件可以让他人便捷地获取你的资料", + "确认删除": "确认删除", + "确定要删除这个分享链接吗?删除后将无法恢复。": "确定要删除这个分享链接吗?删除后将无法恢复。", + "分享删除成功": "分享删除成功", + "删除分享失败": "删除分享失败", + "加载分享列表失败": "加载分享列表失败", + "获取分享列表成功": "获取分享列表成功", "上传文件": "上传文件", "设置存储策略": "设置存储策略" } \ No newline at end of file diff --git a/app/core/api/basicApi.py b/app/core/api/basicApi.py index 5b21347..3d1b32f 100644 --- a/app/core/api/basicApi.py +++ b/app/core/api/basicApi.py @@ -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)""" diff --git a/app/core/services/file_thread.py b/app/core/services/file_thread.py index 5877b89..b3f9b13 100644 --- a/app/core/services/file_thread.py +++ b/app/core/services/file_thread.py @@ -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): diff --git a/app/core/utils/config.py b/app/core/utils/config.py index 7b98104..6a429de 100644 --- a/app/core/utils/config.py +++ b/app/core/utils/config.py @@ -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) diff --git a/app/core/utils/version.py b/app/core/utils/version.py index 7008426..9f13c53 100644 --- a/app/core/utils/version.py +++ b/app/core/utils/version.py @@ -3,4 +3,4 @@ Version information for the application """ -version = "Alpha 0.1" +version = "Alpha 0.2" diff --git a/app/view/main_window.py b/app/view/main_window.py index 49becc5..c5007ca 100644 --- a/app/view/main_window.py +++ b/app/view/main_window.py @@ -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, diff --git a/app/view/ownFiled_interface.py b/app/view/ownFiled_interface.py index 47ff227..91fdbe2 100644 --- a/app/view/ownFiled_interface.py +++ b/app/view/ownFiled_interface.py @@ -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) diff --git a/app/view/setting_interface.py b/app/view/setting_interface.py index 148a4de..df49f6b 100644 --- a/app/view/setting_interface.py +++ b/app/view/setting_interface.py @@ -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): diff --git a/app/view/share_interface.py b/app/view/share_interface.py new file mode 100644 index 0000000..920a372 --- /dev/null +++ b/app/view/share_interface.py @@ -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() \ No newline at end of file diff --git a/app/view/widgets/custom_background_messageBox.py b/app/view/widgets/custom_background_messageBox.py index 8d69fa9..0d4033d 100644 --- a/app/view/widgets/custom_background_messageBox.py +++ b/app/view/widgets/custom_background_messageBox.py @@ -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("确定") diff --git a/app/view/widgets/ownFiled_widgets.py b/app/view/widgets/ownFiled_widgets.py index 4260398..c9001a5 100644 --- a/app/view/widgets/ownFiled_widgets.py +++ b/app/view/widgets/ownFiled_widgets.py @@ -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("搜索文件")) diff --git a/build.py b/build.py index 6ab0e04..ab2263c 100644 --- a/build.py +++ b/build.py @@ -137,7 +137,7 @@ def build_app(): "PyInstaller", MAIN_SCRIPT, "--name", build_name, - "--onefile", # 构建单个可执行文件 + "--onedir", # 构建单个可执行文件 ] # 设置控制台选项