#include "userwidget.h" #include #include #include #include #include UserWidget::UserWidget(UserManager* userManager, QWidget *parent) : QWidget(parent) , m_userManager(userManager) , m_currentFilter(FilterAll) , m_contextMenu(nullptr) { setupUI(); m_tableWidget->setContextMenuPolicy(Qt::CustomContextMenu); connect(m_tableWidget, &QTableWidget::customContextMenuRequested, this, &UserWidget::showContextMenu); setupContextMenu(); // Добавляем инициализацию контекстного меню // Подключаем сигналы если менеджер уже есть if (m_userManager) { connectSignals(); } // Таймер для периодического обновления m_refreshTimer = new QTimer(this); connect(m_refreshTimer, &QTimer::timeout, this, &UserWidget::onRefreshTimer); m_refreshTimer->start(30000); // 30 секунд refreshData(); } UserWidget::~UserWidget() { if (m_refreshTimer) { m_refreshTimer->stop(); } } void UserWidget::setUserManager(UserManager* userManager) { // Отключаем старые соединения если были if (m_userManager) { disconnectSignals(); } m_userManager = userManager; if (m_userManager) { connectSignals(); } refreshData(); } void UserWidget::connectSignals() { connect(m_userManager, &UserManager::userAdded, this, &UserWidget::onUserAdded); connect(m_userManager, &UserManager::userUpdated, this, &UserWidget::onUserUpdated); connect(m_userManager, &UserManager::userRemoved, this, &UserWidget::onUserRemoved); } void UserWidget::disconnectSignals() { disconnect(m_userManager, &UserManager::userAdded, this, &UserWidget::onUserAdded); disconnect(m_userManager, &UserManager::userUpdated, this, &UserWidget::onUserUpdated); disconnect(m_userManager, &UserManager::userRemoved, this, &UserWidget::onUserRemoved); } void UserWidget::setupUI() { QVBoxLayout* mainLayout = new QVBoxLayout(this); mainLayout->setContentsMargins(5, 5, 5, 5); mainLayout->setSpacing(5); // Панель инструментов QHBoxLayout* toolbarLayout = new QHBoxLayout(); // Фильтр QLabel* filterLabel = new QLabel("Фильтр:", this); m_filterCombo = new QComboBox(this); m_filterCombo->addItem("Все зрители", FilterAll); m_filterCombo->addItem("Модераторы", FilterModerators); m_filterCombo->addItem("VIP", FilterVIPs); m_filterCombo->addItem("Подписчики", FilterSubscribers); m_filterCombo->addItem("Активные (10 мин)", FilterActive); connect(m_filterCombo, QOverload::of(&QComboBox::currentIndexChanged), this, &UserWidget::onFilterChanged); // Поиск QLabel* searchLabel = new QLabel("Поиск:", this); m_searchEdit = new QLineEdit(this); m_searchEdit->setPlaceholderText("Введите имя зрителя..."); m_searchEdit->setClearButtonEnabled(true); connect(m_searchEdit, &QLineEdit::textChanged, this, &UserWidget::onSearchTextChanged); // Кнопка обновления QPushButton* refreshBtn = new QPushButton("Обновить", this); refreshBtn->setFixedWidth(100); connect(refreshBtn, &QPushButton::clicked, this, &UserWidget::onRefreshButtonClicked); toolbarLayout->addWidget(filterLabel); toolbarLayout->addWidget(m_filterCombo); toolbarLayout->addWidget(searchLabel); toolbarLayout->addWidget(m_searchEdit); toolbarLayout->addStretch(); toolbarLayout->addWidget(refreshBtn); // Таблица m_tableWidget = new QTableWidget(this); m_tableWidget->setColumnCount(6); m_tableWidget->setHorizontalHeaderLabels( QStringList() << "Имя" << "Сообщений" << "Статус" << "VIP" << "Подписчик" << "Последняя активность"); // Настройка таблицы m_tableWidget->setSortingEnabled(true); m_tableWidget->setSelectionBehavior(QAbstractItemView::SelectRows); m_tableWidget->setAlternatingRowColors(true); m_tableWidget->setEditTriggers(QAbstractItemView::NoEditTriggers); m_tableWidget->setSelectionMode(QAbstractItemView::SingleSelection); m_tableWidget->horizontalHeader()->setStretchLastSection(true); m_tableWidget->horizontalHeader()->setDefaultAlignment(Qt::AlignLeft); m_tableWidget->verticalHeader()->setVisible(false); // Настройка ширины колонок m_tableWidget->setColumnWidth(0, 250); // Имя m_tableWidget->setColumnWidth(1, 80); // Сообщений m_tableWidget->setColumnWidth(2, 100); // Статус m_tableWidget->setColumnWidth(3, 50); // VIP m_tableWidget->setColumnWidth(4, 80); // Подписчик // Последняя колонка растягивается connect(m_tableWidget, &QTableWidget::cellDoubleClicked, this, &UserWidget::onUserDoubleClicked); // Статистика внизу QHBoxLayout* statsLayout = new QHBoxLayout(); m_statsLabel = new QLabel(this); statsLayout->addWidget(m_statsLabel); statsLayout->addStretch(); // Добавляем всё на форму mainLayout->addLayout(toolbarLayout); mainLayout->addWidget(m_tableWidget); mainLayout->addLayout(statsLayout); setLayout(mainLayout); } void UserWidget::refreshData() { if (!m_userManager) { m_tableWidget->setRowCount(0); m_statsLabel->setText("Менеджер пользователей не установлен"); return; } clearTable(); populateTable(); updateStatistics(); } void UserWidget::populateTable() { if (!m_userManager) return; QVector users; // Получаем пользователей в зависимости от фильтра switch (m_currentFilter) { case FilterAll: for (const User& user : m_userManager->getAllUsers()) { users.append(const_cast(&user)); } break; case FilterModerators: users = m_userManager->getModerators(); break; case FilterVIPs: users = m_userManager->getVIPs(); break; case FilterSubscribers: users = m_userManager->getSubscribers(); break; case FilterActive: users = m_userManager->getActiveUsers(10); break; } // Применяем поисковый фильтр if (!m_searchText.isEmpty()) { QString searchLower = m_searchText.toLower(); QVector filteredUsers; for (User* user : users) { if (user->displayName.toLower().contains(searchLower) || user->login.toLower().contains(searchLower)) { filteredUsers.append(user); } } users = filteredUsers; } // Заполняем таблицу m_tableWidget->setRowCount(users.size()); for (int i = 0; i < users.size(); ++i) { User* user = users[i]; // Имя QTableWidgetItem* nameItem = new QTableWidgetItem(user->displayName); nameItem->setData(Qt::UserRole, user->id); m_tableWidget->setItem(i, 0, nameItem); // Количество сообщений m_tableWidget->setItem(i, 1, new QTableWidgetItem(QString::number(user->messageCount))); // Статус QString status; if (user->isModerator) status = "👑 Модератор"; else if (user->isVIP) status = "⭐ VIP"; else if (user->isSubscriber) status = "💎 Подписчик"; else status = "👤 Зритель"; m_tableWidget->setItem(i, 2, new QTableWidgetItem(status)); // VIP QTableWidgetItem* vipItem = new QTableWidgetItem(user->isVIP ? "Да" : "Нет"); if (user->isVIP) { vipItem->setForeground(Qt::darkGreen); } m_tableWidget->setItem(i, 3, vipItem); // Подписчик QTableWidgetItem* subItem = new QTableWidgetItem(user->isSubscriber ? "Да" : "Нет"); if (user->isSubscriber) { subItem->setForeground(Qt::darkBlue); } m_tableWidget->setItem(i, 4, subItem); // Последняя активность QString lastActive; if (user->lastMessageTime.isValid()) { QDateTime now = QDateTime::currentDateTime(); qint64 secs = user->lastMessageTime.secsTo(now); if (secs < 60) { lastActive = "Только что"; } else if (secs < 3600) { lastActive = QString("%1 мин назад").arg(secs / 60); } else if (secs < 86400) { lastActive = QString("%1 ч назад").arg(secs / 3600); } else { lastActive = user->lastMessageTime.toString("dd.MM.yyyy hh:mm"); } } else { lastActive = "Никогда"; } m_tableWidget->setItem(i, 5, new QTableWidgetItem(lastActive)); } // Автоподгон ширины колонок после заполнения m_tableWidget->resizeColumnsToContents(); } void UserWidget::updateStatistics() { if (!m_userManager) return; QString stats = QString("Зрителей: %1 | Сообщений: %2 | Модераторов: %3 | VIP: %4 | Подписчиков: %5") .arg(m_userManager->getUserCount()) .arg(m_userManager->getMessageCount()) .arg(m_userManager->getModerators().size()) .arg(m_userManager->getVIPs().size()) .arg(m_userManager->getSubscribers().size()); m_statsLabel->setText(stats); } void UserWidget::addUserToTable(const User &user) { int row = m_tableWidget->rowCount(); m_tableWidget->insertRow(row); // Имя QTableWidgetItem* nameItem = new QTableWidgetItem(user.displayName); nameItem->setData(Qt::UserRole, user.id); m_tableWidget->setItem(row, 0, nameItem); // Количество сообщений m_tableWidget->setItem(row, 1, new QTableWidgetItem(QString::number(user.messageCount))); // Статус QString status; if (user.isModerator) status = "👑 Модератор"; else if (user.isVIP) status = "⭐ VIP"; else if (user.isSubscriber) status = "💎 Подписчик"; else status = "👤 Зритель"; m_tableWidget->setItem(row, 2, new QTableWidgetItem(status)); // VIP QTableWidgetItem* vipItem = new QTableWidgetItem(user.isVIP ? "Да" : "Нет"); if (user.isVIP) { vipItem->setForeground(Qt::darkGreen); } m_tableWidget->setItem(row, 3, vipItem); // Подписчик QTableWidgetItem* subItem = new QTableWidgetItem(user.isSubscriber ? "Да" : "Нет"); if (user.isSubscriber) { subItem->setForeground(Qt::darkBlue); } m_tableWidget->setItem(row, 4, subItem); // Последняя активность QString lastActive = "Только что"; m_tableWidget->setItem(row, 5, new QTableWidgetItem(lastActive)); updateStatistics(); } void UserWidget::updateUserInTable(const User &user) { for (int row = 0; row < m_tableWidget->rowCount(); ++row) { QTableWidgetItem* item = m_tableWidget->item(row, 0); if (item && item->data(Qt::UserRole).toString() == user.id) { // Обновляем данные item->setText(user.displayName); m_tableWidget->item(row, 1)->setText( QString::number(user.messageCount)); QString status; if (user.isModerator) status = "👑 Модератор"; else if (user.isVIP) status = "⭐ VIP"; else if (user.isSubscriber) status = "💎 Подписчик"; else status = "👤 Зритель"; m_tableWidget->item(row, 2)->setText(status); m_tableWidget->item(row, 3)->setText(user.isVIP ? "Да" : "Нет"); if (user.isVIP) { m_tableWidget->item(row, 3)->setForeground(Qt::darkGreen); } else { m_tableWidget->item(row, 3)->setForeground(Qt::black); } m_tableWidget->item(row, 4)->setText(user.isSubscriber ? "Да" : "Нет"); if (user.isSubscriber) { m_tableWidget->item(row, 4)->setForeground(Qt::darkBlue); } else { m_tableWidget->item(row, 4)->setForeground(Qt::black); } // Обновляем время активности QString lastActive; if (user.lastMessageTime.isValid()) { QDateTime now = QDateTime::currentDateTime(); qint64 secs = user.lastMessageTime.secsTo(now); if (secs < 60) { lastActive = "Только что"; } else if (secs < 3600) { lastActive = QString("%1 мин назад").arg(secs / 60); } else if (secs < 86400) { lastActive = QString("%1 ч назад").arg(secs / 3600); } else { lastActive = user.lastMessageTime.toString("dd.MM hh:mm"); } } m_tableWidget->item(row, 5)->setText(lastActive); break; } } updateStatistics(); } void UserWidget::removeUserFromTable(const QString &displayName) { // Ищем пользователя по displayName for (int row = 0; row < m_tableWidget->rowCount(); ++row) { QTableWidgetItem* item = m_tableWidget->item(row, 0); if (item && item->text() == displayName) { m_tableWidget->removeRow(row); updateStatistics(); break; } } } void UserWidget::clearTable() { m_tableWidget->setRowCount(0); } void UserWidget::onRefreshTimer() { if (!m_userManager) return; QDateTime now = QDateTime::currentDateTime(); for (int row = 0; row < m_tableWidget->rowCount(); ++row) { QTableWidgetItem* idItem = m_tableWidget->item(row, 0); if (idItem) { QString userId = idItem->data(Qt::UserRole).toString(); User* user = m_userManager->findUserById(userId); if (user && user->lastMessageTime.isValid()) { qint64 secs = user->lastMessageTime.secsTo(now); QString lastActive; if (secs < 60) { lastActive = "Только что"; } else if (secs < 3600) { lastActive = QString("%1 мин назад").arg(secs / 60); } else if (secs < 86400) { lastActive = QString("%1 ч назад").arg(secs / 3600); } else { lastActive = user->lastMessageTime.toString("dd.MM hh:mm"); } // Проверяем, существует ли ячейка if (m_tableWidget->item(row, 5)) { m_tableWidget->item(row, 5)->setText(lastActive); } } } } } bool UserWidget::userMatchesFilter(const User* user) const { if (!user) return false; // Проверка фильтра switch (m_currentFilter) { case FilterAll: break; case FilterModerators: if (!user->isModerator) return false; break; case FilterVIPs: if (!user->isVIP) return false; break; case FilterSubscribers: if (!user->isSubscriber) return false; break; case FilterActive: // Активные за последние 10 минут if (user->lastMessageTime.secsTo(QDateTime::currentDateTime()) > 10*60) return false; break; } // Проверка поиска if (!m_searchText.isEmpty()) { QString searchLower = m_searchText.toLower(); if (!user->displayName.toLower().contains(searchLower) && !user->login.toLower().contains(searchLower)) { return false; } } return true; } void UserWidget::onUserAdded(const User &user) { // Проверяем, проходит ли пользователь текущий фильтр и поиск if (userMatchesFilter(&user)) { addUserToTable(user); } } void UserWidget::onUserUpdated(const User &user) { // Ищем пользователя в таблице int row = findRowByUserId(user.id); if (row >= 0) { // Пользователь уже в таблице if (userMatchesFilter(&user)) { // Обновляем существующую запись updateUserInTable(user); } else { // Удаляем, так как пользователь больше не проходит фильтр removeUserFromTableByUserId(user.id); } } else { // Пользователя нет в таблице if (userMatchesFilter(&user)) { // Добавляем, так как пользователь теперь проходит фильтр addUserToTable(user); } } } int UserWidget::findRowByUserId(const QString &userId) const { for (int row = 0; row < m_tableWidget->rowCount(); ++row) { QTableWidgetItem* item = m_tableWidget->item(row, 0); if (item && item->data(Qt::UserRole).toString() == userId) { return row; } } return -1; } void UserWidget::removeUserFromTableByUserId(const QString &userId) { int row = findRowByUserId(userId); if (row >= 0) { m_tableWidget->removeRow(row); updateStatistics(); } } void UserWidget::onUserRemoved(const QString &userId, const QString &displayName) { Q_UNUSED(displayName); removeUserFromTableByUserId(userId); } void UserWidget::onUserDoubleClicked(int row, int column) { Q_UNUSED(column); if (!m_userManager) return; QTableWidgetItem* item = m_tableWidget->item(row, 0); if (item) { QString userId = item->data(Qt::UserRole).toString(); User* user = m_userManager->findUserById(userId); if (user) { qDebug() << "Детали зрителя:" << "\nИмя:" << user->displayName << "\nID:" << user->id << "\nLogin:" << user->login << "\nСообщений:" << user->messageCount << "\nМодератор:" << user->isModerator << "\nVIP:" << user->isVIP << "\nПодписчик:" << user->isSubscriber << "\nПоследнее сообщение:" << user->lastMessageTime.toString(); // Здесь можно открыть диалог с детальной информацией // или выполнить другие действия } } } void UserWidget::onFilterChanged(int index) { m_currentFilter = static_cast(m_filterCombo->itemData(index).toInt()); refreshData(); } void UserWidget::onSearchTextChanged(const QString &text) { m_searchText = text; refreshData(); } void UserWidget::onRefreshButtonClicked() { refreshData(); } void UserWidget::setupContextMenu() { m_contextMenu = new QMenu(this); m_banAction = m_contextMenu->addAction("Забанить"); connect(m_banAction, &QAction::triggered, this, &UserWidget::onBanUser); m_timeoutAction = m_contextMenu->addAction("Таймаут (5 мин)"); connect(m_timeoutAction, &QAction::triggered, this, &UserWidget::onTimeoutUser); m_unbanAction = m_contextMenu->addAction("Разбанить"); connect(m_unbanAction, &QAction::triggered, this, &UserWidget::onUnbanUser); m_contextMenu->addSeparator(); m_setModAction = m_contextMenu->addAction("Сделать модератором"); connect(m_setModAction, &QAction::triggered, this, &UserWidget::onSetModerator); m_removeModAction = m_contextMenu->addAction("Забрать модератора"); connect(m_removeModAction, &QAction::triggered, this, &UserWidget::onRemoveModerator); m_contextMenu->addSeparator(); m_setVIPAction = m_contextMenu->addAction("Сделать VIP"); connect(m_setVIPAction, &QAction::triggered, this, &UserWidget::onSetVIP); m_removeVIPAction = m_contextMenu->addAction("Забрать VIP"); connect(m_removeVIPAction, &QAction::triggered, this, &UserWidget::onRemoveVIP); m_contextMenu->addSeparator(); m_infoAction = m_contextMenu->addAction("Информация"); connect(m_infoAction, &QAction::triggered, this, &UserWidget::onShowUserInfo); } // Добавляем обработчик контекстного меню: void UserWidget::showContextMenu(const QPoint &pos) { QTableWidgetItem* item = m_tableWidget->itemAt(pos); if (!item) { return; // Клик был не по ячейке (например, по заголовку или пустой области) } int row = item->row(); QTableWidgetItem* nameItem = m_tableWidget->item(row, 0); if (nameItem) { m_selectedUserId = nameItem->data(Qt::UserRole).toString(); m_selectedUserName = nameItem->text(); // Получаем данные пользователя User* user = m_userManager->findUserById(m_selectedUserId); if (user) { // Обновляем состояние пунктов меню m_setModAction->setEnabled(!user->isModerator); m_removeModAction->setEnabled(user->isModerator); m_setVIPAction->setEnabled(!user->isVIP); m_removeVIPAction->setEnabled(user->isVIP); // Показываем меню в позиции клика m_contextMenu->exec(m_tableWidget->viewport()->mapToGlobal(pos)); } } } // Добавляем вспомогательный метод: User* UserWidget::getSelectedUser() { if (!m_selectedUserId.isEmpty()) { return m_userManager->findUserById(m_selectedUserId); } return nullptr; } // Добавляем слоты для обработки действий меню: void UserWidget::onBanUser() { if (!m_selectedUserId.isEmpty() && !m_selectedUserName.isEmpty()) { emit banUserRequested(m_selectedUserId, m_selectedUserName); } } void UserWidget::onTimeoutUser() { if (!m_selectedUserId.isEmpty() && !m_selectedUserName.isEmpty()) { emit timeoutUserRequested(m_selectedUserId, m_selectedUserName, 5); } } void UserWidget::onUnbanUser() { if (!m_selectedUserId.isEmpty() && !m_selectedUserName.isEmpty()) { emit unbanUserRequested(m_selectedUserId, m_selectedUserName); } } void UserWidget::onSetModerator() { if (!m_selectedUserId.isEmpty() && !m_selectedUserName.isEmpty()) { emit setModeratorRequested(m_selectedUserId, m_selectedUserName); } } void UserWidget::onRemoveModerator() { if (!m_selectedUserId.isEmpty() && !m_selectedUserName.isEmpty()) { emit removeModeratorRequested(m_selectedUserId, m_selectedUserName); } } void UserWidget::onSetVIP() { if (!m_selectedUserId.isEmpty() && !m_selectedUserName.isEmpty()) { emit setVIPRequested(m_selectedUserId, m_selectedUserName); } } void UserWidget::onRemoveVIP() { if (!m_selectedUserId.isEmpty() && !m_selectedUserName.isEmpty()) { emit removeVIPRequested(m_selectedUserId, m_selectedUserName); } } void UserWidget::onShowUserInfo() { if (!m_selectedUserId.isEmpty() && !m_selectedUserName.isEmpty()) { emit userInfoRequested(m_selectedUserId, m_selectedUserName); } }