From eca7992d03f433042bb0ffeceb184165c07422fd Mon Sep 17 00:00:00 2001 From: rain Date: Wed, 8 Jul 2026 11:46:09 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E6=A0=B7=E5=BC=8F=E3=80=81?= =?UTF-8?q?=E5=8A=9F=E8=83=BD=E5=AE=8C=E6=88=90=EF=BC=8C=E5=BE=85=E6=B5=8B?= =?UTF-8?q?=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- html/apply.html | 1 + html/c.html | 1 + html/cai.html | 183 +++------------ html/caiinfo.html | 252 ++++++++++++++++++-- html/chongzhi.html | 1 + html/cjdlt.html | 1 + html/fabumianfeiwenzhang.html | 1 + html/fc3d.html | 1 + html/fenxiao.html | 1 + html/fufei.html | 1 + html/gonggao.html | 1 + html/gonggaolist.html | 1 + html/index.html | 1 + html/jine.html | 1 + html/kaijiang.html | 1 + html/kl8.html | 1 + html/mianfei.html | 1 + html/mianfeilist.html | 1 + html/mianfeishoucang.html | 1 + html/pl3.html | 1 + html/pl5.html | 1 + html/qlc.html | 1 + html/qxc.html | 1 + html/ssq.html | 1 + html/tixian.html | 1 + html/tixianlist.html | 1 + html/tuiguang.html | 1 + html/usercenter.html | 1 + html/wodefenxiao.html | 1 + html/yinsixieyi.html | 1 + html/yonghuxieyi.html | 1 + public/css/cai.css | 330 +++++++-------------------- public/css/caiinfo.css | 278 +++++++++++++++++++++- public/css/finance.css | 42 +++- public/css/layout-fix.css | 19 ++ public/css/pagination.css | 60 +++++ public/css/public.css | 3 +- public/css/publish.css | 5 +- utils/ApiClient.js | 2 +- utils/CommonUtil.js | 418 ++++++++++++++++++++++------------ 40 files changed, 1025 insertions(+), 596 deletions(-) create mode 100644 public/css/layout-fix.css diff --git a/html/apply.html b/html/apply.html index d94bdf1..683ecf7 100644 --- a/html/apply.html +++ b/html/apply.html @@ -11,6 +11,7 @@ + diff --git a/html/c.html b/html/c.html index 5a5e864..c8af7fb 100644 --- a/html/c.html +++ b/html/c.html @@ -14,6 +14,7 @@ + diff --git a/html/cai.html b/html/cai.html index 987de6d..9816ee6 100644 --- a/html/cai.html +++ b/html/cai.html @@ -11,6 +11,7 @@ + @@ -83,6 +84,7 @@
预测列表
+ @@ -92,6 +94,13 @@
+ + + + + + + @@ -119,37 +128,6 @@ - -
-
-
专家排行榜
-
-
专家
- - - - - - - - - - - - - - -
排名专家命中率方案数操作
- 加载中... -
-
@@ -284,7 +262,6 @@ renderPlanFilter(); renderTypeFilter(); loadArticles(); - loadRanking(); updateBreadcrumb(); }); $box.append(btn); @@ -361,7 +338,6 @@ renderTypeFilter(); syncCurrentUrl(); loadArticles(); - loadRanking(); }); $box.append(btn); }); @@ -416,7 +392,6 @@ $(this).addClass("active"); syncCurrentUrl(); loadArticles(); - loadRanking(); }); $box.append(btn); }); @@ -439,6 +414,10 @@ return String(activeTypeId || currentTypeId || ""); } + function getCurrentTypeName() { + return $.trim($("#typeFilter .cxz-filter-btn.active").text() || ""); + } + function escapeText(value) { return CommonUtil.escapeHtml ? CommonUtil.escapeHtml(String(value || "")) : String(value || ""); } @@ -467,11 +446,16 @@ const issue = item.latestIssue || item.issue || item.expect || item.period || item.issueNo || ""; if (!issue) return "--"; const lotteryName = currentLottery ? currentLottery.name : item.lotteryName || item.menuName || ""; - return (lotteryName ? lotteryName + "-第" : "第") + issue + "期"; + return (lotteryName ? lotteryName + "-" : "") + issue + "期"; } function getRecommendTitle(item) { + const currentName = getCurrentTypeName(); return ( + currentName || + item.playName || + item.typeName || + item.menuName || item.latestTitle || item.title || item.articleTitle || @@ -518,26 +502,6 @@ return "--"; } - function getRatePercentText(item) { - const raw = - item.hitRate ?? - item.successRate ?? - item.correctRate ?? - item.winRate ?? - item.rate ?? - ""; - if (raw !== "" && raw !== null && raw !== undefined) { - if (String(raw).indexOf("%") >= 0) return String(raw); - const value = Number(raw); - if (Number.isFinite(value)) { - return (value > 0 && value <= 1 ? value * 100 : value).toFixed(2) + "%"; - } - } - const hit = Number(item.correctCount ?? item.hitCount ?? item.hit ?? 0); - const total = Number(item.totalCount ?? item.total ?? item.articleCount ?? 0); - return total > 0 ? ((hit / total) * 100).toFixed(2) + "%" : "0.00%"; - } - function buildCaiinfoUrl(item) { const params = new URLSearchParams(); params.set("createBy", getExpertId(item)); @@ -593,20 +557,25 @@ const periodText = getIssueText(item); const predictHtml = getRecommendTitle(item); const avatarHtml = avatar - ? '' - : '' + + ? '' + escapeText(expertName) + '' + : '' + escapeText(expertName).charAt(0) + ""; const linkUrl = buildCaiinfoUrl(item); + const rankNo = (currentPage - 1) * currentRowPageSize + i + 1; $body.append( "" + '' + + '' + + rankNo + + "" + avatarHtml + - "" + + '' + escapeText(expertName) + - "" + + "" + + "" + "" + escapeText(periodText) + "" + @@ -655,101 +624,6 @@ }); } - // Load expert ranking - function loadRanking() { - const typeId = getCurrentTypeId(); - if (!typeId) { - $("#rankBody").html( - '暂无数据', - ); - return; - } - ApiClient.get(ApiClient.API.payArticleExpertRank, { - menuId: typeId, - issueCount: 50, - pageNum: 1, - pageSize: currentRowPageSize, - }).then(function (res) { - if (ApiClient.isSuccess(res) && ApiClient.pickData(res, null)) { - const data = ApiClient.pickData(res, {}) || {}; - renderRanking(data.records || data.rows || ApiClient.asArray(data)); - } - }); - } - - function renderRanking(list) { - const $body = $("#rankBody"); - $body.empty(); - if (!list || list.length === 0) { - $body.html( - '暂无数据', - ); - return; - } - const experts = list - .map(function (item) { - const total = Number(item.totalCount || item.total || 0); - const hit = Number(item.correctCount || item.hit || 0); - const rateText = getRatePercentText(item); - const hitText = getCorrectDisplayText(item); - const rateNum = rateText - ? parseFloat(String(rateText).replace("%", "")) - : total > 0 - ? (hit / total) * 100 - : 0; - return { - raw: item, - name: getExpertName(item), - createBy: getExpertId(item), - total: total || item.articleCount || item.totalCount || 0, - hit, - hitText, - rate: Number.isFinite(rateNum) ? rateNum : 0, - rank: item.rank, - accountType: item.accountType, - }; - }) - .sort(function (a, b) { - return (a.rank || 9999) - (b.rank || 9999) || b.rate - a.rate; - }) - .slice(0, 10); - - $.each(experts, function (i, exp) { - const rate = exp.rate.toFixed(1); - const badgeClass = - i === 0 - ? "cxz-rank-1" - : i === 1 - ? "cxz-rank-2" - : i === 2 - ? "cxz-rank-3" - : "cxz-rank-n"; - $body.append( - "" + - '' + - (i + 1) + - "" + - '' + - escapeText(exp.name) + - "" + - '' + - escapeText(exp.hitText !== "--" ? exp.hitText : rate + "%") + - "" + - "" + - exp.total + - "" + - '查看' + - "", - ); - }); - } - // Period selector $(document).on("click", ".cxz-period-btn", function () { $(".cxz-period-btn").removeClass("active"); @@ -765,7 +639,6 @@ CommonUtil.loadPageConfig(); setTimeout(function () { loadArticles(); - loadRanking(); updateBreadcrumb(); }, 300); }); diff --git a/html/caiinfo.html b/html/caiinfo.html index 603718c..27293b7 100644 --- a/html/caiinfo.html +++ b/html/caiinfo.html @@ -11,6 +11,7 @@ + @@ -58,10 +59,16 @@
- 首页> - 预测推荐> - 专家详情 + + + 返回上一页 + +
@@ -279,6 +286,7 @@ let currentWalletOverview = {}; let expertCollected = false; let currentArticleList = []; + let currentMenuMeta = { lottery: null, parent: null, current: null }; const purchasedArticleIds = new Set(); const purchasedArticleKeys = new Set(); @@ -344,6 +352,94 @@ }); } + 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 + ? '' + text + "" + : '' + text + ""; + return index === 0 ? node : '>' + node; + }) + .join(""); + + $("#breadcrumbTrail").html(html); + $("#breadcrumbBack").attr("href", getReturnListHref()); + } + function getArticleId(item) { return String( (item && @@ -388,6 +484,11 @@ ); } + 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 || ""); } @@ -529,6 +630,7 @@ return { code: 500, msg: error.message || "获取专家预测详情失败", data: [] }; }), loadPurchasedRecordsForCurrentExpert(), + loadBreadcrumbMenuMeta(), ]).then(function ([profileRes, listRes]) { const profile = ApiClient.pickData(profileRes, {}) || {}; const listData = ApiClient.pickData(listRes, []) || []; @@ -583,7 +685,7 @@ "专家"; if (avatar) $("#expertAvatar").attr("src", avatar); $("#expertName").text(name); - $("#breadcrumbExpert").text(name || "专家详情"); + updateBreadcrumb(name || "专家详情"); expertCollected = isExpertCollected(data); applyExpertCollectState(expertCollected); } @@ -611,9 +713,15 @@ const predictedCode = getArticleRecommend(item); const hasDrawResult = isArticleDrawn(item, openCode); const isLatestIssue = isLatestArticle(item, list); + const price = Number(getArticlePrice(item)); const unlockedByVisibleRecommend = - predictedCode && !isMaskedRecommendText(predictedCode); - const purchased = isArticlePurchased(item); + isLatestIssue && + !hasDrawResult && + price > 0 && + !isArticleMasked(item) && + predictedCode && + !isMaskedRecommendText(predictedCode); + const purchased = isArticlePurchased(item) || unlockedByVisibleRecommend; const canShowRecommend = canShowArticleRecommend( item, isLatestIssue, @@ -621,17 +729,21 @@ unlockedByVisibleRecommend, ); const articleStatus = getArticleStatus(item); + let recommendOptions = null; + let recommendText = ""; let openCodeHtml = renderOpenCodeBalls(openCode, item); let predictHtml = ""; - if (isPendingPublishText(predictedCode)) { - predictHtml = '待发布'; + const pendingPublish = isPendingPublishArticle(item, articleStatus, predictedCode); + if (pendingPublish) { + predictHtml = '' + getPendingPublishText() + ''; } else if (canShowRecommend && predictedCode) { - const recommendOptions = getRecommendRenderOptions(item, articleStatus.text); + recommendOptions = getRecommendRenderOptions(item, articleStatus.text); + recommendText = formatRecommendText(predictedCode, Infinity, recommendOptions); predictHtml = - buildRecommendHtml(predictedCode, Infinity, recommendOptions) + - ' "; + '' + + '"; } else if (predictedCode) { predictHtml = '' + @@ -654,7 +766,7 @@ actionHtml = ' '; + ')">购买 '; } else if (shouldShowBuyButton(item, isLatestIssue, hasDrawResult, purchased) && articleId) { actionHtml = '' + + "
" + + "
", + }); + 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 ""; @@ -1332,6 +1532,10 @@ let text = statusText || "待开奖"; let style = "color:#8c8c8c;"; + if (isPendingPublishText(text)) { + text = "待开奖"; + } + if (isWinStatusText(text)) { style = "color:#de2103;"; } @@ -1885,7 +2089,7 @@ return; } - ApiClient.get(ApiClient.API.walletPrepurchasePayArticle, params, { + ApiClient.post(ApiClient.API.walletPrepurchasePayArticle, params, { headers: CommonUtil.authHeaders(), }).then(function (res) { if (ApiClient.isSuccess(res)) { @@ -1899,15 +2103,15 @@ }); } - function buildCaiListUrl() { + function buildCaiListUrl(lotteryCode) { const params = new URLSearchParams(); - params.set("id", code || "ssq"); + params.set("id", lotteryCode || code || "ssq"); if (parentId) params.set("parentId", parentId); return "cai.html?" + params.toString(); } // Breadcrumb - $("#breadcrumbCai").attr("href", buildCaiListUrl()); + updateBreadcrumb("专家详情"); // Init $(function () { diff --git a/html/chongzhi.html b/html/chongzhi.html index 0e28179..21a70c2 100644 --- a/html/chongzhi.html +++ b/html/chongzhi.html @@ -11,6 +11,7 @@ + diff --git a/html/cjdlt.html b/html/cjdlt.html index 34dc8ff..ff509d1 100644 --- a/html/cjdlt.html +++ b/html/cjdlt.html @@ -11,6 +11,7 @@ + diff --git a/html/fabumianfeiwenzhang.html b/html/fabumianfeiwenzhang.html index 115cb1f..18205e0 100644 --- a/html/fabumianfeiwenzhang.html +++ b/html/fabumianfeiwenzhang.html @@ -12,6 +12,7 @@ + diff --git a/html/fc3d.html b/html/fc3d.html index 01d1d58..3b3fda7 100644 --- a/html/fc3d.html +++ b/html/fc3d.html @@ -11,6 +11,7 @@ + diff --git a/html/fenxiao.html b/html/fenxiao.html index c0eb87c..222c8d6 100644 --- a/html/fenxiao.html +++ b/html/fenxiao.html @@ -12,6 +12,7 @@ + diff --git a/html/fufei.html b/html/fufei.html index e2454d7..64e3a36 100644 --- a/html/fufei.html +++ b/html/fufei.html @@ -6,6 +6,7 @@ 神彩算 - 发布付费文章 + diff --git a/html/gonggao.html b/html/gonggao.html index 87866a5..2a7cbd4 100644 --- a/html/gonggao.html +++ b/html/gonggao.html @@ -11,6 +11,7 @@ + diff --git a/html/gonggaolist.html b/html/gonggaolist.html index 152d48c..6c94730 100644 --- a/html/gonggaolist.html +++ b/html/gonggaolist.html @@ -11,6 +11,7 @@ + diff --git a/html/index.html b/html/index.html index dd61b8a..a874cde 100644 --- a/html/index.html +++ b/html/index.html @@ -6,6 +6,7 @@ 神彩算 - 多文章彩票预测首页 + diff --git a/html/jine.html b/html/jine.html index 9be0746..a2a0c55 100644 --- a/html/jine.html +++ b/html/jine.html @@ -11,6 +11,7 @@ + diff --git a/html/kaijiang.html b/html/kaijiang.html index 7233d4d..a4315b1 100644 --- a/html/kaijiang.html +++ b/html/kaijiang.html @@ -11,6 +11,7 @@ + diff --git a/html/kl8.html b/html/kl8.html index 00ce8b7..a934e56 100644 --- a/html/kl8.html +++ b/html/kl8.html @@ -11,6 +11,7 @@ + diff --git a/html/mianfei.html b/html/mianfei.html index 518c249..54bf608 100644 --- a/html/mianfei.html +++ b/html/mianfei.html @@ -12,6 +12,7 @@ + diff --git a/html/mianfeilist.html b/html/mianfeilist.html index b26d439..cb15b73 100644 --- a/html/mianfeilist.html +++ b/html/mianfeilist.html @@ -11,6 +11,7 @@ + diff --git a/html/mianfeishoucang.html b/html/mianfeishoucang.html index 020090f..72d46d3 100644 --- a/html/mianfeishoucang.html +++ b/html/mianfeishoucang.html @@ -11,6 +11,7 @@ + diff --git a/html/pl3.html b/html/pl3.html index 32772b9..fe2b180 100644 --- a/html/pl3.html +++ b/html/pl3.html @@ -11,6 +11,7 @@ + diff --git a/html/pl5.html b/html/pl5.html index 7fc7f64..4b74bb3 100644 --- a/html/pl5.html +++ b/html/pl5.html @@ -11,6 +11,7 @@ + diff --git a/html/qlc.html b/html/qlc.html index 192a9ee..a37a221 100644 --- a/html/qlc.html +++ b/html/qlc.html @@ -11,6 +11,7 @@ + diff --git a/html/qxc.html b/html/qxc.html index d811fa8..59a9b6a 100644 --- a/html/qxc.html +++ b/html/qxc.html @@ -11,6 +11,7 @@ + diff --git a/html/ssq.html b/html/ssq.html index 53debf2..4427151 100644 --- a/html/ssq.html +++ b/html/ssq.html @@ -11,6 +11,7 @@ + diff --git a/html/tixian.html b/html/tixian.html index ebc69eb..296a614 100644 --- a/html/tixian.html +++ b/html/tixian.html @@ -11,6 +11,7 @@ + diff --git a/html/tixianlist.html b/html/tixianlist.html index 2f20105..8706051 100644 --- a/html/tixianlist.html +++ b/html/tixianlist.html @@ -11,6 +11,7 @@ + diff --git a/html/tuiguang.html b/html/tuiguang.html index 9cc827f..f51055c 100644 --- a/html/tuiguang.html +++ b/html/tuiguang.html @@ -12,6 +12,7 @@ + diff --git a/html/usercenter.html b/html/usercenter.html index 3f9d8a3..cba3845 100644 --- a/html/usercenter.html +++ b/html/usercenter.html @@ -11,6 +11,7 @@ + diff --git a/html/wodefenxiao.html b/html/wodefenxiao.html index f627298..aaddd78 100644 --- a/html/wodefenxiao.html +++ b/html/wodefenxiao.html @@ -11,6 +11,7 @@ + diff --git a/html/yinsixieyi.html b/html/yinsixieyi.html index adfece9..c3dd9f9 100644 --- a/html/yinsixieyi.html +++ b/html/yinsixieyi.html @@ -11,6 +11,7 @@ + diff --git a/html/yonghuxieyi.html b/html/yonghuxieyi.html index ae35499..93693b0 100644 --- a/html/yonghuxieyi.html +++ b/html/yonghuxieyi.html @@ -11,6 +11,7 @@ + diff --git a/public/css/cai.css b/public/css/cai.css index d92d38a..6e70393 100644 --- a/public/css/cai.css +++ b/public/css/cai.css @@ -191,6 +191,27 @@ .cxz-data-table { width: 100%; border-collapse: collapse; + table-layout: fixed; +} + +.cxz-data-table .cxz-col-expert { + width: 30%; +} + +.cxz-data-table .cxz-col-issue { + width: 22%; +} + +.cxz-data-table .cxz-col-recommend { + width: 22%; +} + +.cxz-data-table .cxz-col-rate { + width: 13%; +} + +.cxz-data-table .cxz-col-action { + width: 13%; } .cxz-data-table thead { @@ -216,12 +237,28 @@ vertical-align: middle; } +.cxz-data-table th:nth-child(1), +.cxz-data-table td:nth-child(1) { + text-align: left; +} + +.cxz-data-table td:nth-child(2), +.cxz-data-table td:nth-child(3) { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + .cxz-data-table tbody tr { transition: background 0.2s ease; } +.cxz-table-box .cxz-data-table tbody tr:nth-child(even) { + background: #eef4fc; +} + .cxz-data-table tbody tr:hover { - background: var(--bg-light, #f4f6fb); + background: #e4edf9; } .cxz-data-table tbody tr:last-child td { @@ -282,136 +319,51 @@ border-radius: var(--radius-md, 8px) !important; } -/* ---------------------------------------------------------- - 5. Ranking Box - ---------------------------------------------------------- */ -.cxz-ranking-box { - background: var(--white, #ffffff); - border-radius: var(--radius-lg, 12px); - overflow: hidden; - box-shadow: var(--shadow-md, 0 4px 16px rgba(0, 0, 0, 0.08)); - margin-bottom: 20px; - border: 1px solid var(--border-light, #e5e7eb); -} - -.cxz-ranking-header { - display: flex; - align-items: center; - justify-content: space-between; - padding: 16px 20px; - border-bottom: 2px solid var(--gold, #fdb933); - background: linear-gradient(135deg, var(--bg-light, #f4f6fb), #ffffff); -} - -.cxz-ranking-title { - font-size: 16px; - font-weight: 700; - color: var(--primary, #102b6a); - padding-left: 14px; - border-left: 4px solid var(--gold, #fdb933); - line-height: 1.4; -} - -.cxz-rank-table { - width: 100%; - border-collapse: collapse; -} - -.cxz-rank-table thead { - background: var(--bg-light, #f4f6fb); -} - -.cxz-rank-table th { - padding: 12px 16px; - font-size: 13px; - font-weight: 600; - color: var(--text-muted, #6b7280); - text-align: center; - white-space: nowrap; - border-bottom: 1px solid var(--border-light, #e5e7eb); -} - -.cxz-rank-table td { - padding: 14px 16px; - font-size: 14px; - text-align: center; - border-bottom: 1px solid var(--border-light, #e5e7eb); - color: var(--text-dark, #1a1a2e); - vertical-align: middle; -} - -.cxz-rank-table tbody tr { - transition: background 0.2s ease; -} - -.cxz-rank-table tbody tr:hover { - background: var(--bg-light, #f4f6fb); -} - -.cxz-rank-table tbody tr:last-child td { - border-bottom: none; -} - -/* Highlight top-3 rows */ -.cxz-rank-table tbody tr:nth-child(1) { - background: rgba(253, 185, 51, 0.04); -} - -.cxz-rank-table tbody tr:nth-child(2) { - background: rgba(192, 192, 192, 0.04); -} - -.cxz-rank-table tbody tr:nth-child(3) { - background: rgba(205, 127, 50, 0.04); -} - -/* ---------------------------------------------------------- - 6. Rank Badges - ---------------------------------------------------------- */ -.cxz-rank-badge { - display: inline-flex; - align-items: center; - justify-content: center; - width: 28px; - height: 28px; - border-radius: 50%; - font-size: 13px; - font-weight: 700; - color: #ffffff; - text-shadow: 0 1px 2px rgba(0, 0, 0, 0.15); - flex-shrink: 0; -} - -/* 1st place - Gold */ -.cxz-rank-1 { - background: linear-gradient(135deg, #fdb933, #f5a623); - box-shadow: 0 2px 8px rgba(253, 185, 51, 0.45); -} - -/* 2nd place - Silver */ -.cxz-rank-2 { - background: linear-gradient(135deg, #c0c0c0, #a8a8a8); - box-shadow: 0 2px 8px rgba(192, 192, 192, 0.45); -} - -/* 3rd place - Bronze */ -.cxz-rank-3 { - background: linear-gradient(135deg, #cd7f32, #b5651d); - box-shadow: 0 2px 8px rgba(205, 127, 50, 0.45); -} - -/* 4th and beyond - Muted */ -.cxz-rank-n { - background: var(--text-muted, #6b7280); - box-shadow: 0 1px 4px rgba(107, 114, 128, 0.3); -} - .cxz-expert-cell { display: inline-flex; align-items: center; gap: 10px; + max-width: 100%; color: var(--text); font-weight: 600; + overflow: hidden; + vertical-align: middle; + white-space: nowrap; +} + +.cxz-expert-rank { + display: inline-flex; + align-items: center; + justify-content: center; + min-width: 28px; + color: var(--text-muted, #6b7280); + font-weight: 700; + flex-shrink: 0; +} + +.cxz-expert-name { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.table-img, +.cxz-table-avatar-placeholder { + display: inline-flex; + align-items: center; + justify-content: center; + width: 32px; + height: 32px; + border-radius: 50%; + vertical-align: middle; + object-fit: cover; +} + +.cxz-table-avatar-placeholder { + background: linear-gradient(135deg, var(--navy, #08183c), var(--primary, #102b6a)); + color: #ffffff; + font-size: 13px; + font-weight: 700; } .cxz-expert-avatar { @@ -437,40 +389,6 @@ font-size: 13px; } -/* ---------------------------------------------------------- - 7. Expert Link - ---------------------------------------------------------- */ -.cxz-expert-link { - display: inline-flex; - align-items: center; - gap: 6px; - color: var(--primary, #102b6a); - text-decoration: none; - font-weight: 600; - font-size: 14px; - transition: all 0.2s ease; - white-space: nowrap; -} - -.cxz-expert-link:hover { - color: var(--orange, #f15a22); -} - -.cxz-expert-link::before { - content: ""; - display: inline-block; - width: 4px; - height: 4px; - border-radius: 50%; - background: var(--primary, #102b6a); - flex-shrink: 0; - transition: background 0.2s ease; -} - -.cxz-expert-link:hover::before { - background: var(--orange, #f15a22); -} - /* ---------------------------------------------------------- 8. Buy / Action Button ---------------------------------------------------------- */ @@ -578,10 +496,6 @@ font-size: 12px; } - .cxz-ranking-box { - margin-bottom: 16px; - } - .cxz-cai-layout { grid-template-columns: 1fr; } @@ -686,53 +600,6 @@ min-width: 560px; } - /* Ranking */ - .cxz-ranking-box { - border-radius: var(--radius-md, 8px); - margin-bottom: 16px; - } - - .cxz-ranking-header { - padding: 12px 14px; - } - - .cxz-ranking-title { - font-size: 14px; - padding-left: 10px; - border-left-width: 3px; - } - - .cxz-rank-table th { - padding: 10px 6px; - font-size: 12px; - } - - .cxz-rank-table td { - padding: 10px 6px; - font-size: 12px; - } - - .cxz-ranking-box { - overflow-x: auto; - -webkit-overflow-scrolling: touch; - } - - .cxz-rank-table { - min-width: 420px; - } - - /* Rank badges - slightly smaller on mobile */ - .cxz-rank-badge { - width: 24px; - height: 24px; - font-size: 12px; - } - - /* Expert link */ - .cxz-expert-link { - font-size: 13px; - } - /* Buy button */ .cxz-btn-buy { padding: 4px 14px; @@ -813,32 +680,12 @@ margin: 1px 0; } - .cxz-rank-table { - min-width: 360px; - } - - .cxz-rank-badge { - width: 22px; - height: 22px; - font-size: 11px; - } - .cxz-btn-buy { padding: 3px 10px; font-size: 10px; border-radius: 10px; } - .cxz-expert-link { - font-size: 12px; - gap: 4px; - } - - .cxz-expert-link::before { - width: 3px; - height: 3px; - } - .cxz-pagination { padding: 12px 6px; } @@ -874,24 +721,6 @@ animation: cxz-fade-in 0.4s ease-out; } -.cxz-ranking-box { - animation: cxz-fade-in 0.5s ease-out; -} - -/* Badge pulse animation for 1st place */ -@keyframes cxz-badge-pulse { - 0%, 100% { - box-shadow: 0 2px 8px rgba(253, 185, 51, 0.45); - } - 50% { - box-shadow: 0 2px 14px rgba(253, 185, 51, 0.7); - } -} - -.cxz-rank-1 { - animation: cxz-badge-pulse 2s ease-in-out infinite; -} - /* ---------------------------------------------------------- 15. Print Styles ---------------------------------------------------------- */ @@ -903,16 +732,13 @@ display: none !important; } - .cxz-ranking-box, .cxz-table-box { box-shadow: none; border: 1px solid #ccc; } .cxz-data-table th, - .cxz-data-table td, - .cxz-rank-table th, - .cxz-rank-table td { + .cxz-data-table td { padding: 6px 8px; font-size: 11px; } diff --git a/public/css/caiinfo.css b/public/css/caiinfo.css index 3fbe163..4230c88 100644 --- a/public/css/caiinfo.css +++ b/public/css/caiinfo.css @@ -2,6 +2,38 @@ caiinfo.css - Expert Detail Page (caiinfo.html) ============================================================ */ +.cxz-breadcrumb { + gap: 10px 14px; +} + +.cxz-breadcrumb-back { + display: inline-flex; + align-items: center; + gap: 4px; + flex-shrink: 0; + color: #5f6f86; +} + +.cxz-breadcrumb-back .layui-icon { + font-size: 14px; +} + +.cxz-breadcrumb-trail { + display: flex; + align-items: center; + flex-wrap: wrap; + gap: 8px; + min-width: 0; +} + +.cxz-table-box .cxz-data-table tbody tr:nth-child(even) { + background: #eef4fc; +} + +.cxz-table-box .cxz-data-table tbody tr:hover { + background: #e4edf9; +} + /* ---------------------------------------------------------- 1. Expert Card ---------------------------------------------------------- */ @@ -879,38 +911,250 @@ } .cxz-data-table td .fushi234item { - display: inline-flex; - align-items: center; - flex-wrap: wrap; + display: inline-block; + max-width: 100%; + overflow: hidden; + white-space: nowrap; gap: 0; - color: #333; + color: #de2103; font-weight: 700; line-height: 1.8; vertical-align: middle; } -.cxz-data-table td .recommend-number-text, -.cxz-data-table td .recommend-separator { - color: #333; - font-size: 14px; +.cxz-data-table td .cxz-recommend-wrap { + display: inline-grid; + grid-template-columns: minmax(0, 1fr) auto; + align-items: center; + gap: 6px; + max-width: 100%; + vertical-align: middle; } -.cxz-data-table td .recommend-number-hit, -.recommend-number-dialog .recommend-number-hit, -.recommend-number-content .recommend-number-hit { +.cxz-data-table td .cxz-recommend-content { + display: block; + min-width: 0; +} + +.cxz-data-table td .recommend-clickable { + cursor: pointer; +} + +.cxz-data-table td .recommend-clickable .fushi234item { + display: inline-flex; + align-items: center; + max-width: 100%; + min-width: 0; +} + +.cxz-data-table td .recommend-preview-text { + display: inline-block; + min-width: 0; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.cxz-data-table td .recommend-view-all { + display: inline-flex; + flex: 0 0 auto; + align-items: center; + margin-left: 6px; + color: #409eff; + font-size: 12px; + white-space: nowrap; +} + +.cxz-data-table td .recommend-clickable:hover .recommend-view-all { color: #de2103; } +.cxz-data-table td .recommend-number-text, +.cxz-data-table td .recommend-separator, +.cxz-number-popup .recommend-number-text, +.cxz-number-popup .recommend-separator { + color: #de2103; + font-size: 14px; + font-weight: 700; +} + +.cxz-data-table td .recommend-number-hit, +.cxz-number-popup .recommend-number-hit, +.recommend-number-dialog .recommend-number-hit, +.recommend-number-content .recommend-number-hit { + color: #de2103; + font-weight: 700; +} + .cxz-data-table td .recommend-number-blue, +.cxz-number-popup .recommend-number-blue, .recommend-number-dialog .recommend-number-blue, .recommend-number-content .recommend-number-blue { color: #1c89f8; + font-weight: 700; } .cxz-data-table td .recommend-number-missed, +.cxz-number-popup .recommend-number-missed, .recommend-number-dialog .recommend-number-missed, .recommend-number-content .recommend-number-missed { color: #999; + font-weight: 700; +} + +.cxz-data-table td .cxz-number-wrap--recommend { + display: inline-flex; + align-items: center; + max-width: 100%; + min-width: 0; + padding: 0; + border: 0; + background: transparent; + color: #de2103; + cursor: default; + font: inherit; + font-weight: 700; + line-height: 1.8; + vertical-align: middle; +} + +.cxz-data-table td .cxz-number-wrap--recommend .cxz-number-preview { + min-width: 0; + max-width: 100%; +} + +.cxz-data-table td .cxz-number-wrap--recommend .cxz-number-preview--full, +.cxz-data-table td .cxz-number-wrap--recommend.cxz-number-wrap--fit .cxz-number-preview--full { + display: inline-flex; + align-items: center; + overflow: hidden; + white-space: nowrap; +} + +.cxz-data-table td .cxz-number-wrap--recommend .cxz-number-preview--short, +.cxz-data-table td .cxz-number-wrap--recommend .cxz-number-more { + display: none; +} + +.cxz-data-table td .cxz-number-wrap--recommend.cxz-number-wrap--overflow { + cursor: pointer; +} + +.cxz-data-table td .cxz-number-wrap--recommend.cxz-number-wrap--overflow .cxz-number-preview--full { + display: none; +} + +.cxz-data-table td .cxz-number-wrap--recommend.cxz-number-wrap--overflow .cxz-number-preview--short { + display: inline-flex; + align-items: center; + overflow: hidden; + white-space: nowrap; +} + +.cxz-data-table td .cxz-number-wrap--recommend.cxz-number-wrap--overflow .cxz-number-more { + display: inline-flex; + flex: 0 0 auto; + margin-left: 6px; + color: #409eff; + font-size: 12px; + white-space: nowrap; +} + +.cxz-data-table td .cxz-number-wrap--recommend:hover .cxz-number-more { + color: #de2103; +} + +.cxz-number-wrap--recommend-full { + display: block; + color: #de2103; + font-size: 15px; + font-weight: 700; + line-height: 1.9; + white-space: normal; + word-break: break-all; +} + +.cxz-number-wrap--recommend-full .cxz-number-group { + display: inline; +} + +.cxz-number-popup { + padding: 18px 22px; +} + +.cxz-number-popup-title { + margin-bottom: 12px; + color: #102b6a; + font-size: 16px; + font-weight: 800; +} + +.cxz-number-popup .cxz-number-wrap--popup { + padding: 14px 16px; + border: 1px solid #ffd8d2; + border-radius: 6px; + background: #fff7f5; + max-height: 180px; + overflow-y: auto; +} + +.recommend-number-layer { + border-radius: 6px; + overflow: hidden; +} + +.recommend-number-layer .layui-layer-title { + border-bottom: 1px solid #f0f0f0; + font-size: 16px; + font-weight: 700; + height: 48px; + line-height: 48px; +} + +.recommend-number-layer .layui-layer-content { + padding: 0; +} + +.recommend-number-dialog { + padding: 18px 22px 8px; +} + +.recommend-number-content { + background: #fff7f5; + border: 1px solid #ffd8d2; + border-radius: 6px; + color: #de2103; + line-height: 1.9; + max-height: 180px; + overflow-y: auto; + padding: 14px 16px; + text-align: left; + word-break: break-all; +} + +.recommend-number-content .fushi234item { + display: block; + overflow: visible; + white-space: normal; + word-break: break-all; +} + +.recommend-number-actions { + display: flex; + justify-content: flex-start; + padding-top: 14px; +} + +.recommend-dialog-copy { + height: 30px; + padding: 0 14px; + border: none; + border-radius: 15px; + background: #102b6a; + color: #fff; + cursor: pointer; + font-size: 12px; + font-weight: 700; } .cxz-data-table td .cxz-status-hit { @@ -963,6 +1207,18 @@ font-weight: 700; } +.cxz-data-table td span[style*="#8c8c8c"] .caiinfo-status-label, +.cxz-data-table td span[style*="#8c8c8c"] .caiinfo-status-front, +.cxz-data-table td span[style*="#8c8c8c"] .caiinfo-status-back, +.cxz-data-table td span[style*="#8c8c8c"] .caiinfo-status-separator { + color: #8c8c8c; +} + +.cxz-data-table td .cxz-pending-publish-text { + color: #de2103; + font-weight: 700; +} + .cxz-data-table td .cxz-number-ball--wrong, .cxz-number-wrap--popup .cxz-number-ball--wrong { position: relative; diff --git a/public/css/finance.css b/public/css/finance.css index a8d3a55..33add92 100644 --- a/public/css/finance.css +++ b/public/css/finance.css @@ -320,7 +320,8 @@ /* Page wrapper */ .je-container { - max-width: 900px; + max-width: 1200px; + min-width: 1200px; margin: 0 auto; padding: 20px 16px; /* min-height: 100vh; */ @@ -1235,9 +1236,12 @@ ============================ */ @keyframes pulse-dot { - 0%, 100% { + + 0%, + 100% { opacity: 1; } + 50% { opacity: 0.3; } @@ -1248,6 +1252,7 @@ opacity: 0; transform: translateY(12px); } + to { opacity: 1; transform: translateY(0); @@ -1266,12 +1271,29 @@ animation-fill-mode: backwards; } -.cz-amount-item:nth-child(1) { animation-delay: 0.03s; } -.cz-amount-item:nth-child(2) { animation-delay: 0.06s; } -.cz-amount-item:nth-child(3) { animation-delay: 0.09s; } -.cz-amount-item:nth-child(4) { animation-delay: 0.12s; } -.cz-amount-item:nth-child(5) { animation-delay: 0.15s; } -.cz-amount-item:nth-child(6) { animation-delay: 0.18s; } +.cz-amount-item:nth-child(1) { + animation-delay: 0.03s; +} + +.cz-amount-item:nth-child(2) { + animation-delay: 0.06s; +} + +.cz-amount-item:nth-child(3) { + animation-delay: 0.09s; +} + +.cz-amount-item:nth-child(4) { + animation-delay: 0.12s; +} + +.cz-amount-item:nth-child(5) { + animation-delay: 0.15s; +} + +.cz-amount-item:nth-child(6) { + animation-delay: 0.18s; +} /* ============================ QR Code Section (Recharge) @@ -1386,6 +1408,7 @@ /* Tablets and small desktops */ @media (max-width: 768px) { + .cz-page, .je-container, .tx-page, @@ -1456,6 +1479,7 @@ /* Mobile devices */ @media (max-width: 640px) { + .cz-page, .je-container, .tx-page, @@ -1625,4 +1649,4 @@ .tx-balance-value { font-size: 18px; } -} +} \ No newline at end of file diff --git a/public/css/layout-fix.css b/public/css/layout-fix.css new file mode 100644 index 0000000..76f0224 --- /dev/null +++ b/public/css/layout-fix.css @@ -0,0 +1,19 @@ +html { + min-height: 100%; +} + +body { + min-height: 100vh; + display: flex; + flex-direction: column; +} + +body > main { + flex: 0 0 auto; +} + +body > footer.site-footer { + margin-top: auto; + padding-top: 28px; + flex-shrink: 0; +} diff --git a/public/css/pagination.css b/public/css/pagination.css index 51f2c2e..e655591 100644 --- a/public/css/pagination.css +++ b/public/css/pagination.css @@ -469,6 +469,66 @@ button.cxz-number-wrap { background: #f7f9ff; } +.cxz-data-table td .cxz-number-wrap--recommend .cxz-number-more { + height: auto; + margin-left: 6px; + padding: 0; + border: 0; + border-radius: 0; + background: transparent; + color: #409eff; + font-size: 12px; + font-weight: 700; + line-height: inherit; +} + +.cxz-data-table td .cxz-number-wrap--recommend:hover .cxz-number-more { + border-color: transparent; + background: transparent; + color: #de2103; +} + +.cxz-data-table td .cxz-number-wrap--recommend { + border: 0; + background: transparent; + box-shadow: none; +} + +.cxz-data-table td .cxz-number-wrap--recommend:hover { + background: transparent; + box-shadow: none; +} + +.cxz-number-popup .recommend-number-text, +.cxz-number-popup .recommend-separator { + color: #de2103; +} + +.cxz-number-popup .recommend-number-hit { + color: #de2103; +} + +.cxz-number-popup .recommend-number-blue { + color: #1c89f8; +} + +.cxz-number-popup .recommend-number-missed { + color: #999; +} + +.cxz-data-table td .cxz-number-wrap--recommend .recommend-number-text, +.cxz-data-table td .cxz-number-wrap--recommend .recommend-separator, +.cxz-data-table td .cxz-number-wrap--recommend .recommend-number-hit, +.cxz-data-table td .cxz-number-wrap--recommend .recommend-number-blue, +.cxz-data-table td .cxz-number-wrap--recommend .recommend-number-missed, +.cxz-number-popup .recommend-number-text, +.cxz-number-popup .recommend-separator, +.cxz-number-popup .recommend-number-hit, +.cxz-number-popup .recommend-number-blue, +.cxz-number-popup .recommend-number-missed { + font-weight: 700 !important; +} + .cxz-number-popup { padding: 20px 22px 22px; background: #fff; diff --git a/public/css/public.css b/public/css/public.css index 07a0f98..d041fbe 100644 --- a/public/css/public.css +++ b/public/css/public.css @@ -563,6 +563,7 @@ select { ---------------------------------------------------------- */ .cxz-main { max-width: 1200px; + min-width: 1200px; margin: 0 auto; padding: 20px; } @@ -1245,4 +1246,4 @@ select { height: auto; aspect-ratio: 1 / 1; } -} +} \ No newline at end of file diff --git a/public/css/publish.css b/public/css/publish.css index 535d8c1..19cb671 100644 --- a/public/css/publish.css +++ b/public/css/publish.css @@ -7,6 +7,7 @@ .publish-card, .cxz-form-card { width: 100%; + padding: 20px; margin: 26px auto 54px; overflow: hidden; border: 1px solid var(--border-light); @@ -430,6 +431,7 @@ } @media (max-width: 768px) { + .page-main, .page-wrap { width: calc(100% - 24px); @@ -437,6 +439,7 @@ .publish-card, .cxz-form-card { + margin: 18px auto 32px; border-radius: 16px; } @@ -465,4 +468,4 @@ .publish-tab-item:last-child { border-bottom: 0; } -} +} \ No newline at end of file diff --git a/utils/ApiClient.js b/utils/ApiClient.js index 6eda8e0..4adf7b2 100644 --- a/utils/ApiClient.js +++ b/utils/ApiClient.js @@ -704,7 +704,7 @@ const ApiClient = { shouldLoadMenuChildren() { if (typeof window === 'undefined') return true; - return /\/(?:cai|fufei)\.html(?:$|\?)/.test( + return /\/(?:cai|caiinfo|fufei)\.html(?:$|\?)/.test( window.location.pathname + window.location.search ); }, diff --git a/utils/CommonUtil.js b/utils/CommonUtil.js index b131b9a..3e7a888 100644 --- a/utils/CommonUtil.js +++ b/utils/CommonUtil.js @@ -876,7 +876,16 @@ const CommonUtil = { .replace(/"/g, '"') .replace(/'/g, '''); const escapeJs = (value) => escapeHtml(value).replace(/\\/g, '\\\\'); - const isMasked = (value) => /\*/.test(String(value || '')); + const isMasked = (value) => { + const text = String(value || '').trim(); + return ( + !text || + /\*/.test(text) || + text.indexOf('购买后可见') >= 0 || + text.indexOf('登录后购买可见') >= 0 || + text.indexOf('待发布') >= 0 + ); + }; const normalizeRecommend = (value) => String(value || '') .replace(/[,、]/g, ',') @@ -886,6 +895,10 @@ const CommonUtil = { .replace(/\+,/g, '+') .replace(/,\+/g, '+') .replace(/^,|,$/g, ''); + const formatIssueText = (issue) => { + const value = String(issue || '').trim(); + return /^\d{7,}$/.test(value) ? value.slice(-3) : value; + }; const splitNumbers = (value) => { const text = normalizeRecommend(value); if (!text || isMasked(text)) return []; @@ -898,73 +911,106 @@ const CommonUtil = { const text = String(value || '').trim(); return /^\d+$/.test(text) ? String(Number(text)) : text; }; - const getWrongNumbers = (item) => { - const codeResultWrong = (typeof ApiClient !== 'undefined' ? ApiClient.asArray(item && item.codeResults) : []) - .filter((result) => result && typeof result === 'object' && result.correct === false) - .map((result) => result.code || result.value || result.number || result.result || '') - .filter(Boolean); - const raw = - item.wrongNumbers || - item.wrongCode || - item.errorNumbers || - item.errorCode || - item.missNumbers || - item.missCode || - item.reverseNumbers || - item.reverseCode || - item.falseNumbers || - item.falseCode || - item.fanZhi || - item.fanzhi || - codeResultWrong.join(','); - const groups = splitNumbers(Array.isArray(raw) ? raw.join(',') : raw); - const values = groups.flat(); - return new Set( + const toNumberSet = (values) => + new Set( values.flatMap((num) => { const text = String(num); return [text, text.padStart(2, '0'), normalizeNumberKey(text)]; }) ); - }; - const getHitNumbers = (item) => { - const codeResultHit = (typeof ApiClient !== 'undefined' ? ApiClient.asArray(item && item.codeResults) : []) - .filter((result) => result && typeof result === 'object' && result.correct === true) - .map((result) => result.code || result.value || result.number || result.result || '') - .filter(Boolean); - const raw = - item.hitNumbers || - item.hitCode || - item.hitCodes || - item.correctNumbers || - item.correctCode || - item.correctCodes || - item.winNumbers || - item.winCode || - item.winCodes || - codeResultHit.join(','); - const groups = splitNumbers(Array.isArray(raw) ? raw.join(',') : raw); - const values = groups.flat(); - return new Set( - values.flatMap((num) => { - const text = String(num); - return [text, text.padStart(2, '0'), normalizeNumberKey(text)]; - }) + const getRecommendGroupSeparator = (value) => (String(value || '').includes('|') ? '|' : '+'); + const splitRecommendValues = (value) => splitNumbers(value).flat(); + const getArticleRecommendValue = (item) => + (item && (item.predictedCode || item.recommendCode || item.latestRecommend || item.recommend || item.content)) || ''; + const getArticleIssueValue = (item) => + (item && (item.issue || item.latestIssue || item.expect || item.period)) || ''; + const isMaskedRecommendText = (value) => isMasked(value); + const isTruthyFlag = (value) => + value === true || value === 1 || value === '1' || String(value).toLowerCase() === 'true'; + const isArticlePrePurchased = (item) => + Boolean( + item && + (isTruthyFlag(item.prePurchased) || + isTruthyFlag(item.pre_purchased) || + isTruthyFlag(item.prepaid) || + isTruthyFlag(item.prepaidArticle)) ); + const isArticlePrePurchase = (item) => + Boolean( + item && + (isTruthyFlag(item.prePurchase) || + isTruthyFlag(item.pre_purchase) || + isTruthyFlag(item.canPrePurchase) || + isTruthyFlag(item.preOrder)) + ); + const getArticleCodeNumbers = (item, fields) => { + let value = ''; + fields.some((field) => { + if (item && item[field] !== undefined && item[field] !== null && item[field] !== '') { + value = item[field]; + return true; + } + return false; + }); + if (Array.isArray(value)) { + return value.flatMap((entry) => + splitRecommendValues( + entry && typeof entry === 'object' + ? entry.code || entry.value || entry.number || entry.result || '' + : entry + ) + ); + } + return splitRecommendValues(value); }; - const getCodeResultMarks = (item, recommend) => { - const recommendGroups = splitNumbers(recommend); - const codeResults = - typeof ApiClient !== 'undefined' ? ApiClient.asArray(item && item.codeResults) : []; - if (!recommendGroups.length || !codeResults.length) return null; - - const flatCount = recommendGroups.reduce((total, group) => total + group.length, 0); + const getCodeResultNumbers = (item, correctValue) => + (typeof ApiClient !== 'undefined' ? ApiClient.asArray(item && item.codeResults) : []) + .filter((result) => result && typeof result === 'object' && result.correct === correctValue) + .flatMap((result) => splitRecommendValues(result.code || result.value || result.number || result.result || '')); + const getArticleHitNumbers = (item) => { + const fields = [ + 'hitCodes', + 'hitCode', + 'hitNumbers', + 'hits', + 'rightCodes', + 'rightCode', + 'rightNumbers', + 'correctCodes', + 'correctCode', + 'correctNumbers', + ]; + const hitNumbers = getArticleCodeNumbers(item, fields); + return hitNumbers.length ? hitNumbers : getCodeResultNumbers(item, true); + }; + const getArticleMissedNumbers = (item) => { + const fields = [ + 'missCodes', + 'missCode', + 'missNumbers', + 'missedCodes', + 'missedCode', + 'missedNumbers', + 'wrongCodes', + 'wrongCode', + 'wrongNumbers', + 'errorCodes', + 'errorCode', + 'errorNumbers', + ]; + const missedNumbers = getArticleCodeNumbers(item, fields); + return missedNumbers.length ? missedNumbers : getCodeResultNumbers(item, false); + }; + const getCodeResultCorrectGroups = (item, recommendGroups) => { + const codeResults = typeof ApiClient !== 'undefined' ? ApiClient.asArray(item && item.codeResults) : []; + if (!codeResults.length || !recommendGroups || !recommendGroups.length) return []; const marks = codeResults.map((result) => { if (!result || typeof result !== 'object') return null; return result.correct === true ? true : result.correct === false ? false : null; }); - if (!marks.some((mark) => mark === true || mark === false)) return null; - if (marks.length < flatCount) return null; - + if (!marks.some((mark) => mark === true || mark === false)) return []; + const recommendCount = recommendGroups.reduce((total, group) => total + group.length, 0); + if (marks.length < recommendCount) return []; let offset = 0; return recommendGroups.map((group) => { const groupMarks = marks.slice(offset, offset + group.length); @@ -972,67 +1018,121 @@ const CommonUtil = { return groupMarks; }); }; - const getPositionMarkerSets = (item, recommend) => { - const recommendGroups = splitNumbers(recommend); - if (!recommendGroups.length) return null; - + const getCodeResultMarkerGroups = (item, correctValue) => { const codeResults = typeof ApiClient !== 'undefined' ? ApiClient.asArray(item && item.codeResults) : []; - if (codeResults.length === recommendGroups.length) { - return codeResults.map((result) => { - if (result && typeof result === 'object' && result.correct !== false) { - return new Set(); - } + if (!codeResults.length) return []; + return codeResults + .map((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; - const values = splitNumbers(value).flat(); - return new Set( - values.flatMap((num) => { - const text = String(num); - return [text, text.padStart(2, '0'), normalizeNumberKey(text)]; - }) - ); - }); - } + return String(value || '').trim() ? [String(value).trim()] : []; + }) + .filter((group) => group.length > 0); + }; + const getArticleHitMarkerGroups = (item, recommendGroups) => { + if (!recommendGroups || !recommendGroups.length) return []; + const hitNumbers = getArticleHitNumbers(item); + if (hitNumbers.length === recommendGroups.length) return hitNumbers.map((num) => splitRecommendValues(num)); + const codeResultGroups = getCodeResultMarkerGroups(item, true); + return codeResultGroups.length === recommendGroups.length ? codeResultGroups : []; + }; + const getArticleMissedMarkerGroups = (item, recommendGroups) => { + if (!recommendGroups || recommendGroups.length <= 1) return []; + const missedNumbers = getArticleMissedNumbers(item); + if (missedNumbers.length === recommendGroups.length) return missedNumbers.map((num) => splitRecommendValues(num)); + const codeResultGroups = getCodeResultMarkerGroups(item, false); + return codeResultGroups.length === recommendGroups.length ? codeResultGroups : []; + }; + const isWinStatusText = (value) => /全对|正确|命中|中奖|中/.test(String(value || '')); + const isLoseStatusText = (value) => /全错|错误|错/.test(String(value || '')); + const isArticleDrawn = (item, openCode) => { + if (openCode && String(openCode).trim()) return true; + const text = getExplicitStatusText(item); + if (/正确|错误|已开奖|开奖号|开奖/.test(text) && !/未开奖|待开奖/.test(text)) return true; + if (isWinStatusText(text) || isLoseStatusText(text)) return true; + return Boolean( + item && + (item.isDrawn === true || + item.drawn === true || + item.opened === true || + item.openStatus === 1 || + item.openStatus === '1' || + item.drawStatus === 1 || + item.drawStatus === '1') + ); + }; + const isLatestArticle = (item, list) => { + if (item && isTruthyFlag(item.isLatest)) return true; + const currentId = getArticleId(item); + const firstId = getArticleId(Array.isArray(list) ? list[0] : null); + if (currentId && firstId) return String(currentId) === String(firstId); + const currentIssue = Number(getArticleIssueValue(item)); + const issues = (Array.isArray(list) ? list : []) + .map((row) => Number(getArticleIssueValue(row))) + .filter((issue) => Number.isFinite(issue)); + return issues.length > 0 && Number.isFinite(currentIssue) && currentIssue === Math.max(...issues); + }; + const isArticleMasked = (item, recommend) => + Boolean( + item && + (item.masked === true || + item.masked === 1 || + item.masked === '1' || + item.masked === 'true' || + isMaskedRecommendText(recommend)) + ); + const canShowArticleRecommend = (item, isLatestIssue, hasDrawResult, unlockedByVisibleRecommend, recommend) => { + if (!isArticleMasked(item, recommend)) return true; + if (hasDrawResult) return true; + if (!isLatestIssue) return true; + return Boolean((ApiClient.getToken ? ApiClient.getToken() : this.getToken()) && (isPurchased(item) || unlockedByVisibleRecommend)); + }; + const shouldShowBuyButton = (item, isLatestIssue, hasDrawResult, purchased, recommend) => + Boolean(isLatestIssue && !hasDrawResult && isArticleMasked(item, recommend) && !purchased); + const shouldShowPrePurchaseButton = (item, purchased) => + Boolean( + !purchased && + !isArticlePrePurchased(item) && + (isArticlePrePurchase(item) || (!getArticleId(item) && getExplicitStatusText(item) === '待发布')) + ); + const getRecommendLockedText = () => + ApiClient.getToken && ApiClient.getToken() ? '购买后可见' : '登录后购买可见'; + const applyRecommendHitOptions = (options, item, statusText) => { + const recommendGroups = splitNumbers(getArticleRecommendValue(item)); + const codeResultGroupMarks = getCodeResultCorrectGroups(item, recommendGroups); + const hitGroups = getArticleHitMarkerGroups(item, recommendGroups); + const missedGroups = getArticleMissedMarkerGroups(item, recommendGroups); + const hitNumbers = hitGroups.length ? hitGroups.flat() : getArticleHitNumbers(item); + const missedNumbers = missedGroups.length ? missedGroups.flat() : getArticleMissedNumbers(item); + const isFinalStatus = isWinStatusText(statusText) || isLoseStatusText(statusText); - const rawStatus = String( - item.status || - item.resultText || - item.statusText || - item.drawStatusText || - item.openStatusText || - (typeof item.result === 'string' ? item.result : '') - ).trim(); - const resultValue = item.result ?? item.winStatus; - const isMissResult = - /全错|错误|未中|错/.test(rawStatus) || - resultValue === 0 || - resultValue === '0' || - resultValue === false; - if (!isMissResult) return null; - - const openGroups = splitNumbers(item.openCode || item.lotteryResult || item.resultCode || item.openResult || ''); - const openValues = openGroups.flat(); - if (openValues.length === recommendGroups.length) { - return openValues.map((num) => { - const text = String(num); - return new Set([text, text.padStart(2, '0'), normalizeNumberKey(text)]); - }); - } - - return null; + options.codeResultMarks = codeResultGroupMarks.length ? codeResultGroupMarks : null; + options.hitSet = codeResultGroupMarks.length || (hitNumbers.length > 0 && isFinalStatus) ? toNumberSet(hitNumbers) : new Set(); + options.hitGroupSets = + hitGroups.length === recommendGroups.length ? hitGroups.map((group) => toNumberSet(group)) : null; + options.wrongSet = !codeResultGroupMarks.length && missedNumbers.length > 0 && isFinalStatus ? toNumberSet(missedNumbers) : new Set(); + options.wrongGroupSets = + !codeResultGroupMarks.length && missedGroups.length === recommendGroups.length + ? missedGroups.map((group) => toNumberSet(group)) + : null; + return options; }; const renderBalls = (value, options = {}) => { const groups = splitNumbers(value); if (!groups.length) return '--'; const wrongSet = options.wrongSet || new Set(); const hitSet = options.hitSet || new Set(); - const markerSets = options.markerSets || null; + const markerSets = options.wrongGroupSets || null; + const hitGroupSets = options.hitGroupSets || null; const codeResultMarks = options.codeResultMarks || null; const isRecommend = options.type === 'recommend'; const isOpen = options.type === 'open'; const count = groups.reduce((total, group) => total + group.length, 0); + const groupSeparator = getRecommendGroupSeparator(value); const buildBody = (limit) => { let rendered = 0; return groups @@ -1046,6 +1146,7 @@ const CommonUtil = { const isTextToken = /[^\d]/.test(String(num)); const normalized = String(num).padStart(2, '0'); const markerSet = Array.isArray(markerSets) ? markerSets[groupIndex] : null; + const hitGroupSet = Array.isArray(hitGroupSets) ? hitGroupSets[groupIndex] : null; const codeResultMark = Array.isArray(codeResultMarks) && codeResultMarks[groupIndex] && @@ -1053,7 +1154,9 @@ const CommonUtil = { ? codeResultMarks[groupIndex][numberIndex] : null; const isExplicitHit = - hitSet.has(normalized) || hitSet.has(String(num)) || hitSet.has(normalizeNumberKey(num)); + (hitGroupSet + ? hitGroupSet.has(normalized) || hitGroupSet.has(String(num)) || hitGroupSet.has(normalizeNumberKey(num)) + : hitSet.has(normalized) || hitSet.has(String(num)) || hitSet.has(normalizeNumberKey(num))); const isHit = codeResultMark === true || isExplicitHit; const isWrong = isHit ? false @@ -1065,24 +1168,34 @@ const CommonUtil = { const isBlueGroup = (isOpen && groupIndex > 0) || (Number.isInteger(options.blueStartGroupIndex) && groupIndex >= options.blueStartGroupIndex); - const classes = [ - isTextToken ? 'cxz-number-text' : isBlueGroup ? 'bule-q' : 'red-q', - isTextToken ? '' : 'cxz-number-ball', - isWrong ? 'cxz-number-ball--wrong' : '', - isHit ? 'cxz-number-ball--hit' : '', - ] - .filter(Boolean) - .join(' '); + const classes = isRecommend + ? [ + 'recommend-number-text', + isBlueGroup ? 'recommend-number-blue' : '', + isWrong ? 'recommend-number-missed' : '', + isHit ? 'recommend-number-hit' : '', + ] + .filter(Boolean) + .join(' ') + : [ + isTextToken ? 'cxz-number-text' : isBlueGroup ? 'bule-q' : 'red-q', + isTextToken ? '' : 'cxz-number-ball', + isWrong ? 'cxz-number-ball--wrong' : '', + isHit ? 'cxz-number-ball--hit' : '', + ] + .filter(Boolean) + .join(' '); const title = isWrong ? '标记号码' : options.type === 'open' ? '开奖号码' : '推荐号码'; return `${escapeHtml(num)}`; }) - .join(''); + .join(isRecommend ? ',' : ''); return `${balls}`; }) - .join(''); + .filter(Boolean) + .join(isRecommend ? `${escapeHtml(groupSeparator)}` : ''); }; const fullBody = buildBody(); - const shortBody = buildBody(5); + const shortBody = buildBody(4); const body = isRecommend ? fullBody : buildBody(); const classes = [ 'cxz-number-wrap', @@ -1098,8 +1211,8 @@ const CommonUtil = { if (isRecommend && count > 4) { window.__cxzNumberPopups = window.__cxzNumberPopups || {}; const popupId = `cxz-number-${Date.now()}-${Math.random().toString(36).slice(2)}`; - window.__cxzNumberPopups[popupId] = `
推荐号码 · 共${count}码
${fullBody}
`; - meta += '查看更多'; + window.__cxzNumberPopups[popupId] = `
完整推荐号码 · 共${count}码
${fullBody}
`; + meta += '点击查看全部'; return ``; } return `
${body}${meta}
`; @@ -1133,7 +1246,8 @@ const CommonUtil = { } alert($(content).text()); }; - const getArticleId = (item) => item.id || item.articleId || item.payArticleId || item.latestArticleId || ''; + const getArticleId = (item) => + item.id || item.articleId || item.payArticleId || item.latestArticleId || item.contentId || item.recordId || ''; const isCollected = (value) => value === 1 || value === 2 || value === true || value === '1' || value === '2'; const isPurchased = (item) => item.isBuy === 1 || @@ -1346,7 +1460,7 @@ const CommonUtil = { return; } - list.forEach((item) => { + list.forEach((item, index) => { const menuMeta = window.__cxzCaiinfoMenuMeta || {}; const metaLottery = menuMeta.lottery || {}; const metaPlay = menuMeta.current || {}; @@ -1372,35 +1486,55 @@ const CommonUtil = { const openCode = item.openCode || item.lotteryResult || item.resultCode || item.openResult || ''; const recommend = item.predictedCode || item.recommendCode || item.latestRecommend || item.recommend || ''; const openRows = /快乐8|kl8/i.test(`${lotteryName}${currentCode}`) ? 2 : 1; - const locked = isMasked(recommend); - const canCopy = recommend && !locked; const status = getStatus(item, openCode, playName); const price = Number(item.price || item.amount || item.points || 0); const articleId = getArticleId(item); const blueStartGroupIndex = /大乐透|双色球|dlt|ssq/i.test(`${lotteryName}${currentCode}`) ? 1 : undefined; - const unlockedByVisibleRecommend = !openCode && price > 0 && !locked && !!recommend; + const hasDrawResult = isArticleDrawn(item, openCode); + const isLatestIssue = isLatestArticle(item, list); + const hasVisibleRecommend = Boolean(recommend && !isMaskedRecommendText(recommend)); + const unlockedByVisibleRecommend = + isLatestIssue && !hasDrawResult && price > 0 && !isArticleMasked(item, recommend) && hasVisibleRecommend; const purchased = isPurchased(item) || unlockedByVisibleRecommend; + const prePurchased = isArticlePrePurchased(item); + const canShowRecommend = canShowArticleRecommend( + item, + isLatestIssue, + hasDrawResult, + unlockedByVisibleRecommend, + recommend + ); + const canCopy = canShowRecommend && hasVisibleRecommend; const collectText = isCollected(item.isCollect) ? '已收藏' : '收藏'; const collectClass = isCollected(item.isCollect) ? ' collected' : ''; - const buyButton = - locked && !openCode && !purchased && articleId - ? ` ` - : ''; - const purchasedBadge = purchased ? '已购买 ' : ''; - const wrongSet = getWrongNumbers(item); - const hitSet = getHitNumbers(item); - const codeResultMarks = getCodeResultMarks(item, recommend); - const markerSets = getPositionMarkerSets(item, recommend); - const recommendHtml = locked - ? '购买后可见' - : renderBalls(recommend, { - type: 'recommend', - wrongSet, - hitSet, - markerSets: codeResultMarks ? null : markerSets, - codeResultMarks, - blueStartGroupIndex, - }); + let actionPrefix = ''; + if (prePurchased) { + actionPrefix = '已预购 '; + } else if (shouldShowPrePurchaseButton(item, purchased) && typeof window.openPrePurchaseDialog === 'function') { + actionPrefix = ` `; + } else if (shouldShowBuyButton(item, isLatestIssue, hasDrawResult, purchased, recommend) && articleId) { + actionPrefix = ` `; + } else if (purchased) { + actionPrefix = '已购买 '; + } + const recommendOptions = applyRecommendHitOptions( + { + type: 'recommend', + blueStartGroupIndex, + }, + item, + status.text + ); + let recommendHtml = ''; + if (String(recommend || '').trim() === '待发布') { + recommendHtml = '待发布'; + } else if (canCopy) { + recommendHtml = renderBalls(recommend, recommendOptions); + } else if (recommend) { + recommendHtml = `${escapeHtml(getRecommendLockedText())}`; + } else { + recommendHtml = '暂无'; + } const copyHtml = canCopy ? `` : '-'; @@ -1409,13 +1543,13 @@ const CommonUtil = { ` ${escapeHtml(nameText)} ${escapeHtml(playName)} - ${escapeHtml(issue)} + ${escapeHtml(formatIssueText(issue))} ${renderBalls(openCode, { type: 'open', openRows })} ${recommendHtml} ${copyHtml} ${status.html || escapeHtml(status.text)} ${Number.isFinite(price) ? price.toFixed(2) : '0.00'} - ${purchasedBadge}${buyButton} + ${actionPrefix} ` ); });