This commit is contained in:
2023-10-21 18:38:11 +08:00
parent 20a086cfb5
commit 50b435ba80
15 changed files with 706 additions and 160 deletions

View File

@@ -9,7 +9,7 @@ const accountInfo = useAccount()
</script>
<template>
<NSpace justify="center" align="center" vertical>
<NSpace justify="center" align="center" vertical style="width: 100%;">
<NCard embedded style="max-width: 90%;width: 800px;">
<NSpace align="center" justify="center" vertical>
<NText style="font-size: 3rem">
@@ -32,7 +32,7 @@ const accountInfo = useAccount()
</NAlert>
<NAlert>
Bilibili 账户:
<NTag v-if="accountInfo?.isBiliVerified" type="success"> 已认证 </NTag>
<NTag v-if="accountInfo?.isBiliVerified" type="success"> 已认证 | {{ accountInfo?.biliId }} </NTag>
<template v-else>
<NTag type="error" size="small"> 未认证 </NTag>
<NDivider vertical />
@@ -40,7 +40,7 @@ const accountInfo = useAccount()
</template>
</NAlert>
</NCard>
<div style="max-width: 90%;width: 800px;">
<div style="width: 100%;">
<NDivider/>
<SettingsManageView />
</div>

View File

@@ -2,7 +2,7 @@
import { useAccount } from '@/api/account'
import { QueryGetAPI } from '@/api/query'
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 { use } from 'echarts/core'
import { CanvasRenderer } from 'echarts/renderers'
@@ -409,9 +409,11 @@ onMounted(async () => {
<template>
<NCard size="small">
<VChart :option="fansOption" style="height: 200px" />
<VChart :option="guardsOption" style="height: 200px" />
<VChart :option="upstatViewOption" style="height: 200px" />
<VChart :option="upstatLikeOption" style="height: 200px" />
<NSpace vertical>
<VChart :option="fansOption" style="height: 200px" />
<VChart :option="guardsOption" style="height: 200px" />
<VChart :option="upstatViewOption" style="height: 200px" />
<VChart :option="upstatLikeOption" style="height: 200px" />
</NSpace>
</NCard>
</template>

View File

@@ -5,9 +5,28 @@ import { QueryGetAPI, QueryPostAPI } from '@/api/query'
import ScheduleList from '@/components/ScheduleList.vue'
import { SCHEDULE_API_URL } from '@/data/constants'
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 { SelectBaseOption, SelectMixedOption } from 'naive-ui/es/select/src/interface'
import { computed, onMounted, ref } from 'vue'
import {
NAlert,
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 = {
user: {
@@ -28,7 +47,7 @@ const rules = {
trigger: ['input'],
},
}
const weekdays = ['周一', '周二', '周三', '周四', '周五', '周六', '周日']
const yearOptions = [
{
label: new Date().getFullYear().toString(),
@@ -41,16 +60,45 @@ const yearOptions = [
] as SelectMixedOption[]
const weekOptions = computed(() => {
let weeks = [] as SelectMixedOption[]
const all = getAllWeeks(addScheduleYear.value.value as number)
const all = getAllWeeks(selectedScheduleYear.value)
all.forEach((week) => {
const isExist = (schedules.value?.findIndex((s) => s.year == selectedScheduleYear.value && s.week == week[0] + 1) ?? -1) > -1
weeks.push({
label: `${week[0] + 1}周 (${week[1]})`,
label: `${isExist ? '(已安排)' : ''} ${week[0] + 1}周 (${week[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
})
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) {
const startDate = startOfYear(new Date(year, 0, 1))
const endDate = endOfYear(new Date(year, 11, 31))
@@ -61,8 +109,8 @@ function getAllWeeks(year: number) {
let index = 0
while (isBefore(date, endDate)) {
const weekStart = format(date, 'MM-dd')
const weekEnd = format(endOfWeek(date, { weekStartsOn: 1 }), 'MM-dd')
const weekStart = format(date, 'MM/dd')
const weekEnd = format(endOfWeek(date, { weekStartsOn: 1 }), 'MM/dd')
weeks.push([index, `${weekStart} - ${weekEnd}`])
@@ -78,10 +126,13 @@ const message = useMessage()
const showUpdateModal = ref(false)
const showAddModal = ref(false)
const showCopyModal = ref(false)
const updateScheduleModel = ref<ScheduleWeekInfo>({} as ScheduleWeekInfo)
const selectedExistTag = ref()
const addScheduleYear = ref(yearOptions[0])
const addScheduleWeek = ref()
const selectedDay = ref(0)
const selectedScheduleYear = ref(new Date().getFullYear())
const selectedScheduleWeek = ref(Number(format(Date.now(), 'w')) + 1)
async function get() {
await QueryGetAPI<ScheduleWeekInfo[]>(SCHEDULE_API_URL + 'get', {
@@ -90,7 +141,6 @@ async function get() {
.then((data) => {
if (data.code == 200) {
schedules.value = data.data
console.log(data.data)
} else {
message.error('加载失败: ' + data.message)
}
@@ -100,53 +150,86 @@ async function get() {
message.error('加载失败')
})
}
const isAdding = ref(false)
const isLoading = ref(false)
async function addSchedule() {
isAdding.value = true
isLoading.value = true
await QueryPostAPI(SCHEDULE_API_URL + 'update', {
year: addScheduleYear.value.value,
week: addScheduleWeek.value,
year: selectedScheduleYear.value,
week: selectedScheduleWeek.value,
})
.then((data) => {
if (data.code == 200) {
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
updateScheduleModel.value = model
showUpdateModal.value = true
get()
} else {
message.error('添加失败: ' + data.message)
}
})
.finally(() => {
isAdding.value = false
isLoading.value = false
})
}
async function onUpdateSchedule(schedule: ScheduleWeekInfo) {
updateScheduleModel.value = schedule
async function onCopySchedule() {
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', {
year: addScheduleYear.value.value,
week: addScheduleWeek.value,
})
.then((data) => {
if (data.code == 200) {
message.success('添加成功')
const model = {
year: addScheduleYear.value.value as number,
week: addScheduleWeek.value.value as number,
days: new Array(7) as ScheduleDayInfo[],
} as ScheduleWeekInfo
showAddModal.value = false
showUpdateModal.value = true
year: updateScheduleModel.value.year,
week: updateScheduleModel.value.week,
day: selectedDay.value,
days: updateScheduleModel.value?.days,
}).then((data) => {
if (data.code == 200) {
message.success('成功')
const s = schedules.value?.find((s) => s.year == selectedScheduleYear.value && s.week == selectedScheduleWeek.value)
if (s) {
s.days[selectedDay.value] = updateScheduleModel.value.days[selectedDay.value]
} else {
schedules.value?.push(updateScheduleModel.value)
}
})
.finally(() => {
isAdding.value = false
})
//updateScheduleModel.value = {} as ScheduleWeekInfo
} else {
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(() => {
get()
@@ -154,17 +237,65 @@ onMounted(() => {
</script>
<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="添加周程">
<NSelect :options="yearOptions" />
<NSelect :options="weekOptions" />
<NSpace vertical>
年份
<NSelect :options="yearOptions" v-model:value="selectedScheduleYear" />
第几周
<NSelect :options="weekOptions" v-model:value="selectedScheduleWeek" />
</NSpace>
<NDivider />
<NButton @click="addSchedule" :loading="isAdding"> 添加 </NButton>
<NButton @click="addSchedule" :loading="isLoading"> 添加 </NButton>
</NModal>
<NModal v-model:show="showUpdateModal">
<NTabs>
<NTabPane v-for="(day, index) in updateScheduleModel.days" v-bind:key="day.time" :name="index" :tab="index.toString()"> </NTabPane>
</NTabs>
<NModal v-model:show="showCopyModal" style="width: 600px; max-width: 90vw" preset="card" title="复制周程">
<NAlert type="info"> 复制为 </NAlert>
<NSpace vertical>
年份
<NSelect :options="yearOptions" v-model:value="selectedScheduleYear" />
第几周
<NSelect :options="weekOptions" v-model:value="selectedScheduleWeek" />
</NSpace>
<NDivider />
<NButton @click="onCopySchedule" :loading="isLoading"> 复制 </NButton>
</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>

View File

@@ -1,36 +1,197 @@
<script setup lang="ts">
import { useAccount } from '@/api/account'
import { NButton, NCard, NCheckbox, NCheckboxGroup, NDivider, NForm, NSpace, NSwitch, useMessage } from 'naive-ui'
import { ref } from 'vue'
import { NButton, NCard, NCheckbox, NCheckboxGroup, NDivider, NForm, NSelect, NSpace, NSwitch, NTabPane, NTabs, SelectOption, useMessage } from 'naive-ui'
import { computed, onMounted, ref } from 'vue'
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 { 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 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) {
if (account.value) {
if (accountInfo.value) {
if (enable) {
//从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 {
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 }) {
if (account.value) {
if (accountInfo.value) {
//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) => {
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))
if (accountInfo.value) {
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) {
if (account.value) {
async function SaveComboSetting(value: boolean) {
if (accountInfo.value) {
//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) => {
if (data.code == 200) {
//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>
<template>
<NCard v-if="account" title="设置">
<NDivider style="margin: 0"> 启用功能 </NDivider>
<NCheckboxGroup v-model:value="account.settings.enableFunctions" @update:value="SaveComboGroupSetting">
<NCheckbox :value="FunctionTypes.SongList"> 歌单 </NCheckbox>
<NCheckbox :value="FunctionTypes.QuestionBox"> 提问箱(棉花糖 </NCheckbox>
</NCheckboxGroup>
<NDivider > 通知 </NDivider>
<NSpace>
<NCheckbox v-model:checked="account.settings.sendEmail.recieveQA" @update:checked="SaveComboSetting"> 收到新提问时发送邮件 </NCheckbox>
<NCheckbox v-model:checked="account.settings.sendEmail.recieveQAReply" @update:checked="SaveComboSetting"> 提问收到回复时发送邮件 </NCheckbox>
</NSpace>
<NDivider> 提问箱 </NDivider>
<NSpace>
<NCheckbox v-model:checked="account.settings.questionBox.allowUnregistedUser" @update:checked="SaveComboSetting"> 允许未注册用户提问 </NCheckbox>
</NSpace>
<NCard v-if="accountInfo" title="设置" style="min-height: 800px">
<NTabs>
<NTabPane tab="常规" name="general">
<NDivider style="margin: 0"> 启用功能 </NDivider>
<NCheckboxGroup v-model:value="accountInfo.settings.enableFunctions" @update:value="SaveComboGroupSetting">
<NCheckbox :value="FunctionTypes.SongList"> 歌单 </NCheckbox>
<NCheckbox :value="FunctionTypes.QuestionBox"> 提问箱(棉花糖 </NCheckbox>
<NCheckbox :value="FunctionTypes.Schedule"> 日程 </NCheckbox>
</NCheckboxGroup>
<NDivider> 通知 </NDivider>
<NSpace>
<NCheckbox v-model:checked="accountInfo.settings.sendEmail.recieveQA" @update:checked="SaveComboSetting"> 收到新提问时发送邮件 </NCheckbox>
<NCheckbox v-model:checked="accountInfo.settings.sendEmail.recieveQAReply" @update:checked="SaveComboSetting"> 提问收到回复时发送邮件 </NCheckbox>
</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>
</template>