|
|
|
@ -14,110 +14,152 @@ |
|
|
|
</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 class="title">{{ boardData.title }}</div> |
|
|
|
<div class="content">{{ boardData.content }}</div> |
|
|
|
</div> |
|
|
|
<template v-if="boardData.type === 3"> |
|
|
|
<template v-else-if="boardData.type === 3"> |
|
|
|
<div class="qr-box"> |
|
|
|
<Qrcode :options="{margin:0}" :width="60" :text="previewUrl" logo="/logo.png" /> |
|
|
|
<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"> |
|
|
|
<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> |
|
|
|
<span v-for="(v, i) in item" :key="i">{{ v }}</span> |
|
|
|
</div> |
|
|
|
</marquee> |
|
|
|
</div> |
|
|
|
</template> |
|
|
|
|
|
|
|
<div :class="['screen-box', 'screen-' + boardData.category]" v-if="boardData.type === 4"> |
|
|
|
<swiper |
|
|
|
v-for="item in showScreenList" |
|
|
|
:key="item.value" |
|
|
|
:modules="[Autoplay]" |
|
|
|
:slides-per-view="1" |
|
|
|
:loop="true" |
|
|
|
:autoplay="{ delay: 5000, disableOnInteraction: false }" |
|
|
|
class="my-swiper" |
|
|
|
> |
|
|
|
<swiper-slide v-for="(slide, index) in boardData[item.prop] || []" :key="index"> |
|
|
|
<div class="swiper-slide-box"> |
|
|
|
<img :src="slide" alt="图片" /> |
|
|
|
</div> |
|
|
|
</swiper-slide> |
|
|
|
</swiper> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</template> |
|
|
|
<script setup lang="ts"> |
|
|
|
import { Autoplay } from "swiper/modules"; |
|
|
|
import {getAccessToken} from "@/utils/auth"; |
|
|
|
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`; |
|
|
|
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) => { |
|
|
|
|
|
|
|
const screenProps = [ |
|
|
|
{ prop: 'oneScreen', value: 1 }, |
|
|
|
{ prop: 'twoScreen', value: 2 }, |
|
|
|
{ prop: 'threeScreen', value: 3 }, |
|
|
|
{ prop: 'fourScreen', value: 4 } |
|
|
|
] |
|
|
|
const showScreenList = computed(() => { |
|
|
|
return screenProps.filter((item) => item.value <= props.boardData.category) |
|
|
|
}) |
|
|
|
watch( |
|
|
|
() => props.boardData, |
|
|
|
(nv) => { |
|
|
|
if (nv) { |
|
|
|
if(nv.barrage){ |
|
|
|
if (nv.barrage) { |
|
|
|
barrageList.value = JSON.parse(nv.barrage) || [] |
|
|
|
if(isArray(nv.barrages)){ |
|
|
|
nv.barrages.forEach((v,index)=>{ |
|
|
|
if (isArray(nv.barrages)) { |
|
|
|
nv.barrages.forEach((v, index) => { |
|
|
|
barrageList.value[index % 2].push(v) |
|
|
|
}) |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
}, { immediate: true,deep: true }); |
|
|
|
}, |
|
|
|
{ immediate: true, deep: true } |
|
|
|
) |
|
|
|
// **2. 监听 `boardData.code`,等它有值后再赋值 WebSocket 服务器地址** |
|
|
|
watch(() => props.boardData.code, (newCode) => { |
|
|
|
watch( |
|
|
|
() => props.boardData.code, |
|
|
|
(newCode) => { |
|
|
|
if (newCode) { |
|
|
|
server.value = (import.meta.env.VITE_WEBSOCKET_URL + "/infra/ws").replace("http", "ws") + |
|
|
|
`?token=${getAccessToken()}&code=${newCode}`; |
|
|
|
server.value = |
|
|
|
(import.meta.env.VITE_WEBSOCKET_URL + '/infra/ws').replace('http', 'ws') + |
|
|
|
`?token=${getAccessToken()}&code=${newCode}` |
|
|
|
} |
|
|
|
}, { immediate: true }); |
|
|
|
}, |
|
|
|
{ immediate: true } |
|
|
|
) |
|
|
|
|
|
|
|
watchEffect(() => { |
|
|
|
if (server.value) { |
|
|
|
socket.value = useWebSocket(server.value, { |
|
|
|
autoReconnect: true, |
|
|
|
heartbeat: false, |
|
|
|
immediate: true, // **确保有值时立刻连接** |
|
|
|
}); |
|
|
|
immediate: true // **确保有值时立刻连接** |
|
|
|
}) |
|
|
|
} |
|
|
|
}); |
|
|
|
}) |
|
|
|
|
|
|
|
// **4. 监听 WebSocket 消息** |
|
|
|
watch( |
|
|
|
() => socket.value?.data, |
|
|
|
(newData) => { |
|
|
|
if (!newData) return; |
|
|
|
if (!newData) return |
|
|
|
|
|
|
|
try { |
|
|
|
const data = JSON.parse(newData); |
|
|
|
console.log("收到 WebSocket 消息:", data); |
|
|
|
const data = JSON.parse(newData) |
|
|
|
console.log('收到 WebSocket 消息:', data) |
|
|
|
|
|
|
|
if(data.type === 'barrage-push'){ |
|
|
|
const content = JSON.parse(data.content); |
|
|
|
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); |
|
|
|
shortestArray.push(content) |
|
|
|
} |
|
|
|
} catch (error) { |
|
|
|
message.error("处理消息发生异常:" + newData); |
|
|
|
console.error(error); |
|
|
|
message.error('处理消息发生异常:' + newData) |
|
|
|
console.error(error) |
|
|
|
} |
|
|
|
} |
|
|
|
); |
|
|
|
) |
|
|
|
</script> |
|
|
|
|
|
|
|
<style scoped lang="scss"> |
|
|
|
.swiper-slide-box{ |
|
|
|
img{ |
|
|
|
.swiper-slide-box { |
|
|
|
img { |
|
|
|
width: 100%; |
|
|
|
height: 100%; |
|
|
|
} |
|
|
|
} |
|
|
|
.text-box{ |
|
|
|
|
|
|
|
.text-box { |
|
|
|
position: absolute; |
|
|
|
z-index: 2; |
|
|
|
width: 100%; |
|
|
|
@ -127,19 +169,22 @@ watch( |
|
|
|
font-family: 宋体; |
|
|
|
color: #fdf76d; |
|
|
|
font-weight: bold; |
|
|
|
.title{ |
|
|
|
|
|
|
|
.title { |
|
|
|
font-size: 8vw; |
|
|
|
margin-bottom: 5vh; |
|
|
|
text-align: center; |
|
|
|
letter-spacing: 1vw; |
|
|
|
} |
|
|
|
.content{ |
|
|
|
|
|
|
|
.content { |
|
|
|
font-size: 4.2vw; |
|
|
|
text-align: center; |
|
|
|
letter-spacing: 0.5vw; |
|
|
|
} |
|
|
|
} |
|
|
|
.qr-box{ |
|
|
|
|
|
|
|
.qr-box { |
|
|
|
position: absolute; |
|
|
|
z-index: 2; |
|
|
|
top: 2vh; |
|
|
|
@ -148,10 +193,12 @@ watch( |
|
|
|
flex-flow: column; |
|
|
|
align-items: center; |
|
|
|
font-size: 1vw; |
|
|
|
span{ |
|
|
|
|
|
|
|
span { |
|
|
|
margin-top: 1vh; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/* 弹幕区域 */ |
|
|
|
.barrage-container { |
|
|
|
position: absolute; |
|
|
|
@ -162,12 +209,51 @@ watch( |
|
|
|
color: #000; |
|
|
|
z-index: 2; |
|
|
|
} |
|
|
|
marquee{ |
|
|
|
|
|
|
|
marquee { |
|
|
|
margin-bottom: 2vh; |
|
|
|
.div-box{ |
|
|
|
span{ |
|
|
|
|
|
|
|
.div-box { |
|
|
|
span { |
|
|
|
margin-right: 3vw; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
.screen-box { |
|
|
|
position: absolute; |
|
|
|
z-index: 2; |
|
|
|
width: 100%; |
|
|
|
height: 100%; |
|
|
|
top: 0; |
|
|
|
display: grid; |
|
|
|
padding: 3%; |
|
|
|
gap: 2vw; |
|
|
|
box-sizing: border-box; |
|
|
|
} |
|
|
|
|
|
|
|
.screen-1 { |
|
|
|
grid-template: 1fr / 1fr; |
|
|
|
} |
|
|
|
|
|
|
|
/* 二屏:上下平分 */ |
|
|
|
.screen-2 { |
|
|
|
grid-template: repeat(2, 1fr) / 1fr; |
|
|
|
} |
|
|
|
|
|
|
|
/* 三屏:上二下一 */ |
|
|
|
.screen-3 { |
|
|
|
grid-template: 1fr 1fr / repeat(2, 1fr); |
|
|
|
} |
|
|
|
|
|
|
|
.screen-3 .image:nth-child(3) { |
|
|
|
grid-column: span 2; /* 第三个元素跨两列 */ |
|
|
|
} |
|
|
|
.screen-3 .my-swiper:nth-child(3) { |
|
|
|
grid-column: 1 / -1; /* 让第三个元素从第1列跨到最后1列 */ |
|
|
|
} |
|
|
|
/* 四屏:2x2 网格 */ |
|
|
|
.screen-4 { |
|
|
|
grid-template: repeat(2, 1fr) / repeat(2, 1fr); |
|
|
|
} |
|
|
|
</style> |
|
|
|
|