feat: 更新 SongList 组件,增强分页功能和样式优化

- 添加了每页大小的动态设置,支持用户自定义分页。
- 优化了 OpenLiveLayout 组件的侧边栏样式,提升了用户体验。
- 改进了 OpenLiveIndex 组件的卡片布局,增强了视觉效果。
- 更新了 OpenQueue 组件,增加了辅助函数以改善队列状态显示。
This commit is contained in:
2025-04-20 15:01:21 +08:00
parent 94a315a906
commit f9417870ce
4 changed files with 256 additions and 171 deletions

View File

@@ -62,6 +62,7 @@ const songsInternal = ref<SongsInfo[]>([]) // 内部维护的歌曲列表,避
const playingSong = ref<SongsInfo>() // 当前正在试听的歌曲
const isLrcLoading = ref<string>() // 歌词加载状态(存储歌曲 key
const isLoading = ref(false) // 通用加载状态 (用于 API 请求)
const pageSize = ref(25) // 每页大小
// --- 搜索与筛选状态 ---
const searchMusicKeyword = ref('') // 歌曲名称搜索关键词
@@ -743,22 +744,16 @@ onMounted(() => {
v-model:checked-row-keys="selectedColumn"
:columns="columns"
:data="songsComputed"
:row-key="(row: SongsInfo) => row.key"
size="small"
:bordered="false"
:single-line="false"
flex-height
style="height: 60vh;"
:pagination="{
// itemCount: songsComputed.length, // 根据筛选结果分页或总数分页?取决于需求
pageSize: 25, // 默认每页大小
pageSizes: [10, 25, 50, 100, 200], // 可选的每页大小
itemCount: songsInternal.length,
defaultPageSize: pageSize,
pageSizes: [10, 25, 50, 100, 200],
showSizePicker: true,
showQuickJumper: true, // 显示快速跳转
// size: 'small', // 已在 NDataTable 设置 size
showQuickJumper: true,
}"
:loading="isLoading && songsComputed.length === 0"
remote
striped
/>

View File

@@ -272,84 +272,62 @@ onMounted(async () => {
<NLayoutSider
ref="sider"
bordered
show-trigger="arrow-circle"
show-trigger
default-collapsed
collapse-mode="width"
:collapsed-width="64"
:width="200"
:width="180"
:native-scrollbar="false"
style="height: 100%;"
default-collapsed
style="height: 100%"
>
<!-- 主播信息区域 (优化样式和过渡) -->
<Transition name="fade">
<!-- 添加简单的淡入淡出效果 -->
<Transition>
<div
v-if="danmakuClient.authInfo?.anchor_info && siderWidth > 64"
style="padding: 20px 10px; text-align: center;"
v-if="danmakuClient.authInfo"
style="margin-top: 8px"
>
<NAvatar
:src="danmakuClient.authInfo.anchor_info.uface"
:img-props="{ referrerpolicy: 'no-referrer' }"
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' }"
<NSpace
vertical
justify="center"
align="center"
>
{{ danmakuClient.authInfo.anchor_info.uname }}
</NEllipsis>
</div>
<!-- 折叠时显示小头像 (可选) -->
<div
v-else-if="danmakuClient.authInfo?.anchor_info && siderWidth <= 64"
style="padding: 15px 0; text-align: center;"
>
<NAvatar
:src="danmakuClient.authInfo.anchor_info.uface"
:img-props="{ referrerpolicy: 'no-referrer' }"
round
size="medium"
:style="{ boxShadow: '0 1px 4px rgba(0, 0, 0, 0.1)' }"
/>
<NAvatar
:src="danmakuClient.authInfo?.anchor_info?.uface"
:img-props="{ referrerpolicy: 'no-referrer' }"
round
bordered
:style="{
boxShadow: isDarkMode ? 'rgb(195 192 192 / 35%) 0px 0px 8px' : '0 2px 3px rgba(0, 0, 0, 0.1)',
}"
/>
<NEllipsis
v-if="siderWidth > 100"
style="max-width: 100%"
>
<NText strong>
{{ danmakuClient.authInfo?.anchor_info?.uname }}
</NText>
</NEllipsis>
</NSpace>
</div>
</Transition>
<!-- 导航菜单 -->
<NMenu
:value="route.name?.toString()"
:default-value="$route.name?.toString()"
:collapsed-width="64"
:collapsed-icon-size="22"
:options="menuOptions"
:indent="24"
style="margin-top: 10px;"
/>
<!-- 侧边栏底部提示/链接 (优化样式和显示逻辑) -->
<NSpace
v-if="siderWidth > 150"
vertical
align="center"
style="position: absolute; bottom: 20px; left: 0; right: 0; padding: 0 15px;"
>
<NSpace justify="center">
<NText
v-if="siderWidth > 150"
depth="3"
style="font-size: 12px; text-align: center;"
>
遇到问题或有建议?
有更多功能建议请
<NButton
text
type="info"
size="tiny"
@click="router.push({ name: 'about' })"
@click="$router.push({ name: 'about' })"
>
点此反馈
反馈
</NButton>
</NText>
</NSpace>

View File

@@ -13,13 +13,17 @@ const accountInfo = useAccount()
<template>
<NDivider> 功能 </NDivider>
<NSpace justify="center">
<NSpace
justify="center"
:size="[20, 20]"
item-style="display: flex;"
>
<NCard
hoverable
embedded
bordered
size="small"
title="弹幕抽奖"
style="width: 300px"
style="width: 300px; flex-grow: 1;"
>
通过弹幕或者礼物收集用户, 并进行抽取, 允许设置多种条件
<template #footer>
@@ -33,10 +37,10 @@ const accountInfo = useAccount()
</NCard>
<NCard
hoverable
embedded
bordered
size="small"
title="弹幕点播"
style="width: 300px"
style="width: 300px; flex-grow: 1;"
>
通过弹幕或者SC进行点歌或者其他的点播(比如跳舞或者点播视频之类的), 注册后可以保存和导出
<template #footer>
@@ -50,10 +54,10 @@ const accountInfo = useAccount()
</NCard>
<NCard
hoverable
embedded
bordered
size="small"
title="弹幕排队"
style="width: 300px"
style="width: 300px; flex-grow: 1;"
>
通过发送弹幕或者礼物进行排队, 允许设置多种条件
<template #footer>
@@ -68,10 +72,10 @@ const accountInfo = useAccount()
<NCard
hoverable
embedded
bordered
size="small"
title="读弹幕"
style="width: 300px"
style="width: 300px; flex-grow: 1;"
>
通过浏览器自带的tts服务读弹幕 (此功能需要 Chrome, Edge 等现代浏览器!)
<template #footer>
@@ -85,42 +89,50 @@ const accountInfo = useAccount()
</NCard>
</NSpace>
<br>
<NAlert
<br>
<NSpace
v-if="accountInfo?.eventFetcherState?.online != true"
type="warning"
title="可用性警告"
style="max-width: 600px; margin: 0 auto"
justify="center"
>
当浏览器在后台运行时, 定时器和 Websocket 连接将受到严格限制, 这会导致弹幕接收功能无法正常工作 (详见
<NButton
text
tag="a"
href="https://developer.chrome.com/blog/background_tabs/"
target="_blank"
type="info"
<NAlert
type="warning"
title="可用性警告"
style="max-width: 600px; margin: 0 auto"
>
此文章
</NButton>), 虽然本站已经针对此问题做出了处理, 一般情况下即使掉线了也会重连, 不过还是有可能会遗漏事件
<br>
为避免这种情况, 建议注册本站账后使用
<NButton
type="primary"
text
size="tiny"
tag="a"
href="https://www.wolai.com/fje5wLtcrDoZcb9rk2zrFs"
target="_blank"
>
VtsuruEventFetcher
</NButton>, 否则请在使用功能时尽量保持网页在前台运行
</NAlert>
当浏览器在后台运行时, 定时器和 Websocket 连接将受到严格限制, 这会导致弹幕接收功能无法正常工作 (详见
<NButton
text
tag="a"
href="https://developer.chrome.com/blog/background_tabs/"
target="_blank"
type="info"
>
此文章
</NButton>), 虽然本站已经针对此问题做出了处理, 一般情况下即使掉线了也会重连, 不过还是有可能会遗漏事件
<br>
为避免这种情况, 建议注册本站账后使用
<NButton
type="primary"
text
size="tiny"
tag="a"
href="https://www.wolai.com/fje5wLtcrDoZcb9rk2zrFs"
target="_blank"
>
VtsuruEventFetcher
</NButton>, 否则请在使用功能时尽量保持网页在前台运行
</NAlert>
</NSpace>
<br v-if="accountInfo?.eventFetcherState?.online != true">
<NDivider> 还有更多 </NDivider>
<NSpace
justify="center"
align="center"
vertical
>
舰长积分动态抽奖视频征集歌单棉花糖日程表...
<p style="font-size: 1.1em; color: var(--n-text-color-2)">
舰长积分动态抽奖视频征集歌单棉花糖日程表...
</p>
<p>
详见
<NButton

View File

@@ -67,7 +67,7 @@
useMessage,
useNotification,
} from 'naive-ui';
import { computed, h, onActivated, onDeactivated, onMounted, onUnmounted, ref, VNodeChild } from 'vue';
import { computed, h, onActivated, onDeactivated, onMounted, onUnmounted, ref, VNodeChild, CSSProperties } from 'vue';
// import { useRoute } from 'vue-router' // 未使用
import QueueOBS from '../obs/QueueOBS.vue';
import { copyToClipboard } from '@/Utils';
@@ -971,6 +971,46 @@
client.offEvent('gift', onGetGift);
dispose();
});
// --- 辅助函数 ---
function getIndexStyle(status: QueueStatus): CSSProperties {
// 基础颜色定义 - 扁平化风格
let backgroundColor;
// 根据状态设置不同的颜色
switch (status) {
case QueueStatus.Progressing:
backgroundColor = '#18a058'; // 处理中 - 绿色
break;
case QueueStatus.Waiting:
backgroundColor = '#2080f0'; // 等待中 - 蓝色
break;
case QueueStatus.Finish:
backgroundColor = '#86909c'; // 已完成 - 灰色
break;
case QueueStatus.Cancel:
backgroundColor = '#d03050'; // 已取消 - 红色
break;
default:
backgroundColor = '#2080f0'; // 默认 - 蓝色
}
const style: CSSProperties = {
display: 'inline-flex',
alignItems: 'center',
justifyContent: 'center',
fontWeight: 'bold',
width: '24px', // 确保宽高一致以形成完美圆形
height: '24px', // 保持一致的宽高
borderRadius: '50%', // 圆形
color: 'white',
fontSize: '13px', // 适当调整字体大小
backgroundColor, // 扁平化的纯色背景
transition: 'opacity 0.2s', // 仅保留简单的过渡效果
};
return style;
}
</script>
<template>
@@ -1067,8 +1107,9 @@
>
<NSpace
align="center"
justify="start"
justify="space-between"
wrap
:item-style="{ marginBottom: '8px' }"
>
<!-- 队列统计信息 -->
<NSpace align="center">
@@ -1170,7 +1211,6 @@
<!-- 队列列表 -->
<NSpin :show="isLoading && originQueue.length === 0">
<!-- 初始加载时显示 Spin -->
<NList
v-if="queue.length > 0"
hoverable
@@ -1195,18 +1235,21 @@
:wrap="false"
>
<!-- 左侧信息 -->
<NSpace align="center">
<NTag
round
size="small"
:type="queueData.status == QueueStatus.Progressing ? 'success' : 'default'"
style="min-width: 30px; text-align: center;"
<NSpace
align="center"
:size="8"
:wrap="false"
>
<span
:style="getIndexStyle(queueData.status)"
class="queue-index"
:class="{ 'queue-index-processing': queueData.status === QueueStatus.Progressing }"
>
{{ index + 1 }}
</NTag>
</span>
<NText
strong
style="font-size: 16px; margin-right: 5px;"
style="font-size: 16px;"
>
<NTooltip>
<template #trigger>
@@ -1262,7 +1305,7 @@
<NIcon
:component="Info24Filled"
size="16"
style="cursor: help; color: #aaa; margin-left: 5px;"
style="cursor: help; color: #aaa;"
/>
</template>
<NCard
@@ -1303,8 +1346,9 @@
<NSpace
justify="end"
align="center"
:size="5"
:size="6"
:wrap="false"
style="flex-shrink: 0;"
>
<!-- 开始/暂停处理 -->
<NTooltip>
@@ -1418,18 +1462,23 @@
:bordered="false"
style="margin-bottom: 10px;"
>
<NSpace align="center">
<NInputGroup style="width: 300px">
<NInputGroupLabel> 筛选用户 </NInputGroupLabel>
<NInput
v-model:value="filterName"
clearable
placeholder="输入用户名"
/>
</NInputGroup>
<NCheckbox v-model:checked="filterNameContains">
模糊匹配
</NCheckbox>
<NSpace
align="center"
justify="space-between"
>
<NSpace align="center">
<NInputGroup style="width: 300px">
<NInputGroupLabel> 筛选用户 </NInputGroupLabel>
<NInput
v-model:value="filterName"
clearable
placeholder="输入用户名"
/>
</NInputGroup>
<NCheckbox v-model:checked="filterNameContains">
模糊匹配
</NCheckbox>
</NSpace>
<NButton
size="small"
type="error"
@@ -1461,13 +1510,21 @@
:disabled="!configCanEdit"
>
<NSpin :show="isLoading">
<NCollapse>
<!-- 规则设置 -->
<NCollapseItem
<NSpace
vertical
:size="20"
style="padding-top: 10px;"
>
<!-- 加入规则 -->
<NCard
size="small"
title="加入规则"
name="rules"
:bordered="false"
>
<NSpace vertical>
<NSpace
vertical
:size="12"
>
<NSpace align="center">
<NInputGroup style="width: 350px">
<NInputGroupLabel> 弹幕关键词 </NInputGroupLabel>
@@ -1525,6 +1582,7 @@
<NSpace
v-if="!settings.allowAllDanmaku"
vertical
:size="10"
style="margin-left: 20px;"
>
<NInputGroup style="width: 270px">
@@ -1555,14 +1613,20 @@
</NCheckbox>
</NSpace>
</NSpace>
</NCollapseItem>
</NCard>
<!-- 礼物设置 -->
<NCollapseItem
<NDivider />
<!-- 礼物规则 -->
<NCard
size="small"
title="礼物规则"
name="gift"
:bordered="false"
>
<NSpace vertical>
<NSpace
vertical
:size="12"
>
<NCheckbox
v-model:checked="settings.allowGift"
@update:checked="updateSettings"
@@ -1572,6 +1636,7 @@
<NSpace
v-if="settings.allowGift"
vertical
:size="10"
style="margin-left: 20px;"
>
<NInputGroup style="width: 250px">
@@ -1651,12 +1716,15 @@
</NCheckbox>
</NSpace>
</NSpace>
</NCollapseItem>
</NCard>
<!-- 冷却设置 -->
<NCollapseItem
<NDivider />
<!-- 冷却时间 (CD) -->
<NCard
size="small"
title="冷却时间 (CD)"
name="cooldown"
:bordered="false"
>
<NCheckbox
v-model:checked="settings.enableCooldown"
@@ -1667,6 +1735,7 @@
<NSpace
v-if="settings.enableCooldown"
vertical
:size="10"
style="margin-left: 20px; margin-top: 10px;"
>
<NInputGroup style="width: 280px">
@@ -1702,14 +1771,20 @@
/>
</NInputGroup>
</NSpace>
</NCollapseItem>
</NCard>
<!-- 显示设置 -->
<NCollapseItem
<NDivider />
<!-- 显示与界面 -->
<NCard
size="small"
title="显示与界面"
name="display"
:bordered="false"
>
<NSpace vertical>
<NSpace
vertical
:size="12"
>
<NDivider
title-placement="left"
style="margin: 5px 0;"
@@ -1741,11 +1816,11 @@
其他界面设置
</NDivider>
<NCheckbox v-model:checked="isWarnMessageAutoClose">
自动关闭加入队列失败的通知消息 (默认3秒)
自动关闭"加入队列失败"的通知消息 (默认3秒)
</NCheckbox>
</NSpace>
</NCollapseItem>
</NCollapse>
</NCard>
</NSpace>
</NSpin>
</NTabPane>
</NTabs>
@@ -1802,14 +1877,17 @@
style="padding-top: 100px;"
/>
</div>
<NCollapse style="margin-top: 15px;">
<NCollapse
style="margin-top: 15px;"
accordion
>
<NCollapseItem title="详细说明">
<NUl>
<NLi>在 OBS 中添加一个新的“浏览器”来源。</NLi>
<NLi>将上方 URL 粘贴到“URL”栏中。</NLi>
<NLi>在 OBS 中添加一个新的"浏览器"来源。</NLi>
<NLi>将上方 URL 粘贴到"URL"栏中。</NLi>
<NLi>推荐宽度设置为 280-350px高度根据需要调整 (例如 500-700px)。</NLi>
<NLi>可在“设置”标签页中调整 OBS 组件的显示内容。</NLi>
<NLi>如果需要自定义样式,可以在 OBS 的“自定义 CSS”中添加覆盖样式。</NLi>
<NLi>可在"设置"标签页中调整 OBS 组件的显示内容。</NLi>
<NLi>如果需要自定义样式,可以在 OBS 的"自定义 CSS"中添加覆盖样式。</NLi>
</NUl>
</NCollapseItem>
</NCollapse>
@@ -1817,20 +1895,14 @@
</template>
<style>
/* 移除全局动画,仅在需要的地方应用 */
/* @keyframes loading { ... } */
/* 处理中状态的边框动画 */
@keyframes animated-border {
0% {
box-shadow: 0 0 0 0px rgba(103, 194, 58, 0.7);
/* 对应 success 颜色 */
}
70% {
box-shadow: 0 0 0 5px rgba(103, 194, 58, 0);
}
100% {
box-shadow: 0 0 0 0px rgba(103, 194, 58, 0);
}
@@ -1838,11 +1910,7 @@
/* 处理中状态的卡片左边框或标签动画 */
.n-card[style*="border-left: 4px solid #63e2b7;"],
/* 处理中卡片的左边框 */
.n-tag--success[style*="animation: animated-border"]
/* 处理中标签 (如果应用了动画) */
{
.n-tag--success[style*="animation: animated-border"] {
animation: animated-border 1.5s infinite;
}
@@ -1853,9 +1921,41 @@
text-overflow: ellipsis;
}
/* 如果需要特定列换行,可以单独设置 */
/* .n-data-table-td[data-col-key="user.name"] { white-space: normal; } */
/* 序号悬停效果 - 扁平化风格 */
.queue-index:hover {
opacity: 0.85;
}
/* 移除旧的 round 动画 */
/* @keyframes animated-border-round { ... } */
/* 处理中状态的序号动画 - 扁平化风格 */
.queue-index-processing {
position: relative;
}
.queue-index-processing::after {
content: '';
position: absolute;
top: -2px;
left: -2px;
right: -2px;
bottom: -2px;
border-radius: 50%;
border: 2px solid #18a058;
opacity: 0;
animation: flat-pulse 2s infinite;
}
@keyframes flat-pulse {
0% {
transform: scale(1);
opacity: 0.7;
}
70% {
transform: scale(1.1);
opacity: 0;
}
100% {
transform: scale(1.1);
opacity: 0;
}
}
</style>