diff --git a/src/components/EventFetcherStatusCard.vue b/src/components/EventFetcherStatusCard.vue index ec18259..b85bfd0 100644 --- a/src/components/EventFetcherStatusCard.vue +++ b/src/components/EventFetcherStatusCard.vue @@ -22,7 +22,9 @@ const accountInfo = useAccount() {{ accountInfo?.eventFetcherOnline ? (accountInfo?.eventFetcherStatus == 'ok' ? '正常' : `异常: ${accountInfo.eventFetcherStatus}`) : '未连接' }} - - 关于 EVENT-FETCHER + diff --git a/src/data/DanmakuClient.ts b/src/data/DanmakuClient.ts index 91c5b52..9cb1393 100644 --- a/src/data/DanmakuClient.ts +++ b/src/data/DanmakuClient.ts @@ -161,11 +161,11 @@ export default class DanmakuClient { sc: [], } - public async Start(): Promise { + public async Start(): Promise<{ success: boolean; message: string }> { if (!this.client) { console.log('[OPEN-LIVE] 正在启动弹幕客户端') const result = await this.initClient() - if (result) { + if (result.success) { this.timer = setInterval(() => { this.sendHeartbeat() }, 20 * 1000) @@ -173,7 +173,10 @@ export default class DanmakuClient { return result } else { console.warn('[OPEN-LIVE] 弹幕客户端已被启动过') - return false + return { + success: false, + message: '弹幕客户端已被启动过', + } } } public Stop() { @@ -194,7 +197,8 @@ export default class DanmakuClient { } private sendHeartbeat() { if (this.client) { - (this.authInfo ? QueryPostAPI(OPEN_LIVE_API_URL + 'heartbeat', this.authInfo) : QueryGetAPI(OPEN_LIVE_API_URL + 'heartbeat-internal')).then((data) => { + const query = this.authInfo ? QueryPostAPI(OPEN_LIVE_API_URL + 'heartbeat', this.authInfo) : QueryGetAPI(OPEN_LIVE_API_URL + 'heartbeat-internal') + query.then((data) => { if (data.code != 200) { console.error('[OPEN-LIVE] 心跳失败: ' + data.message) this.client.stop() @@ -239,40 +243,56 @@ export default class DanmakuClient { } return this } - private async initClient() { + private async initClient(): Promise<{ success: boolean; message: string }> { const auth = await this.getAuthInfo() - if (auth) { - const chatClient = new ChatClientDirectOpenLive(auth) + if (auth.data) { + const chatClient = new ChatClientDirectOpenLive(auth.data) //chatClient.msgHandler = this; chatClient.CMD_CALLBACK_MAP = this.CMD_CALLBACK_MAP chatClient.start() - console.log('[OPEN-LIVE] 已连接房间: ' + auth.anchor_info.room_id) - this.roomAuthInfo.value = auth + this.roomAuthInfo.value = auth.data this.client = chatClient - return true + console.log('[OPEN-LIVE] 已连接房间: ' + auth.data.anchor_info.room_id) + return { + success: true, + message: '', + } } else { console.log('[OPEN-LIVE] 无法开启场次') - return false + return { + success: false, + message: auth.message, + } } } - private async getAuthInfo() { + private async getAuthInfo(): Promise<{ data: OpenLiveInfo | null; message: string }> { try { const data = await QueryPostAPI(OPEN_LIVE_API_URL + 'start', this.authInfo?.Code ? this.authInfo : undefined) if (data.code == 200) { console.log('[OPEN-LIVE] 已获取场次信息') - return data.data + return { + data: data.data, + message: '', + } } else { console.error('无法获取场次数据: ' + data.message) - return null + return { + data: null, + message: data.message, + } } } catch (err) { console.error(err) + + return { + data: null, + message: err?.toString() || '未知错误', + } } - return null } private CMD_CALLBACK_MAP = { - LIVE_OPEN_PLATFORM_DM: this.onDanmaku, - LIVE_OPEN_PLATFORM_SEND_GIFT: this.onGift, + LIVE_OPEN_PLATFORM_DM: this.onDanmaku.bind(this), + LIVE_OPEN_PLATFORM_SEND_GIFT: this.onGift.bind(this), } } diff --git a/src/main.ts b/src/main.ts index 9e77431..052268b 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,26 +1,33 @@ import { QueryGetAPI } from '@/api/query' import { BASE_API } from '@/data/constants' -import { createApp } from 'vue' +import { createApp, h } from 'vue' import App from './App.vue' import router from './router' import { GetSelfAccount } from './api/account' import { GetNotifactions } from './data/notifactions' - -let currentVersion -QueryGetAPI(BASE_API + 'vtsuru/version').then((version) => { - if (version.code == 200) { - currentVersion = version.data - const savedVersion = localStorage.getItem('Version') - - if (currentVersion && savedVersion && savedVersion !== currentVersion) { - alert('发现新的版本更新, 请按 Ctrl+F5 强制刷新页面') - } - - localStorage.setItem('Version', currentVersion) - } -}) +import { NText, createDiscreteApi } from 'naive-ui' createApp(App).use(router).mount('#app') GetSelfAccount() GetNotifactions() +let currentVersion: string +const { notification } = createDiscreteApi(['notification']) +QueryGetAPI(BASE_API + 'vtsuru/version').then((version) => { + if (version.code == 200) { + currentVersion = version.data + const savedVersion = localStorage.getItem('Version') + + if (currentVersion && savedVersion && savedVersion !== currentVersion) { + //alert('发现新的版本更新, 请按 Ctrl+F5 强制刷新页面') + notification.info({ + title: '发现新的版本更新', + content: '请按 Ctrl+F5 强制刷新页面', + duration: 5000, + meta: () => h(NText, { depth: 3 }, () => currentVersion), + }) + } + + localStorage.setItem('Version', currentVersion) + } +}) diff --git a/src/router/index.ts b/src/router/index.ts index d18965b..315fafa 100644 --- a/src/router/index.ts +++ b/src/router/index.ts @@ -183,19 +183,21 @@ const routes: Array = [ { path: 'live-lottery', name: 'manage-liveLottery', - component: () => import('@/views/manage/LiveLotteryManage.vue'), + component: () => import('@/views/open_live/OpenLottery.vue'), meta: { title: '直播抽奖', keepAlive: true, + danmaku: true, }, }, { path: 'song-request', name: 'manage-songRequest', - component: () => import('@/views/manage/SongRequestManage.vue'), + component: () => import('@/views/open_live/MusicRequest.vue'), meta: { title: '弹幕点歌', keepAlive: true, + danmaku: true, }, }, ], diff --git a/src/views/ManageLayout.vue b/src/views/ManageLayout.vue index 585975c..50fad1b 100644 --- a/src/views/ManageLayout.vue +++ b/src/views/ManageLayout.vue @@ -18,26 +18,36 @@ import { NAlert, NBackTop, NCountdown, + NTooltip, } from 'naive-ui' import { h, onMounted, ref } from 'vue' import { BrowsersOutline, Chatbox, Moon, MusicalNote, Sunny, AnalyticsSharp } from '@vicons/ionicons5' -import { CalendarClock24Filled, Lottery24Filled, VehicleShip24Filled, VideoAdd20Filled } from '@vicons/fluent' +import { CalendarClock24Filled, Chat24Filled, Info24Filled, Lottery24Filled, VehicleShip24Filled, VideoAdd20Filled } from '@vicons/fluent' import { isLoadingAccount, useAccount } from '@/api/account' import RegisterAndLogin from '@/components/RegisterAndLogin.vue' -import { RouterLink } from 'vue-router' +import { RouterLink, useRoute } from 'vue-router' import { useElementSize, useStorage } from '@vueuse/core' import { ACCOUNT_API_URL } from '@/data/constants' import { QueryGetAPI } from '@/api/query' import { FunctionTypes, ThemeType } from '@/api/api-models' import { isDarkMode } from '@/Utils' +import DanmakuLayout from './manage/DanmakuLayout.vue' +import { computed } from '@vue/reactivity' const accountInfo = useAccount() const message = useMessage() +const route = useRoute() const windowWidth = window.innerWidth const sider = ref() const { width } = useElementSize(sider) const themeType = useStorage('Settings.Theme', ThemeType.Auto) +const type = computed(() => { + if (route.meta.danmaku) { + return 'danmaku' + } + return '' +}) const canResendEmail = ref(false) @@ -153,33 +163,94 @@ const menuOptions = [ }, { label: () => - h( - RouterLink, - { - to: { - name: 'manage-liveLottery', + h(NText, () => [ + '弹幕相关', + h( + NTooltip, + { + style: 'padding: 0;', }, - }, - { default: () => '直播抽奖' } - ), - key: 'manage-liveLottery', - icon: renderIcon(Lottery24Filled), - //disabled: accountInfo.value?.isEmailVerified == false, - }, - { - label: () => - h( - RouterLink, - { - to: { - name: 'manage-songRequest', - }, - }, - { default: () => '弹幕点歌' } - ), - key: 'manage-songRequest', - icon: renderIcon(MusicalNote), + { + trigger: () => h(NIcon, { component: Info24Filled }), + default: () => + h( + NAlert, + { + type: 'warning', + size: 'small', + title: '可用性警告', + style: 'max-width: 600px;', + }, + () => + h('div', {}, [ + '当浏览器在后台运行时, 定时器和 Websocket 连接将受到严格限制, 这会导致弹幕接收功能无法正常工作 (详见', + h( + NButton, + { + text: true, + tag: 'a', + href: 'https://developer.chrome.com/blog/background_tabs/', + target: '_blank', + type: 'info', + }, + '此文章' + ), + '), 虽然本站已经针对此问题做出了处理, 一般情况下即使掉线了也会重连, 不过还是有可能会遗漏事件', + h('br'), + '为避免这种情况, 建议注册本站账后使用', + h( + NButton, + { + type: 'primary', + text: true, + size: 'small', + tag: 'a', + href: 'https://www.yuque.com/megghy/dez70g/vfvcyv3024xvaa1p', + target: '_blank', + }, + 'VtsuruEventFetcher' + ), + ', 否则请在使用功能时尽量保持网页在前台运行', + ]) + ), + } + ), + ]), + key: 'manage-danmaku', + icon: renderIcon(Chat24Filled), //disabled: accountInfo.value?.isEmailVerified == false, + children: [ + { + label: () => + h( + RouterLink, + { + to: { + name: 'manage-liveLottery', + }, + }, + { default: () => '直播抽奖' } + ), + key: 'manage-liveLottery', + icon: renderIcon(Lottery24Filled), + //disabled: accountInfo.value?.isEmailVerified == false, + }, + { + label: () => + h( + RouterLink, + { + to: { + name: 'manage-songRequest', + }, + }, + { default: () => '点歌' } + ), + key: 'manage-songRequest', + icon: renderIcon(MusicalNote), + //disabled: accountInfo.value?.isEmailVerified == false, + }, + ], }, ] @@ -226,7 +297,7 @@ onMounted(() => { - 回到主页 + 回到主页 @@ -260,9 +331,10 @@ onMounted(() => {
- + - + + - 通过弹幕或者SC进行点歌, 注册后可以保存和导出 (开发中 + 通过弹幕或者SC进行点歌, 注册后可以保存和导出 (这个是歌势用的点歌, 不是拿来放歌的那种!) @@ -30,7 +30,7 @@ const accountInfo = useAccount()
- 当浏览器在后台运行时定时器和 Websocket 连接将受到严格限制, 这会导致弹幕接收功能无法正常工作 (详见 + 当浏览器在后台运行时, 定时器和 Websocket 连接将受到严格限制, 这会导致弹幕接收功能无法正常工作 (详见 此文章), 虽然本站已经针对此问题做出了处理, 一般情况下即使掉线了也会重连, 不过还是有可能会遗漏事件
diff --git a/src/views/open_live/OpenLottery.vue b/src/views/open_live/OpenLottery.vue index 3381c16..532d1b9 100644 --- a/src/views/open_live/OpenLottery.vue +++ b/src/views/open_live/OpenLottery.vue @@ -317,10 +317,8 @@ onMounted(async () => { message.info('从历史记录中加载 ' + users.length + ' 位用户') } } - if (props.client) { - props.client.on('danmaku', onDanmaku) - props.client.on('gift', onGift) - } + props.client?.on('danmaku', onDanmaku) + props.client?.on('gift', onGift) timer = setInterval(updateUsers, 1000 * 10) }) onUnmounted(() => {