mirror of
https://github.com/Megghy/vtsuru.live.git
synced 2025-12-07 02:46:55 +08:00
272 lines
8.4 KiB
TypeScript
272 lines
8.4 KiB
TypeScript
import type { ComputedRef, Ref } from 'vue'
|
||
import type { DanmakuWindowSettings } from '../../store/useDanmakuWindow'
|
||
import type { EventModel } from '@/api/api-models'
|
||
import { computed } from 'vue'
|
||
import { EventDataTypes } from '@/api/api-models'
|
||
import { GetGuardColor } from '@/Utils'
|
||
|
||
// 粉丝勋章等级对应的颜色
|
||
export const MEDAL_LEVEL_COLORS: { [key: number]: string } = {
|
||
1: '#68a49a',
|
||
2: '#5b9a8f',
|
||
3: '#539288',
|
||
4: '#4e8a80',
|
||
5: '#607ea0',
|
||
6: '#54708f',
|
||
7: '#4e6887',
|
||
8: '#49617e',
|
||
9: '#8d7a9b',
|
||
10: '#816d8f',
|
||
11: '#776385',
|
||
12: '#6e5a7c',
|
||
13: '#c06d80',
|
||
14: '#b66174',
|
||
15: '#ac586a',
|
||
16: '#a34f61',
|
||
17: '#caa44a',
|
||
18: '#bf973e',
|
||
19: '#b68c35',
|
||
20: '#ae812f',
|
||
21: '#347368',
|
||
22: '#2e685e',
|
||
23: '#285e55',
|
||
24: '#25564e',
|
||
25: '#354b86',
|
||
26: '#2e4179',
|
||
27: '#293a6f',
|
||
28: '#243466',
|
||
29: '#624180',
|
||
30: '#573873',
|
||
31: '#4f3168',
|
||
32: '#482b5f',
|
||
33: '#a23e54',
|
||
34: '#92364a',
|
||
35: '#843042',
|
||
36: '#772a3b',
|
||
37: '#f38b3c',
|
||
38: '#e87b2e',
|
||
39: '#de6e23',
|
||
40: '#d5621a',
|
||
}
|
||
|
||
// 获取粉丝勋章颜色
|
||
export function getMedalColor(level: number): string {
|
||
// 处理超过40级的情况,颜色循环使用
|
||
if (level > 40) {
|
||
level = 40
|
||
}
|
||
|
||
// 如果找不到对应等级颜色或者等级小于1,返回默认颜色
|
||
return MEDAL_LEVEL_COLORS[level] || '#999999'
|
||
}
|
||
|
||
export interface BaseDanmakuItemProps {
|
||
item: EventModel & { randomId: string, isNew?: boolean, disappearAt?: number }
|
||
setting: DanmakuWindowSettings
|
||
}
|
||
|
||
export function useDanmakuUtils(
|
||
props: BaseDanmakuItemProps,
|
||
emojiData: Ref<{ updateAt?: number, data: { inline: { [key: string]: string }, plain: { [key: string]: string } } }>
|
||
| { data: { inline: { [key: string]: string }, plain: { [key: string]: string } } },
|
||
) {
|
||
// 计算SC弹幕的颜色类
|
||
const scColorClass = computed(() => {
|
||
if (props.item.type === EventDataTypes.SC) {
|
||
const price = props.item?.price || 0
|
||
if (price === 0) return 'sc-0'
|
||
if (price > 0 && price < 50) return 'sc-50'
|
||
if (price >= 50 && price < 100) return 'sc-100'
|
||
if (price >= 100 && price < 500) return 'sc-500'
|
||
if (price >= 500 && price < 1000) return 'sc-1000'
|
||
if (price >= 1000 && price < 2000) return 'sc-2000'
|
||
if (price >= 2000) return 'sc-max'
|
||
}
|
||
return ''
|
||
})
|
||
|
||
// 根据类型计算样式
|
||
const typeClass = computed(() => {
|
||
switch (props.item.type) {
|
||
case EventDataTypes.Message: return 'message-item'
|
||
case EventDataTypes.Gift: return 'gift-item'
|
||
case EventDataTypes.SC: return `sc-item ${scColorClass.value}`
|
||
case EventDataTypes.Guard: return 'guard-item'
|
||
case EventDataTypes.Enter: return 'enter-item'
|
||
case EventDataTypes.Like: return 'like-item'
|
||
default: return ''
|
||
}
|
||
})
|
||
|
||
// 获取舰长颜色
|
||
const guardColor = computed(() => GetGuardColor(props.item.guard_level))
|
||
|
||
// 舰长样式类
|
||
const guardLevelClass = computed(() => {
|
||
if (props.item.type === EventDataTypes.Guard) {
|
||
return `guard-level-${props.item.guard_level || 0}`
|
||
}
|
||
return ''
|
||
})
|
||
|
||
// 检查是否需要显示头像
|
||
const showAvatar = computed(() => props.setting.showAvatar)
|
||
|
||
// 解析包含内联表情的消息
|
||
const parsedMessage = computed<{ type: 'text' | 'emoji', content?: string, url?: string, name?: string }[]>(() => {
|
||
// 仅处理非纯表情的普通消息
|
||
if (props.item.type !== EventDataTypes.Message || props.item.emoji || !props.item.msg) {
|
||
return []
|
||
}
|
||
|
||
const segments: { type: 'text' | 'emoji', content?: string, url?: string, name?: string }[] = []
|
||
let lastIndex = 0
|
||
const regex = /\[([^\]]+)\]/g // 匹配 [表情名]
|
||
let match
|
||
|
||
try {
|
||
// 兼容 Ref 和 普通对象两种传参
|
||
const store = (emojiData as any)?.value ?? emojiData
|
||
const availableEmojis = (store?.data ?? { inline: {}, plain: {} }) as {
|
||
inline?: { [key: string]: string }
|
||
plain?: { [key: string]: string }
|
||
}
|
||
|
||
while ((match = regex.exec(props.item.msg)) !== null) {
|
||
// 添加表情前的文本部分
|
||
if (match.index > lastIndex) {
|
||
segments.push({ type: 'text', content: props.item.msg.substring(lastIndex, match.index) })
|
||
}
|
||
|
||
const emojiFullName = match[0] // 完整匹配,例如 "[哈哈]"
|
||
const emojiName = match[1] // 去除方括号后的名称,例如 "哈哈"
|
||
// 兼容键名为带/不带方括号的两种情况
|
||
const emojiInfo = (availableEmojis.inline?.[emojiFullName]
|
||
?? availableEmojis.inline?.[emojiName]
|
||
?? availableEmojis.plain?.[emojiFullName]
|
||
?? availableEmojis.plain?.[emojiName])
|
||
|
||
if (emojiInfo) {
|
||
// 找到了表情
|
||
segments.push({ type: 'emoji', url: emojiInfo, name: emojiFullName })
|
||
} else {
|
||
// 未找到表情,当作普通文本处理
|
||
segments.push({ type: 'text', content: emojiFullName })
|
||
}
|
||
|
||
lastIndex = regex.lastIndex
|
||
}
|
||
|
||
// 添加最后一个表情后的文本部分
|
||
if (lastIndex < props.item.msg.length) {
|
||
segments.push({ type: 'text', content: props.item.msg.substring(lastIndex) })
|
||
}
|
||
} catch (error) {
|
||
console.error('Error parsing message for emojis:', error)
|
||
// 解析出错时,返回原始文本
|
||
return [{ type: 'text', content: props.item.msg }]
|
||
}
|
||
|
||
// 如果解析后为空(例如,消息只包含无法识别的[]),则返回原始文本
|
||
if (segments.length === 0 && props.item.msg) {
|
||
return [{ type: 'text', content: props.item.msg }]
|
||
}
|
||
|
||
return segments
|
||
})
|
||
|
||
// 获取不同类型消息的显示标签
|
||
const typeLabel = computed(() => {
|
||
switch (props.item.type) {
|
||
case EventDataTypes.Message: return '' // 普通消息不需要标签
|
||
case EventDataTypes.Gift: return '【礼物】'
|
||
case EventDataTypes.SC: return '【SC】'
|
||
case EventDataTypes.Guard: return '【舰长】'
|
||
case EventDataTypes.Enter: return '【进场】'
|
||
case EventDataTypes.Like: return '【点赞】'
|
||
default: return ''
|
||
}
|
||
})
|
||
|
||
// 获取礼物或SC的价格文本
|
||
const priceText = computed(() => {
|
||
if (props.item.type === EventDataTypes.SC
|
||
|| (props.item.type === EventDataTypes.Gift && props.item.price > 0)) {
|
||
return `¥${props.item.price || 0}`
|
||
}
|
||
return ''
|
||
})
|
||
|
||
// 获取用户名显示
|
||
const displayName = computed(() => {
|
||
return props.item.uname || '匿名用户'
|
||
})
|
||
|
||
// 获取消息显示内容
|
||
const displayContent = computed(() => {
|
||
switch (props.item.type) {
|
||
case EventDataTypes.Message:
|
||
return props.item.msg || ''
|
||
case EventDataTypes.Gift:
|
||
return `${props.item.num || 1} × ${props.item.msg}`
|
||
case EventDataTypes.SC:
|
||
return props.item.msg || ''
|
||
case EventDataTypes.Guard:
|
||
return props.item.msg || '开通了舰长'
|
||
case EventDataTypes.Enter:
|
||
return '进入了直播间'
|
||
case EventDataTypes.Like:
|
||
return '点赞了'
|
||
default:
|
||
return ''
|
||
}
|
||
})
|
||
|
||
// 根据风格及类型获取文本颜色
|
||
const textModeColor = computed(() => {
|
||
if (props.item.type === EventDataTypes.SC) {
|
||
return '#FFD700' // SC消息金色
|
||
} else if (props.item.type === EventDataTypes.Gift) {
|
||
return '#FF69B4' // 礼物消息粉色
|
||
} else if (props.item.type === EventDataTypes.Guard) {
|
||
return guardColor.value // 舰长消息使用舰长颜色
|
||
} else if (props.item.type === EventDataTypes.Enter) {
|
||
return '#67C23A' // 入场消息绿色
|
||
} else if (props.item.type === EventDataTypes.Like) {
|
||
return '#F56C6C' // 点赞消息红色
|
||
}
|
||
return undefined // 普通消息使用默认颜色
|
||
})
|
||
|
||
// 计算粉丝勋章颜色
|
||
const medalColor = computed(() => {
|
||
if (props.item.fans_medal_level && props.item.fans_medal_level > 0) {
|
||
return getMedalColor(props.item.fans_medal_level)
|
||
}
|
||
return '#999999' // 默认颜色
|
||
})
|
||
|
||
return {
|
||
scColorClass,
|
||
typeClass,
|
||
guardColor,
|
||
guardLevelClass,
|
||
showAvatar,
|
||
parsedMessage,
|
||
typeLabel,
|
||
priceText,
|
||
displayName,
|
||
displayContent,
|
||
textModeColor,
|
||
medalColor, // 添加粉丝勋章颜色计算属性
|
||
}
|
||
}
|
||
|
||
// 返回类型定义,便于TypeScript类型推断
|
||
export type DanmakuUtils = ReturnType<typeof useDanmakuUtils>
|
||
|
||
// 类型别名,用于清晰表达每个计算属性的类型
|
||
export type ComputedDanmakuUtils = {
|
||
[K in keyof DanmakuUtils]: ComputedRef<any>
|
||
}
|