mirror of
https://github.com/Megghy/vtsuru.live.git
synced 2025-12-07 02:46:55 +08:00
alpha
This commit is contained in:
@@ -22,9 +22,11 @@ export interface UserInfo {
|
|||||||
createAt: number
|
createAt: number
|
||||||
biliId?: number
|
biliId?: number
|
||||||
biliRoomId?: number
|
biliRoomId?: number
|
||||||
indexType: IndexTypes
|
extra?: {
|
||||||
songListType: SongListTypes
|
enableFunctions: FunctionTypes[]
|
||||||
enableFunctions: FunctionTypes[]
|
isInBlackList: boolean
|
||||||
|
templateTypes: { [key: string]: string }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
export interface AccountInfo extends UserInfo {
|
export interface AccountInfo extends UserInfo {
|
||||||
isEmailVerified: boolean
|
isEmailVerified: boolean
|
||||||
@@ -46,10 +48,15 @@ export interface UserSetting {
|
|||||||
sendEmail: Setting_SendEmail
|
sendEmail: Setting_SendEmail
|
||||||
questionBox: Setting_QuestionBox
|
questionBox: Setting_QuestionBox
|
||||||
enableFunctions: FunctionTypes[]
|
enableFunctions: FunctionTypes[]
|
||||||
|
|
||||||
|
indexTemplate: string | null,
|
||||||
|
songListTemplate: string | null
|
||||||
|
scheduleTemplate: string | null
|
||||||
}
|
}
|
||||||
export enum FunctionTypes {
|
export enum FunctionTypes {
|
||||||
SongList,
|
SongList,
|
||||||
QuestionBox,
|
QuestionBox,
|
||||||
|
Schedule,
|
||||||
}
|
}
|
||||||
export interface SongAuthorInfo {
|
export interface SongAuthorInfo {
|
||||||
name: string
|
name: string
|
||||||
@@ -130,9 +137,10 @@ export interface ScheduleWeekInfo {
|
|||||||
days: ScheduleDayInfo[]
|
days: ScheduleDayInfo[]
|
||||||
}
|
}
|
||||||
export interface ScheduleDayInfo {
|
export interface ScheduleDayInfo {
|
||||||
title: string
|
title: string | null
|
||||||
tag: string
|
tag: string | null
|
||||||
time: number
|
tagColor: string | null
|
||||||
|
time: string | null
|
||||||
}
|
}
|
||||||
export enum ThemeType {
|
export enum ThemeType {
|
||||||
Auto = 'auto',
|
Auto = 'auto',
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useWindowSize } from '@vueuse/core'
|
import { useWindowSize } from '@vueuse/core'
|
||||||
import { NGrid, NList, NListItem, NSpace, NCard, NEmpty, NGridItem } from 'naive-ui'
|
import { NGrid, NList, NListItem, NSpace, NCard, NEmpty, NGridItem, NText, NTime, NButton, NPopconfirm, NEllipsis, NBadge } from 'naive-ui'
|
||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
import { ScheduleWeekInfo } from '@/api/api-models'
|
import { ScheduleWeekInfo } from '@/api/api-models'
|
||||||
import { QueryGetAPI } from '@/api/query'
|
import { QueryGetAPI } from '@/api/query'
|
||||||
@@ -8,24 +8,92 @@ import { SCHEDULE_API_URL } from '@/data/constants'
|
|||||||
|
|
||||||
const { width } = useWindowSize()
|
const { width } = useWindowSize()
|
||||||
|
|
||||||
|
const weekdays = ['周一', '周二', '周三', '周四', '周五', '周六', '周日']
|
||||||
|
function getDateFromWeek(year: number, week: number, dayOfWeek: number): Date {
|
||||||
|
// week starts from 1-52, dayOfWeek starts from 0-6 where 0 is Monday
|
||||||
|
var simple = new Date(year, 0, 1 + (week - 1) * 7);
|
||||||
|
var dow = simple.getDay();
|
||||||
|
var ISOweekStart = simple;
|
||||||
|
if (dow <= 4)
|
||||||
|
ISOweekStart.setDate(simple.getDate() - simple.getDay() + 1);
|
||||||
|
else
|
||||||
|
ISOweekStart.setDate(simple.getDate() + 8 - simple.getDay());
|
||||||
|
return new Date(ISOweekStart.getFullYear(), ISOweekStart.getMonth(), ISOweekStart.getDate() + dayOfWeek);
|
||||||
|
}
|
||||||
|
|
||||||
defineProps<{
|
defineProps<{
|
||||||
schedules: ScheduleWeekInfo[]
|
schedules: ScheduleWeekInfo[],
|
||||||
|
isSelf: boolean
|
||||||
|
}>()
|
||||||
|
const emit = defineEmits<{
|
||||||
|
(e: 'onUpdate', schedule: ScheduleWeekInfo): void
|
||||||
|
(e: 'onDelete', schedule: ScheduleWeekInfo): void
|
||||||
|
(e: 'onCopy', schedule: ScheduleWeekInfo): void
|
||||||
}>()
|
}>()
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<NEmpty v-if="(schedules?.length ?? 0) == 0" />
|
<NEmpty v-if="(schedules?.length ?? 0) == 0" />
|
||||||
<NList>
|
<NList style="padding: 0;" bordered >
|
||||||
<NListItem v-for="item in schedules" v-bind:key="item.year + ' ' + item.week">
|
<NListItem v-for="item in schedules" v-bind:key="item.year + ' ' + item.week" style="padding: 0">
|
||||||
<NGrid x-gap="12" :cols="7">
|
<NCard size="small" :bordered="false">
|
||||||
<NGridItem v-for="(day, index) in item.days" v-bind:key="index">
|
<template #header>
|
||||||
<NCard size="small" style="height: 100px; width: 100%">
|
<NText> {{ item.year }}年第{{ item.week }}周 </NText>
|
||||||
<template #header-extra>
|
</template>
|
||||||
{{ day ? day.tag : '休息' }}
|
<template v-if="isSelf" #header-extra>
|
||||||
</template>
|
<NSpace>
|
||||||
</NCard>
|
<NButton size="small" @click="emit('onUpdate', item)"> 编辑 </NButton>
|
||||||
</NGridItem>
|
<NButton size="small" @click="emit('onCopy', item)"> 复制 </NButton>
|
||||||
</NGrid>
|
<NPopconfirm @positive-click="emit('onDelete', item)">
|
||||||
|
<template #trigger>
|
||||||
|
<NButton size="small" type="error"> 删除 </NButton>
|
||||||
|
</template>
|
||||||
|
确定删除?
|
||||||
|
</NPopconfirm>
|
||||||
|
</NSpace>
|
||||||
|
</template>
|
||||||
|
<NGrid x-gap="8" cols="1 1000:7">
|
||||||
|
<NGridItem v-for="(day, index) in item.days" v-bind:key="index">
|
||||||
|
<NCard
|
||||||
|
size="small"
|
||||||
|
:style="{ height: '65px', backgroundColor: day.tagColor + '1f' }"
|
||||||
|
content-style="padding: 5px;"
|
||||||
|
header-style="padding: 0px 6px 0px 6px;"
|
||||||
|
:embedded="day?.tag != undefined"
|
||||||
|
>
|
||||||
|
<template #header-extra>
|
||||||
|
<template v-if="day.tag">
|
||||||
|
<NSpace :size="5">
|
||||||
|
<NBadge v-if="day.tagColor" dot :color="day.tagColor"/>
|
||||||
|
<NEllipsis>
|
||||||
|
<NText :style="{ color: day.tagColor }">
|
||||||
|
{{ day.tag }}
|
||||||
|
</NText>
|
||||||
|
</NEllipsis>
|
||||||
|
</NSpace>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<NText depth="3" style="font-size: 11px" italic> 休息 </NText>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
<template #header>
|
||||||
|
<NText :depth="3" style="font-size: 12px" strong :italic="!day.tag">
|
||||||
|
<NTime :time="getDateFromWeek(item.year, item.week, index)" format="MM/dd" />
|
||||||
|
{{ weekdays[index] }}
|
||||||
|
{{ day.time }}
|
||||||
|
</NText>
|
||||||
|
</template>
|
||||||
|
<template v-if="day?.title">
|
||||||
|
<NEllipsis>
|
||||||
|
<NText style="font-size: 13px">
|
||||||
|
{{ day.title }}
|
||||||
|
</NText>
|
||||||
|
</NEllipsis>
|
||||||
|
</template>
|
||||||
|
</NCard>
|
||||||
|
</NGridItem>
|
||||||
|
</NGrid>
|
||||||
|
</NCard>
|
||||||
</NListItem>
|
</NListItem>
|
||||||
</NList>
|
</NList>
|
||||||
<NGrid v-if="width > 1000"> </NGrid>
|
<NGrid v-if="width > 1000"> </NGrid>
|
||||||
|
|||||||
@@ -40,6 +40,14 @@ const routes: Array<RouteRecordRaw> = [
|
|||||||
title: '棉花糖 (提问箱',
|
title: '棉花糖 (提问箱',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: 'schedule',
|
||||||
|
name: 'user-schedule',
|
||||||
|
component: () => import('@/views/view/ScheduleView.vue'),
|
||||||
|
meta: {
|
||||||
|
title: '日程',
|
||||||
|
},
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
//管理页面
|
//管理页面
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ import { RouterLink } from 'vue-router'
|
|||||||
import { useElementSize, useStorage } from '@vueuse/core'
|
import { useElementSize, useStorage } from '@vueuse/core'
|
||||||
import { ACCOUNT_API_URL } from '@/data/constants'
|
import { ACCOUNT_API_URL } from '@/data/constants'
|
||||||
import { QueryGetAPI } from '@/api/query'
|
import { QueryGetAPI } from '@/api/query'
|
||||||
import { ThemeType } from '@/api/api-models'
|
import { FunctionTypes, ThemeType } from '@/api/api-models'
|
||||||
import { isDarkMode } from '@/Utils'
|
import { isDarkMode } from '@/Utils'
|
||||||
|
|
||||||
const accountInfo = useAccount()
|
const accountInfo = useAccount()
|
||||||
@@ -71,7 +71,7 @@ const menuOptions = [
|
|||||||
to: {
|
to: {
|
||||||
name: 'manage-schedule',
|
name: 'manage-schedule',
|
||||||
},
|
},
|
||||||
disabled: !accountInfo.value?.isEmailVerified,
|
disabled: !accountInfo.value?.isEmailVerified || (accountInfo.value?.settings?.enableFunctions.indexOf(FunctionTypes.Schedule) ?? -1) == -1,
|
||||||
},
|
},
|
||||||
{ default: () => '日程' }
|
{ default: () => '日程' }
|
||||||
),
|
),
|
||||||
@@ -86,7 +86,7 @@ const menuOptions = [
|
|||||||
to: {
|
to: {
|
||||||
name: 'manage-songList',
|
name: 'manage-songList',
|
||||||
},
|
},
|
||||||
disabled: !accountInfo.value?.isEmailVerified,
|
disabled: !accountInfo.value?.isEmailVerified || (accountInfo.value?.settings?.enableFunctions.indexOf(FunctionTypes.SongList) ?? -1) == -1,
|
||||||
},
|
},
|
||||||
{ default: () => '歌单' }
|
{ default: () => '歌单' }
|
||||||
),
|
),
|
||||||
@@ -101,6 +101,7 @@ const menuOptions = [
|
|||||||
to: {
|
to: {
|
||||||
name: 'manage-questionBox',
|
name: 'manage-questionBox',
|
||||||
},
|
},
|
||||||
|
disabled: !accountInfo.value?.isEmailVerified || (accountInfo.value?.settings?.enableFunctions.indexOf(FunctionTypes.QuestionBox) ?? -1) == -1,
|
||||||
},
|
},
|
||||||
{ default: () => '棉花糖 (提问箱' }
|
{ default: () => '棉花糖 (提问箱' }
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -1,23 +1,6 @@
|
|||||||
<!-- 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 {
|
import { NAvatar, NIcon, NLayout, NLayoutHeader, NLayoutSider, NMenu, NSpace, NText, NButton, NResult, NPageHeader, NSwitch, NModal, NEllipsis, MenuOption, NSpin } from 'naive-ui'
|
||||||
NAvatar,
|
|
||||||
NIcon,
|
|
||||||
NLayout,
|
|
||||||
NLayoutHeader,
|
|
||||||
NLayoutSider,
|
|
||||||
NMenu,
|
|
||||||
NSpace,
|
|
||||||
NText,
|
|
||||||
NButton,
|
|
||||||
NResult,
|
|
||||||
NPageHeader,
|
|
||||||
NSwitch,
|
|
||||||
NModal,
|
|
||||||
NEllipsis,
|
|
||||||
MenuOption,
|
|
||||||
NSpin,
|
|
||||||
} from 'naive-ui'
|
|
||||||
import { computed, h, onMounted, ref } from 'vue'
|
import { computed, h, onMounted, ref } from 'vue'
|
||||||
import { BookOutline as BookIcon, Chatbox, Home, Moon, MusicalNote, PersonOutline as PersonIcon, Sunny, 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'
|
||||||
@@ -28,12 +11,13 @@ import { useAccount } from '@/api/account'
|
|||||||
import RegisterAndLogin from '@/components/RegisterAndLogin.vue'
|
import RegisterAndLogin from '@/components/RegisterAndLogin.vue'
|
||||||
import { useElementSize, useStorage } from '@vueuse/core'
|
import { useElementSize, useStorage } from '@vueuse/core'
|
||||||
import { isDarkMode } from '@/Utils'
|
import { isDarkMode } from '@/Utils'
|
||||||
|
import { CalendarClock24Filled } from '@vicons/fluent'
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const id = computed(() => {
|
const id = computed(() => {
|
||||||
return route.params.id
|
return route.params.id
|
||||||
})
|
})
|
||||||
const themeType = useStorage('Settings.Theme', ThemeType.Auto);
|
const themeType = useStorage('Settings.Theme', ThemeType.Auto)
|
||||||
|
|
||||||
const userInfo = ref<UserInfo>()
|
const userInfo = ref<UserInfo>()
|
||||||
const biliUserInfo = ref()
|
const biliUserInfo = ref()
|
||||||
@@ -90,10 +74,25 @@ onMounted(async () => {
|
|||||||
},
|
},
|
||||||
{ default: () => '歌单' }
|
{ default: () => '歌单' }
|
||||||
),
|
),
|
||||||
show: (userInfo.value?.enableFunctions.indexOf(FunctionTypes.SongList) ?? -1) > -1,
|
show: (userInfo.value?.extra?.enableFunctions.indexOf(FunctionTypes.SongList) ?? -1) > -1,
|
||||||
key: 'user-songList',
|
key: 'user-songList',
|
||||||
icon: renderIcon(MusicalNote),
|
icon: renderIcon(MusicalNote),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
label: () =>
|
||||||
|
h(
|
||||||
|
RouterLink,
|
||||||
|
{
|
||||||
|
to: {
|
||||||
|
name: 'user-schedule',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ default: () => '日程' }
|
||||||
|
),
|
||||||
|
show: (userInfo.value?.extra?.enableFunctions.indexOf(FunctionTypes.Schedule) ?? -1) > -1,
|
||||||
|
key: 'user-schedule',
|
||||||
|
icon: renderIcon(CalendarClock24Filled),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
label: () =>
|
label: () =>
|
||||||
h(
|
h(
|
||||||
@@ -105,7 +104,7 @@ onMounted(async () => {
|
|||||||
},
|
},
|
||||||
{ default: () => '棉花糖 (提问箱' }
|
{ default: () => '棉花糖 (提问箱' }
|
||||||
),
|
),
|
||||||
show: (userInfo.value?.enableFunctions.indexOf(FunctionTypes.QuestionBox) ?? -1) > -1,
|
show: (userInfo.value?.extra?.enableFunctions.indexOf(FunctionTypes.QuestionBox) ?? -1) > -1,
|
||||||
key: 'user-questionBox',
|
key: 'user-questionBox',
|
||||||
icon: renderIcon(Chatbox),
|
icon: renderIcon(Chatbox),
|
||||||
},
|
},
|
||||||
@@ -127,7 +126,7 @@ onMounted(async () => {
|
|||||||
<NIcon :component="Sunny" />
|
<NIcon :component="Sunny" />
|
||||||
</template>
|
</template>
|
||||||
<template #unchecked>
|
<template #unchecked>
|
||||||
<NIcon :component="Moon"/>
|
<NIcon :component="Moon" />
|
||||||
</template>
|
</template>
|
||||||
</NSwitch>
|
</NSwitch>
|
||||||
<template v-if="accountInfo">
|
<template v-if="accountInfo">
|
||||||
@@ -160,14 +159,17 @@ onMounted(async () => {
|
|||||||
<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" :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;'}`">
|
<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-if="userInfo" v-slot="{ Component }">
|
<RouterView v-if="userInfo" v-slot="{ Component }">
|
||||||
<KeepAlive>
|
<KeepAlive>
|
||||||
<component :is="Component" :bili-info="biliUserInfo" :user-info="userInfo" />
|
<component :is="Component" :bili-info="biliUserInfo" :user-info="userInfo" />
|
||||||
</KeepAlive>
|
</KeepAlive>
|
||||||
</RouterView>
|
</RouterView>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<NSpin show/>
|
<NSpin show />
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</NLayout>
|
</NLayout>
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ const accountInfo = useAccount()
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<NSpace justify="center" align="center" vertical>
|
<NSpace justify="center" align="center" vertical style="width: 100%;">
|
||||||
<NCard embedded style="max-width: 90%;width: 800px;">
|
<NCard embedded style="max-width: 90%;width: 800px;">
|
||||||
<NSpace align="center" justify="center" vertical>
|
<NSpace align="center" justify="center" vertical>
|
||||||
<NText style="font-size: 3rem">
|
<NText style="font-size: 3rem">
|
||||||
@@ -32,7 +32,7 @@ const accountInfo = useAccount()
|
|||||||
</NAlert>
|
</NAlert>
|
||||||
<NAlert>
|
<NAlert>
|
||||||
Bilibili 账户:
|
Bilibili 账户:
|
||||||
<NTag v-if="accountInfo?.isBiliVerified" type="success"> 已认证 </NTag>
|
<NTag v-if="accountInfo?.isBiliVerified" type="success"> 已认证 | {{ accountInfo?.biliId }} </NTag>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<NTag type="error" size="small"> 未认证 </NTag>
|
<NTag type="error" size="small"> 未认证 </NTag>
|
||||||
<NDivider vertical />
|
<NDivider vertical />
|
||||||
@@ -40,7 +40,7 @@ const accountInfo = useAccount()
|
|||||||
</template>
|
</template>
|
||||||
</NAlert>
|
</NAlert>
|
||||||
</NCard>
|
</NCard>
|
||||||
<div style="max-width: 90%;width: 800px;">
|
<div style="width: 100%;">
|
||||||
<NDivider/>
|
<NDivider/>
|
||||||
<SettingsManageView />
|
<SettingsManageView />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
import { useAccount } from '@/api/account'
|
import { useAccount } from '@/api/account'
|
||||||
import { QueryGetAPI } from '@/api/query'
|
import { QueryGetAPI } from '@/api/query'
|
||||||
import { HISTORY_API_URL } from '@/data/constants'
|
import { HISTORY_API_URL } from '@/data/constants'
|
||||||
import { NCard, useMessage } from 'naive-ui'
|
import { NCard, NSpace, useMessage } from 'naive-ui'
|
||||||
import { onMounted, ref } from 'vue'
|
import { onMounted, ref } from 'vue'
|
||||||
import { use } from 'echarts/core'
|
import { use } from 'echarts/core'
|
||||||
import { CanvasRenderer } from 'echarts/renderers'
|
import { CanvasRenderer } from 'echarts/renderers'
|
||||||
@@ -409,9 +409,11 @@ onMounted(async () => {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<NCard size="small">
|
<NCard size="small">
|
||||||
<VChart :option="fansOption" style="height: 200px" />
|
<NSpace vertical>
|
||||||
<VChart :option="guardsOption" style="height: 200px" />
|
<VChart :option="fansOption" style="height: 200px" />
|
||||||
<VChart :option="upstatViewOption" style="height: 200px" />
|
<VChart :option="guardsOption" style="height: 200px" />
|
||||||
<VChart :option="upstatLikeOption" style="height: 200px" />
|
<VChart :option="upstatViewOption" style="height: 200px" />
|
||||||
|
<VChart :option="upstatLikeOption" style="height: 200px" />
|
||||||
|
</NSpace>
|
||||||
</NCard>
|
</NCard>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -5,9 +5,28 @@ import { QueryGetAPI, QueryPostAPI } from '@/api/query'
|
|||||||
import ScheduleList from '@/components/ScheduleList.vue'
|
import ScheduleList from '@/components/ScheduleList.vue'
|
||||||
import { SCHEDULE_API_URL } from '@/data/constants'
|
import { SCHEDULE_API_URL } from '@/data/constants'
|
||||||
import { addWeeks, endOfWeek, endOfYear, format, isBefore, startOfWeek, startOfYear } from 'date-fns'
|
import { addWeeks, endOfWeek, endOfYear, format, isBefore, startOfWeek, startOfYear } from 'date-fns'
|
||||||
import { NButton, NDivider, NForm, NFormItem, NModal, NSelect, NTabPane, NTabs, useMessage } from 'naive-ui'
|
import {
|
||||||
import { SelectBaseOption, SelectMixedOption } from 'naive-ui/es/select/src/interface'
|
NAlert,
|
||||||
import { computed, onMounted, ref } from 'vue'
|
NBadge,
|
||||||
|
NButton,
|
||||||
|
NColorPicker,
|
||||||
|
NDivider,
|
||||||
|
NForm,
|
||||||
|
NFormItem,
|
||||||
|
NInput,
|
||||||
|
NInputGroup,
|
||||||
|
NInputGroupLabel,
|
||||||
|
NModal,
|
||||||
|
NSelect,
|
||||||
|
NSpace,
|
||||||
|
NTabPane,
|
||||||
|
NTabs,
|
||||||
|
NText,
|
||||||
|
NTimePicker,
|
||||||
|
useMessage,
|
||||||
|
} from 'naive-ui'
|
||||||
|
import { SelectMixedOption, SelectOption } from 'naive-ui/es/select/src/interface'
|
||||||
|
import { VNode, computed, h, onMounted, ref } from 'vue'
|
||||||
|
|
||||||
const rules = {
|
const rules = {
|
||||||
user: {
|
user: {
|
||||||
@@ -28,7 +47,7 @@ const rules = {
|
|||||||
trigger: ['input'],
|
trigger: ['input'],
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
const weekdays = ['周一', '周二', '周三', '周四', '周五', '周六', '周日']
|
||||||
const yearOptions = [
|
const yearOptions = [
|
||||||
{
|
{
|
||||||
label: new Date().getFullYear().toString(),
|
label: new Date().getFullYear().toString(),
|
||||||
@@ -41,16 +60,45 @@ const yearOptions = [
|
|||||||
] as SelectMixedOption[]
|
] as SelectMixedOption[]
|
||||||
const weekOptions = computed(() => {
|
const weekOptions = computed(() => {
|
||||||
let weeks = [] as SelectMixedOption[]
|
let weeks = [] as SelectMixedOption[]
|
||||||
const all = getAllWeeks(addScheduleYear.value.value as number)
|
const all = getAllWeeks(selectedScheduleYear.value)
|
||||||
all.forEach((week) => {
|
all.forEach((week) => {
|
||||||
|
const isExist = (schedules.value?.findIndex((s) => s.year == selectedScheduleYear.value && s.week == week[0] + 1) ?? -1) > -1
|
||||||
weeks.push({
|
weeks.push({
|
||||||
label: `第${week[0] + 1}周 (${week[1]})`,
|
label: `${isExist ? '(已安排)' : ''} 第${week[0] + 1}周 (${week[1]})`,
|
||||||
value: week[0] + 1,
|
value: week[0] + 1,
|
||||||
disabled: (schedules.value?.findIndex((s) => s.year == addScheduleYear.value.value && s.week == week[0] + 1) ?? -1) > -1,
|
disabled: isExist,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
return weeks
|
return weeks
|
||||||
})
|
})
|
||||||
|
const dayOptions = computed(() => {
|
||||||
|
let days = [] as SelectMixedOption[]
|
||||||
|
for (let i = 0; i < 7; i++) {
|
||||||
|
try {
|
||||||
|
days.push({
|
||||||
|
label: updateScheduleModel.value?.days[i].tag ? weekdays[i] + ' (已安排)' : weekdays[i],
|
||||||
|
value: i,
|
||||||
|
})
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return days
|
||||||
|
})
|
||||||
|
const existTagOptions = computed(() => {
|
||||||
|
let colors = [] as SelectMixedOption[]
|
||||||
|
schedules.value?.forEach((s) => {
|
||||||
|
s.days.forEach((d) => {
|
||||||
|
if (d.tag && !colors.find((c) => c.value == d.tagColor && c.label == d.tag)) {
|
||||||
|
colors.push({
|
||||||
|
label: d.tag,
|
||||||
|
value: d.tagColor ?? '',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
return colors
|
||||||
|
})
|
||||||
function getAllWeeks(year: number) {
|
function getAllWeeks(year: number) {
|
||||||
const startDate = startOfYear(new Date(year, 0, 1))
|
const startDate = startOfYear(new Date(year, 0, 1))
|
||||||
const endDate = endOfYear(new Date(year, 11, 31))
|
const endDate = endOfYear(new Date(year, 11, 31))
|
||||||
@@ -61,8 +109,8 @@ function getAllWeeks(year: number) {
|
|||||||
let index = 0
|
let index = 0
|
||||||
|
|
||||||
while (isBefore(date, endDate)) {
|
while (isBefore(date, endDate)) {
|
||||||
const weekStart = format(date, 'MM-dd')
|
const weekStart = format(date, 'MM/dd')
|
||||||
const weekEnd = format(endOfWeek(date, { weekStartsOn: 1 }), 'MM-dd')
|
const weekEnd = format(endOfWeek(date, { weekStartsOn: 1 }), 'MM/dd')
|
||||||
|
|
||||||
weeks.push([index, `${weekStart} - ${weekEnd}`])
|
weeks.push([index, `${weekStart} - ${weekEnd}`])
|
||||||
|
|
||||||
@@ -78,10 +126,13 @@ const message = useMessage()
|
|||||||
|
|
||||||
const showUpdateModal = ref(false)
|
const showUpdateModal = ref(false)
|
||||||
const showAddModal = ref(false)
|
const showAddModal = ref(false)
|
||||||
|
const showCopyModal = ref(false)
|
||||||
const updateScheduleModel = ref<ScheduleWeekInfo>({} as ScheduleWeekInfo)
|
const updateScheduleModel = ref<ScheduleWeekInfo>({} as ScheduleWeekInfo)
|
||||||
|
const selectedExistTag = ref()
|
||||||
|
|
||||||
const addScheduleYear = ref(yearOptions[0])
|
const selectedDay = ref(0)
|
||||||
const addScheduleWeek = ref()
|
const selectedScheduleYear = ref(new Date().getFullYear())
|
||||||
|
const selectedScheduleWeek = ref(Number(format(Date.now(), 'w')) + 1)
|
||||||
|
|
||||||
async function get() {
|
async function get() {
|
||||||
await QueryGetAPI<ScheduleWeekInfo[]>(SCHEDULE_API_URL + 'get', {
|
await QueryGetAPI<ScheduleWeekInfo[]>(SCHEDULE_API_URL + 'get', {
|
||||||
@@ -90,7 +141,6 @@ async function get() {
|
|||||||
.then((data) => {
|
.then((data) => {
|
||||||
if (data.code == 200) {
|
if (data.code == 200) {
|
||||||
schedules.value = data.data
|
schedules.value = data.data
|
||||||
console.log(data.data)
|
|
||||||
} else {
|
} else {
|
||||||
message.error('加载失败: ' + data.message)
|
message.error('加载失败: ' + data.message)
|
||||||
}
|
}
|
||||||
@@ -100,53 +150,86 @@ async function get() {
|
|||||||
message.error('加载失败')
|
message.error('加载失败')
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
const isAdding = ref(false)
|
const isLoading = ref(false)
|
||||||
async function addSchedule() {
|
async function addSchedule() {
|
||||||
isAdding.value = true
|
isLoading.value = true
|
||||||
await QueryPostAPI(SCHEDULE_API_URL + 'update', {
|
await QueryPostAPI(SCHEDULE_API_URL + 'update', {
|
||||||
year: addScheduleYear.value.value,
|
year: selectedScheduleYear.value,
|
||||||
week: addScheduleWeek.value,
|
week: selectedScheduleWeek.value,
|
||||||
})
|
})
|
||||||
.then((data) => {
|
.then((data) => {
|
||||||
if (data.code == 200) {
|
if (data.code == 200) {
|
||||||
message.success('添加成功')
|
message.success('添加成功')
|
||||||
const model = {
|
|
||||||
year: addScheduleYear.value.value as number,
|
|
||||||
week: addScheduleWeek.value.value as number,
|
|
||||||
days: new Array(7) as ScheduleDayInfo[],
|
|
||||||
} as ScheduleWeekInfo
|
|
||||||
schedules.value?.push(model)
|
|
||||||
showAddModal.value = false
|
showAddModal.value = false
|
||||||
updateScheduleModel.value = model
|
get()
|
||||||
showUpdateModal.value = true
|
} else {
|
||||||
|
message.error('添加失败: ' + data.message)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
isAdding.value = false
|
isLoading.value = false
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
async function onUpdateSchedule(schedule: ScheduleWeekInfo) {
|
async function onCopySchedule() {
|
||||||
updateScheduleModel.value = schedule
|
if (schedules.value?.find((s) => s.year == selectedScheduleYear.value && s.week == selectedScheduleWeek.value)) {
|
||||||
|
message.error('想要复制到的周已存在')
|
||||||
|
} else {
|
||||||
|
updateScheduleModel.value.year = selectedScheduleYear.value
|
||||||
|
updateScheduleModel.value.week = selectedScheduleWeek.value
|
||||||
|
await onUpdateSchedule()
|
||||||
|
showCopyModal.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async function onUpdateSchedule() {
|
||||||
await QueryPostAPI(SCHEDULE_API_URL + 'update', {
|
await QueryPostAPI(SCHEDULE_API_URL + 'update', {
|
||||||
year: addScheduleYear.value.value,
|
year: updateScheduleModel.value.year,
|
||||||
week: addScheduleWeek.value,
|
week: updateScheduleModel.value.week,
|
||||||
})
|
day: selectedDay.value,
|
||||||
.then((data) => {
|
days: updateScheduleModel.value?.days,
|
||||||
if (data.code == 200) {
|
}).then((data) => {
|
||||||
message.success('添加成功')
|
if (data.code == 200) {
|
||||||
const model = {
|
message.success('成功')
|
||||||
year: addScheduleYear.value.value as number,
|
const s = schedules.value?.find((s) => s.year == selectedScheduleYear.value && s.week == selectedScheduleWeek.value)
|
||||||
week: addScheduleWeek.value.value as number,
|
if (s) {
|
||||||
days: new Array(7) as ScheduleDayInfo[],
|
s.days[selectedDay.value] = updateScheduleModel.value.days[selectedDay.value]
|
||||||
} as ScheduleWeekInfo
|
} else {
|
||||||
showAddModal.value = false
|
schedules.value?.push(updateScheduleModel.value)
|
||||||
showUpdateModal.value = true
|
|
||||||
}
|
}
|
||||||
})
|
//updateScheduleModel.value = {} as ScheduleWeekInfo
|
||||||
.finally(() => {
|
} else {
|
||||||
isAdding.value = false
|
message.error('修改失败: ' + data.message)
|
||||||
})
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
async function onDeleteSchedule(schedule: ScheduleWeekInfo) {
|
||||||
|
await QueryGetAPI(SCHEDULE_API_URL + 'del', {
|
||||||
|
year: schedule.year,
|
||||||
|
week: schedule.week,
|
||||||
|
}).then((data) => {
|
||||||
|
if (data.code == 200) {
|
||||||
|
message.success('已删除')
|
||||||
|
get()
|
||||||
|
} else {
|
||||||
|
message.error('删除失败: ' + data.message)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
function onOpenUpdateModal(schedule: ScheduleWeekInfo) {
|
||||||
|
updateScheduleModel.value = JSON.parse(JSON.stringify(schedule))
|
||||||
|
showUpdateModal.value = true
|
||||||
|
}
|
||||||
|
function onOpenCopyModal(schedule: ScheduleWeekInfo) {
|
||||||
|
updateScheduleModel.value = JSON.parse(JSON.stringify(schedule))
|
||||||
|
showCopyModal.value = true
|
||||||
|
}
|
||||||
|
function onSelectChange(value: string | null, option: SelectMixedOption) {
|
||||||
|
if (value) {
|
||||||
|
updateScheduleModel.value.days[selectedDay.value].tagColor = value
|
||||||
|
updateScheduleModel.value.days[selectedDay.value].tag = option.label as string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const renderOption = ({ node, option }: { node: VNode; option: SelectOption }) =>
|
||||||
|
h(NSpace, { align: 'center', size: 3, style: 'margin-left: 5px' }, () => [option.value ? h(NBadge, { dot: true, color: option.value?.toString() }) : null, node])
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
get()
|
get()
|
||||||
@@ -154,17 +237,65 @@ onMounted(() => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<NButton @click="showAddModal = true"> 添加日程 </NButton>
|
<NButton @click="showAddModal = true"> 添加周程 </NButton>
|
||||||
|
<NDivider />
|
||||||
<NModal v-model:show="showAddModal" style="width: 600px; max-width: 90vw" preset="card" title="添加周程">
|
<NModal v-model:show="showAddModal" style="width: 600px; max-width: 90vw" preset="card" title="添加周程">
|
||||||
<NSelect :options="yearOptions" />
|
<NSpace vertical>
|
||||||
<NSelect :options="weekOptions" />
|
年份
|
||||||
|
<NSelect :options="yearOptions" v-model:value="selectedScheduleYear" />
|
||||||
|
第几周
|
||||||
|
<NSelect :options="weekOptions" v-model:value="selectedScheduleWeek" />
|
||||||
|
</NSpace>
|
||||||
<NDivider />
|
<NDivider />
|
||||||
<NButton @click="addSchedule" :loading="isAdding"> 添加 </NButton>
|
<NButton @click="addSchedule" :loading="isLoading"> 添加 </NButton>
|
||||||
</NModal>
|
</NModal>
|
||||||
<NModal v-model:show="showUpdateModal">
|
<NModal v-model:show="showCopyModal" style="width: 600px; max-width: 90vw" preset="card" title="复制周程">
|
||||||
<NTabs>
|
<NAlert type="info"> 复制为 </NAlert>
|
||||||
<NTabPane v-for="(day, index) in updateScheduleModel.days" v-bind:key="day.time" :name="index" :tab="index.toString()"> </NTabPane>
|
<NSpace vertical>
|
||||||
</NTabs>
|
年份
|
||||||
|
<NSelect :options="yearOptions" v-model:value="selectedScheduleYear" />
|
||||||
|
第几周
|
||||||
|
<NSelect :options="weekOptions" v-model:value="selectedScheduleWeek" />
|
||||||
|
</NSpace>
|
||||||
|
<NDivider />
|
||||||
|
<NButton @click="onCopySchedule" :loading="isLoading"> 复制 </NButton>
|
||||||
</NModal>
|
</NModal>
|
||||||
<ScheduleList :schedules="schedules ?? []" />
|
<NModal v-model:show="showUpdateModal" style="width: 600px; max-width: 90vw" preset="card" title="编辑周程">
|
||||||
|
<NSelect :options="dayOptions" v-model:value="selectedDay" />
|
||||||
|
<NDivider />
|
||||||
|
<template v-if="updateScheduleModel">
|
||||||
|
<NSpace vertical>
|
||||||
|
<NSpace>
|
||||||
|
<NInputGroup>
|
||||||
|
<NInputGroupLabel type="primary"> 标签 </NInputGroupLabel>
|
||||||
|
<NInput v-model:value="updateScheduleModel.days[selectedDay].tag" placeholder="标签 | 留空视为无安排" style="max-width: 300px" maxlength="10" show-count />
|
||||||
|
</NInputGroup>
|
||||||
|
<NSelect
|
||||||
|
v-model:value="selectedExistTag"
|
||||||
|
@update:value="onSelectChange"
|
||||||
|
:options="existTagOptions"
|
||||||
|
filterable
|
||||||
|
clearable
|
||||||
|
placeholder="使用过的标签"
|
||||||
|
style="max-width: 150px"
|
||||||
|
:render-option="renderOption"
|
||||||
|
/>
|
||||||
|
</NSpace>
|
||||||
|
<NInputGroup>
|
||||||
|
<NInputGroupLabel> 内容 </NInputGroupLabel>
|
||||||
|
<NInput v-model:value="updateScheduleModel.days[selectedDay].title" placeholder="内容" style="max-width: 200px" maxlength="30" show-count />
|
||||||
|
</NInputGroup>
|
||||||
|
<NTimePicker default-formatted-value="20:00" v-model:formatted-value="updateScheduleModel.days[selectedDay].time" format="HH:mm" />
|
||||||
|
<NColorPicker
|
||||||
|
v-model:value="updateScheduleModel.days[selectedDay].tagColor"
|
||||||
|
:swatches="['#FFFFFF', '#18A058', '#2080F0', '#F0A020', 'rgba(208, 48, 80, 1)']"
|
||||||
|
default-value="#61B589"
|
||||||
|
:show-alpha="false"
|
||||||
|
:modes="['hex']"
|
||||||
|
/>
|
||||||
|
<NButton @click="onUpdateSchedule()"> 保存 </NButton>
|
||||||
|
</NSpace>
|
||||||
|
</template>
|
||||||
|
</NModal>
|
||||||
|
<ScheduleList :schedules="schedules ?? []" @on-update="onOpenUpdateModal" @on-delete="onDeleteSchedule" @on-copy="onOpenCopyModal" is-self />
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -1,36 +1,197 @@
|
|||||||
<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, NSpace, NSwitch, useMessage } from 'naive-ui'
|
import { NButton, NCard, NCheckbox, NCheckboxGroup, NDivider, NForm, NSelect, NSpace, NSwitch, NTabPane, NTabs, SelectOption, useMessage } from 'naive-ui'
|
||||||
import { ref } from 'vue'
|
import { computed, onMounted, ref } from 'vue'
|
||||||
import { useRequest } from 'vue-request'
|
import { useRequest } from 'vue-request'
|
||||||
import { FunctionTypes } from '@/api/api-models'
|
import { FunctionTypes, ScheduleWeekInfo, SongFrom, SongLanguage, SongsInfo } from '@/api/api-models'
|
||||||
import { QueryPostAPI } from '@/api/query'
|
import { QueryPostAPI } from '@/api/query'
|
||||||
import { ACCOUNT_API_URL } from '@/data/constants'
|
import { ACCOUNT_API_URL, FETCH_API } from '@/data/constants'
|
||||||
|
import ScheduleView from '../view/ScheduleView.vue'
|
||||||
|
import UserIndexView from '../view/UserIndexView.vue'
|
||||||
|
import SongListView from '../view/SongListView.vue'
|
||||||
|
|
||||||
const account = useAccount()
|
const accountInfo = useAccount()
|
||||||
const message = useMessage()
|
const message = useMessage()
|
||||||
|
|
||||||
|
const templateOptions = [
|
||||||
|
{
|
||||||
|
label: '主页',
|
||||||
|
value: 'index',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '歌单',
|
||||||
|
value: 'songlist',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '日程表',
|
||||||
|
value: 'schedule',
|
||||||
|
},
|
||||||
|
] as SelectOption[]
|
||||||
|
const fakeSongList = [
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
key: 'song1',
|
||||||
|
name: '歌曲1',
|
||||||
|
author: ['作者1'],
|
||||||
|
url: 'https://example.com/song1.mp3',
|
||||||
|
from: SongFrom.Custom,
|
||||||
|
language: [SongLanguage.Chinese],
|
||||||
|
createTime: Date.now(),
|
||||||
|
updateTime: Date.now(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
key: 'song2',
|
||||||
|
name: '歌曲2',
|
||||||
|
author: ['作者2'],
|
||||||
|
url: 'https://example.com/song2.mp3',
|
||||||
|
from: SongFrom.Custom,
|
||||||
|
language: [SongLanguage.Chinese],
|
||||||
|
createTime: Date.now(),
|
||||||
|
updateTime: Date.now(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 3,
|
||||||
|
key: 'song3',
|
||||||
|
name: '歌曲3',
|
||||||
|
author: ['作者3'],
|
||||||
|
url: 'https://example.com/song3.mp3',
|
||||||
|
from: SongFrom.Custom,
|
||||||
|
language: [SongLanguage.Chinese],
|
||||||
|
createTime: Date.now(),
|
||||||
|
updateTime: Date.now(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 4,
|
||||||
|
key: 'song4',
|
||||||
|
name: '歌曲4',
|
||||||
|
author: ['作者4'],
|
||||||
|
url: 'https://example.com/song4.mp3',
|
||||||
|
from: SongFrom.Custom,
|
||||||
|
language: [SongLanguage.Chinese],
|
||||||
|
createTime: Date.now(),
|
||||||
|
updateTime: Date.now(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 5,
|
||||||
|
key: 'song5',
|
||||||
|
name: '歌曲5',
|
||||||
|
author: ['作者5'],
|
||||||
|
url: 'https://example.com/song5.mp3',
|
||||||
|
from: SongFrom.Custom,
|
||||||
|
language: [SongLanguage.Chinese],
|
||||||
|
createTime: Date.now(),
|
||||||
|
updateTime: Date.now(),
|
||||||
|
},
|
||||||
|
] as SongsInfo[]
|
||||||
|
const fakeSchedule = [
|
||||||
|
{
|
||||||
|
year: 2023,
|
||||||
|
week: 30,
|
||||||
|
days: [
|
||||||
|
{
|
||||||
|
title: '唱唱歌!',
|
||||||
|
tag: '歌回',
|
||||||
|
tagColor: '#61B589',
|
||||||
|
time: '10:00',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '玩点游戏',
|
||||||
|
tag: '游戏',
|
||||||
|
tagColor: '#A36565',
|
||||||
|
time: '20:00',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Title 3',
|
||||||
|
tag: 'Tag 3',
|
||||||
|
tagColor: '#7BCDEF',
|
||||||
|
time: '12:00 PM',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: null,
|
||||||
|
tag: null,
|
||||||
|
tagColor: null,
|
||||||
|
time: null,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: null,
|
||||||
|
tag: null,
|
||||||
|
tagColor: null,
|
||||||
|
time: null,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: null,
|
||||||
|
tag: null,
|
||||||
|
tagColor: null,
|
||||||
|
time: null,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: null,
|
||||||
|
tag: null,
|
||||||
|
tagColor: null,
|
||||||
|
time: null,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
] as ScheduleWeekInfo[]
|
||||||
|
const selectedOption = ref(templateOptions[0].value)
|
||||||
|
|
||||||
|
const scheduleTemplateOptions = [
|
||||||
|
{
|
||||||
|
label: '默认',
|
||||||
|
value: '',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '粉粉',
|
||||||
|
value: 'PinkySchedule',
|
||||||
|
},
|
||||||
|
] as SelectOption[]
|
||||||
|
const selectedScheduleTemplate = ref(accountInfo.value?.settings.scheduleTemplate ?? scheduleTemplateOptions[0].value?.toString())
|
||||||
|
const songListTemplateOptions = [
|
||||||
|
{
|
||||||
|
label: '默认',
|
||||||
|
value: '',
|
||||||
|
},
|
||||||
|
] as SelectOption[]
|
||||||
|
const selectedSongListTemplate = ref(accountInfo.value?.settings.songListTemplate ?? songListTemplateOptions[0].value)
|
||||||
|
|
||||||
|
const biliUserInfo = ref()
|
||||||
|
|
||||||
function UpdateEnableFunction(func: FunctionTypes, enable: boolean) {
|
function UpdateEnableFunction(func: FunctionTypes, enable: boolean) {
|
||||||
if (account.value) {
|
if (accountInfo.value) {
|
||||||
if (enable) {
|
if (enable) {
|
||||||
//从account.value?.settings.enableFunctions中移除指定的func
|
//从account.value?.settings.enableFunctions中移除指定的func
|
||||||
account.value.settings.enableFunctions = account.value?.settings.enableFunctions.filter((f) => f != func)
|
accountInfo.value.settings.enableFunctions = accountInfo.value?.settings.enableFunctions.filter((f) => f != func)
|
||||||
} else {
|
} else {
|
||||||
account.value.settings.enableFunctions.push(func)
|
accountInfo.value.settings.enableFunctions.push(func)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
async function RequestBiliUserData() {
|
||||||
|
await fetch(FETCH_API + `https://account.bilibili.com/api/member/getCardByMid?mid=10021741`)
|
||||||
|
.then(async (respone) => {
|
||||||
|
let data = await respone.json()
|
||||||
|
if (data.code == 0) {
|
||||||
|
biliUserInfo.value = data.card
|
||||||
|
} else {
|
||||||
|
throw new Error('Bili User API Error: ' + data.message)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.error(err)
|
||||||
|
})
|
||||||
|
}
|
||||||
async function SaveComboGroupSetting(value: (string | number)[], meta: { actionType: 'check' | 'uncheck'; value: string | number }) {
|
async function SaveComboGroupSetting(value: (string | number)[], meta: { actionType: 'check' | 'uncheck'; value: string | number }) {
|
||||||
if (account.value) {
|
if (accountInfo.value) {
|
||||||
//UpdateEnableFunction(meta.value as FunctionTypes, meta.actionType == 'check')
|
//UpdateEnableFunction(meta.value as FunctionTypes, meta.actionType == 'check')
|
||||||
await QueryPostAPI(ACCOUNT_API_URL + 'update-setting', account.value?.settings)
|
await QueryPostAPI(ACCOUNT_API_URL + 'update-setting', accountInfo.value?.settings)
|
||||||
.then((data) => {
|
.then((data) => {
|
||||||
if (data.code == 200) {
|
if (data.code == 200) {
|
||||||
//message.success('保存成功')
|
//message.success('保存成功')
|
||||||
} else {
|
} else {
|
||||||
message.error('修改失败')
|
message.error('修改失败')
|
||||||
if (account.value) {
|
if (accountInfo.value) {
|
||||||
account.value.settings.enableFunctions = account.value.settings.enableFunctions.filter((f) => f != (meta.value as FunctionTypes))
|
accountInfo.value.settings.enableFunctions = accountInfo.value.settings.enableFunctions.filter((f) => f != (meta.value as FunctionTypes))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -40,10 +201,10 @@ async function SaveComboGroupSetting(value: (string | number)[], meta: { actionT
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
async function SaveComboSetting(value :boolean) {
|
async function SaveComboSetting(value: boolean) {
|
||||||
if (account.value) {
|
if (accountInfo.value) {
|
||||||
//UpdateEnableFunction(meta.value as FunctionTypes, meta.actionType == 'check')
|
//UpdateEnableFunction(meta.value as FunctionTypes, meta.actionType == 'check')
|
||||||
await QueryPostAPI(ACCOUNT_API_URL + 'update-setting', account.value?.settings)
|
await QueryPostAPI(ACCOUNT_API_URL + 'update-setting', accountInfo.value?.settings)
|
||||||
.then((data) => {
|
.then((data) => {
|
||||||
if (data.code == 200) {
|
if (data.code == 200) {
|
||||||
//message.success('保存成功')
|
//message.success('保存成功')
|
||||||
@@ -57,23 +218,61 @@ async function SaveComboSetting(value :boolean) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
const componentType = computed(() => {
|
||||||
|
switch (selectedOption.value) {
|
||||||
|
case 'index':
|
||||||
|
return UserIndexView
|
||||||
|
case 'songlist':
|
||||||
|
return SongListView
|
||||||
|
case 'schedule':
|
||||||
|
return ScheduleView
|
||||||
|
default:
|
||||||
|
return UserIndexView
|
||||||
|
}
|
||||||
|
})
|
||||||
|
onMounted(() => {
|
||||||
|
RequestBiliUserData()
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<NCard v-if="account" title="设置">
|
<NCard v-if="accountInfo" title="设置" style="min-height: 800px">
|
||||||
<NDivider style="margin: 0"> 启用功能 </NDivider>
|
<NTabs>
|
||||||
<NCheckboxGroup v-model:value="account.settings.enableFunctions" @update:value="SaveComboGroupSetting">
|
<NTabPane tab="常规" name="general">
|
||||||
<NCheckbox :value="FunctionTypes.SongList"> 歌单 </NCheckbox>
|
<NDivider style="margin: 0"> 启用功能 </NDivider>
|
||||||
<NCheckbox :value="FunctionTypes.QuestionBox"> 提问箱(棉花糖 </NCheckbox>
|
<NCheckboxGroup v-model:value="accountInfo.settings.enableFunctions" @update:value="SaveComboGroupSetting">
|
||||||
</NCheckboxGroup>
|
<NCheckbox :value="FunctionTypes.SongList"> 歌单 </NCheckbox>
|
||||||
<NDivider > 通知 </NDivider>
|
<NCheckbox :value="FunctionTypes.QuestionBox"> 提问箱(棉花糖 </NCheckbox>
|
||||||
<NSpace>
|
<NCheckbox :value="FunctionTypes.Schedule"> 日程 </NCheckbox>
|
||||||
<NCheckbox v-model:checked="account.settings.sendEmail.recieveQA" @update:checked="SaveComboSetting"> 收到新提问时发送邮件 </NCheckbox>
|
</NCheckboxGroup>
|
||||||
<NCheckbox v-model:checked="account.settings.sendEmail.recieveQAReply" @update:checked="SaveComboSetting"> 提问收到回复时发送邮件 </NCheckbox>
|
<NDivider> 通知 </NDivider>
|
||||||
</NSpace>
|
<NSpace>
|
||||||
<NDivider> 提问箱 </NDivider>
|
<NCheckbox v-model:checked="accountInfo.settings.sendEmail.recieveQA" @update:checked="SaveComboSetting"> 收到新提问时发送邮件 </NCheckbox>
|
||||||
<NSpace>
|
<NCheckbox v-model:checked="accountInfo.settings.sendEmail.recieveQAReply" @update:checked="SaveComboSetting"> 提问收到回复时发送邮件 </NCheckbox>
|
||||||
<NCheckbox v-model:checked="account.settings.questionBox.allowUnregistedUser" @update:checked="SaveComboSetting"> 允许未注册用户提问 </NCheckbox>
|
</NSpace>
|
||||||
</NSpace>
|
<NDivider> 提问箱 </NDivider>
|
||||||
|
<NSpace>
|
||||||
|
<NCheckbox v-model:checked="accountInfo.settings.questionBox.allowUnregistedUser" @update:checked="SaveComboSetting"> 允许未注册用户提问 </NCheckbox>
|
||||||
|
</NSpace>
|
||||||
|
</NTabPane>
|
||||||
|
<NTabPane tab="模板" name="template">
|
||||||
|
<NSpace vertical>
|
||||||
|
<NSpace align="center"> 页面 <NSelect :options="templateOptions" v-model:value="selectedOption" style="width: 150px" /> </NSpace>
|
||||||
|
<NDivider style="margin: 5px 0 5px 0" title-placement="left"> 模板 </NDivider>
|
||||||
|
<template v-if="selectedOption == 'index'">
|
||||||
|
<NSelect style="width: 150px" />
|
||||||
|
<UserIndexView :user-info="accountInfo" :bili-info="biliUserInfo" />
|
||||||
|
</template>
|
||||||
|
<template v-else-if="selectedOption == 'songlist'">
|
||||||
|
<NSelect :options="songListTemplateOptions" v-model:value="selectedSongListTemplate" style="width: 150px" />
|
||||||
|
<SongListView :user-info="accountInfo" :bili-info="biliUserInfo" :fake-data="fakeSongList" :is-self="false" />
|
||||||
|
</template>
|
||||||
|
<template v-else-if="selectedOption == 'schedule'">
|
||||||
|
<NSelect :options="scheduleTemplateOptions" v-model:value="selectedScheduleTemplate" style="width: 150px" />
|
||||||
|
<ScheduleView :user-info="accountInfo" :bili-info="biliUserInfo" :fake-data="fakeSchedule" :is-self="false" :template="selectedScheduleTemplate" />
|
||||||
|
</template>
|
||||||
|
</NSpace>
|
||||||
|
</NTabPane>
|
||||||
|
</NTabs>
|
||||||
</NCard>
|
</NCard>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -0,0 +1,75 @@
|
|||||||
|
<template>
|
||||||
|
<NSpin v-if="isLoading" show />
|
||||||
|
<component v-else v-bind="$attrs" :is="componentType" :user-info="userInfo" :currentData="currentData" />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { ScheduleWeekInfo } from '@/api/api-models'
|
||||||
|
import DefaultScheduleTemplate from '@/views/view/scheduleTemplate/DefaultScheduleTemplate.vue'
|
||||||
|
import { computed, onMounted, ref, watch } from 'vue'
|
||||||
|
import { UserInfo } from '@/api/api-models'
|
||||||
|
import { QueryGetAPI } from '@/api/query'
|
||||||
|
import { SCHEDULE_API_URL } from '@/data/constants'
|
||||||
|
import { NSpin, useMessage } from 'naive-ui'
|
||||||
|
import PinkySchedule from './scheduleTemplate/PinkySchedule.vue'
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
biliInfo: any | undefined
|
||||||
|
userInfo: UserInfo | undefined
|
||||||
|
template?: string | undefined
|
||||||
|
fakeData?: ScheduleWeekInfo[]
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const componentType = computed(() => {
|
||||||
|
const type = props.template ?? props.userInfo?.extra?.templateTypes['schedule']?.toLowerCase()
|
||||||
|
if (props.userInfo) {
|
||||||
|
switch (type?.toLocaleLowerCase()) {
|
||||||
|
case 'test':
|
||||||
|
return DefaultScheduleTemplate
|
||||||
|
case 'pinkyschedule':
|
||||||
|
return PinkySchedule
|
||||||
|
default:
|
||||||
|
return DefaultScheduleTemplate
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return DefaultScheduleTemplate
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const currentData = ref<ScheduleWeekInfo[]>()
|
||||||
|
const isLoading = ref(true)
|
||||||
|
const message = useMessage()
|
||||||
|
|
||||||
|
const errMessage = ref('')
|
||||||
|
|
||||||
|
async function get() {
|
||||||
|
isLoading.value = true
|
||||||
|
await QueryGetAPI<ScheduleWeekInfo[]>(SCHEDULE_API_URL + 'get', {
|
||||||
|
id: props.userInfo?.id,
|
||||||
|
})
|
||||||
|
.then((data) => {
|
||||||
|
if (data.code == 200) {
|
||||||
|
currentData.value = data.data
|
||||||
|
} else {
|
||||||
|
errMessage.value = data.message
|
||||||
|
message.error('加载失败: ' + data.message)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.error(err)
|
||||||
|
message.error('加载失败')
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
isLoading.value = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
if (!props.fakeData) {
|
||||||
|
await get()
|
||||||
|
} else {
|
||||||
|
currentData.value = props.fakeData
|
||||||
|
isLoading.value = false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
<template>
|
<template>
|
||||||
<NSpin v-if="isLoading" show />
|
<NSpin v-if="isLoading" show />
|
||||||
<component v-else :is="songListType" :user-info="userInfo" :songs="songs" />
|
<component v-else :is="componentType" :user-info="userInfo" :currentData="currentData" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { SongListTypes, SongsInfo } from '@/api/api-models'
|
import { SongsInfo } from '@/api/api-models'
|
||||||
import DefaultSongListTemplate from '@/views/view/songListTemplate/DefaultSongListTemplate.vue'
|
import DefaultSongListTemplate from '@/views/view/songListTemplate/DefaultSongListTemplate.vue'
|
||||||
import { computed, onMounted, ref } from 'vue'
|
import { computed, onMounted, ref } from 'vue'
|
||||||
import { UserInfo } from '@/api/api-models'
|
import { UserInfo } from '@/api/api-models'
|
||||||
@@ -12,16 +12,19 @@ import { QueryGetAPI } from '@/api/query'
|
|||||||
import { SONG_API_URL } from '@/data/constants'
|
import { SONG_API_URL } from '@/data/constants'
|
||||||
import { NSpin, useMessage } from 'naive-ui'
|
import { NSpin, useMessage } from 'naive-ui'
|
||||||
|
|
||||||
const { biliInfo, userInfo } = defineProps<{
|
const props = defineProps<{
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
biliInfo: any | undefined
|
biliInfo: any | undefined
|
||||||
userInfo: UserInfo | undefined
|
userInfo: UserInfo | undefined
|
||||||
|
template?: string | undefined
|
||||||
|
fakeData?: SongsInfo[]
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const songListType = computed(() => {
|
const componentType = computed(() => {
|
||||||
if (userInfo) {
|
const type = props.template ?? props.userInfo?.extra?.templateTypes['songlist']?.toLowerCase()
|
||||||
switch (userInfo.songListType) {
|
if (props.userInfo) {
|
||||||
case SongListTypes.Default:
|
switch (type?.toLocaleLowerCase()) {
|
||||||
|
case '':
|
||||||
return DefaultSongListTemplate
|
return DefaultSongListTemplate
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@@ -31,7 +34,7 @@ const songListType = computed(() => {
|
|||||||
return DefaultSongListTemplate
|
return DefaultSongListTemplate
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
const songs = ref<SongsInfo[]>()
|
const currentData = ref<SongsInfo[]>()
|
||||||
const isLoading = ref(true)
|
const isLoading = ref(true)
|
||||||
const message = useMessage()
|
const message = useMessage()
|
||||||
|
|
||||||
@@ -40,11 +43,11 @@ const errMessage = ref('')
|
|||||||
async function getSongs() {
|
async function getSongs() {
|
||||||
isLoading.value = true
|
isLoading.value = true
|
||||||
await QueryGetAPI<SongsInfo[]>(SONG_API_URL + 'get', {
|
await QueryGetAPI<SongsInfo[]>(SONG_API_URL + 'get', {
|
||||||
id: userInfo?.id,
|
id: props.userInfo?.id,
|
||||||
})
|
})
|
||||||
.then((data) => {
|
.then((data) => {
|
||||||
if (data.code == 200) {
|
if (data.code == 200) {
|
||||||
songs.value = data.data
|
currentData.value = data.data
|
||||||
} else {
|
} else {
|
||||||
errMessage.value = data.message
|
errMessage.value = data.message
|
||||||
message.error('加载失败: ' + data.message)
|
message.error('加载失败: ' + data.message)
|
||||||
@@ -60,6 +63,11 @@ async function getSongs() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
await getSongs()
|
if (!props.fakeData) {
|
||||||
|
await getSongs()
|
||||||
|
} else {
|
||||||
|
currentData.value = props.fakeData
|
||||||
|
isLoading.value = false
|
||||||
|
}
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<component :is="indexType" :user-info="userInfo" :bili-info="biliInfo"/>
|
<component :is="componentType" :user-info="userInfo" :bili-info="biliInfo" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
@@ -8,16 +8,18 @@ import DefaultIndexTemplate from '@/views/view/indexTemplate/DefaultIndexTemplat
|
|||||||
import { computed, onMounted, ref } from 'vue'
|
import { computed, onMounted, ref } from 'vue'
|
||||||
import { UserInfo } from '@/api/api-models'
|
import { UserInfo } from '@/api/api-models'
|
||||||
|
|
||||||
const { biliInfo, userInfo } = defineProps<{
|
const props = defineProps<{
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
biliInfo: any | undefined
|
biliInfo: any | undefined
|
||||||
userInfo: UserInfo | undefined
|
userInfo: UserInfo | undefined
|
||||||
|
template?: string | undefined
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const indexType = computed(() => {
|
const componentType = computed(() => {
|
||||||
if (userInfo) {
|
const type = props.template ?? props.userInfo?.extra?.templateTypes['index']?.toLowerCase()
|
||||||
switch (userInfo.indexType) {
|
if (props.userInfo) {
|
||||||
case IndexTypes.Default:
|
switch (type?.toLocaleLowerCase()) {
|
||||||
|
case '':
|
||||||
return DefaultIndexTemplate
|
return DefaultIndexTemplate
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|||||||
19
src/views/view/scheduleTemplate/DefaultScheduleTemplate.vue
Normal file
19
src/views/view/scheduleTemplate/DefaultScheduleTemplate.vue
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { useAccount } from '@/api/account'
|
||||||
|
import { ScheduleWeekInfo, UserInfo } from '@/api/api-models'
|
||||||
|
import ScheduleList from '@/components/ScheduleList.vue'
|
||||||
|
import { NDivider } from 'naive-ui'
|
||||||
|
|
||||||
|
const accountInfo = useAccount()
|
||||||
|
|
||||||
|
defineProps<{
|
||||||
|
userInfo: UserInfo | undefined
|
||||||
|
currentData: ScheduleWeekInfo[] | undefined
|
||||||
|
}>()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<NDivider style="margin-top: 10px" />
|
||||||
|
<ScheduleList v-if="currentData" :schedules="currentData ?? []" :is-self="accountInfo?.id == userInfo?.id" v-bind="$attrs" />
|
||||||
|
<NDivider />
|
||||||
|
</template>
|
||||||
23
src/views/view/scheduleTemplate/PinkySchedule.vue
Normal file
23
src/views/view/scheduleTemplate/PinkySchedule.vue
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { ScheduleWeekInfo, UserInfo } from '@/api/api-models';
|
||||||
|
|
||||||
|
defineProps<{
|
||||||
|
userInfo: UserInfo | undefined
|
||||||
|
currentData: ScheduleWeekInfo[] | undefined
|
||||||
|
}>()
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<div class="schedule-template pinky">
|
||||||
|
<div v-for="item in currentData" :key="item.week">
|
||||||
|
test
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.schedule-template pinky {
|
||||||
|
background-color: #ebf0fa;
|
||||||
|
background-image: linear-gradient(90deg, #ffffff 10%, rgba(0, 0, 0, 0) 10%), linear-gradient(#ffffff 10%, rgba(0, 0, 0, 0) 10%);
|
||||||
|
background-size: 20px 20px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -1,19 +1,19 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useAccount } from '@/api/account';
|
import { useAccount } from '@/api/account'
|
||||||
import { SongsInfo, UserInfo } from '@/api/api-models'
|
import { SongsInfo, UserInfo } from '@/api/api-models'
|
||||||
import SongList from '@/components/SongList.vue'
|
import SongList from '@/components/SongList.vue'
|
||||||
import { NDivider } from 'naive-ui';
|
import { NDivider } from 'naive-ui'
|
||||||
|
|
||||||
const accountInfo = useAccount()
|
const accountInfo = useAccount()
|
||||||
|
|
||||||
defineProps<{
|
defineProps<{
|
||||||
userInfo: UserInfo | undefined
|
userInfo: UserInfo | undefined
|
||||||
songs: SongsInfo[] | undefined
|
currentData: SongsInfo[] | undefined
|
||||||
}>()
|
}>()
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<NDivider style="margin-top: 10px;"/>
|
<NDivider style="margin-top: 10px" />
|
||||||
<SongList v-if="songs" :songs="songs ?? []" :is-self="accountInfo?.id == userInfo?.id"/>
|
<SongList v-if="currentData" :songs="currentData ?? []" :is-self="accountInfo?.id == userInfo?.id" v-bind="$attrs" />
|
||||||
<NDivider/>
|
<NDivider />
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
Reference in New Issue
Block a user