tracexd/target/classes/templates/browse.html
2026-06-09 16:03:44 +08:00

272 lines
12 KiB
HTML

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<link rel="icon" type="image/x-icon" href="/favicon.ico">
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>TraceXD - 浏览记录</title>
<link rel="stylesheet" href="/css/style.css">
</head>
<body>
<div id="app">
<header class="app-header">
<a href="/" class="header-link">首页</a>
<span class="header-title">浏览记录</span>
<a href="#" onclick="logout(); return false;" class="header-logout">退出</a>
</header>
<main class="main-content">
<!-- 筛选项区域 -->
<div id="filter-section" class="filter-section">
<div class="filter-group">
<label class="filter-label">人物</label>
<div id="filter-persons" class="filter-tags"></div>
</div>
<div class="filter-group">
<label class="filter-label">地点</label>
<div id="filter-locations" class="filter-tags"></div>
</div>
<div class="filter-group">
<label class="filter-label">分类</label>
<div id="filter-categories" class="filter-tags"></div>
</div>
<div class="filter-group">
<label class="filter-label">时间</label>
<div class="filter-date-range">
<input type="date" id="filter-start-date" class="date-input">
<span class="date-sep">~</span>
<input type="date" id="filter-end-date" class="date-input">
</div>
</div>
<div class="filter-actions">
<button id="btn-reset" class="btn btn-secondary">重置</button>
<button id="btn-query" class="btn btn-primary">查询</button>
</div>
</div>
<!-- 折叠后的筛选项摘要 -->
<div id="filter-summary" class="filter-summary hidden">
<span id="filter-summary-text"></span>
<button id="btn-expand" class="btn btn-link">展开筛选项</button>
</div>
<!-- 金额汇总 -->
<div id="summary-bar" class="summary-bar hidden">
金额汇总: <strong id="total-amount">¥0.00</strong>
</div>
<!-- 数据表格 -->
<div class="table-wrapper">
<table class="data-table" id="data-table">
<thead>
<tr>
<th class="col-time">时间</th>
<th class="col-person">人物</th>
<th class="col-location">地点</th>
<th class="col-content">事项内容</th>
<th class="col-amount">金额</th>
<th class="col-category">分类</th>
</tr>
</thead>
<tbody id="table-body">
<tr><td colspan="6" class="empty-cell">请点击"查询"查看记录</td></tr>
</tbody>
</table>
</div>
<!-- 分页 -->
<div id="pagination" class="pagination hidden">
<button id="btn-prev" class="btn btn-page">&lt; 上一页</button>
<span id="page-info"></span>
<button id="btn-next" class="btn btn-page">下一页 &gt;</button>
</div>
</main>
</div>
<script src="/js/app.js"></script>
<script>
(function() {
var currentPage = 1;
var pageSize = 20;
var totalPages = 0;
var filterCollapsed = false;
var selectedPersons = [];
var selectedLocations = [];
var selectedCategories = [];
function init() {
loadFilters();
document.getElementById('btn-query').addEventListener('click', function() {
doQuery(1);
});
document.getElementById('btn-reset').addEventListener('click', function() {
resetFilters();
});
document.getElementById('btn-expand').addEventListener('click', function() {
toggleFilterSection(false);
});
document.getElementById('btn-prev').addEventListener('click', function() {
if (currentPage > 1) doQuery(currentPage - 1);
});
document.getElementById('btn-next').addEventListener('click', function() {
if (currentPage < totalPages) doQuery(currentPage + 1);
});
}
function loadFilters() {
fetch('/api/records/filters')
.then(function(r) { return r.json(); })
.then(function(resp) {
if (resp.code !== 200) return;
var data = resp.data;
renderFilterTags('filter-persons', data.persons || [], 'person');
renderFilterTags('filter-locations', data.locations || [], 'location');
renderFilterTags('filter-categories', data.categories || [], 'category');
});
}
function renderFilterTags(containerId, items, type) {
var container = document.getElementById(containerId);
container.innerHTML = '';
items.forEach(function(item) {
var tag = document.createElement('span');
tag.className = 'tag-btn';
tag.textContent = item;
tag.dataset.type = type;
tag.dataset.value = item;
tag.addEventListener('click', function() {
this.classList.toggle('active');
});
container.appendChild(tag);
});
}
function getSelectedTags(type) {
var tags = document.querySelectorAll('.tag-btn[data-type="' + type + '"].active');
return Array.from(tags).map(function(t) { return t.dataset.value; });
}
function getQueryParams() {
selectedPersons = getSelectedTags('person');
selectedLocations = getSelectedTags('location');
selectedCategories = getSelectedTags('category');
var startDate = document.getElementById('filter-start-date').value;
var endDate = document.getElementById('filter-end-date').value;
return {
persons: selectedPersons,
locations: selectedLocations,
categories: selectedCategories,
startDate: startDate,
endDate: endDate
};
}
function buildFilterSummary(params) {
var parts = [];
if (params.persons.length) parts.push('人物(' + params.persons.join(',') + ')');
if (params.locations.length) parts.push('地点(' + params.locations.join(',') + ')');
if (params.categories.length) parts.push('分类(' + params.categories.join(',') + ')');
if (params.startDate || params.endDate) {
parts.push('时间(' + (params.startDate || '不限') + '~' + (params.endDate || '不限') + ')');
}
return parts.join(' | ') || '无条件';
}
function toggleFilterSection(collapsed) {
filterCollapsed = collapsed;
document.getElementById('filter-section').classList.toggle('hidden', collapsed);
document.getElementById('filter-summary').classList.toggle('hidden', !collapsed);
}
function doQuery(page) {
var params = getQueryParams();
var queryStr = '?page=' + page + '&size=' + pageSize;
if (params.persons.length) {
params.persons.forEach(function(p) { queryStr += '&persons=' + encodeURIComponent(p); });
}
if (params.locations.length) {
params.locations.forEach(function(l) { queryStr += '&locations=' + encodeURIComponent(l); });
}
if (params.categories.length) {
params.categories.forEach(function(c) { queryStr += '&categories=' + encodeURIComponent(c); });
}
if (params.startDate) queryStr += '&startDate=' + params.startDate;
if (params.endDate) queryStr += '&endDate=' + params.endDate;
fetch('/api/records' + queryStr)
.then(function(r) { return r.json(); })
.then(function(resp) {
if (resp.code !== 200) return;
renderTable(resp.data);
toggleFilterSection(true);
var summary = document.getElementById('filter-summary-text');
summary.textContent = '已选: ' + buildFilterSummary(params);
});
}
function renderTable(data) {
if (!data) return;
var records = data.records || [];
var totalAmount = data.totalAmount || 0;
var total = data.total || 0;
currentPage = Number(data.page) || 1;
totalPages = Math.ceil(total / pageSize);
// 更新金额汇总
var summaryBar = document.getElementById('summary-bar');
summaryBar.classList.remove('hidden');
document.getElementById('total-amount').textContent = '¥' + Number(totalAmount).toFixed(2);
// 更新表格
var tbody = document.getElementById('table-body');
if (records.length === 0) {
tbody.innerHTML = '<tr><td colspan="6" class="empty-cell">暂无数据</td></tr>';
} else {
var html = '';
records.forEach(function(r) {
html += '<tr>' +
'<td>' + formatTime(r.time) + '</td>' +
'<td>' + (r.person || '我') + '</td>' +
'<td>' + (r.location || '-') + '</td>' +
'<td>' + (r.content || '') + '</td>' +
'<td class="col-amount-cell">¥' + (r.amount ? Number(r.amount).toFixed(2) : '0.00') + '</td>' +
'<td><span class="badge badge ' + badgeClass(r.category) + '">' +
(r.category || '-') + '</span></td>' +
'</tr>';
});
tbody.innerHTML = html;
}
// 更新分页
var pagination = document.getElementById('pagination');
if (totalPages > 1) {
pagination.classList.remove('hidden');
document.getElementById('page-info').textContent =
'第 ' + currentPage + ' / ' + totalPages + ' 页(共 ' + total + ' 条)';
document.getElementById('btn-prev').disabled = currentPage <= 1;
document.getElementById('btn-next').disabled = currentPage >= totalPages;
} else {
pagination.classList.add('hidden');
}
}
function resetFilters() {
document.querySelectorAll('.tag-btn').forEach(function(t) { t.classList.remove('active'); });
document.getElementById('filter-start-date').value = '';
document.getElementById('filter-end-date').value = '';
document.getElementById('table-body').innerHTML =
'<tr><td colspan="6" class="empty-cell">请点击"查询"查看记录</td></tr>';
document.getElementById('summary-bar').classList.add('hidden');
document.getElementById('pagination').classList.add('hidden');
toggleFilterSection(false);
}
init();
})();
</script>
</body>
</html>