重构多个组件以优化代码格式和可读性,删除不必要的文件,更新类型定义,添加数据分析路由

This commit is contained in:
2025-03-27 18:37:01 +08:00
parent 8bcf201fd4
commit 24f1c413c4
115 changed files with 10879 additions and 2691 deletions

View File

@@ -2,7 +2,7 @@
import { useAccount } from '@/api/account'
import { QAInfo, UserInfo } from '@/api/api-models'
import { QueryGetAPI, QueryPostAPI } from '@/api/query'
import { QUESTION_API_URL, TURNSTILE_KEY } from '@/data/constants'
import { AVATAR_URL, QUESTION_API_URL, TURNSTILE_KEY } from '@/data/constants'
import GraphemeSplitter from 'grapheme-splitter'
import {
NAlert,
@@ -264,7 +264,7 @@ onUnmounted(() => {
</NCard>
<template v-if="item.answer" #footer>
<NSpace align="center" :size="6" :wrap="false">
<NAvatar :src="biliInfo?.face + '@64w'" circle :size="45" :img-props="{ referrerpolicy: 'no-referrer' }" />
<NAvatar :src="AVATAR_URL + userInfo?.biliId + '?size=64'" circle :size="45" :img-props="{ referrerpolicy: 'no-referrer' }" />
<NDivider vertical />
<NText style="font-size: 16px">
{{ item.answer?.message }}

View File

@@ -1,24 +1,41 @@
<template>
<NSpin v-if="isLoading" show />
<component
v-else
:is="ScheduleTemplateMap[componentType ?? ''].compoent"
:bili-info="biliInfo"
:user-info="userInfo"
:data="currentData"
v-bind="$attrs"
/>
<div v-else>
<NDivider style="margin: 16px 0 16px 0" title-placement="left">
订阅链接
<NTooltip>
<template #trigger>
<NIcon>
<TagQuestionMark16Filled />
</NIcon>
</template>
通过订阅链接可以订阅日程表到日历软件中
</NTooltip>
</NDivider>
<NFlex align="center">
<NInputGroup style="max-width: 400px;">
<NInput :value="`${SCHEDULE_API_URL}${userInfo?.id}.ics`" readonly />
<NButton secondary @click="copyToClipboard(`${SCHEDULE_API_URL}${userInfo?.id}.ics`)">
复制
</NButton>
</NInputGroup>
</NFlex>
<NDivider />
<component :is="ScheduleTemplateMap[componentType ?? ''].compoent" :bili-info="biliInfo"
:user-info="userInfo" :data="currentData" v-bind="$attrs" />
</div>
</template>
<script lang="ts" setup>
import { ScheduleWeekInfo, UserInfo } from '@/api/api-models'
import { QueryGetAPI } from '@/api/query'
import { SCHEDULE_API_URL, ScheduleTemplateMap } from '@/data/constants'
import { NSpin, useMessage } from 'naive-ui'
import { copyToClipboard } from '@/Utils'
import { TagQuestionMark16Filled } from '@vicons/fluent'
import { NButton, NDivider, NFlex, NInput, NInputGroup, NSpin, NTooltip, useMessage } from 'naive-ui'
import { computed, onMounted, ref } from 'vue'
const props = defineProps<{
// eslint-disable-next-line @typescript-eslint/no-explicit-any
biliInfo: any | undefined
userInfo: UserInfo | undefined
template?: string | undefined

View File

@@ -1,9 +1,21 @@
<template>
<NSpin v-if="isLoading" show />
<component v-else ref="dynamicConfigRef" :config="selectedTemplateConfig?.name ? currentConfig : undefined"
:is="SongListTemplateMap[componentType ?? '']?.compoent" :user-info="userInfo" :bili-info="biliInfo"
:data="currentData" :live-request-settings="settings" :live-request-active="songsActive" @request-song="requestSong"
v-bind="$attrs" />
<NSpin
v-if="isLoading"
show
/>
<component
:is="SongListTemplateMap[componentType ?? '']?.compoent"
v-else
ref="dynamicConfigRef"
:config="selectedTemplateConfig?.name ? currentConfig : undefined"
:user-info="userInfo"
:bili-info="biliInfo"
:data="currentData"
:live-request-settings="settings"
:live-request-active="songsActive"
v-bind="$attrs"
@request-song="requestSong"
/>
</template>
<script lang="ts" setup>
@@ -23,7 +35,6 @@ const nextRequestTime = useStorage('SongList.NextRequestTime', new Date())
const minRequestTime = 30
const props = defineProps<{
// eslint-disable-next-line @typescript-eslint/no-explicit-any
biliInfo: any | undefined
userInfo: UserInfo | undefined
template?: string | undefined

View File

@@ -1,5 +1,9 @@
<template>
<component :is="componentType" :user-info="userInfo" :bili-info="biliInfo" />
<component
:is="componentType"
:user-info="userInfo"
:bili-info="biliInfo"
/>
</template>
<script lang="ts" setup>
@@ -8,7 +12,6 @@ import DefaultIndexTemplate from '@/views/view/indexTemplate/DefaultIndexTemplat
import { computed } from 'vue'
const props = defineProps<{
// eslint-disable-next-line @typescript-eslint/no-explicit-any
biliInfo: any | undefined
userInfo: UserInfo | undefined
template?: string | undefined

View File

@@ -7,7 +7,6 @@ import { NEmpty, NFlex, NList, NListItem, NSpin, useMessage } from 'naive-ui'
import { ref } from 'vue'
const props = defineProps<{
// eslint-disable-next-line @typescript-eslint/no-explicit-any
biliInfo: any | undefined
userInfo: UserInfo
template?: string | undefined
@@ -42,10 +41,21 @@ async function get() {
<template>
<NSpin :show="isLoading">
<NFlex justify="center">
<NEmpty v-if="videoTables.length == 0" description="没有正在进行的征集表" />
<NEmpty
v-if="videoTables.length == 0"
description="没有正在进行的征集表"
/>
<NList v-else>
<NListItem v-for="item in videoTables" :key="item.id">
<VideoCollectInfoCard :item="item" canClick style="width: 500px; max-width: 70vw" from="user" />
<NListItem
v-for="item in videoTables"
:key="item.id"
>
<VideoCollectInfoCard
:item="item"
can-click
style="width: 500px; max-width: 70vw"
from="user"
/>
</NListItem>
</NList>
</NFlex>

View File

@@ -54,24 +54,44 @@ function delReply(id: number) {
:src="VTSURU_API_URL + 'user-face/' + item.user.id + '?size=64'"
:img-props="{ referrerpolicy: 'no-referrer' }"
/>
<NFlex vertical style="flex: 1" :size="2">
<NFlex
vertical
style="flex: 1"
:size="2"
>
<NFlex align="center">
<NTag v-if="item.isDeleted" type="warning" :bordered="false"> 已删除 </NTag>
<NTag
v-if="item.isDeleted"
type="warning"
:bordered="false"
>
已删除
</NTag>
<NText>
{{ item.user.name }}
</NText>
<NText depth="3">
<NTooltip>
<template #trigger>
<NTime :time="item.sendAt" type="relative" />
<NTime
:time="item.sendAt"
type="relative"
/>
</template>
<NTime :time="item.sendAt" />
</NTooltip>
</NText>
</NFlex>
<div class="editor-content-view" v-html="item.content"></div>
<div
class="editor-content-view"
v-html="item.content"
/>
<NCard v-if="item.replies.length > 0" size="small" style="margin-bottom: 10px">
<NCard
v-if="item.replies.length > 0"
size="small"
style="margin-bottom: 10px"
>
<NFlex vertical>
<ForumReplyItem
v-for="reply in item.replies"
@@ -79,7 +99,7 @@ function delReply(id: number) {
:item="reply"
:comment="item"
:topic="topic"
showReplyButton
show-reply-button
:reply-to="reply.replyTo ? item.replies.find((r) => r.id === reply.replyTo) : undefined"
:reply-to-id="reply.replyTo"
@delete="delReply"
@@ -91,6 +111,9 @@ function delReply(id: number) {
<template #trigger>
<NButton
size="small"
text
:loading="useForum.isLikeLoading"
:disabled="!canOprate"
@click="
useForum.LikeComment(item.id, !item.isLiked).then((success) => {
if (success) {
@@ -99,12 +122,12 @@ function delReply(id: number) {
}
})
"
text
:loading="useForum.isLikeLoading"
:disabled="!canOprate"
>
<template #icon>
<NIcon :component="item.isLiked ? Heart : HeartOutline" :color="item.isLiked ? '#dd484f' : ''" />
<NIcon
:component="item.isLiked ? Heart : HeartOutline"
:color="item.isLiked ? '#dd484f' : ''"
/>
</template>
{{ item.likeCount }}
</NButton>
@@ -113,7 +136,12 @@ function delReply(id: number) {
</NTooltip>
<NTooltip>
<template #trigger>
<NButton size="small" @click="useForum.SetReplyingComment(item)" text :disabled="!canOprate">
<NButton
size="small"
text
:disabled="!canOprate"
@click="useForum.SetReplyingComment(item)"
>
<template #icon>
<NIcon :component="ArrowReply16Filled" />
</template>
@@ -122,12 +150,19 @@ function delReply(id: number) {
</template>
回复
</NTooltip>
<NFlex style="flex: 1" justify="end">
<NFlex
style="flex: 1"
justify="end"
>
<NTooltip v-if="item.user.id === accountInfo.id || topic.isAdmin">
<template #trigger>
<NPopconfirm @positive-click="delComment(item.id)">
<template #trigger>
<NButton size="small" text :disabled="!canOprate">
<NButton
size="small"
text
:disabled="!canOprate"
>
<template #icon>
<NIcon
:component="Delete24Filled"
@@ -145,9 +180,16 @@ function delReply(id: number) {
<template #trigger>
<NPopconfirm @positive-click="restoreComment(item.id)">
<template #trigger>
<NButton size="small" text :disabled="!canOprate">
<NButton
size="small"
text
:disabled="!canOprate"
>
<template #icon>
<NIcon :component="SyncCircleSharp" color="#7f7f7f" />
<NIcon
:component="SyncCircleSharp"
color="#7f7f7f"
/>
</template>
</NButton>
</template>

View File

@@ -72,12 +72,33 @@ function onDropdownSelect(key: string) {
<template>
<NFlex align="center">
<NFlex align="center" :wrap="false">
<NTag v-if="item.isDeleted" size="small" round :bordered="false"> 已删除 </NTag>
<NTag v-if="item.isPinned" size="small" round :bordered="false">
<NIcon :component="Star24Filled" color="#dba913" />
<NFlex
align="center"
:wrap="false"
>
<NTag
v-if="item.isDeleted"
size="small"
round
:bordered="false"
>
已删除
</NTag>
<NTag size="small" style="color: gray">
<NTag
v-if="item.isPinned"
size="small"
round
:bordered="false"
>
<NIcon
:component="Star24Filled"
color="#dba913"
/>
</NTag>
<NTag
size="small"
style="color: gray"
>
<template #icon>
<NIcon :component="Chat24Regular" />
</template>
@@ -90,17 +111,29 @@ function onDropdownSelect(key: string) {
{{ item.title }}
</NText>
</NFlex>
<NFlex style="flex: 1; color: gray; font-size: small" justify="end" align="center">
<NFlex
style="flex: 1; color: gray; font-size: small"
justify="end"
align="center"
>
<template v-if="item.latestRepliedBy">
<span>
<NIcon :component="ArrowReply24Filled" size="15" />
<NIcon
:component="ArrowReply24Filled"
size="15"
/>
@{{ item.latestRepliedBy.name }}
</span>
</template>
<template v-else> @{{ item.user?.name }} 发布于 </template>
<template v-else>
@{{ item.user?.name }} 发布于
</template>
<NTooltip>
<template #trigger>
<NTime :time="item.createAt" type="relative" />
<NTime
:time="item.createAt"
type="relative"
/>
</template>
<NTime :time="item.createAt" />
</NTooltip>

View File

@@ -28,13 +28,24 @@ const emits = defineEmits<{
</script>
<template>
<NFlex align="center" class="forum-reply-item">
<NFlex :wrap="false" align="center">
<NFlex
align="center"
class="forum-reply-item"
>
<NFlex
:wrap="false"
align="center"
>
<NTooltip v-if="replyTo">
<template #trigger>
<NIcon :component="ArrowReply16Filled" />
</template>
<ForumReplyItem :item="replyTo" :comment="comment" :topic="topic" :show-reply-button="false" />
<ForumReplyItem
:item="replyTo"
:comment="comment"
:topic="topic"
:show-reply-button="false"
/>
</NTooltip>
<NAvatar
:src="getUserAvatarUrl(item.user.id)"
@@ -43,16 +54,31 @@ const emits = defineEmits<{
round
style="margin-top: -3px; min-width: 28px; min-height: 28px"
/>
<NText strong depth="3" style="white-space: nowrap">
<NText
strong
depth="3"
style="white-space: nowrap"
>
{{ item.user.name }}
</NText>
</NFlex>
{{ item.content }}
<NFlex justify="end" align="center" :wrap="false" style="flex: 1">
<NFlex
justify="end"
align="center"
:wrap="false"
style="flex: 1"
>
<NTooltip>
<template #trigger>
<NText depth="3" style="font-size: small; min-width: 50px">
<NTime :time="item.sendAt" type="relative" />
<NText
depth="3"
style="font-size: small; min-width: 50px"
>
<NTime
:time="item.sendAt"
type="relative"
/>
</NText>
</template>
<NTime :time="item.sendAt" />
@@ -61,10 +87,10 @@ const emits = defineEmits<{
<template #trigger>
<NButton
size="tiny"
@click="useForum.SetReplyingComment(comment, item)"
round
secondary
:disabled="!canOprate"
@click="useForum.SetReplyingComment(comment, item)"
>
<template #icon>
<NIcon :component="ArrowReply16Filled" />

View File

@@ -57,7 +57,6 @@ type PostReplyModel = {
}
const { biliInfo, userInfo } = defineProps<{
// eslint-disable-next-line @typescript-eslint/no-explicit-any
biliInfo: any | undefined
userInfo: UserInfo | undefined
}>()
@@ -169,14 +168,32 @@ onMounted(async () => {
</script>
<template>
<template v-if="!topic.id"> </template>
<template v-if="!topic.id" />
<template v-else>
<div size="small" embedded style="max-width: 1500px; margin: 0 auto">
<div
size="small"
embedded
style="max-width: 1500px; margin: 0 auto"
>
<NBackTop />
<NBadge class="back-forum-badge" style="width: 100%; left: 0" type="info" :offset="[3, 3]">
<NBadge
class="back-forum-badge"
style="width: 100%; left: 0"
type="info"
:offset="[3, 3]"
>
<NCard size="small">
<NFlex align="center" :wrap="false">
<NTag v-if="topic.isDeleted" type="warning" :bordered="false"> 已删除 </NTag>
<NFlex
align="center"
:wrap="false"
>
<NTag
v-if="topic.isDeleted"
type="warning"
:bordered="false"
>
已删除
</NTag>
<NText style="font-size: large; font-weight: bold; text-align: center; width: 100%">
<NEllipsis style="width: 100%">
{{ topic.title }}
@@ -187,9 +204,15 @@ onMounted(async () => {
<template #value>
<NTooltip>
<template #trigger>
<NButton text @click="() => $router.push({ name: 'user-forum', params: { id: userInfo?.name } })">
<NButton
text
@click="() => $router.push({ name: 'user-forum', params: { id: userInfo?.name } })"
>
<template #icon>
<NIcon :component="ArrowCircleLeft12Regular" color="white" />
<NIcon
:component="ArrowCircleLeft12Regular"
color="white"
/>
</template>
</NButton>
</template>
@@ -197,9 +220,15 @@ onMounted(async () => {
</NTooltip>
</template>
</NBadge>
<NCard content-style="padding: 0 12px 0 12px;" embedded>
<NCard
content-style="padding: 0 12px 0 12px;"
embedded
>
<template #header>
<NFlex align="center" :size="5">
<NFlex
align="center"
:size="5"
>
<NAvatar
:src="VTSURU_API_URL + 'user-face/' + topic?.user?.id + '?size=64'"
:img-props="{ referrerpolicy: 'no-referrer' }"
@@ -212,7 +241,10 @@ onMounted(async () => {
<NTooltip>
<template #trigger>
<NText depth="3">
<NTime :time="topic.createAt" type="relative" />
<NTime
:time="topic.createAt"
type="relative"
/>
</NText>
</template>
<NTime :time="topic.createAt" />
@@ -228,7 +260,11 @@ onMounted(async () => {
<NFlex>
<NTooltip>
<template #trigger>
<NButton size="small" :bordered="topic.isLiked" text>
<NButton
size="small"
:bordered="topic.isLiked"
text
>
<template #icon>
<NIcon :component="Eye24Regular" />
</template>
@@ -241,6 +277,11 @@ onMounted(async () => {
<template #trigger>
<NButton
size="small"
:bordered="topic.isLiked"
secondary
:type="topic.isLiked ? 'primary' : 'default'"
:loading="useForum.isLikeLoading"
:disabled="!canOprate"
@click="
useForum.LikeTopic(topic.id, !topic.isLiked).then((success) => {
if (success) {
@@ -249,14 +290,12 @@ onMounted(async () => {
}
})
"
:bordered="topic.isLiked"
secondary
:type="topic.isLiked ? 'primary' : 'default'"
:loading="useForum.isLikeLoading"
:disabled="!canOprate"
>
<template #icon>
<NIcon :component="topic.isLiked ? Heart : HeartOutline" :color="topic.isLiked ? '#dd484f' : ''" />
<NIcon
:component="topic.isLiked ? Heart : HeartOutline"
:color="topic.isLiked ? '#dd484f' : ''"
/>
</template>
{{ topic.likeCount }}
</NButton>
@@ -265,7 +304,12 @@ onMounted(async () => {
</NTooltip>
<NTooltip>
<template #trigger>
<NButton size="small" @click="showCommentModal = true" secondary :disabled="!canOprate">
<NButton
size="small"
secondary
:disabled="!canOprate"
@click="showCommentModal = true"
>
<template #icon>
<NIcon :component="Comment24Regular" />
</template>
@@ -274,12 +318,19 @@ onMounted(async () => {
</template>
评论
</NTooltip>
<NFlex style="flex: 1" justify="end">
<NFlex
style="flex: 1"
justify="end"
>
<NTooltip v-if="topic?.user?.id === accountInfo.id || topic.isAdmin">
<template #trigger>
<NPopconfirm @positive-click="delTopic(topic.id)">
<template #trigger>
<NButton size="small" text :disabled="!canOprate">
<NButton
size="small"
text
:disabled="!canOprate"
>
<template #icon>
<NIcon
:component="Delete24Filled"
@@ -297,9 +348,16 @@ onMounted(async () => {
<template #trigger>
<NPopconfirm @positive-click="restoreTopic(topic.id)">
<template #trigger>
<NButton size="small" text :disabled="!canOprate">
<NButton
size="small"
text
:disabled="!canOprate"
>
<template #icon>
<NIcon :component="SyncCircleSharp" color="#7f7f7f" />
<NIcon
:component="SyncCircleSharp"
color="#7f7f7f"
/>
</template>
</NButton>
</template>
@@ -311,12 +369,24 @@ onMounted(async () => {
</NFlex>
</NFlex>
</template>
<div class="editor-content-view" v-html="topic.content"></div>
<div
class="editor-content-view"
v-html="topic.content"
/>
</NCard>
<NDivider>
<NButton @click="showCommentModal = true" type="primary" :disabled="!canOprate">发送评论</NButton>
<NButton
type="primary"
:disabled="!canOprate"
@click="showCommentModal = true"
>
发送评论
</NButton>
</NDivider>
<NFlex align="center" justify="center">
<NFlex
align="center"
justify="center"
>
<NPagination
v-if="comments && (comments?.data?.length ?? 0) > 0"
v-model:page="pn"
@@ -326,15 +396,34 @@ onMounted(async () => {
@update:page="refreshComments"
/>
</NFlex>
<br />
<NEmpty v-if="!comments || !comments.data || comments.data.length === 0" description="暂无评论" />
<NList v-else hoverable bordered size="small">
<NListItem v-for="item in comments.data" :key="item.id">
<ForumCommentItem :item="item" :topic="topic" @delete="onDeleteComment" />
<br>
<NEmpty
v-if="!comments || !comments.data || comments.data.length === 0"
description="暂无评论"
/>
<NList
v-else
hoverable
bordered
size="small"
>
<NListItem
v-for="item in comments.data"
:key="item.id"
>
<ForumCommentItem
:item="item"
:topic="topic"
@delete="onDeleteComment"
/>
</NListItem>
</NList>
<br />
<NFlex v-if="(comments?.data?.length ?? 0) > 5" align="center" justify="center">
<br>
<NFlex
v-if="(comments?.data?.length ?? 0) > 5"
align="center"
justify="center"
>
<NPagination
v-if="comments && (comments?.data.length ?? 0) > 0"
v-model:page="pn"
@@ -347,15 +436,41 @@ onMounted(async () => {
<NDivider />
</div>
</template>
<NModal v-model:show="showCommentModal" preset="card" style="width: 1000px; max-width: 90vw; height: auto">
<template #header> 发送评论 </template>
<VEditor v-model:value="currentCommentContent.content" :max-length="1111" ref="editorRef" />
<NButton type="primary" @click="postComment" :loading="!token || useForum.isLoading"> 发布 </NButton>
<NModal
v-model:show="showCommentModal"
preset="card"
style="width: 1000px; max-width: 90vw; height: auto"
>
<template #header>
发送评论
</template>
<VEditor
ref="editorRef"
v-model:value="currentCommentContent.content"
:max-length="1111"
/>
<NButton
type="primary"
:loading="!token || useForum.isLoading"
@click="postComment"
>
发布
</NButton>
</NModal>
<NModal v-model:show="useForum.showReplyModal" preset="card" style="width: 1000px; max-width: 90vw; height: auto">
<template #header> 发送回复 </template>
<NModal
v-model:show="useForum.showReplyModal"
preset="card"
style="width: 1000px; max-width: 90vw; height: auto"
>
<template #header>
发送回复
</template>
<template v-if="useForum.replyingReply">
<NCard size="small" title="正在回复" embedded>
<NCard
size="small"
title="正在回复"
embedded
>
<ForumReplyItem
v-if="useForum.replyingReply && useForum.replyingComment"
:item="useForum.replyingReply"
@@ -374,9 +489,18 @@ onMounted(async () => {
show-count
/>
<NDivider />
<NButton type="primary" @click="postReply" :loading="!token || useForum.isLoading"> 发布 </NButton>
<NButton
type="primary"
:loading="!token || useForum.isLoading"
@click="postReply"
>
发布
</NButton>
</NModal>
<TurnstileVerify ref="turnstile" v-model="token" />
<TurnstileVerify
ref="turnstile"
v-model="token"
/>
</template>
<style>

View File

@@ -27,7 +27,6 @@ import ForumCommentItem from './ForumCommentItem.vue'
import { useAccount } from '@/api/account'
const { biliInfo, userInfo } = defineProps<{
// eslint-disable-next-line @typescript-eslint/no-explicit-any
biliInfo: any | undefined
userInfo: UserInfo | undefined
}>()
@@ -102,22 +101,57 @@ onUnmounted(() => {
</script>
<template>
<NAlert v-if="!forumInfo" type="error"> 用户未创建粉丝讨论区 </NAlert>
<NAlert
v-if="!forumInfo"
type="error"
>
用户未创建粉丝讨论区
</NAlert>
<NCard
v-else-if="
(forumInfo.level < ForumUserLevels.Member && forumInfo.settings.requireApply) ||
forumInfo.settings.allowedViewerLevel > forumInfo.level
forumInfo.settings.allowedViewerLevel > forumInfo.level
"
>
<NAlert type="warning"> 你需要成为成员才能访问 {{ forumInfo.name }} </NAlert>
<br />
<NAlert v-if="forumInfo.isApplied" type="success"> 已申请, 正在等待管理员审核 </NAlert>
<NCard v-else title="加入该讨论区">
<NAlert v-if="!accountInfo.id" type="error"> 需要登录后才能够加入 </NAlert>
<NAlert v-else-if="forumInfo.settings.requireApply" type="warning"> 申请需要审核 </NAlert>
<NAlert v-else type="success"> 该讨论区可直接加入 </NAlert>
<NAlert type="warning">
你需要成为成员才能访问 {{ forumInfo.name }}
</NAlert>
<br>
<NAlert
v-if="forumInfo.isApplied"
type="success"
>
已申请, 正在等待管理员审核
</NAlert>
<NCard
v-else
title="加入该讨论区"
>
<NAlert
v-if="!accountInfo.id"
type="error"
>
需要登录后才能够加入
</NAlert>
<NAlert
v-else-if="forumInfo.settings.requireApply"
type="warning"
>
申请需要审核
</NAlert>
<NAlert
v-else
type="success"
>
该讨论区可直接加入
</NAlert>
<NDivider />
<NButton type="primary" @click="ApplyToForum" :loading="useForum.isLoading" :disabled="!accountInfo.id">
<NButton
type="primary"
:loading="useForum.isLoading"
:disabled="!accountInfo.id"
@click="ApplyToForum"
>
{{ forumInfo.settings.requireApply ? '申请' : '' }}加入
</NButton>
</NCard>
@@ -127,41 +161,90 @@ onUnmounted(() => {
<NCard size="small">
<template #header>
<NFlex justify="center">
<NText style="font-size: large">{{ forumInfo.name }}</NText>
<NText style="font-size: large">
{{ forumInfo.name }}
</NText>
</NFlex>
</template>
</NCard>
<NFlex>
<NCard style="max-width: 300px">
<NFlex vertical>
<NButton @click="showPostTopicModal = true"> 发布话题 </NButton>
<NCard v-if="forumInfo.isAdmin" size="small" title="管理员">
</NCard>
<NButton @click="showPostTopicModal = true">
发布话题
</NButton>
<NCard
v-if="forumInfo.isAdmin"
size="small"
title="管理员"
/>
</NFlex>
</NCard>
<NList bordered style="flex: 1" size="small" hoverable clickable>
<NListItem v-for="item in topics?.data ?? []" :key="item.id">
<a :href="`${$route.path}/topic/${item.id}`" target="_blank">
<ForumPreviewItem :item="item" :forum="forumInfo" />
<NList
bordered
style="flex: 1"
size="small"
hoverable
clickable
>
<NListItem
v-for="item in topics?.data ?? []"
:key="item.id"
>
<a
:href="`${$route.path}/topic/${item.id}`"
target="_blank"
>
<ForumPreviewItem
:item="item"
:forum="forumInfo"
/>
</a>
</NListItem>
</NList>
</NFlex>
</NFlex>
<NModal preset="card" v-model:show="showPostTopicModal" style="width: 800px; max-width: 95%">
<NModal
v-model:show="showPostTopicModal"
preset="card"
style="width: 800px; max-width: 95%"
>
<template #header>
发布话题
<NDivider vertical />
<NText depth="3" style="font-size: small"> 保存于 <NTime :time="lastBackupTopic" format="HH:mm:ss" /> </NText>
<NText
depth="3"
style="font-size: small"
>
保存于 <NTime
:time="lastBackupTopic"
format="HH:mm:ss"
/>
</NText>
</template>
<NFlex vertical>
<NInput v-model:value="currentPostTopicModel.title" placeholder="标题" />
<VEditor v-model:value="currentPostTopicModel.content" :max-length="2333" ref="editor" />
<NButton type="primary" @click="postTopic" :loading="!token || useForum.isLoading"> 发布 </NButton>
<NInput
v-model:value="currentPostTopicModel.title"
placeholder="标题"
/>
<VEditor
ref="editor"
v-model:value="currentPostTopicModel.content"
:max-length="2333"
/>
<NButton
type="primary"
:loading="!token || useForum.isLoading"
@click="postTopic"
>
发布
</NButton>
</NFlex>
</NModal>
<TurnstileVerify ref="turnstile" v-model="token" />
<TurnstileVerify
ref="turnstile"
v-model="token"
/>
</template>
</template>

View File

@@ -72,46 +72,103 @@ export const Config: TemplateConfig<ConfigType> = {
<NDivider />
<template v-if="userInfo?.biliId">
<template v-if="userInfo?.id == accountInfo?.id">
<NButton type="primary"
@click="$router.push({ name: 'manage-index', query: { tab: 'setting', setting: 'index' } })">
<NButton
type="primary"
@click="$router.push({ name: 'manage-index', query: { tab: 'setting', setting: 'index' } })"
>
自定义个人主页
</NButton>
<NDivider />
</template>
<template v-if="indexInfo?.notification">
<NCard size="small" content-style="text-align: center">
<NCard
size="small"
content-style="text-align: center"
>
{{ indexInfo?.notification }}
</NCard>
<br />
<br>
</template>
<NSpace justify="center" align="center" vertical>
<NAvatar v-if="biliInfo" :src="biliInfo?.face" :size="width > 750 ? 175 : 100" round bordered :img-props="{
referrerpolicy: 'no-referrer',
}"
:style="{ boxShadow: isDarkMode ? 'rgb(195 192 192 / 35%) 0px 5px 20px' : '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
justify="center"
align="center"
vertical
>
<NAvatar
v-if="biliInfo"
:src="biliInfo?.face"
:size="width > 750 ? 175 : 100"
round
bordered
:img-props="{
referrerpolicy: 'no-referrer',
}"
:style="{ boxShadow: isDarkMode ? 'rgb(195 192 192 / 35%) 0px 5px 20px' : '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">
<NText
strong
depth="3"
style="font-size: medium"
>
{{ userInfo?.biliId }}
</NText>
<NText strong depth="2" style="font-size: medium">
<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)">
<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>
<template v-if="Object.keys(indexInfo.links || {}).length > 0">
<NFlex align="center">
<NDivider vertical />
<NButton type="info" secondary tag="a" :href="link[1]" target="_blank"
v-for="link in Object.entries(indexInfo.links || {})" :key="link[0] + link[1]">
<NButton
v-for="link in Object.entries(indexInfo.links || {})"
:key="link[0] + link[1]"
type="info"
secondary
tag="a"
:href="link[1]"
target="_blank"
>
{{ link[0] }}
</NButton>
</NFlex>
@@ -122,13 +179,25 @@ export const Config: TemplateConfig<ConfigType> = {
<NText>相关视频</NText>
</NDivider>
<NFlex justify="center">
<SimpleVideoCard v-for="video in indexInfo.videos" :video="video" :key="video.id" />
<SimpleVideoCard
v-for="video in indexInfo.videos"
:key="video.id"
:video="video"
/>
</NFlex>
</template>
</template>
<template v-else>
<NSpace justify="center" align="center">
<NText strong style="font-size: 32px"> {{ userInfo?.name }} </NText>
<NSpace
justify="center"
align="center"
>
<NText
strong
style="font-size: 32px"
>
{{ userInfo?.name }}
</NText>
未认证
</NSpace>
</template>

View File

@@ -11,6 +11,11 @@ defineProps<ScheduleConfigType>()
<template>
<NDivider style="margin-top: 10px" />
<ScheduleList v-if="data" :schedules="data ?? []" :is-self="false" v-bind="$attrs" />
<ScheduleList
v-if="data"
:schedules="data ?? []"
:is-self="false"
v-bind="$attrs"
/>
<NDivider />
</template>

View File

@@ -48,17 +48,28 @@ onMounted(() => {
<template>
<NSpace>
<NSelect :options="options" v-model:value="selectedDate" style="width: 200px" placeholder="选择其他周表" />
<SaveCompoent :compoent="table" :file-name="`周表_${selectedDate}_${userInfo?.name}`" />
<NSelect
v-model:value="selectedDate"
:options="options"
style="width: 200px"
placeholder="选择其他周表"
/>
<SaveCompoent
:compoent="table"
:file-name="`周表_${selectedDate}_${userInfo?.name}`"
/>
</NSpace>
<NDivider />
<div ref="table" class="schedule-template pinky container">
<div
ref="table"
class="schedule-template pinky container"
>
<div class="schedule-template pinky day-container">
<div
class="schedule-template pinky day-item"
:id="index.toString()"
v-for="(item, index) in currentWeek?.days"
:id="index.toString()"
:key="index"
class="schedule-template pinky day-item"
>
<div class="schedule-template pinky header">
<span class="schedule-template pinky week">
@@ -72,15 +83,25 @@ onMounted(() => {
</span>
</div>
<div class="schedule-template pinky day-content-container">
<span v-if="item.tag" class="schedule-template pinky day-content" id="work">
<span
v-if="item.tag"
id="work"
class="schedule-template pinky day-content"
>
{{ item.title }}
</span>
<span v-else class="schedule-template pinky day-content" id="rest"> 休息 </span>
<span
v-else
id="rest"
class="schedule-template pinky day-content"
> 休息 </span>
</div>
</div>
</div>
<div class="schedule-template pinky title-container">
<div class="schedule-template pinky title">S C H E D U L E</div>
<div class="schedule-template pinky title">
S C H E D U L E
</div>
</div>
</div>
</template>

View File

@@ -56,11 +56,19 @@ const buttons = (song: SongsInfo) => [
<template>
<NDivider style="margin-top: 10px" />
<SongList v-if="data" :songs="data ?? []" :is-self="accountInfo?.id == userInfo?.id" :extraButton="buttons"
v-bind="$attrs" />
<SongList
v-if="data"
:songs="data ?? []"
:is-self="accountInfo?.id == userInfo?.id"
:extra-button="buttons"
v-bind="$attrs"
/>
<NCollapse v-if="userInfo?.canRequestSong">
<NCollapseItem title="点歌列表">
<NCard size="small" embedded>
<NCard
size="small"
embedded
>
<div style="height: 400px; width: 700px; max-width: 100%; position: relative; margin: 0 auto">
<LiveRequestOBS :id="userInfo?.id" />
</div>

View File

@@ -100,65 +100,126 @@ function loadMore() {
}
</script>
<template>
<div :style="{
display: 'flex',
justifyContent: 'center',
flexDirection: windowSize.width.value > 900 ? 'row' : 'column',
gap: '10px',
width: '100%',
}">
<NCard size="small" :style="{ width: windowSize.width.value > 900 ? '400px' : '100%' }">
<div
:style="{
display: 'flex',
justifyContent: 'center',
flexDirection: windowSize.width.value > 900 ? 'row' : 'column',
gap: '10px',
width: '100%',
}"
>
<NCard
size="small"
:style="{ width: windowSize.width.value > 900 ? '400px' : '100%' }"
>
<NCollapseTransition>
<SongPlayer v-if="selectedSong" :song="selectedSong" v-model:is-lrc-loading="isLrcLoading" />
<SongPlayer
v-if="selectedSong"
v-model:is-lrc-loading="isLrcLoading"
:song="selectedSong"
/>
</NCollapseTransition>
<NDivider> 标签 </NDivider>
<NSpace>
<NButton v-for="tag in tags" :key="tag" size="small" secondary
<NButton
v-for="tag in tags"
:key="tag"
size="small"
secondary
:type="selectedTag == tag ? 'primary' : 'default'"
@click="selectedTag == tag ? (selectedTag = '') : (selectedTag = tag)">
@click="selectedTag == tag ? (selectedTag = '') : (selectedTag = tag)"
>
{{ tag }}
</NButton>
</NSpace>
<NDivider> 搜索歌曲 </NDivider>
<NSpace vertical>
<NInput v-model:value="searchKeyword" placeholder="歌名" clearable />
<NSelect v-model:value="selectedAuthor" :options="authors.map((a) => {
return { label: a, value: a }
})
" placeholder="选择歌手" clearable />
<NInput
v-model:value="searchKeyword"
placeholder="歌名"
clearable
/>
<NSelect
v-model:value="selectedAuthor"
:options="authors.map((a) => {
return { label: a, value: a }
})
"
placeholder="选择歌手"
clearable
/>
<NDivider />
<LiveRequestOBS v-if="userInfo?.extra?.enableFunctions.includes(FunctionTypes.SongRequest)" />
</NSpace>
</NCard>
<NEmpty v-if="!data || songs?.length == 0" description="暂无曲目" style="max-width: 0 auto" />
<NScrollbar v-else ref="container" :style="{
flexGrow: 1,
height: windowSize.width.value > 900 ? '90vh' : '800px',
overflowY: 'auto',
overflowX: 'hidden',
}" @scroll="onScroll">
<NGrid cols="1 600:2 900:3 1200:4" x-gap="10" y-gap="10" responsive="self">
<NGridItem v-for="item in songs" :key="item.key">
<NCard size="small" style="height: 200px; min-width: 300px">
<NEmpty
v-if="!data || songs?.length == 0"
description="暂无曲目"
style="max-width: 0 auto"
/>
<NScrollbar
v-else
ref="container"
:style="{
flexGrow: 1,
height: windowSize.width.value > 900 ? '90vh' : '800px',
overflowY: 'auto',
overflowX: 'hidden',
}"
@scroll="onScroll"
>
<NGrid
cols="1 600:2 900:3 1200:4"
x-gap="10"
y-gap="10"
responsive="self"
>
<NGridItem
v-for="item in songs"
:key="item.key"
>
<NCard
size="small"
style="height: 200px; min-width: 300px"
>
<template #header>
<NSpace :wrap="false" align="center">
<NSpace
:wrap="false"
align="center"
>
<div
:style="`border-radius: 4px; background-color: ${item.options ? '#bd5757' : '#577fb8'}; width: 7px; height: 20px`">
</div>
:style="`border-radius: 4px; background-color: ${item.options ? '#bd5757' : '#577fb8'}; width: 7px; height: 20px`"
/>
<NEllipsis>
{{ item.name }}
</NEllipsis>
</NSpace>
</template>
<NSpace vertical>
<NSpace v-if="(item.author?.length ?? 0) > 0" :size="0">
<div v-for="(author, index) in item.author" v-bind:key="author">
<NButton size="small" text
@click="selectedAuthor == author ? (selectedAuthor = undefined) : (selectedAuthor = author)">
<NText depth="3" :style="{ color: selectedAuthor == author ? '#82bcd3' : '' }">
<NSpace
v-if="(item.author?.length ?? 0) > 0"
:size="0"
>
<div
v-for="(author, index) in item.author"
:key="author"
>
<NButton
size="small"
text
@click="selectedAuthor == author ? (selectedAuthor = undefined) : (selectedAuthor = author)"
>
<NText
depth="3"
:style="{ color: selectedAuthor == author ? '#82bcd3' : '' }"
>
{{ author }}
</NText>
<NDivider v-if="index < (item.author?.length ?? 0) - 1" vertical />
<NDivider
v-if="index < (item.author?.length ?? 0) - 1"
vertical
/>
</NButton>
</div>
</NSpace>
@@ -170,15 +231,41 @@ function loadMore() {
</NEllipsis>
<template v-if="item.options">
<NSpace>
<NTag v-if="item.options?.scMinPrice" size="small" type="error" :bordered="false">
SC | {{ item.options?.scMinPrice }}</NTag>
<NTag v-if="item.options?.fanMedalMinLevel" size="small" type="info" :bordered="false">
粉丝牌 | {{ item.options?.fanMedalMinLevel }}</NTag>
<NTag v-if="item.options?.needZongdu" size="small" :color="{ color: GetGuardColor(1) }">
<NTag
v-if="item.options?.scMinPrice"
size="small"
type="error"
:bordered="false"
>
SC | {{ item.options?.scMinPrice }}
</NTag>
<NTag
v-if="item.options?.fanMedalMinLevel"
size="small"
type="info"
:bordered="false"
>
粉丝牌 | {{ item.options?.fanMedalMinLevel }}
</NTag>
<NTag
v-if="item.options?.needZongdu"
size="small"
:color="{ color: GetGuardColor(1) }"
>
总督
</NTag>
<NTag v-if="item.options?.needTidu" size="small" :color="{ color: GetGuardColor(2) }"> 提督 </NTag>
<NTag v-if="item.options?.needJianzhang" size="small" :color="{ color: GetGuardColor(3) }">
<NTag
v-if="item.options?.needTidu"
size="small"
:color="{ color: GetGuardColor(2) }"
>
提督
</NTag>
<NTag
v-if="item.options?.needJianzhang"
size="small"
:color="{ color: GetGuardColor(3) }"
>
舰长
</NTag>
</NSpace>
@@ -186,11 +273,18 @@ function loadMore() {
</NSpace>
</template>
<template #action>
<NSpace align="center" :wrap="false">
<NSpace
align="center"
:wrap="false"
>
<NTooltip v-if="item.url">
<template #trigger>
<NButton size="small" @click="selectedSong = item" type="success"
:loading="isLrcLoading == item.key">
<NButton
size="small"
type="success"
:loading="isLrcLoading == item.key"
@click="selectedSong = item"
>
<template #icon>
<NIcon :component="Play24Filled" />
</template>
@@ -200,13 +294,17 @@ function loadMore() {
</NTooltip>
<NTooltip>
<template #trigger>
<NButton size="small" @click="() => {
<NButton
size="small"
:type="liveRequestSettings?.allowFromWeb == false || item.options ? 'warning' : 'info'"
:loading="isLoading == item.key"
@click="() => {
isLoading = item.key
emits('requestSong', item)
isLoading = ''
}
" :type="liveRequestSettings?.allowFromWeb == false || item.options ? 'warning' : 'info'"
:loading="isLoading == item.key">
"
>
<template #icon>
<NIcon :component="CloudAdd20Filled" />
</template>
@@ -221,26 +319,44 @@ function loadMore() {
}}
</NTooltip>
<NPopover v-if="(item.tags?.length ?? 0) > 3" trigger="hover">
<NPopover
v-if="(item.tags?.length ?? 0) > 3"
trigger="hover"
>
<template #trigger>
<NButton size="small" secondary :type="item.tags?.includes(selectedTag) ? 'primary' : 'default'">
<NButton
size="small"
secondary
:type="item.tags?.includes(selectedTag) ? 'primary' : 'default'"
>
标签
</NButton>
</template>
<NSpace :wrap="false">
<NButton v-for="tag in item.tags" size="tiny" :key="tag"
<NButton
v-for="tag in item.tags"
:key="tag"
size="tiny"
:type="selectedTag == tag ? 'primary' : 'default'"
@click="() => (selectedTag == tag ? (selectedTag = '') : (selectedTag = tag))"
:type="selectedTag == tag ? 'primary' : 'default'">
>
<NEllipsis style="max-width: 50px">
{{ tag }}
</NEllipsis>
</NButton>
</NSpace>
</NPopover>
<NSpace v-else :wrap="false">
<NButton v-for="tag in item.tags" size="tiny" :key="tag"
<NSpace
v-else
:wrap="false"
>
<NButton
v-for="tag in item.tags"
:key="tag"
size="tiny"
:type="selectedTag == tag ? 'primary' : 'default'"
@click="() => (selectedTag == tag ? (selectedTag = '') : (selectedTag = tag))"
:type="selectedTag == tag ? 'primary' : 'default'">
>
<NEllipsis style="max-width: 50px">
{{ tag }}
</NEllipsis>
@@ -253,7 +369,12 @@ function loadMore() {
</NGrid>
<NDivider />
<NSpace justify="center">
<NButton v-if="data.length > index" @click="loadMore"> 加载更多 </NButton>
<NButton
v-if="data.length > index"
@click="loadMore"
>
加载更多
</NButton>
</NSpace>
</NScrollbar>
</div>

View File

@@ -36,33 +36,42 @@ export const Config: TemplateConfig<TraditionalConfigType> = {
<template>
WIP...
<div :style="{
backgroundImage: `${props.config?.background ? 'url(' + FILE_BASE_URL + props.config?.background[0] + ')' : ''}`,
height: '100%', 'max-width': '100%',
minHeight: '400px',
backgroundSize: 'cover',
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
position: 'relative',
}">
<div :style="{
height: '100%',
width: '100%',
<div
:style="{
backgroundImage: `${props.config?.background ? 'url(' + FILE_BASE_URL + props.config?.background[0] + ')' : ''}`,
height: '100%', 'max-width': '100%',
minHeight: '400px',
backdropFilter: 'blur(10px)',
backgroundSize: 'cover',
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'center',
}">
<div style="
position: 'relative',
}"
>
<div
:style="{
height: '100%',
width: '100%',
minHeight: '400px',
backdropFilter: 'blur(10px)',
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'center',
}"
>
<div
style="
border-radius: 20px;
border: 3px solid var(--pinky-border-color-dark);
height: 50px;
width: 400px;
">
<div v-for="song in props.data" :key="song.id">
"
>
<div
v-for="song in props.data"
:key="song.id"
>
{{ song.name }}
</div>
</div>