|
@@ -1,131 +1,551 @@
|
|
|
<template>
|
|
|
- <div class="container">
|
|
|
- <!-- 左侧知识库列表 -->
|
|
|
- <div class="knowledge-base">
|
|
|
- <h3>选择知识库</h3>
|
|
|
- <div
|
|
|
- v-for="item in knowledgeBaseList"
|
|
|
- :key="item.dataset_id"
|
|
|
- :class="['knowledge-item', { 'active': selectedDatasetIds.includes(item.dataset_id) }]"
|
|
|
- @click="toggleDatasetSelection(item.dataset_id)"
|
|
|
- >
|
|
|
- {{ item.name }}
|
|
|
+ <div class="app-container">
|
|
|
+ <!-- 顶部导航 -->
|
|
|
+ <div class="app-header">
|
|
|
+ <div class="header-content">
|
|
|
+ <div class="header-main">
|
|
|
+ <h1 class="app-title">智能知识库助手</h1>
|
|
|
+ <p style="color: rgba(255, 255, 255, 0.8);
|
|
|
+ font-size: 1rem;
|
|
|
+ margin: 8px 0 0 0;
|
|
|
+ font-weight: 500;">基于RAG技术的智能问答系统</p>
|
|
|
+ </div>
|
|
|
+ <div class="header-stats">
|
|
|
+ <div class="stat-item">
|
|
|
+ <span class="stat-number">{{ knowledgeBaseList.length }}</span>
|
|
|
+ <span class="stat-label">知识库</span>
|
|
|
+ </div>
|
|
|
+ <div class="stat-item">
|
|
|
+ <span class="stat-number">{{ selectedDatasetIds.length }}</span>
|
|
|
+ <span class="stat-label">已选择</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
- <!-- 右侧搜索框和搜索结果 -->
|
|
|
- <div class="search-area">
|
|
|
- <!-- 搜索框 + 提问按钮 -->
|
|
|
- <div class="search-container">
|
|
|
- <el-input
|
|
|
- v-model="query"
|
|
|
- placeholder="请输入提问内容"
|
|
|
- suffix-icon="el-icon-search"
|
|
|
- class="search-input"
|
|
|
- ></el-input>
|
|
|
- <!-- 当 loading 时禁用按钮 -->
|
|
|
- <el-button
|
|
|
- @click="chat"
|
|
|
- type="primary"
|
|
|
- :disabled="loading"
|
|
|
- style="margin-left: 15px"
|
|
|
- >
|
|
|
- {{ "提问" }}
|
|
|
- </el-button>
|
|
|
- </div>
|
|
|
+ <div class="main-layout">
|
|
|
+ <!-- 左侧知识库侧边栏 -->
|
|
|
+ <div class="sidebar">
|
|
|
+ <div class="sidebar-header">
|
|
|
+ <div class="sidebar-title">
|
|
|
+ <h3>📚 知识库选择</h3>
|
|
|
+ <p class="sidebar-subtitle">选择要查询的知识库</p>
|
|
|
+ </div>
|
|
|
+ <!-- <el-button-->
|
|
|
+ <!-- class="refresh-btn"-->
|
|
|
+ <!-- size="small"-->
|
|
|
+ <!-- @click="getKnowledgeBaseList"-->
|
|
|
+ <!-- :loading="refreshing"-->
|
|
|
+ <!-- >-->
|
|
|
+ <!-- <span v-if="!refreshing">🔄</span>-->
|
|
|
+ <!-- <span v-else>...</span>-->
|
|
|
+ <!-- </el-button>-->
|
|
|
+ </div>
|
|
|
|
|
|
+ <div class="knowledge-list">
|
|
|
+ <div
|
|
|
+ v-for="item in knowledgeBaseList"
|
|
|
+ :key="item.dataset_id"
|
|
|
+ :class="['knowledge-card', { 'active': selectedDatasetIds.includes(item.dataset_id) }]"
|
|
|
+ @click="toggleDatasetSelection(item.dataset_id)"
|
|
|
+ >
|
|
|
+ <div class="card-content">
|
|
|
+ <div class="knowledge-icon">
|
|
|
+ <span v-if="selectedDatasetIds.includes(item.dataset_id)">✅</span>
|
|
|
+ <span v-else>📖</span>
|
|
|
+ </div>
|
|
|
+ <div class="knowledge-info">
|
|
|
+ <span class="knowledge-name">{{ item.name }}</span>
|
|
|
+ <span class="knowledge-desc" v-if="item.description">{{ item.description }}</span>
|
|
|
+ <span class="knowledge-status" :class="{ 'active': selectedDatasetIds.includes(item.dataset_id) }">
|
|
|
+ {{ selectedDatasetIds.includes(item.dataset_id) ? '已选择' : '点击选择' }}
|
|
|
+ </span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
|
|
|
- <!-- 搜索中的提示 -->
|
|
|
- <div v-if="loading" class="loading-spinner">回答中...</div>
|
|
|
- <!-- 展示 chat_res 数据 -->
|
|
|
- <div v-if="chatSummary" class="chat-summary">
|
|
|
- <h4>回答内容</h4>
|
|
|
- <div v-html="parsedChatSummary"></div> <!-- 这里将解析后的内容渲染到页面 -->
|
|
|
+ <div class="sidebar-footer">
|
|
|
+ <div class="selected-count">
|
|
|
+ <span class="count-number">{{ selectedDatasetIds.length }}</span>
|
|
|
+ <span class="count-label">个知识库已选择</span>
|
|
|
+ </div>
|
|
|
+ <el-button
|
|
|
+ v-if="selectedDatasetIds.length > 0"
|
|
|
+ class="clear-btn"
|
|
|
+ size="small"
|
|
|
+ @click="clearSelection"
|
|
|
+ >
|
|
|
+ 清空选择
|
|
|
+ </el-button>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
- <div class="search-results">
|
|
|
- <!-- 只有当 searchResults 不为空时才显示外层的卡片 -->
|
|
|
- <el-card v-if="searchResults.length > 0" class="search-card">
|
|
|
- <h4>搜索内容</h4> <!-- 新增标题 "搜索内容" -->
|
|
|
-
|
|
|
- <!-- 现有的搜索结果卡片循环 -->
|
|
|
- <div v-for="result in searchResults" :key="result.contentSummary">
|
|
|
- <el-card
|
|
|
- class="result-card"
|
|
|
- @click="handleDetails(result)"
|
|
|
- >
|
|
|
- <h3>{{ result.contentSummary }}</h3>
|
|
|
- <p>{{ result.content.substring(0, 100) }}...</p>
|
|
|
- <div class="meta">
|
|
|
- <span>相似度: {{ result.score.toFixed(2) }}</span>
|
|
|
- <span>知识库: {{ result.datasetName }}</span>
|
|
|
+
|
|
|
+ <!-- 右侧主内容区 -->
|
|
|
+ <div class="content-area">
|
|
|
+ <!-- 搜索区域 -->
|
|
|
+ <div class="search-section">
|
|
|
+ <div class="search-card">
|
|
|
+ <div class="search-header">
|
|
|
+ <div class="search-title">
|
|
|
+ <h2>💬 开始提问</h2>
|
|
|
+ <p>输入您的问题,AI将基于选定的知识库为您解答</p>
|
|
|
+ </div>
|
|
|
+ <div class="search-tips">
|
|
|
+ <span class="tip-item">🔍 智能检索</span>
|
|
|
+ <span class="tip-item">🤖 AI分析</span>
|
|
|
+ <span class="tip-item">📚 多源参考</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="search-input-group">
|
|
|
+ <div class="input-container">
|
|
|
+ <el-input
|
|
|
+ v-model="query"
|
|
|
+ placeholder="例如:请介绍一下机器学习的基本概念..."
|
|
|
+ size="large"
|
|
|
+ class="search-input"
|
|
|
+ @keyup.enter="chat"
|
|
|
+ :disabled="loading"
|
|
|
+ >
|
|
|
+ <template #prefix>
|
|
|
+ <span class="input-icon">💭</span>
|
|
|
+ </template>
|
|
|
+ </el-input>
|
|
|
+ <div class="input-tips" v-if="selectedDatasetIds.length > 0">
|
|
|
+ 将在 {{ selectedDatasetIds.length }} 个知识库中搜索答案
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <el-button
|
|
|
+ @click="chat"
|
|
|
+ type="primary"
|
|
|
+ :disabled="loading || !query.trim() || selectedDatasetIds.length === 0"
|
|
|
+ size="large"
|
|
|
+ class="search-button"
|
|
|
+ >
|
|
|
+ <span v-if="loading" class="button-loading">
|
|
|
+ <span class="loading-spinner"></span>
|
|
|
+ 思考中...
|
|
|
+ </span>
|
|
|
+ <span v-else class="button-content">
|
|
|
+ <span class="button-icon">🚀</span>
|
|
|
+ 开始提问
|
|
|
+ </span>
|
|
|
+ </el-button>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 快速提问示例 -->
|
|
|
+ <!-- <div class="quick-questions" v-if="!loading">-->
|
|
|
+ <!-- <p class="quick-title">💡 试试这些问题:</p>-->
|
|
|
+ <!-- <div class="question-chips">-->
|
|
|
+ <!-- <el-tag-->
|
|
|
+ <!-- v-for="question in quickQuestions"-->
|
|
|
+ <!-- :key="question"-->
|
|
|
+ <!-- class="question-chip"-->
|
|
|
+ <!-- @click="query = question; chat()"-->
|
|
|
+ <!-- :disabled="loading"-->
|
|
|
+ <!-- >-->
|
|
|
+ <!-- {{ question }}-->
|
|
|
+ <!-- </el-tag>-->
|
|
|
+ <!-- </div>-->
|
|
|
+ <!-- </div>-->
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 加载状态 -->
|
|
|
+ <div v-if="loading" class="loading-section">
|
|
|
+ <div class="loading-content">
|
|
|
+ <div class="loading-animation">
|
|
|
+ <div class="thinking-robot">🤖</div>
|
|
|
+ <div class="thinking-dots">
|
|
|
+ <span></span>
|
|
|
+ <span></span>
|
|
|
+ <span></span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <p class="loading-text">AI正在分析您的问题,请稍候...</p>
|
|
|
+ <p class="loading-subtext">正在从 {{ selectedDatasetIds.length }} 个知识库中检索相关信息</p>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 最终回答 - 始终展开 -->
|
|
|
+ <div v-if="chatSummary && !loading" class="thinking-card answer-card">
|
|
|
+ <div class="card-header">
|
|
|
+ <div class="header-icon">💡</div>
|
|
|
+ <div class="header-content">
|
|
|
+ <h3>最终回答</h3>
|
|
|
+ <p>基于知识库内容生成的完整答案</p>
|
|
|
+ </div>
|
|
|
+ <div class="header-actions">
|
|
|
+ <el-tooltip content="复制回答" placement="top">
|
|
|
+ <el-button text class="action-btn" @click="copyToClipboard(chatSummary)">
|
|
|
+ 📋
|
|
|
+ </el-button>
|
|
|
+ </el-tooltip>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="card-body">
|
|
|
+ <div v-html="parsedChatSummary" class="thinking-content"></div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 思考过程展示 - 可折叠 -->
|
|
|
+ <div v-if="showThinkingProcess && !loading" class="thinking-section">
|
|
|
+ <!-- RAG 思考过程 - 可折叠 -->
|
|
|
+ <div v-if="ragSummary" class="collapsible-section">
|
|
|
+ <div class="collapsible-header" @click="toggleSection('rag')">
|
|
|
+ <div class="header-main">
|
|
|
+ <div class="header-icon">🔍</div>
|
|
|
+ <div class="header-content">
|
|
|
+ <h3>检索增强分析</h3>
|
|
|
+ <p>从知识库中检索相关信息并进行分析</p>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="header-actions">
|
|
|
+ <div class="collapse-icon" :class="{ rotated: expandedSections.rag }">
|
|
|
+ <span>▼</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div v-show="expandedSections.rag" class="collapsible-content">
|
|
|
+ <div class="card-body">
|
|
|
+ <div v-html="parsedRagSummary" class="thinking-content"></div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- LLM 思考过程 - 可折叠 -->
|
|
|
+ <div v-if="llmSummary" class="collapsible-section">
|
|
|
+ <div class="collapsible-header" @click="toggleSection('llm')">
|
|
|
+ <div class="header-main">
|
|
|
+ <div class="header-icon">🤔</div>
|
|
|
+ <div class="header-content">
|
|
|
+ <h3>智能推理</h3>
|
|
|
+ <p>基于检索内容进行深度分析和推理</p>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="header-actions">
|
|
|
+ <div class="collapse-icon" :class="{ rotated: expandedSections.llm }">
|
|
|
+ <span>▼</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div v-show="expandedSections.llm" class="collapsible-content">
|
|
|
+ <div class="card-body">
|
|
|
+ <div v-html="parsedLlmSummary" class="thinking-content"></div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 搜索结果 - 可折叠 -->
|
|
|
+ <div v-if="searchResults.length > 0 && !loading" class="collapsible-section results-section">
|
|
|
+ <div class="collapsible-header" @click="toggleSection('results')">
|
|
|
+ <div class="header-main">
|
|
|
+ <div class="header-icon">📋</div>
|
|
|
+ <div class="header-content">
|
|
|
+ <h3>参考内容</h3>
|
|
|
+ <p>AI回答时参考的知识库内容 ({{ searchResults.length }} 条)</p>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="header-actions">
|
|
|
+ <div class="collapse-icon" :class="{ rotated: expandedSections.results }">
|
|
|
+ <span>▼</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div v-show="expandedSections.results" class="collapsible-content">
|
|
|
+ <div class="results-container">
|
|
|
+ <div class="results-stats">
|
|
|
+ <span class="stat">共找到 {{ searchResults.length }} 条相关内容</span>
|
|
|
+ <span class="stat">平均相关度: {{ averageScore }}%</span>
|
|
|
+ </div>
|
|
|
+ <div class="results-grid">
|
|
|
+ <div
|
|
|
+ v-for="(result, index) in searchResults"
|
|
|
+ :key="result.contentSummary + index"
|
|
|
+ class="result-card"
|
|
|
+ @click="handleDetails(result)"
|
|
|
+ >
|
|
|
+ <div class="result-badge">#{{ index + 1 }}</div>
|
|
|
+ <div class="result-header">
|
|
|
+ <h4 class="result-title">{{ result.contentSummary }}</h4>
|
|
|
+ <div class="result-meta">
|
|
|
+ <span class="score-badge" :class="getScoreClass(result.score)">
|
|
|
+ {{ (result.score * 100).toFixed(1) }}%
|
|
|
+ </span>
|
|
|
+ <span class="source-tag">{{ result.datasetName }}</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <p class="result-preview">{{ result.content.substring(0, 150) }}...</p>
|
|
|
+ <div class="result-footer">
|
|
|
+ <span class="view-details">
|
|
|
+ <span class="view-icon">🔍</span>
|
|
|
+ 查看详情
|
|
|
+ </span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 空状态 -->
|
|
|
+ <div v-if="!loading && !showThinkingProcess && searchResults.length === 0 && hasSearched" class="empty-state">
|
|
|
+ <div class="empty-content">
|
|
|
+ <div class="empty-icon">🔍</div>
|
|
|
+ <h3>未找到相关内容</h3>
|
|
|
+ <p>请尝试调整问题关键词或选择其他知识库</p>
|
|
|
+ <el-button @click="query = ''" class="retry-btn">
|
|
|
+ 重新提问
|
|
|
+ </el-button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 初始状态 -->
|
|
|
+ <div v-if="!loading && !hasSearched" class="welcome-state">
|
|
|
+ <div class="welcome-content">
|
|
|
+ <div class="welcome-icon">🤖</div>
|
|
|
+ <h3>欢迎使用智能知识库助手</h3>
|
|
|
+ <p>选择左侧的知识库,输入您的问题,AI将为您提供精准的答案</p>
|
|
|
+ <div class="feature-list">
|
|
|
+ <div class="feature-item">
|
|
|
+ <span class="feature-icon">🔍</span>
|
|
|
+ <span>智能检索多个知识库</span>
|
|
|
+ </div>
|
|
|
+ <div class="feature-item">
|
|
|
+ <span class="feature-icon">🤔</span>
|
|
|
+ <span>展示AI思考过程</span>
|
|
|
</div>
|
|
|
- </el-card>
|
|
|
+ <div class="feature-item">
|
|
|
+ <span class="feature-icon">📚</span>
|
|
|
+ <span>提供参考来源</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
- </el-card>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
</div>
|
|
|
- </div>
|
|
|
|
|
|
- <!-- 弹窗:展示完整内容 -->
|
|
|
- <el-dialog v-model="dialogVisible" width="80%">
|
|
|
- <div>
|
|
|
- <h3>{{ selectedResult.contentSummary }}</h3>
|
|
|
- <p>{{ selectedResult.content }}</p>
|
|
|
- <hr />
|
|
|
- <div>
|
|
|
- <h4>原文内容:</h4>
|
|
|
- <p>{{ originalContent }}</p>
|
|
|
+ <!-- 美化后的弹窗 -->
|
|
|
+ <el-dialog
|
|
|
+ v-model="dialogVisible"
|
|
|
+ width="85%"
|
|
|
+ class="content-dialog"
|
|
|
+ :close-on-click-modal="false"
|
|
|
+ >
|
|
|
+ <template #header>
|
|
|
+ <div class="dialog-header">
|
|
|
+ <div class="dialog-title">
|
|
|
+ <div class="title-icon">📄</div>
|
|
|
+ <div class="title-content">
|
|
|
+ <h3>{{ selectedResult.contentSummary }}</h3>
|
|
|
+ <div class="title-meta">
|
|
|
+ <span class="meta-badge knowledge-badge">
|
|
|
+ <span class="badge-icon">📚</span>
|
|
|
+ {{ selectedResult.datasetName }}
|
|
|
+ </span>
|
|
|
+ <span class="meta-badge score-badge" :class="getScoreClass(selectedResult.score)">
|
|
|
+ <span class="badge-icon">🎯</span>
|
|
|
+ 相关度: {{ (selectedResult.score * 100).toFixed(1) }}%
|
|
|
+ </span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+
|
|
|
+ <div class="dialog-content">
|
|
|
+ <div class="content-tabs">
|
|
|
+ <div
|
|
|
+ class="tab-item"
|
|
|
+ :class="{ active: activeTab === 'summary' }"
|
|
|
+ @click="activeTab = 'summary'"
|
|
|
+ >
|
|
|
+ <span class="tab-icon">📝</span>
|
|
|
+ 摘要内容
|
|
|
+ </div>
|
|
|
+ <div
|
|
|
+ class="tab-item"
|
|
|
+ :class="{ active: activeTab === 'original' }"
|
|
|
+ @click="activeTab = 'original'"
|
|
|
+ >
|
|
|
+ <span class="tab-icon">📖</span>
|
|
|
+ 完整原文
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="tab-content">
|
|
|
+ <div v-show="activeTab === 'summary'" class="content-section summary-section">
|
|
|
+ <div class="section-header">
|
|
|
+ <h4>内容摘要</h4>
|
|
|
+ <el-button
|
|
|
+ text
|
|
|
+ class="copy-btn"
|
|
|
+ @click="copyToClipboard(selectedResult.content)"
|
|
|
+ >
|
|
|
+ 📋 复制内容
|
|
|
+ </el-button>
|
|
|
+ </div>
|
|
|
+ <div class="summary-content">
|
|
|
+ {{ selectedResult.content }}
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div v-show="activeTab === 'original'" class="content-section original-section">
|
|
|
+ <div class="section-header">
|
|
|
+ <h4>完整原文</h4>
|
|
|
+ <el-button
|
|
|
+ text
|
|
|
+ class="copy-btn"
|
|
|
+ @click="copyToClipboard(originalContent)"
|
|
|
+ >
|
|
|
+ 📋 复制原文
|
|
|
+ </el-button>
|
|
|
+ </div>
|
|
|
+ <div class="original-content">
|
|
|
+ <pre>{{ originalContent }}</pre>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
- </div>
|
|
|
- <template #footer>
|
|
|
- <el-button @click="dialogVisible = false">关闭</el-button>
|
|
|
- </template>
|
|
|
- </el-dialog>
|
|
|
+
|
|
|
+ <template #footer>
|
|
|
+ <div class="dialog-footer">
|
|
|
+ <div class="footer-actions">
|
|
|
+ <el-button
|
|
|
+ class="action-btn secondary"
|
|
|
+ @click="dialogVisible = false"
|
|
|
+ >
|
|
|
+ 取消
|
|
|
+ </el-button>
|
|
|
+ <el-button
|
|
|
+ type="primary"
|
|
|
+ class="action-btn primary"
|
|
|
+ @click="dialogVisible = false"
|
|
|
+ >
|
|
|
+ 关闭
|
|
|
+ </el-button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ </el-dialog>
|
|
|
+ </div>
|
|
|
</template>
|
|
|
|
|
|
<script setup>
|
|
|
-import { ref, onMounted, computed } from 'vue';
|
|
|
-import { ElMessage } from 'element-plus';
|
|
|
-import { marked } from 'marked';
|
|
|
-import {API_BASE_URL} from "@/config"; // 使用命名导入
|
|
|
+import {ref, onMounted, computed} from 'vue';
|
|
|
+import {ElMessage, ElMessageBox} from 'element-plus';
|
|
|
+import {marked} from 'marked';
|
|
|
+import {API_BASE_URL} from "@/config";
|
|
|
|
|
|
-// 存储选择的知识库数据
|
|
|
+// 响应式数据
|
|
|
const knowledgeBaseList = ref([]);
|
|
|
-const selectedDatasetIds = ref([11,12]);
|
|
|
-
|
|
|
-// 搜索框输入内容
|
|
|
+const selectedDatasetIds = ref([11, 12]);
|
|
|
const query = ref('');
|
|
|
-
|
|
|
-// 存储搜索结果
|
|
|
const searchResults = ref([]);
|
|
|
-
|
|
|
-// 存储chat_res总结
|
|
|
const chatSummary = ref('');
|
|
|
-
|
|
|
-// 弹窗显示状态
|
|
|
+const ragSummary = ref('');
|
|
|
+const llmSummary = ref('');
|
|
|
const dialogVisible = ref(false);
|
|
|
-
|
|
|
-// 存储选中的搜索结果
|
|
|
const selectedResult = ref({});
|
|
|
-
|
|
|
-// 存储原文内容
|
|
|
const originalContent = ref('');
|
|
|
-
|
|
|
-// 搜索加载状态
|
|
|
const loading = ref(false);
|
|
|
+const refreshing = ref(false);
|
|
|
+const hasSearched = ref(false);
|
|
|
+const activeTab = ref('summary');
|
|
|
+
|
|
|
+// 可折叠区域的展开状态
|
|
|
+const expandedSections = ref({
|
|
|
+ rag: false,
|
|
|
+ llm: false,
|
|
|
+ results: false
|
|
|
+});
|
|
|
+
|
|
|
+// 快速提问示例
|
|
|
+const quickQuestions = ref([
|
|
|
+ "请总结一下主要概念",
|
|
|
+ "有哪些重要的注意事项?",
|
|
|
+ "请提供相关的示例说明",
|
|
|
+ "这个主题的核心要点是什么?"
|
|
|
+]);
|
|
|
+
|
|
|
+// 计算属性
|
|
|
+const showThinkingProcess = computed(() => {
|
|
|
+ return chatSummary.value || ragSummary.value || llmSummary.value;
|
|
|
+});
|
|
|
+
|
|
|
+const averageScore = computed(() => {
|
|
|
+ if (searchResults.value.length === 0) return 0;
|
|
|
+ const total = searchResults.value.reduce((sum, result) => sum + result.score, 0);
|
|
|
+ return (total / searchResults.value.length * 100).toFixed(1);
|
|
|
+});
|
|
|
+
|
|
|
+// 解析 Markdown 内容
|
|
|
+const parseMarkdown = (content) => {
|
|
|
+ if (!content) return '';
|
|
|
+ const markdownSymbols = /[#*+\-`>!]/;
|
|
|
+ const isMarkdown = markdownSymbols.test(content);
|
|
|
+ return isMarkdown ? marked(content) : content;
|
|
|
+};
|
|
|
+
|
|
|
+const parsedChatSummary = computed(() => parseMarkdown(chatSummary.value));
|
|
|
+const parsedRagSummary = computed(() => parseMarkdown(ragSummary.value));
|
|
|
+const parsedLlmSummary = computed(() => parseMarkdown(llmSummary.value));
|
|
|
+
|
|
|
+// 根据分数获取样式类
|
|
|
+const getScoreClass = (score) => {
|
|
|
+ if (score >= 0.8) return 'high';
|
|
|
+ if (score >= 0.6) return 'medium';
|
|
|
+ return 'low';
|
|
|
+};
|
|
|
|
|
|
-// 请求知识库列表
|
|
|
+// 复制到剪贴板
|
|
|
+const copyToClipboard = async (text) => {
|
|
|
+ try {
|
|
|
+ await navigator.clipboard.writeText(text);
|
|
|
+ ElMessage.success('已复制到剪贴板');
|
|
|
+ } catch (err) {
|
|
|
+ ElMessage.error('复制失败');
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+// 切换可折叠区域
|
|
|
+const toggleSection = (section) => {
|
|
|
+ expandedSections.value[section] = !expandedSections.value[section];
|
|
|
+};
|
|
|
+
|
|
|
+// 清空选择
|
|
|
+const clearSelection = async () => {
|
|
|
+ try {
|
|
|
+ await ElMessageBox.confirm('确定要清空所有选中的知识库吗?', '提示', {
|
|
|
+ confirmButtonText: '确定',
|
|
|
+ cancelButtonText: '取消',
|
|
|
+ type: 'warning'
|
|
|
+ });
|
|
|
+ selectedDatasetIds.value = [];
|
|
|
+ ElMessage.success('已清空选择');
|
|
|
+ } catch {
|
|
|
+ // 用户取消操作
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+// 方法
|
|
|
const getKnowledgeBaseList = async () => {
|
|
|
+ refreshing.value = true;
|
|
|
try {
|
|
|
const response = await fetch(`${API_BASE_URL}/dataset/list`);
|
|
|
const data = await response.json();
|
|
|
knowledgeBaseList.value = data.data;
|
|
|
+ // ElMessage.success('知识库列表已更新');
|
|
|
} catch (error) {
|
|
|
ElMessage.error('获取知识库列表失败');
|
|
|
+ } finally {
|
|
|
+ refreshing.value = false;
|
|
|
}
|
|
|
};
|
|
|
|
|
|
-// 选择或取消选择知识库
|
|
|
const toggleDatasetSelection = (datasetId) => {
|
|
|
const index = selectedDatasetIds.value.indexOf(datasetId);
|
|
|
if (index === -1) {
|
|
@@ -135,56 +555,59 @@ const toggleDatasetSelection = (datasetId) => {
|
|
|
}
|
|
|
};
|
|
|
|
|
|
-// 执行搜索操作
|
|
|
const chat = async () => {
|
|
|
- if (loading.value) return; // 防止重复调用
|
|
|
- if (!query.value.trim()) {
|
|
|
- ElMessage.warning('请输入提问内容');
|
|
|
- return;
|
|
|
- }
|
|
|
+ if (loading.value || !query.value.trim()) return;
|
|
|
|
|
|
if (selectedDatasetIds.value.length === 0) {
|
|
|
ElMessage.warning('请先选择知识库');
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
+ // 重置状态
|
|
|
chatSummary.value = '';
|
|
|
+ ragSummary.value = '';
|
|
|
+ llmSummary.value = '';
|
|
|
searchResults.value = [];
|
|
|
- selectedResult.value = {};
|
|
|
- originalContent.value = '';
|
|
|
+ hasSearched.value = true;
|
|
|
+ // 重置展开状态
|
|
|
+ expandedSections.value = {
|
|
|
+ rag: false,
|
|
|
+ llm: false,
|
|
|
+ results: false
|
|
|
+ };
|
|
|
+ loading.value = true;
|
|
|
|
|
|
- loading.value = true; // 开始搜索时显示加载提示
|
|
|
- const datasetIds = selectedDatasetIds.value.join(',');
|
|
|
try {
|
|
|
- const response = await fetch(`${API_BASE_URL}/chat?query=${query.value}&datasetIds=${datasetIds}`);
|
|
|
+ const datasetIds = selectedDatasetIds.value.join(',');
|
|
|
+ const response = await fetch(`${API_BASE_URL}/chat?query=${encodeURIComponent(query.value)}&datasetIds=${datasetIds}`);
|
|
|
const data = await response.json();
|
|
|
- searchResults.value = data.data.results.map((item) => ({
|
|
|
- ...item,
|
|
|
- }));
|
|
|
|
|
|
- // 获取并设置 chat_res
|
|
|
- if (data.data.chat_res) {
|
|
|
- chatSummary.value = data.data.chat_res;
|
|
|
+ if (data.data) {
|
|
|
+ searchResults.value = data.data.results || [];
|
|
|
+ chatSummary.value = data.data.chat_res || '';
|
|
|
+ ragSummary.value = data.data.rag_summary || '';
|
|
|
+ llmSummary.value = data.data.llm_summary || '';
|
|
|
+ } else {
|
|
|
+ ElMessage.error('未获取到有效数据');
|
|
|
}
|
|
|
|
|
|
} catch (error) {
|
|
|
- ElMessage.error('搜索失败');
|
|
|
+ ElMessage.error('请求失败,请稍后重试');
|
|
|
} finally {
|
|
|
- loading.value = false; // 搜索结束后隐藏加载提示
|
|
|
+ loading.value = false;
|
|
|
}
|
|
|
};
|
|
|
|
|
|
-// 展示选中的搜索结果的完整内容
|
|
|
const handleDetails = async (result) => {
|
|
|
selectedResult.value = result;
|
|
|
- dialogVisible.value = true; // 打开弹窗
|
|
|
+ dialogVisible.value = true;
|
|
|
+ activeTab.value = 'summary';
|
|
|
|
|
|
- // 请求完整内容
|
|
|
try {
|
|
|
const response = await fetch(`${API_BASE_URL}/content/get?docId=${result.docId}`);
|
|
|
const data = await response.json();
|
|
|
if (data.status_code === 200) {
|
|
|
- originalContent.value = data.data.text; // 显示原文内容
|
|
|
+ originalContent.value = data.data.text;
|
|
|
} else {
|
|
|
ElMessage.error('获取原文内容失败');
|
|
|
}
|
|
@@ -193,119 +616,1355 @@ const handleDetails = async (result) => {
|
|
|
}
|
|
|
};
|
|
|
|
|
|
-// 计算属性:解析 chatSummary(如果是 Markdown 则解析)
|
|
|
-const parsedChatSummary = computed(() => {
|
|
|
- if (chatSummary.value) {
|
|
|
- // 分开检查 Markdown 格式的常见符号
|
|
|
- const markdownSymbols = /[#*+\-`>!]/; // 检测 Markdown 中常见的特殊字符
|
|
|
- const isMarkdown = markdownSymbols.test(chatSummary.value);
|
|
|
-
|
|
|
- // 如果检测到 Markdown 符号,解析为 Markdown 格式
|
|
|
- if (isMarkdown) {
|
|
|
- return marked(chatSummary.value); // 使用 marked 库解析 Markdown
|
|
|
- }
|
|
|
- }
|
|
|
- return chatSummary.value; // 如果不是 Markdown 格式,直接返回文本
|
|
|
-});
|
|
|
-
|
|
|
-
|
|
|
-// 页面初始化加载知识库列表
|
|
|
+// 生命周期
|
|
|
onMounted(() => {
|
|
|
getKnowledgeBaseList();
|
|
|
});
|
|
|
</script>
|
|
|
|
|
|
<style scoped>
|
|
|
-.container {
|
|
|
+/* 基础样式 */
|
|
|
+.app-container {
|
|
|
+ min-height: 100vh;
|
|
|
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
|
+ font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
|
+}
|
|
|
+
|
|
|
+/* 顶部导航样式 */
|
|
|
+.app-header {
|
|
|
+ background: rgba(255, 255, 255, 0.1);
|
|
|
+ backdrop-filter: blur(20px);
|
|
|
+ padding: 16px 0;
|
|
|
+ border-bottom: 1px solid rgba(255, 255, 255, 0.1);
|
|
|
+}
|
|
|
+
|
|
|
+.header-content {
|
|
|
+ max-width: 1400px;
|
|
|
+ margin: 0 auto;
|
|
|
+ padding: 0 24px;
|
|
|
display: flex;
|
|
|
justify-content: space-between;
|
|
|
+ align-items: center;
|
|
|
+}
|
|
|
+
|
|
|
+.header-main {
|
|
|
+ flex: 1;
|
|
|
+}
|
|
|
+
|
|
|
+.app-title {
|
|
|
+ color: white;
|
|
|
+ font-size: 2.25rem;
|
|
|
+ font-weight: 800;
|
|
|
+ margin: 0;
|
|
|
+ text-shadow: 0 2px 10px rgba(0, 0, 0, 0.3);
|
|
|
+ background: linear-gradient(135deg, #fff 0%, #e2e8f0 100%);
|
|
|
+ -webkit-background-clip: text;
|
|
|
+ -webkit-text-fill-color: transparent;
|
|
|
+ background-clip: text;
|
|
|
+}
|
|
|
+
|
|
|
+.app-subtitle {
|
|
|
+ color: rgba(255, 255, 255, 0.8);
|
|
|
+ font-size: 1rem;
|
|
|
+ margin: 8px 0 0 0;
|
|
|
+ font-weight: 500;
|
|
|
+}
|
|
|
+
|
|
|
+.header-stats {
|
|
|
+ display: flex;
|
|
|
+ gap: 24px;
|
|
|
+}
|
|
|
+
|
|
|
+.stat-item {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ align-items: center;
|
|
|
+}
|
|
|
+
|
|
|
+.stat-number {
|
|
|
+ color: white;
|
|
|
+ font-size: 1.5rem;
|
|
|
+ font-weight: 700;
|
|
|
+}
|
|
|
+
|
|
|
+.stat-label {
|
|
|
+ color: rgba(255, 255, 255, 0.7);
|
|
|
+ font-size: 0.8rem;
|
|
|
+ margin-top: 4px;
|
|
|
+}
|
|
|
+
|
|
|
+/* 主布局 */
|
|
|
+.main-layout {
|
|
|
+ display: flex;
|
|
|
+ max-width: 1400px;
|
|
|
+ margin: 0 auto;
|
|
|
+ padding: 24px;
|
|
|
+ gap: 24px;
|
|
|
+ min-height: calc(100vh - 120px);
|
|
|
+ align-items: flex-start;
|
|
|
+}
|
|
|
+
|
|
|
+/* 侧边栏样式 - 固定位置 */
|
|
|
+.sidebar {
|
|
|
+ width: 320px;
|
|
|
+ flex-shrink: 0;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ position: sticky;
|
|
|
+ top: 24px;
|
|
|
+ max-height: calc(100vh - 120px);
|
|
|
+ overflow-y: auto;
|
|
|
+}
|
|
|
+
|
|
|
+.sidebar-header {
|
|
|
+ background: white;
|
|
|
padding: 20px;
|
|
|
- height: 100vh; /* 设置容器高度为视口高度 */
|
|
|
+ border-radius: 20px;
|
|
|
+ margin-bottom: 16px;
|
|
|
+ box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-between;
|
|
|
+ align-items: flex-start;
|
|
|
+ flex-shrink: 0;
|
|
|
}
|
|
|
|
|
|
-.knowledge-base {
|
|
|
- width: 20%;
|
|
|
- background-color: #f9f9f9;
|
|
|
- padding: 15px;
|
|
|
- border-radius: 8px;
|
|
|
- height: 100%; /* 确保高度为100% */
|
|
|
- overflow-y: auto; /* 启用垂直滚动 */
|
|
|
+.sidebar-title h3 {
|
|
|
+ margin: 0 0 6px 0;
|
|
|
+ color: #2d3748;
|
|
|
+ font-size: 1.25rem;
|
|
|
+ font-weight: 700;
|
|
|
+}
|
|
|
+
|
|
|
+.sidebar-subtitle {
|
|
|
+ margin: 0;
|
|
|
+ color: #718096;
|
|
|
+ font-size: 0.85rem;
|
|
|
+}
|
|
|
+
|
|
|
+.refresh-btn {
|
|
|
+ padding: 8px;
|
|
|
+ border-radius: 10px;
|
|
|
+ border: 1px solid #e2e8f0;
|
|
|
+ background: white;
|
|
|
+ transition: all 0.3s ease;
|
|
|
+}
|
|
|
+
|
|
|
+.refresh-btn:hover {
|
|
|
+ background: #f7fafc;
|
|
|
+ transform: rotate(90deg);
|
|
|
+}
|
|
|
+
|
|
|
+.knowledge-list {
|
|
|
+ flex: 1;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ gap: 12px;
|
|
|
+ overflow-y: auto;
|
|
|
+ min-height: 0;
|
|
|
}
|
|
|
|
|
|
-.knowledge-item {
|
|
|
- padding: 10px;
|
|
|
- border-radius: 5px;
|
|
|
+.knowledge-card {
|
|
|
+ background: white;
|
|
|
+ border-radius: 16px;
|
|
|
cursor: pointer;
|
|
|
- margin: 5px 0;
|
|
|
- transition: background-color 0.3s ease;
|
|
|
+ transition: all 0.3s ease;
|
|
|
+ border: 2px solid transparent;
|
|
|
+ box-shadow: 0 4px 16px rgba(0, 0, 0, 0.08);
|
|
|
+ overflow: hidden;
|
|
|
}
|
|
|
|
|
|
-.knowledge-item:hover {
|
|
|
- background-color: #e6f7ff;
|
|
|
+.knowledge-card:hover {
|
|
|
+ transform: translateY(-4px);
|
|
|
+ box-shadow: 0 8px 32px rgba(0, 0, 0, 0.15);
|
|
|
}
|
|
|
|
|
|
-.knowledge-item.active {
|
|
|
- background-color: #b3d8ff;
|
|
|
+.knowledge-card.active {
|
|
|
+ border-color: #4299e1;
|
|
|
+ background: linear-gradient(135deg, #ebf8ff 0%, #ffffff 100%);
|
|
|
}
|
|
|
|
|
|
-.search-area {
|
|
|
- width: 75%;
|
|
|
- background-color: #ffffff;
|
|
|
- padding: 20px;
|
|
|
- border-radius: 8px;
|
|
|
- box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
|
|
|
- height: 100%; /* 确保高度为100% */
|
|
|
- overflow-y: auto; /* 启用垂直滚动 */
|
|
|
+.card-content {
|
|
|
+ padding: 16px;
|
|
|
+ display: flex;
|
|
|
+ align-items: flex-start;
|
|
|
+ gap: 12px;
|
|
|
}
|
|
|
|
|
|
-.search-container {
|
|
|
+.knowledge-icon {
|
|
|
+ font-size: 1.25rem;
|
|
|
+ width: 40px;
|
|
|
+ height: 40px;
|
|
|
display: flex;
|
|
|
+ align-items: center;
|
|
|
justify-content: center;
|
|
|
+ background: #f7fafc;
|
|
|
+ border-radius: 10px;
|
|
|
+ flex-shrink: 0;
|
|
|
+}
|
|
|
+
|
|
|
+.knowledge-info {
|
|
|
+ flex: 1;
|
|
|
+ min-width: 0;
|
|
|
+}
|
|
|
+
|
|
|
+.knowledge-name {
|
|
|
+ display: block;
|
|
|
+ font-weight: 600;
|
|
|
+ color: #2d3748;
|
|
|
+ margin-bottom: 4px;
|
|
|
+ font-size: 0.95rem;
|
|
|
+ line-height: 1.4;
|
|
|
+}
|
|
|
+
|
|
|
+.knowledge-desc {
|
|
|
+ display: block;
|
|
|
+ color: #718096;
|
|
|
+ font-size: 0.8rem;
|
|
|
+ line-height: 1.4;
|
|
|
+ margin-bottom: 6px;
|
|
|
+}
|
|
|
+
|
|
|
+.knowledge-status {
|
|
|
+ font-size: 0.75rem;
|
|
|
+ color: #a0aec0;
|
|
|
+ font-weight: 500;
|
|
|
+}
|
|
|
+
|
|
|
+.knowledge-status.active {
|
|
|
+ color: #4299e1;
|
|
|
+ font-weight: 600;
|
|
|
+}
|
|
|
+
|
|
|
+.sidebar-footer {
|
|
|
+ background: white;
|
|
|
+ padding: 16px 20px;
|
|
|
+ border-radius: 16px;
|
|
|
+ margin-top: 16px;
|
|
|
+ box-shadow: 0 4px 16px rgba(0, 0, 0, 0.08);
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-between;
|
|
|
align-items: center;
|
|
|
- margin-bottom: 20px;
|
|
|
+ flex-shrink: 0;
|
|
|
+ position: sticky;
|
|
|
+ bottom: 0;
|
|
|
+ z-index: 10;
|
|
|
}
|
|
|
|
|
|
-.search-input {
|
|
|
- width: 60%; /* 控制搜索框的宽度 */
|
|
|
+.selected-count {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 8px;
|
|
|
}
|
|
|
|
|
|
-.result-card {
|
|
|
- margin-top: 10px;
|
|
|
- cursor: pointer;
|
|
|
+.count-number {
|
|
|
+ background: #4299e1;
|
|
|
+ color: white;
|
|
|
+ padding: 4px 10px;
|
|
|
+ border-radius: 12px;
|
|
|
+ font-size: 0.85rem;
|
|
|
+ font-weight: 700;
|
|
|
}
|
|
|
|
|
|
-.result-card:hover {
|
|
|
- background-color: #f5f5f5;
|
|
|
+.count-label {
|
|
|
+ color: #718096;
|
|
|
+ font-size: 0.85rem;
|
|
|
+}
|
|
|
+
|
|
|
+.clear-btn {
|
|
|
+ font-size: 0.8rem;
|
|
|
+ padding: 6px 12px;
|
|
|
+ border-radius: 8px;
|
|
|
+}
|
|
|
+
|
|
|
+/* 主内容区样式 */
|
|
|
+.content-area {
|
|
|
+ flex: 1;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ gap: 24px;
|
|
|
+ min-height: 0;
|
|
|
+}
|
|
|
+
|
|
|
+.search-section {
|
|
|
+ width: 100%;
|
|
|
}
|
|
|
|
|
|
-.meta {
|
|
|
+.search-card {
|
|
|
+ background: white;
|
|
|
+ padding: 32px;
|
|
|
+ border-radius: 24px;
|
|
|
+ box-shadow: 0 12px 40px rgba(0, 0, 0, 0.12);
|
|
|
+}
|
|
|
+
|
|
|
+.search-header {
|
|
|
display: flex;
|
|
|
justify-content: space-between;
|
|
|
- font-size: 12px;
|
|
|
- color: #888;
|
|
|
+ align-items: flex-start;
|
|
|
+ margin-bottom: 28px;
|
|
|
+}
|
|
|
+
|
|
|
+.search-title h2 {
|
|
|
+ margin: 0 0 8px 0;
|
|
|
+ color: #2d3748;
|
|
|
+ font-size: 1.75rem;
|
|
|
+ font-weight: 700;
|
|
|
+}
|
|
|
+
|
|
|
+.search-title p {
|
|
|
+ margin: 0;
|
|
|
+ color: #718096;
|
|
|
+ font-size: 1rem;
|
|
|
+}
|
|
|
+
|
|
|
+.search-tips {
|
|
|
+ display: flex;
|
|
|
+ gap: 12px;
|
|
|
+}
|
|
|
+
|
|
|
+.tip-item {
|
|
|
+ background: #f7fafc;
|
|
|
+ padding: 6px 12px;
|
|
|
+ border-radius: 12px;
|
|
|
+ font-size: 0.8rem;
|
|
|
+ color: #718096;
|
|
|
+ border: 1px solid #e2e8f0;
|
|
|
+}
|
|
|
+
|
|
|
+/* 搜索框和按钮高度统一 */
|
|
|
+.search-input-group {
|
|
|
+ display: flex;
|
|
|
+ gap: 16px;
|
|
|
+ margin-bottom: 24px;
|
|
|
+ align-items: stretch;
|
|
|
+}
|
|
|
+
|
|
|
+.input-container {
|
|
|
+ flex: 1;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+}
|
|
|
+
|
|
|
+.search-input {
|
|
|
+ border-radius: 16px;
|
|
|
+ height: 100%;
|
|
|
+}
|
|
|
+
|
|
|
+.search-input :deep(.el-input__wrapper) {
|
|
|
+ border-radius: 16px;
|
|
|
+ padding: 16px 20px;
|
|
|
+ box-shadow: 0 4px 16px rgba(0, 0, 0, 0.08);
|
|
|
+ border: 1px solid #e2e8f0;
|
|
|
+ transition: all 0.3s ease;
|
|
|
+ height: 56px;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+}
|
|
|
+
|
|
|
+.search-input :deep(.el-input__wrapper:hover) {
|
|
|
+ border-color: #4299e1;
|
|
|
+ box-shadow: 0 4px 20px rgba(66, 153, 225, 0.15);
|
|
|
+}
|
|
|
+
|
|
|
+.search-input :deep(.el-input__wrapper.is-focus) {
|
|
|
+ border-color: #4299e1;
|
|
|
+ box-shadow: 0 4px 20px rgba(66, 153, 225, 0.2);
|
|
|
+}
|
|
|
+
|
|
|
+.input-icon {
|
|
|
+ font-size: 1.25rem;
|
|
|
+ margin-right: 8px;
|
|
|
+}
|
|
|
+
|
|
|
+.input-tips {
|
|
|
+ font-size: 0.8rem;
|
|
|
+ color: #718096;
|
|
|
+ margin-top: 8px;
|
|
|
+ text-align: center;
|
|
|
+}
|
|
|
+
|
|
|
+.search-button {
|
|
|
+ border-radius: 16px;
|
|
|
+ padding: 16px 32px;
|
|
|
+ font-weight: 600;
|
|
|
+ transition: all 0.3s ease;
|
|
|
+ min-width: 140px;
|
|
|
+ height: 56px;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+}
|
|
|
+
|
|
|
+.search-button:not(:disabled):hover {
|
|
|
+ transform: translateY(-2px);
|
|
|
+ box-shadow: 0 8px 25px rgba(66, 153, 225, 0.4);
|
|
|
+}
|
|
|
+
|
|
|
+.button-loading {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 8px;
|
|
|
}
|
|
|
|
|
|
.loading-spinner {
|
|
|
+ width: 16px;
|
|
|
+ height: 16px;
|
|
|
+ border: 2px solid transparent;
|
|
|
+ border-top: 2px solid white;
|
|
|
+ border-radius: 50%;
|
|
|
+ animation: spin 1s linear infinite;
|
|
|
+}
|
|
|
+
|
|
|
+@keyframes spin {
|
|
|
+ 0% {
|
|
|
+ transform: rotate(0deg);
|
|
|
+ }
|
|
|
+ 100% {
|
|
|
+ transform: rotate(360deg);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+.button-content {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 8px;
|
|
|
+}
|
|
|
+
|
|
|
+.button-icon {
|
|
|
+ font-size: 1.1rem;
|
|
|
+}
|
|
|
+
|
|
|
+.quick-questions {
|
|
|
+ border-top: 1px solid #e2e8f0;
|
|
|
+ padding-top: 20px;
|
|
|
+}
|
|
|
+
|
|
|
+.quick-title {
|
|
|
+ margin: 0 0 12px 0;
|
|
|
+ color: #718096;
|
|
|
+ font-size: 0.9rem;
|
|
|
+ font-weight: 600;
|
|
|
+}
|
|
|
+
|
|
|
+.question-chips {
|
|
|
+ display: flex;
|
|
|
+ flex-wrap: wrap;
|
|
|
+ gap: 8px;
|
|
|
+}
|
|
|
+
|
|
|
+.question-chip {
|
|
|
+ cursor: pointer;
|
|
|
+ border-radius: 20px;
|
|
|
+ padding: 8px 16px;
|
|
|
+ transition: all 0.3s ease;
|
|
|
+ border: 1px solid #e2e8f0;
|
|
|
+ background: white;
|
|
|
+ font-size: 0.85rem;
|
|
|
+}
|
|
|
+
|
|
|
+.question-chip:hover {
|
|
|
+ background: #4299e1;
|
|
|
+ color: white;
|
|
|
+ transform: translateY(-1px);
|
|
|
+ box-shadow: 0 4px 12px rgba(66, 153, 225, 0.3);
|
|
|
+}
|
|
|
+
|
|
|
+/* 加载状态 */
|
|
|
+.loading-section {
|
|
|
+ background: white;
|
|
|
+ border-radius: 24px;
|
|
|
+ padding: 60px 32px;
|
|
|
text-align: center;
|
|
|
- font-size: 16px;
|
|
|
- color: #888;
|
|
|
- margin-top: 20px;
|
|
|
+ box-shadow: 0 12px 40px rgba(0, 0, 0, 0.12);
|
|
|
}
|
|
|
|
|
|
-.chat-summary {
|
|
|
- background-color: #f0f8ff;
|
|
|
- padding: 15px;
|
|
|
- border-radius: 8px;
|
|
|
- margin-bottom: 20px;
|
|
|
+.loading-content {
|
|
|
+ max-width: 400px;
|
|
|
+ margin: 0 auto;
|
|
|
}
|
|
|
|
|
|
-.chat-summary h4 {
|
|
|
- font-size: 18px;
|
|
|
- font-weight: bold;
|
|
|
+.loading-animation {
|
|
|
+ margin-bottom: 24px;
|
|
|
}
|
|
|
|
|
|
-.chat-summary p {
|
|
|
- font-size: 14px;
|
|
|
- line-height: 1.5;
|
|
|
+.thinking-robot {
|
|
|
+ font-size: 4rem;
|
|
|
+ margin-bottom: 16px;
|
|
|
+ animation: bounce 2s infinite;
|
|
|
+}
|
|
|
+
|
|
|
+@keyframes bounce {
|
|
|
+ 0%, 20%, 50%, 80%, 100% {
|
|
|
+ transform: translateY(0);
|
|
|
+ }
|
|
|
+ 40% {
|
|
|
+ transform: translateY(-10px);
|
|
|
+ }
|
|
|
+ 60% {
|
|
|
+ transform: translateY(-5px);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+.thinking-dots {
|
|
|
+ display: flex;
|
|
|
+ justify-content: center;
|
|
|
+ gap: 8px;
|
|
|
+ margin-bottom: 20px;
|
|
|
+}
|
|
|
+
|
|
|
+.thinking-dots span {
|
|
|
+ width: 12px;
|
|
|
+ height: 12px;
|
|
|
+ border-radius: 50%;
|
|
|
+ background: #4299e1;
|
|
|
+ animation: bounce 1.4s infinite ease-in-out;
|
|
|
+}
|
|
|
+
|
|
|
+.thinking-dots span:nth-child(1) {
|
|
|
+ animation-delay: -0.32s;
|
|
|
+}
|
|
|
+
|
|
|
+.thinking-dots span:nth-child(2) {
|
|
|
+ animation-delay: -0.16s;
|
|
|
+}
|
|
|
+
|
|
|
+.loading-text {
|
|
|
+ color: #2d3748;
|
|
|
+ font-size: 1.2rem;
|
|
|
+ font-weight: 600;
|
|
|
+ margin: 0 0 8px 0;
|
|
|
+}
|
|
|
+
|
|
|
+.loading-subtext {
|
|
|
+ color: #718096;
|
|
|
+ font-size: 0.95rem;
|
|
|
+ margin: 0;
|
|
|
+}
|
|
|
+
|
|
|
+/* 最终回答卡片 */
|
|
|
+.thinking-card.answer-card {
|
|
|
+ background: white;
|
|
|
+ border-radius: 20px;
|
|
|
+ overflow: hidden;
|
|
|
+ box-shadow: 0 12px 40px rgba(0, 0, 0, 0.12);
|
|
|
+ border-left: 6px solid #48bb78;
|
|
|
+}
|
|
|
+
|
|
|
+.answer-card .card-header {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: space-between;
|
|
|
+ padding: 24px 28px;
|
|
|
+ background: linear-gradient(135deg, #f0fff4 0%, #ffffff 100%);
|
|
|
+ border-bottom: 1px solid #e2e8f0;
|
|
|
+}
|
|
|
+
|
|
|
+.answer-card .card-body {
|
|
|
+ padding: 0 28px 28px;
|
|
|
+}
|
|
|
+
|
|
|
+/* 可折叠区域样式 */
|
|
|
+.collapsible-section {
|
|
|
+ background: white;
|
|
|
+ border-radius: 20px;
|
|
|
+ overflow: hidden;
|
|
|
+ box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
|
|
|
+ margin-bottom: 16px;
|
|
|
+ transition: all 0.3s ease;
|
|
|
+}
|
|
|
+
|
|
|
+.collapsible-section:hover {
|
|
|
+ box-shadow: 0 12px 40px rgba(0, 0, 0, 0.15);
|
|
|
+}
|
|
|
+
|
|
|
+.collapsible-header {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: space-between;
|
|
|
+ padding: 20px 28px;
|
|
|
+ cursor: pointer;
|
|
|
+ transition: background-color 0.3s ease;
|
|
|
+ border-bottom: 1px solid transparent;
|
|
|
+}
|
|
|
+
|
|
|
+.collapsible-header:hover {
|
|
|
+ background: rgba(0, 0, 0, 0.02);
|
|
|
+}
|
|
|
+
|
|
|
+.collapsible-section:has(.collapsible-content:not([style*="display: none"])) .collapsible-header {
|
|
|
+ border-bottom-color: #e2e8f0;
|
|
|
+}
|
|
|
+
|
|
|
+.header-main {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 16px;
|
|
|
+ flex: 1;
|
|
|
+}
|
|
|
+
|
|
|
+.header-actions {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 12px;
|
|
|
+}
|
|
|
+
|
|
|
+.action-btn {
|
|
|
+ padding: 8px;
|
|
|
+ border-radius: 10px;
|
|
|
+ transition: all 0.3s ease;
|
|
|
+}
|
|
|
+
|
|
|
+.action-btn:hover {
|
|
|
+ background: #f7fafc;
|
|
|
+}
|
|
|
+
|
|
|
+.collapse-icon {
|
|
|
+ transition: transform 0.3s ease;
|
|
|
+ color: #718096;
|
|
|
+ font-size: 14px;
|
|
|
+}
|
|
|
+
|
|
|
+.collapse-icon.rotated {
|
|
|
+ transform: rotate(180deg);
|
|
|
+}
|
|
|
+
|
|
|
+.collapsible-content {
|
|
|
+ padding: 0;
|
|
|
+}
|
|
|
+
|
|
|
+/* 为不同部分设置不同的左侧边框颜色 */
|
|
|
+.collapsible-section:nth-child(1) .collapsible-header {
|
|
|
+ border-left: 6px solid #4299e1;
|
|
|
+}
|
|
|
+
|
|
|
+.collapsible-section:nth-child(2) .collapsible-header {
|
|
|
+ border-left: 6px solid #9f7aea;
|
|
|
+}
|
|
|
+
|
|
|
+.collapsible-section:nth-child(3) .collapsible-header {
|
|
|
+ border-left: 6px solid #ed8936;
|
|
|
+}
|
|
|
+
|
|
|
+.header-icon {
|
|
|
+ font-size: 1.75rem;
|
|
|
+ width: 52px;
|
|
|
+ height: 52px;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ background: white;
|
|
|
+ border-radius: 12px;
|
|
|
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
|
|
+}
|
|
|
+
|
|
|
+.header-content h3 {
|
|
|
+ margin: 0 0 6px 0;
|
|
|
+ color: #2d3748;
|
|
|
+ font-size: 1.3rem;
|
|
|
+ font-weight: 700;
|
|
|
+}
|
|
|
+
|
|
|
+.header-content p {
|
|
|
+ margin: 0;
|
|
|
+ color: #718096;
|
|
|
+ font-size: 0.9rem;
|
|
|
+}
|
|
|
+
|
|
|
+.card-body {
|
|
|
+ padding: 0 28px 28px;
|
|
|
+}
|
|
|
+
|
|
|
+.thinking-content {
|
|
|
+ line-height: 1.7;
|
|
|
+ color: #4a5568;
|
|
|
+ font-size: 1rem;
|
|
|
+}
|
|
|
+
|
|
|
+.thinking-content :deep(h1),
|
|
|
+.thinking-content :deep(h2),
|
|
|
+.thinking-content :deep(h3) {
|
|
|
+ color: #2d3748;
|
|
|
+ margin-top: 1.5em;
|
|
|
+ margin-bottom: 0.5em;
|
|
|
+}
|
|
|
+
|
|
|
+.thinking-content :deep(p) {
|
|
|
+ margin-bottom: 1em;
|
|
|
+}
|
|
|
+
|
|
|
+.thinking-content :deep(ul),
|
|
|
+.thinking-content :deep(ol) {
|
|
|
+ margin: 1em 0;
|
|
|
+ padding-left: 1.5em;
|
|
|
+}
|
|
|
+
|
|
|
+.thinking-content :deep(li) {
|
|
|
+ margin-bottom: 0.5em;
|
|
|
+}
|
|
|
+
|
|
|
+.thinking-content :deep(code) {
|
|
|
+ background: #f7fafc;
|
|
|
+ padding: 2px 6px;
|
|
|
+ border-radius: 4px;
|
|
|
+ font-family: 'Monaco', 'Consolas', monospace;
|
|
|
+ color: #e53e3e;
|
|
|
+ font-size: 0.9em;
|
|
|
+}
|
|
|
+
|
|
|
+.thinking-content :deep(pre) {
|
|
|
+ background: #2d3748;
|
|
|
+ color: #e2e8f0;
|
|
|
+ padding: 20px;
|
|
|
+ border-radius: 12px;
|
|
|
+ overflow-x: auto;
|
|
|
+ margin: 1.5em 0;
|
|
|
+ font-size: 0.9em;
|
|
|
+}
|
|
|
+
|
|
|
+.thinking-content :deep(blockquote) {
|
|
|
+ border-left: 4px solid #4299e1;
|
|
|
+ margin: 1.5em 0;
|
|
|
+ padding-left: 20px;
|
|
|
+ color: #718096;
|
|
|
+ font-style: italic;
|
|
|
+}
|
|
|
+
|
|
|
+/* 搜索结果样式 */
|
|
|
+.results-container {
|
|
|
+ padding: 0;
|
|
|
+}
|
|
|
+
|
|
|
+.results-stats {
|
|
|
+ padding: 20px 28px;
|
|
|
+ background: #f7fafc;
|
|
|
+ border-bottom: 1px solid #e2e8f0;
|
|
|
+ display: flex;
|
|
|
+ gap: 24px;
|
|
|
+}
|
|
|
+
|
|
|
+.results-stats .stat {
|
|
|
+ color: #718096;
|
|
|
+ font-size: 0.9rem;
|
|
|
+ font-weight: 500;
|
|
|
+}
|
|
|
+
|
|
|
+.results-grid {
|
|
|
+ padding: 24px 28px;
|
|
|
+ display: grid;
|
|
|
+ grid-template-columns: repeat(auto-fit, minmax(380px, 1fr));
|
|
|
+ gap: 20px;
|
|
|
+}
|
|
|
+
|
|
|
+.result-card {
|
|
|
+ background: white;
|
|
|
+ border-radius: 16px;
|
|
|
+ padding: 20px;
|
|
|
+ cursor: pointer;
|
|
|
+ transition: all 0.3s ease;
|
|
|
+ border: 1px solid #e2e8f0;
|
|
|
+ box-shadow: 0 4px 16px rgba(0, 0, 0, 0.06);
|
|
|
+ position: relative;
|
|
|
+ overflow: hidden;
|
|
|
+}
|
|
|
+
|
|
|
+.result-card:hover {
|
|
|
+ transform: translateY(-4px);
|
|
|
+ box-shadow: 0 8px 32px rgba(0, 0, 0, 0.15);
|
|
|
+ border-color: #4299e1;
|
|
|
+}
|
|
|
+
|
|
|
+.result-badge {
|
|
|
+ position: absolute;
|
|
|
+ top: 12px;
|
|
|
+ right: 12px;
|
|
|
+ background: #4299e1;
|
|
|
+ color: white;
|
|
|
+ padding: 4px 10px;
|
|
|
+ border-radius: 12px;
|
|
|
+ font-size: 0.8rem;
|
|
|
+ font-weight: 700;
|
|
|
+}
|
|
|
+
|
|
|
+.result-header {
|
|
|
+ margin-bottom: 12px;
|
|
|
+}
|
|
|
+
|
|
|
+.result-title {
|
|
|
+ margin: 0 0 12px 0;
|
|
|
+ color: #2d3748;
|
|
|
+ font-size: 1.1rem;
|
|
|
+ font-weight: 600;
|
|
|
+ line-height: 1.4;
|
|
|
+}
|
|
|
+
|
|
|
+.result-meta {
|
|
|
+ display: flex;
|
|
|
+ gap: 8px;
|
|
|
+ align-items: center;
|
|
|
+}
|
|
|
+
|
|
|
+.score-badge {
|
|
|
+ padding: 4px 10px;
|
|
|
+ border-radius: 12px;
|
|
|
+ font-size: 0.8rem;
|
|
|
+ font-weight: 700;
|
|
|
+ color: white;
|
|
|
+}
|
|
|
+
|
|
|
+.score-badge.high {
|
|
|
+ background: #48bb78;
|
|
|
+}
|
|
|
+
|
|
|
+.score-badge.medium {
|
|
|
+ background: #ed8936;
|
|
|
+}
|
|
|
+
|
|
|
+.score-badge.low {
|
|
|
+ background: #e53e3e;
|
|
|
+}
|
|
|
+
|
|
|
+.source-tag {
|
|
|
+ background: #f7fafc;
|
|
|
+ padding: 4px 10px;
|
|
|
+ border-radius: 12px;
|
|
|
+ font-size: 0.8rem;
|
|
|
+ color: #718096;
|
|
|
+ border: 1px solid #e2e8f0;
|
|
|
+}
|
|
|
+
|
|
|
+.result-preview {
|
|
|
+ margin: 0 0 16px 0;
|
|
|
+ color: #718096;
|
|
|
+ line-height: 1.5;
|
|
|
+ font-size: 0.9rem;
|
|
|
+ display: -webkit-box;
|
|
|
+ -webkit-line-clamp: 3;
|
|
|
+ -webkit-box-orient: vertical;
|
|
|
+ overflow: hidden;
|
|
|
+}
|
|
|
+
|
|
|
+.result-footer {
|
|
|
+ display: flex;
|
|
|
+ justify-content: flex-end;
|
|
|
+}
|
|
|
+
|
|
|
+.view-details {
|
|
|
+ color: #4299e1;
|
|
|
+ font-weight: 600;
|
|
|
+ font-size: 0.9rem;
|
|
|
+ transition: color 0.3s ease;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 6px;
|
|
|
+}
|
|
|
+
|
|
|
+.view-icon {
|
|
|
+ font-size: 0.8rem;
|
|
|
+}
|
|
|
+
|
|
|
+.result-card:hover .view-details {
|
|
|
+ color: #2b6cb0;
|
|
|
+}
|
|
|
+
|
|
|
+/* 空状态 */
|
|
|
+.empty-state {
|
|
|
+ background: white;
|
|
|
+ border-radius: 24px;
|
|
|
+ padding: 80px 32px;
|
|
|
+ text-align: center;
|
|
|
+ box-shadow: 0 12px 40px rgba(0, 0, 0, 0.12);
|
|
|
+}
|
|
|
+
|
|
|
+.empty-content {
|
|
|
+ max-width: 400px;
|
|
|
+ margin: 0 auto;
|
|
|
+}
|
|
|
+
|
|
|
+.empty-icon {
|
|
|
+ font-size: 4rem;
|
|
|
+ margin-bottom: 20px;
|
|
|
+}
|
|
|
+
|
|
|
+.empty-state h3 {
|
|
|
+ margin: 0 0 12px 0;
|
|
|
+ color: #2d3748;
|
|
|
+ font-size: 1.5rem;
|
|
|
+ font-weight: 700;
|
|
|
+}
|
|
|
+
|
|
|
+.empty-state p {
|
|
|
+ margin: 0 0 24px 0;
|
|
|
+ color: #718096;
|
|
|
+ font-size: 1rem;
|
|
|
+ line-height: 1.5;
|
|
|
+}
|
|
|
+
|
|
|
+.retry-btn {
|
|
|
+ border-radius: 12px;
|
|
|
+ padding: 10px 24px;
|
|
|
+}
|
|
|
+
|
|
|
+/* 欢迎状态 */
|
|
|
+.welcome-state {
|
|
|
+ background: white;
|
|
|
+ border-radius: 24px;
|
|
|
+ padding: 80px 32px;
|
|
|
+ text-align: center;
|
|
|
+ box-shadow: 0 12px 40px rgba(0, 0, 0, 0.12);
|
|
|
+}
|
|
|
+
|
|
|
+.welcome-content {
|
|
|
+ max-width: 500px;
|
|
|
+ margin: 0 auto;
|
|
|
+}
|
|
|
+
|
|
|
+.welcome-icon {
|
|
|
+ font-size: 5rem;
|
|
|
+ margin-bottom: 24px;
|
|
|
+ animation: float 3s ease-in-out infinite;
|
|
|
+}
|
|
|
+
|
|
|
+@keyframes float {
|
|
|
+ 0%, 100% {
|
|
|
+ transform: translateY(0px);
|
|
|
+ }
|
|
|
+ 50% {
|
|
|
+ transform: translateY(-10px);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+.welcome-state h3 {
|
|
|
+ margin: 0 0 16px 0;
|
|
|
+ color: #2d3748;
|
|
|
+ font-size: 1.75rem;
|
|
|
+ font-weight: 700;
|
|
|
+}
|
|
|
+
|
|
|
+.welcome-state p {
|
|
|
+ margin: 0 0 32px 0;
|
|
|
+ color: #718096;
|
|
|
+ font-size: 1.1rem;
|
|
|
+ line-height: 1.6;
|
|
|
+}
|
|
|
+
|
|
|
+.feature-list {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ gap: 12px;
|
|
|
+ max-width: 300px;
|
|
|
+ margin: 0 auto;
|
|
|
+}
|
|
|
+
|
|
|
+.feature-item {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 12px;
|
|
|
+ padding: 12px 16px;
|
|
|
+ background: #f7fafc;
|
|
|
+ border-radius: 12px;
|
|
|
+ color: #4a5568;
|
|
|
+ font-size: 0.95rem;
|
|
|
+}
|
|
|
+
|
|
|
+.feature-icon {
|
|
|
+ font-size: 1.2rem;
|
|
|
+}
|
|
|
+
|
|
|
+/* 弹窗样式 */
|
|
|
+.content-dialog :deep(.el-dialog) {
|
|
|
+ border-radius: 24px;
|
|
|
+ overflow: hidden;
|
|
|
+ box-shadow: 0 25px 50px rgba(0, 0, 0, 0.2);
|
|
|
+}
|
|
|
+
|
|
|
+.content-dialog :deep(.el-dialog__header) {
|
|
|
+ padding: 0;
|
|
|
+ margin: 0;
|
|
|
+}
|
|
|
+
|
|
|
+.content-dialog :deep(.el-dialog__headerbtn) {
|
|
|
+ top: 24px;
|
|
|
+ right: 24px;
|
|
|
+ width: 32px;
|
|
|
+ height: 32px;
|
|
|
+ border-radius: 8px;
|
|
|
+ background: #f7fafc;
|
|
|
+}
|
|
|
+
|
|
|
+.content-dialog :deep(.el-dialog__headerbtn:hover) {
|
|
|
+ background: #e2e8f0;
|
|
|
+}
|
|
|
+
|
|
|
+.dialog-header {
|
|
|
+ padding: 0;
|
|
|
+}
|
|
|
+
|
|
|
+.dialog-title {
|
|
|
+ display: flex;
|
|
|
+ align-items: flex-start;
|
|
|
+ gap: 16px;
|
|
|
+ padding: 32px 32px 0;
|
|
|
+}
|
|
|
+
|
|
|
+.title-icon {
|
|
|
+ font-size: 2.5rem;
|
|
|
+ width: 60px;
|
|
|
+ height: 60px;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
|
+ border-radius: 16px;
|
|
|
+ color: white;
|
|
|
+ flex-shrink: 0;
|
|
|
+}
|
|
|
+
|
|
|
+.title-content {
|
|
|
+ flex: 1;
|
|
|
+ min-width: 0;
|
|
|
+}
|
|
|
+
|
|
|
+.title-content h3 {
|
|
|
+ margin: 0 0 12px 0;
|
|
|
+ color: #2d3748;
|
|
|
+ font-size: 1.5rem;
|
|
|
+ font-weight: 700;
|
|
|
+ line-height: 1.4;
|
|
|
+}
|
|
|
+
|
|
|
+.title-meta {
|
|
|
+ display: flex;
|
|
|
+ gap: 12px;
|
|
|
+ flex-wrap: wrap;
|
|
|
+}
|
|
|
+
|
|
|
+.meta-badge {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 6px;
|
|
|
+ padding: 6px 12px;
|
|
|
+ border-radius: 12px;
|
|
|
+ font-size: 0.85rem;
|
|
|
+ font-weight: 500;
|
|
|
+}
|
|
|
+
|
|
|
+.knowledge-badge {
|
|
|
+ background: #ebf8ff;
|
|
|
+ color: #2b6cb0;
|
|
|
+ border: 1px solid #bee3f8;
|
|
|
+}
|
|
|
+
|
|
|
+.score-badge.high {
|
|
|
+ background: #f0fff4;
|
|
|
+ color: #2f855a;
|
|
|
+ border: 1px solid #c6f6d5;
|
|
|
+}
|
|
|
+
|
|
|
+.score-badge.medium {
|
|
|
+ background: #fffaf0;
|
|
|
+ color: #c05621;
|
|
|
+ border: 1px solid #feebc8;
|
|
|
+}
|
|
|
+
|
|
|
+.score-badge.low {
|
|
|
+ background: #fff5f5;
|
|
|
+ color: #c53030;
|
|
|
+ border: 1px solid #fed7d7;
|
|
|
+}
|
|
|
+
|
|
|
+.badge-icon {
|
|
|
+ font-size: 0.8rem;
|
|
|
+}
|
|
|
+
|
|
|
+.dialog-content {
|
|
|
+ padding: 0;
|
|
|
+}
|
|
|
+
|
|
|
+.content-tabs {
|
|
|
+ display: flex;
|
|
|
+ padding: 0 32px;
|
|
|
+ border-bottom: 1px solid #e2e8f0;
|
|
|
+ margin-top: 24px;
|
|
|
+}
|
|
|
+
|
|
|
+.tab-item {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 8px;
|
|
|
+ padding: 12px 20px;
|
|
|
+ cursor: pointer;
|
|
|
+ border-bottom: 2px solid transparent;
|
|
|
+ color: #718096;
|
|
|
+ font-weight: 500;
|
|
|
+ transition: all 0.3s ease;
|
|
|
+}
|
|
|
+
|
|
|
+.tab-item:hover {
|
|
|
+ color: #4299e1;
|
|
|
+}
|
|
|
+
|
|
|
+.tab-item.active {
|
|
|
+ color: #4299e1;
|
|
|
+ border-bottom-color: #4299e1;
|
|
|
+}
|
|
|
+
|
|
|
+.tab-icon {
|
|
|
+ font-size: 1.1rem;
|
|
|
+}
|
|
|
+
|
|
|
+.tab-content {
|
|
|
+ padding: 0 32px 32px;
|
|
|
+}
|
|
|
+
|
|
|
+.content-section {
|
|
|
+ margin-top: 24px;
|
|
|
+}
|
|
|
+
|
|
|
+.section-header {
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-between;
|
|
|
+ align-items: center;
|
|
|
+ margin-bottom: 16px;
|
|
|
+}
|
|
|
+
|
|
|
+.section-header h4 {
|
|
|
+ margin: 0;
|
|
|
+ color: #2d3748;
|
|
|
+ font-size: 1.2rem;
|
|
|
+ font-weight: 600;
|
|
|
+}
|
|
|
+
|
|
|
+.copy-btn {
|
|
|
+ font-size: 0.9rem;
|
|
|
+ padding: 6px 12px;
|
|
|
+ border-radius: 8px;
|
|
|
+}
|
|
|
+
|
|
|
+.summary-content {
|
|
|
+ background: #f7fafc;
|
|
|
+ padding: 20px;
|
|
|
+ border-radius: 12px;
|
|
|
+ color: #4a5568;
|
|
|
+ line-height: 1.6;
|
|
|
+ font-size: 0.95rem;
|
|
|
+ border: 1px solid #e2e8f0;
|
|
|
+}
|
|
|
+
|
|
|
+.original-content {
|
|
|
+ background: #f7fafc;
|
|
|
+ border-radius: 12px;
|
|
|
+ border: 1px solid #e2e8f0;
|
|
|
+ max-height: 500px;
|
|
|
+ overflow-y: auto;
|
|
|
+}
|
|
|
+
|
|
|
+.original-content pre {
|
|
|
+ margin: 0;
|
|
|
+ padding: 20px;
|
|
|
+ color: #4a5568;
|
|
|
+ line-height: 1.6;
|
|
|
+ font-size: 0.9rem;
|
|
|
+ white-space: pre-wrap;
|
|
|
+ word-wrap: break-word;
|
|
|
+}
|
|
|
+
|
|
|
+.dialog-footer {
|
|
|
+ padding: 0 32px 24px;
|
|
|
+}
|
|
|
+
|
|
|
+.footer-actions {
|
|
|
+ display: flex;
|
|
|
+ justify-content: flex-end;
|
|
|
+ gap: 12px;
|
|
|
+}
|
|
|
+
|
|
|
+.action-btn {
|
|
|
+ border-radius: 12px;
|
|
|
+ padding: 10px 24px;
|
|
|
+ font-weight: 500;
|
|
|
+ transition: all 0.3s ease;
|
|
|
+}
|
|
|
+
|
|
|
+.action-btn.secondary {
|
|
|
+ border: 1px solid #e2e8f0;
|
|
|
+ background: white;
|
|
|
+ color: #718096;
|
|
|
+}
|
|
|
+
|
|
|
+.action-btn.secondary:hover {
|
|
|
+ background: #f7fafc;
|
|
|
+ border-color: #cbd5e0;
|
|
|
+}
|
|
|
+
|
|
|
+.action-btn.primary {
|
|
|
+ background: #4299e1;
|
|
|
+ border: 1px solid #4299e1;
|
|
|
+ color: white;
|
|
|
+}
|
|
|
+
|
|
|
+.action-btn.primary:hover {
|
|
|
+ background: #3182ce;
|
|
|
+ border-color: #3182ce;
|
|
|
+ transform: translateY(-1px);
|
|
|
+ box-shadow: 0 4px 12px rgba(66, 153, 225, 0.3);
|
|
|
+}
|
|
|
+
|
|
|
+/* 响应式设计 */
|
|
|
+@media (max-width: 1200px) {
|
|
|
+ .main-layout {
|
|
|
+ flex-direction: column;
|
|
|
+ }
|
|
|
+
|
|
|
+ .sidebar {
|
|
|
+ width: 100%;
|
|
|
+ position: static;
|
|
|
+ max-height: none;
|
|
|
+ margin-bottom: 20px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .sidebar-footer {
|
|
|
+ position: static;
|
|
|
+ }
|
|
|
+
|
|
|
+ .results-grid {
|
|
|
+ grid-template-columns: repeat(auto-fit, minmax(350px, 1fr));
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+@media (max-width: 768px) {
|
|
|
+ .app-title {
|
|
|
+ font-size: 1.75rem;
|
|
|
+ }
|
|
|
+
|
|
|
+ .header-content {
|
|
|
+ flex-direction: column;
|
|
|
+ gap: 16px;
|
|
|
+ text-align: center;
|
|
|
+ }
|
|
|
+
|
|
|
+ .header-stats {
|
|
|
+ gap: 32px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .main-layout {
|
|
|
+ padding: 16px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .search-card {
|
|
|
+ padding: 24px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .search-header {
|
|
|
+ flex-direction: column;
|
|
|
+ gap: 16px;
|
|
|
+ text-align: center;
|
|
|
+ }
|
|
|
+
|
|
|
+ .search-tips {
|
|
|
+ justify-content: center;
|
|
|
+ }
|
|
|
+
|
|
|
+ .search-input-group {
|
|
|
+ flex-direction: column;
|
|
|
+ }
|
|
|
+
|
|
|
+ .search-button {
|
|
|
+ width: 100%;
|
|
|
+ }
|
|
|
+
|
|
|
+ .collapsible-header {
|
|
|
+ padding: 16px 20px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .header-main {
|
|
|
+ gap: 12px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .header-icon {
|
|
|
+ width: 44px;
|
|
|
+ height: 44px;
|
|
|
+ font-size: 1.5rem;
|
|
|
+ }
|
|
|
+
|
|
|
+ .results-grid {
|
|
|
+ grid-template-columns: 1fr;
|
|
|
+ }
|
|
|
+
|
|
|
+ .dialog-title {
|
|
|
+ flex-direction: column;
|
|
|
+ text-align: center;
|
|
|
+ gap: 12px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .title-content h3 {
|
|
|
+ font-size: 1.3rem;
|
|
|
+ }
|
|
|
+
|
|
|
+ .content-tabs {
|
|
|
+ padding: 0 20px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .tab-content {
|
|
|
+ padding: 0 20px 20px;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+@media (max-width: 480px) {
|
|
|
+ .app-title {
|
|
|
+ font-size: 1.5rem;
|
|
|
+ }
|
|
|
+
|
|
|
+ .search-card {
|
|
|
+ padding: 20px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .thinking-card .card-header,
|
|
|
+ .collapsible-header {
|
|
|
+ padding: 16px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .card-body {
|
|
|
+ padding: 0 16px 16px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .results-grid {
|
|
|
+ grid-template-columns: 1fr;
|
|
|
+ }
|
|
|
+
|
|
|
+ .content-dialog :deep(.el-dialog) {
|
|
|
+ width: 95% !important;
|
|
|
+ margin: 20px auto;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/* 侧边栏滚动条样式 */
|
|
|
+.sidebar::-webkit-scrollbar {
|
|
|
+ width: 6px;
|
|
|
+}
|
|
|
+
|
|
|
+.sidebar::-webkit-scrollbar-track {
|
|
|
+ background: #f1f1f1;
|
|
|
+ border-radius: 3px;
|
|
|
+}
|
|
|
+
|
|
|
+.sidebar::-webkit-scrollbar-thumb {
|
|
|
+ background: #c1c1c1;
|
|
|
+ border-radius: 3px;
|
|
|
+}
|
|
|
+
|
|
|
+.sidebar::-webkit-scrollbar-thumb:hover {
|
|
|
+ background: #a8a8a8;
|
|
|
+}
|
|
|
+
|
|
|
+.knowledge-list::-webkit-scrollbar {
|
|
|
+ width: 4px;
|
|
|
+}
|
|
|
+
|
|
|
+.knowledge-list::-webkit-scrollbar-track {
|
|
|
+ background: transparent;
|
|
|
+}
|
|
|
+
|
|
|
+.knowledge-list::-webkit-scrollbar-thumb {
|
|
|
+ background: #d1d1d1;
|
|
|
+ border-radius: 2px;
|
|
|
}
|
|
|
-</style>
|
|
|
+</style>
|