|
|
|
@ -1,7 +1,10 @@ |
|
|
|
<template> |
|
|
|
<view class="book-container"> |
|
|
|
<!-- 自定义顶部导航栏 --> |
|
|
|
<!-- 条件编译 --> |
|
|
|
|
|
|
|
<!-- #ifndef H5 --> |
|
|
|
<uv-status-bar></uv-status-bar> |
|
|
|
<!-- 自定义顶部导航栏 --> |
|
|
|
<view class="custom-navbar" :class="{ 'navbar-hidden': !showNavbar }"> |
|
|
|
<uv-status-bar></uv-status-bar> |
|
|
|
<view class="navbar-content"> |
|
|
|
@ -12,6 +15,7 @@ |
|
|
|
|
|
|
|
</view> |
|
|
|
</view> |
|
|
|
<!-- #endif --> |
|
|
|
|
|
|
|
<!-- Swiper内容区域 --> |
|
|
|
<swiper |
|
|
|
@ -24,72 +28,86 @@ |
|
|
|
:key="index" |
|
|
|
class="swiper-item" |
|
|
|
> |
|
|
|
<view class="content-area" @click="toggleNavbar"> |
|
|
|
|
|
|
|
<!-- 会员限制页面 --> |
|
|
|
<view v-if="!isMember && pagePay[index] === 'Y'" class="member-content" > |
|
|
|
<text class="member-title">{{ pageTitles[index] }}</text> |
|
|
|
<view class="member-button" @click.stop="unlockBook"> |
|
|
|
<text class="member-button-text">升级会员解锁</text> |
|
|
|
<scroll-view scroll-y style="height: 100vh;"> |
|
|
|
<view scroll-y class="content-area" @click="toggleNavbar"> |
|
|
|
|
|
|
|
<!-- 会员限制页面 --> |
|
|
|
<view v-if="!isMember && pagePay[index] === 'Y'" class="member-content" > |
|
|
|
<text class="member-title">{{ pageTitles[index] }}</text> |
|
|
|
<view class="member-button" @click.stop="unlockBook"> |
|
|
|
<text class="member-button-text">升级会员解锁</text> |
|
|
|
</view> |
|
|
|
</view> |
|
|
|
</view> |
|
|
|
|
|
|
|
<!-- 图片卡片页面 --> |
|
|
|
<view class="card-content" v-else-if="pageTypes[index] === '1'"> |
|
|
|
<view class="card-line"> |
|
|
|
<image :src="configParamContent('highlight_icon')" class="card-line-image" mode="aspectFill" /> |
|
|
|
<text class="card-line-text">划线重点</text> |
|
|
|
</view> |
|
|
|
<view v-for="(item, itemIndex) in page" :key="itemIndex"> |
|
|
|
<image class="card-image" v-if="item && item.type === 'image'" :src="item.imageUrl" mode="aspectFill"></image> |
|
|
|
<view class="english-text-container clickable-text" v-else-if="item && item.type === 'text' && item.language === 'en' && item.content" @click.stop="handleTextClick(item.content, item, index)"> |
|
|
|
<text |
|
|
|
v-for="(token, tokenIndex) in splitEnglishSentence(item.content)" |
|
|
|
:key="tokenIndex" |
|
|
|
|
|
|
|
:class="['english-token', { 'clickable-word': token.isWord && findWordDefinition(token.text) }]" |
|
|
|
@tap="token.isWord && findWordDefinition(token.text) ? handleWordClick(token.text) : null" |
|
|
|
user-select |
|
|
|
>{{ token.text }}</text> |
|
|
|
<!-- 图片卡片页面 --> |
|
|
|
<view class="card-content" v-else-if="pageTypes[index] === '1'"> |
|
|
|
<view class="card-line"> |
|
|
|
<image :src="configParamContent('highlight_icon')" class="card-line-image" mode="aspectFill" /> |
|
|
|
<text class="card-line-text">划线重点</text> |
|
|
|
</view> |
|
|
|
<view v-else-if="item && item.type === 'text' && item.language === 'zh' && item.content" @click.stop="handleTextClick(item.content, item, index)"> |
|
|
|
<text class="chinese-text clickable-text" user-select>{{ item.content }}</text> |
|
|
|
<view v-for="(item, itemIndex) in page" :key="itemIndex"> |
|
|
|
<image class="card-image" v-if="item && item.type === 'image'" :src="item.imageUrl" mode="aspectFill"></image> |
|
|
|
<view class="english-text-container clickable-text" v-else-if="item && item.type === 'text' && item.language === 'en' && item.content" @click.stop="handleTextClick(item.content, item, index)"> |
|
|
|
<text |
|
|
|
v-for="(token, tokenIndex) in splitEnglishSentence(item.content)" |
|
|
|
:key="tokenIndex" |
|
|
|
:class="['english-token', { 'clickable-word': token.isWord && findWordDefinition(token.text) }]" |
|
|
|
@tap="token.isWord && findWordDefinition(token.text) ? handleWordClick(token.text) : null" |
|
|
|
user-select |
|
|
|
>{{ token.text }}</text> |
|
|
|
</view> |
|
|
|
<view v-else-if="item && item.type === 'text' && item.language === 'zh' && item.content" @click.stop="handleTextClick(item.content, item, index)"> |
|
|
|
<text class="chinese-text clickable-text" user-select>{{ item.content }}</text> |
|
|
|
</view> |
|
|
|
</view> |
|
|
|
</view> |
|
|
|
</view> |
|
|
|
|
|
|
|
<view v-else> |
|
|
|
<view v-for="(item, itemIndex) in page" :key="itemIndex"> |
|
|
|
<!-- 文本页面 --> |
|
|
|
<view v-if="item && item.type === 'text' && item.content" class="text-content" > |
|
|
|
<view :class="{ 'text-highlight': isTextHighlighted(page, itemIndex) }" @click.stop="handleTextClick(item.content, item, index)"> |
|
|
|
<text |
|
|
|
class="content-text clickable-text" |
|
|
|
user-select |
|
|
|
> |
|
|
|
{{ item.content }} |
|
|
|
</text> |
|
|
|
<view v-else> |
|
|
|
<view v-for="(item, itemIndex) in page" :key="itemIndex"> |
|
|
|
<!-- 文本页面 --> |
|
|
|
<view v-if="item && item.type === 'text' && item.content" class="text-content" > |
|
|
|
<view :class="{ 'text-highlight': isTextHighlighted(page, itemIndex) }" @click.stop="handleTextClick(item.content, item, index)"> |
|
|
|
<text |
|
|
|
class="content-text clickable-text" |
|
|
|
user-select |
|
|
|
> |
|
|
|
{{ item.content }} |
|
|
|
</text> |
|
|
|
</view> |
|
|
|
</view> |
|
|
|
</view> |
|
|
|
|
|
|
|
<!-- 文本页面 --> |
|
|
|
<view v-else-if="item.type === 'image'" class="image-container"> |
|
|
|
<image class="content-image" :src="item.imageUrl" mode="aspectFill"></image> |
|
|
|
</view> |
|
|
|
<!-- 文本页面 --> |
|
|
|
<view v-else-if="item.type === 'image'" class="image-container"> |
|
|
|
<image class="content-image" :src="item.imageUrl" mode="widthFix"></image> |
|
|
|
</view> |
|
|
|
|
|
|
|
<!-- 视频页面 --> |
|
|
|
<view v-else-if="item.type === 'video'" class="video-content"> |
|
|
|
<video :src="item.video" class="video-player" controls :poster="item.poster"></video> |
|
|
|
<!-- 视频页面 --> |
|
|
|
<view v-else-if="item.type === 'video'" class="video-content" @click.stop> |
|
|
|
<!-- 视频加载状态 --> |
|
|
|
<view v-if="videoLoading" class="video-loading"> |
|
|
|
<text class="loading-text">视频加载中...</text> |
|
|
|
</view> |
|
|
|
|
|
|
|
<!-- 视频播放器 --> |
|
|
|
<video |
|
|
|
v-else |
|
|
|
src="https://qiniu-web-assets.dcloud.net.cn/unidoc/zh/2minute-demo.mp4" |
|
|
|
class="video-player" |
|
|
|
controls |
|
|
|
:poster="item.coverUrl" |
|
|
|
@loadstart="onVideoLoadStart" |
|
|
|
@loadeddata="onVideoLoadStart" |
|
|
|
|
|
|
|
@error="onVideoError" |
|
|
|
></video> |
|
|
|
</view> |
|
|
|
</view> |
|
|
|
|
|
|
|
|
|
|
|
</view> |
|
|
|
</view> |
|
|
|
</view> |
|
|
|
</scroll-view> |
|
|
|
</swiper-item> |
|
|
|
</swiper> |
|
|
|
|
|
|
|
|
|
|
|
<!-- 自定义底部控制栏 --> |
|
|
|
<view class="custom-tabbar" :class="{ 'tabbar-hidden': !showNavbar }"> |
|
|
|
<!-- 音频控制栏组件 --> |
|
|
|
@ -261,6 +279,9 @@ export default { |
|
|
|
isAudioLoading: false, // 音频是否正在加载 |
|
|
|
hasAudioData: false, // 是否有音频数据 |
|
|
|
audioLoadFailed: false, // 音频加载是否失败 |
|
|
|
|
|
|
|
// 视频状态相关 |
|
|
|
videoLoading: false, // 视频是否正在加载 |
|
|
|
courseIdList: [], |
|
|
|
bookTitle: '', |
|
|
|
courseList: [ |
|
|
|
@ -319,6 +340,29 @@ export default { |
|
|
|
} |
|
|
|
}, |
|
|
|
methods: { |
|
|
|
// 视频事件处理方法 |
|
|
|
onVideoLoadStart() { |
|
|
|
console.log("视频开始加载📺📺📺📺📺📺📺📺📺📺"); |
|
|
|
|
|
|
|
this.videoLoading = true; |
|
|
|
}, |
|
|
|
|
|
|
|
onVideoCanPlay() { |
|
|
|
console.log("视频可以播放"); |
|
|
|
|
|
|
|
this.videoLoading = false; |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
onVideoError() { |
|
|
|
this.videoLoading = false; |
|
|
|
uni.showToast({ |
|
|
|
title: '视频加载失败', |
|
|
|
icon: 'none', |
|
|
|
duration: 2000 |
|
|
|
}); |
|
|
|
}, |
|
|
|
|
|
|
|
// 獲取用戶會員信息 判斷是否和傳參傳過來的會員id相同 |
|
|
|
async getMemberInfo(){ |
|
|
|
const memberRes = await this.$api.member.getUserMemberInfo() |
|
|
|
@ -1700,7 +1744,7 @@ export default { |
|
|
|
<style lang="scss" scoped> |
|
|
|
.book-container { |
|
|
|
width: 100%; |
|
|
|
height: 100vh; |
|
|
|
min-height: 100vh; |
|
|
|
background-color: #F8F8F8; |
|
|
|
position: relative; |
|
|
|
overflow: hidden; |
|
|
|
@ -1755,23 +1799,55 @@ export default { |
|
|
|
|
|
|
|
.content-swiper { |
|
|
|
flex: 1; |
|
|
|
height: calc(100vh - 100rpx); |
|
|
|
// min-height: calc(100vh - 100rpx); |
|
|
|
// margin-top: 100rpx; |
|
|
|
margin-bottom: 100rpx; |
|
|
|
|
|
|
|
height: 100vh; |
|
|
|
} |
|
|
|
|
|
|
|
.swiper-item { |
|
|
|
height: 100%; |
|
|
|
min-height: 100vh; |
|
|
|
// background-color: red; |
|
|
|
} |
|
|
|
|
|
|
|
.content-area { |
|
|
|
flex: 1; |
|
|
|
padding: 0 40rpx; |
|
|
|
padding-top: 100rpx; |
|
|
|
padding: 30rpx 40rpx 100rpx; |
|
|
|
/* #ifndef H5 */ |
|
|
|
padding: 100rpx 40rpx; |
|
|
|
/* #endif */ |
|
|
|
// padding-top: ; |
|
|
|
// background: linear-gradient(180deg, #DEFFFF 0%, #FBFEFF 22.65%, #F0FBFF 100%); |
|
|
|
height: 100%; |
|
|
|
min-height: 100%; |
|
|
|
box-sizing: border-box; |
|
|
|
overflow-y: auto; |
|
|
|
.content-image{ |
|
|
|
width: 100%; |
|
|
|
height: auto; |
|
|
|
margin: 30rpx auto; |
|
|
|
} |
|
|
|
.video-content{ |
|
|
|
width: 100%; |
|
|
|
height: auto; |
|
|
|
margin: 30rpx auto; |
|
|
|
position: relative; |
|
|
|
|
|
|
|
.video-player{ |
|
|
|
// height: 100%; |
|
|
|
width: 100%; |
|
|
|
// margin: 0 auto; |
|
|
|
// height: auto; |
|
|
|
} |
|
|
|
|
|
|
|
.video-loading { |
|
|
|
position: absolute; |
|
|
|
top: 50%; |
|
|
|
left: 50%; |
|
|
|
transform: translate(-50%, -50%); |
|
|
|
color: #666; |
|
|
|
font-size: 28rpx; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
.card-content { |
|
|
|
@ -1811,6 +1887,7 @@ export default { |
|
|
|
width: 590rpx; |
|
|
|
height: 268rpx; |
|
|
|
border-radius: 24rpx; |
|
|
|
margin: 30rpx auto; |
|
|
|
// margin-bottom: 20rpx; |
|
|
|
} |
|
|
|
.english-text { |
|
|
|
@ -1900,22 +1977,23 @@ export default { |
|
|
|
color: #FFFFFF; |
|
|
|
} |
|
|
|
|
|
|
|
.video-content { |
|
|
|
width: 100vw; |
|
|
|
margin: 200rpx -40rpx 0; |
|
|
|
height: 500rpx; |
|
|
|
background-color: #FFFFFF; |
|
|
|
// padding: 40rpx; |
|
|
|
border-radius: 24rpx; |
|
|
|
display: flex; |
|
|
|
align-items: center; |
|
|
|
justify-content: center; |
|
|
|
.video-player{ |
|
|
|
width: 670rpx; |
|
|
|
margin: 0 auto; |
|
|
|
height: 376rpx; |
|
|
|
} |
|
|
|
} |
|
|
|
// .video-content { |
|
|
|
// width: 100%; |
|
|
|
// height: auto; |
|
|
|
// // margin: 200rpx -40rpx 0; |
|
|
|
// // height: 500rpx; |
|
|
|
// background-color: #FFFFFF; |
|
|
|
// // padding: 40rpx; |
|
|
|
// border-radius: 24rpx; |
|
|
|
// display: flex; |
|
|
|
// align-items: center; |
|
|
|
// justify-content: center; |
|
|
|
// .video-player{ |
|
|
|
// width: 100%; |
|
|
|
// margin: 0 auto; |
|
|
|
// height: auto; |
|
|
|
// } |
|
|
|
// } |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|