666ghj commited on
Commit
e2e7a0b
·
1 Parent(s): 28608e6

Enhance agent bio display and tool result presentation in Step4Report component

Browse files

- Updated the AgentInterview class to display the full agent bio, truncating only if it exceeds 1000 characters for better readability.
- Enhanced the Step4Report component to include structured display for tool results, allowing users to toggle between raw and structured views for various tools, improving user experience and clarity.
- Introduced new components for parsing and displaying results from different tools, including InsightForge, PanoramaSearch, InterviewAgents, and QuickSearch, providing a comprehensive view of the data.

backend/app/services/zep_tools.py CHANGED
@@ -307,7 +307,11 @@ class AgentInterview:
307
 
308
  def to_text(self) -> str:
309
  text = f"**{self.agent_name}** ({self.agent_role})\n"
310
- text += f"_简介: {self.agent_bio[:100]}..._\n\n"
 
 
 
 
311
  text += f"**Q:** {self.question}\n\n"
312
  text += f"**A:** {self.response}\n"
313
  if self.key_quotes:
@@ -1409,7 +1413,7 @@ class ZepToolsService:
1409
  interview = AgentInterview(
1410
  agent_name=agent_name,
1411
  agent_role=agent_role,
1412
- agent_bio=agent_bio[:150],
1413
  question=combined_prompt,
1414
  response=response_text,
1415
  key_quotes=key_quotes[:5]
 
307
 
308
  def to_text(self) -> str:
309
  text = f"**{self.agent_name}** ({self.agent_role})\n"
310
+ # 显示完整的agent_bio,只在超过1000字符时截断
311
+ if len(self.agent_bio) > 1000:
312
+ text += f"_简介: {self.agent_bio[:1000]}..._\n\n"
313
+ else:
314
+ text += f"_简介: {self.agent_bio}_\n\n"
315
  text += f"**Q:** {self.question}\n\n"
316
  text += f"**A:** {self.response}\n"
317
  if self.key_quotes:
 
1413
  interview = AgentInterview(
1414
  agent_name=agent_name,
1415
  agent_role=agent_role,
1416
+ agent_bio=agent_bio[:1000], # 扩大bio长度限制
1417
  question=combined_prompt,
1418
  response=response_text,
1419
  key_quotes=key_quotes[:5]
frontend/src/components/Step4Report.vue CHANGED
@@ -160,12 +160,50 @@
160
 
161
  <!-- tool_result -->
162
  <template v-if="log.action === 'tool_result'">
163
- <div class="tool-result-block">
164
  <div class="result-header">
165
- <span class="result-tool">{{ log.details?.tool_name }}</span>
166
  <span class="result-length">{{ log.details?.result_length }} chars</span>
 
 
 
 
 
 
167
  </div>
168
- <div class="result-content" v-if="log.details?.result">
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
169
  <pre>{{ log.details.result }}</pre>
170
  </div>
171
  </div>
@@ -251,7 +289,7 @@
251
  </template>
252
 
253
  <script setup>
254
- import { ref, computed, watch, onMounted, onUnmounted, nextTick, h } from 'vue'
255
  import { getAgentLog, getConsoleLog } from '../api/report'
256
 
257
  const props = defineProps({
@@ -275,6 +313,504 @@ const isComplete = ref(false)
275
  const startTime = ref(null)
276
  const mainContent = ref(null)
277
  const logContent = ref(null)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
278
 
279
  // Computed
280
  const statusClass = computed(() => {
@@ -1289,4 +1825,561 @@ watch(() => props.reportId, (newId) => {
1289
  .log-msg.error { color: #EF5350; }
1290
  .log-msg.warning { color: #FFA726; }
1291
  .log-msg.success { color: #66BB6A; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1292
  </style>
 
160
 
161
  <!-- tool_result -->
162
  <template v-if="log.action === 'tool_result'">
163
+ <div class="tool-result-block" :class="'tool-' + log.details?.tool_name">
164
  <div class="result-header">
165
+ <span class="result-tool">{{ getToolDisplayName(log.details?.tool_name) }}</span>
166
  <span class="result-length">{{ log.details?.result_length }} chars</span>
167
+ <button
168
+ class="toggle-raw-btn"
169
+ @click.stop="toggleRawResult(log.timestamp)"
170
+ >
171
+ {{ showRawResult[log.timestamp] ? '收起原文' : '查看原文' }}
172
+ </button>
173
  </div>
174
+
175
+ <!-- 结构化展示 -->
176
+ <div class="result-structured" v-if="!showRawResult[log.timestamp] && log.details?.result">
177
+ <!-- insight_forge 深度洞察 -->
178
+ <template v-if="log.details?.tool_name === 'insight_forge'">
179
+ <InsightForgeResult :result="parseInsightForge(log.details.result)" />
180
+ </template>
181
+
182
+ <!-- panorama_search 广度搜索 -->
183
+ <template v-else-if="log.details?.tool_name === 'panorama_search'">
184
+ <PanoramaResult :result="parsePanorama(log.details.result)" />
185
+ </template>
186
+
187
+ <!-- interview_agents 深度采访 -->
188
+ <template v-else-if="log.details?.tool_name === 'interview_agents'">
189
+ <InterviewResult :result="parseInterview(log.details.result)" />
190
+ </template>
191
+
192
+ <!-- quick_search 简单搜索 -->
193
+ <template v-else-if="log.details?.tool_name === 'quick_search'">
194
+ <QuickSearchResult :result="parseQuickSearch(log.details.result)" />
195
+ </template>
196
+
197
+ <!-- 其他工具 - 显示原文 -->
198
+ <template v-else>
199
+ <div class="result-content">
200
+ <pre>{{ log.details.result }}</pre>
201
+ </div>
202
+ </template>
203
+ </div>
204
+
205
+ <!-- 原文展示 -->
206
+ <div class="result-raw" v-if="showRawResult[log.timestamp] && log.details?.result">
207
  <pre>{{ log.details.result }}</pre>
208
  </div>
209
  </div>
 
289
  </template>
290
 
291
  <script setup>
292
+ import { ref, computed, watch, onMounted, onUnmounted, nextTick, h, reactive } from 'vue'
293
  import { getAgentLog, getConsoleLog } from '../api/report'
294
 
295
  const props = defineProps({
 
313
  const startTime = ref(null)
314
  const mainContent = ref(null)
315
  const logContent = ref(null)
316
+ const showRawResult = reactive({}) // 控制显示原文
317
+
318
+ // 切换显示原文
319
+ const toggleRawResult = (timestamp) => {
320
+ showRawResult[timestamp] = !showRawResult[timestamp]
321
+ }
322
+
323
+ // 工具显示名称
324
+ const getToolDisplayName = (toolName) => {
325
+ const names = {
326
+ 'insight_forge': '🔍 深度洞察检索',
327
+ 'panorama_search': '🌐 广度搜索',
328
+ 'interview_agents': '🎤 深度采访',
329
+ 'quick_search': '⚡ 快速检索',
330
+ 'get_graph_statistics': '📊 图谱统计',
331
+ 'get_entities_by_type': '👥 实体查询'
332
+ }
333
+ return names[toolName] || toolName
334
+ }
335
+
336
+ // ========== 工具结果解析器 ==========
337
+
338
+ // 解析 insight_forge 结果
339
+ const parseInsightForge = (text) => {
340
+ const result = {
341
+ query: '',
342
+ requirement: '',
343
+ stats: { facts: 0, entities: 0, relationships: 0 },
344
+ subQueries: [],
345
+ facts: [],
346
+ entities: [],
347
+ relations: []
348
+ }
349
+
350
+ try {
351
+ // 提取原始问题
352
+ const queryMatch = text.match(/原始问题:\s*(.+?)(?:\n|$)/)
353
+ if (queryMatch) result.query = queryMatch[1].trim()
354
+
355
+ // 提取模拟需求
356
+ const reqMatch = text.match(/模拟需求:\s*(.+?)(?:\n|$)/)
357
+ if (reqMatch) result.requirement = reqMatch[1].trim()
358
+
359
+ // 提取统计
360
+ const factMatch = text.match(/相关事实:\s*(\d+)/)
361
+ const entityMatch = text.match(/涉及实体:\s*(\d+)/)
362
+ const relMatch = text.match(/关系链:\s*(\d+)/)
363
+ if (factMatch) result.stats.facts = parseInt(factMatch[1])
364
+ if (entityMatch) result.stats.entities = parseInt(entityMatch[1])
365
+ if (relMatch) result.stats.relationships = parseInt(relMatch[1])
366
+
367
+ // 提取子问题
368
+ const subQSection = text.match(/### 分析的子问题\n([\s\S]*?)(?=###|\n\n###|$)/)
369
+ if (subQSection) {
370
+ const lines = subQSection[1].split('\n').filter(l => l.match(/^\d+\./))
371
+ result.subQueries = lines.map(l => l.replace(/^\d+\.\s*/, '').trim())
372
+ }
373
+
374
+ // 提取关键事实
375
+ const factsSection = text.match(/### 【关键事实】[\s\S]*?\n([\s\S]*?)(?=###|$)/)
376
+ if (factsSection) {
377
+ const lines = factsSection[1].split('\n').filter(l => l.match(/^\d+\./))
378
+ result.facts = lines.map(l => {
379
+ const match = l.match(/^\d+\.\s*"?(.+?)"?\s*$/)
380
+ return match ? match[1].replace(/^"|"$/g, '') : l.replace(/^\d+\.\s*/, '').trim()
381
+ }).slice(0, 15) // 限制显示数量
382
+ }
383
+
384
+ // 提取核心实体
385
+ const entitySection = text.match(/### 【核心实体】\n([\s\S]*?)(?=###|$)/)
386
+ if (entitySection) {
387
+ const entityBlocks = entitySection[1].split(/\n- \*\*/).slice(1)
388
+ result.entities = entityBlocks.map(block => {
389
+ const nameMatch = block.match(/^(.+?)\*\*\s*\((.+?)\)/)
390
+ const summaryMatch = block.match(/摘要:\s*"?(.+?)"?\n/)
391
+ const factsMatch = block.match(/相关事实:\s*(\d+)/)
392
+ return {
393
+ name: nameMatch ? nameMatch[1].trim() : '',
394
+ type: nameMatch ? nameMatch[2].trim() : '',
395
+ summary: summaryMatch ? summaryMatch[1].trim() : '',
396
+ factCount: factsMatch ? parseInt(factsMatch[1]) : 0
397
+ }
398
+ }).filter(e => e.name).slice(0, 10)
399
+ }
400
+
401
+ // 提取关系链
402
+ const relSection = text.match(/### 【关系链】\n([\s\S]*?)(?=###|$)/)
403
+ if (relSection) {
404
+ const lines = relSection[1].split('\n').filter(l => l.startsWith('-'))
405
+ result.relations = lines.map(l => {
406
+ const match = l.match(/^-\s*(.+?)\s*--\[(.+?)\]-->\s*(.+)$/)
407
+ if (match) {
408
+ return { source: match[1].trim(), relation: match[2].trim(), target: match[3].trim() }
409
+ }
410
+ return null
411
+ }).filter(Boolean).slice(0, 10)
412
+ }
413
+ } catch (e) {
414
+ console.warn('解析 insight_forge 结果失败:', e)
415
+ }
416
+
417
+ return result
418
+ }
419
+
420
+ // 解析 panorama_search 结果
421
+ const parsePanorama = (text) => {
422
+ const result = {
423
+ query: '',
424
+ stats: { nodes: 0, edges: 0, activeFacts: 0, historicalFacts: 0 },
425
+ activeFacts: [],
426
+ historicalFacts: [],
427
+ entities: []
428
+ }
429
+
430
+ try {
431
+ // 提取查询
432
+ const queryMatch = text.match(/查询:\s*(.+?)(?:\n|$)/)
433
+ if (queryMatch) result.query = queryMatch[1].trim()
434
+
435
+ // 提取统计
436
+ const nodesMatch = text.match(/总节点数:\s*(\d+)/)
437
+ const edgesMatch = text.match(/总边数:\s*(\d+)/)
438
+ const activeMatch = text.match(/当前有效事实:\s*(\d+)/)
439
+ const histMatch = text.match(/历史\/过期事实:\s*(\d+)/)
440
+ if (nodesMatch) result.stats.nodes = parseInt(nodesMatch[1])
441
+ if (edgesMatch) result.stats.edges = parseInt(edgesMatch[1])
442
+ if (activeMatch) result.stats.activeFacts = parseInt(activeMatch[1])
443
+ if (histMatch) result.stats.historicalFacts = parseInt(histMatch[1])
444
+
445
+ // 提取当前有效事实
446
+ const activeSection = text.match(/### 【当前有效事实】[\s\S]*?\n([\s\S]*?)(?=###|$)/)
447
+ if (activeSection) {
448
+ const lines = activeSection[1].split('\n').filter(l => l.match(/^\d+\./))
449
+ result.activeFacts = lines.map(l => {
450
+ const match = l.match(/^\d+\.\s*"?(.+?)"?\s*$/)
451
+ return match ? match[1].replace(/^"|"$/g, '') : l.replace(/^\d+\.\s*/, '').trim()
452
+ }).slice(0, 15)
453
+ }
454
+
455
+ // 提取历史事实
456
+ const histSection = text.match(/### 【历史\/过期事实】[\s\S]*?\n([\s\S]*?)(?=###|$)/)
457
+ if (histSection) {
458
+ const lines = histSection[1].split('\n').filter(l => l.match(/^\d+\./))
459
+ result.historicalFacts = lines.map(l => {
460
+ const content = l.replace(/^\d+\.\s*/, '').trim()
461
+ // 提取时间范围
462
+ const timeMatch = content.match(/^\[(.+?)\s*-\s*(.+?)\]\s*(.+)$/)
463
+ if (timeMatch) {
464
+ return { timeRange: `${timeMatch[1]} - ${timeMatch[2]}`, content: timeMatch[3].replace(/^"|"$/g, '') }
465
+ }
466
+ return { timeRange: '', content: content.replace(/^"|"$/g, '') }
467
+ }).slice(0, 10)
468
+ }
469
+
470
+ // 提取涉及实体
471
+ const entitySection = text.match(/### 【涉及实体】\n([\s\S]*?)(?=###|$)/)
472
+ if (entitySection) {
473
+ const lines = entitySection[1].split('\n').filter(l => l.startsWith('-'))
474
+ result.entities = lines.map(l => {
475
+ const match = l.match(/^-\s*\*\*(.+?)\*\*\s*\((.+?)\)/)
476
+ if (match) return { name: match[1].trim(), type: match[2].trim() }
477
+ return null
478
+ }).filter(Boolean).slice(0, 15)
479
+ }
480
+ } catch (e) {
481
+ console.warn('解析 panorama_search 结果失败:', e)
482
+ }
483
+
484
+ return result
485
+ }
486
+
487
+ // 解析 interview_agents 结果
488
+ const parseInterview = (text) => {
489
+ const result = {
490
+ topic: '',
491
+ agentCount: '',
492
+ selectionReason: '',
493
+ interviews: [],
494
+ summary: ''
495
+ }
496
+
497
+ try {
498
+ // 提取采访主题
499
+ const topicMatch = text.match(/\*\*采访主题:\*\*\s*(.+?)(?:\n|$)/)
500
+ if (topicMatch) result.topic = topicMatch[1].trim()
501
+
502
+ // 提取采访人数
503
+ const countMatch = text.match(/\*\*采访人数:\*\*\s*(.+?)(?:\n|$)/)
504
+ if (countMatch) result.agentCount = countMatch[1].trim()
505
+
506
+ // 提取选择理由
507
+ const reasonSection = text.match(/### 采访对象选择理由\n([\s\S]*?)(?=---|###|$)/)
508
+ if (reasonSection) {
509
+ result.selectionReason = reasonSection[1].trim().substring(0, 300) + '...'
510
+ }
511
+
512
+ // 提取采访实录
513
+ const interviewMatches = text.matchAll(/#### 采访 #(\d+):\s*(.+?)\n\*\*(.+?)\*\*\s*\((.+?)\)\n_简介:\s*(.+?)_\n\n\*\*Q:\*\*\s*([\s\S]*?)\n\n\*\*A:\*\*\s*([\s\S]*?)(?=\*\*关键引言|\n---|\n####|$)/g)
514
+
515
+ for (const match of interviewMatches) {
516
+ const interview = {
517
+ num: match[1],
518
+ title: match[2].trim(),
519
+ name: match[3].trim(),
520
+ role: match[4].trim(),
521
+ bio: match[5].trim().substring(0, 100) + '...',
522
+ question: match[6].trim().split('\n')[0].substring(0, 150) + '...',
523
+ answer: match[7].trim().substring(0, 500) + '...',
524
+ quotes: []
525
+ }
526
+
527
+ // 提取关键引言
528
+ const quoteSection = text.match(new RegExp(`#### 采访 #${match[1]}[\\s\\S]*?\\*\\*关键引言:\\*\\*\\n([\\s\\S]*?)(?=\\n---)`))
529
+ if (quoteSection) {
530
+ const quotes = quoteSection[1].match(/> "(.+?)"/g)
531
+ if (quotes) {
532
+ interview.quotes = quotes.map(q => q.replace(/^> "|"$/g, '')).slice(0, 2)
533
+ }
534
+ }
535
+
536
+ result.interviews.push(interview)
537
+ }
538
+
539
+ // 提取采访摘要
540
+ const summarySection = text.match(/### 采访摘要与核心观点\n([\s\S]*?)$/)
541
+ if (summarySection) {
542
+ result.summary = summarySection[1].trim().substring(0, 500) + '...'
543
+ }
544
+ } catch (e) {
545
+ console.warn('解析 interview_agents 结果失败:', e)
546
+ }
547
+
548
+ return result
549
+ }
550
+
551
+ // 解析 quick_search 结果
552
+ const parseQuickSearch = (text) => {
553
+ const result = {
554
+ query: '',
555
+ count: 0,
556
+ facts: []
557
+ }
558
+
559
+ try {
560
+ const queryMatch = text.match(/搜索查询:\s*(.+?)(?:\n|$)/)
561
+ if (queryMatch) result.query = queryMatch[1].trim()
562
+
563
+ const countMatch = text.match(/找到\s*(\d+)\s*条/)
564
+ if (countMatch) result.count = parseInt(countMatch[1])
565
+
566
+ const factsSection = text.match(/### 相关事实:\n([\s\S]*)$/)
567
+ if (factsSection) {
568
+ const lines = factsSection[1].split('\n').filter(l => l.match(/^\d+\./))
569
+ result.facts = lines.map(l => l.replace(/^\d+\.\s*/, '').trim()).slice(0, 20)
570
+ }
571
+ } catch (e) {
572
+ console.warn('解析 quick_search 结果失败:', e)
573
+ }
574
+
575
+ return result
576
+ }
577
+
578
+ // ========== 子组件定义 ==========
579
+
580
+ // InsightForge 结果展示组件
581
+ const InsightForgeResult = {
582
+ props: ['result'],
583
+ setup(props) {
584
+ const expanded = ref({ facts: true, entities: false, relations: false, subQueries: false })
585
+ const toggleSection = (section) => { expanded.value[section] = !expanded.value[section] }
586
+ return () => h('div', { class: 'insight-result' }, [
587
+ // 统计卡片
588
+ h('div', { class: 'stats-cards' }, [
589
+ h('div', { class: 'stat-card facts' }, [
590
+ h('span', { class: 'stat-num' }, props.result.stats.facts),
591
+ h('span', { class: 'stat-name' }, '相关事实')
592
+ ]),
593
+ h('div', { class: 'stat-card entities' }, [
594
+ h('span', { class: 'stat-num' }, props.result.stats.entities),
595
+ h('span', { class: 'stat-name' }, '涉及实体')
596
+ ]),
597
+ h('div', { class: 'stat-card relations' }, [
598
+ h('span', { class: 'stat-num' }, props.result.stats.relationships),
599
+ h('span', { class: 'stat-name' }, '关系链')
600
+ ])
601
+ ]),
602
+
603
+ // 子问题
604
+ props.result.subQueries.length > 0 && h('div', { class: 'collapsible-section' }, [
605
+ h('div', { class: 'section-title', onClick: () => toggleSection('subQueries') }, [
606
+ h('span', {}, '📋 分析的子问题'),
607
+ h('span', { class: 'toggle-icon' }, expanded.value.subQueries ? '−' : '+')
608
+ ]),
609
+ expanded.value.subQueries && h('div', { class: 'sub-queries' },
610
+ props.result.subQueries.map((q, i) => h('div', { class: 'sub-query', key: i }, [
611
+ h('span', { class: 'query-num' }, i + 1),
612
+ h('span', { class: 'query-text' }, q)
613
+ ]))
614
+ )
615
+ ]),
616
+
617
+ // 关键事实
618
+ props.result.facts.length > 0 && h('div', { class: 'collapsible-section' }, [
619
+ h('div', { class: 'section-title', onClick: () => toggleSection('facts') }, [
620
+ h('span', {}, `📌 关键事实 (${props.result.facts.length})`),
621
+ h('span', { class: 'toggle-icon' }, expanded.value.facts ? '−' : '+')
622
+ ]),
623
+ expanded.value.facts && h('div', { class: 'facts-list' },
624
+ props.result.facts.map((fact, i) => h('div', { class: 'fact-item', key: i }, [
625
+ h('span', { class: 'fact-num' }, i + 1),
626
+ h('span', { class: 'fact-text' }, fact)
627
+ ]))
628
+ )
629
+ ]),
630
+
631
+ // 核心实体
632
+ props.result.entities.length > 0 && h('div', { class: 'collapsible-section' }, [
633
+ h('div', { class: 'section-title', onClick: () => toggleSection('entities') }, [
634
+ h('span', {}, `👥 核心实体 (${props.result.entities.length})`),
635
+ h('span', { class: 'toggle-icon' }, expanded.value.entities ? '−' : '+')
636
+ ]),
637
+ expanded.value.entities && h('div', { class: 'entities-grid' },
638
+ props.result.entities.map((e, i) => h('div', { class: 'entity-card', key: i }, [
639
+ h('div', { class: 'entity-header' }, [
640
+ h('span', { class: 'entity-name' }, e.name),
641
+ h('span', { class: 'entity-type' }, e.type)
642
+ ]),
643
+ e.summary && h('div', { class: 'entity-summary' }, e.summary.substring(0, 100) + '...')
644
+ ]))
645
+ )
646
+ ]),
647
+
648
+ // 关系链
649
+ props.result.relations.length > 0 && h('div', { class: 'collapsible-section' }, [
650
+ h('div', { class: 'section-title', onClick: () => toggleSection('relations') }, [
651
+ h('span', {}, `🔗 关系链 (${props.result.relations.length})`),
652
+ h('span', { class: 'toggle-icon' }, expanded.value.relations ? '−' : '+')
653
+ ]),
654
+ expanded.value.relations && h('div', { class: 'relations-list' },
655
+ props.result.relations.map((r, i) => h('div', { class: 'relation-item', key: i }, [
656
+ h('span', { class: 'rel-source' }, r.source),
657
+ h('span', { class: 'rel-arrow' }, '→'),
658
+ h('span', { class: 'rel-type' }, r.relation),
659
+ h('span', { class: 'rel-arrow' }, '→'),
660
+ h('span', { class: 'rel-target' }, r.target)
661
+ ]))
662
+ )
663
+ ])
664
+ ])
665
+ }
666
+ }
667
+
668
+ // PanoramaResult 展示组件
669
+ const PanoramaResult = {
670
+ props: ['result'],
671
+ setup(props) {
672
+ const expanded = ref({ active: true, history: false, entities: false })
673
+ const toggleSection = (section) => { expanded.value[section] = !expanded.value[section] }
674
+ return () => h('div', { class: 'panorama-result' }, [
675
+ // 统计卡片
676
+ h('div', { class: 'stats-cards' }, [
677
+ h('div', { class: 'stat-card nodes' }, [
678
+ h('span', { class: 'stat-num' }, props.result.stats.nodes),
679
+ h('span', { class: 'stat-name' }, '总节点')
680
+ ]),
681
+ h('div', { class: 'stat-card edges' }, [
682
+ h('span', { class: 'stat-num' }, props.result.stats.edges),
683
+ h('span', { class: 'stat-name' }, '总边数')
684
+ ]),
685
+ h('div', { class: 'stat-card active' }, [
686
+ h('span', { class: 'stat-num' }, props.result.stats.activeFacts),
687
+ h('span', { class: 'stat-name' }, '有效事实')
688
+ ]),
689
+ h('div', { class: 'stat-card history' }, [
690
+ h('span', { class: 'stat-num' }, props.result.stats.historicalFacts),
691
+ h('span', { class: 'stat-name' }, '历史事实')
692
+ ])
693
+ ]),
694
+
695
+ // 当前有效事实
696
+ props.result.activeFacts.length > 0 && h('div', { class: 'collapsible-section' }, [
697
+ h('div', { class: 'section-title active', onClick: () => toggleSection('active') }, [
698
+ h('span', {}, `✅ 当前有效事实 (${props.result.activeFacts.length})`),
699
+ h('span', { class: 'toggle-icon' }, expanded.value.active ? '−' : '+')
700
+ ]),
701
+ expanded.value.active && h('div', { class: 'facts-list' },
702
+ props.result.activeFacts.map((fact, i) => h('div', { class: 'fact-item active', key: i }, [
703
+ h('span', { class: 'fact-num' }, i + 1),
704
+ h('span', { class: 'fact-text' }, fact)
705
+ ]))
706
+ )
707
+ ]),
708
+
709
+ // 历史事实
710
+ props.result.historicalFacts.length > 0 && h('div', { class: 'collapsible-section' }, [
711
+ h('div', { class: 'section-title history', onClick: () => toggleSection('history') }, [
712
+ h('span', {}, `📜 历史/过期事实 (${props.result.historicalFacts.length})`),
713
+ h('span', { class: 'toggle-icon' }, expanded.value.history ? '−' : '+')
714
+ ]),
715
+ expanded.value.history && h('div', { class: 'facts-list' },
716
+ props.result.historicalFacts.map((fact, i) => h('div', { class: 'fact-item history', key: i }, [
717
+ h('span', { class: 'fact-num' }, i + 1),
718
+ h('div', { class: 'fact-content' }, [
719
+ fact.timeRange && h('span', { class: 'time-range' }, fact.timeRange),
720
+ h('span', { class: 'fact-text' }, fact.content)
721
+ ])
722
+ ]))
723
+ )
724
+ ]),
725
+
726
+ // 涉及实体
727
+ props.result.entities.length > 0 && h('div', { class: 'collapsible-section' }, [
728
+ h('div', { class: 'section-title', onClick: () => toggleSection('entities') }, [
729
+ h('span', {}, `👥 涉及实体 (${props.result.entities.length})`),
730
+ h('span', { class: 'toggle-icon' }, expanded.value.entities ? '−' : '+')
731
+ ]),
732
+ expanded.value.entities && h('div', { class: 'entity-tags' },
733
+ props.result.entities.map((e, i) => h('span', { class: 'entity-tag', key: i }, [
734
+ h('span', { class: 'tag-name' }, e.name),
735
+ h('span', { class: 'tag-type' }, e.type)
736
+ ]))
737
+ )
738
+ ])
739
+ ])
740
+ }
741
+ }
742
+
743
+ // InterviewResult 展示组件
744
+ const InterviewResult = {
745
+ props: ['result'],
746
+ setup(props) {
747
+ const expandedInterview = ref(0)
748
+ return () => h('div', { class: 'interview-result' }, [
749
+ // 采访信息
750
+ h('div', { class: 'interview-header' }, [
751
+ h('div', { class: 'interview-topic' }, props.result.topic),
752
+ h('div', { class: 'interview-count' }, props.result.agentCount)
753
+ ]),
754
+
755
+ // 采访列表
756
+ props.result.interviews.length > 0 && h('div', { class: 'interviews-list' },
757
+ props.result.interviews.map((interview, i) => h('div', {
758
+ class: ['interview-card', { expanded: expandedInterview.value === i }],
759
+ key: i,
760
+ onClick: () => { expandedInterview.value = expandedInterview.value === i ? -1 : i }
761
+ }, [
762
+ h('div', { class: 'interview-card-header' }, [
763
+ h('span', { class: 'interview-num' }, `#${interview.num}`),
764
+ h('span', { class: 'interview-name' }, interview.name),
765
+ h('span', { class: 'interview-role' }, interview.role),
766
+ h('span', { class: 'expand-icon' }, expandedInterview.value === i ? '−' : '+')
767
+ ]),
768
+ expandedInterview.value === i && h('div', { class: 'interview-card-body' }, [
769
+ h('div', { class: 'interview-bio' }, interview.bio),
770
+ h('div', { class: 'interview-qa' }, [
771
+ h('div', { class: 'qa-question' }, [
772
+ h('span', { class: 'qa-label' }, 'Q:'),
773
+ h('span', {}, interview.question)
774
+ ]),
775
+ h('div', { class: 'qa-answer' }, [
776
+ h('span', { class: 'qa-label' }, 'A:'),
777
+ h('span', {}, interview.answer)
778
+ ])
779
+ ]),
780
+ interview.quotes.length > 0 && h('div', { class: 'interview-quotes' },
781
+ interview.quotes.map((q, qi) => h('div', { class: 'quote-item', key: qi }, `"${q}"`))
782
+ )
783
+ ])
784
+ ]))
785
+ ),
786
+
787
+ // 摘要
788
+ props.result.summary && h('div', { class: 'interview-summary' }, [
789
+ h('div', { class: 'summary-title' }, '📋 核心观点摘要'),
790
+ h('div', { class: 'summary-content' }, props.result.summary)
791
+ ])
792
+ ])
793
+ }
794
+ }
795
+
796
+ // QuickSearchResult 展示组件
797
+ const QuickSearchResult = {
798
+ props: ['result'],
799
+ setup(props) {
800
+ return () => h('div', { class: 'quick-search-result' }, [
801
+ h('div', { class: 'search-header' }, [
802
+ h('span', { class: 'search-query' }, props.result.query),
803
+ h('span', { class: 'search-count' }, `${props.result.count} 条结果`)
804
+ ]),
805
+ props.result.facts.length > 0 && h('div', { class: 'search-facts' },
806
+ props.result.facts.map((fact, i) => h('div', { class: 'search-fact-item', key: i }, [
807
+ h('span', { class: 'fact-num' }, i + 1),
808
+ h('span', { class: 'fact-text' }, fact)
809
+ ]))
810
+ )
811
+ ])
812
+ }
813
+ }
814
 
815
  // Computed
816
  const statusClass = computed(() => {
 
1825
  .log-msg.error { color: #EF5350; }
1826
  .log-msg.warning { color: #FFA726; }
1827
  .log-msg.success { color: #66BB6A; }
1828
+
1829
+ /* ========== 工具结果结构化展示样式 ========== */
1830
+
1831
+ /* 切换原文按钮 */
1832
+ .toggle-raw-btn {
1833
+ background: transparent;
1834
+ border: 1px solid #DDD;
1835
+ border-radius: 4px;
1836
+ padding: 2px 8px;
1837
+ font-size: 10px;
1838
+ color: #666;
1839
+ cursor: pointer;
1840
+ transition: all 0.2s;
1841
+ }
1842
+
1843
+ .toggle-raw-btn:hover {
1844
+ background: #F5F5F5;
1845
+ border-color: #CCC;
1846
+ }
1847
+
1848
+ /* 原文展示 */
1849
+ .result-raw {
1850
+ margin-top: 10px;
1851
+ }
1852
+
1853
+ .result-raw pre {
1854
+ margin: 0;
1855
+ font-family: 'JetBrains Mono', monospace;
1856
+ font-size: 11px;
1857
+ white-space: pre-wrap;
1858
+ word-break: break-word;
1859
+ background: rgba(0, 0, 0, 0.03);
1860
+ padding: 12px;
1861
+ border-radius: 4px;
1862
+ max-height: 400px;
1863
+ overflow-y: auto;
1864
+ }
1865
+
1866
+ /* 工具类型特定背景 */
1867
+ .tool-result-block.tool-insight_forge {
1868
+ background: linear-gradient(135deg, #FFF8E1 0%, #FFF3E0 100%);
1869
+ border-color: #FFE082;
1870
+ }
1871
+
1872
+ .tool-result-block.tool-panorama_search {
1873
+ background: linear-gradient(135deg, #E3F2FD 0%, #E1F5FE 100%);
1874
+ border-color: #90CAF9;
1875
+ }
1876
+
1877
+ .tool-result-block.tool-interview_agents {
1878
+ background: linear-gradient(135deg, #F3E5F5 0%, #FCE4EC 100%);
1879
+ border-color: #CE93D8;
1880
+ }
1881
+
1882
+ .tool-result-block.tool-quick_search {
1883
+ background: linear-gradient(135deg, #E8F5E9 0%, #F1F8E9 100%);
1884
+ border-color: #A5D6A7;
1885
+ }
1886
+
1887
+ /* 统计卡片 */
1888
+ .stats-cards {
1889
+ display: flex;
1890
+ gap: 8px;
1891
+ margin-bottom: 12px;
1892
+ flex-wrap: wrap;
1893
+ }
1894
+
1895
+ .stat-card {
1896
+ display: flex;
1897
+ flex-direction: column;
1898
+ align-items: center;
1899
+ padding: 8px 12px;
1900
+ background: #FFF;
1901
+ border-radius: 8px;
1902
+ box-shadow: 0 1px 3px rgba(0,0,0,0.08);
1903
+ min-width: 60px;
1904
+ }
1905
+
1906
+ .stat-card .stat-num {
1907
+ font-size: 18px;
1908
+ font-weight: 700;
1909
+ color: #333;
1910
+ font-family: 'JetBrains Mono', monospace;
1911
+ }
1912
+
1913
+ .stat-card .stat-name {
1914
+ font-size: 10px;
1915
+ color: #888;
1916
+ margin-top: 2px;
1917
+ }
1918
+
1919
+ .stat-card.facts .stat-num { color: #E65100; }
1920
+ .stat-card.entities .stat-num { color: #7B1FA2; }
1921
+ .stat-card.relations .stat-num { color: #1565C0; }
1922
+ .stat-card.nodes .stat-num { color: #1976D2; }
1923
+ .stat-card.edges .stat-num { color: #00838F; }
1924
+ .stat-card.active .stat-num { color: #2E7D32; }
1925
+ .stat-card.history .stat-num { color: #795548; }
1926
+
1927
+ /* 可折叠区块 */
1928
+ .collapsible-section {
1929
+ margin-top: 10px;
1930
+ background: rgba(255,255,255,0.7);
1931
+ border-radius: 6px;
1932
+ overflow: hidden;
1933
+ }
1934
+
1935
+ .section-title {
1936
+ display: flex;
1937
+ justify-content: space-between;
1938
+ align-items: center;
1939
+ padding: 8px 12px;
1940
+ background: rgba(0,0,0,0.03);
1941
+ cursor: pointer;
1942
+ font-size: 12px;
1943
+ font-weight: 600;
1944
+ color: #555;
1945
+ transition: background 0.2s;
1946
+ }
1947
+
1948
+ .section-title:hover {
1949
+ background: rgba(0,0,0,0.06);
1950
+ }
1951
+
1952
+ .section-title.active { color: #2E7D32; }
1953
+ .section-title.history { color: #795548; }
1954
+
1955
+ .toggle-icon {
1956
+ width: 18px;
1957
+ height: 18px;
1958
+ display: flex;
1959
+ align-items: center;
1960
+ justify-content: center;
1961
+ background: rgba(0,0,0,0.08);
1962
+ border-radius: 4px;
1963
+ font-size: 14px;
1964
+ }
1965
+
1966
+ /* 子问题列表 */
1967
+ .sub-queries {
1968
+ padding: 8px 12px;
1969
+ }
1970
+
1971
+ .sub-query {
1972
+ display: flex;
1973
+ gap: 8px;
1974
+ padding: 6px 0;
1975
+ border-bottom: 1px dashed #EEE;
1976
+ }
1977
+
1978
+ .sub-query:last-child {
1979
+ border-bottom: none;
1980
+ }
1981
+
1982
+ .query-num {
1983
+ width: 20px;
1984
+ height: 20px;
1985
+ display: flex;
1986
+ align-items: center;
1987
+ justify-content: center;
1988
+ background: #E3F2FD;
1989
+ color: #1565C0;
1990
+ border-radius: 50%;
1991
+ font-size: 11px;
1992
+ font-weight: 600;
1993
+ flex-shrink: 0;
1994
+ }
1995
+
1996
+ .query-text {
1997
+ font-size: 12px;
1998
+ color: #444;
1999
+ line-height: 1.5;
2000
+ }
2001
+
2002
+ /* 事实列表 */
2003
+ .facts-list {
2004
+ padding: 8px 12px;
2005
+ max-height: 300px;
2006
+ overflow-y: auto;
2007
+ }
2008
+
2009
+ .fact-item {
2010
+ display: flex;
2011
+ gap: 8px;
2012
+ padding: 6px 0;
2013
+ border-bottom: 1px solid #F0F0F0;
2014
+ }
2015
+
2016
+ .fact-item:last-child {
2017
+ border-bottom: none;
2018
+ }
2019
+
2020
+ .fact-item.active {
2021
+ background: rgba(46, 125, 50, 0.05);
2022
+ margin: 0 -12px;
2023
+ padding: 6px 12px;
2024
+ }
2025
+
2026
+ .fact-item.history {
2027
+ background: rgba(121, 85, 72, 0.05);
2028
+ margin: 0 -12px;
2029
+ padding: 6px 12px;
2030
+ }
2031
+
2032
+ .fact-num {
2033
+ min-width: 20px;
2034
+ height: 20px;
2035
+ display: flex;
2036
+ align-items: center;
2037
+ justify-content: center;
2038
+ background: #F5F5F5;
2039
+ color: #888;
2040
+ border-radius: 4px;
2041
+ font-size: 10px;
2042
+ font-weight: 600;
2043
+ flex-shrink: 0;
2044
+ }
2045
+
2046
+ .fact-text {
2047
+ font-size: 12px;
2048
+ color: #444;
2049
+ line-height: 1.5;
2050
+ }
2051
+
2052
+ .fact-content {
2053
+ display: flex;
2054
+ flex-direction: column;
2055
+ gap: 4px;
2056
+ }
2057
+
2058
+ .time-range {
2059
+ font-size: 10px;
2060
+ color: #888;
2061
+ font-family: 'JetBrains Mono', monospace;
2062
+ }
2063
+
2064
+ /* 实体网格 */
2065
+ .entities-grid {
2066
+ display: grid;
2067
+ grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
2068
+ gap: 8px;
2069
+ padding: 8px 12px;
2070
+ }
2071
+
2072
+ .entity-card {
2073
+ background: #FFF;
2074
+ border: 1px solid #EEE;
2075
+ border-radius: 6px;
2076
+ padding: 8px 10px;
2077
+ }
2078
+
2079
+ .entity-header {
2080
+ display: flex;
2081
+ justify-content: space-between;
2082
+ align-items: center;
2083
+ margin-bottom: 4px;
2084
+ }
2085
+
2086
+ .entity-name {
2087
+ font-size: 12px;
2088
+ font-weight: 600;
2089
+ color: #333;
2090
+ }
2091
+
2092
+ .entity-type {
2093
+ font-size: 10px;
2094
+ color: #7B1FA2;
2095
+ background: #F3E5F5;
2096
+ padding: 2px 6px;
2097
+ border-radius: 10px;
2098
+ }
2099
+
2100
+ .entity-summary {
2101
+ font-size: 11px;
2102
+ color: #666;
2103
+ line-height: 1.4;
2104
+ }
2105
+
2106
+ /* 实体标签 */
2107
+ .entity-tags {
2108
+ display: flex;
2109
+ flex-wrap: wrap;
2110
+ gap: 6px;
2111
+ padding: 8px 12px;
2112
+ }
2113
+
2114
+ .entity-tag {
2115
+ display: flex;
2116
+ align-items: center;
2117
+ gap: 4px;
2118
+ background: #FFF;
2119
+ border: 1px solid #EEE;
2120
+ border-radius: 15px;
2121
+ padding: 4px 10px;
2122
+ }
2123
+
2124
+ .tag-name {
2125
+ font-size: 11px;
2126
+ font-weight: 500;
2127
+ color: #333;
2128
+ }
2129
+
2130
+ .tag-type {
2131
+ font-size: 9px;
2132
+ color: #888;
2133
+ }
2134
+
2135
+ /* 关系链 */
2136
+ .relations-list {
2137
+ padding: 8px 12px;
2138
+ }
2139
+
2140
+ .relation-item {
2141
+ display: flex;
2142
+ align-items: center;
2143
+ gap: 6px;
2144
+ padding: 6px 0;
2145
+ border-bottom: 1px solid #F0F0F0;
2146
+ flex-wrap: wrap;
2147
+ }
2148
+
2149
+ .relation-item:last-child {
2150
+ border-bottom: none;
2151
+ }
2152
+
2153
+ .rel-source, .rel-target {
2154
+ font-size: 11px;
2155
+ font-weight: 500;
2156
+ color: #333;
2157
+ background: #E3F2FD;
2158
+ padding: 2px 8px;
2159
+ border-radius: 4px;
2160
+ }
2161
+
2162
+ .rel-arrow {
2163
+ font-size: 12px;
2164
+ color: #999;
2165
+ }
2166
+
2167
+ .rel-type {
2168
+ font-size: 10px;
2169
+ color: #FFF;
2170
+ background: #1565C0;
2171
+ padding: 2px 8px;
2172
+ border-radius: 10px;
2173
+ }
2174
+
2175
+ /* 采访结果 */
2176
+ .interview-result {
2177
+ padding: 8px 0;
2178
+ }
2179
+
2180
+ .interview-header {
2181
+ display: flex;
2182
+ flex-direction: column;
2183
+ gap: 4px;
2184
+ padding: 8px 12px;
2185
+ background: rgba(255,255,255,0.7);
2186
+ border-radius: 6px;
2187
+ margin-bottom: 10px;
2188
+ }
2189
+
2190
+ .interview-topic {
2191
+ font-size: 13px;
2192
+ font-weight: 600;
2193
+ color: #333;
2194
+ }
2195
+
2196
+ .interview-count {
2197
+ font-size: 11px;
2198
+ color: #7B1FA2;
2199
+ }
2200
+
2201
+ .interviews-list {
2202
+ display: flex;
2203
+ flex-direction: column;
2204
+ gap: 8px;
2205
+ }
2206
+
2207
+ .interview-card {
2208
+ background: #FFF;
2209
+ border: 1px solid #EEE;
2210
+ border-radius: 6px;
2211
+ cursor: pointer;
2212
+ transition: all 0.2s;
2213
+ }
2214
+
2215
+ .interview-card:hover {
2216
+ border-color: #DDD;
2217
+ box-shadow: 0 2px 8px rgba(0,0,0,0.05);
2218
+ }
2219
+
2220
+ .interview-card.expanded {
2221
+ border-color: #CE93D8;
2222
+ }
2223
+
2224
+ .interview-card-header {
2225
+ display: flex;
2226
+ align-items: center;
2227
+ gap: 8px;
2228
+ padding: 10px 12px;
2229
+ }
2230
+
2231
+ .interview-num {
2232
+ font-size: 11px;
2233
+ font-weight: 600;
2234
+ color: #7B1FA2;
2235
+ background: #F3E5F5;
2236
+ padding: 2px 6px;
2237
+ border-radius: 4px;
2238
+ }
2239
+
2240
+ .interview-name {
2241
+ font-size: 12px;
2242
+ font-weight: 600;
2243
+ color: #333;
2244
+ }
2245
+
2246
+ .interview-role {
2247
+ font-size: 10px;
2248
+ color: #888;
2249
+ background: #F5F5F5;
2250
+ padding: 2px 8px;
2251
+ border-radius: 10px;
2252
+ }
2253
+
2254
+ .expand-icon {
2255
+ margin-left: auto;
2256
+ width: 20px;
2257
+ height: 20px;
2258
+ display: flex;
2259
+ align-items: center;
2260
+ justify-content: center;
2261
+ background: #F5F5F5;
2262
+ border-radius: 4px;
2263
+ font-size: 14px;
2264
+ color: #666;
2265
+ }
2266
+
2267
+ .interview-card-body {
2268
+ padding: 0 12px 12px;
2269
+ border-top: 1px solid #F0F0F0;
2270
+ }
2271
+
2272
+ .interview-bio {
2273
+ font-size: 11px;
2274
+ color: #888;
2275
+ font-style: italic;
2276
+ padding: 8px 0;
2277
+ }
2278
+
2279
+ .interview-qa {
2280
+ display: flex;
2281
+ flex-direction: column;
2282
+ gap: 8px;
2283
+ }
2284
+
2285
+ .qa-question, .qa-answer {
2286
+ font-size: 11px;
2287
+ line-height: 1.5;
2288
+ }
2289
+
2290
+ .qa-label {
2291
+ font-weight: 600;
2292
+ color: #7B1FA2;
2293
+ margin-right: 4px;
2294
+ }
2295
+
2296
+ .qa-question {
2297
+ color: #555;
2298
+ background: #FAFAFA;
2299
+ padding: 6px 8px;
2300
+ border-radius: 4px;
2301
+ }
2302
+
2303
+ .qa-answer {
2304
+ color: #333;
2305
+ }
2306
+
2307
+ .interview-quotes {
2308
+ margin-top: 8px;
2309
+ padding-top: 8px;
2310
+ border-top: 1px dashed #EEE;
2311
+ }
2312
+
2313
+ .quote-item {
2314
+ font-size: 11px;
2315
+ color: #666;
2316
+ font-style: italic;
2317
+ padding: 4px 0 4px 12px;
2318
+ border-left: 2px solid #CE93D8;
2319
+ margin-bottom: 4px;
2320
+ }
2321
+
2322
+ .interview-summary {
2323
+ margin-top: 10px;
2324
+ background: rgba(255,255,255,0.7);
2325
+ border-radius: 6px;
2326
+ padding: 10px 12px;
2327
+ }
2328
+
2329
+ .summary-title {
2330
+ font-size: 12px;
2331
+ font-weight: 600;
2332
+ color: #555;
2333
+ margin-bottom: 6px;
2334
+ }
2335
+
2336
+ .summary-content {
2337
+ font-size: 11px;
2338
+ color: #666;
2339
+ line-height: 1.5;
2340
+ }
2341
+
2342
+ /* 快速搜索结果 */
2343
+ .quick-search-result {
2344
+ padding: 8px 0;
2345
+ }
2346
+
2347
+ .search-header {
2348
+ display: flex;
2349
+ justify-content: space-between;
2350
+ align-items: center;
2351
+ padding: 8px 12px;
2352
+ background: rgba(255,255,255,0.7);
2353
+ border-radius: 6px;
2354
+ margin-bottom: 10px;
2355
+ }
2356
+
2357
+ .search-query {
2358
+ font-size: 12px;
2359
+ font-weight: 600;
2360
+ color: #333;
2361
+ }
2362
+
2363
+ .search-count {
2364
+ font-size: 11px;
2365
+ color: #2E7D32;
2366
+ background: #E8F5E9;
2367
+ padding: 2px 8px;
2368
+ border-radius: 10px;
2369
+ }
2370
+
2371
+ .search-facts {
2372
+ padding: 0 12px;
2373
+ }
2374
+
2375
+ .search-fact-item {
2376
+ display: flex;
2377
+ gap: 8px;
2378
+ padding: 6px 0;
2379
+ border-bottom: 1px solid #F0F0F0;
2380
+ }
2381
+
2382
+ .search-fact-item:last-child {
2383
+ border-bottom: none;
2384
+ }
2385
  </style>