diff --git a/app.json b/app.json
index cc97cac..d37606f 100644
--- a/app.json
+++ b/app.json
@@ -8,7 +8,6 @@
"pages/search/search",
"pages/recite/recite",
"pages/review/review",
- "pages/pendingQuestion/pendingQuestion",
"pages/keyWord/keyWord",
"pages/question/question",
"pages/managePoems/managePoems"
diff --git a/cloudfunctions/answerquestion/config.json b/cloudfunctions/answerquestion/config.json
deleted file mode 100644
index 0bf75a8..0000000
--- a/cloudfunctions/answerquestion/config.json
+++ /dev/null
@@ -1,5 +0,0 @@
-{
- "permissions": {
- "openapi": []
- }
-}
\ No newline at end of file
diff --git a/cloudfunctions/answerquestion/index.js b/cloudfunctions/answerquestion/index.js
deleted file mode 100644
index 9c6a694..0000000
--- a/cloudfunctions/answerquestion/index.js
+++ /dev/null
@@ -1,133 +0,0 @@
-// cloudfunctions/answerquestion/index.js
-const cloud = require('wx-server-sdk');
-
-cloud.init({env: cloud.DYNAMIC_CURRENT_ENV});
-
-const db = cloud.database();
-const _ = db.command;
-
-exports.main = async function(event, context) {
- console.log('=== answerquestion 云函数开始执行 ===');
- console.log('收到参数:', JSON.stringify(event, null, 2));
-
- try {
- // 参数验证
- if (!event.questionId) {
- throw new Error('缺少问题ID(questionId)');
- }
-
- if (!event.answers || !Array.isArray(event.answers) || event.answers.length === 0) {
- throw new Error('答案内容不能为空');
- }
-
- const { questionId, answers, keywords = [], question, poemId = '', title = '' } = event;
-
- console.log('开始处理问题:', questionId);
-
- // 1. 验证问题是否存在
- let questionDoc;
- try {
- questionDoc = await db.collection('Question').doc(questionId).get();
- console.log('查询到的问题:', questionDoc.data);
-
- if (!questionDoc.data) {
- throw new Error(`问题不存在,ID: ${questionId}`);
- }
-
- // 检查问题是否已被回答
- if (questionDoc.data.status === 1) {
- throw new Error('该问题已被回答,无法重复提交');
- }
-
- } catch (dbError) {
- console.error('查询问题失败:', dbError);
- throw new Error(`查询问题失败: ${dbError.message}`);
- }
-
- // 2. 创建答案记录
- console.log('开始创建答案记录...');
- const answerData = {
- question: question || questionDoc.data.question,
- answers: answers,
- keywords: Array.isArray(keywords) ? keywords : [],
- poemId: poemId,
- title: title,
- status: 1,
- createTime: db.serverDate()
- };
-
- console.log('答案数据:', answerData);
-
- let answerResult;
- try {
- answerResult = await db.collection('Answer').add({
- data: answerData
- });
- console.log('答案创建成功,ID:', answerResult._id);
- } catch (addError) {
- console.error('创建答案失败:', addError);
- throw new Error(`创建答案记录失败: ${addError.message}`);
- }
-
- // 3. 更新问题状态
- console.log('开始更新问题状态...');
- const updateData = {
- status: 1,
- answerId: answerResult._id,
- answerTime: db.serverDate(),
- updateTime: db.serverDate()
- };
-
- console.log('更新数据:', updateData);
-
- let updateResult;
- try {
- updateResult = await db.collection('Question')
- .doc(questionId)
- .update({
- data: updateData
- });
- console.log('问题状态更新成功:', updateResult);
- } catch (updateError) {
- console.error('更新问题状态失败:', updateError);
-
- // 如果更新问题失败,尝试删除已创建的答案记录
- try {
- await db.collection('Answer').doc(answerResult._id).remove();
- console.log('已回滚删除答案记录');
- } catch (rollbackError) {
- console.error('回滚失败:', rollbackError);
- }
-
- throw new Error(`更新问题状态失败: ${updateError.message}`);
- }
-
- // 返回成功结果
- const successResult = {
- success: true,
- message: '答案提交成功',
- answerId: answerResult._id,
- data: {
- answer: answerResult,
- question: updateResult
- }
- };
-
- console.log('云函数执行成功,返回:', successResult);
- return successResult;
-
- } catch (error) {
- console.error('=== 云函数执行失败 ===');
- console.error('错误信息:', error.message);
- console.error('错误堆栈:', error.stack);
-
- const errorResult = {
- success: false,
- message: error.message || '答案提交失败',
- error: error.stack
- };
-
- console.log('返回错误结果:', errorResult);
- return errorResult;
- }
-};
\ No newline at end of file
diff --git a/cloudfunctions/answerquestion/package.json b/cloudfunctions/answerquestion/package.json
deleted file mode 100644
index 40649fd..0000000
--- a/cloudfunctions/answerquestion/package.json
+++ /dev/null
@@ -1,9 +0,0 @@
-{
- "name": "answerquestion",
- "version": "1.0.0",
- "description": "管理员回答问题",
- "main": "index.js",
- "dependencies": {
- "wx-server-sdk": "~2.6.3"
- }
-}
\ No newline at end of file
diff --git a/cloudfunctions/getAnsweredQuestions/config.json b/cloudfunctions/getAnsweredQuestions/config.json
deleted file mode 100644
index 5ecc33e..0000000
--- a/cloudfunctions/getAnsweredQuestions/config.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
- "permissions": {
- "openapi": [
- ]
- }
-}
\ No newline at end of file
diff --git a/cloudfunctions/getAnsweredQuestions/index.js b/cloudfunctions/getAnsweredQuestions/index.js
deleted file mode 100644
index 28c1817..0000000
--- a/cloudfunctions/getAnsweredQuestions/index.js
+++ /dev/null
@@ -1,96 +0,0 @@
-// 云函数 getAnsweredQuestions
-const cloud = require('wx-server-sdk')
-cloud.init()
-
-const db = cloud.database()
-
-exports.main = async (event, context) => {
- const wxContext = cloud.getWXContext()
- const openid = wxContext.OPENID
-
- try {
- // 查询该用户提出的所有问题
- const questionsResult = await db.collection('Question')
- .where({
- _openid: openid
- })
- .orderBy('createTime', 'desc') // 按提问时间倒序排列
- .get()
-
- // 获取问题列表
- const questions = questionsResult.data
-
- // 对于每个问题,尝试获取对应的回答(如果有)
- const questionsWithAnswers = await Promise.all(questions.map(async (question) => {
- try {
- // 查询对应问题的回答(通过问题内容匹配)
- const answerResult = await db.collection('Answer')
- .where({
- question: question.question, // 通过问题内容精确匹配
- _openid: openid // 确保是该用户的问题的回答
- })
- .get()
-
- // 获取回答内容(如果有)
- const answerData = answerResult.data[0] || null
-
- // 正确获取answers数组内容
- let answerContent = null;
- if (answerData) {
- // 检查answers是否存在且为数组
- if (Array.isArray(answerData.answers) && answerData.answers.length > 0) {
- // 使用数组中的第一个元素作为答案
- answerContent = answerData.answers[0];
- console.log(`找到问题答案: ${question.question.substring(0, 20)}...`);
- } else if (answerData.answer) {
- // 兼容可能直接存储在answer字段的情况
- answerContent = answerData.answer;
- console.log(`从answer字段获取答案`);
- }
- }
-
- return {
- ...question,
- answer: answerContent, // 返回正确获取的答案内容
- answerData: answerData // 返回完整的回答数据
- }
- } catch (error) {
- console.error(`获取问题回答失败:`, error)
- return {
- ...question,
- answer: null,
- answerData: null
- }
- }
- }))
-
- // 按状态排序:已回答(status=1)的排在前面,待处理(status=0)的排在后面,然后按创建时间倒序
- const sortedQuestions = questionsWithAnswers.sort((a, b) => {
- // 将状态标准化为数字:已回答=1,待处理=0
- const statusA = a.status === 1 ? 1 : 0;
- const statusB = b.status === 1 ? 1 : 0;
-
- // 先按状态排序:已回答的排在前面
- if (statusA !== statusB) {
- return statusB - statusA;
- }
-
- // 状态相同时按创建时间倒序
- return (b.createTime || 0) - (a.createTime || 0);
- });
-
- return {
- code: 200,
- data: sortedQuestions, // 返回所有问题,包括已回答和待处理的
- total: sortedQuestions.length,
- message: '获取成功'
- }
- } catch (error) {
- console.error('获取用户问题列表失败:', error)
- return {
- code: 500,
- data: null,
- message: '获取失败:' + error.message
- }
- }
-}
\ No newline at end of file
diff --git a/cloudfunctions/getAnsweredQuestions/package.json b/cloudfunctions/getAnsweredQuestions/package.json
deleted file mode 100644
index bbbb35c..0000000
--- a/cloudfunctions/getAnsweredQuestions/package.json
+++ /dev/null
@@ -1,14 +0,0 @@
-{
- "name": "getAnsweredQuestions",
- "version": "1.0.0",
- "description": "",
- "main": "index.js",
- "scripts": {
- "test": "echo \"Error: no test specified\" && exit 1"
- },
- "author": "",
- "license": "ISC",
- "dependencies": {
- "wx-server-sdk": "~3.0.1"
- }
-}
\ No newline at end of file
diff --git a/cloudfunctions/getQuestionDetail/config.json b/cloudfunctions/getQuestionDetail/config.json
deleted file mode 100644
index 5ecc33e..0000000
--- a/cloudfunctions/getQuestionDetail/config.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
- "permissions": {
- "openapi": [
- ]
- }
-}
\ No newline at end of file
diff --git a/cloudfunctions/getQuestionDetail/index.js b/cloudfunctions/getQuestionDetail/index.js
deleted file mode 100644
index 89ca3e1..0000000
--- a/cloudfunctions/getQuestionDetail/index.js
+++ /dev/null
@@ -1,43 +0,0 @@
-// 云函数 getQuestionDetail
-const cloud = require('wx-server-sdk')
-cloud.init()
-
-const db = cloud.database()
-
-exports.main = async (event, context) => {
- const { questionId } = event
-
- try {
- // 从问题表获取原始问题
- const questionResult = await db.collection('Question')
- .doc(questionId)
- .get()
-
- // 从回答表获取回答
- const answerResult = await db.collection('answer')
- .where({
- _id: questionId // 根据设计,answer表的_id对应question表的_id
- })
- .get()
-
- // 提取回答内容(如果有)
- const answerData = answerResult.data[0] || null
- const answerContent = answerData ? answerData.answer : null
-
- return {
- code: 200,
- data: {
- question: questionResult.data,
- answer: answerContent
- },
- message: '获取成功'
- }
- } catch (error) {
- console.error('获取问题详情失败:', error)
- return {
- code: 500,
- data: null,
- message: '获取失败:' + error.message
- }
- }
-}
\ No newline at end of file
diff --git a/cloudfunctions/getQuestionDetail/package.json b/cloudfunctions/getQuestionDetail/package.json
deleted file mode 100644
index 707fe46..0000000
--- a/cloudfunctions/getQuestionDetail/package.json
+++ /dev/null
@@ -1,14 +0,0 @@
-{
- "name": "getQuestionDetail",
- "version": "1.0.0",
- "description": "",
- "main": "index.js",
- "scripts": {
- "test": "echo \"Error: no test specified\" && exit 1"
- },
- "author": "",
- "license": "ISC",
- "dependencies": {
- "wx-server-sdk": "~3.0.1"
- }
-}
\ No newline at end of file
diff --git a/cloudfunctions/initDatabase/config.json b/cloudfunctions/initDatabase/config.json
deleted file mode 100644
index 6ecbd09..0000000
--- a/cloudfunctions/initDatabase/config.json
+++ /dev/null
@@ -1,9 +0,0 @@
-{
- "permissions": {
- "openapi": [],
- "db": {
- "cloudbase_env": "cloud1-0g2sr1117862afae",
- "auth_mode": "admin"
- }
- }
-}
\ No newline at end of file
diff --git a/cloudfunctions/initDatabase/index.js b/cloudfunctions/initDatabase/index.js
deleted file mode 100644
index 99e0df7..0000000
--- a/cloudfunctions/initDatabase/index.js
+++ /dev/null
@@ -1,106 +0,0 @@
-// cloudfunctions/initDatabase/index.js
-const cloud = require('wx-server-sdk')
-
-// 初始化云环境
-cloud.init({
- env: cloud.DYNAMIC_CURRENT_ENV
-})
-
-// 获取数据库引用
-const db = cloud.database()
-
-// 集合名称 - 使用正确的首字母大写格式
-const COLLECTIONS = {
- REVIEW: 'Review',
- POETIES: 'poeties',
- USER: 'user'
-}
-
-exports.main = async (event, context) => {
- try {
- console.log('开始初始化数据库...')
-
- // 检查并创建review集合
- await createCollectionIfNotExists(COLLECTIONS.REVIEW)
-
- // 检查并创建poeties集合
- await createCollectionIfNotExists(COLLECTIONS.POETIES)
-
- // 检查并创建user集合
- await createCollectionIfNotExists(COLLECTIONS.USER)
-
- // 为review集合创建索引(可选,但有助于提高查询性能)
- await createIndexesForReviewCollection()
-
- return {
- success: true,
- message: '数据库初始化成功',
- collections: Object.values(COLLECTIONS)
- }
- } catch (error) {
- console.error('数据库初始化失败:', error)
- return {
- success: false,
- message: '数据库初始化失败',
- error: error.message,
- errorCode: error.code
- }
- }
-}
-
-// 检查集合是否存在,如果不存在则创建
-async function createCollectionIfNotExists(collectionName) {
- try {
- console.log(`检查集合 ${collectionName} 是否存在...`)
-
- // 尝试获取集合信息
- await db.collection(collectionName).count()
- console.log(`集合 ${collectionName} 已存在`)
-
- return true
- } catch (error) {
- // 如果集合不存在,会抛出错误
- if (error.code === -502004 || error.code === -502025 || error.code === -502005) {
- console.log(`集合 ${collectionName} 不存在(错误码: ${error.code}),正在创建...`)
- try {
- // 创建集合
- await db.createCollection(collectionName)
- console.log(`集合 ${collectionName} 创建成功`)
- return true
- } catch (createError) {
- console.error(`创建集合 ${collectionName} 失败:`, createError)
- throw createError
- }
- } else {
- // 其他错误直接抛出
- throw error
- }
- }
-}
-
-// 为review集合创建索引
-async function createIndexesForReviewCollection() {
- try {
- console.log('为review集合创建索引...')
-
- // 为openid创建索引,提高查询性能
- await db.collection(COLLECTIONS.REVIEW).createIndex({
- openid: 1
- })
-
- // 为poemId创建索引
- await db.collection(COLLECTIONS.REVIEW).createIndex({
- poemId: 1
- })
-
- // 为reciteDateTime创建索引,用于按时间排序
- await db.collection(COLLECTIONS.REVIEW).createIndex({
- reciteDateTime: -1
- })
-
- console.log('review集合索引创建成功')
- } catch (error) {
- console.error('创建索引失败:', error)
- // 索引创建失败不应该阻止整个初始化过程
- }
-}
\ No newline at end of file
diff --git a/cloudfunctions/initDatabase/package.json b/cloudfunctions/initDatabase/package.json
deleted file mode 100644
index e143eb3..0000000
--- a/cloudfunctions/initDatabase/package.json
+++ /dev/null
@@ -1,14 +0,0 @@
-{
- "name": "init-database",
- "version": "1.0.0",
- "description": "初始化数据库集合和索引",
- "main": "index.js",
- "scripts": {
- "test": "echo \"Error: no test specified\" && exit 1"
- },
- "author": "",
- "license": "ISC",
- "dependencies": {
- "wx-server-sdk": "~3.0.1"
- }
-}
\ No newline at end of file
diff --git a/cloudfunctions/pendingquestion/config.json b/cloudfunctions/pendingquestion/config.json
deleted file mode 100644
index c9e9daf..0000000
--- a/cloudfunctions/pendingquestion/config.json
+++ /dev/null
@@ -1,9 +0,0 @@
-{
- "permissions": {
- "openapi": [],
- "db": {
- "query": true,
- "read": true
- }
- }
-}
\ No newline at end of file
diff --git a/cloudfunctions/pendingquestion/index.js b/cloudfunctions/pendingquestion/index.js
deleted file mode 100644
index d8823df..0000000
--- a/cloudfunctions/pendingquestion/index.js
+++ /dev/null
@@ -1,70 +0,0 @@
-const cloud = require('wx-server-sdk')
-cloud.init({
- env: cloud.DYNAMIC_CURRENT_ENV
-})
-
-exports.main = async (event, context) => {
- try {
- // 添加日志记录,便于调试
- console.log('pendingquestion云函数被调用,event:', event)
-
- const db = cloud.database()
- const _ = db.command
-
- // 获取查询参数
- const { pageSize = 20, pageNum = 1 } = event
-
- // 计算起始位置
- const skip = (pageNum - 1) * pageSize
-
- // 查询待处理问题
- const result = await db.collection('Question')
- .where({
- status: 0 // 0表示待处理状态
- })
- .orderBy('createTime', 'asc')
- .skip(skip)
- .limit(pageSize)
- .get()
-
- console.log('查询结果:', result)
-
- // 获取总数用于分页
- const countResult = await db.collection('Question')
- .where({
- status: 0
- })
- .count()
-
- console.log('总数结果:', countResult)
-
- // 构建并返回标准格式的响应
- const response = {
- success: true,
- data: result.data,
- total: countResult.total,
- pageNum: pageNum,
- pageSize: pageSize,
- hasMore: skip + result.data.length < countResult.total,
- message: '查询成功'
- }
-
- console.log('返回响应:', response)
- return response
-
- } catch (error) {
- console.error('获取待处理问题失败:', error)
-
- // 构建错误响应
- const errorResponse = {
- success: false,
- message: '获取待处理问题失败:' + error.message,
- data: [],
- total: 0,
- error: error.message
- }
-
- console.log('返回错误响应:', errorResponse)
- return errorResponse
- }
-}
\ No newline at end of file
diff --git a/cloudfunctions/pendingquestion/package.json b/cloudfunctions/pendingquestion/package.json
deleted file mode 100644
index bf718b6..0000000
--- a/cloudfunctions/pendingquestion/package.json
+++ /dev/null
@@ -1,9 +0,0 @@
-{
- "name": "pendingquestion",
- "version": "1.0.0",
- "description": "获取待处理问题列表",
- "main": "index.js",
- "dependencies": {
- "wx-server-sdk": "~2.6.3"
- }
-}
\ No newline at end of file
diff --git a/cloudfunctions/reviewManagement/index.js b/cloudfunctions/reviewManagement/index.js
index ef91788..16299b9 100644
--- a/cloudfunctions/reviewManagement/index.js
+++ b/cloudfunctions/reviewManagement/index.js
@@ -5,10 +5,19 @@ cloud.init({
})
const db = cloud.database()
+const _ = db.command
const reviewCollection = db.collection('Review')
exports.main = async (event, context) => {
- const { action, reviewData, recordId, openid, poemId, page = 1, pageSize = 20 } = event
+ const {
+ action,
+ reviewData,
+ recordId,
+ openid,
+ poemId,
+ page = 1,
+ pageSize = 20
+ } = event
try {
switch (action) {
@@ -24,6 +33,10 @@ exports.main = async (event, context) => {
return await deleteReviewRecord(recordId)
case 'getReviewStats':
return await getReviewStats(openid)
+ case 'checkExistingRecord': // 检查是否存在记录
+ return await checkExistingRecord(poemId, openid)
+ case 'updateReviewRecord': // 更新已有记录
+ return await updateReviewRecord(recordId, reviewData)
default:
return {
success: false,
@@ -77,6 +90,137 @@ exports.main = async (event, context) => {
}
}
+// 检查是否已存在该用户该诗歌的记录
+async function checkExistingRecord(poemId, openid) {
+ if (!poemId || !openid) {
+ return {
+ success: false,
+ message: '古诗ID和用户ID不能为空'
+ }
+ }
+
+ try {
+ console.log('检查是否存在记录,poemId:', poemId, 'openid:', openid);
+
+ const result = await reviewCollection
+ .where({
+ poemId: poemId,
+ openid: openid
+ })
+ .orderBy('reciteDateTime', 'desc')
+ .limit(1)
+ .get()
+
+ console.log('查询结果:', result);
+
+ if (result.data && result.data.length > 0) {
+ const existingRecord = result.data[0];
+ console.log('找到已存在记录,ID:', existingRecord._id);
+
+ return {
+ success: true,
+ exists: true,
+ recordId: existingRecord._id,
+ recordData: existingRecord
+ }
+ } else {
+ console.log('未找到已存在记录');
+ return {
+ success: true,
+ exists: false,
+ recordId: null
+ }
+ }
+ } catch (error) {
+ console.error('检查记录失败:', error);
+ throw error;
+ }
+}
+
+// 更新已有背诵记录
+async function updateReviewRecord(recordId, reviewData) {
+ if (!recordId) {
+ return {
+ success: false,
+ message: '记录ID不能为空'
+ }
+ }
+
+ // 验证必要字段
+ if (!reviewData || !reviewData.poemId || !reviewData.openid) {
+ return {
+ success: false,
+ message: '缺少必要字段'
+ }
+ }
+
+ // 详细日志记录输入数据
+ console.log('更新记录 - 接收到的原始reviewData数据结构:', Object.keys(reviewData).join(', '))
+ console.log('更新记录 - 接收到的reviewData是否包含content字段:', reviewData.hasOwnProperty('content'))
+ console.log('更新记录 - 接收到的reviewData是否包含score字段:', reviewData.hasOwnProperty('score'))
+
+ // 先从传入的数据中删除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 updateData = {};
+ updateData.poemName = reviewData.poemName || ''; // 古诗名称
+ updateData.accuracy = Number(reviewData.accuracy) || 0; // 正确率,确保是数字类型
+ updateData.duration = Number(reviewData.duration) || 0; // 背诵时长(秒),确保是数字类型
+ updateData.reciteDateTime = reviewData.reciteDateTime || new Date().toISOString(); // 背诵时间
+ updateData.updateTime = db.serverDate(); // 更新时间
+
+ // 再次明确确认updateData中没有content和score字段
+ if (updateData.hasOwnProperty('content')) {
+ console.error('更新记录 - 警告: updateData中仍存在content字段,将被强制删除!')
+ delete updateData.content;
+ }
+ if (updateData.hasOwnProperty('score')) {
+ console.error('更新记录 - 警告: updateData中仍存在score字段,将被强制删除!')
+ delete updateData.score;
+ }
+
+ // 详细日志记录最终更新的数据
+ console.log('更新记录 - 最终准备更新的数据结构:', Object.keys(updateData).join(', '))
+ console.log('更新记录 - 最终准备更新的数据:', JSON.stringify(updateData))
+
+ try {
+ // 执行更新操作
+ const result = await reviewCollection.doc(recordId).update({
+ data: updateData
+ })
+
+ console.log('背诵记录更新成功,ID:', recordId, '更新结果:', result)
+
+ // 返回结果
+ return {
+ success: true,
+ _id: recordId,
+ message: '背诵记录更新成功',
+ updatedFields: Object.keys(updateData), // 返回实际更新的字段列表,便于调试
+ stats: result.stats // 返回更新统计信息
+ }
+ } catch (error) {
+ if (error.errCode === -502005) {
+ return {
+ success: false,
+ message: '背诵记录不存在或已被删除'
+ }
+ }
+ throw error
+ }
+}
+
// 添加背诵记录
async function addReviewRecord(reviewData) {
// 验证必要字段
diff --git a/pages/guiding/guiding.js b/pages/guiding/guiding.js
index d37b847..02d1e3e 100644
--- a/pages/guiding/guiding.js
+++ b/pages/guiding/guiding.js
@@ -52,9 +52,9 @@ Page({
url: `/pages/index/index?openid=${cachedUserInfo.openid}`
});
} else {
- console.log('缓存中为管理员,跳转到pendingQuestion页面');
+ console.log('缓存中为管理员,跳转到managePoem页面');
wx.reLaunch({
- url: `/pages/pendingQuestion/pendingQuestion?openid=${cachedUserInfo.openid}`
+ url: `/pages/managePoems/managePoems?openid=${cachedUserInfo.openid}`
});
}
return; // 跳转后可以return,避免后续代码执行
@@ -95,9 +95,9 @@ Page({
url: `/pages/index/index?openid=${userInfo.openid}`
});
} else {
- console.log('管理员,跳转到pendingQuestion页面');
+ console.log('管理员,跳转到managePoems页面');
wx.reLaunch({
- url: `/pages/pendingQuestion/pendingQuestion?openid=${userInfo.openid}`
+ url: `/pages/managePoems/managePoems?openid=${userInfo.openid}`
});
}
diff --git a/pages/index/index.js b/pages/index/index.js
index be99d10..5a5faa5 100644
--- a/pages/index/index.js
+++ b/pages/index/index.js
@@ -26,7 +26,8 @@ Page({
});
}
},
-// 加载用户数据和诗词数据
+
+ // 加载用户数据和诗词数据
async loadUserDataAndPoems() {
try {
this.setData({ isLoading: true });
@@ -40,25 +41,28 @@ Page({
if (userResult.success) {
const userInfo = userResult.data.userInfo;
this.setData({
- userInfo: userInfo,
- reviewedPoems: userInfo.reviewedPoems || {}
+ userInfo: userInfo
});
-
- console.log('用户复习记录:', this.data.reviewedPoems);
}
if (poemsResult.success) {
const allPoems = poemsResult.data.poems;
- // 筛选其他诗词
- const { reviewedPoems, otherPoems } = this.classifyPoems(allPoems);
+
+ // 获取用户的复习记录
+ const reviewResult = await this.getUserReviewRecords(this.data.openid);
+ const reviewedPoemIds = reviewResult.success ? reviewResult.data.reviews.map(item => item.poemId) : [];
+
+ // 分类诗词:把需要复习的古诗放到末尾
+ const { reviewedPoems, otherPoems, poems } = this.classifyPoems(allPoems, reviewedPoemIds);
this.setData({
- poems: allPoems,
+ poems: poems,
reviewedPoems: reviewedPoems,
otherPoems: otherPoems
});
-
+ console.log('复习诗词数量:', reviewedPoems.length);
+ console.log('其他诗词数量:', otherPoems.length);
}
} catch (error) {
@@ -71,6 +75,26 @@ Page({
this.setData({ isLoading: false });
}
},
+
+ // 从Review表获取用户的复习记录
+ async getUserReviewRecords(openid) {
+ try {
+ const result = await wx.cloud.callFunction({
+ name: 'reviewManagement',
+ data: {
+ action: 'getUserReviews',
+ openid: openid,
+ page: 1,
+ pageSize: 100 // 获取所有复习记录
+ }
+ });
+ return result.result;
+ } catch (error) {
+ console.error('获取复习记录失败:', error);
+ return { success: false, message: '获取复习记录失败' };
+ }
+ },
+
// 获取用户信息
async getUserInfo(openid) {
try {
@@ -87,6 +111,7 @@ Page({
return { success: false, message: '获取用户信息失败' };
}
},
+
// 获取所有诗词
async getAllPoems() {
try {
@@ -102,28 +127,38 @@ Page({
return { success: false, message: '获取诗词失败' };
}
},
- // 分类诗词:今天需要复习的 vs 其他诗词
- classifyPoems(allPoems) {
- const reviewedPoems = this.data.reviewedPoems;
+
+ // 分类诗词:把需要复习的古诗放到末尾
+ classifyPoems(allPoems, reviewedPoemIds) {
+ const reviewedPoems = [];
const otherPoems = [];
allPoems.forEach(poem => {
- const poemReviewData = reviewedPoems[poem._id];
- if (poemReviewData==null){
+ if (reviewedPoemIds.includes(poem._id)) {
+ // 需要复习的诗词
+ reviewedPoems.push(poem);
+ } else {
+ // 其他诗词
otherPoems.push(poem);
}
-
});
- return { reviewedPoems, otherPoems };
+ // 把需要复习的诗词放到其他诗词的末尾
+ const finalPoems = [...otherPoems, ...reviewedPoems];
+
+ return {
+ reviewedPoems: reviewedPoems,
+ otherPoems: otherPoems,
+ poems: finalPoems // 最终的诗词列表,复习的放在末尾
+ };
},
+
// 进入学习页面
goToStudy(e) {
const poemId = e.currentTarget.dataset.id;
-
- wx.navigateTo({
- url: `/pages/study/study?id=${poemId}`
- });
+ wx.navigateTo({
+ url: `/pages/study/study?id=${poemId}`
+ });
},
// 进入背诵页面
@@ -138,16 +173,14 @@ Page({
}
},
-
review(){
- const userInfo= this.data.userInfo;
- console.log('跳转到复习页面,诗歌ID:', userInfo);
+ const userInfo = this.data.userInfo;
+ console.log('跳转到复习页面,用户信息:', userInfo);
wx.navigateTo({
- url: `/pages/review/review?userInfo=${userInfo}`
+ url: `/pages/review/review?userInfo=${encodeURIComponent(JSON.stringify(userInfo))}`
})
},
-
question(){
wx.navigateTo({
url: '/pages/question/question'
@@ -162,15 +195,19 @@ Page({
onShow() {
// 每次显示页面时检查数据
- if (this.data.poems.length === 0 && !this.data.isLoading) {
- this.loadPoemsFromDatabase();
+ if (this.data.openid && this.data.poems.length === 0 && !this.data.isLoading) {
+ this.loadUserDataAndPoems();
}
},
onPullDownRefresh() {
- this.loadPoemsFromDatabase().finally(() => {
+ if (this.data.openid) {
+ this.loadUserDataAndPoems().finally(() => {
+ wx.stopPullDownRefresh();
+ });
+ } else {
wx.stopPullDownRefresh();
- });
+ }
},
onShareAppMessage() {
@@ -179,7 +216,4 @@ Page({
path: '/pages/index/index'
};
}
-})
-
-
-
\ No newline at end of file
+})
\ No newline at end of file
diff --git a/pages/keyWord/keyWord.js b/pages/keyWord/keyWord.js
index aea5c78..f624a1a 100644
--- a/pages/keyWord/keyWord.js
+++ b/pages/keyWord/keyWord.js
@@ -1,327 +1,20 @@
// 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();
- }
- },
+ /**
+ * 页面的初始数据
+ */
+ data: {
- // 输入回答
- 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(' > ');
- },
+ /**
+ * 生命周期函数--监听页面加载
+ */
+ onLoad(options) {
- // 重置关键词
- 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.json b/pages/keyWord/keyWord.json
deleted file mode 100644
index 8835af0..0000000
--- a/pages/keyWord/keyWord.json
+++ /dev/null
@@ -1,3 +0,0 @@
-{
- "usingComponents": {}
-}
\ No newline at end of file
diff --git a/pages/keyWord/keyWord.wxml b/pages/keyWord/keyWord.wxml
index 4929bdd..b9cd149 100644
--- a/pages/keyWord/keyWord.wxml
+++ b/pages/keyWord/keyWord.wxml
@@ -1,83 +1,2 @@
-
-
-
-
-
- 问题:
-
- {{question}}
-
-
-
-
-
- 关键词管理:
-
-
-
- 当前路径:
- {{getCurrentKeywordPath()}}
-
-
-
-
- 第{{currentLevel + 1}}级关键词
-
-
-
-
-
- {{item}}
-
-
-
-
-
-
-
- +
- {{isCreatingNew ? '取消新建' : '新建关键词'}}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 答案:
-
-
-
-
-
-
\ No newline at end of file
+pages/keyWord/keyWord.wxml
\ No newline at end of file
diff --git a/pages/keyWord/keyWord.wxss b/pages/keyWord/keyWord.wxss
deleted file mode 100644
index c00a635..0000000
--- a/pages/keyWord/keyWord.wxss
+++ /dev/null
@@ -1,231 +0,0 @@
-/* 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 f16bc80..cd2a961 100644
--- a/pages/login/login.js
+++ b/pages/login/login.js
@@ -142,7 +142,7 @@ Page({
// 根据用户角色决定跳转页面:role为false时是管理员,跳转到pendingQuestion页面
if (userData.role === false) {
wx.reLaunch({
- url: `/pages/pendingQuestion/pendingQuestion?openid=${this.data.openid}`
+ url: `/pages/managePoems/managePoems?openid=${this.data.openid}`
});
} else {
wx.reLaunch({
diff --git a/pages/managePoems/managePoems.wxml b/pages/managePoems/managePoems.wxml
index d0afb9a..3ab7e87 100644
--- a/pages/managePoems/managePoems.wxml
+++ b/pages/managePoems/managePoems.wxml
@@ -89,12 +89,3 @@
-
-
-
- 待解决问题
-
-
- 管理古诗
-
-
\ No newline at end of file
diff --git a/pages/pendingQuestion/pendingQuestion.js b/pages/pendingQuestion/pendingQuestion.js
deleted file mode 100644
index c5c7b64..0000000
--- a/pages/pendingQuestion/pendingQuestion.js
+++ /dev/null
@@ -1,169 +0,0 @@
-// pages/pendingQuestion/pendingQuestion.js
-Page({
- data: {
- pendingQuestions: [] // 初始化为空数组
- },
-
- onLoad(options) {
- this.loadPendingQuestions();
- },
-
- onShow() {
- // 页面显示时重新加载数据,确保数据最新
- this.loadPendingQuestions();
- },
-
- /**
- * 从Question集合加载待解决问题
- */
- 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'
- });
- }
- }
- },
-
- /**
- * 格式化时间
- */
- 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)}`
- });
- },
-
- /**
- * 跳转到待解决问题页面(当前页面)
- */
- goToPendingQuestion() {
- // 已经在当前页面,刷新数据
- this.loadPendingQuestions();
- },
-
- /**
- * 跳转到古诗管理页面
- */
- goToManagePoems() {
- wx.navigateTo({
- url: '/pages/managePoems/managePoems'
- });
- },
-
- /**
- * 生命周期函数--监听页面初次渲染完成
- */
- onReady() {
-
- },
-
- /**
- * 生命周期函数--监听页面隐藏
- */
- onHide() {
-
- },
-
- /**
- * 生命周期函数--监听页面卸载
- */
- onUnload() {
-
- },
-
- /**
- * 页面相关事件处理函数--监听用户下拉动作
- */
- onPullDownRefresh() {
- this.loadPendingQuestions().then(() => {
- wx.stopPullDownRefresh();
- }).catch(() => {
- wx.stopPullDownRefresh();
- });
- },
-
- /**
- * 页面上拉触底事件的处理函数
- */
- onReachBottom() {
-
- },
-
- /**
- * 用户点击右上角分享
- */
- onShareAppMessage() {
- return {
- title: '待解决问题',
- path: '/pages/pendingQuestion/pendingQuestion'
- };
- }
-})
\ No newline at end of file
diff --git a/pages/pendingQuestion/pendingQuestion.json b/pages/pendingQuestion/pendingQuestion.json
deleted file mode 100644
index 8835af0..0000000
--- a/pages/pendingQuestion/pendingQuestion.json
+++ /dev/null
@@ -1,3 +0,0 @@
-{
- "usingComponents": {}
-}
\ No newline at end of file
diff --git a/pages/pendingQuestion/pendingQuestion.wxml b/pages/pendingQuestion/pendingQuestion.wxml
deleted file mode 100644
index 581e943..0000000
--- a/pages/pendingQuestion/pendingQuestion.wxml
+++ /dev/null
@@ -1,38 +0,0 @@
-
-
-
-
-
-
-
- {{item.question}}
-
-
- {{item.createdAt}}
- ›
-
-
-
-
- 暂无待解决问题
-
-
-
-
-
-
- 待解决问题
-
-
- 管理古诗
-
-
\ No newline at end of file
diff --git a/pages/pendingQuestion/pendingQuestion.wxss b/pages/pendingQuestion/pendingQuestion.wxss
deleted file mode 100644
index d70cbdd..0000000
--- a/pages/pendingQuestion/pendingQuestion.wxss
+++ /dev/null
@@ -1,102 +0,0 @@
-/* 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/recite/recite.js b/pages/recite/recite.js
index c1baac3..8e60768 100644
--- a/pages/recite/recite.js
+++ b/pages/recite/recite.js
@@ -23,80 +23,166 @@ Page({
startReciteTime: 0, // 开始背诵时间
reciteDuration: '', // 背诵时长
reciteDateTime: '', // 背诵时间
- openid: '' // 添加 openid
+ openid: '' ,// 添加 openid
+ isRecording: false, // 是否正在录音
+ isProcessing: false, // 是否正在处理语音
+ recognitionProgress: 0, // 识别进度
+ lastRecognitionTime: 0, // 上次识别时间(用于防抖)
+ existingRecordId: null // 新增:已存在记录的ID
},
- 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) {
+ onLoad(options) {
+ console.log('背诵页面参数:', options);
+
+ // 接收从学习页面传递的参数
+ if (options.poemData) {
+ try {
+ console.log('原始poemData参数:', options.poemData);
+
+ // 尝试直接解析
+ let poemData;
+ try {
+ poemData = JSON.parse(options.poemData);
+ console.log('直接解析成功');
+ } catch (firstError) {
+ console.log('直接解析失败,尝试URL解码:', firstError);
+
+ // 尝试一次URL解码
+ try {
+ const decodedOnce = decodeURIComponent(options.poemData);
+ poemData = JSON.parse(decodedOnce);
+ console.log('一次URL解码成功');
+ } catch (secondError) {
+ console.log('一次URL解码失败,尝试二次URL解码:', secondError);
+
+ // 尝试二次URL解码(处理双重编码的情况)
+ try {
+ const decodedTwice = decodeURIComponent(decodeURIComponent(options.poemData));
+ poemData = JSON.parse(decodedTwice);
+ console.log('二次URL解码成功');
+ } catch (thirdError) {
+ console.log('所有解析方式都失败,使用备用方案:', thirdError);
+ // 使用备用方案
+ this.fallbackToIdOnly(options);
+ return;
+ }
+ }
+ }
+
+ console.log('从学习页面传递的诗歌数据:', poemData);
+ this.setData({
+ poemId: poemData._id || options.id,
+ poemTitle: poemData.title || '',
+ poemAuthor: poemData.author || '',
+ originalText: poemData.content || '' // 直接从传递的数据中获取原文
+ });
+
+ // 设置导航栏标题为诗歌名称
+ this.setNavigationBarTitle(poemData.title || '背诵');
+
+ } catch (error) {
+ console.error('解析诗歌数据失败:', error);
+ // 如果解析失败,使用备用方案
+ this.fallbackToIdOnly(options);
+ }
+ }
+ // 接收从index页面传递的参数
+ else if (options.id && options.title && options.author) {
+ try {
+ const poemTitle = this.safeDecodeURIComponent(options.title);
+ const poemAuthor = this.safeDecodeURIComponent(options.author);
+
this.setData({
- poemId: options.id
+ poemId: options.id,
+ poemTitle: poemTitle,
+ poemAuthor: poemAuthor
});
+
+ // 设置导航栏标题
+ this.setNavigationBarTitle(poemTitle);
+
+ // 获取古诗原文
this.getPoemDetail(options.id);
+ } catch (error) {
+ console.error('处理标题和作者参数失败:', error);
+ this.fallbackToIdOnly(options);
}
}
- }
- // 接收从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
+ // 只传递了id的情况
+ else if (options.id) {
+ this.setData({
+ poemId: options.id
+ });
+ // 获取古诗原文
+ this.getPoemDetail(options.id);
+ } else {
+ console.error('缺少必要的参数');
+ wx.showToast({
+ title: '参数错误',
+ icon: 'none'
+ });
+ setTimeout(() => {
+ wx.navigateBack();
+ }, 1500);
+ }
+
+ //获取用户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();
+ }
});
- // 获取古诗原文
- 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();
- }
- });
- },
+ },
+
+ // 安全的URL解码函数
+ safeDecodeURIComponent(str) {
+ try {
+ return decodeURIComponent(str);
+ } catch (error) {
+ console.log('URL解码失败,返回原字符串:', error);
+ return str;
+ }
+ },
+
+ // 备用方案:只使用ID
+ fallbackToIdOnly(options) {
+ console.log('使用备用方案,仅通过ID获取数据');
+ if (options.id) {
+ this.setData({
+ poemId: options.id
+ });
+ // 获取古诗详情
+ this.getPoemDetail(options.id);
+ } else {
+ wx.showToast({
+ title: '无法获取诗歌数据',
+ icon: 'none'
+ });
+ setTimeout(() => {
+ wx.navigateBack();
+ }, 1500);
+ }
+ },
// 获取用户 openid
async getUserOpenId() {
try {
@@ -111,35 +197,82 @@ Page({
console.error('获取openid失败:', error)
}
},
- // 保存背诵记录到数据库
+
+ // 检查是否已存在该用户该诗歌的记录
+ async checkExistingRecord(poemId, openid) {
+ try {
+ console.log('检查是否已存在记录,poemId:', poemId, 'openid:', openid);
+
+ const result = await wx.cloud.callFunction({
+ name: 'reviewManagement',
+ data: {
+ action: 'checkExistingRecord',
+ poemId: poemId,
+ openid: openid
+ }
+ });
+
+ console.log('检查记录结果:', result);
+
+ if (result && result.result && result.result.success) {
+ if (result.result.exists) {
+ console.log('已存在记录,记录ID:', result.result.recordId);
+ this.setData({
+ existingRecordId: result.result.recordId
+ });
+ return true;
+ } else {
+ console.log('不存在记录,将创建新记录');
+ this.setData({
+ existingRecordId: null
+ });
+ return false;
+ }
+ } else {
+ console.log('检查记录失败,默认创建新记录');
+ this.setData({
+ existingRecordId: null
+ });
+ return false;
+ }
+ } catch (error) {
+ console.error('检查记录时出错:', error);
+ this.setData({
+ existingRecordId: null
+ });
+ return false;
+ }
+ },
+
+ // 修改保存背诵记录方法,支持更新已有记录
async saveReviewRecord(reviewData) {
try {
- console.log('开始保存背诵记录')
- console.log('传入的reviewData完整内容:', JSON.stringify(reviewData))
- console.log('传入的reviewData数据结构:', Object.keys(reviewData || {}).join(', '))
+ console.log('开始保存背诵记录');
+ console.log('传入的reviewData完整内容:', JSON.stringify(reviewData));
+ console.log('传入的reviewData数据结构:', Object.keys(reviewData || {}).join(', '));
// 验证必要字段
if (!reviewData || !reviewData.poemId || !reviewData.openid) {
- console.error('缺少必要字段')
+ console.error('缺少必要字段');
wx.showToast({
title: '数据保存失败:缺少必要字段',
icon: 'none'
- })
- return false
+ });
+ return false;
}
// 先检查并删除可能存在的content和score字段
if (reviewData.hasOwnProperty('content')) {
- console.warn('前端发现content字段,将被删除:', reviewData.content)
- delete reviewData.content
+ console.warn('前端发现content字段,将被删除:', reviewData.content);
+ delete reviewData.content;
}
if (reviewData.hasOwnProperty('score')) {
- console.warn('前端发现score字段,将被删除:', reviewData.score)
- delete reviewData.score
+ console.warn('前端发现score字段,将被删除:', reviewData.score);
+ delete reviewData.score;
}
// 详细日志记录清理后的数据结构
- console.log('前端清理后reviewData的数据结构:', Object.keys(reviewData).join(', '))
+ console.log('前端清理后reviewData的数据结构:', Object.keys(reviewData).join(', '));
// 创建一个全新的干净对象,不继承任何原型属性,只包含我们需要的字段
const dataToSave = {};
@@ -152,54 +285,80 @@ Page({
// 再次检查dataToSave中是否存在content和score字段
if (dataToSave.hasOwnProperty('content')) {
- console.error('警告: 前端dataToSave中仍存在content字段,将被强制删除!')
+ console.error('警告: 前端dataToSave中仍存在content字段,将被强制删除!');
delete dataToSave.content;
}
if (dataToSave.hasOwnProperty('score')) {
- console.error('警告: 前端dataToSave中仍存在score字段,将被强制删除!')
+ console.error('警告: 前端dataToSave中仍存在score字段,将被强制删除!');
delete dataToSave.score;
}
// 详细日志记录最终发送的数据
- console.log('前端最终发送到数据库的数据结构:', Object.keys(dataToSave).join(', '))
- console.log('前端最终发送到数据库的数据:', JSON.stringify(dataToSave))
+ console.log('前端最终发送到数据库的数据结构:', Object.keys(dataToSave).join(', '));
+ console.log('前端最终发送到数据库的数据:', JSON.stringify(dataToSave));
- // 调用云函数
- const result = await wx.cloud.callFunction({
- name: 'reviewManagement',
- data: {
- action: 'addReviewRecord',
- reviewData: dataToSave
- }
- })
+ // 先检查是否已存在记录
+ const hasExistingRecord = await this.checkExistingRecord(dataToSave.poemId, dataToSave.openid);
+
+ let result;
+ if (hasExistingRecord && this.data.existingRecordId) {
+ // 更新已有记录
+ console.log('更新已有记录,记录ID:', this.data.existingRecordId);
+ result = await wx.cloud.callFunction({
+ name: 'reviewManagement',
+ data: {
+ action: 'updateReviewRecord',
+ recordId: this.data.existingRecordId,
+ reviewData: dataToSave
+ }
+ });
+ } else {
+ // 创建新记录
+ console.log('创建新记录');
+ 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
+ console.log('背诵记录保存成功:', result.result._id);
+ console.log('云函数返回的保存字段列表:', result.result.savedFields?.join(', ') || '未知');
+
+ // 如果是新增记录,保存记录ID
+ if (!hasExistingRecord && result.result._id) {
+ this.setData({
+ existingRecordId: result.result._id
+ });
+ }
+
+ return true;
} else {
- console.error('背诵记录保存失败:', result.result?.message || '未知错误')
- return false
+ console.error('背诵记录保存失败:', result.result?.message || '未知错误');
+ return false;
}
} catch (error) {
- console.error('保存背诵记录异常:', error)
- return false
+ console.error('保存背诵记录异常:', error);
+ return false;
}
},
// 计算背诵难度
calculateDifficulty(duration, originalText) {
// 基于原文长度和背诵时间计算难度
- const textLength = (originalText || '').length
- const avgSpeed = textLength / Math.max(1, duration)
+ const textLength = (originalText || '').length;
+ const avgSpeed = textLength / Math.max(1, duration);
if (textLength > 200 || avgSpeed < 0.5) {
- return 'difficult'
+ return 'difficult';
} else if (textLength > 100 || avgSpeed < 1) {
- return 'medium'
+ return 'medium';
} else {
- return 'easy'
+ return 'easy';
}
},
@@ -454,13 +613,28 @@ Page({
console.log('重置背诵状态');
// 重置所有相关数据
this.setData({
- content: '',
- showResult: false,
- accuracyRate: 0,
- startReciteTime: 0,
- reciteDuration: '',
- reciteDateTime: ''
+ content: '',
+ showResult: false,
+ accuracyRate: 0,
+ startReciteTime: 0,
+ reciteDuration: '',
+ reciteDateTime: '',
+ isRecording: false,
+ isProcessing: false,
+ recognitionProgress: 0
+ // 注意:不重置 existingRecordId,因为重新背诵时还是同一首诗
});
+
+ // 停止所有可能的录音
+ try {
+ recorderManager.stop();
+ } catch (e) {
+ console.log('停止录音时出错:', e);
+ }
+
+ wx.hideLoading();
+ wx.hideToast();
+
console.log('重置完成,准备新的背诵');
},
@@ -508,114 +682,254 @@ Page({
},
//开始录音
touchStart: function () {
- const that = this; // 保存this引用
+ const that = this;
+
+ // 防抖处理,避免快速重复点击
+ const now = Date.now();
+ if (now - this.data.lastRecognitionTime < 2000) {
+ console.log("操作过于频繁,请稍后再试");
+ return;
+ }
+
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("录音失败");
- }
- })
+ scope: 'scope.record',
+ success() {
+ console.log("录音授权成功");
+ // 记录开始背诵的时间
+ const startTime = new Date().getTime();
+ console.log('开始背诵时间:', startTime);
+
+ that.setData({
+ startReciteTime: startTime,
+ isRecording: true,
+ isProcessing: false,
+ recognitionProgress: 0,
+ lastRecognitionTime: now
+ });
+
+ // 显示录音中状态
+ wx.showToast({
+ title: '录音中...',
+ icon: 'none',
+ duration: 60000 // 最长60秒
+ });
+
+ recorderManager.start(options);
+ recorderManager.onStart(() => {
+ console.log('recorder start');
+ that.setData({
+ isRecording: true
+ });
+ });
+
+ // 开始进度模拟(让用户感知进度)
+ that.startProgressSimulation();
+ },
+ fail() {
+ console.log("录音授权失败");
+ wx.showToast({
+ title: '需要麦克风权限',
+ icon: 'none'
+ });
+ }
+ });
},
-
//停止录音
touchEnd: function () {
- let that = this
+ let that = this;
+
+ if (!this.data.isRecording) {
+ return;
+ }
+
+ this.setData({
+ isRecording: false,
+ isProcessing: true,
+ recognitionProgress: 50 // 切换到处理阶段
+ });
+
+ // 隐藏录音Toast,显示处理Toast
+ wx.hideToast();
+ wx.showLoading({
+ title: '识别中...',
+ mask: true
+ });
+
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);
- }
- })
+ console.log('录音停止,文件路径:', res.tempFilePath);
+ tempFilePath = res.tempFilePath;
+
+ // 更新进度
+ that.setData({
+ recognitionProgress: 70
+ });
+
+ // 获取文件长度并开始识别
+ wx.getFileSystemManager().getFileInfo({
+ filePath: tempFilePath,
+ success: function (res) {
+ filesize = res.size;
+ console.log('文件长度:', res.size);
+
+ that.setData({
+ recognitionProgress: 80
+ });
+
+ that.shibie();
+ },
+ fail: function (res) {
+ console.log("读取文件长度错误", res);
+ that.recognitionFailed("文件读取失败");
+ }
+ });
});
- },
+},
//语音识别
- shibie(){
- let that = this
+ shibie() {
+ let that = this;
+
+ // 更新进度
+ this.setData({
+ recognitionProgress: 90
+ });
+
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 = "";
+ filePath: tempFilePath,
+ encoding: 'base64',
+ success: function (res) {
+ console.log("开始语音识别请求");
+
+ // 设置请求超时
+ const requestTask = 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",
+ timeout: 10000, // 10秒超时
+ success: function (res) {
+ wx.hideLoading();
+
+ if (res.data.result == '' || !res.data.result) {
+ that.recognitionFailed('听不清楚,请重新说一遍!');
+ return;
+ }
+
+ console.log("识别成功:", res.data);
+ let recognizedText = res.data.result;
+
+ // 快速处理识别结果
+ recognizedText = that.processRecognitionResult(recognizedText);
+
+ that.setData({
+ content: recognizedText,
+ isProcessing: false,
+ recognitionProgress: 100
+ });
+
+ console.log('语音识别完成,识别文本:', recognizedText);
+
+ // 短暂显示成功状态
+ wx.showToast({
+ title: '识别完成',
+ icon: 'success',
+ duration: 1000
+ });
+
+ // 自动检查背诵结果(可选)
+ setTimeout(() => {
+ that.checkRecitation();
+ }, 500);
+ },
+ fail: function (res) {
+ that.recognitionFailed('网络请求失败,请重试');
}
- }
-
- // 确保最终是字符串格式
- 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);
- }
- }); //语音识别结束
- }
- })
- }
+ });
+
+ // 可以取消请求(如果需要)
+ // that.requestTask = requestTask;
+ },
+ fail: function (res) {
+ that.recognitionFailed('文件读取失败');
+ }
+ });
+},
+// 快速处理识别结果
+processRecognitionResult(recognizedText) {
+ if (Array.isArray(recognizedText)) {
+ // 百度API的标准格式是返回字符串数组,如["识别结果"]
+ if (recognizedText.length > 0) {
+ // 直接取第一个结果,不进行复杂处理
+ recognizedText = recognizedText[0];
+ } else {
+ recognizedText = "";
+ }
+ }
+
+ // 确保是字符串
+ if (recognizedText !== null && recognizedText !== undefined) {
+ recognizedText = String(recognizedText);
+ } else {
+ recognizedText = "";
+ }
+
+ // 简单的文本清理(去除明显错误字符)
+ recognizedText = recognizedText.replace(/[^\u4e00-\u9fa5a-zA-Z0-9\s,。?!、:;""''()《》]/g, '');
+
+ return recognizedText;
+},
+
+// 开始进度模拟
+startProgressSimulation() {
+ let progress = 0;
+ const interval = setInterval(() => {
+ if (!this.data.isRecording && !this.data.isProcessing) {
+ clearInterval(interval);
+ return;
+ }
+
+ if (this.data.isRecording) {
+ // 录音阶段:缓慢增长到40%
+ progress = Math.min(40, progress + 1);
+ }
+
+ this.setData({
+ recognitionProgress: progress
+ });
+
+ if (progress >= 100) {
+ clearInterval(interval);
+ }
+ }, 100);
+},
+
+// 识别失败处理
+recognitionFailed(message) {
+ wx.hideLoading();
+ this.setData({
+ isRecording: false,
+ isProcessing: false,
+ recognitionProgress: 0
+ });
+
+ wx.showModal({
+ title: '提示',
+ content: message,
+ showCancel: false,
+ success: () => {
+ // 重置状态
+ this.resetRecitation();
+ }
+ });
+}
})
\ No newline at end of file
diff --git a/pages/recite/recite.wxml b/pages/recite/recite.wxml
index a773be3..fdff445 100644
--- a/pages/recite/recite.wxml
+++ b/pages/recite/recite.wxml
@@ -11,7 +11,27 @@
识别结果:
-
+
+
+
+
+
+
+
+ 识别中... {{recognitionProgress}}%
+
+
+
+
+
+
@@ -40,25 +60,10 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/pages/recite/recite.wxss b/pages/recite/recite.wxss
index 53e3f56..7ec1085 100644
--- a/pages/recite/recite.wxss
+++ b/pages/recite/recite.wxss
@@ -140,25 +140,28 @@
text-shadow: 0 2rpx 4rpx rgba(0, 0, 0, 0.1);
}
-/* 进度条样式 */
-.progress-bar {
- margin-bottom: 30rpx;
+/* 进度条容器 */
+.progress-container {
+ margin: 40rpx 0;
+ padding: 0 30rpx;
}
+/* 进度条背景 */
.progress-bg {
- width: 100%;
- height: 30rpx;
- background: #f0f0f0;
- border-radius: 15rpx;
+ flex: 1;
+ height: 24rpx;
+ background-color: #f0f0f0;
+ border-radius: 12rpx;
overflow: hidden;
- box-shadow: inset 0 2rpx 4rpx rgba(0, 0, 0, 0.1);
+ position: relative;
}
+/* 进度条填充 */
.progress-fill {
height: 100%;
- border-radius: 15rpx;
- transition: width 0.6s ease;
- box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.2);
+ border-radius: 12rpx;
+ transition: width 0.5s ease-in-out, background 0.5s ease;
+ min-width: 0%;
}
/* 评价文字 */
@@ -209,6 +212,49 @@
padding: 20rpx 0;
}
+/* 语音识别进度条样式 */
+.recognition-progress {
+ margin: 40rpx;
+ background: #fff;
+ padding: 30rpx;
+ border-radius: 15rpx;
+ box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.08);
+}
+
+.recognition-progress .progress-bar {
+ width: 100%;
+ height: 20rpx;
+ background: #eee;
+ border-radius: 10rpx;
+ overflow: hidden;
+ margin-bottom: 20rpx;
+}
+
+.recognition-progress .progress-inner {
+ height: 100%;
+ background: linear-gradient(90deg, #4CAF50, #45a049);
+ border-radius: 10rpx;
+ transition: width 0.3s ease;
+}
+
+.recognition-progress .progress-text {
+ font-size: 28rpx;
+ text-align: center;
+ margin-top: 20rpx;
+ color: #333;
+ display: block;
+}
+
+/* 录音按钮状态 */
+.record-btn.recording {
+ background-color: #FF4757;
+}
+
+.record-btn.processing {
+ background-color: #FFA502;
+ opacity: 0.7;
+}
+
/* 响应式设计 */
@media screen and (min-width: 768px) {
.recite-container {
diff --git a/pages/review/review.js b/pages/review/review.js
index 565f5eb..cad62ea 100644
--- a/pages/review/review.js
+++ b/pages/review/review.js
@@ -65,14 +65,18 @@ Page({
*/
data: {
// 遗忘曲线数据 - 显示未来7天的复习计划
- 复习计划数据: [],
+ 复习计划数据: [],
// 需要复习的古诗列表
poemsToReview: [],
// Canvas尺寸
canvasWidth: 0,
canvasHeight: 300,
// 今日需要复习的总数
- todayReviewCount: 0
+ todayReviewCount: 0,
+ // 用户是否有背诵记录
+ hasRecitationRecords: false,
+ // 加载状态
+ isLoading: true
},
/**
@@ -91,21 +95,31 @@ Page({
*/
async loadPoemsToReview() {
try {
- wx.showLoading({ title: '加载复习计划...' })
+ this.setData({ isLoading: true });
// 从数据库加载真实背诵记录
let reciteRecords = await this.loadReciteRecordsFromDatabase();
- // 如果没有记录或记录为空,使用模拟数据
- let recordsToUse = reciteRecords && reciteRecords.length > 0 ? reciteRecords : this.getMockReciteRecords();
-
- // 增强:为每条记录获取完整的诗歌信息,特别是作者信息
- if (reciteRecords && reciteRecords.length > 0) {
- recordsToUse = await this.enrichRecordsWithPoemDetails(reciteRecords);
+ console.log('获取到的背诵记录数量:', reciteRecords ? reciteRecords.length : 0);
+
+ // 检查是否有背诵记录
+ if (!reciteRecords || reciteRecords.length === 0) {
+ console.log('用户暂无背诵记录');
+ this.setData({
+ poemsToReview: [],
+ 复习计划数据: [],
+ todayReviewCount: 0,
+ hasRecitationRecords: false,
+ isLoading: false
+ });
+ return [];
}
+ // 为每条记录获取完整的诗歌信息,特别是作者信息
+ reciteRecords = await this.enrichRecordsWithPoemDetails(reciteRecords);
+
// 使用SM2算法计算复习计划
- const poemsWithReviewPlan = this.calculateReviewPlan(recordsToUse);
+ const poemsWithReviewPlan = this.calculateReviewPlan(reciteRecords);
// 生成未来7天的复习计划数据(用于图表显示)
const reviewPlanData = this.generateReviewPlanData(poemsWithReviewPlan);
@@ -116,27 +130,23 @@ Page({
this.setData({
poemsToReview: poemsWithReviewPlan,
复习计划数据: reviewPlanData,
- todayReviewCount
+ todayReviewCount,
+ hasRecitationRecords: true,
+ isLoading: false
});
- 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
+ poemsToReview: [],
+ 复习计划数据: [],
+ todayReviewCount: 0,
+ hasRecitationRecords: false,
+ isLoading: false
});
- return poemsWithReviewPlan;
+ return [];
}
},
@@ -198,59 +208,7 @@ Page({
},
/**
- * 获取模拟的背诵记录数据
- */
- 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 {
@@ -263,19 +221,39 @@ Page({
throw new Error('获取用户信息失败')
}
- // 查询用户的背诵记录
+ console.log('当前用户openid:', result.openid);
+
+ // 查询用户的背诵记录,按诗歌ID分组,获取每组的最新记录
const db = wx.cloud.database()
- const records = await db.collection('Review')
+ const allRecords = await db.collection('Review')
.where({
openid: result.openid
})
.orderBy('reciteDateTime', 'desc')
.get()
- console.log('从数据库获取的背诵记录:', records.data);
+ console.log('从数据库获取的所有背诵记录:', allRecords.data);
+
+ // 按poemId分组,只保留每个poemId的最新记录
+ const latestRecordsMap = new Map();
+
+ allRecords.data.forEach(record => {
+ const poemId = record.poemId;
+ // 如果还没有这个poemId的记录,或者当前记录时间更晚,则更新
+ if (!latestRecordsMap.has(poemId) ||
+ new Date(record.reciteDateTime) > new Date(latestRecordsMap.get(poemId).reciteDateTime)) {
+ latestRecordsMap.set(poemId, record);
+ }
+ });
+
+ // 将Map转换为数组
+ const uniqueRecords = Array.from(latestRecordsMap.values());
+
+ console.log('去重后的最新记录数量:', uniqueRecords.length);
+ console.log('去重后的记录:', uniqueRecords);
// 转换数据库记录格式,确保字段名一致
- const formattedRecords = records.data.map(record => {
+ const formattedRecords = uniqueRecords.map(record => {
return {
poemId: record.poemId,
poemTitle: record.poemName || '未知标题',
@@ -291,58 +269,18 @@ Page({
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)) {
@@ -351,20 +289,20 @@ Page({
}
});
- console.log('去重后的记录数量:', uniqueRecords.length);
+ 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,
+ lastReciteTime: record.lastReciteTime,
+ accuracy: record.accuracy || 0,
reciteCount: record.reciteCount || 0,
- ef: record.ef || 2.5, // 易度因子
- nextReviewInterval: record.nextReviewInterval || 1 // 下次复习间隔
+ ef: record.ef || 2.5,
+ nextReviewInterval: record.nextReviewInterval || 1
};
// 计算距离上次背诵的时间(天)
@@ -378,32 +316,39 @@ Page({
else if (poem.accuracy >= 40) quality = 2;
else if (poem.accuracy >= 20) quality = 1;
- // 使用SM2计算下次复习间隔
+ // 使用SM2计算下次复习间隔(传入当前状态)
+ const sm2 = new 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);
+ // 计算距离下次复习还有多少天
+ const daysUntilNextReview = Math.max(0, Math.ceil(reviewResult.nextInterval - daysSinceLastRecite));
+ // 判断是否需要今天复习 - 统一使用一个变量声明
+ const needsReviewToday = daysUntilNextReview === 0;
+
+ console.log(`《${poem.title}》:
+ 距离上次: ${daysSinceLastRecite.toFixed(1)}天
+ SM2建议间隔: ${reviewResult.nextInterval}天
+ 距离下次复习: ${daysUntilNextReview}天
+ 需要今天复习: ${needsReviewToday}`);
+
return {
...poem,
- daysSinceLastRecite: Math.round(daysSinceLastRecite * 10) / 10, // 保留一位小数
+ daysSinceLastRecite: Math.round(daysSinceLastRecite * 10) / 10,
nextReviewInterval: reviewResult.nextInterval,
newEF: reviewResult.newEF,
needsReviewToday,
- retentionRate,
- reviewUrgency: needsReviewToday ? (100 - retentionRate) : 0
+ daysUntilNextReview, // 这个字段用于显示
+ retentionRate: sm2.calculateRetentionRate(daysSinceLastRecite * 24, poem.ef * 10),
+ reviewUrgency: needsReviewToday ? (100 - (sm2.calculateRetentionRate(daysSinceLastRecite * 24, poem.ef * 10) || 0)) : daysUntilNextReview
};
});
- // 按复习紧急程度排序(今天需要复习的排前面,按记忆保留率从低到高)
+ // 按复习紧急程度排序
poemsWithReviewStatus.sort((a, b) => {
if (a.needsReviewToday && !b.needsReviewToday) return -1;
if (!a.needsReviewToday && b.needsReviewToday) return 1;
@@ -412,17 +357,18 @@ Page({
return poemsWithReviewStatus;
},
-
+
/**
* 生成未来7天的复习计划数据(用于图表显示)
*/
generateReviewPlanData(poems) {
const planData = [];
const now = new Date();
+ const todayStart = new Date(now.getFullYear(), now.getMonth(), now.getDate());
// 初始化未来7天的数据
for (let i = 0; i < 7; i++) {
- const date = new Date(now);
+ const date = new Date(todayStart);
date.setDate(date.getDate() + i);
planData.push({
day: i === 0 ? '今天' : i === 1 ? '明天' : `${i}天后`,
@@ -433,27 +379,18 @@ Page({
// 统计每天需要复习的古诗数量
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);
+ // 直接使用计算好的 daysUntilNextReview
+ const daysUntilNextReview = poem.daysUntilNextReview;
- // 计算下次复习日期距离今天的天数(使用宽松计算,不四舍五入)
- const daysUntilNextReview = Math.floor((nextReviewDate - now) / (24 * 60 * 60 * 1000));
+ console.log(`《${poem.title}》: ${daysUntilNextReview}天后复习`);
- // 更新对应天数的复习数量
+ // 分配到对应的天数
if (daysUntilNextReview >= 0 && daysUntilNextReview < 7) {
planData[daysUntilNextReview].count++;
}
});
+ console.log('复习计划数据:', planData);
return planData;
},
@@ -578,157 +515,169 @@ Page({
},
/**
- * 绘制复习计划图表
- * 显示未来7天每天需要复习的古诗数量
- */
- 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;
- }
-
- // 设置画布背景
+ * 绘制复习计划图表
+ * 显示未来7天每天需要复习的古诗数量
+ */
+drawReviewPlanChart() {
+ const ctx = wx.createCanvasContext('reviewPlanChart');
+ const { canvasWidth, canvasHeight } = this.data;
+ const data = this.data.复习计划数据;
+
+ // 确保有数据
+ if (!data || data.length === 0 || !this.data.hasRecitationRecords) {
+ // 绘制无数据状态
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.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;
+
+ // 计算Y轴最大值(至少为1,避免除以0)
+ const maxCount = Math.max(...data.map(item => item.count), 1);
+ const maxYAxisValue = Math.max(maxCount, 5); // 确保Y轴至少有5的刻度,避免只有1个数据点时图表太扁
+
+ // 绘制坐标轴
+ ctx.setStrokeStyle('#e0e0e0');
+ ctx.setLineWidth(1);
+
+ // 垂直网格线 - 修正:当只有1个数据点时也要正确绘制
+ const dataPoints = data.length;
+ const xStep = dataPoints > 1 ? plotWidth / (dataPoints - 1) : plotWidth;
+
+ for (let i = 0; i < dataPoints; i++) {
+ const x = padding + (dataPoints > 1 ? (plotWidth / (dataPoints - 1)) * i : 0);
+ ctx.beginPath();
+ ctx.moveTo(x, padding);
+ ctx.lineTo(x, canvasHeight - padding);
+ ctx.stroke();
+ }
+
+ // 水平网格线
+ const yAxisSteps = Math.min(5, maxYAxisValue);
+ 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 + (dataPoints > 1 ? (plotWidth / (dataPoints - 1)) * index : plotWidth / 2);
- // 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);
- });
+ // 绘制日期
+ ctx.fillText(item.dateStr, x, canvasHeight - padding + 15);
- // 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);
- }
+ // 绘制天数
+ ctx.fillText(item.day, x, canvasHeight - padding + 30);
+ });
+
+ // Y轴标签
+ ctx.setTextAlign('right');
+ for (let i = 0; i <= yAxisSteps; i++) {
+ const count = Math.round(maxYAxisValue * (1 - i / yAxisSteps));
+ const y = padding + (plotHeight / yAxisSteps) * i;
+ ctx.fillText(count.toString(), padding - 10, y + 5);
+ }
+
+ // 绘制数据点和连线
+ ctx.setStrokeStyle('#4CAF50');
+ ctx.setLineWidth(2);
+ ctx.beginPath();
+
+ // 先绘制所有点
+ const points = data.map((item, index) => {
+ const x = padding + (dataPoints > 1 ? (plotWidth / (dataPoints - 1)) * index : plotWidth / 2);
+ const valueRatio = item.count / maxYAxisValue;
+ const y = canvasHeight - padding - (valueRatio * plotHeight);
- // 绘制曲线图
- 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();
+ return { x, y, count: item.count };
+ });
+
+ // 绘制连线(只有在有多个数据点时)
+ if (dataPoints > 1) {
+ points.forEach((point, index) => {
+ if (index === 0) {
+ ctx.moveTo(point.x, point.y);
+ } else {
+ // 使用贝塞尔曲线使线条更平滑
+ const prevPoint = points[index - 1];
+ const cpx1 = prevPoint.x + (point.x - prevPoint.x) / 2;
+ const cpy1 = prevPoint.y;
+ const cpx2 = prevPoint.x + (point.x - prevPoint.x) / 2;
+ const cpy2 = point.y;
- // 连接点形成曲线
- 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.bezierCurveTo(cpx1, cpy1, cpx2, cpy2, point.x, point.y);
+ }
});
-
- // 绘制标题
- ctx.setFillStyle('#333333');
- ctx.setFontSize(16);
- ctx.setTextAlign('center');
- ctx.fillText('未来7天复习计划', canvasWidth / 2, padding - 10);
-
- ctx.draw();
- },
+ ctx.stroke();
+ }
- // 删除不再需要的辅助函数,所有功能现在在drawReviewPlanChart中集中实现
+ // 绘制数据点
+ points.forEach(point => {
+ ctx.beginPath();
+ ctx.arc(point.x, point.y, 4, 0, 2 * Math.PI);
+ ctx.fillStyle = '#4CAF50';
+ ctx.fill();
+ });
-
+ // 绘制每个数据点的值
+ ctx.setFillStyle('#333333');
+ ctx.setFontSize(12);
+ ctx.setTextAlign('center');
+
+ points.forEach(point => {
+ // 在点的上方显示数值
+ ctx.fillText(point.count.toString(), point.x, point.y - 10);
+ });
+ // 绘制标题
+ ctx.setFillStyle('#333333');
+ ctx.setFontSize(16);
+ ctx.setTextAlign('center');
+ ctx.fillText('未来7天复习计划', canvasWidth / 2, padding - 10);
+
+ // 如果没有数据点连线,显示提示
+ if (dataPoints === 1) {
+ ctx.setFillStyle('#666666');
+ ctx.setFontSize(12);
+ ctx.fillText('(只有今天有复习计划)', canvasWidth / 2, padding - 25);
+ }
+
+ ctx.draw();
+},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh() {
// 刷新页面数据
- this.drawForgettingCurve();
- wx.stopPullDownRefresh();
+ this.loadPoemsToReview().then(() => {
+ // 数据加载完成后绘制图表
+ this.drawReviewPlanChart();
+ wx.stopPullDownRefresh();
+ }).catch(error => {
+ console.error('下拉刷新失败:', error);
+ wx.stopPullDownRefresh();
+ });
},
/**
diff --git a/pages/review/review.wxml b/pages/review/review.wxml
index 1836a6c..3d28840 100644
--- a/pages/review/review.wxml
+++ b/pages/review/review.wxml
@@ -21,36 +21,38 @@
+
-
- 今日推荐复习
-
-
-
-
-
-
-
- 上次背诵: {{item.daysSinceLastRecite}}天前
- 正确率: {{item.accuracy}}%
- 记忆保留率: {{item.retentionRate}}%
- 下次复习: {{item.nextReviewInterval}}天后
-
-
-
-
-
-
+
+ 今日推荐复习
+
+
+
+
+
+
+
+ 上次背诵: {{item.daysSinceLastRecite}}天前
+ 正确率: {{item.accuracy}}%
+ 记忆保留率: {{item.retentionRate}}%
+ 下次复习: {{item.daysUntilNextReview}}天后
+ 下次复习: 今天
+
+
+
+
+
-
-
-
- 暂无推荐复习的古诗
- 开始学习新古诗后,系统会自动生成复习计划
-
\ No newline at end of file
+
+
+
+ 暂无推荐复习的古诗
+ 开始学习新古诗后,系统会自动生成复习计划
+
+
\ No newline at end of file
diff --git a/pages/search/search.js b/pages/search/search.js
index 0ff533b..c250f0b 100644
--- a/pages/search/search.js
+++ b/pages/search/search.js
@@ -63,10 +63,13 @@ Page({
// 清空输入框
clearInput() {
+ console.log('clearInput called'); // 添加日志调试
this.setData({
keyword: '',
showResults: false,
searchResults: []
+ }, () => {
+ console.log('clearInput completed, keyword:', this.data.keyword); // 确认清空完成
});
},
@@ -142,72 +145,9 @@ Page({
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;
diff --git a/pages/search/search.wxss b/pages/search/search.wxss
index b34eb94..290bf96 100644
--- a/pages/search/search.wxss
+++ b/pages/search/search.wxss
@@ -13,14 +13,7 @@
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%;
@@ -30,20 +23,28 @@
.clear-btn {
position: absolute;
- right: 30rpx;
+ right: 10rpx;
top: 50%;
transform: translateY(-50%);
width: 40rpx;
height: 40rpx;
- background: #f0f0f0;
+ background: #ccc;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
- color: #999;
+ z-index: 10; /* 确保在最上层 */
+}
+
+.clear-btn text {
+ color: #fff;
font-size: 24rpx;
}
+.search-input-wrapper {
+ position: relative;
+ flex: 1;
+}
.search-btn {
background: #07c160;
color: white;
diff --git a/pages/study/study.js b/pages/study/study.js
index 6c575e8..2859802 100644
--- a/pages/study/study.js
+++ b/pages/study/study.js
@@ -14,7 +14,9 @@ Page({
authorInfo: '',
loadError: false,
poemType: '',
- token: ''
+ token: '',
+ isPlaying:false,//音频是否正在播放
+ currentPlayingType: '' //当前播放音频类型
},
onLoad(options) {
@@ -314,41 +316,80 @@ Page({
},
// 语音播报功能(支持不同类型内容的朗读)
- 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);
- },
+speakPoem(e) {
+ const contentType = e?.currentTarget?.dataset?.type || 'all';
+
+ // 如果正在播放相同类型的内容,则停止播放
+ if (this.data.isPlaying && this.data.currentPlayingType === contentType) {
+ this.stopAudio();
+ return;
+ }
+
+ // 如果正在播放其他类型的内容,先停止再播放新的
+ if (this.data.isPlaying) {
+ this.stopAudio();
+ // 给一点延迟确保完全停止
+ setTimeout(() => {
+ this.playAudio(contentType);
+ }, 300);
+ } else {
+ this.playAudio(contentType);
+ }
+},
+
+// 播放音频
+playAudio(contentType) {
+ 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.setData({
+ isPlaying: true,
+ currentPlayingType: contentType
+ });
+
+ // 调用语音合成播放函数
+ this.playstart(textToSpeak);
+},
+// 停止音频播放
+stopAudio() {
+ innerAudioContext.stop();
+ this.setData({
+ isPlaying: false,
+ currentPlayingType: ''
+ });
+
+ wx.showToast({
+ title: '已停止播放',
+ icon: 'success',
+ duration: 1000
+ });
+},
// 分享功能
onShareAppMessage() {
const poem = this.data.poem;
@@ -373,11 +414,17 @@ Page({
},
onHide() {
-
+ console.log('页面隐藏,停止语音播放');
+ if (this.data.isPlaying) {
+ this.stopAudio();
+ }
},
onUnload() {
-
+ console.log('页面卸载,停止语音播放');
+ if (this.data.isPlaying) {
+ this.stopAudio();
+ }
},
onPullDownRefresh() {
diff --git a/project.private.config.json b/project.private.config.json
index 495197f..498471f 100644
--- a/project.private.config.json
+++ b/project.private.config.json
@@ -1,42 +1,70 @@
{
- "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
- }
- ]
+ "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/guiding/guiding",
+ "pathName": "pages/guiding/guiding",
+ "query": "",
+ "scene": null,
+ "launchMode": "default"
+ },
+ {
+ "name": "pages/search/search",
+ "pathName": "pages/search/search",
+ "query": "",
+ "launchMode": "default",
+ "scene": null
+ },
+ {
+ "name": "pages/review/review",
+ "pathName": "pages/review/review",
+ "query": "userInfo=%5Bobject%20Object%5D",
+ "launchMode": "default",
+ "scene": null
+ },
+ {
+ "name": "pages/index/index",
+ "pathName": "pages/index/index",
+ "query": "openid=oXrNu16e8evHW3pAyXuBHPL3lsZo",
+ "launchMode": "default",
+ "scene": null
+ },
+ {
+ "name": "pages/recite/recite",
+ "pathName": "pages/recite/recite",
+ "query": "id=poet_064&poemData=%257B%2522_id%2522%253A%2522poet_064%2522%252C%2522author%2522%253A%2522%25E6%259B%25B9%25E6%25A4%258D%2522%252C%2522author_info%2522%253A%257B%2522intro%2522%253A%2522%25E6%259B%25B9%25E6%25A4%258D%25EF%25BC%2588192%25E5%25B9%25B4%25E2%2580%2594232%25E5%25B9%25B4%25EF%25BC%2589%25EF%25BC%258C%25E5%25AD%2597%25E5%25AD%2590%25E5%25BB%25BA%25EF%25BC%258C%25E4%25B8%2589%25E5%259B%25BD%25E6%2597%25B6%25E6%259C%259F%25E8%2591%2597%25E5%2590%258D%25E6%2596%2587%25E5%25AD%25A6%25E5%25AE%25B6%25EF%25BC%258C%25E5%25BB%25BA%25E5%25AE%2589%25E6%2596%2587%25E5%25AD%25A6%25E7%259A%2584%25E4%25BB%25A3%25E8%25A1%25A8%25E4%25BA%25BA%25E7%2589%25A9%25E3%2580%2582%25E4%25B8%258E%25E6%259B%25B9%25E6%2593%258D%25E3%2580%2581%25E6%259B%25B9%25E4%25B8%2595%25E5%2590%2588%25E7%25A7%25B0'%25E4%25B8%2589%25E6%259B%25B9'%25E3%2580%2582%2522%252C%2522name%2522%253A%2522%25E6%259B%25B9%25E6%25A4%258D%2522%257D%252C%2522background%2522%253A%2522%25E8%25BF%2599%25E9%25A6%2596%25E8%25AF%2597%25E7%2594%25A8%25E5%2590%258C%25E6%25A0%25B9%25E8%2580%258C%25E7%2594%259F%25E7%259A%2584%25E8%2590%2581%25E5%2592%258C%25E8%25B1%2586%25E6%259D%25A5%25E6%25AF%2594%25E5%2596%25BB%25E5%2590%258C%25E7%2588%25B6%25E5%2585%25B1%25E6%25AF%258D%25E7%259A%2584%25E5%2585%2584%25E5%25BC%259F%25EF%25BC%258C%25E7%2594%25A8%25E8%2590%2581%25E7%2585%258E%25E5%2585%25B6%25E8%25B1%2586%25E6%259D%25A5%25E6%25AF%2594%25E5%2596%25BB%25E5%2590%258C%25E8%2583%259E%25E9%25AA%25A8%25E8%2582%2589%25E7%259A%2584%25E5%2593%25A5%25E5%2593%25A5%25E6%259B%25B9%25E4%25B8%2595%25E6%25AE%258B%25E5%25AE%25B3%25E5%25BC%259F%25E5%25BC%259F%25EF%25BC%258C%25E8%25A1%25A8%25E8%25BE%25BE%25E4%25BA%2586%25E5%25AF%25B9%25E6%259B%25B9%25E4%25B8%2595%25E7%259A%2584%25E5%25BC%25BA%25E7%2583%2588%25E4%25B8%258D%25E6%25BB%25A1%25EF%25BC%258C%25E7%2594%259F%25E5%258A%25A8%25E5%25BD%25A2%25E8%25B1%25A1%25E3%2580%2581%25E6%25B7%25B1%25E5%2585%25A5%25E6%25B5%2585%25E5%2587%25BA%25E5%259C%25B0%25E5%258F%258D%25E6%2598%25A0%25E4%25BA%2586%25E5%25B0%2581%25E5%25BB%25BA%25E7%25BB%259F%25E6%25B2%25BB%25E9%259B%2586%25E5%259B%25A2%25E5%2586%2585%25E9%2583%25A8%25E7%259A%2584%25E6%25AE%258B%25E9%2585%25B7%25E6%2596%2597%25E4%25BA%2589%25E5%2592%258C%25E8%25AF%2597%25E4%25BA%25BA%25E8%2587%25AA%25E8%25BA%25AB%25E5%25A4%2584%25E5%25A2%2583%25E8%2589%25B0%25E9%259A%25BE%25EF%25BC%258C%25E6%25B2%2589%25E9%2583%2581%25E6%2584%25A4%25E6%25BF%2580%25E7%259A%2584%25E6%2580%259D%25E6%2583%25B3%25E6%2584%259F%25E6%2583%2585%25E3%2580%2582%2522%252C%2522content%2522%253A%2522%25E7%2585%25AE%25E8%25B1%2586%25E7%2587%2583%25E8%25B1%2586%25E8%2590%2581%25EF%25BC%258C%25E8%25B1%2586%25E5%259C%25A8%25E9%2587%259C%25E4%25B8%25AD%25E6%25B3%25A3%25E3%2580%2582%255Cn%25E6%259C%25AC%25E6%2598%25AF%25E5%2590%258C%25E6%25A0%25B9%25E7%2594%259F%25EF%25BC%258C%25E7%259B%25B8%25E7%2585%258E%25E4%25BD%2595%25E5%25A4%25AA%25E6%2580%25A5%25EF%25BC%259F%2522%252C%2522dynasty%2522%253A%2522%25E4%25B8%2589%25E5%259B%25BD%2522%252C%2522theme%2522%253A%255B%2522%25E5%2585%2584%25E5%25BC%259F%2522%252C%2522%25E6%2594%25BF%25E6%25B2%25BB%2522%255D%252C%2522title%2522%253A%2522%25E4%25B8%2583%25E6%25AD%25A5%25E8%25AF%2597%2522%252C%2522translation%2522%253A%2522%25E9%2594%2585%25E9%2587%258C%25E7%2585%25AE%25E7%259D%2580%25E8%25B1%2586%25E5%25AD%2590%25EF%25BC%258C%25E8%25B1%2586%25E7%25A7%25B8%25E5%259C%25A8%25E9%2594%2585%25E5%25BA%2595%25E4%25B8%258B%25E7%2587%2583%25E7%2583%25A7%25EF%25BC%258C%25E8%25B1%2586%25E5%25AD%2590%25E5%259C%25A8%25E9%2594%2585%25E9%2587%258C%25E9%259D%25A2%25E5%2593%25AD%25E6%25B3%25A3%25E3%2580%2582%25E8%25B1%2586%25E5%25AD%2590%25E5%2592%258C%25E8%25B1%2586%25E7%25A7%25B8%25E6%259C%25AC%25E6%259D%25A5%25E6%2598%25AF%25E5%2590%258C%25E4%25B8%2580%25E6%259D%25A1%25E6%25A0%25B9%25E4%25B8%258A%25E7%2594%259F%25E9%2595%25BF%25E5%2587%25BA%25E6%259D%25A5%25E7%259A%2584%25EF%25BC%258C%25E8%25B1%2586%25E7%25A7%25B8%25E6%2580%258E%25E8%2583%25BD%25E8%25BF%2599%25E6%25A0%25B7%25E6%2580%25A5%25E8%25BF%25AB%25E5%259C%25B0%25E7%2585%258E%25E7%2586%25AC%25E8%25B1%2586%25E5%25AD%2590%25E5%2591%25A2%25EF%25BC%259F%2522%252C%2522type%2522%253A%2522%25E8%25AF%2597%2522%257D",
+ "launchMode": "default",
+ "scene": null
+ },
+ {
+ "name": "pages/study/study",
+ "pathName": "pages/study/study",
+ "query": "id=poet_064",
+ "launchMode": "default",
+ "scene": null
}
+ ]
}
+ }
}
\ No newline at end of file
diff --git a/study_helper b/study_helper
deleted file mode 160000
index aa1ac34..0000000
--- a/study_helper
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit aa1ac34077d7f850e81caa893d9af18074c7ce33