wangguoyu 5 hours ago
parent
commit
26df555367

+ 0 - 427
pages/chanpinneirong/index3.vue

@@ -1,427 +0,0 @@
-<template>
-	<view class="ezy-xuexi-page3">
-		<view class="icon-title-navBar-box">
-			<text class="nav-bar-title">学习</text>
-		</view>
-		<!-- 吸顶单元标题 -->
-		<view
-			v-if="currentStickyTitle"
-			class="item-dy-box item-fixed"
-		>
-			<view class="dy-left-box">L{{ currentStickyDengjiId }}</view>
-			<view class="dy-right-box">
-				<view class="right-content">
-					<view class="dy-name">{{ currentStickyTitle }}</view>
-					<view>{{ currentStickyIntro }}</view>
-				</view>
-			</view>
-		</view>
-
-		<!-- 滚动区域 -->
-		<scroll-view
-			v-if="existData"
-			class="ezy-page-body xuexi-page-body"
-			scroll-y
-			:scroll-top="scrollTop"
-			@scroll="handleScroll"
-		>
-			<view class="xxjl-card-box-padding">
-				<view class="xxjl-card-box">
-					<!-- 显示内容 -->
-					<view class="card-body-box">
-						<img :src="banbenInfo.cover" />
-						<view class="body-right">
-							<view class="right-name">{{ banbenInfo.chanpinName }}</view>
-							<view>等级:{{banbenInfo.dengjiName}}</view>
-							<view>版本:{{banbenInfo.name}}</view>
-							<view>单元:{{banbenInfo.curDanyuanName}}</view>
-							<view>课程:{{banbenInfo.curKechengName}}</view>
-						</view>
-					</view>
-					<view class="card-progress-box">
-						<view class="xx-progress-box">
-							<view>学习进度</view>
-							<progress :percent="curProcess" class="xx-progress" stroke-width="20"
-								backgroundColor="#3c7dfd" activeColor="#ffd11c" />
-						</view>
-						<ezyActiveVue class="ezy-btn-active jxxx-btn" @aclick="handlePlay(banbenInfo,'jixu')"></ezyActiveVue>
-					</view>
-				</view>
-			</view>
-
-			<view class="xx-item-list">
-				<!-- <view class="xx-item-title">— 以下为当前等级课程目录 —</view> -->
-
-				<view v-for="(danyuanItem, index) in danyuanList" :key="danyuanItem.danyuanId">
-
-					<!-- 单元标题(带唯一ID,用于位置查询) -->
-					<view class="xx-item-title" :id="`title-${danyuanItem.danyuanId}`">
-						— {{ danyuanItem.danyuanName }} {{ danyuanItem.danyuanIntro }} —
-					</view>
-
-					<!-- 节列表 -->
-					<ezyActiveVue
-						class="ezy-list-item-active xx-item-box"
-						v-for="jieItem in danyuanItem.jieList"
-						:key="jieItem.jieId"
-						@aclick="handlePlay(jieItem,'play')"
-					>
-						<view
-							class="xx-item-status"
-							:class="jieItem.wanchengFlag == 1 ? 'completed-status' : 'uncompleted-status'"
-						></view>
-						<img :src="jieItem.cover" />
-						<view class="xx-text-box">
-							<view>{{ jieItem.jieName }}</view>
-							<view>{{ jieItem.jieIntro }}</view>
-						</view>
-						<view class="xx-item-btn"></view>
-					</ezyActiveVue>
-				</view>
-
-				<view class="xx-item-title">本级别最后一单元啦~</view>
-				<view class="xx-more-btn" @click="moreBtn"></view>
-			</view>
-		</scroll-view>
-		<!-- 回到顶部 -->
-		<view class="go-top-btn" @click="goTopBtn"></view>
-
-		<!-- 无数据占位 -->
-		<view v-if="!existData" class="ezy-page-body xuexi-page-body"></view>
-
-		<!-- 弹窗组件 -->
-		<danyuanInfoVue ref="dyRef" v-if="isShow" @close="isShow = false"></danyuanInfoVue>
-
-		<!-- 底部 tabBar -->
-		<custom-tab-bar :show="true" :current-index="currentTabIndex" />
-	</view>
-</template>
-
-<script>
-	import ezyActiveVue from "@/components/ezyActive/ezyActive.vue";
-	import CustomTabBar from '@/components/custom-tabbar/index.vue';
-	import cacheManager from "@/utils/cacheManager.js";
-	import {
-		shuxueChanpinBanbenInfo,
-		shuxueSave
-	} from "@/api/chanpinneirong.js"
-	import {
-		onLoad,
-		onShow,
-		onHide,
-		onUnload
-	} from "@dcloudio/uni-app"
-	import danyuanInfoVue from '@/pages/xinshuxue/components/danyuanInfo.vue';
-	import {
-		toast
-	} from '../../utils/common';
-  import {updateXuexiProcess} from "./useNeirongShuxue"
-	export default {
-		data() {
-			return {
-				canExitApp: false,
-
-				danyuanList: [],
-				banbenInfo: {},
-				banbenId: '',
-				danyuanId: '',
-				chanpinId: '',
-				dengjiId: '',
-				curProcess: '',
-				currentTabIndex: 1,
-				existData: true,
-				isShow: false,
-
-				stickyHeight: 0, // 吸顶栏高度(px)
-				currentStickyTitle: '', // 初始为空,不显示吸顶
-				currentStickyIntro: '',
-				currentStickyDengjiId: '',
-				titlePositions: [],
-				scrollTop: 0,
-			}
-		},
-		components: {
-			CustomTabBar,
-			danyuanInfoVue,
-			ezyActiveVue
-		},
-		onLoad(options) {
-			uni.hideTabBar()
-		},
-		onShow() {
-			this.currentTabIndex = 1
-			const cacheData = cacheManager.get('xuexi-shuxue');
-
-			if (cacheData) {
-				console.log('使用缓存数据');
-				this.updateFromCache();
-			} else {
-				console.log('重新请求数据');
-				const cacheDataAuth = cacheManager.get('auth');
-				this.initFromOptions(cacheDataAuth);
-			}
-		},
-		onHide() {
-			console.log('学习页面隐藏')
-		},
-		onUnload() {
-			// 页面卸载无需特殊处理
-		},
-		methods: {
-			goTopBtn() {
-				this.scrollTop = -1; // 先设一个无效值(确保变化)
-				this.$nextTick(() => {
-					this.scrollTop = 0; // 再滚到顶部
-				});
-			},
-			moreBtn() {
-				uni.switchTab({
-					url: '/pages/chanpinXuanze/index'
-				})
-			},
-
-			initFromOptions(options) {
-				console.log('options', options);
-				this.chanpinId = options.chanpinId;
-				this.danyuanId = options.danyuanId;
-				this.banbenId = options.banbenId;
-				this.dengjiId = options.dengjiId;
-				this.loadDataFromApi();
-			},
-			// 从缓存更新数据方法
-			updateFromCache() {
-				updateXuexiProcess()
-				const cacheData = cacheManager.get('xuexi-shuxue');
-				console.log('cacheData 从缓存更新数据方法', cacheData);
-				if (cacheData) {
-					this.banbenInfo = { ...cacheData };
-					this.curProcess = cacheData.curProcess;
-					this.danyuanList = [...(cacheData.danyuanList || [])];
-
-					this.$nextTick(() => {
-						this.updateTitlePositions();
-					});
-				}
-			},
-
-			loadDataFromApi() {
-				this.banbenInfo = {}
-				this.curProcess = ''
-				this.danyuanList = []
-				const req = {
-					banbenId: this.banbenId
-				}
-				shuxueChanpinBanbenInfo(req).then(res => {
-					if (res.code === 0) {
-						this.banbenInfo = res.data;
-						this.curProcess =res.data.curProcess * 100;
-						this.danyuanList = res.data.danyuanList || [];
-
-						if (!this.danyuanList.length) {
-							this.existData = false
-						}
-						// 保存到缓存(新增了参数保存)
-						const cacheData = {
-							...res.data,
-							banbenId: this.banbenId,
-							chanpinId: this.chanpinId,
-							danyuanId: this.danyuanId,
-							dengjiId: this.dengjiId
-						};
-						cacheManager.set('xuexi-shuxue', cacheData);
-
-						// 更新全局auth信息
-						cacheManager.updateObject('auth', {
-							chanpinId: res.data.chanpinId,
-							banbenId: this.banbenId,
-							danyuanId: res.data.curDanyuanId,
-							dengjiId: res.data.dengjiId
-						});
-						this.danyuanId = res.data.curDanyuanId
-						this.dengjiId = res.data.dengjiId
-						this.chanpinId = res.data.chanpinId
-						// 数据加载完成后初始化观察器
-						this.$nextTick(() => {
-							this.updateTitlePositions();
-						});
-					}
-				}).catch(res => {
-					cacheManager.remove("xuexi-shuxue");
-					toast("加载失败,请重试");
-				});
-			},
-
-			updateStickyHeight() {
-					const query = uni.createSelectorQuery().in(this);
-					query.select('.item-fixed').boundingClientRect(res => {
-						if (res) {
-							this.stickyHeight = res.height; // 单位 px
-						} else {
-							this.stickyHeight = 100; // 默认 fallback(约 200rpx = 100px)
-						}
-					}).exec();
-				},
-
-			// 获取所有标题在 scroll-view 中的绝对位置
-			updateTitlePositions() {
-				this.titlePositions = [];
-				this.danyuanTitleRefs = [];
-
-				if (!this.danyuanList.length) return;
-
-				const query = uni.createSelectorQuery().in(this);
-				this.danyuanList.forEach(item => {
-					query.select(`#title-${item.danyuanId}`).boundingClientRect();
-					this.danyuanTitleRefs.push(item);
-				});
-
-				query.exec((rects) => {
-					rects.forEach((rect, index) => {
-						if (rect) {
-							// 在 scroll-view 初始未滚动时,rect.top 就是内容中的绝对 top
-							this.titlePositions.push({
-								danyuanId: this.danyuanTitleRefs[index].danyuanId,
-								name: this.danyuanTitleRefs[index].danyuanName,
-								intro: this.danyuanTitleRefs[index].danyuanIntro,
-								top: rect.top
-							});
-						}
-					});
-					this.titlePositions.sort((a, b) => a.top - b.top);
-					// 👇 新增:更新吸顶栏高度
-								this.updateStickyHeight();
-				});
-			},
-
-			handleScroll(e) {
-				const scrollTop = e.detail.scrollTop;
-				const firstTitle = this.titlePositions[0];
-
-				if (!firstTitle) {
-					this.currentStickyTitle = '';
-					return;
-				}
-
-				// 👇 关键修改:提前触发吸顶
-				const triggerOffset = scrollTop + this.stickyHeightPx;
-
-				// 如果还没滚到第一个标题的顶部(考虑吸顶高度),则隐藏
-				if (triggerOffset < firstTitle.top) {
-					this.currentStickyTitle = '';
-					return;
-				}
-
-				// 找出最后一个 top <= triggerOffset 的标题(即刚进入吸顶区的单元)
-				let matched = null;
-				for (let i = this.titlePositions.length - 1; i >= 0; i--) {
-					if (this.titlePositions[i].top <= triggerOffset) {
-						matched = this.titlePositions[i];
-						break;
-					}
-				}
-
-				if (matched) {
-					this.currentStickyTitle = matched.name;
-					this.currentStickyIntro = matched.intro;
-					this.currentStickyDengjiId = this.banbenInfo.dengjiId || 1;
-				} else {
-					this.currentStickyTitle = '';
-				}
-			},
-
-			getJieAndDanyuan(data, jieId) {
-				for (let danyuan of data.danyuanList) {
-					for (let jie of danyuan.jieList) {
-						if (jie.jieId == jieId) {
-							return { danyuan, jie }
-						}
-					}
-				}
-				return null;
-			},
-
-			async saveAndNavigate(jieId, type, da, code) {
-				if (code == 'jixu') {
-					if (!this.banbenId || !this.danyuanId) {
-						toast("banbenId或者danyuanId 丢失")
-						return false
-					}
-				}
-
-				let req = {
-					"banbenId": this.banbenId,
-					"danyuanId": da.danyuanId,
-					"jieId": jieId
-				}
-
-				try {
-					const res = await shuxueSave(req);
-					if (res.code == 0 && res.data) {
-						let curJieAndDanyuan = this.getJieAndDanyuan(this.banbenInfo, jieId);
-						if (!curJieAndDanyuan) {
-							toast("未找到课程信息");
-							return false;
-						}
-
-						const cacheData = cacheManager.get('xuexi-shuxue') || {};
-						cacheData.curDanyuanName = curJieAndDanyuan.danyuan.danyuanName;
-						cacheData.curKechengName = curJieAndDanyuan.jie.jieIntro;
-						cacheData.curJieId = jieId;
-						cacheData.type = curJieAndDanyuan.jie.type;
-						cacheManager.set('xuexi-shuxue', cacheData);
-
-						if (type == 1) {
-							uni.navigateTo({ url: `/pages/xinshuxue/lookShipin?jieId=${jieId}` })
-						} else {
-							uni.navigateTo({ url: `/pages/xinshuxue/unitTest?jieId=${jieId}` })
-						}
-					} else {
-						toast("保存位置出错");
-						return false;
-					}
-				} catch (error) {
-					toast("保存失败");
-					return false;
-				}
-			},
-			handlePlay(da, code) {
-				let jieId = code === 'jixu' ? da.curJieId : da.jieId;
-				if (!jieId) {
-					toast("无课程ID");
-					return;
-				}
-				this.saveAndNavigate(jieId, da.type, da, code);
-			},
-			handleClickDanyuan(item) {
-				if (!item.danyuanId) {
-					toast("danyuanId丢失")
-					return false
-				}
-				this.isShow = true;
-				setTimeout(() => {
-					// 更新为点击的动态单元Id [临时]
-					this.$refs.dyRef.handleShow(item.danyuanId)
-				}, 100)
-			},
-			handleBack() {
-				uni.navigateTo({
-					url: `/pages/chanpinXuanze/banben?dengjiId=` + this.dengjiId
-				})
-			},
-		},
-
-		// 计算吸顶栏下方的偏移(确保内容不被遮挡)
-		computed: {
-			stickyHeightPx() {
-					// 175rpx ≈ 87.5px,取整 90px 足够安全
-					return 90;
-				},
-			topOffset() {
-					// 只有当吸顶栏显示时,才增加上边距避免内容被遮挡
-					return this.currentStickyTitle ? '80rpx' : '0rpx';
-				}
-
-		}
-	}
-</script>
-

+ 0 - 427
pages/chanpinneirong/index4.vue

@@ -1,427 +0,0 @@
-<template>
-	<view class="ezy-xuexi-page3">
-		<view class="icon-title-navBar-box">
-			<text class="nav-bar-title">学习</text>
-		</view>
-		<!-- 吸顶单元标题 -->
-		<view
-			class="item-dy-box item-fixed"
-		>
-			<view class="dy-left-box">L{{ currentStickyDengjiId }}</view>
-			<view class="dy-right-box">
-				<view class="right-content" v-if="currentStickyTitle">
-					<view class="dy-name">{{ currentStickyTitle }}</view>
-					<view>{{ currentStickyIntro }}</view>
-				</view>
-				<view class="right-default" v-else><view>课程目录</view></view>
-			</view>
-		</view>
-
-		<!-- 滚动区域 -->
-		<scroll-view
-			v-if="existData"
-			class="ezy-page-body xuexi-page-body"
-			scroll-y
-			:scroll-top="scrollTop"
-			@scroll="handleScroll"
-		>
-			<view class="xxjl-card-box-padding">
-				<view class="xxjl-card-box">
-					<!-- 显示内容 -->
-					<view class="card-body-box">
-						<img :src="banbenInfo.cover" />
-						<view class="body-right">
-							<view class="right-name">{{ banbenInfo.chanpinName }}</view>
-							<view>等级:{{banbenInfo.dengjiName}}</view>
-							<view>版本:{{banbenInfo.name}}</view>
-							<view>单元:{{banbenInfo.curDanyuanName}}</view>
-							<view>课程:{{banbenInfo.curKechengName}}</view>
-						</view>
-					</view>
-					<view class="card-progress-box">
-						<view class="xx-progress-box">
-							<view>学习进度</view>
-							<progress :percent="curProcess" class="xx-progress" stroke-width="20"
-								backgroundColor="#3c7dfd" activeColor="#ffd11c" />
-						</view>
-						<ezyActiveVue class="ezy-btn-active jxxx-btn" @aclick="handlePlay(banbenInfo,'jixu')"></ezyActiveVue>
-					</view>
-				</view>
-			</view>
-
-			<view class="xx-item-list">
-				<!-- <view class="xx-item-title">— 以下为当前等级课程目录 —</view> -->
-
-				<view v-for="(danyuanItem, index) in danyuanList" :key="danyuanItem.danyuanId">
-
-					<!-- 单元标题(带唯一ID,用于位置查询) -->
-					<view class="xx-item-title" :id="`title-${danyuanItem.danyuanId}`">
-						— {{ danyuanItem.danyuanName }} {{ danyuanItem.danyuanIntro }} —
-					</view>
-
-					<!-- 节列表 -->
-					<ezyActiveVue
-						class="ezy-list-item-active xx-item-box"
-						v-for="jieItem in danyuanItem.jieList"
-						:key="jieItem.jieId"
-						@aclick="handlePlay(jieItem,'play')"
-					>
-						<view
-							class="xx-item-status"
-							:class="jieItem.wanchengFlag == 1 ? 'completed-status' : 'uncompleted-status'"
-						></view>
-						<img :src="jieItem.cover" />
-						<view class="xx-text-box">
-							<view>{{ jieItem.jieName }}</view>
-							<view>{{ jieItem.jieIntro }}</view>
-						</view>
-						<view class="xx-item-btn"></view>
-					</ezyActiveVue>
-				</view>
-
-				<view class="xx-item-title">本级别最后一单元啦~</view>
-				<view class="xx-more-btn" @click="moreBtn"></view>
-			</view>
-		</scroll-view>
-		<!-- 回到顶部 -->
-		<view class="go-top-btn" @click="goTopBtn"></view>
-
-		<!-- 无数据占位 -->
-		<view v-if="!existData" class="ezy-page-body xuexi-page-body"></view>
-
-		<!-- 弹窗组件 -->
-		<danyuanInfoVue ref="dyRef" v-if="isShow" @close="isShow = false"></danyuanInfoVue>
-
-		<!-- 底部 tabBar -->
-		<custom-tab-bar :show="true" :current-index="currentTabIndex" />
-	</view>
-</template>
-
-<script>
-	import ezyActiveVue from "@/components/ezyActive/ezyActive.vue";
-	import CustomTabBar from '@/components/custom-tabbar/index.vue';
-	import cacheManager from "@/utils/cacheManager.js";
-	import {
-		shuxueChanpinBanbenInfo,
-		shuxueSave
-	} from "@/api/chanpinneirong.js"
-	import {
-		onLoad,
-		onShow,
-		onHide,
-		onUnload
-	} from "@dcloudio/uni-app"
-	import danyuanInfoVue from '@/pages/xinshuxue/components/danyuanInfo.vue';
-	import {
-		toast
-	} from '../../utils/common';
-  import {updateXuexiProcess} from "./useNeirongShuxue"
-	export default {
-		data() {
-			return {
-				canExitApp: false,
-
-				danyuanList: [],
-				banbenInfo: {},
-				banbenId: '',
-				danyuanId: '',
-				chanpinId: '',
-				dengjiId: '',
-				curProcess: '',
-				currentTabIndex: 1,
-				existData: true,
-				isShow: false,
-
-				stickyHeight: 0, // 吸顶栏高度(px)
-				currentStickyTitle: '', // 初始为空,不显示吸顶
-				currentStickyIntro: '',
-				currentStickyDengjiId: '',
-				titlePositions: [],
-				scrollTop: 0,
-			}
-		},
-		components: {
-			CustomTabBar,
-			danyuanInfoVue,
-			ezyActiveVue
-		},
-		onLoad(options) {
-			uni.hideTabBar()
-		},
-		onShow() {
-			this.currentTabIndex = 1
-			const cacheData = cacheManager.get('xuexi-shuxue');
-
-			if (cacheData) {
-				console.log('使用缓存数据');
-				this.updateFromCache();
-			} else {
-				console.log('重新请求数据');
-				const cacheDataAuth = cacheManager.get('auth');
-				this.initFromOptions(cacheDataAuth);
-			}
-		},
-		onHide() {
-			console.log('学习页面隐藏')
-		},
-		onUnload() {
-			// 页面卸载无需特殊处理
-		},
-		methods: {
-			goTopBtn() {
-				this.scrollTop = -1; // 先设一个无效值(确保变化)
-				this.$nextTick(() => {
-					this.scrollTop = 0; // 再滚到顶部
-				});
-			},
-			moreBtn() {
-				uni.switchTab({
-					url: '/pages/chanpinXuanze/index'
-				})
-			},
-
-			initFromOptions(options) {
-				console.log('options', options);
-				this.chanpinId = options.chanpinId;
-				this.danyuanId = options.danyuanId;
-				this.banbenId = options.banbenId;
-				this.dengjiId = options.dengjiId;
-				this.loadDataFromApi();
-			},
-			// 从缓存更新数据方法
-			updateFromCache() {
-				updateXuexiProcess()
-				const cacheData = cacheManager.get('xuexi-shuxue');
-				console.log('cacheData 从缓存更新数据方法', cacheData);
-				if (cacheData) {
-					this.banbenInfo = { ...cacheData };
-					this.curProcess = cacheData.curProcess;
-					this.danyuanList = [...(cacheData.danyuanList || [])];
-
-					this.$nextTick(() => {
-						this.updateTitlePositions();
-					});
-				}
-			},
-
-			loadDataFromApi() {
-				this.banbenInfo = {}
-				this.curProcess = ''
-				this.danyuanList = []
-				const req = {
-					banbenId: this.banbenId
-				}
-				shuxueChanpinBanbenInfo(req).then(res => {
-					if (res.code === 0) {
-						this.banbenInfo = res.data;
-						this.curProcess =res.data.curProcess * 100;
-						this.danyuanList = res.data.danyuanList || [];
-
-						if (!this.danyuanList.length) {
-							this.existData = false
-						}
-						// 保存到缓存(新增了参数保存)
-						const cacheData = {
-							...res.data,
-							banbenId: this.banbenId,
-							chanpinId: this.chanpinId,
-							danyuanId: this.danyuanId,
-							dengjiId: this.dengjiId
-						};
-						cacheManager.set('xuexi-shuxue', cacheData);
-
-						// 更新全局auth信息
-						cacheManager.updateObject('auth', {
-							chanpinId: res.data.chanpinId,
-							banbenId: this.banbenId,
-							danyuanId: res.data.curDanyuanId,
-							dengjiId: res.data.dengjiId
-						});
-						this.danyuanId = res.data.curDanyuanId
-						this.dengjiId = res.data.dengjiId
-						this.chanpinId = res.data.chanpinId
-						// 数据加载完成后初始化观察器
-						this.$nextTick(() => {
-							this.updateTitlePositions();
-						});
-					}
-				}).catch(res => {
-					cacheManager.remove("xuexi-shuxue");
-					toast("加载失败,请重试");
-				});
-			},
-
-			updateStickyHeight() {
-					const query = uni.createSelectorQuery().in(this);
-					query.select('.item-fixed').boundingClientRect(res => {
-						if (res) {
-							this.stickyHeight = res.height; // 单位 px
-						} else {
-							this.stickyHeight = 100; // 默认 fallback(约 200rpx = 100px)
-						}
-					}).exec();
-				},
-
-			// 获取所有标题在 scroll-view 中的绝对位置
-			updateTitlePositions() {
-				this.titlePositions = [];
-				this.danyuanTitleRefs = [];
-
-				if (!this.danyuanList.length) return;
-
-				const query = uni.createSelectorQuery().in(this);
-				this.danyuanList.forEach(item => {
-					query.select(`#title-${item.danyuanId}`).boundingClientRect();
-					this.danyuanTitleRefs.push(item);
-				});
-
-				query.exec((rects) => {
-					rects.forEach((rect, index) => {
-						if (rect) {
-							// 在 scroll-view 初始未滚动时,rect.top 就是内容中的绝对 top
-							this.titlePositions.push({
-								danyuanId: this.danyuanTitleRefs[index].danyuanId,
-								name: this.danyuanTitleRefs[index].danyuanName,
-								intro: this.danyuanTitleRefs[index].danyuanIntro,
-								top: rect.top
-							});
-						}
-					});
-					this.titlePositions.sort((a, b) => a.top - b.top);
-					// 👇 新增:更新吸顶栏高度
-								this.updateStickyHeight();
-				});
-			},
-
-			handleScroll(e) {
-				const scrollTop = e.detail.scrollTop;
-				const firstTitle = this.titlePositions[0];
-
-				if (!firstTitle) {
-					this.currentStickyTitle = '';
-					return;
-				}
-
-				// 👇 关键修改:提前触发吸顶
-				const triggerOffset = scrollTop + this.stickyHeightPx;
-
-				// 如果还没滚到第一个标题的顶部(考虑吸顶高度),则隐藏
-				if (triggerOffset < firstTitle.top) {
-					this.currentStickyTitle = '';
-					return;
-				}
-
-				// 找出最后一个 top <= triggerOffset 的标题(即刚进入吸顶区的单元)
-				let matched = null;
-				for (let i = this.titlePositions.length - 1; i >= 0; i--) {
-					if (this.titlePositions[i].top <= triggerOffset) {
-						matched = this.titlePositions[i];
-						break;
-					}
-				}
-
-				if (matched) {
-					this.currentStickyTitle = matched.name;
-					this.currentStickyIntro = matched.intro;
-					this.currentStickyDengjiId = this.banbenInfo.dengjiId || 1;
-				} else {
-					this.currentStickyTitle = '';
-				}
-			},
-
-			getJieAndDanyuan(data, jieId) {
-				for (let danyuan of data.danyuanList) {
-					for (let jie of danyuan.jieList) {
-						if (jie.jieId == jieId) {
-							return { danyuan, jie }
-						}
-					}
-				}
-				return null;
-			},
-
-			async saveAndNavigate(jieId, type, da, code) {
-				if (code == 'jixu') {
-					if (!this.banbenId || !this.danyuanId) {
-						toast("banbenId或者danyuanId 丢失")
-						return false
-					}
-				}
-
-				let req = {
-					"banbenId": this.banbenId,
-					"danyuanId": da.danyuanId,
-					"jieId": jieId
-				}
-
-				try {
-					const res = await shuxueSave(req);
-					if (res.code == 0 && res.data) {
-						let curJieAndDanyuan = this.getJieAndDanyuan(this.banbenInfo, jieId);
-						if (!curJieAndDanyuan) {
-							toast("未找到课程信息");
-							return false;
-						}
-
-						const cacheData = cacheManager.get('xuexi-shuxue') || {};
-						cacheData.curDanyuanName = curJieAndDanyuan.danyuan.danyuanName;
-						cacheData.curKechengName = curJieAndDanyuan.jie.jieIntro;
-						cacheData.curJieId = jieId;
-						cacheData.type = curJieAndDanyuan.jie.type;
-						cacheManager.set('xuexi-shuxue', cacheData);
-
-						if (type == 1) {
-							uni.navigateTo({ url: `/pages/xinshuxue/lookShipin?jieId=${jieId}` })
-						} else {
-							uni.navigateTo({ url: `/pages/xinshuxue/unitTest?jieId=${jieId}` })
-						}
-					} else {
-						toast("保存位置出错");
-						return false;
-					}
-				} catch (error) {
-					toast("保存失败");
-					return false;
-				}
-			},
-			handlePlay(da, code) {
-				let jieId = code === 'jixu' ? da.curJieId : da.jieId;
-				if (!jieId) {
-					toast("无课程ID");
-					return;
-				}
-				this.saveAndNavigate(jieId, da.type, da, code);
-			},
-			handleClickDanyuan(item) {
-				if (!item.danyuanId) {
-					toast("danyuanId丢失")
-					return false
-				}
-				this.isShow = true;
-				setTimeout(() => {
-					// 更新为点击的动态单元Id [临时]
-					this.$refs.dyRef.handleShow(item.danyuanId)
-				}, 100)
-			},
-			handleBack() {
-				uni.navigateTo({
-					url: `/pages/chanpinXuanze/banben?dengjiId=` + this.dengjiId
-				})
-			},
-		},
-
-		// 计算吸顶栏下方的偏移(确保内容不被遮挡)
-		computed: {
-			stickyHeightPx() {
-					// 175rpx ≈ 87.5px,取整 90px 足够安全
-					return 90;
-				},
-			topOffset() {
-					// 只有当吸顶栏显示时,才增加上边距避免内容被遮挡
-					return this.currentStickyTitle ? '80rpx' : '0rpx';
-				}
-
-		}
-	}
-</script>
-

+ 0 - 442
pages/chanpinneirong/index44带吸顶有问题.vue

@@ -1,442 +0,0 @@
-<template>
-	<view class="ezy-xuexi-page3">
-		<view class="icon-title-navBar-box">
-			<text class="nav-bar-title">学习</text>
-		</view>
-		<!-- 吸顶单元标题 -->
-	<!-- 	<ezyActiveVue
-			v-if="currentStickyTitle &&existData"
-			class="item-dy-box item-fixed"
-			@aclick="handleClickDanyuan(currentDanyuanId)"
-		>
-			<view class="ezy-list-item-active item-dy-body">
-				<view class="dy-left-box">L{{ currentStickyDengjiId }}</view>
-				<view class="dy-right-box">
-					<view class="right-content">
-						<view class="dy-name">{{ currentStickyTitle }}</view>
-						<view>{{ currentStickyIntro }}</view>
-					</view>
-				</view>
-			</view>
-		</ezyActiveVue> -->
-
-		<!-- 滚动区域 -->
-		<scroll-view
-			v-if="existData"
-			class="ezy-page-body xuexi-page-body"
-			scroll-y
-			:scroll-top="scrollTop"
-			@scroll="handleScroll"
-		>
-			<view class="xxjl-card-box-padding">
-				<view class="xxjl-card-box">
-					<!-- 显示内容 -->
-					<view class="card-body-box">
-						<img :src="banbenInfo.cover" />
-						<view class="body-right">
-							<view class="right-name">{{ banbenInfo.chanpinName }}</view>
-							<view>等级:{{banbenInfo.dengjiName}}</view>
-							<view>版本:{{banbenInfo.name}}</view>
-							<view>单元:{{banbenInfo.curDanyuanName}}</view>
-							<view>课程:{{banbenInfo.curKechengName}}</view>
-						</view>
-					</view>
-					<view class="card-progress-box">
-						<view class="xx-progress-box">
-							<view>学习进度</view>
-							<progress :percent="curProcess" class="xx-progress" stroke-width="20"
-								backgroundColor="#3c7dfd" activeColor="#ffd11c" />
-						</view>
-						<ezyActiveVue class="ezy-btn-active jxxx-btn" @aclick="handlePlay(banbenInfo,'jixu')"></ezyActiveVue>
-					</view>
-				</view>
-			</view>
-
-			<view class="xx-item-list">
-				<!-- <view class="xx-item-title">— 以下为当前等级课程目录 —</view> -->
-
-				<view v-for="(danyuanItem, index) in danyuanList" :key="danyuanItem.danyuanId">
-					<!-- 单元标题(带唯一ID,用于位置查询) -->
-					<view class="xx-item-title" @click="handleClickDanyuan(danyuanItem.danyuanId)" :id="`title-${danyuanItem.danyuanId}`">
-						— {{ danyuanItem.danyuanName }} {{ danyuanItem.danyuanIntro }} —
-					</view>
-					<!-- 节列表 -->
-					<ezyActiveVue
-						class="ezy-list-item-active xx-item-box"
-						v-for="jieItem in danyuanItem.jieList"
-						:key="jieItem.jieId"
-						@aclick="handlePlay(jieItem,'play')"
-					>
-						<view
-							class="xx-item-status"
-							:class="jieItem.wanchengFlag == 1 ? 'completed-status' : 'uncompleted-status'"
-						></view>
-						<img :src="jieItem.cover" />
-						<view class="xx-text-box">
-							<view>{{ jieItem.jieName }}</view>
-							<view>{{ jieItem.jieIntro }}</view>
-						</view>
-						<view class="xx-item-btn"></view>
-					</ezyActiveVue>
-				</view>
-
-				<view class="xx-item-title">本级别最后一单元啦~</view>
-				<view class="xx-more-btn" @click="moreBtn"></view>
-			</view>
-		</scroll-view>
-		<!-- 回到顶部 -->
-		<view v-if="currentStickyTitle&&existData" class="go-top-btn" @click="goTopBtn"></view>
-
-		<!-- 无数据占位 -->
-		<view v-if="!existData" class="ezy-page-body">
-			<view class="ezy-no-sj">
-				<icon></icon>
-				<text>暂无数据</text>
-			</view>
-		</view>
-
-		<!-- 弹窗组件 -->
-		<danyuanInfoVue ref="dyRef" v-if="isShow" @close="isShow = false"></danyuanInfoVue>
-
-		<!-- 底部 tabBar -->
-		<custom-tab-bar :show="true" :current-index="currentTabIndex" />
-	</view>
-</template>
-
-<script>
-	import ezyActiveVue from "@/components/ezyActive/ezyActive.vue";
-	import CustomTabBar from '@/components/custom-tabbar/index.vue';
-	import cacheManager from "@/utils/cacheManager.js";
-	import {
-		shuxueChanpinBanbenInfo,
-		shuxueSave
-	} from "@/api/chanpinneirong.js"
-	import {
-		onLoad,
-		onShow,
-		onHide,
-		onUnload
-	} from "@dcloudio/uni-app"
-	import danyuanInfoVue from '@/pages/xinshuxue/components/danyuanInfo.vue';
-	import {
-		toast
-	} from '../../utils/common';
-  import {updateXuexiProcess} from "./useNeirongShuxue"
-	export default {
-		data() {
-			return {
-				canExitApp: false,
-
-				danyuanList: [],
-				banbenInfo: {},
-				banbenId: '',
-				danyuanId: '',
-				chanpinId: '',
-				dengjiId: '',
-				curProcess: '',
-				currentTabIndex: 1,
-				existData: true,
-				isShow: false,
-
-				stickyHeight: 0, // 吸顶栏高度(px)
-				currentStickyTitle: '', // 初始为空,不显示吸顶
-				currentStickyIntro: '',
-				currentDanyuanId:'',
-				currentStickyDengjiId: '',
-				titlePositions: [],
-				scrollTop: 0,
-			}
-		},
-		components: {
-			CustomTabBar,
-			danyuanInfoVue,
-			ezyActiveVue
-		},
-		onLoad(options) {
-			uni.hideTabBar()
-		},
-		onShow() {
-			this.currentTabIndex = 1
-			const cacheData = cacheManager.get('xuexi-shuxue');
-
-			if (cacheData) {
-				console.log('使用缓存数据');
-				this.updateFromCache();
-			} else {
-				console.log('重新请求数据');
-				const cacheDataAuth = cacheManager.get('auth');
-				this.initFromOptions(cacheDataAuth);
-			}
-		},
-		onHide() {
-			console.log('学习页面隐藏')
-		},
-		onUnload() {
-			// 页面卸载无需特殊处理
-		},
-		methods: {
-			goTopBtn() {
-				this.scrollTop = -1; // 先设一个无效值(确保变化)
-				this.$nextTick(() => {
-					this.scrollTop = 0; // 再滚到顶部
-				});
-			},
-			moreBtn() {
-				uni.switchTab({
-					url: '/pages/chanpinXuanze/index'
-				})
-			},
-
-			initFromOptions(options) {
-				//console.log('options', options);
-				this.chanpinId = options.chanpinId;
-				this.danyuanId = options.danyuanId;
-				this.banbenId = options.banbenId;
-				this.dengjiId = options.dengjiId;
-				this.loadDataFromApi();
-			},
-			// 从缓存更新数据方法
-			updateFromCache() {
-				updateXuexiProcess()
-				const cacheData = cacheManager.get('xuexi-shuxue');
-				console.log('cacheData 从缓存更新数据方法', cacheData);
-				if (cacheData) {
-					this.banbenInfo = { ...cacheData };
-					this.curProcess = cacheData.curProcess;
-					this.danyuanList = [...(cacheData.danyuanList || [])];
-					this.banbenId = cacheData.banbenId
-					// this.$nextTick(() => {
-					// 	this.updateTitlePositions();
-					// });
-				}
-			},
-
-			loadDataFromApi() {
-				this.banbenInfo = {}
-				this.curProcess = ''
-				this.danyuanList = []
-				const req = {
-					banbenId: this.banbenId
-				}
-				shuxueChanpinBanbenInfo(req).then(res => {
-					if (res.code === 0) {
-						this.banbenInfo = res.data;
-						this.banbenInfo.danyuanId = res.data.curDanyuanId;
-						this.curProcess =res.data.curProcess * 100;
-						this.danyuanList = res.data.danyuanList || [];
-						if (this.danyuanList.length ==0) {
-							this.existData = false
-						}else{
-							this.existData = true
-						}
-						// 保存到缓存(新增了参数保存)
-						const cacheData = {
-							...res.data,
-							banbenId: this.banbenId,
-							chanpinId: this.chanpinId,
-							danyuanId: this.danyuanId,
-							dengjiId: this.dengjiId
-						};
-						cacheManager.set('xuexi-shuxue', cacheData);
-
-						// 更新全局auth信息
-						cacheManager.updateObject('auth', {
-							chanpinId: res.data.chanpinId,
-							banbenId: this.banbenId,
-							danyuanId: res.data.curDanyuanId,
-							dengjiId: res.data.dengjiId
-						});
-						this.danyuanId = res.data.curDanyuanId
-						this.dengjiId = res.data.dengjiId
-						this.chanpinId = res.data.chanpinId
-						// 数据加载完成后初始化观察器
-						// this.$nextTick(() => {
-						// 	this.updateTitlePositions();
-						// });
-					}
-				}).catch(res => {
-					cacheManager.remove("xuexi-shuxue");
-					toast("加载失败,请重试");
-				});
-			},
-
-			// updateStickyHeight() {
-			// 		const query = uni.createSelectorQuery().in(this);
-			// 		query.select('.item-fixed').boundingClientRect(res => {
-			// 			if (res) {
-			// 				this.stickyHeight = res.height; // 单位 px
-			// 			} else {
-			// 				this.stickyHeight = 100; // 默认 fallback(约 200rpx = 100px)
-			// 			}
-			// 		}).exec();
-			// 	},
-
-			// 获取所有标题在 scroll-view 中的绝对位置
-			// updateTitlePositions() {
-			// 	this.titlePositions = [];
-			// 	this.danyuanTitleRefs = [];
-
-			// 	if (!this.danyuanList.length) return;
-
-			// 	const query = uni.createSelectorQuery().in(this);
-			// 	this.danyuanList.forEach(item => {
-			// 		query.select(`#title-${item.danyuanId}`).boundingClientRect();
-			// 		this.danyuanTitleRefs.push(item);
-			// 	});
-
-			// 	query.exec((rects) => {
-			// 		rects.forEach((rect, index) => {
-			// 			if (rect) {
-			// 				// 在 scroll-view 初始未滚动时,rect.top 就是内容中的绝对 top
-			// 				this.titlePositions.push({
-			// 					danyuanId: this.danyuanTitleRefs[index].danyuanId,
-			// 					name: this.danyuanTitleRefs[index].danyuanName,
-			// 					intro: this.danyuanTitleRefs[index].danyuanIntro,
-			// 					top: rect.top
-			// 				});
-			// 			}
-			// 		});
-			// 		this.titlePositions.sort((a, b) => a.top - b.top);
-			// 		// 👇 新增:更新吸顶栏高度
-			// 					this.updateStickyHeight();
-			// 	});
-			// },
-
-			handleScroll(e) {
-				const scrollTop = e.detail.scrollTop;
-				// const firstTitle = this.titlePositions[0];
-
-				// if (!firstTitle) {
-				// 	this.currentStickyTitle = '';
-				// 	return;
-				// }
-
-				// // 👇 关键修改:提前触发吸顶
-				// const triggerOffset = scrollTop + this.stickyHeightPx;
-
-				// // 如果还没滚到第一个标题的顶部(考虑吸顶高度),则隐藏
-				// if (triggerOffset < firstTitle.top) {
-				// 	this.currentStickyTitle = '';
-				// 	return;
-				// }
-
-				// // 找出最后一个 top <= triggerOffset 的标题(即刚进入吸顶区的单元)
-				// let matched = null;
-				// for (let i = this.titlePositions.length - 1; i >= 0; i--) {
-				// 	if (this.titlePositions[i].top <= triggerOffset) {
-				// 		matched = this.titlePositions[i];
-				// 		break;
-				// 	}
-				// }
-				// console.log('matched',matched);
-				// if (matched) {
-				// 	this.currentStickyTitle = matched.name;
-				// 	this.currentStickyIntro = matched.intro;
-				// 	this.currentDanyuanId = matched.danyuanId;
-				// 	this.currentStickyDengjiId = this.banbenInfo.dengjiId || 1;
-				// } else {
-				// 	this.currentStickyTitle = '';
-				// }
-			},
-
-			getJieAndDanyuan(data, jieId) {
-				for (let danyuan of data.danyuanList) {
-					for (let jie of danyuan.jieList) {
-						if (jie.jieId == jieId) {
-							return { danyuan, jie }
-						}
-					}
-				}
-				return null;
-			},
-
-			async saveAndNavigate(jieId, type, da, code) {
-				
-				console.log('this.banbenId',this.banbenId);
-				console.log('this.danyuanIddanyuanId',da.danyuanId);
-				if (code == 'jixu') {
-					if (!this.banbenId || !da.danyuanId) {
-						toast("banbenId或者danyuanId 丢失")
-						return false
-					}
-				}
-
-				let req = {
-					"banbenId": this.banbenId,
-					"danyuanId": da.danyuanId,
-					"jieId": jieId
-				}
-				console.log('req',req);
-				try {
-					const res = await shuxueSave(req);
-					if (res.code == 0 && res.data) {
-						let curJieAndDanyuan = this.getJieAndDanyuan(this.banbenInfo, jieId);
-						if (!curJieAndDanyuan) {
-							toast("未找到课程信息");
-							return false;
-						}
-
-						const cacheData = cacheManager.get('xuexi-shuxue') || {};
-						cacheData.curDanyuanName = curJieAndDanyuan.danyuan.danyuanName;
-						cacheData.curKechengName = curJieAndDanyuan.jie.jieIntro;
-						cacheData.danyuanId = curJieAndDanyuan.jie.danyuanId;
-						cacheData.curJieId = jieId;
-						cacheData.type = curJieAndDanyuan.jie.type;
-						cacheManager.set('xuexi-shuxue', cacheData);
-
-						if (type == 1) {
-							uni.navigateTo({ url: `/pages/xinshuxue/lookShipin?jieId=${jieId}` })
-						} else {
-							uni.navigateTo({ url: `/pages/xinshuxue/unitTest?jieId=${jieId}` })
-						}
-					} else {
-						toast("保存位置出错");
-						return false;
-					}
-				} catch (error) {
-					toast("保存失败");
-					return false;
-				}
-			},
-			handlePlay(da, code) {
-				let jieId = code === 'jixu' ? da.curJieId : da.jieId;
-				if (!jieId) {
-					toast("无课程ID");
-					return;
-				}
-				this.saveAndNavigate(jieId, da.type, da, code);
-			},
-			handleClickDanyuan(danyuanId) {
-				
-				if (!danyuanId) {
-					toast("danyuanId丢失")
-					return false
-				}
-				this.isShow = true;
-				setTimeout(() => {
-					// 更新为点击的动态单元Id [临时]
-					this.$refs.dyRef.handleShow(danyuanId)
-				}, 100)
-			},
-			handleBack() {
-				uni.navigateTo({
-					url: `/pages/chanpinXuanze/banben?dengjiId=` + this.dengjiId
-				})
-			},
-		},
-
-		// 计算吸顶栏下方的偏移(确保内容不被遮挡)
-		computed: {
-			// stickyHeightPx() {
-			// 		// 175rpx ≈ 87.5px,取整 90px 足够安全
-			// 		return 90;
-			// 	},
-			// topOffset() {
-			// 		// 只有当吸顶栏显示时,才增加上边距避免内容被遮挡
-			// 		return this.currentStickyTitle ? '80rpx' : '0rpx';
-			// 	}
-
-		}
-	}
-</script>
-

+ 0 - 527
pages/chanpinneirong/tx版本无回到顶部.vue

@@ -1,527 +0,0 @@
-<template>
-	<view class="ezy-xuexi-page">
-		<view class="icon-title-navBar-box">
-			<text class="nav-bar-title">学习</text>
-		</view>
-		<view v-if="existData" class="ezy-page-body xuexi-page-body" ref="containerRef">
-			<view class="xxjl-card-box-padding">
-				<view class="xxjl-card-box">
-					<!-- 显示内容 -->
-					<view class="card-body-box">
-						<img :src="banbenInfo.cover" />
-						<view class="body-right">
-							<view class="right-name">{{ banbenInfo.chanpinName }}</view>
-							<view>等级:{{ banbenInfo.dengjiName }}</view>
-							<view>版本:{{ banbenInfo.name }}</view>
-							<view>单元:{{ banbenInfo.curDanyuanName }}</view>
-							<view>课程:{{ banbenInfo.curKechengName }}</view>
-						</view>
-					</view>
-					<view class="card-progress-box">
-						<view class="xx-progress-box">
-							<view>学习进度</view>
-							<progress :percent="curProcess" class="xx-progress" stroke-width="20"
-								backgroundColor="#3c7dfd" activeColor="#ffd11c" />
-						</view>
-						<ezyActiveVue class="ezy-btn-active jxxx-btn" @aclick="handlePlay(banbenInfo,'jixu')">
-						</ezyActiveVue>
-					</view>
-				</view>
-			</view>
-			<view class="xx-item-list">
-				<view class="xx-item-title cccAAA2">— 以下为当前等级课程目录 —</view>
-
-				<template v-if="unitPositions.length">
-					<ezyActiveVue class="ezy-list-item-active item-dy-box item-fixed cccAAAB"
-						@aclick="handleClickDanyuan(danyuanList[activeAAAIndex])"
-						:id="`unit-title-${danyuanList[activeAAAIndex].danyuanId}`">
-						<view class="dy-left-box">L{{ banbenInfo.dengjiId }}</view>
-						<view class="dy-right-box">
-							<view class="right-content">
-								<view class="dy-name">{{ danyuanList[activeAAAIndex].danyuanName }}</view>
-								<view>{{ danyuanList[activeAAAIndex].danyuanIntro }}</view>
-							</view>
-						</view>
-					</ezyActiveVue>
-				</template>
-
-
-				<view v-for="(danyuanItem,index) in danyuanList" :key="danyuanItem.danyuanId">
-					<!-- 第一单元 -->
-					<ezyActiveVue v-show="false" v-if="index ==0"
-						class="ezy-list-item-active item-dy-box item-fixed cccAAA"
-						@aclick="handleClickDanyuan(danyuanItem)" :id="`unit-title-${danyuanItem.danyuanId}`">
-						<view class="dy-left-box">L{{ banbenInfo.dengjiId }}</view>
-						<view class="dy-right-box">
-							<view class="right-content">
-								<view class="dy-name">{{ danyuanItem.danyuanName }}</view>
-								<view>{{ danyuanItem.danyuanIntro }}</view>
-							</view>
-						</view>
-					</ezyActiveVue>
-					<!-- 其他单元 -->
-					<view v-if="index !=0" class="xx-item-title cccAAA" :id="`unit-title-${danyuanItem.danyuanId}`">—
-						{{ danyuanItem.danyuanName }}
-						{{ danyuanItem.danyuanIntro }} —
-					</view>
-
-
-					<ezyActiveVue class="ezy-list-item-active xx-item-box" v-for="jieItem in danyuanItem.jieList"
-						:key="jieItem.jieId" @aclick="handlePlay(jieItem,'play')">
-						<view class="xx-item-status"
-							:class="jieItem.wanchengFlag == 1 ? 'completed-status' : 'uncompleted-status'">
-						</view>
-						<img :src="jieItem.cover" />
-						<view class="xx-text-box">
-							<view>{{ jieItem.jieName }}</view>
-							<view>{{ jieItem.jieIntro }}</view>
-						</view>
-						<view class="xx-item-btn"></view>
-					</ezyActiveVue>
-
-
-				</view>
-				<view class="xx-item-title">本级别最后一单元啦~</view>
-				<view class="xx-more-btn" @click="moreBtn"></view>
-			</view>
-
-			<!-- 回到顶部 -->
-			<view class="go-top-btn" v-if="showTop" @click="handleTop"></view>
-
-		</view>
-		<view v-if="!existData">
-
-		</view>
-
-		<danyuanInfoVue ref="dyRef" v-if="isShow" @close="isShow= false"></danyuanInfoVue>
-		<custom-tab-bar :show="true" :current-index="currentTabIndex" />
-
-	</view>
-</template>
-
-<script>
-	import ezyActiveVue from "@/components/ezyActive/ezyActive.vue";
-	import CustomTabBar from '@/components/custom-tabbar/index.vue';
-	import cacheManager from "@/utils/cacheManager.js";
-	import {
-		shuxueChanpinBanbenInfo,
-		shuxueSave
-	} from "@/api/chanpinneirong.js"
-	import {
-		onLoad,
-		onShow,
-		onHide,
-		onUnload
-	} from "@dcloudio/uni-app"
-	import danyuanInfoVue from '@/pages/xinshuxue/components/danyuanInfo.vue';
-	import {
-		toast
-	} from '../../utils/common';
-	import {
-		updateXuexiProcess
-	} from "./useNeirongShuxue"
-	import {
-		nextTick
-	} from "vue";
-
-	export default {
-		data() {
-			return {
-				//-------------- start ---------------/
-				// 单元位置信息
-				unitPositions: [],
-				// 节流定时器
-				throttleTimer: null,
-				activeAAAIndex: 0,
-				defaultD: 0,
-        showTop: false,
-				//-------------- end ---------------/
-
-
-				canExitApp: false,
-
-				danyuanList: [],
-				currentUnitName: "",
-				isShow: true,
-				banbenInfo: {},
-				banbenId: '',
-				danyuanId: '',
-				chanpinId: '',
-				dengjiId: '',
-				curProcess: '',
-				currentTabIndex: 1,
-				cacheManagerLocal: null,
-				hasCache: false, // 是否有缓存的标志
-				needRefresh: false, // 是否需要刷新数据的标志
-				existData: true,
-
-				currentVisibleUnit: '', // 当前可见的单元名称
-				observer: null, // IntersectionObserver实例
-				lastToastUnitId: null, // 上一次toast的单元ID,防止重复触发
-				isFixed: false,
-			}
-		},
-		components: {
-			CustomTabBar,
-			danyuanInfoVue,
-			ezyActiveVue
-		},
-		onShow() {
-			this.currentTabIndex = 1
-			const cacheData = cacheManager.get('xuexi-shuxue');
-
-			if (cacheData) {
-				console.log('使用缓存数据');
-				this.updateFromCache();
-			} else {
-				console.log('重新请求数据');
-				this.hasCache = false;
-				const cacheDataAuth = cacheManager.get('auth');
-				this.initFromOptions(cacheDataAuth);
-			}
-
-			// 数据加载完成后初始化观察器
-			this.$nextTick(() => {
-				setTimeout(() => {
-					this.initObserver();
-				}, 300);
-			});
-
-			// 页面显示时重新计算单元位置
-			this.$nextTick(() => {
-				setTimeout(() => {
-					this.doReady()
-				}, 100);
-			});
-		},
-		onHide() {
-			console.log('学习页面隐藏')
-			this.needRefresh = true;
-			// 销毁观察器
-			if (this.observer) {
-				this.observer.disconnect();
-				this.observer = null;
-			}
-		},
-		onUnload() {
-			// 页面卸载时销毁观察器
-			if (this.observer) {
-				this.observer.disconnect();
-				this.observer = null;
-			}
-		},
-		onLoad(options) {
-			uni.hideTabBar()
-		},
-		methods: {
-			moreBtn() {
-				uni.switchTab({
-					url: '/pages/chanpinXuanze/index'
-				})
-			},
-			// 初始化IntersectionObserver
-			initObserver() {
-				if (this.observer) {
-					this.observer.disconnect();
-					this.observer = null;
-				}
-
-				// 监听每个单元标题的位置
-				this.observer = uni.createIntersectionObserver(this).relativeToViewport({
-					top: 0, // 监听进入屏幕顶部的标题
-					bottom: 0
-				});
-
-				this.danyuanList.forEach((item, index) => {
-					const titleId = `unit-title-${item.danyuanId}`;
-					this.observer.observe(`#${titleId}`, (res) => {
-						if (res.intersectionRatio > 0) {
-							// 标题在屏幕顶部区域内
-							if (this.lastToastUnitId !== item.danyuanId) {
-								this.lastToastUnitId = item.danyuanId;
-								// console.log(`显示${item.danyuanName}`);
-
-								// console.log('item', item);
-								if (item.danyuanName != "第一单元") {
-									this.isFixed = true
-								} else {
-									this.isFixed = false
-								}
-								// uni.showToast({
-								//   title: `${item.danyuanName}`,
-								//   icon: 'none',
-								//   duration: 1500
-								// });
-							}
-						}
-					});
-				});
-
-				// 页面初始时显示第一个单元
-				if (this.danyuanList.length > 0) {
-					this.lastToastUnitId = this.danyuanList[0].danyuanId;
-				}
-			},
-
-			// 从参数初始化方法
-			initFromOptions(options) {
-				console.log('options', options);
-				this.chanpinId = options.chanpinId;
-				this.danyuanId = options.danyuanId;
-				this.banbenId = options.banbenId;
-				this.dengjiId = options.dengjiId;
-				this.loadDataFromApi();
-			},
-			// 从缓存更新数据方法
-			updateFromCache() {
-				updateXuexiProcess()
-				const cacheData = cacheManager.get('xuexi-shuxue');
-				console.log('cacheData 从缓存更新数据方法', cacheData);
-				if (cacheData) {
-					this.banbenInfo = {
-						...cacheData
-					};
-					this.curProcess = cacheData.curProcess;
-					this.danyuanList = [...(cacheData.danyuanList || [])];
-
-					// 数据更新后重新初始化观察器
-					this.$nextTick(() => {
-						setTimeout(() => {
-							this.initObserver();
-						}, 300);
-					});
-				}
-			},
-
-			shouldUseCache(options, cacheData) {
-				if (options.banbenId && cacheData.banbenId === options.banbenId) {
-					return true;
-				}
-				return false;
-			},
-			// 改成了
-			loadDataFromApi() {
-				this.banbenInfo = {}
-				this.curProcess = ''
-				this.danyuanList = []
-				const req = {
-					banbenId: this.banbenId
-				}
-				shuxueChanpinBanbenInfo(req).then(res => {
-					if (res.code === 0) {
-						this.banbenInfo = res.data;
-						this.curProcess = res.data.curProcess * 100;
-						this.danyuanList = res.data.danyuanList || [];
-
-						if (!this.danyuanList) {
-							this.existData = false
-						}
-						// 保存到缓存(新增了参数保存)
-						const cacheData = {
-							...res.data,
-							banbenId: this.banbenId,
-							chanpinId: this.chanpinId,
-							danyuanId: this.danyuanId,
-							dengjiId: this.dengjiId
-						};
-						cacheManager.set('xuexi-shuxue', cacheData);
-						this.hasCache = true;
-
-						// 更新全局auth信息
-						cacheManager.updateObject('auth', {
-							chanpinId: res.data.chanpinId,
-							banbenId: this.banbenId,
-							danyuanId: res.data.curDanyuanId,
-							dengjiId: res.data.dengjiId
-						});
-						this.danyuanId = res.data.curDanyuanId
-						this.dengjiId = res.data.dengjiId
-						this.chanpinId = res.data.chanpinId
-						// 数据加载完成后初始化观察器
-						this.$nextTick(() => {
-							setTimeout(() => {
-								this.initObserver();
-							}, 300);
-						});
-					}
-				}).catch(res => {
-					cacheManager.remove("xuexi-shuxue");
-					this.hasCache = false;
-					toast("加载失败,请重试");
-				});
-			},
-			getJieAndDanyuan(data, jieId) {
-				// 循环单元
-				for (let danyuan of data.danyuanList) {
-					// 循环节
-					for (let jie of danyuan.jieList) {
-						if (jie.jieId == jieId) {
-							return {
-								danyuan: danyuan,
-								jie: jie
-							}
-						}
-					}
-				}
-				return null;
-			},
-			// 统一保存和跳转方法
-			async saveAndNavigate(jieId, type, da, code) {
-				if (code == 'jixu') { // 追加参数判断 非继续 [临时]
-					if (!this.banbenId || !this.danyuanId) {
-						toast("banbenId或者danyuanId 丢失")
-						return false
-					}
-				}
-
-				let req = {
-					"banbenId": this.banbenId,
-					"danyuanId": da.danyuanId, // 修改当时版本ID为数据内版本Id [临时]
-					"jieId": jieId
-				}
-
-				try {
-					const res = await shuxueSave(req);
-					console.log('res', res);
-
-					if (res.code == 0 && res.data) {
-						let curJieAndDanyuan = this.getJieAndDanyuan(this.banbenInfo, jieId);
-						console.log('curJieAndDanyuan', curJieAndDanyuan);
-
-						if (!curJieAndDanyuan) {
-							toast("未找到课程信息");
-							return false;
-						}
-
-						// 更新缓存
-						const cacheData = cacheManager.get('xuexi-shuxue') || {};
-						cacheData.curDanyuanName = curJieAndDanyuan.danyuan.danyuanName;
-						cacheData.curKechengName = curJieAndDanyuan.jie.jieIntro;
-						cacheData.curJieId = jieId;
-						cacheData.type = curJieAndDanyuan.jie.type;
-						cacheManager.set('xuexi-shuxue', cacheData);
-
-						// 跳转到学习页面
-						if (type == 1) {
-							uni.navigateTo({
-								url: `/pages/xinshuxue/lookShipin?jieId=${jieId}`
-							})
-						} else {
-							uni.navigateTo({
-								url: `/pages/xinshuxue/unitTest?jieId=${jieId}`
-							})
-						}
-					} else {
-						toast("保存位置出错");
-						return false;
-					}
-				} catch (error) {
-					toast("保存失败");
-					return false;
-				}
-			},
-			handlePlay(da, code) {
-				console.log('da', da);
-				let jieId = null;
-				if (code == 'jixu') {
-					if (!da.curJieId) {
-						toast("无课程ID");
-						return;
-					}
-					jieId = da.curJieId;
-				} else {
-					jieId = da.jieId;
-				}
-				// [临时]
-				this.saveAndNavigate(jieId, da.type, da, code);
-			},
-			handleClickDanyuan(item) {
-				// 更新为点击的动态单元Id [临时]
-				if (!item.danyuanId) {
-					toast("this.danyuanId丢失")
-					return false
-				}
-				this.isShow = true;
-				setTimeout(() => {
-					// 更新为点击的动态单元Id [临时]
-					this.$refs.dyRef.handleShow(item.danyuanId)
-				}, 100)
-			},
-			handleBack() {
-				uni.navigateTo({
-					url: `/pages/chanpinXuanze/banben?dengjiId=` + this.dengjiId
-				})
-			},
-
-			// 计算单元位置
-			calculateUnitPositions() {
-				this.unitPositions = [];
-				// 使用selectAll获取所有单元元素
-				uni
-					.createSelectorQuery()
-					.in(this)
-					.selectAll(".cccAAA")
-					.boundingClientRect((rects) => {
-						this.unitPositions = [...rects]
-					})
-					.exec();
-				setTimeout(() => {
-					uni
-						.createSelectorQuery()
-						.in(this)
-						.selectAll(".cccAAAB")
-						.boundingClientRect((rects) => {
-							this.defaultD = rects[0].top;
-						})
-						.exec();
-				},500)
-			},
-			// 绑定滚动事件
-			bindScrollEvent() {
-				const checkScroll = () => {
-					const query = uni.createSelectorQuery().in(this)
-					query.select('.cccAAA2').boundingClientRect()
-					query.exec((res) => {
-						let arr = []
-						this.unitPositions.forEach((item, index) => {
-							if ((item.top + res[0].top) < this.defaultD+20) {
-								arr.push(index)
-							}
-              if (res[0].top< 0) {
-                this.showTop = true
-              } else {
-                this.showTop = false
-              }
-						})
-						if (arr.length) {
-							if (this.activeAAAIndex != arr[arr.length - 1]) {
-								// console.log('当前数据', this.danyuanList[arr[arr.length - 1]])
-								this.activeAAAIndex = arr[arr.length - 1];
-							}
-						}
-					})
-
-					clearTimeout(this.scrollTimer)
-					this.scrollTimer = null;
-					this.scrollTimer = setTimeout(checkScroll, 200) // 每200ms检查一次
-				}
-
-				checkScroll()
-			},
-			// 执行初始化
-			doReady() {
-				// 页面加载完成后初始化
-				this.$nextTick(() => {
-					this.bindScrollEvent();
-					this.calculateUnitPositions();
-					// 添加初始调试信息
-					console.log("页面初始化完成,单元位置:", this.unitPositions);
-				});
-			},
-      handleTop() {
-        console.log('ccc', this.$refs.containerRef.$el.scrollTop)
-        this.$refs.containerRef.$el.scrollTop = 0;
-      }
-		},
-	}
-</script>

+ 0 - 427
pages/chanpinneirong/tx版本有回到顶部的.vue

@@ -1,427 +0,0 @@
-<template>
-	<view class="ezy-xuexi-page3">
-		<view class="icon-title-navBar-box">
-			<text class="nav-bar-title">学习</text>
-		</view>
-		<!-- 吸顶单元标题 -->
-		<view
-			v-if="currentStickyTitle"
-			class="item-dy-box item-fixed"
-		>
-			<view class="dy-left-box">L{{ currentStickyDengjiId }}</view>
-			<view class="dy-right-box">
-				<view class="right-content">
-					<view class="dy-name">{{ currentStickyTitle }}</view>
-					<view>{{ currentStickyIntro }}</view>
-				</view>
-			</view>
-		</view>
-
-		<!-- 滚动区域 -->
-		<scroll-view
-			v-if="existData"
-			class="ezy-page-body xuexi-page-body"
-			scroll-y
-			:scroll-top="scrollTop"
-			@scroll="handleScroll"
-		>
-			<view class="xxjl-card-box-padding">
-				<view class="xxjl-card-box">
-					<!-- 显示内容 -->
-					<view class="card-body-box">
-						<img :src="banbenInfo.cover" />
-						<view class="body-right">
-							<view class="right-name">{{ banbenInfo.chanpinName }}</view>
-							<view>等级:{{banbenInfo.dengjiName}}</view>
-							<view>版本:{{banbenInfo.name}}</view>
-							<view>单元:{{banbenInfo.curDanyuanName}}</view>
-							<view>课程:{{banbenInfo.curKechengName}}</view>
-						</view>
-					</view>
-					<view class="card-progress-box">
-						<view class="xx-progress-box">
-							<view>学习进度</view>
-							<progress :percent="curProcess" class="xx-progress" stroke-width="20"
-								backgroundColor="#3c7dfd" activeColor="#ffd11c" />
-						</view>
-						<ezyActiveVue class="ezy-btn-active jxxx-btn" @aclick="handlePlay(banbenInfo,'jixu')"></ezyActiveVue>
-					</view>
-				</view>
-			</view>
-
-			<view class="xx-item-list">
-				<!-- <view class="xx-item-title">— 以下为当前等级课程目录 —</view> -->
-
-				<view v-for="(danyuanItem, index) in danyuanList" :key="danyuanItem.danyuanId">
-
-					<!-- 单元标题(带唯一ID,用于位置查询) -->
-					<view class="xx-item-title" :id="`title-${danyuanItem.danyuanId}`">
-						— {{ danyuanItem.danyuanName }} {{ danyuanItem.danyuanIntro }} —
-					</view>
-
-					<!-- 节列表 -->
-					<ezyActiveVue
-						class="ezy-list-item-active xx-item-box"
-						v-for="jieItem in danyuanItem.jieList"
-						:key="jieItem.jieId"
-						@aclick="handlePlay(jieItem,'play')"
-					>
-						<view
-							class="xx-item-status"
-							:class="jieItem.wanchengFlag == 1 ? 'completed-status' : 'uncompleted-status'"
-						></view>
-						<img :src="jieItem.cover" />
-						<view class="xx-text-box">
-							<view>{{ jieItem.jieName }}</view>
-							<view>{{ jieItem.jieIntro }}</view>
-						</view>
-						<view class="xx-item-btn"></view>
-					</ezyActiveVue>
-				</view>
-
-				<view class="xx-item-title">本级别最后一单元啦~</view>
-				<view class="xx-more-btn" @click="moreBtn"></view>
-			</view>
-		</scroll-view>
-		<!-- 回到顶部 -->
-		<view v-if="currentStickyTitle" class="go-top-btn" @click="goTopBtn"></view>
-
-		<!-- 无数据占位 -->
-		<view v-if="!existData" class="ezy-page-body xuexi-page-body"></view>
-
-		<!-- 弹窗组件 -->
-		<danyuanInfoVue ref="dyRef" v-if="isShow" @close="isShow = false"></danyuanInfoVue>
-
-		<!-- 底部 tabBar -->
-		<custom-tab-bar :show="true" :current-index="currentTabIndex" />
-	</view>
-</template>
-
-<script>
-	import ezyActiveVue from "@/components/ezyActive/ezyActive.vue";
-	import CustomTabBar from '@/components/custom-tabbar/index.vue';
-	import cacheManager from "@/utils/cacheManager.js";
-	import {
-		shuxueChanpinBanbenInfo,
-		shuxueSave
-	} from "@/api/chanpinneirong.js"
-	import {
-		onLoad,
-		onShow,
-		onHide,
-		onUnload
-	} from "@dcloudio/uni-app"
-	import danyuanInfoVue from '@/pages/xinshuxue/components/danyuanInfo.vue';
-	import {
-		toast
-	} from '../../utils/common';
-  import {updateXuexiProcess} from "./useNeirongShuxue"
-	export default {
-		data() {
-			return {
-				canExitApp: false,
-
-				danyuanList: [],
-				banbenInfo: {},
-				banbenId: '',
-				danyuanId: '',
-				chanpinId: '',
-				dengjiId: '',
-				curProcess: '',
-				currentTabIndex: 1,
-				existData: true,
-				isShow: false,
-
-				stickyHeight: 0, // 吸顶栏高度(px)
-				currentStickyTitle: '', // 初始为空,不显示吸顶
-				currentStickyIntro: '',
-				currentStickyDengjiId: '',
-				titlePositions: [],
-				scrollTop: 0,
-			}
-		},
-		components: {
-			CustomTabBar,
-			danyuanInfoVue,
-			ezyActiveVue
-		},
-		onLoad(options) {
-			uni.hideTabBar()
-		},
-		onShow() {
-			this.currentTabIndex = 1
-			const cacheData = cacheManager.get('xuexi-shuxue');
-
-			if (cacheData) {
-				console.log('使用缓存数据');
-				this.updateFromCache();
-			} else {
-				console.log('重新请求数据');
-				const cacheDataAuth = cacheManager.get('auth');
-				this.initFromOptions(cacheDataAuth);
-			}
-		},
-		onHide() {
-			console.log('学习页面隐藏')
-		},
-		onUnload() {
-			// 页面卸载无需特殊处理
-		},
-		methods: {
-			goTopBtn() {
-				this.scrollTop = -1; // 先设一个无效值(确保变化)
-				this.$nextTick(() => {
-					this.scrollTop = 0; // 再滚到顶部
-				});
-			},
-			moreBtn() {
-				uni.switchTab({
-					url: '/pages/chanpinXuanze/index'
-				})
-			},
-
-			initFromOptions(options) {
-				console.log('options', options);
-				this.chanpinId = options.chanpinId;
-				this.danyuanId = options.danyuanId;
-				this.banbenId = options.banbenId;
-				this.dengjiId = options.dengjiId;
-				this.loadDataFromApi();
-			},
-			// 从缓存更新数据方法
-			updateFromCache() {
-				updateXuexiProcess()
-				const cacheData = cacheManager.get('xuexi-shuxue');
-				console.log('cacheData 从缓存更新数据方法', cacheData);
-				if (cacheData) {
-					this.banbenInfo = { ...cacheData };
-					this.curProcess = cacheData.curProcess;
-					this.danyuanList = [...(cacheData.danyuanList || [])];
-
-					this.$nextTick(() => {
-						this.updateTitlePositions();
-					});
-				}
-			},
-
-			loadDataFromApi() {
-				this.banbenInfo = {}
-				this.curProcess = ''
-				this.danyuanList = []
-				const req = {
-					banbenId: this.banbenId
-				}
-				shuxueChanpinBanbenInfo(req).then(res => {
-					if (res.code === 0) {
-						this.banbenInfo = res.data;
-						this.curProcess =res.data.curProcess * 100;
-						this.danyuanList = res.data.danyuanList || [];
-
-						if (!this.danyuanList.length) {
-							this.existData = false
-						}
-						// 保存到缓存(新增了参数保存)
-						const cacheData = {
-							...res.data,
-							banbenId: this.banbenId,
-							chanpinId: this.chanpinId,
-							danyuanId: this.danyuanId,
-							dengjiId: this.dengjiId
-						};
-						cacheManager.set('xuexi-shuxue', cacheData);
-
-						// 更新全局auth信息
-						cacheManager.updateObject('auth', {
-							chanpinId: res.data.chanpinId,
-							banbenId: this.banbenId,
-							danyuanId: res.data.curDanyuanId,
-							dengjiId: res.data.dengjiId
-						});
-						this.danyuanId = res.data.curDanyuanId
-						this.dengjiId = res.data.dengjiId
-						this.chanpinId = res.data.chanpinId
-						// 数据加载完成后初始化观察器
-						this.$nextTick(() => {
-							this.updateTitlePositions();
-						});
-					}
-				}).catch(res => {
-					cacheManager.remove("xuexi-shuxue");
-					toast("加载失败,请重试");
-				});
-			},
-
-			updateStickyHeight() {
-					const query = uni.createSelectorQuery().in(this);
-					query.select('.item-fixed').boundingClientRect(res => {
-						if (res) {
-							this.stickyHeight = res.height; // 单位 px
-						} else {
-							this.stickyHeight = 100; // 默认 fallback(约 200rpx = 100px)
-						}
-					}).exec();
-				},
-
-			// 获取所有标题在 scroll-view 中的绝对位置
-			updateTitlePositions() {
-				this.titlePositions = [];
-				this.danyuanTitleRefs = [];
-
-				if (!this.danyuanList.length) return;
-
-				const query = uni.createSelectorQuery().in(this);
-				this.danyuanList.forEach(item => {
-					query.select(`#title-${item.danyuanId}`).boundingClientRect();
-					this.danyuanTitleRefs.push(item);
-				});
-
-				query.exec((rects) => {
-					rects.forEach((rect, index) => {
-						if (rect) {
-							// 在 scroll-view 初始未滚动时,rect.top 就是内容中的绝对 top
-							this.titlePositions.push({
-								danyuanId: this.danyuanTitleRefs[index].danyuanId,
-								name: this.danyuanTitleRefs[index].danyuanName,
-								intro: this.danyuanTitleRefs[index].danyuanIntro,
-								top: rect.top
-							});
-						}
-					});
-					this.titlePositions.sort((a, b) => a.top - b.top);
-					// 👇 新增:更新吸顶栏高度
-								this.updateStickyHeight();
-				});
-			},
-
-			handleScroll(e) {
-				const scrollTop = e.detail.scrollTop;
-				const firstTitle = this.titlePositions[0];
-
-				if (!firstTitle) {
-					this.currentStickyTitle = '';
-					return;
-				}
-
-				// 👇 关键修改:提前触发吸顶
-				const triggerOffset = scrollTop + this.stickyHeightPx;
-
-				// 如果还没滚到第一个标题的顶部(考虑吸顶高度),则隐藏
-				if (triggerOffset < firstTitle.top) {
-					this.currentStickyTitle = '';
-					return;
-				}
-
-				// 找出最后一个 top <= triggerOffset 的标题(即刚进入吸顶区的单元)
-				let matched = null;
-				for (let i = this.titlePositions.length - 1; i >= 0; i--) {
-					if (this.titlePositions[i].top <= triggerOffset) {
-						matched = this.titlePositions[i];
-						break;
-					}
-				}
-
-				if (matched) {
-					this.currentStickyTitle = matched.name;
-					this.currentStickyIntro = matched.intro;
-					this.currentStickyDengjiId = this.banbenInfo.dengjiId || 1;
-				} else {
-					this.currentStickyTitle = '';
-				}
-			},
-
-			getJieAndDanyuan(data, jieId) {
-				for (let danyuan of data.danyuanList) {
-					for (let jie of danyuan.jieList) {
-						if (jie.jieId == jieId) {
-							return { danyuan, jie }
-						}
-					}
-				}
-				return null;
-			},
-
-			async saveAndNavigate(jieId, type, da, code) {
-				if (code == 'jixu') {
-					if (!this.banbenId || !this.danyuanId) {
-						toast("banbenId或者danyuanId 丢失")
-						return false
-					}
-				}
-
-				let req = {
-					"banbenId": this.banbenId,
-					"danyuanId": da.danyuanId,
-					"jieId": jieId
-				}
-				console.log('req',req);
-				try {
-					const res = await shuxueSave(req);
-					if (res.code == 0 && res.data) {
-						let curJieAndDanyuan = this.getJieAndDanyuan(this.banbenInfo, jieId);
-						if (!curJieAndDanyuan) {
-							toast("未找到课程信息");
-							return false;
-						}
-
-						const cacheData = cacheManager.get('xuexi-shuxue') || {};
-						cacheData.curDanyuanName = curJieAndDanyuan.danyuan.danyuanName;
-						cacheData.curKechengName = curJieAndDanyuan.jie.jieIntro;
-						cacheData.curJieId = jieId;
-						cacheData.type = curJieAndDanyuan.jie.type;
-						cacheManager.set('xuexi-shuxue', cacheData);
-
-						if (type == 1) {
-							uni.navigateTo({ url: `/pages/xinshuxue/lookShipin?jieId=${jieId}` })
-						} else {
-							uni.navigateTo({ url: `/pages/xinshuxue/unitTest?jieId=${jieId}` })
-						}
-					} else {
-						toast("保存位置出错");
-						return false;
-					}
-				} catch (error) {
-					toast("保存失败");
-					return false;
-				}
-			},
-			handlePlay(da, code) {
-				let jieId = code === 'jixu' ? da.curJieId : da.jieId;
-				if (!jieId) {
-					toast("无课程ID");
-					return;
-				}
-				this.saveAndNavigate(jieId, da.type, da, code);
-			},
-			handleClickDanyuan(item) {
-				if (!item.danyuanId) {
-					toast("danyuanId丢失")
-					return false
-				}
-				this.isShow = true;
-				setTimeout(() => {
-					// 更新为点击的动态单元Id [临时]
-					this.$refs.dyRef.handleShow(item.danyuanId)
-				}, 100)
-			},
-			handleBack() {
-				uni.navigateTo({
-					url: `/pages/chanpinXuanze/banben?dengjiId=` + this.dengjiId
-				})
-			},
-		},
-
-		// 计算吸顶栏下方的偏移(确保内容不被遮挡)
-		computed: {
-			stickyHeightPx() {
-					// 175rpx ≈ 87.5px,取整 90px 足够安全
-					return 90;
-				},
-			topOffset() {
-					// 只有当吸顶栏显示时,才增加上边距避免内容被遮挡
-					return this.currentStickyTitle ? '80rpx' : '0rpx';
-				}
-
-		}
-	}
-</script>
-

+ 0 - 414
pages/chanpinneirong/zuichu1.vue

@@ -1,414 +0,0 @@
-<template>
-	<view class="ezy-xuexi-page">
-		<view class="icon-title-navBar-box">
-			<text class="nav-bar-title">学习</text>
-		</view>
-		<view v-if="existData" class="ezy-page-body xuexi-page-body">
-			<view class="xxjl-card-box-padding">
-				<view class="xxjl-card-box">
-					<!-- 显示内容 -->
-					<view class="card-body-box">
-						<img :src="banbenInfo.cover" />
-						<view class="body-right">
-							<view class="right-name">{{ banbenInfo.chanpinName }}</view>
-							<view>等级:{{banbenInfo.dengjiName}}</view>
-							<view>版本:{{banbenInfo.name}}</view>
-							<view>单元:{{banbenInfo.curDanyuanName}}</view>
-							<view>课程:{{banbenInfo.curKechengName}}</view>
-						</view>
-					</view>
-					<view class="card-progress-box">
-						<view class="xx-progress-box">
-							<view>学习进度</view>
-							<progress :percent="curProcess" class="xx-progress" stroke-width="20"
-								backgroundColor="#3c7dfd" activeColor="#ffd11c" />
-						</view>
-						<ezyActiveVue class="ezy-btn-active jxxx-btn" @aclick="handlePlay(banbenInfo,'jixu')"></ezyActiveVue>
-					</view>
-				</view>
-				</view>
-					<view class="xx-item-list">
-						<view class="xx-item-title">— 以下为当前等级课程目录 —</view>
-						<view v-for="(danyuanItem,index) in danyuanList" :key="danyuanItem.danyuanId">
-							<!-- 第一单元 -->
-								<ezyActiveVue v-if="index ==0" class="ezy-list-item-active item-dy-box item-fixed" @aclick="handleClickDanyuan(danyuanItem)" :id="`unit-title-${danyuanItem.danyuanId}`">
-									<view class="dy-left-box">L{{banbenInfo.dengjiId}}</view>
-									<view class="dy-right-box">
-										<view class="right-content">
-											<view class="dy-name">{{danyuanItem.danyuanName}}</view>
-											<view>{{danyuanItem.danyuanIntro}}</view>
-										</view>
-									</view>
-								</ezyActiveVue>
-							<!-- 其他单元 -->
-							<view v-if="index !=0" class="xx-item-title" :id="`unit-title-${danyuanItem.danyuanId}`">— {{danyuanItem.danyuanName}}
-								{{danyuanItem.danyuanIntro}} —
-							</view>
-						
-
-							<ezyActiveVue class="ezy-list-item-active xx-item-box" v-for="jieItem in danyuanItem.jieList" :key="jieItem.jieId"
-								@aclick="handlePlay(jieItem,'play')">
-								<view class="xx-item-status"
-									:class="jieItem.wanchengFlag == 1 ? 'completed-status' : 'uncompleted-status'">
-								</view>
-								<img :src="jieItem.cover" />
-								<view class="xx-text-box">
-									<view>{{ jieItem.jieName }}</view>
-									<view>{{ jieItem.jieIntro }}</view>
-								</view>
-								<view class="xx-item-btn"></view>
-							</ezyActiveVue>
-
-
-						</view>
-						<view class="xx-item-title">本级别最后一单元啦~</view>
-						<view class="xx-more-btn" @click="moreBtn"></view>
-					</view>
-				
-				<!-- 回到顶部 -->
-				<view class="go-top-btn" v-if="false"></view>
-				
-			</view>
-			<view v-if="!existData">
-
-			</view>
-
-		<danyuanInfoVue ref="dyRef" v-if="isShow" @close="isShow= false"></danyuanInfoVue>
-		<custom-tab-bar :show="true" :current-index="currentTabIndex" />
-		
-	</view>
-</template>
-
-<script>
-	import ezyActiveVue from "@/components/ezyActive/ezyActive.vue";
-	import CustomTabBar from '@/components/custom-tabbar/index.vue';
-	import cacheManager from "@/utils/cacheManager.js";
-	import {
-		shuxueChanpinBanbenInfo,
-		shuxueSave
-	} from "@/api/chanpinneirong.js"
-	import {
-		onLoad,
-		onShow,
-		onHide,
-		onUnload
-	} from "@dcloudio/uni-app"
-	import danyuanInfoVue from '@/pages/xinshuxue/components/danyuanInfo.vue';
-	import {
-		toast
-	} from '../../utils/common';
-  import {updateXuexiProcess} from "./useNeirongShuxue"
-	export default {
-		data() {
-			return {
-				canExitApp: false,
-
-				danyuanList: [],
-				currentUnitName: "",
-				isShow: true,
-				banbenInfo: {},
-				banbenId: '',
-				danyuanId: '',
-				chanpinId: '',
-				dengjiId: '',
-				curProcess: '',
-				currentTabIndex: 1,
-				cacheManagerLocal: null,
-				hasCache: false, // 是否有缓存的标志
-				needRefresh: false, // 是否需要刷新数据的标志
-				existData: true,
-
-				currentVisibleUnit: '', // 当前可见的单元名称
-				observer: null, // IntersectionObserver实例
-				lastToastUnitId: null ,// 上一次toast的单元ID,防止重复触发
-				isFixed:false,
-			}
-		},
-		components: {
-			CustomTabBar,
-			danyuanInfoVue,
-			ezyActiveVue
-		},
-		onShow() {
-			this.currentTabIndex = 1
-			const cacheData = cacheManager.get('xuexi-shuxue');
-
-			if (cacheData) {
-				console.log('使用缓存数据');
-				this.updateFromCache();
-			} else {
-				console.log('重新请求数据');
-				this.hasCache = false;
-				const cacheDataAuth = cacheManager.get('auth');
-				this.initFromOptions(cacheDataAuth);
-			}
-
-			// 数据加载完成后初始化观察器
-			this.$nextTick(() => {
-				setTimeout(() => {
-					this.initObserver();
-				}, 300);
-			});
-		},
-		onHide() {
-			console.log('学习页面隐藏')
-			this.needRefresh = true;
-			// 销毁观察器
-			if (this.observer) {
-				this.observer.disconnect();
-				this.observer = null;
-			}
-		},
-		onUnload() {
-			// 页面卸载时销毁观察器
-			if (this.observer) {
-				this.observer.disconnect();
-				this.observer = null;
-			}
-		},
-		onLoad(options) {
-			uni.hideTabBar()
-		},
-		methods: {
-			moreBtn(){
-				uni.switchTab({
-					url: '/pages/chanpinXuanze/index'
-				})
-			},
-			// 初始化IntersectionObserver
-			initObserver() {
-			  if (this.observer) {
-			    this.observer.disconnect();
-			    this.observer = null;
-			  }
-			  
-			  // 监听每个单元标题的位置
-			  this.observer = uni.createIntersectionObserver(this).relativeToViewport({
-			    top: 0,  // 监听进入屏幕顶部的标题
-			    bottom: 0
-			  });
-			  
-			  this.danyuanList.forEach((item, index) => {
-			    const titleId = `unit-title-${item.danyuanId}`;
-			    this.observer.observe(`#${titleId}`, (res) => {
-			      if (res.intersectionRatio > 0) {
-			        // 标题在屏幕顶部区域内
-			        if (this.lastToastUnitId !== item.danyuanId) {
-			          this.lastToastUnitId = item.danyuanId;
-			          console.log(`显示${item.danyuanName}`);
-			          
-						console.log('item',item);
-						if(item.danyuanName !="第一单元"){
-							this.isFixed = true 
-						}else{
-							this.isFixed = false
-						}
-			          // uni.showToast({
-			          //   title: `${item.danyuanName}`,
-			          //   icon: 'none',
-			          //   duration: 1500
-			          // });
-			        }
-			      }
-			    });
-			  });
-			  
-			  // 页面初始时显示第一个单元
-			  if (this.danyuanList.length > 0) {
-			    this.lastToastUnitId = this.danyuanList[0].danyuanId;
-			  }
-			},
-
-			// 从参数初始化方法
-			initFromOptions(options) {
-				console.log('options', options);
-				this.chanpinId = options.chanpinId;
-				this.danyuanId = options.danyuanId;
-				this.banbenId = options.banbenId;
-				this.dengjiId = options.dengjiId;
-				this.loadDataFromApi();
-			},
-			// 从缓存更新数据方法
-			updateFromCache() {
-				updateXuexiProcess()
-				const cacheData = cacheManager.get('xuexi-shuxue');
-				console.log('cacheData 从缓存更新数据方法', cacheData);
-				if (cacheData) {
-					this.banbenInfo = {
-						...cacheData
-					};
-					this.curProcess = cacheData.curProcess;
-					this.danyuanList = [...(cacheData.danyuanList || [])];
-
-					// 数据更新后重新初始化观察器
-					this.$nextTick(() => {
-						setTimeout(() => {
-							this.initObserver();
-						}, 300);
-					});
-				}
-			},
-
-			shouldUseCache(options, cacheData) {
-				if (options.banbenId && cacheData.banbenId === options.banbenId) {
-					return true;
-				}
-				return false;
-			},
-			// 改成了
-			loadDataFromApi() {
-				this.banbenInfo = {}
-				this.curProcess = ''
-				this.danyuanList = []
-				const req = {
-					banbenId: this.banbenId
-				}
-				shuxueChanpinBanbenInfo(req).then(res => {
-					if (res.code === 0) {
-						this.banbenInfo = res.data;
-						this.curProcess =res.data.curProcess * 100;
-						this.danyuanList = res.data.danyuanList || [];
-
-						if (!this.danyuanList) {
-							this.existData = false
-						}
-						// 保存到缓存(新增了参数保存)
-						const cacheData = {
-							...res.data,
-							banbenId: this.banbenId,
-							chanpinId: this.chanpinId,
-							danyuanId: this.danyuanId,
-							dengjiId: this.dengjiId
-						};
-						cacheManager.set('xuexi-shuxue', cacheData);
-						this.hasCache = true;
-
-						// 更新全局auth信息
-						cacheManager.updateObject('auth', {
-							chanpinId: res.data.chanpinId,
-							banbenId: this.banbenId,
-							danyuanId: res.data.curDanyuanId,
-							dengjiId: res.data.dengjiId
-						});
-						this.danyuanId = res.data.curDanyuanId
-						this.dengjiId = res.data.dengjiId
-						this.chanpinId = res.data.chanpinId
-						// 数据加载完成后初始化观察器
-						this.$nextTick(() => {
-							setTimeout(() => {
-								this.initObserver();
-							}, 300);
-						});
-					}
-				}).catch(res => {
-					cacheManager.remove("xuexi-shuxue");
-					this.hasCache = false;
-					toast("加载失败,请重试");
-				});
-			},
-			getJieAndDanyuan(data, jieId) {
-				// 循环单元
-				for (let danyuan of data.danyuanList) {
-					// 循环节
-					for (let jie of danyuan.jieList) {
-						if (jie.jieId == jieId) {
-							return {
-								danyuan: danyuan,
-								jie: jie
-							}
-						}
-					}
-				}
-				return null;
-			},
-			// 统一保存和跳转方法
-			async saveAndNavigate(jieId, type,da, code) {
-				if (code == 'jixu') { // 追加参数判断 非继续 [临时]
-					if (!this.banbenId || !this.danyuanId) {
-						toast("banbenId或者danyuanId 丢失")
-						return false
-					}
-				}	 
-			
-				let req = {
-					"banbenId": this.banbenId,
-					"danyuanId": da.danyuanId, // 修改当时版本ID为数据内版本Id [临时]
-					"jieId": jieId
-				}
-
-				try {
-					const res = await shuxueSave(req);
-					console.log('res', res);
-
-					if (res.code == 0 && res.data) {
-						let curJieAndDanyuan = this.getJieAndDanyuan(this.banbenInfo, jieId);
-						console.log('curJieAndDanyuan', curJieAndDanyuan);
-
-						if (!curJieAndDanyuan) {
-							toast("未找到课程信息");
-							return false;
-						}
-
-						// 更新缓存
-						const cacheData = cacheManager.get('xuexi-shuxue') || {};
-						cacheData.curDanyuanName = curJieAndDanyuan.danyuan.danyuanName;
-						cacheData.curKechengName = curJieAndDanyuan.jie.jieIntro;
-						cacheData.curJieId = jieId;
-						cacheData.type = curJieAndDanyuan.jie.type;
-						cacheManager.set('xuexi-shuxue', cacheData);
-
-						// 跳转到学习页面
-						if (type == 1) {
-							uni.navigateTo({
-								url: `/pages/xinshuxue/lookShipin?jieId=${jieId}`
-							})
-						} else {
-							uni.navigateTo({
-								url: `/pages/xinshuxue/unitTest?jieId=${jieId}`
-							})
-						}
-					} else {
-						toast("保存位置出错");
-						return false;
-					}
-				} catch (error) {
-					toast("保存失败");
-					return false;
-				}
-			},
-			handlePlay(da, code) {
-				console.log('da', da);
-				let jieId = null;
-				if (code == 'jixu') {
-					if (!da.curJieId) {
-						toast("无课程ID");
-						return;
-					}
-					jieId = da.curJieId;
-				} else {
-					jieId = da.jieId;
-				}
-				// [临时]
-				this.saveAndNavigate(jieId, da.type, da, code);
-			},
-			handleClickDanyuan(item) {
-				// 更新为点击的动态单元Id [临时]
-				if (!item.danyuanId) {
-					toast("this.danyuanId丢失")
-					return false
-				}
-				this.isShow = true;
-				setTimeout(() => {
-					// 更新为点击的动态单元Id [临时]
-					this.$refs.dyRef.handleShow(item.danyuanId)
-				}, 100)
-			},
-			handleBack() {
-				uni.navigateTo({
-					url: `/pages/chanpinXuanze/banben?dengjiId=` + this.dengjiId
-				})
-			},
-		}
-	}
-</script>
-

+ 0 - 427
pages/chanpinneirong/大哥轮训版本.vue

@@ -1,427 +0,0 @@
-<template>
-	<view class="ezy-xuexi-page3">
-		<view class="icon-title-navBar-box">
-			<text class="nav-bar-title">学习</text>
-		</view>
-		<!-- 吸顶单元标题 -->
-		<view
-			v-if="currentStickyTitle"
-			class="item-dy-box item-fixed"
-		>
-			<view class="dy-left-box">L{{ currentStickyDengjiId }}</view>
-			<view class="dy-right-box">
-				<view class="right-content">
-					<view class="dy-name">{{ currentStickyTitle }}</view>
-					<view>{{ currentStickyIntro }}</view>
-				</view>
-			</view>
-		</view>
-
-		<!-- 滚动区域 -->
-		<scroll-view
-			v-if="existData"
-			class="ezy-page-body xuexi-page-body"
-			scroll-y
-			:scroll-top="scrollTop"
-			@scroll="handleScroll"
-		>
-			<view class="xxjl-card-box-padding">
-				<view class="xxjl-card-box">
-					<!-- 显示内容 -->
-					<view class="card-body-box">
-						<img :src="banbenInfo.cover" />
-						<view class="body-right">
-							<view class="right-name">{{ banbenInfo.chanpinName }}</view>
-							<view>等级:{{banbenInfo.dengjiName}}</view>
-							<view>版本:{{banbenInfo.name}}</view>
-							<view>单元:{{banbenInfo.curDanyuanName}}</view>
-							<view>课程:{{banbenInfo.curKechengName}}</view>
-						</view>
-					</view>
-					<view class="card-progress-box">
-						<view class="xx-progress-box">
-							<view>学习进度</view>
-							<progress :percent="curProcess" class="xx-progress" stroke-width="20"
-								backgroundColor="#3c7dfd" activeColor="#ffd11c" />
-						</view>
-						<ezyActiveVue class="ezy-btn-active jxxx-btn" @aclick="handlePlay(banbenInfo,'jixu')"></ezyActiveVue>
-					</view>
-				</view>
-			</view>
-
-			<view class="xx-item-list">
-				<!-- <view class="xx-item-title">— 以下为当前等级课程目录 —</view> -->
-
-				<view v-for="(danyuanItem, index) in danyuanList" :key="danyuanItem.danyuanId">
-
-					<!-- 单元标题(带唯一ID,用于位置查询) -->
-					<view class="xx-item-title" :id="`title-${danyuanItem.danyuanId}`">
-						— {{ danyuanItem.danyuanName }} {{ danyuanItem.danyuanIntro }} —
-					</view>
-
-					<!-- 节列表 -->
-					<ezyActiveVue
-						class="ezy-list-item-active xx-item-box"
-						v-for="jieItem in danyuanItem.jieList"
-						:key="jieItem.jieId"
-						@aclick="handlePlay(jieItem,'play')"
-					>
-						<view
-							class="xx-item-status"
-							:class="jieItem.wanchengFlag == 1 ? 'completed-status' : 'uncompleted-status'"
-						></view>
-						<img :src="jieItem.cover" />
-						<view class="xx-text-box">
-							<view>{{ jieItem.jieName }}</view>
-							<view>{{ jieItem.jieIntro }}</view>
-						</view>
-						<view class="xx-item-btn"></view>
-					</ezyActiveVue>
-				</view>
-
-				<view class="xx-item-title">本级别最后一单元啦~</view>
-				<view class="xx-more-btn" @click="moreBtn"></view>
-			</view>
-		</scroll-view>
-		<!-- 回到顶部 -->
-		<view class="go-top-btn" @click="goTopBtn"></view>
-
-		<!-- 无数据占位 -->
-		<view v-if="!existData" class="ezy-page-body xuexi-page-body"></view>
-
-		<!-- 弹窗组件 -->
-		<danyuanInfoVue ref="dyRef" v-if="isShow" @close="isShow = false"></danyuanInfoVue>
-
-		<!-- 底部 tabBar -->
-		<custom-tab-bar :show="true" :current-index="currentTabIndex" />
-	</view>
-</template>
-
-<script>
-	import ezyActiveVue from "@/components/ezyActive/ezyActive.vue";
-	import CustomTabBar from '@/components/custom-tabbar/index.vue';
-	import cacheManager from "@/utils/cacheManager.js";
-	import {
-		shuxueChanpinBanbenInfo,
-		shuxueSave
-	} from "@/api/chanpinneirong.js"
-	import {
-		onLoad,
-		onShow,
-		onHide,
-		onUnload
-	} from "@dcloudio/uni-app"
-	import danyuanInfoVue from '@/pages/xinshuxue/components/danyuanInfo.vue';
-	import {
-		toast
-	} from '../../utils/common';
-  import {updateXuexiProcess} from "./useNeirongShuxue"
-	export default {
-		data() {
-			return {
-				canExitApp: false,
-
-				danyuanList: [],
-				banbenInfo: {},
-				banbenId: '',
-				danyuanId: '',
-				chanpinId: '',
-				dengjiId: '',
-				curProcess: '',
-				currentTabIndex: 1,
-				existData: true,
-				isShow: false,
-
-				stickyHeight: 0, // 吸顶栏高度(px)
-				currentStickyTitle: '', // 初始为空,不显示吸顶
-				currentStickyIntro: '',
-				currentStickyDengjiId: '',
-				titlePositions: [],
-				scrollTop: 0,
-			}
-		},
-		components: {
-			CustomTabBar,
-			danyuanInfoVue,
-			ezyActiveVue
-		},
-		onLoad(options) {
-			uni.hideTabBar()
-		},
-		onShow() {
-			this.currentTabIndex = 1
-			const cacheData = cacheManager.get('xuexi-shuxue');
-
-			if (cacheData) {
-				console.log('使用缓存数据');
-				this.updateFromCache();
-			} else {
-				console.log('重新请求数据');
-				const cacheDataAuth = cacheManager.get('auth');
-				this.initFromOptions(cacheDataAuth);
-			}
-		},
-		onHide() {
-			console.log('学习页面隐藏')
-		},
-		onUnload() {
-			// 页面卸载无需特殊处理
-		},
-		methods: {
-			goTopBtn() {
-				this.scrollTop = -1; // 先设一个无效值(确保变化)
-				this.$nextTick(() => {
-					this.scrollTop = 0; // 再滚到顶部
-				});
-			},
-			moreBtn() {
-				uni.switchTab({
-					url: '/pages/chanpinXuanze/index'
-				})
-			},
-
-			initFromOptions(options) {
-				console.log('options', options);
-				this.chanpinId = options.chanpinId;
-				this.danyuanId = options.danyuanId;
-				this.banbenId = options.banbenId;
-				this.dengjiId = options.dengjiId;
-				this.loadDataFromApi();
-			},
-			// 从缓存更新数据方法
-			updateFromCache() {
-				updateXuexiProcess()
-				const cacheData = cacheManager.get('xuexi-shuxue');
-				console.log('cacheData 从缓存更新数据方法', cacheData);
-				if (cacheData) {
-					this.banbenInfo = { ...cacheData };
-					this.curProcess = cacheData.curProcess;
-					this.danyuanList = [...(cacheData.danyuanList || [])];
-
-					this.$nextTick(() => {
-						this.updateTitlePositions();
-					});
-				}
-			},
-
-			loadDataFromApi() {
-				this.banbenInfo = {}
-				this.curProcess = ''
-				this.danyuanList = []
-				const req = {
-					banbenId: this.banbenId
-				}
-				shuxueChanpinBanbenInfo(req).then(res => {
-					if (res.code === 0) {
-						this.banbenInfo = res.data;
-						this.curProcess =res.data.curProcess * 100;
-						this.danyuanList = res.data.danyuanList || [];
-
-						if (!this.danyuanList.length) {
-							this.existData = false
-						}
-						// 保存到缓存(新增了参数保存)
-						const cacheData = {
-							...res.data,
-							banbenId: this.banbenId,
-							chanpinId: this.chanpinId,
-							danyuanId: this.danyuanId,
-							dengjiId: this.dengjiId
-						};
-						cacheManager.set('xuexi-shuxue', cacheData);
-
-						// 更新全局auth信息
-						cacheManager.updateObject('auth', {
-							chanpinId: res.data.chanpinId,
-							banbenId: this.banbenId,
-							danyuanId: res.data.curDanyuanId,
-							dengjiId: res.data.dengjiId
-						});
-						this.danyuanId = res.data.curDanyuanId
-						this.dengjiId = res.data.dengjiId
-						this.chanpinId = res.data.chanpinId
-						// 数据加载完成后初始化观察器
-						this.$nextTick(() => {
-							this.updateTitlePositions();
-						});
-					}
-				}).catch(res => {
-					cacheManager.remove("xuexi-shuxue");
-					toast("加载失败,请重试");
-				});
-			},
-
-			updateStickyHeight() {
-					const query = uni.createSelectorQuery().in(this);
-					query.select('.item-fixed').boundingClientRect(res => {
-						if (res) {
-							this.stickyHeight = res.height; // 单位 px
-						} else {
-							this.stickyHeight = 100; // 默认 fallback(约 200rpx = 100px)
-						}
-					}).exec();
-				},
-
-			// 获取所有标题在 scroll-view 中的绝对位置
-			updateTitlePositions() {
-				this.titlePositions = [];
-				this.danyuanTitleRefs = [];
-
-				if (!this.danyuanList.length) return;
-
-				const query = uni.createSelectorQuery().in(this);
-				this.danyuanList.forEach(item => {
-					query.select(`#title-${item.danyuanId}`).boundingClientRect();
-					this.danyuanTitleRefs.push(item);
-				});
-
-				query.exec((rects) => {
-					rects.forEach((rect, index) => {
-						if (rect) {
-							// 在 scroll-view 初始未滚动时,rect.top 就是内容中的绝对 top
-							this.titlePositions.push({
-								danyuanId: this.danyuanTitleRefs[index].danyuanId,
-								name: this.danyuanTitleRefs[index].danyuanName,
-								intro: this.danyuanTitleRefs[index].danyuanIntro,
-								top: rect.top
-							});
-						}
-					});
-					this.titlePositions.sort((a, b) => a.top - b.top);
-					// 👇 新增:更新吸顶栏高度
-								this.updateStickyHeight();
-				});
-			},
-
-			handleScroll(e) {
-				const scrollTop = e.detail.scrollTop;
-				const firstTitle = this.titlePositions[0];
-
-				if (!firstTitle) {
-					this.currentStickyTitle = '';
-					return;
-				}
-
-				// 👇 关键修改:提前触发吸顶
-				const triggerOffset = scrollTop + this.stickyHeightPx;
-
-				// 如果还没滚到第一个标题的顶部(考虑吸顶高度),则隐藏
-				if (triggerOffset < firstTitle.top) {
-					this.currentStickyTitle = '';
-					return;
-				}
-
-				// 找出最后一个 top <= triggerOffset 的标题(即刚进入吸顶区的单元)
-				let matched = null;
-				for (let i = this.titlePositions.length - 1; i >= 0; i--) {
-					if (this.titlePositions[i].top <= triggerOffset) {
-						matched = this.titlePositions[i];
-						break;
-					}
-				}
-
-				if (matched) {
-					this.currentStickyTitle = matched.name;
-					this.currentStickyIntro = matched.intro;
-					this.currentStickyDengjiId = this.banbenInfo.dengjiId || 1;
-				} else {
-					this.currentStickyTitle = '';
-				}
-			},
-
-			getJieAndDanyuan(data, jieId) {
-				for (let danyuan of data.danyuanList) {
-					for (let jie of danyuan.jieList) {
-						if (jie.jieId == jieId) {
-							return { danyuan, jie }
-						}
-					}
-				}
-				return null;
-			},
-
-			async saveAndNavigate(jieId, type, da, code) {
-				if (code == 'jixu') {
-					if (!this.banbenId || !this.danyuanId) {
-						toast("banbenId或者danyuanId 丢失")
-						return false
-					}
-				}
-
-				let req = {
-					"banbenId": this.banbenId,
-					"danyuanId": da.danyuanId,
-					"jieId": jieId
-				}
-
-				try {
-					const res = await shuxueSave(req);
-					if (res.code == 0 && res.data) {
-						let curJieAndDanyuan = this.getJieAndDanyuan(this.banbenInfo, jieId);
-						if (!curJieAndDanyuan) {
-							toast("未找到课程信息");
-							return false;
-						}
-
-						const cacheData = cacheManager.get('xuexi-shuxue') || {};
-						cacheData.curDanyuanName = curJieAndDanyuan.danyuan.danyuanName;
-						cacheData.curKechengName = curJieAndDanyuan.jie.jieIntro;
-						cacheData.curJieId = jieId;
-						cacheData.type = curJieAndDanyuan.jie.type;
-						cacheManager.set('xuexi-shuxue', cacheData);
-
-						if (type == 1) {
-							uni.navigateTo({ url: `/pages/xinshuxue/lookShipin?jieId=${jieId}` })
-						} else {
-							uni.navigateTo({ url: `/pages/xinshuxue/unitTest?jieId=${jieId}` })
-						}
-					} else {
-						toast("保存位置出错");
-						return false;
-					}
-				} catch (error) {
-					toast("保存失败");
-					return false;
-				}
-			},
-			handlePlay(da, code) {
-				let jieId = code === 'jixu' ? da.curJieId : da.jieId;
-				if (!jieId) {
-					toast("无课程ID");
-					return;
-				}
-				this.saveAndNavigate(jieId, da.type, da, code);
-			},
-			handleClickDanyuan(item) {
-				if (!item.danyuanId) {
-					toast("danyuanId丢失")
-					return false
-				}
-				this.isShow = true;
-				setTimeout(() => {
-					// 更新为点击的动态单元Id [临时]
-					this.$refs.dyRef.handleShow(item.danyuanId)
-				}, 100)
-			},
-			handleBack() {
-				uni.navigateTo({
-					url: `/pages/chanpinXuanze/banben?dengjiId=` + this.dengjiId
-				})
-			},
-		},
-
-		// 计算吸顶栏下方的偏移(确保内容不被遮挡)
-		computed: {
-			stickyHeightPx() {
-					// 175rpx ≈ 87.5px,取整 90px 足够安全
-					return 90;
-				},
-			topOffset() {
-					// 只有当吸顶栏显示时,才增加上边距避免内容被遮挡
-					return this.currentStickyTitle ? '80rpx' : '0rpx';
-				}
-
-		}
-	}
-</script>
-

+ 0 - 414
pages/chanpinneirong/最初的index.vue

@@ -1,414 +0,0 @@
-<template>
-	<view class="ezy-xuexi-page">
-		<view class="icon-title-navBar-box">
-			<text class="nav-bar-title">学习</text>
-		</view>
-		<view v-if="existData" class="ezy-page-body xuexi-page-body">
-			<view class="xxjl-card-box-padding">
-				<view class="xxjl-card-box">
-					<!-- 显示内容 -->
-					<view class="card-body-box">
-						<img :src="banbenInfo.cover" />
-						<view class="body-right">
-							<view class="right-name">{{ banbenInfo.chanpinName }}</view>
-							<view>等级:{{banbenInfo.dengjiName}}</view>
-							<view>版本:{{banbenInfo.name}}</view>
-							<view>单元:{{banbenInfo.curDanyuanName}}</view>
-							<view>课程:{{banbenInfo.curKechengName}}</view>
-						</view>
-					</view>
-					<view class="card-progress-box">
-						<view class="xx-progress-box">
-							<view>学习进度</view>
-							<progress :percent="curProcess" class="xx-progress" stroke-width="20"
-								backgroundColor="#3c7dfd" activeColor="#ffd11c" />
-						</view>
-						<ezyActiveVue class="ezy-btn-active jxxx-btn" @aclick="handlePlay(banbenInfo,'jixu')"></ezyActiveVue>
-					</view>
-				</view>
-				</view>
-					<view class="xx-item-list">
-						<view class="xx-item-title">— 以下为当前等级课程目录 —</view>
-						<view v-for="(danyuanItem,index) in danyuanList" :key="danyuanItem.danyuanId">
-							<!-- 第一单元 -->
-								<ezyActiveVue v-if="index ==0" class="ezy-list-item-active item-dy-box item-fixed" @aclick="handleClickDanyuan(danyuanItem)" :id="`unit-title-${danyuanItem.danyuanId}`">
-									<view class="dy-left-box">L{{banbenInfo.dengjiId}}</view>
-									<view class="dy-right-box">
-										<view class="right-content">
-											<view class="dy-name">{{danyuanItem.danyuanName}}</view>
-											<view>{{danyuanItem.danyuanIntro}}</view>
-										</view>
-									</view>
-								</ezyActiveVue>
-							<!-- 其他单元 -->
-							<view v-if="index !=0" class="xx-item-title" :id="`unit-title-${danyuanItem.danyuanId}`">— {{danyuanItem.danyuanName}}
-								{{danyuanItem.danyuanIntro}} —
-							</view>
-						
-
-							<ezyActiveVue class="ezy-list-item-active xx-item-box" v-for="jieItem in danyuanItem.jieList" :key="jieItem.jieId"
-								@aclick="handlePlay(jieItem,'play')">
-								<view class="xx-item-status"
-									:class="jieItem.wanchengFlag == 1 ? 'completed-status' : 'uncompleted-status'">
-								</view>
-								<img :src="jieItem.cover" />
-								<view class="xx-text-box">
-									<view>{{ jieItem.jieName }}</view>
-									<view>{{ jieItem.jieIntro }}</view>
-								</view>
-								<view class="xx-item-btn"></view>
-							</ezyActiveVue>
-
-
-						</view>
-						<view class="xx-item-title">本级别最后一单元啦~</view>
-						<view class="xx-more-btn" @click="moreBtn"></view>
-					</view>
-				
-				<!-- 回到顶部 -->
-				<view class="go-top-btn" v-if="false"></view>
-				
-			</view>
-			<view v-if="!existData">
-
-			</view>
-
-		<danyuanInfoVue ref="dyRef" v-if="isShow" @close="isShow= false"></danyuanInfoVue>
-		<custom-tab-bar :show="true" :current-index="currentTabIndex" />
-		
-	</view>
-</template>
-
-<script>
-	import ezyActiveVue from "@/components/ezyActive/ezyActive.vue";
-	import CustomTabBar from '@/components/custom-tabbar/index.vue';
-	import cacheManager from "@/utils/cacheManager.js";
-	import {
-		shuxueChanpinBanbenInfo,
-		shuxueSave
-	} from "@/api/chanpinneirong.js"
-	import {
-		onLoad,
-		onShow,
-		onHide,
-		onUnload
-	} from "@dcloudio/uni-app"
-	import danyuanInfoVue from '@/pages/xinshuxue/components/danyuanInfo.vue';
-	import {
-		toast
-	} from '../../utils/common';
-  import {updateXuexiProcess} from "./useNeirongShuxue"
-	export default {
-		data() {
-			return {
-				canExitApp: false,
-
-				danyuanList: [],
-				currentUnitName: "",
-				isShow: true,
-				banbenInfo: {},
-				banbenId: '',
-				danyuanId: '',
-				chanpinId: '',
-				dengjiId: '',
-				curProcess: '',
-				currentTabIndex: 1,
-				cacheManagerLocal: null,
-				hasCache: false, // 是否有缓存的标志
-				needRefresh: false, // 是否需要刷新数据的标志
-				existData: true,
-
-				currentVisibleUnit: '', // 当前可见的单元名称
-				observer: null, // IntersectionObserver实例
-				lastToastUnitId: null ,// 上一次toast的单元ID,防止重复触发
-				isFixed:false,
-			}
-		},
-		components: {
-			CustomTabBar,
-			danyuanInfoVue,
-			ezyActiveVue
-		},
-		onShow() {
-			this.currentTabIndex = 1
-			const cacheData = cacheManager.get('xuexi-shuxue');
-
-			if (cacheData) {
-				console.log('使用缓存数据');
-				this.updateFromCache();
-			} else {
-				console.log('重新请求数据');
-				this.hasCache = false;
-				const cacheDataAuth = cacheManager.get('auth');
-				this.initFromOptions(cacheDataAuth);
-			}
-
-			// 数据加载完成后初始化观察器
-			this.$nextTick(() => {
-				setTimeout(() => {
-					this.initObserver();
-				}, 300);
-			});
-		},
-		onHide() {
-			console.log('学习页面隐藏')
-			this.needRefresh = true;
-			// 销毁观察器
-			if (this.observer) {
-				this.observer.disconnect();
-				this.observer = null;
-			}
-		},
-		onUnload() {
-			// 页面卸载时销毁观察器
-			if (this.observer) {
-				this.observer.disconnect();
-				this.observer = null;
-			}
-		},
-		onLoad(options) {
-			uni.hideTabBar()
-		},
-		methods: {
-			moreBtn(){
-				uni.switchTab({
-					url: '/pages/chanpinXuanze/index'
-				})
-			},
-			// 初始化IntersectionObserver
-			initObserver() {
-			  if (this.observer) {
-			    this.observer.disconnect();
-			    this.observer = null;
-			  }
-			  
-			  // 监听每个单元标题的位置
-			  this.observer = uni.createIntersectionObserver(this).relativeToViewport({
-			    top: 0,  // 监听进入屏幕顶部的标题
-			    bottom: 0
-			  });
-			  
-			  this.danyuanList.forEach((item, index) => {
-			    const titleId = `unit-title-${item.danyuanId}`;
-			    this.observer.observe(`#${titleId}`, (res) => {
-			      if (res.intersectionRatio > 0) {
-			        // 标题在屏幕顶部区域内
-			        if (this.lastToastUnitId !== item.danyuanId) {
-			          this.lastToastUnitId = item.danyuanId;
-			          console.log(`显示${item.danyuanName}`);
-			          
-						console.log('item',item);
-						if(item.danyuanName !="第一单元"){
-							this.isFixed = true 
-						}else{
-							this.isFixed = false
-						}
-			          // uni.showToast({
-			          //   title: `${item.danyuanName}`,
-			          //   icon: 'none',
-			          //   duration: 1500
-			          // });
-			        }
-			      }
-			    });
-			  });
-			  
-			  // 页面初始时显示第一个单元
-			  if (this.danyuanList.length > 0) {
-			    this.lastToastUnitId = this.danyuanList[0].danyuanId;
-			  }
-			},
-
-			// 从参数初始化方法
-			initFromOptions(options) {
-				console.log('options', options);
-				this.chanpinId = options.chanpinId;
-				this.danyuanId = options.danyuanId;
-				this.banbenId = options.banbenId;
-				this.dengjiId = options.dengjiId;
-				this.loadDataFromApi();
-			},
-			// 从缓存更新数据方法
-			updateFromCache() {
-				updateXuexiProcess()
-				const cacheData = cacheManager.get('xuexi-shuxue');
-				console.log('cacheData 从缓存更新数据方法', cacheData);
-				if (cacheData) {
-					this.banbenInfo = {
-						...cacheData
-					};
-					this.curProcess = cacheData.curProcess;
-					this.danyuanList = [...(cacheData.danyuanList || [])];
-
-					// 数据更新后重新初始化观察器
-					this.$nextTick(() => {
-						setTimeout(() => {
-							this.initObserver();
-						}, 300);
-					});
-				}
-			},
-
-			shouldUseCache(options, cacheData) {
-				if (options.banbenId && cacheData.banbenId === options.banbenId) {
-					return true;
-				}
-				return false;
-			},
-			// 改成了
-			loadDataFromApi() {
-				this.banbenInfo = {}
-				this.curProcess = ''
-				this.danyuanList = []
-				const req = {
-					banbenId: this.banbenId
-				}
-				shuxueChanpinBanbenInfo(req).then(res => {
-					if (res.code === 0) {
-						this.banbenInfo = res.data;
-						this.curProcess =res.data.curProcess * 100;
-						this.danyuanList = res.data.danyuanList || [];
-
-						if (!this.danyuanList) {
-							this.existData = false
-						}
-						// 保存到缓存(新增了参数保存)
-						const cacheData = {
-							...res.data,
-							banbenId: this.banbenId,
-							chanpinId: this.chanpinId,
-							danyuanId: this.danyuanId,
-							dengjiId: this.dengjiId
-						};
-						cacheManager.set('xuexi-shuxue', cacheData);
-						this.hasCache = true;
-
-						// 更新全局auth信息
-						cacheManager.updateObject('auth', {
-							chanpinId: res.data.chanpinId,
-							banbenId: this.banbenId,
-							danyuanId: res.data.curDanyuanId,
-							dengjiId: res.data.dengjiId
-						});
-						this.danyuanId = res.data.curDanyuanId
-						this.dengjiId = res.data.dengjiId
-						this.chanpinId = res.data.chanpinId
-						// 数据加载完成后初始化观察器
-						this.$nextTick(() => {
-							setTimeout(() => {
-								this.initObserver();
-							}, 300);
-						});
-					}
-				}).catch(res => {
-					cacheManager.remove("xuexi-shuxue");
-					this.hasCache = false;
-					toast("加载失败,请重试");
-				});
-			},
-			getJieAndDanyuan(data, jieId) {
-				// 循环单元
-				for (let danyuan of data.danyuanList) {
-					// 循环节
-					for (let jie of danyuan.jieList) {
-						if (jie.jieId == jieId) {
-							return {
-								danyuan: danyuan,
-								jie: jie
-							}
-						}
-					}
-				}
-				return null;
-			},
-			// 统一保存和跳转方法
-			async saveAndNavigate(jieId, type,da, code) {
-				if (code == 'jixu') { // 追加参数判断 非继续 [临时]
-					if (!this.banbenId || !this.danyuanId) {
-						toast("banbenId或者danyuanId 丢失")
-						return false
-					}
-				}	 
-			
-				let req = {
-					"banbenId": this.banbenId,
-					"danyuanId": da.danyuanId, // 修改当时版本ID为数据内版本Id [临时]
-					"jieId": jieId
-				}
-
-				try {
-					const res = await shuxueSave(req);
-					console.log('res', res);
-
-					if (res.code == 0 && res.data) {
-						let curJieAndDanyuan = this.getJieAndDanyuan(this.banbenInfo, jieId);
-						console.log('curJieAndDanyuan', curJieAndDanyuan);
-
-						if (!curJieAndDanyuan) {
-							toast("未找到课程信息");
-							return false;
-						}
-
-						// 更新缓存
-						const cacheData = cacheManager.get('xuexi-shuxue') || {};
-						cacheData.curDanyuanName = curJieAndDanyuan.danyuan.danyuanName;
-						cacheData.curKechengName = curJieAndDanyuan.jie.jieIntro;
-						cacheData.curJieId = jieId;
-						cacheData.type = curJieAndDanyuan.jie.type;
-						cacheManager.set('xuexi-shuxue', cacheData);
-
-						// 跳转到学习页面
-						if (type == 1) {
-							uni.navigateTo({
-								url: `/pages/xinshuxue/lookShipin?jieId=${jieId}`
-							})
-						} else {
-							uni.navigateTo({
-								url: `/pages/xinshuxue/unitTest?jieId=${jieId}`
-							})
-						}
-					} else {
-						toast("保存位置出错");
-						return false;
-					}
-				} catch (error) {
-					toast("保存失败");
-					return false;
-				}
-			},
-			handlePlay(da, code) {
-				console.log('da', da);
-				let jieId = null;
-				if (code == 'jixu') {
-					if (!da.curJieId) {
-						toast("无课程ID");
-						return;
-					}
-					jieId = da.curJieId;
-				} else {
-					jieId = da.jieId;
-				}
-				// [临时]
-				this.saveAndNavigate(jieId, da.type, da, code);
-			},
-			handleClickDanyuan(item) {
-				// 更新为点击的动态单元Id [临时]
-				if (!item.danyuanId) {
-					toast("this.danyuanId丢失")
-					return false
-				}
-				this.isShow = true;
-				setTimeout(() => {
-					// 更新为点击的动态单元Id [临时]
-					this.$refs.dyRef.handleShow(item.danyuanId)
-				}, 100)
-			},
-			handleBack() {
-				uni.navigateTo({
-					url: `/pages/chanpinXuanze/banben?dengjiId=` + this.dengjiId
-				})
-			},
-		}
-	}
-</script>
-