| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350 |
- <template>
- <div class="security-route-panel">
-
- <div class="panel-header">
- <div class="header-left">
- <span class="status-dot"></span>
- <span class="title-text">特勤安保路线</span>
- <span class="status-text text-danger">未开始</span>
- <span class="level-text text-danger">一级</span>
- <button class="btn-start">立即开始</button>
- </div>
- </div>
- <div class="carousel-wrapper">
-
- <div
- class="nav-arrow left-arrow"
- :class="{ 'is-disabled': !canScrollLeft, 'is-active': canScrollLeft }"
- @click="handlePrev"
- >
- <img v-if="canScrollLeft" src="@/assets/main/main-right.png" class="arrow-img left-facing" />
- <img v-else src="@/assets/main/main-left.png" class="arrow-img" />
- </div>
- <div class="route-list-window">
- <div class="route-list-track">
-
- <div class="route-card" v-for="route in visibleRoutes" :key="route.id">
-
- <div class="card-top-video">
- <XgVideoPlayer :src="route.mainVideo" :autoplay="true" />
- </div>
- <div class="card-bottom-content">
- <div class="route-name">
- <span class="blue-dot"></span>
- {{ route.name }}
- </div>
-
- <div class="map-and-info">
- <div class="map-container">
- <IntersectionMap :mapData="intersectionData" />
- </div>
-
- <div class="info-action-box">
- <div class="info-list">
- <span>等级:<span class="text-green">{{ route.level }}</span></span>
- <span>方式:<span class="text-green">{{ route.mode }}</span></span>
- <span>时间:<span class="text-green">{{ route.time }}</span></span>
- </div>
- <button class="btn-unlock">立即解锁</button>
- </div>
- </div>
- </div>
- </div>
- </div>
- </div>
- <div
- class="nav-arrow right-arrow"
- :class="{ 'is-disabled': !canScrollRight, 'is-active': canScrollRight }"
- @click="handleNext"
- >
- <img v-if="canScrollRight" src="@/assets/main/main-right.png" class="arrow-img" />
- <img v-else src="@/assets/main/main-left.png" class="arrow-img left-facing" />
- </div>
- </div>
- </div>
- </template>
- <script>
- import IntersectionMap from '@/components/ui/IntersectionMapVideos.vue';
- import XgVideoPlayer from '@/components/ui/XgVideoPlayer.vue';
- import { apiGetSecurityRoutes, apiGetIntersectionData } from '@/api';
- export default {
- name: 'SecurityRoutePanelSwitch',
- components: {
- IntersectionMap,
- XgVideoPlayer,
- },
- data() {
- return {
- intersectionData: {},
-
- currentIndex: 0,
- pageSize: 3,
-
- routeList: []
- };
- },
- computed: {
- visibleRoutes() {
- return this.routeList.slice(this.currentIndex, this.currentIndex + this.pageSize);
- },
- canScrollLeft() {
- return this.currentIndex > 0;
- },
- canScrollRight() {
- return this.currentIndex < this.routeList.length - this.pageSize;
- }
- },
- async mounted() {
- this.routeList = await apiGetSecurityRoutes() || [];
- this.intersectionData = await apiGetIntersectionData() || {};
- },
- methods: {
- handlePrev() {
- if (this.canScrollLeft) {
- this.currentIndex--;
- }
- },
- handleNext() {
- if (this.canScrollRight) {
- this.currentIndex++;
- }
- }
- }
- };
- </script>
- <style scoped>
- /* ================== 整体面板 ================== */
- .security-route-panel {
- width: 100%;
- height: 100%;
- display: flex;
- flex-direction: column;
- box-sizing: border-box;
- padding: 5px;
- }
- /* ================== 顶部 Header ================== */
- .panel-header {
- display: flex;
- align-items: center;
- margin-bottom: 20px;
- padding-left: 30px;
- }
- .header-left {
- display: flex;
- align-items: center;
- gap: 12px;
- }
- .status-dot {
- width: 10px;
- height: 10px;
- background-color: #68e75f;
- border-radius: 50%;
- box-shadow: 0 0 8px rgba(104, 231, 95, 0.6);
- }
- .title-text {
- color: #ffffff;
- font-size: 16px;
- font-weight: bold;
- }
- .text-danger {
- color: #ff5252;
- font-size: 14px;
- }
- .btn-start {
- margin-left: 15px;
- background: rgba(40, 90, 180, 0.6);
- border: 1px solid #448aff;
- color: #fff;
- padding: 4px 16px;
- border-radius: 4px;
- cursor: pointer;
- transition: all 0.3s;
- }
- .btn-start:hover {
- background: rgba(40, 90, 180, 0.9);
- box-shadow: 0 0 10px rgba(68, 138, 255, 0.5);
- }
- /* ================== 轮播区容器 ================== */
- .carousel-wrapper {
- flex: 1;
- display: flex;
- align-items: center;
- gap: 15px;
- min-height: 0;
- }
- /* ================== 图片导航按钮统一样式 ================== */
- .nav-arrow {
- flex-shrink: 0;
- width: 30px;
- height: 30px;
- display: flex;
- justify-content: center;
- align-items: center;
- transition: all 0.3s;
- background: transparent;
- border: none;
- outline: none;
- }
- .arrow-img {
- width: 100%;
- height: 100%;
- object-fit: contain;
- transition: transform 0.2s ease, filter 0.3s ease;
- }
- .left-facing {
- transform: rotate(180deg);
- }
- .nav-arrow.is-active {
- cursor: pointer;
- }
- .nav-arrow.is-active:hover .arrow-img {
- transform: scale(1.15);
- filter: drop-shadow(0 0 10px rgba(0, 229, 255, 0.8));
- }
- .nav-arrow.left-arrow.is-active:hover .arrow-img.left-facing {
- transform: rotate(180deg) scale(1.15);
- }
- .nav-arrow.is-disabled {
- cursor: not-allowed;
- opacity: 0.5;
- }
- /* ================== 卡片列表视窗 ================== */
- .route-list-window {
- flex: 1;
- height: 100%;
- overflow: hidden;
- }
- .route-list-track {
- display: flex;
- height: 100%;
- gap: 15px;
- }
- /* 【核心修改】:删除了这里原本的 .fade-enter-active, .fade-leave-active 动画样式 */
- /* ================== 单个卡片样式 ================== */
- .route-card {
- flex: 1;
- display: flex;
- flex-direction: column;
- background: rgba(22, 34, 60, 0.6);
- border: 1px solid rgba(100, 150, 255, 0.2);
- border-radius: 6px;
- overflow: hidden;
- box-shadow: 0 4px 15px rgba(0, 0, 0, 0.3);
- }
- .card-top-video {
- width: 100%;
- height: 55%;
- background: #000;
- border-bottom: 1px solid rgba(100, 150, 255, 0.2);
- }
- .responsive-video {
- width: 100%;
- height: 100%;
- display: block;
- object-fit: cover;
- }
- .card-bottom-content {
- flex: 1;
- padding: 12px;
- display: flex;
- flex-direction: column;
- }
- .route-name {
- color: #fff;
- font-size: 14px;
- display: flex;
- align-items: center;
- margin-bottom: 12px;
- }
- .blue-dot {
- width: 8px;
- height: 8px;
- background-color: #448aff;
- border-radius: 50%;
- margin-right: 8px;
- box-shadow: 0 0 5px rgba(68, 138, 255, 0.8);
- }
- .map-and-info {
- display: flex;
- flex: 1;
- gap: 15px;
- }
- .map-container {
- width: 110px;
- height: 110px;
- flex-shrink: 0;
- border-radius: 4px;
- overflow: hidden;
- position: relative;
- }
- .info-action-box {
- flex: 1;
- display: flex;
- flex-direction: column;
- justify-content: space-between;
- }
- .info-list {
- display: flex;
- flex-direction: column;
- gap: 6px;
- font-size: 12px;
- color: #a0a5b0;
- }
- .text-green {
- color: #48c79c;
- }
- .btn-unlock {
- align-self: flex-start;
- background: rgba(40, 90, 180, 0.6);
- border: 1px solid #448aff;
- color: #fff;
- padding: 4px 12px;
- border-radius: 4px;
- cursor: pointer;
- font-size: 12px;
- transition: all 0.3s;
- }
- .btn-unlock:hover {
- background: rgba(40, 90, 180, 0.9);
- box-shadow: 0 0 8px rgba(68, 138, 255, 0.5);
- }
- </style>
|