feat(lite): 发布多跳代理跳板 9999091814
This commit is contained in:
+217
-1
@@ -292,6 +292,205 @@ class WhitelistDialog(QDialog):
|
||||
self.accept()
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class JumpServices:
|
||||
"""代理跳板管理所需的节点摘要与配置回调。"""
|
||||
|
||||
load_icon: object
|
||||
load_profiles: object
|
||||
available_nodes: object
|
||||
add_profile: object
|
||||
remove_profile: object
|
||||
|
||||
|
||||
class JumpDialog(QDialog):
|
||||
"""配置最多三跳的节点链路;设置中只保存节点稳定标识。"""
|
||||
|
||||
def __init__(self, services: JumpServices, parent=None):
|
||||
super().__init__(parent)
|
||||
self._services = services
|
||||
self.setWindowTitle("代理跳板")
|
||||
self.setWindowIcon(services.load_icon())
|
||||
self.setFixedSize(560, 520)
|
||||
self.setStyleSheet(f"""
|
||||
QDialog {{ background-color: {C_BG}; }}
|
||||
QLabel {{ color: {C_TEXT}; font-family: "Microsoft YaHei", "Segoe UI", sans-serif; font-size: 12px; }}
|
||||
QListWidget {{ background-color: {C_CARD}; border: 1px solid {C_BORDER}; border-radius: 6px;
|
||||
padding: 4px; color: {C_TEXT}; font-size: 12px; }}
|
||||
QListWidget::item {{ padding: 5px 7px; border-radius: 4px; }}
|
||||
QListWidget::item:selected {{ background-color: #e3e8ee; color: {C_TEXT}; }}
|
||||
QPushButton#btnOk {{ background-color: {C_GREEN}; color: #fff; border: none;
|
||||
border-radius: 6px; padding: 5px 12px; font-size: 12px; font-weight: bold; }}
|
||||
QPushButton#btnOk:hover {{ background-color: #5ed978; }}
|
||||
QPushButton#btnDanger {{ background-color: {C_RED}; color: #fff; border: none;
|
||||
border-radius: 6px; padding: 5px 12px; font-size: 12px; font-weight: bold; }}
|
||||
QPushButton#btnSmall {{ background-color: {C_CARD}; color: {C_SUBTEXT}; border: 1px solid {C_BORDER};
|
||||
border-radius: 6px; padding: 4px 10px; font-size: 11px; }}
|
||||
QPushButton#btnSmall:hover {{ background-color: #e3e8ee; color: {C_TEXT}; }}
|
||||
""")
|
||||
layout = QVBoxLayout(self)
|
||||
layout.setSpacing(8)
|
||||
layout.setContentsMargins(18, 14, 18, 14)
|
||||
|
||||
title = QLabel("代理跳板")
|
||||
title.setStyleSheet(f"color: {C_TITLE}; font-size: 15px; font-weight: bold;")
|
||||
title.setAlignment(Qt.AlignCenter)
|
||||
layout.addWidget(title)
|
||||
hint = QLabel("按顺序加入节点,至少 2 个、最多 3 个;最后一个节点是出口")
|
||||
hint.setStyleSheet(f"color: {C_SUBTEXT}; font-size: 11px;")
|
||||
hint.setAlignment(Qt.AlignCenter)
|
||||
layout.addWidget(hint)
|
||||
|
||||
saved_label = QLabel("已保存跳板")
|
||||
saved_label.setStyleSheet(f"color: {C_SUBTEXT}; font-size: 11px;")
|
||||
layout.addWidget(saved_label)
|
||||
self.profiles = QListWidget()
|
||||
self.profiles.setFixedHeight(68)
|
||||
self.profiles.setToolTip("选择已保存跳板后可删除;新链路在下方配置")
|
||||
layout.addWidget(self.profiles)
|
||||
|
||||
columns = QHBoxLayout()
|
||||
columns.setSpacing(8)
|
||||
left = QVBoxLayout()
|
||||
left.addWidget(QLabel("当前可用节点"))
|
||||
self.available = QListWidget()
|
||||
self.available.setToolTip("选择节点后点击“加入链路”")
|
||||
left.addWidget(self.available, 1)
|
||||
columns.addLayout(left, 1)
|
||||
|
||||
middle = QVBoxLayout()
|
||||
middle.addStretch()
|
||||
self.btn_add_node = QPushButton("加入链路 →")
|
||||
self.btn_add_node.setObjectName("btnSmall")
|
||||
self.btn_add_node.clicked.connect(self._add_node)
|
||||
middle.addWidget(self.btn_add_node)
|
||||
self.btn_remove_node = QPushButton("← 移除")
|
||||
self.btn_remove_node.setObjectName("btnSmall")
|
||||
self.btn_remove_node.clicked.connect(self._remove_node)
|
||||
middle.addWidget(self.btn_remove_node)
|
||||
middle.addStretch()
|
||||
columns.addLayout(middle)
|
||||
|
||||
right = QVBoxLayout()
|
||||
right.addWidget(QLabel("新跳板链路"))
|
||||
self.route = QListWidget()
|
||||
self.route.setToolTip("顺序即连接顺序;可用上下按钮调整")
|
||||
right.addWidget(self.route, 1)
|
||||
reorder = QHBoxLayout()
|
||||
self.btn_up = QPushButton("上移")
|
||||
self.btn_up.setObjectName("btnSmall")
|
||||
self.btn_up.clicked.connect(lambda: self._move_node(-1))
|
||||
reorder.addWidget(self.btn_up)
|
||||
self.btn_down = QPushButton("下移")
|
||||
self.btn_down.setObjectName("btnSmall")
|
||||
self.btn_down.clicked.connect(lambda: self._move_node(1))
|
||||
reorder.addWidget(self.btn_down)
|
||||
right.addLayout(reorder)
|
||||
columns.addLayout(right, 1)
|
||||
layout.addLayout(columns, 1)
|
||||
|
||||
self.lb_status = QLabel("")
|
||||
self.lb_status.setStyleSheet(f"color: {C_SUBTEXT}; font-size: 11px;")
|
||||
self.lb_status.setFixedHeight(16)
|
||||
layout.addWidget(self.lb_status)
|
||||
|
||||
actions = QHBoxLayout()
|
||||
self.btn_add_profile = QPushButton("添加")
|
||||
self.btn_add_profile.setObjectName("btnOk")
|
||||
self.btn_add_profile.clicked.connect(self._add_profile)
|
||||
actions.addWidget(self.btn_add_profile)
|
||||
self.btn_delete_profile = QPushButton("删除选中跳板")
|
||||
self.btn_delete_profile.setObjectName("btnDanger")
|
||||
self.btn_delete_profile.clicked.connect(self._delete_profile)
|
||||
actions.addWidget(self.btn_delete_profile)
|
||||
actions.addStretch()
|
||||
self.btn_close = QPushButton("关闭")
|
||||
self.btn_close.setObjectName("btnSmall")
|
||||
self.btn_close.clicked.connect(self.accept)
|
||||
actions.addWidget(self.btn_close)
|
||||
layout.addLayout(actions)
|
||||
|
||||
self._reload()
|
||||
|
||||
def _reload(self):
|
||||
self.available.clear()
|
||||
for node in self._services.available_nodes():
|
||||
item = QListWidgetItem(node["label"])
|
||||
item.setData(Qt.UserRole, node["ref"])
|
||||
self.available.addItem(item)
|
||||
self.profiles.clear()
|
||||
for profile in self._services.load_profiles():
|
||||
state = "可用" if profile.get("available") else "节点不可用"
|
||||
item = QListWidgetItem(f"{profile['name']} · {profile['route']} · {state}")
|
||||
item.setData(Qt.UserRole, profile["id"])
|
||||
self.profiles.addItem(item)
|
||||
self.route.clear()
|
||||
self.lb_status.setText("")
|
||||
|
||||
def _add_node(self):
|
||||
item = self.available.currentItem()
|
||||
if item is None:
|
||||
self._status("请先选择一个节点", error=True)
|
||||
return
|
||||
ref = item.data(Qt.UserRole)
|
||||
refs = {self.route.item(i).data(Qt.UserRole) for i in range(self.route.count())}
|
||||
if ref in refs:
|
||||
self._status("同一条链路不能重复使用节点", error=True)
|
||||
return
|
||||
if self.route.count() >= 3:
|
||||
self._status("最多只能配置 3 个节点", error=True)
|
||||
return
|
||||
copy = QListWidgetItem(item.text())
|
||||
copy.setData(Qt.UserRole, ref)
|
||||
self.route.addItem(copy)
|
||||
self._status(f"当前链路:{self.route.count()} 个节点")
|
||||
|
||||
def _remove_node(self):
|
||||
item = self.route.currentItem()
|
||||
if item is not None:
|
||||
self.route.takeItem(self.route.row(item))
|
||||
|
||||
def _move_node(self, offset):
|
||||
row = self.route.currentRow()
|
||||
new_row = row + offset
|
||||
if row < 0 or not 0 <= new_row < self.route.count():
|
||||
return
|
||||
item = self.route.takeItem(row)
|
||||
self.route.insertItem(new_row, item)
|
||||
self.route.setCurrentRow(new_row)
|
||||
|
||||
def _add_profile(self):
|
||||
if not 2 <= self.route.count() <= 3:
|
||||
self._status("请先配置 2-3 个节点的链路", error=True)
|
||||
return
|
||||
refs = [self.route.item(i).data(Qt.UserRole) for i in range(self.route.count())]
|
||||
try:
|
||||
self._services.add_profile(refs)
|
||||
except Exception as error:
|
||||
self._status(str(error), error=True)
|
||||
return
|
||||
self._reload()
|
||||
self._status("跳板已添加")
|
||||
|
||||
def _delete_profile(self):
|
||||
item = self.profiles.currentItem()
|
||||
if item is None:
|
||||
self._status("请先选择要删除的跳板", error=True)
|
||||
return
|
||||
try:
|
||||
self._services.remove_profile(item.data(Qt.UserRole))
|
||||
except Exception as error:
|
||||
self._status(str(error), error=True)
|
||||
return
|
||||
self._reload()
|
||||
self._status("跳板已删除")
|
||||
|
||||
def _status(self, message, error=False):
|
||||
self.lb_status.setText(message)
|
||||
color = C_RED if error else C_GREEN
|
||||
self.lb_status.setStyleSheet(f"color: {color}; font-size: 11px;")
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class ThirdPartyServices:
|
||||
"""第三方订阅管理框所需的显式业务服务。"""
|
||||
@@ -475,6 +674,7 @@ class SettingsServices:
|
||||
set_update_via_proxy: object
|
||||
set_skip_update_check: object
|
||||
open_whitelist: object
|
||||
open_jump: object
|
||||
open_third_party: object
|
||||
has_login: object
|
||||
open_login: object
|
||||
@@ -489,7 +689,7 @@ class SettingsDialog(QDialog):
|
||||
self._services = services
|
||||
self.setWindowTitle("设置")
|
||||
self.setWindowIcon(services.load_icon())
|
||||
self.setFixedSize(300, 396)
|
||||
self.setFixedSize(300, 430)
|
||||
self.setStyleSheet(f"""
|
||||
QDialog {{ background-color: {C_BG}; }}
|
||||
QLabel {{ color: {C_TEXT}; font-family: "Microsoft YaHei", "Segoe UI", sans-serif; font-size: 12px; }}
|
||||
@@ -544,6 +744,19 @@ class SettingsDialog(QDialog):
|
||||
whitelist_row.addWidget(self.btn_whitelist)
|
||||
layout.addLayout(whitelist_row)
|
||||
|
||||
jump_row = QHBoxLayout()
|
||||
jump_label = QLabel("代理跳板")
|
||||
jump_label.setStyleSheet(f"color: {C_SUBTEXT}; font-size: 12px;")
|
||||
jump_label.setToolTip("把当前可用节点串成 2-3 跳链路;最后一个节点作为对外出口")
|
||||
jump_row.addWidget(jump_label)
|
||||
jump_row.addStretch()
|
||||
self.btn_jump = QPushButton("添加")
|
||||
self.btn_jump.setCursor(Qt.PointingHandCursor)
|
||||
self.btn_jump.setStyleSheet(self.btn_whitelist.styleSheet())
|
||||
self.btn_jump.clicked.connect(self._open_jump)
|
||||
jump_row.addWidget(self.btn_jump)
|
||||
layout.addLayout(jump_row)
|
||||
|
||||
third_party_row = QHBoxLayout()
|
||||
third_party_label = QLabel("第三方")
|
||||
third_party_label.setStyleSheet(f"color: {C_SUBTEXT}; font-size: 12px;")
|
||||
@@ -636,6 +849,9 @@ class SettingsDialog(QDialog):
|
||||
def _open_whitelist(self):
|
||||
self._services.open_whitelist(self)
|
||||
|
||||
def _open_jump(self):
|
||||
self._services.open_jump(self)
|
||||
|
||||
def _open_third_party(self):
|
||||
self._services.open_third_party(self)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user