WholeProcessPlatform/frontend/src/views/dianZhanZhuanTi/components/ShuiDianZhanJieShao.vue
2026-07-31 11:13:49 +08:00

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>