|
|
@@ -0,0 +1,463 @@
|
|
|
+#include "onlineuserswidget.h"
|
|
|
+#include <QApplication>
|
|
|
+#include <QDateTime>
|
|
|
+#include <QDebug>
|
|
|
+#include <QJsonDocument>
|
|
|
+
|
|
|
+OnlineUsersWidget::OnlineUsersWidget(QWidget *parent)
|
|
|
+ : QWidget(parent)
|
|
|
+ , m_mainLayout(nullptr)
|
|
|
+ , m_headerLayout(nullptr)
|
|
|
+ , m_titleLabel(nullptr)
|
|
|
+ , m_countLabel(nullptr)
|
|
|
+ , m_refreshButton(nullptr)
|
|
|
+ , m_searchEdit(nullptr)
|
|
|
+ , m_usersTable(nullptr)
|
|
|
+ , m_contextMenu(nullptr)
|
|
|
+ , m_privateMessageAction(nullptr)
|
|
|
+ , m_viewProfileAction(nullptr)
|
|
|
+ , m_kickUserAction(nullptr)
|
|
|
+{
|
|
|
+ setupUI();
|
|
|
+ setupTable();
|
|
|
+ setupContextMenu();
|
|
|
+ applyStyles();
|
|
|
+
|
|
|
+ // 添加一些示例数据
|
|
|
+ addUser(OnlineUser("1", "管理员", "在线"));
|
|
|
+ addUser(OnlineUser("2", "用户A", "在线"));
|
|
|
+ addUser(OnlineUser("3", "用户B", "离开"));
|
|
|
+ addUser(OnlineUser("4", "用户C", "忙碌"));
|
|
|
+}
|
|
|
+
|
|
|
+OnlineUsersWidget::~OnlineUsersWidget()
|
|
|
+{
|
|
|
+}
|
|
|
+
|
|
|
+void OnlineUsersWidget::setupUI()
|
|
|
+{
|
|
|
+ m_mainLayout = new QVBoxLayout(this);
|
|
|
+ m_mainLayout->setContentsMargins(8, 8, 8, 8);
|
|
|
+ m_mainLayout->setSpacing(6);
|
|
|
+
|
|
|
+ // 头部布局
|
|
|
+ m_headerLayout = new QHBoxLayout();
|
|
|
+
|
|
|
+ m_titleLabel = new QLabel("在线用户", this);
|
|
|
+ m_titleLabel->setObjectName("titleLabel");
|
|
|
+
|
|
|
+ m_countLabel = new QLabel("(0)", this);
|
|
|
+ m_countLabel->setObjectName("countLabel");
|
|
|
+
|
|
|
+ m_refreshButton = new QPushButton("刷新", this);
|
|
|
+ m_refreshButton->setObjectName("refreshButton");
|
|
|
+ m_refreshButton->setMaximumWidth(60);
|
|
|
+
|
|
|
+ m_headerLayout->addWidget(m_titleLabel);
|
|
|
+ m_headerLayout->addWidget(m_countLabel);
|
|
|
+ m_headerLayout->addStretch();
|
|
|
+ m_headerLayout->addWidget(m_refreshButton);
|
|
|
+
|
|
|
+ // 搜索框
|
|
|
+ m_searchEdit = new QLineEdit(this);
|
|
|
+ m_searchEdit->setPlaceholderText("搜索用户...");
|
|
|
+ m_searchEdit->setObjectName("searchEdit");
|
|
|
+
|
|
|
+ // 用户表格
|
|
|
+ m_usersTable = new QTableWidget(this);
|
|
|
+ m_usersTable->setObjectName("usersTable");
|
|
|
+
|
|
|
+ // 添加到主布局
|
|
|
+ m_mainLayout->addLayout(m_headerLayout);
|
|
|
+ m_mainLayout->addWidget(m_searchEdit);
|
|
|
+ m_mainLayout->addWidget(m_usersTable);
|
|
|
+
|
|
|
+ // 连接信号
|
|
|
+ connect(m_refreshButton, &QPushButton::clicked, this, &OnlineUsersWidget::onRefreshClicked);
|
|
|
+ connect(m_searchEdit, &QLineEdit::textChanged, this, &OnlineUsersWidget::onSearchTextChanged);
|
|
|
+}
|
|
|
+
|
|
|
+void OnlineUsersWidget::setupTable()
|
|
|
+{
|
|
|
+ // 设置表格列
|
|
|
+ m_usersTable->setColumnCount(3);
|
|
|
+ QStringList headers;
|
|
|
+ headers << "状态" << "用户名" << "最后活动";
|
|
|
+ m_usersTable->setHorizontalHeaderLabels(headers);
|
|
|
+
|
|
|
+ // 设置表格属性
|
|
|
+ m_usersTable->setSelectionBehavior(QAbstractItemView::SelectRows);
|
|
|
+ m_usersTable->setSelectionMode(QAbstractItemView::SingleSelection);
|
|
|
+ m_usersTable->setAlternatingRowColors(true);
|
|
|
+ m_usersTable->setShowGrid(false);
|
|
|
+ m_usersTable->verticalHeader()->setVisible(false);
|
|
|
+
|
|
|
+ // 设置列宽
|
|
|
+ QHeaderView *header = m_usersTable->horizontalHeader();
|
|
|
+ header->setStretchLastSection(true);
|
|
|
+ header->resizeSection(0, 60); // 状态列
|
|
|
+ header->resizeSection(1, 120); // 用户名列
|
|
|
+
|
|
|
+ // 连接信号
|
|
|
+ connect(m_usersTable, &QTableWidget::itemDoubleClicked,
|
|
|
+ [this](QTableWidgetItem *item) {
|
|
|
+ if (item) {
|
|
|
+ onTableDoubleClicked(item->row(), item->column());
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ m_usersTable->setContextMenuPolicy(Qt::CustomContextMenu);
|
|
|
+ connect(m_usersTable, &QTableWidget::customContextMenuRequested,
|
|
|
+ this, &OnlineUsersWidget::onTableContextMenu);
|
|
|
+}
|
|
|
+
|
|
|
+void OnlineUsersWidget::setupContextMenu()
|
|
|
+{
|
|
|
+ m_contextMenu = new QMenu(this);
|
|
|
+
|
|
|
+ m_privateMessageAction = new QAction("发送私信", this);
|
|
|
+ m_viewProfileAction = new QAction("查看资料", this);
|
|
|
+ m_kickUserAction = new QAction("踢出用户", this);
|
|
|
+
|
|
|
+ m_contextMenu->addAction(m_privateMessageAction);
|
|
|
+ m_contextMenu->addAction(m_viewProfileAction);
|
|
|
+ m_contextMenu->addSeparator();
|
|
|
+ m_contextMenu->addAction(m_kickUserAction);
|
|
|
+
|
|
|
+ // 连接信号
|
|
|
+ connect(m_privateMessageAction, &QAction::triggered, [this]() {
|
|
|
+ if (!m_selectedUserId.isEmpty()) {
|
|
|
+ emit privateMessageRequested(m_selectedUserId, m_selectedUsername);
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ connect(m_viewProfileAction, &QAction::triggered, [this]() {
|
|
|
+ if (!m_selectedUserId.isEmpty()) {
|
|
|
+ emit userProfileRequested(m_selectedUserId, m_selectedUsername);
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ connect(m_kickUserAction, &QAction::triggered, [this]() {
|
|
|
+ // TODO: 实现踢出用户功能
|
|
|
+ qDebug() << "踢出用户:" << m_selectedUsername;
|
|
|
+ });
|
|
|
+}
|
|
|
+
|
|
|
+void OnlineUsersWidget::applyStyles()
|
|
|
+{
|
|
|
+ setStyleSheet(
|
|
|
+ "#titleLabel {"
|
|
|
+ " font-size: 14px;"
|
|
|
+ " font-weight: bold;"
|
|
|
+ " color: #2c3e50;"
|
|
|
+ "}"
|
|
|
+
|
|
|
+ "#countLabel {"
|
|
|
+ " font-size: 12px;"
|
|
|
+ " color: #7f8c8d;"
|
|
|
+ " margin-left: 5px;"
|
|
|
+ "}"
|
|
|
+
|
|
|
+ "#refreshButton {"
|
|
|
+ " padding: 4px 8px;"
|
|
|
+ " border: 1px solid #bdc3c7;"
|
|
|
+ " border-radius: 3px;"
|
|
|
+ " background-color: #ecf0f1;"
|
|
|
+ "}"
|
|
|
+
|
|
|
+ "#refreshButton:hover {"
|
|
|
+ " background-color: #d5dbdb;"
|
|
|
+ "}"
|
|
|
+
|
|
|
+ "#refreshButton:pressed {"
|
|
|
+ " background-color: #bdc3c7;"
|
|
|
+ "}"
|
|
|
+
|
|
|
+ "#searchEdit {"
|
|
|
+ " padding: 6px;"
|
|
|
+ " border: 1px solid #bdc3c7;"
|
|
|
+ " border-radius: 3px;"
|
|
|
+ " font-size: 12px;"
|
|
|
+ "}"
|
|
|
+
|
|
|
+ "#usersTable {"
|
|
|
+ " border: 1px solid #bdc3c7;"
|
|
|
+ " border-radius: 3px;"
|
|
|
+ " background-color: white;"
|
|
|
+ " gridline-color: #ecf0f1;"
|
|
|
+ "}"
|
|
|
+
|
|
|
+ "#usersTable::item {"
|
|
|
+ " padding: 8px;"
|
|
|
+ " border-bottom: 1px solid #ecf0f1;"
|
|
|
+ "}"
|
|
|
+
|
|
|
+ "#usersTable::item:selected {"
|
|
|
+ " background-color: #3498db;"
|
|
|
+ " color: white;"
|
|
|
+ "}"
|
|
|
+
|
|
|
+ "#usersTable::item:hover {"
|
|
|
+ " background-color: #ecf0f1;"
|
|
|
+ "}"
|
|
|
+ );
|
|
|
+}
|
|
|
+
|
|
|
+void OnlineUsersWidget::addUser(const OnlineUser &user)
|
|
|
+{
|
|
|
+ // 检查用户是否已存在
|
|
|
+ int existingRow = findUserRow(user.userId);
|
|
|
+ if (existingRow >= 0) {
|
|
|
+ // 更新现有用户
|
|
|
+ m_users[existingRow] = user;
|
|
|
+ updateTableRow(existingRow, user);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 添加新用户
|
|
|
+ m_users.append(user);
|
|
|
+
|
|
|
+ // 如果当前有过滤条件,检查是否匹配
|
|
|
+ if (!m_currentFilter.isEmpty()) {
|
|
|
+ if (!user.username.contains(m_currentFilter, Qt::CaseInsensitive)) {
|
|
|
+ updateUserCountLabel();
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 添加到表格
|
|
|
+ int row = m_usersTable->rowCount();
|
|
|
+ m_usersTable->insertRow(row);
|
|
|
+ updateTableRow(row, user);
|
|
|
+
|
|
|
+ updateUserCountLabel();
|
|
|
+}
|
|
|
+
|
|
|
+void OnlineUsersWidget::removeUser(const QString &userId)
|
|
|
+{
|
|
|
+ int row = findUserRow(userId);
|
|
|
+ if (row >= 0) {
|
|
|
+ m_users.removeAt(row);
|
|
|
+ m_usersTable->removeRow(row);
|
|
|
+ updateUserCountLabel();
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void OnlineUsersWidget::updateUserStatus(const QString &userId, const QString &status)
|
|
|
+{
|
|
|
+ int userIndex = -1;
|
|
|
+ for (int i = 0; i < m_users.size(); ++i) {
|
|
|
+ if (m_users[i].userId == userId) {
|
|
|
+ userIndex = i;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (userIndex >= 0) {
|
|
|
+ m_users[userIndex].status = status;
|
|
|
+ m_users[userIndex].lastSeen = QDateTime::currentDateTime();
|
|
|
+
|
|
|
+ // 更新表格中对应的行
|
|
|
+ int row = findUserRow(userId);
|
|
|
+ if (row >= 0) {
|
|
|
+ updateTableRow(row, m_users[userIndex]);
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void OnlineUsersWidget::updateUserList(const QJsonArray &users)
|
|
|
+{
|
|
|
+ clearUsers();
|
|
|
+
|
|
|
+ for (const auto &value : users) {
|
|
|
+ if (value.isObject()) {
|
|
|
+ QJsonObject userObj = value.toObject();
|
|
|
+ OnlineUser user;
|
|
|
+ user.userId = userObj["id"].toString();
|
|
|
+ user.username = userObj["username"].toString();
|
|
|
+ user.status = userObj["status"].toString("在线");
|
|
|
+ user.isAdmin = userObj["isAdmin"].toBool(false);
|
|
|
+
|
|
|
+ if (userObj.contains("lastSeen")) {
|
|
|
+ user.lastSeen = QDateTime::fromString(userObj["lastSeen"].toString(), Qt::ISODate);
|
|
|
+ } else {
|
|
|
+ user.lastSeen = QDateTime::currentDateTime();
|
|
|
+ }
|
|
|
+
|
|
|
+ addUser(user);
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void OnlineUsersWidget::clearUsers()
|
|
|
+{
|
|
|
+ m_users.clear();
|
|
|
+ m_usersTable->setRowCount(0);
|
|
|
+ updateUserCountLabel();
|
|
|
+}
|
|
|
+
|
|
|
+QList<OnlineUser> OnlineUsersWidget::getAllUsers() const
|
|
|
+{
|
|
|
+ return m_users;
|
|
|
+}
|
|
|
+
|
|
|
+OnlineUser OnlineUsersWidget::getUser(const QString &userId) const
|
|
|
+{
|
|
|
+ for (const auto &user : m_users) {
|
|
|
+ if (user.userId == userId) {
|
|
|
+ return user;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return OnlineUser();
|
|
|
+}
|
|
|
+
|
|
|
+int OnlineUsersWidget::getUserCount() const
|
|
|
+{
|
|
|
+ return m_users.size();
|
|
|
+}
|
|
|
+
|
|
|
+void OnlineUsersWidget::setSearchVisible(bool visible)
|
|
|
+{
|
|
|
+ m_searchEdit->setVisible(visible);
|
|
|
+}
|
|
|
+
|
|
|
+void OnlineUsersWidget::filterUsers(const QString &keyword)
|
|
|
+{
|
|
|
+ m_currentFilter = keyword;
|
|
|
+
|
|
|
+ // 清空表格
|
|
|
+ m_usersTable->setRowCount(0);
|
|
|
+
|
|
|
+ // 重新添加匹配的用户
|
|
|
+ for (const auto &user : m_users) {
|
|
|
+ if (keyword.isEmpty() || user.username.contains(keyword, Qt::CaseInsensitive)) {
|
|
|
+ int row = m_usersTable->rowCount();
|
|
|
+ m_usersTable->insertRow(row);
|
|
|
+ updateTableRow(row, user);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ updateUserCountLabel();
|
|
|
+}
|
|
|
+
|
|
|
+void OnlineUsersWidget::onTableDoubleClicked(int row, int column)
|
|
|
+{
|
|
|
+ Q_UNUSED(column);
|
|
|
+
|
|
|
+ if (row >= 0 && row < m_usersTable->rowCount()) {
|
|
|
+ QTableWidgetItem *userItem = m_usersTable->item(row, 1); // 用户名列
|
|
|
+ if (userItem) {
|
|
|
+ QString username = userItem->text();
|
|
|
+ // 从用户列表中找到对应的userId
|
|
|
+ for (const auto &user : m_users) {
|
|
|
+ if (user.username == username) {
|
|
|
+ emit userDoubleClicked(user.userId, username);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void OnlineUsersWidget::onTableContextMenu(const QPoint &position)
|
|
|
+{
|
|
|
+ QTableWidgetItem *item = m_usersTable->itemAt(position);
|
|
|
+ if (!item) return;
|
|
|
+
|
|
|
+ int row = item->row();
|
|
|
+ QTableWidgetItem *userItem = m_usersTable->item(row, 1);
|
|
|
+ if (!userItem) return;
|
|
|
+
|
|
|
+ QString username = userItem->text();
|
|
|
+ // 从用户列表中找到对应的userId
|
|
|
+ for (const auto &user : m_users) {
|
|
|
+ if (user.username == username) {
|
|
|
+ m_selectedUserId = user.userId;
|
|
|
+ m_selectedUsername = username;
|
|
|
+
|
|
|
+ // 根据用户权限显示/隐藏菜单项
|
|
|
+ m_kickUserAction->setVisible(user.isAdmin); // 示例:只有管理员能踢人
|
|
|
+
|
|
|
+ QPoint globalPos = m_usersTable->mapToGlobal(position);
|
|
|
+ emit userRightClicked(user.userId, username, globalPos);
|
|
|
+ m_contextMenu->exec(globalPos);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void OnlineUsersWidget::onSearchTextChanged(const QString &text)
|
|
|
+{
|
|
|
+ filterUsers(text);
|
|
|
+}
|
|
|
+
|
|
|
+void OnlineUsersWidget::onRefreshClicked()
|
|
|
+{
|
|
|
+ // TODO: 发送刷新用户列表的请求
|
|
|
+ qDebug() << "刷新用户列表";
|
|
|
+}
|
|
|
+
|
|
|
+void OnlineUsersWidget::updateUserCountLabel()
|
|
|
+{
|
|
|
+ int totalUsers = m_users.size();
|
|
|
+ int displayedUsers = m_usersTable->rowCount();
|
|
|
+
|
|
|
+ if (m_currentFilter.isEmpty()) {
|
|
|
+ m_countLabel->setText(QString("(%1)").arg(totalUsers));
|
|
|
+ } else {
|
|
|
+ m_countLabel->setText(QString("(%1/%2)").arg(displayedUsers).arg(totalUsers));
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void OnlineUsersWidget::updateTableRow(int row, const OnlineUser &user)
|
|
|
+{
|
|
|
+ // 状态列
|
|
|
+ QTableWidgetItem *statusItem = new QTableWidgetItem(getStatusIcon(user.status) + " " + user.status);
|
|
|
+ statusItem->setFlags(statusItem->flags() & ~Qt::ItemIsEditable);
|
|
|
+ m_usersTable->setItem(row, 0, statusItem);
|
|
|
+
|
|
|
+ // 用户名列
|
|
|
+ QTableWidgetItem *nameItem = new QTableWidgetItem(user.username);
|
|
|
+ nameItem->setFlags(nameItem->flags() & ~Qt::ItemIsEditable);
|
|
|
+ if (user.isAdmin) {
|
|
|
+ nameItem->setText(user.username + " (管理员)");
|
|
|
+ nameItem->setForeground(QColor("#e74c3c"));
|
|
|
+ }
|
|
|
+ m_usersTable->setItem(row, 1, nameItem);
|
|
|
+
|
|
|
+ // 最后活动时间列
|
|
|
+ QString timeText = user.lastSeen.toString("hh:mm");
|
|
|
+ QTableWidgetItem *timeItem = new QTableWidgetItem(timeText);
|
|
|
+ timeItem->setFlags(timeItem->flags() & ~Qt::ItemIsEditable);
|
|
|
+ m_usersTable->setItem(row, 2, timeItem);
|
|
|
+}
|
|
|
+
|
|
|
+int OnlineUsersWidget::findUserRow(const QString &userId) const
|
|
|
+{
|
|
|
+ for (int i = 0; i < m_users.size(); ++i) {
|
|
|
+ if (m_users[i].userId == userId) {
|
|
|
+ // 在表格中查找对应行
|
|
|
+ for (int row = 0; row < m_usersTable->rowCount(); ++row) {
|
|
|
+ QTableWidgetItem *nameItem = m_usersTable->item(row, 1);
|
|
|
+ if (nameItem && nameItem->text().contains(m_users[i].username)) {
|
|
|
+ return row;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return -1;
|
|
|
+}
|
|
|
+
|
|
|
+QString OnlineUsersWidget::getStatusIcon(const QString &status) const
|
|
|
+{
|
|
|
+ if (status == "在线") {
|
|
|
+ return "🟢";
|
|
|
+ } else if (status == "离开") {
|
|
|
+ return "🟡";
|
|
|
+ } else if (status == "忙碌") {
|
|
|
+ return "🔴";
|
|
|
+ } else {
|
|
|
+ return "⚪";
|
|
|
+ }
|
|
|
+}
|