Files
taimed-international-app/pages/user/myBook/index.vue

223 lines
4.9 KiB
Vue

<template>
<view class="my-book-page">
<!-- 自定义导航栏 -->
<nav-bar :title="$t('book.myBook')"></nav-bar>
<!-- 书籍列表 -->
<scroll-view
v-if="(bookList && bookList.length > 0) || loading"
scroll-y
class="book-scroll"
:style="{ height: scrollHeight + 'px' }"
:scroll-into-view="scrollTarget"
@scrolltolower="loadMore"
>
<view :id="scrollTarget" />
<view class="book-list">
<BookCard
v-for="item in bookList"
:key="item.id"
:book="item"
@click="goToDetail(item.id)"
@read="goToReader(item.id)"
@listen="goToListen(item.id)"
@review="goToReview(item.id)"
/>
<!-- 加载提示 -->
<view v-if="showLoadMore && hasMore" class="load-tip">
<text>{{ $t('common.loadMore') }}</text>
</view>
<view v-if="!hasMore && bookList && bookList.length > 0" class="load-tip">
<text>{{ $t('common.noMore') }}</text>
</view>
</view>
</scroll-view>
<!-- 空状态 -->
<view v-else-if="!loading" class="empty-state">
<image src="@/static/null_img.png" mode="aspectFit" />
<text class="empty-text">{{ $t('book.nullText') }}</text>
<wd-button type="primary" @click="goToBuy">
{{ $t('book.choose') }}
</wd-button>
</view>
</view>
</template>
<script setup lang="ts">
import { ref, onMounted, computed } from 'vue'
import { onShow } from '@dcloudio/uni-app'
import { useI18n } from 'vue-i18n'
import { bookApi } from '@/api/modules/book'
import type { IBook } from '@/types/book'
import BookCard from '@/components/book/BookCard.vue'
const { t } = useI18n()
// 数据状态
const bookList = ref<IBook[]>([])
const loading = ref(false)
const page = ref({
current: 1,
limit: 10
})
const total = ref(0)
const scrollTarget = ref('top')
const scrollHeight = ref(0)
const showLoadMore = ref(false)
// 计算属性
const hasMore = computed(() => bookList.value && bookList.value.length < total.value)
// 生命周期
onMounted(() => {
initScrollHeight()
loadBookList()
})
// 初始化滚动区域高度
function initScrollHeight() {
const systemInfo = uni.getSystemInfoSync()
const statusBarHeight = systemInfo.statusBarHeight || 0
let navBarHeight = 44
if (systemInfo.model.includes('iPhone')) {
const modelNumber = parseInt(systemInfo.model.match(/\d+/)?.[0] || '0')
if (modelNumber >= 11) {
navBarHeight = 48
}
}
const totalNavHeight = statusBarHeight + navBarHeight
scrollHeight.value = systemInfo.windowHeight - totalNavHeight
}
// 加载书单列表
async function loadBookList() {
if (loading.value || (!hasMore.value && bookList.value && bookList.value.length > 0)) {
return
}
loading.value = true
try {
const res = await bookApi.getMyBooks(page.value.current, page.value.limit)
if (res.page && res.page.records) {
total.value = res.page.total
const newBooks = res.page.records
if (newBooks.length > 0) {
bookList.value = [...(bookList.value || []), ...newBooks]
page.value.current += 1
showLoadMore.value = true
}
}
} catch (error) {
console.error('Failed to load book list:', error)
} finally {
loading.value = false
}
}
// 加载更多
function loadMore() {
if (!loading.value && hasMore.value) {
loadBookList()
}
}
// 页面跳转
function goToDetail(bookId: number) {
uni.navigateTo({
url: `/pages/book/detail?id=${bookId}`
})
}
function goToReader(bookId: number) {
uni.navigateTo({
url: `/pages/book/reader?isBuy=0&bookId=${bookId}`
})
}
function goToListen(bookId: number) {
uni.navigateTo({
url: `/pages/book/listen/index?bookId=${bookId}`
})
}
function goToReview(bookId: number) {
uni.navigateTo({
url: `/pages/book/review?bookId=${bookId}&page=0`
})
}
function goToBuy() {
uni.switchTab({
url: '/pages/book/index'
})
}
// 页面显示时重新加载
onShow(() => {
// 回到顶部
scrollTarget.value = 'top'
setTimeout(() => {
scrollTarget.value = ''
}, 300)
// 重置并重新加载
bookList.value = []
page.value.current = 1
total.value = 0
showLoadMore.value = false
loadBookList()
})
</script>
<style lang="scss" scoped>
.my-book-page {
background: #f7faf9;
min-height: 100vh;
.book-scroll {
.book-list {
padding: 0 20rpx 10rpx;
}
}
.load-tip {
font-size: 30rpx;
text-align: center;
padding: 40rpx 0;
color: #ccc;
}
.empty-state {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding-top: 200rpx;
image {
width: 400rpx;
height: 300rpx;
margin-bottom: 40rpx;
}
.empty-text {
font-size: 28rpx;
color: #999;
margin-bottom: 50rpx;
}
button {
width: 180rpx;
height: 60rpx;
font-size: 28rpx;
border-radius: 50rpx;
}
}
}
</style>