SpecialTaskMonitorPanel.vue 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  1. <template>
  2. <div class="special-task-panel">
  3. <swiper class="my-swiper" :options="swiperOptions" ref="mySwiper">
  4. <swiper-slide v-for="(item, index) in combinedList" :key="index" class="custom-slide">
  5. <div class="top-monitor">
  6. <VideoMonitorBox
  7. v-if="item.video"
  8. :videoUrl="item.video.url"
  9. />
  10. <div v-else class="empty-placeholder"></div>
  11. </div>
  12. <div class="bottom-card">
  13. <IntersectionControlCard
  14. v-if="item.card"
  15. :data="item.card"
  16. @action-click="handleCardAction"
  17. />
  18. </div>
  19. </swiper-slide>
  20. </swiper>
  21. <div class="nav-btn left-btn swiper-button-prev"></div>
  22. <div class="nav-btn right-btn swiper-button-next"></div>
  23. </div>
  24. </template>
  25. <script>
  26. import { Swiper, SwiperSlide } from 'vue-awesome-swiper';
  27. import 'swiper/css/swiper.css';
  28. import VideoMonitorBox from './VideoMonitorBox.vue';
  29. import IntersectionControlCard from './IntersectionControlCard.vue';
  30. export default {
  31. name: 'SpecialTaskMonitorPanel',
  32. components: { Swiper, SwiperSlide, VideoMonitorBox, IntersectionControlCard },
  33. props: {
  34. panelData: { type: Object, required: true }
  35. },
  36. data() {
  37. return {
  38. swiperOptions: {
  39. slidesPerView: 3, // 显示 3 列
  40. slidesPerGroup: 3, // 每次滑动 3 列
  41. spaceBetween: 20, // 列间距 20px
  42. simulateTouch: true, // 允许鼠标拖拽
  43. speed: 600, // 滑动动画 600ms,更加优雅
  44. // 开启内部监听,当 SmartDialog 缩放导致容器尺寸变化时,Swiper 自动重新计算
  45. observer: true,
  46. observeParents: true,
  47. observeSlideChildren: true,
  48. navigation: {
  49. nextEl: '.swiper-button-next',
  50. prevEl: '.swiper-button-prev'
  51. }
  52. }
  53. };
  54. },
  55. computed: {
  56. // 将独立的两组数据合并为 "列数据",方便 swiper 渲染
  57. combinedList() {
  58. if (!this.panelData) return [];
  59. const videos = this.panelData.videos || [];
  60. const cards = this.panelData.intersections || [];
  61. const maxLen = Math.max(videos.length, cards.length);
  62. const list = [];
  63. for (let i = 0; i < maxLen; i++) {
  64. list.push({
  65. video: videos[i] || null,
  66. card: cards[i] || null
  67. });
  68. }
  69. return list;
  70. }
  71. },
  72. methods: {
  73. // 处理卡片按钮点击事件
  74. handleCardAction(cardData) {
  75. if (cardData.btnText === '立即执行') {
  76. // 1. 修改文案和按钮样式状态(可选:根据你的业务,让按钮退回普通样式)
  77. cardData.btnText = '立即解锁';
  78. cardData.btnType = 'normal';
  79. // 2. 自动切换到下一个
  80. this.slideNext();
  81. // 3. (可选) 这里可以顺便发送请求给后端,告知该路口已执行
  82. console.log(`路口 [${cardData.name}] 已执行特勤方案`);
  83. } else if (cardData.btnText === '立即解锁') {
  84. // 如果再次点击(已经是解锁状态),可以重置回执行状态,按需保留
  85. cardData.btnText = '立即执行';
  86. cardData.btnType = 'primary';
  87. console.log(`路口 [${cardData.name}] 已解除特勤方案`);
  88. }
  89. },
  90. // 调用 Swiper 实例滑动到下一页
  91. slideNext() {
  92. // 兼容不同版本的 vue-awesome-swiper 实例获取方式
  93. const swiperInstance = this.$refs.mySwiper.$swiper || this.$refs.mySwiper.swiper;
  94. if (swiperInstance) {
  95. swiperInstance.slideNext();
  96. }
  97. }
  98. }
  99. }
  100. </script>
  101. <style scoped>
  102. .special-task-panel {
  103. position: relative;
  104. width: 100%;
  105. height: 100%;
  106. padding: 20px 60px; /* 给左右箭头留出足够空间 */
  107. box-sizing: border-box;
  108. }
  109. .my-swiper {
  110. width: 100%;
  111. height: 100%;
  112. padding: 10px 5px; /* 防止子元素的阴影被裁切 */
  113. }
  114. .custom-slide {
  115. display: flex;
  116. flex-direction: column;
  117. height: 100%;
  118. box-sizing: border-box;
  119. }
  120. /* 分配上下比例 */
  121. .top-monitor {
  122. height: 40%; /* 视频区域占 40% */
  123. min-height: 120px; /* 防止缩太小导致完全看不见 */
  124. width: 100%;
  125. }
  126. .bottom-card {
  127. height: calc(60% - 15px); /* 卡片占 60% 减去间距 */
  128. margin-top: 15px;
  129. width: 100%;
  130. min-height: 180px;
  131. }
  132. .empty-placeholder {
  133. height: 100%;
  134. width: 100%;
  135. background: rgba(255,255,255,0.02);
  136. border-radius: 6px;
  137. border: 1px dashed rgba(255,255,255,0.1);
  138. box-sizing: border-box;
  139. }
  140. /* ================= 自定义左右箭头样式 ================= */
  141. .swiper-button-prev:after, .swiper-button-next:after { display: none; } /* 隐藏默认箭头 */
  142. .nav-btn {
  143. position: absolute;
  144. top: 50%;
  145. margin-top: -22px; /* 替代 translateY 居中,防止和 rotate 冲突 */
  146. width: 44px;
  147. height: 44px;
  148. background-color: transparent;
  149. background-size: contain;
  150. background-position: center;
  151. background-repeat: no-repeat;
  152. border: none;
  153. cursor: pointer;
  154. z-index: 10;
  155. transition: filter 0.3s ease; /* 动画过渡 */
  156. }
  157. /* ---------------- 左侧按钮逻辑 ---------------- */
  158. .left-btn {
  159. left: 10px;
  160. /* 正常可用状态:借用右可用图,旋转 180 度变成向左 */
  161. background-image: url('@/assets/main/main-right.png');
  162. transform: rotate(180deg);
  163. }
  164. .left-btn.swiper-button-disabled {
  165. /* 禁用状态:使用原生的左禁用图,不旋转 */
  166. background-image: url('@/assets/main/main-left.png');
  167. transform: rotate(0deg);
  168. cursor: not-allowed;
  169. }
  170. /* ---------------- 右侧按钮逻辑 ---------------- */
  171. .right-btn {
  172. right: 10px;
  173. /* 正常可用状态:直接使用右可用图,不旋转 */
  174. background-image: url('@/assets/main/main-right.png');
  175. transform: rotate(0deg);
  176. }
  177. .right-btn.swiper-button-disabled {
  178. /* 禁用状态:借用左禁用图,旋转 180 度变成向右 */
  179. background-image: url('@/assets/main/main-left.png');
  180. transform: rotate(180deg);
  181. cursor: not-allowed;
  182. }
  183. /* ---------------- 悬停特效 (仅对可用状态生效) ---------------- */
  184. /* 左按钮悬停:保持 180 度旋转并放大 */
  185. .left-btn:not(.swiper-button-disabled):hover {
  186. transform: rotate(180deg) scale(1.1);
  187. filter: drop-shadow(0 0 8px rgba(68, 138, 255, 0.6));
  188. }
  189. /* 右按钮悬停:保持 0 度旋转并放大 */
  190. .right-btn:not(.swiper-button-disabled):hover {
  191. transform: rotate(0deg) scale(1.1);
  192. filter: drop-shadow(0 0 8px rgba(68, 138, 255, 0.6));
  193. }
  194. </style>