Files
2026Technology-Competition/src/web/static/history.html
T

136 lines
7.5 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>历史记录</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { font-family: "Microsoft YaHei", sans-serif; background: #EDEFF3; height: 100vh; overflow: hidden; display: flex; }
.sidebar { width: 220px; height: 100vh; background: #FAFBFC; border-right: 1px solid #E4E7EB; display: flex; flex-direction: column; flex-shrink: 0; }
.sidebar-brand { display: flex; align-items: center; gap: 8px; padding: 20px 16px; font-size: 15px; font-weight: 700; color: #1565C0; }
.sidebar-nav { flex: 1; padding: 8px 0; }
.nav-item { display: flex; align-items: center; height: 48px; padding: 0 16px; margin: 2px 8px; border-radius: 8px; cursor: pointer; font-size: 14px; color: #555; }
.nav-item:hover { background: #EEF2F7; color: #333; }
.nav-item.active { background: #E3F2FD; color: #1565C0; font-weight: 600; }
.nav-icon { margin-right: 10px; }
.sidebar-footer { padding: 16px; border-top: 1px solid #E8ECF0; }
.btn-logout { width: 100%; height: 36px; border: 1px solid #E0E0E0; border-radius: 8px; background: #fff; color: #666; cursor: pointer; font-family: inherit; }
.btn-logout:hover { border-color: #E53935; color: #E53935; }
.main { flex: 1; overflow-y: auto; padding: 32px 48px; }
.page-title { font-size: 22px; font-weight: 700; color: #1A1A1A; margin-bottom: 20px; }
.search-row { display: flex; gap: 12px; margin-bottom: 20px; }
.search-input { flex: 1; height: 40px; border: 1px solid #E0E0E0; border-radius: 8px; padding: 0 14px; font-size: 14px; font-family: inherit; outline: none; }
.search-input:focus { border-color: #1565C0; }
.filter-select { width: 130px; height: 40px; border: 1px solid #E0E0E0; border-radius: 8px; padding: 0 10px; font-size: 14px; font-family: inherit; outline: none; background: #fff; flex-shrink: 0; }
.filter-select:focus { border-color: #1565C0; }
.btn-back { height: 40px; padding: 0 18px; border: 1px solid #E0E0E0; border-radius: 8px; background: #fff; color: #666; cursor: pointer; font-family: inherit; }
.btn-back:hover { border-color: #1565C0; color: #1565C0; }
.list-empty { text-align: center; color: #999; padding: 80px 0; }
.record-item { background: #fff; border-radius: 10px; padding: 18px 22px; margin-bottom: 12px; box-shadow: 0 1px 3px rgba(0,0,0,0.05); display: flex; align-items: center; justify-content: space-between; }
.record-title { font-size: 15px; font-weight: 600; color: #333; margin-bottom: 4px; }
.record-meta { font-size: 12px; color: #999; }
.record-actions { display: flex; gap: 8px; }
.btn-dl { height: 32px; padding: 0 14px; border: none; border-radius: 6px; background: #1565C0; color: #fff; font-size: 13px; cursor: pointer; font-family: inherit; }
.btn-del { height: 32px; padding: 0 14px; border: 1px solid #E53935; border-radius: 6px; background: #fff; color: #E53935; font-size: 13px; cursor: pointer; font-family: inherit; }
.lang-row { display: flex; gap: 8px; margin-bottom: 10px; }
.lang-btn { flex: 1; height: 32px; border: 1px solid #E0E0E0; border-radius: 8px; background: #fff; color: #666; font-size: 13px; cursor: pointer; font-family: inherit; }
.lang-btn.active { border-color: #1565C0; color: #1565C0; font-weight: 600; }
</style>
</head>
<body>
<aside class="sidebar">
<div class="sidebar-brand">📊 <span data-i18n="app_name">演示文稿智能生成</span></div>
<nav class="sidebar-nav">
<div class="nav-item" onclick="location.href='/workspace'"><span class="nav-icon">📄</span><span data-i18n="nav_generate">PPT 生成</span></div>
<div class="nav-item active"><span class="nav-icon">🕐</span><span data-i18n="nav_history">历史记录</span></div>
<div class="nav-item" onclick="location.href='/settings'"><span class="nav-icon">⚙️</span><span data-i18n="nav_settings">个人设置</span></div>
</nav>
<div class="sidebar-footer">
<div class="lang-row">
<button class="lang-btn" id="langZh" onclick="setLang('zh')">中文</button>
<button class="lang-btn" id="langJa" onclick="setLang('ja')">日本語</button>
</div>
<button class="btn-logout" onclick="doLogout()" data-i18n="btn_logout">退出登录</button>
</div>
</aside>
<main class="main">
<div class="page-title" data-i18n="nav_history">历史记录</div>
<div class="search-row">
<input class="search-input" id="searchInput" data-i18n-ph="history_search_ph" placeholder="搜索标题…" oninput="loadRecords()">
<select class="filter-select" id="langFilter" onchange="loadRecords()">
<option value="" data-i18n="filter_all_lang">全部语言</option>
<option value="zh" data-i18n="lang_zh">中文</option>
<option value="ja" data-i18n="lang_ja">日本語</option>
</select>
<button class="btn-back" onclick="location.href='/workspace'" data-i18n="btn_new_doc"> 新建</button>
</div>
<div id="recordList"></div>
</main>
<script>
let I18N = {};
async function loadLang(lang) {
try {
const res = await fetch('/static/lang/' + lang + '.json');
I18N = await res.json();
} catch (e) { I18N = {}; }
document.querySelectorAll('[data-i18n]').forEach(el => {
el.textContent = I18N[el.dataset.i18n] || el.textContent;
});
document.querySelectorAll('[data-i18n-ph]').forEach(el => {
el.placeholder = I18N[el.dataset.i18nPh] || el.placeholder;
});
document.getElementById('langZh').classList.toggle('active', lang === 'zh');
document.getElementById('langJa').classList.toggle('active', lang === 'ja');
}
function setLang(lang) {
loadLang(lang).then(loadRecords);
localStorage.setItem('ui_lang', lang);
}
async function loadRecords() {
const kw = document.getElementById('searchInput').value.trim();
const lang = document.getElementById('langFilter').value;
try {
const res = await fetch('/api/records?keyword=' + encodeURIComponent(kw) + '&language=' + encodeURIComponent(lang));
const data = await res.json();
if (data.code !== 0) { window.location.href = '/login'; return; }
render(data.data.records);
} catch (e) { window.location.href = '/login'; }
}
function render(records) {
const list = document.getElementById('recordList');
if (!records || records.length === 0) {
list.innerHTML = '<div class="list-empty">' + (I18N.history_empty || '暂无历史记录,去工作台生成一个吧') + '</div>';
return;
}
list.innerHTML = records.map(r => `
<div class="record-item">
<div>
<div class="record-title">${r.title || I18N.unnamed_title || '未命名'}</div>
<div class="record-meta">${r.scene === 'report' ? (I18N.scene_report || '社内汇报') : (I18N.scene_training || '社内培训')} · ${r.language === 'ja' ? (I18N.lang_ja || '日本語') : (I18N.lang_zh || '中文')} · ${r.page_count || '-'} ${I18N.preview_pages || '页'} · ${r.created_at}</div>
</div>
<div class="record-actions">
<button class="btn-dl" onclick="download(${r.id})">${I18N.btn_download || '下载'}</button>
<button class="btn-del" onclick="del(${r.id})">${I18N.btn_delete || '删除'}</button>
</div>
</div>`).join('');
}
function download(id) { window.location.href = '/api/files/' + id + '/download'; }
async function del(id) {
if (!confirm(I18N.confirm_del_record || '确认删除该记录?')) return;
await fetch('/api/records/' + id, { method: 'DELETE' });
loadRecords();
}
async function doLogout() { await fetch('/api/logout', { method: 'POST' }); window.location.href = '/login'; }
(async function init() {
const saved = localStorage.getItem('ui_lang');
await loadLang(saved === 'ja' ? 'ja' : 'zh');
loadRecords();
})();
</script>
</body>
</html>