mirror of
https://github.com/Megghy/vtsuru.live.git
synced 2025-12-06 18:36:55 +08:00
add more custom item in index
This commit is contained in:
@@ -104,6 +104,7 @@ export interface UserSetting {
|
||||
queue: Setting_Queue
|
||||
point: Setting_Point
|
||||
questionDisplay: Setting_QuestionDisplay
|
||||
index: Setting_Index
|
||||
|
||||
enableFunctions: FunctionTypes[]
|
||||
|
||||
@@ -111,6 +112,13 @@ export interface UserSetting {
|
||||
songListTemplate: string | null
|
||||
scheduleTemplate: string | null
|
||||
}
|
||||
export interface Setting_Index {
|
||||
videos: string[]
|
||||
notification: string
|
||||
links: {
|
||||
[key: string]: string
|
||||
}
|
||||
}
|
||||
export interface Setting_LiveRequest {
|
||||
orderPrefix: string
|
||||
sortType?: QueueSortType
|
||||
@@ -474,7 +482,7 @@ export enum SongRequestFrom {
|
||||
Danmaku,
|
||||
SC,
|
||||
Web,
|
||||
Gift
|
||||
Gift,
|
||||
}
|
||||
export enum QueueFrom {
|
||||
Manual,
|
||||
@@ -716,3 +724,11 @@ export enum PointFrom {
|
||||
Manual,
|
||||
Use,
|
||||
}
|
||||
|
||||
export interface ResponseUserIndexModel{
|
||||
notification: string
|
||||
videos: VideoCollectVideo[]
|
||||
links: {
|
||||
[key: string]: string
|
||||
}
|
||||
}
|
||||
|
||||
34
src/components/SimpleVideoCard.vue
Normal file
34
src/components/SimpleVideoCard.vue
Normal file
@@ -0,0 +1,34 @@
|
||||
<script setup lang="ts">
|
||||
import { VideoCollectVideo } from '@/api/api-models'
|
||||
import { NCard, NEllipsis, NImage, NText } from 'naive-ui'
|
||||
|
||||
const props = defineProps<{
|
||||
video: VideoCollectVideo
|
||||
width?: number
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<NCard size="small" hoverable :style="`max-width: ${width ?? 300}px;`">
|
||||
<template #cover>
|
||||
<NImage :src="video.cover" :img-props="{ referrerpolicy: 'no-referrer' }" />
|
||||
</template>
|
||||
<template #header>
|
||||
<a :href="`https://bilibili.com/video/${video.id}`" target="_blank">
|
||||
<NText>
|
||||
<NEllipsis>{{ video.title }}</NEllipsis>
|
||||
</NText>
|
||||
</a>
|
||||
</template>
|
||||
<NText depth="3" style="white-space: pre-line">
|
||||
<NEllipsis>
|
||||
{{ video.description }}
|
||||
<template #tooltip>
|
||||
<div style="white-space: pre-line; max-width: 300px">
|
||||
{{ video.description }}
|
||||
</div>
|
||||
</template>
|
||||
</NEllipsis>
|
||||
</NText>
|
||||
</NCard>
|
||||
</template>
|
||||
@@ -48,6 +48,7 @@ export const VTSURU_API_URL = { toString: () => `${BASE_API_URL}vtsuru/` }
|
||||
export const POINT_API_URL = { toString: () => `${BASE_API_URL}point/` }
|
||||
export const BILI_AUTH_API_URL = { toString: () => `${BASE_API_URL}bili-auth/` }
|
||||
export const FORUM_API_URL = { toString: () => `${BASE_API_URL}forum/` }
|
||||
export const USER_INDEX_API_URL = { toString: () => `${BASE_API_URL}user-index/` }
|
||||
|
||||
export const ScheduleTemplateMap = {
|
||||
'': {
|
||||
|
||||
@@ -6,10 +6,28 @@ import {
|
||||
downloadConfigDirect,
|
||||
useAccount,
|
||||
} from '@/api/account'
|
||||
import { FunctionTypes, ScheduleWeekInfo, SongFrom, SongLanguage, SongRequestOption, SongsInfo } from '@/api/api-models'
|
||||
import {
|
||||
FunctionTypes,
|
||||
ResponseUserIndexModel,
|
||||
ScheduleWeekInfo,
|
||||
SongFrom,
|
||||
SongLanguage,
|
||||
SongRequestOption,
|
||||
SongsInfo,
|
||||
VideoCollectVideo,
|
||||
} from '@/api/api-models'
|
||||
import { QueryGetAPI, QueryPostAPI } from '@/api/query'
|
||||
import DynamicForm from '@/components/DynamicForm.vue'
|
||||
import SimpleVideoCard from '@/components/SimpleVideoCard.vue'
|
||||
import { TemplateConfig } from '@/data/VTsuruTypes'
|
||||
import { FETCH_API, IndexTemplateMap, ScheduleTemplateMap, SongListTemplateMap } from '@/data/constants'
|
||||
import {
|
||||
FETCH_API,
|
||||
IndexTemplateMap,
|
||||
ScheduleTemplateMap,
|
||||
SongListTemplateMap,
|
||||
USER_INDEX_API_URL,
|
||||
} from '@/data/constants'
|
||||
import { Delete24Regular } from '@vicons/fluent'
|
||||
import {
|
||||
NAlert,
|
||||
NButton,
|
||||
@@ -18,15 +36,21 @@ import {
|
||||
NCheckboxGroup,
|
||||
NDivider,
|
||||
NEmpty,
|
||||
NFlex,
|
||||
NIcon,
|
||||
NInput,
|
||||
NList,
|
||||
NListItem,
|
||||
NModal,
|
||||
NPopconfirm,
|
||||
NSelect,
|
||||
NSpace,
|
||||
NSpin,
|
||||
NTabPane,
|
||||
NTabs,
|
||||
NTag,
|
||||
NText,
|
||||
NTooltip,
|
||||
SelectOption,
|
||||
useMessage,
|
||||
} from 'naive-ui'
|
||||
@@ -225,6 +249,15 @@ const selectedTemplateConfig = computed(() => {
|
||||
|
||||
const biliUserInfo = ref()
|
||||
const settingModalVisiable = ref(false)
|
||||
const showAddVideoModal = ref(false)
|
||||
const showAddLinkModal = ref(false)
|
||||
|
||||
const indexDisplayInfo = ref<ResponseUserIndexModel>()
|
||||
const addVideoUrl = ref('')
|
||||
const isLoading = ref(false)
|
||||
const addLinkName = ref('')
|
||||
const addLinkUrl = ref('')
|
||||
const linkKey = ref(0)
|
||||
|
||||
async function RequestBiliUserData() {
|
||||
await fetch(FETCH_API + `https://account.bilibili.com/api/member/getCardByMid?mid=10021741`).then(async (respone) => {
|
||||
@@ -303,6 +336,96 @@ async function SaveTemplateSetting() {
|
||||
await SaveComboSetting()
|
||||
}
|
||||
}
|
||||
async function updateIndexSettings() {
|
||||
await QueryPostAPI(USER_INDEX_API_URL + 'update-setting', accountInfo.value.settings.index)
|
||||
.then((data) => {
|
||||
if (data.code == 200) {
|
||||
message.success('已保存')
|
||||
} else {
|
||||
message.error('保存失败: ' + data.message)
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
message.error('保存失败: ' + err)
|
||||
})
|
||||
}
|
||||
async function addVideo() {
|
||||
if (!addVideoUrl.value) {
|
||||
message.error('请输入视频链接')
|
||||
return
|
||||
}
|
||||
isLoading.value = true
|
||||
await QueryGetAPI<VideoCollectVideo>(USER_INDEX_API_URL + 'add-video', {
|
||||
video: addVideoUrl.value,
|
||||
})
|
||||
.then((data) => {
|
||||
if (data.code == 200) {
|
||||
message.success('已添加')
|
||||
indexDisplayInfo.value?.videos.push(data.data)
|
||||
accountInfo.value?.settings.index.videos.push(data.data.id)
|
||||
addVideoUrl.value = ''
|
||||
} else {
|
||||
message.error('保存失败: ' + data.message)
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
message.error('保存失败: ' + err)
|
||||
})
|
||||
.finally(() => {
|
||||
isLoading.value = false
|
||||
})
|
||||
}
|
||||
async function removeVideo(id: string) {
|
||||
isLoading.value = true
|
||||
await QueryGetAPI<VideoCollectVideo>(USER_INDEX_API_URL + 'del-video', {
|
||||
video: id,
|
||||
})
|
||||
.then((data) => {
|
||||
if (data.code == 200) {
|
||||
message.success('已删除')
|
||||
if (indexDisplayInfo.value) {
|
||||
indexDisplayInfo.value.videos = indexDisplayInfo.value?.videos.filter((v) => v.id != id)
|
||||
}
|
||||
|
||||
accountInfo.value.settings.index.videos = accountInfo.value.settings.index.videos.filter((v) => v != id)
|
||||
} else {
|
||||
message.error('删除失败: ' + data.message)
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
message.error('删除失败: ' + err)
|
||||
})
|
||||
.finally(() => {
|
||||
isLoading.value = false
|
||||
})
|
||||
}
|
||||
async function addLink() {
|
||||
if (!addLinkName.value || !addLinkUrl.value) {
|
||||
message.error('请输入名称和链接')
|
||||
return
|
||||
}
|
||||
try {
|
||||
new URL(addLinkUrl.value)
|
||||
} catch (e) {
|
||||
message.error('请输入正确的链接')
|
||||
return
|
||||
}
|
||||
if (Object.keys(accountInfo.value.settings.index.links).includes(addLinkName.value)) {
|
||||
message.error(addLinkName.value + '已存在')
|
||||
return
|
||||
}
|
||||
accountInfo.value.settings.index.links[addLinkName.value] = addLinkUrl.value
|
||||
await updateIndexSettings()
|
||||
addLinkName.value = ''
|
||||
addLinkUrl.value = ''
|
||||
location.reload()
|
||||
}
|
||||
async function removeLink(name: string) {
|
||||
delete accountInfo.value.settings.index.links[name]
|
||||
await updateIndexSettings()
|
||||
|
||||
location.reload()
|
||||
}
|
||||
async function onOpenTemplateSettings() {
|
||||
settingModalVisiable.value = true
|
||||
nextTick(async () => {
|
||||
@@ -354,6 +477,23 @@ function unblockUser(id: number) {
|
||||
message.error(err)
|
||||
})
|
||||
}
|
||||
async function getIndexInfo() {
|
||||
try {
|
||||
isLoading.value = true
|
||||
const data = await QueryGetAPI<ResponseUserIndexModel>(USER_INDEX_API_URL + 'get', { id: accountInfo.value.id })
|
||||
if (data.code == 200) {
|
||||
return data.data
|
||||
} else if (data.code != 404) {
|
||||
message?.error('无法获取数据: ' + data.message)
|
||||
return undefined
|
||||
}
|
||||
} catch (err) {
|
||||
message?.error('无法获取数据: ' + err)
|
||||
return undefined
|
||||
} finally {
|
||||
isLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
onActivated(() => {
|
||||
if (route.query.tab) {
|
||||
@@ -365,6 +505,10 @@ onActivated(() => {
|
||||
})
|
||||
onMounted(async () => {
|
||||
await RequestBiliUserData()
|
||||
indexDisplayInfo.value = await getIndexInfo()
|
||||
if (route.query.tab) {
|
||||
message.info('已切换到指定面板, 在页面下方')
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -403,6 +547,57 @@ onMounted(async () => {
|
||||
</NCheckbox>
|
||||
</NSpace>
|
||||
</NTabPane>
|
||||
<NTabPane tab="主页" name="index">
|
||||
<NDivider> 通知 </NDivider>
|
||||
<NInput v-model:value="accountInfo.settings.index.notification" type="textarea" />
|
||||
<br /><br />
|
||||
<NButton type="primary" @click="updateIndexSettings"> 保存 </NButton>
|
||||
<NDivider> 展示视频 </NDivider>
|
||||
<NButton type="primary" @click="showAddVideoModal = true"> 添加视频 </NButton>
|
||||
<br /><br />
|
||||
<NEmpty v-if="accountInfo.settings.index.videos.length == 0" />
|
||||
<NFlex v-else>
|
||||
<NCard v-for="item in indexDisplayInfo?.videos ?? []" :key="item.id" style="width: 300px">
|
||||
<SimpleVideoCard :video="item" />
|
||||
<template #footer>
|
||||
<NButton type="warning" @click="removeVideo(item.id)"> 删除 </NButton>
|
||||
</template>
|
||||
</NCard>
|
||||
</NFlex>
|
||||
<NDivider> 其他链接 </NDivider>
|
||||
<NButton type="primary" @click="showAddLinkModal = true"> 添加链接 </NButton>
|
||||
<br /><br />
|
||||
<NEmpty v-if="Object.entries(indexDisplayInfo?.links ?? {}).length == 0" />
|
||||
<NFlex v-else :key="linkKey">
|
||||
<NFlex v-for="item in Object.entries(indexDisplayInfo?.links ?? {})" :key="item[0]" align="center">
|
||||
<NTooltip>
|
||||
<template #trigger>
|
||||
<NTag :bordered="false" size="small" type="info">
|
||||
{{ item[0] }}
|
||||
</NTag>
|
||||
</template>
|
||||
{{ item[1] }}
|
||||
</NTooltip>
|
||||
<NPopconfirm @positive-click="removeLink(item[0])">
|
||||
<template #trigger>
|
||||
<NButton type="error" text>
|
||||
<template #icon>
|
||||
<NIcon :component="Delete24Regular" />
|
||||
</template>
|
||||
</NButton>
|
||||
</template>
|
||||
确定要删除这个链接吗?
|
||||
</NPopconfirm>
|
||||
</NFlex>
|
||||
</NFlex>
|
||||
<NModal v-model:show="showAddLinkModal" :show-icon="false" preset="dialog" title="添加链接">
|
||||
<NFlex vertical>
|
||||
<NInput v-model:value="addLinkName" placeholder="链接名称" />
|
||||
<NInput v-model:value="addLinkUrl" placeholder="链接地址" />
|
||||
<NButton type="primary" @click="addLink"> 添加 </NButton>
|
||||
</NFlex>
|
||||
</NModal>
|
||||
</NTabPane>
|
||||
<NTabPane tab="黑名单" name="blacklist">
|
||||
<NList v-if="accountInfo.biliBlackList && Object.keys(accountInfo.biliBlackList).length > 0">
|
||||
<NListItem v-for="item in Object.entries(accountInfo.biliBlackList)" :key="item[0]">
|
||||
@@ -470,4 +665,15 @@ onMounted(async () => {
|
||||
:config="selectedTemplateConfig"
|
||||
/>
|
||||
</NModal>
|
||||
<NModal
|
||||
preset="card"
|
||||
v-model:show="showAddVideoModal"
|
||||
closable
|
||||
style="width: 600px; max-width: 90vw"
|
||||
title="添加视频"
|
||||
>
|
||||
<NInput v-model:value="addVideoUrl" placeholder="请输入视频链接" />
|
||||
<NDivider />
|
||||
<NButton type="primary" @click="addVideo" :loading="isLoading"> 添加视频 </NButton>
|
||||
</NModal>
|
||||
</template>
|
||||
|
||||
@@ -1,9 +1,15 @@
|
||||
<script lang="ts" setup>
|
||||
import { isDarkMode } from '@/Utils'
|
||||
import { UserInfo } from '@/api/api-models'
|
||||
import { useAccount } from '@/api/account'
|
||||
import { ResponseUserIndexModel, UserInfo } from '@/api/api-models'
|
||||
import { QueryGetAPI } from '@/api/query'
|
||||
import SimpleVideoCard from '@/components/SimpleVideoCard.vue'
|
||||
import { TemplateConfig } from '@/data/VTsuruTypes'
|
||||
import { NAvatar, NButton, NDivider, NSpace, NText } from 'naive-ui'
|
||||
import { USER_INDEX_API_URL } from '@/data/constants'
|
||||
import { NAlert, NAvatar, NButton, NCard, NDivider, NFlex, NSpace, NText, useMessage } from 'naive-ui'
|
||||
import { ref } from 'vue'
|
||||
|
||||
defineExpose({ Config, DefaultConfig })
|
||||
const width = window.innerWidth
|
||||
|
||||
const props = defineProps<{
|
||||
@@ -11,10 +17,32 @@ const props = defineProps<{
|
||||
biliInfo: any | undefined
|
||||
currentData?: any
|
||||
}>()
|
||||
const isLoading = ref(true)
|
||||
const message = useMessage()
|
||||
const accountInfo = useAccount()
|
||||
|
||||
const indexInfo = ref<ResponseUserIndexModel>((await getIndexInfo()) || ({} as ResponseUserIndexModel))
|
||||
async function getIndexInfo() {
|
||||
try {
|
||||
isLoading.value = true
|
||||
const data = await QueryGetAPI<ResponseUserIndexModel>(USER_INDEX_API_URL + 'get', { id: props.userInfo?.name })
|
||||
if (data.code == 200) {
|
||||
return data.data
|
||||
} else if (data.code != 404) {
|
||||
message?.error('无法获取数据: ' + data.message)
|
||||
return undefined
|
||||
}
|
||||
} catch (err) {
|
||||
message?.error('无法获取数据: ' + err)
|
||||
return undefined
|
||||
} finally {
|
||||
isLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function navigate(url: string) {
|
||||
window.open(url, '_blank')
|
||||
}
|
||||
defineExpose({ Config, DefaultConfig })
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
@@ -50,6 +78,19 @@ export const Config: TemplateConfig<ConfigType> = {
|
||||
<template>
|
||||
<NDivider />
|
||||
<template v-if="userInfo?.biliId">
|
||||
<template v-if="userInfo?.id == accountInfo?.id">
|
||||
<NButton type="primary" @click="$router.push({ name: 'manage-index', query: { tab: 'index' } })">
|
||||
自定义个人主页
|
||||
</NButton>
|
||||
<NDivider />
|
||||
</template>
|
||||
<template v-if="indexInfo?.notification">
|
||||
<NCard size="small" content-style="text-align: center">
|
||||
{{ indexInfo?.notification }}
|
||||
</NCard>
|
||||
<br />
|
||||
</template>
|
||||
|
||||
<NSpace justify="center" align="center" vertical>
|
||||
<NAvatar
|
||||
v-if="biliInfo"
|
||||
@@ -79,7 +120,31 @@ export const Config: TemplateConfig<ConfigType> = {
|
||||
<NButton type="primary" secondary @click="navigate('https://live.bilibili.com/' + userInfo?.biliRoomId)">
|
||||
直播间
|
||||
</NButton>
|
||||
<temlate v-if="Object.keys(indexInfo.links || {}).length > 0">
|
||||
<NFlex align="center">
|
||||
<NDivider vertical />
|
||||
<NButton
|
||||
type="info"
|
||||
secondary
|
||||
tag="a"
|
||||
:href="link[1]"
|
||||
target="_blank"
|
||||
v-for="link in Object.entries(indexInfo.links || {})"
|
||||
:key="link[0] + link[1]"
|
||||
>
|
||||
{{ link[0] }}
|
||||
</NButton>
|
||||
</NFlex>
|
||||
</temlate>
|
||||
</NSpace>
|
||||
<template v-if="indexInfo.videos?.length || 0 > 0">
|
||||
<NDivider>
|
||||
<NText>相关视频</NText>
|
||||
</NDivider>
|
||||
<NFlex justify="center">
|
||||
<SimpleVideoCard v-for="video in indexInfo.videos" :video="video" :key="video.id" />
|
||||
</NFlex>
|
||||
</template>
|
||||
</template>
|
||||
<template v-else>
|
||||
<NSpace justify="center" align="center">
|
||||
@@ -88,3 +153,4 @@ export const Config: TemplateConfig<ConfigType> = {
|
||||
</NSpace>
|
||||
</template>
|
||||
</template>
|
||||
import { QueryGetAPI } from '@/api/query' import { USER_INDEX_API_URL } from '@/data/constants'
|
||||
|
||||
Reference in New Issue
Block a user