mirror of
https://github.com/Megghy/vtsuru.live.git
synced 2025-12-06 18:36:55 +08:00
feat: 添加更新日志功能;优化组件和状态管理;修复部分逻辑错误
This commit is contained in:
3
default.d.ts
vendored
3
default.d.ts
vendored
@@ -1,5 +1,5 @@
|
||||
|
||||
import type { LoadingBarProviderInst, MessageProviderInst, ModalProviderInst, NotificationProviderInst } from 'naive-ui'
|
||||
import type { DialogProviderInst, LoadingBarProviderInst, MessageProviderInst, ModalProviderInst, NotificationProviderInst } from 'naive-ui'
|
||||
import type { useRoute } from 'vue-router'
|
||||
|
||||
declare module 'vue3-aplayer' {
|
||||
@@ -23,5 +23,6 @@ declare global {
|
||||
$modal: ModalProviderInst
|
||||
$mitt: Emitter<MittType>
|
||||
$notification: NotificationProviderInst
|
||||
$dialog: DialogProviderInst
|
||||
}
|
||||
}
|
||||
|
||||
@@ -992,7 +992,7 @@
|
||||
<NIcon :component="BarChartOutline" /> {{ webfetcher.sessionEventCount?.toLocaleString() ?? 0 }}
|
||||
</NStatistic>
|
||||
<NStatistic label="已发送">
|
||||
{{ ((webfetcher.bytesSentSession ?? 0) / 1024).toFixed(2) }} KB <!-- Assuming exposed -->
|
||||
{{ ((webfetcher.bytesSentSession ?? 0) / 1024 / 1024).toFixed(2) }} Mb <!-- Assuming exposed -->
|
||||
</NStatistic>
|
||||
</NFlex>
|
||||
</NCard>
|
||||
|
||||
@@ -10,6 +10,10 @@
|
||||
const webfetcher = useWebFetcher();
|
||||
|
||||
const coverRef = ref();
|
||||
const isHover = ref(false);
|
||||
const roomCover = computed(() => {
|
||||
return isHover.value ? roomInfo.value?.keyframe : roomInfo.value?.user_cover;
|
||||
});
|
||||
const { width, height } = useElementSize(coverRef);
|
||||
|
||||
function logout() {
|
||||
@@ -90,8 +94,8 @@
|
||||
<template #header>
|
||||
<NSpace align="center">
|
||||
直播状态
|
||||
<NTag :type="!accountInfo.streamerInfo?.isStreaming ? 'error' : 'success'">
|
||||
{{ !accountInfo.streamerInfo?.isStreaming ? '未直播' : '直播中' }}
|
||||
<NTag :type="roomInfo?.live_status === 1 ? 'success' : 'error'">
|
||||
{{ roomInfo?.live_status === 1 ? '直播中' : '未直播' }}
|
||||
</NTag>
|
||||
</NSpace>
|
||||
<NText
|
||||
@@ -102,13 +106,13 @@
|
||||
</NText>
|
||||
</template>
|
||||
<div
|
||||
v-if="roomInfo?.user_cover"
|
||||
v-if="roomCover"
|
||||
style="position: relative"
|
||||
>
|
||||
<div style="position: relative; width: 100%; max-width: 500px;">
|
||||
<NImage
|
||||
ref="coverRef"
|
||||
:src="roomInfo?.user_cover"
|
||||
:src="roomCover"
|
||||
style="width: 100%; opacity: 0.5; border-radius: 8px;"
|
||||
referrerpolicy="no-referrer"
|
||||
:img-props="{ referrerpolicy: 'no-referrer', style: { width: '100%' } }"
|
||||
@@ -118,6 +122,8 @@
|
||||
style="position: absolute; z-index: 1; top: 0; width: 100%; background: linear-gradient(to top, rgba(0,0,0,0.8), rgba(0,0,0,0.2), transparent)"
|
||||
:style="{ height: `${height}px`, maxWidth: '500px', cursor: 'pointer' }"
|
||||
@click="openUrl(`https://live.bilibili.com/${accountInfo.biliRoomId}`)"
|
||||
@mouseenter="isHover = true"
|
||||
@mouseleave="isHover = false"
|
||||
/>
|
||||
<NText style="position: absolute; bottom: 12px; left: 16px; z-index: 2; color: white; font-size: 18px">
|
||||
{{ roomInfo?.title }}
|
||||
|
||||
@@ -117,14 +117,6 @@ import { isTauri } from '@/data/constants';
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<NAlert
|
||||
v-if="!isTauri"
|
||||
type="error"
|
||||
title="错误"
|
||||
>
|
||||
此应用在 Tauri 环境外运行,无法使用
|
||||
</NAlert>
|
||||
<template v-else>
|
||||
<WindowBar />
|
||||
|
||||
<div
|
||||
@@ -279,7 +271,6 @@ import { isTauri } from '@/data/constants';
|
||||
</div>
|
||||
</NLayoutContent>
|
||||
</NLayout>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
@@ -333,6 +333,9 @@ watch(minimizeOnStart, (newValue) => {
|
||||
Tauri 客户端
|
||||
</NButton>
|
||||
</p>
|
||||
<p>
|
||||
反馈: 🐧 873260337
|
||||
</p>
|
||||
<!-- Add more about info -->
|
||||
</NCard>
|
||||
</template>
|
||||
|
||||
@@ -4,6 +4,7 @@ import { info, error } from '@tauri-apps/plugin-log';
|
||||
import { QueryBiliAPI } from './utils'; // 假设 Bili API 工具路径
|
||||
import { BiliRoomInfo, BiliStreamingInfo, FetcherStatisticData } from './models'; // 假设模型路径
|
||||
import { useTauriStore } from '../store/useTauriStore';
|
||||
import { useAccount } from '@/api/account';
|
||||
// import { useAccount } from '@/api/account'; // 如果需要账户信息
|
||||
|
||||
// const accountInfo = useAccount(); // 如果需要
|
||||
@@ -185,10 +186,8 @@ export async function getHistoricalStatistics(days: number = 7): Promise<Fetcher
|
||||
* 更新房间和直播流信息
|
||||
*/
|
||||
async function updateRoomAndStreamingInfo() {
|
||||
// 需要一个房间ID来查询,这个ID可能来自设置、登录信息或固定配置
|
||||
// const roomId = accountInfo.value?.roomid ?? settings.value.roomId; // 示例:获取房间ID
|
||||
const roomId = 21484828; // !!! 示例:这里需要替换成实际获取房间ID的逻辑 !!!
|
||||
if (!roomId) {
|
||||
const account = useAccount();
|
||||
if (!account.value.biliRoomId) {
|
||||
// error("无法获取房间ID以更新直播信息");
|
||||
return;
|
||||
}
|
||||
@@ -196,7 +195,7 @@ async function updateRoomAndStreamingInfo() {
|
||||
try {
|
||||
// 查询房间基本信息
|
||||
const roomRes = await QueryBiliAPI(
|
||||
`https://api.live.bilibili.com/room/v1/Room/get_info?room_id=${roomId}`
|
||||
`https://api.live.bilibili.com/room/v1/Room/get_info?room_id=${account.value.biliRoomId}`
|
||||
);
|
||||
const json = await roomRes.json();
|
||||
if (json.code === 0) {
|
||||
|
||||
@@ -4,6 +4,18 @@ import { isPermissionGranted, onAction, sendNotification } from "@tauri-apps/plu
|
||||
import { openUrl } from "@tauri-apps/plugin-opener";
|
||||
import { CN_HOST } from "@/data/constants";
|
||||
|
||||
export function onReceivedNotification(type: string, data: any) {
|
||||
switch (type) {
|
||||
case 'question-box':
|
||||
|
||||
onReceivedQuestion(data);
|
||||
break;
|
||||
|
||||
default:
|
||||
console.warn(`Unhandled notification type: ${type}`);
|
||||
}
|
||||
}
|
||||
|
||||
export async function onReceivedQuestion(question: QAInfo) {
|
||||
const setting = useSettings();
|
||||
if (setting.settings.notificationSettings.enableTypes.includes("question-box")) {
|
||||
|
||||
1
src/components.d.ts
vendored
1
src/components.d.ts
vendored
@@ -57,6 +57,7 @@ declare module 'vue' {
|
||||
SongPlayer: typeof import('./components/SongPlayer.vue')['default']
|
||||
TempComponent: typeof import('./components/TempComponent.vue')['default']
|
||||
TurnstileVerify: typeof import('./components/TurnstileVerify.vue')['default']
|
||||
UpdateNoteContainer: typeof import('./components/UpdateNoteContainer.vue')['default']
|
||||
UserBasicInfoCard: typeof import('./components/UserBasicInfoCard.vue')['default']
|
||||
VEditor: typeof import('./components/VEditor.vue')['default']
|
||||
VideoCollectInfoCard: typeof import('./components/VideoCollectInfoCard.vue')['default']
|
||||
|
||||
@@ -139,7 +139,7 @@ const showContent = ref(!isViolation)
|
||||
<br>
|
||||
</template>
|
||||
|
||||
<NText :style="{ filter: showContent ? '' : 'blur(3.7px)', cursor: showContent ? '' : 'pointer' }">
|
||||
<NText :style="{ filter: showContent ? '' : 'blur(3.7px)', cursor: showContent ? '' : 'pointer', whiteSpace: 'pre-wrap' }">
|
||||
<NButton
|
||||
v-if="isViolation"
|
||||
size="small"
|
||||
|
||||
@@ -16,6 +16,7 @@ onMounted(() => {
|
||||
window.$route = useRoute()
|
||||
window.$modal = useModal()
|
||||
window.$notification = useNotification()
|
||||
window.$dialog = useDialog()
|
||||
const providerStore = useLoadingBarStore()
|
||||
providerStore.setLoadingBar(window.$loadingBar)
|
||||
})
|
||||
|
||||
93
src/components/UpdateNoteContainer.vue
Normal file
93
src/components/UpdateNoteContainer.vue
Normal file
@@ -0,0 +1,93 @@
|
||||
<script setup lang="ts">
|
||||
import { updateNoteItemContentType, updateNotes } from '@/data/UpdateNote';
|
||||
import { NDivider, NGrid } from 'naive-ui';
|
||||
import { VNode } from 'vue';
|
||||
|
||||
const currentVer = '1'
|
||||
const savedVer = useStorage('UpdateNoteVer', 0)
|
||||
function renderContent(content: updateNoteItemContentType): VNode | string | undefined {
|
||||
if (Array.isArray(content)) {
|
||||
return h('div', { style: { whiteSpace: 'pre-wrap' } }, content.map(item => renderContent(item)))
|
||||
}
|
||||
if (typeof content === 'string') {
|
||||
return content
|
||||
}
|
||||
if (typeof content === 'function') {
|
||||
return content()
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<NScrollbar
|
||||
style="max-height: 80vh;"
|
||||
trigger="none"
|
||||
>
|
||||
<NFlex vertical>
|
||||
<div
|
||||
v-for="item in updateNotes"
|
||||
:key="item.ver"
|
||||
>
|
||||
<NDivider title-placement="left">
|
||||
{{ item.date }}
|
||||
</NDivider>
|
||||
<NGrid
|
||||
x-gap="10"
|
||||
>
|
||||
<template
|
||||
v-for="note in item.items"
|
||||
:key="note.title"
|
||||
>
|
||||
<NGridItem span="6">
|
||||
<div style="">
|
||||
<NTag
|
||||
v-if="note.type === 'fix'"
|
||||
type="info"
|
||||
round
|
||||
:bordered="false"
|
||||
>
|
||||
错误修复
|
||||
</NTag>
|
||||
<NTag
|
||||
v-else-if="note.type === 'new'"
|
||||
type="success"
|
||||
round
|
||||
:bordered="false"
|
||||
>
|
||||
功能添加
|
||||
</NTag>
|
||||
<NTag
|
||||
v-else-if="note.type === 'optimize'"
|
||||
:color="{ textColor: '#000', color: '#f0ad4e', borderColor: '#f0ad4e' }"
|
||||
round
|
||||
:bordered="false"
|
||||
>
|
||||
功能优化
|
||||
</NTag>
|
||||
<NTag
|
||||
v-else-if="note.type === 'other'"
|
||||
type="error"
|
||||
round
|
||||
:bordered="false"
|
||||
>
|
||||
其他
|
||||
</NTag>
|
||||
</div>
|
||||
</NGridItem>
|
||||
<NGridItem span="18">
|
||||
<NFlex vertical>
|
||||
<template
|
||||
v-for="content in note.content"
|
||||
:key="content"
|
||||
>
|
||||
<component :is="renderContent(content)" />
|
||||
</template>
|
||||
</NFlex>
|
||||
</NGridItem>
|
||||
</template>
|
||||
</NGrid>
|
||||
</div>
|
||||
</NFlex>
|
||||
</NScrollbar>
|
||||
</template>
|
||||
155
src/data/Initializer.ts
Normal file
155
src/data/Initializer.ts
Normal file
@@ -0,0 +1,155 @@
|
||||
import { GetSelfAccount, useAccount, UpdateAccountLoop } from "@/api/account";
|
||||
import { QueryGetAPI } from "@/api/query";
|
||||
import { useAuthStore } from "@/store/useAuthStore";
|
||||
import { useNotificationStore } from "@/store/useNotificationStore";
|
||||
import { createDiscreteApi, NText, NFlex, NButton } from "naive-ui";
|
||||
import { BASE_API_URL, isTauri, apiFail } from "./constants";
|
||||
import { GetNotifactions } from "./notifactions";
|
||||
import HyperDX from '@hyperdx/browser'
|
||||
import EasySpeech from "easy-speech";
|
||||
import { checkUpdateNote } from "./UpdateNote";
|
||||
|
||||
let currentVersion: string
|
||||
let isHaveNewVersion = false
|
||||
|
||||
const { notification } = createDiscreteApi(['notification'])
|
||||
|
||||
export function InitVTsuru() {
|
||||
QueryGetAPI<string>(`${BASE_API_URL}vtsuru/version`)
|
||||
.then((version) => {
|
||||
if (version.code == 200) {
|
||||
currentVersion = version.data
|
||||
const savedVersion = localStorage.getItem('Version')
|
||||
localStorage.setItem('Version', currentVersion)
|
||||
|
||||
if (currentVersion && savedVersion && savedVersion !== currentVersion) {
|
||||
setTimeout(() => {
|
||||
location.reload()
|
||||
}, 1000)
|
||||
// alert('发现新的版本更新, 请按 Ctrl+F5 强制刷新页面')
|
||||
notification.info({
|
||||
title: '发现新的版本更新',
|
||||
content: '将自动刷新页面',
|
||||
duration: 5000,
|
||||
meta: () => h(NText, { depth: 3 }, () => currentVersion),
|
||||
})
|
||||
}
|
||||
else {
|
||||
InitVersionCheck();
|
||||
}
|
||||
}
|
||||
InitOther();
|
||||
})
|
||||
.catch(() => {
|
||||
apiFail.value = true
|
||||
console.log('默认API调用失败, 切换至故障转移节点')
|
||||
})
|
||||
.finally(async () => {
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
async function InitOther() {
|
||||
if (process.env.NODE_ENV !== 'development') {
|
||||
HyperDX.init({
|
||||
apiKey: '7d1eb66c-24b8-445e-a406-dc2329fa9423',
|
||||
service: 'vtsuru.live',
|
||||
tracePropagationTargets: [/vtsuru.suki.club/i], // Set to link traces from frontend to backend requests
|
||||
consoleCapture: true, // Capture console logs (default false)
|
||||
advancedNetworkCapture: true, // Capture full HTTP request/response headers and bodies (default false)
|
||||
})
|
||||
}
|
||||
// 加载其他数据
|
||||
InitTTS()
|
||||
await GetSelfAccount()
|
||||
const account = useAccount()
|
||||
const useAuth = useAuthStore()
|
||||
if (account.value.id) {
|
||||
if (account.value.biliUserAuthInfo && !useAuth.currentToken) {
|
||||
useAuth.currentToken = account.value.biliUserAuthInfo.token
|
||||
}
|
||||
HyperDX.setGlobalAttributes({
|
||||
userId: account.value.id.toString(),
|
||||
userName: account.value.name,
|
||||
})
|
||||
}
|
||||
useAuth.getAuthInfo()
|
||||
GetNotifactions()
|
||||
UpdateAccountLoop()
|
||||
|
||||
useNotificationStore().init()
|
||||
}
|
||||
function InitVersionCheck() {
|
||||
setInterval(() => {
|
||||
if (isHaveNewVersion) {
|
||||
return
|
||||
}
|
||||
QueryGetAPI<string>(`${BASE_API_URL}vtsuru/version`).then(
|
||||
(keepCheckData) => {
|
||||
if (
|
||||
keepCheckData.code == 200
|
||||
&& keepCheckData.data != currentVersion
|
||||
) {
|
||||
isHaveNewVersion = true
|
||||
currentVersion = keepCheckData.data
|
||||
localStorage.setItem('Version', currentVersion)
|
||||
console.log(`[vtsuru] 发现新版本: ${currentVersion}`)
|
||||
|
||||
const url = new URL(window.location.href)
|
||||
const path = url.pathname
|
||||
|
||||
if (!path.startsWith('/obs')) {
|
||||
if (isTauri) {
|
||||
location.reload();
|
||||
}
|
||||
else {
|
||||
const n = notification.info({
|
||||
title: '发现新的版本更新',
|
||||
content: '是否现在刷新?',
|
||||
meta: () => h(NText, { depth: 3 }, () => currentVersion),
|
||||
action: () =>
|
||||
h(NFlex, null, () => [
|
||||
h(
|
||||
NButton,
|
||||
{
|
||||
text: true,
|
||||
type: 'primary',
|
||||
onClick: () => location.reload(),
|
||||
size: 'small',
|
||||
},
|
||||
{ default: () => '刷新' },
|
||||
),
|
||||
h(
|
||||
NButton,
|
||||
{
|
||||
text: true,
|
||||
onClick: () => n.destroy(),
|
||||
size: 'small',
|
||||
},
|
||||
{ default: () => '稍后' },
|
||||
),
|
||||
]),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
}, 60 * 1000)
|
||||
}
|
||||
function InitTTS() {
|
||||
try {
|
||||
const result = EasySpeech.detect()
|
||||
if (result.speechSynthesis) {
|
||||
EasySpeech.init({ maxTimeout: 5000, interval: 250 })
|
||||
.then(() => console.log('[SpeechSynthesis] 已加载tts服务'))
|
||||
.catch(e => console.error(e))
|
||||
}
|
||||
else {
|
||||
console.log('[SpeechSynthesis] 当前浏览器不支持tts服务')
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
console.log('[SpeechSynthesis] 当前浏览器不支持tts服务')
|
||||
}
|
||||
}
|
||||
91
src/data/UpdateNote.ts
Normal file
91
src/data/UpdateNote.ts
Normal file
@@ -0,0 +1,91 @@
|
||||
import UpdateNoteContainer from "@/components/UpdateNoteContainer.vue";
|
||||
import { NButton, NImage, NTag } from "naive-ui";
|
||||
import { VNode } from "vue";
|
||||
import { FETCH_API } from "./constants";
|
||||
|
||||
export const updateNotes: updateNoteType[] = [
|
||||
{
|
||||
ver: 2,
|
||||
date: '2025.4.8',
|
||||
items: [
|
||||
{
|
||||
type: 'new',
|
||||
title: 'EventFetcher Tauri 客户端开始测试',
|
||||
content: [
|
||||
['比当前所有 EventFetcher 部署方法都更要简单且支持扫码登录的客户端开始测试力, 支持Windows, Linux, MacOS (后两个没测试过'],
|
||||
[
|
||||
'如果对此感兴趣的话可以使用 ',
|
||||
() => h(NButton, {
|
||||
text: true, tag: 'a', href: FETCH_API + 'https://github.com/Megghy/vtsuru-fetcher-client/releases/download/app-v0.1.0/vtsuru-fetcher-client_0.1.0_x64-setup.exe', target: '_blank', type: 'info'
|
||||
}, () => '这个链接'),
|
||||
' 下载Windows客户端, 其他平台请在下面的客户端 Repo 中的 Release 下载',
|
||||
],
|
||||
[
|
||||
'当前可能存在一些问题, 可以加入秋秋群 873260337 进行反馈, 有功能需求也可以提出'
|
||||
],
|
||||
[],
|
||||
[
|
||||
'源码: ',
|
||||
() => h(NButton, {
|
||||
text: true, tag: 'a', href: 'https://github.com/Megghy/vtsuru-fetvher-client', target: '_blank', type: 'info'
|
||||
}, () => ' 客户端 Repo '),
|
||||
' | ',
|
||||
() => h(NButton, {
|
||||
text: true, tag: 'a', href: 'https://github.com/Megghy/vtsuru.live/tree/master/src/client', target: '_blank', type: 'info'
|
||||
}, () => ' UI/逻辑 '),
|
||||
],
|
||||
[
|
||||
() => h(NImage, { src: 'https://pan.suki.club/d/vtsuru/imgur/01295402D7FBBF192FE5608179A4A7A6.png', width: 200 }),
|
||||
]
|
||||
]
|
||||
}
|
||||
],
|
||||
},
|
||||
{
|
||||
ver: 1,
|
||||
date: '2025.3.18',
|
||||
items: [
|
||||
{
|
||||
title: '歌单',
|
||||
type: 'optimize',
|
||||
content: [
|
||||
[
|
||||
'新增一个歌单样式: 列表',
|
||||
() => h(NImage, { src: 'https://pan.suki.club/d/vtsuru/imgur/QQ20250408-134631.png', width: 300, height: 200 }),
|
||||
]
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
export const currentUpdateNoteVer = updateNotes.sort((a, b) => b.ver - a.ver)[0].ver;
|
||||
export const currentUpdateNote = updateNotes.sort((a, b) => b.ver - a.ver)[0].items;
|
||||
export const savedUpdateNoteVer = useStorage('UpdateNoteVer', 0);
|
||||
|
||||
export function checkUpdateNote() {
|
||||
if (savedUpdateNoteVer.value < currentUpdateNoteVer) {
|
||||
window.$dialog.create({
|
||||
title: '更新日志',
|
||||
content: () => h(UpdateNoteContainer),
|
||||
negativeText: '确定',
|
||||
positiveText: '下次更新前不再提示',
|
||||
onPositiveClick: () => {
|
||||
savedUpdateNoteVer.value = currentUpdateNoteVer;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export type updateType = 'fix' | 'new' | 'optimize' | 'other';
|
||||
export type updateNoteType = {
|
||||
ver: number;
|
||||
date: string;
|
||||
items: updateNoteItemType[];
|
||||
};
|
||||
export type updateNoteItemType = {
|
||||
title?: string | (() => VNode);
|
||||
type: updateType;
|
||||
content: updateNoteItemContentType[];
|
||||
};
|
||||
export type updateNoteItemContentType = (() => VNode) | string | updateNoteItemContentType[];
|
||||
156
src/main.ts
156
src/main.ts
@@ -1,158 +1,18 @@
|
||||
import { QueryGetAPI } from '@/api/query'
|
||||
import { apiFail, BASE_API_URL, isTauri } from '@/data/constants'
|
||||
import HyperDX from '@hyperdx/browser'
|
||||
import EasySpeech from 'easy-speech'
|
||||
import { createDiscreteApi, NButton, NFlex, NText } from 'naive-ui'
|
||||
import { createPinia } from 'pinia'
|
||||
import { createApp, h } from 'vue'
|
||||
import { GetSelfAccount, UpdateAccountLoop, useAccount } from './api/account'
|
||||
import App from './App.vue'
|
||||
import { GetNotifactions } from './data/notifactions'
|
||||
import emitter from './mitt'
|
||||
import router from './router'
|
||||
import { useAuthStore } from './store/useAuthStore'
|
||||
import { useNotificationStore } from './store/useNotificationStore'
|
||||
import EasySpeech from 'easy-speech';
|
||||
import { createPinia } from 'pinia';
|
||||
import { createApp } from 'vue';
|
||||
import App from './App.vue';
|
||||
import { InitVTsuru } from './data/Initializer';
|
||||
import emitter from './mitt';
|
||||
import router from './router';
|
||||
|
||||
const pinia = createPinia()
|
||||
export const getPinia = () => pinia
|
||||
|
||||
QueryGetAPI<string>(`${BASE_API_URL}vtsuru/version`)
|
||||
.then((version) => {
|
||||
if (version.code == 200) {
|
||||
currentVersion = version.data
|
||||
const savedVersion = localStorage.getItem('Version')
|
||||
localStorage.setItem('Version', currentVersion)
|
||||
|
||||
if (currentVersion && savedVersion && savedVersion !== currentVersion) {
|
||||
setTimeout(() => {
|
||||
location.reload()
|
||||
}, 1000)
|
||||
// alert('发现新的版本更新, 请按 Ctrl+F5 强制刷新页面')
|
||||
notification.info({
|
||||
title: '发现新的版本更新',
|
||||
content: '将自动刷新页面',
|
||||
duration: 5000,
|
||||
meta: () => h(NText, { depth: 3 }, () => currentVersion),
|
||||
})
|
||||
}
|
||||
else {
|
||||
setInterval(() => {
|
||||
if (isHaveNewVersion) {
|
||||
return
|
||||
}
|
||||
QueryGetAPI<string>(`${BASE_API_URL}vtsuru/version`).then(
|
||||
(keepCheckData) => {
|
||||
if (
|
||||
keepCheckData.code == 200
|
||||
&& keepCheckData.data != currentVersion
|
||||
) {
|
||||
isHaveNewVersion = true
|
||||
currentVersion = keepCheckData.data
|
||||
localStorage.setItem('Version', currentVersion)
|
||||
console.log(`[vtsuru] 发现新版本: ${currentVersion}`)
|
||||
|
||||
const url = new URL(window.location.href)
|
||||
const path = url.pathname
|
||||
|
||||
if (!path.startsWith('/obs')) {
|
||||
if (isTauri) {
|
||||
location.reload();
|
||||
}
|
||||
else {
|
||||
const n = notification.info({
|
||||
title: '发现新的版本更新',
|
||||
content: '是否现在刷新?',
|
||||
meta: () => h(NText, { depth: 3 }, () => currentVersion),
|
||||
action: () =>
|
||||
h(NFlex, null, () => [
|
||||
h(
|
||||
NButton,
|
||||
{
|
||||
text: true,
|
||||
type: 'primary',
|
||||
onClick: () => location.reload(),
|
||||
size: 'small',
|
||||
},
|
||||
{ default: () => '刷新' },
|
||||
),
|
||||
h(
|
||||
NButton,
|
||||
{
|
||||
text: true,
|
||||
onClick: () => n.destroy(),
|
||||
size: 'small',
|
||||
},
|
||||
{ default: () => '稍后' },
|
||||
),
|
||||
]),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
}, 60 * 1000)
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
apiFail.value = true
|
||||
console.log('默认API调用失败, 切换至故障转移节点')
|
||||
})
|
||||
.finally(async () => {
|
||||
if (process.env.NODE_ENV !== 'development') {
|
||||
HyperDX.init({
|
||||
apiKey: '7d1eb66c-24b8-445e-a406-dc2329fa9423',
|
||||
service: 'vtsuru.live',
|
||||
tracePropagationTargets: [/vtsuru.suki.club/i], // Set to link traces from frontend to backend requests
|
||||
consoleCapture: true, // Capture console logs (default false)
|
||||
advancedNetworkCapture: true, // Capture full HTTP request/response headers and bodies (default false)
|
||||
})
|
||||
}
|
||||
// 加载其他数据
|
||||
InitTTS()
|
||||
await GetSelfAccount()
|
||||
const account = useAccount()
|
||||
const useAuth = useAuthStore()
|
||||
if (account.value.id) {
|
||||
if (account.value.biliUserAuthInfo && !useAuth.currentToken) {
|
||||
useAuth.currentToken = account.value.biliUserAuthInfo.token
|
||||
}
|
||||
HyperDX.setGlobalAttributes({
|
||||
userId: account.value.id.toString(),
|
||||
userName: account.value.name,
|
||||
})
|
||||
}
|
||||
useAuth.getAuthInfo()
|
||||
GetNotifactions()
|
||||
UpdateAccountLoop()
|
||||
})
|
||||
|
||||
const app = createApp(App)
|
||||
app.use(router).use(pinia).mount('#app')
|
||||
|
||||
let currentVersion: string
|
||||
let isHaveNewVersion = false
|
||||
|
||||
const { notification } = createDiscreteApi(['notification'])
|
||||
|
||||
useNotificationStore().init()
|
||||
InitVTsuru();
|
||||
|
||||
window.$mitt = emitter
|
||||
|
||||
function InitTTS() {
|
||||
try {
|
||||
const result = EasySpeech.detect()
|
||||
if (result.speechSynthesis) {
|
||||
EasySpeech.init({ maxTimeout: 5000, interval: 250 })
|
||||
.then(() => console.log('[SpeechSynthesis] 已加载tts服务'))
|
||||
.catch(e => console.error(e))
|
||||
}
|
||||
else {
|
||||
console.log('[SpeechSynthesis] 当前浏览器不支持tts服务')
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
console.log('[SpeechSynthesis] 当前浏览器不支持tts服务')
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ import { ZstdCodec, ZstdInit } from '@oneidentity/zstd-js/wasm';
|
||||
|
||||
import { encode } from "@msgpack/msgpack";
|
||||
import { getVersion } from '@tauri-apps/api/app';
|
||||
import { onReceivedNotification } from '@/client/data/notification';
|
||||
|
||||
export const useWebFetcher = defineStore('WebFetcher', () => {
|
||||
const route = useRoute();
|
||||
@@ -275,7 +276,7 @@ export const useWebFetcher = defineStore('WebFetcher', () => {
|
||||
Stop(); // 服务器要求断开,调用 Stop 清理所有资源
|
||||
});
|
||||
connection.on('Request', async (url: string, method: string, body: string, useCookie: boolean) => onRequest(url, method, body, useCookie));
|
||||
|
||||
connection.on('Notification', (type: string, data: any) => { onReceivedNotification(type, data); });
|
||||
// --- 尝试启动连接 ---
|
||||
try {
|
||||
await connection.start();
|
||||
|
||||
@@ -4,6 +4,7 @@ import { cookie, isLoadingAccount, useAccount } from '@/api/account'
|
||||
import { ThemeType } from '@/api/api-models'
|
||||
import { QueryGetAPI } from '@/api/query'
|
||||
import RegisterAndLogin from '@/components/RegisterAndLogin.vue'
|
||||
import { checkUpdateNote } from '@/data/UpdateNote';
|
||||
import { ACCOUNT_API_URL } from '@/data/constants'
|
||||
import { useAuthStore } from '@/store/useAuthStore'
|
||||
import { useMusicRequestProvider } from '@/store/useMusicRequest'
|
||||
@@ -485,6 +486,9 @@ onMounted(() => {
|
||||
canResendEmail.value = true
|
||||
}
|
||||
}
|
||||
// 当进入管理页时检查更新日志
|
||||
|
||||
checkUpdateNote();
|
||||
})
|
||||
</script>
|
||||
|
||||
|
||||
@@ -163,7 +163,7 @@ function saveShareImage() {
|
||||
// 生成的ba64图片
|
||||
canvas.toBlob(
|
||||
(data) => {
|
||||
saveAs(data, `vtsuru-提问箱-${accountInfo.value?.name}.png`)
|
||||
saveAs(data!, `vtsuru-提问箱-${accountInfo.value?.name}.png`)
|
||||
},
|
||||
'image/png',
|
||||
1,
|
||||
|
||||
@@ -183,6 +183,7 @@ onUnmounted(() => {
|
||||
|
||||
.question-display-text {
|
||||
min-height: 50px;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
.question-display-image {
|
||||
|
||||
@@ -54,6 +54,11 @@ export default defineConfig({
|
||||
Components({
|
||||
resolvers: [NaiveUiResolver()],
|
||||
dts: 'src/components.d.ts',
|
||||
// allow auto load markdown components under `./src/components/`
|
||||
extensions: ['vue', 'md'],
|
||||
|
||||
// allow auto import and register components used in markdown
|
||||
include: [/\.vue$/, /\.vue\?vue/, /\.md$/],
|
||||
}),
|
||||
monacoEditorPlugin({ languageWorkers: ['css'] }),
|
||||
oxlintPlugin(),
|
||||
|
||||
Reference in New Issue
Block a user