From 021f56a83f3b5d4a483d3a40d6324c0d3240df4f Mon Sep 17 00:00:00 2001 From: Yuzhiran Dev Date: Fri, 10 Jul 2026 16:42:48 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=AE=8C=E6=95=B4=E7=89=88=20uni-app?= =?UTF-8?q?=20=E5=89=8D=E7=AB=AF=20+=20=E5=90=8E=E7=AB=AF=20API=20?= =?UTF-8?q?=E6=94=B9=E9=80=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 后端: - 新增知识搜索 API GET /api/knowledge/search - 成就 API 改造为 AI 主题(知识探索/收藏/AI问答/社交) 前端新增 7 个页面: - favorites (我的收藏) - achievements (成就) - leaderboard (排行榜) - search (搜索) - progress (学习进度) - feedback (意见反馈) - settings (设置) - news (AI资讯详情) 完善: - 个人中心导航全部接真实页面,清除所有'开发中'stub - index 搜索按钮 → search 页 - trend 卡片点击 → news 详情页 - api.js 新增 feedbackApi + knowledgeApi.search - userApi.achievements 端点 构建验证: ✅ DONE Build complete (16 pages, 16 bundles) --- backend/wdkj-server/src/routes/knowledge.js | 39 ++ backend/wdkj-server/src/routes/user.js | 402 +++++++-------- frontend/ai-dimension/src/pages.json | 8 + .../src/pages/achievements/achievements.vue | 354 +++++++++++++ .../src/pages/favorites/favorites.vue | 249 +++++++++ .../src/pages/feedback/feedback.vue | 458 +++++++++++++++++ .../ai-dimension/src/pages/index/index.vue | 2 +- .../src/pages/leaderboard/leaderboard.vue | 320 ++++++++++++ frontend/ai-dimension/src/pages/news/news.vue | 356 +++++++++++++ .../src/pages/profile/profile.vue | 26 +- .../src/pages/progress/progress.vue | 484 ++++++++++++++++++ .../ai-dimension/src/pages/search/search.vue | 386 ++++++++++++++ .../src/pages/settings/settings.vue | 195 +++++++ .../ai-dimension/src/pages/trend/trend.vue | 3 +- frontend/ai-dimension/src/utils/api.js | 34 +- 15 files changed, 3108 insertions(+), 208 deletions(-) create mode 100644 frontend/ai-dimension/src/pages/achievements/achievements.vue create mode 100644 frontend/ai-dimension/src/pages/favorites/favorites.vue create mode 100644 frontend/ai-dimension/src/pages/feedback/feedback.vue create mode 100644 frontend/ai-dimension/src/pages/leaderboard/leaderboard.vue create mode 100644 frontend/ai-dimension/src/pages/news/news.vue create mode 100644 frontend/ai-dimension/src/pages/progress/progress.vue create mode 100644 frontend/ai-dimension/src/pages/search/search.vue create mode 100644 frontend/ai-dimension/src/pages/settings/settings.vue diff --git a/backend/wdkj-server/src/routes/knowledge.js b/backend/wdkj-server/src/routes/knowledge.js index 3173f27..f5e70c5 100755 --- a/backend/wdkj-server/src/routes/knowledge.js +++ b/backend/wdkj-server/src/routes/knowledge.js @@ -5,6 +5,45 @@ const ApiResponse = require('../utils/response') const { userAuthMiddleware } = require('../middleware/auth') const logger = require('../utils/logger') +/** + * 搜索知识 + * GET /api/knowledge/search?q=xxx&page=1&limit=20 + */ +router.get('/search', async (req, res) => { + try { + const { q, page = 1, limit = 20 } = req.query + if (!q || q.trim().length === 0) { + return ApiResponse.error(res, '请输入搜索关键词', 400) + } + + const query = { + status: 'approved', + $or: [ + { title: { $regex: q, $options: 'i' } }, + { content: { $regex: q, $options: 'i' } }, + { tags: { $regex: q, $options: 'i' } } + ] + } + + const total = await Knowledge.countDocuments(query) + const result = await Knowledge.find(query) + .sort({ viewCount: -1, createdAt: -1 }) + .skip((parseInt(page) - 1) * parseInt(limit)) + .limit(parseInt(limit)) + .lean() + + return ApiResponse.paginated(res, result, { + page: parseInt(page), + limit: parseInt(limit), + total + }) + + } catch (error) { + logger.error('搜索知识失败:', error) + return ApiResponse.serverError(res, error.message) + } +}) + /** * 获取知识列表 * GET /api/knowledge diff --git a/backend/wdkj-server/src/routes/user.js b/backend/wdkj-server/src/routes/user.js index 55081a2..249de6d 100755 --- a/backend/wdkj-server/src/routes/user.js +++ b/backend/wdkj-server/src/routes/user.js @@ -202,133 +202,133 @@ router.get('/achievements', async (req, res) => { try { const user = req.user - // 生成成就数据 - const achievements = [ - { - name: '探索成就', - achievements: [ + // 生成成就数据(AI 维度主题) + const achievements = [ { - id: 'dim1_complete', - name: '一维探索者', - description: '完成一维空间的探索', - icon: '⭐', - unlocked: user.exploreData.dim1?.completed || false, - date: user.exploreData.dim1?.completed ? user.updatedAt.toISOString().split('T')[0] : null + name: '知识探索', + achievements: [ + { + id: 'dim1_read', + name: 'AI起源探索者', + description: '完成 AI 起源维度的知识阅读', + icon: '📜', + unlocked: user.exploreData.dim1?.completed || false, + date: user.exploreData.dim1?.completed ? user.updatedAt.toISOString().split('T')[0] : null + }, + { + id: 'dim2_read', + name: 'AI发展探索者', + description: '完成 AI 发展维度的知识阅读', + icon: '🏛', + unlocked: user.exploreData.dim2?.completed || false, + date: user.exploreData.dim2?.completed ? user.updatedAt.toISOString().split('T')[0] : null + }, + { + id: 'dim3_read', + name: 'AI当前探索者', + description: '完成 AI 当前格局维度的知识阅读', + icon: '♟', + unlocked: user.exploreData.dim3?.completed || false, + date: user.exploreData.dim3?.completed ? user.updatedAt.toISOString().split('T')[0] : null + }, + { + id: 'dim4_read', + name: 'AI学习探索者', + description: '完成 AI 学习与工具维度的知识阅读', + icon: '🧠', + unlocked: user.exploreData.dim4?.completed || false, + date: user.exploreData.dim4?.completed ? user.updatedAt.toISOString().split('T')[0] : null + }, + { + id: 'dim5_read', + name: 'AI趋势探索者', + description: '完成 AI 趋势追踪维度的知识阅读', + icon: '⚡', + unlocked: user.exploreData.dim5?.completed || false, + date: user.exploreData.dim5?.completed ? user.updatedAt.toISOString().split('T')[0] : null + } + ] }, { - id: 'dim2_complete', - name: '二维探索者', - description: '完成二维空间的探索', - icon: '✨', - unlocked: user.exploreData.dim2?.completed || false, - date: user.exploreData.dim2?.completed ? user.updatedAt.toISOString().split('T')[0] : null + name: '收藏成就', + achievements: [ + { + id: 'first_collection', + name: '初次收藏', + description: '收藏你的第一个 AI 知识', + icon: '❤️', + unlocked: user.collectedKnowledge?.length > 0, + date: user.collectedKnowledge?.length > 0 ? user.updatedAt.toISOString().split('T')[0] : null + }, + { + id: 'five_collections', + name: '收藏爱好者', + description: '收藏5个 AI 知识', + icon: '🧡', + unlocked: user.collectedKnowledge?.length >= 5, + date: user.collectedKnowledge?.length >= 5 ? user.updatedAt.toISOString().split('T')[0] : null + }, + { + id: 'ten_collections', + name: '收藏大师', + description: '收藏10个 AI 知识', + icon: '💛', + unlocked: user.collectedKnowledge?.length >= 10, + date: user.collectedKnowledge?.length >= 10 ? user.updatedAt.toISOString().split('T')[0] : null + } + ] }, { - id: 'dim3_complete', - name: '三维探索者', - description: '完成三维空间的探索', - icon: '🌟', - unlocked: user.exploreData.dim3?.completed || false, - date: user.exploreData.dim3?.completed ? user.updatedAt.toISOString().split('T')[0] : null + name: 'AI 问答', + achievements: [ + { + id: 'first_ask', + name: '初次提问', + description: '向 AI 助手提出第一个问题', + icon: '🤖', + unlocked: user.achievements?.includes('first_ask') || false, + date: user.achievements?.includes('first_ask') ? user.updatedAt.toISOString().split('T')[0] : null + }, + { + id: 'ten_asks', + name: '勤学好问', + description: '向 AI 助手提出 10 个问题', + icon: '💡', + unlocked: user.achievements?.includes('ten_asks') || false, + date: user.achievements?.includes('ten_asks') ? user.updatedAt.toISOString().split('T')[0] : null + }, + { + id: 'fifty_asks', + name: 'AI 达人', + description: '向 AI 助手提出 50 个问题', + icon: '🧠', + unlocked: user.achievements?.includes('fifty_asks') || false, + date: user.achievements?.includes('fifty_asks') ? user.updatedAt.toISOString().split('T')[0] : null + } + ] }, { - id: 'dim4_complete', - name: '四维探索者', - description: '完成四维空间的探索', - icon: '💫', - unlocked: user.exploreData.dim4?.completed || false, - date: user.exploreData.dim4?.completed ? user.updatedAt.toISOString().split('T')[0] : null - }, - { - id: 'dim5_complete', - name: '五维探索者', - description: '完成五维空间的探索', - icon: '🌌', - unlocked: user.exploreData.dim5?.completed || false, - date: user.exploreData.dim5?.completed ? user.updatedAt.toISOString().split('T')[0] : null + name: '社交成就', + achievements: [ + { + id: 'first_share', + name: '初次分享', + description: '分享你的第一个知识给朋友', + icon: '📤', + unlocked: user.achievements?.includes('first_share') || false, + date: user.achievements?.includes('first_share') ? user.updatedAt.toISOString().split('T')[0] : null + }, + { + id: 'five_shares', + name: '分享达人', + description: '分享 5 次知识给朋友', + icon: '📣', + unlocked: user.achievements?.includes('five_shares') || false, + date: user.achievements?.includes('five_shares') ? user.updatedAt.toISOString().split('T')[0] : null + } + ] } ] - }, - { - name: '创作成就', - achievements: [ - { - id: 'first_work', - name: '初次创作', - description: '创作你的第一个作品', - icon: '🎨', - unlocked: user.achievements?.includes('first_work') || false, - date: user.achievements?.includes('first_work') ? user.updatedAt.toISOString().split('T')[0] : null - }, - { - id: 'five_works', - name: '创作达人', - description: '创作5个作品', - icon: '🏆', - unlocked: user.achievements?.includes('five_works') || false, - date: user.achievements?.includes('five_works') ? user.updatedAt.toISOString().split('T')[0] : null - }, - { - id: 'ten_works', - name: '创作大师', - description: '创作10个作品', - icon: '👑', - unlocked: user.achievements?.includes('ten_works') || false, - date: user.achievements?.includes('ten_works') ? user.updatedAt.toISOString().split('T')[0] : null - } - ] - }, - { - name: '收藏成就', - achievements: [ - { - id: 'first_collection', - name: '初次收藏', - description: '收藏你的第一个知识', - icon: '❤️', - unlocked: user.collectedKnowledge?.length > 0, - date: user.collectedKnowledge?.length > 0 ? user.updatedAt.toISOString().split('T')[0] : null - }, - { - id: 'five_collections', - name: '收藏爱好者', - description: '收藏5个知识', - icon: '🧡', - unlocked: user.collectedKnowledge?.length >= 5, - date: user.collectedKnowledge?.length >= 5 ? user.updatedAt.toISOString().split('T')[0] : null - }, - { - id: 'ten_collections', - name: '收藏大师', - description: '收藏10个知识', - icon: '💛', - unlocked: user.collectedKnowledge?.length >= 10, - date: user.collectedKnowledge?.length >= 10 ? user.updatedAt.toISOString().split('T')[0] : null - } - ] - }, - { - name: '社交成就', - achievements: [ - { - id: 'first_share', - name: '初次分享', - description: '分享你的第一个作品', - icon: '📤', - unlocked: user.achievements?.includes('first_share') || false, - date: user.achievements?.includes('first_share') ? user.updatedAt.toISOString().split('T')[0] : null - }, - { - id: 'five_shares', - name: '分享达人', - description: '分享5个作品', - icon: '📣', - unlocked: user.achievements?.includes('five_shares') || false, - date: user.achievements?.includes('five_shares') ? user.updatedAt.toISOString().split('T')[0] : null - } - ] - } - ] return ApiResponse.success(res, achievements) @@ -338,87 +338,87 @@ router.get('/achievements', async (req, res) => { } }) -/** - * 获取用户统计数据 - * GET /api/user/stats - */ -router.get('/stats', async (req, res) => { - try { - const user = req.user - const { Gallery } = require('../models') - - // 获取用户作品数 - const worksCount = await Gallery.countDocuments({ userId: user._id }) - - // 获取等级信息 - const levelInfo = user.getLevelInfo() - - // 计算探索时长(分钟转小时,保留1位小数) - const playTimeHours = Math.round((user.totalPlayTime || 0) / 60 * 10) / 10 - - return ApiResponse.success(res, { - level: levelInfo.level, - levelName: levelInfo.name, - levelPoints: levelInfo.points, - levelProgress: levelInfo.progress, - totalProgress: levelInfo.totalProgress, - nextLevelPoints: levelInfo.nextLevelPoints, - totalPlayTime: playTimeHours, - totalScore: user.totalScore || 0, - worksCount: worksCount, - collectionCount: user.collectedKnowledge?.length || 0, - achievementCount: user.achievements?.length || 0, - unlockedDims: user.unlockedDims || [1] - }) - - } catch (error) { - logger.error('获取用户统计失败:', error) - return ApiResponse.serverError(res, error.message) - } -}) - -/** - * 获取用户作品列表 - * GET /api/user/works - */ -router.get('/works', async (req, res) => { - try { - const user = req.user - const { page = 1, limit = 20 } = req.query - const { Gallery } = require('../models') - - const query = { userId: user._id } - - const total = await Gallery.countDocuments(query) - const works = await Gallery.find(query) - .sort({ createdAt: -1 }) - .skip((parseInt(page) - 1) * parseInt(limit)) - .limit(parseInt(limit)) - .lean() - - // 格式化作品数据 - const formattedWorks = works.map(work => ({ - id: work._id, - title: work.title, - description: work.description, - imageUrl: work.imageUrl, - dimension: work.dimension, - status: work.status, - likes: work.likes || 0, - views: work.views || 0, - createdAt: work.createdAt - })) - - return ApiResponse.paginated(res, formattedWorks, { - page: parseInt(page), - limit: parseInt(limit), - total - }) - - } catch (error) { - logger.error('获取用户作品失败:', error) - return ApiResponse.serverError(res, error.message) - } -}) - +/** + * 获取用户统计数据 + * GET /api/user/stats + */ +router.get('/stats', async (req, res) => { + try { + const user = req.user + const { Gallery } = require('../models') + + // 获取用户作品数 + const worksCount = await Gallery.countDocuments({ userId: user._id }) + + // 获取等级信息 + const levelInfo = user.getLevelInfo() + + // 计算探索时长(分钟转小时,保留1位小数) + const playTimeHours = Math.round((user.totalPlayTime || 0) / 60 * 10) / 10 + + return ApiResponse.success(res, { + level: levelInfo.level, + levelName: levelInfo.name, + levelPoints: levelInfo.points, + levelProgress: levelInfo.progress, + totalProgress: levelInfo.totalProgress, + nextLevelPoints: levelInfo.nextLevelPoints, + totalPlayTime: playTimeHours, + totalScore: user.totalScore || 0, + worksCount: worksCount, + collectionCount: user.collectedKnowledge?.length || 0, + achievementCount: user.achievements?.length || 0, + unlockedDims: user.unlockedDims || [1] + }) + + } catch (error) { + logger.error('获取用户统计失败:', error) + return ApiResponse.serverError(res, error.message) + } +}) + +/** + * 获取用户作品列表 + * GET /api/user/works + */ +router.get('/works', async (req, res) => { + try { + const user = req.user + const { page = 1, limit = 20 } = req.query + const { Gallery } = require('../models') + + const query = { userId: user._id } + + const total = await Gallery.countDocuments(query) + const works = await Gallery.find(query) + .sort({ createdAt: -1 }) + .skip((parseInt(page) - 1) * parseInt(limit)) + .limit(parseInt(limit)) + .lean() + + // 格式化作品数据 + const formattedWorks = works.map(work => ({ + id: work._id, + title: work.title, + description: work.description, + imageUrl: work.imageUrl, + dimension: work.dimension, + status: work.status, + likes: work.likes || 0, + views: work.views || 0, + createdAt: work.createdAt + })) + + return ApiResponse.paginated(res, formattedWorks, { + page: parseInt(page), + limit: parseInt(limit), + total + }) + + } catch (error) { + logger.error('获取用户作品失败:', error) + return ApiResponse.serverError(res, error.message) + } +}) + module.exports = router diff --git a/frontend/ai-dimension/src/pages.json b/frontend/ai-dimension/src/pages.json index 00167bf..2b2c9ba 100644 --- a/frontend/ai-dimension/src/pages.json +++ b/frontend/ai-dimension/src/pages.json @@ -4,6 +4,14 @@ {"path": "pages/login/login", "style": {"navigationBarTitleText": "登录", "navigationStyle": "custom"}}, {"path": "pages/detail/detail", "style": {"navigationBarTitleText": "AI 知识", "navigationStyle": "custom"}}, {"path": "pages/payment/payment", "style": {"navigationBarTitleText": "订阅升级", "navigationStyle": "custom"}}, + {"path": "pages/favorites/favorites", "style": {"navigationBarTitleText": "我的收藏", "navigationStyle": "custom"}}, + {"path": "pages/achievements/achievements", "style": {"navigationBarTitleText": "成就", "navigationStyle": "custom"}}, + {"path": "pages/leaderboard/leaderboard", "style": {"navigationBarTitleText": "排行榜", "navigationStyle": "custom"}}, + {"path": "pages/search/search", "style": {"navigationBarTitleText": "搜索", "navigationStyle": "custom"}}, + {"path": "pages/progress/progress", "style": {"navigationBarTitleText": "学习进度", "navigationStyle": "custom"}}, + {"path": "pages/feedback/feedback", "style": {"navigationBarTitleText": "意见反馈", "navigationStyle": "custom"}}, + {"path": "pages/settings/settings", "style": {"navigationBarTitleText": "设置", "navigationStyle": "custom"}}, + {"path": "pages/news/news", "style": {"navigationBarTitleText": "AI 资讯", "navigationStyle": "custom"}}, {"path": "pages/dimension/dimension", "style": {"navigationBarTitleText": "", "navigationStyle": "custom"}}, {"path": "pages/chat/chat", "style": {"navigationBarTitleText": "AI 问答", "navigationStyle": "custom"}}, {"path": "pages/trend/trend", "style": {"navigationBarTitleText": "AI 趋势", "navigationStyle": "custom"}}, diff --git a/frontend/ai-dimension/src/pages/achievements/achievements.vue b/frontend/ai-dimension/src/pages/achievements/achievements.vue new file mode 100644 index 0000000..391521c --- /dev/null +++ b/frontend/ai-dimension/src/pages/achievements/achievements.vue @@ -0,0 +1,354 @@ + + + + + diff --git a/frontend/ai-dimension/src/pages/favorites/favorites.vue b/frontend/ai-dimension/src/pages/favorites/favorites.vue new file mode 100644 index 0000000..0b5165e --- /dev/null +++ b/frontend/ai-dimension/src/pages/favorites/favorites.vue @@ -0,0 +1,249 @@ + + + + + diff --git a/frontend/ai-dimension/src/pages/feedback/feedback.vue b/frontend/ai-dimension/src/pages/feedback/feedback.vue new file mode 100644 index 0000000..351204e --- /dev/null +++ b/frontend/ai-dimension/src/pages/feedback/feedback.vue @@ -0,0 +1,458 @@ + + + + + diff --git a/frontend/ai-dimension/src/pages/index/index.vue b/frontend/ai-dimension/src/pages/index/index.vue index e898d66..5a9bc10 100644 --- a/frontend/ai-dimension/src/pages/index/index.vue +++ b/frontend/ai-dimension/src/pages/index/index.vue @@ -140,7 +140,7 @@ function goProfile() { } function onSearch() { - uni.showToast({ title: '搜索功能开发中', icon: 'none' }) + uni.navigateTo({ url: '/pages/search/search' }) } function onNotify() { diff --git a/frontend/ai-dimension/src/pages/leaderboard/leaderboard.vue b/frontend/ai-dimension/src/pages/leaderboard/leaderboard.vue new file mode 100644 index 0000000..c6128d7 --- /dev/null +++ b/frontend/ai-dimension/src/pages/leaderboard/leaderboard.vue @@ -0,0 +1,320 @@ + + + + + diff --git a/frontend/ai-dimension/src/pages/news/news.vue b/frontend/ai-dimension/src/pages/news/news.vue new file mode 100644 index 0000000..d465d6f --- /dev/null +++ b/frontend/ai-dimension/src/pages/news/news.vue @@ -0,0 +1,356 @@ + + + + + diff --git a/frontend/ai-dimension/src/pages/profile/profile.vue b/frontend/ai-dimension/src/pages/profile/profile.vue index 0dd2a09..12762b2 100644 --- a/frontend/ai-dimension/src/pages/profile/profile.vue +++ b/frontend/ai-dimension/src/pages/profile/profile.vue @@ -72,6 +72,16 @@ 成就 + + 🥇 + 排行榜 + + + + 💬 + 意见反馈 + + ℹ️ 关于 AI 维度 @@ -234,15 +244,23 @@ function goShare() { } function goFavorites() { - uni.showToast({ title: '收藏页面开发中', icon: 'none' }) + uni.navigateTo({ url: '/pages/favorites/favorites' }) } function goProgress() { - uni.showToast({ title: '学习进度开发中', icon: 'none' }) + uni.navigateTo({ url: '/pages/progress/progress' }) } function goAchievements() { - uni.showToast({ title: '成就系统开发中', icon: 'none' }) + uni.navigateTo({ url: '/pages/achievements/achievements' }) +} + +function goLeaderboard() { + uni.navigateTo({ url: '/pages/leaderboard/leaderboard' }) +} + +function goFeedback() { + uni.navigateTo({ url: '/pages/feedback/feedback' }) } function goAbout() { @@ -254,7 +272,7 @@ function goAbout() { } function goSettings() { - uni.showToast({ title: '设置功能开发中', icon: 'none' }) + uni.navigateTo({ url: '/pages/settings/settings' }) } diff --git a/frontend/ai-dimension/src/pages/progress/progress.vue b/frontend/ai-dimension/src/pages/progress/progress.vue new file mode 100644 index 0000000..3e90de9 --- /dev/null +++ b/frontend/ai-dimension/src/pages/progress/progress.vue @@ -0,0 +1,484 @@ + + + + + diff --git a/frontend/ai-dimension/src/pages/search/search.vue b/frontend/ai-dimension/src/pages/search/search.vue new file mode 100644 index 0000000..b766027 --- /dev/null +++ b/frontend/ai-dimension/src/pages/search/search.vue @@ -0,0 +1,386 @@ + + + + + diff --git a/frontend/ai-dimension/src/pages/settings/settings.vue b/frontend/ai-dimension/src/pages/settings/settings.vue new file mode 100644 index 0000000..ff015ef --- /dev/null +++ b/frontend/ai-dimension/src/pages/settings/settings.vue @@ -0,0 +1,195 @@ + + + + + diff --git a/frontend/ai-dimension/src/pages/trend/trend.vue b/frontend/ai-dimension/src/pages/trend/trend.vue index a4cf896..ad02e1b 100644 --- a/frontend/ai-dimension/src/pages/trend/trend.vue +++ b/frontend/ai-dimension/src/pages/trend/trend.vue @@ -144,7 +144,8 @@ function goDetail(item) { }) return } - uni.showToast({ title: '详情页面开发中', icon: 'none' }) + uni.setStorageSync('__news_item__', JSON.stringify(item)) + uni.navigateTo({ url: '/pages/news/news' }) } function getSampleData() { diff --git a/frontend/ai-dimension/src/utils/api.js b/frontend/ai-dimension/src/utils/api.js index 9f4041b..c4662c5 100644 --- a/frontend/ai-dimension/src/utils/api.js +++ b/frontend/ai-dimension/src/utils/api.js @@ -95,6 +95,16 @@ export const knowledgeApi = { /** 获取维度问答 */ questions: (dim) => request('GET', `/api/knowledge/${dim}/questions`), + + /** 搜索知识 */ + search: (q, options = {}) => { + const params = { q, ...options } + const query = Object.entries(params) + .filter(([_, v]) => v !== undefined && v !== null) + .map(([k, v]) => `${k}=${encodeURIComponent(v)}`) + .join('&') + return request('GET', `/api/knowledge/search${query ? '?' + query : ''}`) + }, } /** @@ -129,6 +139,14 @@ export const userApi = { /** 排行榜 */ leaderboard: (dim = null) => request('GET', `/api/user/leaderboard${dim ? '?dim=' + dim : ''}`), + + /** 获取成就 */ + achievements: (token) => + request('GET', '/api/user/achievements', null, token), + + /** 用户统计 */ + stats: (token) => + request('GET', '/api/user/stats', null, token), } /** @@ -162,10 +180,24 @@ export const trendApi = { request('GET', `/api/trend/${id}`), } +/** + * 意见反馈 + */ +export const feedbackApi = { + /** 提交反馈 */ + create: (data, token) => + request('POST', '/api/feedback', data, token), + /** 我的反馈列表 */ + my: (token) => + request('GET', '/api/feedback/my', null, token), +} + export default { auth: authApi, knowledge: knowledgeApi, aiChat: aiChatApi, user: userApi, - payment: paymentApi + payment: paymentApi, + trend: trendApi, + feedback: feedbackApi } \ No newline at end of file