<template>
  <div class="mobile-chat">
    <!-- 移动端AI问答页面 -->
    <MobileHeader title="AI问答" @back="goBack" @menu="showHistoryDrawer" />
    
    <div class="mobile-content">
      
      <!-- 通用历史记录抽屉 -->
      <MobileHistoryDrawer
        :visible="!isSending && !hasTypingMessage && showHistory"
        title="历史记录"
        :historyData="historyData"
        :loading="isLoadingHistory"
        @close="showHistory = false"
        @createNewTask="startNewChat"
        @handleHistoryItem="handleHistoryItem"
        @deleteHistoryItem="deleteHistoryItem"
      />
      <!-- 初始状态：AI助手介绍和功能卡片 -->
      <div v-if="!showChat" class="initial-content">
        <!-- AI助手介绍 -->
        <div class="ai-intro">
          <div class="ai-avatar">
            <img :src="aiAvatarIcon" alt="AI头像" class="ai-avatar-img">
          </div>
          <div class="ai-greeting">
            <h3>我是蜀道安全管理AI智能助手，您的得力帮手</h3>
            <p>我可以帮您处理这些事情</p>
          </div>
        </div>

        <!-- 功能卡片 -->
        <div class="function-cards">
          <div 
            v-for="(card, index) in functionCards" 
            :key="card.id || index"
            class="function-card" 
            @click="handleFunctionCard(card.function_title)"
          >
            <div class="card-header">
              <div class="card-icon">
                <img :src="getFunctionCardIcon(card.function_title)" :alt="card.function_title" class="card-icon-img">
              </div>
              <h4>{{ card.function_title }}</h4>
            </div>
            <div class="card-description">
              <p>{{ card.function_content }}</p>
            </div>
          </div>
          
          <!-- 如果没有数据，显示默认卡片 -->
          <div v-if="functionCards.length === 0" class="function-card" @click="handleFunctionCard('桥梁结构设计问题')">
            <div class="card-header">
              <div class="card-icon">
                <img :src="bridgeIcon" alt="桥梁结构设计问题" class="card-icon-img">
              </div>
              <h4>桥梁结构设计问题</h4>
            </div>
            <div class="card-description">
              <p>各类桥梁结构设计，计算与分析</p>
            </div>
          </div>
          <div v-if="functionCards.length === 0" class="function-card" @click="handleFunctionCard('桥梁施工技术咨询')">
            <div class="card-header">
              <div class="card-icon">
                <img :src="constructionIcon" alt="施工技术咨询" class="card-icon-img">
              </div>
              <h4>施工技术咨询</h4>
            </div>
            <div class="card-description">
              <p>桥梁施工方法，工艺与技术要点</p>
            </div>
          </div>
          <div v-if="functionCards.length === 0" class="function-card" @click="handleFunctionCard('材料与力学问题')">
            <div class="card-header">
              <div class="card-icon">
                <img :src="materialIcon" alt="材料与力学问题" class="card-icon-img">
              </div>
              <h4>材料与力学问题</h4>
            </div>
            <div class="card-description">
              <p>建筑材料性能与结构力学分析</p>
            </div>
          </div>
          <div v-if="functionCards.length === 0" class="function-card" @click="handleFunctionCard('规范标准查询')">
            <div class="card-header">
              <div class="card-icon">
                <img :src="standardIcon" alt="规范标准查询" class="card-icon-img">
              </div>
              <h4>规范标准查询</h4>
            </div>
            <div class="card-description">
              <p>行业规范，标准解读与应用</p>
            </div>
          </div>
        </div>
      </div>

      <!-- 聊天对话区域 -->
      <div v-else class="chat-messages">
        <div 
          v-for="(message, index) in chatMessages" 
          :key="index"
          :class="['message-item', message.type]"
        >
          <!-- 用户消息 -->
          <div v-if="message.type === 'user'" class="user-message">
            <div class="message-content">
              <!-- 文本内容 -->
              <div v-if="message.content" class="message-text">{{ message.content }}</div>
            </div>
            <div class="message-actions">
              <button class="action-btn copy-btn" @click="copyUserMessage(message)" title="复制">
                <img :src="copyIcon" alt="复制" class="action-icon">
              </button>
              <button class="action-btn edit-btn" @click="editUserMessage(message)" title="编辑">
                <img :src="editIcon" alt="编辑" class="action-icon">
              </button>
            </div>
          </div>
          
          <!-- AI消息 -->
          <div v-else-if="message.type === 'ai'" class="ai-message">
            <!-- 网络搜索胶囊 - 在输出框上方 -->
            <div v-if="message.webSearchRaw && message.webSearchRaw.total > 0" class="web-search-capsule-outer">
              <WebSearchCapsule
                :total="message.webSearchRaw.total"
                :results="message.webSearchRaw.results"
                :isExpanded="false"
                @toggle="handleWebSearchToggle(message.webSearchRaw)"
              />
                </div>
            
            <!-- AI消息主体 -->
            <div class="ai-message-main">
              <!-- AI头像 -->
              <div class="ai-avatar-small">
                <img :src="aiAvatarIcon" alt="AI" class="ai-icon">
              </div>
              
              <!-- 白色气泡容器 - 所有内容都在里面 -->
              <div class="message-content" :data-message-index="index" :ref="el => messageContentRefs[index] = el">
                <!-- AI响应内容 -->
                <div class="ai-response-content">
                  <!-- 进度统计卡片 -->
                  <div
                    v-if="message.showStats"
                    class="stats-card"
                  >
                <div class="stats-left">
                  <StatusAvatar 
                    :status="getAvatarStatus(message.currentStatus, message.progress)" 
                    :size="28"
                    class="stats-avatar"
                  />
                  <span v-html="message.statusMessage" class="status-text"></span>
                    </div>
                
                <!-- 进度条 -->
                <div v-if="message.progress < 100" class="progress-capsule-inline">
                  <div class="progress-bar-mini">
                    <div class="progress-fill" :style="{ width: message.progress + '%' }"></div>
                    <div class="progress-dot" :style="{ left: message.progress + '%' }"></div>
                  </div>
                  <span class="progress-percentage">{{ message.progress }}%</span>
                </div>
            </div>
            
                  <!-- 问题总结 -->
                  <div v-if="message.summary" class="question-summary">
                    <StreamMarkdown :content="message.summary" :streaming="false" />
              </div>
                  
                  <!-- 报告生成中的Loading动画 - 当还没有报告时显示 -->
                  <div v-if="message.isTyping && (!message.reports || message.reports.length === 0) && message.progress < 100" class="report-loading">
                    <span class="loading-text">AI正在思考中...</span>
                    <div class="thinking-animation">
                      <span class="dot"></span>
                      <span class="dot"></span>
                      <span class="dot"></span>
                    </div>
                  </div>
                  
                  <!-- 报告列表 -->
              <div v-if="message.reports && message.reports.length > 0" class="reports-list">
                <template v-for="(report, rIndex) in message.reports" :key="`${report.source_file}-${report.file_index}-${rIndex}`">
                  <!-- 类别标题 -->
                  <CategoryTitle
                    v-if="report.type === 'category_title'"
                    :category="report.category"
                    :number="report.number"
                    :count="report.count"
                    @toggle="(data) => handleCategoryToggle(index, data)"
                  />
                  <!-- 文件报告 -->
                  <FileReportCard
                    v-else-if="!report.type || report.type !== 'category_title'"
                    v-show="isCategoryExpanded(index, report.metadata?._displayCategory || report.metadata?.primary_category)"
                    :report="report"
                    @preview-file="handleFilePreview"
                  />
                </template>
                
                <!-- 分类下的Loading动画 -->
                <div v-if="message.isTyping && message.progress < 100 && hasOnlyCategoryTitles(message.reports)" class="report-loading">
                  <span class="loading-text">AI正在思考中...</span>
                  <div class="thinking-animation">
                    <span class="dot"></span>
                    <span class="dot"></span>
                    <span class="dot"></span>
                </div>
                </div>
              </div>
              
                  <!-- 网络搜索总结 -->
                  <div v-if="message.hasWebSearchResults && message.webSearchSummary">
                    <WebSearchSummary :summary="message.webSearchSummary" />
                  </div>
                  
                  <!-- AI文本内容（如果没有报告数据时显示） -->
                  <div v-if="!message.reports || message.reports.length === 0" class="ai-text">
                    <div v-if="message.displayContent && message.displayContent.length > 0" class="ai-markdown-content">
                      <div v-html="message.displayContent"></div>
                    </div>
                  </div>
                </div>
                
                <!-- 操作按钮 - 在白色气泡内 -->
                <div v-show="!message.isTyping && ((message.displayContent && message.displayContent.length > 0) || message.summary)" class="divider"></div>
                <div v-show="!message.isTyping && ((message.displayContent && message.displayContent.length > 0) || message.summary)" class="message-actions">
                  <div class="left-actions">
                    <button class="action-btn copy-btn" @click="copyAIMessage(message)" title="复制">
                      <img :src="copyIcon" alt="复制" class="action-icon">
                    </button>
                    <button class="action-btn regenerate-btn" @click="regenerateResponse(index)" :disabled="hasTypingMessage" title="重新生成">
                      <img :src="regenerateIcon" alt="重新生成" class="action-icon">
                    </button>
                    <button class="action-btn voice-btn" @click="handleVoiceRead(message)" :title="isSpeaking(message.id) ? '停止朗读' : '语音朗读'" :class="{ speaking: isSpeaking(message.id) }">
                      <img :src="voiceIcon" alt="语音朗读" class="action-icon">
                    </button>
                  </div>
                  <div class="right-actions">
                    <button 
                      class="action-btn thumbs-up-btn" 
                      :class="{ active: message.userFeedback === 'like' }"
                      @click="handleThumbsUp(message)"
                      :title="message.userFeedback === 'like' ? '取消点赞' : '点赞'"
                    >
                      <img :src="likeIcon" alt="点赞" class="action-icon">
                    </button>
                    <button 
                      class="action-btn thumbs-down-btn"
                      :class="{ active: message.userFeedback === 'dislike' }"
                      @click="handleThumbsDown(message)"
                      :title="message.userFeedback === 'dislike' ? '取消点踩' : '点踩'"
                    >
                      <img :src="dislikeIcon" alt="踩" class="action-icon">
                    </button>
                  </div>
                </div>
              </div>
            </div>
            
            <!-- 推荐问题Loading -->
            <div v-show="!message.isTyping && ((message.displayContent && message.displayContent.length > 0) || message.summary || (message.reports && message.reports.length > 0)) && isGettingRelatedQuestions && (relatedQuestionsMessageId === message.id || relatedQuestionsMessageId === message.ai_message_id) && aiRelatedQuestions.length === 0" class="related-questions-loading">
              <div class="thinking-animation">
                <span class="dot"></span>
                <span class="dot"></span>
                <span class="dot"></span>
              </div>
            </div>
            
            <!-- 猜你想问 -->
            <div v-show="!message.isTyping && ((message.displayContent && message.displayContent.length > 0) || message.summary || (message.reports && message.reports.length > 0)) && (relatedQuestionsMessageId === message.id || relatedQuestionsMessageId === message.ai_message_id) && aiRelatedQuestions.length > 0" class="related-questions">
              <div 
                v-for="(question, index) in aiRelatedQuestions" 
                :key="index"
                class="related-question-item" 
                @click="handleRelatedQuestion(question)"
              >
                <span>{{ question }}</span>
                <svg class="arrow-icon" viewBox="0 0 16 16" fill="none">
                  <path d="M6 4L10 8L6 12" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
                </svg>
              </div>
            </div>
          </div>
        </div>
      </div>

      <!-- 底部输入区域 -->
      <div class="chat-input-section">
        <div class="input-container">
          <div class="input-box">
            <!-- 联网搜索 -->
            <button 
              class="network-search-btn" 
              :class="{ active: isNetworkSearchEnabled }"
              @click="toggleNetworkSearch"
              :title="isNetworkSearchEnabled ? '关闭联网搜索' : '启用联网搜索'"
              :disabled="isSending || hasTypingMessage"
            >
              <div class="icon-container">
                <img :src="isNetworkSearchEnabled ? networkSearchIconOn : networkSearchIconOff" alt="联网搜索" class="action-icon">
              </div>
            </button>
            <div class="divider"></div>
            <input 
              type="text" 
              placeholder="请在此处发送消息"
              class="message-input"
              v-model="messageText"
              @keyup.enter="sendMessage"
              :disabled="isSending || hasTypingMessage"
              maxlength="2000"
            >
            <button class="voice-btn" @click="handleVoiceClick" :disabled="isSending || hasTypingMessage" :class="{ 'recording': isListening }">
              <div class="icon-container">
                <img :src="voiceInputIcon" alt="语音" class="action-icon">
                <div v-if="isListening" class="recording-indicator"></div>
              </div>
            </button>
            <div class="divider"></div>
            <button 
              v-if="!isSending" 
              class="send-btn" 
              @click="sendMessage" 
              :disabled="hasTypingMessage || !messageText.trim()"
            >
              <img :src="messageText.trim() ? sendIconFilled : sendIconEmpty" alt="发送" class="send-icon">
            </button>
            <button 
              v-else 
              class="send-btn stop-btn" 
              @click="handleStopGeneration"
              title="停止生成"
            >
              <span class="stop-text" style="color: #FF4D4F;">停止</span>
            </button>
          </div>
        </div>
      </div>
    </div>
    
    <!-- 移动端轻提示 -->
    <MobileToast
      :visible="showToast"
      :message="toastMessage"
      :duration="toastDuration"
      @close="showToast = false"
    />
    
    <!-- 删除确认弹窗 -->
    <DeleteConfirmModal
      :visible="showDeleteModal"
      :title="deleteConfirmTitle"
      :message="deleteConfirmMessage"
      @confirm="confirmDelete"
      @cancel="cancelDelete"
      @close="cancelDelete"
    />
    
    <!-- 网络搜索弹窗 -->
    <div v-if="showWebSearchModal" class="web-search-modal-overlay" @click="showWebSearchModal = false">
      <div class="web-search-modal" @click.stop>
        <div class="web-search-modal-header">
          <h3>联网搜索结果</h3>
          <button class="close-btn" @click="showWebSearchModal = false">✕</button>
        </div>
        <div class="web-search-modal-content">
          <div v-if="currentWebSearchData && currentWebSearchData.results" class="search-results">
            <div class="search-count">找到 {{ currentWebSearchData.total || currentWebSearchData.results.length }} 个相关结果</div>
            <div 
              v-for="(result, index) in currentWebSearchData.results" 
              :key="index" 
              class="search-result-item"
              @click="handleSearchResultClick(result)"
            >
              <div class="result-header">
                <div class="result-index">{{ index + 1 }}</div>
                <div class="result-title">{{ result.title }}</div>
              </div>
              <div class="result-content">{{ result.content || result.snippet }}</div>
              <div class="result-footer">
                <span class="result-url">{{ formatUrl(result.url || result.link) }}</span>
                <span v-if="result.score" class="result-score">{{ (result.score * 100).toFixed(1) }}%</span>
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>
    
    <!-- 网页预览弹窗 -->
    <div v-if="showWebPreview" class="web-preview-overlay" @click="showWebPreview = false">
      <div class="web-preview-modal" @click.stop>
        <div class="web-preview-header">
          <h3>{{ previewTitle }}</h3>
          <button class="close-btn" @click="showWebPreview = false">✕</button>
        </div>
        <div class="web-preview-content">
          <iframe v-if="previewUrl" :src="previewUrl" frameborder="0" class="preview-iframe"></iframe>
          <div v-else class="iframe-error">
            <p>无法加载网页预览</p>
            <button class="open-link-btn" @click="openInNewTab">在新标签页中打开</button>
          </div>
        </div>
      </div>
    </div>
    
    <!-- 文件预览弹窗 -->
    <div v-if="showFilePreview" class="file-preview-overlay" @click="showFilePreview = false">
      <div class="file-preview-modal" @click.stop>
        <div class="file-preview-header">
          <div class="header-left">
            <svg class="file-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor">
              <path d="M14,2H6A2,2 0 0,0 4,4V20A2,2 0 0,0 6,22H18A2,2 0 0,0 20,20V8L14,2M18,20H6V4H13V9H18V20Z" />
            </svg>
            <div class="header-text">
              <h3>文件预览</h3>
              <span v-if="previewFileName" class="file-name">{{ previewFileName }}</span>
            </div>
          </div>
          <button class="close-btn" @click="showFilePreview = false">✕</button>
        </div>
        <div class="file-preview-content">
          <div v-if="fileLoading" class="file-loading">
            <div class="loading-spinner"></div>
            <p>加载中...</p>
          </div>
          <div v-else-if="fileError" class="file-error">
            <svg class="error-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor">
              <path d="M12,2C17.53,2 22,6.47 22,12C22,17.53 17.53,22 12,22C6.47,22 2,17.53 2,12C2,6.47 6.47,2 12,2M15.59,7L12,10.59L8.41,7L7,8.41L10.59,12L7,15.59L8.41,17L12,13.41L15.59,17L17,15.59L13.41,12L17,8.41L15.59,7Z" />
            </svg>
            <p>{{ fileError }}</p>
          </div>
          <iframe
            v-else-if="previewFilePath"
            :src="previewFilePath"
            frameborder="0"
            class="file-iframe"
            @load="fileLoading = false"
            @error="fileError = '文件加载失败'"
          ></iframe>
          <div v-else class="file-empty">
            <svg class="empty-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor">
              <path d="M14,2H6A2,2 0 0,0 4,4V20A2,2 0 0,0 6,22H18A2,2 0 0,0 20,20V8L14,2M18,20H6V4H13V9H18V20Z" />
            </svg>
            <p>暂无预览内容</p>
          </div>
        </div>
      </div>
    </div>
    
  </div>
  
</template>

<script setup>
import { useRouter, useRoute } from 'vue-router'
import MobileHeader from '@/components/MobileHeader.vue'
import DeleteConfirmModal from '@/components/DeleteConfirmModal.vue'
import MobileToast from '@/components/MobileToast.vue'
import MobileHistoryDrawer from '@/components/MobileHistoryDrawer.vue'
import CategoryTitle from '@/components/CategoryTitle.vue'
import FileReportCard from '@/components/FileReportCard.vue'
import StreamMarkdown from '@/components/StreamMarkdown.vue'
import WebSearchCapsule from '@/components/WebSearchCapsule.vue'
import WebSearchSummary from '@/components/WebSearchSummary.vue'
import StatusAvatar from '@/components/StatusAvatar.vue'
import { ref, onMounted, watch, nextTick, computed, onBeforeUnmount, onActivated, onDeactivated } from 'vue'
import { apis } from '@/request/apis.js'
// ===== 已删除：getUserId - 不再需要，改用token =====
// import { getUserId } from '@/utils/userManager.js'
import { useSpeechRecognition } from '@/composables/useSpeechRecognition'
import { createSSEConnection, closeSSEConnection } from '@/utils/sse'
import { getApiPrefix } from '@/utils/apiConfig'
import { renderMarkdown } from '@/utils/markdown'
import { stopSSEStream, updateAIMessageContent } from '@/utils/api.js'
import { getToken, getTokenType } from '@/utils/auth.js'
import Vditor from 'vditor'
import 'vditor/dist/index.css'
import 'katex/dist/katex.min.css'

// 导入图片资源 - 与PC端保持一致
import aiAvatarIcon from '@/assets/Chat/29.png'
import bridgeIcon from '@/assets/Chat/4.png'
import constructionIcon from '@/assets/Chat/5.png'
import materialIcon from '@/assets/Chat/6.png'
import standardIcon from '@/assets/Chat/7.png'
import deleteIcon from '@/assets/AIWriting/8.png'
import sendIconEmpty from '@/assets/Chat/15.png'
import sendIconFilled from '@/assets/Chat/16.png'

// 对话操作图标
import copyIcon from '@/assets/AIWriting/5.png'
import editIcon from '@/assets/AIWriting/6.png'
import regenerateIcon from '@/assets/AIWriting/7.png'
import voiceIcon from '@/assets/AIWriting/9.png'
import likeIcon from '@/assets/AIWriting/10.png'
import dislikeIcon from '@/assets/AIWriting/11.png'

// 语音输入图标
import voiceInputIcon from '@/assets/Chat/18.png'

// 联网搜索图标
import networkSearchIconOn from '@/assets/Chat/24.png'
import networkSearchIconOff from '@/assets/Chat/25.png'


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

const goBack = () => {
  router.go(-1)
}

// 显示历史记录抽屉的方法
const showHistoryDrawer = () => {
  if (!isSending.value && !hasTypingMessage.value) {
    showHistory.value = true
  }
  // AI处理中时不执行任何操作，不记录点击意图
}


const showHistory = ref(false)

// 历史记录相关状态
const historyData = ref([])
const historyTotal = ref(0)
const isLoadingHistory = ref(false)

// 聊天相关状态
const showChat = ref(false)
const chatMessages = ref([])
const messageText = ref('')
const isSending = ref(false)
const ai_conversation_id = ref(0)

// 删除确认弹窗状态
const showDeleteModal = ref(false)
const deleteTargetItem = ref(null) // 要删除的历史记录项
const deleteType = ref('') // 删除类型：'history' 或 'message'

// 网络搜索弹窗
const showWebSearchModal = ref(false)
const currentWebSearchData = ref(null)

// 网页预览弹窗
const showWebPreview = ref(false)
const previewUrl = ref('')
const previewTitle = ref('')

// 文件预览相关
const showFilePreview = ref(false)
const previewFilePath = ref('')
const previewFileName = ref('')
const fileLoading = ref(false)
const fileError = ref('')

// 消息内容引用
const messageContentRefs = ref({})

// 语音识别功能
const {
  isSupported: speechSupported,
  isListening,
  transcript,
  error: speechError,
  startListening,
  stopListening,
  speakText,
  stopSpeaking
} = useSpeechRecognition()

// 语音合成相关状态
const speakingMessageId = ref(null)
const currentAudio = ref(null)
const audioQueue = ref([])
const isPlayingQueue = ref(false)

// Toast状态
const showToast = ref(false)
const toastMessage = ref('')
const toastDuration = ref(2000)

// 功能卡片和热点问题数据
const functionCards = ref([])
const hotQuestions = ref([])

// 联网搜索相关
const isNetworkSearchEnabled = ref(true) // 联网搜索是否启用，默认为true
const onlineSearchResults = ref({}) // 存储每个消息的联网搜索结果
const isGettingOnlineSearch = ref(false) // 是否正在获取联网搜索结果
const onlineSearchLoadingMessages = ref(new Set()) // 记录正在加载联网搜索的消息ID
const expandedOnlineSearchResults = ref({}) // 记录每个消息的联网搜索结果展开状态
const expandedSearchSources = ref({}) // 记录每个消息的搜索来源展开状态

// AI回复相关推荐问题
const aiRelatedQuestions = ref([]) // AI回复后的相关推荐问题
const isGettingRelatedQuestions = ref(false) // 是否正在获取相关推荐问题
const relatedQuestionsMessageId = ref(null) // 关联的消息ID
const isAIReplyProcessComplete = ref(false) // AI回复流程是否完成

// SSE连接管理
let sseConnection = null

// 报告生成相关状态
const categoryExpandStates = ref({}) // 分类展开状态
const currentQuestion = ref('') // 当前问题
const streamingReports = ref(new Map()) // 流式报告缓存
const reportTypewriters = new Map() // 存储每个报告字段的打字机定时器
const typewriterIntervals = new Map() // 存储打字机定时器

// 功能卡片图标计数器
let functionCardIconIndex = 0

// 计算属性 - 是否有正在打字的AI消息
const hasTypingMessage = computed(() => {
  const result = chatMessages.value.some(message => message.type === 'ai' && message.isTyping)
  console.log('hasTypingMessage计算:', result, '聊天消息:', chatMessages.value.map(m => ({ type: m.type, isTyping: m.isTyping })))
  return result
})

// 删除确认消息
const deleteConfirmMessage = computed(() => {
  if (deleteType.value === 'history') {
    const title = deleteTargetItem.value?.item?.title || ''
    return `确定要删除历史记录"${title}"吗？删除后将无法恢复。`
  } else if (deleteType.value === 'message') {
    return '确定要删除这条消息吗？删除后将无法恢复。'
  }
  return '确定要删除吗？删除后将无法恢复。'
})

// 删除确认标题
const deleteConfirmTitle = computed(() => {
  if (deleteType.value === 'history') {
    return '删除历史记录'
  } else if (deleteType.value === 'message') {
    return '删除消息'
  }
  return '删除确认'
})

// 格式化文件大小
const formatFileSize = (bytes) => {
  if (bytes === 0) return '0 Bytes'
  const k = 1024
  const sizes = ['Bytes', 'KB', 'MB', 'GB']
  const i = Math.floor(Math.log(bytes) / Math.log(k))
  return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]
}

// 处理文件标签格式的回显
const processFileDisplay = (text, file) => {
  if (!file) {
    // 如果没有文件对象，尝试从文本中提取文件信息
    return processFileDisplayFromText(text)
  }
  
  // 查找文件标签并替换为显示格式
  const fileDisplay = `
📄 文件信息：
文件名：${file.name}
文件大小：${formatFileSize(file.size)}
文件类型：${file.type}

📝 文件内容：
${file.content}

---
`
  
  // 替换文件标签为显示格式
  let processedText = text
    .replace(/<word>.*?<\/word>/gs, fileDisplay)
    .replace(/<filename>.*?<\/filename>/g, '')
    .replace(/<filesize>.*?<\/filesize>/g, '')
  
  return processedText
}

// 从文本中提取文件信息并转换为显示格式
const processFileDisplayFromText = (text) => {
  // 提取文件名
  const filenameMatch = text.match(/<filename>(.*?)<\/filename>/)
  const filename = filenameMatch ? filenameMatch[1] : '未知文件'
  
  // 提取文件大小
  const filesizeMatch = text.match(/<filesize>(.*?)<\/filesize>/)
  const filesize = filesizeMatch ? parseInt(filesizeMatch[1]) : 0
  
  // 提取文件内容
  const wordMatch = text.match(/<word>(.*?)<\/word>/s)
  const fileContent = wordMatch ? wordMatch[1].trim() : '无内容'
  
  // 创建文件显示格式
  const fileDisplay = `
📄 文件信息：
文件名：${filename}
文件大小：${formatFileSize(filesize)}
文件类型：${filename.endsWith('.docx') ? '.docx' : filename.endsWith('.doc') ? '.doc' : '未知'}

📝 文件内容：
${fileContent}

---
`
  
  // 替换文件标签为显示格式
  let processedText = text
    .replace(/<word>.*?<\/word>/gs, fileDisplay)
    .replace(/<filename>.*?<\/filename>/g, '')
    .replace(/<filesize>.*?<\/filesize>/g, '')
  
  return processedText
}

// 使用Vditor渲染Markdown内容
const renderWithVditor = (content) => {
  return new Promise((resolve) => {
    try {
      console.log('开始使用Vditor渲染，内容长度:', content.length)
      console.log('原始内容:', content)
      
      // 创建一个临时的DOM元素
      const tempDiv = document.createElement('div')
      tempDiv.style.display = 'none'
      document.body.appendChild(tempDiv)
      
      // 使用Vditor.preview方法渲染 - 简化配置
      Vditor.preview(tempDiv, content, {
        mode: 'light',
        markdown: {
          toc: false,
          mark: false,
          footnotes: false,
          autoSpace: false,
          fixTermTypo: false,
          chinesePunct: false,
          linkBase: '',
          linkPrefix: '',
          listStyle: false,
          paragraphBeginningSpace: false
        },
        theme: {
          current: 'light',
          path: 'https://cdn.jsdelivr.net/npm/vditor@3.10.9/dist/css/content-theme'
        },
        after: () => {
          // 获取Vditor渲染的结果并进行规范引用处理
          let html = tempDiv.innerHTML
          
          // 处理规范引用 - 将<file></file>标签内容转换为可点击的规范引用
          html = processStandardReferences(html)
          
          console.log('Vditor渲染完成，HTML长度:', html.length)
          console.log('HTML预览:', html.substring(0, 200) + '...')
          
          // 清理临时元素
          document.body.removeChild(tempDiv)
          
          // 等待DOM更新后绑定点击事件
          nextTick(() => {
            bindStandardReferenceEvents()
          })
          
          resolve(html)
        }
      })
      
    } catch (error) {
      console.error('Vditor渲染错误:', error)
      // 渲染失败时使用简单HTML转换
      const fallbackHtml = content.replace(/\n/g, '<br>')
      resolve(fallbackHtml)
    }
  })
}

// LaTeX公式转换函数
const convertLatexToDisplay = (text) => {
  if (!text) return text
  
  let convertedText = text
  
  // 希腊字母转换
  const greekLetters = {
    '\\alpha': 'α', '\\beta': 'β', '\\gamma': 'γ', '\\delta': 'δ', '\\epsilon': 'ε',
    '\\varepsilon': 'ε', '\\zeta': 'ζ', '\\eta': 'η', '\\theta': 'θ', '\\iota': 'ι',
    '\\kappa': 'κ', '\\lambda': 'λ', '\\mu': 'μ', '\\nu': 'ν', '\\xi': 'ξ',
    '\\pi': 'π', '\\rho': 'ρ', '\\sigma': 'σ', '\\tau': 'τ', '\\upsilon': 'υ',
    '\\phi': 'φ', '\\chi': 'χ', '\\psi': 'ψ', '\\omega': 'ω',
    '\\Gamma': 'Γ', '\\Delta': 'Δ', '\\Theta': 'Θ', '\\Lambda': 'Λ', '\\Xi': 'Ξ',
    '\\Pi': 'Π', '\\Sigma': 'Σ', '\\Upsilon': 'Υ', '\\Phi': 'Φ', '\\Psi': 'Ψ', '\\Omega': 'Ω'
  }
  
  // 转换希腊字母
  for (const [latex, symbol] of Object.entries(greekLetters)) {
    const regex = new RegExp(latex.replace(/\\/g, '\\\\'), 'g')
    convertedText = convertedText.replace(regex, symbol)
  }
  
  // 分数转换 \frac{a}{b} → a/b
  convertedText = convertedText.replace(/\\frac\{([^}]+)\}\{([^}]+)\}/g, '$1/$2')
  
  // 根号转换 \sqrt{a} → √a
  convertedText = convertedText.replace(/\\sqrt\{([^}]+)\}/g, '√$1')
  
  // 积分符号转换
  convertedText = convertedText.replace(/\\int/g, '∫')
  convertedText = convertedText.replace(/\\sum/g, '∑')
  convertedText = convertedText.replace(/\\prod/g, '∏')
  convertedText = convertedText.replace(/\\partial/g, '∂')
  convertedText = convertedText.replace(/\\nabla/g, '∇')
  convertedText = convertedText.replace(/\\infty/g, '∞')
  
  // 其他数学符号
  convertedText = convertedText.replace(/\\pm/g, '±')
  convertedText = convertedText.replace(/\\times/g, '×')
  convertedText = convertedText.replace(/\\div/g, '÷')
  convertedText = convertedText.replace(/\\leq/g, '≤')
  convertedText = convertedText.replace(/\\geq/g, '≥')
  convertedText = convertedText.replace(/\\neq/g, '≠')
  convertedText = convertedText.replace(/\\approx/g, '≈')
  convertedText = convertedText.replace(/\\equiv/g, '≡')
  convertedText = convertedText.replace(/\\propto/g, '∝')
  
  // 集合符号
  convertedText = convertedText.replace(/\\in/g, '∈')
  convertedText = convertedText.replace(/\\notin/g, '∉')
  convertedText = convertedText.replace(/\\subset/g, '⊂')
  convertedText = convertedText.replace(/\\supset/g, '⊃')
  convertedText = convertedText.replace(/\\cup/g, '∪')
  convertedText = convertedText.replace(/\\cap/g, '∩')
  convertedText = convertedText.replace(/\\emptyset/g, '∅')
  
  // 逻辑符号
  convertedText = convertedText.replace(/\\land/g, '∧')
  convertedText = convertedText.replace(/\\lor/g, '∨')
  convertedText = convertedText.replace(/\\neg/g, '¬')
  convertedText = convertedText.replace(/\\rightarrow/g, '→')
  convertedText = convertedText.replace(/\\leftarrow/g, '←')
  convertedText = convertedText.replace(/\\leftrightarrow/g, '↔')
  convertedText = convertedText.replace(/\\forall/g, '∀')
  convertedText = convertedText.replace(/\\exists/g, '∃')
  
  console.log('LaTeX转换:', text, '→', convertedText)
  return convertedText
}

// 处理AI回复中的特殊字符和表情符号
const processAIResponse = (text) => {
  if (!text) return text
  
  console.log('原始AI回复:', text)
  console.log('原始文本长度:', text.length)
  console.log('原始文本字符码:', Array.from(text).map(char => char.charCodeAt(0)))
  
  try {
    // 方法1：尝试直接解码
    if (text.includes('%')) {
      const decoded = decodeURIComponent(text)
      console.log('URL解码后:', decoded)
      text = decoded
    }
    
    // 方法2：处理可能的编码问题
    if (text.includes('??')) {
      // 如果包含问号，尝试修复编码
      const cleaned = text.replace(/\?\?/g, '')
      console.log('清理问号后:', cleaned)
      text = cleaned
    }
    
    // 方法3：处理Unicode转义序列
    if (text.includes('\\u')) {
      const unicodeDecoded = text.replace(/\\u[\dA-F]{4}/gi, (match) => {
        return String.fromCharCode(parseInt(match.replace(/\\u/g, ''), 16))
      })
      console.log('Unicode解码后:', unicodeDecoded)
      text = unicodeDecoded
    }
    
    // 方法4：处理HTML实体（包括&lt;、&gt;等）
    if (text.includes('&')) {
      console.log('检测到&符号，可能包含HTML实体')
      const textarea = document.createElement('textarea')
      textarea.innerHTML = text
      const htmlDecoded = textarea.value
      console.log('HTML解码后:', htmlDecoded)
      
      // 检查是否解码成功
      if (htmlDecoded !== text) {
        console.log('HTML实体解码成功，内容已变化')
        text = htmlDecoded
      } else {
        console.log('HTML实体解码未生效，可能不是HTML实体')
      }
    }
    
    // 方法5：处理其他可能的编码问题
    if (text.includes('\uFFFD')) {
      console.log('检测到替换字符，尝试修复')
      // 替换字符通常表示编码错误
      const fixed = text.replace(/\uFFFD/g, '')
      console.log('修复替换字符后:', fixed)
      text = fixed
    }
    
    // 方法6：LaTeX公式转换（新增）
    const latexConverted = convertLatexToDisplay(text)
    console.log('LaTeX转换后:', latexConverted)
    
    console.log('最终处理结果:', latexConverted)
    return latexConverted
    
  } catch (error) {
    console.error('处理AI回复时出错:', error)
    return text
  }
}

// 处理规范引用 - 将<file></file>标签内容转换为可点击的规范引用
const processStandardReferences = (html) => {
  if (!html) return html
  
  console.log('开始处理规范引用，HTML长度:', html.length)
  
  // 处理<file></file>标签为可点击的标准引用
  const processedHtml = html.replace(/<file>(.*?)<\/file>/g, (match, content) => {
    console.log('发现文件引用:', content)
    
    // 检查是否已经是处理过的规范引用
    if (/^<span\s+class="standard-reference"/i.test(content)) {
      return match
    }
    
    // 检查是否是标准格式：书名号+内容+括号+编号
    const standardMatch = content.match(/^([《「『【]?[\s\S]*?[》」』】]?)[\s]*\(([^)]+)\)$/)
    if (standardMatch) {
      const standardName = standardMatch[1]
      const standardNumber = standardMatch[2]
      console.log('标准格式规范:', { standardName, standardNumber })
      return `<span class="standard-reference" data-standard="${content}" data-name="${standardName}" data-number="${standardNumber}" title="点击查看标准详情" style="background-color: #EAEAEE; color: #616161; font-size: 0.75rem; padding: 3px 8px; border-radius: 6px; cursor: pointer; display: inline-block; margin: 4px 2px; border: 1px solid #EAEAEE; font-weight: 500; transition: all 0.2s ease; line-height: 1.4;">${content}</span>`
    }
    
    // 普通文件引用格式
    console.log('普通文件引用格式:', content)
    return `<span class="standard-reference" data-reference="${content}" title="点击查看详情" style="background-color: #EAEAEE; color: #616161; font-size: 0.75rem; padding: 3px 8px; border-radius: 6px; cursor: pointer; display: inline-block; margin: 4px 2px; border: 1px solid #EAEAEE; font-weight: 500; transition: all 0.2s ease; line-height: 1.4;">${content}</span>`
  })
  
  console.log('规范引用处理完成')
  return processedHtml
}

// 将Markdown格式转换为HTML格式
const markdownToHtml = (text) => {
  if (!text) return text
  
  console.log('开始转换Markdown:', text)
  
  let html = text
  
  // 检查是否包含Markdown格式
  const hasMarkdown = /(?:^|<br>)#{1,6}\s*/.test(html) || /\*\*.*?\*\*/.test(html) || /^\s*[-*]\s+/.test(html)
  console.log('Markdown格式检测结果:', hasMarkdown)
  
  // 检查是否已经包含HTML标签（如<strong>、<em>等）
  const hasHtmlTags = /<[^>]*>/.test(html)
  console.log('HTML标签检测结果:', hasHtmlTags)
  
  // 如果包含Markdown格式，进行转换
  if (hasMarkdown) {
    console.log('检测到Markdown格式，进行Markdown转换')
    
    // 转换标题
    html = html.replace(/^#{6}\s*(.+)$/gm, '<h6>$1</h6>')
    html = html.replace(/^#{5}\s*(.+)$/gm, '<h5>$1</h5>')
    html = html.replace(/^#{4}\s*(.+)$/gm, '<h4>$1</h4>')
    html = html.replace(/^#{3}\s*(.+)$/gm, '<h3>$1</h3>')
    html = html.replace(/^#{2}\s*(.+)$/gm, '<h2>$1</h2>')
    html = html.replace(/^#{1}\s*(.+)$/gm, '<h1>$1</h1>')
    
    // 转换加粗
    html = html.replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>')
    
    // 转换斜体
    html = html.replace(/\*(.*?)\*/g, '<em>$1</em>')
    
    // 转换列表
    html = html.replace(/^\s*[-*]\s+(.+)$/gm, '<li>$1</li>')
    html = html.replace(/(<li>.*<\/li>)/s, '<ul>$1</ul>')
    
    // 转换代码
    html = html.replace(/`(.*?)`/g, '<code>$1</code>')
    
    console.log('Markdown转换完成:', html)
  } else if (hasHtmlTags) {
    console.log('检测到HTML标签，跳过Markdown转换')
  } else {
    console.log('未检测到特殊格式，保持原文本')
  }
  
  // 处理换行
  html = html.replace(/\n/g, '<br>')
  
  console.log('最终HTML:', html)
  return html
}

// 格式化时间函数
const formatTime = (timestamp) => {
  if (!timestamp) return ''
  const date = new Date(timestamp)
  const now = new Date()
  const diff = now - date
  
  // 如果是今天
  if (diff < 24 * 60 * 60 * 1000 && date.getDate() === now.getDate()) {
    return date.toLocaleTimeString('zh-CN', { 
      hour: '2-digit', 
      minute: '2-digit' 
    })
  }
  
  // 如果是昨天
  if (diff < 48 * 60 * 60 * 1000 && date.getDate() === now.getDate() - 1) {
    return '昨天 ' + date.toLocaleTimeString('zh-CN', { 
      hour: '2-digit', 
      minute: '2-digit' 
    })
  }
  
  // 其他情况显示日期
  return date.toLocaleDateString('zh-CN', { 
    month: '2-digit', 
    day: '2-digit' 
  })
}

// 生成对话标题
const generateConversationTitle = (content) => {
  if (!content) return '新对话'
  // 取前30个字符作为标题
  const title = content.replace(/<[^>]*>/g, '').trim()
  return title.length > 30 ? title.substring(0, 30) + '...' : title
}

// 时间格式化（增强解析与本地日判断：今天/昨天/M月D日 HH:MM）
const parseToDate = (input) => {
  if (!input) return null
  if (typeof input === 'number') {
    const ms = input < 1e12 ? input * 1000 : input
    return new Date(ms)
  }
  if (typeof input === 'string') {
    // 优先尝试标准解析
    let d = new Date(input)
    if (!isNaN(d)) return d
    // 兼容 "YYYY-MM-DD HH:mm:ss"
    const normalized = input.replace(/-/g, '/').replace('T', ' ')
    d = new Date(normalized)
    if (!isNaN(d)) return d
  }
  return new Date(input)
}

const formatHistoryTime = (timestamp) => {
  const date = parseToDate(timestamp)
  if (!date || isNaN(date)) return '未知时间'
  const now = new Date()
  const isToday = date.toDateString() === now.toDateString()
  const yesterday = new Date(now.getFullYear(), now.getMonth(), now.getDate() - 1)
  const isYesterday = date.toDateString() === yesterday.toDateString()
  if (isToday) {
    return date.toLocaleTimeString('zh-CN', { hour: '2-digit', minute: '2-digit' })
  }
  if (isYesterday) {
    return '昨天 ' + date.toLocaleTimeString('zh-CN', { hour: '2-digit', minute: '2-digit' })
  }
  const month = date.getMonth() + 1
  const day = date.getDate()
  const time = date.toLocaleTimeString('zh-CN', { hour: '2-digit', minute: '2-digit' })
  return `${month}月${day}日 ${time}`
}

// 获取历史记录列表
const getHistoryRecordList = async () => {
  try {
    console.log('📋 开始获取移动端AI问答历史记录列表...')
    isLoadingHistory.value = true
    const startTime = performance.now()
    
    const response = await apis.getHistoryRecord({ 
      // ===== 已删除：user_id - 后端从token解析 =====
      ai_conversation_id: 0, // 0表示获取对话列表
      business_type: 0 // AI问答类型
    })
    
    const endTime = performance.now()
    console.log(`📋 移动端AI问答历史记录API调用耗时: ${(endTime - startTime).toFixed(2)}ms`)
    console.log('📋 移动端历史记录列表响应:', response)
    
    if (response.statusCode === 200) {
      // 设置历史记录总数
      historyTotal.value = response.total || 0
      
      // 转换后端数据为前端格式
      historyData.value = response.data.map(conversation => ({
        id: conversation.id,
        title: generateConversationTitle(conversation.content),
        time: formatHistoryTime(conversation.updated_at),
        businessType: conversation.business_type,
        isActive: false,
        // 保存原始数据用于后续查询
        rawData: conversation
      }))
      // 高亮当前对话
      if (ai_conversation_id.value) {
        historyData.value.forEach(item => { item.isActive = item.id === ai_conversation_id.value })
      }
      console.log(`✅ 移动端AI问答历史记录列表已设置: ${historyData.value.length}条记录，总数: ${historyTotal.value}`)
    } else {
      console.error('❌ 获取移动端历史记录列表失败:', response.statusCode)
    }
  } catch (error) {
    console.error('❌ 获取移动端历史记录列表失败:', error)
  } finally {
    isLoadingHistory.value = false
  }
}

// 渲染Markdown内容
const renderMarkdownContent = (content) => {
  try {
    let html = renderMarkdown(content)
    html = processStandardReferences(html)
    
    nextTick(() => {
      bindStandardReferenceEvents()
    })
    
    return html
  } catch (error) {
    console.error('Markdown渲染失败:', error)
    return content.replace(/\n/g, '<br>')
  }
}

// 解析搜索来源
const parseSearchSources = (searchSourceStr) => {
  try {
    if (!searchSourceStr || typeof searchSourceStr !== 'string' || !searchSourceStr.trim()) {
      return null
    }
    
    const sources = JSON.parse(searchSourceStr)
    
    if (!Array.isArray(sources)) {
      return null
    }
    
    const validSources = sources.filter(source => 
      source && typeof source === 'object' && source.title && source.content
    )
    
    return validSources.length > 0 ? validSources : null
  } catch (error) {
    console.error('解析搜索来源失败:', error)
    return null
  }
}

// 转换用户反馈状态
const convertUserFeedback = (feedback) => {
  switch (parseInt(feedback)) {
    case 2: return 'like'
    case 3: return 'dislike'
    default: return null
  }
}

// 转换前端反馈状态为后端格式
const convertFeedbackToBackend = (feedback) => {
  switch (feedback) {
    case 'like': return 2
    case 'dislike': return 3
    default: return 0
  }
}

// 处理历史记录点击
// 获取历史对话消息（与PC端完全一致）
const getConversationMessages = async (conversationId) => {
  try {
    const response = await apis.getHistoryRecord({ 
      // ===== 已删除：user_id - 后端从token解析 =====
      ai_conversation_id: conversationId,
      business_type: 0
    })
    
    if (response.statusCode === 200) {
      if (!response.data || !Array.isArray(response.data)) {
        console.error('响应数据格式错误')
        return false
      }
      
      const messages = await Promise.all(response.data.map(async (message, index) => {
        const userFeedback = convertUserFeedback(message.user_feedback)
        
        // 如果是用户消息且包含文件标签，提取文件信息
        let file = null
        let userContent = message.content
        
        // 获取前一条用户消息作为AI消息的问题上下文
        let userQuestion = null
        if (message.type === 'ai' && index > 0) {
          const prevMessage = response.data[index - 1]
          if (prevMessage && prevMessage.type === 'user') {
            // 提取用户实际问题（去除文件标签）
            if (prevMessage.content.includes('</filesize>')) {
              const userMessageMatch = prevMessage.content.split('</filesize>')[1]
              userQuestion = userMessageMatch ? userMessageMatch.trim() : prevMessage.content
            } else {
              userQuestion = prevMessage.content
            }
          }
        }
        
        if (message.type === 'user' && message.content.includes('</filesize>')) {
          // 提取文件信息
          const filenameMatch = message.content.match(/<filename>(.*?)<\/filename>/)
          const filesizeMatch = message.content.match(/<filesize>(.*?)<\/filesize>/)
          const wordMatch = message.content.match(/<word>(.*?)<\/word>/s)
          
          if (filenameMatch && filesizeMatch) {
            const filename = filenameMatch[1]
            const filesize = parseInt(filesizeMatch[1])
            const fileContent = wordMatch ? wordMatch[1].trim() : ''
            
            // 创建文件对象
            file = {
              name: filename,
              size: filesize,
              type: filename.endsWith('.docx') ? '.docx' : filename.endsWith('.doc') ? '.doc' : '.docx',
              icon: getFileIcon(filename.endsWith('.docx') ? '.docx' : filename.endsWith('.doc') ? '.doc' : '.docx'),
              content: fileContent
            }
            
            // 提取用户实际说的话（</filesize>标签后的内容）
            const userMessageMatch = message.content.split('</filesize>')[1]
            userContent = userMessageMatch ? userMessageMatch.trim() : ''
          }
        }
        
        // 为AI消息准备displayContent
        let displayContent = userContent || ''
        let reports = []
        let summary = message.summary || '' // 从后端恢复summary字段
        
        if (message.type === 'ai') {
          try {
            const contentStr = message.content || ''
            
            // 尝试解析content为JSON
            if (contentStr.trim().startsWith('[') || contentStr.trim().startsWith('{')) {
              try {
                const parsedContent = JSON.parse(contentStr)
                
                // 检查是否是新格式（包含reports和webSearch数据）
                if (parsedContent.reports && Array.isArray(parsedContent.reports)) {
                  reports = parsedContent.reports
                  // 恢复网络搜索数据
                  if (parsedContent.webSearchRaw) {
                    message.webSearchRaw = parsedContent.webSearchRaw
                  }
                  if (parsedContent.webSearchSummary) {
                    message.webSearchSummary = parsedContent.webSearchSummary
                    message.hasWebSearchResults = parsedContent.hasWebSearchResults || false
                  }
                  // ===== 🔧 修复：从content JSON中恢复summary =====
                  if (parsedContent.summary) {
                    summary = parsedContent.summary
                  }
                } else if (Array.isArray(parsedContent)) {
                  // 旧格式，直接是reports数组
                  reports = parsedContent
                } else {
                  throw new Error('Not an array or valid format')
                }
              } catch (jsonError) {
                // JSON解析失败,当作普通Markdown文本处理
                let processedContent = contentStr
                  .replace(/\\n/g, '\n')
                  .replace(/\\t/g, '\t')
                  .replace(/\\r/g, '\r')
                
                displayContent = renderMarkdownContent(processedContent)
              }
            } else {
              // 不是JSON格式,直接按Markdown处理
              let processedContent = contentStr
                .replace(/\\n/g, '\n')
                .replace(/\\t/g, '\t')
                .replace(/\\r/g, '\r')
              
              displayContent = renderMarkdownContent(processedContent)
            }
          } catch (error) {
            console.error('历史记录处理失败:', error)
            displayContent = message.content || ''
          }
        }
        
        // 如果有reports,计算相关统计信息
        let totalFiles = 0
        let completedCount = 0
        let progress = 100
        
        if (reports.length > 0) {
          // 过滤掉category_title类型,只统计实际报告
          const actualReports = reports.filter(r => r.type !== 'category_title')
          totalFiles = actualReports.length
          completedCount = actualReports.filter(r => r.status === 'completed').length
          progress = totalFiles > 0 ? Math.round((completedCount / totalFiles) * 100) : 100
        }
        
        return {
          type: message.type, // 'user' 或 'ai'
          content: userContent, // 使用提取的用户消息
            displayContent: displayContent,
          reports: reports, // 添加reports数组
          summary: summary, // 添加summary字段
          totalFiles: totalFiles, // 总文件数
          completedCount: completedCount, // 完成数
          progress: progress, // 进度
          file: file, // 添加文件对象
            isTyping: false,
          id: message.id,
          ai_message_id: message.type === 'ai' ? message.id : undefined, // AI消息的后端ID
          userFeedback: userFeedback,
          userQuestion: userQuestion, // AI消息对应的用户问题
          searchSources: message.type === 'ai' && message.search_source ? parseSearchSources(message.search_source) : null, // 解析搜索来源
          // 保存原始数据
          rawData: message,
          // 网络搜索数据（如果有）
          webSearchRaw: message.webSearchRaw || null,
          webSearchSummary: message.webSearchSummary || null,
          hasWebSearchResults: message.hasWebSearchResults || false,
          webSearchTotal: message.webSearchRaw?.total || 0,
          // 状态管理（历史记录默认完成状态）
          showStats: totalFiles > 0,
          currentStatus: 'completed',
          statusMessage: totalFiles > 0 
            ? (message.webSearchRaw?.total > 0 
                ? ` <span class="ai-name">蜀道安全管理AI智能助手</span>已为您分析 <span class="file-count">${totalFiles}</span> 个知识库文件，以及 <span class="file-count">${message.webSearchRaw.total}</span> 个相关网络资源`
                : ` <span class="ai-name">蜀道安全管理AI智能助手</span>已为您分析 <span class="file-count">${totalFiles}</span> 个知识库文件`)
            : ''
        }
      }))
      
      chatMessages.value = messages
      ai_conversation_id.value = conversationId
      
      const lastAIMessage = messages.filter(msg => msg.type === 'ai').pop()
      
      // 恢复所有AI消息的联网搜索结果、推荐问题和分类展开状态
      messages.forEach((message, index) => {
        if (message.type === 'ai' && message.rawData) {
          // 恢复联网搜索结果
          if (message.rawData.search_source) {
            try {
              const searchResults = JSON.parse(message.rawData.search_source)
              if (Array.isArray(searchResults) && searchResults.length > 0) {
                onlineSearchResults.value[message.id] = searchResults
              }
            } catch (error) {
              console.error('解析搜索结果失败:', error)
            }
          }
          
          // 恢复reports的分类展开状态
          if (message.reports && message.reports.length > 0) {
            if (!categoryExpandStates.value[index]) {
              categoryExpandStates.value[index] = {}
            }
            
            const categories = message.reports
              .filter(r => r.type === 'category_title')
              .map(r => r.category)
            
            categories.forEach(category => {
              categoryExpandStates.value[index][category] = true
            })
          }
        
        // 恢复推荐问题（只处理最后一条AI消息）
          if (message === lastAIMessage) {
            let questions = []
          
            // 尝试从guess_you_want字段恢复（文本格式，换行分隔）
            if (message.rawData.guess_you_want) {
          try {
                questions = message.rawData.guess_you_want.trim()
                  .split('\n')
              .map(q => q.trim())
              .filter(q => q.length > 0)
                  .filter((q, index, arr) => arr.indexOf(q) === index)
                  .slice(0, 3)
              } catch (error) {
                console.error('解析guess_you_want失败:', error)
              }
            }
            
            // 如果guess_you_want没有数据，尝试从relate_question恢复（JSON格式）
            if (questions.length === 0 && message.rawData.relate_question) {
              try {
                const relatedQuestions = JSON.parse(message.rawData.relate_question)
                if (Array.isArray(relatedQuestions) && relatedQuestions.length > 0) {
                  questions = relatedQuestions.slice(0, 3)
                }
              } catch (error) {
                console.error('解析relate_question失败:', error)
              }
            }
            
            // 设置推荐问题
            if (questions.length > 0) {
              aiRelatedQuestions.value = questions
              // 使用ai_message_id以确保显示条件匹配
              relatedQuestionsMessageId.value = message.ai_message_id || message.id
              console.log('✅ 从历史记录恢复推荐问题:', questions)
              console.log('✅ relatedQuestionsMessageId:', relatedQuestionsMessageId.value)
            }
          }
        }
      })
      
      return true
    }
  } catch (error) {
    console.error('加载历史记录失败:', error)
    showToastMessage('加载历史记录失败，请稍后重试', 2000)
    return false
  }
}

// 清除所有打字机定时器
const clearAllTypeIntervals = () => {
  typewriterIntervals.forEach((interval, messageId) => {
    clearInterval(interval)
  })
  typewriterIntervals.clear()
  
  reportTypewriters.forEach((interval, key) => {
    clearInterval(interval)
  })
  reportTypewriters.clear()
}

const handleHistoryItem = async (historyItem) => {
  if (isSending.value) return
  
  if (speakingMessageId.value) {
    stopAllAudio()
    speakingMessageId.value = null
  }
  
  clearAllTypeIntervals()
  
  // 设置激活状态
  historyData.value.forEach(item => {
    item.isActive = item.id === historyItem.id
  })
  
  // 关闭历史记录弹窗（移动端特有）
  showHistory.value = false
  
  // 清空当前消息
    chatMessages.value = []
  aiRelatedQuestions.value = []
  relatedQuestionsMessageId.value = null
  
  // 加载历史对话
  const success = await getConversationMessages(historyItem.id)
  
  if (success) {
    showChat.value = true
    await nextTick()
    scrollToBottom()
  } else {
    showToastMessage('加载历史记录失败', 2000)
  }
}

// 新建对话
const startNewChat = () => {
  console.log('开始新建对话')
  
  // 停止任何正在进行的朗读
  if (speakingMessageId.value) {
    stopAllAudio()
    speakingMessageId.value = null
  }
  
  // 关闭历史记录弹窗
  showHistory.value = false
  
  // 重置聊天状态
  chatMessages.value = []
  ai_conversation_id.value = 0
  messageText.value = ''
  
  // 清除联网搜索结果
  onlineSearchResults.value = {}
  expandedOnlineSearchResults.value = {}
  
  // 清除推荐问题
  aiRelatedQuestions.value = []
  relatedQuestionsMessageId.value = null
  
  // 清除所有历史记录的选中状态
  historyData.value.forEach((item) => {
    item.isActive = false
  })
  
  // 切换到初始状态（显示功能卡片）
  showChat.value = false
  
  console.log('新建对话完成')
}

// 删除历史记录
const deleteHistoryItem = async (historyItem, index) => {
  try {
    console.log('开始删除移动端历史记录:', historyItem)
    
    const response = await apis.deleteHistoryRecord({
      // ===== 已删除：user_id - 后端从token解析 =====
      ai_conversation_id: historyItem.id
    })
    
    if (response.statusCode === 200) {
      // 从本地数据中移除
      historyData.value.splice(index, 1)
      historyTotal.value = Math.max(0, historyTotal.value - 1)
      
      // 如果删除的是当前激活的历史记录，执行新建任务
      if (historyItem.isActive) {
        console.log('删除激活的历史记录，执行新建任务')
        startNewChat()
      }
      
      console.log('✅ 移动端历史记录删除成功')
      // 轻提示
      showToastMessage('删除成功')
    } else {
      console.error('❌ 删除移动端历史记录失败:', response)
    }
  } catch (error) {
    console.error('❌ 删除移动端历史记录失败:', error)
  }
}

// 自动发送消息功能
const autoSendMessage = async (message) => {
  if (!message || !message.trim()) return
  
  console.log('移动端自动发送消息:', message)
  
  // 显示聊天界面
  showChat.value = true
  
  // 如果是新对话，清除所有历史记录的选中状态
  if (chatMessages.value.length === 0) {
    historyData.value.forEach((item) => {
      item.isActive = false
    })
  }
  
  // 设置消息文本
  messageText.value = message
  
  // 使用nextTick确保DOM更新后再发送消息
  // await nextTick()
  
  // 发送消息
  await sendMessage()
}

// ========== 语音合成相关函数 ==========

// 获取TTS服务地址（自动判断是否使用代理）
const getTTSUrl = () => {
  // 在开发环境中使用代理，生产环境中使用直接地址
  const isDevelopment = import.meta.env.DEV
  if (isDevelopment) {
    return '/api/tts/voice'  // 使用Vite代理
  } else {
    return window.location.origin + '/tts/voice'  // 生产环境直接地址
  }
}

// 测试TTS服务连接
const testTTSConnection = async () => {
  // 自动获取TTS服务地址
  const ttsUrl = getTTSUrl()
  
  try {
    console.log('开始测试TTS服务连接...')
    console.log('使用代理地址:', ttsUrl)
    
    // 使用简单的测试文本
    const testText = '测试'
    
    const controller = new AbortController()
    const timeoutId = setTimeout(() => controller.abort(), 8000) // 8秒超时
    
    // 准备请求头，添加认证 Token
    const headers = { 'Content-Type': 'application/json' }
    const token = getToken()
    const tokenType = getTokenType()
    if (token && tokenType) {
      // 确保 Bearer 首字母大写
      const bearerType = tokenType.charAt(0).toUpperCase() + tokenType.slice(1).toLowerCase()
      headers['Authorization'] = `${bearerType} ${token}`
    }
    
    const response = await fetch(ttsUrl, {
      method: 'POST',
      headers,
      body: JSON.stringify({
        text: testText
      }),
      signal: controller.signal
    })
    
    clearTimeout(timeoutId)
    
    console.log('TTS连接测试结果:', {
      status: response.status,
      statusText: response.statusText,
      headers: Object.fromEntries(response.headers.entries()),
      url: ttsUrl
    })
    
    if (response.ok) {
      const blob = await response.blob()
      console.log('TTS服务连接正常，测试音频大小:', blob.size, 'bytes')
      return { success: true, message: 'TTS服务连接正常' }
    } else {
      return { success: false, message: `TTS服务响应错误: ${response.status} ${response.statusText}` }
    }
    
  } catch (error) {
    console.error('TTS连接测试失败:', error)
    
    let message = 'TTS服务连接失败'
    if (error.name === 'AbortError') {
      message = 'TTS服务连接超时'
    } else if (error.message.includes('Failed to fetch')) {
      message = '无法连接到TTS服务，请检查网络或服务状态'
    } else {
      message = `TTS服务连接失败: ${error.message}`
    }
    
    return { success: false, message }
  }
}

// 调用TTS接口进行语音合成（带重试机制）
const callTTSAPI = async (text, retryCount = 0) => {
  // 自动获取TTS服务地址
  const ttsUrl = getTTSUrl()
  const maxRetries = 2 // 最大重试次数
  
  try {
    console.log(`开始调用TTS接口，文本长度: ${text.length}, 重试次数: ${retryCount}`)
    console.log('TTS接口地址:', ttsUrl)
    
    // 添加超时控制 - 减少超时时间，提高响应速度
    const controller = new AbortController()
    const timeoutId = setTimeout(() => controller.abort(), 15000) // 15秒超时
    
    // 准备请求头，添加认证 Token
    const headers = { 'Content-Type': 'application/json' }
    const token = getToken()
    const tokenType = getTokenType()
    if (token && tokenType) {
      // 确保 Bearer 首字母大写
      const bearerType = tokenType.charAt(0).toUpperCase() + tokenType.slice(1).toLowerCase()
      headers['Authorization'] = `${bearerType} ${token}`
    }
    
    const response = await fetch(ttsUrl, {
      method: 'POST',
      headers,
      body: JSON.stringify({
        text: text
      }),
      signal: controller.signal
    })
    
    clearTimeout(timeoutId)
    
    console.log('TTS接口响应状态:', response.status, response.statusText)
    
    if (!response.ok) {
      const errorText = await response.text().catch(() => '无法读取错误信息')
      throw new Error(`TTS接口调用失败: ${response.status} ${response.statusText} - ${errorText}`)
    }
    
    // 检查响应类型
    const contentType = response.headers.get('content-type')
    console.log('响应Content-Type:', contentType)
    
    if (!contentType || !contentType.includes('audio')) {
      console.warn('响应可能不是音频格式:', contentType)
    }
    
    // 获取音频数据
    const audioBlob = await response.blob()
    console.log('TTS接口调用成功，音频大小:', audioBlob.size, 'bytes')
    console.log('音频类型:', audioBlob.type)
    
    if (audioBlob.size === 0) {
      throw new Error('TTS接口返回的音频数据为空')
    }
    
    // 创建音频URL
    const audioUrl = URL.createObjectURL(audioBlob)
    return audioUrl
    
  } catch (error) {
    console.error(`TTS接口调用失败 (重试${retryCount}/${maxRetries}):`, error)
    
    // 如果是网络错误或超时，且还有重试次数，则重试
    if (retryCount < maxRetries && (
      error.name === 'AbortError' || 
      error.message.includes('Failed to fetch') ||
      error.message.includes('NetworkError')
    )) {
      console.log(`准备重试TTS请求，等待${(retryCount + 1) * 1000}ms...`)
      await new Promise(resolve => setTimeout(resolve, (retryCount + 1) * 1000))
      return callTTSAPI(text, retryCount + 1)
    }
    
    // 提供更详细的错误信息
    let errorMessage = '语音合成失败'
    
    if (error.name === 'AbortError') {
      errorMessage = '语音合成请求超时，请检查网络连接或稍后重试'
    } else if (error.message.includes('Failed to fetch')) {
      errorMessage = '无法连接到语音合成服务，请检查网络连接或联系管理员'
    } else if (error.message.includes('CORS')) {
      errorMessage = '跨域请求被阻止，请联系管理员配置服务器'
    } else if (error.message.includes('NetworkError')) {
      errorMessage = '网络错误，请检查网络连接'
    } else if (error.message.includes('TTS接口调用失败')) {
      errorMessage = error.message
    } else {
      errorMessage = `语音合成失败: ${error.message}`
    }
    
    // 抛出包含详细信息的错误
    const detailedError = new Error(errorMessage)
    detailedError.originalError = error
    throw detailedError
  }
}

// 清理文本内容，移除HTML标签和非文本字符
const cleanTextForTTS = (text) => {
  if (!text) return ''
  
  // 移除HTML标签
  let cleanText = text.replace(/<[^>]*>/g, '')
  
  // 移除多余的空白字符
  cleanText = cleanText.replace(/\s+/g, ' ').trim()
  
  // 移除特殊字符，保留中文、英文、数字和基本标点
  cleanText = cleanText.replace(/[^\u4e00-\u9fa5a-zA-Z0-9\s，。！？、；：""''（）【】《》]/g, '')
  
  return cleanText
}

// 将长文本分块处理（第一块较小，后续块较大）
const splitTextIntoChunks = (text) => {
  if (text.length <= 60) {
    return [text]
  }
  
  const chunks = []
  let currentChunk = ''
  let isFirstChunk = true
  
  // 按句子分割
  const sentences = text.split(/[。！？]/)
  
  for (const sentence of sentences) {
    if (sentence.trim().length === 0) continue
    
    const sentenceWithPunctuation = sentence + (sentence.match(/[。！？]$/) ? '' : '。')
    
    // 第一块限制为60字，后续块限制为200字
    const maxLength = isFirstChunk ? 60 : 200
    
    if (currentChunk.length + sentenceWithPunctuation.length <= maxLength) {
      currentChunk += sentenceWithPunctuation
    } else {
      if (currentChunk.length > 0) {
        chunks.push(currentChunk.trim())
        currentChunk = sentenceWithPunctuation
        isFirstChunk = false // 第一块已完成
      } else {
        // 如果单个句子就超过最大长度，强制分割
        const maxLen = isFirstChunk ? 60 : 200
        chunks.push(sentenceWithPunctuation.substring(0, maxLen))
        currentChunk = sentenceWithPunctuation.substring(maxLen)
        isFirstChunk = false
      }
    }
  }
  
  if (currentChunk.trim().length > 0) {
    chunks.push(currentChunk.trim())
  }
  
  return chunks
}

// 播放音频
const playAudio = (audioUrl) => {
  return new Promise((resolve, reject) => {
    const audio = new Audio(audioUrl)
    currentAudio.value = audio
    
    audio.onended = () => {
      console.log('音频播放完成')
      currentAudio.value = null
      resolve()
    }
    
    audio.onerror = (error) => {
      console.error('音频播放失败:', error)
      currentAudio.value = null
      reject(error)
    }
    
    audio.onloadstart = () => {
      console.log('开始加载音频')
    }
    
    audio.oncanplay = () => {
      console.log('音频可以播放')
    }
    
    // 开始播放
    audio.play().catch(error => {
      console.error('音频播放启动失败:', error)
      currentAudio.value = null
      reject(error)
    })
  })
}

// 停止所有音频播放
const stopAllAudio = () => {
  try {
    // 停止当前播放的音频
    if (currentAudio.value) {
      currentAudio.value.pause()
      currentAudio.value.currentTime = 0
      currentAudio.value = null
    }
    
    // 清空音频队列
    audioQueue.value = []
    isPlayingQueue.value = false
    
    // 停止浏览器原生语音合成（备用）
    window.speechSynthesis && window.speechSynthesis.cancel()
    
    console.log('所有音频播放已停止')
  } catch (e) {
    console.warn('停止音频播放时出错:', e)
  }
}

// 优化后的并行预加载音频并播放队列
const playAudioQueue = async (chunks, messageId) => {
  console.log(`开始优化播放 ${chunks.length} 个音频片段`)
  
  // 检查是否还在朗读状态
  if (speakingMessageId.value !== messageId) {
    console.log('朗读被中断，停止处理')
    return
  }
  
  try {
    // 第一步：立即预加载第一块并开始播放
    console.log('立即预加载并播放第一块')
    const firstChunk = chunks[0]
    const firstAudioUrl = await callTTSAPI(firstChunk)
    
    // 检查是否还在朗读状态
    if (speakingMessageId.value !== messageId) {
      URL.revokeObjectURL(firstAudioUrl)
      return
    }
    
    // 开始播放第一块的同时，并行预加载后续所有块
    console.log('开始播放第一块，同时预加载后续块')
    const remainingChunks = chunks.slice(1)
    
    // 并行预加载剩余所有音频片段
    const remainingPromises = remainingChunks.map(async (chunk, index) => {
      try {
        console.log(`预加载第 ${index + 2}/${chunks.length} 块音频`)
        const audioUrl = await callTTSAPI(chunk)
        return { index: index + 1, audioUrl, chunk }
      } catch (error) {
        console.error(`第 ${index + 2} 块音频预加载失败:`, error)
        return { index: index + 1, audioUrl: null, chunk, error }
      }
    })
    
    // 播放第一块音频
    console.log('播放第 1/1 块音频')
    await playAudio(firstAudioUrl)
    URL.revokeObjectURL(firstAudioUrl)
    
    // 检查是否还在朗读状态
    if (speakingMessageId.value !== messageId) {
      console.log('朗读被中断，清理预加载的音频')
      // 清理已预加载的音频URL
      const remainingResults = await Promise.allSettled(remainingPromises)
      remainingResults.forEach(result => {
        if (result.status === 'fulfilled' && result.value.audioUrl) {
          URL.revokeObjectURL(result.value.audioUrl)
        }
      })
      return
    }
    
    // 等待所有预加载完成
    console.log('等待所有音频预加载完成...')
    const remainingResults = await Promise.allSettled(remainingPromises)
    
    // 再次检查是否还在朗读状态
    if (speakingMessageId.value !== messageId) {
      console.log('朗读被中断，停止播放队列')
      // 清理已预加载的音频URL
      remainingResults.forEach(result => {
        if (result.status === 'fulfilled' && result.value.audioUrl) {
          URL.revokeObjectURL(result.value.audioUrl)
        }
      })
      return
    }
    
    // 按顺序播放剩余的音频片段
    console.log('开始播放剩余音频片段')
    isPlayingQueue.value = true
    
    for (const result of remainingResults) {
      // 再次检查是否还在朗读状态
      if (speakingMessageId.value !== messageId) {
        console.log('朗读被中断，停止播放队列')
        break
      }
      
      if (result.status === 'fulfilled' && result.value.audioUrl) {
        try {
          console.log(`播放第 ${result.value.index + 1}/${chunks.length} 块音频`)
          await playAudio(result.value.audioUrl)
          URL.revokeObjectURL(result.value.audioUrl)
        } catch (error) {
          console.error(`第 ${result.value.index + 1} 块音频播放失败:`, error)
        }
      } else {
        console.warn(`第 ${result.value.index + 1} 块音频预加载失败，跳过播放`)
      }
    }
    
    isPlayingQueue.value = false
    console.log('音频队列播放完成')
    
  } catch (error) {
    console.error('音频队列播放失败:', error)
    isPlayingQueue.value = false
  }
}

// 语音朗读相关方法
const handleVoiceRead = async (message) => {
  if (speakingMessageId.value === message.id) {
    // 如果正在朗读这条消息，则停止
    stopAllAudio()
    speakingMessageId.value = null
  } else {
    // 如果朗读其他消息，先停止当前朗读
    if (speakingMessageId.value) {
      stopAllAudio()
      speakingMessageId.value = null
    }
    
    // 开始朗读新消息
    const textToRead = message.displayContent || message.content
    if (textToRead && textToRead.trim()) {
      try {
        // 清理文本内容
        const cleanText = cleanTextForTTS(textToRead)
        console.log('清理后的文本:', cleanText)
        
        if (cleanText.length === 0) {
          showToastMessage('文本内容为空，无法进行语音合成')
          return
        }
        
        // 设置朗读状态
        speakingMessageId.value = message.id
        
        // 如果文本较短（小于60字），直接合成
        if (cleanText.length <= 60) {
          console.log('文本较短，直接合成语音')
          const audioUrl = await callTTSAPI(cleanText)
          await playAudio(audioUrl)
          // 清理URL对象
          URL.revokeObjectURL(audioUrl)
        } else {
          // 如果文本较长，使用并行预加载分块处理
          console.log('文本较长，使用并行预加载分块处理')
          const chunks = splitTextIntoChunks(cleanText)
          console.log(`文本分为 ${chunks.length} 块`)
          
          // 使用新的并行预加载播放方法
          await playAudioQueue(chunks, message.id)
        }
        
      } catch (error) {
        console.error('语音合成失败:', error)
        showToastMessage('语音合成失败，请稍后重试')
      } finally {
        // 清除朗读状态
        speakingMessageId.value = null
        isPlayingQueue.value = false
      }
    }
  }
}

// 检查消息是否正在朗读
const isSpeaking = (messageId) => {
  return speakingMessageId.value === messageId
}

// ========== 联网搜索相关函数 ==========

// 切换联网搜索状态
// 处理网络搜索胶囊点击
const handleWebSearchToggle = (webSearchData) => {
  console.log('点击网络搜索胶囊，数据:', webSearchData)
  currentWebSearchData.value = webSearchData
  showWebSearchModal.value = true
}

// 处理搜索结果点击
const handleSearchResultClick = (result) => {
  previewTitle.value = result.title
  previewUrl.value = result.url || result.link
  showWebPreview.value = true
}

// 格式化URL显示
const formatUrl = (url) => {
  if (!url) return ''
  try {
    const urlObj = new URL(url)
    return urlObj.hostname + urlObj.pathname
  } catch (e) {
    return url
  }
}

// 在新标签页打开链接
const openInNewTab = () => {
  if (previewUrl.value) {
    window.open(previewUrl.value, '_blank')
    showWebPreview.value = false
  }
}

// 处理文件预览
const handleFilePreview = (data) => {
  console.log('移动端打开文件预览:', data)

  // 重置状态
  fileError.value = ''
  fileLoading.value = false

  // 处理不同类型的输入参数
  if (typeof data === 'string') {
    previewFilePath.value = data
    previewFileName.value = ''
  } else if (data && data.filePath) {
    previewFilePath.value = data.filePath
    previewFileName.value = data.fileName || ''
  } else {
    fileError.value = '文件路径为空'
    previewFilePath.value = ''
    previewFileName.value = ''
  }

  // 显示加载状态
  if (previewFilePath.value) {
    fileLoading.value = true
    // 设置超时，如果5秒后还在加载，显示错误
    setTimeout(() => {
      if (fileLoading.value) {
        fileLoading.value = false
        fileError.value = '😔 抱歉，未找到文件链接，正在快马加鞭修复中！'
      }
    }, 5000)
  }

  showFilePreview.value = true
}

const toggleNetworkSearch = () => {
  isNetworkSearchEnabled.value = !isNetworkSearchEnabled.value
  console.log('联网搜索状态:', isNetworkSearchEnabled.value ? '已启用' : '已关闭')
  
  // 使用Toast提示
  if (isNetworkSearchEnabled.value) {
    showToastMessage('联网搜索已启用')
  } else {
    showToastMessage('联网搜索已关闭')
  }
}

// 切换联网搜索结果展开状态
const toggleOnlineSearchResults = (messageId) => {
  expandedOnlineSearchResults.value[messageId] = !expandedOnlineSearchResults.value[messageId]
  console.log('联网搜索结果展开状态:', expandedOnlineSearchResults.value[messageId] ? '已展开' : '已收起')
}

// 打开联网搜索结果链接
const openSearchResult = (url) => {
  if (!url || url.trim() === '') {
    console.warn('联网搜索结果URL为空或无效:', url)
    showToastMessage('链接无效')
    return
  }
  
  try {
    // 确保URL包含协议
    let targetUrl = url.trim()
    if (!targetUrl.startsWith('http://') && !targetUrl.startsWith('https://')) {
      targetUrl = 'https://' + targetUrl
    }
    
    console.log('打开联网搜索结果链接:', targetUrl)
    window.open(targetUrl, '_blank', 'noopener,noreferrer')
  } catch (error) {
    console.error('打开联网搜索结果链接失败:', error)
    showToastMessage('打开链接失败')
  }
}

// 联网搜索功能
const performOnlineSearch = async (userInput, messageId) => {
  if (!userInput || userInput.trim() === '') {
    console.log('用户输入为空，跳过搜索')
    return
  }
  
  if (!messageId) {
    console.error('消息ID为空，无法进行联网搜索')
    return
  }
  
  try {
    console.log('开始联网搜索，用户输入:', userInput, '消息ID:', messageId)
    isGettingOnlineSearch.value = true
    onlineSearchLoadingMessages.value.add(messageId)
    
    const response = await apis.onlineSearch({ 
      keywords: userInput.trim()
    })
    
    console.log('联网搜索结果:', response)
    
    if (response.statusCode === 200 && response.results) {
      try {
        // 解析搜索结果
        const results = response.results
      
        // 处理数据格式，确保有必要的字段
        const processedResults = results.map((result, index) => ({
          title: result.title || `搜索结果 ${index + 1}`,
          snippet: result.content || result.snippet || '暂无内容',
          url: result.url || null
        }))
        
        onlineSearchResults.value[messageId] = processedResults
        
        // 设置联网搜索结果默认收起
        expandedOnlineSearchResults.value[messageId] = false
        
        console.log('联网搜索结果已保存，消息ID:', messageId, '结果数量:', processedResults.length)
        console.log('处理后的搜索结果:', processedResults)
        console.log('联网搜索结果已设置为默认收起')
        
        // 保存联网搜索结果到数据库
        try {
          const saveData = {
            id: messageId,
            ai_conversation_id: ai_conversation_id.value,
            search_source: JSON.stringify(processedResults)
          }
          
          const saveResponse = await apis.saveOnlineSearchResult(saveData)
          console.log('联网搜索结果保存到数据库成功:', saveResponse)
        } catch (saveError) {
          console.error('保存联网搜索结果到数据库失败:', saveError)
        }
        
        // 联网搜索完成后，强制滚动到底部显示搜索结果
        setTimeout(() => {
          scrollToBottom()
        }, 100)
        
        // 联网搜索完成后，获取推荐问题
        setTimeout(async () => {
          console.log('联网搜索完成，开始获取推荐问题，消息ID:', messageId)
          // 需要获取AI回复内容来生成推荐问题
          const aiMessage = chatMessages.value.find(msg => msg.id === messageId)
          if (aiMessage && aiMessage.content) {
            await getAIRelatedQuestions(userInput, aiMessage.content, messageId)
          }
        }, 500) // 延迟500ms让联网搜索结果先显示
        
      } catch (parseError) {
        console.error('解析联网搜索结果失败:', parseError)
        onlineSearchResults.value[messageId] = []
      }
    } else {
      console.error('联网搜索失败:', response.statusCode)
      onlineSearchResults.value[messageId] = []
    }
  } catch (error) {
    console.error('联网搜索请求失败:', error)
    onlineSearchResults.value[messageId] = []
  } finally {
    isGettingOnlineSearch.value = false
    onlineSearchLoadingMessages.value.delete(messageId)
  }
}

// ========== 报告生成相关函数 ==========

// 分类展开/收起处理
const handleCategoryToggle = (messageIndex, data) => {
  if (!categoryExpandStates.value[messageIndex]) {
    categoryExpandStates.value[messageIndex] = {}
  }
  categoryExpandStates.value[messageIndex][data.category] = data.expanded
}

const isCategoryExpanded = (messageIndex, category) => {
  if (!category) return true
  if (!categoryExpandStates.value[messageIndex]) {
    categoryExpandStates.value[messageIndex] = {}
    return true
  }
  return categoryExpandStates.value[messageIndex][category] !== false
}

// 检查reports数组是否只包含分类标题，没有实际报告
const hasOnlyCategoryTitles = (reports) => {
  if (!reports || reports.length === 0) return false
  return reports.every(r => r.type === 'category_title')
}

// 将消息状态映射到头像状态
const getAvatarStatus = (currentStatus, progress) => {
  if (progress === 100) return 'completed'
  
  switch (currentStatus) {
    case 'querying_kb':
    case 'web_searching':
      return 'searching'
    case 'analyzing_files':
    case 'analyzing_web':
      return 'analyzing'
    case 'deep_thinking':
      return 'thinking'
    case 'outputting':
      return 'analyzing'
    case 'completed':
      return 'completed'
    case 'error':
      return 'error'
    default:
      return 'idle'
  }
}

// 更新AI消息状态和进度
const updateMessageStatus = (aiMessage, status, customMessage = null) => {
  const statusConfig = {
    querying_kb: {
      message: '🔍 <span class="ai-name">蜀道安全管理AI智能助手</span>正在为您分析知识库……',
      progress: 10
    },
    web_searching: {
      message: '🌐 <span class="ai-name">蜀道安全管理AI智能助手</span>正在为您联网分析……',
      progress: 15
    },
    data_retrieved: {
      message: null,
      progress: 30
    },
    analyzing_files: {
      message: '😊 <span class="ai-name">蜀道安全管理AI智能助手</span>正在为您分析文件内容……',
      progress: 45
    },
    analyzing_web: {
      message: '🤔 <span class="ai-name">蜀道安全管理AI智能助手</span>正在分析联网数据……',
      progress: 70
    },
    deep_thinking: {
      message: '❓ <span class="ai-name">蜀道安全管理AI智能助手</span>正在深度思考中，请您稍等片刻……',
      progress: 75
    },
    outputting: {
      message: '😄 <span class="ai-name">蜀道安全管理AI智能助手</span>正在整理分析中！',
      progress: 90
    },
    completed: {
      message: null,
      progress: 100
    }
  }
  
  const config = statusConfig[status]
  if (config) {
    aiMessage.currentStatus = status
    
    if (status === 'data_retrieved') {
      const kbCount = aiMessage.totalFiles || 0
      const webCount = aiMessage.webSearchTotal || 0
      
      if (webCount > 0) {
        aiMessage.statusMessage = `<span class="ai-name">蜀道安全管理AI智能助手</span>正在为您分析 <span class="file-count">${kbCount}</span> 个知识库文件，以及 <span class="file-count">${webCount}</span> 个相关网络资源`
      } else {
        aiMessage.statusMessage = `<span class="ai-name">蜀道安全管理AI智能助手</span>正在为您分析 <span class="file-count">${kbCount}</span> 个知识库文件`
      }
    } else if (status === 'completed') {
      const kbCount = aiMessage.totalFiles || 0
      const webCount = aiMessage.webSearchTotal || 0
      
      if (webCount > 0) {
        aiMessage.statusMessage = `✅ <span class="ai-name">蜀道安全管理AI智能助手</span>已为您检索到 <span class="file-count">${kbCount}</span> 个知识库文件，以及 <span class="file-count">${webCount}</span> 个相关网络资源`
      } else {
        aiMessage.statusMessage = `✅ <span class="ai-name">蜀道安全管理AI智能助手</span>已为您检索到 <span class="file-count">${kbCount}</span> 个知识库文件`
      }
    } else {
      aiMessage.statusMessage = customMessage || config.message
    }
    
    aiMessage.progress = config.progress
  }
}

// 打字机效果函数
const startReportFieldTypewriter = (report, field, fullContent, speed = 50) => {
  return new Promise((resolve) => {
    const key = `${report.file_index}_${field}`
    
    // 如果已经有打字机在运行,先清除
    if (reportTypewriters.has(key)) {
      clearInterval(reportTypewriters.get(key))
      reportTypewriters.delete(key)
    }
    
    // 初始化打字机状态
    if (!report._typewriterStates) {
      report._typewriterStates = {}
    }
    
    let currentIndex = 0
    report._typewriterStates[field] = { currentIndex: 0, isTyping: true }
    
    const interval = setInterval(() => {
      if (currentIndex < fullContent.length) {
        const charsToAdd = Math.max(1, Math.floor(speed / 10))
        currentIndex = Math.min(currentIndex + charsToAdd, fullContent.length)
        
        // 更新显示内容
        report.report[field] = fullContent.substring(0, currentIndex)
        report._typewriterStates[field].currentIndex = currentIndex
      } else {
        // 打字完成
        clearInterval(interval)
        reportTypewriters.delete(key)
        report._typewriterStates[field].isTyping = false
        report.report[field] = fullContent
        resolve()
      }
    }, 1000 / 60) // 60fps
    
    reportTypewriters.set(key, interval)
  })
}

// SSE消息处理函数
const handleSSEMessage = (data, aiMessageIndex) => {
  const aiMessage = chatMessages.value[aiMessageIndex]
  if (!aiMessage) return
  
  // 🔍 调试：打印SSE返回的完整数据对象
  console.log('🔍 SSE事件完整数据:', {
    type: data.type,
    conversation_id: data.conversation_id,
    message_id: data.message_id,
    ai_conversation_id: data.ai_conversation_id,
    ai_message_id: data.ai_message_id,
    allKeys: Object.keys(data)
  })
  
  // 捕获conversation_id（后端返回的字段名，可能是conversation_id或ai_conversation_id）
  const conversationId = data.conversation_id || data.ai_conversation_id
  if (conversationId) {
    if (ai_conversation_id.value === 0) {
      ai_conversation_id.value = conversationId
      console.log('✅ SSE收到conversation_id并赋值:', conversationId)
    }
  }
  
  // 捕获message_id（后端返回的字段名，可能是message_id或ai_message_id）
  const messageId = data.message_id || data.ai_message_id
  if (messageId) {
    if (!aiMessage.ai_message_id) {
      aiMessage.ai_message_id = messageId
      console.log('✅ SSE收到message_id并赋值:', messageId)
      console.log('  - aiMessage.id (前端临时ID):', aiMessage.id)
      console.log('  - aiMessage.ai_message_id (后端ID):', aiMessage.ai_message_id)
      
      // 同时更新rawData对象，确保操作按钮能够使用正确的ID
      if (!aiMessage.rawData) {
        aiMessage.rawData = {}
      }
      aiMessage.rawData.id = messageId
      console.log('  - aiMessage.rawData.id:', aiMessage.rawData.id)
    }
  }
  
  switch (data.type) {
    case 'intent':
      // 检查是否为专业问题
      if (data.is_professional_question === false) {
        // 非专业问题：立即隐藏状态显示组件
        aiMessage.showStats = false

        // 非专业问题,只输出summary字段内容并终止流程
        const summaryContent = data.summary || '抱歉,我暂时无法回答您的问题。'

        // 只设置summary，不设置content和displayContent，避免重复显示
        aiMessage.summary = summaryContent
        aiMessage.isTyping = false // 停止加载动画

        // 保存到数据库
        if (aiMessage.ai_message_id) {
          updateAIMessageContent(aiMessage.ai_message_id, summaryContent, summaryContent)
            .catch(err => console.error('回写AI消息失败:', err))
        }

        // 关闭SSE连接
        if (sseConnection) {
          closeSSEConnection(sseConnection)
          sseConnection = null
        }

        // 重置发送状态
        isSending.value = false
        streamingReports.value.clear()

        // 重置AI回复流程状态
        isAIReplyProcessComplete.value = true
        
        // ===== 🎯 非专业问题也要更新历史记录和获取推荐问题 =====
        // 更新历史记录
        if (ai_conversation_id.value && ai_conversation_id.value !== 0) {
          // 先清除所有高亮
          historyData.value.forEach((item) => {
            item.isActive = false
          })
          
          // 获取第一条用户消息作为标题
          const firstUserMessage = chatMessages.value.find(msg => msg.type === 'user')
          const title = firstUserMessage ? firstUserMessage.content.substring(0, 20) + '...' : '新对话'
          
          // 检查是否已存在
          const existingIndex = historyData.value.findIndex(item => item.id === ai_conversation_id.value)
          
          if (existingIndex === -1) {
            // 不存在，在最前面插入新项
            const newItem = {
              id: ai_conversation_id.value,
              title: title,
              time: formatTime(new Date().toISOString()),
              businessType: 0,
              isActive: true,
              rawData: {
                id: ai_conversation_id.value,
                content: firstUserMessage?.content || '',
                updated_at: new Date().toISOString()
              }
            }
            historyData.value.unshift(newItem)
            console.log('✅ 非专业问题：已在列表最前面插入新历史记录')
          } else {
            // 已存在，设为高亮并移到最前面
            const existingItem = historyData.value.splice(existingIndex, 1)[0]
            existingItem.isActive = true
            existingItem.time = formatTime(new Date().toISOString())
            historyData.value.unshift(existingItem)
            console.log('✅ 非专业问题：已将历史记录移到最前面')
          }
          
          // 更新历史记录总数
          historyTotal.value = historyData.value.length
        }
        
        // 获取推荐问题
        const lastUserMessage = chatMessages.value.filter(msg => msg.type === 'user').pop()
        if (lastUserMessage && aiMessage.ai_message_id && summaryContent) {
          getAIRelatedQuestions(lastUserMessage.content, summaryContent, aiMessage.ai_message_id)
        }
        
        return // 终止处理
      }

      // 专业问题：意图识别完成，更新为查询知识库状态
      updateMessageStatus(aiMessage, 'querying_kb')

      // 如果启用联网搜索，稍后会更新为web_searching状态
      // （当收到web_search_raw事件时）

      // 保存问题总结并使用打字机效果
      if (data.summary) {
        const fullSummary = data.summary
        aiMessage._fullSummary = fullSummary
        aiMessage.summary = ''

        // 使用打字机效果显示问题总结
        startReportFieldTypewriter(
          { file_index: 'summary', report: aiMessage, _typewriterStates: {} },
          'summary',
          fullSummary,
          50
        ).catch(err => {
          console.error('问题总结打字机效果失败:', err)
          aiMessage.summary = fullSummary
        })
      }
      break
      
    case 'documents':
      aiMessage.totalFiles = data.total
      aiMessage.completedCount = 0
      
      // 收到文档数据后的状态处理
      if (isNetworkSearchEnabled.value) {
        // 如果启用了联网搜索，更新状态为联网搜索中
        updateMessageStatus(aiMessage, 'web_searching')
        // 等待web_search_raw事件后再更新为data_retrieved
      } else {
        // 如果没有联网搜索，直接显示检索结果
        updateMessageStatus(aiMessage, 'data_retrieved')
      }
      break
      
    case 'category_title':
      // 第一个分类标题时，说明开始分析文件
      if (aiMessage.reports.length === 0) {
        // 只有在当前状态进度 >= data_retrieved的进度时，才更新为analyzing_files
        // 这样确保data_retrieved状态能被看到
        if (aiMessage.progress >= 30) {
          updateMessageStatus(aiMessage, 'analyzing_files')
        }
      }
      
      const categoryTitle = {
        type: 'category_title',
        category: data.category,
        number: data.number,
        count: data.count,
        source_file: `【${data.number}、${data.category}】（共${data.count}个文件）`,
        file_index: -1,
        status: 'category'
      }
      aiMessage.reports.push(categoryTitle)
      
      // 初始化该分类为展开状态
      if (!categoryExpandStates.value[aiMessageIndex]) {
        categoryExpandStates.value[aiMessageIndex] = {}
      }
      categoryExpandStates.value[aiMessageIndex][data.category] = true
      
      // 保存当前分类名称,用于后续报告匹配
      aiMessage.currentCategory = data.category
      break
    
    case 'report_start':
      // 调试日志：查看完整的数据结构
      console.log('🔍 [DEBUG] report_start 数据:', {
        file_index: data.file_index,
        source_file: data.source_file,
        file_path: data.file_path,
        metadata: data.metadata,
        完整data: data
      })
      
      const placeholderReport = {
        file_index: data.file_index,
        total_files: aiMessage.totalFiles,
        source_file: data.source_file,
        file_path: data.file_path,
        similarity: data.similarity,
        metadata: {
          ...data.metadata,
          _displayCategory: aiMessage.currentCategory // 存储当前显示的分类名
        },
        report: {
          display_name: '',
          summary: '',
          analysis: '',
          clauses: ''
        },
        status: 'streaming'
      }
      aiMessage.reports.push(placeholderReport)
      streamingReports.value.set(data.file_index, aiMessage.reports.length - 1)
      break
    
    case 'report_chunk':
      // 不再处理report_chunk,等待完整的report消息后用打字机效果显示
      // 这样可以避免与打字机效果冲突
      break
      
    case 'report':
      // 第一个报告开始时，更新到深度思考状态
      if (aiMessage.reports.filter(r => r.status === 'completed').length === 0) {
        updateMessageStatus(aiMessage, 'deep_thinking')
      }
      
      const reportData = data.data || data
      
      // 调试日志：查看完整的report数据
      console.log('🔍 [DEBUG] report 数据:', {
        file_index: reportData.file_index,
        source_file: reportData.source_file,
        file_path: reportData.file_path,
        metadata: reportData.metadata,
        完整reportData: reportData
      })
      
      const idx = streamingReports.value.get(reportData.file_index)
      
      let targetReport
      if (idx !== undefined) {
        const displayCategory = aiMessage.reports[idx].metadata?._displayCategory
        const fullSummary = reportData.report?.summary || ''
        const fullAnalysis = reportData.report?.analysis || ''
        const fullClauses = reportData.report?.clauses || ''
        const fullDisplayName = reportData.report?.display_name || ''
        
        // 创建带空内容的报告对象，保留所有原始字段
        aiMessage.reports[idx] = { 
          ...reportData, // 保留所有原始字段，包括可能的链接字段
          report: {
            display_name: fullDisplayName, // 直接显示
            summary: '',
            analysis: '',
            clauses: ''
          },
          status: 'completed',
          metadata: {
            ...reportData.metadata, // 保留所有metadata字段
            _displayCategory: displayCategory || aiMessage.currentCategory
          },
          _fullContent: {
            display_name: fullDisplayName,
            summary: fullSummary,
            analysis: fullAnalysis,
            clauses: fullClauses
          }
        }
        targetReport = aiMessage.reports[idx]
        streamingReports.value.delete(reportData.file_index)
      } else {
        const fullSummary = reportData.report?.summary || ''
        const fullAnalysis = reportData.report?.analysis || ''
        const fullClauses = reportData.report?.clauses || ''
        const fullDisplayName = reportData.report?.display_name || ''
        
        const newReport = { 
          ...reportData, // 保留所有原始字段，包括可能的链接字段
          report: {
            display_name: fullDisplayName, // 直接显示
            summary: '',
            analysis: '',
            clauses: ''
          },
          status: 'completed',
          metadata: {
            ...reportData.metadata, // 保留所有metadata字段
            _displayCategory: aiMessage.currentCategory
          },
          _fullContent: {
            display_name: fullDisplayName,
            summary: fullSummary,
            analysis: fullAnalysis,
            clauses: fullClauses
          }
        }
        aiMessage.reports.push(newReport)
        targetReport = newReport
      }
      
      // 使用顺序打字机效果：概述 -> 解析 -> 相关条款
      if (targetReport._fullContent && !targetReport._typewriterCompleted) {
        // 标记打字机已启动，防止重复触发
        targetReport._typewriterStarted = true
        
        // 先打概述（速度200 = 每次20个字符，极快）
        startReportFieldTypewriter(targetReport, 'summary', targetReport._fullContent.summary || '', 200)
          .then(() => {
            // 概述完成后打解析
            return startReportFieldTypewriter(targetReport, 'analysis', targetReport._fullContent.analysis || '', 200)
          })
          .then(() => {
            // 解析完成后打相关条款
            if (targetReport._fullContent.clauses) {
              return startReportFieldTypewriter(targetReport, 'clauses', targetReport._fullContent.clauses || '', 200)
            }
          })
          .then(() => {
            // 全部完成，标记为已完成
            targetReport._typewriterCompleted = true
          })
          .catch(err => {
            console.error('报告打字机效果失败:', err)
            // 失败时直接显示完整内容
            targetReport.report.summary = targetReport._fullContent.summary || ''
            targetReport.report.analysis = targetReport._fullContent.analysis || ''
            targetReport.report.clauses = targetReport._fullContent.clauses || ''
            targetReport._typewriterCompleted = true
          })
        
        console.log('📝 [DEBUG] 报告打字机已启动:', {
          file_index: targetReport.file_index,
          summary_length: targetReport._fullContent.summary?.length || 0,
          analysis_length: targetReport._fullContent.analysis?.length || 0,
          clauses_length: targetReport._fullContent.clauses?.length || 0
        })
      }
      
      // 更新进度
      aiMessage.completedCount = aiMessage.reports.filter(r => r.status === 'completed' && r.type !== 'category_title').length
      const reportProgress = aiMessage.totalFiles > 0 ? (aiMessage.completedCount / aiMessage.totalFiles) : 0
      
      // 根据报告完成度更新状态
      if (reportProgress >= 1) {
        // 所有报告完成，进入输出阶段
        updateMessageStatus(aiMessage, 'outputting')
      } else if (reportProgress >= 0.5) {
        // 过半，深度思考
        updateMessageStatus(aiMessage, 'deep_thinking')
      } else {
        // 继续分析文件
        const currentProgress = 30 + Math.round(reportProgress * 30) // 30%~60%
        aiMessage.progress = currentProgress
      }
      break
      
    case 'web_search_raw':
      // 处理网络搜索原始数据
      if (data.results && data.results.length > 0) {
        aiMessage.webSearchRaw = {
          results: data.results || [],
          keywords: data.keywords || [],
          total: data.total || 0
        }
        aiMessage.webSearchTotal = data.total || 0
        
        // 更新状态显示：已检索到数据（联网搜索完成）
        const statusMsg = `<span class="ai-name">蜀道安全管理AI智能助手</span>已为您检索到 <span class="file-count">${aiMessage.totalFiles || 0}</span> 个知识库文件，以及 <span class="file-count">${aiMessage.webSearchTotal}</span> 个相关网络资源`
        updateMessageStatus(aiMessage, 'data_retrieved', statusMsg)
        
        console.log(`[网络搜索] 收到原始数据: ${data.total} 条结果`)
      } else {
        // 如果没有搜索结果，也要更新状态，但只显示知识库文件数
        const statusMsg = `<span class="ai-name">蜀道安全管理AI智能助手</span>已为您检索到 <span class="file-count">${aiMessage.totalFiles || 0}</span> 个知识库文件`
        updateMessageStatus(aiMessage, 'data_retrieved', statusMsg)
        console.log('[网络搜索] 无搜索结果，仅使用知识库')
      }
      break
      
    case 'web_search_summary':
      // 处理网络搜索AI总结 - 使用更快的打字机效果
      if (data.has_results && data.summary) {
        // 检查是否已经有内容，避免重复触发
        if (aiMessage._webSearchSummaryCompleted) {
          console.log('[网络搜索] 总结已完成，跳过重复打字机')
          break
        }
        
        // 保存完整内容
        aiMessage._fullWebSearchSummary = data.summary
        aiMessage.webSearchSummary = '' // 先置空，通过打字机填充
        aiMessage.hasWebSearchResults = true
        
        // 使用更快的打字机效果（速度200 = 每次20个字符）
        const summaryReport = {
          file_index: 'web_search_summary',
          report: aiMessage,
          _typewriterStates: {}
        }
        
        startReportFieldTypewriter(summaryReport, 'webSearchSummary', data.summary, 200)
          .then(() => {
            // 标记为已完成，防止重复触发
            aiMessage._webSearchSummaryCompleted = true
          })
          .catch(err => {
            console.error('网络搜索总结打字机效果失败:', err)
            aiMessage.webSearchSummary = data.summary
            aiMessage._webSearchSummaryCompleted = true
          })
        
        console.log('[网络搜索] 收到AI总结，长度:', data.summary.length)
        
        // 网络搜索总结已接收,准备保存并更新为完成状态
        updateMessageStatus(aiMessage, 'outputting')
        
        // 保存完整数据到后端 - 使用完整的summary而不是打字机过程中的部分内容
        if (aiMessage.ai_message_id) {
          const contentData = {
            reports: aiMessage.reports || [],
            webSearchRaw: aiMessage.webSearchRaw || null,
            webSearchSummary: aiMessage._fullWebSearchSummary || data.summary, // 使用完整的webSearchSummary
            hasWebSearchResults: true,
            // ===== 🔧 修复：将summary也包含在content JSON中 =====
            summary: aiMessage.summary || aiMessage._fullSummary || ''
          }
          
          const collectedContent = JSON.stringify(contentData)
          
          // 同时保存summary字段（作为单独字段）
          const summaryToSave = aiMessage.summary || aiMessage._fullSummary || ''
          
          updateAIMessageContent(aiMessage.ai_message_id, collectedContent, summaryToSave)
            .then((response) => {
              console.log('[网络搜索] AI消息保存成功，更新为完成状态')
              // 保存成功后，更新状态为completed
              updateMessageStatus(aiMessage, 'completed')
              aiMessage.isTyping = false
              isSending.value = false
              streamingReports.value.clear()
              
              // 重置AI回复流程状态
              isAIReplyProcessComplete.value = true
            })
            .catch(err => {
              console.error('[网络搜索] AI消息保存失败:', err)
              // 即使保存失败，也更新为完成状态
              updateMessageStatus(aiMessage, 'completed')
              aiMessage.isTyping = false
              isSending.value = false
            })
        } else {
          // 没有ai_message_id时，直接更新为完成状态
          updateMessageStatus(aiMessage, 'completed')
          aiMessage.isTyping = false
          isSending.value = false
        }
      }
      break
      
    case 'error':
      showToastMessage(data.message || '发生错误', 2000)
      isSending.value = false
        break
      
    case 'completed':
      console.log('[SSE] 收到completed事件')
      isSending.value = false
      streamingReports.value.clear()
      aiMessage.isTyping = false
      
      // 只有在还未完成时才更新状态
      if (aiMessage.progress < 100) {
        updateMessageStatus(aiMessage, 'completed')
      }
      
      showToastMessage('报告生成完成', 2000)
      break
      
    case 'interrupted':
      isSending.value = false
      streamingReports.value.clear()
      aiMessage.isTyping = false
      showToastMessage(data.message || '报告生成已中断', 2000)
      break
  }
}

// SSE错误处理
const handleSSEError = (error) => {
  console.error('❌ SSE连接异常断开:', error)
  
  // 清理SSE连接
  if (sseConnection) {
    closeSSEConnection(sseConnection)
    sseConnection = null
  }
  
  isSending.value = false
  streamingReports.value.clear()
  
  // 将所有正在打字的AI消息标记为完成
  chatMessages.value.forEach(message => {
    if (message.type === 'ai' && message.isTyping) {
      message.isTyping = false
    }
  })
  
  // 重置AI回复流程状态
  isAIReplyProcessComplete.value = true
  
  // 这是非预期的连接断开（网络错误、后端崩溃等）
  showToastMessage('连接已断开', 2000)
}

// SSE完成处理
const handleSSEComplete = () => {
  isSending.value = false
  
  chatMessages.value.forEach(message => {
    if (message.type === 'ai' && message.isTyping) {
      // 确保进度设置为100%
      if (message.showStats && message.reports && message.reports.length > 0) {
        message.progress = 100
        console.log('✅ SSE完成，设置进度为100%')
      }
      
      // 只有在进度达到100%或没有报告时才停止typing状态
      if (message.progress >= 100 || !message.showStats) {
        message.isTyping = false
      }
      
      if (message.ai_message_id) {
        // 构建完整的内容数据，包含报告、网络搜索结果和summary
        const contentData = {
          reports: message.reports || [],
          webSearchRaw: message.webSearchRaw || null,
          // 使用完整的webSearchSummary，而不是打字机过程中的部分内容
          webSearchSummary: message._fullWebSearchSummary || message.webSearchSummary || null,
          hasWebSearchResults: message.hasWebSearchResults || false,
          // ===== 🔧 修复：将summary也包含在content JSON中 =====
          summary: message.summary || message._fullSummary || ''
        }
        
        const collectedContent = message.reports && message.reports.length > 0 
          ? JSON.stringify(contentData)
          : message.content
        
        if (collectedContent) {
          // 同时保存summary字段（作为单独字段）
          const summaryToSave = message.summary || message._fullSummary || ''
          
          updateAIMessageContent(message.ai_message_id, collectedContent, summaryToSave)
            .catch(err => console.error('回写AI消息失败:', err))
        }
      }
    }
  })
  
  isAIReplyProcessComplete.value = true
  
  // ===== 🎯 更新历史记录列表并高亮 =====
  console.log('📝 AI回复完成，准备更新历史记录')
  console.log('  - ai_conversation_id:', ai_conversation_id.value)
  
  if (ai_conversation_id.value && ai_conversation_id.value !== 0) {
    console.log('✅ 开始更新历史记录列表')
    
    // 先清除所有高亮
    historyData.value.forEach((item) => {
      item.isActive = false
    })
    
    // 获取第一条用户消息作为标题
    const firstUserMessage = chatMessages.value.find(msg => msg.type === 'user')
    const title = firstUserMessage ? firstUserMessage.content.substring(0, 20) + '...' : '新对话'
    console.log('📝 生成的标题:', title)
    
    // 检查是否已存在
    const existingIndex = historyData.value.findIndex(item => item.id === ai_conversation_id.value)
    console.log('🔍 检查是否已存在，索引:', existingIndex)
    
    if (existingIndex === -1) {
      // 不存在，在最前面插入新项
      const newItem = {
        id: ai_conversation_id.value,
        title: title,
        time: formatTime(new Date().toISOString()),
        businessType: 0,
        isActive: true,
        rawData: {
          id: ai_conversation_id.value,
          content: firstUserMessage?.content || '',
          updated_at: new Date().toISOString()
        }
      }
      console.log('📦 准备插入的新项:', newItem)
      historyData.value.unshift(newItem)
      console.log('✅ 已在列表最前面插入新历史记录并设为高亮')
      console.log('📊 更新后的历史记录数量:', historyData.value.length)
    } else {
      // 已存在，设为高亮并移到最前面
      console.log('🔄 历史记录已存在，将其移到最前面')
      const existingItem = historyData.value.splice(existingIndex, 1)[0]
      existingItem.isActive = true
      existingItem.time = formatTime(new Date().toISOString())
      historyData.value.unshift(existingItem)
      console.log('✅ 已将现有历史记录移到最前面并设为高亮')
    }
    
    // 更新历史记录总数
    historyTotal.value = historyData.value.length
    console.log('📊 最终历史记录总数:', historyTotal.value)
  } else {
    console.warn('⚠️ ai_conversation_id 为 0 或未设置，跳过历史记录更新')
  }
  
  // ===== 🎯 获取AI相关推荐问题 =====
  console.log('🎯 准备获取AI相关推荐问题')
  // 找到最后一条用户消息和最后一条AI消息
  const lastUserMessage = chatMessages.value.filter(msg => msg.type === 'user').pop()
  const lastAIMessage = chatMessages.value.filter(msg => msg.type === 'ai').pop()
  
  if (lastUserMessage && lastAIMessage && lastAIMessage.ai_message_id) {
    console.log('📝 找到最后一条用户消息和AI消息')
    console.log('  - 用户消息:', lastUserMessage.content)
    console.log('  - AI消息ID:', lastAIMessage.ai_message_id)
    
    // 构建AI回复内容用于生成推荐问题
    let aiReplyContent = ''
    
    // 优先使用summary字段
    if (lastAIMessage.summary) {
      aiReplyContent = lastAIMessage.summary
    } 
    // 其次使用content字段
    else if (lastAIMessage.content) {
      aiReplyContent = lastAIMessage.content
    }
    // 如果有reports，使用reports的summary
    else if (lastAIMessage.reports && lastAIMessage.reports.length > 0) {
      const summaries = lastAIMessage.reports
        .filter(r => r.report && r.report.summary)
        .map(r => r.report.summary)
        .slice(0, 3) // 只取前3个
      aiReplyContent = summaries.join('\n\n')
    }
    
    if (aiReplyContent && aiReplyContent.trim()) {
      console.log('📝 AI回复内容长度:', aiReplyContent.length)
      // 获取AI相关推荐问题
      getAIRelatedQuestions(
        lastUserMessage.content, 
        aiReplyContent, 
        lastAIMessage.ai_message_id
      )
    } else {
      console.warn('⚠️ AI回复内容为空，跳过推荐问题获取')
    }
  } else {
    console.warn('⚠️ 未找到有效的用户消息或AI消息，跳过推荐问题获取')
  }
}

// SSE中断处理
const handleSSEInterrupted = (data) => {
  if (sseConnection) {
    closeSSEConnection(sseConnection)
    sseConnection = null
  }
  
  isSending.value = false
  streamingReports.value.clear()
  
  chatMessages.value.forEach(message => {
    if (message.type === 'ai' && message.isTyping) {
      message.isTyping = false
      
      // 中断时设置为完成状态
      updateMessageStatus(message, 'completed')
      
      // 保留已接收的网络搜索数据
      // 即使中断，也要确保已接收的webSearchRaw和webSearchSummary能够显示
      if (message.webSearchRaw || message.webSearchSummary) {
        console.log('✅ 保留已接收的网络搜索数据')
      }
      
      if (message.ai_message_id) {
        // 构建完整的内容数据，包含报告、网络搜索结果和summary
        const contentData = {
          reports: message.reports || [],
          webSearchRaw: message.webSearchRaw || null,
          // 使用完整的webSearchSummary，而不是打字机过程中的部分内容
          webSearchSummary: message._fullWebSearchSummary || message.webSearchSummary || null,
          hasWebSearchResults: message.hasWebSearchResults || false,
          // ===== 🔧 修复：将summary也包含在content JSON中 =====
          summary: message.summary || message._fullSummary || ''
        }
        
        const collectedContent = message.reports && message.reports.length > 0 
          ? JSON.stringify(contentData)
          : message.content
        
        if (collectedContent) {
          // 同时保存summary字段（作为单独字段）
          const summaryToSave = message.summary || message._fullSummary || ''
          
          updateAIMessageContent(message.ai_message_id, collectedContent, summaryToSave)
            .catch(err => console.error('回写AI消息失败:', err))
        }
      }
    }
  })
  
  isAIReplyProcessComplete.value = true
  showToastMessage(data.message || '报告生成已中断', 2000)
}

// 停止报告生成
const handleStopGeneration = async () => {
  if (!sseConnection || ai_conversation_id.value === undefined || ai_conversation_id.value === null) {
    return
  }
  
  if (sseConnection) {
    closeSSEConnection(sseConnection)
    sseConnection = null
  }
  
  isSending.value = false
  streamingReports.value.clear()
  
  chatMessages.value.forEach(message => {
    if (message.type === 'ai' && message.isTyping) {
      message.isTyping = false
      
      // 停止时设置为完成状态
      updateMessageStatus(message, 'completed')
      
      // 保留已接收的网络搜索数据
      // 即使停止，也要确保已接收的webSearchRaw和webSearchSummary能够显示
      if (message.webSearchRaw || message.webSearchSummary) {
        console.log('✅ 停止时保留已接收的网络搜索数据')
      }
      
      // 回写数据到后端，包含网络搜索结果和summary
      if (message.ai_message_id) {
        const contentData = {
          reports: message.reports || [],
          webSearchRaw: message.webSearchRaw || null,
          // 使用完整的webSearchSummary，而不是打字机过程中的部分内容
          webSearchSummary: message._fullWebSearchSummary || message.webSearchSummary || null,
          hasWebSearchResults: message.hasWebSearchResults || false,
          // ===== 🔧 修复：将summary也包含在content JSON中 =====
          summary: message.summary || message._fullSummary || ''
        }
        
        const collectedContent = message.reports && message.reports.length > 0 
          ? JSON.stringify(contentData)
          : message.content
        
        if (collectedContent) {
          // 同时保存summary字段（作为单独字段）
          const summaryToSave = message.summary || message._fullSummary || ''
          
          updateAIMessageContent(message.ai_message_id, collectedContent, summaryToSave)
            .catch(err => console.error('回写AI消息失败:', err))
        }
      }
    }
  })
  
  isAIReplyProcessComplete.value = true
  showToastMessage('已停止生成', 2000)
  
  // ===== 已删除：getUserId() - stopSSEStream 函数需要更新 =====
  stopSSEStream(null, ai_conversation_id.value)
    .catch(error => console.warn('停止请求失败:', error))
}

// ReportGenerator提交处理
const handleReportGeneratorSubmit = async (data) => {
  isSending.value = true
  currentQuestion.value = data.question
  
  // 添加用户消息
  chatMessages.value.push({
    id: Date.now(),
    type: 'user',
    content: data.question,
    timestamp: new Date().toISOString()
  })
  
  // 添加AI消息占位符
  const aiMessageIndex = chatMessages.value.length
  chatMessages.value.push({
    id: Date.now() + 1,
    type: 'ai',
    userQuestion: data.question, // 用户问题
    summary: '',
    totalFiles: 0,
    webSearchTotal: 0,
    progress: 0,
    completedCount: 0,
    reports: [],
    isTyping: true,
    content: '',
    displayContent: '',
    timestamp: new Date().toISOString(),
    // 新增：状态管理
    currentStatus: 'querying_kb', // 当前状态
    statusMessage: '🔍 蜀道安全管理AI智能助手正在为您分析知识库……', // 状态消息
    showStats: true, // 是否显示统计卡片
    // 新增：后端消息ID（初始为null，从SSE接收后赋值）
    ai_message_id: null,
    rawData: null,
    userFeedback: null
  })

  try {
    const apiPrefix = getApiPrefix()
    const url = `${apiPrefix}/report/complete-flow`

    // 构建 POST 请求体
    const requestBody = {
      user_question: data.question,
      window_size: data.windowSize,
      n_results: 2,
      ai_conversation_id: ai_conversation_id.value,
      is_network_search_enabled: isNetworkSearchEnabled.value
    }

    console.log('📤 发起 SSE POST 请求:', {
      url,
      body: requestBody
    })

    sseConnection = createSSEConnection(url, {
      onMessage: (eventData) => handleSSEMessage(eventData, aiMessageIndex),
      onError: handleSSEError,
      onComplete: handleSSEComplete,
      onInterrupted: handleSSEInterrupted
    }, {
      body: requestBody
    })

  } catch (error) {
    console.error('启动失败:', error)
    showToastMessage(`启动失败: ${error.message}`, 2000)
    isSending.value = false
  }
}

// ========== 推荐问题相关函数 ==========

// 处理推荐问题点击
const handleRelatedQuestion = (question) => {
  console.log('点击推荐问题:', question)
  
  // 清除相关推荐问题
  aiRelatedQuestions.value = []
  relatedQuestionsMessageId.value = null
  
  // 将问题设置到输入框并自动发送
  messageText.value = question
  sendMessage()
}

// 获取AI回复相关推荐问题
const getAIRelatedQuestions = async (userMessage, aiReply, messageId) => {
  if (!userMessage || !aiReply || !messageId) {
    console.log('参数不完整，跳过相关推荐问题获取')
    return
  }
  
  try {
    console.log('开始获取AI回复相关推荐问题')
    console.log('用户问题:', userMessage)
    console.log('AI回复:', aiReply.substring(0, 100) + '...')
    console.log('消息ID:', messageId)
    
    isGettingRelatedQuestions.value = true
    
    // 设计提示词让大模型返回3条相关推荐问题（与PC端一致）
    const prompt = `基于以下对话内容，直接生成3条相关的推荐问题。
【对话内容】
用户问题：${userMessage}
AI回复：${aiReply}

【生成要求】
- 直接输出3个问题，每个问题一行
- 问题要与原问题相关但有所延伸
- 问题要具体、实用，符合中文表达习惯
- 不要包含任何编号、标题、说明文字
- 不要重复上述指令内容`
    
    const response = await apis.guessYouWant({
      message: prompt,
      // ===== 已删除：user_id - 后端从token解析 =====
      ai_message_id: messageId
    })
    
    console.log('AI相关推荐问题响应:', response)
    
    if (response.statusCode === 200 && response.data && response.data.reply) {
      // 解析大模型返回的推荐问题
      const replyText = response.data.reply.trim()
      const questions = replyText.split('\n')
        .map(q => q.trim())
        .filter(q => q.length > 0)
        .filter((q, index, arr) => arr.indexOf(q) === index) // 去重
        .slice(0, 3) // 只取前3个
      
      if (questions.length > 0) {
        aiRelatedQuestions.value = questions
        relatedQuestionsMessageId.value = messageId
        console.log('✅ AI相关推荐问题数据已设置:', aiRelatedQuestions.value)
        console.log('✅ relatedQuestionsMessageId设置为:', relatedQuestionsMessageId.value)
        console.log('✅ messageId类型:', typeof messageId, '值:', messageId)
        
        // 强制更新DOM
        nextTick(() => {
          console.log('✅ DOM已更新，推荐问题应该显示了')
          scrollToBottom()
        })
      } else {
        console.log('解析推荐问题失败，使用默认问题')
        aiRelatedQuestions.value = []
        relatedQuestionsMessageId.value = null
      }
    } else {
      console.error('获取AI相关推荐问题失败:', response.statusCode)
      aiRelatedQuestions.value = []
      relatedQuestionsMessageId.value = null
    }
  } catch (error) {
    console.error('获取AI相关推荐问题失败:', error)
    aiRelatedQuestions.value = []
    relatedQuestionsMessageId.value = null
  } finally {
    isGettingRelatedQuestions.value = false
  }
}

// 页面卸载事件处理函数
const handlePageUnload = () => {
  if (speakingMessageId.value) {
    stopAllAudio()
    speakingMessageId.value = null
  }
}

// 页面可见性变化处理函数
const handleVisibilityChange = () => {
  if (document.hidden && speakingMessageId.value) {
    stopAllAudio()
    speakingMessageId.value = null
  }
}

// 处理中括号引用点击
const handleStandardReferenceClick = async (event) => {
  const target = event.target
  if (target.classList.contains('standard-reference')) {
    // 只有点击标准引用时才阻止事件冒泡和默认行为
    event.preventDefault()
    event.stopPropagation()
    const standard = target.getAttribute('data-standard')
    const reference = target.getAttribute('data-reference')
    
    // 添加调试信息
    console.log('点击的元素:', target)
    console.log('元素的HTML:', target.outerHTML)
    console.log('data-standard属性:', standard)
    console.log('data-reference属性:', reference)
    console.log('所有data属性:', target.dataset)
    
    console.log('点击了标准引用:', standard || reference)
    
    // 确定要查询的文件名
    let fileName = ''
    if (standard) {
      fileName = standard
    } else if (reference) {
      // 如果是普通引用，使用完整的引用标题作为文件名
      fileName = reference
    }
    
    if (fileName) {
      try {
        // 调用后端接口获取文件链接
        const response = await apis.getFileLink({ fileName })
        console.log('获取文件链接响应:', response)
        
        if (response.statusCode === 200 && response.data) {
          const fileLink = response.data
          console.log('获取到文件链接:', fileLink)
          
          // 如果有文件链接，打开预览
          if (fileLink) {
            // 在新窗口中打开文件链接
            window.open(fileLink, '_blank')
            // showToastMessage('正在打开文件预览...')
          } else {
            showToastMessage('暂无文件')
          }
        } else {
          showToastMessage('暂无文件')
        }
      } catch (error) {
        console.error('获取文件链接失败:', error)
        showToastMessage('获取文件失败，请稍后重试')
      }
    }
  }
}

// 绑定规范引用点击事件
const bindStandardReferenceEvents = () => {
  const references = document.querySelectorAll('.standard-reference')
  console.log('找到规范引用元素数量:', references.length)
  
  references.forEach((ref, index) => {
    // 移除之前的事件监听器
    ref.removeEventListener('click', handleStandardReferenceClick)
    
    // 添加新的点击事件监听器
    ref.addEventListener('click', handleStandardReferenceClick)
    
    console.log(`绑定规范引用 ${index + 1}:`, ref.textContent)
  })
}

// 页面加载时不再自动加载历史记录，改为点击菜单时加载
onMounted(async () => {
  try {
    console.log('🚀 移动端AI问答页面初始化，加载功能卡片...')
    await getFunctionCards()
    
    // 添加页面卸载和可见性变化事件监听器，在刷新时停止语音朗读
    window.addEventListener('beforeunload', handlePageUnload)
    window.addEventListener('unload', handlePageUnload)
    document.addEventListener('visibilitychange', handleVisibilityChange)
    
    // 添加规范引用点击事件监听器
    document.addEventListener('click', handleStandardReferenceClick)
    
    // 添加滚动监听，实现进度卡片悬浮效果
    // 移动端使用 window 滚动而不是容器滚动
    console.log('✅ 移动端：添加 window 滚动监听')
    

    
    // 测试TTS服务连接
    try {
      const ttsTest = await testTTSConnection()
      if (ttsTest.success) {
        console.log('✅ 移动端TTS服务连接正常')
      } else {
        console.warn('⚠️ 移动端TTS服务连接异常:', ttsTest.message)
      }
    } catch (error) {
      console.warn('⚠️ 移动端TTS服务连接测试失败:', error)
    }
    
    // 检查是否有自动发送的消息
    const autoMessage = route.query.autoMessage
    if (autoMessage) {
      console.log('检测到移动端自动发送消息:', autoMessage)
      
      // 立即清除URL中的autoMessage参数，防止刷新时重复发送
      router.replace({
        path: route.path,
        query: { ...route.query, autoMessage: undefined }
      })
      
      // 延迟一下，确保组件完全挂载
      // setTimeout(() => {
      //   autoSendMessage(autoMessage)
      // }, 100)
      autoSendMessage(autoMessage)
    }
    
    console.log('✅ 移动端AI问答页面初始化完成')
  } catch (error) {
    console.error('❌ 移动端AI问答页面初始化失败:', error)
  }
})

// 监听历史记录抽屉显示状态，显示时加载数据
watch(showHistory, async (newVal) => {
  if (newVal && historyData.value.length === 0) {
    console.log('📋 历史记录抽屉打开，开始加载数据...')
    await getHistoryRecordList()
  }
})

// 监听语音识别错误
watch(transcript, (newVal) => {
  if (!newVal || isListening.value) return
  messageText.value = newVal
})

watch(speechError, (newVal) => {
  if (newVal) {
    console.error('语音识别错误:', newVal)
    showToastMessage(newVal)
  }
})


// 根据功能卡片标题返回对应的图标
const getFunctionCardIcon = (title) => {
  // 按顺序循环使用4个图标
  const icons = [bridgeIcon, constructionIcon, materialIcon, standardIcon]
  const icon = icons[functionCardIconIndex % icons.length]
  functionCardIconIndex++
  return icon
}

// 点击功能卡片
const handleFunctionCard = (cardType) => {
  console.log('点击功能卡片:', cardType)
  
  // 重置聊天状态，准备创建新的对话
  chatMessages.value = []
  ai_conversation_id.value = 0
  
  // 显示聊天界面
  showChat.value = true
  
  // 直接使用卡片类型作为消息内容
  const message = `请详细介绍${cardType}的相关内容`
  messageText.value = message
  
  // 自动发送消息
  sendMessage()
}

// 调用流式聊天接口
const callStreamChatWithDB = async (messageToAI, aiMessage, userMessage, currentMessage, responseContent, onlineSearchContent = null) => {
  try {
    console.log('开始调用流式聊天接口...')
    
    // 重置状态
    responseContent.value = ''
    aiMessage.content = ''
    aiMessage.isTyping = true
    aiMessage.isStreaming = true // 确保标记为流式

    const response = await fetch('/apiv1/stream/chat-with-db', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        message: messageToAI,
        // ===== 已删除：user_id - 后端从token解析 =====
        ai_conversation_id: ai_conversation_id.value,
        business_type: 0, // AI问答类型
        ai_message_id: aiMessage.id,
        online_search_content: onlineSearchContent // 添加联网搜索内容
      })
    })
    
    if (!response.ok) {
      throw new Error(`HTTP错误: ${response.status}`)
    }
    
    const reader = response.body.getReader()
    const decoder = new TextDecoder('utf-8')
    let buffer = ''
    let isStreamComplete = false

    // 【核心修改】创建专门的内容更新函数 - 先接收完所有数据
    const updateContent = (newContent) => {
      if (!newContent) return
      
      // 处理转义的换行符
      const processedContent = newContent.replace(/\\n/g, '\n')
      
      // 累加到总内容，但不立即显示
      responseContent.value += processedContent
      
      console.log('接收内容:', processedContent, '总长度:', responseContent.value.length)
    }

    // 【新增】接收完所有数据后的逐字渲染函数
    const renderContentCharByChar = (fullContent) => {
      if (!fullContent) {
        console.log('renderContentCharByChar: 内容为空')
        return Promise.resolve()
      }
      
      console.log('renderContentCharByChar: 开始渲染，内容长度:', fullContent.length)
      const processedContent = fullContent.replace(/\\n/g, '\n')
      
      // 清空当前内容，准备逐字显示
      aiMessage.content = ''
      aiMessage.displayContent = '' // 同时清空displayContent
      console.log('renderContentCharByChar: 已清空aiMessage.content和displayContent')
      
      return new Promise((resolve) => {
        let currentIndex = 0
        let isRendering = true // 添加渲染状态标志
        
        const renderNextChar = async () => {
          // 检查是否还在渲染状态
          if (!isRendering) {
            console.log('renderContentCharByChar: 渲染已停止')
            return
          }
          
          if (currentIndex < processedContent.length) {
            const char = processedContent[currentIndex]
            aiMessage.content += char
            
            console.log(`renderContentCharByChar: 添加字符 ${currentIndex + 1}/${processedContent.length}: "${char}", 当前内容长度: ${aiMessage.content.length}`)
            
            // 【关键修复】实时进行Markdown渲染，添加超时保护
            try {
              const renderPromise = renderWithVditor(aiMessage.content)
              const timeoutPromise = new Promise((_, reject) => 
                setTimeout(() => reject(new Error('渲染超时')), 1000) // 1秒超时
              )
              
              const htmlReply = await Promise.race([renderPromise, timeoutPromise])
              aiMessage.displayContent = htmlReply
            } catch (error) {
              console.error('Markdown渲染失败:', error)
              // 如果渲染失败，使用纯文本，但继续渲染
              aiMessage.displayContent = aiMessage.content
            }
            
            // 每次添加一个字符都触发更新
            chatMessages.value = [...chatMessages.value]
            
            // 滚动到底部
            setTimeout(scrollToBottom, 0)
            
            currentIndex++
            
            // 使用setTimeout而不是await，避免阻塞Vue更新
            setTimeout(renderNextChar, 10) // 10ms 每个字，加快速度
          } else {
            // 渲染完成
            console.log('renderContentCharByChar: 渲染完成')
            isRendering = false
            resolve()
          }
        }
        
        // 开始渲染
        console.log('renderContentCharByChar: 开始第一个字符')
        renderNextChar()
        
        // 添加超时保护，防止无限卡住
        setTimeout(() => {
          if (isRendering) {
            console.warn('renderContentCharByChar: 渲染超时，强制完成')
            isRendering = false
            resolve()
          }
        }, 30000) // 30秒超时
      })
    }

    while (!isStreamComplete) {
      const { done, value } = await reader.read()
      
      if (done) {
        console.log('流读取完成')
        isStreamComplete = true
        break
      }
      
      const chunk = decoder.decode(value, { stream: true })
      console.log('原始数据块:', chunk)
      
      buffer += chunk
      const lines = buffer.split('\n')
      buffer = lines.pop() || ''
      
      for (const line of lines) {
        if (line.trim() === '') continue
        
        console.log('处理数据行:', line)
        
        if (line.startsWith('data: ')) {
          const data = line.substring(6)
          
          if (data === '[DONE]') {
            console.log('收到结束信号 [DONE]')
            isStreamComplete = true
            break
          }
          
          try {
            const parsed = JSON.parse(data)
            
            if (parsed.type === 'initial') {
              console.log('收到初始响应:', parsed)
              if (parsed.ai_conversation_id) {
                ai_conversation_id.value = parsed.ai_conversation_id
              }
              if (parsed.ai_message_id) {
                const oldMessageId = aiMessage.id
                aiMessage.id = parsed.ai_message_id
                
                // 将搜索结果从旧ID迁移到新ID
                if (onlineSearchResults.value[oldMessageId]) {
                  onlineSearchResults.value[parsed.ai_message_id] = onlineSearchResults.value[oldMessageId]
                  expandedOnlineSearchResults.value[parsed.ai_message_id] = expandedOnlineSearchResults.value[oldMessageId] || false
                  
                  // 删除旧ID的搜索结果
                  delete onlineSearchResults.value[oldMessageId]
                  delete expandedOnlineSearchResults.value[oldMessageId]
                  
                  console.log('搜索结果已从旧ID迁移到新ID:', oldMessageId, '->', parsed.ai_message_id)
                  
                  // 使用新的ai_message_id重新保存到数据库
                  try {
                    const saveData = {
                      id: parsed.ai_message_id,
                      ai_conversation_id: ai_conversation_id.value,
                      search_source: JSON.stringify(onlineSearchResults.value[parsed.ai_message_id])
                    }
                    
                    const saveResponse = await apis.saveOnlineSearchResult(saveData)
                    console.log('使用新ID保存联网搜索结果到数据库成功:', saveResponse)
                  } catch (saveError) {
                    console.error('使用新ID保存联网搜索结果到数据库失败:', saveError)
                  }
                }
              }
              continue
            }
            
            if (parsed.error) {
              throw new Error(parsed.error)
            }
            
            // 【修改】只累加内容，不立即逐字输出
            if (parsed.choices && parsed.choices.length > 0) {
              const choice = parsed.choices[0]
              if (choice.delta && choice.delta.content) {
                updateContent(choice.delta.content)
              }
              
              if (choice.finish_reason) {
                console.log('收到完成信号:', choice.finish_reason)
                isStreamComplete = true
                break
              }
            } else if (parsed.content) {
              updateContent(parsed.content)
            } else {
              const textContent = String(parsed)
              if (textContent && textContent !== '[object Object]') {
                updateContent(textContent)
              }
            }
          } catch (e) {
            console.log('作为文本内容处理:', data)
            updateContent(data)
          }
        } else {
          console.log('处理纯文本内容:', line)
          updateContent(line)
        }
      }
      
      if (isStreamComplete) break
    }
    
    // 流式响应完成
    console.log('流式响应完成，开始逐字渲染，最终内容长度:', responseContent.value.length)
    
    // 开始逐字渲染（保持isTyping = true来显示光标）
    await renderContentCharByChar(responseContent.value)
    
    // 渲染完成后设置状态
    aiMessage.isTyping = false
    aiMessage.isStreaming = false
    
    // 【简化】后续处理
    await processFinalResponse(aiMessage, responseContent.value, userMessage, !!onlineSearchContent)
    
  } catch (error) {
    console.error('调用流式聊天接口失败:', error)
    aiMessage.isTyping = false
    aiMessage.isStreaming = false
    aiMessage.content = '抱歉，网络连接出现问题，请检查网络后重试。'
    aiMessage.displayContent = await renderWithVditor(aiMessage.content)
  }
}

// 提取后续处理逻辑到单独函数
const processFinalResponse = async (aiMessage, finalContent, userMessage, hasOnlineSearch = false) => {
  const processedReply = processAIResponse(finalContent)
  aiMessage.content = finalContent
  
  // 只有在displayContent为空或没有HTML标签时才重新渲染
  if (!aiMessage.displayContent || !aiMessage.displayContent.includes('<')) {
    const htmlReply = await renderWithVditor(processedReply)
    aiMessage.displayContent = htmlReply
  }
  
  // 只需要在最后更新一次
  chatMessages.value = [...chatMessages.value]
  
  // 获取历史记录
  await getHistoryRecordList()
  
  if (ai_conversation_id.value > 0) {
    historyData.value.forEach((item) => {
      item.isActive = item.id === ai_conversation_id.value
    })
  }
  
  // 启动后续流程
  setTimeout(async () => {
    isAIReplyProcessComplete.value = false
    
    // 如果还没有进行联网搜索，则进行联网搜索
    if (!hasOnlineSearch && isNetworkSearchEnabled.value) {
      await performOnlineSearch(userMessage.content, aiMessage.id)
    }
    
    // 获取推荐问题
    await getAIRelatedQuestions(userMessage.content, processedReply, aiMessage.id)
    
    setTimeout(() => {
      scrollToBottom()
      isAIReplyProcessComplete.value = true
      console.log('AI回复流程完成')
    }, 100)
  }, 200)
}

// 发送消息
// 发送消息 - 使用SSE接口
const sendMessage = async () => {
  if (!messageText.value.trim() || isSending.value) return
  
  console.log('📤 移动端发送消息:', messageText.value)
  
  // 清除推荐问题
  aiRelatedQuestions.value = []
  relatedQuestionsMessageId.value = null
  
  isSending.value = true
  showChat.value = true
  
  // 如果是新对话，清除所有历史记录的选中状态
  if (chatMessages.value.length === 0) {
    historyData.value.forEach((item) => {
      item.isActive = false
    })
    expandedSearchSources.value = {}
    expandedOnlineSearchResults.value = {}
    onlineSearchResults.value = {}
  }
  
  // 保存当前消息并清空输入框
  const currentMessage = messageText.value
  messageText.value = ''
  
  // 调用ReportGenerator的SSE接口
  await handleReportGeneratorSubmit({
    question: currentMessage,
    windowSize: 3,
    nResults: 10
  })
  
  scrollToBottom()
}


// 开始打字效果（非流式接口使用）
const startTypingEffect = async (aiMessage, htmlReply, processedReply, currentMessage) => {
  console.log('开始打字效果，HTML长度:', htmlReply.length)
  
  // 开始打字效果 - 按完整HTML标签或文本块显示，避免标签分割
  const textBlocks = []
  let currentBlock = ''
  let inTag = false
  let tagContent = ''
  
  // 将HTML内容分割成完整的块
  for (let i = 0; i < htmlReply.length; i++) {
    const char = htmlReply[i]
    
    if (char === '<') {
      // 如果之前有文本内容，先保存
      if (currentBlock && !inTag) {
        textBlocks.push({ type: 'text', content: currentBlock })
        currentBlock = ''
      }
      inTag = true
      tagContent = char
    } else if (char === '>') {
      // 标签结束
      tagContent += char
      textBlocks.push({ type: 'tag', content: tagContent })
      inTag = false
      tagContent = ''
    } else if (inTag) {
      // 在标签内
      tagContent += char
    } else {
      // 普通文本
      currentBlock += char
    }
  }
  
  // 保存最后一个文本块
  if (currentBlock) {
    textBlocks.push({ type: 'text', content: currentBlock })
  }
  
  // 检查是否有未闭合的标签
  if (tagContent) {
    textBlocks.push({ type: 'tag', content: tagContent })
  }
  
  console.log('分割后的文本块:', textBlocks)
  
  let currentBlockIndex = 0
  let currentCharIndex = 0
  
  const typeInterval = setInterval(() => {
    if (currentBlockIndex < textBlocks.length) {
      const currentBlock = textBlocks[currentBlockIndex]
      
      if (currentBlock.type === 'tag') {
        // 标签直接显示，不分字符
        console.log('显示HTML标签:', currentBlock.content)
        aiMessage.displayContent += currentBlock.content
        currentBlockIndex++
        currentCharIndex = 0
      } else {
        // 文本按字符显示
        if (currentCharIndex < currentBlock.content.length) {
          const newContent = aiMessage.displayContent + currentBlock.content[currentCharIndex]
          aiMessage.displayContent = newContent
          currentCharIndex++
        } else {
          // 当前文本块完成，移动到下一个
          currentBlockIndex++
          currentCharIndex = 0
        }
      }
      
      // 强制触发Vue响应式更新
      chatMessages.value = [...chatMessages.value]
      
      // 自动滚动到底部
      scrollToBottom()
    } else {
      // 所有块都显示完成
      aiMessage.isTyping = false
      aiMessage.content = processedReply // 保存完整内容
      clearInterval(typeInterval)
      console.log('打字完成，最终displayContent:', aiMessage.displayContent)
      
      // 强制触发Vue响应式更新，确保hasTypingMessage计算属性更新
      chatMessages.value = [...chatMessages.value]
      console.log('打字完成，强制更新响应式数据')
      
      // AI回复完成后，进行联网搜索（如果启用）
      if (isNetworkSearchEnabled.value) {
        console.log('开始联网搜索，消息ID:', aiMessage.id, '用户输入:', currentMessage)
        performOnlineSearch(currentMessage, aiMessage.id)
        // 联网搜索启用时，推荐问题会在联网搜索完成后获取
      } else {
        // 联网搜索未启用时，直接获取推荐问题
        setTimeout(async () => {
          console.log('联网搜索未启用，开始获取推荐问题，消息ID:', aiMessage.id)
          await getAIRelatedQuestions(currentMessage, processedReply, aiMessage.id)
        }, 1000)
      }
      
      // AI回复完成后，获取最新的历史记录
      getHistoryRecordList()
    }
  }, 50) // 每50ms显示一个字符
}


// 滚动到底部
const scrollToBottom = () => {
  nextTick(() => {
    const chatContainer = document.querySelector('.chat-messages')
    if (chatContainer) {
      chatContainer.scrollTop = chatContainer.scrollHeight
    }
  })
}

// 获取功能卡片数据
const getFunctionCards = async () => {
  try {
    console.log('开始获取功能卡片...')
    const response = await apis.getFunctionCard({ function_type: 0 }) // 0为AI问答类型
    console.log('功能卡片响应:', response)
    
    if (response.statusCode === 200) {
      functionCards.value = response.data
      console.log('功能卡片数据已设置:', functionCards.value)
    } else {
      console.error('获取功能卡片失败:', response.statusCode)
    }
  } catch (error) {
    console.error('获取功能卡片失败:', error)
  }
}

// 获取热点问题数据
const getHotQuestions = async () => {
  try {
    console.log('开始获取热点问题...')
    const response = await apis.getHotQuestion({ question_type: 0 }) // 0为AI问答类型
    console.log('热点问题响应:', response)
    
    if (response.statusCode === 200) {
      hotQuestions.value = response.data
      console.log('热点问题数据已设置:', hotQuestions.value)
    } else {
      console.error('获取热点问题失败:', response.statusCode)
    }
  } catch (error) {
    console.error('获取热点问题失败:', error)
  }
}

// Toast显示函数
const showToastMessage = (message, duration = 2000) => {
  // 立即关闭当前提示（如果有的话）
  showToast.value = false
  
  // 使用 nextTick 确保关闭动画完成后再显示新提示
  nextTick(() => {
    toastMessage.value = message
    toastDuration.value = duration
    showToast.value = true
  })
}

// 复制到剪贴板
const copyToClipboard = async (text) => {
  try {
    await navigator.clipboard.writeText(text)
    showToastMessage('复制成功')
  } catch (error) {
    console.error('复制失败:', error)
    showToastMessage('复制失败', 'error')
  }
}

// 复制用户消息
const copyUserMessage = (message) => {
  copyToClipboard(message.content)
}

// 复制AI消息
const copyAIMessage = (message) => {
  // 优先使用summary（非专业问题的回复），然后是displayContent，最后是content
  let textToCopy = message.summary || message.displayContent || message.content
  
  // 如果包含HTML标签，转换为纯文本
  if (textToCopy && textToCopy.includes('<')) {
    // 创建临时DOM元素来提取纯文本
    const tempDiv = document.createElement('div')
    tempDiv.innerHTML = textToCopy
    textToCopy = tempDiv.textContent || tempDiv.innerText || textToCopy
  }
  
  // 如果还是没有内容，尝试从reports中提取
  if (!textToCopy && message.reports && message.reports.length > 0) {
    textToCopy = message.reports
      .filter(r => r.type !== 'category_title')
      .map(r => r.report || '')
      .join('\n\n')
  }
  
  // 如果有网络搜索总结，也添加进去
  if (message.webSearchSummary) {
    textToCopy = textToCopy 
      ? `${textToCopy}\n\n【网络搜索总结】\n${message.webSearchSummary}`
      : message.webSearchSummary
  }
  
  if (textToCopy && textToCopy.trim()) {
  copyToClipboard(textToCopy)
  } else {
    showToastMessage('暂无可复制的内容')
  }
}

// 编辑用户消息
const editUserMessage = (message) => {
  console.log('编辑用户消息:', message.content)
  messageText.value = message.content
  
  // 聚焦到输入框
  nextTick(() => {
    const inputElement = document.querySelector('.message-input')
    if (inputElement) {
      inputElement.focus()
      inputElement.setSelectionRange(inputElement.value.length, inputElement.value.length)
    }
  })
}



// 语音输入相关方法
const handleVoiceClick = () => {
  console.log('点击语音按钮')
  if (!speechSupported.value) {
    showToastMessage('当前浏览器不支持语音识别')
    return
  }
  if (isListening.value) {
    // 如果正在录音，则停止
    stopVoiceInput()
  } else {
    // 开始语音输入
    startVoiceInput()
  }
}

const startVoiceInput = () => {
  console.log('开始语音输入')
  
  // 开始语音识别
  const success = startListening()
  if (!success) {
    showToastMessage('语音识别启动失败，请检查麦克风权限')
  }
}

const stopVoiceInput = () => {
  console.log('停止语音输入')
  stopListening()
  
  // 语音识别完成后，将结果填入输入框
  if (transcript.value.trim()) {
    messageText.value = transcript.value
  }
}

// 重新生成AI回复
const regenerateResponse = async (messageIndex) => {
  console.log('重新生成回复，消息索引:', messageIndex)
  
  // 找到对应的用户消息
  if (messageIndex > 0) {
    const userMessage = chatMessages.value[messageIndex - 1]
    if (userMessage && userMessage.type === 'user') {
      console.log('重新发送用户消息:', userMessage.content)
      
      // 恢复消息文本
      messageText.value = userMessage.content
      
      // 调用sendMessage函数重新发送
      await sendMessage()
    }
  }
}

// 删除弹窗相关方法
const handleDeleteClick = (messageIndex) => {
  console.log('点击删除按钮，消息索引:', messageIndex)
  
  // 检查是否只剩一句AI回复和一句用户发言
  if (chatMessages.value.length === 2) {
    showToastMessage('第一句话无法删除', 'warning')
    return
  }
  
  // 设置删除类型和要删除的消息索引
  deleteType.value = 'message'
  deleteTargetItem.value = { messageIndex }
  showDeleteModal.value = true
}

// 点赞和点踩功能
const handleThumbsUp = async (message) => {
  console.log('点赞消息:', message.id)
  
  // 如果已经点赞，则取消点赞
  if (message.userFeedback === 'like') {
    message.userFeedback = null
  } else {
    // 设置点赞，取消点踩
    message.userFeedback = 'like'
  }
  
  // 强制触发Vue响应式更新
  chatMessages.value = [...chatMessages.value]
  
  // 同步反馈到后端
  await syncFeedbackToBackend(message)
}

const handleThumbsDown = async (message) => {
  console.log('点踩消息:', message.id)
  
  // 如果已经点踩，则取消点踩
  if (message.userFeedback === 'dislike') {
    message.userFeedback = null
  } else {
    // 设置点踩，取消点赞
    message.userFeedback = 'dislike'
  }
  
  // 强制触发Vue响应式更新
  chatMessages.value = [...chatMessages.value]
  
  // 同步反馈到后端
  await syncFeedbackToBackend(message)
}

// 同步用户反馈到后端
const syncFeedbackToBackend = async (message) => {
  try {
    console.log('🔍 syncFeedbackToBackend 收到的message对象:', {
      id: message.id,
      ai_message_id: message.ai_message_id,
      rawData: message.rawData,
      rawData_id: message.rawData?.id
    })
    
    // 优先使用ai_message_id，其次使用rawData.id
    const messageId = message.ai_message_id || (message.rawData && message.rawData.id)
    
    if (!messageId) {
      console.warn('❌ 消息缺少ID，无法同步反馈')
      console.warn('  - message.ai_message_id:', message.ai_message_id)
      console.warn('  - message.rawData:', message.rawData)
      return
    }
    
    const feedback = convertFeedbackToBackend(message.userFeedback)
    console.log('✅ 同步反馈到后端:', {
      messageId: messageId,
      feedback: feedback
    })
    
    // 调用后端点赞点踩接口
    const response = await apis.likeAndDislike({
      id: messageId,
      user_feedback: feedback
    })
    
    if (response.statusCode === 200) {
    console.log('反馈同步成功')
      // 根据反馈类型显示不同提示
      if (feedback === 2) {
        showToastMessage('点赞成功')
      } else if (feedback === 3) {
        showToastMessage('点踩成功')
      } else {
        showToastMessage('已取消反馈')
      }
    } else {
      console.error('反馈同步失败:', response.msg)
      showToastMessage('反馈提交失败，请稍后重试', 'error')
    }
    
  } catch (error) {
    console.error('同步反馈失败:', error)
    showToastMessage('反馈提交失败，请稍后重试', 'error')
  }
}

// 统一的确认删除函数
const confirmDelete = async () => {
  if (!deleteTargetItem.value) return
  
  if (deleteType.value === 'history') {
    await confirmDeleteHistory()
  } else if (deleteType.value === 'message') {
    await confirmDeleteMessage()
  }
}

// 确认删除历史记录
const confirmDeleteHistory = async () => {
  const { item: historyItem, index } = deleteTargetItem.value
  
  try {
    // 调用删除接口
    const response = await apis.deleteHistoryRecord({ 
      ai_conversation_id: historyItem.id 
    })
    
    if (response.statusCode === 200) {
      // 删除成功，从列表中移除
      historyData.value.splice(index, 1)
      
      // 如果删除的是当前激活的历史记录，执行新建任务
      if (historyItem.isActive) {
        console.log('删除激活的历史记录，执行新建任务')
        startNewChat()
      }
      
      console.log('历史记录删除成功')
      showToastMessage('删除成功')
    } else {
      console.error('删除历史记录失败:', response.msg)
      showToastMessage(response.msg || '删除失败', 'error')
    }
  } catch (error) {
    console.error('删除历史记录失败:', error)
    showToastMessage('删除失败，请稍后重试', 'error')
  } finally {
    // 关闭弹窗并清除目标项
    showDeleteModal.value = false
    deleteTargetItem.value = null
    deleteType.value = ''
  }
}

// 确认删除消息
const confirmDeleteMessage = async () => {
  const { messageIndex } = deleteTargetItem.value
  
  try {
    const aiMessage = chatMessages.value[messageIndex]
    
    // 检查是否有id
    if (aiMessage && aiMessage.id) {
      try {
        // 调用后端删除接口
        const response = await apis.deleteConversation({
          ai_message_id: aiMessage.id
        })
        
        if (response.statusCode === 200) {
          // 后端删除成功，从前端数组中删除AI消息和对应的用户消息
          // 删除AI消息
          chatMessages.value.splice(messageIndex, 1)
          // 删除对应的用户消息（前一条）
          if (messageIndex > 0) {
            chatMessages.value.splice(messageIndex - 1, 1)
          }
          
          console.log('删除成功')
          showToastMessage('删除成功')
        } else {
          console.error('删除失败:', response.msg)
          showToastMessage('删除失败，请稍后重试', 'error')
        }
      } catch (error) {
        console.error('删除接口调用失败:', error)
        showToastMessage('删除失败，请稍后重试', 'error')
      }
    } else {
      console.log('没有id，仅从前端删除')
      // 没有id的情况，仅从前端删除
      chatMessages.value.splice(messageIndex, 1)
      showToastMessage('删除成功')
    }
  } catch (error) {
    console.error('删除消息失败:', error)
    showToastMessage('删除失败，请稍后重试', 'error')
  } finally {
    // 关闭弹窗并清除目标项
    showDeleteModal.value = false
    deleteTargetItem.value = null
    deleteType.value = ''
  }
}

// 取消删除
const cancelDelete = () => {
  showDeleteModal.value = false
  deleteTargetItem.value = null
  deleteType.value = ''
}

// 组件销毁前，强制停止任何朗读
onBeforeUnmount(() => {
  if (speakingMessageId.value) {
    stopAllAudio()
    speakingMessageId.value = null
  }
  
  // 清理页面卸载和可见性变化事件监听器
  window.removeEventListener('beforeunload', handlePageUnload)
  window.removeEventListener('unload', handlePageUnload)
  document.removeEventListener('visibilitychange', handleVisibilityChange)

  // 清理规范引用点击事件监听器
  document.removeEventListener('click', handleStandardReferenceClick)
})

// 页面重新激活时，重新渲染所有AI消息的markdown内容
onActivated(async () => {
  console.log('移动端页面重新激活，检查并重新渲染markdown内容')
  
  // 等待DOM更新
  await nextTick()
  
  // 重新渲染所有AI消息的markdown内容
  for (const message of chatMessages.value) {
    if (message.type === 'ai' && message.content && !message.isTyping) {
      try {
        console.log('重新渲染AI消息markdown:', message.id)
        const processedReply = processAIResponse(message.content)
        const processedReplyWithFileDisplay = processFileDisplay(processedReply, message.file)
        const htmlReply = await renderWithVditor(processedReplyWithFileDisplay)
        message.displayContent = htmlReply
        
        // 重新绑定规范引用点击事件
        setTimeout(() => {
          bindStandardReferenceEvents()
        }, 100)
      } catch (error) {
        console.error('重新渲染markdown失败:', error)
        // 如果重新渲染失败，保持原有内容
      }
    }
  }
  
  // 强制触发Vue响应式更新
  chatMessages.value = [...chatMessages.value]
  
  console.log('移动端页面重新激活完成，markdown内容已重新渲染')
})
</script>

<style lang="less" scoped>
.mobile-chat {
  min-height: 100vh;
  background: #EBF3FF;
  font-family: "Alibaba PuHuiTi 3.0", sans-serif;
  overflow-x: hidden; // 防止水平滚动
  -webkit-overflow-scrolling: touch; // iOS平滑滚动
  touch-action: manipulation; // 禁用双击缩放
}

/* 使用通用 MobileHeader 组件后，移除页面内头部样式 */

.mobile-content {
  padding: 20px;
  text-align: center;
  position: relative;
  padding-bottom: 80px; // 减少底部预留空间
  
  .coming-soon {
    font-size: 20px;
    color: #666;
    margin-top: 50px;
  }
}

/* AI助手介绍 */
.ai-intro {
  display: flex;
  flex-direction: column;
  align-items: center;
  margin-bottom: 30px;
  
  .ai-avatar {
    width: 180px;
    height: 180px;
    border-radius: 20px;
    display: flex;
    align-items: center;
    justify-content: center;
    margin-bottom: 16px;
    animation: avatar-float 3s ease-in-out infinite;
    
    .ai-avatar-img {
      width: 100%;
      height: 100%;
      object-fit: contain;
      filter: drop-shadow(0 4px 16px rgba(91, 141, 239, 0.2));
    }
  }
  
  @keyframes avatar-float {
    0%, 100% {
      transform: translateY(0);
    }
    50% {
      transform: translateY(-8px);
    }
  }
  
  .ai-greeting {
    text-align: center;
    
    h3 {
      font-size: 18px;
      font-weight: 600;
      color: #1f2937;
      margin: 0 0 8px 0;
    }
    
    p {
      font-size: 14px;
      color: #6b7280;
      margin: 0;
    }
  }
}

/* 功能卡片 */
.function-cards {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 12px;
  margin-top: 30px;
  
  .function-card {
    background: white;
    padding: 16px;
    border-radius: 12px;
    border: 1px solid #E5E8EB;
    cursor: pointer;
    transition: all 0.3s ease;
    display: flex;
    flex-direction: column;
    justify-content: center;
    text-align: left;
    
    &:hover {
      box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
      transform: translateY(-2px);
    }
    
    .card-header {
      display: flex;
      align-items: center;
      margin-bottom: 8px;
      
      .card-icon {
        width: 32px;
        height: 32px;
        margin-right: 12px;
        
        .card-icon-img {
          width: 100%;
          height: 100%;
          object-fit: cover;
        }
      }
      
      h4 {
        font-size: 16px;
        font-weight: 600;
        color: #1f2937;
        margin: 0;
        flex: 1;
      }
    }
    
    .card-description {
      p {
        font-size: 14px;
        color: #6b7280;
        margin: 0;
        line-height: 1.4;
        // 超出两行显示省略号
        display: -webkit-box;
        -webkit-line-clamp: 1;
        line-clamp: 1;
        -webkit-box-orient: vertical;
        overflow: hidden;
      }
    }
  }
}

/* 聊天消息区域 */
.chat-messages {
  max-height: calc(100vh - 180px); // 减少底部空间
  overflow-y: auto;
  padding: 20px 0;
  margin-bottom: 10px; // 减少底部边距
  
  .message-item {
    margin-bottom: 20px;
    
    &.user {
      display: flex;
      justify-content: flex-end;
      
      .user-message {
        background: #3e7bfa;
        color: white;
        padding: 12px 16px;
        border-radius: 18px 18px 4px 18px;
        max-width: 80%;
        text-align: left;
        
        .message-content {
          .message-text {
            font-size: 20px;
            line-height: 1.4;
            word-wrap: break-word;
          }
        }
        
        .message-actions {
          margin-top: 8px;
          display: flex;
          gap: 8px;
          justify-content: flex-end;
          
          .action-btn {
            background: transparent;
            border: none;
            color: white;
            padding: 4px;
            border-radius: 4px;
            cursor: pointer;
            display: flex;
            align-items: center;
            justify-content: center;
            transition: all 0.2s ease;
            
            .action-icon {
              width: 20px;
              height: 20px;
              object-fit: contain;
              filter: brightness(0) invert(1); // 将图标变为白色
            }
          }
        }
      }
    }
    
  }
}

/* AI消息样式 */
.ai-message {
  display: flex;
  flex-direction: column;
  gap: 0;
  overflow-x: hidden;
  max-width: 100%;

  .ai-message-main {
    display: flex;
    gap: 12px;
    overflow-x: hidden;
    max-width: 100%;
  }

  .ai-avatar-small {
    width: 52px;
    height: 52px;
    flex-shrink: 0;

    .ai-icon {
      width: 100%;
      height: 100%;
      object-fit: contain;
    }
  }

  // 网络搜索胶囊外层容器
  .web-search-capsule-outer {
    margin-bottom: 8px;
    display: flex;
    justify-content: flex-start;
    padding-left: 62px; // 为头像留空间
    overflow-x: hidden;
    max-width: 100%;
  }

  // AI消息主体容器
  .ai-message-main {
    display: flex;
    gap: 10px;
    align-items: flex-start;
    margin-bottom: 16px;
    overflow-x: hidden;
    max-width: 100%;
  }

  // AI头像样式 - 移动端（用户已设置为52px）
  .ai-avatar-small {
    width: 52px;
    height: 52px;
    flex-shrink: 0;
    animation: avatar-pulse 2s ease-in-out infinite;

    .ai-icon {
      width: 100%;
      height: 100%;
      object-fit: contain;
      filter: drop-shadow(0 2px 8px rgba(91, 141, 239, 0.15));
    }
  }

  @keyframes avatar-pulse {
    0%, 100% {
      transform: scale(1);
    }
    50% {
      transform: scale(1.05);
    }
  }

  // 白色气泡容器 - 包裹所有AI内容
  .message-content {
    flex: 1;
    min-width: 0;
    max-width: 100%;
    background: white;
    border-radius: 12px;
    padding: 16px;
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
    text-align: left;
    overflow-x: hidden;
    word-wrap: break-word;
    overflow-wrap: break-word;
  }
  
  // AI响应内容容器
  .ai-response-content {
    text-align: left;
  }
  
  // 进度统计卡片（白色背景）
  .stats-card {
    display: flex;
    flex-direction: column;
    gap: 12px;
    padding: 12px 16px;
    background: #f8f9fa;
    border-radius: 8px;
    margin-bottom: 12px;
    border: 1px solid #e8ecf1;
    transition: box-shadow 0.3s ease;
  }
  
  .stats-left {
    display: flex;
    align-items: center;
    gap: 10px;
    width: 100%;
    
    span {
      font-size: 13px;
      font-weight: 500;
      color: #2d3748;
      flex: 1;
      line-height: 1.4;
    }
  }
  
  .stats-avatar {
    flex-shrink: 0;
  }
  
  .status-text {
    font-size: 13px;
    font-weight: 500;
    color: #2d3748;
  }
  
  .stats-left :deep(.ai-name) {
    color: #5b8def !important;
    font-weight: 600 !important;
  }
  
  .stats-left :deep(.file-count) {
    color: #5b8def !important;
    font-weight: 600 !important;
  }
  
  .stats-right {
    flex-shrink: 0;
  }
  
  // 问题总结卡片
  .question-summary {
    margin-bottom: 12px;
    padding: 12px 16px;
    background: white;
    border-radius: 12px;
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
    font-size: 14px;
    line-height: 1.8;
    color: #606266;
  }
  
  .reports-list {
    margin-top: 12px;
  }
  
  // AI文本内容
  .ai-text {
    text-align: left;
    max-width: 100%;
    overflow-x: hidden;
    word-wrap: break-word;

    .ai-markdown-content {
      text-align: left;
      max-width: 100%;
      overflow-x: hidden;
      word-wrap: break-word;

      * {
        text-align: left !important;
        max-width: 100%;
        word-wrap: break-word;
      }
    }
  }

  .question-summary {
    text-align: left;
    margin-bottom: 12px;
    max-width: 100%;
    overflow-x: hidden;
    word-wrap: break-word;

    * {
      text-align: left !important;
      max-width: 100%;
      word-wrap: break-word;
    }
  }
  
  // 分隔线
  .divider {
    height: 1px;
    background: #e5e7eb;
    margin: 12px 0;
  }
  
  // 进度条内联样式（在白色卡片内）
  .progress-capsule-inline {
    display: flex;
    align-items: center;
    gap: 10px;
    background: rgba(91, 141, 239, 0.08);
    border: 1px solid rgba(91, 141, 239, 0.2);
    padding: 6px 12px;
    border-radius: 20px;
    width: 100%;
    max-width: 100%;
  }
  
  .progress-bar-mini {
    position: relative;
    flex: 1;
    min-width: 60px;
    height: 6px;
    background: rgba(91, 141, 239, 0.15);
    border-radius: 3px;
    overflow: visible;
  }
  
  .progress-fill {
    height: 100%;
    background: linear-gradient(90deg, #5b8def 0%, #4a7ad8 100%);
    border-radius: 3px;
    transition: width 0.6s ease;
    box-shadow: 0 0 8px rgba(91, 141, 239, 0.3);
  }
  
  .progress-dot {
    position: absolute;
    top: 50%;
    transform: translate(-50%, -50%);
    width: 12px;
    height: 12px;
    background: white;
    border: 2px solid #5b8def;
    border-radius: 50%;
    box-shadow: 0 2px 6px rgba(91, 141, 239, 0.4);
    transition: left 0.6s ease;
    z-index: 1;
  }
  
  .progress-percentage {
    font-weight: 600;
    min-width: 35px;
    text-align: right;
    color: #5b8def;
    font-size: 12px;
  }
  
  .report-loading {
    display: flex;
    flex-direction: row;
    align-items: center;
    justify-content: flex-start;
    gap: 12px;
    padding: 12px 0;
    margin-top: 12px;
    margin-bottom: 8px;
    
    .loading-text {
      color: #6B7280;
      font-size: 14px;
      font-weight: 500;
    }
    
    .thinking-animation {
      display: flex;
      gap: 4px;
      
      .dot {
        width: 6px;
        height: 6px;
        background: #9CA3AF;
        border-radius: 50%;
        animation: thinking 1.4s infinite ease-in-out;
        
        &:nth-child(1) { animation-delay: -0.32s; }
        &:nth-child(2) { animation-delay: -0.16s; }
        &:nth-child(3) { animation-delay: 0s; }
      }
    }
  }
  
  .message-content {
    background: white;
    color: #374151;
    padding: 12px 6px;
    border-radius: 0px 18px 18px 18px;
    max-width: calc(100vw - 120px);
    width: fit-content;
    min-width: 120px;
    word-wrap: break-word;
    line-height: 1.5;
    font-size: 14px;
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
    white-space: normal;
    overflow-wrap: break-word;
    word-break: break-word;
    text-align: left;
    
    .ai-text {
      min-height: 20px;
      line-height: 1.5;
      
      .typing-indicator {
        color: #9CA3AF;
        font-style: italic;
        font-size: 14px;
        display: flex;
        align-items: center;
        gap: 8px;
        white-space: normal;
        word-wrap: break-word;
        overflow-wrap: break-word;
        
        .thinking-animation {
          .dot {
            display: inline-block;
            width: 4px;
            height: 4px;
            border-radius: 50%;
            background: #9CA3AF;
            margin: 0 1px;
            animation: thinking 1.4s infinite ease-in-out;
            
            &:nth-child(1) { animation-delay: -0.32s; }
            &:nth-child(2) { animation-delay: -0.16s; }
          }
        }
      }
    }
    
    .divider {
      width: 100%;
      height: 1px;
      background: #E5E7EB;
      margin: 8px 0;
    }
    
    .message-actions {
      display: flex;
      justify-content: space-between;
      align-items: center;
      margin-top: 8px;
      
      .left-actions {
        display: flex;
        gap: 8px;
        flex-wrap: wrap;
      }
      
      .right-actions {
        display: flex;
        gap: 4px;
      }
      
      .action-btn {
        background: transparent;
        border: none;
        color: #6B7280;
        padding: 6px;
        border-radius: 4px;
        cursor: pointer;
        display: flex;
        align-items: center;
        justify-content: center;
        transition: all 0.3s ease;
        
        
        &:disabled {
          opacity: 0.5;
          cursor: not-allowed;
        }
        
        .action-icon {
          width: 20px;
          height: 20px;
          object-fit: contain;
        }
        
        &.thumbs-up-btn {
          transition: all 0.3s cubic-bezier(0.68, -0.55, 0.265, 1.55);
          
          &.active {
            .action-icon {
              filter: brightness(0) saturate(100%) invert(27%) sepia(51%) saturate(2878%) hue-rotate(142deg) brightness(104%) contrast(97%);
            }
          }
        }
        
        &.thumbs-down-btn {
          transition: all 0.3s cubic-bezier(0.68, -0.55, 0.265, 1.55);
          
          &.active {
            .action-icon {
              filter: brightness(0) saturate(100%) invert(27%) sepia(51%) saturate(2878%) hue-rotate(0deg) brightness(104%) contrast(97%);
            }
          }
        }
      }
    }
  }
}

/* 底部输入区域 */
.chat-input-section {
  position: fixed;
  bottom: 20px; // 减少底部距离
  left: 0;
  right: 0;
  background: #EBF3FF;
  padding: 8px 20px; // 减少内边距
  z-index: 1;
  transform: translateZ(0); // 启用硬件加速
  -webkit-transform: translateZ(0); // Safari兼容
  will-change: transform; // 提示浏览器优化
  
  .input-container {
    max-width: 100%;
    
  .input-box {
    display: flex;
    align-items: center;
    gap: 4px; // 减少间距
    background: white;
    border-radius: 16px;
    padding: 8px 12px; // 减少左右padding
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
    transition: box-shadow 0.3s ease;
    border: 1px solid #3E7BFA;
    height: 57px; // 恢复原来的高度
    transform: translateZ(0); // 启用硬件加速
    -webkit-transform: translateZ(0); // Safari兼容
    will-change: transform; // 提示浏览器优化
      
      &:focus-within {
        box-shadow: 0 2px 12px rgba(62, 123, 250, 0.2);
      }
      
      .message-input {
        flex: 1;
        border: none;
        background: transparent;
        font-size: 16px !important; // 强制字体大小
        color: #2C3E50;
        outline: none;
        transition: opacity 0.3s ease;
        line-height: 1.4 !important; // 强制行高
        height: auto !important; // 自动高度
        
        &::placeholder {
          color: #A0A6B8;
          font-size: 16px !important;
        }
        
        &:disabled {
          cursor: not-allowed;
        }
        
      }
      
      .divider {
        width: 1px;
        height: 31px;
        background-color: #D6D5DE;
        margin: 0 2px; // 减少margin
      }
      
      .voice-btn, .network-search-btn {
        background: none;
        border: none;
        cursor: pointer;
        padding: 6px; // 减少padding
        border-radius: 6px;
        transition: all 0.3s ease;
        display: flex;
        align-items: center;
        justify-content: center;
        position: relative;
        
        &:hover:not(:disabled) {
          background: rgba(102, 126, 234, 0.1);
        }
        
        &:disabled {
          cursor: not-allowed;
          opacity: 0.5;
        }
        
        .icon-container {
          width: 20px;
          height: 20px;
          display: flex;
          align-items: center;
          justify-content: center;
          position: relative;
          
          .action-icon {
            width: 20px;
            height: 20px;
            object-fit: contain;
          }
        }
      }
      
      .voice-btn {
        &.recording {
          background: rgba(239, 68, 68, 0.1);
          animation: pulse 1.5s ease-in-out infinite;
        }
        &.speaking {
          color: #dc2626;
          background: rgba(239, 68, 68, 0.08);
          
          .action-icon {
            filter: brightness(0) saturate(100%) invert(27%) sepia(51%) saturate(2878%) hue-rotate(0deg) brightness(104%) contrast(97%);
          }
        }
        
        .icon-container {
          .recording-indicator {
            position: absolute;
            top: -2px;
            right: -2px;
            width: 8px;
            height: 8px;
            background: #ef4444;
            border-radius: 50%;
            animation: blink 1s ease-in-out infinite;
          }
        }
      }
      
      .network-search-btn {
        &.active {
          background: rgba(62, 123, 250, 0.1);
          
          .action-icon {
            color: #3E7BFA;
          }
        }
      }
      
      .send-btn {
        background: none;
        border: none;
        cursor: pointer;
        border-radius: 6px;
        transition: background 0.3s ease;
        display: flex;
        align-items: center;
        justify-content: center;
        padding: 4px; // 添加少量padding
        
        &:hover:not(:disabled) {
          background: rgba(102, 126, 234, 0.1);
        }
        
        &:disabled {
          cursor: not-allowed;
        }
        
        .send-icon {
          width: 80px; // 减少宽度
          height: 36px; // 减少高度
          object-fit: contain;
        }
      }
    }
  }
}

/* 思考动画 */
@keyframes thinking {
  0%, 80%, 100% {
    transform: scale(0);
  }
  40% {
    transform: scale(1);
  }
}

/* 语音输入动画 */
@keyframes pulse {
  0% {
    transform: scale(1);
  }
  50% {
    transform: scale(1.05);
  }
  100% {
    transform: scale(1);
  }
}

@keyframes blink {
  0%, 50% {
    opacity: 1;
  }
  51%, 100% {
    opacity: 0.3;
  }
}

/* 联网搜索Loading样式 */
.online-search-loading {
  display: flex;
  align-items: center;
  justify-content: flex-start; // 左对齐
  padding: 12px;
  margin-left: 44px; /* 与AI头像对齐 */
  width: fit-content;
}

.online-search-loading .thinking-animation {
  display: flex;
  gap: 4px;
}

.online-search-loading .thinking-animation .dot {
  width: 6px;
  height: 6px;
  background: #9CA3AF;
  border-radius: 50%;
  animation: thinking 1.4s infinite ease-in-out;
}

.online-search-loading .thinking-animation .dot:nth-child(1) { 
  animation-delay: -0.32s; 
}

.online-search-loading .thinking-animation .dot:nth-child(2) { 
  animation-delay: -0.16s; 
}

.online-search-loading .thinking-animation .dot:nth-child(3) { 
  animation-delay: 0s; 
}

/* 联网搜索结果样式 */
.online-search-results {
  margin: 8px 0 12px 0;
  margin-top: 10px;
  background: white;
  border-radius: 12px;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
  margin-left: 44px; /* 与AI头像对齐 */
  overflow-x: auto; /* 支持横向滑动 */
  overflow-y: hidden; /* 禁用纵向滚动 */
  width: fit-content;
  max-width: calc(100vw - 120px);
  max-height: 200px; /* 限制最大高度 */
  
  .online-search-header {
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 12px;
    cursor: pointer;
    transition: background-color 0.2s ease;
    font-size: 14px;
    color: #37415180;
    
    &:hover {
      background-color: #f9fafb;
    }
    
    .expand-icon {
      margin-left: 8px;
      transition: transform 0.2s ease;
      color: #6b7280;
      
      &.expanded {
        transform: rotate(180deg);
      }
    }
  }
  
  .online-search-content {
    border-top: 1px solid #e5e7eb;
    max-height: 150px; /* 限制内容区域高度 */
    overflow-y: auto; /* 内容过多时纵向滚动 */
    
    .search-results-section {
      padding: 12px;
      display: flex;
      flex-direction: column;
      gap: 8px;
      
      .section-title {
        font-size: 14px;
        font-weight: 600;
        color: #374151;
        margin-bottom: 8px;
        display: block;
      }
      
      .search-results-container {
        display: flex;
        gap: 8px;
        overflow-x: auto;
        overflow-y: hidden;
        padding-bottom: 4px;
        
        /* 自定义滚动条样式 */
        &::-webkit-scrollbar {
          height: 4px;
        }
        
        &::-webkit-scrollbar-track {
          background: #f1f1f1;
          border-radius: 2px;
        }
        
        &::-webkit-scrollbar-thumb {
          background: #c1c1c1;
          border-radius: 2px;
        }
        
        &::-webkit-scrollbar-thumb:hover {
          background: #a8a8a8;
        }
      }
      
      .search-result-card {
        background: #f9fafb;
        border-radius: 8px;
        padding: 12px;
        cursor: pointer;
        transition: all 0.2s ease;
        border: 1px solid #e5e7eb;
        width: 200px; /* 设置最小宽度 */
        flex-shrink: 0; /* 防止卡片收缩 */
        text-align: left;

        
        &:hover {
          background: #f3f4f6;
          border-color: #d1d5db;
        }
        
        &:last-child {
          margin-bottom: 0;
        }
        
        .result-title {
          font-size: 14px;
          font-weight: 600;
          color: #1f2937;
          margin-bottom: 6px;
          line-height: 1.4;
          display: -webkit-box;
          -webkit-line-clamp: 2;
          line-clamp: 2;
          -webkit-box-orient: vertical;
          overflow: hidden;
        }
        
        .result-snippet {
          font-size: 13px;
          color: #6b7280;
          line-height: 1.4;
          // margin-bottom: 6px;
          display: -webkit-box;
          -webkit-line-clamp: 2;
          line-clamp: 3;
          -webkit-box-orient: vertical;
          overflow: hidden;
        }
        
        .result-url {
          font-size: 12px;
          color: #9ca3af;
          line-height: 1.3;
          word-break: break-all;
        }
      }
    }
  }
}

/* 推荐问题Loading样式 */
.related-questions-loading {
  display: flex;
  align-items: center;
  justify-content: flex-start; // 左对齐
  padding: 12px;
  margin-left: 62px; /* 与AI消息白色气泡对齐 (52px头像 + 10px gap) */
  width: fit-content;
}

.related-questions-loading .thinking-animation {
  display: flex;
  gap: 4px;
}

.related-questions-loading .thinking-animation .dot {
  width: 6px;
  height: 6px;
  background: #9CA3AF;
  border-radius: 50%;
  animation: thinking 1.4s infinite ease-in-out;
}

.related-questions-loading .thinking-animation .dot:nth-child(1) { 
  animation-delay: -0.32s; 
}

.related-questions-loading .thinking-animation .dot:nth-child(2) { 
  animation-delay: -0.16s; 
}

.related-questions-loading .thinking-animation .dot:nth-child(3) { 
  animation-delay: 0s; 
}

/* 推荐问题样式 */
.related-questions {
  margin: 0px 0 12px 0;
  // margin-top: 0px;
  margin-left: 62px; /* 与AI消息白色气泡对齐 (52px头像 + 10px gap) */
  width: fit-content;
  max-width: calc(100vw - 120px);
  
  .related-question-item {
    background: rgba(0,0,0,.05); /* 与PC端一致的背景色 */
    border-radius: 12px;
    padding: 12px 16px;
    margin-bottom: 8px;
    cursor: pointer;
    transition: all 0.2s ease;
    border: 1px solid #e9ecef; /* 与PC端一致的边框色 */
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
    display: flex;
    align-items: center;
    justify-content: space-between;
    
    &:hover {
      background: #e9ecef; /* 与PC端一致的悬停背景色 */
      border-color: #dee2e6; /* 与PC端一致的悬停边框色 */
      transform: translateY(-1px);
      box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15);
    }
    
    &:last-child {
      margin-bottom: 0;
    }
    
    span {
      font-size: 14px;
      color: #374151;
      line-height: 1.4;
      flex: 1;
      margin-right: 8px;
      text-align: left; /* 确保文字居左对齐 */
    }
    
    .arrow-icon {
      width: 16px;
      height: 16px;
      color: #9ca3af;
      flex-shrink: 0;
      transition: transform 0.2s ease;
    }
    
    &:hover .arrow-icon {
      transform: translateX(2px);
      color: #6b7280;
    }
  }
}

/* 规范引用样式 */
:deep(.standard-reference) {
  background-color: #EAEAEE !important;
  color: #616161 !important;
  font-size: 0.75rem !important;
  padding: 3px 8px !important;
  border-radius: 6px !important;
  cursor: pointer !important;
  display: inline-block !important;
  margin: 4px 2px !important;
  border: 1px solid #EAEAEE !important;
  font-weight: 500 !important;
  transition: all 0.2s ease !important;
  line-height: 1.4 !important;
}

:deep(.standard-reference:hover) {
  background-color: #d1d5db !important;
  border-color: #d1d5db !important;
}

/* 进度统计卡片样式 - 移动端适配 */
.stats-card {
  background: white;
  border-radius: 8px;
  padding: 12px 16px;
  margin-bottom: 16px;
  display: flex;
  flex-direction: column;
  gap: 8px;
  border: 1px solid #e8ecf1;
  transition: all 0.3s ease, box-shadow 0.3s ease;
  
  &.is-sticky {
    /* position, top, left, width, zIndex 由 inline style 动态设置 */
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
    border-bottom: 2px solid #5b8def;
    animation: slideDown 0.3s ease;
  }
  
  @keyframes slideDown {
    from {
      transform: translateY(-10px);
      opacity: 0;
    }
    to {
      transform: translateY(0);
      opacity: 1;
    }
  }
  
  .stats-left {
    display: flex;
    align-items: center;
    gap: 8px;
    flex: 1;
    min-width: 0;
    
    .stats-avatar {
      flex-shrink: 0;
    }
    
    .status-text {
      font-size: 13px;
      line-height: 1.4;
      color: #374151;
      flex: 1;
      min-width: 0;
      
      :deep(.ai-name) {
        font-weight: 600;
        color: #1f2937;
      }
      
      :deep(.file-count) {
        font-weight: 600;
        color: #3e7bfa;
      }
    }
  }
  
  .progress-capsule-inline {
    display: flex;
    align-items: center;
    gap: 8px;
    
    .progress-bar-mini {
      flex: 1;
      height: 4px;
      background: #e5e7eb;
      border-radius: 2px;
      position: relative;
      overflow: hidden;
      
      .progress-fill {
        height: 100%;
        background: linear-gradient(90deg, #3e7bfa 0%, #5b8eff 100%);
        border-radius: 2px;
        transition: width 0.3s ease;
      }
      
      .progress-dot {
        position: absolute;
        top: 50%;
        transform: translate(-50%, -50%);
        width: 8px;
        height: 8px;
        background: #3e7bfa;
        border-radius: 50%;
        box-shadow: 0 0 0 2px rgba(62, 123, 250, 0.2);
        transition: left 0.3s ease;
      }
    }
    
    .progress-percentage {
      font-size: 12px;
      font-weight: 600;
      color: #3e7bfa;
      min-width: 40px;
      text-align: right;
    }
  }
  
  .stats-right {
    display: flex;
    justify-content: flex-end;
  }
}

/* 问题总结样式 - 移动端适配 */
.question-summary {
  background: #f9fafb;
  // border-left: 3px solid #3e7bfa;
  padding: 12px;
  margin-bottom: 12px;
  border-radius: 8px;
  font-size: 14px;
  line-height: 1.6;
  color: #374151;
}

/* 报告Loading动画 - 移动端适配 */
.report-loading {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 20px;
  gap: 12px;
  
  .loading-text {
    font-size: 14px;
    color: #6b7280;
  }
  
  .thinking-animation {
    display: flex;
    gap: 6px;
    
    .dot {
      width: 8px;
      height: 8px;
      border-radius: 50%;
      background: #3e7bfa;
      animation: thinking 1.4s infinite ease-in-out;
      
      &:nth-child(1) { animation-delay: -0.32s; }
      &:nth-child(2) { animation-delay: -0.16s; }
      &:nth-child(3) { animation-delay: 0s; }
    }
  }
}

/* 报告列表样式 - 移动端适配 */
.reports-list {
  margin-bottom: 12px;
}

/* AI markdown内容样式 */
.ai-markdown-content {
  font-size: 14px;
  line-height: 1.6;
  color: #374151;
}

/* 网络搜索弹窗样式 */
.web-search-modal-overlay {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background: rgba(0, 0, 0, 0.5);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 9999;
  padding: 20px;
}

.web-search-modal {
  background: white;
  border-radius: 12px;
  width: 100%;
  max-width: 500px;
  max-height: 80vh;
  display: flex;
  flex-direction: column;
  box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
}

.web-search-modal-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 16px 20px;
  border-bottom: 1px solid #e5e7eb;
  
  h3 {
    margin: 0;
    font-size: 18px;
    font-weight: 600;
    color: #1f2937;
  }
  
  .close-btn {
    background: none;
    border: none;
    font-size: 24px;
    color: #6b7280;
    cursor: pointer;
    padding: 0;
    width: 32px;
    height: 32px;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 50%;
    transition: all 0.2s;
    
    &:active {
      background: #f3f4f6;
      color: #1f2937;
    }
  }
}

.web-search-modal-content {
  flex: 1;
  overflow-y: auto;
  padding: 20px;
}

.search-results {
  .search-count {
    font-size: 14px;
    color: #6b7280;
    margin-bottom: 16px;
    padding-bottom: 12px;
    border-bottom: 1px solid #e5e7eb;
  }
  
  .search-result-item {
    padding: 16px;
    margin-bottom: 12px;
    background: #f8f9fa;
    border-radius: 8px;
    border: 1px solid #e4e7ed;
    cursor: pointer;
    transition: all 0.2s;
    
    &:active {
      background: white;
      border-color: #5b8def;
      transform: scale(0.98);
    }
    
    .result-header {
      display: flex;
      align-items: flex-start;
      gap: 12px;
      margin-bottom: 8px;
    }
    
    .result-index {
      flex-shrink: 0;
      width: 24px;
      height: 24px;
      border-radius: 50%;
      background: linear-gradient(135deg, #5b8def 0%, #0063f7 100%);
      color: white;
      display: flex;
      align-items: center;
      justify-content: center;
      font-size: 12px;
      font-weight: 600;
    }
    
    .result-title {
      flex: 1;
      font-size: 15px;
      font-weight: 600;
      color: #303133;
      line-height: 1.5;
      overflow: hidden;
      text-overflow: ellipsis;
      display: -webkit-box;
      -webkit-line-clamp: 2;
      line-clamp: 2;
      -webkit-box-orient: vertical;
    }
    
    .result-content {
      font-size: 13px;
      color: #606266;
      line-height: 1.6;
      margin-bottom: 12px;
      overflow: hidden;
      text-overflow: ellipsis;
      display: -webkit-box;
      -webkit-line-clamp: 3;
      line-clamp: 3;
      -webkit-box-orient: vertical;
    }
    
    .result-footer {
      display: flex;
      align-items: center;
      gap: 8px;
      font-size: 12px;
      color: #909399;
    }
    
    .result-url {
      flex: 1;
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
    }
    
    .result-score {
      flex-shrink: 0;
      padding: 2px 8px;
      background: #f0f2f5;
      border-radius: 4px;
      font-weight: 500;
      color: #606266;
    }
  }
}

/* 网页预览弹窗样式 */
.web-preview-overlay {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background: rgba(0, 0, 0, 0.7);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 10000;
  padding: 10px;
}

.web-preview-modal {
  background: white;
  border-radius: 12px;
  width: 100%;
  height: 90vh;
  display: flex;
  flex-direction: column;
  box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
}

.web-preview-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 16px 20px;
  border-bottom: 1px solid #e5e7eb;
  
  h3 {
    margin: 0;
    font-size: 16px;
    font-weight: 600;
    color: #1f2937;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    flex: 1;
    margin-right: 12px;
  }
  
  .close-btn {
    background: none;
    border: none;
    font-size: 24px;
    color: #6b7280;
    cursor: pointer;
    padding: 0;
    width: 32px;
    height: 32px;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 50%;
    transition: all 0.2s;
    flex-shrink: 0;
    
    &:active {
      background: #f3f4f6;
      color: #1f2937;
    }
  }
}

.web-preview-content {
  flex: 1;
  overflow: hidden;
  
  .preview-iframe {
    width: 100%;
    height: 100%;
    border: none;
  }
  
  .iframe-error {
    width: 100%;
    height: 100%;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    background: #f5f7fa;
    
    p {
      font-size: 16px;
      color: #606266;
      margin-bottom: 20px;
    }
    
    .open-link-btn {
      padding: 10px 24px;
      background: #3e7bfa;
      color: white;
      border: none;
      border-radius: 6px;
      font-size: 14px;
      cursor: pointer;
      
      &:active {
        background: #2563eb;
      }
    }
  }
}

/* 文件预览弹窗样式 */
.file-preview-overlay {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background: rgba(0, 0, 0, 0.7);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 10000;
  padding: 10px;
}

.file-preview-modal {
  background: white;
  border-radius: 12px;
  width: 100%;
  height: 90vh;
  display: flex;
  flex-direction: column;
  box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
  animation: slideUp 0.3s ease;
}

.file-preview-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 16px 20px;
  border-bottom: 1px solid #e5e7eb;
  
  .header-left {
    display: flex;
    align-items: center;
    gap: 12px;
    flex: 1;
    min-width: 0;
  }
  
  .file-icon {
    width: 24px;
    height: 24px;
    color: #5b8def;
    flex-shrink: 0;
  }
  
  .header-text {
    display: flex;
    flex-direction: column;
    gap: 4px;
    min-width: 0;
    flex: 1;
    
    h3 {
      margin: 0;
      font-size: 16px;
      font-weight: 600;
      color: #1f2937;
    }
    
    .file-name {
      font-size: 12px;
      color: #6b7280;
      background: #f3f4f6;
      padding: 4px 10px;
      border-radius: 10px;
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
      max-width: 250px;
    }
  }
  
  .close-btn {
    background: none;
    border: none;
    font-size: 24px;
    color: #6b7280;
    cursor: pointer;
    padding: 0;
    width: 32px;
    height: 32px;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 50%;
    transition: all 0.2s;
    flex-shrink: 0;
    
    &:active {
      background: #f3f4f6;
      color: #1f2937;
    }
  }
}

.file-preview-content {
  flex: 1;
  overflow: hidden;
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  
  .file-iframe {
    width: 100%;
    height: 100%;
    border: none;
  }
  
  .file-loading {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 16px;
    
    .loading-spinner {
      width: 40px;
      height: 40px;
      border: 3px solid #f3f3f3;
      border-top: 3px solid #5b8def;
      border-radius: 50%;
      animation: spin 1s linear infinite;
    }
    
    p {
      color: #909399;
      font-size: 14px;
      margin: 0;
    }
  }
  
  .file-error {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 16px;
    
    .error-icon {
      width: 48px;
      height: 48px;
      color: #f56c6c;
    }
    
    p {
      color: #f56c6c;
      font-size: 14px;
      margin: 0;
    }
  }
  
  .file-empty {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 16px;
    
    .empty-icon {
      width: 48px;
      height: 48px;
      color: #c0c4cc;
    }
    
    p {
      color: #909399;
      font-size: 14px;
      margin: 0;
    }
  }
}

@keyframes spin {
  0% { transform: rotate(0deg); }
  100% { transform: rotate(360deg); }
}

</style>
