From ff742c0c434908e92be02f421de8fe78de06e00a Mon Sep 17 00:00:00 2001
From: TortoiseGit <3490929155@qq.com>
Date: Wed, 12 Nov 2025 16:24:31 +0800
Subject: [PATCH] add
---
app.js | 10 +-
app.json | 14 +-
components/navigation-bar/navigation-bar.js | 9 +-
pages/guiding/guiding.js | 181 +++--
pages/home/home.js | 86 ---
pages/home/home.json | 4 -
pages/home/home.wxml | 33 -
pages/home/home.wxss | 50 --
pages/index/index.js | 187 ++++-
pages/index/index.json | 6 +-
pages/index/index.wxml | 70 +-
pages/index/index.wxss | 226 +++++-
pages/keyWord/keyWord.js | 323 ++++++++-
pages/keyWord/keyWord.wxml | 83 ++-
pages/keyWord/keyWord.wxss | 232 ++++++-
pages/login/login.js | 124 +++-
pages/pendingQuestion/pendingQuestion.js | 125 +++-
pages/pendingQuestion/pendingQuestion.wxml | 38 +-
pages/pendingQuestion/pendingQuestion.wxss | 103 ++-
pages/questionAnswer/questionAnswer.js | 66 --
pages/questionAnswer/questionAnswer.json | 3 -
pages/questionAnswer/questionAnswer.wxml | 2 -
pages/questionAnswer/questionAnswer.wxss | 1 -
pages/recite/recite.js | 645 +++++++++++++++--
pages/recite/recite.wxml | 64 +-
pages/recite/recite.wxss | 219 +++++-
pages/review/review.js | 723 +++++++++++++++++++-
pages/review/review.wxml | 56 +-
pages/review/review.wxss | 216 +++++-
pages/search/search.js | 273 +++++++-
pages/search/search.wxml | 98 ++-
pages/search/search.wxss | 303 +++++++-
pages/study/study.js | 426 +++++++++++-
pages/study/study.wxml | 126 +++-
pages/study/study.wxss | 351 +++++++++-
pages/textbookFilter/textbookFilter.js | 66 --
pages/textbookFilter/textbookFilter.json | 3 -
pages/textbookFilter/textbookFilter.wxml | 2 -
pages/textbookFilter/textbookFilter.wxss | 1 -
project.config.json | 16 +-
project.private.config.json | 82 +--
41 files changed, 5045 insertions(+), 601 deletions(-)
delete mode 100644 pages/home/home.js
delete mode 100644 pages/home/home.json
delete mode 100644 pages/home/home.wxml
delete mode 100644 pages/home/home.wxss
delete mode 100644 pages/questionAnswer/questionAnswer.js
delete mode 100644 pages/questionAnswer/questionAnswer.json
delete mode 100644 pages/questionAnswer/questionAnswer.wxml
delete mode 100644 pages/questionAnswer/questionAnswer.wxss
delete mode 100644 pages/textbookFilter/textbookFilter.js
delete mode 100644 pages/textbookFilter/textbookFilter.json
delete mode 100644 pages/textbookFilter/textbookFilter.wxml
delete mode 100644 pages/textbookFilter/textbookFilter.wxss
diff --git a/app.js b/app.js
index 4af33be..04da539 100644
--- a/app.js
+++ b/app.js
@@ -1,2 +1,10 @@
// app.js
-App({})
+App({
+ onLaunch() {
+ wx.cloud.init({
+ env: 'cloud1-0g2sr1117862afae'
+ });
+
+ console.log('小程序启动,云环境初始化完成');
+ }
+})
diff --git a/app.json b/app.json
index 4c253a7..cc97cac 100644
--- a/app.json
+++ b/app.json
@@ -2,16 +2,16 @@
"pages": [
"pages/guiding/guiding",
- "pages/home/home",
- "pages/recite/recite",
- "pages/review/review",
+ "pages/login/login",
"pages/index/index",
+ "pages/study/study",
"pages/search/search",
- "pages/login/login",
- "pages/keyWord/keyWord",
- "pages/questionAnswer/questionAnswer",
+ "pages/recite/recite",
+ "pages/review/review",
"pages/pendingQuestion/pendingQuestion",
- "pages/textbookFilter/textbookFilter"
+ "pages/keyWord/keyWord",
+ "pages/question/question",
+ "pages/managePoems/managePoems"
],
"window": {
"navigationBarTitleText": "古诗学习助手",
diff --git a/components/navigation-bar/navigation-bar.js b/components/navigation-bar/navigation-bar.js
index eb1770e..48ed08c 100644
--- a/components/navigation-bar/navigation-bar.js
+++ b/components/navigation-bar/navigation-bar.js
@@ -60,10 +60,15 @@ Component({
lifetimes: {
attached() {
const rect = wx.getMenuButtonBoundingClientRect()
- const platform = (wx.getDeviceInfo() || wx.getSystemInfoSync()).platform
+ const deviceInfo = wx.getDeviceInfo()
+ const platform = deviceInfo ? deviceInfo.platform : 'devtools'
const isAndroid = platform === 'android'
const isDevtools = platform === 'devtools'
- const { windowWidth, safeArea: { top = 0, bottom = 0 } = {} } = wx.getWindowInfo() || wx.getSystemInfoSync()
+ const windowInfo = wx.getWindowInfo()
+ const windowWidth = windowInfo ? windowInfo.windowWidth : 375
+ const safeArea = windowInfo && windowInfo.safeArea ? windowInfo.safeArea : {}
+ const top = safeArea.top || 0
+ const bottom = safeArea.bottom || 0
this.setData({
ios: !isAndroid,
innerPaddingRight: `padding-right: ${windowWidth - rect.left}px`,
diff --git a/pages/guiding/guiding.js b/pages/guiding/guiding.js
index 005584b..d37b847 100644
--- a/pages/guiding/guiding.js
+++ b/pages/guiding/guiding.js
@@ -1,71 +1,134 @@
-// pages/guiding/guiding.js
Page({
-
- /**
- * 页面的初始数据
- */
data: {
-
+ ifEntry: false,
+ userInfo: null
},
- /**
- * 生命周期函数--监听页面加载
- */
onLoad(options) {
-
+ // 页面加载时先检查本地缓存
+ this.checkLocalLoginStatus();
},
-
- /**
- * 生命周期函数--监听页面初次渲染完成
- */
- onReady() {
-
+ // 检查本地登录状态(快速检查)
+ checkLocalLoginStatus() {
+ try {
+ const cachedUserInfo = wx.getStorageSync('userInfo');
+ if (cachedUserInfo) {
+ this.setData({
+ ifEntry: true,
+ userInfo: cachedUserInfo
+ });
+ console.log('从缓存读取用户信息:', cachedUserInfo);
+ } else {
+ this.setData({
+ ifEntry: false,
+ userInfo: null
+ });
+ }
+ } catch (error) {
+ console.error('读取缓存失败:', error);
+ this.setData({ ifEntry: false });
+ }
},
-
- /**
- * 生命周期函数--监听页面显示
- */
- onShow() {
-
+//检查登录状态
+ async checkLoginStatus() {
+ try {
+ // 强制清除缓存,确保从服务器获取最新的用户信息
+ this.clearStorage();
+ // 先检查本地是否有缓存
+ const cachedUserInfo = wx.getStorageSync('userInfo');
+ if (cachedUserInfo) {
+ console.log('使用缓存用户信息,直接进入主页');
+ this.setData({
+ ifEntry: true,
+ userInfo: cachedUserInfo
+ });
+ // 调试日志:查看缓存中的用户角色值
+ console.log('缓存用户角色值:', cachedUserInfo.role, '类型:', typeof cachedUserInfo.role);
+
+ // 有缓存时直接跳转到主页 - 安全比较,处理不同类型的值
+ if (cachedUserInfo.role === true || cachedUserInfo.role === 'true') {
+ console.log('缓存中为普通用户,跳转到index页面');
+ wx.reLaunch({
+ url: `/pages/index/index?openid=${cachedUserInfo.openid}`
+ });
+ } else {
+ console.log('缓存中为管理员,跳转到pendingQuestion页面');
+ wx.reLaunch({
+ url: `/pages/pendingQuestion/pendingQuestion?openid=${cachedUserInfo.openid}`
+ });
+ }
+ return; // 跳转后可以return,避免后续代码执行
+ }
+ // 1. 如果没有缓存,调用 wx.login() 获取 code
+ const loginRes = await wx.login();
+ const code = loginRes.code;
+
+ if (!code) {
+ console.error('获取code失败');
+ return;
+ }
+
+ // 2. 调用后端登录接口,将code传给后端
+ const result = await wx.cloud.callFunction({
+ name: 'auth',
+ data: {
+ action: 'loginWithCode',
+ code: code
+ }
+ });
+ if (result.result.success) {
+ const userInfo = result.result.data.userInfo;
+ wx.setStorageSync('userInfo', userInfo);
+ // 登录成功,直接进入用户页面
+ this.setData({
+ ifEntry: true,
+ userInfo: userInfo
+ });
+
+ // 调试日志:查看用户角色值
+ console.log('用户角色值:', userInfo.role, '类型:', typeof userInfo.role);
+
+ // 跳转到用户首页 - 安全比较,处理不同类型的值
+ if (userInfo.role === true || userInfo.role === 'true') {
+ console.log('普通用户,跳转到index页面');
+ wx.reLaunch({
+ url: `/pages/index/index?openid=${userInfo.openid}`
+ });
+ } else {
+ console.log('管理员,跳转到pendingQuestion页面');
+ wx.reLaunch({
+ url: `/pages/pendingQuestion/pendingQuestion?openid=${userInfo.openid}`
+ });
+ }
+
+ } else {
+ // 登录失败
+ console.error('登录失败:', result.result.message);
+ this.setData({
+ ifEntry: false
+ });
+ const openid = result.result.openid || '';
+ wx.reLaunch({
+ url: `/pages/login/login?openid=${openid}`
+ });
+ }
+ } catch (error) {
+ console.error('检查登录状态失败:', error);
+ this.clearStorage();
+ }
},
- /**
- * 生命周期函数--监听页面隐藏
- */
- onHide() {
-
+ clearStorage() {
+ wx.removeStorageSync('token');
+ wx.removeStorageSync('userInfo');
+ this.setData({ ifEntry: false });
},
+
- /**
- * 生命周期函数--监听页面卸载
- */
- onUnload() {
-
- },
-
- /**
- * 页面相关事件处理函数--监听用户下拉动作
- */
- onPullDownRefresh() {
-
- },
-
- /**
- * 页面上拉触底事件的处理函数
- */
- onReachBottom() {
-
- },
-
- /**
- * 用户点击右上角分享
- */
- onShareAppMessage() {
-
- },
- request_enter(){
- wx.redirectTo({
- url: '/pages/login/login',
- });
+ // 进入按钮
+ async request_enter() {
+ this.checkLoginStatus();
+
+
}
-})
\ No newline at end of file
+});
\ No newline at end of file
diff --git a/pages/home/home.js b/pages/home/home.js
deleted file mode 100644
index 403ede8..0000000
--- a/pages/home/home.js
+++ /dev/null
@@ -1,86 +0,0 @@
-// pages/home/nome.js
-Page({
-
- /**
- * 页面的初始数据
- */
- data: {
-
- },
-
- /**
- * 生命周期函数--监听页面加载
- */
- onLoad(options) {
-
- },
-
- /**
- * 生命周期函数--监听页面初次渲染完成
- */
- onReady() {
-
- },
-
- /**
- * 生命周期函数--监听页面显示
- */
- onShow() {
-
- },
-
- /**
- * 生命周期函数--监听页面隐藏
- */
- onHide() {
-
- },
-
- /**
- * 生命周期函数--监听页面卸载
- */
- onUnload() {
-
- },
-
- /**
- * 页面相关事件处理函数--监听用户下拉动作
- */
- onPullDownRefresh() {
-
- },
-
- /**
- * 页面上拉触底事件的处理函数
- */
- onReachBottom() {
-
- },
-
- /**
- * 用户点击右上角分享
- */
- onShareAppMessage() {
-
- },
- review(){
- wx.navigateTo({
- url: '/pages/review/review'
- })
- },
- filter(){
- wx.navigateTo({
- url: '/pages/textbookFilter/textbookFilter'
- })
-},
-question(){
- wx.navigateTo({
- url: '/pages/pendingQuestion/pendingQuestion'
- })
-},
-search(){
- wx.navigateTo({
- url: '/pages/search/search'
- })
-}
-})
\ No newline at end of file
diff --git a/pages/home/home.json b/pages/home/home.json
deleted file mode 100644
index 105ebcd..0000000
--- a/pages/home/home.json
+++ /dev/null
@@ -1,4 +0,0 @@
-{
- "usingComponents": {}
-
-}
\ No newline at end of file
diff --git a/pages/home/home.wxml b/pages/home/home.wxml
deleted file mode 100644
index 054d938..0000000
--- a/pages/home/home.wxml
+++ /dev/null
@@ -1,33 +0,0 @@
-
-
-
- 欢迎回来{{userInfo.nickName || '勤奋的'}}小朋友
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/pages/home/home.wxss b/pages/home/home.wxss
deleted file mode 100644
index b2849ae..0000000
--- a/pages/home/home.wxss
+++ /dev/null
@@ -1,50 +0,0 @@
-/* pages/home/home.wxss */
-.bottom-buttons {
- display: flex; /* 按钮横向排列 */
- position: fixed;
- justify-content: space-around;
- bottom: 200rpx ;
- border-top: 1px solid rgba(255, 255, 255, 0); /* 顶部边框分隔 */
- border-bottom: 1px solid #eee;
- width: 100%;
- left : 0%;
-}
-.func-btn {
- display: flex;
- flex-direction: column;
- align-items: center;
- background-color: transparent; /* 清除默认按钮背景 */
- border-top: 1px solid rgba(255, 255, 255, 0);
- width: 20%;
-}
-.btn-icon {
- width: 200rpx;
-}
-.user-info {
- display: flex;
- align-items: baseline;
- padding: 30rpx 20rpx;
- top: 0;
- right:0;
- position: absolute; /* 脱离文档流,基于父容器 .home-container 定位 */
- z-index: 1; /* 确保在其他元素上方显示 */
-}
-/* 头像:圆形裁剪,固定大小 */
-.user-avatar {
- width: 100rpx;
- height: 100rpx;
- border-radius: 50%; /* 圆形 */
- border: 2rpx solid #000; /* 白色边框增加层次感 */
- box-shadow: 0 2rpx 8rpx rgba(0,0,0,0.1); /* 轻微阴影 */
- margin-right: 20rpx; /* 与昵称间距 */
-}
-
-/* 昵称:字体大小和颜色 */
-.user-name {
- font-size: 36rpx;
- font-weight: 500;
- color: #333;
-}
-.container{
- position: relative; /* 关键:让子元素 .user-info 能基于它做绝对定位 */
-}
diff --git a/pages/index/index.js b/pages/index/index.js
index 5ac0437..be99d10 100644
--- a/pages/index/index.js
+++ b/pages/index/index.js
@@ -1,2 +1,185 @@
-// index.js 代码控制
-Page({})
+Page({
+ data: {
+ userInfo: {},
+ openid: '',
+ poems: [], // 所有诗词列表
+ reviewedPoems: [], // 需要复习的诗词
+ otherPoems: [], // 其他诗词
+ isLoading: false
+ },
+
+ onLoad(options) {
+ console.log('首页加载完成');
+ if (options.openid) {
+ this.setData({
+ openid: options.openid
+ });
+ console.log('获取到openid:', options.openid);
+
+ // 根据openid加载用户数据和诗词数据
+ this.loadUserDataAndPoems();
+ } else {
+ console.error('未收到openid参数');
+ wx.showToast({
+ title: '参数错误',
+ icon: 'none'
+ });
+ }
+ },
+// 加载用户数据和诗词数据
+ async loadUserDataAndPoems() {
+ try {
+ this.setData({ isLoading: true });
+
+ // 并行执行:获取用户信息和诗词列表
+ const [userResult, poemsResult] = await Promise.all([
+ this.getUserInfo(this.data.openid),
+ this.getAllPoems()
+ ]);
+
+ if (userResult.success) {
+ const userInfo = userResult.data.userInfo;
+ this.setData({
+ userInfo: userInfo,
+ reviewedPoems: userInfo.reviewedPoems || {}
+ });
+
+ console.log('用户复习记录:', this.data.reviewedPoems);
+ }
+
+ if (poemsResult.success) {
+ const allPoems = poemsResult.data.poems;
+ // 筛选其他诗词
+ const { reviewedPoems, otherPoems } = this.classifyPoems(allPoems);
+
+ this.setData({
+ poems: allPoems,
+ reviewedPoems: reviewedPoems,
+ otherPoems: otherPoems
+ });
+
+
+ }
+
+ } catch (error) {
+ console.error('加载数据失败:', error);
+ wx.showToast({
+ title: '加载失败',
+ icon: 'none'
+ });
+ } finally {
+ this.setData({ isLoading: false });
+ }
+ },
+ // 获取用户信息
+ async getUserInfo(openid) {
+ try {
+ const result = await wx.cloud.callFunction({
+ name: 'auth',
+ data: {
+ action: 'getUserByOpenid',
+ openid: openid
+ }
+ });
+ return result.result;
+ } catch (error) {
+ console.error('获取用户信息失败:', error);
+ return { success: false, message: '获取用户信息失败' };
+ }
+ },
+ // 获取所有诗词
+ async getAllPoems() {
+ try {
+ const result = await wx.cloud.callFunction({
+ name: 'poemManagement',
+ data: {
+ action: 'getAllPoems'
+ }
+ });
+ return result.result;
+ } catch (error) {
+ console.error('获取诗词列表失败:', error);
+ return { success: false, message: '获取诗词失败' };
+ }
+ },
+ // 分类诗词:今天需要复习的 vs 其他诗词
+ classifyPoems(allPoems) {
+ const reviewedPoems = this.data.reviewedPoems;
+ const otherPoems = [];
+
+ allPoems.forEach(poem => {
+ const poemReviewData = reviewedPoems[poem._id];
+ if (poemReviewData==null){
+ otherPoems.push(poem);
+ }
+
+ });
+
+ return { reviewedPoems, otherPoems };
+ },
+ // 进入学习页面
+ goToStudy(e) {
+ const poemId = e.currentTarget.dataset.id;
+
+ wx.navigateTo({
+ url: `/pages/study/study?id=${poemId}`
+ });
+ },
+
+ // 进入背诵页面
+ goToRecite(e) {
+ const poemId = e.currentTarget.dataset.id;
+ console.log('跳转到背诵页面,诗歌ID:', poemId);
+ const poem = this.data.poems.find(p => p._id === poemId);
+ if (poemId && poem) {
+ wx.navigateTo({
+ url: `/pages/recite/recite?id=${poemId}&title=${encodeURIComponent(poem.title)}&author=${encodeURIComponent(poem.author)}`
+ });
+ }
+ },
+
+
+ review(){
+ const userInfo= this.data.userInfo;
+ console.log('跳转到复习页面,诗歌ID:', userInfo);
+ wx.navigateTo({
+ url: `/pages/review/review?userInfo=${userInfo}`
+ })
+ },
+
+
+ question(){
+ wx.navigateTo({
+ url: '/pages/question/question'
+ })
+ },
+
+ search(){
+ wx.navigateTo({
+ url: '/pages/search/search'
+ })
+ },
+
+ onShow() {
+ // 每次显示页面时检查数据
+ if (this.data.poems.length === 0 && !this.data.isLoading) {
+ this.loadPoemsFromDatabase();
+ }
+ },
+
+ onPullDownRefresh() {
+ this.loadPoemsFromDatabase().finally(() => {
+ wx.stopPullDownRefresh();
+ });
+ },
+
+ onShareAppMessage() {
+ return {
+ title: '古诗学习小程序',
+ path: '/pages/index/index'
+ };
+ }
+})
+
+
+
\ No newline at end of file
diff --git a/pages/index/index.json b/pages/index/index.json
index b1cb3d1..86104bf 100644
--- a/pages/index/index.json
+++ b/pages/index/index.json
@@ -1,5 +1,5 @@
{
- "usingComponents": {
-
- }
+ "usingComponents": {},
+ "enablePullDownRefresh": true,
+ "backgroundTextStyle": "light"
}
\ No newline at end of file
diff --git a/pages/index/index.wxml b/pages/index/index.wxml
index e6f2c90..6ccbac6 100644
--- a/pages/index/index.wxml
+++ b/pages/index/index.wxml
@@ -1,6 +1,68 @@
-
+
+
+
-
- Weixin
-
+
+
+
+ 📚
+ 复习
+
+
+ ❓
+ 问答
+
+
+ 🔎
+ 搜索
+
+
+
+
+
+
+
+
+ ⏳
+ 加载中...
+
+
+
+
+ ⚠️
+ 加载失败,请重试
+
+
+
+
+
+
+ 📖
+ 暂无诗歌数据
+
+
+
+
+
+
+ {{item.title}}
+
+ {{item.author}}
+ {{item.dynasty}}
+
+ 点击学习经典唐诗
+
+
+ 学习
+ 背诵
+
+
+
+
+
\ No newline at end of file
diff --git a/pages/index/index.wxss b/pages/index/index.wxss
index 8c2b75a..34a5b36 100644
--- a/pages/index/index.wxss
+++ b/pages/index/index.wxss
@@ -1,10 +1,226 @@
-/**index.wxss**/
-page {
- height: 100vh;
+/* 页面容器 */
+.page-container {
+ min-height: 100vh;
+ background-color: #f5f5f5;
+}
+
+/* 头部区域 */
+.header-section {
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+ padding: 60rpx 40rpx;
+ color: white;
+}
+
+.header-title {
+ font-size: 48rpx;
+ font-weight: 700;
+ margin-bottom: 16rpx;
+}
+
+.header-subtitle {
+ font-size: 28rpx;
+ opacity: 0.9;
+}
+
+/* 功能按钮区域 */
+.function-grid {
+ display: flex;
+ justify-content: space-between;
+ padding: 30rpx 40rpx;
+ background: white;
+ margin: 20rpx 40rpx;
+ border-radius: 20rpx;
+ box-shadow: 0 4rpx 20rpx rgba(0,0,0,0.06);
+}
+
+.function-item {
display: flex;
flex-direction: column;
+ align-items: center;
+ flex: 1;
+}
+
+.function-icon {
+ width: 80rpx;
+ height: 80rpx;
+ margin-bottom: 16rpx;
+ border-radius: 50%;
+ background: #f0f7ff;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-size: 36rpx;
+}
+
+.function-text {
+ font-size: 24rpx;
+ color: #333;
+}
+
+/* 诗歌列表区域 */
+.poem-section {
+ background: white;
+ margin: 0 40rpx 40rpx;
+ border-radius: 20rpx;
+ overflow: hidden;
+ box-shadow: 0 4rpx 20rpx rgba(0,0,0,0.06);
}
-.scrollarea {
+
+.list-header {
+ padding: 30rpx 40rpx 20rpx;
+ border-bottom: 2rpx solid #f0f0f0;
+}
+
+.list-title {
+ font-size: 36rpx;
+ font-weight: 700;
+ color: #333;
+}
+
+.poem-list {
+ padding: 0;
+}
+
+.poem-item {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 30rpx 40rpx;
+ border-bottom: 1rpx solid #f8f8f8;
+ transition: background-color 0.3s;
+}
+
+.poem-item:active {
+ background-color: #f8f9fa;
+}
+
+.poem-content {
flex: 1;
- overflow-y: hidden;
+ margin-right: 30rpx;
+}
+
+.poem-title {
+ display: block;
+ font-size: 34rpx;
+ font-weight: 600;
+ color: #333;
+ margin-bottom: 12rpx;
+}
+
+.poem-meta {
+ display: flex;
+ align-items: center;
+ margin-bottom: 8rpx;
+}
+
+.poem-author {
+ font-size: 26rpx;
+ color: #666;
+ margin-right: 20rpx;
+}
+
+.poem-dynasty {
+ font-size: 24rpx;
+ color: #999;
+ background: #f0f0f0;
+ padding: 4rpx 12rpx;
+ border-radius: 12rpx;
+}
+
+.poem-preview {
+ display: block;
+ font-size: 26rpx;
+ color: #999;
+ font-style: italic;
+ line-height: 1.4;
+}
+
+.action-buttons {
+ display: flex;
+ gap: 15rpx;
+}
+
+.btn-study, .btn-recite {
+ padding: 16rpx 24rpx;
+ border-radius: 16rpx;
+ font-size: 24rpx;
+ font-weight: 500;
+ transition: all 0.3s;
+}
+
+.btn-study {
+ background-color: #1989fa;
+ color: white;
+}
+
+.btn-study:active {
+ background-color: #1576d9;
+}
+
+.btn-recite {
+ background-color: #07c160;
+ color: white;
+}
+
+.btn-recite:active {
+ background-color: #06a652;
+}
+
+/* 状态样式 */
+.loading-state, .empty-state, .error-state {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ padding: 120rpx 40rpx;
+ text-align: center;
+}
+
+.loading-state {
+ color: #666;
+}
+
+.empty-state {
+ color: #999;
+}
+
+.error-state {
+ color: #ff4757;
+}
+
+.state-icon {
+ font-size: 80rpx;
+ margin-bottom: 30rpx;
+ opacity: 0.7;
+}
+
+.state-text {
+ font-size: 32rpx;
+ margin-bottom: 40rpx;
+}
+
+.btn-retry, .btn-mock {
+ padding: 20rpx 40rpx;
+ border-radius: 30rpx;
+ font-size: 28rpx;
+ margin: 10rpx 0;
+ transition: all 0.3s;
+}
+
+.btn-retry {
+ background-color: #1989fa;
+ color: white;
+}
+
+.btn-retry:active {
+ background-color: #1576d9;
+}
+
+.btn-mock {
+ background-color: #07c160;
+ color: white;
}
+
+.btn-mock:active {
+ background-color: #06a652;
+}
\ No newline at end of file
diff --git a/pages/keyWord/keyWord.js b/pages/keyWord/keyWord.js
index f624a1a..aea5c78 100644
--- a/pages/keyWord/keyWord.js
+++ b/pages/keyWord/keyWord.js
@@ -1,20 +1,327 @@
// pages/keyWord/keyWord.js
Page({
-
- /**
- * 页面的初始数据
- */
data: {
-
+ questionId: '',
+ question: '',
+ answer: '',
+ // 关键词相关数据
+ allKeywords: [], // 所有关键词数组
+ currentLevel: 0, // 当前关键词层级
+ selectedKeywords: [], // 已选择的关键词路径
+ availableKeywords: [], // 当前层级可用的关键词
+ newKeyword: '', // 新建关键词输入
+ isCreatingNew: false // 是否正在创建新关键词
},
- /**
- * 生命周期函数--监听页面加载
- */
onLoad(options) {
+ if (options) {
+ this.setData({
+ questionId: options.id || '',
+ question: options.question ? decodeURIComponent(options.question) : ''
+ });
+ // 加载已有的关键词数据
+ this.loadExistingKeywords();
+ }
+ },
+
+ // 加载已有关键词数据
+ async loadExistingKeywords() {
+ try {
+ const db = wx.cloud.database();
+ // 从数据库获取所有已有的关键词数据
+ const result = await db.collection('Answer').get();
+
+ const allKeywords = [];
+ result.data.forEach(item => {
+ if (item.keywords && Array.isArray(item.keywords)) {
+ // 将每个答案的关键词路径添加到总列表中
+ allKeywords.push(item.keywords);
+ }
+ });
+
+ this.setData({ allKeywords });
+ this.updateAvailableKeywords();
+
+ } catch (error) {
+ console.error('加载关键词失败:', error);
+ // 如果answers集合不存在,就初始化为空数组
+ this.setData({ allKeywords: [] });
+ }
+ },
+
+ // 更新当前可用的关键词
+ updateAvailableKeywords() {
+ const { selectedKeywords, allKeywords, currentLevel } = this.data;
+
+ // 获取当前层级的所有可能关键词
+ const available = new Set();
+
+ allKeywords.forEach(keywordPath => {
+ if (keywordPath.length > currentLevel) {
+ // 检查路径是否匹配已选择的关键词
+ let isMatch = true;
+ for (let i = 0; i < selectedKeywords.length; i++) {
+ if (keywordPath[i] !== selectedKeywords[i]) {
+ isMatch = false;
+ break;
+ }
+ }
+
+ if (isMatch && keywordPath[currentLevel]) {
+ available.add(keywordPath[currentLevel]);
+ }
+ }
+ });
+
+ this.setData({
+ availableKeywords: Array.from(available)
+ });
+ },
+
+ // 选择关键词
+ selectKeyword(e) {
+ const keyword = e.currentTarget.dataset.keyword;
+ const { selectedKeywords, currentLevel } = this.data;
+
+ // 更新已选择的关键词路径
+ const newSelectedKeywords = [...selectedKeywords];
+ if (newSelectedKeywords.length > currentLevel) {
+ newSelectedKeywords[currentLevel] = keyword;
+ } else {
+ newSelectedKeywords.push(keyword);
+ }
+
+ this.setData({
+ selectedKeywords: newSelectedKeywords,
+ currentLevel: currentLevel + 1,
+ isCreatingNew: false,
+ newKeyword: ''
+ });
+
+ // 更新可用关键词
+ this.updateAvailableKeywords();
+ },
+
+ // 新建关键词
+ toggleCreateNew() {
+ this.setData({
+ isCreatingNew: !this.data.isCreatingNew,
+ newKeyword: ''
+ });
+ },
+ // 输入新建关键词
+ onNewKeywordInput(e) {
+ this.setData({
+ newKeyword: e.detail.value
+ });
},
+ // 确认新建关键词
+ confirmNewKeyword() {
+ const { newKeyword, selectedKeywords, currentLevel } = this.data;
+
+ if (!newKeyword.trim()) {
+ wx.showToast({
+ title: '请输入关键词',
+ icon: 'none'
+ });
+ return;
+ }
+
+ // 更新已选择的关键词路径
+ const newSelectedKeywords = [...selectedKeywords];
+ if (newSelectedKeywords.length > currentLevel) {
+ newSelectedKeywords[currentLevel] = newKeyword.trim();
+ } else {
+ newSelectedKeywords.push(newKeyword.trim());
+ }
+
+ this.setData({
+ selectedKeywords: newSelectedKeywords,
+ currentLevel: currentLevel + 1,
+ isCreatingNew: false,
+ newKeyword: ''
+ });
+
+ // 更新可用关键词(新建的关键词会出现在下一级选择中)
+ this.updateAvailableKeywords();
+ },
+
+ // 返回上一级
+ goBack() {
+ const { currentLevel } = this.data;
+ if (currentLevel > 0) {
+ this.setData({
+ currentLevel: currentLevel - 1,
+ isCreatingNew: false,
+ newKeyword: ''
+ });
+ this.updateAvailableKeywords();
+ }
+ },
+
+ // 输入回答
+ onAnswerInput(e) {
+ this.setData({
+ answer: e.detail.value
+ });
+ },
+
+ // 获取当前关键词路径显示
+ getCurrentKeywordPath() {
+ const { selectedKeywords, currentLevel } = this.data;
+ const currentPath = selectedKeywords.slice(0, currentLevel);
+
+ if (currentPath.length === 0) {
+ return '请选择关键词(从第一级开始)';
+ }
+
+ return currentPath.join(' > ');
+ },
+
+ // 重置关键词
+ resetKeywords() {
+ this.setData({
+ selectedKeywords: [],
+ currentLevel: 0,
+ isCreatingNew: false,
+ newKeyword: ''
+ });
+ this.updateAvailableKeywords();
+
+ wx.showToast({
+ title: '关键词已重置',
+ icon: 'success'
+ });
+ },
+
+ // 提交答案和关键词
+async submitAnswer() {
+ const { questionId, question, selectedKeywords, answer } = this.data;
+
+ // 验证输入
+ if (selectedKeywords.length === 0) {
+ wx.showToast({
+ title: '请至少选择一个关键词',
+ icon: 'none'
+ });
+ return;
+ }
+
+ if (!answer.trim()) {
+ wx.showToast({
+ title: '请输入答案',
+ icon: 'none'
+ });
+ return;
+ }
+
+ // 显示加载中
+ wx.showLoading({
+ title: '提交中...',
+ mask: true
+ });
+
+ try {
+ const db = wx.cloud.database();
+
+ // 1. 从Question集合获取问题详情,包括用户的_openid
+ let userOpenid = '';
+ let questionDocId = questionId;
+
+ if (questionId) {
+ try {
+ const questionDetail = await db.collection('Question').doc(questionId).get();
+ userOpenid = questionDetail.data._openid || '';
+ console.log('获取到用户openid:', userOpenid);
+ } catch (detailError) {
+ console.error('获取问题详情失败:', detailError);
+ // 如果通过ID查询失败,尝试通过问题内容查询
+ const queryResult = await db.collection('Question').where({
+ question: question
+ }).get();
+
+ if (queryResult.data.length > 0) {
+ const firstQuestion = queryResult.data[0];
+ userOpenid = firstQuestion._openid || '';
+ questionDocId = firstQuestion._id;
+ console.log('通过问题内容获取到用户openid:', userOpenid);
+ }
+ }
+ }
+
+ // 2. 保存答案到Answer集合,按照数据库要求的格式
+ const saveData = {
+ createTime: db.serverDate(), // 使用服务器时间
+ keywords: selectedKeywords, // 关键词数组
+ question: question, // 问题内容
+ questionId: questionDocId, // 保存问题ID,便于后续查询
+ answers: [answer.trim()], // 答案使用answers数组
+ status: 1, // 标记为已回答状态
+ userId: userOpenid // 保存提问用户的openid,但使用userId而非_openid,避免系统保留字段冲突
+ };
+
+ await db.collection('Answer').add({
+ data: saveData
+ });
+
+ console.log('答案保存成功:', saveData);
+
+ // 3. 更新Question集合中对应问题的状态为已回答(status: 1)
+ if (questionDocId) {
+ try {
+ await db.collection('Question').doc(questionDocId).update({
+ data: {
+ status: 1
+ }
+ });
+ console.log('问题状态更新成功,问题ID:', questionDocId);
+ } catch (updateError) {
+ console.error('更新问题状态失败:', updateError);
+ // 尝试通过问题内容更新
+ const updateResult = await db.collection('Question').where({
+ question: question
+ }).update({
+ data: {
+ status: 1
+ }
+ });
+ console.log('通过问题内容更新状态结果:', updateResult);
+ }
+ }
+
+ wx.hideLoading();
+ wx.showToast({
+ title: '提交成功',
+ icon: 'success',
+ duration: 2000,
+ success: () => {
+ setTimeout(() => {
+ wx.navigateBack();
+ }, 1500);
+ }
+ });
+
+ } catch (error) {
+ console.error('提交失败:', error);
+ wx.hideLoading();
+
+ let errorMsg = '提交失败,请重试';
+ if (error.errCode === -502005) {
+ errorMsg = '数据库集合不存在,请检查配置';
+ } else if (error.errCode === -501007) {
+ errorMsg = '参数错误,请检查输入';
+ }
+
+ wx.showToast({
+ title: errorMsg,
+ icon: 'none',
+ duration: 2000
+ });
+ }
+},
+
/**
* 生命周期函数--监听页面初次渲染完成
*/
diff --git a/pages/keyWord/keyWord.wxml b/pages/keyWord/keyWord.wxml
index b9cd149..4929bdd 100644
--- a/pages/keyWord/keyWord.wxml
+++ b/pages/keyWord/keyWord.wxml
@@ -1,2 +1,83 @@
-pages/keyWord/keyWord.wxml
\ No newline at end of file
+
+
+
+
+
+ 问题:
+
+ {{question}}
+
+
+
+
+
+ 关键词管理:
+
+
+
+ 当前路径:
+ {{getCurrentKeywordPath()}}
+
+
+
+
+ 第{{currentLevel + 1}}级关键词
+
+
+
+
+
+ {{item}}
+
+
+
+
+
+
+
+ +
+ {{isCreatingNew ? '取消新建' : '新建关键词'}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 答案:
+
+
+
+
+
+
\ No newline at end of file
diff --git a/pages/keyWord/keyWord.wxss b/pages/keyWord/keyWord.wxss
index d2cd14f..c00a635 100644
--- a/pages/keyWord/keyWord.wxss
+++ b/pages/keyWord/keyWord.wxss
@@ -1 +1,231 @@
-/* pages/keyWord/keyWord.wxss */
\ No newline at end of file
+/* pages/keyWord/keyWord.wxss */
+.header {
+ text-align: center;
+ margin-bottom: 15rpx;
+}
+
+.title {
+ font-size: 40rpx;
+ font-weight: bold;
+ color: #333;
+}
+
+.content {
+ background-color: #fff;
+ border-radius: 20rpx;
+ padding: 30rpx;
+}
+
+.form-group {
+ margin-bottom: 40rpx;
+}
+
+.label {
+ font-size: 32rpx;
+ font-weight: bold;
+ color: #333;
+ display: block;
+ margin-bottom: 15rpx;
+}
+
+.question-box {
+ background-color: #f9f9f9;
+ border: 1rpx solid #eee;
+ border-radius: 10rpx;
+ padding: 20rpx;
+ min-height: 100rpx;
+}
+
+.question-text {
+ font-size: 30rpx;
+ color: #666;
+ line-height: 44rpx;
+}
+
+/* 关键词路径显示 */
+.keyword-path-box {
+ background-color: #f0f8ff;
+ border: 1rpx solid #d1e9ff;
+ border-radius: 10rpx;
+ padding: 20rpx;
+ margin-bottom: 30rpx;
+}
+
+.path-label {
+ font-size: 28rpx;
+ color: #1890ff;
+ font-weight: bold;
+}
+
+.path-text {
+ font-size: 28rpx;
+ color: #333;
+ margin-left: 15rpx;
+}
+
+/* 关键词层级区域 */
+.keywords-level-section {
+ background-color: #fafafa;
+ border: 1rpx solid #f0f0f0;
+ border-radius: 10rpx;
+ padding: 25rpx;
+ margin-bottom: 20rpx;
+}
+
+.level-title {
+ font-size: 28rpx;
+ color: #666;
+ font-weight: bold;
+ margin-bottom: 20rpx;
+ display: block;
+}
+
+/* 关键词列表 */
+.keywords-list {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 20rpx;
+ margin-bottom: 25rpx;
+}
+
+.keyword-item {
+ background: #1890ff;
+ color: white;
+ padding: 16rpx 32rpx;
+ border-radius: 8rpx;
+ font-size: 28rpx;
+ cursor: pointer;
+ transition: all 0.3s;
+}
+
+.keyword-item:active {
+ background: #096dd9;
+ transform: scale(0.95);
+}
+
+/* 新建关键词区域 */
+.new-keyword-section {
+ border-top: 1rpx dashed #e8e8e8;
+ padding-top: 25rpx;
+}
+
+.create-new-btn {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ padding: 20rpx;
+ border: 2rpx dashed #1890ff;
+ color: #1890ff;
+ border-radius: 8rpx;
+ font-size: 28rpx;
+ transition: all 0.3s;
+}
+
+.create-new-btn.active {
+ background: #e6f7ff;
+ border-style: solid;
+}
+
+.btn-icon {
+ margin-right: 10rpx;
+ font-weight: bold;
+}
+
+.new-keyword-input {
+ display: flex;
+ gap: 15rpx;
+ align-items: center;
+ margin-top: 20rpx;
+}
+
+.keyword-input {
+ flex: 1;
+ border: 1rpx solid #ddd;
+ padding: 20rpx;
+ border-radius: 8rpx;
+ font-size: 28rpx;
+ background: white;
+}
+
+.confirm-btn {
+ background: #52c41a;
+ color: white;
+ border: none;
+ padding: 20rpx 30rpx;
+ border-radius: 8rpx;
+ font-size: 26rpx;
+}
+
+.confirm-btn:active {
+ background: #389e0d;
+}
+
+/* 关键词操作按钮 */
+.keyword-actions {
+ display: flex;
+ gap: 20rpx;
+}
+
+.action-btn {
+ flex: 1;
+ border: none;
+ padding: 20rpx;
+ border-radius: 8rpx;
+ font-size: 28rpx;
+ transition: all 0.3s;
+}
+
+.back-btn {
+ background: #faad14;
+ color: white;
+}
+
+.back-btn:active {
+ background: #d48806;
+}
+
+.reset-btn {
+ background: #ff4d4f;
+ color: white;
+}
+
+.reset-btn:active {
+ background: #cf1322;
+}
+
+/* 答案输入框 */
+.textarea {
+ width: 100%;
+ border: 1rpx solid #eee;
+ border-radius: 10rpx;
+ padding: 20rpx;
+ font-size: 30rpx;
+ color: #333;
+ min-height: 120rpx;
+ box-sizing: border-box;
+}
+
+.answer-textarea {
+ min-height: 500rpx;
+}
+
+/* 提交按钮 */
+.submit-btn {
+ margin-top: 40rpx;
+ font-size: 32rpx;
+ height: 88rpx;
+ line-height: 88rpx;
+ border-radius: 44rpx;
+ background: #1890ff;
+ color: white;
+}
+
+.submit-btn:active {
+ background: #096dd9;
+}
+
+/* 去除textarea默认样式 */
+input, textarea {
+ font-family: inherit;
+ outline: none;
+}
\ No newline at end of file
diff --git a/pages/login/login.js b/pages/login/login.js
index 43bfe24..f16bc80 100644
--- a/pages/login/login.js
+++ b/pages/login/login.js
@@ -5,14 +5,30 @@ Page({
* 页面的初始数据
*/
data: {
- isLoading: false // 默认不显示提示文字
+ isLoading: false ,// 默认不显示提示文字
+ errorLoading: false,
+ openid:''
},
/**
* 生命周期函数--监听页面加载
*/
onLoad(options) {
-
+ // 接收从guiding页面传递的openid
+ if (options.openid) {
+ this.setData({
+ openid: options.openid
+ });
+ console.log('接收到的openid:', options.openid);
+ } else {
+ wx.showToast({
+ title: '参数错误',
+ icon: 'none'
+ });
+ setTimeout(() => {
+ wx.navigateBack();
+ }, 1500);
+ }
},
/**
@@ -63,28 +79,90 @@ Page({
onShareAppMessage() {
},
- request_login(){
- //更新当前页面数据
- this.setData({
- isLoading: true,
- errorLoading: false
- });
- // 这里可以添加实际的微信授权逻辑(示例)
- wx.getUserProfile({
- desc: '用于古诗学习助手的登录授权', // 授权说明
- success: (res) => {
- console.log('授权成功', res);
- // 授权成功后,可隐藏提示文字(根据需求)
+ async request_login() {
+ try {
+ this.setData({ isLoading: true });
+
+ // 获取用户授权信息
+ const { userInfo } = await wx.getUserProfile({
+ desc: '用于古诗学习助手的登录授权'
+ });
+
+ console.log('授权成功', userInfo);
+ this.setData({ isLoading: false });
+
+ // 调用注册接口
+ await this.registerUser(userInfo);
+
+
+
+ } catch (err) {
+ console.log('授权失败', err);
+ this.setData({
+ isLoading: false,
+ errorLoading: true
+ });
+
+ wx.showToast({
+ title: '授权失败,请重试',
+ icon: 'none'
+ });
+ }
+ },
+ // 注册用户方法
+ async registerUser(userInfo) {
+ try {
+ console.log('开始注册用户,openid:', this.data.openid);
+
+ const result = await wx.cloud.callFunction({
+ name: 'auth',
+ data: {
+ action: 'registerUser',
+ openid: this.data.openid, // 使用传递的openid
+ userInfo: userInfo
+ }
+ });
+
+ console.log('注册结果:', result);
+
+ if (result.result.success) {
+ const userData = result.result.data.userInfo;
+
+ // 保存用户信息到本地
+ wx.setStorageSync('userInfo', userData);
+
this.setData({ isLoading: false });
- wx.navigateTo({
- url: '/pages/home/home'
- });
- },
- fail: (err) => {
- console.log('授权失败', err);
- // 授权失败也隐藏提示文字
- this.setData({ isLoading: false ,errorLoading: true});
+
+ wx.showToast({
+ title: '注册成功',
+ icon: 'success',
+ success: () => {
+ // 注册成功后跳转
+ setTimeout(() => {
+ // 根据用户角色决定跳转页面:role为false时是管理员,跳转到pendingQuestion页面
+ if (userData.role === false) {
+ wx.reLaunch({
+ url: `/pages/pendingQuestion/pendingQuestion?openid=${this.data.openid}`
+ });
+ } else {
+ wx.reLaunch({
+ url: `/pages/index/index?openid=${this.data.openid}`
+ });
+ }
+ }, 1500);
+ }
+ });
+ } else {
+ throw new Error(result.result.message || '注册失败');
}
- });
+ } catch (error) {
+ console.error('注册失败:', error);
+ this.setData({ isLoading: false });
+
+ wx.showToast({
+ title: '注册失败: ' + error.message,
+ icon: 'none'
+ });
+ }
}
})
\ No newline at end of file
diff --git a/pages/pendingQuestion/pendingQuestion.js b/pages/pendingQuestion/pendingQuestion.js
index a28a4ef..c5c7b64 100644
--- a/pages/pendingQuestion/pendingQuestion.js
+++ b/pages/pendingQuestion/pendingQuestion.js
@@ -1,31 +1,127 @@
// pages/pendingQuestion/pendingQuestion.js
Page({
+ data: {
+ pendingQuestions: [] // 初始化为空数组
+ },
+
+ onLoad(options) {
+ this.loadPendingQuestions();
+ },
+
+ onShow() {
+ // 页面显示时重新加载数据,确保数据最新
+ this.loadPendingQuestions();
+ },
/**
- * 页面的初始数据
+ * 从Question集合加载待解决问题
*/
- data: {
-
+ async loadPendingQuestions() {
+ try {
+ const db = wx.cloud.database();
+ const _ = db.command;
+
+ // 从Question集合获取待解决问题(status为0或不存在的问题)
+ const result = await db.collection('Question').where({
+ status: _.in([0, null, undefined])
+ }).get();
+
+ console.log('待解决问题数据:', result.data);
+
+ // 转换数据格式,适配现有界面
+ const pendingQuestions = result.data.map(item => {
+ return {
+ id: item._id, // 使用数据库的_id作为唯一标识
+ question: item.question || '未知问题', // 使用question字段
+ createdAt: this.formatTime(item.createTime || item.timestamp || new Date()),
+ status: item.status || 0 // 记录问题状态
+ };
+ });
+
+ this.setData({
+ pendingQuestions: pendingQuestions
+ });
+
+ if (pendingQuestions.length === 0) {
+ console.log('暂无待解决问题');
+ }
+
+ } catch (error) {
+ console.error('加载待解决问题失败:', error);
+
+ // 处理集合不存在的情况
+ if (error.errCode === -502005) {
+ console.log('Question集合不存在,请先创建集合');
+ // 保持空数组,界面会显示"暂无待解决问题"
+ this.setData({
+ pendingQuestions: []
+ });
+ } else {
+ wx.showToast({
+ title: '加载失败',
+ icon: 'none'
+ });
+ }
+ }
},
/**
- * 生命周期函数--监听页面加载
+ * 格式化时间
*/
- onLoad(options) {
+ formatTime(date) {
+ if (!date) return '未知时间';
+
+ const d = new Date(date);
+ const year = d.getFullYear();
+ const month = (d.getMonth() + 1).toString().padStart(2, '0');
+ const day = d.getDate().toString().padStart(2, '0');
+ const hours = d.getHours().toString().padStart(2, '0');
+ const minutes = d.getMinutes().toString().padStart(2, '0');
+
+ return `${year}-${month}-${day} ${hours}:${minutes}`;
+ },
+ /**
+ * 跳转到关键词回答页面
+ */
+ goToKeywordAnswer(e) {
+ const questionId = e.currentTarget.dataset.id;
+ const question = e.currentTarget.dataset.question;
+
+ if (!questionId) {
+ wx.showToast({
+ title: '问题ID不存在',
+ icon: 'none'
+ });
+ return;
+ }
+
+ wx.navigateTo({
+ url: `/pages/keyWord/keyWord?id=${questionId}&question=${encodeURIComponent(question)}`
+ });
},
/**
- * 生命周期函数--监听页面初次渲染完成
+ * 跳转到待解决问题页面(当前页面)
*/
- onReady() {
+ goToPendingQuestion() {
+ // 已经在当前页面,刷新数据
+ this.loadPendingQuestions();
+ },
+ /**
+ * 跳转到古诗管理页面
+ */
+ goToManagePoems() {
+ wx.navigateTo({
+ url: '/pages/managePoems/managePoems'
+ });
},
/**
- * 生命周期函数--监听页面显示
+ * 生命周期函数--监听页面初次渲染完成
*/
- onShow() {
+ onReady() {
},
@@ -47,7 +143,11 @@ Page({
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh() {
-
+ this.loadPendingQuestions().then(() => {
+ wx.stopPullDownRefresh();
+ }).catch(() => {
+ wx.stopPullDownRefresh();
+ });
},
/**
@@ -61,6 +161,9 @@ Page({
* 用户点击右上角分享
*/
onShareAppMessage() {
-
+ return {
+ title: '待解决问题',
+ path: '/pages/pendingQuestion/pendingQuestion'
+ };
}
})
\ No newline at end of file
diff --git a/pages/pendingQuestion/pendingQuestion.wxml b/pages/pendingQuestion/pendingQuestion.wxml
index 94931ca..581e943 100644
--- a/pages/pendingQuestion/pendingQuestion.wxml
+++ b/pages/pendingQuestion/pendingQuestion.wxml
@@ -1,2 +1,38 @@
-pages/pendingQuestion/pendingQuestion.wxml
\ No newline at end of file
+
+
+
+
+
+
+ {{item.question}}
+
+
+ {{item.createdAt}}
+ ›
+
+
+
+
+ 暂无待解决问题
+
+
+
+
+
+
+ 待解决问题
+
+
+ 管理古诗
+
+
\ No newline at end of file
diff --git a/pages/pendingQuestion/pendingQuestion.wxss b/pages/pendingQuestion/pendingQuestion.wxss
index 5503503..d70cbdd 100644
--- a/pages/pendingQuestion/pendingQuestion.wxss
+++ b/pages/pendingQuestion/pendingQuestion.wxss
@@ -1 +1,102 @@
-/* pages/pendingQuestion/pendingQuestion.wxss */
\ No newline at end of file
+/* pages/pendingQuestion/pendingQuestion.wxss */
+page {
+ background-color: #f5f5f5;
+ padding-bottom: 120rpx; /* 为底部导航栏留出空间 */
+}
+.header {
+ text-align: center;
+ margin-bottom: 15rpx;
+}
+
+.title {
+ font-size: 40rpx;
+ font-weight: bold;
+ color: #333;
+}
+
+.question-list {
+ background-color: #fff;
+ border-radius: 20rpx;
+ overflow: hidden;
+}
+
+.question-item {
+ padding: 30rpx;
+ border-bottom: 1rpx solid rgb(123, 135, 168);
+ display: flex;
+ flex-direction: column;
+ gap: 15rpx;
+}
+
+.question-item:last-child {
+ border-bottom: none;
+}
+
+.question-content {
+ flex: 1;
+}
+
+.question-text {
+ font-size: 32rpx;
+ color: #333;
+ line-height: 48rpx;
+}
+
+.question-meta {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+}
+
+.question-time {
+ font-size: 26rpx;
+ color: #999;
+}
+
+/* 底部导航栏样式 */
+.tab-bar {
+ position: fixed;
+ bottom: 0;
+ left: 0;
+ width: 100%;
+ height: 120rpx;
+ background-color: #fff;
+ display: flex;
+ border-top: 1rpx solid #e0e0e0;
+ box-shadow: 0 -2rpx 10rpx rgba(0, 0, 0, 0.05);
+ z-index: 999;
+}
+
+.tab-item {
+ flex: 1;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ height: 100%;
+}
+
+.tab-item.active {
+ background-color: #f8f8f8;
+}
+
+.tab-text {
+ font-size: 32rpx;
+ color: #666;
+}
+
+.tab-item.active .tab-text {
+ color: #07c160;
+ font-weight: bold;
+}
+
+.question-arrow {
+ font-size: 36rpx;
+ color: #999;
+}
+
+.empty-state {
+ padding: 80rpx 0;
+ text-align: center;
+ color: #999;
+ font-size: 32rpx;
+}
\ No newline at end of file
diff --git a/pages/questionAnswer/questionAnswer.js b/pages/questionAnswer/questionAnswer.js
deleted file mode 100644
index a39da9c..0000000
--- a/pages/questionAnswer/questionAnswer.js
+++ /dev/null
@@ -1,66 +0,0 @@
-// pages/questionAnswer/questionAnswer.js
-Page({
-
- /**
- * 页面的初始数据
- */
- data: {
-
- },
-
- /**
- * 生命周期函数--监听页面加载
- */
- onLoad(options) {
-
- },
-
- /**
- * 生命周期函数--监听页面初次渲染完成
- */
- onReady() {
-
- },
-
- /**
- * 生命周期函数--监听页面显示
- */
- onShow() {
-
- },
-
- /**
- * 生命周期函数--监听页面隐藏
- */
- onHide() {
-
- },
-
- /**
- * 生命周期函数--监听页面卸载
- */
- onUnload() {
-
- },
-
- /**
- * 页面相关事件处理函数--监听用户下拉动作
- */
- onPullDownRefresh() {
-
- },
-
- /**
- * 页面上拉触底事件的处理函数
- */
- onReachBottom() {
-
- },
-
- /**
- * 用户点击右上角分享
- */
- onShareAppMessage() {
-
- }
-})
\ No newline at end of file
diff --git a/pages/questionAnswer/questionAnswer.json b/pages/questionAnswer/questionAnswer.json
deleted file mode 100644
index 8835af0..0000000
--- a/pages/questionAnswer/questionAnswer.json
+++ /dev/null
@@ -1,3 +0,0 @@
-{
- "usingComponents": {}
-}
\ No newline at end of file
diff --git a/pages/questionAnswer/questionAnswer.wxml b/pages/questionAnswer/questionAnswer.wxml
deleted file mode 100644
index 11d3531..0000000
--- a/pages/questionAnswer/questionAnswer.wxml
+++ /dev/null
@@ -1,2 +0,0 @@
-
-pages/questionAnswer/questionAnswer.wxml
\ No newline at end of file
diff --git a/pages/questionAnswer/questionAnswer.wxss b/pages/questionAnswer/questionAnswer.wxss
deleted file mode 100644
index 8c75b00..0000000
--- a/pages/questionAnswer/questionAnswer.wxss
+++ /dev/null
@@ -1 +0,0 @@
-/* pages/questionAnswer/questionAnswer.wxss */
\ No newline at end of file
diff --git a/pages/recite/recite.js b/pages/recite/recite.js
index df3aa0e..c1baac3 100644
--- a/pages/recite/recite.js
+++ b/pages/recite/recite.js
@@ -1,66 +1,621 @@
-// pages/recite/recite.js
+const recorderManager = wx.getRecorderManager()
+const options = {
+ duration: 60000,//指定录音的时长,单位 ms
+ sampleRate: 16000,//采样率
+ numberOfChannels: 1,//录音通道数
+ encodeBitRate: 48000,//编码码率
+ format: 'pcm',//音频格式,有效值 aac/mp3
+ }
+ var filesize,tempFilePath
Page({
+ /**
+ * 页面的初始数据
+ */
+ data: {
+ token:'',
+ content:'',
+ poemId: '',
+ poemTitle: '',
+ poemAuthor: '',
+ originalText: '',
+ accuracyRate: 0,
+ showResult: false,
+ startReciteTime: 0, // 开始背诵时间
+ reciteDuration: '', // 背诵时长
+ reciteDateTime: '', // 背诵时间
+ openid: '' // 添加 openid
+ },
- /**
- * 页面的初始数据
- */
- data: {
-
- },
-
- /**
- * 生命周期函数--监听页面加载
- */
onLoad(options) {
+ console.log('背诵页面参数:', options);
+ // 接收从学习页面传递的参数
+ if (options.poemData) {
+ try {
+ const poemData = decodeURIComponent(options.poemData);
+ const poem = JSON.parse(poemData);
+ console.log('从学习页面传递的诗歌数据:', poem);
+ this.setData({
+ poemId: poem._id || options.id,
+ poemTitle: poem.title || '',
+ poemAuthor: poem.author || '',
+ originalText: poem.content || '' // 直接从传递的数据中获取原文
+ });
+ } catch (error) {
+ console.error('解析诗歌数据失败:', error);
+ // 如果解析失败,尝试使用id获取
+ if (options.id) {
+ this.setData({
+ poemId: options.id
+ });
+ this.getPoemDetail(options.id);
+ }
+ }
+ }
+ // 接收从index页面传递的参数
+ else if (options.id && options.title && options.author) {
+ this.setData({
+ poemId: options.id,
+ poemTitle: decodeURIComponent(options.title),
+ poemAuthor: decodeURIComponent(options.author)
+ });
+ // 获取古诗原文
+ this.getPoemDetail(options.id);
+ }
+ // 只传递了id的情况
+ else if (options.id) {
+ this.setData({
+ poemId: options.id
+ });
+ // 获取古诗原文
+ this.getPoemDetail(options.id);
+ }
+
+ //获取用户openid
+ this.getUserOpenId();
+ //获取storge中的token
+ let that=this;
+ wx.getStorage({
+ key:'expires_in',
+ success(res){
+ console.log("缓存中有access_token")
+ console.log("token失效时间:",res.data)
+ const newT = new Date().getTime();
+ // 用当前时间和存储的时间判断,token是否已过期
+ if (newT > parseInt(res.data)) {
+ console.log("token过期,重新获取token")
+ that.getToken();
+ } else {
+ console.log("获取本地缓存的token")
+ that.setData({
+ token:wx.getStorageSync('access_token')
+ });
+ }
+ },fail(){
+ console.log("缓存中没有access_token")
+ that.getToken();
+ }
+ });
},
-
- /**
- * 生命周期函数--监听页面初次渲染完成
- */
- onReady() {
-
+ // 获取用户 openid
+ async getUserOpenId() {
+ try {
+ const res = await wx.cloud.callFunction({
+ name: 'getOpenId'
+ })
+ this.setData({
+ openid: res.result.openid
+ })
+ console.log('获取用户openid:', this.data.openid)
+ } catch (error) {
+ console.error('获取openid失败:', error)
+ }
},
-
- /**
- * 生命周期函数--监听页面显示
- */
- onShow() {
-
+ // 保存背诵记录到数据库
+ async saveReviewRecord(reviewData) {
+ try {
+ console.log('开始保存背诵记录')
+ console.log('传入的reviewData完整内容:', JSON.stringify(reviewData))
+ console.log('传入的reviewData数据结构:', Object.keys(reviewData || {}).join(', '))
+
+ // 验证必要字段
+ if (!reviewData || !reviewData.poemId || !reviewData.openid) {
+ console.error('缺少必要字段')
+ wx.showToast({
+ title: '数据保存失败:缺少必要字段',
+ icon: 'none'
+ })
+ return false
+ }
+
+ // 先检查并删除可能存在的content和score字段
+ if (reviewData.hasOwnProperty('content')) {
+ console.warn('前端发现content字段,将被删除:', reviewData.content)
+ delete reviewData.content
+ }
+ if (reviewData.hasOwnProperty('score')) {
+ console.warn('前端发现score字段,将被删除:', reviewData.score)
+ delete reviewData.score
+ }
+
+ // 详细日志记录清理后的数据结构
+ console.log('前端清理后reviewData的数据结构:', Object.keys(reviewData).join(', '))
+
+ // 创建一个全新的干净对象,不继承任何原型属性,只包含我们需要的字段
+ const dataToSave = {};
+ dataToSave.openid = reviewData.openid;
+ dataToSave.poemId = reviewData.poemId;
+ dataToSave.poemName = reviewData.poemName || this.data.poemTitle || '';
+ dataToSave.accuracy = Number(reviewData.accuracy || 0);
+ dataToSave.duration = Number(reviewData.duration || 0);
+ dataToSave.reciteDateTime = reviewData.reciteDateTime || new Date().toISOString();
+
+ // 再次检查dataToSave中是否存在content和score字段
+ if (dataToSave.hasOwnProperty('content')) {
+ console.error('警告: 前端dataToSave中仍存在content字段,将被强制删除!')
+ delete dataToSave.content;
+ }
+ if (dataToSave.hasOwnProperty('score')) {
+ console.error('警告: 前端dataToSave中仍存在score字段,将被强制删除!')
+ delete dataToSave.score;
+ }
+
+ // 详细日志记录最终发送的数据
+ console.log('前端最终发送到数据库的数据结构:', Object.keys(dataToSave).join(', '))
+ console.log('前端最终发送到数据库的数据:', JSON.stringify(dataToSave))
+
+ // 调用云函数
+ const result = await wx.cloud.callFunction({
+ name: 'reviewManagement',
+ data: {
+ action: 'addReviewRecord',
+ reviewData: dataToSave
+ }
+ })
+
+ // 处理结果
+ if (result && result.result && result.result.success) {
+ console.log('背诵记录保存成功:', result.result._id)
+ console.log('云函数返回的保存字段列表:', result.result.savedFields?.join(', ') || '未知')
+ return true
+ } else {
+ console.error('背诵记录保存失败:', result.result?.message || '未知错误')
+ return false
+ }
+ } catch (error) {
+ console.error('保存背诵记录异常:', error)
+ return false
+ }
},
- /**
- * 生命周期函数--监听页面隐藏
- */
- onHide() {
-
+ // 计算背诵难度
+ calculateDifficulty(duration, originalText) {
+ // 基于原文长度和背诵时间计算难度
+ const textLength = (originalText || '').length
+ const avgSpeed = textLength / Math.max(1, duration)
+
+ if (textLength > 200 || avgSpeed < 0.5) {
+ return 'difficult'
+ } else if (textLength > 100 || avgSpeed < 1) {
+ return 'medium'
+ } else {
+ return 'easy'
+ }
},
- /**
- * 生命周期函数--监听页面卸载
- */
- onUnload() {
-
+ // 获取古诗详情
+ async getPoemDetail(poemId) {
+ try {
+ console.log('调用云函数获取古诗详情,ID:', poemId);
+ const result = await wx.cloud.callFunction({
+ name: 'poemManagement',
+ data: {
+ action: 'getPoemDetail',
+ id: poemId
+ }
+ });
+
+ console.log('云函数返回结果:', result);
+
+ if (result.result && result.result.success) {
+ // 直接使用result.result.data,因为云函数返回的结构是{success: true, data: poem对象}
+ const poem = result.result.data;
+ this.setData({
+ originalText: poem.content || ''
+ });
+ console.log('获取到古诗原文:', this.data.originalText);
+ } else {
+ const errorMessage = result.result?.message || '获取古诗详情失败';
+ console.error('获取古诗详情失败:', errorMessage);
+ wx.showToast({
+ title: errorMessage,
+ icon: 'none'
+ });
+ }
+ } catch (error) {
+ console.error('获取古诗详情出错:', error);
+ wx.showToast({
+ title: '网络错误,请重试',
+ icon: 'none'
+ });
+ }
},
-
+
+ // 清理文本,去除标点符号和空白字符
+ cleanText(text) {
+ if (!text) return '';
+ // 转换为字符串
+ text = String(text);
+
+ // 去除所有空格(包括全角空格)、换行符、制表符
+ text = text.replace(/[\s\n\t\u3000]/g, '');
+
+ // 去除所有标点符号(包括中文标点)
+ text = text.replace(/[\p{P}\p{S}]/gu, '');
+
+ // 去除所有特殊符号
+ text = text.replace(/[!"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~]/g, '');
+
+ console.log('清理后的文本:', text);
+ return text;
+ },
+
+ // 计算文本相似度(正确率)
+ calculateAccuracy(recognizedText, originalText) {
+ const cleanedRecognized = this.cleanText(recognizedText);
+ const cleanedOriginal = this.cleanText(originalText);
+
+ console.log('清理后的识别文本:', cleanedRecognized);
+ console.log('清理后的原文:', cleanedOriginal);
+ console.log('原文长度:', cleanedOriginal.length);
+
+ if (!cleanedOriginal || cleanedOriginal.length === 0) {
+ console.log('原文为空或长度为0');
+ return 0;
+ }
+ if (!cleanedRecognized || cleanedRecognized.length === 0) {
+ console.log('识别文本为空或长度为0');
+ return 0;
+ }
+
+ // 使用动态规划计算最长公共子序列长度
+ const m = cleanedRecognized.length;
+ const n = cleanedOriginal.length;
+ const dp = Array(m + 1).fill().map(() => Array(n + 1).fill(0));
+
+ for (let i = 1; i <= m; i++) {
+ for (let j = 1; j <= n; j++) {
+ if (cleanedRecognized[i - 1] === cleanedOriginal[j - 1]) {
+ dp[i][j] = dp[i - 1][j - 1] + 1;
+ } else {
+ dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);
+ }
+ }
+ }
+
+ const lcsLength = dp[m][n];
+ console.log('最长公共子序列长度:', lcsLength);
+
+ // 计算正确率(以原文长度为基准)
+ const accuracyRate = Math.round((lcsLength / cleanedOriginal.length) * 100);
+ console.log('计算得到的正确率:', accuracyRate + '%');
+ return accuracyRate;
+ },
+
+ // 检查背诵结果
+ // 修改 checkRecitation 方法,在计算完正确率后保存记录
+ async checkRecitation() {
+ console.log('开始检查背诵结果');
+ console.log('当前识别文本类型:', typeof this.data.content);
+ console.log('当前识别文本内容:', this.data.content);
+ console.log('当前古诗原文:', this.data.originalText);
+
+ // 确保content是字符串格式
+ let contentText = this.data.content;
+ console.log('开始处理识别文本格式...');
+
+ if (Array.isArray(contentText)) {
+ console.log('识别文本是数组格式,长度:', contentText.length);
+ // 处理可能的嵌套数组情况
+ if (contentText.length > 0 && Array.isArray(contentText[0])) {
+ console.log('识别文本是嵌套数组');
+ contentText = contentText[0].join('');
+ } else {
+ contentText = contentText.join('');
+ }
+ } else if (contentText !== null && contentText !== undefined) {
+ console.log('识别文本不是数组,转换为字符串');
+ contentText = String(contentText);
+ } else {
+ console.log('识别文本为空');
+ contentText = '';
+ }
+
+ console.log('处理后的识别文本:', contentText);
+
+ if (!contentText || contentText.trim() === '') {
+ wx.showToast({
+ title: '请先进行录音',
+ icon: 'none'
+ });
+ console.log('识别文本为空,无法检查');
+ return;
+ }
+
+ if (!this.data.originalText || this.data.originalText.trim() === '') {
+ wx.showToast({
+ title: '获取古诗原文失败',
+ icon: 'none'
+ });
+ console.log('古诗原文为空,无法检查');
+ // 尝试重新获取古诗原文
+ if (this.data.poemId) {
+ console.log('尝试重新获取古诗原文');
+ this.getPoemDetail(this.data.poemId);
+ }
+ return;
+ }
+
+ // 计算正确率
+ const accuracyRate = this.calculateAccuracy(contentText, this.data.originalText);
+ console.log('检查背诵结果完成,正确率:', accuracyRate + '%');
+
+ // 计算背诵时长
+ let reciteDuration = '';
+ let durationSeconds = 0;
+ if (this.data.startReciteTime > 0) {
+ const endTime = new Date().getTime();
+ const durationMs = endTime - this.data.startReciteTime;
+ durationSeconds = Math.floor(durationMs / 1000);
+
+ // 格式化时长显示 (MM:SS)
+ const minutes = Math.floor(durationSeconds / 60);
+ const seconds = durationSeconds % 60;
+ reciteDuration = `${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`;
+ console.log('背诵时长:', reciteDuration);
+ } else {
+ reciteDuration = '--:--';
+ console.log('未记录开始时间,无法计算背诵时长');
+ }
+
+ // 记录和格式化背诵时间
+ const now = new Date();
+ const year = now.getFullYear();
+ const month = (now.getMonth() + 1).toString().padStart(2, '0');
+ const day = now.getDate().toString().padStart(2, '0');
+ const hours = now.getHours().toString().padStart(2, '0');
+ const minutes = now.getMinutes().toString().padStart(2, '0');
+ const reciteDateTime = `${year}-${month}-${day} ${hours}:${minutes}`;
+ console.log('背诵时间:', reciteDateTime);
+
+ // 准备保存到数据库的数据(包含所有必要字段,确保古诗ID和古诗名挨着放)
+ const reviewRecord = {
+ openid: this.data.openid, // 用户ID
+ poemId: this.data.poemId, // 古诗ID
+ poemName: this.data.poemTitle, // 古诗名称(与古诗ID挨着放)
+ accuracy: accuracyRate, // 正确率
+ duration: durationSeconds, // 背诵时长(秒)
+ reciteDateTime: reciteDateTime // 背诵时间
+ };
+
+ // 先显示结果,然后异步保存(避免UI阻塞)
+ this.setData({
+ accuracyRate: accuracyRate,
+ showResult: true,
+ reciteDuration: reciteDuration,
+ reciteDateTime: reciteDateTime
+ });
+
+ // 异步保存到数据库
+ try {
+ const saveSuccess = await this.saveReviewRecord(reviewRecord);
+
+ if (saveSuccess) {
+ console.log('背诵记录保存成功');
+ // saveReviewRecord 内部已经有成功提示
+ } else {
+ console.log('背诵记录保存失败,但不影响结果显示');
+ // 不重复显示错误提示,saveReviewRecord 内部已经处理
+ }
+ } catch (err) {
+ console.error('保存背诵记录时发生异常:', err);
+ // 异常已在 saveReviewRecord 内部处理
+ }
+
+ // 延迟显示根据正确率的提示消息,避免与保存结果提示重叠
+ setTimeout(() => {
+ console.log('背诵正确率:', accuracyRate + '%');
+ console.log('背诵时长:', reciteDuration);
+
+ // 根据正确率给出提示
+ let message = '';
+ if (accuracyRate >= 90) {
+ message = '太棒了!背诵得很准确!';
+ } else if (accuracyRate >= 70) {
+ message = '不错的背诵!继续加油!';
+ } else if (accuracyRate >= 50) {
+ message = '还可以,再练习一下会更好!';
+ } else {
+ message = '需要多练习,加油!';
+ }
+
+ wx.showToast({
+ title: message,
+ icon: 'none',
+ duration: 2000
+ });
+ }, 1000); // 延迟1秒显示提示消息
+ },
+
/**
- * 页面相关事件处理函数--监听用户下拉动作
+ * 重置背诵状态,清除识别结果和正确率
*/
- onPullDownRefresh() {
-
+ resetRecitation: function() {
+ console.log('重置背诵状态');
+ // 重置所有相关数据
+ this.setData({
+ content: '',
+ showResult: false,
+ accuracyRate: 0,
+ startReciteTime: 0,
+ reciteDuration: '',
+ reciteDateTime: ''
+ });
+ console.log('重置完成,准备新的背诵');
},
/**
- * 页面上拉触底事件的处理函数
+ * 当用户编辑识别结果时触发
*/
- onReachBottom() {
-
+ onContentInput(e) {
+ console.log('用户编辑了识别结果', e.detail.value);
+ this.setData({
+ content: e.detail.value
+ });
},
- /**
- * 用户点击右上角分享
- */
- onShareAppMessage() {
+// 获取token
+ getToken:function(){
+ let that=this;
+ let ApiKey='qdHcePu7v0WpIlJnaeGJZZqp';
+ let SecretKey='nuZCuBziZoO2gjE6NGEMKCdxKs4sbaNq';
+ const url = 'https://aip.baidubce.com/oauth/2.0/token?grant_type=client_credentials&client_id='+ApiKey+'&client_secret='+SecretKey
+ wx.request({
+ url:url,
+ method: 'POST',
+ success(res){
+ console.log("创建access_token成功",res)
+ //将access_token存储到storage中
+ wx.setStorage({
+ key:'access_token',
+ data:res.data.access_token
+ });
+ var date=new Date().getTime();
+ let time=date+2592000*1000;
+ console.log('三十天后的时间',time);
+ wx.setStorage({
+ key:'expires_in',
+ data:time
+ });
+ that.setData({
+ token:res.data.access_token
+ });
+ },
+ fail(err) {
+ console.error('获取token失败:', err);
+ }
+ });
+ },
+ //开始录音
+ touchStart: function () {
+ const that = this; // 保存this引用
+ wx.authorize({
+ scope: 'scope.record',
+ success() {
+ console.log("录音授权成功");
+ // 记录开始背诵的时间
+ const startTime = new Date().getTime();
+ console.log('开始背诵时间:', startTime);
+ that.setData({
+ startReciteTime: startTime
+ });
+
+ recorderManager.start(options);
+ recorderManager.onStart(() => {
+ console.log('recorder start')
+ });
+ },
+ fail() {
+ console.log("录音失败");
+ }
+ })
+ },
- }
+ //停止录音
+ touchEnd: function () {
+ let that = this
+ recorderManager.stop();
+ recorderManager.onStop((res) => {
+ console.log('文件路径==', res)
+ tempFilePath= res.tempFilePath;
+ //获取文件长度
+ wx.getFileSystemManager().getFileInfo({
+ filePath: tempFilePath,
+ success: function (res) {
+ filesize = res.size
+ console.log('文件长度', res)
+ that.shibie()
+ }, fail: function (res) {
+ console.log("读取文件长度错误",res);
+ }
+ })
+ });
+ },
+ //语音识别
+ shibie(){
+ let that = this
+ wx.getFileSystemManager().readFile({
+ filePath: tempFilePath,
+ encoding: 'base64',
+ success: function (res) {
+ wx.request({
+ url: 'http://vop.baidu.com/server_api',
+ data: {
+ token: that.data.token,
+ cuid: "12_56",
+ format: 'pcm',
+ rate: 16000,
+ channel: 1,
+ speech: res.data,
+ len: filesize
+ },
+ headers: {
+ 'Content-Type': 'application/json'
+ },
+ method: "post",
+ success: function (res) {
+ if (res.data.result == '') {
+ wx.showModal({
+ title: '提示',
+ content: '听不清楚,请重新说一遍!',
+ showCancel: false
+ })
+ return;
+ }
+ console.log("识别成功==",res.data);
+ let recognizedText = res.data.result;
+ console.log("原始识别结果格式:", typeof recognizedText);
+ console.log("原始识别结果内容:", recognizedText);
+
+ // 百度API通常返回数组格式的结果,需要正确处理
+ if (Array.isArray(recognizedText)) {
+ console.log("处理数组格式结果");
+ // 百度API的标准格式是返回字符串数组,如["识别结果"]
+ if (recognizedText.length > 0) {
+ recognizedText = recognizedText[0];
+ } else {
+ recognizedText = "";
+ }
+ }
+
+ // 确保最终是字符串格式
+ if (recognizedText !== null && recognizedText !== undefined) {
+ recognizedText = String(recognizedText);
+ } else {
+ recognizedText = "";
+ }
+
+ console.log("最终处理后的识别文本:", recognizedText);
+ that.setData({
+ content: recognizedText
+ });
+ console.log('语音识别完成,识别文本:', recognizedText);
+ },
+ fail: function (res) {
+ console.log("失败",res);
+ }
+ }); //语音识别结束
+ }
+ })
+ }
})
\ No newline at end of file
diff --git a/pages/recite/recite.wxml b/pages/recite/recite.wxml
index 86229e5..a773be3 100644
--- a/pages/recite/recite.wxml
+++ b/pages/recite/recite.wxml
@@ -1,2 +1,64 @@
-pages/recite/recite.wxml
\ No newline at end of file
+
+
+
+
+
+
+ 识别结果:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 背诵用时
+ {{reciteDuration}}
+
+
+
+ 背诵时间
+ {{reciteDateTime}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/pages/recite/recite.wxss b/pages/recite/recite.wxss
index 6b0172c..53e3f56 100644
--- a/pages/recite/recite.wxss
+++ b/pages/recite/recite.wxss
@@ -1 +1,218 @@
-/* pages/recite/recite.wxss */
\ No newline at end of file
+/* pages/recite/recite.wxss */
+.recite-container {
+ padding: 20rpx;
+ background-color: #f8f8f8;
+ min-height: 100vh;
+ box-sizing: border-box;
+}
+
+/* 头部样式 */
+.header {
+ text-align: center;
+ margin-bottom: 40rpx;
+ padding: 30rpx 0;
+ background: linear-gradient(135deg, #ffecd2 0%, #fcb69f 100%);
+ border-radius: 15rpx;
+ box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.1);
+}
+
+.title {
+ font-size: 48rpx;
+ font-weight: bold;
+ color: #333;
+ display: block;
+ margin-bottom: 10rpx;
+}
+
+.subtitle {
+ font-size: 32rpx;
+ color: #666;
+}
+
+/* 原文和识别结果区域 */
+.original-section,
+.recognition-section {
+ background: #fff;
+ padding: 30rpx;
+ margin-bottom: 30rpx;
+ border-radius: 15rpx;
+ box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.08);
+}
+
+.section-title {
+ font-size: 36rpx;
+ font-weight: bold;
+ color: #333;
+ display: block;
+ margin-bottom: 20rpx;
+}
+
+.original-text {
+ font-size: 34rpx;
+ line-height: 1.8;
+ color: #444;
+ white-space: pre-wrap;
+ word-break: break-all;
+}
+
+.result-textarea {
+ width: 100%;
+ min-height: 200rpx;
+ padding: 20rpx;
+ font-size: 34rpx;
+ line-height: 1.6;
+ color: #333;
+ background: #f9f9f9;
+ border: 1rpx solid #ddd;
+ border-radius: 10rpx;
+ box-sizing: border-box;
+}
+
+/* 正确率显示区域 */
+.result-section {
+ text-align: center;
+ margin: 40rpx 0;
+}
+
+/* 进度条样式正确率显示 */
+.accuracy-progress-container {
+ background: #fff;
+ padding: 40rpx;
+ border-radius: 20rpx;
+ box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.1);
+ margin-bottom: 30rpx;
+}
+
+.accuracy-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 20rpx;
+}
+
+/* 背诵信息显示区域 */
+.info-section {
+ margin-bottom: 30rpx;
+ padding: 15rpx 0;
+ border-top: 1rpx solid #f0f0f0;
+}
+
+/* 信息项样式 */
+.info-item {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 10rpx 0;
+}
+
+.info-label {
+ font-size: 32rpx;
+ color: #666;
+}
+
+.info-value {
+ font-size: 34rpx;
+ font-weight: bold;
+}
+
+/* 背诵时长样式 */
+.duration-value {
+ color: #007AFF;
+ letter-spacing: 2rpx;
+}
+
+/* 背诵时间样式 */
+.date-value {
+ color: #34C759;
+ font-family: 'Courier New', monospace;
+}
+
+.accuracy-title {
+ font-size: 36rpx;
+ font-weight: bold;
+ color: #333;
+}
+
+.accuracy-value {
+ font-size: 48rpx;
+ font-weight: bold;
+ color: #4CAF50;
+ text-shadow: 0 2rpx 4rpx rgba(0, 0, 0, 0.1);
+}
+
+/* 进度条样式 */
+.progress-bar {
+ margin-bottom: 30rpx;
+}
+
+.progress-bg {
+ width: 100%;
+ height: 30rpx;
+ background: #f0f0f0;
+ border-radius: 15rpx;
+ overflow: hidden;
+ box-shadow: inset 0 2rpx 4rpx rgba(0, 0, 0, 0.1);
+}
+
+.progress-fill {
+ height: 100%;
+ border-radius: 15rpx;
+ transition: width 0.6s ease;
+ box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.2);
+}
+
+/* 评价文字 */
+.accuracy-comment {
+ font-size: 32rpx;
+ color: #666;
+ display: block;
+ padding: 20rpx 0;
+ background: #f9f9f9;
+ border-radius: 10rpx;
+ margin-top: 10rpx;
+}
+
+/* 检查按钮区域 */
+.check-section {
+ text-align: center;
+ margin: 40rpx 0;
+}
+
+.btn-check {
+ width: 60%;
+ font-size: 36rpx;
+ padding: 20rpx 0;
+ background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
+ border: none;
+ color: white;
+}
+
+/* 重置按钮 */
+.btn-reset {
+ margin-top: 30rpx;
+ font-size: 32rpx;
+ width: 50%;
+}
+
+/* 控制按钮区域 */
+.control-buttons {
+ display: flex;
+ justify-content: space-around;
+ margin-top: 40rpx;
+ padding-bottom: 40rpx;
+}
+
+.btn-record-start,
+.btn-record-stop {
+ width: 280rpx;
+ font-size: 34rpx;
+ padding: 20rpx 0;
+}
+
+/* 响应式设计 */
+@media screen and (min-width: 768px) {
+ .recite-container {
+ max-width: 800rpx;
+ margin: 0 auto;
+ }
+}
\ No newline at end of file
diff --git a/pages/review/review.js b/pages/review/review.js
index 2945a72..565f5eb 100644
--- a/pages/review/review.js
+++ b/pages/review/review.js
@@ -1,52 +1,747 @@
// pages/review/review.js
+
+// SM2算法实现
+class SM2 {
+ constructor() {
+ this.ef = 2.5; // 初始易度因子(Easiness Factor)
+ this.reps = 0; // 重复次数
+ this.lastInterval = 0; // 上次复习间隔
+ }
+
+ /**
+ * 根据用户回忆质量计算下次复习间隔和更新易度因子
+ * @param {number} quality - 回忆质量 (0-5分): 0=完全错误, 5=完美回忆
+ * @returns {object} - {nextInterval: 下次复习间隔(天), newEF: 新的易度因子}
+ */
+ calculateNextReview(quality) {
+ // 根据回忆质量更新易度因子
+ if (quality < 3) { // 回忆失败 (0-2分)
+ // 重置重复次数,间隔设为1天
+ this.reps = 0;
+ this.lastInterval = 1;
+ // 降低易度因子,但不低于1.3
+ this.ef = Math.max(1.3, this.ef - 0.2);
+ } else { // 回忆成功 (3-5分)
+ // 第一次成功后,使用特定的计算方式
+ if (this.reps === 0) {
+ this.lastInterval = 1; // 第一次成功后1天后复习
+ } else if (this.reps === 1) {
+ this.lastInterval = 6; // 第二次成功后6天后复习
+ } else {
+ // 之后使用易度因子计算
+ this.lastInterval = Math.round(this.lastInterval * this.ef);
+ }
+ // 更新易度因子公式:EF' = EF + (0.1 - (5 - q) * (0.08 + (5 - q) * 0.02))
+ this.ef = Math.max(1.3, this.ef + (0.1 - (5 - quality) * (0.08 + (5 - quality) * 0.02)));
+ this.reps++;
+ }
+
+ return {
+ nextInterval: this.lastInterval,
+ newEF: this.ef,
+ repetitions: this.reps
+ };
+ }
+
+ /**
+ * 基于艾宾浩斯公式计算记忆保留率
+ * R = e^(-t/S),其中t是时间间隔(小时),S是记忆稳定性
+ * @param {number} hoursSinceLastReview - 距离上次复习的小时数
+ * @param {number} stability - 记忆稳定性(可从易度因子推导)
+ * @returns {number} - 记忆保留率 (0-100%)
+ */
+ calculateRetentionRate(hoursSinceLastReview, stability) {
+ // 默认稳定性,如果未提供则基于易度因子计算
+ const S = stability || (this.ef * 10); // 简化:易度因子*10作为稳定性
+ const R = Math.exp(-hoursSinceLastReview / S);
+ return Math.round(R * 100);
+ }
+}
+
Page({
/**
* 页面的初始数据
*/
data: {
-
+ // 遗忘曲线数据 - 显示未来7天的复习计划
+ 复习计划数据: [],
+ // 需要复习的古诗列表
+ poemsToReview: [],
+ // Canvas尺寸
+ canvasWidth: 0,
+ canvasHeight: 300,
+ // 今日需要复习的总数
+ todayReviewCount: 0
},
/**
* 生命周期函数--监听页面加载
*/
- onLoad(options) {
-
+ onLoad: function (options) {
+ // 初始化数据加载
+ this.loadPoemsToReview().then(() => {
+ // 数据加载完成后绘制图表
+ this.drawReviewPlanChart();
+ });
+ },
+
+ /**
+ * 加载需要复习的古诗数据
+ */
+ async loadPoemsToReview() {
+ try {
+ wx.showLoading({ title: '加载复习计划...' })
+
+ // 从数据库加载真实背诵记录
+ let reciteRecords = await this.loadReciteRecordsFromDatabase();
+
+ // 如果没有记录或记录为空,使用模拟数据
+ let recordsToUse = reciteRecords && reciteRecords.length > 0 ? reciteRecords : this.getMockReciteRecords();
+
+ // 增强:为每条记录获取完整的诗歌信息,特别是作者信息
+ if (reciteRecords && reciteRecords.length > 0) {
+ recordsToUse = await this.enrichRecordsWithPoemDetails(reciteRecords);
+ }
+
+ // 使用SM2算法计算复习计划
+ const poemsWithReviewPlan = this.calculateReviewPlan(recordsToUse);
+
+ // 生成未来7天的复习计划数据(用于图表显示)
+ const reviewPlanData = this.generateReviewPlanData(poemsWithReviewPlan);
+
+ // 计算今天需要复习的数量
+ const todayReviewCount = poemsWithReviewPlan.filter(p => p.needsReviewToday).length;
+
+ this.setData({
+ poemsToReview: poemsWithReviewPlan,
+ 复习计划数据: reviewPlanData,
+ todayReviewCount
+ });
+
+ wx.hideLoading()
+ return poemsWithReviewPlan;
+ } catch (error) {
+ wx.hideLoading()
+ console.error('加载复习数据失败:', error);
+ // 出错时使用模拟数据
+ const mockRecords = this.getMockReciteRecords();
+ const poemsWithReviewPlan = this.calculateReviewPlan(mockRecords);
+ const reviewPlanData = this.generateReviewPlanData(poemsWithReviewPlan);
+ const todayReviewCount = poemsWithReviewPlan.filter(p => p.needsReviewToday).length;
+
+ this.setData({
+ poemsToReview: poemsWithReviewPlan,
+ 复习计划数据: reviewPlanData,
+ todayReviewCount
+ });
+
+ return poemsWithReviewPlan;
+ }
+ },
+
+ /**
+ * 为复习记录获取完整的诗歌详情信息
+ */
+ async enrichRecordsWithPoemDetails(records) {
+ try {
+ console.log('正在获取完整诗歌信息...');
+
+ // 为每条记录异步获取诗歌详情
+ const enrichedRecords = [];
+ for (const record of records) {
+ try {
+ // 调用云函数获取诗歌详情
+ const { result } = await wx.cloud.callFunction({
+ name: 'poemManagement',
+ data: {
+ action: 'getPoemDetail',
+ id: record.poemId || record._id
+ }
+ });
+
+ // 如果成功获取到详情,更新作者信息
+ if (result && result.success && result.data) {
+ const poemDetail = result.data;
+
+ // 创建增强后的记录,保留原始字段,更新作者信息
+ const enrichedRecord = {
+ ...record,
+ poemAuthor: poemDetail.author || record.poemAuthor || record.author || '未知作者',
+ poemTitle: poemDetail.title || record.poemTitle || record.poemName || '未知标题'
+ };
+
+ enrichedRecords.push(enrichedRecord);
+ console.log(`成功获取诗歌ID: ${record.poemId || record._id} 的完整信息`);
+ } else {
+ // 获取失败时保留原始记录
+ console.log(`无法获取诗歌ID: ${record.poemId || record._id} 的详情,使用原始数据`);
+ enrichedRecords.push(record);
+ }
+ } catch (error) {
+ // 单条记录获取失败不影响整体
+ console.error(`获取诗歌详情失败: ${record.poemId || record._id}`, error);
+ enrichedRecords.push(record);
+ }
+
+ // 添加小延迟避免请求过于密集
+ await new Promise(resolve => setTimeout(resolve, 50));
+ }
+
+ console.log('诗歌信息获取完成');
+ return enrichedRecords;
+ } catch (error) {
+ console.error('增强诗歌记录时发生错误:', error);
+ // 如果整体出错,返回原始记录
+ return records;
+ }
+ },
+
+ /**
+ * 获取模拟的背诵记录数据
+ */
+ getMockReciteRecords() {
+ // 仅在数据库加载失败时使用模拟数据
+ const now = Date.now();
+ const dayMs = 24 * 60 * 60 * 1000;
+
+ return [
+ {
+ poemId: 'poet_001',
+ poemTitle: '静夜思',
+ poemAuthor: '李白',
+ lastReciteTime: now - dayMs, // 1天前
+ accuracy: 85,
+ reciteCount: 2,
+ ef: 2.3,
+ nextReviewInterval: 1
+ },
+ {
+ poemId: 'poet_002',
+ poemTitle: '春晓',
+ poemAuthor: '孟浩然',
+ lastReciteTime: now - 3 * dayMs, // 3天前
+ accuracy: 60,
+ reciteCount: 1,
+ ef: 2.0,
+ nextReviewInterval: 1
+ },
+ {
+ poemId: 'poet_003',
+ poemTitle: '望庐山瀑布',
+ poemAuthor: '李白',
+ lastReciteTime: now - 0.5 * dayMs, // 0.5天前
+ accuracy: 95,
+ reciteCount: 3,
+ ef: 2.7,
+ nextReviewInterval: 6
+ },
+ {
+ poemId: 'poet_004',
+ poemTitle: '登鹳雀楼',
+ poemAuthor: '王之涣',
+ lastReciteTime: now - 7 * dayMs, // 7天前
+ accuracy: 70,
+ reciteCount: 2,
+ ef: 2.1,
+ nextReviewInterval: 3
+ }
+ ]
+ },
+
+ /**
+ * 从数据库加载真实的背诵记录
+ */
+ async loadReciteRecordsFromDatabase() {
+ try {
+ // 首先获取用户openid
+ const { result } = await wx.cloud.callFunction({
+ name: 'getOpenId'
+ })
+
+ if (!result.openid) {
+ throw new Error('获取用户信息失败')
+ }
+
+ // 查询用户的背诵记录
+ const db = wx.cloud.database()
+ const records = await db.collection('Review')
+ .where({
+ openid: result.openid
+ })
+ .orderBy('reciteDateTime', 'desc')
+ .get()
+
+ console.log('从数据库获取的背诵记录:', records.data);
+
+ // 转换数据库记录格式,确保字段名一致
+ const formattedRecords = records.data.map(record => {
+ return {
+ poemId: record.poemId,
+ poemTitle: record.poemName || '未知标题',
+ poemAuthor: record.poemAuthor || record.author || '未知作者', // 尝试多种可能的作者字段
+ lastReciteTime: new Date(record.reciteDateTime).getTime(),
+ accuracy: record.accuracy || record.matchRate || 0,
+ reciteCount: record.reciteCount || 1,
+ ef: record.ef || 2.5,
+ nextReviewInterval: record.nextReviewInterval || 1
+ };
+ });
+
+ return formattedRecords;
+ } catch (error) {
+ console.error('加载背诵记录失败:', error);
+ // 如果加载失败,返回空数组,让上层函数处理
+ return [];
+ }
+ },
+
+ /**
+ * 手动初始化数据库
+ */
+ async initDatabaseManually() {
+ try {
+ wx.showLoading({ title: '初始化数据库...' })
+
+ const result = await wx.cloud.callFunction({
+ name: 'initDatabase'
+ })
+
+ wx.hideLoading()
+
+ if (result.result && result.result.success) {
+ wx.showToast({
+ title: '数据库初始化成功',
+ icon: 'success'
+ })
+ // 重新加载数据
+ this.loadPoemsToReview()
+ } else {
+ wx.showToast({
+ title: result.result?.message || '初始化失败',
+ icon: 'none'
+ })
+ }
+ } catch (error) {
+ wx.hideLoading()
+ wx.showToast({
+ title: '初始化失败: ' + error.message,
+ icon: 'none'
+ })
+ }
+ },
+
+ /**
+ * 基于SM2算法计算复习计划和优先级
+ */
+ calculateReviewPlan(records) {
+ const now = Date.now();
+ const sm2 = new SM2();
+
+ // 去重处理,确保每首诗只出现一次
+ const uniqueRecords = [];
+ const poemIdSet = new Set();
+
+ // 优先保留最新的记录(根据传入的顺序,假设前面的记录更新)
+ records.forEach(record => {
+ const poemId = record.poemId || record._id;
+ if (poemId && !poemIdSet.has(poemId)) {
+ poemIdSet.add(poemId);
+ uniqueRecords.push(record);
+ }
+ });
+
+ console.log('去重后的记录数量:', uniqueRecords.length);
+
+ // 计算每首诗的复习状态和下次复习时间
+ const poemsWithReviewStatus = uniqueRecords.map(record => {
+ // 标准化字段名,确保兼容性
+ const poem = {
+ id: record.poemId || record._id,
+ title: record.poemTitle || record.poemName || '未知标题',
+ author: record.poemAuthor || '未知作者',
+ lastReciteTime: record.lastReciteTime || (record.reciteDateTime ? new Date(record.reciteDateTime).getTime() : now - 24 * 60 * 60 * 1000),
+ accuracy: record.accuracy || record.matchRate || 0,
+ reciteCount: record.reciteCount || 0,
+ ef: record.ef || 2.5, // 易度因子
+ nextReviewInterval: record.nextReviewInterval || 1 // 下次复习间隔
+ };
+
+ // 计算距离上次背诵的时间(天)
+ const daysSinceLastRecite = (now - poem.lastReciteTime) / (24 * 60 * 60 * 1000);
+
+ // 根据正确率转换为SM2质量评分 (0-5分)
+ let quality = 0;
+ if (poem.accuracy >= 90) quality = 5;
+ else if (poem.accuracy >= 80) quality = 4;
+ else if (poem.accuracy >= 60) quality = 3;
+ else if (poem.accuracy >= 40) quality = 2;
+ else if (poem.accuracy >= 20) quality = 1;
+
+ // 使用SM2计算下次复习间隔
+ sm2.ef = poem.ef;
+ sm2.reps = poem.reciteCount;
+ sm2.lastInterval = poem.nextReviewInterval;
+
+ const reviewResult = sm2.calculateNextReview(quality);
+
+ // 判断是否需要今天复习
+ const needsReviewToday = daysSinceLastRecite >= poem.nextReviewInterval - 0.1; // 允许0.1天的误差
+
+ // 计算记忆保留率(基于艾宾浩斯公式)
+ const hoursSinceLastReview = daysSinceLastRecite * 24;
+ const retentionRate = sm2.calculateRetentionRate(hoursSinceLastReview, poem.ef * 10);
+
+ return {
+ ...poem,
+ daysSinceLastRecite: Math.round(daysSinceLastRecite * 10) / 10, // 保留一位小数
+ nextReviewInterval: reviewResult.nextInterval,
+ newEF: reviewResult.newEF,
+ needsReviewToday,
+ retentionRate,
+ reviewUrgency: needsReviewToday ? (100 - retentionRate) : 0
+ };
+ });
+
+ // 按复习紧急程度排序(今天需要复习的排前面,按记忆保留率从低到高)
+ poemsWithReviewStatus.sort((a, b) => {
+ if (a.needsReviewToday && !b.needsReviewToday) return -1;
+ if (!a.needsReviewToday && b.needsReviewToday) return 1;
+ return b.reviewUrgency - a.reviewUrgency;
+ });
+
+ return poemsWithReviewStatus;
+ },
+
+ /**
+ * 生成未来7天的复习计划数据(用于图表显示)
+ */
+ generateReviewPlanData(poems) {
+ const planData = [];
+ const now = new Date();
+
+ // 初始化未来7天的数据
+ for (let i = 0; i < 7; i++) {
+ const date = new Date(now);
+ date.setDate(date.getDate() + i);
+ planData.push({
+ day: i === 0 ? '今天' : i === 1 ? '明天' : `${i}天后`,
+ dateStr: `${date.getMonth() + 1}/${date.getDate()}`,
+ count: 0
+ });
+ }
+
+ // 统计每天需要复习的古诗数量
+ poems.forEach(poem => {
+ // 优先使用needsReviewToday标志来判断是否今天需要复习
+ if (poem.needsReviewToday) {
+ // 如果标记为今天需要复习,直接计入今天的数量
+ planData[0].count++;
+ return; // 跳过后续计算
+ }
+
+ // 对于不是今天需要复习的,正常计算下次复习日期
+ const lastReviewDate = new Date(poem.lastReciteTime);
+ const nextReviewDate = new Date(lastReviewDate);
+ nextReviewDate.setDate(nextReviewDate.getDate() + poem.nextReviewInterval);
+
+ // 计算下次复习日期距离今天的天数(使用宽松计算,不四舍五入)
+ const daysUntilNextReview = Math.floor((nextReviewDate - now) / (24 * 60 * 60 * 1000));
+
+ // 更新对应天数的复习数量
+ if (daysUntilNextReview >= 0 && daysUntilNextReview < 7) {
+ planData[daysUntilNextReview].count++;
+ }
+ });
+
+ return planData;
+ },
+
+ /**
+ * 跳转到学习页面
+ */
+ goToStudy(e) {
+ const poemId = e.currentTarget.dataset.id;
+ if (poemId) {
+ wx.navigateTo({
+ url: `/pages/study/study?id=${poemId}`,
+ fail: function(res) {
+ console.error('跳转到学习页面失败:', res);
+ wx.showToast({
+ title: '跳转失败,请重试',
+ icon: 'none'
+ });
+ }
+ });
+ } else {
+ console.error('缺少诗歌ID参数');
+ wx.showToast({
+ title: '参数错误',
+ icon: 'none'
+ });
+ }
+ },
+
+ /**
+ * 跳转到背诵页面
+ */
+ goToRecite(e) {
+ const poemId = e.currentTarget.dataset.id;
+ const poemTitle = e.currentTarget.dataset.title;
+ const poemAuthor = e.currentTarget.dataset.author;
+ if (poemId) {
+ // 查找对应的诗歌完整数据
+ const poem = this.data.poemsToReview.find(p => p.id === poemId);
+
+ try {
+ if (poem) {
+ // 构建完整的诗歌数据对象
+ const poemData = {
+ _id: poem.id,
+ title: poem.title,
+ author: poem.author,
+ content: '加载中...' // 实际内容会在背诵页面加载
+ };
+
+ // 编码并传递数据
+ const encodedPoemData = encodeURIComponent(JSON.stringify(poemData));
+ wx.navigateTo({
+ url: `/pages/recite/recite?id=${poemId}&poemData=${encodedPoemData}`,
+ fail: function(res) {
+ console.error('跳转到背诵页面失败:', res);
+ // 降级方案:传递ID、标题和作者
+ wx.navigateTo({
+ url: `/pages/recite/recite?id=${poemId}&title=${encodeURIComponent(poemTitle || '未知古诗')}&author=${encodeURIComponent(poemAuthor || '未知作者')}`
+ });
+ }
+ });
+ } else {
+ // 降级方案:传递ID、标题和作者
+ console.warn('无法找到完整诗歌数据,传递ID、标题和作者:', poemId);
+ wx.navigateTo({
+ url: `/pages/recite/recite?id=${poemId}&title=${encodeURIComponent(poemTitle || '未知古诗')}&author=${encodeURIComponent(poemAuthor || '未知作者')}`,
+ fail: function(res) {
+ console.error('跳转到背诵页面失败:', res);
+ wx.showToast({
+ title: '跳转失败,请重试',
+ icon: 'none'
+ });
+ }
+ });
+ }
+ } catch (error) {
+ console.error('处理诗歌数据时出错:', error);
+ // 异常情况下,使用降级方案
+ wx.navigateTo({
+ url: `/pages/recite/recite?id=${poemId}&title=${encodeURIComponent(poemTitle || '未知古诗')}&author=${encodeURIComponent(poemAuthor || '未知作者')}`
+ });
+ }
+ } else {
+ console.error('缺少诗歌ID参数');
+ wx.showToast({
+ title: '参数错误',
+ icon: 'none'
+ });
+ }
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
- onReady() {
-
+ onReady: function () {
+ // 获取系统信息设置Canvas尺寸
+ const sysInfo = wx.getSystemInfoSync();
+ const windowWidth = sysInfo.windowWidth;
+
+ this.setData({
+ canvasWidth: windowWidth - 40, // 左右各留20px边距
+ canvasHeight: 300
+ });
+
+ // 确保数据加载完成后绘制图表
+ if (this.data.复习计划数据 && this.data.复习计划数据.length > 0) {
+ this.drawReviewPlanChart();
+ }
},
-
+
/**
* 生命周期函数--监听页面显示
*/
- onShow() {
-
+ onShow: function() {
+ // 每次页面显示时重新加载数据并绘制图表
+ this.loadPoemsToReview().then(() => {
+ setTimeout(() => {
+ // 延迟执行确保Canvas已准备好
+ this.drawReviewPlanChart();
+ }, 100);
+ });
},
/**
- * 生命周期函数--监听页面隐藏
+ * 绘制复习计划图表
+ * 显示未来7天每天需要复习的古诗数量
*/
- onHide() {
+ drawReviewPlanChart() {
+ const ctx = wx.createCanvasContext('reviewPlanChart');
+ const { canvasWidth, canvasHeight } = this.data;
+ const data = this.data.复习计划数据;
+
+ // 确保有数据
+ if (!data || data.length === 0) {
+ // 绘制无数据状态
+ ctx.setFillStyle('#ffffff');
+ ctx.fillRect(0, 0, canvasWidth, canvasHeight);
+ ctx.setFillStyle('#999999');
+ ctx.setFontSize(14);
+ ctx.setTextAlign('center');
+ ctx.fillText('暂无复习计划数据', canvasWidth / 2, canvasHeight / 2);
+ ctx.draw();
+ return;
+ }
+
+ // 设置画布背景
+ ctx.setFillStyle('#ffffff');
+ ctx.fillRect(0, 0, canvasWidth, canvasHeight);
+
+ // 计算坐标转换参数
+ const padding = 40;
+ const plotWidth = canvasWidth - 2 * padding;
+ const plotHeight = canvasHeight - 2 * padding;
+ const maxCount = Math.max(...data.map(item => item.count), 1); // 至少为1,避免除以0
+
+ // 绘制坐标轴
+ ctx.setStrokeStyle('#e0e0e0');
+ ctx.setLineWidth(1);
+
+ // 垂直网格线
+ for (let i = 0; i <= 6; i++) {
+ const x = padding + (plotWidth / 6) * i;
+ ctx.beginPath();
+ ctx.moveTo(x, padding);
+ ctx.lineTo(x, canvasHeight - padding);
+ ctx.stroke();
+ }
+
+ // 水平网格线
+ const maxYAxisValue = Math.ceil(maxCount * 1.2); // 留出20%的顶部空间
+ const yAxisSteps = Math.min(5, maxYAxisValue); // 最多5条水平线
+ for (let i = 0; i <= yAxisSteps; i++) {
+ const y = padding + (plotHeight / yAxisSteps) * i;
+ ctx.beginPath();
+ ctx.moveTo(padding, y);
+ ctx.lineTo(canvasWidth - padding, y);
+ ctx.stroke();
+ }
+
+ // 绘制坐标轴标签
+ ctx.setFillStyle('#666666');
+ ctx.setFontSize(12);
+ ctx.setTextAlign('center');
+
+ // X轴标签(显示日期)
+ data.forEach((item, index) => {
+ const x = padding + (plotWidth / 6) * index;
+
+ // 绘制日期
+ ctx.fillText(item.dateStr, x, canvasHeight - padding + 15);
+
+ // 绘制天数
+ ctx.fillText(item.day, x, canvasHeight - padding + 30);
+ });
+
+ // Y轴标签
+ ctx.setTextAlign('right');
+ for (let i = 0; i <= yAxisSteps; i++) {
+ const count = maxYAxisValue * (1 - i / yAxisSteps);
+ const y = padding + (plotHeight / yAxisSteps) * i;
+ ctx.fillText(Math.round(count), padding - 10, y + 5);
+ }
+
+ // 绘制曲线图
+ if (data.length > 1) {
+ ctx.setStrokeStyle('#4CAF50'); // 使用绿色作为主色调
+ ctx.setLineWidth(2);
+ ctx.beginPath();
+
+ data.forEach((item, index) => {
+ const x = padding + (plotWidth / 6) * index;
+ const valueRatio = item.count / maxYAxisValue;
+ const y = canvasHeight - padding - (valueRatio * plotHeight);
+
+ // 绘制点
+ ctx.beginPath();
+ ctx.arc(x, y, 4, 0, 2 * Math.PI);
+ ctx.fillStyle = '#4CAF50';
+ ctx.fill();
+
+ // 连接点形成曲线
+ if (index === 0) {
+ ctx.moveTo(x, y);
+ } else {
+ // 使用贝塞尔曲线使线条更平滑
+ const prevItem = data[index - 1];
+ const prevX = padding + (plotWidth / 6) * (index - 1);
+ const prevY = canvasHeight - padding - ((prevItem.count / maxYAxisValue) * plotHeight);
+
+ const cpx1 = prevX + (x - prevX) / 2;
+ const cpy1 = prevY;
+ const cpx2 = prevX + (x - prevX) / 2;
+ const cpy2 = y;
+
+ ctx.bezierCurveTo(cpx1, cpy1, cpx2, cpy2, x, y);
+ }
+ });
+
+ ctx.stroke();
+ }
+
+ // 绘制每个数据点的值
+ ctx.setFillStyle('#333333');
+ ctx.setFontSize(12);
+ ctx.setTextAlign('center');
+
+ data.forEach((item, index) => {
+ const x = padding + (plotWidth / 6) * index;
+ const valueRatio = item.count / maxYAxisValue;
+ const y = canvasHeight - padding - (valueRatio * plotHeight);
+
+ // 在点的上方显示数值
+ ctx.fillText(item.count.toString(), x, y - 10);
+ });
+
+ // 绘制标题
+ ctx.setFillStyle('#333333');
+ ctx.setFontSize(16);
+ ctx.setTextAlign('center');
+ ctx.fillText('未来7天复习计划', canvasWidth / 2, padding - 10);
+
+ ctx.draw();
+ },
+
+ // 删除不再需要的辅助函数,所有功能现在在drawReviewPlanChart中集中实现
+
+
+ /**
+ * 页面相关事件处理函数--监听用户下拉动作
+ */
+ onPullDownRefresh() {
+ // 刷新页面数据
+ this.drawForgettingCurve();
+ wx.stopPullDownRefresh();
},
/**
- * 生命周期函数--监听页面卸载
+ * 生命周期函数--监听页面隐藏
*/
- onUnload() {
+ onHide() {
},
/**
- * 页面相关事件处理函数--监听用户下拉动作
+ * 生命周期函数--监听页面卸载
*/
- onPullDownRefresh() {
+ onUnload() {
},
diff --git a/pages/review/review.wxml b/pages/review/review.wxml
index e781415..1836a6c 100644
--- a/pages/review/review.wxml
+++ b/pages/review/review.wxml
@@ -1,2 +1,56 @@
-pages/review/review.wxml
\ No newline at end of file
+
+
+
+
+
+ 复习计划
+
+
+
+ 根据SM2算法和艾宾浩斯遗忘曲线生成的复习计划,横轴为天数,纵轴为每天需要复习的古诗数量。
+
+
+
+ 今日有 {{todayReviewCount}} 首古诗需要复习
+
+
+ 今日暂无需要复习的古诗
+
+
+
+
+
+ 今日推荐复习
+
+
+
+
+
+
+
+ 上次背诵: {{item.daysSinceLastRecite}}天前
+ 正确率: {{item.accuracy}}%
+ 记忆保留率: {{item.retentionRate}}%
+ 下次复习: {{item.nextReviewInterval}}天后
+
+
+
+
+
+
+
+
+
+
+
+ 暂无推荐复习的古诗
+ 开始学习新古诗后,系统会自动生成复习计划
+
+
\ No newline at end of file
diff --git a/pages/review/review.wxss b/pages/review/review.wxss
index f6ff097..a0c419f 100644
--- a/pages/review/review.wxss
+++ b/pages/review/review.wxss
@@ -1 +1,215 @@
-/* pages/review/review.wxss */
\ No newline at end of file
+/* pages/review/review.wxss */
+
+/* 页面容器 */
+.container {
+ padding: 30rpx;
+ background-color: #f8f8f8;
+ min-height: 100vh;
+}
+
+/* 页面标题 */
+.page-header {
+ text-align: center;
+ margin-bottom: 15rpx;
+}
+
+.page-title {
+ font-size: 40rpx;
+ font-weight: bold;
+ color: #333;
+}
+
+/* 遗忘曲线区域 */
+.curve-section {
+ background-color: #fff;
+ border-radius: 20rpx;
+ padding: 30rpx;
+ margin-bottom: 30rpx;
+ box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
+}
+
+.section-title {
+ font-size: 32rpx;
+ font-weight: 600;
+ color: #333;
+ margin-bottom: 20rpx;
+}
+
+.curve-container {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+}
+
+.curve-description {
+ margin-top: 20rpx;
+ font-size: 24rpx;
+ color: rgb(126, 123, 123);
+ line-height: 36rpx;
+ text-align: center;
+}
+
+/* 复习计划区域 */
+.plan-section {
+ background-color: #fff;
+ border-radius: 20rpx;
+ padding: 30rpx;
+ box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
+}
+
+.section-subtitle {
+ font-size: 24rpx;
+ color: #666;
+ margin-bottom: 20rpx;
+}
+
+/* 复习古诗列表 */
+.poem-list {
+ width: 100%;
+}
+
+.poem-item {
+ background-color: #ffffff;
+ border-radius: 20rpx;
+ padding: 30rpx;
+ margin-bottom: 20rpx;
+ box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
+ display: flex;
+ flex-direction: column;
+ transition: transform 0.2s ease;
+ position: relative;
+}
+
+.poem-item.urgent {
+ border-left: 8rpx solid #ff6b6b;
+}
+
+.poem-item:last-child {
+ margin-bottom: 0;
+}
+
+.poem-header {
+ display: flex;
+ flex-direction: column;
+ margin-bottom: 20rpx;
+}
+
+.poem-title {
+ font-size: 28rpx;
+ font-weight: 600;
+ color: #333;
+ margin-bottom: 10rpx;
+ display: flex;
+ align-items: center;
+}
+
+.poem-details {
+ margin-bottom: 15rpx;
+}
+
+.poem-author {
+ font-size: 24rpx;
+ color: #666;
+ margin-bottom: 15rpx;
+}
+
+.review-tag {
+ background-color: #ff6b6b;
+ color: white;
+ padding: 4rpx 16rpx;
+ border-radius: 20rpx;
+ font-size: 20rpx;
+ margin-left: 16rpx;
+ vertical-align: middle;
+}
+
+.poem-meta {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 20rpx;
+ margin-bottom: 20rpx;
+}
+
+.meta-item {
+ font-size: 22rpx;
+ color: #666;
+ background-color: #f5f5f5;
+ padding: 6rpx 16rpx;
+ border-radius: 24rpx;
+}
+
+.meta-item.retention-rate {
+ background-color: #e8f5e9;
+ color: #388e3c;
+}
+
+.meta-item.next-review {
+ background-color: #e3f2fd;
+ color: #1976d2;
+}
+
+.poem-stats {
+ font-size: 24rpx;
+ color: #999;
+ margin-bottom: 10rpx;
+}
+
+/* 操作按钮 */
+.action-buttons {
+ display: flex;
+ justify-content: space-between;
+ gap: 20rpx;
+ margin-top: 10rpx;
+}
+
+.study-btn, .recite-btn {
+ flex: 1;
+ font-size: 24rpx;
+ padding: 16rpx 0;
+ margin: 0;
+ border-radius: 40rpx;
+ border: none;
+}
+
+.study-btn {
+ background-color: #f0f0f0;
+ color: #333;
+}
+
+.recite-btn {
+ background-color: #4CAF50;
+ color: white;
+}
+
+/* 今日复习统计 */
+.today-summary {
+ margin-top: 30rpx;
+ padding: 20rpx;
+ background-color: #f8f9fa;
+ border-radius: 16rpx;
+ text-align: center;
+}
+
+.today-count {
+ font-size: 28rpx;
+ color: #555;
+}
+
+.today-count .highlight {
+ color: #ff6b6b;
+ font-weight: bold;
+ font-size: 36rpx;
+}
+
+/* 空状态 */
+.empty-state {
+ text-align: center;
+ padding: 60rpx 0;
+ color: #999;
+}
+
+.empty-state .hint {
+ margin-top: 20rpx;
+ font-size: 26rpx;
+ color: #bbb;
+}
\ No newline at end of file
diff --git a/pages/search/search.js b/pages/search/search.js
index 4c1333a..0ff533b 100644
--- a/pages/search/search.js
+++ b/pages/search/search.js
@@ -5,14 +5,22 @@ Page({
* 页面的初始数据
*/
data: {
-
+ keyword: '',
+ showResults: false,
+ searchResults: [],
+ searchHistory: [],
+ isLoading: false,
+ hasMore: true,
+ page: 1,
+ pageSize: 10,
+ total: 0
},
/**
* 生命周期函数--监听页面加载
*/
onLoad(options) {
-
+ this.loadSearchHistory();
},
/**
@@ -26,7 +34,246 @@ Page({
* 生命周期函数--监听页面显示
*/
onShow() {
+ this.loadSearchHistory();
+ },
+
+ // 从本地存储加载搜索历史
+ loadSearchHistory() {
+ const history = wx.getStorageSync('searchHistory') || [];
+ this.setData({
+ searchHistory: history
+ });
+ },
+
+ // 输入处理
+ onInput(e) {
+ const value = e.detail.value;
+ this.setData({
+ keyword: value
+ });
+
+ // 如果输入为空,隐藏结果
+ if (!value.trim()) {
+ this.setData({
+ showResults: false,
+ searchResults: []
+ });
+ }
+ },
+
+ // 清空输入框
+ clearInput() {
+ this.setData({
+ keyword: '',
+ showResults: false,
+ searchResults: []
+ });
+ },
+
+ // 执行搜索
+ async doSearch() {
+ const { keyword, page } = this.data;
+
+ if (!keyword.trim()) {
+ wx.showToast({
+ title: '请输入搜索关键词',
+ icon: 'none'
+ });
+ return;
+ }
+
+ // 显示加载状态
+ if (page === 1) {
+ this.setData({
+ isLoading: true
+ });
+ }
+
+ try {
+ // 调用云函数搜索
+ const result = await wx.cloud.callFunction({
+ name: 'poemManagement',
+ data: {
+ action: 'searchPoems',
+ keyword: keyword.trim(),
+ page: page,
+ pageSize: this.data.pageSize
+ }
+ });
+
+ if (result.result.success) {
+ const { poems, total } = result.result.data;
+
+ const newResults = page === 1 ? poems : [...this.data.searchResults, ...poems];
+
+ this.setData({
+ searchResults: newResults,
+ showResults: true,
+ total: total,
+ hasMore: (page * this.data.pageSize) < total,
+ isLoading: false
+ });
+
+ // 添加到搜索历史
+ this.addToHistory(keyword.trim());
+
+ // 显示搜索结果提示
+ if (page === 1 && poems.length > 0) {
+ wx.showToast({
+ title: `找到 ${total} 条结果`,
+ icon: 'success',
+ duration: 1500
+ });
+ }
+ } else {
+ wx.showToast({
+ title: result.result.message || '搜索失败',
+ icon: 'none'
+ });
+ this.setData({ isLoading: false });
+
+ // 如果云函数失败,使用模拟数据作为备选
+ this.useMockData(keyword);
+ }
+ } catch (error) {
+ console.error('搜索错误:', error);
+ wx.showToast({
+ title: '搜索失败,请检查网络',
+ icon: 'none'
+ });
+ this.setData({ isLoading: false });
+
+ // 如果云函数调用失败,使用模拟数据作为备选
+ this.useMockData(keyword);
+ }
+ },
+
+ // 使用模拟数据(备选方案)
+ useMockData(keyword) {
+ const mockPoems = [
+ {
+ _id: '1',
+ title: '静夜思',
+ author: '李白',
+ dynasty: '唐代',
+ content: '床前明月光,疑是地上霜。举头望明月,低头思故乡。'
+ },
+ {
+ _id: '2',
+ title: '望庐山瀑布',
+ author: '李白',
+ dynasty: '唐代',
+ content: '日照香炉生紫烟,遥看瀑布挂前川。飞流直下三千尺,疑是银河落九天。'
+ },
+ {
+ _id: '3',
+ title: '早发白帝城',
+ author: '李白',
+ dynasty: '唐代',
+ content: '朝辞白帝彩云间,千里江陵一日还。两岸猿声啼不住,轻舟已过万重山。'
+ },
+ {
+ _id: '4',
+ title: '春晓',
+ author: '孟浩然',
+ dynasty: '唐代',
+ content: '春眠不觉晓,处处闻啼鸟。夜来风雨声,花落知多少。'
+ },
+ {
+ _id: '5',
+ title: '登鹳雀楼',
+ author: '王之涣',
+ dynasty: '唐代',
+ content: '白日依山尽,黄河入海流。欲穷千里目,更上一层楼。'
+ },
+ ];
+
+ // 过滤搜索结果
+ const results = mockPoems.filter(poem =>
+ poem.title.includes(keyword) ||
+ poem.author.includes(keyword) ||
+ poem.content.includes(keyword) ||
+ poem.dynasty.includes(keyword)
+ );
+
+ this.setData({
+ searchResults: results,
+ showResults: true,
+ total: results.length,
+ hasMore: false,
+ isLoading: false
+ });
+
+ // 添加到搜索历史
+ this.addToHistory(keyword);
+ },
+
+ // 通过历史记录搜索
+ searchByHistory(e) {
+ const keyword = e.currentTarget.dataset.keyword;
+ this.setData({
+ keyword: keyword,
+ page: 1,
+ searchResults: [],
+ hasMore: true
+ });
+ this.doSearch();
+ },
+
+ // 添加到搜索历史
+ addToHistory(keyword) {
+ let { searchHistory } = this.data;
+ // 移除已存在的相同关键词
+ searchHistory = searchHistory.filter(item => item !== keyword);
+ // 添加到最前面
+ searchHistory.unshift(keyword);
+ // 限制历史记录数量
+ if (searchHistory.length > 10) {
+ searchHistory = searchHistory.slice(0, 10);
+ }
+ // 保存到本地存储
+ wx.setStorageSync('searchHistory', searchHistory);
+ this.setData({
+ searchHistory: searchHistory
+ });
+ },
+ // 清除搜索历史
+ clearHistory() {
+ wx.showModal({
+ title: '确认清除',
+ content: '确定要清除所有搜索历史吗?',
+ success: (res) => {
+ if (res.confirm) {
+ wx.removeStorageSync('searchHistory');
+ this.setData({
+ searchHistory: []
+ });
+ }
+ }
+ });
+ },
+
+ // 跳转到学习页面
+ goToStudy(e) {
+ const poem = e.currentTarget.dataset.poem;
+ if (poem && poem._id) {
+ wx.navigateTo({
+ url: `/pages/study/study?id=${poem._id}`
+ });
+ } else {
+ const id = e.currentTarget.dataset.id;
+ if (id) {
+ wx.navigateTo({
+ url: `/pages/study/study?id=${id}`
+ });
+ } else {
+ wx.showToast({
+ title: '诗歌信息错误',
+ icon: 'none'
+ });
+ }
+ }
},
/**
@@ -47,14 +294,32 @@ Page({
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh() {
-
+ this.setData({
+ page: 1,
+ searchResults: [],
+ hasMore: true
+ });
+
+ if (this.data.keyword.trim()) {
+ this.doSearch().then(() => {
+ wx.stopPullDownRefresh();
+ });
+ } else {
+ wx.stopPullDownRefresh();
+ }
},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom() {
-
+ if (this.data.hasMore && !this.data.isLoading) {
+ this.setData({
+ page: this.data.page + 1
+ }, () => {
+ this.doSearch();
+ });
+ }
},
/**
diff --git a/pages/search/search.wxml b/pages/search/search.wxml
index 836f6be..c8da72a 100644
--- a/pages/search/search.wxml
+++ b/pages/search/search.wxml
@@ -1,2 +1,98 @@
-pages/search/search.wxml
\ No newline at end of file
+
+
+
+
+
+
+ ✕
+
+
+ 搜索
+
+
+
+
+
+ 搜索中...
+
+
+
+
+
+
+
+ {{item}}
+
+
+
+
+
+
+ 请输入关键词搜索古诗
+ 支持搜索古诗名、作者、诗句内容
+
+
+
+
+
+
+
+
+
+ {{item.title}}
+
+ {{item.author}}
+ {{item.dynasty}}
+
+ {{item.content}}
+
+
+ 学习
+
+
+
+
+
+ 🔍
+ 没有找到相关古诗
+ 换个关键词试试吧
+
+
+
+
+ 点击加载更多
+ 已加载 {{searchResults.length}} / {{total}} 条
+
+
+
+
+ 没有更多数据了
+
+
+
+
\ No newline at end of file
diff --git a/pages/search/search.wxss b/pages/search/search.wxss
index ab78402..b34eb94 100644
--- a/pages/search/search.wxss
+++ b/pages/search/search.wxss
@@ -1 +1,302 @@
-/* pages/search/search.wxss */
\ No newline at end of file
+/* pages/search/search.wxss */
+.search-container {
+ min-height: 100vh;
+ background-color: #f8f9fa;
+ padding: 20rpx;
+}
+
+/* 搜索栏样式 */
+.search-bar {
+ display: flex;
+ align-items: center;
+ gap: 20rpx;
+ margin-bottom: 30rpx;
+}
+
+.search-input-wrapper {
+ flex: 1;
+ position: relative;
+ background: #ffffff;
+ border-radius: 50rpx;
+ padding: 20rpx 30rpx;
+ box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.08);
+}
+
+.search-input {
+ width: 100%;
+ font-size: 28rpx;
+ color: #333;
+}
+
+.clear-btn {
+ position: absolute;
+ right: 30rpx;
+ top: 50%;
+ transform: translateY(-50%);
+ width: 40rpx;
+ height: 40rpx;
+ background: #f0f0f0;
+ border-radius: 50%;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ color: #999;
+ font-size: 24rpx;
+}
+
+.search-btn {
+ background: #07c160;
+ color: white;
+ padding: 20rpx 40rpx;
+ border-radius: 50rpx;
+ font-size: 28rpx;
+ font-weight: 500;
+ white-space: nowrap;
+}
+
+/* 加载状态 */
+.loading-section {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ padding: 80rpx 0;
+}
+
+.loading-spinner {
+ width: 60rpx;
+ height: 60rpx;
+ border: 4rpx solid #f0f0f0;
+ border-top: 4rpx solid #07c160;
+ border-radius: 50%;
+ animation: spin 1s linear infinite;
+ margin-bottom: 20rpx;
+}
+
+@keyframes spin {
+ 0% { transform: rotate(0deg); }
+ 100% { transform: rotate(360deg); }
+}
+
+.loading-text {
+ font-size: 28rpx;
+ color: #666;
+}
+
+/* 搜索历史 */
+.history-section {
+ background: white;
+ border-radius: 20rpx;
+ padding: 30rpx;
+ margin-bottom: 20rpx;
+}
+
+.history-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 25rpx;
+}
+
+.history-title {
+ font-size: 30rpx;
+ font-weight: 600;
+ color: #333;
+}
+
+.clear-history {
+ font-size: 26rpx;
+ color: #07c160;
+}
+
+.history-tags {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 20rpx;
+}
+
+.history-tag {
+ background: #f8f9fa;
+ padding: 16rpx 30rpx;
+ border-radius: 30rpx;
+ font-size: 26rpx;
+ color: #333;
+ border: 1rpx solid #e9ecef;
+}
+
+.history-tag:active {
+ background: #e9ecef;
+}
+
+/* 空状态 */
+.empty-section {
+ text-align: center;
+ padding: 120rpx 40rpx;
+}
+
+.empty-text {
+ display: block;
+ font-size: 30rpx;
+ color: #666;
+ margin-bottom: 15rpx;
+}
+
+.empty-subtext {
+ display: block;
+ font-size: 26rpx;
+ color: #999;
+}
+
+/* 搜索结果 */
+.results-section {
+ background: white;
+ border-radius: 20rpx;
+ overflow: hidden;
+}
+
+.results-header {
+ padding: 30rpx;
+ border-bottom: 1rpx solid #f0f0f0;
+}
+
+.results-title {
+ font-size: 30rpx;
+ font-weight: 600;
+ color: #333;
+ display: block;
+}
+
+.results-list {
+ padding: 0;
+}
+
+.result-item {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ padding: 30rpx;
+ border-bottom: 1rpx solid #f8f9fa;
+ transition: background-color 0.2s;
+}
+
+.result-item:active {
+ background-color: #f8f9fa;
+}
+
+.result-item:last-child {
+ border-bottom: none;
+}
+
+.poem-info {
+ flex: 1;
+}
+
+.poem-title {
+ font-size: 32rpx;
+ font-weight: 600;
+ color: #333;
+ margin-bottom: 12rpx;
+}
+
+.poem-meta {
+ display: flex;
+ align-items: center;
+ gap: 20rpx;
+ margin-bottom: 15rpx;
+}
+
+.poem-author {
+ font-size: 26rpx;
+ color: #07c160;
+ font-weight: 500;
+}
+
+.poem-dynasty {
+ font-size: 24rpx;
+ color: #999;
+ background: #f8f9fa;
+ padding: 4rpx 12rpx;
+ border-radius: 12rpx;
+}
+
+.poem-content {
+ font-size: 26rpx;
+ color: #666;
+ line-height: 1.5;
+ display: -webkit-box;
+ -webkit-line-clamp: 2;
+ -webkit-box-orient: vertical;
+ overflow: hidden;
+}
+
+.study-btn {
+ background: #07c160;
+ color: white;
+ padding: 15rpx 25rpx;
+ border-radius: 20rpx;
+ font-size: 24rpx;
+ font-weight: 500;
+ white-space: nowrap;
+ margin-left: 20rpx;
+}
+
+/* 空结果状态 */
+.empty-results {
+ text-align: center;
+ padding: 80rpx 40rpx;
+}
+
+.empty-icon {
+ font-size: 80rpx;
+ display: block;
+ margin-bottom: 20rpx;
+}
+
+.empty-text {
+ display: block;
+ font-size: 30rpx;
+ color: #666;
+ margin-bottom: 10rpx;
+}
+
+.empty-subtext {
+ display: block;
+ font-size: 26rpx;
+ color: #999;
+}
+
+/* 加载更多 */
+.load-more {
+ text-align: center;
+ padding: 40rpx;
+ background: #f8f9fa;
+ margin: 20rpx;
+ border-radius: 15rpx;
+}
+
+.load-more:active {
+ background: #e9ecef;
+}
+
+.load-more-text {
+ display: block;
+ font-size: 28rpx;
+ color: #07c160;
+ margin-bottom: 10rpx;
+}
+
+.load-more-tips {
+ display: block;
+ font-size: 24rpx;
+ color: #999;
+}
+
+/* 没有更多数据 */
+.no-more {
+ text-align: center;
+ padding: 30rpx;
+ color: #999;
+ font-size: 26rpx;
+ background: #f8f9fa;
+ margin: 20rpx;
+ border-radius: 15rpx;
+}
\ No newline at end of file
diff --git a/pages/study/study.js b/pages/study/study.js
index fe324fa..6c575e8 100644
--- a/pages/study/study.js
+++ b/pages/study/study.js
@@ -1,66 +1,418 @@
-// pages/all.js
+// pages/study/study.js
+const innerAudioContext = wx.createInnerAudioContext()
Page({
-
- /**
- * 页面的初始数据
- */
data: {
-
+ poemTitle: '加载中...',
+ poetInfo: '',
+ originalText: '',
+ translationText: '',
+ backgroundInfo: '',
+ showTranslation: false,
+ showBackground: false,
+ poem: null,
+ isLoading: true,
+ authorInfo: '',
+ loadError: false,
+ poemType: '',
+ token: ''
},
- /**
- * 生命周期函数--监听页面加载
- */
onLoad(options) {
+ console.log('学习页面参数:', options);
+ this.options = options;
+
+ // 初始化语音上下文事件监听
+ this.initAudioEvents();
+
+ // 获取token
+ this.getToken();
+
+ if (options.poemData) {
+ // 从首页传递的完整诗歌数据
+ try {
+ const poemData = decodeURIComponent(options.poemData);
+ const poem = JSON.parse(poemData);
+ console.log('解析后的诗歌数据:', poem);
+ this.processPoemData(poem);
+ } catch (error) {
+ console.error('解析诗歌数据失败:', error);
+ this.showError('数据解析失败');
+ }
+ } else if (options.id) {
+ // 根据ID从数据库加载诗词数据
+ console.log('通过ID加载诗歌:', options.id);
+ this.loadPoemFromDatabase(options.id);
+ } else {
+ this.showError('缺少诗歌信息');
+ }
+ },
+
+ // 初始化音频事件监听
+ initAudioEvents() {
+ innerAudioContext.onPlay(() => {
+ console.log('开始播放语音');
+ });
+
+ innerAudioContext.onStop(() => {
+ console.log('停止播放语音');
+ });
+
+ innerAudioContext.onEnded(() => {
+ console.log('语音播放结束');
+ });
+
+ innerAudioContext.onError((res) => {
+ console.error('语音播放错误:', res);
+ wx.showToast({
+ title: '语音播放失败',
+ icon: 'none'
+ });
+ });
+ },
+
+ // 获取token(复用recite.js中的逻辑)
+ getToken() {
+ let that = this;
+ wx.getStorage({
+ key: 'expires_in',
+ success(res) {
+ console.log("缓存中有access_token");
+ console.log("token失效时间:", res.data);
+ const newT = new Date().getTime();
+ // 用当前时间和存储的时间判断,token是否已过期
+ if (newT > parseInt(res.data)) {
+ console.log("token过期,重新获取token");
+ that.fetchNewToken();
+ } else {
+ console.log("获取本地缓存的token");
+ that.setData({
+ token: wx.getStorageSync('access_token')
+ });
+ }
+ }, fail() {
+ console.log("缓存中没有access_token,直接获取新token");
+ // 尝试从storage直接获取token
+ const token = wx.getStorageSync('access_token');
+ if (token) {
+ that.setData({ token: token });
+ } else {
+ // 直接获取新token,不再依赖背诵功能初始化
+ that.fetchNewToken();
+ }
+ }
+ });
+ },
+
+ // 获取新token
+ fetchNewToken() {
+ let that = this;
+ let ApiKey = 'qdHcePu7v0WpIlJnaeGJZZqp';
+ let SecretKey = 'nuZCuBziZoO2gjE6NGEMKCdxKs4sbaNq';
+ const url = 'https://aip.baidubce.com/oauth/2.0/token?grant_type=client_credentials&client_id=' + ApiKey + '&client_secret=' + SecretKey;
+ wx.request({
+ url: url,
+ method: 'POST',
+ success(res) {
+ console.log("创建access_token成功", res);
+ // 将access_token存储到storage中
+ wx.setStorage({
+ key: 'access_token',
+ data: res.data.access_token
+ });
+ var date = new Date().getTime();
+ let time = date + 2592000 * 1000;
+ console.log('三十天后的时间', time);
+ wx.setStorage({
+ key: 'expires_in',
+ data: time
+ });
+ that.setData({
+ token: res.data.access_token
+ });
+ },
+ fail(err) {
+ console.error('获取token失败:', err);
+ wx.showToast({
+ title: '语音服务初始化失败',
+ icon: 'none'
+ });
+ }
+ });
+ },
+
+ // 语音合成播放函数
+ playstart(text) {
+ let that = this;
+ if (!that.data.token) {
+ wx.showToast({
+ title: '语音服务未初始化',
+ icon: 'none'
+ });
+ return;
+ }
+
+ console.log("开始语音合成", text);
+ // 对文本进行URL编码
+ const encodedText = encodeURIComponent(text);
+
+ innerAudioContext.src = 'https://tsn.baidu.com/text2audio?lan=zh&ctp=1&cuid=12_56&tok=' + that.data.token + '&tex=' + encodedText + '&vol=10&per=111&spd=5&pit=5&aue=3';
+ innerAudioContext.play();
+
+ wx.showToast({
+ title: '开始朗读',
+ icon: 'success',
+ duration: 1000
+ });
+ },
+ // 从数据库加载诗歌数据
+ async loadPoemFromDatabase(id) {
+ try {
+ console.log('开始从数据库加载诗歌,ID:', id);
+
+ wx.showLoading({
+ title: '加载中...',
+ });
+
+ const db = wx.cloud.database();
+ const result = await db.collection('poeties').doc(id).get();
+
+ wx.hideLoading();
+
+ console.log('数据库查询结果:', result);
+
+ if (result.data) {
+ this.processPoemData(result.data);
+ } else {
+ this.showError('诗歌不存在');
+ }
+ } catch (error) {
+ wx.hideLoading();
+ console.error('数据库查询失败:', error);
+
+ // 如果直接查询失败,尝试使用云函数
+ this.loadPoemFromCloudFunction(id);
+ }
},
- /**
- * 生命周期函数--监听页面初次渲染完成
- */
- onReady() {
+ // 云函数加载(备用方案)
+ async loadPoemFromCloudFunction(id) {
+ try {
+ console.log('尝试通过云函数加载诗歌,ID:', id);
+
+ const result = await wx.cloud.callFunction({
+ name: 'getPoemDetail',
+ data: { id: id }
+ });
- },
+ console.log('云函数返回结果:', result);
- /**
- * 生命周期函数--监听页面显示
- */
- onShow() {
+ if (result.result && result.result.success) {
+ const poem = result.result.data;
+ this.processPoemData(poem);
+ } else {
+ this.showError(result.result?.message || '诗歌不存在');
+ }
+ } catch (error) {
+ console.error('调用云函数失败:', error);
+
+ }
},
- /**
- * 生命周期函数--监听页面隐藏
- */
- onHide() {
+
+ // 处理诗歌数据
+ processPoemData(poem) {
+ console.log('处理诗歌数据:', poem);
+
+ // 设置页面数据
+ this.setData({
+ poem: poem,
+ poemTitle: poem.title || '未知标题',
+ poetInfo: this.formatPoetInfo(poem),
+ originalText: poem.content || '暂无内容',
+ translationText: poem.translation || '暂无译文',
+ backgroundInfo: poem.background || '暂无背景信息',
+ authorInfo: poem.author_info ? poem.author_info.intro : (poem.author ? `暂无${poem.author}的详细信息` : '暂无作者信息'),
+ poemType: poem.type || '诗',
+ isLoading: false,
+ loadError: false
+ });
+
+ // 设置导航栏标题
+ wx.setNavigationBarTitle({
+ title: poem.title || '古诗学习'
+ });
},
- /**
- * 生命周期函数--监听页面卸载
- */
- onUnload() {
+ // 格式化作者信息
+ formatPoetInfo(poem) {
+ let info = '';
+ if (poem.dynasty) {
+ info += poem.dynasty;
+ }
+ if (poem.author) {
+ info += (info ? ' • ' : '') + poem.author;
+ }
+ return info || '未知信息';
+ },
+ // 切换译文显示状态
+ toggleTranslation() {
+ this.setData({
+ showTranslation: !this.data.showTranslation
+ });
+ },
+
+ // 切换背景信息显示状态
+ toggleBackground() {
+ this.setData({
+ showBackground: !this.data.showBackground
+ });
},
- /**
- * 页面相关事件处理函数--监听用户下拉动作
- */
- onPullDownRefresh() {
+ // 显示错误信息
+ showError(message) {
+ this.setData({
+ isLoading: false,
+ loadError: true,
+ poemTitle: '加载失败',
+ originalText: message
+ });
+
+ wx.showToast({
+ title: message,
+ icon: 'none',
+ duration: 2000
+ });
+ },
+ // 重新加载
+ reloadData() {
+ const options = this.options || {};
+ if (options.id) {
+ this.setData({ isLoading: true, loadError: false });
+ this.loadPoemFromDatabase(options.id);
+ } else {
+ wx.navigateBack();
+ }
},
- /**
- * 页面上拉触底事件的处理函数
- */
- onReachBottom() {
+ // 复制诗歌内容
+ copyContent() {
+ const content = this.data.originalText;
+ wx.setClipboardData({
+ data: content,
+ success: () => {
+ wx.showToast({
+ title: '已复制到剪贴板',
+ icon: 'success'
+ });
+ }
+ });
+ },
+ // 语音播报功能(支持不同类型内容的朗读)
+ speakPoem(e) {
+ // 获取要朗读的内容类型
+ const contentType = e?.currentTarget?.dataset?.type || 'all';
+ let textToSpeak = '';
+ let title = '';
+
+ switch(contentType) {
+ case 'content':
+ textToSpeak = this.data.originalText;
+ title = '朗读原文';
+ break;
+ case 'translation':
+ textToSpeak = this.data.translationText;
+ title = '朗读译文';
+ break;
+ case 'background':
+ textToSpeak = this.data.backgroundInfo;
+ title = '朗读背景';
+ break;
+ case 'author':
+ textToSpeak = this.data.authorInfo;
+ title = '朗读作者简介';
+ break;
+ default:
+ // 默认朗读全部内容(包括题目、作者、原文、译文、背景、作者简介)
+ textToSpeak = `${this.data.poemTitle},${this.data.poetInfo},${this.data.originalText},${this.data.translationText},${this.data.backgroundInfo},${this.data.authorInfo}`;
+ title = '语音播报';
+ }
+
+ console.log(`准备朗读${contentType}内容:`, textToSpeak);
+
+ // 调用语音合成播放函数
+ this.playstart(textToSpeak);
},
- /**
- * 用户点击右上角分享
- */
+ // 分享功能
onShareAppMessage() {
+ const poem = this.data.poem;
+ if (poem && poem._id) {
+ return {
+ title: `${poem.title} - ${poem.author}`,
+ path: `/pages/study/study?id=${poem._id}`
+ };
+ }
+ return {
+ title: `${this.data.poemTitle}`,
+ path: '/pages/study/study'
+ };
+ },
+ onReady() {
+ // 可以在这里添加动画效果等
+ },
+
+ onShow() {
+ // 页面显示时的逻辑
+ },
+
+ onHide() {
+
+ },
+
+ onUnload() {
+
+ },
+
+ onPullDownRefresh() {
+ const options = this.options || {};
+ if (options.poemData) {
+ // 如果是传递完整数据,不需要重新加载
+ wx.stopPullDownRefresh();
+ } else if (options.id) {
+ this.loadPoemFromDatabase(options.id).then(() => {
+ wx.stopPullDownRefresh();
+ });
+ } else {
+ wx.stopPullDownRefresh();
+ }
+ },
+
+ onReachBottom() {
+ // 可以在这里添加加载更多相关内容
+ },
+
+ // 跳转到背诵页面
+ goToRecite() {
+ const poem = this.data.poem;
+ if (poem && poem._id) {
+ console.log('跳转到背诵页面,诗歌ID:', poem._id);
+ // 将诗歌数据编码后传递给背诵页面
+ const poemData = encodeURIComponent(JSON.stringify(poem));
+ wx.navigateTo({
+ url: `/pages/recite/recite?id=${poem._id}&poemData=${poemData}`
+ });
+ } else {
+ wx.showToast({
+ title: '背诵功能暂不可用',
+ icon: 'none'
+ });
+ }
}
})
\ No newline at end of file
diff --git a/pages/study/study.wxml b/pages/study/study.wxml
index 9a8ac45..428fa23 100644
--- a/pages/study/study.wxml
+++ b/pages/study/study.wxml
@@ -1,2 +1,124 @@
-
-pages/all.wxml
\ No newline at end of file
+
+
+
+
+
+
+ 诗歌加载中...
+
+
+
+
+
+
+
+ 加载失败
+ {{originalText}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{originalText}}
+
+
+
+
+
+
+
+ {{translationText}}
+
+
+
+
+
+
+
+ {{backgroundInfo}}
+
+
+
+
+
+
+
+ {{authorInfo}}
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/pages/study/study.wxss b/pages/study/study.wxss
index 2f7da79..0466732 100644
--- a/pages/study/study.wxss
+++ b/pages/study/study.wxss
@@ -1 +1,350 @@
-/* pages/all.wxss */
\ No newline at end of file
+/* pages/study/study.wxss */
+
+/* 页面容器 */
+.study-container {
+ min-height: 100vh;
+ background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
+ padding: 20rpx;
+ box-sizing: border-box;
+}
+
+/* 加载状态 */
+.loading-state {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ height: 70vh;
+}
+
+.loading-content {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+}
+
+.loading-icon {
+ width: 120rpx;
+ height: 120rpx;
+ margin-bottom: 30rpx;
+}
+
+.loading-text {
+ font-size: 32rpx;
+ color: #666;
+}
+
+/* 错误状态 */
+.error-state {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ height: 70vh;
+}
+
+.error-content {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ text-align: center;
+ padding: 0 60rpx;
+}
+
+.error-icon {
+ width: 150rpx;
+ height: 150rpx;
+ margin-bottom: 30rpx;
+ opacity: 0.7;
+}
+
+.error-title {
+ font-size: 36rpx;
+ font-weight: bold;
+ color: #ff6b6b;
+ margin-bottom: 20rpx;
+}
+
+.error-desc {
+ font-size: 28rpx;
+ color: #999;
+ margin-bottom: 50rpx;
+ line-height: 1.6;
+}
+
+.btn-retry {
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+ color: white;
+ border: none;
+ border-radius: 50rpx;
+ padding: 20rpx 60rpx;
+ font-size: 30rpx;
+ box-shadow: 0 4rpx 15rpx rgba(102, 126, 234, 0.4);
+}
+
+/* 诗歌详情样式 */
+.poem-detail {
+ padding-bottom: 40rpx;
+}
+
+/* 卡片通用样式 */
+.header-card,
+.content-card {
+ background: white;
+ border-radius: 20rpx;
+ box-shadow: 0 8rpx 30rpx rgba(0, 0, 0, 0.08);
+ margin-bottom: 30rpx;
+ overflow: hidden;
+ transition: all 0.3s ease;
+}
+
+.content-card:active {
+ transform: translateY(-4rpx);
+ box-shadow: 0 12rpx 40rpx rgba(0, 0, 0, 0.12);
+}
+
+/* 头部卡片 */
+.header-card {
+ padding: 40rpx 30rpx;
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+ color: white;
+ text-align: center;
+}
+
+/* 操作按钮组 */
+.action-buttons {
+ display: flex;
+ justify-content: flex-end;
+ margin-top: 20rpx;
+ gap: 20rpx;
+}
+
+.btn-icon {
+ font-size: 28rpx;
+ margin-right: 8rpx;
+}
+
+.header-actions {
+ display: flex;
+ gap: 10rpx;
+}
+
+/* 文本区域语音播报按钮样式 */
+.btn-speak-text {
+ margin-top: 20rpx;
+ font-size: 28rpx;
+ padding: 0 20rpx;
+ align-self: flex-start;
+}
+
+/* 小喇叭图标按钮样式 */
+.btn-speak-icon {
+ margin-left: 8px;
+ padding: 0;
+ width: 30px;
+ height: 30px;
+ line-height: 30px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ min-width: 30px;
+}
+
+.poem-title {
+ display: block;
+ font-size: 48rpx;
+ font-weight: bold;
+ margin-bottom: 20rpx;
+ text-shadow: 0 2rpx 4rpx rgba(0, 0, 0, 0.1);
+}
+
+.poem-meta {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ flex-wrap: wrap;
+}
+
+.poet-info {
+ font-size: 30rpx;
+ opacity: 0.9;
+ margin-right: 20rpx;
+}
+
+.poem-type-tag {
+ background: rgba(255, 255, 255, 0.2);
+ padding: 8rpx 20rpx;
+ border-radius: 30rpx;
+ font-size: 24rpx;
+ backdrop-filter: blur(10rpx);
+}
+
+/* 内容卡片内部样式 */
+.content-card {
+ padding: 30rpx;
+}
+
+.card-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 25rpx;
+}
+
+.section-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 0;
+}
+
+.section-title-wrapper {
+ display: flex;
+ align-items: center;
+}
+
+.section-icon {
+ width: 36rpx;
+ height: 36rpx;
+ margin-right: 15rpx;
+}
+
+.card-title {
+ font-size: 34rpx;
+ font-weight: bold;
+ color: #333;
+}
+
+.toggle-wrapper {
+ display: flex;
+ align-items: center;
+}
+
+.toggle-text {
+ font-size: 26rpx;
+ color: #666;
+ margin-right: 10rpx;
+}
+
+.toggle-icon {
+ font-size: 24rpx;
+ color: #999;
+}
+
+/* 复制按钮 */
+.btn-copy {
+ display: flex;
+ align-items: center;
+ background: #f8f9fa;
+ border: 1rpx solid #e9ecef;
+ border-radius: 30rpx;
+ padding: 12rpx 24rpx;
+ font-size: 24rpx;
+ color: #666;
+}
+
+.btn-speak {
+ font-size: 28rpx;
+ padding: 0 20rpx;
+}
+
+.copy-icon {
+ width: 24rpx;
+ height: 24rpx;
+ margin-right: 8rpx;
+}
+
+/* 诗歌内容 */
+.original-text {
+ padding: 10rpx 0;
+}
+
+.poem-content {
+ font-size: 36rpx;
+ line-height: 1.8;
+ color: #2c3e50;
+ text-align: center;
+ white-space: pre-line;
+}
+
+/* 译文内容 */
+.translation-text {
+ padding-top: 25rpx;
+ border-top: 1rpx solid #f1f3f4;
+}
+
+.translation-content {
+ font-size: 30rpx;
+ line-height: 1.7;
+ color: #555;
+ white-space: pre-line;
+}
+
+/* 背景信息 */
+.background-text {
+ padding-top: 25rpx;
+ border-top: 1rpx solid #f1f3f4;
+}
+
+.background-content {
+ font-size: 30rpx;
+ line-height: 1.7;
+ color: #555;
+ white-space: pre-line;
+}
+
+/* 作者信息 */
+.author-text {
+ padding-top: 10rpx;
+}
+
+.author-content {
+ font-size: 30rpx;
+ line-height: 1.7;
+ color: #555;
+ white-space: pre-line;
+ margin-bottom: 16px; /* 恢复正常边距 */
+}
+
+/* 底部背诵按钮容器 */
+.bottom-container {
+ margin: 20px;
+ margin-bottom: 30px;
+}
+
+/* 背诵按钮样式 */
+.recite-button {
+ width: 100%;
+ height: 50px;
+ background-color: #07c160;
+ color: #fff;
+ border-radius: 25px;
+ font-size: 18px;
+ font-weight: 600;
+ line-height: 50px;
+ padding: 0;
+ box-shadow: 0 4px 12px rgba(7, 193, 96, 0.3);
+}
+
+.recite-button:active {
+ background-color: #06ad56;
+ box-shadow: 0 2px 6px rgba(7, 193, 96, 0.4);
+}
+
+/* 响应式调整 */
+@media (max-width: 480px) {
+ .study-container {
+ padding: 15rpx;
+ }
+
+ .header-card,
+ .content-card {
+ border-radius: 15rpx;
+ }
+
+ .poem-title {
+ font-size: 42rpx;
+ }
+
+ .poem-content {
+ font-size: 32rpx;
+ }
+}
\ No newline at end of file
diff --git a/pages/textbookFilter/textbookFilter.js b/pages/textbookFilter/textbookFilter.js
deleted file mode 100644
index 27d8f6d..0000000
--- a/pages/textbookFilter/textbookFilter.js
+++ /dev/null
@@ -1,66 +0,0 @@
-// pages/textbookFilter/textbookFilter.js
-Page({
-
- /**
- * 页面的初始数据
- */
- data: {
-
- },
-
- /**
- * 生命周期函数--监听页面加载
- */
- onLoad(options) {
-
- },
-
- /**
- * 生命周期函数--监听页面初次渲染完成
- */
- onReady() {
-
- },
-
- /**
- * 生命周期函数--监听页面显示
- */
- onShow() {
-
- },
-
- /**
- * 生命周期函数--监听页面隐藏
- */
- onHide() {
-
- },
-
- /**
- * 生命周期函数--监听页面卸载
- */
- onUnload() {
-
- },
-
- /**
- * 页面相关事件处理函数--监听用户下拉动作
- */
- onPullDownRefresh() {
-
- },
-
- /**
- * 页面上拉触底事件的处理函数
- */
- onReachBottom() {
-
- },
-
- /**
- * 用户点击右上角分享
- */
- onShareAppMessage() {
-
- }
-})
\ No newline at end of file
diff --git a/pages/textbookFilter/textbookFilter.json b/pages/textbookFilter/textbookFilter.json
deleted file mode 100644
index 8835af0..0000000
--- a/pages/textbookFilter/textbookFilter.json
+++ /dev/null
@@ -1,3 +0,0 @@
-{
- "usingComponents": {}
-}
\ No newline at end of file
diff --git a/pages/textbookFilter/textbookFilter.wxml b/pages/textbookFilter/textbookFilter.wxml
deleted file mode 100644
index 79f5a3d..0000000
--- a/pages/textbookFilter/textbookFilter.wxml
+++ /dev/null
@@ -1,2 +0,0 @@
-
-pages/textbookFilter/textbookFilter.wxml
\ No newline at end of file
diff --git a/pages/textbookFilter/textbookFilter.wxss b/pages/textbookFilter/textbookFilter.wxss
deleted file mode 100644
index 0a28b1c..0000000
--- a/pages/textbookFilter/textbookFilter.wxss
+++ /dev/null
@@ -1 +0,0 @@
-/* pages/textbookfilter/textbookfilter.wxss */
\ No newline at end of file
diff --git a/project.config.json b/project.config.json
index 6ddbc3b..e0a5224 100644
--- a/project.config.json
+++ b/project.config.json
@@ -5,6 +5,7 @@
"minified": true,
"uglifyFileName": false,
"enhance": true,
+ "packNpmManually": false,
"packNpmRelationList": [],
"babelSetting": {
"ignore": [],
@@ -12,7 +13,15 @@
"outputPath": ""
},
"useCompilerPlugins": false,
- "minifyWXML": true
+ "minifyWXML": true,
+ "compileWorklet": false,
+ "uploadWithSourceMap": true,
+ "minifyWXSS": true,
+ "localPlugins": false,
+ "disableUseStrict": false,
+ "condition": false,
+ "swc": false,
+ "disableSWC": true
},
"compileType": "miniprogram",
"simulatorPluginLibVersion": {},
@@ -21,5 +30,8 @@
"include": []
},
"appid": "wx60a2f42279236d44",
- "editorSetting": {}
+ "editorSetting": {},
+ "libVersion": "3.10.2",
+ "cloudfunctionRoot": "cloudfunctions/",
+ "cloudfunctionTemplateRoot": "cloudfunctionTemplate/"
}
\ No newline at end of file
diff --git a/project.private.config.json b/project.private.config.json
index 75f724c..495197f 100644
--- a/project.private.config.json
+++ b/project.private.config.json
@@ -1,48 +1,42 @@
{
- "libVersion": "3.10.2",
- "projectname": "miniprogram-1",
- "setting": {
- "urlCheck": false,
- "coverView": true,
- "lazyloadPlaceholderEnable": false,
- "skylineRenderEnable": false,
- "preloadBackgroundData": false,
- "autoAudits": false,
- "showShadowRootInWxmlPanel": true,
- "compileHotReLoad": true
- },
- "condition": {
- "miniprogram": {
- "list": [
- {
- "name": "pages/login/login",
- "pathName": "pages/login/login",
- "query": "",
- "scene": null,
- "launchMode": "default"
- },
- {
- "name": "pages/guiding/guiding",
- "pathName": "pages/guiding/guiding",
- "query": "",
- "launchMode": "default",
- "scene": null
- },
- {
- "name": "pages/search/search",
- "pathName": "pages/search/search",
- "query": "",
- "launchMode": "default",
- "scene": null
- },
- {
- "name": "pages/home/home",
- "pathName": "pages/home/home",
- "query": "",
- "launchMode": "default",
- "scene": null
+ "libVersion": "3.10.2",
+ "projectname": "miniprogram-2",
+ "setting": {
+ "urlCheck": false,
+ "coverView": true,
+ "lazyloadPlaceholderEnable": false,
+ "skylineRenderEnable": false,
+ "preloadBackgroundData": false,
+ "autoAudits": false,
+ "showShadowRootInWxmlPanel": true,
+ "compileHotReLoad": true,
+ "useApiHook": true,
+ "useApiHostProcess": true,
+ "useStaticServer": false,
+ "useLanDebug": false,
+ "showES6CompileOption": false,
+ "checkInvalidKey": true,
+ "ignoreDevUnusedFiles": true,
+ "bigPackageSizeSupport": false
+ },
+ "condition": {
+ "miniprogram": {
+ "list": [
+ {
+ "name": "pages/question/question",
+ "pathName": "pages/question/question",
+ "query": "",
+ "scene": null,
+ "launchMode": "default"
+ },
+ {
+ "name": "pages/managePoems/managePoems",
+ "pathName": "pages/managePoems/managePoems",
+ "query": "",
+ "launchMode": "default",
+ "scene": null
+ }
+ ]
}
- ]
}
- }
}
\ No newline at end of file