281 lines
6.4 KiB
Vue
281 lines
6.4 KiB
Vue
<template>
|
|
<SidePanelItem title="水电站介绍">
|
|
<div class="station-intro">
|
|
<a-empty v-if="mediaData.length === 0" description="暂无介绍信息" />
|
|
|
|
<template v-else>
|
|
<div
|
|
class="container"
|
|
@mouseenter="isHovering = true"
|
|
@mouseleave="isHovering = false"
|
|
>
|
|
<div
|
|
class="carousel-track"
|
|
:class="{ 'no-transition': isTransitioning }"
|
|
:style="{ transform: `translateX(-${currentIndex * 100}%)` }"
|
|
>
|
|
<div
|
|
v-for="(item, index) in renderMediaData"
|
|
:key="index"
|
|
class="carousel-item"
|
|
@click="openDetail"
|
|
>
|
|
<img :src="item.image" :alt="item.title" />
|
|
</div>
|
|
</div>
|
|
|
|
<div class="pagination-dots-fixed">
|
|
<span
|
|
v-for="(dot, index) in mediaData"
|
|
:key="index"
|
|
class="dot"
|
|
:class="{ active: getCurrentRealIndex() === index }"
|
|
@click="goToSlide(index)"
|
|
></span>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="description-text">
|
|
{{ currentDescription }}
|
|
</div>
|
|
</template>
|
|
</div>
|
|
|
|
<!-- 详情弹框 -->
|
|
<a-modal v-model:open="modalVisible" title="设施详情" width="80%" :footer="null">
|
|
<div v-if="detailDataSource.length" class="detail-container">
|
|
<ArtsDetail :dataSource="detailDataSource" :index="0" />
|
|
</div>
|
|
</a-modal>
|
|
</SidePanelItem>
|
|
</template>
|
|
|
|
<script lang="ts" setup>
|
|
import { inject, watch, type Ref, ref, computed, onMounted, onUnmounted } from 'vue';
|
|
import SidePanelItem from '@/components/SidePanelItem/index.vue';
|
|
import ArtsDetail from '@/components/carouselIntroduce/ArtsDetail.vue';
|
|
import { overviewvmsstbprptGetKendoList } from '@/api/home';
|
|
|
|
defineOptions({
|
|
name: 'ShuiDianZhanJieShao'
|
|
});
|
|
|
|
interface MediaItem {
|
|
image: string;
|
|
title: string;
|
|
description: string;
|
|
}
|
|
|
|
interface DianZhanStation {
|
|
wbsCode: string;
|
|
stcd: string;
|
|
ennm: string;
|
|
lgtd: number;
|
|
lttd: number;
|
|
}
|
|
|
|
const baseUrl = import.meta.env.VITE_APP_ATTACHMENT_URL;
|
|
const dianZhanStation = inject<Ref<DianZhanStation | null>>('dianZhanStation');
|
|
|
|
// 弹框
|
|
const modalVisible = ref(false);
|
|
|
|
// 媒体数据
|
|
const mediaData = ref<MediaItem[]>([]);
|
|
const renderMediaData = ref<MediaItem[]>([]);
|
|
const currentIndex = ref(1);
|
|
const isHovering = ref(false);
|
|
const isTransitioning = ref(false);
|
|
let timer: any = null;
|
|
|
|
const detailDataSource = computed(() => {
|
|
return mediaData.value.map(item => ({
|
|
url: item.image || '',
|
|
description: item.description || '',
|
|
title: item.title || ''
|
|
}));
|
|
});
|
|
|
|
const currentDescription = computed(() => {
|
|
const realIndex = getCurrentRealIndex();
|
|
return mediaData.value[realIndex]?.description || '';
|
|
});
|
|
|
|
const getCurrentRealIndex = () => {
|
|
const len = mediaData.value.length;
|
|
if (len === 0) return 0;
|
|
let realIndex = currentIndex.value - 1;
|
|
if (realIndex < 0) realIndex = len - 1;
|
|
if (realIndex >= len) realIndex = 0;
|
|
return realIndex;
|
|
};
|
|
|
|
const initRenderData = () => {
|
|
const len = mediaData.value.length;
|
|
if (len === 0) {
|
|
renderMediaData.value = [];
|
|
return;
|
|
}
|
|
if (len === 1) {
|
|
renderMediaData.value = [...mediaData.value];
|
|
currentIndex.value = 0;
|
|
return;
|
|
}
|
|
renderMediaData.value = [
|
|
mediaData.value[len - 1],
|
|
...mediaData.value,
|
|
mediaData.value[0]
|
|
];
|
|
currentIndex.value = 1;
|
|
};
|
|
|
|
const goToSlide = (targetIndex: number) => {
|
|
if (isTransitioning.value || mediaData.value.length <= 1) return;
|
|
currentIndex.value = targetIndex + 1;
|
|
};
|
|
|
|
const startAutoPlay = () => {
|
|
if (timer) clearInterval(timer);
|
|
if (mediaData.value.length <= 1) return;
|
|
timer = setInterval(() => {
|
|
if (!isHovering.value && !isTransitioning.value) {
|
|
currentIndex.value++;
|
|
}
|
|
}, 4000);
|
|
};
|
|
|
|
const openDetail = () => {
|
|
if (mediaData.value.length) {
|
|
modalVisible.value = true;
|
|
}
|
|
};
|
|
|
|
const fetchStationInfo = async (stcd: string) => {
|
|
try {
|
|
const params = {
|
|
filter: {
|
|
logic: 'and',
|
|
filters: [
|
|
{ field: 'stcd', operator: 'eq', dataType: 'string', value: stcd },
|
|
{ field: 'sttpCode', operator: 'eq', dataType: 'string', value: 'ENG' }
|
|
]
|
|
},
|
|
sort: [{ field: 'siteStepSort', dir: 'asc' }],
|
|
select: ['inffile', 'introduce', 'ennm', 'logo', 'precis']
|
|
};
|
|
const res = await overviewvmsstbprptGetKendoList(params);
|
|
const dataList = res?.data?.data;
|
|
if (dataList && Array.isArray(dataList)) {
|
|
mediaData.value = dataList
|
|
.filter((item: any) => item.inffile || item.introduce)
|
|
.map((item: any) => ({
|
|
image: item.inffile ? `${baseUrl}?${item.inffile}&view=jpg` : '',
|
|
title: item.ennm || '',
|
|
description: item.introduce || ''
|
|
}));
|
|
} else {
|
|
mediaData.value = [];
|
|
}
|
|
initRenderData();
|
|
startAutoPlay();
|
|
} catch {
|
|
mediaData.value = [];
|
|
initRenderData();
|
|
}
|
|
};
|
|
|
|
watch(
|
|
() => dianZhanStation?.value,
|
|
(newVal) => {
|
|
if (newVal?.stcd) {
|
|
fetchStationInfo(newVal.stcd);
|
|
}
|
|
},
|
|
{ deep: true, immediate: true }
|
|
);
|
|
|
|
onUnmounted(() => {
|
|
if (timer) clearInterval(timer);
|
|
});
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
.container {
|
|
width: 100%;
|
|
height: 228px;
|
|
position: relative;
|
|
overflow: hidden;
|
|
|
|
.carousel-track {
|
|
display: flex;
|
|
width: 100%;
|
|
height: 100%;
|
|
transition: transform 0.5s ease-in-out;
|
|
|
|
&.no-transition {
|
|
transition: none;
|
|
}
|
|
|
|
.carousel-item {
|
|
min-width: 100%;
|
|
height: 100%;
|
|
position: relative;
|
|
flex-shrink: 0;
|
|
cursor: pointer;
|
|
|
|
img {
|
|
width: 100%;
|
|
height: 100%;
|
|
object-fit: cover;
|
|
}
|
|
}
|
|
}
|
|
|
|
.pagination-dots-fixed {
|
|
position: absolute;
|
|
bottom: 10px;
|
|
right: 10px;
|
|
display: flex;
|
|
gap: 6px;
|
|
z-index: 10;
|
|
|
|
.dot {
|
|
width: 5px;
|
|
height: 5px;
|
|
border-radius: 50%;
|
|
background-color: #d8d8d8;
|
|
cursor: pointer;
|
|
transition: background-color 0.3s ease;
|
|
|
|
&.active {
|
|
background-color: #005293;
|
|
}
|
|
|
|
&:hover {
|
|
opacity: 0.8;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
.description-text {
|
|
font-size: 14px;
|
|
line-height: 1.5;
|
|
transition: all 0.3s ease;
|
|
margin-top: 12px;
|
|
min-height: 44px;
|
|
|
|
display: -webkit-box;
|
|
-webkit-line-clamp: 2;
|
|
-webkit-box-orient: vertical;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
word-break: break-word;
|
|
}
|
|
|
|
.detail-container {
|
|
width: 100%;
|
|
height: 100%;
|
|
}
|
|
</style>
|