initial: blog-app snapshot

This commit is contained in:
omo
2026-08-17 17:11:27 +08:00
commit 65cb9d5ead
125 changed files with 14720 additions and 0 deletions

View File

@@ -0,0 +1,95 @@
/* 项目详情页逻辑: 项目头部 + release 时间线 + 最近 issues. */
import { fetchJson, escapeHtml, truncate, formatDate } from "../utils.js";
export function initProjectDetailPage(projectId) {
projectId = Number.parseInt(projectId, 10);
if (Number.isNaN(projectId)) {
console.error("project-detail: invalid project id");
return;
}
async function loadProject() {
const header = document.getElementById("projectHeader");
try {
const p = await fetchJson(`/api/projects/${projectId}`);
header.innerHTML = `
<div class="card mb-3">
<div class="card-body">
<h1 class="card-title h3">${escapeHtml(p.name)}
<small class="text-muted fs-6">${escapeHtml(p.full_name || "")}</small>
</h1>
<p class="card-text">${escapeHtml(p.description || "暂无描述")}</p>
<p class="mb-0">
<span class="badge bg-warning text-dark">⭐ Stars: ${p.stars ?? 0}</span>
<span class="badge bg-secondary">🍴 Forks: ${p.forks ?? 0}</span>
<span class="badge bg-info text-dark">${escapeHtml(p.language || "N/A")}</span>
<span class="badge bg-light text-dark">更新于 ${formatDate(p.updated_at)}</span>
</p>
</div>
</div>`;
} catch (err) {
console.error("加载项目信息失败:", err);
header.innerHTML =
'<div class="empty-hint empty-hint-error">加载失败, <button type="button" class="btn btn-sm btn-outline-danger js-retry-btn">点击重试</button></div>';
}
}
async function loadReleases() {
const list = document.getElementById("releaseTimeline");
try {
const releases = await fetchJson(`/api/projects/${projectId}/releases`);
if (!releases.length) {
list.innerHTML = '<li class="list-group-item empty-hint">暂无 release</li>';
return;
}
list.innerHTML = releases
.map(
(r) => `
<li class="list-group-item">
<div class="d-flex justify-content-between align-items-baseline">
<strong>${escapeHtml(r.tag_name || r.name || "release")}</strong>
<small class="text-muted">${formatDate(r.published_at)}</small>
</div>
<div class="text-muted small mt-1">${escapeHtml(truncate(r.body, 200))}</div>
</li>`
)
.join("");
} catch (err) {
console.error("加载 releases 失败:", err);
list.innerHTML =
'<li class="list-group-item empty-hint empty-hint-error">加载失败, <button type="button" class="btn btn-sm btn-outline-danger js-retry-btn">点击重试</button></li>';
}
}
async function loadIssues() {
const tbody = document.getElementById("issueTableBody");
try {
const issues = await fetchJson(`/api/projects/${projectId}/issues`);
if (!issues.length) {
tbody.innerHTML = '<tr><td colspan="4" class="empty-hint">暂无 issue</td></tr>';
return;
}
tbody.innerHTML = issues
.map(
(i) => `
<tr>
<td>${escapeHtml(truncate(i.title, 60))}</td>
<td><span class="badge ${i.state === "open" ? "badge-state-open" : "badge-state-closed"}">${escapeHtml(i.state || "")}</span></td>
<td>${formatDate(i.created_at)}</td>
<td>${escapeHtml(i.user || "-")}</td>
</tr>`
)
.join("");
} catch (err) {
console.error("加载 issues 失败:", err);
tbody.innerHTML =
'<tr><td colspan="4" class="empty-hint empty-hint-error">加载失败, <button type="button" class="btn btn-sm btn-outline-danger js-retry-btn">点击重试</button></td></tr>';
}
}
document.addEventListener("DOMContentLoaded", () => {
loadProject();
loadReleases();
loadIssues();
});
}