Browse Source

feat: 优化H5适配和页面布局,修复视频加载问题

- 为H5平台添加条件编译,优化导航栏和页面布局
- 修复视频加载状态处理和错误提示
- 调整会员中心权益展示方式为动态渲染
- 优化书籍页面滚动体验和图片视频显示
- 统一页面标题和导航栏样式
hfll
hflllll 2 weeks ago
parent
commit
ed8da100e4
9 changed files with 287 additions and 136 deletions
  1. +61
    -10
      pages.json
  2. +12
    -9
      pages/components/SplashScreen.vue
  3. +0
    -2
      pages/index/desk.vue
  4. +26
    -12
      pages/index/member.vue
  5. +7
    -0
      pages/index/user.vue
  6. +153
    -75
      subPages/home/book.vue
  7. +10
    -10
      subPages/home/directory.vue
  8. +16
    -17
      subPages/member/recharge.vue
  9. +2
    -1
      utils/share.js

+ 61
- 10
pages.json View File

@ -3,12 +3,17 @@
{
"path": "pages/index/home",
"style": {
"navigationStyle": "custom"
"navigationStyle": "custom",
"navigationBarTitleText": "主页"
}
},
{
"path": "pages/index/desk",
"style": {
// #ifdef H5
"navigationStyle": "custom",
// #endif
"navigationBarTitleText": "书桌",
"enablePullDownRefresh": true
}
@ -17,14 +22,17 @@
"path": "pages/index/member",
"style": {
"navigationStyle": "custom",
"enablePullDownRefresh": true
"enablePullDownRefresh": true,
"navigationBarTitleText": "会员中心"
}
},
{
"path": "pages/index/user",
"style": {
"navigationStyle": "custom",
"enablePullDownRefresh": true
"enablePullDownRefresh": true,
"navigationBarTitleText": "用户中心"
}
}
],
@ -35,18 +43,23 @@
{
"path": "login/login",
"style": {
"navigationStyle": "custom"
"navigationStyle": "custom",
"navigationBarTitleText": "登录"
}
},
{
"path": "login/userInfo",
"style": {
"navigationStyle": "custom"
"navigationStyle": "custom",
"navigationBarTitleText": "用户信息"
}
},
{
"path": "home/plan",
"style": {
// #ifdef H5
"navigationStyle": "custom",
// #endif
"navigationBarTitleText": "个人语境方案定制",
"navigationBarTextStyle": "white",
"navigationBarBackgroundColor": "#06DADC"
@ -55,44 +68,68 @@
{
"path": "home/search",
"style": {
// #ifdef H5
"navigationStyle": "custom",
// #endif
"navigationBarTitleText": "搜索"
}
},
{
"path": "home/submit",
"style": {
// #ifdef H5
"navigationStyle": "custom",
// #endif
"navigationBarTitleText": "报名个人语境定制"
}
},
{
"path": "home/directory",
"style": {
"navigationBarTitleText": "",
// #ifndef H5
"navigationBarTitleText": "目录",
"navigationBarBackgroundColor": "#264C8F",
"navigationBarTextStyle": "white"
"navigationBarTextStyle": "white",
// #endif
// #ifdef H5
"navigationStyle": "custom",
"navigationBarTitleText": "目录"
// #endif
}
},
{
"path": "user/introduce",
"style": {
// #ifdef H5
"navigationStyle": "custom",
// #endif
"navigationBarTitleText": "产品介绍"
}
},
{
"path": "user/policy",
"style": {
// #ifdef H5
"navigationStyle": "custom",
// #endif
"navigationBarTitleText": "服务协议与隐私政策"
}
},
{
"path": "user/profile",
"style": {
// #ifdef H5
"navigationStyle": "custom",
// #endif
"navigationBarTitleText": "资料修改"
}
},
{
"path": "user/discount",
"style": {
// #ifdef H5
"navigationStyle": "custom",
// #endif
"navigationBarTitleText": "我的优惠券"
}
},
@ -105,36 +142,50 @@
{
"path": "user/team",
"style": {
// #ifdef H5
"navigationStyle": "custom",
// #endif
"navigationBarTitleText": "我的团队"
}
},
{
"path": "user/cash",
"style": {
// #ifdef H5
"navigationStyle": "custom",
// #endif
"navigationBarTitleText": "提现"
}
},
{
"path": "member/recharge",
"style": {
"navigationStyle": "custom"
"navigationStyle": "custom",
"navigationBarTitleText": "充值"
}
},
{
"path": "home/book",
"style": {
"navigationStyle": "custom"
"navigationStyle": "custom",
"navigationBarTitleText": "书籍"
}
},
{
"path": "home/music",
"style": {
// #ifdef H5
"navigationStyle": "custom",
// #endif
"navigationBarTitleText": "音乐切换"
}
},
{
"path": "user/share",
"style": {
// #ifdef H5
"navigationStyle": "custom",
// #endif
"navigationBarTitleText": "分享"
}
}
@ -149,7 +200,7 @@
}
},
"globalStyle": {
"navigationBarTitleText": "uni-app",
"navigationBarTitleText": "四零语境",
"navigationBarBackgroundColor": "#fff",
"navigationBarTextStyle": "black"
},


+ 12
- 9
pages/components/SplashScreen.vue View File

@ -180,7 +180,7 @@ export default {
.splash-content {
flex: 1;
width: 100%;
height: 100%;
height: 80%;
overflow: hidden;
//
@ -197,7 +197,7 @@ export default {
.skip-button {
position: absolute;
top: 100rpx;
top: 40rpx;
left: 40rpx;
background-color: #0000004D;
@ -217,13 +217,16 @@ export default {
//
.text-container {
position: absolute;
bottom: 80rpx;
left: 40rpx;
z-index: 10001;
// position: absolute;
// bottom: 80rpx;
// left: 40rpx;
// z-index: 10001;
height: 20%;
width: 100%;
padding: 20rpx;
display: flex;
flex-direction: column;
gap: 8rpx;
gap: 30rpx;
}
//
@ -233,7 +236,7 @@ export default {
font-size: 32rpx; // 16pxrpx
line-height: 48rpx; // 24pxrpx
letter-spacing: 0;
color: #FFFFFF;
color: black;
background: transparent;
}
@ -244,7 +247,7 @@ export default {
font-size: 32rpx; // 16pxrpx
line-height: 48rpx; // 24pxrpx
letter-spacing: 0;
color: #FFFFFF;
color: black;
background: transparent;
}


+ 0
- 2
pages/index/desk.vue View File

@ -1,7 +1,5 @@
<template>
<view class="desk-container">
<!-- 顶部搜索栏 -->
<view class="header">
<view class="search-container">


+ 26
- 12
pages/index/member.vue View File

@ -1,6 +1,7 @@
<template>
<view class="container">
<view class="header">
<!-- #ifndef H5 -->
<view class="header-bg">
<image
src="/static/会员背景.png"
@ -9,6 +10,7 @@
/>
<text class="header-title">会员中心</text>
</view>
<!-- #endif -->
<view class="header-content">
<view class="zuanshi">
@ -80,18 +82,12 @@
<view class="benefits-list">
<!-- 碎片学习 系统掌握 -->
<view class="benefit-item">
<view class="benefit-content">
<view class="benefit-title">碎片学习 系统掌握</view>
<view class="benefit-desc">根据薄弱点智能推荐每节课3-5分钟碎片化完成系统学习</view>
</view>
<view class="benefit-icon">
<image src="/static/会员图片1.png" mode="aspectFit"></image>
</view>
<view class="benefit-item" v-for="(item, index) in memberBenefits" :key="index">
<uv-parse :content="item"></uv-parse>
</view>
<!-- 匹配水平 -->
<view class="benefit-item">
<!-- <view class="benefit-item">
<view class="benefit-content">
<view class="benefit-title">匹配水平</view>
<view class="benefit-desc">依据水平精准推课不做无用功快速提升</view>
@ -99,10 +95,10 @@
<view class="benefit-icon">
<image src="/static/会员图片2.png" mode="aspectFit"></image>
</view>
</view>
</view> -->
<!-- 科学闭环测 讲练结合 -->
<view class="benefit-item">
<!-- <view class="benefit-item">
<view class="benefit-content">
<view class="benefit-title">科学闭环测 讲练结合</view>
<view class="benefit-desc">精心设计科学的学习流程 测试-讲解-练习-检验知识掌握更牢固</view>
@ -110,7 +106,7 @@
<view class="benefit-icon">
<image src="/static/会员图片3.png" mode="aspectFit"></image>
</view>
</view>
</view> -->
</view>
</view>
@ -191,6 +187,7 @@ export default{
return {
isLogin: uni.getStorageSync('token') ? true : false,
memberInfo: [],
memberBenefits: [],
userInfo: {
name: '战斗世界',
avatar: '/static/默认头像.png'
@ -242,6 +239,17 @@ export default{
if (memberRes.code === 200) {
this.memberInfo = [...memberRes.result]
}
if(!this.memberInfo.length){
//
this.getMemberBenefits()
}
},
//
async getMemberBenefits() {
const benefitsRes = await this.$api.member.getMemberList()
if (benefitsRes.code === 200) {
this.memberBenefits = benefitsRes.result.map(item => item.content)
}
},
//
async getUserInfo() {
@ -322,7 +330,13 @@ export default{
.header-content{
margin: 0 18rpx;
/* #ifndef H5 */
margin-top: -150rpx;
/* #endif */
/* #ifdef H5 */
margin-top: 100rpx;
/* #endif */
// height: 256rpx;
border-radius: 32rpx;
border-width: 2rpx;


+ 7
- 0
pages/index/user.vue View File

@ -2,8 +2,10 @@
<view class="user-container">
<!-- 用户信息区域 -->
<view class="user-info-section">
<!-- #ifndef H5 -->
<view class="page-title">我的</view>
<!-- #endif -->
<view class="user-info">
<view class="user-avatar" @click="goLogin">
<image :src="isLogin ? userInfo.avatar === 'undefined' ? displayInfo.avatar : userInfo.avatar : displayInfo.avatar" mode="aspectFill"></image>
@ -304,7 +306,12 @@ export default {
/* 用户信息区域 */
.user-info-section {
background: linear-gradient(180deg, #ABFFFF 0%, #FFFFFF 100%);
height: 300rpx;
/* #ifdef H5 */
height: 200rpx;
/* #endif */
// display: flex;
// align-items: center;
padding: 60rpx 32rpx 0rpx;


+ 153
- 75
subPages/home/book.vue View File

@ -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;
// }
// }


+ 10
- 10
subPages/home/directory.vue View File

@ -2,15 +2,15 @@
<view class="directory-container">
<view class="book-container">
<view class="book-info">
<view class="book-cover">
<image :src="bookInfo.booksImg" mode="aspectFill" :style="{width: '100%', height: '100%'}"></image>
</view>
<view class="book-details">
<view class="book-title">{{ bookInfo.translate }}</view>
<view class="book-subtitle">{{ bookInfo.booksName }}</view>
<view class="book-author">{{ bookInfo.booksAuthor }}</view>
<view class="book-level" :class="classMap[bookInfo.vipInfo.title]">{{ bookInfo.vipInfo.title }}</view>
</view>
<view class="book-cover">
<image :src="bookInfo.booksImg" mode="aspectFill" :style="{width: '100%', height: '100%'}"></image>
</view>
<view class="book-details">
<view class="book-title">{{ bookInfo.translate }}</view>
<view class="book-subtitle">{{ bookInfo.booksName }}</view>
<view class="book-author">{{ bookInfo.booksAuthor }}</view>
<view class="book-level" :class="classMap[bookInfo.vipInfo.title]">{{ bookInfo.vipInfo.title }}</view>
</view>
</view>
<view class="book-knowledge">
<view class="book-knowledge-title">
@ -172,7 +172,7 @@ export default {
return this.isCourseSortReversed ? [...list].reverse() : list;
},
computedList(){
return this.courseList.records.slice(0, 5)
return this.courseList.records?.slice(0, 5) || []
}
},
methods: {


+ 16
- 17
subPages/member/recharge.vue View File

@ -7,11 +7,13 @@
class="header-img"
mode="scaleToFill"
/>
<text class="header-title">会员开通</text>
<!-- 加一个推出箭头 -->
<view class="header-icon" @click="goBack">
<uv-icon name="arrow-left" color="#000" size="20" />
</view>
<!-- #ifndef H5 -->
<text class="header-title">会员开通</text>
<!-- 加一个推出箭头 -->
<view class="header-icon" @click="goBack">
<uv-icon name="arrow-left" color="#000" size="20" />
</view>
<!-- #endif -->
<!-- 轮播容器 -->
<view class="uv-demo-block swiper-container">
<uv-swiper
@ -113,18 +115,12 @@
<view class="benefits-list">
<!-- 碎片学习 系统掌握 -->
<view class="benefit-item">
<view class="benefit-content">
<view class="benefit-title">碎片学习 系统掌握</view>
<view class="benefit-desc">根据薄弱点智能推荐每节课3-5分钟碎片化完成系统学习</view>
</view>
<view class="benefit-icon">
<image src="/static/会员图片1.png" mode="aspectFit"></image>
</view>
<view class="benefit-item" v-for="item in packageList" :key="item.id">
<uv-parse :content="item.content" />
</view>
<!-- 匹配水平 -->
<view class="benefit-item">
<!-- 匹配水平 -->
<!-- <view class="benefit-item">
<view class="benefit-content">
<view class="benefit-title">匹配水平</view>
<view class="benefit-desc">依据水平精准推课不做无用功快速提升</view>
@ -134,7 +130,7 @@
</view>
</view>
<!-- 科学闭环测 讲练结合 -->
科学闭环测 讲练结合
<view class="benefit-item">
<view class="benefit-content">
<view class="benefit-title">科学闭环测 讲练结合</view>
@ -143,7 +139,7 @@
<view class="benefit-icon">
<image src="/static/会员图片3.png" mode="aspectFit"></image>
</view>
</view>
</view> -->
</view>
</view>
@ -282,6 +278,9 @@
position: relative;
width: 100%;
height: 500rpx;
/* #ifdef H5 */
margin-top: -100rpx;
/* #endif */
// background: red;
.header-img{
width: 100%;


+ 2
- 1
utils/share.js View File

@ -21,7 +21,8 @@ function share() { //微信分享
} = res.result
console.log(appId);
jWeixin.config({
debug: true,
// debug: true,
debug: false,
appId: appId,
nonceStr: nonceStr,
signature: signature,


Loading…
Cancel
Save