update
This commit is contained in:
parent
155a2e5cba
commit
34ff339e7f
@ -5,7 +5,7 @@ services:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
image: tradus-web:1.2.9
|
||||
image: tradus-web:1.3.0
|
||||
container_name: tradus-web
|
||||
ports:
|
||||
- '6000:80'
|
||||
|
||||
16
src/App.vue
16
src/App.vue
@ -302,19 +302,7 @@ onUnmounted(() => {
|
||||
</svg>
|
||||
<span class="agent-name">首页</span>
|
||||
</RouterLink>
|
||||
<RouterLink to="/crypto-analysis" class="agent-item" @click="showMobileMenu = false">
|
||||
<svg
|
||||
class="agent-icon"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
>
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M7 12l5-5 5 5M7 17l5-5 5 5" />
|
||||
</svg>
|
||||
<span class="agent-name">加密货币分析</span>
|
||||
</RouterLink>
|
||||
<RouterLink to="/astock-analysis" class="agent-item" @click="showMobileMenu = false">
|
||||
<RouterLink to="/ai-agents" class="agent-item" @click="showMobileMenu = false">
|
||||
<svg
|
||||
class="agent-icon"
|
||||
viewBox="0 0 24 24"
|
||||
@ -330,7 +318,7 @@ onUnmounted(() => {
|
||||
<polyline points="3.27 6.96 12 12.01 20.73 6.96"></polyline>
|
||||
<line x1="12" y1="22.08" x2="12" y2="12"></line>
|
||||
</svg>
|
||||
<span class="agent-name">A股分析</span>
|
||||
<span class="agent-name">AI分析智能体</span>
|
||||
</RouterLink>
|
||||
</div>
|
||||
|
||||
|
||||
@ -20,12 +20,6 @@ const router = createRouter({
|
||||
title: 'AI 助理团队',
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '/ai-agent',
|
||||
name: 'ai-agent',
|
||||
component: () => import('../views/AIAgentView.vue'),
|
||||
meta: { requiresVIP: true },
|
||||
},
|
||||
{
|
||||
path: '/download',
|
||||
name: 'download',
|
||||
@ -42,21 +36,12 @@ const router = createRouter({
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '/crypto-analysis',
|
||||
name: 'crypto-analysis',
|
||||
component: () => import('../views/CryptoAnalysisView.vue'),
|
||||
path: '/analysis/:type',
|
||||
name: 'universal-analysis',
|
||||
component: () => import('../views/UniversalAnalysisView.vue'),
|
||||
meta: {
|
||||
requiresAuth: true,
|
||||
title: '加密货币分析助理',
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '/astock-analysis',
|
||||
name: 'astock-analysis',
|
||||
component: () => import('../views/AStockAnalysisView.vue'),
|
||||
meta: {
|
||||
requiresAuth: true,
|
||||
title: 'A股分析助理',
|
||||
title: '智能分析助理',
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -6,23 +6,23 @@ const router = useRouter()
|
||||
const agents = [
|
||||
{
|
||||
id: 'crypto-analysis',
|
||||
name: '加密货币AI分析助理',
|
||||
description: '通过 AI 技术,获取加密货币的深度分析报告',
|
||||
name: '加密货币分析智能体',
|
||||
description: '获取加密货币的深度AI智能分析报告',
|
||||
icon: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M7 12l5-5 5 5M7 17l5-5 5 5"/>
|
||||
</svg>`,
|
||||
route: '/crypto-analysis',
|
||||
route: '/analysis/crypto',
|
||||
},
|
||||
{
|
||||
id: 'astock-analysis',
|
||||
name: 'A股AI分析助理',
|
||||
description: '通过 AI 技术,获取 A 股上市公司的深度分析报告',
|
||||
name: 'A股股票分析智能体',
|
||||
description: '获取A股上市公司的深度AI智能分析报告',
|
||||
icon: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z"></path>
|
||||
<polyline points="3.27 6.96 12 12.01 20.73 6.96"></polyline>
|
||||
<line x1="12" y1="22.08" x2="12" y2="12"></line>
|
||||
</svg>`,
|
||||
route: '/astock-analysis',
|
||||
route: '/analysis/stock',
|
||||
},
|
||||
]
|
||||
|
||||
@ -35,8 +35,8 @@ const navigateToAgent = (route: string) => {
|
||||
<div class="ai-agents-view">
|
||||
<div class="content-container">
|
||||
<div class="header-section">
|
||||
<h1 class="title">AI 助理团队</h1>
|
||||
<p class="description">选择专业的 AI 助手,获取精准的分析和建议</p>
|
||||
<h1 class="title">AI 分析智能体</h1>
|
||||
<p class="description">选择专业的 AI 分析智能体,获取精准的分析和建议</p>
|
||||
</div>
|
||||
|
||||
<div class="agents-grid">
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -18,7 +18,7 @@ const openAuthModal = (mode: 'login' | 'register') => {
|
||||
<div class="hero-content">
|
||||
<h1 class="hero-title">
|
||||
<span class="accent">tradus</span>
|
||||
<span class="free-tag">Alpha</span>
|
||||
<span class="free-tag">测试版</span>
|
||||
</h1>
|
||||
<p class="hero-subtitle">基于AI大语言模型的智能投研助理</p>
|
||||
<div class="hero-actions" v-if="!isAuthenticated">
|
||||
|
||||
@ -1,8 +1,14 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, nextTick } from 'vue'
|
||||
import { ref, computed, nextTick, watch } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import { http } from '../services/api'
|
||||
|
||||
const stockCode = ref('')
|
||||
// 获取路由参数,确定分析类型
|
||||
const route = useRoute()
|
||||
const analysisType = computed(() => (route.params.type as string) || 'crypto')
|
||||
|
||||
// 分析标的代码
|
||||
const symbolCode = ref('')
|
||||
const isAnalyzing = ref(false)
|
||||
const analysisContent = ref('')
|
||||
const analysisContainer = ref<HTMLElement | null>(null)
|
||||
@ -14,6 +20,74 @@ const copySuccess = ref(false)
|
||||
const apiBaseUrl =
|
||||
import.meta.env.MODE === 'development' ? 'http://127.0.0.1:8000' : 'https://api.ibtc.work'
|
||||
|
||||
// 根据类型设置页面标题和预设列表
|
||||
const pageTitle = computed(() => {
|
||||
switch (analysisType.value) {
|
||||
case 'stock':
|
||||
return 'A股股票分析智能体'
|
||||
case 'crypto':
|
||||
default:
|
||||
return '加密货币分析智能体'
|
||||
}
|
||||
})
|
||||
|
||||
const pageDescription = computed(() => {
|
||||
switch (analysisType.value) {
|
||||
case 'stock':
|
||||
return '获取A股上市公司的深度AI智能分析'
|
||||
case 'crypto':
|
||||
default:
|
||||
return '获取加密货币的深度AI智能分析'
|
||||
}
|
||||
})
|
||||
|
||||
const inputPlaceholder = computed(() => {
|
||||
switch (analysisType.value) {
|
||||
case 'stock':
|
||||
return '请输入上市公司股票代码'
|
||||
case 'crypto':
|
||||
default:
|
||||
return '请输入BTC、ETH, 一次只能分析一个币种'
|
||||
}
|
||||
})
|
||||
|
||||
// 是否为股票分析模式
|
||||
const isStockMode = computed(() => analysisType.value === 'stock')
|
||||
|
||||
// 常见标的列表
|
||||
const commonItemsList = computed(() => {
|
||||
switch (analysisType.value) {
|
||||
case 'stock':
|
||||
return [
|
||||
{ code: '000001', label: '平安银行' },
|
||||
{ code: '000651', label: '格力电器' },
|
||||
{ code: '601318', label: '中国平安' },
|
||||
{ code: '600519', label: '贵州茅台' },
|
||||
{ code: '601888', label: '中国中免' },
|
||||
{ code: '000858', label: '五粮液' },
|
||||
{ code: '600276', label: '恒瑞医药' },
|
||||
{ code: '002594', label: '比亚迪' },
|
||||
{ code: '600036', label: '招商银行' },
|
||||
{ code: '603288', label: '海天味业' },
|
||||
]
|
||||
case 'crypto':
|
||||
default:
|
||||
return [
|
||||
{ code: 'BTC', label: 'BTC' },
|
||||
{ code: 'ETH', label: 'ETH' },
|
||||
{ code: 'SOL', label: 'SOL' },
|
||||
{ code: 'SUI', label: 'SUI' },
|
||||
{ code: 'TRX', label: 'TRX' },
|
||||
{ code: 'XRP', label: 'XRP' },
|
||||
{ code: 'BNB', label: 'BNB' },
|
||||
{ code: 'ADA', label: 'ADA' },
|
||||
{ code: 'DOGE', label: 'DOGE' },
|
||||
{ code: 'SHIB', label: 'SHIB' },
|
||||
]
|
||||
}
|
||||
})
|
||||
|
||||
// 滚动到底部
|
||||
const scrollToBottom = async () => {
|
||||
await nextTick()
|
||||
if (analysisContainer.value) {
|
||||
@ -22,25 +96,26 @@ const scrollToBottom = async () => {
|
||||
}
|
||||
|
||||
// 处理回车键
|
||||
const handleKeyup = (event: KeyboardEvent) => {
|
||||
const handleKeydown = (event: KeyboardEvent) => {
|
||||
if (event.key === 'Enter') {
|
||||
event.preventDefault()
|
||||
handleAnalysis()
|
||||
}
|
||||
}
|
||||
|
||||
// 处理错误消息的显示
|
||||
const setErrorMessage = (message: string) => {
|
||||
// 直接使用错误消息内容
|
||||
analysisContent.value = `错误: ${message}`
|
||||
currentThought.value = '分析失败'
|
||||
}
|
||||
|
||||
// 分析逻辑
|
||||
const handleAnalysis = async () => {
|
||||
const code = stockCode.value.trim()
|
||||
const code = symbolCode.value.trim()
|
||||
if (!code || isAnalyzing.value) return
|
||||
|
||||
// 验证股票代码格式
|
||||
if (!/^\d{6}$/.test(code)) {
|
||||
// 股票模式下验证股票代码格式
|
||||
if (isStockMode.value && !/^\d{6}$/.test(code)) {
|
||||
setErrorMessage('请输入正确的6位股票代码')
|
||||
return
|
||||
}
|
||||
@ -51,7 +126,19 @@ const handleAnalysis = async () => {
|
||||
currentThought.value = '准备开始分析...'
|
||||
|
||||
try {
|
||||
const response = await http.post(`${apiBaseUrl}/adata/${code}/analysis`, {})
|
||||
let response
|
||||
|
||||
// 根据分析类型调用不同的API
|
||||
if (isStockMode.value) {
|
||||
// 调用A股分析接口
|
||||
response = await http.post(`${apiBaseUrl}/adata/${code}/analysis`, {})
|
||||
} else {
|
||||
// 调用加密货币分析接口
|
||||
const requestData = {
|
||||
symbol: code.toUpperCase(),
|
||||
}
|
||||
response = await http.post(`${apiBaseUrl}/crypto/analysis_v2`, requestData)
|
||||
}
|
||||
|
||||
if (!response.ok) {
|
||||
// 解析错误响应
|
||||
@ -95,7 +182,16 @@ const handleAnalysis = async () => {
|
||||
|
||||
switch (data.event) {
|
||||
case 'workflow_started':
|
||||
currentThought.value = '开始分析...'
|
||||
currentThought.value = 'Agent 正在分析...'
|
||||
break
|
||||
case 'node_started':
|
||||
currentThought.value = `Agent 调用: ${data.data.title}`
|
||||
break
|
||||
case 'node_finished':
|
||||
currentThought.value = `Agent 完成: ${data.data.title}`
|
||||
break
|
||||
case 'node_failed':
|
||||
currentThought.value = `Agent 分析失败 ${data.data.title}`
|
||||
break
|
||||
|
||||
case 'text_chunk':
|
||||
@ -107,7 +203,6 @@ const handleAnalysis = async () => {
|
||||
|
||||
case 'workflow_finished':
|
||||
if (data.data && data.data.outputs && data.data.outputs.text) {
|
||||
// 如果最终结果与当前内容不同,则更新
|
||||
if (analysisContent.value !== data.data.outputs.text) {
|
||||
analysisContent.value = data.data.outputs.text
|
||||
await scrollToBottom()
|
||||
@ -122,14 +217,13 @@ const handleAnalysis = async () => {
|
||||
await scrollToBottom()
|
||||
break
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('解析响应数据出错:', e)
|
||||
} catch (error) {
|
||||
console.error('解析响应数据出错:', error)
|
||||
setErrorMessage('解析响应数据时出错,请稍后重试')
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('分析请求失败:', error)
|
||||
} catch {
|
||||
setErrorMessage('抱歉,分析请求失败,请稍后重试')
|
||||
} finally {
|
||||
isAnalyzing.value = false
|
||||
@ -139,14 +233,15 @@ const handleAnalysis = async () => {
|
||||
|
||||
const resetView = () => {
|
||||
showInitialView.value = true
|
||||
stockCode.value = ''
|
||||
symbolCode.value = ''
|
||||
analysisContent.value = ''
|
||||
currentThought.value = ''
|
||||
isAnalyzing.value = false
|
||||
}
|
||||
|
||||
// 清除输入
|
||||
const clearInput = () => {
|
||||
stockCode.value = ''
|
||||
symbolCode.value = ''
|
||||
analysisContent.value = ''
|
||||
currentThought.value = ''
|
||||
}
|
||||
@ -168,37 +263,50 @@ const copyAnalysis = async () => {
|
||||
console.error('复制失败')
|
||||
}
|
||||
}
|
||||
|
||||
// 处理快速分析项的点击
|
||||
const handleCommonAnalysis = (item: { code: string }) => {
|
||||
symbolCode.value = item.code
|
||||
handleAnalysis()
|
||||
}
|
||||
|
||||
// 监听路由变化,重置视图
|
||||
watch(
|
||||
() => route.params.type,
|
||||
() => {
|
||||
resetView()
|
||||
},
|
||||
)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="stock-analysis-view">
|
||||
<div class="universal-analysis-view">
|
||||
<div class="content-container">
|
||||
<!-- 初始视图 -->
|
||||
<div v-if="showInitialView" class="initial-content">
|
||||
<div class="header-section">
|
||||
<h1 class="title">A股AI分析助理</h1>
|
||||
<p class="description">通过 AI 技术,获取 A 股上市公司的深度分析报告</p>
|
||||
<h1 class="title">{{ pageTitle }}</h1>
|
||||
<p class="description">{{ pageDescription }}</p>
|
||||
</div>
|
||||
|
||||
<div class="search-section">
|
||||
<div class="search-container" :class="{ 'is-analyzing': isAnalyzing }">
|
||||
<div class="input-wrapper">
|
||||
<div class="input-container" :class="{ 'has-input': stockCode }">
|
||||
<div class="input-container">
|
||||
<div class="input-area">
|
||||
<input
|
||||
v-model="stockCode"
|
||||
v-model="symbolCode"
|
||||
type="text"
|
||||
class="search-input"
|
||||
:class="{ 'is-selected': stockCode }"
|
||||
placeholder="请输入6位股票代码"
|
||||
maxlength="6"
|
||||
@keyup="handleKeyup"
|
||||
:placeholder="inputPlaceholder"
|
||||
@keydown="handleKeydown"
|
||||
:disabled="isAnalyzing"
|
||||
:maxlength="isStockMode ? 6 : undefined"
|
||||
/>
|
||||
<button
|
||||
v-if="stockCode"
|
||||
v-if="symbolCode.trim() || analysisContent"
|
||||
class="clear-button"
|
||||
@click.stop="clearInput"
|
||||
@click="clearInput"
|
||||
:disabled="isAnalyzing"
|
||||
>
|
||||
<svg
|
||||
@ -216,8 +324,8 @@ const copyAnalysis = async () => {
|
||||
</div>
|
||||
<button
|
||||
class="analyze-button"
|
||||
@click="() => handleAnalysis()"
|
||||
:disabled="!stockCode || isAnalyzing"
|
||||
@click="handleAnalysis"
|
||||
:disabled="isAnalyzing || !symbolCode.trim()"
|
||||
>
|
||||
{{ isAnalyzing ? '分析中...' : '开始分析' }}
|
||||
</button>
|
||||
@ -225,14 +333,34 @@ const copyAnalysis = async () => {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 快速分析列表 -->
|
||||
<div class="common-analysis-section">
|
||||
<div class="common-analysis-container">
|
||||
<div class="section-header">
|
||||
<span class="section-title">点击直接快速分析</span>
|
||||
</div>
|
||||
<div class="common-analysis-list">
|
||||
<button
|
||||
v-for="item in commonItemsList"
|
||||
:key="item.label"
|
||||
class="common-analysis-item"
|
||||
@click="handleCommonAnalysis(item)"
|
||||
:disabled="isAnalyzing"
|
||||
>
|
||||
{{ item.label }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 分析视图 -->
|
||||
<div v-else class="analysis-view">
|
||||
<div class="analysis-header">
|
||||
<div class="target-info">
|
||||
<span class="label">正在分析股票:</span>
|
||||
<span class="value">{{ stockCode }}</span>
|
||||
<span class="label">正在分析:</span>
|
||||
<span class="value">{{ symbolCode.toUpperCase() }}</span>
|
||||
</div>
|
||||
<div class="action-buttons" v-if="!isAnalyzing">
|
||||
<button class="action-button" @click="resetView">
|
||||
@ -271,7 +399,8 @@ const copyAnalysis = async () => {
|
||||
<span></span>
|
||||
</div>
|
||||
<div class="status-text">
|
||||
<div class="status-label">AI 正在分析</div>
|
||||
<!-- <div class="status-label">Agent 正在分析</div> -->
|
||||
<div class="thought-text" v-if="currentThought">{{ currentThought }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -293,8 +422,7 @@ const copyAnalysis = async () => {
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
/* 基础样式 */
|
||||
.stock-analysis-view {
|
||||
.universal-analysis-view {
|
||||
min-height: 100vh;
|
||||
height: 100vh;
|
||||
background-color: var(--color-bg-primary);
|
||||
@ -311,7 +439,7 @@ const copyAnalysis = async () => {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 0;
|
||||
overflow: hidden;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
@ -320,7 +448,7 @@ const copyAnalysis = async () => {
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
margin-bottom: 15vh;
|
||||
gap: 2rem;
|
||||
gap: 1.5rem;
|
||||
transition: all 0.5s ease;
|
||||
}
|
||||
|
||||
@ -343,11 +471,11 @@ const copyAnalysis = async () => {
|
||||
transition: all 0.5s ease;
|
||||
}
|
||||
|
||||
/* 搜索区域样式 */
|
||||
.search-section {
|
||||
width: 100%;
|
||||
max-width: 800px;
|
||||
transition: all 0.5s ease;
|
||||
padding: 0 1.5rem;
|
||||
}
|
||||
|
||||
.search-container {
|
||||
@ -358,6 +486,7 @@ const copyAnalysis = async () => {
|
||||
|
||||
.input-wrapper {
|
||||
position: relative;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.input-container {
|
||||
@ -365,6 +494,7 @@ const copyAnalysis = async () => {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
min-height: 5.5rem;
|
||||
border: 2px solid var(--color-accent);
|
||||
border-radius: var(--border-radius);
|
||||
background-color: var(--color-bg-primary);
|
||||
@ -377,14 +507,14 @@ const copyAnalysis = async () => {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
position: relative;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.search-input {
|
||||
flex: 1;
|
||||
border: none;
|
||||
background: none;
|
||||
padding: 0.5rem;
|
||||
padding: 0;
|
||||
font-size: 0.95rem;
|
||||
color: var(--color-text-primary);
|
||||
outline: none;
|
||||
@ -405,10 +535,15 @@ const copyAnalysis = async () => {
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.clear-button:hover {
|
||||
.clear-button:hover:not(:disabled) {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.clear-button:disabled {
|
||||
opacity: 0.4;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.clear-button svg {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
@ -455,6 +590,100 @@ const copyAnalysis = async () => {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.analysis-status {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 1rem;
|
||||
text-align: center;
|
||||
gap: 1rem;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.progress-dots {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.progress-dots span {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
background-color: var(--color-accent);
|
||||
border-radius: 50%;
|
||||
opacity: 0.3;
|
||||
animation: pulse-dot 1s infinite;
|
||||
}
|
||||
|
||||
.progress-dots span:nth-child(2) {
|
||||
animation-delay: 0.2s;
|
||||
}
|
||||
|
||||
.progress-dots span:nth-child(3) {
|
||||
animation-delay: 0.4s;
|
||||
}
|
||||
|
||||
.status-text {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.status-label {
|
||||
font-weight: 500;
|
||||
color: var(--color-accent);
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.thought-text {
|
||||
font-size: 0.9rem;
|
||||
color: var(--color-text-secondary);
|
||||
}
|
||||
|
||||
.analysis-container {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
overflow-y: auto;
|
||||
border-radius: var(--border-radius);
|
||||
margin-top: 1rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.analysis-container.fade-in {
|
||||
background-color: var(--color-bg-secondary);
|
||||
}
|
||||
|
||||
.analysis-content {
|
||||
font-size: 1rem;
|
||||
line-height: 1.6;
|
||||
white-space: pre-wrap;
|
||||
word-break: break-word;
|
||||
color: var(--color-text-primary);
|
||||
padding: 1.5rem 2rem;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif;
|
||||
}
|
||||
|
||||
.error-message {
|
||||
color: #ff3333;
|
||||
font-weight: 500;
|
||||
padding: 1rem;
|
||||
background-color: rgba(255, 51, 51, 0.08);
|
||||
border: 1px solid rgba(255, 51, 51, 0.2);
|
||||
border-radius: 0.5rem;
|
||||
margin: 1rem;
|
||||
text-align: center;
|
||||
font-size: 1rem;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.error-message::before {
|
||||
content: '⚠️ ';
|
||||
}
|
||||
|
||||
.target-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@ -517,96 +746,73 @@ const copyAnalysis = async () => {
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
/* 分析状态样式 */
|
||||
.analysis-status {
|
||||
/* 快速分析列表样式 */
|
||||
.common-analysis-section {
|
||||
width: 100%;
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
padding: 0 1.5rem;
|
||||
}
|
||||
|
||||
.common-analysis-container {
|
||||
width: 100%;
|
||||
padding: 0 1.5rem;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.section-header {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 1rem;
|
||||
text-align: center;
|
||||
gap: 1rem;
|
||||
margin-top: 1rem;
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
.progress-dots {
|
||||
.section-title {
|
||||
font-size: 0.9rem;
|
||||
color: var(--color-text-secondary);
|
||||
}
|
||||
|
||||
.common-analysis-list {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.progress-dots span {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
background-color: var(--color-accent);
|
||||
border-radius: 50%;
|
||||
opacity: 0.3;
|
||||
animation: pulse-dot 1s infinite;
|
||||
}
|
||||
|
||||
.progress-dots span:nth-child(2) {
|
||||
animation-delay: 0.2s;
|
||||
}
|
||||
|
||||
.progress-dots span:nth-child(3) {
|
||||
animation-delay: 0.4s;
|
||||
}
|
||||
|
||||
.status-text {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.status-label {
|
||||
font-weight: 500;
|
||||
.common-analysis-item {
|
||||
padding: 0.35rem 0.75rem;
|
||||
font-size: 0.9rem;
|
||||
color: var(--color-accent);
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
/* 分析内容样式 */
|
||||
.analysis-container {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
overflow-y: auto;
|
||||
border-radius: var(--border-radius);
|
||||
margin-top: 1rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.analysis-container.fade-in {
|
||||
background-color: var(--color-bg-secondary);
|
||||
border: 1px solid var(--color-accent);
|
||||
border-radius: var(--border-radius);
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.analysis-content {
|
||||
padding: 1.5rem;
|
||||
line-height: 2;
|
||||
white-space: pre-wrap;
|
||||
word-break: break-word;
|
||||
color: var(--color-text-primary);
|
||||
.common-analysis-item:hover:not(:disabled) {
|
||||
background-color: var(--color-accent);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.error-message {
|
||||
color: #ff3333;
|
||||
font-weight: 500;
|
||||
padding: 1rem;
|
||||
background-color: rgba(255, 51, 51, 0.08);
|
||||
border: 1px solid rgba(255, 51, 51, 0.2);
|
||||
border-radius: 0.5rem;
|
||||
margin: 1rem;
|
||||
text-align: center;
|
||||
font-size: 1rem;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.error-message::before {
|
||||
content: '⚠️ ';
|
||||
.common-analysis-item:disabled {
|
||||
opacity: 0.6;
|
||||
cursor: not-allowed;
|
||||
border-color: var(--color-border);
|
||||
color: var(--color-text-secondary);
|
||||
}
|
||||
|
||||
/* 动画 */
|
||||
@keyframes fadeIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(-10px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes pulse-dot {
|
||||
0%,
|
||||
100% {
|
||||
@ -619,43 +825,10 @@ const copyAnalysis = async () => {
|
||||
}
|
||||
}
|
||||
|
||||
.fade-in {
|
||||
animation: fadeIn 0.3s ease-out;
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(-10px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
/* 响应式样式 */
|
||||
/* 响应式设计 */
|
||||
@media (max-width: 768px) {
|
||||
.initial-content {
|
||||
margin-bottom: 12vh;
|
||||
gap: 1.5rem;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 1.75rem;
|
||||
}
|
||||
|
||||
.search-container {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.search-input {
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.analysis-content {
|
||||
padding: 1.25rem 1.5rem;
|
||||
font-size: 0.95rem;
|
||||
.universal-analysis-view {
|
||||
padding: 0.75rem;
|
||||
}
|
||||
|
||||
.analysis-view {
|
||||
@ -664,7 +837,7 @@ const copyAnalysis = async () => {
|
||||
}
|
||||
|
||||
.analysis-header {
|
||||
padding: 0.75rem;
|
||||
padding: 1rem;
|
||||
flex-direction: column;
|
||||
gap: 0.75rem;
|
||||
align-items: flex-start;
|
||||
@ -682,9 +855,81 @@ const copyAnalysis = async () => {
|
||||
padding: 0.6rem;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.analysis-content {
|
||||
padding: 1.25rem 1.5rem;
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
|
||||
.initial-content {
|
||||
margin-bottom: 12vh;
|
||||
gap: 1.5rem;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 1.75rem;
|
||||
}
|
||||
|
||||
.description {
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.search-input {
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.analyze-button {
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.search-container {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.search-section {
|
||||
padding: 0 1rem;
|
||||
}
|
||||
|
||||
.common-analysis-section {
|
||||
padding: 0 1rem;
|
||||
}
|
||||
|
||||
.common-analysis-container {
|
||||
padding: 0 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.universal-analysis-view {
|
||||
padding: 0.5rem;
|
||||
}
|
||||
|
||||
.analysis-view {
|
||||
padding: 0.5rem;
|
||||
}
|
||||
|
||||
.analysis-header {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.target-info {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.target-info .label {
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
.target-info .value {
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.analysis-content {
|
||||
padding: 1rem;
|
||||
font-size: 0.9rem;
|
||||
line-height: 2;
|
||||
}
|
||||
|
||||
.initial-content {
|
||||
margin-bottom: 10vh;
|
||||
gap: 1rem;
|
||||
@ -707,20 +952,6 @@ const copyAnalysis = async () => {
|
||||
padding: 0.6rem;
|
||||
}
|
||||
|
||||
.analysis-content {
|
||||
padding: 1rem;
|
||||
font-size: 0.9rem;
|
||||
line-height: 2;
|
||||
}
|
||||
|
||||
.target-info .label {
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
.target-info .value {
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.action-buttons {
|
||||
gap: 0.5rem;
|
||||
}
|
||||
@ -729,24 +960,26 @@ const copyAnalysis = async () => {
|
||||
padding: 0.5rem;
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
}
|
||||
|
||||
/* 滚动条样式 */
|
||||
:deep(*::-webkit-scrollbar) {
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
}
|
||||
.search-container {
|
||||
padding: 0.75rem;
|
||||
}
|
||||
|
||||
:deep(*::-webkit-scrollbar-track) {
|
||||
background: transparent;
|
||||
}
|
||||
.search-section {
|
||||
padding: 0 0.75rem;
|
||||
}
|
||||
|
||||
:deep(*::-webkit-scrollbar-thumb) {
|
||||
background-color: rgba(125, 125, 125, 0.2);
|
||||
border-radius: 3px;
|
||||
}
|
||||
.common-analysis-section {
|
||||
padding: 0 0.75rem;
|
||||
}
|
||||
|
||||
:deep(*::-webkit-scrollbar-thumb:hover) {
|
||||
background-color: rgba(125, 125, 125, 0.3);
|
||||
.common-analysis-container {
|
||||
padding: 0 0.75rem;
|
||||
}
|
||||
|
||||
.common-analysis-item {
|
||||
font-size: 0.85rem;
|
||||
padding: 0.3rem 0.6rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Loading…
Reference in New Issue
Block a user