From 7f6953735fe0b71fdaf8e8462715c36785e23806 Mon Sep 17 00:00:00 2001 From: wintbit Date: Sat, 28 Mar 2026 21:23:57 +0800 Subject: [PATCH 01/24] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E5=BC=B9?= =?UTF-8?q?=E5=B9=95=E6=A0=B7=E5=BC=8F=E6=94=AF=E6=8C=81=EF=BC=8C=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E5=BC=B9=E5=B9=95=E5=B1=9E=E6=80=A7=E5=A4=84=E7=90=86?= =?UTF-8?q?=E9=80=BB=E8=BE=91=EF=BC=9B=E6=9B=B4=E6=96=B0=E7=9B=B8=E5=85=B3?= =?UTF-8?q?=E7=BB=84=E4=BB=B6=E4=BB=A5=E6=94=AF=E6=8C=81=E6=A8=A1=E5=BC=8F?= =?UTF-8?q?=E5=92=8C=E9=A2=9C=E8=89=B2=E8=AE=BE=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/live/LivePlayer.vue | 12 +++++++- src/danmu/DanmuService.ts | 46 ++++++++++++++++++++++++++---- src/types/api.ts | 7 +++++ 3 files changed, 58 insertions(+), 7 deletions(-) diff --git a/src/components/live/LivePlayer.vue b/src/components/live/LivePlayer.vue index 6553291..d98ef80 100644 --- a/src/components/live/LivePlayer.vue +++ b/src/components/live/LivePlayer.vue @@ -98,6 +98,15 @@ async function sendDanmuByRealtime(d: Danmu): Promise { }), }; + const m = d?.mode; + if (m === 0 || m === 1 || m === 2) { + myAttributes.mode = m; + } + const c = typeof d?.color === 'string' ? d.color.trim() : ''; + if (c) { + myAttributes.color = c; + } + try { await danmuService.value.sendMessage(content, myAttributes); } catch (error) { @@ -145,7 +154,8 @@ function pushDanmuToPlayer(msg: DanmuMessage) { const payload = { text: msg.text, - color: '#FFFFFF', + color: msg.color ?? '#FFFFFF', + mode: msg.mode ?? 0, time: 0, border: msg.nickname === userInfoStore.userInfo?.nickname, }; diff --git a/src/danmu/DanmuService.ts b/src/danmu/DanmuService.ts index b9db743..ac7a3e5 100644 --- a/src/danmu/DanmuService.ts +++ b/src/danmu/DanmuService.ts @@ -1,6 +1,6 @@ import { useLocalStorage } from '@vueuse/core'; import { v4 as uuid } from 'uuid'; -import type { DanmuAttributes, DanmuMessage } from '../types/api'; +import type { DanmuAttributes, DanmuMessage, DanmuMode } from '../types/api'; import { fetchJson } from '../api/http'; import { buildLiveJsonUrl } from '../utils/urlProxy'; @@ -76,6 +76,28 @@ async function getSharedImClient(): Promise { return sharedImClientInitPromise; } +function normalizeDanmuStyleFromAttrs(attrs: Record): Pick { + const out: Pick = {}; + const rawMode = attrs.mode; + let n: number | undefined; + if (typeof rawMode === 'number' && Number.isFinite(rawMode)) { + n = Math.round(rawMode); + } else if (typeof rawMode === 'string' && rawMode.trim() !== '') { + const parsed = Number(rawMode); + if (Number.isFinite(parsed)) { + n = Math.round(parsed); + } + } + if (n !== undefined && n >= 0 && n <= 2) { + out.mode = n as DanmuMode; + } + const rawColor = attrs.color; + if (typeof rawColor === 'string' && rawColor.trim()) { + out.color = rawColor.trim(); + } + return out; +} + interface DanmuServiceHandlers { onMessage?: (msg: DanmuMessage) => void; onError?: (error: unknown) => void; @@ -165,17 +187,20 @@ export class DanmuService { private handleRawMessage(message: any, TextMessage: any, source: 'realtime' | 'history'): void { if (message instanceof TextMessage) { const attrs = message.getAttributes?.() || {}; + const attrsRec = attrs as Record; const text = message.getText?.() || ''; + const style = normalizeDanmuStyleFromAttrs(attrsRec); const danmu: DanmuMessage = { id: this.toDanmuId(message, source), timestamp: this.toTimestamp((message as any).timestamp), text: text, - username: attrs.username || '匿名用户', - nickname: attrs.nickname || '', - schoolName: attrs.schoolName || '', - badge: attrs.badge || '', + username: String(attrsRec.username ?? '匿名用户'), + nickname: String(attrsRec.nickname ?? ''), + schoolName: String(attrsRec.schoolName ?? ''), + badge: String(attrsRec.badge ?? ''), source, + ...style, }; this.emitDanmu(danmu); return; @@ -183,9 +208,10 @@ export class DanmuService { const content = message?.content; const rawText = content?._lctext ?? content?.text ?? ''; - const rawAttrs = content?._lcattrs ?? message?.attributes ?? {}; + const rawAttrs = (content?._lcattrs ?? message?.attributes ?? {}) as Record; if (rawText) { + const style = normalizeDanmuStyleFromAttrs(rawAttrs); const danmu: DanmuMessage = { id: this.toDanmuId(message, source), timestamp: this.toTimestamp(message?.timestamp ?? rawAttrs.sendTime), @@ -195,6 +221,7 @@ export class DanmuService { schoolName: String(rawAttrs.schoolName || ''), badge: String(rawAttrs.badge || ''), source, + ...style, }; this.emitDanmu(danmu); } @@ -354,6 +381,7 @@ export class DanmuService { const id = String(rec.id ?? rec.messageId ?? rec.msgId ?? `api-${chatRoomId}-${idx}`); const timestampValue = rec.timestamp ?? rec.sendTime ?? attrs.sendTime ?? nestedAttrs.sendTime; const timestamp = this.toTimestamp(timestampValue); + const style = normalizeDanmuStyleFromAttrs({ ...nestedAttrs, ...attrs }); return { id, @@ -364,6 +392,7 @@ export class DanmuService { schoolName: String(attrs.schoolName ?? nestedAttrs.schoolName ?? rec.schoolName ?? ''), badge: String(attrs.badge ?? nestedAttrs.badge ?? rec.badge ?? ''), source: 'history', + ...style, } as DanmuMessage; }) .filter((v): v is DanmuMessage => !!v); @@ -397,6 +426,10 @@ export class DanmuService { message.setAttributes(attrs); await this.conversationInstance.send(message); + const style = normalizeDanmuStyleFromAttrs({ + mode: attrs.mode as unknown, + color: attrs.color as unknown, + }); this.emitDanmu({ id: uuid(), timestamp: Date.now(), @@ -406,6 +439,7 @@ export class DanmuService { schoolName: attrs.schoolName, badge: attrs.badge, source: 'realtime', + ...style, }); } catch (error) { this.handlers.onError?.(error); diff --git a/src/types/api.ts b/src/types/api.ts index 1a63c1b..79619f5 100644 --- a/src/types/api.ts +++ b/src/types/api.ts @@ -83,12 +83,17 @@ export interface ScheduleEntry extends AnyRecord { export type Schedule = ScheduleEntry[] | AnyRecord; // Danmu (Comments via Leancloud Realtime) +/** artplayer-plugin-danmuku: 0 scroll, 1 top, 2 bottom */ +export type DanmuMode = 0 | 1 | 2; + export interface DanmuData { text: string; username: string; nickname: string; schoolName: string; badge: string; + mode?: DanmuMode; + color?: string; } export interface DanmuAttributes { @@ -99,6 +104,8 @@ export interface DanmuAttributes { position: string; isAdmin: boolean; username?: string; + mode?: DanmuMode; + color?: string; } export interface DanmuMessage extends DanmuData { From e06598b41ca6467a3bdc7b70a40c710a86ab4dfa Mon Sep 17 00:00:00 2001 From: wintbit Date: Sat, 28 Mar 2026 21:32:11 +0800 Subject: [PATCH 02/24] =?UTF-8?q?feat:=20=E6=9B=B4=E6=96=B0=20ScheduleList?= =?UTF-8?q?=20=E7=BB=84=E4=BB=B6=E6=A0=B7=E5=BC=8F=EF=BC=8C=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=E6=97=A5=E6=9C=9F=E5=88=86=E9=9A=94=E7=AC=A6=E5=9B=BE?= =?UTF-8?q?=E6=A0=87=E5=92=8C=E6=A0=B7=E5=BC=8F=EF=BC=9B=E8=B0=83=E6=95=B4?= =?UTF-8?q?=20overflow-x=20=E5=B1=9E=E6=80=A7=E4=BB=A5=E6=94=B9=E5=96=84?= =?UTF-8?q?=E5=B8=83=E5=B1=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/panels/ScheduleList.vue | 36 +++++++++++++++++++++----- src/styles/primevue-theme.css | 3 ++- 2 files changed, 32 insertions(+), 7 deletions(-) diff --git a/src/components/panels/ScheduleList.vue b/src/components/panels/ScheduleList.vue index c9a0477..23b855f 100644 --- a/src/components/panels/ScheduleList.vue +++ b/src/components/panels/ScheduleList.vue @@ -34,7 +34,10 @@ function onTeamSelect(payload: TeamSelectPayload) {
- {{ group.dateLabel }} + +