chore: opt tempalte style
This commit is contained in:
@@ -8,7 +8,7 @@ import { Textarea } from '@/components/ui/textarea'
|
||||
import { Badge } from '@/components/ui/badge'
|
||||
import { api } from '@/api'
|
||||
import { toast } from 'vue-sonner'
|
||||
import { Save, RefreshCcw, Info, ChevronDown, ChevronUp } from 'lucide-vue-next'
|
||||
import { Save, RefreshCcw, Info, ChevronDown, ChevronUp, Shield, Terminal } from 'lucide-vue-next'
|
||||
|
||||
const props = defineProps<{
|
||||
activeTab?: string,
|
||||
@@ -26,6 +26,7 @@ const expandedEvents = ref<Record<string, boolean>>({})
|
||||
const eventGroups = [
|
||||
{
|
||||
title: '系统与安全事件',
|
||||
description: '配置帐号登录、安全告警等系统级事件的通知推送',
|
||||
events: [
|
||||
{
|
||||
id: 'user_login',
|
||||
@@ -49,6 +50,7 @@ const eventGroups = [
|
||||
},
|
||||
{
|
||||
title: '任务执行事件',
|
||||
description: '配置定时任务执行状态的通知行为(支持全局模板定制)',
|
||||
events: [
|
||||
{
|
||||
id: 'task_success',
|
||||
@@ -110,120 +112,125 @@ function toggleExpand(id: string) {
|
||||
expandedEvents.value[id] = !expandedEvents.value[id]
|
||||
}
|
||||
|
||||
|
||||
|
||||
onMounted(() => {
|
||||
loadSettings()
|
||||
// 默认展开第一个
|
||||
expandedEvents.value['user_login'] = true
|
||||
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="space-y-6">
|
||||
<Card class="border-none shadow-none bg-transparent">
|
||||
<CardHeader class="px-0 pt-0 pb-4">
|
||||
<div class="space-y-4">
|
||||
<div class="flex items-center justify-between gap-4">
|
||||
<CardTitle class="text-xl sm:text-2xl font-bold tracking-tight">通知模板管理</CardTitle>
|
||||
<div class="flex items-center gap-2 shrink-0">
|
||||
<Button variant="outline" size="sm" @click="loadSettings" :disabled="loading" class="h-9 w-9 sm:w-auto p-0 sm:px-3">
|
||||
<RefreshCcw class="w-4 h-4 sm:mr-2" :class="{ 'animate-spin': loading }" />
|
||||
<span class="hidden sm:inline">刷新</span>
|
||||
</Button>
|
||||
<Button size="sm" @click="saveSettings" :disabled="saving" class="h-9 px-4">
|
||||
<Save class="w-4 h-4 sm:mr-2" />
|
||||
<span class="hidden sm:inline">{{ saving ? '保存中...' : '提交修改' }}</span>
|
||||
<span class="sm:hidden">保存</span>
|
||||
</Button>
|
||||
</div>
|
||||
<!-- 外层主卡片 -->
|
||||
<Card>
|
||||
<CardHeader class="border-b bg-muted/10 !pb-0">
|
||||
<div class="flex items-start justify-between gap-4">
|
||||
<div class="flex-1 min-w-0">
|
||||
<CardTitle class="font-bold tracking-tight">通知模板</CardTitle>
|
||||
<CardDescription class="text-xs sm:text-sm mt-1 text-muted-foreground/80">
|
||||
定制消息格式,支持全局前缀与动态变量内置变量
|
||||
</CardDescription>
|
||||
</div>
|
||||
<div class="flex items-center gap-1.5 sm:gap-2 shrink-0">
|
||||
<Button variant="outline" size="sm" @click="loadSettings" :disabled="loading" class="h-8 sm:h-9 px-2 sm:px-3 text-xs sm:text-sm">
|
||||
<RefreshCcw class="w-3.5 h-3.5 sm:mr-2" :class="{ 'animate-spin': loading }" />
|
||||
<span class="hidden sm:inline">刷新</span>
|
||||
</Button>
|
||||
<Button size="sm" @click="saveSettings" :disabled="saving" class="h-8 sm:h-9 px-3 sm:px-4 text-xs sm:text-sm">
|
||||
<Save class="w-3.5 h-3.5 sm:mr-2" />
|
||||
<span class="hidden sm:inline">{{ saving ? '保存中...' : '提交修改' }}</span>
|
||||
<span class="sm:hidden">提交</span>
|
||||
</Button>
|
||||
</div>
|
||||
<CardDescription class="text-xs sm:text-sm leading-relaxed max-w-2xl">
|
||||
定制通知的消息格式,支持局前缀与动态变量内置变量
|
||||
</CardDescription>
|
||||
</div>
|
||||
</CardHeader>
|
||||
|
||||
<CardContent class="px-0 space-y-6">
|
||||
<!-- 全局前缀 -->
|
||||
<div class="p-5 rounded-2xl bg-accent/20 border border-accent/30 space-y-4">
|
||||
<div class="flex items-center gap-2 text-sm font-bold text-foreground">
|
||||
<div class="w-1.5 h-4 bg-primary rounded-full" />
|
||||
全局消息前缀
|
||||
</div>
|
||||
<div class="flex flex-col lg:flex-row gap-4 items-start lg:items-end">
|
||||
<div class="flex-1 w-full space-y-2">
|
||||
<Label class="text-[11px] text-muted-foreground ml-1 font-medium tracking-wide uppercase">该前缀会添加在所有通知标题的最前面</Label>
|
||||
<Input v-model="prefix" placeholder="例如: [生产环境]" class="bg-background/60 h-10 border-accent/20 focus:border-primary/50" />
|
||||
<CardContent class="p-3 sm:px-5 sm:py-4 space-y-4">
|
||||
<!-- 全局前缀设置 (内嵌卡片) -->
|
||||
<Card class="bg-muted/5 border-dashed shadow-sm">
|
||||
<CardHeader class="pb-2 px-4 pt-3">
|
||||
<div class="flex items-center gap-2">
|
||||
<div class="w-1.5 h-4 bg-primary rounded-full" />
|
||||
<CardTitle class="text-base font-bold">全局消息前缀</CardTitle>
|
||||
</div>
|
||||
<div class="flex items-center gap-3 px-4 h-10 rounded-lg bg-background/40 border border-dashed border-muted-foreground/20 text-[11px] text-muted-foreground shrink-0 w-full lg:w-auto">
|
||||
预览效果: <span class="font-mono text-primary font-bold tracking-tight">{{ prefix }} 用户登录成功</span>
|
||||
<CardDescription class="text-[11px] sm:text-xs text-muted-foreground/80">该前缀会添加在所有通知标题的最前面,用于快速识别消息来源</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent class="px-4 pb-3 space-y-3">
|
||||
<div class="flex flex-col sm:flex-row gap-4 items-start sm:items-center">
|
||||
<div class="flex-1 w-full">
|
||||
<Label class="text-[10px] font-bold text-muted-foreground uppercase tracking-wider ml-1 mb-1.5 block">前缀文本</Label>
|
||||
<Input v-model="prefix" placeholder="例如: [生产环境]"
|
||||
class="h-9 text-sm bg-background border-muted-foreground/20 focus:border-primary/50" />
|
||||
</div>
|
||||
<div class="flex items-center gap-3 px-4 h-9 rounded-lg bg-muted/40 border border-dashed border-muted-foreground/20 text-[11px] text-muted-foreground shrink-0 w-full sm:w-auto mt-4 sm:mt-5">
|
||||
预览效果: <span class="font-mono text-primary font-bold tracking-tight">{{ prefix }} 用户登录成功</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<!-- 模板详情 -->
|
||||
<div class="w-full space-y-8 mt-4">
|
||||
<!-- 模板详情分组列表 -->
|
||||
<div class="space-y-5">
|
||||
<div v-for="group in eventGroups" :key="group.title" class="space-y-4">
|
||||
<div class="flex items-center gap-3 ml-1">
|
||||
<h3 class="text-[11px] font-black text-muted-foreground uppercase tracking-[0.2em]">{{ group.title }}</h3>
|
||||
<div class="h-[1px] flex-1 bg-gradient-to-r from-border/60 to-transparent" />
|
||||
</div>
|
||||
|
||||
<!-- 任务执行事件专属提示 -->
|
||||
<div v-if="group.title === '任务执行事件'" class="p-3 rounded-lg bg-yellow-500/5 border border-yellow-500/10 flex gap-3 text-xs text-yellow-600/80 leading-relaxed mb-6">
|
||||
<Info class="w-4 h-4 shrink-0 mt-0.5 text-yellow-500/50" />
|
||||
<!-- 分组标题 (参考事件绑定样式) -->
|
||||
<div class="flex items-start gap-2.5 px-1">
|
||||
<component :is="group.title === '任务执行事件' ? Terminal : Shield" class="w-5 h-5 text-primary shrink-0 mt-0.5" />
|
||||
<div>
|
||||
<span class="font-bold">配置提示:</span>
|
||||
部分变量(如 <code v-pre class="bg-yellow-500/10 px-1 rounded text-yellow-600">{{ output }}</code>)依赖于的具体绑定配置。请确保在
|
||||
<span class="font-bold underline decoration-dotted cursor-pointer hover:text-yellow-600" @click="emit('update:activeTab', 'events')">事件绑定</span>
|
||||
的高级设置中开启了 <span class="text-foreground/80">“发送任务日志”</span>,否则通知消息中该变量将为空。
|
||||
<h3 class="text-sm font-bold text-foreground leading-none">{{ group.title }}</h3>
|
||||
<p class="text-[11px] text-muted-foreground mt-1.5">{{ group.description }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-for="event in group.events" :key="event.id"
|
||||
class="border rounded-xl bg-background/50 overflow-hidden transition-all duration-200"
|
||||
:class="{ 'ring-1 ring-primary/20 bg-accent/5': expandedEvents[event.id] }">
|
||||
|
||||
<div class="flex items-center justify-between p-4 cursor-pointer select-none" @click="toggleExpand(event.id)">
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="w-8 h-8 rounded-lg bg-primary/5 flex items-center justify-center text-primary">
|
||||
<Info class="w-4 h-4" />
|
||||
</div>
|
||||
<div class="text-left">
|
||||
<div class="text-sm font-semibold">{{ event.name }}</div>
|
||||
<div class="text-[10px] text-muted-foreground font-normal uppercase tracking-tight">ID: {{ event.id }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<component :is="expandedEvents[event.id] ? ChevronUp : ChevronDown" class="w-4 h-4 text-muted-foreground" />
|
||||
</div>
|
||||
|
||||
<div v-if="expandedEvents[event.id]" class="px-4 pb-6 space-y-4 pt-2 border-t border-dashed">
|
||||
<!-- 变量提示 -->
|
||||
<div class="flex flex-wrap items-center gap-2 py-2">
|
||||
<span class="text-[10px] font-bold text-muted-foreground mr-1 uppercase">可用参数:</span>
|
||||
<Badge v-for="v in event.variables" :key="v" variant="secondary"
|
||||
class="cursor-pointer hover:bg-primary/10 hover:text-primary transition-colors py-0.5 px-2 font-medium text-[11px] border-none"
|
||||
@click="insertVariable(event.keys.text, v)"
|
||||
v-text="'{{' + v + '}}'">
|
||||
</Badge>
|
||||
<!-- 事件网格 -->
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div v-for="event in group.events" :key="event.id"
|
||||
class="flex flex-col p-4 rounded-xl border bg-background hover:border-primary/20 transition-all duration-200 shadow-sm"
|
||||
:class="{ 'ring-1 ring-primary/20 bg-accent/5': expandedEvents[event.id] }">
|
||||
|
||||
<!-- 卡片头部 (可点击折叠) -->
|
||||
<div class="flex items-center justify-between cursor-pointer select-none"
|
||||
:class="{ 'mb-4 border-b pb-3 border-dashed': expandedEvents[event.id] }"
|
||||
@click="toggleExpand(event.id)">
|
||||
<div class="flex items-center gap-2.5">
|
||||
<div class="w-7 h-7 rounded-lg bg-primary/5 flex items-center justify-center text-primary">
|
||||
<Info class="w-3.5 h-3.5" />
|
||||
</div>
|
||||
<div class="flex flex-col">
|
||||
<span class="text-sm font-bold">{{ event.name }}</span>
|
||||
<span class="text-[10px] text-muted-foreground font-mono opacity-50">ID: {{ event.id }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<component :is="expandedEvents[event.id] ? ChevronUp : ChevronDown" class="w-4 h-4 text-muted-foreground" />
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col gap-5">
|
||||
<!-- 标题模板 -->
|
||||
<div class="space-y-2.5">
|
||||
<Label class="text-[11px] font-bold flex items-center gap-1.5 text-muted-foreground uppercase tracking-wide">
|
||||
推送标题模板
|
||||
</Label>
|
||||
<Input v-model="templates[event.keys.title]" placeholder="通知标题" class="bg-background/80 h-10 border-accent/20 focus:border-primary/50" />
|
||||
<!-- 展开后的编辑表单 -->
|
||||
<div v-if="expandedEvents[event.id]" class="space-y-4 animate-in fade-in slide-in-from-top-1 duration-200">
|
||||
<!-- 可用参数 -->
|
||||
<div class="flex flex-col gap-1.5 p-2.5 rounded-lg bg-muted/30 border border-dashed">
|
||||
<span class="text-[9px] font-bold text-muted-foreground uppercase tracking-wider">可用参数 (点击插入):</span>
|
||||
<div class="flex flex-wrap gap-1.5">
|
||||
<Badge v-for="v in event.variables" :key="v" variant="secondary"
|
||||
class="cursor-pointer hover:bg-primary/20 hover:text-primary transition-colors py-0 px-2 font-mono text-[10px] border-none bg-muted-foreground/10"
|
||||
@click="insertVariable(event.keys.text, v)"
|
||||
v-text="'{{' + v + '}}'" />
|
||||
</div>
|
||||
</div>
|
||||
<!-- 内容模板 -->
|
||||
<div class="space-y-2.5">
|
||||
<Label class="text-[11px] font-bold flex items-center gap-1.5 text-muted-foreground uppercase tracking-wide">
|
||||
推送正文模板
|
||||
</Label>
|
||||
<Textarea v-model="templates[event.keys.text]"
|
||||
:rows="4"
|
||||
placeholder="通知详细内容..."
|
||||
class="resize-none font-sans leading-relaxed bg-background/80 border-accent/20 focus:border-primary/50" />
|
||||
|
||||
<!-- 模板内容输入 -->
|
||||
<div class="space-y-4">
|
||||
<div class="space-y-1.5">
|
||||
<Label class="text-[10px] font-bold text-muted-foreground uppercase tracking-wider ml-1">推送标题模板</Label>
|
||||
<Input v-model="templates[event.keys.title]" placeholder="通知标题"
|
||||
class="h-9 text-sm bg-background border-muted-foreground/20 focus:border-primary/50" />
|
||||
</div>
|
||||
<div class="space-y-1.5">
|
||||
<Label class="text-[10px] font-bold text-muted-foreground uppercase tracking-wider ml-1">推送正文模板</Label>
|
||||
<Textarea v-model="templates[event.keys.text]"
|
||||
:rows="4"
|
||||
placeholder="通知详细内容..."
|
||||
class="resize-none font-sans text-sm leading-relaxed bg-background border-muted-foreground/20 focus:border-primary/50" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user