fix: 增加批量导出功能

master^2
Lxy 1 month ago
parent 693a359010
commit 0ade46805a

@ -936,6 +936,50 @@
</div> </div>
<div id="queryResult" class="hidden" style="margin-top: 20px;"></div> <div id="queryResult" class="hidden" style="margin-top: 20px;"></div>
</div> </div>
<div class="card">
<div class="card-header">
<div>
<div class="card-title">批量导出数据</div>
<div class="card-subtitle">导出所有配置品种的K线数据</div>
</div>
</div>
<div class="form-row">
<div class="form-group">
<label class="form-label">数据类型</label>
<select class="form-select" id="batchExportDataType">
<option value="futures">期货</option>
<option value="stock">股票</option>
</select>
</div>
<div class="form-group">
<label class="form-label">周期</label>
<select class="form-select" id="batchExportPeriod">
<option value="">全部周期</option>
<option value="5min">5分钟</option>
<option value="15min">15分钟</option>
<option value="30min">30分钟</option>
<option value="60min">60分钟</option>
<option value="daily">日线</option>
</select>
</div>
<div class="form-group">
<label class="form-label">结束日期</label>
<input type="date" class="form-input" id="batchExportEndTime">
</div>
<div class="form-group">
<button class="btn btn-success" onclick="batchExportData()" style="height: 42px;" id="btnBatchExport">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg>
批量导出
</button>
</div>
</div>
<div id="batchExportProgress" class="hidden" style="margin-top: 16px;">
<div class="progress-bar"><div class="progress-fill" id="batchExportProgressFill" style="width: 0%"></div></div>
<p id="batchExportProgressText" style="text-align: center; color: var(--gray-500); font-size: 14px; margin-top: 8px;"></p>
</div>
<div id="batchExportResult" class="hidden" style="margin-top: 16px;"></div>
</div>
</div> </div>
<!-- Tasks --> <!-- Tasks -->
@ -1992,6 +2036,163 @@
showToast(`已导出 ${periodCount} 个周期数据`, 'success'); showToast(`已导出 ${periodCount} 个周期数据`, 'success');
} }
// 批量导出数据
async function batchExportData() {
const dataType = document.getElementById('batchExportDataType').value;
const period = document.getElementById('batchExportPeriod').value;
const endDate = document.getElementById('batchExportEndTime').value;
// 获取所有配置的品种
try {
const res = await fetch(`${API}/config/symbols`);
const data = await res.json();
if (!data.symbols || data.symbols.length === 0) {
showToast('暂无可导出的品种,请先添加品种配置', 'error');
return;
}
// 过滤指定数据类型的品种
const symbols = data.symbols.filter(s => s.data_type === dataType);
if (symbols.length === 0) {
showToast(`暂无${dataType === 'futures' ? '期货' : '股票'}品种配置`, 'error');
return;
}
const symbolList = symbols.map(s => s.symbol);
// 显示进度条
const progressContainer = document.getElementById('batchExportProgress');
const progressFill = document.getElementById('batchExportProgressFill');
const progressText = document.getElementById('batchExportProgressText');
const resultContainer = document.getElementById('batchExportResult');
progressContainer.classList.remove('hidden');
resultContainer.classList.add('hidden');
const endDateDisplay = endDate || new Date().toISOString().slice(0, 10);
addLog(`开始批量导出: ${dataType === 'futures' ? '期货' : '股票'} ${symbolList.length} 个品种, 结束日期: ${endDateDisplay}`);
let successCount = 0;
let failedCount = 0;
let totalCandles = 0;
const failedSymbols = [];
// 逐个获取并导出数据
for (let i = 0; i < symbolList.length; i++) {
const symbol = symbolList[i];
const progress = ((i + 1) / symbolList.length * 100).toFixed(0);
progressFill.style.width = `${progress}%`;
progressText.textContent = `正在导出: ${symbol} (${i + 1}/${symbolList.length})`;
try {
// 构建查询参数
const params = new URLSearchParams();
if (period) params.append('period', period);
if (endDate) {
const endDateTime = new Date(endDate);
endDateTime.setHours(23, 59, 59, 999);
params.append('end_time', endDateTime.toISOString());
}
const queryString = params.toString();
const url = `${API}/data/latest/${symbol}${queryString ? '?' + queryString : ''}`;
const fetchRes = await fetch(url);
if (!fetchRes.ok) {
failedCount++;
failedSymbols.push(symbol);
continue;
}
const fetchData = await fetchRes.json();
if (!fetchData.timeframes || fetchData.timeframes.length === 0) {
failedCount++;
failedSymbols.push(symbol);
continue;
}
// 导出单个品种数据
const timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19);
const filename = `${symbol}_${period || '多周期'}_截至${endDateDisplay}_${timestamp}.json`;
const exportObj = {
symbol: fetchData.symbol || symbol,
type: fetchData.type || dataType,
period: period || 'all',
end_date: endDateDisplay,
current_price: fetchData.current_price,
timestamp: fetchData.timestamp || new Date().toISOString(),
timeframes: {}
};
fetchData.timeframes.forEach(tf => {
exportObj.timeframes[tf.period] = tf.candles || [];
});
const jsonStr = JSON.stringify(exportObj, null, 2);
const blob = new Blob([jsonStr], { type: 'application/json' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = filename;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
const candleCount = fetchData.timeframes.reduce((sum, tf) => sum + (tf.candles ? tf.candles.length : 0), 0);
successCount++;
totalCandles += candleCount;
// 添加小延迟避免浏览器阻止多次下载
await new Promise(resolve => setTimeout(resolve, 200));
} catch (e) {
console.error(`导出 ${symbol} 失败:`, e);
failedCount++;
failedSymbols.push(symbol);
}
}
// 显示结果
progressContainer.classList.add('hidden');
resultContainer.classList.remove('hidden');
let resultHtml = `
<div style="padding: 16px; background: var(--success-light); border-radius: 8px; border-left: 4px solid var(--success);">
<h4 style="color: var(--success); margin-bottom: 12px;">批量导出完成</h4>
<div style="display: grid; gap: 8px;">
<div>成功: <strong>${successCount}</strong> 个品种</div>
<div>失败: <strong>${failedCount}</strong> 个品种</div>
<div>总计: <strong>${totalCandles}</strong> 条K线</div>
</div>
`;
if (failedSymbols.length > 0) {
resultHtml += `
<div style="margin-top: 12px; padding: 12px; background: white; border-radius: 6px;">
<div style="color: var(--danger); font-weight: 500; margin-bottom: 8px;">失败的品种:</div>
<div style="color: var(--gray-600); font-size: 13px;">${failedSymbols.join(', ')}</div>
</div>
`;
}
resultHtml += '</div>';
resultContainer.innerHTML = resultHtml;
addLog(`批量导出完成: 成功 ${successCount}, 失败 ${failedCount}, 总计 ${totalCandles} 条K线`, successCount > 0 ? 'success' : 'error');
showToast(`批量导出完成: ${successCount} 个品种成功`, successCount > 0 ? 'success' : 'error');
} catch (e) {
console.error('批量导出失败:', e);
showToast(`批量导出失败: ${e.message}`, 'error');
addLog(`批量导出失败: ${e.message}`, 'error');
}
}
function renderKlineChart(timeframe, symbol) { function renderKlineChart(timeframe, symbol) {
const resultContainer = document.getElementById('queryResult'); const resultContainer = document.getElementById('queryResult');
@ -3001,6 +3202,7 @@
function initDefaultDate() { function initDefaultDate() {
const today = new Date().toISOString().slice(0, 10); const today = new Date().toISOString().slice(0, 10);
document.getElementById('queryEndTime').value = today; document.getElementById('queryEndTime').value = today;
document.getElementById('batchExportEndTime').value = today;
} }
// 初始化 // 初始化

Binary file not shown.

Binary file not shown.
Loading…
Cancel
Save