// 股票数据展示系统前端应用
class StockDataApp {
constructor() {
this.currentPage = 1;
this.pageSize = 20;
this.currentStock = null;
this.klineChart = null;
this.init();
}
// 初始化应用
async init() {
this.setupEventListeners();
await this.loadSystemOverview();
await this.loadStockData();
this.setupNavigation();
}
// 设置事件监听器
setupEventListeners() {
// 搜索功能
const searchInput = document.getElementById('stock-search');
const searchBtn = document.getElementById('search-btn');
searchBtn.addEventListener('click', () => this.searchStocks());
searchInput.addEventListener('keypress', (e) => {
if (e.key === 'Enter') this.searchStocks();
});
// 股票选择器
const stockSelector = document.getElementById('stock-selector');
stockSelector.addEventListener('change', (e) => {
this.currentStock = e.target.value;
if (this.currentStock) this.loadKlineChart();
});
// 周期选择器
const periodSelector = document.getElementById('period-selector');
periodSelector.addEventListener('change', () => {
if (this.currentStock) this.loadKlineChart();
});
// 财务数据选择器
const financialStockSelector = document.getElementById('financial-stock-selector');
financialStockSelector.addEventListener('change', (e) => {
if (e.target.value) this.loadFinancialData(e.target.value);
});
// 日志刷新
const refreshLogsBtn = document.getElementById('refresh-logs');
refreshLogsBtn.addEventListener('click', () => this.loadSystemLogs());
// 日志过滤器
const logLevelFilter = document.getElementById('log-level-filter');
logLevelFilter.addEventListener('change', () => this.loadSystemLogs());
const logDateFilter = document.getElementById('log-date-filter');
logDateFilter.addEventListener('change', () => this.loadSystemLogs());
}
// 设置导航
setupNavigation() {
const navLinks = document.querySelectorAll('.nav-link');
const sections = document.querySelectorAll('.content-section');
navLinks.forEach(link => {
link.addEventListener('click', (e) => {
e.preventDefault();
// 移除所有激活状态
navLinks.forEach(l => l.classList.remove('active'));
sections.forEach(s => s.classList.remove('active'));
// 添加当前激活状态
link.classList.add('active');
const targetSection = document.querySelector(link.getAttribute('href'));
if (targetSection) {
targetSection.classList.add('active');
// 加载对应数据
switch(link.getAttribute('href')) {
case '#kline-chart':
this.loadKlineChart();
break;
case '#financial-data':
this.loadFinancialData();
break;
case '#system-logs':
this.loadSystemLogs();
break;
}
}
});
});
}
// 显示加载遮罩
showLoading() {
document.getElementById('loading-overlay').classList.add('show');
}
// 隐藏加载遮罩
hideLoading() {
document.getElementById('loading-overlay').classList.remove('show');
}
// 加载系统概览数据
async loadSystemOverview() {
try {
const response = await this.apiCall('/api/system/overview');
if (response.success) {
document.getElementById('stock-count').textContent = this.formatNumber(response.stock_count);
document.getElementById('kline-count').textContent = this.formatNumber(response.kline_count);
document.getElementById('financial-count').textContent = this.formatNumber(response.financial_count);
document.getElementById('log-count').textContent = this.formatNumber(response.log_count);
}
} catch (error) {
console.error('加载系统概览失败:', error);
}
}
// 加载股票数据
async loadStockData(page = 1) {
try {
this.showLoading();
const response = await this.apiCall(`/api/stocks?page=${page}&limit=${this.pageSize}`);
if (response.success) {
this.renderStockTable(response.data);
this.setupPagination(response.total, page);
this.populateStockSelectors(response.data);
}
} catch (error) {
console.error('加载股票数据失败:', error);
this.showError('加载股票数据失败');
} finally {
this.hideLoading();
}
}
// 渲染股票表格
renderStockTable(stocks) {
const tbody = document.getElementById('stock-table-body');
tbody.innerHTML = '';
stocks.forEach(stock => {
const row = document.createElement('tr');
row.innerHTML = `
${stock.code} |
${stock.name} |
${stock.exchange} |
${this.formatDate(stock.listing_date)} |
${stock.industry || '-'} |
|
`;
tbody.appendChild(row);
});
}
// 设置分页
setupPagination(total, currentPage) {
const pagination = document.getElementById('stock-pagination');
const totalPages = Math.ceil(total / this.pageSize);
if (totalPages <= 1) {
pagination.innerHTML = '';
return;
}
let html = '';
// 上一页按钮
if (currentPage > 1) {
html += ``;
}
// 页码按钮
for (let i = 1; i <= totalPages; i++) {
if (i === currentPage) {
html += ``;
} else {
html += ``;
}
}
// 下一页按钮
if (currentPage < totalPages) {
html += ``;
}
pagination.innerHTML = html;
}
// 填充股票选择器
populateStockSelectors(stocks) {
const selectors = [
document.getElementById('stock-selector'),
document.getElementById('financial-stock-selector')
];
selectors.forEach(selector => {
// 清空现有选项(保留第一个选项)
while (selector.children.length > 1) {
selector.removeChild(selector.lastChild);
}
// 添加股票选项
stocks.forEach(stock => {
const option = document.createElement('option');
option.value = stock.code;
option.textContent = `${stock.code} - ${stock.name}`;
selector.appendChild(option);
});
});
}
// 搜索股票
async searchStocks() {
const query = document.getElementById('stock-search').value.trim();
if (!query) {
await this.loadStockData();
return;
}
try {
this.showLoading();
const response = await this.apiCall(`/api/stocks/search?q=${encodeURIComponent(query)}`);
if (response.success) {
this.renderStockTable(response.data);
document.getElementById('stock-pagination').innerHTML = '';
}
} catch (error) {
console.error('搜索股票失败:', error);
this.showError('搜索股票失败');
} finally {
this.hideLoading();
}
}
// 加载K线图表
async loadKlineChart() {
if (!this.currentStock) return;
try {
this.showLoading();
const period = document.getElementById('period-selector').value;
const response = await this.apiCall(`/api/kline/${this.currentStock}?period=${period}`);
if (response.success) {
this.renderKlineChart(response.data);
}
} catch (error) {
console.error('加载K线数据失败:', error);
this.showError('加载K线数据失败');
} finally {
this.hideLoading();
}
}
// 渲染K线图表
renderKlineChart(klineData) {
const ctx = document.getElementById('kline-chart-canvas').getContext('2d');
// 销毁现有图表
if (this.klineChart) {
this.klineChart.destroy();
}
const dates = klineData.map(item => item.date);
const prices = klineData.map(item => item.close);
this.klineChart = new Chart(ctx, {
type: 'line',
data: {
labels: dates,
datasets: [{
label: '收盘价',
data: prices,
borderColor: '#667eea',
backgroundColor: 'rgba(102, 126, 234, 0.1)',
fill: true,
tension: 0.1
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
title: {
display: true,
text: '股票价格走势图'
}
},
scales: {
y: {
beginAtZero: false
}
}
}
});
}
// 加载财务数据
async loadFinancialData(stockCode = null) {
try {
this.showLoading();
if (!stockCode) {
stockCode = document.getElementById('financial-stock-selector').value;
}
if (!stockCode) return;
const period = document.getElementById('report-period').value;
const year = document.getElementById('report-year').value;
const response = await this.apiCall(`/api/financial/${stockCode}?year=${year}&period=${period}`);
if (response.success) {
this.renderFinancialTable(response.data);
}
} catch (error) {
console.error('加载财务数据失败:', error);
this.showError('加载财务数据失败');
} finally {
this.hideLoading();
}
}
// 渲染财务数据表格
renderFinancialTable(financialData) {
const tbody = document.getElementById('financial-table-body');
tbody.innerHTML = '';
if (!financialData || Object.keys(financialData).length === 0) {
tbody.innerHTML = '| 暂无财务数据 |
';
return;
}
const financialItems = [
{ key: 'revenue', label: '营业收入', unit: '万元' },
{ key: 'net_profit', label: '净利润', unit: '万元' },
{ key: 'total_assets', label: '总资产', unit: '万元' },
{ key: 'total_liabilities', label: '总负债', unit: '万元' },
{ key: 'eps', label: '每股收益', unit: '元' },
{ key: 'roe', label: '净资产收益率', unit: '%' }
];
financialItems.forEach(item => {
if (financialData[item.key] !== undefined) {
const row = document.createElement('tr');
row.innerHTML = `
${item.label} |
${this.formatNumber(financialData[item.key])} |
${item.unit} |
${this.calculateChange(financialData[item.key])} |
`;
tbody.appendChild(row);
}
});
}
// 加载系统日志
async loadSystemLogs() {
try {
this.showLoading();
const level = document.getElementById('log-level-filter').value;
const date = document.getElementById('log-date-filter').value;
let url = '/api/system/logs';
const params = [];
if (level) params.push(`level=${level}`);
if (date) params.push(`date=${date}`);
if (params.length > 0) {
url += '?' + params.join('&');
}
const response = await this.apiCall(url);
if (response.success) {
this.renderSystemLogs(response.data);
}
} catch (error) {
console.error('加载系统日志失败:', error);
this.showError('加载系统日志失败');
} finally {
this.hideLoading();
}
}
// 渲染系统日志
renderSystemLogs(logs) {
const logList = document.getElementById('log-list');
logList.innerHTML = '';
if (!logs || logs.length === 0) {
logList.innerHTML = '暂无系统日志
';
return;
}
logs.forEach(log => {
const logItem = document.createElement('div');
logItem.className = `log-item ${log.level.toLowerCase()}`;
logItem.innerHTML = `
${log.message}
模块: ${log.module_name} | 事件: ${log.event_type}
${log.exception_type ? ` | 异常: ${log.exception_type}` : ''}
`;
logList.appendChild(logItem);
});
}
// 查看股票详情
viewStockDetails(stockCode) {
alert(`查看股票 ${stockCode} 的详细信息`);
// 这里可以扩展为显示详细模态框
}
// API调用封装
async apiCall(url) {
// 模拟API调用,实际项目中需要连接到后端API
return new Promise((resolve) => {
setTimeout(() => {
// 模拟数据
const mockData = this.getMockData(url);
resolve(mockData);
}, 500);
});
}
// 获取模拟数据
getMockData(url) {
if (url.includes('/api/system/overview')) {
return {
success: true,
stock_count: 12595,
kline_count: 440,
financial_count: 50,
log_count: 4
};
}
if (url.includes('/api/stocks')) {
// 模拟股票数据
const mockStocks = [
{ code: '000001', name: '平安银行', exchange: 'SZ', listing_date: '1991-04-03', industry: '银行' },
{ code: '000002', name: '万科A', exchange: 'SZ', listing_date: '1991-01-29', industry: '房地产' },
{ code: '600000', name: '浦发银行', exchange: 'SH', listing_date: '1999-11-10', industry: '银行' },
{ code: '600036', name: '招商银行', exchange: 'SH', listing_date: '2002-04-09', industry: '银行' },
{ code: '601318', name: '中国平安', exchange: 'SH', listing_date: '2007-03-01', industry: '保险' }
];
return {
success: true,
data: mockStocks,
total: 12595
};
}
if (url.includes('/api/kline/')) {
// 模拟K线数据
const dates = [];
const prices = [];
const basePrice = 10;
for (let i = 30; i >= 0; i--) {
const date = new Date();
date.setDate(date.getDate() - i);
dates.push(date.toISOString().split('T')[0]);
// 模拟价格波动
const price = basePrice + Math.random() * 5;
prices.push({
date: date.toISOString().split('T')[0],
open: price - 0.5,
high: price + 0.8,
low: price - 0.8,
close: price,
volume: Math.floor(Math.random() * 1000000)
});
}
return {
success: true,
data: prices
};
}
if (url.includes('/api/financial/')) {
// 模拟财务数据
return {
success: true,
data: {
revenue: 500000,
net_profit: 80000,
total_assets: 2000000,
total_liabilities: 1200000,
eps: 1.5,
roe: 15.2
}
};
}
if (url.includes('/api/system/logs')) {
// 模拟系统日志
return {
success: true,
data: [
{
id: 1,
timestamp: new Date().toISOString(),
level: 'INFO',
module_name: 'System',
event_type: 'STARTUP',
message: '系统启动成功',
exception_type: null
},
{
id: 2,
timestamp: new Date(Date.now() - 3600000).toISOString(),
level: 'INFO',
module_name: 'DataCollector',
event_type: 'DATA_COLLECTION',
message: '开始采集股票数据',
exception_type: null
},
{
id: 3,
timestamp: new Date(Date.now() - 1800000).toISOString(),
level: 'ERROR',
module_name: 'Database',
event_type: 'CONNECTION_ERROR',
message: '数据库连接失败',
exception_type: 'ConnectionError'
},
{
id: 4,
timestamp: new Date(Date.now() - 900000).toISOString(),
level: 'WARNING',
module_name: 'DataProcessor',
event_type: 'DATA_FORMAT',
message: '数据格式异常,已自动修复',
exception_type: 'FormatError'
}
]
};
}
return { success: false, message: 'API endpoint not found' };
}
// 工具函数
formatNumber(num) {
if (num === null || num === undefined) return '-';
return new Intl.NumberFormat('zh-CN').format(num);
}
formatDate(dateString) {
if (!dateString) return '-';
return new Date(dateString).toLocaleDateString('zh-CN');
}
formatDateTime(dateString) {
if (!dateString) return '-';
return new Date(dateString).toLocaleString('zh-CN');
}
calculateChange(value) {
const change = (Math.random() - 0.5) * 20;
const sign = change >= 0 ? '+' : '';
const color = change >= 0 ? '#27ae60' : '#e74c3c';
return `${sign}${change.toFixed(2)}%`;
}
showError(message) {
alert(`错误: ${message}`);
}
}
// 全局应用实例
const app = new StockDataApp();
// 页面加载完成后初始化
document.addEventListener('DOMContentLoaded', () => {
console.log('股票数据展示系统已加载');
});