Files
AIProd/components/HorizontalDateList.vue
2025-10-24 15:45:38 +08:00

201 lines
4.0 KiB
Vue

<!-- HorizontalDateList.vue -->
<template>
<div class="horizontal-date-wrapper">
<div
class="nav-button prev-button"
:class="{ disabled: !canScrollPrev }"
@click="scrollPrev"
>
<img :src="prevIcon" alt="Previous" />
</div>
<div ref="scrollWrapper" class="scroll-wrapper">
<div ref="scrollContent" class="scroll-content">
<slot></slot>
</div>
</div>
<div
class="nav-button next-button"
:class="{ disabled: !canScrollNext }"
@click="scrollNext"
>
<img :src="nextIcon" alt="Next" />
</div>
</div>
</template>
<script>
import IconPrev from '@/static/launches/icon_prev.png';
import IconNext from '@/static/launches/icon_next.png';
import IconPrevDisabled from '@/static/launches/icon_prev_disabled.png';
import IconNextDisabled from '@/static/launches/icon_next_disabled.png';
export default {
data() {
return {
scrollPosition: 0,
maxScroll: 0,
canScrollPrev: false,
canScrollNext: true
}
},
computed: {
prevIcon() {
return this.canScrollPrev ? IconPrev : IconPrevDisabled;
},
nextIcon() {
return this.canScrollNext ? IconNext : IconNextDisabled;
}
},
mounted() {
this.initScroll();
window.addEventListener('resize', this.updateScrollState);
},
beforeDestroy() {
window.removeEventListener('resize', this.updateScrollState);
},
methods: {
initScroll() {
this.updateScrollState();
},
forceUpdate() {
this.updateScrollState();
},
updateScrollState() {
this.$nextTick(() => {
const wrapper = this.$refs.scrollWrapper;
const content = this.$refs.scrollContent;
if (wrapper && content) {
// 重置滚动位置
wrapper.scrollLeft = 0;
this.scrollPosition = 0;
// 重新计算最大滚动距离
this.maxScroll = Math.max(0, content.offsetWidth - wrapper.offsetWidth);
// 更新按钮状态
this.updateButtonStates();
}
});
},
updateButtonStates() {
const wrapper = this.$refs.scrollWrapper;
if (wrapper) {
this.scrollPosition = wrapper.scrollLeft;
this.canScrollPrev = this.scrollPosition > 0;
this.canScrollNext = this.scrollPosition < this.maxScroll;
}
},
scrollPrev() {
const wrapper = this.$refs.scrollWrapper;
if (!wrapper || wrapper.scrollLeft <= 0) return;
const scrollAmount = wrapper.offsetWidth * 0.8;
const newPosition = Math.max(0, wrapper.scrollLeft - scrollAmount);
wrapper.scrollTo({
left: newPosition,
behavior: 'smooth'
});
// 更新状态
setTimeout(() => {
this.updateButtonStates();
}, 300);
},
scrollNext() {
const wrapper = this.$refs.scrollWrapper;
if (!wrapper) return;
const scrollAmount = wrapper.offsetWidth * 0.8;
const newPosition = Math.min(this.maxScroll, wrapper.scrollLeft + scrollAmount);
wrapper.scrollTo({
left: newPosition,
behavior: 'smooth'
}
);
// 更新状态
setTimeout(() => {
this.updateButtonStates();
}, 300);
}
},
watch: {
// 监听插槽内容变化
'$slots.default'() {
this.updateScrollState();
}
}
}
</script>
<!-- HorizontalDateList.vue -->
<style scoped lang="scss">
.horizontal-date-wrapper {
display: flex;
align-items: center;
gap: 12px;
width: 100%;
.nav-button {
width: 44px;
height: 44px;
cursor: pointer;
background-color: #FFFFFF;
border-radius: 4px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
flex-shrink: 0;
display: flex;
align-items: center;
justify-content: center;
&:active {
opacity: 0.8;
}
img {
width: 44px;
height: 44px;
}
&.disabled {
cursor: not-allowed;
opacity: 0.5;
}
}
.scroll-wrapper {
flex: 1; /* 使用flex而不是固定宽度 */
min-width: 0; /* 允许flex项目收缩 */
overflow-x: auto;
overflow-y: hidden;
position: relative;
/* 隐藏滚动条 */
-ms-overflow-style: none;
scrollbar-width: none;
&::-webkit-scrollbar {
display: none;
}
}
.scroll-content {
display: flex; /* 使用flex布局 */
gap: 12px; /* 设置元素间距 */
white-space: nowrap;
width: max-content; /* 确保内容宽度正确 */
}
}
</style>