feat: 知识管理平台精简版 - PyQt6桌面应用

主要功能:
- 账号管理:添加/编辑/删除账号,测试登录
- 浏览任务:批量浏览应读/选读内容并标记已读
- 截图管理:wkhtmltoimage截图,查看历史
- 金山文档:扫码登录/微信快捷登录,自动上传截图

技术栈:
- PyQt6 GUI框架
- Playwright 浏览器自动化
- SQLite 本地数据存储
- wkhtmltoimage 网页截图

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-18 22:16:36 +08:00
commit 83fef6dff2
24 changed files with 6133 additions and 0 deletions

635
ui/styles.py Normal file
View File

@@ -0,0 +1,635 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
QSS Modern Stylesheet - 现代化UI样式
老王说:样式写好了界面才漂亮,这些都是精心调教过的!
"""
# 浅色主题
LIGHT_THEME = {
"bg_primary": "#ffffff",
"bg_secondary": "#fafafa",
"bg_tertiary": "#f0f0f0",
"bg_card": "#ffffff",
"text_primary": "#1a1a1a",
"text_secondary": "#595959",
"text_muted": "#8c8c8c",
"text_placeholder": "#bfbfbf",
"border": "#e8e8e8",
"border_light": "#f0f0f0",
"accent": "#1677ff",
"accent_hover": "#4096ff",
"accent_pressed": "#0958d9",
"accent_bg": "#e6f4ff",
"success": "#52c41a",
"success_bg": "#f6ffed",
"warning": "#faad14",
"warning_bg": "#fffbe6",
"error": "#ff4d4f",
"error_bg": "#fff2f0",
"sidebar_bg": "#001529",
"sidebar_text": "#ffffffd9",
"sidebar_text_muted": "#ffffff73",
"sidebar_hover": "#1677ff",
"sidebar_active_bg": "#1677ff",
"shadow": "rgba(0, 0, 0, 0.08)",
}
# 深色主题
DARK_THEME = {
"bg_primary": "#141414",
"bg_secondary": "#1f1f1f",
"bg_tertiary": "#262626",
"bg_card": "#1f1f1f",
"text_primary": "#ffffffd9",
"text_secondary": "#ffffffa6",
"text_muted": "#ffffff73",
"text_placeholder": "#ffffff40",
"border": "#424242",
"border_light": "#303030",
"accent": "#1668dc",
"accent_hover": "#3c89e8",
"accent_pressed": "#1554ad",
"accent_bg": "#111a2c",
"success": "#49aa19",
"success_bg": "#162312",
"warning": "#d89614",
"warning_bg": "#2b2111",
"error": "#dc4446",
"error_bg": "#2c1618",
"sidebar_bg": "#000000",
"sidebar_text": "#ffffffd9",
"sidebar_text_muted": "#ffffff73",
"sidebar_hover": "#1668dc",
"sidebar_active_bg": "#1668dc",
"shadow": "rgba(0, 0, 0, 0.45)",
}
def get_stylesheet(theme: dict) -> str:
"""Generate QSS stylesheet based on theme"""
return f"""
/* ==================== Global Reset ==================== */
* {{
outline: none;
}}
QWidget {{
font-family: "Microsoft YaHei UI", "Segoe UI", "PingFang SC", sans-serif;
font-size: 14px;
color: {theme["text_primary"]};
background-color: transparent;
}}
QMainWindow {{
background-color: {theme["bg_secondary"]};
}}
/* ==================== Scroll Area ==================== */
QScrollArea {{
background-color: transparent;
border: none;
}}
QScrollArea > QWidget > QWidget {{
background-color: transparent;
}}
/* ==================== Sidebar ==================== */
#sidebar {{
background-color: {theme["sidebar_bg"]};
border: none;
}}
#sidebar QPushButton {{
color: {theme["sidebar_text"]};
background-color: transparent;
border: none;
border-radius: 0;
padding: 14px 20px;
text-align: left;
font-size: 14px;
font-weight: 500;
}}
#sidebar QPushButton:hover {{
background-color: rgba(255, 255, 255, 0.08);
}}
#sidebar QPushButton:checked {{
background-color: {theme["sidebar_active_bg"]};
color: white;
font-weight: 600;
}}
#sidebar_title {{
color: white;
font-size: 16px;
font-weight: bold;
padding: 20px;
background-color: transparent;
}}
/* ==================== Buttons ==================== */
QPushButton {{
background-color: {theme["bg_card"]};
color: {theme["text_primary"]};
border: 1px solid {theme["border"]};
border-radius: 6px;
padding: 8px 16px;
font-weight: 500;
}}
QPushButton:hover {{
border-color: {theme["accent"]};
color: {theme["accent"]};
}}
QPushButton:pressed {{
background-color: {theme["bg_tertiary"]};
}}
QPushButton:disabled {{
background-color: {theme["bg_tertiary"]};
color: {theme["text_muted"]};
border-color: {theme["border"]};
}}
/* Primary Button */
QPushButton#primary {{
background-color: {theme["accent"]};
color: white;
border: none;
font-weight: 600;
}}
QPushButton#primary:hover {{
background-color: {theme["accent_hover"]};
}}
QPushButton#primary:pressed {{
background-color: {theme["accent_pressed"]};
}}
QPushButton#primary:disabled {{
background-color: {theme["accent"]};
opacity: 0.6;
}}
/* Success Button */
QPushButton#success {{
background-color: {theme["success"]};
color: white;
border: none;
}}
QPushButton#success:hover {{
background-color: #73d13d;
}}
/* Danger Button */
QPushButton#danger {{
background-color: {theme["error"]};
color: white;
border: none;
}}
QPushButton#danger:hover {{
background-color: #ff7875;
}}
/* ==================== Input Fields ==================== */
QLineEdit {{
background-color: {theme["bg_card"]};
color: {theme["text_primary"]};
border: 1px solid {theme["border"]};
border-radius: 6px;
padding: 6px 10px;
selection-background-color: {theme["accent"]};
selection-color: white;
}}
QLineEdit:hover {{
border-color: {theme["accent"]};
}}
QLineEdit:focus {{
border-color: {theme["accent"]};
border-width: 2px;
padding: 5px 9px;
}}
QLineEdit:disabled {{
background-color: {theme["bg_tertiary"]};
color: {theme["text_muted"]};
}}
QLineEdit[readOnly="true"] {{
background-color: {theme["bg_secondary"]};
}}
/* ==================== TextEdit ==================== */
QTextEdit, QPlainTextEdit {{
background-color: {theme["bg_card"]};
color: {theme["text_primary"]};
border: 1px solid {theme["border"]};
border-radius: 6px;
padding: 10px 14px;
selection-background-color: {theme["accent"]};
}}
QTextEdit:focus, QPlainTextEdit:focus {{
border-color: {theme["accent"]};
}}
/* ==================== ComboBox ==================== */
QComboBox {{
background-color: {theme["bg_card"]};
color: {theme["text_primary"]};
border: 1px solid {theme["border"]};
border-radius: 6px;
padding: 6px 10px;
padding-right: 30px;
}}
QComboBox:hover {{
border-color: {theme["accent"]};
}}
QComboBox:focus {{
border-color: {theme["accent"]};
}}
QComboBox::drop-down {{
border: none;
width: 30px;
subcontrol-position: right center;
}}
QComboBox::down-arrow {{
image: none;
border-left: 5px solid transparent;
border-right: 5px solid transparent;
border-top: 6px solid {theme["text_secondary"]};
margin-right: 10px;
}}
QComboBox QAbstractItemView {{
background-color: {theme["bg_card"]};
border: 1px solid {theme["border"]};
border-radius: 6px;
padding: 4px;
selection-background-color: {theme["accent_bg"]};
selection-color: {theme["accent"]};
outline: none;
}}
QComboBox QAbstractItemView::item {{
padding: 8px 12px;
border-radius: 4px;
}}
QComboBox QAbstractItemView::item:hover {{
background-color: {theme["bg_tertiary"]};
}}
/* ==================== SpinBox ==================== */
QSpinBox {{
background-color: {theme["bg_card"]};
color: {theme["text_primary"]};
border: 1px solid {theme["border"]};
border-radius: 6px;
padding: 10px 14px;
padding-right: 30px;
}}
QSpinBox:hover {{
border-color: {theme["accent"]};
}}
QSpinBox:focus {{
border-color: {theme["accent"]};
}}
QSpinBox::up-button, QSpinBox::down-button {{
width: 20px;
border: none;
background: transparent;
}}
QSpinBox::up-arrow, QSpinBox::down-arrow {{
width: 0;
height: 0;
border: none;
background: transparent;
}}
/* ==================== CheckBox ==================== */
QCheckBox {{
spacing: 10px;
color: {theme["text_primary"]};
}}
QCheckBox::indicator {{
width: 18px;
height: 18px;
border: 1px solid {theme["border"]};
border-radius: 4px;
background-color: {theme["bg_card"]};
}}
QCheckBox::indicator:hover {{
border-color: {theme["accent"]};
}}
QCheckBox::indicator:checked {{
background-color: {theme["accent"]};
border-color: {theme["accent"]};
}}
/* ==================== GroupBox - Card Style ==================== */
QGroupBox {{
background-color: {theme["bg_card"]};
border: 1px solid {theme["border"]};
border-radius: 10px;
margin-top: 14px;
padding: 24px 0 0 0;
}}
QGroupBox::title {{
subcontrol-origin: margin;
subcontrol-position: top left;
left: 16px;
top: 0px;
padding: 4px 12px;
background-color: {theme["accent"]};
color: white;
border-radius: 4px;
font-weight: 600;
font-size: 13px;
}}
/* ==================== Table ==================== */
QTableWidget {{
background-color: {theme["bg_card"]};
border: 1px solid {theme["border"]};
border-radius: 8px;
gridline-color: {theme["border_light"]};
selection-background-color: {theme["accent_bg"]};
selection-color: {theme["text_primary"]};
}}
QTableWidget::item {{
padding: 8px 4px;
border: none;
}}
QTableWidget::item:selected {{
background-color: {theme["accent_bg"]};
color: {theme["accent"]};
}}
QTableWidget::item:hover {{
background-color: {theme["bg_secondary"]};
}}
QHeaderView::section {{
background-color: {theme["bg_secondary"]};
color: {theme["text_secondary"]};
padding: 12px 8px;
border: none;
border-bottom: 1px solid {theme["border"]};
font-weight: 600;
font-size: 13px;
}}
QHeaderView::section:first {{
border-top-left-radius: 8px;
}}
QHeaderView::section:last {{
border-top-right-radius: 8px;
}}
QTableCornerButton::section {{
background-color: {theme["bg_secondary"]};
border: none;
}}
/* ==================== List ==================== */
QListWidget {{
background-color: {theme["bg_card"]};
border: 1px solid {theme["border"]};
border-radius: 8px;
padding: 4px;
outline: none;
}}
QListWidget::item {{
padding: 12px 14px;
border-radius: 6px;
margin: 2px 0;
}}
QListWidget::item:selected {{
background-color: {theme["accent_bg"]};
color: {theme["accent"]};
}}
QListWidget::item:hover:!selected {{
background-color: {theme["bg_secondary"]};
}}
/* ==================== Progress Bar ==================== */
QProgressBar {{
background-color: {theme["bg_tertiary"]};
border: none;
border-radius: 4px;
height: 8px;
text-align: center;
}}
QProgressBar::chunk {{
background-color: {theme["accent"]};
border-radius: 4px;
}}
/* ==================== ScrollBar ==================== */
QScrollBar:vertical {{
background-color: transparent;
width: 8px;
margin: 4px 2px;
}}
QScrollBar::handle:vertical {{
background-color: {theme["border"]};
border-radius: 4px;
min-height: 40px;
}}
QScrollBar::handle:vertical:hover {{
background-color: {theme["text_muted"]};
}}
QScrollBar::add-line:vertical, QScrollBar::sub-line:vertical {{
height: 0;
background: none;
}}
QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical {{
background: none;
}}
QScrollBar:horizontal {{
background-color: transparent;
height: 8px;
margin: 2px 4px;
}}
QScrollBar::handle:horizontal {{
background-color: {theme["border"]};
border-radius: 4px;
min-width: 40px;
}}
QScrollBar::handle:horizontal:hover {{
background-color: {theme["text_muted"]};
}}
QScrollBar::add-line:horizontal, QScrollBar::sub-line:horizontal {{
width: 0;
background: none;
}}
/* ==================== Tab Widget ==================== */
QTabWidget::pane {{
border: 1px solid {theme["border"]};
border-radius: 8px;
background-color: {theme["bg_card"]};
padding: 8px;
}}
QTabBar::tab {{
background-color: transparent;
color: {theme["text_secondary"]};
padding: 10px 20px;
margin-right: 4px;
border-bottom: 2px solid transparent;
}}
QTabBar::tab:selected {{
color: {theme["accent"]};
border-bottom: 2px solid {theme["accent"]};
}}
QTabBar::tab:hover:!selected {{
color: {theme["text_primary"]};
}}
/* ==================== ToolTip ==================== */
QToolTip {{
background-color: {theme["bg_tertiary"]};
color: {theme["text_primary"]};
border: 1px solid {theme["border"]};
border-radius: 6px;
padding: 8px 12px;
}}
/* ==================== Message Box ==================== */
QMessageBox {{
background-color: {theme["bg_card"]};
}}
QMessageBox QLabel {{
color: {theme["text_primary"]};
font-size: 14px;
}}
QMessageBox QPushButton {{
min-width: 80px;
min-height: 32px;
}}
/* ==================== Dialog ==================== */
QDialog {{
background-color: {theme["bg_card"]};
}}
/* ==================== Label ==================== */
QLabel {{
color: {theme["text_primary"]};
background-color: transparent;
}}
QLabel#title {{
font-size: 20px;
font-weight: 600;
color: {theme["text_primary"]};
}}
QLabel#subtitle {{
font-size: 14px;
color: {theme["text_secondary"]};
}}
QLabel#help {{
font-size: 13px;
color: {theme["text_muted"]};
}}
/* ==================== Frame ==================== */
QFrame {{
background-color: transparent;
}}
QFrame#card {{
background-color: {theme["bg_card"]};
border: 1px solid {theme["border"]};
border-radius: 10px;
}}
QFrame#separator {{
background-color: {theme["border"]};
max-height: 1px;
}}
/* ==================== Log Widget ==================== */
#log_widget {{
background-color: {theme["bg_secondary"]};
border-top: 1px solid {theme["border"]};
}}
#log_widget QPlainTextEdit {{
background-color: {theme["bg_secondary"]};
border: none;
color: {theme["text_secondary"]};
font-family: "Cascadia Code", "Consolas", "Monaco", monospace;
font-size: 12px;
padding: 8px;
}}
/* ==================== Status Colors ==================== */
.success {{
color: {theme["success"]};
}}
.warning {{
color: {theme["warning"]};
}}
.error {{
color: {theme["error"]};
}}
/* ==================== Form Label ==================== */
QLabel#formLabel {{
color: {theme["text_secondary"]};
font-weight: 500;
}}
"""
def apply_theme(app, theme_name: str = "light"):
"""Apply theme to application"""
theme = LIGHT_THEME if theme_name == "light" else DARK_THEME
app.setStyleSheet(get_stylesheet(theme))