Files
mce/templates/MetaLab-2026/html/posts/show.html
Victor_Jay 0af6f7c971 feat: 文章详情页右下角悬浮操作栏 + 作者自赋能拦截
- 赞/踩/赋能/收藏/评论/回顶部移至右下角悬浮操作栏,固定定位
- 回顶部按钮页面顶部隐藏,滚动后显示,平滑滚动
- 评论按钮点击定位到评论区(NAV 下方)
- 赋能按钮作者可见但禁止自赋能,点击弹出 toast 提示
- 未登录用户赋能/收藏点击跳转登录页
- postReactions 引用统一改为 postFloatBar
2026-06-02 23:45:51 +08:00

216 lines
11 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.

{{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 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>
</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">&times;</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">&times;</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>