<script setup lang="ts">
import { ref, computed, onMounted, defineAsyncComponent, inject } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { zebraApi, type ZebraOrder, type BanmaAccount } from '../../api/zebra'
import AccountManager from '../common/AccountManager.vue'
import { batchConvertImages } from '../../utils/imageProxy'
import { handlePlatformFileExport } from '../../utils/settings'
import { getUsernameFromToken } from '../../utils/token'

const TrialExpiredDialog = defineAsyncComponent(() => import('../common/TrialExpiredDialog.vue'))

const refreshVipStatus = inject<() => Promise<boolean>>('refreshVipStatus')

// 接收VIP状态
const props = defineProps<{
  isVip: boolean
}>()

type Shop = { id: string; shopName: string }

const accounts = ref<BanmaAccount[]>([])
const accountId = ref<number>()
// 收起功能移除

const shopList = ref<Shop[]>([])
const selectedShops = ref<string[]>([])
const dateRange = ref<string[]>([])

const loading = ref(false)
const exportLoading = ref(false)
const progressPercentage = ref(0)
const showProgress = ref(false)
const allOrderData = ref<ZebraOrder[]>([])
const currentPage = ref(1)
const pageSize = ref(15)
const currentBatchId = ref('')

// 批量获取状态
const fetchCurrentPage = ref(1)
const fetchTotalPages = ref(0)
const fetchTotalItems = ref(0)
const isFetching = ref(false)
let abortController: AbortController | null = null

// 试用期过期弹框
const showTrialExpiredDialog = ref(false)
const trialExpiredType = ref<'device' | 'account' | 'both'>('account')

const checkExpiredType = inject<() => 'device' | 'account' | 'both'>('checkExpiredType')
function selectAccount(id: number) {
  accountId.value = id
  loadShops()
}

const paginatedData = computed(() => {
  const start = (currentPage.value - 1) * pageSize.value
  const end = start + pageSize.value
  return allOrderData.value.slice(start, end)
})

function formatJpy(v?: number) {
  const n = Number(v || 0)
  return `¥${n.toLocaleString('ja-JP')}`
}

function formatCny(v?: number) {
  const n = Number(v || 0)
  return `¥${n.toLocaleString('zh-CN')}`
}

async function loadShops() {
  try {
    const resp = await zebraApi.getShops({ accountId: Number(accountId.value) || undefined })
    const list = (resp as any)?.data?.data?.list ?? (resp as any)?.list ?? []
    shopList.value = list
  } catch (e) {
    console.error('获取店铺列表失败:', e)
  }
}

async function loadAccounts() {
  try {
    const username = getUsernameFromToken()
    const res = await zebraApi.getAccounts(username)
    const list = (res as any)?.data ?? res
    accounts.value = Array.isArray(list) ? list : []
    const def = accounts.value.find(a => a.isDefault === 1) || accounts.value[0]
    accountId.value = def?.id
    await loadShops()
  } catch (e) {
    accounts.value = []
  }
}

function handleSizeChange(size: number) {
  pageSize.value = size
  currentPage.value = 1
}

function handleCurrentChange(page: number) {
  currentPage.value = page
}

async function fetchData() {
  if (isFetching.value) return

  // 刷新VIP状态
  if (refreshVipStatus) await refreshVipStatus()

  // VIP检查
  if (!props.isVip) {
    if (checkExpiredType) trialExpiredType.value = checkExpiredType()
    showTrialExpiredDialog.value = true
    return
  }

  abortController = new AbortController()
  loading.value = true
  isFetching.value = true
  showProgress.value = true
  progressPercentage.value = 0
  allOrderData.value = []
  fetchCurrentPage.value = 1
  fetchTotalItems.value = 0
  currentBatchId.value = `ZEBRA_${Date.now()}`

  const [startDate = '', endDate = ''] = dateRange.value || []
  await fetchPageData(startDate, endDate)
}

async function fetchPageData(startDate: string, endDate: string) {
  if (!isFetching.value) return

  try {
    const response = await zebraApi.getOrders({
      accountId: Number(accountId.value) || undefined,
      startDate,
      endDate,
      page: fetchCurrentPage.value,
      pageSize: 50,
      shopIds: selectedShops.value.join(','),
      batchId: currentBatchId.value
    }, abortController?.signal)

    const data = (response as any)?.data || response
    const orders = data.orders || []
    allOrderData.value = [...allOrderData.value, ...orders]

    fetchTotalPages.value = data.totalPages || 0
    fetchTotalItems.value = data.total || 0

    if (fetchCurrentPage.value < fetchTotalPages.value && isFetching.value) {
      progressPercentage.value = Math.round((fetchCurrentPage.value / fetchTotalPages.value) * 100)
      fetchCurrentPage.value++
      setTimeout(() => fetchPageData(startDate, endDate), 200)
    } else {
      progressPercentage.value = 100
      finishFetching()
    }
  } catch (e: any) {
    if (e.name !== 'AbortError') {
      console.error('获取订单数据失败:', e)
    }
    finishFetching()
  }
}

function finishFetching() {
  isFetching.value = false
  loading.value = false
  abortController = null
  // 确保进度条完全填满
  progressPercentage.value = 100
  currentPage.value = 1
  // 进度条保留显示，不自动隐藏
}

function stopFetch() {
  abortController?.abort()
  abortController = null
  isFetching.value = false
  loading.value = false
  // 进度条保留显示，不自动隐藏
}


function showMessage(message: string, type: 'info' | 'success' | 'warning' | 'error' = 'info') {
  ElMessage({ message, type })
}

async function exportToExcel() {
  if (!allOrderData.value.length) {
    showMessage('没有数据可供导出', 'warning')
    return
  }

  exportLoading.value = true

  try {
    const ExcelJS = (await import('exceljs')).default
    const workbook = new ExcelJS.Workbook()
    const worksheet = workbook.addWorksheet('斑马订单数据')

    worksheet.columns = [
      { header: '下单时间', key: 'orderedAt', width: 15 },
      { header: '商品图片', key: 'image', width: 15 },
      { header: '商品名称', key: 'productTitle', width: 25 },
      { header: '乐天订单号', key: 'shopOrderNumber', width: 20 },
      { header: '下单距今', key: 'timeSinceOrder', width: 12 },
      { header: '订单金额/日元', key: 'priceJpy', width: 15 },
      { header: '数量', key: 'productQuantity', width: 8 },
      { header: '税费/日元', key: 'shippingFeeJpy', width: 12 },
      { header: '回款抽点rmb', key: 'serviceFee', width: 15 },
      { header: '商品番号', key: 'productNumber', width: 15 },
      { header: '1688订单号', key: 'poNumber', width: 15 },
      { header: '采购金额/rmb', key: 'shippingFeeCny', width: 15 },
      { header: '国际运费/rmb', key: 'internationalShippingFee', width: 15 },
      { header: '国内物流', key: 'poLogisticsCompany', width: 12 },
      { header: '国内单号', key: 'poTrackingNumber', width: 15 },
      { header: '日本单号', key: 'internationalTrackingNumber', width: 15 },
      { header: '地址状态', key: 'trackInfo', width: 12 }
    ]

    const imageUrls = allOrderData.value.map(order => order.productImage || '')
    const imageBase64s = await batchConvertImages(imageUrls, 80)

    for (let i = 0; i < allOrderData.value.length; i++) {
      const order = allOrderData.value[i]
      const base64Image = imageBase64s[i]

      const row = worksheet.addRow({
        orderedAt: order.orderedAt || '',
        image: base64Image ? '图片' : '无图片',
        productTitle: order.productTitle || '',
        shopOrderNumber: order.shopOrderNumber || '',
        timeSinceOrder: order.timeSinceOrder || '',
        priceJpy: formatJpy(order.priceJpy),
        productQuantity: order.productQuantity || 0,
        shippingFeeJpy: formatJpy(order.shippingFeeJpy),
        serviceFee: order.serviceFee || '',
        productNumber: order.productNumber || '',
        poNumber: order.poNumber || '',
        shippingFeeCny: formatCny(order.shippingFeeCny),
        internationalShippingFee: order.internationalShippingFee || '',
        poLogisticsCompany: order.poLogisticsCompany || '',
        poTrackingNumber: order.poTrackingNumber || '',
        internationalTrackingNumber: order.internationalTrackingNumber || '',
        trackInfo: order.trackInfo || ''
      })

      if (base64Image) {
        const base64Data = base64Image.split(',')[1]
        if (base64Data) {
          const imageId = workbook.addImage({
            base64: base64Data,
            extension: 'jpeg',
          })

          worksheet.addImage(imageId, {
            tl: { col: 1, row: row.number - 1 },
            ext: { width: 60, height: 60 }
          })

          row.height = 50
        }
      }
    }

    const buffer = await workbook.xlsx.writeBuffer()
    const blob = new Blob([buffer], {
      type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
    })
    const fileName = `斑马订单数据_${new Date().toISOString().slice(0, 10)}.xlsx`

    const success = await handlePlatformFileExport('zebra', blob, fileName)

    if (success) {
      showMessage('Excel文件导出成功！', 'success')
    }
  } catch (error) {
    showMessage('导出失败', 'error')
  } finally {
    exportLoading.value = false
  }
}

onMounted(async () => {
  await loadAccounts()
  try {
    const latest = await zebraApi.getLatestOrders()
    const data = (latest as any)?.data || latest
    allOrderData.value = data?.orders || []
  } catch {}
})

// 账号对话框
const accountDialogVisible = ref(false)
const accountForm = ref<BanmaAccount>({ isDefault: 0, status: 1 })
const isEditMode = ref(false)
const formUsername = ref('')
const formPassword = ref('')
const rememberPwd = ref(true)
const managerVisible = ref(false)
const accountManagerRef = ref()

function openAddAccount() {
  isEditMode.value = false
  accountForm.value = { name: '', username: '', isDefault: 0, status: 1 }
  formUsername.value = ''
  formPassword.value = ''
  accountDialogVisible.value = true
}

function openManageAccount() {
  const cur = accounts.value.find(a => a.id === accountId.value)
  if (!cur) return
  isEditMode.value = true
  accountForm.value = { ...cur }
  formUsername.value = cur.username || ''
  formPassword.value = localStorage.getItem(`banma:pwd:${cur.username || ''}`) || ''
  accountDialogVisible.value = true
}

async function submitAccount() {
  if (!formUsername.value) { ElMessage({ message: '请输入账号', type: 'warning' }); return }
  const payload: BanmaAccount = {
    id: accountForm.value.id,
    name: accountForm.value.name || formUsername.value,
    username: formUsername.value,
    password: formPassword.value || '',
    isDefault: accountForm.value.isDefault || 0,
    status: accountForm.value.status || 1,
  }
  try {
    const username = getUsernameFromToken()
    const res = await zebraApi.saveAccount(payload, username)
    const id = (res as any)?.data?.id || (res as any)?.id
    if (!id) throw new Error((res as any)?.msg || '保存失败')
    accountDialogVisible.value = false
    await loadAccounts()
    if (id) accountId.value = id
    if (managerVisible.value && accountManagerRef.value?.load) {
      accountManagerRef.value.load()
    }
  } catch (e: any) {
    ElMessage({ message: e?.message || '账号或密码错误，无法获取Token', type: 'error' })
  }
}

async function removeCurrentAccount() {
  if (!isEditMode.value || !accountForm.value.id) return
  try {
    await ElMessageBox.confirm('确认删除该账号？', '提示', { type: 'warning' })
  } catch { return }
  await zebraApi.removeAccount(accountForm.value.id)
  accountDialogVisible.value = false
  await loadAccounts()
}
</script>
<template>
  <div class="zebra-root">
    <div class="layout">
      <aside class="aside">
        <div class="aside-header">
          <span>操作流程</span>
        </div>
        <div class="aside-steps">
          <section class="step step-accounts">
            <div class="step-index">1</div>
            <div class="step-body">
              <div class="step-title">需要查询的账号</div>
              <div class="tip">请选择需要查询数据的账号，如未添加账号，请点击“添加账号”。</div>
              <template v-if="accounts.length">
                <el-scrollbar :class="['account-list', { 'scroll-limit': accounts.length > 3 }]">
              <div>
                <div
                  v-for="a in accounts"
                  :key="a.id"
                  :class="['acct-item', { selected: accountId === a.id }]"
                  @click="selectAccount(Number(a.id))"
                >
                  <span class="acct-row">
                    <span :class="['status-dot', a.status === 1 ? 'on' : 'off']"></span>
                    <img class="avatar" src="/image/img_v3_02qd_052605f0-4be3-44db-9691-35ee5ff6201g.jpg" alt="avatar" />
                    <span class="acct-text">{{ a.name || a.username }}</span>
                    <span v-if="accountId === a.id" class="acct-check">✔️</span>
                  </span>
                </div>
              </div>
                </el-scrollbar>
              </template>
              <template v-else>
                <div class="placeholder-box">
                  <img class="placeholder-img" src="/icon/image.png" alt="add-account" />
                  <div class="placeholder-tip">请添加 斑马ERP 账号</div>
                </div>
              </template>
              <div class="step-actions btn-row sticky-actions">
                <el-button size="small" class="w50" @click="openAddAccount">添加账号</el-button>
                <el-button size="small" class="w50 btn-blue" @click="managerVisible = true">账号管理</el-button>
              </div>
            </div>
          </section>

          <section class="step">
            <div class="step-index">2</div>
            <div class="step-body">
              <div class="step-title">需要查询的日期</div>
              <div class="tip">请选择查询数据的日期范围。</div>
              <el-select v-model="selectedShops" multiple placeholder="选择店铺" :disabled="loading || !accounts.length" size="small" style="width: 100%">
                <el-option v-for="shop in shopList" :key="shop.id" :label="shop.shopName" :value="shop.id" />
          </el-select>
              <div style="height: 8px"></div>
              <el-date-picker v-model="dateRange" type="daterange" range-separator="至" start-placeholder="开始日期" end-placeholder="结束日期" :disabled="loading || !accounts.length" size="small" style="width: 100%" />
            </div>
          </section>

          <section class="step">
            <div class="step-index">3</div>
            <div class="step-body">
              <div class="step-title">获取数据</div>
              <div class="tip">点击下方按钮，开始查询订单数据。</div>
              <div class="btn-col">
                <el-button size="small" class="w100 btn-blue" :disabled="loading || !accounts.length" @click="fetchData">{{ loading ? '处理中...' : '获取数据' }}</el-button>
                <el-button size="small" :disabled="!loading" @click="stopFetch" class="w100">停止获取</el-button>
              </div>
            </div>
          </section>

          <section class="step">
            <div class="step-index">4</div>
            <div class="step-body">
              <div class="step-title">导出数据</div>
              <div class="tip">点击下方按钮导出所有订单数据到 Excel 文件</div>
              <div class="btn-col">
                <el-button size="small" type="success" :disabled="exportLoading || !allOrderData.length" :loading="exportLoading" @click="exportToExcel" class="w100">{{ exportLoading ? '导出中...' : '导出数据' }}</el-button>
                <!-- 导出进度条 -->
              </div>
          </div>
          </section>
        </div>
      </aside>

      <div class="content">
        <!-- 数据表格（无数据时也显示表头） -->
        <div class="table-section">
          <div v-if="showProgress" class="progress-section">
            <div class="progress-box">
              <div class="progress-container">
                <div class="progress-bar">
                  <div class="progress-fill" :style="{ width: progressPercentage + '%' }"></div>
                </div>
                <div class="progress-text">{{ progressPercentage }}%</div>
              </div>
            </div>
          </div>
          <div class="table-wrapper">
            <table class="table">
              <thead>
                <tr>
                  <th>下单时间</th>
                  <th>商品图片</th>
                  <th>商品名称</th>
                  <th>乐天订单号</th>
                  <th>下单距今</th>
                  <th>订单金额/日元</th>
                  <th>数量</th>
                  <th>税费/日元</th>
                  <th>回款抽点rmb</th>
                  <th>商品番号</th>
                  <th>1688订单号</th>
                  <th>采购金额/rmb</th>
                  <th>国际运费/rmb</th>
                  <th>国内物流</th>
                  <th>国内单号</th>
                  <th>日本单号</th>
                  <th>地址状态</th>
                </tr>
              </thead>
              <tbody>
                <tr v-for="row in paginatedData" :key="row.shopOrderNumber + (row.productNumber || '')">
                  <td>{{ row.orderedAt || '-' }}</td>
                  <td>
                    <div class="image-container" v-if="row.productImage">
                      <el-image :src="row.productImage" class="thumb" fit="contain" :preview-src-list="[row.productImage]" />
                    </div>
                    <span v-else>无图片</span>
                  </td>
                  <td class="truncate" :title="row.productTitle">{{ row.productTitle }}</td>
                  <td class="truncate" :title="row.shopOrderNumber">{{ row.shopOrderNumber }}</td>
                  <td>{{ row.timeSinceOrder || '-' }}</td>
                  <td><span class="price-tag">{{ formatJpy(row.priceJpy) }}</span></td>
                  <td>{{ row.productQuantity || 0 }}</td>
                  <td><span class="fee-tag">{{ formatJpy(row.shippingFeeJpy) }}</span></td>
                  <td>{{ row.serviceFee || '-' }}</td>
                  <td class="truncate" :title="row.productNumber">{{ row.productNumber }}</td>
                  <td class="truncate" :title="row.poNumber">{{ row.poNumber }}</td>
                  <td><span class="fee-tag">{{ formatCny(row.shippingFeeCny) }}</span></td>
                  <td>{{ row.internationalShippingFee || '-' }}</td>
                  <td>{{ row.poLogisticsCompany || '-' }}</td>
                  <td class="truncate" :title="row.poTrackingNumber">{{ row.poTrackingNumber }}</td>
                  <td class="truncate" :title="row.internationalTrackingNumber">{{ row.internationalTrackingNumber }}</td>
                  <td>
                    <span v-if="row.trackInfo" class="tag">{{ row.trackInfo }}</span>
                    <span v-else>暂无</span>
                  </td>
                </tr>
              </tbody>
            </table>
          </div>

          <div v-if="paginatedData.length === 0" class="empty-abs">
            <div v-if="loading" class="empty-container">
              <div class="spinner">⟳</div>
              <div>加载中...</div>
            </div>
            <div v-else class="empty-container">
              <div class="empty-icon" style="font-size:48px;">📄</div>
              <div class="empty-text">暂无数据，请获取订单</div>
            </div>
          </div>
        </div>

        <!-- 底部区域：分页器 -->
        <div class="pagination-fixed">
          <el-pagination
            background
            :current-page="currentPage"
            :page-sizes="[15,30,50,100]"
            :page-size="pageSize"
            layout="total, sizes, prev, pager, next, jumper"
            :total="allOrderData.length"
            @size-change="handleSizeChange"
            @current-change="handleCurrentChange"
          />
        </div>
      </div>
    </div>

    <!-- 账号新增/编辑对话框 -->
    <el-dialog v-model="accountDialogVisible" width="420px" class="add-account-dialog">
      <template #header>
        <div class="aad-header">
          <img class="aad-icon" src="/icon/image.png" alt="logo" />
          <div class="aad-title">添加账号</div>
        </div>
      </template>
      <div class="aad-row">
        <el-input v-model="formUsername" placeholder="请输入账号" />
      </div>
      <div class="aad-row">
        <el-input v-model="formPassword" placeholder="请输入密码" type="password" show-password />
      </div>
      <div class="aad-row aad-opts">
        <el-checkbox v-model="rememberPwd">保存密码</el-checkbox>
      </div>
      <template #footer>
        <el-button type="primary" class="btn-blue" style="width: 100%" @click="submitAccount">登录</el-button>
      </template>
    </el-dialog>

    <!-- 试用期过期弹框 -->
    <TrialExpiredDialog v-model="showTrialExpiredDialog" :expired-type="trialExpiredType" />

    <AccountManager ref="accountManagerRef" v-model="managerVisible" platform="zebra" @add="openAddAccount" @refresh="loadAccounts" />
  </div>
</template>

<script lang="ts">
export default {
  name: 'ZebraDashboard',
}
</script>

<style scoped>
.zebra-root { position: absolute; inset: 0; background: #f5f5f5; padding: 12px; box-sizing: border-box; }
.layout { background: #fff; border-radius: 4px; padding: 12px; box-shadow: 0 2px 4px rgba(0,0,0,0.05); height: 100%; display: grid; grid-template-columns: 220px 1fr; gap: 12px; }
.aside { border: 1px solid #ebeef5; border-radius: 4px; padding: 10px; display: flex; flex-direction: column; transition: width 0.2s ease; }
.aside.collapsed { width: 56px; overflow: hidden; }
.aside-header { display: flex; justify-content: flex-start; align-items: center; font-weight: 600; color: #606266; margin-bottom: 8px; }
.aside-steps { position: relative; }
.step { display: grid; grid-template-columns: 22px 1fr; gap: 10px; position: relative; padding: 8px 0; }
.step + .step { border-top: 1px dashed #ebeef5; }
.step-index { width: 22px; height: 22px; background: #1677FF; color: #fff; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-size: 12px; font-weight: 600; margin-top: 2px; }
.step-body { min-width: 0; text-align: left; }
.step-title { font-size: 13px; color: #606266; margin-bottom: 6px; font-weight: 600; text-align: left; }
.aside-steps:before { content: ''; position: absolute; left: 11px; top: 20px; bottom: 0; width: 1px; background: rgba(229, 231, 235, 0.6); }
.account-list {height: auto; }
.step-actions { margin-top: 8px; display: flex; gap: 8px; }
.step-accounts { position: relative; }
.sticky-actions { position: sticky; bottom: 0; background: #fafafa; padding-top: 8px; }
.scroll-limit { max-height: 160px; }
.btn-row { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
.btn-col { display: flex; flex-direction: column; gap: 6px; }
.w50 { width: 48%; }
.w100 { width: 100%; }
.placeholder-box { display:flex; align-items:center; justify-content:center; flex-direction:column; height: 140px; background: #fff; border: 1px solid #ebeef5; border-radius: 4px; }
.placeholder-img { width: 120px; opacity: 0.9; }
.placeholder-tip { margin-top: 6px; font-size: 12px; color: #a8abb2; }
.aside :deep(.el-date-editor) { width: 100%; }
.aside :deep(.el-range-editor.el-input__wrapper) { width: 100%; box-sizing: border-box; }
.aside :deep(.el-input),
.aside :deep(.el-input__wrapper),
.aside :deep(.el-select) { width: 100%; box-sizing: border-box; }
.aside :deep(.el-button + .el-button) { margin-left: 0 !important; }
.btn-row :deep(.el-button) { width: 100%; }
.btn-col :deep(.el-button) { width: 100%; }
.btn-blue { background: #1677FF; border-color: #1677FF; color: #fff; }
.btn-blue:disabled { background: #a6c8ff; border-color: #a6c8ff; color: #fff; }
.tip { color: #909399; font-size: 12px; margin-bottom: 8px; text-align: left; }
.avatar { width: 22px; height: 22px; border-radius: 50%; margin-right: 6px; vertical-align: -2px; }
.acct-text { vertical-align: middle; }
.acct-row { display: grid; grid-template-columns: 8px 18px 1fr auto; align-items: center; gap: 6px; width: 100%; }
.acct-text { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; font-size: 12px; }
.status-dot { width: 6px; height: 6px; border-radius: 50%; display: inline-block; }
.status-dot.on { background: #22c55e; }
.status-dot.off { background: #f87171; }
.acct-item { padding: 6px 8px; border-radius: 8px; cursor: pointer; }
.acct-item.selected { background: #eef5ff; box-shadow: inset 0 0 0 1px #d6e4ff; }
.acct-check { display: inline-flex; align-items: center; justify-content: center; width: 18px; height: 18px; border-radius: 50%; background: transparent; color: #111; font-size: 14px; }
.account-list::-webkit-scrollbar { width: 0; height: 0; }
.add-account-dialog .aad-header { display:flex; flex-direction: column; align-items:center; gap:8px; padding-top: 8px; width: 100%; }
.add-account-dialog .aad-icon { width: 120px; height: auto; }
.add-account-dialog .aad-title { font-weight: 600; font-size: 18px; text-align: center; }
.add-account-dialog .aad-row { margin-top: 12px; }
.add-account-dialog .aad-opts { display:flex; align-items:center; }

/* 居中 header，避免右上角关闭按钮影响视觉中心 */
:deep(.add-account-dialog .el-dialog__header) { text-align: center; padding-right: 0; display: block; }
.content { display: grid; grid-template-rows: 1fr auto; min-height: 0; }
.table-section { min-height: 0; overflow: hidden; position: relative; background: #fff; border: 1px solid #ebeef5; border-radius: 4px; display: flex; flex-direction: column; }
.table-wrapper { flex: 1; overflow: auto; overflow-x: auto; }
.table-wrapper { scrollbar-width: thin; scrollbar-color: #c0c4cc transparent; }
.table-wrapper::-webkit-scrollbar { width: 6px; height: 6px; }
.table-wrapper::-webkit-scrollbar-track { background: transparent; }
.table-wrapper::-webkit-scrollbar-thumb { background: #c0c4cc; border-radius: 3px; }
.table-wrapper:hover::-webkit-scrollbar-thumb { background: #a8abb2; }
.table { width: max-content; min-width: 100%; border-collapse: collapse; font-size: 13px; }
.table th { background: #f5f7fa; color: #909399; font-weight: 600; padding: 12px 8px; border-bottom: 2px solid #ebeef5; text-align: left; white-space: nowrap; }
.table td { padding: 10px 8px; border-bottom: 1px solid #f0f0f0; vertical-align: middle; }
.table tbody tr:hover { background: #f9f9f9; }
.truncate { max-width: 180px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.image-container { display: flex; justify-content: center; align-items: center; width: 28px; height: 24px; margin: 0 auto; background: #f8f9fa; border-radius: 2px; }
.thumb { width: 22px; height: 22px; object-fit: contain; border-radius: 2px; }
.price-tag { color: #e6a23c; font-weight: bold; }
.fee-tag { color: #909399; font-weight: 500; }
.table-loading { position: absolute; inset: 0; background: rgba(255, 255, 255, 0.95); display: flex; flex-direction: column; justify-content: center; align-items: center; font-size: 14px; color: #606266; }
.spinner { font-size: 24px; animation: spin 1s linear infinite; margin-bottom: 8px; }
@keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } }
.pagination-fixed { position: sticky; bottom: 0; z-index: 2; padding: 8px 12px; background: #f9f9f9; border-radius: 4px; display: flex; justify-content: center; border-top: 1px solid #ebeef5; margin-top: 8px; }
.tag { display: inline-block; padding: 0 6px; margin-left: 6px; font-size: 12px; background: #ecf5ff; color: #409EFF; border-radius: 3px; }
.empty-abs { position: absolute; left: 0; right: 0; top: 48px; bottom: 0; display: flex; align-items: center; justify-content: center; pointer-events: none; }
.progress-section { margin: 0px 12px 0px 12px; }
.progress-box { padding: 4px 0; }
.progress-container { display: flex; align-items: center; gap: 8px; }
.progress-bar { flex: 1; height: 6px; background: #e3eeff; border-radius: 999px; overflow: hidden; }
.progress-fill { height: 100%; background: #1677FF; border-radius: 999px; transition: width 0.3s ease; }
.progress-text { font-size: 13px; color: #1677FF; font-weight: 500; min-width: 44px; text-align: right; }
.export-progress { display: flex; align-items: center; gap: 8px; margin-top: 6px; padding: 0 4px; }
.export-progress-bar { flex: 1; height: 4px; background: #e3eeff; border-radius: 2px; overflow: hidden; }
.export-progress-fill { height: 100%; background: #67c23a; border-radius: 2px; transition: width 0.3s ease; }
.export-progress-text { font-size: 11px; color: #67c23a; font-weight: 500; min-width: 32px; text-align: right; }
</style>


