// API配置 const API_URL = 'api.php?action=fund_data'; const STATS_URL = 'api.php?action=get_stats'; const FUND_CHART_API = (code) => `api.php?action=get_fund_chart&fund_code=${code}`; // 全局数据存储 let fundData = null; let visitStats = null; // 自动刷新相关变量 let autoRefreshInterval = null; const AUTO_REFRESH_INTERVAL = 10000; // 10秒自动刷新一次 // 取消控制器,避免并发请求 let fundController = null; const cardCharts = new Map(); // 页面加载完成后初始化 document.addEventListener('DOMContentLoaded', function() { // 应用已保存的主题(优先使用用户选择,其次匹配系统偏好) applySavedTheme(); // 添加动画样式 addAnimationStyles(); loadFundData(); loadVisitStats(); // 启动自动刷新 startAutoRefresh(); document.getElementById('refreshBtn').addEventListener('click', function() { loadFundData(); loadVisitStats(); }); document.getElementById('themeBtn').addEventListener('click', toggleTheme); document.getElementById('statsBtn').addEventListener('click', function() { toggleStats(); loadVisitStats(); // 刷新统计数据 }); }); /** * 启动自动刷新基金数据 */ function startAutoRefresh() { // 清除可能存在的旧定时器 if (autoRefreshInterval) { clearInterval(autoRefreshInterval); } // 添加防抖变量 let isLoading = false; // 设置新的定时器 autoRefreshInterval = setInterval(function() { // 如果上一个请求还在处理中,跳过本次请求 if (isLoading) { console.log('上一个请求未完成,跳过本次刷新...'); return; } console.log('自动刷新基金数据...'); isLoading = true; loadFundData(true).finally(() => { isLoading = false; }); }, AUTO_REFRESH_INTERVAL); } /** * 停止自动刷新 */ function stopAutoRefresh() { if (autoRefreshInterval) { clearInterval(autoRefreshInterval); autoRefreshInterval = null; } } /** * 更新页面元素的数值(不刷新整个UI) * @param {Object} newData - 新的基金数据 * @param {Object} oldData - 旧的基金数据 */ function updateElements(newData, oldData) { if (!oldData) return; const { summary: newSummary, channelStats: newChannelStats } = newData; const { summary: oldSummary, channelStats: oldChannelStats } = oldData; // 更新仪表盘统计数据 if (newSummary.total !== oldSummary.total) { const el = document.querySelector('[data-stat="total-funds"]'); if (el) { el.textContent = newSummary.total; el.classList.add('highlight'); setTimeout(() => el.classList.remove('highlight'), 300); } } if (newSummary.upCount !== oldSummary.upCount) { const el = document.querySelector('[data-stat="up-funds"]'); if (el) { el.textContent = newSummary.upCount; el.classList.add('highlight'); setTimeout(() => el.classList.remove('highlight'), 300); } } if (newSummary.downCount !== oldSummary.downCount) { const el = document.querySelector('[data-stat="down-funds"]'); if (el) { el.textContent = newSummary.downCount; el.classList.add('highlight'); setTimeout(() => el.classList.remove('highlight'), 300); } } if (formatNumber(newSummary.avgChange) !== formatNumber(oldSummary.avgChange)) { const el = document.querySelector('[data-stat="avg-change"]'); if (el) { el.textContent = formatNumber(newSummary.avgChange) + '%'; el.classList.add('highlight'); setTimeout(() => el.classList.remove('highlight'), 300); } } // 更新总盈亏 if (newSummary.formattedTotalProfitLoss !== oldSummary.formattedTotalProfitLoss) { const el = document.querySelector('[data-stat="total-profit"]'); if (el) { const profitClass = newSummary.profitLossClass; el.innerHTML = ` ${newSummary.profitLossIcon} ${newSummary.formattedTotalProfitLoss} ${newSummary.formattedProfitLossRate}% `; el.classList.add('highlight'); setTimeout(() => el.classList.remove('highlight'), 300); } } // 更新渠道统计数据 for (const [channelName, newStats] of Object.entries(newChannelStats)) { const oldStats = oldChannelStats[channelName]; if (!oldStats) continue; // 更新平均涨跌 if (formatNumber(newStats.avgChange) !== formatNumber(oldStats.avgChange)) { const elements = document.querySelectorAll(`.channel-avg-change[data-channel="${channelName}"]`); elements.forEach(el => { el.textContent = formatNumber(newStats.avgChange) + '%'; // 高亮变化的元素 el.classList.add('highlight'); setTimeout(() => el.classList.remove('highlight'), 300); }); } // 更新渠道盈亏 if (newStats.totalProfitLoss !== oldStats.totalProfitLoss) { const profitClass = newStats.totalProfitLoss > 0 ? 'profit-positive' : 'profit-negative'; const profitIcon = newStats.totalProfitLoss > 0 ? '📈' : '📉'; const elements = document.querySelectorAll(`.channel-profit-section[data-channel="${channelName}"]`); elements.forEach(el => { el.className = 'channel-profit-section ' + profitClass; el.textContent = `${profitIcon} 渠道盈亏: ${formatCurrency(newStats.totalProfitLoss)} (${formatNumber(newStats.profitLossRate)}%)`; // 高亮变化的元素 el.classList.add('highlight'); setTimeout(() => el.classList.remove('highlight'), 300); }); } } // 更新基金卡片数据 for (const [channelName, newStats] of Object.entries(newChannelStats)) { const oldStats = oldChannelStats[channelName]; if (!oldStats) continue; for (const [fundCode, newFund] of Object.entries(newStats.funds)) { const oldFund = oldStats.funds[fundCode]; if (!oldFund) continue; // 解析基本数据 const parsedUnitNetValue = parseFloat(newFund.unit_net_value || newFund.dwjz) || 1; const parsedCurrentNetValue = parseFloat(newFund.current_net_value || newFund.gsz) || 1; const parsedShares = parseFloat(newFund.shares || (newFund.investment / parsedUnitNetValue)) || 0; const todayProfitLoss = parsedShares * (parsedCurrentNetValue - parsedUnitNetValue); const oldParsedUnitNetValue = parseFloat(oldFund.unit_net_value || oldFund.dwjz) || 1; const oldParsedCurrentNetValue = parseFloat(oldFund.current_net_value || oldFund.gsz) || 1; const oldParsedShares = parseFloat(oldFund.shares || (oldFund.investment / oldParsedUnitNetValue)) || 0; const oldTodayProfitLoss = oldParsedShares * (oldParsedCurrentNetValue - oldParsedUnitNetValue); // 更新估算净值 if (newFund.gsz !== oldFund.gsz) { const gszEl = document.querySelector(`.est-net-value[data-fund="${fundCode}"]`); if (gszEl) { gszEl.textContent = newFund.gsz; gszEl.classList.add('highlight'); setTimeout(() => gszEl.classList.remove('highlight'), 300); } } // 更新涨跌幅 if (newFund.gszzl !== oldFund.gszzl) { const changeEl = document.querySelector(`.change-display[data-fund="${fundCode}"]`); if (changeEl) { const change = parseFloat(newFund.gszzl); const changeDisplayClass = change > 0 ? 'change-positive' : 'change-negative'; const changeIcon = change > 0 ? '📈' : (change < 0 ? '📉' : '➡️'); const changeDisplay = change > 0 ? '+' + newFund.gszzl + '%' : newFund.gszzl + '%'; changeEl.className = 'change-display ' + changeDisplayClass + ' highlight'; changeEl.innerHTML = `${changeIcon}${changeDisplay}`; setTimeout(() => changeEl.classList.remove('highlight'), 300); } } // 更新今日盈亏 if (Math.abs(todayProfitLoss - oldTodayProfitLoss) > 0.01) { const todayProfitEl = document.querySelector(`.today-profit[data-fund="${fundCode}"]`); if (todayProfitEl) { const todayProfitClass = todayProfitLoss >= 0 ? 'profit-positive' : 'profit-negative'; const todayProfitIcon = todayProfitLoss >= 0 ? '📈' : '📉'; todayProfitEl.className = 'profit-value today-profit ' + todayProfitClass + ' highlight'; todayProfitEl.textContent = `${todayProfitIcon} ${formatCurrency(todayProfitLoss)}`; setTimeout(() => todayProfitEl.classList.remove('highlight'), 300); } } // 更新更新时间 if (newFund.gztime !== oldFund.gztime) { const timeEl = document.querySelector(`.update-time[data-fund="${fundCode}"]`); if (timeEl) { timeEl.innerHTML = ` ${newFund.gztime}`; } } } } } /** * 加载基金数据 * @param {boolean} isAutoRefresh - 是否为自动刷新模式 * @returns {Promise} - 返回Promise以便使用finally方法 */ async function loadFundData(isAutoRefresh = false) { try { // 只有手动刷新时才显示加载动画 if (!isAutoRefresh) { showLoading(); } // 若存在未完成的请求,则取消 if (fundController) { try { fundController.abort(); } catch (e) {} } fundController = new AbortController(); const response = await fetch(API_URL, { signal: fundController.signal }); const result = await response.json(); if (result.success) { // 保存旧数据用于比较 const oldData = { ...fundData }; // 更新数据 fundData = result.data; // 更新最后更新时间 document.getElementById('lastUpdate').innerHTML = ` 最后更新: ${fundData.timestamp} `; if (isAutoRefresh && oldData) { // 自动刷新时只更新变化的元素,不重新渲染整个页面 updateElements(fundData, oldData); // 检查是否有数据变化 const hasDataChanged = JSON.stringify(fundData) !== JSON.stringify(oldData); if (hasDataChanged) { showUpdateNotification(); } } else { // 首次加载或手动刷新时重新渲染整个页面 renderPage(); } } else { throw new Error('API返回错误'); } } catch (error) { // 忽略主动取消造成的错误 if (error && (error.name === 'AbortError' || error.message === 'The operation was aborted.')) { return; } console.error('加载基金数据失败:', error); // 只有手动刷新时才显示错误信息 if (!isAutoRefresh) { showError('数据加载失败,请稍后重试'); } // 重新抛出错误以便外部捕获 throw error; } } /** * 显示数据更新通知 */ function showUpdateNotification() { // 检查是否已存在通知元素 let notification = document.getElementById('updateNotification'); // 如果不存在,创建通知元素 if (!notification) { notification = document.createElement('div'); notification.id = 'updateNotification'; notification.style.cssText = ` position: fixed; bottom: 20px; right: 20px; background-color: #4CAF50; color: white; padding: 12px 20px; border-radius: 6px; box-shadow: 0 2px 10px rgba(0,0,0,0.2); z-index: 1000; opacity: 0; transition: opacity 0.3s ease; font-size: 14px; `; document.body.appendChild(notification); } // 设置通知内容 notification.textContent = '基金数据已更新'; // 显示通知 notification.style.opacity = '1'; // 3秒后隐藏通知 setTimeout(() => { notification.style.opacity = '0'; }, 3000); // 添加数据变化的视觉反馈 highlightChangedElements(); } /** * 高亮显示变化的元素 */ function highlightChangedElements() { // 为关键数据元素添加高亮效果 const keyElements = document.querySelectorAll('.fund-item .fund-value, .fund-item .fund-change, .fund-item .fund-profit, .stat-card .stat-number'); keyElements.forEach(element => { // 保存原始样式 const originalBackground = element.style.backgroundColor; const originalTransition = element.style.transition; // 添加高亮样式 element.style.backgroundColor = '#fff3cd2d'; element.style.transition = 'background-color 0.5s ease'; // 1.5秒后恢复原始样式 setTimeout(() => { element.style.backgroundColor = originalBackground; element.style.transition = originalTransition; }, 1500); }); // 为上涨/下跌状态变化的元素添加特殊效果 const statusElements = document.querySelectorAll('.fund-item .fund-status'); statusElements.forEach(element => { // 添加闪烁动画 element.style.animation = 'pulse 1s ease-in-out 2'; // 动画结束后移除动画样式 setTimeout(() => { element.style.animation = ''; }, 2000); }); } // 添加动画样式 function addAnimationStyles() { // 检查是否已存在动画样式 if (document.getElementById('animationStyles')) return; const styleElement = document.createElement('style'); styleElement.id = 'animationStyles'; styleElement.textContent = ` @keyframes pulse { 0% { opacity: 1; transform: scale(1); } 50% { opacity: 0.7; transform: scale(1.05); } 100% { opacity: 1; transform: scale(1); } } .highlight { animation: highlight 0.3s ease-in-out; } @keyframes highlight { 0% { background-color: transparent; } 50% { background-color: rgba(255, 255, 0, 0.03); } 100% { background-color: transparent; } } `; document.head.appendChild(styleElement); } // 加载访问统计 async function loadVisitStats() { try { const response = await fetch(STATS_URL); const result = await response.json(); if (result.success) { visitStats = result.data; updateStatsDisplay(); } } catch (error) { console.error('加载访问统计失败:', error); } } // 更新统计显示 function updateStatsDisplay() { if (!visitStats) return; // 更新页脚统计 document.getElementById('todayVisits').textContent = visitStats.today_visits; document.getElementById('totalVisits').textContent = visitStats.total_visits; document.getElementById('uniqueVisitors').textContent = visitStats.unique_visitors; // 更新统计面板 const statsGrid = document.getElementById('statsGrid'); const recentVisits = document.getElementById('recentVisits'); if (statsGrid) { statsGrid.innerHTML = `
${visitStats.total_visits}
三天访问量
${visitStats.today_visits}
今日访问
${visitStats.unique_visitors}
独立访客
${visitStats.today_unique_visitors}
今日独立访客
`; } if (recentVisits && visitStats.recent_visits) { let visitsHTML = '

最近访问记录

'; if (visitStats.recent_visits.length === 0) { visitsHTML += '

暂无访问记录

'; } else { visitStats.recent_visits.forEach(visit => { visitsHTML += `
${visit.ip} ${getBrowserInfo(visit.user_agent)}
${formatTime(visit.date)}
`; }); } recentVisits.innerHTML = visitsHTML; } } // 获取浏览器信息 function getBrowserInfo(userAgent) { if (userAgent.includes('Chrome')) return 'Chrome'; if (userAgent.includes('Firefox')) return 'Firefox'; if (userAgent.includes('Safari')) return 'Safari'; if (userAgent.includes('Edge')) return 'Edge'; if (userAgent.includes('MSIE') || userAgent.includes('Trident')) return 'IE'; return '其他浏览器'; } // 显示/隐藏统计面板 function toggleStats() { const panel = document.getElementById('statsPanel'); if (panel.style.display === 'none') { panel.style.display = 'block'; loadVisitStats(); // 确保数据最新 } else { panel.style.display = 'none'; } } // 格式化时间 function formatTime(dateString) { const date = new Date(dateString); const now = new Date(); const diff = now - date; if (diff < 60000) { // 1分钟内 return '刚刚'; } else if (diff < 3600000) { // 1小时内 return Math.floor(diff / 60000) + '分钟前'; } else if (diff < 86400000) { // 1天内 return Math.floor(diff / 3600000) + '小时前'; } else { return date.toLocaleDateString() + ' ' + date.toLocaleTimeString([], {hour: '2-digit', minute:'2-digit'}); } } // 显示加载状态 function showLoading() { document.getElementById('content').innerHTML = `

正在加载基金数据...

`; } // 显示错误信息 function showError(message) { document.getElementById('content').innerHTML = `

数据加载失败

${message}

`; } // 渲染页面 function renderPage() { if (!fundData) return; const { summary, channelStats, errors, timestamp } = fundData; // 更新最后更新时间 document.getElementById('lastUpdate').innerHTML = ` 最后更新: ${timestamp} `; // 构建页面内容 let contentHTML = `
${summary.total}
监控基金
${summary.upCount}
上涨
${summary.downCount}
下跌
${formatNumber(summary.avgChange)}%
平均涨跌
${summary.formattedTotalInvestment}
总投资
${summary.profitLossIcon} ${summary.formattedTotalProfitLoss} ${summary.formattedProfitLossRate}%
总盈亏
`; // 渠道统计 contentHTML += '
'; for (const [channelName, stats] of Object.entries(channelStats)) { const channelClass = getChannelClass(channelName); const profitClass = stats.totalProfitLoss > 0 ? 'profit-positive' : 'profit-negative'; const profitIcon = stats.totalProfitLoss > 0 ? '📈' : '📉'; contentHTML += `
${getChannelIcon(channelName)}
${channelName}
基金数量
${stats.count} 只
平均涨跌
${formatNumber(stats.avgChange)}%
投资金额
${formatCurrency(stats.totalInvestment)}
当前价值
${formatCurrency(stats.totalCurrentValue)}
${profitIcon} 渠道盈亏: ${formatCurrency(stats.totalProfitLoss)} (${formatNumber(stats.profitLossRate)}%)
`; } contentHTML += '
'; // 错误信息 if (errors && errors.length > 0) { contentHTML += `

数据获取异常

`; } // 基金卡片 contentHTML += generateChannelSections(channelStats); document.getElementById('content').innerHTML = contentHTML; // 添加动画效果 setTimeout(() => { const cards = document.querySelectorAll('.fund-card, .stat-card, .channel-stat-card'); cards.forEach((card, index) => { card.style.animationDelay = (index * 0.05) + 's'; }); }, 100); // 绑定基金卡片点击翻转与图表加载 bindFundCardFlipEvents(); } // 绑定基金卡片翻转与图表加载 function bindFundCardFlipEvents() { const cards = document.querySelectorAll('.flip-card'); cards.forEach(card => { card.addEventListener('click', async (e) => { const fundCode = card.getAttribute('data-fundcode'); const isFlipped = card.classList.contains('flipped'); if (!isFlipped) { card.classList.add('flipped'); await loadCardChart(fundCode, card); } else { card.classList.remove('flipped'); destroyCardChart(fundCode); } }); // 背面关闭按钮只控制翻回 const closeBtn = card.querySelector('.flip-close'); if (closeBtn) { closeBtn.addEventListener('click', (e) => { e.stopPropagation(); card.classList.remove('flipped'); const fundCode = card.getAttribute('data-fundcode'); destroyCardChart(fundCode); }); } // 阻止点击图表容器导致翻回(便于交互) const chartContainer = card.querySelector('.card-back .chart-container'); if (chartContainer) { chartContainer.addEventListener('click', (e) => e.stopPropagation()); chartContainer.addEventListener('touchstart', (e) => e.stopPropagation(), { passive: true }); } }); } async function loadCardChart(fundCode, cardEl) { try { const response = await fetch(FUND_CHART_API(fundCode)); const result = await response.json(); if (!result.success) throw new Error(result.message || '图表数据加载失败'); renderCardChart(fundCode, result.data, cardEl); } catch (err) { console.error('加载卡片图表失败:', err); const container = cardEl.querySelector('.card-back .chart-container'); if (container) { container.innerHTML = `
图表加载失败:${err.message}
`; } } } function renderCardChart(fundCode, chartData, cardEl) { const canvas = cardEl.querySelector(`#fund-chart-${fundCode}`); if (!canvas) return; const ctx = canvas.getContext('2d'); const isDark = document.body.classList.contains('theme-dark'); const axisColor = isDark ? '#cbd5e1' : '#374151'; const gridColor = isDark ? 'rgba(203,213,225,0.15)' : 'rgba(55,65,81,0.15)'; const lineColor = '#6366f1'; const fillColor = isDark ? 'rgba(99,102,241,0.15)' : 'rgba(99,102,241,0.2)'; if (cardCharts.has(fundCode)) { try { cardCharts.get(fundCode).destroy(); } catch (e) {} cardCharts.delete(fundCode); } const chart = new Chart(ctx, { type: 'line', data: { labels: chartData.labels, datasets: [{ label: '估算净值', data: chartData.netValues, borderColor: lineColor, backgroundColor: fillColor, borderWidth: 3, fill: true, tension: 0.4, pointRadius: 3, pointHoverRadius: 6, pointBackgroundColor: '#ffffff', pointBorderColor: lineColor, pointBorderWidth: 2 }] }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { display: false }, tooltip: { mode: 'index', intersect: false } }, scales: { x: { ticks: { color: axisColor }, grid: { color: gridColor } }, y: { ticks: { color: axisColor }, grid: { color: gridColor } } } } }); cardCharts.set(fundCode, chart); } function destroyCardChart(fundCode) { if (cardCharts.has(fundCode)) { try { cardCharts.get(fundCode).destroy(); } catch (e) {} cardCharts.delete(fundCode); } } // 生成渠道分组的基金卡片 function generateChannelSections(channelStats) { let html = ''; for (const [channelName, stats] of Object.entries(channelStats)) { // 按涨跌幅排序 const sortedFunds = Object.entries(stats.funds) .sort(([,a], [,b]) => parseFloat(b.gszzl) - parseFloat(a.gszzl)); const channelClass = getChannelClass(channelName); const channelIcon = getChannelIcon(channelName); html += `
${channelIcon}

${channelName}渠道

${getChannelDescription(channelName)}

`; let rank = 1; for (const [fundCode, fund] of sortedFunds) { const change = parseFloat(fund.gszzl); const changeClass = change > 0 ? 'positive' : (change < 0 ? 'negative' : ''); const changeDisplayClass = change > 0 ? 'change-positive' : 'change-negative'; const changeIcon = change > 0 ? '📈' : (change < 0 ? '📉' : '➡️'); const changeDisplay = change > 0 ? '+' + fund.gszzl + '%' : fund.gszzl + '%'; // 历史涨幅样式 // 添加调试代码确保昨日涨幅数据正确处理 console.log(`基金${fund.fundcode}昨日涨幅数据:`, { yesterday_change: fund.yesterday_change, formatted_yesterday_change: fund.formatted_yesterday_change }); const yesterdayChange = fund.yesterday_change || 0; // 确保有默认值 const yesterdayChangeClass = yesterdayChange > 0 ? 'profit-positive' : (yesterdayChange < 0 ? 'profit-negative' : ''); // 盈亏信息 const investment = fund.investment; const currentValue = fund.current_value; const profitLoss = fund.profit_loss; const profitLossRate = (profitLoss / investment) * 100; // 份额和净值信息(从后端新计算的数据) const shares = (fund.shares || (investment / (parseFloat(fund.dwjz) || 1))).toFixed(4); const unitNetValue = (fund.unit_net_value || parseFloat(fund.dwjz)).toFixed(4); const currentNetValue = (fund.current_net_value || parseFloat(fund.gsz)).toFixed(4); // 解析基本数据 const parsedCurrentNetValue = parseFloat(currentNetValue); // 当日净值 const parsedUnitNetValue = parseFloat(unitNetValue); // 前一日净值(昨日单位净值) const parsedShares = parseFloat(shares); // 份额 // 计算标准涨幅 const todayChange = parsedUnitNetValue !== 0 ? ((parsedCurrentNetValue - parsedUnitNetValue) / parsedUnitNetValue) * 100 : 0; // 计算今日盈亏(基于净值变化) const todayProfitLoss = parsedShares * (parsedCurrentNetValue - parsedUnitNetValue); // 计算今日价值(累计方式) const calculatedCurrentValue = parsedShares * parsedCurrentNetValue; // 直接计算当前价值 // 添加日志以调试计算问题 // if (fund.fundcode === '012863') { // console.log(`基金012863计算详情:`); // console.log(`- 份额: ${parsedShares.toFixed(4)}`); // console.log(`- 前一日净值: ${parsedUnitNetValue.toFixed(4)}`); // console.log(`- 当日净值: ${parsedCurrentNetValue.toFixed(4)}`); // console.log(`- 计算的标准涨幅: ${todayChange.toFixed(2)}%`); // console.log(`- 投资金额: ${investment.toFixed(2)}元`); // console.log(`- 今日盈亏: ${todayProfitLoss.toFixed(2)}元 (份额 × (当日净值 - 昨日单位净值))`); // console.log(`- 当前价值: ${calculatedCurrentValue.toFixed(2)}元 (份额 × 当日净值)`); // } // 今日盈亏样式 const todayProfitClass = todayProfitLoss >= 0 ? 'profit-positive' : 'profit-negative'; const todayProfitIcon = todayProfitLoss >= 0 ? '📈' : '📉'; html += `
${fund.name} #${rank}
${fund.fundcode}
单位净值
${fund.dwjz}
估算净值
${fund.gsz}
昨日涨幅
${fund.formatted_yesterday_change}
${changeIcon} ${changeDisplay}
持仓: ${formatCurrency(investment)}
份额: ${shares}
净值: ${unitNetValue}
当前净值: ${currentNetValue}
今日盈亏: ${todayProfitIcon} ${formatCurrency(todayProfitLoss)}
${channelIcon} ${channelName}
${fund.gztime}
建议: ${channelName}申购
`; rank++; } html += '
'; } return html; } // 工具函数 function formatNumber(number) { return Number(number).toFixed(2); } function formatCurrency(amount) { if (amount >= 10000) { return (amount / 10000).toFixed(2) + '万元'; } return amount.toFixed(2) + '元'; } function getChannelClass(channelName) { const channelMap = { '招商银行': 'cmb', '天天基金': 'tt', '支付宝': 'zfb' }; return channelMap[channelName] || 'cmb'; } function getChannelIcon(channelName) { const iconMap = { '招商银行': '🏦', '天天基金': '📱', '支付宝': '💙' }; return iconMap[channelName] || '🏦'; } function getChannelDescription(channelName) { const descMap = { '招商银行': '银行渠道,申购费率较低,服务稳定', '天天基金': '专业基金平台,品种齐全,数据精准', '支付宝': '便捷支付,操作简单,用户体验佳' }; return descMap[channelName] || '基金购买渠道'; } function toggleTheme() { const body = document.body; const isDark = body.classList.toggle('theme-dark'); try { localStorage.setItem('theme', isDark ? 'dark' : 'light'); } catch (e) { /* 忽略存储错误 */ } } /** * 根据本地存储或系统偏好应用主题 */ function applySavedTheme() { let saved = null; try { saved = localStorage.getItem('theme'); } catch (e) { /* 忽略 */ } const prefersDark = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches; const useDark = saved ? (saved === 'dark') : prefersDark; if (useDark) { document.body.classList.add('theme-dark'); } else { document.body.classList.remove('theme-dark'); } } // 自动刷新机制已在startAutoRefresh函数中实现