From 8bed5bbc1af27f946b8b06989c61eb2b873932ed Mon Sep 17 00:00:00 2001 From: Megghy Date: Sat, 26 Apr 2025 01:35:59 +0800 Subject: [PATCH] =?UTF-8?q?=20=20feat:=20=E6=9B=B4=E6=96=B0=E7=BB=84?= =?UTF-8?q?=E4=BB=B6=E5=92=8C=E9=85=8D=E7=BD=AE=EF=BC=8C=E5=A2=9E=E5=BC=BA?= =?UTF-8?q?=E5=8A=9F=E8=83=BD=E5=92=8C=E7=94=A8=E6=88=B7=E4=BD=93=E9=AA=8C?= =?UTF-8?q?,=20=E6=B7=BB=E5=8A=A0=E7=AD=BE=E5=88=B0=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在 .editorconfig 中调整文件格式设置,统一代码风格。 - 在 default.d.ts 中为 naive-ui 添加 TabPaneSlots 接口声明,增强类型支持。 - 在多个组件中优化了模板和样式,提升用户交互体验。 - 在 ClientAutoAction.vue 中新增签到设置标签页,丰富功能选项。 - 在 Utils.ts 中增强 GUID 处理逻辑,增加输入验证和错误处理。 - 更新多个组件的逻辑,简化代码结构,提升可读性和维护性。 --- .editorconfig | 2 +- default.d.ts | 6 + src/Utils.ts | 59 +- src/client/ClientAutoAction.vue | 9 + src/client/ClientLayout.vue | 2 - .../autoaction/CheckInTemplateHelper.vue | 155 ++++ .../autoaction/SingleTemplateEditor.vue | 2 +- .../components/autoaction/TemplateEditor.vue | 93 +- .../autoaction/settings/CheckInSettings.vue | 398 +++++++++ .../autoaction/settings/TemplateSettings.vue | 2 +- src/client/data/initialize.ts | 9 +- src/client/data/utils.ts | 17 +- src/client/store/autoAction/actionUtils.ts | 2 +- .../store/autoAction/modules/checkin.ts | 436 ++++++++++ src/client/store/autoAction/utils.ts | 12 +- src/client/store/useAutoAction.ts | 120 ++- src/client/store/useBiliCookie.ts | 2 +- src/client/store/useBiliFunction.ts | 2 +- src/components.d.ts | 3 + src/components/SongList.vue | 12 +- src/components/TurnstileVerify.vue | 8 +- src/store/usePointStore.ts | 48 +- src/views/view/QuestionBoxView.vue | 819 ++++++++++++++---- .../TraditionalSongListTemplate.vue | 114 ++- 24 files changed, 2004 insertions(+), 328 deletions(-) create mode 100644 src/client/components/autoaction/CheckInTemplateHelper.vue create mode 100644 src/client/components/autoaction/settings/CheckInSettings.vue create mode 100644 src/client/store/autoAction/modules/checkin.ts diff --git a/.editorconfig b/.editorconfig index c6c70cd..384654f 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,4 +1,4 @@ -[*.{js,jsx,ts,tsx,vue,vine.ts}] +[*] indent_style = space indent_size = 2 end_of_line = lf diff --git a/default.d.ts b/default.d.ts index ca6e055..39c2151 100644 --- a/default.d.ts +++ b/default.d.ts @@ -15,6 +15,12 @@ declare module '*.js' { export = content } +declare module 'naive-ui' { + interface TabPaneSlots { + tab?: () => VNode[] + } +} + declare global { interface Window { $message: MessageProviderInst diff --git a/src/Utils.ts b/src/Utils.ts index 2f43172..f8aea7a 100644 --- a/src/Utils.ts +++ b/src/Utils.ts @@ -156,6 +156,9 @@ export function getOUIdAvatarUrl(ouid: string) { export class GuidUtils { // 将数字转换为GUID public static numToGuid(value: number): string { + if (!Number.isSafeInteger(value) || value < 0) { + throw new Error('输入必须是非负安全整数'); + } const buffer = new ArrayBuffer(16); const view = new DataView(buffer); view.setBigUint64(8, BigInt(value)); // 将数字写入后8个字节 @@ -164,44 +167,60 @@ export class GuidUtils { // 检查GUID是否由数字生成 public static isGuidFromUserId(guid: string): boolean { - const buffer = GuidUtils.guidToBuffer(guid); - const view = new DataView(buffer); - for (let i = 0; i < 8; i++) { - if (view.getUint8(i) !== 0) return false; // 检查前8个字节是否为0 + try { + const buffer = GuidUtils.guidToBuffer(guid); + const view = new DataView(buffer); + for (let i = 0; i < 8; i++) { + if (view.getUint8(i) !== 0) return false; // 检查前8个字节是否为0 + } + return true; + } catch (e) { + return false; } - return true; } // 将GUID转换为数字 public static guidToLong(guid: string): number { - const buffer = GuidUtils.guidToBuffer(guid); - const view = new DataView(buffer); - return Number(view.getBigUint64(8)); + try { + const buffer = GuidUtils.guidToBuffer(guid); + const view = new DataView(buffer); + return Number(view.getBigUint64(8)); + } catch (e) { + throw new Error('无效的GUID格式'); + } } // 辅助方法:将ArrayBuffer转换为GUID字符串 private static bufferToGuid(buffer: ArrayBuffer): string { const bytes = new Uint8Array(buffer); - const guid = bytes.reduce((str, byte, idx) => { - const pair = byte.toString(16).padStart(2, '0'); - return ( - str + - pair + - (idx === 3 || idx === 5 || idx === 7 || idx === 9 ? '-' : '') - ); - }, ''); - return guid; + let hex = ''; + + for (let i = 0; i < 16; i++) { + hex += bytes[i].toString(16).padStart(2, '0'); + } + + // 标准GUID格式:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx + return hex.substring(0, 8) + '-' + + hex.substring(8, 12) + '-' + + hex.substring(12, 16) + '-' + + hex.substring(16, 20) + '-' + + hex.substring(20); } // 辅助方法:将GUID字符串转换为ArrayBuffer private static guidToBuffer(guid: string): ArrayBuffer { const hex = guid.replace(/-/g, ''); - if (hex.length !== 32) throw new Error('Invalid GUID format.'); + if (hex.length !== 32) throw new Error('无效的GUID格式'); + const buffer = new ArrayBuffer(16); - const view = new DataView(buffer); + const view = new Uint8Array(buffer); + for (let i = 0; i < 16; i++) { - view.setUint8(i, parseInt(hex.substr(i * 2, 2), 16)); + const byteValue = parseInt(hex.substr(i * 2, 2), 16); + if (isNaN(byteValue)) throw new Error('GUID包含非法字符'); + view.set([byteValue], i); } + return buffer; } } diff --git a/src/client/ClientAutoAction.vue b/src/client/ClientAutoAction.vue index 0581697..c0e72f8 100644 --- a/src/client/ClientAutoAction.vue +++ b/src/client/ClientAutoAction.vue @@ -33,6 +33,7 @@ import GlobalScheduledSettings from './components/autoaction/settings/GlobalSche import TimerCountdown from './components/autoaction/TimerCountdown.vue'; import DataManager from './components/autoaction/DataManager.vue'; import ActionHistoryViewer from './components/autoaction/ActionHistoryViewer.vue'; +import CheckInSettings from './components/autoaction/settings/CheckInSettings.vue'; const autoActionStore = useAutoAction(); const message = useMessage(); @@ -699,6 +700,14 @@ onMounted(() => { + + + + + +
+ + + + +
+
+
+ 用户信息 +
+ {{user.name}} - 用户名称 +
+
+ {{user.uid}} - 用户ID +
+
+
+
+ 签到信息 +
+ {{checkin.points}} - 基础签到积分 +
+
+ {{checkin.bonusPoints}} - 早鸟额外积分 (普通签到为0) +
+
+ {{checkin.totalPoints}} - 总获得积分 +
+
+ {{checkin.isEarlyBird}} - 是否是早鸟签到 (true/false) +
+
+ {{checkin.cooldownSeconds}} - 签到冷却时间(秒) +
+
+ {{checkin.time}} - 签到时间对象 +
+
+
+ +
+
+ 示例模板: +
+ 普通签到: {{user.name}} 签到成功!获得 {{checkin.totalPoints}} 积分。 +
+
+ 早鸟签到: 恭喜 {{user.name}} 完成早鸟签到!额外获得 {{checkin.bonusPoints}} 积分,共获得 {{checkin.totalPoints}} 积分! +
+
+ 条件表达式: {{js: checkin.isEarlyBird ? `恭喜 ${user.name} 获得早鸟奖励!` : `${user.name} 签到成功!`}} 获得 {{checkin.totalPoints}} 积分。 +
+
+
+
+ + + + + diff --git a/src/client/components/autoaction/SingleTemplateEditor.vue b/src/client/components/autoaction/SingleTemplateEditor.vue index 906c136..1600322 100644 --- a/src/client/components/autoaction/SingleTemplateEditor.vue +++ b/src/client/components/autoaction/SingleTemplateEditor.vue @@ -31,7 +31,7 @@ function handleTemplateUpdate(payload: { index: number, value: string }) { diff --git a/src/store/usePointStore.ts b/src/store/usePointStore.ts index 113eeaf..e046006 100644 --- a/src/store/usePointStore.ts +++ b/src/store/usePointStore.ts @@ -4,6 +4,7 @@ import { POINT_API_URL } from "@/data/constants"; import { MessageApiInjection } from "naive-ui/es/message/src/MessageProvider"; import { defineStore } from "pinia"; import { useAuthStore } from "./useAuthStore"; +import { GuidUtils } from "@/Utils"; export const usePointStore = defineStore('point', () => { const useAuth = useAuthStore() @@ -41,9 +42,54 @@ export const usePointStore = defineStore('point', () => { } return [] } + /** + * 给用户添加或扣除积分 + * @param userId 用户ID + * @param count 积分数量(正数为增加,负数为减少) + * @param reason 积分变动原因 + * @param remark 备注信息 + * @returns 成功时返回修改后的积分值,失败时返回null + */ + async function addPoints(userId: string, count: number, reason: string, remark?: string) { + if (count === 0) { + console.warn('[point] 积分变动数量不能为0'); + return null; + } + + try { + // 根据用户ID构建参数 + const params: Record = GuidUtils.isGuidFromUserId(userId) ? { + uId: GuidUtils.guidToLong(userId), + count: count, + reason: reason || '', + } : { + oid: userId, + count: count, + reason: reason || '', + }; + + if (remark) { + params.remark = remark; + } + + const data = await QueryGetAPI(POINT_API_URL + 'give-point', params); + + if (data.code === 200) { + console.log(`[point] 用户 ${userId} 积分${count > 0 ? '增加' : '减少'} ${Math.abs(count)} 成功,当前积分:${data.data}`); + return data.data; // 返回修改后的积分值 + } else { + console.error('[point] 积分操作失败:', data.message); + return null; + } + } catch (err) { + console.error('[point] 积分操作出错:', err); + return null; + } + } return { GetSpecificPoint, - GetGoods + GetGoods, + addPoints } }) \ No newline at end of file diff --git a/src/views/view/QuestionBoxView.vue b/src/views/view/QuestionBoxView.vue index a256257..406db05 100644 --- a/src/views/view/QuestionBoxView.vue +++ b/src/views/view/QuestionBoxView.vue @@ -206,197 +206,289 @@ onUnmounted(() => {