From a62c2a3aa1918b19149929e05da62db9660dddfb Mon Sep 17 00:00:00 2001 From: wsy182 <2392948297@qq.com> Date: Fri, 27 Feb 2026 13:15:37 +0800 Subject: [PATCH] feat: paginate/search words and improve review flow --- memora-api/internal/handler/word.go | 3 +- .../internal/repository/word_repository.go | 15 +++- memora-api/internal/service/word.go | 4 +- memora-web/src/services/api/words.ts | 2 +- memora-web/src/views/Review.vue | 44 +++++++++-- memora-web/src/views/Words.vue | 76 +++++++++++++++---- 6 files changed, 116 insertions(+), 28 deletions(-) diff --git a/memora-api/internal/handler/word.go b/memora-api/internal/handler/word.go index 7925862..a1ef779 100644 --- a/memora-api/internal/handler/word.go +++ b/memora-api/internal/handler/word.go @@ -100,8 +100,9 @@ func (h *WordHandler) SubmitReview(c *gin.Context) { func (h *WordHandler) GetWords(c *gin.Context) { limit, _ := strconv.Atoi(c.DefaultQuery("limit", "20")) offset, _ := strconv.Atoi(c.DefaultQuery("offset", "0")) + query := strings.TrimSpace(c.DefaultQuery("q", "")) - words, total, err := h.wordService.GetAllWords(limit, offset) + words, total, err := h.wordService.GetAllWords(limit, offset, query) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return diff --git a/memora-api/internal/repository/word_repository.go b/memora-api/internal/repository/word_repository.go index a5b8692..978f587 100644 --- a/memora-api/internal/repository/word_repository.go +++ b/memora-api/internal/repository/word_repository.go @@ -26,11 +26,20 @@ func (r *WordRepository) Create(word *model.Word) error { return r.db.Create(word).Error } -func (r *WordRepository) List(limit, offset int) ([]model.Word, int64, error) { +func (r *WordRepository) List(limit, offset int, query string) ([]model.Word, int64, error) { var words []model.Word var total int64 - r.db.Model(&model.Word{}).Count(&total) - if err := r.db.Limit(limit).Offset(offset).Order("created_at DESC").Find(&words).Error; err != nil { + + db := r.db.Model(&model.Word{}) + if query != "" { + like := "%" + query + "%" + db = db.Where("word LIKE ? OR definition LIKE ?", like, like) + } + + if err := db.Count(&total).Error; err != nil { + return nil, 0, err + } + if err := db.Limit(limit).Offset(offset).Order("created_at DESC").Find(&words).Error; err != nil { return nil, 0, err } return words, total, nil diff --git a/memora-api/internal/service/word.go b/memora-api/internal/service/word.go index ca38647..18f21e0 100644 --- a/memora-api/internal/service/word.go +++ b/memora-api/internal/service/word.go @@ -412,8 +412,8 @@ func (s *WordService) SubmitReviewAnswer(userID int64, req request.ReviewAnswerR } // 获取所有单词 -func (s *WordService) GetAllWords(limit, offset int) ([]model.Word, int64, error) { - return s.wordRepo.List(limit, offset) +func (s *WordService) GetAllWords(limit, offset int, query string) ([]model.Word, int64, error) { + return s.wordRepo.List(limit, offset, strings.TrimSpace(query)) } // 获取记忆统计 diff --git a/memora-web/src/services/api/words.ts b/memora-web/src/services/api/words.ts index fd5bb89..4c5314a 100644 --- a/memora-web/src/services/api/words.ts +++ b/memora-web/src/services/api/words.ts @@ -6,7 +6,7 @@ export async function addWord(word: string) { return res.data } -export async function getWords(params: { limit?: number; offset?: number } = {}) { +export async function getWords(params: { limit?: number; offset?: number; q?: string } = {}) { const res = await http.get<{ data: Word[]; total: number }>('/words', { params }) return res.data } diff --git a/memora-web/src/views/Review.vue b/memora-web/src/views/Review.vue index 44fd47b..dcc6ab3 100644 --- a/memora-web/src/views/Review.vue +++ b/memora-web/src/views/Review.vue @@ -29,16 +29,36 @@ - -
- 提交 + + +
+ 提交 + 下一题 换一个
- + + +
@@ -53,10 +73,11 @@ import { audioUrl, getReviewWords, submitReview } from '../services/api' import type { MemoryRecord, ReviewMode, ReviewResult } from '../services/api' const mode = ref('spelling') -const record = ref(null) // MemoryRecord (含 word) +const record = ref(null) const answer = ref('') const loading = ref(false) const result = ref(null) +const submitted = ref(false) const modeHint = computed(() => { if (mode.value === 'spelling') return '听读音,拼写单词' @@ -66,12 +87,17 @@ const modeHint = computed(() => { async function loadOne() { result.value = null + submitted.value = false answer.value = '' - const res = await getReviewWords({ mode: mode.value, limit: 1 }) + const res = await getReviewWords({ mode: mode.value, limit: 1 }) const arr = (res as any).data ?? (res as any) record.value = Array.isArray(arr) && arr.length ? (arr[0] as MemoryRecord) : null } +function nextOne() { + loadOne().catch(console.error) +} + function play() { if (!record.value?.word?.word) return const a = new Audio(audioUrl(record.value.word.word, 'uk')) @@ -79,12 +105,14 @@ function play() { } async function submit() { - if (!record.value) return + if (!record.value || submitted.value) return if (!answer.value.trim()) return + loading.value = true try { const res = await submitReview({ recordId: record.value.id, answer: answer.value, mode: mode.value }) result.value = (res as any).data ?? (res as any) + submitted.value = true } finally { loading.value = false } diff --git a/memora-web/src/views/Words.vue b/memora-web/src/views/Words.vue index 852c969..cdefe6c 100644 --- a/memora-web/src/views/Words.vue +++ b/memora-web/src/views/Words.vue @@ -1,32 +1,82 @@