- 写文章页:可见性/类型/转载来源/创作声明/禁止转载 属性表单 - 类型切换联动(转载显示来源,原创显示禁止转载复选框) - 文章详情页:私密角标、置顶标记、创作声明横幅、转载来源 - 管理后台:置顶/取消置顶操作按钮 - PostService Create/Update 签名扩展接受文章属性 - DTO/接口同步更新
235 lines
12 KiB
HTML
235 lines
12 KiB
HTML
{{template "layout/header.html" .}}
|
||
<body>
|
||
|
||
{{template "layout/nav.html" .}}
|
||
|
||
<div class="container post-detail">
|
||
<article class="post-article">
|
||
<header class="post-detail-header">
|
||
<h1 class="post-detail-title">{{.Post.Title}}</h1>
|
||
<div class="post-detail-meta">
|
||
{{if .Post.AuthorName}}
|
||
<span>作者:<a href="/space/{{.Post.UserID}}" class="post-meta-author">{{.Post.AuthorName}}</a></span>
|
||
{{else}}
|
||
<span>作者:该用户已注销</span>
|
||
{{end}}
|
||
<span>阅读:{{.Post.ViewsCount}}</span>
|
||
<span>点赞:{{.Post.LikesCount}}</span>
|
||
<span>收藏:{{.Post.FavoritesCount}}</span>
|
||
<span>评论:{{.Post.CommentsCount}}</span>
|
||
<span>发布时间:{{.Post.CreatedAt.Format "2006-01-02 15:04"}}</span>
|
||
{{if eq .Post.Visibility "private"}}
|
||
<span class="post-badge post-badge-private">私密</span>
|
||
{{end}}
|
||
{{if .Post.PinType}}
|
||
<span class="post-badge post-badge-pinned">📌 {{if eq .Post.PinType "global"}}全局置顶{{else}}分类置顶{{end}}</span>
|
||
{{end}}
|
||
{{if and .Post.IsLocked (eq $.Post.UserID $.UID)}}
|
||
<span class="post-status post-status-locked">已锁定{{if .Post.LockReason}}:{{.Post.LockReason}}{{end}}</span>
|
||
{{end}}
|
||
{{if and .Post.PendingBody (eq $.Post.UserID $.UID)}}
|
||
<span class="post-status post-status-revision">修订审核中</span>
|
||
{{end}}
|
||
</div>
|
||
<!-- 文章属性声明 -->
|
||
{{if or .Post.Declaration .Post.ReprintSource .Post.ReprintProhibited (eq .Post.PostType "reprint")}}
|
||
<div class="post-declarations">
|
||
{{if .Post.Declaration}}
|
||
<div class="post-declaration-item">⚡ {{declarationLabel .Post.Declaration}}</div>
|
||
{{end}}
|
||
{{if eq .Post.PostType "reprint"}}
|
||
<div class="post-declaration-item">📎 转载自:{{.Post.ReprintSource}}</div>
|
||
{{else if .Post.ReprintProhibited}}
|
||
<div class="post-declaration-item">⚠ 未经作者许可,禁止转载</div>
|
||
{{end}}
|
||
</div>
|
||
{{end}}
|
||
</header>
|
||
|
||
{{if .Post.RejectReason}}
|
||
<div class="post-reject-reason">
|
||
<strong>退回理由:</strong>{{.Post.RejectReason}}
|
||
</div>
|
||
{{end}}
|
||
|
||
<div class="post-detail-body" id="postContent"></div>
|
||
</article>
|
||
|
||
<!-- 右下角悬浮操作栏 -->
|
||
<div class="post-float-bar" id="postFloatBar" data-post-id="{{.Post.ID}}">
|
||
<button class="float-btn float-like-btn" id="likeBtn" title="点赞">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M14 9V5a3 3 0 0 0-3-3l-4 9v11h11.28a2 2 0 0 0 2-1.7l1.38-9a2 2 0 0 0-2-2.3H14zM7 22H4a2 2 0 0 1-2-2v-7a2 2 0 0 1 2-2h3"/></svg>
|
||
<span class="float-count" id="likesCount">{{.Post.LikesCount}}</span>
|
||
</button>
|
||
<button class="float-btn float-dislike-btn" id="dislikeBtn" title="踩">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M10 15v4a3 3 0 0 0 3 3l4-9V2H5.72a2 2 0 0 0-2 1.7l-1.38 9a2 2 0 0 0 2 2.3H10zM17 2h2.67A2.31 2.31 0 0 1 22 4v7a2.31 2.31 0 0 1-2.33 2H17"/></svg>
|
||
</button>
|
||
{{if .IsLoggedIn}}
|
||
<button class="float-btn float-energize-btn" id="energizeBtn" data-id="{{.Post.ID}}" data-uid="{{$.UID}}" data-author-uid="{{.Post.UserID}}" title="赋能">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"/></svg>
|
||
</button>
|
||
<button class="float-btn float-fav-btn" id="favBtn" title="收藏">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M19 21l-7-5-7 5V5a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2z"/></svg>
|
||
<span class="float-count" id="favCount">{{.Post.FavoritesCount}}</span>
|
||
</button>
|
||
{{else}}
|
||
<button class="float-btn float-energize-btn" onclick="location.href='/auth/login?redirect=/posts/{{.Post.ID}}'" title="赋能">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"/></svg>
|
||
</button>
|
||
<button class="float-btn float-fav-btn" onclick="location.href='/auth/login?redirect=/posts/{{.Post.ID}}'" title="收藏">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M19 21l-7-5-7 5V5a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2z"/></svg>
|
||
<span class="float-count">0</span>
|
||
</button>
|
||
{{end}}
|
||
<button class="float-btn float-comment-btn" id="floatCommentBtn" title="评论">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/></svg>
|
||
</button>
|
||
<button class="float-btn float-top-btn" id="floatTopBtn" title="回顶部" style="display:none">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="18 15 12 9 6 15"/></svg>
|
||
</button>
|
||
</div>
|
||
|
||
{{if .IsLoggedIn}}
|
||
<div class="post-actions">
|
||
{{if and $.Post (or (eq $.Post.UserID $.UID) $.CanAccessAdmin) (ne $.Post.Status "pending") (not $.Post.IsLocked)}}
|
||
<a href="/studio/write?id={{$.Post.ID}}" class="btn btn-secondary">编辑</a>
|
||
{{end}}
|
||
{{if and $.Post (eq $.Post.UserID $.UID) (or (eq $.Post.Status "draft") (eq $.Post.Status "rejected")) (not $.Post.IsLocked)}}
|
||
<button class="btn btn-primary" id="submitAuditBtn" data-id="{{$.Post.ID}}">提交审核</button>
|
||
{{end}}
|
||
</div>
|
||
|
||
<!-- 赋能弹窗 -->
|
||
<div class="energize-overlay" id="energizeOverlay" style="display:none">
|
||
<div class="energize-modal">
|
||
<div class="energize-modal-header">
|
||
<h3>为这篇文章注入能量</h3>
|
||
<button class="energize-close" id="energizeClose">×</button>
|
||
</div>
|
||
<div class="energize-modal-body">
|
||
<p class="energize-hint" id="energizeCapHint" style="display:none">
|
||
今日赋能经验奖励已达上限,继续赋能将消耗域能,对方仍会获得激励。
|
||
</p>
|
||
<div class="energize-options">
|
||
<button class="energize-option-btn" id="energizeLight" data-amount="10">
|
||
<span class="energize-option-title">轻赋</span>
|
||
<span class="energize-option-cost">消耗 1 域能</span>
|
||
<span class="energize-option-gain">获得 10 经验</span>
|
||
</button>
|
||
<button class="energize-option-btn" id="energizeHeavy" data-amount="20">
|
||
<span class="energize-option-title">重赋</span>
|
||
<span class="energize-option-cost">消耗 2 域能</span>
|
||
<span class="energize-option-gain">获得 20 经验</span>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 选择收藏夹弹窗 -->
|
||
<div class="fav-overlay" id="favOverlay" style="display:none">
|
||
<div class="fav-modal">
|
||
<div class="fav-modal-header">
|
||
<h3>选择收藏夹</h3>
|
||
<button class="fav-close" id="favClose">×</button>
|
||
</div>
|
||
<div class="fav-modal-body">
|
||
<div class="fav-folder-list" id="favFolderCheckList">
|
||
<div class="fav-loading">加载中...</div>
|
||
</div>
|
||
<div class="fav-new-folder">
|
||
<input type="text" class="fav-new-input" id="favNewInput" placeholder="新建收藏夹..." maxlength="50">
|
||
<button class="fav-new-btn" id="favNewConfirm">创建并收藏</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
{{end}}
|
||
|
||
<!-- 评论区 -->
|
||
{{if .Post.AllowComment}}
|
||
<section class="comments-section" id="comments">
|
||
<h3 class="comments-title">评论 <span class="comments-count" id="commentsTotal">0</span></h3>
|
||
|
||
{{if .IsLoggedIn}}
|
||
<div class="comment-form">
|
||
<textarea class="comment-input" id="commentInput" placeholder="写下你的评论..." rows="3" maxlength="5000"></textarea>
|
||
<div class="comment-form-actions">
|
||
<label class="comment-upload-btn" id="commentUploadLabel">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="16" height="16"><rect x="3" y="3" width="18" height="18" rx="2" ry="2"/><circle cx="8.5" cy="8.5" r="1.5"/><polyline points="21 15 16 10 5 21"/></svg>
|
||
图片
|
||
</label>
|
||
<input type="file" id="commentImageInput" accept="image/*" style="display:none" />
|
||
<button class="btn btn-primary comment-submit-btn" id="commentSubmitBtn">发表评论</button>
|
||
</div>
|
||
<div class="comment-upload-preview" id="commentUploadPreview"></div>
|
||
</div>
|
||
{{else}}
|
||
<div class="comment-login-hint">
|
||
<a href="/auth/login?redirect=/posts/{{$.Post.ID}}">登录</a> 后参与评论
|
||
</div>
|
||
{{end}}
|
||
|
||
<div class="comments-list" id="commentsList"></div>
|
||
<div class="comments-loading" id="commentsLoading" style="display:none">加载中...</div>
|
||
<button class="btn btn-secondary comments-load-more" id="commentsLoadMore" style="display:none">加载更多</button>
|
||
</section>
|
||
{{end}}
|
||
|
||
<!-- @ 提及下拉 -->
|
||
<div class="mention-dropdown" id="mentionDropdown" style="display:none"></div>
|
||
</div>
|
||
|
||
{{template "layout/footer.html" .}}
|
||
|
||
{{/* MD 内容安全传递给 JS */}}
|
||
<textarea id="postMdContent" style="display:none">{{.Post.Body}}</textarea>
|
||
|
||
<link rel="stylesheet" href="/static/vditor/dist/index.css?v={{assetV "/static/vditor/dist/index.css"}}">
|
||
<script src="/static/vditor/dist/method.min.js?v={{assetV "/static/vditor/dist/method.min.js"}}" nonce="{{.CSPNonce}}"></script>
|
||
<script src="/static/js/shortcode.js?v={{assetV "/static/js/shortcode.js"}}" nonce="{{.CSPNonce}}"></script>
|
||
<script src="/static/js/post-view.js?v={{assetV "/static/js/post-view.js"}}" nonce="{{.CSPNonce}}"></script>
|
||
<script src="/static/js/post-energize.js?v={{assetV "/static/js/post-energize.js"}}" nonce="{{.CSPNonce}}"></script>
|
||
<script src="/static/js/post-reactions.js?v={{assetV "/static/js/post-reactions.js"}}" nonce="{{.CSPNonce}}"></script>
|
||
<script src="/static/js/post-favorite.js?v={{assetV "/static/js/post-favorite.js"}}" nonce="{{.CSPNonce}}"></script>
|
||
<script src="/static/js/post-comments.js?v={{assetV "/static/js/post-comments.js"}}" nonce="{{.CSPNonce}}"></script>
|
||
<script nonce="{{.CSPNonce}}">
|
||
(function() {
|
||
var topBtn = document.getElementById('floatTopBtn');
|
||
var commentBtn = document.getElementById('floatCommentBtn');
|
||
var comments = document.getElementById('comments');
|
||
var nav = document.querySelector('nav');
|
||
var navHeight = nav ? nav.offsetHeight : 60;
|
||
|
||
// 回顶部:页面顶部时隐藏按钮
|
||
function toggleTopBtn() {
|
||
if (!topBtn) return;
|
||
if (window.scrollY > 300) {
|
||
topBtn.style.display = '';
|
||
} else {
|
||
topBtn.style.display = 'none';
|
||
}
|
||
}
|
||
|
||
window.addEventListener('scroll', toggleTopBtn);
|
||
toggleTopBtn();
|
||
|
||
if (topBtn) {
|
||
topBtn.addEventListener('click', function() {
|
||
window.scrollTo({ top: 0, behavior: 'smooth' });
|
||
});
|
||
}
|
||
|
||
// 评论按钮:滚动到评论区域顶部(NAV 下方)
|
||
if (commentBtn && comments) {
|
||
commentBtn.addEventListener('click', function() {
|
||
var rect = comments.getBoundingClientRect();
|
||
var scrollTop = window.scrollY + rect.top - navHeight - 12;
|
||
window.scrollTo({ top: scrollTop, behavior: 'smooth' });
|
||
});
|
||
}
|
||
})();
|
||
</script>
|
||
</body>
|
||
</html>
|