// 全局变量 let fundsData = []; let fundChart = null; // 页面加载完成后初始化 document.addEventListener('DOMContentLoaded', function() { loadFundsData(); loadOperationLog(); populateFundSelector(); loadEmailConfig(); // 表单提交事件 document.getElementById('fundForm').addEventListener('submit', function(e) { e.preventDefault(); saveFund(); }); // SMTP配置表单提交事件 if (document.getElementById('smtpConfigForm')) { console.log('找到smtpConfigForm,添加提交事件监听'); document.getElementById('smtpConfigForm').addEventListener('submit', function(e) { console.log('表单提交事件触发'); e.preventDefault(); console.log('阻止默认提交行为'); saveSmtpConfig(); }); } else { console.log('未找到smtpConfigForm元素'); } }); // 加载推荐基金数据 async function loadRecommendedFunds() { try { showLoading(); const response = await fetch('admin_api.php?action=get_recommended_funds'); const result = await response.json(); if (result.success) { renderRecommendedFundsTable(result.data); } else { throw new Error(result.message || '加载数据失败'); } } catch (error) { console.error('加载推荐基金数据失败:', error); showMessage('数据加载失败: ' + error.message, 'error'); } finally { hideLoading(); } } // 渲染推荐基金表格 function renderRecommendedFundsTable(data) { const tbody = document.getElementById('recommendedFundsList'); if (!data || data.length === 0) { tbody.innerHTML = ` 暂无推荐基金数据 `; return; } let html = ''; data.forEach((fund, index) => { // 处理状态显示,支持多种可能的状态值 const getStatusText = (status) => { const normalizedStatus = (status || '').toLowerCase().trim(); if (normalizedStatus.includes('approve') || normalizedStatus === '已批准') { return '已批准'; } else if (normalizedStatus.includes('pend') || normalizedStatus === '待审核') { return '待审核'; } else if (normalizedStatus.includes('reject') || normalizedStatus === '已拒绝') { return '已拒绝'; } return '未知'; }; const getStatusClass = (status) => { const normalizedStatus = (status || '').toLowerCase().trim(); if (normalizedStatus.includes('approve') || normalizedStatus === '已批准') { return 'status-approved'; } else if (normalizedStatus.includes('pend') || normalizedStatus === '待审核') { return 'status-pending'; } else if (normalizedStatus.includes('reject') || normalizedStatus === '已拒绝') { return 'status-rejected'; } return 'status-unknown'; }; const statusText = getStatusText(fund.status); const statusClass = getStatusClass(fund.status); // 根据状态决定操作按钮 let actionButtons = ''; if (fund.status === 'pending') { actionButtons = ` `; } else { // 已批准或已拒绝状态只显示删除按钮 actionButtons = ` `; } html += ` ${fund.fund_code} ${fund.ip} ${fund.timestamp} ${fund.channel} ${parseFloat(fund.amount).toFixed(2)} ${statusText}
${actionButtons}
`; }); tbody.innerHTML = html; } // 搜索推荐基金 function searchRecommendedFunds(keyword) { const rows = document.querySelectorAll('#recommendedFundsList tr'); keyword = keyword.toLowerCase(); rows.forEach(row => { const fundCode = row.querySelector('td:first-child').textContent.toLowerCase(); if (fundCode.includes(keyword)) { row.style.display = ''; } else { row.style.display = 'none'; } }); } // 批准推荐基金 async function approveRecommendedFund(fundCode) { if (!confirm(`确定要批准基金 ${fundCode} 吗?`)) { return; } try { showLoading(); const response = await fetch('admin_api.php?action=approve_recommended_fund&fund_code=' + fundCode); const result = await response.json(); if (result.success) { showMessage('基金批准成功', 'success'); loadRecommendedFunds(); } else { throw new Error(result.message || '操作失败'); } } catch (error) { console.error('批准推荐基金失败:', error); showMessage('操作失败: ' + error.message, 'error'); } finally { hideLoading(); } } // 删除推荐基金 async function deleteRecommendedFund(fundCode) { if (!confirm(`确定要删除推荐基金 ${fundCode} 吗?此操作不可恢复。`)) { return; } try { showLoading(); const response = await fetch('admin_api.php?action=delete_recommended_fund&fund_code=' + fundCode); const result = await response.json(); if (result.success) { showMessage('基金删除成功', 'success'); loadRecommendedFunds(); } else { throw new Error(result.message || '操作失败'); } } catch (error) { console.error('删除推荐基金失败:', error); showMessage('操作失败: ' + error.message, 'error'); } finally { hideLoading(); } } // 切换标签页 function switchTab(tabName) { // 隐藏所有标签内容 document.querySelectorAll('.tab-content').forEach(tab => { tab.classList.remove('active'); }); // 移除所有标签的激活状态 document.querySelectorAll('.tab').forEach(tab => { tab.classList.remove('active'); }); // 显示选中的标签内容 document.getElementById(tabName).classList.add('active'); // 激活选中的标签 if (event) { event.target.classList.add('active'); } else { // 如果event对象不存在,通过其他方式获取激活的标签 document.querySelectorAll('.tab').forEach(tab => { if (tab.getAttribute('onclick') && tab.getAttribute('onclick').includes(tabName)) { tab.classList.add('active'); } }); } // 如果是图表标签,加载默认图表 if (tabName === 'fundCharts') { const fundSelector = document.getElementById('fundSelector'); if (fundSelector.value) { loadFundChart(fundSelector.value); } } else if (tabName === 'recommendedFunds') { // 加载推荐基金数据 loadRecommendedFunds(); // 添加搜索事件监听 const searchInput = document.getElementById('searchRecommendedFund'); searchInput.oninput = function() { searchRecommendedFunds(this.value); }; } else if (tabName === 'emailSettings') { // 加载邮箱配置 loadEmailConfig(); } } // 加载基金数据 async function loadFundsData() { try { showLoading(); const response = await fetch('admin_api.php?action=get_funds'); const result = await response.json(); if (result.success) { fundsData = result.data; renderFundsTable(); populateFundSelector(); } else { throw new Error(result.message || '加载数据失败'); } } catch (error) { console.error('加载基金数据失败:', error); showMessage('数据加载失败: ' + error.message, 'error'); } } // 渲染基金表格 function renderFundsTable() { const tbody = document.getElementById('fundsTableBody'); if (fundsData.length === 0) { tbody.innerHTML = ` 暂无基金数据,点击"添加基金"开始配置 `; return; } let html = ''; fundsData.forEach((fund, index) => { const channelName = getChannelName(fund.channel); const channelClass = getChannelClass(fund.channel); html += ` ${fund.fund_code} ${fund.name || '加载中...'} ${getChannelIcon(fund.channel)} ${channelName} ${parseFloat(fund.investment).toFixed(2)}
`; }); tbody.innerHTML = html; // 加载基金名称 loadFundNames(); } // 加载基金名称 async function loadFundNames() { const nameCells = document.querySelectorAll('#fundsTableBody td:nth-child(2)'); // 先尝试从localStorage获取缓存的基金名称 const fundNameCache = JSON.parse(localStorage.getItem('fundNameCache') || '{}'); // 收集所有需要获取名称的基金代码 const fundsWithoutNames = fundsData.filter(fund => !fund.name && !fundNameCache[fund.fund_code]); // 先应用缓存的名称 fundsData.forEach((fund, index) => { if (fundNameCache[fund.fund_code]) { nameCells[index].textContent = fundNameCache[fund.fund_code]; fundsData[index].name = fundNameCache[fund.fund_code]; } }); // 如果有需要获取名称的基金,通过服务器端代理获取 if (fundsWithoutNames.length > 0) { try { const fundCodes = fundsWithoutNames.map(fund => fund.fund_code).join(','); const response = await fetch(`admin_api.php?action=get_fund_names&fund_codes=${fundCodes}`); const result = await response.json(); if (result.success && result.data) { // 更新页面显示和缓存 fundsData.forEach((fund, index) => { if (result.data[fund.fund_code]) { const fundName = result.data[fund.fund_code]; nameCells[index].textContent = fundName; fundsData[index].name = fundName; fundNameCache[fund.fund_code] = fundName; } }); // 保存缓存 localStorage.setItem('fundNameCache', JSON.stringify(fundNameCache)); } } catch (error) { console.error('获取基金名称失败:', error); // 即使失败也给每个基金一个默认名称 fundsData.forEach((fund, index) => { if (!fund.name && !fundNameCache[fund.fund_code]) { nameCells[index].textContent = `基金${fund.fund_code}`; } }); } } } // 填充基金选择器 function populateFundSelector() { const selector = document.getElementById('fundSelector'); selector.innerHTML = ''; fundsData.forEach(fund => { const option = document.createElement('option'); option.value = fund.fund_code; option.textContent = `${fund.fund_code} - ${fund.name || '加载中...'}`; selector.appendChild(option); }); } // 加载操作日志 async function loadOperationLog() { try { const response = await fetch('admin_api.php?action=get_operation_log&limit=50'); const result = await response.json(); if (result.success) { renderOperationLog(result.data); } else { throw new Error(result.message || '加载操作日志失败'); } } catch (error) { console.error('加载操作日志失败:', error); document.getElementById('operationLogContent').innerHTML = `
加载操作日志失败: ${error.message}
`; } } // 渲染操作日志 function renderOperationLog(operations) { const container = document.getElementById('operationLogContent'); if (operations.length === 0) { container.innerHTML = `
暂无操作记录
`; return; } let html = ''; operations.forEach(operation => { const typeClass = `type-${operation.type}`; const typeText = { 'add': '添加', 'update': '更新', 'delete': '删除' }[operation.type] || operation.type; const channelName = getChannelName(operation.channel); html += `
${typeText} ${operation.fund_code} ${operation.channel ? ` - ${channelName}` : ''} ${operation.investment ? ` - ${operation.investment}元` : ''} ${operation.details ? ` - ${operation.details}` : ''}
${formatTime(operation.date)}
`; }); container.innerHTML = html; } // 加载基金图表 async function loadFundChart(fundCode) { if (!fundCode) { document.getElementById('chartTitle').textContent = '请选择基金查看净值变化'; if (fundChart) { fundChart.destroy(); } return; } try { const response = await fetch(`api.php?action=get_fund_chart&fund_code=${fundCode}`); const result = await response.json(); if (result.success) { renderFundChart(fundCode, result.data); } else { throw new Error(result.message || '加载图表数据失败'); } } catch (error) { console.error('加载基金图表失败:', error); document.getElementById('chartTitle').textContent = `加载图表失败: ${error.message}`; } } // 渲染基金图表 function renderFundChart(fundCode, chartData) { const ctx = document.getElementById('fundChart').getContext('2d'); const fundName = fundsData.find(f => f.fund_code === fundCode)?.name || fundCode; document.getElementById('chartTitle').textContent = `${fundName} - 近5日净值变化`; // 销毁现有图表 if (fundChart) { fundChart.destroy(); } // 创建新图表 fundChart = new Chart(ctx, { type: 'line', data: { labels: chartData.labels, datasets: [ { label: '估算净值', data: chartData.netValues, borderColor: '#6366f1', backgroundColor: 'rgba(99, 102, 241, 0.2)', borderWidth: 3, fill: true, tension: 1, // 最大张力,使曲线更平滑 pointRadius: 5, pointHoverRadius: 8, pointBackgroundColor: '#ffffff', pointBorderColor: '#6366f1', pointBorderWidth: 2, pointHoverBackgroundColor: '#6366f1', pointHoverBorderColor: '#ffffff', pointHoverBorderWidth: 2 }, { label: '涨跌幅 (%)', data: chartData.changes, borderColor: '#10b981', backgroundColor: 'rgba(16, 185, 129, 0.2)', borderWidth: 3, fill: false, tension: 1, // 最大张力,使曲线更平滑 pointRadius: 5, pointHoverRadius: 8, pointBackgroundColor: '#ffffff', pointBorderColor: '#10b981', pointBorderWidth: 2, pointHoverBackgroundColor: '#10b981', pointHoverBorderColor: '#ffffff', pointHoverBorderWidth: 2, yAxisID: 'y1' } ] }, options: { responsive: true, maintainAspectRatio: false, interaction: { mode: 'index', intersect: false, axis: 'x' }, animation: { duration: 1500, easing: 'easeInOutQuart' }, scales: { x: { grid: { display: false }, ticks: { font: { size: 12 } } }, y: { type: 'linear', display: true, position: 'left', title: { display: true, text: '估算净值', font: { size: 14, weight: 'bold' } }, grid: { color: 'rgba(0, 0, 0, 0.05)' }, ticks: { font: { size: 11 }, callback: function(value) { return value.toFixed(4); } } }, y1: { type: 'linear', display: true, position: 'right', title: { display: true, text: '涨跌幅 (%)', font: { size: 14, weight: 'bold' } }, grid: { drawOnChartArea: false, }, ticks: { font: { size: 11 }, callback: function(value) { return value > 0 ? '+' + value.toFixed(2) + '%' : value.toFixed(2) + '%'; } } } }, plugins: { legend: { position: 'top', labels: { usePointStyle: true, padding: 20, font: { size: 13 } } }, tooltip: { backgroundColor: 'rgba(0, 0, 0, 0.8)', padding: 12, titleFont: { size: 14, weight: 'bold' }, bodyFont: { size: 13 }, borderColor: '#6366f1', borderWidth: 1, displayColors: true, callbacks: { title: function(tooltipItems) { return tooltipItems[0].label; }, label: function(context) { let label = context.dataset.label || ''; if (label) { label += ': '; } if (context.datasetIndex === 0) { label += context.parsed.y.toFixed(4); } else { const value = context.parsed.y; label += (value > 0 ? '+' : '') + value.toFixed(2) + '%'; } return label; }, afterBody: function(context) { // 添加额外信息或空行以美化显示 return ''; } } } } } }); } // 显示添加模态框 function showAddModal() { document.getElementById('modalTitle').textContent = '添加基金'; document.getElementById('fundForm').reset(); document.getElementById('editIndex').value = ''; document.getElementById('fundModal').style.display = 'flex'; } // 编辑基金 function editFund(index) { const fund = fundsData[index]; document.getElementById('modalTitle').textContent = '编辑基金'; document.getElementById('fundCode').value = fund.fund_code; document.getElementById('channel').value = fund.channel; document.getElementById('investment').value = fund.investment; document.getElementById('editIndex').value = index; document.getElementById('fundModal').style.display = 'flex'; } // 关闭模态框 function closeModal() { document.getElementById('fundModal').style.display = 'none'; } // 保存基金 async function saveFund() { const editIndex = document.getElementById('editIndex').value; const fundCode = document.getElementById('fundCode').value.trim(); const channel = document.getElementById('channel').value; const investment = parseFloat(document.getElementById('investment').value); // 验证表单数据 if (!fundCode) { showMessage('请输入基金代码', 'error'); return; } if (!/^\d{6}$/.test(fundCode)) { showMessage('基金代码必须是6位数字', 'error'); return; } if (!channel) { showMessage('请选择渠道', 'error'); return; } if (!investment || investment <= 0) { showMessage('投资金额必须大于0', 'error'); return; } // 检查重复(添加时检查,编辑时不检查自身) if (editIndex === '' || fundsData[editIndex].fund_code !== fundCode) { const exists = fundsData.some((fund, index) => fund.fund_code === fundCode && fund.channel === channel && (editIndex === '' || index !== parseInt(editIndex)) ); if (exists) { showMessage('该渠道下已存在相同的基金代码', 'error'); return; } } try { const action = editIndex === '' ? 'add_fund' : 'update_fund'; const payload = { fund_code: fundCode, channel: channel, investment: investment }; if (editIndex !== '') { payload.index = parseInt(editIndex); } console.log('发送请求:', action, payload); const response = await fetch('admin_api.php?action=' + action, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(payload) }); const result = await response.json(); console.log('响应结果:', result); if (result.success) { showMessage(result.message, 'success'); closeModal(); loadFundsData(); loadOperationLog(); // 刷新操作日志 } else { throw new Error(result.message || '保存失败'); } } catch (error) { console.error('保存基金失败:', error); showMessage('保存失败: ' + error.message, 'error'); } } // 删除基金 async function deleteFund(index) { if (!confirm('确定要删除这只基金吗?')) { return; } try { const response = await fetch('admin_api.php?action=delete_fund', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ index: index }) }); const result = await response.json(); if (result.success) { showMessage(result.message, 'success'); loadFundsData(); loadOperationLog(); // 刷新操作日志 } else { throw new Error(result.message || '删除失败'); } } catch (error) { console.error('删除基金失败:', error); showMessage('删除失败: ' + error.message, 'error'); } } // 工具函数 function getChannelName(channel) { const channels = { '0': '招商银行', '1': '天天基金', '2': '支付宝' }; return channels[channel] || '未知渠道'; } function getChannelClass(channel) { const classMap = { '0': 'channel-cmb', '1': 'channel-tt', '2': 'channel-zfb' }; return classMap[channel] || 'channel-cmb'; } function getChannelIcon(channel) { const iconMap = { '0': '🏦', '1': '📱', '2': '💙' }; return iconMap[channel] || '🏦'; } // 显示消息 function showMessage(message, type) { const messageEl = document.getElementById('message'); messageEl.textContent = message; messageEl.className = `alert alert-${type === 'success' ? 'success' : 'error'}`; messageEl.style.display = 'block'; // 自动隐藏成功消息,错误消息保持显示直到用户操作 if (type === 'success') { setTimeout(() => { messageEl.style.display = 'none'; }, 3000); } } // 显示加载状态 function showLoading() { const tbody = document.getElementById('fundsTableBody'); tbody.innerHTML = `
加载中... `; } // 格式化时间 function formatTime(dateString) { const date = new Date(dateString); const year = date.getFullYear(); const month = String(date.getMonth() + 1).padStart(2, '0'); const day = String(date.getDate()).padStart(2, '0'); const hours = String(date.getHours()).padStart(2, '0'); const minutes = String(date.getMinutes()).padStart(2, '0'); const seconds = String(date.getSeconds()).padStart(2, '0'); return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`; } // 邮箱设置相关函数 /** * 加载邮箱配置 */ async function loadEmailConfig() { try { showLoading(); const response = await fetch('admin_api.php?action=get_email_config'); const result = await response.json(); if (result.success) { const config = result.data; // 填充SMTP配置表单 document.getElementById('smtpServer').value = config.smtp_server || ''; document.getElementById('smtpPort').value = config.smtp_port || ''; document.getElementById('smtpSecure').value = config.smtp_secure || ''; document.getElementById('username').value = config.username || ''; document.getElementById('password').value = config.password || ''; document.getElementById('fromEmail').value = config.from_email || ''; document.getElementById('fromName').value = config.from_name || ''; // 渲染收件人列表 renderEmailRecipients(config.to_emails || []); } else { throw new Error(result.message || '加载邮箱配置失败'); } } catch (error) { console.error('加载邮箱配置失败:', error); showMessage('加载邮箱配置失败: ' + error.message, 'error'); } finally { hideLoading(); } } /** * 保存SMTP配置 */ async function saveSmtpConfig() { try { console.log('saveSmtpConfig函数被调用'); showLoading(); // 检查表单元素是否存在 console.log('smtpServer元素:', document.getElementById('smtpServer')); console.log('fromName元素:', document.getElementById('fromName')); const smtpConfig = { smtp_server: document.getElementById('smtpServer').value, smtp_port: document.getElementById('smtpPort').value, smtp_secure: document.getElementById('smtpSecure').value, username: document.getElementById('username').value, password: document.getElementById('password').value, from_email: document.getElementById('fromEmail').value, from_name: document.getElementById('fromName').value }; console.log('表单数据:', smtpConfig); const response = await fetch('admin_api.php', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ action: 'update_email_config', ...smtpConfig }) }); const result = await response.json(); if (result.success) { showMessage('SMTP配置保存成功', 'success'); } else { throw new Error(result.message || '保存SMTP配置失败'); } } catch (error) { console.error('保存SMTP配置失败:', error); showMessage('保存SMTP配置失败: ' + error.message, 'error'); } finally { hideLoading(); } } /** * 添加邮箱收件人 */ async function addEmailRecipient() { try { showLoading(); const newEmail = document.getElementById('newEmail').value; if (!newEmail) { showMessage('请输入邮箱地址', 'warning'); return; } const response = await fetch('admin_api.php', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ action: 'add_email_recipient', email: newEmail }) }); const result = await response.json(); if (result.success) { showMessage('邮箱收件人添加成功', 'success'); document.getElementById('newEmail').value = ''; renderEmailRecipients(result.data || []); } else { throw new Error(result.message || '添加邮箱收件人失败'); } } catch (error) { console.error('添加邮箱收件人失败:', error); showMessage('添加邮箱收件人失败: ' + error.message, 'error'); } finally { hideLoading(); } } /** * 删除邮箱收件人 */ async function deleteEmailRecipient(email) { try { showLoading(); const response = await fetch('admin_api.php', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ action: 'delete_email_recipient', email: email }) }); const result = await response.json(); if (result.success) { showMessage('邮箱收件人删除成功', 'success'); renderEmailRecipients(result.data || []); } else { throw new Error(result.message || '删除邮箱收件人失败'); } } catch (error) { console.error('删除邮箱收件人失败:', error); showMessage('删除邮箱收件人失败: ' + error.message, 'error'); } finally { hideLoading(); } } /** * 渲染邮箱收件人列表 */ function renderEmailRecipients(emails) { const listContainer = document.getElementById('emailRecipientsList'); if (!listContainer) { return; } if (!emails || emails.length === 0) { listContainer.innerHTML = '

暂无收件人

'; return; } let html = '
'; emails.forEach(email => { html += `
${email}
`; }); html += '
'; listContainer.innerHTML = html; } // 点击模态框外部关闭模态框 window.onclick = function(event) { const modal = document.getElementById('fundModal'); if (event.target === modal) { closeModal(); } }