| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220 |
- <template>
- <div class="special-task-panel">
-
- <swiper class="my-swiper" :options="swiperOptions" ref="mySwiper">
- <swiper-slide v-for="(item, index) in combinedList" :key="index" class="custom-slide">
-
- <div class="top-monitor">
- <VideoMonitorBox
- v-if="item.video"
- :videoUrl="item.video.url"
- />
- <div v-else class="empty-placeholder"></div>
- </div>
- <div class="bottom-card">
- <IntersectionControlCard
- v-if="item.card"
- :data="item.card"
- @action-click="handleCardAction"
- />
- </div>
-
- </swiper-slide>
- </swiper>
- <div class="nav-btn left-btn swiper-button-prev"></div>
- <div class="nav-btn right-btn swiper-button-next"></div>
- </div>
- </template>
- <script>
- import { Swiper, SwiperSlide } from 'vue-awesome-swiper';
- import 'swiper/css/swiper.css';
- import VideoMonitorBox from './VideoMonitorBox.vue';
- import IntersectionControlCard from './IntersectionControlCard.vue';
- export default {
- name: 'SpecialTaskMonitorPanel',
- components: { Swiper, SwiperSlide, VideoMonitorBox, IntersectionControlCard },
- props: {
- panelData: { type: Object, required: true }
- },
- data() {
- return {
- swiperOptions: {
- slidesPerView: 3, // 显示 3 列
- slidesPerGroup: 3, // 每次滑动 3 列
- spaceBetween: 20, // 列间距 20px
- simulateTouch: true, // 允许鼠标拖拽
- speed: 600, // 滑动动画 600ms,更加优雅
- // 开启内部监听,当 SmartDialog 缩放导致容器尺寸变化时,Swiper 自动重新计算
- observer: true,
- observeParents: true,
- observeSlideChildren: true,
- navigation: {
- nextEl: '.swiper-button-next',
- prevEl: '.swiper-button-prev'
- }
- }
- };
- },
- computed: {
- // 将独立的两组数据合并为 "列数据",方便 swiper 渲染
- combinedList() {
- if (!this.panelData) return [];
- const videos = this.panelData.videos || [];
- const cards = this.panelData.intersections || [];
- const maxLen = Math.max(videos.length, cards.length);
-
- const list = [];
- for (let i = 0; i < maxLen; i++) {
- list.push({
- video: videos[i] || null,
- card: cards[i] || null
- });
- }
- return list;
- }
- },
- methods: {
- // 处理卡片按钮点击事件
- handleCardAction(cardData) {
- if (cardData.btnText === '立即执行') {
- // 1. 修改文案和按钮样式状态(可选:根据你的业务,让按钮退回普通样式)
- cardData.btnText = '立即解锁';
- cardData.btnType = 'normal';
-
- // 2. 自动切换到下一个
- this.slideNext();
-
- // 3. (可选) 这里可以顺便发送请求给后端,告知该路口已执行
- console.log(`路口 [${cardData.name}] 已执行特勤方案`);
-
- } else if (cardData.btnText === '立即解锁') {
- // 如果再次点击(已经是解锁状态),可以重置回执行状态,按需保留
- cardData.btnText = '立即执行';
- cardData.btnType = 'primary';
-
- console.log(`路口 [${cardData.name}] 已解除特勤方案`);
- }
- },
-
- // 调用 Swiper 实例滑动到下一页
- slideNext() {
- // 兼容不同版本的 vue-awesome-swiper 实例获取方式
- const swiperInstance = this.$refs.mySwiper.$swiper || this.$refs.mySwiper.swiper;
- if (swiperInstance) {
- swiperInstance.slideNext();
- }
- }
- }
- }
- </script>
- <style scoped>
- .special-task-panel {
- position: relative;
- width: 100%;
- height: 100%;
- padding: 20px 60px; /* 给左右箭头留出足够空间 */
- box-sizing: border-box;
- }
- .my-swiper {
- width: 100%;
- height: 100%;
- padding: 10px 5px; /* 防止子元素的阴影被裁切 */
- }
- .custom-slide {
- display: flex;
- flex-direction: column;
- height: 100%;
- box-sizing: border-box;
- }
- /* 分配上下比例 */
- .top-monitor {
- height: 40%; /* 视频区域占 40% */
- min-height: 120px; /* 防止缩太小导致完全看不见 */
- width: 100%;
- }
- .bottom-card {
- height: calc(60% - 15px); /* 卡片占 60% 减去间距 */
- margin-top: 15px;
- width: 100%;
- min-height: 180px;
- }
- .empty-placeholder {
- height: 100%;
- width: 100%;
- background: rgba(255,255,255,0.02);
- border-radius: 6px;
- border: 1px dashed rgba(255,255,255,0.1);
- box-sizing: border-box;
- }
- /* ================= 自定义左右箭头样式 ================= */
- .swiper-button-prev:after, .swiper-button-next:after { display: none; } /* 隐藏默认箭头 */
- .nav-btn {
- position: absolute;
- top: 50%;
- margin-top: -22px; /* 替代 translateY 居中,防止和 rotate 冲突 */
- width: 44px;
- height: 44px;
- background-color: transparent;
- background-size: contain;
- background-position: center;
- background-repeat: no-repeat;
- border: none;
- cursor: pointer;
- z-index: 10;
- transition: filter 0.3s ease; /* 动画过渡 */
- }
- /* ---------------- 左侧按钮逻辑 ---------------- */
- .left-btn {
- left: 10px;
- /* 正常可用状态:借用右可用图,旋转 180 度变成向左 */
- background-image: url('@/assets/main/main-right.png');
- transform: rotate(180deg);
- }
- .left-btn.swiper-button-disabled {
- /* 禁用状态:使用原生的左禁用图,不旋转 */
- background-image: url('@/assets/main/main-left.png');
- transform: rotate(0deg);
- cursor: not-allowed;
- }
- /* ---------------- 右侧按钮逻辑 ---------------- */
- .right-btn {
- right: 10px;
- /* 正常可用状态:直接使用右可用图,不旋转 */
- background-image: url('@/assets/main/main-right.png');
- transform: rotate(0deg);
- }
- .right-btn.swiper-button-disabled {
- /* 禁用状态:借用左禁用图,旋转 180 度变成向右 */
- background-image: url('@/assets/main/main-left.png');
- transform: rotate(180deg);
- cursor: not-allowed;
- }
- /* ---------------- 悬停特效 (仅对可用状态生效) ---------------- */
- /* 左按钮悬停:保持 180 度旋转并放大 */
- .left-btn:not(.swiper-button-disabled):hover {
- transform: rotate(180deg) scale(1.1);
- filter: drop-shadow(0 0 8px rgba(68, 138, 255, 0.6));
- }
- /* 右按钮悬停:保持 0 度旋转并放大 */
- .right-btn:not(.swiper-button-disabled):hover {
- transform: rotate(0deg) scale(1.1);
- filter: drop-shadow(0 0 8px rgba(68, 138, 255, 0.6));
- }
- </style>
|