2026-06-04 23:12:34 +08:00
|
|
|
{% extends 'frontend/base.html' %}
|
|
|
|
|
|
|
|
|
|
{% block title %}{{ article.title }} - {{ g.site_config.site_name }}{% endblock %}
|
|
|
|
|
|
|
|
|
|
{% block extra_css %}
|
|
|
|
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/highlight.js@11.9.0/styles/github.min.css">
|
|
|
|
|
<link rel="stylesheet" href="{{ url_for('static', filename='css/markdown.css') }}">
|
|
|
|
|
{% endblock %}
|
|
|
|
|
|
|
|
|
|
{% block extra_js %}
|
|
|
|
|
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
|
2026-06-04 23:20:59 +08:00
|
|
|
<script src="https://cdn.jsdelivr.net/npm/highlight.js@11.9.0/highlight.min.js"></script>
|
2026-06-04 23:12:34 +08:00
|
|
|
<script>
|
|
|
|
|
// 配置 marked
|
|
|
|
|
marked.setOptions({
|
|
|
|
|
highlight: function(code, lang) {
|
|
|
|
|
if (lang && hlse.getLanguage(lang)) {
|
|
|
|
|
try {
|
|
|
|
|
return hlse.highlight(code, { language: lang }).value;
|
|
|
|
|
} catch (e) {}
|
|
|
|
|
}
|
|
|
|
|
return hlse.highlightAuto(code).value;
|
|
|
|
|
},
|
|
|
|
|
breaks: true,
|
|
|
|
|
gfm: true
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 渲染 Markdown
|
|
|
|
|
const content = document.getElementById('markdown-content');
|
|
|
|
|
if (content) {
|
|
|
|
|
const markdownText = content.getAttribute('data-content');
|
|
|
|
|
content.innerHTML = marked.parse(markdownText);
|
|
|
|
|
}
|
|
|
|
|
</script>
|
|
|
|
|
{% endblock %}
|
|
|
|
|
|
|
|
|
|
{% block content %}
|
|
|
|
|
<article class="article-detail">
|
|
|
|
|
<header class="article-header">
|
|
|
|
|
<h1 class="article-title">{{ article.title }}</h1>
|
|
|
|
|
|
|
|
|
|
<div class="article-meta">
|
|
|
|
|
{% if article.author %}
|
|
|
|
|
<span class="author">
|
|
|
|
|
{% if article.author.avatar %}
|
|
|
|
|
<img src="{{ article.author.avatar }}" alt="{{ article.author.name }}" class="author-avatar">
|
|
|
|
|
{% endif %}
|
|
|
|
|
{{ article.author.name }}
|
|
|
|
|
</span>
|
|
|
|
|
{% endif %}
|
|
|
|
|
{% if article.category %}
|
|
|
|
|
<span class="category">
|
|
|
|
|
<a href="{{ url_for('frontend.category', category_id=article.category.id) }}">
|
|
|
|
|
{{ article.category.name }}
|
|
|
|
|
</a>
|
|
|
|
|
</span>
|
|
|
|
|
{% endif %}
|
|
|
|
|
<span class="date">{{ article.published_at|datetime_format('%Y-%m-%d %H:%M') }}</span>
|
|
|
|
|
<span class="views">{{ article.view_count }} 阅读</span>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{% if article.tags %}
|
|
|
|
|
<div class="article-tags">
|
|
|
|
|
{% for tag in article.tags %}
|
|
|
|
|
<a href="{{ url_for('frontend.tag', tag_id=tag.id) }}"
|
|
|
|
|
class="tag" style="--tag-color: {{ tag.color }}">
|
|
|
|
|
{{ tag.name }}
|
|
|
|
|
</a>
|
|
|
|
|
{% endfor %}
|
|
|
|
|
</div>
|
|
|
|
|
{% endif %}
|
|
|
|
|
</header>
|
|
|
|
|
|
|
|
|
|
{% if article.summary %}
|
|
|
|
|
<div class="article-summary">
|
|
|
|
|
<strong>摘要:</strong>{{ article.summary }}
|
|
|
|
|
</div>
|
|
|
|
|
{% endif %}
|
|
|
|
|
|
|
|
|
|
{% if article.cover_image %}
|
|
|
|
|
<div class="article-cover">
|
|
|
|
|
<img src="{{ article.cover_image }}" alt="{{ article.title }}">
|
|
|
|
|
</div>
|
|
|
|
|
{% endif %}
|
|
|
|
|
|
|
|
|
|
<div id="markdown-content" class="article-content markdown-body" data-content="{{ article.content | e }}">
|
|
|
|
|
<!-- Markdown 将在这里渲染 -->
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{% if article.author and article.author.bio %}
|
|
|
|
|
<div class="author-card">
|
|
|
|
|
{% if article.author.avatar %}
|
|
|
|
|
<img src="{{ article.author.avatar }}" alt="{{ article.author.name }}" class="author-avatar-lg">
|
|
|
|
|
{% endif %}
|
|
|
|
|
<div class="author-info">
|
|
|
|
|
<h4>{{ article.author.name }}</h4>
|
|
|
|
|
<p>{{ article.author.bio }}</p>
|
|
|
|
|
{% if article.author.website %}
|
|
|
|
|
<a href="{{ article.author.website }}" target="_blank">个人主页</a>
|
|
|
|
|
{% endif %}
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
{% endif %}
|
|
|
|
|
|
|
|
|
|
{% if article.references.all() %}
|
|
|
|
|
<div class="references-section">
|
|
|
|
|
<h3>📚 参考来源</h3>
|
|
|
|
|
<ul>
|
|
|
|
|
{% for ref in article.references %}
|
|
|
|
|
<li>
|
|
|
|
|
{% if ref.url %}
|
|
|
|
|
<a href="{{ ref.url }}" target="_blank">{{ ref.title }}</a>
|
|
|
|
|
{% else %}
|
|
|
|
|
{{ ref.title }}
|
|
|
|
|
{% endif %}
|
|
|
|
|
{% if ref.description %}
|
|
|
|
|
<span class="ref-desc">{{ ref.description }}</span>
|
|
|
|
|
{% endif %}
|
|
|
|
|
</li>
|
|
|
|
|
{% endfor %}
|
|
|
|
|
</ul>
|
|
|
|
|
</div>
|
|
|
|
|
{% endif %}
|
|
|
|
|
|
|
|
|
|
{% if article.attachments.all() %}
|
|
|
|
|
<div class="attachments-section">
|
|
|
|
|
<h3>📎 相关附件</h3>
|
|
|
|
|
<ul>
|
|
|
|
|
{% for att in article.attachments %}
|
|
|
|
|
<li>
|
|
|
|
|
<a href="/uploads/{{ att.filename }}" target="blank">
|
|
|
|
|
{{ att.filename }}
|
|
|
|
|
</a>
|
|
|
|
|
<span class="filesize">{{ att.get_filesize_display() }}</span>
|
|
|
|
|
{% if att.description %}
|
|
|
|
|
<span class="att-desc">{{ att.description }}</span>
|
|
|
|
|
{% endif %}
|
|
|
|
|
</li>
|
|
|
|
|
{% endfor %}
|
|
|
|
|
</ul>
|
|
|
|
|
</div>
|
|
|
|
|
{% endif %}
|
|
|
|
|
|
|
|
|
|
<footer class="article-footer">
|
|
|
|
|
<div class="article-nav">
|
|
|
|
|
<a href="{{ url_for('frontend.index') }}">← 返回首页</a>
|
|
|
|
|
</div>
|
|
|
|
|
</footer>
|
|
|
|
|
</article>
|
|
|
|
|
{% endblock %}
|