620 lines
21 KiB
HTML
620 lines
21 KiB
HTML
<!doctype html>
|
||
<html lang="zh-CN">
|
||
<head>
|
||
<meta charset="UTF-8" />
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||
<title id="page-title">彩先知 - 免费文章详情</title>
|
||
<script src="../public/css/layui/layui.js"></script>
|
||
<script src="../config.js"></script>
|
||
<script src="../utils/ApiClient.js"></script>
|
||
<script src="../utils/DateUtil.js"></script>
|
||
<script src="../utils/CommonUtil.js"></script>
|
||
<link rel="stylesheet" href="../public/css/layui/css/layui.css" />
|
||
<link rel="stylesheet" href="../public/css/public.css" />
|
||
<link rel="stylesheet" href="../public/css/mianfei.css" />
|
||
</head>
|
||
<body>
|
||
<!-- Header -->
|
||
<nav class="topbar" id="navbar">
|
||
<div class="topbar__inner">
|
||
<a href="index.html" class="topbar__brand">
|
||
<img
|
||
src="../images/home-logo.png"
|
||
alt="彩先知"
|
||
class="topbar__logo"
|
||
/>
|
||
</a>
|
||
<div class="topbar__nav" id="navLotteryItems"></div>
|
||
<div class="topbar__user">
|
||
<div id="notLoggedInBox" class="topbar__guest">
|
||
<a href="login.html" class="topbar__btn topbar__btn--ghost">登录</a>
|
||
<a href="reg.html" class="topbar__btn topbar__btn--primary"
|
||
>免费注册</a
|
||
>
|
||
</div>
|
||
<div id="loggedInBox" class="topbar__logged" style="display: none">
|
||
<a href="usercenter.html" class="topbar__profile">
|
||
<img
|
||
id="userAvatar"
|
||
src="../public/img/userimg.png"
|
||
alt=""
|
||
class="topbar__avatar"
|
||
/>
|
||
<div class="topbar__meta">
|
||
<span id="userName" class="topbar__uname">用户</span>
|
||
<span id="userLevel" class="topbar__ubadge">会员</span>
|
||
</div>
|
||
</a>
|
||
<button class="topbar__btn topbar__btn--logout" onclick="logout()">
|
||
退出登录
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</nav>
|
||
|
||
<!-- Main -->
|
||
<main class="cxz-main">
|
||
<div class="cxz-breadcrumb">
|
||
<a href="index.html">首页</a><span class="sep">></span>
|
||
<a href="mianfeilist.html?id=1">免费推荐</a><span class="sep">></span>
|
||
<span>文章详情</span>
|
||
</div>
|
||
|
||
<!-- Article Card -->
|
||
<div class="cxz-article-card">
|
||
<div class="cxz-article-header">
|
||
<h1 class="cxz-article-title" id="articleTitle">加载中...</h1>
|
||
<div class="cxz-article-meta">
|
||
<span>作者:<span class="author" id="articleAuthor">--</span></span>
|
||
<span id="articleTime">--</span>
|
||
</div>
|
||
</div>
|
||
<div class="cxz-article-body" id="articleContent">
|
||
<p style="text-align: center; color: var(--text-muted)">加载中...</p>
|
||
</div>
|
||
<div class="cxz-article-actions">
|
||
<button class="cxz-action-btn" id="btnLike" onclick="toggleLike()">
|
||
<i class="layui-icon layui-icon-praise"></i>
|
||
<span id="likeText">点赞</span>(<span id="likeCount">0</span>)
|
||
</button>
|
||
<button
|
||
class="cxz-action-btn"
|
||
id="btnCollect"
|
||
onclick="toggleCollect()"
|
||
>
|
||
<i class="layui-icon layui-icon-star-fill"></i>
|
||
<span id="collectText">收藏</span>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Prev/Next Navigation -->
|
||
<div class="cxz-article-nav">
|
||
<a class="cxz-nav-item" id="prevArticle" href="javascript:void(0)">
|
||
<div class="cxz-nav-label">上一篇</div>
|
||
<div class="cxz-nav-title" id="prevTitle">没有了</div>
|
||
</a>
|
||
<a class="cxz-nav-item" id="nextArticle" href="javascript:void(0)">
|
||
<div class="cxz-nav-label">下一篇</div>
|
||
<div class="cxz-nav-title" id="nextTitle">没有了</div>
|
||
</a>
|
||
</div>
|
||
|
||
<!-- Comment Section -->
|
||
<div class="cxz-comment-section">
|
||
<div class="cxz-comment-header">
|
||
<i class="layui-icon layui-icon-dialogue"></i>
|
||
<span>评论(<span id="commentCount">0</span>)</span>
|
||
</div>
|
||
|
||
<!-- Comment Input -->
|
||
<div class="cxz-comment-input-box">
|
||
<textarea
|
||
class="cxz-comment-textarea"
|
||
id="commentInput"
|
||
placeholder="请输入您的评论..."
|
||
></textarea>
|
||
<button class="cxz-comment-submit-btn" onclick="submitComment()">
|
||
发表评论
|
||
</button>
|
||
</div>
|
||
|
||
<!-- Comment List -->
|
||
<div class="cxz-comment-list" id="commentList">
|
||
<div class="cxz-comment-empty">
|
||
<i class="layui-icon layui-icon-face-surprised"></i>
|
||
<span>暂无评论,快来抢沙发吧~</span>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="cxz-pagination" id="commentPager"></div>
|
||
</div>
|
||
</main>
|
||
|
||
<!-- Footer -->
|
||
<footer class="site-footer">
|
||
<div class="site-footer__inner">
|
||
<img
|
||
src="../images/home-logo.png"
|
||
alt="彩先知"
|
||
class="site-footer__logo"
|
||
/>
|
||
<p id="copyright-info">彩先知以开奖数据、免费文章、资讯推荐为核心</p>
|
||
<div class="site-footer__meta">
|
||
<a
|
||
id="beian"
|
||
href="https://beian.miit.gov.cn/"
|
||
target="_blank"
|
||
style="font-size: 12px; color: rgba(255, 255, 255, 0.5)"
|
||
>工信部备案号</a
|
||
>
|
||
<span id="additional-info">本站仅供数据分析参考</span>
|
||
</div>
|
||
</div>
|
||
</footer>
|
||
<button
|
||
class="gotop"
|
||
id="backTop"
|
||
type="button"
|
||
onclick="window.scrollTo({ top: 0, behavior: 'smooth' })"
|
||
>
|
||
↑
|
||
</button>
|
||
<script>
|
||
const $ = layui.$;
|
||
const urlParams = new URLSearchParams(window.location.search);
|
||
const pathMatch = window.location.pathname.match(/\/mianfei\/([^/]+)\.html$/);
|
||
const articleId = urlParams.get("id") || (pathMatch ? decodeURIComponent(pathMatch[1]) : "");
|
||
let isLiked = false;
|
||
let isCollected = false;
|
||
let articleData = null;
|
||
let commentPage = 1;
|
||
const commentPageSize = 10;
|
||
let likeBusy = false;
|
||
let collectBusy = false;
|
||
|
||
function logout() {
|
||
localStorage.removeItem("token");
|
||
window.location.reload();
|
||
}
|
||
|
||
function checkLogin() {
|
||
var token = localStorage.getItem("token");
|
||
if (token) {
|
||
$("#notLoggedInBox").hide();
|
||
$("#loggedInBox").css("display", "flex");
|
||
CommonUtil.getCurrentUserDetail(
|
||
function (data) {
|
||
$("#userName").text(data.nickName || "用户");
|
||
$("#userLevel").text(data.isExpert === "1" ? "专家" : "会员");
|
||
if (data.avatar) $("#userAvatar").attr("src", data.avatar);
|
||
},
|
||
function () {
|
||
$("#notLoggedInBox").show();
|
||
$("#loggedInBox").hide();
|
||
},
|
||
);
|
||
}
|
||
}
|
||
|
||
function renderNav() {
|
||
ApiClient.get(ApiClient.API.homeNav).then(function (res) {
|
||
if (!ApiClient.isSuccess(res)) return;
|
||
var list = ApiClient.asArray(ApiClient.pickData(res, []));
|
||
var $nav = $("#navLotteryItems");
|
||
$nav.find(".topbar__nav-link").not(":first").remove();
|
||
$.each(list, function (i, item) {
|
||
if (item.type && item.type !== "lottery") return;
|
||
var code = item.code || item.suoxie || "";
|
||
$nav.append(
|
||
'<a href="cai.html?id=' +
|
||
code +
|
||
'" class="topbar__nav-link">' +
|
||
escapeHtml(item.name || "") +
|
||
"</a>",
|
||
);
|
||
});
|
||
});
|
||
}
|
||
|
||
$(window).on("scroll", function () {
|
||
$(window).scrollTop() > 320
|
||
? $("#backTop").addClass("is-visible")
|
||
: $("#backTop").removeClass("is-visible");
|
||
});
|
||
|
||
// Prevent prev/next navigation when no valid href
|
||
$("#prevArticle, #nextArticle").on("click", function (e) {
|
||
var href = $(this).attr("href");
|
||
if (!href || href === "javascript:void(0)" || href === "") {
|
||
e.preventDefault();
|
||
}
|
||
});
|
||
|
||
// === Load Article ===
|
||
function loadArticle() {
|
||
var token = localStorage.getItem("token");
|
||
var opts = token ? { headers: CommonUtil.authHeaders() } : {};
|
||
|
||
ApiClient.get(ApiClient.API.freeArticleDetail(articleId), null, opts)
|
||
.then(function (res) {
|
||
if (ApiClient.isSuccess(res) && ApiClient.pickData(res, null)) {
|
||
articleData = ApiClient.pickData(res, {});
|
||
renderArticle(articleData);
|
||
loadRelatedArticles();
|
||
} else {
|
||
layui.layer.msg("文章不存在或已删除", { icon: 2 });
|
||
$("#articleTitle").text("文章不存在");
|
||
$("#articleContent").html(
|
||
'<p style="text-align:center;color:var(--text-muted);">文章不存在或已删除</p>',
|
||
);
|
||
}
|
||
})
|
||
.catch(function () {
|
||
layui.layer.msg("加载失败,请稍后重试", { icon: 2 });
|
||
});
|
||
}
|
||
|
||
function renderArticle(data) {
|
||
$("#articleTitle").text(data.title || "无标题");
|
||
$("#articleAuthor").text(data.issuer || data.nickName || data.author || data.createUserName || "佚名");
|
||
$("#articleTime").text(data.updateTime || data.createTime || data.publishTime || "--");
|
||
$("#articleContent").html(scopeArticleContent(data.content || ""));
|
||
document.title = "彩先知 - " + (data.title || "免费文章详情");
|
||
|
||
renderArticleNav(data.prev || data.previous || data.prevArticle, data.next || data.nextArticle);
|
||
|
||
isLiked = isTruthyState(data.isLike ?? data.liked ?? data.isLiked);
|
||
updateLikeButton(data);
|
||
|
||
isCollected = isTruthyState(data.isCollect ?? data.collected ?? data.isCollected);
|
||
updateCollectButton();
|
||
}
|
||
|
||
function scopeArticleContent(content) {
|
||
var html = String(content || "");
|
||
if (!html.trim()) {
|
||
return '<p class="cxz-content-empty">暂无内容</p>';
|
||
}
|
||
|
||
var scopedStyles = [];
|
||
html = html.replace(/<style[^>]*>([\s\S]*?)<\/style>/gi, function (_, css) {
|
||
scopedStyles.push(prefixContentCss(css));
|
||
return "";
|
||
});
|
||
|
||
var styleHtml = scopedStyles.length
|
||
? '<style data-cxz-content-style>' + scopedStyles.join("\n") + "</style>"
|
||
: "";
|
||
return styleHtml + '<div class="cxz-content-inner">' + html + "</div>";
|
||
}
|
||
|
||
function prefixContentCss(css) {
|
||
return String(css || "").replace(/(^|})(\s*)([^@{}][^{}]*)\{/g, function (match, close, space, selectorText) {
|
||
var selectors = selectorText
|
||
.split(",")
|
||
.map(function (selector) {
|
||
selector = selector.trim();
|
||
if (!selector) return "";
|
||
if (
|
||
selector.indexOf("#articleContent") === 0 ||
|
||
selector.indexOf(".cxz-article-body") === 0
|
||
) {
|
||
return selector;
|
||
}
|
||
if (/^(html|body)$/i.test(selector)) return "#articleContent";
|
||
if (/^(html|body)\b/i.test(selector)) {
|
||
return selector.replace(/^(html|body)\b/i, "#articleContent");
|
||
}
|
||
return "#articleContent " + selector;
|
||
})
|
||
.filter(Boolean)
|
||
.join(", ");
|
||
return close + space + selectors + " {";
|
||
});
|
||
}
|
||
|
||
function isTruthyState(value) {
|
||
return (
|
||
value === true ||
|
||
value === 1 ||
|
||
value === "1" ||
|
||
value === 0 ||
|
||
value === "0"
|
||
);
|
||
}
|
||
|
||
function articleLinkOf(item) {
|
||
var id = item && (item.id || item.articleId || item.freeArticleId);
|
||
return CommonUtil.freeArticleHref(id);
|
||
}
|
||
|
||
function applyNavItem($item, $title, item, emptyText) {
|
||
if (item && (item.id || item.articleId || item.freeArticleId)) {
|
||
$item.attr("href", articleLinkOf(item)).removeClass("is-disabled").css("opacity", "");
|
||
$title.text(item.title || emptyText);
|
||
return;
|
||
}
|
||
$item.attr("href", "javascript:void(0)").addClass("is-disabled").css("opacity", "");
|
||
$title.text("没有了");
|
||
}
|
||
|
||
function renderArticleNav(prev, next) {
|
||
applyNavItem($("#prevArticle"), $("#prevTitle"), prev, "上一篇");
|
||
applyNavItem($("#nextArticle"), $("#nextTitle"), next, "下一篇");
|
||
}
|
||
|
||
function loadRelatedArticles() {
|
||
ApiClient.get(ApiClient.API.freeArticleRelated(articleId))
|
||
.then(function (res) {
|
||
if (!ApiClient.isSuccess(res)) return;
|
||
var data = ApiClient.pickData(res, {}) || {};
|
||
var prev = data.prev || data.previous || data.prevArticle;
|
||
var next = data.next || data.nextArticle;
|
||
var rows = ApiClient.asArray(data);
|
||
if (!prev && rows[0]) prev = rows[0];
|
||
if (!next && rows[1]) next = rows[1];
|
||
if (prev || next) renderArticleNav(prev, next);
|
||
})
|
||
.catch(function () {});
|
||
}
|
||
|
||
function updateLikeButton(data) {
|
||
var count = data.likeCount || 0;
|
||
$("#likeCount").text(count);
|
||
if (isLiked) {
|
||
$("#btnLike").addClass("liked");
|
||
$("#likeText").text("已点赞");
|
||
} else {
|
||
$("#btnLike").removeClass("liked");
|
||
$("#likeText").text("点赞");
|
||
}
|
||
}
|
||
|
||
function updateCollectButton() {
|
||
if (isCollected) {
|
||
$("#btnCollect").addClass("collected");
|
||
$("#collectText").text("已收藏");
|
||
} else {
|
||
$("#btnCollect").removeClass("collected");
|
||
$("#collectText").text("收藏");
|
||
}
|
||
}
|
||
|
||
// === Like ===
|
||
function toggleLike() {
|
||
if (likeBusy) return;
|
||
var token = localStorage.getItem("token");
|
||
if (!token) {
|
||
layui.layer.confirm(
|
||
"点赞需要先登录,是否前往登录?",
|
||
{ icon: 3, title: "提示" },
|
||
function (index) {
|
||
layui.layer.close(index);
|
||
window.location.href = "login.html";
|
||
},
|
||
);
|
||
return;
|
||
}
|
||
var wasLiked = isLiked;
|
||
likeBusy = true;
|
||
$("#btnLike").prop("disabled", true);
|
||
ApiClient.post(ApiClient.API.freeArticleLike(articleId), {}, { headers: CommonUtil.authHeaders() })
|
||
.then(function (res) {
|
||
if (ApiClient.isSuccess(res)) {
|
||
loadArticle();
|
||
layui.layer.msg(wasLiked ? "已取消点赞" : "点赞成功", {
|
||
icon: 1,
|
||
time: 1500,
|
||
});
|
||
} else {
|
||
layui.layer.msg(res.msg || "操作失败", { icon: 2 });
|
||
}
|
||
})
|
||
.finally(function () {
|
||
likeBusy = false;
|
||
$("#btnLike").prop("disabled", false);
|
||
});
|
||
}
|
||
|
||
// === Collect ===
|
||
function toggleCollect() {
|
||
if (collectBusy) return;
|
||
var token = localStorage.getItem("token");
|
||
if (!token) {
|
||
layui.layer.confirm(
|
||
"收藏需要先登录,是否前往登录?",
|
||
{ icon: 3, title: "提示" },
|
||
function (index) {
|
||
layui.layer.close(index);
|
||
window.location.href = "login.html";
|
||
},
|
||
);
|
||
return;
|
||
}
|
||
var wasCollected = isCollected;
|
||
collectBusy = true;
|
||
$("#btnCollect").prop("disabled", true);
|
||
ApiClient.post(ApiClient.API.freeArticleCollect(articleId), {}, { headers: CommonUtil.authHeaders() })
|
||
.then(function (res) {
|
||
if (ApiClient.isSuccess(res)) {
|
||
loadArticle();
|
||
layui.layer.msg(wasCollected ? "已取消收藏" : "收藏成功", {
|
||
icon: 1,
|
||
time: 1500,
|
||
});
|
||
} else {
|
||
layui.layer.msg(res.msg || "操作失败", { icon: 2 });
|
||
}
|
||
})
|
||
.finally(function () {
|
||
collectBusy = false;
|
||
$("#btnCollect").prop("disabled", false);
|
||
});
|
||
}
|
||
|
||
// === Comments ===
|
||
function loadComments(pageNum) {
|
||
if (pageNum) commentPage = pageNum;
|
||
ApiClient.get(ApiClient.API.freeArticleComments(articleId), {
|
||
pageNum: commentPage,
|
||
pageSize: commentPageSize,
|
||
})
|
||
.then(function (res) {
|
||
if (ApiClient.isSuccess(res) && ApiClient.pickData(res, null)) {
|
||
var data = ApiClient.pickData(res, {}) || {};
|
||
var list = ApiClient.asArray(data);
|
||
var total = data.totalRow || data.total || data.count || list.length || 0;
|
||
renderComments(list);
|
||
$("#commentCount").text(total);
|
||
renderCommentPager(total);
|
||
} else {
|
||
renderCommentEmpty();
|
||
}
|
||
})
|
||
.catch(function () {
|
||
renderCommentEmpty("加载失败");
|
||
});
|
||
}
|
||
|
||
function renderComments(list) {
|
||
var $container = $("#commentList");
|
||
$container.empty();
|
||
if (!list || list.length === 0) {
|
||
renderCommentEmpty();
|
||
return;
|
||
}
|
||
$.each(list, function (i, item) {
|
||
var avatarHtml;
|
||
if (item.avatar) {
|
||
avatarHtml =
|
||
'<img class="cxz-comment-avatar" src="' +
|
||
escapeHtml(item.avatar) +
|
||
'" />';
|
||
} else {
|
||
var initial = (item.nickName || item.nickname || item.userName || "匿").charAt(0);
|
||
avatarHtml =
|
||
'<div class="cxz-comment-avatar-placeholder">' +
|
||
escapeHtml(initial) +
|
||
"</div>";
|
||
}
|
||
var name = escapeHtml(item.nickName || item.nickname || item.userName || "匿名用户");
|
||
var text = escapeHtml(item.comment || item.content || "");
|
||
var time = item.createTime || item.commentTime || item.updateTime || "--";
|
||
if (time && time.length > 16) time = time.substring(0, 16);
|
||
|
||
$container.append(
|
||
'<div class="cxz-comment-item">' +
|
||
avatarHtml +
|
||
'<div class="cxz-comment-body">' +
|
||
'<div class="cxz-comment-name">' +
|
||
name +
|
||
"</div>" +
|
||
'<div class="cxz-comment-text">' +
|
||
text +
|
||
"</div>" +
|
||
'<div class="cxz-comment-time">' +
|
||
time +
|
||
"</div>" +
|
||
"</div>" +
|
||
"</div>",
|
||
);
|
||
});
|
||
}
|
||
|
||
function renderCommentEmpty(msg) {
|
||
$("#commentList").html(
|
||
'<div class="cxz-comment-empty">' +
|
||
'<i class="layui-icon layui-icon-face-surprised"></i>' +
|
||
"<span>" +
|
||
(msg || "暂无评论,快来抢沙发吧~") +
|
||
"</span>" +
|
||
"</div>",
|
||
);
|
||
}
|
||
|
||
function renderCommentPager(total) {
|
||
if (total <= 0) {
|
||
$("#commentPager").empty();
|
||
return;
|
||
}
|
||
layui.laypage.render({
|
||
elem: "commentPager",
|
||
count: total,
|
||
limit: commentPageSize,
|
||
curr: commentPage,
|
||
jump: function (obj, first) {
|
||
if (!first) {
|
||
commentPage = obj.curr;
|
||
loadComments();
|
||
}
|
||
},
|
||
});
|
||
}
|
||
|
||
function submitComment() {
|
||
var content = $("#commentInput").val().trim();
|
||
if (!content) {
|
||
layui.layer.msg("请输入评论内容", { icon: 2 });
|
||
return;
|
||
}
|
||
var token = localStorage.getItem("token");
|
||
if (!token) {
|
||
layui.layer.confirm(
|
||
"发表评论需要先登录,是否前往登录?",
|
||
{ icon: 3, title: "提示" },
|
||
function (index) {
|
||
layui.layer.close(index);
|
||
window.location.href = "login.html";
|
||
},
|
||
);
|
||
return;
|
||
}
|
||
|
||
var $btn = $(".cxz-comment-submit-btn");
|
||
$btn.text("提交中...").prop("disabled", true);
|
||
|
||
ApiClient.post(ApiClient.API.freeArticleComment(articleId), { comment: content }, { headers: CommonUtil.authHeaders() })
|
||
.then(function (res) {
|
||
if (ApiClient.isSuccess(res)) {
|
||
layui.layer.msg("评论成功", { icon: 1, time: 1500 });
|
||
$("#commentInput").val("");
|
||
commentPage = 1;
|
||
loadComments();
|
||
} else {
|
||
layui.layer.msg(res.msg || "评论失败", { icon: 2 });
|
||
}
|
||
$btn.text("发表评论").prop("disabled", false);
|
||
})
|
||
.catch(function () {
|
||
layui.layer.msg("网络错误,请重试", { icon: 2 });
|
||
$btn.text("发表评论").prop("disabled", false);
|
||
});
|
||
}
|
||
|
||
function escapeHtml(str) {
|
||
if (!str) return "";
|
||
return String(str)
|
||
.replace(/&/g, "&")
|
||
.replace(/</g, "<")
|
||
.replace(/>/g, ">")
|
||
.replace(/"/g, """);
|
||
}
|
||
|
||
// === Init ===
|
||
$(function () {
|
||
checkLogin();
|
||
renderNav();
|
||
CommonUtil.loadPageConfig();
|
||
if (articleId) {
|
||
loadArticle();
|
||
loadComments();
|
||
} else {
|
||
layui.layer.msg("文章ID不存在", { icon: 2 });
|
||
}
|
||
});
|
||
</script>
|
||
</body>
|
||
</html>
|