diff --git a/.gitignore b/.gitignore index 14af40c..df40e5b 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ CMakeLists.txt.user .idea/ .vscode/ .vs/ +Android/ *.iml *.suo *.vsidx diff --git a/CMakeLists.txt b/CMakeLists.txt index 0c279e3..b912669 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -18,33 +18,6 @@ else() set(QT_VERSION_MAJOR 6) endif() -#set(PROJECT_SOURCES -# main.cpp -# mainwidget.cpp -# mainwidget.h -# mainwidget.ui -# model/data.h -# resource.qrc -# sessionfriendarea.h -# sessionfriendarea.cpp -# debug.h -# messageshowarea.h -# messageshowarea.cpp -# messageeditarea.h -# messageeditarea.cpp -# SelfInfoWidget.h -# SelfInfoWidget.cpp -# userinfowidget.h -# userinfowidget.cpp -# sessiondetailwidget.h -# sessiondetailwidget.cpp -# choosefrienddialog.h -# choosefrienddialog.cpp -# groupsessiondetailwidget.h -# groupsessiondetailwidget.cpp -#) - - file(GLOB PROJECT_SOURCES model/*.h *.cpp @@ -53,14 +26,20 @@ file(GLOB PROJECT_SOURCES *.qrc ) -if(${QT_VERSION_MAJOR} GREATER_EQUAL 6) +if(ANDROID) + # Android 平台:附加 Android 可部署资源 qt_add_executable(ClientChat MANUAL_FINALIZATION ${PROJECT_SOURCES} ) + # 指定自定义的 Android 部署目录 + set_property(TARGET ClientChat APPEND PROPERTY QT_ANDROID_PACKAGE_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/android") else() - if(ANDROID) - add_library(ClientChat SHARED ${PROJECT_SOURCES}) + if(${QT_VERSION_MAJOR} GREATER_EQUAL 6) + qt_add_executable(ClientChat + MANUAL_FINALIZATION + ${PROJECT_SOURCES} + ) else() add_executable(ClientChat ${PROJECT_SOURCES}) endif() @@ -68,13 +47,12 @@ endif() target_link_libraries(ClientChat PRIVATE Qt${QT_VERSION_MAJOR}::Widgets) -include(GNUInstallDirs) -install(TARGETS ClientChat - BUNDLE DESTINATION . - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} - RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} -) - -if(QT_VERSION_MAJOR EQUAL 6) +if(${QT_VERSION_MAJOR} EQUAL 6) qt_finalize_executable(ClientChat) endif() + +include(GNUInstallDirs) +install(TARGETS ClientChat + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} +) diff --git a/historymessagewidget.cpp b/historymessagewidget.cpp new file mode 100644 index 0000000..5c3f6f5 --- /dev/null +++ b/historymessagewidget.cpp @@ -0,0 +1,238 @@ +#include "historymessagewidget.h" + + +//工厂函数 +HistoryItem* HistoryItem::makeHistoryItem(const Message& message) +{ + // 1. 创建出对象 + HistoryItem* item = new HistoryItem(); + item->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); + + // 2. 创建布局 + QGridLayout* layout = new QGridLayout(); + layout->setVerticalSpacing(0); + layout->setHorizontalSpacing(10); + layout->setContentsMargins(0, 0, 0, 0); + item->setLayout(layout); + + // 3. 创建头像 + QPushButton* avatarBtn = new QPushButton(); + avatarBtn->setFixedSize(40, 40); + avatarBtn->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + avatarBtn->setIconSize(QSize(40, 40)); + // 当前消息发送者的头像 + avatarBtn->setIcon(message.sender.avatar); + avatarBtn->setStyleSheet("QPushButton { border: none; }"); + + // 4. 创建昵称和时间 + QLabel* nameLabel = new QLabel(); + nameLabel->setText(message.sender.nickname + " | " + message.time); + nameLabel->setFixedHeight(20); // 高度设置为头像高度的一半 + nameLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); + + // 5. 消息内容部分 + QWidget* contentWidget = nullptr; + if (message.messageType == model::MessageType::TEXT_TYPE) { + // 文本消息 + QLabel* label = new QLabel(); + label->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + label->setWordWrap(true); + label->setText(QString(message.content)); + label->adjustSize(); // 设置让 label 能够自动调整大小 + contentWidget = label; + } + else if (message.messageType == model::MessageType::IMAGE_TYPE) { + // 图片消息 + //contentWidget = new ImageButton(message.fileId, message.content); + } + else if (message.messageType == model::MessageType::FILE_TYPE) { + // 文件消息 + //contentWidget = new FileLabel(message.fileId, message.fileName); + } + else if (message.messageType == model::MessageType::SPEECH_TYPE) { + // 语音消息 + //contentWidget = new SpeechLabel(message.fileId); + } + else { + LOG() << "错误的消息类型! messageType=" << message.messageType; + } + + // 6. 把上述控件添加到布局中 + layout->addWidget(avatarBtn, 0, 0, 2, 1); + layout->addWidget(nameLabel, 0, 1, 1, 1); + layout->addWidget(contentWidget, 1, 1, 5, 1); + + return item; + +} + +HistoryMessageWidget::HistoryMessageWidget(QWidget *parent) + : QDialog(parent) +{ + // 1. 设置窗口本身属性 + this->setFixedSize(600, 600); + this->setWindowTitle("历史消息"); + this->setWindowIcon(QIcon(":/resource/image/logo.png")); + this->setStyleSheet("QWidget { background-color: rgb(255, 255, 255); }"); + this->setAttribute(Qt::WA_DeleteOnClose); + + // 2. 创建布局管理器. + QGridLayout* layout = new QGridLayout(); + layout->setSpacing(10); + layout->setContentsMargins(30, 30, 30, 0); + this->setLayout(layout); + + // 3. 创建单选按钮 + keyRadioBtn = new QRadioButton(); + timeRadioBtn = new QRadioButton(); + keyRadioBtn->setText("按关键词查询"); + timeRadioBtn->setText("按时间查询"); + // 默认按照关键词查询 + keyRadioBtn->setChecked(true); + layout->addWidget(keyRadioBtn, 0, 0, 1, 2); + layout->addWidget(timeRadioBtn, 0, 2, 1, 2); + + // 4. 创建搜索框 + searchEdit = new QLineEdit(); + searchEdit->setFixedHeight(50); + searchEdit->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); + searchEdit->setPlaceholderText("要搜索的关键词"); + searchEdit->setStyleSheet("QLineEdit { border: none; border-radius: 10px; color: rgb(129, 129, 129); background-color: rgb(240, 240, 240); font-size: 16px; padding-left: 10px; }"); + layout->addWidget(searchEdit, 1, 0, 1, 8); + + // 5. 创建搜索按钮 + QPushButton* searchBtn = new QPushButton(); + searchBtn->setFixedSize(50, 50); + searchBtn->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + searchBtn->setIconSize(QSize(30, 30)); + searchBtn->setIcon(QIcon(":/resource/image/search.png")); + QString btnStyle = "QPushButton { border: none; background-color: rgb(240, 240, 240); border-radius: 10px; }"; + btnStyle += "QPushButton:pressed { background-color: rgb(220, 220, 220); }"; + searchBtn->setStyleSheet(btnStyle); + layout->addWidget(searchBtn, 1, 8, 1, 1); + + // 6. 创建时间相关的部分控件, 初始情况下要隐藏 + QLabel* begTag = new QLabel(); + begTag->setText("开始时间"); + QLabel* endTag = new QLabel(); + endTag->setText("结束时间"); + begTimeEdit = new QDateTimeEdit(); + endTimeEdit = new QDateTimeEdit(); + + begTimeEdit->setStyleSheet("QDateTimeEdit { color: rgb(0, 0, 0); } "); + endTimeEdit->setStyleSheet("QDateTimeEdit { color: rgb(0, 0, 0); } "); + // [联调新增] + begTimeEdit->setDisplayFormat("yyyy-MM-dd hh:mm"); + endTimeEdit->setDisplayFormat("yyyy-MM-dd hh:mm"); + begTimeEdit->setFixedHeight(40); + endTimeEdit->setFixedHeight(40); + + begTag->hide(); + endTag->hide(); + begTimeEdit->hide(); + endTimeEdit->hide(); + + // 7. 创建滚动区域 + initScrollArea(layout); + + // 8. 设置槽函数 + connect(keyRadioBtn, &QRadioButton::clicked, this, [=]() { + // 把时间相关的控件, 隐藏起来 + layout->removeWidget(begTag); + layout->removeWidget(begTimeEdit); + layout->removeWidget(endTag); + layout->removeWidget(endTimeEdit); + begTag->hide(); + begTimeEdit->hide(); + endTag->hide(); + endTimeEdit->hide(); + + // 把关键词搜索框显示加入布局 + layout->addWidget(searchEdit, 1, 0, 1, 8); + searchEdit->show(); + }); + + connect(timeRadioBtn, &QRadioButton::clicked, this, [=]() { + // 关键词搜索框隐藏并从布局中删除掉. + layout->removeWidget(searchEdit); + searchEdit->hide(); + + // 把时间相关的控件, 添加到布局中, 并且进行显示. + layout->addWidget(begTag, 1, 0, 1, 1); + layout->addWidget(begTimeEdit, 1, 1, 1, 3); + layout->addWidget(endTag, 1, 4, 1, 1); + layout->addWidget(endTimeEdit, 1, 5, 1, 3); + begTag->show(); + begTimeEdit->show(); + endTag->show(); + endTimeEdit->show(); + }); + + //connect(searchBtn, &QPushButton::clicked, this, &HistoryMessageWidget::clickSearchBtn); + + // 构造测试数据 +#if TEST_UI + for (int i = 0; i < 30; ++i) { + // 注意此处代码和前面的差别. + // 前面有个代码, UserInfo 必须要 new 出来才能构造. 当时 Item 对象里, 持有了 const UserInfo& , 不是 new 的话 + // 就可能使引用指向的对象失效的. + // 此处后续的代码, 都是按照传值的方式来使用 message 的内容, 不 new 也行. + model::UserInfo sender; + sender.userId = ""; + sender.nickname = "张三" + QString::number(i); + sender.avatar = QIcon(":/resource/image/defaultAvatar.png"); + sender.description = ""; + sender.phone = "18612345678"; + Message message = Message::makeMessage(model::MessageType::TEXT_TYPE, "", sender, QString("消息内容" + QString::number(i)).toUtf8(), ""); + this->addHistoryMessage(message); + } +#endif + +} + +void HistoryMessageWidget::addHistoryMessage(const Message& message) +{ + HistoryItem* item = HistoryItem::makeHistoryItem(message); + container->layout()->addWidget(item); + +} + +void HistoryMessageWidget::clear() +{ + QVBoxLayout* layout = dynamic_cast(container->layout()); + for (int i = layout->count() - 1; i >= 0; i--) { + //之前使用的是takeat,效果和这个是一样的, + QWidget* w = layout->itemAt(i)->widget(); + if (w == nullptr) { + continue; + } + layout->removeWidget(w); + w->deleteLater(); + } +} + +void HistoryMessageWidget::initScrollArea(QGridLayout* layout) +{ + // 1. 创建滚动区域对象 + QScrollArea* scrollArea = new QScrollArea(); + scrollArea->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + scrollArea->setWidgetResizable(true); + scrollArea->verticalScrollBar()->setStyleSheet("QScrollBar:vertical { width: 2px; background-color: rgb(255, 255, 255); }"); + scrollArea->horizontalScrollBar()->setStyleSheet("QScrollBar:horizontal { height: 0; }"); + scrollArea->setStyleSheet("QScrollArea { border: none; }"); + + // 2. 创建 QWidget, 持有要加入的新的内容 + container = new QWidget(); + scrollArea->setWidget(container); + + // 3. 创建 container 中的布局管理器. + QVBoxLayout* vlayout = new QVBoxLayout(); + vlayout->setSpacing(10); + vlayout->setContentsMargins(0, 0, 0, 0); + vlayout->setAlignment(Qt::AlignTop); + container->setLayout(vlayout); + + // 4. 把滚动区加入到整个 layout 中 + layout->addWidget(scrollArea, 2, 0, 1, 9); + +} diff --git a/historymessagewidget.h b/historymessagewidget.h new file mode 100644 index 0000000..9d58a87 --- /dev/null +++ b/historymessagewidget.h @@ -0,0 +1,58 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "debug.h" +#include "model/data.h" + +using model::Message; + +//////////////////////////////////////////////////////////////////// +/// 表示一个历史消息元素 +//////////////////////////////////////////////////////////////////// +class HistoryItem : public QWidget { + Q_OBJECT +public: + HistoryItem() {} + + static HistoryItem* makeHistoryItem(const Message& message); +}; + +//////////////////////////////////////////////////////////////////// +/// 展示历史消息窗口 +//////////////////////////////////////////////////////////////////// +class HistoryMessageWidget : public QDialog +{ + Q_OBJECT + +public: + HistoryMessageWidget(QWidget *parent); + + //在窗口中添加一个历史消息 + void addHistoryMessage(const Message& message); + + //清空窗口中所有的历史消息 + void clear(); + +private: + void initScrollArea(QGridLayout* layout); + + QRadioButton* keyRadioBtn; + QRadioButton* timeRadioBtn; + QLineEdit* searchEdit; + + QDateTimeEdit* begTimeEdit; + QDateTimeEdit* endTimeEdit; + + //持有所有的历史消息结果的容器对象 + QWidget* container; +}; diff --git a/messageeditarea.cpp b/messageeditarea.cpp index 3ece097..15aebb6 100644 --- a/messageeditarea.cpp +++ b/messageeditarea.cpp @@ -177,6 +177,10 @@ MessageEditArea::MessageEditArea(QWidget *parent) vlayout->addWidget(sendTextButton, 0, Qt::AlignRight | Qt::AlignVCenter); + connect(showHistoryBtn, &QPushButton::clicked, this, [=]() { + HistoryMessageWidget* historyMessageWidget = new HistoryMessageWidget(this); + historyMessageWidget->exec(); + }); } diff --git a/messageeditarea.h b/messageeditarea.h index d168201..f51e16c 100644 --- a/messageeditarea.h +++ b/messageeditarea.h @@ -11,6 +11,8 @@ //#include //#include +#include "historymessagewidget.h" + //编辑消息的区域 class MessageEditArea : public QWidget {