topfans/frontend/pages/support-activity/components/MessageBoard.vue

134 lines
3.3 KiB
Vue
Raw Permalink 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>
<view class="message-board">
<scroll-view
class="message-board-scroll"
scroll-y
:scroll-top="scrollTop"
:scroll-with-animation="true"
:show-scrollbar="false"
:enhanced="true"
:bounces="false"
>
<view v-for="msg in messages" :key="msg.id" class="message-bubble">
<text class="msg-user">{{ msg.user }}</text>
<text class="msg-content">{{ msg.content }}</text>
</view>
<view v-if="messages.length === 0" class="empty-tip">
<text>暂无留言,快来抢沙发吧~</text>
</view>
</scroll-view>
</view>
</template>
<script setup>
import { ref, watch, nextTick, onMounted } from "vue";
const props = defineProps({
messages: {
type: Array,
default: () => [],
},
});
// scroll-top 需要 string 类型,每次都用一个递增的"超大值"作为目标位置,
// 既保证 scroll-view 一定滚动到底部,又因为值唯一(Vue 响应式触发)
// 即使连续推多条消息也能逐条滚动。
const scrollTop = ref("0");
const scrollVersion = ref(0);
function scrollToBottom() {
scrollVersion.value += 1;
nextTick(() => {
// 递增到比内容高度大得多的值,确保始终滚到最底部
scrollTop.value = String(99999 + scrollVersion.value * 1000);
});
}
// 监听消息数量变化(新增/删除),自动滚动到底部
watch(
() => props.messages.length,
() => scrollToBottom(),
);
// 组件挂载后,如果已有历史消息,也立即滚动到底部
onMounted(() => {
if (props.messages.length > 0) {
scrollToBottom();
}
});
</script>
<style scoped>
.message-board {
width: 100%;
position: fixed;
bottom: 128rpx;
padding: 0 34rpx; /* 对应 Figma 中 left: 17px */
}
.message-board-scroll {
height: 352rpx;
width: 100%;
display: flex;
flex-direction: column;
align-items: flex-start; /* 气泡左对齐,宽度适应内容 */
gap: 6rpx; /* 对应 Figma 中 3px 间距,rpx 自动换算 */
}
/* 留言气泡:对应 Figma 留言板 的圆角矩形 */
.message-bubble {
/* display: inline-flex; */
align-items: baseline;
flex-wrap: wrap;
max-width: max-content;
padding: 6rpx 16rpx; /* 适当内边距,让文字与气泡边距舒适 */
background: rgba(42, 17, 17, 0.3);
border-radius: 16rpx; /* 8px = 16rpx */
/* Figma 中的微妙旋转与倾斜,营造手写/手帐感 */
transform: rotate(-0.44deg) skewX(-0.1deg);
animation: bubbleFadeIn 0.3s ease-out;
box-sizing: border-box;
margin-bottom: 4rpx;
}
.msg-user {
font-size: 24rpx; /* 12px = 24rpx */
color: #acf0c3;
font-weight: bold;
text-shadow: -1rpx 1rpx 4rpx rgba(206, 9, 9, 0.45);
font-family: "Abhaya Libre ExtraBold", sans-serif;
line-height: 1.5;
}
.msg-content {
font-size: 24rpx; /* 12px = 24rpx */
color: #ffffff;
font-weight: bold;
text-shadow: -1rpx 1rpx 4rpx rgba(206, 9, 9, 0.45);
font-family: "Abhaya Libre ExtraBold", sans-serif;
line-height: 1.5;
word-break: break-all;
}
.empty-tip {
display: flex;
align-items: center;
justify-content: center;
height: 200rpx;
width: 100%;
color: rgba(255, 255, 255, 0.5);
font-size: 24rpx;
}
@keyframes bubbleFadeIn {
from {
opacity: 0;
transform: translateY(20rpx) rotate(-0.44deg) skewX(-0.1deg);
}
to {
opacity: 1;
transform: translateY(0) rotate(-0.44deg) skewX(-0.1deg);
}
}
</style>