|
|
@@ -31,7 +31,7 @@
|
|
|
<div class="lower-box" v-if="!showVideo">
|
|
|
<div
|
|
|
class="content-box"
|
|
|
- ref="contentBox"
|
|
|
+ ref="middleBox"
|
|
|
@mousedown="handleMouseDown"
|
|
|
@mousemove="handleMouseMove"
|
|
|
@mouseup="handleMouseUp"
|
|
|
@@ -44,7 +44,7 @@
|
|
|
:key="item.id"
|
|
|
:class="['slide-item', { active: activeButton === index }]"
|
|
|
:style="courseList.length <= 3 ? { float: 'left' } : {}"
|
|
|
- @click="!isDragging && !hasMoved && handleCourseItemClick(item)"
|
|
|
+ @click="!isDragging && !hasMoved && handleCourseItemClick(item, index)"
|
|
|
v-memo="[activeButton === index, item.isDisabled]"
|
|
|
>
|
|
|
<div class="box-content">
|
|
|
@@ -91,7 +91,7 @@
|
|
|
<script setup>
|
|
|
// 返回图标
|
|
|
import { ArrowLeftBold } from '@element-plus/icons-vue';
|
|
|
-import { ref, onMounted, onUnmounted, watch } from 'vue';
|
|
|
+import {ref, onMounted, onUnmounted, watch, nextTick} from 'vue';
|
|
|
// 导入路由
|
|
|
import { useRouter } from 'vue-router';
|
|
|
// 导入按钮图片
|
|
|
@@ -111,7 +111,6 @@ import {DICT_TYPE} from "@/utils/dictUtils.js";
|
|
|
const CONSTANTS = {
|
|
|
SCROLL_SPEED: 2,
|
|
|
ANIMATION_DURATION: '0.3s',
|
|
|
- DEFAULT_ACTIVE_INDEX: 0,
|
|
|
// 根据acLabel获取图片
|
|
|
IMAGE_MAP: {
|
|
|
'1': "",
|
|
|
@@ -138,15 +137,17 @@ const courseList = ref([])
|
|
|
const selectedCourseData = ref(null)
|
|
|
// 保存原始API返回的数据
|
|
|
const resData = ref([])
|
|
|
+// 选中的按钮索引存储键名
|
|
|
+const blocklyActiveButton = ref("aiCourseSectionActiveButton")
|
|
|
// 当前激活的按钮索引
|
|
|
-const activeButton = ref(CONSTANTS.DEFAULT_ACTIVE_INDEX)
|
|
|
+const activeButton = ref(Number(localStorage.getItem(blocklyActiveButton.value)) || 0)
|
|
|
// 拖动相关变量
|
|
|
const isDragging = ref(false)
|
|
|
const startX = ref(0)
|
|
|
const scrollLeft = ref(0)
|
|
|
const hasMoved = ref(false) // 标记是否发生了移动
|
|
|
// 获取contentBox元素的引用
|
|
|
-const contentBox = ref(null)
|
|
|
+const middleBox = ref(null)
|
|
|
|
|
|
// 根据内容类型获取星星数量
|
|
|
const getStarCount = (contentType) => {
|
|
|
@@ -179,21 +180,21 @@ const handleMouseDown = (e) => {
|
|
|
// 初始化移动状态为false
|
|
|
hasMoved.value = false
|
|
|
// 计算鼠标在容器内的相对X坐标的位置 e.pageX鼠标相对于整个页面的X坐标
|
|
|
- if (contentBox.value) {
|
|
|
- startX.value = e.pageX - contentBox.value.offsetLeft
|
|
|
- scrollLeft.value = contentBox.value.scrollLeft
|
|
|
+ if (middleBox.value) {
|
|
|
+ startX.value = e.pageX - middleBox.value.offsetLeft
|
|
|
+ scrollLeft.value = middleBox.value.scrollLeft
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 鼠标移动事件处理函数
|
|
|
const handleMouseMove = (e) => {
|
|
|
- if (!isDragging.value || !contentBox.value) return
|
|
|
+ if (!isDragging.value || !middleBox.value) return
|
|
|
// 标记已发生移动
|
|
|
hasMoved.value = true
|
|
|
// 计算新的滚动位置
|
|
|
- const x = e.pageX - contentBox.value.offsetLeft
|
|
|
+ const x = e.pageX - middleBox.value.offsetLeft
|
|
|
const walk = (x - startX.value) * CONSTANTS.SCROLL_SPEED // 滚动速度
|
|
|
- contentBox.value.scrollLeft = scrollLeft.value - walk
|
|
|
+ middleBox.value.scrollLeft = scrollLeft.value - walk
|
|
|
}
|
|
|
|
|
|
// 鼠标松开事件处理函数
|
|
|
@@ -212,29 +213,31 @@ const handleMouseLeave = () => {
|
|
|
const handleScroll = () => {}
|
|
|
|
|
|
// 监听activeButton变化,自动滚动到中间位置
|
|
|
-watch(activeButton, (newIndex) => {
|
|
|
- if (contentBox.value && newIndex !== -1) {
|
|
|
- // 使用requestAnimationFrame优化滚动
|
|
|
- requestAnimationFrame(() => {
|
|
|
- // 找到对应的课程卡片元素
|
|
|
- const courseElement = contentBox.value.querySelector(`.slide-item:nth-child(${newIndex + 1})`);
|
|
|
- if (courseElement) {
|
|
|
- // 计算滚动位置,选中的卡片居中
|
|
|
- const containerWidth = contentBox.value.clientWidth;
|
|
|
- const elementLeft = courseElement.offsetLeft;
|
|
|
- const elementWidth = courseElement.offsetWidth;
|
|
|
- // 计算居中位置
|
|
|
- const scrollPosition = elementLeft - (containerWidth / 2) + (elementWidth / 2);
|
|
|
- // 平滑滚动到计算的位置
|
|
|
- contentBox.value.scrollTo({
|
|
|
- left: scrollPosition,
|
|
|
- behavior: 'smooth'
|
|
|
- });
|
|
|
- }
|
|
|
- });
|
|
|
- }
|
|
|
+watch(activeButton, () => {
|
|
|
+ middleBoxWidth()
|
|
|
});
|
|
|
|
|
|
+// 中间卡片居中显示和放大效果
|
|
|
+const middleBoxWidth = ()=> {
|
|
|
+ if (middleBox.value) {
|
|
|
+ // 找到对应的课程卡片元素
|
|
|
+ const courseElement = middleBox.value.querySelector(`.middle-inner-box:nth-child(${activeButton.value + 1})`);
|
|
|
+ if (courseElement) {
|
|
|
+ // 计算滚动位置,选中的卡片居中
|
|
|
+ const containerWidth = middleBox.value.clientWidth;
|
|
|
+ const elementLeft = courseElement.offsetLeft;
|
|
|
+ const elementWidth = courseElement.offsetWidth;
|
|
|
+ // 计算居中位置
|
|
|
+ const scrollPosition = elementLeft - (containerWidth / 2) + (elementWidth / 2);
|
|
|
+ // 平滑滚动到计算的位置
|
|
|
+ middleBox.value.scrollTo({
|
|
|
+ left: scrollPosition,
|
|
|
+ behavior: 'smooth'
|
|
|
+ });
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
// 获取课程数据函数优化
|
|
|
const fetchCourseData = async () => {
|
|
|
if (typeId.value) {
|
|
|
@@ -263,6 +266,12 @@ const fetchCourseData = async () => {
|
|
|
// 一次性更新响应式数据,减少DOM更新次数
|
|
|
resData.value = newResData;
|
|
|
courseList.value = newCourseItems;
|
|
|
+
|
|
|
+ // 数据加载完成后,等待下一个DOM更新周期,然后调用middleBoxWidth
|
|
|
+ nextTick(() => {
|
|
|
+ middleBoxWidth();
|
|
|
+ });
|
|
|
+
|
|
|
}
|
|
|
} catch (error) {
|
|
|
console.error('Failed to fetch course data:', error)
|
|
|
@@ -306,7 +315,12 @@ onUnmounted(() => {
|
|
|
});
|
|
|
|
|
|
// 处理课程项点击事件
|
|
|
-const handleCourseItemClick = (item) => {
|
|
|
+const handleCourseItemClick = (item, index) => {
|
|
|
+
|
|
|
+ activeButton.value = index
|
|
|
+ localStorage.setItem(blocklyActiveButton.value, activeButton.value)
|
|
|
+
|
|
|
+
|
|
|
// 如果在拖动过程中或移动过,则不触发点击事件
|
|
|
if (hasMoved.value || isDragging.value) return
|
|
|
|
|
|
@@ -329,6 +343,8 @@ const handleCourseItemClick = (item) => {
|
|
|
|
|
|
// 返回编程课列表
|
|
|
const goBackIndex = () => {
|
|
|
+ localStorage.removeItem(blocklyActiveButton.value)
|
|
|
+
|
|
|
// 隐藏视频和游戏界面
|
|
|
showVideo.value = false
|
|
|
// 返回时携带原始的课程参数
|