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

View File

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

View File

@@ -13,13 +13,17 @@ const accountInfo = useAccount()
<template> <template>
<NDivider> 功能 </NDivider> <NDivider> 功能 </NDivider>
<NSpace justify="center"> <NSpace
justify="center"
:size="[20, 20]"
item-style="display: flex;"
>
<NCard <NCard
hoverable hoverable
embedded bordered
size="small" size="small"
title="弹幕抽奖" title="弹幕抽奖"
style="width: 300px" style="width: 300px; flex-grow: 1;"
> >
通过弹幕或者礼物收集用户, 并进行抽取, 允许设置多种条件 通过弹幕或者礼物收集用户, 并进行抽取, 允许设置多种条件
<template #footer> <template #footer>
@@ -33,10 +37,10 @@ const accountInfo = useAccount()
</NCard> </NCard>
<NCard <NCard
hoverable hoverable
embedded bordered
size="small" size="small"
title="弹幕点播" title="弹幕点播"
style="width: 300px" style="width: 300px; flex-grow: 1;"
> >
通过弹幕或者SC进行点歌或者其他的点播(比如跳舞或者点播视频之类的), 注册后可以保存和导出 通过弹幕或者SC进行点歌或者其他的点播(比如跳舞或者点播视频之类的), 注册后可以保存和导出
<template #footer> <template #footer>
@@ -50,10 +54,10 @@ const accountInfo = useAccount()
</NCard> </NCard>
<NCard <NCard
hoverable hoverable
embedded bordered
size="small" size="small"
title="弹幕排队" title="弹幕排队"
style="width: 300px" style="width: 300px; flex-grow: 1;"
> >
通过发送弹幕或者礼物进行排队, 允许设置多种条件 通过发送弹幕或者礼物进行排队, 允许设置多种条件
<template #footer> <template #footer>
@@ -68,10 +72,10 @@ const accountInfo = useAccount()
<NCard <NCard
hoverable hoverable
embedded bordered
size="small" size="small"
title="读弹幕" title="读弹幕"
style="width: 300px" style="width: 300px; flex-grow: 1;"
> >
通过浏览器自带的tts服务读弹幕 (此功能需要 Chrome, Edge 等现代浏览器!) 通过浏览器自带的tts服务读弹幕 (此功能需要 Chrome, Edge 等现代浏览器!)
<template #footer> <template #footer>
@@ -85,8 +89,12 @@ const accountInfo = useAccount()
</NCard> </NCard>
</NSpace> </NSpace>
<br> <br>
<NAlert <br>
<NSpace
v-if="accountInfo?.eventFetcherState?.online != true" v-if="accountInfo?.eventFetcherState?.online != true"
justify="center"
>
<NAlert
type="warning" type="warning"
title="可用性警告" title="可用性警告"
style="max-width: 600px; margin: 0 auto" style="max-width: 600px; margin: 0 auto"
@@ -114,13 +122,17 @@ const accountInfo = useAccount()
VtsuruEventFetcher VtsuruEventFetcher
</NButton>, 否则请在使用功能时尽量保持网页在前台运行 </NButton>, 否则请在使用功能时尽量保持网页在前台运行
</NAlert> </NAlert>
</NSpace>
<br v-if="accountInfo?.eventFetcherState?.online != true">
<NDivider> 还有更多 </NDivider> <NDivider> 还有更多 </NDivider>
<NSpace <NSpace
justify="center" justify="center"
align="center" align="center"
vertical vertical
> >
<p style="font-size: 1.1em; color: var(--n-text-color-2)">
舰长积分动态抽奖视频征集歌单棉花糖日程表... 舰长积分动态抽奖视频征集歌单棉花糖日程表...
</p>
<p> <p>
详见 详见
<NButton <NButton

View File

@@ -67,7 +67,7 @@
useMessage, useMessage,
useNotification, useNotification,
} from 'naive-ui'; } 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 { useRoute } from 'vue-router' // 未使用
import QueueOBS from '../obs/QueueOBS.vue'; import QueueOBS from '../obs/QueueOBS.vue';
import { copyToClipboard } from '@/Utils'; import { copyToClipboard } from '@/Utils';
@@ -971,6 +971,46 @@
client.offEvent('gift', onGetGift); client.offEvent('gift', onGetGift);
dispose(); 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> </script>
<template> <template>
@@ -1067,8 +1107,9 @@
> >
<NSpace <NSpace
align="center" align="center"
justify="start" justify="space-between"
wrap wrap
:item-style="{ marginBottom: '8px' }"
> >
<!-- 队列统计信息 --> <!-- 队列统计信息 -->
<NSpace align="center"> <NSpace align="center">
@@ -1170,7 +1211,6 @@
<!-- 队列列表 --> <!-- 队列列表 -->
<NSpin :show="isLoading && originQueue.length === 0"> <NSpin :show="isLoading && originQueue.length === 0">
<!-- 初始加载时显示 Spin -->
<NList <NList
v-if="queue.length > 0" v-if="queue.length > 0"
hoverable hoverable
@@ -1195,18 +1235,21 @@
:wrap="false" :wrap="false"
> >
<!-- 左侧信息 --> <!-- 左侧信息 -->
<NSpace align="center"> <NSpace
<NTag align="center"
round :size="8"
size="small" :wrap="false"
:type="queueData.status == QueueStatus.Progressing ? 'success' : 'default'" >
style="min-width: 30px; text-align: center;" <span
:style="getIndexStyle(queueData.status)"
class="queue-index"
:class="{ 'queue-index-processing': queueData.status === QueueStatus.Progressing }"
> >
{{ index + 1 }} {{ index + 1 }}
</NTag> </span>
<NText <NText
strong strong
style="font-size: 16px; margin-right: 5px;" style="font-size: 16px;"
> >
<NTooltip> <NTooltip>
<template #trigger> <template #trigger>
@@ -1262,7 +1305,7 @@
<NIcon <NIcon
:component="Info24Filled" :component="Info24Filled"
size="16" size="16"
style="cursor: help; color: #aaa; margin-left: 5px;" style="cursor: help; color: #aaa;"
/> />
</template> </template>
<NCard <NCard
@@ -1303,8 +1346,9 @@
<NSpace <NSpace
justify="end" justify="end"
align="center" align="center"
:size="5" :size="6"
:wrap="false" :wrap="false"
style="flex-shrink: 0;"
> >
<!-- 开始/暂停处理 --> <!-- 开始/暂停处理 -->
<NTooltip> <NTooltip>
@@ -1417,6 +1461,10 @@
size="small" size="small"
:bordered="false" :bordered="false"
style="margin-bottom: 10px;" style="margin-bottom: 10px;"
>
<NSpace
align="center"
justify="space-between"
> >
<NSpace align="center"> <NSpace align="center">
<NInputGroup style="width: 300px"> <NInputGroup style="width: 300px">
@@ -1430,6 +1478,7 @@
<NCheckbox v-model:checked="filterNameContains"> <NCheckbox v-model:checked="filterNameContains">
模糊匹配 模糊匹配
</NCheckbox> </NCheckbox>
</NSpace>
<NButton <NButton
size="small" size="small"
type="error" type="error"
@@ -1461,13 +1510,21 @@
:disabled="!configCanEdit" :disabled="!configCanEdit"
> >
<NSpin :show="isLoading"> <NSpin :show="isLoading">
<NCollapse> <NSpace
<!-- 规则设置 --> vertical
<NCollapseItem :size="20"
title="加入规则" style="padding-top: 10px;"
name="rules" >
<!-- 加入规则 -->
<NCard
size="small"
title="加入规则"
:bordered="false"
>
<NSpace
vertical
:size="12"
> >
<NSpace vertical>
<NSpace align="center"> <NSpace align="center">
<NInputGroup style="width: 350px"> <NInputGroup style="width: 350px">
<NInputGroupLabel> 弹幕关键词 </NInputGroupLabel> <NInputGroupLabel> 弹幕关键词 </NInputGroupLabel>
@@ -1525,6 +1582,7 @@
<NSpace <NSpace
v-if="!settings.allowAllDanmaku" v-if="!settings.allowAllDanmaku"
vertical vertical
:size="10"
style="margin-left: 20px;" style="margin-left: 20px;"
> >
<NInputGroup style="width: 270px"> <NInputGroup style="width: 270px">
@@ -1555,14 +1613,20 @@
</NCheckbox> </NCheckbox>
</NSpace> </NSpace>
</NSpace> </NSpace>
</NCollapseItem> </NCard>
<!-- 礼物设置 --> <NDivider />
<NCollapseItem
<!-- 礼物规则 -->
<NCard
size="small"
title="礼物规则" title="礼物规则"
name="gift" :bordered="false"
>
<NSpace
vertical
:size="12"
> >
<NSpace vertical>
<NCheckbox <NCheckbox
v-model:checked="settings.allowGift" v-model:checked="settings.allowGift"
@update:checked="updateSettings" @update:checked="updateSettings"
@@ -1572,6 +1636,7 @@
<NSpace <NSpace
v-if="settings.allowGift" v-if="settings.allowGift"
vertical vertical
:size="10"
style="margin-left: 20px;" style="margin-left: 20px;"
> >
<NInputGroup style="width: 250px"> <NInputGroup style="width: 250px">
@@ -1651,12 +1716,15 @@
</NCheckbox> </NCheckbox>
</NSpace> </NSpace>
</NSpace> </NSpace>
</NCollapseItem> </NCard>
<!-- 冷却设置 --> <NDivider />
<NCollapseItem
<!-- 冷却时间 (CD) -->
<NCard
size="small"
title="冷却时间 (CD)" title="冷却时间 (CD)"
name="cooldown" :bordered="false"
> >
<NCheckbox <NCheckbox
v-model:checked="settings.enableCooldown" v-model:checked="settings.enableCooldown"
@@ -1667,6 +1735,7 @@
<NSpace <NSpace
v-if="settings.enableCooldown" v-if="settings.enableCooldown"
vertical vertical
:size="10"
style="margin-left: 20px; margin-top: 10px;" style="margin-left: 20px; margin-top: 10px;"
> >
<NInputGroup style="width: 280px"> <NInputGroup style="width: 280px">
@@ -1702,14 +1771,20 @@
/> />
</NInputGroup> </NInputGroup>
</NSpace> </NSpace>
</NCollapseItem> </NCard>
<!-- 显示设置 --> <NDivider />
<NCollapseItem
<!-- 显示与界面 -->
<NCard
size="small"
title="显示与界面" title="显示与界面"
name="display" :bordered="false"
>
<NSpace
vertical
:size="12"
> >
<NSpace vertical>
<NDivider <NDivider
title-placement="left" title-placement="left"
style="margin: 5px 0;" style="margin: 5px 0;"
@@ -1741,11 +1816,11 @@
其他界面设置 其他界面设置
</NDivider> </NDivider>
<NCheckbox v-model:checked="isWarnMessageAutoClose"> <NCheckbox v-model:checked="isWarnMessageAutoClose">
自动关闭加入队列失败的通知消息 (默认3秒) 自动关闭"加入队列失败"的通知消息 (默认3秒)
</NCheckbox> </NCheckbox>
</NSpace> </NSpace>
</NCollapseItem> </NCard>
</NCollapse> </NSpace>
</NSpin> </NSpin>
</NTabPane> </NTabPane>
</NTabs> </NTabs>
@@ -1802,14 +1877,17 @@
style="padding-top: 100px;" style="padding-top: 100px;"
/> />
</div> </div>
<NCollapse style="margin-top: 15px;"> <NCollapse
style="margin-top: 15px;"
accordion
>
<NCollapseItem title="详细说明"> <NCollapseItem title="详细说明">
<NUl> <NUl>
<NLi>在 OBS 中添加一个新的“浏览器”来源。</NLi> <NLi>在 OBS 中添加一个新的"浏览器"来源。</NLi>
<NLi>将上方 URL 粘贴到“URL”栏中。</NLi> <NLi>将上方 URL 粘贴到"URL"栏中。</NLi>
<NLi>推荐宽度设置为 280-350px高度根据需要调整 (例如 500-700px)。</NLi> <NLi>推荐宽度设置为 280-350px高度根据需要调整 (例如 500-700px)。</NLi>
<NLi>可在“设置”标签页中调整 OBS 组件的显示内容。</NLi> <NLi>可在"设置"标签页中调整 OBS 组件的显示内容。</NLi>
<NLi>如果需要自定义样式,可以在 OBS 的“自定义 CSS”中添加覆盖样式。</NLi> <NLi>如果需要自定义样式,可以在 OBS 的"自定义 CSS"中添加覆盖样式。</NLi>
</NUl> </NUl>
</NCollapseItem> </NCollapseItem>
</NCollapse> </NCollapse>
@@ -1817,20 +1895,14 @@
</template> </template>
<style> <style>
/* 移除全局动画,仅在需要的地方应用 */
/* @keyframes loading { ... } */
/* 处理中状态的边框动画 */ /* 处理中状态的边框动画 */
@keyframes animated-border { @keyframes animated-border {
0% { 0% {
box-shadow: 0 0 0 0px rgba(103, 194, 58, 0.7); box-shadow: 0 0 0 0px rgba(103, 194, 58, 0.7);
/* 对应 success 颜色 */
} }
70% { 70% {
box-shadow: 0 0 0 5px rgba(103, 194, 58, 0); box-shadow: 0 0 0 5px rgba(103, 194, 58, 0);
} }
100% { 100% {
box-shadow: 0 0 0 0px rgba(103, 194, 58, 0); box-shadow: 0 0 0 0px rgba(103, 194, 58, 0);
} }
@@ -1838,11 +1910,7 @@
/* 处理中状态的卡片左边框或标签动画 */ /* 处理中状态的卡片左边框或标签动画 */
.n-card[style*="border-left: 4px solid #63e2b7;"], .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; animation: animated-border 1.5s infinite;
} }
@@ -1853,9 +1921,41 @@
text-overflow: ellipsis; 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> </style>