diff --git a/CMakeLists.txt b/CMakeLists.txt index 63f116c..3b9f8b1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -33,7 +33,8 @@ target_include_directories(Habbit PRIVATE src) target_include_directories(HabbitWinWidget PRIVATE src) qt_add_resources(APP_RESOURCES assets/resources.qrc) -target_sources(Habbit PRIVATE ${APP_RESOURCES}) +qt_add_resources(THEMES themes/themes.qrc) +target_sources(Habbit PRIVATE ${APP_RESOURCES} ${THEMES}) target_link_libraries(Habbit PRIVATE Qt6::Widgets Qt6::Sql) target_link_libraries(HabbitWinWidget PRIVATE Qt6::Widgets Qt6::Sql) \ No newline at end of file diff --git a/assets/icons/blue/calendar.svg b/assets/icons/blue/calendar.svg new file mode 100644 index 0000000..a9473f9 --- /dev/null +++ b/assets/icons/blue/calendar.svg @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/assets/icons/blue/event.svg b/assets/icons/blue/event.svg new file mode 100644 index 0000000..cd5a8ef --- /dev/null +++ b/assets/icons/blue/event.svg @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/assets/icons/blue/habit.svg b/assets/icons/blue/habit.svg new file mode 100644 index 0000000..7dc1b0e --- /dev/null +++ b/assets/icons/blue/habit.svg @@ -0,0 +1,7 @@ + + + + \ No newline at end of file diff --git a/assets/icons/blue/home.svg b/assets/icons/blue/home.svg new file mode 100644 index 0000000..94288a0 --- /dev/null +++ b/assets/icons/blue/home.svg @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/assets/icons/blue/pomodoro.svg b/assets/icons/blue/pomodoro.svg new file mode 100644 index 0000000..fe47117 --- /dev/null +++ b/assets/icons/blue/pomodoro.svg @@ -0,0 +1,7 @@ + + + + \ No newline at end of file diff --git a/assets/icons/blue/setting.svg b/assets/icons/blue/setting.svg new file mode 100644 index 0000000..a7c6815 --- /dev/null +++ b/assets/icons/blue/setting.svg @@ -0,0 +1,7 @@ + + + + \ No newline at end of file diff --git a/assets/icons/blue/timeline.svg b/assets/icons/blue/timeline.svg new file mode 100644 index 0000000..8fbe1df --- /dev/null +++ b/assets/icons/blue/timeline.svg @@ -0,0 +1,7 @@ + + + + \ No newline at end of file diff --git a/assets/resources.qrc b/assets/resources.qrc index 073bc89..6f18024 100644 --- a/assets/resources.qrc +++ b/assets/resources.qrc @@ -1,6 +1,5 @@ - icons/default/ images/logo.png images/add.png images/delete.png @@ -11,6 +10,20 @@ images/backward.png images/active.png images/inactive.png + icons/default/calendar.svg + icons/default/event.svg + icons/default/habit.svg + icons/default/home.svg + icons/default/pomodoro.svg + icons/default/setting.svg + icons/default/timeline.svg + icons/blue/calendar.svg + icons/blue/event.svg + icons/blue/habit.svg + icons/blue/home.svg + icons/blue/pomodoro.svg + icons/blue/setting.svg + icons/blue/timeline.svg json_style/ diff --git a/assets/themes/blue.json b/assets/themes/blue.json new file mode 100644 index 0000000..3a89998 --- /dev/null +++ b/assets/themes/blue.json @@ -0,0 +1,46 @@ +{ + "normal": { + "background": "#f0f9ff", + "border": "1.5px solid #91d5ff", + "border_radius": "8", + "text_color": "#0000FF", + "font_size": "16", + "font_family": "Arial", + "padding_vertical": "0", + "padding_horizontal": "12", + "hover_background": "#cceeff", + "icons": { + "home": ":/assets/icons/blue/home.svg", + "habit": ":/assets/icons/blue/habit.svg", + "event": ":/assets/icons/blue/event.svg", + "pomodoro": ":/assets/icons/blue/pomodoro.svg", + "timeline": ":/assets/icons/blue/timeline.svg", + "calendar": ":/assets/icons/blue/calendar.svg", + "settings": ":/assets/icons/blue/setting.svg" + }, + "icon_width": "24", + "icon_height": "24" + }, + "selected": { + "background": "#1890ff", + "border": "2px solid #096dd9", + "border_radius": "8", + "text_color": "#ffffff", + "font_size": "18", + "font_family": "Arial", + "padding_vertical": "0", + "padding_horizontal": "12", + "hover_background": "#096dd9", + "icons": { + "home": ":/assets/icons/blue/home.svg", + "habit": ":/assets/icons/blue/habit.svg", + "event": ":/assets/icons/blue/event.svg", + "pomodoro": ":/assets/icons/blue/pomodoro.svg", + "timeline": ":/assets/icons/blue/timeline.svg", + "calendar": ":/assets/icons/blue/calendar.svg", + "settings": ":/assets/icons/blue/setting.svg" + }, + "icon_width": "28", + "icon_height": "28" + } +} \ No newline at end of file diff --git a/assets/themes/default.json b/assets/themes/default.json index 6531390..96d86c7 100644 --- a/assets/themes/default.json +++ b/assets/themes/default.json @@ -3,20 +3,20 @@ "background": "#e6f4ff", "border": "1.5px solid #1890ff", "border_radius": "8", - "text_color": "#1890ff", + "text_color": "#ff005c", "font_size": "16", "font_family": "Arial", "padding_vertical": "0", "padding_horizontal": "12", "hover_background": "#bae0ff", "icons": { - "home": ":assets/icons/default/home.svg", - "habit": ":assets/icons/default/habit.svg", - "event": ":assets/icons/default/event.svg", - "pomodoro": ":assets/icons/default/pomodoro.svg", - "timeline": ":assets/icons/default/timeline.svg", - "calendar": ":assets/icons/default/calendar.svg", - "settings": ":assets/icons/default/setting.svg" + "home": ":/assets/icons/default/home.svg", + "habit": ":/assets/icons/default/habit.svg", + "event": ":/assets/icons/default/event.svg", + "pomodoro": ":/assets/icons/default/pomodoro.svg", + "timeline": ":/assets/icons/default/timeline.svg", + "calendar": ":/assets/icons/default/calendar.svg", + "settings": ":/assets/icons/default/setting.svg" }, "icon_width": "24", "icon_height": "24" @@ -32,13 +32,13 @@ "padding_horizontal": "12", "hover_background": "#096dd9", "icons": { - "home": ":assets/icons/default/home.svg", - "habit": ":assets/icons/default/habit.svg", - "event": ":assets/icons/default/event.svg", - "pomodoro": ":assets/icons/default/pomodoro.svg", - "timeline": ":assets/icons/default/timeline.svg", - "calendar": ":assets/icons/default/calendar.svg", - "settings": ":assets/icons/default/setting.svg" + "home": ":/assets/icons/default/home.svg", + "habit": ":/assets/icons/default/habit.svg", + "event": ":/assets/icons/default/event.svg", + "pomodoro": ":/assets/icons/default/pomodoro.svg", + "timeline": ":/assets/icons/default/timeline.svg", + "calendar": ":/assets/icons/default/calendar.svg", + "settings": ":/assets/icons/default/setting.svg" }, "icon_width": "28", "icon_height": "28" diff --git a/assets/themes/themes.qrc b/assets/themes/themes.qrc new file mode 100644 index 0000000..66a5c2a --- /dev/null +++ b/assets/themes/themes.qrc @@ -0,0 +1,7 @@ + + + blue.json + default.json + themes.json + + \ No newline at end of file diff --git a/src/DBLayer.cpp b/src/DBLayer.cpp index bc5ce5c..324e338 100644 --- a/src/DBLayer.cpp +++ b/src/DBLayer.cpp @@ -6,9 +6,6 @@ DBLayer::DBLayer(std::string db_file_name) : db_file_name_(std::move(db_file_name)) { - // 调试:输出可用数据库驱动 - qDebug() << "可用数据库驱动:" << QSqlDatabase::drivers(); - qDebug() << "准备使用数据库文件:" << QString::fromStdString(db_file_name_); // 初始化 SQLite 数据库连接 db_ = QSqlDatabase::addDatabase("QSQLITE"); db_.setDatabaseName(QString::fromStdString(db_file_name_)); @@ -118,23 +115,6 @@ DBLayer::DBLayer(std::string db_file_name) : db_file_name_(std::move(db_file_nam qDebug() << "UserSettingsTable 创建成功"; } - // 创建 PomodoroStateTable 用于存储番茄钟状态 - QString pomodoroStateSql = "CREATE TABLE IF NOT EXISTS PomodoroStateTable (" - "userId INTEGER PRIMARY KEY, " - "state INTEGER, " - "totalSeconds INTEGER, " - "remainingSeconds INTEGER, " - "remark TEXT, " - "startTime TEXT)"; - if (!query.exec(pomodoroStateSql)) - { - qDebug() << "PomodoroStateTable 创建失败:" << query.lastError().text(); - } - else - { - qDebug() << "PomodoroStateTable 创建成功"; - } - // 构造函数中初始化完后关闭数据库 closeDatabase(); } @@ -146,15 +126,11 @@ DBLayer::~DBLayer() bool DBLayer::openDatabase() const { - qDebug() << "[openDatabase] 当前数据库文件:" << db_.databaseName(); if (db_.isOpen()) { - qDebug() << "[openDatabase] 数据库已打开"; return true; } - bool ok = db_.open(); - qDebug() << "[openDatabase] 尝试打开数据库,结果:" << ok << ", 错误信息:" << db_.lastError().text(); - return ok; + return db_.open(); } void DBLayer::closeDatabase() const @@ -775,7 +751,7 @@ std::size_t DBLayer::getCurrentUserID() const currentUserId = query.value("userId").toULongLong(); } - // 不在这里关闭数据库,让调用者决定何时关闭 + closeDatabase(); return currentUserId; } @@ -840,144 +816,3 @@ bool DBLayer::setActiveHabit(std::size_t habit_id) closeDatabase(); return true; } - - -bool DBLayer::savePomodoroState(int state, int total_seconds, int remaining_seconds, const std::string& remark, const std::string& start_time) -{ - if (!openDatabase()) - { - //qDebug() << "数据库打开失败,无法保存番茄钟状态"; - return false; - } - - std::size_t currentUserId = getCurrentUserID(); - if (currentUserId == 0) - { - // 如果没有登录用户,使用默认用户ID 1 - currentUserId = 1; - //qDebug() << "没有当前登录用户,使用默认用户ID:" << currentUserId; - } - - // 确保数据库连接仍然打开 - if (!db_.isOpen()) { - //qDebug() << "数据库连接已关闭,重新打开"; - if (!openDatabase()) { - //qDebug() << "重新打开数据库失败"; - return false; - } - } - - QSqlQuery query(db_); - query.prepare("INSERT OR REPLACE INTO PomodoroStateTable " - "(userId, state, totalSeconds, remainingSeconds, remark, startTime) " - "VALUES (:userId, :state, :totalSeconds, :remainingSeconds, :remark, :startTime)"); - - query.bindValue(":userId", static_cast(currentUserId)); - query.bindValue(":state", state); - query.bindValue(":totalSeconds", total_seconds); - query.bindValue(":remainingSeconds", remaining_seconds); - query.bindValue(":remark", QString::fromStdString(remark)); - query.bindValue(":startTime", QString::fromStdString(start_time)); - - if (!query.exec()) - { - //qDebug() << "保存番茄钟状态失败:" << query.lastError().text(); - closeDatabase(); - return false; - } - - closeDatabase(); - return true; -} - -bool DBLayer::loadPomodoroState(int& state, int& total_seconds, int& remaining_seconds, std::string& remark, std::string& start_time) -{ - if (!openDatabase()) - { - //qDebug() << "数据库打开失败,无法加载番茄钟状态"; - return false; - } - - std::size_t currentUserId = getCurrentUserID(); - if (currentUserId == 0) - { - // 如果没有登录用户,使用默认用户ID 1 - currentUserId = 1; - //qDebug() << "没有当前登录用户,使用默认用户ID:" << currentUserId; - } - - // 确保数据库连接仍然打开 - if (!db_.isOpen()) { - //qDebug() << "数据库连接已关闭,重新打开"; - if (!openDatabase()) { - //qDebug() << "重新打开数据库失败"; - return false; - } - } - - QSqlQuery query(db_); - query.prepare("SELECT state, totalSeconds, remainingSeconds, remark, startTime " - "FROM PomodoroStateTable WHERE userId = :userId"); - query.bindValue(":userId", static_cast(currentUserId)); - - if (!query.exec()) - { - //qDebug() << "查询番茄钟状态失败:" << query.lastError().text(); - closeDatabase(); - return false; - } - - if (query.next()) - { - state = query.value("state").toInt(); - total_seconds = query.value("totalSeconds").toInt(); - remaining_seconds = query.value("remainingSeconds").toInt(); - remark = query.value("remark").toString().toStdString(); - start_time = query.value("startTime").toString().toStdString(); - closeDatabase(); - return true; - } - - closeDatabase(); - return false; // 没有找到保存的状态 -} - -bool DBLayer::clearPomodoroState() -{ - if (!openDatabase()) - { - //qDebug() << "数据库打开失败,无法清除番茄钟状态"; - return false; - } - - std::size_t currentUserId = getCurrentUserID(); - if (currentUserId == 0) - { - // 如果没有登录用户,使用默认用户ID 1 - currentUserId = 1; - //qDebug() << "没有当前登录用户,使用默认用户ID:" << currentUserId; - } - - // 确保数据库连接仍然打开 - if (!db_.isOpen()) { - //qDebug() << "数据库连接已关闭,重新打开"; - if (!openDatabase()) { - //qDebug() << "重新打开数据库失败"; - return false; - } - } - - QSqlQuery query(db_); - query.prepare("DELETE FROM PomodoroStateTable WHERE userId = :userId"); - query.bindValue(":userId", static_cast(currentUserId)); - - if (!query.exec()) - { - //qDebug() << "清除番茄钟状态失败:" << query.lastError().text(); - closeDatabase(); - return false; - } - - closeDatabase(); - return true; -} \ No newline at end of file diff --git a/src/DBLayer.h b/src/DBLayer.h index 4db27bc..8ef3906 100644 --- a/src/DBLayer.h +++ b/src/DBLayer.h @@ -218,37 +218,6 @@ class DBLayer */ [[nodiscard]] std::size_t getCurrentUserID() const; - /** - * @brief 保存番茄钟状态 - * @author 遥远 - * @param state 番茄钟状态(0-停止,1-工作,2-休息) - * @param total_seconds 总时长(秒) - * @param remaining_seconds 剩余时长(秒) - * @param remark 备注信息 - * @param start_time 开始时间 - * @return 保存是否成功 - */ - bool savePomodoroState(int state, int total_seconds, int remaining_seconds, const std::string& remark, const std::string& start_time); - - /** - * @brief 加载番茄钟状态 - * @author 遥远 - * @param state 番茄钟状态(输出参数) - * @param total_seconds 总时长(输出参数) - * @param remaining_seconds 剩余时长(输出参数) - * @param remark 备注信息(输出参数) - * @param start_time 开始时间(输出参数) - * @return 是否成功加载到状态 - */ - bool loadPomodoroState(int& state, int& total_seconds, int& remaining_seconds, std::string& remark, std::string& start_time); - - /** - * @brief 清除番茄钟状态 - * @author 遥远 - * @return 清除是否成功 - */ - bool clearPomodoroState(); - /** * @brief 停用指定ID的习惯 * @author XTUG diff --git a/src/PomodoroWidget.cpp b/src/PomodoroWidget.cpp index 37abd6e..1f29b2f 100644 --- a/src/PomodoroWidget.cpp +++ b/src/PomodoroWidget.cpp @@ -1,58 +1,96 @@ #include "PomodoroWidget.h" -#include -#include -#include +#include"Pomodoro.h" PomodoroWidget::PomodoroWidget(QWidget *parent) - : QWidget(parent), state_(IDLE), total_seconds_(0), remaining_seconds_(0) + : QWidget(parent), state_(IDLE), total_seconds_(0), remaining_seconds_(0), pause_duration_(0) { - // 设置固定大小 - setFixedSize(400, 400); - - // 初始化圆形钟参数 - circle_radius_ = 140; - circle_center_ = QPoint(200, 200); - - initCircularInterface(); - setupButtonStyles(); - - // 初始化计时器 + const QFont font("Microsoft YaHei", 20); + + hours_edit_ = new QLineEdit("00", this); + minutes_edit_ = new QLineEdit("00", this); + seconds_edit_ = new QLineEdit("00", this); + control_button_ = new QPushButton("开始", this); + time_display_ = new QLabel("00:00:00", this); + + hours_edit_->setFont(font); + minutes_edit_->setFont(font); + seconds_edit_->setFont(font); + control_button_->setFont(font); + time_display_->setFont(font); + time_display_->setAlignment(Qt::AlignCenter); + + hours_edit_->setFixedWidth(60); + minutes_edit_->setFixedWidth(60); + seconds_edit_->setFixedWidth(60); + + const auto time_layout = new QHBoxLayout; + time_layout->addWidget(hours_edit_); + time_layout->addWidget(new QLabel(":")); + time_layout->addWidget(minutes_edit_); + time_layout->addWidget(new QLabel(":")); + time_layout->addWidget(seconds_edit_); + + const auto main_layout = new QVBoxLayout(this); + main_layout->addLayout(time_layout); + main_layout->addWidget(control_button_); + main_layout->addWidget(time_display_); + + connect(control_button_, &QPushButton::clicked, this, &PomodoroWidget::handleControlButton); + timer_ = new QTimer(this); timer_->setInterval(1000); // 每秒更新 connect(timer_, &QTimer::timeout, this, &PomodoroWidget::updateTimer); - - // 设置初始时间显示 - updateTimeDisplay(); - updateRemarkDisplay(); - - // 强制重绘 - update(); } PomodoroWidget::PomodoroWidget(const Pomodoro& pomo, QWidget* parent) - : QWidget(parent), state_(RUNNING) + : QWidget(parent), state_(RUNNING), pause_duration_(0) { - // 设置固定大小 - setFixedSize(400, 400); - - // 初始化圆形钟参数 - circle_radius_ = 140; - circle_center_ = QPoint(200, 200); - - initCircularInterface(); - setupButtonStyles(); - + const QFont font("Microsoft YaHei", 20); + + hours_edit_ = new QLineEdit("00", this); + minutes_edit_ = new QLineEdit("00", this); + seconds_edit_ = new QLineEdit("00", this); + control_button_ = new QPushButton("暂停", this); + time_display_ = new QLabel("00:00:00", this); + + hours_edit_->setFont(font); + minutes_edit_->setFont(font); + seconds_edit_->setFont(font); + control_button_->setFont(font); + time_display_->setFont(font); + time_display_->setAlignment(Qt::AlignCenter); + + hours_edit_->setFixedWidth(60); + minutes_edit_->setFixedWidth(60); + seconds_edit_->setFixedWidth(60); + + const auto time_layout = new QHBoxLayout; + time_layout->addWidget(hours_edit_); + time_layout->addWidget(new QLabel(":")); + time_layout->addWidget(minutes_edit_); + time_layout->addWidget(new QLabel(":")); + time_layout->addWidget(seconds_edit_); + + const auto main_layout = new QVBoxLayout(this); + main_layout->addLayout(time_layout); + main_layout->addWidget(control_button_); + main_layout->addWidget(time_display_); + // 设置初始时间 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; + hours_edit_->setText(QString::number(h).rightJustified(2, '0')); + minutes_edit_->setText(QString::number(m).rightJustified(2, '0')); + seconds_edit_->setText(QString::number(s).rightJustified(2, '0')); total_seconds_ = h * 3600 + m * 60 + s; remaining_seconds_ = total_seconds_; start_time_ = QTime::currentTime(); remark_ = QString::fromStdString(pomo.record); - // 初始化计时器 + connect(control_button_, &QPushButton::clicked, this, &PomodoroWidget::handleControlButton); + timer_ = new QTimer(this); timer_->setInterval(1000); // 每秒更新 connect(timer_, &QTimer::timeout, this, &PomodoroWidget::updateTimer); @@ -98,25 +136,25 @@ void PomodoroWidget::initCircularInterface() image_button_ = new QPushButton("📷", top_container); image_button_->setFixedSize(30, 30); image_button_->setStyleSheet("QPushButton { border: none; background: transparent; font-size: 25px; }"); - + music_button_ = new QPushButton("🎵", top_container); music_button_->setFixedSize(30, 30); music_button_->setStyleSheet("QPushButton { border: none; background: transparent; font-size: 25px; }"); - + top_layout->addStretch(); top_layout->addWidget(image_button_); top_layout->addWidget(music_button_); top_layout->addStretch(); - + // 创建备注显示区域 QWidget* remark_container = new QWidget(circle_container); remark_container->setFixedHeight(15); remark_container->setStyleSheet("background: transparent;"); - + QVBoxLayout* remark_layout = new QVBoxLayout(remark_container); remark_layout->setContentsMargins(0, 0, 0, 0); remark_layout->setSpacing(0); - + remark_label_ = new QLabel("", remark_container); remark_label_->setAlignment(Qt::AlignCenter); remark_label_->setStyleSheet( @@ -129,16 +167,16 @@ void PomodoroWidget::initCircularInterface() "}" ); remark_layout->addWidget(remark_label_, 0, Qt::AlignCenter); - + // 创建中间时间显示容器 - 占据更多空间 QWidget* time_container = new QWidget(circle_container); time_container->setFixedHeight(220); time_container->setStyleSheet("background: transparent;"); - + QVBoxLayout* time_layout = new QVBoxLayout(time_container); time_layout->setContentsMargins(0, 0, 0, 0); time_layout->setSpacing(20); - + // 时间输入框(可编辑) time_edit_ = new QLineEdit("00 : 00 : 00", time_container); time_edit_->setAlignment(Qt::AlignCenter); @@ -154,7 +192,7 @@ void PomodoroWidget::initCircularInterface() " margin-top: -5px;" "}" ); - + // 时间显示标签(只读) time_display_ = new QLabel("00 : 00 : 00", time_container); time_display_->setAlignment(Qt::AlignCenter); @@ -169,25 +207,25 @@ void PomodoroWidget::initCircularInterface() "}" ); time_display_->hide(); // 初始隐藏,显示输入框 - + time_layout->addStretch(); time_layout->addWidget(time_edit_, 0, Qt::AlignCenter); time_layout->addWidget(time_display_, 0, Qt::AlignCenter); time_layout->addStretch(); - + // 创建底部按钮容器 - 更靠近时间显示 QWidget* bottom_container = new QWidget(circle_container); bottom_container->setFixedHeight(50); bottom_container->setStyleSheet("background: transparent;"); - + QHBoxLayout* bottom_layout = new QHBoxLayout(bottom_container); bottom_layout->setContentsMargins(0, 0, 0, 0); bottom_layout->setSpacing(50); - + control_button_ = new QPushButton("▶", bottom_container); control_button_->setFixedSize(35, 35); control_button_->setStyleSheet("QPushButton { border: none; background: transparent; font-size: 30px; }"); - + reset_button_ = new QPushButton("🔄", bottom_container); reset_button_->setFixedSize(30, 30); reset_button_->setStyleSheet("QPushButton { border: none; background: transparent; font-size: 25px; }"); @@ -316,212 +354,78 @@ void PomodoroWidget::setupButtonStyles() if (music_button_) music_button_->setStyleSheet(music_button_->styleSheet() + hover_style); if (control_button_) control_button_->setStyleSheet(control_button_->styleSheet() + hover_style); if (reset_button_) reset_button_->setStyleSheet(reset_button_->styleSheet() + hover_style); + control_button_->setText("暂停"); } void PomodoroWidget::handleControlButton() { - if (state_ == IDLE) { - // 检查是否已设置时间 + switch (state_) { + case IDLE: { + const int h = hours_edit_->text().toInt(); + const int m = minutes_edit_->text().toInt(); + const int s = seconds_edit_->text().toInt(); + total_seconds_ = h * 3600 + m * 60 + s; + if (total_seconds_ == 0) { - QMessageBox::warning(this, "提示", "请先设置时间!"); + QMessageBox::warning(this, "错误", "时间不能为零!"); return; } - - // 输入备注 + bool ok; - QString remark = QInputDialog::getText(this, "输入备注", "请输入备注信息:", QLineEdit::Normal, "", &ok); + const QString input = QInputDialog::getText(this, "备注", "请输入番茄钟备注:", QLineEdit::Normal, "", &ok); if (!ok) return; - - if (remark.trimmed().isEmpty()) { - QMessageBox::warning(this, "提示", "备注不能为空!"); - return; - } - - remark_ = remark.trimmed(); - updateRemarkDisplay(); - - // 开始计时 + remark_ = input; + + remaining_seconds_ = total_seconds_; + start_time_ = QTime::currentTime(); timer_->start(); state_ = RUNNING; - control_button_->setText("⏸"); - - // 切换到只读显示模式 - time_edit_->hide(); - time_display_->show(); - time_display_->setText(formatTime(remaining_seconds_)); - - // 发出状态改变信号 - emit stateChanged(); - - } else if (state_ == RUNNING) { - // 暂停 + control_button_->setText("暂停"); + break; + } + case RUNNING: + pause_start_ = QTime::currentTime(); timer_->stop(); state_ = PAUSED; - control_button_->setText("▶"); - - // 发出状态改变信号 - emit stateChanged(); - - } else if (state_ == PAUSED) { - // 继续 + control_button_->setText("继续"); + break; + case PAUSED: + pause_duration_ += pause_start_.secsTo(QTime::currentTime()); timer_->start(); state_ = RUNNING; - control_button_->setText("⏸"); - - // 发出状态改变信号 - emit stateChanged(); - } - - update(); -} - -void PomodoroWidget::handleResetButton() -{ - // 重置所有状态 - timer_->stop(); - state_ = IDLE; - total_seconds_ = 0; - remaining_seconds_ = 0; - remark_.clear(); - - // 重置UI - control_button_->setText("▶"); - time_edit_->setText("00 : 00 : 00"); - time_edit_->show(); - time_display_->hide(); - updateRemarkDisplay(); - - update(); - - // 发出状态改变信号 - emit stateChanged(); -} - -void PomodoroWidget::handleTimeEditFinished() -{ - QString time_text = time_edit_->text(); - int hours, minutes, seconds; - - if (parseTimeInput(time_text, hours, minutes, seconds)) { - total_seconds_ = hours * 3600 + minutes * 60 + seconds; - remaining_seconds_ = total_seconds_; - time_edit_->setText(formatTime(total_seconds_)); - } else { - QMessageBox::warning(this, "格式错误", "时间格式不正确!请使用 HH:MM:SS 格式"); - time_edit_->setText("00 : 00 : 00"); - total_seconds_ = 0; - remaining_seconds_ = 0; + control_button_->setText("暂停"); + break; } } void PomodoroWidget::updateTimer() { - if (state_ == RUNNING) { - if (remaining_seconds_ > 0) { - remaining_seconds_--; - time_display_->setText(formatTime(remaining_seconds_)); - update(); - emit timerUpdated(); - } - if (remaining_seconds_ <= 0) { - remaining_seconds_ = 0; - timer_->stop(); - state_ = IDLE; - control_button_->setText("▶"); - QMessageBox::information(this, "时间到", "番茄钟时间到!"); - emit stateChanged(); - } - } -} + const int elapsed = start_time_.secsTo(QTime::currentTime()) - pause_duration_; + remaining_seconds_ = total_seconds_ - elapsed; -void PomodoroWidget::updateTimeDisplay() -{ - if (time_edit_) { - time_edit_->setText(formatTime(total_seconds_)); - } - if (time_display_) { - time_display_->setText(formatTime(remaining_seconds_)); + if (remaining_seconds_ <= 0) { + timer_->stop(); + handleTimeUp(); + return; } -} -void PomodoroWidget::updateRemarkDisplay() -{ - if (remark_label_) { - remark_label_->setText(remark_); - } -} + const int h = remaining_seconds_ / 3600; + const int m = remaining_seconds_ % 3600 / 60; + const int s = remaining_seconds_ % 60; -bool PomodoroWidget::parseTimeInput(const QString& input, int& hours, int& minutes, int& seconds) -{ - QStringList parts = input.split(":"); - if (parts.size() != 3) return false; - - bool ok1, ok2, ok3; - hours = parts[0].trimmed().toInt(&ok1); - minutes = parts[1].trimmed().toInt(&ok2); - seconds = parts[2].trimmed().toInt(&ok3); - - if (!ok1 || !ok2 || !ok3) return false; - if (hours < 0 || hours > 23 || minutes < 0 || minutes > 59 || seconds < 0 || seconds > 59) return false; - - return true; + time_display_->setText(QString("%1:%2:%3") + .arg(h, 2, 10, QLatin1Char('0')) + .arg(m, 2, 10, QLatin1Char('0')) + .arg(s, 2, 10, QLatin1Char('0'))); } -QString PomodoroWidget::formatTime(int total_seconds) const +void PomodoroWidget::handleTimeUp() { - int hours = total_seconds / 3600; - int minutes = (total_seconds % 3600) / 60; - int seconds = total_seconds % 60; - - return QString("%1 : %2 : %3") - .arg(hours, 2, 10, QChar('0')) - .arg(minutes, 2, 10, QChar('0')) - .arg(seconds, 2, 10, QChar('0')); -} - -void PomodoroWidget::handleImageButton() -{ - // 图片按钮功能(待实现) - QMessageBox::information(this, "图片", "图片功能待实现"); -} - -void PomodoroWidget::handleMusicButton() -{ - // 音乐按钮功能(待实现) - QMessageBox::information(this, "音乐", "音乐功能待实现"); -} - -QString PomodoroWidget::getTimeDisplayText() const -{ - if (state_ == IDLE) { - return "00 : 00 : 00"; - } - return formatTime(remaining_seconds_); -} - -void PomodoroWidget::restoreState(int state, int total_seconds, int remaining_seconds, const QString& remark, const QString& /*start_time*/) -{ - if (timer_) timer_->stop(); - state_ = static_cast(state); - total_seconds_ = total_seconds; - remaining_seconds_ = remaining_seconds; - remark_ = remark; - start_time_ = QTime::currentTime(); // 仅用于兼容,不参与倒计时计算 - - if (state_ == IDLE) { - control_button_->setText("▶"); - time_edit_->setText("00 : 00 : 00"); - time_edit_->show(); - time_display_->hide(); - } else { - control_button_->setText(state_ == RUNNING ? "⏸" : "▶"); - time_edit_->hide(); - time_display_->show(); - time_display_->setText(formatTime(remaining_seconds_)); - if (state_ == RUNNING) timer_->start(); - } - updateRemarkDisplay(); - update(); - emit stateChanged(); + QMessageBox::information(this, "时间到", QString("番茄钟已完成!\n备注:%1").arg(remark_)); + state_ = IDLE; + control_button_->setText("开始"); + remaining_seconds_ = 0; + pause_duration_ = 0; + time_display_->setText("00:00:00"); } diff --git a/src/PomodoroWidget.h b/src/PomodoroWidget.h index 842688a..21d7c65 100644 --- a/src/PomodoroWidget.h +++ b/src/PomodoroWidget.h @@ -8,88 +8,78 @@ #ifndef POMODOROWIDGET_H #define POMODOROWIDGET_H -#include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include #include +#include #include -#include -#include -#include +#include +#include #include "Pomodoro.h" -class PomodoroWidget : public QWidget +/** + * @class PomodoroWidget + * @brief 实现番茄钟计时器功能的Qt控件 + */ +class PomodoroWidget final : public QWidget { - Q_OBJECT +Q_OBJECT public: - enum State { - IDLE, // 空闲状态 - RUNNING, // 运行状态 - PAUSED // 暂停状态 - }; - - explicit PomodoroWidget(QWidget *parent = nullptr); + /** + * @brief 构造函数,初始化番茄钟控件 + * @param parent 父窗口部件,默认为nullptr + */ + explicit PomodoroWidget(QWidget* parent = nullptr); + /** + * @brief 新增:根据番茄钟数据初始化 + */ explicit PomodoroWidget(const Pomodoro& pomo, QWidget* parent = nullptr); - - // 公共方法,供外部获取状态和信息 - 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_; } - - // 恢复番茄钟状态 - void restoreState(int state, int total_seconds, int remaining_seconds, const QString& remark, const QString& start_time); - -signals: - void stateChanged(); // 状态改变信号 - void timerUpdated(); // 定时器更新信号 - -protected: - void paintEvent(QPaintEvent* event) override; private slots: - void handleImageButton(); - void handleMusicButton(); + /** + * @brief 处理控制按钮点击事件 + */ void handleControlButton(); - void handleResetButton(); - void handleTimeEditFinished(); - void updateTimer(); -private: + /** + * @brief 更新计时器显示 + */ + void updateTimer(); - State state_; - int total_seconds_; - int remaining_seconds_; - QTimer* timer_; - QTime start_time_; - QString remark_; + /** + * @brief 处理时间到事件 + */ + void handleTimeUp(); - // UI组件 - QPushButton* image_button_; - QPushButton* music_button_; - QPushButton* control_button_; - QPushButton* reset_button_; - QLineEdit* time_edit_; // 时间输入框 - QLabel* remark_label_; // 备注显示标签 - QLabel* time_display_; // 时间显示标签 +private: + /** + * @enum State + * @brief 定义番茄钟的状态 + */ + enum State + { + IDLE, /**< 空闲状态 */ + RUNNING,/**< 运行状态 */ + PAUSED /**< 暂停状态 */ + }; - // 圆形钟参数 - int circle_radius_; - QPoint circle_center_; + State state_; /**< 当前番茄钟状态 */ + QTimer* timer_; /**< Qt计时器对象 */ + QTime start_time_; /**< 开始时间 */ + int total_seconds_; /**< 总秒数 */ + int remaining_seconds_; /**< 剩余秒数 */ + int pause_duration_; /**< 暂停持续时间 */ + QTime pause_start_; /**< 暂停开始时间 */ - // 私有方法 - 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; + QLineEdit* hours_edit_; /**< 小时输入框 */ + QLineEdit* minutes_edit_; /**< 分钟输入框 */ + QLineEdit* seconds_edit_; /**< 秒输入框 */ + QPushButton* control_button_;/**< 控制按钮 */ + QLabel* time_display_; /**< 时间显示标签 */ + QString remark_; /**< 备注信息 */ }; #endif // POMODOROWIDGET_H \ No newline at end of file diff --git a/src/ServiceLayer.cpp b/src/ServiceLayer.cpp index 2adf71a..160e626 100644 --- a/src/ServiceLayer.cpp +++ b/src/ServiceLayer.cpp @@ -4,7 +4,7 @@ #include #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) @@ -305,31 +305,8 @@ QStringList ServiceLayer::getAvailableThemes() for (const auto& value : themes) { theme_names.append(value.toString()); } - return theme_names; -} - -bool ServiceLayer::savePomodoroState(int state, int total_seconds, int remaining_seconds, const std::string& remark, const std::string& start_time) -{ - // 参数验证 - if (state < 0 || state > 2 || total_seconds <= 0 || remaining_seconds < 0 || remaining_seconds > total_seconds) - { - return false; - } - // 调用数据库层保存番茄钟状态 - return this->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) -{ - // 调用数据库层加载番茄钟状态 - return this->db_layer.loadPomodoroState(state, total_seconds, remaining_seconds, remark, start_time); -} - -bool ServiceLayer::clearPomodoroState() -{ - // 调用数据库层清除番茄钟状态 - return this->db_layer.clearPomodoroState(); + return theme_names; } QString ServiceLayer::getCurrentThemeName() diff --git a/src/ServiceLayer.h b/src/ServiceLayer.h index 62187f9..35582a2 100644 --- a/src/ServiceLayer.h +++ b/src/ServiceLayer.h @@ -162,7 +162,7 @@ class ServiceLayer bool pomodoroTick(const Pomodoro& pomodoro, const Time& count_time); /** - * @brief 获取指定日期的习惯打卡记录统计 + * @brief 获取指定月份的习惯打卡记录统计 * @author Rain * @param date 指定日期(使用其年月部分) * @return 每月每天的实际打卡数和应当打卡数 @@ -241,37 +241,6 @@ class ServiceLayer * @return 主题配置的JSON对象 */ static QJsonObject getThemeConfig(const QString &theme_name); - - /** - * @brief 保存番茄钟状态到数据库 - * @author Rain - * @param state 番茄钟状态 (0=IDLE, 1=RUNNING, 2=PAUSED) - * @param total_seconds 总秒数 - * @param remaining_seconds 剩余秒数 - * @param remark 备注 - * @param start_time 开始时间戳 - * @return 保存是否成功 - */ - bool savePomodoroState(int state, int total_seconds, int remaining_seconds, const std::string& remark, const std::string& start_time); - - /** - * @brief 从数据库加载番茄钟状态 - * @author Rain - * @param state 输出:番茄钟状态 - * @param total_seconds 输出:总秒数 - * @param remaining_seconds 输出:剩余秒数 - * @param remark 输出:备注 - * @param start_time 输出:开始时间戳 - * @return 加载是否成功 - */ - bool loadPomodoroState(int& state, int& total_seconds, int& remaining_seconds, std::string& remark, std::string& start_time); - - /** - * @brief 清除番茄钟状态 - * @author Rain - * @return 清除是否成功 - */ - bool clearPomodoroState(); }; inline const QString ServiceLayer::THEMES_PATH = "themes/themes.json"; // 主题列表配置文件路径 diff --git a/src/ViewLayer.cpp b/src/ViewLayer.cpp index 1bb34cf..c9d4320 100644 --- a/src/ViewLayer.cpp +++ b/src/ViewLayer.cpp @@ -1,16 +1,12 @@ #include #include +#include #include -#include -#include -#include -#include #include "ViewLayer.h" #include -#include #include "CalendarView.h" #include "NavigationBar.h" - +#include ViewLayer::ViewLayer(QWidget *parent) : QWidget(parent), cur_view_type(ViewType::MAIN_VIEW) @@ -41,41 +37,6 @@ void ViewLayer::init() initSettingsView(); resetCurrentView(ViewType::MAIN_VIEW); - - // 程序启动后立即检查并恢复番茄钟状态 - QTimer::singleShot(100, this, &ViewLayer::checkAndRestorePomodoroState); - - // 测试数据库操作 - QTimer::singleShot(500, this, [this]() { - std::cout << "=== 测试数据库操作 ===" << std::endl; - - // 测试保存 - bool save_result = sv_Layer.savePomodoroState(1, 300, 250, "测试番茄钟", "2024-01-01 10:00:00"); - std::cout << "测试保存结果:" << (save_result ? "成功" : "失败") << std::endl; - - // 测试加载 - int state, total_seconds, remaining_seconds; - std::string remark, start_time; - bool load_result = sv_Layer.loadPomodoroState(state, total_seconds, remaining_seconds, remark, start_time); - std::cout << "测试加载结果:" << (load_result ? "成功" : "失败") << std::endl; - - if (load_result) { - std::cout << "加载的数据:" << std::endl; - std::cout << " 状态:" << state << std::endl; - std::cout << " 总时长:" << total_seconds << std::endl; - std::cout << " 剩余时长:" << remaining_seconds << std::endl; - std::cout << " 备注:" << remark << std::endl; - std::cout << " 开始时间:" << start_time << std::endl; - } - - // 测试清除 - bool clear_result = sv_Layer.clearPomodoroState(); - std::cout << "测试清除结果:" << (clear_result ? "成功" : "失败") << std::endl; - - std::cout << "=== 数据库操作测试完成 ===" << std::endl; - // 移除自动退出逻辑,方便用户手动测试 - // QTimer::singleShot(3000, qApp, &QApplication::quit); - }); } void ViewLayer::resetCurrentView(ViewType view) @@ -235,15 +196,21 @@ void ViewLayer::initEventManageView() event_manage_widget->setLayout(layout); } - // 顶部标题 + // 顶部标题 + 返回按钮 QHBoxLayout *topLayout = new QHBoxLayout(); QLabel *title = new QLabel("事项管理", event_manage_widget); QFont titleFont; titleFont.setPointSize(18); titleFont.setBold(true); title->setFont(titleFont); + QPushButton *backButton = new QPushButton(); + backButton->setIcon(QIcon(":/assets/images/back.png")); + backButton->setIconSize(QSize(30, 30)); + backButton->setFixedSize(45, 45); + connect(backButton, &QPushButton::clicked, this, &ViewLayer::onBackToNavigation); topLayout->addWidget(title); topLayout->addStretch(); + topLayout->addWidget(backButton); layout->addLayout(topLayout, 0, 0, 1, 4); // 滚动区域和事项列表容器 @@ -315,8 +282,8 @@ void ViewLayer::initEventManageView() cardLayout->addWidget(remindLabel); cardLayout->addLayout(buttonLayout); - row = i / eventsPerRow; - col = i % eventsPerRow; + int row = i / eventsPerRow; + int col = i % eventsPerRow; grid->addWidget(eventCard, row, col); } @@ -579,12 +546,11 @@ void ViewLayer::initMainView() 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* indexLabel = new QLabel(QString::number(index + 1)); + indexLabel->setFixedSize(30, 50); + indexLabel->setAlignment(Qt::AlignCenter); + indexLabel->setStyleSheet("font-weight:bold;font-size:22px;border-radius:8px;background:#fff;border:1px solid #e0e0e0;"); // 日期 QLabel* dateLabel = new QLabel( @@ -594,7 +560,26 @@ void ViewLayer::initMainView() ); 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); }); + + // 删除按钮 + 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, "提示", "删除成功"); + initMainView(); + } else { + QMessageBox::warning(this, "错误", "删除失败"); + } + } + }); // 打卡次数 QLabel* checkinLabel = new QLabel( @@ -602,69 +587,12 @@ void ViewLayer::initMainView() ); 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(indexLabel); cardLayout->addWidget(dateLabel); + cardLayout->addWidget(editBtn); + cardLayout->addWidget(delBtn); cardLayout->addWidget(checkinLabel); - cardLayout->addWidget(checkinBtn); cardLayout->addStretch(); habitCard->setLayout(cardLayout); @@ -691,6 +619,38 @@ void ViewLayer::initMainView() habitGroup->setLayout(groupLayout); gridLayout->addWidget(habitGroup, 0, 0, 2, 1); + // 刷新图片按钮 + QPushButton* refreshBtn = new QPushButton(main_widget); + refreshBtn->setIcon(QIcon(":/assets/images/update.png")); + refreshBtn->setIconSize(QSize(48, 48)); + refreshBtn->setFixedSize(60, 60); + refreshBtn->setStyleSheet(R"( + border: none; + background: transparent; + outline: none; + )"); + refreshBtn->setToolTip("刷新当前页面"); + + QLabel* refreshLabel = new QLabel("点我更新", main_widget); + refreshLabel->setAlignment(Qt::AlignHCenter | Qt::AlignTop); + refreshLabel->setStyleSheet("font-size:14px; color:#888;"); + + // 用QWidget包裹刷新按钮和文字 + QWidget* refreshWidget = new QWidget(main_widget); + QVBoxLayout* refreshLayout = new QVBoxLayout(refreshWidget); + refreshLayout->addWidget(refreshBtn, 0, Qt::AlignHCenter); + refreshLayout->addWidget(refreshLabel, 0, Qt::AlignHCenter); + refreshLayout->setContentsMargins(0, 0, 0, 0); + refreshWidget->setLayout(refreshLayout); + + // 插入到主布局 habit 区下方 + gridLayout->addWidget(refreshWidget, 2, 0, Qt::AlignLeft); + + // 点击刷新 + connect(refreshBtn, &QPushButton::clicked, [this]() { + initMainView(); // 直接重新初始化主视图 + }); + // 2. 右上:事项区 QGroupBox* eventGroup = new QGroupBox("活跃事项", main_widget); QWidget* eventListContainer = new QWidget(); @@ -711,9 +671,24 @@ void ViewLayer::initMainView() 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); }); + connect(delBtn, &QPushButton::clicked, [this, event]() { + if (QMessageBox::question(this, "确认删除", "确定删除该事项吗?") == QMessageBox::Yes) { + if (sv_Layer.deleteEvent(event.event_id)) { + QMessageBox::information(this, "提示", "删除成功"); + initMainView(); + } 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; @@ -727,27 +702,26 @@ void ViewLayer::initMainView() // 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); - + QHBoxLayout* pomoLayout = new QHBoxLayout(pomoGroup); + Date pomoToday = std::chrono::year_month_day(std::chrono::floor(std::chrono::system_clock::now())); + DateRecord pomoTodayRecord = sv_Layer.getAllRecordsByDate(pomoToday); + QLayoutItem* child; + while ((child = pomoLayout->takeAt(0)) != nullptr) { + delete child->widget(); + delete child; + } + if (!pomoTodayRecord.pomodoro_records.empty()) { + pomoLayout->addStretch(1); + for (const auto& pair : pomoTodayRecord.pomodoro_records) { + const Pomodoro& pomo = pair.second; + PomodoroWidget* pomoWidget = new PomodoroWidget(pomo, pomoGroup); + pomoLayout->addWidget(pomoWidget); + } + pomoLayout->addStretch(1); + } else { + QLabel* noPomo = new QLabel("暂无番茄钟"); + pomoLayout->addWidget(noPomo, 0, Qt::AlignCenter); + } pomoGroup->setLayout(pomoLayout); gridLayout->addWidget(pomoGroup, 1, 1); @@ -799,27 +773,12 @@ void ViewLayer::initMainView() gridLayout->setRowStretch(0, 2); gridLayout->setRowStretch(1, 2); gridLayout->setRowStretch(2, 1); - gridLayout->setColumnStretch(0, 5); // 习惯框占5份 - gridLayout->setColumnStretch(1, 2); // 右侧区域占2份 + gridLayout->setColumnStretch(0, 2); + gridLayout->setColumnStretch(1, 3); // 把内容区加到主垂直布局 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::initTimelineView() @@ -833,15 +792,18 @@ void ViewLayer::initTimelineView() QVBoxLayout *layout = new QVBoxLayout(timeline_widget); - // 顶部标题 + // 顶部标题 + 返回按钮 QHBoxLayout *topLayout = new QHBoxLayout(); QLabel *title = new QLabel("时间线", timeline_widget); QFont titleFont; titleFont.setPointSize(18); titleFont.setBold(true); title->setFont(titleFont); + QPushButton *backButton = createButton(":/assets/images/back.png", "返回", 45, 45); + connect(backButton, &QPushButton::clicked, this, &ViewLayer::onBackToNavigation); topLayout->addWidget(title); topLayout->addStretch(); + topLayout->addWidget(backButton); layout->addLayout(topLayout); // 日期选择栏 @@ -922,6 +884,7 @@ void ViewLayer::refreshTimeline() // 向服务层请求当天记录 DateRecord raw_record = sv_Layer.getAllRecordsByDate(date); + std::vector events = sv_Layer.getEventsByDate(date); // 定义用于排序合并的向量(按 Time 升序) using TimelineItem = std::tuple; // time, type, content @@ -944,15 +907,13 @@ void ViewLayer::refreshTimeline() } // 插入事件记录 - for (const auto &pair : raw_record.event_records) + for (const auto &event : events) { - const Time &time = pair.first; - const Event &event = pair.second; - timeline_items.emplace_back(time, "事项 ", event.title); + timeline_items.emplace_back(event.event_time, "事件", event.title); } // 按时间排序 - std::ranges::sort(timeline_items, [](const TimelineItem &a, const TimelineItem &b) { + std::sort(timeline_items.begin(), timeline_items.end(), [](const TimelineItem &a, const TimelineItem &b) { return std::get<0>(a) < std::get<0>(b); }); @@ -983,74 +944,22 @@ void ViewLayer::initPomodoroView() titleFont.setPointSize(18); titleFont.setBold(true); title->setFont(titleFont); + QPushButton *backButton = new QPushButton(); + backButton->setIcon(QIcon(":/assets/images/back.png")); + backButton->setIconSize(QSize(30, 30)); + backButton->setFixedSize(45, 45); + connect(backButton, &QPushButton::clicked, this, &ViewLayer::onBackToNavigation); topLayout->addWidget(title); topLayout->addStretch(); + topLayout->addWidget(backButton); layout->addLayout(topLayout); - // 主体容器内容 - 圆形钟居中显示 - QWidget* center_container = new QWidget(pomodoro_widget); - QVBoxLayout* center_layout = new QVBoxLayout(center_container); - center_layout->setContentsMargins(0, 0, 0, 0); - + // 主体容器内容 if (!pomodoro_widget_component) { pomodoro_widget_component = new PomodoroWidget(pomodoro_widget); - - // 连接番茄钟信号到主界面更新 - connect(pomodoro_widget_component, &PomodoroWidget::stateChanged, this, &ViewLayer::updateMainPomodoroDisplay); - connect(pomodoro_widget_component, &PomodoroWidget::timerUpdated, this, &ViewLayer::updateMainPomodoroDisplay); - - // 番茄钟组件创建后,立即检查并恢复状态 - QTimer::singleShot(50, this, &ViewLayer::checkAndRestorePomodoroState); - - // 连接状态改变信号到保存状态 - connect(pomodoro_widget_component, &PomodoroWidget::stateChanged, this, [this]() { - if (pomodoro_widget_component) { - qDebug() << "番茄钟状态改变,当前状态:" << pomodoro_widget_component->getState(); - - // 如果是IDLE状态,清除数据库中的状态 - if (pomodoro_widget_component->getState() == PomodoroWidget::IDLE) { - qDebug() << "清除番茄钟状态"; - sv_Layer.clearPomodoroState(); - } else { - // 保存番茄钟状态到数据库 - qDebug() << "保存番茄钟状态到数据库"; - bool success = sv_Layer.savePomodoroState( - static_cast(pomodoro_widget_component->getState()), - pomodoro_widget_component->getTotalSeconds(), - pomodoro_widget_component->getRemainingSeconds(), - pomodoro_widget_component->getRemark().toStdString(), - QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss").toStdString() - ); - qDebug() << "保存番茄钟状态结果:" << (success ? "成功" : "失败"); - } - } - }); - - // 连接定时器更新信号到保存状态(每秒保存一次) - connect(pomodoro_widget_component, &PomodoroWidget::timerUpdated, this, [this]() { - if (pomodoro_widget_component && pomodoro_widget_component->getState() == PomodoroWidget::RUNNING) { - // 每秒保存运行中的番茄钟状态 - bool success = sv_Layer.savePomodoroState( - static_cast(pomodoro_widget_component->getState()), - pomodoro_widget_component->getTotalSeconds(), - pomodoro_widget_component->getRemainingSeconds(), - pomodoro_widget_component->getRemark().toStdString(), - QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss").toStdString() - ); - if (!success) { - qDebug() << "定时器更新时保存番茄钟状态失败"; - } - } - }); } - - // 添加弹性空间使圆形钟居中 - center_layout->addStretch(); - center_layout->addWidget(pomodoro_widget_component, 0, Qt::AlignCenter); - center_layout->addStretch(); - - layout->addWidget(center_container); + layout->addWidget(pomodoro_widget_component); } void ViewLayer::initSettingsView() @@ -1064,65 +973,22 @@ void ViewLayer::initSettingsView() auto *layout = new QVBoxLayout(settings_widget); - // 顶部:标题 + // 顶部:标题 + 返回按钮 QHBoxLayout *topLayout = new QHBoxLayout(); QLabel *title = new QLabel("个人设置", settings_widget); QFont titleFont; titleFont.setPointSize(18); titleFont.setBold(true); title->setFont(titleFont); + QPushButton *backButton = new QPushButton(); + backButton->setIcon(QIcon(":/assets/images/back.png")); + backButton->setIconSize(QSize(30, 30)); + backButton->setFixedSize(45, 45); + connect(backButton, &QPushButton::clicked, this, &ViewLayer::onBackToNavigation); topLayout->addWidget(title); topLayout->addStretch(); + topLayout->addWidget(backButton); layout->addLayout(topLayout); - - // 用户信息 - QHBoxLayout *userInfoLayout = new QHBoxLayout(); - QLabel *userLabel = new QLabel("用户名:", settings_widget); - QLineEdit *userName = new QLineEdit("XX要努力学习", settings_widget); - QLabel *idLabel = new QLabel("ID:", settings_widget); - QLineEdit *userId = new QLineEdit("123456789", settings_widget); - userInfoLayout->addWidget(userLabel); - userInfoLayout->addWidget(userName); - userInfoLayout->addWidget(idLabel); - userInfoLayout->addWidget(userId); - layout->addLayout(userInfoLayout); - - // UI皮肤选择 - QHBoxLayout *skinLayout = new QHBoxLayout(); - QLabel *skinLabel = new QLabel("UI皮肤", settings_widget); - QRadioButton *defaultSkin = new QRadioButton("默认", settings_widget); - QRadioButton *customSkin = new QRadioButton("哈比兔主题", settings_widget); - skinLayout->addWidget(skinLabel); - skinLayout->addWidget(defaultSkin); - skinLayout->addWidget(customSkin); - layout->addLayout(skinLayout); - - // 集成到Windows日历 - QHBoxLayout *calendarLayout = new QHBoxLayout(); - QLabel *calendarLabel = new QLabel("集成到Windows日历", settings_widget); - QCheckBox *calendarCheckBox = new QCheckBox(settings_widget); - calendarLayout->addWidget(calendarLabel); - calendarLayout->addWidget(calendarCheckBox); - layout->addLayout(calendarLayout); - - // 邮件提醒 - QHBoxLayout *emailLayout = new QHBoxLayout(); - QLabel *emailLabel = new QLabel("邮件提醒", settings_widget); - QCheckBox *emailCheckBox = new QCheckBox(settings_widget); - QLineEdit *emailInput = new QLineEdit(settings_widget); - emailLayout->addWidget(emailLabel); - emailLayout->addWidget(emailCheckBox); - emailLayout->addWidget(emailInput); - layout->addLayout(emailLayout); - - // 设置按钮 - QHBoxLayout *buttonLayout = new QHBoxLayout(); - QPushButton *saveButton = new QPushButton("保存", settings_widget); - QPushButton *cancelButton = new QPushButton("取消", settings_widget); - buttonLayout->addStretch(); - buttonLayout->addWidget(saveButton); - buttonLayout->addWidget(cancelButton); - layout->addLayout(buttonLayout); } void ViewLayer::initCalendarView() @@ -1139,15 +1005,18 @@ void ViewLayer::initCalendarView() layout = new QVBoxLayout(calendar_widget); } - // 标题 + // 标题和返回按钮 auto top_layout = new QHBoxLayout(); auto title = new QLabel("日历", calendar_widget); - QFont titleFont; - titleFont.setPointSize(18); - titleFont.setBold(true); - title->setFont(titleFont); + title->setFont(QFont("Arial", 18, QFont::Bold)); + auto backButton = new QPushButton(); + backButton->setIcon(QIcon(":/assets/images/back.png")); + backButton->setIconSize(QSize(30, 30)); + backButton->setFixedSize(45, 45); + connect(backButton, &QPushButton::clicked, this, &ViewLayer::onBackToNavigation); top_layout->addWidget(title); top_layout->addStretch(); + top_layout->addWidget(backButton); layout->addLayout(top_layout); // 创建 CalendarView 并传入 ServiceLayer @@ -1157,7 +1026,6 @@ void ViewLayer::initCalendarView() }); layout->addWidget(calendar_view); - calendar_widget->setLayout(layout); } @@ -1317,15 +1185,21 @@ void ViewLayer::initHabitManageView() habit_manage_widget->setLayout(gridLayout); } - // 顶部标题 + // 顶部标题 + 返回按钮 QHBoxLayout *topLayout = new QHBoxLayout(); QLabel *title = new QLabel("习惯管理", habit_manage_widget); QFont titleFont; titleFont.setPointSize(18); titleFont.setBold(true); title->setFont(titleFont); + QPushButton *backButton = new QPushButton(); + backButton->setIcon(QIcon(":/assets/images/back.png")); + backButton->setIconSize(QSize(30, 30)); + backButton->setFixedSize(45, 45); + connect(backButton, &QPushButton::clicked, this, &ViewLayer::onBackToNavigation); topLayout->addWidget(title); topLayout->addStretch(); + topLayout->addWidget(backButton); gridLayout->addLayout(topLayout, 0, 0, 1, 4); // 占据第0行,4列 // 活跃习惯展示区(滚动区域) @@ -1467,9 +1341,9 @@ void ViewLayer::initHabitManageView() std::vector inactiveHabits = sv_Layer.getInactiveHabits(); int inactiveRow = 0, inactiveCol = 0; - for (size_t j = 0; j < inactiveHabits.size(); ++j) + for (size_t i = 0; i < inactiveHabits.size(); ++i) { - const Habit &habit = inactiveHabits[j]; + const Habit &habit = inactiveHabits[i]; QWidget *habitCard = new QWidget(); habitCard->setFixedSize(180, 150); habitCard->setStyleSheet @@ -1533,8 +1407,8 @@ void ViewLayer::initHabitManageView() cardLayout->addWidget(endLabel); cardLayout->addLayout(buttonLayout); - inactiveRow = j / habitsPerRow; - inactiveCol = j % habitsPerRow; + inactiveRow = i / habitsPerRow; + inactiveCol = i % habitsPerRow; inactiveHabitGridLayout->addWidget(habitCard, inactiveRow, inactiveCol); } @@ -1571,6 +1445,24 @@ void ViewLayer::initNavigationView() { const QString current_theme = sv_Layer.getCurrentThemeName(); const QJsonObject theme_config = sv_Layer.getThemeConfig(current_theme); navigation_bar->loadTheme(theme_config); + + auto timer = new QTimer(this); + connect(timer, &QTimer::timeout, this, [=,this]() { + static bool isDefaultTheme = true; + if (isDefaultTheme) { + const QJsonObject theme_config = sv_Layer.getThemeConfig("blue"); + navigation_bar->loadTheme(theme_config); + isDefaultTheme = false; + } else { + const QJsonObject theme_config = sv_Layer.getThemeConfig("default"); + navigation_bar->loadTheme(theme_config); + isDefaultTheme = true; + } + + }); + timer->start(5000); + + } void ViewLayer::onBackToNavigation() @@ -1672,99 +1564,3 @@ bool ViewLayer::eventFilter(QObject* watched, QEvent* event) } return QWidget::eventFilter(watched, event); } - -void ViewLayer::updateMainPomodoroDisplay() const -{ - if (!pomodoro_widget_component) { - showNoPomodoro(); - return; - } - - // 检查番茄钟状态 - if (pomodoro_widget_component->getState() == PomodoroWidget::IDLE) { - showNoPomodoro(); - return; - } - - // 获取番茄钟信息 - QString remark = pomodoro_widget_component->getRemark(); - QString timeText = pomodoro_widget_component->getTimeDisplayText(); - - // 更新显示 - 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(); - } -} - -void ViewLayer::startMainPomodoroTimer() const -{ - if (main_pomodoro_timer) { - main_pomodoro_timer->start(); - } -} - -void ViewLayer::stopMainPomodoroTimer() const -{ - if (main_pomodoro_timer) { - main_pomodoro_timer->stop(); - } -} - -void ViewLayer::checkAndRestorePomodoroState() -{ - qDebug() << "开始检查并恢复番茄钟状态..."; - - // 检查是否有正在运行的番茄钟 - if (pomodoro_widget_component && pomodoro_widget_component->getState() != PomodoroWidget::IDLE) { - 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 && pomodoro_widget_component) { // 0 = IDLE - qDebug() << "恢复番茄钟状态..."; - pomodoro_widget_component->restoreState(state, total_seconds, remaining_seconds, - QString::fromStdString(remark), - QString::fromStdString(start_time)); - startMainPomodoroTimer(); - updateMainPomodoroDisplay(); - qDebug() << "番茄钟状态恢复完成"; - } else { - qDebug() << "状态为IDLE或番茄钟组件不存在,显示无番茄钟"; - showNoPomodoro(); - } - } else { - qDebug() << "没有找到保存的番茄钟状态,显示无番茄钟"; - showNoPomodoro(); - } -} - -void ViewLayer::showNoPomodoro() const -{ - 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 f05f48d..66d60e9 100644 --- a/src/ViewLayer.h +++ b/src/ViewLayer.h @@ -166,12 +166,6 @@ private slots: // 当前对话框的 QLabel 指针 QLabel* dialogLabel = nullptr; - // 主界面番茄钟显示相关 - QLabel* main_pomodoro_remark_label = nullptr; /**< 主界面番茄钟备注标签 */ - QLabel* main_pomodoro_time_label = nullptr; /**< 主界面番茄钟时间标签 */ - QLabel* main_pomodoro_no_pomodoro_label = nullptr; /**< 主界面无番茄钟标签 */ - QTimer* main_pomodoro_timer = nullptr; /**< 主界面番茄钟更新定时器 */ - // ================= 各视图初始化 ================= /** * @brief 初始化导航视图 @@ -286,41 +280,6 @@ private slots: * @author 冰柠 */ static bool parseTime(const std::string& str, Time& result); - -private: - /** - * @brief 创建底部导航栏 - * @param parent 父窗口部件 - * @return 返回包含导航栏的布局 - * @author Rain - */ - QHBoxLayout* createBottomNavigationBar(QWidget* parent = nullptr); - - /** - * @brief 更新主界面番茄钟显示 - * @author Rain - */ - void updateMainPomodoroDisplay() const; - - /** - * @brief 启动主界面番茄钟定时器 - * @author Rain - */ - void startMainPomodoroTimer() const; - - /** - * @brief 停止主界面番茄钟定时器 - * @author Rain - */ - void stopMainPomodoroTimer() const; - - /** - * @brief 检查并恢复番茄钟状态 - * @author Rain - */ - void checkAndRestorePomodoroState(); - - void showNoPomodoro() const; }; #endif //VIEWLAYER_H \ No newline at end of file diff --git a/src/main/main.cpp b/src/main/main.cpp index a04065b..1d78ed2 100644 --- a/src/main/main.cpp +++ b/src/main/main.cpp @@ -1,6 +1,4 @@ #include - -#include "SimpleAuthSystem.h" #include "ViewLayer.h" // 临时测试函数 @@ -19,16 +17,6 @@ int testHabbitModule(int argc, char* argv[], const ViewLayer::ViewType type) return app.exec(); } -int testHabbitModule(int argc, char* argv[]) -{ - QApplication app(argc, argv); - - SimpleAuthSystem authSystem; - authSystem.show(); - - return app.exec(); -} - // 其他部分的临时测试函数在本行上方添加 int main(const int argc, char *argv[])