2136 lines
75 KiB
HTML
2136 lines
75 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/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()">
|
||
×
|
||
</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">></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>
|