This commit is contained in:
parent
51bfddd40f
commit
6803f84677
@ -5,6 +5,7 @@
|
||||
|
||||
class VisitorRecord {
|
||||
public:
|
||||
int id = -1;
|
||||
std::string url;
|
||||
std::string userAgent;
|
||||
std::string visitorUuid;
|
||||
@ -19,4 +20,6 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
using VisitorRecords = Wt::Dbo::collection<Wt::Dbo::ptr<VisitorRecord>>;
|
||||
|
||||
#endif // __VISITORRECORD_H__
|
@ -1,54 +1,170 @@
|
||||
#include "VisitorRecordTableModel.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 {
|
||||
return 5;
|
||||
}
|
||||
|
||||
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 {
|
||||
if (index.column() == 0) {
|
||||
return Wt::ItemFlag::UserCheckable | Wt::ItemFlag::Selectable;
|
||||
} else if (index.column() == 1) {
|
||||
return Wt::ItemFlag::XHTMLText | Wt::ItemFlag::Selectable;
|
||||
} else {
|
||||
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 {
|
||||
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) &&
|
||||
index.column() == 0) { // Assuming first column for checkbox
|
||||
|
||||
return true;
|
||||
} else if ((role == Wt::ItemDataRole::Checked || role == Wt::ItemDataRole::Edit) &&
|
||||
index.column() == 1) { // Assuming first column for checkbox
|
||||
|
||||
return false;
|
||||
return std::get<1>(m_records.at(row));
|
||||
} else if ((index.column() == 1) && (role == Wt::ItemDataRole::Display)) {
|
||||
auto pos = record.url.rfind("/");
|
||||
auto name = record.url;
|
||||
if (pos != std::string::npos && pos < (name.size() - 1)) {
|
||||
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 {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,16 +1,33 @@
|
||||
#ifndef __VISITORRECORDTABLEMODEL_H__
|
||||
#define __VISITORRECORDTABLEMODEL_H__
|
||||
|
||||
#include "Database/VisitorRecord.h"
|
||||
#include <Wt/WAbstractTableModel.h>
|
||||
|
||||
class Session;
|
||||
|
||||
class VisitorRecordTableModel : public Wt::WAbstractTableModel {
|
||||
public:
|
||||
VisitorRecordTableModel(Session &session);
|
||||
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 rowCount(const Wt::WModelIndex &parent = Wt::WModelIndex()) 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::cpp17::any headerData(int section, Wt::Orientation orientation = Wt::Orientation::Horizontal,
|
||||
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__
|
@ -3,59 +3,60 @@
|
||||
#include "Database/Session.h"
|
||||
#include "VisitorRecordTableModel.h"
|
||||
#include <Wt/Dbo/QueryModel.h>
|
||||
#include <Wt/WBreak.h>
|
||||
#include <Wt/WItemDelegate.h>
|
||||
#include <Wt/WLabel.h>
|
||||
#include <Wt/WMessageBox.h>
|
||||
#include <Wt/WPushButton.h>
|
||||
#include <Wt/WTableView.h>
|
||||
|
||||
class CheckBoxDelegate : public Wt::WAbstractItemDelegate {
|
||||
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) {
|
||||
VisitorRecordsPage::VisitorRecordsPage(Session &session) : m_model{std::make_shared<VisitorRecordTableModel>(session)} {
|
||||
addNew<Wt::WLabel>("访客数据统计");
|
||||
addNew<Wt::WBreak>();
|
||||
auto button = addNew<Wt::WPushButton>("删除");
|
||||
button->clicked().connect(this, &VisitorRecordsPage::onDeleteButtonClicked);
|
||||
|
||||
auto view = addNew<Wt::WTableView>();
|
||||
view->setColumnWidth(0, Wt::WLength::Auto);
|
||||
view->setColumnWidth(1, Wt::WLength::Auto);
|
||||
view->setHeaderHeight(30); // 设置表头的高度
|
||||
view->setWidth(Wt::WLength("99%"));
|
||||
view->setColumnResizeEnabled(true);
|
||||
view->setAlternatingRowColors(true);
|
||||
view->setAlternatingRowColors(true); // 限制于 stripe-40px.gif 类似的文件,wt 有提供脚本生成
|
||||
view->setRowHeight(50);
|
||||
view->setSelectionMode(Wt::SelectionMode::None);
|
||||
view->setColumnAlignment(0, Wt::AlignmentFlag::Center);
|
||||
view->setHeaderAlignment(0,Wt::AlignmentFlag::Center);
|
||||
view->setSortingEnabled(0,false);
|
||||
view->setColumnAlignment(2, Wt::AlignmentFlag::Center);
|
||||
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->setItemDelegateForColumn(0, std::make_shared<CheckBoxDelegate>());
|
||||
// view->setHeaderItemDelegate( std::make_shared<CheckBoxDelegate>());
|
||||
|
||||
// auto model = std::make_shared<Wt::Dbo::QueryModel<Wt::Dbo::ptr<VisitorRecord>>>();
|
||||
// model->setQuery(session.find<VisitorRecord>());
|
||||
// model->addColumn("id", "ID",Wt::ItemFlag::UserCheckable);
|
||||
// model->addColumn("url", "URL");
|
||||
// model->addColumn("time", "Time");
|
||||
// model->addColumn("user_agent", "User Agent");
|
||||
auto model = std::make_shared<VisitorRecordTableModel>();
|
||||
view->setModel(model);
|
||||
|
||||
/*
|
||||
* Configure column widths and matching table width
|
||||
*/
|
||||
const int WIDTH = 240;
|
||||
for (int i = 0; i < view->model()->columnCount(); ++i) view->setColumnWidth(i, 120);
|
||||
view->setModel(m_model);
|
||||
view->setColumnWidth(0, 60);
|
||||
view->setColumnWidth(1, 180);
|
||||
view->setColumnWidth(2, 180);
|
||||
view->setColumnWidth(3, 480);
|
||||
}
|
||||
|
||||
void VisitorRecordsPage::onDeleteButtonClicked() {
|
||||
auto selected = m_model->selected();
|
||||
if (selected.empty()) {
|
||||
Wt::WMessageBox::show("删除记录", "请先选择要删除的记录", Wt::StandardButton::Ok);
|
||||
} else {
|
||||
auto anwser =
|
||||
Wt::WMessageBox::show("删除记录", "确定删除选择的记录?", Wt::StandardButton::Ok | Wt::StandardButton::Cancel);
|
||||
if (anwser == Wt::StandardButton::Ok) {
|
||||
}
|
||||
}
|
||||
}
|
@ -4,12 +4,19 @@
|
||||
#include <Wt/WContainerWidget.h>
|
||||
|
||||
class Session;
|
||||
class VisitorRecordTableModel;
|
||||
|
||||
// https://infima.dev/
|
||||
|
||||
class VisitorRecordsPage : public Wt::WContainerWidget {
|
||||
public:
|
||||
VisitorRecordsPage(Session &session);
|
||||
|
||||
protected:
|
||||
void onDeleteButtonClicked();
|
||||
|
||||
private:
|
||||
std::shared_ptr<VisitorRecordTableModel> m_model;
|
||||
};
|
||||
|
||||
#endif // __VISITORRECORDSPAGE_H__
|
Loading…
Reference in New Issue
Block a user