Files
2026-08-14 16:07:32 +08:00

2136 lines
75 KiB
HTML
Raw Permalink 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.
<!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/layout-fix.css" />
<link rel="stylesheet" href="../public/css/caiinfo.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">
<a href="index.html" class="topbar__nav-link">首页</a>
</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="javascript:history.back();" class="cxz-breadcrumb-back" id="breadcrumbBack">
<i class="layui-icon layui-icon-left"></i>
<span>返回上一页</span>
</a>
<div class="cxz-breadcrumb-trail" id="breadcrumbTrail">
<a href="index.html">首页</a><span class="sep">></span>
<a href="cai.html?id=" id="breadcrumbCai">预测推荐</a
><span class="sep">></span>
<span id="breadcrumbExpert">专家详情</span>
</div>
</div>
<!-- Expert Card -->
<div class="cxz-expert-card">
<img
id="expertAvatar"
class="cxz-expert-avatar"
src="../public/img/userimg.png"
alt="专家头像"
/>
<div class="cxz-expert-info">
<div class="cxz-expert-name" id="expertName">专家</div>
<div class="cxz-expert-desc" id="expertDesc">专业彩票分析师</div>
<div class="cxz-expert-stats">
<div class="cxz-stat-item">
<div class="cxz-stat-num" id="statTotal">0</div>
<div class="cxz-stat-label">总方案</div>
</div>
<div class="cxz-stat-item">
<div class="cxz-stat-num" id="statHit">0</div>
<div class="cxz-stat-label">命中</div>
</div>
<div class="cxz-stat-item">
<div class="cxz-stat-num" id="statRate">0%</div>
<div class="cxz-stat-label">命中率</div>
</div>
</div>
</div>
<button
class="cxz-btn-collect"
id="btnCollectExpert"
onclick="toggleExpertCollect()"
>
<i class="layui-icon layui-icon-star-fill"></i> 收藏专家
</button>
</div>
<!-- Article Table -->
<div class="cxz-table-box">
<div class="cxz-table-header">
<div class="cxz-table-title">预测方案列表</div>
</div>
<table class="cxz-data-table">
<thead>
<tr>
<th>期号</th>
<th>开奖号码</th>
<th>预测号码</th>
<th>状态</th>
<th>操作</th>
</tr>
</thead>
<tbody id="articleBody">
<tr>
<td
colspan="5"
style="
text-align: center;
color: var(--text-muted);
padding: 40px;
"
>
加载中...
</td>
</tr>
</tbody>
</table>
<div class="cxz-pagination" id="pagination"></div>
</div>
</main>
<!-- Buy Dialog -->
<div class="cxz-dialog-overlay" id="buyDialog">
<div class="cxz-dialog-box">
<div class="cxz-dialog-header">
<div class="cxz-dialog-title">购买方案</div>
<button class="cxz-dialog-close" onclick="closeBuyDialog()">
&times;
</button>
</div>
<div class="cxz-dialog-body">
<div class="cxz-pay-hero">
<div class="cxz-pay-hero-label">应付金额</div>
<div class="cxz-pay-hero-price">¥ <span id="payFinalAmount">--</span></div>
<div class="cxz-pay-hero-sub">
原价 ¥ <span id="payOriginalAmount">--</span>
<span class="cxz-pay-deduct">积分抵扣 ¥ <span id="pointDeductAmount">0.00</span></span>
</div>
</div>
<div class="cxz-pay-section-title">选择支付余额</div>
<div class="cxz-pay-wallets">
<button
class="cxz-pay-option selected"
data-type="account"
type="button"
onclick="selectPayType(this)"
>
<span class="cxz-pay-radio"></span>
<span class="cxz-pay-info">
<span class="cxz-pay-name">账户余额</span>
<span class="cxz-pay-desc">通用余额</span>
</span>
<span class="cxz-pay-balance">¥ <span id="accountBalance">0.00</span></span>
</button>
<button
class="cxz-pay-option"
data-type="sales"
type="button"
onclick="selectPayType(this)"
>
<span class="cxz-pay-radio"></span>
<span class="cxz-pay-info">
<span class="cxz-pay-name">分销余额</span>
<span class="cxz-pay-desc">推广收益</span>
</span>
<span class="cxz-pay-balance">¥ <span id="distributionBalance">0.00</span></span>
</button>
<button
class="cxz-pay-option"
data-type="expert"
type="button"
onclick="selectPayType(this)"
>
<span class="cxz-pay-radio"></span>
<span class="cxz-pay-info">
<span class="cxz-pay-name">专家余额</span>
<span class="cxz-pay-desc">专家佣金</span>
</span>
<span class="cxz-pay-balance">¥ <span id="expertBalance">0.00</span></span>
</button>
</div>
<label class="cxz-point-card">
<span class="cxz-point-main">
<input type="checkbox" id="usePoint" />
<span>
<span class="cxz-point-title">积分抵扣</span>
<span class="cxz-point-desc" id="pointBalance">可用积分 --</span>
</span>
</span>
<span class="cxz-point-value">- ¥ <span id="pointDeductPreview">0.00</span></span>
</label>
<div class="cxz-pay-total-card">
<div>
<span id="selectedWalletLabel">账户余额</span>
<em id="payBalanceHint">余额 ¥ <span id="selectedWalletBalance">0.00</span></em>
</div>
<strong>实付 ¥ <span id="payFooterAmount">--</span></strong>
</div>
</div>
<div class="cxz-dialog-footer">
<button class="cxz-dialog-confirm" onclick="confirmBuy()">
确认购买
</button>
</div>
</div>
</div>
<!-- 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>
window.__cxzCaiinfoDisableCompat = true;
const $ = layui.$;
const urlParams = new URLSearchParams(window.location.search);
const createBy = urlParams.get("createBy") || urlParams.get("expertId") || "";
const code = urlParams.get("code") || urlParams.get("id") || "";
const parentId =
urlParams.get("parentId") ||
urlParams.get("menuId") ||
urlParams.get("typeId") ||
"";
const accountType = urlParams.get("accountType") || "1";
const purchasedArticleId =
urlParams.get("payArticleId") ||
urlParams.get("articleId") ||
urlParams.get("contentId") ||
"";
let currentArticleId = "";
let currentPurchaseMode = "purchase";
let currentPrePurchaseItem = null;
let selectedPayType = "account";
let currentPayAmount = 0;
let currentPointDeductLimit = 0;
let currentWalletOverview = {};
let expertCollected = false;
let currentArticleList = [];
let currentMenuMeta = { lottery: null, parent: null, current: null };
const purchasedArticleIds = new Set();
const purchasedArticleKeys = new Set();
function logout() {
localStorage.removeItem("token");
window.location.reload();
}
function checkLogin() {
const 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 loadBasicConfig() {
Promise.all([
ApiClient.get(ApiClient.API.webConfig).catch(function () {
return { data: {} };
}),
ApiClient.get(ApiClient.API.configCopyright).catch(function () {
return { data: [] };
}),
ApiClient.get(ApiClient.API.configSite).catch(function () {
return { data: {} };
}),
]).then(function (results) {
if (!CommonUtil.applyWebConfigs) return;
CommonUtil.applyWebConfigs(ApiClient.pickData(results[0], {}) || {});
CommonUtil.applyCopyrightConfigs(ApiClient.pickData(results[1], []) || []);
CommonUtil.applyWebConfigs(ApiClient.pickData(results[2], {}) || {});
});
}
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 itemCode = item.code || item.suoxie || "";
var itemName = CommonUtil.escapeHtml
? CommonUtil.escapeHtml(item.name || "")
: item.name || "";
$nav.append(
'<a href="cai.html?id=' +
itemCode +
'" class="topbar__nav-link">' +
itemName +
"</a>",
);
});
});
}
function findMenuMeta(menus) {
let lottery = null;
let parent = null;
let current = null;
const targetCode = String(code || "").toLowerCase();
const targetMenuId = String(parentId || "");
function walk(items, parentNode, rootNode) {
$.each(items || [], function (_, item) {
const root = rootNode || item;
const itemCode = String(item.code || item.suoxie || "").toLowerCase();
const itemId = String(item.id || item.menuId || "");
if (targetCode && itemCode === targetCode) {
lottery = root;
}
if (targetMenuId && itemId === targetMenuId) {
lottery = root;
current = item;
parent = parentNode && parentNode !== root ? parentNode : null;
}
if (item.children && item.children.length) {
walk(item.children, item, root);
}
});
}
walk(menus || [], null, null);
return { lottery: lottery, parent: parent, current: current };
}
function loadBreadcrumbMenuMeta() {
return ApiClient.loadMenuCompat()
.then(function (res) {
const menus = ApiClient.asArray(ApiClient.pickData(res, []));
currentMenuMeta = findMenuMeta(menus);
return currentMenuMeta;
})
.catch(function () {
currentMenuMeta = { lottery: null, parent: null, current: null };
return currentMenuMeta;
});
}
function getReturnListHref() {
const referrer = document.referrer || "";
if (referrer && referrer.indexOf(location.origin) === 0) {
return referrer;
}
return buildCaiListUrl();
}
function updateBreadcrumb(expertName) {
const lottery = currentMenuMeta.lottery || {};
const parent = currentMenuMeta.parent || {};
const current = currentMenuMeta.current || {};
const lotteryName = lottery.name || lottery.title || code || "预测推荐";
const lotteryCode = lottery.code || lottery.suoxie || code || "";
const listHref = buildCaiListUrl(lotteryCode);
const items = [
{ text: "首页", href: "index.html" },
{ text: lotteryName, href: listHref },
];
if (parent.name) {
items.push({ text: parent.name, href: listHref });
}
if (current.name && current.name !== parent.name) {
items.push({ text: current.name, href: listHref });
}
items.push({ text: expertName || "专家详情" });
const html = items
.map(function (item, index) {
const text = CommonUtil.escapeHtml(item.text || "");
const node = item.href
? '<a href="' + CommonUtil.escapeHtml(item.href) + '">' + text + "</a>"
: '<span id="breadcrumbExpert">' + text + "</span>";
return index === 0 ? node : '<span class="sep">&gt;</span>' + node;
})
.join("");
$("#breadcrumbTrail").html(html);
$("#breadcrumbBack").attr("href", getReturnListHref());
}
function getArticleId(item) {
return String(
(item &&
(item.id ||
item.articleId ||
item.payArticleId ||
item.latestArticleId ||
item.contentId ||
item.recordId)) ||
"",
);
}
function getArticleRecommend(item) {
return (
(item &&
(item.predictedCode ||
item.recommendCode ||
item.latestRecommend ||
item.recommend ||
item.content)) ||
""
);
}
function getArticleOpenCode(item) {
return (
(item &&
(item.openCode ||
item.lotteryResult ||
item.resultCode ||
item.openResult)) ||
""
);
}
function getArticleIssue(item) {
return (
(item &&
(item.issue || item.latestIssue || item.expect || item.period)) ||
""
);
}
function formatIssueText(issue) {
const value = String(issue || "").trim();
return /^\d{7,}$/.test(value) ? value.slice(-3) : value;
}
function getArticleExpertId(item) {
return String((item && (item.expertId || item.createBy || item.accountId)) || createBy || "");
}
function getArticleMenuIdFromMenus(item) {
const menus = ApiClient.asArray(item && item.menus);
const menu =
menus.find(function (entry) {
return entry && entry.type !== "lottery" && (entry.id || entry.menuId);
}) ||
menus.find(function (entry) {
return entry && (entry.id || entry.menuId);
});
return menu ? String(menu.id || menu.menuId || "") : "";
}
function getArticleMenuId(item) {
return String(
(item &&
(item.menuId ||
item.playMenuId ||
item.typeMenuId ||
item.parentId ||
item.typeId ||
getArticleMenuIdFromMenus(item))) ||
parentId ||
"",
);
}
function getArticlePrice(item) {
const value =
item && (item.price ?? item.points ?? item.amount ?? item.coin ?? 0);
const price = Number(value);
return Number.isFinite(price) ? price.toFixed(2) : "0.00";
}
function getArticlePointDeductLimit(item) {
const value =
item &&
(item.pointDeductionAmount ??
item.pointDeductAmount ??
item.pointsDeductionAmount ??
item.deductionAmount ??
0);
return parseMoney(value);
}
function getArticlePurchaseKey(item) {
const keys = getArticlePurchaseKeys(item);
return keys[0] || "";
}
function getArticlePurchaseKeys(item) {
if (!item) return [];
const expert = item.expertId || item.createBy || createBy;
const lottery = item.code || item.suoxie || code;
const menu = getArticleMenuId(item);
const issue = getArticleIssue(item);
if (!expert || !issue) return [];
const normalized = function (parts) {
return parts.map(function (value) {
return String(value || "");
}).join("|");
};
return [
normalized([expert, lottery, menu, issue]),
normalized([expert, lottery, issue]),
normalized([expert, issue]),
];
}
function rememberPurchasedArticle(item, fallbackId) {
const articleId = getArticleId(item) || fallbackId;
if (articleId) purchasedArticleIds.add(String(articleId));
getArticlePurchaseKeys(item).forEach(function (key) {
if (key) purchasedArticleKeys.add(key);
});
}
function rememberPurchasedRecords(records) {
ApiClient.asArray(records).forEach(function (item) {
rememberPurchasedArticle(item, item && (item.recordId || item.articleId || item.contentId));
});
}
function loadPurchasedRecordsForCurrentExpert() {
if (!ApiClient.getToken()) return Promise.resolve();
return ApiClient.get(
ApiClient.API.minePurchasedPayArticleList,
{
pageNum: 1,
pageSize: 200,
},
{ headers: CommonUtil.authHeaders() },
)
.then(function (res) {
if (!ApiClient.isSuccess(res)) return;
const data = ApiClient.pickData(res, {}) || {};
const rows = ApiClient.asArray(data);
rememberPurchasedRecords(rows);
})
.catch(function () {});
}
$(window).on("scroll", function () {
$(window).scrollTop() > 320
? $("#backTop").addClass("is-visible")
: $("#backTop").removeClass("is-visible");
});
// Load expert articles
function loadArticles() {
if (purchasedArticleId && !createBy) {
loadPurchasedArticleDetail(purchasedArticleId);
return;
}
if (!createBy) {
showTableMessage("参数错误,请从专家列表页面进入");
return;
}
const params = {
expertId: createBy,
menuId: parentId,
accountType: accountType,
pageNum: 1,
pageSize: 50,
};
Promise.all([
ApiClient.get(ApiClient.API.payArticleExpertProfile, {
expertId: params.expertId,
accountType: params.accountType,
}).catch(function () {
return { code: 500, data: {} };
}),
ApiClient.get(ApiClient.API.payArticleExpertList, params).catch(function (error) {
return { code: 500, msg: error.message || "获取专家预测详情失败", data: [] };
}),
loadPurchasedRecordsForCurrentExpert(),
loadBreadcrumbMenuMeta(),
]).then(function ([profileRes, listRes]) {
const profile = ApiClient.pickData(profileRes, {}) || {};
const listData = ApiClient.pickData(listRes, []) || [];
const list = ApiClient.asArray(listData);
updateExpertInfo({
...profile,
expertId: createBy,
accountType: accountType,
});
if (listRes.code === 0 || listRes.code === 200 || listRes.success === true) {
currentArticleList = list;
renderArticles(list);
updateStats(list, listData);
} else {
showTableMessage(listRes.msg || "获取专家预测列表失败");
}
});
}
function loadPurchasedArticleDetail(articleId) {
CommonUtil.requireAuth(function () {
ApiClient.get(ApiClient.API.minePayArticleDetail(articleId), null, {
headers: CommonUtil.authHeaders(),
})
.then(function (res) {
if (!ApiClient.isSuccess(res)) {
showTableMessage(res.msg || "获取已购文章失败");
return;
}
const article = ApiClient.pickData(res, {}) || {};
rememberPurchasedArticle(article, articleId);
updateExpertInfo(article);
currentArticleList = [article];
renderArticles(currentArticleList);
updateStats(currentArticleList);
})
.catch(function (error) {
showTableMessage(error.message || "获取已购文章失败");
});
});
}
function updateExpertInfo(data) {
const avatar = data.avatar || data.headImg || data.accountAvatar || data.expertAvatar;
const name =
data.nickname ||
data.nickName ||
data.expertName ||
data.createUserName ||
data.createByName ||
data.accountName ||
"专家";
if (avatar) $("#expertAvatar").attr("src", avatar);
$("#expertName").text(name);
updateBreadcrumb(name || "专家详情");
expertCollected = isExpertCollected(data);
applyExpertCollectState(expertCollected);
}
function showTableMessage(message) {
$("#articleBody").html(
'<tr><td colspan="5" style="text-align:center;color:var(--text-muted);padding:40px;">' +
CommonUtil.escapeHtml(message || "暂无数据") +
"</td></tr>",
);
}
function renderArticles(list) {
const $body = $("#articleBody");
$body.empty();
if (!list || list.length === 0) {
$body.html(
'<tr><td colspan="5" style="text-align:center;color:var(--text-muted);padding:40px;">暂无数据</td></tr>',
);
return;
}
$.each(list, function (i, item) {
const articleId = getArticleId(item);
const openCode = getArticleOpenCode(item);
const predictedCode = getArticleRecommend(item);
const hasDrawResult = isArticleDrawn(item, openCode);
const isLatestIssue = isLatestArticle(item, list);
const price = Number(getArticlePrice(item));
const unlockedByVisibleRecommend =
isLatestIssue &&
!hasDrawResult &&
price > 0 &&
!isArticleMasked(item) &&
predictedCode &&
!isMaskedRecommendText(predictedCode);
const purchased = isArticlePurchased(item) || unlockedByVisibleRecommend;
const canShowRecommend = canShowArticleRecommend(
item,
isLatestIssue,
hasDrawResult,
unlockedByVisibleRecommend,
);
const articleStatus = getArticleStatus(item);
let recommendOptions = null;
let recommendText = "";
let openCodeHtml = renderOpenCodeBalls(openCode, item);
let predictHtml = "";
const pendingPublish = isPendingPublishArticle(item, articleStatus, predictedCode);
if (pendingPublish) {
predictHtml = '<span class="cxz-pending-publish-text">' + getPendingPublishText() + '</span>';
} else if (canShowRecommend && predictedCode) {
recommendOptions = getRecommendRenderOptions(item, articleStatus.text);
recommendText = formatRecommendText(predictedCode, Infinity, recommendOptions);
predictHtml =
'<span class="cxz-recommend-wrap"><span class="cxz-recommend-content"></span>' +
'<button class="cxz-btn-copy" onclick="event.stopPropagation(); copyCode(\'' +
escapeJs(recommendText) +
"')\">复制</button></span>";
} else if (predictedCode) {
predictHtml =
'<span style="color:var(--text-muted);">' +
getRecommendLockedText() +
"</span>";
} else {
predictHtml =
'<span style="color:var(--text-muted);">暂无</span>';
}
let statusText =
'<span style="' +
articleStatus.style +
'">' +
(articleStatus.html || CommonUtil.escapeHtml(articleStatus.text)) +
"</span>";
let actionHtml = "";
if (isArticlePrePurchased(item)) {
actionHtml = '<span class="cxz-prebuy-badge">已预购</span>';
} else if (shouldShowPrePurchaseButton(item, purchased)) {
actionHtml =
'<button class="cxz-btn-prebuy" onclick="openPrePurchaseDialog(' +
i +
')">购买</button> ';
} else if (shouldShowBuyButton(item, isLatestIssue, hasDrawResult, purchased) && articleId) {
actionHtml =
'<button class="cxz-btn-buy" onclick="openBuyDialog(\'' +
articleId +
"')\">购买</button> ";
} else if (purchased) {
actionHtml = '<span style="color:#27ae60;margin-right:8px;">已购买</span>';
}
const isCollected = isPayArticleCollected(item);
actionHtml +=
'<button class="cxz-btn-article-collect' +
(isCollected ? " collected" : "") +
'" data-id="' +
articleId +
'" onclick="toggleArticleCollect(this, \'' +
articleId +
"')\">" +
(isCollected ? "已收藏" : "收藏") +
"</button>";
$body.append(
"<tr>" +
"<td>" +
CommonUtil.escapeHtml(formatIssueText(getArticleIssue(item)) || "--") +
"</td>" +
"<td>" +
openCodeHtml +
"</td>" +
"<td>" +
predictHtml +
"</td>" +
"<td>" +
statusText +
"</td>" +
"<td>" +
actionHtml +
"</td>" +
"</tr>",
);
if (canShowRecommend && predictedCode && recommendOptions) {
const $recommendCell = $body.children("tr").last().find(".cxz-recommend-content");
const isFolded = fitRecommendCell($recommendCell, predictedCode, recommendOptions);
if (isFolded) {
$recommendCell
.addClass("recommend-clickable")
.on("click", function () {
showFullRecommend(predictedCode, recommendOptions);
});
}
}
});
}
function getExplicitStatusText(item) {
const textValue =
item &&
(item.resultText ||
item.statusText ||
item.drawStatusText ||
item.openStatusText ||
(typeof item.result === "string" && !isNumericStatusValue(item.result)
? item.result
: "") ||
(typeof item.status === "string" && !isNumericStatusValue(item.status)
? item.status
: ""));
return String(
textValue || "",
).trim();
}
function isNumericStatusValue(value) {
if (value === null || value === undefined || value === "") return false;
return /^-?\d+(?:\.\d+)?$/.test(String(value).trim());
}
function getNumericArticleStatus(item) {
if (!item || !isNumericStatusValue(item.status)) return null;
const value = Number(item.status);
return Number.isFinite(value) ? value : null;
}
function isWinStatusText(text) {
return /全对|正确|命中|中奖|中/.test(String(text || ""));
}
function isLoseStatusText(text) {
return /全错|错误|错/.test(String(text || ""));
}
function isTruthyFlag(value) {
return (
value === true ||
value === 1 ||
value === "1" ||
String(value).toLowerCase() === "true"
);
}
function isArticlePrePurchase(item) {
return Boolean(
item &&
(isTruthyFlag(item.prePurchase) ||
isTruthyFlag(item.pre_purchase) ||
isTruthyFlag(item.canPrePurchase) ||
isTruthyFlag(item.preOrder)),
);
}
function isArticlePrePurchased(item) {
return Boolean(
item &&
(isTruthyFlag(item.subscribed) ||
isTruthyFlag(item.prePurchased) ||
isTruthyFlag(item.pre_purchased) ||
isTruthyFlag(item.prepaid)),
);
}
function isPendingPublishText(value) {
const text = String(value || "").trim();
return text === "待发布" || text === getPendingPublishText() || text.indexOf("发布中") >= 0;
}
function getPendingPublishText() {
return "号码发布中请稍等";
}
function isPendingPublishArticle(item, articleStatus, recommend) {
const statusText = String(
getExplicitStatusText(item) ||
(articleStatus && articleStatus.text) ||
"",
).trim();
return Boolean(
isPendingPublishText(recommend) ||
isPendingPublishText(statusText) ||
(isArticlePrePurchase(item) && !getArticleRecommend(item)),
);
}
function isCodeResultsAllCorrect(item) {
const list = ApiClient.asArray(item && item.codeResults);
return list.length > 0 && list.every(function (code) {
return code && code.correct === true;
});
}
function isArticleHit(item) {
const displayStatus = getArticleStatus(item);
if (displayStatus.style && displayStatus.style.indexOf("#8c8c8c") >= 0) return false;
if (displayStatus.style && displayStatus.style.indexOf("#de2103") >= 0) return true;
const statusText = getExplicitStatusText(item);
const resultValue = item && (item.result ?? item.winStatus);
if (isWinStatusText(statusText)) return true;
if (isLoseStatusText(statusText) || /待|未开奖/.test(statusText)) return false;
if (resultValue === 1 || resultValue === "1" || resultValue === true) return true;
if (resultValue === 0 || resultValue === "0" || resultValue === false) return false;
return isCodeResultsAllCorrect(item);
}
function isArticleDrawn(item, openCode) {
if (openCode && String(openCode).trim()) return true;
const statusText = getExplicitStatusText(item);
if (
/正确|错误|已开奖|开奖号|开奖/.test(statusText) &&
!/未开奖|待开奖/.test(statusText)
) {
return true;
}
if (isWinStatusText(statusText) || isLoseStatusText(statusText)) return true;
return Boolean(
item.isDrawn === true ||
item.drawn === true ||
item.opened === true ||
item.openStatus === 1 ||
item.openStatus === "1" ||
item.drawStatus === 1 ||
item.drawStatus === "1",
);
}
function isLatestArticle(item, list) {
const currentId = getArticleId(item);
const lastId = getArticleId(list && list[0]);
if (currentId && lastId) return currentId === lastId;
const currentIssue = Number(getArticleIssue(item));
const issues = ApiClient.asArray(list)
.map(function (row) {
return Number(getArticleIssue(row));
})
.filter(function (issue) {
return Number.isFinite(issue);
});
return (
issues.length > 0 &&
Number.isFinite(currentIssue) &&
currentIssue === Math.max.apply(null, issues)
);
}
function isArticleMasked(item) {
const recommend = getArticleRecommend(item);
return (
item.masked === true ||
item.masked === 1 ||
item.masked === "1" ||
item.masked === "true" ||
isMaskedRecommendText(recommend)
);
}
function isMaskedRecommendText(recommend) {
const text = String(recommend || "").trim();
return !text ||
/^[*\s]+$/.test(text) ||
text.indexOf("购买后可见") >= 0 ||
isPendingPublishText(text);
}
function isArticlePurchased(item) {
if (!item) return false;
const articleId = getArticleId(item);
if (articleId && purchasedArticleIds.has(String(articleId))) return true;
const purchasedByKey = getArticlePurchaseKeys(item).some(function (key) {
return purchasedArticleKeys.has(key);
});
if (purchasedByKey) return true;
return (
item.isBuy === 1 ||
item.isBuy === 2 ||
item.isBuy === true ||
item.isBuy === "1" ||
item.isBuy === "2" ||
item.buyStatus === 1 ||
item.buyStatus === "1" ||
item.purchased === true ||
item.purchased === 1 ||
item.purchased === "1" ||
item.isPurchased === true ||
item.isPurchased === 1 ||
item.isPurchased === "1" ||
item.bought === true ||
item.bought === 1 ||
item.bought === "1" ||
item.unlocked === true ||
item.unlocked === 1 ||
item.unlocked === "1" ||
item.canView === true ||
item.canView === 1 ||
item.canView === "1" ||
item.payStatus === 1 ||
item.payStatus === "1" ||
item.subscribed === true ||
item.subscribed === 1 ||
item.subscribed === "1" ||
item.subscribed === "true" ||
item.subscribeStatus === 1 ||
item.subscribeStatus === "1"
);
}
function canShowArticleRecommend(
item,
isLatestIssue,
hasDrawResult,
unlockedByVisibleRecommend,
) {
if (!isArticleMasked(item)) return true;
if (hasDrawResult) return true;
if (!isLatestIssue) return true;
return Boolean(
ApiClient.getToken() &&
(isArticlePurchased(item) || unlockedByVisibleRecommend),
);
}
function getRecommendLockedText() {
return ApiClient.getToken() ? "购买后可见" : "登录后购买可见";
}
function shouldShowBuyButton(item, isLatestIssue, hasDrawResult, purchased) {
return Boolean(
isLatestIssue &&
!hasDrawResult &&
isArticleMasked(item) &&
!purchased,
);
}
function shouldShowPrePurchaseButton(item, purchased) {
return Boolean(
!purchased &&
!isArticlePrePurchased(item) &&
!getArticleId(item),
);
}
function getRecommendRenderOptions(item, statusText) {
const options = getRecommendFormatOptions(item);
return applyRecommendHitOptions(options, item, statusText || getArticleStatus(item).text);
}
function getRecommendFormatOptions(item) {
return {
padSmallNumbers: isSsqLottery(item),
blueGroupsAfterFirst: isFrontBackLottery(item),
allHitsBlue: isBlueBallRecommend(item),
};
}
function buildRecommendHtml(recommend, limit, options) {
const nums = splitRecommendValues(recommend);
const contentHtml = buildRecommendContentHtml(recommend, limit, options || {});
const more =
Number.isFinite(limit) && nums.length > limit
? '<span class="recommend-view-all">点击查看全部</span>'
: "";
if (more) {
return (
'<span class="fushi234item"><span class="recommend-preview-text">' +
contentHtml +
"</span>" +
more +
"</span>"
);
}
return (
'<span class="fushi234item">' +
(contentHtml || CommonUtil.escapeHtml(formatRecommendText(recommend, limit, options || {}))) +
"</span>"
);
}
function fitRecommendCell($cell, recommend, options) {
const nums = splitRecommendValues(recommend);
const previewLimit = 4;
if (nums.length <= previewLimit) {
$cell.html(buildRecommendHtml(recommend, Infinity, options || {}));
return false;
}
$cell.html(buildRecommendHtml(recommend, Infinity, options || {}));
const cellEl = $cell.get(0);
const contentEl = $cell.find(".fushi234item").get(0);
if (cellEl && contentEl && contentEl.scrollWidth <= cellEl.clientWidth) {
return false;
}
$cell.html(buildRecommendHtml(recommend, previewLimit, options || {}));
return true;
}
function showFullRecommend(recommend, options) {
const fullText = formatRecommendText(recommend, Infinity, options || {});
if (!fullText) return;
layui.layer.open({
title: "完整推荐号码",
area: ["520px", "auto"],
skin: "recommend-number-layer",
btn: ["确定"],
content:
'<div class="recommend-number-dialog">' +
'<div class="recommend-number-content">' +
buildRecommendHtml(recommend, Infinity, options || {}) +
"</div>" +
'<div class="recommend-number-actions">' +
'<button class="recommend-dialog-copy" type="button">复制全部</button>' +
"</div>" +
"</div>",
});
setTimeout(function () {
$(".recommend-dialog-copy")
.off("click")
.on("click", function () {
copyCode(fullText);
});
}, 0);
}
function buildRecommendContentHtml(recommend, limit, options) {
const groups = parseRecommendGroups(recommend);
if (!groups.length) return "";
const rawGroupSeparator = getRecommendGroupSeparator(recommend);
let remain = Number.isFinite(limit) ? limit : Infinity;
const groupHtml = [];
groups.forEach(function (group, groupIndex) {
if (remain <= 0) return;
const visibleGroup = Number.isFinite(remain) ? group.slice(0, remain) : group;
if (!visibleGroup.length) return;
groupHtml.push(
visibleGroup
.map(function (num, numberIndex) {
const text = formatRecommendNumber(num, options || {});
const classNames = ["recommend-number-text"];
const resultMark = getRecommendCodeResultMark(options || {}, groupIndex, numberIndex);
const isMissed = isRecommendMissed(num, options || {}, groupIndex);
const isHit = isRecommendHit(num, options || {}, groupIndex);
const isBlueNumber = isRecommendBlueNumber(options || {}, groupIndex, rawGroupSeparator);
const isBlueHit = isRecommendBlueHitNumber(options || {}, groupIndex, rawGroupSeparator);
if (isHit) {
classNames.push(isBlueNumber || isBlueHit ? "recommend-number-blue" : "recommend-number-hit");
} else if (resultMark === false) {
classNames.push("recommend-number-missed");
} else if (resultMark === true) {
classNames.push(isBlueHit ? "recommend-number-blue" : "recommend-number-hit");
} else if (isMissed) {
classNames.push("recommend-number-missed");
} else if (isBlueNumber) {
classNames.push("recommend-number-blue");
}
return (
'<span class="' +
classNames.join(" ") +
'">' +
CommonUtil.escapeHtml(text) +
"</span>"
);
})
.join('<span class="recommend-separator">,</span>'),
);
remain -= visibleGroup.length;
});
return groupHtml.join(
'<span class="recommend-separator">' +
CommonUtil.escapeHtml(rawGroupSeparator) +
"</span>",
);
}
function parseRecommendGroups(recommend) {
if (Array.isArray(recommend)) return [splitRecommendValues(recommend)];
const text = String(recommend || "").trim();
if (!text) return [];
const groupSeparator = getRecommendGroupSeparator(text);
return text
.replace(//g, ",")
.replace(/、/g, ",")
.split(groupSeparator)
.map(function (group) {
return splitRecommendValues(group);
})
.filter(function (group) {
return group.length > 0;
});
}
function splitRecommendValues(value) {
if (Array.isArray(value)) {
return value
.flatMap(function (item) {
return splitRecommendValues(
item && typeof item === "object"
? item.code || item.value || item.number || item.result || ""
: item,
);
})
.filter(Boolean);
}
return String(value || "")
.split(/[,+,、\s/|]+/)
.map(function (item) {
return item.trim();
})
.filter(Boolean);
}
function getRecommendGroupSeparator(recommend) {
return String(recommend || "").indexOf("|") >= 0 ? "|" : "+";
}
function formatRecommendText(recommend, limit, options) {
const groups = parseRecommendGroups(recommend);
if (!groups.length) return "";
let remain = Number.isFinite(limit) ? limit : Infinity;
const formattedGroups = [];
groups.forEach(function (group) {
if (remain <= 0) return;
const visibleGroup = Number.isFinite(remain) ? group.slice(0, remain) : group;
if (!visibleGroup.length) return;
formattedGroups.push(
visibleGroup
.map(function (num) {
return formatRecommendNumber(num, options || {});
})
.join(","),
);
remain -= visibleGroup.length;
});
return formattedGroups.join(getRecommendGroupSeparator(recommend));
}
function formatRecommendNumber(num, options) {
const text = String(num || "").trim();
if (options && options.padSmallNumbers && /^\d$/.test(text)) return "0" + text;
return text;
}
function normalizeRecommendNumberKey(value) {
const text = String(value || "").trim();
return /^\d+$/.test(text) ? String(Number(text)) : text;
}
function isRecommendBlueNumber(options, groupIndex, groupSeparator) {
return Boolean(
options &&
options.blueGroupsAfterFirst &&
groupIndex > 0 &&
groupSeparator === "+",
);
}
function isRecommendBlueHitNumber(options, groupIndex, groupSeparator) {
return Boolean(
options &&
(options.allHitsBlue ||
(options.blueGroupsAfterFirst &&
groupIndex > 0 &&
groupSeparator === "+")),
);
}
function getRecommendCodeResultMark(options, groupIndex, numberIndex) {
if (!options || !options.useCodeResultMarks) return null;
if (
Array.isArray(options.codeResultGroupMarks) &&
options.codeResultGroupMarks[groupIndex] &&
options.codeResultGroupMarks[groupIndex][numberIndex] !== undefined
) {
return options.codeResultGroupMarks[groupIndex][numberIndex];
}
return null;
}
function isRecommendMissed(num, options, groupIndex) {
if (!options || !options.markMissedNumbers || !options.missedNumberSet) return false;
const key = normalizeRecommendNumberKey(num);
if (
Array.isArray(options.missedGroupSets) &&
options.missedGroupSets[groupIndex]
) {
return options.missedGroupSets[groupIndex].has(key);
}
return options.missedNumberSet.has(key);
}
function isRecommendHit(num, options, groupIndex) {
if (!options || !options.markHitNumbers || !options.hitNumberSet) return false;
const key = normalizeRecommendNumberKey(num);
if (Array.isArray(options.hitGroupSets) && options.hitGroupSets[groupIndex]) {
return options.hitGroupSets[groupIndex].has(key);
}
return options.hitNumberSet.has(key);
}
function applyRecommendHitOptions(options, item, statusText) {
const recommendGroups = parseRecommendGroups(getArticleRecommend(item));
const codeResultGroupMarks = getCodeResultCorrectGroups(item, recommendGroups);
const hitGroups = getArticleHitMarkerGroups(item, recommendGroups);
const missedGroups = getArticleMissedMarkerGroups(item, recommendGroups);
let hitNumbers = hitGroups.length ? hitGroups.flat() : getArticleHitNumbers(item);
let missedNumbers = missedGroups.length ? missedGroups.flat() : getArticleMissedNumbers(item);
const isFinalStatus = isWinStatusText(statusText) || isLoseStatusText(statusText);
if (codeResultGroupMarks.length) {
options.useCodeResultMarks = true;
options.codeResultGroupMarks = codeResultGroupMarks;
options.markMissedNumbers = false;
options.markHitNumbers = hitNumbers.length > 0 && isFinalStatus;
options.hitNumberSet = new Set(hitNumbers.map(normalizeRecommendNumberKey));
options.hitGroupSets =
hitGroups.length === recommendGroups.length
? hitGroups.map(function (group) {
return new Set(group.map(normalizeRecommendNumberKey));
})
: null;
return options;
}
options.markHitNumbers = hitNumbers.length > 0 && isFinalStatus;
options.hitNumberSet = new Set(hitNumbers.map(normalizeRecommendNumberKey));
options.hitGroupSets =
hitGroups.length === recommendGroups.length
? hitGroups.map(function (group) {
return new Set(group.map(normalizeRecommendNumberKey));
})
: null;
options.markMissedNumbers = missedNumbers.length > 0 && isFinalStatus;
options.missedNumberSet = new Set(missedNumbers.map(normalizeRecommendNumberKey));
options.missedGroupSets =
missedGroups.length === recommendGroups.length
? missedGroups.map(function (group) {
return new Set(group.map(normalizeRecommendNumberKey));
})
: null;
return options;
}
function getCodeResultCorrectGroups(item, recommendGroups) {
const codeResults = ApiClient.asArray(item && item.codeResults);
if (!codeResults.length || !recommendGroups || !recommendGroups.length) return [];
const marks = codeResults.map(function (result) {
if (!result || typeof result !== "object") return null;
return result.correct === true ? true : result.correct === false ? false : null;
});
if (!marks.some(function (mark) { return mark === true || mark === false; })) return [];
const recommendCount = recommendGroups.reduce(function (total, group) {
return total + group.length;
}, 0);
if (marks.length < recommendCount) return [];
let offset = 0;
return recommendGroups.map(function (group) {
const groupMarks = marks.slice(offset, offset + group.length);
offset += group.length;
return groupMarks;
});
}
function getOpenCodeHitMarkGroups(item, recommendGroups) {
if (!recommendGroups || !recommendGroups.length) return [];
const openValues = splitRecommendValues(getArticleOpenCode(item));
if (!openValues.length) return [];
if (recommendGroups.length > 1 && openValues.length === recommendGroups.length) {
return recommendGroups.map(function (group, groupIndex) {
const openKey = normalizeRecommendNumberKey(openValues[groupIndex]);
return group.map(function (value) {
return normalizeRecommendNumberKey(value) === openKey;
});
});
}
const openValueSet = new Set(openValues.map(normalizeRecommendNumberKey));
return recommendGroups.map(function (group) {
return group.map(function (value) {
return openValueSet.has(normalizeRecommendNumberKey(value));
});
});
}
function getArticleMissedMarkerGroups(item, recommendGroups) {
if (!recommendGroups || recommendGroups.length <= 1) return [];
const missedNumbers = getArticleMissedNumbers(item);
if (missedNumbers.length === recommendGroups.length) {
return missedNumbers.map(function (num) {
return splitRecommendValues(num);
});
}
const codeResultGroups = getCodeResultMarkerGroups(item, false);
if (codeResultGroups.length === recommendGroups.length) return codeResultGroups;
return [];
}
function getArticleHitMarkerGroups(item, recommendGroups) {
if (!recommendGroups || !recommendGroups.length) return [];
const hitNumbers = getArticleHitNumbers(item);
if (hitNumbers.length === recommendGroups.length) {
return hitNumbers.map(function (num) {
return splitRecommendValues(num);
});
}
const codeResultGroups = getCodeResultMarkerGroups(item, true);
if (codeResultGroups.length === recommendGroups.length) return codeResultGroups;
return [];
}
function getCodeResultMarkerGroups(item, correctValue) {
const codeResults = ApiClient.asArray(item && item.codeResults);
if (!codeResults.length) return [];
return codeResults
.map(function (result) {
if (Array.isArray(result)) return [];
if (result && typeof result === "object" && result.correct !== correctValue) return [];
const value =
result && typeof result === "object"
? result.code || result.value || result.number || result.result || ""
: result;
return String(value || "").trim() ? [String(value).trim()] : [];
})
.filter(function (group) {
return group.length > 0;
});
}
function getArticleHitNumbers(item) {
const fields = [
"hitCodes",
"hitCode",
"hitNumbers",
"hits",
"rightCodes",
"rightCode",
"rightNumbers",
"correctCodes",
"correctCode",
"correctNumbers",
];
const hitNumbers = getArticleCodeNumbers(item, fields);
if (hitNumbers.length) return hitNumbers;
return getCodeResultNumbers(item, true);
}
function getArticleMissedNumbers(item) {
const fields = [
"missCodes",
"missCode",
"missNumbers",
"missedCodes",
"missedCode",
"missedNumbers",
"wrongCodes",
"wrongCode",
"wrongNumbers",
"errorCodes",
"errorCode",
"errorNumbers",
];
const missedNumbers = getArticleCodeNumbers(item, fields);
if (missedNumbers.length) return missedNumbers;
return getCodeResultNumbers(item, false);
}
function getArticleCodeNumbers(item, fields) {
let value = "";
fields.some(function (field) {
if (item && item[field] !== undefined && item[field] !== null && item[field] !== "") {
value = item[field];
return true;
}
return false;
});
return splitRecommendValues(value);
}
function getCodeResultNumbers(item, correctValue) {
return ApiClient.asArray(item && item.codeResults)
.filter(function (result) {
return result && typeof result === "object" && result.correct === correctValue;
})
.flatMap(function (result) {
return splitRecommendValues(result.code || result.value || result.number || result.result || "");
});
}
function getArticleStatus(item) {
const statusText = getExplicitStatusText(item);
const numericStatus = getNumericArticleStatus(item);
const resultValue = item && (item.result ?? item.winStatus);
let text = statusText || "待开奖";
let style = "color:#8c8c8c;";
if (isPendingPublishText(text)) {
text = "待开奖";
}
if (isWinStatusText(text)) {
style = "color:#de2103;";
}
if (isLoseStatusText(text)) {
text = text === "错误" ? "错" : text;
style = "color:#8c8c8c;";
}
if (!statusText && isCodeResultsAllCorrect(item)) {
text = "全对";
style = "color:#de2103;";
}
if (
!statusText &&
numericStatus !== null
) {
text = "中" + numericStatus;
style = "color:#de2103;";
}
if (
!statusText &&
numericStatus === null &&
(resultValue === 1 || resultValue === "1" || resultValue === true)
) {
text = "正确";
style = "color:#de2103;";
}
if (
!statusText &&
numericStatus === null &&
(resultValue === 0 || resultValue === "0" || resultValue === false)
) {
text = "错";
style = "color:#8c8c8c;";
}
return getDisplayStatusInfo(item, text, style);
}
function getDisplayStatusInfo(item, statusText, style) {
const text = String(statusText || "").trim();
let nextStyle = style || "color:#8c8c8c;";
let html = CommonUtil.escapeHtml(text);
if (isQlcLottery(item) && isGroupPlay(item) && /^中[01]$/.test(text)) {
nextStyle = "color:#8c8c8c;";
}
if (isThreeDigitLottery(item) && isGroupPlay(item) && /^中[012]$/.test(text)) {
nextStyle = "color:#8c8c8c;";
}
if (isKl8Lottery(item) && isDanmaPlay(item) && text === "中0") {
nextStyle = "color:#8c8c8c;";
}
if ((isDltLottery(item) || isSsqLottery(item)) && isDuplexPlay(item)) {
if (/^中(?:1\+0|0\+0)$/.test(text)) nextStyle = "color:#8c8c8c;";
html = buildFrontBackStatusHtml(text);
}
return { text: text, html: html, style: nextStyle };
}
function buildFrontBackStatusHtml(statusText) {
const match = String(statusText || "").trim().match(/^中(\d+)\+(\d+)$/);
if (!match) return CommonUtil.escapeHtml(statusText);
return [
'<span class="caiinfo-status-label">中</span>',
'<span class="caiinfo-status-front">' + CommonUtil.escapeHtml(match[1]) + "</span>",
'<span class="caiinfo-status-separator">+</span>',
'<span class="caiinfo-status-back">' + CommonUtil.escapeHtml(match[2]) + "</span>",
].join("");
}
function getArticleMatchText(item) {
return [
item && item.lotteryName,
item && item.name,
item && item.codeName,
item && item.code,
item && item.lotteryCode,
item && item.suoxie,
item && item.playName,
item && item.playType,
item && item.typeName,
item && item.parentTypeName,
item && item.menuName,
item && item.title,
item && item.articleTitle,
item && item.latestTitle,
].filter(Boolean).join(" ").toLowerCase();
}
function isSsqLottery(item) {
const text = getArticleMatchText(item);
return text.indexOf("双色球") >= 0 || text.indexOf("ssq") >= 0;
}
function isDltLottery(item) {
const text = getArticleMatchText(item);
return text.indexOf("大乐透") >= 0 || text.indexOf("cjdlt") >= 0 || /\bdlt\b/.test(text);
}
function isQlcLottery(item) {
const text = getArticleMatchText(item);
return text.indexOf("七乐彩") >= 0 || text.indexOf("qlc") >= 0;
}
function isKl8Lottery(item) {
const text = getArticleMatchText(item);
return text.indexOf("快乐8") >= 0 || text.indexOf("kl8") >= 0;
}
function isThreeDigitLottery(item) {
const text = getArticleMatchText(item);
return (
text.indexOf("福彩3d") >= 0 ||
text.indexOf("fc3d") >= 0 ||
text.indexOf("排列三") >= 0 ||
text.indexOf("排列3") >= 0 ||
text.indexOf("pl3") >= 0
);
}
function isFrontBackLottery(item) {
return isSsqLottery(item) || isDltLottery(item);
}
function isGroupPlay(item) {
return getArticleMatchText(item).indexOf("组选") >= 0;
}
function isDuplexPlay(item) {
return getArticleMatchText(item).indexOf("复式") >= 0;
}
function isDanmaPlay(item) {
return getArticleMatchText(item).indexOf("胆码") >= 0;
}
function isBlueBallRecommend(item) {
const text = getArticleMatchText(item);
if (!isFrontBackLottery(item)) return false;
if (text.indexOf("红球") >= 0 || text.indexOf("红区") >= 0 || text.indexOf("前区") >= 0) {
return false;
}
return text.indexOf("蓝") >= 0 || text.indexOf("后区") >= 0;
}
function renderBalls(codeStr, options) {
if (!codeStr) return '<span style="color:var(--text-muted);">--</span>';
if (isMaskedRecommendText(codeStr)) {
return '<span style="color:var(--text-muted);">' +
CommonUtil.escapeHtml(String(codeStr || "")) +
"</span>";
}
const parts = parseRecommendGroups(codeStr);
let html = "";
$.each(parts, function (groupIndex, group) {
const ballClass = groupIndex === 0 ? "red-q" : "bule-q";
$.each(group, function (i, b) {
const text = String(b || "").trim();
if (!text) return;
const missedClass = isRecommendMissed(text, options, groupIndex)
? " cxz-ball-missed"
: "";
html +=
'<span class="' +
ballClass +
missedClass +
'">' +
CommonUtil.escapeHtml(text) +
"</span>";
});
});
return html;
}
function renderOpenCodeBalls(codeStr, item) {
if (!codeStr) return '<span style="color:var(--text-muted);">--</span>';
const text = String(codeStr || "").trim();
if (text.indexOf("+") >= 0 || text.indexOf("|") >= 0) return renderBalls(text);
const numbers = splitRecommendValues(text);
if (!numbers.length) return '<span style="color:var(--text-muted);">--</span>';
const frontCount = isDltLottery(item) ? 5 : 6;
return numbers
.map(function (num, index) {
const ballClass =
isFrontBackLottery(item) && index >= frontCount ? "bule-q" : "red-q";
return (
'<span class="' +
ballClass +
'">' +
CommonUtil.escapeHtml(num) +
"</span>"
);
})
.join("");
}
function escapeJs(value) {
return String(value || "")
.replace(/\\/g, "\\\\")
.replace(/'/g, "\\'")
.replace(/\r?\n/g, " ");
}
function updateStats(list) {
let total = list.length,
hit = 0;
$.each(list, function (i, item) {
if (isArticleHit(item)) hit++;
});
$("#statTotal").text(total);
$("#statHit").text(hit);
$("#statRate").text(
total > 0 ? ((hit / total) * 100).toFixed(1) + "%" : "0%",
);
}
// Copy predicted numbers
function copyCode(code) {
if (!code) return;
if (navigator.clipboard) {
navigator.clipboard.writeText(code).then(function () {
layui.layer.msg("已复制到剪贴板", { icon: 1 });
});
} else {
const textarea = document.createElement("textarea");
textarea.value = code;
document.body.appendChild(textarea);
textarea.select();
document.execCommand("copy");
document.body.removeChild(textarea);
layui.layer.msg("已复制到剪贴板", { icon: 1 });
}
}
// Expert collect toggle
function toggleExpertCollect() {
CommonUtil.requireAuth(function () {
ApiClient.post(
ApiClient.API.expertCollect(createBy, accountType),
{},
{ headers: CommonUtil.authHeaders() },
).then(function (res) {
if (res.code === 0) {
expertCollected = getCollectResult(res, expertCollected, "expertCollected");
applyExpertCollectState(expertCollected);
layui.layer.msg(expertCollected ? "收藏成功" : "已取消收藏", {
icon: 1,
});
} else {
layui.layer.msg(res.msg || "操作失败", { icon: 2 });
}
});
});
}
function isExpertCollected(data) {
return (
data.isCollect === true ||
data.isCollect === 1 ||
data.isCollect === "1" ||
data.isCollect === 2 ||
data.isCollect === "2" ||
data.expertCollected === true ||
data.expertCollected === 1 ||
data.expertCollected === "1" ||
data.expertCollected === 2 ||
data.expertCollected === "2" ||
data.collected === true ||
data.collected === 1 ||
data.collected === "1" ||
data.collected === 2 ||
data.collected === "2"
);
}
function applyExpertCollectState(collected) {
const $btn = $("#btnCollectExpert");
if (collected) {
$btn
.addClass("collected")
.html('<i class="layui-icon layui-icon-star-fill"></i> 已收藏');
} else {
$btn
.removeClass("collected")
.html('<i class="layui-icon layui-icon-star-fill"></i> 收藏专家');
}
}
function getCollectResult(res, beforeCollected, field) {
const data = res && res.data ? res.data : {};
if (data.collected !== undefined) return !!data.collected;
if (field && data[field] !== undefined) return !!data[field];
if (data.isCollect !== undefined) return data.isCollect === true || data.isCollect === 1 || data.isCollect === "1" || data.isCollect === 2 || data.isCollect === "2";
return !beforeCollected;
}
// Article collect toggle
function toggleArticleCollect(btn, articleId) {
CommonUtil.requireAuth(function () {
ApiClient.post(
ApiClient.API.payArticleCollect(articleId),
{},
{ headers: CommonUtil.authHeaders() },
).then(function (res) {
if (res.code === 0) {
const $btn = $(btn);
const isCollected = $btn.hasClass("collected");
const afterCollected = getCollectResult(res, isCollected, "payArticleCollected");
if (!afterCollected) {
$btn.removeClass("collected").text("收藏");
} else {
$btn.addClass("collected").text("已收藏");
}
layui.layer.msg(afterCollected ? "收藏成功" : "已取消收藏", {
icon: 1,
});
} else {
layui.layer.msg(res.msg || "操作失败", { icon: 2 });
}
});
});
}
function isPayArticleCollected(item) {
return (
item.isCollect === true ||
item.isCollect === 1 ||
item.isCollect === "1" ||
item.articleCollected === true ||
item.articleCollected === 1 ||
item.articleCollected === "1" ||
item.collected === true ||
item.collected === 1 ||
item.collected === "1" ||
item.payArticleCollected === true ||
item.payArticleCollected === 1 ||
item.payArticleCollected === "1"
);
}
// Buy dialog
function requirePurchaseAuth() {
if (ApiClient.getToken()) return true;
CommonUtil.requireAuth(function () {});
return false;
}
function openPrePurchaseDialog(index) {
const item = currentArticleList[index] || {};
if (!requirePurchaseAuth()) return;
currentPurchaseMode = "prepurchase";
currentArticleId = "";
currentPrePurchaseItem = item;
currentPayAmount = parseMoney(getArticlePrice(item));
currentPointDeductLimit = getArticlePointDeductLimit(item);
$("#usePoint").prop("checked", false);
$(".cxz-pay-option").removeClass("selected");
$('.cxz-pay-option[data-type="account"]').addClass("selected");
selectedPayType = "account";
$(".cxz-dialog-title").text("预购方案");
updatePayCalculation();
loadUserBalance();
$("#buyDialog").addClass("active");
}
function openBuyDialog(articleId) {
if (!requirePurchaseAuth()) return;
currentPurchaseMode = "purchase";
currentPrePurchaseItem = null;
currentArticleId = articleId;
const article = currentArticleList.find(function (item) {
return getArticleId(item) === String(articleId);
});
currentPayAmount = parseMoney(getArticlePrice(article || {}));
currentPointDeductLimit = getArticlePointDeductLimit(article || {});
$("#usePoint").prop("checked", false);
$(".cxz-pay-option").removeClass("selected");
$('.cxz-pay-option[data-type="account"]').addClass("selected");
selectedPayType = "account";
$(".cxz-dialog-title").text("购买方案");
updatePayCalculation();
loadUserBalance();
$("#buyDialog").addClass("active");
}
function closeBuyDialog() {
$("#buyDialog").removeClass("active");
currentArticleId = "";
currentPurchaseMode = "purchase";
currentPrePurchaseItem = null;
currentPointDeductLimit = 0;
}
function selectPayType(el) {
$(".cxz-pay-option").removeClass("selected");
$(el).addClass("selected");
selectedPayType = $(el).data("type");
updatePayCalculation();
}
function loadUserBalance() {
ApiClient.get(ApiClient.API.walletOverview, null, {
headers: CommonUtil.authHeaders(),
}).then(function (res) {
if (res.code === 0 && res.data) {
currentWalletOverview = res.data || {};
$("#usePoint").prop(
"checked",
pointValue() > 0 && currentPayAmount > 0 && currentPointDeductLimit > 0,
);
$("#accountBalance").text(
formatMoney(walletValue("account")),
);
$("#distributionBalance").text(
formatMoney(walletValue("sales")),
);
$("#expertBalance").text(formatMoney(walletValue("expert")));
$("#pointBalance").text(
"可用积分 " +
formatPoint(pointValue()) +
",本单最多抵扣 ¥" +
formatMoney(currentPointDeductLimit),
);
updatePayCalculation();
}
});
}
function parseMoney(value) {
const number = Number(String(value ?? 0).replace(/[^\d.-]/g, ""));
return Number.isFinite(number) ? number : 0;
}
function formatMoney(value) {
const number = Number(value || 0);
return Number.isFinite(number) ? number.toFixed(2) : "0.00";
}
function formatPoint(value) {
const number = Number(value || 0);
return Number.isFinite(number) ? String(Math.floor(number)) : "0";
}
function walletValue(type) {
const data = currentWalletOverview || {};
if (type === "sales") {
return parseMoney(
data.salesBalance ??
data.distributionBalance ??
data.rebateBalance ??
data.saleBalance,
);
}
if (type === "expert") {
return parseMoney(data.expertBalance ?? data.commissionBalance);
}
return parseMoney(data.balance ?? data.accountBalance ?? data.walletBalance);
}
function pointValue() {
const data = currentWalletOverview || {};
return parseMoney(data.points ?? data.point ?? data.integral ?? data.score);
}
function walletLabel(type) {
if (type === "sales") return "分销余额";
if (type === "expert") return "专家余额";
return "账户余额";
}
function updatePayCalculation() {
const usePoint = $("#usePoint").prop("checked") === true;
const pointDeduct = usePoint
? Math.min(currentPayAmount, pointValue(), currentPointDeductLimit)
: 0;
const finalAmount = Math.max(currentPayAmount - pointDeduct, 0);
const walletBalance = walletValue(selectedPayType);
$("#payOriginalAmount").text(formatMoney(currentPayAmount));
$("#pointDeductAmount").text(formatMoney(pointDeduct));
$("#pointDeductPreview").text(formatMoney(pointDeduct));
$("#payFinalAmount").text(formatMoney(finalAmount));
$("#payFooterAmount").text(formatMoney(finalAmount));
$(".cxz-point-card").toggleClass("selected", usePoint);
$("#selectedWalletLabel").text(walletLabel(selectedPayType));
$("#selectedWalletBalance").text(formatMoney(walletBalance));
$("#payBalanceHint").toggleClass("is-insufficient", walletBalance < finalAmount);
$(".cxz-dialog-confirm").prop("disabled", walletBalance < finalAmount);
$(".cxz-dialog-confirm").text(
walletBalance < finalAmount
? "余额不足"
: currentPurchaseMode === "prepurchase"
? "确认预购"
: "确认购买",
);
}
function confirmBuy() {
if (currentPurchaseMode === "prepurchase") {
confirmPrePurchase();
return;
}
if (!currentArticleId) return;
const params = {
articleId: currentArticleId,
walletType:
selectedPayType === "sales" || selectedPayType === "expert"
? selectedPayType
: "account",
usePoints: $("#usePoint").prop("checked") === true,
};
ApiClient.post(ApiClient.API.walletPurchasePayArticle, params, {
headers: CommonUtil.authHeaders(),
}).then(function (res) {
if (res.code === 0) {
layui.layer.msg("购买成功", { icon: 1 });
const purchased = currentArticleList.find(function (item) {
return getArticleId(item) === String(currentArticleId);
});
rememberPurchasedArticle(purchased, currentArticleId);
closeBuyDialog();
loadArticles();
} else {
layui.layer.msg(res.msg || "购买失败", { icon: 2 });
}
});
}
function confirmPrePurchase() {
const item = currentPrePurchaseItem || {};
const params = {
menuId: getArticleMenuId(item),
expertId: getArticleExpertId(item),
accountType: Number(item.accountType || accountType) || 1,
issue: getArticleIssue(item),
count: 1,
walletType:
selectedPayType === "sales" || selectedPayType === "expert"
? selectedPayType
: "account",
usePoints: $("#usePoint").prop("checked") === true,
};
if (!params.menuId || !params.expertId || !params.issue) {
layui.layer.msg("预购参数缺失,请刷新后重试", { icon: 2 });
return;
}
ApiClient.post(ApiClient.API.walletPrepurchasePayArticle, params, {
headers: CommonUtil.authHeaders(),
}).then(function (res) {
if (ApiClient.isSuccess(res)) {
layui.layer.msg("预购成功", { icon: 1 });
item.subscribed = true;
closeBuyDialog();
loadArticles();
} else {
layui.layer.msg(res.msg || "预购失败", { icon: 2 });
}
});
}
function buildCaiListUrl(lotteryCode) {
const params = new URLSearchParams();
params.set("id", lotteryCode || code || "ssq");
if (parentId) params.set("parentId", parentId);
return "cai.html?" + params.toString();
}
// Breadcrumb
updateBreadcrumb("专家详情");
// Init
$(function () {
checkLogin();
loadBasicConfig();
renderNav();
$("#buyDialog").on("click", function (event) {
if (event.target === this) closeBuyDialog();
});
$(".cxz-dialog-box").on("click", function (event) {
event.stopPropagation();
});
$("#usePoint").on("change", updatePayCalculation);
loadArticles();
});
</script>
</body>
</html>