1145 lines
23 KiB
Vue
1145 lines
23 KiB
Vue
<script setup lang="ts">
|
||
import { ref, nextTick } from 'vue'
|
||
import { useUserStore } from '../stores/user'
|
||
|
||
interface AnalysisRequest {
|
||
symbol: string
|
||
timeframe?: string
|
||
}
|
||
|
||
const userStore = useUserStore()
|
||
const cryptoCode = ref('')
|
||
const isAnalyzing = ref(false)
|
||
const analysisContent = ref('')
|
||
const analysisContainer = ref<HTMLElement | null>(null)
|
||
const currentThought = ref('')
|
||
const selectedTimeframe = ref('all')
|
||
|
||
const timeframes = [
|
||
{ label: '全部', value: 'all' },
|
||
{ label: '15分钟', value: '15m' },
|
||
{ label: '1小时', value: '1h' },
|
||
{ label: '4小时', value: '4h' },
|
||
{ label: '1天', value: '1d' },
|
||
]
|
||
|
||
// 根据环境选择API基础URL
|
||
const apiBaseUrl =
|
||
import.meta.env.MODE === 'development' ? 'http://127.0.0.1:8000' : 'https://api.ibtc.work'
|
||
|
||
const scrollToBottom = async () => {
|
||
await nextTick()
|
||
if (analysisContainer.value) {
|
||
analysisContainer.value.scrollTop = analysisContainer.value.scrollHeight
|
||
}
|
||
}
|
||
|
||
// 处理回车键
|
||
const handleKeydown = (event: KeyboardEvent) => {
|
||
if (event.key === 'Enter') {
|
||
event.preventDefault()
|
||
handleAnalysis()
|
||
}
|
||
}
|
||
|
||
const handleAnalysis = async () => {
|
||
if (!cryptoCode.value.trim() || isAnalyzing.value) return
|
||
|
||
isAnalyzing.value = true
|
||
analysisContent.value = ''
|
||
currentThought.value = '准备开始分析...'
|
||
|
||
try {
|
||
const headers: Record<string, string> = {
|
||
'Content-Type': 'application/json',
|
||
}
|
||
if (userStore.authHeader) {
|
||
headers['Authorization'] = userStore.authHeader
|
||
}
|
||
|
||
const requestData: AnalysisRequest = {
|
||
symbol: cryptoCode.value.trim().toUpperCase(),
|
||
}
|
||
|
||
// 只有当不是 'all' 时才添加 timeframe
|
||
if (selectedTimeframe.value !== 'all') {
|
||
requestData.timeframe = selectedTimeframe.value
|
||
}
|
||
|
||
const response = await fetch(`${apiBaseUrl}/crypto/crypto/analysis`, {
|
||
method: 'POST',
|
||
headers,
|
||
body: JSON.stringify(requestData),
|
||
})
|
||
|
||
if (!response.ok) {
|
||
throw new Error(`HTTP error! status: ${response.status}`)
|
||
}
|
||
|
||
const reader = response.body?.getReader()
|
||
if (!reader) {
|
||
throw new Error('无法获取响应流')
|
||
}
|
||
|
||
const decoder = new TextDecoder()
|
||
let buffer = ''
|
||
|
||
while (true) {
|
||
const { done, value } = await reader.read()
|
||
if (done) break
|
||
|
||
const chunk = decoder.decode(value, { stream: true })
|
||
buffer += chunk
|
||
|
||
const lines = buffer.split('\n')
|
||
buffer = lines.pop() || ''
|
||
|
||
for (const line of lines) {
|
||
if (!line.trim() || !line.startsWith('data: ')) continue
|
||
|
||
try {
|
||
const jsonStr = line.slice(6)
|
||
const data = JSON.parse(jsonStr)
|
||
|
||
switch (data.event) {
|
||
case 'agent_message':
|
||
if (data.answer) {
|
||
analysisContent.value += data.answer
|
||
await scrollToBottom()
|
||
}
|
||
break
|
||
|
||
case 'agent_thought':
|
||
if (data.tool) {
|
||
currentThought.value = data.tool
|
||
}
|
||
break
|
||
|
||
case 'error':
|
||
const errorMessage = data.error || '未知错误'
|
||
analysisContent.value = `<div class="error-message">分析过程中出现错误:${errorMessage}</div>`
|
||
currentThought.value = '分析过程出现错误'
|
||
await scrollToBottom()
|
||
break
|
||
}
|
||
} catch (e) {
|
||
console.error('解析响应数据出错:', e)
|
||
analysisContent.value = '<div class="error-message">解析响应数据时出错,请稍后重试</div>'
|
||
currentThought.value = '数据解析出错'
|
||
}
|
||
}
|
||
}
|
||
} catch (error) {
|
||
console.error('分析请求失败:', error)
|
||
analysisContent.value = '<div class="error-message">抱歉,分析请求失败,请稍后重试</div>'
|
||
currentThought.value = '请求失败'
|
||
} finally {
|
||
isAnalyzing.value = false
|
||
await scrollToBottom()
|
||
}
|
||
}
|
||
|
||
// 清除输入
|
||
const clearInput = () => {
|
||
cryptoCode.value = ''
|
||
analysisContent.value = ''
|
||
currentThought.value = ''
|
||
}
|
||
</script>
|
||
|
||
<template>
|
||
<div class="crypto-analysis-view">
|
||
<div class="content-container">
|
||
<div class="initial-content" :class="{ minimized: analysisContent }">
|
||
<div class="header-section" :class="{ minimized: analysisContent }">
|
||
<h1 class="title">加密货币AI分析专家</h1>
|
||
<p class="description">通过 AI 技术,获取加密货币的深度分析报告</p>
|
||
</div>
|
||
|
||
<div class="search-section">
|
||
<div class="search-container" :class="{ 'is-analyzing': isAnalyzing }">
|
||
<div class="input-wrapper">
|
||
<div class="input-container">
|
||
<div class="input-area">
|
||
<input
|
||
v-model="cryptoCode"
|
||
type="text"
|
||
class="search-input"
|
||
placeholder="请输入加密货币代码,如 BTC"
|
||
@keydown="handleKeydown"
|
||
:disabled="isAnalyzing"
|
||
/>
|
||
<button
|
||
v-if="cryptoCode.trim() || analysisContent"
|
||
class="clear-button"
|
||
@click="clearInput"
|
||
:disabled="isAnalyzing"
|
||
>
|
||
<svg
|
||
viewBox="0 0 24 24"
|
||
fill="none"
|
||
stroke="currentColor"
|
||
stroke-width="2"
|
||
stroke-linecap="round"
|
||
stroke-linejoin="round"
|
||
>
|
||
<line x1="18" y1="6" x2="6" y2="18"></line>
|
||
<line x1="6" y1="6" x2="18" y2="18"></line>
|
||
</svg>
|
||
</button>
|
||
</div>
|
||
<!-- 时间周期选择 -->
|
||
<div class="timeframe-selector">
|
||
<div
|
||
v-for="timeframe in timeframes"
|
||
:key="timeframe.value"
|
||
class="timeframe-option"
|
||
:class="{ active: selectedTimeframe === timeframe.value }"
|
||
@click="selectedTimeframe = timeframe.value"
|
||
>
|
||
<span class="timeframe-label">{{ timeframe.label }}</span>
|
||
</div>
|
||
</div>
|
||
<button
|
||
class="analyze-button"
|
||
@click="handleAnalysis"
|
||
:disabled="isAnalyzing || !cryptoCode.trim()"
|
||
>
|
||
{{ isAnalyzing ? '分析中...' : '开始分析' }}
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 分析状态和结果区域 -->
|
||
<div v-if="isAnalyzing" class="analysis-status fade-in">
|
||
<div class="progress-dots">
|
||
<span></span>
|
||
<span></span>
|
||
<span></span>
|
||
</div>
|
||
<p class="status-text">
|
||
<span class="status-label">AI分析进行中</span>
|
||
<span class="thought-text" v-if="currentThought">{{ currentThought }}</span>
|
||
</p>
|
||
</div>
|
||
|
||
<div
|
||
v-if="analysisContent"
|
||
class="analysis-container"
|
||
ref="analysisContainer"
|
||
:class="{ 'fade-in': analysisContent }"
|
||
>
|
||
<div class="analysis-content" v-html="analysisContent"></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<style scoped>
|
||
/* 移除不需要的时间周期选择样式 */
|
||
.timeframe-section {
|
||
display: none;
|
||
}
|
||
|
||
.crypto-analysis-view {
|
||
min-height: 100vh;
|
||
height: 100vh;
|
||
background-color: var(--color-bg-primary);
|
||
padding: 1rem;
|
||
display: flex;
|
||
flex-direction: column;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.content-container {
|
||
max-width: 1200px;
|
||
margin: 0 auto;
|
||
width: 100%;
|
||
height: 100%;
|
||
display: flex;
|
||
flex-direction: column;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.header-section {
|
||
text-align: center;
|
||
transition: all 0.5s ease;
|
||
}
|
||
|
||
.header-section.minimized {
|
||
padding: 0.5rem 0;
|
||
}
|
||
|
||
.initial-content {
|
||
display: flex;
|
||
flex-direction: column;
|
||
justify-content: center;
|
||
align-items: center;
|
||
min-height: 100%;
|
||
padding: 2rem 1rem;
|
||
gap: 2rem;
|
||
transition: all 0.5s ease;
|
||
}
|
||
|
||
.initial-content.minimized {
|
||
min-height: auto;
|
||
padding: 0;
|
||
gap: 1rem;
|
||
}
|
||
|
||
.title {
|
||
font-size: 2.5rem;
|
||
font-weight: 700;
|
||
color: var(--color-text-primary);
|
||
margin-bottom: 0.5rem;
|
||
transition: all 0.5s ease;
|
||
}
|
||
|
||
.header-section.minimized .title {
|
||
font-size: 1.5rem;
|
||
margin-bottom: 0.25rem;
|
||
}
|
||
|
||
.description {
|
||
font-size: 1rem;
|
||
color: var(--color-text-secondary);
|
||
transition: all 0.5s ease;
|
||
}
|
||
|
||
.header-section.minimized .description {
|
||
font-size: 0.9rem;
|
||
}
|
||
|
||
.main-section {
|
||
flex: 1;
|
||
display: flex;
|
||
flex-direction: column;
|
||
min-height: 0;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.main-section.centered {
|
||
justify-content: center;
|
||
padding: 2rem 0;
|
||
}
|
||
|
||
.main-section.top-aligned {
|
||
justify-content: flex-start;
|
||
padding-top: 1rem;
|
||
}
|
||
|
||
.search-section {
|
||
width: 100%;
|
||
transition: all 0.5s ease;
|
||
}
|
||
|
||
.search-container {
|
||
position: relative;
|
||
padding: 1.5rem;
|
||
transition: all 0.3s ease;
|
||
}
|
||
|
||
.input-wrapper {
|
||
position: relative;
|
||
margin-bottom: 0;
|
||
}
|
||
|
||
.input-container {
|
||
position: relative;
|
||
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);
|
||
padding: 0.75rem 1rem;
|
||
transition: all 0.3s ease;
|
||
gap: 0.75rem;
|
||
}
|
||
|
||
.input-area {
|
||
display: flex;
|
||
align-items: center;
|
||
width: 100%;
|
||
gap: 0.5rem;
|
||
}
|
||
|
||
.input-container:hover {
|
||
border-color: var(--color-accent);
|
||
opacity: 0.8;
|
||
}
|
||
|
||
.input-container.has-selection {
|
||
background-color: transparent;
|
||
}
|
||
|
||
.selected-tag {
|
||
display: flex;
|
||
align-items: center;
|
||
background-color: var(--color-accent);
|
||
color: white;
|
||
padding: 0.5rem 0.75rem;
|
||
border-radius: var(--border-radius);
|
||
margin-right: 0.5rem;
|
||
font-size: 1rem;
|
||
font-weight: 500;
|
||
transition: all 0.3s ease;
|
||
gap: 0.5rem;
|
||
}
|
||
|
||
.tag-text {
|
||
margin-right: 0.25rem;
|
||
}
|
||
|
||
.tag-remove {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
padding: 0.15rem;
|
||
border: none;
|
||
background: rgba(255, 255, 255, 0.2);
|
||
border-radius: 50%;
|
||
cursor: pointer;
|
||
transition: all 0.2s ease;
|
||
line-height: 0;
|
||
}
|
||
|
||
.tag-remove:hover {
|
||
background: rgba(255, 255, 255, 0.3);
|
||
transform: scale(1.1);
|
||
}
|
||
|
||
.tag-remove svg {
|
||
width: 14px;
|
||
height: 14px;
|
||
stroke: currentColor;
|
||
stroke-width: 2.5;
|
||
}
|
||
|
||
.search-input {
|
||
flex: 1;
|
||
border: none;
|
||
background: none;
|
||
padding: 0;
|
||
font-size: 1.1rem;
|
||
color: var(--color-text-primary);
|
||
outline: none;
|
||
height: 2.5rem;
|
||
}
|
||
|
||
.crypto-options {
|
||
position: absolute;
|
||
top: calc(100% + 5px);
|
||
left: 0;
|
||
right: 0;
|
||
background-color: var(--color-bg-primary);
|
||
border: 2px solid var(--color-border);
|
||
border-radius: var(--border-radius);
|
||
max-height: 300px;
|
||
overflow-y: auto;
|
||
z-index: 10;
|
||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||
margin: 0 1.5rem;
|
||
}
|
||
|
||
.crypto-option {
|
||
padding: 1rem 1.5rem;
|
||
cursor: pointer;
|
||
transition: background-color 0.2s ease;
|
||
border-bottom: 1px solid var(--color-border);
|
||
}
|
||
|
||
.crypto-option:last-child {
|
||
border-bottom: none;
|
||
}
|
||
|
||
.crypto-option:hover {
|
||
background-color: var(--color-bg-secondary);
|
||
}
|
||
|
||
.crypto-name {
|
||
font-weight: 500;
|
||
color: var(--color-text-primary);
|
||
}
|
||
|
||
.analyze-button {
|
||
width: 100%;
|
||
padding: 0.75rem;
|
||
font-size: 1rem;
|
||
font-weight: 500;
|
||
color: white;
|
||
background-color: var(--color-accent);
|
||
border: none;
|
||
border-radius: var(--border-radius);
|
||
cursor: pointer;
|
||
transition: all 0.2s ease;
|
||
}
|
||
|
||
.analyze-button:hover:not(:disabled) {
|
||
background-color: var(--color-accent-hover);
|
||
}
|
||
|
||
.analyze-button:disabled {
|
||
opacity: 0.7;
|
||
cursor: not-allowed;
|
||
}
|
||
|
||
.clear-button {
|
||
position: absolute;
|
||
right: 0.5rem;
|
||
top: 50%;
|
||
transform: translateY(-50%);
|
||
background: none;
|
||
border: none;
|
||
padding: 0.25rem;
|
||
cursor: pointer;
|
||
color: var(--color-text-secondary);
|
||
opacity: 0.6;
|
||
transition: all 0.2s ease;
|
||
}
|
||
|
||
.clear-button:hover:not(:disabled) {
|
||
opacity: 1;
|
||
}
|
||
|
||
.clear-button:disabled {
|
||
opacity: 0.4;
|
||
cursor: not-allowed;
|
||
}
|
||
|
||
.clear-button svg {
|
||
width: 16px;
|
||
height: 16px;
|
||
}
|
||
|
||
.selected-crypto-info {
|
||
margin-bottom: 2rem;
|
||
}
|
||
|
||
.crypto-card {
|
||
width: 100%;
|
||
max-width: 800px;
|
||
margin: 0 auto;
|
||
}
|
||
|
||
.crypto-card.is-analyzing {
|
||
position: relative;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.analysis-status {
|
||
margin-top: 1rem;
|
||
text-align: center;
|
||
width: 100%;
|
||
}
|
||
|
||
.analysis-progress {
|
||
height: 40px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
margin-bottom: 1rem;
|
||
}
|
||
|
||
.progress-dots {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
}
|
||
|
||
.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 {
|
||
color: var(--color-text-secondary);
|
||
font-size: 1rem;
|
||
margin: 0;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
gap: 1rem;
|
||
width: 100%;
|
||
}
|
||
|
||
.status-label {
|
||
font-weight: 500;
|
||
color: var(--color-accent);
|
||
font-size: 1.1rem;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.thought-text {
|
||
font-size: 0.95rem;
|
||
color: var(--color-text-secondary);
|
||
text-align: left;
|
||
line-height: 1.5;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.no-results {
|
||
padding: 1rem;
|
||
text-align: center;
|
||
color: var(--color-text-secondary);
|
||
background-color: var(--color-bg-primary);
|
||
border: 1px solid var(--color-border);
|
||
border-radius: var(--border-radius);
|
||
margin-top: 0.5rem;
|
||
}
|
||
|
||
.analysis-container {
|
||
flex: 1;
|
||
display: flex;
|
||
flex-direction: column;
|
||
min-height: 0;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.analysis-content {
|
||
flex: 1;
|
||
overflow-y: auto;
|
||
padding: 0rem 1.5rem;
|
||
color: var(--color-text-primary);
|
||
line-height: 1.8;
|
||
font-size: 1.1rem;
|
||
white-space: pre-wrap;
|
||
}
|
||
|
||
.analysis-content :deep(h1),
|
||
.analysis-content :deep(h2),
|
||
.analysis-content :deep(h3),
|
||
.analysis-content :deep(h4) {
|
||
margin-top: 1.5em;
|
||
margin-bottom: 0.8em;
|
||
font-weight: 600;
|
||
line-height: 1.4;
|
||
}
|
||
|
||
.analysis-content :deep(p) {
|
||
margin-bottom: 1em;
|
||
white-space: pre-wrap;
|
||
}
|
||
|
||
.analysis-content :deep(ul),
|
||
.analysis-content :deep(ol) {
|
||
margin: 1em 0;
|
||
padding-left: 2em;
|
||
}
|
||
|
||
.analysis-content :deep(li) {
|
||
margin: 0.5em 0;
|
||
line-height: 1.6;
|
||
}
|
||
|
||
.analysis-content :deep(code) {
|
||
font-family: monospace;
|
||
background-color: rgba(var(--color-accent-rgb), 0.1);
|
||
padding: 0.2em 0.4em;
|
||
border-radius: 3px;
|
||
font-size: 0.9em;
|
||
}
|
||
|
||
.analysis-content :deep(pre) {
|
||
background-color: rgba(var(--color-accent-rgb), 0.05);
|
||
padding: 1em;
|
||
border-radius: var(--border-radius);
|
||
overflow-x: auto;
|
||
margin: 1em 0;
|
||
}
|
||
|
||
.analysis-content :deep(pre code) {
|
||
background-color: transparent;
|
||
padding: 0;
|
||
font-size: 0.9em;
|
||
line-height: 1.5;
|
||
}
|
||
|
||
.analysis-content :deep(blockquote) {
|
||
border-left: 4px solid var(--color-accent);
|
||
margin: 1em 0;
|
||
padding: 0.5em 0 0.5em 1em;
|
||
background-color: rgba(var(--color-accent-rgb), 0.05);
|
||
}
|
||
|
||
.analysis-content :deep(table) {
|
||
width: 100%;
|
||
border-collapse: collapse;
|
||
margin: 1em 0;
|
||
}
|
||
|
||
.analysis-content :deep(th),
|
||
.analysis-content :deep(td) {
|
||
border: 1px solid var(--color-border);
|
||
padding: 0.5em;
|
||
text-align: left;
|
||
}
|
||
|
||
.analysis-content :deep(th) {
|
||
background-color: rgba(var(--color-accent-rgb), 0.05);
|
||
font-weight: 600;
|
||
}
|
||
|
||
:deep(.thought-step) {
|
||
display: flex;
|
||
align-items: flex-start;
|
||
gap: 0.75rem;
|
||
margin: 1rem 0;
|
||
padding: 0.75rem;
|
||
background-color: rgba(var(--color-accent-rgb), 0.05);
|
||
border-radius: 0.5rem;
|
||
border: 1px solid rgba(var(--color-accent-rgb), 0.1);
|
||
}
|
||
|
||
:deep(.thought-icon) {
|
||
font-size: 1.2rem;
|
||
line-height: 1;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
:deep(.thought-content) {
|
||
color: var(--color-text-secondary);
|
||
font-size: 0.95rem;
|
||
line-height: 1.5;
|
||
}
|
||
|
||
:deep(.error-message) {
|
||
margin: 1rem 0;
|
||
padding: 0.75rem 1rem;
|
||
background-color: rgba(255, 0, 0, 0.05);
|
||
border: 1px solid rgba(255, 0, 0, 0.1);
|
||
border-radius: 0.5rem;
|
||
color: #ff4444;
|
||
font-size: 0.95rem;
|
||
}
|
||
|
||
.analysis-content::-webkit-scrollbar {
|
||
width: 6px;
|
||
}
|
||
|
||
.analysis-content::-webkit-scrollbar-track {
|
||
background: transparent;
|
||
}
|
||
|
||
.analysis-content::-webkit-scrollbar-thumb {
|
||
background-color: var(--color-border);
|
||
border-radius: 3px;
|
||
}
|
||
|
||
.analysis-content::-webkit-scrollbar-thumb:hover {
|
||
background-color: var(--color-accent);
|
||
}
|
||
|
||
@media (max-width: 768px) {
|
||
.crypto-analysis-view {
|
||
padding: 0.5rem;
|
||
}
|
||
|
||
.initial-content {
|
||
padding: 1rem 0.5rem;
|
||
gap: 1.5rem;
|
||
}
|
||
|
||
.initial-content.minimized {
|
||
padding: 0;
|
||
gap: 0.75rem;
|
||
}
|
||
|
||
.header-section {
|
||
padding: 0.75rem 0;
|
||
}
|
||
|
||
.header-section.minimized {
|
||
padding: 0.25rem 0;
|
||
}
|
||
|
||
.title {
|
||
font-size: 1.75rem;
|
||
}
|
||
|
||
.header-section.minimized .title {
|
||
font-size: 1.25rem;
|
||
}
|
||
|
||
.search-container {
|
||
padding: 1rem;
|
||
margin: 0 -0.5rem;
|
||
}
|
||
|
||
.search-input {
|
||
height: 3rem;
|
||
font-size: 1rem;
|
||
padding: 0.75rem 2rem 0.75rem 1rem;
|
||
border-radius: 0;
|
||
}
|
||
|
||
.analysis-container {
|
||
margin: 0.5rem -0.5rem 0;
|
||
border-left: none;
|
||
border-right: none;
|
||
border-radius: 0;
|
||
padding: 1rem;
|
||
}
|
||
|
||
.analysis-content {
|
||
font-size: 1rem;
|
||
padding-right: 0.25rem;
|
||
}
|
||
|
||
.crypto-card {
|
||
margin: 0;
|
||
}
|
||
|
||
.status-text {
|
||
font-size: 0.95rem;
|
||
gap: 0.75rem;
|
||
}
|
||
|
||
.status-label {
|
||
font-size: 1rem;
|
||
}
|
||
|
||
.thought-text {
|
||
font-size: 0.9rem;
|
||
max-width: 95%;
|
||
}
|
||
|
||
.selected-tag {
|
||
padding: 0.4rem 0.6rem;
|
||
font-size: 0.95rem;
|
||
}
|
||
|
||
.tag-remove {
|
||
padding: 0.12rem;
|
||
}
|
||
|
||
.tag-remove svg {
|
||
width: 12px;
|
||
height: 12px;
|
||
}
|
||
|
||
.timeframe-selector {
|
||
gap: 0.35rem;
|
||
}
|
||
|
||
.timeframe-selector .timeframe-label {
|
||
padding: 0.2rem 0.5rem;
|
||
font-size: 0.85rem;
|
||
}
|
||
}
|
||
|
||
@media (max-width: 480px) {
|
||
.initial-content {
|
||
padding: 0.75rem 0.5rem;
|
||
gap: 1rem;
|
||
}
|
||
|
||
.initial-content.minimized {
|
||
gap: 0.5rem;
|
||
}
|
||
|
||
.title {
|
||
font-size: 1.5rem;
|
||
}
|
||
|
||
.header-section.minimized .title {
|
||
font-size: 1.1rem;
|
||
}
|
||
|
||
.description {
|
||
font-size: 0.9rem;
|
||
}
|
||
|
||
.header-section.minimized .description {
|
||
font-size: 0.8rem;
|
||
}
|
||
|
||
.crypto-option {
|
||
padding: 0.5rem 0.75rem;
|
||
}
|
||
|
||
.analysis-container {
|
||
padding: 1rem;
|
||
}
|
||
|
||
.timeframe-selector {
|
||
gap: 0.25rem;
|
||
}
|
||
|
||
.timeframe-selector .timeframe-label {
|
||
padding: 0.15rem 0.4rem;
|
||
font-size: 0.8rem;
|
||
}
|
||
|
||
.analyze-button {
|
||
font-size: 0.95rem;
|
||
padding: 0.6rem;
|
||
}
|
||
}
|
||
|
||
.search-container.is-analyzing {
|
||
opacity: 0.8;
|
||
pointer-events: none;
|
||
}
|
||
|
||
.search-input.is-selected {
|
||
border-color: var(--color-accent);
|
||
background-color: rgba(var(--color-accent-rgb), 0.05);
|
||
}
|
||
|
||
.search-tips {
|
||
position: absolute;
|
||
top: 100%;
|
||
left: 1.5rem;
|
||
right: 1.5rem;
|
||
margin-top: 0.5rem;
|
||
padding: 0.75rem;
|
||
background-color: rgba(var(--color-accent-rgb), 0.1);
|
||
border-radius: var(--border-radius);
|
||
color: var(--color-accent);
|
||
font-size: 0.9rem;
|
||
text-align: center;
|
||
animation: fadeInOut 3s forwards;
|
||
}
|
||
|
||
@keyframes pulse-dot {
|
||
0%,
|
||
100% {
|
||
transform: scale(1);
|
||
opacity: 0.3;
|
||
}
|
||
50% {
|
||
transform: scale(1.2);
|
||
opacity: 1;
|
||
}
|
||
}
|
||
|
||
.fade-in {
|
||
animation: fadeIn 0.3s ease-out;
|
||
}
|
||
|
||
@keyframes fadeIn {
|
||
from {
|
||
opacity: 0;
|
||
transform: translateY(-10px);
|
||
}
|
||
to {
|
||
opacity: 1;
|
||
transform: translateY(0);
|
||
}
|
||
}
|
||
|
||
@keyframes fadeInOut {
|
||
0% {
|
||
opacity: 0;
|
||
transform: translateY(-10px);
|
||
}
|
||
10% {
|
||
opacity: 1;
|
||
transform: translateY(0);
|
||
}
|
||
90% {
|
||
opacity: 1;
|
||
transform: translateY(0);
|
||
}
|
||
100% {
|
||
opacity: 0;
|
||
transform: translateY(-10px);
|
||
}
|
||
}
|
||
|
||
@media (prefers-color-scheme: dark) {
|
||
.search-tips {
|
||
background-color: rgba(var(--color-accent-rgb), 0.2);
|
||
}
|
||
|
||
.progress-dots span {
|
||
background-color: var(--color-accent);
|
||
opacity: 0.4;
|
||
}
|
||
|
||
.status-label {
|
||
color: var(--color-accent);
|
||
}
|
||
|
||
.thought-text {
|
||
color: var(--color-text-secondary);
|
||
}
|
||
|
||
:deep(.thought-step) {
|
||
background-color: rgba(var(--color-accent-rgb), 0.1);
|
||
border-color: rgba(var(--color-accent-rgb), 0.2);
|
||
}
|
||
|
||
:deep(.error-message) {
|
||
background-color: rgba(255, 0, 0, 0.1);
|
||
border-color: rgba(255, 0, 0, 0.2);
|
||
}
|
||
|
||
.search-container {
|
||
border-color: var(--color-border);
|
||
}
|
||
|
||
.search-input {
|
||
background-color: rgba(255, 255, 255, 0.05);
|
||
border-color: var(--color-accent);
|
||
}
|
||
|
||
.search-input:focus,
|
||
.search-input:hover {
|
||
border-color: var(--color-accent);
|
||
}
|
||
}
|
||
|
||
.timeframe-selector {
|
||
display: flex;
|
||
gap: 0.5rem;
|
||
flex-wrap: nowrap;
|
||
margin-top: 0.5rem;
|
||
overflow-x: auto;
|
||
scrollbar-width: none; /* Firefox */
|
||
-ms-overflow-style: none; /* IE and Edge */
|
||
padding-bottom: 4px;
|
||
}
|
||
|
||
/* 隐藏滚动条 */
|
||
.timeframe-selector::-webkit-scrollbar {
|
||
display: none;
|
||
}
|
||
|
||
.timeframe-selector .timeframe-option {
|
||
cursor: pointer;
|
||
transition: all 0.2s ease;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.timeframe-selector .timeframe-label {
|
||
display: inline-block;
|
||
padding: 0.25rem 0.75rem;
|
||
border-radius: var(--border-radius);
|
||
font-size: 0.9rem;
|
||
color: var(--color-text-secondary);
|
||
background-color: var(--color-bg-secondary);
|
||
transition: all 0.2s ease;
|
||
white-space: nowrap;
|
||
border: 1px solid transparent;
|
||
}
|
||
|
||
.timeframe-selector .timeframe-option:not(.active):hover .timeframe-label {
|
||
border-color: var(--color-accent);
|
||
color: var(--color-accent);
|
||
background-color: transparent;
|
||
}
|
||
|
||
.timeframe-selector .timeframe-option.active .timeframe-label {
|
||
background-color: transparent;
|
||
color: var(--color-accent);
|
||
border: 1px solid var(--color-accent);
|
||
}
|
||
|
||
@media (prefers-color-scheme: dark) {
|
||
.timeframe-selector .timeframe-label {
|
||
background-color: rgba(255, 255, 255, 0.05);
|
||
}
|
||
|
||
.timeframe-selector .timeframe-option:not(.active):hover .timeframe-label {
|
||
background-color: transparent;
|
||
}
|
||
}
|
||
|
||
.input-area {
|
||
display: flex;
|
||
align-items: center;
|
||
width: 100%;
|
||
gap: 0.5rem;
|
||
}
|
||
|
||
.search-button {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 0.5rem;
|
||
padding: 0.5rem 1rem;
|
||
background-color: var(--color-accent);
|
||
color: white;
|
||
border: none;
|
||
border-radius: var(--border-radius);
|
||
font-size: 0.95rem;
|
||
font-weight: 500;
|
||
cursor: pointer;
|
||
transition: all 0.2s ease;
|
||
min-width: 100px;
|
||
}
|
||
|
||
.search-button:hover:not(:disabled) {
|
||
background-color: var(--color-accent-hover);
|
||
transform: translateY(-1px);
|
||
}
|
||
|
||
.search-button:disabled {
|
||
opacity: 0.6;
|
||
cursor: not-allowed;
|
||
}
|
||
|
||
.search-icon {
|
||
width: 18px;
|
||
height: 18px;
|
||
stroke: currentColor;
|
||
}
|
||
|
||
.search-button-text {
|
||
white-space: nowrap;
|
||
}
|
||
|
||
@media (max-width: 480px) {
|
||
.search-button {
|
||
padding: 0.4rem 0.75rem;
|
||
min-width: 80px;
|
||
font-size: 0.9rem;
|
||
}
|
||
|
||
.search-icon {
|
||
width: 16px;
|
||
height: 16px;
|
||
}
|
||
}
|
||
|
||
.clear-button {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
padding: 0.5rem;
|
||
background: transparent;
|
||
border: none;
|
||
color: var(--color-text-secondary);
|
||
cursor: pointer;
|
||
transition: all 0.2s ease;
|
||
}
|
||
|
||
.clear-button:hover:not(:disabled) {
|
||
color: var(--color-text-primary);
|
||
}
|
||
|
||
.clear-button:disabled {
|
||
opacity: 0.6;
|
||
cursor: not-allowed;
|
||
}
|
||
</style>
|