initial: novel-app snapshot

This commit is contained in:
omo
2026-08-17 17:11:30 +08:00
commit 1ae06209ac
34 changed files with 2821 additions and 0 deletions

44
static/js/pages/index.js Normal file
View File

@@ -0,0 +1,44 @@
/* index page: 小说列表 dashboard. */
import { escapeHtml, fetchJson } from "../utils.js";
import { statusBadge } from "../components/status-badge.js";
function renderCard(n) {
const target = n.target_words || 0;
const current = n.current_words || 0;
const pct = target > 0 ? Math.min(100, Math.round((current / target) * 100)) : 0;
return ''
+ '<div class="col-md-4">'
+ ' <div class="card h-100">'
+ ' <div class="card-body">'
+ ' <h5 class="card-title">' + escapeHtml(n.title) + '</h5>'
+ ' <p class="card-text mb-2">'
+ ' <span class="badge text-bg-primary">' + escapeHtml(n.genre || "未分类") + '</span> '
+ statusBadge(n.status)
+ ' </p>'
+ ' <p class="card-text text-muted small">' + escapeHtml(n.style || "") + '</p>'
+ ' <div class="progress mb-2" role="progressbar" aria-label="写作进度">'
+ ' <div class="progress-bar" style="width:' + pct + '%">' + pct + '%</div>'
+ ' </div>'
+ ' <p class="card-text small text-muted">' + current + ' / ' + target + ' 字</p>'
+ ' <a class="btn btn-outline-primary btn-sm" href="/novel/' + n.id + '">打开</a>'
+ ' </div>'
+ ' </div>'
+ '</div>';
}
async function loadNovels() {
const container = document.getElementById("novelCards");
try {
const novels = await fetchJson("/api/novels");
if (!novels.length) {
container.innerHTML = '<div class="text-muted">还没有小说, 点击右上角"拆书创建新小说"开始。</div>';
return;
}
container.innerHTML = novels.map(renderCard).join("");
} catch (err) {
container.innerHTML = '<div class="alert alert-danger">加载失败: ' + escapeHtml(err.message) + "</div>";
}
}
loadNovels();