<template>
  <div class="chat-container">
    <!-- 会话记录侧边栏 -->
    <div class="chat-sidebar" :class="{ 'sidebar-collapsed': isSidebarCollapsed }">
      <div class="sidebar-header">
        <h3 v-show="!isSidebarCollapsed">{{ t('vabI18n.chat.history') }}</h3>
        <el-button 
          type="primary" 
          class="new-chat-btn"
          @click="() => createNewChat()"
          v-show="!isSidebarCollapsed"
        >
          <svg viewBox="0 0 24 24" class="new-chat-icon">
            <path fill="currentColor" d="M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/>
          </svg>
          {{ t('vabI18n.chat.newChat') }}
        </el-button>
      </div>
      <div class="chat-history" v-show="!isSidebarCollapsed">
        <div 
          v-for="(chat, index) in chatHistory" 
          :key="index"
          class="chat-history-item"
          :class="{ 'active': currentChatIndex === index }"
          @click="switchChat(index)"
        >
          <span class="chat-title">{{ chat.title || t('vabI18n.chat.chat') + ` ${index + 1}` }}</span>
          <span class="chat-time">{{ formatTime(chat.timestamp) }}</span>
        </div>
      </div>

      <div class="sidebar-toggle" @click="toggleSidebar">
        <svg viewBox="0 0 24 24" class="toggle-icon" :class="{ 'collapsed': isSidebarCollapsed }">
          <path fill="currentColor" d="M15.41 7.41L14 6l-6 6 6 6 1.41-1.41L10.83 12z"/>
        </svg>
      </div>
    </div>

    <!-- 主聊天区域 -->
    <div class="chat-main" :class="{ 'main-expanded': isSidebarCollapsed }">
      <!-- 聊天消息区域 -->
      <div class="chat-messages" ref="messagesContainer">
        <div v-for="(msg, index) in messages" 
             :key="index" 
             class="message-item"
             :class="{ 'user-message': msg.isUser, 'assistant-message': !msg.isUser }"
             @mouseenter="handleMessageHover(index, true)"
             @mouseleave="handleMessageHover(index, false)">
          <!-- 头像 -->
          <div class="avatar" :class="{ 'user-avatar': msg.isUser, 'assistant-avatar': !msg.isUser }">
            <svg v-if="msg.isUser" viewBox="0 0 24 24" class="avatar-icon">
              <path fill="currentColor" d="M12 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm0 2c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z"/>
            </svg>
            <img v-else :src="getAssistantAvatar()" :alt="$t('vabI18n.chat.assistant')" class="avatar-icon" />
          </div>
          
          <!-- 消息内容 -->
          <div class="message-content-wrapper" :class="{ 'has-charts': !msg.isUser && msg.echarts && msg.echarts.length > 0 }">
            <div class="message-content">
            <div class="message-header">
              <span class="message-sender">{{ msg.isUser ? t('vabI18n.chat.you') : t('vabI18n.chat.assistant') }}</span>
            </div>
            <div class="message-body">
              <!-- 编辑状态 -->
              <template v-if="editingMessageIndex === index">
                <div class="edit-container">
                  <div class="edit-input-wrapper">
                    <el-input
                      v-model="editingMessageText"
                      type="textarea"
                      :autosize="{ minRows: 3, maxRows: 8 }"
                      class="edit-textarea"
                      :placeholder="$t('vabI18n.chat.placeholder')"
                      @keydown.enter.ctrl="handleSaveEdit"
                      @keydown.escape="handleCancelEdit"
                    />
                    <div class="edit-actions">
                      <button 
                        @click="handleCancelEdit"
                        class="edit-btn cancel-btn"
                      >
                        {{ t('vabI18n.chat.cancel') }}
                      </button>
                      <button 
                        @click="handleSaveEdit"
                        :disabled="!editingMessageText.trim()"
                        class="edit-btn save-btn"
                      >
                        {{ t('vabI18n.chat.send') }}
                      </button>
                    </div>
                  </div>
                </div>
              </template>

              <!-- 正常显示状态 -->
              <template v-else>
                <template v-if="!msg.isUser">
                  <div v-if="parseThink(msg.text).think" class="think-section">
                    <div class="think-header">
                      <svg viewBox="0 0 24 24" class="think-icon">
                        <path fill="currentColor" d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-2h2v2zm0-4h-2V7h2v6z"/>
                      </svg>
                      {{ t('vabI18n.chat.think') }}
                    </div>
                    {{ parseThink(msg.text).think }}
                  </div>
                  <div v-if="msg.formattedText" class="answer-content">
                    <div v-html="msg.formattedText"></div>
                    <!-- 渲染 ECharts 图表 -->
                    <div v-if="msg.echarts && msg.echarts.length > 0" class="echarts-container">
                      <div 
                        v-for="chart in msg.echarts" 
                        :key="chart.id"
                        class="chart-wrapper"
                      >
                        <VabChart
                          :option="chart.option"
                          :init-options="{ renderer: 'svg' }"
                          theme="vab-echarts-theme"
                          class="message-chart"
                        />
                      </div>
                    </div>
                  </div>
                  <div v-else class="loading-content">
                    <span class="loading-dots">
                      <span class="dot"></span>
                      <span class="dot"></span>
                      <span class="dot"></span>
                    </span>
                  </div>
                  
                  <!-- 文件来源信息 -->
                  <div v-if="msg.sources && ((msg.sources.tracePdf && msg.sources.tracePdf.length > 0) || (msg.sources.traceExcel && msg.sources.traceExcel.trim())) && props.chatType === '1'" class="sources-section">
                    <div class="sources-header">
                      <svg viewBox="0 0 24 24" class="sources-icon">
                        <path fill="currentColor" d="M14,2H6A2,2 0 0,0 4,4V20A2,2 0 0,0 6,22H18A2,2 0 0,0 20,20V8L14,2M18,20H6V4H13V9H18V20Z"/>
                      </svg>
                      {{ t('vabI18n.chat.sources') }}
                    </div>
                    <div class="sources-list">
                      <!-- PDF文件来源 -->
                      <div 
                        v-for="source in msg.sources.tracePdf" 
                        :key="source.fileName"
                        class="source-item"
                        @click="handleSourceClick(source, msg.conversationId, msg.messageId)"
                      >
                        <img 
                          :src="getFileTypeIcon(getFileExtension(source.fileName))" 
                          :alt="getFileExtension(source.fileName)"
                          class="source-icon"
                        />
                        <el-tooltip 
                          :content="source.fileName" 
                          placement="top" 
                          :disabled="source.fileName.length <= 25"
                        >
                          <span class="source-name">{{ source.fileName }}</span>
                        </el-tooltip>
                      </div>
                      <!-- Excel文件来源 -->
                      <div 
                        v-if="msg.sources.traceExcel && msg.sources.traceExcel.trim()"
                        class="source-item excel-item"
                        @click="handleExcelClick(msg.sources.traceExcel)"
                      >
                        <img 
                          :src="getFileTypeIcon('excel')" 
                          alt="excel"
                          class="source-icon"
                        />
                        <el-tooltip 
                          :content="generateExcelFileName()" 
                          placement="top"
                        >
                          <span class="source-name">{{ generateExcelFileName() }}</span>
                        </el-tooltip>
                      </div>
                    </div>
                  </div>
                  
                  <!-- 推荐问题按钮 -->
                  <div v-if="msg.isOpenRemark && props.recommendQuestions && props.recommendQuestions.length > 0" class="recommend-questions-section">
                    <div class="recommend-questions-header">
                      <!-- <svg viewBox="0 0 24 24" class="recommend-icon">
                        <path fill="currentColor" d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z"/>
                      </svg> -->
                      <!-- 推荐问题 -->
                    </div>
                    <div class="recommend-questions-list">
                      <el-tooltip 
                        v-for="(question, qIndex) in props.recommendQuestions" 
                        :key="qIndex"
                        :content="question" 
                        placement="top" 
                        :disabled="question.length <= 30"
                      >
                        <button 
                          class="recommend-question-btn"
                          @click="handleRecommendQuestionClick(question)"
                          :disabled="isLoading"
                        >
                          <svg viewBox="0 0 24 24" class="question-icon">
                            <path fill="currentColor" d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 17h-2v-2h2v2zm2.07-7.75l-.9.92C13.45 12.9 13 13.5 13 15h-2v-.5c0-1.1.45-2.1 1.17-2.83l1.24-1.26c.37-.36.59-.86.59-1.41 0-1.1-.9-2-2-2s-2 .9-2 2H8c0-2.21 1.79-4 4-4s4 1.79 4 4c0 .88-.36 1.68-.93 2.25z"/>
                          </svg>
                          <span class="question-text">{{ question }}</span>
                        </button>
                      </el-tooltip>
                    </div>
                  </div>
                </template>
                <template v-else>
                  {{ msg.text }}
                </template>
              </template>
            </div>
            <span v-if="msg.isLoading" class="typing-indicator">
              <span class="dot"></span>
              <span class="dot"></span>
              <span class="dot"></span>
            </span>

            <!-- 消息操作按钮 -->
            <div 
              v-show="hoveredMessageIndex === index && !msg.isLoading && editingMessageIndex !== index" 
              class="message-actions"
              :class="{ 'user-actions': msg.isUser, 'assistant-actions': !msg.isUser }"
            >
              <!-- 用户消息操作 -->
              <template v-if="msg.isUser">
                <el-tooltip :content="$t('vabI18n.chat.copy')" placement="top">
                  <button class="action-btn copy-btn" @click="handleCopyMessage(msg)">
                    <svg viewBox="0 0 24 24" class="action-icon">
                      <path fill="currentColor" d="M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12V1zm3 4H8c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h11c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm0 16H8V7h11v14z"/>
                    </svg>
                  </button>
                </el-tooltip>
                <el-tooltip :content="$t('vabI18n.chat.edit')" placement="top">
                  <button class="action-btn edit-btn" @click="handleEditMessage(msg, index)">
                    <svg viewBox="0 0 24 24" class="action-icon">
                      <path fill="currentColor" d="M3 17.25V21h3.75L17.81 9.94l-3.75-3.75L3 17.25zM20.71 7.04c.39-.39.39-1.02 0-1.41l-2.34-2.34c-.39-.39-1.02-.39-1.41 0l-1.83 1.83 3.75 3.75 1.83-1.83z"/>
                    </svg>
                  </button>
                </el-tooltip>
              </template>

              <!-- 助手消息操作 -->
              <template v-else>
                <el-tooltip :content="$t('vabI18n.chat.copy')" placement="top">
                  <button class="action-btn copy-btn" @click="handleCopyMessage(msg)">
                    <svg viewBox="0 0 24 24" class="action-icon">
                      <path fill="currentColor" d="M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12V1zm3 4H8c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h11c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm0 16H8V7h11v14z"/>
                    </svg>
                  </button>
                </el-tooltip>
                <el-tooltip :content="$t('vabI18n.chat.regenerate')" placement="top">
                  <button class="action-btn regenerate-btn" @click="handleRegenerateMessage(index)">
                    <svg viewBox="0 0 24 24" class="action-icon">
                      <path fill="currentColor" d="M17.65 6.35C16.2 4.9 14.21 4 12 4c-4.42 0-7.99 3.58-7.99 8s3.57 8 7.99 8c3.73 0 6.84-2.55 7.73-6h-2.08c-.82 2.33-3.04 4-5.65 4-3.31 0-6-2.69-6-6s2.69-6 6-6c1.66 0 3.14.69 4.22 1.78L13 11h7V4l-2.35 2.35z"/>
                    </svg>
                  </button>
                </el-tooltip>
                <el-tooltip :content="$t('vabI18n.chat.like')" placement="top">
                  <button class="action-btn like-btn" @click="handleLikeMessage(index)">
                    <svg viewBox="0 0 24 24" class="action-icon">
                      <path fill="currentColor" d="M1 21h4V9H1v12zm22-11c0-1.1-.9-2-2-2h-6.31l.95-4.57.03-.32c0-.41-.17-.79-.44-1.06L14.17 1 7.59 7.59C7.22 7.95 7 8.45 7 9v10c0 1.1.9 2 2 2h9c.83 0 1.54-.5 1.84-1.22l3.02-7.05c.09-.23.14-.47.14-.73v-2z"/>
                    </svg>
                  </button>
                </el-tooltip>
                <el-tooltip :content="$t('vabI18n.chat.dislike')" placement="top">
                  <button class="action-btn dislike-btn" @click="handleDislikeMessage(index)">
                    <svg viewBox="0 0 24 24" class="action-icon">
                      <path fill="currentColor" d="M15 3H6c-.83 0-1.54.5-1.84 1.22l-3.02 7.05c-.09.23-.14.47-.14.73v2c0 1.1.9 2 2 2h6.31l-.95 4.57-.03.32c0 .41.17.79.44 1.06L9.83 23l6.59-6.59c.36-.36.58-.86.58-1.41V5c0-1.1-.9-2-2-2zm4 0v12h4V3h-4z"/>
                    </svg>
                  </button>
                </el-tooltip>
              </template>
            </div>
          </div>
          </div>
        </div>
        <div ref="bottomAnchor"></div>
        
      </div>

      <!-- 输入区域 -->
      <div class="input-area">
        <!-- 中止按钮 -->
        <div v-if="showStopButton" class="stop-button-container">
          <el-tooltip :content="$t('vabI18n.chat.stopGenerating')" placement="top">
            <button 
              class="stop-button"
              @click="handleStopStream"
            >
              <svg viewBox="0 0 24 24" class="stop-icon">
                <path fill="currentColor" d="M6 6h12v12H6z"/>
              </svg>
            </button>
          </el-tooltip>
        </div>

        <div class="input-wrapper">
          <el-input
            v-model="inputMessage"
            :placeholder="props.placeholder"
            :class="{ 'is-loading': isLoading }"
            @keyup.enter="sendMessage"
            type="textarea"
            :autosize="{ minRows: 1, maxRows: 4 }"
          >
          </el-input>
          <el-button 
            type="primary" 
            class="send-button"
            @click="sendMessage" 
            :disabled="!inputMessage.trim() || isLoading"
          >
            <template v-if="isLoading">
              <span class="loading-icon"></span>
            </template>
            <svg v-else viewBox="0 0 24 24" class="send-icon" width="16" height="16">
              <path fill="currentColor" d="M3.4 20.4l17.45-7.48a1 1 0 000-1.84L3.4 3.6a.993.993 0 00-1.39.91L2 9.12c0 .5.37.93.87.99L17 12L2.87 13.88c-.5.07-.87.5-.87 1l.01 4.61c0 .71.73 1.2 1.39.91z"/>
            </svg>
          </el-button>
        </div>
        <div class="input-footer">
          <span class="footer-hint">{{ t('vabI18n.chat.sendHint') }}</span>
        </div>
      </div>
    </div>

    <!-- 文件预览模态框 -->
    <el-dialog
      v-model="filePreviewVisible"
      :title="t('vabI18n.chat.previewTitle')"
      width="80%"
      class="file-preview-dialog"
      :close-on-click-modal="false"
      center
    >
      <div v-loading="previewLoading" class="preview-content">
        <vue-office-pdf
          v-if="previewFileUrl"
          :src="previewFileUrl"
          @rendered="() => { previewLoading = false }"
          @error="() => { previewLoading = false; ElMessage.error(t('vabI18n.chat.previewTitleFail')) }"
          class="pdf-preview"
        />
      </div>
    </el-dialog>

    <!-- Excel表格预览模态框 -->
    <el-dialog
      v-model="excelPreviewVisible"
      :title="$t('vabI18n.chat.excelPreviewTitle')"
      width="90%"
      class="excel-preview-dialog"
      :close-on-click-modal="false"
      center
    >
      <div v-loading="excelPreviewLoading" class="excel-preview-content">
        <div 
          v-if="excelPreviewContent"
          v-html="excelPreviewContent"
          class="excel-table-content"
        />
      </div>
    </el-dialog>
  </div>
</template>

<script setup lang="ts">
// 组件注册
const components = {
  VueOfficePdf,
  VabChart
}

import { marked } from 'marked'
import DOMPurify from 'dompurify'
import { ref, reactive, nextTick, watch, onMounted } from 'vue'
import { ElMessage } from 'element-plus'
import VueOfficePdf from '@vue-office/pdf'
import VabChart from '@/plugins/VabChart/index.vue'
import { ChevronDownIcon, ChevronUpIcon } from '@heroicons/vue/24/solid'
import { sendChatMessage, type ChatMessageResponse, type ChatMessageSendRequest, stopMessagesStream, getFilePathList, type TraceFile, type TraceData } from '@/api/chat' 
import { useI18n } from 'vue-i18n'
import { throttle } from 'lodash'

const expanded = ref(false)
const { t, locale } = useI18n()
// Props 定义
interface Props {
  chatType: string // 聊天类型，必填
  userId: string // 用户ID，必填
  assistantAvatar?: string // 助手头像，可选，默认使用内置头像
  initialMessages?: Message[] // 初始消息，可选
  placeholder?: string // 输入框占位符，可选
  maxHistoryCount?: number // 最大历史会话数量，可选
  openRemark?: string // 开场白，可选
  recommendQuestions?: string[] // 推荐问题列表，可选
}

const props = withDefaults(defineProps<Props>(), {
  assistantAvatar: '',
  initialMessages: () => [],
  placeholder: "",
  maxHistoryCount: 50,
  openRemark: '',
  recommendQuestions: () => []
})

// Events 定义
interface Emits {
  streamComplete: [data: {
    conversationId: string
    messageId: string
    content: string
  }] // 对话流完成事件
  sourceClick: [source: TraceFile] // 文件来源点击事件
  messageReceived: [message: Message] // 收到新消息事件
  chatCreated: [chatIndex: number] // 创建新会话事件
  chatSwitched: [chatIndex: number] // 切换会话事件
}

const emit = defineEmits<Emits>()

interface EChartData {
  id: string
  option: any
}

interface Message {
  text: string
  isUser: boolean
  isLoading?: boolean
  formattedText?: string
  sources?: TraceData  // 更新为TraceData类型
  conversationId?: string  // 会话ID
  messageId?: string  // 消息ID
  echarts?: EChartData[]  // ECharts 图表数据
  isOpenRemark?: boolean  // 是否为开场白消息
}

// 消息体底部锚点
const bottomAnchor = ref<HTMLElement | null>(null)

const messages = ref<Message[]>([])
const inputMessage = ref('')
const messagesContainer = ref<HTMLElement | null>(null)
const isLoading = ref(false)
const currentTaskId = ref('')
const showStopButton = ref(false)
const hoveredMessageIndex = ref(-1)
const editingMessageIndex = ref(-1)
const editingMessageText = ref('')

// 文件预览相关状态
const filePreviewVisible = ref(false)
const previewFileUrl = ref('')
const previewLoading = ref(false)

// Excel表格预览相关状态
const excelPreviewVisible = ref(false)
const excelPreviewContent = ref('')
const excelPreviewLoading = ref(false)

// 添加新的状态管理
interface ChatSession {
  messages: Message[]
  title: string
  timestamp: number
  conversationId?: string
  lastMessageId?: string
}

const isSidebarCollapsed = ref(false)
const chatHistory = ref<ChatSession[]>([])
const currentChatIndex = ref(0)

// 计算sessionStorage的key
const getChatHistoryKey = () => {
  return `chat_history_${props.chatType}`
}

// 从sessionStorage加载会话历史
const loadChatHistory = () => {
  const savedHistory = sessionStorage.getItem(getChatHistoryKey())
  if (savedHistory) {
    try {
      chatHistory.value = JSON.parse(savedHistory)
      // 限制历史会话数量
      if (chatHistory.value.length > props.maxHistoryCount) {
        chatHistory.value = chatHistory.value.slice(-props.maxHistoryCount)
      }
      // 如果有历史会话，默认选中最后一个
      if (chatHistory.value.length > 0) {
        currentChatIndex.value = chatHistory.value.length - 1
        messages.value = [...chatHistory.value[currentChatIndex.value].messages]
      }
    } catch (e) {
      console.error('加载会话历史失败:', e)
      chatHistory.value = []
    }
  }
}

// 保存会话历史到sessionStorage
const saveChatHistory = () => {
  // 限制历史会话数量
  if (chatHistory.value.length > props.maxHistoryCount) {
    chatHistory.value = chatHistory.value.slice(-props.maxHistoryCount)
  }
  sessionStorage.setItem(getChatHistoryKey(), JSON.stringify(chatHistory.value))
}

// 切换侧边栏状态
const toggleSidebar = () => {
  isSidebarCollapsed.value = !isSidebarCollapsed.value
}

// 创建新会话
const createNewChat = async () => {
  // 保存当前会话（如果有内容的话）
  if (messages.value.length > 0 && chatHistory.value[currentChatIndex.value]) {
    chatHistory.value[currentChatIndex.value] = {
      messages: [...messages.value],
      title: messages.value[0]?.text.slice(0, 20) + '...' || '新会话',
      timestamp: Date.now(),
      conversationId: chatHistory.value[currentChatIndex.value].conversationId,
      lastMessageId: chatHistory.value[currentChatIndex.value].lastMessageId
    }
    saveChatHistory()
  }
  
  // 创建新会话初始消息
  let initialMessages = [...props.initialMessages]
  
  // 如果有开场白，添加助手消息
  if (props.openRemark && props.openRemark.trim()) {
    const formatted = await formatAnswer(props.openRemark)
    const openRemarkMessage: Message = {
      text: props.openRemark,
      isUser: false,
      formattedText: formatted.html,
      echarts: formatted.echarts,
      isOpenRemark: true
    }
    initialMessages.push(openRemarkMessage)
  }
  
  // 创建新会话
  const newSession: ChatSession = {
    messages: initialMessages,
    title: '新会话',
    timestamp: Date.now()
  }
  chatHistory.value.push(newSession)
  currentChatIndex.value = chatHistory.value.length - 1
  messages.value = initialMessages
  saveChatHistory()
  
  // 发射会话创建事件
  emit('chatCreated', currentChatIndex.value)
  
  // 滚动到底部显示新消息
  await nextTick()
  throttledScrollToBottom()
}

// 切换会话
const switchChat = (index: number) => {
  if (index === currentChatIndex.value) return
  
  // 保存当前会话
  if (messages.value.length > 0 && chatHistory.value[currentChatIndex.value]) {
    chatHistory.value[currentChatIndex.value] = {
      messages: [...messages.value],
      title: messages.value[0]?.text.slice(0, 20) + '...' || '新会话',
      timestamp: Date.now(),
      conversationId: chatHistory.value[currentChatIndex.value].conversationId,
      lastMessageId: chatHistory.value[currentChatIndex.value].lastMessageId
    }
    saveChatHistory()
  }
  
  // 切换到选中的会话
  currentChatIndex.value = index
  messages.value = [...chatHistory.value[index].messages]
  
  // 发射会话切换事件
  emit('chatSwitched', index)
}

// 更新当前会话的会话ID和消息ID
const updateSessionIds = (conversationId: string, messageId: string) => {
  if (chatHistory.value[currentChatIndex.value]) {
    chatHistory.value[currentChatIndex.value].conversationId = conversationId
    chatHistory.value[currentChatIndex.value].lastMessageId = messageId
    saveChatHistory()
  }
}

// 格式化时间
const formatTime = (timestamp: number) => {
  const date = new Date(timestamp)
  return `${date.getMonth() + 1}/${date.getDate()} ${date.getHours()}:${date.getMinutes().toString().padStart(2, '0')}`
}

// 生成Excel文件名 (格式: YYYYMMDDHHMM.excel)
const generateExcelFileName = (timestamp: number = Date.now()) => {
  const date = new Date(timestamp)
  const year = date.getFullYear()
  const month = (date.getMonth() + 1).toString().padStart(2, '0')
  const day = date.getDate().toString().padStart(2, '0')
  const hour = date.getHours().toString().padStart(2, '0')
  const minute = date.getMinutes().toString().padStart(2, '0')
  
  return `${year}${month}${day}${hour}${minute}.excel`
}

// 监听消息变化，自动保存到当前会话
watch(messages, (newMessages) => {
  if (newMessages.length > 0 && currentChatIndex.value < chatHistory.value.length) {
    const currentSession = chatHistory.value[currentChatIndex.value]
    chatHistory.value[currentChatIndex.value] = {
      messages: [...newMessages],
      title: newMessages[0]?.text.slice(0, 20) + '...' || '新会话',
      timestamp: Date.now(),
      conversationId: currentSession?.conversationId,
      lastMessageId: currentSession?.lastMessageId
    }
    saveChatHistory()
  }
}, { deep: true })


// 用户是否正在拖动滚动条
let isUserScrolling = false

// 初始化时加载会话历史
onMounted(async () => {
  loadChatHistory()
  // 如果没有历史会话，创建一个新会话
  if (chatHistory.value.length === 0) {
    await createNewChat()
  }

    //监听用户是否拖动滚动条
  messagesContainer.value?.addEventListener('scroll', () => {
    const el = messagesContainer.value
    if (!el) return
    const threshold = 100
    isUserScrolling = el.scrollHeight - el.scrollTop - el.clientHeight > threshold
  })

})

// 解析ECharts代码块
const parseECharts = (text: string): { processedText: string; echarts: EChartData[] } => {
  const echartsRegex = /```echarts\n([\s\S]*?)\n```/g
  const echarts: EChartData[] = []
  let match
  let processedText = text

  while ((match = echartsRegex.exec(text)) !== null) {
    try {
      const echartsJson = match[1].trim()
      const option = JSON.parse(echartsJson)
      const id = `echart_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`
      
      echarts.push({ id, option })
      
      // 将echarts代码块替换为占位符
      processedText = processedText.replace(match[0], `<div class="echart-placeholder" data-chart-id="${id}"></div>`)
    } catch (error) {
      console.error('解析ECharts配置失败:', error)
      // 如果解析失败，保留原始代码块
    }
  }

  return { processedText, echarts }
}

// 格式化答案
const formatAnswer = async (text: string) => {
  try {
    // 先解析ECharts代码块
    const { processedText, echarts } = parseECharts(text)
    
    // 配置marked以更好地支持表格  
    const html = await marked.parse(processedText, {
      gfm: true,
      breaks: true,
      async: true
    })

    // 为表格添加容器包装和强制样式属性
    const wrappedHtml = html.replace(
      /<table>/g, 
      `<details class="table-wrapper" open>
      <summary>
          Expand / Collapse table
      </summary>
      <div class="table-container"><table class="excel-table" border="1" width="auto" cellspacing="0" cellpadding="0">`
    ).replace(
    /<\/table>/g,
    '</table></div></details>'

    ).replace(
      /<th>/g,
      '<th style="border: 1px solid #cbd5e1; padding: 12px; background: #f1f5f9;">'
    ).replace(
      /<td>/g,
      '<td style="border: 1px solid #cbd5e1; padding: 12px;">'
    )

    const sanitizedHtml = DOMPurify.sanitize(wrappedHtml, {
      ALLOWED_TAGS: [
        'strong', 'em', 'code', 'pre', 'a', 'br', 'p', 
        'ul', 'ol', 'li', 'h1', 'h2', 'h3', 'span',
        'blockquote', 'hr', 'table', 'thead', 'tbody',
        'tr', 'th', 'td', 'div', 'details', 'summary'
      ],
      ALLOWED_ATTR: ['href', 'target', 'class', 'style', 'data-chart-id', 'border', 'cellspacing', 'cellpadding', 'open']
    }) as string

    console.log('Markdown输入:', processedText)
    console.log('生成的HTML:', sanitizedHtml)

    return { html: sanitizedHtml, echarts }
  } catch (error) {
    console.error('Error formatting answer:', error)
    return { html: text, echarts: [] }
  }
}

const sendMessage = async () => {
  if (!inputMessage.value.trim() || isLoading.value) return

  try {
    isLoading.value = true
    const userQuery = inputMessage.value
    inputMessage.value = ''

    // 添加用户消息
    const userMessage: Message = {
      text: userQuery,
      isUser: true,
    }
    messages.value.push(userMessage)
    
    // 发射消息接收事件
    emit('messageReceived', userMessage)

    // 创建响应式机器人消息
    const botMessage = reactive<Message>({
      text: '',
      isUser: false,
      isLoading: true,
      formattedText: ''
    })
    messages.value.push(botMessage)
    await scrollToBottom()

    // 获取当前会话的会话ID
    const currentSession = chatHistory.value[currentChatIndex.value]
    const conversationId = currentSession?.conversationId

    // 发起请求
    const response = await sendChatMessage({
      content: userQuery,
      userId: props.userId,
      chatType: props.chatType,
      conversationId: conversationId // 如果有会话ID则带上
    })

    // 显示中止按钮
    showStopButton.value = true

    if (!response.body) throw new Error('No response body')

    const reader = response.body.getReader()
    const decoder = new TextDecoder()
    let buffer = ''
    let newConversationId = ''
    let newMessageId = ''

    while (true) {
      const { done, value } = await reader.read()
      if (done) break

      buffer += decoder.decode(value, { stream: true })
      const chunks = buffer.split(/\n\n/)
      buffer = chunks.pop() || ''

      for (const chunk of chunks) {
        const eventData = chunk.trim()
        if (!eventData) continue

        const jsonData = eventData.replace(/^data:\s*/, '')
        
        try {
          const data = JSON.parse(jsonData) as ChatMessageResponse
          if (data.answer) {
            botMessage.text += data.answer
            const formatted = await formatAnswer(parseThink(botMessage.text).answer)
            botMessage.formattedText = formatted.html
            botMessage.echarts = formatted.echarts
            
            // 更新会话ID和消息ID
            if (data.conversationId) {
              newConversationId = data.conversationId
            }
            if (data.messageId) {
              newMessageId = data.messageId
            }
            
            // 获取并存储taskId
            if (data.taskId) {
              currentTaskId.value = data.taskId
            }
            
            throttledScrollToBottom()
          }
        } catch (e) {
          console.error('解析错误:', e)
        }
      }
    }

    // 处理剩余数据
    if (buffer) {
      const eventData = buffer.trim()
      if (eventData) {
        try {
          const jsonData = eventData.replace(/^data:\s*/, '')
          const data = JSON.parse(jsonData)
          if (data.answer) {
            botMessage.text += data.answer
            const formatted = await formatAnswer(parseThink(botMessage.text).answer)
            botMessage.formattedText = formatted.html
            botMessage.echarts = formatted.echarts
            
            if (data.conversationId) {
              newConversationId = data.conversationId
            }
            if (data.messageId) {
              newMessageId = data.messageId
            }
          }
        } catch (e) {
          console.error('最终解析错误:', e)
        }
      }
    }

    // 更新会话ID和消息ID
    if (newConversationId || newMessageId) {
      updateSessionIds(newConversationId, newMessageId)
    }

    // 获取文件来源数据（仅当chatType=1时）
    if (newConversationId && newMessageId && props.chatType === '1') {
      try {
        const { data } = await getFilePathList(newConversationId, newMessageId)
        if (data && ((data.tracePdf && data.tracePdf.length > 0) || (data.traceExcel && data.traceExcel.trim()))) {
          botMessage.sources = data
          botMessage.conversationId = newConversationId
          botMessage.messageId = newMessageId
        }
      } catch (error) {
        console.log('获取文件来源失败:', error)
        // 不显示错误信息，静默失败
      }
    }

    // 发射消息接收事件
    emit('messageReceived', botMessage)

    // 流式请求完成，发送事件
    emit('streamComplete', {
      conversationId: newConversationId,
      messageId: newMessageId,
      content: userQuery
    })

  } catch (error) {
    console.error('请求失败:', error)
    const errorMessage: Message = {
      text: '请求失败，请稍后重试',
      isUser: false,
    }
    messages.value.push(errorMessage)
    emit('messageReceived', errorMessage)
  } finally {
    isLoading.value = false
    showStopButton.value = false // 隐藏中止按钮
    currentTaskId.value = '' // 清除taskId
    if (messages.value[messages.value.length - 1]?.isLoading) {
      messages.value[messages.value.length - 1].isLoading = false
    }
    scrollToBottomForce()
  }
}

const scrollToBottomForce = async () => {
  await nextTick()
  if (messagesContainer.value) {
    messagesContainer.value.scrollTop = messagesContainer.value.scrollHeight
  }
}

//将锚点滚动到最底部
const scrollToBottom = async () => {
  await nextTick()
  setTimeout(() => {
  requestAnimationFrame(() => {
    if (messagesContainer.value && !isUserScrolling) {
      bottomAnchor.value.scrollIntoView({
          behavior: 'smooth',
          block: 'start'
        })
    }
  })
  },60)
}

const throttledScrollToBottom = throttle(() => {
    scrollToBottom()
}, 300,{ leading: true, trailing: true })

// 解析方法
const parseThink = (text: string) => {
  const thinkRegex = /<think>(.*?)<\/think>/s
  const match = text.match(thinkRegex)
  return {
    think: match?.[1] || '',
    answer: match ? text.replace(thinkRegex, '').trim() : text,
  }
}

// 获取文件类型图标 - 使用动态导入确保路径正确
const getFileTypeIcon = (fileType: string) => {
  const icons = require.context('@/assets/img/filetype-icon', false, /\.png$/)
  // console.log(icons)
  // console.log('所有图标 keys:', icons.keys())

  const getIconUrl = (iconName: string) => {
    const fullName = `./${iconName}.png`
    if (icons.keys().includes(fullName)) {
      return icons(fullName)
    } else {
      // console.warn('图标不存在:', fullName)
      return ''
    }
  }

  const iconMap: Record<string, string> = {
    'pdf': getIconUrl('pdf'),
    'doc': getIconUrl('word'),
    'docx': getIconUrl('word'),
    'xls': getIconUrl('excel'),
    'xlsx': getIconUrl('excel'),
    'ppt': getIconUrl('ppt'),
    'pptx': getIconUrl('ppt'),
    'txt': getIconUrl('txt'),
    'xml': getIconUrl('xml'),
    'zip': getIconUrl('zip'),
    'mp4': getIconUrl('video'),
    'avi': getIconUrl('video'),
    'mp3': getIconUrl('audio'),
    'wav': getIconUrl('audio'),
    'jpg': getIconUrl('img'),
    'jpeg': getIconUrl('img'),
    'png': getIconUrl('img'),
    'gif': getIconUrl('img'),
  }
  return iconMap[fileType.toLowerCase()] || getIconUrl('unknown')
}

// 获取文件扩展名
const getFileExtension = (fileName: string) => {
  return fileName.split('.').pop()?.toLowerCase() || ''
}

// 处理文件来源点击事件
const handleSourceClick = async (source: TraceFile, conversationId?: string, messageId?: string) => {
  // 只有chatType=1时才允许点击预览
  if (props.chatType !== '1') {
    return
  }

  if (!conversationId || !messageId) {
    ElMessage.warning('缺少会话信息，无法预览文件')
    return
  }

  try {
    previewLoading.value = true
    filePreviewVisible.value = true
    
    // 构造完整的PDF预览URL
    const baseUrl = window.location.protocol + '//' + window.location.host
    previewFileUrl.value = baseUrl + source.filePath
    
    console.log('预览文件:', source)
    console.log('预览URL:', previewFileUrl.value)
    // 发射文件来源点击事件，让父组件处理
    emit('sourceClick', source)
  } catch (error) {
    console.error('获取文件预览失败:', error)
    ElMessage.error('获取文件预览失败')
    filePreviewVisible.value = false
  } finally {
    previewLoading.value = false
  }
}

// 处理Excel表格点击事件
const handleExcelClick = async (excelContent: string) => {
  try {
    excelPreviewLoading.value = true
    excelPreviewVisible.value = true
    
    // 将 \n 字符串转换为真正的换行符
    const processedContent = excelContent.replace(/\\n/g, '\n')
    
    // 如果marked转换失败，尝试手动解析markdown表格
    let htmlContent = ''
    try {
      const formattedContent = await formatAnswer(processedContent)
      htmlContent = formattedContent.html
    } catch (error) {
      console.warn('marked解析失败，尝试手动解析:', error)
      htmlContent = parseMarkdownTable(processedContent)
    }
    
    // 如果HTML中没有table标签，尝试手动解析
    if (!htmlContent.includes('<table')) {
      console.warn('未检测到表格，手动解析markdown')
      htmlContent = parseMarkdownTable(processedContent)
    }
    
    excelPreviewContent.value = htmlContent
    
    console.log('原始Excel数据:', excelContent)
    console.log('处理后的Excel数据:', processedContent)
    console.log('最终HTML:', htmlContent)
  } catch (error) {
    console.error('解析Excel表格失败:', error)
    ElMessage.error('解析Excel表格失败')
    excelPreviewVisible.value = false
  } finally {
    excelPreviewLoading.value = false
  }
}

// 手动解析markdown表格为HTML
const parseMarkdownTable = (markdown: string): string => {
  const lines = markdown.trim().split('\n')
  if (lines.length < 3) return markdown // 至少需要表头、分隔线、数据行
  
  const headerLine = lines[0]
  const separatorLine = lines[1]
  const dataLines = lines.slice(2)
  
  // 检查是否是有效的markdown表格
  if (!headerLine.includes('|') || !separatorLine.includes('---')) {
    return markdown
  }
  
  // 解析表头
  const headers = headerLine.split('|').map(h => h.trim()).filter(h => h)
  
  // 解析数据行
  const rows = dataLines.map(line => 
    line.split('|').map(cell => cell.trim()).filter(cell => cell)
  ).filter(row => row.length > 0)
  
  // 生成HTML表格
  let html = `
    <div class="table-container">
      <table class="excel-table" border="1" cellspacing="0" cellpadding="0" style="border-collapse: collapse; width: auto; border: 2px solid #cbd5e1;">
        <thead>
          <tr>
  `
  
  headers.forEach(header => {
    html += `<th style="border: 1px solid #cbd5e1; padding: 12px; background: linear-gradient(135deg, #f1f5f9 0%, #e2e8f0 100%); font-weight: 600; text-align: left; position: sticky; top: 0; z-index: 2; white-space: nowrap;">${header}</th>`
    //html += `<th style="border: 1px solid #cbd5e1; padding: 12px; background: linear-gradient(135deg, #f1f5f9 0%, #e2e8f0 100%); font-weight: 600; text-align: left;">${header}</th>`
  })
  
  html += `
          </tr>
        </thead>
        <tbody>
  `
  
  rows.forEach((row, rowIndex) => {
    html += '<tr>'
    row.forEach((cell, cellIndex) => {
      const cellStyle = rowIndex % 2 === 0 
        ? 'border: 1px solid #cbd5e1; padding: 12px; background: white;'
        : 'border: 1px solid #cbd5e1; padding: 12px; background: #f8fafc;'
      html += `<td style="${cellStyle}">${cell}</td>`
    })
    html += '</tr>'
  })
  
  html += `
        </tbody>
      </table>
    </div>
  `
  
  return html
}

// 获取助手头像
const getAssistantAvatar = () => {
  return props.assistantAvatar || new URL('@/assets/assistant.png', import.meta.url).href
}

// 中止流式回答
const handleStopStream = async () => {
  if (currentTaskId.value) {
    try {
      await stopMessagesStream(Number(props.chatType), currentTaskId.value, props.userId)
      
      

      // 隐藏中止按钮
      showStopButton.value = false
      


      // 停止加载状态
      isLoading.value = false
      
      // 移除当前消息的加载状态
      if (messages.value.length > 0) {
        const lastMessage = messages.value[messages.value.length - 1]
        if (!lastMessage.isUser && lastMessage.isLoading) {
          lastMessage.isLoading = false
          if (!lastMessage.text.trim()) {
            lastMessage.text = '已停止生成'
          }
        }
      }
      
      // 清除taskId
      currentTaskId.value = ''
      
      ElMessage.success('已停止生成')
    } catch (error) {
      console.error('停止流式回答失败:', error)
      ElMessage.error('停止失败，请重试')
    }
  }
}

// 处理消息悬浮事件
const handleMessageHover = (index: number, isHovered: boolean) => {
  hoveredMessageIndex.value = isHovered ? index : -1
}

// 复制消息内容
const handleCopyMessage = async (message: Message) => {
  const textToCopy = message.text
  
  try {
    // 优先使用现代的 Clipboard API
    if (navigator.clipboard && window.isSecureContext) {
      await navigator.clipboard.writeText(textToCopy)
      ElMessage.success('已复制到剪贴板')
      return
    }
    
    // 回退到传统方法
    const textArea = document.createElement('textarea')
    textArea.value = textToCopy
    textArea.style.position = 'fixed'
    textArea.style.left = '-999999px'
    textArea.style.top = '-999999px'
    document.body.appendChild(textArea)
    textArea.focus()
    textArea.select()
    
    const successful = document.execCommand('copy')
    document.body.removeChild(textArea)
    
    if (successful) {
      ElMessage.success('已复制到剪贴板')
    } else {
      throw new Error('execCommand failed')
    }
  } catch (error) {
    console.error('复制失败:', error)
    ElMessage.error('复制失败')
  }
}

// 编辑消息
const handleEditMessage = (message: Message, index: number) => {
  editingMessageIndex.value = index
  editingMessageText.value = message.text
  hoveredMessageIndex.value = -1 // 隐藏操作按钮
}

// 取消编辑
const handleCancelEdit = () => {
  editingMessageIndex.value = -1
  editingMessageText.value = ''
}

// 保存编辑
const handleSaveEdit = async () => {
  if (!editingMessageText.value.trim()) return
  
  const newText = editingMessageText.value.trim()
  
  // 重置编辑状态
  editingMessageIndex.value = -1
  editingMessageText.value = ''
  
  // 将编辑后的文本作为新的用户问题发送
  inputMessage.value = newText
  await sendMessage()
}

// 重新生成回答
const handleRegenerateMessage = async (index: number) => {
  if (index === 0) return
  
  // 获取前一条用户消息
  const userMessage = messages.value[index - 1]
  if (!userMessage || !userMessage.isUser) return
  
  // 删除当前助手消息及之后的消息
  messages.value = messages.value.slice(0, index)
  
  // 直接重新生成，不创建新的用户消息
  await regenerateResponse(userMessage.text)
}

// 重新生成回答的核心逻辑
const regenerateResponse = async (userQuery: string) => {
  try {
    isLoading.value = true

    // 创建响应式机器人消息
    const botMessage = reactive<Message>({
      text: '',
      isUser: false,
      isLoading: true,
      formattedText: ''
    })
    messages.value.push(botMessage)
    throttledScrollToBottom()

    // 获取当前会话的会话ID
    const currentSession = chatHistory.value[currentChatIndex.value]
    const conversationId = currentSession?.conversationId

    // 发起请求
    const response = await sendChatMessage({
      content: userQuery,
      userId: props.userId,
      chatType: props.chatType,
      conversationId: conversationId
    })

    // 显示中止按钮
    showStopButton.value = true

    if (!response.body) throw new Error('No response body')

    const reader = response.body.getReader()
    const decoder = new TextDecoder()
    let buffer = ''
    let newConversationId = ''
    let newMessageId = ''

    while (true) {
      const { done, value } = await reader.read()
      if (done) break

      buffer += decoder.decode(value, { stream: true })
      const chunks = buffer.split(/\n\n/)
      buffer = chunks.pop() || ''

      for (const chunk of chunks) {
        const eventData = chunk.trim()
        if (!eventData) continue

        const jsonData = eventData.replace(/^data:\s*/, '')
        
        try {
          const data = JSON.parse(jsonData) as ChatMessageResponse
          if (data.answer) {
            botMessage.text += data.answer
            const formatted = await formatAnswer(parseThink(botMessage.text).answer)
            botMessage.formattedText = formatted.html
            botMessage.echarts = formatted.echarts
            
            // 更新会话ID和消息ID
            if (data.conversationId) {
              newConversationId = data.conversationId
            }
            if (data.messageId) {
              newMessageId = data.messageId
            }
            
            // 获取并存储taskId
            if (data.taskId) {
              currentTaskId.value = data.taskId
            }
            
            throttledScrollToBottom()
          }
        } catch (e) {
          console.error('解析错误:', e)
        }
      }
    }

    // 处理剩余数据
    if (buffer) {
      const eventData = buffer.trim()
      if (eventData) {
        try {
          const jsonData = eventData.replace(/^data:\s*/, '')
          const data = JSON.parse(jsonData)
          if (data.answer) {
            botMessage.text += data.answer
            const formatted = await formatAnswer(parseThink(botMessage.text).answer)
            botMessage.formattedText = formatted.html
            botMessage.echarts = formatted.echarts
            
            if (data.conversationId) {
              newConversationId = data.conversationId
            }
            if (data.messageId) {
              newMessageId = data.messageId
            }
          }
        } catch (e) {
          console.error('最终解析错误:', e)
        }
      }
    }

    // 更新会话ID和消息ID
    if (newConversationId || newMessageId) {
      updateSessionIds(newConversationId, newMessageId)
    }

    // 获取文件来源数据（仅当chatType=1时）
    if (newConversationId && newMessageId && props.chatType === '1') {
      try {
        const { data } = await getFilePathList(newConversationId, newMessageId)
        if (data && ((data.tracePdf && data.tracePdf.length > 0) || (data.traceExcel && data.traceExcel.trim()))) {
          botMessage.sources = data
          botMessage.conversationId = newConversationId
          botMessage.messageId = newMessageId
        }
      } catch (error) {
        console.log('获取文件来源失败:', error)
        // 不显示错误信息，静默失败
      }
    }

    // 发射消息接收事件
    emit('messageReceived', botMessage)

    // 流式请求完成，发送事件
    emit('streamComplete', {
      conversationId: newConversationId,
      messageId: newMessageId,
      content: userQuery
    })

  } catch (error) {
    console.error('重新生成失败:', error)
    const errorMessage: Message = {
      text: '重新生成失败，请稍后重试',
      isUser: false,
    }
    messages.value.push(errorMessage)
    emit('messageReceived', errorMessage)
  } finally {
    isLoading.value = false
    showStopButton.value = false
    currentTaskId.value = ''
    if (messages.value[messages.value.length - 1]?.isLoading) {
      messages.value[messages.value.length - 1].isLoading = false
    }
    scrollToBottomForce()
  }
}

// 点赞消息
const handleLikeMessage = (index: number) => {
  // TODO: 实现点赞逻辑，调用相关API
  ElMessage.success('感谢您的反馈！')
}

// 点踩消息
const handleDislikeMessage = (index: number) => {
  // TODO: 实现点踩逻辑，调用相关API
  ElMessage.info('感谢您的反馈，我们会继续改进')
}

// 处理推荐问题点击事件
const handleRecommendQuestionClick = (question: string) => {
  if (!question.trim() || isLoading.value) return
  
  // 将问题内容填入输入框
  inputMessage.value = question
  
  // 发送问题
  sendMessage()
}
</script>

<style scoped>
.chat-container {
  height: 100%;
  display: flex;
  background: #ffffff;
  position: relative;
  overflow: hidden;
  border: 1px solid #e2e8f0;
  border-radius: 12px;
  box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
}

/* 侧边栏样式 */
.chat-sidebar {
  width: 280px;
  background: #f8fafc;
  border-right: 1px solid #e2e8f0;
  display: flex;
  flex-direction: column;
  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
  position: relative;
  flex-shrink: 0;
  transform-origin: left;
  will-change: width, transform;
  border-top-left-radius: 12px;
  border-bottom-left-radius: 12px;
}

.sidebar-collapsed {
  width: 48px;
}

.sidebar-header {
  padding: 1rem;
  border-bottom: 1px solid #e2e8f0;
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
  transition: opacity 0.2s ease;
  white-space: nowrap;
  overflow: hidden;
}

.sidebar-collapsed .sidebar-header {
  opacity: 0;
  visibility: hidden;
}

.chat-history {
  flex: 1;
  overflow-y: auto;
  padding: 0.5rem;
  transition: opacity 0.2s ease;
}

.sidebar-collapsed .chat-history {
  opacity: 0;
  visibility: hidden;
}

.sidebar-header h3 {
  margin: 0;
  font-size: 1rem;
  color: #1e293b;
}

.new-chat-btn {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  width: 100%;
  justify-content: center;
  background: #4f46e5;
  border: none;
  padding: 0.5rem;
  border-radius: 8px;
  color: white;
  font-size: 0.875rem;
  cursor: pointer;
  transition: all 0.2s ease;
}

.new-chat-btn:hover {
  background: #4338ca;
}

.new-chat-icon {
  width: 16px;
  height: 16px;
}

.chat-history-item {
  padding: 0.75rem;
  border-radius: 8px;
  cursor: pointer;
  transition: all 0.2s ease;
  margin-bottom: 0.5rem;
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
}

.chat-history-item:hover {
  background: #f1f5f9;
}

.chat-history-item.active {
  background: #e0e7ff;
}

.chat-title {
  font-size: 0.875rem;
  color: #1e293b;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.chat-time {
  font-size: 0.75rem;
  color: #64748b;
}

.sidebar-toggle {
  position: absolute;
  right: -12px;
  top: 50%;
  transform: translateY(-50%);
  width: 24px;
  height: 24px;
  background: #ffffff;
  border: 1px solid #e2e8f0;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
  z-index: 10;
  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}

.sidebar-toggle:hover {
  background: #f8fafc;
  box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
  transform: translateY(-50%) scale(1.1);
}

.sidebar-toggle:active {
  transform: translateY(-50%) scale(0.95);
}

.toggle-icon {
  width: 16px;
  height: 16px;
  transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);
  transform-origin: center;
}

.toggle-icon.collapsed {
  transform: rotate(180deg);
}

/* 主聊天区域样式调整 */
.chat-main {
  flex: 1;
  display: flex;
  flex-direction: column;
  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
  min-width: 0;
  width: 100%;
  position: relative;
  overflow: hidden;
  border-top-right-radius: 12px;
  border-bottom-right-radius: 12px;
}

.main-expanded {
  margin-left: 0;
  width: 100%;
}

.chat-messages {
  flex: 1;
  overflow-y: auto;
  padding: 2rem 0;
  padding-bottom: 100px;
  scroll-behavior: smooth;
  background: #f9fafb;
  width: 100%;
}

.message-item {
  display: flex;
  padding: 1rem 2rem;
  position: relative;
  width: 100%;
  box-sizing: border-box;
}

.message-item.user-message {
  flex-direction: row-reverse;
}

.avatar {
  width: 40px;
  height: 40px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  margin: 0 1rem;
  flex-shrink: 0;
}

.message-content-wrapper {
  max-width: 85%;
  position: relative;
}

/* 当消息包含图表时，扩大消息气泡宽度 */
.assistant-message .message-content-wrapper:has(.echarts-container) {
  max-width: 95%;
  width: 95%;
}

/* 兼容性：通过类名控制图表消息宽度 */
.assistant-message .message-content-wrapper.has-charts {
  max-width: 95%;
  width: 95%;
}





.message-content {
  padding: 1rem 1.5rem;
  border-radius: 12px;
  position: relative;
  word-break: break-word;
  transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}

.user-message .message-content {
  background: #EFF6FF;
  border-radius: 16px 2px 16px 16px;
}

.assistant-message .message-content {
  background: #FDFDFD;
  border-radius: 2px 16px 16px 16px;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
}

/* 消息操作按钮样式 */
.message-actions {
  position: absolute;
  bottom: -2.5rem;
  display: flex;
  gap: 0.5rem;
  background: rgba(255, 255, 255, 0.95);
  backdrop-filter: blur(8px);
  border-radius: 12px;
  padding: 0.5rem;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
  border: 1px solid rgba(0, 0, 0, 0.08);
  animation: fadeInUp 0.2s ease-out;
  z-index: 10;
}

.user-actions {
  right: 0;
}

.assistant-actions {
  left: 0;
}

/* 操作按钮样式 */
.action-btn {
  width: 32px;
  height: 32px;
  border-radius: 8px;
  border: none;
  background: rgba(0, 0, 0, 0.04);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
  position: relative;
  overflow: hidden;
}

.action-btn:hover {
  background: rgba(0, 0, 0, 0.08);
  transform: translateY(-1px);
  box-shadow: 0 4px 8px rgba(0, 0, 0, 0.12);
}

.action-btn:active {
  transform: translateY(0);
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.08);
}

.action-icon {
  width: 16px;
  height: 16px;
  color: #64748b;
  transition: color 0.2s ease;
}

.action-btn:hover .action-icon {
  color: #374151;
}

/* 特定按钮颜色 */
.copy-btn:hover {
  background: rgba(59, 130, 246, 0.1);
}

.copy-btn:hover .action-icon {
  color: #3b82f6;
}

.edit-btn:hover {
  background: rgba(245, 158, 11, 0.1);
}

.edit-btn:hover .action-icon {
  color: #f59e0b;
}

.regenerate-btn:hover {
  background: rgba(16, 185, 129, 0.1);
}

.regenerate-btn:hover .action-icon {
  color: #10b981;
}

.like-btn:hover {
  background: rgba(34, 197, 94, 0.1);
}

.like-btn:hover .action-icon {
  color: #22c55e;
}

.dislike-btn:hover {
  background: rgba(239, 68, 68, 0.1);
}

.dislike-btn:hover .action-icon {
  color: #ef4444;
}

/* 按钮出现动画 */
@keyframes fadeInUp {
  from {
    opacity: 0;
    transform: translateY(10px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

/* 编辑容器样式 */
.edit-container {
  width: 100%;
  max-width: 600px;
  position: relative;
}

.edit-input-wrapper {
  position: relative;
  background: #ffffff;
  border: 1.5px solid #e5e7eb;
  border-radius: 12px;
  transition: all 0.3s ease;
  overflow: hidden;
}

.edit-input-wrapper:focus-within {
  border-color: #3b82f6;
  box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
}

.edit-textarea {
  width: 100%;
}

.edit-textarea :deep(.el-textarea__inner) {
  border: none;
  background: transparent;
  font-size: 0.95rem;
  line-height: 1.6;
  padding: 1rem 1rem 3rem 1rem;
  resize: none;
  box-shadow: none;
  border-radius: 0;
}

.edit-textarea :deep(.el-textarea__inner:focus) {
  border: none;
  box-shadow: none;
  outline: none;
}

.edit-actions {
  position: absolute;
  bottom: 0.75rem;
  right: 0.75rem;
  display: flex;
  gap: 0.5rem;
  z-index: 10;
}

.edit-btn {
  padding: 0.375rem 0.75rem;
  border-radius: 6px;
  font-size: 0.875rem;
  font-weight: 500;
  cursor: pointer;
  transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
  border: 1px solid transparent;
  outline: none;
  min-width: 48px;
  text-align: center;
}

.cancel-btn {
  background: #f8fafc;
  color: #64748b;
  border-color: #e2e8f0;
}

.cancel-btn:hover {
  background: #f1f5f9;
  color: #475569;
  border-color: #cbd5e1;
  transform: translateY(-1px);
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.08);
}

.cancel-btn:active {
  transform: translateY(0);
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.06);
}

.save-btn {
  background: #3b82f6;
  color: #ffffff;
  border-color: #3b82f6;
}

.save-btn:hover:not(:disabled) {
  background: #2563eb;
  border-color: #2563eb;
  transform: translateY(-1px);
  box-shadow: 0 4px 8px rgba(59, 130, 246, 0.3);
}

.save-btn:active:not(:disabled) {
  transform: translateY(0);
  box-shadow: 0 2px 4px rgba(59, 130, 246, 0.2);
}

.save-btn:disabled {
  background: #e5e7eb;
  color: #9ca3af;
  border-color: #e5e7eb;
  cursor: not-allowed;
  transform: none;
  box-shadow: none;
}

.message-header {
  margin-bottom: 0.5rem;
  display: flex;
  align-items: center;
}

.message-sender {
  font-size: 0.875rem;
  color: #6b7280;
  font-weight: 500;
}

.message-body {
  font-size: 0.95rem;
  line-height: 1.6;
  color: #1f2937;
}

.think-section {
  background: #f8fafc;
  border: 1px solid #e2e8f0;
  border-radius: 8px;
  padding: 1rem;
  margin-bottom: 1rem;
  font-size: 0.9rem;
  color: #64748b;
}

.think-header {
  display: flex;
  align-items: center;
  color: #64748b;
  font-weight: 600;
  margin-bottom: 0.5rem;
  font-size: 0.85rem;
  text-transform: uppercase;
  letter-spacing: 0.05em;
}

.think-icon {
  width: 16px;
  height: 16px;
  margin-right: 0.5rem;
}

.answer-content {
  color: #1f2937;
  line-height: 1.7;
  font-size: 0.95rem;
}

/* 标题样式 */
.answer-content :deep(h1),
.answer-content :deep(h2),
.answer-content :deep(h3) {
  margin: 1.5rem 0 1rem;
  font-weight: 600;
  line-height: 1.3;
}

.answer-content :deep(h1) {
  font-size: 1.5rem;
  border-bottom: 1px solid #e5e7eb;
  padding-bottom: 0.5rem;
}

.answer-content :deep(h2) {
  font-size: 1.3rem;
}

.answer-content :deep(h3) {
  font-size: 1.1rem;
}

/* 段落和列表样式 */
.answer-content :deep(p) {
  margin: 1rem 0;
}

.answer-content :deep(ul),
.answer-content :deep(ol) {
  margin: 1rem 0;
  padding-left: 1.5rem;
}

.answer-content :deep(li) {
  margin: 0.5rem 0;
}

.answer-content :deep(ul li) {
  list-style-type: disc;
}

.answer-content :deep(ol li) {
  list-style-type: decimal;
}

/* 链接样式 */
.answer-content :deep(a) {
  color: #4f46e5;
  text-decoration: none;
  border-bottom: 1px solid transparent;
  transition: border-color 0.2s ease;
}

.answer-content :deep(a:hover) {
  border-bottom-color: #4f46e5;
}

/* 强调和加粗 */
.answer-content :deep(em) {
  font-style: italic;
}

.answer-content :deep(strong) {
  font-weight: 600;
  color: #111827;
}

/* 代码样式 */
.answer-content :deep(code) {
  font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
  font-size: 0.9em;
  background: #f3f4f6;
  padding: 0.2em 0.4em;
  border-radius: 4px;
  color: #ef4444;
}

.answer-content :deep(pre) {
  background: #1e293b;
  color: #e2e8f0;
  padding: 1rem;
  border-radius: 8px;
  overflow-x: auto;
  margin: 1rem 0;
}

.answer-content :deep(pre code) {
  background: none;
  padding: 0;
  color: inherit;
  font-size: 0.9rem;
  line-height: 1.6;
}

/* 引用样式 */
.answer-content :deep(blockquote) {
  margin: 1rem 0;
  padding: 0.5rem 1rem;
  border-left: 4px solid #e5e7eb;
  background: #f9fafb;
  color: #4b5563;
}

.answer-content :deep(blockquote p) {
  margin: 0.5rem 0;
}

/* 水平线 */
.answer-content :deep(hr) {
  margin: 2rem 0;
  border: 0;
  border-top: 1px solid #e5e7eb;
}

/* 文件来源样式 */
.sources-section {
  margin-top: 1rem;
  padding: 0.75rem;
  background: #f8fafc;
  border: 1px solid #e2e8f0;
  border-radius: 8px;
  font-size: 0.875rem;
}

.sources-header {
  display: flex;
  align-items: center;
  color: #64748b;
  font-weight: 600;
  margin-bottom: 0.5rem;
  font-size: 0.8rem;
  text-transform: uppercase;
  letter-spacing: 0.05em;
}

.sources-icon {
  width: 14px;
  height: 14px;
  margin-right: 0.5rem;
}

.sources-list {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
}

.source-item {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.5rem 0.75rem;
  background: #ffffff;
  border: 1px solid #e2e8f0;
  border-radius: 20px;
  cursor: pointer;
  transition: all 0.2s ease;
  font-size: 0.8rem;
  max-width: 200px;
}

.source-item:hover {
  background: #f1f5f9;
  border-color: #cbd5e1;
  transform: translateY(-1px);
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
}

.source-icon {
  width: 16px;
  height: 16px;
  flex-shrink: 0;
  object-fit: contain;
}

.source-name {
  color: #374151;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  flex: 1;
  font-weight: 500;
}

/* 表格样式优化 */
.answer-content :deep(table) {
  width: auto; 
  min-width: 1000px;
  max-width: 2500px;
  margin: 1.5rem 0;
  border-collapse: collapse;
  border-radius: 8px;
  overflow: auto;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
  background: white;
  font-size: 0.9rem;
  line-height: 1.4;
  border: 1px solid #e5e7eb;

}

::v-deep .table-wrapper summary {
  cursor: pointer;
  display: list-item;
  align-items: center;
  gap: 8px;
  font-weight: 600;
  padding: 6px 8px;
  background: #f8fafc;
  /* border: 1px solid #e2e8f0; */
  /* border-radius: 6px; */
  font-size: 14px;
}


/* .excel-table th,
.excel-table td {
  white-space: nowrap;
} */

.excel-table th {
  white-space: nowrap;     /* 表头不换行 */
}

.excel-table td {
  min-width: 1000px;        /* 内容列设置最小宽度 */
  white-space: normal;     /* 允许内容换行（可选） */
}


.answer-content :deep(th),
.answer-content :deep(td) {
  padding: 0.75rem 1rem;
  border: 1px solid #e5e7eb;
  text-align: left;
  vertical-align: top;
  word-wrap: break-word;
  max-width: 200px;
  word-break: break-word;
  white-space: normal;
}

.answer-content :deep(th) {
  white-space: nowrap;
  background: #f8fafc;
  font-weight: 600;
  color: #374151;
  font-size: 0.85rem;
  text-transform: uppercase;
  letter-spacing: 0.025em;
  position: sticky;
  top: 0;
  z-index: 1;
  border-bottom: 2px solid #e5e7eb;
}

.answer-content :deep(td) {
  background: white;
  transition: background-color 0.2s ease;
}

.answer-content :deep(tr:hover td) {
  background: #f8fafc;
}

.answer-content :deep(tr:nth-child(even) td) {
  background: #fafbfc;
}

.answer-content :deep(tr:nth-child(even):hover td) {
  background: #f1f5f9;
}

.table-container {
  max-height: 350px;        /* 限制高度，超出时滚动 */
  overflow-x: auto;
  overflow-x: auto;         /* 保留横向滚动条（必要） */
  width: 100%;
  max-width: 100%;
}

.table-container table {
  max-width: 2000px;
  table-layout: auto; /* 或 fixed，视实际内容适配性而定 */
  width: auto;
}


/* 表格容器，支持横向滚动 */
.answer-content :deep(.table-container) {
  overflow-x: auto;
  overflow-y: auto;
  width:100%;
  max-width: 100%;          /* 限制在父容器内不超出 */
  max-height: 350px;        /* 限制表格高度，触发纵向滚动（根据实际需要可调） */
  margin: 1.5rem 0;
  border-radius: 8px;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
}

/* 移动端表格优化 */
@media (max-width: 768px) {
  .answer-content :deep(table) {
    min-width: 600px;
    font-size: 0.8rem;
    margin: 1rem 0;
    border: 1px solid #e5e7eb;
  }
  
  .answer-content :deep(th),
  .answer-content :deep(td) {
    padding: 0.5rem 0.75rem;
    max-width: 150px;
    border: 1px solid #e5e7eb;
  }
  
  .answer-content :deep(th) {
    font-size: 0.75rem;
    border-bottom: 2px solid #e5e7eb;
  }
}

.input-area {
  position: absolute;
  bottom: 0;
  left: 0;
  right: 0;
  background: #f9fafb;
  padding: 2rem;
  position: relative;
}

/* 中止按钮容器 */
.stop-button-container {
  position: absolute;
  top: -2.5rem;
  left: 50%;
  transform: translateX(-50%);
  z-index: 10;
  animation: fadeInDown 0.3s ease-out;
}

/* 中止按钮样式 */
.stop-button {
  width: 36px;
  height: 36px;
  border-radius: 50%;
  background: rgba(239, 68, 68, 0.1);
  backdrop-filter: blur(10px);
  border: 1.5px solid rgba(239, 68, 68, 0.2);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
  box-shadow: 0 3px 8px rgba(239, 68, 68, 0.15);
  position: relative;
  overflow: hidden;
}

.stop-button::before {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background: radial-gradient(circle, rgba(239, 68, 68, 0.1) 0%, transparent 70%);
  opacity: 0;
  transition: opacity 0.3s ease;
}

.stop-button:hover {
  background: rgba(239, 68, 68, 0.15);
  border-color: rgba(239, 68, 68, 0.3);
  transform: scale(1.05);
  box-shadow: 0 6px 20px rgba(239, 68, 68, 0.25);
}

.stop-button:hover::before {
  opacity: 1;
}

.stop-button:active {
  transform: scale(0.95);
  box-shadow: 0 2px 8px rgba(239, 68, 68, 0.2);
}

.stop-icon {
  width: 16px;
  height: 16px;
  color: #ef4444;
  transition: all 0.3s ease;
  z-index: 1;
  position: relative;
}

.stop-button:hover .stop-icon {
  color: #dc2626;
  transform: scale(1.1);
}

/* 按钮出现动画 */
@keyframes fadeInDown {
  from {
    opacity: 0;
    transform: translateX(-50%) translateY(-20px);
  }
  to {
    opacity: 1;
    transform: translateX(-50%) translateY(0);
  }
}

.input-wrapper {
  max-width: 900px;
  margin: 0 auto;
  position: relative;
  background: #ffffff;
  border-radius: 16px;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
  transition: all 0.3s ease;
}

.input-wrapper:focus-within {
  box-shadow: 0 4px 20px rgba(79, 70, 229, 0.15);
}

:deep(.el-textarea__inner) {
  padding: 1rem 4rem 1rem 1.5rem;
  border-radius: 16px;
  border: 1px solid #e5e7eb;
  background: transparent;
  font-size: 0.95rem;
  line-height: 1.5;
  resize: none;
  transition: all 0.3s ease;
  box-shadow: none;
}

:deep(.el-textarea__inner:focus) {
  border-color: #4f46e5;
}

.send-button {
  position: absolute;
  right: 0.75rem;
  top: 50%;
  transform: translateY(-50%);
  height: 2.5rem;
  width: 2.5rem;
  min-height: 2.5rem;
  padding: 0;
  border-radius: 50%;
  background: #4f46e5;
  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
  display: flex;
  align-items: center;
  justify-content: center;
  border: none;
  cursor: pointer;
  box-shadow: 0 2px 8px rgba(79, 70, 229, 0.25);
  overflow: hidden;
}

.send-button:not(:disabled) {
  background: linear-gradient(135deg, #4f46e5 0%, #4338ca 100%);
}

.send-button:disabled {
  opacity: 0.7;
  cursor: not-allowed;
}

.send-button:hover:not(:disabled) {
  transform: translateY(-50%) scale(1.05);
  box-shadow: 0 4px 12px rgba(79, 70, 229, 0.3);
  background: linear-gradient(135deg, #6366f1 0%, #4f46e5 100%);
}

.send-icon {
  width: 1.25rem;
  height: 1.25rem;
  margin-left: 2px;
  transition: transform 0.3s ease;
}

.loading-icon {
  width: 1.25rem;
  height: 1.25rem;
  border: 2px solid rgba(255, 255, 255, 0.3);
  border-radius: 50%;
  border-top-color: #ffffff;
  animation: spin 0.8s linear infinite;
}

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

:deep(.el-button.is-loading) {
  pointer-events: none;
  opacity: 0.9;
}

:deep(.el-button.is-loading .el-loading-mask) {
  display: none;
}

:deep(.el-button.is-loading::before) {
  display: none;
}

.input-footer {
  display: none;
}

@media (max-width: 768px) {
  .chat-container {
    border-radius: 8px;
  }

  .chat-sidebar {
    border-radius: 0;
  }

  .chat-main {
    border-radius: 0;
  }

  .chat-sidebar {
    position: absolute;
    height: 100%;
    z-index: 10;
    box-shadow: none;
  }

  .sidebar-collapsed {
    transform: translateX(-100%);
  }

  .sidebar-toggle {
    right: -16px;
    background: #ffffff;
    box-shadow: none;
  }

  .message-item {
    padding: 0.75rem 1rem;
  }

  .message-content-wrapper {
    max-width: 90%;
  }

  .message-content {
    padding: 0.75rem 1rem;
  }

  /* 移动端消息操作按钮适配 */
  .message-actions {
    bottom: -2rem;
    padding: 0.375rem;
    gap: 0.375rem;
    border-radius: 8px;
  }

  .action-btn {
    width: 28px;
    height: 28px;
    border-radius: 6px;
  }

  .action-icon {
    width: 14px;
    height: 14px;
  }

  .input-area {
    padding: 1.5rem 1rem;
    padding-top: 2.5rem;
  }

  .avatar {
    width: 32px;
    height: 32px;
    margin: 0 0.75rem;
  }

  .chat-messages {
    padding-bottom: 100px;
  }
  
  .send-button {
    right: 0.5rem;
    height: 2.25rem;
    width: 2.25rem;
    min-height: 2.25rem;
  }

  .send-button .send-icon,
  .loading-icon {
    width: 1rem;
    height: 1rem;
  }

  .loading-icon {
    border-width: 1.5px;
  }

  /* 移动端文件来源样式 */
  .sources-section {
    padding: 0.5rem;
    margin-top: 0.75rem;
  }

  .sources-list {
    gap: 0.375rem;
  }

  .source-item {
    padding: 0.375rem 0.5rem;
    font-size: 0.75rem;
    max-width: 150px;
  }

  .source-icon {
    width: 14px;
    height: 14px;
  }

  /* 移动端中止按钮适配 */
  .stop-button-container {
    top: -2rem;
  }
  
  .stop-button {
    width: 32px;
    height: 32px;
  }
  
  .stop-icon {
    width: 14px;
    height: 14px;
  }
}

.loading-content {
  padding: 1rem;
  display: flex;
  justify-content: center;
}

.loading-dots {
  display: flex;
  align-items: center;
  gap: 0.5rem;
}

.loading-dots .dot {
  width: 0.5rem;
  height: 0.5rem;
  background-color: #e2e8f0;
  border-radius: 50%;
  animation: pulse 1.4s infinite;
}

.loading-dots .dot:nth-child(2) {
  animation-delay: 0.2s;
}

.loading-dots .dot:nth-child(3) {
  animation-delay: 0.4s;
}

@keyframes pulse {
  0%, 80%, 100% {
    transform: scale(0.8);
    opacity: 0.4;
  }
  40% {
    transform: scale(1);
    opacity: 1;
  }
}

.typing-indicator {
  display: inline-flex;
  align-items: center;
  gap: 0.25rem;
  margin-top: 0.5rem;
}

.dot {
  width: 4px;
  height: 4px;
  background: #6b7280;
  border-radius: 50%;
  animation: bounce 1.4s infinite;
}

.dot:nth-child(2) { animation-delay: 0.2s; }
.dot:nth-child(3) { animation-delay: 0.4s; }

@keyframes bounce {
  0%, 80%, 100% { transform: translateY(0); }
  40% { transform: translateY(-4px); }
}

.avatar-icon {
  width: 24px;
  height: 24px;
  object-fit: cover;
  border-radius: 50%;
}

/* 文件预览模态框样式 */
.file-preview-dialog {
  .el-dialog {
    border-radius: 12px;
    overflow: hidden;
  }
  
  .el-dialog__header {
    background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
    color: white;
    border-radius: 12px 12px 0 0;
    margin: 0;
    padding: 20px 24px;
  }
  
  .el-dialog__title {
    color: white;
    font-weight: 600;
    font-size: 18px;
  }
  
  .el-dialog__headerbtn {
    .el-dialog__close {
      color: white;
      font-size: 20px;
      transition: all 0.2s ease;
      
      &:hover {
        color: rgba(255, 255, 255, 0.8);
        transform: scale(1.1);
      }
    }
  }
  
  .el-dialog__body {
    padding: 0;
    background: #f5f7fa;
  }
}

.preview-content {
  min-height: 600px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: #f5f7fa;
  border-radius: 0 0 12px 12px;
  
  .pdf-preview {
    width: 100%;
    height: 600px;
    border-radius: 8px;
    overflow: hidden;
    box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
    background: white;
  }
}

/* ECharts 图表样式 */
.echarts-container {
  margin: 1rem auto; /* 使用auto实现水平居中 */
  padding: 1.5rem;
  background: #f8fafc;
  border-radius: 12px;
  border: 1px solid #e2e8f0;
  /* 缩减到60%宽度，保持良好的显示比例 */
  width: 60%;
  max-width: 1000px; /* 设置最大宽度避免在大屏上过宽 */
  min-width: 400px; /* 设置最小宽度确保可读性 */
  position: relative;
}

::v-deep .icon-chevron {
  width: 20px;
  height: 20px;
  transition: transform 0.3s ease;
  color: #333; /* 让箭头颜色和文字颜色一致 */
}


.chart-wrapper {
  margin-bottom: 1.5rem;
  background: #ffffff;
  border-radius: 8px;
  padding: 1.5rem;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
}

.chart-wrapper:last-child {
  margin-bottom: 0;
}

.message-chart {
  width: 100%;
  height: 500px;
  min-height: 500px;
}

/* 响应式图表适配 */
@media (max-width: 768px) {
  .echarts-container {
    margin: 0.75rem auto; /* 移动端也使用auto居中 */
    padding: 1rem;
    /* 移动端保持70%宽度，确保在小屏幕上有足够显示空间 */
    width: 70%;
    max-width: 100%;
    min-width: 280px; /* 移动端最小宽度 */
  }
  
  .chart-wrapper {
    padding: 1rem;
    margin-bottom: 1rem;
  }
  
  .message-chart {
    height: 400px;
    min-height: 400px;
  }
  
  /* 移动端消息内容区域调整 */
  .message-content-wrapper {
    max-width: 92%;
  }
  
  /* 移动端包含图表的消息气泡宽度 */
  .assistant-message .message-content-wrapper.has-charts {
    max-width: 96%;
    width: 96%;
  }
}

/* 移动端文件预览适配 */
@media (max-width: 768px) {
  .file-preview-dialog {
    .el-dialog {
      width: 95% !important;
      margin: 2.5vh auto;
      max-height: 95vh;
      overflow: hidden;
    }
    
    .el-dialog__header {
      padding: 16px 20px;
    }
    
    .el-dialog__title {
      font-size: 16px;
    }
  }
  
  .preview-content {
    min-height: 500px;
    
    .pdf-preview {
      height: 500px;
    }
  }
}

/* Excel表格预览模态框样式 */
.excel-preview-dialog {
  .el-dialog {
    border-radius: 12px;
    overflow: hidden;
  }
  
  .el-dialog__header {
    background: linear-gradient(135deg, #22c55e 0%, #16a34a 100%);
    color: white;
    border-radius: 12px 12px 0 0;
    margin: 0;
    padding: 20px 24px;
  }
  
  .el-dialog__title {
    color: white;
    font-weight: 600;
    font-size: 18px;
  }
  
  .el-dialog__headerbtn {
    .el-dialog__close {
      color: white;
      font-size: 20px;
      transition: all 0.2s ease;
      
      &:hover {
        color: rgba(255, 255, 255, 0.8);
        transform: scale(1.1);
      }
    }
  }
  
  .el-dialog__body {
    padding: 0;
    background: #f5f7fa;
    max-height: 70vh;
    overflow-y: auto;
  }
}

.excel-preview-content {
  min-height: 400px;
  display: flex;
  align-items: flex-start;
  justify-content: center;
  background: #f5f7fa;
  border-radius: 0 0 12px 12px;
  padding: 20px;
  
  .excel-table-content {
    width: 100%;
    background: white;
    border-radius: 8px;
    padding: 20px;
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
    overflow-x: auto;
    
    /* 表格样式优化 - 加强边框显示，使用更高优先级 */
    :deep(table),
    :deep(.answer-content table),
    :deep(.table-container table) {
      width: 100% !important;
      border-collapse: collapse !important;
      margin: 0 !important;
      font-size: 14px !important;
      border: 2px solid #cbd5e1 !important;
      background: white !important;
      border-radius: 8px !important;
      overflow: hidden !important;
      box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08) !important;
      max-width: none !important;
    }
    
    :deep(th),
    :deep(td),
    :deep(.answer-content th),
    :deep(.answer-content td),
    :deep(.table-container th),
    :deep(.table-container td) {
      padding: 12px 16px !important;
      text-align: left !important;
      border: 1px solid #cbd5e1 !important;
      word-break: break-word !important;
      vertical-align: top !important;
      position: relative !important;
      max-width: none !important;
    }
    
    :deep(th),
    :deep(.answer-content th),
    :deep(.table-container th) {
      background: linear-gradient(135deg, #f1f5f9 0%, #e2e8f0 100%) !important;
      font-weight: 600 !important;
      color: #1e293b !important;
      position: sticky !important;
      top: 0 !important;
      z-index: 2 !important;
      border-bottom: 2px solid #94a3b8 !important;
      border-right: 1px solid #94a3b8 !important;
      font-size: 13px !important;
      text-transform: uppercase !important;
      letter-spacing: 0.025em !important;
    }
    
    :deep(td),
    :deep(.answer-content td),
    :deep(.table-container td) {
      background: white !important;
      border-right: 1px solid #cbd5e1 !important;
      border-bottom: 1px solid #cbd5e1 !important;
      transition: all 0.2s ease !important;
    }
    
    :deep(tr:nth-child(even) td),
    :deep(.answer-content tr:nth-child(even) td),
    :deep(.table-container tr:nth-child(even) td) {
      background: #f8fafc !important;
    }
    
    :deep(tr:hover td),
    :deep(.answer-content tr:hover td),
    :deep(.table-container tr:hover td) {
      background: #e0f2fe !important;
      transform: scale(1.002) !important;
    }
    
    /* 第一列样式加强 */
    :deep(th:first-child),
    :deep(td:first-child),
    :deep(.answer-content th:first-child),
    :deep(.answer-content td:first-child),
    :deep(.table-container th:first-child),
    :deep(.table-container td:first-child) {
      border-left: 2px solid #cbd5e1 !important;
      font-weight: 500 !important;
    }
    
    /* 最后一列右边框加强 */
    :deep(th:last-child),
    :deep(td:last-child),
    :deep(.answer-content th:last-child),
    :deep(.answer-content td:last-child),
    :deep(.table-container th:last-child),
    :deep(.table-container td:last-child) {
      border-right: 2px solid #cbd5e1 !important;
    }
    
    /* 最后一行底边框加强 */
    :deep(tr:last-child td),
    :deep(.answer-content tr:last-child td),
    :deep(.table-container tr:last-child td) {
      border-bottom: 2px solid #cbd5e1 !important;
    }

    /* Markdown 表格容器样式 */
    :deep(.table-container) {
      overflow-x: auto !important;
      margin: 1rem 0 !important;
      border-radius: 8px !important;
      box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1) !important;
      border: 1px solid #cbd5e1 !important;
      background: white !important;
    }

    /* 表格标题样式 */
    :deep(h1),
    :deep(h2),
    :deep(h3) {
      margin: 1.5rem 0 1rem !important;
      font-weight: 600 !important;
      color: #1e293b !important;
    }

    /* 段落间距 */
    :deep(p) {
      margin: 1rem 0 !important;
      color: #374151 !important;
      line-height: 1.6 !important;
    }
  }
}

/* 移动端适配 */
@media (max-width: 768px) {
  .excel-preview-dialog {
    .el-dialog {
      width: 95% !important;
      margin: 2.5vh auto;
    }
    
    .excel-table-content {
      padding: 12px;
      
      :deep(table) {
        font-size: 12px;
      }
      
      :deep(th),
      :deep(td) {
        padding: 8px 12px;
      }
    }
  }
}

/* Excel文件按钮样式 */
.excel-item {
  border: 1px solid #22c55e !important;
  background: linear-gradient(135deg, #dcfce7 0%, #bbf7d0 100%);
  
  &:hover {
    background: linear-gradient(135deg, #bbf7d0 0%, #86efac 100%);
    border-color: #16a34a !important;
    transform: translateY(-2px);
    box-shadow: 0 4px 12px rgba(34, 197, 94, 0.3);
  }
  
  .source-name {
    color: #15803d;
    font-weight: 600;
  }
}

.recommend-questions-section {
  margin-top: 1rem;
  padding: 0.75rem;
  background: #f8fafc;
  border: 1px solid #e2e8f0;
  border-radius: 8px;
  font-size: 0.875rem;
}

.recommend-questions-header {
  display: flex;
  align-items: center;
  color: #64748b;
  font-weight: 600;
  margin-bottom: 0.5rem;
  font-size: 0.8rem;
  text-transform: uppercase;
  letter-spacing: 0.05em;
}

.recommend-icon {
  width: 14px;
  height: 14px;
  margin-right: 0.5rem;
}

.recommend-questions-list {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
}

.recommend-question-btn {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.5rem 0.75rem;
  background: linear-gradient(135deg, #ffffff 0%, #f8fafc 100%);
  border: 1px solid #e2e8f0;
  border-radius: 20px;
  cursor: pointer;
  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
  font-size: 0.8rem;
  max-width: 280px;
  color: #374151;
  font-weight: 500;
  outline: none;
  position: relative;
  overflow: hidden;
  line-height: 1.4;
  min-width: 0;
}

.question-text {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  flex: 1;
  min-width: 0;
}

.recommend-question-btn::before {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background: linear-gradient(135deg, rgba(79, 70, 229, 0.1) 0%, rgba(67, 56, 202, 0.1) 100%);
  opacity: 0;
  transition: opacity 0.3s ease;
}

.recommend-question-btn:hover:not(:disabled) {
  background: linear-gradient(135deg, #f1f5f9 0%, #e2e8f0 100%);
  border-color: #4f46e5;
  transform: translateY(-2px);
  box-shadow: 0 4px 12px rgba(79, 70, 229, 0.2);
  color: #4f46e5;
}

.recommend-question-btn:hover:not(:disabled)::before {
  opacity: 1;
}

.recommend-question-btn:active:not(:disabled) {
  transform: translateY(-1px);
  box-shadow: 0 2px 8px rgba(79, 70, 229, 0.15);
}

.recommend-question-btn:disabled {
  opacity: 0.5;
  cursor: not-allowed;
  transform: none;
  box-shadow: none;
  background: #f8fafc;
  border-color: #e5e7eb;
  color: #9ca3af;
}

.recommend-question-btn:disabled::before {
  opacity: 0;
}

.question-icon {
  width: 16px;
  height: 16px;
  flex-shrink: 0;
  transition: all 0.3s ease;
  z-index: 1;
  position: relative;
  color: #64748b;
}

.recommend-question-btn:hover:not(:disabled) .question-icon {
  color: #4f46e5;
  transform: scale(1.1);
}

.recommend-question-btn:disabled .question-icon {
  color: #9ca3af;
}

/* 移动端适配 */
@media (max-width: 768px) {
  .recommend-questions-section {
    padding: 0.5rem;
    margin-top: 0.75rem;
  }
  
  .recommend-questions-list {
    gap: 0.375rem;
  }
  
  .recommend-question-btn {
    padding: 0.375rem 0.5rem;
    font-size: 0.75rem;
    max-width: 200px;
    border-radius: 16px;
  }
  
  .question-text {
    font-size: 0.75rem;
  }
  
  .question-icon {
    width: 14px;
    height: 14px;
  }
  
  .recommend-questions-header {
    font-size: 0.75rem;
    margin-bottom: 0.375rem;
  }
  
  .recommend-icon {
    width: 12px;
    height: 12px;
  }
}
</style>