mirror of
https://github.com/Megghy/vtsuru.live.git
synced 2025-12-06 18:36:55 +08:00
update
This commit is contained in:
@@ -9,6 +9,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/node": "^20.2.5",
|
"@types/node": "^20.2.5",
|
||||||
|
"@vicons/fluent": "^0.12.0",
|
||||||
"@vitejs/plugin-vue": "^4.2.3",
|
"@vitejs/plugin-vue": "^4.2.3",
|
||||||
"@vueuse/core": "^10.1.2",
|
"@vueuse/core": "^10.1.2",
|
||||||
"@vueuse/integrations": "^10.1.2",
|
"@vueuse/integrations": "^10.1.2",
|
||||||
@@ -18,6 +19,7 @@
|
|||||||
"linqts": "^1.15.0",
|
"linqts": "^1.15.0",
|
||||||
"universal-cookie": "^4.0.4",
|
"universal-cookie": "^4.0.4",
|
||||||
"vite": "^4.3.9",
|
"vite": "^4.3.9",
|
||||||
|
"vite-svg-loader": "^4.0.0",
|
||||||
"vue": "^3.2.13",
|
"vue": "^3.2.13",
|
||||||
"vue-meta": "^3.0.0-alpha.10",
|
"vue-meta": "^3.0.0-alpha.10",
|
||||||
"vue-request": "^2.0.3",
|
"vue-request": "^2.0.3",
|
||||||
|
|||||||
16
src/App.vue
16
src/App.vue
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<NMessageProvider>
|
<NMessageProvider>
|
||||||
<NNotificationProvider>
|
<NNotificationProvider>
|
||||||
<NConfigProvider :theme-overrides="themeOverrides" style="height: 100vh" :locale="zhCN" :date-locale="dateZhCN">
|
<NConfigProvider :theme-overrides="themeOverrides" :theme="theme" style="height: 100vh" :locale="zhCN" :date-locale="dateZhCN">
|
||||||
<ViewerLayout v-if="layout == 'viewer'" />
|
<ViewerLayout v-if="layout == 'viewer'" />
|
||||||
<ManageLayout v-else-if="layout == 'manage'" />
|
<ManageLayout v-else-if="layout == 'manage'" />
|
||||||
<template v-else>
|
<template v-else>
|
||||||
@@ -16,8 +16,10 @@
|
|||||||
import ViewerLayout from '@/views/ViewerLayout.vue'
|
import ViewerLayout from '@/views/ViewerLayout.vue'
|
||||||
import ManageLayout from '@/views/ManageLayout.vue'
|
import ManageLayout from '@/views/ManageLayout.vue'
|
||||||
import { useRoute } from 'vue-router'
|
import { useRoute } from 'vue-router'
|
||||||
import { NConfigProvider, NMessageProvider, NNotificationProvider, zhCN, dateZhCN } from 'naive-ui'
|
import { NConfigProvider, NMessageProvider, NNotificationProvider, zhCN, dateZhCN, useOsTheme, darkTheme } from 'naive-ui'
|
||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
|
import { useStorage } from '@vueuse/core'
|
||||||
|
import { ThemeType } from './api/api-models'
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const layout = computed(() => {
|
const layout = computed(() => {
|
||||||
@@ -30,6 +32,16 @@ const layout = computed(() => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const themeType = useStorage('Settings.Theme', ThemeType.Auto);
|
||||||
|
const theme = computed(() => {
|
||||||
|
if (themeType.value == ThemeType.Auto) {
|
||||||
|
var osThemeRef = useOsTheme(); //获取当前系统主题
|
||||||
|
return osThemeRef.value === "dark" ? darkTheme : null;
|
||||||
|
} else {
|
||||||
|
return themeType.value == ThemeType.Dark ? darkTheme : null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
const themeOverrides = {
|
const themeOverrides = {
|
||||||
common: {
|
common: {
|
||||||
//primaryColor: '#9ddddc',
|
//primaryColor: '#9ddddc',
|
||||||
|
|||||||
13
src/Utils.ts
13
src/Utils.ts
@@ -1,3 +1,14 @@
|
|||||||
|
import { useOsTheme } from "naive-ui"
|
||||||
|
import { ThemeType } from "./api/api-models"
|
||||||
|
import { useStorage } from "@vueuse/core"
|
||||||
|
|
||||||
|
const osThemeRef = useOsTheme() //获取当前系统主题
|
||||||
export function NavigateToNewTab(url: string) {
|
export function NavigateToNewTab(url: string) {
|
||||||
window.open(url, '_blank')
|
window.open(url, '_blank')
|
||||||
}
|
}
|
||||||
|
const themeType = useStorage('Settings.Theme', ThemeType.Auto)
|
||||||
|
export function isDarkMode(): boolean {
|
||||||
|
|
||||||
|
if (themeType.value == ThemeType.Auto) return osThemeRef.value === 'dark'
|
||||||
|
else return themeType.value == ThemeType.Dark
|
||||||
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { useLocalStorage } from '@vueuse/core'
|
|||||||
import { createDiscreteApi } from 'naive-ui'
|
import { createDiscreteApi } from 'naive-ui'
|
||||||
|
|
||||||
export const ACCOUNT = ref<AccountInfo>()
|
export const ACCOUNT = ref<AccountInfo>()
|
||||||
|
export const isLoadingAccount = ref(true)
|
||||||
|
|
||||||
const { message } = createDiscreteApi(['message'])
|
const { message } = createDiscreteApi(['message'])
|
||||||
const cookie = useLocalStorage('JWT_Token', '')
|
const cookie = useLocalStorage('JWT_Token', '')
|
||||||
@@ -15,6 +16,7 @@ export async function GetSelfAccount() {
|
|||||||
const result = await Self()
|
const result = await Self()
|
||||||
if (result.code == 200) {
|
if (result.code == 200) {
|
||||||
ACCOUNT.value = result.data
|
ACCOUNT.value = result.data
|
||||||
|
isLoadingAccount.value = false
|
||||||
console.log('[vtsuru] 已获取账户信息')
|
console.log('[vtsuru] 已获取账户信息')
|
||||||
return result.data
|
return result.data
|
||||||
} else if (result.code == 401) {
|
} else if (result.code == 401) {
|
||||||
@@ -27,6 +29,7 @@ export async function GetSelfAccount() {
|
|||||||
message.error(result.message)
|
message.error(result.message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
isLoadingAccount.value = false
|
||||||
}
|
}
|
||||||
export function useAccount() {
|
export function useAccount() {
|
||||||
return ACCOUNT
|
return ACCOUNT
|
||||||
|
|||||||
@@ -13,6 +13,9 @@ export interface PaginationResponse<T> {
|
|||||||
export enum IndexTypes {
|
export enum IndexTypes {
|
||||||
Default,
|
Default,
|
||||||
}
|
}
|
||||||
|
export enum SongListTypes {
|
||||||
|
Default,
|
||||||
|
}
|
||||||
export interface UserInfo {
|
export interface UserInfo {
|
||||||
name: string
|
name: string
|
||||||
id: number
|
id: number
|
||||||
@@ -20,26 +23,33 @@ export interface UserInfo {
|
|||||||
biliId?: number
|
biliId?: number
|
||||||
biliRoomId?: number
|
biliRoomId?: number
|
||||||
indexType: IndexTypes
|
indexType: IndexTypes
|
||||||
|
songListType: SongListTypes
|
||||||
|
enableFunctions: FunctionTypes[]
|
||||||
}
|
}
|
||||||
export interface AccountInfo extends UserInfo {
|
export interface AccountInfo extends UserInfo {
|
||||||
isEmailVerified: boolean
|
isEmailVerified: boolean
|
||||||
isBiliVerified: boolean
|
isBiliVerified: boolean
|
||||||
enableFunctions: string[]
|
|
||||||
biliVerifyCode?: string
|
biliVerifyCode?: string
|
||||||
emailVerifyUrl?: string
|
bindEmail?: string
|
||||||
settings: UserSetting
|
settings: UserSetting
|
||||||
|
|
||||||
|
nextSendEmailTime?: number
|
||||||
}
|
}
|
||||||
export interface Setting_SendEmail {
|
export interface Setting_SendEmail {
|
||||||
recieveQA: boolean
|
recieveQA: boolean
|
||||||
recieveQAReply: boolean
|
recieveQAReply: boolean
|
||||||
}
|
}
|
||||||
|
export interface Setting_QuestionBox {
|
||||||
|
allowUnregistedUser: boolean
|
||||||
|
}
|
||||||
export interface UserSetting {
|
export interface UserSetting {
|
||||||
sendEmail: Setting_SendEmail
|
sendEmail: Setting_SendEmail
|
||||||
|
questionBox: Setting_QuestionBox
|
||||||
enableFunctions: FunctionTypes[]
|
enableFunctions: FunctionTypes[]
|
||||||
}
|
}
|
||||||
export enum FunctionTypes {
|
export enum FunctionTypes {
|
||||||
SongList = 'SongList',
|
SongList,
|
||||||
QuestionBox = 'QuestionBox',
|
QuestionBox,
|
||||||
}
|
}
|
||||||
export interface SongAuthorInfo {
|
export interface SongAuthorInfo {
|
||||||
name: string
|
name: string
|
||||||
@@ -61,6 +71,8 @@ export interface SongsInfo {
|
|||||||
language: SongLanguage[]
|
language: SongLanguage[]
|
||||||
description?: string
|
description?: string
|
||||||
tags?: string[]
|
tags?: string[]
|
||||||
|
createTime: number
|
||||||
|
updateTime: number
|
||||||
}
|
}
|
||||||
export enum SongLanguage {
|
export enum SongLanguage {
|
||||||
Chinese, // 中文
|
Chinese, // 中文
|
||||||
@@ -92,7 +104,7 @@ export interface QAInfo {
|
|||||||
isReaded?: boolean
|
isReaded?: boolean
|
||||||
isSenderRegisted: boolean
|
isSenderRegisted: boolean
|
||||||
isPublic: boolean
|
isPublic: boolean
|
||||||
isFavorite:boolean
|
isFavorite: boolean
|
||||||
sendAt: number
|
sendAt: number
|
||||||
}
|
}
|
||||||
export interface LotteryUserInfo {
|
export interface LotteryUserInfo {
|
||||||
@@ -112,3 +124,8 @@ export interface LotteryUserCardInfo {
|
|||||||
isGuard: boolean
|
isGuard: boolean
|
||||||
isCharge: boolean
|
isCharge: boolean
|
||||||
}
|
}
|
||||||
|
export enum ThemeType {
|
||||||
|
Auto = 'auto',
|
||||||
|
Light = 'light',
|
||||||
|
Dark = 'dark',
|
||||||
|
}
|
||||||
@@ -1,18 +1,19 @@
|
|||||||
/* eslint-disable indent */
|
/* eslint-disable indent */
|
||||||
import { useLocalStorage } from '@vueuse/core'
|
import { useLocalStorage } from '@vueuse/core'
|
||||||
import { APIRoot, PaginationResponse } from './api-models'
|
import { APIRoot, PaginationResponse } from './api-models'
|
||||||
|
import { Cookies20Regular } from '@vicons/fluent'
|
||||||
|
|
||||||
const cookie = useLocalStorage('JWT_Token', '')
|
const cookie = useLocalStorage('JWT_Token', '')
|
||||||
|
|
||||||
export async function QueryPostAPI<T>(url: string, body?: unknown, headers?: [string, string][]): Promise<APIRoot<T>> {
|
export async function QueryPostAPI<T>(url: string, body?: unknown, headers?: [string, string][]): Promise<APIRoot<T>> {
|
||||||
headers ??= []
|
headers ??= []
|
||||||
headers?.push(['Authorization', `Bearer ${cookie.value}`])
|
if (cookie.value) headers?.push(['Authorization', `Bearer ${cookie.value}`])
|
||||||
headers?.push(['Content-Type', 'application/json'])
|
headers?.push(['Content-Type', 'application/json'])
|
||||||
|
|
||||||
const data = await fetch(url, {
|
const data = await fetch(url, {
|
||||||
method: 'post',
|
method: 'post',
|
||||||
headers: headers,
|
headers: headers,
|
||||||
body: JSON.stringify(body),
|
body: typeof body === 'string' ? body : JSON.stringify(body),
|
||||||
}) // 不处理异常, 在页面处理
|
}) // 不处理异常, 在页面处理
|
||||||
return (await data.json()) as APIRoot<T>
|
return (await data.json()) as APIRoot<T>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -205,8 +205,6 @@ function onLoginButtonClick(e: MouseEvent) {
|
|||||||
</NSpace>
|
</NSpace>
|
||||||
</NTabPane>
|
</NTabPane>
|
||||||
</NTabs>
|
</NTabs>
|
||||||
|
|
||||||
<NDivider />
|
|
||||||
<VueTurnstile ref="turnstile" :site-key="TURNSTILE_KEY" v-model="token" theme="auto" style="text-align: center" />
|
<VueTurnstile ref="turnstile" :site-key="TURNSTILE_KEY" v-model="token" theme="auto" style="text-align: center" />
|
||||||
</template>
|
</template>
|
||||||
</NCard>
|
</NCard>
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { SongAuthorInfo, SongFrom, SongLanguage, SongsInfo } from '@/api/api-models'
|
import { SongAuthorInfo, SongFrom, SongLanguage, SongsInfo, UserInfo } from '@/api/api-models'
|
||||||
import { QueryPostAPI } from '@/api/query'
|
import { QueryGetAPI, QueryPostAPI } from '@/api/query'
|
||||||
import { SONG_API_URL } from '@/data/constants'
|
import { SONG_API_URL } from '@/data/constants'
|
||||||
|
import { refDebounced, useDebounceFn } from '@vueuse/core'
|
||||||
import { List } from 'linqts'
|
import { List } from 'linqts'
|
||||||
import {
|
import {
|
||||||
|
DataTableBaseColumn,
|
||||||
DataTableColumns,
|
DataTableColumns,
|
||||||
FormInst,
|
FormInst,
|
||||||
FormRules,
|
FormRules,
|
||||||
@@ -16,48 +18,54 @@ import {
|
|||||||
NEllipsis,
|
NEllipsis,
|
||||||
NForm,
|
NForm,
|
||||||
NFormItem,
|
NFormItem,
|
||||||
|
NIcon,
|
||||||
NInput,
|
NInput,
|
||||||
NList,
|
NList,
|
||||||
NListItem,
|
NListItem,
|
||||||
NModal,
|
NModal,
|
||||||
|
NPopconfirm,
|
||||||
NSelect,
|
NSelect,
|
||||||
NSpace,
|
NSpace,
|
||||||
NTag,
|
NTag,
|
||||||
NText,
|
NText,
|
||||||
|
NTooltip,
|
||||||
useMessage,
|
useMessage,
|
||||||
} from 'naive-ui'
|
} from 'naive-ui'
|
||||||
import { FilterOptionValue } from 'naive-ui/es/data-table/src/interface'
|
import { onMounted, h, ref, watch, computed, reactive } from 'vue'
|
||||||
import { nextTick } from 'process'
|
|
||||||
import { onMounted, h, ref, watch, computed } from 'vue'
|
|
||||||
import APlayer from 'vue3-aplayer'
|
import APlayer from 'vue3-aplayer'
|
||||||
|
import { NotepadEdit20Filled, Delete24Filled, Play24Filled, SquareArrowForward24Filled } from '@vicons/fluent'
|
||||||
|
import NeteaseIcon from '@/svgs/netease.svg'
|
||||||
|
import FiveSingIcon from '@/svgs/fivesing.svg'
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
songs: SongsInfo[]
|
songs: SongsInfo[]
|
||||||
canEdit?: boolean
|
canEdit?: boolean
|
||||||
|
isSelf: boolean
|
||||||
}>()
|
}>()
|
||||||
watch(
|
watch(
|
||||||
() => props.songs,
|
() => props.songs,
|
||||||
(newV) => {
|
(newV) => {
|
||||||
let map = new Map()
|
|
||||||
newV.forEach((s) => {
|
|
||||||
s.tags?.forEach((t) => map.set(t, t))
|
|
||||||
})
|
|
||||||
map.forEach((tag) => {
|
|
||||||
tagsSelectOption.value.push({
|
|
||||||
label: tag,
|
|
||||||
value: tag,
|
|
||||||
})
|
|
||||||
})
|
|
||||||
songsInternal.value = newV
|
songsInternal.value = newV
|
||||||
|
setTimeout(() => {
|
||||||
|
columns.value = createColumns()
|
||||||
|
}, 1)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
const songsInternal = ref(props.songs)
|
const songsInternal = ref(props.songs)
|
||||||
|
const songsComputed = computed(() => {
|
||||||
|
if (debouncedInput.value) {
|
||||||
|
//忽略大小写比较
|
||||||
|
return songsInternal.value.filter((s) => s.name.toLowerCase().includes(debouncedInput.value.toLowerCase()))
|
||||||
|
}
|
||||||
|
return songsInternal.value
|
||||||
|
})
|
||||||
const message = useMessage()
|
const message = useMessage()
|
||||||
|
|
||||||
const showModal = ref(false)
|
const showModal = ref(false)
|
||||||
const updateSongModel = ref<SongsInfo>({} as SongsInfo)
|
const updateSongModel = ref<SongsInfo>({} as SongsInfo)
|
||||||
|
const searchMusicKeyword = ref()
|
||||||
|
const debouncedInput = refDebounced(searchMusicKeyword, 500)
|
||||||
|
|
||||||
const columns = ref<DataTableColumns<SongsInfo>>()
|
|
||||||
const aplayerMusic = ref<{
|
const aplayerMusic = ref<{
|
||||||
title: string
|
title: string
|
||||||
artist: string
|
artist: string
|
||||||
@@ -106,19 +114,46 @@ const songSelectOption = [
|
|||||||
value: SongLanguage.Other,
|
value: SongLanguage.Other,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
const tagsSelectOption = ref<{ label: string; value: string }[]>([])
|
const tagsSelectOption = computed(() => {
|
||||||
const tags = ref<string[]>([])
|
return new List(songsInternal.value)
|
||||||
const tagsOptions = computed(() => {
|
.SelectMany((s) => new List(s?.tags))
|
||||||
return tags.value.map((t) => ({
|
.Distinct()
|
||||||
label: t,
|
.ToArray()
|
||||||
value: t,
|
.map((t) => ({
|
||||||
}))
|
label: t,
|
||||||
|
value: t,
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
const authorsOptions = computed(() => {
|
||||||
|
return new List(songsInternal.value)
|
||||||
|
.SelectMany((s) => new List(s?.author))
|
||||||
|
.Distinct()
|
||||||
|
.ToArray()
|
||||||
|
.map((t) => ({
|
||||||
|
label: t,
|
||||||
|
value: t,
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
|
||||||
|
const columns = ref<DataTableColumns<SongsInfo>>()
|
||||||
|
const authorColumn = ref<DataTableBaseColumn<SongsInfo>>({
|
||||||
|
title: '作者',
|
||||||
|
key: 'artist',
|
||||||
|
width: 200,
|
||||||
|
resizable: true,
|
||||||
|
filter(value, row) {
|
||||||
|
return (row.author?.findIndex((t) => t == value.toString()) ?? -1) > -1
|
||||||
|
},
|
||||||
|
filterOptions: authorsOptions.value,
|
||||||
|
render(data) {
|
||||||
|
return h(NSpace, { size: 5 }, () => data.author.map((a) => h(NButton, { size: 'tiny', type: 'info', secondary: true, onClick: () => (authorColumn.value.filterOptionValue = a) }, () => a)))
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
function createColumns(): DataTableColumns<SongsInfo> {
|
function createColumns(): DataTableColumns<SongsInfo> {
|
||||||
|
authorColumn.value.filterOptions = authorsOptions.value
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
title: '名称',
|
|
||||||
key: 'name',
|
key: 'name',
|
||||||
resizable: true,
|
resizable: true,
|
||||||
minWidth: 100,
|
minWidth: 100,
|
||||||
@@ -127,21 +162,18 @@ function createColumns(): DataTableColumns<SongsInfo> {
|
|||||||
render(data) {
|
render(data) {
|
||||||
return h(NSpace, { size: 5 }, () => [h(NText, () => data.name), h(NText, { depth: '3' }, () => data.translateName)])
|
return h(NSpace, { size: 5 }, () => [h(NText, () => data.name), h(NText, { depth: '3' }, () => data.translateName)])
|
||||||
},
|
},
|
||||||
|
title: '曲名',
|
||||||
},
|
},
|
||||||
{
|
authorColumn.value,
|
||||||
title: '作者',
|
|
||||||
key: 'artist',
|
|
||||||
width: 200,
|
|
||||||
resizable: true,
|
|
||||||
render(data) {
|
|
||||||
return h(NSpace, { size: 5 }, () => data.author.map((a) => h(NTag, { bordered: false, size: 'small', type: 'info' }, () => a)))
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
title: '语言',
|
title: '语言',
|
||||||
key: 'language',
|
key: 'language',
|
||||||
width: 150,
|
width: 150,
|
||||||
resizable: true,
|
resizable: true,
|
||||||
|
filterOptions: songSelectOption,
|
||||||
|
filter(value, row) {
|
||||||
|
return (row.language?.findIndex((t) => t == (value.toString() as unknown as SongLanguage)) ?? -1) > -1
|
||||||
|
},
|
||||||
render(data) {
|
render(data) {
|
||||||
return (data.language?.length ?? 0) > 0
|
return (data.language?.length ?? 0) > 0
|
||||||
? h(NSpace, { size: 5 }, () => data.language?.map((a) => h(NTag, { bordered: false, size: 'small' }, () => songSelectOption.find((s) => s.value == a)?.label)))
|
? h(NSpace, { size: 5 }, () => data.language?.map((a) => h(NTag, { bordered: false, size: 'small' }, () => songSelectOption.find((s) => s.value == a)?.label)))
|
||||||
@@ -163,7 +195,7 @@ function createColumns(): DataTableColumns<SongsInfo> {
|
|||||||
filter(value, row) {
|
filter(value, row) {
|
||||||
return (row.tags?.findIndex((t) => t == value.toString()) ?? -1) > -1
|
return (row.tags?.findIndex((t) => t == value.toString()) ?? -1) > -1
|
||||||
},
|
},
|
||||||
filterOptions: tagsOptions.value,
|
filterOptions: tagsSelectOption.value,
|
||||||
render(data) {
|
render(data) {
|
||||||
return (data.tags?.length ?? 0) > 0 ? h(NSpace, { size: 5 }, () => data.tags?.map((a) => h(NTag, { bordered: false, size: 'small' }, () => a))) : null
|
return (data.tags?.length ?? 0) > 0 ? h(NSpace, { size: 5 }, () => data.tags?.map((a) => h(NTag, { bordered: false, size: 'small' }, () => a))) : null
|
||||||
},
|
},
|
||||||
@@ -172,64 +204,89 @@ function createColumns(): DataTableColumns<SongsInfo> {
|
|||||||
title: '操作',
|
title: '操作',
|
||||||
key: 'manage',
|
key: 'manage',
|
||||||
disabled: () => !props.canEdit,
|
disabled: () => !props.canEdit,
|
||||||
width: 200,
|
width: props.isSelf ? 170 : 100,
|
||||||
render(data) {
|
render(data) {
|
||||||
return h(
|
return h(
|
||||||
NSpace,
|
NSpace,
|
||||||
{
|
{
|
||||||
justify: 'space-around',
|
justify: 'end',
|
||||||
|
size: 10
|
||||||
},
|
},
|
||||||
() => [
|
() => [
|
||||||
h(
|
|
||||||
NButton,
|
|
||||||
{
|
|
||||||
size: 'small',
|
|
||||||
onClick: () => {
|
|
||||||
updateSongModel.value = JSON.parse(JSON.stringify(data))
|
|
||||||
showModal.value = true
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
default: () => '修改',
|
|
||||||
}
|
|
||||||
),
|
|
||||||
h(
|
|
||||||
NButton,
|
|
||||||
{
|
|
||||||
type: 'primary',
|
|
||||||
size: 'small',
|
|
||||||
onClick: () => {
|
|
||||||
aplayerMusic.value = {
|
|
||||||
title: data.name,
|
|
||||||
artist: data.author.join('/') ?? '',
|
|
||||||
src: data.url,
|
|
||||||
pic: '',
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
default: () => '播放',
|
|
||||||
}
|
|
||||||
),
|
|
||||||
h(
|
|
||||||
NButton,
|
|
||||||
{
|
|
||||||
type: 'error',
|
|
||||||
size: 'small',
|
|
||||||
onClick: () => {
|
|
||||||
aplayerMusic.value = {
|
|
||||||
title: data.name,
|
|
||||||
artist: data.author.join('/') ?? '',
|
|
||||||
src: data.url,
|
|
||||||
pic: '',
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
default: () => '删除',
|
|
||||||
}
|
|
||||||
),
|
|
||||||
GetPlayButton(data),
|
GetPlayButton(data),
|
||||||
|
data.url
|
||||||
|
? h(NTooltip, null, {
|
||||||
|
trigger: () =>
|
||||||
|
h(
|
||||||
|
NButton,
|
||||||
|
{
|
||||||
|
type: 'primary',
|
||||||
|
size: 'small',
|
||||||
|
circle: true,
|
||||||
|
onClick: () => {
|
||||||
|
aplayerMusic.value = {
|
||||||
|
title: data.name,
|
||||||
|
artist: data.author.join('/') ?? '',
|
||||||
|
src: data.url,
|
||||||
|
pic: '',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: () => h(NIcon, { component: Play24Filled }),
|
||||||
|
}
|
||||||
|
),
|
||||||
|
default: () => '试听',
|
||||||
|
})
|
||||||
|
: null,
|
||||||
|
props.isSelf
|
||||||
|
? [
|
||||||
|
h(NTooltip, null, {
|
||||||
|
trigger: () =>
|
||||||
|
h(
|
||||||
|
NButton,
|
||||||
|
{
|
||||||
|
size: 'small',
|
||||||
|
circle: true,
|
||||||
|
secondary: true,
|
||||||
|
onClick: () => {
|
||||||
|
updateSongModel.value = JSON.parse(JSON.stringify(data))
|
||||||
|
showModal.value = true
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: () => h(NIcon, { component: NotepadEdit20Filled }),
|
||||||
|
}
|
||||||
|
),
|
||||||
|
default: () => '修改',
|
||||||
|
}),
|
||||||
|
h(NTooltip, null, {
|
||||||
|
trigger: () =>
|
||||||
|
h(
|
||||||
|
NPopconfirm,
|
||||||
|
{
|
||||||
|
onPositiveClick: () => delSong(data),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
trigger: () =>
|
||||||
|
h(
|
||||||
|
NButton,
|
||||||
|
{
|
||||||
|
type: 'error',
|
||||||
|
size: 'small',
|
||||||
|
circle: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: () => h(NIcon, { component: Delete24Filled }),
|
||||||
|
}
|
||||||
|
),
|
||||||
|
default: () => '确认删除该歌曲?',
|
||||||
|
}
|
||||||
|
),
|
||||||
|
default: () => '删除',
|
||||||
|
}),
|
||||||
|
]
|
||||||
|
: null,
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
@@ -239,34 +296,67 @@ function createColumns(): DataTableColumns<SongsInfo> {
|
|||||||
function GetPlayButton(song: SongsInfo) {
|
function GetPlayButton(song: SongsInfo) {
|
||||||
switch (song.from) {
|
switch (song.from) {
|
||||||
case SongFrom.FiveSing: {
|
case SongFrom.FiveSing: {
|
||||||
return h(
|
return h(NTooltip, null, {
|
||||||
NButton,
|
trigger: () =>
|
||||||
{
|
h(
|
||||||
size: 'small',
|
h(
|
||||||
color: '#00BBB3',
|
NButton,
|
||||||
onClick: () => {
|
{
|
||||||
window.open(`http://5sing.kugou.com/bz/${song.id}.html`)
|
size: 'small',
|
||||||
},
|
color: '#00BBB3',
|
||||||
},
|
ghost: true,
|
||||||
{
|
onClick: () => {
|
||||||
default: () => '在5sing打开',
|
window.open(`http://5sing.kugou.com/bz/${song.id}.html`)
|
||||||
}
|
},
|
||||||
)
|
},
|
||||||
|
{
|
||||||
|
icon: () => h(FiveSingIcon, { class: 'svg-icon fivesing' }),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
),
|
||||||
|
default: () => '在5sing打开',
|
||||||
|
})
|
||||||
}
|
}
|
||||||
case SongFrom.Netease:
|
case SongFrom.Netease:
|
||||||
return h(
|
return h(NTooltip, null, {
|
||||||
NButton,
|
trigger: () =>
|
||||||
{
|
h(
|
||||||
size: 'small',
|
NButton,
|
||||||
color: '#C20C0C',
|
{
|
||||||
onClick: () => {
|
size: 'small',
|
||||||
window.open(`https://music.163.com/#/song?id=${song.id}`)
|
color: '#C20C0C',
|
||||||
},
|
ghost: true,
|
||||||
},
|
onClick: () => {
|
||||||
{
|
window.open(`https://music.163.com/#/song?id=${song.id}`)
|
||||||
default: () => '在网易云打开',
|
},
|
||||||
}
|
},
|
||||||
)
|
{
|
||||||
|
icon: () => h(NeteaseIcon, { class: 'svg-icon netease' }),
|
||||||
|
}
|
||||||
|
),
|
||||||
|
default: () => '在网易云打开',
|
||||||
|
})
|
||||||
|
case SongFrom.Custom:
|
||||||
|
return song.url
|
||||||
|
? h(NTooltip, null, {
|
||||||
|
trigger: () =>
|
||||||
|
h(
|
||||||
|
NButton,
|
||||||
|
{
|
||||||
|
size: 'small',
|
||||||
|
color: '#6b95bd',
|
||||||
|
ghost: true,
|
||||||
|
onClick: () => {
|
||||||
|
window.open(song.url)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: () => h(NIcon, { component: SquareArrowForward24Filled }),
|
||||||
|
}
|
||||||
|
),
|
||||||
|
default: () => '打开链接',
|
||||||
|
})
|
||||||
|
: null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function renderCell(value: string | number) {
|
function renderCell(value: string | number) {
|
||||||
@@ -290,13 +380,21 @@ async function updateSong() {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
async function delSong(song: SongsInfo) {
|
||||||
|
await QueryGetAPI<SongsInfo>(SONG_API_URL + 'del', {
|
||||||
|
key: song.key,
|
||||||
|
}).then((data) => {
|
||||||
|
if (data.code == 200) {
|
||||||
|
songsInternal.value = songsInternal.value.filter((s) => s.key != song.key)
|
||||||
|
message.success('已删除歌曲')
|
||||||
|
} else {
|
||||||
|
message.error('未能删除歌曲: ' + data.message)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
songsInternal.value = props.songs
|
songsInternal.value = props.songs
|
||||||
tags.value = new List(songsInternal.value)
|
|
||||||
.SelectMany((s) => new List(s?.tags))
|
|
||||||
.Distinct()
|
|
||||||
.ToArray()
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
columns.value = createColumns()
|
columns.value = createColumns()
|
||||||
}, 1)
|
}, 1)
|
||||||
@@ -304,14 +402,22 @@ onMounted(() => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
歌单 {{ songsInternal.length }}
|
<NCard embedded size="small">
|
||||||
<NCard embedded>
|
<NInput placeholder="搜索歌曲" v-model:value="searchMusicKeyword" size="small" style="max-width: 150px" />
|
||||||
<NButton> </NButton>
|
|
||||||
</NCard>
|
</NCard>
|
||||||
|
<NDivider style="margin: 5px 0 5px 0"> 共 {{ songsInternal.length }} 首 </NDivider>
|
||||||
<Transition>
|
<Transition>
|
||||||
<APlayer v-if="aplayerMusic" :music="aplayerMusic" autoplay />
|
<div v-if="aplayerMusic">
|
||||||
|
<APlayer :music="aplayerMusic" autoplay />
|
||||||
|
<NDivider style="margin: 15px 0 15px 0" />
|
||||||
|
</div>
|
||||||
</Transition>
|
</Transition>
|
||||||
<NDataTable size="small" :columns="columns" :data="songsInternal"> </NDataTable>
|
<NDataTable
|
||||||
|
size="small"
|
||||||
|
:columns="columns"
|
||||||
|
:data="songsComputed"
|
||||||
|
:pagination="{ itemCount: songsInternal.length, defaultPageSize: 25, pageSizes: [25, 50, 200], size: 'small', showSizePicker: true }"
|
||||||
|
/>
|
||||||
<NModal v-model:show="showModal" style="max-width: 600px" preset="card">
|
<NModal v-model:show="showModal" style="max-width: 600px" preset="card">
|
||||||
<template #header> 修改信息 </template>
|
<template #header> 修改信息 </template>
|
||||||
<NForm ref="formRef" :rules="updateSongRules" :model="updateSongModel" :render-cell="renderCell">
|
<NForm ref="formRef" :rules="updateSongRules" :model="updateSongModel" :render-cell="renderCell">
|
||||||
@@ -338,3 +444,12 @@ onMounted(() => {
|
|||||||
<NButton @click="updateSong" type="success"> 更新 </NButton>
|
<NButton @click="updateSong" type="success"> 更新 </NButton>
|
||||||
</NModal>
|
</NModal>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.netease path:nth-child(2) {
|
||||||
|
fill: #c20c0c;
|
||||||
|
}
|
||||||
|
.fivesing:first-child {
|
||||||
|
fill: #00bbb3;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@@ -1,5 +1,10 @@
|
|||||||
|
import { ref } from 'vue'
|
||||||
|
|
||||||
const debugAPI = import.meta.env.VITE_DEBUG_API
|
const debugAPI = import.meta.env.VITE_DEBUG_API
|
||||||
const releseAPI = `${document.location.protocol}//api.vtsuru.live/`
|
const releseAPI = `${document.location.protocol}//api.vtsuru.live/`
|
||||||
|
|
||||||
|
export const isBackendUsable = ref(true)
|
||||||
|
|
||||||
export const BASE_API = process.env.NODE_ENV === 'development' ? debugAPI : releseAPI
|
export const BASE_API = process.env.NODE_ENV === 'development' ? debugAPI : releseAPI
|
||||||
export const FETCH_API = 'https://fetch.vtsuru.live/'
|
export const FETCH_API = 'https://fetch.vtsuru.live/'
|
||||||
export const FIVESING_SEARCH_API = 'http://search.5sing.kugou.com/home/json?sort=1&page=1&filter=3&type=0&keyword='
|
export const FIVESING_SEARCH_API = 'http://search.5sing.kugou.com/home/json?sort=1&page=1&filter=3&type=0&keyword='
|
||||||
|
|||||||
@@ -1,22 +1,33 @@
|
|||||||
import { QueryGetAPI } from '@/api/query'
|
import { QueryGetAPI } from '@/api/query'
|
||||||
import { useRequest } from 'vue-request'
|
import { useRequest } from 'vue-request'
|
||||||
import { NOTIFACTION_API_URL } from './constants'
|
import { NOTIFACTION_API_URL, isBackendUsable } from './constants'
|
||||||
import { NotifactionInfo } from '@/api/api-models'
|
import { NotifactionInfo } from '@/api/api-models'
|
||||||
import { useAccount } from '@/api/account'
|
import { useAccount } from '@/api/account'
|
||||||
|
import { ref } from 'vue'
|
||||||
|
|
||||||
const account = useAccount()
|
const account = useAccount()
|
||||||
const { data, run } = useRequest(get, {
|
const n = ref<NotifactionInfo>()
|
||||||
errorRetryCount: 5,
|
let isLoading = false
|
||||||
pollingInterval: 5000,
|
|
||||||
pollingWhenHidden: false,
|
|
||||||
})
|
|
||||||
function get() {
|
function get() {
|
||||||
return QueryGetAPI<NotifactionInfo>(NOTIFACTION_API_URL + 'get')
|
if (isLoading) return
|
||||||
|
QueryGetAPI<NotifactionInfo>(NOTIFACTION_API_URL + 'get')
|
||||||
|
.then((data) => {
|
||||||
|
if (data.code == 200) {
|
||||||
|
n.value = data.data
|
||||||
|
isBackendUsable.value = true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
isBackendUsable.value = false
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
isLoading = false
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export const notifactions = () => data
|
export const notifactions = () => n
|
||||||
export const GetNotifactions = () => {
|
export const GetNotifactions = () => {
|
||||||
if (account) {
|
if (account) {
|
||||||
run()
|
setInterval(get, 5000)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ const routes: Array<RouteRecordRaw> = [
|
|||||||
{
|
{
|
||||||
path: '',
|
path: '',
|
||||||
name: 'user-index',
|
name: 'user-index',
|
||||||
component: () => import('@/views/view/IndexView.vue'),
|
component: () => import('@/views/view/UserIndexView.vue'),
|
||||||
meta: {
|
meta: {
|
||||||
title: '主页',
|
title: '主页',
|
||||||
},
|
},
|
||||||
@@ -37,7 +37,7 @@ const routes: Array<RouteRecordRaw> = [
|
|||||||
name: 'user-questionBox',
|
name: 'user-questionBox',
|
||||||
component: () => import('@/views/view/QuestionBoxView.vue'),
|
component: () => import('@/views/view/QuestionBoxView.vue'),
|
||||||
meta: {
|
meta: {
|
||||||
title: '棉花糖(提问箱',
|
title: '棉花糖 (提问箱',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|||||||
1
src/svgs/fivesing.svg
Normal file
1
src/svgs/fivesing.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" height="50" width="50" viewBox="0 0 1024 1024"><path d="M256 0h512c140.8 0 256 115.2 256 256v512c0 140.8-115.2 256-256 256H256c-140.8 0-256-115.2-256-256V256c0-140.8 115.2-256 256-256z m256 486.4c-12.8-25.6-34.133333-42.666667-46.933333-64-21.333333-25.6-38.4-55.466667-55.466667-85.333333L384 273.066667c-8.533333-21.333333-17.066667-46.933333-34.133333-64-8.533333-8.533333-25.6-12.8-38.4-12.8-12.8 4.266667-21.333333 8.533333-29.866667 12.8-21.333333 21.333333-34.133333 42.666667-51.2 64-29.866667 51.2-46.933333 106.666667-55.466667 162.133333-12.8 72.533333-8.533333 149.333333 12.8 221.866667 17.066667 59.733333 46.933333 115.2 93.866667 157.866666 12.8 17.066667 42.666667 21.333333 59.733333 8.533334 25.6-12.8 42.666667-29.866667 59.733334-51.2 21.333333-25.6 38.4-51.2 51.2-81.066667 25.6-46.933333 51.2-93.866667 64-149.333333 8.533333-17.066667 4.266667-38.4-4.266667-55.466667z m337.066667-8.533333c-8.533333-25.6-21.333333-46.933333-34.133334-68.266667-21.333333-34.133333-46.933333-64-76.8-89.6-51.2-46.933333-110.933333-85.333333-174.933333-115.2-21.333333-8.533333-42.666667-17.066667-68.266667-21.333333-17.066667-4.266667-38.4-4.266667-55.466666 0-17.066667 0-34.133333 12.8-34.133334 29.866666v21.333334c4.266667 38.4 21.333333 72.533333 38.4 102.4s38.4 59.733333 59.733334 85.333333l25.6 25.6c42.666667 38.4 93.866667 68.266667 153.6 85.333333 25.6 4.266667 51.2 4.266667 72.533333 4.266667 25.6-4.266667 55.466667-4.266667 76.8-21.333333 8.533333-4.266667 12.8-12.8 12.8-21.333334 8.533333-4.266667 4.266667-17.066667 4.266667-17.066666z m-8.533334 81.066666c-25.6-12.8-51.2 4.266667-81.066666 0-34.133333 0-64-4.266667-98.133334-8.533333-25.6-4.266667-46.933333-8.533333-72.533333-8.533333-12.8 4.266667-25.6 4.266667-34.133333 17.066666-12.8 17.066667-21.333333 34.133333-25.6 55.466667-21.333333 55.466667-51.2 106.666667-81.066667 153.6-8.533333 12.8-17.066667 21.333333-21.333333 34.133333-4.266667 8.533333-8.533333 21.333333 0 34.133334 8.533333 12.8 25.6 12.8 38.4 12.8 55.466667 4.266667 115.2-8.533333 166.4-34.133334 51.2-25.6 102.4-59.733333 145.066666-98.133333 34.133333-34.133333 68.266667-72.533333 76.8-123.733333 0-8.533333 0-25.6-12.8-34.133334z"></path></svg>
|
||||||
|
After Width: | Height: | Size: 2.2 KiB |
6
src/svgs/netease.svg
Normal file
6
src/svgs/netease.svg
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||||
|
<g>
|
||||||
|
<path fill="none" d="M0 0h24v24H0z"/>
|
||||||
|
<path d="M12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10-4.477 10-10 10zm-1.086-10.432c.24-.84 1.075-1.541 1.99-1.648.187.694.388 1.373.545 2.063.053.23.037.495-.018.727-.213.892-1.248 1.242-1.978.685-.53-.405-.742-1.12-.539-1.827zm3.817-.197c-.125-.465-.256-.927-.393-1.42.5.13.908.36 1.255.698 1.257 1.221 1.385 3.3.294 4.731-1.135 1.49-3.155 2.134-5.028 1.605-2.302-.65-3.808-2.952-3.441-5.316.274-1.768 1.27-3.004 2.9-3.733.407-.182.58-.56.42-.93-.157-.364-.54-.504-.944-.343-2.721 1.089-4.32 4.134-3.67 6.987.713 3.118 3.495 5.163 6.675 4.859 1.732-.165 3.164-.948 4.216-2.347 1.506-2.002 1.297-4.783-.463-6.499-.666-.65-1.471-1.018-2.39-1.153-.083-.013-.217-.052-.232-.106-.087-.313-.18-.632-.206-.954-.029-.357.29-.64.65-.645.253-.003.434.13.603.3.303.3.704.322.988.062.29-.264.296-.678.018-1.008-.566-.672-1.586-.891-2.43-.523-.847.37-1.321 1.187-1.2 2.093.038.28.11.557.167.842l-.26.072c-.856.24-1.561.704-2.098 1.414-.921 1.22-.936 2.828-.041 3.947 1.274 1.594 3.747 1.284 4.523-.568.284-.676.275-1.368.087-2.065z"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.1 KiB |
@@ -1,12 +1,47 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { NAvatar, NButton, NCard, NDivider, NIcon, NLayout, NLayoutFooter, NLayoutHeader, NLayoutSider, NMenu, NSpace, NText } from 'naive-ui'
|
import {
|
||||||
import { h } from 'vue'
|
NAlert,
|
||||||
import { BookOutline } from '@vicons/ionicons5'
|
NAvatar,
|
||||||
import { useAccount } from '@/api/account'
|
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 RegisterAndLogin from '@/components/RegisterAndLogin.vue'
|
||||||
import { RouterLink } from 'vue-router'
|
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 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) {
|
function renderIcon(icon: unknown) {
|
||||||
return () => h(NIcon, null, { default: () => h(icon as any) })
|
return () => h(NIcon, null, { default: () => h(icon as any) })
|
||||||
@@ -21,11 +56,12 @@ const menuOptions = [
|
|||||||
to: {
|
to: {
|
||||||
name: 'manage-songList',
|
name: 'manage-songList',
|
||||||
},
|
},
|
||||||
|
disabled: !accountInfo.value?.isEmailVerified,
|
||||||
},
|
},
|
||||||
{ default: () => '歌单' }
|
{ default: () => '歌单' }
|
||||||
),
|
),
|
||||||
key: 'manage-songList',
|
key: 'manage-songList',
|
||||||
icon: renderIcon(BookOutline),
|
icon: renderIcon(MusicalNote),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: () =>
|
label: () =>
|
||||||
@@ -39,7 +75,7 @@ const menuOptions = [
|
|||||||
{ default: () => '棉花糖 (提问箱' }
|
{ default: () => '棉花糖 (提问箱' }
|
||||||
),
|
),
|
||||||
key: 'manage-questionBox',
|
key: 'manage-questionBox',
|
||||||
icon: renderIcon(BookOutline),
|
icon: renderIcon(Chatbox),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: () =>
|
label: () =>
|
||||||
@@ -53,38 +89,117 @@ const menuOptions = [
|
|||||||
{ default: () => '动态抽奖' }
|
{ default: () => '动态抽奖' }
|
||||||
),
|
),
|
||||||
key: 'manage-lottery',
|
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>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<NLayout v-if="accountInfo">
|
<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)">
|
<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">
|
<NLayoutSider ref="sider" bordered show-trigger collapse-mode="width" :default-collapsed="windowWidth < 750" :collapsed-width="64" :width="180" :native-scrollbar="false">
|
||||||
<NButton>
|
<NSpace justify="center" style="margin-top: 16px">
|
||||||
<RouterLink :to="{ name: 'manage-index' }"> 个人中心 </RouterLink>
|
<NButton @click="$router.push({ name: 'manage-index' })" type="info" style="width: 100%">
|
||||||
</NButton>
|
<template #icon>
|
||||||
<NMenu :default-value="$route.name?.toString()" :collapsed-width="64" :collapsed-icon-size="22" :options="menuOptions" />
|
<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>
|
</NLayoutSider>
|
||||||
<NLayout style="height: 100%">
|
<NLayout style="height: 100%">
|
||||||
<div style="box-sizing: border-box; padding: 20px">
|
<div style="box-sizing: border-box; padding: 20px">
|
||||||
<RouterView v-slot="{ Component }">
|
<RouterView v-slot="{ Component }" v-if="accountInfo?.isEmailVerified">
|
||||||
<KeepAlive>
|
<KeepAlive>
|
||||||
<component :is="Component" />
|
<component :is="Component" />
|
||||||
</KeepAlive>
|
</KeepAlive>
|
||||||
</RouterView>
|
</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>
|
</div>
|
||||||
</NLayout>
|
</NLayout>
|
||||||
</NLayout>
|
</NLayout>
|
||||||
</NLayout>
|
</NLayout>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<div style="display: flex; justify-content: center; align-items: center; flex-direction: column; padding: 50px; height: 100%; box-sizing: border-box">
|
<NLayoutContent style="display: flex; justify-content: center; align-items: center; flex-direction: column; padding: 50px; height: 100%; box-sizing: border-box">
|
||||||
<NText> 请登录或注册后使用 </NText>
|
<template v-if="!isLoadingAccount">
|
||||||
<NButton tag="a" href="/"> 回到主页 </NButton>
|
<NSpace vertical justify="center" align="center">
|
||||||
<NDivider />
|
<NText> 请登录或注册后使用 </NText>
|
||||||
<RegisterAndLogin style="max-width: 500px; min-width: 350px" />
|
<NButton tag="a" href="/"> 回到主页 </NButton>
|
||||||
</div>
|
</NSpace>
|
||||||
|
<NDivider />
|
||||||
|
<RegisterAndLogin style="max-width: 500px; min-width: 350px" />
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<NSpin :loading="isLoadingAccount">
|
||||||
|
正在请求账户数据...
|
||||||
|
</NSpin>
|
||||||
|
</template>
|
||||||
|
</NLayoutContent>
|
||||||
</template>
|
</template>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -1,14 +1,16 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ACCOUNT } from '@/api/account'
|
import { ACCOUNT, useAccount } from '@/api/account'
|
||||||
import { AccountInfo } from '@/api/api-models'
|
import { AccountInfo } from '@/api/api-models'
|
||||||
import { QueryGetAPI } from '@/api/query'
|
import { QueryGetAPI } from '@/api/query'
|
||||||
import { ACCOUNT_API_URL, TURNSTILE_KEY } from '@/data/constants'
|
import { ACCOUNT_API_URL, TURNSTILE_KEY } from '@/data/constants'
|
||||||
import router from '@/router'
|
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 { ref } from 'vue'
|
||||||
import { useRoute } from 'vue-router'
|
import { useRoute } from 'vue-router'
|
||||||
import VueTurnstile from 'vue-turnstile'
|
import VueTurnstile from 'vue-turnstile'
|
||||||
|
|
||||||
|
const accountInfo = useAccount()
|
||||||
|
|
||||||
const message = useMessage()
|
const message = useMessage()
|
||||||
const token = ref('')
|
const token = ref('')
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
@@ -23,8 +25,11 @@ async function VerifyAccount() {
|
|||||||
).then((data) => {
|
).then((data) => {
|
||||||
if (data.code == 200) {
|
if (data.code == 200) {
|
||||||
ACCOUNT.value = data.data
|
ACCOUNT.value = data.data
|
||||||
router.push('index')
|
|
||||||
message.success('成功激活账户: ' + ACCOUNT.value.name)
|
message.success('成功激活账户: ' + ACCOUNT.value.name)
|
||||||
|
router.push('/manage')
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
message.error('激活失败: ' + data.message)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -32,7 +37,7 @@ async function VerifyAccount() {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div style="display: flex; align-items: center; justify-content: center; height: 100%">
|
<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>
|
<template #header> 激活账户 </template>
|
||||||
<NSpin :show="!token">
|
<NSpin :show="!token">
|
||||||
<NSpace justify="center" align="center" vertical>
|
<NSpace justify="center" align="center" vertical>
|
||||||
@@ -41,5 +46,8 @@ async function VerifyAccount() {
|
|||||||
</NSpace>
|
</NSpace>
|
||||||
</NSpin>
|
</NSpin>
|
||||||
</NCard>
|
</NCard>
|
||||||
|
<NAlert v-else type="error">
|
||||||
|
此账户已完成验证
|
||||||
|
</NAlert>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -1,74 +1,51 @@
|
|||||||
<!-- eslint-disable vue/component-name-in-template-casing -->
|
<!-- eslint-disable vue/component-name-in-template-casing -->
|
||||||
<script setup lang="ts">
|
<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 { 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 { GetInfo, useUser, useUserWithUId } from '@/api/user'
|
||||||
import { RouterLink, useRoute } from 'vue-router'
|
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 { FETCH_API } from '@/data/constants'
|
||||||
import { useAccount } from '@/api/account'
|
import { useAccount } from '@/api/account'
|
||||||
import RegisterAndLogin from '@/components/RegisterAndLogin.vue'
|
import RegisterAndLogin from '@/components/RegisterAndLogin.vue'
|
||||||
|
import { useElementSize, useStorage } from '@vueuse/core'
|
||||||
|
import { isDarkMode } from '@/Utils'
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const id = computed(() => {
|
const id = computed(() => {
|
||||||
return Number(route.params.id)
|
return Number(route.params.id)
|
||||||
})
|
})
|
||||||
const theme = useOsTheme()
|
const themeType = useStorage('Settings.Theme', ThemeType.Auto);
|
||||||
|
|
||||||
const userInfo = ref<UserInfo>()
|
const userInfo = ref<UserInfo>()
|
||||||
const biliUserInfo = ref()
|
const biliUserInfo = ref()
|
||||||
const accountInfo = useAccount()
|
const accountInfo = useAccount()
|
||||||
|
|
||||||
const registerAndLoginModalVisiable = ref(false)
|
const registerAndLoginModalVisiable = ref(false)
|
||||||
|
const sider = ref()
|
||||||
|
const { width } = useElementSize(sider)
|
||||||
|
|
||||||
function renderIcon(icon: unknown) {
|
function renderIcon(icon: unknown) {
|
||||||
return () => h(NIcon, null, { default: () => h(icon as any) })
|
return () => h(NIcon, null, { default: () => h(icon as any) })
|
||||||
}
|
}
|
||||||
const menuOptions = [
|
const menuOptions = ref<MenuOption[]>()
|
||||||
{
|
|
||||||
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),
|
|
||||||
},
|
|
||||||
]
|
|
||||||
async function RequestBiliUserData() {
|
async function RequestBiliUserData() {
|
||||||
await fetch(FETCH_API + `https://account.bilibili.com/api/member/getCardByMid?mid=${userInfo.value?.biliId}`)
|
await fetch(FETCH_API + `https://account.bilibili.com/api/member/getCardByMid?mid=${userInfo.value?.biliId}`)
|
||||||
.then(async (respone) => {
|
.then(async (respone) => {
|
||||||
@@ -86,6 +63,52 @@ async function RequestBiliUserData() {
|
|||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
userInfo.value = await useUser()
|
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()
|
await RequestBiliUserData()
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
@@ -97,8 +120,15 @@ onMounted(async () => {
|
|||||||
<NLayoutHeader style="height: 50px; padding: 5px 15px 5px 15px">
|
<NLayoutHeader style="height: 50px; padding: 5px 15px 5px 15px">
|
||||||
<NPageHeader :subtitle="($route.meta.title as string) ?? ''">
|
<NPageHeader :subtitle="($route.meta.title as string) ?? ''">
|
||||||
<template #extra>
|
<template #extra>
|
||||||
<NSpace>
|
<NSpace align="center">
|
||||||
<NSwitch> </NSwitch>
|
<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">
|
<template v-if="accountInfo">
|
||||||
<NButton style="right: 0px; position: relative" type="primary" @click="$router.push({ name: 'manage-index' })"> 个人中心 </NButton>
|
<NButton style="right: 0px; position: relative" type="primary" @click="$router.push({ name: 'manage-index' })"> 个人中心 </NButton>
|
||||||
</template>
|
</template>
|
||||||
@@ -108,36 +138,38 @@ onMounted(async () => {
|
|||||||
</NSpace>
|
</NSpace>
|
||||||
</template>
|
</template>
|
||||||
<template #title>
|
<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>
|
</template>
|
||||||
</NPageHeader>
|
</NPageHeader>
|
||||||
</NLayoutHeader>
|
</NLayoutHeader>
|
||||||
<NLayout has-sider style="height: calc(100vh - 50px)">
|
<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>
|
<Transition>
|
||||||
<div v-if="biliUserInfo" style="margin-top: 15px;">
|
<div v-if="biliUserInfo" style="margin-top: 8px">
|
||||||
<NSpace vertical justify="center" align="center">
|
<NSpace vertical justify="center" align="center">
|
||||||
<NAvatar :src="biliUserInfo.face" :img-props="{ referrerpolicy: 'no-referrer' }" />
|
<NAvatar :src="biliUserInfo.face" :img-props="{ referrerpolicy: 'no-referrer' }" round bordered style="box-shadow: 0 2px 3px rgba(0, 0, 0, 0.1)" />
|
||||||
<NText strong>
|
<NEllipsis v-if="width > 100" style="max-width: 100%">
|
||||||
{{ biliUserInfo.name }}
|
<NText strong>
|
||||||
</NText>
|
{{ biliUserInfo.name }}
|
||||||
|
</NText>
|
||||||
|
</NEllipsis>
|
||||||
</NSpace>
|
</NSpace>
|
||||||
</div>
|
</div>
|
||||||
</Transition>
|
</Transition>
|
||||||
<NMenu :default-value="$route.name?.toString()" :collapsed-width="64" :collapsed-icon-size="22" :options="menuOptions" />
|
<NMenu :default-value="$route.name?.toString()" :collapsed-width="64" :collapsed-icon-size="22" :options="menuOptions" />
|
||||||
</NLayoutSider>
|
</NLayoutSider>
|
||||||
<NLayout style="height: 100%">
|
<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 }">
|
<RouterView v-slot="{ Component }">
|
||||||
<KeepAlive>
|
<KeepAlive>
|
||||||
<component :is="Component" />
|
<component :is="Component" :bili-info="biliUserInfo" />
|
||||||
</KeepAlive>
|
</KeepAlive>
|
||||||
</RouterView>
|
</RouterView>
|
||||||
</div>
|
</div>
|
||||||
</NLayout>
|
</NLayout>
|
||||||
</NLayout>
|
</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 />
|
<RegisterAndLogin />
|
||||||
</NModal>
|
</NModal>
|
||||||
</template>
|
</template>
|
||||||
@@ -146,7 +178,6 @@ onMounted(async () => {
|
|||||||
.viewer-page-content{
|
.viewer-page-content{
|
||||||
height: 100%;
|
height: 100%;
|
||||||
border-radius: 18px;
|
border-radius: 18px;
|
||||||
box-shadow: inset 5px 5px 6px #8b8b8b17, inset -5px -5px 6px #8b8b8b17;
|
|
||||||
padding: 15px;
|
padding: 15px;
|
||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
|||||||
@@ -90,7 +90,7 @@ onMounted(async () => {
|
|||||||
请在点击
|
请在点击
|
||||||
<NText type="primary" strong> 开始认证 </NText>
|
<NText type="primary" strong> 开始认证 </NText>
|
||||||
后五分钟之内使用
|
后五分钟之内使用
|
||||||
<NText> 需要认证的账户 </NText>
|
<NText strong type="primary"> 需要认证的账户 </NText>
|
||||||
在自己的直播间内发送
|
在自己的直播间内发送
|
||||||
<NButton type="info" text @click="copyCode">
|
<NButton type="info" text @click="copyCode">
|
||||||
{{ accountInfo?.biliVerifyCode }}
|
{{ accountInfo?.biliVerifyCode }}
|
||||||
|
|||||||
@@ -1,32 +1,48 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useAccount } from '@/api/account'
|
import { useAccount } from '@/api/account'
|
||||||
import { NAlert, NButton, NCard, NDivider, NSpace, NTag, NText, NThing, NTime } from 'naive-ui'
|
import { NAlert, NButton, NCard, NDivider, NSpace, NTag, NText, NThing, NTime } from 'naive-ui'
|
||||||
|
import SettingsManageView from './SettingsManageView.vue';
|
||||||
|
|
||||||
const accountInfo = useAccount()
|
const accountInfo = useAccount()
|
||||||
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<NCard embedded style="max-width: 500px">
|
<NSpace justify="center" align="center" vertical>
|
||||||
<NSpace align="center" justify="center" vertical>
|
<NCard embedded style="max-width: 90%;width: 800px;">
|
||||||
<NText style="font-size: 3rem">
|
<NSpace align="center" justify="center" vertical>
|
||||||
{{ accountInfo?.name }}
|
<NText style="font-size: 3rem">
|
||||||
</NText>
|
{{ accountInfo?.name }}
|
||||||
<NText style="color: gray">
|
</NText>
|
||||||
于
|
<NText style="color: gray">
|
||||||
<NTime :time="accountInfo?.createAt" />
|
于
|
||||||
注册
|
<NTime :time="accountInfo?.createAt" />
|
||||||
</NText>
|
注册
|
||||||
</NSpace>
|
</NText>
|
||||||
|
</NSpace>
|
||||||
|
|
||||||
<NDivider />
|
<NDivider />
|
||||||
<NAlert>
|
<NAlert>
|
||||||
Bilibili 账户:
|
邮箱:
|
||||||
<NTag v-if="accountInfo?.isBiliVerified" type="success"> 已认证 </NTag>
|
<NTag v-if="accountInfo?.isEmailVerified" type="success"> 已认证 | {{ accountInfo?.bindEmail }} </NTag>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<NTag type="error" size="small"> 未认证 </NTag>
|
<NTag type="error" size="small"> 未认证 </NTag>
|
||||||
<NDivider vertical />
|
</template>
|
||||||
<NButton size="small" @click="$router.push({ name: 'manage-biliVerify' })" type="info"> 前往认证 </NButton>
|
</NAlert>
|
||||||
</template>
|
<NAlert>
|
||||||
</NAlert>
|
Bilibili 账户:
|
||||||
</NCard>
|
<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>
|
</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) {
|
async function favorite(question: QAInfo, fav: boolean) {
|
||||||
await QueryGetAPI(QUESTION_API_URL + 'read', {
|
await QueryGetAPI(QUESTION_API_URL + 'favorite', {
|
||||||
id: question.id,
|
id: question.id,
|
||||||
read: fav ? 'true' : 'false',
|
favorite: fav ? 'true' : 'false',
|
||||||
})
|
})
|
||||||
.then((data) => {
|
.then((data) => {
|
||||||
if (data.code == 200) {
|
if (data.code == 200) {
|
||||||
@@ -164,6 +164,11 @@ function onOpenModal(question: QAInfo) {
|
|||||||
replyMessage.value = question.answer?.message
|
replyMessage.value = question.answer?.message
|
||||||
replyModalVisiable.value = true
|
replyModalVisiable.value = true
|
||||||
}
|
}
|
||||||
|
function refresh() {
|
||||||
|
isSendGetted = false
|
||||||
|
isRevieveGetted = false
|
||||||
|
onTabChange(selectedTabItem.value)
|
||||||
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
GetRecieveQAInfo()
|
GetRecieveQAInfo()
|
||||||
@@ -171,12 +176,12 @@ onMounted(() => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<NButton type="primary"> 刷新 </NButton>
|
<NButton type="primary" @click="refresh"> 刷新 </NButton>
|
||||||
<NDivider style="margin: 10px 0 10px 0" />
|
<NDivider style="margin: 10px 0 10px 0" />
|
||||||
<NTabs animated @update:value="onTabChange" v-model:value="selectedTabItem">
|
<NTabs animated @update:value="onTabChange" v-model:value="selectedTabItem">
|
||||||
<NTabPane tab="我收到的" name="0">
|
<NTabPane tab="我收到的" name="0">
|
||||||
只显示收藏 <NSwitch v-model:value="onlyFavorite" />
|
只显示收藏 <NSwitch v-model:value="onlyFavorite" />
|
||||||
<NList>
|
<NList :bordered="false">
|
||||||
<NListItem v-for="item in recieveQuestionsFiltered" :key="item.id">
|
<NListItem v-for="item in recieveQuestionsFiltered" :key="item.id">
|
||||||
<NCard :embedded="!item.isReaded" hoverable size="small">
|
<NCard :embedded="!item.isReaded" hoverable size="small">
|
||||||
<template #header>
|
<template #header>
|
||||||
@@ -209,7 +214,7 @@ onMounted(() => {
|
|||||||
收藏
|
收藏
|
||||||
</NButton>
|
</NButton>
|
||||||
<NButton size="small"> 举报 </NButton>
|
<NButton size="small"> 举报 </NButton>
|
||||||
<NButton size="small"> 拉黑 </NButton>
|
<NButton size="small" @click="blacklist(item)"> 拉黑 </NButton>
|
||||||
</NSpace>
|
</NSpace>
|
||||||
</template>
|
</template>
|
||||||
<template #header-extra>
|
<template #header-extra>
|
||||||
@@ -279,3 +284,9 @@ onMounted(() => {
|
|||||||
<NButton :loading="isRepling" @click="reply" type="primary"> 发送 </NButton>
|
<NButton :loading="isRepling" @click="reply" type="primary"> 发送 </NButton>
|
||||||
</NModal>
|
</NModal>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.n-list{
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useAccount } from '@/api/account'
|
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 { ref } from 'vue'
|
||||||
import { useRequest } from 'vue-request'
|
import { useRequest } from 'vue-request'
|
||||||
import { FunctionTypes } from '@/api/api-models'
|
import { FunctionTypes } from '@/api/api-models'
|
||||||
@@ -20,24 +20,60 @@ function UpdateEnableFunction(func: FunctionTypes, enable: boolean) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
async function SaveSetting() {
|
async function SaveComboGroupSetting(value: (string | number)[], meta: { actionType: 'check' | 'uncheck'; value: string | number }) {
|
||||||
const data = await QueryPostAPI(ACCOUNT_API_URL + 'update-setting', {
|
if (account.value) {
|
||||||
setting: JSON.stringify(account.value?.settings),
|
//UpdateEnableFunction(meta.value as FunctionTypes, meta.actionType == 'check')
|
||||||
})
|
await QueryPostAPI(ACCOUNT_API_URL + 'update-setting', account.value?.settings)
|
||||||
if (data.code == 200) {
|
.then((data) => {
|
||||||
message.success('保存成功')
|
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>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<NCard v-if="account">
|
<NCard v-if="account" title="设置">
|
||||||
<NDivider> 启用功能 </NDivider>
|
<NDivider style="margin: 0"> 启用功能 </NDivider>
|
||||||
<NCheckboxGroup v-model:value="account.settings.enableFunctions">
|
<NCheckboxGroup v-model:value="account.settings.enableFunctions" @update:value="SaveComboGroupSetting">
|
||||||
<NCheckbox :value="FunctionTypes.SongList"> 歌单 </NCheckbox>
|
<NCheckbox :value="FunctionTypes.SongList"> 歌单 </NCheckbox>
|
||||||
<NCheckbox :value="FunctionTypes.QuestionBox"> 提问箱(棉花糖 </NCheckbox>
|
<NCheckbox :value="FunctionTypes.QuestionBox"> 提问箱(棉花糖 </NCheckbox>
|
||||||
</NCheckboxGroup>
|
</NCheckboxGroup>
|
||||||
<NDivider />
|
<NDivider > 通知 </NDivider>
|
||||||
<NButton @click="SaveSetting"> 保存 </NButton>
|
<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>
|
</NCard>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -171,10 +171,22 @@ async function addNeteaseSongs() {
|
|||||||
}
|
}
|
||||||
async function addFingsingSongs(song: SongsInfo) {
|
async function addFingsingSongs(song: SongsInfo) {
|
||||||
isModalLoading.value = true
|
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)
|
await addSongs([song], SongFrom.FiveSing)
|
||||||
.then((data) => {
|
.then((data) => {
|
||||||
if (data.code == 200) {
|
if (data.code == 200) {
|
||||||
message.success(`已添加歌曲`)
|
message.success(`已添加歌曲`)
|
||||||
|
addSongModel.value = {} as SongsInfo
|
||||||
songs.value.push(...data.data)
|
songs.value.push(...data.data)
|
||||||
} else {
|
} else {
|
||||||
message.error('添加失败: ' + data.message)
|
message.error('添加失败: ' + data.message)
|
||||||
@@ -311,8 +323,21 @@ onMounted(async () => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<NButton @click="showModal = true"> 添加歌曲 </NButton>
|
<NSpace>
|
||||||
<NModal v-model:show="showModal" style="max-width: 1000px;" preset="card">
|
<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>
|
<template #header> 添加歌曲 </template>
|
||||||
<NSpin :show="isModalLoading">
|
<NSpin :show="isModalLoading">
|
||||||
<NTabs default-value="custom" animated>
|
<NTabs default-value="custom" animated>
|
||||||
@@ -377,10 +402,10 @@ onMounted(async () => {
|
|||||||
</NTag>
|
</NTag>
|
||||||
</NSpace>
|
</NSpace>
|
||||||
</td>
|
</td>
|
||||||
<td style="display: flex; justify-content: flex-end;">
|
<td style="display: flex; justify-content: flex-end">
|
||||||
<!-- 在这里播放song.url链接中的音频 -->
|
<!-- 在这里播放song.url链接中的音频 -->
|
||||||
<NButton size="small" v-if="!song.url" @click="playFivesingSong(song)" :loading="isGettingFivesingSongPlayUrl == song.id"> 试听 </NButton>
|
<NButton size="small" v-if="!song.url" @click="playFivesingSong(song)" :loading="isGettingFivesingSongPlayUrl == song.id"> 试听 </NButton>
|
||||||
<audio v-else controls autoplay style="max-height: 30px">
|
<audio v-else controls style="max-height: 30px">
|
||||||
<source :src="song.url" />
|
<source :src="song.url" />
|
||||||
</audio>
|
</audio>
|
||||||
</td>
|
</td>
|
||||||
@@ -398,5 +423,6 @@ onMounted(async () => {
|
|||||||
</NTabs>
|
</NTabs>
|
||||||
</NSpin>
|
</NSpin>
|
||||||
</NModal>
|
</NModal>
|
||||||
<SongList :songs="songs" />
|
<SongList :songs="songs" is-self />
|
||||||
|
<NDivider />
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -50,6 +50,7 @@ async function SendQuestion() {
|
|||||||
if (data.code == 200) {
|
if (data.code == 200) {
|
||||||
message.success('成功发送棉花糖')
|
message.success('成功发送棉花糖')
|
||||||
questionMessage.value = ''
|
questionMessage.value = ''
|
||||||
|
fileList.value = []
|
||||||
} else {
|
} else {
|
||||||
message.error(data.message)
|
message.error(data.message)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,40 +1,70 @@
|
|||||||
<script setup lang="ts">
|
<template>
|
||||||
import { SongsInfo } from '@/api/api-models'
|
<NSpin v-if="isLoading" show />
|
||||||
import { QueryGetAPI, QueryGetPaginationAPI } from '@/api/query'
|
<component v-else :is="songListType" :user-info="userInfo" :songs="songs" />
|
||||||
import SongList from '@/components/SongList.vue'
|
</template>
|
||||||
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'
|
|
||||||
|
|
||||||
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 songs = ref<SongsInfo[]>()
|
||||||
const uId = useRouteParams('id', '-1', { transform: Number })
|
const uId = useRouteParams('id', '-1', { transform: Number })
|
||||||
|
const isLoading = ref(true)
|
||||||
|
const message = useMessage()
|
||||||
|
|
||||||
const errMessage = ref('')
|
const errMessage = ref('')
|
||||||
|
|
||||||
async function getSongs() {
|
async function getSongs() {
|
||||||
|
isLoading.value = true
|
||||||
await QueryGetAPI<SongsInfo[]>(SONG_API_URL + 'get', {
|
await QueryGetAPI<SongsInfo[]>(SONG_API_URL + 'get', {
|
||||||
id: uId.value,
|
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 () => {
|
onMounted(async () => {
|
||||||
|
userInfo.value = await useUser()
|
||||||
await getSongs()
|
await getSongs()
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
|
||||||
<SongList v-if="songs" :songs="songs ?? []" />
|
|
||||||
<NAlert v-else-if="errMessage" type="error">
|
|
||||||
{{ errMessage }}
|
|
||||||
</NAlert>
|
|
||||||
</template>
|
|
||||||
|
|||||||
@@ -1,23 +1,22 @@
|
|||||||
<template>
|
<template>
|
||||||
<div style="display: flex; justify-content: center">
|
<component :is="indexType" :user-info="userInfo" :bili-info="biliInfo"/>
|
||||||
<div>
|
|
||||||
<NText strong tag="h1"> vtsuru </NText>
|
|
||||||
<component :is="indexType" :user-info="userInfo"/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { useAccount } from '@/api/account'
|
|
||||||
import { useUser } from '@/api/user'
|
import { useUser } from '@/api/user'
|
||||||
import { IndexTypes } from '@/api/api-models'
|
import { IndexTypes } from '@/api/api-models'
|
||||||
import { NButton, NText } from 'naive-ui'
|
|
||||||
import DefaultIndexTemplate from '@/views/view/indexTemplate/DefaultIndexTemplate.vue'
|
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(() => {
|
const indexType = computed(() => {
|
||||||
if (userInfo) {
|
if (userInfo.value) {
|
||||||
switch (userInfo.indexType) {
|
switch (userInfo.value.indexType) {
|
||||||
case IndexTypes.Default:
|
case IndexTypes.Default:
|
||||||
return DefaultIndexTemplate
|
return DefaultIndexTemplate
|
||||||
|
|
||||||
@@ -28,6 +27,9 @@ const indexType = computed(() => {
|
|||||||
return DefaultIndexTemplate
|
return DefaultIndexTemplate
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
const accountInfo = useAccount()
|
const userInfo = ref<UserInfo>()
|
||||||
const userInfo = await useUser()
|
|
||||||
|
onMounted(async () => {
|
||||||
|
userInfo.value = await useUser()
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
@@ -1,11 +1,44 @@
|
|||||||
<script lang="ts" setup>
|
<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<{
|
const props = defineProps<{
|
||||||
userInfo: UserInfo
|
userInfo: UserInfo | undefined
|
||||||
|
biliInfo: any | undefined
|
||||||
}>()
|
}>()
|
||||||
|
function navigate(url: string) {
|
||||||
|
window.open(url, '_blank')
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
1
|
<NDivider />
|
||||||
</template>
|
<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>
|
||||||
@@ -2,9 +2,10 @@
|
|||||||
import { defineConfig } from 'vite'
|
import { defineConfig } from 'vite'
|
||||||
import vue from '@vitejs/plugin-vue'
|
import vue from '@vitejs/plugin-vue'
|
||||||
import path from 'path'
|
import path from 'path'
|
||||||
|
import svgLoader from 'vite-svg-loader'
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
plugins: [vue()],
|
plugins: [vue(), svgLoader()],
|
||||||
resolve: {
|
resolve: {
|
||||||
alias: {
|
alias: {
|
||||||
'@': path.resolve(__dirname, 'src'),
|
'@': path.resolve(__dirname, 'src'),
|
||||||
@@ -14,3 +15,4 @@ export default defineConfig({
|
|||||||
'process.env': {},
|
'process.env': {},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
125
yarn.lock
125
yarn.lock
@@ -264,6 +264,11 @@
|
|||||||
"@nodelib/fs.scandir" "2.1.5"
|
"@nodelib/fs.scandir" "2.1.5"
|
||||||
fastq "^1.6.0"
|
fastq "^1.6.0"
|
||||||
|
|
||||||
|
"@trysound/sax@0.2.0":
|
||||||
|
version "0.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@trysound/sax/-/sax-0.2.0.tgz#cccaab758af56761eb7bf37af6f03f326dd798ad"
|
||||||
|
integrity sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==
|
||||||
|
|
||||||
"@types/cookie@^0.3.3":
|
"@types/cookie@^0.3.3":
|
||||||
version "0.3.3"
|
version "0.3.3"
|
||||||
resolved "https://registry.yarnpkg.com/@types/cookie/-/cookie-0.3.3.tgz#85bc74ba782fb7aa3a514d11767832b0e3bc6803"
|
resolved "https://registry.yarnpkg.com/@types/cookie/-/cookie-0.3.3.tgz#85bc74ba782fb7aa3a514d11767832b0e3bc6803"
|
||||||
@@ -400,6 +405,11 @@
|
|||||||
"@typescript-eslint/types" "5.62.0"
|
"@typescript-eslint/types" "5.62.0"
|
||||||
eslint-visitor-keys "^3.3.0"
|
eslint-visitor-keys "^3.3.0"
|
||||||
|
|
||||||
|
"@vicons/fluent@^0.12.0":
|
||||||
|
version "0.12.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@vicons/fluent/-/fluent-0.12.0.tgz#7e9a39190114f7cb682a71dd166e7efec35e5e9c"
|
||||||
|
integrity sha512-ATCiqPuiJ6RI5GBlD3BIpZ9Xw4MsCA4RpI5oR6MCti4quS4mX1Gp6N74FCzw7lgOj+80rV4HMKhZTVInwimpVQ==
|
||||||
|
|
||||||
"@vicons/ionicons5@^0.12.0":
|
"@vicons/ionicons5@^0.12.0":
|
||||||
version "0.12.0"
|
version "0.12.0"
|
||||||
resolved "https://registry.yarnpkg.com/@vicons/ionicons5/-/ionicons5-0.12.0.tgz#c39fda04420dfae3b58053faf8aaf3555253299d"
|
resolved "https://registry.yarnpkg.com/@vicons/ionicons5/-/ionicons5-0.12.0.tgz#c39fda04420dfae3b58053faf8aaf3555253299d"
|
||||||
@@ -428,7 +438,7 @@
|
|||||||
"@vue/compiler-core" "3.3.4"
|
"@vue/compiler-core" "3.3.4"
|
||||||
"@vue/shared" "3.3.4"
|
"@vue/shared" "3.3.4"
|
||||||
|
|
||||||
"@vue/compiler-sfc@3.3.4":
|
"@vue/compiler-sfc@3.3.4", "@vue/compiler-sfc@^3.2.20":
|
||||||
version "3.3.4"
|
version "3.3.4"
|
||||||
resolved "https://registry.yarnpkg.com/@vue/compiler-sfc/-/compiler-sfc-3.3.4.tgz#b19d942c71938893535b46226d602720593001df"
|
resolved "https://registry.yarnpkg.com/@vue/compiler-sfc/-/compiler-sfc-3.3.4.tgz#b19d942c71938893535b46226d602720593001df"
|
||||||
integrity sha512-6y/d8uw+5TkCuzBkgLS0v3lSM3hJDntFEiUORM11pQ/hKvkhSKZrXW6i69UyXlJQisJxuUEJKAWEqWbWsLeNKQ==
|
integrity sha512-6y/d8uw+5TkCuzBkgLS0v3lSM3hJDntFEiUORM11pQ/hKvkhSKZrXW6i69UyXlJQisJxuUEJKAWEqWbWsLeNKQ==
|
||||||
@@ -844,6 +854,11 @@ colorette@^2.0.16:
|
|||||||
resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.20.tgz#9eb793e6833067f7235902fcd3b09917a000a95a"
|
resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.20.tgz#9eb793e6833067f7235902fcd3b09917a000a95a"
|
||||||
integrity sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==
|
integrity sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==
|
||||||
|
|
||||||
|
commander@^7.2.0:
|
||||||
|
version "7.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7"
|
||||||
|
integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==
|
||||||
|
|
||||||
commander@^8.2.0:
|
commander@^8.2.0:
|
||||||
version "8.3.0"
|
version "8.3.0"
|
||||||
resolved "https://registry.yarnpkg.com/commander/-/commander-8.3.0.tgz#4837ea1b2da67b9c616a67afbb0fafee567bca66"
|
resolved "https://registry.yarnpkg.com/commander/-/commander-8.3.0.tgz#4837ea1b2da67b9c616a67afbb0fafee567bca66"
|
||||||
@@ -897,6 +912,38 @@ css-render@^0.15.10, css-render@^0.15.12:
|
|||||||
"@emotion/hash" "~0.8.0"
|
"@emotion/hash" "~0.8.0"
|
||||||
csstype "~3.0.5"
|
csstype "~3.0.5"
|
||||||
|
|
||||||
|
css-select@^5.1.0:
|
||||||
|
version "5.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/css-select/-/css-select-5.1.0.tgz#b8ebd6554c3637ccc76688804ad3f6a6fdaea8a6"
|
||||||
|
integrity sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==
|
||||||
|
dependencies:
|
||||||
|
boolbase "^1.0.0"
|
||||||
|
css-what "^6.1.0"
|
||||||
|
domhandler "^5.0.2"
|
||||||
|
domutils "^3.0.1"
|
||||||
|
nth-check "^2.0.1"
|
||||||
|
|
||||||
|
css-tree@^2.2.1:
|
||||||
|
version "2.3.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-2.3.1.tgz#10264ce1e5442e8572fc82fbe490644ff54b5c20"
|
||||||
|
integrity sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==
|
||||||
|
dependencies:
|
||||||
|
mdn-data "2.0.30"
|
||||||
|
source-map-js "^1.0.1"
|
||||||
|
|
||||||
|
css-tree@~2.2.0:
|
||||||
|
version "2.2.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-2.2.1.tgz#36115d382d60afd271e377f9c5f67d02bd48c032"
|
||||||
|
integrity sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==
|
||||||
|
dependencies:
|
||||||
|
mdn-data "2.0.28"
|
||||||
|
source-map-js "^1.0.1"
|
||||||
|
|
||||||
|
css-what@^6.1.0:
|
||||||
|
version "6.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/css-what/-/css-what-6.1.0.tgz#fb5effcf76f1ddea2c81bdfaa4de44e79bac70f4"
|
||||||
|
integrity sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==
|
||||||
|
|
||||||
css@^3.0.0:
|
css@^3.0.0:
|
||||||
version "3.0.0"
|
version "3.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/css/-/css-3.0.0.tgz#4447a4d58fdd03367c516ca9f64ae365cee4aa5d"
|
resolved "https://registry.yarnpkg.com/css/-/css-3.0.0.tgz#4447a4d58fdd03367c516ca9f64ae365cee4aa5d"
|
||||||
@@ -911,6 +958,13 @@ cssesc@^3.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee"
|
resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee"
|
||||||
integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==
|
integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==
|
||||||
|
|
||||||
|
csso@^5.0.5:
|
||||||
|
version "5.0.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/csso/-/csso-5.0.5.tgz#f9b7fe6cc6ac0b7d90781bb16d5e9874303e2ca6"
|
||||||
|
integrity sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==
|
||||||
|
dependencies:
|
||||||
|
css-tree "~2.2.0"
|
||||||
|
|
||||||
csstype@^3.1.1:
|
csstype@^3.1.1:
|
||||||
version "3.1.2"
|
version "3.1.2"
|
||||||
resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.2.tgz#1d4bf9d572f11c14031f0436e1c10bc1f571f50b"
|
resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.2.tgz#1d4bf9d572f11c14031f0436e1c10bc1f571f50b"
|
||||||
@@ -1008,6 +1062,36 @@ doctrine@^3.0.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
esutils "^2.0.2"
|
esutils "^2.0.2"
|
||||||
|
|
||||||
|
dom-serializer@^2.0.0:
|
||||||
|
version "2.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-2.0.0.tgz#e41b802e1eedf9f6cae183ce5e622d789d7d8e53"
|
||||||
|
integrity sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==
|
||||||
|
dependencies:
|
||||||
|
domelementtype "^2.3.0"
|
||||||
|
domhandler "^5.0.2"
|
||||||
|
entities "^4.2.0"
|
||||||
|
|
||||||
|
domelementtype@^2.3.0:
|
||||||
|
version "2.3.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.3.0.tgz#5c45e8e869952626331d7aab326d01daf65d589d"
|
||||||
|
integrity sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==
|
||||||
|
|
||||||
|
domhandler@^5.0.2, domhandler@^5.0.3:
|
||||||
|
version "5.0.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-5.0.3.tgz#cc385f7f751f1d1fc650c21374804254538c7d31"
|
||||||
|
integrity sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==
|
||||||
|
dependencies:
|
||||||
|
domelementtype "^2.3.0"
|
||||||
|
|
||||||
|
domutils@^3.0.1:
|
||||||
|
version "3.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/domutils/-/domutils-3.1.0.tgz#c47f551278d3dc4b0b1ab8cbb42d751a6f0d824e"
|
||||||
|
integrity sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==
|
||||||
|
dependencies:
|
||||||
|
dom-serializer "^2.0.0"
|
||||||
|
domelementtype "^2.3.0"
|
||||||
|
domhandler "^5.0.3"
|
||||||
|
|
||||||
emoji-regex@^10.0.0:
|
emoji-regex@^10.0.0:
|
||||||
version "10.2.1"
|
version "10.2.1"
|
||||||
resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-10.2.1.tgz#a41c330d957191efd3d9dfe6e1e8e1e9ab048b3f"
|
resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-10.2.1.tgz#a41c330d957191efd3d9dfe6e1e8e1e9ab048b3f"
|
||||||
@@ -1040,6 +1124,11 @@ enquirer@^2.3.6:
|
|||||||
ansi-colors "^4.1.1"
|
ansi-colors "^4.1.1"
|
||||||
strip-ansi "^6.0.1"
|
strip-ansi "^6.0.1"
|
||||||
|
|
||||||
|
entities@^4.2.0:
|
||||||
|
version "4.5.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/entities/-/entities-4.5.0.tgz#5d268ea5e7113ec74c4d033b79ea5a35a488fb48"
|
||||||
|
integrity sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==
|
||||||
|
|
||||||
error-ex@^1.3.1:
|
error-ex@^1.3.1:
|
||||||
version "1.3.2"
|
version "1.3.2"
|
||||||
resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf"
|
resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf"
|
||||||
@@ -2027,6 +2116,16 @@ magic-string@^0.30.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
"@jridgewell/sourcemap-codec" "^1.4.15"
|
"@jridgewell/sourcemap-codec" "^1.4.15"
|
||||||
|
|
||||||
|
mdn-data@2.0.28:
|
||||||
|
version "2.0.28"
|
||||||
|
resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.28.tgz#5ec48e7bef120654539069e1ae4ddc81ca490eba"
|
||||||
|
integrity sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==
|
||||||
|
|
||||||
|
mdn-data@2.0.30:
|
||||||
|
version "2.0.30"
|
||||||
|
resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.30.tgz#ce4df6f80af6cfbe218ecd5c552ba13c4dfa08cc"
|
||||||
|
integrity sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==
|
||||||
|
|
||||||
memory-fs@^0.2.0:
|
memory-fs@^0.2.0:
|
||||||
version "0.2.0"
|
version "0.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.2.0.tgz#f2bb25368bc121e391c2520de92969caee0a0290"
|
resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.2.0.tgz#f2bb25368bc121e391c2520de92969caee0a0290"
|
||||||
@@ -2138,7 +2237,7 @@ npm-run-path@^4.0.1:
|
|||||||
dependencies:
|
dependencies:
|
||||||
path-key "^3.0.0"
|
path-key "^3.0.0"
|
||||||
|
|
||||||
nth-check@^2.1.1:
|
nth-check@^2.0.1, nth-check@^2.1.1:
|
||||||
version "2.1.1"
|
version "2.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-2.1.1.tgz#c9eab428effce36cd6b92c924bdb000ef1f1ed1d"
|
resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-2.1.1.tgz#c9eab428effce36cd6b92c924bdb000ef1f1ed1d"
|
||||||
integrity sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==
|
integrity sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==
|
||||||
@@ -2549,7 +2648,7 @@ slice-ansi@^4.0.0:
|
|||||||
astral-regex "^2.0.0"
|
astral-regex "^2.0.0"
|
||||||
is-fullwidth-code-point "^3.0.0"
|
is-fullwidth-code-point "^3.0.0"
|
||||||
|
|
||||||
source-map-js@^1.0.2:
|
source-map-js@^1.0.1, source-map-js@^1.0.2:
|
||||||
version "1.0.2"
|
version "1.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c"
|
resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c"
|
||||||
integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==
|
integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==
|
||||||
@@ -2684,6 +2783,18 @@ supports-preserve-symlinks-flag@^1.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09"
|
resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09"
|
||||||
integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==
|
integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==
|
||||||
|
|
||||||
|
svgo@^3.0.2:
|
||||||
|
version "3.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/svgo/-/svgo-3.0.2.tgz#5e99eeea42c68ee0dc46aa16da093838c262fe0a"
|
||||||
|
integrity sha512-Z706C1U2pb1+JGP48fbazf3KxHrWOsLme6Rv7imFBn5EnuanDW1GPaA/P1/dvObE670JDePC3mnj0k0B7P0jjQ==
|
||||||
|
dependencies:
|
||||||
|
"@trysound/sax" "0.2.0"
|
||||||
|
commander "^7.2.0"
|
||||||
|
css-select "^5.1.0"
|
||||||
|
css-tree "^2.2.1"
|
||||||
|
csso "^5.0.5"
|
||||||
|
picocolors "^1.0.0"
|
||||||
|
|
||||||
tapable@^0.1.8:
|
tapable@^0.1.8:
|
||||||
version "0.1.10"
|
version "0.1.10"
|
||||||
resolved "https://registry.yarnpkg.com/tapable/-/tapable-0.1.10.tgz#29c35707c2b70e50d07482b5d202e8ed446dafd4"
|
resolved "https://registry.yarnpkg.com/tapable/-/tapable-0.1.10.tgz#29c35707c2b70e50d07482b5d202e8ed446dafd4"
|
||||||
@@ -2836,6 +2947,14 @@ vdirs@^0.1.4, vdirs@^0.1.8:
|
|||||||
dependencies:
|
dependencies:
|
||||||
evtd "^0.2.2"
|
evtd "^0.2.2"
|
||||||
|
|
||||||
|
vite-svg-loader@^4.0.0:
|
||||||
|
version "4.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/vite-svg-loader/-/vite-svg-loader-4.0.0.tgz#1cec4337dba3c23ab13bcabb111896e251b047ac"
|
||||||
|
integrity sha512-0MMf1yzzSYlV4MGePsLVAOqXsbF5IVxbn4EEzqRnWxTQl8BJg/cfwIzfQNmNQxZp5XXwd4kyRKF1LytuHZTnqA==
|
||||||
|
dependencies:
|
||||||
|
"@vue/compiler-sfc" "^3.2.20"
|
||||||
|
svgo "^3.0.2"
|
||||||
|
|
||||||
vite@^4.3.9:
|
vite@^4.3.9:
|
||||||
version "4.4.11"
|
version "4.4.11"
|
||||||
resolved "https://registry.yarnpkg.com/vite/-/vite-4.4.11.tgz#babdb055b08c69cfc4c468072a2e6c9ca62102b0"
|
resolved "https://registry.yarnpkg.com/vite/-/vite-4.4.11.tgz#babdb055b08c69cfc4c468072a2e6c9ca62102b0"
|
||||||
|
|||||||
Reference in New Issue
Block a user