814 lines
18 KiB
Vue
814 lines
18 KiB
Vue
<template>
|
|
<view class="home-page">
|
|
<!-- 顶部背景区域 -->
|
|
<view class="home-bg" :style="{ paddingTop: getNotchHeight() + 'px' }">
|
|
<wd-search
|
|
hide-cancel
|
|
light
|
|
clearable
|
|
class="search-bar"
|
|
:placeholder="$t('bookHome.searchPlaceholder')"
|
|
@search="handleSearch"
|
|
/>
|
|
<view class="icon-hua">
|
|
<image
|
|
src="../../static/home_icon.png"
|
|
mode="aspectFit"
|
|
class="icon-hua-img"
|
|
/>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 内容区域 -->
|
|
<view class="content-wrapper">
|
|
<!-- 我的书单 & 推荐图书模块 -->
|
|
<view class="mine-block">
|
|
<!-- 我的书单 -->
|
|
<view class="mine-1">
|
|
<text class="mine-title">{{ $t('bookHome.block1') }}</text>
|
|
<view
|
|
v-if="myBooksList.length > 0"
|
|
class="mine-more"
|
|
@click="handleMoreClick"
|
|
>
|
|
{{ $t('bookHome.more') }}
|
|
<image src="@/static/icon/icon_right.png" />
|
|
</view>
|
|
<view v-if="myBooksList.length > 0" class="mine-1-list">
|
|
<view
|
|
v-for="(item, index) in myBooksList"
|
|
:key="index"
|
|
class="mine-item"
|
|
@click="handleMyBookClick(item.id)"
|
|
>
|
|
<image :src="item.images" />
|
|
<text>{{ item.name }}</text>
|
|
</view>
|
|
</view>
|
|
<text v-else class="zanwu">{{ $t('common.data_null') }}</text>
|
|
</view>
|
|
|
|
<!-- 推荐图书 -->
|
|
<view class="mine-2">
|
|
<text class="mine-title">{{ $t('bookHome.block2') }}</text>
|
|
<swiper
|
|
v-if="recommendBooksList.length > 0"
|
|
autoplay
|
|
:interval="3000"
|
|
:duration="500"
|
|
class="recommend-list"
|
|
>
|
|
<swiper-item
|
|
v-for="(item, index) in recommendBooksList"
|
|
:key="index"
|
|
class="recommend-item"
|
|
@click="handleBookClick(item.id)"
|
|
>
|
|
<image :src="item.images" width="100%" height="100%" />
|
|
<text>{{ item.name }}</text>
|
|
</swiper-item>
|
|
</swiper>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 活动图书模块 -->
|
|
<view v-if="showActivity" class="activity-block">
|
|
<text class="activity-title">{{ $t('bookHome.activityTitle') }}</text>
|
|
<scroll-view class="scroll-view" scroll-x :show-scrollbar="false">
|
|
<view class="activity-label-list">
|
|
<view
|
|
v-for="(item, index) in activityLabelList"
|
|
:key="index"
|
|
:class="[
|
|
'activity-label-item',
|
|
currentActivityIndex === index ? 'active-label' : ''
|
|
]"
|
|
@click="handleActivityLabelClick(item.id, index)"
|
|
>
|
|
<text>{{ item.title }}</text>
|
|
</view>
|
|
</view>
|
|
</scroll-view>
|
|
<scroll-view
|
|
v-if="activityList.length > 0"
|
|
class="scroll-view"
|
|
scroll-x
|
|
:show-scrollbar="false"
|
|
>
|
|
<view class="activity-list">
|
|
<view
|
|
v-for="(item, index) in activityList"
|
|
:key="index"
|
|
class="activity-item"
|
|
@click="handleBookClick(item.bookId)"
|
|
>
|
|
<image :src="item.images" />
|
|
<text class="activity-text">{{ item.name }}</text>
|
|
</view>
|
|
</view>
|
|
</scroll-view>
|
|
<text v-else class="zanwu" style="padding: 80rpx 0">{{ $t('global.dataNull') }}</text>
|
|
</view>
|
|
|
|
<!-- 分类图书模块 -->
|
|
<view v-if="showCategory" class="book-block">
|
|
<!-- 一级分类标签 -->
|
|
<scroll-view class="scroll-view" scroll-x :show-scrollbar="false">
|
|
<view class="book-tab-one">
|
|
<view
|
|
v-for="(item, index) in categoryLevel1List"
|
|
:key="index"
|
|
:class="[
|
|
'tab-one-item',
|
|
currentLevel1Index === index ? 'tab-one-active' : ''
|
|
]"
|
|
@click="handleCategoryLevel1Click(item.id, index)"
|
|
>
|
|
<text>{{ item.title }}</text>
|
|
</view>
|
|
</view>
|
|
</scroll-view>
|
|
|
|
<!-- 二级分类标签 -->
|
|
<scroll-view
|
|
v-if="categoryLevel2List.length > 0"
|
|
class="scroll-view"
|
|
scroll-x
|
|
:show-scrollbar="false"
|
|
style="background: #fff; margin-top: 15rpx"
|
|
>
|
|
<view class="book-tab-two">
|
|
<view
|
|
v-for="(item, index) in categoryLevel2List"
|
|
:key="index"
|
|
:class="[
|
|
'tab-two-item',
|
|
currentLevel2Index === index ? 'tab-two-active' : ''
|
|
]"
|
|
@click="handleCategoryLevel2Click(item.id, index)"
|
|
>
|
|
<text>{{ item.title }}</text>
|
|
</view>
|
|
</view>
|
|
</scroll-view>
|
|
|
|
<!-- 分类图书列表 -->
|
|
<view v-if="categoryBookList.length > 0" class="book-list">
|
|
<view
|
|
v-for="(item, index) in categoryBookList"
|
|
:key="index"
|
|
class="book-item"
|
|
@click="handleBookClick(item.bookId)"
|
|
>
|
|
<image :src="item.images" />
|
|
<text class="book-text">{{ item.name }}</text>
|
|
<text class="book-price">{{ item.minPrice }} {{ t('global.coin') }}</text>
|
|
<text v-if="formatStats(item)" class="book-flag">{{
|
|
formatStats(item)
|
|
}}</text>
|
|
</view>
|
|
</view>
|
|
<text v-else class="zanwu" style="padding: 100rpx 0">{{ $t('global.dataNull') }}</text>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { ref, onMounted } from 'vue'
|
|
import { onShow } from '@dcloudio/uni-app'
|
|
import { useI18n } from 'vue-i18n'
|
|
import { homeApi } from '@/api/modules/book_home'
|
|
import { getNotchHeight } from '@/utils/system'
|
|
import type {
|
|
IBook,
|
|
IBookWithStats,
|
|
ILabel,
|
|
IVipInfo
|
|
} from '@/types/book'
|
|
|
|
const { t } = useI18n()
|
|
|
|
// 状态定义
|
|
const showMyBooks = ref(false)
|
|
const showActivity = ref(false)
|
|
const showCategory = ref(false)
|
|
|
|
// 我的书单
|
|
const myBooksList = ref<IBook[]>([])
|
|
|
|
// 推荐图书
|
|
const recommendBooksList = ref<IBook[]>([])
|
|
|
|
// 活动图书
|
|
const activityLabelList = ref<ILabel[]>([])
|
|
const activityList = ref<IBookWithStats[]>([])
|
|
const currentActivityIndex = ref(0)
|
|
|
|
// 分类图书
|
|
const categoryLevel1List = ref<ILabel[]>([])
|
|
const categoryLevel2List = ref<ILabel[]>([])
|
|
const categoryBookList = ref<IBookWithStats[]>([])
|
|
const currentLevel1Index = ref(0)
|
|
const currentLevel2Index = ref(0)
|
|
|
|
// VIP信息
|
|
const vipInfo = ref<IVipInfo | null>(null)
|
|
|
|
/**
|
|
* 获取VIP信息
|
|
*/
|
|
const getVipInfo = async () => {
|
|
const res = await homeApi.getVipInfo()
|
|
if (res.vipInfo) {
|
|
vipInfo.value = res.vipInfo
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 获取我的书单
|
|
*/
|
|
const getMyBooks = async () => {
|
|
const res = await homeApi.getMyBooks(1, 10)
|
|
if (res && res.code === 0) {
|
|
showMyBooks.value = true
|
|
if (res.page.records && res.page.records.length > 0) {
|
|
myBooksList.value = res.page.records
|
|
}
|
|
} else {
|
|
// 未登录,跳转到登录页
|
|
uni.navigateTo({
|
|
url: '/pages/login/login'
|
|
})
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 获取推荐图书
|
|
*/
|
|
const getRecommendBooks = async () => {
|
|
const res = await homeApi.getRecommendBooks()
|
|
if (res.books && res.books.length > 0) {
|
|
recommendBooksList.value = res.books
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 获取活动标签列表
|
|
*/
|
|
const getActivityLabels = async () => {
|
|
const res = await homeApi.getBookLabelList(1)
|
|
showActivity.value = true
|
|
if (res.lableList && res.lableList.length > 0) {
|
|
activityLabelList.value = res.lableList
|
|
// 默认加载第一个标签的图书列表
|
|
await getBooksByLabel(res.lableList[0].id, 'activity')
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 获取分类标签列表
|
|
*/
|
|
const getCategoryLabels = async () => {
|
|
const res = await homeApi.getBookLabelList(0)
|
|
showCategory.value = true
|
|
if (res.lableList && res.lableList.length > 0) {
|
|
categoryLevel1List.value = res.lableList
|
|
// 默认加载第一个标签的二级标签
|
|
await getSubLabels(res.lableList[0].id, 0)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 获取二级标签列表
|
|
*/
|
|
const getSubLabels = async (pid: number, index: number) => {
|
|
const res = await homeApi.getSubLabelList(pid)
|
|
currentLevel1Index.value = index
|
|
if (res.lableList && res.lableList.length > 0) {
|
|
categoryLevel2List.value = res.lableList
|
|
currentLevel2Index.value = 0
|
|
// 加载第一个二级标签的图书列表
|
|
await getBooksByLabel(res.lableList[0].id, 'category')
|
|
} else {
|
|
// 没有二级标签,直接加载一级标签的图书列表
|
|
categoryLevel2List.value = []
|
|
await getBooksByLabel(pid, 'category')
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 根据标签获取图书列表
|
|
*/
|
|
const getBooksByLabel = async (
|
|
labelId: number,
|
|
type: 'activity' | 'category'
|
|
) => {
|
|
const res = await homeApi.getBooksByLabel(labelId)
|
|
if (type === 'activity') {
|
|
if (res.bookList && res.bookList.length > 0) {
|
|
activityList.value = res.bookList
|
|
} else {
|
|
activityList.value = []
|
|
}
|
|
} else {
|
|
if (res.bookList && res.bookList.length > 0) {
|
|
categoryBookList.value = res.bookList
|
|
} else {
|
|
categoryBookList.value = []
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 格式化价格
|
|
*/
|
|
const formatPrice = (book: IBookWithStats): string => {
|
|
// 已购买不显示价格
|
|
if (book.isBuy) return ''
|
|
|
|
// VIP用户且图书为VIP专享
|
|
if (vipInfo.value?.id && book.isVip === '2') {
|
|
const price = book.sysDictData?.dictValue
|
|
return price ? `$ ${price} NZD` : ''
|
|
}
|
|
|
|
// 普通用户
|
|
if (!vipInfo.value?.id) {
|
|
const price = book.sysDictData?.dictValue
|
|
return price ? `$ ${price} NZD` : ''
|
|
}
|
|
|
|
return ''
|
|
}
|
|
|
|
/**
|
|
* 格式化统计信息
|
|
*/
|
|
const formatStats = (book: IBookWithStats): string => {
|
|
if (book.readCount && book.readCount > 0) {
|
|
return `${book.readCount}${t('bookHome.readingCount')}`
|
|
}
|
|
|
|
if (book.buyCount && book.buyCount > 0) {
|
|
return `${book.buyCount}${t('bookHome.purchased')}`
|
|
}
|
|
|
|
return ''
|
|
}
|
|
|
|
/**
|
|
* 处理搜索点击
|
|
*/
|
|
const handleSearch = ({ value }: { value: string }) => {
|
|
uni.navigateTo({
|
|
url: `/pages/book/search?keyword=${value}`
|
|
})
|
|
}
|
|
|
|
/**
|
|
* 处理我的书单图书点击
|
|
*/
|
|
const handleMyBookClick = (bookId: number) => {
|
|
uni.navigateTo({
|
|
url: `/pages/book/reader?isBuy=0&bookId=${bookId}`
|
|
})
|
|
}
|
|
|
|
/**
|
|
* 处理图书点击
|
|
*/
|
|
const handleBookClick = (bookId: number) => {
|
|
uni.navigateTo({
|
|
url: `/pages/book/detail?id=${bookId}`
|
|
})
|
|
}
|
|
|
|
/**
|
|
* 处理更多按钮点击
|
|
*/
|
|
const handleMoreClick = () => {
|
|
uni.switchTab({
|
|
url: '/pages/book/index'
|
|
})
|
|
}
|
|
|
|
/**
|
|
* 处理活动标签点击
|
|
*/
|
|
const handleActivityLabelClick = async (labelId: number, index: number) => {
|
|
currentActivityIndex.value = index
|
|
await getBooksByLabel(labelId, 'activity')
|
|
}
|
|
|
|
/**
|
|
* 处理一级分类标签点击
|
|
*/
|
|
const handleCategoryLevel1Click = async (labelId: number, index: number) => {
|
|
await getSubLabels(labelId, index)
|
|
}
|
|
|
|
/**
|
|
* 处理二级分类标签点击
|
|
*/
|
|
const handleCategoryLevel2Click = async (labelId: number, index: number) => {
|
|
currentLevel2Index.value = index
|
|
await getBooksByLabel(labelId, 'category')
|
|
}
|
|
|
|
/**
|
|
* 页面加载
|
|
*/
|
|
onMounted(() => {
|
|
// 重置活动标签选中状态
|
|
currentActivityIndex.value = 0
|
|
showActivity.value = false
|
|
})
|
|
|
|
/**
|
|
* 页面显示
|
|
*/
|
|
onShow(() => {
|
|
// 刷新数据
|
|
getVipInfo()
|
|
getMyBooks()
|
|
getRecommendBooks()
|
|
getActivityLabels()
|
|
getCategoryLabels()
|
|
})
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
.home-page {
|
|
min-height: 100vh;
|
|
background: #f7faf9;
|
|
}
|
|
|
|
.home-bg {
|
|
background-image: url('@/static/icon/home_bg.jpg');
|
|
background-position: center center;
|
|
background-repeat: no-repeat;
|
|
background-size: cover;
|
|
padding: 30rpx;
|
|
position: relative;
|
|
|
|
.icon-hua {
|
|
width: 100%;
|
|
text-align: center;
|
|
display: block;
|
|
padding-top: 20rpx;
|
|
|
|
.icon-hua-img {
|
|
width: 100%;
|
|
height: 160rpx;
|
|
}
|
|
}
|
|
}
|
|
|
|
.search-bar {
|
|
background-color: transparent !important;
|
|
}
|
|
|
|
.content-wrapper {
|
|
padding-bottom: 40rpx;
|
|
}
|
|
|
|
.mine-block {
|
|
padding: 20rpx;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
|
|
.mine-1,
|
|
.mine-2 {
|
|
width: 49%;
|
|
height: 290rpx;
|
|
border-radius: 15rpx;
|
|
padding: 30rpx;
|
|
position: relative;
|
|
|
|
.mine-title {
|
|
font-size: 32rpx;
|
|
color: #333;
|
|
line-height: 32rpx;
|
|
}
|
|
|
|
.mine-more {
|
|
position: absolute;
|
|
top: 30rpx;
|
|
right: 5rpx;
|
|
font-size: 28rpx;
|
|
line-height: 34rpx;
|
|
color: #999;
|
|
|
|
image {
|
|
display: inline-block;
|
|
vertical-align: text-bottom;
|
|
width: 34rpx;
|
|
height: 34rpx;
|
|
}
|
|
}
|
|
|
|
.mine-1-list {
|
|
width: 260rpx;
|
|
overflow: hidden;
|
|
display: flex;
|
|
align-items: center;
|
|
|
|
.mine-item {
|
|
width: 110rpx;
|
|
text-align: center;
|
|
margin: 20rpx 25rpx 0 0;
|
|
|
|
image {
|
|
width: 110rpx;
|
|
height: 135rpx;
|
|
border-radius: 10rpx;
|
|
}
|
|
|
|
text {
|
|
display: block;
|
|
width: 120rpx;
|
|
font-size: 28rpx;
|
|
color: #333;
|
|
line-height: 32rpx;
|
|
overflow: hidden;
|
|
white-space: nowrap;
|
|
text-overflow: ellipsis;
|
|
padding-top: 10rpx;
|
|
}
|
|
}
|
|
}
|
|
|
|
.recommend-list {
|
|
width: 310rpx;
|
|
height: 164rpx;
|
|
margin-top: 20rpx;
|
|
|
|
.recommend-item {
|
|
display: flex;
|
|
align-items: center;
|
|
|
|
image {
|
|
width: 120rpx;
|
|
height: 164rpx;
|
|
border-radius: 10rpx;
|
|
}
|
|
|
|
text {
|
|
display: block;
|
|
width: 190rpx;
|
|
font-size: 28rpx;
|
|
color: #333;
|
|
line-height: 40rpx;
|
|
overflow: hidden;
|
|
white-space: nowrap;
|
|
text-overflow: ellipsis;
|
|
padding-left: 15rpx;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
.mine-1 {
|
|
background-image: linear-gradient(60deg, #fff9e9 20%, #fffbf2 100%);
|
|
}
|
|
|
|
.mine-2 {
|
|
background-image: linear-gradient(60deg, #fef0f0 20%, #fdf9f8 100%);
|
|
}
|
|
}
|
|
|
|
.activity-block {
|
|
margin: 0 20rpx;
|
|
padding: 20rpx;
|
|
background: #fff;
|
|
border-radius: 15rpx;
|
|
min-height: 445rpx;
|
|
|
|
.activity-title {
|
|
display: block;
|
|
font-size: 38rpx;
|
|
color: #333;
|
|
line-height: 40rpx;
|
|
padding: 15rpx 0 20rpx;
|
|
}
|
|
|
|
.activity-label-list {
|
|
display: flex;
|
|
align-items: center;
|
|
margin-top: 10rpx;
|
|
padding-left: 10rpx;
|
|
|
|
.activity-label-item {
|
|
display: flex;
|
|
align-items: center;
|
|
background-color: #d7ece8;
|
|
border-radius: 10rpx;
|
|
margin-right: 20rpx;
|
|
padding: 0 10rpx;
|
|
flex-shrink: 0;
|
|
|
|
text {
|
|
display: inline-block;
|
|
text-align: center;
|
|
min-width: 110rpx;
|
|
height: 58rpx;
|
|
line-height: 58rpx;
|
|
color: #55aa7f;
|
|
font-size: 30rpx;
|
|
overflow: hidden;
|
|
white-space: nowrap;
|
|
text-overflow: ellipsis;
|
|
}
|
|
}
|
|
|
|
.active-label {
|
|
background-color: #55aa7f;
|
|
|
|
text {
|
|
color: #fff;
|
|
}
|
|
}
|
|
}
|
|
|
|
.activity-list {
|
|
width: 100%;
|
|
margin-top: 20rpx;
|
|
padding-left: 10rpx;
|
|
display: flex;
|
|
align-items: center;
|
|
|
|
.activity-item {
|
|
margin-right: 25rpx;
|
|
text-align: center;
|
|
flex-shrink: 0;
|
|
|
|
image {
|
|
width: 160rpx;
|
|
height: 200rpx;
|
|
border-radius: 10rpx;
|
|
}
|
|
|
|
.activity-text {
|
|
display: block;
|
|
width: 160rpx;
|
|
font-size: 28rpx;
|
|
color: #333;
|
|
padding-top: 10rpx;
|
|
line-height: 40rpx;
|
|
overflow: hidden;
|
|
white-space: nowrap;
|
|
text-overflow: ellipsis;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
.book-block {
|
|
padding: 20rpx 20rpx 0;
|
|
|
|
.book-tab-one {
|
|
display: flex;
|
|
align-items: center;
|
|
|
|
.tab-one-item {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
background-color: #d7ece8;
|
|
border-radius: 15rpx;
|
|
margin-right: 15rpx;
|
|
text-align: center;
|
|
min-width: 25%;
|
|
height: 160rpx;
|
|
flex-shrink: 0;
|
|
|
|
text {
|
|
display: flex;
|
|
width: 98%;
|
|
font-size: 32rpx;
|
|
color: #55aa7f;
|
|
line-height: 50rpx;
|
|
overflow: hidden;
|
|
}
|
|
}
|
|
|
|
.tab-one-item:last-child {
|
|
margin-right: 0;
|
|
}
|
|
|
|
.tab-one-active {
|
|
background-color: #55aa7f;
|
|
|
|
text {
|
|
color: #fff;
|
|
}
|
|
}
|
|
}
|
|
|
|
.book-tab-two {
|
|
display: flex;
|
|
align-items: center;
|
|
height: 70rpx;
|
|
border-radius: 10rpx;
|
|
|
|
.tab-two-item {
|
|
min-width: 25%;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
border-right: 1rpx solid #acacac33;
|
|
flex-shrink: 0;
|
|
|
|
text {
|
|
display: block;
|
|
text-align: center;
|
|
width: 165rpx;
|
|
font-size: 28rpx;
|
|
line-height: 48rpx;
|
|
overflow: hidden;
|
|
white-space: nowrap;
|
|
text-overflow: ellipsis;
|
|
}
|
|
}
|
|
|
|
.tab-two-item:last-child {
|
|
border-right: none;
|
|
}
|
|
|
|
.tab-two-active {
|
|
text {
|
|
color: #55aa7f;
|
|
font-weight: bold;
|
|
}
|
|
}
|
|
}
|
|
|
|
.book-list {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
flex-wrap: wrap;
|
|
margin-top: 20rpx;
|
|
|
|
.book-item {
|
|
width: 49%;
|
|
background-color: #fff;
|
|
border-radius: 15rpx;
|
|
height: 575rpx;
|
|
position: relative;
|
|
margin-bottom: 20rpx;
|
|
|
|
image {
|
|
width: 85%;
|
|
margin: 25rpx auto 0;
|
|
border-radius: 15rpx;
|
|
height: 380rpx;
|
|
display: block;
|
|
}
|
|
|
|
.book-text {
|
|
display: block;
|
|
font-size: 28rpx;
|
|
color: #333;
|
|
line-height: 36rpx;
|
|
width: 80%;
|
|
margin: 15rpx auto 0;
|
|
max-height: 72rpx;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.book-price {
|
|
position: absolute;
|
|
font-size: 28rpx;
|
|
color: #ff4703;
|
|
left: 30rpx;
|
|
bottom: 20rpx;
|
|
}
|
|
|
|
.book-flag {
|
|
display: block;
|
|
font-size: 26rpx;
|
|
color: #999;
|
|
position: absolute;
|
|
right: 6%;
|
|
bottom: 20rpx;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
.zanwu {
|
|
display: block;
|
|
text-align: center;
|
|
font-size: 28rpx;
|
|
color: #999;
|
|
padding: 40rpx 0;
|
|
}
|
|
|
|
.scroll-view {
|
|
white-space: nowrap;
|
|
}
|
|
</style>
|