#include "TaskPage.h" #include "Application.h" #include "BoostLog.h" #include "Database/Session.h" #include #include #include #include #include #include #include #include #include #include #include TaskPage::TaskPage() : Wt::WTemplate(tr("Wt.Task.Home")) { using namespace Wt; addStyleClass("bulma-is-flex-grow-1 bulma-is-flex bulma-is-flex-direction-column"); auto addButton = bindNew("add-new", "创建"); addButton->clicked().connect(this, [this]() { showAddTaskDialog(-1); }); update(); } std::unique_ptr TaskPage::createTask(const Wt::Dbo::ptr &task) { auto ret = std::make_unique(tr("Wt.Task.Item")); auto app = dynamic_cast(Wt::WApplication::instance()); ret->bindString("content", task->content); ret->bindString("remark", task->remark); ret->setHtmlTagName("li"); auto checkbox = ret->bindNew("finished-checkbox"); checkbox->setChecked(task->finished); checkbox->clicked().connect(ret.get(), [this, id = task.id(), checkbox]() { { auto session = Database::session(); Wt::Dbo::Transaction transaction(*session); Wt::Dbo::ptr task = session->find().where("id = ?").bind(id); if (task) { task.modify()->finished = checkbox->isChecked(); } } update(); }); auto addButton = ret->bindNew("add-child", tr("Wt.Icon.Add"), Wt::TextFormat::XHTML); addButton->clicked().connect(ret.get(), [id = task.id(), this]() { showAddTaskDialog(id); }); auto removeButton = ret->bindNew("remove-button", tr("Wt.Icon.Remove"), Wt::TextFormat::XHTML); removeButton->clicked().connect(ret.get(), [this, id = task.id()]() { Wt::StandardButton result = Wt::WMessageBox::show("删除任务", "是否删除任务?子任务也将一起删除", Wt::StandardButton::Ok | Wt::StandardButton::Cancel); if (result == Wt::StandardButton::Ok) { { auto session = Database::session(); Wt::Dbo::Transaction transaction(*session); Wt::Dbo::ptr task = session->find().where("id = ?").bind(id); if (task) { removeTask(task); } } update(); } }); auto collapseButton = ret->bindNew("collapse-button", tr("Wt.Icon.Expand"), Wt::TextFormat::XHTML); collapseButton->setCheckable(true); if (task->children.empty()) { ret->bindEmpty("children"); collapseButton->addStyleClass("bulma-is-invisible"); } else { auto list = ret->bindNew("children"); list->setList(true); for (auto child : task->children) { list->addWidget(createTask(child)); } collapseButton->clicked().connect(ret.get(), [collapseButton, list]() { collapseButton->setText(collapseButton->isChecked() ? tr("Wt.Icon.Fold") : tr("Wt.Icon.Expand")); if (collapseButton->isChecked()) { list->addStyleClass("bulma-is-hidden"); } else { list->removeStyleClass("bulma-is-hidden"); } }); } return ret; } class AddTaskModel : public Wt::WFormModel { public: static constexpr Field ContentField = "task-content"; static constexpr Field DeadlineDateField = "task-deadlinedate"; static constexpr Field DeadlineTimeField = "task-deadlinetime"; static constexpr Field RemarkField = "task-remark"; AddTaskModel() : Wt::WFormModel() { initializeModels(); addField(ContentField); addField(DeadlineDateField); addField(DeadlineTimeField); addField(RemarkField); setValidator(DeadlineDateField, createDeadlineValidator()); setValue(DeadlineDateField, Wt::WDate{}); setValue(DeadlineTimeField, Wt::WTime{}); } std::shared_ptr timeModel() { return m_timeModel; } Wt::WTime timefRow(int row) { return Wt::cpp17::any_cast(m_timeModel->data(row, 0, Wt::ItemDataRole::User)); } int rowOfTime(const Wt::WTime &time) { int ret = -1; for (int i = 0; i < m_timeModel->rowCount(); ++i) { if (timefRow(i) == time) { ret = i; break; } } return -1; } Task task() { Task t; t.content = Wt::asString(value(ContentField)).toUTF8(); t.remark = Wt::asString(value(RemarkField)).toUTF8(); auto date = Wt::cpp17::any_cast(value(AddTaskModel::DeadlineDateField)); auto time = Wt::cpp17::any_cast(value(AddTaskModel::DeadlineTimeField)); t.deadline = Wt::WDateTime(date, time).toTimePoint(); return t; } protected: std::shared_ptr createDeadlineValidator() { auto v = std::make_shared(); auto current = Wt::WDate::currentDate(); v->setBottom(current); v->setTop(current.addYears(1)); v->setFormat("yyyy/MM/dd"); v->setMandatory(true); return v; } void initializeModels() { m_timeModel = std::make_shared(48, 1); for (int i = 0; i < 48; i++) { Wt::WTime time(i / 2, (i % 2) * 30); m_timeModel->setData(i, 0, time.toString("HH:mm"), Wt::ItemDataRole::Display); m_timeModel->setData(i, 0, time, Wt::ItemDataRole::User); } } private: std::shared_ptr m_timeModel; }; class AddTaskView : public Wt::WTemplateFormView { public: AddTaskView(const Wt::WString &text) : Wt::WTemplateFormView(text), m_model{std::make_shared()} { setFormWidget(AddTaskModel::ContentField, std::make_unique()); auto remark = std::make_unique(); remark->setColumns(40); remark->setRows(5); setFormWidget(AddTaskModel::RemarkField, std::move(remark)); auto dateEdit = std::make_unique(); auto dateEdit_ = dateEdit.get(); setFormWidget( AddTaskModel::DeadlineDateField, std::move(dateEdit), [this, dateEdit_] { // updateViewValue() Wt::WDate date = Wt::cpp17::any_cast(m_model->value(AddTaskModel::DeadlineDateField)); dateEdit_->setDate(date); }, [this, dateEdit_] { // updateModelValue() Wt::WDate date = dateEdit_->date(); m_model->setValue(AddTaskModel::DeadlineDateField, date); }); auto timeEdit = std::make_unique(); auto timeEdit_ = timeEdit.get(); timeEdit_->setModel(m_model->timeModel()); setFormWidget( AddTaskModel::DeadlineTimeField, std::move(timeEdit), [this, timeEdit_] { // updateViewValue() Wt::WTime time = Wt::cpp17::any_cast(m_model->value(AddTaskModel::DeadlineTimeField)); int row = m_model->rowOfTime(time); timeEdit_->setCurrentIndex(row); }, [this, timeEdit_] { // updateModelValue() Wt::WTime time = m_model->timefRow(timeEdit_->currentIndex()); m_model->setValue(AddTaskModel::DeadlineTimeField, time); }); auto button = bindWidget("submit-button", std::make_unique("确定")); button->clicked().connect(this, &AddTaskView::process); updateView(m_model.get()); } Wt::Signal &accepted() { return m_accepted; } std::unique_ptr task() const { return std::make_unique(m_model->task()); } protected: void process() { updateModel(m_model.get()); if (m_model->validate()) { m_accepted.emit(m_model->task()); // updateView(m_model.get()); } else { LOG(error) << "validate failed."; updateView(m_model.get()); } } private: Wt::Signal m_accepted; std::shared_ptr m_model; }; void TaskPage::showAddTaskDialog(int64_t parentId) { auto d = std::make_unique("添加任务"); d->setMovable(false); auto content = d->contents()->addNew(tr("Wt.Task.Add")); auto cancelButton = content->bindNew("cancel-button", "取消"); cancelButton->clicked().connect(content, [dialog = d.get()]() { dialog->reject(); }); content->accepted().connect(this, [dialog = d.get()](const Task &task) { dialog->accept(); }); auto code = d->exec(); if (code == Wt::DialogCode::Accepted) { auto task = content->task(); auto session = Database::session(); if (session) { Wt::Dbo::Transaction transaction(*session); Wt::Dbo::ptr parent; if (parentId >= 0) { parent = session->find(std::format("where id = {}", parentId)); } if (parent) { task->parent = parent; } session->add(std::move(task)); } update(); } } void TaskPage::update() { auto session = Database::session(); Wt::Dbo::Transaction transaction(*session); Tasks tasks = session->find("WHERE parent_id IS NULL"); if (tasks.empty()) { bindEmpty("tasks"); return; } else { auto list = bindNew("tasks"); list->setList(true); auto app = dynamic_cast(Wt::WApplication::instance()); if (app->inDocusaurus()) { list->addStyleClass("bulma-is-list-style-none"); } for (auto &task : tasks) { list->addWidget(createTask(task)); } } } void TaskPage::removeTask(Wt::Dbo::ptr task) { if (!task->children.empty()) { for (auto child : task->children) { removeTask(child); } } task.remove(); }