<template>
  <div class="notice-page">
    <!-- 页头 -->
    <div class="page-header">
      <div class="header-left">
        <div class="header-icon">
          <svg viewBox="0 0 24 24" width="28" height="28"><path d="M12 22c1.1 0 2-.9 2-2h-4a2 2 0 0 0 2 2zm6-6v-5c0-3.07-1.64-5.64-4.5-6.32V4c0-.83-.67-1.5-1.5-1.5s-1.5.67-1.5 1.5v.68C7.63 5.36 6 7.92 6 11v5l-2 2v1h16v-1l-2-2z" fill="#409eff"/></svg>
        </div>
        <div>
          <h2 class="page-title">通知管理</h2>
          <p class="page-desc">管理通知渠道开关与消息模板，控制各业务事件的通知行为</p>
        </div>
      </div>
    </div>

    <!-- 标签页 -->
    <el-tabs v-model="activeTab" type="border-card" class="main-tabs">
      <!-- ==================== 渠道配置 ==================== -->
      <el-tab-pane name="channel">
        <template #label>
          <span class="tab-label">
            <svg viewBox="0 0 24 24" width="16" height="16"><path d="M21 3H3c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h18c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 16H3V5h18v14z" fill="currentColor"/><path d="M8 14h8v2H8zm0-4h8v2H8z" fill="currentColor"/></svg>
            渠道配置
          </span>
        </template>

        <div class="channel-section" v-loading="channelLoading">
          <!-- 短信渠道 -->
          <div class="channel-group">
            <div class="group-header sms-header">
              <div class="group-icon sms-icon">
                <svg viewBox="0 0 24 24" width="22" height="22"><path d="M20 2H4c-1.1 0-2 .9-2 2v18l4-4h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm0 14H5.17L4 17.17V4h16v12z" fill="currentColor"/><path d="M7 9h10v2H7z" fill="currentColor"/><path d="M7 6h7v2H7z" fill="currentColor"/></svg>
              </div>
              <div class="group-title-area">
                <h3>短信通知</h3>
                <span class="group-count">{{ smsChannels.length }} 个事件</span>
              </div>
              <div class="group-summary">
                <el-tag type="success" size="small" effect="dark" round>{{ smsChannels.filter(c => c.enabled).length }} 已启用</el-tag>
                <el-tag type="info" size="small" effect="plain" round>{{ smsChannels.filter(c => !c.enabled).length }} 已关闭</el-tag>
              </div>
            </div>
            <div class="channel-cards">
              <div v-for="item in smsChannels" :key="item.id" class="channel-card" :class="{ 'enabled': item.enabled, 'disabled': !item.enabled }">
                <div class="card-top">
                  <span class="card-event-name">{{ item.eventName || '-' }}</span>
                  <el-switch v-model="item.enabled" :loading="item._switching" size="small" @change="(val) => handleToggle(item, val)" />
                </div>
                <div class="card-bottom">
                  <code class="card-event-type">{{ item.eventType }}</code>
                  <span v-if="item.note" class="card-note" :title="item.note">{{ item.note }}</span>
                </div>
              </div>
              <el-empty v-if="smsChannels.length === 0" description="暂无短信通知配置" :image-size="60" />
            </div>
          </div>

          <!-- 邮件渠道 -->
          <div class="channel-group">
            <div class="group-header email-header">
              <div class="group-icon email-icon">
                <svg viewBox="0 0 24 24" width="22" height="22"><path d="M20 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 4l-8 5-8-5V6l8 5 8-5v2z" fill="currentColor"/></svg>
              </div>
              <div class="group-title-area">
                <h3>邮件通知</h3>
                <span class="group-count">{{ emailChannels.length }} 个事件</span>
              </div>
              <div class="group-summary">
                <el-tag type="success" size="small" effect="dark" round>{{ emailChannels.filter(c => c.enabled).length }} 已启用</el-tag>
                <el-tag type="info" size="small" effect="plain" round>{{ emailChannels.filter(c => !c.enabled).length }} 已关闭</el-tag>
              </div>
            </div>
            <div class="channel-cards">
              <div v-for="item in emailChannels" :key="item.id" class="channel-card" :class="{ 'enabled': item.enabled, 'disabled': !item.enabled }">
                <div class="card-top">
                  <span class="card-event-name">{{ item.eventName || '-' }}</span>
                  <el-switch v-model="item.enabled" :loading="item._switching" size="small" @change="(val) => handleToggle(item, val)" />
                </div>
                <div class="card-bottom">
                  <code class="card-event-type">{{ item.eventType }}</code>
                  <span v-if="item.note" class="card-note" :title="item.note">{{ item.note }}</span>
                </div>
              </div>
              <el-empty v-if="emailChannels.length === 0" description="暂无邮件通知配置" :image-size="60" />
            </div>
          </div>

          <!-- 其他渠道 -->
          <div class="channel-group" v-if="otherChannels.length > 0">
            <div class="group-header other-header">
              <div class="group-icon other-icon">
                <svg viewBox="0 0 24 24" width="22" height="22"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z" fill="currentColor"/></svg>
              </div>
              <div class="group-title-area">
                <h3>其他渠道</h3>
                <span class="group-count">{{ otherChannels.length }} 个事件</span>
              </div>
            </div>
            <div class="channel-cards">
              <div v-for="item in otherChannels" :key="item.id" class="channel-card" :class="{ 'enabled': item.enabled, 'disabled': !item.enabled }">
                <div class="card-top">
                  <span class="card-event-name">{{ item.eventName || '-' }}</span>
                  <el-tag size="small" type="info">{{ item.channel }}</el-tag>
                  <el-switch v-model="item.enabled" :loading="item._switching" size="small" @change="(val) => handleToggle(item, val)" />
                </div>
                <div class="card-bottom">
                  <code class="card-event-type">{{ item.eventType }}</code>
                </div>
              </div>
            </div>
          </div>
        </div>
      </el-tab-pane>

      <!-- ==================== 模板管理 ==================== -->
      <el-tab-pane name="template">
        <template #label>
          <span class="tab-label">
            <svg viewBox="0 0 24 24" width="16" height="16"><path d="M14 2H6c-1.1 0-2 .9-2 2v16c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V8l-6-6zm4 18H6V4h7v5h5v11z" fill="currentColor"/><path d="M8 12h8v2H8zm0 4h5v2H8z" fill="currentColor"/></svg>
            模板管理
          </span>
        </template>

        <div class="template-section">
          <div class="tpl-toolbar">
            <div class="tpl-toolbar-left">
              <el-radio-group v-model="tplTypeFilter" size="default" @change="filterTemplates">
                <el-radio-button value="">全部</el-radio-button>
                <el-radio-button value="email">
                  <svg viewBox="0 0 24 24" width="14" height="14" style="vertical-align:-2px;margin-right:3px"><path d="M20 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 4l-8 5-8-5V6l8 5 8-5v2z" fill="currentColor"/></svg>
                  邮件模板
                </el-radio-button>
                <el-radio-button value="phone">
                  <svg viewBox="0 0 24 24" width="14" height="14" style="vertical-align:-2px;margin-right:3px"><path d="M20 2H4c-1.1 0-2 .9-2 2v18l4-4h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm0 14H5.17L4 17.17V4h16v12z" fill="currentColor"/></svg>
                  短信模板
                </el-radio-button>
              </el-radio-group>
            </div>
            <el-button type="primary" :icon="Plus" @click="openTplDialog()">新增模板</el-button>
          </div>

          <el-table :data="filteredTemplates" v-loading="tplLoading" stripe border style="width: 100%">
            <el-table-column prop="id" label="ID" width="65" align="center" />
            <el-table-column prop="name" label="模板名称" min-width="140">
              <template #default="{ row }">
                <span class="tpl-name">{{ row.name }}</span>
              </template>
            </el-table-column>
            <el-table-column prop="tag" label="模板标识" min-width="160">
              <template #default="{ row }">
                <code class="tpl-tag">{{ row.tag }}</code>
              </template>
            </el-table-column>
            <el-table-column prop="type" label="类型" width="110" align="center">
              <template #default="{ row }">
                <el-tag :type="row.type === 'email' ? 'warning' : 'success'" effect="dark" size="small" round>
                  <span style="display:inline-flex;align-items:center;gap:3px">
                    <svg v-if="row.type === 'email'" viewBox="0 0 24 24" width="12" height="12"><path d="M20 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 4l-8 5-8-5V6l8 5 8-5v2z" fill="currentColor"/></svg>
                    <svg v-else viewBox="0 0 24 24" width="12" height="12"><path d="M20 2H4c-1.1 0-2 .9-2 2v18l4-4h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm0 14H5.17L4 17.17V4h16v12z" fill="currentColor"/></svg>
                    {{ row.type === 'email' ? '邮件' : '短信' }}
                  </span>
                </el-tag>
              </template>
            </el-table-column>
            <el-table-column prop="content" label="模板内容" min-width="240">
              <template #default="{ row }">
                <div class="tpl-content-preview" :title="row.content">{{ row.content || '-' }}</div>
              </template>
            </el-table-column>
            <el-table-column prop="args" label="参数" min-width="180">
              <template #default="{ row }">
                <div v-if="row.args" class="args-tags">
                  <el-tag v-for="arg in parseArgs(row.args)" :key="arg" size="small" type="info" effect="plain" class="arg-tag">{{ arg }}</el-tag>
                </div>
                <span v-else class="no-args">-</span>
              </template>
            </el-table-column>
            <el-table-column prop="note" label="说明" min-width="160" show-overflow-tooltip>
              <template #default="{ row }"><span class="note-text">{{ row.note || '-' }}</span></template>
            </el-table-column>
            <el-table-column label="更新时间" width="160" align="center">
              <template #default="{ row }">{{ formatTime(row.UpdatedAt) }}</template>
            </el-table-column>
            <el-table-column label="操作" width="130" align="center" fixed="right">
              <template #default="{ row }">
                <el-button size="small" link type="primary" @click="openTplDialog(row)">编辑</el-button>
                <el-button size="small" link type="danger" @click="handleDeleteTpl(row)">删除</el-button>
              </template>
            </el-table-column>
          </el-table>
        </div>
      </el-tab-pane>
    </el-tabs>

    <!-- ==================== 模板编辑弹窗 ==================== -->
    <el-dialog v-model="tplDialogVisible" :title="tplForm.id ? '编辑模板' : '新增模板'" width="1060px" append-to-body destroy-on-close @opened="onTplDialogOpened">
      <div class="tpl-dialog-body">
        <!-- 左侧：表单 -->
        <div class="tpl-dialog-left">
          <el-form :model="tplForm" :rules="tplRules" ref="tplFormRef" label-width="90px" label-position="right">
            <el-form-item label="模板名称" prop="name">
              <el-input v-model="tplForm.name" placeholder="例：用户注册通知" maxlength="100" show-word-limit />
            </el-form-item>
            <el-form-item label="模板标识" prop="tag">
              <el-input v-model="tplForm.tag" placeholder="例：user_register_notify" maxlength="100" :disabled="!!tplForm.id" />
            </el-form-item>
            <el-form-item label="模板类型" prop="type">
              <el-radio-group v-model="tplForm.type">
                <el-radio value="email">
                  <span class="radio-with-icon">
                    <svg viewBox="0 0 24 24" width="14" height="14"><path d="M20 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 4l-8 5-8-5V6l8 5 8-5v2z" fill="#e6a23c"/></svg>
                    邮件
                  </span>
                </el-radio>
                <el-radio value="phone">
                  <span class="radio-with-icon">
                    <svg viewBox="0 0 24 24" width="14" height="14"><path d="M20 2H4c-1.1 0-2 .9-2 2v18l4-4h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm0 14H5.17L4 17.17V4h16v12z" fill="#67c23a"/></svg>
                    短信
                  </span>
                </el-radio>
              </el-radio-group>
            </el-form-item>
            <el-form-item label="可用参数" v-if="tplFormArgs.length > 0">
              <div class="args-btn-group">
                <el-button v-for="arg in tplFormArgs" :key="arg" size="small" @click="insertArg(arg)" class="arg-insert-btn">
                  <svg viewBox="0 0 24 24" width="12" height="12" style="margin-right:3px"><path d="M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z" fill="currentColor"/></svg>
                  {{ arg }}
                </el-button>
              </div>
              <div class="form-hint">点击参数按钮可将其插入到模板内容光标处</div>
            </el-form-item>
            <el-form-item label="模板内容" prop="content">
              <el-input ref="contentInputRef" v-model="tplForm.content" type="textarea" :rows="8" placeholder="模板内容，点击上方参数按钮插入变量" />
            </el-form-item>
          </el-form>
        </div>

        <!-- 右侧：渲染预览 -->
        <div class="tpl-dialog-right">
          <div class="preview-header">
            <span class="preview-title">
              <svg viewBox="0 0 24 24" width="16" height="16"><path d="M12 4.5C7 4.5 2.73 7.61 1 12c1.73 4.39 6 7.5 11 7.5s9.27-3.11 11-7.5c-1.73-4.39-6-7.5-11-7.5zM12 17c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5zm0-8c-1.66 0-3 1.34-3 3s1.34 3 3 3 3-1.34 3-3-1.34-3-3-3z" fill="currentColor"/></svg>
              渲染预览
            </span>
            <el-button size="small" link type="primary" @click="fetchPreview" :loading="previewLoading">
              <el-icon><Refresh /></el-icon> 刷新
            </el-button>
          </div>

          <div class="preview-body" v-loading="previewLoading">
            <template v-if="previewData.rendered">
              <div class="preview-rendered" :class="{ 'html-mode': tplForm.type === 'email' }" v-html="previewRenderedHtml"></div>
            </template>
            <div v-else-if="previewError" class="preview-empty">
              <el-icon :size="32" color="#f56c6c"><WarningFilled /></el-icon>
              <p>{{ previewError }}</p>
            </div>
            <div v-else class="preview-empty">
              <el-icon :size="32" color="#c0c4cc"><View /></el-icon>
              <p>填写模板标识和类型后自动加载预览</p>
            </div>
          </div>
        </div>
      </div>
      <template #footer>
        <el-button @click="tplDialogVisible = false">取消</el-button>
        <el-button type="primary" :loading="tplSubmitting" @click="handleSubmitTpl">确定</el-button>
      </template>
    </el-dialog>
  </div>
</template>

<script setup>
import { ref, computed, onMounted, nextTick, watch } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { Plus, Refresh, View, WarningFilled } from '@element-plus/icons-vue'
import {
  getNoticeChannelList, updateNoticeChannel,
  getNoticeTemplateList, addNoticeTemplate, updateNoticeTemplate, deleteNoticeTemplate,
  previewNoticeTemplate
} from '@/api/admin/noticeChannel'

// ==================== 渠道配置 ====================
const activeTab = ref('channel')
const channelLoading = ref(false)
const channelList = ref([])

const smsChannels = computed(() => channelList.value.filter(c => c.channel === 'sms'))
const emailChannels = computed(() => channelList.value.filter(c => c.channel === 'email'))
const otherChannels = computed(() => channelList.value.filter(c => c.channel !== 'sms' && c.channel !== 'email'))

const loadChannels = async () => {
  channelLoading.value = true
  try {
    const res = await getNoticeChannelList()
    const body = res?.data
    if (body?.code === 200) {
      const list = body.data?.data || body.data || []
      channelList.value = (Array.isArray(list) ? list : []).map(item => ({ ...item, _switching: false }))
    }
  } catch {
    ElMessage.error('加载渠道配置失败')
  } finally {
    channelLoading.value = false
  }
}

const handleToggle = async (row, val) => {
  row._switching = true
  try {
    const fd = new FormData()
    fd.append('id', row.id)
    fd.append('enabled', val)
    const res = await updateNoticeChannel(fd)
    if (res?.data?.code === 200) {
      const chName = row.channel === 'sms' ? '短信' : row.channel === 'email' ? '邮件' : row.channel
      ElMessage.success(`已${val ? '启用' : '关闭'} ${row.eventName} - ${chName}`)
    } else {
      row.enabled = !val
      ElMessage.error(res?.data?.message || '操作失败')
    }
  } catch {
    row.enabled = !val
    ElMessage.error('操作失败')
  } finally {
    row._switching = false
  }
}

// ==================== 模板管理 ====================
const tplLoading = ref(false)
const templateList = ref([])
const tplTypeFilter = ref('')

const filteredTemplates = computed(() => {
  if (!tplTypeFilter.value) return templateList.value
  return templateList.value.filter(t => t.type === tplTypeFilter.value)
})

const filterTemplates = () => {}

const loadTemplates = async () => {
  tplLoading.value = true
  try {
    const res = await getNoticeTemplateList()
    const body = res?.data
    if (body?.code === 200) {
      const list = body.data?.data || body.data || []
      templateList.value = Array.isArray(list) ? list : []
    }
  } catch {
    ElMessage.error('加载模板列表失败')
  } finally {
    tplLoading.value = false
  }
}

// 模板弹窗
const tplDialogVisible = ref(false)
const tplSubmitting = ref(false)
const tplFormRef = ref(null)
const tplForm = ref({ id: 0, name: '', tag: '', content: '', type: 'email', args: '' })
const tplRules = {
  name: [{ required: true, message: '请输入模板名称', trigger: 'blur' }],
  tag: [{ required: true, message: '请输入模板标识', trigger: 'blur' }],
  type: [{ required: true, message: '请选择类型', trigger: 'change' }],
  content: [{ required: true, message: '请输入模板内容', trigger: 'blur' }]
}

const contentInputRef = ref(null)

const tplFormArgs = computed(() => parseArgs(tplForm.value.args))

const insertArg = (arg) => {
  const snippet = `{{index . "${arg}"}}`
  const textarea = contentInputRef.value?.textarea
  if (textarea) {
    const start = textarea.selectionStart
    const end = textarea.selectionEnd
    const before = tplForm.value.content.slice(0, start)
    const after = tplForm.value.content.slice(end)
    tplForm.value.content = before + snippet + after
    nextTick(() => {
      const pos = start + snippet.length
      textarea.focus()
      textarea.setSelectionRange(pos, pos)
    })
  } else {
    tplForm.value.content += snippet
  }
}

const openTplDialog = (row) => {
  if (row) {
    tplForm.value = { id: row.id, name: row.name, tag: row.tag, content: row.content, type: row.type, args: row.args || '' }
  } else {
    tplForm.value = { id: 0, name: '', tag: '', content: '', type: 'email', args: '' }
  }
  tplDialogVisible.value = true
}

const handleSubmitTpl = async () => {
  const formEl = tplFormRef.value
  if (!formEl) return
  await formEl.validate()

  tplSubmitting.value = true
  try {
    const fd = new FormData()
    if (tplForm.value.id) fd.append('id', tplForm.value.id)
    fd.append('name', tplForm.value.name)
    fd.append('tag', tplForm.value.tag)
    fd.append('content', tplForm.value.content)
    fd.append('type', tplForm.value.type)
    if (tplForm.value.args) fd.append('args', tplForm.value.args)

    const apiFn = tplForm.value.id ? updateNoticeTemplate : addNoticeTemplate
    const res = await apiFn(fd)
    if (res?.data?.code === 200) {
      ElMessage.success(tplForm.value.id ? '模板已更新' : '模板已添加')
      tplDialogVisible.value = false
      loadTemplates()
    } else {
      ElMessage.error(res?.data?.message || '操作失败')
    }
  } catch {
    ElMessage.error('操作失败')
  } finally {
    tplSubmitting.value = false
  }
}

const handleDeleteTpl = async (row) => {
  try {
    await ElMessageBox.confirm(`确定要删除模板「${row.name}」吗？`, '确认删除', { type: 'warning', confirmButtonText: '删除', cancelButtonText: '取消' })
    const res = await deleteNoticeTemplate({ id: row.id })
    if (res?.data?.code === 200) {
      ElMessage.success('模板已删除')
      loadTemplates()
    } else {
      ElMessage.error(res?.data?.message || '删除失败')
    }
  } catch {}
}

// ==================== 渲染预览 ====================
const previewLoading = ref(false)
const previewError = ref('')
const previewData = ref({ rendered: '', default_args: null })

const previewRenderedHtml = computed(() => {
  const text = previewData.value.rendered || ''
  if (tplForm.value.type === 'email') return text
  return text.replace(/\n/g, '<br>')
})

let previewTimer = null
const debouncedFetchPreview = () => {
  clearTimeout(previewTimer)
  previewTimer = setTimeout(() => fetchPreview(), 600)
}

const fetchPreview = async () => {
  const tag = tplForm.value.tag?.trim()
  const type = tplForm.value.type
  const content = tplForm.value.content?.trim()

  if (!content && (!tag || !type)) {
    previewData.value = { rendered: '', default_args: null }
    previewError.value = ''
    return
  }

  previewLoading.value = true
  previewError.value = ''
  try {
    const params = {}
    if (content) {
      params.content = content
    } else {
      params.tag = tag
      params.type = type
    }
    const res = await previewNoticeTemplate(params)
    const body = res?.data
    if (body?.code === 200 && body.data) {
      previewData.value = body.data
    } else {
      previewData.value = { rendered: '', default_args: null }
      previewError.value = body?.message || '渲染失败'
    }
  } catch {
    previewData.value = { rendered: '', default_args: null }
    previewError.value = '请求失败'
  } finally {
    previewLoading.value = false
  }
}

const onTplDialogOpened = () => {
  if (tplForm.value.tag && tplForm.value.type) {
    fetchPreview()
  }
}

watch(() => tplForm.value.tag, debouncedFetchPreview)
watch(() => tplForm.value.type, debouncedFetchPreview)
watch(() => tplForm.value.content, debouncedFetchPreview)

// ==================== 工具函数 ====================
const formatTime = (t) => {
  if (!t) return '-'
  const d = new Date(t)
  if (isNaN(d.getTime())) return t
  return d.toLocaleString('zh-CN', { year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', second: '2-digit' })
}

const parseArgs = (args) => {
  if (!args) return []
  return args.split('/').map(s => s.trim()).filter(Boolean)
}

// ==================== 初始化 ====================
onMounted(() => {
  loadChannels()
  loadTemplates()
})
</script>

<style scoped>
.notice-page { padding: 20px 24px; }

/* ===== 页头 ===== */
.page-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 20px; }
.header-left { display: flex; align-items: center; gap: 14px; }
.header-icon { width: 48px; height: 48px; border-radius: 12px; background: linear-gradient(135deg, #ecf5ff 0%, #d9ecff 100%); display: flex; align-items: center; justify-content: center; flex-shrink: 0; }
.page-title { margin: 0 0 2px; font-size: 20px; font-weight: 700; color: #1d2129; }
.page-desc { margin: 0; font-size: 13px; color: #86909c; }

/* ===== 标签页 ===== */
.main-tabs { border-radius: 8px; }
.tab-label { display: inline-flex; align-items: center; gap: 6px; }

/* ===== 渠道配置 ===== */
.channel-section { padding: 4px 0; }
.channel-group { margin-bottom: 24px; }
.channel-group:last-child { margin-bottom: 0; }

.group-header { display: flex; align-items: center; gap: 12px; padding: 14px 18px; border-radius: 10px; margin-bottom: 14px; }
.sms-header { background: linear-gradient(135deg, #f0f9eb 0%, #e1f3d8 100%); }
.email-header { background: linear-gradient(135deg, #fdf6ec 0%, #faecd8 100%); }
.other-header { background: linear-gradient(135deg, #f4f4f5 0%, #e9e9eb 100%); }

.group-icon { width: 40px; height: 40px; border-radius: 10px; display: flex; align-items: center; justify-content: center; flex-shrink: 0; }
.sms-icon { background: #67c23a; color: #fff; }
.email-icon { background: #e6a23c; color: #fff; }
.other-icon { background: #909399; color: #fff; }

.group-title-area { flex: 1; }
.group-title-area h3 { margin: 0; font-size: 15px; font-weight: 600; color: #1d2129; }
.group-count { font-size: 12px; color: #86909c; }
.group-summary { display: flex; gap: 6px; }

.channel-cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: 12px; }

.channel-card { border: 1px solid #e4e7ed; border-radius: 8px; padding: 14px 16px; transition: all .25s; background: #fff; }
.channel-card:hover { box-shadow: 0 4px 12px rgba(0,0,0,.08); transform: translateY(-1px); }
.channel-card.enabled { border-left: 3px solid #67c23a; }
.channel-card.disabled { border-left: 3px solid #dcdfe6; opacity: .7; }

.card-top { display: flex; align-items: center; justify-content: space-between; margin-bottom: 8px; }
.card-event-name { font-size: 14px; font-weight: 500; color: #1d2129; }
.card-bottom { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.card-event-type { font-size: 11px; font-family: 'Consolas','Monaco',monospace; color: #909399; background: #f5f7fa; padding: 1px 6px; border-radius: 3px; }
.card-note { font-size: 12px; color: #a8abb2; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 160px; }

/* ===== 模板管理 ===== */
.template-section { padding: 4px 0; }
.tpl-toolbar { display: flex; align-items: center; justify-content: space-between; margin-bottom: 16px; flex-wrap: wrap; gap: 10px; }
.tpl-toolbar-left { display: flex; align-items: center; gap: 12px; }

.tpl-name { font-weight: 500; color: #1d2129; }
.tpl-tag { font-size: 12px; font-family: 'Consolas','Monaco',monospace; color: #606266; background: #f0f2f5; padding: 2px 8px; border-radius: 4px; }
.tpl-content-preview { font-size: 12px; color: #606266; max-height: 42px; overflow: hidden; text-overflow: ellipsis; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; line-height: 1.6; }
.note-text { color: #86909c; font-size: 13px; }
.no-args { color: #c0c4cc; }

.args-tags { display: flex; flex-wrap: wrap; gap: 4px; }
.args-btn-group { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 4px; }
.arg-insert-btn { font-family: 'Consolas','Monaco',monospace; font-size: 12px; }
.arg-tag { font-family: 'Consolas','Monaco',monospace; font-size: 11px; }
.form-hint { margin-top: 6px; font-size: 12px; color: #a8abb2; line-height: 1.4; }
.form-hint code { background: #f5f7fa; padding: 1px 5px; border-radius: 3px; font-size: 11px; color: #606266; }

/* ===== 弹窗双栏布局 ===== */
.radio-with-icon { display: inline-flex; align-items: center; gap: 4px; }

.tpl-dialog-body {
  display: flex;
  gap: 20px;
  min-height: 380px;
}

.tpl-dialog-left {
  flex: 1;
  min-width: 0;
}

.tpl-dialog-right {
  width: 380px;
  flex-shrink: 0;
  border: 1px solid #e4e7ed;
  border-radius: 8px;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  background: #fafbfc;
}

.preview-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 10px 14px;
  background: #f5f7fa;
  border-bottom: 1px solid #e4e7ed;
}

.preview-title {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 13px;
  font-weight: 600;
  color: #303133;
}

.preview-body {
  flex: 1;
  overflow-y: auto;
  padding: 14px;
}

.preview-rendered {
  font-size: 13px;
  line-height: 1.7;
  color: #303133;
  word-break: break-all;
  white-space: pre-wrap;
  background: #fff;
  border: 1px solid #ebeef5;
  border-radius: 6px;
  padding: 12px;
  max-height: 320px;
  overflow-y: auto;
}

.preview-rendered.html-mode {
  white-space: normal;
  transform: scale(0.75);
  transform-origin: top left;
  width: 133.33%;
  max-height: 426px;
}

.preview-args {
  margin-top: 14px;
}

.preview-args-title {
  font-size: 12px;
  font-weight: 600;
  color: #606266;
  margin-bottom: 8px;
}

.preview-args-list {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.preview-arg-item {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 12px;
}

.preview-arg-key {
  background: #ecf5ff;
  color: #409eff;
  padding: 2px 8px;
  border-radius: 3px;
  font-family: 'Consolas','Monaco',monospace;
  flex-shrink: 0;
}

.preview-arg-val {
  color: #606266;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.preview-empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  height: 100%;
  min-height: 200px;
  color: #909399;
}

.preview-empty p {
  margin: 10px 0 0;
  font-size: 13px;
}
</style>
