mirror of
https://github.com/Megghy/vtsuru.live.git
synced 2025-12-06 18:36:55 +08:00
feat: 更新依赖项和配置,添加新通知类型
- 在 package.json 中添加了 @types/md5 和 @vueuse/integrations 依赖。 - 更新了 tsconfig.json 中的模块解析方式为 bundler。 - 在组件声明中移除了不再使用的 Naive UI 组件。 - 在弹幕窗口和设置中添加了启用动画的选项,并更新了相关样式。 - 实现了私信发送失败的通知功能,增强了用户体验。
This commit is contained in:
@@ -50,6 +50,7 @@ import {
|
||||
NText,
|
||||
NTooltip,
|
||||
useMessage,
|
||||
NCard,
|
||||
} from 'naive-ui'
|
||||
import { computed, h, onMounted, ref, watch } from 'vue'
|
||||
import { RouterLink, useRoute } from 'vue-router'
|
||||
@@ -644,37 +645,125 @@ onMounted(() => {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
padding: 50px;
|
||||
height: 100vh;
|
||||
width: 100vw;
|
||||
background: linear-gradient(135deg, rgba(250,250,250,0.8) 0%, rgba(240,240,245,0.9) 100%);
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
box-sizing: border-box;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
"
|
||||
:class="isDarkMode ? 'login-dark-bg' : ''"
|
||||
>
|
||||
<template v-if="!isLoadingAccount">
|
||||
<NSpace
|
||||
vertical
|
||||
justify="center"
|
||||
align="center"
|
||||
<NCard
|
||||
style="max-width: 520px; width: 100%; min-width: 350px; border-radius: 12px; box-shadow: 0 8px 24px rgba(0,0,0,0.08); margin: 16px;"
|
||||
:bordered="false"
|
||||
>
|
||||
<NText> 请登录或注册后使用 </NText>
|
||||
<NButton
|
||||
tag="a"
|
||||
href="/"
|
||||
<template #header>
|
||||
<NFlex
|
||||
justify="center"
|
||||
align="center"
|
||||
style="padding: 12px 0;"
|
||||
>
|
||||
<NText
|
||||
strong
|
||||
style="font-size: 1.8rem; text-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); background-image: linear-gradient(to right, #36d1dc, #5b86e5); -webkit-background-clip: text; -webkit-text-fill-color: transparent;"
|
||||
>
|
||||
VTSURU CENTER
|
||||
</NText>
|
||||
</NFlex>
|
||||
</template>
|
||||
|
||||
<NSpace
|
||||
vertical
|
||||
size="large"
|
||||
style="padding: 8px 0;"
|
||||
>
|
||||
回到主页
|
||||
</NButton>
|
||||
</NSpace>
|
||||
<NDivider />
|
||||
<RegisterAndLogin style="max-width: 500px; min-width: 350px" />
|
||||
<NFlex
|
||||
justify="center"
|
||||
align="center"
|
||||
>
|
||||
<NText style="font-size: 16px; text-align: center;">
|
||||
请登录或注册后使用
|
||||
</NText>
|
||||
</NFlex>
|
||||
|
||||
<NAlert
|
||||
type="info"
|
||||
style="border-radius: 8px;"
|
||||
>
|
||||
<NFlex
|
||||
vertical
|
||||
align="center"
|
||||
size="small"
|
||||
>
|
||||
<div style="text-align: center;">
|
||||
如果你不是主播且不发送棉花糖(提问)的话则不需要注册登录
|
||||
</div>
|
||||
<NFlex
|
||||
justify="center"
|
||||
style="width: 100%; margin-top: 8px;"
|
||||
>
|
||||
<NButton
|
||||
type="primary"
|
||||
size="small"
|
||||
@click="$router.push({ name: 'bili-user'})"
|
||||
>
|
||||
<template #icon>
|
||||
<NIcon :component="BrowsersOutline" />
|
||||
</template>
|
||||
前往 Bilibili 认证用户主页
|
||||
</NButton>
|
||||
</NFlex>
|
||||
</NFlex>
|
||||
</NAlert>
|
||||
|
||||
<NDivider style="margin: 8px 0;" />
|
||||
|
||||
<RegisterAndLogin />
|
||||
|
||||
<NFlex justify="center">
|
||||
<NButton
|
||||
secondary
|
||||
tag="a"
|
||||
href="/"
|
||||
style="min-width: 100px;"
|
||||
>
|
||||
回到主页
|
||||
</NButton>
|
||||
</NFlex>
|
||||
</NSpace>
|
||||
</NCard>
|
||||
</template>
|
||||
<template v-else>
|
||||
<NSpin
|
||||
:loading="isLoadingAccount"
|
||||
style="overflow: hidden"
|
||||
<NCard
|
||||
:bordered="false"
|
||||
style="min-width: 300px; width: 100%; max-width: 400px; border-radius: 12px; box-shadow: 0 8px 24px rgba(0,0,0,0.08); margin: 16px;"
|
||||
>
|
||||
正在请求账户数据...
|
||||
</NSpin>
|
||||
<NFlex
|
||||
vertical
|
||||
justify="center"
|
||||
align="center"
|
||||
style="padding: 20px 10px;"
|
||||
>
|
||||
<NSpin
|
||||
:loading="isLoadingAccount"
|
||||
size="large"
|
||||
>
|
||||
<NText>正在请求账户数据...</NText>
|
||||
</NSpin>
|
||||
</NFlex>
|
||||
</NCard>
|
||||
</template>
|
||||
</NLayoutContent>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.login-dark-bg {
|
||||
background: linear-gradient(135deg, rgba(30,30,35,0.9) 0%, rgba(20,20,25,0.95) 100%) !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -5,6 +5,7 @@ import { QueryGetAPI } from '@/api/query'
|
||||
import { POINT_API_URL } from '@/data/constants'
|
||||
import { objectsToCSV } from '@/Utils'
|
||||
import { Info24Filled } from '@vicons/fluent'
|
||||
import { Warning24Regular } from '@vicons/fluent'
|
||||
import { useStorage } from '@vueuse/core'
|
||||
import { format } from 'date-fns'
|
||||
import { saveAs } from 'file-saver'
|
||||
@@ -63,6 +64,7 @@ const ps = ref(25)
|
||||
// 弹窗控制
|
||||
const showModal = ref(false)
|
||||
const showGivePointModal = ref(false)
|
||||
const showResetAllPointsModal = ref(false)
|
||||
const isLoading = ref(true)
|
||||
|
||||
// 积分调整表单
|
||||
@@ -70,6 +72,10 @@ const addPointCount = ref(0)
|
||||
const addPointReason = ref<string>('')
|
||||
const addPointTarget = ref<number>()
|
||||
|
||||
// 重置所有积分确认
|
||||
const resetConfirmText = ref('')
|
||||
const RESET_CONFIRM_TEXT = '我确认删除'
|
||||
|
||||
// 用户数据
|
||||
const users = ref<ResponsePointUserModel[]>([])
|
||||
// 根据筛选条件过滤后的用户
|
||||
@@ -277,6 +283,37 @@ async function deleteUser(user: ResponsePointUserModel) {
|
||||
}
|
||||
}
|
||||
|
||||
// 重置所有用户积分
|
||||
async function resetAllPoints() {
|
||||
// 验证确认文本
|
||||
if (resetConfirmText.value !== RESET_CONFIRM_TEXT) {
|
||||
message.error(`请输入"${RESET_CONFIRM_TEXT}"以确认操作`)
|
||||
return
|
||||
}
|
||||
|
||||
isLoading.value = true
|
||||
try {
|
||||
const data = await QueryGetAPI(POINT_API_URL + 'reset')
|
||||
|
||||
if (data.code == 200) {
|
||||
message.success('已重置所有用户积分')
|
||||
resetConfirmText.value = ''
|
||||
showResetAllPointsModal.value = false
|
||||
|
||||
// 重新加载用户数据
|
||||
setTimeout(() => {
|
||||
refresh()
|
||||
}, 1500)
|
||||
} else {
|
||||
message.error('重置失败: ' + data.message)
|
||||
}
|
||||
} catch (err) {
|
||||
message.error('重置失败: ' + err)
|
||||
} finally {
|
||||
isLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 导出用户积分数据
|
||||
function exportData() {
|
||||
try {
|
||||
@@ -360,6 +397,12 @@ onMounted(async () => {
|
||||
>
|
||||
导出积分数据
|
||||
</NButton>
|
||||
<NButton
|
||||
type="error"
|
||||
@click="showResetAllPointsModal = true"
|
||||
>
|
||||
重置所有积分
|
||||
</NButton>
|
||||
</NFlex>
|
||||
</template>
|
||||
|
||||
@@ -516,6 +559,46 @@ onMounted(async () => {
|
||||
</NButton>
|
||||
</NFlex>
|
||||
</NModal>
|
||||
|
||||
<!-- 重置所有用户积分弹窗 -->
|
||||
<NModal
|
||||
v-model:show="showResetAllPointsModal"
|
||||
preset="card"
|
||||
style="max-width: 500px"
|
||||
title="重置所有用户积分"
|
||||
>
|
||||
<NFlex
|
||||
vertical
|
||||
:gap="16"
|
||||
>
|
||||
<NFlex
|
||||
align="center"
|
||||
:gap="8"
|
||||
>
|
||||
<NIcon
|
||||
:component="Warning24Regular"
|
||||
color="red"
|
||||
/>
|
||||
<NText type="error">
|
||||
警告:此操作将删除所有用户积分记录,不可恢复!
|
||||
</NText>
|
||||
</NFlex>
|
||||
<NText>请输入 <b>"{{ RESET_CONFIRM_TEXT }}"</b> 以确认操作</NText>
|
||||
<NInput
|
||||
v-model:value="resetConfirmText"
|
||||
placeholder="请输入确认文本"
|
||||
/>
|
||||
|
||||
<NButton
|
||||
type="error"
|
||||
:loading="isLoading"
|
||||
@click="resetAllPoints"
|
||||
:disabled="resetConfirmText !== RESET_CONFIRM_TEXT"
|
||||
>
|
||||
确认重置所有用户积分
|
||||
</NButton>
|
||||
</NFlex>
|
||||
</NModal>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
@@ -115,13 +115,13 @@ const showOBSModal = ref(false)
|
||||
|
||||
const settings = computed({
|
||||
get: () => {
|
||||
if (accountInfo.value) {
|
||||
if (accountInfo.value.id) {
|
||||
return accountInfo.value.settings.songRequest
|
||||
}
|
||||
return defaultSettings
|
||||
},
|
||||
set: (value) => {
|
||||
if (accountInfo.value) {
|
||||
if (accountInfo.value.id) {
|
||||
accountInfo.value.settings.songRequest = value
|
||||
}
|
||||
},
|
||||
@@ -231,7 +231,7 @@ const configCanEdit = computed(() => {
|
||||
const table = ref()
|
||||
|
||||
async function getAllSong() {
|
||||
if (accountInfo.value) {
|
||||
if (accountInfo.value.id) {
|
||||
try {
|
||||
const data = await QueryGetAPI<SongRequestInfo[]>(SONG_REQUEST_API_URL + 'get-all', {
|
||||
id: accountInfo.value.id,
|
||||
@@ -263,7 +263,7 @@ async function addSong(danmaku: EventModel) {
|
||||
})
|
||||
return
|
||||
}
|
||||
if (accountInfo.value) {
|
||||
if (accountInfo.value.id) {
|
||||
await QueryPostAPI<SongRequestInfo>(SONG_REQUEST_API_URL + 'try-add', danmaku).then((data) => {
|
||||
if (data.code == 200) {
|
||||
message.success(`[${danmaku.uname}] 添加曲目: ${data.data.songName}`)
|
||||
@@ -310,7 +310,7 @@ async function addSongManual() {
|
||||
message.error('请输入名称')
|
||||
return
|
||||
}
|
||||
if (accountInfo.value) {
|
||||
if (accountInfo.value.id) {
|
||||
await QueryPostAPIWithParams<SongRequestInfo>(SONG_REQUEST_API_URL + 'add', {
|
||||
name: newSongName.value,
|
||||
}).then((data) => {
|
||||
@@ -409,7 +409,7 @@ function checkMessage(msg: string) {
|
||||
.startsWith(accountInfo.value ? settings.value.orderPrefix.toLowerCase() : defaultPrefix.value)
|
||||
}
|
||||
async function onUpdateFunctionEnable() {
|
||||
if (accountInfo.value) {
|
||||
if (accountInfo.value.id) {
|
||||
const oldValue = JSON.parse(JSON.stringify(accountInfo.value.settings.enableFunctions))
|
||||
if (accountInfo.value?.settings.enableFunctions.includes(FunctionTypes.SongRequest)) {
|
||||
accountInfo.value.settings.enableFunctions = accountInfo.value.settings.enableFunctions.filter(
|
||||
@@ -428,7 +428,7 @@ async function onUpdateFunctionEnable() {
|
||||
`已${accountInfo.value?.settings.enableFunctions.includes(FunctionTypes.SongRequest) ? '启用' : '禁用'}点播功能`,
|
||||
)
|
||||
} else {
|
||||
if (accountInfo.value) {
|
||||
if (accountInfo.value.id) {
|
||||
accountInfo.value.settings.enableFunctions = oldValue
|
||||
}
|
||||
message.error(
|
||||
@@ -444,7 +444,7 @@ async function onUpdateFunctionEnable() {
|
||||
}
|
||||
}
|
||||
async function updateSettings() {
|
||||
if (accountInfo.value) {
|
||||
if (accountInfo.value.id) {
|
||||
isLoading.value = true
|
||||
await SaveSetting('SongRequest', settings.value)
|
||||
.then((msg) => {
|
||||
@@ -714,7 +714,7 @@ function GetGuardColor(level: number | null | undefined): string {
|
||||
return ''
|
||||
}
|
||||
async function updateActive() {
|
||||
if (!accountInfo.value) return
|
||||
if (!accountInfo.value.id) return
|
||||
try {
|
||||
const data = await QueryGetAPI<SongRequestInfo[]>(SONG_REQUEST_API_URL + 'get-active', {
|
||||
id: accountInfo.value?.id,
|
||||
@@ -763,7 +763,7 @@ let timer: any
|
||||
let updateActiveTimer: any
|
||||
const updateKey = ref(0)
|
||||
onMounted(() => {
|
||||
if (accountInfo.value) {
|
||||
if (accountInfo.value.id) {
|
||||
settings.value = accountInfo.value.settings.songRequest
|
||||
}
|
||||
client.onEvent('danmaku', onGetDanmaku)
|
||||
|
||||
@@ -132,13 +132,13 @@
|
||||
// 队列设置 (登录后使用账户设置, 否则使用默认设置)
|
||||
const settings = computed({
|
||||
get: () => {
|
||||
if (accountInfo.value) {
|
||||
if (accountInfo.value.id) {
|
||||
return accountInfo.value.settings.queue;
|
||||
}
|
||||
return defaultSettings;
|
||||
},
|
||||
set: (value) => {
|
||||
if (accountInfo.value) {
|
||||
if (accountInfo.value.id) {
|
||||
accountInfo.value.settings.queue = value;
|
||||
}
|
||||
},
|
||||
@@ -215,7 +215,7 @@
|
||||
|
||||
// 获取所有队列数据
|
||||
async function getAll() {
|
||||
if (accountInfo.value) {
|
||||
if (accountInfo.value.id) {
|
||||
try {
|
||||
isLoading.value = true;
|
||||
const data = await QueryGetAPI<ResponseQueueModel[]>(QUEUE_API_URL + 'get-all', {
|
||||
@@ -258,7 +258,7 @@
|
||||
return;
|
||||
}
|
||||
|
||||
if (accountInfo.value) { // 已登录,调用 API
|
||||
if (accountInfo.value.id) { // 已登录,调用 API
|
||||
try {
|
||||
const data = await QueryPostAPI<ResponseQueueModel>(QUEUE_API_URL + 'try-add', danmaku);
|
||||
if (data.code == 200) {
|
||||
@@ -321,7 +321,7 @@
|
||||
message.error('请输入用户名');
|
||||
return;
|
||||
}
|
||||
if (accountInfo.value) { // 已登录,调用 API
|
||||
if (accountInfo.value.id) { // 已登录,调用 API
|
||||
try {
|
||||
const data = await QueryPostAPIWithParams<ResponseQueueModel>(QUEUE_API_URL + 'add', {
|
||||
name: newQueueName.value,
|
||||
@@ -484,7 +484,7 @@
|
||||
|
||||
// 更新功能启用状态
|
||||
async function onUpdateFunctionEnable() {
|
||||
if (accountInfo.value) {
|
||||
if (accountInfo.value.id) {
|
||||
const oldValue = JSON.parse(JSON.stringify(accountInfo.value.settings.enableFunctions));
|
||||
const isEnabling = !accountInfo.value.settings.enableFunctions.includes(FunctionTypes.Queue);
|
||||
|
||||
@@ -508,14 +508,14 @@
|
||||
message.success(`已${isEnabling ? '启用' : '禁用'}队列功能`);
|
||||
} else {
|
||||
// 回滚状态
|
||||
if (accountInfo.value) {
|
||||
if (accountInfo.value.id) {
|
||||
accountInfo.value.settings.enableFunctions = oldValue;
|
||||
}
|
||||
message.error(`队列功能${isEnabling ? '启用' : '禁用'}失败: ${data.message}`);
|
||||
}
|
||||
} catch (err: any) {
|
||||
// 回滚状态
|
||||
if (accountInfo.value) {
|
||||
if (accountInfo.value.id) {
|
||||
accountInfo.value.settings.enableFunctions = oldValue;
|
||||
}
|
||||
message.error(`队列功能${isEnabling ? '启用' : '禁用'}失败: ${err.message || err}`);
|
||||
@@ -526,7 +526,7 @@
|
||||
|
||||
// 更新设置
|
||||
async function updateSettings() {
|
||||
if (accountInfo.value) {
|
||||
if (accountInfo.value.id) {
|
||||
isLoading.value = true;
|
||||
try {
|
||||
const success = await SaveSetting('Queue', settings.value);
|
||||
@@ -550,7 +550,7 @@
|
||||
async function deleteQueue(values: ResponseQueueModel[]) {
|
||||
if (!values || values.length === 0) return;
|
||||
|
||||
if (accountInfo.value) { // 已登录,调用 API
|
||||
if (accountInfo.value.id) { // 已登录,调用 API
|
||||
isLoading.value = true;
|
||||
try {
|
||||
const idsToDelete = values.map((s) => s.id);
|
||||
@@ -578,7 +578,7 @@
|
||||
|
||||
// 取消所有活动队列项
|
||||
async function deactiveAllSongs() {
|
||||
if (accountInfo.value) { // 已登录,调用 API
|
||||
if (accountInfo.value.id) { // 已登录,调用 API
|
||||
isLoading.value = true;
|
||||
try {
|
||||
const data = await QueryGetAPI(QUEUE_API_URL + 'deactive');
|
||||
@@ -826,7 +826,7 @@
|
||||
|
||||
// 定时更新活动队列信息 (增量更新)
|
||||
async function updateActive() {
|
||||
if (!accountInfo.value) return; // 未登录则不执行
|
||||
if (!accountInfo.value.id) return; // 未登录则不执行
|
||||
try {
|
||||
const data = await QueryGetAPI<ResponseQueueModel[]>(QUEUE_API_URL + 'get-active', {
|
||||
id: accountInfo.value?.id,
|
||||
@@ -922,7 +922,7 @@
|
||||
async function init() {
|
||||
dispose(); // 先清理旧的计时器
|
||||
// 如果登录了,获取一次全量数据
|
||||
if (accountInfo.value) {
|
||||
if (accountInfo.value.id) {
|
||||
originQueue.value = await getAll();
|
||||
}
|
||||
// 设置定时器
|
||||
@@ -945,7 +945,7 @@
|
||||
// --- 生命周期钩子 ---
|
||||
onMounted(async () => {
|
||||
// 挂载时初始化
|
||||
if (accountInfo.value) {
|
||||
if (accountInfo.value.id) {
|
||||
// 如果已登录,同步一次设置到本地状态 (虽然 computed 会处理,但显式同步更清晰)
|
||||
settings.value = accountInfo.value.settings.queue;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user