mirror of
https://github.com/KingHWJ/WeReadBoard.git
synced 2026-11-03 18:24:58 +08:00
1. 数据源切换至 MySQL: - app.py 从 SQLite 切换到 PyMySQL,所有 API 读取 MySQL weread_dashboard 数据库 - 新增 db_required 装饰器统一管理数据库连接和异常处理 - 新增 /api/health 健康检查接口,检测 MySQL 连接状态 - MySQL 连接失败时返回 HTTP 503 和告警信息 - 修复 fetched_at 字段 Unix 时间戳到 MySQL TIMESTAMP 的兼容问题 2. 前端数据库连接告警: - 页面加载时优先调用 /api/health 检查 MySQL 连接 - 连接失败时显示固定告警横幅,阻止后续数据加载 - CSS 告警横幅样式(黄色背景、下滑动画) 3. 代码中文注释: - app.py:所有函数、API 接口、工具函数添加完整中文文档注释 - fetch_data.py:所有 SQLite 建表语句添加列级中文注释 - migrate_to_mysql.py:保留已有中文注释 4. 技术文档与元数据文档: - docs/TECHNICAL_DOC.md:项目概述、系统架构、API 设计、部署指南 - docs/METADATA_DOC.md:18 张表完整字段说明、索引、表关系、数据类型规范
182 lines
7.5 KiB
HTML
182 lines
7.5 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="zh-CN">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>我的读书数据</title>
|
|
<link rel="stylesheet" href="/static/css/style.css">
|
|
<script src="https://cdn.jsdelivr.net/npm/echarts@5.5.0/dist/echarts.min.js"></script>
|
|
</head>
|
|
<body>
|
|
<!-- 数据库连接告警横幅(MySQL 连接失败时显示) -->
|
|
<div id="dbAlertBanner" class="db-alert-banner" style="display:none;">
|
|
<span class="db-alert-icon">⚠️</span>
|
|
<span class="db-alert-text">MySQL 数据库连接失败,数据可能无法正常加载。请检查 MySQL 服务是否已启动。</span>
|
|
<span class="db-alert-detail" id="dbAlertDetail"></span>
|
|
</div>
|
|
|
|
<!-- 导航栏 -->
|
|
<nav class="navbar">
|
|
<div class="nav-container">
|
|
<div class="nav-logo">
|
|
<span class="logo-icon">📖</span>
|
|
<span class="logo-text">我的读书数据</span>
|
|
</div>
|
|
<div class="nav-links">
|
|
<a href="#overview" class="nav-link active">总览</a>
|
|
<a href="#charts" class="nav-link">数据图表</a>
|
|
<a href="#top-books" class="nav-link">读书排行</a>
|
|
<a href="#shelf" class="nav-link">书架</a>
|
|
<a href="#notes" class="nav-link">笔记</a>
|
|
<a href="#discover" class="nav-link">推荐</a>
|
|
</div>
|
|
<div class="nav-update" id="fetchTime"></div>
|
|
</div>
|
|
</nav>
|
|
|
|
<!-- Hero 总览区 -->
|
|
<section id="overview" class="hero-section">
|
|
<div class="hero-bg"></div>
|
|
<div class="hero-content">
|
|
<h1 class="hero-title">阅读,是一场漫长的旅行</h1>
|
|
<p class="hero-subtitle" id="heroSubtitle">正在加载数据...</p>
|
|
<div class="hero-stats" id="heroStats">
|
|
<!-- 动态填充 -->
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- 核心数据卡片 -->
|
|
<section class="stats-cards-section">
|
|
<div class="container">
|
|
<div class="stats-grid" id="statsGrid">
|
|
<!-- 动态填充 -->
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- 图表区 -->
|
|
<section id="charts" class="charts-section">
|
|
<div class="container">
|
|
<h2 class="section-title">📊 阅读数据可视化</h2>
|
|
<div class="charts-grid">
|
|
<div class="chart-card large">
|
|
<h3 class="chart-title">年度阅读时长趋势</h3>
|
|
<div id="chart-yearly" class="chart-container"></div>
|
|
</div>
|
|
<div class="chart-card">
|
|
<h3 class="chart-title">24小时阅读时段分布</h3>
|
|
<div id="chart-hours" class="chart-container"></div>
|
|
</div>
|
|
<div class="chart-card">
|
|
<h3 class="chart-title">偏好阅读分类</h3>
|
|
<div id="chart-categories" class="chart-container"></div>
|
|
</div>
|
|
<div class="chart-card large">
|
|
<h3 class="chart-title">偏好作者 TOP 10</h3>
|
|
<div id="chart-authors" class="chart-container"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- 读书排行 -->
|
|
<section id="top-books" class="top-books-section">
|
|
<div class="container">
|
|
<h2 class="section-title">🏆 阅读时长排行</h2>
|
|
<div class="top-books-grid" id="topBooksGrid">
|
|
<!-- 动态填充 -->
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- 书架 -->
|
|
<section id="shelf" class="shelf-section">
|
|
<div class="container">
|
|
<h2 class="section-title">📚 我的书架</h2>
|
|
<div class="shelf-controls">
|
|
<div class="shelf-filters">
|
|
<input type="text" id="shelfSearch" class="shelf-search" placeholder="搜索分类...">
|
|
<label class="filter-checkbox">
|
|
<input type="checkbox" id="finishedOnly"> 仅看已读完
|
|
</label>
|
|
</div>
|
|
<div class="shelf-info" id="shelfInfo"></div>
|
|
</div>
|
|
<div class="book-grid" id="bookGrid">
|
|
<!-- 动态填充 -->
|
|
</div>
|
|
<div class="pagination" id="pagination">
|
|
<!-- 动态填充 -->
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- 笔记 -->
|
|
<section id="notes" class="notes-section">
|
|
<div class="container">
|
|
<h2 class="section-title">✍️ 笔记概览</h2>
|
|
<div class="notes-summary" id="notesSummary">
|
|
<!-- 动态填充 -->
|
|
</div>
|
|
<div class="notebooks-grid" id="notebooksGrid">
|
|
<!-- 动态填充 -->
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- 推荐发现 -->
|
|
<section id="discover" class="discover-section">
|
|
<div class="container">
|
|
<h2 class="section-title">🔍 推荐发现</h2>
|
|
<div class="discover-tabs">
|
|
<button class="discover-tab active" id="tabRecommend" onclick="switchDiscoverTab('recommend')">为你推荐</button>
|
|
<button class="discover-tab" id="tabSearch" onclick="switchDiscoverTab('search')">搜索书籍</button>
|
|
</div>
|
|
<!-- 推荐区 -->
|
|
<div id="recommendPanel" class="discover-panel">
|
|
<div class="discover-controls">
|
|
<button class="btn-primary" onclick="loadRecommend(0)">刷新推荐</button>
|
|
<button class="btn-secondary" onclick="loadMoreRecommend()">换一批</button>
|
|
</div>
|
|
<div class="discover-grid" id="recommendGrid"></div>
|
|
</div>
|
|
<!-- 搜索区 -->
|
|
<div id="searchPanel" class="discover-panel" style="display:none">
|
|
<div class="search-box">
|
|
<input type="text" id="searchInput" class="search-input" placeholder="输入书名、作者或关键词..." onkeydown="if(event.key==='Enter')doSearch()">
|
|
<button class="btn-primary" onclick="doSearch()">搜索</button>
|
|
</div>
|
|
<div class="discover-grid" id="searchGrid"></div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- 书籍详情弹窗 -->
|
|
<div class="modal-overlay" id="bookDetailModal" onclick="closeModal('bookDetailModal')">
|
|
<div class="modal-content book-detail-modal" onclick="event.stopPropagation()">
|
|
<span class="modal-close" onclick="closeModal('bookDetailModal')">×</span>
|
|
<div id="bookDetailBody" class="modal-body"></div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 笔记详情弹窗 -->
|
|
<div class="modal-overlay" id="notesDetailModal" onclick="closeModal('notesDetailModal')">
|
|
<div class="modal-content notes-detail-modal" onclick="event.stopPropagation()">
|
|
<span class="modal-close" onclick="closeModal('notesDetailModal')">×</span>
|
|
<div id="notesDetailBody" class="modal-body"></div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 页脚 -->
|
|
<footer class="footer">
|
|
<div class="container">
|
|
<p>数据来源:微信读书 · 数据存储:本地 SQLite · 更新时间:<span id="footerTime"></span></p>
|
|
<p class="footer-hint">点击书籍封面可在微信读书 App 中打开</p>
|
|
</div>
|
|
</footer>
|
|
|
|
<script src="/static/js/app.js"></script>
|
|
</body>
|
|
</html>
|