|
|
@@ -0,0 +1,420 @@
|
|
|
+import sys
|
|
|
+import json
|
|
|
+from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QHBoxLayout,
|
|
|
+ QVBoxLayout, QListWidget, QListWidgetItem, QCheckBox,
|
|
|
+ QPushButton, QLineEdit, QTextEdit, QGroupBox,
|
|
|
+ QSplitter, QLabel, QMessageBox, QComboBox, QShortcut)
|
|
|
+from PyQt5.QtCore import Qt
|
|
|
+from PyQt5.QtGui import QKeySequence
|
|
|
+
|
|
|
+class MainWindow(QMainWindow):
|
|
|
+ def __init__(self):
|
|
|
+ super().__init__()
|
|
|
+ self.setWindowTitle("三栏布局 - 用户独立话术管理 (快捷键F1-F5)")
|
|
|
+ self.setGeometry(100, 100, 1300, 750)
|
|
|
+
|
|
|
+ # 数据存储:用户话术字典 {用户名: [话术列表]}
|
|
|
+ self.user_scripts = {}
|
|
|
+ # 当前选中的用户
|
|
|
+ self.current_user = None
|
|
|
+
|
|
|
+ # 中央部件
|
|
|
+ central_widget = QWidget()
|
|
|
+ self.setCentralWidget(central_widget)
|
|
|
+
|
|
|
+ # 主布局 - 水平分割器
|
|
|
+ main_splitter = QSplitter(Qt.Horizontal)
|
|
|
+ central_layout = QHBoxLayout(central_widget)
|
|
|
+ central_layout.addWidget(main_splitter)
|
|
|
+ central_layout.setContentsMargins(10, 10, 10, 10)
|
|
|
+
|
|
|
+ # ---------- 左侧:日志显示 ----------
|
|
|
+ left_widget = QWidget()
|
|
|
+ left_layout = QVBoxLayout(left_widget)
|
|
|
+ left_layout.setContentsMargins(5, 5, 5, 5)
|
|
|
+
|
|
|
+ log_label = QLabel("📋 运行日志")
|
|
|
+ log_label.setStyleSheet("font-weight: bold; font-size: 14px;")
|
|
|
+ left_layout.addWidget(log_label)
|
|
|
+
|
|
|
+ self.log_text = QTextEdit()
|
|
|
+ self.log_text.setReadOnly(True)
|
|
|
+ self.log_text.setPlaceholderText("日志信息将显示在这里...")
|
|
|
+ self.log_text.append("[系统] 程序启动")
|
|
|
+ self.log_text.append("[系统] 界面初始化完成")
|
|
|
+ self.log_text.append("[系统] 快捷键: F1-F5 快速切换用户")
|
|
|
+ left_layout.addWidget(self.log_text)
|
|
|
+
|
|
|
+ log_btn_layout = QHBoxLayout()
|
|
|
+ clear_log_btn = QPushButton("清空日志")
|
|
|
+ clear_log_btn.clicked.connect(self.clear_log)
|
|
|
+ log_btn_layout.addStretch()
|
|
|
+ log_btn_layout.addWidget(clear_log_btn)
|
|
|
+ left_layout.addLayout(log_btn_layout)
|
|
|
+
|
|
|
+ # ---------- 中间:在线用户列表 ----------
|
|
|
+ middle_widget = QWidget()
|
|
|
+ middle_layout = QVBoxLayout(middle_widget)
|
|
|
+ middle_layout.setContentsMargins(5, 5, 5, 5)
|
|
|
+
|
|
|
+ user_label = QLabel("👥 在线用户列表")
|
|
|
+ user_label.setStyleSheet("font-weight: bold; font-size: 14px;")
|
|
|
+ middle_layout.addWidget(user_label)
|
|
|
+
|
|
|
+ select_btn_layout = QHBoxLayout()
|
|
|
+ self.select_all_btn = QPushButton("✅ 全选")
|
|
|
+ self.deselect_all_btn = QPushButton("❌ 取消全选")
|
|
|
+ self.select_all_btn.clicked.connect(self.select_all_users)
|
|
|
+ self.deselect_all_btn.clicked.connect(self.deselect_all_users)
|
|
|
+ select_btn_layout.addWidget(self.select_all_btn)
|
|
|
+ select_btn_layout.addWidget(self.deselect_all_btn)
|
|
|
+ select_btn_layout.addStretch()
|
|
|
+ middle_layout.addLayout(select_btn_layout)
|
|
|
+
|
|
|
+ self.user_list_widget = QListWidget()
|
|
|
+ self.user_list_widget.setSelectionMode(QListWidget.NoSelection)
|
|
|
+ # 添加示例用户
|
|
|
+ sample_users = ["用户A", "用户B", "用户C", "用户D", "用户E"]
|
|
|
+ for user in sample_users:
|
|
|
+ self.add_user_item(user)
|
|
|
+ # 初始化用户话术数据
|
|
|
+ self.user_scripts[user] = [f"话术1: 您好{user},请问有什么可以帮您?",
|
|
|
+ f"话术2: {user},感谢您的反馈,我们会尽快处理。",
|
|
|
+ f"话术3: {user},请您稍等,我为您转接专员。"]
|
|
|
+ middle_layout.addWidget(self.user_list_widget)
|
|
|
+
|
|
|
+ user_count_label = QLabel(f"当前在线人数: {self.user_list_widget.count()}")
|
|
|
+ user_count_label.setStyleSheet("color: gray;")
|
|
|
+ middle_layout.addWidget(user_count_label)
|
|
|
+
|
|
|
+ # ---------- 右侧:用户独立话术列表 ----------
|
|
|
+ right_widget = QWidget()
|
|
|
+ right_layout = QVBoxLayout(right_widget)
|
|
|
+ right_layout.setContentsMargins(5, 5, 5, 5)
|
|
|
+
|
|
|
+ # 顶部:用户选择下拉框
|
|
|
+ user_select_layout = QHBoxLayout()
|
|
|
+ user_select_layout.addWidget(QLabel("选择用户 (快捷键F1-F5):"))
|
|
|
+ self.user_combo = QComboBox()
|
|
|
+ self.user_combo.addItems(sample_users)
|
|
|
+ self.user_combo.currentTextChanged.connect(self.on_user_changed)
|
|
|
+ user_select_layout.addWidget(self.user_combo)
|
|
|
+ user_select_layout.addStretch()
|
|
|
+ right_layout.addLayout(user_select_layout)
|
|
|
+
|
|
|
+ # 话术列表标题
|
|
|
+ script_label = QLabel("📝 话术列表 (当前用户)")
|
|
|
+ script_label.setStyleSheet("font-weight: bold; font-size: 14px;")
|
|
|
+ right_layout.addWidget(script_label)
|
|
|
+
|
|
|
+ # 话术列表显示
|
|
|
+ self.script_list_widget = QListWidget()
|
|
|
+ right_layout.addWidget(self.script_list_widget)
|
|
|
+
|
|
|
+ # 话术编辑区域
|
|
|
+ script_edit_layout = QVBoxLayout()
|
|
|
+
|
|
|
+ # 添加话术
|
|
|
+ add_layout = QHBoxLayout()
|
|
|
+ self.script_input = QLineEdit()
|
|
|
+ self.script_input.setPlaceholderText("输入新话术内容...")
|
|
|
+ add_btn = QPushButton("➕ 添加")
|
|
|
+ add_btn.clicked.connect(self.add_script)
|
|
|
+ add_layout.addWidget(self.script_input)
|
|
|
+ add_layout.addWidget(add_btn)
|
|
|
+ script_edit_layout.addLayout(add_layout)
|
|
|
+
|
|
|
+ # 删除和修改按钮
|
|
|
+ modify_layout = QHBoxLayout()
|
|
|
+ delete_btn = QPushButton("🗑️ 删除选中")
|
|
|
+ delete_btn.clicked.connect(self.delete_script)
|
|
|
+ modify_btn = QPushButton("✏️ 修改选中")
|
|
|
+ modify_btn.clicked.connect(self.modify_script)
|
|
|
+ modify_layout.addWidget(delete_btn)
|
|
|
+ modify_layout.addWidget(modify_btn)
|
|
|
+ script_edit_layout.addLayout(modify_layout)
|
|
|
+
|
|
|
+ # 保存按钮
|
|
|
+ save_layout = QHBoxLayout()
|
|
|
+ save_layout.addStretch()
|
|
|
+ self.save_scripts_btn = QPushButton("💾 保存话术配置")
|
|
|
+ self.save_scripts_btn.setStyleSheet("background-color: #2196F3; color: white; font-weight: bold; padding: 8px 20px; border-radius: 5px;")
|
|
|
+ self.save_scripts_btn.clicked.connect(self.save_user_scripts)
|
|
|
+ save_layout.addWidget(self.save_scripts_btn)
|
|
|
+ script_edit_layout.addLayout(save_layout)
|
|
|
+
|
|
|
+ right_layout.addLayout(script_edit_layout)
|
|
|
+
|
|
|
+ # 发送任务按钮
|
|
|
+ send_layout = QHBoxLayout()
|
|
|
+ send_layout.addStretch()
|
|
|
+ self.send_task_btn = QPushButton("🚀 发送任务")
|
|
|
+ self.send_task_btn.setStyleSheet("background-color: #4CAF50; color: white; font-weight: bold; padding: 8px 20px; border-radius: 5px;")
|
|
|
+ self.send_task_btn.clicked.connect(self.send_task)
|
|
|
+ send_layout.addWidget(self.send_task_btn)
|
|
|
+ right_layout.addLayout(send_layout)
|
|
|
+
|
|
|
+ # ---------- 将三个部件加入分割器 ----------
|
|
|
+ main_splitter.addWidget(left_widget)
|
|
|
+ main_splitter.addWidget(middle_widget)
|
|
|
+ main_splitter.addWidget(right_widget)
|
|
|
+ main_splitter.setSizes([300, 450, 450])
|
|
|
+
|
|
|
+ # 初始化显示第一个用户的话术
|
|
|
+ if sample_users:
|
|
|
+ self.current_user = sample_users[0]
|
|
|
+ self.load_user_scripts(self.current_user)
|
|
|
+
|
|
|
+ # ---------- 设置快捷键 F1-F5 ----------
|
|
|
+ self.setup_shortcuts()
|
|
|
+
|
|
|
+ # 窗口样式
|
|
|
+ self.setStyleSheet("""
|
|
|
+ QGroupBox {
|
|
|
+ font-weight: bold;
|
|
|
+ border: 1px solid #ccc;
|
|
|
+ border-radius: 5px;
|
|
|
+ margin-top: 10px;
|
|
|
+ }
|
|
|
+ QGroupBox::title {
|
|
|
+ subcontrol-origin: margin;
|
|
|
+ left: 10px;
|
|
|
+ padding: 0 5px 0 5px;
|
|
|
+ }
|
|
|
+ QListWidget::item {
|
|
|
+ padding: 5px;
|
|
|
+ }
|
|
|
+ QTextEdit {
|
|
|
+ background-color: #f8f8f8;
|
|
|
+ font-family: Consolas, monospace;
|
|
|
+ }
|
|
|
+ QComboBox {
|
|
|
+ padding: 5px;
|
|
|
+ min-width: 150px;
|
|
|
+ }
|
|
|
+ """)
|
|
|
+
|
|
|
+ # ---------- 快捷键设置 ----------
|
|
|
+ def setup_shortcuts(self):
|
|
|
+ """设置F1-F5快捷键切换用户"""
|
|
|
+ # F1 选择第一个用户,F2 第二个,以此类推
|
|
|
+ shortcuts = [
|
|
|
+ (Qt.Key_F1, 0),
|
|
|
+ (Qt.Key_F2, 1),
|
|
|
+ (Qt.Key_F3, 2),
|
|
|
+ (Qt.Key_F4, 3),
|
|
|
+ (Qt.Key_F5, 4),
|
|
|
+ ]
|
|
|
+
|
|
|
+ for key, index in shortcuts:
|
|
|
+ shortcut = QShortcut(QKeySequence(key), self)
|
|
|
+ # 使用lambda捕获index,并绑定到切换函数
|
|
|
+ shortcut.activated.connect(lambda idx=index: self.switch_to_user_by_index(idx))
|
|
|
+
|
|
|
+ def switch_to_user_by_index(self, index):
|
|
|
+ """根据索引切换到对应用户"""
|
|
|
+ if index < self.user_combo.count():
|
|
|
+ user = self.user_combo.itemText(index)
|
|
|
+ self.user_combo.setCurrentText(user)
|
|
|
+ self.log_text.append(f"[快捷键] 切换到用户: {user}")
|
|
|
+ else:
|
|
|
+ self.log_text.append(f"[快捷键] 用户索引 {index} 不存在")
|
|
|
+
|
|
|
+ # ---------- 用户管理 ----------
|
|
|
+ def add_user_item(self, username):
|
|
|
+ """添加一个带复选框的用户项"""
|
|
|
+ item = QListWidgetItem()
|
|
|
+ checkbox = QCheckBox(username)
|
|
|
+ self.user_list_widget.addItem(item)
|
|
|
+ self.user_list_widget.setItemWidget(item, checkbox)
|
|
|
+ checkbox.stateChanged.connect(self.on_user_check_changed)
|
|
|
+
|
|
|
+ def get_user_checkboxes(self):
|
|
|
+ """获取所有用户复选框"""
|
|
|
+ checkboxes = []
|
|
|
+ for i in range(self.user_list_widget.count()):
|
|
|
+ item = self.user_list_widget.item(i)
|
|
|
+ widget = self.user_list_widget.itemWidget(item)
|
|
|
+ if isinstance(widget, QCheckBox):
|
|
|
+ checkboxes.append(widget)
|
|
|
+ return checkboxes
|
|
|
+
|
|
|
+ def get_selected_users(self):
|
|
|
+ """获取所有选中的用户"""
|
|
|
+ selected = []
|
|
|
+ for cb in self.get_user_checkboxes():
|
|
|
+ if cb.isChecked():
|
|
|
+ selected.append(cb.text())
|
|
|
+ return selected
|
|
|
+
|
|
|
+ def select_all_users(self):
|
|
|
+ for cb in self.get_user_checkboxes():
|
|
|
+ cb.setChecked(True)
|
|
|
+ self.log_text.append("[操作] 已全选所有用户")
|
|
|
+
|
|
|
+ def deselect_all_users(self):
|
|
|
+ for cb in self.get_user_checkboxes():
|
|
|
+ cb.setChecked(False)
|
|
|
+ self.log_text.append("[操作] 已取消全选所有用户")
|
|
|
+
|
|
|
+ def on_user_check_changed(self):
|
|
|
+ """复选框状态改变时更新下拉框列表"""
|
|
|
+ # 获取当前所有用户
|
|
|
+ current_users = []
|
|
|
+ for i in range(self.user_list_widget.count()):
|
|
|
+ item = self.user_list_widget.item(i)
|
|
|
+ widget = self.user_list_widget.itemWidget(item)
|
|
|
+ if isinstance(widget, QCheckBox):
|
|
|
+ current_users.append(widget.text())
|
|
|
+
|
|
|
+ # 更新下拉框
|
|
|
+ current_selection = self.user_combo.currentText()
|
|
|
+ self.user_combo.clear()
|
|
|
+ self.user_combo.addItems(current_users)
|
|
|
+
|
|
|
+ # 如果之前的用户还在列表中,恢复选择
|
|
|
+ if current_selection in current_users:
|
|
|
+ self.user_combo.setCurrentText(current_selection)
|
|
|
+ elif current_users:
|
|
|
+ self.user_combo.setCurrentIndex(0)
|
|
|
+
|
|
|
+ # ---------- 话术管理 (用户独立) ----------
|
|
|
+ def load_user_scripts(self, username):
|
|
|
+ """加载指定用户的话术列表"""
|
|
|
+ if username in self.user_scripts:
|
|
|
+ self.script_list_widget.clear()
|
|
|
+ for script in self.user_scripts[username]:
|
|
|
+ self.script_list_widget.addItem(script)
|
|
|
+ self.current_user = username
|
|
|
+ self.log_text.append(f"[系统] 加载用户 '{username}' 的话术列表")
|
|
|
+
|
|
|
+ def on_user_changed(self, username):
|
|
|
+ """下拉框切换用户"""
|
|
|
+ if username and username != self.current_user:
|
|
|
+ # 如果当前用户有修改但未保存,先保存
|
|
|
+ if self.current_user and self.current_user in self.user_scripts:
|
|
|
+ # 自动保存当前用户的话术
|
|
|
+ self.save_current_user_scripts()
|
|
|
+
|
|
|
+ self.load_user_scripts(username)
|
|
|
+
|
|
|
+ def save_current_user_scripts(self):
|
|
|
+ """保存当前用户的话术到内存"""
|
|
|
+ if self.current_user and self.current_user in self.user_scripts:
|
|
|
+ scripts = []
|
|
|
+ for i in range(self.script_list_widget.count()):
|
|
|
+ scripts.append(self.script_list_widget.item(i).text())
|
|
|
+ self.user_scripts[self.current_user] = scripts
|
|
|
+ self.log_text.append(f"[系统] 自动保存用户 '{self.current_user}' 的话术配置")
|
|
|
+
|
|
|
+ def save_user_scripts(self):
|
|
|
+ """手动保存当前用户的话术(并持久化)"""
|
|
|
+ if not self.current_user:
|
|
|
+ QMessageBox.warning(self, "提示", "请先选择一个用户!")
|
|
|
+ return
|
|
|
+
|
|
|
+ # 保存到内存
|
|
|
+ self.save_current_user_scripts()
|
|
|
+
|
|
|
+ # 持久化保存到文件
|
|
|
+ try:
|
|
|
+ with open("user_scripts_backup.json", "w", encoding="utf-8") as f:
|
|
|
+ json.dump(self.user_scripts, f, ensure_ascii=False, indent=2)
|
|
|
+ self.log_text.append(f"[系统] 话术配置已保存到文件 user_scripts_backup.json")
|
|
|
+ QMessageBox.information(self, "保存成功", f"用户 '{self.current_user}' 的话术配置已保存!")
|
|
|
+ except Exception as e:
|
|
|
+ QMessageBox.critical(self, "保存失败", f"保存到文件时出错: {str(e)}")
|
|
|
+
|
|
|
+ def add_script(self):
|
|
|
+ """添加话术到当前用户"""
|
|
|
+ text = self.script_input.text().strip()
|
|
|
+ if text:
|
|
|
+ self.script_list_widget.addItem(text)
|
|
|
+ self.script_input.clear()
|
|
|
+ # 自动保存
|
|
|
+ self.save_current_user_scripts()
|
|
|
+ self.log_text.append(f"[操作] 为用户 '{self.current_user}' 添加话术: {text}")
|
|
|
+ else:
|
|
|
+ QMessageBox.information(self, "提示", "话术内容不能为空!")
|
|
|
+
|
|
|
+ def delete_script(self):
|
|
|
+ """删除当前用户选中的话术"""
|
|
|
+ current_row = self.script_list_widget.currentRow()
|
|
|
+ if current_row >= 0:
|
|
|
+ item_text = self.script_list_widget.item(current_row).text()
|
|
|
+ self.script_list_widget.takeItem(current_row)
|
|
|
+ # 自动保存
|
|
|
+ self.save_current_user_scripts()
|
|
|
+ self.log_text.append(f"[操作] 删除用户 '{self.current_user}' 的话术: {item_text}")
|
|
|
+ else:
|
|
|
+ QMessageBox.information(self, "提示", "请先选中一条话术!")
|
|
|
+
|
|
|
+ def modify_script(self):
|
|
|
+ """修改当前用户选中的话术"""
|
|
|
+ current_row = self.script_list_widget.currentRow()
|
|
|
+ if current_row >= 0:
|
|
|
+ old_text = self.script_list_widget.item(current_row).text()
|
|
|
+ new_text, ok = QMessageBox.getText(self, "修改话术", "请输入新的话术内容:",
|
|
|
+ text=old_text)
|
|
|
+ if ok and new_text.strip():
|
|
|
+ self.script_list_widget.item(current_row).setText(new_text.strip())
|
|
|
+ # 自动保存
|
|
|
+ self.save_current_user_scripts()
|
|
|
+ self.log_text.append(f"[操作] 修改用户 '{self.current_user}' 的话术: {old_text} -> {new_text.strip()}")
|
|
|
+ elif ok and not new_text.strip():
|
|
|
+ QMessageBox.information(self, "提示", "话术内容不能为空!")
|
|
|
+ else:
|
|
|
+ QMessageBox.information(self, "提示", "请先选中一条话术!")
|
|
|
+
|
|
|
+ def clear_log(self):
|
|
|
+ self.log_text.clear()
|
|
|
+ self.log_text.append("[系统] 日志已清空")
|
|
|
+ self.log_text.append("[系统] 快捷键: F1-F5 快速切换用户")
|
|
|
+
|
|
|
+ # ---------- 发送任务 ----------
|
|
|
+ def send_task(self):
|
|
|
+ """发送任务到选中的用户"""
|
|
|
+ selected_users = self.get_selected_users()
|
|
|
+
|
|
|
+ if not selected_users:
|
|
|
+ QMessageBox.warning(self, "提示", "请至少选择一个用户!")
|
|
|
+ return
|
|
|
+
|
|
|
+ # 获取每个用户选中的话术 (这里演示:取每个用户的第一条话术)
|
|
|
+ task_details = []
|
|
|
+ for user in selected_users:
|
|
|
+ if user in self.user_scripts and self.user_scripts[user]:
|
|
|
+ script = self.user_scripts[user][0] # 取第一条
|
|
|
+ task_details.append(f"{user}: {script}")
|
|
|
+ else:
|
|
|
+ task_details.append(f"{user}: (无话术)")
|
|
|
+
|
|
|
+ # 记录日志
|
|
|
+ self.log_text.append(f"[任务] 发送给 {len(selected_users)} 个用户:")
|
|
|
+ for detail in task_details:
|
|
|
+ self.log_text.append(f" - {detail}")
|
|
|
+ self.log_text.append("[任务] 发送完成 (功能待实现)")
|
|
|
+
|
|
|
+ # 显示详情
|
|
|
+ msg = "\n".join(task_details)
|
|
|
+ QMessageBox.information(self, "发送任务",
|
|
|
+ f"已发送任务给 {len(selected_users)} 个用户:\n\n{msg}")
|
|
|
+
|
|
|
+ # ---------- 窗口关闭时保存 ----------
|
|
|
+ def closeEvent(self, event):
|
|
|
+ """关闭窗口时自动保存所有用户话术"""
|
|
|
+ if self.current_user:
|
|
|
+ self.save_current_user_scripts()
|
|
|
+ try:
|
|
|
+ with open("user_scripts_backup.json", "w", encoding="utf-8") as f:
|
|
|
+ json.dump(self.user_scripts, f, ensure_ascii=False, indent=2)
|
|
|
+ self.log_text.append("[系统] 程序退出,已自动保存所有话术配置")
|
|
|
+ except:
|
|
|
+ pass
|
|
|
+ event.accept()
|
|
|
+
|
|
|
+if __name__ == "__main__":
|
|
|
+ app = QApplication(sys.argv)
|
|
|
+ window = MainWindow()
|
|
|
+ window.show()
|
|
|
+ sys.exit(app.exec_())
|