<!--
  组件名称：DeviceList.vue
  组件描述：模拟设备列表公共组件，支持启停、手动上报、状态展示

  @author czj
  @date 2026-07-29
-->

<template>
  <div class="device-list">
    <!-- 筛选栏 -->
    <div class="filter-bar">
      <el-select v-model="filterEnvConfigId" placeholder="选择环境" clearable style="width: 200px" @change="loadDevices">
        <el-option v-for="env in envConfigs" :key="env.id" :label="env.name" :value="env.id" />
      </el-select>
      <el-select v-model="filterStatus" placeholder="状态" clearable style="width: 120px" @change="loadDevices">
        <el-option label="运行中" value="running" />
        <el-option label="已停止" value="stopped" />
        <el-option label="异常" value="error" />
      </el-select>
      <div style="flex: 1" />

      <!-- 批量操作 -->
      <template v-if="selectedIds.length > 0">
        <el-tag size="small" type="info" style="margin-right: 8px">已选 {{ selectedIds.length }} 个</el-tag>
        <el-button type="success" size="small" :loading="batchOperating" @click="handleBatchStart">批量启动</el-button>
        <el-button type="warning" size="small" :loading="batchOperating" @click="handleBatchStop">批量停止</el-button>
        <el-popconfirm title="确定删除选中的设备？" @confirm="handleBatchDelete">
          <template #reference>
            <el-button type="danger" size="small" :loading="batchOperating">批量删除</el-button>
          </template>
        </el-popconfirm>
      </template>

      <el-button type="primary" @click="openCreateDialog">
        <el-icon><Plus /></el-icon>新增设备
      </el-button>
      <el-button @click="openImportDialog">
        <el-icon><Upload /></el-icon>批量导入
      </el-button>
    </div>

    <!-- 设备列表 -->
    <el-table
      :data="devices"
      v-loading="loading"
      stripe
      style="width: 100%"
      @selection-change="handleSelectionChange"
    >
      <el-table-column type="selection" width="50" />
      <el-table-column prop="deviceName" label="设备名称" min-width="140" />
      <el-table-column prop="deviceId" label="设备 ID" min-width="160" />
      <el-table-column label="状态" width="100">
        <template #default="{ row }">
          <el-tag :type="row.status === 'running' ? 'success' : row.status === 'error' ? 'danger' : 'info'" size="small">
            {{ row.status === 'running' ? '运行中' : row.status === 'error' ? '异常' : '已停止' }}
          </el-tag>
        </template>
      </el-table-column>
      <el-table-column prop="totalReports" label="上报次数" width="90" align="center" />
      <el-table-column label="最后上报" width="170">
        <template #default="{ row }">
          {{ row.lastReportedAt ? formatTime(row.lastReportedAt) : '-' }}
        </template>
      </el-table-column>
      <el-table-column label="操作" width="300" fixed="right">
        <template #default="{ row }">
          <el-button
            :type="row.status === 'running' ? 'warning' : 'success'"
            size="small"
            :loading="operatingId === row.id"
            @click="toggleDevice(row)"
          >
            {{ row.status === 'running' ? '停止' : '启动' }}
          </el-button>
          <el-button
            link
            type="primary"
            size="small"
            @click="openEditDialog(row)"
          >
            编辑
          </el-button>
          <el-button
            link
            type="primary"
            size="small"
            :disabled="row.status !== 'running'"
            @click="openManualReport(row)"
          >
            手动上报
          </el-button>
          <el-popconfirm title="确定删除此设备？" @confirm="handleDelete(row)">
            <template #reference>
              <el-button link type="danger" size="small">删除</el-button>
            </template>
          </el-popconfirm>
        </template>
      </el-table-column>
    </el-table>

    <!-- 分页 -->
    <div class="pagination-wrapper" v-if="total > limit">
      <el-pagination
        v-model:current-page="currentPage"
        :page-size="limit"
        :total="total"
        layout="prev, pager, next, total"
        @change="loadDevices"
      />
    </div>

    <!-- 新增设备弹窗 -->
    <el-dialog v-model="createDialogVisible" title="新增模拟设备" width="600px">
      <el-form ref="createFormRef" :model="createForm" :rules="createRules" label-width="100px" label-position="top">
        <el-form-item label="关联环境" prop="envConfigId">
          <el-select v-model="createForm.envConfigId" placeholder="选择环境配置" style="width: 100%">
            <el-option v-for="env in envConfigs" :key="env.id" :label="env.name" :value="env.id" />
          </el-select>
        </el-form-item>
        <el-form-item label="设备名称" prop="deviceName">
          <el-input v-model="createForm.deviceName" :placeholder="`如：${deviceTypeLabel}设备1`" />
        </el-form-item>
        <el-form-item label="设备 ID" prop="deviceId">
          <el-input v-model="createForm.deviceId" :placeholder="`如：${deviceType}_001`" />
        </el-form-item>
        <!-- 门口屏授权码选择 -->
        <el-form-item v-if="hasRealTopics && deviceType === 'door' && appTokenOptions.length > 0" label="门口屏授权码">
          <el-select v-model="createForm.topicParams!.app_token" placeholder="选择授权码" style="width: 100%" size="small">
            <el-option v-for="token in appTokenOptions" :key="token" :label="token" :value="token" />
          </el-select>
        </el-form-item>
        <el-form-item label="自动重连">
          <el-switch v-model="createForm.autoReconnect" />
        </el-form-item>

        <!-- 上报配置区域 -->
        <el-divider content-position="left">上报配置</el-divider>
        <el-form-item label="启用定时上报">
          <el-switch v-model="createForm.reportConfig!.enabled" @change="onReportEnabledChange" />
        </el-form-item>
        <el-form-item label="上报间隔（秒）" v-if="createForm.reportConfig?.enabled">
          <el-input-number
            v-model="createForm.reportConfig!.interval"
            :min="1"
            :max="86400"
            :step="1"
            placeholder="请输入上报间隔"
            style="width: 200px"
          />
          <span class="form-hint" style="margin-left: 10px; color: #909399; font-size: 12px;">
            范围: 1秒 ~ 24小时（86400秒）
          </span>
        </el-form-item>

        <!-- 主题参数配置（仅门口屏/无纸化显示）- 已隐藏 -->
        <!-- <el-divider v-if="hasRealTopics" content-position="left">主题参数配置</el-divider>
        <div v-if="hasRealTopics && topicTemplates.length > 0" class="topic-params-section">
          <div v-for="tmpl in topicTemplates" :key="tmpl.key" class="topic-template-item">
            <div class="topic-template-header">
              <el-tag :type="tmpl.direction === 'publish' ? 'primary' : 'success'" size="small" class="direction-tag">
                {{ tmpl.direction === 'publish' ? '上报' : '订阅' }}
              </el-tag>
              <span class="topic-label">{{ tmpl.label }}</span>
              <span class="topic-template-path">{{ tmpl.template }}</span>
            </div>
            <div v-if="tmpl.params.length > 0" class="topic-param-inputs">
              <el-row :gutter="12">
                <el-col v-for="paramKey in tmpl.params" :key="paramKey" :span="12">
                  <el-form-item :label="tmpl.paramLabels[paramKey] || paramKey" class="compact-form-item">
                    <el-input
                      v-if="paramKey === 'company_id'"
                      v-model="(createForm.topicParams as any)[paramKey]"
                      size="small"
                      disabled
                    />
                    <el-select
                      v-else-if="paramKey === 'room_id' && defaultTopicParams?.room_ids?.length"
                      v-model="(createForm.topicParams as any)[paramKey]"
                      size="small"
                      style="width: 100%"
                      placeholder="选择会议室"
                    >
                      <el-option v-for="room in defaultTopicParams.room_ids" :key="room" :label="room" :value="room" />
                    </el-select>
                    <el-input
                      v-else
                      v-model="(createForm.topicParams as any)[paramKey]"
                      :placeholder="tmpl.paramDefaults[paramKey] || `请输入${tmpl.paramLabels[paramKey] || paramKey}`"
                      size="small"
                    />
                  </el-form-item>
                </el-col>
              </el-row>
            </div>
          </div>
        </div> -->
      </el-form>
      <template #footer>
        <el-button @click="createDialogVisible = false">取消</el-button>
        <el-button type="primary" :loading="creating" @click="handleCreate">创建</el-button>
      </template>
    </el-dialog>

    <!-- 手动上报弹窗 -->
    <el-dialog v-model="reportDialogVisible" title="手动上报" width="500px">
      <el-form ref="reportFormRef" :model="reportForm" label-width="100px" label-position="top">
        <el-form-item label="上报主题" prop="topic">
          <el-input v-model="reportForm.topic" placeholder="如：event、status、heartbeat" />
        </el-form-item>
        <el-form-item label="上报内容" prop="payload">
          <el-input
            v-model="reportForm.payloadStr"
            type="textarea"
            :rows="6"
            placeholder='如：{"status": "online", "battery": 85}'
          />
        </el-form-item>
      </el-form>
      <template #footer>
        <el-button @click="reportDialogVisible = false">取消</el-button>
        <el-button type="primary" :loading="reporting" @click="handleManualReport">发送</el-button>
      </template>
    </el-dialog>

    <!-- 编辑设备弹窗 -->
    <el-dialog v-model="editDialogVisible" title="编辑设备" width="500px">
      <el-form ref="editFormRef" :model="editForm" label-width="100px" label-position="top">
        <el-form-item label="设备名称">
          <el-input v-model="editForm.deviceName" />
        </el-form-item>
        <el-form-item label="自动重连">
          <el-switch v-model="editForm.autoReconnect" />
        </el-form-item>
      </el-form>
      <template #footer>
        <el-button @click="editDialogVisible = false">取消</el-button>
        <el-button type="primary" :loading="editing" @click="handleEdit">保存</el-button>
      </template>
    </el-dialog>

    <!-- 批量导入弹窗 -->
    <el-dialog v-model="importDialogVisible" title="批量导入设备" width="700px" destroy-on-close>
      <div class="import-content">
        <el-alert type="info" :closable="false" style="margin-bottom: 16px">
          <template #title>
            上传 Excel 文件批量创建{{ deviceTypeLabel }}设备。
            <el-link type="primary" :underline="false" @click="handleDownloadTemplate" :loading="downloadingTemplate">
              下载模板文件
            </el-link>
          </template>
        </el-alert>

        <el-upload
          ref="importUploadRef"
          :auto-upload="false"
          accept=".xlsx"
          :limit="1"
          :on-change="handleImportFileChange"
          :on-remove="() => { importFile = null; importFileList = [] }"
          :file-list="importFileList"
          drag
        >
          <el-icon style="font-size: 40px; color: #c0c4cc"><Upload /></el-icon>
          <div style="margin-top: 8px">将 .xlsx 文件拖到此处，或 <em>点击上传</em></div>
        </el-upload>

        <!-- 导入结果 -->
        <div v-if="importResult" style="margin-top: 16px">
          <el-divider content-position="left">导入结果</el-divider>
          <el-alert
            :type="importResult.failedCount > 0 ? 'warning' : 'success'"
            :closable="false"
            style="margin-bottom: 12px"
          >
            <template #title>
              {{ importResult.message }}
            </template>
          </el-alert>
          <el-table v-if="importResult.results.length > 0" :data="importResult.results" max-height="300" size="small">
            <el-table-column prop="row" label="行号" width="60" />
            <el-table-column prop="deviceName" label="设备名称" min-width="120" />
            <el-table-column prop="deviceId" label="设备ID" min-width="120" />
            <el-table-column label="结果" width="80">
              <template #default="{ row }">
                <el-tag :type="row.success ? 'success' : 'danger'" size="small">
                  {{ row.success ? '成功' : '失败' }}
                </el-tag>
              </template>
            </el-table-column>
            <el-table-column prop="error" label="错误信息" min-width="200">
              <template #default="{ row }">
                <span v-if="row.error" style="color: #f56c6c">{{ row.error }}</span>
                <span v-else>-</span>
              </template>
            </el-table-column>
          </el-table>
        </div>
      </div>
      <template #footer>
        <el-button @click="importDialogVisible = false">关闭</el-button>
        <el-button
          type="primary"
          :loading="importing"
          :disabled="!importFile"
          @click="handleImport"
        >
          开始导入
        </el-button>
      </template>
    </el-dialog>
  </div>
</template>

<script setup lang="ts">
import { ref, onMounted, computed } from 'vue'
import { ElMessage } from 'element-plus'
import { Plus, Upload } from '@element-plus/icons-vue'
import {
  listSimulators,
  createSimulator,
  updateSimulator,
  deleteSimulator,
  startSimulator,
  stopSimulator,
  manualReport,
  listEnvConfigs,
  getTopicTemplates,
  batchStartDevices,
  batchStopDevices,
  batchDeleteDevices,
  importDevices,
  downloadImportTemplate,
} from '@/api/deviceSim'
import type { Simulator, SimulatorCreate, EnvConfig, DeviceType, TopicTemplate, DeviceImportResponse } from '@/types/device'
import { DEVICE_TYPE_LABELS } from '@/types/device'

const props = defineProps<{
  deviceType: DeviceType
}>()

const emit = defineEmits<{
  (e: 'device-changed'): void
}>()

const deviceTypeLabel = computed(() => DEVICE_TYPE_LABELS[props.deviceType])

// 设备列表
const loading = ref(false)
const devices = ref<Simulator[]>([])
const total = ref(0)
const currentPage = ref(1)
const limit = 20
const operatingId = ref('')

// 批量操作
const selectedIds = ref<string[]>([])
const batchOperating = ref(false)

// 筛选
const envConfigs = ref<EnvConfig[]>([])
const filterEnvConfigId = ref('')
const filterStatus = ref('')

// 新增弹窗
const createDialogVisible = ref(false)
const creating = ref(false)
const createFormRef = ref()
const createForm = ref<SimulatorCreate>({
  envConfigId: '',
  deviceType: props.deviceType,
  deviceName: '',
  deviceId: '',
  autoReconnect: true,
  topicParams: {},
  reportConfig: {
    interval: 30,
    enabled: true,
  },
})
const createRules = {
  envConfigId: [{ required: true, message: '请选择环境配置', trigger: 'change' }],
  deviceName: [{ required: true, message: '请输入设备名称', trigger: 'blur' }],
  deviceId: [{ required: true, message: '请输入设备 ID', trigger: 'blur' }],
}

// 主题模板
const topicTemplates = ref<TopicTemplate[]>([])
const hasRealTopics = ref(false)
const loadingTemplates = ref(false)

// 环境配置的默认主题参数
const defaultTopicParams = ref<{
  company_id?: string
  room_ids?: string[]
  app_token_prefix?: string
  app_token_start?: number
  app_token_end?: number
  conference_id?: string
  conference_name?: string
} | null>(null)

// 授权码选项列表
const appTokenOptions = computed(() => {
  if (!defaultTopicParams.value?.app_token_prefix) return []
  const prefix = defaultTopicParams.value.app_token_prefix
  const start = defaultTopicParams.value.app_token_start ?? 1
  const end = defaultTopicParams.value.app_token_end ?? 10
  const options: string[] = []
  for (let i = start; i <= end; i++) {
    options.push(`${prefix}-${String(i).padStart(4, '0')}`)
  }
  return options
})

// 手动上报弹窗
const reportDialogVisible = ref(false)
const reporting = ref(false)
const reportFormRef = ref()
const reportForm = ref({ topic: '', payloadStr: '{}', deviceId: '' })

// 编辑设备弹窗
const editDialogVisible = ref(false)
const editing = ref(false)
const editFormRef = ref()
const editForm = ref({
  id: '',
  deviceName: '',
  autoReconnect: true,
  topicParams: {} as Record<string, string>,
})

// 批量导入
const importDialogVisible = ref(false)
const importing = ref(false)
const importFile = ref<File | null>(null)
const importFileList = ref<any[]>([])
const importResult = ref<DeviceImportResponse | null>(null)
const downloadingTemplate = ref(false)

/** 加载设备列表 */
async function loadDevices() {
  loading.value = true
  try {
    const params: any = {
      skip: (currentPage.value - 1) * limit,
      limit,
      deviceType: props.deviceType,
    }
    if (filterEnvConfigId.value) params.envConfigId = filterEnvConfigId.value
    if (filterStatus.value) params.status = filterStatus.value

    const res = await listSimulators(params)
    devices.value = res.items
    total.value = res.total
  } catch (e: any) {
    ElMessage.error('加载设备列表失败: ' + (e.message || ''))
  } finally {
    loading.value = false
  }
}

/** 加载环境配置列表 */
async function loadEnvConfigs() {
  try {
    const res = await listEnvConfigs({ limit: 100 })
    envConfigs.value = res.items
  } catch (_) { /* ignore */ }
}

/** 加载主题模板 */
async function loadTopicTemplates() {
  if (!props.deviceType) return
  loadingTemplates.value = true
  try {
    const res = await getTopicTemplates(props.deviceType)
    topicTemplates.value = res.templates
    hasRealTopics.value = res.hasRealTopics
  } catch (_) {
    topicTemplates.value = []
    hasRealTopics.value = false
  } finally {
    loadingTemplates.value = false
  }
}

/** 打开新增弹窗 */
async function openCreateDialog() {
  createForm.value = {
    envConfigId: filterEnvConfigId.value || '',
    deviceType: props.deviceType,
    deviceName: '',
    deviceId: '',
    autoReconnect: true,
    topicParams: {},
    reportConfig: {
      interval: 30,
      enabled: true,
    },
  }
  defaultTopicParams.value = null
  createDialogVisible.value = true

  // 加载主题模板
  await loadTopicTemplates()

  // 如果有预选环境，加载其默认参数
  if (createForm.value.envConfigId) {
    await loadDefaultTopicParams(createForm.value.envConfigId)
  }
}

/** 加载环境配置的默认主题参数 */
async function loadDefaultTopicParams(envConfigId: string) {
  const env = envConfigs.value.find(e => e.id === envConfigId)
  if (env?.defaultTopicParams) {
    defaultTopicParams.value = env.defaultTopicParams

    // 预填默认参数值
    if (hasRealTopics.value) {
      const defaults: Record<string, string> = {}
      // 使用环境配置的 company_id
      if (env.defaultTopicParams.company_id) {
        defaults.company_id = env.defaultTopicParams.company_id
      }
      // 使用环境配置的第一个 room_id
      if (env.defaultTopicParams.room_ids?.length) {
        defaults.room_id = env.defaultTopicParams.room_ids[0]
      }
      // 其他参数使用模板默认值
      for (const tmpl of topicTemplates.value) {
        for (const paramKey of tmpl.params) {
          if (!defaults[paramKey] && tmpl.paramDefaults[paramKey]) {
            defaults[paramKey] = tmpl.paramDefaults[paramKey]
          }
        }
      }
      createForm.value.topicParams = defaults
    }
  }
}

/** 上报启用状态变化 */
function onReportEnabledChange(enabled: boolean) {
  if (enabled && !createForm.value.reportConfig?.interval) {
    // 启用时确保 interval 有默认值
    createForm.value.reportConfig = {
      ...createForm.value.reportConfig,
      interval: 30,
      enabled: true,
    }
  }
}

/** 创建设备 */
async function handleCreate() {
  const valid = await createFormRef.value?.validate().catch(() => false)
  if (!valid) return

  creating.value = true
  try {
    await createSimulator(createForm.value)
    ElMessage.success('模拟设备已创建')
    createDialogVisible.value = false
    await loadDevices()
    emit('device-changed')
  } catch (e: any) {
    ElMessage.error('创建失败: ' + (e.message || ''))
  } finally {
    creating.value = false
  }
}

/** 切换设备启停 */
async function toggleDevice(row: Simulator) {
  operatingId.value = row.id
  try {
    if (row.status === 'running') {
      await stopSimulator(row.id)
      ElMessage.success('设备已停止')
    } else {
      await startSimulator(row.id)
      ElMessage.success('设备已启动')
    }
    await loadDevices()
    emit('device-changed')
  } catch (e: any) {
    ElMessage.error('操作失败: ' + (e.message || ''))
  } finally {
    operatingId.value = ''
  }
}

/** 删除设备 */
async function handleDelete(row: Simulator) {
  try {
    await deleteSimulator(row.id)
    ElMessage.success('设备已删除')
    await loadDevices()
    emit('device-changed')
  } catch (e: any) {
    ElMessage.error('删除失败: ' + (e.message || ''))
  }
}

/** 打开编辑弹窗 */
function openEditDialog(row: Simulator) {
  editForm.value = {
    id: row.id,
    deviceName: row.deviceName,
    autoReconnect: row.autoReconnect,
    topicParams: {},
  }
  editDialogVisible.value = true
}

/** 保存编辑 */
async function handleEdit() {
  editing.value = true
  try {
    await updateSimulator(editForm.value.id, {
      deviceName: editForm.value.deviceName,
      autoReconnect: editForm.value.autoReconnect,
    })
    ElMessage.success('设备已更新')
    editDialogVisible.value = false
    await loadDevices()
    emit('device-changed')
  } catch (e: any) {
    ElMessage.error('更新失败: ' + (e.message || ''))
  } finally {
    editing.value = false
  }
}

/** 打开手动上报弹窗 */
function openManualReport(row: Simulator) {
  reportForm.value = { topic: 'status', payloadStr: '{}', deviceId: row.id }
  reportDialogVisible.value = true
}

/** 手动上报 */
async function handleManualReport() {
  let payload: any = {}
  try {
    payload = JSON.parse(reportForm.value.payloadStr)
  } catch (_) {
    ElMessage.warning('JSON 格式错误')
    return
  }

  reporting.value = true
  try {
    await manualReport(reportForm.value.deviceId, {
      topic: reportForm.value.topic,
      payload,
    })
    ElMessage.success('上报成功')
    reportDialogVisible.value = false
    await loadDevices()
  } catch (e: any) {
    ElMessage.error('上报失败: ' + (e.message || ''))
  } finally {
    reporting.value = false
  }
}

/** 格式化时间 */
function formatTime(t: string) {
  return t?.replace('T', ' ').substring(0, 19) || '-'
}

/** 表格选择变化 */
function handleSelectionChange(rows: Simulator[]) {
  selectedIds.value = rows.map(r => r.id)
}

/** 批量启动 */
async function handleBatchStart() {
  batchOperating.value = true
  try {
    const res = await batchStartDevices(selectedIds.value)
    ElMessage.success(res.message)
    selectedIds.value = []
    await loadDevices()
    emit('device-changed')
  } catch (e: any) {
    ElMessage.error('批量启动失败: ' + (e.message || ''))
  } finally {
    batchOperating.value = false
  }
}

/** 批量停止 */
async function handleBatchStop() {
  batchOperating.value = true
  try {
    const res = await batchStopDevices(selectedIds.value)
    ElMessage.success(res.message)
    selectedIds.value = []
    await loadDevices()
    emit('device-changed')
  } catch (e: any) {
    ElMessage.error('批量停止失败: ' + (e.message || ''))
  } finally {
    batchOperating.value = false
  }
}

/** 批量删除 */
async function handleBatchDelete() {
  batchOperating.value = true
  try {
    const res = await batchDeleteDevices(selectedIds.value)
    ElMessage.success(res.message)
    selectedIds.value = []
    await loadDevices()
    emit('device-changed')
  } catch (e: any) {
    ElMessage.error('批量删除失败: ' + (e.message || ''))
  } finally {
    batchOperating.value = false
  }
}

/** 打开导入弹窗 */
function openImportDialog() {
  importFile.value = null
  importFileList.value = []
  importResult.value = null
  importDialogVisible.value = true
}

/** 导入文件选择变化 */
function handleImportFileChange(file: any) {
  importFile.value = file.raw
  importFileList.value = [file]
}

/** 下载模板 */
async function handleDownloadTemplate() {
  downloadingTemplate.value = true
  try {
    const blob = await downloadImportTemplate(props.deviceType)
    const url = window.URL.createObjectURL(blob)
    const a = document.createElement('a')
    a.href = url
    a.download = `${deviceTypeLabel.value}_设备导入模板.xlsx`
    a.click()
    window.URL.revokeObjectURL(url)
  } catch (e: any) {
    ElMessage.error('下载模板失败: ' + (e.message || ''))
  } finally {
    downloadingTemplate.value = false
  }
}

/** 执行导入 */
async function handleImport() {
  if (!importFile.value) {
    ElMessage.warning('请先选择文件')
    return
  }

  importing.value = true
  try {
    const result = await importDevices(importFile.value, props.deviceType)
    importResult.value = result
    if (result.successCount > 0) {
      await loadDevices()
      emit('device-changed')
    }
  } catch (e: any) {
    ElMessage.error('导入失败: ' + (e.message || ''))
  } finally {
    importing.value = false
  }
}

onMounted(() => {
  loadEnvConfigs()
  loadDevices()
})
</script>

<style scoped>
.device-list {
  width: 100%;
}

.filter-bar {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-bottom: 16px;
}

.pagination-wrapper {
  display: flex;
  justify-content: flex-end;
  margin-top: 16px;
}

.topic-params-section {
  max-height: 300px;
  overflow-y: auto;
  border: 1px solid #ebeef5;
  border-radius: 4px;
  padding: 12px;
  margin-bottom: 12px;
}

.topic-template-item {
  margin-bottom: 16px;
  padding-bottom: 12px;
  border-bottom: 1px solid #f0f0f0;
}

.topic-template-item:last-child {
  margin-bottom: 0;
  padding-bottom: 0;
  border-bottom: none;
}

.topic-template-header {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 8px;
}

.direction-tag {
  flex-shrink: 0;
}

.topic-label {
  font-weight: 500;
  color: #303133;
}

.topic-template-path {
  font-family: 'Courier New', monospace;
  font-size: 12px;
  color: #909399;
  margin-left: auto;
}

.topic-param-inputs {
  padding-left: 8px;
}

.compact-form-item {
  margin-bottom: 8px;
}

.compact-form-item :deep(.el-form-item__label) {
  font-size: 13px;
  padding-bottom: 2px;
}
</style>