|
@@ -1,243 +1,257 @@
|
|
|
<template>
|
|
<template>
|
|
|
<view class="container">
|
|
<view class="container">
|
|
|
- <view @click="aaaa">asdfasdfdasfads</view>
|
|
|
|
|
- <view class="icon-title-navBar-box">
|
|
|
|
|
- <view class="nav-bar-icon" @click="handleBack"></view>
|
|
|
|
|
- <view class="nav-bar-title">学习</view>
|
|
|
|
|
|
|
+ <!-- 当前单元标题(吸顶效果) -->
|
|
|
|
|
+ <view class="sticky-title" v-if="currentUnitName">
|
|
|
|
|
+ {{ showStickyTitle ? currentUnitName : "学习" }}
|
|
|
</view>
|
|
</view>
|
|
|
- <!-- z-paging 列表 -->
|
|
|
|
|
- <z-paging ref="paging" @scroll="onScroll" @query="loadData" :refresher-enabled="false"
|
|
|
|
|
- :loading-more-enabled="false" :auto="false" :fixed="false" :hide-empty-view="true">
|
|
|
|
|
|
|
|
|
|
|
|
+ <!-- 返回顶部按钮 -->
|
|
|
|
|
+ <view class="back-top" v-if="showBackTop" @click="scrollToTop">
|
|
|
|
|
+ <text>↑</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+
|
|
|
|
|
+ <scroll-view scroll-y class="scroll-view" :scroll-top="scrollTop" @scroll="onScroll">
|
|
|
|
|
+ <view class="subject-info" v-if="subjectDetail">
|
|
|
|
|
+ <text class="course-name">{{ subjectDetail.chanpinName }}</text>
|
|
|
|
|
+ <text class="course-name">等级:{{ subjectDetail.dengjiName }}</text>
|
|
|
|
|
+ <text class="course-name">版本:{{ subjectDetail.dengjiName }}</text>
|
|
|
|
|
+ <text class="course-name">单元:{{ subjectDetail.curDanyuanName }}</text>
|
|
|
|
|
+ <progress :percent="20" class="exam-progress-box" stroke-width="10" backgroundColor="#3c7dfd"
|
|
|
|
|
+ activeColor="#ffd11c" />
|
|
|
|
|
+ <text class="course-name">继续学习</text>
|
|
|
|
|
+ </view>
|
|
|
<!-- 单元列表 -->
|
|
<!-- 单元列表 -->
|
|
|
- <view v-for="(danyuan, danyuanIndex) in danyuanList" :key="danyuan.danyuanId"
|
|
|
|
|
- :id="'danyuan-' + danyuanIndex" class="danyuan-item">
|
|
|
|
|
- <!-- 当前学科信息 -->
|
|
|
|
|
- <view class="subject-info" v-if="subjectDetail">
|
|
|
|
|
- <text class="course-name">{{ subjectDetail.chanpinName }}</text>
|
|
|
|
|
- <text class="course-name">等级:{{ subjectDetail.dengjiName }}</text>
|
|
|
|
|
- <text class="course-name">版本:{{ subjectDetail.dengjiName }}</text>
|
|
|
|
|
- <text class="course-name">单元:{{ subjectDetail.curDanyuanName }}</text>
|
|
|
|
|
- <text class="course-name">课程:{{ subjectDetail.chanpinName }}</text>
|
|
|
|
|
- </view>
|
|
|
|
|
|
|
+ <view v-for="(unit, index) in danyuanList" :key="unit.danyuanId" :id="'unit-' + index" class="unit-item">
|
|
|
<!-- 单元标题 -->
|
|
<!-- 单元标题 -->
|
|
|
- <view class="danyuan-header">
|
|
|
|
|
- <text class="danyuan-title">{{ danyuan.danyuanName }}</text>
|
|
|
|
|
- <text class="danyuan-intro">{{ danyuan.danyuanIntro }}</text>
|
|
|
|
|
|
|
+ <view class="unit-title">
|
|
|
|
|
+ <text class="unit-name">{{ unit.danyuanName }}</text>
|
|
|
|
|
+ <text class="unit-intro">{{ unit.danyuanIntro }}</text>
|
|
|
</view>
|
|
</view>
|
|
|
-
|
|
|
|
|
|
|
+ <text class="unit-name">{{ unit.danyuanName }}</text>
|
|
|
<!-- 节列表 -->
|
|
<!-- 节列表 -->
|
|
|
- <view v-if="danyuan.jieList && danyuan.jieList.length > 0">
|
|
|
|
|
- <view v-for="jie in danyuan.jieList" :key="jie.jieId" class="jie-item">
|
|
|
|
|
- <text class="jie-name">{{ jie.number }}. {{ jie.jieName }}</text>
|
|
|
|
|
- <text class="jie-desc">{{ jie.jieIntro }}</text>
|
|
|
|
|
|
|
+ <view v-for="section in unit.jieList" :key="section.jieId" class="section-item">
|
|
|
|
|
+ <view class="status" :class="section.wanchengFlag === 1 ? 'completed' : 'uncompleted'">
|
|
|
|
|
+ {{ section.wanchengFlag === 1 ? '已完成' : '未开始' }}
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <view class="section-left">
|
|
|
|
|
+ <image class="section-cover" :src="section.cover" mode="aspectFill"></image>
|
|
|
|
|
+ <view class="section-info">
|
|
|
|
|
+ <text class="section-name">{{ section.jieName }}</text>
|
|
|
|
|
+ <text class="section-desc">{{ section.jieIntro }}</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <view class="section-right">
|
|
|
|
|
+ <text class="section-number">第{{ section.number }}节</text>
|
|
|
|
|
+ <text>播放按钮</text>
|
|
|
</view>
|
|
</view>
|
|
|
- </view>
|
|
|
|
|
-
|
|
|
|
|
- <!-- 无课程内容提示 -->
|
|
|
|
|
- <view v-else class="empty-jie">
|
|
|
|
|
- <text class="empty-text">暂无课程内容</text>
|
|
|
|
|
</view>
|
|
</view>
|
|
|
</view>
|
|
</view>
|
|
|
- </z-paging>
|
|
|
|
|
|
|
|
|
|
- <!-- 回到顶部按钮 -->
|
|
|
|
|
- <view v-if="showBackToTop" class="back-to-top" @click="scrollToTop">
|
|
|
|
|
- <text class="back-to-top-icon">↑</text>
|
|
|
|
|
- <text class="back-to-top-text">顶部</text>
|
|
|
|
|
- </view>
|
|
|
|
|
-
|
|
|
|
|
- <!-- 当前单元提示 -->
|
|
|
|
|
- <view v-if="currentUnitName" class="current-unit-tip">
|
|
|
|
|
- <text>{{ currentUnitName }}</text>
|
|
|
|
|
- </view>
|
|
|
|
|
-
|
|
|
|
|
- <CustomTabBar :currentTabNumber="0"></CustomTabBar>
|
|
|
|
|
-
|
|
|
|
|
|
|
+ <!-- 底部占位 -->
|
|
|
|
|
+ <view style="height: 100px; text-align: center;">查看更多内容</view>
|
|
|
|
|
+ </scroll-view>
|
|
|
|
|
+ <CustomTabBar :currentTabNumber="1"></CustomTabBar>
|
|
|
</view>
|
|
</view>
|
|
|
</template>
|
|
</template>
|
|
|
|
|
|
|
|
<script>
|
|
<script>
|
|
|
|
|
+ import CustomTabBar from '@/components/custom-tabbar/custom-tabbar.vue';
|
|
|
import {
|
|
import {
|
|
|
shuxueChanpinBanben
|
|
shuxueChanpinBanben
|
|
|
} from "@/api/chanpinneirong.js"
|
|
} from "@/api/chanpinneirong.js"
|
|
|
- import CustomTabBar from '@/components/custom-tabbar/custom-tabbar.vue';
|
|
|
|
|
export default {
|
|
export default {
|
|
|
data() {
|
|
data() {
|
|
|
return {
|
|
return {
|
|
|
- activeSubjectId: 2,
|
|
|
|
|
-
|
|
|
|
|
- // 学科详情数据
|
|
|
|
|
|
|
+ danyuanList: [],
|
|
|
|
|
+ currentUnitName: "",
|
|
|
subjectDetail: null,
|
|
subjectDetail: null,
|
|
|
|
|
+ showBackTop: false,
|
|
|
|
|
+ scrollTop: 0,
|
|
|
|
|
+ pageCacheKey: 'learn_page_scroll_cache',
|
|
|
|
|
+ hasRestoredScroll: false,
|
|
|
|
|
+ showStickyTitle: false
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ components: {
|
|
|
|
|
+ CustomTabBar
|
|
|
|
|
+ },
|
|
|
|
|
+ onShow() {
|
|
|
|
|
|
|
|
- // 单元列表
|
|
|
|
|
- danyuanList: [],
|
|
|
|
|
|
|
+ console.log('学习页面显示,尝试恢复滚动位置')
|
|
|
|
|
+ // 如果没有恢复过,从缓存恢复
|
|
|
|
|
+ if (!this.hasRestoredScroll) {
|
|
|
|
|
+ this.restoreScrollPosition()
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
|
|
|
- // 当前单元名称
|
|
|
|
|
- currentUnitName: '',
|
|
|
|
|
|
|
+ // 新增:页面隐藏时保存滚动位置
|
|
|
|
|
+ onHide() {
|
|
|
|
|
|
|
|
- // 单元位置信息
|
|
|
|
|
- unitPositions: [],
|
|
|
|
|
|
|
+ console.log('学习页面隐藏,保存滚动位置')
|
|
|
|
|
+ this.saveScrollPosition()
|
|
|
|
|
+ },
|
|
|
|
|
|
|
|
- // 新增:是否显示回到顶部按钮
|
|
|
|
|
- showBackToTop: false,
|
|
|
|
|
|
|
+ onLoad() {
|
|
|
|
|
|
|
|
- // 新增:显示回到顶部按钮的阈值(滚动多少距离后显示)
|
|
|
|
|
- backToTopThreshold: 500
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ this.loadDataFromApi()
|
|
|
},
|
|
},
|
|
|
- components: {
|
|
|
|
|
- CustomTabBar
|
|
|
|
|
- },
|
|
|
|
|
- mounted() {
|
|
|
|
|
- //初始化加载数据
|
|
|
|
|
- this.$nextTick(() => {
|
|
|
|
|
- // this.$refs.paging.reload()
|
|
|
|
|
- })
|
|
|
|
|
|
|
+
|
|
|
|
|
+ onReady() {
|
|
|
|
|
+
|
|
|
|
|
+ // 页面渲染完成后执行
|
|
|
|
|
+ setTimeout(() => {
|
|
|
|
|
+ this.getUnitPositions();
|
|
|
|
|
+ }, 300);
|
|
|
},
|
|
},
|
|
|
|
|
|
|
|
methods: {
|
|
methods: {
|
|
|
- aaaa(){
|
|
|
|
|
- this.$refs.paging.reload()
|
|
|
|
|
- },
|
|
|
|
|
- handleBack() {
|
|
|
|
|
|
|
|
|
|
- },
|
|
|
|
|
- // z-paging 加载数据
|
|
|
|
|
- async loadData() {
|
|
|
|
|
- // 目前只处理数学学科
|
|
|
|
|
- if (this.activeSubjectId !== 2) {
|
|
|
|
|
- this.$refs.paging.complete([])
|
|
|
|
|
- return
|
|
|
|
|
|
|
+
|
|
|
|
|
+ loadDataFromApi() {
|
|
|
|
|
+ const req = {
|
|
|
|
|
+ banbenId: 7
|
|
|
}
|
|
}
|
|
|
|
|
+ shuxueChanpinBanben(req).then(res => {
|
|
|
|
|
+ this.subjectDetail = res.data
|
|
|
|
|
+ this.danyuanList = res.data.danyuanList || []
|
|
|
|
|
+ // 初始化当前单元
|
|
|
|
|
+ if (this.danyuanList.length > 0) {
|
|
|
|
|
+ this.currentUnitName = this.danyuanList[0].danyuanName
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+ },
|
|
|
|
|
|
|
|
- try {
|
|
|
|
|
- // 调用数学API
|
|
|
|
|
- const req = {
|
|
|
|
|
- banbenId: 7
|
|
|
|
|
|
|
+ // 保存滚动位置到缓存
|
|
|
|
|
+ saveScrollPosition() {
|
|
|
|
|
+ if (this.scrollTop > 0) {
|
|
|
|
|
+ const scrollData = {
|
|
|
|
|
+ scrollTop: this.scrollTop,
|
|
|
|
|
+ currentUnit: this.currentUnitName,
|
|
|
|
|
+ timestamp: Date.now()
|
|
|
}
|
|
}
|
|
|
- const res = await shuxueChanpinBanben(req)
|
|
|
|
|
|
|
+ uni.setStorageSync(this.pageCacheKey, scrollData)
|
|
|
|
|
+ console.log('保存滚动位置到缓存:', scrollData.scrollTop, '当前单元:', scrollData.currentUnit)
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
|
|
|
- if (res.code === 0 && res.data) {
|
|
|
|
|
- // 直接使用后端返回的数据
|
|
|
|
|
- this.subjectDetail = res.data
|
|
|
|
|
- this.danyuanList = res.data.danyuanList || []
|
|
|
|
|
|
|
+ // 从缓存恢复滚动位置
|
|
|
|
|
+ restoreScrollPosition() {
|
|
|
|
|
+ try {
|
|
|
|
|
+ const saved = uni.getStorageSync(this.pageCacheKey)
|
|
|
|
|
+ if (saved && saved.scrollTop > 0) {
|
|
|
|
|
+ this.showStickyTitle = true
|
|
|
|
|
+ // 延迟执行滚动
|
|
|
|
|
+ setTimeout(() => {
|
|
|
|
|
+ this.scrollTop = saved.scrollTop
|
|
|
|
|
+
|
|
|
|
|
+ // 恢复当前单元名称
|
|
|
|
|
+ if (saved.currentUnit) {
|
|
|
|
|
+ this.currentUnitName = saved.currentUnit
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- // 告诉z-paging加载完成
|
|
|
|
|
- this.$refs.paging.complete(this.danyuanList)
|
|
|
|
|
|
|
+ this.hasRestoredScroll = true
|
|
|
|
|
|
|
|
- // 计算单元位置
|
|
|
|
|
- this.$nextTick(() => {
|
|
|
|
|
- this.calculateUnitPositions()
|
|
|
|
|
- })
|
|
|
|
|
|
|
+ // 再次延迟确保滚动生效
|
|
|
|
|
+ // setTimeout(() => {
|
|
|
|
|
+ // this.scrollTop = saved.scrollTop + 0.01
|
|
|
|
|
+ // }, 50)
|
|
|
|
|
+ }, 100)
|
|
|
|
|
+ } else {
|
|
|
|
|
+ this.showStickyTitle = false
|
|
|
}
|
|
}
|
|
|
- } catch (error) {
|
|
|
|
|
- console.error('加载失败:', error)
|
|
|
|
|
- this.$refs.paging.complete(false)
|
|
|
|
|
|
|
+ } catch (e) {
|
|
|
|
|
+ console.error('读取缓存失败:', e)
|
|
|
}
|
|
}
|
|
|
},
|
|
},
|
|
|
- // 计算单元位置
|
|
|
|
|
- calculateUnitPositions() {
|
|
|
|
|
- if (!this.danyuanList || this.danyuanList.length === 0) return
|
|
|
|
|
- // 清空位置信息
|
|
|
|
|
- this.unitPositions = []
|
|
|
|
|
- // 使用 $nextTick 确保DOM已更新
|
|
|
|
|
- this.$nextTick(() => {
|
|
|
|
|
- // 稍微延迟确保渲染完成
|
|
|
|
|
- setTimeout(() => {
|
|
|
|
|
- const query = uni.createSelectorQuery().in(this)
|
|
|
|
|
-
|
|
|
|
|
- // 一次性查询所有单元
|
|
|
|
|
- const selectors = this.danyuanList.map((_, index) => {
|
|
|
|
|
- return query.select(`#danyuan-${index}`).boundingClientRect()
|
|
|
|
|
- })
|
|
|
|
|
- // 执行查询
|
|
|
|
|
- query.exec((results) => {
|
|
|
|
|
- if (results && results.length === this.danyuanList.length) {
|
|
|
|
|
- results.forEach((rect, index) => {
|
|
|
|
|
- if (rect) {
|
|
|
|
|
- // 保存相对位置(注意:这是相对于屏幕的位置)
|
|
|
|
|
- this.$set(this.unitPositions, index, {
|
|
|
|
|
- top: rect.top,
|
|
|
|
|
- height: rect.height,
|
|
|
|
|
- name: this.danyuanList[index]
|
|
|
|
|
- .danyuanName
|
|
|
|
|
- })
|
|
|
|
|
- }
|
|
|
|
|
- })
|
|
|
|
|
- console.log('单元位置计算完成:', this.unitPositions)
|
|
|
|
|
- }
|
|
|
|
|
- })
|
|
|
|
|
- }, 100)
|
|
|
|
|
- })
|
|
|
|
|
|
|
+
|
|
|
|
|
+ // 获取每个单元的位置信息
|
|
|
|
|
+ getUnitPositions() {
|
|
|
|
|
+ const query = uni.createSelectorQuery().in(this);
|
|
|
|
|
+
|
|
|
|
|
+ this.danyuanList.forEach((unit, index) => {
|
|
|
|
|
+ query.select('#unit-' + index).boundingClientRect();
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ query.exec((res) => {
|
|
|
|
|
+ console.log('单元位置信息:', res);
|
|
|
|
|
+ });
|
|
|
},
|
|
},
|
|
|
- // 滚动事件 - 修改后版本
|
|
|
|
|
- onScroll(e) {
|
|
|
|
|
- const scrollTop = e.detail.scrollTop
|
|
|
|
|
- console.log('滚动位置:', scrollTop)
|
|
|
|
|
|
|
|
|
|
- // 新增:控制回到顶部按钮的显示/隐藏
|
|
|
|
|
- this.showBackToTop = scrollTop > this.backToTopThreshold
|
|
|
|
|
|
|
+ // 滚动事件处理
|
|
|
|
|
+ onScroll(e) {
|
|
|
|
|
+ const scrollTop = e.detail.scrollTop;
|
|
|
|
|
|
|
|
- // 查找当前滚动到的单元
|
|
|
|
|
- let currentUnitName = ''
|
|
|
|
|
|
|
+ // 更新滚动位置
|
|
|
|
|
+ this.scrollTop = scrollTop;
|
|
|
|
|
|
|
|
- for (let i = this.unitPositions.length - 1; i >= 0; i--) {
|
|
|
|
|
- const position = this.unitPositions[i]
|
|
|
|
|
- if (position && scrollTop >= position.top - 50) {
|
|
|
|
|
- currentUnitName = position.name
|
|
|
|
|
- break
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ // 实时保存到缓存
|
|
|
|
|
+ const saveData = {
|
|
|
|
|
+ scrollTop: scrollTop,
|
|
|
|
|
+ currentUnit: this.currentUnitName,
|
|
|
|
|
+ timestamp: Date.now()
|
|
|
}
|
|
}
|
|
|
|
|
+ uni.setStorageSync(this.pageCacheKey, saveData)
|
|
|
|
|
|
|
|
- // 更新当前单元提示
|
|
|
|
|
- if (currentUnitName && currentUnitName !== this.currentUnitName) {
|
|
|
|
|
- this.currentUnitName = currentUnitName
|
|
|
|
|
- console.log('切换到单元:', currentUnitName)
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ // 显示/隐藏返回顶部按钮
|
|
|
|
|
+ this.showBackTop = scrollTop > 400;
|
|
|
|
|
+
|
|
|
|
|
+ // 监听滚动,更新当前显示的单元
|
|
|
|
|
+ this.updateCurrentUnit(scrollTop);
|
|
|
},
|
|
},
|
|
|
|
|
|
|
|
- // 新增:回到顶部方法
|
|
|
|
|
- scrollToTop() {
|
|
|
|
|
- console.log('执行回到顶部')
|
|
|
|
|
-
|
|
|
|
|
- // 方法1: 使用z-paging的scrollToTop方法(最简单)
|
|
|
|
|
- if (this.$refs.paging) {
|
|
|
|
|
- this.$refs.paging.scrollToTop({
|
|
|
|
|
- animated: true,
|
|
|
|
|
- duration: 300
|
|
|
|
|
- })
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ // 更新当前显示的单元
|
|
|
|
|
+ updateCurrentUnit(scrollTop) {
|
|
|
|
|
|
|
|
- // 方法2: 对于H5环境,额外滚动整个页面
|
|
|
|
|
- // #ifdef H5
|
|
|
|
|
- setTimeout(() => {
|
|
|
|
|
- window.scrollTo({
|
|
|
|
|
- top: 0,
|
|
|
|
|
- behavior: 'smooth'
|
|
|
|
|
- })
|
|
|
|
|
- }, 100)
|
|
|
|
|
- // #endif
|
|
|
|
|
-
|
|
|
|
|
- // 方法3: 对于App环境,使用uni的pageScrollTo
|
|
|
|
|
- // #ifdef APP-PLUS
|
|
|
|
|
- uni.pageScrollTo({
|
|
|
|
|
- scrollTop: 0,
|
|
|
|
|
- duration: 300
|
|
|
|
|
- })
|
|
|
|
|
- // #endif
|
|
|
|
|
|
|
+ const query = uni.createSelectorQuery().in(this);
|
|
|
|
|
|
|
|
- // 隐藏回到顶部按钮
|
|
|
|
|
- this.showBackToTop = false
|
|
|
|
|
|
|
+ for (let i = 0; i < this.danyuanList.length; i++) {
|
|
|
|
|
+ query.select('#unit-' + i).boundingClientRect();
|
|
|
|
|
+ }
|
|
|
|
|
+ query.exec((res) => {
|
|
|
|
|
+ // 添加一个偏移量,让切换更平滑
|
|
|
|
|
+ const offset = 80;
|
|
|
|
|
+ for (let i = 0; i < res.length; i++) {
|
|
|
|
|
+ const rect = res[i];
|
|
|
|
|
+ if (rect) {
|
|
|
|
|
+ // 计算单元在页面中的实际位置(考虑滚动)
|
|
|
|
|
+ const unitTop = rect.top + scrollTop;
|
|
|
|
|
+ if (scrollTop + offset >= unitTop) {
|
|
|
|
|
+ if (i == res.length - 1 || scrollTop + offset < (res[i + 1].top + scrollTop)) {
|
|
|
|
|
+ // 每次滚动都设置吸顶标题显示状态
|
|
|
|
|
+ this.showStickyTitle = i >= 1
|
|
|
|
|
+
|
|
|
|
|
+ if (this.currentUnitName !== this.danyuanList[i].danyuanName) {
|
|
|
|
|
+ this.currentUnitName = this.danyuanList[i].danyuanName;
|
|
|
|
|
+
|
|
|
|
|
+ // 单元变化时立即保存
|
|
|
|
|
+ const saveData = {
|
|
|
|
|
+ scrollTop: this.scrollTop,
|
|
|
|
|
+ currentUnit: this.currentUnitName,
|
|
|
|
|
+ timestamp: Date.now()
|
|
|
|
|
+ }
|
|
|
|
|
+ uni.setStorageSync(this.pageCacheKey, saveData)
|
|
|
|
|
+ }
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+ },
|
|
|
|
|
|
|
|
- // 可选:显示提示
|
|
|
|
|
- uni.showToast({
|
|
|
|
|
- title: '回到顶部',
|
|
|
|
|
- icon: 'none',
|
|
|
|
|
- duration: 800
|
|
|
|
|
- })
|
|
|
|
|
|
|
+ // 返回顶部
|
|
|
|
|
+ scrollToTop() {
|
|
|
|
|
+ // 清除缓存
|
|
|
|
|
+ uni.removeStorageSync(this.pageCacheKey)
|
|
|
|
|
+ this.hasRestoredScroll = false
|
|
|
|
|
+ this.showStickyTitle = false
|
|
|
|
|
+ // 回到顶部
|
|
|
|
|
+ this.scrollTop = this.scrollTop + 1; // 先改变值触发重新渲染
|
|
|
|
|
+ this.$nextTick(() => {
|
|
|
|
|
+ this.scrollTop = 0;
|
|
|
|
|
+ this.currentUnitName = this.danyuanList[0].danyuanName;
|
|
|
|
|
+ });
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
</script>
|
|
</script>
|
|
@@ -245,204 +259,154 @@
|
|
|
<style scoped>
|
|
<style scoped>
|
|
|
.container {
|
|
.container {
|
|
|
height: 100vh;
|
|
height: 100vh;
|
|
|
- background-color: #f8f9fa;
|
|
|
|
|
|
|
+ background-color: #f5f5f5;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- /* 学科标签页 */
|
|
|
|
|
- .subject-tabs {
|
|
|
|
|
- background-color: white;
|
|
|
|
|
- padding: 20rpx 0;
|
|
|
|
|
- border-bottom: 1rpx solid #eee;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- .tabs-scroll {
|
|
|
|
|
- white-space: nowrap;
|
|
|
|
|
- height: 80rpx;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- .tabs-content {
|
|
|
|
|
- display: inline-flex;
|
|
|
|
|
- padding: 0 30rpx;
|
|
|
|
|
|
|
+ /* 吸顶标题 */
|
|
|
|
|
+ .sticky-title {
|
|
|
|
|
+ position: fixed;
|
|
|
|
|
+ top: 0;
|
|
|
|
|
+ left: 0;
|
|
|
|
|
+ right: 0;
|
|
|
|
|
+ z-index: 100;
|
|
|
|
|
+ background-color: #1890ff;
|
|
|
|
|
+ color: white;
|
|
|
|
|
+ padding: 12px 16px;
|
|
|
|
|
+ font-size: 16px;
|
|
|
|
|
+ font-weight: bold;
|
|
|
|
|
+ text-align: center;
|
|
|
|
|
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- .tab-item {
|
|
|
|
|
- padding: 0 40rpx;
|
|
|
|
|
- height: 60rpx;
|
|
|
|
|
|
|
+ /* 返回顶部按钮 */
|
|
|
|
|
+ .back-top {
|
|
|
|
|
+ position: fixed;
|
|
|
|
|
+ right: 16px;
|
|
|
|
|
+ bottom: 100px;
|
|
|
|
|
+ z-index: 99;
|
|
|
|
|
+ width: 44px;
|
|
|
|
|
+ height: 44px;
|
|
|
|
|
+ background-color: #1890ff;
|
|
|
|
|
+ border-radius: 50%;
|
|
|
display: flex;
|
|
display: flex;
|
|
|
align-items: center;
|
|
align-items: center;
|
|
|
justify-content: center;
|
|
justify-content: center;
|
|
|
|
|
+ color: white;
|
|
|
|
|
+ font-size: 20px;
|
|
|
|
|
+ box-shadow: 0 2px 8px rgba(24, 144, 255, 0.4);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- .tab-text {
|
|
|
|
|
- font-size: 32rpx;
|
|
|
|
|
- color: #666;
|
|
|
|
|
- position: relative;
|
|
|
|
|
- padding: 10rpx 0;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- .tab-item.active .tab-text {
|
|
|
|
|
- color: #4a6fe3;
|
|
|
|
|
- font-weight: bold;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- .tab-item.active .tab-text::after {
|
|
|
|
|
- content: '';
|
|
|
|
|
- position: absolute;
|
|
|
|
|
- bottom: 0;
|
|
|
|
|
- left: 0;
|
|
|
|
|
- right: 0;
|
|
|
|
|
- height: 4rpx;
|
|
|
|
|
- background-color: #4a6fe3;
|
|
|
|
|
- border-radius: 2rpx;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /* 学科信息 */
|
|
|
|
|
- .subject-info {
|
|
|
|
|
- padding: 30rpx;
|
|
|
|
|
- background-color: white;
|
|
|
|
|
- margin-bottom: 20rpx;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- .course-name {
|
|
|
|
|
- display: block;
|
|
|
|
|
- font-size: 36rpx;
|
|
|
|
|
- font-weight: bold;
|
|
|
|
|
- color: #333;
|
|
|
|
|
- margin-bottom: 10rpx;
|
|
|
|
|
|
|
+ .back-top:active {
|
|
|
|
|
+ background-color: #096dd9;
|
|
|
|
|
+ transform: scale(0.95);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- .current-info {
|
|
|
|
|
- display: block;
|
|
|
|
|
- font-size: 28rpx;
|
|
|
|
|
- color: #666;
|
|
|
|
|
|
|
+ /* 滚动区域 */
|
|
|
|
|
+ .scroll-view {
|
|
|
|
|
+ height: 100%;
|
|
|
|
|
+ padding-top: 50px;
|
|
|
|
|
+ /* 给吸顶标题留出空间 */
|
|
|
|
|
+ box-sizing: border-box;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/* 单元样式 */
|
|
/* 单元样式 */
|
|
|
- .danyuan-item {
|
|
|
|
|
- background-color: white;
|
|
|
|
|
- margin: 20rpx;
|
|
|
|
|
- border-radius: 16rpx;
|
|
|
|
|
|
|
+ .unit-item {
|
|
|
|
|
+ margin: 12px;
|
|
|
|
|
+ background: white;
|
|
|
|
|
+ border-radius: 8px;
|
|
|
overflow: hidden;
|
|
overflow: hidden;
|
|
|
- box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.05);
|
|
|
|
|
- margin-bottom: 30rpx;
|
|
|
|
|
|
|
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- .danyuan-header {
|
|
|
|
|
- background: #4a6fe3;
|
|
|
|
|
- padding: 30rpx;
|
|
|
|
|
|
|
+ .unit-title {
|
|
|
|
|
+ padding: 16px;
|
|
|
|
|
+ background: linear-gradient(135deg, #1890ff, #36cfc9);
|
|
|
color: white;
|
|
color: white;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- .danyuan-title {
|
|
|
|
|
- display: block;
|
|
|
|
|
- font-size: 32rpx;
|
|
|
|
|
|
|
+ .unit-name {
|
|
|
|
|
+ font-size: 18px;
|
|
|
font-weight: bold;
|
|
font-weight: bold;
|
|
|
- margin-bottom: 10rpx;
|
|
|
|
|
|
|
+ display: block;
|
|
|
|
|
+ margin-bottom: 4px;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- .danyuan-intro {
|
|
|
|
|
- font-size: 28rpx;
|
|
|
|
|
|
|
+ .unit-intro {
|
|
|
|
|
+ font-size: 14px;
|
|
|
opacity: 0.9;
|
|
opacity: 0.9;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/* 节样式 */
|
|
/* 节样式 */
|
|
|
- .jie-item {
|
|
|
|
|
- padding: 24rpx 30rpx;
|
|
|
|
|
- border-bottom: 1rpx solid #f0f0f0;
|
|
|
|
|
|
|
+ .section-item {
|
|
|
|
|
+ padding: 12px 16px;
|
|
|
|
|
+ border-bottom: 1px solid #f0f0f0;
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ justify-content: space-between;
|
|
|
|
|
+ align-items: center;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- .jie-item:last-child {
|
|
|
|
|
|
|
+ .section-item:last-child {
|
|
|
border-bottom: none;
|
|
border-bottom: none;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- .jie-name {
|
|
|
|
|
- display: block;
|
|
|
|
|
- font-size: 30rpx;
|
|
|
|
|
- font-weight: 500;
|
|
|
|
|
- color: #333;
|
|
|
|
|
- margin-bottom: 8rpx;
|
|
|
|
|
|
|
+ .section-left {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ flex: 1;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- .jie-desc {
|
|
|
|
|
- display: block;
|
|
|
|
|
- font-size: 26rpx;
|
|
|
|
|
- color: #666;
|
|
|
|
|
|
|
+ .section-cover {
|
|
|
|
|
+ width: 60px;
|
|
|
|
|
+ height: 60px;
|
|
|
|
|
+ border-radius: 6px;
|
|
|
|
|
+ margin-right: 12px;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- /* 空课程提示 */
|
|
|
|
|
- .empty-jie {
|
|
|
|
|
- padding: 40rpx;
|
|
|
|
|
- text-align: center;
|
|
|
|
|
|
|
+ .section-info {
|
|
|
|
|
+ flex: 1;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- .empty-text {
|
|
|
|
|
- font-size: 28rpx;
|
|
|
|
|
- color: #999;
|
|
|
|
|
|
|
+ .section-name {
|
|
|
|
|
+ font-size: 16px;
|
|
|
|
|
+ color: #333;
|
|
|
|
|
+ display: block;
|
|
|
|
|
+ margin-bottom: 4px;
|
|
|
|
|
+ font-weight: 500;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- /* 新增:回到顶部按钮样式 */
|
|
|
|
|
- .back-to-top {
|
|
|
|
|
- position: fixed;
|
|
|
|
|
- bottom: 120rpx;
|
|
|
|
|
- right: 30rpx;
|
|
|
|
|
- width: 100rpx;
|
|
|
|
|
- height: 100rpx;
|
|
|
|
|
- background-color: rgba(74, 111, 227, 0.9);
|
|
|
|
|
- border-radius: 50%;
|
|
|
|
|
- display: flex;
|
|
|
|
|
- flex-direction: column;
|
|
|
|
|
- align-items: center;
|
|
|
|
|
- justify-content: center;
|
|
|
|
|
- z-index: 999;
|
|
|
|
|
- box-shadow: 0 4rpx 20rpx rgba(74, 111, 227, 0.3);
|
|
|
|
|
- transition: all 0.3s ease;
|
|
|
|
|
|
|
+ .section-desc {
|
|
|
|
|
+ font-size: 13px;
|
|
|
|
|
+ color: #666;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- .back-to-top:active {
|
|
|
|
|
- transform: scale(0.95);
|
|
|
|
|
- background-color: rgba(74, 111, 227, 1);
|
|
|
|
|
|
|
+ .section-right {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ flex-direction: column;
|
|
|
|
|
+ align-items: flex-end;
|
|
|
|
|
+ margin-left: 12px;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- .back-to-top-icon {
|
|
|
|
|
- font-size: 40rpx;
|
|
|
|
|
- color: white;
|
|
|
|
|
- font-weight: bold;
|
|
|
|
|
|
|
+ .section-number {
|
|
|
|
|
+ font-size: 12px;
|
|
|
|
|
+ color: #999;
|
|
|
|
|
+ margin-bottom: 4px;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- .back-to-top-text {
|
|
|
|
|
- font-size: 22rpx;
|
|
|
|
|
- color: white;
|
|
|
|
|
- margin-top: 4rpx;
|
|
|
|
|
|
|
+ .status {
|
|
|
|
|
+ font-size: 12px;
|
|
|
|
|
+ padding: 2px 8px;
|
|
|
|
|
+ border-radius: 10px;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- /* 当前单元提示 */
|
|
|
|
|
- .current-unit-tip {
|
|
|
|
|
- position: fixed;
|
|
|
|
|
- top: 200rpx;
|
|
|
|
|
- left: 50%;
|
|
|
|
|
- transform: translateX(-50%);
|
|
|
|
|
- background-color: rgba(74, 111, 227, 0.9);
|
|
|
|
|
- color: white;
|
|
|
|
|
- padding: 20rpx 40rpx;
|
|
|
|
|
- border-radius: 50rpx;
|
|
|
|
|
- font-size: 28rpx;
|
|
|
|
|
- z-index: 1000;
|
|
|
|
|
- box-shadow: 0 4rpx 20rpx rgba(74, 111, 227, 0.3);
|
|
|
|
|
- animation: fadeInOut 3s ease-in-out;
|
|
|
|
|
|
|
+ .status.completed {
|
|
|
|
|
+ background-color: #f6ffed;
|
|
|
|
|
+ color: #52c41a;
|
|
|
|
|
+ border: 1px solid #b7eb8f;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- @keyframes fadeInOut {
|
|
|
|
|
-
|
|
|
|
|
- 0%,
|
|
|
|
|
- 100% {
|
|
|
|
|
- opacity: 0;
|
|
|
|
|
- transform: translateX(-50%) translateY(-20rpx);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- 10%,
|
|
|
|
|
- 90% {
|
|
|
|
|
- opacity: 1;
|
|
|
|
|
- transform: translateX(-50%) translateY(0);
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ .status.uncompleted {
|
|
|
|
|
+ background-color: #fff7e6;
|
|
|
|
|
+ color: #fa8c16;
|
|
|
|
|
+ border: 1px solid #ffd591;
|
|
|
}
|
|
}
|
|
|
</style>
|
|
</style>
|