2024-11-28 19:34:24 +08:00
|
|
|
#include "VisitorRecordTableModel.h"
|
|
|
|
#include "BoostLog.h"
|
2024-11-29 20:01:10 +08:00
|
|
|
#include "Database/Session.h"
|
|
|
|
#include "DateTime.h"
|
|
|
|
|
|
|
|
VisitorRecordTableModel::VisitorRecordTableModel(Session &session) : m_session(session) {
|
2025-01-17 02:55:03 +08:00
|
|
|
filter("");
|
2024-11-29 20:01:10 +08:00
|
|
|
}
|
2024-11-28 19:34:24 +08:00
|
|
|
|
|
|
|
int VisitorRecordTableModel::columnCount(const Wt::WModelIndex &parent) const {
|
|
|
|
return 5;
|
|
|
|
}
|
|
|
|
|
|
|
|
int VisitorRecordTableModel::rowCount(const Wt::WModelIndex &parent) const {
|
2024-11-29 20:01:10 +08:00
|
|
|
return m_records.size();
|
2024-11-28 19:34:24 +08:00
|
|
|
}
|
|
|
|
|
2024-11-29 20:01:10 +08:00
|
|
|
Wt::WFlags<Wt::HeaderFlag> VisitorRecordTableModel::headerFlags(int section, Wt::Orientation orientation) const {
|
|
|
|
if (section == 0 && orientation == Wt::Orientation::Horizontal) {
|
|
|
|
return Wt::HeaderFlag::UserCheckable;
|
2024-11-28 23:39:15 +08:00
|
|
|
} else {
|
2024-11-29 20:01:10 +08:00
|
|
|
return Wt::None;
|
2024-11-28 23:39:15 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-11-29 20:01:10 +08:00
|
|
|
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{};
|
|
|
|
}
|
2024-11-28 23:39:15 +08:00
|
|
|
} else {
|
2024-11-29 20:01:10 +08:00
|
|
|
return Wt::WAbstractTableModel::headerData(section, orientation, role);
|
2024-11-28 23:39:15 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-11-29 20:01:10 +08:00
|
|
|
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));
|
2024-11-28 23:39:15 +08:00
|
|
|
return true;
|
|
|
|
} else {
|
2024-11-29 20:01:10 +08:00
|
|
|
return Wt::WAbstractTableModel::setHeaderData(section, orientation, value, role);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void VisitorRecordTableModel::sort(int column, Wt::SortOrder order) {
|
|
|
|
if (column == 2) {
|
|
|
|
layoutAboutToBeChanged().emit();
|
2024-12-01 15:10:25 +08:00
|
|
|
std::sort(m_records.begin(), m_records.end(), [order](const Item &lfh, const Item &rfh) {
|
2024-11-29 20:01:10 +08:00
|
|
|
if (order == Wt::SortOrder::Ascending) {
|
2024-12-01 15:10:25 +08:00
|
|
|
return std::get<0>(lfh).time < std::get<0>(rfh).time;
|
2024-11-29 20:01:10 +08:00
|
|
|
} else {
|
2024-12-01 15:10:25 +08:00
|
|
|
return std::get<0>(lfh).time > std::get<0>(rfh).time;
|
2024-11-29 20:01:10 +08:00
|
|
|
}
|
|
|
|
});
|
|
|
|
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;
|
2024-11-28 23:39:15 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-11-28 19:34:24 +08:00
|
|
|
Wt::cpp17::any VisitorRecordTableModel::data(const Wt::WModelIndex &index, Wt::ItemDataRole role) const {
|
2024-11-29 20:01:10 +08:00
|
|
|
// 选择框
|
|
|
|
int row = index.row();
|
|
|
|
auto &record = std::get<0>(m_records.at(row));
|
2024-11-28 23:39:15 +08:00
|
|
|
if ((role == Wt::ItemDataRole::Checked || role == Wt::ItemDataRole::Edit) &&
|
|
|
|
index.column() == 0) { // Assuming first column for checkbox
|
2024-11-29 20:01:10 +08:00
|
|
|
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{};
|
|
|
|
}
|
|
|
|
}
|
2024-11-28 23:39:15 +08:00
|
|
|
|
2024-11-29 20:01:10 +08:00
|
|
|
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)) {
|
2024-11-28 23:39:15 +08:00
|
|
|
|
2024-11-29 20:01:10 +08:00
|
|
|
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;
|
2024-11-28 19:34:24 +08:00
|
|
|
} else {
|
2024-11-29 20:01:10 +08:00
|
|
|
return Wt::WAbstractTableModel::setData(index, value, role);
|
2024-11-28 19:34:24 +08:00
|
|
|
}
|
|
|
|
}
|
2024-11-29 20:01:10 +08:00
|
|
|
|
2024-12-01 15:10:25 +08:00
|
|
|
Wt::WModelIndexSet VisitorRecordTableModel::selectedIndexes() const {
|
|
|
|
Wt::WModelIndexSet ret;
|
2024-11-29 20:01:10 +08:00
|
|
|
for (int i = 0; i < m_records.size(); i++) {
|
|
|
|
if (std::get<1>(m_records.at(i))) {
|
2024-12-01 15:10:25 +08:00
|
|
|
ret.insert(index(i, 0));
|
2024-11-29 20:01:10 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2025-01-17 02:55:03 +08:00
|
|
|
void VisitorRecordTableModel::filter(const std::string &search) {
|
|
|
|
layoutAboutToBeChanged().emit();
|
|
|
|
m_records.clear();
|
|
|
|
|
|
|
|
Wt::Dbo::Transaction transaction(m_session);
|
|
|
|
VisitorRecords records = m_session.find<VisitorRecord>().where(std::format("url LIKE '%{}%'", search));
|
|
|
|
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();
|
|
|
|
}
|
|
|
|
layoutChanged().emit();
|
|
|
|
}
|
|
|
|
|
2024-12-01 15:10:25 +08:00
|
|
|
bool VisitorRecordTableModel::removeSelectedRows(const Wt::WModelIndexSet &indexes) {
|
2024-11-29 20:01:10 +08:00
|
|
|
Wt::Dbo::Transaction transaction(m_session);
|
2024-12-01 15:10:25 +08:00
|
|
|
layoutAboutToBeChanged().emit();
|
|
|
|
std::vector<int> ids;
|
|
|
|
for (auto &index : indexes) {
|
|
|
|
auto &r = std::get<0>(m_records.at(index.row()));
|
|
|
|
ids.push_back(r.id);
|
|
|
|
Wt::Dbo::ptr<VisitorRecord> item = m_session.find<VisitorRecord>().where("id = ?").bind(r.id);
|
|
|
|
item.remove();
|
|
|
|
}
|
|
|
|
for (auto id : ids) {
|
|
|
|
std::erase_if(m_records, [id](const Item &item) { return std::get<0>(item).id == id; });
|
2024-11-29 20:01:10 +08:00
|
|
|
}
|
2024-12-01 15:10:25 +08:00
|
|
|
layoutChanged().emit();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool VisitorRecordTableModel::removeRows(int row, int count, const Wt::WModelIndex &parent) {
|
|
|
|
if ((row + count) > m_records.size()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
Wt::Dbo::Transaction transaction(m_session);
|
|
|
|
beginRemoveRows(parent, row, row + count - 1);
|
|
|
|
for (int i = row; i < (row + count); ++i) {
|
|
|
|
auto &r = std::get<0>(m_records.at(i));
|
|
|
|
Wt::Dbo::ptr<VisitorRecord> item = m_session.find<VisitorRecord>().where("id = ?").bind(r.id);
|
|
|
|
item.remove();
|
|
|
|
}
|
|
|
|
m_records.erase(m_records.cbegin() + row, m_records.cbegin() + row + count);
|
|
|
|
endRemoveRows();
|
|
|
|
return true;
|
|
|
|
}
|