Files
termi-blog/backend/assets/views/admin/posts.html

200 lines
8.1 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.
{% extends "admin/base.html" %}
{% block main_content %}
<section class="form-panel">
<div class="table-head">
<div>
<h2>新建 Markdown 文章</h2>
<div class="table-note">直接生成 `content/posts/*.md` 文件,后端会自动解析 frontmatter、同步分类和标签。</div>
</div>
</div>
<form method="post" action="/admin/posts" class="form-grid">
<div class="field">
<label>标题</label>
<input type="text" name="title" value="{{ create_form.title }}" required>
</div>
<div class="field">
<label>Slug</label>
<input type="text" name="slug" value="{{ create_form.slug }}" placeholder="可留空自动生成">
</div>
<div class="field">
<label>分类</label>
<input type="text" name="category" value="{{ create_form.category }}" placeholder="例如 tech">
</div>
<div class="field">
<label>标签</label>
<input type="text" name="tags" value="{{ create_form.tags }}" placeholder="逗号分隔">
</div>
<div class="field">
<label>文章类型</label>
<input type="text" name="post_type" value="{{ create_form.post_type }}">
</div>
<div class="field">
<label>封面图</label>
<input type="text" name="image" value="{{ create_form.image }}" placeholder="可选">
</div>
<div class="field field-wide">
<label>摘要</label>
<textarea name="description">{{ create_form.description }}</textarea>
</div>
<div class="field field-wide">
<label>正文 Markdown</label>
<textarea name="content" style="min-height: 22rem; font-family: var(--font-mono); line-height: 1.65;">{{ create_form.content }}</textarea>
</div>
<div class="field field-wide">
<div class="actions">
<label class="chip"><input type="checkbox" name="published" checked style="margin-right: 8px;">发布</label>
<label class="chip"><input type="checkbox" name="pinned" style="margin-right: 8px;">置顶</label>
<button type="submit" class="btn btn-primary">创建文章</button>
</div>
</div>
</form>
</section>
<section class="form-panel">
<div class="table-head">
<div>
<h2>导入 Markdown 文件</h2>
<div class="table-note">支持选择单个 `.md/.markdown` 文件,也支持直接选择一个本地 Markdown 文件夹批量导入。</div>
</div>
</div>
<form id="markdown-import-form" class="form-grid">
<div class="field">
<label>选择文件</label>
<input id="markdown-files" type="file" accept=".md,.markdown" multiple>
</div>
<div class="field">
<label>选择文件夹</label>
<input id="markdown-folder" type="file" accept=".md,.markdown" webkitdirectory directory multiple>
</div>
<div class="field field-wide">
<div class="actions">
<button id="import-submit" type="submit" class="btn btn-success">导入 Markdown</button>
</div>
<div class="field-hint" style="margin-top: 10px;">导入时会从 frontmatter 和正文里提取标题、slug、摘要、分类、标签与内容并写入服务器 `content/posts`。</div>
<div id="import-notice" class="notice"></div>
</div>
</form>
</section>
<section class="table-panel">
<div class="table-head">
<div>
<h2>内容列表</h2>
<div class="table-note">直接跳到前台文章、分类筛选和 API 明细。</div>
</div>
</div>
{% if rows | length > 0 %}
<div class="table-wrap">
<table>
<thead>
<tr>
<th>ID</th>
<th>文章</th>
<th>分类</th>
<th>标签</th>
<th>时间</th>
<th>跳转</th>
</tr>
</thead>
<tbody>
{% for row in rows %}
<tr>
<td class="mono">#{{ row.id }}</td>
<td>
<div class="item-title">
<strong>{{ row.title }}</strong>
<span class="item-meta">{{ row.slug }}</span>
<span class="item-meta">{{ row.file_path }}</span>
</div>
</td>
<td>
<div class="item-title">
<strong>{{ row.category_name }}</strong>
<a href="{{ row.category_frontend_url }}" class="inline-link" target="_blank" rel="noreferrer noopener">查看该分类文章</a>
</div>
</td>
<td>
<div class="inline-links">
{% if row.tags | length > 0 %}
{% for tag in row.tags %}
<span class="chip">#{{ tag }}</span>
{% endfor %}
{% else %}
<span class="badge-soft">暂无标签</span>
{% endif %}
</div>
</td>
<td class="mono">{{ row.created_at }}</td>
<td>
<div class="actions">
<a href="{{ row.edit_url }}" class="btn btn-success">编辑 Markdown</a>
<a href="{{ row.frontend_url }}" class="btn btn-primary" target="_blank" rel="noreferrer noopener">前台详情</a>
<a href="{{ row.api_url }}" class="btn btn-ghost" target="_blank" rel="noreferrer noopener">API</a>
</div>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% else %}
<div class="empty">当前没有可管理的文章数据。</div>
{% endif %}
</section>
{% endblock %}
{% block page_scripts %}
<script>
const importForm = document.getElementById("markdown-import-form");
const importFiles = document.getElementById("markdown-files");
const importFolder = document.getElementById("markdown-folder");
const importNotice = document.getElementById("import-notice");
function showImportNotice(message, kind) {
importNotice.textContent = message;
importNotice.className = "notice show " + (kind === "success" ? "notice-success" : "notice-error");
}
importForm?.addEventListener("submit", async (event) => {
event.preventDefault();
const selectedFiles = [
...(importFiles?.files ? Array.from(importFiles.files) : []),
...(importFolder?.files ? Array.from(importFolder.files) : []),
].filter((file) => file.name.endsWith(".md") || file.name.endsWith(".markdown"));
if (!selectedFiles.length) {
showImportNotice("请先选择要导入的 Markdown 文件或文件夹。", "error");
return;
}
const payload = new FormData();
selectedFiles.forEach((file) => {
const uploadName = file.webkitRelativePath || file.name;
payload.append("files", file, uploadName);
});
try {
const response = await fetch("/admin/posts/import", {
method: "POST",
body: payload,
});
if (!response.ok) {
throw new Error(await response.text() || "import failed");
}
const result = await response.json();
showImportNotice(`已导入 ${result.count} 个 Markdown 文件,正在刷新列表。`, "success");
setTimeout(() => window.location.reload(), 900);
} catch (error) {
showImportNotice("导入失败:" + (error?.message || "unknown error"), "error");
}
});
</script>
{% endblock %}