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
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>
|
|
|