mirror of
https://github.com/Megghy/vtsuru.live.git
synced 2025-12-06 18:36:55 +08:00
Compare commits
2 Commits
00ce0fc7e1
...
0591d0575d
| Author | SHA1 | Date | |
|---|---|---|---|
| 0591d0575d | |||
| 8b908f5ac9 |
BIN
message_render_content.txt
Normal file
BIN
message_render_content.txt
Normal file
Binary file not shown.
@@ -87,6 +87,33 @@ const batchUpdate_Option = ref<SongRequestOption | undefined>() // 批量编辑
|
|||||||
const columns = ref<DataTableColumns<SongsInfo>>() // 表格列定义
|
const columns = ref<DataTableColumns<SongsInfo>>() // 表格列定义
|
||||||
const selectedColumn = ref<DataTableRowKey[]>([]) // 表格选中行的 Key 数组
|
const selectedColumn = ref<DataTableRowKey[]>([]) // 表格选中行的 Key 数组
|
||||||
|
|
||||||
|
// 分页相关
|
||||||
|
const currentPage = ref(1) // 当前页码
|
||||||
|
const handlePageChange = (page: number) => {
|
||||||
|
currentPage.value = page
|
||||||
|
}
|
||||||
|
|
||||||
|
// 暴露分页方法
|
||||||
|
const nextPage = () => {
|
||||||
|
const pagination = songsComputed.value.length > 0 ? Math.ceil(songsComputed.value.length / pageSize.value) : 1
|
||||||
|
if (currentPage.value < pagination) {
|
||||||
|
currentPage.value++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const prevPage = () => {
|
||||||
|
if (currentPage.value > 1) {
|
||||||
|
currentPage.value--
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 暴露给父组件
|
||||||
|
defineExpose({
|
||||||
|
nextPage,
|
||||||
|
prevPage,
|
||||||
|
currentPage
|
||||||
|
})
|
||||||
|
|
||||||
// --- 计算属性 ---
|
// --- 计算属性 ---
|
||||||
|
|
||||||
// 筛选后的歌曲列表
|
// 筛选后的歌曲列表
|
||||||
@@ -163,8 +190,6 @@ const authorsOptions = computed(() => {
|
|||||||
}))
|
}))
|
||||||
})
|
})
|
||||||
|
|
||||||
// --- 表格列定义 ---
|
|
||||||
|
|
||||||
// 作者列定义 (包含筛选逻辑)
|
// 作者列定义 (包含筛选逻辑)
|
||||||
const authorColumn = ref<DataTableBaseColumn<SongsInfo>>({
|
const authorColumn = ref<DataTableBaseColumn<SongsInfo>>({
|
||||||
title: '作者',
|
title: '作者',
|
||||||
@@ -751,7 +776,8 @@ onMounted(() => {
|
|||||||
pageSizes: [10, 25, 50, 100, 200],
|
pageSizes: [10, 25, 50, 100, 200],
|
||||||
showSizePicker: true,
|
showSizePicker: true,
|
||||||
showQuickJumper: true,
|
showQuickJumper: true,
|
||||||
|
page: currentPage,
|
||||||
|
onUpdatePage: handlePageChange
|
||||||
}"
|
}"
|
||||||
:loading="isLoading && songsComputed.length === 0"
|
:loading="isLoading && songsComputed.length === 0"
|
||||||
striped
|
striped
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -21,4 +21,5 @@ export interface ScheduleConfigType {
|
|||||||
userInfo: UserInfo | undefined
|
userInfo: UserInfo | undefined
|
||||||
biliInfo: any | undefined
|
biliInfo: any | undefined
|
||||||
data: ScheduleWeekInfo[] | undefined
|
data: ScheduleWeekInfo[] | undefined
|
||||||
|
config?: any
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import DefaultIndexTemplateVue from '@/views/view/indexTemplate/DefaultIndexTemplate.vue';
|
import DefaultIndexTemplateVue from '@/views/view/indexTemplate/DefaultIndexTemplate.vue';
|
||||||
import { defineAsyncComponent, ref } from 'vue';
|
import { defineAsyncComponent, ref, markRaw } from 'vue';
|
||||||
|
|
||||||
const debugAPI =
|
const debugAPI =
|
||||||
import.meta.env.VITE_API == 'dev'
|
import.meta.env.VITE_API == 'dev'
|
||||||
@@ -74,40 +74,40 @@ export const ScheduleTemplateMap: TemplateMapType = {
|
|||||||
'': {
|
'': {
|
||||||
name: '默认',
|
name: '默认',
|
||||||
//settingName: 'Template.Schedule.Default',
|
//settingName: 'Template.Schedule.Default',
|
||||||
component: defineAsyncComponent(
|
component: markRaw(defineAsyncComponent(
|
||||||
() => import('@/views/view/scheduleTemplate/DefaultScheduleTemplate.vue')
|
() => import('@/views/view/scheduleTemplate/DefaultScheduleTemplate.vue')
|
||||||
)
|
))
|
||||||
},
|
},
|
||||||
pinky: {
|
pinky: {
|
||||||
name: '粉粉',
|
name: '粉粉',
|
||||||
//settingName: 'Template.Schedule.Pinky',
|
//settingName: 'Template.Schedule.Pinky',
|
||||||
component: defineAsyncComponent(
|
component: markRaw(defineAsyncComponent(
|
||||||
() => import('@/views/view/scheduleTemplate/PinkySchedule.vue')
|
() => import('@/views/view/scheduleTemplate/PinkySchedule.vue')
|
||||||
)
|
))
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
export const SongListTemplateMap: TemplateMapType = {
|
export const SongListTemplateMap: TemplateMapType = {
|
||||||
'': {
|
'': {
|
||||||
name: '默认',
|
name: '默认',
|
||||||
//settingName: 'Template.SongList.Default',
|
//settingName: 'Template.SongList.Default',
|
||||||
component: defineAsyncComponent(
|
component: markRaw(defineAsyncComponent(
|
||||||
() => import('@/views/view/songListTemplate/DefaultSongListTemplate.vue')
|
() => import('@/views/view/songListTemplate/DefaultSongListTemplate.vue')
|
||||||
)
|
))
|
||||||
},
|
},
|
||||||
simple: {
|
simple: {
|
||||||
name: '简单',
|
name: '简单',
|
||||||
//settingName: 'Template.SongList.Simple',
|
//settingName: 'Template.SongList.Simple',
|
||||||
component: defineAsyncComponent(
|
component: markRaw(defineAsyncComponent(
|
||||||
() => import('@/views/view/songListTemplate/SimpleSongListTemplate.vue')
|
() => import('@/views/view/songListTemplate/SimpleSongListTemplate.vue')
|
||||||
)
|
))
|
||||||
},
|
},
|
||||||
traditional: {
|
traditional: {
|
||||||
name: '列表',
|
name: '列表',
|
||||||
settingName: 'Template.SongList.Traditional',
|
settingName: 'Template.SongList.Traditional',
|
||||||
component: defineAsyncComponent(
|
component: markRaw(defineAsyncComponent(
|
||||||
() =>
|
() =>
|
||||||
import('@/views/view/songListTemplate/TraditionalSongListTemplate.vue')
|
import('@/views/view/songListTemplate/TraditionalSongListTemplate.vue')
|
||||||
)
|
))
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -115,7 +115,7 @@ export const IndexTemplateMap: TemplateMapType = {
|
|||||||
'': {
|
'': {
|
||||||
name: '默认',
|
name: '默认',
|
||||||
//settingName: 'Template.Index.Default',
|
//settingName: 'Template.Index.Default',
|
||||||
component: DefaultIndexTemplateVue
|
component: markRaw(DefaultIndexTemplateVue)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -57,7 +57,7 @@
|
|||||||
SelectOption,
|
SelectOption,
|
||||||
useMessage,
|
useMessage,
|
||||||
} from 'naive-ui';
|
} from 'naive-ui';
|
||||||
import { computed, h, nextTick, onActivated, onMounted, ref, shallowRef } from 'vue';
|
import { computed, h, nextTick, onActivated, onMounted, ref, shallowRef, markRaw } from 'vue';
|
||||||
import { useRoute } from 'vue-router';
|
import { useRoute } from 'vue-router';
|
||||||
|
|
||||||
// 模板定义类型接口
|
// 模板定义类型接口
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -89,9 +89,10 @@ const filteredUsers = computed(() => {
|
|||||||
|
|
||||||
// 根据关键词搜索
|
// 根据关键词搜索
|
||||||
if (settings.value.searchKeyword) {
|
if (settings.value.searchKeyword) {
|
||||||
|
const keyword = settings.value.searchKeyword.toLowerCase()
|
||||||
return (
|
return (
|
||||||
user.info.name?.toLowerCase().includes(settings.value.searchKeyword.toLowerCase()) == true ||
|
user.info.name?.toLowerCase().includes(keyword) == true ||
|
||||||
user.info.userId?.toString() == settings.value.searchKeyword
|
user.info.userId?.toString() == keyword
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -103,6 +104,76 @@ const filteredUsers = computed(() => {
|
|||||||
// 当前查看的用户详情
|
// 当前查看的用户详情
|
||||||
const currentUser = ref<ResponsePointUserModel>()
|
const currentUser = ref<ResponsePointUserModel>()
|
||||||
|
|
||||||
|
// 渲染用户名或用户ID
|
||||||
|
const renderUsername = (user: ResponsePointUserModel) => {
|
||||||
|
if (user.info?.name) {
|
||||||
|
return user.info.name
|
||||||
|
}
|
||||||
|
|
||||||
|
return h(NFlex, null, () => [
|
||||||
|
'未知',
|
||||||
|
h(NText, { depth: 3 }, { default: () => `(${user.info.userId ?? user.info.openId})` }),
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
// 渲染订单数量,更友好的显示方式
|
||||||
|
const renderOrderCount = (user: ResponsePointUserModel) => {
|
||||||
|
if (!user.isAuthed) return h(NText, { depth: 3 }, { default: () => '未认证' })
|
||||||
|
return user.orderCount > 0 ? h(NText, {}, { default: () => formatNumber(user.orderCount) }) : h(NText, { depth: 3 }, { default: () => '无订单' })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 渲染时间戳为相对时间和绝对时间
|
||||||
|
const renderTime = (timestamp: number) => {
|
||||||
|
return h(NTooltip, null, {
|
||||||
|
trigger: () => h(NTime, { time: timestamp, type: 'relative' }),
|
||||||
|
default: () => h(NTime, { time: timestamp }),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 渲染操作按钮
|
||||||
|
const renderActions = (user: ResponsePointUserModel) => {
|
||||||
|
return h(NFlex, { justify: 'center', gap: 8 }, () => [
|
||||||
|
h(
|
||||||
|
NButton,
|
||||||
|
{
|
||||||
|
onClick: () => {
|
||||||
|
currentUser.value = user
|
||||||
|
showModal.value = true
|
||||||
|
},
|
||||||
|
type: 'info',
|
||||||
|
size: 'small',
|
||||||
|
},
|
||||||
|
{ default: () => '详情' },
|
||||||
|
),
|
||||||
|
h(
|
||||||
|
NPopconfirm,
|
||||||
|
{ onPositiveClick: () => deleteUser(user) },
|
||||||
|
{
|
||||||
|
default: () => '确定要删除这个用户吗?记录将无法恢复',
|
||||||
|
trigger: () =>
|
||||||
|
h(
|
||||||
|
NButton,
|
||||||
|
{
|
||||||
|
type: 'error',
|
||||||
|
size: 'small',
|
||||||
|
},
|
||||||
|
{ default: () => '删除' },
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
// 格式化数字,添加千位符
|
||||||
|
const formatNumber = (num: number) => {
|
||||||
|
return num.toLocaleString('zh-CN')
|
||||||
|
}
|
||||||
|
|
||||||
|
// 渲染积分,添加千位符并加粗
|
||||||
|
const renderPoint = (num: number) => {
|
||||||
|
return h(NText, { strong: true }, { default: () => formatNumber(num) })
|
||||||
|
}
|
||||||
|
|
||||||
// 数据表格列定义
|
// 数据表格列定义
|
||||||
const column: DataTableColumns<ResponsePointUserModel> = [
|
const column: DataTableColumns<ResponsePointUserModel> = [
|
||||||
{
|
{
|
||||||
@@ -115,77 +186,29 @@ const column: DataTableColumns<ResponsePointUserModel> = [
|
|||||||
{
|
{
|
||||||
title: '用户名',
|
title: '用户名',
|
||||||
key: 'username',
|
key: 'username',
|
||||||
render: (row: ResponsePointUserModel) => {
|
render: (row: ResponsePointUserModel) => renderUsername(row),
|
||||||
return (
|
|
||||||
row.info?.name ??
|
|
||||||
h(NFlex, null, () => [
|
|
||||||
'未知',
|
|
||||||
h(NText, { depth: 3 }, { default: () => `(${row.info.userId ?? row.info.openId})` }),
|
|
||||||
])
|
|
||||||
)
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '积分',
|
title: '积分',
|
||||||
key: 'point',
|
key: 'point',
|
||||||
sorter: 'default',
|
sorter: 'default',
|
||||||
render: (row: ResponsePointUserModel) => {
|
render: (row: ResponsePointUserModel) => renderPoint(row.point),
|
||||||
return row.point
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '订单数量',
|
title: '订单数量',
|
||||||
key: 'orders',
|
key: 'orderCount',
|
||||||
render: (row: ResponsePointUserModel) => {
|
render: (row: ResponsePointUserModel) => renderOrderCount(row),
|
||||||
return row.isAuthed ? row.orderCount : '无'
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '最后更新于',
|
title: '最后更新于',
|
||||||
key: 'updateAt',
|
key: 'updateAt',
|
||||||
sorter: 'default',
|
sorter: 'default',
|
||||||
render: (row: ResponsePointUserModel) => {
|
render: (row: ResponsePointUserModel) => renderTime(row.updateAt),
|
||||||
return h(NTooltip, null, {
|
|
||||||
trigger: () => h(NTime, { time: row.updateAt, type: 'relative' }),
|
|
||||||
default: () => h(NTime, { time: row.updateAt }),
|
|
||||||
})
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '操作',
|
title: '操作',
|
||||||
key: 'action',
|
key: 'action',
|
||||||
render: (row: ResponsePointUserModel) => {
|
render: (row: ResponsePointUserModel) => renderActions(row),
|
||||||
return h(NFlex, { justify: 'center', gap: 8 }, () => [
|
|
||||||
h(
|
|
||||||
NButton,
|
|
||||||
{
|
|
||||||
onClick: () => {
|
|
||||||
currentUser.value = row
|
|
||||||
showModal.value = true
|
|
||||||
},
|
|
||||||
type: 'info',
|
|
||||||
size: 'small',
|
|
||||||
},
|
|
||||||
{ default: () => '详情' },
|
|
||||||
),
|
|
||||||
h(
|
|
||||||
NPopconfirm,
|
|
||||||
{ onPositiveClick: () => deleteUser(row) },
|
|
||||||
{
|
|
||||||
default: () => '确定要删除这个用户吗?记录将无法恢复',
|
|
||||||
trigger: () =>
|
|
||||||
h(
|
|
||||||
NButton,
|
|
||||||
{
|
|
||||||
type: 'error',
|
|
||||||
size: 'small',
|
|
||||||
},
|
|
||||||
{ default: () => '删除' },
|
|
||||||
),
|
|
||||||
},
|
|
||||||
),
|
|
||||||
])
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -445,10 +468,10 @@ onMounted(async () => {
|
|||||||
<NDivider />
|
<NDivider />
|
||||||
|
|
||||||
<!-- 无数据提示 -->
|
<!-- 无数据提示 -->
|
||||||
<template v-if="filteredUsers.length == 0">
|
<NEmpty
|
||||||
<NDivider />
|
v-if="filteredUsers.length == 0"
|
||||||
<NEmpty :description="settings.onlyAuthed ? '没有已认证的用户' : '没有用户'" />
|
:description="isLoading ? '加载中...' : (settings.onlyAuthed ? '没有已认证的用户' : '没有用户')"
|
||||||
</template>
|
/>
|
||||||
|
|
||||||
<!-- 用户数据表格 -->
|
<!-- 用户数据表格 -->
|
||||||
<NDataTable
|
<NDataTable
|
||||||
@@ -465,6 +488,7 @@ onMounted(async () => {
|
|||||||
onUpdatePage: (page) => (pn = page),
|
onUpdatePage: (page) => (pn = page),
|
||||||
onUpdatePageSize: (pageSize) => (ps = pageSize)
|
onUpdatePageSize: (pageSize) => (ps = pageSize)
|
||||||
}"
|
}"
|
||||||
|
:loading="isLoading"
|
||||||
/>
|
/>
|
||||||
</NSpin>
|
</NSpin>
|
||||||
|
|
||||||
@@ -592,8 +616,8 @@ onMounted(async () => {
|
|||||||
<NButton
|
<NButton
|
||||||
type="error"
|
type="error"
|
||||||
:loading="isLoading"
|
:loading="isLoading"
|
||||||
@click="resetAllPoints"
|
|
||||||
:disabled="resetConfirmText !== RESET_CONFIRM_TEXT"
|
:disabled="resetConfirmText !== RESET_CONFIRM_TEXT"
|
||||||
|
@click="resetAllPoints"
|
||||||
>
|
>
|
||||||
确认重置所有用户积分
|
确认重置所有用户积分
|
||||||
</NButton>
|
</NButton>
|
||||||
|
|||||||
@@ -44,7 +44,7 @@
|
|||||||
:author-name="message.authorName"
|
:author-name="message.authorName"
|
||||||
:author-type="message.authorType"
|
:author-type="message.authorType"
|
||||||
:privilege-type="message.privilegeType"
|
:privilege-type="message.privilegeType"
|
||||||
:rich-content="getShowRichContent(message)"
|
:content-parts="getShowContentParts(message)"
|
||||||
:repeated="message.repeated"
|
:repeated="message.repeated"
|
||||||
/>
|
/>
|
||||||
<paid-message
|
<paid-message
|
||||||
@@ -205,7 +205,7 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
getGiftShowNameAndNum: constants.getGiftShowNameAndNum,
|
getGiftShowNameAndNum: constants.getGiftShowNameAndNum,
|
||||||
getShowContent: constants.getShowContent,
|
getShowContent: constants.getShowContent,
|
||||||
getShowRichContent: constants.getShowRichContent,
|
getShowContentParts: constants.getShowContentParts,
|
||||||
getShowAuthorName: constants.getShowAuthorName,
|
getShowAuthorName: constants.getShowAuthorName,
|
||||||
|
|
||||||
addMessage(message) {
|
addMessage(message) {
|
||||||
|
|||||||
@@ -29,7 +29,7 @@
|
|||||||
id="message"
|
id="message"
|
||||||
class="style-scope yt-live-chat-text-message-renderer"
|
class="style-scope yt-live-chat-text-message-renderer"
|
||||||
>
|
>
|
||||||
<template v-for="(content, index) in richContent">
|
<template v-for="(content, index) in contentParts">
|
||||||
<span
|
<span
|
||||||
v-if="content.type === CONTENT_TYPE_TEXT"
|
v-if="content.type === CONTENT_TYPE_TEXT"
|
||||||
:key="index"
|
:key="index"
|
||||||
@@ -81,7 +81,7 @@ const props = defineProps({
|
|||||||
time: Date,
|
time: Date,
|
||||||
authorName: String,
|
authorName: String,
|
||||||
authorType: Number,
|
authorType: Number,
|
||||||
richContent: Array,
|
contentParts: Array,
|
||||||
privilegeType: Number,
|
privilegeType: Number,
|
||||||
repeated: Number
|
repeated: Number
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ const props = defineProps<{
|
|||||||
userInfo: UserInfo | undefined
|
userInfo: UserInfo | undefined
|
||||||
biliInfo: any | undefined
|
biliInfo: any | undefined
|
||||||
currentData?: any
|
currentData?: any
|
||||||
|
config?: any
|
||||||
}>()
|
}>()
|
||||||
const isLoading = ref(true)
|
const isLoading = ref(true)
|
||||||
const message = useMessage()
|
const message = useMessage()
|
||||||
|
|||||||
@@ -4,9 +4,9 @@ import { SongsInfo } from '@/api/api-models'
|
|||||||
import SongList from '@/components/SongList.vue'
|
import SongList from '@/components/SongList.vue'
|
||||||
import { SongListConfigType } from '@/data/TemplateTypes'
|
import { SongListConfigType } from '@/data/TemplateTypes'
|
||||||
import LiveRequestOBS from '@/views/obs/LiveRequestOBS.vue'
|
import LiveRequestOBS from '@/views/obs/LiveRequestOBS.vue'
|
||||||
import { CloudAdd20Filled } from '@vicons/fluent'
|
import { CloudAdd20Filled, ChevronLeft24Filled, ChevronRight24Filled } from '@vicons/fluent'
|
||||||
import { NButton, NCard, NCollapse, NCollapseItem, NDivider, NIcon, NTooltip, useMessage } from 'naive-ui'
|
import { NButton, NCard, NCollapse, NCollapseItem, NDivider, NIcon, NTooltip, useMessage } from 'naive-ui'
|
||||||
import { h, ref } from 'vue'
|
import { h, ref, onMounted, onUnmounted } from 'vue'
|
||||||
|
|
||||||
const accountInfo = useAccount()
|
const accountInfo = useAccount()
|
||||||
|
|
||||||
@@ -16,6 +16,50 @@ const emits = defineEmits(['requestSong'])
|
|||||||
|
|
||||||
const isLoading = ref('')
|
const isLoading = ref('')
|
||||||
const message = useMessage()
|
const message = useMessage()
|
||||||
|
const songListRef = ref<InstanceType<typeof SongList> | null>(null)
|
||||||
|
|
||||||
|
// 处理翻页逻辑
|
||||||
|
const handlePrevPage = () => {
|
||||||
|
if (songListRef.value) {
|
||||||
|
songListRef.value.prevPage()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleNextPage = () => {
|
||||||
|
if (songListRef.value) {
|
||||||
|
songListRef.value.nextPage()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 键盘快捷键处理函数
|
||||||
|
const handleKeyDown = (event: KeyboardEvent) => {
|
||||||
|
// 忽略在输入框内的按键
|
||||||
|
if (event.target instanceof HTMLInputElement ||
|
||||||
|
event.target instanceof HTMLTextAreaElement ||
|
||||||
|
event.target instanceof HTMLSelectElement) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 左方向键 - 上一页
|
||||||
|
if (event.key === 'ArrowLeft') {
|
||||||
|
handlePrevPage()
|
||||||
|
event.preventDefault()
|
||||||
|
}
|
||||||
|
// 右方向键 - 下一页
|
||||||
|
else if (event.key === 'ArrowRight') {
|
||||||
|
handleNextPage()
|
||||||
|
event.preventDefault()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加和移除事件监听器
|
||||||
|
onMounted(() => {
|
||||||
|
window.addEventListener('keydown', handleKeyDown)
|
||||||
|
})
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
window.removeEventListener('keydown', handleKeyDown)
|
||||||
|
})
|
||||||
|
|
||||||
const buttons = (song: SongsInfo) => [
|
const buttons = (song: SongsInfo) => [
|
||||||
accountInfo.value?.id != props.userInfo?.id
|
accountInfo.value?.id != props.userInfo?.id
|
||||||
@@ -55,25 +99,89 @@ const buttons = (song: SongsInfo) => [
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<NDivider style="margin-top: 10px" />
|
<div class="song-list-container">
|
||||||
<SongList
|
<NDivider style="margin-top: 10px" />
|
||||||
v-if="data"
|
<!-- 左侧翻页按钮 -->
|
||||||
:songs="data ?? []"
|
<div class="page-button page-button-left">
|
||||||
:is-self="accountInfo?.id == userInfo?.id"
|
<NButton
|
||||||
:extra-button="buttons"
|
circle
|
||||||
v-bind="$attrs"
|
secondary
|
||||||
/>
|
size="large"
|
||||||
<NCollapse v-if="userInfo?.canRequestSong">
|
title="上一页 (←)"
|
||||||
<NCollapseItem title="点歌列表">
|
@click="handlePrevPage"
|
||||||
<NCard
|
|
||||||
size="small"
|
|
||||||
embedded
|
|
||||||
>
|
>
|
||||||
<div style="height: 400px; width: 700px; max-width: 100%; position: relative; margin: 0 auto">
|
<template #icon>
|
||||||
<LiveRequestOBS :id="userInfo?.id" />
|
<NIcon :component="ChevronLeft24Filled" />
|
||||||
</div>
|
</template>
|
||||||
</NCard>
|
</NButton>
|
||||||
</NCollapseItem>
|
</div>
|
||||||
</NCollapse>
|
|
||||||
<NDivider />
|
<!-- 右侧翻页按钮 -->
|
||||||
|
<div class="page-button page-button-right">
|
||||||
|
<NButton
|
||||||
|
circle
|
||||||
|
secondary
|
||||||
|
size="large"
|
||||||
|
title="下一页 (→)"
|
||||||
|
@click="handleNextPage"
|
||||||
|
>
|
||||||
|
<template #icon>
|
||||||
|
<NIcon :component="ChevronRight24Filled" />
|
||||||
|
</template>
|
||||||
|
</NButton>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<SongList
|
||||||
|
v-if="data"
|
||||||
|
ref="songListRef"
|
||||||
|
:songs="data ?? []"
|
||||||
|
:is-self="accountInfo?.id == userInfo?.id"
|
||||||
|
:extra-button="buttons"
|
||||||
|
v-bind="$attrs"
|
||||||
|
/>
|
||||||
|
<NCollapse v-if="userInfo?.canRequestSong">
|
||||||
|
<NCollapseItem title="点歌列表">
|
||||||
|
<NCard
|
||||||
|
size="small"
|
||||||
|
embedded
|
||||||
|
>
|
||||||
|
<div style="height: 400px; width: 700px; max-width: 100%; position: relative; margin: 0 auto">
|
||||||
|
<LiveRequestOBS :id="userInfo?.id" />
|
||||||
|
</div>
|
||||||
|
</NCard>
|
||||||
|
</NCollapseItem>
|
||||||
|
</NCollapse>
|
||||||
|
<NDivider />
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.song-list-container {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-button {
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
z-index: 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-button-left {
|
||||||
|
left: -20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-button-right {
|
||||||
|
right: -20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.page-button-left {
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-button-right {
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
Reference in New Issue
Block a user