<!--
  页面名称：Reports.vue
  页面描述：报告中心页面，展示已完成执行记录并支持生成/导出报告
             支持 UI 自动化报告（HTML iframe）和安全测试报告（Markdown 渲染）

  @author czj
  @date 2026-07-09
  @last-modified 2026-08-17
-->

<template>
  <div class="reports-page">
    <!-- 页面头部 -->
    <div class="page-header">
      <h2>报告中心</h2>
      <el-button @click="loadReports">
        <el-icon><Refresh /></el-icon>
        刷新
      </el-button>
    </div>

    <!-- 报告列表 -->
    <el-card class="reports-list" shadow="never">
      <el-table
        :data="reports"
        stripe
        style="width: 100%"
        v-loading="loading"
      >
        <el-table-column prop="name" label="执行名称" min-width="200" />
        <el-table-column label="报告类型" width="120">
          <template #default="{ row }">
            <el-tag :type="reportTypeTag(row.case_type)" size="small">
              {{ reportTypeLabel(row.case_type) }}
            </el-tag>
          </template>
        </el-table-column>
        <el-table-column label="触发方式" width="90">
          <template #default="{ row }">
            <el-tag
              :type="row.trigger_type === 'scheduled' ? 'warning' : 'info'"
              size="small"
            >
              {{ row.trigger_type === 'scheduled' ? '定时' : '手动' }}
            </el-tag>
          </template>
        </el-table-column>
        <el-table-column label="通过率" width="120">
          <template #default="{ row }">
            <div class="pass-rate-cell">
              <span :class="getPassRateClass(row.pass_rate)">
                {{ row.pass_rate }}%
              </span>
              <el-progress
                :percentage="row.pass_rate"
                :stroke-width="8"
                :color="getProgressColor(row.pass_rate)"
                :show-text="false"
                style="width: 60px"
              />
            </div>
          </template>
        </el-table-column>
        <el-table-column label="通过/失败/总数" width="180">
          <template #default="{ row }">
            <div class="count-cell">
              <span class="count-passed">{{ row.passed }}</span>
              <span class="count-sep">/</span>
              <span class="count-failed">{{ row.failed }}</span>
              <span class="count-sep">/</span>
              <span class="count-total">{{ row.total_cases }}</span>
            </div>
          </template>
        </el-table-column>
        <el-table-column label="耗时" width="100">
          <template #default="{ row }">
            {{ row.duration?.toFixed(1) || 0 }}s
          </template>
        </el-table-column>
        <el-table-column label="执行时间" width="180">
          <template #default="{ row }">
            {{ formatTime(row.created_at) }}
          </template>
        </el-table-column>
        <el-table-column label="操作" width="200" fixed="right">
          <template #default="{ row }">
            <el-button size="small" type="primary" link @click="viewReport(row)">
              <el-icon><View /></el-icon>
              查看报告
            </el-button>
            <el-button size="small" type="primary" link @click="downloadReport(row)">
              <el-icon><Files /></el-icon>
              下载
            </el-button>
          </template>
        </el-table-column>
      </el-table>

      <div class="pagination">
        <el-pagination
          v-model:current-page="currentPage"
          v-model:page-size="pageSize"
          :total="total"
          layout="total, prev, pager, next"
          @current-change="loadReports"
        />
      </div>
    </el-card>

    <!-- UI 自动化报告预览弹窗 -->
    <el-dialog
      v-model="previewDialogVisible"
      title="UI 自动化测试报告"
      width="90%"
      top="5vh"
      destroy-on-close
      fullscreen
    >
      <div class="report-preview-container">
        <iframe
          v-if="previewUrl"
          :src="previewUrl"
          class="report-iframe"
          sandbox="allow-same-origin allow-scripts"
        />
        <el-empty v-else description="报告加载失败" />
      </div>
    </el-dialog>

    <!-- 安全测试报告预览弹窗 -->
    <el-dialog
      v-model="securityPreviewVisible"
      title="安全测试报告"
      width="90%"
      top="5vh"
      destroy-on-close
      fullscreen
    >
      <template #header>
        <div class="security-report-header">
          <span>安全测试报告</span>
          <div class="security-report-actions">
            <el-button type="warning" size="small" :disabled="!securityReportContent" @click="openErpDialog">
              <el-icon><Upload /></el-icon>
              上传到 ERP
            </el-button>
            <el-button type="danger" size="small" :disabled="!securityReportContent" @click="openErpTaskDialog">
              <el-icon><List /></el-icon>
              创建ERP任务
            </el-button>
            <el-button type="primary" size="small" @click="downloadSecurityReport">
              <el-icon><Download /></el-icon>
              下载报告
            </el-button>
          </div>
        </div>
      </template>
      <div class="security-report-container" v-loading="loadingSecurity">
        <div
          v-if="securityReportContent"
          class="markdown-body"
          v-html="renderedMarkdown"
        />
        <el-empty v-else-if="!loadingSecurity" description="报告加载失败" />
      </div>
    </el-dialog>

    <!-- 安全测试报告 ERP 上传对话框 -->
    <el-dialog
      v-model="erpDialogVisible"
      title="上传安全测试报告到 ERP"
      width="520px"
      :close-on-click-modal="false"
      append-to-body
    >
      <el-form :model="erpForm" label-width="100px" v-loading="uploadingErp">
        <el-form-item label="上传类型" required>
          <el-radio-group v-model="erpForm.target">
            <el-radio value="erp">ERP测试单</el-radio>
            <el-radio value="project">项目资料</el-radio>
            <el-radio value="cooperation">协作文档</el-radio>
          </el-radio-group>
        </el-form-item>

        <el-form-item v-if="erpForm.target === 'erp'" label="测试单 ID">
          <el-input-number v-model="erpForm.developtestingId" :min="0" :controls="false" style="width: 100%" placeholder="留空使用系统默认配置" />
        </el-form-item>
        <el-form-item v-if="erpForm.target === 'erp'" label="抄送人">
          <el-input v-model="erpForm.copyuserNamesText" placeholder="多个姓名用逗号分隔（可选）" clearable />
        </el-form-item>
        <el-form-item v-if="erpForm.target === 'erp'" label="创建人">
          <el-input v-model="erpForm.createuserName" placeholder="姓名（可选）" clearable />
        </el-form-item>

        <el-form-item v-if="erpForm.target !== 'erp'" label="项目 ID" required>
          <el-input-number v-model="erpForm.projectId" :min="1" :controls="false" style="width: 100%" placeholder="请输入项目 ID" />
        </el-form-item>
        <el-form-item v-if="erpForm.target !== 'erp'" label="文件名称">
          <el-input v-model="erpForm.name" placeholder="默认取报告文件名" clearable />
        </el-form-item>
        <el-form-item v-if="erpForm.target === 'cooperation'" label="分组 ID">
          <el-input-number v-model="erpForm.groupId" :min="1" :controls="false" style="width: 100%" placeholder="可选" />
        </el-form-item>
      </el-form>
      <template #footer>
        <el-button @click="erpDialogVisible = false">取消</el-button>
        <el-button type="primary" :loading="uploadingErp" @click="handleUploadErp">确认上传</el-button>
      </template>
    </el-dialog>

    <!-- 安全测试 ERP 任务创建对话框 -->
    <el-dialog
      v-model="erpTaskDialogVisible"
      title="创建安全测试 ERP 任务"
      width="720px"
      :close-on-click-modal="false"
      append-to-body
    >
      <!-- 报告摘要（只读） -->
      <el-collapse v-if="taskPreview" style="margin-bottom: 16px;">
        <el-collapse-item title="报告摘要（只读）" name="summary">
          <el-descriptions :column="2" border size="small">
            <el-descriptions-item label="测试目标">{{ taskPreview.summary?.target || '-' }}</el-descriptions-item>
            <el-descriptions-item label="服务器 IP">{{ taskPreview.summary?.server_ip || '-' }}</el-descriptions-item>
            <el-descriptions-item label="测试时间">{{ taskPreview.summary?.start_time || '-' }} ~ {{ taskPreview.summary?.end_time || '-' }}</el-descriptions-item>
            <el-descriptions-item label="用例总数">{{ taskPreview.summary?.total || 0 }}</el-descriptions-item>
            <el-descriptions-item label="高危漏洞"><span style="color: #f56c6c; font-weight: bold;">{{ taskPreview.summary?.high || 0 }}</span></el-descriptions-item>
            <el-descriptions-item label="中危漏洞"><span style="color: #e6a23c; font-weight: bold;">{{ taskPreview.summary?.medium || 0 }}</span></el-descriptions-item>
            <el-descriptions-item label="低危漏洞"><span style="color: #ffd700; font-weight: bold;">{{ taskPreview.summary?.low || 0 }}</span></el-descriptions-item>
            <el-descriptions-item label="信息类"><span style="color: #409eff; font-weight: bold;">{{ taskPreview.summary?.info || 0 }}</span></el-descriptions-item>
          </el-descriptions>
        </el-collapse-item>
      </el-collapse>

      <!-- 任务配置表单 -->
      <el-form :model="taskForm" label-width="100px" v-loading="taskLoading">
        <el-row :gutter="16">
          <el-col :span="24">
            <el-form-item label="任务名称">
              <el-input v-model="taskForm.task_name" placeholder="自动生成，可修改" clearable />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="16">
          <el-col :span="12">
            <el-form-item label="任务类型">
              <el-select v-model="taskForm.type_id" placeholder="选择类型" style="width: 100%">
                <el-option
                  v-for="t in taskPreview?.task_types || []"
                  :key="t.id"
                  :label="t.name"
                  :value="t.id"
                />
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="任务状态">
              <el-select v-model="taskForm.status_id" placeholder="选择状态" style="width: 100%">
                <el-option
                  v-for="s in taskPreview?.task_statuses || []"
                  :key="s.id"
                  :label="s.name"
                  :value="s.id"
                />
              </el-select>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="16">
          <el-col :span="12">
            <el-form-item label="责任人">
              <el-select v-model="taskForm.user_id" placeholder="选择责任人" filterable style="width: 100%">
                <el-option
                  v-for="u in taskPreview?.staff_list || []"
                  :key="u.id"
                  :label="u.name"
                  :value="u.id"
                />
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="创建人">
              <el-select v-model="taskForm.createuser_id" placeholder="选择创建人" filterable style="width: 100%">
                <el-option
                  v-for="u in taskPreview?.staff_list || []"
                  :key="u.id"
                  :label="u.name"
                  :value="u.id"
                />
              </el-select>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="16">
          <el-col :span="8">
            <el-form-item label="紧急程度">
              <el-select v-model="taskForm.level" placeholder="紧急程度" style="width: 100%">
                <el-option :label="'1 - 低优先级'" :value="1" />
                <el-option :label="'2 - 正常优先级'" :value="2" />
                <el-option :label="'3 - 中优先级'" :value="3" />
                <el-option :label="'4 - 高优先级'" :value="4" />
                <el-option :label="'5 - 最高优先级'" :value="5" />
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="8">
            <el-form-item label="期望工时(h)">
              <el-input-number v-model="taskForm.excepthour" :min="1" :max="999" :controls="false" style="width: 100%" />
            </el-form-item>
          </el-col>
          <el-col :span="8">
            <el-form-item label="截止天数">
              <el-input-number v-model="taskForm.deadline_days" :min="1" :max="365" :controls="false" style="width: 100%" />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="16">
          <el-col :span="24">
            <el-form-item label="关联需求">
              <el-input
                :model-value="taskPreview?.requirement ? `${taskPreview.requirement.name} (ID: ${taskPreview.requirement.id})` : '未找到关联需求'"
                readonly
                class="requirement-input"
              />
            </el-form-item>
          </el-col>
        </el-row>
      </el-form>
      <template #footer>
        <el-button @click="erpTaskDialogVisible = false">取消</el-button>
        <el-button type="primary" :loading="taskLoading" @click="handleCreateTask">确认创建</el-button>
      </template>
    </el-dialog>
  </div>
</template>

<script setup lang="ts">
/**
 * 报告中心页面
 *
 * 功能：
 * 1. 展示已完成执行记录列表（支持 UI 自动化和安全测试）
 * 2. 预览 UI 自动化报告（HTML iframe）
 * 3. 预览安全测试报告（Markdown 渲染）
 * 4. 下载报告文件
 */

import { ref, computed, reactive, watch, onMounted } from 'vue'
import { useRoute } from 'vue-router'
import { ElMessage } from 'element-plus'
import { Refresh, View, Download, Files, Upload, List } from '@element-plus/icons-vue'
import { marked } from 'marked'
import { reportApi } from '@/api/reports'
import {
  getSecurityReport,
  getSecurityReportDownloadUrl,
  uploadSecurityReportToErp,
  uploadSecurityReportToProject,
  uploadSecurityReportToCooperation,
  getSecurityTaskPreview,
  createSecurityTask,
} from '@/api/security'

const route = useRoute()

// ==================== 响应式数据 ====================

const loading = ref(false)
const reports = ref<any[]>([])
const total = ref(0)
const currentPage = ref(1)
const pageSize = ref(20)

// UI 自动化报告预览
const previewDialogVisible = ref(false)
const previewUrl = ref('')

// 安全测试报告预览
const securityPreviewVisible = ref(false)
const securityReportContent = ref('')
const loadingSecurity = ref(false)
const currentSecurityExecutionId = ref('')

// 安全测试报告 ERP 上传
const erpDialogVisible = ref(false)
const uploadingErp = ref(false)
const erpForm = reactive({
  target: 'erp',
  developtestingId: undefined as number | undefined,
  copyuserNamesText: '',
  createuserName: '',
  projectId: undefined as number | undefined,
  name: '',
  groupId: undefined as number | undefined,
})

// 安全测试 ERP 任务创建
const erpTaskDialogVisible = ref(false)
const taskLoading = ref(false)
const taskPreview = ref<any>(null)
const taskForm = reactive({
  task_name: '',
  type_id: 3,
  status_id: 1,
  level: 4,
  excepthour: 21,
  user_id: 101,
  createuser_id: 441,
  deadline_days: 7,
  develop_id: undefined as number | undefined,
})

/** 当前用例类型（来自路由参数，默认 ui；接口测试模块固定为 api） */
const currentType = computed(() => {
  if (route.path.startsWith('/api-test')) return 'api'
  return (route.params.type as string) || 'ui'
})

/** 报告类型标签映射 */
const REPORT_TYPE_LABELS: Record<string, string> = {
  ui: 'UI自动化测试',
  api: '接口自动化测试',
  security: '安全测试',
  functional: '功能测试报告',
  deploy: '部署测试',
  performance: '性能测试',
}
const REPORT_TYPE_TAGS: Record<string, string> = {
  ui: 'primary',
  api: 'success',
  security: 'danger',
  functional: 'warning',
  deploy: 'info',
  performance: 'warning',
}
const reportTypeLabel = (type: string) => REPORT_TYPE_LABELS[type] || type
const reportTypeTag = (type: string) => REPORT_TYPE_TAGS[type] || 'info'

/** 渲染后的 Markdown */
const renderedMarkdown = computed(() => {
  if (!securityReportContent.value) return ''
  return marked.parse(securityReportContent.value) as string
})

// ==================== 方法定义 ====================

const loadReports = async () => {
  loading.value = true
  try {
    const response = await reportApi.list({
      skip: (currentPage.value - 1) * pageSize.value,
      limit: pageSize.value,
      case_type: currentType.value,
    })
    reports.value = response.items
    total.value = response.total
  } catch (error: any) {
    ElMessage.error('加载报告列表失败: ' + error.message)
  } finally {
    loading.value = false
  }
}

const getPassRateClass = (rate: number) => {
  if (rate >= 90) return 'rate-success'
  if (rate >= 70) return 'rate-warning'
  return 'rate-danger'
}

const getProgressColor = (rate: number) => {
  if (rate >= 90) return '#67c23a'
  if (rate >= 70) return '#e6a23c'
  return '#f56c6c'
}

const formatTime = (time: string) => {
  if (!time) return '-'
  return new Date(time).toLocaleString('zh-CN', {
    year: 'numeric',
    month: '2-digit',
    day: '2-digit',
    hour: '2-digit',
    minute: '2-digit',
  })
}

/**
 * 查看报告
 * - UI 自动化报告：iframe 预览 HTML
 * - 安全测试报告：Markdown 渲染
 */
const viewReport = async (row: any) => {
  if (row.case_type === 'security') {
    // 安全测试报告 - Markdown 渲染
    currentSecurityExecutionId.value = row.id
    securityReportContent.value = ''
    securityPreviewVisible.value = true
    loadingSecurity.value = true

    try {
      const response = await getSecurityReport(row.id)
      securityReportContent.value = response.content
    } catch (error: any) {
      ElMessage.error('加载安全测试报告失败: ' + error.message)
      securityReportContent.value = ''
    } finally {
      loadingSecurity.value = false
    }
  } else {
    // UI 自动化报告 - iframe 预览
    previewUrl.value = reportApi.generateUrl(row.id)
    previewDialogVisible.value = true
  }
}

/**
 * 下载报告
 * - UI 自动化报告：下载 HTML 文件
 * - 安全测试报告：下载 Markdown 文件
 */
const downloadReport = (row: any) => {
  if (row.case_type === 'security') {
    // 安全测试报告 - 下载 Markdown
    const url = getSecurityReportDownloadUrl(row.id)
    window.open(url, '_blank')
  } else {
    // UI 自动化报告 - 下载 HTML
    const url = reportApi.downloadUrl(row.id)
    window.open(url, '_blank')
  }
}

/**
 * 下载安全测试报告（弹窗内按钮）
 */
const downloadSecurityReport = () => {
  if (currentSecurityExecutionId.value) {
    const url = getSecurityReportDownloadUrl(currentSecurityExecutionId.value)
    window.open(url, '_blank')
  }
}

/**
 * 打开 ERP 上传对话框
 */
const openErpDialog = () => {
  erpForm.target = 'erp'
  erpForm.developtestingId = undefined
  erpForm.copyuserNamesText = ''
  erpForm.createuserName = ''
  erpForm.projectId = undefined
  erpForm.name = ''
  erpForm.groupId = undefined
  erpDialogVisible.value = true
}

/**
 * 上传安全报告到 ERP
 * - erp: 上传到 ERP 测试单（自动生成报告 → DOCX → 创建报告）
 * - project: 上传到项目资料
 * - cooperation: 上传到协作文档
 */
const handleUploadErp = async () => {
  const executionId = currentSecurityExecutionId.value
  if (!executionId) return

  // 校验
  if (erpForm.target !== 'erp' && !erpForm.projectId) {
    ElMessage.warning('请输入项目 ID')
    return
  }

  const copyuserNames = erpForm.copyuserNamesText
    .split(/[,，、]/)
    .map((s: string) => s.trim())
    .filter(Boolean)

  uploadingErp.value = true
  try {
    let res: any
    if (erpForm.target === 'erp') {
      res = await uploadSecurityReportToErp(executionId, {
        developtesting_id: erpForm.developtestingId,
        copyuser_names: copyuserNames,
        createuser_name: erpForm.createuserName || undefined,
      })
    } else if (erpForm.target === 'project') {
      res = await uploadSecurityReportToProject(executionId, {
        project_id: erpForm.projectId as number,
        name: erpForm.name || undefined,
      })
    } else {
      res = await uploadSecurityReportToCooperation(executionId, {
        project_id: erpForm.projectId as number,
        name: erpForm.name || undefined,
        group_id: erpForm.groupId,
      })
    }

    if (res?.success) {
      ElMessage.success(res.message || '上传成功')
      erpDialogVisible.value = false
    } else {
      ElMessage.error(res?.message || '上传失败，请查看日志')
    }
  } catch (error: any) {
    ElMessage.error('ERP 上传失败: ' + (error?.message || '未知错误'))
  } finally {
    uploadingErp.value = false
  }
}

/**
 * 打开 ERP 任务创建对话框
 * 调用预览接口获取报告摘要、基础数据、默认配置
 */
const openErpTaskDialog = async () => {
  const executionId = currentSecurityExecutionId.value
  if (!executionId) return

  taskLoading.value = true
  erpTaskDialogVisible.value = true
  taskPreview.value = null

  // 重置表单为默认值
  taskForm.task_name = ''
  taskForm.type_id = 3
  taskForm.status_id = 1
  taskForm.level = 4
  taskForm.excepthour = 21
  taskForm.user_id = 101
  taskForm.createuser_id = 441
  taskForm.deadline_days = 7
  taskForm.develop_id = undefined

  try {
    const res = await getSecurityTaskPreview(executionId)
    taskPreview.value = res

    // 应用预览数据中的默认配置
    if (res.default_config) {
      taskForm.type_id = res.default_config.type_id ?? 3
      taskForm.status_id = res.default_config.status_id ?? 1
      taskForm.level = res.default_config.level ?? 4
      taskForm.excepthour = res.default_config.excepthour ?? 21
      taskForm.user_id = res.default_config.user_id ?? 101
      taskForm.createuser_id = res.default_config.createuser_id ?? 441
      taskForm.deadline_days = res.default_config.deadline_days ?? 7
    }
    taskForm.task_name = res.task_name || ''

    // 如果有关联需求，设置 develop_id
    if (res.requirement?.id) {
      taskForm.develop_id = res.requirement.id
    }
  } catch (error: any) {
    ElMessage.error('获取任务预览失败: ' + (error?.message || '未知错误'))
    erpTaskDialogVisible.value = false
  } finally {
    taskLoading.value = false
  }
}

/**
 * 创建 ERP 任务
 */
const handleCreateTask = async () => {
  const executionId = currentSecurityExecutionId.value
  if (!executionId) return

  // 校验
  if (!taskForm.task_name) {
    ElMessage.warning('请输入任务名称')
    return
  }

  taskLoading.value = true
  try {
    const res = await createSecurityTask(executionId, {
      type_id: taskForm.type_id,
      status_id: taskForm.status_id,
      level: taskForm.level,
      excepthour: taskForm.excepthour,
      user_id: taskForm.user_id,
      createuser_id: taskForm.createuser_id,
      deadline_days: taskForm.deadline_days,
      develop_id: taskForm.develop_id || undefined,
      task_name: taskForm.task_name || undefined,
    })

    if (res?.success) {
      ElMessage.success(res.message || 'ERP任务创建成功')
      erpTaskDialogVisible.value = false
    } else {
      ElMessage.error(res?.message || 'ERP任务创建失败，请查看日志')
    }
  } catch (error: any) {
    ElMessage.error('创建 ERP 任务失败: ' + (error?.message || '未知错误'))
  } finally {
    taskLoading.value = false
  }
}

// ==================== 生命周期 ====================

onMounted(() => {
  loadReports()
})

// 路由类型变化时重新加载（切换报告类型子菜单）
watch(currentType, () => {
  currentPage.value = 1
  loadReports()
})
</script>

<style lang="scss" scoped>
.reports-page {
  .page-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 20px;

    h2 {
      margin: 0;
      font-size: 20px;
    }
  }

  .reports-list {
    .pass-rate-cell {
      display: flex;
      align-items: center;
      gap: 8px;
    }

    .count-cell {
      font-size: 13px;

      .count-passed { color: #67c23a; font-weight: 600; }
      .count-sep { color: #909399; margin: 0 2px; }
      .count-failed { color: #f56c6c; font-weight: 600; }
      .count-total { color: #606266; }
    }

    .rate-success { color: #67c23a; font-weight: 600; }
    .rate-warning { color: #e6a23c; font-weight: 600; }
    .rate-danger { color: #f56c6c; font-weight: 600; }

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

  .report-preview-container {
    height: calc(100vh - 200px);

    .report-iframe {
      width: 100%;
      height: 100%;
      border: none;
      border-radius: 4px;
    }
  }

  // 安全测试报告样式
  .security-report-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    width: 100%;
  }

  .security-report-actions {
    display: flex;
    gap: 8px;
  }

  .security-report-container {
    height: calc(100vh - 200px);
    overflow-y: auto;
    padding: 20px;
    background-color: var(--el-bg-color);
    border-radius: 4px;
  }
}

// Markdown 样式（GitHub 风格）
.markdown-body {
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Noto Sans', Helvetica, Arial, sans-serif;
  font-size: 14px;
  line-height: 1.6;
  color: var(--el-text-color-primary);

  :deep(h1) {
    font-size: 2em;
    font-weight: 600;
    padding-bottom: 0.3em;
    border-bottom: 1px solid var(--el-border-color);
    margin-top: 24px;
    margin-bottom: 16px;
  }

  :deep(h2) {
    font-size: 1.5em;
    font-weight: 600;
    padding-bottom: 0.3em;
    border-bottom: 1px solid var(--el-border-color);
    margin-top: 24px;
    margin-bottom: 16px;
  }

  :deep(h3) {
    font-size: 1.25em;
    font-weight: 600;
    margin-top: 24px;
    margin-bottom: 16px;
  }

  :deep(h4) {
    font-size: 1em;
    font-weight: 600;
    margin-top: 24px;
    margin-bottom: 16px;
  }

  :deep(p) {
    margin-bottom: 16px;
  }

  :deep(table) {
    border-collapse: collapse;
    width: 100%;
    margin-bottom: 16px;

    th, td {
      padding: 8px 13px;
      border: 1px solid var(--el-border-color);
    }

    th {
      font-weight: 600;
      background-color: var(--el-fill-color-light);
    }

    tr:nth-child(2n) {
      background-color: var(--el-fill-color-lighter);
    }
  }

  :deep(code) {
    padding: 0.2em 0.4em;
    margin: 0;
    font-size: 85%;
    font-family: ui-monospace, SFMono-Regular, SF Mono, Menlo, Consolas, Liberation Mono, monospace;
    background-color: var(--el-fill-color-light);
    border-radius: 6px;
  }

  :deep(pre) {
    padding: 16px;
    overflow: auto;
    font-size: 85%;
    line-height: 1.45;
    background-color: var(--el-fill-color-light);
    border-radius: 6px;
    margin-bottom: 16px;

    code {
      padding: 0;
      background-color: transparent;
    }
  }

  :deep(ul), :deep(ol) {
    padding-left: 2em;
    margin-bottom: 16px;
  }

  :deep(li) {
    margin-bottom: 4px;
  }

  :deep(blockquote) {
    padding: 0 1em;
    color: var(--el-text-color-secondary);
    border-left: 0.25em solid var(--el-border-color);
    margin-bottom: 16px;
  }

  :deep(hr) {
    height: 0.25em;
    padding: 0;
    margin: 24px 0;
    background-color: var(--el-border-color);
    border: 0;
  }

  :deep(a) {
    color: var(--el-color-primary);
    text-decoration: none;

    &:hover {
      text-decoration: underline;
    }
  }

  :deep(img) {
    max-width: 100%;
    height: auto;
  }

  // 特殊样式：OWASP 矩阵表格
  :deep(.owasp-matrix) {
    text-align: center;

    td:first-child {
      text-align: left;
    }
  }
}
</style>
