<template>
  <div class="app-container">
    <!-- 主要内容区域 -->
    <div class="main-content">
      <!-- 左侧知识库列表 -->
      <div v-if="showLeftPanel" class="left-panel">
        <div class="panel-header">
          <div class="header-left">
            <h3>知识库列表</h3>
            <span class="knowledge-count">{{ knowledgeList.length }} 个知识库</span>
          </div>
          <el-button type="primary" size="small" @click="handleAdd" v-hasPermi="['llm:knowledge:add']"
            icon="el-icon-plus">
            新增知识库
          </el-button>
        </div>

        <!-- 搜索框 -->
        <div class="search-box">
          <el-input v-model="queryParams.collectionName" placeholder="请输入知识库名称" clearable @clear="handleQuery"
            @keyup.enter.native="handleQuery">
            <template slot="append">
              <el-button @click="handleQuery" icon="el-icon-search">
              </el-button>
            </template>
          </el-input>
        </div>

        <div class="knowledge-cards" v-loading="loading">
          <div v-for="(item, index) in knowledgeList" :key="index" class="knowledge-card"
            :class="{ 'active': (selectedKnowledge ? selectedKnowledge.collectionName : '') === item.collectionName }"
            @click="selectKnowledge(item)">
            <div class="card-header">
              <div class="card-title">
                <i class="el-icon-folder folder-icon">
                </i>
                <span class="title-text">{{ item.collectionName }}</span>
              </div>
              <div class="card-actions">
                <el-dropdown trigger="click" @command="handleCardAction">
                  <el-button type="text" size="small" icon="el-icon-more">
                  </el-button>
                  <el-dropdown-menu slot="dropdown">
                    <el-dropdown-item :command="{ action: 'edit', data: item }" icon="el-icon-edit">编辑</el-dropdown-item>
                    <el-dropdown-item :command="{ action: 'upload', data: item }"
                      icon="el-icon-upload">上传文件</el-dropdown-item>
                    <el-dropdown-item :command="{ action: 'delete', data: item }" icon="el-icon-delete"
                      divided>删除</el-dropdown-item>
                  </el-dropdown-menu>
                </el-dropdown>
              </div>
            </div>

            <div class="card-content">
              <p class="description">{{ item.description || '暂无描述' }}</p>
              <div class="meta-info">
                <span class="create-time">{{ parseTime(item.createdTime, '{y}-{m}-{d}') }}</span>
                <span class="file-count">{{ item.fileCount || 0 }} 个文件</span>
              </div>
            </div>
          </div>

          <!-- 空状态 - 无论是否搜索，当列表为空且未加载时都显示创建按钮 -->
          <div v-if="knowledgeList.length === 0 && !loading" class="empty-state">
            <i class="el-icon-folder-opened empty-icon"></i>
            <p>{{ queryParams.collectionName ? '搜索结果为空' : '暂无知识库' }}</p>
            <el-button type="primary" @click="handleAdd">{{ queryParams.collectionName ? '创建知识库' : '创建第一个知识库'
            }}</el-button>
          </div>
        </div>
      </div>

      <!-- 右侧面板 -->
      <div class="right-panel">
        <div class="panel-header">
          <div class="header-left">
            <el-button class="collapse-icon-btn" :icon="showLeftPanel ? 'el-icon-s-fold' : 'el-icon-s-unfold'" @click="showLeftPanel = !showLeftPanel" />
            <h3>{{ isChatMode ? '知识库对话' : (isScanMode ? '内容列表' : '文件列表') }}</h3>
          </div>
          <div class="header-actions">
            <el-button v-if="!isChatMode && !isScanMode" type="primary" icon="el-icon-chat-round" @click="handleChat(selectedKnowledge)"
              :disabled="!selectedKnowledge" v-hasPermi="['llm:knowledge:chat']">
              开始对话
            </el-button>
            <el-button v-if="!isChatMode && !isScanMode" type="primary" icon="el-icon-upload" @click="handleUpload(selectedKnowledge)"
              :disabled="!selectedKnowledge" v-hasPermi="['llm:knowledge:upload']">
              上传文件
            </el-button>
            <el-button v-if="!isChatMode && !isScanMode" type="default" icon="el-icon-notebook-2" @click="switchToTitleMode"
              :disabled="!selectedKnowledge">
              查看内容
            </el-button>
            <el-button v-if="isChatMode" type="default" icon="el-icon-document" @click="switchToFileMode">
              返回文件列表
            </el-button>
            <el-button v-if="isScanMode" type="default" icon="el-icon-document" @click="switchToFileMode">
              返回文件列表
            </el-button>
          </div>
        </div>

        <!-- 文件列表模式 -->
        <div v-if="!isChatMode && !isScanMode" class="file-content" v-loading="fileLoading">
          <div v-if="selectedKnowledge" class="selected-knowledge">
            <i class="el-icon-folder folder-icon">
            </i>
            <span class="knowledge-name">{{ selectedKnowledge.collectionName }}</span>
          </div>

          <div v-if="!selectedKnowledge" class="empty-state">
            <i class="el-icon-document empty-icon">
            </i>
            <p>请选择一个知识库查看文件</p>
          </div>

          <div v-else-if="fileList.length === 0" class="empty-state">
            <i class="el-icon-document empty-icon">
            </i>
            <p>该知识库暂无文件</p>
            <el-button type="primary" @click="handleUpload(selectedKnowledge)">上传文件</el-button>
          </div>

          <div v-else class="file-list">
            <div v-for="(file, index) in pagedFileList" :key="index" class="file-item" @click="handleFileClick(file)">
              <div class="file-info">
                <i class="el-icon-document file-icon">
                </i>
                <div class="file-details">
                  <div class="file-name">{{ file }}</div>
                  <div class="file-meta">
                    <span class="file-type">{{ getFileType(file) }}</span>
                  </div>
                </div>
              </div>
              <div class="file-actions">
                <el-button type="primary" size="small" icon="el-icon-view" @click.stop="handleFileClick(file)">
                  浏览
                </el-button>
                <el-button type="danger" size="small" icon="el-icon-delete" @click.stop="handleDeleteFile(file)">
                  删除
                </el-button>
              </div>
            </div>
          </div>

          <!-- 文件列表分页 -->
          <pagination v-show="fileTotal > 0" :total="fileTotal" :page.sync="filePageNum" :limit.sync="filePageSize"
            @pagination="handleFilePagination" :autoScroll="false" />
        </div>

        <!-- 内容列表模式 -->
        <div v-if="!isChatMode && isScanMode" class="title-content" v-loading="titleLoading">
          <div v-if="selectedKnowledge" class="selected-knowledge">
            <i class="el-icon-folder folder-icon">
            </i>
            <span class="knowledge-name">{{ selectedKnowledge.collectionName }}</span>
            <span v-if="currentFileName" class="file-name-badge">{{ currentFileName }}</span>
            <span class="title-mode-badge">浏览模式</span>
          </div>

          <div v-if="!selectedKnowledge" class="empty-state">
            <i class="el-icon-notebook-2 empty-icon">
            </i>
            <p>请选择一个知识库查看内容</p>
          </div>

          <div v-else-if="titleList.length === 0" class="empty-state">
            <i class="el-icon-notebook-2 empty-icon">
            </i>
            <p>该知识库暂无内容</p>
          </div>

          <div v-else class="title-content-container">
            <!-- 左侧标题列表 -->
            <div class="title-list">
              <div v-for="(title, index) in titleList" :key="index" class="title-item"
                :class="{ 'active': selectedTitle === title }"
                @click="selectTitle(title)">
                <i class="el-icon-notebook-2 title-icon"></i>
                <span class="title-text">{{ title }}</span>
              </div>
            </div>

            <!-- 右侧内容列表 -->
            <div class="content-list" v-loading="contentLoading">
              <div v-if="!selectedTitle" class="empty-state">
                <i class="el-icon-document empty-icon"></i>
                <p>请选择一个标题查看内容</p>
              </div>
              <div v-else-if="contentList.length === 0" class="empty-state">
                <i class="el-icon-document empty-icon"></i>
                <p>该标题暂无内容</p>
              </div>
              <div v-else class="content-items">
                <div v-for="(content, index) in contentList" :key="index" class="content-item">
                  <div class="content-header">
                    <span class="content-index">{{ index + 1 }}</span>
                    <span v-if="content.file_name" class="content-file-name">{{ content.file_name }}</span>
                  </div>
                  <div class="content-body">
                    {{ content.content }}
                  </div>
                </div>
              </div>
            </div>
          </div>
        </div>

        <!-- 聊天模式 -->
        <div v-if="selectedKnowledge && isChatMode" class="chat-content">
          <div class="selected-knowledge">
            <i class="el-icon-folder folder-icon">
            </i>
            <span class="knowledge-name">{{ selectedKnowledge.collectionName }}</span>
            <span class="chat-mode-badge">对话模式</span>
          </div>

          <!-- 聊天消息区域 -->
          <div class="chat-messages" ref="chatMessagesRef">
            <div v-if="chatMessages.length === 0" class="welcome-message">
              <div class="welcome-content">
                <div class="welcome-icon">🤖</div>
                <h3>知识库助手</h3>
                <p>我是基于「{{ selectedKnowledge ? selectedKnowledge.collectionName : "" }}」知识库的AI助手</p>
                <p>您可以询问关于该知识库内容的问题，我会为您提供准确的答案</p>
              </div>
            </div>

            <div v-else class="message-list">
              <div v-for="(message, index) in chatMessages" :key="index" class="message-item"
                :class="[message.type, { 'typing': message.type === 'ai' && isSending && index === chatMessages.length - 1 }]">
                <div class="message-avatar">
                  <div v-if="message.type === 'user'" class="user-avatar">
                    <i class="el-icon-user"></i>
                  </div>
                  <div v-else class="ai-avatar">
                    🤖
                  </div>
                </div>
                <div class="message-content">
                  <div class="message-bubble" :class="message.type">
                    <div class="message-text" v-html="formatMessage(message.content)"></div>

                    <!-- AI回答的引用文件信息 -->
                    <div v-if="message.type === 'ai' && message.references && message.references.length > 0"
                      class="message-references">
                      <div class="references-header">
                        <i class="el-icon-document reference-icon"></i>
                        <span>参考文件 ({{ message.references.length }})</span>
                        <el-button v-if="message.references.length > 3" type="text" size="small" class="toggle-references"
                          @click="toggleReferences(index)">
                          {{ message.showAllReferences ? '收起' : '展开全部' }}
                        </el-button>
                      </div>
                      <div class="references-list">
                        <div v-for="(ref, refIndex) in getDisplayedReferences(message)" :key="refIndex"
                          class="reference-item">
                          <div class="reference-info">
                            <span class="reference-filename" :title="ref.fileName">{{ ref.fileName }}</span>
                            <span class="reference-similarity" :class="getSimilarityClass(ref.similarity)">
                              相似度: {{ formatSimilarity(ref.similarity) }}%
                            </span>
                          </div>
                        </div>
                      </div>
                    </div>

                    <div class="message-time">{{ message.time }}</div>

                    <!-- Token用量统计 -->
                    <div v-if="message.type === 'ai' && message.tokenUsage"
                      class="token-usage">
                      <div class="token-info">
                        <span class="token-label">消耗Token:</span>
                        <span class="token-value">{{ message.tokenUsage.totalTokens || 0 }}</span>
                        <span class="token-breakdown">(输入: {{ message.tokenUsage.promptTokens || 0 }}, 输出: {{ message.tokenUsage.completionTokens || 0 }})</span>
                      </div>
                    </div>
                  </div>
                </div>
              </div>

              <!-- 注意：加载状态已整合到AI消息的typing样式中，不需要单独的加载div -->
            </div>
          </div>

          <!-- 输入区域 -->
          <div class="chat-input-area">
            <div class="input-container">
              <el-input v-model="chatInput" type="textarea" :rows="3"
                :placeholder="isSending ? 'AI正在思考中...' : '请输入您的问题...'" @keyup.enter.native="handleEnter"
                @keyup.ctrl.enter.native="handleCtrlEnter" :disabled="isSending" resize="none" />
              <div class="input-actions">
                <el-button v-if="!isSending" type="primary" icon="el-icon-promotion" @click="sendMessage"
                  :disabled="!chatInput.trim() || chatInput.length > 2000">
                  发送
                </el-button>
                <el-button v-else type="danger" icon="el-icon-delete" @click="stopGenerating">
                  停止生成
                </el-button>
              </div>
            </div>
            <div class="input-tips">
              <span>按 Enter 发送，Ctrl + Enter 换行</span>
              <span v-if="chatInput" class="char-count" :class="{ 'warning': chatInput.length > 1500 }">
                {{ chatInput.length }} 字符
                <span v-if="chatInput.length > 2000" class="error-text">(超出限制)</span>
              </span>
            </div>
          </div>
        </div>
      </div>
    </div>

    <!-- 添加或修改知识库对话框 -->
    <el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
      <el-form ref="knowledgeFormRef" :model="form" :rules="rules" label-width="120px">
        <el-form-item label="知识库名称" prop="collectionName">
          <el-input v-model="form.collectionName" placeholder="请输入知识库名称" :disabled="isModify" />
        </el-form-item>
        <el-form-item label="新知识库名称" prop="newCollectionName" v-if="isModify">
          <el-input v-model="form.newCollectionName" placeholder="请输入新知识库名称" />
        </el-form-item>
        <el-form-item label="描述" prop="description" v-if="!isModify">
          <el-input type="textarea" v-model="form.description" placeholder="请输入描述" />
        </el-form-item>
      </el-form>
      <div slot="footer" class="dialog-footer">
        <el-button type="primary" @click="submitForm">确 定</el-button>
        <el-button @click="cancel">取 消</el-button>
      </div>
    </el-dialog>

    <!-- 上传文件对话框 -->
    <el-dialog title="上传文件到知识库" :visible.sync="uploadOpen" width="500px" append-to-body>
      <el-form ref="uploadFormRef" :model="uploadForm" :rules="uploadRules" label-width="80px">
        <el-form-item label="知识库" prop="collectionName">
          <el-input v-model="uploadForm.collectionName" disabled />
        </el-form-item>
        <el-form-item label="选择文件" prop="file">
          <el-upload ref="knowledgeUpload" multiple accept=".docx,.doc,.pdf" :headers="upload.headers" :action="''"
            :disabled="upload.isUploading" :on-progress="handleFileUploadProgress" :on-success="handleFileSuccess"
            :auto-upload="false" drag :on-change="handleFileChange">
            <i class="el-icon-upload"></i>
            <div class="el-upload__text">将文件拖到此处，或<em>点击上传</em></div>
            <div slot="tip" class="el-upload__tip text-center">
              <div class="el-upload__tip">
                <el-checkbox v-model="upload.updateSupport" />
                是否更新已经存在的文件数据
              </div>
              <span>支持 .docx、.doc、.pdf 格式文件</span>
            </div>
          </el-upload>
        </el-form-item>
      </el-form>
      <div slot="footer" class="dialog-footer">
        <el-button type="primary" @click="submitUpload">确 定</el-button>
        <el-button @click="cancelUpload">取 消</el-button>
      </div>

      <div v-if="percentageDisplay">
        <el-progress :percentage="percentage" :stroke-width="25" :text-inside="true">
          <el-button text>{{ progress }}</el-button>
        </el-progress>
      </div>
    </el-dialog>
  </div>
</template>

<script>
import { parseTime } from "@/utils/ruoyi";
import { Message } from 'element-ui'
import { getToken } from "@/utils/auth";
import { listKnowledge, listKnowLedgeByCollectionName, addKnowledge, updateKnowledge, delKnowledge, insertKnowledgeFile, listKnowledgeDocument, deleteKnowledgeFile, getProcessValue, listTiles, listByTitle, listTilesByFile } from "@/api/llm/knowLedge";
import { getAnswer, getAnswerStream, getContextFile } from '@/api/llm/rag';
import { marked } from 'marked';

export default {
  name: 'KnowledgeManager',
  // 处理引用文件的显示状态
  data() {
    return {
      knowledgeList: [],
      showLeftPanel: true,
      open: false,
      uploadOpen: false,
      loading: true,
      fileLoading: false,
      percentage: 0,
      progress: "",
      percentageDisplay: false,
      showSearch: true,
      ids: [],
      single: true,
      multiple: true,
      total: 0,
      title: "",
      isModify: false,
      selectedKnowledge: null,
      fileList: [],

      // 文件分页相关状态
      fileTotal: 0,
      filePageNum: 1,
      filePageSize: 10,

      // 聊天相关状态
      isChatMode: false,
      chatMessages: [],
      chatInput: '',
      isSending: false,
      streamingStarted: false,
      chatMessagesRef: null,

      // 表单数据
      form: {
        collectionName: '',
        description: '',
        newCollectionName: ''
      },

      // 查询参数
      queryParams: {
        collectionName: ''
      },

      // 表单验证规则
      rules: {
        collectionName: [
          { required: true, message: "知识库名称不能为空", trigger: "blur" }
        ]
      },

      // 上传配置
      upload: {
        file: null,
        fileList: [],
        // 是否禁用上传
        isUploading: false,
        // 是否更新已经存在的文件数据
        updateSupport: true,
        // 设置上传的请求头部
        headers: { Authorization: "Bearer " + getToken() }
      },

      // 上传表单
      uploadForm: {
        collectionName: '',
        file: null
      },

      // 上传验证规则
      uploadRules: {
        file: [
          { required: true, message: "请选择要上传的文件", trigger: "blur" }
        ],
        collectionName: [
          { required: true, message: "请选择知识库", trigger: "blur" }
        ]
      },

      // 定时器
      timer: null,

      // 标题和内容相关状态
      isScanMode: false, // 是否显示浏览模式
      titleList: [], // 标题列表
      contentList: [], // 内容列表
      selectedTitle: null, // 选中的标题
      currentFileName: null, // 当前浏览的文件名
      titleLoading: false, // 标题加载状态
      contentLoading: false // 内容加载状态
    }
  },
  mounted() {
    // 重置状态，确保页面刷新后不会停留在对话模式或浏览模式
    this.isChatMode = false;
    this.isScanMode = false;
    this.chatMessages = [];
    this.chatInput = '';
    this.streamingStarted = false;
    this.titleList = [];
    this.contentList = [];
    this.selectedTitle = null;
    
    this.getList();
  },

  computed: {
    pagedFileList() {
      const start = (this.filePageNum - 1) * this.filePageSize;
      const end = start + this.filePageSize;
      return this.fileList.slice(start, end);
    }
  },

  // 生命周期钩子
  beforeDestroy() {
    // 组件卸载时清理请求控制器和超时定时器
    if (window.currentController) {
      window.currentController.abort();
      window.currentController = null;
    }
    if (window.responseTimeout) {
      clearTimeout(window.responseTimeout);
      window.responseTimeout = null;
    }
  },
  methods: {
    /** 查询知识库列表 */
    getList() {
      this.loading = true;
      // 将查询参数传递给API调用
      if (this.queryParams.collectionName == '') {
        listKnowledge().then(response => {
          // 确保返回的数据是数组格式
          if (Array.isArray(response.data)) {
            this.knowledgeList = response.data;
            console.log(this.knowledgeList);
            this.knowledgeList.map(item => {
              listKnowledgeDocument(item.collectionName).then(response => {
                item.fileCount = response.data.length;
              })
            })
          } else {
            this.knowledgeList = [];
          }
          this.loading = false;
        }).catch(error => {
          this.loading = false;
          this.$modal.msgError("获取知识库列表失败：" + error.message);
        });
      }
      else {
        listKnowLedgeByCollectionName({ collectionName: this.queryParams.collectionName }).then(response => {
          // 确保返回的数据是数组格式
          if (Array.isArray(response.data)) {
            this.knowledgeList = response.data;
            this.knowledgeList.map(item => {
              listKnowledgeDocument(item.collectionName).then(response => {
                item.fileCount = response.data.length;
              })
            })
          } else {
            this.knowledgeList = [];
          }
          this.loading = false;
        }).catch(error => {
          this.loading = false;
          this.$modal.msgError("获取知识库列表失败：" + error.message);
        });
      }
    },
    /** 选择知识库 */
    selectKnowledge(knowledge) {
      // 如果正在生成回答，先停止
      if (this.isSending) {
        this.stopGenerating();
      }

      this.selectedKnowledge = knowledge;
      // 重置聊天模式
      this.isChatMode = false;
      this.chatMessages = [];
      this.chatInput = '';
      this.streamingStarted = false;

      // 重置文件分页状态
      this.filePageNum = 1;
      this.filePageSize = 10;

      this.fileLoading = true;
      listKnowledgeDocument(knowledge.collectionName).then(response => {
        this.fileList = response.data;
        this.fileTotal = response.data.length;
        this.fileLoading = false;
      }).catch(error => {
        this.fileLoading = false;
        this.$modal.msgError("获取文件列表失败：" + error.message);
      });
    },

    /** 开始对话 */
    handleChat(knowledge) {
      if (!knowledge) {
        this.$modal.msgWarning("请先选择要对话的知识库");
        return;
      }
      // 如果正在生成回答，先停止
      if (this.isSending) {
        this.stopGenerating();
      }

      this.isChatMode = true;
      this.chatMessages = [];
      this.chatInput = '';
      this.streamingStarted = false;
      // 滚动到底部
      this.$nextTick(() => {
        this.scrollToBottom();
      });
    },

    /** 切换到文件模式 */
    switchToFileMode() {
      // 如果正在生成回答，先停止
      if (this.isSending) {
        this.stopGenerating();
      }

      this.isChatMode = false;
      this.isScanMode = false;
      this.currentFileName = null;
    },

    /** 切换到浏览模式 */
    switchToTitleMode(fileName) {
      this.isScanMode = true;
      this.isChatMode = false;
      this.titleList = [];
      this.contentList = [];
      this.selectedTitle = null;
      this.currentFileName = fileName;
      
      // 获取标题列表
      this.titleLoading = true;
      if (fileName) {
        // 使用新接口根据文件名获取标题列表
        listTilesByFile(this.selectedKnowledge.collectionName, fileName).then(response => {
          if (Array.isArray(response.data)) {
            this.titleList = response.data;
          } else {
            this.titleList = [];
          }
          this.titleLoading = false;
        }).catch(error => {
          this.titleLoading = false;
          this.$modal.msgError("获取标题列表失败：" + error.message);
        });
      } else {
        // 获取所有标题
        listTiles(this.selectedKnowledge.collectionName).then(response => {
          if (Array.isArray(response.data)) {
            this.titleList = response.data;
          } else {
            this.titleList = [];
          }
          this.titleLoading = false;
        }).catch(error => {
          this.titleLoading = false;
          this.$modal.msgError("获取标题列表失败：" + error.message);
        });
      }
    },
    
    /** 点击文件进入浏览模式 */
    handleFileClick(file) {
      this.switchToTitleMode(file);
    },

    /** 选择标题 */
    selectTitle(title) {
      this.selectedTitle = title;
      this.contentList = [];
      
      // 获取内容列表
      this.contentLoading = true;
      listByTitle(this.selectedKnowledge.collectionName, title).then(response => {
        if (Array.isArray(response.data)) {
          this.contentList = response.data;
        } else {
          this.contentList = [];
        }
        this.contentLoading = false;
      }).catch(error => {
        this.contentLoading = false;
        this.$modal.msgError("获取内容列表失败：" + error.message);
      });
    },

    /** 发送消息 */
    sendMessage() {
      if (!this.chatInput.trim()) return;

      // 如果正在发送，先停止当前请求
      if (this.isSending) {
        this.stopGenerating();
      }

      // 立即设置为发送状态，防止重复发送
      this.isSending = true;

      // 如果用户快速发送多条消息，取消之前的请求（放在发送新请求之前）
      if (window.currentController) {
        window.currentController.abort();
        window.currentController = null;
      }
      if (window.responseTimeout) {
        clearTimeout(window.responseTimeout);
        window.responseTimeout = null;
      }

      const userMessage = {
        type: 'user',
        content: this.chatInput.trim(),
        time: parseTime(new Date(), '{y}-{m}-{d} {h}:{i}:{s}')
      };

      this.chatMessages.push(userMessage);
      const currentInput = this.chatInput;
      this.chatInput = '';
      this.streamingStarted = false;

      // 滚动到底部
      this.$nextTick(() => {
        this.scrollToBottom();
      });

      // 创建AI消息占位符
      const aiMessage = {
        type: 'ai',
        content: '',
        time: parseTime(new Date(), '{y}-{m}-{d} {h}:{i}:{s}'),
        references: [], // 引用的文件信息
        showAllReferences: false, // 是否显示所有引用文件
        tokenUsage: {
          promptTokens: 0,
          completionTokens: 0,
          totalTokens: 0
        }
      };
      this.chatMessages.push(aiMessage);

      // 使用流式API获取回答
      const eventSource = getAnswerStream(
        currentInput,
        this.selectedKnowledge.collectionName,
        // onMessage: 接收到每个字符时的回调
        (jsonData, eventType) => {
          const that = this;
          if (!that.streamingStarted) that.streamingStarted = true;

          // 根据事件类型处理不同的响应
          if (eventType === 'error') {
            // 错误事件
            console.error('收到错误事件:', jsonData);
            if (jsonData.errorMessage) {
              aiMessage.content = jsonData.errorMessage;
            }
            return;
          }

          if (eventType === 'completed') {
            // 完成事件，更新最终的token用量
            if (jsonData.promptTokens !== undefined || jsonData.completionTokens !== undefined || jsonData.totalTokens !== undefined) {
              const index = that.chatMessages.length - 1;
              if (that.chatMessages[index] && that.chatMessages[index].tokenUsage) {
                that.$set(that.chatMessages[index].tokenUsage, 'promptTokens', jsonData.promptTokens || 0);
                that.$set(that.chatMessages[index].tokenUsage, 'completionTokens', jsonData.completionTokens || 0);
                that.$set(that.chatMessages[index].tokenUsage, 'totalTokens', jsonData.totalTokens || 0);
              }
            }
            return;
          }

          // 默认处理 answer 事件
          if (!jsonData) {
            return;
          }

          // 更新token用量
          if (jsonData.promptTokens !== undefined || jsonData.completionTokens !== undefined || jsonData.totalTokens !== undefined) {
            const index = that.chatMessages.length - 1;
            if (that.chatMessages[index] && that.chatMessages[index].tokenUsage) {
              that.$set(that.chatMessages[index].tokenUsage, 'promptTokens', jsonData.promptTokens || that.chatMessages[index].tokenUsage.promptTokens);
              that.$set(that.chatMessages[index].tokenUsage, 'completionTokens', jsonData.completionTokens || that.chatMessages[index].tokenUsage.completionTokens);
              that.$set(that.chatMessages[index].tokenUsage, 'totalTokens', jsonData.totalTokens || that.chatMessages[index].tokenUsage.totalTokens);
            }
          }

          // 获取内容
          const content = jsonData.content;
          if (content && content !== '[DONE]') {
            // 直接追加内容，不使用打字机效果
            aiMessage.content += String(content);
            aiMessage.time = parseTime(new Date(), '{y}-{m}-{d} {h}:{i}:{s}');
            that.$nextTick(() => that.scrollToBottom());
          }

          if (window.responseTimeout) {
            clearTimeout(window.responseTimeout);
          }
          window.responseTimeout = setTimeout(() => {
            if (that.isSending) {
              console.log('=== 响应超时强制结束 ===');
              that.isSending = false;
              if (window.responseTimeout) {
                clearTimeout(window.responseTimeout);
                window.responseTimeout = null;
              }
            }
          }, 300000);

        },
        // onError: 发生错误时的回调
        (error) => {
          const that = this;
          console.error('=== 流式回答错误 ===', error);

          if (window.responseTimeout) {
            clearTimeout(window.responseTimeout);
            window.responseTimeout = null;
          }

          if (aiMessage.content === '') {
            aiMessage.content = '抱歉，我暂时无法回答您的问题，请稍后再试。';
          } else {
            aiMessage.content += '\n\n[回答生成中断]';
          }
          that.isSending = false;

          that.$nextTick(() => {
            that.scrollToBottom();
          });
        },
        // onComplete: 回答完成时的回调
        async () => {
          const that = this;
          console.log('=== 回答完成 ===');

          if (window.responseTimeout) {
            clearTimeout(window.responseTimeout);
            window.responseTimeout = null;
          }

          try {
            // 获取上下文引用文件
            const response = await getContextFile(currentInput, that.selectedKnowledge.collectionName);
            console.log('=== 上下文文件 ===', response)
            if (response && Array.isArray(response)) {
              aiMessage.references = response.map(item => ({
                fileName: item.file_name,
                similarity: item.score,
                content: item.content
              }));
              that.$forceUpdate(); // 强制更新以显示引用文件
            }
          } catch (error) {
            console.error('获取上下文文件失败:', error);
          }

          that.isSending = false;
          that.$nextTick(() => {
            that.scrollToBottom();
          });
        }
      );

      window.currentController = eventSource;
    },

    // 设置初始超时定时器（30秒无任何响应才超时）
    initResponseTimeout() {
      window.responseTimeout = setTimeout(() => {
        if (this.isSending) {
          console.log('=== 初始连接超时强制结束 ===')
          this.isSending = false;
          if (window.currentController) {
            window.currentController.abort();
            window.currentController = null;
          }
          window.responseTimeout = null;
        }
      }, 300000); // 5分钟超时
    },

    /** 停止生成回答 */
    stopGenerating() {
      // 清除超时定时器
      if (window.responseTimeout) {
        clearTimeout(window.responseTimeout);
        window.responseTimeout = null;
      }

      if (window.currentController) {
        window.currentController.abort();
        window.currentController = null;
      }

      // 如果最后一条消息是空的AI消息，移除它（防止重复消息）
      const lastMessage = this.chatMessages[this.chatMessages.length - 1];
      if (lastMessage && lastMessage.type === 'ai' && !lastMessage.content) {
        this.chatMessages.pop();
      }

      this.isSending = false;
    },

    /** 滚动到底部 */
    scrollToBottom() {
      // 确保在 DOM 更新后执行滚动操作
      this.$nextTick(() => {
        const container = this.$refs.chatMessagesRef;
        if (container) {
          container.scrollTop = container.scrollHeight;
        }
      });
    },

    /** 处理Enter键 */
    handleEnter(event) {
      if (!event.shiftKey) {
        event.preventDefault();
        this.sendMessage();
      }
    },

    /** 处理Ctrl+Enter键 */
    handleCtrlEnter() {
      this.chatInput += '\n';
    },

    /** 格式化消息内容 */
    formatMessage(content) {
      // 直接使用marked解析markdown内容
      return marked(content);
    },

    /** 格式化相似度 */
    formatSimilarity(similarity) {
      // 如果相似度已经是百分比形式(>1)，直接返回
      if (similarity > 1) {
        return similarity.toFixed(1);
      }
      // 如果是0-1之间的值，转换为百分比
      return (similarity * 100).toFixed(1);
    },

    /** 获取相似度的CSS类 */
    getSimilarityClass(similarity) {
      const percentage = similarity > 1 ? similarity : similarity * 100;
      if (percentage >= 80) return 'high-similarity';
      if (percentage >= 60) return 'medium-similarity';
      return 'low-similarity';
    },

    /** 切换引用文件的展开/收起状态 */
    toggleReferences(messageIndex) {
      const message = this.chatMessages[messageIndex];
      message.showAllReferences = !message.showAllReferences;
      this.$forceUpdate();
    },

    /** 获取要显示的引用文件列表 */
    getDisplayedReferences(message) {
      if (!message.references || message.references.length === 0) {
        return [];
      }

      // 如果引用文件数量 <= 3 或者设置为显示全部，则显示所有
      if (message.references.length <= 3 || message.showAllReferences) {
        return message.references;
      }

      // 否则只显示前3个
      return message.references.slice(0, 3);
    },

    /** 处理卡片操作 */
    handleCardAction(command) {
      const { action, data } = command;
      switch (action) {
        case 'edit':
          this.handleUpdate(data);
          break;
        case 'upload':
          this.handleUpload(data);
          break;
        case 'delete':
          this.handleDelete(data);
          break;
      }
    },

    /** 删除文件 */
    handleDeleteFile(file) {
      this.$modal.confirm(`是否确认删除文件"${file}"？`).then(() => {
        deleteKnowledgeFile(file, this.selectedKnowledge.collectionName).then(response => {
          this.$modal.msgSuccess("删除成功");
          // 刷新文件列表
          if (this.selectedKnowledge) {
            this.fileLoading = true;
            listKnowledgeDocument(this.selectedKnowledge.collectionName).then(response => {
              this.fileList = response.data;
              this.fileTotal = response.data.length;

              // 如果当前页没有数据了，回到上一页
              const maxPage = Math.ceil(this.fileTotal / this.filePageSize);
              if (this.filePageNum > maxPage && maxPage > 0) {
                this.filePageNum = maxPage;
              }

              this.fileLoading = false;
            }).catch(error => {
              this.fileLoading = false;
              this.$modal.msgError("刷新文件列表失败：" + error.message);
            });
          }
        }).catch(error => {
          this.$modal.msgError("删除失败：" + error.message);
        });
      }).catch(() => { });
    },

    /** 获取文件类型 */
    getFileType(fileName) {
      if (!fileName) return '未知';
      const extension = fileName.split('.').pop()?.toLowerCase();
      switch (extension) {
        case 'pdf':
          return 'PDF文档';
        case 'docx':
          return 'Word文档';
        case 'doc':
          return 'Word文档';
        default:
          return '文档';
      }
    },

    /** 文件分页处理 */
    handleFilePagination() {
      this.fileLoading = true;
      listKnowledgeDocument(this.selectedKnowledge.collectionName).then(response => {
        this.fileList = response.data;
        this.fileTotal = response.data.length;
        this.fileLoading = false;
      }).catch(error => {
        this.fileLoading = false;
        this.$modal.msgError("获取文件列表失败：" + error.message);
      });
    },

    // 取消按钮
    cancel() {
      this.open = false;
      this.reset();
    },

    // 表单重置
    reset() {
      this.form.collectionName = '';
      this.form.description = '';
      this.form.newCollectionName = '';
      // 强制更新DOM
      this.$nextTick(() => {
        this.$refs.form.resetFields();
      });
    },

    /** 重置表单 */
    resetForm(refName) {
      if (this.$refs[refName]) {
        this.$refs[refName].resetFields();
      }
    },
    /** 搜索按钮操作 */
    handleQuery() {
      // 重置页码
      this.queryParams.pageNum = 1;
      this.getList();
    },

    /** 重置按钮操作 */
    resetQuery() {
      this.resetForm("queryForm");
      this.handleQuery();
    },

    /** 新增按钮操作 */
    handleAdd() {
      this.reset();
      this.open = true;
      this.title = "添加知识库";
      this.isModify = false;
    },

    /** 修改按钮操作 */
    handleUpdate(row) {
      this.reset();
      const collectionName = row?.collectionName || this.ids[0];
      if (!collectionName) {
        this.$modal.msgWarning("请先选择要修改的知识库");
        return;
      }
      this.form.collectionName = collectionName;
      this.form.newCollectionName = '';
      this.isModify = true;
      this.open = true;
      this.title = "修改知识库";
    },

    /** 提交按钮 */
    submitForm() {
      this.$refs["knowledgeFormRef"].validate(valid => {
        if (valid) {
          if (this.form.newCollectionName) {
            // 修改操作
            updateKnowledge(this.form.collectionName, this.form.newCollectionName).then(response => {
              this.$modal.msgSuccess("修改成功");
              this.open = false;
              this.getList();
            }).catch(error => {
              this.$modal.msgError("修改失败：" + error.message);
            });
          } else {
            // 新增操作
            addKnowledge(this.form.collectionName, this.form.description).then(response => {
              this.$modal.msgSuccess("新增成功");
              this.open = false;
              this.getList();
            }).catch(error => {
              this.$modal.msgError("新增失败：" + error.message);
            });
          }
        }
      });
    },

    /** 删除按钮操作 */
    handleDelete(row) {
      const collectionNames = row?.collectionName || this.ids;
      if (!collectionNames) {
        this.$modal.msgWarning("请先选择要删除的知识库");
        return;
      }
      this.$modal.confirm('是否确认删除知识库名称为"' + collectionNames + '"的数据项？').then(() => {
        return delKnowledge(collectionNames);
      }).then(() => {
        this.getList();
        // 如果删除的是当前选中的知识库，清空选择
        if (this.selectedKnowledge?.collectionName === collectionNames) {
          // 如果正在生成回答，先停止
          if (this.isSending) {
            this.stopGenerating();
          }
          this.selectedKnowledge = null;
          this.fileList = [];
          this.isChatMode = false;
          this.chatMessages = [];
        }
        this.$modal.msgSuccess("删除成功");
      }).catch((error) => {
        if (error !== 'cancel') {
          this.$modal.msgError("删除失败：" + error.message);
        }
      });
    },

    /** 上传文件按钮操作 */
    handleUpload(row) {
      this.percentage = 0;
      this.percentageDisplay = false;
      const collectionName = row?.collectionName;
      if (!collectionName) {
        this.$modal.msgWarning("请先选择要上传文件的知识库");
        return;
      }
      this.uploadForm.collectionName = collectionName;
      this.uploadOpen = true;
    },

    // 文件上传中处理
    handleFileUploadProgress(event, file, fileList) {
      this.upload.isUploading = true;
    },

    handleFileChange(file, fileList) {
      this.upload.fileList = fileList.map(f => f.raw)
    },

    // 文件上传成功处理
    handleFileSuccess(response, file, fileList) {
      this.upload.isUploading = false;
      this.$refs.upload.clearFiles();
      this.$alert(response.msg, "导入结果", { dangerouslyUseHTMLString: true });
      this.uploadOpen = false;
      this.getList();
      // 如果当前有选中的知识库，刷新文件列表
      if (this.selectedKnowledge) {
        this.fileLoading = true;
        listKnowledgeDocument(this.selectedKnowledge.collectionName).then((response) => {
          this.fileList = response.data;
          this.fileTotal = response.data.length;
          this.fileLoading = false;
        }).catch((error) => {
          this.fileLoading = false;
          this.$modal.msgError("刷新文件列表失败：" + error.message);
        });
      }
    },

    // 提交上传
    submitUpload() {
      try {
        this.percentageDisplay = true;
        var timer;
        clearInterval(timer);
        this.getProcess()
      } catch (error) {
        console.error('文件上传失败:', error)
        this.$message.error('文件上传失败')
      }
    },

    getProcess() {
      var timer = setInterval(() => { //隔2000毫秒获取进度
        getProcessValue().then((res) => {
          if (res.code == 200 && res.msg.includes(":")) {
            this.progress = res.msg;
            this.percentage = Number(res.msg.split(":")[1].replace("%", ""))
          }
        });
      }, 30000);

      insertKnowledgeFile(this.upload.fileList, this.uploadForm.collectionName).then((response) => {
        this.percentageDisplay = false;
        this.$modal.msgSuccess("上传成功");
        this.uploadOpen = false;
        this.$refs.knowledgeUpload.clearFiles();
        this.getList();
      }).catch((error) => {
        this.$modal.msgError("上传失败：" + error.message);
      });
    },

    // 取消上传
    cancelUpload() {
      this.uploadOpen = false;
      this.$refs.knowledgeUpload.clearFiles();
    },

  }

}
</script>

<style lang="scss" scoped>
.app-container {
  height: 100vh;
  display: flex;
  flex-direction: column;
  background: #f5f7fa;
}

.main-content {
  flex: 1;
  display: flex;
  gap: 16px;
  padding: 16px;
  overflow: hidden;
}

.left-panel {
  width: 20%;
  background: white;
  border-radius: 8px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

.right-panel {
  flex: 1;
  background: white;
  border-radius: 8px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

.panel-header {
  padding: 20px 24px;
  border-bottom: 1px solid #e4e7ed;
  display: flex;
  justify-content: space-between;
  align-items: center;
  background: #fafbfc;

  .header-left {
    display: flex;
    align-items: center;
    gap: 8px;
  }

  .collapse-icon-btn {
    padding: 0;
    width: 24px;
    height: 24px;
    border: none;
    background: transparent;
    color: #6c757d;
    transition: all 0.3s;

    &:hover {
      background: #e9ecef;
      color: #495057;
    }
  }

  h3 {
    margin: 0;
    font-size: 16px;
    font-weight: 600;
    color: #303133;
  }

  .knowledge-count {
    font-size: 12px;
    color: #909399;
    background: #f0f2f5;
    padding: 4px 8px;
    border-radius: 4px;
  }

  .search-box {
    padding: 16px;
    border-bottom: 1px solid #e4e4e4;
  }

  .header-actions {
    display: flex;
    gap: 8px;
  }
}

.knowledge-cards {
  flex: 1;
  padding: 16px;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.knowledge-card {
  background: white;
  border: 1px solid #e4e7ed;
  border-radius: 8px;
  padding: 16px;
  cursor: pointer;
  transition: all 0.3s ease;
  position: relative;

  &:hover {
    border-color: #409eff;
    box-shadow: 0 4px 12px rgba(64, 158, 255, 0.15);
    transform: translateY(-2px);
  }

  &.active {
    border-color: #409eff;
    background: #f0f9ff;
    box-shadow: 0 4px 12px rgba(64, 158, 255, 0.2);
  }

  .card-header {
    display: flex;
    justify-content: space-between;
    align-items: flex-start;
    margin-bottom: 12px;

    .card-title {
      display: flex;
      align-items: center;
      gap: 8px;
      flex: 1;

      .folder-icon {
        color: #409eff;
        font-size: 18px;
      }

      .title-text {
        font-weight: 600;
        color: #303133;
        font-size: 14px;
        line-height: 1.4;
      }
    }

    .card-actions {
      opacity: 0;
      transition: opacity 0.3s ease;
    }
  }

  &:hover .card-actions {
    opacity: 1;
  }

  .card-content {
    .description {
      color: #606266;
      font-size: 13px;
      line-height: 1.5;
      margin: 0 0 12px 0;
      display: -webkit-box;
      -webkit-line-clamp: 2;
      -webkit-box-orient: vertical;
      overflow: hidden;
    }

    .meta-info {
      display: flex;
      justify-content: space-between;
      align-items: center;
      font-size: 12px;
      color: #909399;

      .create-time {
        background: #f0f2f5;
        padding: 2px 6px;
        border-radius: 3px;
      }

      .file-count {
        background: #e1f3d8;
        color: #67c23a;
        padding: 2px 6px;
        border-radius: 3px;
      }
    }
  }
}

.file-content {
  flex: 1;
  padding: 20px;
  overflow-y: auto;
}

.title-content {
  flex: 1;
  padding: 20px;
  overflow: hidden;
}

.title-content-container {
  display: flex;
  height: calc(100vh - 240px);
  gap: 20px;
  overflow: hidden;
}

.title-list {
  width: 300px;
  background: #fafbfc;
  border-radius: 8px;
  border: 1px solid #e4e7ed;
  padding: 12px;
  overflow-y: auto;
}

.title-item {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 10px 12px;
  margin-bottom: 8px;
  border-radius: 6px;
  cursor: pointer;
  transition: all 0.3s ease;
  background: white;
  border: 1px solid #e4e7ed;

  &:hover {
    border-color: #409eff;
    background: #f0f9ff;
  }

  &.active {
    border-color: #409eff;
    background: #e6f7ff;
    box-shadow: 0 2px 8px rgba(64, 158, 255, 0.15);
  }

  .title-icon {
    color: #409eff;
    font-size: 16px;
  }

  .title-text {
    flex: 1;
    font-size: 14px;
    color: #303133;
    line-height: 1.4;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
}

.content-list {
  flex: 1;
  background: #fafbfc;
  border-radius: 8px;
  border: 1px solid #e4e7ed;
  padding: 20px;
  overflow-y: auto;
}

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

.content-item {
  background: white;
  border: 1px solid #e4e7ed;
  border-radius: 8px;
  padding: 16px;
  transition: all 0.3s ease;

  &:hover {
    border-color: #409eff;
    box-shadow: 0 2px 8px rgba(64, 158, 255, 0.15);
  }

  .content-header {
    display: flex;
    align-items: center;
    gap: 12px;
    margin-bottom: 12px;

    .content-index {
      background: #409eff;
      color: white;
      width: 24px;
      height: 24px;
      border-radius: 50%;
      display: flex;
      align-items: center;
      justify-content: center;
      font-size: 12px;
      font-weight: 600;
    }

    .content-file-name {
      font-size: 12px;
      color: #909399;
      background: #f0f2f5;
      padding: 4px 8px;
      border-radius: 4px;
      flex: 1;
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
    }
  }

  .content-body {
    font-size: 14px;
    color: #303133;
    line-height: 1.6;
    white-space: pre-wrap;
    word-break: break-word;
  }
}

.title-mode-badge {
  background: #67c23a;
  color: white;
  padding: 2px 8px;
  border-radius: 12px;
  font-size: 12px;
  margin-left: auto;
}

// 聊天模式样式
.chat-content {
  flex: 1;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

.selected-knowledge {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 12px 16px;
  background: #f0f9ff;
  border-radius: 6px;
  margin-bottom: 20px;

  .folder-icon {
    color: #409eff;
    font-size: 16px;
  }

  .knowledge-name {
    font-weight: 600;
    color: #303133;
  }

  .chat-mode-badge {
    background: #409eff;
    color: white;
    padding: 2px 8px;
    border-radius: 12px;
    font-size: 12px;
    margin-left: auto;
  }
}

.chat-messages {
  flex: 1;
  padding: 20px;
  overflow-y: auto;
  background: #fafbfc;
}

.welcome-message {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 100%;
  text-align: center;

  .welcome-content {
    max-width: 400px;

    .welcome-icon {
      font-size: 48px;
      margin-bottom: 16px;
    }

    h3 {
      margin: 0 0 12px 0;
      color: #303133;
      font-size: 18px;
    }

    p {
      margin: 0 0 8px 0;
      color: #606266;
      font-size: 14px;
      line-height: 1.6;
    }
  }
}

.message-list {
  display: flex;
  flex-direction: column;
  gap: 20px;
}

.message-item {
  display: flex;
  gap: 12px;

  &.user {
    flex-direction: row-reverse;

    .message-content {
      align-items: flex-end;
    }

    .message-bubble {
      background: #409eff;
      color: white;
      border-radius: 18px 18px 4px 18px;
    }
  }

  &.ai {
    .message-bubble {
      background: white;
      color: #303133;
      border: 1px solid #e4e7ed;
      border-radius: 18px 18px 18px 4px;
    }
  }
}

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

  .user-avatar {
    // background: #409eff;
    color: #409eff;
    font-size: 18px;
  }

  .ai-avatar {
    // background: #67c23a;
    color: white;
    font-size: 18px;
  }
}

.message-content {
  display: flex;
  flex-direction: column;
  max-width: 70%;
}

.message-bubble {
  padding: 12px 16px;
  line-height: 1.5;
  word-wrap: break-word;

  .message-text {
    margin-bottom: 8px;
    white-space: pre-wrap;
  }

  .message-references {
    margin: 12px 0;
    padding: 12px;
    background: rgba(0, 0, 0, 0.05);
    border-radius: 6px;
    border-left: 3px solid #409eff;

    .references-header {
      display: flex;
      align-items: center;
      justify-content: space-between;
      gap: 6px;
      margin-bottom: 8px;
      font-size: 13px;
      font-weight: 600;
      color: #409eff;

      .reference-icon {
        font-size: 14px;
      }

      .toggle-references {
        font-size: 11px;
        color: #909399;
        padding: 0;
        height: auto;

        &:hover {
          color: #409eff;
        }
      }
    }

    .references-list {
      display: flex;
      flex-direction: column;
      gap: 6px;
    }

    .reference-item {
      padding: 6px 8px;
      background: rgba(255, 255, 255, 0.8);
      border-radius: 4px;
      border: 1px solid rgba(0, 0, 0, 0.1);

      .reference-info {
        display: flex;
        justify-content: space-between;
        align-items: center;
        gap: 8px;

        .reference-filename {
          font-size: 12px;
          color: #303133;
          font-weight: 500;
          flex: 1;
          overflow: hidden;
          text-overflow: ellipsis;
          white-space: nowrap;
        }

        .reference-similarity {
          font-size: 11px;
          padding: 2px 6px;
          border-radius: 10px;
          white-space: nowrap;
          font-weight: 500;
          transition: all 0.3s ease;

          &.high-similarity {
            color: #67c23a;
            background: #e1f3d8;
          }

          &.medium-similarity {
            color: #e6a23c;
            background: #fdf5e6;
          }

          &.low-similarity {
            color: #f56c6c;
            background: #fef0f0;
          }
        }
      }
    }
  }

  .message-time {
    font-size: 12px;
    opacity: 0.7;
  }

  .token-usage {
    margin-top: 8px;
    padding-top: 8px;
    border-top: 1px dashed #e0e0e0;
  }

  .token-info {
    display: flex;
    align-items: center;
    font-size: 12px;
    color: #666;
  }

  .token-label {
    margin-right: 4px;
    color: #999;
  }

  .token-value {
    font-weight: 600;
    color: #409eff;
    margin-right: 8px;
  }

  .token-breakdown {
    color: #999;
    font-size: 11px;
  }
}

// 打字机效果的光标 - 只在正在生成且有内容时显示
.message-bubble.ai.typing .message-text:after {
  content: '|';
  animation: blink 1s infinite;
  color: #409eff;
}

// 加载状态：内容为空时显示加载动画
.message-bubble.ai.typing:empty::before {
  content: '';
  display: inline-block;
  width: 60px;
  height: 20px;
  background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 60 20'%3E%3Ccircle cx='10' cy='10' r='4' fill='%23409eff'%3E%3Canimate attributeName='opacity' values='1;0.3;1' dur='1s' repeatCount='indefinite'/%3E%3C/circle%3E%3Ccircle cx='30' cy='10' r='4' fill='%23409eff'%3E%3Canimate attributeName='opacity' values='0.3;1;0.3' dur='1s' begin='0.2s' repeatCount='indefinite'/%3E%3C/circle%3E%3Ccircle cx='50' cy='10' r='4' fill='%23409eff'%3E%3Canimate attributeName='opacity' values='0.3;1;0.3' dur='1s' begin='0.4s' repeatCount='indefinite'/%3E%3C/circle%3E%3C/svg%3E") no-repeat center;
  background-size: contain;
}

.message-bubble.ai.typing:empty .message-text {
  display: none;
}

@keyframes blink {

  0%,
  50% {
    opacity: 1;
  }

  51%,
  100% {
    opacity: 0;
  }
}

.chat-input-area {
  padding: 20px;
  border-top: 1px solid #e4e7ed;
  background: white;
}

.input-container {
  display: flex;
  gap: 12px;
  align-items: flex-end;

  .el-textarea {
    flex: 1;
  }

  .input-actions {
    flex-shrink: 0;
  }
}

.input-tips {
  margin-top: 8px;
  text-align: center;
  font-size: 12px;
  color: #999;
  display: flex;
  justify-content: space-between;
  align-items: center;

  .char-count {
    color: #409eff;
    font-weight: 500;

    &.warning {
      color: #e6a23c;
    }

    .error-text {
      color: #f56c6c;
      font-size: 11px;
    }
  }
}

.file-list {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.file-item {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 16px;
  background: #fafbfc;
  border: 1px solid #e4e7ed;
  border-radius: 6px;
  transition: all 0.3s ease;

  &:hover {
    border-color: #409eff;
    box-shadow: 0 2px 8px rgba(64, 158, 255, 0.1);
  }

  .file-info {
    display: flex;
    align-items: center;
    gap: 12px;
    flex: 1;

    .file-icon {
      color: #409eff;
      font-size: 20px;
    }

    .file-details {
      .file-name {
        font-weight: 500;
        color: #303133;
        margin-bottom: 4px;
      }

      .file-meta {
        display: flex;
        gap: 12px;
        font-size: 12px;
        color: #909399;

        .file-type {
          background: #e1f3d8;
          color: #67c23a;
          padding: 2px 6px;
          border-radius: 3px;
        }
      }
    }
  }

  .file-actions {
    opacity: 0;
    transition: opacity 0.3s ease;
  }

  &:hover .file-actions {
    opacity: 1;
  }
}

// 文件列表分页样式
.file-content {
  :deep(.pagination-container) {
    margin-top: 20px;
    padding: 20px 0;
    text-align: center;
    background: transparent;
  }
}

.empty-state {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 60px 20px;
  text-align: center;
  color: #909399;

  .empty-icon {
    font-size: 48px;
    margin-bottom: 16px;
    opacity: 0.5;
  }

  p {
    margin: 0 0 16px 0;
    font-size: 14px;
  }
}

.dialog-footer {
  text-align: right;
}

.el-upload__tip {
  font-size: 12px;
  color: #606266;
  margin-top: 7px;
}

:deep(.el-upload-dragger) {
  width: 350px;
}

:deep(.el-upload__text) {
  margin: 10px 0 16px;
  color: #606266;
  font-size: 14px;
}

:deep(.el-upload__tip) {
  font-size: 12px;
  color: #606266;
  margin-top: 7px;
}

// 修复上传文件列表中文件名过长的问题
:deep(.el-upload-list) {
  max-width: 100%;

  .el-upload-list__item {
    max-width: 100%;
    overflow: hidden;

    .el-upload-list__item-name {
      width: 350px; // 为删除按钮等留出空间
      white-space: nowrap;
      overflow: hidden;
      text-overflow: ellipsis;
      display: inline-block;
      vertical-align: middle;
    }
  }
}

// 修复拖拽区域内文件名显示问题
:deep(.el-upload-dragger) {
  .el-upload-list {
    .el-upload-list__item {
      margin: 0 10px 10px 10px;
      padding: 8px 12px;
      background: #f5f7fa;
      border-radius: 4px;

      .el-upload-list__item-name {
        color: #606266;
        font-size: 13px;
        max-width: calc(100% - 60px);
        word-wrap: break-word;
        word-break: break-all;
        white-space: normal;
        line-height: 1.4;
      }
    }
  }
}

// 响应式设计
@media (max-width: 1200px) {
  .main-content {
    flex-direction: column;
  }

  .left-panel {
    width: 100%;
    height: 300px;
  }
}

@media (max-width: 768px) {
  .top-toolbar {
    padding: 12px 16px;
  }

  .main-content {
    padding: 12px;
    gap: 12px;
  }

  .panel-header {
    padding: 16px 20px;
  }

  .knowledge-cards {
    padding: 12px;
  }

  .file-content {
    padding: 16px;
  }

  .chat-messages {
    padding: 16px;
  }

  .chat-input-area {
    padding: 16px;
  }

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

  .message-references {
    .references-header {
      flex-direction: column;
      align-items: flex-start;
      gap: 4px;

      .toggle-references {
        font-size: 10px;
      }
    }

    .reference-item {
      .reference-info {
        flex-direction: column;
        align-items: flex-start;
        gap: 4px;

        .reference-filename {
          font-size: 11px;
        }

        .reference-similarity {
          font-size: 10px;
          align-self: flex-end;
        }
      }
    }
  }
}

// Loading动画样式
.loading-dots {
  display: flex;
  gap: 4px;
  padding: 8px 0;

  span {
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background-color: #ccc;
    animation: loading 1.4s infinite ease-in-out;

    &:nth-child(1) {
      animation-delay: -0.32s;
    }

    &:nth-child(2) {
      animation-delay: -0.16s;
    }
  }
}

@keyframes loading {

  0%,
  80%,
  100% {
    transform: scale(0);
  }

  40% {
    transform: scale(1);
  }
}
</style>