<template>
  <div class="converter-container">
    <div class="converter-header">
      <div class="header-content">
        <div class="header-icon">
          <i class="fas fa-exchange-alt"></i>
        </div>
        <div class="header-text">
          <h2>格式转换</h2>
          <p v-if="currentMode === 'stp-to-gltf'">将STP文件转换为GLTF格式，用于Web展示</p>
          <p v-else>将IFC建筑信息模型转换为STP通用三维格式</p>
        </div>
      </div>
    </div>

    <div class="converter-content">
      <div class="converter-tabs">
        <div 
          class="tab-item" 
          :class="{ active: currentMode === 'stp-to-gltf' }"
          @click="currentMode = 'stp-to-gltf'"
        >
          <i class="fas fa-cube"></i>
          <span>STP 转 GLTF</span>
        </div>
        <div 
          class="tab-item" 
          :class="{ active: currentMode === 'ifc-to-stp' }"
          @click="currentMode = 'ifc-to-stp'"
        >
          <i class="fas fa-building"></i>
          <span>IFC 转 STP</span>
        </div>
        <div 
          class="tab-item" 
          :class="{ active: currentMode === 'ifc-to-stp-batch' }"
          @click="currentMode = 'ifc-to-stp-batch'"
        >
          <i class="fas fa-layer-group"></i>
          <span>IFC 批量转 STP</span>
        </div>
      </div>

      <div class="conversion-flow">
        <div class="flow-step">
          <div class="step-icon">
            <i class="fas fa-file-upload"></i>
          </div>
          <h3 v-if="currentMode === 'stp-to-gltf'">1. 选择STP文件</h3>
          <h3 v-else-if="currentMode === 'ifc-to-stp'">1. 选择IFC文件</h3>
          <h3 v-else>1. 选择多个IFC</h3>
          <p v-if="currentMode === 'stp-to-gltf'">选择要转换的STEP格式文件</p>
          <p v-else-if="currentMode === 'ifc-to-stp'">选择要转换的IFC格式文件</p>
          <p v-else>输入多个要转换的IFC格式文件</p>
        </div>
        <div class="flow-arrow">
          <i class="fas fa-arrow-right"></i>
        </div>
        <div class="flow-step">
          <div class="step-icon">
            <i class="fas fa-cogs"></i>
          </div>
          <h3>2. 转换处理</h3>
          <p v-if="currentMode === 'stp-to-gltf'">自动处理几何数据和材质</p>
          <p v-else-if="currentMode === 'ifc-to-stp'">解析BIM数据并重建BREP几何</p>
          <p v-else>批量解析BIM数据并重建BREP几何</p>
        </div>
        <div class="flow-arrow">
          <i class="fas fa-arrow-right"></i>
        </div>
        <div class="flow-step">
          <div class="step-icon">
            <i class="fas fa-download"></i>
          </div>
          <h3 v-if="currentMode === 'stp-to-gltf'">3. 下载GLTF</h3>
          <h3 v-else-if="currentMode === 'ifc-to-stp'">3. 获取STP</h3>
          <h3 v-else>3. 获取多个STP</h3>
          <p v-if="currentMode === 'stp-to-gltf'">获取Web优化的3D模型文件</p>
          <p v-else-if="currentMode === 'ifc-to-stp'">获取通用的STEP实体模型文件</p>
          <p v-else>获取批量的STEP实体模型文件</p>
        </div>
      </div>

      <div class="converter-panel">
        <div class="upload-section" v-if="currentMode === 'stp-to-gltf'">
          <div 
            class="upload-area" 
            id="file-upload-area"
            @dragover.prevent
            @drop.prevent="handleStpDrop"
          >
            <i class="fas" :class="stpSelectedFile ? 'fa-file-alt' : 'fa-cloud-upload-alt'"></i>
            <h4 v-if="!stpSelectedFile">拖拽STP文件到此处</h4>
            <h4 v-else>已选择文件: {{ stpSelectedFile.name }}</h4>
            <p v-if="!stpSelectedFile">或点击选择文件</p>
            <p v-else>文件大小: {{ (stpSelectedFile.size / 1024 / 1024).toFixed(2) }} MB</p>
            <input type="file" id="stp-file-input" accept=".stp,.step" class="hidden-input" @change="handleStpSelect">
            <button class="upload-btn" @click="selectFile">
              <i class="fas" :class="stpSelectedFile ? 'fa-sync-alt' : 'fa-folder-open'"></i>
              {{ stpSelectedFile ? '重新选择' : '选择文件' }}
            </button>
          </div>
        </div>

        <div class="path-input-section" v-else-if="currentMode === 'ifc-to-stp'">
          <div class="input-group">
            <label><i class="fas fa-file-import"></i> 输入IFC绝对路径：</label>
            <input type="text" v-model="ifcPath" placeholder="例如：D:/CAD/input.ifc" class="path-input">
          </div>
          <div class="input-group">
            <label><i class="fas fa-file-export"></i> 输出STP绝对路径：</label>
            <input type="text" v-model="stpPath" placeholder="例如：D:/CAD/output.stp" class="path-input">
          </div>
        </div>

        <div class="batch-input-section" v-else-if="currentMode === 'ifc-to-stp-batch'">
          <div class="batch-header">
            <h4><i class="fas fa-list-ol"></i> 批量转换列表</h4>
            <button class="add-batch-btn" @click="addBatchItem">
              <i class="fas fa-plus"></i> 添加项
            </button>
          </div>
          <div class="batch-list">
            <div class="batch-item" v-for="(item, index) in batchItems" :key="index">
              <div class="batch-index">{{ index + 1 }}</div>
              <div class="batch-inputs">
                <div class="batch-input-field">
                  <span class="batch-label">IFC 路径:</span>
                  <input type="text" v-model="item.ifcPath" placeholder="例如：D:/CAD/input1.ifc" class="path-input batch-input">
                </div>
                <div class="batch-input-field">
                  <span class="batch-label">STP 路径:</span>
                  <input type="text" v-model="item.stpPath" placeholder="例如：D:/CAD/output1.stp" class="path-input batch-input">
                </div>
              </div>
              <button class="remove-batch-btn" @click="removeBatchItem(index)" :disabled="batchItems.length <= 1" title="移除该项">
                <i class="fas fa-trash-alt"></i>
              </button>
            </div>
          </div>
        </div>

        <div class="conversion-options">
          <h4>转换选项</h4>
          <!-- STP to GLTF Options -->
          <div class="options-grid" v-if="currentMode === 'stp-to-gltf'">
            <div class="option-item">
              <label>输出质量:</label>
              <select id="gltf-quality">
                <option value="high">高质量</option>
                <option value="medium" selected>中等质量</option>
                <option value="low">低质量(适合Web)</option>
              </select>
            </div>
            <div class="option-item">
              <label>
                <input type="checkbox" id="optimize-size" checked>
                优化文件大小
              </label>
            </div>
            <div class="option-item">
              <label>
                <input type="checkbox" id="include-textures">
                包含纹理
              </label>
            </div>
            <div class="option-item">
              <label>
                <input type="checkbox" id="compress-gltf" checked>
                压缩GLTF
              </label>
            </div>
          </div>
          
          <!-- IFC to STP Options -->
          <div class="options-grid" v-else>
            <div class="option-item" v-if="currentMode === 'ifc-to-stp-batch'">
              <label>
                <input type="checkbox" v-model="continueOnError">
                出错时继续转换其余项
              </label>
            </div>
            <div class="option-item">
              <label>STEP版本:</label>
              <select id="stp-version">
                <option value="AP203">AP203</option>
                <option value="AP214" selected>AP214</option>
                <option value="AP242">AP242</option>
              </select>
            </div>
            <div class="option-item">
              <label>
                <input type="checkbox" id="ifc-heal" checked>
                自动修复几何缺陷
              </label>
            </div>
            <div class="option-item">
              <label>
                <input type="checkbox" id="ifc-colors" checked>
                导出颜色/材质信息
              </label>
            </div>
            <div class="option-item">
              <label>
                <input type="checkbox" id="ifc-props">
                包含属性数据
              </label>
            </div>
          </div>
        </div>

        <div class="conversion-actions">
          <button class="action-btn primary" :disabled="!canConvert || isConverting" @click="startConversion">
            <i :class="isConverting ? 'fas fa-spinner fa-spin' : 'fas fa-play'"></i>
            {{ isConverting ? '转换中...' : '开始转换' }}
          </button>
          <button class="action-btn secondary" @click="resetForm" :disabled="isConverting">
            <i class="fas fa-undo"></i>
            重置
          </button>
        </div>
      </div>

      <div class="conversion-progress" v-show="isConverting || convertResult">
        <div class="progress-content">
          <div class="progress-icon" :class="{ 'success-icon': convertResult === 'success', 'error-icon': convertResult === 'error' }">
            <i v-if="isConverting" class="fas fa-spinner fa-spin"></i>
            <i v-else-if="convertResult === 'success'" class="fas fa-check-circle"></i>
            <i v-else-if="convertResult === 'error'" class="fas fa-times-circle"></i>
          </div>
          <h4>{{ progressTitle }}</h4>
          <div class="progress-bar" v-if="isConverting">
            <div class="progress-fill" style="width: 100%; animation: pulse 1.5s infinite;"></div>
          </div>
          <p>{{ progressStatus }}</p>
        </div>
      </div>
    </div>
  </div>
</template>

<script setup>
import { ref, computed, onMounted, onUnmounted } from 'vue'
import websocketService from '@/services/websocketService'
import * as OV from 'online-3d-viewer'

const currentMode = ref('stp-to-gltf')

// STP to GLTF 状态
const stpSelectedFile = ref(null)

const handleStpDrop = (e) => {
  if (e.dataTransfer.files.length > 0) {
    const file = e.dataTransfer.files[0]
    if (file.name.toLowerCase().endsWith('.stp') || file.name.toLowerCase().endsWith('.step')) {
      stpSelectedFile.value = file
    }
  }
}

const handleStpSelect = (e) => {
  if (e.target.files.length > 0) {
    stpSelectedFile.value = e.target.files[0]
  }
}

// IFC to STP 转换相关状态
const ifcPath = ref('')
const stpPath = ref('')

// IFC 批量转 STP 状态
const batchItems = ref([
  { ifcPath: '', stpPath: '' },
  { ifcPath: '', stpPath: '' }
])
const continueOnError = ref(true)

const addBatchItem = () => {
  batchItems.value.push({ ifcPath: '', stpPath: '' })
}

const removeBatchItem = (index) => {
  if (batchItems.value.length > 1) {
    batchItems.value.splice(index, 1)
  }
}

const isConverting = ref(false)
const convertResult = ref('') // 'success', 'error', ''
const progressTitle = ref('准备转换...')
const progressStatus = ref('')

const canConvert = computed(() => {
  if (currentMode.value === 'ifc-to-stp') {
    return ifcPath.value.trim() !== '' && stpPath.value.trim() !== ''
  } else if (currentMode.value === 'ifc-to-stp-batch') {
    return batchItems.value.length > 0 && batchItems.value.every(item => item.ifcPath.trim() !== '' && item.stpPath.trim() !== '')
  } else if (currentMode.value === 'stp-to-gltf') {
    return stpSelectedFile.value !== null
  }
  return false
})

const selectFile = () => {
  document.getElementById('stp-file-input').click()
}

const resetForm = () => {
  ifcPath.value = ''
  stpPath.value = ''
  batchItems.value = [
    { ifcPath: '', stpPath: '' },
    { ifcPath: '', stpPath: '' }
  ]
  continueOnError.value = true
  
  stpSelectedFile.value = null
  const input = document.getElementById('stp-file-input')
  if (input) input.value = ''

  convertResult.value = ''
  progressTitle.value = '准备转换...'
  progressStatus.value = ''
}

const startConversion = () => {
  if (currentMode.value === 'stp-to-gltf') {
    isConverting.value = true
    convertResult.value = ''
    progressTitle.value = '格式转换中...'
    progressStatus.value = '正在解析STP几何信息 (由于是前端纯净方案，可能需要稍作等待)...'

    try {
      OV.Init3DViewerElements()
      const importer = new OV.Importer()
      importer.ImportFiles(
        OV.InputFilesFromFileObjects([stpSelectedFile.value]),
        new OV.ImportSettings(),
        {
          onLoadStart: () => {
            console.log('[STP->GLTF] onLoadStart')
            progressStatus.value = '开始读取文件...'
          },
          onImportStart: () => {
            console.log('[STP->GLTF] onImportStart')
            progressStatus.value = '文件读取完成，正在启动引擎重构模型网格...'
          },
          onFileListProgress: (current, total) => {
            console.log(`[STP->GLTF] onFileListProgress: ${current} / ${total}`)
          },
          onFileLoadProgress: (current, total) => {
            console.log(`[STP->GLTF] onFileLoadProgress: ${current} / ${total}`)
            // You can optionally show parsing/decoding progress percentages here
          },
          onImportSuccess: (importResult) => {
            console.log('[STP->GLTF] onImportSuccess', importResult)
            progressStatus.value = '构建模型成功，正在将其编码为 GLB 打包格式 (该步骤同样需要几分钟甚至更久，且浏览器可能会卡顿，请耐心等待)...'
            
            // Give UI a chance to update before heavy synchronous JS blocking
            setTimeout(() => {
              try {
                console.log('[STP->GLTF] Starting Export Process...')
                const model = importResult.model
                const exporter = new OV.Exporter()
                exporter.Export(
                  model,
                  new OV.ExporterSettings(),
                  OV.FileFormat.Binary,
                  'glb',
                  {
                    onSuccess: (exportedFiles) => {
                      console.log('[STP->GLTF] EXPORT onSuccess', exportedFiles)
                      try {
                    const glbFile = exportedFiles[0]
                    const blob = new Blob([glbFile.GetBufferContent()], { type: 'model/gltf-binary' })
                    const url = URL.createObjectURL(blob)
                    const a = document.createElement('a')
                    a.href = url
                    a.download = stpSelectedFile.value.name.replace(/\.[^/.]+$/, "") + ".glb"
                    document.body.appendChild(a)
                    a.click()
                    setTimeout(() => {
                      document.body.removeChild(a)
                      URL.revokeObjectURL(url)
                    }, 100)

                    isConverting.value = false
                    convertResult.value = 'success'
                    progressTitle.value = '转换完成!'
                    progressStatus.value = '浏览器已成功下载导出的 GLB 文件。'
                  } catch (err) {
                    isConverting.value = false
                    convertResult.value = 'error'
                    progressTitle.value = '导出失败'
                    progressStatus.value = err.message || '导出文件时发生错误'
                  }
                },
                onError: () => {
                  console.error('[STP->GLTF] EXPORT onError triggered by engine')
                  isConverting.value = false
                  convertResult.value = 'error'
                  progressTitle.value = '转换失败'
                  progressStatus.value = '编码为 GLB 格式时发生引擎错误'
                }
              }
            )
          } catch (exportException) {
            console.error('[STP->GLTF] Catch Export Exception:', exportException)
            isConverting.value = false
            convertResult.value = 'error'
            progressTitle.value = '导出异常'
            progressStatus.value = `导出模型异常: ${exportException.message || exportException}`
          }
        }, 100)
      },
      onImportError: (importError) => {
            console.error('[STP->GLTF] onImportError', importError)
            isConverting.value = false
            convertResult.value = 'error'
            progressTitle.value = '解析失败'
            
            let errMsg = '无法解析该STP文件'
            if (importError) {
              if (importError.code === OV.ImportErrorCode.NoImportableFile) errMsg = '找不到可导入的文件内容。'
              else if (importError.code === OV.ImportErrorCode.FailedToLoadFile) errMsg = '文件加载失败。'
              else if (importError.code === OV.ImportErrorCode.ImportFailed) errMsg = '模型解析失败，文件结构可能不完整或引擎异常崩溃 (建议检查浏览器 Console 面板错误信息)。'
              if (importError.message) errMsg += ` (${importError.message})`
            }
            
            progressStatus.value = errMsg
          }
        }
      )
    } catch (error) {
      console.error('[STP->GLTF] Catch Error:', error)
      isConverting.value = false
      convertResult.value = 'error'
      progressTitle.value = '转换异常'
      progressStatus.value = error.message || '前端转换出现异常'
    }
  } else if (currentMode.value === 'ifc-to-stp') {
    isConverting.value = true
    convertResult.value = ''
    progressTitle.value = '正在转换...'
    progressStatus.value = '已发送转换请求，后台处理中...'
    
    websocketService.send({
      type: "convert_ifc_to_stp",
      ifc_path: ifcPath.value.trim(),
      stp_path: stpPath.value.trim()
    })
  } else if (currentMode.value === 'ifc-to-stp-batch') {
    isConverting.value = true
    convertResult.value = ''
    progressTitle.value = '正在批量转换...'
    progressStatus.value = `已发送 ${batchItems.value.length} 个文件的请求，处理中...`
    
    websocketService.send({
      type: "convert_ifc_to_stp_batch",
      continue_on_error: continueOnError.value,
      items: batchItems.value.map(item => ({
        ifc_path: item.ifcPath.trim(),
        stp_path: item.stpPath.trim()
      }))
    })
  }
}

const handleWsMessage = (message) => {
  if (!isConverting.value) return
  
  // 成功看 msg.type === "info" 
  if (message.type === "info") {
    if (message.data?.items && Array.isArray(message.data.items)) {
      isConverting.value = false
      convertResult.value = 'success'
      progressTitle.value = '批量转换完成!'
      let successCount = message.data.items.filter(i => i.status === 'success' || i.success).length || batchItems.value.length
      progressStatus.value = `处理完成收工 (成功: ${successCount}项, 耗时: ${message.data.duration_ms || '?'}ms)`
    } else if (message.data?.stp_path) {
      isConverting.value = false
      convertResult.value = 'success'
      progressTitle.value = '转换成功!'
      progressStatus.value = `文件已保存至: ${message.data.stp_path} (耗时: ${message.data.duration_ms}ms)`
    } else {
      isConverting.value = false
      convertResult.value = 'success'
      progressTitle.value = '转换完成!'
      progressStatus.value = `处理完成收工 (耗时: ${message.data?.duration_ms || '?'}ms)`
    }
  } 
  // 失败看 msg.type === "error"
  else if (message.type === "error") {
    isConverting.value = false
    convertResult.value = 'error'
    progressTitle.value = '转换失败'
    progressStatus.value = message.message || '未知错误'
  }
}

onMounted(() => {
  websocketService.on('onMessage', handleWsMessage)
})

onUnmounted(() => {
  websocketService.off('onMessage', handleWsMessage)
})
</script>

<style scoped>
.converter-container {
  padding: 24px;
  max-width: 900px;
  margin: 0 auto;
}

.converter-header {
  margin-bottom: 32px;
}

.header-content {
  display: flex;
  align-items: center;
  gap: 20px;
}

.header-icon {
  width: 64px;
  height: 64px;
  background: var(--color-primary-gradient);
  border-radius: 12px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 28px;
  color: white;
  box-shadow: 0 4px 16px var(--color-primary-rgb);
}

.header-text h2 {
  color: var(--color-text-primary);
  margin: 0 0 8px 0;
  font-size: 32px;
  font-weight: 600;
}

.header-text p {
  color: var(--color-text-secondary);
  margin: 0;
  font-size: 16px;
}

.converter-content {
  background: var(--color-bg-secondary);
  border: 1px solid var(--color-border-primary);
  border-radius: 16px;
  padding: 32px;
}

.converter-tabs {
  display: flex;
  gap: 16px;
  margin-bottom: 32px;
  padding: 8px;
  background: var(--color-bg-tertiary);
  border-radius: 12px;
  border: 1px solid var(--color-border-primary);
}

.tab-item {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 12px 24px;
  border-radius: 8px;
  color: var(--color-text-secondary);
  font-weight: 500;
  cursor: pointer;
  transition: all 0.3s ease;
}

.tab-item:hover {
  background: var(--color-bg-hover);
  color: var(--color-text-primary);
}

.tab-item.active {
  background: var(--color-primary-gradient);
  color: white;
  box-shadow: 0 4px 12px var(--color-primary-rgb);
}

.conversion-flow {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 20px;
  margin-bottom: 40px;
  flex-wrap: wrap;
}

.flow-step {
  flex: 1;
  min-width: 200px;
  text-align: center;
  padding: 20px;
  background: var(--color-bg-tertiary);
  border-radius: 12px;
  border: 1px solid var(--color-border-primary);
}

.step-icon {
  width: 56px;
  height: 56px;
  background: var(--color-primary-gradient);
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 24px;
  color: white;
  margin: 0 auto 16px;
}

.flow-step h3 {
  color: var(--color-text-primary);
  margin: 0 0 8px 0;
  font-size: 18px;
  font-weight: 600;
}

.flow-step p {
  color: var(--color-text-secondary);
  margin: 0;
  font-size: 14px;
}

.flow-arrow {
  color: var(--color-text-tertiary);
  font-size: 20px;
  flex-shrink: 0;
}

@media (max-width: 768px) {
  .flow-arrow {
    transform: rotate(90deg);
  }
}

.converter-panel {
  display: grid;
  gap: 32px;
}

.upload-section {
  margin-bottom: 24px;
}

.upload-area {
  border: 2px dashed var(--color-border-primary);
  border-radius: 12px;
  padding: 48px 24px;
  text-align: center;
  background: var(--color-bg-tertiary);
  transition: all 0.3s ease;
  cursor: pointer;
}

.upload-area:hover {
  border-color: var(--color-primary);
  background: var(--color-bg-hover);
}

.upload-area i {
  font-size: 48px;
  color: var(--color-primary);
  margin-bottom: 16px;
}

.upload-area h4 {
  color: var(--color-text-primary);
  margin: 0 0 8px 0;
  font-size: 20px;
  font-weight: 600;
}

.upload-area p {
  color: var(--color-text-secondary);
  margin: 0 0 24px 0;
  font-size: 14px;
}

.upload-btn {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 12px 24px;
  background: var(--color-primary-gradient);
  border: none;
  border-radius: 8px;
  color: white;
  font-size: 14px;
  font-weight: 500;
  cursor: pointer;
  transition: all 0.3s ease;
}

.upload-btn:hover {
  background: var(--color-primary-gradient-hover);
  transform: translateY(-2px);
}

.conversion-options {
  background: var(--color-bg-tertiary);
  border-radius: 12px;
  padding: 24px;
  border: 1px solid var(--color-border-primary);
}

.conversion-options h4 {
  color: var(--color-text-primary);
  margin: 0 0 20px 0;
  font-size: 18px;
  font-weight: 600;
}

.options-grid {
  display: grid;
  gap: 16px;
}

.option-item {
  display: flex;
  align-items: center;
  gap: 12px;
}

.option-item label {
  color: var(--color-text-primary);
  font-size: 14px;
  font-weight: 500;
  cursor: pointer;
  display: flex;
  align-items: center;
  gap: 8px;
}

.option-item select {
  padding: 8px 12px;
  background: var(--color-bg-input);
  border: 1px solid var(--color-border-primary);
  border-radius: 6px;
  color: var(--color-text-primary);
  font-size: 14px;
  min-width: 160px;
}

.option-item select:focus {
  outline: none;
  border-color: var(--color-primary);
}

.option-item input[type="checkbox"] {
  width: 16px;
  height: 16px;
  accent-color: var(--color-primary);
}

.conversion-actions {
  display: flex;
  gap: 16px;
  justify-content: center;
  margin-top: 24px;
}

.action-btn {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 14px 28px;
  border: none;
  border-radius: 8px;
  font-size: 16px;
  font-weight: 500;
  cursor: pointer;
  transition: all 0.3s ease;
  min-width: 140px;
  justify-content: center;
}

.action-btn.primary {
  background: var(--color-success);
  color: white;
  box-shadow: 0 4px 16px var(--color-success-rgb-1);
}

.action-btn.primary:hover:not(:disabled) {
  background: var(--color-success-rgb-5);
  transform: translateY(-2px);
  box-shadow: 0 6px 20px var(--color-success-rgb-3);
}

.action-btn.primary:disabled {
  background: var(--color-text-tertiary);
  cursor: not-allowed;
  transform: none;
  box-shadow: none;
}

.action-btn.secondary {
  background: var(--color-bg-tertiary);
  border: 1px solid var(--color-border-primary);
  color: var(--color-text-secondary);
}

.action-btn.secondary:hover {
  background: var(--color-bg-hover);
  color: var(--color-text-primary);
}

.conversion-progress {
  background: var(--color-bg-tertiary);
  border: 1px solid var(--color-border-primary);
  border-radius: 12px;
  padding: 32px;
  text-align: center;
  margin-top: 24px;
}

.progress-content {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 16px;
}

.progress-icon i {
  font-size: 48px;
  color: var(--color-primary);
}

.progress-content h4 {
  color: var(--color-text-primary);
  margin: 0;
  font-size: 20px;
  font-weight: 600;
}

.progress-content p {
  color: var(--color-text-secondary);
  margin: 0;
  font-size: 14px;
}

.progress-bar {
  width: 100%;
  max-width: 300px;
  height: 8px;
  background: var(--color-bg-primary);
  border-radius: 4px;
  overflow: hidden;
  margin: 16px 0;
}

.progress-fill {
  height: 100%;
  background: var(--color-primary-gradient);
  border-radius: 4px;
  transition: width 0.3s ease;
  width: 0%;
}

/* 隐藏元素 */
.hidden-input {
  display: none;
}

/* 批量输入样式 */
.batch-input-section {
  display: flex;
  flex-direction: column;
  gap: 16px;
  background: var(--color-bg-tertiary);
  padding: 24px;
  border-radius: 12px;
  border: 1px solid var(--color-border-primary);
  margin-bottom: 24px;
}

.batch-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  border-bottom: 1px solid var(--color-border-primary);
  padding-bottom: 12px;
}

.batch-header h4 {
  color: var(--color-text-primary);
  margin: 0;
  font-size: 16px;
  display: flex;
  align-items: center;
  gap: 8px;
}

.batch-header h4 i {
  color: var(--color-primary);
}

.add-batch-btn {
  background: var(--color-bg-secondary);
  border: 1px dashed var(--color-primary);
  color: var(--color-primary);
  padding: 6px 12px;
  border-radius: 6px;
  cursor: pointer;
  font-size: 13px;
  transition: all 0.3s;
}

.add-batch-btn:hover {
  background: var(--color-primary-rgb-2);
}

.batch-list {
  display: flex;
  flex-direction: column;
  gap: 12px;
  max-height: 300px;
  overflow-y: auto;
  padding-right: 8px;
}

.batch-list::-webkit-scrollbar {
  width: 6px;
}
.batch-list::-webkit-scrollbar-thumb {
  background: var(--color-border-primary);
  border-radius: 3px;
}

.batch-item {
  display: flex;
  align-items: center;
  gap: 12px;
  background: var(--color-bg-secondary);
  padding: 12px;
  border-radius: 8px;
  border: 1px solid var(--color-border-primary);
}

.batch-index {
  width: 24px;
  height: 24px;
  border-radius: 50%;
  background: var(--color-primary-rgb-2);
  color: var(--color-primary);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 12px;
  font-weight: bold;
  flex-shrink: 0;
}

.batch-inputs {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.batch-input-field {
  display: flex;
  align-items: center;
  gap: 8px;
}

.batch-label {
  width: 70px;
  color: var(--color-text-secondary);
  font-size: 13px;
  text-align: right;
  flex-shrink: 0;
}

.batch-input {
  flex: 1;
  padding: 8px 12px;
  font-size: 13px;
}

.remove-batch-btn {
  background: transparent;
  border: none;
  color: var(--color-danger);
  width: 32px;
  height: 32px;
  border-radius: 6px;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: all 0.3s;
}

.remove-batch-btn:hover:not(:disabled) {
  background: rgba(245, 108, 108, 0.1);
}

.remove-batch-btn:disabled {
  color: var(--color-text-tertiary);
  cursor: not-allowed;
}

/* 路径输入样式 */
.path-input-section {
  display: flex;
  flex-direction: column;
  gap: 20px;
  background: var(--color-bg-tertiary);
  padding: 24px;
  border-radius: 12px;
  border: 1px solid var(--color-border-primary);
  margin-bottom: 24px;
}

.input-group {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.input-group label {
  color: var(--color-text-primary);
  font-size: 15px;
  font-weight: 600;
  display: flex;
  align-items: center;
  gap: 8px;
}

.input-group label i {
  color: var(--color-primary);
}

.path-input {
  padding: 12px 16px;
  border: 1px solid var(--color-border-primary);
  border-radius: 8px;
  background: var(--color-bg-input);
  color: var(--color-text-primary);
  font-size: 14px;
  transition: all 0.3s ease;
}

.path-input:focus {
  outline: none;
  border-color: var(--color-primary);
  box-shadow: 0 0 0 2px var(--color-primary-rgb-2);
}

.progress-icon.success-icon i {
  color: var(--color-success);
}

.progress-icon.error-icon i {
  color: var(--color-danger);
}

@keyframes pulse {
  0% { opacity: 0.6; }
  50% { opacity: 1; }
  100% { opacity: 0.6; }
}

/* 使用全局CSS变量 - 已在theme.css中统一定义 */
</style>