You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

173 lines
4.2 KiB

<template>
<div class="wrap">
<swiper
:modules="[Autoplay]"
:slides-per-view="1"
:loop="true"
:autoplay="{ delay: 5000, disableOnInteraction: false }"
class="my-swiper"
>
<swiper-slide v-for="(item, index) in boardData.backgroundImages || []" :key="index">
<div class="swiper-slide-box">
<img :src="item" alt="图片" />
</div>
</swiper-slide>
</swiper>
<div class="text-box" v-if="[1].includes(boardData.type)">
<div class="title">{{boardData.title}}</div>
<div class="content">{{boardData.content}}</div>
</div>
<template v-if="boardData.type === 3">
<div class="qr-box">
<Qrcode :options="{margin:0}" :width="60" :text="previewUrl" logo="/logo.png" />
<span>扫码送祝福</span>
</div>
<!-- 弹幕容器 -->
<div class="barrage-container">
<marquee direction="left" scrollamount="10" v-for="(item,index) in barrageList" :key="index">
<div class="div-box">
<span v-for="(v,i) in item" :key="i">{{ v }}</span>
</div>
</marquee>
</div>
</template>
</div>
</template>
<script setup lang="ts">
import { Autoplay } from "swiper/modules";
import {getAccessToken} from "@/utils/auth";
import { useWebSocket } from '@vueuse/core'
import { isArray } from 'min-dash'
const message = useMessage() // 消息弹窗
const props = defineProps({
boardData: {
type: Object,
default: () => ({ backgroundImages: [],code:'' }),
},
});
const server = ref<string | null>(null);
const socket = ref<any>(null);
const previewUrl = computed(()=>{
const baseUrl = window.location.origin;
return `${baseUrl}/#/board?code=${props.boardData.code}&isBarrage=true`;
})
const barrageList = ref([])
watch(() => props.boardData, (nv) => {
if (nv) {
if(nv.barrage){
barrageList.value = JSON.parse(nv.barrage) || []
if(isArray(nv.barrages)){
nv.barrages.forEach((v,index)=>{
barrageList.value[index % 2].push(v)
})
}
}
}
}, { immediate: true,deep: true });
// **2. 监听 `boardData.code`,等它有值后再赋值 WebSocket 服务器地址**
watch(() => props.boardData.code, (newCode) => {
if (newCode) {
server.value = (import.meta.env.VITE_WEBSOCKET_URL + "/infra/ws").replace("http", "ws") +
`?token=${getAccessToken()}&code=${newCode}`;
}
}, { immediate: true });
watchEffect(() => {
if (server.value) {
socket.value = useWebSocket(server.value, {
autoReconnect: true,
heartbeat: false,
immediate: true, // **确保有值时立刻连接**
});
}
});
// **4. 监听 WebSocket 消息**
watch(
() => socket.value?.data,
(newData) => {
if (!newData) return;
try {
const data = JSON.parse(newData);
console.log("收到 WebSocket 消息:", data);
if(data.type === 'barrage-push'){
const content = JSON.parse(data.content);
// 找到 barrageList.value 中长度最短的数组
const shortestArray = barrageList.value.reduce((minArr, currentArr) =>
currentArr.length < minArr.length ? currentArr : minArr
);
// 将 content 推入找到的最短数组
shortestArray.push(content);
}
} catch (error) {
message.error("处理消息发生异常:" + newData);
console.error(error);
}
}
);
</script>
<style scoped lang="scss">
.swiper-slide-box{
img{
width: 100%;
height: 100%;
}
}
.text-box{
position: absolute;
z-index: 2;
width: 100%;
top: 16vh;
padding: 0 4vw;
box-sizing: border-box;
font-family: 宋体;
color: #fdf76d;
font-weight: bold;
.title{
font-size: 8vw;
margin-bottom: 5vh;
text-align: center;
letter-spacing: 1vw;
}
.content{
font-size: 4.2vw;
text-align: center;
letter-spacing: 0.5vw;
}
}
.qr-box{
position: absolute;
z-index: 2;
top: 2vh;
right: 1vw;
display: flex;
flex-flow: column;
align-items: center;
font-size: 1vw;
span{
margin-top: 1vh;
}
}
/* 弹幕区域 */
.barrage-container {
position: absolute;
bottom: 2vh;
left: 0;
width: 100%;
pointer-events: none;
color: #000;
z-index: 2;
}
marquee{
margin-bottom: 2vh;
.div-box{
span{
margin-right: 3vw;
}
}
}
</style>