feat: 添加通知功能自定义telegram域名功能
This commit is contained in:
@@ -193,7 +193,7 @@ const handleCommandInputKeydown = (event: KeyboardEvent) => {
|
||||
event.preventDefault(); // Prevent default if needed, e.g., form submission
|
||||
sendCommand(); // Call the existing sendCommand function
|
||||
} else {
|
||||
// --- 新增:处理其他按键,取消列表选中状态 ---
|
||||
// --- 处理其他按键,取消列表选中状态 ---
|
||||
// 检查按下的键是否是普通输入键或删除键等,而不是导航键或修饰键
|
||||
if (!['ArrowUp', 'ArrowDown', 'Enter', 'Shift', 'Control', 'Alt', 'Meta', 'Tab', 'Escape'].includes(event.key)) {
|
||||
const target = commandInputSyncTarget.value;
|
||||
|
||||
@@ -22,7 +22,7 @@ const props = defineProps<{
|
||||
// 定义组件发出的事件 (添加 edit-connection)
|
||||
const emit = defineEmits(['edit-connection']);
|
||||
|
||||
// 新增:用于跟踪每个连接测试状态的响应式对象
|
||||
// 用于跟踪每个连接测试状态的响应式对象
|
||||
const testingState = reactive<Record<number, boolean>>({});
|
||||
|
||||
// 组件挂载时获取标签列表 (连接列表由父组件传入)
|
||||
@@ -54,7 +54,7 @@ const getConnectionTagNames = (conn: ConnectionInfo): string[] => {
|
||||
.filter((name): name is string => !!name); // 过滤掉未找到的标签并确保类型为 string
|
||||
};
|
||||
|
||||
// 新增:计算按标签分组的连接
|
||||
// 计算按标签分组的连接
|
||||
const groupedConnections = computed(() => {
|
||||
const groups: { [key: string]: ConnectionInfo[] } = {};
|
||||
const untaggedKey = '_untagged_'; // 特殊键,用于未标记的连接
|
||||
@@ -118,7 +118,7 @@ const formatTimestamp = (timestamp: number | null): string => {
|
||||
return new Date(timestamp * 1000).toLocaleString(); // 乘以 1000 转换为毫秒
|
||||
};
|
||||
|
||||
// 新增:处理删除连接的方法
|
||||
// 处理删除连接的方法
|
||||
const handleDelete = async (conn: ConnectionInfo) => {
|
||||
// 在函数内部获取 store 实例
|
||||
const connectionsStore = useConnectionsStore();
|
||||
@@ -135,7 +135,7 @@ const handleDelete = async (conn: ConnectionInfo) => {
|
||||
}
|
||||
};
|
||||
|
||||
// 新增:处理测试连接的方法
|
||||
// 处理测试连接的方法
|
||||
const handleTestConnection = async (connectionId: number) => {
|
||||
const connectionsStore = useConnectionsStore(); // 获取 store 实例
|
||||
testingState[connectionId] = true; // 设置为正在测试状态
|
||||
|
||||
@@ -207,10 +207,10 @@ const handleEncodingChange = (event: Event) => {
|
||||
// const handleCloseContainer = () => { ... };
|
||||
// const handleMinimizeContainer = () => { ... };
|
||||
|
||||
// 新增:Monaco Editor 组件的引用
|
||||
// Monaco Editor 组件的引用
|
||||
const monacoEditorRef = ref<InstanceType<typeof MonacoEditor> | null>(null);
|
||||
|
||||
// 新增:聚焦活动编辑器的方法
|
||||
// 聚焦活动编辑器的方法
|
||||
const focusActiveEditor = (): boolean => {
|
||||
if (monacoEditorRef.value) {
|
||||
monacoEditorRef.value.focus();
|
||||
@@ -219,7 +219,7 @@ const focusActiveEditor = (): boolean => {
|
||||
return false; // 聚焦失败
|
||||
};
|
||||
|
||||
// 新增:暴露聚焦方法
|
||||
// 暴露聚焦方法
|
||||
defineExpose({ focusActiveEditor });
|
||||
|
||||
// +++ 注册/注销自定义聚焦动作 +++
|
||||
@@ -296,7 +296,7 @@ const handleKeyDown = (event: KeyboardEvent) => {
|
||||
<span v-if="currentTabIsModified" class="modified-indicator">*</span>
|
||||
</span>
|
||||
<div class="editor-actions">
|
||||
<!-- +++ 新增:编码选择下拉菜单 +++ -->
|
||||
<!-- +++ 编码选择下拉菜单 +++ -->
|
||||
<div class="encoding-select-wrapper" v-if="activeTab && !currentTabIsLoading">
|
||||
<select
|
||||
ref="encodingSelectRef"
|
||||
|
||||
@@ -19,7 +19,7 @@ const props = defineProps({
|
||||
type: Object as PropType<LayoutNode>,
|
||||
required: true,
|
||||
},
|
||||
// 新增:标识是否为顶层渲染器
|
||||
// 标识是否为顶层渲染器
|
||||
isRootRenderer: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
@@ -30,7 +30,7 @@ const props = defineProps({
|
||||
required: false, // 改为非必需
|
||||
default: null, // 提供默认值 null
|
||||
},
|
||||
// *** 新增:接收编辑器相关 props ***
|
||||
// *** 接收编辑器相关 props ***
|
||||
editorTabs: {
|
||||
type: Array as PropType<any[]>, // 使用 any[] 简化,或导入具体类型
|
||||
default: () => [],
|
||||
|
||||
@@ -133,6 +133,11 @@
|
||||
<input type="text" id="telegram-chatid" v-model="telegramConfig.chatId" required
|
||||
class="w-full px-3 py-2 border border-border rounded-md shadow-sm bg-background text-foreground focus:outline-none focus:ring-1 focus:ring-primary focus:border-primary">
|
||||
</div>
|
||||
<div>
|
||||
<label for="telegram-custom-domain" class="block text-sm font-medium text-text-secondary mb-1">{{ $t('settings.notifications.form.telegramCustomDomain') }}</label>
|
||||
<input type="url" id="telegram-custom-domain" v-model="telegramConfig.customDomain" placeholder="https://api.example.com"
|
||||
class="w-full px-3 py-2 border border-border rounded-md shadow-sm bg-background text-foreground focus:outline-none focus:ring-1 focus:ring-primary focus:border-primary">
|
||||
</div>
|
||||
<div>
|
||||
<label for="telegram-message" class="block text-sm font-medium text-text-secondary mb-1">{{ $t('settings.notifications.form.telegramMessageTemplate') }}</label>
|
||||
<textarea id="telegram-message" v-model="telegramConfig.messageTemplate" rows="3" :placeholder="`${$t('settings.notifications.form.telegramMessagePlaceholder')} {event}, {timestamp}, {details}.`"
|
||||
@@ -211,7 +216,8 @@ import {
|
||||
NotificationEvent,
|
||||
WebhookConfig,
|
||||
EmailConfig,
|
||||
TelegramConfig
|
||||
TelegramConfig,
|
||||
NotificationChannelType
|
||||
} from '../types/server.types';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
@@ -305,7 +311,7 @@ const emailConfig = ref<SmtpEmailConfig>({ // Use extended type
|
||||
smtpPass: '',
|
||||
from: ''
|
||||
});
|
||||
const telegramConfig = ref<TelegramConfig>({ botToken: '', chatId: '', messageTemplate: '' });
|
||||
const telegramConfig = ref<TelegramConfig>({ botToken: '', chatId: '', messageTemplate: '', customDomain: '' });
|
||||
const webhookHeadersString = ref('{}'); // For textarea binding
|
||||
|
||||
// Watch for initialData changes (when editing)
|
||||
@@ -331,7 +337,13 @@ watch(() => props.initialData, (newData) => {
|
||||
from: savedConfig.from || ''
|
||||
};
|
||||
} else if (newData.channel_type === 'telegram') {
|
||||
telegramConfig.value = { ...(newData.config as TelegramConfig) };
|
||||
const savedConfig = newData.config as TelegramConfig;
|
||||
telegramConfig.value = {
|
||||
botToken: savedConfig.botToken || '',
|
||||
chatId: savedConfig.chatId || '',
|
||||
messageTemplate: savedConfig.messageTemplate || '',
|
||||
customDomain: savedConfig.customDomain || '' // Add customDomain
|
||||
};
|
||||
}
|
||||
} else {
|
||||
// Reset form if initialData becomes null (e.g., switching from edit to add)
|
||||
@@ -341,7 +353,7 @@ watch(() => props.initialData, (newData) => {
|
||||
emailConfig.value = {
|
||||
to: '', bodyTemplate: '', smtpHost: '', smtpPort: 587, smtpSecure: true, smtpUser: '', smtpPass: '', from: '' // Changed from subjectTemplate
|
||||
};
|
||||
telegramConfig.value = { botToken: '', chatId: '', messageTemplate: '' };
|
||||
telegramConfig.value = { botToken: '', chatId: '', messageTemplate: '', customDomain: '' }; // Add customDomain
|
||||
webhookHeadersString.value = '{}';
|
||||
}
|
||||
headerError.value = null; // Reset header error on data change
|
||||
@@ -359,9 +371,9 @@ watch(() => formData.channel_type, (newType, oldType) => {
|
||||
emailConfig.value = {
|
||||
to: '', bodyTemplate: '', smtpHost: '', smtpPort: 587, smtpSecure: true, smtpUser: '', smtpPass: '', from: '' // Changed from subjectTemplate
|
||||
};
|
||||
telegramConfig.value = { botToken: '', chatId: '', messageTemplate: '' };
|
||||
webhookHeadersString.value = '{}';
|
||||
headerError.value = null;
|
||||
telegramConfig.value = { botToken: '', chatId: '', messageTemplate: '', customDomain: '' }; // Add customDomain
|
||||
webhookHeadersString.value = '{}';
|
||||
headerError.value = null;
|
||||
testError.value = null;
|
||||
testResult.value = null;
|
||||
testingNotification.value = false;
|
||||
|
||||
@@ -810,7 +810,7 @@ const formatXtermLabel = (key: keyof ITheme): string => {
|
||||
return key.replace(/([A-Z])/g, ' $1').replace(/^./, (str) => str.toUpperCase());
|
||||
};
|
||||
|
||||
// --- 新增:计算属性 ---
|
||||
// --- 计算属性 ---
|
||||
|
||||
// 获取当前激活主题的名称
|
||||
const activeThemeName = computed(() => {
|
||||
@@ -938,7 +938,7 @@ const handleFocusAndSelect = (event: FocusEvent) => {
|
||||
<main class="flex-grow p-3 md:p-4 md:px-6 overflow-y-auto min-h-0">
|
||||
<section v-if="currentTab === 'ui'">
|
||||
<h3 class="mt-0 border-b border-border pb-2 mb-4 text-lg font-semibold text-foreground">{{ t('styleCustomizer.uiStyles') }}</h3>
|
||||
<!-- 新增:主题模式选择 - 小屏幕堆叠 -->
|
||||
<!-- 主题模式选择 - 小屏幕堆叠 -->
|
||||
<div class="grid grid-cols-1 md:grid-cols-[auto_1fr] items-start md:items-center gap-2 md:gap-3 mb-6">
|
||||
<label class="text-left text-foreground text-sm font-medium mb-1 md:mb-0">{{ t('styleCustomizer.themeModeLabel', '主题模式:') }}</label> <!-- TODO: 添加翻译 -->
|
||||
<div class="flex gap-2 justify-start flex-wrap">
|
||||
|
||||
Reference in New Issue
Block a user