import math import random from PyQt5.QtCore import Qt, QPropertyAnimation, QPoint, QTimer, QParallelAnimationGroup, pyqtProperty, \ QEasingCurve, QSize, pyqtSignal, QPointF, QByteArray from PyQt5.QtGui import QPixmap, QIcon, QColor, QPainter, QLinearGradient from PyQt5.QtSvg import QSvgRenderer, QSvgWidget from PyQt5.QtWidgets import (QWidget, QVBoxLayout, QHBoxLayout, QLabel, QPushButton, QFrame, QGridLayout, QSizePolicy, QScrollArea, QSizeGrip, QLineEdit) class AnimatedCard(QFrame): """动画卡片控件,与Home页面一致""" def __init__(self, start_color, end_color, parent=None): super().__init__(parent) self.start_color = QColor(start_color) self.end_color = QColor(end_color) self._angle = 0 self.hover_state = False self.init_animations() self.setMinimumSize(300, 320) self.setMaximumSize(300, 320) self.setCursor(Qt.PointingHandCursor) self.setStyleSheet(""" AnimatedCard { border-radius: 16px; border: 1px solid rgba(255, 255, 255, 0.1); transition: all 0.3s ease; } """) def init_animations(self): # 渐变角度动画 self.angle_animation = QPropertyAnimation(self, b"angle") self.angle_animation.setDuration(20000) self.angle_animation.setStartValue(0) self.angle_animation.setEndValue(360) self.angle_animation.setLoopCount(-1) self.angle_animation.start() # 悬停动画组 self.hover_animation = QPropertyAnimation(self, b"geometry") self.hover_animation.setDuration(300) self.hover_animation.setEasingCurve(QEasingCurve.OutBack) # 阴影动画 self.shadow_animation = QPropertyAnimation(self, b"styleSheet") self.shadow_animation.setDuration(300) # 并行动画组 self.animation_group = QParallelAnimationGroup(self) self.animation_group.addAnimation(self.hover_animation) self.animation_group.addAnimation(self.shadow_animation) def paintEvent(self, event): painter = QPainter(self) painter.setRenderHint(QPainter.Antialiasing) painter.setPen(Qt.NoPen) # 创建主渐变背景 rect = self.rect() # 添加动态渐变背景 if self.hover_state: # 悬停状态使用更亮的渐变 gradient = QLinearGradient(rect.topLeft(), rect.bottomRight()) gradient.setColorAt(0, self.start_color.lighter(120)) gradient.setColorAt(1, self.end_color.lighter(120)) else: # 普通状态 gradient = QLinearGradient(rect.topLeft(), rect.bottomRight()) gradient.setColorAt(0, self.start_color) gradient.setColorAt(1, self.end_color) # 添加动态旋转效果 cx = rect.center().x() cy = rect.center().y() radius = max(rect.width(), rect.height()) * 1.2 start_x = cx + radius * 0.5 * math.cos(math.radians(self._angle)) start_y = cy + radius * 0.5 * math.sin(math.radians(self._angle)) end_x = cx - radius * 0.5 * math.cos(math.radians(self._angle)) end_y = cy - radius * 0.5 * math.sin(math.radians(self._angle)) moving_gradient = QLinearGradient(start_x, start_y, end_x, end_y) moving_gradient.setColorAt(0, QColor(255, 255, 255, 30)) moving_gradient.setColorAt(1, QColor(255, 255, 255, 10)) # 绘制背景 painter.setBrush(gradient) painter.drawRoundedRect(rect, 16, 16) # 绘制动态效果层 painter.setBrush(moving_gradient) painter.drawRoundedRect(rect, 16, 16) # 绘制高光边框 if self.hover_state: border_gradient = QLinearGradient(rect.topLeft(), rect.topRight()) border_gradient.setColorAt(0, QColor(255, 255, 255, 100)) border_gradient.setColorAt(1, QColor(255, 255, 255, 30)) painter.setPen(QColor(255, 255, 255, 150)) painter.drawRoundedRect(rect.adjusted(1, 1, -1, -1), 16, 16) def enterEvent(self, event): self.hover_state = True self.animate_hover(True) self.update() super().enterEvent(event) def leaveEvent(self, event): self.hover_state = False self.animate_hover(False) self.update() super().leaveEvent(event) def animate_hover(self, hover): """执行悬停动画""" # 保存当前位置和大小 current_geometry = self.geometry() # 目标位置和大小 if hover: target_geometry = current_geometry.adjusted(-5, -5, 5, 5) shadow_value = 20 else: target_geometry = current_geometry.adjusted(5, 5, -5, -5) shadow_value = 5 # 设置位置动画 self.hover_animation.setStartValue(current_geometry) self.hover_animation.setEndValue(target_geometry) # 设置阴影动画 self.shadow_animation.setStartValue(self.styleSheet()) self.shadow_animation.setEndValue(f""" AnimatedCard {{ border-radius: 16px; border: 1px solid rgba(255, 255, 255, 0.1); box-shadow: 0 10px {shadow_value}px rgba(0, 0, 0, 0.3); transition: all 0.3s ease; }} """) # 开始动画 self.animation_group.start() @pyqtProperty(int) def angle(self): return self._angle @angle.setter def angle(self, value): self._angle = value self.update() class AppDetailWindow(QWidget): """应用详情窗口 - 修复透明问题""" def __init__(self, app_data, parent=None): super().__init__(parent) self.app_data = app_data self.setObjectName("appDetail") self.setWindowFlags(Qt.FramelessWindowHint | Qt.Popup) self.setAttribute(Qt.WA_TranslucentBackground) self.init_ui() self.setStyleSheet(self.get_styles()) def get_styles(self): return """ /* ===== 应用详情样式 ===== */ #appDetail { background-color: rgba(26, 32, 44, 0.98); /* 提高不透明度 */ border: 1px solid rgba(74, 85, 104, 0.7); border-radius: 12px; box-shadow: 0 15px 50px rgba(0, 0, 0, 0.5); } #titleBar { background-color: rgba(45, 55, 72, 1); /* 完全不透明 */ border-top-left-radius: 12px; border-top-right-radius: 12px; padding: 15px 20px; } #detailTitle { font-size: 24px; font-weight: bold; color: #ffffff; } #closeBtn { background-color: transparent; border: none; padding: 5px; border-radius: 4px; } #closeBtn:hover { background-color: rgba(239, 68, 68, 0.2); } #detailIcon { background-color: rgba(255, 255, 255, 0.1); border-radius: 60px; padding: 10px; } #detailAuthor, #detailVersion, #detailUpload { font-size: 16px; color: rgba(255, 255, 255, 0.9); padding: 8px 15px; border-radius: 8px; background-color: rgba(255, 255, 255, 0.05); margin: 5px 0; } #detailDesc { font-size: 17px; color: rgba(255, 255, 255, 0.85); line-height: 1.6; padding: 0 0 15px 0; } #detailSection { font-size: 19px; font-weight: 600; color: #ffffff; margin-top: 15px; margin-bottom: 10px; } #featureItem { font-size: 15px; color: rgba(255, 255, 255, 0.8); padding-left: 15px; padding-bottom: 8px; } #detailStartBtn { background-color: #4f46e5; color: white; border-radius: 8px; padding: 12px 30px; font-size: 16px; font-weight: 600; min-width: 140px; transition: background-color 0.3s; } #detailStartBtn:hover { background-color: #4338ca; } #detailCancelBtn { background-color: rgba(255, 255, 255, 0.1); color: rgba(255, 255, 255, 0.9); border-radius: 8px; padding: 12px 30px; font-size: 16px; min-width: 100px; transition: background-color 0.3s; } #detailCancelBtn:hover { background-color: rgba(255, 255, 255, 0.2); } /* 内容区域背景色 */ QWidget#content { background-color: rgba(26, 32, 44, 1); /* 完全不透明 */ border-radius: 0 0 12px 12px; padding: 20px; } """ def init_ui(self): layout = QVBoxLayout(self) layout.setContentsMargins(1, 1, 1, 1) # 减小边距使阴影更明显 layout.setSpacing(0) # 标题栏 title_bar = QWidget() title_bar.setObjectName("titleBar") title_bar_layout = QHBoxLayout(title_bar) title_bar_layout.setContentsMargins(15, 5, 15, 5) title = QLabel(self.app_data["title"]) title.setObjectName("detailTitle") close_btn = QPushButton() close_btn.setObjectName("closeBtn") close_btn.setIcon(QIcon(":/icons/close.png")) close_btn.setIconSize(QSize(16, 16)) close_btn.clicked.connect(self.close) title_bar_layout.addWidget(title) title_bar_layout.addStretch() title_bar_layout.addWidget(close_btn) # 内容区域 content = QWidget() content.setObjectName("content") content_layout = QVBoxLayout(content) content_layout.setContentsMargins(20, 20, 20, 20) # 应用信息 info_layout = QHBoxLayout() info_layout.setSpacing(20) # 左侧 - 图标和基本信息 left_panel = QWidget() left_layout = QVBoxLayout(left_panel) left_layout.setSpacing(15) # 图标 icon_frame = QFrame() icon_frame.setFixedSize(120, 120) icon_frame.setObjectName("detailIcon") icon_layout = QVBoxLayout(icon_frame) icon = QLabel() icon_pixmap = QPixmap(f":/icons/{self.app_data['icon']}.png").scaled(80, 80, Qt.KeepAspectRatio, Qt.SmoothTransformation) # 转换图标为白色 painter = QPainter(icon_pixmap) painter.setCompositionMode(QPainter.CompositionMode_SourceIn) painter.fillRect(icon_pixmap.rect(), Qt.white) painter.end() icon.setPixmap(icon_pixmap) icon.setAlignment(Qt.AlignCenter) icon_layout.addWidget(icon) # 作者信息 author_label = QLabel(f"开发者: {self.app_data['author']}") author_label.setObjectName("detailAuthor") # 版本信息 version_label = QLabel(f"版本: {self.app_data['version']}") version_label.setObjectName("detailVersion") # 上传时间 upload_label = QLabel(f"上传时间: {self.app_data['upload_time']}") upload_label.setObjectName("detailUpload") left_layout.addWidget(icon_frame, 0, Qt.AlignCenter) left_layout.addWidget(author_label) left_layout.addWidget(version_label) left_layout.addWidget(upload_label) left_layout.addStretch() # 右侧 - 描述和功能 right_panel = QWidget() right_layout = QVBoxLayout(right_panel) right_layout.setSpacing(15) # 应用描述 desc_label = QLabel(self.app_data["desc"]) desc_label.setObjectName("detailDesc") desc_label.setWordWrap(True) # 功能列表 features_label = QLabel("功能特点:") features_label.setObjectName("detailSection") features_list = QWidget() features_layout = QVBoxLayout(features_list) features_layout.setSpacing(8) for feature in self.app_data["features"]: feature_item = QLabel(f"• {feature}") feature_item.setObjectName("featureItem") feature_item.setWordWrap(True) features_layout.addWidget(feature_item) # 操作按钮 btn_layout = QHBoxLayout() btn_layout.setSpacing(15) btn_layout.setContentsMargins(0, 20, 0, 0) # 添加上边距 start_btn = QPushButton("立即使用") start_btn.setObjectName("detailStartBtn") start_btn.clicked.connect(lambda: self.parent().app_selected.emit(self.app_data["id"])) cancel_btn = QPushButton("返回") cancel_btn.setObjectName("detailCancelBtn") cancel_btn.clicked.connect(self.close) btn_layout.addStretch() btn_layout.addWidget(start_btn) btn_layout.addWidget(cancel_btn) btn_layout.addStretch() right_layout.addWidget(desc_label) right_layout.addWidget(features_label) right_layout.addWidget(features_list) right_layout.addStretch() right_layout.addLayout(btn_layout) info_layout.addWidget(left_panel) info_layout.addWidget(right_panel) content_layout.addLayout(info_layout) # 添加到主布局 layout.addWidget(title_bar) layout.addWidget(content) # 添加大小控制点 size_grip = QSizeGrip(self) size_grip.setFixedSize(16, 16) size_grip.setStyleSheet("background: transparent;") layout.addWidget(size_grip, 0, Qt.AlignBottom | Qt.AlignRight) # 设置窗口尺寸 self.resize(700, 550) class AppCenter(QWidget): """应用中心页面,与Home页面卡片样式一致""" app_selected = pyqtSignal(str) def __init__(self): super().__init__() self.setObjectName("AppCenterPage") self.app_detail_window = None self.current_category = "全部" self.init_ui() self.setStyleSheet(self.get_styles()) # 启动加载动画 self.start_loading_animation() # 初始化应用数据 - 模拟API请求延迟 QTimer.singleShot(800, self.load_data) # 0.8秒后加载数据 # 添加背景粒子效果 self.particles = [] self.init_particles() self.particle_timer = QTimer(self) self.particle_timer.timeout.connect(self.update_particles) self.particle_timer.start(30) def init_ui(self): # 主布局 main_layout = QVBoxLayout() main_layout.setContentsMargins(40, 30, 40, 30) main_layout.setSpacing(20) # 标题和搜索区域 header_layout = QHBoxLayout() title = QLabel("应用中心") title.setObjectName("pageTitle") # 搜索框 search_container = QFrame() search_container.setObjectName("searchContainer") search_layout = QHBoxLayout(search_container) search_layout.setContentsMargins(15, 5, 15, 5) self.search_input = QLineEdit() self.search_input.setPlaceholderText("搜索应用...") self.search_input.setObjectName("searchInput") self.search_input.textChanged.connect(self.filter_apps) search_btn = QPushButton() search_btn.setObjectName("searchBtn") search_btn.setIcon(QIcon(":/icons/search.png")) search_btn.setIconSize(QSize(20, 20)) search_layout.addWidget(self.search_input) search_layout.addWidget(search_btn) header_layout.addWidget(title) header_layout.addStretch() header_layout.addWidget(search_container) main_layout.addLayout(header_layout) # 应用类别过滤 self.category_layout = QHBoxLayout() self.category_layout.setSpacing(15) categories = ["全部", "文档处理", "图像处理", "媒体工具", "其他"] for category in categories: btn = QPushButton(category) btn.setObjectName("categoryBtn") if category == "全部": btn.setProperty("active", True) btn.clicked.connect(lambda _, c=category: self.filter_by_category(c)) self.category_layout.addWidget(btn) self.category_layout.addStretch() main_layout.addLayout(self.category_layout) # 加载动画容器(初始时显示) self.loading_container = QWidget() self.loading_container.setVisible(True) loading_layout = QVBoxLayout(self.loading_container) loading_layout.setAlignment(Qt.AlignCenter) # 旋转SVG动画 self.loading_label = QLabel() self.loading_label.setObjectName("loadingLabel") loading_layout.addWidget(self.loading_label, alignment=Qt.AlignCenter) # 加载中文本 loading_text = QLabel("正在加载中...") loading_text.setObjectName("loadingText") loading_layout.addWidget(loading_text, alignment=Qt.AlignCenter) # 卡片容器(初始时隐藏) self.apps_container = QWidget() self.apps_container.setVisible(False) self.apps_layout = QGridLayout(self.apps_container) self.apps_layout.setSpacing(30) self.apps_layout.setAlignment(Qt.AlignTop | Qt.AlignHCenter) # 滚动区域(包含加载容器和卡片容器) scroll_area = QScrollArea() scroll_area.setWidgetResizable(True) # 创建包装容器 scroll_widget = QWidget() scroll_layout = QVBoxLayout(scroll_widget) scroll_layout.addWidget(self.loading_container) scroll_layout.addWidget(self.apps_container) scroll_layout.setContentsMargins(0, 0, 0, 0) scroll_area.setWidget(scroll_widget) scroll_area.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) scroll_area.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) scroll_area.setStyleSheet(""" QScrollArea { border: none; background: transparent; } QScrollBar:vertical { border: none; background: rgba(45, 55, 72, 0.3); width: 10px; margin: 0px 0px 0px 0px; } QScrollBar::handle:vertical { background: rgba(74, 85, 104, 0.7); min-height: 20px; border-radius: 4px; } QScrollBar::add-line:vertical, QScrollBar::sub-line:vertical { height: 0px; background: none; } """) main_layout.addWidget(scroll_area) self.setLayout(main_layout) # 启动加载动画 self.start_loading_animation() # 初始化应用数据 - 模拟API请求延迟 QTimer.singleShot(800, self.load_data) # 0.8秒后加载数据 # 添加背景粒子效果 self.particles = [] self.init_particles() self.particle_timer = QTimer(self) self.particle_timer.timeout.connect(self.update_particles) self.particle_timer.start(30) def start_loading_animation(self): """启动加载动画""" # 直接使用QSvgWidget加载SVG self.loading_svg = QSvgWidget() self.loading_svg.setFixedSize(80, 80) self.loading_svg.setStyleSheet("background: transparent;") # 添加SVG内容 svg_data = QByteArray(b''' ''') self.loading_svg.load(svg_data) # 添加到布局 self.loading_label.setLayout(QVBoxLayout()) self.loading_label.layout().setContentsMargins(0, 0, 0, 0) self.loading_label.layout().addWidget(self.loading_svg, alignment=Qt.AlignCenter) # 添加样式类 self.loading_label.setObjectName("loadingLabel") def load_data(self): """模拟加载数据""" self.apps = self.get_apps_data() # 隐藏加载动画,显示应用列表 self.loading_container.setVisible(False) self.apps_container.setVisible(True) # 计算列数并显示应用卡片 # cards_per_row = max(1, self.width() // 350) if self.width() > 0 else 3 cards_per_row = 3 self.display_apps(self.apps, cards_per_row) # 重置动画状态 if hasattr(self, 'loading_svg'): self.loading_svg.deleteLater() def filter_apps(self): """根据搜索关键词过滤应用""" keyword = self.search_input.text().strip().lower() if not keyword and self.current_category == "全部": self.display_apps(self.apps, max(1, self.width() // 350) if self.width() > 0 else 3) return filtered_apps = [] for app in self.apps: # 检查搜索条件 match_search = (not keyword or keyword in app["title"].lower() or keyword in app["desc"].lower() or keyword in app["author"].lower()) # 检查类别条件 match_category = (self.current_category == "全部" or app["category"] == self.current_category) if match_search and match_category: filtered_apps.append(app) # 计算列数并显示过滤后的应用 # cards_per_row = max(1, self.width() // 350) if self.width() > 0 else 3 cards_per_row = 3 self.display_apps(filtered_apps, cards_per_row) def filter_by_category(self, category): """根据类别过滤应用""" self.current_category = category # 更新按钮状态 for i in range(self.category_layout.count()): item = self.category_layout.itemAt(i) if item and item.widget(): btn = item.widget() btn.setProperty("active", btn.text() == category) # 重新应用样式 btn.style().unpolish(btn) btn.style().polish(btn) self.filter_apps() # 应用新的过滤 def display_apps(self, apps, cards_per_row=3): """显示应用列表""" # 清除当前内容 for i in reversed(range(self.apps_layout.count())): widget = self.apps_layout.itemAt(i).widget() if widget: widget.setParent(None) widget.deleteLater() # 添加新卡片 - 使用栅格布局 row, col = 0, 0 for i, app in enumerate(apps): card = self.create_app_card(app) self.apps_layout.addWidget(card, row, col) col += 1 if col >= cards_per_row: col = 0 row += 1 # 如果没有应用显示提示信息 if not apps: no_result = QLabel("没有找到匹配的应用") no_result.setObjectName("noResult") no_result.setAlignment(Qt.AlignCenter) self.apps_layout.addWidget(no_result, 0, 0, 1, cards_per_row) def show_app_detail(self, app_data): """显示应用详情弹窗""" if self.app_detail_window: self.app_detail_window.close() self.app_detail_window.deleteLater() self.app_detail_window = AppDetailWindow(app_data, self) # 计算位置(居中显示) center_point = self.mapToGlobal(self.rect().center()) self.app_detail_window.move(center_point - QPoint(self.app_detail_window.width() // 2, self.app_detail_window.height() // 2)) self.app_detail_window.show() def init_particles(self): """初始化粒子效果""" for _ in range(50): particle = { "pos": QPointF(random.randint(0, self.width()), random.randint(0, self.height())), "size": random.randint(2, 5), "speed": random.uniform(0.5, 2.0), "color": random.choice([ (66, 153, 225, 70), # 蓝色 (72, 187, 120, 60), # 绿色 (237, 137, 54, 60), # 橙色 (224, 42, 94, 50) # 粉色 ]) } self.particles.append(particle) def update_particles(self): """更新粒子位置""" for particle in self.particles: # 添加随机偏移 particle["pos"].setX(particle["pos"].x() + random.uniform(-particle["speed"], particle["speed"])) particle["pos"].setY(particle["pos"].y() + particle["speed"]) # 如果粒子移出底部,重置到顶部 if particle["pos"].y() > self.height(): particle["pos"] = QPointF(random.randint(0, self.width()), -10) self.update() def paintEvent(self, event): """绘制背景粒子效果""" painter = QPainter(self) painter.setRenderHint(QPainter.Antialiasing) # 绘制背景 painter.fillRect(self.rect(), QColor(26, 32, 44)) # 绘制粒子 for particle in self.particles: r, g, b, a = particle["color"] painter.setBrush(QColor(r, g, b, a)) painter.setPen(Qt.NoPen) painter.drawEllipse(particle["pos"], particle["size"], particle["size"]) def resizeEvent(self, event): """窗口大小改变时调整粒子""" # 移除超出边界的粒子 self.particles = [ p for p in self.particles if 0 <= p["pos"].x() <= self.width() and 0 <= p["pos"].y() <= self.height() ] # 补充新粒子 while len(self.particles) < 50: self.init_particles() super().resizeEvent(event) def create_app_card(self, app): """创建应用卡片,完全使用Home页面样式""" card = AnimatedCard(app['color'][0], app['color'][1]) card.setObjectName("appCard") # 使用垂直布局作为卡片的主布局 layout = QVBoxLayout(card) layout.setContentsMargins(25, 25, 25, 25) layout.setSpacing(15) # 图标部分 icon_frame = QFrame() icon_frame.setFixedSize(90, 90) icon_frame.setObjectName("appCardIcon") icon_layout = QVBoxLayout(icon_frame) icon_layout.setContentsMargins(0, 0, 0, 0) icon = QLabel() icon_pixmap = QPixmap(f":/icons/{app['icon']}.png").scaled(56, 56, Qt.KeepAspectRatio, Qt.SmoothTransformation) # 转换图标为白色 painter = QPainter(icon_pixmap) painter.setCompositionMode(QPainter.CompositionMode_SourceIn) painter.fillRect(icon_pixmap.rect(), Qt.white) painter.end() icon.setPixmap(icon_pixmap) icon.setAlignment(Qt.AlignCenter) icon_layout.addWidget(icon) layout.addWidget(icon_frame, 0, Qt.AlignHCenter) # 标题 title = QLabel(app["title"]) title.setObjectName("appCardTitle") title.setWordWrap(True) # 确保长标题可以换行 layout.addWidget(title) # 描述 desc = QLabel(app["desc"]) desc.setObjectName("appCardDesc") desc.setWordWrap(True) layout.addWidget(desc) # 添加伸缩空间保持底部对齐 layout.addStretch() # 按钮区域 - 使用水平布局并使按钮居中 btn_container = QWidget() btn_layout = QHBoxLayout(btn_container) btn_layout.setContentsMargins(0, 0, 0, 0) btn_layout.setSpacing(10) # 开始使用按钮 start_btn = QPushButton("开始使用") start_btn.setObjectName("appCardBtn") start_btn.setCursor(Qt.PointingHandCursor) start_btn.setMinimumWidth(100) # 设置最小宽度防止重叠 start_btn.clicked.connect(lambda: self.app_selected.emit(app["id"])) # 详情按钮 detail_btn = QPushButton("详情") detail_btn.setObjectName("appCardBtn") detail_btn.setCursor(Qt.PointingHandCursor) detail_btn.setMinimumWidth(100) # 设置最小宽度防止重叠 detail_btn.clicked.connect(lambda: self.show_app_detail(app)) # 添加到按钮布局 btn_layout.addWidget(start_btn) btn_layout.addWidget(detail_btn) # 将按钮布局添加到容器 layout.addWidget(btn_container, 0, Qt.AlignHCenter) return card def get_apps_data(self): """获取应用数据""" return [ { "id": "word_to_pdf", "title": "Word转PDF", "desc": "批量将Word文档转换为PDF格式", "color": ("#6366F1", "#4F46E5"), "icon": "word_to_pdf", "author": "年糕崽崽", "upload_time": "2023-06-15", "version": "1.2.0", "category": "文档处理", "features": [ "支持批量转换多个Word文件", "保持原始文档格式", "支持DOC和DOCX格式", "转换速度快,效率高" ] }, { "id": "pdf_tools_page", "title": "PDF合并/拆分工具", "desc": "合并多个PDF文件为一个", "color": ("#8B5CF6", "#7C3AED"), "icon": "pdf_merge", "author": "年糕崽崽", "upload_time": "2023-09-05", "version": "极速版1.1.0", "category": "文档处理", "features": [ "拖拽排序PDF文件", "自定义合并顺序", "保留原始文档质量", "支持大文件处理" ] }, # { # "id": "ocr", # "title": "OCR文字识别", # "desc": "从图片中提取文字内容", # "color": ("#4ADE80", "#22C55E"), # "icon": "ocr", # "author": "张伟", # "upload_time": "2023-07-22", # "version": "2.0.1", # "category": "图像处理", # "features": [ # "支持多种图片格式", # "识别准确率高", # "支持多语言识别", # "导出为可编辑文本" # ] # }, # { # "id": "image_convert", # "title": "图片格式转换", # "desc": "转换图像文件格式并优化", # "color": ("#F97316", "#EA580C"), # "icon": "image_convert", # "author": "王芳", # "upload_time": "2023-08-10", # "version": "1.5.3", # "category": "图像处理", # "features": [ # "支持JPEG、PNG、WEBP等格式", # "批量转换功能", # "图片压缩优化", # "保持图片质量" # ] # }, # { # "id": "video_compress", # "title": "视频压缩", # "desc": "减小视频文件大小", # "color": ("#3B82F6", "#2563EB"), # "icon": "video_compress", # "author": "赵雷", # "upload_time": "2023-10-02", # "version": "1.3.2", # "category": "媒体工具", # "features": [ # "支持多种视频格式", # "自定义压缩参数", # "保持视频质量", # "批量处理功能" # ] # }, # { # "id": "audio_extract", # "title": "音频提取", # "desc": "从视频中提取音频", # "color": ("#06B6D4", "#0891B2"), # "icon": "audio_extract", # "author": "杨帆", # "upload_time": "2023-10-15", # "version": "1.0.2", # "category": "媒体工具", # "features": [ # "支持常见视频格式", # "无损音频提取", # "批量处理功能", # "转换速度快" # ] # }, # { # "id": "text_summary", # "title": "文本摘要", # "desc": "自动提取文章关键信息", # "color": ("#F59E0B", "#D97706"), # "icon": "text_summary", # "author": "周华", # "upload_time": "2023-10-28", # "version": "2.1.0", # "category": "文档处理", # "features": [ # "多种摘要算法可选", # "自定义摘要长度", # "支持多语言处理", # "处理结果可导出" # ] # }, # { # "id": "color_picker", # "title": "颜色提取器", # "desc": "从图像中提取配色方案", # "color": ("#EC4899", "#DB2777"), # "icon": "color_picker", # "author": "吴敏", # "upload_time": "2023-11-05", # "version": "1.0.0", # "category": "图像处理", # "features": [ # "自动分析主色调", # "提取多种配色方案", # "支持导出配色代码", # "直观的颜色展示" # ] # } ] def get_styles(self): return """ /* ===== 应用中心页面样式 ===== */ #AppCenterPage { background-color: #1a202c; } #pageTitle { font-size: 28px; font-weight: bold; color: #ffffff; text-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); margin-bottom: 30px; } #searchContainer { background-color: rgba(255, 255, 255, 0.15); border-radius: 12px; min-width: 300px; } #searchInput { background: transparent; border: none; color: white; font-size: 16px; padding: 8px 0; } #searchInput::placeholder { color: rgba(255, 255, 255, 0.5); } #searchBtn { background: transparent; border: none; padding: 0; } #searchBtn:hover { background-color: rgba(255, 255, 255, 0.1); border-radius: 6px; } #categoryBtn { background-color: rgba(74, 85, 104, 0.5); color: rgba(255, 255, 255, 0.8); border-radius: 8px; padding: 8px 20px; font-size: 15px; transition: all 0.3s ease; } #categoryBtn[active="true"] { background-color: #4f46e5; color: white; } #categoryBtn:hover { background-color: rgba(74, 85, 104, 0.7); } #noResult { font-size: 20px; color: rgba(255, 255, 255, 0.5); padding: 50px; } /* ===== 加载动画样式 ===== */ #loadingText { font-size: 24px; color: rgba(255, 255, 255, 0.7); margin-top: 20px; text-align: center; letter-spacing: 2px; } #loadingLabel { margin: 0; padding: 0; } /* ===== 应用卡片样式 ===== */ #appCard { border: 1px solid rgba(255, 255, 255, 0.1); } #appCardTitle { font-size: 22px; font-weight: 700; color: white; text-align: center; margin-top: 15px; letter-spacing: 0.5px; padding: 0 10px; /* 添加内边距防止文字溢出 */ } #appCardDesc { font-size: 15px; color: rgba(255, 255, 255, 0.8); text-align: center; padding: 0 15px; line-height: 1.5; } #appCardBtn { background-color: rgba(255, 255, 255, 0.15); /* 半透明背景 */ color: white; border-radius: 12px; padding: 10px 15px; /* 减少内边距 */ font-weight: 500; border: 1px solid rgba(255, 255, 255, 0.2); min-width: 80px; /* 减小最小宽度 */ transition: all 0.3s ease; font-size: 14px; /* 减小字体大小 */ } #appCardBtn:hover { background-color: rgba(255, 255, 255, 0.25); transform: translateY(-2px); } #appCardIcon { background-color: rgba(255, 255, 255, 0.15); border-radius: 50%; transition: all 0.3s ease; } #appCardIcon:hover { transform: scale(1.05); } """