mirror of
https://github.com/Megghy/vtsuru.live.git
synced 2025-12-06 18:36:55 +08:00
feat: 优化 SongList 组件,增强歌曲管理功能
- 增加了歌曲搜索和筛选功能,支持按语言、标签和作者筛选。 - 改进了歌曲编辑和删除操作的用户体验,添加了确认提示。 - 更新了表格列定义,确保动态生成筛选选项。 - 优化了歌曲播放器的状态管理,增强了播放体验。 - 规范化了代码注释,提升了可读性和维护性。
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -11,7 +11,7 @@ const props = defineProps<{
|
|||||||
song: SongsInfo | undefined
|
song: SongsInfo | undefined
|
||||||
isLrcLoading?: string
|
isLrcLoading?: string
|
||||||
}>()
|
}>()
|
||||||
const emits = defineEmits(['update:isLrcLoading'])
|
const emits = defineEmits(['update:isLrcLoading', 'update:close', 'update:volume'])
|
||||||
|
|
||||||
const aplayerMusic = ref({
|
const aplayerMusic = ref({
|
||||||
title: '',
|
title: '',
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ import { onMounted, onUnmounted, ref } from 'vue'
|
|||||||
const timer = ref<any>()
|
const timer = ref<any>()
|
||||||
const visible = ref(true)
|
const visible = ref(true)
|
||||||
const active = ref(true)
|
const active = ref(true)
|
||||||
|
|
||||||
|
const originalBackgroundColor = ref('')
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
timer.value = setInterval(() => {
|
timer.value = setInterval(() => {
|
||||||
if (!visible.value || !active.value) return
|
if (!visible.value || !active.value) return
|
||||||
@@ -22,9 +24,21 @@ onMounted(() => {
|
|||||||
active.value = a
|
active.value = a
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// 使 .n-layout-content 背景透明
|
||||||
|
const layoutContent = document.querySelector('.n-layout-content');
|
||||||
|
if (layoutContent instanceof HTMLElement) {
|
||||||
|
originalBackgroundColor.value = layoutContent.style.backgroundColor
|
||||||
|
layoutContent.style.setProperty('background-color', 'transparent');
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
clearInterval(timer.value)
|
clearInterval(timer.value)
|
||||||
|
// 还原 .n-layout-content 背景颜色
|
||||||
|
const layoutContent = document.querySelector('.n-layout-content');
|
||||||
|
if (layoutContent instanceof HTMLElement) {
|
||||||
|
layoutContent.style.setProperty('background-color', originalBackgroundColor.value);
|
||||||
|
}
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -46,9 +60,3 @@ onUnmounted(() => {
|
|||||||
</RouterView>
|
</RouterView>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style>
|
|
||||||
.body,html,.n-element,.n-layout-content {
|
|
||||||
background-color: transparent !important;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { isDarkMode } from '@/Utils'
|
import { isDarkMode } from '@/Utils' // 引入暗黑模式判断工具
|
||||||
import { ThemeType } from '@/api/api-models'
|
import { ThemeType } from '@/api/api-models' // 引入主题类型枚举
|
||||||
import { AuthInfo } from '@/data/DanmakuClients/OpenLiveClient'
|
import { AuthInfo } from '@/data/DanmakuClients/OpenLiveClient' // 引入开放平台认证信息类型
|
||||||
import { useDanmakuClient } from '@/store/useDanmakuClient';
|
import { useDanmakuClient } from '@/store/useDanmakuClient'; // 引入弹幕客户端状态管理
|
||||||
import { Lottery24Filled, PeopleQueue24Filled, TabletSpeaker24Filled } from '@vicons/fluent'
|
import { Lottery24Filled, PeopleQueue24Filled, TabletSpeaker24Filled } from '@vicons/fluent' // 引入 Fluent UI 图标
|
||||||
import { Moon, MusicalNote, Sunny } from '@vicons/ionicons5'
|
import { Moon, MusicalNote, Sunny } from '@vicons/ionicons5' // 引入 Ionicons 图标
|
||||||
import { useElementSize, useStorage } from '@vueuse/core'
|
import { useElementSize, useStorage } from '@vueuse/core' // 引入 VueUse 组合式函数
|
||||||
import {
|
import {
|
||||||
NAlert,
|
NAlert,
|
||||||
NAvatar,
|
NAvatar,
|
||||||
@@ -27,21 +27,28 @@ import {
|
|||||||
NTag,
|
NTag,
|
||||||
NText,
|
NText,
|
||||||
useMessage,
|
useMessage,
|
||||||
} from 'naive-ui'
|
} from 'naive-ui' // 引入 Naive UI 组件
|
||||||
import { h, onMounted, onUnmounted, ref } from 'vue'
|
import { h, onMounted, onUnmounted, ref, computed } from 'vue' // 引入 Vue 相关 API
|
||||||
import { RouterLink, useRoute } from 'vue-router'
|
import { RouterLink, useRoute, useRouter } from 'vue-router' // 引入 Vue Router 相关 API
|
||||||
|
|
||||||
const route = useRoute()
|
// -- 基本状态和工具 --
|
||||||
const message = useMessage()
|
const route = useRoute() // 获取当前路由信息
|
||||||
const themeType = useStorage('Settings.Theme', ThemeType.Auto)
|
const router = useRouter() // 获取路由实例
|
||||||
|
const message = useMessage() // 获取 Naive UI 消息提示 API
|
||||||
|
const themeType = useStorage('Settings.Theme', ThemeType.Auto) // 使用 useStorage 持久化主题设置 (默认自动)
|
||||||
|
const danmakuClient = useDanmakuClient(); // 获取弹幕客户端实例
|
||||||
|
|
||||||
const sider = ref()
|
// -- 侧边栏状态 --
|
||||||
const { width } = useElementSize(sider)
|
const sider = ref<HTMLElement | null>(null) // 侧边栏 DOM 引用
|
||||||
|
const { width: siderWidth } = useElementSize(sider) // 实时获取侧边栏宽度
|
||||||
|
|
||||||
const authInfo = ref<AuthInfo>()
|
// -- 认证与连接状态 --
|
||||||
const danmakuClient = await useDanmakuClient().initOpenlive();
|
const authInfo = ref<AuthInfo>() // 存储从路由查询参数获取的认证信息
|
||||||
|
const danmakuClientError = ref<string>() // 存储弹幕客户端初始化错误信息
|
||||||
|
|
||||||
const menuOptions = [
|
// -- 菜单配置 --
|
||||||
|
// 定义菜单项, 使用 h 函数渲染 RouterLink 以实现路由跳转
|
||||||
|
const menuOptions = computed(() => [ // 改为 computed 以便将来可能动态修改
|
||||||
{
|
{
|
||||||
label: () =>
|
label: () =>
|
||||||
h(
|
h(
|
||||||
@@ -49,10 +56,10 @@ const menuOptions = [
|
|||||||
{
|
{
|
||||||
to: {
|
to: {
|
||||||
name: 'open-live-lottery',
|
name: 'open-live-lottery',
|
||||||
query: route.query,
|
query: route.query, // 保留查询参数
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{ default: () => '抽奖' },
|
{ default: () => '弹幕抽奖' },
|
||||||
),
|
),
|
||||||
key: 'open-live-lottery',
|
key: 'open-live-lottery',
|
||||||
icon: renderIcon(Lottery24Filled),
|
icon: renderIcon(Lottery24Filled),
|
||||||
@@ -67,7 +74,7 @@ const menuOptions = [
|
|||||||
query: route.query,
|
query: route.query,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{ default: () => '点歌' },
|
{ default: () => '弹幕点歌' }, // 优化名称
|
||||||
),
|
),
|
||||||
key: 'open-live-live-request',
|
key: 'open-live-live-request',
|
||||||
icon: renderIcon(MusicalNote),
|
icon: renderIcon(MusicalNote),
|
||||||
@@ -82,7 +89,7 @@ const menuOptions = [
|
|||||||
query: route.query,
|
query: route.query,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{ default: () => '排队' },
|
{ default: () => '弹幕排队' }, // 优化名称
|
||||||
),
|
),
|
||||||
key: 'open-live-queue',
|
key: 'open-live-queue',
|
||||||
icon: renderIcon(PeopleQueue24Filled),
|
icon: renderIcon(PeopleQueue24Filled),
|
||||||
@@ -97,37 +104,74 @@ const menuOptions = [
|
|||||||
query: route.query,
|
query: route.query,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{ default: () => '读弹幕' },
|
{ default: () => '弹幕朗读' }, // 优化名称
|
||||||
),
|
),
|
||||||
key: 'open-live-speech',
|
key: 'open-live-speech',
|
||||||
icon: renderIcon(TabletSpeaker24Filled),
|
icon: renderIcon(TabletSpeaker24Filled),
|
||||||
},
|
},
|
||||||
]
|
])
|
||||||
|
|
||||||
|
// -- 工具函数 --
|
||||||
|
/**
|
||||||
|
* 渲染 Naive UI 图标的辅助函数
|
||||||
|
* @param icon 图标组件
|
||||||
|
*/
|
||||||
function renderIcon(icon: unknown) {
|
function renderIcon(icon: unknown) {
|
||||||
return () => h(NIcon, null, { default: () => h(icon as any) })
|
return () => h(NIcon, null, { default: () => h(icon as any) })
|
||||||
}
|
}
|
||||||
const danmakuClientError = ref<string>()
|
|
||||||
|
// -- 主题切换逻辑 --
|
||||||
|
const isDarkValue = computed({
|
||||||
|
get: () => themeType.value === ThemeType.Dark || (themeType.value === ThemeType.Auto && isDarkMode.value),
|
||||||
|
set: (value) => {
|
||||||
|
themeType.value = value ? ThemeType.Dark : ThemeType.Light;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// -- 生命周期钩子 --
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
|
// 1. 从路由查询参数解析认证信息
|
||||||
authInfo.value = route.query as unknown as AuthInfo
|
authInfo.value = route.query as unknown as AuthInfo
|
||||||
|
|
||||||
|
// 2. 检查是否存在必要的 Code 参数
|
||||||
if (authInfo.value?.Code) {
|
if (authInfo.value?.Code) {
|
||||||
danmakuClient.initOpenlive(authInfo.value)
|
try {
|
||||||
|
// 3. 初始化开放平台弹幕客户端
|
||||||
|
await danmakuClient.initOpenlive(authInfo.value) // 改为 await 处理可能的异步初始化
|
||||||
|
// 可选: 初始化成功提示
|
||||||
|
// message.success('弹幕客户端连接中...')
|
||||||
|
} catch (error: any) {
|
||||||
|
// 4. 处理初始化错误
|
||||||
|
console.error("Danmaku client initialization failed:", error);
|
||||||
|
danmakuClientError.value = `弹幕客户端初始化失败: ${error.message || '未知错误'}`;
|
||||||
|
message.error(danmakuClientError.value);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
message.error('你不是从幻星平台访问此页面, 或未提供对应参数, 无法使用此功能')
|
// 5. 如果缺少 Code, 显示错误信息
|
||||||
|
message.error('无效访问: 缺少必要的认证参数 (Code)。请通过幻星平台获取链接。')
|
||||||
|
// authInfo 清空, 触发 v-if 显示错误页
|
||||||
|
authInfo.value = undefined;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
onUnmounted(() => {
|
|
||||||
})
|
// onUnmounted 清理 (如果需要)
|
||||||
|
// onUnmounted(() => {
|
||||||
|
// danmakuClient.dispose(); // 示例: 如果有清理逻辑
|
||||||
|
// })
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
<!-- 情况一: 缺少认证信息, 显示错误提示页 -->
|
||||||
<NLayoutContent
|
<NLayoutContent
|
||||||
v-if="!authInfo?.Code"
|
v-if="!authInfo?.Code"
|
||||||
style="height: 100vh"
|
style="height: 100vh; display: flex; align-items: center; justify-content: center;"
|
||||||
|
content-style="padding: 24px;"
|
||||||
>
|
>
|
||||||
<NResult
|
<NResult
|
||||||
status="error"
|
status="error"
|
||||||
title="无效访问"
|
title="无效访问"
|
||||||
|
description="请确保您是通过正确的幻星平台 H5 插件链接访问此页面。"
|
||||||
>
|
>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
请前往
|
请前往
|
||||||
@@ -140,177 +184,288 @@ onUnmounted(() => {
|
|||||||
>
|
>
|
||||||
幻星平台 | VTsuru
|
幻星平台 | VTsuru
|
||||||
</NButton>
|
</NButton>
|
||||||
并点击 获取 , 再点击 获取 H5 插件链接来获取可用链接
|
获取 H5 插件链接。
|
||||||
<br>
|
|
||||||
或者直接在那个页面用也可以, 虽然并不推荐
|
|
||||||
</template>
|
</template>
|
||||||
</NResult>
|
</NResult>
|
||||||
</NLayoutContent>
|
</NLayoutContent>
|
||||||
|
|
||||||
|
<!-- 情况二: 存在认证信息, 显示主布局 -->
|
||||||
<NLayout
|
<NLayout
|
||||||
v-else
|
v-else
|
||||||
style="height: 100vh"
|
style="height: 100vh"
|
||||||
|
:native-scrollbar="false"
|
||||||
>
|
>
|
||||||
|
<!-- 顶部导航栏 -->
|
||||||
<NLayoutHeader
|
<NLayoutHeader
|
||||||
style="height: 45px; padding: 5px 15px 5px 15px"
|
style="height: 60px; display: flex; align-items: center; padding: 0 20px;"
|
||||||
bordered
|
bordered
|
||||||
>
|
>
|
||||||
<NPageHeader :subtitle="($route.meta.title as string) ?? ''">
|
<!-- 使用 NPageHeader 增强语义和结构 -->
|
||||||
<template #extra>
|
<NPageHeader style="width: 100%;">
|
||||||
<NSpace align="center">
|
<!-- 标题区域 -->
|
||||||
<NTag :type="danmakuClient.connected ? 'success' : 'warning'">
|
<template #title>
|
||||||
{{ danmakuClient.connected ? `已连接 | ${danmakuClient.authInfo?.anchor_info?.uname}` : '未连接' }}
|
<NButton
|
||||||
</NTag>
|
text
|
||||||
<NSwitch
|
style="text-decoration: none;"
|
||||||
:default-value="!isDarkMode"
|
@click="router.push({ name: 'open-live-index', query: route.query })"
|
||||||
@update:value="(value: string & number & boolean) => (themeType = value ? ThemeType.Light : ThemeType.Dark)
|
>
|
||||||
"
|
<NText
|
||||||
|
strong
|
||||||
|
style="font-size: 1.5rem; line-height: 1;"
|
||||||
|
type="primary"
|
||||||
>
|
>
|
||||||
|
<!-- 网站/应用 Logo 或名称 -->
|
||||||
|
<img
|
||||||
|
src="/favicon.ico"
|
||||||
|
alt="VTsuru Logo"
|
||||||
|
style="height: 24px; vertical-align: middle; margin-right: 8px;"
|
||||||
|
> <!-- 可选: 添加 Logo -->
|
||||||
|
VTsuru 开放平台
|
||||||
|
</NText>
|
||||||
|
</NButton>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- 副标题/当前页面信息 -->
|
||||||
|
<template #subtitle>
|
||||||
|
<NText depth="3">
|
||||||
|
{{ $route.meta.title as string ?? '功能模块' }}
|
||||||
|
</NText>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- 右侧额外操作区域 -->
|
||||||
|
<template #extra>
|
||||||
|
<NSpace
|
||||||
|
align="center"
|
||||||
|
:size="20"
|
||||||
|
>
|
||||||
|
<!-- 连接状态指示 -->
|
||||||
|
<NTag
|
||||||
|
:type="danmakuClient.connected ? 'success' : 'warning'"
|
||||||
|
round
|
||||||
|
size="small"
|
||||||
|
>
|
||||||
|
<template #icon>
|
||||||
|
<NIcon :component="danmakuClient.connected ? Sunny : Moon" /> <!-- 示例图标 -->
|
||||||
|
</template>
|
||||||
|
{{ danmakuClient.connected ? `已连接: ${danmakuClient.authInfo?.anchor_info?.uname ?? '主播'}` : '连接中...' }}
|
||||||
|
</NTag>
|
||||||
|
<!-- 主题切换开关 -->
|
||||||
|
<NSwitch v-model:value="isDarkValue">
|
||||||
<template #checked>
|
<template #checked>
|
||||||
<NIcon :component="Sunny" />
|
<NIcon :component="Moon" />
|
||||||
</template>
|
</template>
|
||||||
<template #unchecked>
|
<template #unchecked>
|
||||||
<NIcon :component="Moon" />
|
<NIcon :component="Sunny" />
|
||||||
</template>
|
</template>
|
||||||
</NSwitch>
|
</NSwitch>
|
||||||
</NSpace>
|
</NSpace>
|
||||||
</template>
|
</template>
|
||||||
<template #title>
|
|
||||||
<NButton
|
|
||||||
text
|
|
||||||
tag="a"
|
|
||||||
@click="$router.push({ name: 'open-live-index', query: $route.query })"
|
|
||||||
>
|
|
||||||
<NText
|
|
||||||
strong
|
|
||||||
style="font-size: 1.4rem; text-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); text-justify: auto"
|
|
||||||
>
|
|
||||||
VTSURU | 开放平台
|
|
||||||
</NText>
|
|
||||||
</NButton>
|
|
||||||
</template>
|
|
||||||
</NPageHeader>
|
</NPageHeader>
|
||||||
</NLayoutHeader>
|
</NLayoutHeader>
|
||||||
|
|
||||||
|
<!-- 主体内容区域 (包含侧边栏和内容) -->
|
||||||
<NLayout
|
<NLayout
|
||||||
has-sider
|
has-sider
|
||||||
style="height: calc(100vh - 45px - 30px)"
|
style="height: calc(100vh - 60px - 40px)"
|
||||||
>
|
>
|
||||||
|
<!-- 左侧导航栏 -->
|
||||||
<NLayoutSider
|
<NLayoutSider
|
||||||
ref="sider"
|
ref="sider"
|
||||||
bordered
|
bordered
|
||||||
show-trigger
|
show-trigger="arrow-circle"
|
||||||
default-collapsed
|
|
||||||
collapse-mode="width"
|
collapse-mode="width"
|
||||||
:collapsed-width="64"
|
:collapsed-width="64"
|
||||||
:width="180"
|
:width="200"
|
||||||
:native-scrollbar="false"
|
:native-scrollbar="false"
|
||||||
style="height: 100%"
|
style="height: 100%;"
|
||||||
|
default-collapsed
|
||||||
>
|
>
|
||||||
<Transition>
|
<!-- 主播信息区域 (优化样式和过渡) -->
|
||||||
|
<Transition name="fade">
|
||||||
|
<!-- 添加简单的淡入淡出效果 -->
|
||||||
<div
|
<div
|
||||||
v-if="danmakuClient.authInfo"
|
v-if="danmakuClient.authInfo?.anchor_info && siderWidth > 64"
|
||||||
style="margin-top: 8px"
|
style="padding: 20px 10px; text-align: center;"
|
||||||
>
|
>
|
||||||
<NSpace
|
<NAvatar
|
||||||
vertical
|
:src="danmakuClient.authInfo.anchor_info.uface"
|
||||||
justify="center"
|
:img-props="{ referrerpolicy: 'no-referrer' }"
|
||||||
align="center"
|
round
|
||||||
|
bordered
|
||||||
|
size="large"
|
||||||
|
:style="{
|
||||||
|
marginBottom: '10px',
|
||||||
|
boxShadow: '0 2px 8px rgba(0, 0, 0, 0.1)',
|
||||||
|
}"
|
||||||
|
/>
|
||||||
|
<!-- 限制最大宽度并居中 -->
|
||||||
|
<NEllipsis
|
||||||
|
style="max-width: 160px; margin: 0 auto; font-weight: bold;"
|
||||||
|
:tooltip="{ placement: 'bottom' }"
|
||||||
>
|
>
|
||||||
<NAvatar
|
{{ danmakuClient.authInfo.anchor_info.uname }}
|
||||||
:src="danmakuClient.authInfo?.anchor_info?.uface"
|
</NEllipsis>
|
||||||
:img-props="{ referrerpolicy: 'no-referrer' }"
|
</div>
|
||||||
round
|
<!-- 折叠时显示小头像 (可选) -->
|
||||||
bordered
|
<div
|
||||||
:style="{
|
v-else-if="danmakuClient.authInfo?.anchor_info && siderWidth <= 64"
|
||||||
boxShadow: isDarkMode ? 'rgb(195 192 192 / 35%) 0px 0px 8px' : '0 2px 3px rgba(0, 0, 0, 0.1)',
|
style="padding: 15px 0; text-align: center;"
|
||||||
}"
|
>
|
||||||
/>
|
<NAvatar
|
||||||
<NEllipsis
|
:src="danmakuClient.authInfo.anchor_info.uface"
|
||||||
v-if="width > 100"
|
:img-props="{ referrerpolicy: 'no-referrer' }"
|
||||||
style="max-width: 100%"
|
round
|
||||||
>
|
size="medium"
|
||||||
<NText strong>
|
:style="{ boxShadow: '0 1px 4px rgba(0, 0, 0, 0.1)' }"
|
||||||
{{ danmakuClient.authInfo?.anchor_info?.uname }}
|
/>
|
||||||
</NText>
|
|
||||||
</NEllipsis>
|
|
||||||
</NSpace>
|
|
||||||
</div>
|
</div>
|
||||||
</Transition>
|
</Transition>
|
||||||
|
|
||||||
|
<!-- 导航菜单 -->
|
||||||
<NMenu
|
<NMenu
|
||||||
:default-value="$route.name?.toString()"
|
:value="route.name?.toString()"
|
||||||
:collapsed-width="64"
|
:collapsed-width="64"
|
||||||
:collapsed-icon-size="22"
|
:collapsed-icon-size="22"
|
||||||
:options="menuOptions"
|
:options="menuOptions"
|
||||||
|
:indent="24"
|
||||||
|
style="margin-top: 10px;"
|
||||||
/>
|
/>
|
||||||
<NSpace justify="center">
|
|
||||||
|
<!-- 侧边栏底部提示/链接 (优化样式和显示逻辑) -->
|
||||||
|
<NSpace
|
||||||
|
v-if="siderWidth > 150"
|
||||||
|
vertical
|
||||||
|
align="center"
|
||||||
|
style="position: absolute; bottom: 20px; left: 0; right: 0; padding: 0 15px;"
|
||||||
|
>
|
||||||
<NText
|
<NText
|
||||||
v-if="width > 150"
|
|
||||||
depth="3"
|
depth="3"
|
||||||
|
style="font-size: 12px; text-align: center;"
|
||||||
>
|
>
|
||||||
有更多功能建议请
|
遇到问题或有建议?
|
||||||
<NButton
|
<NButton
|
||||||
text
|
text
|
||||||
type="info"
|
type="info"
|
||||||
@click="$router.push({ name: 'about' })"
|
size="tiny"
|
||||||
|
@click="router.push({ name: 'about' })"
|
||||||
>
|
>
|
||||||
反馈
|
点此反馈
|
||||||
</NButton>
|
</NButton>
|
||||||
</NText>
|
</NText>
|
||||||
</NSpace>
|
</NSpace>
|
||||||
</NLayoutSider>
|
</NLayoutSider>
|
||||||
|
|
||||||
|
<!-- 右侧主内容区域 -->
|
||||||
<NLayoutContent
|
<NLayoutContent
|
||||||
style="height: 100%; padding: 10px"
|
style="height: 100%;"
|
||||||
|
content-style="padding: 15px; height: 100%;"
|
||||||
:native-scrollbar="false"
|
:native-scrollbar="false"
|
||||||
>
|
>
|
||||||
|
<!-- 弹幕客户端错误提示 -->
|
||||||
<NAlert
|
<NAlert
|
||||||
v-if="danmakuClientError"
|
v-if="danmakuClientError"
|
||||||
type="error"
|
type="error"
|
||||||
title="无法启动弹幕客户端"
|
title="弹幕客户端错误"
|
||||||
|
closable
|
||||||
|
style="margin-bottom: 15px;"
|
||||||
|
@close="danmakuClientError = undefined"
|
||||||
>
|
>
|
||||||
{{ danmakuClientError }}
|
{{ danmakuClientError }}
|
||||||
</NAlert>
|
</NAlert>
|
||||||
<RouterView
|
|
||||||
v-if="danmakuClient.authInfo"
|
<!-- 路由视图: 根据认证状态显示不同内容 -->
|
||||||
v-slot="{ Component }"
|
<RouterView v-slot="{ Component }">
|
||||||
>
|
<!-- 情况一: 认证信息加载中或连接中 -->
|
||||||
<KeepAlive>
|
<div
|
||||||
|
v-if="!danmakuClient.authInfo && !danmakuClientError"
|
||||||
|
style="display: flex; justify-content: center; align-items: center; height: 80%;"
|
||||||
|
>
|
||||||
|
<NSpin size="large">
|
||||||
|
<template #description>
|
||||||
|
正在加载主播信息并连接服务...
|
||||||
|
</template>
|
||||||
|
</NSpin>
|
||||||
|
</div>
|
||||||
|
<!-- 情况二: 加载/连接成功, 渲染对应页面 -->
|
||||||
|
<KeepAlive v-else-if="Component && danmakuClient.authInfo">
|
||||||
<component
|
<component
|
||||||
:is="Component"
|
:is="Component"
|
||||||
|
:key="route.fullPath"
|
||||||
:room-info="danmakuClient.authInfo"
|
:room-info="danmakuClient.authInfo"
|
||||||
:code="authInfo.Code"
|
:code="authInfo.Code"
|
||||||
/>
|
/>
|
||||||
</KeepAlive>
|
</KeepAlive>
|
||||||
|
<!-- 情况三: 组件无法渲染或其他错误 (理论上不应发生, 但作为后备) -->
|
||||||
|
<NResult
|
||||||
|
v-else-if="!danmakuClientError"
|
||||||
|
status="warning"
|
||||||
|
title="页面加载失败"
|
||||||
|
description="无法加载当前功能模块,请尝试刷新或联系开发者。"
|
||||||
|
/>
|
||||||
</RouterView>
|
</RouterView>
|
||||||
<template v-else>
|
|
||||||
{{ }}
|
<!-- 返回顶部按钮 -->
|
||||||
<NAlert
|
<NBackTop
|
||||||
type="info"
|
:right="40"
|
||||||
title="正在请求弹幕客户端认证信息..."
|
:bottom="60"
|
||||||
>
|
/>
|
||||||
<NSpin show />
|
|
||||||
</NAlert>
|
|
||||||
</template>
|
|
||||||
<NBackTop />
|
|
||||||
</NLayoutContent>
|
</NLayoutContent>
|
||||||
</NLayout>
|
</NLayout>
|
||||||
|
|
||||||
|
<!-- 底部信息栏 -->
|
||||||
<NLayoutFooter
|
<NLayoutFooter
|
||||||
style="height: 30px"
|
style="height: 40px; display: flex; align-items: center; justify-content: center; padding: 0 20px;"
|
||||||
bordered
|
bordered
|
||||||
>
|
>
|
||||||
<NSpace
|
<NText
|
||||||
justify="center"
|
depth="3"
|
||||||
align="center"
|
style="font-size: 12px;"
|
||||||
style="height: 100%"
|
|
||||||
>
|
>
|
||||||
|
© {{ new Date().getFullYear() }} <!-- 动态年份 -->
|
||||||
<NButton
|
<NButton
|
||||||
text
|
text
|
||||||
tag="a"
|
tag="a"
|
||||||
href="/"
|
href="https://vtsuru.live"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
type="info"
|
type="primary"
|
||||||
|
style="margin-left: 5px;"
|
||||||
>
|
>
|
||||||
vtsuru.live
|
vtsuru.live
|
||||||
</NButton>
|
</NButton>
|
||||||
</NSpace>
|
- 由 VTsuru 提供支持
|
||||||
|
</NText>
|
||||||
</NLayoutFooter>
|
</NLayoutFooter>
|
||||||
</NLayout>
|
</NLayout>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
/* 添加过渡动画 */
|
||||||
|
.fade-enter-active,
|
||||||
|
.fade-leave-active {
|
||||||
|
transition: opacity 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fade-enter-from,
|
||||||
|
.fade-leave-to {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.n-pageheader-wrapper {
|
||||||
|
width: 100% !important;
|
||||||
|
}
|
||||||
|
/* 优化 NPageHeader 在窄屏幕下的表现 (可选) */
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.n-page-header-wrapper {
|
||||||
|
padding: 0 10px !important; /* 减少内边距 */
|
||||||
|
}
|
||||||
|
.n-page-header__title {
|
||||||
|
font-size: 1.2rem !important; /* 缩小标题字号 */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 确保 NLayoutContent 的内边距生效 */
|
||||||
|
:deep(.n-layout-scroll-container) {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
Reference in New Issue
Block a user