// 每日国际汇率 JavaScript 功能 // API配置 const API = { endpoints: [], currentIndex: 0, defaultCurrency: 'CNY', localFallback: '返回接口.json', // 初始化API接口列表 async init() { try { const res = await fetch('./接口集合.json'); const endpoints = await res.json(); this.endpoints = endpoints.map(endpoint => `${endpoint}/v2/exchange_rate`); } catch (e) { // 如果无法加载接口集合,使用默认接口 this.endpoints = ['https://60s.viki.moe/v2/exchange_rate']; } }, // 获取当前接口URL getCurrentUrl(currency) { if (this.endpoints.length === 0) return null; const url = new URL(this.endpoints[this.currentIndex]); url.searchParams.append('currency', currency); return url.toString(); }, // 切换到下一个接口 switchToNext() { this.currentIndex = (this.currentIndex + 1) % this.endpoints.length; return this.currentIndex < this.endpoints.length; }, // 重置到第一个接口 reset() { this.currentIndex = 0; } }; // 常用货币列表 const POPULAR_CURRENCIES = [ { code: 'CNY', name: '人民币', flag: '🇨🇳' }, { code: 'USD', name: '美元', flag: '🇺🇸' }, { code: 'EUR', name: '欧元', flag: '🇪🇺' }, { code: 'JPY', name: '日元', flag: '🇯🇵' }, { code: 'GBP', name: '英镑', flag: '🇬🇧' }, { code: 'AUD', name: '澳元', flag: '🇦🇺' }, { code: 'CAD', name: '加元', flag: '🇨🇦' }, { code: 'CHF', name: '瑞士法郎', flag: '🇨🇭' }, { code: 'HKD', name: '港币', flag: '🇭🇰' }, { code: 'SGD', name: '新加坡元', flag: '🇸🇬' }, { code: 'KRW', name: '韩元', flag: '🇰🇷' }, { code: 'THB', name: '泰铢', flag: '🇹🇭' } ]; // 货币优先级排序 - 经济发达、交易频繁的国家货币优先 const CURRENCY_PRIORITY = { // 第一梯队:全球主要储备货币和交易货币 'USD': 1, // 美元 - 全球储备货币 'EUR': 2, // 欧元 - 欧盟统一货币 'JPY': 3, // 日元 - 亚洲主要货币 'GBP': 4, // 英镑 - 传统储备货币 'CNY': 5, // 人民币 - 中国货币 // 第二梯队:发达国家货币 'CHF': 10, // 瑞士法郎 - 避险货币 'CAD': 11, // 加拿大元 'AUD': 12, // 澳大利亚元 'NZD': 13, // 新西兰元 'SEK': 14, // 瑞典克朗 'NOK': 15, // 挪威克朗 'DKK': 16, // 丹麦克朗 // 第三梯队:亚洲发达经济体 'HKD': 20, // 港币 'SGD': 21, // 新加坡元 'KRW': 22, // 韩元 'TWD': 23, // 新台币 // 第四梯队:重要新兴市场货币 'RUB': 30, // 俄罗斯卢布 'INR': 31, // 印度卢比 'BRL': 32, // 巴西雷亚尔 'MXN': 33, // 墨西哥比索 'ZAR': 34, // 南非兰特 'TRY': 35, // 土耳其里拉 // 第五梯队:亚洲重要货币 'THB': 40, // 泰铢 'MYR': 41, // 马来西亚林吉特 'IDR': 42, // 印尼盾 'PHP': 43, // 菲律宾比索 'VND': 44, // 越南盾 // 第六梯队:中东石油国家货币 'SAR': 50, // 沙特里亚尔 'AED': 51, // 阿联酋迪拉姆 'QAR': 52, // 卡塔尔里亚尔 'KWD': 53, // 科威特第纳尔 // 第七梯队:欧洲其他货币 'PLN': 60, // 波兰兹罗提 'CZK': 61, // 捷克克朗 'HUF': 62, // 匈牙利福林 'RON': 63, // 罗马尼亚列伊 'BGN': 64, // 保加利亚列弗 'HRK': 65, // 克罗地亚库纳 // 第八梯队:拉美货币 'ARS': 70, // 阿根廷比索 'CLP': 71, // 智利比索 'COP': 72, // 哥伦比亚比索 'PEN': 73, // 秘鲁索尔 'UYU': 74, // 乌拉圭比索 // 其他货币默认优先级为 999 }; // 货币名称映射 const CURRENCY_NAMES = { 'CNY': '人民币', 'USD': '美元', 'EUR': '欧元', 'JPY': '日元', 'GBP': '英镑', 'AUD': '澳元', 'CAD': '加元', 'CHF': '瑞士法郎', 'HKD': '港币', 'SGD': '新加坡元', 'KRW': '韩元', 'THB': '泰铢', 'AED': '阿联酋迪拉姆', 'AFN': '阿富汗尼', 'ALL': '阿尔巴尼亚列克', 'AMD': '亚美尼亚德拉姆', 'ANG': '荷属安的列斯盾', 'AOA': '安哥拉宽扎', 'ARS': '阿根廷比索', 'AWG': '阿鲁巴弗罗林', 'AZN': '阿塞拜疆马纳特', 'BAM': '波黑马克', 'BBD': '巴巴多斯元', 'BDT': '孟加拉塔卡', 'BGN': '保加利亚列弗', 'BHD': '巴林第纳尔', 'BIF': '布隆迪法郎', 'BMD': '百慕大元', 'BND': '文莱元', 'BOB': '玻利维亚诺', 'BRL': '巴西雷亚尔', 'BSD': '巴哈马元', 'BTN': '不丹努尔特鲁姆', 'BWP': '博茨瓦纳普拉', 'BYN': '白俄罗斯卢布', 'BZD': '伯利兹元', 'CDF': '刚果法郎', 'CLP': '智利比索', 'COP': '哥伦比亚比索', 'CRC': '哥斯达黎加科朗', 'CUP': '古巴比索', 'CVE': '佛得角埃斯库多', 'CZK': '捷克克朗', 'DJF': '吉布提法郎', 'DKK': '丹麦克朗', 'DOP': '多米尼加比索', 'DZD': '阿尔及利亚第纳尔', 'EGP': '埃及镑', 'ERN': '厄立特里亚纳克法', 'ETB': '埃塞俄比亚比尔', 'FJD': '斐济元', 'FKP': '福克兰群岛镑', 'FOK': '法罗群岛克朗', 'GEL': '格鲁吉亚拉里', 'GGP': '根西岛镑', 'GHS': '加纳塞地', 'GIP': '直布罗陀镑', 'GMD': '冈比亚达拉西', 'GNF': '几内亚法郎', 'GTQ': '危地马拉格查尔', 'GYD': '圭亚那元', 'HNL': '洪都拉斯伦皮拉', 'HRK': '克罗地亚库纳', 'HTG': '海地古德', 'HUF': '匈牙利福林', 'IDR': '印尼盾', 'ILS': '以色列新谢克尔', 'IMP': '马恩岛镑', 'INR': '印度卢比', 'IQD': '伊拉克第纳尔', 'IRR': '伊朗里亚尔', 'ISK': '冰岛克朗', 'JEP': '泽西岛镑', 'JMD': '牙买加元', 'JOD': '约旦第纳尔', 'KES': '肯尼亚先令', 'KGS': '吉尔吉斯斯坦索姆', 'KHR': '柬埔寨瑞尔', 'KID': '基里巴斯元', 'KMF': '科摩罗法郎', 'KWD': '科威特第纳尔', 'KYD': '开曼群岛元', 'KZT': '哈萨克斯坦坚戈', 'LAK': '老挝基普', 'LBP': '黎巴嫩镑', 'LKR': '斯里兰卡卢比', 'LRD': '利比里亚元', 'LSL': '莱索托洛蒂', 'LYD': '利比亚第纳尔', 'MAD': '摩洛哥迪拉姆', 'MDL': '摩尔多瓦列伊', 'MGA': '马达加斯加阿里亚里', 'MKD': '北马其顿第纳尔', 'MMK': '缅甸缅元', 'MNT': '蒙古图格里克', 'MOP': '澳门帕塔卡', 'MRU': '毛里塔尼亚乌吉亚', 'MUR': '毛里求斯卢比', 'MVR': '马尔代夫拉菲亚', 'MWK': '马拉维克瓦查', 'MXN': '墨西哥比索', 'MYR': '马来西亚林吉特', 'MZN': '莫桑比克梅蒂卡尔', 'NAD': '纳米比亚元', 'NGN': '尼日利亚奈拉', 'NIO': '尼加拉瓜科多巴', 'NOK': '挪威克朗', 'NPR': '尼泊尔卢比', 'NZD': '新西兰元', 'OMR': '阿曼里亚尔', 'PAB': '巴拿马巴波亚', 'PEN': '秘鲁索尔', 'PGK': '巴布亚新几内亚基那', 'PHP': '菲律宾比索', 'PKR': '巴基斯坦卢比', 'PLN': '波兰兹罗提', 'PYG': '巴拉圭瓜拉尼', 'QAR': '卡塔尔里亚尔', 'RON': '罗马尼亚列伊', 'RSD': '塞尔维亚第纳尔', 'RUB': '俄罗斯卢布', 'RWF': '卢旺达法郎', 'SAR': '沙特里亚尔', 'SBD': '所罗门群岛元', 'SCR': '塞舌尔卢比', 'SDG': '苏丹镑', 'SEK': '瑞典克朗', 'SHP': '圣赫勒拿镑', 'SLE': '塞拉利昂利昂', 'SLL': '塞拉利昂利昂(旧)', 'SOS': '索马里先令', 'SRD': '苏里南元', 'SSP': '南苏丹镑', 'STN': '圣多美和普林西比多布拉', 'SYP': '叙利亚镑', 'SZL': '斯威士兰里兰吉尼', 'TJS': '塔吉克斯坦索莫尼', 'TMT': '土库曼斯坦马纳特', 'TND': '突尼斯第纳尔', 'TOP': '汤加潘加', 'TRY': '土耳其里拉', 'TTD': '特立尼达和多巴哥元', 'TVD': '图瓦卢元', 'TWD': '新台币', 'TZS': '坦桑尼亚先令', 'UAH': '乌克兰格里夫纳', 'UGX': '乌干达先令', 'UYU': '乌拉圭比索', 'UZS': '乌兹别克斯坦苏姆', 'VES': '委内瑞拉玻利瓦尔', 'VND': '越南盾', 'VUV': '瓦努阿图瓦图', 'WST': '萨摩亚塔拉', 'XAF': '中非法郎', 'XCD': '东加勒比元', 'XCG': '加勒比盾', 'XDR': '特别提款权', 'XOF': '西非法郎', 'XPF': '太平洋法郎', 'YER': '也门里亚尔', 'ZAR': '南非兰特', 'ZMW': '赞比亚克瓦查', 'ZWL': '津巴布韦元' }; // 货币旗帜映射 const CURRENCY_FLAGS = { 'CNY': '🇨🇳', 'USD': '🇺🇸', 'EUR': '🇪🇺', 'JPY': '🇯🇵', 'GBP': '🇬🇧', 'AUD': '🇦🇺', 'CAD': '🇨🇦', 'CHF': '🇨🇭', 'HKD': '🇭🇰', 'SGD': '🇸🇬', 'KRW': '🇰🇷', 'THB': '🇹🇭', 'AED': '🇦🇪', 'AFN': '🇦🇫', 'ALL': '🇦🇱', 'AMD': '🇦🇲', 'ANG': '🇳🇱', 'AOA': '🇦🇴', 'ARS': '🇦🇷', 'AWG': '🇦🇼', 'AZN': '🇦🇿', 'BAM': '🇧🇦', 'BBD': '🇧🇧', 'BDT': '🇧🇩', 'BGN': '🇧🇬', 'BHD': '🇧🇭', 'BIF': '🇧🇮', 'BMD': '🇧🇲', 'BND': '🇧🇳', 'BOB': '🇧🇴', 'BRL': '🇧🇷', 'BSD': '🇧🇸', 'BTN': '🇧🇹', 'BWP': '🇧🇼', 'BYN': '🇧🇾', 'BZD': '🇧🇿', 'CDF': '🇨🇩', 'CLP': '🇨🇱', 'COP': '🇨🇴', 'CRC': '🇨🇷', 'CUP': '🇨🇺', 'CVE': '🇨🇻', 'CZK': '🇨🇿', 'DJF': '🇩🇯', 'DKK': '🇩🇰', 'DOP': '🇩🇴', 'DZD': '🇩🇿', 'EGP': '🇪🇬', 'ERN': '🇪🇷', 'ETB': '🇪🇹', 'FJD': '🇫🇯', 'FKP': '🇫🇰', 'FOK': '🇫🇴', 'GEL': '🇬🇪', 'GGP': '🇬🇬', 'GHS': '🇬🇭', 'GIP': '🇬🇮', 'GMD': '🇬🇲', 'GNF': '🇬🇳', 'GTQ': '🇬🇹', 'GYD': '🇬🇾', 'HNL': '🇭🇳', 'HRK': '🇭🇷', 'HTG': '🇭🇹', 'HUF': '🇭🇺', 'IDR': '🇮🇩', 'ILS': '🇮🇱', 'IMP': '🇮🇲', 'INR': '🇮🇳', 'IQD': '🇮🇶', 'IRR': '🇮🇷', 'ISK': '🇮🇸', 'JEP': '🇯🇪', 'JMD': '🇯🇲', 'JOD': '🇯🇴', 'KES': '🇰🇪', 'KGS': '🇰🇬', 'KHR': '🇰🇭', 'KID': '🇰🇮', 'KMF': '🇰🇲', 'KWD': '🇰🇼', 'KYD': '🇰🇾', 'KZT': '🇰🇿', 'LAK': '🇱🇦', 'LBP': '🇱🇧', 'LKR': '🇱🇰', 'LRD': '🇱🇷', 'LSL': '🇱🇸', 'LYD': '🇱🇾', 'MAD': '🇲🇦', 'MDL': '🇲🇩', 'MGA': '🇲🇬', 'MKD': '🇲🇰', 'MMK': '🇲🇲', 'MNT': '🇲🇳', 'MOP': '🇲🇴', 'MRU': '🇲🇷', 'MUR': '🇲🇺', 'MVR': '🇲🇻', 'MWK': '🇲🇼', 'MXN': '🇲🇽', 'MYR': '🇲🇾', 'MZN': '🇲🇿', 'NAD': '🇳🇦', 'NGN': '🇳🇬', 'NIO': '🇳🇮', 'NOK': '🇳🇴', 'NPR': '🇳🇵', 'NZD': '🇳🇿', 'OMR': '🇴🇲', 'PAB': '🇵🇦', 'PEN': '🇵🇪', 'PGK': '🇵🇬', 'PHP': '🇵🇭', 'PKR': '🇵🇰', 'PLN': '🇵🇱', 'PYG': '🇵🇾', 'QAR': '🇶🇦', 'RON': '🇷🇴', 'RSD': '🇷🇸', 'RUB': '🇷🇺', 'RWF': '🇷🇼', 'SAR': '🇸🇦', 'SBD': '🇸🇧', 'SCR': '🇸🇨', 'SDG': '🇸🇩', 'SEK': '🇸🇪', 'SHP': '🇸🇭', 'SLE': '🇸🇱', 'SLL': '🇸🇱', 'SOS': '🇸🇴', 'SRD': '🇸🇷', 'SSP': '🇸🇸', 'STN': '🇸🇹', 'SYP': '🇸🇾', 'SZL': '🇸🇿', 'TJS': '🇹🇯', 'TMT': '🇹🇲', 'TND': '🇹🇳', 'TOP': '🇹🇴', 'TRY': '🇹🇷', 'TTD': '🇹🇹', 'TVD': '🇹🇻', 'TWD': '🇹🇼', 'TZS': '🇹🇿', 'UAH': '🇺🇦', 'UGX': '🇺🇬', 'UYU': '🇺🇾', 'UZS': '🇺🇿', 'VES': '🇻🇪', 'VND': '🇻🇳', 'VUV': '🇻🇺', 'WST': '🇼🇸', 'XAF': '🌍', 'XCD': '🏝️', 'XCG': '🏝️', 'XDR': '🌐', 'XOF': '🌍', 'XPF': '🌊', 'YER': '🇾🇪', 'ZAR': '🇿🇦', 'ZMW': '🇿🇲', 'ZWL': '🇿🇼' }; // DOM元素 let elements = {}; let currentRates = []; let filteredRates = []; // 初始化 document.addEventListener('DOMContentLoaded', function() { initElements(); initCurrencySelector(); bindEvents(); loadExchangeRates(); }); // 初始化DOM元素 function initElements() { elements = { currencySelect: document.getElementById('currency-select'), searchInput: document.getElementById('search-input'), loading: document.getElementById('loading'), content: document.getElementById('exchange-content'), baseCurrency: document.getElementById('base-currency'), updateTime: document.getElementById('update-time'), ratesGrid: document.getElementById('rates-grid'), totalCurrencies: document.getElementById('total-currencies'), lastUpdate: document.getElementById('last-update') }; } // 初始化货币选择器 function initCurrencySelector() { if (!elements.currencySelect) return; // 添加常用货币选项 POPULAR_CURRENCIES.forEach(currency => { const option = document.createElement('option'); option.value = currency.code; option.textContent = `${currency.flag} ${currency.code} - ${currency.name}`; if (currency.code === API.defaultCurrency) { option.selected = true; } elements.currencySelect.appendChild(option); }); } // 绑定事件 function bindEvents() { // 货币选择变化 if (elements.currencySelect) { elements.currencySelect.addEventListener('change', function() { loadExchangeRates(this.value); }); } // 搜索功能 if (elements.searchInput) { elements.searchInput.addEventListener('input', function() { filterRates(this.value); }); } } // 加载汇率数据 async function loadExchangeRates(currency = API.defaultCurrency) { try { showLoading(true); // 尝试从API获取数据 const data = await fetchFromAPI(currency); if (data && data.code === 200 && data.data) { currentRates = data.data.rates || []; displayExchangeRates(data.data); } else { // 尝试从本地获取数据 const localData = await fetchFromLocal(); if (localData && localData.code === 200 && localData.data) { currentRates = localData.data.rates || []; displayExchangeRates(localData.data); showError('使用本地数据,可能不是最新汇率'); } else { throw new Error('无法获取汇率数据'); } } } catch (error) { console.error('加载汇率失败:', error); showError('加载汇率数据失败,请稍后重试'); } finally { showLoading(false); } } // 从API获取数据 async function fetchFromAPI(currency) { // 初始化API接口列表 await API.init(); // 重置API索引到第一个接口 API.reset(); // 尝试所有API接口 for (let i = 0; i < API.endpoints.length; i++) { try { const url = API.getCurrentUrl(currency); console.log(`尝试接口 ${i + 1}/${API.endpoints.length}: ${url}`); const response = await fetch(url, { cache: 'no-store' }); if (!response.ok) { throw new Error(`HTTP ${response.status}: ${response.statusText}`); } const data = await response.json(); if (data && data.code === 200) { console.log(`接口 ${i + 1} 请求成功`); return data; } throw new Error(data && data.message ? data.message : '接口返回异常'); } catch (e) { console.warn(`接口 ${i + 1} 失败:`, e.message); // 如果不是最后一个接口,切换到下一个 if (i < API.endpoints.length - 1) { API.switchToNext(); continue; } // 所有接口都失败了 console.warn('所有远程接口都失败,尝试本地数据'); return null; } } } // 从本地获取数据 async function fetchFromLocal() { try { const response = await fetch(API.localFallback + `?t=${Date.now()}`); if (!response.ok) throw new Error(`本地文件HTTP ${response.status}`); const data = await response.json(); return data; } catch (e) { console.error('读取本地返回接口.json失败:', e); return null; } } // 显示汇率数据 function displayExchangeRates(data) { if (!data || !data.rates) { showError('没有获取到汇率数据'); return; } // 更新基础货币信息 if (elements.baseCurrency) { const baseCurrencyName = CURRENCY_NAMES[data.base_code] || data.base_code; const baseCurrencyFlag = CURRENCY_FLAGS[data.base_code] || '💱'; elements.baseCurrency.textContent = `${baseCurrencyFlag} ${data.base_code} - ${baseCurrencyName}`; } // 更新时间信息 if (elements.updateTime && data.updated) { elements.updateTime.textContent = `更新时间: ${data.updated}`; } // 更新统计信息 updateStats(data); // 显示汇率列表 filteredRates = data.rates; renderRates(filteredRates); // 显示内容区域 if (elements.content) { elements.content.classList.add('fade-in'); elements.content.style.display = 'block'; } } // 更新统计信息 function updateStats(data) { if (elements.totalCurrencies) { elements.totalCurrencies.textContent = data.rates ? data.rates.length : 0; } if (elements.lastUpdate && data.updated) { elements.lastUpdate.textContent = data.updated; } } // 渲染汇率列表 function renderRates(rates) { if (!elements.ratesGrid || !rates) return; // 按优先级排序货币 const sortedRates = [...rates].sort((a, b) => { const priorityA = CURRENCY_PRIORITY[a.currency] || 999; const priorityB = CURRENCY_PRIORITY[b.currency] || 999; // 优先级相同时按货币代码字母顺序排序 if (priorityA === priorityB) { return a.currency.localeCompare(b.currency); } return priorityA - priorityB; }); elements.ratesGrid.innerHTML = ''; sortedRates.forEach(rate => { const rateCard = createRateCard(rate); elements.ratesGrid.appendChild(rateCard); }); } // 创建汇率卡片 function createRateCard(rate) { const card = document.createElement('div'); card.className = 'rate-card'; const currencyName = CURRENCY_NAMES[rate.currency] || rate.currency; const currencyFlag = CURRENCY_FLAGS[rate.currency] || '💱'; card.innerHTML = `
${currencyFlag} ${rate.currency}
${formatRate(rate.rate)}
${currencyName}
`; return card; } // 格式化汇率 function formatRate(rate) { if (rate >= 1) { return rate.toFixed(4); } else if (rate >= 0.01) { return rate.toFixed(6); } else { return rate.toFixed(8); } } // 过滤汇率数据 function filterRates(searchTerm) { if (!searchTerm.trim()) { renderRates(currentRates); return; } const filtered = currentRates.filter(rate => { const currencyName = CURRENCY_NAMES[rate.currency] || rate.currency; return rate.currency.toLowerCase().includes(searchTerm.toLowerCase()) || currencyName.toLowerCase().includes(searchTerm.toLowerCase()); }); renderRates(filtered); } // 显示/隐藏加载状态 function showLoading(show) { if (elements.loading) { elements.loading.style.display = show ? 'block' : 'none'; } if (elements.content) { elements.content.style.display = show ? 'none' : 'block'; } } // 显示错误信息 function showError(message) { if (elements.content) { elements.content.innerHTML = `

⚠️ 加载失败

${escapeHtml(message)}

请检查网络连接或稍后重试

`; elements.content.style.display = 'block'; } } // HTML转义 function escapeHtml(text) { if (!text) return ''; const div = document.createElement('div'); div.textContent = text; return div.innerHTML; } // 错误处理 window.addEventListener('error', function(event) { console.error('页面错误:', event.error); }); // 网络状态监听 window.addEventListener('online', function() { console.log('网络已连接'); }); window.addEventListener('offline', function() { console.log('网络已断开'); showError('网络连接已断开,请检查网络设置'); }); // 导出函数供外部调用 window.ExchangeRate = { loadExchangeRates, showError, showLoading };