// 全局变量
let fundsData = [];
let fundChart = null;
// 页面加载完成后初始化
document.addEventListener('DOMContentLoaded', function() {
loadFundsData();
loadOperationLog();
populateFundSelector();
// 表单提交事件
document.getElementById('fundForm').addEventListener('submit', function(e) {
e.preventDefault();
saveFund();
});
});
// 加载推荐基金数据
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');
// 激活选中的标签
event.target.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);
};
}
}
// 加载基金数据
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 now = new Date();
const diff = now - date;
if (diff < 60000) {
return '刚刚';
} else if (diff < 3600000) {
return Math.floor(diff / 60000) + '分钟前';
} else if (diff < 86400000) {
return Math.floor(diff / 3600000) + '小时前';
} else {
return date.toLocaleDateString() + ' ' + date.toLocaleTimeString([], {hour: '2-digit', minute:'2-digit'});
}
}
// 点击模态框外部关闭
window.onclick = function(event) {
const modal = document.getElementById('fundModal');
if (event.target === modal) {
closeModal();
}
}