""" 右侧工作区基类 - 装饰性背景光晕 - 毛玻璃效果头部 - 面包屑导航 + 版本标签 - 上传区域(虚线边框+图标) """ from PySide6.QtWidgets import ( QWidget, QVBoxLayout, QHBoxLayout, QLabel, QPushButton, QFrame, QScrollArea, QFileDialog, QGraphicsDropShadowEffect, QGraphicsBlurEffect ) from PySide6.QtCore import Signal, Qt, QSize from PySide6.QtGui import ( QFont, QDragEnterEvent, QDropEvent, QPainter, QColor, QRadialGradient, QLinearGradient, QPen, QBrush ) class BackgroundDecoration(QWidget): """装饰性背景 - 渐变光晕""" def __init__(self, parent=None): super().__init__(parent) self.setAttribute(Qt.WidgetAttribute.WA_TransparentForMouseEvents) def paintEvent(self, event): painter = QPainter(self) painter.setRenderHint(QPainter.RenderHint.Antialiasing) # 右上角奶酪色光晕 gradient1 = QRadialGradient( self.width() + 50, -50, # 圆心 500 # 半径 ) gradient1.setColorAt(0, QColor(251, 191, 36, 25)) # cheese-500/10 gradient1.setColorAt(1, QColor(251, 191, 36, 0)) painter.setBrush(gradient1) painter.setPen(Qt.PenStyle.NoPen) painter.drawEllipse( int(self.width() * 0.5), int(-self.height() * 0.2), int(self.width() * 0.7), int(self.height() * 0.7) ) # 左侧紫色光晕 gradient2 = QRadialGradient( self.width() * 0.1, self.height() * 0.3, 350 ) gradient2.setColorAt(0, QColor(139, 92, 246, 12)) # purple-500/5 gradient2.setColorAt(1, QColor(139, 92, 246, 0)) painter.setBrush(gradient2) painter.drawEllipse( int(-self.width() * 0.1), int(self.height() * 0.1), int(self.width() * 0.5), int(self.height() * 0.5) ) class UploadArea(QFrame): """上传区域 - 还原HTML设计样式""" files_dropped = Signal(list) def __init__(self, accept_types: str = "所有文件", parent=None): super().__init__(parent) self.accept_types = accept_types self.setAcceptDrops(True) self.setMinimumHeight(208) # h-52 = 13rem = 208px self.setCursor(Qt.CursorShape.PointingHandCursor) self._hovered = False self.setup_ui() def setup_ui(self): layout = QVBoxLayout(self) layout.setAlignment(Qt.AlignmentFlag.AlignCenter) layout.setSpacing(12) # 图标容器 (圆形背景) self.icon_container = QFrame() self.icon_container.setFixedSize(56, 56) self.icon_container.setStyleSheet(""" background: #334155; border-radius: 28px; """) icon_layout = QVBoxLayout(self.icon_container) icon_layout.setContentsMargins(0, 0, 0, 0) icon_layout.setAlignment(Qt.AlignmentFlag.AlignCenter) # 云上传图标 icon_label = QLabel("☁️") icon_label.setFont(QFont("Segoe UI Emoji", 24)) icon_label.setAlignment(Qt.AlignmentFlag.AlignCenter) icon_label.setStyleSheet("background: transparent;") icon_layout.addWidget(icon_label) layout.addWidget(self.icon_container, 0, Qt.AlignmentFlag.AlignCenter) # 主文字 self.title_label = QLabel("点击或拖拽文件到此处") self.title_label.setStyleSheet("color: white; font-size: 16px; font-weight: 500; background: transparent;") self.title_label.setAlignment(Qt.AlignmentFlag.AlignCenter) layout.addWidget(self.title_label) # 副文字 self.desc_label = QLabel(f"支持批量上传 ({self.accept_types})") self.desc_label.setStyleSheet("color: #64748b; font-size: 13px; background: transparent;") self.desc_label.setAlignment(Qt.AlignmentFlag.AlignCenter) layout.addWidget(self.desc_label) self.update_style() def update_style(self): if self._hovered: self.setStyleSheet(""" UploadArea { background: rgba(30, 41, 59, 0.5); border: 2px dashed rgba(251, 191, 36, 0.5); border-radius: 16px; } """) else: self.setStyleSheet(""" UploadArea { background: rgba(30, 41, 59, 0.3); border: 2px dashed #334155; border-radius: 16px; } """) def enterEvent(self, event): self._hovered = True self.update_style() def leaveEvent(self, event): self._hovered = False self.update_style() def mousePressEvent(self, event): if event.button() == Qt.MouseButton.LeftButton: self.open_file_dialog() def open_file_dialog(self): files, _ = QFileDialog.getOpenFileNames( self, "选择文件", "", f"{self.accept_types}" ) if files: self.files_dropped.emit(files) def dragEnterEvent(self, event: QDragEnterEvent): if event.mimeData().hasUrls(): event.acceptProposedAction() self._hovered = True self.update_style() def dragLeaveEvent(self, event): self._hovered = False self.update_style() def dropEvent(self, event: QDropEvent): self._hovered = False self.update_style() files = [] for url in event.mimeData().urls(): if url.isLocalFile(): files.append(url.toLocalFile()) if files: self.files_dropped.emit(files) class GlassHeader(QFrame): """毛玻璃效果头部""" def __init__(self, parent=None): super().__init__(parent) self.setFixedHeight(80) self.setStyleSheet(""" GlassHeader { background: rgba(30, 41, 59, 0.7); border-bottom: 1px solid rgba(51, 65, 85, 0.3); } """) class BaseWorkspace(QWidget): """工作区基类""" def __init__(self, parent=None): super().__init__(parent) self.setObjectName("workspace") self.setup_base_ui() def setup_base_ui(self): # 主布局 self.main_layout = QVBoxLayout(self) self.main_layout.setContentsMargins(0, 0, 0, 0) self.main_layout.setSpacing(0) # 背景装饰层 self.bg_decoration = BackgroundDecoration(self) # 头部区域 self.header = GlassHeader() header_layout = QVBoxLayout(self.header) header_layout.setContentsMargins(32, 16, 32, 16) header_layout.setSpacing(4) # 面包屑 breadcrumb_layout = QHBoxLayout() breadcrumb_layout.setSpacing(8) self.breadcrumb_category = QLabel("首页") self.breadcrumb_category.setStyleSheet("color: #64748b; font-size: 12px;") breadcrumb_layout.addWidget(self.breadcrumb_category) arrow = QLabel("›") arrow.setStyleSheet("color: #64748b; font-size: 12px;") breadcrumb_layout.addWidget(arrow) self.breadcrumb_tool = QLabel("控制台") self.breadcrumb_tool.setStyleSheet("color: #fbbf24; font-size: 12px;") breadcrumb_layout.addWidget(self.breadcrumb_tool) breadcrumb_layout.addStretch() header_layout.addLayout(breadcrumb_layout) # 标题行 title_layout = QHBoxLayout() # 标题 + 版本标签 title_container = QHBoxLayout() title_container.setSpacing(8) self.title_label = QLabel("欢迎回来") self.title_label.setStyleSheet("color: white; font-size: 22px; font-weight: 700;") title_container.addWidget(self.title_label) self.version_label = QLabel("v2.1") self.version_label.setStyleSheet(""" color: #64748b; font-size: 12px; background: #1e293b; border: 1px solid #334155; border-radius: 4px; padding: 2px 8px; """) self.version_label.setVisible(False) title_container.addWidget(self.version_label) title_container.addStretch() title_layout.addLayout(title_container, 1) # 按钮区域 btn_layout = QHBoxLayout() btn_layout.setSpacing(12) self.history_btn = QPushButton("🕐 历史") self.history_btn.setStyleSheet(""" QPushButton { background: transparent; border: 1px solid #334155; border-radius: 8px; color: #cbd5e1; padding: 8px 16px; font-size: 13px; } QPushButton:hover { background: #334155; } """) btn_layout.addWidget(self.history_btn) self.export_btn = QPushButton("📤 导出") self.export_btn.setStyleSheet(""" QPushButton { background: #f59e0b; border: none; border-radius: 8px; color: white; padding: 8px 16px; font-size: 13px; font-weight: 500; } QPushButton:hover { background: #fbbf24; } """) btn_layout.addWidget(self.export_btn) title_layout.addLayout(btn_layout) header_layout.addLayout(title_layout) self.main_layout.addWidget(self.header) # 内容区域 scroll_area = QScrollArea() scroll_area.setWidgetResizable(True) scroll_area.setFrameShape(QFrame.Shape.NoFrame) scroll_area.setStyleSheet("background: transparent;") self.content_widget = QWidget() self.content_widget.setStyleSheet("background: transparent;") self.content_layout = QVBoxLayout(self.content_widget) self.content_layout.setContentsMargins(32, 24, 32, 24) self.content_layout.setSpacing(24) scroll_area.setWidget(self.content_widget) self.main_layout.addWidget(scroll_area, 1) def resizeEvent(self, event): super().resizeEvent(event) # 调整背景装饰大小 self.bg_decoration.setGeometry(0, 0, self.width(), self.height()) self.bg_decoration.lower() def set_breadcrumb(self, category: str, tool: str): self.breadcrumb_category.setText(category) self.breadcrumb_tool.setText(tool) def set_title(self, title: str): self.title_label.setText(title) self.version_label.setVisible(bool(title and title != "欢迎回来")) def clear_content(self): while self.content_layout.count(): item = self.content_layout.takeAt(0) if item.widget(): item.widget().deleteLater() class ShortcutCard(QFrame): """快捷入口卡片""" clicked = Signal() def __init__(self, data: dict, parent=None): super().__init__(parent) self.data = data self.setFixedSize(200, 140) self.setCursor(Qt.CursorShape.PointingHandCursor) self._hovered = False self.setup_ui() def setup_ui(self): layout = QVBoxLayout(self) layout.setContentsMargins(16, 16, 16, 16) layout.setSpacing(8) # 图标 color = self.data.get("color", "#64748b") self.icon_frame = QFrame() self.icon_frame.setFixedSize(40, 40) r, g, b = self.hex_to_rgb(color) self.icon_frame.setStyleSheet(f""" background: rgba({r}, {g}, {b}, 0.1); border-radius: 8px; """) icon_layout = QVBoxLayout(self.icon_frame) icon_layout.setContentsMargins(0, 0, 0, 0) icon_layout.setAlignment(Qt.AlignmentFlag.AlignCenter) icon = QLabel(self.data.get("icon", "")) icon.setFont(QFont("Segoe UI Emoji", 16)) icon.setStyleSheet(f"color: {color}; background: transparent;") icon_layout.addWidget(icon) layout.addWidget(self.icon_frame) # 名称 name = QLabel(self.data.get("name", "")) name.setStyleSheet("color: white; font-size: 14px; font-weight: 500;") layout.addWidget(name) # 描述 desc = QLabel(self.data.get("desc", "")) desc.setStyleSheet("color: #64748b; font-size: 11px;") layout.addWidget(desc) layout.addStretch() self.update_style() def update_style(self): if self._hovered: self.setStyleSheet(""" ShortcutCard { background: rgba(30, 41, 59, 0.8); border: 1px solid rgba(251, 191, 36, 0.5); border-radius: 16px; } """) else: self.setStyleSheet(""" ShortcutCard { background: rgba(30, 41, 59, 0.5); border: 1px solid #334155; border-radius: 16px; } """) def enterEvent(self, event): self._hovered = True self.update_style() def leaveEvent(self, event): self._hovered = False self.update_style() def mousePressEvent(self, event): if event.button() == Qt.MouseButton.LeftButton: self.clicked.emit() @staticmethod def hex_to_rgb(hex_color: str) -> tuple: hex_color = hex_color.lstrip('#') return tuple(int(hex_color[i:i+2], 16) for i in (0, 2, 4)) class WelcomeWorkspace(BaseWorkspace): """欢迎页面工作区""" tool_clicked = Signal(str, str) def __init__(self, parent=None): super().__init__(parent) self.setup_welcome_ui() def setup_welcome_ui(self): self.set_breadcrumb("首页", "控制台") self.set_title("欢迎回来") self.version_label.setVisible(False) self.history_btn.hide() self.export_btn.hide() # 中心内容 center_widget = QWidget() center_layout = QVBoxLayout(center_widget) center_layout.setAlignment(Qt.AlignmentFlag.AlignCenter) center_layout.setSpacing(24) # Logo区域 logo_container = QFrame() logo_container.setFixedSize(128, 128) logo_container.setStyleSheet(""" background: #1e293b; border: 1px solid #334155; border-radius: 64px; """) logo_layout = QVBoxLayout(logo_container) logo_layout.setAlignment(Qt.AlignmentFlag.AlignCenter) logo = QLabel("🧀") logo.setFont(QFont("Segoe UI Emoji", 48)) logo.setStyleSheet("color: #fbbf24; background: transparent;") logo_layout.addWidget(logo) center_layout.addWidget(logo_container, 0, Qt.AlignmentFlag.AlignCenter) # 标题 title = QLabel("开始你的创作") title.setFont(QFont("Microsoft YaHei", 28, QFont.Weight.Bold)) title.setStyleSheet("color: white;") title.setAlignment(Qt.AlignmentFlag.AlignCenter) center_layout.addWidget(title) # 描述 desc = QLabel("从左侧选择一个工具。无论是压缩图片、拆分PDF还是处理数据,我们都能搞定。") desc.setStyleSheet("color: #94a3b8; font-size: 14px;") desc.setAlignment(Qt.AlignmentFlag.AlignCenter) desc.setWordWrap(True) desc.setMaximumWidth(500) center_layout.addWidget(desc) center_layout.addSpacing(20) # 快捷入口 shortcuts_layout = QHBoxLayout() shortcuts_layout.setSpacing(16) shortcuts_layout.setAlignment(Qt.AlignmentFlag.AlignCenter) shortcuts = [ {"id": "img-compress", "category": "image", "name": "图片压缩", "desc": "智能无损压缩", "icon": "🖼", "color": "#3b82f6"}, {"id": "pdf-split", "category": "pdf", "name": "PDF 拆分", "desc": "提取特定页面", "icon": "✂️", "color": "#ef4444"}, {"id": "img-convert", "category": "image", "name": "格式转换", "desc": "格式互转工具", "icon": "🔄", "color": "#22c55e"}, ] for item in shortcuts: card = ShortcutCard(item) card.clicked.connect(lambda i=item: self.tool_clicked.emit(i["id"], i["category"])) shortcuts_layout.addWidget(card) center_layout.addLayout(shortcuts_layout) self.content_layout.addStretch() self.content_layout.addWidget(center_widget) self.content_layout.addStretch()