293 lines
7.5 KiB
Vue
Executable File
293 lines
7.5 KiB
Vue
Executable File
<script lang="ts" setup>
|
|
import SideBar from "~/components/shell/SideBar.vue";
|
|
import TitleBar from "~/components/shell/TitleBar.vue";
|
|
import FloaterBar from "~/components/shell/FloaterBar.vue";
|
|
import {useMainLayoutStore} from "~/strores/UseMainLayoutStore";
|
|
import type {UserInfoType} from "~/types/UserType";
|
|
import type {HttpType} from "~/types/baseType";
|
|
import {useSessionSignalRStore} from "~/strores/HubStore";
|
|
import {POSITION, useToast} from "vue-toastification";
|
|
import { useDataStore} from "~/strores/DataStore";
|
|
|
|
const audio = ref<any>(null);
|
|
const audio1 = ref<any>(null);
|
|
const audio2 = ref<any>(null);
|
|
const toast = useToast()
|
|
const visible = ref<boolean>(false)
|
|
const DataStore = useDataStore()
|
|
const mainRef = ref<HTMLElement>()
|
|
const mainLayoutStore = useMainLayoutStore()
|
|
const ServerList = ref<{ label: string, value: string }[]>([])
|
|
|
|
const {isScrolling} = useScroll(mainRef)
|
|
watch(isScrolling, (newValue) => {
|
|
mainLayoutStore.IsScrolling = newValue
|
|
})
|
|
onMounted(() => {
|
|
$fetch('/Api/User/Info', {
|
|
method: 'GET',
|
|
headers: {
|
|
'Authorization': 'Bearer ' + useCookie('token').value
|
|
},
|
|
baseURL: useRuntimeConfig().public.baseUrl
|
|
}).then(res => {
|
|
const data = res as HttpType<UserInfoType>
|
|
if (data.code === 200) {
|
|
mainLayoutStore.UserInfo = data.data
|
|
}
|
|
})
|
|
})
|
|
//获取服务器列表
|
|
onMounted(() => {
|
|
$fetch('/Api/Server/GetServerList', {
|
|
method: 'GET',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'Authorization': 'Bearer ' + useCookie('token').value
|
|
},
|
|
baseURL: useRuntimeConfig().public.baseUrl
|
|
}).then(res => {
|
|
const data = res as { name: string, id: string }[]
|
|
ServerList.value = data.map(item => ({label: item.name, value: item.id}))
|
|
if (!mainLayoutStore.SelectServer.value) {
|
|
mainLayoutStore.SelectServer = ServerList.value[0];
|
|
}
|
|
})
|
|
})
|
|
const signalR = useSessionSignalRStore();
|
|
|
|
|
|
//有且运行一次
|
|
onMounted(() => {
|
|
signalR.initConnection();
|
|
signalR.connection?.on('userJoined', (data: any) => {
|
|
mainLayoutStore.setOnlineUsers(data)
|
|
})
|
|
signalR.connection?.on('userLeft', (data: any) => {
|
|
const uses = mainLayoutStore.OnlineUsers.filter(item => item.id !== data)
|
|
mainLayoutStore.setOnlineUsers(uses)
|
|
})
|
|
signalR.connection?.on('ReceiveData', (id: string, type: string, message: string) => {
|
|
if (id !== mainLayoutStore.SelectServer.value) return
|
|
DataStore.setData(type, message)
|
|
})
|
|
signalR.connection?.on('ReceiveWaring', (value: string,valueName) => {
|
|
// //两位小数
|
|
audio2.value.currentTime = 0;
|
|
audio2.value && audio2.value.click()
|
|
audio2.value && audio2.value.play()
|
|
toast.error(`你设定的${valueName}已经达到警告阈值,当前值为 ${value}`,{
|
|
position: POSITION.BOTTOM_RIGHT,
|
|
timeout:5000,
|
|
})
|
|
})
|
|
signalR.connection?.on('ReceiveNotify', (value: string,valueName) => {
|
|
//两位小数
|
|
audio1.value.currentTime = 0;
|
|
const {
|
|
isSupported,
|
|
show,
|
|
} = useWebNotification({
|
|
title: `你设定的${valueName}已经达到通知阈值,当前值为 ${value}`,
|
|
dir: 'auto',
|
|
lang: 'en',
|
|
renotify: true,
|
|
tag: '通知',
|
|
})
|
|
if(isSupported.value){
|
|
Notification.requestPermission().then(res => {
|
|
console.log(res)
|
|
})
|
|
show()
|
|
}
|
|
audio1.value && audio1.value.click()
|
|
audio1.value && audio1.value.play()
|
|
toast.info(`你设定的${valueName}已经达到通知阈值,当前值为 ${value}`,{
|
|
position: POSITION.BOTTOM_RIGHT,
|
|
timeout:5000,
|
|
})
|
|
})
|
|
signalR.connection?.on("sendMessage", (id, message) => {
|
|
audio.value.currentTime = 0;
|
|
audio.value && audio.value.click()
|
|
audio.value && audio.value.play()
|
|
const user = mainLayoutStore.OnlineUsers.find(user => user.id === id)
|
|
toast.info(`${user?.nickName}向你发送了一条消息`, {
|
|
position: POSITION.BOTTOM_RIGHT,
|
|
})
|
|
console.log(`${user?.nickName}对你说: ${message}`)
|
|
setTimeout(() => {
|
|
audio.value && audio.value.pause()
|
|
const speech = useSpeechSynthesis(`${user?.nickName}对你说:${message}`, {
|
|
voice: window.speechSynthesis.getVoices().find(v => v.name === 'Microsoft Xiaoxiao Online (Natural) - Chinese (Mainland) (zh-CN)') ?? window.speechSynthesis.getVoices().find(v => v.lang.includes('zh-CN')) ?? window.speechSynthesis.getVoices()[0],
|
|
lang: 'zh-CN',
|
|
pitch: 1,
|
|
rate: 1,
|
|
})
|
|
speech.speak()
|
|
}, 1000)
|
|
|
|
})
|
|
})
|
|
onUnmounted(() => {
|
|
signalR.connection?.stop()
|
|
})
|
|
|
|
let isShift = false
|
|
onKeyStroke('Shift', (e) => {
|
|
e.preventDefault()
|
|
setTimeout(() => {
|
|
isShift = true
|
|
},100)
|
|
setTimeout(() => {
|
|
isShift = false
|
|
},200)
|
|
if(isShift){
|
|
visible.value = !visible.value
|
|
}
|
|
},{eventName: 'keyup'})
|
|
|
|
</script>
|
|
|
|
<template>
|
|
<section class="layout">
|
|
<audio ref="audio">
|
|
<source src="/audios/idle1.mp3" type="audio/mpeg">
|
|
您的浏览器不支持 audio 元素。
|
|
</audio>
|
|
<audio ref="audio1">
|
|
<source src="/audios/audio_0a383a4c11.mp3" type="audio/mpeg">
|
|
您的浏览器不支持 audio 元素。
|
|
</audio>
|
|
<audio ref="audio2">
|
|
<source src="/audios/bibibi.mp3" type="audio/mpeg">
|
|
您的浏览器不支持 audio 元素。
|
|
</audio>
|
|
<SideBar/>
|
|
<TitleBar/>
|
|
<n-modal v-model:show.lazy="visible" auto-focus >
|
|
<Term/>
|
|
</n-modal>
|
|
<div class="main-box">
|
|
<n-back-top :right="40" style="z-index: 200"/>
|
|
<div ref="mainRef" class="main">
|
|
<div class="hero">
|
|
<div class="hero-box">
|
|
<h3>你好 {{ mainLayoutStore.UserInfo.nickName }}</h3>
|
|
<p>欢迎来到龙盾云御,这是基于Nuxt+Vue3的后台管理系统</p>
|
|
</div>
|
|
<div class="server-dropdown">
|
|
<n-select v-model:value="mainLayoutStore.SelectServer.value" :options="ServerList" />
|
|
</div>
|
|
<HeroBox/>
|
|
</div>
|
|
<div class="out">
|
|
<slot/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<FloaterBar/>
|
|
</section>
|
|
</template>
|
|
|
|
<style lang="scss" scoped>
|
|
@import "base";
|
|
|
|
.layout {
|
|
width: 100%;
|
|
height: 100vh;
|
|
max-width: 100vw;
|
|
display: grid;
|
|
grid:
|
|
"sidebar titlebar" auto
|
|
"sidebar main" 1fr
|
|
"sidebar folder" auto
|
|
/ auto 1fr;
|
|
grid-auto-flow: row dense;
|
|
}
|
|
|
|
.main-box {
|
|
grid-area: main;
|
|
overflow-y: auto;
|
|
|
|
&::-webkit-scrollbar {
|
|
width: 0;
|
|
}
|
|
}
|
|
|
|
.main {
|
|
display: grid;
|
|
width: 100%;
|
|
grid-template-rows: 125px 75px 1fr;
|
|
will-change: scroll-position;
|
|
padding-bottom: 400px;
|
|
}
|
|
|
|
.hero {
|
|
grid-row: 1/3;
|
|
grid-column: 1;
|
|
overflow: hidden;
|
|
border-radius: 0 0 $radius*2 $radius*2;
|
|
position: relative;
|
|
|
|
.hero-box {
|
|
position: absolute;
|
|
z-index: 11;
|
|
left: $padding*3;
|
|
top: $padding*3;
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: $gap;
|
|
|
|
h3, p {
|
|
color: #FFF;
|
|
font-size: 30px;
|
|
font-style: normal;
|
|
font-weight: 700;
|
|
line-height: normal;
|
|
}
|
|
|
|
p {
|
|
font-size: 23px;
|
|
font-style: normal;
|
|
font-weight: 500;
|
|
line-height: 130%; /* 29.9px */
|
|
}
|
|
}
|
|
}
|
|
|
|
.server-dropdown {
|
|
position: absolute;
|
|
z-index: 11;
|
|
right: $padding*3;
|
|
top: $padding*3;
|
|
:deep(.n-select>.n-base-selection){
|
|
.n-base-selection__border{
|
|
border: unset;
|
|
}
|
|
.n-base-selection-label{
|
|
background: rgba(255, 255, 255, 0.1);
|
|
backdrop-filter: blur(400px);
|
|
>div{
|
|
color: #FFF;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
.out {
|
|
grid-row: 2/4;
|
|
grid-column: 1;
|
|
z-index: 10;
|
|
width: 100%;
|
|
display: flex;
|
|
padding: 0 20px 20px;
|
|
}
|
|
|
|
:deep(.n-modal-mask){
|
|
backdrop-filter: blur(100px);
|
|
background: rgba(0,0,0,.7);
|
|
}
|
|
|
|
</style> |