zhuizhu hace 6 meses
padre
commit
f0cbc344e2
Se han modificado 7 ficheros con 370 adiciones y 123 borrados
  1. 10 0
      MainPanel.cpp
  2. 31 0
      api/roomapi.cpp
  3. 44 0
      api/roomapi.h
  4. 13 6
      network/websocketclient.cpp
  5. 3 0
      network/websocketclient.h
  6. 255 115
      widgets/onlineuserswidget.cpp
  7. 14 2
      widgets/onlineuserswidget.h

+ 10 - 0
MainPanel.cpp

@@ -261,6 +261,11 @@ void MainPanel::connectSignals()
     // 连接WebSocket相关信号
     // 已移除与 UserProfileWidget 相关的在线状态更新
     // connect(webSocketClient, &WebSocketClient::statsUpdate, statsWidget, &StatsWidget::updateStats); // 暂时移除统计在主面板的更新
+    
+    // 连接用户列表更新信号到在线用户widget
+    connect(webSocketClient, &WebSocketClient::userListUpdate, 
+            m_onlineUsersWidget, &OnlineUsersWidget::onUserListUpdate);
+    
     connect(webSocketClient, &WebSocketClient::liveStatus, this, [this](const QString &msg) {
         // 这里可以处理 liveStatus 相关逻辑
         QJsonParseError err;
@@ -324,6 +329,11 @@ void MainPanel::setPushRoomId(const QString &id)
     if (m_chatWindow) {
         m_chatWindow->initWebsocket(id);
     }
+    
+    // 设置在线用户widget的房间ID
+    if (m_onlineUsersWidget) {
+        m_onlineUsersWidget->setCurrentRoomId(id);
+    }
 
     // 若当前是播放器,使用防抖播放
     m_pendingRoomId = id;

+ 31 - 0
api/roomapi.cpp

@@ -52,8 +52,11 @@ void initRoomType()
     QtJsonSerializer::JsonSerializer::registerOptionalConverters<int>();
     QtJsonSerializer::JsonSerializer::registerOptionalConverters<qint64>();
     QtJsonSerializer::JsonSerializer::registerListConverters<RoomInfo>();
+    QtJsonSerializer::JsonSerializer::registerListConverters<RoomUserInfo>();
     qRegisterMetaType<RoomInfo>("RoomInfo");
     qRegisterMetaType<RoomListData>("RoomListData");
+    qRegisterMetaType<RoomUserInfo>("RoomUserInfo");
+    qRegisterMetaType<RoomUsersResp>("RoomUsersResp");
     qRegisterMetaType<std::optional<QString>>("std::optional<QString>");
     qRegisterMetaType<std::optional<int>>("std::optional<int>");
     qRegisterMetaType<std::optional<qint64>>("std::optional<qint64>");
@@ -113,3 +116,31 @@ QFuture<HttpResponse> getRoomApi(const QString &id)
 
     return TC::RequestClient::globalInstance()->postAsync("/room", doc);
 }
+
+RoomUsersResp::RoomUsersResp(const QJsonObject &object)
+{
+    QtJsonSerializer::JsonSerializer serializer;
+    RoomUsersResp roomUsersResp;
+    try {
+        roomUsersResp = serializer.deserialize<RoomUsersResp>(object);
+        data = roomUsersResp.data;
+        total = roomUsersResp.total;
+    } catch (const QtJsonSerializer::Exception &e) {
+        qDebug() << "Deserialization error:" << e.what();
+    }
+}
+
+QList<RoomUserInfo> RoomUsersResp::getRoomUsers() const
+{
+    return data;
+}
+
+QFuture<HttpResponse> getRoomUsersApi(const QString &roomId)
+{
+    QJsonObject jsonData;
+    jsonData["id"] = roomId;
+    qDebug() << jsonData;
+    QJsonDocument doc(jsonData);
+
+    return TC::RequestClient::globalInstance()->postAsync("/room/users", doc);
+}

+ 44 - 0
api/roomapi.h

@@ -48,6 +48,27 @@ public:
 };
 Q_DECLARE_METATYPE(RoomInfo)
 
+class RoomUserInfo
+{
+    Q_GADGET
+    Q_PROPERTY(QString userId MEMBER userId)
+    Q_PROPERTY(QString userName MEMBER userName)
+    Q_PROPERTY(QString clientId MEMBER clientId)
+public:
+    RoomUserInfo() = default;
+
+    QString userId;     // 用户ID
+    QString userName;   // 用户名
+    QString clientId;   // 客户端ID
+
+    friend bool operator==(const RoomUserInfo &lhs, const RoomUserInfo &rhs)
+    {
+        return lhs.userId == rhs.userId && lhs.userName == rhs.userName
+               && lhs.clientId == rhs.clientId;
+    }
+};
+Q_DECLARE_METATYPE(RoomUserInfo)
+
 class RoomListData
 {
     Q_GADGET
@@ -69,10 +90,33 @@ private:
 };
 Q_DECLARE_METATYPE(RoomListData)
 
+class RoomUsersResp
+{
+    Q_GADGET
+    Q_PROPERTY(qint64 total MEMBER total)
+    Q_PROPERTY(QList<RoomUserInfo> data MEMBER data)
+
+public:
+    RoomUsersResp() {}
+    RoomUsersResp(const QJsonObject &object);
+
+    RoomUsersResp(const RoomUsersResp &other) = default;
+    RoomUsersResp &operator=(const RoomUsersResp &other) = default;
+
+    QList<RoomUserInfo> getRoomUsers() const;
+
+private:
+    qint64 total = 0;
+    QList<RoomUserInfo> data;
+};
+Q_DECLARE_METATYPE(RoomUsersResp)
+
 QFuture<HttpResponse> getRoomListApi(const RoomInfo &roomInfo = RoomInfo(),
                                      int page = 1,
                                      int pageSize = 100);
 
 QFuture<HttpResponse> getRoomApi(const QString &id);
 
+QFuture<HttpResponse> getRoomUsersApi(const QString &roomId);
+
 #endif // ROOMAPI_H

+ 13 - 6
network/websocketclient.cpp

@@ -1,16 +1,17 @@
 #include "websocketclient.h"
 
+#include <QBuffer>
+#include <QDir>
+#include <QFile>
+#include <QFileInfo>
+#include <QImageReader>
+#include <QJsonArray>
 #include <QJsonDocument>
 #include <QJsonObject>
 #include <QJsonParseError>
+#include <QStandardPaths>
 #include <QUrl>
 #include <QUrlQuery>
-#include <QFileInfo>
-#include <QBuffer>
-#include <QImageReader>
-#include <QStandardPaths>
-#include <QDir>
-#include <QFile>
 
 #include "networkaccessmanager.h"
 
@@ -258,6 +259,12 @@ void WebSocketClient::onTextMessageReceived(const QString& message)
         } else if (type == "stats_update") {
             emit statsUpdate(obj);
             return;
+        } else if (type == "user_list_update") {
+            // 处理用户列表更新消息
+            const QString roomId = obj["roomId"].toString();
+            const QJsonArray userList = obj["data"].toArray();
+            emit userListUpdate(roomId, userList);
+            return;
         }
 
         // 设置消息类型

+ 3 - 0
network/websocketclient.h

@@ -51,6 +51,9 @@ signals:
     void statsUpdate(const QJsonObject& statsData);
     // 推流通知信号
     void streamNotification(bool show);
+    
+    // 用户列表更新信号
+    void userListUpdate(const QString& roomId, const QJsonArray& userList);
 
     // 连接状态变化信号
     void connectionStateChanged(bool connected);

+ 255 - 115
widgets/onlineuserswidget.cpp

@@ -3,6 +3,12 @@
 #include <QDateTime>
 #include <QDebug>
 #include <QJsonDocument>
+#include "network/networkaccessmanager.h"
+#include "qglobal.h"
+
+#include <qtpromise/qpromise.h>
+#include <qtpromise/qpromisefuture.h>
+#include <qtpromise/qpromisehelpers.h>
 
 OnlineUsersWidget::OnlineUsersWidget(QWidget *parent)
     : QWidget(parent)
@@ -13,6 +19,7 @@ OnlineUsersWidget::OnlineUsersWidget(QWidget *parent)
     , m_refreshButton(nullptr)
     , m_searchEdit(nullptr)
     , m_usersTable(nullptr)
+    , m_emptyStateLabel(nullptr)
     , m_contextMenu(nullptr)
     , m_privateMessageAction(nullptr)
     , m_viewProfileAction(nullptr)
@@ -22,7 +29,7 @@ OnlineUsersWidget::OnlineUsersWidget(QWidget *parent)
     setupTable();
     setupContextMenu();
     applyStyles();
-    
+
     // 添加一些示例数据
     addUser(OnlineUser("1", "管理员", "在线"));
     addUser(OnlineUser("2", "用户A", "在线"));
@@ -30,48 +37,55 @@ OnlineUsersWidget::OnlineUsersWidget(QWidget *parent)
     addUser(OnlineUser("4", "用户C", "忙碌"));
 }
 
-OnlineUsersWidget::~OnlineUsersWidget()
-{
-}
+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_emptyStateLabel = new QLabel(this);
+    m_emptyStateLabel->setObjectName("emptyStateLabel");
+    m_emptyStateLabel->setText("请选择一个房间查看在线用户");
+    m_emptyStateLabel->setAlignment(Qt::AlignCenter);
+    m_emptyStateLabel->setWordWrap(true);
+    m_emptyStateLabel->hide(); // 默认隐藏
+
     // 添加到主布局
     m_mainLayout->addLayout(m_headerLayout);
     m_mainLayout->addWidget(m_searchEdit);
     m_mainLayout->addWidget(m_usersTable);
-    
+    m_mainLayout->addWidget(m_emptyStateLabel);
+
     // 连接信号
     connect(m_refreshButton, &QPushButton::clicked, this, &OnlineUsersWidget::onRefreshClicked);
     connect(m_searchEdit, &QLineEdit::textChanged, this, &OnlineUsersWidget::onSearchTextChanged);
@@ -82,16 +96,16 @@ void OnlineUsersWidget::setupTable()
     // 设置表格列 - 原有3列 + 新增6列
     m_usersTable->setColumnCount(9);
     QStringList headers;
-    headers << "状态" << "用户名" << "最后活动" << "1" << "2" << "3" << "4" << "5" << "6";
+    headers << "状态" << "用户名" << "最后活动" << "0" << "1" << "2" << "3" << "4" << "5" << "6";
     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(false);
@@ -99,49 +113,50 @@ void OnlineUsersWidget::setupTable()
     header->resizeSection(1, 120); // 用户名列
     header->resizeSection(2, 80);  // 最后活动列
     // 设置新增的1-6列宽度
-    for (int i = 3; i < 9; ++i) {
+    for (int i = 3; i < 10; ++i) {
         header->resizeSection(i, 40);
     }
-    
+
     // 连接信号
-    connect(m_usersTable, &QTableWidget::itemDoubleClicked, 
-            [this](QTableWidgetItem *item) {
-                if (item) {
-                    onTableDoubleClicked(item->row(), item->column());
-                }
-            });
-    
+    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);
+    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;
@@ -150,62 +165,55 @@ void OnlineUsersWidget::setupContextMenu()
 
 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;"
-        "}"
-    );
+    // setStyleSheet("#titleLabel {"
+    //               "    font-size: 14px;"
+    //               "    font-weight: bold;"
+    //               "    color: #2c3e50;"
+    //               "}"
+
+    //               "#countLabel {"
+    //               "    font-size: 12px;"
+    //               "    color: #7f8c8d;"
+    //               "    margin-left: 5px;"
+    //               "}"
+
+    //               "#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;"
+    //               "}"
+
+    //               "#emptyStateLabel {"
+    //               "    font-size: 14px;"
+    //               "    color: #7f8c8d;"
+    //               "    padding: 40px 20px;"
+    //               "    background-color: #f8f9fa;"
+    //               "    border: 1px solid #e9ecef;"
+    //               "    border-radius: 6px;"
+    //               "    margin: 20px 0;"
+    //               "}");
 }
 
 void OnlineUsersWidget::addUser(const OnlineUser &user)
@@ -218,10 +226,10 @@ void OnlineUsersWidget::addUser(const OnlineUser &user)
         updateTableRow(existingRow, user);
         return;
     }
-    
+
     // 添加新用户
     m_users.append(user);
-    
+
     // 如果当前有过滤条件,检查是否匹配
     if (!m_currentFilter.isEmpty()) {
         if (!user.username.contains(m_currentFilter, Qt::CaseInsensitive)) {
@@ -229,13 +237,19 @@ void OnlineUsersWidget::addUser(const OnlineUser &user)
             return;
         }
     }
-    
+
     // 添加到表格
     int row = m_usersTable->rowCount();
     m_usersTable->insertRow(row);
     updateTableRow(row, user);
-    
+
     updateUserCountLabel();
+    
+    // 确保表格可见(隐藏空状态)
+    if (m_emptyStateLabel->isVisible()) {
+        m_emptyStateLabel->hide();
+        m_usersTable->show();
+    }
 }
 
 void OnlineUsersWidget::removeUser(const QString &userId)
@@ -257,11 +271,11 @@ void OnlineUsersWidget::updateUserStatus(const QString &userId, const QString &s
             break;
         }
     }
-    
+
     if (userIndex >= 0) {
         m_users[userIndex].status = status;
         m_users[userIndex].lastSeen = QDateTime::currentDateTime();
-        
+
         // 更新表格中对应的行
         int row = findUserRow(userId);
         if (row >= 0) {
@@ -273,7 +287,7 @@ void OnlineUsersWidget::updateUserStatus(const QString &userId, const QString &s
 void OnlineUsersWidget::updateUserList(const QJsonArray &users)
 {
     clearUsers();
-    
+
     for (const auto &value : users) {
         if (value.isObject()) {
             QJsonObject userObj = value.toObject();
@@ -282,13 +296,13 @@ void OnlineUsersWidget::updateUserList(const QJsonArray &users)
             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);
         }
     }
@@ -329,10 +343,10 @@ void OnlineUsersWidget::setSearchVisible(bool 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)) {
@@ -341,14 +355,14 @@ void OnlineUsersWidget::filterUsers(const QString &keyword)
             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) {
@@ -367,22 +381,24 @@ void OnlineUsersWidget::onTableDoubleClicked(int row, int column)
 void OnlineUsersWidget::onTableContextMenu(const QPoint &position)
 {
     QTableWidgetItem *item = m_usersTable->itemAt(position);
-    if (!item) return;
-    
+    if (!item)
+        return;
+
     int row = item->row();
     QTableWidgetItem *userItem = m_usersTable->item(row, 1);
-    if (!userItem) return;
-    
+    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);
@@ -398,15 +414,21 @@ void OnlineUsersWidget::onSearchTextChanged(const QString &text)
 
 void OnlineUsersWidget::onRefreshClicked()
 {
-    // TODO: 发送刷新用户列表的请求
-    qDebug() << "刷新用户列表";
+    if (!m_currentRoomId.isEmpty()) {
+        refreshRoomUsers();
+    } else {
+        // 当没有房间ID时,显示空状态提示
+        clearUsers();
+        updateEmptyState();
+        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 {
@@ -417,10 +439,11 @@ void OnlineUsersWidget::updateUserCountLabel()
 void OnlineUsersWidget::updateTableRow(int row, const OnlineUser &user)
 {
     // 状态列
-    QTableWidgetItem *statusItem = new QTableWidgetItem(getStatusIcon(user.status) + " " + user.status);
+    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);
@@ -429,13 +452,13 @@ void OnlineUsersWidget::updateTableRow(int row, const OnlineUser &user)
         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);
-    
+
     // 添加1-6列的内容
     for (int i = 3; i < 9; ++i) {
         QTableWidgetItem *numberItem = new QTableWidgetItem(QString::number(i - 2));
@@ -473,4 +496,121 @@ QString OnlineUsersWidget::getStatusIcon(const QString &status) const
     } else {
         return "⚪";
     }
-}
+}
+
+void OnlineUsersWidget::updateEmptyState()
+{
+    // 根据当前状态显示相应的空状态提示
+    if (m_currentRoomId.isEmpty()) {
+        m_emptyStateLabel->setText("请选择一个房间查看在线用户");
+        m_emptyStateLabel->show();
+        m_usersTable->hide();
+    } else {
+        m_emptyStateLabel->hide();
+        m_usersTable->show();
+    }
+}
+
+void OnlineUsersWidget::updateUserListFromRoomUsers(const QList<RoomUserInfo> &roomUsers)
+{
+    clearUsers();
+
+    for (const auto &roomUser : roomUsers) {
+        OnlineUser user;
+        user.userId = roomUser.userId;
+        user.username = roomUser.userName;
+        user.status = "在线"; // 房间用户默认为在线状态
+        user.lastSeen = QDateTime::currentDateTime();
+        user.isAdmin = false; // 可以根据需要设置管理员状态
+
+        addUser(user);
+    }
+}
+
+void OnlineUsersWidget::setCurrentRoomId(const QString &roomId)
+{
+    m_currentRoomId = roomId;
+    if (!roomId.isEmpty()) {
+        refreshRoomUsers();
+    } else {
+        // 当房间ID为空时,显示空状态
+        clearUsers();
+        updateEmptyState();
+    }
+}
+
+void OnlineUsersWidget::refreshRoomUsers()
+{
+    if (m_currentRoomId.isEmpty()) {
+        qDebug() << "房间ID为空,无法刷新用户列表";
+        return;
+    }
+
+    qDebug() << "刷新房间用户列表,房间ID:" << m_currentRoomId;
+
+    // 调用API获取房间用户列表
+    QFuture<HttpResponse> future = getRoomUsersApi(m_currentRoomId);
+
+    // 使用QtPromise处理异步响应
+    QtPromise::QPromise<HttpResponse> roomUsersPromise = QtPromise::resolve(future);
+
+    roomUsersPromise
+        .then([this](const HttpResponse &response) {
+            qDebug() << response.rawData;
+            if (response.code == 0) {
+                // 解析响应数据
+
+                qDebug() << "response.data" << response.data;
+                if (response.data.isArray()) {
+                    QJsonArray userArray = response.data.toArray();
+                    
+                    // 直接从数组解析用户数据
+                    QList<RoomUserInfo> roomUsers;
+                    for (const QJsonValue &value : userArray) {
+                        if (value.isObject()) {
+                            QJsonObject userObj = value.toObject();
+                            RoomUserInfo userInfo;
+                            userInfo.userId = userObj["userId"].toString();
+                            userInfo.userName = userObj["userName"].toString();
+                            userInfo.clientId = userObj["clientId"].toString();
+                            roomUsers.append(userInfo);
+                        }
+                    }
+
+                    // 更新用户列表
+                    updateUserListFromRoomUsers(roomUsers);
+
+                    qDebug() << "成功获取房间用户列表,用户数量:" << roomUsers.size();
+                }
+            } else {
+                qDebug() << "获取房间用户列表失败:" << response.message;
+            }
+        })
+        .fail([](const std::exception &e) { qDebug() << "获取房间用户列表异常:" << e.what(); });
+}
+
+void OnlineUsersWidget::onUserListUpdate(const QString &roomId, const QJsonArray &userList)
+{
+    // 检查是否是当前房间的用户列表更新
+    if (roomId != m_currentRoomId) {
+        return;
+    }
+
+    qDebug() << "收到房间用户列表更新消息,房间ID:" << roomId << "用户数量:" << userList.size();
+
+    // 将QJsonArray转换为RoomUserInfo列表
+    QList<RoomUserInfo> roomUsers;
+    for (const QJsonValue &value : userList) {
+        if (value.isObject()) {
+            QJsonObject userObj = value.toObject();
+            RoomUserInfo userInfo;
+            userInfo.userId = userObj["userId"].toString();
+            userInfo.userName = userObj["userName"].toString();
+            userInfo.clientId = userObj["clientId"].toString();
+            roomUsers.append(userInfo);
+        }
+    }
+
+    // 更新用户列表显示
+    updateUserListFromRoomUsers(roomUsers);
+}

+ 14 - 2
widgets/onlineuserswidget.h

@@ -14,6 +14,8 @@
 #include <QTimer>
 #include <QJsonObject>
 #include <QJsonArray>
+#include <QFuture>
+#include "api/roomapi.h"
 
 struct OnlineUser {
     QString userId;
@@ -41,8 +43,13 @@ public:
     void removeUser(const QString &userId);
     void updateUserStatus(const QString &userId, const QString &status);
     void updateUserList(const QJsonArray &users);
+    void updateUserListFromRoomUsers(const QList<RoomUserInfo> &roomUsers);
     void clearUsers();
     
+    // 房间用户管理
+    void setCurrentRoomId(const QString &roomId);
+    void refreshRoomUsers();
+    
     // 获取用户信息
     QList<OnlineUser> getAllUsers() const;
     OnlineUser getUser(const QString &userId) const;
@@ -65,6 +72,8 @@ private slots:
     void onSearchTextChanged(const QString &text);
     void onRefreshClicked();
     void updateUserCountLabel();
+public slots:
+    void onUserListUpdate(const QString& roomId, const QJsonArray& userList);
 
 private:
     void setupUI();
@@ -74,6 +83,7 @@ private:
     void updateTableRow(int row, const OnlineUser &user);
     int findUserRow(const QString &userId) const;
     QString getStatusIcon(const QString &status) const;
+    void updateEmptyState();  // 更新空状态显示
     
     // UI组件
     QVBoxLayout *m_mainLayout;
@@ -83,6 +93,7 @@ private:
     QPushButton *m_refreshButton;
     QLineEdit *m_searchEdit;
     QTableWidget *m_usersTable;
+    QLabel *m_emptyStateLabel;  // 空状态提示标签
     
     // 右键菜单
     QMenu *m_contextMenu;
@@ -93,10 +104,11 @@ private:
     // 数据
     QList<OnlineUser> m_users;
     QString m_currentFilter;
-    
+    QString m_currentRoomId;
+
     // 当前选中的用户
     QString m_selectedUserId;
     QString m_selectedUsername;
 };
 
-#endif // ONLINEUSERSWIDGET_H
+#endif // ONLINEUSERSWIDGET_H