修改功能,现已测试注册登录忘记密码

This commit is contained in:
rain
2026-06-12 11:42:49 +08:00
parent 0bf2f573ed
commit cfc64c69e5
26 changed files with 2238 additions and 898 deletions
+59
View File
@@ -228,7 +228,66 @@
colorLight: "#ffffff",
});
var DEFAULT_DOWNLOAD_INFO = {
appName: "彩先知",
version: "1.0.0",
intro: "随时随地查看预测分析,不错过任何精彩推荐",
downloadUrl: downloadUrl,
};
function normalizeDownloadInfo(response) {
var data = response && (response.data || response.result || response);
if (!data || typeof data !== "object") return DEFAULT_DOWNLOAD_INFO;
return {
appName: data.appName || data.name || DEFAULT_DOWNLOAD_INFO.appName,
version: data.version || data.versionName || DEFAULT_DOWNLOAD_INFO.version,
intro:
data.intro ||
data.description ||
data.remark ||
DEFAULT_DOWNLOAD_INFO.intro,
downloadUrl:
data.downloadUrl ||
data.apkUrl ||
data.androidUrl ||
data.url ||
DEFAULT_DOWNLOAD_INFO.downloadUrl,
iosUrl: data.iosUrl || data.iphoneUrl || data.appStoreUrl || "",
};
}
function applyDownloadInfo(info) {
var nextInfo = $.extend({}, DEFAULT_DOWNLOAD_INFO, info || {});
if (nextInfo.appName) {
$(".hero-text h1").html("下载" + CommonUtil.escapeHtml(nextInfo.appName) + "<span>APP</span>");
}
if (nextInfo.intro) {
$(".hero-text p").first().text(nextInfo.intro);
}
if (nextInfo.downloadUrl) {
$("#androidBtn").attr("href", nextInfo.downloadUrl);
CommonUtil.generateQRCode(nextInfo.downloadUrl, "downloadQrImg", {
width: 180,
height: 180,
colorDark: "#102b6a",
colorLight: "#ffffff",
});
}
if (nextInfo.iosUrl) $("#iosBtn").attr("href", nextInfo.iosUrl);
}
function loadDownloadInfo() {
return ApiClient.post(ApiClient.API.appDownloadInfo, {}, { publicRequest: true })
.then(function (response) {
if (ApiClient.isSuccess(response) || response.data || response.result) {
applyDownloadInfo(normalizeDownloadInfo(response));
}
})
.catch(function () {});
}
// Load download links from page config
loadDownloadInfo();
ApiClient.post("/api/web/index", {})
.then(function (result) {
if (result.data && result.data.webConfigs) {
+81 -366
View File
@@ -13,6 +13,7 @@
<link rel="stylesheet" href="../public/css/public.css" />
<link rel="stylesheet" href="../public/css/publish.css" />
<link rel="stylesheet" href="../public/css/finance.css" />
<link rel="stylesheet" href="../public/css/chongzhi.css" />
</head>
<body>
<!-- Header -->
@@ -33,7 +34,7 @@
>免费注册</a
>
</div>
<div id="loggedInBox" class="topbar__logged" style="display: none">
<div id="loggedInBox" class="topbar__logged cz-hidden">
<a href="usercenter.html" class="topbar__profile">
<img
id="userAvatar"
@@ -66,8 +67,7 @@
<div class="cz-balance-bar">
<div class="cz-balance-bar__item">
<div
class="cz-balance-bar__icon"
style="background: rgba(16, 43, 106, 0.08); color: var(--primary)"
class="cz-balance-bar__icon account"
>
<i class="layui-icon layui-icon-rmb"></i>
</div>
@@ -103,7 +103,7 @@
</div>
<!-- QR Code Payment Section -->
<div class="cxz-form-card" id="qrcodeCard" style="display: none">
<div class="cxz-form-card cz-hidden" id="qrcodeCard">
<div class="cz-qr-section" id="qrSection">
<div class="cz-qr-header">
<div class="cz-qr-icon">
@@ -127,7 +127,7 @@
</button>
</div>
<div class="cz-pay-result" id="paymentSuccess" style="display: none">
<div class="cz-pay-result cz-hidden" id="paymentSuccess">
<div class="cz-pay-result__icon success">
<i class="layui-icon layui-icon-ok-circle"></i>
</div>
@@ -136,7 +136,7 @@
<a href="usercenter.html" class="cz-pay-result__btn">返回个人中心</a>
</div>
<div class="cz-pay-result" id="paymentTimeout" style="display: none">
<div class="cz-pay-result cz-hidden" id="paymentTimeout">
<div class="cz-pay-result__icon timeout">
<i class="layui-icon layui-icon-close-fill"></i>
</div>
@@ -176,7 +176,7 @@
id="beian"
href="https://beian.miit.gov.cn/"
target="_blank"
style="font-size: 12px; color: rgba(255, 255, 255, 0.5)"
class="cz-footer-link"
>工信部备案号</a
>
<span id="additional-info">本站仅供数据分析参考</span>
@@ -193,359 +193,6 @@
</button>
<script src="../public/js/qrcode.min.js"></script>
<style>
/* Balance Bar */
.cz-balance-bar {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 16px;
margin-bottom: 24px;
}
.cz-balance-bar__item {
background: var(--white);
border-radius: var(--radius-lg);
padding: 20px 24px;
box-shadow: var(--shadow-sm);
display: flex;
align-items: center;
gap: 16px;
border: 1px solid var(--border-light);
transition: all 0.3s;
}
.cz-balance-bar__item:hover {
border-color: transparent;
box-shadow: var(--shadow-md);
transform: translateY(-2px);
}
.cz-balance-bar__icon {
width: 48px;
height: 48px;
border-radius: 14px;
display: flex;
align-items: center;
justify-content: center;
font-size: 22px;
flex-shrink: 0;
}
.cz-balance-bar__label {
font-size: 13px;
color: var(--text-muted);
}
.cz-balance-bar__value {
font-size: 24px;
font-weight: 800;
color: var(--text-dark);
line-height: 1.2;
}
.cz-balance-bar__value .unit {
font-size: 14px;
font-weight: 400;
color: var(--text-muted);
margin-left: 2px;
}
/* Package Grid */
.cz-package-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 14px;
margin-bottom: 24px;
}
.cz-package-loading {
grid-column: 1/-1;
text-align: center;
padding: 40px;
color: var(--text-muted);
}
.cz-package-loading i {
font-size: 28px;
display: block;
margin-bottom: 8px;
color: var(--primary);
}
.cz-pkg {
position: relative;
background: var(--bg-light);
border: 2px solid var(--border-light);
border-radius: var(--radius-lg);
padding: 20px 16px;
text-align: center;
cursor: pointer;
transition: all 0.3s;
user-select: none;
overflow: hidden;
}
.cz-pkg:hover {
border-color: var(--primary);
transform: translateY(-2px);
box-shadow: var(--shadow-sm);
}
.cz-pkg.selected {
border-color: var(--primary);
background: rgba(16, 43, 106, 0.04);
box-shadow: 0 0 0 3px rgba(16, 43, 106, 0.12);
}
.cz-pkg.selected::after {
content: "\2713";
position: absolute;
top: 0;
right: 0;
width: 24px;
height: 24px;
background: var(--primary);
color: #fff;
font-size: 12px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 0 var(--radius-lg) 0 var(--radius-lg);
}
.cz-pkg__price {
font-size: 28px;
font-weight: 800;
color: var(--primary);
line-height: 1.2;
}
.cz-pkg__price span {
font-size: 14px;
font-weight: 600;
}
.cz-pkg__credit {
display: inline-block;
margin-top: 8px;
padding: 3px 10px;
border-radius: 999px;
font-size: 12px;
font-weight: 700;
background: rgba(253, 185, 51, 0.12);
color: var(--gold);
}
.cz-pkg__bonus {
display: inline-block;
margin-top: 4px;
padding: 2px 8px;
border-radius: 999px;
font-size: 11px;
font-weight: 600;
background: rgba(241, 90, 34, 0.1);
color: var(--orange);
}
.cz-pkg.selected .cz-pkg__price {
color: var(--primary);
}
/* Hot / Recommend tag */
.cz-pkg__tag {
position: absolute;
top: 8px;
left: 8px;
padding: 2px 8px;
border-radius: 999px;
font-size: 10px;
font-weight: 700;
letter-spacing: 0.5px;
color: #fff;
}
.cz-pkg__tag.hot {
background: linear-gradient(135deg, #ff6b35, #f15a22);
}
.cz-pkg__tag.rec {
background: linear-gradient(135deg, var(--primary), #1a3f8f);
}
/* QR Code Section */
.cz-qr-section {
text-align: center;
padding: 24px 0;
}
.cz-qr-header {
margin-bottom: 16px;
}
.cz-qr-icon {
width: 48px;
height: 48px;
border-radius: 50%;
background: rgba(7, 193, 96, 0.1);
color: #07c160;
display: inline-flex;
align-items: center;
justify-content: center;
font-size: 24px;
margin-bottom: 8px;
}
.cz-qr-title {
font-size: 18px;
font-weight: 700;
color: var(--text-dark);
}
.cz-qr-subtitle {
font-size: 13px;
color: var(--text-muted);
margin-top: 4px;
}
.cz-qr-amount {
font-size: 32px;
font-weight: 800;
color: var(--orange);
margin: 16px 0;
}
.cz-qr-amount span {
font-size: 16px;
font-weight: 600;
color: var(--text-muted);
}
.cz-qr-wrap {
display: inline-block;
padding: 16px;
background: #fff;
border: 2px solid var(--border-light);
border-radius: var(--radius-lg);
margin-bottom: 12px;
}
.cz-qr-wrap img,
.cz-qr-wrap canvas {
display: block;
width: 200px;
height: 200px;
}
.cz-qr-hint {
font-size: 13px;
color: var(--text-muted);
margin-top: 8px;
}
.cz-qr-timer {
font-size: 14px;
color: var(--orange);
margin-top: 8px;
font-weight: 700;
}
.cz-qr-cancel {
margin-top: 16px;
padding: 8px 24px;
border: 1px solid var(--border-light);
border-radius: 999px;
background: transparent;
color: var(--text-muted);
font-size: 13px;
cursor: pointer;
transition: all 0.25s;
}
.cz-qr-cancel:hover {
border-color: var(--primary);
color: var(--primary);
}
/* Payment Result */
.cz-pay-result {
text-align: center;
padding: 40px 0;
}
.cz-pay-result__icon {
width: 72px;
height: 72px;
border-radius: 50%;
display: inline-flex;
align-items: center;
justify-content: center;
font-size: 36px;
margin-bottom: 16px;
}
.cz-pay-result__icon.success {
background: rgba(40, 167, 69, 0.12);
color: var(--green);
}
.cz-pay-result__icon.timeout {
background: rgba(255, 193, 7, 0.15);
color: #ffc107;
}
.cz-pay-result__title {
font-size: 20px;
font-weight: 700;
color: var(--text-dark);
margin-bottom: 8px;
}
.cz-pay-result__desc {
font-size: 13px;
color: var(--text-muted);
margin-bottom: 20px;
}
.cz-pay-result__btn {
display: inline-block;
padding: 10px 32px;
border-radius: 999px;
font-size: 14px;
font-weight: 700;
background: linear-gradient(135deg, var(--primary), #1a3f8f);
color: #fff;
text-decoration: none;
cursor: pointer;
border: none;
transition: all 0.3s;
}
.cz-pay-result__btn:hover {
box-shadow: 0 6px 20px rgba(16, 43, 106, 0.4);
transform: translateY(-2px);
}
/* Tips Card */
.cz-tips-card {
background: var(--white);
border-radius: var(--radius-lg);
padding: 20px 24px;
border: 1px solid var(--border-light);
margin-top: 24px;
}
.cz-tips-title {
font-size: 14px;
font-weight: 700;
color: var(--text-dark);
margin-bottom: 12px;
}
.cz-tips-title i {
color: var(--primary);
margin-right: 4px;
}
.cz-tips-list {
margin: 0;
padding-left: 18px;
}
.cz-tips-list li {
font-size: 12px;
color: var(--text-muted);
line-height: 2;
position: relative;
}
/* Responsive */
@media (max-width: 768px) {
.cz-balance-bar {
grid-template-columns: 1fr;
}
.cz-package-grid {
grid-template-columns: repeat(2, 1fr);
gap: 10px;
}
.cz-pkg {
padding: 16px 12px;
}
.cz-pkg__price {
font-size: 22px;
}
}
@media (max-width: 480px) {
.cz-package-grid {
grid-template-columns: repeat(2, 1fr);
gap: 8px;
}
.cz-pkg__price {
font-size: 20px;
}
.cz-pkg__credit {
font-size: 11px;
}
}
</style>
<script>
const $ = layui.$;
var configList = [];
@@ -556,6 +203,9 @@
var countdownSeconds = 300;
var pollingStartTime = 0;
var MAX_POLLING_DURATION = 300000;
var POLLING_INTERVAL = 3000;
var currentRechargeOutTradeNo = "";
var currentRechargeExpireTime = 0;
function escapeHtml(value) {
return String(value == null ? "" : value)
@@ -643,7 +293,7 @@
}
})
.catch(function () {
renderEmptyPackages("加载失败,请刷新重试");
renderEmptyPackages("加载失败,请刷新重试");
});
}
@@ -751,12 +401,18 @@
var data = ApiClient.pickData ? ApiClient.pickData(res) : res.data;
if (ApiClient.isSuccess(res) && data) {
var payUrl = getRechargePaymentUrl(data);
var outTradeNo = getRechargeOutTradeNo(data);
var expireTime = getRechargeExpireTime(data);
var amount = data.amount || data.paidAmount || selectedConfigAmount;
if (!payUrl) {
layui.layer.msg("\u8ba2\u5355\u672a\u8fd4\u56de\u652f\u4ed8\u4e8c\u7ef4\u7801\u5185\u5bb9", { icon: 2 });
return;
}
showQRCode(payUrl, amount);
if (!outTradeNo) {
layui.layer.msg("充值订单未返回订单号,无法查询支付状态", { icon: 2 });
return;
}
showQRCode(payUrl, amount, outTradeNo, expireTime);
} else {
layui.layer.msg(res.msg || "创建订单失败", { icon: 2 });
}
@@ -772,9 +428,11 @@
if (!data || typeof data !== "object") return "";
return (
data.qrCode ||
data.qrcode ||
data.payUrl ||
data.paymentUrl ||
data.codeUrl ||
data.code_url ||
data.qrCodeUrl ||
data.nativeUrl ||
data.url ||
@@ -782,8 +440,22 @@
);
}
function getRechargeOutTradeNo(data) {
if (!data || typeof data !== "object") return "";
return data.outTradeNo || data.out_trade_no || data.orderNo || data.order_no || data.tradeNo || data.trade_no || "";
}
function getRechargeExpireTime(data) {
if (!data || typeof data !== "object") return 0;
var value = data.expireTime || data.expire_time || data.expiredAt || data.expired_at || data.expiresAt || data.expires_at;
if (!value) return Date.now() + MAX_POLLING_DURATION;
var parsed = typeof value === "number" ? value : Date.parse(value);
if (!parsed || Number.isNaN(parsed)) return Date.now() + MAX_POLLING_DURATION;
return parsed < 10000000000 ? parsed * 1000 : parsed;
}
// Show QR code
function showQRCode(url, amount) {
function showQRCode(url, amount, outTradeNo, expireTime) {
$("#qrcodeCard").show();
$("#qrSection").show();
$("#paymentSuccess").hide();
@@ -803,7 +475,10 @@
countdownSeconds = 300;
pollingStartTime = Date.now();
currentRechargeOutTradeNo = outTradeNo || "";
currentRechargeExpireTime = expireTime || Date.now() + MAX_POLLING_DURATION;
startCountdown();
startPolling();
$("html,body").animate(
{ scrollTop: $("#qrcodeCard").offset().top - 20 },
@@ -839,7 +514,7 @@
function doPoll() {
var elapsed = Date.now() - pollingStartTime;
if (elapsed >= MAX_POLLING_DURATION) {
if (elapsed >= MAX_POLLING_DURATION || Date.now() >= currentRechargeExpireTime) {
stopPolling();
clearInterval(countdownTimer);
$("#qrSection").hide();
@@ -847,14 +522,54 @@
return;
}
scheduleNextPoll();
if (!currentRechargeOutTradeNo) {
scheduleNextPoll();
return;
}
queryRechargeOrderStatus(currentRechargeOutTradeNo)
.then(function (paid) {
if (paid) {
stopPolling();
if (countdownTimer) clearInterval(countdownTimer);
$("#qrSection").hide();
$("#paymentTimeout").hide();
$("#paymentSuccess").show();
layui.layer.msg("充值成功", { icon: 1 });
return;
}
scheduleNextPoll();
})
.catch(function () {
scheduleNextPoll();
});
}
function scheduleNextPoll() {
var delay = 1000 + Math.floor(Math.random() * 4000);
var delay = POLLING_INTERVAL + Math.floor(Math.random() * 1200);
pollingTimer = setTimeout(doPoll, delay);
}
function queryRechargeOrderStatus(outTradeNo) {
return ApiClient.get(ApiClient.API.walletRechargeOrderStatus(outTradeNo))
.then(function (res) {
if (!ApiClient.isSuccess(res)) return false;
var data = ApiClient.pickData ? ApiClient.pickData(res, {}) : res.data || {};
var status = String(data.status || data.payStatus || data.walletStatus || data.orderStatus || data.tradeStatus || "").toLowerCase();
return (
data.paid === true ||
data.success === true ||
data.paySuccess === true ||
status === "success" ||
status === "paid" ||
status === "2" ||
status === "1" ||
Number(data.status) === 2 ||
Number(data.payStatus) === 2
);
});
}
function stopPolling() {
if (pollingTimer) {
clearTimeout(pollingTimer);
+93 -40
View File
@@ -81,7 +81,7 @@
提示:实习期为三天,连续每天发布2篇以上的优质免费文章审核通过后,自动升级为正式专家。
</div>
<div class="publish-tip-item">
免费文章标准:标题20字以上(含期号、彩种、专家名称、标题),正文500字以上,需原创。
免费文章标准:标题15-30字(含期号、彩种、专家名称、标题),正文500字以上,需原创。
</div>
</div>
@@ -105,11 +105,11 @@
class="form-input"
id="articleTitle"
placeholder="请输入文章标题(含期号、彩种、标题)"
maxlength="100"
maxlength="30"
oninput="updateTitleCount()"
/>
<span class="form-input-count"
><span id="titleCount">0</span>/100</span
><span id="titleCount">0</span>/30</span
>
</div>
</div>
@@ -175,6 +175,13 @@
var layer;
var editor;
var pageReady = false;
var publishPermission = {
canFree: false,
canPaid: false,
};
var REQUIRED_DAILY_FREE_ARTICLES = 2;
var FREE_TITLE_MIN_LENGTH = 15;
var FREE_TITLE_MAX_LENGTH = 30;
function autoFormatEditorContent() {
if (!editor) return;
@@ -227,8 +234,9 @@
renderLoginState();
renderNav();
CommonUtil.requireAuth(function () {
loadPublishIdentity().then(function (allowed) {
if (!allowed) {
loadPublishPermission().then(function (permission) {
publishPermission = permission;
if (!permission.canFree) {
layer.alert(
"您还不是实习或正式专家,暂不能发布文章。",
{ title: "暂无权限" },
@@ -381,48 +389,89 @@
});
}
function checkPaidPublishPermission() {
function parsePublishPermission(userData) {
var status = String(
userData.expertStatus || userData.expert_status || "",
).toLowerCase();
var isIntern = /实习|intern|trial/.test(status);
var isRegularByStatus = /正式|regular|formal/.test(status);
var hasExpertIdentity =
isTruthy(userData.is_expert) ||
isTruthy(userData.isExpert) ||
isTruthy(userData.expert) ||
isTruthy(userData.is_regular_expert) ||
isTruthy(userData.isRegularExpert) ||
isTruthy(userData.regularExpert) ||
isIntern ||
isRegularByStatus;
var isRegularExpert =
isTruthy(userData.is_regular_expert) ||
isTruthy(userData.isRegularExpert) ||
isTruthy(userData.regularExpert) ||
isRegularByStatus;
return {
hasExpertIdentity: hasExpertIdentity,
isRegularExpert: hasExpertIdentity && isRegularExpert,
canFree: hasExpertIdentity,
canPaid: false,
todayFreeArticleCount: 0,
paidReason: isRegularExpert
? "今天需先发布2篇审核通过的免费文章后,才能发布付费文章"
: "正式专家才能发布付费文章",
};
}
function loadPublishPermission() {
return ApiClient.get(ApiClient.API.mineSummary)
.then(function (res) {
if (!ApiClient.isSuccess(res) || !res.data) {
return { canPaid: false, reason: "暂时无法获取发布权限。" };
return {
hasExpertIdentity: false,
isRegularExpert: false,
canFree: false,
canPaid: false,
todayFreeArticleCount: 0,
paidReason: "暂时无法获取发布权限。",
};
}
var status = String(
res.data.expertStatus || res.data.expert_status || "",
).toLowerCase();
var hasExpertIdentity =
isTruthy(res.data.is_expert) ||
isTruthy(res.data.isExpert) ||
isTruthy(res.data.expert) ||
isTruthy(res.data.is_regular_expert) ||
isTruthy(res.data.isRegularExpert) ||
isTruthy(res.data.regularExpert) ||
isTruthy(res.data.internExpert) ||
isTruthy(res.data.isInternExpert) ||
/实习|intern|trial|正式|regular|formal/.test(status);
if (!hasExpertIdentity) {
return { canPaid: false, reason: "专家才能发布付费文章。" };
}
var permission = parsePublishPermission(res.data);
if (!permission.canFree || !permission.isRegularExpert) return permission;
return loadTodayPublishedFreeCount().then(function (count) {
return {
canPaid: count >= 2,
reason:
count >= 2
? ""
: "今天需先发布2篇审核通过的免费文章后,才能发布付费文章(当前" +
count +
"篇)。",
};
permission.todayFreeArticleCount = count;
permission.canPaid = count >= REQUIRED_DAILY_FREE_ARTICLES;
permission.paidReason = permission.canPaid
? ""
: "今天需先发布2篇审核通过的免费文章后,才能发布付费文章(当前" +
count +
"篇)。";
return permission;
});
})
.catch(function () {
return { canPaid: false, reason: "暂时无法获取发布权限。" };
return {
hasExpertIdentity: false,
isRegularExpert: false,
canFree: false,
canPaid: false,
todayFreeArticleCount: 0,
paidReason: "暂时无法获取发布权限。",
};
});
}
function checkPaidPublishPermission() {
return loadPublishPermission().then(function (permission) {
publishPermission = permission;
return {
canPaid: permission.canPaid,
reason: permission.paidReason,
};
});
}
function isExpertUser(data) {
if (!data) return false;
var levelText = CommonUtil.getUserLevelText
@@ -504,10 +553,11 @@
item.name || item.menuName || item.title || "",
).trim();
var code = String(item.suoxie || item.code || "").trim();
var type = String(item.type || "").toLowerCase();
if (["首页", "专家推荐"].indexOf(name) > -1) return false;
if (/^(home|index|zhuanjia|expert|expertRecommend)$/i.test(code))
return false;
return !!(item.id || item.menuId) && !!name;
return type === "lottery" && !!(item.id || item.menuId) && !!name;
}
function loadMenuList() {
@@ -577,12 +627,12 @@
layer.msg("请选择彩种", { icon: 2 });
return;
}
if (title.length < 20) {
layer.msg("标题不能少于20字", { icon: 2 });
if (title.length < FREE_TITLE_MIN_LENGTH) {
layer.msg("免费文章标题需控制在15-30个字之间", { icon: 2 });
return;
}
if (title.length > 100) {
layer.msg("标题不能超过100字", { icon: 2 });
if (title.length > FREE_TITLE_MAX_LENGTH) {
layer.msg("免费文章标题需控制在15-30个字之间", { icon: 2 });
return;
}
if (!content || text.length < 500) {
@@ -597,6 +647,9 @@
menuId: menuId,
title: title,
content: content,
accountType: 1,
}, {
headers: CommonUtil.authHeaders(),
})
.then(function (res) {
if (isApiSuccess(res)) {
@@ -604,7 +657,7 @@
"发布成功,请等待审核",
{ icon: 1, time: 1600 },
function () {
window.location.href = "usercenter.html";
window.location.href = "index.html";
},
);
} else {
+105
View File
@@ -6,6 +6,7 @@
<title id="page-title">彩先知 - 分销中心</title>
<script src="../public/css/layui/layui.js"></script>
<script src="../config.js"></script>
<script src="../public/js/qrcode.min.js"></script>
<script src="../utils/ApiClient.js"></script>
<script src="../utils/DateUtil.js"></script>
<script src="../utils/CommonUtil.js"></script>
@@ -102,6 +103,16 @@
<!-- Action Section -->
<div class="action-section">
<div class="action-card">
<div class="action-card-title">
<i class="layui-icon layui-icon-share"></i> 推广分享
</div>
<div class="action-card-desc">
复制分销链接,邀请好友注册获得佣金;也可以生成推广二维码进行分享。
</div>
<button class="action-card-btn btn-apply-dist" id="fzlj" type="button">复制链接</button>
<button class="action-card-btn btn-apply-expert" id="tgewm" type="button">推广二维码</button>
</div>
<div class="action-card">
<div class="action-card-title">
<i class="layui-icon layui-icon-group"></i> 申请分销
@@ -210,6 +221,7 @@
// Check login state for topbar
checkLogin();
renderNav();
initShareFunctions();
// Check auth and load user detail
CommonUtil.requireAuth(function () {
@@ -264,6 +276,99 @@
: $("#backTop").removeClass("is-visible");
});
function getApiBaseUrl() {
return (ApiClient.baseUrl || (window.CONFIG && CONFIG.API_BASE_URL) || window.location.origin || "").replace(/\/$/, "");
}
function normalizeShareLink(data) {
var affUrl = (data && (data.affUrl || data.shareUrl || data.inviteUrl || data.url)) || "";
if (!affUrl) return "";
if (/^https?:\/\//i.test(affUrl)) return affUrl;
return getApiBaseUrl() + "/html/reg.html?affUrl=" + String(affUrl).replace(/^\//, "");
}
function getAffInfo() {
return ApiClient.post("/api/web/rebate/getAffInfo", {}, { headers: CommonUtil.authHeaders() });
}
function copyText(text) {
if (navigator.clipboard && navigator.clipboard.writeText) {
return navigator.clipboard.writeText(text);
}
var $input = $("<textarea>").val(text).css({ position: "fixed", left: "-9999px", top: "-9999px" });
$("body").append($input);
$input[0].select();
document.execCommand("copy");
$input.remove();
return Promise.resolve();
}
function initShareFunctions() {
$("#fzlj").on("click", copyShareLink);
$("#tgewm").on("click", showQrCodeModal);
}
function copyShareLink() {
CommonUtil.requireAuth(function () {
getAffInfo()
.then(function (res) {
if (res.code !== 0 || !res.data) throw new Error(res.msg || "获取分享链接失败");
var shareLink = normalizeShareLink(res.data);
if (!shareLink) throw new Error("接口未返回分享链接");
return copyText(shareLink);
})
.then(function () {
layer.msg("链接已复制到剪贴板", { icon: 1, time: 1500 });
})
.catch(function (err) {
layer.msg(err.message || "复制失败,请手动复制", { icon: 2 });
});
});
}
function showQrCodeModal() {
CommonUtil.requireAuth(function () {
getAffInfo()
.then(function (res) {
if (res.code !== 0 || !res.data) throw new Error(res.msg || "获取推广信息失败");
var shareLink = normalizeShareLink(res.data);
if (res.data.affCodeUrl && res.data.affUrl) {
shareLink = String(res.data.affCodeUrl).replace(/\/$/, "") + "/" + String(res.data.affUrl).replace(/^\//, "");
}
if (!shareLink) throw new Error("接口未返回推广二维码链接");
createCustomModal(shareLink);
})
.catch(function (err) {
layer.msg(err.message || "生成二维码失败", { icon: 2 });
});
});
}
function createCustomModal(shareLink) {
var $mask = $('<div class="custom-modal-mask"></div>');
var $modal = $('<div class="custom-modal-content"></div>');
var $qrContainer = $('<div class="custom-qr-container"></div>');
$modal.append('<h3 class="custom-modal-title">推广二维码</h3>');
$modal.append($qrContainer);
$modal.append('<button class="custom-modal-close" type="button">关闭</button>');
$mask.append($modal);
$("body").append($mask);
new QRCode($qrContainer[0], {
text: shareLink,
width: 200,
height: 200,
colorDark: "#000000",
colorLight: "#ffffff",
correctLevel: QRCode.CorrectLevel.H,
});
$mask.on("click", function (event) {
if (event.target === $mask[0]) $mask.remove();
});
$modal.find(".custom-modal-close").on("click", function () {
$mask.remove();
});
}
function loadUserDetail() {
CommonUtil.getCurrentUserDetail(
function (data) {
+1 -1
View File
@@ -162,7 +162,7 @@
});
function loadNotice() {
ApiClient.post("/api/web/notice/getDetail?id=" + noticeId).then(
ApiClient.get(ApiClient.API.noticeDetail(noticeId)).then(
function (res) {
if (res.code === 0 && res.data) {
renderNotice(res.data);
+8 -3
View File
@@ -159,13 +159,18 @@
// Load notice list
function loadNotices() {
ApiClient.post("/api/web/notice/getPageList", {
ApiClient.get(ApiClient.API.noticeList, {
pageNum: currentPage,
pageSize: pageSize,
count: pageSize,
}).then(function (res) {
if (res.code === 0 && res.data) {
renderNotices(res.data.records || []);
renderPagination(res.data.totalRow || 0);
var data = res.data || {};
var records = Array.isArray(data)
? data
: data.records || data.rows || data.list || [];
renderNotices(records);
renderPagination(data.totalRow || data.total || records.length || 0);
} else {
$("#noticeList").html(
'<div class="cxz-notice-item"><span style="color:var(--text-muted);">暂无公告</span></div>',
+8 -2
View File
@@ -282,14 +282,20 @@
);
return;
}
ApiClient.get("/api/web/lottery/result/list", {
ApiClient.get(ApiClient.API.lotteryResultList, {
menuId: currentMenuId,
pageNum: 1,
pageSize: 50,
}).catch(function () {
return ApiClient.get(ApiClient.API.latestLotteryResult);
}).then(function (res) {
if (ApiClient.isSuccess(res) && ApiClient.pickData(res, null)) {
const data = ApiClient.pickData(res, {}) || {};
const list = data.records || data.rows || data.list || ApiClient.asArray(data);
let list = data.records || data.rows || data.list || ApiClient.asArray(data);
list = ApiClient.asArray(list).filter(function (item) {
const code = item && (item.code || item.suoxie || item.lotteryCode);
return !code || !currentSuoxie || String(code).toLowerCase() === String(currentSuoxie).toLowerCase();
});
renderResults(list);
} else {
$("#resultBody").html(
+12 -17
View File
@@ -119,7 +119,6 @@
let layerInstance = null;
let currentCaptchaId = "";
let currentVerifyType = "captcha";
let currentSlideToken = "";
let currentSlideUuid = "";
let currentSlideX = 0;
@@ -149,7 +148,6 @@
function refreshCaptcha() {
currentCaptchaId = "";
currentSlideToken = "";
currentSlideUuid = "";
currentSlideX = 0;
$("#captchaId").val("");
@@ -193,9 +191,6 @@
function completeSlideCaptcha(result) {
const data = result || {};
const nested = data.data || {};
currentSlideToken =
data.slideToken || data.token || data.verifyToken ||
nested.slideToken || nested.token || nested.verifyToken || "";
currentSlideUuid =
data.slideUuid || data.uuid || nested.slideUuid || nested.uuid || "";
currentSlideX =
@@ -222,7 +217,6 @@
onSuccess: completeSlideCaptcha,
onCancel: function () {
currentSlideUuid = "";
currentSlideToken = "";
currentSlideX = 0;
},
onError: function (error) {
@@ -288,7 +282,6 @@
}
return {
verifyType: "slide",
slideToken: currentSlideToken,
slideUuid: currentSlideUuid,
slideX: currentSlideX,
};
@@ -316,10 +309,8 @@
const payload = Object.assign(
{
grantType: "password",
clientId: ApiClient.clientId,
client_id: ApiClient.clientId,
phoneNumber: $("#phoneInput").val().trim(),
password: hex_md5($("#passwordInput").val()),
password: hex_md5($("#passwordInput").val().trim()),
deviceType: "WEB",
},
getVerifyParams(false) || {},
@@ -332,17 +323,21 @@
})
.then(function (res) {
if (ApiClient.isSuccess(res) && res.data) {
localStorage.setItem(
"token",
JSON.stringify(
res.data.token || res.data.access_token || res.data,
),
);
const data = res.data || {};
localStorage.setItem("token", data.access_token || data.token || "");
localStorage.setItem("userInfo", JSON.stringify(data));
sessionStorage.removeItem("authExpiredRedirecting");
sessionStorage.removeItem("authExpiredMessage");
layerInstance.msg(
"登录成功,即将跳转到首页",
{ icon: 1, time: 1200 },
function () {
window.location.href = "index.html";
const redirect = new URLSearchParams(window.location.search).get("redirect");
window.location.href =
redirect ||
(typeof CommonUtil !== "undefined"
? CommonUtil.siteHref("/index.html")
: "index.html");
},
);
return;
+50
View File
@@ -101,6 +101,17 @@
</a>
</div>
<div class="cxz-recommend-section">
<div class="cxz-recommend-card">
<div class="cxz-recommend-title">相关推荐</div>
<ul class="cxz-recommend-list" id="freeRelatedList"></ul>
</div>
<div class="cxz-recommend-card">
<div class="cxz-recommend-title">最新文章</div>
<ul class="cxz-recommend-list" id="freeLatestList"></ul>
</div>
</div>
<!-- Comment Section -->
<div class="cxz-comment-section">
<div class="cxz-comment-header">
@@ -356,8 +367,47 @@
if (!prev && rows[0]) prev = rows[0];
if (!next && rows[1]) next = rows[1];
if (prev || next) renderArticleNav(prev, next);
renderFreeArticleList("#freeRelatedList", rows.filter(function (item) {
var id = item && (item.id || item.articleId || item.freeArticleId);
return id && String(id) !== String(articleId);
}).slice(0, 8));
})
.catch(function () {});
loadFreeArticleLatest();
}
function loadFreeArticleLatest() {
ApiClient.get(ApiClient.API.freeArticleLatest, { articleCount: 8 })
.then(function (res) {
if (!ApiClient.isSuccess(res)) return;
renderFreeArticleList("#freeLatestList", ApiClient.asArray(ApiClient.pickData(res, [])));
})
.catch(function () {
renderFreeArticleList("#freeLatestList", []);
});
}
function renderFreeArticleList(selector, list) {
var $list = $(selector).empty();
if (!list || !list.length) {
$list.html('<li class="cxz-recommend-empty">暂无文章</li>');
return;
}
$.each(list, function (_, item) {
var id = item.id || item.articleId || item.freeArticleId;
if (!id) return;
var title = item.title || item.articleTitle || item.name || "无标题";
var time = DateUtil.formatDateToYMD(item.updateTime || item.createTime || item.publishTime) || "";
$list.append(
'<li><a href="' +
escapeHtml(articleLinkOf(item)) +
'">' +
escapeHtml(title) +
'</a><span>' +
escapeHtml(time) +
"</span></li>",
);
});
}
function updateLikeButton(data) {
+57 -9
View File
@@ -114,6 +114,7 @@
let currentPage = 1;
const pageSize = 15;
let lotteryMenus = [];
let freeArticleGroups = [];
function logout() {
localStorage.removeItem("token");
@@ -155,7 +156,7 @@
if (
menuId &&
lotteryMenus.some(function (item) {
return String(item.id) === String(menuId);
return sameLotteryKey(item, menuId);
})
) {
return;
@@ -165,14 +166,24 @@
}
function renderLotteryTabs() {
return ApiClient.get(ApiClient.API.homeNav)
.then(function (res) {
return Promise.all([
ApiClient.get(ApiClient.API.homeNav).catch(function () {
return { data: [] };
}),
ApiClient.get(ApiClient.API.freeArticleRecommend, { articleCount: pageSize }).catch(function () {
return { data: [] };
}),
])
.then(function (results) {
const res = results[0];
const recommendRes = results[1];
lotteryMenus = normalizeLotteryMenus(ApiClient.pickData(res, []));
freeArticleGroups = ApiClient.asArray(ApiClient.pickData(recommendRes, []));
resolveCurrentMenuId();
const $tabs = $("#lotteryTabs");
$tabs.empty();
$.each(lotteryMenus, function (_, item) {
const id = item.id || "";
const id = item.id || item.menuId || item.code || item.suoxie || "";
$tabs.append(
'<a href="mianfeilist.html?id=' +
encodeURIComponent(id) +
@@ -198,13 +209,41 @@
$(".cxz-lottery-tab").removeClass("active");
$('.cxz-lottery-tab[data-id="' + menuId + '"]').addClass("active");
const currentMenu = lotteryMenus.find(function (item) {
return String(item.id) === String(menuId);
return sameLotteryKey(item, menuId);
});
const name = (currentMenu && currentMenu.name) || "免费推荐";
$("#listTitle").text(name + " - 免费推荐");
document.title = "彩先知 - " + name + "免费推荐";
}
function sameLotteryKey(item, value) {
const key = String(value || "").toLowerCase();
if (!key || !item) return false;
return (
String(item.id || "").toLowerCase() === key ||
String(item.menuId || "").toLowerCase() === key ||
String(item.code || "").toLowerCase() === key ||
String(item.suoxie || "").toLowerCase() === key ||
String(item.lotteryCode || "").toLowerCase() === key ||
String(item.name || "").toLowerCase() === key
);
}
function currentMenuInfo() {
return lotteryMenus.find(function (item) {
return sameLotteryKey(item, menuId);
});
}
function currentFallbackGroup() {
const menu = currentMenuInfo();
return (
freeArticleGroups.find(function (group) {
return sameLotteryKey(group, menuId) || sameLotteryKey(group, menu && (menu.code || menu.suoxie || menu.id));
}) || null
);
}
// Load articles
function loadArticles() {
if (!menuId) {
@@ -214,7 +253,7 @@
renderPagination(0);
return;
}
ApiClient.get("/api/web/free-article/list", {
ApiClient.get(ApiClient.API.freeArticleList, {
menuId: menuId,
pageNum: currentPage,
pageSize: pageSize,
@@ -225,13 +264,22 @@
renderArticles(list);
renderPagination(data.totalRow || data.total || data.count || list.length || 0);
} else {
$("#articleList").html(
'<div class="cxz-article-item"><span style="color:var(--text-muted);">暂无数据</span></div>',
);
renderFallbackArticles();
}
}).catch(function () {
renderFallbackArticles();
});
}
function renderFallbackArticles() {
const group = currentFallbackGroup();
const allArticles = ApiClient.asArray(group && group.articles);
const startIndex = (currentPage - 1) * pageSize;
const records = allArticles.slice(startIndex, startIndex + pageSize);
renderArticles(records);
renderPagination(allArticles.length);
}
function renderArticles(list) {
const $container = $("#articleList");
$container.empty();
+298 -72
View File
@@ -25,16 +25,16 @@
</div>
<div class="auth-body">
<form id="registerForm" autocomplete="off">
<div class="form-group" id="nicknameContainer">
<label for="nickname">昵称</label>
<div class="form-group" id="nickNameContainer">
<label for="nickName">昵称</label>
<input
type="text"
id="nickname"
id="nickName"
class="form-input"
placeholder="请输入昵称(2-20个字符"
maxlength="20"
placeholder="请输入昵称(2-6个汉字或2-12个英文"
maxlength="12"
/>
<div class="error-message" id="nicknameError"></div>
<div class="error-message" id="nickNameError"></div>
</div>
<div class="form-group" id="phoneContainer">
@@ -146,10 +146,10 @@
let layerInstance = null;
let currentCaptchaId = "";
let currentVerifyType = "captcha";
let currentSlideToken = "";
let currentSlideUuid = "";
let currentSlideX = 0;
let pendingSmsAfterVerify = false;
let smsCodeIssuedPhone = "";
let referralPreviewData = null;
function showError(field, message) {
@@ -172,19 +172,27 @@
function loadVerifyConfig() {
return AuthPageUtil.loadVerifyType().then(function (type) {
currentVerifyType = type;
currentVerifyType = type === "none" ? "captcha" : type;
});
}
function refreshCaptcha() {
if (currentVerifyType === "none") {
currentCaptchaId = "";
currentSlideUuid = "";
currentSlideX = 0;
$("#captchaGroup").hide();
$("#captchaRefreshLink").hide();
return;
}
$("#captchaGroup").show();
currentCaptchaId = "";
currentSlideToken = "";
currentSlideUuid = "";
currentSlideX = 0;
$("#captchaId").val("");
$("#captchaInput").val("");
$("#captchaGroup").removeClass("is-slide-mode");
$("#sendSmsBtn").prop("disabled", currentVerifyType === "slide");
if (currentVerifyType === "slide") {
renderSlideCaptchaEntry();
return;
@@ -220,9 +228,6 @@
function completeSlideCaptcha(result) {
const data = result || {};
const nested = data.data || {};
currentSlideToken =
data.slideToken || data.token || data.verifyToken ||
nested.slideToken || nested.token || nested.verifyToken || "";
currentSlideUuid =
data.slideUuid || data.uuid || nested.slideUuid || nested.uuid || "";
currentSlideX =
@@ -235,7 +240,7 @@
$("#captchaContainer").html(
'<div class="slide-verify-card is-success"><div class="slide-verify-mark">✓</div><div class="slide-verify-main"><div class="slide-verify-title">验证已通过</div><div class="slide-verify-desc">可以获取短信或注册</div></div><div class="slide-verify-action">完成</div></div>',
);
$("#sendSmsBtn").prop("disabled", false);
checkSmsWaitAfterVerify();
if (pendingSmsAfterVerify) {
pendingSmsAfterVerify = false;
sendSmsCode();
@@ -254,7 +259,6 @@
onSuccess: completeSlideCaptcha,
onCancel: function () {
currentSlideUuid = "";
currentSlideToken = "";
currentSlideX = 0;
},
onError: function (error) {
@@ -277,7 +281,6 @@
$("#captchaInput").hide();
$("#captchaRefreshLink").hide();
$("#captchaGroup").addClass("is-slide-mode");
$("#sendSmsBtn").prop("disabled", true);
$("#captchaContainer")
.removeAttr("onclick")
.html(
@@ -295,12 +298,19 @@
}
function validateNickname(show) {
const value = $("#nickname").val().trim();
if (value.length < 2 || value.length > 20) {
if (show) showError("nickname", "昵称长度应为2-20个字符");
const value = $("#nickName").val().trim();
if (!value) {
if (show) showError("nickName", "请输入昵称");
return false;
}
clearError("nickname");
const nicknameWidth = Array.from(value).reduce(function (total, char) {
return total + (/[\u4e00-\u9fa5\uff00-\uffef]/.test(char) ? 2 : 1);
}, 0);
if (value.length < 2 || nicknameWidth > 12) {
if (show) showError("nickName", "昵称长度应为2-6个汉字或2-12个英文");
return false;
}
clearError("nickName");
return true;
}
@@ -342,72 +352,276 @@
return true;
}
function getVerifyParams(show) {
function hasIssuedSmsCodeForCurrentPhone() {
const phone = $("#phone").val().trim();
return !!phone && smsCodeIssuedPhone === phone;
}
function handlePhoneInput() {
const phone = $("#phone").val().trim();
if (smsCodeIssuedPhone && smsCodeIssuedPhone !== phone) {
$("#smsCode").val("");
clearError("smsCode");
}
}
function validateSmsCodePhone(show) {
const phone = $("#phone").val().trim();
if (smsCodeIssuedPhone && smsCodeIssuedPhone !== phone) {
if (show) showError("smsCode", "手机号已修改,请重新获取短信验证码");
return false;
}
return true;
}
function getOptionalGraphVerifyParams() {
if (hasIssuedSmsCodeForCurrentPhone()) return {};
if (currentVerifyType === "slide" && currentSlideUuid) {
return {
verifyType: "slide",
slideUuid: currentSlideUuid,
slideX: currentSlideX,
};
}
if (currentVerifyType === "captcha") {
const code = $("#captchaInput").val().trim();
if (code && currentCaptchaId) {
return { verifyType: "captcha", code: code, uuid: currentCaptchaId };
}
}
return {};
}
function getGraphVerifyParams(show) {
if (currentVerifyType === "slide") {
if (!currentSlideUuid) {
if (show) layerInstance.msg("请先完成滑动验证", { icon: 2 });
if (show) {
layerInstance.msg("请先完成滑动验证后获取短信验证码", {
icon: 2,
});
}
return null;
}
return {
verifyType: "slide",
slideToken: currentSlideToken,
slideUuid: currentSlideUuid,
slideX: currentSlideX,
};
}
const code = $("#captchaInput").val().trim();
if (!code || !currentCaptchaId) {
if (show) showError("captcha", "请填写有效验证码");
if (!code) {
if (show) {
layerInstance.msg("请先完成图片验证码后获取短信验证码", {
icon: 2,
});
}
return null;
}
if (!currentCaptchaId) {
if (show) {
layerInstance.msg("验证码已失效,请换一张", { icon: 2 });
refreshCaptcha();
}
return null;
}
clearError("captcha");
return { verifyType: "captcha", code, uuid: currentCaptchaId };
}
function sendSmsCode() {
if (!validatePhone(true)) return;
if (currentVerifyType === "slide" && !currentSlideUuid) {
layerInstance.msg("请先完成滑动验证后获取短信验证码", { icon: 2 });
return;
}
const verifyParams = getVerifyParams(true);
if (!verifyParams) return;
$("#sendSmsBtn").prop("disabled", true);
AuthPageUtil.sendSmsCode(
Object.assign(
{ phoneNumber: $("#phone").val().trim() },
verifyParams,
),
function getSmsWaitSeconds() {
const smsCountdownApi = ApiClient.API["authSmsCountdown"];
if (!smsCountdownApi) return Promise.resolve(0);
return ApiClient.get(
smsCountdownApi,
{ phoneNumber: $("#phone").val().trim(), scene: "auth" },
{ publicRequest: true },
)
.then(function (res) {
if (ApiClient.isSuccess(res)) {
layerInstance.msg("短信验证码已发送", { icon: 1 });
AuthPageUtil.startSmsCountdown("#sendSmsBtn");
return;
}
$("#sendSmsBtn").prop("disabled", false);
layerInstance.msg((res && res.msg) || "短信验证码发送失败", {
icon: 2,
});
refreshCaptcha();
if (!ApiClient.isSuccess(res)) return 0;
const data = res.data || {};
return (
Number(
data.seconds ||
data.waitSeconds ||
data.remainingSeconds ||
data.countdown ||
0,
) || 0
);
})
.catch(function () {
$("#sendSmsBtn").prop("disabled", false);
layerInstance.msg("网络异常,短信发送失败", { icon: 2 });
refreshCaptcha();
return 0;
});
}
function checkSmsWaitAfterVerify() {
if (!validatePhone(false)) return;
getSmsWaitSeconds().then(function (waitSeconds) {
if (waitSeconds > 0) {
layerInstance.msg("请 " + waitSeconds + " 秒后再获取短信验证码", {
icon: 0,
});
AuthPageUtil.startSmsCountdown("#sendSmsBtn", waitSeconds);
}
});
}
function refreshDialogCaptcha() {
currentCaptchaId = "";
$("#captchaInput").val("");
$("#captchaId").val("");
$("#captchaDialogInput").val("");
$("#captchaDialogImage").attr("src", "");
return ApiClient.get(
ApiClient.API.authCode,
{},
{ publicRequest: true },
)
.then(function (res) {
if (ApiClient.isSuccess(res) && res.data) {
currentCaptchaId = res.data.uuid || res.data.id || "";
$("#captchaId").val(currentCaptchaId);
$("#captchaDialogImage").attr(
"src",
res.data.img || res.data.image || "",
);
return;
}
layerInstance.msg("获取验证码失败,请重试", { icon: 2 });
})
.catch(function () {
layerInstance.msg("网络错误,无法获取验证码", { icon: 2 });
});
}
function openImageCaptchaDialog() {
const dialogHtml = `
<div class="captcha-dialog">
<div class="captcha-dialog-title">完成图片验证码</div>
<div class="captcha-dialog-desc">请输入右侧图片中的验证码,通过后将自动发送短信验证码。</div>
<div class="captcha-dialog-row">
<input type="text" class="captcha-dialog-input" id="captchaDialogInput" maxlength="6" placeholder="请输入验证码" />
<div class="captcha-dialog-img-box" id="captchaDialogRefresh">
<img class="captcha-dialog-img" id="captchaDialogImage" src="" alt="验证码" />
</div>
</div>
<div class="captcha-dialog-tip">看不清?<span id="captchaDialogRefreshText">换一张</span></div>
<div class="captcha-dialog-actions">
<button type="button" class="captcha-dialog-btn" id="captchaDialogCancel">取消</button>
<button type="button" class="captcha-dialog-btn primary" id="captchaDialogConfirm">确认发送</button>
</div>
</div>
`;
const dialogIndex = layerInstance.open({
type: 1,
title: false,
area: ["420px", "auto"],
shadeClose: true,
closeBtn: 1,
content: dialogHtml,
success: function () {
refreshDialogCaptcha();
$("#captchaDialogRefresh, #captchaDialogRefreshText").on(
"click",
refreshDialogCaptcha,
);
$("#captchaDialogCancel").on("click", function () {
layerInstance.close(dialogIndex);
});
$("#captchaDialogInput").on("keydown", function (event) {
if (event.key === "Enter") {
event.preventDefault();
$("#captchaDialogConfirm").trigger("click");
}
});
$("#captchaDialogConfirm").on("click", function () {
const code = $("#captchaDialogInput").val().trim();
if (!code) {
layerInstance.msg("请输入验证码", { icon: 2 });
return;
}
$("#captchaInput").val(code);
layerInstance.close(dialogIndex);
sendSmsCode();
});
setTimeout(function () {
$("#captchaDialogInput").focus();
}, 80);
},
end: function () {
$("#sendSmsBtn").prop("disabled", false);
},
});
}
function sendSmsCode() {
if (!validatePhone(true)) return;
if (currentVerifyType === "slide" && !currentSlideUuid) {
pendingSmsAfterVerify = true;
openSlideCaptcha();
return;
}
if (
currentVerifyType === "captcha" &&
!$("#captchaInput").val().trim()
) {
$("#sendSmsBtn").prop("disabled", true);
openImageCaptchaDialog();
return;
}
const verifyParams = getGraphVerifyParams(true);
if (!verifyParams) return;
$("#sendSmsBtn").prop("disabled", true);
getSmsWaitSeconds().then(function (waitSeconds) {
if (waitSeconds > 0) {
layerInstance.msg("请 " + waitSeconds + " 秒后再获取短信验证码", {
icon: 0,
});
AuthPageUtil.startSmsCountdown("#sendSmsBtn", waitSeconds);
return;
}
AuthPageUtil.sendSmsCode(
Object.assign(
{ phoneNumber: $("#phone").val().trim() },
verifyParams,
),
)
.then(function (res) {
if (ApiClient.isSuccess(res)) {
smsCodeIssuedPhone = $("#phone").val().trim();
layerInstance.msg("短信验证码已发送", { icon: 1 });
AuthPageUtil.startSmsCountdown("#sendSmsBtn");
return;
}
$("#sendSmsBtn").prop("disabled", false);
layerInstance.msg((res && res.msg) || "短信验证码发送失败", {
icon: 2,
});
refreshCaptcha();
})
.catch(function () {
$("#sendSmsBtn").prop("disabled", false);
layerInstance.msg("网络异常,短信发送失败", { icon: 2 });
refreshCaptcha();
});
});
}
function validateForm() {
if (
!validateNickname(true) ||
!validatePhone(true) ||
!validateSmsCode(true) ||
!validateSmsCodePhone(true) ||
!validatePassword(true) ||
!validateConfirmPassword(true)
)
return false;
if (!getVerifyParams(true)) return false;
if (!$("#agreementCheckbox").prop("checked")) {
layerInstance.msg("请阅读并同意用户协议和隐私政策", { icon: 2 });
return false;
@@ -417,8 +631,6 @@
function buildRegisterData() {
const params = new URLSearchParams(window.location.search);
const inviteCode =
params.get("inviteCode") || params.get("referralCode") || "";
const referrerId =
(referralPreviewData &&
(referralPreviewData.referrerId ||
@@ -427,46 +639,58 @@
params.get("referrerId") ||
params.get("pid") ||
0;
const invitePayload = inviteCode ? { inviteCode: inviteCode } : {};
return Object.assign(
{
phoneNumber: $("#phone").val().trim(),
password: hex_md5($("#password").val()),
smsCode: $("#smsCode").val().trim(),
nickname: $("#nickname").val().trim(),
nickName: $("#nickName").val().trim(),
referrerId: referrerId,
},
invitePayload,
getVerifyParams(false) || {},
getOptionalGraphVerifyParams(),
);
}
function loadReferralBindPreview() {
function getInviteCodeFromQuery() {
const params = new URLSearchParams(window.location.search);
const inviteCode =
params.get("inviteCode") || params.get("referralCode") || "";
if (!inviteCode) return;
ApiClient.get(
return params.get("inviteCode") || params.get("referralCode") || "";
}
function loadReferralBindPreview() {
const inviteCode = getInviteCodeFromQuery();
if (!inviteCode) return Promise.resolve();
return ApiClient.get(
ApiClient.API.referralBindPreview,
{ inviteCode: inviteCode },
{ publicRequest: true },
)
.then(function (res) {
if (!ApiClient.isSuccess(res)) return;
if (!ApiClient.isSuccess(res)) {
throw new Error((res && res.msg) || "推荐人信息无效");
}
referralPreviewData = ApiClient.pickData(res, {}) || {};
const name =
referralPreviewData.nickname ||
referralPreviewData.nickName ||
referralPreviewData.nickname ||
referralPreviewData.userName ||
referralPreviewData.phoneNumber ||
"";
if (name)
if (name) {
layerInstance.msg("已识别推荐人:" + name, {
icon: 1,
time: 1500,
});
}
})
.catch(function () {
.catch(function (error) {
referralPreviewData = null;
layerInstance.msg(
(error && error.message) || "推荐人信息无效",
{ icon: 2 },
);
});
}
@@ -509,12 +733,14 @@
event.preventDefault();
if (validateForm()) register();
});
$("#nickname").on("blur", function () {
$("#nickName").on("blur", function () {
validateNickname(true);
});
$("#phone").on("blur", function () {
validatePhone(true);
});
$("#phone")
.on("input", handlePhoneInput)
.on("blur", function () {
validatePhone(true);
});
$("#smsCode").on("blur", function () {
validateSmsCode(true);
});
+141 -15
View File
@@ -127,10 +127,10 @@
let layerInstance = null;
let currentCaptchaId = "";
let currentVerifyType = "captcha";
let currentSlideToken = "";
let currentSlideUuid = "";
let currentSlideX = 0;
let pendingSmsAfterVerify = false;
let smsCodeIssuedPhone = "";
function showError(field, message) {
const containerId =
@@ -158,13 +158,12 @@
function refreshCaptcha() {
currentCaptchaId = "";
currentSlideToken = "";
currentSlideUuid = "";
currentSlideX = 0;
$("#captchaId").val("");
$("#captchaInput").val("");
$("#captchaGroup").removeClass("is-slide-mode");
$("#sendSmsBtn").prop("disabled", currentVerifyType === "slide");
$("#sendSmsBtn").prop("disabled", false);
if (currentVerifyType === "slide") {
renderSlideCaptchaEntry();
return;
@@ -200,9 +199,6 @@
function completeSlideCaptcha(result) {
const data = result || {};
const nested = data.data || {};
currentSlideToken =
data.slideToken || data.token || data.verifyToken ||
nested.slideToken || nested.token || nested.verifyToken || "";
currentSlideUuid =
data.slideUuid || data.uuid || nested.slideUuid || nested.uuid || "";
currentSlideX =
@@ -234,7 +230,6 @@
onSuccess: completeSlideCaptcha,
onCancel: function () {
currentSlideUuid = "";
currentSlideToken = "";
currentSlideX = 0;
},
onError: function (error) {
@@ -257,7 +252,7 @@
$("#captchaInput").hide();
$("#captchaRefreshLink").hide();
$("#captchaGroup").addClass("is-slide-mode");
$("#sendSmsBtn").prop("disabled", true);
$("#sendSmsBtn").prop("disabled", false);
$("#captchaContainer")
.removeAttr("onclick")
.html(
@@ -294,6 +289,29 @@
return true;
}
function hasIssuedSmsCodeForCurrentPhone() {
const phone = $("#phone").val().trim();
return !!phone && smsCodeIssuedPhone === phone;
}
function handlePhoneInput() {
const phone = $("#phone").val().trim();
if (smsCodeIssuedPhone && smsCodeIssuedPhone !== phone) {
smsCodeIssuedPhone = "";
$("#smsCode").val("");
}
}
function validateSmsCodePhone(show) {
if (!validateSmsCode(show)) return false;
if (!hasIssuedSmsCodeForCurrentPhone()) {
if (show) showError("smsCode", "请先获取当前手机号的短信验证码");
return false;
}
clearError("smsCode");
return true;
}
function validatePassword(show) {
if (!AuthPageUtil.isPassword($("#password").val())) {
if (show) showError("password", "密码长度应为6-20位");
@@ -320,7 +338,6 @@
}
return {
verifyType: "slide",
slideToken: currentSlideToken,
slideUuid: currentSlideUuid,
slideX: currentSlideX,
};
@@ -334,23 +351,131 @@
return { verifyType: "captcha", code, uuid: currentCaptchaId };
}
function getSmsWaitSeconds() {
const smsCountdownApi = ApiClient.API["authSmsCountdown"];
if (!smsCountdownApi) return Promise.resolve(0);
return ApiClient.get(
smsCountdownApi,
{ phoneNumber: $("#phone").val().trim(), scene: "auth" },
{ publicRequest: true },
)
.then(function (res) {
if (!ApiClient.isSuccess(res)) return 0;
const data = res.data || {};
return Number(data.seconds || data.waitSeconds || data.remainingSeconds || data.countdown || 0) || 0;
})
.catch(function () {
return 0;
});
}
function refreshDialogCaptcha() {
currentCaptchaId = "";
$("#captchaInput").val("");
$("#captchaId").val("");
$("#captchaDialogInput").val("");
$("#captchaDialogImage").attr("src", "");
return ApiClient.get(ApiClient.API.authCode, {}, { publicRequest: true })
.then(function (res) {
if (ApiClient.isSuccess(res) && res.data) {
currentCaptchaId = res.data.uuid || res.data.id || "";
$("#captchaId").val(currentCaptchaId);
$("#captchaDialogImage").attr("src", res.data.img || res.data.image || "");
return;
}
layerInstance.msg("获取验证码失败,请重试", { icon: 2 });
})
.catch(function () {
layerInstance.msg("网络错误,无法获取验证码", { icon: 2 });
});
}
function openImageCaptchaDialog() {
const dialogHtml =
'<div class="captcha-dialog">' +
'<div class="captcha-dialog-title">完成图片验证码</div>' +
'<div class="captcha-dialog-desc">请输入右侧图片中的验证码,通过后将自动发送短信验证码。</div>' +
'<div class="captcha-dialog-row">' +
'<input type="text" class="captcha-dialog-input" id="captchaDialogInput" maxlength="6" placeholder="请输入验证码" />' +
'<div class="captcha-dialog-img-box" id="captchaDialogRefresh">' +
'<img class="captcha-dialog-img" id="captchaDialogImage" src="" alt="验证码" />' +
'</div></div>' +
'<div class="captcha-dialog-tip">看不清?<span id="captchaDialogRefreshText">换一张</span></div>' +
'<div class="captcha-dialog-actions">' +
'<button type="button" class="captcha-dialog-btn" id="captchaDialogCancel">取消</button>' +
'<button type="button" class="captcha-dialog-btn primary" id="captchaDialogConfirm">确认发送</button>' +
'</div></div>';
const dialogIndex = layerInstance.open({
type: 1,
title: false,
area: ["420px", "auto"],
shadeClose: true,
closeBtn: 1,
content: dialogHtml,
success: function () {
refreshDialogCaptcha();
$("#captchaDialogRefresh, #captchaDialogRefreshText").on("click", refreshDialogCaptcha);
$("#captchaDialogCancel").on("click", function () {
layerInstance.close(dialogIndex);
});
$("#captchaDialogInput").on("keydown", function (event) {
if (event.key === "Enter") {
event.preventDefault();
$("#captchaDialogConfirm").trigger("click");
}
});
$("#captchaDialogConfirm").on("click", function () {
const code = $("#captchaDialogInput").val().trim();
if (!code) {
layerInstance.msg("请输入验证码", { icon: 2 });
return;
}
$("#captchaInput").val(code);
layerInstance.close(dialogIndex);
sendSmsCode();
});
setTimeout(function () {
$("#captchaDialogInput").focus();
}, 80);
},
end: function () {
$("#sendSmsBtn").prop("disabled", false);
},
});
}
function sendSmsCode() {
if (!validatePhone(true)) return;
if (currentVerifyType === "slide" && !currentSlideUuid) {
layerInstance.msg("请先完成滑动验证后获取短信验证码", { icon: 2 });
pendingSmsAfterVerify = true;
openSlideCaptcha();
return;
}
if (currentVerifyType === "captcha" && !$("#captchaInput").val().trim()) {
$("#sendSmsBtn").prop("disabled", true);
openImageCaptchaDialog();
return;
}
const verifyParams = getVerifyParams(true);
if (!verifyParams) return;
$("#sendSmsBtn").prop("disabled", true);
AuthPageUtil.sendForgotPasswordSmsCode(
getSmsWaitSeconds().then(function (waitSeconds) {
if (waitSeconds > 0) {
layerInstance.msg("请 " + waitSeconds + " 秒后再获取短信验证码", { icon: 0 });
AuthPageUtil.startSmsCountdown("#sendSmsBtn", waitSeconds);
throw new Error("__SMS_WAIT__");
}
return AuthPageUtil.sendForgotPasswordSmsCode(
Object.assign(
{ phoneNumber: $("#phone").val().trim() },
verifyParams,
),
)
);
})
.then(function (res) {
if (ApiClient.isSuccess(res)) {
smsCodeIssuedPhone = $("#phone").val().trim();
layerInstance.msg("短信验证码已发送", { icon: 1 });
AuthPageUtil.startSmsCountdown("#sendSmsBtn");
return;
@@ -361,7 +486,8 @@
});
refreshCaptcha();
})
.catch(function () {
.catch(function (error) {
if (error && error.message === "__SMS_WAIT__") return;
$("#sendSmsBtn").prop("disabled", false);
layerInstance.msg("网络异常,短信发送失败", { icon: 2 });
refreshCaptcha();
@@ -371,7 +497,7 @@
function validateForm() {
if (
!validatePhone(true) ||
!validateSmsCode(true) ||
!validateSmsCodePhone(true) ||
!validatePassword(true) ||
!validateConfirmPassword(true)
)
@@ -433,7 +559,7 @@
event.preventDefault();
if (validateForm()) submitResetPassword();
});
$("#phone").on("blur", function () {
$("#phone").on("input", handlePhoneInput).on("blur", function () {
validatePhone(true);
});
$("#smsCode").on("blur", function () {
+3 -222
View File
@@ -3,235 +3,16 @@
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>3D研究室 - 微信登录</title>
<title>彩先知 - 微信登录</title>
<script src="../config.js"></script>
<script src="../utils/ApiClient.js"></script>
<script src="../utils/AuthPageUtil.js"></script>
<style>
* {
box-sizing: border-box;
}
body {
min-height: 100vh;
margin: 0;
color: #1f2933;
font-family:
-apple-system, BlinkMacSystemFont, "Segoe UI", "Microsoft YaHei",
sans-serif;
background:
radial-gradient(circle at 72% 18%, rgba(255, 190, 98, 0.45), rgba(255, 190, 98, 0) 32%),
linear-gradient(135deg, #ff7a1a 0%, #ff6f12 54%, #ff8a23 100%);
}
.page {
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
padding: 40px 16px;
}
.card {
width: 420px;
max-width: 100%;
padding: 28px 30px 30px;
border-radius: 10px;
background: #ffffff;
box-shadow: 0 18px 48px rgba(124, 38, 4, 0.18);
}
.brand {
margin-bottom: 20px;
color: #de2103;
font-size: 28px;
font-weight: 800;
text-align: center;
}
.title {
margin-bottom: 8px;
font-size: 18px;
font-weight: 700;
text-align: center;
}
.desc {
min-height: 22px;
margin-bottom: 18px;
color: #667085;
font-size: 13px;
line-height: 22px;
text-align: center;
}
.status {
display: flex;
align-items: center;
justify-content: center;
gap: 10px;
margin: 14px 0 20px;
color: #de2103;
font-size: 14px;
}
.spinner {
width: 18px;
height: 18px;
border: 2px solid #f5c7bd;
border-top-color: #de2103;
border-radius: 50%;
animation: spin 0.8s linear infinite;
}
@keyframes spin {
to {
transform: rotate(360deg);
}
}
.bind-form {
display: none;
}
.form-row {
margin-top: 14px;
}
.form-row label {
display: block;
margin-bottom: 6px;
color: #344054;
font-size: 13px;
font-weight: 600;
}
.input,
.sms-input {
width: 100%;
height: 42px;
padding: 0 12px;
border: 1px solid #d9dee7;
border-radius: 5px;
color: #1f2933;
font-size: 14px;
outline: none;
}
.input:focus,
.sms-input:focus {
border-color: #de2103;
box-shadow: 0 0 0 3px rgba(222, 33, 3, 0.08);
}
.sms-row {
display: flex;
gap: 10px;
}
.captcha-row {
display: none;
}
.captcha-box {
display: flex;
gap: 10px;
}
.captcha-box .input {
flex: 1;
min-width: 0;
}
.captcha-image {
flex: 0 0 108px;
height: 42px;
border: 1px solid #d9dee7;
border-radius: 5px;
background: #f8fafc;
cursor: pointer;
object-fit: cover;
}
.sms-input {
flex: 1;
min-width: 0;
}
.sms-button {
flex: 0 0 108px;
height: 42px;
border: 1px solid #de2103;
border-radius: 5px;
background: #fff8f6;
color: #de2103;
cursor: pointer;
}
.sms-button:disabled,
.submit:disabled {
cursor: not-allowed;
opacity: 0.65;
}
.submit {
width: 100%;
height: 42px;
margin-top: 20px;
border: 0;
border-radius: 5px;
background: linear-gradient(180deg, #ff4a22 0%, #de2103 100%);
color: #ffffff;
font-size: 14px;
font-weight: 700;
cursor: pointer;
}
.tips {
margin-top: 12px;
padding: 10px 12px;
border: 1px solid #f2d2ca;
border-radius: 5px;
background: #fff8f6;
color: #8a4b42;
font-size: 12px;
line-height: 20px;
}
.error {
display: none;
margin-top: 14px;
padding: 10px 12px;
border: 1px solid #f4c7c7;
border-radius: 5px;
background: #fff5f5;
color: #b42318;
font-size: 13px;
line-height: 20px;
}
.actions {
display: flex;
justify-content: center;
gap: 16px;
margin-top: 16px;
}
.actions a,
.actions button {
border: 0;
background: transparent;
color: #de2103;
font-size: 13px;
text-decoration: none;
cursor: pointer;
}
</style>
<link rel="stylesheet" href="../public/css/social-callback.css" />
</head>
<body>
<div class="page">
<div class="card">
<div class="brand">3D研究室</div>
<div class="brand">彩先知</div>
<div class="title" id="title">微信登录</div>
<div class="desc" id="desc">正在处理微信授权,请稍候...</div>
<div class="status" id="status">
+128 -22
View File
@@ -59,6 +59,7 @@
</div>
<div class="tl-filter-bar">
<div class="tl-flow-tabs" id="walletFlowTabs"></div>
<div class="tl-filter-group">
<span>状态</span>
<div class="tl-segment" id="statusFilters"></div>
@@ -137,6 +138,17 @@
{ value: "sales", label: "销售余额" },
{ value: "expert", label: "专家余额" },
];
const flowTypeOptions = [
{ value: "withdraw", label: "\u63d0\u73b0\u7533\u8bf7", desc: "\u63d0\u73b0\u5230\u6536\u6b3e\u8d26\u6237" },
{ value: "recharge", label: "\u8d26\u6237\u5145\u503c", desc: "\u8d26\u6237\u4f59\u989d\u5145\u503c" },
{ value: "purchase", label: "\u6587\u7ae0\u8d2d\u4e70", desc: "\u8d2d\u4e70\u6587\u7ae0\u652f\u51fa" },
{ value: "sales", label: "\u9080\u8bf7\u8fd4\u5229", desc: "\u5206\u9500\u8fd4\u5229\u6536\u5165" },
{ value: "expert", label: "\u4e13\u5bb6\u4f63\u91d1", desc: "\u4e13\u5bb6\u6536\u76ca\u6536\u5165" },
];
const flowTypeMap = flowTypeOptions.reduce(function (map, item) {
map[item.value] = item;
return map;
}, {});
const statusTextMap = {
0: "审核中",
1: "成功",
@@ -160,6 +172,8 @@
const sourceTextMap = {
sales: "销售余额",
expert: "专家余额",
account: "\u8d26\u6237\u4f59\u989d",
balance: "\u8d26\u6237\u4f59\u989d",
};
const methodTextMap = {
alipay: "支付宝",
@@ -168,12 +182,15 @@
bank: "银行卡",
bank_card: "银行卡",
};
const incomeFlowTypes = new Set(["recharge", "sales", "expert"]);
const outcomeFlowTypes = new Set(["withdraw", "purchase"]);
let currentPage = 1;
let pageSize = 10;
let totalRow = 0;
let currentStatus = "";
let currentSource = "";
let currentFlowType = "withdraw";
function logout() {
localStorage.removeItem("token");
@@ -249,19 +266,40 @@
}
function renderFilters() {
renderFlowTabs();
$(".tl-filter-group").toggle(currentFlowType === "withdraw");
renderSegment("#statusFilters", statusOptions, currentStatus, function (value) {
currentStatus = value;
currentPage = 1;
loadWithdrawList();
loadWalletFlowList();
});
renderSegment("#sourceFilters", sourceOptions, currentSource, function (value) {
currentSource = value;
currentPage = 1;
loadWithdrawList();
loadWalletFlowList();
});
updateFilterSummary();
}
function renderFlowTabs() {
const $wrap = $("#walletFlowTabs").empty();
$.each(flowTypeOptions, function (_, item) {
const $button = $('<button class="tl-flow-tab" type="button"></button>');
$button.attr("data-type", item.value);
$button.html("<span>" + escapeHtml(item.label) + "</span><em>" + escapeHtml(item.desc) + "</em>");
$button.toggleClass("active", item.value === currentFlowType);
$button.on("click", function () {
currentFlowType = item.value;
currentPage = 1;
currentStatus = "";
currentSource = "";
renderFilters();
loadWalletFlowList();
});
$wrap.append($button);
});
}
function renderSegment(selector, options, selectedValue, onChange) {
const $wrap = $(selector).empty();
$.each(options, function (_, item) {
@@ -277,6 +315,10 @@
}
function updateFilterSummary() {
if (currentFlowType !== "withdraw") {
$("#filterSummary").text((flowTypeMap[currentFlowType] && flowTypeMap[currentFlowType].label) || "\u8d44\u91d1\u6d41\u6c34");
return;
}
const statusLabel = statusOptions.find((item) => item.value === currentStatus)?.label || "全部";
const sourceLabel = sourceOptions.find((item) => item.value === currentSource)?.label || "全部";
$("#filterSummary").text(statusLabel + "状态 / " + sourceLabel + "来源");
@@ -299,7 +341,7 @@
}
function getStatus(item) {
return fieldValue(item, ["status", "withdrawStatus", "auditStatus"]);
return item.status ?? item.withdrawStatus ?? item.auditStatus ?? item.flowStatus ?? item.payStatus ?? "";
}
function getStatusText(item) {
@@ -314,7 +356,7 @@
function getSourceText(item) {
const source = fieldValue(item, ["sourceWallet", "walletType"]);
return fieldValue(item, ["sourceWalletText", "walletTypeText"]) || sourceTextMap[source] || source || "--";
return fieldValue(item, ["sourceWalletText", "walletTypeText", "flowTypeText", "typeText"]) || sourceTextMap[source] || source || getFlowTypeText(item);
}
function getMethodText(item) {
@@ -334,22 +376,69 @@
}
function getCreateTime(item) {
const time = fieldValue(item, ["createTime", "createdTime", "applyTime", "submitTime", "updateTime"]);
const time = fieldValue(item, ["createTime", "createdTime", "applyTime", "submitTime", "payTime", "updateTime"]);
return DateUtil.formatDateTimeToYMDHM(time) || time || "--";
}
function loadWithdrawList() {
function getFlowTypeText(item) {
const flowType = fieldValue(item, ["flowType", "type"]) || currentFlowType;
return fieldValue(item, ["flowTypeText", "typeText"]) || (flowTypeMap[flowType] && flowTypeMap[flowType].label) || flowType || "\u8d44\u91d1\u6d41\u6c34";
}
function getRecordSourceText(item) {
const source = fieldValue(item, ["withdrawType", "receiveType", "walletType", "paymentType", "sourceWallet"]);
return (
fieldValue(item, ["withdrawTypeText", "receiveTypeText", "walletTypeText", "paymentTypeText", "sourceWalletText", "remark", "description"]) ||
methodTextMap[source] ||
sourceTextMap[source] ||
source ||
"--"
);
}
function getDisplayAmount(item) {
const amount = getAmount(item);
const flowType = fieldValue(item, ["flowType", "type"]) || currentFlowType;
const isIncome = amount > 0 || (amount === Math.abs(amount) && incomeFlowTypes.has(flowType));
const isOutcome = amount < 0 || outcomeFlowTypes.has(flowType);
const sign = isIncome && !isOutcome ? "+" : isOutcome ? "-" : "";
return {
text: sign + money(Math.abs(amount)) + " \u5143",
className: isIncome && !isOutcome ? "income" : isOutcome ? "outcome" : "neutral",
};
}
function renderTableHeader() {
const $header = $("#withdrawTable thead tr").empty();
const columns =
currentFlowType === "withdraw"
? ["\u7533\u8bf7\u5355\u53f7", "\u63d0\u73b0\u6765\u6e90", "\u63d0\u73b0\u65b9\u5f0f", "\u91d1\u989d", "\u6536\u6b3e\u8d26\u6237", "\u72b6\u6001", "\u7533\u8bf7\u65f6\u95f4"]
: ["\u6d41\u6c34\u53f7", "\u7c7b\u578b", "\u91d1\u989d", "\u65b9\u5f0f/\u6765\u6e90", "\u72b6\u6001", "\u65f6\u95f4"];
$.each(columns, function (_, title) {
$header.append("<th>" + escapeHtml(title) + "</th>");
});
}
function isWithdrawRecordType() {
return currentFlowType === "withdraw";
}
function loadWalletFlowList() {
const params = {
pageNum: currentPage,
pageSize: pageSize,
};
if (currentStatus) params.status = currentStatus;
if (currentSource) params.sourceWallet = currentSource;
if (isWithdrawRecordType()) {
if (currentStatus) params.status = currentStatus;
if (currentSource) params.sourceWallet = currentSource;
} else {
params.flowType = currentFlowType;
}
$("#withdrawTable").addClass("loading");
ApiClient.get(ApiClient.API.walletWithdrawList, params)
ApiClient.get(isWithdrawRecordType() ? ApiClient.API.walletWithdrawList : ApiClient.API.walletFlowList, params)
.then(function (res) {
if (!isSuccess(res)) throw new Error(res.msg || "获取提现记录失败");
if (!isSuccess(res)) throw new Error(res.msg || "获取记录失败");
const data = pickData(res) || res || {};
const records = getRecords(data, res);
totalRow = getTotal(data, records, res);
@@ -357,7 +446,7 @@
renderPagination();
})
.catch(function (err) {
layui.layer.msg(err.message || "获取提现记录失败", { icon: 2 });
layui.layer.msg(err.message || "获取记录失败", { icon: 2 });
totalRow = 0;
renderTable([]);
renderPagination();
@@ -370,9 +459,12 @@
function renderTable(records) {
const $tbody = $("#withdrawTable tbody").empty();
let pageAmount = 0;
renderTableHeader();
if (!records.length) {
$("#withdrawTable").addClass("tl-table-hidden");
$("#emptyState").addClass("show");
$("#emptyState .tl-empty-title").text("\u6682\u65e0\u8bb0\u5f55");
$("#emptyState .tl-empty-desc").text(((flowTypeMap[currentFlowType] && flowTypeMap[currentFlowType].label) || "\u5f53\u524d\u7c7b\u578b") + "\u6682\u65e0\u8d44\u91d1\u6d41\u6c34");
$(".tl-summary").addClass("is-hidden");
} else {
$("#withdrawTable").removeClass("tl-table-hidden");
@@ -384,15 +476,27 @@
const amount = getAmount(item);
pageAmount += Math.abs(amount);
const $row = $("<tr></tr>");
$row.html(
"<td>" + escapeHtml(getRecordNo(item)) + "</td>" +
"<td>" + escapeHtml(getSourceText(item)) + "</td>" +
"<td>" + escapeHtml(getMethodText(item)) + "</td>" +
'<td class="tl-amount">-' + escapeHtml(money(Math.abs(amount))) + "</td>" +
"<td>" + escapeHtml(getReceiveAccountText(item)) + "</td>" +
'<td><span class="tl-status ' + getStatusClass(item) + '">' + escapeHtml(getStatusText(item)) + "</span></td>" +
"<td>" + escapeHtml(getCreateTime(item)) + "</td>",
);
if (isWithdrawRecordType()) {
$row.html(
"<td>" + escapeHtml(getRecordNo(item)) + "</td>" +
"<td>" + escapeHtml(getSourceText(item)) + "</td>" +
"<td>" + escapeHtml(getMethodText(item)) + "</td>" +
'<td class="tl-amount outcome">-' + escapeHtml(money(Math.abs(amount))) + " \u5143</td>" +
"<td>" + escapeHtml(getReceiveAccountText(item)) + "</td>" +
'<td><span class="tl-status ' + getStatusClass(item) + '">' + escapeHtml(getStatusText(item)) + "</span></td>" +
"<td>" + escapeHtml(getCreateTime(item)) + "</td>",
);
} else {
const amountInfo = getDisplayAmount(item);
$row.html(
"<td>" + escapeHtml(getRecordNo(item)) + "</td>" +
"<td>" + escapeHtml(getFlowTypeText(item)) + "</td>" +
'<td class="tl-amount ' + amountInfo.className + '">' + escapeHtml(amountInfo.text) + "</td>" +
"<td>" + escapeHtml(getRecordSourceText(item)) + "</td>" +
'<td><span class="tl-status ' + getStatusClass(item) + '">' + escapeHtml(getStatusText(item)) + "</span></td>" +
"<td>" + escapeHtml(getCreateTime(item)) + "</td>",
);
}
$tbody.append($row);
});
@@ -417,7 +521,7 @@
jump: function (obj, first) {
if (first) return;
currentPage = obj.curr;
loadWithdrawList();
loadWalletFlowList();
},
});
});
@@ -427,6 +531,8 @@
const query = new URLSearchParams(window.location.search);
const status = query.get("status") || "";
const sourceWallet = query.get("sourceWallet") || "";
const flowType = query.get("flowType") || "";
if (flowTypeMap[flowType]) currentFlowType = flowType;
if (statusOptions.some((item) => item.value === status)) currentStatus = status;
if (sourceOptions.some((item) => item.value === sourceWallet)) currentSource = sourceWallet;
}
@@ -442,7 +548,7 @@
renderFilters();
CommonUtil.loadPageConfig({ ads: false, links: false });
CommonUtil.requireAuth(function () {
loadWithdrawList();
loadWalletFlowList();
});
});
</script>
+2 -2
View File
@@ -185,7 +185,7 @@
function loadAgreementDetail() {
var $contentEl = $("#articleContent");
$contentEl.html('<p style="text-align:center;color:var(--text-muted);padding:40px 0;">加载中...</p>');
ApiClient.post("/api/web/agreement/getByType?type=2").then(function (res) {
ApiClient.get(ApiClient.API.agreement(2)).then(function (res) {
if (res.code === 0 && res.data) {
var article = res.data;
if (article.title) {
@@ -254,4 +254,4 @@
});
</script>
</body>
</html>
</html>
+2 -2
View File
@@ -229,7 +229,7 @@
function loadAgreementDetail() {
var $contentEl = $("#articleContent");
$contentEl.html('<p style="text-align:center;color:var(--text-muted);padding:40px 0;">加载中...</p>');
ApiClient.post("/api/web/agreement/getByType?type=1").then(function (res) {
ApiClient.get(ApiClient.API.agreement(1)).then(function (res) {
if (res.code === 0 && res.data) {
var article = res.data;
if (article.title) {
@@ -298,4 +298,4 @@
});
</script>
</body>
</html>
</html>
+98
View File
@@ -517,6 +517,104 @@ body:has(.auth-container) {
background: linear-gradient(135deg, #27ae60, #1a8f50);
}
/* === Captcha Dialog ======================================== */
.captcha-dialog {
padding: 24px;
color: var(--text-dark, #1a1a2e);
}
.captcha-dialog-title {
font-size: 18px;
font-weight: 800;
line-height: 1.3;
}
.captcha-dialog-desc {
margin-top: 8px;
font-size: 13px;
line-height: 1.6;
color: var(--text-muted, #6b7280);
}
.captcha-dialog-row {
display: flex;
gap: 10px;
align-items: center;
margin-top: 18px;
}
.captcha-dialog-input {
flex: 1;
min-width: 0;
height: 44px;
padding: 0 12px;
border: 1px solid var(--border-light, #e5e7eb);
border-radius: var(--radius-md, 10px);
font-size: 14px;
outline: none;
transition: border-color 0.2s ease, box-shadow 0.2s ease;
}
.captcha-dialog-input:focus {
border-color: var(--primary, #102b6a);
box-shadow: 0 0 0 3px rgba(16, 43, 106, 0.08);
}
.captcha-dialog-img-box {
width: 120px;
height: 44px;
border: 1px solid var(--border-light, #e5e7eb);
border-radius: var(--radius-md, 10px);
overflow: hidden;
cursor: pointer;
background: var(--bg-light, #f4f6fb);
flex: 0 0 120px;
}
.captcha-dialog-img {
width: 100%;
height: 100%;
object-fit: cover;
display: block;
}
.captcha-dialog-tip {
margin-top: 10px;
font-size: 12px;
color: var(--text-muted, #6b7280);
}
.captcha-dialog-tip span {
color: var(--primary, #102b6a);
cursor: pointer;
font-weight: 700;
}
.captcha-dialog-actions {
display: flex;
justify-content: flex-end;
gap: 10px;
margin-top: 22px;
}
.captcha-dialog-btn {
min-width: 84px;
height: 38px;
border: 1px solid var(--border-light, #e5e7eb);
border-radius: var(--radius-md, 10px);
background: #ffffff;
color: var(--text-dark, #1a1a2e);
font-size: 13px;
font-weight: 700;
cursor: pointer;
}
.captcha-dialog-btn.primary {
border-color: transparent;
background: linear-gradient(135deg, var(--primary, #102b6a), var(--primary-light, #1a3f8f));
color: #ffffff;
}
/* === Submit Button ========================================= */
.auth-submit-btn {
width: 100%;
+405
View File
@@ -0,0 +1,405 @@
.cz-hidden {
display: none;
}
.cz-footer-link {
font-size: 12px;
color: rgba(255, 255, 255, 0.5);
}
.cz-balance-bar {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 16px;
margin-bottom: 24px;
}
.cz-balance-bar__item {
background: var(--white);
border-radius: var(--radius-lg);
padding: 20px 24px;
box-shadow: var(--shadow-sm);
display: flex;
align-items: center;
gap: 16px;
border: 1px solid var(--border-light);
transition: all 0.3s;
}
.cz-balance-bar__item:hover {
border-color: transparent;
box-shadow: var(--shadow-md);
transform: translateY(-2px);
}
.cz-balance-bar__icon {
width: 48px;
height: 48px;
border-radius: 14px;
display: flex;
align-items: center;
justify-content: center;
font-size: 22px;
flex-shrink: 0;
}
.cz-balance-bar__icon.account {
background: rgba(16, 43, 106, 0.08);
color: var(--primary);
}
.cz-balance-bar__label {
font-size: 13px;
color: var(--text-muted);
}
.cz-balance-bar__value {
font-size: 24px;
font-weight: 800;
color: var(--text-dark);
line-height: 1.2;
}
.cz-balance-bar__value .unit {
font-size: 14px;
font-weight: 400;
color: var(--text-muted);
margin-left: 2px;
}
.cz-package-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 14px;
margin-bottom: 24px;
}
.cz-package-loading {
grid-column: 1/-1;
text-align: center;
padding: 40px;
color: var(--text-muted);
}
.cz-package-loading i {
font-size: 28px;
display: block;
margin-bottom: 8px;
color: var(--primary);
}
.cz-pkg {
position: relative;
background: var(--bg-light);
border: 2px solid var(--border-light);
border-radius: var(--radius-lg);
padding: 20px 16px;
text-align: center;
cursor: pointer;
transition: all 0.3s;
user-select: none;
overflow: hidden;
}
.cz-pkg:hover {
border-color: var(--primary);
transform: translateY(-2px);
box-shadow: var(--shadow-sm);
}
.cz-pkg.selected {
border-color: var(--primary);
background: rgba(16, 43, 106, 0.04);
box-shadow: 0 0 0 3px rgba(16, 43, 106, 0.12);
}
.cz-pkg.selected::after {
content: "\2713";
position: absolute;
top: 0;
right: 0;
width: 24px;
height: 24px;
background: var(--primary);
color: #fff;
font-size: 12px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 0 var(--radius-lg) 0 var(--radius-lg);
}
.cz-pkg__price {
font-size: 28px;
font-weight: 800;
color: var(--primary);
line-height: 1.2;
}
.cz-pkg__price span {
font-size: 14px;
font-weight: 600;
}
.cz-pkg__credit {
display: inline-block;
margin-top: 8px;
padding: 3px 10px;
border-radius: 999px;
font-size: 12px;
font-weight: 700;
background: rgba(253, 185, 51, 0.12);
color: var(--gold);
}
.cz-pkg__bonus {
display: inline-block;
margin-top: 4px;
padding: 2px 8px;
border-radius: 999px;
font-size: 11px;
font-weight: 600;
background: rgba(241, 90, 34, 0.1);
color: var(--orange);
}
.cz-pkg.selected .cz-pkg__price {
color: var(--primary);
}
.cz-pkg__tag {
position: absolute;
top: 8px;
left: 8px;
padding: 2px 8px;
border-radius: 999px;
font-size: 10px;
font-weight: 700;
letter-spacing: 0.5px;
color: #fff;
}
.cz-pkg__tag.hot {
background: linear-gradient(135deg, #ff6b35, #f15a22);
}
.cz-pkg__tag.rec {
background: linear-gradient(135deg, var(--primary), #1a3f8f);
}
.cz-qr-section {
text-align: center;
padding: 24px 0;
}
.cz-qr-header {
margin-bottom: 16px;
}
.cz-qr-icon {
width: 48px;
height: 48px;
border-radius: 50%;
background: rgba(7, 193, 96, 0.1);
color: #07c160;
display: inline-flex;
align-items: center;
justify-content: center;
font-size: 24px;
margin-bottom: 8px;
}
.cz-qr-title {
font-size: 18px;
font-weight: 700;
color: var(--text-dark);
}
.cz-qr-subtitle {
font-size: 13px;
color: var(--text-muted);
margin-top: 4px;
}
.cz-qr-amount {
font-size: 32px;
font-weight: 800;
color: var(--orange);
margin: 16px 0;
}
.cz-qr-amount span {
font-size: 16px;
font-weight: 600;
color: var(--text-muted);
}
.cz-qr-wrap {
display: inline-block;
padding: 16px;
background: #fff;
border: 2px solid var(--border-light);
border-radius: var(--radius-lg);
margin-bottom: 12px;
}
.cz-qr-wrap img,
.cz-qr-wrap canvas {
display: block;
width: 200px;
height: 200px;
}
.cz-qr-hint {
font-size: 13px;
color: var(--text-muted);
margin-top: 8px;
}
.cz-qr-timer {
font-size: 14px;
color: var(--orange);
margin-top: 8px;
font-weight: 700;
}
.cz-qr-cancel {
margin-top: 16px;
padding: 8px 24px;
border: 1px solid var(--border-light);
border-radius: 999px;
background: transparent;
color: var(--text-muted);
font-size: 13px;
cursor: pointer;
transition: all 0.25s;
}
.cz-qr-cancel:hover {
border-color: var(--primary);
color: var(--primary);
}
.cz-pay-result {
text-align: center;
padding: 40px 0;
}
.cz-pay-result__icon {
width: 72px;
height: 72px;
border-radius: 50%;
display: inline-flex;
align-items: center;
justify-content: center;
font-size: 36px;
margin-bottom: 16px;
}
.cz-pay-result__icon.success {
background: rgba(40, 167, 69, 0.12);
color: var(--green);
}
.cz-pay-result__icon.timeout {
background: rgba(255, 193, 7, 0.15);
color: #ffc107;
}
.cz-pay-result__title {
font-size: 20px;
font-weight: 700;
color: var(--text-dark);
margin-bottom: 8px;
}
.cz-pay-result__desc {
font-size: 13px;
color: var(--text-muted);
margin-bottom: 20px;
}
.cz-pay-result__btn {
display: inline-block;
padding: 10px 32px;
border-radius: 999px;
font-size: 14px;
font-weight: 700;
background: linear-gradient(135deg, var(--primary), #1a3f8f);
color: #fff;
text-decoration: none;
cursor: pointer;
border: none;
transition: all 0.3s;
}
.cz-pay-result__btn:hover {
box-shadow: 0 6px 20px rgba(16, 43, 106, 0.4);
transform: translateY(-2px);
}
.cz-tips-card {
background: var(--white);
border-radius: var(--radius-lg);
padding: 20px 24px;
border: 1px solid var(--border-light);
margin-top: 24px;
}
.cz-tips-title {
font-size: 14px;
font-weight: 700;
color: var(--text-dark);
margin-bottom: 12px;
}
.cz-tips-title i {
color: var(--primary);
margin-right: 4px;
}
.cz-tips-list {
margin: 0;
padding-left: 18px;
}
.cz-tips-list li {
font-size: 12px;
color: var(--text-muted);
line-height: 2;
position: relative;
}
@media (max-width: 768px) {
.cz-balance-bar {
grid-template-columns: 1fr;
}
.cz-package-grid {
grid-template-columns: repeat(2, 1fr);
gap: 10px;
}
.cz-pkg {
padding: 16px 12px;
}
.cz-pkg__price {
font-size: 22px;
}
}
@media (max-width: 480px) {
.cz-package-grid {
grid-template-columns: repeat(2, 1fr);
gap: 8px;
}
.cz-pkg__price {
font-size: 20px;
}
.cz-pkg__credit {
font-size: 11px;
}
}
+51 -1
View File
@@ -452,7 +452,7 @@
/* Action Section (legacy 2-column card layout) */
.action-section {
display: grid;
grid-template-columns: repeat(2, 1fr);
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
gap: 20px;
margin-bottom: 24px;
}
@@ -524,6 +524,56 @@
pointer-events: none;
}
.custom-modal-mask {
position: fixed;
inset: 0;
z-index: 9999;
display: flex;
align-items: center;
justify-content: center;
background: rgba(0, 0, 0, 0.45);
}
.custom-modal-content {
width: min(360px, calc(100vw - 32px));
padding: 26px 24px 22px;
border-radius: var(--radius-lg);
background: #fff;
box-shadow: var(--shadow-lg);
text-align: center;
}
.custom-modal-title {
margin: 0 0 18px;
color: var(--primary);
font-size: 18px;
font-weight: 800;
}
.custom-qr-container {
display: inline-flex;
align-items: center;
justify-content: center;
padding: 12px;
border: 1px solid var(--border-light);
border-radius: var(--radius-md);
background: #fff;
}
.custom-modal-close {
display: block;
width: 100%;
height: 38px;
margin-top: 18px;
border: 0;
border-radius: var(--radius-md);
background: var(--primary);
color: #fff;
cursor: pointer;
font-size: 14px;
font-weight: 800;
}
/* Team Section */
.team-section {
background: var(--white, #fff);
+66
View File
@@ -1544,6 +1544,61 @@
grid-template-columns: repeat(2, minmax(0, 1fr));
}
.cxz-recommend-section {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 16px;
margin-top: 24px;
}
.cxz-recommend-card {
padding: 18px 20px;
border: 1px solid #e7edf6;
border-radius: 12px;
background: #fff;
box-shadow: var(--shadow-sm, 0 2px 8px rgba(0, 0, 0, 0.04));
}
.cxz-recommend-title {
margin-bottom: 12px;
color: var(--primary);
font-size: 16px;
font-weight: 800;
}
.cxz-recommend-list {
display: grid;
gap: 10px;
margin: 0;
padding: 0;
list-style: none;
}
.cxz-recommend-list li {
display: grid;
grid-template-columns: minmax(0, 1fr) auto;
gap: 12px;
align-items: center;
color: var(--text-muted);
font-size: 13px;
}
.cxz-recommend-list a {
overflow: hidden;
color: var(--text-dark);
text-overflow: ellipsis;
white-space: nowrap;
}
.cxz-recommend-list a:hover {
color: var(--primary);
}
.cxz-recommend-empty {
grid-template-columns: 1fr !important;
color: var(--text-muted);
}
.cxz-nav-item {
position: relative;
overflow: hidden;
@@ -1664,6 +1719,17 @@
border: 1px solid #e7edf6;
}
@media (max-width: 768px) {
.cxz-recommend-section {
grid-template-columns: 1fr;
}
.cxz-recommend-list li {
grid-template-columns: 1fr;
gap: 4px;
}
}
.cxz-comment-header {
display: flex;
align-items: center;
+219
View File
@@ -0,0 +1,219 @@
* {
box-sizing: border-box;
}
body {
min-height: 100vh;
margin: 0;
color: #1f2933;
font-family:
-apple-system, BlinkMacSystemFont, "Segoe UI", "Microsoft YaHei",
sans-serif;
background:
radial-gradient(circle at 72% 18%, rgba(255, 190, 98, 0.45), rgba(255, 190, 98, 0) 32%),
linear-gradient(135deg, #ff7a1a 0%, #ff6f12 54%, #ff8a23 100%);
}
.page {
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
padding: 40px 16px;
}
.card {
width: 420px;
max-width: 100%;
padding: 28px 30px 30px;
border-radius: 10px;
background: #ffffff;
box-shadow: 0 18px 48px rgba(124, 38, 4, 0.18);
}
.brand {
margin-bottom: 20px;
color: #de2103;
font-size: 28px;
font-weight: 800;
text-align: center;
}
.title {
margin-bottom: 8px;
font-size: 18px;
font-weight: 700;
text-align: center;
}
.desc {
min-height: 22px;
margin-bottom: 18px;
color: #667085;
font-size: 13px;
line-height: 22px;
text-align: center;
}
.status {
display: flex;
align-items: center;
justify-content: center;
gap: 10px;
margin: 14px 0 20px;
color: #de2103;
font-size: 14px;
}
.spinner {
width: 18px;
height: 18px;
border: 2px solid #f5c7bd;
border-top-color: #de2103;
border-radius: 50%;
animation: spin 0.8s linear infinite;
}
@keyframes spin {
to {
transform: rotate(360deg);
}
}
.bind-form {
display: none;
}
.form-row {
margin-top: 14px;
}
.form-row label {
display: block;
margin-bottom: 6px;
color: #344054;
font-size: 13px;
font-weight: 600;
}
.input,
.sms-input {
width: 100%;
height: 42px;
padding: 0 12px;
border: 1px solid #d9dee7;
border-radius: 5px;
color: #1f2933;
font-size: 14px;
outline: none;
}
.input:focus,
.sms-input:focus {
border-color: #de2103;
box-shadow: 0 0 0 3px rgba(222, 33, 3, 0.08);
}
.sms-row {
display: flex;
gap: 10px;
}
.captcha-row {
display: none;
}
.captcha-box {
display: flex;
gap: 10px;
}
.captcha-box .input {
flex: 1;
min-width: 0;
}
.captcha-image {
flex: 0 0 108px;
height: 42px;
border: 1px solid #d9dee7;
border-radius: 5px;
background: #f8fafc;
cursor: pointer;
object-fit: cover;
}
.sms-input {
flex: 1;
min-width: 0;
}
.sms-button {
flex: 0 0 108px;
height: 42px;
border: 1px solid #de2103;
border-radius: 5px;
background: #fff8f6;
color: #de2103;
cursor: pointer;
}
.sms-button:disabled,
.submit:disabled {
cursor: not-allowed;
opacity: 0.65;
}
.submit {
width: 100%;
height: 42px;
margin-top: 20px;
border: 0;
border-radius: 5px;
background: linear-gradient(180deg, #ff4a22 0%, #de2103 100%);
color: #ffffff;
font-size: 14px;
font-weight: 700;
cursor: pointer;
}
.tips {
margin-top: 12px;
padding: 10px 12px;
border: 1px solid #f2d2ca;
border-radius: 5px;
background: #fff8f6;
color: #8a4b42;
font-size: 12px;
line-height: 20px;
}
.error {
display: none;
margin-top: 14px;
padding: 10px 12px;
border: 1px solid #f4c7c7;
border-radius: 5px;
background: #fff5f5;
color: #b42318;
font-size: 13px;
line-height: 20px;
}
.actions {
display: flex;
justify-content: center;
gap: 16px;
margin-top: 16px;
}
.actions a,
.actions button {
border: 0;
background: transparent;
color: #de2103;
font-size: 13px;
text-decoration: none;
cursor: pointer;
}
+59
View File
@@ -79,6 +79,49 @@
background: #f8fafd;
}
.tl-flow-tabs {
display: grid;
grid-template-columns: repeat(5, minmax(120px, 1fr));
gap: 10px;
width: 100%;
}
.tl-flow-tab {
min-height: 70px;
padding: 12px 14px;
border: 1px solid var(--border-light);
border-radius: var(--radius-md);
background: #fff;
text-align: left;
cursor: pointer;
transition: all 0.2s;
}
.tl-flow-tab span,
.tl-flow-tab em {
display: block;
}
.tl-flow-tab span {
color: var(--text-dark);
font-size: 14px;
font-weight: 800;
}
.tl-flow-tab em {
margin-top: 6px;
color: var(--text-muted);
font-size: 12px;
font-style: normal;
}
.tl-flow-tab.active,
.tl-flow-tab:hover {
border-color: var(--primary);
background: rgba(16, 43, 106, 0.06);
box-shadow: var(--shadow-sm);
}
.tl-filter-group {
display: flex;
align-items: center;
@@ -191,6 +234,18 @@
font-weight: 800;
}
.tl-amount.income {
color: #15824a;
}
.tl-amount.outcome {
color: #e04828;
}
.tl-amount.neutral {
color: var(--text-dark);
}
.tl-status {
display: inline-flex;
align-items: center;
@@ -291,4 +346,8 @@
.tl-summary {
grid-template-columns: 1fr;
}
.tl-flow-tabs {
grid-template-columns: 1fr;
}
}
+17 -12
View File
@@ -17,11 +17,15 @@ const $ = layui.$;
CommonUtil.loadPageConfig({ ads: false, links: false });
checkPaidPublishPermission().then(function (result) {
if (!result.canPaid) {
layui.layer.alert(result.reason || "暂无付费文章发布权限。", {
title: "暂无权限",
});
$(".mianfeiThreeSelect").addClass("is-disabled");
$("#gmjl").prop("disabled", true);
layui.layer.alert(
result.reason || "暂无付费文章发布权限",
{
title: "暂无权限",
},
function () {
window.location.href = "fabumianfeiwenzhang.html";
},
);
return;
}
@@ -145,19 +149,20 @@ const $ = layui.$;
const status = String(
res.data.expertStatus || res.data.expert_status || "",
).toLowerCase();
const isRegularExpert =
isTruthy(res.data.is_regular_expert) ||
isTruthy(res.data.isRegularExpert) ||
isTruthy(res.data.regularExpert) ||
/正式|regular|formal/.test(status);
const hasExpertIdentity =
isTruthy(res.data.is_expert) ||
isTruthy(res.data.isExpert) ||
isTruthy(res.data.expert) ||
isTruthy(res.data.is_regular_expert) ||
isTruthy(res.data.isRegularExpert) ||
isTruthy(res.data.regularExpert) ||
isTruthy(res.data.internExpert) ||
isTruthy(res.data.isInternExpert) ||
isRegularExpert ||
/实习|intern|trial|正式|regular|formal/.test(status);
if (!hasExpertIdentity) {
return { canPaid: false, reason: "专家才能发布付费文章。" };
if (!hasExpertIdentity || !isRegularExpert) {
return { canPaid: false, reason: "正式专家才能发布付费文章。" };
}
return loadTodayPublishedFreeCount().then(function (count) {
+156 -8
View File
@@ -13,6 +13,7 @@ const ApiClient = {
authVerifyConfig: '/api/web/auth/verify-config',
authSlideCaptcha: '/api/web/auth/slide-captcha',
authSlideCaptchaVerify: '/api/web/auth/slide-captcha/verify',
authHeartbeat: '/api/web/auth/heartbeat',
authForgotPasswordSmsCode: '/api/web/auth/forgot-password/sms-code',
authForgotPasswordReset: '/api/web/auth/forgot-password/reset',
authSocialLogin: '/api/web/auth/social-login',
@@ -20,7 +21,23 @@ const ApiClient = {
authSocialBind: '/api/web/auth/social-bind',
authSocialBindPhone: '/api/web/auth/social-bind-phone',
authSocialBindPhoneSmsCode: '/api/web/auth/social-bind-phone/sms-code',
appDownloadInfo: '/api/web/app/getDownloadInfo',
agreement: (type) => `/api/web/agreement/${encodeURIComponent(type || '')}`,
noticeList: '/api/web/notice/list',
noticeDetail: (id) => `/api/web/notice/${encodeURIComponent(id || '')}`,
friendLinks: '/api/web/link/friend',
otherRecommendLinks: '/api/web/link/other',
latestLotteryResult: '/api/web/lottery/result/latest',
lotteryResultList: '/api/web/lottery/result/list',
webConfigList: '/api/web/config/list',
webConfig: '/api/web/config',
configList: '/api/web/config/list',
configSite: '/api/web/config/site',
bannerList: '/api/web/banner/list',
adList: '/api/web/ad/list',
freeArticleRecommend: '/api/web/free-article/recommend',
freeArticleList: '/api/web/free-article/list',
freeArticleLatest: '/api/web/free-article/latest',
referralShareInfo: '/api/web/referral/share-info',
referralSummary: '/api/web/referral/summary',
referralInviteList: '/api/web/referral/invite-list',
@@ -38,8 +55,11 @@ const ApiClient = {
menuChildren: (id) => `/api/web/menu/${encodeURIComponent(id)}/children`,
payArticleExpertProfile: '/api/web/pay-article/expert-profile',
payArticleExpertList: '/api/web/pay-article/expert-list',
payArticleExpertRank: '/api/web/pay-article/expert-rank',
payArticlePublishOptions: (menuId) => `/api/web/pay-article/publish/options?menuId=${encodeURIComponent(menuId || '')}`,
payArticlePublish: '/api/web/pay-article/publish',
payArticleLatest: '/api/web/pay-article/latest',
payArticleRelated: (id) => `/api/web/pay-article/${encodeURIComponent(id || '')}/related`,
payArticleCollect: (id) => `/api/web/pay-article/article-collect/${encodeURIComponent(id)}`,
expertCollect: (expertId, accountType = 1) =>
`/api/web/pay-article/expert-collect?${new URLSearchParams({
@@ -50,6 +70,7 @@ const ApiClient = {
walletFlowList: '/api/web/wallet/flow/list',
walletRechargeConfigList: '/api/web/wallet/recharge/config/list',
walletRechargeOrder: '/api/web/wallet/recharge/order',
walletRechargeOrderStatus: (outTradeNo) => `/api/web/wallet/recharge/order/status/${encodeURIComponent(outTradeNo || '')}`,
walletRechargeNotify: '/api/web/wallet/recharge/notify',
walletTransferToAccount: '/api/web/wallet/transfer-to-account',
walletWithdraw: '/api/web/wallet/withdraw',
@@ -62,10 +83,16 @@ const ApiClient = {
walletReceiveAccountDelete: (id) => `/api/web/wallet/receive-account/${encodeURIComponent(id)}`,
walletReceiveAccountUploadQrcode: '/api/web/wallet/receive-account/upload-qrcode',
mineSummary: '/api/web/mine/summary',
mineFreeCollectList: '/api/web/mine/free-collect/list',
minePayCollectList: '/api/web/mine/pay-collect/list',
mineExpertCollectList: '/api/web/mine/expert-collect/list',
mineSignInList: '/api/web/mine/sign-in/list',
mineSignInStatus: '/api/web/mine/sign-in/status',
mineSignIn: '/api/web/mine/sign-in',
walletPurchasePayArticle: '/api/web/wallet/purchase/pay-article',
freeArticlePublish: '/api/web/free-article/publish',
mineFreeArticleList: '/api/web/mine/free-article/list',
mineFreeArticleDetail: (id) => `/api/web/mine/free-article/${encodeURIComponent(id || '')}`,
minePayArticleList: '/api/web/mine/pay-article/list',
minePayArticleDetail: (id) => `/api/web/mine/pay-article/${encodeURIComponent(id)}`,
minePurchasedPayArticleList: '/api/web/mine/purchased-pay-article/list',
@@ -81,6 +108,8 @@ const ApiClient = {
fc3dSchemeList: '/api/web/fc3d/scheme/list',
fc3dSchemeDetail: (id) => `/api/web/fc3d/scheme/${encodeURIComponent(id)}`,
walletPurchaseFc3dScheme: '/api/web/wallet/purchase/fc3d-scheme',
lotteryTrendTools: '/api/web/lottery/trend/tools',
lotteryTrend: '/api/web/lottery/trend',
},
get baseUrl() {
@@ -146,7 +175,9 @@ const ApiClient = {
}
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
return this.normalizeResponse(await this.parseResponse(response), normalized);
const result = this.normalizeResponse(await this.parseResponse(response), normalized);
if (this.isAuthExpiredResponse(result)) this.handleAuthExpired();
return result;
})
.catch((error) => {
clearTimeout(timeoutId);
@@ -225,6 +256,19 @@ const ApiClient = {
return !!res && (res.code === 0 || res.code === 200 || res.success === true);
},
isAuthExpiredResponse(result) {
if (!result || typeof result !== 'object') return false;
const code = Number(result.code);
const message = String(result.msg || result.message || result.error || '').toLowerCase();
return (
code === 401 ||
code === 403 ||
code === 40101 ||
code === 40102 ||
message.indexOf('token') >= 0 && (message.indexOf('expired') >= 0 || message.indexOf('invalid') >= 0)
);
},
getToken() {
const raw = localStorage.getItem('token');
if (!raw) return '';
@@ -240,19 +284,76 @@ const ApiClient = {
return this.getToken();
},
clearToken() {
localStorage.removeItem('token');
localStorage.removeItem('userInfo');
localStorage.removeItem('user');
},
getLocalUserInfo() {
const raw = localStorage.getItem('userInfo') || localStorage.getItem('user') || '';
if (!raw) return {};
try {
return JSON.parse(raw) || {};
} catch (e) {
return {};
}
},
isJwtExpired(token) {
const parts = String(token || '').split('.');
if (parts.length < 2) return false;
try {
let payloadText = parts[1].replace(/-/g, '+').replace(/_/g, '/');
while (payloadText.length % 4) payloadText += '=';
const payload = JSON.parse(decodeURIComponent(escape(window.atob(payloadText))));
return payload.exp ? payload.exp * 1000 <= Date.now() : false;
} catch (e) {
return false;
}
},
isTokenExpired() {
const token = this.token();
if (!token) return true;
if (this.isJwtExpired(token)) return true;
const userInfo = this.getLocalUserInfo();
const expireTime = Number(userInfo.expireTime || userInfo.expire_time || userInfo.expiresAt || userInfo.expires_at || 0);
return expireTime ? expireTime <= Date.now() : false;
},
handleAuthExpired() {
if (typeof window === 'undefined') return;
this.clearToken();
const path = window.location.pathname || '';
if (/\/(login|reg|repwd)\.html$/i.test(path)) return;
const returnUrl = window.location.pathname + window.location.search;
const loginPath = '/login.html?redirect=' + encodeURIComponent(returnUrl);
window.location.href =
typeof CommonUtil !== 'undefined' && CommonUtil.siteHref
? CommonUtil.siteHref(loginPath)
: ((window.CURRENT_ENV === 'development' ? '/html/login.html?redirect=' : '/login.html?redirect=') + encodeURIComponent(returnUrl));
},
buildUrl(url, params) {
if (!params || params instanceof FormData) return url;
const clean = {};
Object.keys(params).forEach((key) => {
if (params[key] !== undefined && params[key] !== null && params[key] !== '') {
clean[key] = params[key];
}
});
const clean = this.cleanParams(params);
const query = new URLSearchParams(clean).toString();
if (!query) return url;
return url + (url.indexOf('?') >= 0 ? '&' : '?') + query;
},
cleanParams(params, omittedKeys = []) {
const clean = {};
const omitted = new Set(omittedKeys || []);
Object.keys(params || {}).forEach((key) => {
const value = params[key];
if (omitted.has(key)) return;
if (value !== undefined && value !== null && value !== '') clean[key] = value;
});
return clean;
},
async parseResponse(response) {
const contentType = response.headers.get('content-type') || '';
if (contentType.indexOf('application/json') >= 0) return response.json();
@@ -380,6 +481,21 @@ const ApiClient = {
if (Array.isArray(value.records)) return value.records;
if (Array.isArray(value.rows)) return value.rows;
if (Array.isArray(value.list)) return value.list;
if (Array.isArray(value.data)) return value.data;
return [];
},
unwrap(result, fallback) {
return this.pickData(result, fallback);
},
listFrom(source, keys = ['records', 'rows', 'list', 'data']) {
if (Array.isArray(source)) return source;
if (!source || typeof source !== 'object') return [];
for (const key of keys || []) {
const nested = this.asArray(source[key]);
if (nested.length) return nested;
}
return [];
},
@@ -416,7 +532,7 @@ const ApiClient = {
};
}
if (url === '/api/web/lotteryResult/getList') {
return { compose: () => this.requestWithMenu('/api/web/lottery/result/list', data) };
return { compose: () => this.requestLegacyLotteryResultCompat(data) };
}
if (url === '/api/web/freeArticle/getPageList') {
return { compose: () => this.requestWithMenu('/api/web/free-article/list', data) };
@@ -646,6 +762,26 @@ const ApiClient = {
return this.get(url, params);
},
async requestLegacyLotteryResultCompat(data = {}) {
const params = this.normalizePageParams(data || {});
const code = params.code || params.suoxie || params.lotteryCode || '';
const pageSize = params.pageSize || params.limit || params.count || 100;
if (code && this.API.lotteryTrend) {
try {
const trendRes = await this.get(this.API.lotteryTrend, {
code,
pageSize,
});
if (this.isSuccess(trendRes) && this.asArray(this.pickData(trendRes, [])).length) {
return trendRes;
}
} catch (e) {
// Fall back to the older result-list endpoint below.
}
}
return this.requestWithMenu('/api/web/lottery/result/list', data);
},
async loadLegacyExpertDetailCompat(data = {}) {
const cacheKey = JSON.stringify(this.normalizePageParams(data));
if (!this._legacyExpertDetailPromises) this._legacyExpertDetailPromises = {};
@@ -847,18 +983,30 @@ const ApiClient = {
},
get(url, params, options = {}) {
if (typeof options === 'function') {
return this.request({ url, method: 'GET', data: params || null }).then(options);
}
return this.request({ url, method: 'GET', data: params || null, ...options });
},
post(url, data, options = {}) {
if (typeof options === 'function') {
return this.request({ url, method: 'POST', data: data || null }).then(options);
}
return this.request({ url, method: 'POST', data: data || null, ...options });
},
put(url, data, options = {}) {
if (typeof options === 'function') {
return this.request({ url, method: 'PUT', data: data || null }).then(options);
}
return this.request({ url, method: 'PUT', data: data || null, ...options });
},
delete(url, data, options = {}) {
if (typeof options === 'function') {
return this.request({ url, method: 'DELETE', data: data || null }).then(options);
}
return this.request({ url, method: 'DELETE', data: data || null, ...options });
},
};
+42
View File
@@ -36,6 +36,48 @@ const CommonUtil = {
return /^(localhost|127\.0\.0\.1|0\.0\.0\.0)$/i.test(window.location.hostname);
},
safeHref(url) {
if (!url) return '#';
const value = String(url).trim();
const lowered = value.toLowerCase();
if (lowered.startsWith('javascript:') || lowered.startsWith('data:') || lowered.startsWith('vbscript:')) {
return '#';
}
return value;
},
siteHref(url) {
const value = this.safeHref(url);
if (!value || value === '#') return value;
if (/^(?:https?:)?\/\//i.test(value) || value.startsWith('javascript:')) return value;
if (!value.startsWith('/')) return value;
if (!this.isLocalHtmlPathMode()) return value;
if (value.startsWith('/html/')) return value;
const freeArticleMatch = value.match(/^\/mianfei\/([^/?#]+)\.html([?#].*)?$/i);
if (freeArticleMatch) {
return `/html/mianfei.html?id=${encodeURIComponent(decodeURIComponent(freeArticleMatch[1]))}${freeArticleMatch[2] || ''}`;
}
if (/^\/[^/?#]+\.html(?:[?#].*)?$/i.test(value)) return `/html${value}`;
return value;
},
isLocalHtmlPathMode() {
const hostname = window.location && window.location.hostname;
return hostname === 'localhost' || hostname === '127.0.0.1' || Boolean(window.location && window.location.port);
},
rewritePageRootLinks() {
const $ = this.$;
$('a[href^="/"]').each((index, link) => {
const $link = $(link);
const href = $link.attr('href');
const nextHref = this.siteHref(href);
if (href !== nextHref) $link.attr('href', nextHref);
});
},
freeArticleHref(articleId) {
const id = encodeURIComponent(articleId || '');
if (!id) return 'javascript:void(0)';
+77 -104
View File
@@ -1,114 +1,87 @@
/**
* 日期时间工具类
*/
class DateUtil {
/**
* 将日期字符串转换为"月日"格式例如"5月2日"
* @param {string|Date} dateStr - 日期字符串或Date对象 "2026-05-02" Date对象
* @returns {string} 返回格式化的日期字符串 "5月2日"
*/
static formatDateToMonthDay(dateStr) {
try {
const date = typeof dateStr === 'string' ? new Date(dateStr) : dateStr;
static formatDateToMonthDay(dateStr) {
const date = DateUtil._parseDate(dateStr);
if (!DateUtil._isValidDate(date)) return '';
return `${date.getMonth() + 1}${date.getDate()}`;
}
if (isNaN(date.getTime())) {
console.error('Invalid date:', dateStr);
return '';
}
static formatDateToYearMonthDay(dateStr) {
const date = DateUtil._parseDate(dateStr);
if (!DateUtil._isValidDate(date)) return '';
return `${date.getFullYear()}${date.getMonth() + 1}${date.getDate()}`;
}
const month = date.getMonth() + 1; // getMonth()返回0-11,需要加1
const day = date.getDate();
return `${month}${day}`;
} catch (error) {
console.error('Error formatting date:', error);
return '';
}
static formatDateToYMD(dateStr) {
const date = DateUtil._parseDate(dateStr);
if (!DateUtil._isValidDate(date)) return '';
return [
date.getFullYear(),
String(date.getMonth() + 1).padStart(2, '0'),
String(date.getDate()).padStart(2, '0'),
].join('-');
}
static formatDateTimeToYMDHMS(dateStr) {
const date = DateUtil._parseDate(dateStr);
if (!DateUtil._isValidDate(date)) return '';
return `${DateUtil.formatDateToYMD(date)} ${DateUtil._timeParts(date).join(':')}`;
}
static formatDateTimeToYMDHM(dateStr) {
const date = DateUtil._parseDate(dateStr);
if (!DateUtil._isValidDate(date)) return '';
const [hour, minute] = DateUtil._timeParts(date);
return `${DateUtil.formatDateToYMD(date)} ${hour}:${minute}`;
}
static formatTimeToHMS(dateStr) {
const date = typeof dateStr === 'number' ? new Date(dateStr) : DateUtil._parseDate(dateStr);
if (!DateUtil._isValidDate(date)) return '';
return DateUtil._timeParts(date).join(':');
}
static getCurrentDate() {
return new Date();
}
static isSameDay(date1, date2) {
const d1 = DateUtil._parseDate(date1);
const d2 = DateUtil._parseDate(date2);
if (!DateUtil._isValidDate(d1) || !DateUtil._isValidDate(d2)) return false;
return (
d1.getFullYear() === d2.getFullYear() &&
d1.getMonth() === d2.getMonth() &&
d1.getDate() === d2.getDate()
);
}
static _parseDate(dateStr) {
if (dateStr instanceof Date) return dateStr;
if (typeof dateStr === 'number') return new Date(dateStr);
if (typeof dateStr !== 'string') return null;
const value = dateStr.trim();
if (!value) return null;
if (/^\d{4}-\d{2}-\d{2}$/.test(value)) {
return new Date(value.replace(/-/g, '/'));
}
return new Date(value);
}
/**
* 将日期字符串转换为"年月日"格式例如"2026年5月2日"
* @param {string|Date} dateStr - 日期字符串或Date对象
* @returns {string} 返回格式化的日期字符串 "2026年5月2日"
*/
static formatDateToYearMonthDay(dateStr) {
try {
const date = typeof dateStr === 'string' ? new Date(dateStr) : dateStr;
static _isValidDate(date) {
return date instanceof Date && !Number.isNaN(date.getTime());
}
if (isNaN(date.getTime())) {
console.error('Invalid date:', dateStr);
return '';
}
const year = date.getFullYear();
const month = date.getMonth() + 1;
const day = date.getDate();
return `${year}${month}${day}`;
} catch (error) {
console.error('Error formatting date:', error);
return '';
}
}
/**
* 将日期字符串转换为"YYYY-MM-DD"格式
* @param {string|Date} dateStr - 日期字符串或Date对象
* @returns {string} 返回格式化的日期字符串 "2026-05-02"
*/
static formatDateToYMD(dateStr) {
try {
const date = typeof dateStr === 'string' ? new Date(dateStr) : dateStr;
if (isNaN(date.getTime())) {
console.error('Invalid date:', dateStr);
return '';
}
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
return `${year}-${month}-${day}`;
} catch (error) {
console.error('Error formatting date:', error);
return '';
}
}
/**
* 获取当前日期
* @returns {Date} 返回当前日期的Date对象
*/
static getCurrentDate() {
return new Date();
}
/**
* 比较两个日期是否为同一天
* @param {string|Date} date1 - 第一个日期
* @param {string|Date} date2 - 第二个日期
* @returns {boolean} 如果是同一天返回true否则返回false
*/
static isSameDay(date1, date2) {
try {
const d1 = typeof date1 === 'string' ? new Date(date1) : date1;
const d2 = typeof date2 === 'string' ? new Date(date2) : date2;
if (isNaN(d1.getTime()) || isNaN(d2.getTime())) {
return false;
}
return d1.getFullYear() === d2.getFullYear() &&
d1.getMonth() === d2.getMonth() &&
d1.getDate() === d2.getDate();
} catch (error) {
console.error('Error comparing dates:', error);
return false;
}
}
static _timeParts(date) {
return [
String(date.getHours()).padStart(2, '0'),
String(date.getMinutes()).padStart(2, '0'),
String(date.getSeconds()).padStart(2, '0'),
];
}
}
// 导出模块,兼容不同引入方式
if (typeof module !== 'undefined' && module.exports) {
module.exports = DateUtil;
module.exports = DateUtil;
} else if (typeof window !== 'undefined') {
window.DateUtil = DateUtil;
}
window.DateUtil = DateUtil;
}