sus
This commit is contained in:
@@ -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"
|
||||
}
|
||||
@@ -98,6 +98,19 @@
|
||||
"预览": "预览",
|
||||
"进入": "进入",
|
||||
"刷新当前": "刷新当前",
|
||||
"我的分享": "我的分享",
|
||||
"分享名称": "分享名称",
|
||||
"分享链接": "分享链接",
|
||||
"访问次数": "访问次数",
|
||||
"操作": "操作",
|
||||
"暂无分享": "暂无分享",
|
||||
"你还没有任何分享,分享文件可以让他人便捷地获取你的资料": "你还没有任何分享,分享文件可以让他人便捷地获取你的资料",
|
||||
"确认删除": "确认删除",
|
||||
"确定要删除这个分享链接吗?删除后将无法恢复。": "确定要删除这个分享链接吗?删除后将无法恢复。",
|
||||
"分享删除成功": "分享删除成功",
|
||||
"删除分享失败": "删除分享失败",
|
||||
"加载分享列表失败": "加载分享列表失败",
|
||||
"获取分享列表成功": "获取分享列表成功",
|
||||
"上传文件": "上传文件",
|
||||
"设置存储策略": "设置存储策略"
|
||||
}
|
||||
@@ -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)
|
||||
try:
|
||||
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"]}}
|
||||
# 转换响应格式以保持向后兼容
|
||||
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": "搜索响应格式无效"}
|
||||
|
||||
logger.warning(f"搜索响应格式不正确: {r}")
|
||||
return {"code": 0, "data": {"objects": []}}
|
||||
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": "添加标签功能已禁用"}
|
||||
|
||||
# 转换响应格式
|
||||
if r.get("id"):
|
||||
return {"code": 0, "msg": "标签添加成功", "data": r}
|
||||
def getMyShares(self, page=1, pageSize=10):
|
||||
"""获取我的分享列表 (Cloudreve V4 API - GET /user/shares/{user-id})
|
||||
|
||||
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):
|
||||
"""获取分享文件信息"""
|
||||
@@ -590,6 +637,28 @@ class MiaoStarsBasicApi:
|
||||
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)"""
|
||||
url = "/file/rename"
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -3,4 +3,4 @@
|
||||
Version information for the application
|
||||
"""
|
||||
|
||||
version = "Alpha 0.1"
|
||||
version = "Alpha 0.2"
|
||||
|
||||
@@ -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):
|
||||
@@ -86,6 +89,13 @@ class MainWindow(CustomFluentWindow):
|
||||
position=NavigationItemPosition.TOP,
|
||||
)
|
||||
|
||||
self.addSubInterface(
|
||||
self.shareInterface,
|
||||
FluentIcon.SHARE,
|
||||
lang("我的分享"),
|
||||
position=NavigationItemPosition.TOP,
|
||||
)
|
||||
|
||||
self.addSubInterface(
|
||||
self.appInfoInterface,
|
||||
FluentIcon.INFO,
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
320
app/view/share_interface.py
Normal 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()
|
||||
@@ -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("确定")
|
||||
|
||||
@@ -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("搜索文件"))
|
||||
|
||||
Reference in New Issue
Block a user