Browse Source

分屏

master
py 9 months ago
parent
commit
4e67a6d983
  1. 11
      src/views/Banner/BannerForm.vue
  2. 202
      src/views/Board/components/welcome.vue

11
src/views/Banner/BannerForm.vue

@ -129,10 +129,10 @@ const formItemList = computed(()=>{
selectData: dataCategory selectData: dataCategory
}, },
{label: '背景图片', prop: 'backgroundImages', type: 'image'}, {label: '背景图片', prop: 'backgroundImages', type: 'image'},
{label: '一屏', prop: 'oneScreen', type: 'image', limit: 6,hide:true,show:formData.value.category === 1}, {label: '一屏', prop: 'oneScreen', type: 'image', limit: 6,hide:true,show:true},
{label: '二屏', prop: 'twoScreen', type: 'image', limit: 6,hide:true,show:formData.value.category === 2}, {label: '二屏', prop: 'twoScreen', type: 'image', limit: 6,hide:true,show:formData.value.category >= 2},
{label: '三屏', prop: 'threeScreen', type: 'image', limit: 6,hide:true,show:formData.value.category === 3}, {label: '三屏', prop: 'threeScreen', type: 'image', limit: 6,hide:true,show:formData.value.category >= 3},
{label: '四屏', prop: 'fourScreen', type: 'image', limit: 6,hide:true,show:formData.value.category === 4} {label: '四屏', prop: 'fourScreen', type: 'image', limit: 6,hide:true,show:formData.value.category >= 4}
] ]
} }
}) })
@ -149,8 +149,7 @@ const getAIContent = async () => {
if (AILoading.value) return if (AILoading.value) return
try { try {
AILoading.value = true AILoading.value = true
const res = await BannerApi.getContentBanner(`帮我生成生日祝福,返回数组格式,一个大数组,嵌套2个数组,里面的数组是生成的祝福语句,可以混一起,其他多余的字符不要,前面加个类似🎉 🌼 🎂 🎈 🌟 💐 这种小图标,数组格式不是字符串,姓名叫${formData.value.fullName},一人两条祝福,,名字可能为空,空就直接三条生日祝福。`) const res = await BannerApi.getContentBanner(`帮我生成生日祝福语带上姓名,姓名叫${formData.value.fullName},返回数组格式,一个大数组,嵌套2个数组存放所有祝福语,一人两条祝福,两个数组各一条,前面加个类似🎉 🌼 🎂 🎈 🌟 💐 这种小图标,其余字符全部不要。`)
console.log(res, '===')
formData.value.barrage = res formData.value.barrage = res
AILoading.value = false AILoading.value = false
} catch { } catch {

202
src/views/Board/components/welcome.vue

@ -14,110 +14,152 @@
</swiper-slide> </swiper-slide>
</swiper> </swiper>
<div class="text-box" v-if="[1].includes(boardData.type)"> <div class="text-box" v-if="[1].includes(boardData.type)">
<div class="title">{{boardData.title}}</div> <div class="title">{{ boardData.title }}</div>
<div class="content">{{boardData.content}}</div> <div class="content">{{ boardData.content }}</div>
</div> </div>
<template v-if="boardData.type === 3"> <template v-else-if="boardData.type === 3">
<div class="qr-box"> <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> <span>扫码送祝福</span>
</div> </div>
<!-- 弹幕容器 --> <!-- 弹幕容器 -->
<div class="barrage-container"> <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"> <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> </div>
</marquee> </marquee>
</div> </div>
</template> </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> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { Autoplay } from "swiper/modules"; import { Autoplay } from 'swiper/modules'
import {getAccessToken} from "@/utils/auth"; import { getAccessToken } from '@/utils/auth'
import { useWebSocket } from '@vueuse/core' import { useWebSocket } from '@vueuse/core'
import { isArray } from 'min-dash' import { isArray } from 'min-dash'
const message = useMessage() // const message = useMessage() //
const props = defineProps({ const props = defineProps({
boardData: { boardData: {
type: Object, type: Object,
default: () => ({ backgroundImages: [],code:'' }), default: () => ({ backgroundImages: [], code: '' })
}, }
}); })
const server = ref<string | null>(null); const server = ref<string | null>(null)
const socket = ref<any>(null); const socket = ref<any>(null)
const previewUrl = computed(()=>{ const previewUrl = computed(() => {
const baseUrl = window.location.origin; const baseUrl = window.location.origin
return `${baseUrl}/#/board?code=${props.boardData.code}&isBarrage=true`; return `${baseUrl}/#/board?code=${props.boardData.code}&isBarrage=true`
}) })
const barrageList = ref([]) const barrageList = ref([])
watch(() => props.boardData, (nv) => {
if (nv) { const screenProps = [
if(nv.barrage){ { prop: 'oneScreen', value: 1 },
barrageList.value = JSON.parse(nv.barrage) || [] { prop: 'twoScreen', value: 2 },
if(isArray(nv.barrages)){ { prop: 'threeScreen', value: 3 },
nv.barrages.forEach((v,index)=>{ { prop: 'fourScreen', value: 4 }
barrageList.value[index % 2].push(v) ]
}) const showScreenList = computed(() => {
return screenProps.filter((item) => item.value <= props.boardData.category)
})
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 }); { immediate: true, deep: true }
)
// **2. `boardData.code` WebSocket ** // **2. `boardData.code` WebSocket **
watch(() => props.boardData.code, (newCode) => { watch(
if (newCode) { () => props.boardData.code,
server.value = (import.meta.env.VITE_WEBSOCKET_URL + "/infra/ws").replace("http", "ws") + (newCode) => {
`?token=${getAccessToken()}&code=${newCode}`; if (newCode) {
} server.value =
}, { immediate: true }); (import.meta.env.VITE_WEBSOCKET_URL + '/infra/ws').replace('http', 'ws') +
`?token=${getAccessToken()}&code=${newCode}`
}
},
{ immediate: true }
)
watchEffect(() => { watchEffect(() => {
if (server.value) { if (server.value) {
socket.value = useWebSocket(server.value, { socket.value = useWebSocket(server.value, {
autoReconnect: true, autoReconnect: true,
heartbeat: false, heartbeat: false,
immediate: true, // **** immediate: true // ****
}); })
} }
}); })
// **4. WebSocket ** // **4. WebSocket **
watch( watch(
() => socket.value?.data, () => socket.value?.data,
(newData) => { (newData) => {
if (!newData) return; if (!newData) return
try { try {
const data = JSON.parse(newData); const data = JSON.parse(newData)
console.log("收到 WebSocket 消息:", data); console.log('收到 WebSocket 消息:', data)
if(data.type === 'barrage-push'){ if (data.type === 'barrage-push') {
const content = JSON.parse(data.content); const content = JSON.parse(data.content)
// barrageList.value // barrageList.value
const shortestArray = barrageList.value.reduce((minArr, currentArr) => const shortestArray = barrageList.value.reduce((minArr, currentArr) =>
currentArr.length < minArr.length ? currentArr : minArr currentArr.length < minArr.length ? currentArr : minArr
); )
// content // content
shortestArray.push(content); shortestArray.push(content)
} }
} catch (error) { } catch (error) {
message.error("处理消息发生异常:" + newData); message.error('处理消息发生异常:' + newData)
console.error(error); console.error(error)
} }
} }
); )
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
.swiper-slide-box{ .swiper-slide-box {
img{ img {
width: 100%; width: 100%;
height: 100%; height: 100%;
} }
} }
.text-box{
.text-box {
position: absolute; position: absolute;
z-index: 2; z-index: 2;
width: 100%; width: 100%;
@ -127,19 +169,22 @@ watch(
font-family: 宋体; font-family: 宋体;
color: #fdf76d; color: #fdf76d;
font-weight: bold; font-weight: bold;
.title{
.title {
font-size: 8vw; font-size: 8vw;
margin-bottom: 5vh; margin-bottom: 5vh;
text-align: center; text-align: center;
letter-spacing: 1vw; letter-spacing: 1vw;
} }
.content{
.content {
font-size: 4.2vw; font-size: 4.2vw;
text-align: center; text-align: center;
letter-spacing: 0.5vw; letter-spacing: 0.5vw;
} }
} }
.qr-box{
.qr-box {
position: absolute; position: absolute;
z-index: 2; z-index: 2;
top: 2vh; top: 2vh;
@ -148,10 +193,12 @@ watch(
flex-flow: column; flex-flow: column;
align-items: center; align-items: center;
font-size: 1vw; font-size: 1vw;
span{
span {
margin-top: 1vh; margin-top: 1vh;
} }
} }
/* 弹幕区域 */ /* 弹幕区域 */
.barrage-container { .barrage-container {
position: absolute; position: absolute;
@ -162,12 +209,51 @@ watch(
color: #000; color: #000;
z-index: 2; z-index: 2;
} }
marquee{
marquee {
margin-bottom: 2vh; margin-bottom: 2vh;
.div-box{
span{ .div-box {
span {
margin-right: 3vw; 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> </style>

Loading…
Cancel
Save