diff --git a/src/DBLayer.cpp b/src/DBLayer.cpp index bca961b..1e684a1 100644 --- a/src/DBLayer.cpp +++ b/src/DBLayer.cpp @@ -1,8 +1,10 @@ #include "DBLayer.h" - +#include "Utility.h" +#include +#include +#include #include -#include "Utility.h" DBLayer::DBLayer(std::string db_file_name) : db_file_name_(std::move(db_file_name)) { @@ -70,7 +72,8 @@ DBLayer::DBLayer(std::string db_file_name) : db_file_name_(std::move(db_file_nam "userId INTEGER, " "recordDate TEXT, " "recordTime TEXT, " - "record TEXT)"; + "recordDuration TEXT, " + "recordMark TEXT)"; if (!query.exec(pomoSql)) { qDebug() << "PomodoroTable 创建失败:" << query.lastError().text(); @@ -193,7 +196,7 @@ std::vector DBLayer::getHabitLists() const return habits; } -bool DBLayer::insertHabit(const Habit &habit) +bool DBLayer::insertHabit(const Habit &habit) const { if (!openDatabase()) { @@ -222,7 +225,7 @@ bool DBLayer::insertHabit(const Habit &habit) return true; } -bool DBLayer::updateHabit(const Habit &habit) +bool DBLayer::updateHabit(const Habit &habit) const { if (!openDatabase()) { @@ -253,7 +256,7 @@ bool DBLayer::updateHabit(const Habit &habit) return true; } -bool DBLayer::deleteHabit(std::size_t habit_id) +bool DBLayer::deleteHabit(std::size_t habit_id) const { if (!openDatabase()) { @@ -274,7 +277,7 @@ bool DBLayer::deleteHabit(std::size_t habit_id) return true; } -bool DBLayer::insertHabitRecord(const Habit &habit) +bool DBLayer::insertHabitRecord(const Habit &habit) const { if (!openDatabase()) { @@ -341,7 +344,7 @@ std::vector DBLayer::getEventLists() const return events; } -bool DBLayer::insertEvent(const Event &event) +bool DBLayer::insertEvent(const Event &event) const { if (!openDatabase()) { @@ -371,7 +374,7 @@ bool DBLayer::insertEvent(const Event &event) return true; } -bool DBLayer::updateEvent(const Event &event) +bool DBLayer::updateEvent(const Event &event) const { if (!openDatabase()) { @@ -403,7 +406,7 @@ bool DBLayer::updateEvent(const Event &event) return true; } -bool DBLayer::deleteEvent(std::size_t event_id) +bool DBLayer::deleteEvent(std::size_t event_id) const { if (!openDatabase()) { @@ -424,31 +427,39 @@ bool DBLayer::deleteEvent(std::size_t event_id) return true; } -void DBLayer::insertPomoRecord(Pomodoro pomo) +void DBLayer::insertPomoRecord(const Pomodoro& pomo) const { if (!openDatabase()) { - qDebug() << "数据库打开失败,无法插入番茄钟记录"; + qDebug() << "数据库打开失败:" << db_.lastError().text(); return; } - Utility utility; + + QDateTime currentDateTime = QDateTime::currentDateTime(); + QString dateStr = currentDateTime.toString("yyyy-MM-dd"); + QString timeStr = currentDateTime.toString("HH:mm:ss"); QSqlQuery query(db_); - query.prepare("INSERT INTO PomodoroTable (userId, recordDate, recordTime, record) " - "VALUES (:userId, :recordDate, :recordTime, :record)"); - query.bindValue(":userId", 0); // 假设 userId 为 0 - query.bindValue(":recordDate", QString::fromStdString(toString(utility.getCurrentTimeStamp().first))); - query.bindValue(":recordTime", QString::fromStdString(toString(pomo.pomodoro_time))); - query.bindValue(":record", QString::fromStdString(pomo.record)); + query.prepare("INSERT INTO PomodoroTable (userId, recordDate, recordTime, recordDuration, recordMark) " + "VALUES (:userId, :recordDate, :recordTime, :recordDuration, :recordMark)"); + + // 确保所有参数都正确绑定 + query.bindValue(":userId", getCurrentUserID()); + query.bindValue(":recordDate", dateStr); + query.bindValue(":recordTime", timeStr); + query.bindValue(":recordDuration", QString::fromStdString(toString(pomo.pomodoro_duration))); + query.bindValue(":recordMark", QString::fromStdString(pomo.record)); if (!query.exec()) { qDebug() << "插入番茄钟记录失败:" << query.lastError().text(); + qDebug() << "执行的SQL:" << query.lastQuery(); + qDebug() << "绑定的值:" << query.boundValues(); } closeDatabase(); } -DateRecord DBLayer::getRecordbyDate(Date date) const +DateRecord DBLayer::getRecordByDate(Date date) const { std::vector> habit_records; std::vector> pomodoro_records; @@ -492,28 +503,28 @@ DateRecord DBLayer::getRecordbyDate(Date date) const } // 查询指定日期的事项记录 - QSqlQuery eventQuery(db_); - eventQuery.prepare("SELECT * FROM EventTable WHERE eventDate = :date AND isDeleted = 0"); - eventQuery.bindValue(":date", dateStr); + QSqlQuery event_query(db_); + event_query.prepare("SELECT * FROM EventTable WHERE eventDate = :date AND isDeleted = 0"); + event_query.bindValue(":date", dateStr); - if (!eventQuery.exec()) + if (!event_query.exec()) { - qDebug() << "查询指定日期的事项记录失败:" << eventQuery.lastError().text(); + qDebug() << "查询指定日期的事项记录失败:" << event_query.lastError().text(); } else { - while (eventQuery.next()) + while (event_query.next()) { Event event{ - eventQuery.value("eventId").toULongLong(), - eventQuery.value("userId").toULongLong(), - eventQuery.value("title").toString().toStdString(), - dateFromString(eventQuery.value("eventDate").toString().toStdString()), - timeFromString(eventQuery.value("eventTime").toString().toStdString()), - eventQuery.value("remindFlag").toBool(), - timeFromString(eventQuery.value("remindTime").toString().toStdString()), - eventQuery.value("isExpiredFlag").toBool(), - eventQuery.value("isDeleted").toBool()}; + event_query.value("eventId").toULongLong(), + event_query.value("userId").toULongLong(), + event_query.value("title").toString().toStdString(), + dateFromString(event_query.value("eventDate").toString().toStdString()), + timeFromString(event_query.value("eventTime").toString().toStdString()), + event_query.value("remindFlag").toBool(), + timeFromString(event_query.value("remindTime").toString().toStdString()), + event_query.value("isExpiredFlag").toBool(), + event_query.value("isDeleted").toBool()}; // 使用事项的时间作为记录时间 Time time = event.event_time; @@ -522,30 +533,32 @@ DateRecord DBLayer::getRecordbyDate(Date date) const } // 查询指定日期的番茄钟使用记录 - QSqlQuery pomodoroQuery(db_); - pomodoroQuery.prepare("SELECT * FROM PomodoroTable WHERE recordDate = :date"); - pomodoroQuery.bindValue(":date", dateStr); + QSqlQuery pomodoro_query(db_); + pomodoro_query.prepare("SELECT * FROM PomodoroTable WHERE recordDate = :date"); + pomodoro_query.bindValue(":date", dateStr); - if (!pomodoroQuery.exec()) + if (!pomodoro_query.exec()) { - qDebug() << "查询指定日期的番茄钟使用记录失败:" << pomodoroQuery.lastError().text(); + qDebug() << "查询指定日期的番茄钟使用记录失败:" << pomodoro_query.lastError().text(); } else { - while (pomodoroQuery.next()) + while (pomodoro_query.next()) { // 提取数据库中的字段 - std::size_t id = pomodoroQuery.value("pomoId").toULongLong(); - QString recordTimeStr = pomodoroQuery.value("recordTime").toString(); - std::string record = pomodoroQuery.value("record").toString().toStdString(); + std::size_t id = pomodoro_query.value("pomoId").toULongLong(); + QString record_end_time = pomodoro_query.value("recordTime").toString(); + QString record_duration_str = pomodoro_query.value("recordDuration").toString(); + std::string record = pomodoro_query.value("recordMark").toString().toStdString(); // 将时间字符串转换为 Time 对象 - Time time = timeFromString(recordTimeStr.toStdString()); + Time duration = timeFromString(record_duration_str.toStdString()); + Time end_time = timeFromString(record_end_time.toStdString()); // 构造 Pomodoro 对象 - Pomodoro pomodoro(id, time, record); + Pomodoro pomodoro(id, duration, record); - pomodoro_records.emplace_back(time, pomodoro); + pomodoro_records.emplace_back(end_time, pomodoro); } } @@ -553,7 +566,7 @@ DateRecord DBLayer::getRecordbyDate(Date date) const return DateRecord(habit_records, pomodoro_records, event_records); } -int DBLayer::getHabitIDMax() +int DBLayer::getHabitIDMax() const { if (!openDatabase()) { @@ -580,7 +593,7 @@ int DBLayer::getHabitIDMax() return maxId; } -int DBLayer::getEventIDMax() +int DBLayer::getEventIDMax() const { if (!openDatabase()) { @@ -607,7 +620,7 @@ int DBLayer::getEventIDMax() return maxId; } -int DBLayer::getPomoIDMax() +int DBLayer::getPomoIDMax() const { if (!openDatabase()) { @@ -747,32 +760,17 @@ std::size_t DBLayer::getCurrentUserID() const { std::size_t currentUserId = 0; - if (!openDatabase()) - { - qDebug() << "数据库打开失败,无法获取当前用户ID"; - return currentUserId; - } - QSqlQuery query(db_); query.prepare("SELECT userId FROM UserSettingsTable WHERE isLoggedIn = 1 LIMIT 1"); - if (!query.exec()) - { - qDebug() << "查询当前用户ID失败:" << query.lastError().text(); - closeDatabase(); - return currentUserId; - } - - if (query.next()) - { + if (query.exec() && query.next()) { currentUserId = query.value("userId").toULongLong(); } - closeDatabase(); return currentUserId; } -bool DBLayer::setInactiveHabit(std::size_t habit_id) +bool DBLayer::setInactiveHabit(const std::size_t habit_id) const { if (!openDatabase()) { @@ -803,7 +801,7 @@ bool DBLayer::setInactiveHabit(std::size_t habit_id) return true; } -bool DBLayer::setActiveHabit(std::size_t habit_id) +bool DBLayer::setActiveHabit(std::size_t habit_id) const { if (!openDatabase()) { @@ -834,7 +832,7 @@ bool DBLayer::setActiveHabit(std::size_t habit_id) return true; } -bool DBLayer::savePomodoroState(int state, int total_seconds, int remaining_seconds, const std::string& remark, const std::string& start_time) +bool DBLayer::savePomodoroState(int state, int total_seconds, int remaining_seconds, const std::string& remark, const std::string& start_time) const { if (!openDatabase()) { @@ -882,7 +880,7 @@ bool DBLayer::savePomodoroState(int state, int total_seconds, int remaining_seco return true; } -bool DBLayer::loadPomodoroState(int& state, int& total_seconds, int& remaining_seconds, std::string& remark, std::string& start_time) +bool DBLayer::loadPomodoroState(int& state, int& total_seconds, int& remaining_seconds, std::string& remark, std::string& start_time) const { if (!openDatabase()) { @@ -934,7 +932,7 @@ bool DBLayer::loadPomodoroState(int& state, int& total_seconds, int& remaining_s return false; // 没有找到状态记录 } -bool DBLayer::clearPomodoroState() +bool DBLayer::clearPomodoroState() const { if (!openDatabase()) { diff --git a/src/DBLayer.h b/src/DBLayer.h index 83f6d9b..61ecbb4 100644 --- a/src/DBLayer.h +++ b/src/DBLayer.h @@ -9,9 +9,7 @@ #define DBLAYER_H #include -#include #include -#include #include #include #include "DateRecord.h" @@ -42,7 +40,6 @@ struct UserSettings */ class DBLayer { -private: std::string db_file_name_; /**< 数据库文件名称 */ mutable QSqlDatabase db_; /**< Qt数据库对象 */ @@ -92,7 +89,7 @@ class DBLayer * @return 插入是否成功,成功返回true,失败返回false * @details 将习惯对象的数据插入到数据库的习惯表中 */ - bool insertHabit(const Habit &habit); + bool insertHabit(const Habit &habit) const; /** * @brief 更新指定ID的习惯 @@ -101,7 +98,7 @@ class DBLayer * @return 更新是否成功,成功返回true,失败返回false * @details 根据习惯ID更新数据库中对应的习惯记录 */ - bool updateHabit(const Habit &habit); + bool updateHabit(const Habit &habit) const; /** * @brief 删除指定ID的习惯 @@ -110,7 +107,7 @@ class DBLayer * @return 删除是否成功,成功返回true,失败返回false * @details 从数据库中删除指定ID的习惯记录 */ - bool deleteHabit(std::size_t habit_id); + bool deleteHabit(std::size_t habit_id) const; /** * @brief 插入一条习惯记录至记录表 @@ -119,7 +116,7 @@ class DBLayer * @return 插入是否成功,成功返回true,失败返回false * @details 向数据库的记录表中插入一条习惯打卡记录,需要获取系统时间,建立打卡时间与记录的映射关系。 */ - bool insertHabitRecord(const Habit &habit); + bool insertHabitRecord(const Habit &habit) const; /** * @brief 获取所有事项列表 @@ -136,7 +133,7 @@ class DBLayer * @return 插入是否成功,成功返回true,失败返回false * @details 将事项对象的数据插入到数据库的事项表中 */ - bool insertEvent(const Event &event); + bool insertEvent(const Event &event) const; /** * @brief 更新指定ID的事项 @@ -145,7 +142,7 @@ class DBLayer * @return 更新是否成功,成功返回true,失败返回false * @details 根据事项ID更新数据库中对应的事项记录 */ - bool updateEvent(const Event &event); + bool updateEvent(const Event &event) const; /** * @brief 删除指定ID的事项 @@ -154,14 +151,14 @@ class DBLayer * @return 删除是否成功,成功返回true,失败返回false * @details 从数据库中删除指定ID的事项记录 */ - bool deleteEvent(std::size_t event_id); + bool deleteEvent(std::size_t event_id) const; /** * @brief 插入一条番茄钟记录 * @author XTUG * @param pomo 需要插入的番茄钟记录 */ - void insertPomoRecord(Pomodoro pomo); + void insertPomoRecord(const Pomodoro& pomo) const; /** * @brief 根据日期获取记录 @@ -169,28 +166,28 @@ class DBLayer * @param date * @return 指定日期的所有记录(包括习惯打卡记录、事项记录、番茄钟专注记录) */ - DateRecord getRecordbyDate(Date date) const; + DateRecord getRecordByDate(Date date) const; /** * @brief 获取数据库中当前最大的 Habit ID * @author XTUG * @return 数据库中的最大 Habit ID */ - int getHabitIDMax(); + int getHabitIDMax() const; /** * @brief 获取数据库中当前最大的 Event ID * @author 遥远 * @return 数据库中的最大 Event ID */ - int getEventIDMax(); + int getEventIDMax() const; /** * @brief 获取数据库中当前最大的 Pomo ID * @author 遥远 * @return 数据库中的最大 Pomo ID */ - int getPomoIDMax(); + int getPomoIDMax() const; /** * @brief 获取用户设置 @@ -225,7 +222,7 @@ class DBLayer * @return 停用是否成功,成功返回true,失败返回false * @details 从数据库中停用指定ID的习惯 */ - bool setInactiveHabit(std::size_t habit_id); + bool setInactiveHabit(std::size_t habit_id) const; /** * @brief 启用指定ID的习惯 @@ -234,7 +231,7 @@ class DBLayer * @return 启用是否成功,成功返回true,失败返回false * @details 从数据库中启用指定ID的习惯 */ - bool setActiveHabit(std::size_t habit_id); + bool setActiveHabit(std::size_t habit_id) const; /** * @brief 保存番茄钟状态到数据库 @@ -246,7 +243,7 @@ class DBLayer * @param start_time 开始时间戳 * @return 保存是否成功 */ - bool savePomodoroState(int state, int total_seconds, int remaining_seconds, const std::string& remark, const std::string& start_time); + bool savePomodoroState(int state, int total_seconds, int remaining_seconds, const std::string& remark, const std::string& start_time) const; /** * @brief 从数据库加载番茄钟状态 @@ -258,14 +255,14 @@ class DBLayer * @param start_time 输出:开始时间戳 * @return 加载是否成功 */ - bool loadPomodoroState(int& state, int& total_seconds, int& remaining_seconds, std::string& remark, std::string& start_time); + bool loadPomodoroState(int& state, int& total_seconds, int& remaining_seconds, std::string& remark, std::string& start_time) const; /** * @brief 清除番茄钟状态 * @author 遥远 * @return 清除是否成功 */ - bool clearPomodoroState(); + bool clearPomodoroState() const; }; #endif // DBLAYER_H diff --git a/src/MainView.cpp b/src/MainView.cpp new file mode 100644 index 0000000..e1ccfbe --- /dev/null +++ b/src/MainView.cpp @@ -0,0 +1,448 @@ +#include "MainView.h" +#include +#include +#include +#include +#include +#include + +MainView::MainView(ServiceLayer& service, PomodoroWidget& pomodoro, QWidget *parent) + : QWidget(parent), service_(service), pomodoro_(pomodoro) +{ + // 初始化对话框文本 + dialogTexts_ = { + "这是今天需要完成的事情哦~", + "加油!你一定可以坚持下去!", + "别忘了打卡和休息哦!", + "每一天都值得被记录!", + "习惯的力量很强大!" + }; + + initUI(); + setupConnections(); +} + +void MainView::initUI() +{ + // 主布局 + mainLayout_ = new QGridLayout(this); + mainLayout_->setContentsMargins(15, 15, 15, 15); + mainLayout_->setSpacing(15); + + // 设置行列比例 + mainLayout_->setColumnStretch(0, 2); // 习惯区域占2份 + mainLayout_->setColumnStretch(1, 1); // 右侧区域占1份 + mainLayout_->setRowStretch(0, 2); // 上部分占2份 + mainLayout_->setRowStretch(1, 1); // 下部分占1份 + + setupHabitsSection(); + setupEventsSection(); + setupPomodoroSection(); + setupMascotSection(); + + refreshAll(); +} + +void MainView::setupConnections() +{ + // 连接番茄钟状态变化信号 + connect(&pomodoro_, &PomodoroWidget::stateChanged, this, &MainView::refreshPomodoro); + connect(&pomodoro_, &PomodoroWidget::timerUpdated, this, &MainView::refreshPomodoro); +} + +void MainView::setupHabitsSection() +{ + habitsGroup_ = new QGroupBox("待打卡习惯", this); + habitsGroup_->setStyleSheet("QGroupBox { font-size: 16px; font-weight: bold; }"); + + habitsScrollArea_ = new QScrollArea(habitsGroup_); + habitsScrollArea_->setWidgetResizable(true); + habitsScrollArea_->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + + habitsContainer_ = new QWidget(); + habitsLayout_ = new QVBoxLayout(habitsContainer_); + habitsLayout_->setAlignment(Qt::AlignTop); + habitsLayout_->setSpacing(10); + + habitsScrollArea_->setWidget(habitsContainer_); + + QVBoxLayout* groupLayout = new QVBoxLayout(habitsGroup_); + groupLayout->addWidget(habitsScrollArea_); + + mainLayout_->addWidget(habitsGroup_, 0, 0, 2, 1); +} + +void MainView::setupEventsSection() +{ + eventsGroup_ = new QGroupBox("活跃事项", this); + eventsGroup_->setStyleSheet("QGroupBox { font-size: 16px; font-weight: bold; }"); + + eventsScrollArea_ = new QScrollArea(eventsGroup_); + eventsScrollArea_->setWidgetResizable(true); + eventsScrollArea_->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + + eventsContainer_ = new QWidget(); + eventsLayout_ = new QVBoxLayout(eventsContainer_); + eventsLayout_->setAlignment(Qt::AlignTop); + eventsLayout_->setSpacing(10); + + eventsScrollArea_->setWidget(eventsContainer_); + + QVBoxLayout* groupLayout = new QVBoxLayout(eventsGroup_); + groupLayout->addWidget(eventsScrollArea_); + + mainLayout_->addWidget(eventsGroup_, 0, 1); +} + +void MainView::setupPomodoroSection() +{ + pomodoroGroup_ = new QGroupBox("当前番茄钟", this); + pomodoroGroup_->setStyleSheet("QGroupBox { font-size: 16px; font-weight: bold; }"); + + QVBoxLayout* layout = new QVBoxLayout(pomodoroGroup_); + layout->setAlignment(Qt::AlignCenter); + + pomodoroTimeLabel_ = new QLabel("00:00:00", pomodoroGroup_); + pomodoroTimeLabel_->setStyleSheet("font-size: 24px; font-weight: bold; color: #1890ff;"); + pomodoroTimeLabel_->setAlignment(Qt::AlignCenter); + + pomodoroRemarkLabel_ = new QLabel("", pomodoroGroup_); + pomodoroRemarkLabel_->setStyleSheet("font-size: 16px; color: #666;"); + pomodoroRemarkLabel_->setAlignment(Qt::AlignCenter); + pomodoroRemarkLabel_->setWordWrap(true); + + layout->addWidget(pomodoroTimeLabel_); + layout->addWidget(pomodoroRemarkLabel_); + + mainLayout_->addWidget(pomodoroGroup_, 1, 1); +} + +void MainView::setupMascotSection() +{ + mascotGroup_ = new QGroupBox("", this); + mascotGroup_->setStyleSheet("QGroupBox { border: none; }"); + + // 外层横向布局(撑开,把吉祥物部分推到右侧) + QHBoxLayout* outerLayout = new QHBoxLayout(mascotGroup_); + outerLayout->addStretch(); // 左边留空,把内容推到右边 + + // 右侧垂直布局(对话框在上,吉祥物在下) + QHBoxLayout* rightLayout = new QHBoxLayout(); + + // 对话框 + QGroupBox* dialogGroup = new QGroupBox(mascotGroup_); + dialogGroup->setStyleSheet( + "QGroupBox {" + " background: #fffbe6;" + " border: 2px solid #ffd666;" + " border-radius: 16px;" + " padding: 16px;" + " min-width: 220px;" + " max-width: 320px;" + "}" + ); + + dialogLabel_ = new QLabel(dialogTexts_.first(), dialogGroup); + dialogLabel_->setStyleSheet( + "QLabel {" + " color: #ad6800;" + " font-size: 18px;" + " font-weight: bold;" + " padding: 4px 8px;" + " background: transparent;" + "}" + ); + dialogLabel_->setWordWrap(true); + dialogLabel_->setAlignment(Qt::AlignLeft | Qt::AlignVCenter); + + QVBoxLayout* dialogLayout = new QVBoxLayout(dialogGroup); + dialogLayout->addWidget(dialogLabel_); + + // 吉祥物 + mascotLabel_ = new QLabel(mascotGroup_); + QPixmap mascotPixmap(":/assets/images/logo.png"); + if (!mascotPixmap.isNull()) { + mascotLabel_->setPixmap( + mascotPixmap.scaled(120, 120, Qt::KeepAspectRatio, Qt::SmoothTransformation)); + } else { + mascotLabel_->setText("LOGO"); + } + mascotLabel_->setAlignment(Qt::AlignRight | Qt::AlignBottom); + + // 添加到右侧布局 + rightLayout->addWidget(dialogGroup, 0, Qt::AlignRight | Qt::AlignTop); + rightLayout->addWidget(mascotLabel_, 0, Qt::AlignRight | Qt::AlignBottom); + + // 把右侧整体加入外层布局 + outerLayout->addLayout(rightLayout); + + mainLayout_->addWidget(mascotGroup_, 2, 1); +} + + +void MainView::refreshAll() +{ + refreshTodoArea(); + refreshMascotDialog(); +} + +void MainView::refreshTodoArea() +{ + refreshHabits(); + refreshEvents(); + refreshPomodoro(); +} + +void MainView::refreshHabits() +{ + clearLayout(habitsLayout_); + + auto habits = service_.getActiveHabits(); + if (habits.empty()) { + QLabel* noHabitLabel = new QLabel("暂无习惯", habitsContainer_); + noHabitLabel->setAlignment(Qt::AlignCenter); + noHabitLabel->setStyleSheet("color: #888; font-size: 16px;"); + habitsLayout_->addWidget(noHabitLabel); + } else { + for (const auto& habit : habits) { + habitsLayout_->addWidget(createHabitCard(habit)); + } + } +} + +void MainView::refreshEvents() +{ + clearLayout(eventsLayout_); + + auto events = service_.getActiveEvents(); + if (events.empty()) { + QLabel* noEventLabel = new QLabel("暂无事项", eventsContainer_); + noEventLabel->setAlignment(Qt::AlignCenter); + noEventLabel->setStyleSheet("color: #888; font-size: 16px;"); + eventsLayout_->addWidget(noEventLabel); + } else { + for (const auto& event : events) { + eventsLayout_->addWidget(createEventCard(event)); + } + } +} + +void MainView::refreshPomodoro() const +{ + if (pomodoro_.getState() != PomodoroWidget::IDLE) { + pomodoroTimeLabel_->setText(pomodoro_.getTimeDisplayText()); + pomodoroRemarkLabel_->setText(pomodoro_.getRemark()); + pomodoroRemarkLabel_->show(); + } else { + pomodoroTimeLabel_->setText("暂无番茄钟"); + pomodoroRemarkLabel_->hide(); + } +} + +void MainView::refreshMascotDialog() +{ + int index = QRandomGenerator::global()->bounded(dialogTexts_.size()); + dialogLabel_->setText(dialogTexts_[index]); +} + +QWidget* MainView::createHabitCard(const Habit& habit) +{ + QWidget* card = new QWidget(habitsContainer_); + card->setStyleSheet( + "QWidget {" + " background: white;" + " border: 1px solid #e0e0e0;" + " border-radius: 8px;" + " padding: 10px;" + "}" + ); + + QHBoxLayout* layout = new QHBoxLayout(card); + layout->setContentsMargins(5, 5, 5, 5); + layout->setSpacing(10); + + // 习惯信息 + QLabel* infoLabel = new QLabel( + QString("%1 (%2/%3)") + .arg(QString::fromStdString(habit.name)) + .arg(service_.getHabitCheckInCount(habit)) + .arg(habit.target_count), + card + ); + infoLabel->setStyleSheet("font-size: 14px;"); + + // 打卡按钮 + QPushButton* checkBtn = new QPushButton("✅", card); + checkBtn->setFixedSize(60, 25); + checkBtn->setStyleSheet( + "QPushButton {" + " background-color: #52c41a;" + " color: white;" + " border: none;" + " border-radius: 4px;" + " font-size: 12px;" + "}" + "QPushButton:hover {" + " background-color: #389e0d;" + "}" + "QPushButton:disabled {" + " background-color: #d9d9d9;" + "}" + ); + + // 如果已完成打卡目标,禁用按钮 + if (service_.getHabitCheckInCount(habit) >= habit.target_count) { + checkBtn->setEnabled(false); + } + + // 编辑按钮 + QPushButton* editBtn = new QPushButton("✏️", card); + editBtn->setFixedSize(50, 25); + editBtn->setStyleSheet( + "QPushButton {" + " background-color: #faad14;" + " color: white;" + " border: none;" + " border-radius: 4px;" + " font-size: 12px;" + "}" + "QPushButton:hover {" + " background-color: #d48806;" + "}" + ); + + // 删除按钮 + QPushButton* deleteBtn = new QPushButton("🗑️", card); + deleteBtn->setFixedSize(50, 25); + deleteBtn->setStyleSheet( + "QPushButton {" + " background-color: #ff4d4f;" + " color: white;" + " border: none;" + " border-radius: 4px;" + " font-size: 12px;" + "}" + "QPushButton:hover {" + " background-color: #cf1322;" + "}" + ); + + // 连接信号 + connect(checkBtn, &QPushButton::clicked, this, [this, habit] + { + if (service_.checkinHabit(habit)) { + emit habitCheckedIn(habit); + refreshHabits(); + } + }); + + connect(editBtn, &QPushButton::clicked, this, [this, habit] + { + emit habitEdited(habit); + }); + + connect(deleteBtn, &QPushButton::clicked, this, [this, habit] + { + if (QMessageBox::question(this, "确认删除", + QString("确定删除习惯「%1」吗?").arg(QString::fromStdString(habit.name))) == QMessageBox::Yes) { + emit habitDeleted(habit); + } + }); + + layout->addWidget(infoLabel, 1); + layout->addWidget(checkBtn); + layout->addWidget(editBtn); + layout->addWidget(deleteBtn); + + return card; +} + +QWidget* MainView::createEventCard(const Event& event) +{ + QWidget* card = new QWidget(eventsContainer_); + card->setStyleSheet( + "QWidget {" + " background: white;" + " border: 1px solid #e0e0e0;" + " border-radius: 8px;" + " padding: 10px;" + "}" + ); + + QHBoxLayout* layout = new QHBoxLayout(card); + layout->setContentsMargins(5, 5, 5, 5); + layout->setSpacing(10); + + // 事项信息 + QLabel* infoLabel = new QLabel( + QString("%1\n%2 %3") + .arg(QString::fromStdString(event.title)) + .arg(QString::fromStdString(toString(event.event_date))) + .arg(QString::fromStdString(toString(event.event_time))), + card + ); + infoLabel->setStyleSheet("font-size: 14px;"); + + // 编辑按钮 + QPushButton* editBtn = new QPushButton("✏️", card); + editBtn->setFixedSize(50, 25); + editBtn->setStyleSheet( + "QPushButton {" + " background-color: #1890ff;" + " color: white;" + " border: none;" + " border-radius: 4px;" + " font-size: 12px;" + "}" + "QPushButton:hover {" + " background-color: #096dd9;" + "}" + ); + + // 删除按钮 + QPushButton* deleteBtn = new QPushButton("🗑️", card); + deleteBtn->setFixedSize(50, 25); + deleteBtn->setStyleSheet( + "QPushButton {" + " background-color: #ff4d4f;" + " color: white;" + " border: none;" + " border-radius: 4px;" + " font-size: 12px;" + "}" + "QPushButton:hover {" + " background-color: #cf1322;" + "}" + ); + + // 连接信号 + connect(editBtn, &QPushButton::clicked, this, [this, event]() { + emit eventEdited(event); + }); + + connect(deleteBtn, &QPushButton::clicked, this, [this, event]() { + if (QMessageBox::question(this, "确认删除", + QString("确定删除事项「%1」吗?").arg(QString::fromStdString(event.title))) == QMessageBox::Yes) { + emit eventDeleted(event); + } + }); + + layout->addWidget(infoLabel, 1); + layout->addWidget(editBtn); + layout->addWidget(deleteBtn); + + return card; +} + +void MainView::clearLayout(QLayout* layout) +{ + if (!layout) return; + + QLayoutItem* item; + while ((item = layout->takeAt(0))) { + if (item->widget()) { + delete item->widget(); + } + delete item; + } +} \ No newline at end of file diff --git a/src/MainView.h b/src/MainView.h new file mode 100644 index 0000000..7401a2a --- /dev/null +++ b/src/MainView.h @@ -0,0 +1,81 @@ +#ifndef MAINVIEW_H +#define MAINVIEW_H + +#include +#include +#include +#include +#include "Habit.h" +#include "Event.h" +#include "PomodoroWidget.h" +#include "ServiceLayer.h" + +class MainView final : public QWidget +{ + Q_OBJECT + + void initUI(); + void setupConnections(); + void setupHabitsSection(); + void setupEventsSection(); + void setupPomodoroSection(); + void setupMascotSection(); + + QWidget* createHabitCard(const Habit& habit); + QWidget* createEventCard(const Event& event); + static void clearLayout(QLayout* layout); + + ServiceLayer& service_; + PomodoroWidget& pomodoro_; + + // UI组件 + QGridLayout* mainLayout_; + + // 习惯区域 + QGroupBox* habitsGroup_; + QScrollArea* habitsScrollArea_; + QWidget* habitsContainer_; + QVBoxLayout* habitsLayout_; + + // 事项区域 + QGroupBox* eventsGroup_; + QScrollArea* eventsScrollArea_; + QWidget* eventsContainer_; + QVBoxLayout* eventsLayout_; + + // 番茄钟区域 + QGroupBox* pomodoroGroup_; + QLabel* pomodoroTimeLabel_; + QLabel* pomodoroRemarkLabel_; + + // 吉祥物区域 + QGroupBox* mascotGroup_; + QLabel* mascotLabel_; + QLabel* dialogLabel_; + + // 对话框文本 + QVector dialogTexts_; + + public: + explicit MainView(ServiceLayer& service, PomodoroWidget& pomodoro, QWidget *parent = nullptr); + + void refreshAll(); + void refreshTodoArea(); + void refreshHabits(); + void refreshEvents(); + void refreshPomodoro() const; + void refreshMascotDialog(); + + signals: + void habitCheckedIn(const Habit &habit); + void habitEdited(const Habit &habit); + void habitDeleted(const Habit &habit); + void habitAddRequested(); + + void eventFinished(const Event &event); + void eventEdited(const Event &event); + void eventDeleted(const Event &event); + void eventAddRequested(); +}; + +#endif // MAINVIEW_H \ No newline at end of file diff --git a/src/Pomodoro.cpp b/src/Pomodoro.cpp index 9d88fee..74807b2 100644 --- a/src/Pomodoro.cpp +++ b/src/Pomodoro.cpp @@ -4,10 +4,10 @@ Pomodoro::Pomodoro( const std::size_t id, - const Time pomodoro_time, std::string record + const Time pomodoro_duration, std::string record ) : id(id), - pomodoro_time(pomodoro_time), + pomodoro_duration(pomodoro_duration), record(std::move(record)) { } diff --git a/src/Pomodoro.h b/src/Pomodoro.h index 6f5dbc7..cbd5261 100644 --- a/src/Pomodoro.h +++ b/src/Pomodoro.h @@ -16,18 +16,18 @@ struct Pomodoro { std::size_t id; /**< 番茄钟记录唯一标识ID */ - Time pomodoro_time; /**< 番茄钟持续时间 */ + Time pomodoro_duration; /**< 番茄钟持续时间 */ std::string record; /**< 番茄钟记录备注 */ /** * @brief 构造函数,初始化番茄钟对象 * @param id 番茄钟ID - * @param pomodoro_time 番茄钟持续时间 + * @param pomodoro_duration 番茄钟持续时间 * @param record 番茄钟记录备注 */ explicit Pomodoro( std::size_t id, - Time pomodoro_time, + Time pomodoro_duration, std::string record ); }; diff --git a/src/PomodoroWidget.cpp b/src/PomodoroWidget.cpp index 65df5b2..79acd8f 100644 --- a/src/PomodoroWidget.cpp +++ b/src/PomodoroWidget.cpp @@ -2,9 +2,10 @@ #include #include #include +#include -PomodoroWidget::PomodoroWidget(QWidget *parent) - : QWidget(parent), state_(IDLE), total_seconds_(0), remaining_seconds_(0) +PomodoroWidget::PomodoroWidget(ServiceLayer& service, QWidget *parent) + : QWidget(parent), service_(service), state_(IDLE), total_seconds_(0), remaining_seconds_(0) { // 设置固定大小 setFixedSize(400, 400); @@ -20,6 +21,7 @@ PomodoroWidget::PomodoroWidget(QWidget *parent) timer_ = new QTimer(this); timer_->setInterval(1000); // 每秒更新 connect(timer_, &QTimer::timeout, this, &PomodoroWidget::updateTimer); + connect(this, &PomodoroWidget::timerFinished, this, &PomodoroWidget::insertPomo); // 设置初始时间显示 updateTimeDisplay(); @@ -29,8 +31,8 @@ PomodoroWidget::PomodoroWidget(QWidget *parent) update(); } -PomodoroWidget::PomodoroWidget(const Pomodoro& pomo, QWidget* parent) - : QWidget(parent), state_(RUNNING) +PomodoroWidget::PomodoroWidget(ServiceLayer& service, const Pomodoro& pomo, QWidget* parent) + : QWidget(parent), service_(service), state_(RUNNING) { // 设置固定大小 setFixedSize(400, 400); @@ -43,9 +45,9 @@ PomodoroWidget::PomodoroWidget(const Pomodoro& pomo, QWidget* parent) setupButtonStyles(); // 设置初始时间 - int h = std::chrono::duration_cast(pomo.pomodoro_time.to_duration()).count(); - int m = std::chrono::duration_cast(pomo.pomodoro_time.to_duration()).count() % 60; - int s = std::chrono::duration_cast(pomo.pomodoro_time.to_duration()).count() % 60; + int h = std::chrono::duration_cast(pomo.pomodoro_duration.to_duration()).count(); + int m = std::chrono::duration_cast(pomo.pomodoro_duration.to_duration()).count() % 60; + int s = std::chrono::duration_cast(pomo.pomodoro_duration.to_duration()).count() % 60; total_seconds_ = h * 3600 + m * 60 + s; remaining_seconds_ = total_seconds_; @@ -66,6 +68,14 @@ PomodoroWidget::PomodoroWidget(const Pomodoro& pomo, QWidget* parent) update(); } +void PomodoroWidget::insertPomo(const Pomodoro& pomo) const +{ + if (service_.insertPomoRecord(pomo)) + { + qDebug() << "番茄钟数据插入成功"; + } +} + void PomodoroWidget::initCircularInterface() { // 设置圆形钟容器 @@ -308,7 +318,7 @@ void PomodoroWidget::paintEvent(QPaintEvent* event) } -void PomodoroWidget::setupButtonStyles() +void PomodoroWidget::setupButtonStyles() const { // 设置按钮悬停效果 QString hover_style = "QPushButton:hover { background-color: rgba(0, 0, 0, 0.1); border-radius: 15px; }"; @@ -431,11 +441,12 @@ void PomodoroWidget::updateTimer() control_button_->setText("▶"); QMessageBox::information(this, "时间到", "番茄钟时间到!"); emit stateChanged(); + emit timerFinished(Pomodoro{0, Time{std::chrono::seconds(total_seconds_)}, remark_.toStdString()}); } } } -void PomodoroWidget::updateTimeDisplay() +void PomodoroWidget::updateTimeDisplay() const { if (time_edit_) { time_edit_->setText(formatTime(total_seconds_)); @@ -445,7 +456,7 @@ void PomodoroWidget::updateTimeDisplay() } } -void PomodoroWidget::updateRemarkDisplay() +void PomodoroWidget::updateRemarkDisplay() const { if (remark_label_) { remark_label_->setText(remark_); diff --git a/src/PomodoroWidget.h b/src/PomodoroWidget.h index 3d7f7c3..ed9f608 100644 --- a/src/PomodoroWidget.h +++ b/src/PomodoroWidget.h @@ -8,19 +8,15 @@ #ifndef POMODOROWIDGET_H #define POMODOROWIDGET_H -#include -#include #include #include -#include #include #include #include #include #include -#include -#include #include "Pomodoro.h" +#include "ServiceLayer.h" class PomodoroWidget : public QWidget { @@ -33,21 +29,25 @@ Q_OBJECT PAUSED // 暂停状态 }; - explicit PomodoroWidget(QWidget *parent = nullptr); - explicit PomodoroWidget(const Pomodoro& pomo, QWidget* parent = nullptr); + explicit PomodoroWidget(ServiceLayer& service, QWidget *parent = nullptr); + explicit PomodoroWidget(ServiceLayer& service, const Pomodoro& pomo, QWidget* parent = nullptr); + + // 将番茄钟记录插入数据库中 + void insertPomo(const Pomodoro& pomo) const; // 公共方法,供外部获取状态和信息 - State getState() const { return state_; } - QString getRemark() const { return remark_; } - QString getTimeDisplayText() const; - int getRemainingSeconds() const { return remaining_seconds_; } - int getTotalSeconds() const { return total_seconds_; } + [[nodiscard]] State getState() const { return state_; } + [[nodiscard]] QString getRemark() const { return remark_; } + [[nodiscard]] QString getTimeDisplayText() const; + [[nodiscard]] int getRemainingSeconds() const { return remaining_seconds_; } + [[nodiscard]] int getTotalSeconds() const { return total_seconds_; } // 恢复番茄钟状态 void restoreState(int state, int total_seconds, int remaining_seconds, const QString& remark, const QString& start_time); signals: void stateChanged(); // 状态改变信号 + void timerFinished(Pomodoro pomo); // 定时器结束信号 void timerUpdated(); // 定时器更新信号 protected: @@ -63,6 +63,7 @@ private slots: private: + ServiceLayer& service_; State state_; int total_seconds_; int remaining_seconds_; @@ -85,11 +86,11 @@ private slots: // 私有方法 void initCircularInterface(); - void setupButtonStyles(); - void updateTimeDisplay(); - void updateRemarkDisplay(); - bool parseTimeInput(const QString& input, int& hours, int& minutes, int& seconds); - QString formatTime(int total_seconds) const; + void setupButtonStyles() const; + void updateTimeDisplay() const; + void updateRemarkDisplay() const; + static bool parseTimeInput(const QString& input, int& hours, int& minutes, int& seconds); + [[nodiscard]] QString formatTime(int total_seconds) const; }; #endif // POMODOROWIDGET_H \ No newline at end of file diff --git a/src/ServiceLayer.cpp b/src/ServiceLayer.cpp index 6bb377b..e33ee0d 100644 --- a/src/ServiceLayer.cpp +++ b/src/ServiceLayer.cpp @@ -5,6 +5,7 @@ #include #include #include +#include bool ServiceLayer::insertHabit(const std::string& name, const Date& start_date, const Date& end_date, std::size_t times_per_day) @@ -168,14 +169,9 @@ std::vector ServiceLayer::getExpiredEvents() const return expired_events; } -bool ServiceLayer::pomodoroTick(const Pomodoro& pomodoro, const Time& count_time) +bool ServiceLayer::insertPomoRecord(const Pomodoro& pomodoro) { - auto now_time = getCurrentTimeStamp().second; - if ((pomodoro.pomodoro_time + count_time) < now_time) - { - db_layer.insertPomoRecord(pomodoro); - return false; - } + db_layer.insertPomoRecord(pomodoro); return true; } @@ -202,20 +198,20 @@ std::vector> ServiceLayer::getHabitRecordsBy should += habit.target_count; } - actual = db_layer.getRecordbyDate(current_day).habit_records.size(); // 注意字段名 + actual = db_layer.getRecordByDate(current_day).habit_records.size(); // 注意字段名 stats.emplace_back(actual, should); } return stats; } -DateRecord ServiceLayer::getAllRecordsByDate(const Date& date) +DateRecord ServiceLayer::getAllRecordsByDate(const Date& date) const { // 获取数据库中的原始数据 - return db_layer.getRecordbyDate(date); + return db_layer.getRecordByDate(date); } -std::pair ServiceLayer::getCurrentTimeStamp() const +std::pair ServiceLayer::getCurrentTimeStamp() { auto now_time=std::chrono::system_clock::now(); auto local_time=std::chrono::current_zone()->to_local(now_time); @@ -226,7 +222,7 @@ std::pair ServiceLayer::getCurrentTimeStamp() const return { std::chrono::year_month_day(std::chrono::floor(local_time)), - std::chrono::hh_mm_ss(seconds) + std::chrono::hh_mm_ss(seconds) }; } @@ -284,6 +280,19 @@ std::vector ServiceLayer::getEventsByDate(const QDate date) const return events; } +std::size_t ServiceLayer::getHabitCheckInCount(const Habit& habit) const +{ + const auto today = std::chrono::year_month_day(std::chrono::floor(std::chrono::system_clock::now())); + DateRecord records_today = getAllRecordsByDate(today); + std::size_t count = 0; + for (const auto& val : records_today.habit_records | std::views::values) { + if (val.habit_id == habit.habit_id) { + count += 1; + } + } + return count; +} + void ServiceLayer::init() { } @@ -355,17 +364,17 @@ QJsonObject ServiceLayer::getThemeConfig(const QString &theme_name) return doc.object(); } -bool ServiceLayer::savePomodoroState(int state, int total_seconds, int remaining_seconds, const std::string& remark, const std::string& start_time) +bool ServiceLayer::savePomodoroState(int state, int total_seconds, int remaining_seconds, const std::string& remark, const std::string& start_time) const { return db_layer.savePomodoroState(state, total_seconds, remaining_seconds, remark, start_time); } -bool ServiceLayer::loadPomodoroState(int& state, int& total_seconds, int& remaining_seconds, std::string& remark, std::string& start_time) +bool ServiceLayer::loadPomodoroState(int& state, int& total_seconds, int& remaining_seconds, std::string& remark, std::string& start_time) const { return db_layer.loadPomodoroState(state, total_seconds, remaining_seconds, remark, start_time); } -bool ServiceLayer::clearPomodoroState() +bool ServiceLayer::clearPomodoroState() const { return db_layer.clearPomodoroState(); } \ No newline at end of file diff --git a/src/ServiceLayer.h b/src/ServiceLayer.h index b9e0341..2000290 100644 --- a/src/ServiceLayer.h +++ b/src/ServiceLayer.h @@ -151,15 +151,12 @@ class ServiceLayer [[nodiscard]] std::vector getExpiredEvents() const; /** - * @brief 番茄钟计时处理 + * @brief 将番茄钟记录插入到数据库中 * @author Rain * @param pomodoro 番茄钟结构体 - * @param count_time 计时时间 - * @return 计时状态,未到时返回true,到达时间返回false - * @details 判断番茄钟是否到时,通过传入的Pomodoro对象中的开始时间和倒计时时间相加得到结束时间, - * 时间未到前返回true,到达后返回false,并把对应的番茄钟记录插入数据库。 + * @return 插入状态,插入成功返回true,插入失败返回false */ - bool pomodoroTick(const Pomodoro& pomodoro, const Time& count_time); + bool insertPomoRecord(const Pomodoro& pomodoro); /** * @brief 获取指定月份的习惯打卡记录统计 @@ -177,7 +174,7 @@ class ServiceLayer * @return 对应日期的所有记录 * @details 调用数据层接口,获取指定日期的习惯打卡和番茄钟使用记录。 */ - [[nodiscard]] DateRecord getAllRecordsByDate(const Date& date); + [[nodiscard]] DateRecord getAllRecordsByDate(const Date& date) const; /** * @brief 获取当前时间戳(日期和时间) @@ -185,7 +182,7 @@ class ServiceLayer * @return 当前日期和时间 * @details 获取系统当前时间,封装为Date和Time对象返回,用于时间相关操作的时间基准。 */ - [[nodiscard]] std::pair getCurrentTimeStamp() const; + [[nodiscard]] static std::pair getCurrentTimeStamp(); /** * @brief 按ID获取习惯 @@ -209,6 +206,8 @@ class ServiceLayer std::vector getEventsByDate(QDate date) const; + std::size_t getHabitCheckInCount(const Habit& habit) const; + /** * @brief 初始化服务层 * @author Rain @@ -243,12 +242,12 @@ class ServiceLayer static QJsonObject getThemeConfig(const QString &theme_name); bool savePomodoroState(int state, int total_seconds, int remaining_seconds, const std::string &remark, - const std::string &start_time); + const std::string &start_time) const; bool loadPomodoroState(int &state, int &total_seconds, int &remaining_seconds, std::string &remark, - std::string &start_time); + std::string &start_time) const; - bool clearPomodoroState(); + bool clearPomodoroState() const; }; inline const QString ServiceLayer::THEMES_PATH = ":/themes/themes.json"; // 主题列表配置文件路径 diff --git a/src/Times.cpp b/src/Times.cpp index 9342112..d43cd44 100644 --- a/src/Times.cpp +++ b/src/Times.cpp @@ -2,16 +2,22 @@ std::string toString(const Date& date) { - return std::to_string(static_cast(date.year())) + "-" - + std::to_string(static_cast(date.month())) + "-" - + std::to_string(static_cast(date.day())); + char buffer[11]; // 足够存放 "YYYY-MM-DD\0" + snprintf(buffer, sizeof(buffer), "%04d-%02d-%02d", + static_cast(date.year()), + static_cast(date.month()), + static_cast(date.day())); + return std::string(buffer); } std::string toString(const Time& time) { - return std::to_string(time.hours().count()) + ":" - + std::to_string(time.minutes().count()) + ":" - + std::to_string(time.seconds().count()); + char buffer[9]; // 足够存放 "HH:MM:SS\0" + snprintf(buffer, sizeof(buffer), "%02d:%02d:%02d", + static_cast(time.hours().count()), + static_cast(time.minutes().count()), + static_cast(time.seconds().count())); + return std::string(buffer); } // 字符串转日期 (格式: YYYY-MM-DD) diff --git a/src/ViewLayer.cpp b/src/ViewLayer.cpp index 457bd7a..932a8d8 100644 --- a/src/ViewLayer.cpp +++ b/src/ViewLayer.cpp @@ -1,16 +1,15 @@ #include -#include +#include #include #include -#include -#include #include "ViewLayer.h" #include #include #include "CalendarView.h" #include "NavigationBar.h" -#include #include +#include +#include "CalendarDialog.h" #include "Settings.h" extern Settings g_settings; @@ -30,7 +29,6 @@ void ViewLayer::init() stacked_widget = new QStackedWidget(this); - main_widget = new QWidget(); habit_manage_widget = new QWidget(); event_manage_widget = new QWidget(); pomodoro_widget = new QWidget(); @@ -38,7 +36,6 @@ void ViewLayer::init() calendar_widget = new QWidget(); settings_widget = new QWidget(); - initMainView(); initHabitManageView(); initEventManageView(); initPomodoroView(); @@ -46,6 +43,8 @@ void ViewLayer::init() initCalendarView(); initSettingsView(); + main_widget = new MainView(this->sv_Layer, *pomodoro_widget_component); + // 添加到 stackedWidget 中 stacked_widget->addWidget(main_widget); stacked_widget->addWidget(habit_manage_widget); @@ -376,603 +375,6 @@ void ViewLayer::EventUpdateView(const Event &event) } } -void ViewLayer::initMainView() -{ - if (!main_widget) - main_widget = new QWidget(this); - - // 彻底清空主界面原有布局和控件,防止布局错乱 - if (main_widget->layout()) { - QLayoutItem* item; - while ((item = main_widget->layout()->takeAt(0)) != nullptr) { - if (item->widget()) { - delete item->widget(); - } - delete item; - } - delete main_widget->layout(); - } - - // 主垂直布局(包含内容区和底部导航栏) - QVBoxLayout* mainVLayout = new QVBoxLayout(main_widget); - - // ======= 内容区:网格布局 ======= - QGridLayout* gridLayout = new QGridLayout(); - // 1. 左侧:习惯列表区 - QGroupBox* habitGroup = new QGroupBox("待打卡习惯", main_widget); - - // 创建滚动区域 - QScrollArea* habitScroll = new QScrollArea(habitGroup); - habitScroll->setWidgetResizable(true); - habitScroll->setFixedHeight(500); // 可根据需要调整高度 - habitScroll->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); - habitScroll->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); - habitScroll->setStyleSheet(R"( - QScrollBar:horizontal { - height: 8px; - background: #f0f0f0; - margin: 0px 20px 0 20px; - border-radius: 4px; - } - QScrollBar::handle:horizontal { - background: #bfbfbf; - min-width: 24px; - border-radius: 4px; - } - QScrollBar:vertical { - width: 8px; - background: #f0f0f0; - margin: 20px 0 20px 0; - border-radius: 4px; - } - QScrollBar::handle:vertical { - background: #bfbfbf; - min-height: 24px; - border-radius: 4px; - } - QScrollBar::add-line, QScrollBar::sub-line { - background: none; - border: none; - } -)"); - - // 容器和布局 - QWidget* habitListContainer = new QWidget(); - habitListContainer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); - QVBoxLayout* habitLayout = new QVBoxLayout(habitListContainer); - - // 获取今日打卡记录 - Date today = std::chrono::year_month_day(std::chrono::floor(std::chrono::system_clock::now())); - DateRecord todayRecord = sv_Layer.getAllRecordsByDate(today); - - std::vector habits = sv_Layer.getActiveHabits(); - if (habits.empty()) { - QLabel* noHabit = new QLabel("暂无习惯"); - noHabit->setAlignment(Qt::AlignCenter); - noHabit->setStyleSheet("color:#888;font-size:18px;margin:20px 0;"); - habitLayout->addWidget(noHabit); - } else { - int index = 0; - for (const auto& habit : habits) { - if (index >= 5) break; // 最多显示5个卡片 - // 统计今日打卡次数 - int todayCheckin = 0; - for (const auto& rec : todayRecord.habit_records) { - if (rec.second.habit_id == habit.habit_id) { - ++todayCheckin; - } - } - QWidget* habitCard = new QWidget(); - habitCard->setFixedHeight(80); // 固定卡片高度 - habitCard->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); // 关键 - - QHBoxLayout* cardLayout = new QHBoxLayout(habitCard); - cardLayout->setAlignment(Qt::AlignVCenter); - cardLayout->setSpacing(16); - - // 习惯名称框 - QLabel* nameLabel = new QLabel(QString::fromStdString(habit.name)); - nameLabel->setFixedSize(120, 50); - nameLabel->setAlignment(Qt::AlignCenter); - nameLabel->setStyleSheet("font-weight:bold;font-size:16px;border-radius:8px;background:#fff;border:1px solid #e0e0e0;"); - nameLabel->setWordWrap(true); - - // 日期 - QLabel* dateLabel = new QLabel( - QString("%1 ~ %2") - .arg(QString::fromStdString(toString(habit.start_date))) - .arg(QString::fromStdString(toString(habit.end_date))) - ); - dateLabel->setStyleSheet("font-size:16px;"); - - - - // 打卡次数 - QLabel* checkinLabel = new QLabel( - QString("打卡:%1 / %2 次").arg(QString::number(todayCheckin)).arg(QString::number(habit.target_count)) - ); - checkinLabel->setStyleSheet("font-size:16px;"); - - // 打卡按钮 - QPushButton* checkinBtn = new QPushButton("打卡"); - checkinBtn->setFixedSize(60, 30); - checkinBtn->setStyleSheet( - "QPushButton {" - " background-color: #52c41a;" - " color: white;" - " border: none;" - " border-radius: 6px;" - " font-size: 14px;" - " font-weight: bold;" - " padding: 2px;" - "}" - "QPushButton:hover {" - " background-color: #389e0d;" - "}" - "QPushButton:pressed {" - " background-color: #237804;" - "}" - ); - - // 连接打卡按钮信号 - connect(checkinBtn, &QPushButton::clicked, [this, habit, checkinLabel, todayCheckin]() mutable { - // 检查是否已达到目标次数 - if (todayCheckin >= habit.target_count) { - QMessageBox::information(this, "打卡完成", QString("习惯「%1」今日打卡已完成!(%2/%3)").arg(QString::fromStdString(habit.name)).arg(QString::number(todayCheckin)).arg(QString::number(habit.target_count))); - return; - } - - if (sv_Layer.checkinHabit(habit)) { - // 直接增加打卡次数 - todayCheckin++; - checkinLabel->setText(QString("打卡:%1 / %2 次").arg(QString::number(todayCheckin)).arg(QString::number(habit.target_count))); - - // 检查是否完成目标 - if (todayCheckin >= habit.target_count) { - QMessageBox::information(this, "打卡完成", QString("习惯「%1」今日打卡已完成!(%2/%3)").arg(QString::fromStdString(habit.name)).arg(QString::number(todayCheckin)).arg(QString::number(habit.target_count))); - // 可选:禁用打卡按钮 - // checkinBtn->setEnabled(false); - // checkinBtn->setStyleSheet( - // "QPushButton {" - // " background-color: #d9d9d9;" - // " color: #999;" - // " border: none;" - // " border-radius: 6px;" - // " font-size: 14px;" - // " font-weight: bold;" - // " padding: 2px;" - // "}" - // ); - } else { - QMessageBox::information(this, "打卡成功", QString("习惯「%1」打卡成功!(%2/%3)").arg(QString::fromStdString(habit.name)).arg(QString::number(todayCheckin)).arg(QString::number(habit.target_count))); - } - } else { - QMessageBox::warning(this, "打卡失败", QString("习惯「%1」打卡失败!").arg(QString::fromStdString(habit.name))); - } - }); - - // 横向布局 - cardLayout->addWidget(nameLabel); - cardLayout->addWidget(dateLabel); - cardLayout->addWidget(checkinLabel); - cardLayout->addWidget(checkinBtn); - cardLayout->addStretch(); - habitCard->setLayout(cardLayout); - - // 卡片样式 - habitCard->setStyleSheet( - "background:#fff;" - "border:1px solid #e0e0e0;" - "border-radius:12px;" - "padding:8px;" - "margin-bottom:12px;" - ); - - habitLayout->addWidget(habitCard); - ++index; - } - } - - habitListContainer->setLayout(habitLayout); - habitScroll->setWidget(habitListContainer); - - QVBoxLayout* groupLayout = new QVBoxLayout(habitGroup); - groupLayout->addWidget(habitScroll); - - habitGroup->setLayout(groupLayout); - gridLayout->addWidget(habitGroup, 0, 0, 2, 1); - - // 2. 右上:事项区 - QGroupBox* eventGroup = new QGroupBox("活跃事项", main_widget); - QWidget* eventListContainer = new QWidget(); - QVBoxLayout* eventLayout = new QVBoxLayout(eventListContainer); - - std::vector events = sv_Layer.getActiveEvents(); - if (events.empty()) { - QLabel* noEvent = new QLabel("暂无事项"); - noEvent->setAlignment(Qt::AlignCenter); - noEvent->setStyleSheet("color:#888;font-size:18px;margin:20px 0;"); - eventLayout->addWidget(noEvent); - } else { - int index = 0; - for (const auto& event : events) { - if (index >= 4) break; // 最多显示4个卡片 - QWidget* eventCard = new QWidget(); - QHBoxLayout* cardLayout = new QHBoxLayout(eventCard); - QLabel* nameLabel = new QLabel(QString::fromStdString(event.title)); - QLabel* dateLabel = new QLabel(QString::fromStdString(toString(event.event_date))); - QLabel* timeLabel = new QLabel(QString::fromStdString(toString(event.event_time))); - cardLayout->addWidget(nameLabel); - cardLayout->addWidget(dateLabel); - cardLayout->addWidget(timeLabel); - eventCard->setLayout(cardLayout); - eventLayout->addWidget(eventCard); - ++index; - } - } - eventListContainer->setLayout(eventLayout); - QVBoxLayout* eventGroupLayout = new QVBoxLayout(eventGroup); - eventGroupLayout->addWidget(eventListContainer); - eventGroup->setLayout(eventGroupLayout); - gridLayout->addWidget(eventGroup, 0, 1); - - // 3. 右下:番茄钟区 - QGroupBox* pomoGroup = new QGroupBox("当前番茄钟", main_widget); - QVBoxLayout* pomoLayout = new QVBoxLayout(pomoGroup); - - // 创建番茄钟显示组件 - main_pomodoro_remark_label = new QLabel("", pomoGroup); - main_pomodoro_remark_label->setAlignment(Qt::AlignCenter); - main_pomodoro_remark_label->setStyleSheet("color:#333;font-size:30px;font-weight:bold;margin:5px 0;"); - main_pomodoro_remark_label->hide(); - - main_pomodoro_time_label = new QLabel("00 : 00 : 00", pomoGroup); - main_pomodoro_time_label->setAlignment(Qt::AlignCenter); - main_pomodoro_time_label->setStyleSheet("color:#1890ff;font-size:20px;font-weight:bold;margin:5px 0;"); - main_pomodoro_time_label->hide(); - - main_pomodoro_no_pomodoro_label = new QLabel("暂无番茄钟", pomoGroup); - main_pomodoro_no_pomodoro_label->setAlignment(Qt::AlignCenter); - main_pomodoro_no_pomodoro_label->setStyleSheet("color:#888;font-size:16px;margin:20px 0;"); - - pomoLayout->addWidget(main_pomodoro_remark_label); - pomoLayout->addWidget(main_pomodoro_time_label); - pomoLayout->addWidget(main_pomodoro_no_pomodoro_label); - - pomoGroup->setLayout(pomoLayout); - gridLayout->addWidget(pomoGroup, 1, 1); - - // 4. 右下角:吉祥物+对话框 - QWidget* dialogMascotWidget = new QWidget(main_widget); - QHBoxLayout* dialogMascotLayout = new QHBoxLayout(dialogMascotWidget); - QGroupBox* dialogGroup = new QGroupBox(dialogMascotWidget); - QVBoxLayout* dialogLayout = new QVBoxLayout(dialogGroup); - if (!dialogLabel) dialogLabel = new QLabel(dialogGroup); - dialogLabel->setText(dialogTexts[0]); - dialogLabel->setAlignment(Qt::AlignLeft | Qt::AlignVCenter); - dialogLabel->setWordWrap(true); - dialogGroup->setStyleSheet(R"( - QGroupBox { - background: #fffbe6; - border: 2px solid #ffd666; - border-radius: 16px; - margin-top: 8px; - margin-bottom: 8px; - padding: 16px; - min-width: 220px; - max-width: 320px; - } -)"); - dialogLabel->setStyleSheet(R"( - QLabel { - color: #ad6800; - font-size: 18px; - font-weight: bold; - padding: 4px 8px; - background: transparent; - } -)"); - dialogLayout->addWidget(dialogLabel); - dialogGroup->setLayout(dialogLayout); - dialogMascotLayout->addWidget(dialogGroup, 2); - QLabel* mascotLabel = new QLabel(dialogMascotWidget); - QPixmap mascotPixmap(":/assets/images/logo.png"); - if (mascotPixmap.isNull()) { - mascotLabel->setText("logo未加载"); - } else { - mascotLabel->setPixmap(mascotPixmap.scaled(120, 120, Qt::KeepAspectRatio, Qt::SmoothTransformation)); - } - dialogMascotLayout->addWidget(mascotLabel, 1, Qt::AlignRight | Qt::AlignBottom); - dialogMascotWidget->setLayout(dialogMascotLayout); - gridLayout->addWidget(dialogMascotWidget, 2, 1); - - // 设置行列拉伸比例 - gridLayout->setRowStretch(0, 2); - gridLayout->setRowStretch(1, 2); - gridLayout->setRowStretch(2, 1); - gridLayout->setColumnStretch(0, 5); // 习惯框占5份 - gridLayout->setColumnStretch(1, 2); // 右侧区域占2份 - - // 把内容区加到主垂直布局 - mainVLayout->addLayout(gridLayout, 10); - mainVLayout->addWidget(navigation_widget, 1); - - - main_widget->setLayout(mainVLayout); - dialogLabel->installEventFilter(this); - main_widget->setStyleSheet("background: #F5F6FA;"); - - // 初始化主界面番茄钟定时器 - if (!main_pomodoro_timer) { - main_pomodoro_timer = new QTimer(this); - main_pomodoro_timer->setInterval(1000); // 每秒更新 - connect(main_pomodoro_timer, &QTimer::timeout, this, &ViewLayer::updateMainPomodoroDisplay); - } - - // 检查并恢复番茄钟状态(延迟执行,确保番茄钟组件已创建) - QTimer::singleShot(200, this, &ViewLayer::checkAndRestorePomodoroState); -} - -void ViewLayer::refreshMainView() -{ - if (!main_content_grid_layout) return; - - // 清空 main_content_grid_layout - QLayoutItem* item; - while ((item = main_content_grid_layout->takeAt(0)) != nullptr) { - if (item->widget()) { - item->widget()->deleteLater(); - } - delete item; - } - - // ========= 左侧:待打卡习惯 ========= - QGroupBox* habitGroup = new QGroupBox("待打卡习惯", main_widget); - QScrollArea* habitScroll = new QScrollArea(habitGroup); - habitScroll->setWidgetResizable(true); - habitScroll->setFixedHeight(500); - habitScroll->setStyleSheet(R"( - QScrollBar:horizontal { - height: 8px; background: #f0f0f0; margin: 0px 20px 0 20px; border-radius: 4px; - } - QScrollBar::handle:horizontal { - background: #bfbfbf; min-width: 24px; border-radius: 4px; - } - QScrollBar:vertical { - width: 8px; background: #f0f0f0; margin: 20px 0 20px 0; border-radius: 4px; - } - QScrollBar::handle:vertical { - background: #bfbfbf; min-height: 24px; border-radius: 4px; - } - QScrollBar::add-line, QScrollBar::sub-line { - background: none; border: none; - })"); - - QWidget* habitListContainer = new QWidget(); - QVBoxLayout* habitLayout = new QVBoxLayout(habitListContainer); - - Date today = std::chrono::year_month_day(std::chrono::floor(std::chrono::system_clock::now())); - DateRecord todayRecord = sv_Layer.getAllRecordsByDate(today); - std::vector habits = sv_Layer.getActiveHabits(); - - if (habits.empty()) { - QLabel* noHabit = new QLabel("暂无习惯"); - noHabit->setAlignment(Qt::AlignCenter); - noHabit->setStyleSheet("color:#888;font-size:18px;margin:20px 0;"); - habitLayout->addWidget(noHabit); - } else { - int index = 0; - for (const auto& habit : habits) { - if (index >= 5) break; - int todayCheckin = std::count_if( - todayRecord.habit_records.begin(), todayRecord.habit_records.end(), - [&habit](const auto& rec) { return rec.second.habit_id == habit.habit_id; }); - - QWidget* habitCard = new QWidget(); - habitCard->setFixedHeight(80); - habitCard->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); - habitCard->setStyleSheet(R"( - background:#fff; - border:1px solid #e0e0e0; - border-radius:12px; - padding:8px; - margin-bottom:12px;)"); - - QHBoxLayout* cardLayout = new QHBoxLayout(habitCard); - cardLayout->setAlignment(Qt::AlignVCenter); - cardLayout->setSpacing(16); - - QLabel* nameLabel = new QLabel(QString::fromStdString(habit.name)); - nameLabel->setFixedHeight(50); - nameLabel->setAlignment(Qt::AlignCenter); - nameLabel->setStyleSheet("font-weight:bold;font-size:22px;border-radius:8px;background:#fff;border:1px solid #e0e0e0;"); - - QLabel* dateLabel = new QLabel( - QString("%1 ~ %2") - .arg(QString::fromStdString(toString(habit.start_date))) - .arg(QString::fromStdString(toString(habit.end_date))) - ); - dateLabel->setStyleSheet("font-size:16px;"); - - QPushButton* editBtn = new QPushButton(); - editBtn->setIcon(QIcon(":/assets/images/modify.png")); - editBtn->setToolTip("编辑"); - connect(editBtn, &QPushButton::clicked, [this, habit]() { - habitUpdateView(habit); - refreshMainView(); - }); - - QPushButton* delBtn = new QPushButton(); - delBtn->setIcon(QIcon(":/assets/images/delete.png")); - delBtn->setToolTip("删除"); - connect(delBtn, &QPushButton::clicked, [this, habit]() { - if (QMessageBox::question(this, "确认删除", "确定删除该习惯吗?") == QMessageBox::Yes) { - if (sv_Layer.deleteHabit(habit.habit_id)) { - QMessageBox::information(this, "提示", "删除成功"); - refreshMainView(); // 刷新 - } else { - QMessageBox::warning(this, "错误", "删除失败"); - } - } - }); - - QLabel* checkinLabel = new QLabel( - QString("打卡:%1 / %2 次").arg(QString::number(todayCheckin)).arg(QString::number(habit.target_count))); - checkinLabel->setStyleSheet("font-size:16px;"); - - cardLayout->addWidget(nameLabel); - cardLayout->addWidget(dateLabel); - cardLayout->addWidget(editBtn); - cardLayout->addWidget(delBtn); - cardLayout->addWidget(checkinLabel); - cardLayout->addStretch(); - - habitCard->setLayout(cardLayout); - habitLayout->addWidget(habitCard); - ++index; - } - } - - habitListContainer->setLayout(habitLayout); - habitScroll->setWidget(habitListContainer); - QVBoxLayout* groupLayout = new QVBoxLayout(habitGroup); - groupLayout->addWidget(habitScroll); - habitGroup->setLayout(groupLayout); - main_content_grid_layout->addWidget(habitGroup, 0, 0, 2, 1); - - // ========= 右上:事项 ========= - QGroupBox* eventGroup = new QGroupBox("活跃事项", main_widget); - QWidget* eventListContainer = new QWidget(); - QVBoxLayout* eventLayout = new QVBoxLayout(eventListContainer); - std::vector events = sv_Layer.getActiveEvents(); - - if (events.empty()) { - QLabel* noEvent = new QLabel("暂无事项"); - noEvent->setAlignment(Qt::AlignCenter); - noEvent->setStyleSheet("color:#888;font-size:18px;margin:20px 0;"); - eventLayout->addWidget(noEvent); - } else { - int index = 0; - for (const auto& event : events) { - if (index >= 4) break; - QWidget* eventCard = new QWidget(); - QHBoxLayout* cardLayout = new QHBoxLayout(eventCard); - QLabel* nameLabel = new QLabel(QString::fromStdString(event.title)); - QLabel* dateLabel = new QLabel(QString::fromStdString(toString(event.event_date))); - QLabel* timeLabel = new QLabel(QString::fromStdString(toString(event.event_time))); - QPushButton* editBtn = new QPushButton("编辑"); - QPushButton* delBtn = new QPushButton("删除"); - connect(editBtn, &QPushButton::clicked, [this, event]() { - EventUpdateView(event); - refreshMainView(); - }); - connect(delBtn, &QPushButton::clicked, [this, event]() { - if (QMessageBox::question(this, "确认删除", "确定删除该事项吗?") == QMessageBox::Yes) { - if (sv_Layer.deleteEvent(event.event_id)) { - QMessageBox::information(this, "提示", "删除成功"); - refreshMainView(); // 刷新 - } else { - QMessageBox::warning(this, "错误", "删除失败"); - } - } - }); - cardLayout->addWidget(nameLabel); - cardLayout->addWidget(dateLabel); - cardLayout->addWidget(timeLabel); - cardLayout->addWidget(editBtn); - cardLayout->addWidget(delBtn); - eventCard->setLayout(cardLayout); - eventLayout->addWidget(eventCard); - ++index; - } - } - - eventListContainer->setLayout(eventLayout); - QVBoxLayout* eventGroupLayout = new QVBoxLayout(eventGroup); - eventGroupLayout->addWidget(eventListContainer); - eventGroup->setLayout(eventGroupLayout); - main_content_grid_layout->addWidget(eventGroup, 0, 1); - - // ========= 右下:番茄钟 ========= - QGroupBox* pomoGroup = new QGroupBox("当前番茄钟", main_widget); - QVBoxLayout* pomoLayout = new QVBoxLayout(pomoGroup); - pomoLayout->setAlignment(Qt::AlignCenter); - - if (pomodoro_widget_component && pomodoro_widget_component->getState() == PomodoroWidget::RUNNING) { - QLabel* timeLabel = new QLabel(pomodoro_widget_component->getTimeDisplayText()); - timeLabel->setStyleSheet("font-size: 24px; font-weight: bold;"); - timeLabel->setAlignment(Qt::AlignCenter); - - QLabel* remarkLabel = new QLabel(pomodoro_widget_component->getRemark()); - remarkLabel->setStyleSheet("font-size: 18px; color: #555;"); - remarkLabel->setWordWrap(true); - remarkLabel->setAlignment(Qt::AlignCenter); - - pomoLayout->addWidget(timeLabel); - pomoLayout->addWidget(remarkLabel); - } else { - QLabel* noPomo = new QLabel("暂无活跃番茄钟"); - noPomo->setStyleSheet("font-size: 20px; color: #999;"); - noPomo->setAlignment(Qt::AlignCenter); - pomoLayout->addWidget(noPomo); - } - - main_content_grid_layout->addWidget(pomoGroup, 1, 1); - - pomoGroup->setLayout(pomoLayout); - main_content_grid_layout->addWidget(pomoGroup, 1, 1); - - // ========= 右下角:吉祥物+对话框 ========= - QWidget* dialogMascotWidget = new QWidget(main_widget); - QHBoxLayout* dialogMascotLayout = new QHBoxLayout(dialogMascotWidget); - QGroupBox* dialogGroup = new QGroupBox(dialogMascotWidget); - QVBoxLayout* dialogLayout = new QVBoxLayout(dialogGroup); - - if (!dialogLabel) dialogLabel = new QLabel(dialogGroup); - dialogLabel->setText(dialogTexts[0]); - dialogLabel->setAlignment(Qt::AlignLeft | Qt::AlignVCenter); - dialogLabel->setWordWrap(true); - - dialogGroup->setStyleSheet(R"( - QGroupBox { - background: #fffbe6; - border: 2px solid #ffd666; - border-radius: 16px; - margin-top: 8px; - margin-bottom: 8px; - padding: 16px; - min-width: 220px; - max-width: 320px; - })"); - dialogLabel->setStyleSheet(R"( - QLabel { - color: #ad6800; - font-size: 18px; - font-weight: bold; - padding: 4px 8px; - background: transparent; - })"); - - dialogLayout->addWidget(dialogLabel); - dialogGroup->setLayout(dialogLayout); - dialogMascotLayout->addWidget(dialogGroup, 2); - - QLabel* mascotLabel = new QLabel(dialogMascotWidget); - QPixmap mascotPixmap(":/assets/images/logo.png"); - mascotLabel->setPixmap(mascotPixmap.scaled(120, 120, Qt::KeepAspectRatio, Qt::SmoothTransformation)); - dialogMascotLayout->addWidget(mascotLabel, 1, Qt::AlignRight | Qt::AlignBottom); - dialogMascotWidget->setLayout(dialogMascotLayout); - - main_content_grid_layout->addWidget(dialogMascotWidget, 2, 1); - - // ========= 拉伸策略 ========= - main_content_grid_layout->setRowStretch(0, 2); - main_content_grid_layout->setRowStretch(1, 2); - main_content_grid_layout->setRowStretch(2, 1); - main_content_grid_layout->setColumnStretch(0, 2); - main_content_grid_layout->setColumnStretch(1, 3); -} - void ViewLayer::initTimelineView() { auto* layout = new QVBoxLayout(timeline_widget); @@ -1082,7 +484,7 @@ void ViewLayer::refreshTimeline() timeline_items.emplace_back(time, "习惯打卡", habit.name); for (const auto& [time, pomo] : raw_record.pomodoro_records) - timeline_items.emplace_back(time, pomo.record, "番茄钟专注 " + toString(pomo.pomodoro_time)); + timeline_items.emplace_back(time, pomo.record, "番茄钟专注 " + toString(pomo.pomodoro_duration)); for (const auto& [time, event] : raw_record.event_records) timeline_items.emplace_back(time, "事项", event.title); @@ -1109,7 +511,7 @@ void ViewLayer::initPomodoroView() { if (!pomodoro_widget->layout()) { pomodoro_main_layout = new QVBoxLayout(pomodoro_widget); - pomodoro_widget_component = new PomodoroWidget(pomodoro_widget); + pomodoro_widget_component = new PomodoroWidget(sv_Layer, pomodoro_widget); // 顶部栏 QHBoxLayout* topLayout = new QHBoxLayout(); QLabel* title = new QLabel("番茄钟", pomodoro_widget); @@ -1131,19 +533,21 @@ void ViewLayer::initPomodoroView() pomodoro_scroll_area->setWidgetResizable(true); pomodoro_main_layout->addWidget(pomodoro_scroll_area); - connect(pomodoro_widget_component, &PomodoroWidget::timerUpdated, this, [this]() { - refreshMainView(); + connect(pomodoro_widget_component, &PomodoroWidget::timerUpdated, this, [this] + { + main_widget->refreshPomodoro(); }); - connect(pomodoro_widget_component, &PomodoroWidget::stateChanged, this, [this]() { - refreshMainView(); + connect(pomodoro_widget_component, &PomodoroWidget::stateChanged, this, [this] + { + main_widget->refreshPomodoro(); }); } refreshPomodoroView(); // 初始化后第一次刷新 } -void ViewLayer::refreshPomodoroView() +void ViewLayer::refreshPomodoroView() const { if (!pomodoro_scroll_area) return; @@ -1159,15 +563,62 @@ void ViewLayer::refreshPomodoroView() if (todayRecord.pomodoro_records.empty()) { QLabel* noDataLabel = new QLabel("暂无番茄钟记录"); noDataLabel->setAlignment(Qt::AlignCenter); + noDataLabel->setStyleSheet("color: #888; font-size: 16px;"); layout->addWidget(noDataLabel); } else { - layout->addStretch(); - for (const auto& pair : todayRecord.pomodoro_records) { - const Pomodoro& pomo = pair.second; - PomodoroWidget* widget = new PomodoroWidget(pomo, container); - layout->addWidget(widget); + // 表格控件 + QTableWidget* table = new QTableWidget(container); + table->setColumnCount(3); + table->setHorizontalHeaderLabels(QStringList() << "备注" << "完成时间" << "持续时间"); + + // 表格样式优化 + table->horizontalHeader()->setStretchLastSection(true); + table->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch); + table->setSelectionMode(QAbstractItemView::NoSelection); + table->setEditTriggers(QAbstractItemView::NoEditTriggers); + table->setStyleSheet( + "QHeaderView::section {" + " background-color: #f5f5f5;" + " font-weight: bold;" + " padding: 4px;" + " border: 1px solid #ddd;" + "}" + "QTableWidget {" + " gridline-color: #eee;" + " font-size: 13px;" + "}" + ); + + // 按时间排序 + std::vector sortedRecords( + todayRecord.pomodoro_records.begin(), todayRecord.pomodoro_records.end()); + + std::sort(sortedRecords.begin(), sortedRecords.end(), + [](const auto& a, const auto& b) { + return a.first < b.first; + }); + + table->setRowCount(static_cast(sortedRecords.size())); + + // 填充表格 + int row = 0; + for (const auto& [time, pomo] : sortedRecords) { + QTableWidgetItem* remarkItem = new QTableWidgetItem(QString::fromStdString(pomo.record)); + QTableWidgetItem* timeItem = new QTableWidgetItem(QString::fromStdString(toString(time))); + QTableWidgetItem* durationItem = new QTableWidgetItem( + QString::fromStdString(toString(pomo.pomodoro_duration))); + + remarkItem->setTextAlignment(Qt::AlignCenter); + timeItem->setTextAlignment(Qt::AlignCenter); + durationItem->setTextAlignment(Qt::AlignCenter); + + table->setItem(row, 0, remarkItem); + table->setItem(row, 1, timeItem); + table->setItem(row, 2, durationItem); + row++; } - layout->addStretch(); + + layout->addWidget(table); } container->setLayout(layout); @@ -1571,7 +1022,7 @@ void ViewLayer::initNavigationView() { this, [this](const NavigationBar::NavButton button) { switch (button) { case NavigationBar::NavButton::MAIN_VIEW: - refreshMainView(); + main_widget->refreshAll(); stacked_widget->setCurrentWidget(main_widget); break; case NavigationBar::NavButton::HABIT_MANAGE_VIEW: @@ -1609,7 +1060,7 @@ void ViewLayer::initNavigationView() { void ViewLayer::onBackToNavigation() { navigation_widget->setCurrentButton(NavigationBar::NavButton::MAIN_VIEW); - refreshMainView(); + main_widget->refreshAll(); stacked_widget->setCurrentWidget(main_widget); } @@ -1621,7 +1072,7 @@ void ViewLayer::setCurrentView(ViewType view) cur_view_type = view; switch (view) { case ViewType::MAIN_VIEW: - initMainView(); + main_widget->refreshAll(); stacked_widget->setCurrentWidget(main_widget); break; case ViewType::HABIT_MANAGE_VIEW: @@ -1650,152 +1101,3 @@ void ViewLayer::setCurrentView(ViewType view) break; } } - -bool ViewLayer::eventFilter(QObject* watched, QEvent* event) -{ - if (watched == dialogLabel && event->type() == QEvent::MouseButtonPress) { - // 随机选一句 - int idx = QRandomGenerator::global()->bounded(dialogTexts.size()); - dialogLabel->setText(dialogTexts[idx]); - return true; // 事件已处理 - } - return QWidget::eventFilter(watched, event); -} - -void ViewLayer::updateMainPomodoroDisplay() -{ - qDebug() << "=== 更新主界面番茄钟显示 ==="; - qDebug() << "番茄钟组件是否存在:" << (pomodoro_widget_component ? "是" : "否"); - - if (!pomodoro_widget_component) { - qDebug() << "番茄钟组件不存在,显示无番茄钟"; - showNoPomodoro(); - return; - } - - // 检查番茄钟状态 - int state = pomodoro_widget_component->getState(); - qDebug() << "当前番茄钟状态:" << state; - - if (state == PomodoroWidget::IDLE) { - qDebug() << "番茄钟状态为IDLE,显示无番茄钟"; - showNoPomodoro(); - return; - } - - // 获取番茄钟信息 - QString remark = pomodoro_widget_component->getRemark(); - QString timeText = pomodoro_widget_component->getTimeDisplayText(); - int remainingSeconds = pomodoro_widget_component->getRemainingSeconds(); - - qDebug() << "番茄钟信息:"; - qDebug() << " 备注:" << remark; - qDebug() << " 时间文本:" << timeText; - qDebug() << " 剩余秒数:" << remainingSeconds; - - // 更新显示 - if (main_pomodoro_remark_label && main_pomodoro_time_label && main_pomodoro_no_pomodoro_label) { - main_pomodoro_remark_label->setText(remark); - main_pomodoro_time_label->setText(timeText); - - main_pomodoro_remark_label->show(); - main_pomodoro_time_label->show(); - main_pomodoro_no_pomodoro_label->hide(); - - qDebug() << "主界面番茄钟显示已更新"; - } else { - qDebug() << "错误:主界面番茄钟标签不存在"; - } - - qDebug() << "=== 主界面番茄钟显示更新完成 ==="; -} - -void ViewLayer::startMainPomodoroTimer() -{ - if (main_pomodoro_timer) { - main_pomodoro_timer->start(); - } -} - -void ViewLayer::stopMainPomodoroTimer() -{ - if (main_pomodoro_timer) { - main_pomodoro_timer->stop(); - } -} - -void ViewLayer::checkAndRestorePomodoroState() -{ - qDebug() << "=== 开始检查并恢复番茄钟状态 ==="; - qDebug() << "番茄钟组件是否存在:" << (pomodoro_widget_component ? "是" : "否"); - - // 检查番茄钟组件是否存在 - if (!pomodoro_widget_component) { - qDebug() << "错误:番茄钟组件不存在,无法恢复状态"; - return; - } - - // 检查是否有正在运行的番茄钟 - if (pomodoro_widget_component->getState() != PomodoroWidget::IDLE) { - qDebug() << "发现正在运行的番茄钟,状态:" << pomodoro_widget_component->getState(); - qDebug() << "启动主界面定时器"; - startMainPomodoroTimer(); - updateMainPomodoroDisplay(); - return; - } - - // 尝试从数据库恢复番茄钟状态 - int state, total_seconds, remaining_seconds; - std::string remark, start_time; - - qDebug() << "尝试从数据库加载番茄钟状态..."; - if (sv_Layer.loadPomodoroState(state, total_seconds, remaining_seconds, remark, start_time)) { - qDebug() << "成功加载番茄钟状态:"; - qDebug() << " 状态:" << state; - qDebug() << " 总时长:" << total_seconds; - qDebug() << " 剩余时长:" << remaining_seconds; - qDebug() << " 备注:" << QString::fromStdString(remark); - qDebug() << " 开始时间:" << QString::fromStdString(start_time); - - // 如果有保存的状态,恢复番茄钟 - if (state != 0) { // 0 = IDLE - qDebug() << "开始恢复番茄钟状态..."; - pomodoro_widget_component->restoreState(state, total_seconds, remaining_seconds, - QString::fromStdString(remark), - QString::fromStdString(start_time)); - - qDebug() << "恢复后的状态:" << pomodoro_widget_component->getState(); - qDebug() << "恢复后的剩余时间:" << pomodoro_widget_component->getRemainingSeconds(); - - // 检查恢复后的状态 - if (pomodoro_widget_component->getState() != PomodoroWidget::IDLE) { - qDebug() << "番茄钟状态恢复成功,启动定时器"; - startMainPomodoroTimer(); - updateMainPomodoroDisplay(); - } else { - // 如果恢复后状态变为IDLE(时间已用完),清除数据库状态 - qDebug() << "番茄钟时间已用完,清除数据库状态"; - sv_Layer.clearPomodoroState(); - showNoPomodoro(); - } - } else { - qDebug() << "状态为IDLE,显示无番茄钟"; - showNoPomodoro(); - } - } else { - qDebug() << "没有找到保存的番茄钟状态,显示无番茄钟"; - showNoPomodoro(); - } - - qDebug() << "=== 番茄钟状态检查完成 ==="; -} - -void ViewLayer::showNoPomodoro() -{ - if (main_pomodoro_remark_label && main_pomodoro_time_label && main_pomodoro_no_pomodoro_label) { - main_pomodoro_remark_label->hide(); - main_pomodoro_time_label->hide(); - main_pomodoro_no_pomodoro_label->show(); - } - stopMainPomodoroTimer(); -} diff --git a/src/ViewLayer.h b/src/ViewLayer.h index cc3315e..dafbeb7 100644 --- a/src/ViewLayer.h +++ b/src/ViewLayer.h @@ -10,15 +10,9 @@ /* Set header file*/ #include -#include #include #include -#include -#include -#include -#include -#include "CalendarDialog.h" #include "Times.h" #include "Event.h" #include "Habit.h" @@ -28,6 +22,7 @@ #include "Utility.h" #include "CalendarView.h" #include "SimpleAuthSystem.h" +#include "MainView.h" /** * @class ViewLayer @@ -74,23 +69,6 @@ class ViewLayer final : public QWidget */ void setCurrentView(ViewType view); - bool eventFilter(QObject *watched, QEvent *event) override; - - // 更新主界面番茄钟显示(主界面右下角) - void updateMainPomodoroDisplay(); - - // 启动主界面番茄钟定时器 - void startMainPomodoroTimer(); - - // 停止主界面番茄钟定时器 - void stopMainPomodoroTimer(); - - // 检查并恢复番茄钟状态(从数据库恢复) - void checkAndRestorePomodoroState(); - - // 显示“无番茄钟”状态 - void showNoPomodoro(); - signals: /** * @brief 习惯添加信号 @@ -135,13 +113,7 @@ private slots: NavigationBar* navigation_widget{}; /**< 导航视图部件 */ QVBoxLayout* main_layout{}; /**< 主布局 */ - QWidget* main_widget{}; /**< 主界面视图部件 */ - QGridLayout* main_content_grid_layout{}; - // 主界面番茄钟显示相关 - QLabel* main_pomodoro_remark_label = nullptr; // 显示番茄钟备注 - QLabel* main_pomodoro_time_label = nullptr; // 显示番茄钟剩余时间 - QLabel* main_pomodoro_no_pomodoro_label = nullptr; // 显示“暂无番茄钟”提示 - QTimer* main_pomodoro_timer = nullptr; // 主界面番茄钟定时器,用于定时刷新显示 + MainView* main_widget{}; /**< 主界面视图部件 */ QWidget* habit_manage_widget{}; /**< 习惯管理视图部件 */ QScrollArea* activeHabitScrollArea{}; @@ -180,17 +152,6 @@ private slots: Date end_date_input{}; /**< 结束日期输入 */ Time event_time_input; /**< 事项时间输入 */ - // 对话框文本列表 - QStringList dialogTexts = { - "这是今天需要完成的事情哦~", - "加油!你一定可以坚持下去!", - "别忘了打卡和休息哦!", - "每一天都值得被记录!", - "习惯的力量很强大!" - }; - // 当前对话框的 QLabel 指针 - QLabel* dialogLabel = nullptr; - // ================= 各视图初始化 ================= /** * @brief 初始化导航视图 @@ -198,14 +159,6 @@ private slots: */ void initNavigationView(); - /** - * @brief 初始化主视图 - * @author - */ - void initMainView(); - - void refreshMainView(); - /** * @brief 初始化习惯管理视图 * @author Rain @@ -273,7 +226,7 @@ private slots: */ void initPomodoroView(); - void refreshPomodoroView(); + void refreshPomodoroView() const; /** * @brief 初始化设置视图 diff --git a/src/main/main.cpp b/src/main/main.cpp index 522f3fa..6a2fd4b 100644 --- a/src/main/main.cpp +++ b/src/main/main.cpp @@ -2,28 +2,18 @@ #include #include "ViewLayer.h" -// 临时测试函数 -// 测试不同界面 -int testHabbitModule(int argc, char* argv[], const ViewLayer::ViewType type) +int main(int argc, char* argv[]) { +#ifdef _WIN32 + ::ShowWindow(::GetConsoleWindow(), SW_HIDE); // 隐藏控制台 +#endif QApplication app(argc, argv); ViewLayer view; view.setFixedSize(1100, 800); view.setWindowTitle("Habbit 效率管理软件"); - view.setCurrentView(type); view.show(); - return app.exec(); -} - -// 其他部分的临时测试函数在本行上方添加 - -int main(const int argc, char *argv[]) -{ -#ifdef _WIN32 - ::ShowWindow(::GetConsoleWindow(), SW_HIDE); // 隐藏控制台 -#endif - return testHabbitModule(argc, argv, ViewLayer::ViewType::MAIN_VIEW); + return QApplication::exec(); } \ No newline at end of file diff --git a/src/main/winWidget.cpp b/src/main/winWidget.cpp index 20b03c0..6a38c8c 100644 --- a/src/main/winWidget.cpp +++ b/src/main/winWidget.cpp @@ -2,7 +2,7 @@ #include #include "CalendarWinWidget.h" -int main(int argc, char *argv[]) +int main(int argc, char* argv[]) { #ifdef _WIN32 ::ShowWindow(::GetConsoleWindow(), SW_HIDE); // 隐藏控制台