ExperimentalCourses.vue 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759
  1. <!-- 实验课程 -->
  2. <template>
  3. <div class="programming-content">
  4. <!-- 标题部分 -->
  5. <div class="top-box">
  6. <!-- 返回按钮 -->
  7. <div class="top-left-box">
  8. <div class="top-left-inner-box">
  9. <!-- 左侧返回图标 -->
  10. <div class="left-content-wrapper" @click="goBackIndex">
  11. <el-icon class="left-icon"><ArrowLeftBold /></el-icon>
  12. <span class="left-text">返回</span>
  13. </div>
  14. </div>
  15. </div>
  16. <!-- 标题 -->
  17. <div class="top-center-box" v-if="!showVideo">
  18. <div class="top-center-inner-box">
  19. <span>{{ coursesTitle }}</span>
  20. </div>
  21. </div>
  22. <!-- 课程提示 -->
  23. <div class="top-right-box">
  24. <div class="top-right-inner-box">
  25. <div class="course-info-box">{{ originalCourseTitle }}</div>
  26. </div>
  27. </div>
  28. </div>
  29. <!-- 课程部分 -->
  30. <div class="lower-box" v-if="!showVideo">
  31. <div
  32. class="content-box"
  33. ref="middleBox"
  34. :class="{ 'short-bg': courseList.length <= 3 }"
  35. @mousedown="handleMouseDown"
  36. @mousemove="handleMouseMove"
  37. @mouseup="handleMouseUp"
  38. @mouseleave="handleMouseLeave"
  39. @scroll="handleScroll"
  40. >
  41. <!-- 动态渲染课程内容 -->
  42. <div
  43. v-for="(item, index) in courseList"
  44. :key="item.id"
  45. :class="['slide-item', { 'three-items': courseList.length <= 3 }, { active: activeButton === index }]"
  46. :style="courseList.length <= 3 ? { float: 'left' } : {}"
  47. @click="!isDragging && !hasMoved && handleCourseItemClick(item, index)"
  48. v-memo="[activeButton === index, item.isDisabled]"
  49. >
  50. <div class="box-content">
  51. <img :src="item.image" :alt="item.title" class="box-image" />
  52. <div class="box-text">{{ item.title }}</div>
  53. </div>
  54. <!-- 星星图标组 -->
  55. <div class="star-group">
  56. <div
  57. v-for="i in getStarCount(item.contentType)"
  58. :key="i"
  59. class="star-icon"
  60. :style="{
  61. backgroundImage: `url(${i <= item.progress ? star01Image : star02Image})`,
  62. }"
  63. ></div>
  64. </div>
  65. </div>
  66. </div>
  67. </div>
  68. <!-- 底部切换按钮 -->
  69. <div v-if="!showVideo && courseList.length > 3" class="bottom-box">
  70. <!-- 左切换按钮 -->
  71. <div class="carousel-btn prev-btn" @click="prevExperimentalCourses" :class="{ 'disabled-btn': activeButton === 0 }" :disabled="activeButton === 0">
  72. <img :src="leftbtn" alt="左按钮" class="btn-image" :class="{ 'disabled-icon': activeButton === 0 }" />
  73. </div>
  74. <!-- 进度条滑块 -->
  75. <div class="line-container" v-if="courseList.length > 0">
  76. <el-slider v-model="activeButton" :max="courseList.length - 1" :step="1" :show-tooltip="false" />
  77. </div>
  78. <!-- 右切换按钮 -->
  79. <div class="carousel-btn next-btn" @click="nextExperimentalCourses" :class="{ 'disabled-btn': activeButton >= courseList.length - 1 }" :disabled="activeButton >= courseList.length - 1">
  80. <img :src="rightbtn" alt="右按钮" class="btn-image" :class="{ 'disabled-icon': activeButton >= courseList.length - 1 }" />
  81. </div>
  82. </div>
  83. <!-- 视频和编程界面 -->
  84. <ExperimentalInterface v-if="showVideo" :courseData="selectedCourseData" :courseList="resData" @closeVideo="showVideo = false" />
  85. </div>
  86. </template>
  87. <script setup>
  88. // 返回图标
  89. import { ArrowLeftBold } from '@element-plus/icons-vue';
  90. import {ref, onMounted,computed , onUnmounted, watch, nextTick} from 'vue';
  91. // 导入路由
  92. import { useRouter } from 'vue-router';
  93. // 导入按钮图片
  94. import leftbtn from '@/assets/programming/leftbtn.png'
  95. import rightbtn from '@/assets/programming/rightbtn.png'
  96. import ExperimentalInterface from './ExperimentalInterface.vue'
  97. // 星星图片
  98. import star02Image from '@/assets/programming/star02.png'
  99. import star01Image from '@/assets/programming/star01.png'
  100. import trackLongImage from '@/assets/programming/track-long.png'
  101. import trackShortImage from '@/assets/programming/track.png'
  102. import {Message} from "@/utils/message/Message.js";
  103. // 根据ID获取实验室课程列表接口
  104. import { getCourseByTypeId } from '@/api/laboratory/index.js'
  105. import {DICT_TYPE} from "@/utils/dictUtils.js";
  106. import {scrollToCenter} from "@/utils/pageCss/scrollToCenter.js";
  107. // 常量配置
  108. const CONSTANTS = {
  109. SCROLL_SPEED: 2,
  110. ANIMATION_DURATION: '0.3s',
  111. // 根据acLabel获取图片
  112. IMAGE_MAP: {
  113. '1': "",
  114. '2': "",
  115. '3': ""
  116. }
  117. }
  118. // 获取路由实例
  119. const router = useRouter()
  120. // 页面标题
  121. const coursesTitle = ref('')
  122. // 保存原始的课程ID和标题
  123. const originalCourseId = ref('')
  124. const originalCourseTitle = ref('')
  125. const isDisabled = ref(false) // 课程是否只读
  126. // 类型ID(从ExperimentType页面传递过来)
  127. const typeId = ref('')
  128. // 控制视频界面显示
  129. const showVideo = ref(false)
  130. // 动态课程项数据
  131. const courseList = ref([])
  132. // 当前选中的课程数据
  133. const selectedCourseData = ref(null)
  134. // 保存原始API返回的数据
  135. const resData = ref([])
  136. // 选中的按钮索引存储键名
  137. const blocklyActiveButton = ref("aiCourseSectionActiveButton")
  138. // 当前激活的按钮索引
  139. const activeButton = ref(Number(localStorage.getItem(blocklyActiveButton.value)) || 0)
  140. // 根据课程项数量动态计算背景图
  141. const backgroundImage = computed(() => {
  142. return courseList.value.length <= 3
  143. ? `url(${trackShortImage})`
  144. : `url(${trackLongImage})`;
  145. })
  146. // 拖动相关变量
  147. const isDragging = ref(false)
  148. const startX = ref(0)
  149. const scrollLeft = ref(0)
  150. const hasMoved = ref(false) // 标记是否发生了移动
  151. // 获取contentBox元素的引用
  152. const middleBox = ref(null)
  153. // 获取星星数量,默认返回1个星星
  154. const getStarCount = () => 1
  155. // 上一节课程
  156. const prevExperimentalCourses = () => {
  157. if (activeButton.value > 0) {
  158. activeButton.value -= 1;
  159. }
  160. }
  161. // 下一节课程
  162. const nextExperimentalCourses = () => {
  163. if (activeButton.value < courseList.value.length - 1) {
  164. activeButton.value += 1;
  165. }
  166. }
  167. // 鼠标按下事件处理函数
  168. const handleMouseDown = (e) => {
  169. // 拖拽状态为true
  170. isDragging.value = true
  171. // 初始化移动状态为false
  172. hasMoved.value = false
  173. // 计算鼠标在容器内的相对X坐标的位置 e.pageX鼠标相对于整个页面的X坐标
  174. if (middleBox.value) {
  175. startX.value = e.pageX - middleBox.value.offsetLeft
  176. scrollLeft.value = middleBox.value.scrollLeft
  177. }
  178. }
  179. // 鼠标移动事件处理函数
  180. const handleMouseMove = (e) => {
  181. if (!isDragging.value || !middleBox.value) return
  182. // 阻止默认行为,防止文本选择等与拖拽冲突
  183. e.preventDefault()
  184. // 标记已发生移动
  185. hasMoved.value = true
  186. // 优化滚动
  187. requestAnimationFrame(() => {
  188. // 计算新的滚动位置
  189. const x = e.pageX - middleBox.value.offsetLeft
  190. const walk = (x - startX.value) * CONSTANTS.SCROLL_SPEED // 滚动速度
  191. middleBox.value.scrollLeft = scrollLeft.value - walk
  192. })
  193. }
  194. // 鼠标松开事件处理函数
  195. const handleMouseUp = () => {
  196. isDragging.value = false;
  197. hasMoved.value = false;
  198. };
  199. // 鼠标离开事件处理函数
  200. const handleMouseLeave = () => {
  201. isDragging.value = false;
  202. hasMoved.value = false;
  203. };
  204. // 滚动事件处理函数
  205. const handleScroll = () => {}
  206. // 监听activeButton变化,自动滚动到中间位置
  207. watch(activeButton, () => {
  208. middleBoxWidth()
  209. });
  210. // 中间卡片居中显示和放大效果
  211. const middleBoxWidth = ()=> {
  212. scrollToCenter(middleBox.value, activeButton.value, {
  213. selector: '.slide-item'
  214. });
  215. }
  216. // 获取课程数据函数优化
  217. const fetchCourseData = async () => {
  218. if (typeId.value) {
  219. try {
  220. const res = await getCourseByTypeId(typeId.value)
  221. if (res && res.data && Array.isArray(res.data)) {
  222. // 使用局部变量处理数据,减少响应式对象的频繁修改
  223. const newResData = [...res.data];
  224. newResData.forEach(item => {
  225. item.isDisabled = isDisabled.value
  226. });
  227. // 批量创建课程项
  228. const newCourseItems = newResData.map((item, index) => {
  229. const image = CONSTANTS.IMAGE_MAP[item.acLabel]; // 根据acLabel获取图片
  230. return {
  231. id: item.id,
  232. title: item.acName,
  233. image: image,
  234. contentType: item.acContentType,
  235. progress: item.progress, // 进度
  236. isDisabled: isDisabled.value // 保存acLabel用于图片映射
  237. };
  238. });
  239. // 一次性更新响应式数据,减少DOM更新次数
  240. resData.value = newResData;
  241. courseList.value = newCourseItems;
  242. // 数据加载完成后,等待下一个DOM更新周期,然后调用middleBoxWidth
  243. await nextTick(() => {
  244. middleBoxWidth();
  245. });
  246. }
  247. } catch (error) {
  248. console.error('Failed to fetch course data:', error)
  249. Message().notifyError('获取课程数据失败,请重试', true)
  250. }
  251. }
  252. }
  253. // 组件挂载时获取路由参数设置标题
  254. onMounted(() => {
  255. // 从history.state中获取参数
  256. const courseTitle = history.state?.typeTitle;
  257. if (courseTitle) {
  258. coursesTitle.value = courseTitle;
  259. }
  260. // 一次性设置多个响应式数据
  261. typeId.value = history.state?.typeId;
  262. originalCourseId.value = history.state?.originalThemeId;
  263. originalCourseTitle.value = history.state?.originalThemeTitle;
  264. isDisabled.value = Boolean(history.state?.isDisabled);
  265. // 调用函数获取课程列表
  266. fetchCourseData();
  267. //课程小节字典
  268. let menuDictStr = localStorage.getItem(DICT_TYPE.BLOCKLY_COURSE_LABEL);
  269. let menuDictJson = menuDictStr ? JSON.parse(menuDictStr) : [];
  270. CONSTANTS.IMAGE_MAP = menuDictJson.reduce((acc, item) => {
  271. acc[item.value] = item.remark;
  272. return acc;
  273. }, {});
  274. });
  275. // 组件卸载时清理
  276. onUnmounted(() => {
  277. // 清理拖拽状态
  278. isDragging.value = false;
  279. hasMoved.value = false;
  280. });
  281. // 处理课程项点击事件
  282. const handleCourseItemClick = (item, index) => {
  283. // 如果在拖动过程中或移动过,则不触发点击事件
  284. if (hasMoved.value || isDragging.value) return
  285. activeButton.value = index
  286. localStorage.setItem(blocklyActiveButton.value, activeButton.value)
  287. // 如果是只读模式,不允许点击
  288. if (isDisabled.value) {
  289. Message().notifyWarning('您的账号并未开放此课程!', true)
  290. return;
  291. }
  292. // 直接使用item中的信息,避免再次查找
  293. showVideo.value = true
  294. selectedCourseData.value = {
  295. ...resData.value.find(course => course.id === item.id),
  296. ztId: originalCourseId.value,
  297. isDisabled: isDisabled.value
  298. }
  299. }
  300. // 返回编程课列表
  301. const goBackIndex = () => {
  302. localStorage.removeItem(blocklyActiveButton.value)
  303. // 隐藏视频和游戏界面
  304. showVideo.value = false
  305. // 返回时携带原始的课程参数
  306. router.push({
  307. path: '/experiment-type',
  308. state: {
  309. themeId: originalCourseId.value,
  310. themeTitle: originalCourseTitle.value
  311. }
  312. })
  313. }
  314. // 监听showVideo状态变化,当从true变为false时重新获取课程数据
  315. watch(showVideo, (newValue, oldValue) => {
  316. // 使用nextTick确保DOM更新完成后再执行
  317. if (oldValue === true && newValue === false) {
  318. setTimeout(() => {
  319. fetchCourseData();
  320. }, 0)
  321. }
  322. })
  323. </script>
  324. <style scoped lang="scss">
  325. @use 'sass:math';
  326. // 定义rpx转换函数
  327. @function rpx($px) {
  328. @return math.div($px, 750) * 100vw;
  329. }
  330. .programming-content{
  331. position: fixed;
  332. top: 0;
  333. left: 0;
  334. right: 0;
  335. bottom: 0;
  336. background-image: url('@/assets/laboratory/laboratory-bg.png');
  337. background-size: cover;
  338. background-position: center;
  339. background-repeat: no-repeat;
  340. display: flex;
  341. flex-direction: column;
  342. user-select: none; /* 禁止文本选择 */
  343. }
  344. .top-box {
  345. height: 20%;
  346. display: flex;
  347. }
  348. .top-left-box,
  349. .top-right-box {
  350. flex: 1;
  351. height: 50%;
  352. display: flex;
  353. align-items: center;
  354. justify-content: center;
  355. }
  356. .top-center-box {
  357. flex: 2;
  358. display: flex;
  359. align-items: center;
  360. justify-content: center;
  361. }
  362. .top-center-inner-box{
  363. width: 100%;
  364. height: 100%;
  365. background-image: url('@/assets/programming/list_title.png');
  366. background-size: 70%;
  367. background-repeat: no-repeat;
  368. background-position: center center; /* 背景图垂直水平居中 */
  369. display: flex;
  370. align-items: center; /* 垂直居中 */
  371. justify-content: center; /* 水平居中 */
  372. cursor: pointer;
  373. position: relative; /* 相对定位 */
  374. span{
  375. font-size: rpx(16);
  376. color: white;
  377. position: relative;
  378. z-index: 1; /* 确保文字在背景图上方 */
  379. display: flex;
  380. align-items: center;
  381. justify-content: center;
  382. padding-bottom: rpx(5);
  383. }
  384. }
  385. .top-left-inner-box{
  386. display: flex;
  387. align-items: center; /* 垂直居中对齐 */
  388. width: 100%;
  389. height: 100%;
  390. .left-content-wrapper{
  391. display: flex;
  392. align-items: center; /* 保持内部元素垂直居中 */
  393. }
  394. .left-icon{
  395. font-size: rpx(14);
  396. color: white;
  397. padding-left: rpx(20);
  398. cursor: pointer;
  399. }
  400. .left-text{
  401. font-size: rpx(14);
  402. color: white;
  403. padding-left: rpx(10);
  404. cursor: pointer;
  405. }
  406. }
  407. .top-right-inner-box {
  408. width: 100%;
  409. height: 100%;
  410. display: flex;
  411. justify-content: flex-end;
  412. align-items: center;
  413. padding-right: rpx(20);
  414. .course-info-box {
  415. width: rpx(60);
  416. height: rpx(20);
  417. background-color: rgb(255, 255, 255);
  418. border-radius: rpx(15);
  419. display: flex;
  420. align-items: center;
  421. justify-content: center;
  422. color: #45300b;
  423. font-size: rpx(10);
  424. cursor: pointer;
  425. }
  426. }
  427. .lower-box {
  428. height: 75%;
  429. overflow: hidden; /* 溢出隐藏 */
  430. position: relative;
  431. display: flex;
  432. align-items: center;
  433. justify-content: center;
  434. margin-top: rpx(-25);
  435. }
  436. .content-box {
  437. width: 100%;
  438. min-width: rpx(700); /* 最小宽度 */
  439. height: 100%;
  440. overflow-x: auto; /* 水平滚动条 */
  441. overflow-y: hidden; /* 取消上下滚动 */
  442. white-space: nowrap; /* 防止元素换行 */
  443. scroll-behavior: smooth; /* 平滑滚动效果 */
  444. -webkit-overflow-scrolling: touch; /* 触摸设备支持 */
  445. position: relative;
  446. flex: 1;
  447. cursor: grab; /* 显示可抓取图标 */
  448. z-index: 2;
  449. padding: 0 rpx(0);
  450. /* 硬件加速 */
  451. transform: translateZ(0);
  452. backface-visibility: hidden;
  453. perspective: 1000px;
  454. /* 设置背景图 */
  455. background-image: v-bind(backgroundImage);
  456. background-size: rpx(1360) rpx(400); /* 固定宽度,背景图大小一致 */
  457. background-position: left calc(-1 * rpx(80));
  458. background-repeat: repeat-x;
  459. background-attachment: local; /* 背景图跟内容一起滚动 */
  460. }
  461. .short-bg {
  462. background-size: rpx(790) rpx(300);
  463. background-position: center calc(-1 * rpx(10));
  464. }
  465. /* 鼠标按下时的光标样式 */
  466. .content-box:active {
  467. cursor: grabbing;
  468. }
  469. /* 隐藏滚动条但保持滚动功能 */
  470. .content-box::-webkit-scrollbar {
  471. display: none;
  472. }
  473. .content-box {
  474. -ms-overflow-style: none;
  475. scrollbar-width: none;
  476. }
  477. .slide-item {
  478. width: rpx(130); /* 设置固定宽度 */
  479. height: rpx(110); /* 高度设置 */
  480. margin-top: rpx(80);
  481. margin-right: rpx(85);
  482. margin-left: rpx(10);
  483. border-radius: rpx(35);
  484. background-color: rgba(255, 255, 255);
  485. display: inline-flex;
  486. align-items: center;
  487. justify-content: center;
  488. transition: transform 0.3s ease;
  489. cursor: pointer;
  490. z-index: 2; /* 内容在背景图上方 */
  491. vertical-align: middle;
  492. position: relative;
  493. /* 硬件加速 */
  494. transform: translateZ(0);
  495. backface-visibility: hidden;
  496. perspective: 1000px;
  497. }
  498. /* 当只有3个课程项时调整间距 */
  499. .slide-item.three-items {
  500. margin-right: rpx(70);
  501. margin-left: rpx(50);
  502. }
  503. /* 奇数项在上层 */
  504. .slide-item:nth-child(odd) {
  505. transform: translateY(-50%) translateZ(0);
  506. }
  507. /* 偶数项在下层 */
  508. .slide-item:nth-child(even) {
  509. transform: translateY(50%) translateZ(0);
  510. }
  511. /* 鼠标悬停放大效果 */
  512. .slide-item:nth-child(odd):hover {
  513. transform: translateY(-50%) scale(1.05); /* 减小放大比例 */
  514. z-index: 10;
  515. transition: transform 0.2s ease-out; /* 更短更平滑的过渡 */
  516. }
  517. .slide-item:nth-child(even):hover {
  518. transform: translateY(50%) scale(1.05); /* 减小放大比例 */
  519. z-index: 10;
  520. transition: transform 0.2s ease-out; /* 更短更平滑的过渡 */
  521. }
  522. /* 默认选中第一个元素的样式 */
  523. /* 奇数项选中样式 */
  524. .slide-item:nth-child(odd).active {
  525. transform: translateY(-50%) scale(1.05); /* 减小放大比例 */
  526. z-index: 10;
  527. box-shadow: 0 rpx(5) rpx(10) rgba(0, 0, 0, 0.2);
  528. }
  529. /* 偶数项选中样式 */
  530. .slide-item:nth-child(even).active {
  531. transform: translateY(50%) scale(1.05); /* 减小放大比例 */
  532. z-index: 10;
  533. box-shadow: 0 rpx(5) rpx(10) rgba(0, 0, 0, 0.2);
  534. }
  535. /* 内容样式 */
  536. .box-content {
  537. display: flex;
  538. flex-direction: column;
  539. align-items: center;
  540. justify-content: center;
  541. height: 100%;
  542. width: 100%;
  543. }
  544. /* 鼠标按下时的光标样式 */
  545. .slide-item:active {
  546. cursor: grabbing;
  547. }
  548. .box-image {
  549. width: 100%;
  550. height: 90%;
  551. object-fit: contain;
  552. }
  553. .box-text {
  554. width: 100%;
  555. height: 20%;
  556. line-height: 20%;
  557. font-size: rpx(13);
  558. color: #333;
  559. text-align: center;
  560. }
  561. /* 星星图标样式 */
  562. .star-group {
  563. position: absolute;
  564. top: 100%;
  565. width: 100%;
  566. display: flex;
  567. justify-content: center;
  568. align-items: center;
  569. z-index: 3;
  570. }
  571. .star-icon {
  572. position: relative;
  573. width: rpx(23);
  574. height: rpx(23);
  575. background-size: contain;
  576. background-repeat: no-repeat;
  577. background-position: center;
  578. margin: 0 rpx(0);
  579. }
  580. /* 轮播图按钮样式 */
  581. .carousel-btn {
  582. width: rpx(40);
  583. height: rpx(40);
  584. border-radius: 50%;
  585. display: flex;
  586. align-items: center;
  587. justify-content: center;
  588. cursor: pointer;
  589. z-index: 10;
  590. transition: all 0.3s ease;
  591. }
  592. .carousel-btn:hover:not(.disabled-btn) {
  593. transform: scale(1.1);
  594. }
  595. .carousel-btn:disabled,
  596. .disabled-btn {
  597. cursor: not-allowed;
  598. box-shadow: none;
  599. }
  600. .disabled-icon {
  601. opacity: 0.5;
  602. }
  603. .prev-btn {
  604. margin-right: rpx(5);
  605. }
  606. .next-btn {
  607. margin-left: rpx(5);
  608. }
  609. .btn-icon {
  610. font-size: rpx(15);
  611. color: #333;
  612. }
  613. /* 底部切换按钮样式 */
  614. .bottom-box {
  615. height: 15%;
  616. display: flex;
  617. align-items: center;
  618. justify-content: center;
  619. }
  620. .bottom-box .carousel-btn {
  621. position: relative;
  622. top: 0;
  623. transform: none;
  624. width: rpx(40);
  625. height: rpx(40);
  626. background-color: transparent;
  627. box-shadow: none;
  628. }
  629. .bottom-box .carousel-btn:hover:not(.disabled-btn) {
  630. transform: none;
  631. }
  632. .line-container {
  633. width: 70%; /* 滑块宽度 */
  634. position: relative;
  635. display: flex;
  636. align-items: center;
  637. justify-content: space-between;
  638. /* el-slider滑块样式 */
  639. :deep(.el-slider__runway) {
  640. height: rpx(10); /* 滑块轨道高度 */
  641. background-color: rgba(228, 227, 254,0.3); /* 轨道背景色 */
  642. border-radius: rpx(10);
  643. }
  644. :deep(.el-slider__bar) {
  645. height: rpx(10); /* 滑块激活部分高度 */
  646. background-color: #2598c2; /* 激活部分颜色为蓝色 */
  647. border-radius: rpx(10);
  648. }
  649. :deep(.el-slider__button) {
  650. width: rpx(35);
  651. height: rpx(35);
  652. margin-top: rpx(-5);
  653. margin-left: rpx(-10);
  654. border: none; /* 移除边框 */
  655. background-image: url('@/assets/programming/xiaozhi.png');
  656. background-size: 100%; /* 背景图片完全覆盖按钮 */
  657. background-position: center; /* 背景图片居中 */
  658. background-repeat: no-repeat; /* 背景图片不重复 */
  659. background-color: transparent; /* 透明背景 */
  660. &:hover {
  661. transform: scale(1.1); /* 悬停时放大效果 */
  662. }
  663. &.hover,
  664. &:active {
  665. transform: scale(1.1); /* 激活时放大效果 */
  666. box-shadow: none; /* 移除阴影效果 */
  667. }
  668. }
  669. :deep(.el-slider__stop) {
  670. width: rpx(10); /* 停止点大小 */
  671. height: rpx(10); /* 停止点大小 */
  672. background-color: white; /* 停止点颜色 */
  673. opacity: 0.2;
  674. }
  675. }
  676. .btn-image {
  677. width: rpx(40);
  678. height: rpx(40);
  679. object-fit: contain;
  680. }
  681. </style>