<template>
  <div class="save-records">
    <!-- ── Toolbar ── -->
    <div class="toolbar">
      <div class="toolbar-row">
        <div class="filter-group">
          <el-select v-model="statusFilter" placeholder="状态" clearable style="width: 100px" @change="loadRecords(1)">
            <el-option label="全部状态" value="" />
            <el-option label="✓ 成功" value="success" />
            <el-option label="♻️ 复用" value="reused" />
            <el-option label="✗ 失败" value="failed" />
          </el-select>

          <el-select v-model="cloudFilter" placeholder="网盘" clearable style="width: 100px" @change="loadRecords(1)">
            <el-option label="全部网盘" value="" />
            <el-option v-for="ct in cloudTypes" :key="ct" :label="cloudLabel(ct)" :value="ct">
              <span :style="{ display: 'inline-flex', alignItems: 'center', gap: '6px' }">
                <img :src="cloudIcon(ct)" style="width:16px;height:16px" />
                {{ cloudLabel(ct) }}
              </span>
            </el-option>
          </el-select>

          <div class="time-btns">
            <button
              v-for="btn in timeButtons" :key="btn.key"
              :class="['time-btn', { active: activeTimeBtn === btn.key }]"
              @click="setTimeFilter(btn.key)"
            >{{ btn.label }}</button>
          </div>

          <el-date-picker
            v-model="dateRange"
            type="daterange"
            range-separator="至"
            start-placeholder="开始日期"
            end-placeholder="结束日期"
            value-format="YYYY-MM-DD"
            style="width: 220px"
            @change="onDateRangeChange"
          />

          <el-input
            v-model="searchKeyword"
            placeholder="搜索资源名称…"
            clearable
            style="width: 180px"
            @clear="loadRecords(1)"
            @keyup.enter="loadRecords(1)"
          >
            <template #prefix>
              <el-icon><Search /></el-icon>
            </template>
          </el-input>
        </div>

        <div class="toolbar-actions">
          <el-button size="small" @click="resetFilters">重置筛选</el-button>
          <span class="record-count">共 {{ total }} 条</span>
        </div>
      </div>
    </div>

    <!-- ── 转存统计汇总 ── -->
    <div v-if="summary" class="save-summary">
      <span class="summary-item summary-all">📊 共 <strong>{{ summary.total }}</strong> 条</span>
      <span class="summary-divider">|</span>
      <span class="summary-item summary-success">✅ 成功 <strong>{{ summary.success }}</strong></span>
      <span class="summary-item summary-reused">♻️ 复用 <strong>{{ summary.reused }}</strong></span>
      <span class="summary-item summary-failed">❌ 失败 <strong>{{ summary.failed }}</strong></span>
      <span class="summary-item summary-rate" v-if="summary.total > 0">
        成功率 <strong>{{ ((summary.success + summary.reused) / summary.total * 100).toFixed(1) }}%</strong>
      </span>
    </div>

    <!-- ── Table ── -->
    <el-card shadow="never" class="save-table-card">
      <template #header><span>📋 转存日志列表</span></template>
      <div class="el-table-wrap">
      <el-table
        :data="records" stripe style="width: 100%"
        v-loading="loading"
        empty-text="暂无转存记录"
        @expand-change="onExpandChange"
        :row-class-name="rowClassName"
      >
      <el-table-column type="expand" width="36">
        <template #default="{ row }">
          <div class="expand-detail">
            <!-- Row 1: 原始链接 + 文件大小 + 文件夹+文件数 -->
            <div class="detail-row">
              <div class="detail-cell" style="flex:2">
                <span class="detail-label">原始链接</span>
                <a :href="row.source_url" target="_blank" class="detail-link">{{ row.source_url }}</a>
              </div>
              <div class="detail-cell" v-if="row.file_size">
                <span class="detail-label">文件大小</span>
                <code class="detail-code">{{ row.file_size ? (function(n){if(n==null||n==='')return'-';var v=typeof n==='string'?parseInt(n,10):n;if(!v||v<=0)return'-';var u=['B','KB','MB','GB','TB'];var i=0,s=v;while(s>=1024&&i<4){s/=1024;i++}return s.toFixed(i===0?0:2)+' '+u[i]})(row.file_size) : '-' }}</code>
              </div>
              <div class="detail-cell" v-if="row.status !== 'reused' && (row.folder_count > 0 || row.file_count > 0)">
                <span class="detail-label">文件夹</span>
                <span><strong>{{ row.folder_count || 0 }}</strong> 个</span>
              </div>
              <div class="detail-cell" v-if="row.status !== 'reused' && (row.folder_count > 0 || row.file_count > 0)">
                <span class="detail-label">文件</span>
                <span><strong>{{ row.file_count || 0 }}</strong> 个</span>
              </div>
              <div class="detail-cell" v-if="row.status === 'reused'">
                <span class="detail-label">复用方式</span>
                <span class="reuse-msg">♻️ 直接使用已有分享链接，无需实际转存</span>
              </div>
            </div>
            <!-- Row 2: 使用账号 + 原始文件夹名 -->
            <div class="detail-row">
              <div class="detail-cell" v-if="row.config_nickname">
                <span class="detail-label">使用账号</span>
                <el-tag size="small" type="success" effect="plain">{{ row.config_nickname }}</el-tag>
              </div>
              <div class="detail-cell" v-if="row.original_folder_name">
                <span class="detail-label">原始文件夹名</span>
                <code class="detail-code">{{ row.original_folder_name }}</code>
              </div>
              <div class="detail-cell" v-if="row.folder_name">
                <span class="detail-label">转存文件夹</span>
                <code class="detail-code">{{ row.folder_name }}</code>
              </div>
            </div>
            <!-- Row 3: 分享链接 + 分享密码 + 耗时 -->
            <div class="detail-row">
              <div class="detail-cell" v-if="row.share_url" style="flex:2">
                <span class="detail-label">分享链接</span>
                <a :href="row.share_url" target="_blank" class="detail-link">{{ row.share_url }}</a>
              </div>
              <div class="detail-cell" v-if="row.share_pwd">
                <span class="detail-label">分享密码</span>
                <el-tag size="small" type="warning">{{ row.share_pwd }}</el-tag>
              </div>
              <div class="detail-cell">
                <span class="detail-label">耗时</span>
                <span :class="['detail-duration', durationClass(row.duration_ms)]">{{ formatDuration(row.duration_ms) }}</span>
              </div>
            </div>
            <!-- Row 4: IP地址 + 归属地 + 创建时间 -->
            <div class="detail-row" v-if="row.ip_address">
              <div class="detail-cell">
                <span class="detail-label">IP 地址</span>
                <code class="detail-code">{{ row.ip_address }}</code>
              </div>
              <div class="detail-cell" v-if="row.ip_location">
                <span class="detail-label">归属地</span>
                <code class="detail-code">{{ formatLocation(row.ip_location) }}</code>
              </div>
              <div class="detail-cell">
                <span class="detail-label">时间</span>
                <code class="detail-code">{{ formatTime(row.created_at) }}</code>
              </div>
            </div>
            <!-- Row 5: 错误信息（整行） -->
            <div class="detail-row" v-if="row.status === 'failed' && row.error_message">
              <div class="detail-cell detail-full">
                <span class="detail-label">错误信息</span>
                <pre class="detail-error">{{ row.error_message.includes(' | ') ? row.error_message.split(' | ')[1] : row.error_message.split(' | ')[0] }}</pre>
              </div>
            </div>
            <div class="detail-row" v-if="row.status === 'failed' && row.error_message && row.error_message.includes(' | ')">
              <div class="detail-cell detail-full">
                <span class="detail-label">友好提示</span>
                <pre class="detail-error">{{ row.error_message.split(' | ')[0] }}</pre>
              </div>
            </div>
          </div>
        </template>
      </el-table-column>

      <el-table-column label="序号" width="68" align="center">
        <template #default="{ $index }">
          {{ (currentPage - 1) * pageSize + $index + 1 }}
        </template>
      </el-table-column>

      <el-table-column label="时间" width="140">
        <template #default="{ row }">
          <span :title="row.created_at">{{ formatTime(row.created_at) }}</span>
        </template>
      </el-table-column>

      <el-table-column label="网盘" width="70" align="center">
        <template #default="{ row }">
          <el-tooltip :content="cloudLabel(row.source_type)" placement="top">
            <img :src="cloudIcon(row.source_type)" style="width:22px;height:22px;cursor:default" />
          </el-tooltip>
        </template>
      </el-table-column>

      <el-table-column label="状态" width="72" align="center">
        <template #default="{ row }">
          <el-tooltip :content="statusTip(row.status)" placement="top">
            <span :class="['status-badge', statusClass(row.status)]">
              {{ statusIcon(row.status) }}
            </span>
          </el-tooltip>
        </template>
      </el-table-column>

      <el-table-column label="资源名称" min-width="160" show-overflow-tooltip>
        <template #default="{ row }">
          <span :title="row.source_title || ''">{{ row.source_title || '-' }}</span>
        </template>
      </el-table-column>

      <el-table-column label="耗时" width="85" align="center">
        <template #default="{ row }">
          <span :class="['duration', durationClass(row.duration_ms)]">
            {{ formatDuration(row.duration_ms) }}
          </span>
        </template>
      </el-table-column>

      <el-table-column label="归属地" min-width="130" show-overflow-tooltip>
        <template #default="{ row }">
          <span v-if="row.ip_location" class="loc-badge">{{ formatLocation(row.ip_location) }}</span>
          <span v-else class="no-data">-</span>
        </template>
      </el-table-column>

      <el-table-column label="备注" min-width="200" show-overflow-tooltip>
        <template #default="{ row }">
          <span v-if="row.status === 'failed' && row.error_message" class="err-msg" :title="row.error_message">
            {{ truncateErr(row.error_message) }}
          </span>
          <span v-else-if="row.status === 'failed'" class="err-msg">失败</span>
          <span v-else-if="row.status === 'reused'" class="reuse-msg">♻️ 复用已有链接</span>
          <span v-else class="no-data">-</span>
        </template>
      </el-table-column>

      <el-table-column label="操作" width="80" fixed="right" align="center">
        <template #default="{ row }">
          <div class="action-cell">
            <el-tooltip content="复制分享链接" placement="top">
              <el-button size="small" circle text :disabled="!row.share_url" @click="copyText(row.share_url!)">
                <el-icon><Link /></el-icon>
              </el-button>
            </el-tooltip>
            <el-tooltip content="打开分享链接" placement="top">
              <el-button size="small" circle text :disabled="!row.share_url" @click="windowOpen(row.share_url!)">
                <el-icon><TopRight /></el-icon>
              </el-button>
            </el-tooltip>
          </div>
        </template>
      </el-table-column>
    </el-table>
    </div>
    </el-card>

    <!-- ── Pagination ── -->
    <div class="pagination-wrap" v-if="total > 0">
      <div class="pagination-info">
        第 {{ (currentPage - 1) * pageSize + 1 }}-{{ Math.min(currentPage * pageSize, total) }} 条，共 {{ total }} 条
      </div>
      <el-pagination
        v-model:current-page="currentPage"
        v-model:page-size="pageSize"
        :total="total"
        :page-sizes="[15, 20, 30, 50, 100]"
        layout="sizes, prev, pager, next, jumper"
        @current-change="loadRecords"
        @size-change="loadRecords(1)"
      />
    </div>
  </div>
</template>

<script setup lang="ts">
import { ref, onMounted } from 'vue'
import { getSaveRecords, getCloudTypes } from '../../api'
import type { SaveRecord } from '../../api'
import { ElMessage } from 'element-plus'
import { Search, CopyDocument, Link, TopRight } from '@element-plus/icons-vue'

const records = ref<SaveRecord[]>([])
const total = ref(0)
const currentPage = ref(1)
const pageSize = ref(20)
const loading = ref(false)
const statusFilter = ref('')
const cloudFilter = ref('')
const searchKeyword = ref('')
const activeTimeBtn = ref('today')
const timeStart = ref('')
const timeEnd = ref('')
const dateRange = ref<string[] | null>(null)
const cloudTypes = ref<string[]>([])
const summary = ref<{ total: number; success: number; failed: number; reused: number } | null>(null)

const timeButtons = [
  { key: 'today', label: '今日' },
  { key: 'week', label: '本周' },
  { key: 'month', label: '本月' },
  { key: 'lastMonth', label: '上月' },
]

// ── Cloud type helpers — loaded from backend API ──
const cloudTypeMap = ref<Record<string, { label: string; icon: string }>>({})
const FALLBACK_ICON_SVG = '<svg viewBox="0 0 24 24" width="16" height="16"><rect rx="4" width="24" height="24" fill="#909399"/><text x="12" y="16" text-anchor="middle" fill="white" font-size="14" font-weight="bold" font-family="Arial">☁</text></svg>'

async function loadCloudTypes() {
  try {
    const res = await getCloudTypes()
    const map: Record<string, { label: string; icon: string }> = {}
    for (const ct of res.types) {
      map[ct.type] = { label: ct.label, icon: ct.icon }
    }
    cloudTypeMap.value = map
  } catch {
    // ignore
  }
}
function cloudLabel(t: string): string { return cloudTypeMap.value[t]?.label || t }
function cloudIcon(t: string): string  { return cloudTypeMap.value[t]?.icon || FALLBACK_ICON_SVG }

function extractCloudTypes(data: SaveRecord[]) {
  const set = new Set<string>()
  data.forEach(r => { if (r.source_type) set.add(r.source_type) })
  const existing = new Set(cloudTypes.value)
  set.forEach(t => { if (!existing.has(t)) cloudTypes.value.push(t) })
}

// ── Formatting helpers ──
function fmt(d: Date): string {
  const y = d.getFullYear()
  const M = String(d.getMonth() + 1).padStart(2, '0')
  const D = String(d.getDate()).padStart(2, '0')
  return `${y}-${M}-${D}`
}

function formatTime(t: string): string {
  if (!t) return '-'
  let ts = t
  if (/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/.test(ts)) ts = ts.replace(' ', 'T') + '+08:00'
  const d = new Date(ts)
  if (isNaN(d.getTime())) return t
  const pad = (n: number) => String(n).padStart(2, '0')
  return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`
}

function formatDuration(ms: number): string {
  if (!ms) return '-'
  if (ms < 1000) return `${ms}ms`
  return `${(ms / 1000).toFixed(1)}s`
}

function durationClass(ms: number): string {
  if (!ms) return ''
  if (ms > 30000) return 'dur-slow'
  if (ms > 10000) return 'dur-warn'
  return 'dur-fast'
}

function fileCountType(n: number): string {
  if (n >= 50) return 'danger'
  if (n >= 10) return 'warning'
  return 'success'
}

function truncateErr(msg: string): string {
  return msg.length > 50 ? msg.slice(0, 50) + '…' : msg
}

// ── Status helpers ──
function statusTip(status: string): string {
  if (status === 'success') return '转存成功'
  if (status === 'reused') return '♻️ 复用已有分享链接'
  return '转存失败'
}
function statusClass(status: string): string {
  if (status === 'success') return 'status-ok'
  if (status === 'reused') return 'status-reuse'
  return 'status-fail'
}
function statusIcon(status: string): string {
  if (status === 'success') return '✓'
  if (status === 'reused') return '♻️'
  return '✗'
}

/** 省份/城市中英文翻译（用于 api.ip.sb 返回的英语地名） */
const CN_PLACES: Record<string, string> = {
  // 省份/直辖市
  'Anhui':'安徽','Beijing':'北京','Chongqing':'重庆','Fujian':'福建','Gansu':'甘肃','Guangdong':'广东',
  'Guangxi':'广西','Guizhou':'贵州','Hainan':'海南','Hebei':'河北','Henan':'河南','Heilongjiang':'黑龙江',
  'Hubei':'湖北','Hunan':'湖南','Inner Mongolia':'内蒙古','Jiangsu':'江苏','Jiangxi':'江西','Jilin':'吉林',
  'Liaoning':'辽宁','Ningxia':'宁夏','Qinghai':'青海','Shaanxi':'陕西','Shandong':'山东','Shanghai':'上海',
  'Shanxi':'山西','Sichuan':'四川','Tianjin':'天津','Tibet':'西藏','Xinjiang':'新疆','Yunnan':'云南','Zhejiang':'浙江',
  'Hong Kong':'香港','Macau':'澳门','Taiwan':'台湾',
  // 主要城市
  'Changsha':'长沙','Hefei':'合肥','Fuzhou':'福州','Lanzhou':'兰州',
  'Guangzhou':'广州','Nanning':'南宁','Guiyang':'贵阳','Haikou':'海口',
  'Shijiazhuang':'石家庄','Zhengzhou':'郑州','Harbin':'哈尔滨','Wuhan':'武汉',
  'Nanjing':'南京','Nanchang':'南昌','Changchun':'长春','Shenyang':'沈阳',
  'Yinchuan':'银川','Xining':'西宁',"Xi'an":"西安",'Jinan':'济南',
  'Taiyuan':'太原','Chengdu':'成都','Shenzhen':'深圳','Hangzhou':'杭州',
  'Suzhou':'苏州','Wuxi':'无锡','Ningbo':'宁波','Dongguan':'东莞',
  'Foshan':'佛山','Zhuhai':'珠海','Qingdao':'青岛','Dalian':'大连',
  'Xiamen':'厦门','Kunming':'昆明','Lhasa':'拉萨','Urumqi':'乌鲁木齐',
  'Linyi':'临沂','Wenzhou':'温州','Quanzhou':'泉州',
};

/** 常见英文ISP → 中文 */
const CN_ISP: Record<string, string> = {
  'China Telecom':'中国电信','China Mobile':'中国移动','China Unicom':'中国联通',
  'Chinanet':'中国电信','ChinaNet':'中国电信','CMNET':'中国移动',
  'CNC Group':'中国联通','unicom':'中国联通','telecom':'中国电信','mobile':'中国移动',
  'China Education and Research Network':'教育网','CERNET':'教育网',
  'China Networks':'中国网络','China163':'中国电信','CHINANET BACKBONE':'中国电信',
  'Tencent Cloud':'腾讯云','Alibaba Cloud':'阿里云','Aliyun':'阿里云','Huawei Cloud':'华为云',
  'Baidu':'百度','Beijing Baidu':'百度',
};

function formatLocation(loc: string): string {
  // Remove "China" prefix
  let s = loc.replace(/^(中国|China)\s*/i, '')
  // Split into parts
  const parts = s.split(/\s+/).filter(Boolean)
  // Translate each part
  return parts.map(p => CN_PLACES[p] || CN_ISP[p] || p).join(' ')
}

function rowClassName({ row }: { row: SaveRecord }) {
  return row.status === 'failed' ? 'row-failed' : ''
}

// ── Actions ──
async function copyText(text: string) {
  try {
    await navigator.clipboard.writeText(text)
    ElMessage.success('已复制到剪贴板')
  } catch {
    // fallback
    const ta = document.createElement('textarea')
    ta.value = text
    document.body.appendChild(ta)
    ta.select()
    document.execCommand('copy')
    document.body.removeChild(ta)
    ElMessage.success('已复制到剪贴板')
  }
}

function windowOpen(url: string) {
  window.open(url, '_blank')
}

function onExpandChange(row: SaveRecord, expanded: boolean[]) {
  // just placeholder for potential future use
}

// ── Filter helpers ──
function setTimeFilter(key: string) {
  activeTimeBtn.value = key
  dateRange.value = null
  const now = new Date()
  const y = now.getFullYear()
  const m = now.getMonth()

  let start: Date
  let end: Date
  switch (key) {
    case 'today':
      start = new Date(y, m, now.getDate())
      end = start
      break
    case 'week': {
      const dow = now.getDay()
      start = new Date(y, m, now.getDate() + (dow === 0 ? -6 : 1 - dow))
      end = now
      break
    }
    case 'month':
      start = new Date(y, m, 1)
      end = now
      break
    case 'lastMonth':
      start = new Date(y, m - 1, 1)
      end = new Date(y, m, 0)
      break
    default:
      start = new Date(y, m, now.getDate())
      end = start
  }
  timeStart.value = fmt(start)
  const nextDay = new Date(end.getFullYear(), end.getMonth(), end.getDate() + 1)
  timeEnd.value = fmt(nextDay)
  loadRecords(1)
}

function onDateRangeChange(val: string[] | null) {
  if (val && val.length === 2) {
    activeTimeBtn.value = ''
    timeStart.value = val[0]
    const next = new Date(val[1])
    next.setDate(next.getDate() + 1)
    timeEnd.value = fmt(next)
    loadRecords(1)
  } else {
    setTimeFilter('today')
  }
}

function resetFilters() {
  statusFilter.value = ''
  cloudFilter.value = ''
  searchKeyword.value = ''
  dateRange.value = null
  setTimeFilter('today')
}

async function loadRecords(page = 1) {
  loading.value = true
  try {
    currentPage.value = page
    const s = statusFilter.value || undefined
    const c = cloudFilter.value || undefined
    const kw = searchKeyword.value || undefined
    const res = await getSaveRecords(page, pageSize.value, timeStart.value, timeEnd.value, s, c, kw)
    records.value = res.records
    total.value = res.total
    summary.value = res.summary || null
    extractCloudTypes(res.records)
  } catch (e) {
    console.error('加载转存记录失败', e)
  } finally {
    loading.value = false
  }
}

onMounted(() => {
  setTimeFilter('today')
  loadCloudTypes()
})
</script>

<style scoped>
.save-records { padding: 0; }

/* ── Toolbar ── */
.toolbar-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 20px;
  padding-bottom: 16px;
  flex-wrap: wrap;
  border-bottom: 1px solid var(--el-border-color-light, #ebeef5);
}
.filter-group {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
}
.toolbar-actions {
  display: flex;
  align-items: center;
  gap: 12px;
}
.record-count {
  font-size: 13px;
  color: var(--text-secondary, #909399);
  white-space: nowrap;
}

/* ── Time buttons ── */
.time-btns {
  display: flex;
  gap: 2px;
  background: var(--el-fill-color-light, #f0f2f5);
  border-radius: 8px;
  padding: 3px;
}
.time-btn {
  border: none;
  background: transparent;
  padding: 6px 14px;
  border-radius: 6px;
  font-size: 13px;
  color: #606266;
  cursor: pointer;
  transition: all .25s ease;
}
.time-btn:hover {
  color: var(--el-color-primary);
  background: rgba(64,158,255,.06);
}
.time-btn.active {
  background: linear-gradient(135deg, var(--el-color-primary), var(--el-color-primary-light-3, #79bbff));
  color: #fff;
  font-weight: 600;
  box-shadow: 0 2px 6px rgba(64,158,255,.3);
}

/* ── Table ── */
.save-table-card {
  border-radius: var(--radius-lg);
  border: 1px solid var(--border) !important;
}
.save-table-card :deep(.el-card__header) {
  font-size: 15px;
  font-weight: 600;
  background: var(--bg-card-header);
  border-bottom: 1px solid var(--border);
  padding: 12px 18px;
}
.save-table-card :deep(.el-card__body) {
  padding: 0;
}
.save-records :deep(.el-table) {
  border: 1px solid var(--el-border-color-light, #ebeef5);
  border-radius: 8px;
}
.save-records .el-table-wrap {
  overflow-x: auto;
}
.save-records :deep(.el-table th.el-table__cell) {
  background-color: var(--el-fill-color-light, #f5f7fa);
  font-weight: 600;
  color: var(--el-text-color-primary, #303133);
  white-space: nowrap;
}
.save-records :deep(.el-table .el-table__cell) {
  padding: 8px 8px;
  white-space: nowrap !important;
}
.save-records :deep(.el-table .el-table__cell .cell) {
  white-space: nowrap !important;
}

/* ── Cell content: one-line with ellipsis ── */
.cell-nowrap {
  display: block;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.save-records :deep(.el-table__row:hover > .el-table__cell) {
  background-color: var(--el-color-primary-light-9, #ecf5ff);
}

/* Row highlight for failed */
.save-records :deep(.row-failed > .el-table__cell) {
  background-color: rgba(245,108,108,.04);
}

/* ── Status badge ── */
.status-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;
  border-radius: 50%;
  font-size: 13px;
  font-weight: bold;
}
.status-ok {
  background: rgba(103,194,58,.15);
  color: #67c23a;
}
.status-reuse {
  background: rgba(64,158,255,.15);
  color: #409eff;
}
.status-fail {
  background: rgba(245,108,108,.15);
  color: #f56c6c;
}

/* ── Cell helpers ── */
.ip-text {
  font-family: monospace;
  font-size: 12px;
}
.loc-badge {
  display: inline-block;
  background: linear-gradient(135deg, #e8f4ff, #f0f8ff);
  color: #1a6ea0;
  font-size: 12px;
  padding: 2px 10px;
  border-radius: 4px;
  border: 1px solid #b8d9f0;
  white-space: nowrap;
}
.err-msg {
  color: var(--el-color-danger);
  font-size: 12px;
}
.reuse-msg {
  color: var(--el-color-primary);
  font-size: 12px;
}
.no-data {
  color: var(--text-secondary, #c0c4cc);
}

/* ── Duration color ── */
.duration { font-size: 12px; font-family: monospace; }
.dur-fast { color: #67c23a; }
.dur-warn { color: #e6a23c; }
.dur-slow { color: #f56c6c; font-weight: 600; }

/* ── Expand detail (flex rows) ── */
.expand-detail {
  padding: 16px 24px;
  background: var(--el-fill-color-lighter, #fafafa);
  border-radius: 6px;
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.detail-row {
  display: flex;
  align-items: flex-start;
  gap: 16px 24px;
  flex-wrap: wrap;
}
.detail-cell {
  display: flex;
  flex-direction: column;
  gap: 4px;
  flex: none;
}
.detail-cell.detail-full {
  flex: 1 1 100%;
}
.detail-label {
  font-size: 12px;
  color: #909399;
  font-weight: 500;
}
.detail-link {
  color: var(--el-color-primary);
  font-size: 13px;
  word-break: break-all;
  text-decoration: none;
}
.detail-link:hover { text-decoration: underline; }
.detail-code {
  font-size: 12px;
  background: #f0f0f0;
  padding: 4px 8px;
  border-radius: 4px;
  word-break: break-all;
}
.detail-error {
  margin: 0;
  font-size: 12px;
  color: #f56c6c;
  background: rgba(245,108,108,.08);
  padding: 8px 12px;
  border-radius: 4px;
  white-space: pre-wrap;
  word-break: break-word;
  max-height: 120px;
  overflow-y: auto;
}

/* ── Action cell ── */
.action-cell {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 2px;
}

/* ── Pagination ── */
.pagination-wrap {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-top: 20px;
  padding: 12px 16px;
  background: var(--el-fill-color-light, #f5f7fa);
  border-radius: 8px;
  flex-wrap: wrap;
  gap: 12px;
}
.pagination-info {
  font-size: 13px;
  color: var(--text-secondary, #909399);
  white-space: nowrap;
}

/* ── 转存统计汇总 ── */
.save-summary {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 10px 16px;
  margin-bottom: 8px;
  background: var(--el-fill-color-light, #f5f7fa);
  border-radius: 8px;
  font-size: 13px;
  flex-wrap: wrap;
}
.summary-item {
  white-space: nowrap;
}
.summary-success strong { color: #67c23a; }
.summary-failed strong { color: #f56c6c; }
.summary-reused strong { color: #e6a23c; }
.summary-rate { color: #909399; }
.summary-rate strong { color: #409eff; }
.summary-divider { color: #dcdfe6; font-size: 12px; user-select: none; }
</style>
