/** * 沪深港通资金流向监控JS */ document.addEventListener('DOMContentLoaded', function() { // 初始化图表 let northChart = null; let southChart = null; let rzrqChart = null; // 融资融券图表实例 // 当前显示的融资融券数据系列 let currentMetric = 'total_rzrq_balance'; // 融资融券数据 let rzrqData = null; // 融资融券图表相关功能 let rzrqIndexSelector = null; let rzrqChartData = null; // 用于存储融资融券图表的原始数据 // 初始化图表函数,确保DOM元素存在 function initCharts() { try { const northChartDom = document.getElementById('northChart'); const southChartDom = document.getElementById('southChart'); const rzrqChartDom = document.getElementById('rzrqChart'); if (northChartDom && !northChart) { try { northChart = echarts.init(northChartDom); console.log('北向资金图表初始化成功'); } catch (e) { console.error('北向资金图表初始化失败:', e); } } if (southChartDom && !southChart) { try { southChart = echarts.init(southChartDom); console.log('南向资金图表初始化成功'); } catch (e) { console.error('南向资金图表初始化失败:', e); } } if (rzrqChartDom && !rzrqChart) { try { rzrqChart = echarts.init(rzrqChartDom); console.log('融资融券图表初始化成功'); } catch (e) { console.error('融资融券图表初始化失败:', e); } } return northChart && southChart && rzrqChart; } catch (e) { console.error('图表初始化过程中发生错误:', e); return false; } } // 确保DOM加载完毕后初始化图表 // 使用setTimeout确保DOM元素完全渲染 setTimeout(function() { if (!initCharts()) { console.log('首次初始化图表不成功,将在数据加载时再次尝试'); } // 开始加载数据 loadData(); // 加载融资融券数据 initRzrqChart(); // 检查是否在交易时段,只有在交易时段才设置自动刷新 if (isWithinTradingHours()) { console.log('当前处于交易时段,启用自动刷新'); // 设置自动刷新 (每分钟刷新一次) window.refreshInterval = setInterval(loadData, 60000); } else { console.log('当前不在交易时段,不启用自动刷新'); } }, 100); /** * 判断当前时间是否在交易时段内 (9:20-16:00) */ function isWithinTradingHours() { const now = new Date(); const hours = now.getHours(); const minutes = now.getMinutes(); const dayOfWeek = now.getDay(); // 0是周日,6是周六 // 如果是周末,不在交易时段 if (dayOfWeek === 0 || dayOfWeek === 6) { return false; } // 计算当前时间的分钟数 const totalMinutes = hours * 60 + minutes; // 交易时段开始时间:9:20 (9*60 + 20 = 560分钟) const tradingStartMinutes = 9 * 60 + 20; // 交易时段结束时间:16:00 (16*60 = 960分钟) const tradingEndMinutes = 16 * 60; // 判断当前时间是否在交易时段内 return totalMinutes >= tradingStartMinutes && totalMinutes <= tradingEndMinutes; } // 设置图表自适应 window.addEventListener('resize', function() { if (northChart) { try { northChart.resize(); } catch (e) { console.error('北向资金图表调整大小失败:', e); } } if (southChart) { try { southChart.resize(); } catch (e) { console.error('南向资金图表调整大小失败:', e); } } if (rzrqChart) { try { rzrqChart.resize(); } catch (e) { console.error('融资融券图表调整大小失败:', e); } } }); // 刷新按钮事件 const refreshBtn = document.getElementById('refreshBtn'); if (refreshBtn) { refreshBtn.addEventListener('click', function() { loadData(); // 如果当前不在交易时段但点击了刷新按钮,显示提示信息 if (!isWithinTradingHours()) { showMessage('当前不在交易时段(9:20-16:00),数据可能不会更新'); } }); } // 融资融券刷新按钮事件 const rzrqRefreshBtn = document.getElementById('rzrqRefreshBtn'); if (rzrqRefreshBtn) { rzrqRefreshBtn.addEventListener('click', function() { if (rzrqChart) { rzrqChart.showLoading(); loadRzrqData(); } }); } // 融资融券指标切换按钮点击事件 const metricButtons = document.querySelectorAll('.btn-group button[data-metric]'); if (metricButtons && metricButtons.length > 0) { metricButtons.forEach(button => { button.addEventListener('click', function() { metricButtons.forEach(btn => btn.classList.remove('active')); this.classList.add('active'); currentMetric = this.getAttribute('data-metric'); updateRzrqChart(); }); }); } /** * 显示消息提示 */ function showMessage(message) { console.log(message); // 可以在这里添加Toast或其他UI提示 // 例如: const updateTimeElem = document.getElementById('updateTime'); if (updateTimeElem) { const originalText = updateTimeElem.textContent; updateTimeElem.textContent = message; setTimeout(() => { updateTimeElem.textContent = originalText; }, 3000); } } /** * 加载北向和南向资金数据 */ function loadData() { // 确保图表已经初始化 initCharts(); // 显示加载中状态 showLoading(true); console.log('开始加载北向资金数据...'); // 获取北向资金数据 (从香港流入A股的资金) fetch('/api/hsgt/northbound') .then(response => { if (!response.ok) { return response.json().then(data => { throw new Error(data.message || '获取北向资金数据失败'); }); } return response.json(); }) .then(data => { console.log('北向资金数据获取成功:', data.status); if (data.status === 'success' && data.data && data.data.success) { // 检查数据结构 console.log('北向资金数据结构:', { hasTimeArray: Array.isArray(data.data.times), timeLength: data.data.times ? data.data.times.length : 0, hasDataObj: !!data.data.data, totalLength: data.data.data && data.data.data.total ? data.data.data.total.length : 0, hasCurrent: !!data.data.current }); // 渲染北向资金数据 renderNorthboundData(data.data); } else { const errorMessage = data.data && data.data.message ? data.data.message : (data.message || '北向资金数据格式错误'); showError('北向资金数据获取失败: ' + errorMessage); // 显示空数据状态 renderEmptyNorthboundChart(); } }) .catch(error => { showError('请求北向资金数据错误: ' + error.message); console.error('北向资金数据请求异常:', error); // 显示空数据状态 renderEmptyNorthboundChart(); }) .finally(() => { // 无论成功失败,都开始请求南向资金数据 loadSouthboundData(); }); } /** * 渲染空的北向资金图表 */ function renderEmptyNorthboundChart() { if (!northChart) return; // 更新统计卡片为暂无数据 updateStatCard('northTotal', null, '暂无数据'); updateStatCard('northSH', null, '暂无数据'); updateStatCard('northSZ', null, '暂无数据'); // 初始化一个简单的图表提示暂无数据 northChart.setOption({ title: { text: '陆股通资金流向(北向)', left: 'center', top: 0 }, graphic: { elements: [{ type: 'text', left: 'center', top: 'middle', style: { text: '暂无数据', fontSize: 20, fill: '#999' } }] } }, true); } /** * 加载南向资金数据 */ function loadSouthboundData() { console.log('开始加载南向资金数据...'); fetch('/api/hsgt/southbound') .then(response => { if (!response.ok) { return response.json().then(data => { throw new Error(data.message || '获取南向资金数据失败'); }); } return response.json(); }) .then(data => { console.log('南向资金数据获取成功:', data.status); if (data.status === 'success' && data.data && data.data.success) { // 检查数据结构 console.log('南向资金数据结构:', { hasTimeArray: Array.isArray(data.data.times), timeLength: data.data.times ? data.data.times.length : 0, hasDataObj: !!data.data.data, totalLength: data.data.data && data.data.data.total ? data.data.data.total.length : 0, hasCurrent: !!data.data.current }); // 渲染南向资金数据 renderSouthboundData(data.data); } else { const errorMessage = data.data && data.data.message ? data.data.message : (data.message || '南向资金数据格式错误'); showError('南向资金数据获取失败: ' + errorMessage); // 显示空数据状态 renderEmptySouthboundChart(); } }) .catch(error => { showError('请求南向资金数据错误: ' + error.message); console.error('南向资金数据请求异常:', error); // 显示空数据状态 renderEmptySouthboundChart(); }) .finally(() => { // 隐藏加载中状态 showLoading(false); }); } /** * 渲染空的南向资金图表 */ function renderEmptySouthboundChart() { if (!southChart) return; // 更新统计卡片为暂无数据 updateStatCard('southTotal', null, '暂无数据'); updateStatCard('southHKSH', null, '暂无数据'); updateStatCard('southHKSZ', null, '暂无数据'); // 初始化一个简单的图表提示暂无数据 southChart.setOption({ title: { text: '港股通资金流向(南向)', left: 'center', top: 0 }, graphic: { elements: [{ type: 'text', left: 'center', top: 'middle', style: { text: '暂无数据', fontSize: 20, fill: '#999' } }] } }, true); } /** * 渲染北向资金数据 (从香港流入A股的资金) */ function renderNorthboundData(data) { if (!northChart) return; try { // 验证数据有效性 if (!data || !data.data || !data.times || !data.current) { renderEmptyNorthboundChart(); return; } // 准备简化的图表数据 const times = data.times; const seriesData = []; // 处理北向资金总量 if (data.data.total && Array.isArray(data.data.total) && data.data.total.length > 0) { seriesData.push({ name: '北向资金', type: 'line', data: data.data.total, lineStyle: {width: 3} }); } // 处理沪股通 if (data.data.sh && Array.isArray(data.data.sh) && data.data.sh.length > 0) { seriesData.push({ name: '沪股通', type: 'line', data: data.data.sh }); } // 处理深股通 if (data.data.sz && Array.isArray(data.data.sz) && data.data.sz.length > 0) { seriesData.push({ name: '深股通', type: 'line', data: data.data.sz }); } // 如果没有有效的系列数据,显示空图表 if (seriesData.length === 0) { renderEmptyNorthboundChart(); return; } // 更新统计卡片 updateStatCard('northTotal', data.current.total); updateStatCard('northSH', data.current.sh); updateStatCard('northSZ', data.current.sz); // 更新时间 const updateTimeElem = document.getElementById('updateTime'); if (updateTimeElem) { let timeText = '最后更新时间: ' + data.update_time; // 如果不在交易时段,添加提示 if (!isWithinTradingHours()) { timeText += ' (非交易时段)'; } updateTimeElem.textContent = timeText; } // 创建简单的图表配置 const option = { title: { text: '陆股通资金流向(北向)', left: 'center', top: 0 }, tooltip: { trigger: 'axis', formatter: function(params) { if (!params || params.length === 0) return ''; const time = params[0].axisValue; let result = `${time}
`; params.forEach(param => { if (param && param.value !== undefined) { const value = param.value; const color = param.color; const marker = ``; let valueText = value !== null ? value.toFixed(2) : 'N/A'; if (value > 0) valueText = '+' + valueText; result += `${marker}${param.seriesName}: ${valueText} 亿
`; } }); return result; } }, legend: { data: seriesData.map(item => item.name), top: 30 }, grid: { left: '3%', right: '4%', bottom: '3%', top: 80, containLabel: true }, xAxis: { type: 'category', boundaryGap: false, data: times, axisLabel: { rotate: 45 } }, yAxis: { type: 'value', name: '净流入金额(亿元)', axisLabel: { formatter: function(value) { return value.toFixed(1); } } }, series: seriesData, color: ['#ec0000', '#1e88e5', '#ff9800'] }; // 安全地设置图表 if (northChart && northChart.setOption) { northChart.setOption(option, true); } else { console.error('北向资金图表实例无效'); const northChartDom = document.getElementById('northChart'); if (northChartDom) { northChart = echarts.init(northChartDom); northChart.setOption(option, true); } } } catch (error) { console.error('设置北向资金图表选项时出错:', error); renderEmptyNorthboundChart(); } } /** * 渲染南向资金数据 (从内地流入港股的资金) */ function renderSouthboundData(data) { if (!southChart) return; try { // 验证数据有效性 if (!data || !data.data || !data.times || !data.current) { renderEmptySouthboundChart(); return; } // 准备简化的图表数据 const times = data.times; const seriesData = []; // 处理南向资金总量 if (data.data.total && Array.isArray(data.data.total) && data.data.total.length > 0) { seriesData.push({ name: '南向资金', type: 'line', data: data.data.total, lineStyle: {width: 3} }); } // 处理沪市港股通 if (data.data.hk_sh && Array.isArray(data.data.hk_sh) && data.data.hk_sh.length > 0) { seriesData.push({ name: '沪市港股通', type: 'line', data: data.data.hk_sh }); } // 处理深市港股通 if (data.data.hk_sz && Array.isArray(data.data.hk_sz) && data.data.hk_sz.length > 0) { seriesData.push({ name: '深市港股通', type: 'line', data: data.data.hk_sz }); } // 如果没有有效的系列数据,显示空图表 if (seriesData.length === 0) { renderEmptySouthboundChart(); return; } // 更新统计卡片 updateStatCard('southTotal', data.current.total); updateStatCard('southHKSH', data.current.hk_sh); updateStatCard('southHKSZ', data.current.hk_sz); // 创建简单的图表配置 const option = { title: { text: '港股通资金流向(南向)', left: 'center', top: 0 }, tooltip: { trigger: 'axis', formatter: function(params) { if (!params || params.length === 0) return ''; const time = params[0].axisValue; let result = `${time}
`; params.forEach(param => { if (param && param.value !== undefined) { const value = param.value; const color = param.color; const marker = ``; let valueText = value !== null ? value.toFixed(2) : 'N/A'; if (value > 0) valueText = '+' + valueText; result += `${marker}${param.seriesName}: ${valueText} 亿
`; } }); return result; } }, legend: { data: seriesData.map(item => item.name), top: 30 }, grid: { left: '3%', right: '4%', bottom: '3%', top: 80, containLabel: true }, xAxis: { type: 'category', boundaryGap: false, data: times, axisLabel: { rotate: 45 } }, yAxis: { type: 'value', name: '净流入金额(亿元)', axisLabel: { formatter: function(value) { return value.toFixed(1); } } }, series: seriesData, color: ['#ec0000', '#1e88e5', '#ff9800'] }; // 安全地设置图表 if (southChart && southChart.setOption) { southChart.setOption(option, true); } else { console.error('南向资金图表实例无效'); const southChartDom = document.getElementById('southChart'); if (southChartDom) { southChart = echarts.init(southChartDom); southChart.setOption(option, true); } } } catch (error) { console.error('设置南向资金图表选项时出错:', error); renderEmptySouthboundChart(); } } /** * 更新统计卡片 */ function updateStatCard(id, value, customText) { const statCard = document.getElementById(id); if (!statCard) return; const statValue = statCard.querySelector('.stat-value'); if (!statValue) return; if (customText) { statValue.textContent = customText; statValue.classList.remove('money-inflow', 'money-outflow'); return; } if (value === null || value === undefined) { statValue.textContent = '--'; statValue.classList.remove('money-inflow', 'money-outflow'); return; } // 格式化显示,保留两位小数 const formattedValue = value.toFixed(2); // 根据正负值设置样式 if (value > 0) { statValue.textContent = '+' + formattedValue; statValue.classList.add('money-inflow'); statValue.classList.remove('money-outflow'); } else if (value < 0) { statValue.textContent = formattedValue; statValue.classList.add('money-outflow'); statValue.classList.remove('money-inflow'); } else { statValue.textContent = formattedValue; statValue.classList.remove('money-inflow', 'money-outflow'); } } /** * 显示错误信息 */ function showError(message) { console.error(message); // 可以添加Toast或其他UI提示 } /** * 显示/隐藏加载状态 */ function showLoading(isLoading) { if (!northChart || !southChart) return; // 实现加载中状态显示 if (isLoading) { northChart.showLoading({text: '加载中...'}); southChart.showLoading({text: '加载中...'}); } else { northChart.hideLoading(); southChart.hideLoading(); } } // ============ 融资融券图表相关功能 ============ /** * 初始化融资融券图表 */ function initRzrqChart() { if (rzrqChart) { rzrqChart.dispose(); } rzrqChart = echarts.init(document.getElementById('rzrqChart')); // 设置图表加载中状态 if (rzrqChart) { rzrqChart.showLoading(); // 加载数据 loadRzrqData(); } } /** * 加载融资融券数据 */ function loadRzrqData() { $.ajax({ url: '/api/rzrq/chart_data', type: 'GET', data: { days: 90 // 默认加载90天数据 }, dataType: 'json', success: function(response) { if (response.status === 'success') { rzrqData = response.data; rzrqChartData = response.data; // 用于存储融资融券图表的原始数据 updateRzrqChart(); $('#rzrqUpdateTime').text('数据更新时间: ' + rzrqData.last_update); // 初始化融资融券图表的索引选择器 if (!rzrqIndexSelector) { initRzrqIndexSelector(); } } else { rzrqChart.hideLoading(); rzrqChart.setOption({ title: { text: '数据加载失败', textStyle: { color: '#999', fontSize: 14 }, left: 'center', top: 'center' } }); } }, error: function(xhr, status, error) { rzrqChart.hideLoading(); rzrqChart.setOption({ title: { text: '数据加载失败: ' + error, textStyle: { color: '#999', fontSize: 14 }, left: 'center', top: 'center' } }); } }); } /** * 初始化融资融券图表的索引选择器 */ function initRzrqIndexSelector() { rzrqIndexSelector = new IndexSelector('rzrqChart', { // 获取图表当前显示的日期范围 getDateRange: function() { // 如果有rzrq图表的日期数据,返回第一个和最后一个日期 if (rzrqChartData && rzrqChartData.dates && rzrqChartData.dates.length) { return { startDate: rzrqChartData.dates[0], endDate: rzrqChartData.dates[rzrqChartData.dates.length - 1] }; } return { startDate: null, endDate: null }; }, // 指数数据更新时的回调 onChange: function(selectedIndices) { updateRzrqChartWithIndices(selectedIndices); } }); } /** * 将数据对齐到日期范围 */ function alignDataToDateRange(sourceDates, sourceValues, targetDates) { const result = new Array(targetDates.length).fill(null); const dateMap = {}; // 创建源数据日期到值的映射 sourceDates.forEach((date, index) => { dateMap[date] = sourceValues[index]; }); // 映射到目标日期 targetDates.forEach((date, index) => { if (dateMap[date] !== undefined) { result[index] = dateMap[date]; } }); return result; } /** * 更新融资融券图表,添加指数数据 */ function updateRzrqChartWithIndices(indices) { if (!rzrqChart) return; // 获取当前图表配置 const option = rzrqChart.getOption(); // 保留原始系列数据(融资融券数据) const originalSeries = option.series.filter(s => s.name.indexOf('指数') === -1); // 清除所有指数系列 option.series = [...originalSeries]; // 如果没有选择指数,则移除右侧Y轴 if (indices.length === 0) { option.yAxis = option.yAxis.filter(axis => axis.name !== '指数值'); rzrqChart.setOption(option, true); return; } // 计算所有指数数据的最小值和最大值 let allValues = []; indices.forEach(index => { if (index.data && index.data.values) { // 过滤掉null和undefined值 const validValues = index.data.values.filter(v => v !== null && v !== undefined); allValues = allValues.concat(validValues); } }); // 计算数据范围 let minValue = Math.min(...allValues); let maxValue = Math.max(...allValues); // 增加一定的边距,使图表更美观 const range = maxValue - minValue; const padding = range * 0.1; // 上下各留10%的边距 minValue = minValue - padding; maxValue = maxValue + padding; minValue = Math.round(minValue * 10) / 10; maxValue = Math.round(maxValue * 10) / 10; // 添加指数系列 indices.forEach(index => { if (!index.data || !index.data.dates) return; // 将指数数据对齐到融资融券数据的日期范围 const alignedData = alignDataToDateRange(index.data.dates, index.data.values, option.xAxis[0].data); // 创建新的Y轴用于指数 if (!option.yAxis.some(axis => axis.name === '指数值')) { option.yAxis.push({ name: '指数值', type: 'value', position: 'right', min: minValue, // 使用计算出的最小值 max: maxValue, // 使用计算出的最大值 splitLine: { show: false }, axisLabel: { formatter: '{value}' } }); } else { // 如果已存在指数Y轴,更新其范围 const indexAxis = option.yAxis.find(axis => axis.name === '指数值'); if (indexAxis) { indexAxis.min = minValue; indexAxis.max = maxValue; } } // 添加指数系列 option.series.push({ name: `${index.name}`, // 移除"指数"后缀,避免在tooltip中显示为"上证指数指数" type: 'line', yAxisIndex: 1, // 使用第二个Y轴 data: alignedData, symbol: 'none', smooth: true, lineStyle: { width: 2, color: index.color }, itemStyle: { color: index.color }, // 标记这是指数数据 isIndex: true }); }); // 更新图例 option.legend = { data: [ ...originalSeries.map(s => s.name), ...indices.map(i => i.name) // 使用原始指数名称 ], selected: { ...option.legend?.selected || {}, ...indices.reduce((acc, index) => { acc[index.name] = true; // 使用原始指数名称 return acc; }, {}) }, top: 40 // 将图例下移,避免与标题重叠 }; // 应用更新 rzrqChart.setOption(option, true); } /** * 更新融资融券图表 */ function updateRzrqChart() { if (!rzrqData || !rzrqData.success) { return; } // 查找当前指标的数据 let currentSeries = rzrqData.series.find(s => s.name === getMetricName(currentMetric)); // 如果未找到,使用第一个系列 if (!currentSeries && rzrqData.series.length > 0) { currentSeries = rzrqData.series[0]; currentMetric = getMetricKey(currentSeries.name); } if (!currentSeries) { rzrqChart.hideLoading(); return; } // 计算数据的最小值和最大值,用于设置Y轴范围 const validData = currentSeries.data.filter(value => value !== null && value !== undefined); let min = Math.min(...validData); let max = Math.max(...validData); // 为了图表美观,给最小值和最大值增加一些间距 const range = max - min; min = min - range * 0.05; // 下方留5%的间距 max = max + range * 0.05; // 上方留5%的间距 // 设置图表选项 const option = { title: { text: currentSeries.name + '走势', left: 'center', top: 10 // 固定标题位置在顶部 }, tooltip: { trigger: 'axis', formatter: function(params) { let tooltip = params[0].axisValue + '
'; params.forEach(param => { // 判断是否为指数系列 const isIndexSeries = param.seriesIndex > 0 && param.seriesName.indexOf('融资') === -1 && param.seriesName.indexOf('融券') === -1; // 对于指数系列,不添加单位;对于融资融券系列,添加相应单位 tooltip += param.marker + ' ' + param.seriesName + ': ' + param.value + (isIndexSeries ? '' : (' ' + currentSeries.unit)) + '
'; }); return tooltip; } }, xAxis: { type: 'category', data: rzrqData.dates, axisLabel: { rotate: 45 }, axisLine: { lineStyle: { color: '#999' } } }, yAxis: { type: 'value', name: currentSeries.unit, min: min, // 设置Y轴最小值为数据的最小值 max: max, // 设置Y轴最大值为数据的最大值 nameTextStyle: { padding: [0, 30, 0, 0] }, axisLine: { show: true, lineStyle: { color: '#999' } }, splitLine: { lineStyle: { color: '#eee' } } }, dataZoom: [ { type: 'inside', start: 0, end: 100 }, { start: 0, end: 100 } ], series: [ { name: currentSeries.name, type: 'line', data: currentSeries.data, lineStyle: { width: 3 }, itemStyle: { color: '#1890ff' }, areaStyle: { color: { type: 'linear', x: 0, y: 0, x2: 0, y2: 1, colorStops: [ { offset: 0, color: 'rgba(24, 144, 255, 0.3)' }, { offset: 1, color: 'rgba(24, 144, 255, 0.1)' } ] } }, connectNulls: true } ], grid: { left: '3%', right: '4%', bottom: '15%', top: '70px', // 增加顶部空间,给标题和图例留出足够位置 containLabel: true }, legend: { top: 40, // 将图例放在标题下方 left: 'center' }, toolbox: { feature: { saveAsImage: {} } } }; rzrqChart.hideLoading(); rzrqChart.setOption(option); // 更新风险指标显示 updateRiskIndicators(); } /** * 更新风险指标显示 */ function updateRiskIndicators() { // 检查是否有风险指标数据 if (!rzrqData || !rzrqData.risk_indicators) { // 隐藏或显示无数据状态 console.log('无风险指标数据'); // 清理可能存在的旧数据 $('#summaryBalanceChangeDesc').text('--'); $('#summarySecuritiesChangeDesc').text('--'); $('#overallRiskLevel').text('N/A'); $('#overallRiskDesc').text('无法加载风险数据。'); $('#overallRiskAlert').removeClass('alert-danger alert-warning alert-success alert-info'); return; } const indicators = rzrqData.risk_indicators; // 辅助函数:格式化描述文本并设置 function formatAndSetDescription(targetSelector, descriptionText, defaultText) { const descElement = $(targetSelector); if (!descriptionText) { descElement.text(defaultText || '--'); return; } // 正则表达式匹配数字(包括正负号和小数点)和百分号 const percentageRegex = /([-+]?\d*\.?\d+)%/; const match = descriptionText.match(percentageRegex); if (match && match[1]) { const percentageValueStr = match[0]; // 例如 "+0.15%" 或 "-3.14%" const numericValue = parseFloat(match[1]); // 例如 0.15 或 -3.14 let colorClass = 'neutral'; if (numericValue > 0) { colorClass = 'positive'; } else if (numericValue < 0) { colorClass = 'negative'; } const styledDescription = descriptionText.replace( percentageRegex, `${percentageValueStr}` ); descElement.html(styledDescription); } else { descElement.text(descriptionText); // 没有百分比则直接显示 } } // 更新综合风险评估 if (indicators.overall_risk) { const overallRisk = indicators.overall_risk; const riskLevel = overallRisk.level; // 设置风险等级和描述 $('#overallRiskLevel').text(riskLevel); $('#overallRiskDesc').text(overallRisk.description); // 根据风险等级设置颜色 const alertElem = $('#overallRiskAlert'); alertElem.removeClass('alert-danger alert-warning alert-success alert-info'); if (riskLevel === '高') { alertElem.addClass('alert-danger'); } else if (riskLevel === '中') { alertElem.addClass('alert-warning'); } else if (riskLevel === '低') { alertElem.addClass('alert-success'); } else { alertElem.addClass('alert-info'); } } else { $('#overallRiskLevel').text('N/A'); $('#overallRiskDesc').text('综合风险数据缺失。'); $('#overallRiskAlert').removeClass('alert-danger alert-warning alert-success').addClass('alert-info'); } // 更新融资融券余额变化 - 新的汇总位置 if (indicators.balance_risk && indicators.balance_risk.description) { formatAndSetDescription('#summaryBalanceChangeDesc', indicators.balance_risk.description); } else { $('#summaryBalanceChangeDesc').text('融资融券余额变化数据: --'); } // 更新卡片内的详细信息(不含移动的描述) if (indicators.recent_balance_change && indicators.balance_risk) { const balanceChange = indicators.recent_balance_change; const balanceRisk = indicators.balance_risk; let rateText = balanceChange.rate > 0 ? '+' : ''; rateText += balanceChange.rate + '%'; $('#balanceChangeRate').text(rateText).removeClass('text-success text-danger').addClass(balanceChange.rate > 0 ? 'text-danger' : 'text-success'); $('#balanceRiskLevel').text(balanceRisk.level); setRiskLevelColor('#balanceRiskLevel', balanceRisk.level); // $('#balanceRiskDesc').text(balanceRisk.description); // 这行被移动了 } else { $('#balanceChangeRate').text('--'); $('#balanceRiskLevel').text('--'); } // 更新融券余额变化 - 新的汇总位置 if (indicators.securities_risk && indicators.securities_risk.description) { formatAndSetDescription('#summarySecuritiesChangeDesc', indicators.securities_risk.description); } else { $('#summarySecuritiesChangeDesc').text('融券余额变化数据: --'); } // 更新卡片内的详细信息(不含移动的描述) if (indicators.securities_balance_change && indicators.securities_risk) { const securitiesChange = indicators.securities_balance_change; const securitiesRisk = indicators.securities_risk; let rateText = securitiesChange.rate > 0 ? '+' : ''; rateText += securitiesChange.rate + '%'; $('#securitiesChangeRate').text(rateText).removeClass('text-success text-danger').addClass(securitiesChange.rate > 0 ? 'text-danger' : 'text-success'); $('#securitiesRiskLevel').text(securitiesRisk.level); setRiskLevelColor('#securitiesRiskLevel', securitiesRisk.level); // $('#securitiesRiskDesc').text(securitiesRisk.description); // 这行被移动了 } else { $('#securitiesChangeRate').text('--'); $('#securitiesRiskLevel').text('--'); } // 更新融资偿还比率 (这部分逻辑不变,仅为上下文) if (indicators.repay_buy_ratio && indicators.repay_risk) { const repayRatio = indicators.repay_buy_ratio; const repayRisk = indicators.repay_risk; $('#repayBuyRatio').text(repayRatio.value); if (repayRatio.value > 1.1) { $('#repayBuyRatio').removeClass('text-success').addClass('text-danger'); } else if (repayRatio.value < 0.9) { $('#repayBuyRatio').removeClass('text-danger').addClass('text-success'); } else { $('#repayBuyRatio').removeClass('text-danger text-success'); } $('#repayRiskLevel').text(repayRisk.level); setRiskLevelColor('#repayRiskLevel', repayRisk.level); $('#repayRiskDesc').text(repayRisk.description); // 这个描述保留在原位 } else { $('#repayBuyRatio').text('--'); $('#repayRiskLevel').text('--'); $('#repayRiskDesc').text('--'); } // 更新融资占比 (这部分逻辑不变,仅为上下文) if (indicators.financing_ratio) { $('#financingRatio').text(indicators.financing_ratio + '%'); if (indicators.financing_ratio_percentile !== undefined) { $('#financingRatioPercentile').text(indicators.financing_ratio_percentile + '%'); if (indicators.financing_ratio_percentile > 80) { $('#financingRatioPercentile').removeClass('text-success text-warning').addClass('text-danger'); } else if (indicators.financing_ratio_percentile > 50) { $('#financingRatioPercentile').removeClass('text-success text-danger').addClass('text-warning'); } else { $('#financingRatioPercentile').removeClass('text-danger text-warning').addClass('text-success'); } } else { $('#financingRatioPercentile').text('数据不足'); } } else { $('#financingRatio').text('--'); $('#financingRatioPercentile').text('--'); } } /** * 根据风险等级设置文本颜色 */ function setRiskLevelColor(selector, level) { const elem = $(selector); elem.removeClass('text-danger text-warning text-success text-info'); if (level === '高') { elem.addClass('text-danger'); } else if (level === '中') { elem.addClass('text-warning'); } else if (level === '低') { elem.addClass('text-success'); } else { elem.addClass('text-info'); } } /** * 根据数据系列键名获取显示名称 */ function getMetricName(metricKey) { const metricMap = { 'total_rzrq_balance': '融资融券余额合计', 'total_financing_buy': '融资买入额合计', 'total_financing_balance': '融资余额合计', 'financing_repayment': '融资偿还', 'securities_balance': '融券余额' }; return metricMap[metricKey] || metricKey; } /** * 根据显示名称获取数据系列键名 */ function getMetricKey(metricName) { const metricMap = { '融资融券余额合计': 'total_rzrq_balance', '融资买入额合计': 'total_financing_buy', '融资余额合计': 'total_financing_balance', '融资偿还': 'financing_repayment', '融券余额': 'securities_balance' }; return metricMap[metricName] || metricName; } });