You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

312 lines
11 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

/**
* 股票信息区块JavaScript
* 处理腾讯股票API调用和数据显示
*/
(function($) {
'use strict';
// 股票信息管理器
window.stockInfoManager = {
// 配置选项
config: {
apiUrl: 'https://sqt.gtimg.cn/q=',
fallbackApiUrl: 'https://qt.gtimg.cn/q=', // 备用API
refreshInterval: 30000, // 30秒刷新一次
retryDelay: 5000, // 重试延迟5秒
maxRetries: 3,
useFallback: false // 是否使用备用API
},
// 当前状态
state: {
isLoading: false,
retryCount: 0,
refreshTimer: null,
lastUpdateTime: null
},
// 初始化
init: function() {
this.bindEvents();
this.loadAllStockData();
this.startAutoRefresh();
},
// 绑定事件
bindEvents: function() {
// 页面可见性变化时的处理
$(document).on('visibilitychange', this.handleVisibilityChange.bind(this));
// 窗口焦点变化时的处理
$(window).on('focus blur', this.handleFocusChange.bind(this));
},
// 加载所有股票数据
loadAllStockData: function() {
const self = this;
$('.stock-info-block').each(function() {
const $block = $(this);
const stockCode = $block.data('stock-code');
if (stockCode) {
self.loadStockData(stockCode, $block);
}
});
},
// 加载单个股票数据
loadStockData: function(stockCode, $block) {
const self = this;
if (self.state.isLoading) {
return;
}
self.state.isLoading = true;
self.showLoading($block);
// 构建API URL
const apiUrl = (self.config.useFallback ? self.config.fallbackApiUrl : self.config.apiUrl) + stockCode;
// 使用JSONP方式调用API
$.ajax({
url: apiUrl,
method: 'GET',
dataType: 'script',
timeout: 10000,
success: function() {
self.handleApiSuccess(stockCode, $block);
},
error: function(xhr, status, error) {
// 如果主API失败且未使用备用API尝试备用API
if (!self.config.useFallback && self.config.fallbackApiUrl) {
console.log('主API失败尝试备用API...');
self.config.useFallback = true;
self.state.isLoading = false;
self.loadStockData(stockCode, $block);
return;
}
self.handleApiError(error, $block);
},
complete: function() {
self.state.isLoading = false;
}
});
},
// 处理API成功响应
handleApiSuccess: function(stockCode, $block) {
try {
// 腾讯API返回的数据格式: v_sz301046="股票名称~代码~当前价格~涨跌额~涨跌幅~..."
const varName = 'v_' + stockCode;
const stockDataString = window[varName];
if (!stockDataString) {
throw new Error('股票数据未找到');
}
const stockData = this.parseStockData(stockDataString);
this.updateStockDisplay(stockData, $block);
this.hideLoading($block);
this.state.retryCount = 0;
this.state.lastUpdateTime = new Date();
} catch (error) {
console.error('解析股票数据失败:', error);
this.handleApiError(error.message, $block);
}
},
// 处理API错误
handleApiError: function(error, $block) {
console.error('股票数据加载失败:', error);
this.state.retryCount++;
if (this.state.retryCount < this.config.maxRetries) {
// 重试
setTimeout(() => {
const stockCode = $block.data('stock-code');
this.loadStockData(stockCode, $block);
}, this.config.retryDelay);
} else {
// 显示错误状态
this.showError($block);
this.state.retryCount = 0;
}
},
// 解析股票数据
parseStockData: function(dataString) {
const parts = dataString.split('~');
if (parts.length < 30) {
throw new Error('股票数据格式不正确');
}
return {
name: parts[1], // 股票名称
code: parts[2], // 股票代码
currentPrice: parseFloat(parts[3]), // 当前价格
prevClose: parseFloat(parts[4]), // 昨收价
openPrice: parseFloat(parts[5]), // 今开价
volume: parseInt(parts[6]), // 成交量(手)
amount: parseFloat(parts[37]), // 成交额(万元)
highPrice: parseFloat(parts[33]), // 最高价
lowPrice: parseFloat(parts[34]), // 最低价
changeAmount: parseFloat(parts[31]), // 涨跌额
changePercent: parseFloat(parts[32]), // 涨跌幅
updateTime: parts[30] // 更新时间
};
},
// 更新股票显示
updateStockDisplay: function(data, $block) {
// 更新基本信息
$block.find('#current-price').text(data.currentPrice.toFixed(2)).addClass('updating');
$block.find('#volume-value').text(this.formatNumber(data.volume)).addClass('updating');
$block.find('#amount-value').text(this.formatNumber(data.amount)).addClass('updating');
// 更新涨跌信息
const changeValue = data.changeAmount > 0 ? '+' + data.changeAmount.toFixed(2) : data.changeAmount.toFixed(2);
const changePercent = data.changePercent > 0 ? '+' + data.changePercent.toFixed(2) + '%' : data.changePercent.toFixed(2) + '%';
$block.find('#price-change').text(changeValue);
$block.find('#change-percent').text(changePercent);
// 设置涨跌颜色
$block.removeClass('price-up price-down price-neutral');
if (data.changeAmount > 0) {
$block.addClass('price-up');
} else if (data.changeAmount < 0) {
$block.addClass('price-down');
} else {
$block.addClass('price-neutral');
}
// 更新详细数据
$block.find('#open-price').text(data.openPrice.toFixed(2));
$block.find('#prev-close').text(data.prevClose.toFixed(2));
$block.find('#high-price').text(data.highPrice.toFixed(2));
$block.find('#low-price').text(data.lowPrice.toFixed(2));
// 更新时间
$block.find('#update-time').text(this.formatUpdateTime(data.updateTime));
// 移除更新动画类
setTimeout(() => {
$block.find('.updating').removeClass('updating');
}, 300);
},
// 格式化数字显示
formatNumber: function(num) {
if (num >= 10000) {
return (num / 10000).toFixed(1) + '万';
} else if (num >= 1000) {
return (num / 1000).toFixed(1) + 'K';
}
return num.toString();
},
// 格式化更新时间
formatUpdateTime: function(timeString) {
if (!timeString || timeString.length < 14) {
return '数据更新中...';
}
// 格式: 20250926161424 -> 2025-09-26 16:14:24
const year = timeString.substr(0, 4);
const month = timeString.substr(4, 2);
const day = timeString.substr(6, 2);
const hour = timeString.substr(8, 2);
const minute = timeString.substr(10, 2);
const second = timeString.substr(12, 2);
return `${year}.${month}.${day} ${hour}:${minute}:${second}`;
},
// 显示加载状态
showLoading: function($block) {
$block.find('#loading-indicator').show();
$block.find('#error-indicator').hide();
$block.find('.stock-main-data').css('opacity', '0.6');
},
// 隐藏加载状态
hideLoading: function($block) {
$block.find('#loading-indicator').hide();
$block.find('.stock-main-data').css('opacity', '1');
},
// 显示错误状态
showError: function($block) {
$block.find('#loading-indicator').hide();
$block.find('#error-indicator').show();
$block.find('.stock-main-data').css('opacity', '0.3');
},
// 刷新数据
refreshData: function() {
this.state.retryCount = 0;
this.loadAllStockData();
},
// 开始自动刷新
startAutoRefresh: function() {
const self = this;
if (self.state.refreshTimer) {
clearInterval(self.state.refreshTimer);
}
self.state.refreshTimer = setInterval(function() {
if (!document.hidden && document.hasFocus()) {
self.loadAllStockData();
}
}, self.config.refreshInterval);
},
// 停止自动刷新
stopAutoRefresh: function() {
if (this.state.refreshTimer) {
clearInterval(this.state.refreshTimer);
this.state.refreshTimer = null;
}
},
// 处理页面可见性变化
handleVisibilityChange: function() {
if (document.hidden) {
this.stopAutoRefresh();
} else {
this.startAutoRefresh();
this.loadAllStockData(); // 页面重新可见时立即刷新
}
},
// 处理窗口焦点变化
handleFocusChange: function(event) {
if (event.type === 'focus') {
this.startAutoRefresh();
this.loadAllStockData();
} else if (event.type === 'blur') {
this.stopAutoRefresh();
}
}
};
// 文档就绪时初始化
$(document).ready(function() {
// 检查是否有股票信息区块
if ($('.stock-info-block').length > 0) {
stockInfoManager.init();
}
});
// 页面卸载时清理
$(window).on('beforeunload', function() {
stockInfoManager.stopAutoRefresh();
});
})(typeof jQuery !== 'undefined' ? jQuery : undefined);