主要功能: - 账号管理:添加/编辑/删除账号,测试登录 - 浏览任务:批量浏览应读/选读内容并标记已读 - 截图管理:wkhtmltoimage截图,查看历史 - 金山文档:扫码登录/微信快捷登录,自动上传截图 技术栈: - PyQt6 GUI框架 - Playwright 浏览器自动化 - SQLite 本地数据存储 - wkhtmltoimage 网页截图 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
651 lines
27 KiB
Python
651 lines
27 KiB
Python
#!/usr/bin/env python3
|
||
# -*- coding: utf-8 -*-
|
||
"""
|
||
KDocs panel - configure doc URL, columns, QR login, manual upload
|
||
"""
|
||
|
||
import base64
|
||
from PyQt6.QtWidgets import (
|
||
QWidget, QVBoxLayout, QHBoxLayout, QLabel, QLineEdit,
|
||
QPushButton, QGroupBox, QSpinBox, QCheckBox,
|
||
QMessageBox, QScrollArea, QFrame
|
||
)
|
||
from PyQt6.QtCore import Qt, pyqtSignal, QTimer
|
||
from PyQt6.QtGui import QPixmap
|
||
|
||
from config import get_config, save_config
|
||
from .constants import (
|
||
PAGE_PADDING, SECTION_SPACING, GROUP_PADDING_TOP, GROUP_PADDING_SIDE,
|
||
GROUP_PADDING_BOTTOM, GROUP_SPACING, FORM_ROW_SPACING, FORM_LABEL_WIDTH,
|
||
FORM_H_SPACING, INPUT_HEIGHT, INPUT_MIN_WIDTH, INPUT_WIDTH_SHORT,
|
||
BUTTON_HEIGHT, BUTTON_HEIGHT_NORMAL, BUTTON_MIN_WIDTH, BUTTON_SPACING,
|
||
QR_CODE_SIZE, get_title_style, get_status_style
|
||
)
|
||
|
||
|
||
class KDocsWidget(QWidget):
|
||
"""KDocs panel"""
|
||
|
||
log_signal = pyqtSignal(str)
|
||
|
||
def __init__(self, parent=None):
|
||
super().__init__(parent)
|
||
self._worker = None
|
||
self._login_check_timer = None
|
||
self._setup_ui()
|
||
self._load_config()
|
||
self._check_wechat_process() # 检测微信进程
|
||
|
||
def _setup_ui(self):
|
||
main_layout = QVBoxLayout(self)
|
||
main_layout.setContentsMargins(16, 16, 16, 16)
|
||
main_layout.setSpacing(16)
|
||
|
||
# ==================== Title + Save button ====================
|
||
header_layout = QHBoxLayout()
|
||
title = QLabel("📤 金山文档上传")
|
||
title.setStyleSheet(get_title_style())
|
||
header_layout.addWidget(title)
|
||
|
||
header_layout.addStretch()
|
||
|
||
self.enable_check = QCheckBox("启用上传")
|
||
self.enable_check.stateChanged.connect(self._on_enable_changed)
|
||
header_layout.addWidget(self.enable_check)
|
||
|
||
self.save_btn = QPushButton("保存配置")
|
||
self.save_btn.setObjectName("primary")
|
||
self.save_btn.setFixedHeight(36)
|
||
self.save_btn.setMinimumWidth(100)
|
||
self.save_btn.clicked.connect(self._save_config)
|
||
header_layout.addWidget(self.save_btn)
|
||
|
||
main_layout.addLayout(header_layout)
|
||
|
||
# ==================== Document config ====================
|
||
config_group = QGroupBox("文档配置")
|
||
config_layout = QVBoxLayout(config_group)
|
||
config_layout.setContentsMargins(16, 24, 16, 16)
|
||
config_layout.setSpacing(12)
|
||
|
||
# Row 1: Doc URL
|
||
row1 = QHBoxLayout()
|
||
row1.setSpacing(10)
|
||
lbl1 = QLabel("文档链接:")
|
||
lbl1.setFixedWidth(70)
|
||
row1.addWidget(lbl1)
|
||
self.doc_url_edit = QLineEdit()
|
||
self.doc_url_edit.setPlaceholderText("https://kdocs.cn/l/xxxxxx")
|
||
self.doc_url_edit.setMinimumHeight(36)
|
||
row1.addWidget(self.doc_url_edit, 1)
|
||
config_layout.addLayout(row1)
|
||
|
||
# Row 2: Sheet name + Unit
|
||
row2 = QHBoxLayout()
|
||
row2.setSpacing(10)
|
||
lbl2 = QLabel("工作表:")
|
||
lbl2.setFixedWidth(70)
|
||
row2.addWidget(lbl2)
|
||
self.sheet_name_edit = QLineEdit()
|
||
self.sheet_name_edit.setPlaceholderText("Sheet1")
|
||
self.sheet_name_edit.setMinimumHeight(36)
|
||
self.sheet_name_edit.setFixedWidth(150)
|
||
row2.addWidget(self.sheet_name_edit)
|
||
row2.addSpacing(30)
|
||
lbl2b = QLabel("所属县区:")
|
||
lbl2b.setFixedWidth(70)
|
||
row2.addWidget(lbl2b)
|
||
self.unit_edit = QLineEdit()
|
||
self.unit_edit.setPlaceholderText("如:XX县")
|
||
self.unit_edit.setMinimumHeight(36)
|
||
row2.addWidget(self.unit_edit, 1)
|
||
config_layout.addLayout(row2)
|
||
|
||
# Row 3: Columns
|
||
row3 = QHBoxLayout()
|
||
row3.setSpacing(10)
|
||
lbl3 = QLabel("县区列:")
|
||
lbl3.setFixedWidth(70)
|
||
row3.addWidget(lbl3)
|
||
self.unit_col_edit = QLineEdit()
|
||
self.unit_col_edit.setFixedWidth(80)
|
||
self.unit_col_edit.setMinimumHeight(36)
|
||
self.unit_col_edit.setPlaceholderText("A")
|
||
row3.addWidget(self.unit_col_edit)
|
||
row3.addSpacing(20)
|
||
lbl3b = QLabel("姓名列:")
|
||
lbl3b.setFixedWidth(60)
|
||
row3.addWidget(lbl3b)
|
||
self.name_col_edit = QLineEdit()
|
||
self.name_col_edit.setFixedWidth(80)
|
||
self.name_col_edit.setMinimumHeight(36)
|
||
self.name_col_edit.setPlaceholderText("C")
|
||
row3.addWidget(self.name_col_edit)
|
||
row3.addSpacing(20)
|
||
lbl3c = QLabel("图片列:")
|
||
lbl3c.setFixedWidth(60)
|
||
row3.addWidget(lbl3c)
|
||
self.image_col_edit = QLineEdit()
|
||
self.image_col_edit.setFixedWidth(80)
|
||
self.image_col_edit.setMinimumHeight(36)
|
||
self.image_col_edit.setPlaceholderText("D")
|
||
row3.addWidget(self.image_col_edit)
|
||
row3.addStretch()
|
||
config_layout.addLayout(row3)
|
||
|
||
# Row 4: Row range - SpinBox加宽确保"不限制"显示完整
|
||
row4 = QHBoxLayout()
|
||
row4.setSpacing(10)
|
||
lbl4 = QLabel("起始行:")
|
||
lbl4.setFixedWidth(70)
|
||
row4.addWidget(lbl4)
|
||
self.row_start_spin = QSpinBox()
|
||
self.row_start_spin.setRange(0, 10000)
|
||
self.row_start_spin.setSpecialValueText("不限制")
|
||
self.row_start_spin.setFixedWidth(120)
|
||
self.row_start_spin.setMinimumHeight(36)
|
||
row4.addWidget(self.row_start_spin)
|
||
row4.addSpacing(20)
|
||
lbl4b = QLabel("结束行:")
|
||
lbl4b.setFixedWidth(60)
|
||
row4.addWidget(lbl4b)
|
||
self.row_end_spin = QSpinBox()
|
||
self.row_end_spin.setRange(0, 10000)
|
||
self.row_end_spin.setSpecialValueText("不限制")
|
||
self.row_end_spin.setFixedWidth(120)
|
||
self.row_end_spin.setMinimumHeight(36)
|
||
row4.addWidget(self.row_end_spin)
|
||
row4.addStretch()
|
||
config_layout.addLayout(row4)
|
||
|
||
main_layout.addWidget(config_group)
|
||
|
||
# ==================== Login status ====================
|
||
login_group = QGroupBox("登录状态")
|
||
login_layout = QHBoxLayout(login_group)
|
||
login_layout.setContentsMargins(16, 24, 16, 16)
|
||
login_layout.setSpacing(20)
|
||
|
||
# QR code
|
||
self.qr_label = QLabel()
|
||
self.qr_label.setFixedSize(150, 150)
|
||
self.qr_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||
self.qr_label.setStyleSheet("border: 1px solid #d0d0d0; border-radius: 6px; background: #fafafa;")
|
||
self.qr_label.setText("点击获取二维码")
|
||
login_layout.addWidget(self.qr_label)
|
||
|
||
# Status and buttons
|
||
status_btn_layout = QVBoxLayout()
|
||
status_btn_layout.setSpacing(10)
|
||
|
||
self.status_label = QLabel("未登录")
|
||
self.status_label.setStyleSheet(get_status_style(False))
|
||
status_btn_layout.addWidget(self.status_label)
|
||
|
||
status_btn_layout.addStretch()
|
||
|
||
self.get_qr_btn = QPushButton("获取二维码")
|
||
self.get_qr_btn.setObjectName("primary")
|
||
self.get_qr_btn.setFixedHeight(36)
|
||
self.get_qr_btn.setMinimumWidth(120)
|
||
self.get_qr_btn.clicked.connect(self._get_qr_code)
|
||
status_btn_layout.addWidget(self.get_qr_btn)
|
||
|
||
# 快捷登录按钮(检测到微信进程时显示)
|
||
self.quick_login_btn = QPushButton("⚡ 微信快捷登录")
|
||
self.quick_login_btn.setFixedHeight(36)
|
||
self.quick_login_btn.setMinimumWidth(130)
|
||
self.quick_login_btn.setStyleSheet("""
|
||
QPushButton {
|
||
background-color: #07C160;
|
||
color: white;
|
||
border: none;
|
||
border-radius: 4px;
|
||
font-weight: bold;
|
||
}
|
||
QPushButton:hover {
|
||
background-color: #06AD56;
|
||
}
|
||
QPushButton:disabled {
|
||
background-color: #ccc;
|
||
}
|
||
""")
|
||
self.quick_login_btn.clicked.connect(self._quick_login)
|
||
self.quick_login_btn.setVisible(False) # 默认隐藏
|
||
status_btn_layout.addWidget(self.quick_login_btn)
|
||
|
||
self.clear_login_btn = QPushButton("清除登录")
|
||
self.clear_login_btn.setFixedHeight(36)
|
||
self.clear_login_btn.setMinimumWidth(120)
|
||
self.clear_login_btn.clicked.connect(self._clear_login)
|
||
status_btn_layout.addWidget(self.clear_login_btn)
|
||
|
||
status_btn_layout.addStretch()
|
||
|
||
login_layout.addLayout(status_btn_layout)
|
||
login_layout.addStretch()
|
||
|
||
main_layout.addWidget(login_group)
|
||
main_layout.addStretch()
|
||
|
||
def _load_config(self):
|
||
"""Load config"""
|
||
config = get_config()
|
||
kdocs = config.kdocs
|
||
|
||
self.enable_check.setChecked(kdocs.enabled)
|
||
self.doc_url_edit.setText(kdocs.doc_url)
|
||
self.sheet_name_edit.setText(kdocs.sheet_name)
|
||
self.unit_col_edit.setText(kdocs.unit_column)
|
||
self.name_col_edit.setText(kdocs.name_column)
|
||
self.image_col_edit.setText(kdocs.image_column)
|
||
self.row_start_spin.setValue(kdocs.row_start)
|
||
self.row_end_spin.setValue(kdocs.row_end)
|
||
self.unit_edit.setText(kdocs.unit)
|
||
|
||
self._on_enable_changed(Qt.CheckState.Checked.value if kdocs.enabled else Qt.CheckState.Unchecked.value)
|
||
|
||
def _save_config(self):
|
||
"""Save config"""
|
||
config = get_config()
|
||
|
||
config.kdocs.enabled = self.enable_check.isChecked()
|
||
config.kdocs.doc_url = self.doc_url_edit.text().strip()
|
||
config.kdocs.sheet_name = self.sheet_name_edit.text().strip()
|
||
config.kdocs.unit_column = self.unit_col_edit.text().strip().upper() or "A"
|
||
config.kdocs.name_column = self.name_col_edit.text().strip().upper() or "C"
|
||
config.kdocs.image_column = self.image_col_edit.text().strip().upper() or "D"
|
||
config.kdocs.row_start = self.row_start_spin.value()
|
||
config.kdocs.row_end = self.row_end_spin.value()
|
||
config.kdocs.unit = self.unit_edit.text().strip()
|
||
|
||
save_config(config)
|
||
self.log_signal.emit("金山文档配置已保存")
|
||
QMessageBox.information(self, "提示", "配置已保存")
|
||
|
||
def _on_enable_changed(self, state):
|
||
"""Enable state changed"""
|
||
pass # Can disable/enable other controls if needed
|
||
|
||
def _get_qr_code(self):
|
||
"""Get login QR code and poll for login status"""
|
||
self.get_qr_btn.setEnabled(False)
|
||
self.qr_label.setText("获取中...")
|
||
self.log_signal.emit("正在获取金山文档登录二维码...")
|
||
|
||
# 停止之前的检查
|
||
if self._login_check_timer:
|
||
self._login_check_timer.stop()
|
||
self._login_check_timer = None
|
||
|
||
from utils.worker import Worker
|
||
|
||
def get_qr_and_wait_login(_signals=None, _should_stop=None):
|
||
"""获取二维码并轮询等待登录(在同一个线程中完成所有Playwright操作)"""
|
||
from core.kdocs_uploader import get_kdocs_uploader
|
||
import time
|
||
|
||
uploader = get_kdocs_uploader()
|
||
uploader._log_callback = lambda msg: _signals.log.emit(msg) if _signals else None
|
||
|
||
# 1. 获取二维码
|
||
result = uploader.request_qr(force=True)
|
||
if not result.get("success"):
|
||
return result
|
||
|
||
if result.get("logged_in"):
|
||
return result
|
||
|
||
# 2. 发送二维码图片(通过信号)
|
||
qr_image = result.get("qr_image", "")
|
||
if qr_image:
|
||
_signals.screenshot_ready.emit(qr_image) # 复用这个信号传递二维码
|
||
|
||
# 3. 在同一个线程中轮询检查登录状态
|
||
max_wait = 120 # 最多等待120秒
|
||
check_interval = 3 # 每3秒检查一次
|
||
waited = 0
|
||
check_count = 0
|
||
|
||
while waited < max_wait:
|
||
if _should_stop and _should_stop():
|
||
return {"success": False, "error": "用户取消"}
|
||
|
||
time.sleep(check_interval)
|
||
waited += check_interval
|
||
check_count += 1
|
||
|
||
# 检查登录状态(在同一个线程中,不会有线程问题)
|
||
if _signals:
|
||
_signals.log.emit(f"[KDocs] 检查登录状态... ({check_count})")
|
||
check_result = uploader.check_login_status()
|
||
if check_result.get("logged_in"):
|
||
return {"success": True, "logged_in": True}
|
||
|
||
# 每30秒提醒一下用户
|
||
if waited % 30 == 0 and _signals:
|
||
remaining = max_wait - waited
|
||
_signals.log.emit(f"[KDocs] 等待扫码登录...(剩余{remaining}秒)")
|
||
|
||
return {"success": False, "error": "登录超时(120秒),请重新获取二维码"}
|
||
|
||
def on_qr_ready(qr_base64):
|
||
"""二维码准备好了,显示出来"""
|
||
if qr_base64:
|
||
qr_bytes = base64.b64decode(qr_base64)
|
||
pixmap = QPixmap()
|
||
pixmap.loadFromData(qr_bytes)
|
||
scaled = pixmap.scaled(140, 140, Qt.AspectRatioMode.KeepAspectRatio, Qt.TransformationMode.SmoothTransformation)
|
||
self.qr_label.setPixmap(scaled)
|
||
self.log_signal.emit("请使用微信扫描二维码登录(120秒内有效)")
|
||
|
||
def on_result(result):
|
||
self.get_qr_btn.setEnabled(True)
|
||
if result and result.get("success"):
|
||
if result.get("logged_in"):
|
||
self.status_label.setText("✅ 已登录")
|
||
self.status_label.setStyleSheet(get_status_style(True))
|
||
self.qr_label.setText("登录成功!")
|
||
self.log_signal.emit("金山文档登录成功")
|
||
else:
|
||
# 不应该走到这里,因为上面会返回logged_in=True
|
||
pass
|
||
else:
|
||
error = result.get("error", "未知错误") if result else "未知错误"
|
||
self.qr_label.setText(f"失败: {error}")
|
||
self.status_label.setText("❌ 未登录")
|
||
self.status_label.setStyleSheet(get_status_style(False))
|
||
self.log_signal.emit(f"登录失败: {error}")
|
||
|
||
def on_error(error):
|
||
self.get_qr_btn.setEnabled(True)
|
||
self.qr_label.setText(f"错误: {error}")
|
||
self.log_signal.emit(f"获取二维码出错: {error}")
|
||
|
||
self._worker = Worker(get_qr_and_wait_login)
|
||
self._worker.signals.log.connect(self.log_signal.emit)
|
||
self._worker.signals.screenshot_ready.connect(on_qr_ready) # 用于接收二维码
|
||
self._worker.signals.result.connect(on_result)
|
||
self._worker.signals.error.connect(on_error)
|
||
self._worker.start()
|
||
|
||
def _clear_login(self):
|
||
"""Clear login status"""
|
||
from core.kdocs_uploader import get_kdocs_uploader
|
||
uploader = get_kdocs_uploader()
|
||
uploader.clear_login()
|
||
|
||
self.status_label.setText("❌ 未登录")
|
||
self.status_label.setStyleSheet(get_status_style(False))
|
||
self.qr_label.setText("点击获取二维码")
|
||
self.log_signal.emit("金山文档登录状态已清除")
|
||
|
||
if self._login_check_timer:
|
||
self._login_check_timer.stop()
|
||
self._login_check_timer = None
|
||
|
||
def _check_wechat_process(self) -> bool:
|
||
"""检测微信进程是否运行
|
||
|
||
Returns:
|
||
bool: 微信是否在运行
|
||
"""
|
||
import subprocess
|
||
try:
|
||
# Windows下检测微信进程(可能是Weixin.exe或WeChat.exe)
|
||
result = subprocess.run(
|
||
['tasklist'],
|
||
capture_output=True, text=True, timeout=5
|
||
)
|
||
output_lower = result.stdout.lower()
|
||
# 检测多种可能的微信进程名
|
||
is_running = 'weixin.exe' in output_lower or 'wechat.exe' in output_lower
|
||
if is_running:
|
||
self.log_signal.emit("检测到微信进程,可使用快捷登录")
|
||
# 始终显示快捷登录按钮,让用户自己决定
|
||
self.quick_login_btn.setVisible(True)
|
||
return is_running
|
||
except Exception:
|
||
# 检测失败也显示按钮
|
||
self.quick_login_btn.setVisible(True)
|
||
return False
|
||
|
||
def _quick_login(self):
|
||
"""微信快捷登录"""
|
||
# 先检测微信是否运行
|
||
is_wechat_running = self._check_wechat_process()
|
||
if not is_wechat_running:
|
||
self.log_signal.emit("⚠️ 未检测到微信进程,快捷登录可能失败。建议先打开微信客户端。")
|
||
|
||
self.quick_login_btn.setEnabled(False)
|
||
self.quick_login_btn.setText("登录中...")
|
||
self.log_signal.emit("正在执行微信快捷登录...")
|
||
|
||
from utils.worker import Worker
|
||
|
||
def do_quick_login(_signals=None, _should_stop=None):
|
||
from core.kdocs_uploader import get_kdocs_uploader
|
||
import time
|
||
|
||
uploader = get_kdocs_uploader()
|
||
uploader._log_callback = lambda msg: _signals.log.emit(msg) if _signals else None
|
||
|
||
# 强制重新登录,使用快捷登录
|
||
from config import KDOCS_LOGIN_STATE_FILE
|
||
try:
|
||
if KDOCS_LOGIN_STATE_FILE.exists():
|
||
KDOCS_LOGIN_STATE_FILE.unlink()
|
||
except Exception:
|
||
pass
|
||
uploader._cleanup_browser()
|
||
|
||
# 启动浏览器并执行快捷登录
|
||
if not uploader._ensure_playwright(use_storage_state=False):
|
||
return {"success": False, "error": uploader._last_error or "浏览器不可用"}
|
||
|
||
from config import get_config
|
||
config = get_config()
|
||
doc_url = config.kdocs.doc_url.strip()
|
||
if not doc_url:
|
||
return {"success": False, "error": "未配置金山文档链接"}
|
||
|
||
if not uploader._open_document(doc_url):
|
||
return {"success": False, "error": uploader._last_error or "打开文档失败"}
|
||
|
||
# 执行登录流程,优先使用微信快捷登录
|
||
agree_names = ["同意", "同意并继续", "我同意", "确定", "确认"]
|
||
|
||
for loop_idx in range(15):
|
||
current_url = uploader._page.url
|
||
uploader.log(f"[KDocs] 循环{loop_idx+1}: URL={current_url[:60]}...")
|
||
|
||
# 【优先】检查是否有"登录并加入编辑"按钮(可能在任何页面弹出)
|
||
try:
|
||
join_btn = uploader._page.get_by_role("button", name="登录并加入编辑")
|
||
btn_count = join_btn.count()
|
||
if btn_count > 0:
|
||
uploader.log(f"[KDocs] 找到'登录并加入编辑'按钮,直接点击")
|
||
join_btn.first.click(timeout=3000)
|
||
time.sleep(2)
|
||
continue
|
||
except Exception as e:
|
||
uploader.log(f"[KDocs] 点击'登录并加入编辑'失败: {e}")
|
||
|
||
# 检查是否已到达文档页面(且没有登录相关元素)
|
||
if "kdocs.cn/l/" in current_url and "account.wps.cn" not in current_url:
|
||
uploader.log(f"[KDocs] URL是文档页面,检查是否真的登录成功...")
|
||
time.sleep(1) # 等待页面稳定
|
||
|
||
# 检查页面上是否还有登录相关元素
|
||
has_login_elements = False
|
||
|
||
# 检查是否有"立即登录"按钮
|
||
try:
|
||
login_btn = uploader._page.get_by_role("button", name="立即登录")
|
||
if login_btn.count() > 0 and login_btn.first.is_visible(timeout=500):
|
||
uploader.log("[KDocs] 页面上有'立即登录'按钮,未真正登录")
|
||
has_login_elements = True
|
||
except Exception:
|
||
pass
|
||
|
||
# 检查是否有二维码(说明还在登录页)
|
||
if not has_login_elements:
|
||
try:
|
||
qr_text = uploader._page.get_by_text("微信扫码登录", exact=False)
|
||
if qr_text.count() > 0 and qr_text.first.is_visible(timeout=500):
|
||
uploader.log("[KDocs] 页面上有'微信扫码登录',未真正登录")
|
||
has_login_elements = True
|
||
except Exception:
|
||
pass
|
||
|
||
# 检查是否有"微信快捷登录"按钮
|
||
if not has_login_elements:
|
||
try:
|
||
quick_btn = uploader._page.get_by_text("微信快捷登录", exact=False)
|
||
if quick_btn.count() > 0 and quick_btn.first.is_visible(timeout=500):
|
||
uploader.log("[KDocs] 页面上有'微信快捷登录'按钮,未真正登录")
|
||
has_login_elements = True
|
||
except Exception:
|
||
pass
|
||
|
||
if has_login_elements:
|
||
uploader.log("[KDocs] 检测到登录元素,继续处理登录流程...")
|
||
# 不return,继续下面的登录处理
|
||
else:
|
||
uploader.log("[KDocs] 确认登录成功!")
|
||
uploader._logged_in = True
|
||
uploader._save_login_state()
|
||
return {"success": True, "logged_in": True}
|
||
|
||
clicked = False
|
||
|
||
# 点击同意按钮
|
||
for name in agree_names:
|
||
try:
|
||
btn = uploader._page.get_by_role("button", name=name)
|
||
if btn.count() > 0 and btn.first.is_visible(timeout=300):
|
||
uploader.log(f"[KDocs] 点击同意: {name}")
|
||
btn.first.click()
|
||
time.sleep(1)
|
||
clicked = True
|
||
break
|
||
except Exception:
|
||
pass
|
||
if clicked:
|
||
continue
|
||
|
||
# 检查页面上是否有"微信快捷登录"按钮(可能URL是kdocs但内容是登录页)
|
||
has_quick_login_btn = False
|
||
try:
|
||
quick_btn = uploader._page.get_by_text("微信快捷登录", exact=False)
|
||
if quick_btn.count() > 0 and quick_btn.first.is_visible(timeout=300):
|
||
has_quick_login_btn = True
|
||
except Exception:
|
||
pass
|
||
|
||
# 在登录页面,先勾选协议复选框,再点击微信快捷登录
|
||
# 条件:URL是account.wps.cn 或者 页面上有微信快捷登录按钮
|
||
if "account.wps.cn" in current_url or has_quick_login_btn:
|
||
uploader.log("[KDocs] 检测到登录页面,尝试勾选协议...")
|
||
# 1. 先勾选"我已阅读并同意"复选框
|
||
try:
|
||
# 尝试多种方式找到协议复选框
|
||
checkbox_selectors = [
|
||
"input[type='checkbox']",
|
||
"span.checkbox",
|
||
"[class*='checkbox']",
|
||
"[class*='agree']",
|
||
]
|
||
checkbox_clicked = False
|
||
for selector in checkbox_selectors:
|
||
try:
|
||
checkboxes = uploader._page.locator(selector)
|
||
if checkboxes.count() > 0:
|
||
for i in range(min(checkboxes.count(), 3)):
|
||
cb = checkboxes.nth(i)
|
||
if cb.is_visible(timeout=200):
|
||
# 检查是否已勾选
|
||
is_checked = cb.is_checked() if hasattr(cb, 'is_checked') else False
|
||
if not is_checked:
|
||
uploader.log("[KDocs] 勾选协议复选框")
|
||
cb.click()
|
||
time.sleep(0.5)
|
||
checkbox_clicked = True
|
||
break
|
||
if checkbox_clicked:
|
||
break
|
||
except Exception:
|
||
pass
|
||
|
||
# 也尝试点击包含"我已阅读"的文本区域
|
||
if not checkbox_clicked:
|
||
try:
|
||
agree_text = uploader._page.get_by_text("我已阅读并同意", exact=False)
|
||
if agree_text.count() > 0 and agree_text.first.is_visible(timeout=300):
|
||
uploader.log("[KDocs] 点击协议文本区域")
|
||
agree_text.first.click()
|
||
time.sleep(0.5)
|
||
except Exception:
|
||
pass
|
||
except Exception as e:
|
||
uploader.log(f"[KDocs] 勾选协议失败: {e}")
|
||
|
||
# 2. 点击微信快捷登录按钮
|
||
try:
|
||
quick = uploader._page.get_by_text("微信快捷登录", exact=False)
|
||
if quick.count() > 0 and quick.first.is_visible(timeout=500):
|
||
uploader.log("[KDocs] 点击微信快捷登录")
|
||
quick.first.click()
|
||
time.sleep(4) # 等待快捷登录完成
|
||
clicked = True
|
||
continue
|
||
except Exception:
|
||
pass
|
||
|
||
# 点击立即登录
|
||
try:
|
||
btn = uploader._page.get_by_role("button", name="立即登录")
|
||
if btn.count() > 0 and btn.first.is_visible(timeout=500):
|
||
uploader.log("[KDocs] 点击立即登录")
|
||
btn.first.click()
|
||
time.sleep(2)
|
||
clicked = True
|
||
continue
|
||
except Exception:
|
||
pass
|
||
|
||
if not clicked:
|
||
break
|
||
|
||
# 最终检查
|
||
if "kdocs.cn/l/" in uploader._page.url:
|
||
uploader._logged_in = True
|
||
uploader._save_login_state()
|
||
return {"success": True, "logged_in": True}
|
||
else:
|
||
return {"success": False, "error": "快捷登录失败,请尝试扫码登录"}
|
||
|
||
def on_result(result):
|
||
self.quick_login_btn.setEnabled(True)
|
||
self.quick_login_btn.setText("⚡ 微信快捷登录")
|
||
if result and result.get("success") and result.get("logged_in"):
|
||
self.status_label.setText("✅ 已登录")
|
||
self.status_label.setStyleSheet(get_status_style(True))
|
||
self.qr_label.setText("快捷登录成功!")
|
||
self.log_signal.emit("金山文档快捷登录成功")
|
||
else:
|
||
error = result.get("error", "未知错误") if result else "未知错误"
|
||
self.log_signal.emit(f"快捷登录失败: {error}")
|
||
|
||
def on_error(error):
|
||
self.quick_login_btn.setEnabled(True)
|
||
self.quick_login_btn.setText("⚡ 微信快捷登录")
|
||
self.log_signal.emit(f"快捷登录出错: {error}")
|
||
|
||
self._worker = Worker(do_quick_login)
|
||
self._worker.signals.log.connect(self.log_signal.emit)
|
||
self._worker.signals.result.connect(on_result)
|
||
self._worker.signals.error.connect(on_error)
|
||
self._worker.start()
|
||
|