feat: 更新依赖和移除不必要的文件, 更新歌单管理列表在小屏幕上的显示效果, 修复自定义配置文件加载

- 在 package.json 中移除不再使用的依赖项,并更新部分依赖版本
- 删除多个不再使用的组件和文件,包括 CheckInTemplateHelper.vue、CommonConfigItems.vue、GlobalSettingsConfig.vue 等
- 更新 bun.lockb 文件以反映依赖变更
This commit is contained in:
2025-05-03 20:17:54 +08:00
parent fe5b420d49
commit 70ff05926c
24 changed files with 302 additions and 4181 deletions

View File

@@ -1,201 +0,0 @@
<script setup lang="ts">
import { GetSelfAccount, useAccount } from '@/api/account'
import { QueryGetAPI } from '@/api/query'
import { BILI_API_URL } from '@/data/constants'
import {
NAlert,
NButton,
NCard,
NCountdown,
NInput,
NInputGroup,
NInputNumber,
NSpace,
NSpin,
NText,
useMessage,
} from 'naive-ui'
import { onMounted, ref } from 'vue'
const message = useMessage()
const accountInfo = useAccount()
const isStart = ref(false)
const timeLeft = ref(0)
const timeOut = ref(false)
const uId = ref()
const roomId = ref()
const timer = ref()
function onStartVerify() {
QueryGetAPI(BILI_API_URL + 'verify', {
uId: uId.value,
}).then((data) => {
if (data.code == 200) {
message.info('已开始认证流程, 请前往直播间发送认证码')
checkStatus()
isStart.value = true
timer.value = setInterval(checkStatus, 2500)
}
})
}
async function checkStatus() {
const data = await QueryGetAPI<{
uId: number
roomId: number
endTime: number
}>(BILI_API_URL + 'status')
if (data.code == 200) {
//正在进行认证
roomId.value ??= data.data.roomId
timeLeft.value = data.data.endTime
return true
} else if (data.code == 201) {
clearInterval(timer.value)
message.success('认证成功')
setTimeout(() => {
GetSelfAccount()
}, 1)
return true
} else if (data.code == 400 && isStart.value) {
timeOut.value = true
clearInterval(timer.value)
message.error('认证超时')
return false
}
return false
}
function copyCode() {
if (navigator.clipboard) {
navigator.clipboard.writeText(accountInfo.value?.biliVerifyCode ?? '')
message.success('已复制认证码到剪切板')
} else {
message.warning('当前环境不支持自动复制, 请手动选择并复制')
}
}
onMounted(async () => {
if (accountInfo.value && !accountInfo.value.isBiliVerified) {
if (await checkStatus()) {
isStart.value = true
timer.value = setInterval(checkStatus, 5000)
}
}
})
</script>
<template>
<NAlert
v-if="accountInfo?.isBiliVerified"
type="success"
>
你已通过验证
</NAlert>
<NAlert v-else-if="!accountInfo">
尚未登录
</NAlert>
<NCard
v-else
embedded
>
<template #header>
Bilibili 身份验证
</template>
<template v-if="isStart">
<NSpace
vertical
justify="center"
align="center"
>
<template v-if="!timeOut">
<NSpin />
<span> 剩余 <NCountdown :duration="timeLeft - Date.now()" /> </span>
</template>
<NAlert
v-else
type="error"
>
认证超时
<NButton
type="info"
@click="
() => {
isStart = false
timeOut = false
}
"
>
重新开始
</NButton>
</NAlert>
<NInputGroup>
<NInput
v-model:value="accountInfo.biliVerifyCode"
:allow-input="() => false"
/>
<NButton @click="copyCode">
复制认证码
</NButton>
</NInputGroup>
<NButton
v-if="roomId"
type="primary"
tag="a"
:href="'https://live.bilibili.com/' + roomId"
target="_blank"
>
前往直播间
</NButton>
</NSpace>
</template>
<template v-else>
<NSpace
vertical
justify="center"
align="center"
>
<NAlert type="info">
<NText>
请在点击
<NText
type="primary"
strong
>
开始认证
</NText>
后2分钟之内使用
<NText
strong
type="primary"
>
需要认证的账户
</NText>
在自己的直播间内发送
<NButton
type="info"
text
@click="copyCode"
>
{{ accountInfo?.biliVerifyCode }}
</NButton>
</NText>
</NAlert>
<NInputNumber
v-model:value="uId"
size="small"
placeholder="输入用户UId"
:min="1"
:show-button="false"
/>
<NButton
size="large"
type="primary"
@click="onStartVerify"
>
开始认证
</NButton>
</NSpace>
</template>
</NCard>
</template>

View File

@@ -1,24 +0,0 @@
<script setup lang="ts">
import { useAccount } from '@/api/account'
import { useDanmakuClient } from '@/store/useDanmakuClient'
import { NAlert } from 'naive-ui'
import OpenLottery from '../open_live/OpenLottery.vue'
const accountInfo = useAccount()
const client = await useDanmakuClient().initOpenlive()
</script>
<template>
<NAlert
v-if="accountInfo?.isBiliVerified != true"
type="info"
>
尚未进行Bilibili认证
</NAlert>
<OpenLottery
v-else
:room-info="client.authInfo!"
:code="accountInfo?.biliAuthCode"
/>
</template>

View File

@@ -1,24 +0,0 @@
<script setup lang="ts">
import { useAccount } from '@/api/account'
import { useDanmakuClient } from '@/store/useDanmakuClient'
import { NAlert } from 'naive-ui'
import MusicRequest from '../open_live/MusicRequest.vue'
const accountInfo = useAccount()
const client = await useDanmakuClient().initOpenlive()
</script>
<template>
<NAlert
v-if="accountInfo?.isBiliVerified != true"
type="info"
>
尚未进行Bilibili认证
</NAlert>
<MusicRequest
v-else
:client="client"
:room-info="client.authInfo!"
:code="accountInfo?.biliAuthCode"
/>
</template>

View File

@@ -1,30 +0,0 @@
<script setup lang="ts">
import { computed, onMounted, ref } from 'vue'
// 组件属性定义
const props = defineProps<{
// 可以根据需要添加属性
}>()
// 事件定义
const emit = defineEmits<{
// 可以根据需要添加事件
}>()
// 组件挂载时的初始化
onMounted(() => {
// 初始化逻辑
})
</script>
<template>
<div class="point-sub-item-manage">
<!-- 组件内容 -->
</div>
</template>
<style scoped>
.point-sub-item-manage {
width: 100%;
}
</style>

View File

@@ -22,7 +22,9 @@ import {
NFlex,
NForm,
NFormItem,
NInput, // 引入 NInput
NGrid,
NGi,
NInput,
NInputNumber,
NModal,
NSelect,
@@ -556,42 +558,50 @@ onMounted(async () => {
</NButton>
</template>
</NEmpty>
<div
<NGrid
v-else
class="goods-grid"
cols="1 500:2 750:3 1000:4 1300:5"
:x-gap="12"
:y-gap="12"
class="goods-list"
style="justify-items: center;"
>
<PointGoodsItem
<NGi
v-for="item in selectedItems"
:key="item.id"
:goods="item"
content-style="max-width: 300px; min-width: 250px; height: 380px;"
class="goods-item"
:class="{ 'pinned-item': item.isPinned }"
style="width: 100%;"
>
<template #footer>
<NFlex
justify="space-between"
align="center"
class="goods-footer"
>
<NTooltip placement="bottom">
<template #trigger>
<NButton
:disabled="getTooltip(item) !== '开始兑换'"
size="small"
type="primary"
class="exchange-btn"
@click="onBuyClick(item)"
>
{{ item.isPinned ? '🔥 兑换' : '兑换' }}
</NButton>
</template>
{{ getTooltip(item) }}
</NTooltip>
</NFlex>
</template>
</PointGoodsItem>
</div>
<PointGoodsItem
:goods="item"
content-style="max-width: 300px; min-width: 250px; height: 380px;"
class="goods-item"
:class="{ 'pinned-item': item.isPinned }"
>
<template #footer>
<NFlex
justify="space-between"
align="center"
class="goods-footer"
>
<NTooltip placement="bottom">
<template #trigger>
<NButton
:disabled="getTooltip(item) !== '开始兑换'"
size="small"
type="primary"
class="exchange-btn"
@click="onBuyClick(item)"
>
{{ item.isPinned ? '🔥 兑换' : '兑换' }}
</NButton>
</template>
{{ getTooltip(item) }}
</NTooltip>
</NFlex>
</template>
</PointGoodsItem>
</NGi>
</NGrid>
</NSpin>
<!-- 兑换确认模态框 -->
@@ -712,7 +722,7 @@ onMounted(async () => {
<style scoped>
.point-goods-container {
max-width: 1200px;
max-width: 1300px;
margin: 0 auto;
padding: 0 8px;
}
@@ -799,11 +809,9 @@ onMounted(async () => {
min-height: 200px;
}
.goods-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
gap: 16px;
.goods-list {
margin-top: 16px;
justify-items: center;
}
.goods-item {
@@ -815,6 +823,7 @@ onMounted(async () => {
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.02);
position: relative;
overflow: hidden;
margin: 0 auto;
}
.goods-item:hover {
@@ -943,10 +952,6 @@ onMounted(async () => {
}
@media (max-width: 768px) {
.goods-grid {
grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
}
.price-text {
font-size: 1.1em;
}

View File

@@ -1,13 +0,0 @@
function TestVineComponent() {
return vine`
<div>
<h1>Test Vine</h1>
<p>This is a test vine component.</p>
<p>Vine is a new way to build web applications.</p>
<p>Enjoy building with Vine!</p>
<footer>Footer content goes here.</footer>
</div>
`
}
export default TestVineComponent

View File

@@ -1,12 +1,18 @@
<template>
<div class="checkin-ranking-view">
<NSpace vertical>
<NCard title="签到排行榜">
<NCard
class="ranking-card"
title="签到排行榜"
>
<template #header-extra>
<NSpace>
<NSpace
:wrap="true"
:size="8"
>
<NSelect
v-model:value="timeRange"
style="width: 180px"
style="min-width: 120px; width: auto"
:options="timeRangeOptions"
@update:value="loadCheckInRanking"
/>
@@ -14,7 +20,7 @@
v-model:value="userFilter"
placeholder="搜索用户"
clearable
style="width: 150px"
style="min-width: 120px; width: auto"
/>
<NButton
type="primary"
@@ -38,125 +44,129 @@
<!-- 自定义排行榜表格 -->
<div
v-else
class="custom-ranking-table"
class="ranking-table-wrapper"
>
<!-- 排行榜头部 -->
<div class="ranking-header">
<div class="ranking-row">
<div class="col-rank">
排名
</div>
<div class="col-user">
用户
</div>
<div class="col-days">
连续签到
</div>
<div class="col-monthly">
本月签到
</div>
<div class="col-total">
签到
</div>
<div class="col-time">
最近签到时间
<div
class="custom-ranking-table"
>
<!-- 排行榜头部 -->
<div class="ranking-header">
<div class="ranking-row">
<div class="col-rank">
排名
</div>
<div class="col-user">
用户
</div>
<div class="col-days">
连续签到
</div>
<div class="col-monthly">
本月签到
</div>
<div class="col-total">
总签到
</div>
<div class="col-time">
最近签到时间
</div>
</div>
</div>
</div>
<!-- 排行榜内容 -->
<div class="ranking-body">
<div
v-for="(item, index) in pagedData"
:key="index"
class="ranking-row"
:class="{'top-three': index < 3}"
>
<!-- 排名列 -->
<div class="col-rank">
<div
class="rank-number"
:class="{
'rank-1': index === 0,
'rank-2': index === 1,
'rank-3': index === 2
}"
>
{{ index + 1 + (pagination.page - 1) * pagination.pageSize }}
<!-- 排行榜内容 -->
<div class="ranking-body">
<div
v-for="(item, index) in pagedData"
:key="index"
class="ranking-row"
:class="{'top-three': index < 3}"
>
<!-- 排名列 -->
<div class="col-rank">
<div
class="rank-number"
:class="{
'rank-1': index === 0,
'rank-2': index === 1,
'rank-3': index === 2
}"
>
{{ index + 1 + (pagination.page - 1) * pagination.pageSize }}
</div>
</div>
</div>
<!-- 用户列 -->
<div class="col-user">
<div class="user-name">
{{ item.name }}
<!-- 用户列 -->
<div class="col-user">
<div class="user-name">
{{ item.name }}
</div>
<div
v-if="item.isAuthed"
class="user-authed"
>
已认证
</div>
</div>
<div
v-if="item.isAuthed"
class="user-authed"
>
已认证
</div>
</div>
<!-- 连续签到列 -->
<div class="col-days">
<div class="days-count">
{{ item.consecutiveDays }}
<!-- 连续签到列 -->
<div class="col-days">
<div class="days-count">
{{ item.consecutiveDays }}
</div>
<div class="days-text">
</div>
</div>
<div class="days-text">
</div>
</div>
<!-- 本月签到列 -->
<div class="col-monthly">
<div class="count-value">
{{ item.monthlyCheckInCount || 0 }}
<!-- 本月签到列 -->
<div class="col-monthly">
<div class="count-value">
{{ item.monthlyCheckInCount || 0 }}
</div>
<div class="count-text">
</div>
</div>
<div class="count-text">
</div>
</div>
<!-- 总签到列 -->
<div class="col-total">
<div class="count-value">
{{ item.totalCheckInCount || 0 }}
<!-- 总签到列 -->
<div class="col-total">
<div class="count-value">
{{ item.totalCheckInCount || 0 }}
</div>
<div class="count-text">
</div>
</div>
<div class="count-text">
</div>
</div>
<!-- 签到时间列 -->
<div class="col-time">
<NTooltip>
<template #trigger>
<NTime
:time="item.lastCheckInTime"
type="relative"
/>
</template>
<template #default>
<NTime
:time="item.lastCheckInTime"
/>
</template>
</NTooltip>
<!-- 签到时间列 -->
<div class="col-time">
<NTooltip>
<template #trigger>
<NTime
:time="item.lastCheckInTime"
type="relative"
/>
</template>
<template #default>
<NTime
:time="item.lastCheckInTime"
/>
</template>
</NTooltip>
</div>
</div>
</div>
</div>
<!-- 分页控制 -->
<div class="ranking-footer">
<NPagination
v-model:page="pagination.page"
v-model:page-size="pagination.pageSize"
:item-count="filteredRankingData.length"
:page-sizes="[10, 20, 50]"
show-size-picker
/>
<!-- 分页控制 -->
<div class="ranking-footer">
<NPagination
v-model:page="pagination.page"
v-model:page-size="pagination.pageSize"
:item-count="filteredRankingData.length"
:page-sizes="[10, 20, 50]"
show-size-picker
/>
</div>
</div>
</div>
</NSpin>
@@ -353,29 +363,27 @@ onMounted(() => {
.custom-ranking-table {
border-radius: 8px;
overflow: hidden;
/* 使用官方阴影变量 */
box-shadow: var(--box-shadow-1);
margin-bottom: 16px;
overflow-x: auto;
}
.ranking-header {
/* 使用官方背景色变量 */
background-color: var(--table-header-color);
font-weight: var(--font-weight-strong);
color: var(--text-color-2);
border-radius: 8px;
}
.ranking-row {
display: flex;
align-items: center;
padding: 12px 16px;
/* 使用官方分割线变量 */
border-bottom: 1px solid var(--divider-color);
transition: background-color 0.3s var(--cubic-bezier-ease-in-out);
}
.ranking-body .ranking-row:hover {
/* 使用官方悬停背景色变量 */
background-color: var(--hover-color);
}
@@ -384,7 +392,6 @@ onMounted(() => {
}
.top-three {
/* 使用官方条纹背景色变量 */
background-color: var(--table-color-striped);
}
@@ -423,12 +430,10 @@ onMounted(() => {
align-items: center;
justify-content: center;
font-weight: var(--font-weight-strong);
/* 使用官方文本和背景色变量 */
color: var(--text-color-2);
background-color: var(--action-color);
}
/* 保持奖牌颜色在暗色模式下也清晰可见 */
.rank-1 {
background: linear-gradient(135deg, #ffe259, #ffa751);
color: white !important;
@@ -475,7 +480,64 @@ onMounted(() => {
padding: 16px;
display: flex;
justify-content: center;
/* 使用官方背景色变量 */
background-color: var(--table-header-color);
}
/* 增强响应式样式 */
.ranking-card :deep(.n-card-header__main) {
font-size: var(--font-size-large);
white-space: nowrap;
}
.ranking-table-wrapper {
overflow-x: auto;
}
/* 响应式调整 */
@media (max-width: 768px) {
.ranking-card :deep(.n-card-header) {
flex-direction: column;
align-items: flex-start;
gap: 12px;
}
.ranking-card :deep(.n-card-header__extra) {
margin-left: 0;
width: 100%;
}
.col-rank {
width: 50px;
}
.col-days,
.col-monthly,
.col-total {
width: 80px;
}
.col-time {
width: 120px;
}
.custom-ranking-table {
min-width: 550px;
}
}
@media (max-width: 480px) {
.col-user {
min-width: 80px;
}
.col-days,
.col-monthly,
.col-total {
width: 70px;
}
.col-time {
width: 100px;
}
}
</style>

View File

@@ -821,6 +821,7 @@ onUnmounted(() => {
position: relative;
width: 100%;
padding: 0 16px;
box-sizing: border-box;
}
/* 表单卡片样式 */

View File

@@ -1,44 +0,0 @@
<script lang="ts" setup>
import { UserInfo } from '@/api/api-models'
import { TemplateConfig } from '@/data/VTsuruConfigTypes'
import { h } from 'vue'
const width = window.innerWidth
const props = defineProps<{
userInfo: UserInfo | undefined
biliInfo: any | undefined
currentData?: any
}>()
function navigate(url: string) {
window.open(url, '_blank')
}
</script>
<script lang="ts">
export type ConfigType = {
cover?: string
}
export const Config: TemplateConfig<ConfigType> = {
name: 'Template.Index.Simple',
items: [
{
name: '封面',
type: 'image',
imageLimit: 1,
key: 'cover',
onUploaded: (url, config) => {
config.cover = url[0]
},
},
{
name: 'test',
key: 'test',
type: 'render',
render: (config) => h('div', '1'),
},
],
}
</script>
<template>1</template>