🐛 fix: 兼容 Chrome 77 等旧浏览器,移除 ES2020 语法(可选链/空值合并)

- ESIM-TOOLS-G: 替换所有 ?. 和 ?? 为 ES5 兼容写法
- ESIM-TOOLS-15: 添加中国 CDN 源(unpkg.zhimg.com/bootcdn.net)
- 涉及 8 个文件,覆盖 giffgaff/simyo 全部业务模块
This commit is contained in:
Abner
2026-06-23 16:35:05 +08:00
parent 04b77f59b8
commit 9c2a5bb18d
8 changed files with 49 additions and 44 deletions

View File

@@ -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');

View File

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

View File

@@ -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) {

View File

@@ -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;

View File

@@ -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

View File

@@ -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);

View File

@@ -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) => {

View File

@@ -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");