fix: add pagination to WordsView (50 words per page)
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 9s
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 9s
This commit is contained in:
parent
f8eaaedcab
commit
75036d3d82
@ -3,7 +3,7 @@ import axios from 'axios'
|
|||||||
const api = axios.create({ baseURL: '/api' })
|
const api = axios.create({ baseURL: '/api' })
|
||||||
|
|
||||||
export const wordsApi = {
|
export const wordsApi = {
|
||||||
getAll: (category) => api.get('/words', { params: { category } }),
|
getAll: (category, page = 1, limit = 50) => api.get('/words', { params: { category, page, limit } }),
|
||||||
getDaily: () => api.get('/words/daily'),
|
getDaily: () => api.get('/words/daily'),
|
||||||
getQuiz: (count = 5) => api.get('/words/quiz', { params: { count } }),
|
getQuiz: (count = 5) => api.get('/words/quiz', { params: { count } }),
|
||||||
getCategories: () => api.get('/words/categories'),
|
getCategories: () => api.get('/words/categories'),
|
||||||
|
|||||||
@ -8,20 +8,30 @@
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="loading" class="loading">불러오는 중...</div>
|
<div v-if="loading" class="loading">불러오는 중...</div>
|
||||||
<div v-else class="card-grid">
|
<div v-else>
|
||||||
<div v-for="word in words" :key="word.id" class="card word-card">
|
<div style="margin-bottom:1rem;color:#718096;font-size:0.875rem">
|
||||||
<div style="display:flex;justify-content:space-between;align-items:flex-start">
|
전체 {{ total }}개 단어 중 {{ (page - 1) * limit + 1 }}–{{ Math.min(page * limit, total) }}번째
|
||||||
<div class="word-en">{{ word.english }}</div>
|
</div>
|
||||||
<span class="badge badge-blue">{{ word.difficulty }}</span>
|
<div class="card-grid">
|
||||||
</div>
|
<div v-for="word in words" :key="word.id" class="card word-card">
|
||||||
<div v-if="word.pronunciation" class="word-pron">[{{ word.pronunciation }}]</div>
|
<div style="display:flex;justify-content:space-between;align-items:flex-start">
|
||||||
<div class="word-ko">{{ word.korean }}</div>
|
<div class="word-en">{{ word.english }}</div>
|
||||||
<div v-if="word.example_en" class="word-example">
|
<span class="badge badge-blue">{{ word.difficulty }}</span>
|
||||||
{{ word.example_en }}<br/>
|
</div>
|
||||||
<span style="color:#a0aec0">{{ word.example_ko }}</span>
|
<div v-if="word.pronunciation" class="word-pron">[{{ word.pronunciation }}]</div>
|
||||||
</div>
|
<div class="word-ko">{{ word.korean }}</div>
|
||||||
|
<div v-if="word.example_en" class="word-example">
|
||||||
|
{{ word.example_en }}<br/>
|
||||||
|
<span style="color:#a0aec0">{{ word.example_ko }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-if="!words.length" class="empty">단어가 없습니다</div>
|
||||||
|
</div>
|
||||||
|
<div v-if="totalPages > 1" style="display:flex;justify-content:center;align-items:center;gap:0.75rem;margin-top:2rem">
|
||||||
|
<button class="btn btn-secondary" :disabled="page <= 1" @click="changePage(page - 1)">이전</button>
|
||||||
|
<span style="color:#4a5568;font-size:0.9rem">{{ page }} / {{ totalPages }}</span>
|
||||||
|
<button class="btn btn-secondary" :disabled="page >= totalPages" @click="changePage(page + 1)">다음</button>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="!words.length" class="empty">단어가 없습니다</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@ -36,18 +46,33 @@ const words = ref([])
|
|||||||
const categories = ref([])
|
const categories = ref([])
|
||||||
const selected = ref('')
|
const selected = ref('')
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
|
const page = ref(1)
|
||||||
|
const total = ref(0)
|
||||||
|
const totalPages = ref(1)
|
||||||
|
const limit = 50
|
||||||
|
|
||||||
const select = async (cat) => {
|
const fetchWords = async (cat, p) => {
|
||||||
selected.value = cat
|
|
||||||
loading.value = true
|
loading.value = true
|
||||||
const res = await wordsApi.getAll(cat || undefined)
|
const res = await wordsApi.getAll(cat || undefined, p, limit)
|
||||||
words.value = res.data
|
words.value = res.data.data
|
||||||
|
total.value = res.data.total
|
||||||
|
totalPages.value = res.data.totalPages
|
||||||
|
page.value = p
|
||||||
loading.value = false
|
loading.value = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const select = async (cat) => {
|
||||||
|
selected.value = cat
|
||||||
|
await fetchWords(cat, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
const changePage = async (p) => {
|
||||||
|
await fetchWords(selected.value, p)
|
||||||
|
window.scrollTo({ top: 0, behavior: 'smooth' })
|
||||||
|
}
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
const [w, c] = await Promise.all([wordsApi.getAll(), wordsApi.getCategories()])
|
const [, c] = await Promise.all([fetchWords('', 1), wordsApi.getCategories()])
|
||||||
words.value = w.data
|
|
||||||
categories.value = c.data
|
categories.value = c.data
|
||||||
questApi.complete({ session_id: session.id, type: 'word' })
|
questApi.complete({ session_id: session.id, type: 'word' })
|
||||||
})
|
})
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user