feat: 添加积分兑换通知功能;优化通知处理逻辑;更新设置以支持新通知类型

This commit is contained in:
2025-04-09 12:45:30 +08:00
parent 6687888c97
commit 655b92081e
5 changed files with 113 additions and 47 deletions

View File

@@ -240,7 +240,7 @@ import { invoke } from '@tauri-apps/api/core';
:bordered="false" :bordered="false"
> >
<NAlert type="warning"> <NAlert type="warning">
未完成 完全完成
</NAlert> </NAlert>
<NDivider /> <NDivider />
<NSpace vertical> <NSpace vertical>
@@ -262,6 +262,14 @@ import { invoke } from '@tauri-apps/api/core';
<component :is="renderNotifidactionEnable('question-box')" /> <component :is="renderNotifidactionEnable('question-box')" />
</template> </template>
</NCard> </NCard>
<NCard
size="small"
title="积分兑换通知"
>
<template #header-extra>
<component :is="renderNotifidactionEnable('goods-buy')" />
</template>
</NCard>
<NCard <NCard
size="small" size="small"
title="弹幕相关" title="弹幕相关"

View File

@@ -1,19 +1,21 @@
<script setup lang="ts"> <script setup lang="ts">
import { isPermissionGranted, onAction, sendNotification } from '@tauri-apps/plugin-notification'; import { isPermissionGranted, onAction, sendNotification } from '@tauri-apps/plugin-notification';
import { NSwitch } from 'naive-ui';
import { useSettings } from './store/useSettings';
import { onReceivedQuestion } from './data/notification';
import { QAInfo } from '@/api/api-models';
const setting = useSettings()
async function testNotification() { async function testNotification() {
let permissionGranted = await isPermissionGranted(); onReceivedQuestion({
if (permissionGranted) { id: 1,
sendNotification({ question: {
title: "测试通知", message: '这是一条测试问题',
body: "这是一个测试通知", },
silent: false, tag: '测试标签',
extra: { type: 'test' }, sender: { name: '测试用户', id: 1, isBiliAuthed: false },
}); isPublic: true,
onAction((event) => { } as QAInfo);
console.log('Notification clicked:', event);
});
}
} }
</script> </script>
@@ -26,6 +28,12 @@ async function testNotification() {
> >
测试通知 测试通知
</NButton> </NButton>
<LabelItem label="关闭弹幕客户端">
<NSwitch
v-model:value="setting.settings.dev_disableDanmakuClient"
@update:value="setting.save()"
/>
</LabelItem>
</NFlex> </NFlex>
</div> </div>
</template> </template>

View File

@@ -29,20 +29,9 @@ export async function initAll(isOnBoot: boolean) {
if (clientInited.value) { if (clientInited.value) {
return; return;
} }
if (isOnBoot) {
if (setting.settings.bootAsMinimized && !isDev) {
const appWindow = getCurrentWindow();
appWindow.hide();
sendNotification({
title: "VTsuru.Client",
body: '已启动并最小化到托盘',
silent: false,
extra: { type: 'question-box' },
});
}
}
let permissionGranted = await isPermissionGranted();
checkUpdate(); checkUpdate();
const appWindow = getCurrentWindow();
let permissionGranted = await isPermissionGranted();
// If not we need to request it // If not we need to request it
if (!permissionGranted) { if (!permissionGranted) {
@@ -51,7 +40,16 @@ export async function initAll(isOnBoot: boolean) {
if (permissionGranted) { if (permissionGranted) {
info('Notification permission granted'); info('Notification permission granted');
} }
}
if (isOnBoot) {
if (setting.settings.bootAsMinimized && !isDev && await appWindow.isVisible()) {
appWindow.hide();
sendNotification({
title: "VTsuru.Client",
body: '已启动并最小化到托盘'
});
}
} }
initNotificationHandler(); initNotificationHandler();
const detach = await attachConsole(); const detach = await attachConsole();
@@ -64,7 +62,7 @@ export async function initAll(isOnBoot: boolean) {
initInfo(); initInfo();
info('[init] 开始更新数据'); info('[init] 开始更新数据');
if (isLoggedIn && accountInfo.value.isBiliVerified) { if (isLoggedIn && accountInfo.value.isBiliVerified && !setting.settings.dev_disableDanmakuClient) {
const danmakuInitNoticeRef = window.$notification.info({ const danmakuInitNoticeRef = window.$notification.info({
title: '正在初始化弹幕客户端...', title: '正在初始化弹幕客户端...',
closable: false closable: false
@@ -96,7 +94,6 @@ export async function initAll(isOnBoot: boolean) {
], ],
}); });
const iconData = await (await fetch('https://oss.suki.club/vtsuru/icon.ico')).arrayBuffer(); const iconData = await (await fetch('https://oss.suki.club/vtsuru/icon.ico')).arrayBuffer();
const appWindow = getCurrentWindow();
const options: TrayIconOptions = { const options: TrayIconOptions = {
// here you can add a tray menu, title, tooltip, event handler, etc // here you can add a tray menu, title, tooltip, event handler, etc
menu: menu, menu: menu,

View File

@@ -1,16 +1,28 @@
import { QAInfo } from "@/api/api-models"; import { QAInfo, ResponsePointGoodModel, ResponsePointOrder2OwnerModel } from "@/api/api-models";
import { useSettings } from "../store/useSettings"; import { useSettings } from "../store/useSettings";
import { isPermissionGranted, onAction, sendNotification } from "@tauri-apps/plugin-notification"; import { isPermissionGranted, onAction, Options, sendNotification } from "@tauri-apps/plugin-notification";
import { openUrl } from "@tauri-apps/plugin-opener"; import { openUrl } from "@tauri-apps/plugin-opener";
import { CN_HOST } from "@/data/constants"; import { CN_HOST } from "@/data/constants";
import { NButton, NFlex } from "naive-ui";
import QuestionItem from "@/components/QuestionItem.vue";
export function onReceivedNotification(type: string, data: any) { export async function trySendNotification(option: Options) {
let permissionGranted = await isPermissionGranted();
if (permissionGranted) {
sendNotification(option);
}
}
export function onReceivedNotification(type: string, json: string) {
console.log(`接收到通知: ${type}`, json);
const data = JSON.parse(json);
switch (type) { switch (type) {
case 'question-box': case 'question-box':
onReceivedQuestion(data); onReceivedQuestion(data);
break; break;
case 'goods-buy':
onGoodsBuy(data);
break;
default: default:
console.warn(`Unhandled notification type: ${type}`); console.warn(`Unhandled notification type: ${type}`);
} }
@@ -20,20 +32,57 @@ export async function onReceivedQuestion(question: QAInfo) {
const setting = useSettings(); const setting = useSettings();
if (setting.settings.notificationSettings.enableTypes.includes("question-box")) { if (setting.settings.notificationSettings.enableTypes.includes("question-box")) {
window.$notification.info({ window.$notification.info({
title: "收到提问", title: "提问",
description: '收到来自 [' + question.sender.name || '匿名用户' + '] 的提问', description: '收到来自 [' + (question.sender.name || '匿名用户') + '] 的提问',
duration: 5, duration: 0,
action: () => h(NFlex, {}, () => [
h(NButton, {
text: true, type: 'info', onClick: () => {
window.$modal.create({
title: '快速查看',
preset: 'card',
style: { maxWidth: '80vw' },
content: () => h(QuestionItem, { item: question }),
});
}
}, () => '快速查看'),
h(NButton, {
text: true, type: 'primary', onClick: () => {
openUrl(`${CN_HOST}manage/question-box`);
}
}, () => '查看详情'),
])
});
trySendNotification({
title: "提问箱",
body: '收到来自 [' + (question.sender.name || '匿名用户') + '] 的提问',
extra: { type: 'question-box' },
});
}
}
export function onGoodsBuy(info: {
data: ResponsePointOrder2OwnerModel,
goods: ResponsePointGoodModel
}) {
const setting = useSettings();
const order = info.data;
const goods = info.goods;
if (setting.settings.notificationSettings.enableTypes.includes("goods-buy")) {
window.$notification.info({
title: "礼物兑换",
description: `${order.customer.name} 兑换了你的 [${goods.name}],数量: ${order.count},总价: ${order.point}`,
duration: 0,
action: () => h(NButton, {
text: true, type: 'primary', onClick: () => {
openUrl(`${CN_HOST}manage/goods-buy`);
}
}, () => '查看详情'),
});
trySendNotification({
title: "礼物兑换",
body: `${order.customer.name} 兑换了你的 [${goods.name}],数量: ${order.count},总价: ${order.point}`,
extra: { type: 'goods-buy' },
}); });
let permissionGranted = await isPermissionGranted();
if (permissionGranted) {
sendNotification({
title: "收到提问",
body: '来自 [' + question.sender.name || '匿名用户' + '] 的提问',
silent: false,
extra: { type: 'question-box' },
});
}
} }
} }

View File

@@ -1,6 +1,6 @@
import { useTauriStore } from './useTauriStore'; import { useTauriStore } from './useTauriStore';
export type NotificationType = 'question-box' | 'danmaku'; export type NotificationType = 'question-box' | 'danmaku' | 'goods-buy';
export type NotificationSettings = { export type NotificationSettings = {
enableTypes: NotificationType[]; enableTypes: NotificationType[];
}; };
@@ -14,6 +14,8 @@ export type VTsuruClientSettings = {
enableNotification: boolean; enableNotification: boolean;
notificationSettings: NotificationSettings; notificationSettings: NotificationSettings;
dev_disableDanmakuClient: boolean;
}; };
export const useSettings = defineStore('settings', () => { export const useSettings = defineStore('settings', () => {
@@ -29,6 +31,8 @@ export const useSettings = defineStore('settings', () => {
notificationSettings: { notificationSettings: {
enableTypes: ['question-box', 'danmaku'], enableTypes: ['question-box', 'danmaku'],
}, },
dev_disableDanmakuClient: false,
}; };
const settings = ref<VTsuruClientSettings>(Object.assign({}, defaultSettings)); const settings = ref<VTsuruClientSettings>(Object.assign({}, defaultSettings));