<template>
  <div class="file-selector" ref="selectorRef">
    <div class="selector-trigger" :class="{ active: dropdownVisible, filled: !!modelValue }" @click="toggleDropdown">
      <FileTextOutlined class="trigger-icon" />
      <span class="trigger-text">{{ displayText || placeholder }}</span>
      <CloseOutlined v-if="modelValue && allowClear" class="trigger-clear" @click.stop="handleClear" />
      <DownOutlined class="trigger-arrow" :class="{ rotated: dropdownVisible }" />
    </div>

    <Teleport to="body">
      <Transition name="dropdown-fade">
        <div v-show="dropdownVisible" class="selector-overlay" @click.self="closeDropdown">
          <div class="selector-panel" :style="panelStyle">
            <!-- 顶部搜索栏 -->
            <div class="panel-header" @click.stop>
              <div class="header-breadcrumb" v-if="viewMode === 'versions'">
                <LeftOutlined class="breadcrumb-back" @click="backToFileList" />
                <span class="breadcrumb-label">{{ selectedGroup?.title }}</span>
                <span class="breadcrumb-count">{{ selectedGroup?.versions?.length }} 个版本</span>
              </div>
              <div v-else class="header-search">
                <SearchOutlined class="search-icon" />
                <input
                  ref="searchInputRef"
                  v-model="searchKeyword"
                  class="search-input"
                  placeholder="输入关键词搜索制度文件..."
                  @input="onSearchInput"
                  @keydown.escape="closeDropdown"
                />
              </div>
            </div>

            <!-- ===== 增强型上传区域（可配置隐藏） ===== -->
            <div class="enhanced-upload-section" v-if="showDropZone" @click.stop>
              <DropZone
                @files-selected="handleFilesSelected"
                @error="handleValidationError"
                :multiple="true"
                :accepted-types="'.docx,.pdf,.txt,.md,.xlsx,.pptx'"
                :max-size="52428800"
              />

              <div v-if="uploadingFiles.length > 0" class="uploading-list">
                <div
                  v-for="file in uploadingFiles"
                  :key="file.id"
                  class="uploading-item"
                >
                  <ProgressBar
                    :percent="uploadProgress[file.id]?.percent || 0"
                    :status="uploadProgress[file.id]?.status || 'idle'"
                    :filename="file.name"
                    :loaded="uploadProgress[file.id]?.loaded || 0"
                    :total="file.size"
                    :error="uploadProgress[file.id]?.error"
                  />
                </div>
              </div>
            </div>

            <!-- 主内容区 -->
            <div class="panel-body" @click.stop>
              <!-- 左侧分类导航（仅文件列表模式） -->
              <div class="category-nav" v-if="!isSearching && viewMode === 'files'">
                <div
                  v-for="cat in categories"
                  :key="cat.key"
                  class="category-item"
                  :class="{ active: activeCategory === cat.key }"
                  @click="selectCategory(cat.key)"
                >
                  <component :is="cat.icon" class="cat-icon" />
                  <span class="cat-name">{{ cat.label }}</span>
                  <span class="cat-count">{{ cat.count }}</span>
                </div>
              </div>

              <!-- 搜索模式占位 -->
              <div class="category-nav search-mode-nav" v-else-if="isSearching && viewMode === 'files'">
                <div class="category-item active">
                  <SearchOutlined class="cat-icon" />
                  <span class="cat-name">搜索结果</span>
                  <span class="cat-count">{{ filteredGroups.length }}</span>
                </div>
              </div>
              <div class="category-nav" v-else-if="viewMode === 'versions'">
                <div class="category-item active">
                  <UnorderedListOutlined class="cat-icon" />
                  <span class="cat-name">版本列表</span>
                </div>
              </div>

              <!-- 右侧列表区 -->
              <div class="file-list-area">
                <!-- ===== 文件列表（直接展示） ===== -->
                <template v-if="viewMode === 'files'">
                  <div class="file-list" ref="listRef">
                    <template v-if="displayGroups.length > 0">
                      <div
                        v-for="(group, index) in displayGroups"
                        :key="group.title"
                        class="file-group-item"
                        @click="openVersionList(group)"
                      >
                        <div class="group-main">
                          <FileTextOutlined class="file-icon" />
                          <div class="group-info">
                            <div class="group-title">{{ group.title }}</div>
                            <div class="group-meta">
                              <span v-if="group.category" class="meta-tag">{{ group.category }}</span>
                              <span class="version-count">
                                <CopyOutlined /> {{ group.versions?.length || 0 }} 个版本
                              </span>
                              <span class="latest-version">最新: v{{ group.currentVersion }}</span>
                            </div>
                          </div>
                          <RightOutlined class="group-arrow" />
                        </div>
                      </div>
                    </template>

                    <div v-else-if="loading" class="list-empty loading">
                      <LoadingOutlined spin /><span>正在加载...</span>
                    </div>

                    <div v-else class="list-empty">
                      <FileSearchOutlined /><span>{{ isSearching ? '没有找到匹配的文件' : '暂无文件' }}</span>
                    </div>
                  </div>
                </template>

                <!-- ===== 版本列表 ===== -->
                <template v-else-if="viewMode === 'versions' && selectedGroup">
                  <div class="version-list">
                    <div
                      v-for="(ver, index) in selectedGroup.versions"
                      :key="ver.id"
                      class="version-item"
                      :class="{ selected: isSelected(ver) }"
                    >
                      <div class="version-main" @click="selectVersion(ver)">
                        <div class="version-badge">v{{ ver.version }}</div>
                        <div class="version-info">
                          <div class="version-date">{{ ver.updateDate }}</div>
                          <div class="version-desc">{{ ver.description || '' }}</div>
                        </div>
                        <CheckOutlined v-if="isSelected(ver)" class="version-check" />
                      </div>
                      <button 
                        class="vectorize-btn" 
                        @click.stop="handleVectorize(ver)"
                        :disabled="isVectorizing"
                        :title="'向量化该文件版本'"
                      >
                        <LoadingOutlined v-if="isVectorizing && vectorizingFileId === ver.id" spin />
                        <span v-else>向量化</span>
                      </button>
                    </div>
                  </div>
                </template>
              </div>
            </div>

            <!-- 底部操作栏 -->
            <div class="panel-footer">
              <span class="footer-hint" v-if="viewMode === 'files'">点击文件查看所有版本</span>
              <span class="footer-hint" v-else>选择一个版本确认</span>
              <span class="footer-total" v-if="viewMode === 'files'">共 {{ mergedGroups.length }} 个文件</span>
            </div>
          </div>
        </div>
      </Transition>
    </Teleport>

    <!-- 向量化知识库选择弹窗（仅管理员可见） -->
    <Teleport to="body">
      <Transition name="modal-fade">
        <div v-if="vectorizeModalVisible" class="vectorize-modal-overlay" @click.self="cancelVectorize">
          <div class="vectorize-modal">
            <div class="modal-header">
              <h3>选择目标知识库</h3>
              <button class="modal-close-btn" @click="cancelVectorize">
                <CloseOutlined />
              </button>
            </div>
            
            <div class="modal-body">
              <p class="modal-hint">请选择要将文件向量化的目标知识库：</p>
              
              <!-- 加载中 -->
              <div v-if="vectorizeCollectionLoading" class="loading-state">
                <LoadingOutlined spin />
                <span>正在加载知识库列表...</span>
              </div>
              
              <!-- 知识库列表 -->
              <div v-else-if="vectorizeCollections.length > 0" class="collection-select-list">
                <label 
                  v-for="col in vectorizeCollections" 
                  :key="col.name"
                  :class="['collection-option', { selected: vectorizeSelectedCollection === col.name }]"
                >
                  <input 
                    type="radio" 
                    name="vectorize-collection"
                    :value="col.name"
                    v-model="vectorizeSelectedCollection"
                  />
                  <span class="option-info">
                    <DatabaseOutlined class="option-icon" />
                    <span class="option-name">{{ col.displayName || col.name }}</span>
                  </span>
                </label>
              </div>
              
              <!-- 空状态 -->
              <div v-else class="empty-state">
                <InboxOutlined class="empty-icon" />
                <p>暂无可用知识库</p>
              </div>
            </div>
            
            <div class="modal-footer">
              <button class="btn btn-secondary" @click="cancelVectorize">取消</button>
              <button 
                class="btn btn-primary" 
                @click="confirmVectorize"
                :disabled="!vectorizeSelectedCollection || isVectorizing"
              >
                <LoadingOutlined v-if="isVectorizing" spin />
                <span v-else>确认向量化</span>
              </button>
            </div>
          </div>
        </div>
      </Transition>
    </Teleport>
  </div>
</template>

<script setup>
import { ref, computed, onMounted, nextTick, onBeforeUnmount } from 'vue'
import { message } from 'ant-design-vue'
import {
  SearchOutlined,
  DownOutlined,
  CloseOutlined,
  FileTextOutlined,
  CheckOutlined,
  LoadingOutlined,
  FileSearchOutlined,
  FolderOutlined,
  AppstoreOutlined,
  UnorderedListOutlined,
  LeftOutlined,
  RightOutlined,
  CopyOutlined,
  DatabaseOutlined,
  InboxOutlined,
  CloudUploadOutlined,
  UploadOutlined,
  DeleteOutlined
} from '@ant-design/icons-vue'
import { fileAPI } from '../api/file.js'
import { userAPI } from '../api/auth.js'
import { ragAPI } from '../api/rag.js'
import { getCurrentUserRole } from '../utils/permission.js'
import DropZone from './ui/DropZone.vue'
import ProgressBar from './ui/ProgressBar.vue'
import { formatFileSize, validateFile } from '../utils/fileValidator.js'

// 用户信息缓存
const userInfoCache = ref(new Map())

// 批量获取用户信息
const fetchUserNames = async (userIds) => {
  if (!userIds || userIds.length === 0) return
  
  const uniqueIds = [...new Set(userIds.filter(id => id))]
  const missingIds = uniqueIds.filter(id => !userInfoCache.value.has(String(id)))
  
  if (missingIds.length === 0) return
  
  for (const userId of missingIds) {
    try {
      const response = await userAPI.getUserById(userId)
      if (response.data?.code === 200 && response.data?.data) {
        const user = response.data.data
        const displayName = user.realName || user.name || user.username || '未知用户'
        userInfoCache.value.set(String(userId), displayName)
      }
    } catch (err) {
      console.warn(`⚠️ 获取用户 ${userId} 信息失败:`, err.message)
      userInfoCache.value.set(String(userId), '未知用户')
    }
  }
}

const props = defineProps({
  modelValue: [String, Number],
  placeholder: { type: String, default: '请选择制度文件' },
  showAllOption: { type: Boolean, default: false },
  allowClear: { type: Boolean, default: true },
  showDropZone: { type: Boolean, default: true }
})

const emit = defineEmits(['update:modelValue', 'change', 'collection-change', 'vectorized', 'file-uploaded'])

const selectorRef = ref(null)
const searchInputRef = ref(null)
const listRef = ref(null)
const dropdownVisible = ref(false)
const searchKeyword = ref('')
const rawFiles = ref([])
const loading = ref(false)
const activeCategory = ref('all')
const panelStyle = ref({})
const viewMode = ref('files')
const selectedGroup = ref(null)

// 向量库相关状态
const collections = ref([])  // 向量库列表
const selectedCollection = ref('')  // 当前选中的向量库
const collectionLoading = ref(false)  // 加载向量库列表中
const step = ref(2)  // 当前步骤：直接使用文件列表模式

// 向量化相关状态
const isVectorizing = ref(false)
const vectorizingFileId = ref(null)
const vectorizeModalVisible = ref(false)
const vectorizeTargetVersion = ref(null)
const vectorizeSelectedCollection = ref('')
const vectorizeCollections = ref([])
const vectorizeCollectionLoading = ref(false)
const isAdmin = ref(false)
const userRole = ref('')
const userDepartmentKb = ref('')

// ===== 新增：上传相关状态 =====
const uploadingFiles = ref([])
const uploadProgress = ref({})

const RECENT_KEY = 'file_selector_recent'
const MAX_RECENT = 5

const isSearching = computed(() => searchKeyword.value.trim().length > 0)

const displayText = computed(() => {
  if (!props.modelValue) return ''
  if (props.modelValue === '__all__' || props.modelValue === 'all') return '全部制度'
  for (const g of mergedGroups.value) {
    const found = g.versions?.find(v => v.id === props.modelValue)
    if (found) return `${g.title} (v${found.version})`
  }
  return ''
})

const extractVersionFromPath = (filePath) => {
  if (!filePath) return 1
  const normalizedPath = filePath.replace(/\\/g, '/')
  const vDigitPattern = /\/v(\d+)\//i
  const match = normalizedPath.match(vDigitPattern)
  return match ? parseInt(match[1]) : 1
}

const parseVersionNumber = (vStr) => {
  if (!vStr) return 1
  const match = String(vStr).match(/v?([\d.]+)/i)
  return match ? parseFloat(match[1]) : 1
}

const mapBackendToFrontend = (item) => {
  // 获取上传者显示名
  let uploaderDisplayName = '未知'
  if (item.uploadUserId) {
    uploaderDisplayName = userInfoCache.value.get(String(item.uploadUserId)) || item.uploadUserName || '未知'
  }
  
  return {
    id: item.id,
    title: item.fileName || item.name,
    category: item.deptName || extractCategory(item.filePath || item.path || ''),
    version: extractVersionFromPath(item.filePath),
    updateDate: item.updateTime ? item.updateTime.split('T')[0] : '',
    description: `${item.fileType || ''} | ${formatFileSize(item.fileSize)} | 上传者: ${uploaderDisplayName}`,
    pdfUrl: item.filePath,
    rawFileName: item.fileName || item.name,
    extension: item.extension,
    deptId: item.deptId,
    deptName: item.deptName,
    fileSize: item.fileSize,
    fileType: item.fileType,
    createTime: item.createTime,
    updateTime: item.updateTime
  }
}

const mergeFilesByName = (files) => {
  const fileMap = new Map()
  files.forEach(file => {
    const baseName = file.rawFileName
    if (!fileMap.has(baseName)) {
      fileMap.set(baseName, { ...file, versions: [file], currentVersion: file.version })
    } else {
      const existing = fileMap.get(baseName)
      existing.versions.push(file)
      if (parseVersionNumber(file.version) > parseVersionNumber(existing.currentVersion)) {
        existing.currentVersion = file.version
      }
    }
  })
  return Array.from(fileMap.values()).sort((a, b) => a.title.localeCompare(b.title))
}

const extractCategory = (path) => {
  if (!path) return ''
  const parts = path.replace(/\\/g, '/').split('/')
  const idx = parts.findIndex(p => p.toLowerCase() === 'resources')
  return idx >= 0 && parts[idx + 1] ? parts[idx + 1] : ''
}

const mergedGroups = computed(() => mergeFilesByName(rawFiles.value))

const categories = computed(() => {
  const cats = [
    { key: 'all', label: '全部文件', icon: AppstoreOutlined, count: mergedGroups.value.length }
  ]
  const categoryMap = {}
  mergedGroups.value.forEach(g => {
    const cat = g.category || '未分类'
    categoryMap[cat] = (categoryMap[cat] || 0) + 1
  })
  Object.entries(categoryMap).sort((a, b) => b[1] - a[1]).forEach(([name, count]) => {
    cats.push({ key: name, label: name, icon: FolderOutlined, count })
  })
  return cats
})

const filteredGroups = computed(() => {
  let results = mergedGroups.value
  if (isSearching.value) {
    const keyword = searchKeyword.value.toLowerCase().trim()
    results = results.filter(g =>
      (g.title || '').toLowerCase().includes(keyword) ||
      (g.category || '').toLowerCase().includes(keyword)
    )
  }
  if (activeCategory.value !== 'all' && !isSearching.value) {
    results = results.filter(g => (g.category || '未分类') === activeCategory.value)
  }
  return results
})

const displayGroups = computed(() => {
  if (isSearching.value) return filteredGroups.value
  if (activeCategory.value === 'all') return mergedGroups.value
  return mergedGroups.value.filter(g => (g.category || '未分类') === activeCategory.value)
})

const isSelected = (ver) => props.modelValue === ver.id

// ========== 向量库选择功能 ==========

// 加载向量库列表
const loadCollections = async () => {
  collectionLoading.value = true
  try {
    console.log('📚 加载向量库列表...')
    const response = await ragAPI.getCollections()
    console.log('📦 getCollections 原始响应:', response)
    console.log('📦 响应类型:', typeof response, Array.isArray(response))
    
    // ragAPI 返回的数据结构可能是：
    // 1. { collections: [...] } - 对象格式
    // 2. ["name1", "name2", ...] - 直接数组（字符串）
    // 3. [{ name: "...", ... }, ...] - 直接数组（对象）
    let list = []
    
    if (response && response.collections) {
      // 情况1：对象格式 { collections: [...] }
      list = response.collections
    } else if (Array.isArray(response)) {
      // 情况2/3：直接是数组
      list = response
    } else if (response && response.data && response.data.collections) {
      // 嵌套在 data 中
      list = response.data.collections
    }
    
    console.log(`✅ 向量库列表加载成功，共 ${list.length} 个`, list)
    
    if (list.length === 0) {
      console.warn('⚠️ 知识库列表为空，可能原因:')
      console.warn('  1. RAG服务未启动')
      console.warn('  2. 尚未创建任何知识库')
      console.warn('  3. API返回格式不匹配')
    }
    
    // 转换为统一格式（兼容字符串和对象两种格式）
    collections.value = list.map(col => {
      if (typeof col === 'string') {
        // 字符串格式：直接作为名称
        return {
          name: col,
          count: 0,
          description: '',
          createdAt: new Date().toISOString(),
          displayName: col,
          visibility: 'unknown'
        }
      } else {
        // 对象格式：保留所有字段
        return {
          name: col.name || col.collection_name || '',
          displayName: col.displayName || col.name || col.collection_name || '',
          count: col.document_count || col.count || col.documentCount || 0,
          description: col.description || '',
          createdAt: col.created_at || new Date().toISOString(),
          visibility: col.visibility || 'unknown',
          department: col.department || ''
        }
      }
    })
    
  } catch (err) {
    console.error('❌ 向量库列表加载失败:', err)
    console.error('   错误详情:', err.message)
    console.error('   错误代码:', err.code)
    
    let errorMsg = '加载知识库列表失败'
    if (err.message?.includes('RAG') || err.message?.includes('5001')) {
      errorMsg = 'RAG服务未启动或连接失败'
    } else if (err.message?.includes('404')) {
      errorMsg = '知识库接口不存在，请检查后端配置'
    } else if (err.message) {
      errorMsg = err.message
    }
    
    message.error(errorMsg, 5)
  } finally {
    collectionLoading.value = false
  }
}

// 选择向量库
const selectCollection = async (collectionName) => {
  selectedCollection.value = collectionName
  step.value = 2
  
  // 清空之前的文件数据
  rawFiles.value = []
  
  // 触发事件通知父组件
  emit('collection-change', collectionName)
  
  // 加载该向量库的文档列表
  await loadDocumentsFromCollection(collectionName)
}

// 返回向量库选择步骤
const backToCollectionSelect = () => {
  step.value = 1
  rawFiles.value = []
  selectedCollection.value = ''
}

// ========== 向量化功能 ==========

// 检查用户是否为管理员
const checkUserRole = async () => {
  try {
    const role = await getCurrentUserRole({ useCache: true })
    userRole.value = role
    isAdmin.value = ['admin', 'super_admin', 'SUPER_ADMIN', '超级管理员', '管理员'].includes(role)
    console.log('👤 用户角色:', role, '- 是否管理员:', isAdmin.value)
    
    // 获取用户的部门向量库（非管理员时使用）
    if (!isAdmin.value) {
      await loadDepartmentKnowledgeBase()
    }
  } catch (error) {
    console.error('❌ 获取用户角色失败:', error)
    isAdmin.value = false
  }
}

// 加载部门向量库信息
const loadDepartmentKnowledgeBase = async () => {
  try {
    const userInfoStr = localStorage.getItem('userInfo')
    if (userInfoStr) {
      const userInfo = JSON.parse(userInfoStr)
      const deptName = userInfo.departmentName || userInfo.deptName || ''
      // 部门向量库命名规则：{部门名}_kb 或 dept_{部门ID}_kb
      userDepartmentKb.value = deptName ? `${deptName}_kb` : 'public_kb'
      console.log('🏢 部门向量库:', userDepartmentKb.value)
    } else {
      userDepartmentKb.value = 'public_kb'
    }
  } catch (error) {
    console.warn('⚠️ 获取部门向量库失败，使用默认值')
    userDepartmentKb.value = 'public_kb'
  }
}

// 处理向量化按钮点击
const handleVectorize = async (version) => {
  vectorizeTargetVersion.value = version
  
  // 先检查用户角色
  await checkUserRole()
  
  if (isAdmin.value) {
    // 管理员：弹出知识库选择弹窗
    vectorizeModalVisible.value = true
    await loadVectorizeCollections()
  } else {
    // 非管理员：直接使用部门向量库
    vectorizeSelectedCollection.value = userDepartmentKb.value
    confirmVectorize()
  }
}

// 加载向量化用的知识库列表
const loadVectorizeCollections = async () => {
  vectorizeCollectionLoading.value = true
  try {
    const response = await ragAPI.getCollections()
    
    let list = []
    if (response && response.collections) {
      list = response.collections
    } else if (Array.isArray(response)) {
      list = response
    }
    
    vectorizeCollections.value = list.map(col => ({
      name: typeof col === 'string' ? col : (col.name || col.collection_name || ''),
      displayName: typeof col === 'string' ? col : (col.displayName || col.name || '')
    }))
    
    // 默认选中第一个
    if (vectorizeCollections.value.length > 0 && !vectorizeSelectedCollection.value) {
      vectorizeSelectedCollection.value = vectorizeCollections.value[0].name
    }
  } catch (error) {
    console.error('❌ 加载知识库列表失败:', error)
    message.error('加载知识库列表失败')
  } finally {
    vectorizeCollectionLoading.value = false
  }
}

// 确认执行向量化（使用智能向量化接口，自动识别知识库）
const confirmVectorize = async () => {
  if (!vectorizeTargetVersion.value) {
    message.error('请先选择要向量化的文件版本')
    return
  }

  isVectorizing.value = true
  vectorizingFileId.value = vectorizeTargetVersion.value.id

  try {
    const fileId = vectorizeTargetVersion.value.id
    const fileName = vectorizeTargetVersion.value.rawFileName || selectedGroup.value?.title

    console.log('🚀 开始智能向量化文件:')
    console.log('   - 文件ID:', fileId)
    console.log('   - 文件名:', fileName)

    message.loading({
      content: '正在提交智能向量化任务，系统将自动匹配知识库...',
      key: 'vectorizing',
      duration: 0
    })

    // 使用智能向量化接口，自动根据文件部门ID匹配知识库
    const result = await ragAPI.smartVectorizeFile(fileId)

    message.destroy('vectorizing')

    // 从响应中提取系统分配的知识库名称
    const assignedCollection = result.collection || result.data?.collection || '系统默认'

    if (result.submitted) {
      message.success({
        content: `智能向量化任务已提交！系统已自动识别知识库「${assignedCollection}」，正在后台处理...`,
        duration: 4
      })
    } else {
      message.success(`文件向量化成功！已添加到知识库：${assignedCollection}`)
    }

    vectorizeModalVisible.value = false

    emit('vectorized', {
      fileId,
      collectionName: assignedCollection,
      fileName
    })

  } catch (error) {
    console.error('❌ 向量化失败:', error)
    message.destroy('vectorizing')
    
    let errorMsg = '向量化失败'
    if (error.message?.includes('Connection refused') || error.message?.includes('连接被拒绝')) {
      errorMsg = 'RAG服务未启动或连接失败'
    } else if (error.message?.includes('timeout') || error.message?.includes('超时')) {
      errorMsg = '向量化超时，请稍后重试'
    } else if (error.message) {
      errorMsg = error.message
    }
    
    message.error(errorMsg, 5)
  } finally {
    isVectorizing.value = false
    vectorizingFileId.value = null
    vectorizeTargetVersion.value = null
  }
}

// 取消向量化
const cancelVectorize = () => {
  vectorizeModalVisible.value = false
  vectorizeTargetVersion.value = null
  vectorizeSelectedCollection.value = ''
}

// 从向量库加载文档列表（用于AI出题）
const loadDocumentsFromCollection = async (collectionName) => {
  
  loading.value = true
  try {
    console.log(`📚 FileSelector: 开始从向量库 "${collectionName}" 加载文档列表...`)
    const response = await ragAPI.getDocumentList(collectionName)
    
    // 🔍 详细调试：打印原始响应
    console.log('📦 getDocumentList 原始响应:', response)
    console.log('📦 响应类型:', typeof response, Array.isArray(response))
    if (response) {
      console.log('📦 响应keys:', Object.keys(response))
      if (response.documents) {
        console.log('📦 documents 字段:', response.documents, '长度:', response.documents.length)
      }
      if (response.data) {
        console.log('📦 data 字段:', response.data)
      }
    }
    
    // 兼容多种响应格式（与 ManageModule 保持一致）
    let documents = []
    
    if (Array.isArray(response?.documents)) {
      // 格式1: { documents: [...] } - 标准对象格式
      documents = response.documents
      console.log('✅ 使用格式1: response.documents')
    } else if (Array.isArray(response)) {
      // 格式2: 直接返回数组 [{...}, ...]
      documents = response
      console.log('✅ 使用格式2: 直接数组，长度:', documents.length)
    } else if (Array.isArray(response?.data?.documents)) {
      // 格式3: { data: { documents: [...] } }
      documents = response.data.documents
      console.log('✅ 使用格式3: response.data.documents')
    } else if (response) {
      // 格式4: 其他情况，尝试将整个响应作为文档列表
      console.warn('⚠️ 未识别的响应格式，尝试使用整个响应')
      documents = Array.isArray(response) ? response : [response]
    }
    
    console.log(`🎯 最终解析到 ${documents.length} 个文档`)
    
    if (documents.length === 0) {
      console.warn('⚠️ 文档列表为空！可能原因:')
      console.warn('  1. 知识库中没有上传任何文件')
      console.warn('  2. API 返回格式不匹配（见上方日志）')
      console.warn('  3. 权限不足无法访问该知识库')
    }
    
    // 将向量库文档格式转换为前端需要的格式（与 ManageModule.getDocumentFilename 一致）
    rawFiles.value = documents.map((doc, index) => {
      // 提取文件名（兼容多种字段名）
      const filename = doc.filename || doc.name || doc.metadata?.filename || doc.path || `文档${index + 1}`
      
      return {
        id: doc.id || index,
        title: filename.replace(/\.[^.]+$/, '').replace(/[_-]/g, ' '),  // 去掉扩展名作为标题
        rawFileName: filename,
        path: doc.path || filename,  // 使用完整路径（与ManageModule一致）
        version: doc.version || doc.metadata?.version || 1,
        category: '知识库文档',
        extension: filename.split('.').pop()?.toLowerCase() || '',
        uploadUserId: null,
        uploadUserName: null,
        createdAt: doc.created_at || new Date().toISOString(),
        updatedAt: doc.updated_at || new Date().toISOString(),
        isVectorized: true,  // 标记为已向量化
        collectionName: collectionName,  // 使用传入的参数
        sourceData: doc  // 保存原始数据供后续使用
      }
    })
    
    console.log(`✅ 向量库文档转换完成，共 ${rawFiles.value.length} 个文件`)
    console.log('📋 文件列表预览:', rawFiles.value.map(f => f.rawFileName))
    
  } catch (err) {
    console.error('❌ 向量库文档列表加载失败:', err)
    console.error('   错误详情:', err.message)
    console.error('   错误代码:', err.code)
    console.error('   错误状态:', err.status)
    
    let errorMsg = '加载知识库文档失败'
    if (err.message?.includes('404')) {
      errorMsg = '知识库不存在或已被删除'
    } else if (err.message?.includes('RAG') || err.message?.includes('5001')) {
      errorMsg = 'RAG服务连接失败'
    } else if (err.message) {
      errorMsg = err.message
    }
    
    message.error(errorMsg, 5)
  } finally {
    loading.value = false
  }
}

// 从普通文件系统加载文件列表
const loadFilesFromFileSystem = async () => {
  if (rawFiles.value.length > 0) return
  loading.value = true
  try {
    const response = await fileAPI.getFiles({ size: 200, current: 1 })
    if (response.data?.code === 200 && response.data.data?.records) {
      const records = response.data.data.records
      
      const uploadUserIds = records.map(file => file.uploadUserId).filter(id => id)
      await fetchUserNames(uploadUserIds)
      
      rawFiles.value = records.map(mapBackendToFrontend)
    }
  } catch (err) {
    console.warn('⚠️ 文件列表加载失败:', err.message)
  } finally {
    loading.value = false
  }
}

// 统一的加载入口：直接调用文件列表接口
const loadFiles = async () => {
  await loadFilesFromFileSystem()
}

const saveRecent = (id) => {
  try {
    let recent = JSON.parse(localStorage.getItem(RECENT_KEY) || '[]')
    recent = recent.filter(rid => rid !== id)
    recent.unshift(id)
    recent = recent.slice(0, MAX_RECENT)
    localStorage.setItem(RECENT_KEY, JSON.stringify(recent))
  } catch {}
}

const toggleDropdown = async () => {
  dropdownVisible.value = !dropdownVisible.value
  if (dropdownVisible.value) {
    viewMode.value = 'files'
    selectedGroup.value = null
    await loadFiles()
    searchKeyword.value = ''
    await nextTick()
    calcPanelPosition()
    searchInputRef.value?.focus()
  }
}

const closeDropdown = () => {
  dropdownVisible.value = false
  searchKeyword.value = ''
}

const calcPanelPosition = () => {
  if (!selectorRef.value) return
  const rect = selectorRef.value.getBoundingClientRect()
  const viewportW = window.innerWidth
  const viewportH = window.innerHeight
  const panelMaxH = 500
  const gap = 6

  const spaceBelow = viewportH - rect.bottom - gap
  const spaceAbove = rect.top - gap
  const panelH = Math.min(panelMaxH, Math.max(spaceBelow, 200))

  let top
  if (spaceBelow >= panelH || spaceBelow > spaceAbove) {
    top = rect.bottom + gap
  } else {
    top = Math.max(gap, viewportH - panelH - gap)
  }

  let left = rect.left
  const width = Math.max(rect.width, 520)
  if (left + width > viewportW - 12) {
    left = viewportW - width - 12
  }
  if (left < 12) left = 12

  panelStyle.value = {
    top: `${top}px`,
    left: `${left}px`,
    width: `${width}px`,
    maxHeight: `${panelH}px`
  }
}

let scrollRafId = null
const onScroll = () => {
  if (!dropdownVisible.value) return
  if (scrollRafId) cancelAnimationFrame(scrollRafId)
  scrollRafId = requestAnimationFrame(() => calcPanelPosition())
}

const selectCategory = (key) => { activeCategory.value = key }

const onSearchInput = () => {}

const openVersionList = (group) => {
  selectedGroup.value = group
  viewMode.value = 'versions'
}

const backToFileList = () => {
  viewMode.value = 'files'
  selectedGroup.value = null
}

const selectVersion = (ver) => {
  saveRecent(ver.id)
  emit('update:modelValue', ver.id)
  emit('change', { ...ver, groupTitle: selectedGroup.value?.title })
  closeDropdown()
}

const handleClear = () => {
  emit('update:modelValue', null)
  emit('change', null)
}

// ===== 新增：文件上传功能 =====
async function handleFilesSelected(files) {
  for (const file of files) {
    await uploadSingleFile(file)
  }
}

async function uploadSingleFile(file) {
  const validation = validateFile(file)
  if (!validation.valid) {
    message.error(`${file.name}: ${validation.errors.join('; ')}`)
    return
  }

  const fileId = Date.now() + '_' + Math.random().toString(36).substr(2, 9)

  uploadProgress.value[fileId] = {
    percent: 0,
    status: 'uploading',
    loaded: 0,
    total: file.size
  }

  uploadingFiles.value.push({
    id: fileId,
    name: file.name,
    size: file.size
  })

  try {
    const formData = new FormData()
    formData.append('file', file)

    const response = await fileAPI.uploadFile(formData, (progress) => {
      uploadProgress.value[fileId] = {
        ...progress,
        status: 'uploading'
      }
    })

    uploadProgress.value[fileId] = {
      ...uploadProgress.value[fileId],
      percent: 100,
      status: 'success'
    }

    message.success(`✅ "${file.name}" 上传成功！`)

    emit('file-uploaded', response.data?.data)

    setTimeout(() => {
      uploadingFiles.value = uploadingFiles.value.filter(f => f.id !== fileId)
      delete uploadProgress.value[fileId]
    }, 3000)

  } catch (error) {
    uploadProgress.value[fileId] = {
      ...uploadProgress.value[fileId],
      status: 'error',
      error: error.response?.data?.message || error.message || '网络错误'
    }

    message.error(`❌ "${file.name}" 上传失败`)
  }
}

function handleValidationError(errors) {
  errors.forEach(err => {
    message.warning(`⚠️ ${err.file.name}: ${err.errors.join(', ')}`)
  })
}

const handleClickOutside = (e) => {
  const panel = document.querySelector('.selector-panel')
  const isInsidePanel = panel && panel.contains(e.target)
  const isInsideTrigger = selectorRef.value && selectorRef.value.contains(e.target)
  if (!isInsidePanel && !isInsideTrigger) closeDropdown()
}

onMounted(() => {
  document.addEventListener('click', handleClickOutside)
  window.addEventListener('resize', calcPanelPosition)
  window.addEventListener('scroll', onScroll, true)
  loadFiles()
})

onBeforeUnmount(() => {
  document.removeEventListener('click', handleClickOutside)
  window.removeEventListener('resize', calcPanelPosition)
  window.removeEventListener('scroll', onScroll, true)
  if (scrollRafId) cancelAnimationFrame(scrollRafId)
})
</script>

<style scoped>
.file-selector { position: relative; width: 100%; }

/* ========== 触发器（白色体系）========== */
.selector-trigger {
  display: flex;
  align-items: center;
  gap: 8px;
  height: 36px;
  padding: 0 12px;
  border: 1px solid #DEE2E6;
  border-radius: 6px;
  background: #FFFFFF;
  cursor: pointer;
  transition: all 0.15s ease;
  user-select: none;
}
.selector-trigger:hover {
  border-color: #ADB5BD;
  background: #F8F9FA;
}
.selector-trigger.active {
  border-color: #212529;
  box-shadow: 0 0 0 3px rgba(33, 37, 41, 0.08);
  background: #FFFFFF;
}
.selector-trigger.filled .trigger-text {
  color: #212529;
  font-weight: 500;
}

.trigger-icon {
  font-size: 14px;
  color: #6C757D;
  flex-shrink: 0;
  transition: color 0.15s;
}
.selector-trigger:hover .trigger-icon,
.selector-trigger.active .trigger-icon {
  color: #212529;
}

.trigger-text {
  flex: 1;
  font-size: 13px;
  color: #ADB5BD;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  line-height: 1.4;
}

.trigger-clear {
  font-size: 12px;
  color: #ADB5BD;
  padding: 3px;
  border-radius: 50%;
  transition: all 0.15s;
  flex-shrink: 0;
}
.trigger-clear:hover {
  color: #C92A2A;
  background: #FFF5F5;
}

.trigger-arrow {
  font-size: 10px;
  color: #6C757D;
  transition: transform 0.2s ease;
  flex-shrink: 0;
}
.trigger-arrow.rotated {
  transform: rotate(180deg);
}

/* ========== 遮罩层 + 面板（白色体系）========== */
.selector-overlay {
  position: fixed;
  inset: 0;
  z-index: 9999;
  overflow: hidden;
}

.dropdown-fade-enter-active .selector-panel {
  animation: panelIn 0.15s ease;
}
.dropdown-fade-leave-active .selector-panel {
  animation: panelOut 0.12s ease forwards;
}
@keyframes panelIn {
  from { opacity: 0; transform: translateY(-8px) scale(0.98); }
  to { opacity: 1; transform: translateY(0) scale(1); }
}
@keyframes panelOut {
  from { opacity: 1; transform: translateY(0) scale(1); }
  to { opacity: 0; transform: translateY(-6px) scale(0.97); }
}

.selector-panel {
  position: fixed;
  background: var(--bg-card);
  border-radius: var(--radius-lg);
  border: 1px solid var(--border-light);
  box-shadow: var(--shadow-lg);
  z-index: 10000;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

.panel-header {
  padding: var(--space-sm) var(--space-md) var(--space-xs);
  border-bottom: 1px solid var(--border-light);
  background: var(--bg-card);
  flex-shrink: 0;
}

.header-search {
  display: flex;
  align-items: center;
  gap: var(--space-xs);
  padding: 0 var(--space-sm);
  height: 32px;
  border: 1px solid var(--border-default);
  border-radius: var(--radius-sm);
  background: var(--bg-card);
  transition: all 0.15s;
}
.header-search:focus-within {
  border-color: var(--text-primary);
  box-shadow: 0 0 0 3px rgba(33, 37, 41, 0.05);
}
.search-icon {
  font-size: 14px;
  color: #ADB5BD;
  flex-shrink: 0;
  transition: color 0.15s;
}
.header-search:focus-within .search-icon {
  color: #212529;
}
.search-input {
  flex: 1;
  border: none;
  outline: none;
  font-size: 13px;
  color: #212529;
  background: transparent;
  line-height: 1.4;
}
.search-input::placeholder {
  color: #ADB5BD;
}

/* 面包屑导航 */
.header-breadcrumb {
  display: flex;
  align-items: center;
  gap: 8px;
  height: 36px;
}
.breadcrumb-back {
  font-size: 14px;
  color: #495057;
  cursor: pointer;
  padding: 4px;
  border-radius: 4px;
  transition: all 0.15s;
}
.breadcrumb-back:hover {
  background: #F1F3F5;
  color: #212529;
}
.breadcrumb-label {
  font-size: 13px;
  font-weight: 600;
  color: #212529;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  max-width: 280px;
}
.breadcrumb-count {
  font-size: 12px;
  color: #6C757D;
  background: #F8F9FA;
  padding: 2px 10px;
  border-radius: 12px;
  margin-left: auto;
}

/* ========== 主体区域（白色体系）========== */
.panel-body {
  display: flex;
  min-height: 240px;
  max-height: 380px;
}

/* 左侧分类 */
.category-nav {
  width: 140px;
  min-width: 130px;
  padding: 8px 6px;
  border-right: 1px solid #E9ECEF;
  background: #F8F9FA;
  overflow-y: auto;
  flex-shrink: 0;
}
.search-mode-nav {
  border-right: none;
}

.category-item {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 10px;
  margin-bottom: 2px;
  border-radius: 6px;
  cursor: pointer;
  transition: all 0.15s ease;
  font-size: 13px;
  color: #495057;
}
.category-item:hover {
  background: #F1F3F5;
  color: #212529;
}
.category-item.active {
  background: #E9ECEF;
  color: #212529;
  font-weight: 600;
}
.cat-icon {
  font-size: 13px;
  flex-shrink: 0;
}
.cat-name {
  flex: 1;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.cat-count {
  font-size: 11px;
  color: #6C757D;
  background: #E9ECEF;
  padding: 1px 7px;
  border-radius: 10px;
  flex-shrink: 0;
}
.category-item.active .cat-count {
  background: #CED4DA;
  color: #212529;
}

/* 右侧列表 */
.file-list-area {
  flex: 1;
  display: flex;
  flex-direction: column;
  min-width: 0;
  overflow: hidden;
}
.file-list {
  flex: 1;
  overflow-y: auto;
  padding: 6px 8px 8px;
}
.version-list {
  flex: 1;
  overflow-y: auto;
  padding: 8px 10px 10px;
}

/* 文件组卡片（白色体系）*/
.file-group-item {
  margin-bottom: 4px;
  padding: 12px 14px;
  border-radius: 6px;
  cursor: pointer;
  transition: all 0.15s ease;
  border: 1px solid transparent;
}
.file-group-item:hover {
  background: #F8F9FA;
  border-color: #DEE2E6;
}

.group-main {
  display: flex;
  align-items: center;
  gap: 10px;
}
.file-icon {
  font-size: 18px;
  color: #ADB5BD;
  flex-shrink: 0;
  transition: color 0.15s;
}
.file-group-item:hover .file-icon {
  color: #495057;
}

.group-info {
  flex: 1;
  min-width: 0;
}
.group-title {
  font-size: 13px;
  font-weight: 600;
  color: #212529;
  line-height: 1.35;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.group-meta {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 4px;
  flex-wrap: wrap;
}
.meta-tag {
  font-size: 11px;
  color: #6C757D;
  background: #F8F9FA;
  padding: 1px 8px;
  border-radius: 4px;
}
.version-count {
  font-size: 11px;
  color: #495057;
  background: #E9ECEF;
  padding: 2px 8px;
  border-radius: 4px;
  display: flex;
  align-items: center;
  gap: 4px;
}
.latest-version {
  font-size: 11px;
  color: #ADB5BD;
}
.group-arrow {
  font-size: 11px;
  color: #CED4DA;
  flex-shrink: 0;
  transition: all 0.15s;
}
.file-group-item:hover .group-arrow {
  color: #495057;
  transform: translateX(2px);
}

/* 版本列表项（白色体系）*/
.version-item {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px 14px;
  margin-bottom: 4px;
  border-radius: 6px;
  cursor: pointer;
  transition: all 0.15s ease;
  border: 1px solid transparent;
}
.version-item:hover {
  background: #F8F9FA;
  border-color: #DEE2E6;
}
.version-item.selected {
  background: #E9ECEF;
  border-color: #CED4DA;
}

.version-badge {
  min-width: 40px;
  height: 40px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: #212529;
  color: #FFFFFF;
  font-size: 13px;
  font-weight: 700;
  border-radius: 6px;
  flex-shrink: 0;
}
.version-item.selected .version-badge {
  background: #000000;
}

.version-info {
  flex: 1;
  min-width: 0;
}
.version-date {
  font-size: 13px;
  color: #495057;
  font-weight: 500;
}
.version-desc {
  font-size: 11px;
  color: #6C757D;
  margin-top: 2px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.version-check {
  font-size: 14px;
  color: #212529;
  flex-shrink: 0;
}

/* 空状态（白色体系）*/
.list-empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 40px 20px;
  color: #ADB5BD;
  font-size: 13px;
}
.list-empty :deep(.anticon) {
  font-size: 32px;
}
.list-empty.loading {
  color: #6C757D;
}

/* ========== 底部（白色体系）========== */
.panel-footer {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 8px 16px;
  border-top: 1px solid #E9ECEF;
  background: #F8F9FA;
  flex-shrink: 0;
}
.footer-hint {
  font-size: 12px;
  color: #ADB5BD;
}
.footer-total {
  font-size: 12px;
  color: #6C757D;
}

/* ========== 向量库选择列表样式（白色体系）========== */
.collection-list {
  padding: 12px;
  height: 100%;
  overflow-y: auto;
}

.collection-header {
  margin-bottom: 10px;
  padding-bottom: 8px;
  border-bottom: 1px solid #E9ECEF;
}

.collection-header h4 {
  margin: 0 0 4px;
  font-size: 13px;
  color: #212529;
  font-weight: 600;
}

.collection-hint {
  display: block;
  font-size: 12px;
  color: #6C757D;
  line-height: 1.4;
}

.loading-state {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 32px;
  color: #6C757D;
  font-size: 13px;
}

.collection-item {
  display: flex;
  align-items: center;
  padding: 10px 12px;
  margin-bottom: 6px;
  background: #FFFFFF;
  border: 1px solid #E9ECEF;
  border-radius: 6px;
  cursor: pointer;
  transition: all 0.15s ease;
}

.collection-item:hover {
  background: #F8F9FA;
  border-color: #CED4DA;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.04);
}

.collection-icon {
  font-size: 20px;
  color: #495057;
  margin-right: 10px;
  flex-shrink: 0;
}

.collection-icon.public {
  color: #2B8A3E;
}
.collection-icon.private {
  color: #212529;
}

.visibility-tag {
  display: inline-block;
  padding: 2px 8px;
  border-radius: 4px;
  font-size: 11px;
  font-weight: 500;
}

.visibility-tag.public {
  background: #EBFBEE;
  color: #2B8A3E;
  border: 1px solid #B2F2BB;
}

.visibility-tag.private {
  background: #F8F9FA;
  color: #495057;
  border: 1px solid #DEE2E6;
}

.desc-text {
  color: #ADB5BD;
  max-width: 200px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.collection-info {
  flex: 1;
  min-width: 0;
}

.collection-name {
  font-size: 13px;
  font-weight: 600;
  color: #212529;
  margin-bottom: 3px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.collection-meta {
  font-size: 12px;
  color: #6C757D;
  display: flex;
  gap: 10px;
}

.collection-arrow {
  color: #CED4DA;
  margin-left: 8px;
  transition: transform 0.15s;
}

.collection-item:hover .collection-arrow {
  color: #495057;
  transform: translateX(2px);
}

.empty-state {
  text-align: center;
  padding: 32px 20px;
  color: #ADB5BD;
}

.empty-icon {
  font-size: 36px;
  margin-bottom: 10px;
  opacity: 0.5;
}

.empty-state p {
  margin: 4px 0;
  font-size: 13px;
}

.empty-hint {
  font-size: 12px !important;
  color: #CED4DA !important;
}

.back-btn {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 5px 12px;
  background: #FFFFFF;
  border: 1px solid #DEE2E6;
  border-radius: 6px;
  font-size: 12px;
  color: #495057;
  cursor: pointer;
  transition: all 0.15s;
}

.back-btn:hover {
  color: #212529;
  border-color: #212529;
  background: #F8F9FA;
}

/* 滚动条（统一灰色）*/
.category-nav::-webkit-scrollbar,
.file-list::-webkit-scrollbar,
.version-list::-webkit-scrollbar,
.collection-list::-webkit-scrollbar {
  width: 4px;
}
.category-nav::-webkit-scrollbar-track,
.file-list::-webkit-scrollbar-track,
.version-list::-webkit-scrollbar-track,
.collection-list::-webkit-scrollbar-track {
  background: transparent;
}
.category-nav::-webkit-scrollbar-thumb,
.file-list::-webkit-scrollbar-thumb,
.version-list::-webkit-scrollbar-thumb,
.collection-list::-webkit-scrollbar-thumb {
  background: #DEE2E6;
  border-radius: 2px;
}
.category-nav::-webkit-scrollbar-thumb:hover,
.file-list::-webkit-scrollbar-thumb:hover,
.version-list::-webkit-scrollbar-thumb:hover,
.collection-list::-webkit-scrollbar-thumb:hover {
  background: #ADB5BD;
}

/* ========== 向量化按钮样式（白色体系）========== */
.version-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
}

.version-main {
  display: flex;
  align-items: center;
  gap: 10px;
  flex: 1;
  cursor: pointer;
  padding: 4px 0;
}

.vectorize-btn {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 4px 12px;
  background: #FFFFFF;
  border: 1px solid #DEE2E6;
  border-radius: 6px;
  font-size: 12px;
  color: #495057;
  cursor: pointer;
  transition: all 0.15s ease;
  white-space: nowrap;
  flex-shrink: 0;
}

.vectorize-btn:hover:not(:disabled) {
  background: #212529;
  color: #FFFFFF;
  border-color: #212529;
}

.vectorize-btn:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

/* ========== 向量化弹窗样式（白色体系）========== */
.vectorize-modal-overlay {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background: rgba(33, 37, 41, 0.45);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 1050;
  padding: 16px;
}

.vectorize-modal {
  width: 100%;
  max-width: 480px;
  background: #FFFFFF;
  border-radius: 8px;
  box-shadow: 0 8px 32px rgba(0, 0, 0, 0.10);
  overflow: hidden;
}

.modal-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 16px 20px;
  border-bottom: 1px solid #E9ECEF;
  background: #F8F9FA;
}

.modal-header h3 {
  margin: 0;
  font-size: 16px;
  font-weight: 600;
  color: #212529;
}

.modal-close-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  background: transparent;
  border: none;
  border-radius: 4px;
  cursor: pointer;
  color: #6C757D;
  transition: all 0.15s ease;
}

.modal-close-btn:hover {
  background: #E9ECEF;
  color: #212529;
}

.modal-body {
  padding: 20px;
}

.modal-hint {
  margin: 0 0 16px;
  font-size: 13px;
  color: #495057;
  line-height: 1.5;
}

.collection-select-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
  max-height: 280px;
  overflow-y: auto;
}

.collection-option {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 12px;
  background: #FFFFFF;
  border: 2px solid #E9ECEF;
  border-radius: 6px;
  cursor: pointer;
  transition: all 0.15s ease;
}

.collection-option:hover {
  border-color: #ADB5BD;
  background: #F8F9FA;
}

.collection-option.selected {
  border-color: #212529;
  background: #F8F9FA;
}

.collection-option input[type="radio"] {
  display: none;
}

.option-info {
  display: flex;
  align-items: center;
  gap: 8px;
  flex: 1;
}

.option-icon {
  font-size: 18px;
  color: #6C757D;
}

.collection-option.selected .option-icon {
  color: #212529;
}

.option-name {
  font-size: 14px;
  font-weight: 500;
  color: #212529;
}

.modal-footer {
  display: flex;
  justify-content: flex-end;
  gap: 10px;
  padding: 16px 20px;
  border-top: 1px solid #E9ECEF;
  background: #F8F9FA;
}

.btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 7px 16px;
  border-radius: 6px;
  font-size: 13px;
  font-weight: 500;
  cursor: pointer;
  transition: all 0.15s ease;
  border: none;
}

.btn-primary {
  background: #212529;
  color: #FFFFFF;
}

.btn-primary:hover:not(:disabled) {
  background: #000000;
}

.btn-primary:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

.btn-secondary {
  background: #FFFFFF;
  color: #212529;
  border: 1px solid #DEE2E6;
}

.btn-secondary:hover {
  background: #F8F9FA;
  border-color: #ADB5BD;
}

/* ========== 弹窗动画 ========== */
.modal-fade-enter-active,
.modal-fade-leave-active {
  transition: opacity 0.2s ease;
}
.modal-fade-enter-from,
.modal-fade-leave-to {
  opacity: 0;
}

/* ========== 增强型上传区域样式 ========== */
.enhanced-upload-section {
  padding: 12px 16px;
  border-bottom: 1px solid #E9ECEF;
  background: #F8F9FA;
  flex-shrink: 0;
}

.uploading-list {
  margin-top: 12px;
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.uploading-item {
  background: white;
  border: 1px solid #e5e7eb;
  border-radius: 8px;
  padding: 12px;
}
</style>
