272 lines
12 KiB
HTML
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">< 上一页</button>
|
|
<span id="page-info"></span>
|
|
<button id="btn-next" class="btn btn-page">下一页 ></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>
|