261 lines
7.8 KiB
JavaScript
261 lines
7.8 KiB
JavaScript
// 播放页评论:只读列表 + 「查看更多评论」追加翻页
|
||
(function () {
|
||
var commentsRoot = document.querySelector("[data-play-comments]");
|
||
if (!commentsRoot) return;
|
||
|
||
var listEl = document.querySelector("[data-comment-list]");
|
||
var totalEl = document.querySelector("[data-comment-total-label]");
|
||
var moreBtn = document.querySelector("[data-comment-more]");
|
||
var loadingMore = false;
|
||
var PAGE_SIZE = 4;
|
||
|
||
function toast(message, type) {
|
||
if (typeof window.showToast === "function") {
|
||
window.showToast(message, type);
|
||
return;
|
||
}
|
||
}
|
||
|
||
function toInt(v, fallback) {
|
||
var n = parseInt(v, 10);
|
||
return isNaN(n) ? fallback || 0 : n;
|
||
}
|
||
|
||
function escapeHtml(s) {
|
||
return String(s == null ? "" : s)
|
||
.replace(/&/g, "&")
|
||
.replace(/</g, "<")
|
||
.replace(/>/g, ">")
|
||
.replace(/"/g, """);
|
||
}
|
||
|
||
function getPageSize() {
|
||
return Math.max(
|
||
1,
|
||
toInt(commentsRoot.getAttribute("data-comment-size"), PAGE_SIZE)
|
||
);
|
||
}
|
||
|
||
function getPage() {
|
||
return Math.max(
|
||
1,
|
||
toInt(commentsRoot.getAttribute("data-comment-page"), 1)
|
||
);
|
||
}
|
||
|
||
function getLoaded() {
|
||
if (!listEl) return 0;
|
||
return listEl.querySelectorAll(".play-comment:not(.play-comment--empty)").length;
|
||
}
|
||
|
||
function setPagerState(page, loaded, total) {
|
||
if (page != null) commentsRoot.setAttribute("data-comment-page", String(page));
|
||
if (loaded != null) commentsRoot.setAttribute("data-comment-loaded", String(loaded));
|
||
if (total != null) commentsRoot.setAttribute("data-comment-total", String(total));
|
||
syncMoreButton(total != null ? total : toInt(commentsRoot.getAttribute("data-comment-total"), 0));
|
||
}
|
||
|
||
function syncMoreButton(total) {
|
||
if (!moreBtn) return;
|
||
var loaded = getLoaded();
|
||
var hasMore = total > loaded;
|
||
if (hasMore) {
|
||
moreBtn.hidden = false;
|
||
moreBtn.removeAttribute("hidden");
|
||
} else {
|
||
moreBtn.hidden = true;
|
||
moreBtn.setAttribute("hidden", "");
|
||
}
|
||
}
|
||
|
||
function formatTime(raw) {
|
||
var s = String(raw || "").trim();
|
||
if (!s) return "刚刚";
|
||
var d = new Date(s);
|
||
if (isNaN(d.getTime())) return s;
|
||
return (
|
||
d.getFullYear() +
|
||
"/" +
|
||
(d.getMonth() + 1) +
|
||
"/" +
|
||
d.getDate() +
|
||
" " +
|
||
String(d.getHours()).padStart(2, "0") +
|
||
":" +
|
||
String(d.getMinutes()).padStart(2, "0")
|
||
);
|
||
}
|
||
|
||
function renderCommentItem(item) {
|
||
item = item || {};
|
||
var avatar = String(item.userAvatar || "").trim();
|
||
var name = escapeHtml(item.userName || "匿名用户");
|
||
var text = escapeHtml(item.text || "");
|
||
var time = escapeHtml(formatTime(item.createdAt));
|
||
var like = toInt(item.countLike, 0);
|
||
var avatarHtml = avatar
|
||
? '<img class="play-comment__avatar lazy" src="/static/images/placeholder.png?v=2" data-src="' +
|
||
escapeHtml(avatar) +
|
||
'" alt="" loading="lazy" decoding="async">'
|
||
: '<img class="play-comment__avatar" src="/static/images/placeholder.png?v=2" data-src="/static/images/profile/avatar.png" alt="" loading="lazy" decoding="async">';
|
||
var children = Array.isArray(item.children) ? item.children : [];
|
||
var replies = children
|
||
.map(function (child) {
|
||
var cName = escapeHtml(child.userName || "用户");
|
||
var replyTo = child.replyUserName
|
||
? " 回复 " + escapeHtml(child.replyUserName)
|
||
: "";
|
||
return (
|
||
'<div class="play-comment__reply">' +
|
||
cName +
|
||
replyTo +
|
||
":" +
|
||
escapeHtml(child.text || "") +
|
||
"</div>"
|
||
);
|
||
})
|
||
.join("");
|
||
var commentId = escapeHtml(item.id || "");
|
||
return (
|
||
'<li class="play-comment" data-comment-id="' +
|
||
commentId +
|
||
'">' +
|
||
avatarHtml +
|
||
'<div class="play-comment__body">' +
|
||
'<div class="play-comment__meta">' +
|
||
'<span class="play-comment__name">' +
|
||
name +
|
||
"</span>" +
|
||
'<span class="play-comment__time">' +
|
||
time +
|
||
"</span>" +
|
||
"</div>" +
|
||
'<p class="play-comment__text">' +
|
||
text +
|
||
"</p>" +
|
||
replies +
|
||
'<div class="play-comment__actions">' +
|
||
'<span class="play-comment__action">' +
|
||
'<img class="icon" src="/static/images/placeholder.png?v=2" data-src="/static/images/heart.svg" alt="" loading="lazy" decoding="async">' +
|
||
"<span>" +
|
||
escapeHtml(like) +
|
||
"</span>" +
|
||
"</span>" +
|
||
"</div></div></li>"
|
||
);
|
||
}
|
||
|
||
function getLoadedIds() {
|
||
var ids = Object.create(null);
|
||
if (!listEl) return ids;
|
||
listEl.querySelectorAll(".play-comment[data-comment-id]").forEach(function (el) {
|
||
var id = String(el.getAttribute("data-comment-id") || "").trim();
|
||
if (id) ids[id] = true;
|
||
});
|
||
return ids;
|
||
}
|
||
|
||
function updateTotal(n) {
|
||
if (!totalEl) return;
|
||
totalEl.textContent = n > 0 ? n + " 条" : "暂无评论";
|
||
}
|
||
|
||
// 下一页追加到现有列表底部,不替换已展示评论。
|
||
function appendList(list, totalCount) {
|
||
if (!listEl) return;
|
||
list = Array.isArray(list) ? list : [];
|
||
var total =
|
||
typeof totalCount === "number"
|
||
? totalCount
|
||
: toInt(commentsRoot.getAttribute("data-comment-total"), 0);
|
||
if (typeof totalCount === "number") {
|
||
updateTotal(totalCount);
|
||
}
|
||
if (!list.length) {
|
||
setPagerState(getPage(), getLoaded(), total);
|
||
return;
|
||
}
|
||
|
||
var empty = listEl.querySelector(".play-comment--empty");
|
||
if (empty) empty.remove();
|
||
|
||
var existing = getLoadedIds();
|
||
var fresh = list.filter(function (item) {
|
||
var id = String((item && item.id) || "").trim();
|
||
return id && !existing[id];
|
||
});
|
||
if (!fresh.length) {
|
||
setPagerState(getPage(), getLoaded(), total);
|
||
return;
|
||
}
|
||
|
||
var wrap = document.createElement("ul");
|
||
wrap.innerHTML = fresh.map(renderCommentItem).join("");
|
||
var nodes = [];
|
||
while (wrap.firstChild) {
|
||
nodes.push(wrap.firstChild);
|
||
listEl.appendChild(wrap.firstChild);
|
||
}
|
||
if (typeof window.initLazyImages === "function") {
|
||
nodes.forEach(function (node) {
|
||
if (node && node.nodeType === 1) window.initLazyImages(node);
|
||
});
|
||
window.initLazyImages(listEl);
|
||
}
|
||
setPagerState(getPage(), getLoaded(), total);
|
||
}
|
||
|
||
function fetchCommentPage(pageNum) {
|
||
if (!window.HttpRequest || typeof window.HttpRequest.get !== "function") {
|
||
return Promise.reject(new Error("评论服务未就绪"));
|
||
}
|
||
var objectId = toInt(commentsRoot.getAttribute("data-object-id"), 0);
|
||
var objectType = toInt(commentsRoot.getAttribute("data-object-type"), 10);
|
||
if (objectId <= 0) return Promise.reject(new Error("缺少评论对象"));
|
||
var pageSize = getPageSize();
|
||
return window.HttpRequest.get("/api/comment/list", {
|
||
params: {
|
||
objectId: objectId,
|
||
objectType: objectType,
|
||
pageNum: pageNum,
|
||
pageSize: pageSize,
|
||
sortOrder: 1,
|
||
},
|
||
}).then(function (res) {
|
||
if (!res || Number(res.code) !== 200 || !res.data) {
|
||
throw new Error((res && (res.tip || res.msg)) || "加载失败");
|
||
}
|
||
return res.data;
|
||
});
|
||
}
|
||
|
||
function loadMoreComments() {
|
||
if (loadingMore || !moreBtn) return;
|
||
var nextPage = getPage() + 1;
|
||
loadingMore = true;
|
||
moreBtn.disabled = true;
|
||
fetchCommentPage(nextPage)
|
||
.then(function (data) {
|
||
var list = data.list || [];
|
||
var total = toInt(data.totalCount, 0);
|
||
commentsRoot.setAttribute("data-comment-page", String(nextPage));
|
||
// 只追加,绝不 renderList 整表替换
|
||
appendList(list, total);
|
||
})
|
||
.catch(function (err) {
|
||
toast((err && err.message) || "加载失败,请稍后重试", "error");
|
||
})
|
||
.finally(function () {
|
||
loadingMore = false;
|
||
if (moreBtn) moreBtn.disabled = false;
|
||
});
|
||
}
|
||
|
||
if (moreBtn) {
|
||
moreBtn.addEventListener("click", function (event) {
|
||
event.preventDefault();
|
||
loadMoreComments();
|
||
});
|
||
}
|
||
})();
|