mirror of
https://github.com/Megghy/vtsuru.live.git
synced 2025-12-07 02:46:55 +08:00
feat: 更新项目配置和组件,增强功能和用户体验
- 在 .gitignore 中添加了 .specstory 文件的忽略规则。 - 更新 tsconfig.json,修正了 vue-vine/types/macros 的引用路径。 - 在组件声明中新增了 NInput 组件的类型支持。 - 优化了 EventModel 接口,调整了 guard_level 的类型为 GuardLevel。 - 增加了 Follow 事件类型到 EventDataTypes 枚举中。 - 在 ClientAutoAction.vue 中引入了新的 store 和组件,增强了功能。 - 更新了多个设置组件,添加了关键词匹配类型和过滤模式的支持。 - 改进了模板编辑器和测试器的功能,支持更灵活的模板管理。 - 在弹幕客户端中新增了关注事件的处理逻辑,提升了事件响应能力。
This commit is contained in:
@@ -1,7 +1,8 @@
|
||||
<script setup lang="ts">
|
||||
import { NSpace, NSwitch, NInputNumber, NInput, NCollapseItem } from 'naive-ui';
|
||||
import { NSpace, NSwitch, NInputNumber, NInput, NCollapseItem, NCollapse } from 'naive-ui';
|
||||
import { AutoActionItem, TriggerType } from '@/client/store/useAutoAction';
|
||||
import { computed } from 'vue';
|
||||
import { createDefaultAutoAction } from '@/client/store/autoAction/utils';
|
||||
|
||||
const props = defineProps({
|
||||
action: {
|
||||
@@ -14,6 +15,54 @@ const props = defineProps({
|
||||
const showUserFilter = computed(() => {
|
||||
return ![TriggerType.SCHEDULED].includes(props.action.triggerType);
|
||||
});
|
||||
|
||||
// 获取默认配置作为比较基准
|
||||
const defaultAction = computed(() => createDefaultAutoAction(props.action.triggerType));
|
||||
|
||||
// 检查设置项是否被修改
|
||||
const isModified = (path: string, value: any) => {
|
||||
const pathParts = path.split('.');
|
||||
let defaultValue: any = defaultAction.value;
|
||||
let currentValue: any = props.action;
|
||||
|
||||
// 遍历路径获取值
|
||||
for (const part of pathParts) {
|
||||
defaultValue = defaultValue && typeof defaultValue === 'object' ? defaultValue[part as keyof typeof defaultValue] : undefined;
|
||||
currentValue = currentValue && typeof currentValue === 'object' ? currentValue[part as keyof typeof currentValue] : undefined;
|
||||
}
|
||||
|
||||
// 处理特殊情况,如果指定了具体值进行比较
|
||||
if (value !== undefined) {
|
||||
return value !== defaultValue;
|
||||
}
|
||||
|
||||
return currentValue !== defaultValue;
|
||||
};
|
||||
|
||||
// 检查用户过滤区域是否有修改
|
||||
const userFilterModified = computed(() => {
|
||||
if (!showUserFilter.value) return false;
|
||||
return isModified('triggerConfig.userFilterEnabled', props.action.triggerConfig.userFilterEnabled) ||
|
||||
isModified('triggerConfig.requireMedal', props.action.triggerConfig.requireMedal) ||
|
||||
isModified('triggerConfig.requireCaptain', props.action.triggerConfig.requireCaptain);
|
||||
});
|
||||
|
||||
// 检查冷却控制区域是否有修改
|
||||
const cooldownModified = computed(() => {
|
||||
return isModified('ignoreCooldown', props.action.ignoreCooldown) ||
|
||||
isModified('actionConfig.delaySeconds', props.action.actionConfig.delaySeconds) ||
|
||||
isModified('actionConfig.cooldownSeconds', props.action.actionConfig.cooldownSeconds);
|
||||
});
|
||||
|
||||
// 检查逻辑表达式是否有修改
|
||||
const logicalExpressionModified = computed(() => {
|
||||
return isModified('logicalExpression', props.action.logicalExpression);
|
||||
});
|
||||
|
||||
// 检查自定义JS是否有修改
|
||||
const customJsModified = computed(() => {
|
||||
return isModified('executeCommand', props.action.executeCommand);
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -21,8 +70,10 @@ const showUserFilter = computed(() => {
|
||||
<NCollapseItem
|
||||
v-if="showUserFilter"
|
||||
key="user-filter"
|
||||
title="用户过滤"
|
||||
:title="userFilterModified ? '用户过滤 *' : '用户过滤'"
|
||||
:title-extra="userFilterModified ? '已修改' : ''"
|
||||
class="settings-section"
|
||||
:class="{'section-modified': userFilterModified}"
|
||||
>
|
||||
<div>
|
||||
<NSpace
|
||||
@@ -34,7 +85,7 @@ const showUserFilter = computed(() => {
|
||||
align="center"
|
||||
justify="space-between"
|
||||
style="width: 100%"
|
||||
class="setting-item"
|
||||
:class="['setting-item', {'setting-modified': isModified('triggerConfig.userFilterEnabled', action.triggerConfig.userFilterEnabled)}]"
|
||||
>
|
||||
<span>启用用户过滤:</span>
|
||||
<NSwitch v-model:value="action.triggerConfig.userFilterEnabled" />
|
||||
@@ -46,7 +97,7 @@ const showUserFilter = computed(() => {
|
||||
align="center"
|
||||
justify="space-between"
|
||||
style="width: 100%"
|
||||
class="setting-item"
|
||||
:class="['setting-item', {'setting-modified': isModified('triggerConfig.requireMedal', action.triggerConfig.requireMedal)}]"
|
||||
>
|
||||
<span>要求本房间勋章:</span>
|
||||
<NSwitch v-model:value="action.triggerConfig.requireMedal" />
|
||||
@@ -57,7 +108,7 @@ const showUserFilter = computed(() => {
|
||||
align="center"
|
||||
justify="space-between"
|
||||
style="width: 100%"
|
||||
class="setting-item"
|
||||
:class="['setting-item', {'setting-modified': isModified('triggerConfig.requireCaptain', action.triggerConfig.requireCaptain)}]"
|
||||
>
|
||||
<span>要求任意舰长:</span>
|
||||
<NSwitch v-model:value="action.triggerConfig.requireCaptain" />
|
||||
@@ -69,8 +120,10 @@ const showUserFilter = computed(() => {
|
||||
|
||||
<NCollapseItem
|
||||
key="cooldown"
|
||||
title="冷却控制"
|
||||
:title="cooldownModified ? '冷却控制 *' : '冷却控制'"
|
||||
:title-extra="cooldownModified ? '已修改' : ''"
|
||||
class="settings-section"
|
||||
:class="{'section-modified': cooldownModified}"
|
||||
>
|
||||
<div>
|
||||
<NSpace
|
||||
@@ -78,7 +131,7 @@ const showUserFilter = computed(() => {
|
||||
align="center"
|
||||
justify="space-between"
|
||||
style="width: 100%"
|
||||
class="setting-item"
|
||||
:class="['setting-item', {'setting-modified': isModified('ignoreCooldown', action.ignoreCooldown)}]"
|
||||
>
|
||||
<span>忽略全局冷却:</span>
|
||||
<NSwitch v-model:value="action.ignoreCooldown" />
|
||||
@@ -89,7 +142,7 @@ const showUserFilter = computed(() => {
|
||||
align="center"
|
||||
justify="space-between"
|
||||
style="width: 100%"
|
||||
class="setting-item"
|
||||
:class="['setting-item', {'setting-modified': isModified('actionConfig.delaySeconds', action.actionConfig.delaySeconds)}]"
|
||||
>
|
||||
<span>延迟执行(秒):</span>
|
||||
<NInputNumber
|
||||
@@ -105,7 +158,7 @@ const showUserFilter = computed(() => {
|
||||
align="center"
|
||||
justify="space-between"
|
||||
style="width: 100%"
|
||||
class="setting-item"
|
||||
:class="['setting-item', {'setting-modified': isModified('actionConfig.cooldownSeconds', action.actionConfig.cooldownSeconds)}]"
|
||||
>
|
||||
<span>冷却时间(秒):</span>
|
||||
<NInputNumber
|
||||
@@ -120,8 +173,10 @@ const showUserFilter = computed(() => {
|
||||
|
||||
<NCollapseItem
|
||||
key="logical-expression"
|
||||
title="逻辑条件表达式"
|
||||
:title="logicalExpressionModified ? '逻辑条件表达式 *' : '逻辑条件表达式'"
|
||||
:title-extra="logicalExpressionModified ? '已修改' : ''"
|
||||
class="settings-section"
|
||||
:class="{'section-modified': logicalExpressionModified}"
|
||||
>
|
||||
<div>
|
||||
<NSpace vertical>
|
||||
@@ -133,6 +188,7 @@ const showUserFilter = computed(() => {
|
||||
type="textarea"
|
||||
placeholder="输入表达式,留空则始终为真"
|
||||
:autosize="{ minRows: 2, maxRows: 5 }"
|
||||
:class="{'input-modified': isModified('logicalExpression', action.logicalExpression)}"
|
||||
/>
|
||||
</NSpace>
|
||||
</div>
|
||||
@@ -140,8 +196,10 @@ const showUserFilter = computed(() => {
|
||||
|
||||
<NCollapseItem
|
||||
key="custom-js"
|
||||
title="自定义JS执行"
|
||||
:title="customJsModified ? '自定义JS执行 *' : '自定义JS执行'"
|
||||
:title-extra="customJsModified ? '已修改' : ''"
|
||||
class="settings-section"
|
||||
:class="{'section-modified': customJsModified}"
|
||||
>
|
||||
<div>
|
||||
<NSpace vertical>
|
||||
@@ -153,6 +211,7 @@ const showUserFilter = computed(() => {
|
||||
type="textarea"
|
||||
placeholder="输入要执行的JS代码"
|
||||
:autosize="{ minRows: 3, maxRows: 8 }"
|
||||
:class="{'input-modified': isModified('executeCommand', action.executeCommand)}"
|
||||
/>
|
||||
</NSpace>
|
||||
</div>
|
||||
@@ -179,6 +238,31 @@ const showUserFilter = computed(() => {
|
||||
background-color: rgba(0, 0, 0, 0.02);
|
||||
}
|
||||
|
||||
.setting-modified {
|
||||
font-weight: bold;
|
||||
background-color: rgba(255, 230, 186, 0.2);
|
||||
}
|
||||
|
||||
.setting-modified:hover {
|
||||
background-color: rgba(255, 230, 186, 0.3);
|
||||
}
|
||||
|
||||
.input-modified {
|
||||
border-color: #faad14;
|
||||
background-color: rgba(255, 230, 186, 0.1);
|
||||
}
|
||||
|
||||
.section-modified :deep(.n-collapse-item__header-main) {
|
||||
font-weight: bold;
|
||||
color: #fa8c16;
|
||||
}
|
||||
|
||||
.section-modified :deep(.n-collapse-item__header-extra) {
|
||||
font-size: 12px;
|
||||
color: #fa8c16;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.description {
|
||||
margin-top: 8px;
|
||||
font-size: 13px;
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
import { NSpace, NInput, NButton, NTag, NDivider, NCollapseItem, useMessage } from 'naive-ui';
|
||||
import { NSpace, NInput, NButton, NTag, NDivider, NCollapseItem, useMessage, NRadioGroup, NRadioButton } from 'naive-ui';
|
||||
import { AutoActionItem, TriggerType } from '@/client/store/useAutoAction';
|
||||
import { KeywordMatchType } from '@/client/store/autoAction/types';
|
||||
import SingleTemplateEditor from '../SingleTemplateEditor.vue';
|
||||
|
||||
const props = defineProps({
|
||||
action: {
|
||||
@@ -16,6 +18,15 @@ const message = useMessage();
|
||||
const tempKeyword = ref('');
|
||||
const tempBlockword = ref('');
|
||||
|
||||
// 初始化匹配类型配置
|
||||
if (!props.action.triggerConfig.keywordMatchType) {
|
||||
props.action.triggerConfig.keywordMatchType = KeywordMatchType.Contains;
|
||||
}
|
||||
|
||||
if (!props.action.triggerConfig.blockwordMatchType) {
|
||||
props.action.triggerConfig.blockwordMatchType = KeywordMatchType.Contains;
|
||||
}
|
||||
|
||||
// 添加关键词
|
||||
function addKeyword() {
|
||||
if (!tempKeyword.value.trim()) return;
|
||||
@@ -61,6 +72,17 @@ function removeBlockword(index: number) {
|
||||
props.action.triggerConfig.blockwords.splice(index, 1);
|
||||
}
|
||||
}
|
||||
|
||||
// 定义弹幕相关的模板占位符
|
||||
const danmakuPlaceholders = [
|
||||
{ name: '{{user.name}}', description: '发送弹幕的用户名' },
|
||||
{ name: '{{user.uid}}', description: '用户UID' },
|
||||
{ name: '{{user.guardLevel}}', description: '用户舰长等级(0-3)' },
|
||||
{ name: '{{user.medalLevel}}', description: '粉丝勋章等级' },
|
||||
{ name: '{{user.medalName}}', description: '粉丝勋章名称' },
|
||||
{ name: '{{message}}', description: '弹幕内容' },
|
||||
{ name: '{{js: message.substring(0, 10)}}', description: '弹幕内容的前10个字符' },
|
||||
];
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -83,6 +105,24 @@ function removeBlockword(index: number) {
|
||||
</NButton>
|
||||
</NSpace>
|
||||
|
||||
<NSpace align="center">
|
||||
<span>匹配方式:</span>
|
||||
<NRadioGroup
|
||||
v-model:value="action.triggerConfig.keywordMatchType"
|
||||
size="small"
|
||||
>
|
||||
<NRadioButton :value="KeywordMatchType.Full">
|
||||
完全
|
||||
</NRadioButton>
|
||||
<NRadioButton :value="KeywordMatchType.Contains">
|
||||
包含
|
||||
</NRadioButton>
|
||||
<NRadioButton :value="KeywordMatchType.Regex">
|
||||
正则
|
||||
</NRadioButton>
|
||||
</NRadioGroup>
|
||||
</NSpace>
|
||||
|
||||
<NSpace>
|
||||
<template v-if="action.triggerConfig.keywords">
|
||||
<NTag
|
||||
@@ -112,6 +152,24 @@ function removeBlockword(index: number) {
|
||||
</NButton>
|
||||
</NSpace>
|
||||
|
||||
<NSpace align="center">
|
||||
<span>匹配方式:</span>
|
||||
<NRadioGroup
|
||||
v-model:value="action.triggerConfig.blockwordMatchType"
|
||||
size="small"
|
||||
>
|
||||
<NRadioButton :value="KeywordMatchType.Full">
|
||||
完全
|
||||
</NRadioButton>
|
||||
<NRadioButton :value="KeywordMatchType.Contains">
|
||||
包含
|
||||
</NRadioButton>
|
||||
<NRadioButton :value="KeywordMatchType.Regex">
|
||||
正则
|
||||
</NRadioButton>
|
||||
</NRadioGroup>
|
||||
</NSpace>
|
||||
|
||||
<NSpace>
|
||||
<template v-if="action.triggerConfig.blockwords">
|
||||
<NTag
|
||||
|
||||
@@ -32,7 +32,7 @@ const enterFilterModeOptions = [
|
||||
>
|
||||
<span>入场过滤模式:</span>
|
||||
<NSelect
|
||||
v-model:value="action.triggerConfig.enterFilterMode"
|
||||
v-model:value="action.triggerConfig.filterMode"
|
||||
style="width: 200px"
|
||||
:options="enterFilterModeOptions"
|
||||
/>
|
||||
|
||||
@@ -61,13 +61,13 @@ function removeGiftName(index: number) {
|
||||
>
|
||||
<span>礼物过滤模式:</span>
|
||||
<NSelect
|
||||
v-model:value="action.triggerConfig.giftFilterMode"
|
||||
v-model:value="action.triggerConfig.filterMode"
|
||||
style="width: 200px"
|
||||
:options="giftFilterModeOptions"
|
||||
/>
|
||||
</NSpace>
|
||||
|
||||
<template v-if="action.triggerConfig.giftFilterMode === 'blacklist' || action.triggerConfig.giftFilterMode === 'whitelist'">
|
||||
<template v-if="action.triggerConfig.filterMode === 'blacklist' || action.triggerConfig.filterMode === 'whitelist'">
|
||||
<NSpace>
|
||||
<NInput
|
||||
v-model:value="tempGiftName"
|
||||
@@ -93,7 +93,7 @@ function removeGiftName(index: number) {
|
||||
</NSpace>
|
||||
</template>
|
||||
|
||||
<template v-if="action.triggerConfig.giftFilterMode === 'value'">
|
||||
<template v-if="action.triggerConfig.filterMode === 'value'">
|
||||
<NSpace
|
||||
align="center"
|
||||
justify="space-between"
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
<script setup lang="ts">
|
||||
import {
|
||||
NCard,
|
||||
NSpace,
|
||||
NInputNumber,
|
||||
NRadioGroup,
|
||||
NRadio,
|
||||
NText,
|
||||
NDivider
|
||||
} from 'naive-ui';
|
||||
import { useAutoAction } from '@/client/store/useAutoAction';
|
||||
import { watch } from 'vue';
|
||||
|
||||
const autoActionStore = useAutoAction();
|
||||
|
||||
// 定时模式选项
|
||||
const schedulingModeOptions = [
|
||||
{ label: '随机模式', value: 'random' },
|
||||
{ label: '顺序模式', value: 'sequential' }
|
||||
];
|
||||
|
||||
// 监听变化,触发定时器重启(如果间隔改变)
|
||||
watch(() => autoActionStore.globalIntervalSeconds, () => {
|
||||
autoActionStore.restartGlobalTimer(); // 确保间隔改变时定时器更新
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<NCard
|
||||
title="全局定时设置"
|
||||
size="small"
|
||||
style="margin-bottom: 16px;"
|
||||
>
|
||||
<NSpace
|
||||
vertical
|
||||
:size="16"
|
||||
>
|
||||
<NText
|
||||
type="info"
|
||||
:depth="3"
|
||||
style="font-size: 12px;"
|
||||
>
|
||||
这里的设置将应用于所有启用了 "使用全局定时器" 选项的定时触发操作。
|
||||
</NText>
|
||||
<NDivider style="margin: 4px 0;" />
|
||||
<NSpace
|
||||
align="center"
|
||||
justify="space-between"
|
||||
style="width: 100%"
|
||||
>
|
||||
<NText>全局发送间隔 (秒):</NText>
|
||||
<NInputNumber
|
||||
v-model:value="autoActionStore.globalIntervalSeconds"
|
||||
:min="10"
|
||||
:max="7200"
|
||||
style="width: 120px"
|
||||
/>
|
||||
</NSpace>
|
||||
|
||||
<NSpace
|
||||
align="center"
|
||||
justify="space-between"
|
||||
style="width: 100%"
|
||||
>
|
||||
<NText>全局发送模式:</NText>
|
||||
<NRadioGroup v-model:value="autoActionStore.globalSchedulingMode">
|
||||
<NSpace>
|
||||
<NRadio
|
||||
v-for="option in schedulingModeOptions"
|
||||
:key="option.value"
|
||||
:value="option.value"
|
||||
>
|
||||
{{ option.label }}
|
||||
</NRadio>
|
||||
</NSpace>
|
||||
</NRadioGroup>
|
||||
</NSpace>
|
||||
</NSpace>
|
||||
</NCard>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
/* 可以添加一些特定样式 */
|
||||
</style>
|
||||
@@ -1,7 +1,16 @@
|
||||
<script setup lang="ts">
|
||||
import { NSpace, NInputNumber, NRadioGroup, NRadio, NCollapseItem } from 'naive-ui';
|
||||
import {
|
||||
NSpace,
|
||||
NInputNumber,
|
||||
NRadioGroup,
|
||||
NRadio,
|
||||
NCollapseItem,
|
||||
NSwitch,
|
||||
NDivider,
|
||||
NText
|
||||
} from 'naive-ui';
|
||||
import { AutoActionItem, TriggerType } from '@/client/store/useAutoAction';
|
||||
import { computed, ref } from 'vue';
|
||||
import { computed, ref, watch } from 'vue';
|
||||
|
||||
const props = defineProps({
|
||||
action: {
|
||||
@@ -10,11 +19,30 @@ const props = defineProps({
|
||||
}
|
||||
});
|
||||
|
||||
// 初始化配置项
|
||||
if (props.action.triggerConfig.useGlobalTimer === undefined) {
|
||||
props.action.triggerConfig.useGlobalTimer = false; // 默认不使用全局定时器
|
||||
}
|
||||
if (props.action.triggerConfig.schedulingMode === undefined) {
|
||||
props.action.triggerConfig.schedulingMode = 'random'; // 默认随机模式
|
||||
}
|
||||
if (props.action.triggerConfig.intervalSeconds === undefined) {
|
||||
props.action.triggerConfig.intervalSeconds = 300; // 默认5分钟
|
||||
}
|
||||
|
||||
const useGlobalTimer = ref(props.action.triggerConfig.useGlobalTimer);
|
||||
|
||||
// 同步到 action
|
||||
watch(useGlobalTimer, (value) => {
|
||||
props.action.triggerConfig.useGlobalTimer = value;
|
||||
});
|
||||
|
||||
// 定时模式选项
|
||||
const schedulingModeOptions = [
|
||||
{ label: '随机模式', value: 'random' },
|
||||
{ label: '顺序模式', value: 'sequential' }
|
||||
];
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -22,11 +50,47 @@ const schedulingModeOptions = [
|
||||
v-if="action.triggerType === TriggerType.SCHEDULED"
|
||||
title="定时触发设置"
|
||||
>
|
||||
<NSpace vertical>
|
||||
<NSpace
|
||||
vertical
|
||||
:size="16"
|
||||
>
|
||||
<NSpace
|
||||
align="center"
|
||||
justify="space-between"
|
||||
style="width: 100%"
|
||||
>
|
||||
<NText>使用全局定时器设置:</NText>
|
||||
<NSwitch v-model:value="useGlobalTimer">
|
||||
<template #checked>
|
||||
是
|
||||
</template>
|
||||
<template #unchecked>
|
||||
否
|
||||
</template>
|
||||
</NSwitch>
|
||||
</NSpace>
|
||||
|
||||
<NText
|
||||
type="info"
|
||||
:depth="3"
|
||||
style="font-size: 12px; margin-top: -8px;"
|
||||
>
|
||||
启用后,此操作将遵循全局定时设置(间隔、模式),忽略下方独立设置。
|
||||
全局设置需在【自动化 -> 全局配置】中修改。
|
||||
</NText>
|
||||
|
||||
<NDivider
|
||||
title-placement="left"
|
||||
style="margin-top: 8px; margin-bottom: 8px;"
|
||||
>
|
||||
独立设置 (仅在不使用全局定时器时生效)
|
||||
</NDivider>
|
||||
|
||||
<NSpace
|
||||
align="center"
|
||||
justify="space-between"
|
||||
style="width: 100%"
|
||||
:class="{ 'disabled-setting': useGlobalTimer }"
|
||||
>
|
||||
<span>发送间隔 (秒):</span>
|
||||
<NInputNumber
|
||||
@@ -34,6 +98,7 @@ const schedulingModeOptions = [
|
||||
:min="60"
|
||||
:max="3600"
|
||||
style="width: 120px"
|
||||
:disabled="useGlobalTimer"
|
||||
/>
|
||||
</NSpace>
|
||||
|
||||
@@ -41,9 +106,13 @@ const schedulingModeOptions = [
|
||||
align="center"
|
||||
justify="space-between"
|
||||
style="width: 100%"
|
||||
:class="{ 'disabled-setting': useGlobalTimer }"
|
||||
>
|
||||
<span>发送模式:</span>
|
||||
<NRadioGroup v-model:value="action.triggerConfig.schedulingMode">
|
||||
<NRadioGroup
|
||||
v-model:value="action.triggerConfig.schedulingMode"
|
||||
:disabled="useGlobalTimer"
|
||||
>
|
||||
<NSpace>
|
||||
<NRadio
|
||||
v-for="option in schedulingModeOptions"
|
||||
@@ -58,3 +127,10 @@ const schedulingModeOptions = [
|
||||
</NSpace>
|
||||
</NCollapseItem>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.disabled-setting {
|
||||
opacity: 0.5;
|
||||
pointer-events: none;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -43,7 +43,7 @@ const scFilterModeOptions = [
|
||||
>
|
||||
<span>最低价格 (元):</span>
|
||||
<NInputNumber
|
||||
v-model:value="action.triggerConfig.minPrice"
|
||||
v-model:value="action.triggerConfig.scMinPrice"
|
||||
:min="0"
|
||||
style="width: 120px"
|
||||
/>
|
||||
|
||||
@@ -11,59 +11,6 @@ const props = defineProps({
|
||||
}
|
||||
});
|
||||
|
||||
// 模板变量占位符选项,根据触发类型动态生成
|
||||
const placeholders = computed(() => {
|
||||
const commonPlaceholders = [
|
||||
{ name: '{{user.name}}', description: '用户名称' },
|
||||
{ name: '{{user.uid}}', description: '用户ID' },
|
||||
{ name: '{{date.formatted}}', description: '当前日期时间' },
|
||||
{ name: '{{timeOfDay()}}', description: '当前时段(早上/下午/晚上)' },
|
||||
];
|
||||
|
||||
let specificPlaceholders: { name: string, description: string }[] = [];
|
||||
|
||||
switch (props.action.triggerType) {
|
||||
case TriggerType.GIFT:
|
||||
specificPlaceholders = [
|
||||
{ name: '{{gift.name}}', description: '礼物名称' },
|
||||
{ name: '{{gift.count}}', description: '礼物数量' },
|
||||
{ name: '{{gift.price}}', description: '礼物单价' },
|
||||
{ name: '{{gift.totalPrice}}', description: '礼物总价值' },
|
||||
{ name: '{{gift.summary}}', description: '礼物摘要(如:5个辣条)' },
|
||||
];
|
||||
break;
|
||||
case TriggerType.GUARD:
|
||||
specificPlaceholders = [
|
||||
{ name: '{{guard.level}}', description: '舰长等级' },
|
||||
{ name: '{{guard.levelName}}', description: '舰长等级名称' },
|
||||
{ name: '{{guard.giftCode}}', description: '礼品码(如已配置)' },
|
||||
];
|
||||
break;
|
||||
case TriggerType.SUPER_CHAT:
|
||||
specificPlaceholders = [
|
||||
{ name: '{{sc.message}}', description: 'SC消息内容' },
|
||||
{ name: '{{sc.price}}', description: 'SC价格' },
|
||||
];
|
||||
break;
|
||||
case TriggerType.FOLLOW:
|
||||
specificPlaceholders = [
|
||||
{ name: '{{follow.time}}', description: '关注时间' },
|
||||
{ name: '{{follow.isNew}}', description: '是否新关注' },
|
||||
];
|
||||
break;
|
||||
case TriggerType.ENTER:
|
||||
specificPlaceholders = [
|
||||
{ name: '{{enter.time}}', description: '入场时间' },
|
||||
{ name: '{{enter.guardLevel}}', description: '舰长等级' },
|
||||
{ name: '{{enter.medalName}}', description: '勋章名称' },
|
||||
{ name: '{{enter.medalLevel}}', description: '勋章等级' },
|
||||
];
|
||||
break;
|
||||
}
|
||||
|
||||
return [...commonPlaceholders, ...specificPlaceholders];
|
||||
});
|
||||
|
||||
// 根据操作类型获取模板标题
|
||||
const templateTitle = computed(() => {
|
||||
switch (props.action.actionType) {
|
||||
@@ -91,6 +38,15 @@ const templateDescription = computed(() => {
|
||||
return '消息内容模板';
|
||||
}
|
||||
});
|
||||
|
||||
// Handle template updates from TemplateEditor
|
||||
function handleTemplateUpdate(payload: { index: number, value: string }) {
|
||||
// Assuming index will always be 0 here as we only render one editor
|
||||
// And assuming action.templates is a string based on previous findings
|
||||
if (payload.index === 0) {
|
||||
props.action.template = payload.value;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -100,11 +56,13 @@ const templateDescription = computed(() => {
|
||||
appear
|
||||
>
|
||||
<TemplateEditor
|
||||
:templates="action.templates"
|
||||
:placeholders="placeholders"
|
||||
:action="props.action"
|
||||
:template-index="0"
|
||||
:title="templateTitle"
|
||||
:description="templateDescription"
|
||||
:check-length="action.actionType === ActionType.SEND_DANMAKU"
|
||||
class="template-editor"
|
||||
@update:template="handleTemplateUpdate"
|
||||
/>
|
||||
</transition>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user