/* chart_view.js — split from chart.js */ function updateChart() { // 只显示旋转加载图标 $('#refreshLoadingSpinner').show(); // 获取参数 const dataSource = $('#dataSource').val() || 'crypto'; let symbol; if (dataSource === 'crypto') { symbol = $('#symbol').val() || 'BTC/USDT:USDT'; } else { symbol = $('#astockSymbol').val() || '000001'; } const timeframe = $('#timeframe').val() || window.DEFAULT_MAIN_TIMEFRAME || '5m'; const timezone = $('#timezone').val() || 'Asia/Shanghai'; const elementTimeframe = $('#elementTimeframe').val() || window.DEFAULT_ELEMENT_TIMEFRAME || '1m'; const subSubTimeframe = $('#subSubTimeframe').val() || ''; // 确保时区参数有效 console.log('更新图表使用时区:', timezone); console.log('数据源:', dataSource, '交易对/股票:', symbol); // 如果symbol为空,不发送请求 if (!symbol) { console.error('交易对/股票代码不能为空'); $('#refreshLoadingSpinner').hide(); return; } console.log(`更新图表: symbol=${symbol}, timeframe=${timeframe}, elementTimeframe=${elementTimeframe}, timezone=${timezone}`); // 获取开始和结束时间(如果已设置) let startTimeMs = null; let endTimeMs = null; if ($('#start_time').val()) { startTimeMs = new Date($('#start_time').val()).getTime(); } if ($('#end_time').val()) { endTimeMs = new Date($('#end_time').val()).getTime(); } // 发送请求 const requestId = ++lastRequestId; // 标记本次请求 $.ajax({ url: '/api/analyze', data: { symbol: symbol, timeframe: timeframe, timezone: timezone, element_timeframe: elementTimeframe, sub_sub_timeframe: subSubTimeframe || undefined, start_time: startTimeMs, end_time: endTimeMs, elements_only: false, zone_kl_lines: parseInt($('#zoneKlLines').val()) || 1000, include_structure_zones: $('#showMainStructureZone').is(':checked') ? 1 : 0 }, success: function(data) { // 隐藏加载图标 $('#refreshLoadingSpinner').hide(); // 忽略过期响应 if (requestId !== lastRequestId) { return; } // 保存当前数据 if (currentData) { // 覆盖前断开旧引用,帮助GC尽快回收 delete currentData.original_kline_data; delete currentData.original_macd; } currentData = data; refreshChart(data); }, error: function(jqXHR, textStatus, errorThrown) { // 隐藏加载图标 $('#refreshLoadingSpinner').hide(); // 显示错误信息 console.error('加载数据失败:', errorThrown); alert('加载数据失败: ' + (jqXHR.responseJSON?.error || errorThrown)); } }); } function captureChartViewState(chart) { if (!chart || !chart.timeScale) return null; const ts = chart.timeScale(); const tsOptions = ts.options ? ts.options() : {}; return { barSpacing: tsOptions.barSpacing, rightOffset: tsOptions.rightOffset, scrollPosition: ts.scrollPosition ? ts.scrollPosition() : null, visibleRange: ts.getVisibleRange ? ts.getVisibleRange() : null, logicalRange: ts.getVisibleLogicalRange ? ts.getVisibleLogicalRange() : null }; } function restoreChartViewState(charts, viewState) { if (!viewState || !Array.isArray(charts) || charts.length === 0) return; const validCharts = charts.filter(c => c && c.timeScale); if (validCharts.length === 0) return; validCharts.forEach(c => { try { const optionsPatch = {}; if (typeof viewState.barSpacing === 'number') optionsPatch.barSpacing = viewState.barSpacing; if (typeof viewState.rightOffset === 'number') optionsPatch.rightOffset = viewState.rightOffset; if (Object.keys(optionsPatch).length) { c.timeScale().applyOptions(optionsPatch); } } catch (e) {} }); let restored = false; // 优先按逻辑范围恢复(对新数据更稳健) if (viewState.logicalRange && viewState.logicalRange.from !== undefined && viewState.logicalRange.to !== undefined) { validCharts.forEach(c => { try { c.timeScale().setVisibleLogicalRange(viewState.logicalRange); restored = true; } catch (e) {} }); } // 逻辑范围失败时,回退到时间可见范围 if (!restored && viewState.visibleRange && viewState.visibleRange.from !== undefined && viewState.visibleRange.to !== undefined) { validCharts.forEach(c => { try { c.timeScale().setVisibleRange(viewState.visibleRange); restored = true; } catch (e) {} }); } // 最后回退到滚动位置 if (!restored && typeof viewState.scrollPosition === 'number') { validCharts.forEach(c => { try { c.timeScale().scrollToPosition(viewState.scrollPosition, false); } catch (e) {} }); } } // 初始化图表