更新:1.课程详情增加骨架屏;2.图书首页和图书详情增加骨架屏;
This commit is contained in:
@@ -24,189 +24,257 @@
|
||||
<!-- 我的书单 & 推荐图书模块 -->
|
||||
<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>
|
||||
<Skeleton
|
||||
ref="myBookSkeleton"
|
||||
theme="image-card"
|
||||
:request="getMyBooks"
|
||||
:size="[{ height:'140px' }]"
|
||||
class="flex-1 w-[50%]"
|
||||
>
|
||||
<template #content="{ data }" >
|
||||
<view class="mine-1">
|
||||
<text class="mine-title">{{ $t('bookHome.block1') }}</text>
|
||||
<view
|
||||
v-if="data.page.records.length > 0"
|
||||
class="mine-more"
|
||||
@click="handleMoreClick"
|
||||
>
|
||||
{{ $t('bookHome.more') }}
|
||||
<image src="@/static/icon/icon_right.png" />
|
||||
</view>
|
||||
<view v-if="data.page.records.length > 0" class="mine-1-list">
|
||||
<view
|
||||
v-for="(item, index) in data.page.records"
|
||||
: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>
|
||||
<text v-else class="zanwu">{{ $t('common.data_null') }}</text>
|
||||
</view>
|
||||
|
||||
</template>
|
||||
</Skeleton>
|
||||
|
||||
<!-- 推荐图书 -->
|
||||
<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>
|
||||
<Skeleton
|
||||
ref="recommendBooksSkeleton"
|
||||
theme="image-card"
|
||||
:request="getRecommendBooks"
|
||||
:size="[{ height:'140px' }]"
|
||||
class="flex-1 w-[50%]"
|
||||
>
|
||||
<template #content="{ data }" >
|
||||
<view class="mine-2">
|
||||
<text class="mine-title">{{ $t('bookHome.block2') }}</text>
|
||||
<swiper
|
||||
v-if="data.books.length > 0"
|
||||
autoplay
|
||||
:interval="3000"
|
||||
:duration="500"
|
||||
class="recommend-list"
|
||||
>
|
||||
<swiper-item
|
||||
v-for="(item, index) in data.books"
|
||||
: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>
|
||||
</template>
|
||||
</Skeleton>
|
||||
</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-block">
|
||||
<Skeleton
|
||||
theme="image-card"
|
||||
:size="Array(5).fill({ height:'28px', width: '18%' })"
|
||||
:request="getActivityLabels"
|
||||
@success="getActivityLabelsSuccess"
|
||||
>
|
||||
<view class="activity-list">
|
||||
<view
|
||||
v-for="(item, index) in activityList"
|
||||
:key="index"
|
||||
class="activity-item"
|
||||
@click="handleBookClick(item.bookId)"
|
||||
<template #loading-top>
|
||||
<wd-skeleton :row-col="[{ width: '45%', height: '35px' }]" animation="gradient" class="mb-2.5!" />
|
||||
</template>
|
||||
<template #content>
|
||||
<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>
|
||||
</template>
|
||||
</Skeleton>
|
||||
<Skeleton
|
||||
ref="activityBooksSkeleton"
|
||||
theme="image-card"
|
||||
:auto="false"
|
||||
:size="Array(4).fill({ height:'100px', width: '23%' })"
|
||||
class="mt-[20rpx]!"
|
||||
:request="getActivityBooks"
|
||||
>
|
||||
<template #content="{ data }">
|
||||
<scroll-view
|
||||
v-if="data.bookList.length > 0"
|
||||
class="scroll-view"
|
||||
scroll-x
|
||||
:show-scrollbar="false"
|
||||
>
|
||||
<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 class="activity-list">
|
||||
<view
|
||||
v-for="(item, index) in data.bookList"
|
||||
: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>
|
||||
</template>
|
||||
</Skeleton>
|
||||
</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-block">
|
||||
<Skeleton
|
||||
ref="categoryLevel1LabelSkeleton"
|
||||
theme="image-card"
|
||||
:size="Array(3).fill({ height:'75px', width: '32%' })"
|
||||
:request="getCategoryLabels"
|
||||
@success="getCategoryLabelsSuccess"
|
||||
>
|
||||
<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)"
|
||||
<template #content>
|
||||
<!-- 一级分类标签 -->
|
||||
<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>
|
||||
</template>
|
||||
</Skeleton>
|
||||
<Skeleton
|
||||
ref="categoryLevel2LabelSkeleton"
|
||||
theme="image-card"
|
||||
:auto="false"
|
||||
:size="Array(4).fill({ height:'30px', width: '24%' })"
|
||||
class="mt-[20rpx]!"
|
||||
:request="getSubLabels"
|
||||
@success="getSubLabelsSuccess"
|
||||
>
|
||||
<template #content>
|
||||
<!-- 二级分类标签 -->
|
||||
<scroll-view
|
||||
v-if="categoryLevel2List.length > 0"
|
||||
class="scroll-view"
|
||||
scroll-x
|
||||
:show-scrollbar="false"
|
||||
style="background: #fff; margin-top: 15rpx"
|
||||
>
|
||||
<text>{{ item.title }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
<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>
|
||||
</template>
|
||||
</Skeleton>
|
||||
|
||||
<!-- 分类图书列表 -->
|
||||
<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>
|
||||
<BookPrice :data="item" class="book-price-container" />
|
||||
</view>
|
||||
</view>
|
||||
<text v-else class="zanwu" style="padding: 100rpx 0">{{ $t('global.dataNull') }}</text>
|
||||
<Skeleton
|
||||
ref="categoryBooksSkeleton"
|
||||
theme="image-card"
|
||||
:auto="false"
|
||||
:size="Array(2).fill({ height:'240px', width: '49%' })"
|
||||
:count="3"
|
||||
class="mt-[20rpx]!"
|
||||
:request="getBooksByLabel"
|
||||
>
|
||||
<template #content="{ data }">
|
||||
<view v-if="data.bookList.length > 0" class="book-list">
|
||||
<view
|
||||
v-for="(item, index) in data.bookList"
|
||||
:key="index"
|
||||
class="book-item"
|
||||
@click="handleBookClick(item.bookId)"
|
||||
>
|
||||
<image :src="item.images" />
|
||||
<text class="book-text">{{ item.name }}</text>
|
||||
<BookPrice :data="item" class="book-price-container" />
|
||||
</view>
|
||||
</view>
|
||||
<text v-else class="zanwu" style="padding: 100rpx 0">{{ $t('global.dataNull') }}</text>
|
||||
</template>
|
||||
</Skeleton>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, computed } from 'vue'
|
||||
import { ref, computed } from 'vue'
|
||||
import { onShow } from '@dcloudio/uni-app'
|
||||
import { homeApi } from '@/api/modules/book_home'
|
||||
import { bookHomeApi } from '@/api/modules/book_home'
|
||||
import { getNotchHeight } from '@/utils/system'
|
||||
import { useUserStore } from '@/stores/user'
|
||||
import BookPrice from '@/components/book/BookPrice.vue'
|
||||
import type {
|
||||
IBook,
|
||||
IBookWithStats,
|
||||
ILabel,
|
||||
IVipInfo
|
||||
} from '@/types/book'
|
||||
import type { ILabel } from '@/types/book'
|
||||
|
||||
const userStore = useUserStore()
|
||||
|
||||
// 状态定义
|
||||
const showMyBooks = ref(false)
|
||||
const showActivity = ref(false)
|
||||
const showCategory = ref(false)
|
||||
|
||||
// 我的书单
|
||||
const myBooksList = ref<IBook[]>([])
|
||||
const myBookSkeleton = ref()
|
||||
|
||||
// 推荐图书
|
||||
const recommendBooksList = ref<IBook[]>([])
|
||||
const recommendBooksSkeleton = ref()
|
||||
|
||||
// 活动图书
|
||||
const activityBooksSkeleton = ref()
|
||||
const activityLabelList = ref<ILabel[]>([])
|
||||
const activityList = ref<IBookWithStats[]>([])
|
||||
const currentActivityIndex = ref(0)
|
||||
|
||||
// 分类图书
|
||||
const categoryLevel1LabelSkeleton = ref()
|
||||
const categoryLevel2LabelSkeleton = ref()
|
||||
const categoryBooksSkeleton = ref()
|
||||
const categoryLevel1List = ref<ILabel[]>([])
|
||||
const categoryLevel2List = ref<ILabel[]>([])
|
||||
const categoryBookList = ref<IBookWithStats[]>([])
|
||||
const currentLevel1Index = ref(0)
|
||||
const currentLevel2Index = ref(0)
|
||||
|
||||
@@ -216,96 +284,62 @@ const vipInfo = computed(() => userStore.userInfo?.userEbookVip?.[0] || null)
|
||||
/**
|
||||
* 获取我的书单
|
||||
*/
|
||||
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 getMyBooks = () => bookHomeApi.getMyBooks(1, 10)
|
||||
|
||||
/**
|
||||
* 获取推荐图书
|
||||
*/
|
||||
const getRecommendBooks = async () => {
|
||||
const res = await homeApi.getRecommendBooks()
|
||||
if (res.books && res.books.length > 0) {
|
||||
recommendBooksList.value = res.books
|
||||
}
|
||||
}
|
||||
const getRecommendBooks = () => bookHomeApi.getRecommendBooks()
|
||||
|
||||
/**
|
||||
* 获取活动标签列表
|
||||
* 获取活动图书
|
||||
*/
|
||||
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 getActivityLabels = () => bookHomeApi.getBookLabelList(1)
|
||||
const getActivityLabelsSuccess = (res: any) => {
|
||||
activityLabelList.value = res.lableList || []
|
||||
activityBooksSkeleton.value.reload()
|
||||
}
|
||||
// 获取活动图书列表
|
||||
const getActivityBooks = () => {
|
||||
// 获取活动标签列表
|
||||
const index = currentActivityIndex.value || 0
|
||||
return bookHomeApi.getBooksByLabel(activityLabelList.value[index].id)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取分类标签列表
|
||||
*/
|
||||
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 getCategoryLabels = () => bookHomeApi.getBookLabelList(0)
|
||||
const getCategoryLabelsSuccess = (res: any) => {
|
||||
categoryLevel1List.value = res.lableList || []
|
||||
currentLevel2Index.value = 0
|
||||
categoryLevel2LabelSkeleton.value.reload()
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取二级标签列表
|
||||
*/
|
||||
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 getSubLabels = async () => {
|
||||
await getCategoryLabels()
|
||||
const pid = categoryLevel1List.value[currentLevel1Index.value]?.id || 0
|
||||
return bookHomeApi.getSubLabelList(pid)
|
||||
}
|
||||
const getSubLabelsSuccess = (res: any) => {
|
||||
categoryLevel2List.value = res.lableList || []
|
||||
categoryBooksSkeleton.value.reload()
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据标签获取图书列表
|
||||
* 获取分类标签图书列表
|
||||
*/
|
||||
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 getBooksByLabel = () => {
|
||||
// 获取当前需要加载的标签id
|
||||
// 判断是否有一级标签当前选中值
|
||||
const level1Id = categoryLevel1List.value[currentLevel1Index.value]?.id
|
||||
// 判断是否有二级标签当前选中值
|
||||
const level2Id = categoryLevel2List.value[currentLevel2Index.value]?.id
|
||||
|
||||
const labelId = level2Id || level1Id || 0
|
||||
return bookHomeApi.getBooksByLabel(labelId)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -349,14 +383,17 @@ const handleMoreClick = () => {
|
||||
*/
|
||||
const handleActivityLabelClick = async (labelId: number, index: number) => {
|
||||
currentActivityIndex.value = index
|
||||
await getBooksByLabel(labelId, 'activity')
|
||||
activityBooksSkeleton.value.reload()
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理一级分类标签点击
|
||||
*/
|
||||
const handleCategoryLevel1Click = async (labelId: number, index: number) => {
|
||||
await getSubLabels(labelId, index)
|
||||
currentLevel1Index.value = index
|
||||
currentLevel2Index.value = 0
|
||||
categoryBooksSkeleton.value.loading = true
|
||||
categoryLevel2LabelSkeleton.value.reload()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -364,27 +401,15 @@ const handleCategoryLevel1Click = async (labelId: number, index: number) => {
|
||||
*/
|
||||
const handleCategoryLevel2Click = async (labelId: number, index: number) => {
|
||||
currentLevel2Index.value = index
|
||||
await getBooksByLabel(labelId, 'category')
|
||||
categoryBooksSkeleton.value.reload()
|
||||
}
|
||||
|
||||
/**
|
||||
* 页面加载
|
||||
*/
|
||||
onMounted(() => {
|
||||
// 重置活动标签选中状态
|
||||
currentActivityIndex.value = 0
|
||||
showActivity.value = false
|
||||
})
|
||||
|
||||
/**
|
||||
* 页面显示
|
||||
*/
|
||||
onShow(() => {
|
||||
// 刷新数据
|
||||
getMyBooks()
|
||||
getRecommendBooks()
|
||||
getActivityLabels()
|
||||
getCategoryLabels()
|
||||
myBookSkeleton.value?.reload()
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -426,12 +451,12 @@ onShow(() => {
|
||||
.mine-block {
|
||||
padding: 20rpx;
|
||||
display: flex;
|
||||
gap: 20rpx;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
.mine-1,
|
||||
.mine-2 {
|
||||
width: 49%;
|
||||
height: 290rpx;
|
||||
border-radius: 15rpx;
|
||||
padding: 30rpx;
|
||||
@@ -584,7 +609,6 @@ onShow(() => {
|
||||
|
||||
.activity-list {
|
||||
width: 100%;
|
||||
margin-top: 20rpx;
|
||||
padding-left: 10rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -617,7 +641,6 @@ onShow(() => {
|
||||
|
||||
.book-block {
|
||||
padding: 20rpx 20rpx 0;
|
||||
|
||||
.book-tab-one {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -700,7 +723,6 @@ onShow(() => {
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
flex-wrap: wrap;
|
||||
margin-top: 20rpx;
|
||||
|
||||
.book-item {
|
||||
width: 49%;
|
||||
|
||||
Reference in New Issue
Block a user