彩先知pc
This commit is contained in:
@@ -0,0 +1,849 @@
|
||||
const ApiClient = {
|
||||
// 后端租户标识,所有接口请求都会带到 header 里。
|
||||
// 后面如果租户号更换,只需要改这里的默认值,或在 config.js 里配置 CURRENT_CONFIG.TENANT_ID。
|
||||
TENANT_ID: '000000',
|
||||
|
||||
API: {
|
||||
authCode: '/api/web/auth/code',
|
||||
authLogin: '/api/web/auth/login',
|
||||
authLogout: '/api/web/auth/logout',
|
||||
authRegister: '/api/web/auth/register',
|
||||
authSmsCode: '/api/web/auth/sms/code',
|
||||
authVerifyConfig: '/api/web/auth/verify-config',
|
||||
authSlideCaptcha: '/api/web/auth/slide-captcha',
|
||||
webConfigList: '/api/web/config/list',
|
||||
referralShareInfo: '/api/web/referral/share-info',
|
||||
referralSummary: '/api/web/referral/summary',
|
||||
referralInviteList: '/api/web/referral/invite-list',
|
||||
referralRebateList: '/api/web/referral/rebate-list',
|
||||
referralBindPreview: '/api/web/referral/bind-preview',
|
||||
referralBind: '/api/web/referral/bind',
|
||||
accountProfile: '/api/web/account/profile',
|
||||
accountProfileAvatar: '/api/web/account/profile/avatar',
|
||||
expertConfig: '/api/web/expert/config',
|
||||
expertApply: '/api/web/expert/apply',
|
||||
expertUploadIdCardFront: '/api/web/expert/upload-id-card-front',
|
||||
expertUploadIdCardBack: '/api/web/expert/upload-id-card-back',
|
||||
homeNav: '/api/web/menu/home-nav',
|
||||
menuChildren: (id) => `/api/web/menu/${encodeURIComponent(id)}/children`,
|
||||
payArticleExpertProfile: '/api/web/pay-article/expert-profile',
|
||||
payArticleExpertList: '/api/web/pay-article/expert-list',
|
||||
payArticlePublishOptions: (menuId) => `/api/web/pay-article/publish/options?menuId=${encodeURIComponent(menuId || '')}`,
|
||||
payArticlePublish: '/api/web/pay-article/publish',
|
||||
payArticleCollect: (id) => `/api/web/pay-article/article-collect/${encodeURIComponent(id)}`,
|
||||
expertCollect: (expertId, accountType = 1) =>
|
||||
`/api/web/pay-article/expert-collect?${new URLSearchParams({
|
||||
expertId: String(expertId || ''),
|
||||
accountType: String(accountType || 1),
|
||||
}).toString()}`,
|
||||
walletOverview: '/api/web/wallet/overview',
|
||||
walletFlowList: '/api/web/wallet/flow/list',
|
||||
walletRechargeConfigList: '/api/web/wallet/recharge/config/list',
|
||||
walletRechargeOrder: '/api/web/wallet/recharge/order',
|
||||
walletRechargeNotify: '/api/web/wallet/recharge/notify',
|
||||
walletTransferToAccount: '/api/web/wallet/transfer-to-account',
|
||||
walletWithdraw: '/api/web/wallet/withdraw',
|
||||
walletWithdrawList: '/api/web/wallet/withdraw/list',
|
||||
walletPrepurchasePayArticle: '/api/web/wallet/prepurchase/pay-article',
|
||||
walletReceiveAccountList: '/api/web/wallet/receive-account/list',
|
||||
walletReceiveAccount: '/api/web/wallet/receive-account',
|
||||
walletReceiveAccountUpdate: (id) => `/api/web/wallet/receive-account/${encodeURIComponent(id)}`,
|
||||
walletReceiveAccountDefault: (id) => `/api/web/wallet/receive-account/${encodeURIComponent(id)}/default`,
|
||||
walletReceiveAccountDelete: (id) => `/api/web/wallet/receive-account/${encodeURIComponent(id)}`,
|
||||
walletReceiveAccountUploadQrcode: '/api/web/wallet/receive-account/upload-qrcode',
|
||||
mineSummary: '/api/web/mine/summary',
|
||||
mineSignInList: '/api/web/mine/sign-in/list',
|
||||
walletPurchasePayArticle: '/api/web/wallet/purchase/pay-article',
|
||||
freeArticlePublish: '/api/web/free-article/publish',
|
||||
mineFreeArticleList: '/api/web/mine/free-article/list',
|
||||
minePayArticleList: '/api/web/mine/pay-article/list',
|
||||
minePayArticleDetail: (id) => `/api/web/mine/pay-article/${encodeURIComponent(id)}`,
|
||||
minePurchasedPayArticleList: '/api/web/mine/purchased-pay-article/list',
|
||||
freeArticleDetail: (id) => `/api/web/free-article/${encodeURIComponent(id)}`,
|
||||
freeArticleRelated: (id) => `/api/web/free-article/${encodeURIComponent(id)}/related`,
|
||||
freeArticleComments: (id) => `/api/web/free-article/${encodeURIComponent(id)}/comments`,
|
||||
freeArticleComment: (id) => `/api/web/free-article/${encodeURIComponent(id)}/comment`,
|
||||
freeArticleLike: (id) => `/api/web/free-article/${encodeURIComponent(id)}/like`,
|
||||
freeArticleCollect: (id) => `/api/web/free-article/${encodeURIComponent(id)}/collect`,
|
||||
fc3dFreePredictMenus: '/api/web/fc3d/free-predict/menus',
|
||||
fc3dFreePredictList: '/api/web/fc3d/free-predict/list',
|
||||
fc3dSchemeList: '/api/web/fc3d/scheme/list',
|
||||
fc3dSchemeDetail: (id) => `/api/web/fc3d/scheme/${encodeURIComponent(id)}`,
|
||||
walletPurchaseFc3dScheme: '/api/web/wallet/purchase/fc3d-scheme',
|
||||
},
|
||||
|
||||
get baseUrl() {
|
||||
return (window.CURRENT_CONFIG && window.CURRENT_CONFIG.API_BASE_URL) || '';
|
||||
},
|
||||
|
||||
get clientId() {
|
||||
return (
|
||||
(window.CURRENT_CONFIG && window.CURRENT_CONFIG.CLIENT_ID) ||
|
||||
'209ef407810e3856f40870a9f0e769d7'
|
||||
);
|
||||
},
|
||||
|
||||
get tenantId() {
|
||||
return (
|
||||
(window.CURRENT_CONFIG && window.CURRENT_CONFIG.TENANT_ID) ||
|
||||
this.TENANT_ID
|
||||
);
|
||||
},
|
||||
|
||||
request(options) {
|
||||
const normalized = this.normalizeLegacyRequest(options || {});
|
||||
if (normalized.compose) return normalized.compose();
|
||||
|
||||
const method = (normalized.method || 'GET').toUpperCase();
|
||||
const url = this.buildUrl(normalized.url, method === 'GET' ? normalized.data : null);
|
||||
const dedupeKey = this.requestDedupeKey(method, url, normalized);
|
||||
const cached = this.getDedupeCache(dedupeKey);
|
||||
if (cached) return cached;
|
||||
|
||||
const controller = new AbortController();
|
||||
const timeoutId = setTimeout(() => controller.abort(), normalized.timeout || 10000);
|
||||
|
||||
const baseHeaders = normalized.publicRequest ? this.publicHeaders() : this.defaultHeaders();
|
||||
const config = {
|
||||
method,
|
||||
headers: {
|
||||
...baseHeaders,
|
||||
...(normalized.headers || {}),
|
||||
},
|
||||
signal: controller.signal,
|
||||
};
|
||||
|
||||
if (normalized.data && ['POST', 'PUT', 'PATCH', 'DELETE'].includes(method)) {
|
||||
config.body =
|
||||
normalized.data instanceof FormData || typeof normalized.data === 'string'
|
||||
? normalized.data
|
||||
: JSON.stringify(normalized.data);
|
||||
if (normalized.data instanceof FormData) delete config.headers['Content-Type'];
|
||||
}
|
||||
|
||||
const promise = fetch(this.baseUrl + url, config)
|
||||
.then(async (response) => {
|
||||
clearTimeout(timeoutId);
|
||||
if (!response.ok) {
|
||||
if (response.status === 401) {
|
||||
localStorage.removeItem('token');
|
||||
window.location.href = 'login.html';
|
||||
}
|
||||
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
||||
}
|
||||
return this.normalizeResponse(await this.parseResponse(response), normalized);
|
||||
})
|
||||
.catch((error) => {
|
||||
clearTimeout(timeoutId);
|
||||
if (error.name === 'AbortError') throw new Error('Request timeout');
|
||||
throw error;
|
||||
});
|
||||
return this.setDedupeCache(dedupeKey, promise);
|
||||
},
|
||||
|
||||
requestDedupeKey(method, url, normalized = {}) {
|
||||
if (method !== 'GET' || normalized.dedupe === false) return '';
|
||||
const token = this.getToken() || '';
|
||||
return [method, this.baseUrl + url, token, this.clientId, this.tenantId].join(' ');
|
||||
},
|
||||
|
||||
getDedupeStore() {
|
||||
if (!this._dedupeStore) this._dedupeStore = new Map();
|
||||
return this._dedupeStore;
|
||||
},
|
||||
|
||||
getDedupeCache(key) {
|
||||
if (!key) return null;
|
||||
const store = this.getDedupeStore();
|
||||
const item = store.get(key);
|
||||
if (!item) return null;
|
||||
if (item.pending) return item.promise;
|
||||
if (Date.now() - item.time > 500) {
|
||||
store.delete(key);
|
||||
return null;
|
||||
}
|
||||
return item.promise;
|
||||
},
|
||||
|
||||
setDedupeCache(key, promise) {
|
||||
if (!key) return promise;
|
||||
const store = this.getDedupeStore();
|
||||
store.set(key, { promise, pending: true, time: Date.now() });
|
||||
promise
|
||||
.then(() => {
|
||||
const item = store.get(key);
|
||||
if (item?.promise === promise) {
|
||||
item.pending = false;
|
||||
item.time = Date.now();
|
||||
setTimeout(() => {
|
||||
if (store.get(key)?.promise === promise) store.delete(key);
|
||||
}, 600);
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
if (store.get(key)?.promise === promise) store.delete(key);
|
||||
});
|
||||
return promise;
|
||||
},
|
||||
|
||||
defaultHeaders() {
|
||||
const headers = {
|
||||
'Content-Type': 'application/json',
|
||||
tenantId: this.tenantId,
|
||||
};
|
||||
const token = this.getToken();
|
||||
if (token) {
|
||||
headers.Authorization = `Bearer ${token}`;
|
||||
headers.clientid = this.clientId;
|
||||
}
|
||||
return headers;
|
||||
},
|
||||
|
||||
publicHeaders() {
|
||||
return {
|
||||
'Content-Type': 'application/json',
|
||||
tenantId: this.tenantId,
|
||||
};
|
||||
},
|
||||
|
||||
isSuccess(res) {
|
||||
return !!res && (res.code === 0 || res.code === 200 || res.success === true);
|
||||
},
|
||||
|
||||
getToken() {
|
||||
const raw = localStorage.getItem('token');
|
||||
if (!raw) return '';
|
||||
try {
|
||||
const parsed = JSON.parse(raw);
|
||||
return parsed.access_token || parsed.accessToken || parsed.token || parsed;
|
||||
} catch (e) {
|
||||
return raw;
|
||||
}
|
||||
},
|
||||
|
||||
token() {
|
||||
return this.getToken();
|
||||
},
|
||||
|
||||
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 query = new URLSearchParams(clean).toString();
|
||||
if (!query) return url;
|
||||
return url + (url.indexOf('?') >= 0 ? '&' : '?') + query;
|
||||
},
|
||||
|
||||
async parseResponse(response) {
|
||||
const contentType = response.headers.get('content-type') || '';
|
||||
if (contentType.indexOf('application/json') >= 0) return response.json();
|
||||
return response.text();
|
||||
},
|
||||
|
||||
normalizeResponse(result, context = {}) {
|
||||
if (!result || typeof result !== 'object') return result;
|
||||
const normalized = { ...result };
|
||||
|
||||
if (normalized.code === 200) {
|
||||
normalized.rawCode = 200;
|
||||
normalized.code = 0;
|
||||
}
|
||||
if (typeof normalized.success === 'undefined') {
|
||||
normalized.success = normalized.code === 0 || normalized.code === 200;
|
||||
}
|
||||
if (Array.isArray(normalized.rows)) {
|
||||
normalized.data = {
|
||||
records: normalized.rows,
|
||||
rows: normalized.rows,
|
||||
totalRow: normalized.total || 0,
|
||||
total: normalized.total || 0,
|
||||
};
|
||||
}
|
||||
if (context.url === '/api/web/pay-article/expert-rank') {
|
||||
this.normalizeExpertRankResponse(normalized, context.data || {});
|
||||
}
|
||||
if (normalized.data && typeof normalized.data === 'object') {
|
||||
if (Object.prototype.hasOwnProperty.call(normalized.data, 'captchaEnabled')) {
|
||||
normalized.data.id = normalized.data.uuid || 'captcha-disabled';
|
||||
normalized.data.image = normalized.data.img || this.transparentGif();
|
||||
if (normalized.data.captchaEnabled === false) this.applyCaptchaDisabledState();
|
||||
}
|
||||
if (normalized.data.access_token && !normalized.data.token) {
|
||||
normalized.data.token = normalized.data.access_token;
|
||||
}
|
||||
}
|
||||
return normalized;
|
||||
},
|
||||
|
||||
normalizeExpertRankResponse(result, params = {}) {
|
||||
const data = result.data && typeof result.data === 'object' ? result.data : {};
|
||||
const source = Array.isArray(data.records)
|
||||
? data.records
|
||||
: Array.isArray(data.rows)
|
||||
? data.rows
|
||||
: Array.isArray(result.rows)
|
||||
? result.rows
|
||||
: [];
|
||||
const rows = source.map((item, index) => {
|
||||
const successRate =
|
||||
item.successRate === undefined || item.successRate === null ? '' : String(item.successRate);
|
||||
const hitRate = item.hitRate || (successRate ? `${successRate}%` : '');
|
||||
return {
|
||||
...item,
|
||||
rank: item.rank || index + 1,
|
||||
createBy: item.createBy || item.expertId,
|
||||
expertId: item.expertId || item.createBy,
|
||||
createUserName: item.createUserName || item.nickname || item.expertName,
|
||||
nickname: item.nickname || item.createUserName || item.expertName,
|
||||
issue: item.issue || item.latestIssue,
|
||||
title: item.title || item.latestTitle || item.latestRecommend,
|
||||
hitRate,
|
||||
};
|
||||
});
|
||||
|
||||
const shouldExpandForLegacyRank =
|
||||
params && !params.pageNum && Number(params.pageSize || 0) >= 50;
|
||||
const records = shouldExpandForLegacyRank
|
||||
? rows.flatMap((item) => {
|
||||
const total = Math.max(Number(item.totalCount) || 1, 1);
|
||||
const hit = Math.max(Number(item.correctCount) || 0, 0);
|
||||
return Array.from({ length: total }, (_, index) => ({
|
||||
...item,
|
||||
status: index < hit ? '1' : '0',
|
||||
}));
|
||||
})
|
||||
: rows;
|
||||
|
||||
result.data = {
|
||||
...data,
|
||||
records,
|
||||
rows: records,
|
||||
totalRow: data.totalRow || data.total || result.total || rows.length,
|
||||
total: data.total || data.totalRow || result.total || rows.length,
|
||||
};
|
||||
},
|
||||
|
||||
transparentGif() {
|
||||
return 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7';
|
||||
},
|
||||
|
||||
applyCaptchaDisabledState() {
|
||||
if (typeof window === 'undefined' || !window.document) return;
|
||||
setTimeout(() => {
|
||||
const input = document.getElementById('captchaInput');
|
||||
const hidden = document.getElementById('captchaId');
|
||||
const box = document.getElementById('captchaContainer');
|
||||
if (input) input.value = '0000';
|
||||
if (hidden) hidden.value = 'captcha-disabled';
|
||||
if (box) box.style.display = 'none';
|
||||
}, 0);
|
||||
},
|
||||
|
||||
normalizePageParams(data) {
|
||||
const params = { ...(data || {}) };
|
||||
if (params.page && !params.pageNum) params.pageNum = params.page;
|
||||
if (params.limit && !params.pageSize) params.pageSize = params.limit;
|
||||
if (params.pageNo && !params.pageNum) params.pageNum = params.pageNo;
|
||||
delete params.page;
|
||||
delete params.limit;
|
||||
delete params.pageNo;
|
||||
return params;
|
||||
},
|
||||
|
||||
pickData(result, fallback) {
|
||||
if (!result || typeof result !== 'object') return fallback;
|
||||
return result.data === undefined || result.data === null ? fallback : result.data;
|
||||
},
|
||||
|
||||
asArray(value) {
|
||||
if (Array.isArray(value)) return value;
|
||||
if (!value || typeof value !== 'object') return [];
|
||||
if (Array.isArray(value.records)) return value.records;
|
||||
if (Array.isArray(value.rows)) return value.rows;
|
||||
if (Array.isArray(value.list)) return value.list;
|
||||
return [];
|
||||
},
|
||||
|
||||
normalizeLegacyRequest(options) {
|
||||
const url = options.url || '';
|
||||
const method = (options.method || 'GET').toUpperCase();
|
||||
const data = options.data || {};
|
||||
|
||||
if (url === '/api/web/index') {
|
||||
return { compose: () => this.loadHomeIndexCompat() };
|
||||
}
|
||||
if (url === '/api/web/menu/getFormatList') {
|
||||
return { compose: () => this.loadMenuCompat() };
|
||||
}
|
||||
if (url === '/api/web/link/getList') {
|
||||
return { compose: () => this.loadLinksCompat() };
|
||||
}
|
||||
if (url === '/api/web/QRCode') {
|
||||
return { compose: () => Promise.resolve({ code: 0, success: true, data: location.origin + '/html/appdown.html' }) };
|
||||
}
|
||||
if (url === '/api/web/user/login') {
|
||||
return { compose: () => this.loginCompat(data) };
|
||||
}
|
||||
if (url.indexOf('/api/user/collect/list') === 0) {
|
||||
return {
|
||||
...options,
|
||||
url: '/api/web/mine/free-collect/list',
|
||||
method: 'GET',
|
||||
data: this.normalizePageParams({
|
||||
...data,
|
||||
page: this.queryValue(url, 'page') || data.page,
|
||||
pageSize: this.queryValue(url, 'pageSize') || data.pageSize,
|
||||
}),
|
||||
};
|
||||
}
|
||||
if (url === '/api/web/lotteryResult/getList') {
|
||||
return { compose: () => this.requestWithMenu('/api/web/lottery/result/list', data) };
|
||||
}
|
||||
if (url === '/api/web/freeArticle/getPageList') {
|
||||
return { compose: () => this.requestWithMenu('/api/web/free-article/list', data) };
|
||||
}
|
||||
if (url === '/api/web/payArticle/getPageList') {
|
||||
return { compose: () => this.requestWithMenu('/api/web/pay-article/expert-rank', data) };
|
||||
}
|
||||
if (url === '/api/web/payArticle/getListByCreateUser') {
|
||||
return {
|
||||
compose: () => this.loadLegacyExpertDetailCompat(data),
|
||||
};
|
||||
}
|
||||
|
||||
const direct = this.mapSimpleLegacyUrl(url, data);
|
||||
if (direct) return { ...options, ...direct };
|
||||
|
||||
if (url.indexOf('/api/web/agreement/getByType') === 0) {
|
||||
const type = this.queryValue(url, 'type') || data.type || '1';
|
||||
return { ...options, url: `/api/web/agreement/${type}`, method: 'GET', data: null };
|
||||
}
|
||||
if (url.indexOf('/api/web/notice/getDetail') === 0) {
|
||||
const id = this.queryValue(url, 'id') || data.id;
|
||||
return { ...options, url: `/api/web/notice/${id}`, method: 'GET', data: null };
|
||||
}
|
||||
if (url.indexOf('/api/web/freeArticle/getDetail') === 0) {
|
||||
const id = this.queryValue(url, 'id') || data.id;
|
||||
return { ...options, url: `/api/web/free-article/${id}`, method: 'GET', data: null };
|
||||
}
|
||||
|
||||
return { ...options, method, data };
|
||||
},
|
||||
|
||||
mapSimpleLegacyUrl(url, data) {
|
||||
const pageParams = this.normalizePageParams(data);
|
||||
const map = {
|
||||
'/api/web/user/register': { url: '/api/web/auth/register', method: 'POST', data: this.normalizeAuthPayload(data) },
|
||||
'/api/web/user/getCaptcha': { url: '/api/web/auth/code', method: 'GET', data: null },
|
||||
'/api/web/user/logout': { url: '/api/web/auth/logout', method: 'POST', data: null },
|
||||
'/api/web/user/getCurrentUserDetail': { url: '/api/web/mine/summary', method: 'GET', data: null },
|
||||
'/api/web/user/getUserBalance': { url: '/api/web/wallet/overview', method: 'GET', data: null },
|
||||
'/api/web/user/signIn': { url: '/api/web/mine/sign-in', method: 'POST', data: null },
|
||||
'/api/web/user/signIn/getPageList': { url: '/api/web/mine/sign-in/list', method: 'GET', data: pageParams },
|
||||
'/api/web/capitalFlow/getPageList': { url: '/api/web/wallet/flow/list', method: 'GET', data: pageParams },
|
||||
'/api/web/payment/getConfigList': { url: '/api/web/wallet/recharge/config/list', method: 'GET', data: null },
|
||||
'/api/web/payment/charge/native': { url: '/api/web/wallet/recharge/order', method: 'POST', data },
|
||||
'/api/web/freeArticle/getPageListByUser': { url: '/api/web/mine/free-article/list', method: 'GET', data: pageParams },
|
||||
'/api/web/freeArticle/push': { url: '/api/web/free-article/publish', method: 'POST', data },
|
||||
'/api/web/payArticle/push': {
|
||||
url: '/api/web/pay-article/publish',
|
||||
method: 'POST',
|
||||
data: {
|
||||
parentId: data.parentId,
|
||||
code: data.code,
|
||||
title: data.title,
|
||||
predictedCode: data.predictedCode || data.predictCode || data.content,
|
||||
issue: data.issue || data.expect,
|
||||
},
|
||||
},
|
||||
'/api/web/payArticle/subscribe': { url: '/api/web/wallet/purchase/pay-article', method: 'POST', data },
|
||||
'/api/web/order/getPageListByUser': { url: '/api/web/mine/purchased-pay-article/list', method: 'GET', data: pageParams },
|
||||
'/api/web/rebate/getAffInfo': { url: '/api/web/referral/share-info', method: 'GET', data: null },
|
||||
'/api/web/rebate/getPageList': { url: '/api/web/referral/rebate-list', method: 'GET', data: pageParams },
|
||||
'/api/web/rebate/getWithdrawList': { url: '/api/web/wallet/withdraw/list', method: 'GET', data: pageParams },
|
||||
'/api/web/rebate/withdraw': { url: '/api/web/wallet/withdraw', method: 'POST', data },
|
||||
'/api/web/rebate/rebateToBalance': { url: '/api/web/wallet/transfer-to-account', method: 'POST', data },
|
||||
'/api/web/distribution/getPageList': { url: '/api/web/referral/invite-list', method: 'GET', data: pageParams },
|
||||
'/api/web/user/applyDistribution': { url: '/api/web/referral/bind', method: 'POST', data },
|
||||
'/api/web/user/applyExpert': { url: '/api/web/expert/apply', method: 'POST', data },
|
||||
'/api/web/user/expert/apply': { url: '/api/web/expert/apply', method: 'POST', data },
|
||||
'/api/web/user/updateInfo': { url: '/api/web/account/profile', method: 'PUT', data },
|
||||
'/api/web/notice/getPageList': { url: '/api/web/notice/list', method: 'GET', data: pageParams },
|
||||
'/api/web/fc3d/freePredict/list': { url: '/api/web/fc3d/free-predict/list', method: 'GET', data: pageParams },
|
||||
'/api/web/fc3d/scheme/list': { url: '/api/web/fc3d/scheme/list', method: 'GET', data: pageParams },
|
||||
'/api/web/fc3d/scheme/buy': { url: '/api/web/wallet/purchase/fc3d-scheme', method: 'POST', data },
|
||||
};
|
||||
|
||||
if (map[url]) return map[url];
|
||||
|
||||
const pathMap = [
|
||||
[/^\/api\/web\/freeArticle\/like\/(.+)$/, (id) => ({ url: `/api/web/free-article/${id}/like`, method: 'POST', data: null })],
|
||||
[/^\/api\/web\/freeArticle\/collect\/(.+)$/, (id) => ({ url: `/api/web/free-article/${id}/collect`, method: 'POST', data: null })],
|
||||
[/^\/api\/web\/freeArticleComment\/getPageList$/, () => ({ url: `/api/web/free-article/${data.articleId}/comments`, method: 'GET', data: pageParams })],
|
||||
[/^\/api\/web\/freeArticleComment\/push$/, () => ({ url: `/api/web/free-article/${data.articleId}/comment`, method: 'POST', data })],
|
||||
[/^\/api\/web\/payArticle\/collect\/(.+)$/, (id) => ({ url: `/api/web/pay-article/article-collect/${id}`, method: 'POST', data: null })],
|
||||
[/^\/api\/web\/expert\/collect\/(.+)$/, (id) => ({
|
||||
url: `/api/web/pay-article/expert-collect?${new URLSearchParams({
|
||||
expertId: id,
|
||||
accountType: data.accountType || 1,
|
||||
}).toString()}`,
|
||||
method: 'POST',
|
||||
data: null,
|
||||
})],
|
||||
];
|
||||
|
||||
for (const [pattern, build] of pathMap) {
|
||||
const match = url.match(pattern);
|
||||
if (match) return build(match[1]);
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
normalizeAuthPayload(data) {
|
||||
const payload = { ...(data || {}) };
|
||||
if (!payload.grantType) payload.grantType = 'password';
|
||||
if (!payload.deviceType) payload.deviceType = 'WEB';
|
||||
if (payload.captcha && !payload.code) payload.code = payload.captcha;
|
||||
if (payload.captchaId && payload.captchaId !== 'captcha-disabled' && !payload.uuid) {
|
||||
payload.uuid = payload.captchaId;
|
||||
}
|
||||
if (payload.captchaId === 'captcha-disabled') {
|
||||
payload.verifyType = payload.verifyType || 'none';
|
||||
delete payload.uuid;
|
||||
delete payload.captchaId;
|
||||
delete payload.captcha;
|
||||
delete payload.code;
|
||||
} else if (!payload.verifyType) {
|
||||
payload.verifyType = payload.code ? 'captcha' : 'none';
|
||||
}
|
||||
return payload;
|
||||
},
|
||||
|
||||
async loginCompat(data) {
|
||||
const payload = this.normalizeAuthPayload(data);
|
||||
const verifyConfig = await this.get('/api/web/auth/verify-config').catch(() => ({ data: null }));
|
||||
const verifyType = verifyConfig.data && verifyConfig.data.verifyType;
|
||||
if (verifyType === 'slide' || verifyConfig.data?.slideCaptchaEnabled) {
|
||||
const slide = await this.get('/api/web/auth/slide-captcha').catch(() => ({ data: null }));
|
||||
if (slide.data && slide.data.uuid) {
|
||||
payload.verifyType = 'slide';
|
||||
payload.slideUuid = slide.data.uuid;
|
||||
payload.slideX = slide.data.targetX || slide.data.x || 0;
|
||||
delete payload.code;
|
||||
delete payload.uuid;
|
||||
delete payload.captcha;
|
||||
delete payload.captchaId;
|
||||
}
|
||||
}
|
||||
return this.post('/api/web/auth/login', payload);
|
||||
},
|
||||
|
||||
queryValue(url, key) {
|
||||
const query = url.split('?')[1];
|
||||
if (!query) return '';
|
||||
return new URLSearchParams(query).get(key) || '';
|
||||
},
|
||||
|
||||
fetchHomeNav() {
|
||||
if (!this._homeNavPromise) {
|
||||
this._homeNavPromise = this.get('/api/web/menu/home-nav').catch((error) => {
|
||||
this._homeNavPromise = null;
|
||||
throw error;
|
||||
});
|
||||
}
|
||||
return this._homeNavPromise;
|
||||
},
|
||||
|
||||
normalizeMenuList(navData, options = {}) {
|
||||
return (navData || [])
|
||||
.filter((item) => item.type === 'lottery')
|
||||
.map((item) => ({
|
||||
...item,
|
||||
suoxie: item.suoxie || item.code,
|
||||
url: item.url || item.path,
|
||||
children: options.keepChildren && Array.isArray(item.children) ? item.children : [],
|
||||
}));
|
||||
},
|
||||
|
||||
shouldLoadMenuChildren() {
|
||||
if (typeof window === 'undefined') return true;
|
||||
return /\/(?:cai|fufei|duanzuhe|shazuhe)\.html(?:$|\?)/.test(
|
||||
window.location.pathname + window.location.search
|
||||
);
|
||||
},
|
||||
|
||||
async loadMenuCompat() {
|
||||
if (!this._menuCompatPromise) {
|
||||
this._menuCompatPromise = (async () => {
|
||||
const nav = await this.fetchHomeNav();
|
||||
const list = this.normalizeMenuList(nav.data, { keepChildren: true });
|
||||
if (!this.shouldLoadMenuChildren()) {
|
||||
return { code: 0, success: true, data: list };
|
||||
}
|
||||
|
||||
const missingChildren = list.filter((item) => item.id && !item.children.length);
|
||||
|
||||
await Promise.all(
|
||||
missingChildren.map((item) =>
|
||||
this.get(`/api/web/menu/${item.id}/children`)
|
||||
.then((res) => {
|
||||
item.children = res.data || [];
|
||||
})
|
||||
.catch(() => {
|
||||
item.children = [];
|
||||
})
|
||||
)
|
||||
);
|
||||
return { code: 0, success: true, data: list };
|
||||
})().catch((error) => {
|
||||
this._menuCompatPromise = null;
|
||||
throw error;
|
||||
});
|
||||
}
|
||||
return this._menuCompatPromise;
|
||||
},
|
||||
|
||||
async loadHomeMenusCompat() {
|
||||
const nav = await this.fetchHomeNav();
|
||||
const list = this.normalizeMenuList(nav.data);
|
||||
return { code: 0, success: true, data: list };
|
||||
},
|
||||
|
||||
async requestWithMenu(url, data) {
|
||||
const params = this.normalizePageParams(data || {});
|
||||
if (url === '/api/web/pay-article/expert-rank' && !params.issueCount && params.pageSize) {
|
||||
params.issueCount = params.pageSize;
|
||||
if (params.pageNum) params.pageSize = params.rowPageSize || 10;
|
||||
}
|
||||
params.menuId =
|
||||
params.menuId ||
|
||||
params.parentId ||
|
||||
params.typeId ||
|
||||
(await this.resolveMenuId(params.code || params.suoxie));
|
||||
delete params.parentId;
|
||||
delete params.typeId;
|
||||
delete params.code;
|
||||
delete params.suoxie;
|
||||
return this.get(url, params);
|
||||
},
|
||||
|
||||
async loadLegacyExpertDetailCompat(data = {}) {
|
||||
const cacheKey = JSON.stringify(this.normalizePageParams(data));
|
||||
if (!this._legacyExpertDetailPromises) this._legacyExpertDetailPromises = {};
|
||||
if (this._legacyExpertDetailPromises[cacheKey]) {
|
||||
return this._legacyExpertDetailPromises[cacheKey];
|
||||
}
|
||||
|
||||
this._legacyExpertDetailPromises[cacheKey] = this.fetchLegacyExpertDetailCompat(data).catch(
|
||||
(error) => {
|
||||
delete this._legacyExpertDetailPromises[cacheKey];
|
||||
throw error;
|
||||
}
|
||||
);
|
||||
return this._legacyExpertDetailPromises[cacheKey];
|
||||
},
|
||||
|
||||
async fetchLegacyExpertDetailCompat(data = {}) {
|
||||
const params = this.normalizePageParams(data);
|
||||
const expertId = params.expertId || params.createBy || '';
|
||||
const accountType = params.accountType || 1;
|
||||
const menuId =
|
||||
params.menuId ||
|
||||
params.parentId ||
|
||||
params.typeId ||
|
||||
(await this.resolveMenuId(params.code || params.suoxie));
|
||||
const listParams = {
|
||||
...params,
|
||||
expertId,
|
||||
accountType,
|
||||
menuId,
|
||||
pageNum: params.pageNum || 1,
|
||||
pageSize: params.pageSize || 50,
|
||||
};
|
||||
delete listParams.createBy;
|
||||
delete listParams.parentId;
|
||||
delete listParams.typeId;
|
||||
delete listParams.code;
|
||||
delete listParams.suoxie;
|
||||
|
||||
const [profileRes, listRes] = await Promise.all([
|
||||
expertId
|
||||
? this.get('/api/web/pay-article/expert-profile', { expertId, accountType }).catch((error) => ({
|
||||
code: 500,
|
||||
msg: error.message || '获取专家资料失败',
|
||||
data: {},
|
||||
}))
|
||||
: Promise.resolve({ code: 0, data: {} }),
|
||||
this.get('/api/web/pay-article/expert-list', listParams),
|
||||
]);
|
||||
|
||||
if (listRes.code !== 0) return listRes;
|
||||
const profile = this.pickData(profileRes, {}) || {};
|
||||
const listData = this.pickData(listRes, {}) || {};
|
||||
const list = this.asArray(listData).map((item) => ({
|
||||
...item,
|
||||
id: item.id || item.articleId || item.payArticleId,
|
||||
issue: item.issue || item.latestIssue || item.expect || item.period,
|
||||
title: item.title || item.latestTitle,
|
||||
predictedCode:
|
||||
item.predictedCode ||
|
||||
item.recommendCode ||
|
||||
item.latestRecommend ||
|
||||
item.recommend ||
|
||||
item.content,
|
||||
openCode: item.openCode || item.lotteryResult || item.resultCode || item.openResult,
|
||||
accountType: item.accountType || accountType,
|
||||
}));
|
||||
|
||||
return {
|
||||
...listRes,
|
||||
data: {
|
||||
...listData,
|
||||
...profile,
|
||||
expertId,
|
||||
createBy: expertId,
|
||||
menuId,
|
||||
parentId: menuId,
|
||||
accountType,
|
||||
avatar: profile.avatar || profile.headImg,
|
||||
nickname: profile.nickname || profile.nickName || profile.expertName,
|
||||
isCollect: profile.isCollect ?? profile.collected ?? profile.expertCollected,
|
||||
payArticleList: list,
|
||||
records: list,
|
||||
rows: list,
|
||||
totalRow: listData.totalRow || listData.total || listRes.total || list.length,
|
||||
total: listData.total || listData.totalRow || listRes.total || list.length,
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
async resolveMenuId(code) {
|
||||
if (!code) return '';
|
||||
if (!this._menuIdCache) {
|
||||
const nav = await this.fetchHomeNav().catch(() => ({ data: [] }));
|
||||
this._menuIdCache = {};
|
||||
(nav.data || []).forEach((item) => {
|
||||
if (item.code && item.id) this._menuIdCache[item.code] = item.id;
|
||||
});
|
||||
}
|
||||
return this._menuIdCache[code] || '';
|
||||
},
|
||||
|
||||
async loadLinksCompat() {
|
||||
const [friend, other] = await Promise.all([
|
||||
this.get('/api/web/link/friend').catch(() => ({ data: [] })),
|
||||
this.get('/api/web/link/other-recommend').catch(() => ({ data: [] })),
|
||||
]);
|
||||
const friendList = (friend.data || []).map((item) => ({ ...item, type: 1 }));
|
||||
const otherList = (other.data || []).map((item) => ({ ...item, type: 2 }));
|
||||
return { code: 0, success: true, data: friendList.concat(otherList) };
|
||||
},
|
||||
|
||||
async loadHomeIndexCompat() {
|
||||
const [configList, config, siteConfig, menus, banners, notices, ads, freeArticles, links, latest] = await Promise.all([
|
||||
this.get('/api/web/config/list').catch(() => ({ data: [] })),
|
||||
this.get('/api/web/config').catch(() => ({ data: {} })),
|
||||
this.get('/api/web/config/site').catch(() => ({ data: {} })),
|
||||
this.loadHomeMenusCompat().catch(() => ({ data: [] })),
|
||||
this.get('/api/web/banner/list').catch(() => ({ data: [] })),
|
||||
this.get('/api/web/notice/list', { count: 6 }).catch(() => ({ data: [] })),
|
||||
this.get('/api/web/ad/list', { inTime: true }).catch(() => ({ data: [] })),
|
||||
this.get('/api/web/free-article/recommend', { articleCount: 5 }).catch(() => ({ data: [] })),
|
||||
this.loadLinksCompat().catch(() => ({ data: [] })),
|
||||
this.get('/api/web/lottery/result/latest').catch(() => ({ data: [] })),
|
||||
]);
|
||||
|
||||
const latestMap = {};
|
||||
const lotteryList = this.asArray(this.pickData(latest, []));
|
||||
lotteryList.forEach((item) => {
|
||||
latestMap[item.code] = {
|
||||
...item,
|
||||
expect: item.expect,
|
||||
name: item.expect,
|
||||
time: item.time,
|
||||
openCode: item.openCode,
|
||||
code: item.openCode,
|
||||
red: item.redNumbers,
|
||||
blue: item.blueNumbers,
|
||||
};
|
||||
});
|
||||
|
||||
const articleGroups = this.asArray(this.pickData(freeArticles, []));
|
||||
const articleMap = {};
|
||||
articleGroups.forEach((group) => {
|
||||
if (Array.isArray(group.articles)) {
|
||||
articleMap[group.code] = group.articles.map((article) => ({
|
||||
...article,
|
||||
code: article.code || group.code,
|
||||
lotteryName: article.lotteryName || group.lotteryName,
|
||||
accountAvatar: article.accountAvatar || article.expertAvatar || article.avatar,
|
||||
createUserName: article.createUserName || article.author || article.nickName || article.issuer,
|
||||
}));
|
||||
return;
|
||||
}
|
||||
const code = group.code || group.suoxie;
|
||||
if (!code) return;
|
||||
if (!articleMap[code]) articleMap[code] = [];
|
||||
articleMap[code].push({
|
||||
...group,
|
||||
accountAvatar: group.accountAvatar || group.expertAvatar || group.avatar,
|
||||
createUserName: group.createUserName || group.author || group.nickName || group.issuer,
|
||||
});
|
||||
});
|
||||
|
||||
const menuList = this.asArray(this.pickData(menus, [])).map((menu) => ({
|
||||
...menu,
|
||||
suoxie: menu.suoxie || menu.code,
|
||||
url: menu.url || menu.path,
|
||||
lotteryResult: latestMap[menu.suoxie || menu.code],
|
||||
freeArticleList: articleMap[menu.suoxie || menu.code] || [],
|
||||
}));
|
||||
|
||||
return {
|
||||
code: 0,
|
||||
success: true,
|
||||
data: {
|
||||
webConfigs: this.asArray(this.pickData(configList, [])).length
|
||||
? this.asArray(this.pickData(configList, []))
|
||||
: this.configObjectToList(config.data || {}),
|
||||
siteConfig: siteConfig.data || {},
|
||||
menuList,
|
||||
navList: menuList,
|
||||
lotteryList,
|
||||
freeArticleData: articleGroups,
|
||||
bannerList: this.asArray(this.pickData(banners, [])),
|
||||
noticeList: this.asArray(this.pickData(notices, [])),
|
||||
adList: this.asArray(this.pickData(ads, [])),
|
||||
linkList: this.asArray(this.pickData(links, [])),
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
configObjectToList(config) {
|
||||
return Object.keys(config || {}).map((key, index) => ({
|
||||
type: key,
|
||||
content: config[key],
|
||||
sort: index,
|
||||
}));
|
||||
},
|
||||
|
||||
get(url, params, options = {}) {
|
||||
return this.request({ url, method: 'GET', data: params || null, ...options });
|
||||
},
|
||||
|
||||
post(url, data, options = {}) {
|
||||
return this.request({ url, method: 'POST', data: data || null, ...options });
|
||||
},
|
||||
|
||||
put(url, data, options = {}) {
|
||||
return this.request({ url, method: 'PUT', data: data || null, ...options });
|
||||
},
|
||||
|
||||
delete(url, data, options = {}) {
|
||||
return this.request({ url, method: 'DELETE', data: data || null, ...options });
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,145 @@
|
||||
const AuthPageUtil = {
|
||||
smsTimers: {},
|
||||
slideUuid: '',
|
||||
slideX: 0,
|
||||
|
||||
isPhone(phone) {
|
||||
return /^1[3-9]\d{9}$/.test(String(phone || '').trim());
|
||||
},
|
||||
|
||||
isPassword(password) {
|
||||
const value = String(password || '');
|
||||
return value.length >= 6 && value.length <= 20;
|
||||
},
|
||||
|
||||
loadVerifyType() {
|
||||
return ApiClient.get(ApiClient.API.authVerifyConfig, {}, { publicRequest: true })
|
||||
.then((res) => {
|
||||
const data = (res && res.data) || {};
|
||||
const yes = (value) =>
|
||||
value === true || value === 1 || value === '1' || String(value).toLowerCase() === 'true';
|
||||
if (
|
||||
yes(data.lottery_slide_captcha) ||
|
||||
yes(data.lotterySlideCaptcha) ||
|
||||
yes(data.slideCaptchaEnabled) ||
|
||||
yes(data.slideEnabled) ||
|
||||
data.verifyType === 'slide'
|
||||
) {
|
||||
return 'slide';
|
||||
}
|
||||
return 'captcha';
|
||||
})
|
||||
.catch(() => 'captcha');
|
||||
},
|
||||
|
||||
sendSmsCode(options) {
|
||||
const params = {
|
||||
phoneNumber: options.phoneNumber,
|
||||
};
|
||||
if (options.verifyType === 'slide') {
|
||||
params.verifyType = 'slide';
|
||||
params.slideUuid = options.slideUuid || this.slideUuid;
|
||||
params.slideX = options.slideX || this.slideX;
|
||||
}
|
||||
if (options.verifyType === 'captcha') {
|
||||
params.verifyType = 'captcha';
|
||||
params.code = options.code || '';
|
||||
params.uuid = options.uuid || '';
|
||||
}
|
||||
return ApiClient.get(ApiClient.API.authSmsCode, params, { publicRequest: true });
|
||||
},
|
||||
|
||||
installSlideCaptchaTenantHeader() {
|
||||
if (typeof window === 'undefined' || window.__cxzSlideCaptchaTenantPatched) return;
|
||||
window.__cxzSlideCaptchaTenantPatched = true;
|
||||
|
||||
const isSlideCaptchaUrl = (url) => String(url || '').indexOf('/api/web/auth/slide-captcha') >= 0;
|
||||
const tenantId = () =>
|
||||
typeof ApiClient !== 'undefined' && ApiClient.tenantId ? ApiClient.tenantId : '000000';
|
||||
|
||||
if (typeof window.fetch === 'function') {
|
||||
const nativeFetch = window.fetch.bind(window);
|
||||
window.fetch = function (input, init = {}) {
|
||||
const requestUrl = typeof input === 'string' ? input : input && input.url;
|
||||
if (!isSlideCaptchaUrl(requestUrl)) return nativeFetch(input, init);
|
||||
|
||||
const isRequestInput = typeof Request !== 'undefined' && input instanceof Request;
|
||||
const headers = new Headers(init.headers || (isRequestInput ? input.headers : undefined));
|
||||
headers.set('tenantId', tenantId());
|
||||
|
||||
if (isRequestInput) {
|
||||
return nativeFetch(new Request(input, { ...init, headers }));
|
||||
}
|
||||
|
||||
return nativeFetch(input, { ...init, headers });
|
||||
};
|
||||
}
|
||||
|
||||
if (window.XMLHttpRequest && window.XMLHttpRequest.prototype) {
|
||||
const xhrProto = window.XMLHttpRequest.prototype;
|
||||
const nativeOpen = xhrProto.open;
|
||||
const nativeSetRequestHeader = xhrProto.setRequestHeader;
|
||||
const nativeSend = xhrProto.send;
|
||||
|
||||
xhrProto.open = function (method, url, ...args) {
|
||||
this.__cxzSlideCaptchaRequest = isSlideCaptchaUrl(url);
|
||||
this.__cxzSlideTenantHeaderSet = false;
|
||||
return nativeOpen.call(this, method, url, ...args);
|
||||
};
|
||||
|
||||
xhrProto.setRequestHeader = function (name, value) {
|
||||
if (this.__cxzSlideCaptchaRequest && String(name || '').toLowerCase() === 'tenantid') {
|
||||
this.__cxzSlideTenantHeaderSet = true;
|
||||
}
|
||||
return nativeSetRequestHeader.call(this, name, value);
|
||||
};
|
||||
|
||||
xhrProto.send = function (...args) {
|
||||
if (this.__cxzSlideCaptchaRequest && !this.__cxzSlideTenantHeaderSet) {
|
||||
nativeSetRequestHeader.call(this, 'tenantId', tenantId());
|
||||
this.__cxzSlideTenantHeaderSet = true;
|
||||
}
|
||||
return nativeSend.apply(this, args);
|
||||
};
|
||||
}
|
||||
},
|
||||
|
||||
startSmsCountdown(buttonSelector, seconds = 60) {
|
||||
const $button = layui.$(buttonSelector);
|
||||
let left = seconds;
|
||||
clearInterval(this.smsTimers[buttonSelector]);
|
||||
$button.prop('disabled', true).text(`${left}s后重发`);
|
||||
this.smsTimers[buttonSelector] = setInterval(() => {
|
||||
left -= 1;
|
||||
if (left <= 0) {
|
||||
clearInterval(this.smsTimers[buttonSelector]);
|
||||
$button.prop('disabled', false).text('获取验证码');
|
||||
return;
|
||||
}
|
||||
$button.text(`${left}s后重发`);
|
||||
}, 1000);
|
||||
},
|
||||
|
||||
loadSlideCaptchaScript() {
|
||||
this.installSlideCaptchaTenantHeader();
|
||||
if (window.SlideCaptcha) return Promise.resolve(window.SlideCaptcha);
|
||||
if (window.__slideCaptchaLoading) return window.__slideCaptchaLoading;
|
||||
window.__slideCaptchaLoading = new Promise((resolve, reject) => {
|
||||
const script = document.createElement('script');
|
||||
script.src = `${ApiClient.baseUrl}/static/slide-captcha/slide-captcha.js`;
|
||||
script.dataset.apiBase = ApiClient.baseUrl || '';
|
||||
script.dataset.tenantId = ApiClient.tenantId || '000000';
|
||||
script.onload = () => {
|
||||
if (window.SlideCaptcha) resolve(window.SlideCaptcha);
|
||||
else reject(new Error('滑动验证组件加载失败'));
|
||||
};
|
||||
script.onerror = () => reject(new Error('滑动验证组件加载失败'));
|
||||
document.body.appendChild(script);
|
||||
});
|
||||
return window.__slideCaptchaLoading;
|
||||
},
|
||||
};
|
||||
|
||||
if (typeof window !== 'undefined') {
|
||||
window.AuthPageUtil = AuthPageUtil;
|
||||
}
|
||||
+1265
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,114 @@
|
||||
/**
|
||||
* 日期时间工具类
|
||||
*/
|
||||
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;
|
||||
|
||||
if (isNaN(date.getTime())) {
|
||||
console.error('Invalid date:', dateStr);
|
||||
return '';
|
||||
}
|
||||
|
||||
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 '';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将日期字符串转换为"年月日"格式,例如:"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;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 导出模块,兼容不同引入方式
|
||||
if (typeof module !== 'undefined' && module.exports) {
|
||||
module.exports = DateUtil;
|
||||
} else if (typeof window !== 'undefined') {
|
||||
window.DateUtil = DateUtil;
|
||||
}
|
||||
Reference in New Issue
Block a user