SocialNetworks_duan/src/views/GroupEvolution/component/groupPost.vue
2025-08-05 10:04:27 +08:00

252 lines
5.6 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="postList-component">
<img
src="@/assets/images/groupEvolution/event-list-title.png"
alt=""
style="margin-top: -17px; margin-left: -11px"
/>
<div
class="post-list-wrapper"
ref="listRef"
@mouseenter="pauseScroll"
@mouseleave="resumeScroll"
>
<div class="scrolling-content">
<div
class="post-item"
v-for="(post, index) in posts"
:key="index"
:class="{ highlighted: post.highlighted }"
@click="handleLeaderPost(post)"
>
<span class="timestamp">{{ post.timestamp }}</span>
<span class="author">【{{ post.author }}】发布了帖文</span>
<div class="influence-section">
<div class="influence-label">互动: {{ post.influence }}</div>
<div class="influence-bar">
<div class="bar-track"></div>
<div class="bar-fill" :style="{ width: getInfluenceWidth(post.influence) }"></div>
<div class="bar-handle-wrapper" :style="{ left: getInfluenceWidth(post.influence) }">
<div class="bar-handle"></div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { ref, computed, defineProps, defineEmits, onMounted, onBeforeUnmount } from "vue"
const props = defineProps({
posts: {
type: Array,
default: () => []
}
})
const emit = defineEmits(["click:openDialog"])
const animationDuration = computed(() => `${props.posts.length * 3}s`)
const maxInfluence = 10000
const getInfluenceWidth = (influence) => {
const percentage = (influence / maxInfluence) * 100
return `${Math.min(percentage, 100)}%`
}
const handleLeaderPost = (item) => {
emit("click:openDialog", item)
console.log(item)
}
const listRef = ref(null)
let scrollTimer = null
let direction = 1 // 1: 向下, -1: 向上
const scrollStep = 1 // 每步像素
const scrollInterval = 80 // 每40ms滚动一次越大越慢
function startScroll() {
if (scrollTimer) return
scrollTimer = setInterval(() => {
const el = listRef.value
if (!el) return
el.scrollTop += direction * scrollStep
// 到底部
if (el.scrollTop + el.clientHeight >= el.scrollHeight) {
direction = -1
}
// 到顶部
if (el.scrollTop <= 0) {
direction = 1
}
}, scrollInterval)
}
function pauseScroll() {
if (scrollTimer) {
clearInterval(scrollTimer)
scrollTimer = null
}
}
function resumeScroll() {
startScroll()
}
onMounted(() => {
startScroll()
})
onBeforeUnmount(() => {
pauseScroll()
})
</script>
<style scoped lang="less">
.postList-component {
width: 100%;
height: 100%;
.post-list-wrapper {
width: 100%;
height: 80%;
overflow: auto;
}
/* 滚动条整体样式 - WebKit浏览器 */
.post-list-wrapper::-webkit-scrollbar {
width: 3px; /* 垂直滚动条宽度 */
height: 5px; /* 水平滚动条高度 */
}
/* 滚动条滑块 */
.post-list-wrapper::-webkit-scrollbar-thumb {
background: rgba(147, 210, 255, 0.3); /* 蓝色半透明滑块 */
border-radius: 4px;
}
/* 鼠标悬停在滑块上的效果 */
.post-list-wrapper::-webkit-scrollbar-thumb:hover {
background: rgba(147, 210, 255, 0.5); /* 更明显的蓝色 */
}
.scrolling-content {
display: flex;
flex-direction: column;
gap: 1px;
animation-name: scroll-up;
animation-timing-function: linear;
animation-iteration-count: infinite;
}
.post-list-wrapper:hover .scrolling-content {
animation-play-state: paused;
}
.post-item {
display: flex;
align-items: center;
height: 36px;
padding: 0 16px;
background: linear-gradient(90deg, rgba(63, 169, 245, 0.16) 0%, rgba(0, 84, 187, 0) 100%);
border-left: 2px solid #3fa9f5;
color: white;
font-size: 14px;
gap: 12px;
flex-shrink: 0;
cursor: pointer;
margin: 8px 0;
}
.post-item.highlighted {
background: linear-gradient(90deg, rgba(63, 169, 245, 0.4) 0%, rgba(0, 84, 187, 0) 100%);
}
.post-type {
display: flex;
align-items: center;
gap: 8px;
flex-shrink: 0;
}
.post-type-text {
color: #8efbff;
font-size: 14px;
font-weight: 400;
text-shadow: 0px 1px 1px rgba(0, 0, 0, 0.25);
}
.timestamp {
flex-shrink: 0;
}
.author {
flex-grow: 1;
text-align: left;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.influence-section {
display: flex;
align-items: center;
gap: 8px;
flex-shrink: 0;
}
.influence-label {
width: 90px;
display: flex;
justify-content: flex-start;
}
.influence-bar {
width: 200px;
height: 10px;
position: relative;
display: flex;
align-items: center;
}
.bar-track {
width: 100%;
height: 2px;
background-color: rgba(208, 222, 238, 0.1);
position: absolute;
}
.bar-fill {
position: absolute;
height: 2px;
background: linear-gradient(270deg, #00f3ff 0%, #00527d 100%);
}
.bar-handle-wrapper {
position: absolute;
top: 50%;
transform: translateY(-50%);
height: 16px;
width: 16px;
display: flex;
justify-content: center;
align-items: center;
}
.bar-handle {
width: 10px;
height: 10px;
border-radius: 50%;
background-color: #e0f1ff;
position: relative;
box-shadow: 0 0 6px 0 rgba(13, 97, 255, 0.8);
}
.bar-handle::before {
content: "";
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 16px;
height: 16px;
border-radius: 50%;
border: 1px solid rgba(21, 154, 255, 0.3);
}
}
</style>