websocket消息支持 (#188)

* Release (#162)

* feat:  websocket server demo

* feat:  ws server demo dev

* feat:  ws server and mobile page

* feat:  手机端发送消息

* feat:  手机网页发送消息

* 添加了抽奖中和抽奖完成时的音效

* feat:  自定义设置弹幕服务器地址

* feat:  ws not done

* fix: 🐛 fix pr-185 #185

为播放音效添加控制

* feat:  server worker demo not done

* feat:  websocket server

* feat:  全局接收websocket消息并存储到indexdb中

---------

Co-authored-by: Silence@2024 <707261624@qq.com>
This commit is contained in:
LOG1997
2026-01-09 17:11:43 +08:00
committed by GitHub
parent 52d2fcd0cb
commit 3eac4e1aac
40 changed files with 3489 additions and 279 deletions

View File

@@ -9,11 +9,15 @@ interface Props {
submitText?: string
submitFunc?: () => void
cancelFunc?: () => void
footer?: null | 'center' | 'left' | 'right'
dialogClass?: string // 添加动态class属性
}
const props = withDefaults(defineProps<Props>(), {
cancelText: i18n.global.t('button.cancel'),
submitText: i18n.global.t('button.confirm'),
cancelFunc: () => {},
footer: 'right',
dialogClass: '',
})
const visible = defineModel('visible', {
type: Boolean,
@@ -43,7 +47,7 @@ const { title, desc, cancelText, submitText, submitFunc, cancelFunc = defaultCan
<template>
<dialog id="my_modal" ref="dialogRef" class="border-none modal">
<div class="modal-box">
<div class="modal-box" :class="[dialogClass]">
<h3 v-if="title" class="text-lg font-bold">
{{ title }}
</h3>
@@ -53,7 +57,7 @@ const { title, desc, cancelText, submitText, submitFunc, cancelFunc = defaultCan
<div>
<slot name="content" />
</div>
<div class="modal-action">
<div class="modal-action" :class="{ 'flex justify-center': footer === 'center' }">
<form method="dialog" class="flex gap-3">
<!-- if there is a button in form, it will close the modal -->
<button class="btn" @click="cancelFunc">

View File

@@ -0,0 +1,57 @@
import { createVNode, render } from 'vue'
import ErrorModalVue from './index.vue'
// 定义弹窗调用函数
function openModal(options = {}) {
// 默认配置
const defaultOptions = {
title: '提示',
desc: '',
// 确认按钮回调
onConfirm: () => {},
// 关闭按钮回调
onClose: () => {},
}
// 合并配置
const finalOptions = { ...defaultOptions, ...options }
// 创建容器
const container = document.createElement('div')
// 创建虚拟节点
const vnode = createVNode(ErrorModalVue, {
'title': finalOptions.title,
'desc': finalOptions.desc,
'modelValue': true, // 默认打开
'onUpdate:modelValue': (val: any) => {
if (!val) {
// 关闭时销毁组件
render(null, container)
document.body.removeChild(container)
}
},
'onConfirm': () => {
finalOptions.onConfirm()
},
'onClose': () => {
finalOptions.onClose()
},
})
// 渲染组件到容器
render(vnode, container)
// 将容器添加到body
document.body.appendChild(container)
// 返回关闭方法(可选)
return {
close: () => {
render(null, container)
document.body.removeChild(container)
},
}
}
export default openModal

View File

@@ -0,0 +1,127 @@
<script setup>
import { Dialog, DialogDescription, DialogPanel, DialogTitle } from '@headlessui/vue'
import { CircleAlert } from 'lucide-vue-next'
import { defineEmits, defineProps, ref, watch } from 'vue'
// 定义组件属性
const props = defineProps({
title: {
type: String,
default: '提示',
},
desc: {
type: String,
required: true,
},
// 控制弹窗显隐
modelValue: {
type: Boolean,
default: false,
},
})
// 定义事件
const emit = defineEmits(['update:modelValue', 'close'])
// 内部显隐状态
const visible = ref(props.modelValue)
// 同步外部 modelValue 变化
watch(() => props.modelValue, (val) => {
visible.value = val
})
// 关闭弹窗
function handleClose() {
visible.value = false
emit('update:modelValue', false)
emit('close')
}
</script>
<template>
<teleport to="body">
<Dialog :open="visible" class="relative z-50" @close="handleClose">
<!-- The backdrop, rendered as a fixed sibling to the panel container -->
<div class="fixed inset-0 bg-black/30" aria-hidden="true" />
<!-- Full-screen container to center the panel -->
<div class="fixed inset-0 flex w-screen items-center justify-center p-4">
<DialogPanel class="max-w-sm rounded bg-base-100 w-9/10 p-6 shadow-md">
<DialogTitle class="font-bold text-lg">
<p class="w-full flex items-center gap-2">
<CircleAlert class="text-red-500" />
<span>
{{ title || '提示' }}
</span>
</p>
</DialogTitle>
<DialogDescription class="py-4">
{{ desc }}
</DialogDescription>
<div class="mr-4 mt-4 flex justify-end">
<button class="btn" @click="handleClose">
确定
</button>
</div>
</DialogPanel>
</div>
</Dialog>
</teleport>
</template>
<style scoped>
.modal-mask {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
background: rgba(0, 0, 0, 0.2);
display: flex;
justify-content: center;
align-items: center;
z-index: 9999;
}
.modal-container {
width: 400px;
background: #fff;
border-radius: 8px;
padding: 20px;
}
.modal-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 16px;
border-bottom: 1px solid #eee;
padding-bottom: 8px;
}
.modal-header button {
background: transparent;
border: none;
font-size: 20px;
cursor: pointer;
}
.modal-footer {
margin-top: 20px;
display: flex;
justify-content: flex-end;
gap: 10px;
}
.modal-footer button {
padding: 6px 12px;
border: 1px solid #ccc;
border-radius: 4px;
cursor: pointer;
}
.modal-footer button:first-child {
background: #409eff;
color: #fff;
border-color: #409eff;
}
</style>