<template>
  <div class="reader-page">
    <!-- 无文件状态 - 显示引导提示 -->
    <div v-if="!hasFileLoaded && !isLoading" class="empty-state-container">
      <div class="empty-state-content">
        <div class="empty-icon">
          <svg width="40" height="40" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path><polyline points="14 2 14 8 20 8"></polyline><line x1="16" y1="13" x2="8" y2="13"></line><line x1="16" y1="17" x2="8" y2="17"></line><polyline points="10 9 9 9 8 9"></polyline></svg>
        </div>
        <p class="empty-title">请选择文件</p>
        <p class="empty-description">从左侧文件列表中选择一个文件开始阅读</p>
        <button @click="handleBack" class="back-to-list-btn">
          <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="19" y1="12" x2="5" y2="12"></line><polyline points="12 19 5 12 12 5"></polyline></svg>
          返回文件列表
        </button>
      </div>
    </div>
    
    <!-- 加载中状态（可选，一般不需要显示） -->
    <div v-else-if="isLoading" class="loading-container">
      <div class="loading-spinner"></div>
      <p>正在加载文件...</p>
    </div>
    
    <!-- 有文件状态 - 显示阅读器 -->
    <div v-else class="reader-container">
      <div class="reader-header">
        <button @click="handleBack" class="back-btn">
          ← 返回列表
        </button>
        <h2>{{ fileName }}</h2>
        
        <div class="toolbar-group">
          <div class="zoom-container">
            <button @click="fitToWidth" class="tool-btn" title="适应宽度">
              <span class="icon">↔</span>
              <span class="label">宽度</span>
            </button>
            <button @click="fitToPage" class="tool-btn" title="适应页面">
              <span class="icon">▢</span>
              <span class="label">页面</span>
            </button>
            <div class="zoom-divider"></div>
            <button @click="zoomOut" class="tool-btn icon-only" title="缩小">
              <span class="icon">−</span>
            </button>
            <span class="zoom-level">{{ Math.round(scale * 100) }}%</span>
            <button @click="zoomIn" class="tool-btn icon-only" title="放大">
              <span class="icon">+</span>
            </button>
          </div>
        </div>
        
        <div class="toolbar-divider"></div>
        
        <div class="toolbar-group">
          <div class="color-picker-wrapper">
            <button @click="toggleColorPicker" class="tool-btn" :class="{ active: showColorPicker }" title="高亮颜色">
              <div 
                class="color-preview" 
                :style="{ backgroundColor: currentColor }"
              ></div>
              <span class="label">颜色</span>
            </button>
            <Transition name="dropdown">
              <div v-if="showColorPicker" class="color-picker-dropdown" @click.stop>
                <div 
                  v-for="color in colors" 
                  :key="color"
                  class="color-option"
                  :class="{ active: currentColor === color }"
                  :style="{ backgroundColor: color }"
                  @click="selectColor(color)"
                ></div>
              </div>
            </Transition>
          </div>
          
          <button
            @click="addHighlight"
            :disabled="!selectedText"
            class="tool-btn primary"
            title="高亮选中文本"
          >
            <span class="icon">◆</span>
            <span class="label">高亮</span>
          </button>

          <div class="speech-controls">
            <button
              @click="toggleSpeech"
              :disabled="!selectedText && !isSpeaking"
              class="tool-btn"
              :class="{ active: isSpeaking }"
              title="朗读控制"
            >
              <span class="icon">{{ isSpeaking ? '■' : '▶' }}</span>
              <span class="label">{{ isSpeaking ? '停止' : '朗读' }}</span>
            </button>
            <button
              @click="toggleSpeechControls"
              class="tool-btn icon-only"
              title="朗读设置"
            >
              <span class="icon">⚙</span>
            </button>
            <Transition name="dropdown">
              <div v-if="showSpeechControls" class="speech-dropdown" @click.stop>
                <div class="speech-control-item">
                  <label class="control-label">语速</label>
                  <div class="rate-control">
                    <button @click="decreaseRate" class="rate-btn" title="减慢语速">−</button>
                    <span class="rate-value">{{ Math.round(speechRate * 100) }}%</span>
                    <button @click="increaseRate" class="rate-btn" title="加快语速">+</button>
                  </div>
                </div>
                <div class="speech-control-item">
                  <label class="control-label">语音</label>
                  <select v-model="selectedVoice" class="voice-select">
                    <option v-for="voice in availableVoices" :key="voice.voiceURI" :value="voice">
                      {{ voice.name }} ({{ voice.lang }})
                    </option>
                  </select>
                </div>
                <div class="speech-control-item">
                  <label class="control-label">测试朗读</label>
                  <button @click="testSpeech" class="test-speech-btn">
                    测试语音
                  </button>
                </div>
              </div>
            </Transition>
          </div>
        </div>
        
        <div class="toolbar-divider"></div>
        
        <div class="toolbar-group">
          <button @click="exportAnnotations" class="tool-btn" title="导出批注">
            <span class="icon">↑</span>
            <span class="label">导出</span>
          </button>
          <label class="tool-btn" title="导入批注">
            <span class="icon">↓</span>
            <span class="label">导入</span>
            <input type="file" @change="importAnnotations" accept=".json" class="hidden-input" />
          </label>
          <button @click="clearAll" class="tool-btn danger" title="清空所有">
            <span class="icon">×</span>
          </button>
          <button
            @click="showSidebar = !showSidebar"
            class="tool-btn"
            :class="{ active: showSidebar }"
            :title="showSidebar ? '隐藏批注栏' : '显示批注栏'"
          >
            <span class="icon">{{ showSidebar ? '✓' : '✎' }}</span>
            <span class="label">{{ showSidebar ? '隐藏批注' : '显示批注' }}</span>
          </button>
        </div>
      </div>
      
      <div class="reader-content" ref="scrollContainerRef">
        <div class="pdf-wrapper">
          <!-- PDF 文件 - 使用 iframe -->
          <div v-if="fileType === 'pdf'" class="pdf-reader">
            <div class="pdf-content" ref="contentRef">
              <div class="pdf-page-container">
                <div class="pdf-page-wrapper" ref="pdfWrapper" @mousedown="handleMouseDown" @mouseup="handleMouseUp">
                  <div class="pdf-page" ref="pageRef" :style="{ transform: `scale(${scale})`, transformOrigin: 'top center' }">
                    <iframe
                      :src="pdfUrl"
                      class="pdf-iframe"
                      @load="onPdfLoad"
                      ref="pdfIframe"
                    ></iframe>
                  </div>
                </div>
              </div>
            </div>
          </div>
          
          <!-- 图片文件 -->
          <div v-else-if="fileType === 'image'" class="image-viewer">
            <div class="image-container" @wheel="handleImageWheel">
              <img 
                :src="pdfUrl" 
                :style="{ transform: `scale(${imageScale})` }"
                alt="图片预览"
              />
            </div>
          </div>
          
          <!-- 文本文件（纯文本） -->
          <div v-else-if="fileType === 'text'" class="text-viewer">
            <pre class="text-content">{{ textContent }}</pre>
          </div>
          
          <!-- 旧版 DOC 文件（mammoth 转换为 HTML） -->
          <div v-else-if="fileType === 'doc'" class="doc-viewer">
            <div class="doc-html-content" v-html="textContent"></div>
          </div>
          
          <!-- DOCX 文件 -->
          <div v-else-if="fileType === 'docx'" class="docx-viewer">
            <div class="docx-container" ref="docxContainer"></div>
          </div>
          
          <!-- XLSX/CSV 文件 -->
          <div v-else-if="fileType === 'xlsx' || fileType === 'csv'" class="xlsx-viewer">
            <div class="xlsx-container" v-if="sheetData.length > 0">
              <table class="xlsx-table">
                <thead>
                  <tr>
                    <th v-for="(cell, colIndex) in sheetData[0]" :key="colIndex">
                      {{ getColumnName(colIndex) }}
                    </th>
                  </tr>
                </thead>
                <tbody>
                  <tr v-for="(row, rowIndex) in sheetData" :key="rowIndex">
                    <td 
                      v-for="(cell, colIndex) in row" 
                      :key="colIndex"
                    >
                      {{ cell }}
                    </td>
                  </tr>
                </tbody>
              </table>
            </div>
            <div v-else class="empty-sheet">此工作表没有数据</div>
          </div>
          
          <!-- PPTX 文件 - 幻灯片展示 -->
          <div v-else-if="fileType === 'pptx'" class="pptx-viewer">
            <div class="pptx-slides-container">
              <div 
                v-for="(slide, index) in pptxSlides" 
                :key="slide.id"
                class="pptx-slide"
              >
                <div class="slide-header">
                  <span class="slide-number">幻灯片 {{ slide.number }}</span>
                </div>
                <div class="slide-content">
                  <ul v-if="slide.texts.length > 0">
                    <li v-for="(text, i) in slide.texts" :key="i">{{ text }}</li>
                  </ul>
                  <p v-else class="no-text">(此页无文本内容)</p>
                </div>
              </div>
              <div v-if="pptxSlides.length === 0" class="empty-pptx">
                无法解析此 PowerPoint 文件
              </div>
            </div>
          </div>
          
          <!-- Markdown 文件 - 带语法高亮 -->
          <div v-else-if="fileType === 'markdown'" class="markdown-viewer">
            <div class="markdown-container" v-html="markdownHtml"></div>
          </div>
          
          <!-- 代码文件 - 带语法高亮 -->
          <div v-else-if="fileType === 'code' || fileType === 'json' || fileType === 'xml'" class="code-viewer">
            <div class="code-container" v-html="textContent"></div>
          </div>
          
          <!-- EPUB 电子书 -->
          <div v-else-if="fileType === 'epub'" class="epub-viewer">
            <div id="epub-reader"></div>
            <div class="epub-controls">
              <button @click="prevPage" class="epub-btn">◀ 上一页</button>
              <button @click="nextPage" class="epub-btn">下一页 ▶</button>
            </div>
          </div>
          
          <!-- HTML 文件 - 沙盒模式 -->
          <div v-else-if="fileType === 'html'" class="html-viewer">
            <iframe
              :srcdoc="htmlContent"
              sandbox="allow-same-origin allow-scripts"
              class="html-iframe"
            ></iframe>
          </div>
          
          <!-- RTF 文件 - 纯文本显示 -->
          <div v-else-if="fileType === 'rtf'" class="rtf-viewer">
            <pre class="rtf-content">{{ rtfContent }}</pre>
          </div>
          
          <!-- MOBI/CHM 等不支持的格式 - 提供下载和提示 -->
          <div v-else-if="['mobi', 'chm'].includes(fileType)" class="unsupported-viewer">
            <div class="unsupported-icon">✕</div>
            <h2>{{ getFileTypeDescription(fileType) }} - 暂不支持在线预览</h2>
            <p class="format-info">当前文件格式: {{ fileExtension?.toUpperCase() || '未知' }}</p>
            <p class="conversion-hint">{{ getConversionSuggestion(fileExtension) }}</p>
            <button @click="downloadCurrentFile" class="download-btn">
              ↓ 下载文件查看
            </button>
          </div>

          <!-- 完全不支持的格式 -->
          <div v-else class="unsupported-viewer">
            <div class="unsupported-icon">?</div>
            <h2>暂不支持此格式预览</h2>
            <p>当前文件格式: {{ fileExtension || '未知' }}</p>
            <p class="supported-formats">
              支持的格式：PDF、DOC、DOCX、XLS、XLSX、PPT、PPTX、TXT、MD、JSON、XML、CSV、HTML、EPUB、图片等
            </p>
            <button @click="downloadCurrentFile" class="download-btn">
              ↓ 下载文件查看
            </button>
          </div>
        </div>
        
        <div class="sidebar" v-show="showSidebar">
          <div class="sidebar-header">
            <h3 class="sidebar-title">
              <span class="title-icon">✎</span>
              批注和高亮
              <span class="annotation-count">{{ allAnnotations.length }}</span>
            </h3>
          </div>
          
          <div class="sidebar-content">
            <div
              v-for="item in allAnnotations"
              :key="item.id"
              class="annotation-item"
              :class="{ active: selectedAnnotationId === item.id }"
              @click="selectedAnnotationId = item.id"
            >
              <div class="item-header">
                <div
                  class="item-color"
                  :style="{ backgroundColor: item.color }"
                ></div>
                <span class="item-time">{{ formatDate(item.createdAt) }}</span>
                <button @click.stop="deleteHighlight(item.id)" class="item-delete" title="删除">
                  ✕
                </button>
              </div>
              <div class="item-text">{{ item.text }}</div>
              <div v-if="item.annotation" class="item-annotation">
                <span class="annotation-label">批注：</span>
                {{ item.annotation }}
              </div>
              <div class="item-edit">
                <textarea
                  v-model="item.annotation"
                  placeholder="添加批注..."
                  class="annotation-textarea"
                  @blur="saveAnnotation(item)"
                  @click.stop
                ></textarea>
              </div>
            </div>
            
            <div v-if="allAnnotations.length === 0" class="empty-state">
              <div class="empty-icon">□</div>
              <p class="empty-text">暂无批注和高亮</p>
              <p class="empty-hint">选中文本后点击"高亮"按钮</p>
            </div>
          </div>
        </div>
      </div>
      
      <div v-if="showAnnotationModal" class="modal-overlay" @click.self="closeAnnotationModal">
        <div class="modal">
          <div class="modal-header">
            <h4 class="modal-title">添加批注</h4>
            <button @click="closeAnnotationModal" class="modal-close">✕</button>
          </div>
          <div class="modal-body">
            <div class="selected-text-preview">
              <span class="preview-label">高亮文本：</span>
              <p class="preview-text">{{ currentHighlight?.text }}</p>
            </div>
            <textarea
              v-model="annotationInput"
              placeholder="在此输入批注内容..."
              class="modal-textarea"
            ></textarea>
            <div v-if="currentHighlight?.annotation" class="existing-annotation">
              <span class="existing-label">当前批注：</span>
              <p class="existing-text">{{ currentHighlight.annotation }}</p>
            </div>
          </div>
          <div class="modal-footer">
            <button @click="closeAnnotationModal" class="modal-btn secondary">取消</button>
            <button @click="saveAnnotationModal" class="modal-btn primary">保存</button>
          </div>
        </div>
      </div>
      
      <!-- ===== 搜索结果导航栏 (新增) ===== -->
      <Transition name="slide-up">
        <div v-if="showNavigationControls" class="search-nav-bar">
          <div class="nav-info">
            <SearchOutlined class="nav-icon" />
            <span>找到 <strong>{{ searchResultCount }}</strong> 处匹配</span>
            <span class="keyword-preview">"{{ searchKeywordPreview }}"</span>
          </div>
          
          <div class="nav-actions">
            <button 
              class="nav-btn" 
              @click="prevHighlight"
              :disabled="currentHighlightIndex <= 0"
              title="上一处匹配"
            >
              <UpOutlined /> 上一处
            </button>
            
            <span class="nav-counter">
              {{ currentHighlightIndex + 1 }} / {{ searchResultCount }}
            </span>
            
            <button 
              class="nav-btn" 
              @click="nextHighlight"
              :disabled="currentHighlightIndex >= searchResultCount - 1"
              title="下一处匹配"
            >
              下一处 <DownOutlined />
            </button>
            
            <button 
              class="nav-btn close-btn" 
              @click="closeSearch"
              title="关闭搜索结果"
            >
              <CloseOutlined />
            </button>
          </div>
        </div>
      </Transition>
    </div>
  </div>
</template>

<script setup>
import { ref, computed, watch, nextTick, onMounted, onUnmounted } from 'vue'
import { useRouter, useRoute } from 'vue-router'
import { saveAs } from 'file-saver'
import { message } from 'ant-design-vue'
import { fileAPI } from '../api/file'
import * as XLSX from 'xlsx'
import { renderAsync } from 'docx-preview'
import mammoth from 'mammoth'
import { marked } from 'marked'
import hljs from 'highlight.js'
// 使用 epubjs（浏览器兼容版本）替代 epub.js
import ePub from 'epubjs'

// 新增导入：统一搜索引擎
import searchCoordinator from '@/utils/searchCoordinator'
import { SearchOutlined, UpOutlined, DownOutlined, CloseOutlined } from '@ant-design/icons-vue'

const router = useRouter()
const route = useRoute()

// 状态
const currentFile = ref(null)
const pdfUrl = ref('')
const fileName = ref('文件')
const fileExtension = ref('')
const fileType = ref('')
const scale = ref(1.0)
const selectedText = ref('')

// 多格式支持状态
const imageScale = ref(1)
const textContent = ref('')
const docxContainer = ref(null)
const sheetData = ref([])
const rawBlobData = ref(null)

// 新增格式状态
const pptxSlides = ref([])
const markdownHtml = ref('')
const epubBook = ref(null)
const epubLocation = ref(null)
const htmlContent = ref('')
const rtfContent = ref('')

const highlights = ref([])
const nextId = ref(1)
const isSpeaking = ref(false)
const synthesis = ref(null)
const selectedAnnotationId = ref(null)
const showAnnotationModal = ref(false)
const currentHighlight = ref(null)
const annotationInput = ref('')
const showColorPicker = ref(false)
const showSpeechControls = ref(false)
const colors = ['#FFEB3B', '#4CAF50', '#2196F3', '#F44336', '#9C27B0', '#FF9800']
const currentColor = ref('#FFEB3B')

const speechRate = ref(1.0)
const availableVoices = ref([])
const selectedVoice = ref(null)
const utterance = ref(null)
const showSidebar = ref(true)
const isLoading = ref(false)
const hasFileLoaded = ref(false)
const targetPage = ref(null)
const returnFrom = ref('')
const searchKeyword = ref('')

// ===== 搜索导航相关状态 (新增) =====
const showNavigationControls = ref(false)
const searchResultCount = ref(0)
const currentHighlightIndex = ref(0)
const searchKeywordPreview = ref('')

// PDF Reader 相关 refs
const contentRef = ref(null)
const scrollContainerRef = ref(null)
const pdfWrapper = ref(null)
const pageRef = ref(null)
const pdfIframe = ref(null)

// 文件类型检测 - 支持 15+ 种格式
const getFileType = (extension) => {
  if (!extension) return 'pdf'
  const ext = extension.toLowerCase()
  
  // PDF 格式
  if (ext === '.pdf') return 'pdf'
  
  // Office 文档
  if (ext === '.docx') return 'docx'
  if (ext === '.doc') return 'doc'
  if (['.xls', '.xlsx'].includes(ext)) return 'xlsx'
  if (['.ppt', '.pptx'].includes(ext)) return 'pptx'
  
  // 图片格式
  if (['.jpg', '.jpeg', '.png', '.gif', '.webp', '.bmp', '.svg', '.ico', '.tiff', '.tif'].includes(ext)) return 'image'
  
  // 文本和代码格式（带语法高亮）
  if (['.txt', '.log'].includes(ext)) return 'text'
  if (['.md', '.markdown'].includes(ext)) return 'markdown'
  if (['.json'].includes(ext)) return 'json'
  if (['.xml'].includes(ext)) return 'xml'
  if (['.csv'].includes(ext)) return 'csv'
  if (['.js', '.ts', '.jsx', '.tsx', '.vue', '.py', '.java', '.c', '.cpp', '.h', '.cs', '.go', '.rs', '.rb', '.php', '.sql', '.sh', '.bat', '.css', '.scss', '.less', '.html', '.htm', '.yaml', '.yml', '.toml', '.ini', '.cfg', '.conf'].includes(ext)) return 'code'
  
  // 电子书格式
  if (ext === '.epub') return 'epub'
  if (ext === '.mobi') return 'mobi'
  
  // 网页格式
  if (['.html', '.htm', '.xhtml'].includes(ext)) return 'html'
  
  // 富文本格式
  if (ext === '.rtf') return 'rtf'
  
  // 帮助文档
  if (ext === '.chm') return 'chm'
  
  return 'unsupported'
}

// 获取格式描述
const getFileTypeDescription = (type) => {
  const descriptions = {
    pdf: 'PDF 文档',
    docx: 'Word 文档',
    doc: '旧版 Word 文档 (.doc)',
    xlsx: 'Excel 表格',
    pptx: 'PowerPoint 演示文稿',
    image: '图片文件',
    text: '文本文件',
    markdown: 'Markdown 文档',
    json: 'JSON 数据',
    xml: 'XML 文档',
    csv: 'CSV 表格数据',
    code: '源代码文件',
    epub: 'EPUB 电子书',
    mobi: 'MOBI 电子书',
    html: '网页文件',
    rtf: 'RTF 富文本',
    chm: 'CHM 帮助文件',
    unsupported: '未知格式'
  }
  return descriptions[type] || '未知格式'
}

// 获取不支持的格式的转换建议
const getConversionSuggestion = (extension) => {
  const suggestions = {
    '.doc': '旧版 Word 格式，建议在 Microsoft Word 中打开后"另存为 .docx 格式"重新上传，以获得最佳预览效果。',
    '.mobi': '建议转换为 EPUB 格式以获得更好的阅读体验。可使用 Calibre、Kindle Preview 等工具转换。',
    '.chm': 'CHM 是 Windows 帮助文件格式，建议使用 HTML 替代或使用 CHM 阅读器打开。',
    '.rtf': '建议将 RTF 转换为 DOCX 或 PDF 格式，可使用 Microsoft Word 或 LibreOffice 打开并另存。'
  }
  return suggestions[extension.toLowerCase()] || '此格式暂不支持在线预览，您可以下载文件后使用专业软件打开。'
}

// 获取 Excel 列名
const getColumnName = (colIndex) => {
  let name = ''
  let index = colIndex
  while (index >= 0) {
    name = String.fromCharCode(65 + (index % 26)) + name
    index = Math.floor(index / 26) - 1
  }
  return name
}

// 图片缩放处理
const handleImageWheel = (e) => {
  e.preventDefault()
  const delta = e.deltaY > 0 ? -0.1 : 0.1
  imageScale.value = Math.max(0.1, Math.min(5, imageScale.value + delta))
}

// 渲染旧版 DOC 文件（使用 mammoth 转换）
const renderDocFile = async (blob) => {
  try {
    console.log('📄 开始渲染旧版 DOC 文件（mammoth 转换）...')
    const arrayBuffer = await blob.arrayBuffer()
    const result = await mammoth.convertToHtml({ arrayBuffer })
    
    if (result.value && result.value.trim()) {
      textContent.value = result.value
      hasFileLoaded.value = true
      console.log('✅ DOC 文件转换成功，长度:', result.value.length)
      
      if (result.messages && result.messages.length > 0) {
        console.log('⚠️ DOC 转换警告:', result.messages)
      }
    } else {
      throw new Error('mammoth 返回空内容，该 .doc 文件可能格式不兼容')
    }
  } catch (error) {
    console.error('❌ DOC 文件渲染失败:', error)
    textContent.value = `<div style="padding:20px;color:#666;">
      <p style="font-size:16px;font-weight:bold;margin-bottom:12px;">⚠️ 旧版 Word (.doc) 格式预览失败</p>
      <p>该文件为旧版 Word 二进制格式（.doc），浏览器端无法完全支持。</p>
      <p style="margin-top:8px;">建议：将文件另存为 <strong>.docx</strong> 格式后重新上传。</p>
      <p style="margin-top:12px;"><a :href="'#'" @click.prevent="handleDownloadDoc" style="color:#1890ff;">📥 下载原始文件</a></p>
    </div>`
    hasFileLoaded.value = true
    message.warning('旧版 .doc 格式预览受限，建议转换为 .docx 格式')
  }
}

// 渲染 DOCX 文件
const renderDocxFile = async (blob) => {
  try {
    console.log('📄 开始渲染 DOCX 文件...')
    await nextTick()
    
    if (docxContainer.value) {
      const arrayBuffer = await blob.arrayBuffer()
      await renderAsync(arrayBuffer, docxContainer.value, null, {
        className: 'docx-viewer-wrapper',
        inWrapper: true,
        ignoreWidth: false,
        ignoreHeight: false,
        ignoreFonts: false,
        breakPages: true,
        ignoreLastRenderedPageBreak: true,
        experimental: false,
        trimXmlDeclaration: true,
        useBase64URL: true,
        renderHeaders: true,
        renderFooters: true,
        renderFootnotes: true,
        renderEndnotes: true,
      })
      console.log('✅ DOCX 渲染成功')
    }
  } catch (error) {
    console.error('❌ DOCX 渲染失败:', error)
    message.error('Word 文档渲染失败')
  }
}

// 解析 XLSX 文件
const parseExcelFile = async (blob) => {
  try {
    console.log('📄 开始解析 XLSX 文件...')
    const arrayBuffer = await blob.arrayBuffer()
    const workbook = XLSX.read(arrayBuffer, { type: 'array' })
    
    const firstSheetName = workbook.SheetNames[0]
    const worksheet = workbook.Sheets[firstSheetName]
    sheetData.value = XLSX.utils.sheet_to_json(worksheet, { header: 1 })
    
    console.log('✅ XLSX 解析成功，数据行数:', sheetData.value.length)
  } catch (error) {
    console.error('❌ XLSX 解析失败:', error)
    message.error('Excel 文件解析失败')
  }
}

// 渲染 PPTX 文件
const renderPptxFile = async (blob) => {
  try {
    console.log('📄 开始解析 PPTX 文件...')
    const arrayBuffer = await blob.arrayBuffer()
    
    // 使用 JSZip 解析 PPTX（PPTX 本质是 ZIP 包）
    const JSZip = await import('jszip')
    const zip = await JSZip.loadAsync(arrayBuffer)
    
    // 获取幻灯片信息
    const presentationXml = await zip.file('ppt/presentation.xml')?.async('text')
    const slideFiles = Object.keys(zip.files).filter(name => 
      name.startsWith('ppt/slides/slide') && name.endsWith('.xml')
    ).sort()
    
    pptxSlides.value = []
    
    for (const slideFile of slideFiles) {
      const slideContent = await zip.file(slideFile)?.async('text')
      // 简单提取文本内容（实际项目可使用更完整的解析器）
      const textMatches = slideContent?.match(/<a:t>([^<]+)<\/a:t>/g) || []
      const texts = textMatches.map(t => t.replace(/<\/?a:t>/g, ''))
      
      if (texts.length > 0 || slideContent) {
        pptxSlides.value.push({
          id: slideFile,
          content: slideContent || '',
          texts: texts,
          number: parseInt(slideFile.match(/\d+/)?.[0] || '0')
        })
      }
    }
    
    console.log('✅ PPTX 解析成功，幻灯片数量:', pptxSlides.value.length)
  } catch (error) {
    console.error('❌ PPTX 解析失败:', error)
    message.error('PowerPoint 文件解析失败')
  }
}

// 渲染 Markdown 文件
const renderMarkdownFile = async (blob) => {
  try {
    console.log('📄 开始渲染 Markdown 文件...')
    const text = await blob.text()
    
    // 配置 marked
    marked.setOptions({
      highlight: function(code, lang) {
        if (lang && hljs.getLanguage(lang)) {
          try {
            return hljs.highlight(code, { language: lang }).value
          } catch (err) {}
        }
        return hljs.highlightAuto(code).value
      },
      breaks: true,
      gfm: true
    })
    
    markdownHtml.value = marked.parse(text)
    console.log('✅ Markdown 渲染成功，长度:', markdownHtml.value.length)
  } catch (error) {
    console.error('❌ Markdown 渲染失败:', error)
    message.error('Markdown 文件渲染失败')
  }
}

// 渲染代码文件（带语法高亮）
const renderCodeFile = async (blob, extension) => {
  try {
    console.log('📄 开始渲染代码文件...')
    const text = await blob.text()
    
    let language = extension.replace('.', '')
    
    // 语言别名映射
    const langMap = {
      'js': 'javascript',
      'ts': 'typescript',
      'py': 'python',
      'sh': 'bash',
      'yml': 'yaml',
      'md': 'markdown',
      'cs': 'csharp',
      'rb': 'ruby'
    }
    
    language = langMap[language] || language
    
    let highlighted
    
    if (language && hljs.getLanguage(language)) {
      highlighted = hljs.highlight(text, { language }).value
    } else {
      highlighted = hljs.highlightAuto(text).value
    }
    
    textContent.value = `<pre class="hljs"><code>${highlighted}</code></pre>`
    hasFileLoaded.value = true
    console.log('✅ 代码文件渲染成功')
  } catch (error) {
    console.error('❌ 代码文件渲染失败:', error)
    message.error('代码文件渲染失败')
  }
}

// 渲染 JSON 文件（带语法高亮和格式化）
const renderJsonFile = async (blob) => {
  try {
    console.log('📄 开始渲染 JSON 文件...')
    const text = await blob.text()
    const json = JSON.parse(text)
    const formatted = JSON.stringify(json, null, 2)
    const highlighted = hljs.highlight(formatted, { language: 'json' }).value
    textContent.value = `<pre class="hljs json-viewer"><code>${highlighted}</code></pre>`
    hasFileLoaded.value = true
    console.log('✅ JSON 文件渲染成功')
  } catch (error) {
    console.error('❌ JSON 文件渲染失败:', error)
    message.error('JSON 文件格式错误或解析失败')
  }
}

// 渲染 XML 文件（带语法高亮）
const renderXmlFile = async (blob) => {
  try {
    console.log('📄 开始渲染 XML 文件...')
    const text = await blob.text()
    const highlighted = hljs.highlight(text, { language: 'xml' }).value
    textContent.value = `<pre class="hljs xml-viewer"><code>${highlighted}</code></pre>`
    hasFileLoaded.value = true
    console.log('✅ XML 文件渲染成功')
  } catch (error) {
    console.error('❌ XML 文件渲染失败:', error)
    message.error('XML 文件渲染失败')
  }
}

// 渲染 EPUB 电子书
const renderEpubFile = async (blob) => {
  try {
    console.log('📄 开始加载 EPUB 电子书...')
    const url = URL.createObjectURL(blob)
    
    epubBook.value = ePub(url)
    epubLocation.value = epubBook.value.renderTo("epub-reader", {
      width: "100%",
      height: "100%",
      spread: "none"
    })
    
    await epubBook.value.ready
    await epubLocation.value.display()
    
    console.log('✅ EPUB 加载成功')
  } catch (error) {
    console.error('❌ EPUB 加载失败:', error)
    message.error('EPUB 电子书加载失败')
  }
}

// 渲染 HTML 文件（沙盒模式）
const renderHtmlFile = async (blob) => {
  try {
    console.log('📄 开始渲染 HTML 文件...')
    const text = await blob.text()
    htmlContent.value = text
    hasFileLoaded.value = true
    console.log('✅ HTML 文件渲染成功')
  } catch (error) {
    console.error('❌ HTML 文件渲染失败:', error)
    message.error('HTML 文件渲染失败')
  }
}

// 渲染 RTF 文件
const renderRtfFile = async (blob) => {
  try {
    console.log('📄 开始处理 RTF 文件...')
    const text = await blob.text()
    
    // 简单提取 RTF 中的纯文本内容
    let plainText = text
      .replace(/\\par\b/g, '\n')
      .replace(/\\[a-z]+\b(\s(-?\d+)?)?{([^}]*)}/gi, '')
      .replace(/[{}]/g, '')
      .replace(/\\'[0-9a-fA-F]{2}/g, '')
      .replace(/\\[^ ]+/g, '')
      .replace(/\n{3,}/g, '\n\n')
    
    rtfContent.value = plainText.trim() || '(无法解析此 RTF 文件)'
    hasFileLoaded.value = true
    console.log('✅ RTF 文件处理完成')
  } catch (error) {
    console.error('❌ RTF 文件处理失败:', error)
    message.error('RTF 文件处理失败')
  }
}

// 下载当前文件
const downloadCurrentFile = async () => {
  try {
    if (rawBlobData.value) {
      const url = URL.createObjectURL(rawBlobData.value)
      const a = document.createElement('a')
      a.href = url
      a.download = fileName.value || 'download'
      document.body.appendChild(a)
      a.click()
      document.body.removeChild(a)
      URL.revokeObjectURL(url)
    }
  } catch (err) {
    console.error('下载失败:', err)
    message.error('下载失败')
  }
}

// 计算属性
const allAnnotations = computed(() => {
  return [...highlights.value].sort((a, b) => {
    return new Date(b.createdAt) - new Date(a.createdAt)
  })
})

// 处理返回
const handleBack = () => {
  if (pdfUrl.value.startsWith('blob:')) {
    URL.revokeObjectURL(pdfUrl.value)
  }
  
  // 统一使用浏览器后退，保持历史记录正确
  // 如果是从QA来的，先保存状态到sessionStorage再后退
  if (returnFrom.value === 'qa') {
    const sid = route.query.sid || ''
    
    // 保存需要恢复的会话信息
    try {
      sessionStorage.setItem('qa_return_from_reader', JSON.stringify({
        sid,
        timestamp: Date.now(),
        from: 'reader'
      }))
      console.log('[READER] 💾 已保存QA会话恢复信息:', { sid })
    } catch (e) {
      console.warn('[READER] 保存会话信息失败:', e)
    }
  }
  
  // 统一使用浏览器原生后退
  router.back()
}

// 缩放等功能
function fitToWidth() {
  scale.value = 1.0
}

function fitToPage() {
  scale.value = 0.8
}

function zoomIn() {
  if (scale.value < 2) {
    scale.value = Math.min(2, scale.value + 0.1)
  }
}

function zoomOut() {
  if (scale.value > 0.3) {
    scale.value = Math.max(0.3, scale.value - 0.1)
  }
}

function toggleColorPicker() {
  showColorPicker.value = !showColorPicker.value
  showSpeechControls.value = false
}

function toggleSpeechControls() {
  showSpeechControls.value = !showSpeechControls.value
  showColorPicker.value = false
}

function selectColor(color) {
  currentColor.value = color
  showColorPicker.value = false
}

// 高亮功能
function addHighlight() {
  if (!selectedText.value) return
  
  const highlight = {
    id: nextId.value++,
    text: selectedText.value,
    color: currentColor.value,
    annotation: '',
    createdAt: new Date().toISOString(),
  }
  
  highlights.value.push(highlight)
  saveToStorage()
  
  currentHighlight.value = highlight
  annotationInput.value = ''
  showAnnotationModal.value = true
  
  selectedText.value = ''
}

function deleteHighlight(id) {
  highlights.value = highlights.value.filter((h) => h.id !== id)
  if (selectedAnnotationId.value === id) {
    selectedAnnotationId.value = null
  }
  saveToStorage()
}

function clearAll() {
  if (confirm('确定要清空所有高亮和批注吗？')) {
    highlights.value = []
    nextId.value = 1
    selectedAnnotationId.value = null
    localStorage.removeItem('pdf-reader-data')
  }
}

function closeAnnotationModal() {
  showAnnotationModal.value = false
  currentHighlight.value = null
  annotationInput.value = ''
}

function saveAnnotationModal() {
  if (currentHighlight.value) {
    currentHighlight.value.annotation = annotationInput.value
    saveToStorage()
  }
  closeAnnotationModal()
}

function saveAnnotation(item) {
  saveToStorage()
}

// 语音功能
function initSpeech() {
  try {
    if (!synthesis.value) {
      synthesis.value = window.speechSynthesis
    }
    
    const updateVoices = () => {
      try {
        availableVoices.value = synthesis.value.getVoices()
        if (availableVoices.value.length > 0 && !selectedVoice.value) {
          const chineseVoice = availableVoices.value.find(v => v.lang.includes('zh'))
          selectedVoice.value = chineseVoice || availableVoices.value[0]
        }
      } catch (e) {
        console.error('获取语音列表失败:', e)
      }
    }
    
    updateVoices()
    if (synthesis.value.onvoiceschanged !== undefined) {
      synthesis.value.onvoiceschanged = updateVoices
    }
  } catch (e) {
    console.error('初始化语音合成失败:', e)
  }
}

function toggleSpeech() {
  if (!synthesis.value) {
    initSpeech()
  }

  if (isSpeaking.value) {
    stopSpeech()
  } else {
    speakSelection()
  }
  showSpeechControls.value = false
}

function speakSelection() {
  try {
    if (!synthesis.value) {
      initSpeech()
    }

    const text = selectedText.value || '请先选择要朗读的文本'
    if (!text) return

    stopSpeech()

    utterance.value = new SpeechSynthesisUtterance(text)
    utterance.value.lang = 'zh-CN'
    utterance.value.rate = speechRate.value
    utterance.value.pitch = 1
    utterance.value.volume = 1
    
    if (selectedVoice.value) {
      utterance.value.voice = selectedVoice.value
    }

    utterance.value.onstart = () => {
      isSpeaking.value = true
    }

    utterance.value.onend = () => {
      isSpeaking.value = false
    }

    utterance.value.onerror = (event) => {
      console.error('语音合成错误:', event)
      isSpeaking.value = false
      message.error('语音合成失败，请检查浏览器设置')
    }

    synthesis.value.speak(utterance.value)
  } catch (e) {
    console.error('朗读失败:', e)
    isSpeaking.value = false
    message.error('朗读失败，请检查浏览器设置')
  }
}

function stopSpeech() {
  try {
    if (synthesis.value) {
      synthesis.value.cancel()
    }
  } catch (e) {
    console.error('停止朗读失败:', e)
  }
  isSpeaking.value = false
}

function testSpeech() {
  try {
    if (!synthesis.value) {
      initSpeech()
    }

    const testText = '这是一段测试语音，用于检查朗读功能是否正常工作。'
    const testUtterance = new SpeechSynthesisUtterance(testText)
    testUtterance.lang = 'zh-CN'
    testUtterance.rate = speechRate.value
    
    if (selectedVoice.value) {
      testUtterance.voice = selectedVoice.value
    }

    synthesis.value.speak(testUtterance)
  } catch (e) {
    console.error('测试朗读失败:', e)
    message.error('测试朗读失败，请检查浏览器设置')
  }
}

function increaseRate() {
  if (speechRate.value < 3) {
    speechRate.value = Math.min(3, speechRate.value + 0.1)
  }
}

function decreaseRate() {
  if (speechRate.value > 0.5) {
    speechRate.value = Math.max(0.5, speechRate.value - 0.1)
  }
}

// 存储功能
function saveToStorage() {
  try {
    localStorage.setItem(
      'pdf-reader-data',
      JSON.stringify({
        highlights: highlights.value,
        nextId: nextId.value,
      })
    )
  } catch (e) {
    console.error('保存失败:', e)
  }
}

function loadFromStorage() {
  try {
    const saved = localStorage.getItem('pdf-reader-data')
    if (saved) {
      const data = JSON.parse(saved)
      highlights.value = data.highlights || []
      nextId.value = data.nextId || 1
    }
  } catch (e) {
    console.error('加载失败:', e)
  }
}

function exportAnnotations() {
  const data = {
    version: '1.0.0',
    exportDate: new Date().toISOString(),
    highlights: JSON.parse(JSON.stringify(highlights.value)),
  }
  const blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' })
  saveAs(blob, 'pdf-annotations.json')
}

async function importAnnotations(event) {
  const file = event.target.files[0]
  if (!file) return

  try {
    const text = await file.text()
    const data = JSON.parse(text)
    highlights.value = data.highlights || []
    if (highlights.value.length > 0) {
      nextId.value = Math.max(...highlights.value.map((h) => h.id)) + 1
    }
    saveToStorage()
    message.success('导入成功')
  } catch (e) {
    message.error('导入失败，请确保文件格式正确')
  }
  event.target.value = ''
}

function formatDate(dateString) {
  return new Date(dateString).toLocaleString('zh-CN', {
    month: 'short',
    day: 'numeric',
    hour: '2-digit',
    minute: '2-digit',
  })
}

// PDF 处理
const pendingPageJump = ref(null)

function onPdfLoad() {
  nextTick(() => {
    console.log('PDF 加载完成')
    if (pendingPageJump.value && pdfIframe.value?.contentWindow) {
      const page = pendingPageJump.value
      pendingPageJump.value = null
      try {
        const iframeWin = pdfIframe.value.contentWindow
        if (iframeWin.PDFViewerApplication) {
          iframeWin.PDFViewerApplication.pdfViewer.currentPageNumber = page
          console.log('✅ PDF postMessage 跳转到第', page, '页')
        } else {
          iframeWin.postMessage({ type: 'jumpToPage', page }, '*')
          setTimeout(() => { try { iframeWin.location.hash = '#page=' + page } catch(e){} }, 300)
        }
      } catch (e) {
        console.warn('PDF 页码跳转失败:', e)
      }
    } else if (!pendingPageJump.value && targetPage.value && targetPage.value > 1) {
      const iframeWin = pdfIframe.value?.contentWindow
      if (iframeWin?.PDFViewerApplication) {
        iframeWin.PDFViewerApplication.pdfViewer.currentPageNumber = targetPage.value
      }
    }
  })
}

// ===== 统一定位引擎 =====
const SEARCHABLE_DOM_TYPES = ['text', 'markdown', 'code', 'json', 'xml', 'doc', 'docx', 'html']

const locateToReference = () => {
  const keyword = searchKeyword.value || ''
  const page = targetPage.value || null

  console.log('🎯 统一定位引擎启动:', { fileType: fileType.value, keyword: keyword.substring(0, 30), page })

  switch (fileType.value) {
    case 'pdf': return locateInPdf(page)
    case 'xlsx':
    case 'csv': return locateInExcel(keyword)
    case 'pptx': return locateInPptx(page, keyword)
    case 'epub': return locateInEpub(keyword)
    default:
      if (SEARCHABLE_DOM_TYPES.includes(fileType.value)) {
        return locateInDom(keyword, page)
      }
      return fallbackScroll(page)
  }
}

// PDF 定位 - 多策略：postMessage > hash > PDFViewerAPI
const locateInPdf = (page) => {
  if (!page || page <= 1) return
  nextTick(() => {
    const iframeWin = pdfIframe.value?.contentWindow
    if (!iframeWin) return

    // 策略1: 直接操作 PDFViewerApplication（最可靠）
    try {
      if (iframeWin.PDFViewerApplication) {
        iframeWin.PDFViewerApplication.pdfViewer.currentPageNumber = page
        console.log('✅ PDF PDFViewerApplication 跳转第', page, '页')
        return
      }
    } catch (e) {}

    // 策略2: postMessage
    try {
      iframeWin.postMessage({ type: 'gotoPage', pageNumber: page }, '*')
      console.log('✅ PDF postMessage 发送跳转命令')
      return
    } catch (e) {}

    // 策略3: location.hash
    try {
      iframeWin.location.hash = '#page=' + page
      console.log('✅ PDF hash 跳转第', page, '页')
    } catch (e) {
      console.warn('⚠️ PDF 所有跳转方式均失败')
    }
  })
}

// DOM 内容搜索定位 - 用于 text/markdown/code/json/xml/doc/docx/html
const locateInDom = (keyword, page) => {
  nextTick(() => {
    const container = scrollContainerRef.value
    if (!container) return

    container.querySelectorAll('.ref-highlight').forEach(el => {
      const parent = el.parentNode
      parent.replaceChild(document.createTextNode(el.textContent), el)
      parent.normalize()
    })

    if (keyword) {
      try {
        const walker = document.createTreeWalker(container, NodeFilter.SHOW_TEXT, {
          acceptNode: (node) => {
            const el = node.parentElement
            if (el?.closest('.ref-highlight')) return NodeFilter.FILTER_REJECT
            if (el?.closest('pre.hljs') || el?.tagName === 'PRE' || el?.classList?.contains('text-content') ||
                el?.classList?.contains('doc-html-content') || el?.classList?.contains('markdown-body')) {
              return NodeFilter.FILTER_ACCEPT
            }
            return node.textContent.length > 10 ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_REJECT
          }
        })
        let found = false
        let node
        while ((node = walker.nextNode()) && !found) {
          const idx = node.textContent.indexOf(keyword.substring(0, 50))
          if (idx !== -1) {
            const range = document.createRange()
            range.setStart(node, Math.max(0, idx - 20))
            range.setEnd(node, Math.min(node.textContent.length, idx + keyword.length + 50))
            const span = document.createElement('mark')
            span.className = 'ref-highlight'
            range.surroundContents(span)
            span.scrollIntoView({ behavior: 'smooth', block: 'center' })
            found = true
            console.log('✅ DOM 搜索定位成功:', keyword.substring(0, 30))
          }
        }
        if (found) return
      } catch (e) {
        console.warn('⚠️ DOM 搜索异常:', e.message)
      }
    }

    if (fileType.value === 'docx' || fileType.value === 'doc') {
      const strategies = [
        '.docx-viewer-wrapper .docx-wrapper > .docx',
        '.docx-viewer-wrapper .docx',
        '.docx-container .docx-wrapper > .docx',
        '.docx-container .docx',
        container === docxContainer.value ? '.docx-viewer-wrapper > div > div[class*="docx"]' : '',
        '.docx-viewer-wrapper > div > div',
      ]
      let pages = null
      for (const sel of strategies) {
        if (!sel) continue
        pages = container.querySelectorAll(sel)
        if (pages.length > 1) {
          console.log(`📄 DOCX 页面选择器命中: "${sel}" → ${pages.length} 个页面`)
          break
        }
      }
      if (!pages || pages.length <= 1) {
        const wrapper = container.querySelector('.docx-viewer-wrapper') || container.querySelector('.docx-wrapper') || container
        pages = wrapper.children
        if (pages.length > 1) console.log(`📄 DOCX 回退选择: wrapper.children → ${pages.length} 个子元素`)
      }

      if (pages && pages.length > 1 && page && page > 1) {
        const targetIdx = Math.min(page - 1, pages.length - 1)
        if (pages[targetIdx]) {
          pages[targetIdx].scrollIntoView({ behavior: 'smooth', block: 'start' })
          pages[targetIdx].style.boxShadow = '0 0 0 3px #ffc107'
          console.log(`✅ DOCX/DOC 按页码定位到第 ${page} 页（共 ${pages.length} 页, idx=${targetIdx}）`)
          setTimeout(() => { if (pages[targetIdx]) pages[targetIdx].style.boxShadow = '' }, 4000)
          return
        } else {
          console.warn(`⚠️ DOCX 第 ${page} 页不存在（共 ${pages.length} 页）`)
        }
      }

      const docxWrapper = container.querySelector('.docx-viewer-wrapper') || container.querySelector('.docx-wrapper') || docxContainer.value
      if (docxWrapper && (!pages || pages.length <= 1)) {
        const innerHTML = docxWrapper.innerHTML.substring(0, 500)
        const childCount = docxWrapper.children.length
        const childTags = Array.from(docxWrapper.children).slice(0, 5).map(c => `${c.tagName.toLowerCase()}.${Array.from(c.classList).join('.')}(children:${c.children.length})`).join(' | ')
        console.log(`📋 DOCX DOM 结构诊断:`, { scrollContainer: container.className, childCount, childTags, innerHTMLSnippet: innerHTML })

        const sectionEls = docxWrapper.querySelectorAll('section, [style*="page-break"], [style*="break-after"], [style*="break-before"], .section, .page')
        console.log(`📋 DOCX 分页元素: ${sectionEls.length} 个`, Array.from(sectionEls).slice(0, 5).map(e => e.tagName + '.' + e.className))

        if (page && page > 1 && docxWrapper.scrollHeight > 200) {
          const totalPagesEstimate = Math.max(page * 2, Math.ceil(docxWrapper.scrollHeight / 800))
          const ratio = (page - 1) / totalPagesEstimate
          const targetScroll = Math.min(ratio * docxWrapper.scrollHeight, docxWrapper.scrollHeight - 100)
          docxWrapper.scrollTo({ top: Math.max(targetScroll, 0), behavior: 'smooth' })
          docxWrapper.style.boxShadow = 'inset 0 0 0 3px #ffc107'
          console.log(`✅ DOCX 百分比估算滚动: 第${page}/${totalPagesEstimate}页 → scrollTop=${Math.round(targetScroll)}, 总高=${docxWrapper.scrollHeight}`)
          setTimeout(() => { if (docxWrapper) docxWrapper.style.boxShadow = '' }, 4000)
          return
        }
      }

      if (!keyword && fileName.value) {
        const titleTerm = fileName.value.replace(/\.(docx?|pdf|xlsx?|pptx?)$/i, '').trim()
        if (titleTerm.length >= 2) {
          try {
            const allTextNodes = container.querySelectorAll('h1, h2, h3, p, span')
            for (const el of allTextNodes) {
              if (el.textContent.includes(titleTerm)) {
                el.scrollIntoView({ behavior: 'smooth', block: 'center' })
                const mark = document.createElement('mark')
                mark.className = 'ref-highlight'
                const range = document.createRange()
                range.selectNodeContents(el)
                range.surroundContents(mark)
                console.log('✅ DOCX/DOC 标题兜底定位成功:', titleTerm)
                return
              }
            }
          } catch (e) {}
        }
      }
    }

    fallbackScroll(page)
  })
}

// Excel/CSV 单元格搜索定位
const locateInExcel = (keyword) => {
  nextTick(() => {
    if (!keyword) { fallbackScroll(targetPage.value); return }

    const rows = document.querySelectorAll('.xlsx-table tbody tr')
    let matchedRow = null
    for (let i = 0; i < rows.length; i++) {
      if (rows[i].textContent.includes(keyword.substring(0, 30))) {
        matchedRow = rows[i]
        break
      }
    }

    if (matchedRow) {
      matchedRow.style.backgroundColor = '#fff3cd'
      matchedRow.scrollIntoView({ behavior: 'smooth', block: 'center' })
      console.log('✅ Excel 行匹配定位成功')

      setTimeout(() => {
        if (matchedRow) matchedRow.style.backgroundColor = ''
      }, 5000)
    } else {
      console.log('⚠️ Excel 未找到匹配行，尝试页码滚动')
      fallbackScroll(targetPage.value)
    }
  })
}

// PPTX 幻灯片页码定位
const locateInPptx = (page, keyword) => {
  nextTick(() => {
    if (page && page > 1) {
      const slides = document.querySelectorAll('.pptx-slide')
      const targetIdx = Math.min(page - 1, slides.length - 1)
      if (slides[targetIdx]) {
        slides[targetIdx].scrollIntoView({ behavior: 'smooth', block: 'start' })
        slides[targetIdx].style.boxShadow = '0 0 0 3px #ffc107'
        console.log('✅ PPTX 幻灯片定位到第', page, '张')
        setTimeout(() => { if (slides[targetIdx]) slides[targetIdx].style.boxShadow = '' }, 3000)
        return
      }
    }

    if (keyword) {
      const allText = document.querySelectorAll('.pptx-slide li, .pptx-slide .slide-text')
      for (const el of allText) {
        if (el.textContent.includes(keyword.substring(0, 30))) {
          el.scrollIntoView({ behavior: 'smooth', block: 'center' })
          el.style.backgroundColor = '#fff3cd'
          console.log('✅ PPTX 文本匹配定位成功')
          setTimeout(() => { el.style.backgroundColor = '' }, 5000)
          return
        }
      }
    }

    console.log('⚠️ PPTX 无法精确定位')
  })
}

// EPUB 电子书定位
const locateInEpub = (keyword) => {
  nextTick(() => {
    if (!epubBook.value) return

    if (keyword) {
      epubBook.value.ready.then(() => {
        return epubBook.value.search(keyword).then((results) => {
          if (results && results.length > 0) {
            epubLocation.value.display(results[0].cfi).then(() => {
              console.log('✅ EPUB CFI 定位成功')
            }).catch(() => { fallbackScroll(null) })
          } else {
            console.log('⚠️ EPUB 搜索无结果')
            fallbackScroll(null)
          }
        }).catch(() => { fallbackScroll(null) })
      })
    } else if (targetPage.value && targetPage.value > 1) {
      try {
        const spineLength = epubBook.value.spine.length
        const targetSpineIdx = Math.min(targetPage.value - 1, spineLength - 1)
        epubBook.value.spine.get(targetSpineIdx).then((item) => {
          epubLocation.value.display(item.cfi).then(() => {
            console.log('✅ EPUB 按 spine 索引定位成功')
          }).catch(() => {})
        })
      } catch (e) {
        console.warn('⚠️ EPUB 定位失败:', e)
      }
    }
  })
}

// 通用回退：按页码估算滚动
const fallbackScroll = (page) => {
  nextTick(() => {
    const container = scrollContainerRef.value
    if (!container) return
    if (page && page > 1) {
      const estimatedScroll = (page - 1) * 60
      container.scrollTo({ top: Math.min(estimatedScroll, container.scrollHeight), behavior: 'smooth' })
      console.log('📄 回退滚动到估算位置:', estimatedScroll)
    } else {
      container.scrollTo({ top: 0, behavior: 'smooth' })
    }
  })
}

watch(hasFileLoaded, (loaded) => {
  if (loaded) {
    const delayMap = { pdf: 800, docx: 1200, doc: 1200, xlsx: 600, pptx: 800, epub: 1500, html: 400 }
    const delay = delayMap[fileType.value] || 400
    setTimeout(() => locateToReference(), delay)
  }
})

function handleMouseDown() {
  // 鼠标按下时的处理
}

function handleMouseUp() {
  setTimeout(() => {
    let text = ''
    
    try {
      const selection = window.getSelection()
      if (selection.rangeCount) {
        text = selection.toString().trim()
      }
      
      if (!text && pdfIframe.value && pdfIframe.value.contentWindow) {
        const iframeSelection = pdfIframe.value.contentWindow.getSelection()
        if (iframeSelection.rangeCount) {
          text = iframeSelection.toString().trim()
        }
      }
    } catch (e) {
      console.error('获取选中文本失败:', e)
    }
    
    if (text) {
      selectedText.value = text
    }
  }, 10)
}

function handleClickOutside(event) {
  if (!event.target.closest('.color-picker-wrapper') && !event.target.closest('.speech-controls')) {
    showColorPicker.value = false
    showSpeechControls.value = false
  }
}

// MIME 类型映射
const MIME_TYPES = {
  '.pdf': 'application/pdf',
  '.doc': 'application/msword',
  '.docx': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
  '.xls': 'application/vnd.ms-excel',
  '.xlsx': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
  '.ppt': 'application/vnd.ms-powerpoint',
  '.pptx': 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
  '.txt': 'text/plain',
  '.jpg': 'image/jpeg',
  '.jpeg': 'image/jpeg',
  '.png': 'image/png',
  '.gif': 'image/gif',
  '.webp': 'image/webp',
  '.bmp': 'image/bmp',
  '.svg': 'image/svg+xml',
  '.json': 'application/json',
  '.xml': 'application/xml',
  '.html': 'text/html',
  '.htm': 'text/html',
  '.csv': 'text/csv'
}

// 根据文件扩展名获取 MIME 类型
const getMimeType = (extension) => {
  if (!extension) return 'application/pdf'
  const ext = extension.toLowerCase().startsWith('.') ? extension.toLowerCase() : '.' + extension.toLowerCase()
  return MIME_TYPES[ext] || 'application/octet-stream'
}

// 加载文件
const loadFile = async (fileId, fileNameStr, extension, targetPage = null) => {
  isLoading.value = true
  hasFileLoaded.value = false
  pdfUrl.value = ''
  textContent.value = ''
  sheetData.value = []
  rawBlobData.value = null

  try {
    console.log('📄 开始加载文件:', { fileId, fileName: fileNameStr, extension, targetPage })
    
    // 设置文件信息
    fileExtension.value = extension || ''
    fileType.value = getFileType(extension)
    fileName.value = fileNameStr || '文件'
    
    // 获取文件内容
    const response = await fileAPI.previewFile(fileId)
    console.log('📄 API 响应:', response)
    console.log('📄 响应是 Blob 吗?:', response.data instanceof Blob)
    
    let blob = response.data
    
    if (!(blob instanceof Blob)) {
      const mimeType = getMimeType(extension)
      blob = new Blob([blob], { type: mimeType })
      console.log('📄 创建新 Blob，MIME 类型:', mimeType)
    }
    
    rawBlobData.value = blob
    
    console.log('📄 文件类型:', fileType.value)
    
    // 根据文件类型进行不同的处理
    if (fileType.value === 'pdf') {
      let pdfBlobUrl = URL.createObjectURL(blob)
      if (targetPage && targetPage > 1) {
        pdfBlobUrl = pdfBlobUrl + '#page=' + targetPage
        console.log('📄 PDF URL 附加页码:', targetPage)
      }
      pdfUrl.value = pdfBlobUrl
      hasFileLoaded.value = true
      console.log('✅ PDF 文件加载成功，URL:', pdfUrl.value)

    } else if (fileType.value === 'image') {
      pdfUrl.value = URL.createObjectURL(blob)
      imageScale.value = 1
      hasFileLoaded.value = true
      console.log('✅ 图片文件加载成功')
      
    } else if (fileType.value === 'text') {
      const text = await blob.text()
      textContent.value = text
      hasFileLoaded.value = true
      console.log('✅ 文本文件加载成功，长度:', text.length)
      
    } else if (fileType.value === 'doc') {
      isLoading.value = false
      await renderDocFile(blob)
      return
      
    } else if (fileType.value === 'docx') {
      isLoading.value = false
      hasFileLoaded.value = true
      await nextTick()
      await renderDocxFile(blob)
      return
      
    } else if (fileType.value === 'xlsx') {
      await parseExcelFile(blob)
      hasFileLoaded.value = true
      console.log('✅ Excel 文件解析完成')
      
    } else if (fileType.value === 'pptx') {
      isLoading.value = false
      hasFileLoaded.value = true
      await nextTick()
      await renderPptxFile(blob)
      
    } else if (fileType.value === 'markdown') {
      isLoading.value = false
      hasFileLoaded.value = true
      await renderMarkdownFile(blob)
      
    } else if (fileType.value === 'code') {
      isLoading.value = false
      await renderCodeFile(blob, extension)
      
    } else if (fileType.value === 'json') {
      isLoading.value = false
      await renderJsonFile(blob)
      
    } else if (fileType.value === 'xml') {
      isLoading.value = false
      await renderXmlFile(blob)
      
    } else if (fileType.value === 'csv') {
      // CSV 使用 XLSX 解析，但显示方式不同
      await parseExcelFile(blob)
      hasFileLoaded.value = true
      console.log('✅ CSV 文件解析完成')
      
    } else if (fileType.value === 'epub') {
      isLoading.value = false
      hasFileLoaded.value = true
      await nextTick()
      await renderEpubFile(blob)
      
    } else if (fileType.value === 'html') {
      isLoading.value = false
      await renderHtmlFile(blob)
      
    } else if (fileType.value === 'rtf') {
      isLoading.value = false
      await renderRtfFile(blob)
      
    } else if (['mobi', 'chm'].includes(fileType.value)) {
      // 不支持的格式 - 提供下载和提示
      hasFileLoaded.value = true
      console.warn(`⚠️ 暂不支持在线预览 ${extension} 格式`)
      
    } else {
      console.warn('⚠️ 不支持的文件类型:', fileType.value)
      pdfUrl.value = URL.createObjectURL(blob)
      hasFileLoaded.value = true
    }
    
    console.log('✅ 文件名:', fileName.value)
  } catch (error) {
    console.error('❌ 文件加载失败:', error)
    
    if (error.response?.status === 404) {
      message.error('文件不存在或已被删除')
    } else if (error.response?.status === 403) {
      message.error('没有权限访问此文件')
    } else if (error.code === 'ERR_NETWORK' || error.message.includes('Network Error')) {
      message.error('网络连接失败，请检查网络设置')
    } else {
      message.error(error.message || '文件加载失败')
    }
  } finally {
    isLoading.value = false
  }
}

// EPUB 电子书控制
const prevPage = () => {
  if (epubLocation.value) {
    epubLocation.value.prev()
  }
}

const nextPage = () => {
  if (epubLocation.value) {
    epubLocation.value.next()
  }
}

// ===== 搜索导航控制方法 (新增) =====
const nextHighlight = () => {
  searchCoordinator.nextHighlight()
}

const prevHighlight = () => {
  searchCoordinator.prevHighlight()
}

const closeSearch = () => {
  searchCoordinator.clearAllHighlights()
  showNavigationControls.value = false
  currentHighlightIndex.value = 0
  searchResultCount.value = 0
  searchKeywordPreview.value = ''
}

onMounted(() => {
  loadFromStorage()
  initSpeech()
  document.addEventListener('click', handleClickOutside)

  // 从路由参数获取文件信息
  const fileId = route.query.id
  const fileTitle = route.query.title
  const fileExtension = route.query.extension
  const page = route.query.page ? parseInt(route.query.page) : null
  const from = route.query.from || ''
  const keyword = route.query.keyword || ''

  targetPage.value = page
  returnFrom.value = from
  searchKeyword.value = keyword

  if (fileId) {
    loadFile(fileId, fileTitle, fileExtension, page)
    
    // ✨ 新增：如果有关键词，自动执行搜索高亮
    if (keyword?.trim()) {
      // 不同文件类型的DOM渲染延迟不同
      const renderDelays = {
        pdf: 1000,      // PDF需要较长时间渲染textLayer
        docx: 1500,     // Word需要mammoth.js解析
        pptx: 1200,     // PPTX需要pptxgenjs渲染
        xlsx: 800,      // Excel使用xlsx库解析
        epub: 2000,     // 电子书最慢
        html: 400,      // HTML较快
        default: 500    // 默认延迟
      }
      
      const delay = renderDelays[fileType.value] || renderDelays.default
      
      console.log(`⏳ 将在 ${delay}ms 后执行搜索: "${keyword.substring(0, 30)}"`)
      
      setTimeout(async () => {
        const container = scrollContainerRef.value
        if (!container) {
          console.warn('⚠️ 文档容器未找到，无法执行搜索')
          return
        }
        
        console.log('🎯 开始执行统一定位...')
        
        try {
          const result = await searchCoordinator.execute({
            keyword,
            fileType: fileType.value,
            container,
            targetPage: page
          })
          
          if (result.success) {
            // 更新UI状态
            showNavigationControls.value = true
            searchResultCount.value = result.resultCount
            searchKeywordPreview.value = keyword.length > 25 
              ? keyword.substring(0, 25) + '...' 
              : keyword
            
            // 显示成功提示
            message.success({
              content: `找到 ${result.resultCount} 处"${searchKeywordPreview.value}"相关内容`,
              duration: 4,
              key: 'searchSuccess'
            })
            
            console.log('✅ 搜索高亮完成!', result.stats)
          } else {
            // 显示警告提示（但不阻断用户）
            message.warning({
              content: result.message || '未能定位到具体位置',
              duration: 3,
              key: 'searchWarning'
            })
            
            console.log('⚠️ 搜索未找到结果:', result.message)
          }
        } catch (error) {
          console.error('❌ 搜索流程异常:', error)
          message.error({
            content: '搜索过程中发生错误',
            duration: 3,
            key: 'searchError'
          })
        }
      }, delay)
    }
  }

  // 监听高亮导航事件（由HighlightRenderer触发）
  const handleHighlightNavigate = (e) => {
    const { currentIndex, total } = e.detail
    currentHighlightIndex.value = currentIndex
    searchResultCount.value = total
  }

  window.addEventListener('highlightNavigate', handleHighlightNavigate)
})

onUnmounted(() => {
  stopSpeech()
  document.removeEventListener('click', handleClickOutside)
  if (pdfUrl.value && pdfUrl.value.startsWith('blob:')) {
    URL.revokeObjectURL(pdfUrl.value)
  }
  
  // 清理高亮相关（新增）
  window.removeEventListener('highlightNavigate', handleHighlightNavigate)
  searchCoordinator.clearAllHighlights()
})
</script>

<style scoped>
.reader-page {
  width: 100%;
  height: 100vh;
  overflow: hidden;
}

/* 无文件状态 - 引导界面 */
.empty-state-container {
  height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--bg-page);
}

.empty-state-content {
  text-align: center;
  padding: var(--space-2xl) var(--space-xl);
  background: var(--bg-card);
  border: 1px solid var(--border-light);
  border-radius: var(--radius-lg);
  max-width: 360px;
}

.empty-icon {
  color: var(--text-disabled);
  margin-bottom: var(--space-md);
}

.empty-title {
  font-size: var(--font-lg);
  font-weight: var(--weight-semibold);
  color: var(--text-primary);
  margin-bottom: var(--space-xs);
}

.empty-description {
  font-size: var(--font-sm);
  color: var(--text-tertiary);
  line-height: 1.5;
  margin-bottom: var(--space-lg);
}

.back-to-list-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-xs);
  padding: var(--space-sm) var(--space-lg);
  background: var(--color-primary);
  color: var(--text-inverse);
  border: none;
  border-radius: var(--radius-md);
  font-size: var(--font-sm);
  font-weight: var(--weight-medium);
  cursor: pointer;
  transition: all var(--transition-normal);
}

.back-to-list-btn:hover {
  background: var(--color-primary-hover);
}

/* 加载中状态 */
.loading-container {
  height: 100vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  background: var(--bg-page);
  gap: var(--space-lg);
}

.loading-spinner {
  width: 40px;
  height: 40px;
  border: 4px solid var(--border-light);
  border-top-color: var(--color-primary);
  border-radius: 50%;
  animation: spin 0.8s linear infinite;
}

@keyframes spin {
  to { transform: rotate(360deg); }
}

.loading-container p {
  color: var(--text-secondary);
  font-size: var(--font-lg);
}

.reader-container {
  height: 100vh;
  display: flex;
  flex-direction: column;
}

.reader-header {
  display: flex;
  align-items: center;
  gap: var(--space-md);
  padding: var(--space-sm) var(--space-lg);
  background: var(--bg-card);
  border-bottom: 1px solid var(--border-light);
  z-index: 100;
  flex-wrap: wrap;
}

.reader-header h2 {
  font-size: var(--font-lg);
  color: var(--text-primary);
  flex-shrink: 0;
  margin: 0;
}

.back-btn {
  background: var(--bg-container);
  color: var(--text-primary);
  padding: var(--space-xs) var(--space-md);
  font-size: var(--font-sm);
  border: none;
  border-radius: var(--radius-md);
  cursor: pointer;
  flex-shrink: 0;
  transition: background 0.15s ease;
}

.back-btn:hover {
  background: var(--bg-hover);
}

.reader-content {
  flex: 1;
  display: flex;
  overflow: hidden;
  position: relative;
}

.pdf-wrapper {
  flex: 1;
  overflow: auto;
  background: var(--bg-page);
}

.pdf-reader {
  width: 100%;
  min-height: 100%;
  display: flex;
  flex-direction: column;
}

.pdf-content {
  flex: 1;
  overflow: auto;
  padding: var(--space-2xl) var(--space-lg);
  display: flex;
  justify-content: center;
  align-items: flex-start;
  background: var(--bg-page);
}

.pdf-page-container {
  display: flex;
  justify-content: center;
  align-items: center;
}

.pdf-page-wrapper {
  position: relative;
  display: flex;
  justify-content: center;
  align-items: center;
}

.pdf-page {
  position: relative;
  background: var(--bg-card);
  border-radius: var(--radius-md);
  transition: transform 0.2s ease;
}

.pdf-iframe {
  display: block;
  border: none;
  border-radius: 6px;
  width: 1200px;
  height: 1600px;
  max-width: calc(100vw - 400px);
  max-height: calc(100vh - 120px);
}

.sidebar {
  width: 340px;
  background: var(--bg-card);
  border-left: 1px solid var(--border-light);
  display: flex;
  flex-direction: column;
  overflow: hidden;
  flex-shrink: 0;
}

.sidebar-header {
  padding: var(--space-md) var(--space-lg);
  border-bottom: 1px solid var(--border-light);
  background: var(--bg-container);
}

.sidebar-title {
  margin: 0;
  font-size: var(--font-md);
  font-weight: var(--weight-semibold);
  color: var(--text-primary);
  display: flex;
  align-items: center;
  gap: var(--space-sm);
}

.title-icon {
  font-size: var(--font-lg);
}

.annotation-count {
  font-size: var(--font-xs);
  font-weight: var(--weight-medium);
  color: var(--color-primary);
  background: var(--bg-active);
  padding: 2px var(--space-sm);
  border-radius: var(--radius-lg);
  margin-left: auto;
}

.sidebar-content {
  flex: 1;
  overflow-y: auto;
  padding: var(--space-md);
}

.annotation-item {
  background: var(--bg-container);
  border: 1px solid var(--border-light);
  border-radius: var(--radius-md);
  padding: var(--space-md);
  margin-bottom: var(--space-sm);
  transition: border-color 0.15s ease;
  cursor: pointer;
}

.annotation-item:hover {
  background: var(--bg-hover);
  border-color: var(--border-default);
}

.annotation-item.active {
  background: var(--bg-active);
  border-color: var(--color-primary);
}

.item-header {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  margin-bottom: var(--space-sm);
}

.item-color {
  width: 12px;
  height: 12px;
  border-radius: var(--radius-xs);
  flex-shrink: 0;
}

.item-time {
  font-size: var(--font-xs);
  color: var(--text-tertiary);
  margin-left: auto;
}

.item-delete {
  width: 22px;
  height: 22px;
  border: none;
  background: transparent;
  color: var(--text-disabled);
  cursor: pointer;
  border-radius: var(--radius-sm);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: var(--font-md);
  transition: all 0.2s;
}

.item-delete:hover {
  background: #fef0f0;
  color: #f56c6c;
}

.item-text {
  font-size: var(--font-sm);
  color: var(--text-primary);
  line-height: 1.6;
  margin-bottom: var(--space-sm);
  padding: var(--space-xs) var(--space-sm);
  background: var(--bg-card);
  border-radius: var(--radius-sm);
  border-left: 2px solid var(--color-primary);
}

.item-annotation {
  font-size: var(--font-xs);
  color: var(--text-secondary);
  line-height: 1.6;
  padding: var(--space-xs) var(--space-sm);
  background: var(--bg-active);
  border-radius: var(--radius-sm);
  margin-bottom: var(--space-sm);
}

.annotation-label {
  font-weight: var(--weight-semibold);
  color: var(--color-primary);
  margin-right: var(--space-xs);
}

.item-edit {
  margin-top: var(--space-xs);
}

.annotation-textarea {
  width: 100%;
  min-height: 52px;
  padding: var(--space-xs) var(--space-sm);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-sm);
  font-size: var(--font-xs);
  line-height: 1.5;
  resize: vertical;
  font-family: inherit;
  box-sizing: border-box;
  transition: border-color 0.15s ease;
}

.annotation-textarea:focus {
  outline: none;
  border-color: var(--color-primary);
}

.empty-state {
  text-align: center;
  padding: var(--space-3xl) var(--space-md);
  color: var(--text-tertiary);
}

.empty-icon {
  font-size: 48px;
  margin-bottom: 12px;
  opacity: 0.4;
}

.empty-text {
  font-size: 14px;
  margin-bottom: 6px;
  font-weight: 500;
}

.empty-hint {
  font-size: 13px;
  opacity: 0.7;
}

.modal-overlay {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background: rgba(0, 0, 0, 0.4);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 1000;
  backdrop-filter: blur(4px);
}

.modal {
  background: var(--bg-card);
  border-radius: var(--radius-lg);
  width: 440px;
  max-width: 90%;
  max-height: 80vh;
  overflow: hidden;
  box-shadow: var(--shadow-xl);
  display: flex;
  flex-direction: column;
}

.modal-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: var(--space-sm) var(--space-lg);
  border-bottom: 1px solid var(--border-light);
}

.modal-title {
  margin: 0;
  font-size: var(--font-lg);
  font-weight: 700;
  color: var(--text-primary);
}

.modal-close {
  width: 26px;
  height: 26px;
  border: none;
  background: var(--bg-container);
  color: var(--text-tertiary);
  cursor: pointer;
  border-radius: var(--radius-sm);
  font-size: var(--font-md);
  display: flex;
  align-items: center;
  justify-content: center;
  transition: all 0.2s;
}

.modal-close:hover {
  background: var(--bg-hover);
  color: var(--text-secondary);
}

.modal-body {
  padding: var(--space-lg);
  overflow-y: auto;
  flex: 1;
}

.selected-text-preview {
  margin-bottom: var(--space-sm);
}

.preview-label {
  font-size: var(--font-xs);
  font-weight: 600;
  color: var(--text-tertiary);
  display: block;
  margin-bottom: var(--space-xs);
}

.preview-text {
  padding: var(--space-xs) var(--space-sm);
  background: var(--bg-container);
  border-radius: var(--radius-md);
  color: var(--text-primary);
  font-size: var(--font-sm);
  line-height: 1.5;
  margin: 0;
  border-left: 2px solid var(--color-primary);
}

.modal-textarea {
  width: 100%;
  min-height: 80px;
  padding: var(--space-xs) var(--space-sm);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-md);
  font-size: var(--font-sm);
  line-height: 1.5;
  resize: vertical;
  font-family: inherit;
  box-sizing: border-box;
  transition: all 0.2s;
}

.modal-textarea:focus {
  outline: none;
  border-color: var(--color-primary);
  box-shadow: 0 0 0 2px var(--color-primary-alpha-15);
}

.existing-annotation {
  margin-top: var(--space-sm);
  padding: var(--space-xs) var(--space-sm);
  background: var(--bg-container);
  border-radius: var(--radius-md);
}

.existing-label {
  font-size: var(--font-xs);
  font-weight: 600;
  color: var(--text-tertiary);
  display: block;
  margin-bottom: var(--space-xs);
}

.existing-text {
  margin: 0;
  color: var(--text-secondary);
  font-size: var(--font-sm);
  line-height: 1.5;
}

.modal-footer {
  display: flex;
  justify-content: flex-end;
  gap: var(--space-sm);
  padding: var(--space-sm) var(--space-lg);
  border-top: 1px solid var(--border-light);
  background: var(--bg-container);
}

.modal-btn {
  padding: var(--space-xs) var(--space-md);
  border: none;
  border-radius: var(--radius-md);
  font-size: var(--font-sm);
  font-weight: 600;
  cursor: pointer;
  transition: all 0.2s;
}

.modal-btn.secondary {
  background: var(--bg-active);
  color: var(--text-secondary);
}

.modal-btn.secondary:hover {
  background: var(--border-light);
}

.modal-btn.primary {
  background: var(--color-primary);
  color: var(--text-inverse);
}

.modal-btn.primary:hover {
  background: var(--color-primary-hover);
}

.toolbar-group {
  display: flex;
  align-items: center;
  gap: 6px;
  flex-wrap: wrap;
}

.toolbar-divider {
  width: 1px;
  height: 32px;
  background: var(--border-light);
  margin: 0 var(--space-md);
}

.tool-btn {
  display: flex;
  align-items: center;
  gap: var(--space-xs);
  padding: var(--space-xs) var(--space-sm);
  border: none;
  border-radius: var(--radius-md);
  background: transparent;
  color: var(--text-secondary);
  font-size: var(--font-sm);
  font-weight: var(--weight-medium);
  cursor: pointer;
  transition: background 0.15s ease, color 0.15s ease;
  line-height: 1;
  position: relative;
}

.tool-btn:hover:not(:disabled) {
  background: var(--bg-hover);
  color: var(--color-primary);
}

.tool-btn:disabled {
  opacity: 0.4;
  cursor: not-allowed;
}

.tool-btn.primary {
  background: var(--color-primary);
  color: var(--text-inverse);
}

.tool-btn.primary:hover:not(:disabled) {
  background: var(--color-primary-hover);
}

.tool-btn.danger:hover:not(:disabled) {
  background: var(--bg-error);
  color: var(--color-error);
}

.tool-btn.active {
  background: var(--bg-active);
  color: var(--color-primary);
}

.tool-btn.icon-only {
  padding: var(--space-xs) var(--space-sm);
}

.tool-btn .icon {
  font-size: var(--font-md);
  line-height: 1;
}

.tool-btn .label {
  white-space: nowrap;
}

.zoom-container {
  display: flex;
  align-items: center;
  gap: var(--space-xs);
  background: var(--bg-container);
  padding: var(--space-xs) var(--space-sm);
  border-radius: var(--radius-md);
}

.zoom-divider {
  width: 1px;
  height: 20px;
  background: var(--border-light);
  margin: 0 var(--space-xs);
}

.zoom-level {
  color: var(--text-secondary);
  font-size: var(--font-sm);
  font-weight: var(--weight-semibold);
  min-width: 48px;
  text-align: center;
  user-select: none;
}

.color-picker-wrapper {
  position: relative;
}

.color-preview {
  width: 18px;
  height: 18px;
  border-radius: var(--radius-xs);
  border: 2px solid var(--border-light);
}

.color-picker-dropdown {
  position: absolute;
  top: calc(100% + var(--space-sm));
  left: 0;
  padding: var(--space-sm);
  background: var(--bg-card);
  border-radius: var(--radius-md);
  display: flex;
  gap: var(--space-xs);
  z-index: 100;
  border: 1px solid var(--border-light);
}

.speech-controls {
  position: relative;
  display: flex;
  align-items: center;
}

.speech-dropdown {
  position: absolute;
  top: calc(100% + var(--space-sm));
  right: 0;
  padding: var(--space-md);
  background: var(--bg-card);
  border-radius: var(--radius-md);
  z-index: 100;
  border: 1px solid var(--border-light);
  min-width: 200px;
}

.speech-control-item {
  margin-bottom: var(--space-md);
}

.speech-control-item:last-child {
  margin-bottom: 0;
}

.control-label {
  display: block;
  font-size: var(--font-xs);
  font-weight: var(--weight-medium);
  color: var(--text-secondary);
  margin-bottom: var(--space-xs);
}

.rate-control {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
}

.rate-btn {
  width: 28px;
  height: 28px;
  border: 1px solid var(--border-default);
  background: var(--bg-card);
  border-radius: var(--radius-sm);
  cursor: pointer;
  font-size: var(--font-md);
  font-weight: var(--weight-semibold);
  display: flex;
  align-items: center;
  justify-content: center;
  transition: border-color 0.15s ease, color 0.15s ease;
}

.rate-btn:hover {
  border-color: var(--color-primary);
  color: var(--color-primary);
}

.rate-value {
  flex: 1;
  text-align: center;
  font-size: var(--font-sm);
  font-weight: var(--weight-semibold);
  color: var(--text-secondary);
}

.voice-select {
  width: 100%;
  padding: var(--space-xs) var(--space-sm);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-sm);
  font-size: var(--font-sm);
  background: var(--bg-card);
  cursor: pointer;
  transition: border-color 0.15s ease;
}

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

.test-speech-btn {
  width: 100%;
  padding: var(--space-xs) var(--space-sm);
  border: 1px solid var(--color-primary);
  background: var(--bg-active);
  color: var(--color-primary);
  border-radius: var(--radius-sm);
  font-size: var(--font-sm);
  font-weight: var(--weight-medium);
  cursor: pointer;
  transition: background 0.15s ease, color 0.15s ease;
}

.test-speech-btn:hover {
  background: var(--color-primary);
  color: var(--text-inverse);
}

.dropdown-enter-active,
.dropdown-leave-active {
  transition: opacity 0.15s ease, transform 0.15s ease;
}

.dropdown-enter-from,
.dropdown-leave-to {
  opacity: 0;
  transform: translateY(-4px);
}

.color-option {
  width: 28px;
  height: 28px;
  border-radius: var(--radius-md);
  cursor: pointer;
  border: 2px solid transparent;
  transition: border-color 0.15s ease;
}

.color-option:hover {
  border-color: var(--text-tertiary);
}

.color-option.active {
  border-color: var(--color-primary);
}

.hidden-input {
  display: none;
}

/* ========== 多格式文件渲染样式 ========== */

/* 图片查看器 */
.image-viewer {
  width: 100%;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: auto;
  padding: var(--space-xl);
}

.image-container {
  max-width: 100%;
  max-height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
}

.image-container img {
  max-width: 100%;
  max-height: calc(100vh - 120px);
  object-fit: contain;
  border-radius: var(--radius-lg);
}

/* 文本查看器 */
.text-viewer {
  width: 100%;
  height: 100%;
  overflow: auto;
  padding: var(--space-2xl) var(--space-lg);
  background: var(--bg-card);
}

.text-content {
  font-family: 'Consolas', 'Monaco', 'Courier New', monospace;
  font-size: var(--font-lg);
  line-height: 1.6;
  color: var(--text-primary);
  white-space: pre-wrap;
  word-wrap: break-word;
  margin: 0;
}

/* 引用内容高亮标记 */
.ref-highlight {
  background-color: #fff3cd;
  color: inherit;
  padding: 1px 2px;
  border-radius: 2px;
  animation: highlight-pulse 1.5s ease-in-out;
}

@keyframes highlight-pulse {
  0% { background-color: #ffc107; }
  100% { background-color: #fff3cd; }
}

/* 旧版 DOC 文件查看器 */
.doc-viewer {
  width: 100%;
  height: 100%;
  overflow: auto;
  padding: var(--space-xl);
  background: var(--bg-page);
}

.doc-html-content {
  background: var(--bg-card);
  border-radius: var(--radius-md);
  padding: var(--space-lg);
  min-height: 200px;
  line-height: 1.8;
  color: var(--text-primary);
}

.doc-html-content :deep(h1),
.doc-html-content :deep(h2),
.doc-html-content :deep(h3) {
  margin-top: 16px;
  margin-bottom: 8px;
}

.doc-html-content :deep(p) {
  margin-bottom: 8px;
}

/* DOCX 查看器 */
.docx-viewer {
  width: 100%;
  height: 100%;
  overflow: auto;
  padding: var(--space-xl);
  background: var(--bg-page);
}

.docx-container {
  background: var(--bg-card);
  border-radius: var(--radius-md);
  min-height: calc(100vh - 160px);
  overflow-y: auto;
}

/* XLSX 查看器 */
.xlsx-viewer {
  width: 100%;
  height: 100%;
  overflow: auto;
  padding: var(--space-xl);
  background: var(--bg-page);
}

.xlsx-container {
  background: var(--bg-card);
  border-radius: var(--radius-lg);
  overflow: hidden;
  border: 1px solid var(--border-light);
}

.xlsx-table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--font-md);
}

.xlsx-table th,
.xlsx-table td {
  border: 1px solid var(--border-default);
  padding: var(--space-sm) var(--space-md);
  text-align: left;
}

.xlsx-table th {
  background: var(--bg-container);
  color: var(--text-primary);
  font-weight: var(--weight-semibold);
  position: sticky;
  top: 0;
  z-index: 1;
}

.xlsx-table tr:nth-child(even) {
  background-color: var(--bg-container);
}

.xlsx-table tr:hover {
  background-color: var(--bg-hover);
}

.empty-sheet {
  text-align: center;
  padding: var(--space-3xl) var(--space-lg);
  color: var(--text-disabled);
  font-size: var(--font-lg);
}

/* 不支持的格式 */
.unsupported-viewer {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  height: 100%;
  padding: var(--space-2xl);
  text-align: center;
}

.unsupported-icon {
  font-size: var(--font-3xl);
  margin-bottom: var(--space-lg);
}

.unsupported-viewer h2 {
  font-size: var(--font-xl);
  color: var(--text-primary);
  margin-bottom: var(--space-md);
}

.unsupported-viewer p {
  font-size: var(--font-lg);
  color: var(--text-secondary);
  margin-bottom: var(--space-lg);
}

.download-btn {
  display: inline-flex;
  align-items: center;
  gap: var(--space-sm);
  padding: var(--space-md) var(--space-xl);
  background: var(--color-primary);
  color: var(--text-inverse);
  border: none;
  border-radius: var(--radius-md);
  font-size: var(--font-md);
  font-weight: var(--weight-medium);
  cursor: pointer;
  transition: background 0.15s ease;
}

.download-btn:hover {
  background: var(--color-primary-hover);
}

/* ========== PPTX 幻灯片样式 ========== */
.pptx-viewer {
  width: 100%;
  height: 100%;
  overflow: auto;
  padding: var(--space-xl);
  background: var(--bg-page);
}

.pptx-slides-container {
  max-width: 960px;
  margin: 0 auto;
  display: flex;
  flex-direction: column;
  gap: var(--space-lg);
}

.pptx-slide {
  background: var(--bg-card);
  border-radius: var(--radius-lg);
  border: 1px solid var(--border-light);
  overflow: hidden;
}

.slide-header {
  background: var(--bg-container);
  color: var(--text-primary);
  padding: var(--space-md) var(--space-lg);
  font-weight: var(--weight-semibold);
}

.slide-content {
  padding: var(--space-lg);
  min-height: 200px;
}

.slide-content ul {
  list-style: none;
  padding: 0;
  margin: 0;
}

.slide-content li {
  padding: var(--space-xs) 0;
  font-size: var(--font-lg);
  line-height: 1.6;
  border-bottom: 1px solid var(--border-light);
}

.slide-content li:last-child {
  border-bottom: none;
}

.no-text {
  color: var(--text-disabled);
  text-align: center;
  padding: var(--space-2xl);
}

.empty-pptx {
  text-align: center;
  padding: var(--space-3xl);
  color: var(--text-disabled);
  font-size: var(--font-lg);
}

/* ========== Markdown 样式 ========== */
.markdown-viewer {
  width: 100%;
  height: 100%;
  overflow: auto;
  padding: var(--space-2xl) var(--space-3xl);
  background: var(--bg-card);
}

.markdown-container {
  max-width: 900px;
  margin: 0 auto;
  line-height: 1.7;
  font-size: var(--font-md);
  color: var(--text-primary);
}

.markdown-container :deep(h1),
.markdown-container :deep(h2),
.markdown-container :deep(h3) {
  margin-top: 1.5em;
  margin-bottom: 0.5em;
  color: var(--text-primary);
}

.markdown-container :deep(pre) {
  background: var(--bg-container);
  border: 1px solid var(--border-light);
  border-radius: var(--radius-md);
  padding: var(--space-lg);
  overflow-x: auto;
  font-size: var(--font-sm);
}

.markdown-container :deep(code) {
  background: var(--bg-container);
  padding: var(--space-xs) var(--space-sm);
  border-radius: var(--radius-sm);
  font-family: 'Consolas', 'Monaco', monospace;
  font-size: var(--font-sm);
}

.markdown-container :deep(blockquote) {
  border-left: 4px solid var(--border-medium);
  margin: 1em 0;
  padding-left: var(--space-lg);
  color: var(--text-secondary);
}

.markdown-container :deep(table) {
  border-collapse: collapse;
  width: 100%;
  margin: 1em 0;
}

.markdown-container :deep(th),
.markdown-container :deep(td) {
  border: 1px solid var(--border-default);
  padding: var(--space-sm) var(--space-md);
  text-align: left;
}

.markdown-container :deep(th) {
  background: var(--bg-container);
  font-weight: var(--weight-semibold);
}

/* ========== 代码文件样式（带语法高亮）========== */
.code-viewer {
  width: 100%;
  height: 100%;
  overflow: auto;
  padding: var(--space-xl);
  background: var(--bg-page);
}

.code-container {
  max-width: 1200px;
  margin: 0 auto;
  background: var(--bg-card);
  border-radius: var(--radius-lg);
  border: 1px solid var(--border-light);
  overflow: hidden;
}

.code-container :deep(.hljs) {
  padding: var(--space-xl) !important;
  margin: 0 !important;
  background: var(--bg-container) !important;
  font-family: 'Fira Code', 'Cascadia Code', Consolas, monospace;
  font-size: var(--font-md);
  line-height: 1.6;
  overflow-x: auto;
}

.code-container :deep(.json-viewer),
.code-container :deep(.xml-viewer) {
  max-height: calc(100vh - 140px);
  overflow-y: auto;
}

/* ========== EPUB 电子书样式 ========== */
.epub-viewer {
  width: 100%;
  height: 100%;
  display: flex;
  flex-direction: column;
  background: var(--bg-page);
}

#epub-reader {
  flex: 1;
  width: 100%;
  overflow: hidden;
}

.epub-controls {
  display: flex;
  justify-content: center;
  gap: var(--space-lg);
  padding: var(--space-lg);
  background: var(--bg-card);
  border-top: 1px solid var(--border-light);
}

.epub-btn {
  padding: var(--space-sm) var(--space-lg);
  background: var(--color-primary);
  color: var(--text-inverse);
  border: none;
  border-radius: var(--radius-md);
  cursor: pointer;
  font-size: var(--font-sm);
  font-weight: var(--weight-medium);
  transition: background 0.15s ease;
}

.epub-btn:hover {
  background: var(--color-primary-hover);
}

/* ========== HTML 查看器样式 ========== */
.html-viewer {
  width: 100%;
  height: 100%;
  overflow: auto;
  background: var(--bg-card);
}

.html-iframe {
  width: 100%;
  height: 100%;
  border: none;
  border-radius: var(--radius-sm);
}

/* ========== RTF 查看器样式 ========== */
.rtf-viewer {
  width: 100%;
  height: 100%;
  overflow: auto;
  padding: var(--space-2xl) var(--space-lg);
  background: var(--bg-card);
}

.rtf-content {
  font-family: 'Times New Roman', serif;
  font-size: var(--font-md);
  line-height: 1.8;
  color: var(--text-primary);
  white-space: pre-wrap;
  word-wrap: break-word;
  margin: 0;
}

/* ========== 不支持格式增强提示 ========== */
.format-info {
  font-size: var(--font-lg);
  color: var(--text-secondary);
  margin-bottom: var(--space-md);
}

.conversion-hint {
  font-size: var(--font-sm);
  color: var(--text-tertiary);
  line-height: 1.6;
  max-width: 500px;
  margin: 0 auto var(--space-lg);
  padding: var(--space-md);
  background: var(--bg-container);
  border-radius: var(--radius-md);
}

.supported-formats {
  font-size: var(--font-sm);
  color: var(--text-tertiary);
  margin-bottom: var(--space-lg);
}

/* ========================================
   统一搜索高亮样式系统 (新增)
   ======================================== */

/* ----- DOM类型高亮 (.search-highlight) ----- */
:deep(.search-highlight) {
  background: linear-gradient(135deg, #fff3cd 0%, #ffe69c 100%);
  border-bottom: 2px solid #ffc107;
  border-radius: 2px;
  padding: 1px 2px;
  margin: 0 1px;
  box-shadow: 0 1px 3px rgba(255, 193, 7, 0.3);
  cursor: pointer;
  position: relative;
  transition: all 0.2s ease;
  display: inline;
}

:deep(.search-highlight:hover) {
  background: linear-gradient(135deg, #ffe69c 0%, #ffd43b 100%);
  box-shadow: 0 2px 6px rgba(255, 193, 7, 0.5);
  transform: translateY(-1px);
}

:deep(.search-highlight.active) {
  background: linear-gradient(135deg, #ffd43b 0%, #fab005 100%);
  border-bottom: 3px solid #f59f00;
  box-shadow: 0 3px 8px rgba(245, 159, 0, 0.7);
  animation: highlight-glow 2s ease-in-out infinite;
}

/* 高亮序号标签 */
:deep(.highlight-label) {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  position: absolute;
  top: -18px;
  left: -4px;
  background: #ffc107;
  color: #212529;
  font-size: 10px;
  font-weight: bold;
  padding: 0 4px;
  border-radius: 8px 8px 0 8px;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
  z-index: 10;
  line-height: 16px;
  min-width: 16px;
  pointer-events: none;
}

:deep(.search-highlight.active .highlight-label) {
  background: #f59f00;
  color: white;
}

/* ----- 表格高亮 (.table-highlight) ----- */
:deep(.table-highlight) {
  background: linear-gradient(135deg, #d4edda 0%, #c3e6cb 100%) !important;
  border: 2px solid #28a745 !important;
  position: relative !important;
  animation: table-pulse 1.5s ease-in-out;
}

:deep(.table-highlight-badge) {
  position: absolute;
  top: -10px;
  right: -10px;
  background: #28a745;
  color: white;
  font-size: 10px;
  font-weight: bold;
  padding: 2px 6px;
  border-radius: 10px;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
  z-index: 10;
  pointer-events: none;
}

/* ========================================
   动画效果 (新增)
   ======================================== */

@keyframes highlight-glow {
  0%, 100% {
    box-shadow: 0 3px 8px rgba(245, 159, 0, 0.6);
  }
  50% {
    box-shadow: 0 4px 16px rgba(245, 159, 0, 0.9), 0 0 20px rgba(245, 159, 0, 0.4);
  }
}

@keyframes highlight-pulse {
  0% { transform: scale(1); opacity: 1; }
  50% { transform: scale(1.05); opacity: 0.8; }
  100% { transform: scale(1); opacity: 1; }
}

@keyframes table-pulse {
  0% {
    transform: scale(1);
    box-shadow: 0 2px 4px rgba(40, 167, 69, 0.4);
  }
  50% {
    transform: scale(1.02);
    box-shadow: 0 4px 12px rgba(40, 167, 69, 0.7);
  }
  100% {
    transform: scale(1);
    box-shadow: 0 2px 4px rgba(40, 167, 69, 0.4);
  }
}

/* ========================================
   搜索导航栏样式 (新增)
   ======================================== */

.search-nav-bar {
  position: fixed;
  bottom: 30px;
  left: 50%;
  transform: translateX(-50%);
  background: white;
  border-radius: 12px;
  box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
  padding: 12px 24px;
  display: flex;
  align-items: center;
  gap: 20px;
  z-index: 1000;
  border: 1px solid #e9ecef;
  animation: slide-up-fade-in 0.3s ease-out;
  max-width: calc(100vw - 40px);
}

.nav-info {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 14px;
  color: #495057;
  white-space: nowrap;
}

.nav-icon {
  color: #ffc107;
  font-size: 16px;
}

.keyword-preview {
  color: #868e96;
  font-style: italic;
  max-width: 150px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.nav-actions {
  display: flex;
  align-items: center;
  gap: 12px;
}

.nav-btn {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 6px 12px;
  border: 1px solid #dee2e6;
  background: white;
  border-radius: 6px;
  font-size: 13px;
  color: #495057;
  cursor: pointer;
  transition: all 0.2s ease;
  white-space: nowrap;
}

.nav-btn:hover:not(:disabled) {
  background: #f8f9fa;
  border-color: #ffc107;
  color: #f59f00;
}

.nav-btn:disabled {
  opacity: 0.4;
  cursor: not-allowed;
}

.nav-counter {
  font-size: 13px;
  font-weight: 600;
  color: #212529;
  min-width: 40px;
  text-align: center;
}

.close-btn {
  background: #fff3cd;
  border-color: #ffc107;
  color: #856404;
}

.close-btn:hover:not(:disabled) {
  background: #ffc107;
  color: white;
}

/* 导航栏过渡动画 */
.slide-up-enter-active,
.slide-up-leave-active {
  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}

.slide-up-enter-from,
.slide-up-leave-to {
  opacity: 0;
  transform: translateX(-50%) translateY(20px);
}

@keyframes slide-up-fade-in {
  from {
    opacity: 0;
    transform: translateX(-50%) translateY(20px);
  }
  to {
    opacity: 1;
    transform: translateX(-50%) translateY(0);
  }
}

/* ========================================
   响应式适配 (新增)
   ======================================== */

@media (max-width: 768px) {
  .search-nav-bar {
    padding: 10px 16px;
    gap: 12px;
    bottom: 20px;
  }
  
  .nav-info {
    font-size: 12px;
    gap: 4px;
  }
  
  .keyword-preview {
    max-width: 80px;
  }
  
  .nav-btn {
    padding: 5px 8px;
    font-size: 12px;
  }
  
  .nav-btn span:not(.nav-icon) {
    display: none;  /* 移动端隐藏文字，只显示图标 */
  }
}

/* 精简阅读器顶部栏：仅保留返回列表和文件名 */
.reader-header .toolbar-group,
.reader-header .toolbar-divider {
  display: none !important;
}

/* 隐藏批注侧边栏 */
.sidebar {
  display: none !important;
}
</style>
