mirror of
https://github.com/Megghy/vtsuru.live.git
synced 2025-12-10 20:36:55 +08:00
feat: 更新依赖和移除不必要的文件, 更新歌单管理列表在小屏幕上的显示效果, 修复自定义配置文件加载
- 在 package.json 中移除不再使用的依赖项,并更新部分依赖版本 - 删除多个不再使用的组件和文件,包括 CheckInTemplateHelper.vue、CommonConfigItems.vue、GlobalSettingsConfig.vue 等 - 更新 bun.lockb 文件以反映依赖变更
This commit is contained in:
@@ -1,155 +0,0 @@
|
||||
<template>
|
||||
<div class="checkin-template-helper">
|
||||
<TemplateHelper :placeholders="checkInPlaceholders" />
|
||||
<NAlert
|
||||
type="info"
|
||||
:show-icon="false"
|
||||
style="margin-top: 8px;"
|
||||
>
|
||||
<template #header>
|
||||
<div class="alert-header">
|
||||
<NIcon
|
||||
:component="Info24Filled"
|
||||
style="margin-right: 4px;"
|
||||
/>
|
||||
签到模板可用变量列表
|
||||
</div>
|
||||
</template>
|
||||
<NDivider style="margin: 6px 0;" />
|
||||
<div class="placeholder-groups">
|
||||
<div class="placeholder-group">
|
||||
<div class="group-title">
|
||||
用户信息
|
||||
</div> <div class="placeholder-item">
|
||||
<code>{{user.name}}</code> - 用户名称
|
||||
</div>
|
||||
<div class="placeholder-item">
|
||||
<code>{{user.uid}}</code> - 用户ID
|
||||
</div>
|
||||
</div>
|
||||
<div class="placeholder-group">
|
||||
<div class="group-title">
|
||||
签到信息
|
||||
</div> <div class="placeholder-item">
|
||||
<code>{{checkin.points}}</code> - 基础签到积分
|
||||
</div>
|
||||
<div class="placeholder-item">
|
||||
<code>{{checkin.bonusPoints}}</code> - 早鸟额外积分 (普通签到为0)
|
||||
</div>
|
||||
<div class="placeholder-item">
|
||||
<code>{{checkin.totalPoints}}</code> - 总获得积分
|
||||
</div>
|
||||
<div class="placeholder-item">
|
||||
<code>{{checkin.isEarlyBird}}</code> - 是否是早鸟签到 (true/false)
|
||||
</div>
|
||||
<div class="placeholder-item">
|
||||
<code>{{checkin.cooldownSeconds}}</code> - 签到冷却时间(秒)
|
||||
</div>
|
||||
<div class="placeholder-item">
|
||||
<code>{{checkin.time}}</code> - 签到时间对象
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<NDivider style="margin: 6px 0;" />
|
||||
<div class="placeholder-example">
|
||||
<div class="example-title">
|
||||
示例模板:
|
||||
</div> <div class="example-item">
|
||||
普通签到: <code>{{user.name}} 签到成功!获得 {{checkin.totalPoints}} 积分。</code>
|
||||
</div>
|
||||
<div class="example-item">
|
||||
早鸟签到: <code>恭喜 {{user.name}} 完成早鸟签到!额外获得 {{checkin.bonusPoints}} 积分,共获得 {{checkin.totalPoints}} 积分!</code>
|
||||
</div>
|
||||
<div class="example-item">
|
||||
条件表达式: <code>{{js: checkin.isEarlyBird ? `恭喜 ${user.name} 获得早鸟奖励!` : `${user.name} 签到成功!`}} 获得 {{checkin.totalPoints}} 积分。</code>
|
||||
</div>
|
||||
</div>
|
||||
</NAlert>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue';
|
||||
import { NAlert, NDivider, NIcon } from 'naive-ui';
|
||||
import { Info24Filled } from '@vicons/fluent';
|
||||
import TemplateHelper from './TemplateHelper.vue';
|
||||
|
||||
// 签到模板的特定占位符
|
||||
const checkInPlaceholders = [
|
||||
{ name: '{{user.name}}', description: '用户名称' },
|
||||
{ name: '{{user.uid}}', description: '用户ID' },
|
||||
{ name: '{{checkin.points}}', description: '基础签到积分' },
|
||||
{ name: '{{checkin.bonusPoints}}', description: '早鸟额外积分 (普通签到为0)' },
|
||||
{ name: '{{checkin.totalPoints}}', description: '总获得积分' },
|
||||
{ name: '{{checkin.isEarlyBird}}', description: '是否是早鸟签到 (true/false)' },
|
||||
{ name: '{{checkin.cooldownSeconds}}', description: '签到冷却时间(秒)' },
|
||||
{ name: '{{checkin.time}}', description: '签到时间对象' }
|
||||
];
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.checkin-template-helper {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.alert-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.placeholder-groups {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.placeholder-group {
|
||||
flex: 1;
|
||||
min-width: 200px;
|
||||
}
|
||||
|
||||
.group-title {
|
||||
font-weight: bold;
|
||||
margin-bottom: 6px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.placeholder-item {
|
||||
margin-bottom: 4px;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.placeholder-item code {
|
||||
padding: 1px 4px;
|
||||
background-color: rgba(0, 0, 0, 0.05);
|
||||
border-radius: 3px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.placeholder-example {
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.example-title {
|
||||
font-weight: bold;
|
||||
margin-bottom: 6px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.example-item {
|
||||
margin-bottom: 4px;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.example-item code {
|
||||
display: block;
|
||||
padding: 4px 8px;
|
||||
background-color: rgba(0, 0, 0, 0.05);
|
||||
border-radius: 3px;
|
||||
margin-top: 2px;
|
||||
font-size: 12px;
|
||||
white-space: nowrap;
|
||||
overflow: auto;
|
||||
}
|
||||
</style>
|
||||
@@ -1,120 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import { NSpace, NSwitch, NInputNumber, NSelect, NCheckbox, NDivider } from 'naive-ui';
|
||||
|
||||
defineProps({
|
||||
config: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
showLiveOnly: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
showDelay: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
showUserFilter: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
showTianXuan: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="common-config-section">
|
||||
<NSpace
|
||||
vertical
|
||||
size="medium"
|
||||
>
|
||||
<NSpace
|
||||
align="center"
|
||||
justify="space-between"
|
||||
style="width: 100%"
|
||||
>
|
||||
<span>启用功能:</span>
|
||||
<NSwitch v-model:value="config.enabled" />
|
||||
</NSpace>
|
||||
|
||||
<NSpace
|
||||
v-if="showLiveOnly"
|
||||
align="center"
|
||||
justify="space-between"
|
||||
style="width: 100%"
|
||||
>
|
||||
<span>仅直播中开启:</span>
|
||||
<NSwitch v-model:value="config.onlyDuringLive" />
|
||||
</NSpace>
|
||||
|
||||
<NSpace
|
||||
v-if="showDelay"
|
||||
align="center"
|
||||
justify="space-between"
|
||||
style="width: 100%"
|
||||
>
|
||||
<span>延迟时间 (秒):</span>
|
||||
<NInputNumber
|
||||
v-model:value="config.delaySeconds"
|
||||
:min="0"
|
||||
:max="300"
|
||||
style="width: 120px"
|
||||
/>
|
||||
</NSpace>
|
||||
|
||||
<NSpace
|
||||
v-if="showTianXuan"
|
||||
align="center"
|
||||
justify="space-between"
|
||||
style="width: 100%"
|
||||
>
|
||||
<span>屏蔽天选时刻:</span>
|
||||
<NSwitch v-model:value="config.ignoreTianXuan" />
|
||||
</NSpace>
|
||||
|
||||
<template v-if="showUserFilter">
|
||||
<NDivider title-placement="left">
|
||||
用户过滤设置
|
||||
</NDivider>
|
||||
|
||||
<NSpace
|
||||
align="center"
|
||||
justify="space-between"
|
||||
style="width: 100%"
|
||||
>
|
||||
<span>启用用户过滤:</span>
|
||||
<NSwitch v-model:value="config.userFilterEnabled" />
|
||||
</NSpace>
|
||||
|
||||
<NSpace
|
||||
v-if="config.userFilterEnabled"
|
||||
align="center"
|
||||
justify="space-between"
|
||||
style="width: 100%"
|
||||
>
|
||||
<span>要求本房间勋章:</span>
|
||||
<NSwitch v-model:value="config.requireMedal" />
|
||||
</NSpace>
|
||||
|
||||
<NSpace
|
||||
v-if="config.userFilterEnabled"
|
||||
align="center"
|
||||
justify="space-between"
|
||||
style="width: 100%"
|
||||
>
|
||||
<span>要求任意舰长:</span>
|
||||
<NSwitch v-model:value="config.requireCaptain" />
|
||||
</NSpace>
|
||||
</template>
|
||||
</NSpace>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.common-config-section {
|
||||
padding: 16px 0;
|
||||
}
|
||||
</style>
|
||||
@@ -1,115 +0,0 @@
|
||||
<template>
|
||||
<div class="template-tester">
|
||||
<NSpace vertical>
|
||||
<NInput
|
||||
v-model:value="template"
|
||||
type="textarea"
|
||||
placeholder="输入包含表达式的模板"
|
||||
/>
|
||||
|
||||
<NSpace>
|
||||
<NButton
|
||||
type="primary"
|
||||
size="small"
|
||||
@click="testTemplate"
|
||||
>
|
||||
测试模板
|
||||
</NButton>
|
||||
<NButton
|
||||
size="small"
|
||||
@click="resetTemplate"
|
||||
>
|
||||
重置
|
||||
</NButton>
|
||||
</NSpace>
|
||||
|
||||
<template
|
||||
v-if="hasResult"
|
||||
>
|
||||
<NDivider style="margin: 5px;" />
|
||||
<NCard
|
||||
title="结果预览"
|
||||
size="small"
|
||||
>
|
||||
<NInput
|
||||
type="textarea"
|
||||
:value="result"
|
||||
readonly
|
||||
/>
|
||||
</NCard>
|
||||
</template>
|
||||
</NSpace>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed } from 'vue';
|
||||
import { NSpace, NInput, NInputGroup, NInputGroupLabel, NButton, useMessage, NDivider } from 'naive-ui';
|
||||
import { evaluateTemplateExpressions } from '@/client/store/autoAction/expressionEvaluator';
|
||||
import { EventModel } from '@/api/api-models';
|
||||
import { TriggerType } from '@/client/store/autoAction/types';
|
||||
import { buildExecutionContext } from '@/client/store/autoAction/utils';
|
||||
|
||||
const props = defineProps({
|
||||
defaultTemplate: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
context: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
});
|
||||
|
||||
const template = ref(props.defaultTemplate || '');
|
||||
const result = ref('');
|
||||
const hasResult = computed(() => result.value !== '');
|
||||
const message = useMessage();
|
||||
|
||||
function evaluateTemplateForUI(template: string, contextObj: Record<string, any>): string {
|
||||
const tempContext = buildExecutionContext(contextObj, undefined, TriggerType.DANMAKU);
|
||||
return evaluateTemplateExpressions(template, tempContext);
|
||||
}
|
||||
|
||||
function testTemplate() {
|
||||
try {
|
||||
result.value = evaluateTemplateForUI(template.value, props.context);
|
||||
} catch (error) {
|
||||
message.error(`表达式求值错误: ${(error as Error).message}`);
|
||||
result.value = `[错误] ${(error as Error).message}`;
|
||||
}
|
||||
}
|
||||
|
||||
function resetTemplate() {
|
||||
template.value = props.defaultTemplate;
|
||||
result.value = '';
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.template-tester {
|
||||
margin-top: 16px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.result-container {
|
||||
margin-top: 8px;
|
||||
padding: 8px;
|
||||
border: 1px solid #d9d9d9;
|
||||
border-radius: 4px;
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
.result-title {
|
||||
font-weight: bold;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.result-content {
|
||||
padding: 8px;
|
||||
background-color: white;
|
||||
border-radius: 4px;
|
||||
border: 1px dashed #d9d9d9;
|
||||
word-break: break-all;
|
||||
}
|
||||
</style>
|
||||
@@ -1,193 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import { EventDataTypes, EventModel } from '@/api/api-models';
|
||||
import { DanmakuWindowSettings, useDanmakuWindow } from '../../store/useDanmakuWindow';
|
||||
import { computed } from 'vue';
|
||||
import { AVATAR_URL } from '@/data/constants';
|
||||
import { GetGuardColor } from '@/Utils';
|
||||
|
||||
export interface BaseDanmakuItemProps {
|
||||
item: EventModel & { randomId: string; isNew?: boolean; disappearAt?: number; };
|
||||
setting: DanmakuWindowSettings;
|
||||
}
|
||||
|
||||
const props = defineProps<BaseDanmakuItemProps>();
|
||||
|
||||
const emojiData = useDanmakuWindow().emojiData;
|
||||
|
||||
// 检查弹幕是否将要消失
|
||||
const isDisappearing = computed(() => {
|
||||
return props.item.disappearAt && Date.now() > props.item.disappearAt - 300; // 提前300ms进入消失动画
|
||||
});
|
||||
|
||||
// 计算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';
|
||||
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 {
|
||||
const availableEmojis = emojiData.data || {}; // 确保 emojiData 已加载
|
||||
|
||||
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 emojiInfo = availableEmojis.inline[emojiFullName] || availableEmojis.plain[emojiFullName];
|
||||
|
||||
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 '【进场】';
|
||||
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 '进入了直播间';
|
||||
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'; // 入场消息绿色
|
||||
}
|
||||
return undefined; // 普通消息使用默认颜色
|
||||
});
|
||||
|
||||
// 向外导出所有计算属性
|
||||
defineExpose({
|
||||
isDisappearing,
|
||||
scColorClass,
|
||||
typeClass,
|
||||
guardColor,
|
||||
guardLevelClass,
|
||||
showAvatar,
|
||||
parsedMessage,
|
||||
typeLabel,
|
||||
priceText,
|
||||
displayName,
|
||||
displayContent,
|
||||
textModeColor
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<slot />
|
||||
</template>
|
||||
Reference in New Issue
Block a user