fix model.
Some checks failed
Deploy / Build (push) Failing after 2m55s

This commit is contained in:
luocai 2024-11-29 20:01:10 +08:00
parent 51bfddd40f
commit 6803f84677
5 changed files with 210 additions and 66 deletions

View File

@ -5,6 +5,7 @@
class VisitorRecord { class VisitorRecord {
public: public:
int id = -1;
std::string url; std::string url;
std::string userAgent; std::string userAgent;
std::string visitorUuid; std::string visitorUuid;
@ -19,4 +20,6 @@ public:
} }
}; };
using VisitorRecords = Wt::Dbo::collection<Wt::Dbo::ptr<VisitorRecord>>;
#endif // __VISITORRECORD_H__ #endif // __VISITORRECORD_H__

View File

@ -1,54 +1,170 @@
#include "VisitorRecordTableModel.h" #include "VisitorRecordTableModel.h"
#include "BoostLog.h" #include "BoostLog.h"
#include "Database/Session.h"
#include "DateTime.h"
VisitorRecordTableModel::VisitorRecordTableModel(Session &session) : m_session(session) {
Wt::Dbo::Transaction transaction(session);
Wt::Dbo::collection<Wt::Dbo::ptr<VisitorRecord>> records = session.find<VisitorRecord>();
int index = 1;
std::unordered_map<std::string, std::string> users;
for (auto &record : records) {
if (record->url.size() <= 1) continue;
if (!users.contains(record->visitorUuid)) {
users.insert({record->visitorUuid, std::format("匿名用户{}", index++)});
}
m_records.push_back({*record, false});
std::get<0>(m_records.back()).visitorUuid = users.at(record->visitorUuid);
std::get<0>(m_records.back()).id = record.id();
}
}
int VisitorRecordTableModel::columnCount(const Wt::WModelIndex &parent) const { int VisitorRecordTableModel::columnCount(const Wt::WModelIndex &parent) const {
return 5; return 5;
} }
int VisitorRecordTableModel::rowCount(const Wt::WModelIndex &parent) const { int VisitorRecordTableModel::rowCount(const Wt::WModelIndex &parent) const {
return 50; return m_records.size();
}
Wt::WFlags<Wt::HeaderFlag> VisitorRecordTableModel::headerFlags(int section, Wt::Orientation orientation) const {
if (section == 0 && orientation == Wt::Orientation::Horizontal) {
return Wt::HeaderFlag::UserCheckable;
} else {
return Wt::None;
}
}
Wt::cpp17::any VisitorRecordTableModel::headerData(int section, Wt::Orientation orientation, Wt::ItemDataRole role) const {
if (orientation == Wt::Orientation::Horizontal) {
if (role == Wt::ItemDataRole::Level) return Wt::WAbstractTableModel::headerData(section, orientation, role);
if ((role == Wt::ItemDataRole::Checked || role == Wt::ItemDataRole::Edit) &&
section == 0) { // Assuming first column for checkbox
return m_selectAll;
} else if ((section == 1) && (role == Wt::ItemDataRole::Display)) {
return "文章";
} else if ((section == 2) && (role == Wt::ItemDataRole::Display)) {
return "时间";
} else if ((section == 3) && (role == Wt::ItemDataRole::Display)) {
return "浏览器";
} else if ((section == 4) && (role == Wt::ItemDataRole::Display)) {
return "用户";
} else {
return Wt::cpp17::any{};
}
} else {
return Wt::WAbstractTableModel::headerData(section, orientation, role);
}
}
bool VisitorRecordTableModel::setHeaderData(int section, Wt::Orientation orientation, const Wt::cpp17::any &value,
Wt::ItemDataRole role) {
if ((section == 0) && (role == Wt::ItemDataRole::Checked)) {
m_selectAll = Wt::cpp17::any_cast<bool>(value);
for (int i = 0; i < m_records.size(); i++) {
std::get<1>(m_records[i]) = m_selectAll;
}
dataChanged().emit(index(0, 0), index(m_records.size(), 0));
return true;
} else {
return Wt::WAbstractTableModel::setHeaderData(section, orientation, value, role);
}
}
void VisitorRecordTableModel::sort(int column, Wt::SortOrder order) {
if (column == 2) {
layoutAboutToBeChanged().emit();
std::sort(m_records.begin(), m_records.end(), [order](const VisitorRecord &lfh, const VisitorRecord &rfh) {
if (order == Wt::SortOrder::Ascending) {
return lfh.time < rfh.time;
} else {
return lfh.time > rfh.time;
}
});
layoutChanged().emit();
}
} }
Wt::WFlags<Wt::ItemFlag> VisitorRecordTableModel::flags(const Wt::WModelIndex &index) const { Wt::WFlags<Wt::ItemFlag> VisitorRecordTableModel::flags(const Wt::WModelIndex &index) const {
if (index.column() == 0) { if (index.column() == 0) {
return Wt::ItemFlag::UserCheckable | Wt::ItemFlag::Selectable; return Wt::ItemFlag::UserCheckable | Wt::ItemFlag::Selectable;
} else if (index.column() == 1) {
return Wt::ItemFlag::XHTMLText | Wt::ItemFlag::Selectable;
} else { } else {
return Wt::ItemFlag::Selectable; return Wt::ItemFlag::Selectable;
} }
} }
Wt::WFlags<Wt::HeaderFlag> VisitorRecordTableModel::headerFlags(int section, Wt::Orientation orientation) const {
if (section == 0 && orientation== Wt::Orientation::Horizontal) {
return Wt::HeaderFlag::UserCheckable|Wt::HeaderFlag::XHTMLText;
} else {
return Wt::HeaderFlag::XHTMLText;
}
}
Wt::cpp17::any VisitorRecordTableModel::headerData(int section, Wt::Orientation orientation, Wt::ItemDataRole role) const {
if ((role == Wt::ItemDataRole::Checked || role == Wt::ItemDataRole::Edit) &&
section == 0) { // Assuming first column for checkbox
return true;
} else if ((role == Wt::ItemDataRole::Display || role == Wt::ItemDataRole::Edit) &&
section == 0) { // Assuming first column for checkbox
return Wt::cpp17::any{};
} else {
return Wt::cpp17::any{};
}
}
Wt::cpp17::any VisitorRecordTableModel::data(const Wt::WModelIndex &index, Wt::ItemDataRole role) const { Wt::cpp17::any VisitorRecordTableModel::data(const Wt::WModelIndex &index, Wt::ItemDataRole role) const {
LOG(info) << "VisitorRecordTableModel: " << role.value(); // 选择框
int row = index.row();
auto &record = std::get<0>(m_records.at(row));
if ((role == Wt::ItemDataRole::Checked || role == Wt::ItemDataRole::Edit) && if ((role == Wt::ItemDataRole::Checked || role == Wt::ItemDataRole::Edit) &&
index.column() == 0) { // Assuming first column for checkbox index.column() == 0) { // Assuming first column for checkbox
return std::get<1>(m_records.at(row));
return true; } else if ((index.column() == 1) && (role == Wt::ItemDataRole::Display)) {
} else if ((role == Wt::ItemDataRole::Checked || role == Wt::ItemDataRole::Edit) && auto pos = record.url.rfind("/");
index.column() == 1) { // Assuming first column for checkbox auto name = record.url;
if (pos != std::string::npos && pos < (name.size() - 1)) {
return false; name = name.substr(pos + 1);
}
return std::format("<a href=\"{}\">{}</a>", record.url, name);
} else if ((index.column() == 2) && (role == Wt::ItemDataRole::Display)) {
return DateTime::toString(record.time);
} else if ((index.column() == 3) && (role == Wt::ItemDataRole::Display)) {
return record.userAgent;
} else if ((index.column() == 4) && (role == Wt::ItemDataRole::Display)) {
return record.visitorUuid;
} else { } else {
return Wt::cpp17::any{}; return Wt::cpp17::any{};
} }
} }
bool VisitorRecordTableModel::setData(const Wt::WModelIndex &index, const Wt::cpp17::any &value, Wt::ItemDataRole role) {
int row = index.row();
if ((index.column() == 0) && (role == Wt::ItemDataRole::Checked)) {
std::get<1>(m_records[row]) = Wt::cpp17::any_cast<bool>(value);
auto iterator = std::find_if_not(m_records.begin(), m_records.end(), [](const Item &item) { return std::get<1>(item); });
if (iterator == m_records.end()) {
if (!m_selectAll) {
m_selectAll = true;
headerDataChanged().emit(Wt::Orientation::Horizontal, 0, 0);
}
} else {
if (m_selectAll) {
m_selectAll = false;
headerDataChanged().emit(Wt::Orientation::Horizontal, 0, 0);
}
}
return true;
} else {
return Wt::WAbstractTableModel::setData(index, value, role);
}
}
std::vector<VisitorRecord> VisitorRecordTableModel::selected() const {
std::vector<VisitorRecord> ret;
for (int i = 0; i < m_records.size(); i++) {
if (std::get<1>(m_records.at(i))) {
ret.push_back(std::get<0>(m_records.at(i)));
}
}
return ret;
}
void VisitorRecordTableModel::removeSelected() {
Wt::Dbo::Transaction transaction(m_session);
for (auto iterator = m_records.cbegin(); iterator != m_records.cend();) {
auto selected = std::get<1>(*iterator);
auto &r = std::get<0>(*iterator);
if (selected) {
Wt::Dbo::ptr<VisitorRecord> item = m_session.find<VisitorRecord>().where("id = ?").bind(r.id);
item.remove();
iterator = m_records.erase(iterator);
} else {
++iterator;
}
}
}

View File

@ -1,16 +1,33 @@
#ifndef __VISITORRECORDTABLEMODEL_H__ #ifndef __VISITORRECORDTABLEMODEL_H__
#define __VISITORRECORDTABLEMODEL_H__ #define __VISITORRECORDTABLEMODEL_H__
#include "Database/VisitorRecord.h"
#include <Wt/WAbstractTableModel.h> #include <Wt/WAbstractTableModel.h>
class Session;
class VisitorRecordTableModel : public Wt::WAbstractTableModel { class VisitorRecordTableModel : public Wt::WAbstractTableModel {
public: public:
VisitorRecordTableModel(Session &session);
Wt::cpp17::any data(const Wt::WModelIndex &index, Wt::ItemDataRole role = Wt::ItemDataRole::Display) const final; Wt::cpp17::any data(const Wt::WModelIndex &index, Wt::ItemDataRole role = Wt::ItemDataRole::Display) const final;
bool setData(const Wt::WModelIndex &index, const Wt::cpp17::any &value, Wt::ItemDataRole role = Wt::ItemDataRole::Edit) final;
int columnCount(const Wt::WModelIndex &parent = Wt::WModelIndex()) const final; int columnCount(const Wt::WModelIndex &parent = Wt::WModelIndex()) const final;
int rowCount(const Wt::WModelIndex &parent = Wt::WModelIndex()) const final; int rowCount(const Wt::WModelIndex &parent = Wt::WModelIndex()) const final;
Wt::WFlags<Wt::ItemFlag> flags(const Wt::WModelIndex &index) const final; Wt::WFlags<Wt::ItemFlag> flags(const Wt::WModelIndex &index) const final;
Wt::WFlags<Wt::HeaderFlag> headerFlags(int section, Wt::Orientation orientation = Wt::Orientation::Horizontal) const final; Wt::WFlags<Wt::HeaderFlag> headerFlags(int section, Wt::Orientation orientation = Wt::Orientation::Horizontal) const final;
Wt::cpp17::any headerData(int section, Wt::Orientation orientation = Wt::Orientation::Horizontal, Wt::cpp17::any headerData(int section, Wt::Orientation orientation = Wt::Orientation::Horizontal,
Wt::ItemDataRole role = Wt::ItemDataRole::Display) const final; Wt::ItemDataRole role = Wt::ItemDataRole::Display) const final;
bool setHeaderData(int section, Wt::Orientation orientation, const Wt::cpp17::any &value,
Wt::ItemDataRole role = Wt::ItemDataRole::Edit) final;
void sort(int column, Wt::SortOrder order = Wt::SortOrder::Ascending) final;
std::vector<VisitorRecord> selected() const;
void removeSelected();
private:
Session &m_session;
using Item = std::tuple<VisitorRecord, bool>;
std::vector<Item> m_records;
bool m_selectAll = false;
}; };
#endif // __VISITORRECORDTABLEMODEL_H__ #endif // __VISITORRECORDTABLEMODEL_H__

View File

@ -3,59 +3,60 @@
#include "Database/Session.h" #include "Database/Session.h"
#include "VisitorRecordTableModel.h" #include "VisitorRecordTableModel.h"
#include <Wt/Dbo/QueryModel.h> #include <Wt/Dbo/QueryModel.h>
#include <Wt/WBreak.h>
#include <Wt/WItemDelegate.h> #include <Wt/WItemDelegate.h>
#include <Wt/WLabel.h> #include <Wt/WLabel.h>
#include <Wt/WMessageBox.h>
#include <Wt/WPushButton.h>
#include <Wt/WTableView.h> #include <Wt/WTableView.h>
class CheckBoxDelegate : public Wt::WAbstractItemDelegate { VisitorRecordsPage::VisitorRecordsPage(Session &session) : m_model{std::make_shared<VisitorRecordTableModel>(session)} {
public:
CheckBoxDelegate() : Wt::WAbstractItemDelegate() {
}
std::unique_ptr<Wt::WWidget> update(Wt::WWidget *widget, const Wt::WModelIndex &index,
Wt::WFlags<Wt::ViewItemRenderFlag> flags) final {
auto checkBox = dynamic_cast<Wt::WCheckBox *>(widget);
if (!checkBox) {
checkBox = new Wt::WCheckBox();
}
LOG(info) << Wt::asString(index.data());
// checkBox->setChecked(index.data().toBool());
checkBox->setEnabled(true);
return std::unique_ptr<Wt::WWidget>(checkBox);
}
};
VisitorRecordsPage::VisitorRecordsPage(Session &session) {
addNew<Wt::WLabel>("访客数据统计"); addNew<Wt::WLabel>("访客数据统计");
addNew<Wt::WBreak>();
auto button = addNew<Wt::WPushButton>("删除");
button->clicked().connect(this, &VisitorRecordsPage::onDeleteButtonClicked);
auto view = addNew<Wt::WTableView>(); auto view = addNew<Wt::WTableView>();
view->setColumnWidth(0, Wt::WLength::Auto); view->setColumnWidth(0, Wt::WLength::Auto);
view->setColumnWidth(1, Wt::WLength::Auto); view->setColumnWidth(1, Wt::WLength::Auto);
view->setHeaderHeight(30); // 设置表头的高度 view->setHeaderHeight(30); // 设置表头的高度
view->setWidth(Wt::WLength("99%"));
view->setColumnResizeEnabled(true); view->setColumnResizeEnabled(true);
view->setAlternatingRowColors(true); view->setAlternatingRowColors(true); // 限制于 stripe-40px.gif 类似的文件wt 有提供脚本生成
view->setRowHeight(50); view->setRowHeight(50);
view->setSelectionMode(Wt::SelectionMode::None); view->setSelectionMode(Wt::SelectionMode::None);
view->setColumnAlignment(0, Wt::AlignmentFlag::Center); view->setColumnAlignment(0, Wt::AlignmentFlag::Center);
view->setHeaderAlignment(0,Wt::AlignmentFlag::Center); view->setColumnAlignment(2, Wt::AlignmentFlag::Center);
view->setSortingEnabled(0,false); view->setColumnAlignment(4, Wt::AlignmentFlag::Center);
view->setHeaderAlignment(0, Wt::AlignmentFlag::Center);
view->setHeaderAlignment(1, Wt::AlignmentFlag::Center);
view->setHeaderAlignment(2, Wt::AlignmentFlag::Center);
view->setHeaderAlignment(3, Wt::AlignmentFlag::Center);
view->setHeaderAlignment(4, Wt::AlignmentFlag::Center);
view->setSortingEnabled(0, false);
view->setSortingEnabled(1, false);
view->setSortingEnabled(3, false);
view->setSortingEnabled(4, false);
// view->setEditTriggers(Wt::EditTrigger::None); // view->setEditTriggers(Wt::EditTrigger::None);
// view->setItemDelegateForColumn(0, std::make_shared<CheckBoxDelegate>()); // view->setItemDelegateForColumn(0, std::make_shared<CheckBoxDelegate>());
// view->setHeaderItemDelegate( std::make_shared<CheckBoxDelegate>()); // view->setHeaderItemDelegate( std::make_shared<CheckBoxDelegate>());
// auto model = std::make_shared<Wt::Dbo::QueryModel<Wt::Dbo::ptr<VisitorRecord>>>(); view->setModel(m_model);
// model->setQuery(session.find<VisitorRecord>()); view->setColumnWidth(0, 60);
// model->addColumn("id", "ID",Wt::ItemFlag::UserCheckable); view->setColumnWidth(1, 180);
// model->addColumn("url", "URL"); view->setColumnWidth(2, 180);
// model->addColumn("time", "Time"); view->setColumnWidth(3, 480);
// model->addColumn("user_agent", "User Agent"); }
auto model = std::make_shared<VisitorRecordTableModel>();
view->setModel(model); void VisitorRecordsPage::onDeleteButtonClicked() {
auto selected = m_model->selected();
/* if (selected.empty()) {
* Configure column widths and matching table width Wt::WMessageBox::show("删除记录", "请先选择要删除的记录", Wt::StandardButton::Ok);
*/ } else {
const int WIDTH = 240; auto anwser =
for (int i = 0; i < view->model()->columnCount(); ++i) view->setColumnWidth(i, 120); Wt::WMessageBox::show("删除记录", "确定删除选择的记录?", Wt::StandardButton::Ok | Wt::StandardButton::Cancel);
if (anwser == Wt::StandardButton::Ok) {
}
}
} }

View File

@ -4,12 +4,19 @@
#include <Wt/WContainerWidget.h> #include <Wt/WContainerWidget.h>
class Session; class Session;
class VisitorRecordTableModel;
// https://infima.dev/ // https://infima.dev/
class VisitorRecordsPage : public Wt::WContainerWidget { class VisitorRecordsPage : public Wt::WContainerWidget {
public: public:
VisitorRecordsPage(Session &session); VisitorRecordsPage(Session &session);
protected:
void onDeleteButtonClicked();
private:
std::shared_ptr<VisitorRecordTableModel> m_model;
}; };
#endif // __VISITORRECORDSPAGE_H__ #endif // __VISITORRECORDSPAGE_H__