ExperimentalCourses.vue 19 KB

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