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

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
+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;
}