mirror of
https://github.com/Megghy/vtsuru.live.git
synced 2025-12-07 02:46:55 +08:00
update
This commit is contained in:
@@ -1,12 +1,47 @@
|
||||
<script setup lang="ts">
|
||||
import { NAvatar, NButton, NCard, NDivider, NIcon, NLayout, NLayoutFooter, NLayoutHeader, NLayoutSider, NMenu, NSpace, NText } from 'naive-ui'
|
||||
import { h } from 'vue'
|
||||
import { BookOutline } from '@vicons/ionicons5'
|
||||
import { useAccount } from '@/api/account'
|
||||
import {
|
||||
NAlert,
|
||||
NAvatar,
|
||||
NButton,
|
||||
NCard,
|
||||
NCountdown,
|
||||
NDivider,
|
||||
NIcon,
|
||||
NLayout,
|
||||
NLayoutContent,
|
||||
NLayoutFooter,
|
||||
NLayoutHeader,
|
||||
NLayoutSider,
|
||||
NMenu,
|
||||
NPageHeader,
|
||||
NSpace,
|
||||
NSpin,
|
||||
NSwitch,
|
||||
NText,
|
||||
NTime,
|
||||
useMessage,
|
||||
} from 'naive-ui'
|
||||
import { h, onMounted, ref } from 'vue'
|
||||
import { BrowsersOutline, Chatbox, Moon, MusicalNote, Sunny } from '@vicons/ionicons5'
|
||||
import { Lottery24Filled } from '@vicons/fluent'
|
||||
import { isLoadingAccount, useAccount } from '@/api/account'
|
||||
import RegisterAndLogin from '@/components/RegisterAndLogin.vue'
|
||||
import { RouterLink } from 'vue-router'
|
||||
import { useElementSize, useStorage } from '@vueuse/core'
|
||||
import { ACCOUNT_API_URL } from '@/data/constants'
|
||||
import { QueryGetAPI } from '@/api/query'
|
||||
import { ThemeType } from '@/api/api-models'
|
||||
import { isDarkMode } from '@/Utils'
|
||||
|
||||
const accountInfo = useAccount()
|
||||
const message = useMessage()
|
||||
|
||||
const windowWidth = window.innerWidth
|
||||
const sider = ref()
|
||||
const { width } = useElementSize(sider)
|
||||
const themeType = useStorage('Settings.Theme', ThemeType.Auto)
|
||||
|
||||
const canResendEmail = ref(false)
|
||||
|
||||
function renderIcon(icon: unknown) {
|
||||
return () => h(NIcon, null, { default: () => h(icon as any) })
|
||||
@@ -21,11 +56,12 @@ const menuOptions = [
|
||||
to: {
|
||||
name: 'manage-songList',
|
||||
},
|
||||
disabled: !accountInfo.value?.isEmailVerified,
|
||||
},
|
||||
{ default: () => '歌单' }
|
||||
),
|
||||
key: 'manage-songList',
|
||||
icon: renderIcon(BookOutline),
|
||||
icon: renderIcon(MusicalNote),
|
||||
},
|
||||
{
|
||||
label: () =>
|
||||
@@ -39,7 +75,7 @@ const menuOptions = [
|
||||
{ default: () => '棉花糖 (提问箱' }
|
||||
),
|
||||
key: 'manage-questionBox',
|
||||
icon: renderIcon(BookOutline),
|
||||
icon: renderIcon(Chatbox),
|
||||
},
|
||||
{
|
||||
label: () =>
|
||||
@@ -53,38 +89,117 @@ const menuOptions = [
|
||||
{ default: () => '动态抽奖' }
|
||||
),
|
||||
key: 'manage-lottery',
|
||||
icon: renderIcon(BookOutline),
|
||||
icon: renderIcon(Lottery24Filled),
|
||||
},
|
||||
]
|
||||
|
||||
async function resendEmail() {
|
||||
await QueryGetAPI(ACCOUNT_API_URL + 'send-verify-email')
|
||||
.then((data) => {
|
||||
if (data.code == 200) {
|
||||
canResendEmail.value = false
|
||||
message.success('发送成功, 请检查你的邮箱. 如果没有收到, 请检查垃圾邮件')
|
||||
if (accountInfo.value && accountInfo.value.nextSendEmailTime) accountInfo.value.nextSendEmailTime += 1000 * 60
|
||||
} else {
|
||||
message.error('发送失败: ' + data.message)
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(err)
|
||||
message.error('发送失败')
|
||||
})
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
if (accountInfo.value?.isEmailVerified == false) {
|
||||
if ((accountInfo.value?.nextSendEmailTime ?? -1) <= 0) {
|
||||
canResendEmail.value = true
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<NLayout v-if="accountInfo">
|
||||
<NLayoutHeader bordered style="height: 50px"> Header Header Header </NLayoutHeader>
|
||||
<NLayoutHeader bordered style="height: 50px; padding: 10px 15px 5px 15px">
|
||||
<NPageHeader>
|
||||
<template #title>
|
||||
<NText strong style="font-size: 1.4rem; text-shadow: 0 2px 4px rgba(0, 0, 0, 0.1)"> VTSURU CENTER </NText>
|
||||
</template>
|
||||
<template #extra>
|
||||
<NSpace align="center" justify="center">
|
||||
<NSwitch :default-value="!isDarkMode()" @update:value="(value: string & number & boolean) => themeType = value ? ThemeType.Light : ThemeType.Dark">
|
||||
<template #checked>
|
||||
<NIcon :component="Sunny" />
|
||||
</template>
|
||||
<template #unchecked>
|
||||
<NIcon :component="Moon" />
|
||||
</template>
|
||||
</NSwitch>
|
||||
<NButton size="small" style="right: 0px; position: relative" type="primary" @click="$router.push({ name: 'user-index', params: { id: accountInfo.id } })"> 回到主页 </NButton>
|
||||
</NSpace>
|
||||
</template>
|
||||
</NPageHeader>
|
||||
</NLayoutHeader>
|
||||
<NLayout has-sider style="height: calc(100vh - 50px)">
|
||||
<NLayoutSider bordered show-trigger collapse-mode="width" :collapsed-width="64" :width="180" :native-scrollbar="false" style="max-height: 320px">
|
||||
<NButton>
|
||||
<RouterLink :to="{ name: 'manage-index' }"> 个人中心 </RouterLink>
|
||||
</NButton>
|
||||
<NMenu :default-value="$route.name?.toString()" :collapsed-width="64" :collapsed-icon-size="22" :options="menuOptions" />
|
||||
<NLayoutSider ref="sider" bordered show-trigger collapse-mode="width" :default-collapsed="windowWidth < 750" :collapsed-width="64" :width="180" :native-scrollbar="false">
|
||||
<NSpace justify="center" style="margin-top: 16px">
|
||||
<NButton @click="$router.push({ name: 'manage-index' })" type="info" style="width: 100%">
|
||||
<template #icon>
|
||||
<NIcon :component="BrowsersOutline" />
|
||||
</template>
|
||||
<template v-if="width >= 180"> 面板 </template>
|
||||
</NButton>
|
||||
</NSpace>
|
||||
<NMenu
|
||||
style="margin-top: 12px"
|
||||
:disabled="accountInfo?.isEmailVerified != true"
|
||||
:default-value="$route.name?.toString()"
|
||||
:collapsed-width="64"
|
||||
:collapsed-icon-size="22"
|
||||
:options="menuOptions"
|
||||
/>
|
||||
<NSpace justify="center">
|
||||
<NText depth="3" v-if="width > 150">
|
||||
有更多功能建议请
|
||||
<NButton text type="info"> 反馈 </NButton>
|
||||
</NText>
|
||||
</NSpace>
|
||||
</NLayoutSider>
|
||||
<NLayout style="height: 100%">
|
||||
<div style="box-sizing: border-box; padding: 20px">
|
||||
<RouterView v-slot="{ Component }">
|
||||
<RouterView v-slot="{ Component }" v-if="accountInfo?.isEmailVerified">
|
||||
<KeepAlive>
|
||||
<component :is="Component" />
|
||||
</KeepAlive>
|
||||
</RouterView>
|
||||
<template v-else>
|
||||
<NAlert type="info">
|
||||
请进行邮箱验证
|
||||
<br /><br />
|
||||
<NButton size="small" type="info" :disabled="!canResendEmail" @click="resendEmail"> 重新发送验证邮件 </NButton>
|
||||
<NCountdown v-if="!canResendEmail" :duration="(accountInfo?.nextSendEmailTime ?? 0) - Date.now()" @finish="canResendEmail = true" />
|
||||
</NAlert>
|
||||
</template>
|
||||
</div>
|
||||
</NLayout>
|
||||
</NLayout>
|
||||
</NLayout>
|
||||
<template v-else>
|
||||
<div style="display: flex; justify-content: center; align-items: center; flex-direction: column; padding: 50px; height: 100%; box-sizing: border-box">
|
||||
<NText> 请登录或注册后使用 </NText>
|
||||
<NButton tag="a" href="/"> 回到主页 </NButton>
|
||||
<NDivider />
|
||||
<RegisterAndLogin style="max-width: 500px; min-width: 350px" />
|
||||
</div>
|
||||
<NLayoutContent style="display: flex; justify-content: center; align-items: center; flex-direction: column; padding: 50px; height: 100%; box-sizing: border-box">
|
||||
<template v-if="!isLoadingAccount">
|
||||
<NSpace vertical justify="center" align="center">
|
||||
<NText> 请登录或注册后使用 </NText>
|
||||
<NButton tag="a" href="/"> 回到主页 </NButton>
|
||||
</NSpace>
|
||||
<NDivider />
|
||||
<RegisterAndLogin style="max-width: 500px; min-width: 350px" />
|
||||
</template>
|
||||
<template v-else>
|
||||
<NSpin :loading="isLoadingAccount">
|
||||
正在请求账户数据...
|
||||
</NSpin>
|
||||
</template>
|
||||
</NLayoutContent>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
<script setup lang="ts">
|
||||
import { ACCOUNT } from '@/api/account'
|
||||
import { ACCOUNT, useAccount } from '@/api/account'
|
||||
import { AccountInfo } from '@/api/api-models'
|
||||
import { QueryGetAPI } from '@/api/query'
|
||||
import { ACCOUNT_API_URL, TURNSTILE_KEY } from '@/data/constants'
|
||||
import router from '@/router'
|
||||
import { NButton, NCard, NSpace, NSpin, useMessage } from 'naive-ui'
|
||||
import { NAlert, NButton, NCard, NSpace, NSpin, useMessage } from 'naive-ui'
|
||||
import { ref } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import VueTurnstile from 'vue-turnstile'
|
||||
|
||||
const accountInfo = useAccount()
|
||||
|
||||
const message = useMessage()
|
||||
const token = ref('')
|
||||
const route = useRoute()
|
||||
@@ -23,8 +25,11 @@ async function VerifyAccount() {
|
||||
).then((data) => {
|
||||
if (data.code == 200) {
|
||||
ACCOUNT.value = data.data
|
||||
router.push('index')
|
||||
message.success('成功激活账户: ' + ACCOUNT.value.name)
|
||||
router.push('/manage')
|
||||
}
|
||||
else {
|
||||
message.error('激活失败: ' + data.message)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -32,7 +37,7 @@ async function VerifyAccount() {
|
||||
|
||||
<template>
|
||||
<div style="display: flex; align-items: center; justify-content: center; height: 100%">
|
||||
<NCard embedded style="max-width: 500px">
|
||||
<NCard v-if="accountInfo?.isEmailVerified != true" embedded style="max-width: 500px">
|
||||
<template #header> 激活账户 </template>
|
||||
<NSpin :show="!token">
|
||||
<NSpace justify="center" align="center" vertical>
|
||||
@@ -41,5 +46,8 @@ async function VerifyAccount() {
|
||||
</NSpace>
|
||||
</NSpin>
|
||||
</NCard>
|
||||
<NAlert v-else type="error">
|
||||
此账户已完成验证
|
||||
</NAlert>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -1,74 +1,51 @@
|
||||
<!-- eslint-disable vue/component-name-in-template-casing -->
|
||||
<script setup lang="ts">
|
||||
import { NAvatar, NCard, NIcon, NLayout, NLayoutFooter, NLayoutHeader, NLayoutSider, NMenu, NSpace, NText, NButton, NEmpty, NResult, NPageHeader, NSwitch, useOsTheme, NModal } from 'naive-ui'
|
||||
import {
|
||||
NAvatar,
|
||||
NIcon,
|
||||
NLayout,
|
||||
NLayoutHeader,
|
||||
NLayoutSider,
|
||||
NMenu,
|
||||
NSpace,
|
||||
NText,
|
||||
NButton,
|
||||
NResult,
|
||||
NPageHeader,
|
||||
NSwitch,
|
||||
NModal,
|
||||
NEllipsis,
|
||||
MenuOption,
|
||||
} from 'naive-ui'
|
||||
import { computed, h, onMounted, ref } from 'vue'
|
||||
import { BookOutline as BookIcon, PersonOutline as PersonIcon, WineOutline as WineIcon } from '@vicons/ionicons5'
|
||||
import { BookOutline as BookIcon, Chatbox, Home, Moon, MusicalNote, PersonOutline as PersonIcon, Sunny, WineOutline as WineIcon } from '@vicons/ionicons5'
|
||||
import { GetInfo, useUser, useUserWithUId } from '@/api/user'
|
||||
import { RouterLink, useRoute } from 'vue-router'
|
||||
import { UserInfo } from '@/api/api-models'
|
||||
import { FunctionTypes, ThemeType, UserInfo } from '@/api/api-models'
|
||||
import { FETCH_API } from '@/data/constants'
|
||||
import { useAccount } from '@/api/account'
|
||||
import RegisterAndLogin from '@/components/RegisterAndLogin.vue'
|
||||
import { useElementSize, useStorage } from '@vueuse/core'
|
||||
import { isDarkMode } from '@/Utils'
|
||||
|
||||
const route = useRoute()
|
||||
const id = computed(() => {
|
||||
return Number(route.params.id)
|
||||
})
|
||||
const theme = useOsTheme()
|
||||
const themeType = useStorage('Settings.Theme', ThemeType.Auto);
|
||||
|
||||
const userInfo = ref<UserInfo>()
|
||||
const biliUserInfo = ref()
|
||||
const accountInfo = useAccount()
|
||||
|
||||
const registerAndLoginModalVisiable = ref(false)
|
||||
const sider = ref()
|
||||
const { width } = useElementSize(sider)
|
||||
|
||||
function renderIcon(icon: unknown) {
|
||||
return () => h(NIcon, null, { default: () => h(icon as any) })
|
||||
}
|
||||
const menuOptions = [
|
||||
{
|
||||
label: () =>
|
||||
h(
|
||||
RouterLink,
|
||||
{
|
||||
to: {
|
||||
name: 'user-index',
|
||||
},
|
||||
},
|
||||
{ default: () => '主页' }
|
||||
),
|
||||
key: 'user-index',
|
||||
icon: renderIcon(BookIcon),
|
||||
},
|
||||
{
|
||||
label: () =>
|
||||
h(
|
||||
RouterLink,
|
||||
{
|
||||
to: {
|
||||
name: 'user-songList',
|
||||
},
|
||||
},
|
||||
{ default: () => '歌单' }
|
||||
),
|
||||
key: 'user-songList',
|
||||
icon: renderIcon(BookIcon),
|
||||
},
|
||||
{
|
||||
label: () =>
|
||||
h(
|
||||
RouterLink,
|
||||
{
|
||||
to: {
|
||||
name: 'user-questionBox',
|
||||
},
|
||||
},
|
||||
{ default: () => '棉花糖 (提问箱' }
|
||||
),
|
||||
key: 'user-questionBox',
|
||||
icon: renderIcon(BookIcon),
|
||||
},
|
||||
]
|
||||
const menuOptions = ref<MenuOption[]>()
|
||||
async function RequestBiliUserData() {
|
||||
await fetch(FETCH_API + `https://account.bilibili.com/api/member/getCardByMid?mid=${userInfo.value?.biliId}`)
|
||||
.then(async (respone) => {
|
||||
@@ -86,6 +63,52 @@ async function RequestBiliUserData() {
|
||||
|
||||
onMounted(async () => {
|
||||
userInfo.value = await useUser()
|
||||
menuOptions.value = [
|
||||
{
|
||||
label: () =>
|
||||
h(
|
||||
RouterLink,
|
||||
{
|
||||
to: {
|
||||
name: 'user-index',
|
||||
},
|
||||
},
|
||||
{ default: () => '主页' }
|
||||
),
|
||||
key: 'user-index',
|
||||
icon: renderIcon(Home),
|
||||
},
|
||||
{
|
||||
label: () =>
|
||||
h(
|
||||
RouterLink,
|
||||
{
|
||||
to: {
|
||||
name: 'user-songList',
|
||||
},
|
||||
},
|
||||
{ default: () => '歌单' }
|
||||
),
|
||||
show: (userInfo.value?.enableFunctions.indexOf(FunctionTypes.SongList) ?? -1) > -1,
|
||||
key: 'user-songList',
|
||||
icon: renderIcon(MusicalNote),
|
||||
},
|
||||
{
|
||||
label: () =>
|
||||
h(
|
||||
RouterLink,
|
||||
{
|
||||
to: {
|
||||
name: 'user-questionBox',
|
||||
},
|
||||
},
|
||||
{ default: () => '棉花糖 (提问箱' }
|
||||
),
|
||||
show: (userInfo.value?.enableFunctions.indexOf(FunctionTypes.QuestionBox) ?? -1) > -1,
|
||||
key: 'user-questionBox',
|
||||
icon: renderIcon(Chatbox),
|
||||
},
|
||||
]
|
||||
await RequestBiliUserData()
|
||||
})
|
||||
</script>
|
||||
@@ -97,8 +120,15 @@ onMounted(async () => {
|
||||
<NLayoutHeader style="height: 50px; padding: 5px 15px 5px 15px">
|
||||
<NPageHeader :subtitle="($route.meta.title as string) ?? ''">
|
||||
<template #extra>
|
||||
<NSpace>
|
||||
<NSwitch> </NSwitch>
|
||||
<NSpace align="center">
|
||||
<NSwitch :default-value="!isDarkMode()" @update:value="(value: string & number & boolean) => themeType = value ? ThemeType.Light : ThemeType.Dark">
|
||||
<template #checked>
|
||||
<NIcon :component="Sunny" />
|
||||
</template>
|
||||
<template #unchecked>
|
||||
<NIcon :component="Moon"/>
|
||||
</template>
|
||||
</NSwitch>
|
||||
<template v-if="accountInfo">
|
||||
<NButton style="right: 0px; position: relative" type="primary" @click="$router.push({ name: 'manage-index' })"> 个人中心 </NButton>
|
||||
</template>
|
||||
@@ -108,36 +138,38 @@ onMounted(async () => {
|
||||
</NSpace>
|
||||
</template>
|
||||
<template #title>
|
||||
<NText style="font-size: 1.5rem"> VTSURU </NText>
|
||||
<NText strong style="font-size: 1.5rem; text-shadow: 0 2px 4px rgba(0, 0, 0, 0.1)"> VTSURU </NText>
|
||||
</template>
|
||||
</NPageHeader>
|
||||
</NLayoutHeader>
|
||||
<NLayout has-sider style="height: calc(100vh - 50px)">
|
||||
<NLayoutSider show-trigger collapse-mode="width" :collapsed-width="64" :width="180" :native-scrollbar="false">
|
||||
<NLayoutSider ref="sider" show-trigger default-collapsed collapse-mode="width" :collapsed-width="64" :width="180" :native-scrollbar="false">
|
||||
<Transition>
|
||||
<div v-if="biliUserInfo" style="margin-top: 15px;">
|
||||
<div v-if="biliUserInfo" style="margin-top: 8px">
|
||||
<NSpace vertical justify="center" align="center">
|
||||
<NAvatar :src="biliUserInfo.face" :img-props="{ referrerpolicy: 'no-referrer' }" />
|
||||
<NText strong>
|
||||
{{ biliUserInfo.name }}
|
||||
</NText>
|
||||
<NAvatar :src="biliUserInfo.face" :img-props="{ referrerpolicy: 'no-referrer' }" round bordered style="box-shadow: 0 2px 3px rgba(0, 0, 0, 0.1)" />
|
||||
<NEllipsis v-if="width > 100" style="max-width: 100%">
|
||||
<NText strong>
|
||||
{{ biliUserInfo.name }}
|
||||
</NText>
|
||||
</NEllipsis>
|
||||
</NSpace>
|
||||
</div>
|
||||
</Transition>
|
||||
<NMenu :default-value="$route.name?.toString()" :collapsed-width="64" :collapsed-icon-size="22" :options="menuOptions" />
|
||||
</NLayoutSider>
|
||||
<NLayout style="height: 100%">
|
||||
<div class="viewer-page-content">
|
||||
<div class="viewer-page-content" :style="`box-shadow:${isDarkMode() ? 'rgb(28 28 28 / 9%) 5px 5px 6px inset, rgba(139, 139, 139, 0.09) -5px -5px 6px inset' : 'inset 5px 5px 6px #8b8b8b17, inset -5px -5px 6px #8b8b8b17;'}`">
|
||||
<RouterView v-slot="{ Component }">
|
||||
<KeepAlive>
|
||||
<component :is="Component" />
|
||||
<component :is="Component" :bili-info="biliUserInfo" />
|
||||
</KeepAlive>
|
||||
</RouterView>
|
||||
</div>
|
||||
</NLayout>
|
||||
</NLayout>
|
||||
</NLayout>
|
||||
<NModal v-model:show="registerAndLoginModalVisiable" style="width: 500px; max-width: 90vw;">
|
||||
<NModal v-model:show="registerAndLoginModalVisiable" style="width: 500px; max-width: 90vw">
|
||||
<RegisterAndLogin />
|
||||
</NModal>
|
||||
</template>
|
||||
@@ -146,7 +178,6 @@ onMounted(async () => {
|
||||
.viewer-page-content{
|
||||
height: 100%;
|
||||
border-radius: 18px;
|
||||
box-shadow: inset 5px 5px 6px #8b8b8b17, inset -5px -5px 6px #8b8b8b17;
|
||||
padding: 15px;
|
||||
margin-right: 10px;
|
||||
box-sizing: border-box;
|
||||
|
||||
@@ -90,7 +90,7 @@ onMounted(async () => {
|
||||
请在点击
|
||||
<NText type="primary" strong> 开始认证 </NText>
|
||||
后五分钟之内使用
|
||||
<NText> 需要认证的账户 </NText>
|
||||
<NText strong type="primary"> 需要认证的账户 </NText>
|
||||
在自己的直播间内发送
|
||||
<NButton type="info" text @click="copyCode">
|
||||
{{ accountInfo?.biliVerifyCode }}
|
||||
|
||||
@@ -1,32 +1,48 @@
|
||||
<script setup lang="ts">
|
||||
import { useAccount } from '@/api/account'
|
||||
import { NAlert, NButton, NCard, NDivider, NSpace, NTag, NText, NThing, NTime } from 'naive-ui'
|
||||
import SettingsManageView from './SettingsManageView.vue';
|
||||
|
||||
const accountInfo = useAccount()
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<NCard embedded style="max-width: 500px">
|
||||
<NSpace align="center" justify="center" vertical>
|
||||
<NText style="font-size: 3rem">
|
||||
{{ accountInfo?.name }}
|
||||
</NText>
|
||||
<NText style="color: gray">
|
||||
于
|
||||
<NTime :time="accountInfo?.createAt" />
|
||||
注册
|
||||
</NText>
|
||||
</NSpace>
|
||||
<NSpace justify="center" align="center" vertical>
|
||||
<NCard embedded style="max-width: 90%;width: 800px;">
|
||||
<NSpace align="center" justify="center" vertical>
|
||||
<NText style="font-size: 3rem">
|
||||
{{ accountInfo?.name }}
|
||||
</NText>
|
||||
<NText style="color: gray">
|
||||
于
|
||||
<NTime :time="accountInfo?.createAt" />
|
||||
注册
|
||||
</NText>
|
||||
</NSpace>
|
||||
|
||||
<NDivider />
|
||||
<NAlert>
|
||||
Bilibili 账户:
|
||||
<NTag v-if="accountInfo?.isBiliVerified" type="success"> 已认证 </NTag>
|
||||
<template v-else>
|
||||
<NTag type="error" size="small"> 未认证 </NTag>
|
||||
<NDivider vertical />
|
||||
<NButton size="small" @click="$router.push({ name: 'manage-biliVerify' })" type="info"> 前往认证 </NButton>
|
||||
</template>
|
||||
</NAlert>
|
||||
</NCard>
|
||||
<NDivider />
|
||||
<NAlert>
|
||||
邮箱:
|
||||
<NTag v-if="accountInfo?.isEmailVerified" type="success"> 已认证 | {{ accountInfo?.bindEmail }} </NTag>
|
||||
<template v-else>
|
||||
<NTag type="error" size="small"> 未认证 </NTag>
|
||||
</template>
|
||||
</NAlert>
|
||||
<NAlert>
|
||||
Bilibili 账户:
|
||||
<NTag v-if="accountInfo?.isBiliVerified" type="success"> 已认证 </NTag>
|
||||
<template v-else>
|
||||
<NTag type="error" size="small"> 未认证 </NTag>
|
||||
<NDivider vertical />
|
||||
<NButton size="small" @click="$router.push({ name: 'manage-biliVerify' })" type="info"> 前往认证 </NButton>
|
||||
</template>
|
||||
</NAlert>
|
||||
</NCard>
|
||||
<div style="max-width: 90%;width: 800px;">
|
||||
<NDivider/>
|
||||
<SettingsManageView />
|
||||
</div>
|
||||
</NSpace>
|
||||
</template>
|
||||
|
||||
2
src/views/manage/HistoryView.vue
Normal file
2
src/views/manage/HistoryView.vue
Normal file
@@ -0,0 +1,2 @@
|
||||
<script setup lang="ts">
|
||||
</script>
|
||||
@@ -104,9 +104,9 @@ async function read(question: QAInfo, read: boolean) {
|
||||
})
|
||||
}
|
||||
async function favorite(question: QAInfo, fav: boolean) {
|
||||
await QueryGetAPI(QUESTION_API_URL + 'read', {
|
||||
await QueryGetAPI(QUESTION_API_URL + 'favorite', {
|
||||
id: question.id,
|
||||
read: fav ? 'true' : 'false',
|
||||
favorite: fav ? 'true' : 'false',
|
||||
})
|
||||
.then((data) => {
|
||||
if (data.code == 200) {
|
||||
@@ -164,6 +164,11 @@ function onOpenModal(question: QAInfo) {
|
||||
replyMessage.value = question.answer?.message
|
||||
replyModalVisiable.value = true
|
||||
}
|
||||
function refresh() {
|
||||
isSendGetted = false
|
||||
isRevieveGetted = false
|
||||
onTabChange(selectedTabItem.value)
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
GetRecieveQAInfo()
|
||||
@@ -171,12 +176,12 @@ onMounted(() => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<NButton type="primary"> 刷新 </NButton>
|
||||
<NButton type="primary" @click="refresh"> 刷新 </NButton>
|
||||
<NDivider style="margin: 10px 0 10px 0" />
|
||||
<NTabs animated @update:value="onTabChange" v-model:value="selectedTabItem">
|
||||
<NTabPane tab="我收到的" name="0">
|
||||
只显示收藏 <NSwitch v-model:value="onlyFavorite" />
|
||||
<NList>
|
||||
<NList :bordered="false">
|
||||
<NListItem v-for="item in recieveQuestionsFiltered" :key="item.id">
|
||||
<NCard :embedded="!item.isReaded" hoverable size="small">
|
||||
<template #header>
|
||||
@@ -209,7 +214,7 @@ onMounted(() => {
|
||||
收藏
|
||||
</NButton>
|
||||
<NButton size="small"> 举报 </NButton>
|
||||
<NButton size="small"> 拉黑 </NButton>
|
||||
<NButton size="small" @click="blacklist(item)"> 拉黑 </NButton>
|
||||
</NSpace>
|
||||
</template>
|
||||
<template #header-extra>
|
||||
@@ -279,3 +284,9 @@ onMounted(() => {
|
||||
<NButton :loading="isRepling" @click="reply" type="primary"> 发送 </NButton>
|
||||
</NModal>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
.n-list{
|
||||
background-color: transparent;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import { useAccount } from '@/api/account'
|
||||
import { NButton, NCard, NCheckbox, NCheckboxGroup, NDivider, NForm, NSwitch, useMessage } from 'naive-ui'
|
||||
import { NButton, NCard, NCheckbox, NCheckboxGroup, NDivider, NForm, NSpace, NSwitch, useMessage } from 'naive-ui'
|
||||
import { ref } from 'vue'
|
||||
import { useRequest } from 'vue-request'
|
||||
import { FunctionTypes } from '@/api/api-models'
|
||||
@@ -20,24 +20,60 @@ function UpdateEnableFunction(func: FunctionTypes, enable: boolean) {
|
||||
}
|
||||
}
|
||||
}
|
||||
async function SaveSetting() {
|
||||
const data = await QueryPostAPI(ACCOUNT_API_URL + 'update-setting', {
|
||||
setting: JSON.stringify(account.value?.settings),
|
||||
})
|
||||
if (data.code == 200) {
|
||||
message.success('保存成功')
|
||||
async function SaveComboGroupSetting(value: (string | number)[], meta: { actionType: 'check' | 'uncheck'; value: string | number }) {
|
||||
if (account.value) {
|
||||
//UpdateEnableFunction(meta.value as FunctionTypes, meta.actionType == 'check')
|
||||
await QueryPostAPI(ACCOUNT_API_URL + 'update-setting', account.value?.settings)
|
||||
.then((data) => {
|
||||
if (data.code == 200) {
|
||||
//message.success('保存成功')
|
||||
} else {
|
||||
message.error('修改失败')
|
||||
if (account.value) {
|
||||
account.value.settings.enableFunctions = account.value.settings.enableFunctions.filter((f) => f != (meta.value as FunctionTypes))
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(err)
|
||||
message.error('修改失败')
|
||||
})
|
||||
}
|
||||
}
|
||||
async function SaveComboSetting(value :boolean) {
|
||||
if (account.value) {
|
||||
//UpdateEnableFunction(meta.value as FunctionTypes, meta.actionType == 'check')
|
||||
await QueryPostAPI(ACCOUNT_API_URL + 'update-setting', account.value?.settings)
|
||||
.then((data) => {
|
||||
if (data.code == 200) {
|
||||
//message.success('保存成功')
|
||||
} else {
|
||||
message.error('修改失败')
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(err)
|
||||
message.error('修改失败')
|
||||
})
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<NCard v-if="account">
|
||||
<NDivider> 启用功能 </NDivider>
|
||||
<NCheckboxGroup v-model:value="account.settings.enableFunctions">
|
||||
<NCard v-if="account" title="设置">
|
||||
<NDivider style="margin: 0"> 启用功能 </NDivider>
|
||||
<NCheckboxGroup v-model:value="account.settings.enableFunctions" @update:value="SaveComboGroupSetting">
|
||||
<NCheckbox :value="FunctionTypes.SongList"> 歌单 </NCheckbox>
|
||||
<NCheckbox :value="FunctionTypes.QuestionBox"> 提问箱(棉花糖 </NCheckbox>
|
||||
</NCheckboxGroup>
|
||||
<NDivider />
|
||||
<NButton @click="SaveSetting"> 保存 </NButton>
|
||||
<NDivider > 通知 </NDivider>
|
||||
<NSpace>
|
||||
<NCheckbox v-model:checked="account.settings.sendEmail.recieveQA" @update:checked="SaveComboSetting"> 收到新提问时发送邮件 </NCheckbox>
|
||||
<NCheckbox v-model:checked="account.settings.sendEmail.recieveQAReply" @update:checked="SaveComboSetting"> 提问收到回复时发送邮件 </NCheckbox>
|
||||
</NSpace>
|
||||
<NDivider> 提问箱 </NDivider>
|
||||
<NSpace>
|
||||
<NCheckbox v-model:checked="account.settings.questionBox.allowUnregistedUser" @update:checked="SaveComboSetting"> 允许未注册用户提问 </NCheckbox>
|
||||
</NSpace>
|
||||
</NCard>
|
||||
</template>
|
||||
|
||||
@@ -171,10 +171,22 @@ async function addNeteaseSongs() {
|
||||
}
|
||||
async function addFingsingSongs(song: SongsInfo) {
|
||||
isModalLoading.value = true
|
||||
if (!song.url) {
|
||||
try {
|
||||
const url = await getFivesingSongUrl(song)
|
||||
song.url = url
|
||||
} catch (err) {
|
||||
isModalLoading.value = false
|
||||
message.error('添加失败')
|
||||
console.error(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
await addSongs([song], SongFrom.FiveSing)
|
||||
.then((data) => {
|
||||
if (data.code == 200) {
|
||||
message.success(`已添加歌曲`)
|
||||
addSongModel.value = {} as SongsInfo
|
||||
songs.value.push(...data.data)
|
||||
} else {
|
||||
message.error('添加失败: ' + data.message)
|
||||
@@ -311,8 +323,21 @@ onMounted(async () => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<NButton @click="showModal = true"> 添加歌曲 </NButton>
|
||||
<NModal v-model:show="showModal" style="max-width: 1000px;" preset="card">
|
||||
<NSpace>
|
||||
<NButton @click="showModal = true" type="primary"> 添加歌曲 </NButton>
|
||||
<NButton
|
||||
@click="
|
||||
() => {
|
||||
getSongs()
|
||||
message.success('完成')
|
||||
}
|
||||
"
|
||||
>
|
||||
刷新
|
||||
</NButton>
|
||||
</NSpace>
|
||||
<NDivider style="margin: 16px 0 16px 0" />
|
||||
<NModal v-model:show="showModal" style="max-width: 1000px" preset="card">
|
||||
<template #header> 添加歌曲 </template>
|
||||
<NSpin :show="isModalLoading">
|
||||
<NTabs default-value="custom" animated>
|
||||
@@ -377,10 +402,10 @@ onMounted(async () => {
|
||||
</NTag>
|
||||
</NSpace>
|
||||
</td>
|
||||
<td style="display: flex; justify-content: flex-end;">
|
||||
<td style="display: flex; justify-content: flex-end">
|
||||
<!-- 在这里播放song.url链接中的音频 -->
|
||||
<NButton size="small" v-if="!song.url" @click="playFivesingSong(song)" :loading="isGettingFivesingSongPlayUrl == song.id"> 试听 </NButton>
|
||||
<audio v-else controls autoplay style="max-height: 30px">
|
||||
<NButton size="small" v-if="!song.url" @click="playFivesingSong(song)" :loading="isGettingFivesingSongPlayUrl == song.id"> 试听 </NButton>
|
||||
<audio v-else controls style="max-height: 30px">
|
||||
<source :src="song.url" />
|
||||
</audio>
|
||||
</td>
|
||||
@@ -398,5 +423,6 @@ onMounted(async () => {
|
||||
</NTabs>
|
||||
</NSpin>
|
||||
</NModal>
|
||||
<SongList :songs="songs" />
|
||||
<SongList :songs="songs" is-self />
|
||||
<NDivider />
|
||||
</template>
|
||||
|
||||
@@ -50,6 +50,7 @@ async function SendQuestion() {
|
||||
if (data.code == 200) {
|
||||
message.success('成功发送棉花糖')
|
||||
questionMessage.value = ''
|
||||
fileList.value = []
|
||||
} else {
|
||||
message.error(data.message)
|
||||
}
|
||||
|
||||
@@ -1,40 +1,70 @@
|
||||
<script setup lang="ts">
|
||||
import { SongsInfo } from '@/api/api-models'
|
||||
import { QueryGetAPI, QueryGetPaginationAPI } from '@/api/query'
|
||||
import SongList from '@/components/SongList.vue'
|
||||
import { SONG_API_URL, USER_API_URL } from '@/data/constants'
|
||||
import { onMounted, ref } from 'vue'
|
||||
import { useRouteParams } from '@vueuse/router'
|
||||
import { useAccount } from '@/api/account'
|
||||
import { NAlert } from 'naive-ui'
|
||||
<template>
|
||||
<NSpin v-if="isLoading" show />
|
||||
<component v-else :is="songListType" :user-info="userInfo" :songs="songs" />
|
||||
</template>
|
||||
|
||||
const accountInfo = useAccount()
|
||||
<script lang="ts" setup>
|
||||
import { useUser } from '@/api/user'
|
||||
import { SongListTypes, SongsInfo } from '@/api/api-models'
|
||||
import DefaultSongListTemplate from '@/views/view/songListTemplate/DefaultSongListTemplate.vue'
|
||||
import { computed, onMounted, ref } from 'vue'
|
||||
import { UserInfo } from '@/api/api-models'
|
||||
import { useRouteParams } from '@vueuse/router'
|
||||
import { QueryGetAPI } from '@/api/query'
|
||||
import { SONG_API_URL } from '@/data/constants'
|
||||
import { NSpin, useMessage } from 'naive-ui'
|
||||
|
||||
defineProps<{
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
biliInfo: any | undefined
|
||||
}>()
|
||||
|
||||
const songListType = computed(() => {
|
||||
if (userInfo.value) {
|
||||
switch (userInfo.value.songListType) {
|
||||
case SongListTypes.Default:
|
||||
return DefaultSongListTemplate
|
||||
|
||||
default:
|
||||
return DefaultSongListTemplate
|
||||
}
|
||||
} else {
|
||||
return DefaultSongListTemplate
|
||||
}
|
||||
})
|
||||
const songs = ref<SongsInfo[]>()
|
||||
const uId = useRouteParams('id', '-1', { transform: Number })
|
||||
const isLoading = ref(true)
|
||||
const message = useMessage()
|
||||
|
||||
const errMessage = ref('')
|
||||
|
||||
async function getSongs() {
|
||||
isLoading.value = true
|
||||
await QueryGetAPI<SongsInfo[]>(SONG_API_URL + 'get', {
|
||||
id: uId.value,
|
||||
}).then((data) => {
|
||||
if (data.code == 200) {
|
||||
songs.value = data.data
|
||||
}
|
||||
else {
|
||||
errMessage.value = data.message
|
||||
}
|
||||
})
|
||||
.then((data) => {
|
||||
if (data.code == 200) {
|
||||
songs.value = data.data
|
||||
} else {
|
||||
errMessage.value = data.message
|
||||
message.error('加载失败: ' + data.message)
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(err)
|
||||
message.error('加载失败')
|
||||
})
|
||||
.finally(() => {
|
||||
isLoading.value = false
|
||||
})
|
||||
}
|
||||
|
||||
const userInfo = ref<UserInfo>()
|
||||
|
||||
onMounted(async () => {
|
||||
userInfo.value = await useUser()
|
||||
await getSongs()
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<SongList v-if="songs" :songs="songs ?? []" />
|
||||
<NAlert v-else-if="errMessage" type="error">
|
||||
{{ errMessage }}
|
||||
</NAlert>
|
||||
</template>
|
||||
|
||||
@@ -1,23 +1,22 @@
|
||||
<template>
|
||||
<div style="display: flex; justify-content: center">
|
||||
<div>
|
||||
<NText strong tag="h1"> vtsuru </NText>
|
||||
<component :is="indexType" :user-info="userInfo"/>
|
||||
</div>
|
||||
</div>
|
||||
<component :is="indexType" :user-info="userInfo" :bili-info="biliInfo"/>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { useAccount } from '@/api/account'
|
||||
import { useUser } from '@/api/user'
|
||||
import { IndexTypes } from '@/api/api-models'
|
||||
import { NButton, NText } from 'naive-ui'
|
||||
import DefaultIndexTemplate from '@/views/view/indexTemplate/DefaultIndexTemplate.vue'
|
||||
import { computed } from 'vue'
|
||||
import { computed, onMounted, ref } from 'vue'
|
||||
import { UserInfo } from '@/api/api-models'
|
||||
|
||||
defineProps<{
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
biliInfo: any | undefined
|
||||
}>()
|
||||
|
||||
const indexType = computed(() => {
|
||||
if (userInfo) {
|
||||
switch (userInfo.indexType) {
|
||||
if (userInfo.value) {
|
||||
switch (userInfo.value.indexType) {
|
||||
case IndexTypes.Default:
|
||||
return DefaultIndexTemplate
|
||||
|
||||
@@ -28,6 +27,9 @@ const indexType = computed(() => {
|
||||
return DefaultIndexTemplate
|
||||
}
|
||||
})
|
||||
const accountInfo = useAccount()
|
||||
const userInfo = await useUser()
|
||||
const userInfo = ref<UserInfo>()
|
||||
|
||||
onMounted(async () => {
|
||||
userInfo.value = await useUser()
|
||||
})
|
||||
</script>
|
||||
@@ -1,11 +1,44 @@
|
||||
<script lang="ts" setup>
|
||||
import { UserInfo } from '@/api/api-models';
|
||||
import { UserInfo } from '@/api/api-models'
|
||||
import { NAvatar, NButton, NDivider, NSpace, NText } from 'naive-ui'
|
||||
|
||||
const width = window.innerWidth
|
||||
|
||||
const props = defineProps<{
|
||||
userInfo: UserInfo
|
||||
userInfo: UserInfo | undefined
|
||||
biliInfo: any | undefined
|
||||
}>()
|
||||
function navigate(url: string) {
|
||||
window.open(url, '_blank')
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
1
|
||||
</template>
|
||||
<NDivider />
|
||||
<template v-if="userInfo?.biliId">
|
||||
<NSpace justify="center" align="center" vertical>
|
||||
<NAvatar :src="biliInfo?.face" :size="width > 750 ? 175 : 100" round bordered style="box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2);" />
|
||||
<NSpace align="baseline" justify="center">
|
||||
<NText strong style="font-size: 32px"> {{ biliInfo?.name }} </NText>
|
||||
<NText strong style="font-size: 20px" depth="3"> ({{ userInfo?.name }}) </NText>
|
||||
</NSpace>
|
||||
<NText strong depth="3" style="font-size: medium">
|
||||
{{ userInfo?.biliId }}
|
||||
</NText>
|
||||
<NText strong depth="2" style="font-size: medium">
|
||||
{{ biliInfo?.sign }}
|
||||
</NText>
|
||||
</NSpace>
|
||||
<NDivider/>
|
||||
<NSpace align="center" justify="center">
|
||||
<NButton type="primary" @click="navigate('https://space.bilibili.com/' + userInfo?.biliId)"> 个人主页 </NButton>
|
||||
<NButton type="primary" secondary @click="navigate('https://live.bilibili.com/' + userInfo?.biliRoomId)"> 直播间 </NButton>
|
||||
</NSpace>
|
||||
</template>
|
||||
<template v-else>
|
||||
<NSpace justify="center" align="center">
|
||||
<NText strong style="font-size: 32px"> {{ userInfo?.name }} </NText>
|
||||
未认证
|
||||
</NSpace>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
18
src/views/view/songListTemplate/DefaultSongListTemplate.vue
Normal file
18
src/views/view/songListTemplate/DefaultSongListTemplate.vue
Normal file
@@ -0,0 +1,18 @@
|
||||
<script setup lang="ts">
|
||||
import { useAccount } from '@/api/account';
|
||||
import { SongsInfo, UserInfo } from '@/api/api-models'
|
||||
import SongList from '@/components/SongList.vue'
|
||||
import { NDivider } from 'naive-ui';
|
||||
|
||||
const accountInfo = useAccount()
|
||||
|
||||
const props = defineProps<{
|
||||
userInfo: UserInfo | undefined
|
||||
songs: SongsInfo[] | undefined
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<SongList v-if="songs" :songs="songs ?? []" :is-self="accountInfo?.id.toString() == $route.params.id?.toString()"/>
|
||||
<NDivider/>
|
||||
</template>
|
||||
Reference in New Issue
Block a user