849 lines
28 KiB
JavaScript
849 lines
28 KiB
JavaScript
// 全局变量
|
|
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 = `
|
|
<tr>
|
|
<td colspan="7" style="text-align: center; padding: 40px; color: var(--gray);">
|
|
<i class="fas fa-inbox" style="font-size: 3rem; margin-bottom: 10px; display: block; opacity: 0.5;"></i>
|
|
暂无推荐基金数据
|
|
</td>
|
|
</tr>
|
|
`;
|
|
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 = `
|
|
<button class="btn btn-primary btn-sm" onclick="approveRecommendedFund('${fund.fund_code}')">
|
|
<i class="fas fa-check"></i> 批准
|
|
</button>
|
|
<button class="btn btn-danger btn-sm" onclick="deleteRecommendedFund('${fund.fund_code}')">
|
|
<i class="fas fa-trash"></i> 删除
|
|
</button>
|
|
`;
|
|
} else {
|
|
// 已批准或已拒绝状态只显示删除按钮
|
|
actionButtons = `
|
|
<button class="btn btn-danger btn-sm" onclick="deleteRecommendedFund('${fund.fund_code}')">
|
|
<i class="fas fa-trash"></i> 删除
|
|
</button>
|
|
`;
|
|
}
|
|
|
|
html += `
|
|
<tr class="fund-row">
|
|
<td class="fund-code-cell"><strong>${fund.fund_code}</strong></td>
|
|
<td class="fund-ip-cell">${fund.ip}</td>
|
|
<td class="fund-time-cell">${fund.timestamp}</td>
|
|
<td class="fund-channel-cell">${fund.channel}</td>
|
|
<td class="fund-amount-cell">${parseFloat(fund.amount).toFixed(2)}</td>
|
|
<td class="fund-status-cell">
|
|
<span class="status-badge ${statusClass}">
|
|
${statusText}
|
|
</span>
|
|
</td>
|
|
<td class="fund-actions-cell">
|
|
<div class="action-buttons">
|
|
${actionButtons}
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
`;
|
|
});
|
|
|
|
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 = `
|
|
<tr>
|
|
<td colspan="5" style="text-align: center; padding: 40px; color: var(--gray);">
|
|
<i class="fas fa-inbox" style="font-size: 3rem; margin-bottom: 10px; display: block; opacity: 0.5;"></i>
|
|
暂无基金数据,点击"添加基金"开始配置
|
|
</td>
|
|
</tr>
|
|
`;
|
|
return;
|
|
}
|
|
|
|
let html = '';
|
|
fundsData.forEach((fund, index) => {
|
|
const channelName = getChannelName(fund.channel);
|
|
const channelClass = getChannelClass(fund.channel);
|
|
|
|
html += `
|
|
<tr>
|
|
<td><strong>${fund.fund_code}</strong></td>
|
|
<td>${fund.name || '加载中...'}</td>
|
|
<td>
|
|
<span class="channel-badge ${channelClass}">
|
|
${getChannelIcon(fund.channel)} ${channelName}
|
|
</span>
|
|
</td>
|
|
<td>${parseFloat(fund.investment).toFixed(2)}</td>
|
|
<td>
|
|
<div class="action-buttons">
|
|
<button class="btn btn-outline btn-sm" onclick="editFund(${index})">
|
|
<i class="fas fa-edit"></i> 编辑
|
|
</button>
|
|
<button class="btn btn-danger btn-sm" onclick="deleteFund(${index})">
|
|
<i class="fas fa-trash"></i> 删除
|
|
</button>
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
`;
|
|
});
|
|
|
|
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 = '<option value="">请选择基金</option>';
|
|
|
|
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 = `
|
|
<div style="text-align: center; padding: 40px; color: var(--gray);">
|
|
<i class="fas fa-exclamation-triangle"></i> 加载操作日志失败: ${error.message}
|
|
</div>
|
|
`;
|
|
}
|
|
}
|
|
|
|
// 渲染操作日志
|
|
function renderOperationLog(operations) {
|
|
const container = document.getElementById('operationLogContent');
|
|
|
|
if (operations.length === 0) {
|
|
container.innerHTML = `
|
|
<div style="text-align: center; padding: 40px; color: var(--gray);">
|
|
<i class="fas fa-inbox"></i> 暂无操作记录
|
|
</div>
|
|
`;
|
|
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 += `
|
|
<div class="operation-item">
|
|
<div class="operation-details">
|
|
<span class="operation-type ${typeClass}">${typeText}</span>
|
|
<strong>${operation.fund_code}</strong>
|
|
${operation.channel ? ` - ${channelName}` : ''}
|
|
${operation.investment ? ` - ${operation.investment}元` : ''}
|
|
${operation.details ? ` - ${operation.details}` : ''}
|
|
</div>
|
|
<div class="operation-time">${formatTime(operation.date)}</div>
|
|
</div>
|
|
`;
|
|
});
|
|
|
|
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 = `
|
|
<tr>
|
|
<td colspan="5" style="text-align: center; padding: 40px; color: var(--gray);">
|
|
<div style="display: inline-block; width: 20px; height: 20px; border: 2px solid #f3f3f3; border-top: 2px solid var(--primary); border-radius: 50%; animation: spin 1s linear infinite;"></div>
|
|
加载中...
|
|
</td>
|
|
</tr>
|
|
`;
|
|
}
|
|
|
|
// 格式化时间
|
|
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();
|
|
}
|
|
} |