[HTML] 纯文本查看 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>A股主要指数实时监控工具</title>
<!-- 引入Tailwind CSS -->
<script src="https://cdn.tailwindcss.com"></script>
<!-- 引入Font Awesome -->
<link href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css" rel="stylesheet">
<!-- 引入Chart.js -->
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.8/dist/chart.umd.min.js"></script>
<!-- 配置Tailwind -->
<script>
tailwind.config = {
theme: {
extend: {
colors: {
primary: '#165DFF',
secondary: '#4080FF',
rise: '#00B42A', // 上涨颜色-绿色
fall: '#F53F3F', // 下跌颜色-红色
neutral: '#86909C', // 中性色
dark: '#1D2129', // 深色文本
light: '#F2F3F5' // 浅色背景
},
fontFamily: {
inter: ['Inter', 'system-ui', 'sans-serif'],
},
},
}
}
</script>
<!-- 自定义工具类 -->
<style type="text/tailwindcss">
[url=home.php?mod=space&uid=1688376]@layer[/url] utilities {
.content-auto {
content-visibility: auto;
}
.card-shadow {
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
}
.index-card-hover {
transition: all 0.3s ease;
}
.index-card-hover:hover {
transform: translateY(-5px);
box-shadow: 0 8px 30px rgba(0, 0, 0, 0.12);
}
.page-transition {
animation: fadeIn 0.3s ease-in-out;
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
}
</style>
</head>
<body class="font-inter bg-gray-50 text-dark">
<!-- 顶部导航栏 -->
<header class="bg-white shadow-md fixed top-0 left-0 right-0 z-50 transition-all duration-300">
<div class="container mx-auto px-4 py-3 flex items-center justify-between">
<div class="flex items-center space-x-2">
<i class="fa fa-line-chart text-primary text-2xl"></i>
<h1 class="text-xl font-bold text-primary">A股指数监控</h1>
</div>
<nav class="hidden md:flex items-center space-x-6">
<a href="#market" class="nav-link text-primary font-medium border-b-2 border-primary py-1" data-page="market">实时行情</a>
<a href="#history" class="nav-link text-gray-600 hover:text-primary transition-colors py-1" data-page="history">历史数据</a>
<a href="#news" class="nav-link text-gray-600 hover:text-primary transition-colors py-1" data-page="news">市场资讯</a>
<a href="#settings" class="nav-link text-gray-600 hover:text-primary transition-colors py-1" data-page="settings">设置</a>
</nav>
<div class="flex items-center space-x-3">
<button id="refreshBtn" class="flex items-center space-x-1 text-gray-600 hover:text-primary transition-colors">
<i class="fa fa-refresh"></i>
<span class="hidden sm:inline">刷新</span>
</button>
<button class="md:hidden text-gray-600" id="mobileMenuBtn">
<i class="fa fa-bars text-xl"></i>
</button>
</div>
</div>
<!-- 移动端菜单 -->
<div id="mobileMenu" class="hidden md:hidden bg-white border-t">
<div class="container mx-auto px-4 py-2 flex flex-col space-y-3">
<a href="#market" class="mobile-nav-link text-primary font-medium py-2" data-page="market">实时行情</a>
<a href="#history" class="mobile-nav-link text-gray-600 hover:text-primary transition-colors py-2" data-page="history">历史数据</a>
<a href="#news" class="mobile-nav-link text-gray-600 hover:text-primary transition-colors py-2" data-page="news">市场资讯</a>
<a href="#settings" class="mobile-nav-link text-gray-600 hover:text-primary transition-colors py-2" data-page="settings">设置</a>
</div>
</div>
</header>
<!-- 主内容区 -->
<main class="container mx-auto px-4 pt-20 pb-10">
<!-- 页面容器 - 所有页面都在这里切换显示 -->
<div id="pageContainer">
<!-- 1. 实时行情页面 -->
<section id="marketPage" class="page-content page-transition">
<!-- 市场状态和时间信息 -->
<div class="bg-white rounded-lg p-4 mb-6 shadow-sm">
<div class="flex flex-col md:flex-row md:items-center justify-between">
<div class="flex items-center space-x-4 mb-3 md:mb-0">
<div class="flex items-center">
<span class="inline-block w-3 h-3 rounded-full bg-rise mr-2 animate-pulse" id="marketStatusIndicator"></span>
<span id="marketStatus" class="font-medium">加载中...</span>
</div>
<span id="currentTime" class="text-neutral">--:--:--</span>
<span id="lastUpdateTime" class="text-xs bg-gray-100 px-2 py-0.5 rounded text-neutral">最后更新: 未更新</span>
</div>
<div class="flex flex-wrap gap-3">
<div class="flex items-center text-sm">
<span class="text-neutral mr-2">刷新频率:</span>
<select id="refreshInterval" class="border border-gray-200 rounded px-2 py-1 focus:outline-none focus:ring-1 focus:ring-primary">
<option value="5">5秒</option>
<option value="10" selected>10秒</option>
<option value="30">30秒</option>
<option value="60">1分钟</option>
<option value="0">不自动刷新</option>
</select>
</div>
<div class="flex items-center text-sm">
<span class="text-neutral mr-2">图表周期:</span>
<div class="flex border border-gray-200 rounded overflow-hidden">
<button class="chart-period-btn px-3 py-1 bg-primary text-white" data-period="day">日K</button>
<button class="chart-period-btn px-3 py-1 bg-white text-gray-600 hover:bg-gray-100" data-period="min">分时</button>
</div>
</div>
</div>
</div>
</div>
<!-- 主要指数卡片 -->
<section class="mb-8">
<h2 class="text-lg font-bold mb-4 flex items-center">
<i class="fa fa-star text-primary mr-2"></i>主要指数
</h2>
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
<!-- 上证指数 -->
<div class="bg-white rounded-lg p-5 card-shadow index-card-hover cursor-pointer" data-detail="sse">
<div class="flex justify-between items-start mb-3">
<div>
<h3 class="font-bold text-lg">上证指数</h3>
<p class="text-neutral text-sm">000001.SH</p>
</div>
<span class="bg-primary/10 text-primary text-xs px-2 py-1 rounded-full">宽基</span>
</div>
<div class="mb-3">
<div class="text-2xl font-bold" id="sseIndex">--</div>
<div class="flex items-center mt-1">
<span class="font-medium" id="sseChange">--</span>
<span class="ml-2 text-sm" id="ssePercent">--</span>
</div>
</div>
<div class="flex justify-between text-sm text-neutral pt-2 border-t border-gray-100">
<div>
<p>今开: <span id="sseOpen">--</span></p>
<p>最高: <span id="sseHigh">--</span></p>
</div>
<div>
<p>昨收: <span id="sseClose">--</span></p>
<p>最低: <span id="sseLow">--</span></p>
</div>
<div>
<p>成交量: <span id="sseVolume">--</span></p>
<p>成交额: <span id="sseAmount">--</span></p>
</div>
</div>
</div>
<!-- 深证成指 -->
<div class="bg-white rounded-lg p-5 card-shadow index-card-hover cursor-pointer" data-detail="szse">
<div class="flex justify-between items-start mb-3">
<div>
<h3 class="font-bold text-lg">深证成指</h3>
<p class="text-neutral text-sm">399001.SZ</p>
</div>
<span class="bg-primary/10 text-primary text-xs px-2 py-1 rounded-full">宽基</span>
</div>
<div class="mb-3">
<div class="text-2xl font-bold" id="szseIndex">--</div>
<div class="flex items-center mt-1">
<span class="font-medium" id="szseChange">--</span>
<span class="ml-2 text-sm" id="szsePercent">--</span>
</div>
</div>
<div class="flex justify-between text-sm text-neutral pt-2 border-t border-gray-100">
<div>
<p>今开: <span id="szseOpen">--</span></p>
<p>最高: <span id="szseHigh">--</span></p>
</div>
<div>
<p>昨收: <span id="szseClose">--</span></p>
<p>最低: <span id="szseLow">--</span></p>
</div>
<div>
<p>成交量: <span id="szseVolume">--</span></p>
<p>成交额: <span id="szseAmount">--</span></p>
</div>
</div>
</div>
<!-- 创业板指 -->
<div class="bg-white rounded-lg p-5 card-shadow index-card-hover cursor-pointer" data-detail="gem">
<div class="flex justify-between items-start mb-3">
<div>
<h3 class="font-bold text-lg">创业板指</h3>
<p class="text-neutral text-sm">399006.SZ</p>
</div>
<span class="bg-primary/10 text-primary text-xs px-2 py-1 rounded-full">宽基</span>
</div>
<div class="mb-3">
<div class="text-2xl font-bold" id="gemIndex">--</div>
<div class="flex items-center mt-1">
<span class="font-medium" id="gemChange">--</span>
<span class="ml-2 text-sm" id="gemPercent">--</span>
</div>
</div>
<div class="flex justify-between text-sm text-neutral pt-2 border-t border-gray-100">
<div>
<p>今开: <span id="gemOpen">--</span></p>
<p>最高: <span id="gemHigh">--</span></p>
</div>
<div>
<p>昨收: <span id="gemClose">--</span></p>
<p>最低: <span id="gemLow">--</span></p>
</div>
<div>
<p>成交量: <span id="gemVolume">--</span></p>
<p>成交额: <span id="gemAmount">--</span></p>
</div>
</div>
</div>
<!-- 科创50 -->
<div class="bg-white rounded-lg p-5 card-shadow index-card-hover cursor-pointer" data-detail="star">
<div class="flex justify-between items-start mb-3">
<div>
<h3 class="font-bold text-lg">科创50</h3>
<p class="text-neutral text-sm">000688.SH</p>
</div>
<span class="bg-primary/10 text-primary text-xs px-2 py-1 rounded-full">宽基</span>
</div>
<div class="mb-3">
<div class="text-2xl font-bold" id="starIndex">--</div>
<div class="flex items-center mt-1">
<span class="font-medium" id="starChange">--</span>
<span class="ml-2 text-sm" id="starPercent">--</span>
</div>
</div>
<div class="flex justify-between text-sm text-neutral pt-2 border-t border-gray-100">
<div>
<p>今开: <span id="starOpen">--</span></p>
<p>最高: <span id="starHigh">--</span></p>
</div>
<div>
<p>昨收: <span id="starClose">--</span></p>
<p>最低: <span id="starLow">--</span></p>
</div>
<div>
<p>成交量: <span id="starVolume">--</span></p>
<p>成交额: <span id="starAmount">--</span></p>
</div>
</div>
</div>
</div>
</section>
<!-- 指数走势图 -->
<section class="mb-8">
<div class="flex justify-between items-center mb-4">
<h2 class="text-lg font-bold flex items-center">
<i class="fa fa-area-chart text-primary mr-2"></i>指数走势
</h2>
<div class="flex space-x-2">
<button class="index-chart-btn px-3 py-1 bg-primary text-white rounded text-sm" data-index="sse">上证指数</button>
<button class="index-chart-btn px-3 py-1 bg-white text-gray-600 hover:bg-gray-100 rounded text-sm" data-index="szse">深证成指</button>
<button class="index-chart-btn px-3 py-1 bg-white text-gray-600 hover:bg-gray-100 rounded text-sm" data-index="gem">创业板指</button>
<button class="index-chart-btn px-3 py-1 bg-white text-gray-600 hover:bg-gray-100 rounded text-sm" data-index="star">科创50</button>
</div>
</div>
<div class="bg-white rounded-lg p-4 md:p-6 shadow-sm">
<div class="h-[400px] relative">
<canvas id="indexChart"></canvas>
<div id="chartLoading" class="absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 flex flex-col items-center">
<i class="fa fa-circle-o-notch fa-spin text-primary text-3xl mb-2"></i>
<p class="text-neutral">加载图表数据中...</p>
</div>
</div>
</div>
</section>
<!-- 行业板块指数 -->
<section>
<h2 class="text-lg font-bold mb-4 flex items-center">
<i class="fa fa-th-large text-primary mr-2"></i>行业板块指数
</h2>
<div class="bg-white rounded-lg shadow-sm overflow-hidden">
<div class="overflow-x-auto">
<table class="min-w-full divide-y divide-gray-200">
<thead class="bg-gray-50">
<tr>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-neutral uppercase tracking-wider">板块名称</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-neutral uppercase tracking-wider">指数代码</th>
<th scope="col" class="px-6 py-3 text-right text-xs font-medium text-neutral uppercase tracking-wider">最新价</th>
<th scope="col" class="px-6 py-3 text-right text-xs font-medium text-neutral uppercase tracking-wider">涨跌幅</th>
<th scope="col" class="px-6 py-3 text-right text-xs font-medium text-neutral uppercase tracking-wider">涨跌额</th>
<th scope="col" class="px-6 py-3 text-right text-xs font-medium text-neutral uppercase tracking-wider">成交量(万手)</th>
<th scope="col" class="px-6 py-3 text-right text-xs font-medium text-neutral uppercase tracking-wider">成交额(亿元)</th>
</tr>
</thead>
<tbody class="bg-white divide-y divide-gray-200" id="sectorTable">
<tr>
<td colspan="7" class="px-6 py-10 text-center text-neutral">
<div class="flex flex-col items-center">
<i class="fa fa-circle-o-notch fa-spin text-primary text-2xl mb-2"></i>
<p>加载板块数据中...</p>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</section>
</section>
<!-- 2. 历史数据页面 -->
<section id="historyPage" class="page-content hidden">
<div class="bg-white rounded-lg p-6 shadow-sm mb-6">
<h2 class="text-xl font-bold mb-6 flex items-center">
<i class="fa fa-history text-primary mr-2"></i>历史数据查询
</h2>
<div class="grid grid-cols-1 md:grid-cols-3 gap-6 mb-6">
<div>
<label class="block text-sm font-medium text-neutral mb-1">指数选择</label>
<select id="historyIndexSelect" class="w-full border border-gray-200 rounded px-3 py-2 focus:outline-none focus:ring-1 focus:ring-primary">
<option value="sse">上证指数 (000001.SH)</option>
<option value="szse">深证成指 (399001.SZ)</option>
<option value="gem">创业板指 (399006.SZ)</option>
<option value="star">科创50 (000688.SH)</option>
</select>
</div>
<div>
<label class="block text-sm font-medium text-neutral mb-1">时间周期</label>
<select id="historyPeriodSelect" class="w-full border border-gray-200 rounded px-3 py-2 focus:outline-none focus:ring-1 focus:ring-primary">
<option value="day">日线</option>
<option value="week">周线</option>
<option value="month">月线</option>
<option value="year">年线</option>
</select>
</div>
<div>
<label class="block text-sm font-medium text-neutral mb-1">日期范围</label>
<div class="flex space-x-2">
<input type="date" id="historyStartDate" class="flex-1 border border-gray-200 rounded px-3 py-2 focus:outline-none focus:ring-1 focus:ring-primary">
<input type="date" id="historyEndDate" class="flex-1 border border-gray-200 rounded px-3 py-2 focus:outline-none focus:ring-1 focus:ring-primary">
</div>
</div>
</div>
<div class="flex justify-end mb-6">
<button id="queryHistoryBtn" class="bg-primary text-white px-4 py-2 rounded hover:bg-primary/90 transition-colors flex items-center">
<i class="fa fa-search mr-2"></i>查询数据
</button>
</div>
<div class="h-[400px] relative">
<canvas id="historyChart"></canvas>
<div id="historyChartLoading" class="absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 flex flex-col items-center hidden">
<i class="fa fa-circle-o-notch fa-spin text-primary text-3xl mb-2"></i>
<p class="text-neutral">加载历史数据中...</p>
</div>
<div id="historyChartEmpty" class="absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 flex flex-col items-center">
<i class="fa fa-bar-chart text-neutral text-3xl mb-2"></i>
<p class="text-neutral">请选择指数和日期范围查询历史数据</p>
</div>
</div>
</div>
<div class="bg-white rounded-lg shadow-sm overflow-hidden">
<div class="p-6 border-b border-gray-100">
<h3 class="font-bold text-lg">历史数据列表</h3>
</div>
<div class="overflow-x-auto">
<table class="min-w-full divide-y divide-gray-200">
<thead class="bg-gray-50">
<tr>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-neutral uppercase tracking-wider">日期</th>
<th scope="col" class="px-6 py-3 text-right text-xs font-medium text-neutral uppercase tracking-wider">开盘价</th>
<th scope="col" class="px-6 py-3 text-right text-xs font-medium text-neutral uppercase tracking-wider">最高价</th>
<th scope="col" class="px-6 py-3 text-right text-xs font-medium text-neutral uppercase tracking-wider">最低价</th>
<th scope="col" class="px-6 py-3 text-right text-xs font-medium text-neutral uppercase tracking-wider">收盘价</th>
<th scope="col" class="px-6 py-3 text-right text-xs font-medium text-neutral uppercase tracking-wider">涨跌幅</th>
<th scope="col" class="px-6 py-3 text-right text-xs font-medium text-neutral uppercase tracking-wider">成交量(万手)</th>
</tr>
</thead>
<tbody class="bg-white divide-y divide-gray-200" id="historyTable">
<tr>
<td colspan="7" class="px-6 py-10 text-center text-neutral">
<p>请先查询历史数据</p>
</td>
</tr>
</tbody>
</table>
</div>
<div class="p-4 flex justify-between items-center border-t border-gray-100">
<div class="text-sm text-neutral">显示 0 条记录</div>
<div class="flex space-x-2">
<button class="px-3 py-1 border border-gray-200 rounded text-sm text-neutral hover:bg-gray-50 disabled:opacity-50" disabled>上一页</button>
<button class="px-3 py-1 border border-gray-200 rounded text-sm text-neutral hover:bg-gray-50 disabled:opacity-50" disabled>下一页</button>
</div>
</div>
</div>
</section>
<!-- 3. 市场资讯页面 -->
<section id="newsPage" class="page-content hidden">
<div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
<div class="lg:col-span-2">
<div class="bg-white rounded-lg shadow-sm overflow-hidden mb-6">
<div class="p-6 border-b border-gray-100">
<h2 class="text-xl font-bold flex items-center">
<i class="fa fa-newspaper-o text-primary mr-2"></i>最新资讯
</h2>
</div>
<div id="newsList" class="divide-y divide-gray-100">
<!-- 新闻条目将通过JavaScript动态生成 -->
</div>
<div class="p-4 flex justify-center border-t border-gray-100">
<button id="loadMoreNews" class="px-4 py-2 border border-primary text-primary rounded hover:bg-primary/5 transition-colors flex items-center">
<i class="fa fa-refresh mr-2"></i>加载更多
</button>
</div>
</div>
</div>
<div>
<div class="bg-white rounded-lg shadow-sm overflow-hidden mb-6">
<div class="p-4 border-b border-gray-100">
<h3 class="font-bold">热门板块</h3>
</div>
<div id="hotSectors" class="p-4">
<!-- 热门板块将通过JavaScript动态生成 -->
</div>
</div>
<div class="bg-white rounded-lg shadow-sm overflow-hidden">
<div class="p-4 border-b border-gray-100">
<h3 class="font-bold">市场日历</h3>
</div>
<div class="p-4">
<div class="text-sm text-neutral mb-4">今日暂无重大事件</div>
<div class="space-y-3">
<div class="p-3 bg-light rounded border border-gray-100">
<div class="font-medium text-sm">明日</div>
<div class="text-xs text-neutral mt-1">• 中国7月CPI数据公布</div>
<div class="text-xs text-neutral">• 3家公司IPO申购</div>
</div>
<div class="p-3 bg-light rounded border border-gray-100">
<div class="font-medium text-sm">本周六</div>
<div class="text-xs text-neutral mt-1">• 美联储公布7月货币政策会议纪要</div>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- 4. 设置页面 -->
<section id="settingsPage" class="page-content hidden">
<div class="bg-white rounded-lg shadow-sm p-6 mb-6">
<h2 class="text-xl font-bold mb-6 flex items-center">
<i class="fa fa-cog text-primary mr-2"></i>系统设置
</h2>
<div class="space-y-6">
<div>
<h3 class="font-bold mb-4">刷新设置</h3>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<label class="block text-sm font-medium text-neutral mb-1">默认刷新频率</label>
<select id="defaultRefreshInterval" class="w-full border border-gray-200 rounded px-3 py-2 focus:outline-none focus:ring-1 focus:ring-primary">
<option value="5">5秒</option>
<option value="10" selected>10秒</option>
<option value="30">30秒</option>
<option value="60">1分钟</option>
<option value="0">不自动刷新</option>
</select>
</div>
<div>
<label class="block text-sm font-medium text-neutral mb-1">休市时自动刷新</label>
<div class="flex items-center space-x-4">
<label class="inline-flex items-center">
<input type="radio" name="refreshWhenClosed" value="yes" class="text-primary focus:ring-primary">
<span class="ml-2">启用</span>
</label>
<label class="inline-flex items-center">
<input type="radio" name="refreshWhenClosed" value="no" checked class="text-primary focus:ring-primary">
<span class="ml-2">禁用</span>
</label>
</div>
</div>
</div>
</div>
<div class="pt-4 border-t border-gray-100">
<h3 class="font-bold mb-4">显示设置</h3>
<div class="space-y-4">
<div class="flex items-center justify-between">
<div>
<div class="font-medium">深色模式</div>
<div class="text-sm text-neutral">切换深色/浅色显示主题</div>
</div>
<label class="relative inline-flex items-center cursor-pointer">
<input type="checkbox" id="darkModeToggle" class="sr-only peer">
<div class="w-11 h-6 bg-gray-200 peer-focus:outline-none peer-focus:ring-1 peer-focus:ring-primary rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-primary"></div>
</label>
</div>
<div class="flex items-center justify-between">
<div>
<div class="font-medium">显示涨跌动画</div>
<div class="text-sm text-neutral">指数涨跌时显示数值变化动画</div>
</div>
<label class="relative inline-flex items-center cursor-pointer">
<input type="checkbox" id="animateChangesToggle" checked class="sr-only peer">
<div class="w-11 h-6 bg-gray-200 peer-focus:outline-none peer-focus:ring-1 peer-focus:ring-primary rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-primary"></div>
</label>
</div>
<div class="flex items-center justify-between">
<div>
<div class="font-medium">显示成交量</div>
<div class="text-sm text-neutral">在指数卡片中显示成交量数据</div>
</div>
<label class="relative inline-flex items-center cursor-pointer">
<input type="checkbox" id="showVolumeToggle" checked class="sr-only peer">
<div class="w-11 h-6 bg-gray-200 peer-focus:outline-none peer-focus:ring-1 peer-focus:ring-primary rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-primary"></div>
</label>
</div>
</div>
</div>
<div class="pt-4 border-t border-gray-100">
<h3 class="font-bold mb-4">通知设置</h3>
<div class="space-y-4">
<div class="flex items-center justify-between">
<div>
<div class="font-medium">指数预警</div>
<div class="text-sm text-neutral">当指数涨跌超过设定阈值时通知</div>
</div>
<label class="relative inline-flex items-center cursor-pointer">
<input type="checkbox" id="alertToggle" class="sr-only peer">
<div class="w-11 h-6 bg-gray-200 peer-focus:outline-none peer-focus:ring-1 peer-focus:ring-primary rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-primary"></div>
</label>
</div>
<div id="alertThresholdContainer" class="hidden grid grid-cols-1 md:grid-cols-2 gap-4 mt-4">
<div>
<label class="block text-sm font-medium text-neutral mb-1">上涨预警阈值 (%)</label>
<input type="number" id="riseThreshold" step="0.1" min="0.1" max="10" value="1.0" class="w-full border border-gray-200 rounded px-3 py-2 focus:outline-none focus:ring-1 focus:ring-primary">
</div>
<div>
<label class="block text-sm font-medium text-neutral mb-1">下跌预警阈值 (%)</label>
<input type="number" id="fallThreshold" step="0.1" min="0.1" max="10" value="1.0" class="w-full border border-gray-200 rounded px-3 py-2 focus:outline-none focus:ring-1 focus:ring-primary">
</div>
</div>
</div>
</div>
<div class="pt-4 border-t border-gray-100 flex justify-end">
<button id="saveSettingsBtn" class="bg-primary text-white px-6 py-2 rounded hover:bg-primary/90 transition-colors flex items-center">
<i class="fa fa-save mr-2"></i>保存设置
</button>
</div>
</div>
</div>
</section>
<!-- 指数详情模态框 -->
<div id="indexDetailModal" class="fixed inset-0 bg-black bg-opacity-50 z-50 hidden flex items-center justify-center p-4">
<div class="bg-white rounded-lg shadow-lg w-full max-w-3xl max-h-[90vh] overflow-hidden flex flex-col">
<div class="p-4 border-b border-gray-100 flex justify-between items-center">
<h3 class="text-lg font-bold" id="modalTitle">指数详情</h3>
<button id="closeModalBtn" class="text-gray-500 hover:text-gray-700">
<i class="fa fa-times text-xl"></i>
</button>
</div>
<div class="p-6 overflow-y-auto flex-1" id="modalContent">
<!-- 模态框内容将通过JavaScript动态生成 -->
</div>
<div class="p-4 border-t border-gray-100 flex justify-end">
<button id="closeModalBtn2" class="bg-gray-100 text-gray-700 px-4 py-2 rounded hover:bg-gray-200 transition-colors">
关闭
</button>
</div>
</div>
</div>
</div>
</main>
<!-- 页脚 -->
<footer class="bg-white border-t border-gray-200 py-6">
<div class="container mx-auto px-4">
<div class="flex flex-col md:flex-row justify-between items-center">
<div class="mb-4 md:mb-0">
<p class="text-neutral text-sm">© 2023 A股指数监控工具. 数据仅供参考,不构成投资建议.</p>
</div>
<div class="flex space-x-6">
<a href="#" class="text-neutral hover:text-primary transition-colors text-sm">关于我们</a>
<a href="#" class="text-neutral hover:text-primary transition-colors text-sm">使用帮助</a>
<a href="#" class="text-neutral hover:text-primary transition-colors text-sm">数据来源</a>
<a href="#" class="text-neutral hover:text-primary transition-colors text-sm">联系我们</a>
</div>
</div>
</div>
</footer>
<!-- 数据更新提示 -->
<div id="updateToast" class="fixed bottom-4 right-4 bg-primary text-white px-4 py-2 rounded-lg shadow-lg transform translate-y-20 opacity-0 transition-all duration-300 flex items-center">
<i class="fa fa-refresh mr-2"></i>
<span>数据已更新</span>
</div>
<!-- 错误提示 -->
<div id="errorToast" class="fixed bottom-4 right-4 bg-fall text-white px-4 py-2 rounded-lg shadow-lg transform translate-y-20 opacity-0 transition-all duration-300 flex items-center">
<i class="fa fa-exclamation-circle mr-2"></i>
<span id="errorMessage">数据加载失败</span>
</div>
<!-- 成功提示 -->
<div id="successToast" class="fixed bottom-4 right-4 bg-rise text-white px-4 py-2 rounded-lg shadow-lg transform translate-y-20 opacity-0 transition-all duration-300 flex items-center">
<i class="fa fa-check-circle mr-2"></i>
<span id="successMessage">操作成功</span>
</div>
<script>
// 全局状态管理
const appState = {
currentPage: 'market',
currentIndex: 'sse',
currentPeriod: 'day',
refreshIntervalId: null,
indexData: {},
sectorData: [],
newsData: [],
settings: {
refreshInterval: 10,
refreshWhenClosed: false,
darkMode: false,
animateChanges: true,
showVolume: true,
alertEnabled: false,
riseThreshold: 1.0,
fallThreshold: 1.0
}
};
// 指数代码映射
const indexCodes = {
sse: { code: '000001', name: '上证指数', market: 'sh' },
szse: { code: '399001', name: '深证成指', market: 'sz' },
gem: { code: '399006', name: '创业板指', market: 'sz' },
star: { code: '000688', name: '科创50', market: 'sh' }
};
// 行业板块代码
const sectorCodes = [
{ name: '半导体', code: '881121' },
{ name: '新能源', code: '881151' },
{ name: '医疗健康', code: '881130' },
{ name: '证券', code: '881157' },
{ name: '银行', code: '881155' },
{ name: '房地产', code: '881153' },
{ name: '国防军工', code: '881124' },
{ name: '计算机', code: '881126' }
];
// 模拟新闻数据
const mockNewsData = [
{
id: 1,
title: '上证指数震荡上行,科技板块领涨',
source: '证券时报',
time: '2023-06-15 09:35',
content: '今日开盘后,上证指数小幅低开后迅速回升,科技板块表现活跃,半导体、人工智能等板块涨幅居前。市场交投情绪回暖,成交量较昨日有所放大。分析人士认为,随着政策面持续向好,市场信心逐步恢复,短期指数有望延续震荡上行趋势。'
},
{
id: 2,
title: '央行开展1000亿元逆回购操作,维持流动性合理充裕',
source: '中国证券报',
time: '2023-06-14 16:20',
content: '央行今日开展1000亿元逆回购操作,中标利率为2.10%,与此前持平。因今日有500亿元逆回购到期,当日实现净投放500亿元。分析指出,央行此举旨在维护半年末流动性平稳,预计后续仍将有结构性货币政策工具推出。'
},
{
id: 3,
title: '新能源板块持续调整,机构称长期投资价值显现',
source: '上海证券报',
time: '2023-06-14 11:05',
content: '近期新能源板块持续调整,光伏、锂电池等细分领域跌幅较大。对此,多家机构表示,板块调整主要受短期情绪影响,长期来看,新能源行业成长逻辑未变,随着产业链供需格局改善和技术进步,优质企业投资价值逐步显现。'
},
{
id: 4,
title: '证监会:进一步规范上市公司信息披露行为',
source: '新华网',
time: '2023-06-13 18:45',
content: '证监会今日发布《关于进一步规范上市公司信息披露的指导意见》,要求上市公司切实提高信息披露质量,确保信息披露的真实、准确、完整、及时、公平。意见明确了对信息披露违规行为的惩戒措施,旨在保护投资者合法权益,维护资本市场秩序。'
},
{
id: 5,
title: '外资持续加仓A股,消费板块获青睐',
source: '第一财经',
time: '2023-06-13 10:20',
content: '近期北向资金持续净流入A股市场,其中消费板块成为外资加仓重点。数据显示,过去一周北向资金累计净买入超过100亿元,食品饮料、家用电器等消费细分领域获显著增持。分析认为,随着国内消费市场逐步复苏,消费板块估值有望得到修复。'
}
];
// 图表实例
let indexChart = null;
let historyChart = null;
// 工具函数 - 格式化数字
function formatNumber(num, decimalPlaces = 2) {
if (num === null || num === undefined || isNaN(num)) return '--';
return num.toLocaleString('zh-CN', {
minimumFractionDigits: decimalPlaces,
maximumFractionDigits: decimalPlaces
});
}
// 工具函数 - 格式化大数字(成交量、成交额)
function formatLargeNumber(num) {
if (num === null || num === undefined || isNaN(num)) return '--';
// 成交量单位处理(手 -> 万手)
if (num > 10000) {
return (num / 10000).toFixed(2) + '万';
}
return num.toFixed(2);
}
// 工具函数 - 格式化成交额(元 -> 亿元)
function formatAmount(num) {
if (num === null || num === undefined || isNaN(num)) return '--';
return (num / 100000000).toFixed(2);
}
// 工具函数 - 显示提示信息
function showToast(elementId, message = '') {
if (message) {
const messageEl = document.getElementById(`${elementId}Message`);
if (messageEl) messageEl.textContent = message;
}
const toast = document.getElementById(elementId);
toast.classList.remove('translate-y-20', 'opacity-0');
setTimeout(() => {
toast.classList.add('translate-y-20', 'opacity-0');
}, 3000);
}
// 页面导航功能
function navigateToPage(pageId) {
// 更新当前页面状态
appState.currentPage = pageId;
// 隐藏所有页面
document.querySelectorAll('.page-content').forEach(page => {
page.classList.add('hidden');
page.classList.remove('page-transition');
});
// 显示目标页面
const targetPage = document.getElementById(`${pageId}Page`);
targetPage.classList.remove('hidden');
// 触发重排后添加动画类
setTimeout(() => {
targetPage.classList.add('page-transition');
}, 10);
// 更新导航栏样式
document.querySelectorAll('.nav-link, .mobile-nav-link').forEach(link => {
if (link.dataset.page === pageId) {
link.classList.add('text-primary', 'font-medium', 'border-b-2', 'border-primary');
link.classList.remove('text-gray-600');
} else {
link.classList.remove('text-primary', 'font-medium', 'border-b-2', 'border-primary');
link.classList.add('text-gray-600');
}
});
// 关闭移动端菜单
document.getElementById('mobileMenu').classList.add('hidden');
// 如果导航到历史数据页面且未初始化图表,初始化图表
if (pageId === 'history' && !historyChart) {
initHistoryChart();
}
// 如果导航到资讯页面且未加载资讯,加载资讯
if (pageId === 'news' && appState.newsData.length === 0) {
loadNewsData();
loadHotSectors();
}
// 如果导航到设置页面,加载设置
if (pageId === 'settings') {
loadSettings();
}
}
// 更新当前时间
function updateCurrentTime() {
const now = new Date();
const year = now.getFullYear();
const month = String(now.getMonth() + 1).padStart(2, '0');
const day = String(now.getDate()).padStart(2, '0');
const hours = String(now.getHours()).padStart(2, '0');
const minutes = String(now.getMinutes()).padStart(2, '0');
const seconds = String(now.getSeconds()).padStart(2, '0');
const timeString = `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
document.getElementById('currentTime').textContent = timeString;
// 设置历史数据默认日期范围(最近30天)
if (!document.getElementById('historyStartDate').value) {
const startDate = new Date();
startDate.setDate(now.getDate() - 30);
document.getElementById('historyStartDate').value = startDate.toISOString().split('T')[0];
document.getElementById('historyEndDate').value = now.toISOString().split('T')[0];
}
}
// 更新最后更新时间
function updateLastUpdateTime() {
const now = new Date();
const hours = String(now.getHours()).padStart(2, '0');
const minutes = String(now.getMinutes()).padStart(2, '0');
const seconds = String(now.getSeconds()).padStart(2, '0');
document.getElementById('lastUpdateTime').textContent = `最后更新: ${hours}:${minutes}:${seconds}`;
}
// 检查市场状态
function checkMarketStatus() {
const now = new Date();
const day = now.getDay();
const hours = now.getHours();
const minutes = now.getMinutes();
const statusEl = document.getElementById('marketStatus');
const indicatorEl = document.getElementById('marketStatusIndicator');
// 周末休市
if (day === 0 || day === 6) {
statusEl.textContent = '休市中';
indicatorEl.className = 'inline-block w-3 h-3 rounded-full bg-neutral mr-2';
indicatorEl.classList.remove('animate-pulse');
return false;
}
// 交易日时间: 9:30-11:30, 13:00-15:00
const isMorningSession = (hours === 9 && minutes >= 30) || (hours > 9 && hours < 11) || (hours === 11 && minutes <= 30);
const isAfternoonSession = (hours === 13 && minutes >= 0) || (hours > 13 && hours < 15) || (hours === 15 && minutes === 0);
if (isMorningSession || isAfternoonSession) {
statusEl.textContent = '交易中';
indicatorEl.className = 'inline-block w-3 h-3 rounded-full bg-rise mr-2 animate-pulse';
return true;
} else {
statusEl.textContent = '休市中';
indicatorEl.className = 'inline-block w-3 h-3 rounded-full bg-neutral mr-2';
indicatorEl.classList.remove('animate-pulse');
return false;
}
}
// 从公开数据源获取指数数据
async function fetchIndexData(indexType) {
try {
const indexInfo = indexCodes[indexType];
// 使用公开的财经数据接口获取数据
const url = `https://hq.sinajs.cn/list=${indexInfo.market}${indexInfo.code}`;
const response = await fetch(url);
const data = await response.text();
// 解析数据
// 新浪财经接口返回格式: var hq_str_sh000001="上证指数,3286.45,23.12,0.71,3265.78,3291.23,3262.15,3263.33,283000000,325600000000";
const match = data.match(/"(.*?)"/);
if (!match) throw new Error('数据格式错误');
const parts = match[1].split(',');
if (parts.length < 9) throw new Error('数据不完整');
const result = {
name: parts[0],
price: parseFloat(parts[1]),
change: parseFloat(parts[2]),
percent: parseFloat(parts[3]),
open: parseFloat(parts[4]),
high: parseFloat(parts[5]),
low: parseFloat(parts[6]),
prevClose: parseFloat(parts[7]),
volume: parseFloat(parts[8]),
amount: parseFloat(parts[9])
};
// 保存到全局状态
appState.indexData[indexType] = result;
return result;
} catch (error) {
console.error(`获取${indexType}数据失败:`, error);
showToast('errorToast', `获取${indexCodes[indexType].name}数据失败`);
return null;
}
}
// 更新指数卡片数据
async function updateIndexCard(indexType) {
const data = await fetchIndexData(indexType);
if (!data) return false;
// 更新指数值
const indexEl = document.getElementById(`${indexType}Index`);
if (appState.settings.animateChanges && indexEl.textContent !== '--') {
animateValueChange(indexEl, parseFloat(indexEl.textContent.replace(/,/g, '')), data.price);
} else {
indexEl.textContent = formatNumber(data.price);
}
// 更新涨跌额和涨跌幅
const changeEl = document.getElementById(`${indexType}Change`);
const percentEl = document.getElementById(`${indexType}Percent`);
// 设置涨跌颜色和内容
if (data.change >= 0) {
changeEl.className = 'rise font-medium';
percentEl.className = 'rise ml-2 text-sm';
changeEl.textContent = `+${formatNumber(data.change)}`;
percentEl.textContent = `+${formatNumber(data.percent, 2)}%`;
} else {
changeEl.className = 'fall font-medium';
percentEl.className = 'fall ml-2 text-sm';
changeEl.textContent = formatNumber(data.change);
percentEl.textContent = `${formatNumber(data.percent, 2)}%`;
}
// 更新其他数据
document.getElementById(`${indexType}Open`).textContent = formatNumber(data.open);
document.getElementById(`${indexType}High`).textContent = formatNumber(data.high);
document.getElementById(`${indexType}Low`).textContent = formatNumber(data.low);
document.getElementById(`${indexType}Close`).textContent = formatNumber(data.prevClose);
// 根据设置决定是否显示成交量
const volumeEl = document.getElementById(`${indexType}Volume`);
const amountEl = document.getElementById(`${indexType}Amount`);
if (appState.settings.showVolume) {
volumeEl.textContent = formatLargeNumber(data.volume);
amountEl.textContent = formatAmount(data.amount) + '亿';
} else {
volumeEl.textContent = '隐藏';
amountEl.textContent = '隐藏';
}
// 检查是否触发预警
if (appState.settings.alertEnabled) {
checkIndexAlert(indexType, data);
}
return true;
}
// 检查指数是否触发预警
function checkIndexAlert(indexType, data) {
if (Math.abs(data.percent) >= appState.settings.riseThreshold && data.percent > 0) {
showToast('errorToast', `${indexCodes[indexType].name}上涨超过${appState.settings.riseThreshold}%`);
} else if (Math.abs(data.percent) >= appState.settings.fallThreshold && data.percent < 0) {
showToast('errorToast', `${indexCodes[indexType].name}下跌超过${appState.settings.fallThreshold}%`);
}
}
// 批量更新所有指数卡片
async function updateAllIndexCards() {
const indexTypes = Object.keys(indexCodes);
let allSuccess = true;
for (const type of indexTypes) {
const success = await updateIndexCard(type);
if (!success) allSuccess = false;
}
if (allSuccess) {
updateLastUpdateTime();
showToast('updateToast', '数据已更新');
}
return allSuccess;
}
// 从数据源获取板块数据
async function fetchSectorData(sector) {
try {
const url = `https://hq.sinajs.cn/list=zs${sector.code}`;
const response = await fetch(url);
const data = await response.text();
// 解析板块数据
const match = data.match(/"(.*?)"/);
if (!match) throw new Error('板块数据格式错误');
const parts = match[1].split(',');
if (parts.length < 9) throw new Error('板块数据不完整');
return {
name: sector.name,
code: sector.code,
price: parseFloat(parts[1]),
change: parseFloat(parts[2]),
percent: parseFloat(parts[3]),
volume: parseFloat(parts[8]),
amount: parseFloat(parts[9])
};
} catch (error) {
console.error(`获取${sector.name}数据失败:`, error);
return {
name: sector.name,
code: sector.code,
price: null,
change: null,
percent: null,
volume: null,
amount: null
};
}
}
// 更新行业板块表格
async function updateSectorTable() {
const tableBody = document.getElementById('sectorTable');
tableBody.innerHTML = '';
// 并行获取所有板块数据
const sectorPromises = sectorCodes.map(sector => fetchSectorData(sector));
const sectorsData = await Promise.all(sectorPromises);
// 保存到全局状态
appState.sectorData = sectorsData;
sectorsData.forEach(sector => {
const row = document.createElement('tr');
row.className = 'hover:bg-gray-50 transition-colors cursor-pointer';
row.addEventListener('click', () => {
showSectorDetail(sector);
});
// 涨跌颜色判断
const changeClass = sector.change !== null && sector.change >= 0 ? 'text-rise' : 'text-fall';
row.innerHTML = `
<td class="px-6 py-4 whitespace-nowrap">
<div class="font-medium">${sector.name}</div>
</td>
<td class="px-6 py-4 whitespace-nowrap text-neutral">
<div>${sector.code}</div>
</td>
<td class="px-6 py-4 whitespace-nowrap text-right">
<div>${formatNumber(sector.price)}</div>
</td>
<td class="px-6 py-4 whitespace-nowrap text-right ${changeClass}">
<div>${sector.percent !== null ? `${sector.percent >= 0 ? '+' : ''}${formatNumber(sector.percent, 2)}%` : '--'}</div>
</td>
<td class="px-6 py-4 whitespace-nowrap text-right ${changeClass}">
<div>${sector.change !== null ? `${sector.change >= 0 ? '+' : ''}${formatNumber(sector.change)}` : '--'}</div>
</td>
<td class="px-6 py-4 whitespace-nowrap text-right text-neutral">
<div>${formatLargeNumber(sector.volume)}</div>
</td>
<td class="px-6 py-4 whitespace-nowrap text-right text-neutral">
<div>${sector.amount !== null ? formatAmount(sector.amount) : '--'}</div>
</td>
`;
tableBody.appendChild(row);
});
}
// 显示板块详情
function showSectorDetail(sector) {
const modalTitle = document.getElementById('modalTitle');
const modalContent = document.getElementById('modalContent');
modalTitle.textContent = `${sector.name}(${sector.code}) 详情`;
// 涨跌颜色判断
const changeClass = sector.change !== null && sector.change >= 0 ? 'text-rise' : 'text-fall';
modalContent.innerHTML = `
<div class="text-center mb-6">
<div class="text-3xl font-bold mb-2">${formatNumber(sector.price)}</div>
<div class="flex items-center justify-center">
<span class="${changeClass} text-xl font-medium">${sector.change !== null ? (sector.change >= 0 ? '+' : '') + formatNumber(sector.change) : '--'}</span>
<span class="${changeClass} ml-3 text-xl">${sector.percent !== null ? (sector.percent >= 0 ? '+' : '') + formatNumber(sector.percent, 2) + '%' : '--'}</span>
</div>
</div>
<div class="grid grid-cols-2 md:grid-cols-4 gap-4 mb-6">
<div class="bg-light p-3 rounded">
<div class="text-sm text-neutral">今开</div>
<div class="font-medium">${formatNumber(sector.open)}</div>
</div>
<div class="bg-light p-3 rounded">
<div class="text-sm text-neutral">最高</div>
<div class="font-medium">${formatNumber(sector.high)}</div>
</div>
<div class="bg-light p-3 rounded">
<div class="text-sm text-neutral">最低</div>
<div class="font-medium">${formatNumber(sector.low)}</div>
</div>
<div class="bg-light p-3 rounded">
<div class="text-sm text-neutral">昨收</div>
<div class="font-medium">${formatNumber(sector.prevClose)}</div>
</div>
</div>
<div class="mb-6">
<h4 class="font-medium mb-3">板块简介</h4>
<p class="text-sm text-neutral">${sector.name}板块包含该行业内的主要上市公司,反映了该行业的整体表现。板块成分股涵盖了行业内规模较大、流动性较好的公司,是投资者了解行业景气度的重要参考指标。</p>
</div>
<div>
<h4 class="font-medium mb-3">近期表现</h4>
<div class="h-[200px]">
<canvas id="sectorDetailChart"></canvas>
</div>
</div>
`;
// 显示模态框
document.getElementById('indexDetailModal').classList.remove('hidden');
// 生成板块详情图表
generateSectorDetailChart(sector);
}
// 生成板块详情图表
function generateSectorDetailChart(sector) {
const ctx = document.getElementById('sectorDetailChart').getContext('2d');
// 生成模拟的板块历史数据
const generateMockData = () => {
const data = [];
let value = sector.price || 5000;
for (let i = 14; i >= 0; i--) {
const date = new Date();
date.setDate(date.getDate() - i);
// 跳过周末
const day = date.getDay();
if (day === 0 || day === 6) continue;
// 随机波动
const change = (Math.random() - 0.5) * 2 * 50;
value += change;
data.push({
date: `${date.getMonth() + 1}/${date.getDate()}`,
value: Number(value.toFixed(2))
});
}
return data;
};
const mockData = generateMockData();
const labels = mockData.map(item => item.date);
const values = mockData.map(item => item.value);
new Chart(ctx, {
type: 'line',
data: {
labels: labels,
datasets: [{
label: sector.name,
data: values,
borderColor: '#165DFF',
backgroundColor: 'rgba(22, 93, 255, 0.1)',
borderWidth: 2,
pointRadius: 3,
pointBackgroundColor: '#165DFF',
fill: true,
tension: 0.2
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
display: false
}
},
scales: {
x: {
grid: {
display: false
}
},
y: {
grid: {
color: 'rgba(0, 0, 0, 0.05)'
}
}
}
}
});
}
// 从数据源获取K线数据
async function fetchKlineData(indexType, period = 'day') {
try {
const indexInfo = indexCodes[indexType];
// 这里使用模拟数据,实际应用中需要替换为真实的K线数据接口
if (period === 'day') {
// 日K线数据 - 模拟最近30天
return generateMockKlineData(30, indexType);
} else {
// 分时数据 - 模拟当天交易时段
return generateMockMinuteData(indexType);
}
} catch (error) {
console.error(`获取${indexType}K线数据失败:`, error);
showToast('errorToast', `获取${indexCodes[indexType].name}图表数据失败`);
return null;
}
}
// 生成模拟K线数据
function generateMockKlineData(days, indexType) {
// 基础值
const baseValues = {
sse: 3260,
szse: 11500,
gem: 2360,
star: 1080
};
// 波动率
const volatility = {
sse: 15,
szse: 80,
gem: 30,
star: 10
};
const data = [];
let value = baseValues[indexType];
const today = new Date();
for (let i = days; i >= 0; i--) {
const date = new Date();
date.setDate(today.getDate() - i);
// 跳过周末
const day = date.getDay();
if (day === 0 || day === 6) continue;
// 随机波动
const change = (Math.random() - 0.5) * 2 * volatility[indexType];
value += change;
const open = value;
const high = value + Math.random() * volatility[indexType] * 0.5;
const low = value - Math.random() * volatility[indexType] * 0.5;
const close = value;
data.push({
date: `${date.getMonth() + 1}/${date.getDate()}`,
open: Number(open.toFixed(2)),
high: Number(high.toFixed(2)),
low: Number(low.toFixed(2)),
close: Number(close.toFixed(2)),
volume: Math.floor(Math.random() * 100000000) + 100000000
});
}
return data;
}
// 生成模拟分时数据
function generateMockMinuteData(indexType) {
// 基础值
const baseValues = {
sse: 3263.33,
szse: 11498.56,
gem: 2364.23,
star: 1081.65
};
// 波动率
const volatility = {
sse: 2,
szse: 8,
gem: 4,
star: 1.5
};
// 生成时间标签(9:30-15:00的交易时间)
const generateTimeLabels = () => {
const labels = [];
let hour = 9;
let minute = 30;
while (hour < 15 || (hour === 15 && minute === 0)) {
labels.push(`${hour.toString().padStart(2, '0')}:${minute.toString().padStart(2, '0')}`);
minute += 5; // 每5分钟一个数据点
if (minute >= 60) {
minute = 0;
hour++;
// 跳过午休时间(11:30-13:00)
if (hour === 11 && minute === 30) {
hour = 13;
minute = 0;
}
}
}
return labels;
};
const timeLabels = generateTimeLabels();
const data = [];
let value = baseValues[indexType];
timeLabels.forEach(time => {
// 随机波动
const change = (Math.random() - 0.5) * 2 * volatility[indexType];
value += change;
data.push({
time: time,
price: Number(value.toFixed(2)),
volume: Math.floor(Math.random() * 1000000) + 500000
});
});
return data;
}
// 初始化主图表
function initChart() {
const ctx = document.getElementById('indexChart').getContext('2d');
// 创建空图表
indexChart = new Chart(ctx, {
type: 'line',
data: {
labels: [],
datasets: [{
label: indexCodes[appState.currentIndex].name,
data: [],
borderColor: '#165DFF',
backgroundColor: 'rgba(22, 93, 255, 0.1)',
borderWidth: 2,
pointRadius: 0,
pointHoverRadius: 4,
pointBackgroundColor: '#165DFF',
fill: true,
tension: 0.2,
spanGaps: false
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
interaction: {
mode: 'index',
intersect: false,
},
plugins: {
legend: {
display: false
},
tooltip: {
backgroundColor: 'rgba(0, 0, 0, 0.7)',
padding: 10,
titleFont: {
size: 14
},
bodyFont: {
size: 13
},
callbacks: {
label: function(context) {
return `指数: ${context.raw}`;
}
}
}
},
scales: {
x: {
grid: {
display: false
},
ticks: {
maxTicksLimit: 8
}
},
y: {
position: 'right',
grid: {
color: 'rgba(0, 0, 0, 0.05)'
},
ticks: {
callback: function(value) {
return value.toLocaleString();
}
}
}
},
animation: {
duration: 1000,
easing: 'easeOutQuart'
}
}
});
// 加载实际数据
updateChartData(appState.currentIndex, appState.currentPeriod);
}
// 初始化历史数据图表
function initHistoryChart() {
const ctx = document.getElementById('historyChart').getContext('2d');
// 创建空图表
historyChart = new Chart(ctx, {
type: 'line',
data: {
labels: [],
datasets: [{
label: indexCodes[appState.currentIndex].name,
data: [],
borderColor: '#165DFF',
backgroundColor: 'rgba(22, 93, 255, 0.1)',
borderWidth: 2,
pointRadius: 3,
pointBackgroundColor: '#165DFF',
fill: true,
tension: 0.2
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
interaction: {
mode: 'index',
intersect: false,
},
plugins: {
legend: {
display: true,
position: 'top'
},
tooltip: {
backgroundColor: 'rgba(0, 0, 0, 0.7)',
padding: 10,
callbacks: {
label: function(context) {
return `指数: ${context.raw}`;
}
}
}
},
scales: {
x: {
grid: {
display: false
}
},
y: {
position: 'right',
grid: {
color: 'rgba(0, 0, 0, 0.05)'
},
ticks: {
callback: function(value) {
return value.toLocaleString();
}
}
}
}
}
});
}
// 更新图表数据
async function updateChartData(indexType, period = 'day') {
// 显示加载指示器
document.getElementById('chartLoading').style.display = 'flex';
try {
const data = await fetchKlineData(indexType, period);
if (!data || data.length === 0) {
throw new Error('未获取到图表数据');
}
// 更新全局状态
appState.currentIndex = indexType;
appState.currentPeriod = period;
// 准备图表数据
const labels = [];
const priceData = [];
if (period === 'day') {
// 日K线数据
data.forEach(item => {
labels.push(item.date);
priceData.push(item.close);
});
} else {
// 分时数据
data.forEach(item => {
labels.push(item.time);
priceData.push(item.price);
});
}
// 设置图表颜色
const colors = {
sse: '#165DFF',
szse: '#722ED1',
gem: '#EB0AA4',
star: '#F5319D'
};
// 更新图表
indexChart.data.labels = labels;
indexChart.data.datasets[0].data = priceData;
indexChart.data.datasets[0].label = indexCodes[indexType].name;
indexChart.data.datasets[0].borderColor = colors[indexType] || '#165DFF';
indexChart.data.datasets[0].backgroundColor = `${colors[indexType] || '#165DFF'}33`; // 添加透明度
indexChart.data.datasets[0].pointBackgroundColor = colors[indexType] || '#165DFF';
indexChart.update();
} catch (error) {
console.error('更新图表失败:', error);
showToast('errorToast', '图表数据更新失败');
} finally {
// 隐藏加载指示器
document.getElementById('chartLoading').style.display = 'none';
}
}
// 查询历史数据
async function queryHistoryData() {
const indexType = document.getElementById('historyIndexSelect').value;
const period = document.getElementById('historyPeriodSelect').value;
const startDate = document.getElementById('historyStartDate').value;
const endDate = document.getElementById('historyEndDate').value;
if (!startDate || !endDate) {
showToast('errorToast', '请选择日期范围');
return;
}
// 显示加载状态
document.getElementById('historyChartEmpty').style.display = 'none';
document.getElementById('historyChartLoading').style.display = 'flex';
document.getElementById('historyTable').innerHTML = `
<tr>
<td colspan="7" class="px-6 py-10 text-center text-neutral">
<div class="flex flex-col items-center">
<i class="fa fa-circle-o-notch fa-spin text-primary text-xl mb-2"></i>
<p>加载历史数据中...</p>
</div>
</td>
</tr>
`;
try {
// 生成模拟的历史数据
const days = Math.ceil((new Date(endDate) - new Date(startDate)) / (1000 * 60 * 60 * 24));
const data = generateMockKlineData(days, indexType);
// 准备图表数据
const labels = data.map(item => item.date);
const priceData = data.map(item => item.close);
// 设置图表颜色
const colors = {
sse: '#165DFF',
szse: '#722ED1',
gem: '#EB0AA4',
star: '#F5319D'
};
// 更新历史图表
historyChart.data.labels = labels;
historyChart.data.datasets[0].data = priceData;
historyChart.data.datasets[0].label = indexCodes[indexType].name;
historyChart.data.datasets[0].borderColor = colors[indexType] || '#165DFF';
historyChart.data.datasets[0].backgroundColor = `${colors[indexType] || '#165DFF'}33`;
historyChart.data.datasets[0].pointBackgroundColor = colors[indexType] || '#165DFF';
historyChart.update();
// 更新历史表格
updateHistoryTable(data);
} catch (error) {
console.error('查询历史数据失败:', error);
showToast('errorToast', '查询历史数据失败');
document.getElementById('historyTable').innerHTML = `
<tr>
<td colspan="7" class="px-6 py-10 text-center text-neutral">
<p>加载历史数据失败,请重试</p>
</td>
</tr>
`;
} finally {
// 隐藏加载状态
document.getElementById('historyChartLoading').style.display = 'none';
}
}
// 更新历史数据表格
function updateHistoryTable(data) {
const tableBody = document.getElementById('historyTable');
tableBody.innerHTML = '';
// 只显示最近30条记录
const displayData = data.slice(-30).reverse();
displayData.forEach(item => {
// 计算涨跌幅
const prevItem = data[data.indexOf(item) - 1];
const change = prevItem ? item.close - prevItem.close : 0;
const percent = prevItem ? (change / prevItem.close) * 100 : 0;
// 涨跌颜色判断
const changeClass = change >= 0 ? 'text-rise' : 'text-fall';
const row = document.createElement('tr');
row.className = 'hover:bg-gray-50 transition-colors';
row.innerHTML = `
<td class="px-6 py-4 whitespace-nowrap">
<div>${item.date}</div>
</td>
<td class="px-6 py-4 whitespace-nowrap text-right">
<div>${formatNumber(item.open)}</div>
</td>
<td class="px-6 py-4 whitespace-nowrap text-right">
<div>${formatNumber(item.high)}</div>
</td>
<td class="px-6 py-4 whitespace-nowrap text-right">
<div>${formatNumber(item.low)}</div>
</td>
<td class="px-6 py-4 whitespace-nowrap text-right">
<div>${formatNumber(item.close)}</div>
</td>
<td class="px-6 py-4 whitespace-nowrap text-right ${changeClass}">
<div>${percent >= 0 ? '+' : ''}${formatNumber(percent, 2)}%</div>
</td>
<td class="px-6 py-4 whitespace-nowrap text-right text-neutral">
<div>${formatLargeNumber(item.volume)}</div>
</td>
`;
tableBody.appendChild(row);
});
// 更新分页信息
const paginationEl = tableBody.nextElementSibling;
paginationEl.querySelector('.text-sm').textContent = `显示 ${displayData.length} 条记录,共 ${data.length} 条`;
}
// 加载新闻数据
function loadNewsData() {
// 使用模拟新闻数据
appState.newsData = mockNewsData;
const newsListEl = document.getElementById('newsList');
newsListEl.innerHTML = '';
appState.newsData.forEach(news => {
const newsItem = document.createElement('div');
newsItem.className = 'p-6 hover:bg-gray-50 transition-colors cursor-pointer';
newsItem.addEventListener('click', () => showNewsDetail(news));
newsItem.innerHTML = `
<h3 class="font-bold text-lg mb-2 line-clamp-2">${news.title}</h3>
<p class="text-neutral text-sm mb-3 line-clamp-2">${news.content}</p>
<div class="flex justify-between text-xs text-neutral">
<span>${news.source}</span>
<span>${news.time}</span>
</div>
`;
newsListEl.appendChild(newsItem);
});
}
// 显示新闻详情
function showNewsDetail(news) {
const modalTitle = document.getElementById('modalTitle');
const modalContent = document.getElementById('modalContent');
modalTitle.textContent = news.title;
modalContent.innerHTML = `
<div class="text-sm text-neutral mb-4 flex justify-between">
<span>${news.source}</span>
<span>${news.time}</span>
</div>
<div class="prose max-w-none">
<p>${news.content}</p>
<p class="mt-4">市场分析人士表示,当前A股市场处于震荡整理阶段,投资者应保持理性,关注政策面和基本面变化,把握结构性机会。中长期来看,随着经济复苏向好和企业盈利改善,A股市场有望逐步走强。</p>
<p class="mt-4">风险提示:市场有风险,投资需谨慎。本文观点仅供参考,不构成投资建议。</p>
</div>
`;
// 显示模态框
document.getElementById('indexDetailModal').classList.remove('hidden');
}
// 加载更多新闻
function loadMoreNews() {
// 复制现有新闻并修改ID和时间,模拟加载更多
const newNews = appState.newsData.map(news => {
const newDate = new Date(news.time);
newDate.setDate(newDate.getDate() - 1);
return {
...news,
id: news.id + 100,
time: `${newDate.getFullYear()}-${String(newDate.getMonth() + 1).padStart(2, '0')}-${String(newDate.getDate()).padStart(2, '0')} ${String(newDate.getHours()).padStart(2, '0')}:${String(newDate.getMinutes()).padStart(2, '0')}`
};
});
appState.newsData = [...appState.newsData, ...newNews];
// 更新新闻列表
loadNewsData();
showToast('successToast', '已加载更多资讯');
}
// 加载热门板块
function loadHotSectors() {
const hotSectorsEl = document.getElementById('hotSectors');
hotSectorsEl.innerHTML = '';
// 从板块数据中筛选涨幅前5的板块
const sortedSectors = [...appState.sectorData]
.sort((a, b) => (b.percent || 0) - (a.percent || 0))
.slice(0, 5);
sortedSectors.forEach((sector, index) => {
const sectorItem = document.createElement('div');
sectorItem.className = 'flex items-center justify-between py-2 border-b border-gray-100 last:border-0';
// 涨跌颜色判断
const changeClass = sector.change !== null && sector.change >= 0 ? 'text-rise' : 'text-fall';
// 前三名添加特殊标识
const rankClass = index < 3 ? 'bg-primary/10 text-primary' : 'bg-gray-100 text-neutral';
sectorItem.innerHTML = `
<div class="flex items-center">
<span class="w-5 h-5 flex items-center justify-center rounded-full ${rankClass} text-xs mr-3">${index + 1}</span>
<span>${sector.name}</span>
</div>
<span class="${changeClass}">${sector.percent !== null ? `${sector.percent >= 0 ? '+' : ''}${formatNumber(sector.percent, 2)}%` : '--'}</span>
`;
sectorItem.addEventListener('click', () => showSectorDetail(sector));
hotSectorsEl.appendChild(sectorItem);
});
}
// 加载设置
function loadSettings() {
// 从全局状态加载设置到表单
document.getElementById('defaultRefreshInterval').value = appState.settings.refreshInterval;
document.getElementById('refreshWhenClosed').value = appState.settings.refreshWhenClosed ? 'yes' : 'no';
document.getElementById('darkModeToggle').checked = appState.settings.darkMode;
document.getElementById('animateChangesToggle').checked = appState.settings.animateChanges;
document.getElementById('showVolumeToggle').checked = appState.settings.showVolume;
document.getElementById('alertToggle').checked = appState.settings.alertEnabled;
document.getElementById('riseThreshold').value = appState.settings.riseThreshold;
document.getElementById('fallThreshold').value = appState.settings.fallThreshold;
// 显示/隐藏预警阈值设置
document.getElementById('alertThresholdContainer').classList.toggle('hidden', !appState.settings.alertEnabled);
// 应用深色模式
applyDarkMode(appState.settings.darkMode);
}
// 保存设置
function saveSettings() {
// 从表单获取设置并保存到全局状态
appState.settings.refreshInterval = parseInt(document.getElementById('defaultRefreshInterval').value);
appState.settings.refreshWhenClosed = document.getElementById('refreshWhenClosed').value === 'yes';
appState.settings.darkMode = document.getElementById('darkModeToggle').checked;
appState.settings.animateChanges = document.getElementById('animateChangesToggle').checked;
appState.settings.showVolume = document.getElementById('showVolumeToggle').checked;
appState.settings.alertEnabled = document.getElementById('alertToggle').checked;
appState.settings.riseThreshold = parseFloat(document.getElementById('riseThreshold').value);
appState.settings.fallThreshold = parseFloat(document.getElementById('fallThreshold').value);
// 应用设置
applySettings();
// 显示成功提示
showToast('successToast', '设置已保存');
// 如果在设置页面,切换到市场页面
navigateToPage('market');
}
// 应用设置
function applySettings() {
// 应用刷新频率设置
document.getElementById('refreshInterval').value = appState.settings.refreshInterval;
setupAutoRefresh();
// 应用深色模式
applyDarkMode(appState.settings.darkMode);
// 重新加载指数卡片以应用成交量显示设置
updateAllIndexCards();
// 显示/隐藏预警阈值设置
document.getElementById('alertThresholdContainer').classList.toggle('hidden', !appState.settings.alertEnabled);
}
// 应用深色模式
function applyDarkMode(enable) {
if (enable) {
document.body.classList.add('bg-gray-900', 'text-white');
document.body.classList.remove('bg-gray-50', 'text-dark');
} else {
document.body.classList.add('bg-gray-50', 'text-dark');
document.body.classList.remove('bg-gray-900', 'text-white');
}
}
// 显示指数详情
function showIndexDetail(indexType) {
const indexInfo = indexCodes[indexType];
const indexData = appState.indexData[indexType];
if (!indexData) {
showToast('errorToast', '暂无该指数数据');
return;
}
const modalTitle = document.getElementById('modalTitle');
const modalContent = document.getElementById('modalContent');
modalTitle.textContent = `${indexInfo.name}(${indexInfo.code}.${indexInfo.market.toUpperCase()}) 详情`;
// 涨跌颜色判断
const changeClass = indexData.change >= 0 ? 'text-rise' : 'text-fall';
modalContent.innerHTML = `
<div class="text-center mb-6">
<div class="text-3xl font-bold mb-2">${formatNumber(indexData.price)}</div>
<div class="flex items-center justify-center">
<span class="${changeClass} text-xl font-medium">${indexData.change >= 0 ? '+' : ''}${formatNumber(indexData.change)}</span>
<span class="${changeClass} ml-3 text-xl">${indexData.change >= 0 ? '+' : ''}${formatNumber(indexData.percent, 2)}%</span>
</div>
</div>
<div class="grid grid-cols-2 md:grid-cols-4 gap-4 mb-6">
<div class="bg-light p-3 rounded">
<div class="text-sm text-neutral">今开</div>
<div class="font-medium">${formatNumber(indexData.open)}</div>
</div>
<div class="bg-light p-3 rounded">
<div class="text-sm text-neutral">最高</div>
<div class="font-medium">${formatNumber(indexData.high)}</div>
</div>
<div class="bg-light p-3 rounded">
<div class="text-sm text-neutral">最低</div>
<div class="font-medium">${formatNumber(indexData.low)}</div>
</div>
<div class="bg-light p-3 rounded">
<div class="text-sm text-neutral">昨收</div>
<div class="font-medium">${formatNumber(indexData.prevClose)}</div>
</div>
</div>
<div class="mb-6">
<h4 class="font-medium mb-3">指数简介</h4>
<p class="text-sm text-neutral">${getIndexDescription(indexType)}</p>
</div>
<div>
<h4 class="font-medium mb-3">近期表现</h4>
<div class="h-[200px]">
<canvas id="indexDetailChart"></canvas>
</div>
</div>
`;
// 显示模态框
document.getElementById('indexDetailModal').classList.remove('hidden');
// 生成指数详情图表
generateIndexDetailChart(indexType);
}
// 获取指数描述
function getIndexDescription(indexType) {
const descriptions = {
sse: '上证指数(000001.SH)是上海证券交易所编制的,以上海证券交易所挂牌上市的全部股票为计算范围,以发行量为权数的加权综合股价指数。上证指数反映了上海证券交易市场的总体走势。',
szse: '深证成指(399001.SZ)是深圳证券交易所的主要股指。它是按一定标准选出500家有代表性的上市公司作为样本股,用样本股的自由流通股数作为权数,采用派氏加权法编制而成的股价指标。',
gem: '创业板指(399006.SZ)是反映创业板市场整体走势的核心指数,由创业板中市值大、流动性好的100家上市公司组成,具有较高的市场代表性。',
star: '科创50(000688.SH)由上海证券交易所科创板中市值大、流动性好的50只证券组成,反映最具市场代表性的一批科创企业的整体表现。'
};
return descriptions[indexType] || '';
}
// 生成指数详情图表
function generateIndexDetailChart(indexType) {
const ctx = document.getElementById('indexDetailChart').getContext('2d');
// 获取该指数的K线数据
fetchKlineData(indexType, 'day').then(data => {
if (!data) return;
const labels = data.map(item => item.date);
const values = data.map(item => item.close);
new Chart(ctx, {
type: 'line',
data: {
labels: labels,
datasets: [{
label: indexCodes[indexType].name,
data: values,
borderColor: '#165DFF',
backgroundColor: 'rgba(22, 93, 255, 0.1)',
borderWidth: 2,
pointRadius: 3,
pointBackgroundColor: '#165DFF',
fill: true,
tension: 0.2
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
display: false
}
},
scales: {
x: {
grid: {
display: false
}
},
y: {
grid: {
color: 'rgba(0, 0, 0, 0.05)'
}
}
}
}
});
});
}
// 数值变化动画
function animateValueChange(element, start, end, duration = 1000) {
let startTimestamp = null;
const step = (timestamp) => {
if (!startTimestamp) startTimestamp = timestamp;
const progress = Math.min((timestamp - startTimestamp) / duration, 1);
const value = start + progress * (end - start);
element.textContent = formatNumber(value);
if (progress < 1) {
window.requestAnimationFrame(step);
}
};
window.requestAnimationFrame(step);
}
// 自动刷新功能
function setupAutoRefresh() {
// 清除现有定时器
if (appState.refreshIntervalId) {
clearInterval(appState.refreshIntervalId);
}
const interval = parseInt(document.getElementById('refreshInterval').value) * 1000;
// 如果选择不自动刷新,直接返回
if (interval === 0) return;
// 设置新的定时器
appState.refreshIntervalId = setInterval(async () => {
// 检查是否在交易时间或设置了休市时刷新
const isTrading = checkMarketStatus();
if (isTrading || appState.settings.refreshWhenClosed) {
const success = await updateAllIndexCards();
if (success) {
// 每3次数据刷新,更新一次图表
const now = new Date();
if (now.getMinutes() % 3 === 0) {
updateChartData(appState.currentIndex, appState.currentPeriod);
updateSectorTable();
}
}
}
}, interval);
}
// 关闭模态框
function closeModal() {
document.getElementById('indexDetailModal').classList.add('hidden');
}
// 页面加载完成后初始化
document.addEventListener('DOMContentLoaded', () => {
// 设置相对定位,使图表加载指示器正确显示
document.querySelector('.h-\\[400px\\]').style.position = 'relative';
// 初始化时间更新
updateCurrentTime();
setInterval(updateCurrentTime, 1000);
// 检查市场状态
checkMarketStatus();
setInterval(checkMarketStatus, 60000); // 每分钟检查一次
// 初始化图表
initChart();
// 加载指数数据
updateAllIndexCards();
// 加载行业板块数据
updateSectorTable();
// 设置自动刷新
setupAutoRefresh();
// 导航链接事件绑定
document.querySelectorAll('.nav-link, .mobile-nav-link').forEach(link => {
link.addEventListener('click', (e) => {
e.preventDefault();
navigateToPage(link.dataset.page);
});
});
// 移动端菜单切换
document.getElementById('mobileMenuBtn').addEventListener('click', () => {
const mobileMenu = document.getElementById('mobileMenu');
mobileMenu.classList.toggle('hidden');
});
// 手动刷新按钮
document.getElementById('refreshBtn').addEventListener('click', async () => {
// 显示加载状态
document.getElementById('refreshBtn').querySelector('i').classList.add('fa-spin');
try {
await updateAllIndexCards();
await updateSectorTable();
updateChartData(appState.currentIndex, appState.currentPeriod);
} finally {
// 隐藏加载状态
document.getElementById('refreshBtn').querySelector('i').classList.remove('fa-spin');
}
});
// 图表周期切换
document.querySelectorAll('.chart-period-btn').forEach(btn => {
btn.addEventListener('click', () => {
// 更新按钮样式
document.querySelectorAll('.chart-period-btn').forEach(b => {
b.classList.remove('bg-primary', 'text-white');
b.classList.add('bg-white', 'text-gray-600', 'hover:bg-gray-100');
});
btn.classList.remove('bg-white', 'text-gray-600', 'hover:bg-gray-100');
btn.classList.add('bg-primary', 'text-white');
// 更新图表数据
updateChartData(appState.currentIndex, btn.dataset.period);
});
});
// 指数图表切换
document.querySelectorAll('.index-chart-btn').forEach(btn => {
btn.addEventListener('click', () => {
// 更新按钮样式
document.querySelectorAll('.index-chart-btn').forEach(b => {
b.classList.remove('bg-primary', 'text-white');
b.classList.add('bg-white', 'text-gray-600', 'hover:bg-gray-100');
});
btn.classList.remove('bg-white', 'text-gray-600', 'hover:bg-gray-100');
btn.classList.add('bg-primary', 'text-white');
// 更新图表数据
updateChartData(btn.dataset.index, appState.currentPeriod);
});
});
// 指数卡片点击事件(显示详情)
document.querySelectorAll('[data-detail]').forEach(card => {
card.addEventListener('click', () => {
showIndexDetail(card.dataset.detail);
});
});
// 关闭模态框按钮
document.getElementById('closeModalBtn').addEventListener('click', closeModal);
document.getElementById('closeModalBtn2').addEventListener('click', closeModal);
// 点击模态框外部关闭
document.getElementById('indexDetailModal').addEventListener('click', (e) => {
if (e.target === document.getElementById('indexDetailModal')) {
closeModal();
}
});
// 历史数据查询按钮
document.getElementById('queryHistoryBtn').addEventListener('click', queryHistoryData);
// 加载更多新闻按钮
document.getElementById('loadMoreNews').addEventListener('click', loadMoreNews);
// 保存设置按钮
document.getElementById('saveSettingsBtn').addEventListener('click', saveSettings);
// 预警开关切换
document.getElementById('alertToggle').addEventListener('change', (e) => {
document.getElementById('alertThresholdContainer').classList.toggle('hidden', !e.target.checked);
});
// 刷新频率变更
document.getElementById('refreshInterval').addEventListener('change', setupAutoRefresh);
// 滚动时导航栏效果
window.addEventListener('scroll', () => {
const header = document.querySelector('header');
if (window.scrollY > 10) {
header.classList.add('py-2', 'shadow');
header.classList.remove('py-3', 'shadow-md');
} else {
header.classList.add('py-3', 'shadow-md');
header.classList.remove('py-2', 'shadow');
}
});
});
</script>
</body>
</html>