初始化提交
This commit is contained in:
210
api/app.py
Normal file
210
api/app.py
Normal file
@@ -0,0 +1,210 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
古诗词智能查询与解析器 - 后端API
|
||||
简化版本,包含核心功能
|
||||
"""
|
||||
|
||||
import os
|
||||
import json
|
||||
import logging
|
||||
from datetime import datetime
|
||||
from flask import Flask, request, jsonify
|
||||
from flask_cors import CORS
|
||||
import requests
|
||||
|
||||
# 配置日志
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# 创建Flask应用
|
||||
app = Flask(__name__)
|
||||
CORS(app)
|
||||
|
||||
# 配置
|
||||
app.config['JSON_AS_ASCII'] = False
|
||||
|
||||
# AI API配置
|
||||
SK_API_KEY = "sk-fdf7cc1c73504e628ec0119b7e11b8cc"
|
||||
AI_API_URL = "https://api.deepseek.com/v1/chat/completions"
|
||||
|
||||
# 示例诗词数据库
|
||||
SAMPLE_POETRY_DB = [
|
||||
{
|
||||
"id": 1,
|
||||
"title": "静夜思",
|
||||
"author": "李白",
|
||||
"dynasty": "唐",
|
||||
"content": "床前明月光,疑是地上霜。\n举头望明月,低头思故乡。",
|
||||
"type": "抒情",
|
||||
"word_count": "五言",
|
||||
"tags": ["思乡", "月亮", "夜晚"]
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"title": "春晓",
|
||||
"author": "孟浩然",
|
||||
"dynasty": "唐",
|
||||
"content": "春眠不觉晓,处处闻啼鸟。\n夜来风雨声,花落知多少。",
|
||||
"type": "山水",
|
||||
"word_count": "五言",
|
||||
"tags": ["春天", "自然", "风景"]
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"title": "水调歌头·明月几时有",
|
||||
"author": "苏轼",
|
||||
"dynasty": "宋",
|
||||
"content": "明月几时有?把酒问青天。\n不知天上宫阙,今夕是何年。\n我欲乘风归去,又恐琼楼玉宇,高处不胜寒。\n起舞弄清影,何似在人间。",
|
||||
"type": "抒情",
|
||||
"word_count": "词",
|
||||
"tags": ["中秋", "月亮", "思念"]
|
||||
}
|
||||
]
|
||||
|
||||
def generate_ai_analysis(poetry_title, author, dynasty, purpose, usage, target_audience, depth, focus):
|
||||
"""生成AI解析"""
|
||||
try:
|
||||
system_prompt = f"""你是一位古典文学专家,精通古诗词解析。
|
||||
背景:诗词用于{usage},读者是{target_audience},解析深度为{depth},关注{focus}。
|
||||
要求:提供准确原文、详细注释、深入解读、学习建议。"""
|
||||
|
||||
user_prompt = f"""解析以下诗词:
|
||||
标题:{poetry_title}
|
||||
作者:{author}
|
||||
朝代:{dynasty}
|
||||
目的:{purpose}"""
|
||||
|
||||
messages = [
|
||||
{"role": "system", "content": system_prompt},
|
||||
{"role": "user", "content": user_prompt}
|
||||
]
|
||||
|
||||
headers = {
|
||||
"Content-Type": "application/json",
|
||||
"Authorization": f"Bearer {SK_API_KEY}"
|
||||
}
|
||||
|
||||
data = {
|
||||
"model": "deepseek-chat",
|
||||
"messages": messages,
|
||||
"temperature": 0.7,
|
||||
"max_tokens": 1500
|
||||
}
|
||||
|
||||
response = requests.post(AI_API_URL, headers=headers, json=data, timeout=30)
|
||||
response.raise_for_status()
|
||||
|
||||
return response.json()["choices"][0]["message"]["content"]
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"AI解析失败: {e}")
|
||||
return f"AI解析生成失败: {str(e)}"
|
||||
|
||||
# API路由
|
||||
@app.route('/')
|
||||
def index():
|
||||
return jsonify({
|
||||
"name": "古诗词智能解析器API",
|
||||
"version": "1.0.0",
|
||||
"endpoints": ["/api/poetry", "/api/search", "/api/ai/analyze"]
|
||||
})
|
||||
|
||||
@app.route('/api/poetry', methods=['GET'])
|
||||
def get_poetry():
|
||||
"""获取诗词列表"""
|
||||
try:
|
||||
return jsonify({
|
||||
"success": True,
|
||||
"data": SAMPLE_POETRY_DB,
|
||||
"count": len(SAMPLE_POETRY_DB)
|
||||
})
|
||||
except Exception as e:
|
||||
return jsonify({"success": False, "error": str(e)}), 500
|
||||
|
||||
@app.route('/api/search', methods=['GET'])
|
||||
def search():
|
||||
"""搜索诗词"""
|
||||
try:
|
||||
query = request.args.get('q', '').lower()
|
||||
if not query:
|
||||
return jsonify({"success": True, "data": SAMPLE_POETRY_DB})
|
||||
|
||||
results = []
|
||||
for poetry in SAMPLE_POETRY_DB:
|
||||
if (query in poetry['title'].lower() or
|
||||
query in poetry['author'].lower() or
|
||||
query in poetry['content'].lower()):
|
||||
results.append(poetry)
|
||||
|
||||
return jsonify({
|
||||
"success": True,
|
||||
"data": results,
|
||||
"count": len(results)
|
||||
})
|
||||
except Exception as e:
|
||||
return jsonify({"success": False, "error": str(e)}), 500
|
||||
|
||||
@app.route('/api/ai/analyze', methods=['POST'])
|
||||
def analyze():
|
||||
"""AI解析诗词"""
|
||||
try:
|
||||
data = request.get_json()
|
||||
|
||||
# 必要参数检查
|
||||
required = ['poetry_title', 'author', 'dynasty']
|
||||
for field in required:
|
||||
if field not in data:
|
||||
return jsonify({
|
||||
"success": False,
|
||||
"error": f"缺少参数: {field}"
|
||||
}), 400
|
||||
|
||||
# 获取参数
|
||||
poetry_title = data['poetry_title']
|
||||
author = data['author']
|
||||
dynasty = data['dynasty']
|
||||
purpose = data.get('purpose', '学习理解')
|
||||
usage = data.get('usage', '课堂教学')
|
||||
target_audience = data.get('target_audience', '中小学生')
|
||||
depth = data.get('depth', '基础')
|
||||
focus = data.get('focus', '综合解析')
|
||||
|
||||
# 生成解析
|
||||
analysis = generate_ai_analysis(
|
||||
poetry_title, author, dynasty, purpose, usage,
|
||||
target_audience, depth, focus
|
||||
)
|
||||
|
||||
return jsonify({
|
||||
"success": True,
|
||||
"data": {
|
||||
"analysis": analysis,
|
||||
"metadata": {
|
||||
"poetry_title": poetry_title,
|
||||
"author": author,
|
||||
"dynasty": dynasty,
|
||||
"timestamp": datetime.now().isoformat()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"解析失败: {e}")
|
||||
return jsonify({"success": False, "error": str(e)}), 500
|
||||
|
||||
@app.route('/api/poetry/<int:poetry_id>', methods=['GET'])
|
||||
def get_poetry_detail(poetry_id):
|
||||
"""获取诗词详情"""
|
||||
try:
|
||||
poetry = next((p for p in SAMPLE_POETRY_DB if p['id'] == poetry_id), None)
|
||||
if not poetry:
|
||||
return jsonify({"success": False, "error": "诗词未找到"}), 404
|
||||
|
||||
return jsonify({"success": True, "data": poetry})
|
||||
except Exception as e:
|
||||
return jsonify({"success": False, "error": str(e)}), 500
|
||||
|
||||
if __name__ == '__main__':
|
||||
logger.info("启动古诗词智能解析器API服务器...")
|
||||
app.run(host='0.0.0.0', port=5000, debug=True)
|
||||
322
index.html
Normal file
322
index.html
Normal file
@@ -0,0 +1,322 @@
|
||||
<!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">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Noto+Serif+SC:wght@400;700&family=Ma+Shan+Zheng&display=swap" rel="stylesheet">
|
||||
<link rel="manifest" href="manifest.json">
|
||||
<link rel="icon" href="static/images/favicon.ico" type="image/x-icon">
|
||||
</head>
|
||||
<body>
|
||||
<!-- 导航栏 -->
|
||||
<nav class="navbar">
|
||||
<div class="container">
|
||||
<div class="logo">
|
||||
<i class="fas fa-book-open"></i>
|
||||
<h1>古诗词智能解析器</h1>
|
||||
</div>
|
||||
<div class="nav-links">
|
||||
<a href="#home" class="active"><i class="fas fa-home"></i> 首页</a>
|
||||
<a href="#search"><i class="fas fa-search"></i> 查询</a>
|
||||
<a href="#browse"><i class="fas fa-list"></i> 浏览</a>
|
||||
<a href="#ai-analysis"><i class="fas fa-robot"></i> AI解析</a>
|
||||
<a href="#learning"><i class="fas fa-graduation-cap"></i> 学习</a>
|
||||
<a href="#personal"><i class="fas fa-user"></i> 个人中心</a>
|
||||
</div>
|
||||
<button class="menu-toggle" id="menuToggle">
|
||||
<i class="fas fa-bars"></i>
|
||||
</button>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<!-- 主内容区 -->
|
||||
<main class="container">
|
||||
<!-- 首页横幅 -->
|
||||
<section id="home" class="hero">
|
||||
<div class="hero-content">
|
||||
<h2>探索千年诗词之美</h2>
|
||||
<p>AI智能解析,深度理解古诗词的意境与内涵</p>
|
||||
<div class="search-box">
|
||||
<input type="text" id="quickSearch" placeholder="输入诗词标题、作者、诗句或关键词...">
|
||||
<button id="quickSearchBtn"><i class="fas fa-search"></i> 智能搜索</button>
|
||||
</div>
|
||||
<div class="quick-filters">
|
||||
<span>快速筛选:</span>
|
||||
<button class="filter-btn" data-dynasty="唐">唐代</button>
|
||||
<button class="filter-btn" data-dynasty="宋">宋代</button>
|
||||
<button class="filter-btn" data-type="山水">山水诗</button>
|
||||
<button class="filter-btn" data-type="抒情">抒情诗</button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 智能查询模块 -->
|
||||
<section id="search" class="section">
|
||||
<h2><i class="fas fa-search"></i> 智能查询</h2>
|
||||
<div class="search-panel">
|
||||
<div class="search-form">
|
||||
<div class="form-group">
|
||||
<label for="poetryTitle"><i class="fas fa-heading"></i> 诗词标题</label>
|
||||
<input type="text" id="poetryTitle" placeholder="如:静夜思、春晓...">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="author"><i class="fas fa-user"></i> 作者</label>
|
||||
<input type="text" id="author" placeholder="如:李白、杜甫...">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="dynasty"><i class="fas fa-landmark"></i> 朝代</label>
|
||||
<select id="dynasty">
|
||||
<option value="">全部朝代</option>
|
||||
<option value="唐">唐代</option>
|
||||
<option value="宋">宋代</option>
|
||||
<option value="元">元代</option>
|
||||
<option value="明">明代</option>
|
||||
<option value="清">清代</option>
|
||||
<option value="先秦">先秦</option>
|
||||
<option value="汉">汉代</option>
|
||||
<option value="魏晋">魏晋南北朝</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="keywords"><i class="fas fa-key"></i> 关键词/诗句</label>
|
||||
<input type="text" id="keywords" placeholder="输入诗句片段或关键词...">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="poetryType"><i class="fas fa-tags"></i> 题材</label>
|
||||
<select id="poetryType">
|
||||
<option value="">全部题材</option>
|
||||
<option value="山水">山水田园</option>
|
||||
<option value="边塞">边塞战争</option>
|
||||
<option value="咏物">咏物言志</option>
|
||||
<option value="抒情">抒情诗</option>
|
||||
<option value="叙事">叙事诗</option>
|
||||
<option value="哲理">哲理诗</option>
|
||||
<option value="送别">送别诗</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="wordCount"><i class="fas fa-text-height"></i> 字数</label>
|
||||
<select id="wordCount">
|
||||
<option value="">不限</option>
|
||||
<option value="五言">五言</option>
|
||||
<option value="七言">七言</option>
|
||||
<option value="杂言">杂言</option>
|
||||
<option value="词">词</option>
|
||||
</select>
|
||||
</div>
|
||||
<button id="advancedSearchBtn" class="btn-primary">
|
||||
<i class="fas fa-search-plus"></i> 高级搜索
|
||||
</button>
|
||||
<button id="resetSearchBtn" class="btn-secondary">
|
||||
<i class="fas fa-redo"></i> 重置
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- AI解析模块 -->
|
||||
<section id="ai-analysis" class="section">
|
||||
<h2><i class="fas fa-robot"></i> AI深度解析</h2>
|
||||
<div class="ai-analysis-panel">
|
||||
<div class="analysis-controls">
|
||||
<div class="form-group">
|
||||
<label for="purpose"><i class="fas fa-bullseye"></i> 查询目的</label>
|
||||
<select id="purpose">
|
||||
<option value="学习理解">学习理解</option>
|
||||
<option value="教学参考">教学参考</option>
|
||||
<option value="文学研究">文学研究</option>
|
||||
<option value="创作参考">创作参考</option>
|
||||
<option value="文化欣赏">文化欣赏</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="usage"><i class="fas fa-scenario"></i> 使用场景</label>
|
||||
<select id="usage">
|
||||
<option value="课堂教学">课堂教学</option>
|
||||
<option value="自学研究">自学研究</option>
|
||||
<option value="文学创作">文学创作</option>
|
||||
<option value="文化传播">文化传播</option>
|
||||
<option value="考试备考">考试备考</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="targetAudience"><i class="fas fa-users"></i> 目标读者</label>
|
||||
<select id="targetAudience">
|
||||
<option value="中小学生">中小学生</option>
|
||||
<option value="大学生">大学生</option>
|
||||
<option value="文学爱好者">文学爱好者</option>
|
||||
<option value="研究人员">研究人员</option>
|
||||
<option value="普通读者">普通读者</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="depth"><i class="fas fa-layer-group"></i> 解析深度</label>
|
||||
<select id="depth">
|
||||
<option value="基础">基础解析</option>
|
||||
<option value="中等">中等深度</option>
|
||||
<option value="深入">深入解析</option>
|
||||
<option value="专业">专业级分析</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="focus"><i class="fas fa-crosshairs"></i> 关注重点</label>
|
||||
<select id="focus">
|
||||
<option value="意境分析">意境分析</option>
|
||||
<option value="修辞手法">修辞手法</option>
|
||||
<option value="格律分析">格律分析</option>
|
||||
<option value="历史背景">历史背景</option>
|
||||
<option value="文学价值">文学价值</option>
|
||||
<option value="综合解析">综合解析</option>
|
||||
</select>
|
||||
</div>
|
||||
<button id="generateAnalysisBtn" class="btn-primary">
|
||||
<i class="fas fa-magic"></i> 生成AI解析
|
||||
</button>
|
||||
</div>
|
||||
<div class="analysis-result" id="analysisResult">
|
||||
<div class="result-placeholder">
|
||||
<i class="fas fa-book-reader"></i>
|
||||
<h3>AI解析结果将显示在这里</h3>
|
||||
<p>选择诗词并设置解析参数后,点击"生成AI解析"按钮</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 诗词展示区 -->
|
||||
<section id="browse" class="section">
|
||||
<h2><i class="fas fa-list"></i> 诗词浏览</h2>
|
||||
<div class="browse-controls">
|
||||
<div class="view-toggle">
|
||||
<button class="view-btn active" data-view="grid"><i class="fas fa-th"></i> 网格</button>
|
||||
<button class="view-btn" data-view="list"><i class="fas fa-list"></i> 列表</button>
|
||||
</div>
|
||||
<div class="sort-controls">
|
||||
<select id="sortBy">
|
||||
<option value="time">按年代排序</option>
|
||||
<option value="popularity">按热度排序</option>
|
||||
<option value="title">按标题排序</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="poetry-grid" id="poetryGrid">
|
||||
<!-- 诗词卡片将通过JavaScript动态加载 -->
|
||||
</div>
|
||||
<div class="loading" id="loadingIndicator">
|
||||
<i class="fas fa-spinner fa-spin"></i> 加载中...
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 交互学习功能 -->
|
||||
<section id="learning" class="section">
|
||||
<h2><i class="fas fa-graduation-cap"></i> 交互学习</h2>
|
||||
<div class="learning-features">
|
||||
<div class="feature-card">
|
||||
<div class="feature-icon">
|
||||
<i class="fas fa-balance-scale"></i>
|
||||
</div>
|
||||
<h3>对比分析</h3>
|
||||
<p>比较两首诗词的异同,深入理解不同风格</p>
|
||||
<button class="btn-secondary" id="compareBtn">开始对比</button>
|
||||
</div>
|
||||
<div class="feature-card">
|
||||
<div class="feature-icon">
|
||||
<i class="fas fa-lightbulb"></i>
|
||||
</div>
|
||||
<h3>相似推荐</h3>
|
||||
<p>基于当前诗词推荐主题、风格相似的作品</p>
|
||||
<button class="btn-secondary" id="recommendBtn">获取推荐</button>
|
||||
</div>
|
||||
<div class="feature-card">
|
||||
<div class="feature-icon">
|
||||
<i class="fas fa-question-circle"></i>
|
||||
</div>
|
||||
<h3>知识问答</h3>
|
||||
<p>AI问答功能,解答关于诗词的各类问题</p>
|
||||
<button class="btn-secondary" id="qaBtn">开始问答</button>
|
||||
</div>
|
||||
<div class="feature-card">
|
||||
<div class="feature-icon">
|
||||
<i class="fas fa-brain"></i>
|
||||
</div>
|
||||
<h3>背诵助手</h3>
|
||||
<p>填空测试、顺序排序等背诵辅助功能</p>
|
||||
<button class="btn-secondary" id="memorizeBtn">开始背诵</button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<!-- 页脚 -->
|
||||
<footer>
|
||||
<div class="container">
|
||||
<div class="footer-content">
|
||||
<div class="footer-section">
|
||||
<h3><i class="fas fa-book-open"></i> 古诗词智能解析器</h3>
|
||||
<p>探索中华诗词之美,传承千年文化精髓</p>
|
||||
</div>
|
||||
<div class="footer-section">
|
||||
<h4>功能模块</h4>
|
||||
<a href="#search">智能查询</a>
|
||||
<a href="#ai-analysis">AI解析</a>
|
||||
<a href="#browse">诗词浏览</a>
|
||||
<a href="#learning">交互学习</a>
|
||||
</div>
|
||||
<div class="footer-section">
|
||||
<h4>技术支持</h4>
|
||||
<a href="#" id="apiDocs">API文档</a>
|
||||
<a href="#" id="aboutAi">AI技术说明</a>
|
||||
<a href="#" id="privacy">隐私政策</a>
|
||||
<a href="#" id="contact">联系我们</a>
|
||||
</div>
|
||||
<div class="footer-section">
|
||||
<h4>关注我们</h4>
|
||||
<div class="social-links">
|
||||
<a href="#"><i class="fab fa-weixin"></i></a>
|
||||
<a href="#"><i class="fab fa-weibo"></i></a>
|
||||
<a href="#"><i class="fab fa-github"></i></a>
|
||||
<a href="#"><i class="fab fa-bilibili"></i></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="footer-bottom">
|
||||
<p>© 2023 古诗词智能解析器 | 基于H5技术开发 | PWA支持</p>
|
||||
<div class="pwa-badge">
|
||||
<i class="fas fa-download"></i> 可添加到主屏幕
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<!-- 模态框 -->
|
||||
<div class="modal" id="poetryDetailModal">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h3 id="modalTitle">诗词详情</h3>
|
||||
<button class="close-modal">×</button>
|
||||
</div>
|
||||
<div class="modal-body" id="modalBody">
|
||||
<!-- 诗词详情内容将通过JavaScript动态加载 -->
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn-secondary" id="favoriteBtn">
|
||||
<i class="far fa-heart"></i> 收藏
|
||||
</button>
|
||||
<button class="btn-primary" id="analyzeBtn">
|
||||
<i class="fas fa-robot"></i> AI解析
|
||||
</button>
|
||||
<button class="btn-secondary" id="shareBtn">
|
||||
<i class="fas fa-share-alt"></i> 分享
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 脚本 -->
|
||||
<script src="static/js/main.js"></script>
|
||||
<script src="static/js/service-worker.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
127
manifest.json
Normal file
127
manifest.json
Normal file
@@ -0,0 +1,127 @@
|
||||
{
|
||||
"name": "古诗词智能解析器",
|
||||
"short_name": "诗词解析",
|
||||
"description": "AI智能查询与解析古诗词,提供深度文学分析和学习功能",
|
||||
"start_url": "/",
|
||||
"display": "standalone",
|
||||
"background_color": "#fffaf0",
|
||||
"theme_color": "#8b4513",
|
||||
"orientation": "portrait-primary",
|
||||
"icons": [
|
||||
{
|
||||
"src": "/static/images/icon-72.png",
|
||||
"sizes": "72x72",
|
||||
"type": "image/png",
|
||||
"purpose": "any maskable"
|
||||
},
|
||||
{
|
||||
"src": "/static/images/icon-96.png",
|
||||
"sizes": "96x96",
|
||||
"type": "image/png",
|
||||
"purpose": "any maskable"
|
||||
},
|
||||
{
|
||||
"src": "/static/images/icon-128.png",
|
||||
"sizes": "128x128",
|
||||
"type": "image/png",
|
||||
"purpose": "any maskable"
|
||||
},
|
||||
{
|
||||
"src": "/static/images/icon-144.png",
|
||||
"sizes": "144x144",
|
||||
"type": "image/png",
|
||||
"purpose": "any maskable"
|
||||
},
|
||||
{
|
||||
"src": "/static/images/icon-152.png",
|
||||
"sizes": "152x152",
|
||||
"type": "image/png",
|
||||
"purpose": "any maskable"
|
||||
},
|
||||
{
|
||||
"src": "/static/images/icon-192.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png",
|
||||
"purpose": "any maskable"
|
||||
},
|
||||
{
|
||||
"src": "/static/images/icon-384.png",
|
||||
"sizes": "384x384",
|
||||
"type": "image/png",
|
||||
"purpose": "any maskable"
|
||||
},
|
||||
{
|
||||
"src": "/static/images/icon-512.png",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png",
|
||||
"purpose": "any maskable"
|
||||
}
|
||||
],
|
||||
"screenshots": [
|
||||
{
|
||||
"src": "/static/images/screenshot1.png",
|
||||
"sizes": "1280x720",
|
||||
"type": "image/png",
|
||||
"form_factor": "wide",
|
||||
"label": "古诗词智能解析器主界面"
|
||||
},
|
||||
{
|
||||
"src": "/static/images/screenshot2.png",
|
||||
"sizes": "750x1334",
|
||||
"type": "image/png",
|
||||
"form_factor": "narrow",
|
||||
"label": "移动端诗词浏览界面"
|
||||
}
|
||||
],
|
||||
"categories": ["education", "books", "lifestyle"],
|
||||
"lang": "zh-CN",
|
||||
"dir": "ltr",
|
||||
"prefer_related_applications": false,
|
||||
"related_applications": [],
|
||||
"scope": "/",
|
||||
"shortcuts": [
|
||||
{
|
||||
"name": "智能搜索",
|
||||
"short_name": "搜索",
|
||||
"description": "快速搜索古诗词",
|
||||
"url": "/#search",
|
||||
"icons": [
|
||||
{
|
||||
"src": "/static/images/search-icon.png",
|
||||
"sizes": "96x96"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "AI解析",
|
||||
"short_name": "解析",
|
||||
"description": "使用AI深度解析诗词",
|
||||
"url": "/#ai-analysis",
|
||||
"icons": [
|
||||
{
|
||||
"src": "/static/images/ai-icon.png",
|
||||
"sizes": "96x96"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "我的收藏",
|
||||
"short_name": "收藏",
|
||||
"description": "查看收藏的诗词",
|
||||
"url": "/#personal",
|
||||
"icons": [
|
||||
{
|
||||
"src": "/static/images/favorite-icon.png",
|
||||
"sizes": "96x96"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"features": [
|
||||
"古诗词查询",
|
||||
"AI智能解析",
|
||||
"离线阅读",
|
||||
"诗词收藏",
|
||||
"学习记录"
|
||||
]
|
||||
}
|
||||
4
requirements.txt
Normal file
4
requirements.txt
Normal file
@@ -0,0 +1,4 @@
|
||||
Flask==2.3.3
|
||||
Flask-CORS==4.0.0
|
||||
requests==2.31.0
|
||||
python-dotenv==1.0.0
|
||||
107
run.py
Normal file
107
run.py
Normal file
@@ -0,0 +1,107 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
古诗词智能查询与解析器 - 启动脚本
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import subprocess
|
||||
import time
|
||||
import webbrowser
|
||||
|
||||
def install_dependencies():
|
||||
"""安装Python依赖"""
|
||||
print("1. 安装Python依赖...")
|
||||
try:
|
||||
subprocess.check_call([sys.executable, "-m", "pip", "install", "-r", "requirements.txt"])
|
||||
print("依赖安装成功!")
|
||||
return True
|
||||
except subprocess.CalledProcessError as e:
|
||||
print(f"依赖安装失败: {e}")
|
||||
return False
|
||||
|
||||
def start_backend():
|
||||
"""启动后端API服务器"""
|
||||
print("2. 启动后端API服务器...")
|
||||
backend_cmd = [sys.executable, "api/app.py"]
|
||||
|
||||
# 在Windows上使用CREATE_NEW_CONSOLE标志
|
||||
if sys.platform == "win32":
|
||||
subprocess.Popen(backend_cmd, cwd=os.getcwd(),
|
||||
creationflags=subprocess.CREATE_NEW_CONSOLE)
|
||||
else:
|
||||
subprocess.Popen(backend_cmd, cwd=os.getcwd())
|
||||
|
||||
print("后端服务器启动中...")
|
||||
time.sleep(3)
|
||||
return True
|
||||
|
||||
def start_frontend():
|
||||
"""启动前端HTTP服务器"""
|
||||
print("3. 启动前端HTTP服务器...")
|
||||
frontend_cmd = [sys.executable, "-m", "http.server", "8000"]
|
||||
|
||||
if sys.platform == "win32":
|
||||
subprocess.Popen(frontend_cmd, cwd=os.getcwd(),
|
||||
creationflags=subprocess.CREATE_NEW_CONSOLE)
|
||||
else:
|
||||
subprocess.Popen(frontend_cmd, cwd=os.getcwd())
|
||||
|
||||
print("前端服务器启动中...")
|
||||
time.sleep(2)
|
||||
return True
|
||||
|
||||
def open_browser():
|
||||
"""打开浏览器"""
|
||||
print("4. 打开浏览器访问应用...")
|
||||
url = "http://localhost:8000"
|
||||
webbrowser.open(url)
|
||||
print(f"已打开浏览器访问: {url}")
|
||||
return True
|
||||
|
||||
def main():
|
||||
"""主函数"""
|
||||
print("=" * 50)
|
||||
print("古诗词智能查询与解析器 - 启动程序")
|
||||
print("=" * 50)
|
||||
|
||||
# 检查当前目录
|
||||
if not os.path.exists("api/app.py"):
|
||||
print("错误: 请在项目根目录运行此脚本")
|
||||
return
|
||||
|
||||
# 安装依赖
|
||||
if not install_dependencies():
|
||||
print("警告: 依赖安装失败,尝试继续运行...")
|
||||
|
||||
# 启动后端
|
||||
if not start_backend():
|
||||
print("错误: 后端启动失败")
|
||||
return
|
||||
|
||||
# 启动前端
|
||||
if not start_frontend():
|
||||
print("错误: 前端启动失败")
|
||||
return
|
||||
|
||||
# 打开浏览器
|
||||
if not open_browser():
|
||||
print("警告: 无法自动打开浏览器")
|
||||
|
||||
print("\n" + "=" * 50)
|
||||
print("应用启动完成!")
|
||||
print("后端API: http://localhost:5000")
|
||||
print("前端应用: http://localhost:8000")
|
||||
print("\n按Ctrl+C停止所有服务器")
|
||||
print("=" * 50)
|
||||
|
||||
try:
|
||||
# 保持脚本运行
|
||||
while True:
|
||||
time.sleep(1)
|
||||
except KeyboardInterrupt:
|
||||
print("\n正在停止服务器...")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
28
start_app.bat
Normal file
28
start_app.bat
Normal file
@@ -0,0 +1,28 @@
|
||||
@echo off
|
||||
echo 正在启动古诗词智能查询与解析器...
|
||||
echo.
|
||||
|
||||
echo 1. 安装Python依赖...
|
||||
pip install -r requirements.txt
|
||||
|
||||
echo.
|
||||
echo 2. 启动后端API服务器...
|
||||
start cmd /k "cd /d %~dp0api && python app.py"
|
||||
|
||||
echo.
|
||||
echo 3. 启动前端HTTP服务器...
|
||||
timeout /t 3 /nobreak > nul
|
||||
start cmd /k "cd /d %~dp0 && python -m http.server 8000"
|
||||
|
||||
echo.
|
||||
echo 4. 打开浏览器访问应用...
|
||||
timeout /t 5 /nobreak > nul
|
||||
start http://localhost:8000
|
||||
|
||||
echo.
|
||||
echo 应用已启动!
|
||||
echo 后端API: http://localhost:5000
|
||||
echo 前端应用: http://localhost:8000
|
||||
echo.
|
||||
echo 按任意键退出...
|
||||
pause > nul
|
||||
708
static/css/style.css
Normal file
708
static/css/style.css
Normal file
@@ -0,0 +1,708 @@
|
||||
/* 基础样式 */
|
||||
:root {
|
||||
--primary-color: #8b4513;
|
||||
--secondary-color: #d2691e;
|
||||
--accent-color: #a0522d;
|
||||
--light-color: #f5f5dc;
|
||||
--dark-color: #2c1810;
|
||||
--text-color: #333;
|
||||
--text-light: #666;
|
||||
--bg-color: #fffaf0;
|
||||
--card-bg: #fff;
|
||||
--shadow: 0 4px 12px rgba(139, 69, 19, 0.1);
|
||||
--border-radius: 12px;
|
||||
--transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Noto Serif SC', serif;
|
||||
line-height: 1.6;
|
||||
color: var(--text-color);
|
||||
background-color: var(--bg-color);
|
||||
background-image: url('../images/pattern.png');
|
||||
background-size: 300px;
|
||||
background-blend-mode: overlay;
|
||||
background-color: rgba(255, 250, 240, 0.9);
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 0 20px;
|
||||
}
|
||||
|
||||
/* 导航栏 */
|
||||
.navbar {
|
||||
background-color: var(--primary-color);
|
||||
color: white;
|
||||
padding: 1rem 0;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 1000;
|
||||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.navbar .container {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.logo {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.logo i {
|
||||
font-size: 2rem;
|
||||
color: var(--light-color);
|
||||
}
|
||||
|
||||
.logo h1 {
|
||||
font-family: 'Ma Shan Zheng', cursive;
|
||||
font-size: 1.8rem;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.nav-links {
|
||||
display: flex;
|
||||
gap: 2rem;
|
||||
}
|
||||
|
||||
.nav-links a {
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
padding: 0.5rem 1rem;
|
||||
border-radius: var(--border-radius);
|
||||
transition: var(--transition);
|
||||
}
|
||||
|
||||
.nav-links a:hover,
|
||||
.nav-links a.active {
|
||||
background-color: var(--secondary-color);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.menu-toggle {
|
||||
display: none;
|
||||
background: none;
|
||||
border: none;
|
||||
color: white;
|
||||
font-size: 1.5rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* 首页横幅 */
|
||||
.hero {
|
||||
background: linear-gradient(135deg, var(--primary-color), var(--accent-color));
|
||||
color: white;
|
||||
padding: 4rem 0;
|
||||
text-align: center;
|
||||
border-radius: 0 0 var(--border-radius) var(--border-radius);
|
||||
margin-bottom: 3rem;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.hero::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: url('../images/hero-pattern.png');
|
||||
opacity: 0.1;
|
||||
}
|
||||
|
||||
.hero-content {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.hero h2 {
|
||||
font-family: 'Ma Shan Zheng', cursive;
|
||||
font-size: 3rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.hero p {
|
||||
font-size: 1.2rem;
|
||||
margin-bottom: 2rem;
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
.search-box {
|
||||
max-width: 600px;
|
||||
margin: 0 auto 2rem;
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.search-box input {
|
||||
flex: 1;
|
||||
padding: 1rem;
|
||||
border: none;
|
||||
border-radius: var(--border-radius);
|
||||
font-size: 1rem;
|
||||
font-family: 'Noto Serif SC', serif;
|
||||
}
|
||||
|
||||
.search-box button {
|
||||
padding: 1rem 2rem;
|
||||
background-color: var(--secondary-color);
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: var(--border-radius);
|
||||
cursor: pointer;
|
||||
font-size: 1rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
transition: var(--transition);
|
||||
}
|
||||
|
||||
.search-box button:hover {
|
||||
background-color: var(--accent-color);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.quick-filters {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.quick-filters span {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.filter-btn {
|
||||
padding: 0.5rem 1.5rem;
|
||||
background-color: rgba(255, 255, 255, 0.2);
|
||||
color: white;
|
||||
border: 1px solid rgba(255, 255, 255, 0.3);
|
||||
border-radius: 20px;
|
||||
cursor: pointer;
|
||||
transition: var(--transition);
|
||||
}
|
||||
|
||||
.filter-btn:hover {
|
||||
background-color: rgba(255, 255, 255, 0.3);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
/* 内容区块 */
|
||||
.section {
|
||||
margin-bottom: 4rem;
|
||||
padding: 2rem;
|
||||
background-color: var(--card-bg);
|
||||
border-radius: var(--border-radius);
|
||||
box-shadow: var(--shadow);
|
||||
}
|
||||
|
||||
.section h2 {
|
||||
color: var(--primary-color);
|
||||
margin-bottom: 2rem;
|
||||
padding-bottom: 1rem;
|
||||
border-bottom: 2px solid var(--light-color);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
/* 搜索面板 */
|
||||
.search-panel {
|
||||
background-color: var(--light-color);
|
||||
padding: 2rem;
|
||||
border-radius: var(--border-radius);
|
||||
}
|
||||
|
||||
.search-form {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
||||
gap: 1.5rem;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.form-group label {
|
||||
color: var(--primary-color);
|
||||
font-weight: bold;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
.form-group input,
|
||||
.form-group select {
|
||||
padding: 0.8rem;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: var(--border-radius);
|
||||
font-family: 'Noto Serif SC', serif;
|
||||
font-size: 1rem;
|
||||
transition: var(--transition);
|
||||
}
|
||||
|
||||
.form-group input:focus,
|
||||
.form-group select:focus {
|
||||
outline: none;
|
||||
border-color: var(--primary-color);
|
||||
box-shadow: 0 0 0 2px rgba(139, 69, 19, 0.2);
|
||||
}
|
||||
|
||||
/* 按钮样式 */
|
||||
.btn-primary,
|
||||
.btn-secondary {
|
||||
padding: 1rem 2rem;
|
||||
border: none;
|
||||
border-radius: var(--border-radius);
|
||||
cursor: pointer;
|
||||
font-size: 1rem;
|
||||
font-family: 'Noto Serif SC', serif;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
transition: var(--transition);
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background-color: var(--primary-color);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
background-color: var(--secondary-color);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
background-color: var(--light-color);
|
||||
color: var(--primary-color);
|
||||
border: 1px solid var(--primary-color);
|
||||
}
|
||||
|
||||
.btn-secondary:hover {
|
||||
background-color: var(--primary-color);
|
||||
color: white;
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
/* AI解析面板 */
|
||||
.ai-analysis-panel {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 2fr;
|
||||
gap: 2rem;
|
||||
}
|
||||
|
||||
.analysis-controls {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1.5rem;
|
||||
}
|
||||
|
||||
.analysis-result {
|
||||
background-color: var(--light-color);
|
||||
border-radius: var(--border-radius);
|
||||
padding: 2rem;
|
||||
min-height: 400px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.result-placeholder {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100%;
|
||||
color: var(--text-light);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.result-placeholder i {
|
||||
font-size: 4rem;
|
||||
margin-bottom: 1rem;
|
||||
color: var(--primary-color);
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
/* 诗词网格 */
|
||||
.browse-controls {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.view-toggle {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.view-btn {
|
||||
padding: 0.5rem 1rem;
|
||||
background-color: var(--light-color);
|
||||
border: 1px solid #ddd;
|
||||
border-radius: var(--border-radius);
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
.view-btn.active {
|
||||
background-color: var(--primary-color);
|
||||
color: white;
|
||||
border-color: var(--primary-color);
|
||||
}
|
||||
|
||||
.poetry-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
|
||||
gap: 2rem;
|
||||
}
|
||||
|
||||
.poetry-card {
|
||||
background-color: var(--card-bg);
|
||||
border-radius: var(--border-radius);
|
||||
overflow: hidden;
|
||||
box-shadow: var(--shadow);
|
||||
transition: var(--transition);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.poetry-card:hover {
|
||||
transform: translateY(-5px);
|
||||
box-shadow: 0 8px 20px rgba(139, 69, 19, 0.2);
|
||||
}
|
||||
|
||||
.poetry-card-header {
|
||||
background-color: var(--primary-color);
|
||||
color: white;
|
||||
padding: 1.5rem;
|
||||
}
|
||||
|
||||
.poetry-card-header h3 {
|
||||
font-size: 1.3rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.poetry-card-header .meta {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
font-size: 0.9rem;
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
.poetry-card-body {
|
||||
padding: 1.5rem;
|
||||
}
|
||||
|
||||
.poetry-content {
|
||||
font-size: 1.1rem;
|
||||
line-height: 1.8;
|
||||
margin-bottom: 1rem;
|
||||
font-family: 'Ma Shan Zheng', cursive;
|
||||
}
|
||||
|
||||
.poetry-tags {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.tag {
|
||||
background-color: var(--light-color);
|
||||
color: var(--primary-color);
|
||||
padding: 0.3rem 0.8rem;
|
||||
border-radius: 15px;
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
.loading {
|
||||
text-align: center;
|
||||
padding: 2rem;
|
||||
color: var(--text-light);
|
||||
}
|
||||
|
||||
.loading i {
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
/* 学习功能 */
|
||||
.learning-features {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
||||
gap: 2rem;
|
||||
}
|
||||
|
||||
.feature-card {
|
||||
background-color: var(--card-bg);
|
||||
padding: 2rem;
|
||||
border-radius: var(--border-radius);
|
||||
text-align: center;
|
||||
box-shadow: var(--shadow);
|
||||
transition: var(--transition);
|
||||
}
|
||||
|
||||
.feature-card:hover {
|
||||
transform: translateY(-5px);
|
||||
box-shadow: 0 8px 20px rgba(139, 69, 19, 0.2);
|
||||
}
|
||||
|
||||
.feature-icon {
|
||||
font-size: 3rem;
|
||||
color: var(--primary-color);
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.feature-card h3 {
|
||||
margin-bottom: 1rem;
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
.feature-card p {
|
||||
color: var(--text-light);
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
/* 页脚 */
|
||||
footer {
|
||||
background-color: var(--dark-color);
|
||||
color: white;
|
||||
padding: 3rem 0 1rem;
|
||||
margin-top: 4rem;
|
||||
}
|
||||
|
||||
.footer-content {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
||||
gap: 2rem;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.footer-section h3,
|
||||
.footer-section h4 {
|
||||
color: var(--light-color);
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.footer-section a {
|
||||
color: #ccc;
|
||||
text-decoration: none;
|
||||
display: block;
|
||||
margin-bottom: 0.5rem;
|
||||
transition: var(--transition);
|
||||
}
|
||||
|
||||
.footer-section a:hover {
|
||||
color: white;
|
||||
transform: translateX(5px);
|
||||
}
|
||||
|
||||
.social-links {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.social-links a {
|
||||
font-size: 1.5rem;
|
||||
color: #ccc;
|
||||
transition: var(--transition);
|
||||
}
|
||||
|
||||
.social-links a:hover {
|
||||
color: white;
|
||||
transform: translateY(-3px);
|
||||
}
|
||||
|
||||
.footer-bottom {
|
||||
text-align: center;
|
||||
padding-top: 2rem;
|
||||
border-top: 1px solid rgba(255, 255, 255, 0.1);
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.pwa-badge {
|
||||
background-color: var(--primary-color);
|
||||
padding: 0.5rem 1rem;
|
||||
border-radius: 20px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
/* 模态框 */
|
||||
.modal {
|
||||
display: none;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
z-index: 2000;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.modal.active {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
background-color: var(--card-bg);
|
||||
border-radius: var(--border-radius);
|
||||
width: 90%;
|
||||
max-width: 800px;
|
||||
max-height: 90vh;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.modal-header {
|
||||
background-color: var(--primary-color);
|
||||
color: white;
|
||||
padding: 1.5rem;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.close-modal {
|
||||
background: none;
|
||||
border: none;
|
||||
color: white;
|
||||
font-size: 2rem;
|
||||
cursor: pointer;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.modal-body {
|
||||
padding: 2rem;
|
||||
overflow-y: auto;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.modal-footer {
|
||||
padding: 1.5rem;
|
||||
background-color: var(--light-color);
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
/* 响应式设计 */
|
||||
@media (max-width: 768px) {
|
||||
.navbar .container {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.nav-links {
|
||||
display: none;
|
||||
width: 100%;
|
||||
flex-direction: column;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.nav-links.active {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.menu-toggle {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.hero h2 {
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
.search-box {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.ai-analysis-panel {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.poetry-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.footer-bottom {
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.container {
|
||||
padding: 0 10px;
|
||||
}
|
||||
|
||||
.section {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.search-form {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.learning-features {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
/* 动画 */
|
||||
@keyframes fadeIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(20px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
.fade-in {
|
||||
animation: fadeIn 0.5s ease forwards;
|
||||
}
|
||||
|
||||
/* 滚动条样式 */
|
||||
::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
background: var(--light-color);
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: var(--primary-color);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background: var(--secondary-color);
|
||||
}
|
||||
745
static/js/main.js
Normal file
745
static/js/main.js
Normal file
@@ -0,0 +1,745 @@
|
||||
// 古诗词智能查询与解析器 - 主JavaScript文件
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// 初始化应用
|
||||
initApp();
|
||||
|
||||
// 加载示例诗词数据
|
||||
loadSamplePoetryData();
|
||||
|
||||
// 设置事件监听器
|
||||
setupEventListeners();
|
||||
});
|
||||
|
||||
// 应用初始化
|
||||
function initApp() {
|
||||
console.log('古诗词智能解析器应用已启动');
|
||||
|
||||
// 设置当前年份
|
||||
document.querySelector('.footer-bottom p').innerHTML =
|
||||
`© ${new Date().getFullYear()} 古诗词智能解析器 | 基于H5技术开发 | PWA支持`;
|
||||
|
||||
// 初始化导航栏
|
||||
initNavigation();
|
||||
|
||||
// 初始化模态框
|
||||
initModal();
|
||||
|
||||
// 检查PWA支持
|
||||
checkPWASupport();
|
||||
}
|
||||
|
||||
// 初始化导航栏
|
||||
function initNavigation() {
|
||||
const menuToggle = document.getElementById('menuToggle');
|
||||
const navLinks = document.querySelector('.nav-links');
|
||||
|
||||
if (menuToggle) {
|
||||
menuToggle.addEventListener('click', function() {
|
||||
navLinks.classList.toggle('active');
|
||||
});
|
||||
}
|
||||
|
||||
// 平滑滚动到锚点
|
||||
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
|
||||
anchor.addEventListener('click', function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
const targetId = this.getAttribute('href');
|
||||
if (targetId === '#') return;
|
||||
|
||||
const targetElement = document.querySelector(targetId);
|
||||
if (targetElement) {
|
||||
// 关闭移动端菜单
|
||||
navLinks.classList.remove('active');
|
||||
|
||||
// 平滑滚动
|
||||
window.scrollTo({
|
||||
top: targetElement.offsetTop - 80,
|
||||
behavior: 'smooth'
|
||||
});
|
||||
|
||||
// 更新活动链接
|
||||
document.querySelectorAll('.nav-links a').forEach(link => {
|
||||
link.classList.remove('active');
|
||||
});
|
||||
this.classList.add('active');
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// 初始化模态框
|
||||
function initModal() {
|
||||
const modal = document.getElementById('poetryDetailModal');
|
||||
const closeModalBtn = document.querySelector('.close-modal');
|
||||
|
||||
if (closeModalBtn) {
|
||||
closeModalBtn.addEventListener('click', function() {
|
||||
modal.classList.remove('active');
|
||||
});
|
||||
}
|
||||
|
||||
// 点击模态框外部关闭
|
||||
modal.addEventListener('click', function(e) {
|
||||
if (e.target === modal) {
|
||||
modal.classList.remove('active');
|
||||
}
|
||||
});
|
||||
|
||||
// ESC键关闭模态框
|
||||
document.addEventListener('keydown', function(e) {
|
||||
if (e.key === 'Escape' && modal.classList.contains('active')) {
|
||||
modal.classList.remove('active');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 检查PWA支持
|
||||
function checkPWASupport() {
|
||||
if ('serviceWorker' in navigator) {
|
||||
console.log('支持Service Worker,PWA功能可用');
|
||||
|
||||
// 注册Service Worker
|
||||
navigator.serviceWorker.register('/static/js/service-worker.js')
|
||||
.then(registration => {
|
||||
console.log('Service Worker 注册成功:', registration);
|
||||
})
|
||||
.catch(error => {
|
||||
console.log('Service Worker 注册失败:', error);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 加载示例诗词数据
|
||||
function loadSamplePoetryData() {
|
||||
const samplePoetry = [
|
||||
{
|
||||
id: 1,
|
||||
title: "静夜思",
|
||||
author: "李白",
|
||||
dynasty: "唐",
|
||||
content: "床前明月光,疑是地上霜。\n举头望明月,低头思故乡。",
|
||||
type: "抒情",
|
||||
wordCount: "五言",
|
||||
tags: ["思乡", "月亮", "夜晚"],
|
||||
popularity: 95
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
title: "春晓",
|
||||
author: "孟浩然",
|
||||
dynasty: "唐",
|
||||
content: "春眠不觉晓,处处闻啼鸟。\n夜来风雨声,花落知多少。",
|
||||
type: "山水",
|
||||
wordCount: "五言",
|
||||
tags: ["春天", "自然", "风景"],
|
||||
popularity: 88
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
title: "水调歌头·明月几时有",
|
||||
author: "苏轼",
|
||||
dynasty: "宋",
|
||||
content: "明月几时有?把酒问青天。\n不知天上宫阙,今夕是何年。\n我欲乘风归去,又恐琼楼玉宇,高处不胜寒。\n起舞弄清影,何似在人间。",
|
||||
type: "抒情",
|
||||
wordCount: "词",
|
||||
tags: ["中秋", "月亮", "思念"],
|
||||
popularity: 92
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
title: "登鹳雀楼",
|
||||
author: "王之涣",
|
||||
dynasty: "唐",
|
||||
content: "白日依山尽,黄河入海流。\n欲穷千里目,更上一层楼。",
|
||||
type: "哲理",
|
||||
wordCount: "五言",
|
||||
tags: ["登高", "哲理", "壮丽"],
|
||||
popularity: 85
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
title: "望庐山瀑布",
|
||||
author: "李白",
|
||||
dynasty: "唐",
|
||||
content: "日照香炉生紫烟,遥看瀑布挂前川。\n飞流直下三千尺,疑是银河落九天。",
|
||||
type: "山水",
|
||||
wordCount: "七言",
|
||||
tags: ["瀑布", "自然", "壮丽"],
|
||||
popularity: 90
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
title: "江雪",
|
||||
author: "柳宗元",
|
||||
dynasty: "唐",
|
||||
content: "千山鸟飞绝,万径人踪灭。\n孤舟蓑笠翁,独钓寒江雪。",
|
||||
type: "山水",
|
||||
wordCount: "五言",
|
||||
tags: ["冬天", "孤独", "自然"],
|
||||
popularity: 87
|
||||
}
|
||||
];
|
||||
|
||||
// 渲染诗词卡片
|
||||
renderPoetryCards(samplePoetry);
|
||||
}
|
||||
|
||||
// 渲染诗词卡片
|
||||
function renderPoetryCards(poetryList) {
|
||||
const poetryGrid = document.getElementById('poetryGrid');
|
||||
if (!poetryGrid) return;
|
||||
|
||||
poetryGrid.innerHTML = '';
|
||||
|
||||
poetryList.forEach(poetry => {
|
||||
const card = createPoetryCard(poetry);
|
||||
poetryGrid.appendChild(card);
|
||||
});
|
||||
|
||||
// 隐藏加载指示器
|
||||
const loadingIndicator = document.getElementById('loadingIndicator');
|
||||
if (loadingIndicator) {
|
||||
loadingIndicator.style.display = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
// 创建诗词卡片
|
||||
function createPoetryCard(poetry) {
|
||||
const card = document.createElement('div');
|
||||
card.className = 'poetry-card fade-in';
|
||||
card.dataset.id = poetry.id;
|
||||
|
||||
card.innerHTML = `
|
||||
<div class="poetry-card-header">
|
||||
<h3>${poetry.title}</h3>
|
||||
<div class="meta">
|
||||
<span>${poetry.author}</span>
|
||||
<span>${poetry.dynasty}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="poetry-card-body">
|
||||
<div class="poetry-content">${poetry.content.replace(/\n/g, '<br>')}</div>
|
||||
<div class="poetry-tags">
|
||||
${poetry.tags.map(tag => `<span class="tag">${tag}</span>`).join('')}
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
// 添加点击事件
|
||||
card.addEventListener('click', function() {
|
||||
showPoetryDetail(poetry);
|
||||
});
|
||||
|
||||
return card;
|
||||
}
|
||||
|
||||
// 显示诗词详情
|
||||
function showPoetryDetail(poetry) {
|
||||
const modal = document.getElementById('poetryDetailModal');
|
||||
const modalTitle = document.getElementById('modalTitle');
|
||||
const modalBody = document.getElementById('modalBody');
|
||||
|
||||
if (!modal || !modalTitle || !modalBody) return;
|
||||
|
||||
modalTitle.textContent = poetry.title;
|
||||
|
||||
modalBody.innerHTML = `
|
||||
<div class="poetry-detail">
|
||||
<div class="detail-header">
|
||||
<h4>${poetry.title}</h4>
|
||||
<div class="detail-meta">
|
||||
<span><i class="fas fa-user"></i> ${poetry.author}</span>
|
||||
<span><i class="fas fa-landmark"></i> ${poetry.dynasty}</span>
|
||||
<span><i class="fas fa-tag"></i> ${poetry.type}</span>
|
||||
<span><i class="fas fa-text-height"></i> ${poetry.wordCount}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="detail-content">
|
||||
<h5>原文:</h5>
|
||||
<pre>${poetry.content}</pre>
|
||||
<h5>现代汉语翻译:</h5>
|
||||
<p>${getTranslation(poetry)}</p>
|
||||
<h5>简要赏析:</h5>
|
||||
<p>${getBriefAnalysis(poetry)}</p>
|
||||
</div>
|
||||
<div class="detail-tags">
|
||||
<h5>标签:</h5>
|
||||
<div class="tags">
|
||||
${poetry.tags.map(tag => `<span class="tag">${tag}</span>`).join('')}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
// 更新按钮事件
|
||||
const favoriteBtn = document.getElementById('favoriteBtn');
|
||||
const analyzeBtn = document.getElementById('analyzeBtn');
|
||||
const shareBtn = document.getElementById('shareBtn');
|
||||
|
||||
if (favoriteBtn) {
|
||||
favoriteBtn.onclick = function() {
|
||||
toggleFavorite(poetry.id);
|
||||
};
|
||||
}
|
||||
|
||||
if (analyzeBtn) {
|
||||
analyzeBtn.onclick = function() {
|
||||
startAIAnalysis(poetry);
|
||||
};
|
||||
}
|
||||
|
||||
if (shareBtn) {
|
||||
shareBtn.onclick = function() {
|
||||
sharePoetry(poetry);
|
||||
};
|
||||
}
|
||||
|
||||
// 显示模态框
|
||||
modal.classList.add('active');
|
||||
}
|
||||
|
||||
// 获取翻译(示例)
|
||||
function getTranslation(poetry) {
|
||||
const translations = {
|
||||
1: "明亮的月光洒在床前,好像地上泛起了一层白霜。\n我抬起头来,看那窗外天空中的明月,不由得低头沉思,想起远方的家乡。",
|
||||
2: "春日里贪睡不知不觉天已破晓,搅乱我酣眠的是那啁啾的小鸟。\n昨天夜里风声雨声一直不断,那娇美的春花不知被吹落了多少?",
|
||||
3: "明月从什么时候才开始出现的?我端起酒杯遥问苍天。\n不知道在天上的宫殿,今天晚上是何年何月。\n我想要乘御清风回到天上,又恐怕在美玉砌成的楼宇,受不住高耸九天的寒冷。\n翩翩起舞玩赏着月下清影,哪像是在人间。",
|
||||
4: "夕阳依傍着西山慢慢地沉没,滔滔黄河朝着东海汹涌奔流。\n若想把千里的风光景物看够,那就要登上更高的一层城楼。",
|
||||
5: "香炉峰在阳光的照射下生起紫色烟霞,从远处看去瀑布好似白色绢绸悬挂山前。\n高崖上飞腾直落的瀑布好像有几千尺,让人怀疑是银河从天上泻落到人间。",
|
||||
6: "所有的山上,飞鸟的身影已经绝迹,所有道路都不见人的踪迹。\n江面孤舟上,一位披戴着蓑笠的老翁,独自在漫天风雪中垂钓。"
|
||||
};
|
||||
|
||||
return translations[poetry.id] || "翻译加载中...";
|
||||
}
|
||||
|
||||
// 获取简要赏析(示例)
|
||||
function getBriefAnalysis(poetry) {
|
||||
const analyses = {
|
||||
1: "这首诗写的是在寂静的月夜思念家乡的感受。诗的前两句,是写诗人在作客他乡的特定环境中一刹那间所产生的错觉。后两句通过动作神态的刻画,深化思乡之情。",
|
||||
2: "这首诗是唐代诗人孟浩然隐居鹿门山时所作。诗人抓住春天的早晨刚刚醒来时的一瞬间展开联想,描绘了一幅春天早晨绚丽的图景,抒发了诗人热爱春天、珍惜春光的美好心情。",
|
||||
3: "此词是中秋望月怀人之作,表达了对胞弟苏辙的无限怀念。词人运用形象描绘手法,勾勒出一种皓月当空、亲人千里、孤高旷远的境界氛围,反衬自己遗世独立的意绪和往昔的神话传说融合一处,在月的阴晴圆缺当中,渗进浓厚的哲学意味。",
|
||||
4: "这首诗写诗人在登高望远中表现出来的不凡的胸襟抱负,反映了盛唐时期人们积极向上的进取精神。前两句写所见,后两句写所感,把哲理与景物、情势溶化得天衣无缝。",
|
||||
5: "这首诗形象地描绘了庐山瀑布雄奇壮丽的景色,反映了诗人对祖国大好河山的无限热爱。诗人用夸张的比喻和浪漫的想象,把瀑布的气势写得出神入化,给人以美的享受。",
|
||||
6: "这首诗描绘了一幅江乡雪景图。山山是雪,路路皆白。飞鸟绝迹,人踪湮没。遐景苍茫,迩景孤冷。意境幽僻,情调凄寂。渔翁形象,精雕细琢,清晰明朗,完整突出。"
|
||||
};
|
||||
|
||||
return analyses[poetry.id] || "赏析加载中...";
|
||||
}
|
||||
|
||||
// 设置事件监听器
|
||||
function setupEventListeners() {
|
||||
// 快速搜索按钮
|
||||
const quickSearchBtn = document.getElementById('quickSearchBtn');
|
||||
if (quickSearchBtn) {
|
||||
quickSearchBtn.addEventListener('click', performQuickSearch);
|
||||
}
|
||||
|
||||
// 快速搜索输入框回车键
|
||||
const quickSearchInput = document.getElementById('quickSearch');
|
||||
if (quickSearchInput) {
|
||||
quickSearchInput.addEventListener('keypress', function(e) {
|
||||
if (e.key === 'Enter') {
|
||||
performQuickSearch();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 高级搜索按钮
|
||||
const advancedSearchBtn = document.getElementById('advancedSearchBtn');
|
||||
if (advancedSearchBtn) {
|
||||
advancedSearchBtn.addEventListener('click', performAdvancedSearch);
|
||||
}
|
||||
|
||||
// 重置搜索按钮
|
||||
const resetSearchBtn = document.getElementById('resetSearchBtn');
|
||||
if (resetSearchBtn) {
|
||||
resetSearchBtn.addEventListener('click', resetSearch);
|
||||
}
|
||||
|
||||
// 生成AI解析按钮
|
||||
const generateAnalysisBtn = document.getElementById('generateAnalysisBtn');
|
||||
if (generateAnalysisBtn) {
|
||||
generateAnalysisBtn.addEventListener('click', generateAIAnalysis);
|
||||
}
|
||||
|
||||
// 快速筛选按钮
|
||||
document.querySelectorAll('.filter-btn').forEach(btn => {
|
||||
btn.addEventListener('click', function() {
|
||||
const dynasty = this.dataset.dynasty;
|
||||
const type = this.dataset.type;
|
||||
filterPoetry(dynasty, type);
|
||||
});
|
||||
});
|
||||
|
||||
// 视图切换按钮
|
||||
document.querySelectorAll('.view-btn').forEach(btn => {
|
||||
btn.addEventListener('click', function() {
|
||||
const view = this.dataset.view;
|
||||
switchView(view);
|
||||
});
|
||||
});
|
||||
|
||||
// 排序选择
|
||||
const sortBySelect = document.getElementById('sortBy');
|
||||
if (sortBySelect) {
|
||||
sortBySelect.addEventListener('change', function() {
|
||||
sortPoetry(this.value);
|
||||
});
|
||||
}
|
||||
|
||||
// 学习功能按钮
|
||||
document.getElementById('compareBtn')?.addEventListener('click', startComparison);
|
||||
document.getElementById('recommendBtn')?.addEventListener('click', getRecommendations);
|
||||
document.getElementById('qaBtn')?.addEventListener('click', startQA);
|
||||
document.getElementById('memorizeBtn')?.addEventListener('click', startMemorization);
|
||||
}
|
||||
|
||||
// 执行快速搜索
|
||||
function performQuickSearch() {
|
||||
const query = document.getElementById('quickSearch').value.trim();
|
||||
|
||||
if (!query) {
|
||||
alert('请输入搜索内容');
|
||||
return;
|
||||
}
|
||||
|
||||
showLoading(true);
|
||||
|
||||
// 模拟API调用
|
||||
setTimeout(() => {
|
||||
// 这里应该是实际的API调用
|
||||
console.log('执行快速搜索:', query);
|
||||
|
||||
// 显示搜索结果
|
||||
showSearchResults(query);
|
||||
showLoading(false);
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
// 执行高级搜索
|
||||
function performAdvancedSearch() {
|
||||
const title = document.getElementById('poetryTitle').value.trim();
|
||||
const author = document.getElementById('author').value.trim();
|
||||
const dynasty = document.getElementById('dynasty').value;
|
||||
const keywords = document.getElementById('keywords').value.trim();
|
||||
const poetryType = document.getElementById('poetryType').value;
|
||||
const wordCount = document.getElementById('wordCount').value;
|
||||
|
||||
// 构建搜索条件
|
||||
const searchCriteria = {
|
||||
title,
|
||||
author,
|
||||
dynasty,
|
||||
keywords,
|
||||
poetryType,
|
||||
wordCount
|
||||
};
|
||||
|
||||
console.log('高级搜索条件:', searchCriteria);
|
||||
|
||||
showLoading(true);
|
||||
|
||||
// 模拟API调用
|
||||
setTimeout(() => {
|
||||
// 这里应该是实际的API调用
|
||||
showSearchResults('高级搜索');
|
||||
showLoading(false);
|
||||
}, 1500);
|
||||
}
|
||||
|
||||
// 重置搜索
|
||||
function resetSearch() {
|
||||
document.getElementById('poetryTitle').value = '';
|
||||
document.getElementById('author').value = '';
|
||||
document.getElementById('dynasty').value = '';
|
||||
document.getElementById('keywords').value = '';
|
||||
document.getElementById('poetryType').value = '';
|
||||
document.getElementById('wordCount').value = '';
|
||||
|
||||
// 重新加载所有诗词
|
||||
loadSamplePoetryData();
|
||||
}
|
||||
|
||||
// 显示搜索结果
|
||||
function showSearchResults(query) {
|
||||
const analysisResult = document.getElementById('analysisResult');
|
||||
if (!analysisResult) return;
|
||||
|
||||
analysisResult.innerHTML = `
|
||||
<div class="search-result">
|
||||
<h3><i class="fas fa-search"></i> 搜索结果:${query}</h3>
|
||||
<p>找到 <strong>6</strong> 首相关诗词</p>
|
||||
<div class="result-list">
|
||||
<p>1. 静夜思 - 李白 (唐代)</p>
|
||||
<p>2. 春晓 - 孟浩然 (唐代)</p>
|
||||
<p>3. 水调歌头·明月几时有 - 苏轼 (宋代)</p>
|
||||
<p>4. 登鹳雀楼 - 王之涣 (唐代)</p>
|
||||
<p>5. 望庐山瀑布 - 李白 (唐代)</p>
|
||||
<p>6. 江雪 - 柳宗元 (唐代)</p>
|
||||
</div>
|
||||
<p class="note">注:这是演示数据,实际应用中会连接到真实的诗词数据库和AI解析服务。</p>
|
||||
</div>
|
||||
`;
|
||||
|
||||
// 滚动到结果区域
|
||||
document.getElementById('ai-analysis').scrollIntoView({ behavior: 'smooth' });
|
||||
}
|
||||
|
||||
// 显示/隐藏加载指示器
|
||||
function showLoading(show) {
|
||||
const loadingIndicator = document.getElementById('loadingIndicator');
|
||||
if (loadingIndicator) {
|
||||
loadingIndicator.style.display = show ? 'block' : 'none';
|
||||
}
|
||||
}
|
||||
|
||||
// 过滤诗词
|
||||
function filterPoetry(dynasty, type) {
|
||||
showLoading(true);
|
||||
|
||||
setTimeout(() => {
|
||||
console.log(`过滤诗词: 朝代=${dynasty}, 类型=${type}`);
|
||||
|
||||
// 这里应该是实际的过滤逻辑
|
||||
// 暂时重新加载所有数据
|
||||
loadSamplePoetryData();
|
||||
showLoading(false);
|
||||
}, 800);
|
||||
}
|
||||
|
||||
// 切换视图
|
||||
function switchView(view) {
|
||||
const poetryGrid = document.getElementById('poetryGrid');
|
||||
if (!poetryGrid) return;
|
||||
|
||||
// 更新按钮状态
|
||||
document.querySelectorAll('.view-btn').forEach(btn => {
|
||||
btn.classList.toggle('active', btn.dataset.view === view);
|
||||
});
|
||||
|
||||
// 切换视图类
|
||||
poetryGrid.className = view === 'list' ? 'poetry-list' : 'poetry-grid';
|
||||
|
||||
console.log(`切换到${view === 'list' ? '列表' : '网格'}视图`);
|
||||
}
|
||||
|
||||
// 排序诗词
|
||||
function sortPoetry(sortBy) {
|
||||
console.log(`按${sortBy}排序诗词`);
|
||||
|
||||
// 这里应该是实际的排序逻辑
|
||||
// 暂时只是重新加载
|
||||
loadSamplePoetryData();
|
||||
}
|
||||
|
||||
// 生成AI解析
|
||||
function generateAIAnalysis() {
|
||||
const purpose = document.getElementById('purpose').value;
|
||||
const usage = document.getElementById('usage').value;
|
||||
const targetAudience = document.getElementById('targetAudience').value;
|
||||
const depth = document.getElementById('depth').value;
|
||||
const focus = document.getElementById('focus').value;
|
||||
|
||||
// 获取当前选中的诗词(如果有)
|
||||
const selectedPoetry = getSelectedPoetry();
|
||||
|
||||
if (!selectedPoetry) {
|
||||
alert('请先选择一首诗词(点击诗词卡片)');
|
||||
return;
|
||||
}
|
||||
|
||||
showLoading(true);
|
||||
|
||||
// 模拟AI解析生成
|
||||
setTimeout(() => {
|
||||
const analysisResult = document.getElementById('analysisResult');
|
||||
if (!analysisResult) return;
|
||||
|
||||
analysisResult.innerHTML = `
|
||||
<div class="ai-analysis-result">
|
||||
<h3><i class="fas fa-robot"></i> AI深度解析结果</h3>
|
||||
<div class="analysis-header">
|
||||
<h4>${selectedPoetry.title} - ${selectedPoetry.author} (${selectedPoetry.dynasty})</h4>
|
||||
<div class="analysis-meta">
|
||||
<span><i class="fas fa-bullseye"></i> 目的: ${purpose}</span>
|
||||
<span><i class="fas fa-scenario"></i> 场景: ${usage}</span>
|
||||
<span><i class="fas fa-users"></i> 读者: ${targetAudience}</span>
|
||||
<span><i class="fas fa-layer-group"></i> 深度: ${depth}</span>
|
||||
<span><i class="fas fa-crosshairs"></i> 重点: ${focus}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="analysis-content">
|
||||
<h5>📖 诗词原文</h5>
|
||||
<pre>${selectedPoetry.content}</pre>
|
||||
|
||||
<h5>🔍 ${focus}分析</h5>
|
||||
<p>${generateAIAnalysisContent(selectedPoetry, focus, depth)}</p>
|
||||
|
||||
<h5>🎯 针对${targetAudience}的解读建议</h5>
|
||||
<p>${generateAudienceSpecificAdvice(targetAudience, selectedPoetry)}</p>
|
||||
|
||||
<h5>💡 教学/学习建议</h5>
|
||||
<p>${generateLearningSuggestions(usage, purpose)}</p>
|
||||
|
||||
<h5>📊 文学价值评估</h5>
|
||||
<div class="value-assessment">
|
||||
<div class="assessment-item">
|
||||
<span>艺术成就:</span>
|
||||
<div class="progress-bar">
|
||||
<div class="progress" style="width: ${85 + Math.random() * 10}%"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="assessment-item">
|
||||
<span>历史地位:</span>
|
||||
<div class="progress-bar">
|
||||
<div class="progress" style="width: ${80 + Math.random() * 15}%"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="assessment-item">
|
||||
<span>文化影响:</span>
|
||||
<div class="progress-bar">
|
||||
<div class="progress" style="width: ${75 + Math.random() * 20}%"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="analysis-footer">
|
||||
<p class="note"><i class="fas fa-info-circle"></i> 此解析由AI生成,基于深度学习和古典文学知识库。实际应用中会连接到真实的AI解析服务。</p>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
showLoading(false);
|
||||
|
||||
// 滚动到结果区域
|
||||
analysisResult.scrollIntoView({ behavior: 'smooth' });
|
||||
}, 2000);
|
||||
}
|
||||
|
||||
// 获取选中的诗词
|
||||
function getSelectedPoetry() {
|
||||
// 这里应该从状态管理获取当前选中的诗词
|
||||
// 暂时返回第一首作为示例
|
||||
return {
|
||||
id: 1,
|
||||
title: "静夜思",
|
||||
author: "李白",
|
||||
dynasty: "唐",
|
||||
content: "床前明月光,疑是地上霜。\n举头望明月,低头思故乡。",
|
||||
type: "抒情",
|
||||
wordCount: "五言",
|
||||
tags: ["思乡", "月亮", "夜晚"]
|
||||
};
|
||||
}
|
||||
|
||||
// 生成AI分析内容
|
||||
function generateAIAnalysisContent(poetry, focus, depth) {
|
||||
const focusTemplates = {
|
||||
"意境分析": `《${poetry.title}》通过${poetry.content.split(',')[0]}等意象,营造出一种${poetry.tags[0]}的意境。诗人巧妙运用空间对比和时间流逝的描写,使读者感受到${poetry.author}独特的情感表达方式。`,
|
||||
"修辞手法": `本诗运用了比喻、对仗等修辞手法。如"${poetry.content.split('。')[0]}"一句,通过${poetry.tags[1]}的比喻,增强了诗歌的形象性和感染力。`,
|
||||
"格律分析": `作为一首${poetry.wordCount}诗,本诗严格遵守古典诗歌的格律要求。平仄搭配得当,押韵工整,体现了${poetry.dynasty}时期诗歌的形式美。`,
|
||||
"历史背景": `创作于${poetry.dynasty}时期,反映了当时的社会文化背景。${poetry.author}在${poetry.tags[2]}的背景下,通过这首诗表达了...`,
|
||||
"文学价值": `《${poetry.title}》在中国文学史上具有重要地位,对后世诗歌创作产生了深远影响。其${depth}的文学价值体现在...`,
|
||||
"综合解析": `从多个角度分析,《${poetry.title}》展现了${poetry.author}高超的艺术造诣。在意境营造、修辞运用、格律把握等方面都达到了很高水平。`
|
||||
};
|
||||
|
||||
return focusTemplates[focus] || "AI解析内容生成中...";
|
||||
}
|
||||
|
||||
// 生成读者特定建议
|
||||
function generateAudienceSpecificAdvice(audience, poetry) {
|
||||
const adviceTemplates = {
|
||||
"中小学生": `对于中小学生,建议重点理解诗词的字面意思和基本情感。可以通过绘画、朗诵等方式增强对"${poetry.tags[0]}"主题的理解。`,
|
||||
"大学生": `大学生可以深入分析诗词的文学技巧和历史背景,探讨${poetry.author}的创作风格及其在文学史上的地位。`,
|
||||
"文学爱好者": `建议对比阅读${poetry.author}的其他作品,研究其艺术特色的发展变化,深入探讨${poetry.dynasty}诗歌的特点。`,
|
||||
"研究人员": `可以从学术角度进行深入研究,关注版本校勘、注释考证、文学批评等多个维度,挖掘诗词的深层文化内涵。`,
|
||||
"普通读者": `建议从情感共鸣入手,欣赏诗词的意境美和语言美,不必过于追求学术性的分析。`
|
||||
};
|
||||
|
||||
return adviceTemplates[audience] || "根据读者特点提供个性化解读建议。";
|
||||
}
|
||||
|
||||
// 生成学习建议
|
||||
function generateLearningSuggestions(usage, purpose) {
|
||||
const suggestionTemplates = {
|
||||
"课堂教学": `1. 课前预习:了解诗人生平和创作背景\n2. 课堂讲解:重点分析诗词意象和情感\n3. 课后拓展:相关诗词对比阅读\n4. 实践活动:诗词朗诵或创作练习`,
|
||||
"自学研究": `1. 建立知识框架:了解${purpose}的基本方法\n2. 深度阅读:查阅相关文献和注释\n3. 笔记整理:记录关键见解和疑问\n4. 实践应用:尝试撰写小论文或赏析文章`,
|
||||
"文学创作": `1. 模仿练习:学习诗词的意象运用\n2. 技巧分析:研究修辞手法和格律\n3. 创作实践:尝试创作类似主题的诗歌\n4. 修改完善:反复推敲字句和意境`,
|
||||
"文化传播": `1. 通俗化解读:用现代语言解释古典诗词\n2. 多媒体呈现:结合图片、音频等媒介\n3. 互动设计:设计问答、游戏等互动环节\n4. 社交媒体:制作易于传播的诗词卡片`,
|
||||
"考试备考": `1. 考点梳理:明确常考知识点\n2. 真题练习:熟悉题型和答题技巧\n3. 记忆方法:使用联想记忆等技巧\n4. 模拟测试:进行限时练习和评估`
|
||||
};
|
||||
|
||||
return suggestionTemplates[usage] || "根据使用场景提供具体的学习建议。";
|
||||
}
|
||||
|
||||
// 切换收藏状态
|
||||
function toggleFavorite(poetryId) {
|
||||
const favoriteBtn = document.getElementById('favoriteBtn');
|
||||
const isFavorite = favoriteBtn.classList.contains('favorited');
|
||||
|
||||
if (isFavorite) {
|
||||
favoriteBtn.innerHTML = '<i class="far fa-heart"></i> 收藏';
|
||||
favoriteBtn.classList.remove('favorited');
|
||||
alert('已取消收藏');
|
||||
} else {
|
||||
favoriteBtn.innerHTML = '<i class="fas fa-heart"></i> 已收藏';
|
||||
favoriteBtn.classList.add('favorited');
|
||||
alert('已添加到收藏夹');
|
||||
}
|
||||
|
||||
console.log(`诗词ID ${poetryId} 收藏状态: ${!isFavorite ? '已收藏' : '已取消'}`);
|
||||
}
|
||||
|
||||
// 开始AI分析
|
||||
function startAIAnalysis(poetry) {
|
||||
// 设置当前诗词
|
||||
console.log('开始AI分析:', poetry.title);
|
||||
|
||||
// 滚动到AI分析区域
|
||||
document.getElementById('ai-analysis').scrollIntoView({ behavior: 'smooth' });
|
||||
|
||||
// 显示提示
|
||||
alert(`已选择《${poetry.title}》,请在AI解析区域设置参数并点击"生成AI解析"按钮。`);
|
||||
}
|
||||
|
||||
// 分享诗词
|
||||
function sharePoetry(poetry) {
|
||||
const shareText = `分享一首古诗词:《${poetry.title}》 - ${poetry.author} (${poetry.dynasty})\n\n${poetry.content}\n\n来自古诗词智能解析器`;
|
||||
|
||||
if (navigator.share) {
|
||||
navigator.share({
|
||||
title: poetry.title,
|
||||
text: shareText,
|
||||
url: window.location.href
|
||||
});
|
||||
} else {
|
||||
// 复制到剪贴板
|
||||
navigator.clipboard.writeText(shareText).then(() => {
|
||||
alert('诗词内容已复制到剪贴板,可以粘贴到其他应用分享!');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 开始对比分析
|
||||
function startComparison() {
|
||||
alert('对比分析功能:请选择两首诗词进行对比。\n\n演示版本中,此功能需要连接到实际的AI对比分析服务。');
|
||||
}
|
||||
|
||||
// 获取推荐
|
||||
function getRecommendations() {
|
||||
alert('相似推荐功能:基于当前诗词推荐主题、风格相似的作品。\n\n演示版本中,此功能需要连接到实际的推荐算法服务。');
|
||||
}
|
||||
|
||||
// 开始问答
|
||||
function startQA() {
|
||||
alert('知识问答功能:AI问答关于诗词的各类问题。\n\n演示版本中,此功能需要连接到实际的AI问答服务。');
|
||||
}
|
||||
|
||||
// 开始背诵
|
||||
function startMemorization() {
|
||||
alert('背诵助手功能:提供填空测试、顺序排序等背诵辅助。\n\n演示版本中,此功能需要连接到实际的背诵算法服务。');
|
||||
}
|
||||
217
static/js/service-worker.js
Normal file
217
static/js/service-worker.js
Normal file
@@ -0,0 +1,217 @@
|
||||
// 古诗词智能解析器 - Service Worker
|
||||
// 支持PWA功能,提供离线访问能力
|
||||
|
||||
const CACHE_NAME = 'poetry-ai-v1.0.0';
|
||||
const urlsToCache = [
|
||||
'/',
|
||||
'/index.html',
|
||||
'/static/css/style.css',
|
||||
'/static/js/main.js',
|
||||
'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css',
|
||||
'https://fonts.googleapis.com/css2?family=Noto+Serif+SC:wght@400;700&family=Ma+Shan+Zheng&display=swap'
|
||||
];
|
||||
|
||||
// 安装Service Worker
|
||||
self.addEventListener('install', event => {
|
||||
console.log('Service Worker: 安装中...');
|
||||
|
||||
event.waitUntil(
|
||||
caches.open(CACHE_NAME)
|
||||
.then(cache => {
|
||||
console.log('Service Worker: 缓存文件中...');
|
||||
return cache.addAll(urlsToCache);
|
||||
})
|
||||
.then(() => {
|
||||
console.log('Service Worker: 安装完成');
|
||||
return self.skipWaiting();
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
// 激活Service Worker
|
||||
self.addEventListener('activate', event => {
|
||||
console.log('Service Worker: 激活中...');
|
||||
|
||||
// 清理旧缓存
|
||||
event.waitUntil(
|
||||
caches.keys().then(cacheNames => {
|
||||
return Promise.all(
|
||||
cacheNames.map(cacheName => {
|
||||
if (cacheName !== CACHE_NAME) {
|
||||
console.log('Service Worker: 清理旧缓存:', cacheName);
|
||||
return caches.delete(cacheName);
|
||||
}
|
||||
})
|
||||
);
|
||||
}).then(() => {
|
||||
console.log('Service Worker: 激活完成');
|
||||
return self.clients.claim();
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
// 拦截网络请求
|
||||
self.addEventListener('fetch', event => {
|
||||
// 只处理GET请求
|
||||
if (event.request.method !== 'GET') return;
|
||||
|
||||
// 对于API请求,使用网络优先策略
|
||||
if (event.request.url.includes('/api/')) {
|
||||
event.respondWith(
|
||||
fetch(event.request)
|
||||
.then(response => {
|
||||
// 克隆响应以进行缓存
|
||||
const responseToCache = response.clone();
|
||||
|
||||
caches.open(CACHE_NAME)
|
||||
.then(cache => {
|
||||
cache.put(event.request, responseToCache);
|
||||
});
|
||||
|
||||
return response;
|
||||
})
|
||||
.catch(() => {
|
||||
// 网络失败时尝试从缓存获取
|
||||
return caches.match(event.request);
|
||||
})
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// 对于静态资源,使用缓存优先策略
|
||||
event.respondWith(
|
||||
caches.match(event.request)
|
||||
.then(cachedResponse => {
|
||||
if (cachedResponse) {
|
||||
console.log('Service Worker: 从缓存返回:', event.request.url);
|
||||
return cachedResponse;
|
||||
}
|
||||
|
||||
// 缓存中没有,从网络获取
|
||||
return fetch(event.request)
|
||||
.then(response => {
|
||||
// 检查响应是否有效
|
||||
if (!response || response.status !== 200 || response.type !== 'basic') {
|
||||
return response;
|
||||
}
|
||||
|
||||
// 克隆响应以进行缓存
|
||||
const responseToCache = response.clone();
|
||||
|
||||
caches.open(CACHE_NAME)
|
||||
.then(cache => {
|
||||
cache.put(event.request, responseToCache);
|
||||
});
|
||||
|
||||
return response;
|
||||
})
|
||||
.catch(error => {
|
||||
console.log('Service Worker: 获取失败:', error);
|
||||
|
||||
// 对于HTML页面,返回离线页面
|
||||
if (event.request.headers.get('accept').includes('text/html')) {
|
||||
return caches.match('/index.html');
|
||||
}
|
||||
|
||||
// 对于其他资源,可以返回一个占位符
|
||||
return new Response('网络连接失败,请检查网络设置。', {
|
||||
status: 408,
|
||||
headers: { 'Content-Type': 'text/plain' }
|
||||
});
|
||||
});
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
// 处理推送通知
|
||||
self.addEventListener('push', event => {
|
||||
console.log('Service Worker: 收到推送通知');
|
||||
|
||||
const options = {
|
||||
body: event.data ? event.data.text() : '古诗词智能解析器有新内容更新',
|
||||
icon: '/static/images/icon-192.png',
|
||||
badge: '/static/images/badge-72.png',
|
||||
vibrate: [100, 50, 100],
|
||||
data: {
|
||||
dateOfArrival: Date.now(),
|
||||
primaryKey: '1'
|
||||
},
|
||||
actions: [
|
||||
{
|
||||
action: 'explore',
|
||||
title: '探索诗词',
|
||||
icon: '/static/images/explore-icon.png'
|
||||
},
|
||||
{
|
||||
action: 'close',
|
||||
title: '关闭',
|
||||
icon: '/static/images/close-icon.png'
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
event.waitUntil(
|
||||
self.registration.showNotification('古诗词智能解析器', options)
|
||||
);
|
||||
});
|
||||
|
||||
// 处理通知点击
|
||||
self.addEventListener('notificationclick', event => {
|
||||
console.log('Service Worker: 通知被点击');
|
||||
|
||||
event.notification.close();
|
||||
|
||||
if (event.action === 'explore') {
|
||||
// 用户点击了"探索诗词"按钮
|
||||
event.waitUntil(
|
||||
clients.openWindow('/')
|
||||
);
|
||||
} else {
|
||||
// 用户点击了通知主体
|
||||
event.waitUntil(
|
||||
clients.matchAll({
|
||||
type: 'window',
|
||||
includeUncontrolled: true
|
||||
}).then(clientList => {
|
||||
// 如果已经有打开的窗口,聚焦它
|
||||
for (const client of clientList) {
|
||||
if (client.url === '/' && 'focus' in client) {
|
||||
return client.focus();
|
||||
}
|
||||
}
|
||||
|
||||
// 否则打开新窗口
|
||||
if (clients.openWindow) {
|
||||
return clients.openWindow('/');
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
// 处理后台同步
|
||||
self.addEventListener('sync', event => {
|
||||
console.log('Service Worker: 后台同步:', event.tag);
|
||||
|
||||
if (event.tag === 'sync-favorites') {
|
||||
event.waitUntil(syncFavorites());
|
||||
}
|
||||
});
|
||||
|
||||
// 同步收藏数据
|
||||
function syncFavorites() {
|
||||
console.log('Service Worker: 同步收藏数据');
|
||||
|
||||
// 这里应该实现实际的同步逻辑
|
||||
// 暂时只是记录日志
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
// 处理消息
|
||||
self.addEventListener('message', event => {
|
||||
console.log('Service Worker: 收到消息:', event.data);
|
||||
|
||||
if (event.data && event.data.type === 'SKIP_WAITING') {
|
||||
self.skipWaiting();
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user