feat: 文章详情页右下角悬浮操作栏 + 作者自赋能拦截

- 赞/踩/赋能/收藏/评论/回顶部移至右下角悬浮操作栏,固定定位
- 回顶部按钮页面顶部隐藏,滚动后显示,平滑滚动
- 评论按钮点击定位到评论区(NAV 下方)
- 赋能按钮作者可见但禁止自赋能,点击弹出 toast 提示
- 未登录用户赋能/收藏点击跳转登录页
- postReactions 引用统一改为 postFloatBar
This commit is contained in:
2026-06-02 23:45:51 +08:00
parent b74ce2da93
commit 0af6f7c971
6 changed files with 169 additions and 16 deletions

View File

@ -36,18 +36,37 @@
<div class="post-detail-body" id="postContent"></div>
</article>
<!-- 赞/踩/收藏按钮 -->
<div class="post-reactions" id="postReactions" data-post-id="{{.Post.ID}}">
<button class="reaction-btn reaction-like-btn" id="likeBtn" title="点赞">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="20" height="20"><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="reaction-like-count" id="likesCount">{{.Post.LikesCount}}</span>
<!-- 右下角悬浮操作栏 -->
<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="reaction-btn reaction-dislike-btn" id="dislikeBtn" title="踩">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="20" height="20"><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 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>
<button class="reaction-btn reaction-fav-btn" id="favBtn" title="收藏">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="20" height="20"><path d="M19 21l-7-5-7 5V5a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2z"/></svg>
<span class="reaction-like-count" id="favCount">{{.Post.FavoritesCount}}</span>
{{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>
@ -59,9 +78,6 @@
{{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}}
{{if and $.Post (ne $.Post.UserID $.UID)}}
<button class="btn btn-primary" id="energizeBtn" data-id="{{$.Post.ID}}">赋能</button>
{{end}}
</div>
<!-- 赋能弹窗 -->
@ -158,5 +174,42 @@
<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>