Procházet zdrojové kódy

添加 新的界面 去适配房间选择

zhuizhu před 7 měsíci
rodič
revize
5120105e8d

+ 20 - 5
AvPlayer2/PlayWidget.cpp

@@ -52,20 +52,35 @@ void PlayWidget::setupUi()
     m_labelVolume = new QLabel("音量", this);
     QHBoxLayout* controlLayout = new QHBoxLayout;
     controlLayout->addWidget(m_btnPlayPause);
-    controlLayout->addWidget(new QLabel("倍速:"));
+    QLabel* speedLabel = new QLabel("倍速:", this);
+    controlLayout->addWidget(speedLabel);
     controlLayout->addWidget(m_comboSpeed);
     controlLayout->addWidget(m_labelVolume);
     controlLayout->addWidget(m_sliderVolume);
     m_audioEffect = std::make_unique<AudioEffectGL>(this);
     QVBoxLayout* mainLayout = new QVBoxLayout(this);
-    mainLayout->addWidget(m_videoWidget.get(), 5);
+    mainLayout->addWidget(m_videoWidget.get(), 100);  // 增加拉伸因子,让视频区域占据更多空间
     QHBoxLayout* progressLayout = new QHBoxLayout;
     progressLayout->addWidget(m_sliderProgress, 8);
     progressLayout->addWidget(m_labelTime, 2);
-    mainLayout->addLayout(progressLayout);
-    mainLayout->addLayout(controlLayout);
-    mainLayout->addWidget(m_audioEffect.get(), 2);
+    mainLayout->addLayout(progressLayout, 0);  // 设置为0,不占用额外空间
+    mainLayout->addLayout(controlLayout, 0);   // 设置为0,不占用额外空间
+    mainLayout->addWidget(m_audioEffect.get(), 0);  // 设置为0,不占用额外空间
     setLayout(mainLayout);
+    
+    // 设置视频组件的最小尺寸策略,确保它能够正常显示
+    m_videoWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+    m_videoWidget->setMinimumSize(320, 240);  // 设置最小尺寸
+    
+    // 隐藏除了m_videoWidget之外的所有UI组件
+    m_sliderProgress->hide();
+    m_labelTime->hide();
+    m_btnPlayPause->hide();
+    speedLabel->hide();
+    m_comboSpeed->hide();
+    m_sliderVolume->hide();
+    m_labelVolume->hide();
+    m_audioEffect->hide();
 }
 
 void PlayWidget::setupConnections()

+ 6 - 0
LearningSmartClient.pro

@@ -39,8 +39,11 @@ SOURCES += \
     widgets/chatView/chat1/chatview.cpp \
     widgets/chatView/chatwindow.cpp \
     widgets/colorlistwidget.cpp \
+    widgets/createmeetingdialog.cpp \
     widgets/framelessbase.cpp \
+    widgets/joinmeetingdialog.cpp \
     widgets/maskoverlay.cpp \
+    widgets/meetingselectionwidget.cpp \
     widgets/userprofilewidget.cpp
 
 HEADERS += \
@@ -72,8 +75,11 @@ HEADERS += \
     widgets/chatView/chat1/chatview.h \
     widgets/chatView/chatwindow.h \
     widgets/colorlistwidget.h \
+    widgets/createmeetingdialog.h \
     widgets/framelessbase.h \
+    widgets/joinmeetingdialog.h \
     widgets/maskoverlay.h \
+    widgets/meetingselectionwidget.h \
     widgets/userprofilewidget.h
 
 msvc {

+ 5 - 9
MainPanel.cpp

@@ -115,14 +115,7 @@ void MainPanel::setRole(const QStringList &roleList)
     }
     setPlayerWidget(newPlayer);
 
-    // 如果是管理也就是教师 直接过滤当前教师的选项
-    if (AppEvent::instance()->hasRole("role.admin")) {
-        // roomInfo.ownerId = AppEvent::instance()->userId();
-        m_roomListWidget->hide();
-
-    } else {
-        m_roomListWidget->show();
-    }
+    m_roomListWidget->hide();
 
     // 设置初始化信息
     const QString &name = AppEvent::instance()->userName();
@@ -188,7 +181,10 @@ void MainPanel::setPlayerWidget(QWidget *newPlayer)
     }
     QVBoxLayout *vbox = new QVBoxLayout(playerContainer);
     vbox->setContentsMargins(0, 0, 0, 0);
-    vbox->addWidget(playerWidget);
+    vbox->addWidget(playerWidget, 1);  // 添加拉伸因子,让播放器组件占据所有可用空间
+    
+    // 确保播放器组件能够正确拉伸
+    playerWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
 }
 
 void MainPanel::roomItemChanged(QListWidgetItem *item)

+ 216 - 13
mainwindow.cpp

@@ -26,6 +26,9 @@
 #include "widgets/framelessbase.h"
 #include "widgets/maskoverlay.h"
 #include "widgets/userprofilewidget.h"
+#include "widgets/meetingselectionwidget.h"
+#include "widgets/joinmeetingdialog.h"
+#include "widgets/createmeetingdialog.h"
 
 #include <QDebug>
 #include <QFutureWatcher>
@@ -62,9 +65,17 @@ MainWindow::MainWindow(QWidget *parent)
     loginWidget = new LoginWidget(this);
     stackedWidget->addWidget(loginWidget);
 
+    // 创建会议选择页面
+    meetingSelectionWidget = new MeetingSelectionWidget(this);
+    stackedWidget->addWidget(meetingSelectionWidget);
+
     mainWidget = nullptr;
 
     connect(loginWidget, &LoginWidget::loginRequested, this, &MainWindow::onLoginSuccess);
+    connect(meetingSelectionWidget, &MeetingSelectionWidget::joinMeetingRequested, this, &MainWindow::onJoinMeetingRequested);
+    connect(meetingSelectionWidget, &MeetingSelectionWidget::createMeetingRequested, this, &MainWindow::onCreateMeetingRequested);
+    connect(meetingSelectionWidget, &MeetingSelectionWidget::logoutRequested, this, &MainWindow::onMeetingSelectionLogout);
+    
     resize(1280, 600);
 
     // 显示登录页面
@@ -168,15 +179,8 @@ void MainWindow::authLogin()
             AppEvent::instance()->setUserId(user.id);
             AppEvent::instance()->setUserName(user.username);
 
-            // 创建主界面部件
-            createMainWindow();
-
-            mainWidget->setRole(user.roleName);
-
-            stackedWidget->addWidget(mainWidget);
-            stackedWidget->setCurrentWidget(mainWidget);
-
-            checkRoom();
+            // 显示会议选择页面
+            showMeetingSelection();
         })
         .fail([this](const std::exception &e) {
             // 处理错误
@@ -191,11 +195,169 @@ void MainWindow::authLogin()
         });
 }
 
-void MainWindow::checkRoom()
+void MainWindow::showMeetingSelection()
 {
-    // 创建两个异步操作的Promise
+    // 设置会议选择页面的用户信息
+    AppEvent *appEvent = AppEvent::instance();
+    meetingSelectionWidget->setUserRoles(appEvent->roles());
+    meetingSelectionWidget->setUserInfo(appEvent->userName(), appEvent->userId());
+    
+    // 显示会议选择页面
+    stackedWidget->setCurrentWidget(meetingSelectionWidget);
+}
+
+void MainWindow::onJoinMeetingRequested()
+{
+    // 创建加入会议对话框
+    JoinMeetingDialog *joinDialog = new JoinMeetingDialog(this);
+    
+    // 获取会议列表并设置到对话框
     RoomInfo roomInfo;
+    roomInfo.id = "";
+    QFuture<HttpResponse> getRoomListFuture = getRoomListApi(roomInfo);
+    
+    // 使用QtPromise处理异步获取会议列表
+    QtPromise::QPromise<HttpResponse> roomListPromise = QtPromise::resolve(getRoomListFuture);
+    
+    roomListPromise
+        .then([this, joinDialog](const HttpResponse &response) {
+            qDebug() << "获取会议列表:" << response.data << response.rawData << response.message;
+            
+            if (response.code == 0) {
+                RoomListData roomListData(response.data.toObject());
+                QList<RoomInfo> roomInfos = roomListData.getRooms();
+                
+                QStringList meetingIds;
+                QStringList meetingNames;
+                
+                // 提取会议ID和名称
+                for (const RoomInfo &room : roomInfos) {
+                    if (room.id.has_value() && room.name.has_value()) {
+                        meetingIds.append(room.id.value());
+                        meetingNames.append(room.name.value());
+                    }
+                }
+                
+                // 设置可用的会议列表
+                joinDialog->setAvailableMeetings(meetingIds, meetingNames);
+            } else {
+                qWarning() << "获取会议列表失败:" << response.message;
+                BubbleTip::showTip(this,
+                                   QString("获取会议列表失败: %1").arg(response.message),
+                                   BubbleTip::Top,
+                                   3000,
+                                   BubbleTip::Type::Error);
+            }
+            
+            // 显示对话框
+            if (joinDialog->exec() == QDialog::Accepted) {
+                QString meetingId = joinDialog->getSelectedMeetingId();
+                
+                // 创建主界面部件(用于加入会议)
+                createMainWindow();
+
+                // 加入会议时,强制使用PlayWidget(观看者模式),无论用户角色如何
+                QStringList joinRoles = {"role.viewer"}; // 强制设置为观看者角色
+                mainWidget->setRole(joinRoles);
+                
+                // 显示主界面
+                stackedWidget->setCurrentWidget(mainWidget);
+                
+                // 检查房间状态并验证会议ID是否存在
+                checkRoom(meetingId);
+            }
+            
+            joinDialog->deleteLater();
+        })
+        .fail([this, joinDialog](const std::exception &e) {
+            qWarning() << "获取会议列表异常:" << e.what();
+            BubbleTip::showTip(this,
+                               QString("获取会议列表失败: %1").arg(e.what()),
+                               BubbleTip::Top,
+                               3000,
+                               BubbleTip::Type::Error);
+            
+            // 即使获取失败也显示对话框,允许手动输入
+            if (joinDialog->exec() == QDialog::Accepted) {
+                QString meetingId = joinDialog->getSelectedMeetingId();
+                
+                // 创建主界面部件(用于加入会议)
+                createMainWindow();
+
+                // 加入会议时,强制使用PlayWidget(观看者模式),无论用户角色如何
+                QStringList joinRoles = {"role.viewer"}; // 强制设置为观看者角色
+                mainWidget->setRole(joinRoles);
+                
+                // 显示主界面
+                stackedWidget->setCurrentWidget(mainWidget);
+                
+                // 检查房间状态并验证会议ID是否存在
+                checkRoom(meetingId);
+            }
+            
+            joinDialog->deleteLater();
+        });
+}
+
+void MainWindow::onCreateMeetingRequested()
+{
+    // 创建创建会议对话框
+    // CreateMeetingDialog *createDialog = new CreateMeetingDialog(this);
+
+    // if (createDialog->exec() == QDialog::Accepted)
+
+    {
+        // MeetingInfo meetingInfo = createDialog->getMeetingInfo();
+
+        // 这里可以添加创建会议的API调用
+        // 例如:createMeetingApi(meetingInfo);
+
+        // 创建主界面部件(用于创建会议)
+        createMainWindow();
+        mainWidget->setRole({"role.admin"});
+        // 根据用户在对话框中的选择决定角色
+        // if (meetingInfo.joinAsAdmin) {
+        //     // 用户选择以管理员身份加入,使用AvRecorder模式
+        //     QStringList adminRoles = {"role.admin"};
+        //     mainWidget->setRole(adminRoles);
+        // } else {
+        //     // 用户选择以观看者身份加入,使用PlayWidget模式
+        //     QStringList viewerRoles = {"role.viewer"};
+        //     mainWidget->setRole(viewerRoles);
+        // }
+
+        // 显示主界面
+        stackedWidget->setCurrentWidget(mainWidget);
+        
+        // 检查房间状态
+        // checkRoom();
+
+        // 这里可以添加特定的创建会议逻辑
+        // 例如:startCreatedMeeting(meetingInfo);
+    }
 
+    // createDialog->deleteLater();
+}
+
+void MainWindow::onMeetingSelectionLogout()
+{
+    // 清理用户信息
+    AppEvent *appEvent = AppEvent::instance();
+    appEvent->setJwtToken(QString()); // 清理token
+    appEvent->setRefreshTime(0);      // 清理token刷新时间
+    appEvent->setRoles({});           // 清理角色
+    appEvent->setUserId({});          // 清理用户id
+    appEvent->setUserName({});        // 清理用户名
+    
+    // 返回登录页面
+    stackedWidget->setCurrentWidget(loginWidget);
+}
+
+void MainWindow::checkRoom(const QString &meetingId)
+{
+    // 创建两个异步操作的Promise
+    RoomInfo roomInfo;
+    roomInfo.id = "";
     QFuture<HttpResponse> getRoomListFuture = getRoomListApi(roomInfo);
 
     // 使用fromQFuture将QFuture转换为QPromise
@@ -203,7 +365,7 @@ void MainWindow::checkRoom()
 
     // 使用QtPromise::all等待所有Promise完成
     QtPromise::all(QVector<QtPromise::QPromise<HttpResponse>>{userInfoPromise})
-        .then([this](const QVector<HttpResponse> &results) {
+        .then([this, meetingId](const QVector<HttpResponse> &results) {
             const HttpResponse &getRoomList = results[0];
             qDebug() << "访问房间:" << getRoomList.data << getRoomList.rawData
                      << getRoomList.message;
@@ -213,6 +375,7 @@ void MainWindow::checkRoom()
             QList<RoomInfo> RoomInfos = roomListData.getRooms();
             qDebug() << RoomInfos.size();
 
+            // 填充房间列表
             for (int var = 0; var < RoomInfos.size(); ++var) {
                 const RoomInfo room = RoomInfos.at(var);
                 if (room.name) {
@@ -222,7 +385,47 @@ void MainWindow::checkRoom()
                     mainWidget->roomListWidget()->addItem(item);
                 }
             }
-            if (RoomInfos.size() > 0) {
+            
+            // 如果有待验证的会议ID,检查是否存在
+            if (!meetingId.isEmpty()) {
+                bool meetingFound = false;
+                int foundIndex = -1;
+                
+                for (int i = 0; i < RoomInfos.size(); ++i) {
+                    const RoomInfo room = RoomInfos.at(i);
+                    if (room.id.value_or("") == meetingId) {
+                        meetingFound = true;
+                        foundIndex = i;
+                        break;
+                    }
+                }
+                
+                if (meetingFound) {
+                    // 会议ID存在,设置为当前房间
+                    mainWidget->roomListWidget()->setCurrentRow(foundIndex);
+                    mainWidget->setPushRoomId(meetingId);
+                    qDebug() << "成功加入会议:" << meetingId;
+
+                    BubbleTip::showTip(this,
+                                       QString("成功加入会议: %1").arg(meetingId),
+                                       BubbleTip::Top,
+                                       3000,
+                                       BubbleTip::Type::Success);
+                } else {
+                    // 会议ID不存在,显示错误提示
+                    qWarning() << "会议ID不存在:" << meetingId;
+
+                    BubbleTip::showTip(this,
+                                       QString("会议ID不存在: %1").arg(meetingId),
+                                       BubbleTip::Top,
+                                       5000,
+                                       BubbleTip::Type::Error);
+
+                    // 回到会议选择页面
+                    showMeetingSelection();
+                }
+            } else if (RoomInfos.size() > 0) {
+                // 没有待验证的会议ID,使用默认的第一个房间
                 mainWidget->roomListWidget()->setCurrentRow(0);
                 mainWidget->setPushRoomId(RoomInfos[0].id.value_or(""));
                 // mainWidget->chatView->initWebsocket(RoomInfos[0].id.value());

+ 9 - 3
mainwindow.h

@@ -7,6 +7,9 @@
 
 class QHBoxLayout;
 class QStackedWidget;
+class MeetingSelectionWidget;
+class JoinMeetingDialog;
+class CreateMeetingDialog;
 
 class MainWindow : public TMainWindow
 {
@@ -20,14 +23,17 @@ public:
 
 private:
     void onLoginSuccess(const QString &username, const QString &password);
-
     void authLogin();
-
-    void checkRoom();
+    void checkRoom(const QString &meetingId = QString());
+    void showMeetingSelection();
+    void onJoinMeetingRequested();
+    void onCreateMeetingRequested();
+    void onMeetingSelectionLogout();
 
     QVBoxLayout *layout;
     class LoginWidget *loginWidget;
     MainPanel *mainWidget;
+    MeetingSelectionWidget *meetingSelectionWidget;
 
     QStackedWidget *stackedWidget;
 };

+ 462 - 0
widgets/createmeetingdialog.cpp

@@ -0,0 +1,462 @@
+#include "createmeetingdialog.h"
+#include <QMessageBox>
+#include <QRandomGenerator>
+#include <QRegularExpression>
+#include <QRegularExpressionValidator>
+#include <QScrollArea>
+
+CreateMeetingDialog::CreateMeetingDialog(QWidget *parent)
+    : QDialog(parent)
+    , m_mainLayout(nullptr)
+    , m_formLayout(nullptr)
+    , m_basicGroup(nullptr)
+    , m_basicLayout(nullptr)
+    , m_meetingNameEdit(nullptr)
+    , m_descriptionEdit(nullptr)
+    , m_startTimeEdit(nullptr)
+    , m_durationSpinBox(nullptr)
+    , m_maxParticipantsSpinBox(nullptr)
+    , m_meetingTypeCombo(nullptr)
+    , m_securityGroup(nullptr)
+    , m_securityLayout(nullptr)
+    , m_passwordCheckBox(nullptr)
+    , m_passwordLayout(nullptr)
+    , m_passwordEdit(nullptr)
+    , m_generatePasswordButton(nullptr)
+    , m_recordCheckBox(nullptr)
+    , m_roleGroup(nullptr)
+    , m_roleLayout(nullptr)
+    , m_joinAsAdminCheckBox(nullptr)
+    , m_buttonLayout(nullptr)
+    , m_createButton(nullptr)
+    , m_cancelButton(nullptr)
+{
+    setWindowTitle("创建会议");
+    setModal(true);
+    resize(500, 600);
+    
+    setupUI();
+    applyStyles();
+    setDefaultValues();
+    
+    // 初始状态下禁用创建按钮
+    m_createButton->setEnabled(false);
+}
+
+CreateMeetingDialog::~CreateMeetingDialog()
+{
+}
+
+void CreateMeetingDialog::setupUI()
+{
+    // 创建滚动区域
+    QScrollArea *scrollArea = new QScrollArea(this);
+    scrollArea->setWidgetResizable(true);
+    scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+    
+    QWidget *scrollWidget = new QWidget();
+    scrollArea->setWidget(scrollWidget);
+    
+    m_mainLayout = new QVBoxLayout(scrollWidget);
+    m_mainLayout->setContentsMargins(20, 20, 20, 20);
+    m_mainLayout->setSpacing(20);
+    
+    // 基本信息组
+    m_basicGroup = new QGroupBox("基本信息", scrollWidget);
+    m_basicLayout = new QFormLayout(m_basicGroup);
+    m_basicLayout->setContentsMargins(15, 15, 15, 15);
+    m_basicLayout->setSpacing(10);
+    
+    // 会议名称
+    m_meetingNameEdit = new QLineEdit();
+    m_meetingNameEdit->setPlaceholderText("请输入会议名称");
+    connect(m_meetingNameEdit, &QLineEdit::textChanged, this, &CreateMeetingDialog::onMeetingNameChanged);
+    m_basicLayout->addRow("会议名称 *:", m_meetingNameEdit);
+    
+    // 会议描述
+    m_descriptionEdit = new QTextEdit();
+    m_descriptionEdit->setPlaceholderText("请输入会议描述(可选)");
+    m_descriptionEdit->setMaximumHeight(80);
+    m_basicLayout->addRow("会议描述:", m_descriptionEdit);
+    
+    // 开始时间
+    m_startTimeEdit = new QDateTimeEdit();
+    m_startTimeEdit->setCalendarPopup(true);
+    m_startTimeEdit->setDisplayFormat("yyyy-MM-dd hh:mm");
+    m_basicLayout->addRow("开始时间 *:", m_startTimeEdit);
+    
+    // 会议时长
+    m_durationSpinBox = new QSpinBox();
+    m_durationSpinBox->setRange(15, 480); // 15分钟到8小时
+    m_durationSpinBox->setSuffix(" 分钟");
+    m_durationSpinBox->setSingleStep(15);
+    m_basicLayout->addRow("会议时长 *:", m_durationSpinBox);
+    
+    // 最大参与人数
+    m_maxParticipantsSpinBox = new QSpinBox();
+    m_maxParticipantsSpinBox->setRange(2, 100);
+    m_maxParticipantsSpinBox->setSuffix(" 人");
+    m_basicLayout->addRow("最大参与人数:", m_maxParticipantsSpinBox);
+    
+    // 会议类型
+    m_meetingTypeCombo = new QComboBox();
+    m_meetingTypeCombo->addItems({"视频会议", "音频会议", "屏幕共享", "网络研讨会"});
+    m_basicLayout->addRow("会议类型:", m_meetingTypeCombo);
+    
+    m_mainLayout->addWidget(m_basicGroup);
+    
+    // 安全设置组
+    m_securityGroup = new QGroupBox("安全设置", scrollWidget);
+    m_securityLayout = new QVBoxLayout(m_securityGroup);
+    m_securityLayout->setContentsMargins(15, 15, 15, 15);
+    m_securityLayout->setSpacing(10);
+    
+    // 密码设置
+    m_passwordCheckBox = new QCheckBox("启用会议密码");
+    connect(m_passwordCheckBox, &QCheckBox::toggled, this, &CreateMeetingDialog::onPasswordCheckChanged);
+    m_securityLayout->addWidget(m_passwordCheckBox);
+    
+    m_passwordLayout = new QHBoxLayout();
+    m_passwordEdit = new QLineEdit();
+    m_passwordEdit->setPlaceholderText("请输入6-12位密码");
+    m_passwordEdit->setMaxLength(12);
+    m_passwordEdit->setEnabled(false);
+    
+    // 设置密码验证器
+    QRegularExpression passwordRegex("[A-Za-z0-9]{6,12}");
+    QRegularExpressionValidator *passwordValidator = new QRegularExpressionValidator(passwordRegex, this);
+    m_passwordEdit->setValidator(passwordValidator);
+    
+    m_generatePasswordButton = new QPushButton("生成密码");
+    m_generatePasswordButton->setEnabled(false);
+    connect(m_generatePasswordButton, &QPushButton::clicked, this, &CreateMeetingDialog::generateRandomPassword);
+    
+    m_passwordLayout->addWidget(m_passwordEdit);
+    m_passwordLayout->addWidget(m_generatePasswordButton);
+    m_securityLayout->addLayout(m_passwordLayout);
+    
+    // 录制设置
+    m_recordCheckBox = new QCheckBox("自动录制会议");
+    m_securityLayout->addWidget(m_recordCheckBox);
+    
+    m_mainLayout->addWidget(m_securityGroup);
+    
+    // 角色选择组
+    m_roleGroup = new QGroupBox("加入方式", scrollWidget);
+    m_roleLayout = new QVBoxLayout(m_roleGroup);
+    m_roleLayout->setContentsMargins(15, 15, 15, 15);
+    m_roleLayout->setSpacing(10);
+    
+    m_joinAsAdminCheckBox = new QCheckBox("以管理员身份加入会议(可以录制和管理)");
+    m_joinAsAdminCheckBox->setChecked(true); // 默认选中管理员模式
+    m_roleLayout->addWidget(m_joinAsAdminCheckBox);
+    
+    QLabel *roleHint = new QLabel("提示:取消勾选将以观看者身份加入会议");
+    roleHint->setStyleSheet("color: #666; font-size: 12px;");
+    m_roleLayout->addWidget(roleHint);
+    
+    m_mainLayout->addWidget(m_roleGroup);
+    
+    // 按钮布局
+    m_buttonLayout = new QHBoxLayout();
+    m_buttonLayout->addStretch();
+    
+    m_cancelButton = new QPushButton("取消");
+    m_createButton = new QPushButton("创建会议");
+    
+    connect(m_cancelButton, &QPushButton::clicked, this, &CreateMeetingDialog::reject);
+    connect(m_createButton, &QPushButton::clicked, this, &CreateMeetingDialog::accept);
+    
+    m_buttonLayout->addWidget(m_cancelButton);
+    m_buttonLayout->addWidget(m_createButton);
+    
+    m_mainLayout->addLayout(m_buttonLayout);
+    
+    // 设置主布局
+    QVBoxLayout *dialogLayout = new QVBoxLayout(this);
+    dialogLayout->setContentsMargins(0, 0, 0, 0);
+    dialogLayout->addWidget(scrollArea);
+}
+
+void CreateMeetingDialog::applyStyles()
+{
+    setStyleSheet(
+        "CreateMeetingDialog {"
+        "    background-color: #f8f9fa;"
+        "}"
+        
+        "QScrollArea {"
+        "    border: none;"
+        "    background-color: #f8f9fa;"
+        "}"
+        
+        "QGroupBox {"
+        "    font-size: 14px;"
+        "    font-weight: bold;"
+        "    color: #495057;"
+        "    border: 2px solid #e9ecef;"
+        "    border-radius: 8px;"
+        "    margin-top: 10px;"
+        "    background-color: white;"
+        "}"
+        
+        "QGroupBox::title {"
+        "    subcontrol-origin: margin;"
+        "    left: 10px;"
+        "    padding: 0 8px 0 8px;"
+        "    background-color: white;"
+        "}"
+        
+        "QLabel {"
+        "    font-size: 13px;"
+        "    color: #495057;"
+        "    font-weight: 500;"
+        "}"
+        
+        "QLineEdit {"
+        "    border: 2px solid #e9ecef;"
+        "    border-radius: 6px;"
+        "    padding: 8px 12px;"
+        "    font-size: 13px;"
+        "    background-color: white;"
+        "}"
+        
+        "QLineEdit:focus {"
+        "    border-color: #007bff;"
+        "    outline: none;"
+        "}"
+        
+        "QLineEdit:disabled {"
+        "    background-color: #f8f9fa;"
+        "    color: #6c757d;"
+        "}"
+        
+        "QTextEdit {"
+        "    border: 2px solid #e9ecef;"
+        "    border-radius: 6px;"
+        "    padding: 8px 12px;"
+        "    font-size: 13px;"
+        "    background-color: white;"
+        "}"
+        
+        "QTextEdit:focus {"
+        "    border-color: #007bff;"
+        "    outline: none;"
+        "}"
+        
+        "QDateTimeEdit, QSpinBox, QComboBox {"
+        "    border: 2px solid #e9ecef;"
+        "    border-radius: 6px;"
+        "    padding: 8px 12px;"
+        "    font-size: 13px;"
+        "    background-color: white;"
+        "}"
+        
+        "QDateTimeEdit:focus, QSpinBox:focus, QComboBox:focus {"
+        "    border-color: #007bff;"
+        "    outline: none;"
+        "}"
+        
+        "QCheckBox {"
+        "    font-size: 13px;"
+        "    color: #495057;"
+        "    spacing: 8px;"
+        "}"
+        
+        "QCheckBox::indicator {"
+        "    width: 16px;"
+        "    height: 16px;"
+        "    border: 2px solid #e9ecef;"
+        "    border-radius: 3px;"
+        "    background-color: white;"
+        "}"
+        
+        "QCheckBox::indicator:checked {"
+        "    background-color: #007bff;"
+        "    border-color: #007bff;"
+        "    image: url(:/icons/check.png);"
+        "}"
+        
+        "QPushButton {"
+        "    border: none;"
+        "    border-radius: 6px;"
+        "    padding: 10px 20px;"
+        "    font-size: 13px;"
+        "    font-weight: 500;"
+        "    min-width: 80px;"
+        "}"
+        
+        "#createButton {"
+        "    background-color: #28a745;"
+        "    color: white;"
+        "}"
+        
+        "#createButton:hover {"
+        "    background-color: #218838;"
+        "}"
+        
+        "#createButton:pressed {"
+        "    background-color: #1e7e34;"
+        "}"
+        
+        "#createButton:disabled {"
+        "    background-color: #6c757d;"
+        "    color: #adb5bd;"
+        "}"
+        
+        "#cancelButton {"
+        "    background-color: #6c757d;"
+        "    color: white;"
+        "}"
+        
+        "#cancelButton:hover {"
+        "    background-color: #545b62;"
+        "}"
+        
+        "#generatePasswordButton {"
+        "    background-color: #17a2b8;"
+        "    color: white;"
+        "    min-width: 60px;"
+        "}"
+        
+        "#generatePasswordButton:hover {"
+        "    background-color: #138496;"
+        "}"
+        
+        "#generatePasswordButton:disabled {"
+        "    background-color: #6c757d;"
+        "    color: #adb5bd;"
+        "}"
+    );
+    
+    m_createButton->setObjectName("createButton");
+    m_cancelButton->setObjectName("cancelButton");
+    m_generatePasswordButton->setObjectName("generatePasswordButton");
+}
+
+void CreateMeetingDialog::setDefaultValues()
+{
+    // 设置默认开始时间为当前时间后1小时
+    QDateTime defaultTime = QDateTime::currentDateTime().addSecs(3600);
+    m_startTimeEdit->setDateTime(defaultTime);
+    m_startTimeEdit->setMinimumDateTime(QDateTime::currentDateTime());
+    
+    // 设置默认时长为60分钟
+    m_durationSpinBox->setValue(60);
+    
+    // 设置默认最大参与人数为10人
+    m_maxParticipantsSpinBox->setValue(10);
+    
+    // 默认选择视频会议
+    m_meetingTypeCombo->setCurrentIndex(0);
+}
+
+void CreateMeetingDialog::onPasswordCheckChanged(bool checked)
+{
+    m_passwordEdit->setEnabled(checked);
+    m_generatePasswordButton->setEnabled(checked);
+    
+    if (!checked) {
+        m_passwordEdit->clear();
+    }
+    
+    validateInput();
+}
+
+void CreateMeetingDialog::onMeetingNameChanged()
+{
+    validateInput();
+}
+
+void CreateMeetingDialog::validateInput()
+{
+    bool isValid = isValidInput();
+    m_createButton->setEnabled(isValid);
+}
+
+void CreateMeetingDialog::generateRandomPassword()
+{
+    const QString chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
+    QString password;
+    
+    for (int i = 0; i < 8; ++i) {
+        int index = QRandomGenerator::global()->bounded(chars.length());
+        password.append(chars.at(index));
+    }
+    
+    m_passwordEdit->setText(password);
+}
+
+bool CreateMeetingDialog::isValidInput() const
+{
+    // 检查会议名称
+    if (m_meetingNameEdit->text().trimmed().isEmpty()) {
+        return false;
+    }
+    
+    // 检查开始时间
+    if (m_startTimeEdit->dateTime() <= QDateTime::currentDateTime()) {
+        return false;
+    }
+    
+    // 如果启用了密码,检查密码有效性
+    if (m_passwordCheckBox->isChecked()) {
+        QString password = m_passwordEdit->text().trimmed();
+        if (password.length() < 6 || password.length() > 12) {
+            return false;
+        }
+    }
+    
+    return true;
+}
+
+MeetingInfo CreateMeetingDialog::getMeetingInfo() const
+{
+    MeetingInfo info;
+    info.meetingName = m_meetingNameEdit->text().trimmed();
+    info.description = m_descriptionEdit->toPlainText().trimmed();
+    info.startTime = m_startTimeEdit->dateTime();
+    info.duration = m_durationSpinBox->value();
+    info.maxParticipants = m_maxParticipantsSpinBox->value();
+    info.requirePassword = m_passwordCheckBox->isChecked();
+    info.password = m_passwordEdit->text().trimmed();
+    info.recordMeeting = m_recordCheckBox->isChecked();
+    info.meetingType = m_meetingTypeCombo->currentText();
+    info.joinAsAdmin = m_joinAsAdminCheckBox->isChecked();
+    
+    return info;
+}
+
+bool CreateMeetingDialog::getJoinAsAdmin() const
+{
+    return m_joinAsAdminCheckBox->isChecked();
+}
+
+void CreateMeetingDialog::accept()
+{
+    if (!isValidInput()) {
+        QString errorMsg;
+        
+        if (m_meetingNameEdit->text().trimmed().isEmpty()) {
+            errorMsg = "请输入会议名称。";
+        } else if (m_startTimeEdit->dateTime() <= QDateTime::currentDateTime()) {
+            errorMsg = "开始时间必须晚于当前时间。";
+        } else if (m_passwordCheckBox->isChecked()) {
+            QString password = m_passwordEdit->text().trimmed();
+            if (password.length() < 6 || password.length() > 12) {
+                errorMsg = "密码长度必须在6-12位之间。";
+            }
+        }
+        
+        QMessageBox::warning(this, "输入错误", errorMsg);
+        return;
+    }
+    
+    m_meetingInfo = getMeetingInfo();
+    QDialog::accept();
+}
+
+void CreateMeetingDialog::reject()
+{
+    QDialog::reject();
+}

+ 101 - 0
widgets/createmeetingdialog.h

@@ -0,0 +1,101 @@
+#ifndef CREATEMEETINGDIALOG_H
+#define CREATEMEETINGDIALOG_H
+
+#include <QDialog>
+#include <QVBoxLayout>
+#include <QHBoxLayout>
+#include <QFormLayout>
+#include <QLabel>
+#include <QLineEdit>
+#include <QPushButton>
+#include <QTextEdit>
+#include <QDateTimeEdit>
+#include <QCheckBox>
+#include <QSpinBox>
+#include <QComboBox>
+#include <QGroupBox>
+#include <QDateTime>
+
+struct MeetingInfo {
+    QString meetingName;
+    QString description;
+    QDateTime startTime;
+    int duration; // 分钟
+    int maxParticipants;
+    bool requirePassword;
+    QString password;
+    bool recordMeeting;
+    QString meetingType;
+    bool joinAsAdmin; // 是否以管理员身份加入
+};
+
+class CreateMeetingDialog : public QDialog
+{
+    Q_OBJECT
+
+public:
+    explicit CreateMeetingDialog(QWidget *parent = nullptr);
+    ~CreateMeetingDialog();
+
+    // 获取会议信息
+    MeetingInfo getMeetingInfo() const;
+    
+    // 设置默认值
+    void setDefaultValues();
+    
+    // 获取是否以管理员身份加入
+    bool getJoinAsAdmin() const;
+
+public slots:
+    void accept() override;
+    void reject() override;
+
+private slots:
+    void onPasswordCheckChanged(bool checked);
+    void onMeetingNameChanged();
+    void validateInput();
+    void generateRandomPassword();
+
+private:
+    void setupUI();
+    void applyStyles();
+    bool isValidInput() const;
+    
+    // UI组件
+    QVBoxLayout *m_mainLayout;
+    QFormLayout *m_formLayout;
+    
+    // 基本信息组
+    QGroupBox *m_basicGroup;
+    QFormLayout *m_basicLayout;
+    QLineEdit *m_meetingNameEdit;
+    QTextEdit *m_descriptionEdit;
+    QDateTimeEdit *m_startTimeEdit;
+    QSpinBox *m_durationSpinBox;
+    QSpinBox *m_maxParticipantsSpinBox;
+    QComboBox *m_meetingTypeCombo;
+    
+    // 安全设置组
+    QGroupBox *m_securityGroup;
+    QVBoxLayout *m_securityLayout;
+    QCheckBox *m_passwordCheckBox;
+    QHBoxLayout *m_passwordLayout;
+    QLineEdit *m_passwordEdit;
+    QPushButton *m_generatePasswordButton;
+    QCheckBox *m_recordCheckBox;
+    
+    // 角色选择
+    QGroupBox *m_roleGroup;
+    QVBoxLayout *m_roleLayout;
+    QCheckBox *m_joinAsAdminCheckBox;
+    
+    // 按钮布局
+    QHBoxLayout *m_buttonLayout;
+    QPushButton *m_createButton;
+    QPushButton *m_cancelButton;
+    
+    // 数据
+    MeetingInfo m_meetingInfo;
+};
+
+#endif // CREATEMEETINGDIALOG_H

+ 299 - 0
widgets/joinmeetingdialog.cpp

@@ -0,0 +1,299 @@
+#include "joinmeetingdialog.h"
+#include <QMessageBox>
+#include <QRegularExpression>
+#include <QRegularExpressionValidator>
+
+JoinMeetingDialog::JoinMeetingDialog(QWidget *parent)
+    : QDialog(parent)
+    , m_mainLayout(nullptr)
+    , m_tabWidget(nullptr)
+    , m_manualTab(nullptr)
+    , m_manualLayout(nullptr)
+    , m_manualLabel(nullptr)
+    , m_meetingIdEdit(nullptr)
+    , m_selectTab(nullptr)
+    , m_selectLayout(nullptr)
+    , m_selectLabel(nullptr)
+    , m_meetingListWidget(nullptr)
+    , m_buttonLayout(nullptr)
+    , m_joinButton(nullptr)
+    , m_cancelButton(nullptr)
+{
+    setWindowTitle("加入会议");
+    setModal(true);
+    resize(400, 300);
+    
+    setupUI();
+    applyStyles();
+    
+    // 默认选择手动输入标签页
+    m_tabWidget->setCurrentIndex(0);
+    
+    // 初始状态下禁用加入按钮
+    m_joinButton->setEnabled(false);
+}
+
+JoinMeetingDialog::~JoinMeetingDialog()
+{
+}
+
+void JoinMeetingDialog::setupUI()
+{
+    m_mainLayout = new QVBoxLayout(this);
+    m_mainLayout->setContentsMargins(20, 20, 20, 20);
+    m_mainLayout->setSpacing(15);
+    
+    // 创建标签页控件
+    m_tabWidget = new QTabWidget(this);
+    
+    // 手动输入标签页
+    m_manualTab = new QWidget();
+    m_manualLayout = new QVBoxLayout(m_manualTab);
+    m_manualLayout->setContentsMargins(15, 15, 15, 15);
+    m_manualLayout->setSpacing(10);
+    
+    m_manualLabel = new QLabel("请输入会议ID:", m_manualTab);
+    m_meetingIdEdit = new QLineEdit(m_manualTab);
+    m_meetingIdEdit->setPlaceholderText("例如:123456789");
+    
+    // 设置输入验证器(只允许数字和字母)
+    QRegularExpression regex("[A-Za-z0-9]{1,20}");
+    QRegularExpressionValidator *validator = new QRegularExpressionValidator(regex, this);
+    m_meetingIdEdit->setValidator(validator);
+    
+    connect(m_meetingIdEdit, &QLineEdit::textChanged, this, &JoinMeetingDialog::onMeetingIdChanged);
+    
+    m_manualLayout->addWidget(m_manualLabel);
+    m_manualLayout->addWidget(m_meetingIdEdit);
+    m_manualLayout->addStretch();
+    
+    m_tabWidget->addTab(m_manualTab, "手动输入");
+    
+    // 选择会议标签页
+    m_selectTab = new QWidget();
+    m_selectLayout = new QVBoxLayout(m_selectTab);
+    m_selectLayout->setContentsMargins(15, 15, 15, 15);
+    m_selectLayout->setSpacing(10);
+    
+    m_selectLabel = new QLabel("选择可用的会议:", m_selectTab);
+    m_meetingListWidget = new QListWidget(m_selectTab);
+    
+    connect(m_meetingListWidget, &QListWidget::itemClicked, this, &JoinMeetingDialog::onMeetingItemClicked);
+    connect(m_meetingListWidget, &QListWidget::itemDoubleClicked, this, &JoinMeetingDialog::accept);
+    
+    m_selectLayout->addWidget(m_selectLabel);
+    m_selectLayout->addWidget(m_meetingListWidget);
+    
+    m_tabWidget->addTab(m_selectTab, "选择会议");
+    
+    // 按钮布局
+    m_buttonLayout = new QHBoxLayout();
+    m_buttonLayout->addStretch();
+    
+    m_cancelButton = new QPushButton("取消", this);
+    m_joinButton = new QPushButton("加入会议", this);
+    
+    connect(m_cancelButton, &QPushButton::clicked, this, &JoinMeetingDialog::reject);
+    connect(m_joinButton, &QPushButton::clicked, this, &JoinMeetingDialog::accept);
+    
+    m_buttonLayout->addWidget(m_cancelButton);
+    m_buttonLayout->addWidget(m_joinButton);
+    
+    // 添加到主布局
+    m_mainLayout->addWidget(m_tabWidget);
+    m_mainLayout->addLayout(m_buttonLayout);
+}
+
+void JoinMeetingDialog::applyStyles()
+{
+    setStyleSheet(
+        "JoinMeetingDialog {"
+        "    background-color: #f8f9fa;"
+        "}"
+        
+        "QTabWidget::pane {"
+        "    border: 1px solid #dee2e6;"
+        "    border-radius: 5px;"
+        "    background-color: white;"
+        "}"
+        
+        "QTabWidget::tab-bar {"
+        "    alignment: center;"
+        "}"
+        
+        "QTabBar::tab {"
+        "    background-color: #e9ecef;"
+        "    border: 1px solid #dee2e6;"
+        "    border-bottom: none;"
+        "    border-top-left-radius: 5px;"
+        "    border-top-right-radius: 5px;"
+        "    padding: 8px 16px;"
+        "    margin-right: 2px;"
+        "}"
+        
+        "QTabBar::tab:selected {"
+        "    background-color: white;"
+        "    border-bottom: 1px solid white;"
+        "}"
+        
+        "QTabBar::tab:hover {"
+        "    background-color: #f8f9fa;"
+        "}"
+        
+        "QLabel {"
+        "    font-size: 14px;"
+        "    color: #495057;"
+        "    font-weight: 500;"
+        "}"
+        
+        "QLineEdit {"
+        "    border: 2px solid #e9ecef;"
+        "    border-radius: 6px;"
+        "    padding: 8px 12px;"
+        "    font-size: 14px;"
+        "    background-color: white;"
+        "}"
+        
+        "QLineEdit:focus {"
+        "    border-color: #007bff;"
+        "    outline: none;"
+        "}"
+        
+        "QListWidget {"
+        "    border: 2px solid #e9ecef;"
+        "    border-radius: 6px;"
+        "    background-color: white;"
+        "    selection-background-color: #007bff;"
+        "    selection-color: white;"
+        "}"
+        
+        "QListWidget::item {"
+        "    padding: 8px 12px;"
+        "    border-bottom: 1px solid #f8f9fa;"
+        "}"
+        
+        "QListWidget::item:hover {"
+        "    background-color: #f8f9fa;"
+        "}"
+        
+        "QPushButton {"
+        "    border: none;"
+        "    border-radius: 6px;"
+        "    padding: 10px 20px;"
+        "    font-size: 14px;"
+        "    font-weight: 500;"
+        "    min-width: 80px;"
+        "}"
+        
+        "#joinButton {"
+        "    background-color: #007bff;"
+        "    color: white;"
+        "}"
+        
+        "#joinButton:hover {"
+        "    background-color: #0056b3;"
+        "}"
+        
+        "#joinButton:pressed {"
+        "    background-color: #004085;"
+        "}"
+        
+        "#joinButton:disabled {"
+        "    background-color: #6c757d;"
+        "    color: #adb5bd;"
+        "}"
+        
+        "#cancelButton {"
+        "    background-color: #6c757d;"
+        "    color: white;"
+        "}"
+        
+        "#cancelButton:hover {"
+        "    background-color: #545b62;"
+        "}"
+        
+        "#cancelButton:pressed {"
+        "    background-color: #3d4142;"
+        "}"
+    );
+    
+    m_joinButton->setObjectName("joinButton");
+    m_cancelButton->setObjectName("cancelButton");
+}
+
+void JoinMeetingDialog::setAvailableMeetings(const QStringList &meetingIds, const QStringList &meetingNames)
+{
+    m_meetingIds = meetingIds;
+    m_meetingNames = meetingNames;
+    
+    m_meetingListWidget->clear();
+    
+    for (int i = 0; i < meetingIds.size() && i < meetingNames.size(); ++i) {
+        QString displayText = QString("%1 - %2").arg(meetingIds[i], meetingNames[i]);
+        QListWidgetItem *item = new QListWidgetItem(displayText);
+        item->setData(Qt::UserRole, meetingIds[i]); // 存储会议ID
+        m_meetingListWidget->addItem(item);
+    }
+    
+    // 如果没有可用会议,禁用选择标签页
+    m_tabWidget->setTabEnabled(1, !meetingIds.isEmpty());
+}
+
+QString JoinMeetingDialog::getSelectedMeetingId() const
+{
+    return m_selectedMeetingId;
+}
+
+void JoinMeetingDialog::onMeetingItemClicked()
+{
+    QListWidgetItem *currentItem = m_meetingListWidget->currentItem();
+    if (currentItem) {
+        m_selectedMeetingId = currentItem->data(Qt::UserRole).toString();
+        m_joinButton->setEnabled(true);
+    }
+}
+
+void JoinMeetingDialog::onMeetingIdChanged()
+{
+    QString text = m_meetingIdEdit->text().trimmed();
+    if (m_tabWidget->currentIndex() == 0) { // 手动输入标签页
+        m_selectedMeetingId = text;
+        m_joinButton->setEnabled(!text.isEmpty());
+    }
+}
+
+void JoinMeetingDialog::validateInput()
+{
+    bool isValid = false;
+    
+    if (m_tabWidget->currentIndex() == 0) { // 手动输入
+        isValid = !m_meetingIdEdit->text().trimmed().isEmpty();
+        m_selectedMeetingId = m_meetingIdEdit->text().trimmed();
+    } else { // 选择会议
+        QListWidgetItem *currentItem = m_meetingListWidget->currentItem();
+        isValid = (currentItem != nullptr);
+        if (isValid) {
+            m_selectedMeetingId = currentItem->data(Qt::UserRole).toString();
+        }
+    }
+    
+    m_joinButton->setEnabled(isValid);
+}
+
+void JoinMeetingDialog::accept()
+{
+    validateInput();
+    
+    if (m_selectedMeetingId.isEmpty()) {
+        QMessageBox::warning(this, "输入错误", "请输入或选择一个有效的会议ID。");
+        return;
+    }
+    
+    QDialog::accept();
+}
+
+void JoinMeetingDialog::reject()
+{
+    m_selectedMeetingId.clear();
+    QDialog::reject();
+}

+ 69 - 0
widgets/joinmeetingdialog.h

@@ -0,0 +1,69 @@
+#ifndef JOINMEETINGDIALOG_H
+#define JOINMEETINGDIALOG_H
+
+#include <QDialog>
+#include <QVBoxLayout>
+#include <QHBoxLayout>
+#include <QLabel>
+#include <QLineEdit>
+#include <QPushButton>
+#include <QListWidget>
+#include <QTabWidget>
+#include <QGroupBox>
+#include <QStringList>
+
+class JoinMeetingDialog : public QDialog
+{
+    Q_OBJECT
+
+public:
+    explicit JoinMeetingDialog(QWidget *parent = nullptr);
+    ~JoinMeetingDialog();
+
+    // 获取选择的会议ID
+    QString getSelectedMeetingId() const;
+    
+    // 设置可用的会议列表
+    void setAvailableMeetings(const QStringList &meetingIds, const QStringList &meetingNames);
+
+public slots:
+    void accept() override;
+    void reject() override;
+
+private slots:
+    void onMeetingItemClicked();
+    void onMeetingIdChanged();
+    void validateInput();
+
+private:
+    void setupUI();
+    void applyStyles();
+    
+    // UI组件
+    QVBoxLayout *m_mainLayout;
+    QTabWidget *m_tabWidget;
+    
+    // 手动输入标签页
+    QWidget *m_manualTab;
+    QVBoxLayout *m_manualLayout;
+    QLabel *m_manualLabel;
+    QLineEdit *m_meetingIdEdit;
+    
+    // 选择会议标签页
+    QWidget *m_selectTab;
+    QVBoxLayout *m_selectLayout;
+    QLabel *m_selectLabel;
+    QListWidget *m_meetingListWidget;
+    
+    // 按钮
+    QHBoxLayout *m_buttonLayout;
+    QPushButton *m_joinButton;
+    QPushButton *m_cancelButton;
+    
+    // 数据
+    QString m_selectedMeetingId;
+    QStringList m_meetingIds;
+    QStringList m_meetingNames;
+};
+
+#endif // JOINMEETINGDIALOG_H

+ 206 - 0
widgets/meetingselectionwidget.cpp

@@ -0,0 +1,206 @@
+#include "meetingselectionwidget.h"
+#include "../appevent.h"
+#include <QApplication>
+#include <QScreen>
+
+MeetingSelectionWidget::MeetingSelectionWidget(QWidget *parent)
+    : QWidget(parent)
+    , m_mainLayout(nullptr)
+    , m_titleLabel(nullptr)
+    , m_welcomeLabel(nullptr)
+    , m_buttonFrame(nullptr)
+    , m_buttonLayout(nullptr)
+    , m_joinMeetingBtn(nullptr)
+    , m_createMeetingBtn(nullptr)
+    , m_logoutBtn(nullptr)
+    , m_isAdmin(false)
+{
+    setupUI();
+    applyStyles();
+}
+
+MeetingSelectionWidget::~MeetingSelectionWidget()
+{
+}
+
+void MeetingSelectionWidget::setupUI()
+{
+    // 主布局
+    m_mainLayout = new QVBoxLayout(this);
+    m_mainLayout->setContentsMargins(50, 50, 50, 50);
+    m_mainLayout->setSpacing(30);
+    
+    // 标题
+    m_titleLabel = new QLabel("智能学习客户端", this);
+    m_titleLabel->setAlignment(Qt::AlignCenter);
+    m_titleLabel->setObjectName("titleLabel");
+    
+    // 欢迎信息
+    m_welcomeLabel = new QLabel("欢迎使用!请选择您要进行的操作:", this);
+    m_welcomeLabel->setAlignment(Qt::AlignCenter);
+    m_welcomeLabel->setObjectName("welcomeLabel");
+    
+    // 按钮容器
+    m_buttonFrame = new QFrame(this);
+    m_buttonFrame->setObjectName("buttonFrame");
+    m_buttonLayout = new QVBoxLayout(m_buttonFrame);
+    m_buttonLayout->setContentsMargins(20, 20, 20, 20);
+    m_buttonLayout->setSpacing(20);
+    
+    // 加入会议按钮
+    m_joinMeetingBtn = new QPushButton("加入会议", this);
+    m_joinMeetingBtn->setObjectName("joinMeetingBtn");
+    m_joinMeetingBtn->setMinimumHeight(60);
+    connect(m_joinMeetingBtn, &QPushButton::clicked, this, &MeetingSelectionWidget::onJoinMeetingClicked);
+    
+    // 创建会议按钮
+    m_createMeetingBtn = new QPushButton("创建会议", this);
+    m_createMeetingBtn->setObjectName("createMeetingBtn");
+    m_createMeetingBtn->setMinimumHeight(60);
+    connect(m_createMeetingBtn, &QPushButton::clicked, this, &MeetingSelectionWidget::onCreateMeetingClicked);
+    
+    // 退出登录按钮
+    m_logoutBtn = new QPushButton("退出登录", this);
+    m_logoutBtn->setObjectName("logoutBtn");
+    m_logoutBtn->setMinimumHeight(40);
+    connect(m_logoutBtn, &QPushButton::clicked, this, &MeetingSelectionWidget::onLogoutClicked);
+    
+    // 添加按钮到布局
+    m_buttonLayout->addWidget(m_joinMeetingBtn);
+    m_buttonLayout->addWidget(m_createMeetingBtn);
+    m_buttonLayout->addStretch();
+    m_buttonLayout->addWidget(m_logoutBtn);
+    
+    // 添加到主布局
+    m_mainLayout->addStretch();
+    m_mainLayout->addWidget(m_titleLabel);
+    m_mainLayout->addWidget(m_welcomeLabel);
+    m_mainLayout->addStretch();
+    m_mainLayout->addWidget(m_buttonFrame, 0, Qt::AlignCenter);
+    m_mainLayout->addStretch();
+    
+    // 设置按钮框架的固定宽度
+    m_buttonFrame->setFixedWidth(300);
+}
+
+void MeetingSelectionWidget::setUserRoles(const QStringList &roles)
+{
+    m_userRoles = roles;
+    m_isAdmin = roles.contains("role.admin");
+    updateButtonsVisibility();
+}
+
+void MeetingSelectionWidget::setUserInfo(const QString &username, const QString &userId)
+{
+    m_username = username;
+    m_userId = userId;
+    
+    // 更新欢迎信息
+    if (!username.isEmpty()) {
+        m_welcomeLabel->setText(QString("欢迎,%1!请选择您要进行的操作:").arg(username));
+    }
+}
+
+void MeetingSelectionWidget::updateButtonsVisibility()
+{
+    // 所有用户都可以加入会议
+    m_joinMeetingBtn->setVisible(true);
+    
+    // 只有管理员可以创建会议
+    m_createMeetingBtn->setVisible(m_isAdmin);
+}
+
+void MeetingSelectionWidget::applyStyles()
+{
+    // 设置整体样式
+    setStyleSheet("MeetingSelectionWidget {"
+                  "    background-color: #f5f5f5;"
+                  "}"
+
+                  "#titleLabel {"
+                  "    font-size: 28px;"
+                  "    font-weight: bold;"
+                  "    color: #2c3e50;"
+                  "    margin-bottom: 10px;"
+                  "}"
+
+                  "#welcomeLabel {"
+                  "    font-size: 16px;"
+                  "    color: #7f8c8d;"
+                  "    margin-bottom: 20px;"
+                  "}"
+
+                  "#buttonFrame {"
+                  "    background-color: white;"
+                  "    border: 1px solid #e0e0e0;"
+                  "    border-radius: 10px;"
+                  "}"
+
+                  "#joinMeetingBtn {"
+                  "    background-color: #3498db;"
+                  "    color: white;"
+                  "    border: none;"
+                  "    border-radius: 8px;"
+                  "    font-size: 16px;"
+                  "    font-weight: bold;"
+                  "    padding: 15px;"
+                  "}"
+
+                  "#joinMeetingBtn:hover {"
+                  "    background-color: #2980b9;"
+                  "}"
+
+                  "#joinMeetingBtn:pressed {"
+                  "    background-color: #21618c;"
+                  "}"
+
+                  "#createMeetingBtn {"
+                  "    background-color: #27ae60;"
+                  "    color: white;"
+                  "    border: none;"
+                  "    border-radius: 8px;"
+                  "    font-size: 16px;"
+                  "    font-weight: bold;"
+                  "    padding: 15px;"
+                  "}"
+
+                  "#createMeetingBtn:hover {"
+                  "    background-color: #229954;"
+                  "}"
+
+                  "#createMeetingBtn:pressed {"
+                  "    background-color: #1e8449;"
+                  "}"
+
+                  "#logoutBtn {"
+                  "    background-color: #e74c3c;"
+                  "    color: white;"
+                  "    border: none;"
+                  "    border-radius: 6px;"
+                  "    font-size: 14px;"
+                  "    padding: 10px;"
+                  "}"
+
+                  "#logoutBtn:hover {"
+                  "    background-color: #c0392b;"
+                  "}"
+
+                  "#logoutBtn:pressed {"
+                  "    background-color: #a93226;"
+                  "}");
+}
+
+void MeetingSelectionWidget::onJoinMeetingClicked()
+{
+    emit joinMeetingRequested();
+}
+
+void MeetingSelectionWidget::onCreateMeetingClicked()
+{
+    emit createMeetingRequested();
+}
+
+void MeetingSelectionWidget::onLogoutClicked()
+{
+    emit logoutRequested();
+}

+ 65 - 0
widgets/meetingselectionwidget.h

@@ -0,0 +1,65 @@
+#ifndef MEETINGSELECTIONWIDGET_H
+#define MEETINGSELECTIONWIDGET_H
+
+#include <QWidget>
+#include <QVBoxLayout>
+#include <QHBoxLayout>
+#include <QPushButton>
+#include <QLabel>
+#include <QFrame>
+#include <QStringList>
+
+class MeetingSelectionWidget : public QWidget
+{
+    Q_OBJECT
+
+public:
+    explicit MeetingSelectionWidget(QWidget *parent = nullptr);
+    ~MeetingSelectionWidget();
+
+    // 设置用户角色
+    void setUserRoles(const QStringList &roles);
+    
+    // 设置用户信息
+    void setUserInfo(const QString &username, const QString &userId);
+
+signals:
+    // 用户选择加入会议
+    void joinMeetingRequested();
+    
+    // 用户选择创建会议
+    void createMeetingRequested();
+    
+    // 用户选择退出登录
+    void logoutRequested();
+
+private slots:
+    void onJoinMeetingClicked();
+    void onCreateMeetingClicked();
+    void onLogoutClicked();
+
+private:
+    void setupUI();
+    void updateButtonsVisibility();
+    void applyStyles();
+
+    // UI组件
+    QVBoxLayout *m_mainLayout;
+    QLabel *m_titleLabel;
+    QLabel *m_welcomeLabel;
+    QFrame *m_buttonFrame;
+    QVBoxLayout *m_buttonLayout;
+    QPushButton *m_joinMeetingBtn;
+    QPushButton *m_createMeetingBtn;
+    QPushButton *m_logoutBtn;
+    
+    // 用户信息
+    QStringList m_userRoles;
+    QString m_username;
+    QString m_userId;
+    
+    // 是否为管理员
+    bool m_isAdmin;
+};
+
+#endif // MEETINGSELECTIONWIDGET_H