diff --git a/src/giffgaff/js/giffgaff-app.js b/src/giffgaff/js/giffgaff-app.js index e2bb4cd..191b08a 100644 --- a/src/giffgaff/js/giffgaff-app.js +++ b/src/giffgaff/js/giffgaff-app.js @@ -143,7 +143,7 @@ class GiffgaffApp { } isIgnoredUnhandledRejection(error) { - const payload = `${error?.message ?? ''}\n${error?.stack ?? ''}`.toLowerCase(); + const payload = `${(error && error.message) || ''}\n${(error && error.stack) || ''}`.toLowerCase(); const ignoredKeywords = [ 'wallet must has at least one account', 'wallet must have at least one account', @@ -516,7 +516,8 @@ class GiffgaffApp { if (!confirmBtn.__bound) { confirmBtn.__bound = true; confirmBtn.addEventListener('click', () => { - const selected = container.querySelector('input[name="directFetchSsn"]:checked')?.value; + const selectedEl = container.querySelector('input[name="directFetchSsn"]:checked'); + const selected = selectedEl && selectedEl.value; if (!selected) return; container.style.display = 'none'; this.handleDirectFetchPull(selected); @@ -743,7 +744,7 @@ class GiffgaffApp { console.log('[Giffgaff] 调用 oauthHandler.processCallback()...'); const result = await oauthHandler.processCallback(callbackUrl); - console.log('[Giffgaff] OAuth 回调处理成功, hasAccessToken:', !!result?.accessToken); + console.log('[Giffgaff] OAuth 回调处理成功, hasAccessToken:', !!(result && result.accessToken)); uiController.showStatus(elements.callbackStatus, t('giffgaff.app.status.oauthSuccess'), "success"); @@ -927,7 +928,7 @@ class GiffgaffApp { console.log('[Giffgaff] 调用 esimService.getMemberInfo()...'); const result = await esimService.getMemberInfo(); - console.log('[Giffgaff] 会员信息获取成功, memberId:', result.data?.memberProfile?.id); + console.log('[Giffgaff] 会员信息获取成功, memberId:', result.data && result.data.memberProfile && result.data.memberProfile.id); uiController.showStatus(elements.memberStatus, t('giffgaff.app.status.memberFetched'), "success"); uiController.showMemberInfo(result.data); @@ -939,8 +940,8 @@ class GiffgaffApp { console.error('[Giffgaff] 获取会员信息失败:', error.message, error); // 针对"账号无号码"错误提供专门提示 - const isWalletEmpty = error.message?.includes('wallet must') || - error.message?.includes('at least one account') || + const isWalletEmpty = (error.message && error.message.includes('wallet must')) || + (error.message && error.message.includes('at least one account')) || error.code === 4001; if (isWalletEmpty) { @@ -998,7 +999,7 @@ class GiffgaffApp { console.log('[Giffgaff] 调用 esimService.reserveESim()...'); const result = await esimService.reserveESim(); - console.log('[Giffgaff] eSIM 预订成功, deliveryStatus:', result.data?.esim?.deliveryStatus); + console.log('[Giffgaff] eSIM 预订成功, deliveryStatus:', result.data && result.data.esim && result.data.esim.deliveryStatus); const status = result.data.esim.deliveryStatus || 'RESERVED'; uiController.showStatus(elements.esimReserveStatus, t('giffgaff.app.status.reserveSuccess', { status }), "success"); @@ -1054,7 +1055,7 @@ class GiffgaffApp { const statusEl = document.getElementById('smsInlineStatus'); console.log('[Giffgaff] === SMS 验证码校验 ==='); - const code = (codeInput?.value ?? '').trim(); + const code = (codeInput && codeInput.value || '').trim(); if (!/^\d{6}$/.test(code)) { console.warn('[Giffgaff] 验证码格式无效:', code.length, '位'); @@ -1106,8 +1107,8 @@ class GiffgaffApp { const activationInput = document.getElementById('manualActivationCode'); const ssnInput = document.getElementById('manualSSN'); - const activationCode = (activationInput?.value ?? '').trim(); - const ssn = (ssnInput?.value ?? '').trim(); + const activationCode = (activationInput && activationInput.value || '').trim(); + const ssn = (ssnInput && ssnInput.value || '').trim(); console.log('[Giffgaff] === 手动 eSIM 信息保存 ==='); console.log('[Giffgaff] 激活码长度:', activationCode.length, ', SSN长度:', ssn.length); @@ -1197,7 +1198,7 @@ class GiffgaffApp { console.error('[Giffgaff] eSIM Token 获取失败:', error.message, error); // 根据错误类型进行差异化处理 - if (error.status === 401 || error.message?.includes('401')) { + if (error.status === 401 || (error.message && error.message.includes('401'))) { uiController.showStatus(elements.tokenStatus, t('giffgaff.app.error.authExpired'), "error"); setTimeout(() => { if (confirm(t('giffgaff.app.prompt.relogin'))) { @@ -1205,7 +1206,7 @@ class GiffgaffApp { uiController.showSection(1); } }, 500); - } else if (error.status === 422 || error.message?.includes('422')) { + } else if (error.status === 422 || (error.message && error.message.includes('422'))) { uiController.showStatus(elements.tokenStatus, t('giffgaff.app.error.ssnNotFound'), "error"); setTimeout(() => uiController.showSection(4), 2000); } else { @@ -1241,7 +1242,7 @@ class GiffgaffApp { * 处理Cookie过期 */ handleCookieExpired(event) { - console.log('[Giffgaff] === Cookie 已过期 ===', event?.detail); + console.log('[Giffgaff] === Cookie 已过期 ===', event && event.detail); uiController.showSection(1); uiController.selectLoginMethod('cookie'); diff --git a/src/giffgaff/js/modules/cookie-handler.js b/src/giffgaff/js/modules/cookie-handler.js index 662c19e..0e91223 100644 --- a/src/giffgaff/js/modules/cookie-handler.js +++ b/src/giffgaff/js/modules/cookie-handler.js @@ -105,7 +105,7 @@ export class CookieHandler { return { valid: false }; } catch (err) { console.error(t('giffgaff.cookie.log.checkError'), err); - return { transientError: true, error: err?.message }; + return { transientError: true, error: err && err.message }; } } diff --git a/src/giffgaff/js/modules/esim-service.js b/src/giffgaff/js/modules/esim-service.js index c61061a..5bb161c 100644 --- a/src/giffgaff/js/modules/esim-service.js +++ b/src/giffgaff/js/modules/esim-service.js @@ -52,7 +52,7 @@ export class ESimService { if (data.errors) { const errorObj = data.errors[0]; - const errorMessage = errorObj?.message || errorObj?.error || JSON.stringify(errorObj); + const errorMessage = (errorObj && errorObj.message) || (errorObj && errorObj.error) || JSON.stringify(errorObj); throw new Error(errorMessage); } @@ -111,7 +111,7 @@ export class ESimService { if (data.errors) { const errorObj = data.errors[0]; - const errorMessage = errorObj?.message || errorObj?.error || JSON.stringify(errorObj); + const errorMessage = (errorObj && errorObj.message) || (errorObj && errorObj.error) || JSON.stringify(errorObj); throw new Error(errorMessage); } @@ -189,7 +189,7 @@ export class ESimService { throw new Error(errorMessage); } - const newSim = swapData?.data?.swapSim?.new; + const newSim = swapData && swapData.data && swapData.data.swapSim && swapData.data.swapSim.new; if (!newSim || !newSim.ssn) { throw new Error(t('giffgaff.esim.errors.swapMissingSim')); } @@ -245,7 +245,7 @@ export class ESimService { if (data.errors) { const errorObj = data.errors[0]; - const errorMessage = errorObj?.message || errorObj?.error || JSON.stringify(errorObj); + const errorMessage = (errorObj && errorObj.message) || (errorObj && errorObj.error) || JSON.stringify(errorObj); throw new Error(errorMessage); } @@ -429,10 +429,10 @@ export class ESimService { const data = await response.json(); if (data.errors) { const errorObj = data.errors[0]; - throw new Error(errorObj?.message || errorObj?.error || JSON.stringify(errorObj)); + throw new Error((errorObj && errorObj.message) || (errorObj && errorObj.error) || JSON.stringify(errorObj)); } - return data?.data?.eSims || []; + return (data && data.data && data.data.eSims) || []; } /** @@ -485,7 +485,7 @@ export class ESimService { // 兼容 GraphQL 错误格式 { errors: [{ message: '...' }] } if (parsed && typeof parsed === 'object' && Array.isArray(parsed.errors) && parsed.errors.length > 0) { const firstError = parsed.errors[0] || {}; - const message = firstError?.message || (typeof firstError === 'string' ? firstError : null); + const message = (firstError && firstError.message) || (typeof firstError === 'string' ? firstError : null); if (message) { return { ...parsed, message }; } @@ -503,7 +503,7 @@ export class ESimService { * @returns {string} 友好的错误提示 */ _friendlyGraphqlError(status, parsed) { - const msg = parsed?.message || parsed?.error || ''; + const msg = (parsed && parsed.message) || (parsed && parsed.error) || ''; // 401: token 过期 if (status === 401) { diff --git a/src/js/modules/qrcode-generator.js b/src/js/modules/qrcode-generator.js index d9e1d1d..7305586 100644 --- a/src/js/modules/qrcode-generator.js +++ b/src/js/modules/qrcode-generator.js @@ -3,9 +3,12 @@ import { tl } from './i18n.js'; // CDN 多源备用列表(主源 + 备用源,任一成功即可,解决 ESIM-TOOLS-15 单一 CDN 故障问题) +// 包含国际源 + 中国镜像源,覆盖全球用户(包括中国大陆) const QRCODE_CDN_URLS = [ 'https://cdn.jsdelivr.net/npm/qrcode@1.5.4/build/qrcode.min.js', - 'https://cdnjs.cloudflare.com/ajax/libs/qrcode-generator/1.4.4/qrcode.min.js' + 'https://cdnjs.cloudflare.com/ajax/libs/qrcode-generator/1.4.4/qrcode.min.js', + 'https://unpkg.zhimg.com/qrcode@1.5.4/build/qrcode.min.js', + 'https://cdn.bootcdn.net/ajax/libs/qrcodejs/1.0.0/qrcode.min.js' ]; const DEFAULT_QR_SIZE = 300; const MIN_QR_SIZE = 200; diff --git a/src/simyo/js/modules/auth-handler.js b/src/simyo/js/modules/auth-handler.js index 10bb715..5416cef 100644 --- a/src/simyo/js/modules/auth-handler.js +++ b/src/simyo/js/modules/auth-handler.js @@ -12,7 +12,7 @@ export class AuthHandler { constructor() { this.apiEndpoints = getApiEndpoints(); } - + /** * 登录Simyo账户 * @param {string} phoneNumber - 手机号 @@ -24,11 +24,11 @@ export class AuthHandler { if (!validatePhoneNumber(phoneNumber)) { throw new Error(t('simyo.errors.invalidPhone')); } - + if (!password) { throw new Error(t('simyo.errors.requirePassword')); } - + try { const response = await fetch(this.apiEndpoints.login, { method: 'POST', @@ -38,23 +38,23 @@ export class AuthHandler { password: password }) }); - + const data = await handleApiResponse(response); - + // 提取session token - const sessionToken = data.result?.sessionToken; - + const sessionToken = data.result && data.result.sessionToken; + if (!sessionToken) { throw new Error(data.message || t('simyo.auth.loginMissingToken')); } - + // 保存到状态 stateManager.setState({ sessionToken: sessionToken, phoneNumber: phoneNumber, password: password }); - + return { success: true, message: t('simyo.auth.loginSuccess'), @@ -65,7 +65,7 @@ export class AuthHandler { throw error; } } - + /** * 检查登录状态 * @returns {boolean} 是否已登录 @@ -73,7 +73,7 @@ export class AuthHandler { isLoggedIn() { return !!stateManager.get('sessionToken'); } - + /** * 获取当前session token * @returns {string} session token diff --git a/src/simyo/js/modules/device-change-handler.js b/src/simyo/js/modules/device-change-handler.js index 8bd38c3..c76ae8e 100644 --- a/src/simyo/js/modules/device-change-handler.js +++ b/src/simyo/js/modules/device-change-handler.js @@ -95,7 +95,7 @@ export class DeviceChangeHandler { return { success: true, message: data.message || t('simyo.device.verifySuccess'), - remainingTries: data.result?.remainingNumberOfTries + remainingTries: data.result && data.result.remainingNumberOfTries }; } catch (error) { console.error(t('simyo.device.log.verifyFailed'), error); diff --git a/src/simyo/js/modules/utils.js b/src/simyo/js/modules/utils.js index f3161d9..9de28d4 100644 --- a/src/simyo/js/modules/utils.js +++ b/src/simyo/js/modules/utils.js @@ -104,7 +104,8 @@ export function openHelp() { align-items: center; justify-content: center; padding: 20px; `; overlay.innerHTML = helpContent; - overlay.querySelector('[data-action="close-help"]')?.addEventListener('click', () => { + const closeBtn = overlay.querySelector('[data-action="close-help"]'); + if (closeBtn) closeBtn.addEventListener('click', () => { overlay.remove(); }); overlay.addEventListener('click', (e) => { diff --git a/src/simyo/js/simyo-app.js b/src/simyo/js/simyo-app.js index 806ef52..3074fe1 100644 --- a/src/simyo/js/simyo-app.js +++ b/src/simyo/js/simyo-app.js @@ -77,32 +77,32 @@ class SimyoApp { const { elements } = uiController; // ===== Step 1: 登录 ===== - elements.loginBtn?.addEventListener('click', () => this.handleLogin()); + if (elements.loginBtn) elements.loginBtn.addEventListener('click', () => this.handleLogin()); // ===== 设备更换流程 ===== this.bindDeviceChangeFlow(); // ===== Step 2: 获取eSIM ===== - elements.getEsimBtn?.addEventListener('click', () => this.handleGetEsim()); + if (elements.getEsimBtn) elements.getEsimBtn.addEventListener('click', () => this.handleGetEsim()); // ===== Step 3: 生成二维码 ===== - elements.generateQrBtn?.addEventListener('click', () => this.handleGenerateQR()); + if (elements.generateQrBtn) elements.generateQrBtn.addEventListener('click', () => this.handleGenerateQR()); // 下一步:确认安装 const nextToConfirmBtn = document.getElementById('nextToConfirmBtn'); - nextToConfirmBtn?.addEventListener('click', () => { + if (nextToConfirmBtn) nextToConfirmBtn.addEventListener('click', () => { uiController.showSection(4); }); // ===== Step 4: 确认安装 ===== - elements.confirmInstallBtn?.addEventListener('click', () => this.handleConfirmInstall()); + if (elements.confirmInstallBtn) elements.confirmInstallBtn.addEventListener('click', () => this.handleConfirmInstall()); // ===== 通用操作 ===== - elements.clearSessionBtn?.addEventListener('click', () => this.handleClearSession()); + if (elements.clearSessionBtn) elements.clearSessionBtn.addEventListener('click', () => this.handleClearSession()); // 帮助按钮 const helpBtn = document.getElementById('helpBtn'); - helpBtn?.addEventListener('click', () => openHelp()); + if (helpBtn) helpBtn.addEventListener('click', () => openHelp()); // 步骤点击跳转 this.bindStepNavigation(); @@ -423,7 +423,7 @@ class SimyoApp { console.log('[Simyo] 步骤2: 生成 LPA 字符串...'); const qrResult = esimService.generateLPAString(); - console.log('[Simyo] LPA 字符串生成成功, 长度:', qrResult.lpaString?.length); + console.log('[Simyo] LPA 字符串生成成功, 长度:', qrResult.lpaString && qrResult.lpaString.length); uiController.showQRResult(qrResult.lpaString); uiController.showStatus(elements.qrStatus, t('simyo.app.status.generateSuccess'), "success");