feat(i18n): replace hardcoded Chinese in frontend components with i18n calls
Replace all user-visible hardcoded Chinese strings in 7 component files with $t() / t() calls using vue-i18n: - Step1GraphBuild: ontology generation, graph build, status badges - Step2EnvSetup: simulation setup, agent personas, platform config, time config, initial activation, modal profile details - Step3Simulation: report generation button - Step4Report: section loading text, interaction button - Step5Interaction: chat interface, survey UI, tool descriptions, error messages, agent selection - GraphPanel: graph status hints, loading states, tooltips - HistoryDatabase: history cards, modal, replay buttons Added missing translation keys to both zh.json and en.json locale files. Added useI18n imports to components that need script-level translations.
This commit is contained in:
parent
70833821a2
commit
fc47ae81b5
|
|
@ -1,14 +1,14 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="graph-panel">
|
<div class="graph-panel">
|
||||||
<div class="panel-header">
|
<div class="panel-header">
|
||||||
<span class="panel-title">Graph Relationship Visualization</span>
|
<span class="panel-title">{{ $t('graph.panelTitle') }}</span>
|
||||||
<!-- 顶部工具栏 (Internal Top Right) -->
|
<!-- 顶部工具栏 (Internal Top Right) -->
|
||||||
<div class="header-tools">
|
<div class="header-tools">
|
||||||
<button class="tool-btn" @click="$emit('refresh')" :disabled="loading" title="刷新图谱">
|
<button class="tool-btn" @click="$emit('refresh')" :disabled="loading" :title="$t('graph.refreshGraph')">
|
||||||
<span class="icon-refresh" :class="{ 'spinning': loading }">↻</span>
|
<span class="icon-refresh" :class="{ 'spinning': loading }">↻</span>
|
||||||
<span class="btn-text">Refresh</span>
|
<span class="btn-text">Refresh</span>
|
||||||
</button>
|
</button>
|
||||||
<button class="tool-btn" @click="$emit('toggle-maximize')" title="最大化/还原">
|
<button class="tool-btn" @click="$emit('toggle-maximize')" :title="$t('graph.toggleMaximize')">
|
||||||
<span class="icon-maximize">⛶</span>
|
<span class="icon-maximize">⛶</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -27,7 +27,7 @@
|
||||||
<path d="M14.5 2A2.5 2.5 0 0 0 12 4.5v15a2.5 2.5 0 0 0 4.96.44 2.5 2.5 0 0 0 2.96-3.08 3 3 0 0 0 .34-5.58 2.5 2.5 0 0 0-1.32-4.24 2.5 2.5 0 0 0-4.44-4.04z" />
|
<path d="M14.5 2A2.5 2.5 0 0 0 12 4.5v15a2.5 2.5 0 0 0 4.96.44 2.5 2.5 0 0 0 2.96-3.08 3 3 0 0 0 .34-5.58 2.5 2.5 0 0 0-1.32-4.24 2.5 2.5 0 0 0-4.44-4.04z" />
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
{{ isSimulating ? 'GraphRAG长短期记忆实时更新中' : '实时更新中...' }}
|
{{ isSimulating ? $t('graph.graphMemoryRealtime') : $t('graph.realtimeUpdating') }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 模拟结束后的提示 -->
|
<!-- 模拟结束后的提示 -->
|
||||||
|
|
@ -39,8 +39,8 @@
|
||||||
<line x1="12" y1="8" x2="12.01" y2="8"></line>
|
<line x1="12" y1="8" x2="12.01" y2="8"></line>
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
<span class="hint-text">还有少量内容处理中,建议稍后手动刷新图谱</span>
|
<span class="hint-text">{{ $t('graph.pendingContentHint') }}</span>
|
||||||
<button class="hint-close-btn" @click="dismissFinishedHint" title="关闭提示">
|
<button class="hint-close-btn" @click="dismissFinishedHint" :title="$t('graph.closeHint')">
|
||||||
<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2">
|
<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2">
|
||||||
<line x1="18" y1="6" x2="6" y2="18"></line>
|
<line x1="18" y1="6" x2="6" y2="18"></line>
|
||||||
<line x1="6" y1="6" x2="18" y2="18"></line>
|
<line x1="6" y1="6" x2="18" y2="18"></line>
|
||||||
|
|
@ -51,7 +51,7 @@
|
||||||
<!-- 节点/边详情面板 -->
|
<!-- 节点/边详情面板 -->
|
||||||
<div v-if="selectedItem" class="detail-panel">
|
<div v-if="selectedItem" class="detail-panel">
|
||||||
<div class="detail-panel-header">
|
<div class="detail-panel-header">
|
||||||
<span class="detail-title">{{ selectedItem.type === 'node' ? 'Node Details' : 'Relationship' }}</span>
|
<span class="detail-title">{{ selectedItem.type === 'node' ? $t('graph.nodeDetails') : $t('graph.relationship') }}</span>
|
||||||
<span v-if="selectedItem.type === 'node'" class="detail-type-badge" :style="{ background: selectedItem.color, color: '#fff' }">
|
<span v-if="selectedItem.type === 'node'" class="detail-type-badge" :style="{ background: selectedItem.color, color: '#fff' }">
|
||||||
{{ selectedItem.entityType }}
|
{{ selectedItem.entityType }}
|
||||||
</span>
|
</span>
|
||||||
|
|
@ -203,13 +203,13 @@
|
||||||
<!-- 加载状态 -->
|
<!-- 加载状态 -->
|
||||||
<div v-else-if="loading" class="graph-state">
|
<div v-else-if="loading" class="graph-state">
|
||||||
<div class="loading-spinner"></div>
|
<div class="loading-spinner"></div>
|
||||||
<p>图谱数据加载中...</p>
|
<p>{{ $t('graph.graphDataLoading') }}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 等待/空状态 -->
|
<!-- 等待/空状态 -->
|
||||||
<div v-else class="graph-state">
|
<div v-else class="graph-state">
|
||||||
<div class="empty-icon">❖</div>
|
<div class="empty-icon">❖</div>
|
||||||
<p class="empty-text">等待本体生成...</p>
|
<p class="empty-text">{{ $t('graph.waitingOntology') }}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@
|
||||||
<!-- 标题区域 -->
|
<!-- 标题区域 -->
|
||||||
<div class="section-header">
|
<div class="section-header">
|
||||||
<div class="section-line"></div>
|
<div class="section-line"></div>
|
||||||
<span class="section-title">推演记录</span>
|
<span class="section-title">{{ $t('history.title') }}</span>
|
||||||
<div class="section-line"></div>
|
<div class="section-line"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -36,16 +36,16 @@
|
||||||
<span
|
<span
|
||||||
class="status-icon"
|
class="status-icon"
|
||||||
:class="{ available: project.project_id, unavailable: !project.project_id }"
|
:class="{ available: project.project_id, unavailable: !project.project_id }"
|
||||||
title="图谱构建"
|
:title="$t('history.graphBuild')"
|
||||||
>◇</span>
|
>◇</span>
|
||||||
<span
|
<span
|
||||||
class="status-icon available"
|
class="status-icon available"
|
||||||
title="环境搭建"
|
:title="$t('history.envSetup')"
|
||||||
>◈</span>
|
>◈</span>
|
||||||
<span
|
<span
|
||||||
class="status-icon"
|
class="status-icon"
|
||||||
:class="{ available: project.report_id, unavailable: !project.report_id }"
|
:class="{ available: project.report_id, unavailable: !project.report_id }"
|
||||||
title="分析报告"
|
:title="$t('history.analysisReport')"
|
||||||
>◆</span>
|
>◆</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -67,13 +67,13 @@
|
||||||
</div>
|
</div>
|
||||||
<!-- 如果有更多文件,显示提示 -->
|
<!-- 如果有更多文件,显示提示 -->
|
||||||
<div v-if="project.files.length > 3" class="files-more">
|
<div v-if="project.files.length > 3" class="files-more">
|
||||||
+{{ project.files.length - 3 }} 个文件
|
{{ $t('history.moreFiles', { count: project.files.length - 3 }) }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- 无文件时的占位 -->
|
<!-- 无文件时的占位 -->
|
||||||
<div class="files-empty" v-else>
|
<div class="files-empty" v-else>
|
||||||
<span class="empty-file-icon">◇</span>
|
<span class="empty-file-icon">◇</span>
|
||||||
<span class="empty-file-text">暂无文件</span>
|
<span class="empty-file-text">{{ $t('history.noFiles') }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -102,7 +102,7 @@
|
||||||
<!-- 加载状态 -->
|
<!-- 加载状态 -->
|
||||||
<div v-if="loading" class="loading-state">
|
<div v-if="loading" class="loading-state">
|
||||||
<span class="loading-spinner"></span>
|
<span class="loading-spinner"></span>
|
||||||
<span class="loading-text">加载中...</span>
|
<span class="loading-text">{{ $t('history.loadingText') }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 历史回放详情弹窗 -->
|
<!-- 历史回放详情弹窗 -->
|
||||||
|
|
@ -126,27 +126,27 @@
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<!-- 模拟需求 -->
|
<!-- 模拟需求 -->
|
||||||
<div class="modal-section">
|
<div class="modal-section">
|
||||||
<div class="modal-label">模拟需求</div>
|
<div class="modal-label">{{ $t('history.simRequirement') }}</div>
|
||||||
<div class="modal-requirement">{{ selectedProject.simulation_requirement || '无' }}</div>
|
<div class="modal-requirement">{{ selectedProject.simulation_requirement || $t('common.none') }}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 文件列表 -->
|
<!-- 文件列表 -->
|
||||||
<div class="modal-section">
|
<div class="modal-section">
|
||||||
<div class="modal-label">关联文件</div>
|
<div class="modal-label">{{ $t('history.relatedFiles') }}</div>
|
||||||
<div class="modal-files" v-if="selectedProject.files && selectedProject.files.length > 0">
|
<div class="modal-files" v-if="selectedProject.files && selectedProject.files.length > 0">
|
||||||
<div v-for="(file, index) in selectedProject.files" :key="index" class="modal-file-item">
|
<div v-for="(file, index) in selectedProject.files" :key="index" class="modal-file-item">
|
||||||
<span class="file-tag" :class="getFileType(file.filename)">{{ getFileTypeLabel(file.filename) }}</span>
|
<span class="file-tag" :class="getFileType(file.filename)">{{ getFileTypeLabel(file.filename) }}</span>
|
||||||
<span class="modal-file-name">{{ file.filename }}</span>
|
<span class="modal-file-name">{{ file.filename }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-empty" v-else>暂无关联文件</div>
|
<div class="modal-empty" v-else>{{ $t('history.noRelatedFiles') }}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 推演回放分割线 -->
|
<!-- 推演回放分割线 -->
|
||||||
<div class="modal-divider">
|
<div class="modal-divider">
|
||||||
<span class="divider-line"></span>
|
<span class="divider-line"></span>
|
||||||
<span class="divider-text">推演回放</span>
|
<span class="divider-text">{{ $t('history.replayTitle') }}</span>
|
||||||
<span class="divider-line"></span>
|
<span class="divider-line"></span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -159,7 +159,7 @@
|
||||||
>
|
>
|
||||||
<span class="btn-step">Step1</span>
|
<span class="btn-step">Step1</span>
|
||||||
<span class="btn-icon">◇</span>
|
<span class="btn-icon">◇</span>
|
||||||
<span class="btn-text">图谱构建</span>
|
<span class="btn-text">{{ $t('history.step1Button') }}</span>
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
class="modal-btn btn-simulation"
|
class="modal-btn btn-simulation"
|
||||||
|
|
@ -167,7 +167,7 @@
|
||||||
>
|
>
|
||||||
<span class="btn-step">Step2</span>
|
<span class="btn-step">Step2</span>
|
||||||
<span class="btn-icon">◈</span>
|
<span class="btn-icon">◈</span>
|
||||||
<span class="btn-text">环境搭建</span>
|
<span class="btn-text">{{ $t('history.step2Button') }}</span>
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
class="modal-btn btn-report"
|
class="modal-btn btn-report"
|
||||||
|
|
@ -176,12 +176,12 @@
|
||||||
>
|
>
|
||||||
<span class="btn-step">Step4</span>
|
<span class="btn-step">Step4</span>
|
||||||
<span class="btn-icon">◆</span>
|
<span class="btn-icon">◆</span>
|
||||||
<span class="btn-text">分析报告</span>
|
<span class="btn-text">{{ $t('history.step4Button') }}</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<!-- 不可回放提示 -->
|
<!-- 不可回放提示 -->
|
||||||
<div class="modal-playback-hint">
|
<div class="modal-playback-hint">
|
||||||
<span class="hint-text">Step3「开始模拟」与 Step5「深度互动」需在运行中启动,不支持历史回放</span>
|
<span class="hint-text">{{ $t('history.replayHint') }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -193,10 +193,12 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, computed, onMounted, onUnmounted, onActivated, watch, nextTick } from 'vue'
|
import { ref, computed, onMounted, onUnmounted, onActivated, watch, nextTick } from 'vue'
|
||||||
import { useRouter, useRoute } from 'vue-router'
|
import { useRouter, useRoute } from 'vue-router'
|
||||||
|
import { useI18n } from 'vue-i18n'
|
||||||
import { getSimulationHistory } from '../api/simulation'
|
import { getSimulationHistory } from '../api/simulation'
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
|
const { t } = useI18n()
|
||||||
|
|
||||||
// 状态
|
// 状态
|
||||||
const projects = ref([])
|
const projects = ref([])
|
||||||
|
|
@ -337,7 +339,7 @@ const truncateText = (text, maxLength) => {
|
||||||
|
|
||||||
// 从模拟需求生成标题(取前20字)
|
// 从模拟需求生成标题(取前20字)
|
||||||
const getSimulationTitle = (requirement) => {
|
const getSimulationTitle = (requirement) => {
|
||||||
if (!requirement) return '未命名模拟'
|
if (!requirement) return t('history.untitledSimulation')
|
||||||
const title = requirement.slice(0, 20)
|
const title = requirement.slice(0, 20)
|
||||||
return requirement.length > 20 ? title + '...' : title
|
return requirement.length > 20 ? title + '...' : title
|
||||||
}
|
}
|
||||||
|
|
@ -353,8 +355,8 @@ const formatSimulationId = (simulationId) => {
|
||||||
const formatRounds = (simulation) => {
|
const formatRounds = (simulation) => {
|
||||||
const current = simulation.current_round || 0
|
const current = simulation.current_round || 0
|
||||||
const total = simulation.total_rounds || 0
|
const total = simulation.total_rounds || 0
|
||||||
if (total === 0) return '未开始'
|
if (total === 0) return t('history.notStarted')
|
||||||
return `${current}/${total} 轮`
|
return t('history.roundsProgress', { current, total })
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取文件类型(用于样式)
|
// 获取文件类型(用于样式)
|
||||||
|
|
@ -382,7 +384,7 @@ const getFileTypeLabel = (filename) => {
|
||||||
|
|
||||||
// 截断文件名(保留扩展名)
|
// 截断文件名(保留扩展名)
|
||||||
const truncateFilename = (filename, maxLength) => {
|
const truncateFilename = (filename, maxLength) => {
|
||||||
if (!filename) return '未知文件'
|
if (!filename) return t('history.unknownFile')
|
||||||
if (filename.length <= maxLength) return filename
|
if (filename.length <= maxLength) return filename
|
||||||
|
|
||||||
const ext = filename.includes('.') ? '.' + filename.split('.').pop() : ''
|
const ext = filename.includes('.') ? '.' + filename.split('.').pop() : ''
|
||||||
|
|
|
||||||
|
|
@ -6,25 +6,25 @@
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
<div class="step-info">
|
<div class="step-info">
|
||||||
<span class="step-num">01</span>
|
<span class="step-num">01</span>
|
||||||
<span class="step-title">本体生成</span>
|
<span class="step-title">{{ $t('step1.ontologyGeneration') }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="step-status">
|
<div class="step-status">
|
||||||
<span v-if="currentPhase > 0" class="badge success">已完成</span>
|
<span v-if="currentPhase > 0" class="badge success">{{ $t('step1.ontologyCompleted') }}</span>
|
||||||
<span v-else-if="currentPhase === 0" class="badge processing">生成中</span>
|
<span v-else-if="currentPhase === 0" class="badge processing">{{ $t('step1.ontologyGenerating') }}</span>
|
||||||
<span v-else class="badge pending">等待</span>
|
<span v-else class="badge pending">{{ $t('step1.ontologyPending') }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
<p class="api-note">POST /api/graph/ontology/generate</p>
|
<p class="api-note">POST /api/graph/ontology/generate</p>
|
||||||
<p class="description">
|
<p class="description">
|
||||||
LLM分析文档内容与模拟需求,提取出现实种子,自动生成合适的本体结构
|
{{ $t('step1.ontologyDesc') }}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<!-- Loading / Progress -->
|
<!-- Loading / Progress -->
|
||||||
<div v-if="currentPhase === 0 && ontologyProgress" class="progress-section">
|
<div v-if="currentPhase === 0 && ontologyProgress" class="progress-section">
|
||||||
<div class="spinner-sm"></div>
|
<div class="spinner-sm"></div>
|
||||||
<span>{{ ontologyProgress.message || '正在分析文档...' }}</span>
|
<span>{{ ontologyProgress.message || $t('step1.analyzingDocs') }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Detail Overlay -->
|
<!-- Detail Overlay -->
|
||||||
|
|
@ -110,34 +110,34 @@
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
<div class="step-info">
|
<div class="step-info">
|
||||||
<span class="step-num">02</span>
|
<span class="step-num">02</span>
|
||||||
<span class="step-title">GraphRAG构建</span>
|
<span class="step-title">{{ $t('step1.graphRagBuild') }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="step-status">
|
<div class="step-status">
|
||||||
<span v-if="currentPhase > 1" class="badge success">已完成</span>
|
<span v-if="currentPhase > 1" class="badge success">{{ $t('step1.ontologyCompleted') }}</span>
|
||||||
<span v-else-if="currentPhase === 1" class="badge processing">{{ buildProgress?.progress || 0 }}%</span>
|
<span v-else-if="currentPhase === 1" class="badge processing">{{ buildProgress?.progress || 0 }}%</span>
|
||||||
<span v-else class="badge pending">等待</span>
|
<span v-else class="badge pending">{{ $t('step1.ontologyPending') }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
<p class="api-note">POST /api/graph/build</p>
|
<p class="api-note">POST /api/graph/build</p>
|
||||||
<p class="description">
|
<p class="description">
|
||||||
基于生成的本体,将文档自动分块后调用 Zep 构建知识图谱,提取实体和关系,并形成时序记忆与社区摘要
|
{{ $t('step1.graphRagDesc') }}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<!-- Stats Cards -->
|
<!-- Stats Cards -->
|
||||||
<div class="stats-grid">
|
<div class="stats-grid">
|
||||||
<div class="stat-card">
|
<div class="stat-card">
|
||||||
<span class="stat-value">{{ graphStats.nodes }}</span>
|
<span class="stat-value">{{ graphStats.nodes }}</span>
|
||||||
<span class="stat-label">实体节点</span>
|
<span class="stat-label">{{ $t('step1.entityNodes') }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="stat-card">
|
<div class="stat-card">
|
||||||
<span class="stat-value">{{ graphStats.edges }}</span>
|
<span class="stat-value">{{ graphStats.edges }}</span>
|
||||||
<span class="stat-label">关系边</span>
|
<span class="stat-label">{{ $t('step1.relationEdges') }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="stat-card">
|
<div class="stat-card">
|
||||||
<span class="stat-value">{{ graphStats.types }}</span>
|
<span class="stat-value">{{ graphStats.types }}</span>
|
||||||
<span class="stat-label">SCHEMA类型</span>
|
<span class="stat-label">{{ $t('step1.schemaTypes') }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -148,23 +148,23 @@
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
<div class="step-info">
|
<div class="step-info">
|
||||||
<span class="step-num">03</span>
|
<span class="step-num">03</span>
|
||||||
<span class="step-title">构建完成</span>
|
<span class="step-title">{{ $t('step1.buildComplete') }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="step-status">
|
<div class="step-status">
|
||||||
<span v-if="currentPhase >= 2" class="badge accent">进行中</span>
|
<span v-if="currentPhase >= 2" class="badge accent">{{ $t('step1.inProgress') }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
<p class="api-note">POST /api/simulation/create</p>
|
<p class="api-note">POST /api/simulation/create</p>
|
||||||
<p class="description">图谱构建已完成,请进入下一步进行模拟环境搭建</p>
|
<p class="description">{{ $t('step1.buildCompleteDesc') }}</p>
|
||||||
<button
|
<button
|
||||||
class="action-btn"
|
class="action-btn"
|
||||||
:disabled="currentPhase < 2 || creatingSimulation"
|
:disabled="currentPhase < 2 || creatingSimulation"
|
||||||
@click="handleEnterEnvSetup"
|
@click="handleEnterEnvSetup"
|
||||||
>
|
>
|
||||||
<span v-if="creatingSimulation" class="spinner-sm"></span>
|
<span v-if="creatingSimulation" class="spinner-sm"></span>
|
||||||
{{ creatingSimulation ? '创建中...' : '进入环境搭建 ➝' }}
|
{{ creatingSimulation ? $t('step1.creating') : $t('step1.enterEnvSetup') + ' ➝' }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -189,9 +189,11 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { computed, ref, watch, nextTick } from 'vue'
|
import { computed, ref, watch, nextTick } from 'vue'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
|
import { useI18n } from 'vue-i18n'
|
||||||
import { createSimulation } from '../api/simulation'
|
import { createSimulation } from '../api/simulation'
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
const { t } = useI18n()
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
currentPhase: { type: Number, default: 0 },
|
currentPhase: { type: Number, default: 0 },
|
||||||
|
|
@ -233,11 +235,11 @@ const handleEnterEnvSetup = async () => {
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
console.error('创建模拟失败:', res.error)
|
console.error('创建模拟失败:', res.error)
|
||||||
alert('创建模拟失败: ' + (res.error || '未知错误'))
|
alert(t('step1.createSimulationFailed', { error: res.error || t('common.unknownError') }))
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('创建模拟异常:', err)
|
console.error('创建模拟异常:', err)
|
||||||
alert('创建模拟异常: ' + err.message)
|
alert(t('step1.createSimulationException', { error: err.message }))
|
||||||
} finally {
|
} finally {
|
||||||
creatingSimulation.value = false
|
creatingSimulation.value = false
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,18 +6,18 @@
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
<div class="step-info">
|
<div class="step-info">
|
||||||
<span class="step-num">01</span>
|
<span class="step-num">01</span>
|
||||||
<span class="step-title">模拟实例初始化</span>
|
<span class="step-title">{{ $t('step2.simInstanceInit') }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="step-status">
|
<div class="step-status">
|
||||||
<span v-if="phase > 0" class="badge success">已完成</span>
|
<span v-if="phase > 0" class="badge success">{{ $t('common.completed') }}</span>
|
||||||
<span v-else class="badge processing">初始化</span>
|
<span v-else class="badge processing">{{ $t('step2.initializing') }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
<p class="api-note">POST /api/simulation/create</p>
|
<p class="api-note">POST /api/simulation/create</p>
|
||||||
<p class="description">
|
<p class="description">
|
||||||
新建simulation实例,拉取模拟世界参数模版
|
{{ $t('step2.simInstanceDesc') }}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div v-if="simulationId" class="info-card">
|
<div v-if="simulationId" class="info-card">
|
||||||
|
|
@ -35,7 +35,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="info-row">
|
<div class="info-row">
|
||||||
<span class="info-label">Task ID</span>
|
<span class="info-label">Task ID</span>
|
||||||
<span class="info-value mono">{{ taskId || '异步任务已完成' }}</span>
|
<span class="info-value mono">{{ taskId || $t('step2.asyncTaskDone') }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -46,41 +46,41 @@
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
<div class="step-info">
|
<div class="step-info">
|
||||||
<span class="step-num">02</span>
|
<span class="step-num">02</span>
|
||||||
<span class="step-title">生成 Agent 人设</span>
|
<span class="step-title">{{ $t('step2.generateAgentPersona') }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="step-status">
|
<div class="step-status">
|
||||||
<span v-if="phase > 1" class="badge success">已完成</span>
|
<span v-if="phase > 1" class="badge success">{{ $t('common.completed') }}</span>
|
||||||
<span v-else-if="phase === 1" class="badge processing">{{ prepareProgress }}%</span>
|
<span v-else-if="phase === 1" class="badge processing">{{ prepareProgress }}%</span>
|
||||||
<span v-else class="badge pending">等待</span>
|
<span v-else class="badge pending">{{ $t('common.pending') }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
<p class="api-note">POST /api/simulation/prepare</p>
|
<p class="api-note">POST /api/simulation/prepare</p>
|
||||||
<p class="description">
|
<p class="description">
|
||||||
结合上下文,自动调用工具从知识图谱梳理实体与关系,初始化模拟个体,并基于现实种子赋予他们独特的行为与记忆
|
{{ $t('step2.generateAgentPersonaDesc') }}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<!-- Profiles Stats -->
|
<!-- Profiles Stats -->
|
||||||
<div v-if="profiles.length > 0" class="stats-grid">
|
<div v-if="profiles.length > 0" class="stats-grid">
|
||||||
<div class="stat-card">
|
<div class="stat-card">
|
||||||
<span class="stat-value">{{ profiles.length }}</span>
|
<span class="stat-value">{{ profiles.length }}</span>
|
||||||
<span class="stat-label">当前Agent数</span>
|
<span class="stat-label">{{ $t('step2.currentAgentCount') }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="stat-card">
|
<div class="stat-card">
|
||||||
<span class="stat-value">{{ expectedTotal || '-' }}</span>
|
<span class="stat-value">{{ expectedTotal || '-' }}</span>
|
||||||
<span class="stat-label">预期Agent总数</span>
|
<span class="stat-label">{{ $t('step2.expectedAgentTotal') }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="stat-card">
|
<div class="stat-card">
|
||||||
<span class="stat-value">{{ totalTopicsCount }}</span>
|
<span class="stat-value">{{ totalTopicsCount }}</span>
|
||||||
<span class="stat-label">现实种子当前关联话题数</span>
|
<span class="stat-label">{{ $t('step2.relatedTopicsCount') }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Profiles List Preview -->
|
<!-- Profiles List Preview -->
|
||||||
<div v-if="profiles.length > 0" class="profiles-preview">
|
<div v-if="profiles.length > 0" class="profiles-preview">
|
||||||
<div class="preview-header">
|
<div class="preview-header">
|
||||||
<span class="preview-title">已生成的 Agent 人设</span>
|
<span class="preview-title">{{ $t('step2.generatedAgentPersonas') }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="profiles-list">
|
<div class="profiles-list">
|
||||||
<div
|
<div
|
||||||
|
|
@ -94,9 +94,9 @@
|
||||||
<span class="profile-username">@{{ profile.name || `agent_${idx}` }}</span>
|
<span class="profile-username">@{{ profile.name || `agent_${idx}` }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="profile-meta">
|
<div class="profile-meta">
|
||||||
<span class="profile-profession">{{ profile.profession || '未知职业' }}</span>
|
<span class="profile-profession">{{ profile.profession || $t('step2.unknownProfession') }}</span>
|
||||||
</div>
|
</div>
|
||||||
<p class="profile-bio">{{ profile.bio || '暂无简介' }}</p>
|
<p class="profile-bio">{{ profile.bio || $t('step2.noBio') }}</p>
|
||||||
<div v-if="profile.interested_topics?.length" class="profile-topics">
|
<div v-if="profile.interested_topics?.length" class="profile-topics">
|
||||||
<span
|
<span
|
||||||
v-for="topic in profile.interested_topics.slice(0, 3)"
|
v-for="topic in profile.interested_topics.slice(0, 3)"
|
||||||
|
|
@ -118,19 +118,19 @@
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
<div class="step-info">
|
<div class="step-info">
|
||||||
<span class="step-num">03</span>
|
<span class="step-num">03</span>
|
||||||
<span class="step-title">生成双平台模拟配置</span>
|
<span class="step-title">{{ $t('step2.dualPlatformConfig') }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="step-status">
|
<div class="step-status">
|
||||||
<span v-if="phase > 2" class="badge success">已完成</span>
|
<span v-if="phase > 2" class="badge success">{{ $t('common.completed') }}</span>
|
||||||
<span v-else-if="phase === 2" class="badge processing">生成中</span>
|
<span v-else-if="phase === 2" class="badge processing">{{ $t('step2.generating') }}</span>
|
||||||
<span v-else class="badge pending">等待</span>
|
<span v-else class="badge pending">{{ $t('common.pending') }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
<p class="api-note">POST /api/simulation/prepare</p>
|
<p class="api-note">POST /api/simulation/prepare</p>
|
||||||
<p class="description">
|
<p class="description">
|
||||||
LLM 根据模拟需求与现实种子,智能设置世界时间流速、推荐算法、每个个体的活跃时间段、发言频率、事件触发等参数
|
{{ $t('step2.dualPlatformConfigDesc') }}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<!-- Config Preview -->
|
<!-- Config Preview -->
|
||||||
|
|
@ -139,40 +139,40 @@
|
||||||
<div class="config-block">
|
<div class="config-block">
|
||||||
<div class="config-grid">
|
<div class="config-grid">
|
||||||
<div class="config-item">
|
<div class="config-item">
|
||||||
<span class="config-item-label">模拟时长</span>
|
<span class="config-item-label">{{ $t('step2.simulationDuration') }}</span>
|
||||||
<span class="config-item-value">{{ simulationConfig.time_config?.total_simulation_hours || '-' }} 小时</span>
|
<span class="config-item-value">{{ simulationConfig.time_config?.total_simulation_hours || '-' }} {{ $t('common.hours') }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="config-item">
|
<div class="config-item">
|
||||||
<span class="config-item-label">每轮时长</span>
|
<span class="config-item-label">{{ $t('step2.roundDuration') }}</span>
|
||||||
<span class="config-item-value">{{ simulationConfig.time_config?.minutes_per_round || '-' }} 分钟</span>
|
<span class="config-item-value">{{ simulationConfig.time_config?.minutes_per_round || '-' }} {{ $t('common.minutes') }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="config-item">
|
<div class="config-item">
|
||||||
<span class="config-item-label">总轮次</span>
|
<span class="config-item-label">{{ $t('step2.totalRounds') }}</span>
|
||||||
<span class="config-item-value">{{ Math.floor((simulationConfig.time_config?.total_simulation_hours * 60 / simulationConfig.time_config?.minutes_per_round)) || '-' }} 轮</span>
|
<span class="config-item-value">{{ Math.floor((simulationConfig.time_config?.total_simulation_hours * 60 / simulationConfig.time_config?.minutes_per_round)) || '-' }} {{ $t('common.rounds') }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="config-item">
|
<div class="config-item">
|
||||||
<span class="config-item-label">每小时活跃</span>
|
<span class="config-item-label">{{ $t('step2.activePerHour') }}</span>
|
||||||
<span class="config-item-value">{{ simulationConfig.time_config?.agents_per_hour_min }}-{{ simulationConfig.time_config?.agents_per_hour_max }}</span>
|
<span class="config-item-value">{{ simulationConfig.time_config?.agents_per_hour_min }}-{{ simulationConfig.time_config?.agents_per_hour_max }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="time-periods">
|
<div class="time-periods">
|
||||||
<div class="period-item">
|
<div class="period-item">
|
||||||
<span class="period-label">高峰时段</span>
|
<span class="period-label">{{ $t('step2.peakHours') }}</span>
|
||||||
<span class="period-hours">{{ simulationConfig.time_config?.peak_hours?.join(':00, ') }}:00</span>
|
<span class="period-hours">{{ simulationConfig.time_config?.peak_hours?.join(':00, ') }}:00</span>
|
||||||
<span class="period-multiplier">×{{ simulationConfig.time_config?.peak_activity_multiplier }}</span>
|
<span class="period-multiplier">×{{ simulationConfig.time_config?.peak_activity_multiplier }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="period-item">
|
<div class="period-item">
|
||||||
<span class="period-label">工作时段</span>
|
<span class="period-label">{{ $t('step2.workHours') }}</span>
|
||||||
<span class="period-hours">{{ simulationConfig.time_config?.work_hours?.[0] }}:00-{{ simulationConfig.time_config?.work_hours?.slice(-1)[0] }}:00</span>
|
<span class="period-hours">{{ simulationConfig.time_config?.work_hours?.[0] }}:00-{{ simulationConfig.time_config?.work_hours?.slice(-1)[0] }}:00</span>
|
||||||
<span class="period-multiplier">×{{ simulationConfig.time_config?.work_activity_multiplier }}</span>
|
<span class="period-multiplier">×{{ simulationConfig.time_config?.work_activity_multiplier }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="period-item">
|
<div class="period-item">
|
||||||
<span class="period-label">早间时段</span>
|
<span class="period-label">{{ $t('step2.morningHours') }}</span>
|
||||||
<span class="period-hours">{{ simulationConfig.time_config?.morning_hours?.[0] }}:00-{{ simulationConfig.time_config?.morning_hours?.slice(-1)[0] }}:00</span>
|
<span class="period-hours">{{ simulationConfig.time_config?.morning_hours?.[0] }}:00-{{ simulationConfig.time_config?.morning_hours?.slice(-1)[0] }}:00</span>
|
||||||
<span class="period-multiplier">×{{ simulationConfig.time_config?.morning_activity_multiplier }}</span>
|
<span class="period-multiplier">×{{ simulationConfig.time_config?.morning_activity_multiplier }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="period-item">
|
<div class="period-item">
|
||||||
<span class="period-label">低谷时段</span>
|
<span class="period-label">{{ $t('step2.offPeakHours') }}</span>
|
||||||
<span class="period-hours">{{ simulationConfig.time_config?.off_peak_hours?.[0] }}:00-{{ simulationConfig.time_config?.off_peak_hours?.slice(-1)[0] }}:00</span>
|
<span class="period-hours">{{ simulationConfig.time_config?.off_peak_hours?.[0] }}:00-{{ simulationConfig.time_config?.off_peak_hours?.slice(-1)[0] }}:00</span>
|
||||||
<span class="period-multiplier">×{{ simulationConfig.time_config?.off_peak_activity_multiplier }}</span>
|
<span class="period-multiplier">×{{ simulationConfig.time_config?.off_peak_activity_multiplier }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -182,8 +182,8 @@
|
||||||
<!-- Agent 配置 -->
|
<!-- Agent 配置 -->
|
||||||
<div class="config-block">
|
<div class="config-block">
|
||||||
<div class="config-block-header">
|
<div class="config-block-header">
|
||||||
<span class="config-block-title">Agent 配置</span>
|
<span class="config-block-title">{{ $t('step2.agentConfig') }}</span>
|
||||||
<span class="config-block-badge">{{ simulationConfig.agent_configs?.length || 0 }} 个</span>
|
<span class="config-block-badge">{{ simulationConfig.agent_configs?.length || 0 }} {{ $t('common.items') }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="agents-cards">
|
<div class="agents-cards">
|
||||||
<div
|
<div
|
||||||
|
|
@ -205,7 +205,7 @@
|
||||||
|
|
||||||
<!-- 活跃时间轴 -->
|
<!-- 活跃时间轴 -->
|
||||||
<div class="agent-timeline">
|
<div class="agent-timeline">
|
||||||
<span class="timeline-label">活跃时段</span>
|
<span class="timeline-label">{{ $t('step2.activeTimePeriod') }}</span>
|
||||||
<div class="mini-timeline">
|
<div class="mini-timeline">
|
||||||
<div
|
<div
|
||||||
v-for="hour in 24"
|
v-for="hour in 24"
|
||||||
|
|
@ -228,34 +228,34 @@
|
||||||
<div class="agent-params">
|
<div class="agent-params">
|
||||||
<div class="param-group">
|
<div class="param-group">
|
||||||
<div class="param-item">
|
<div class="param-item">
|
||||||
<span class="param-label">发帖/时</span>
|
<span class="param-label">{{ $t('step2.postsPerHour') }}</span>
|
||||||
<span class="param-value">{{ agent.posts_per_hour }}</span>
|
<span class="param-value">{{ agent.posts_per_hour }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="param-item">
|
<div class="param-item">
|
||||||
<span class="param-label">评论/时</span>
|
<span class="param-label">{{ $t('step2.commentsPerHour') }}</span>
|
||||||
<span class="param-value">{{ agent.comments_per_hour }}</span>
|
<span class="param-value">{{ agent.comments_per_hour }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="param-item">
|
<div class="param-item">
|
||||||
<span class="param-label">响应延迟</span>
|
<span class="param-label">{{ $t('step2.responseDelay') }}</span>
|
||||||
<span class="param-value">{{ agent.response_delay_min }}-{{ agent.response_delay_max }}min</span>
|
<span class="param-value">{{ agent.response_delay_min }}-{{ agent.response_delay_max }}min</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="param-group">
|
<div class="param-group">
|
||||||
<div class="param-item">
|
<div class="param-item">
|
||||||
<span class="param-label">活跃度</span>
|
<span class="param-label">{{ $t('step2.activityLevel') }}</span>
|
||||||
<span class="param-value with-bar">
|
<span class="param-value with-bar">
|
||||||
<span class="mini-bar" :style="{ width: (agent.activity_level * 100) + '%' }"></span>
|
<span class="mini-bar" :style="{ width: (agent.activity_level * 100) + '%' }"></span>
|
||||||
{{ (agent.activity_level * 100).toFixed(0) }}%
|
{{ (agent.activity_level * 100).toFixed(0) }}%
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="param-item">
|
<div class="param-item">
|
||||||
<span class="param-label">情感倾向</span>
|
<span class="param-label">{{ $t('step2.sentimentBias') }}</span>
|
||||||
<span class="param-value" :class="agent.sentiment_bias > 0 ? 'positive' : agent.sentiment_bias < 0 ? 'negative' : 'neutral'">
|
<span class="param-value" :class="agent.sentiment_bias > 0 ? 'positive' : agent.sentiment_bias < 0 ? 'negative' : 'neutral'">
|
||||||
{{ agent.sentiment_bias > 0 ? '+' : '' }}{{ agent.sentiment_bias?.toFixed(1) }}
|
{{ agent.sentiment_bias > 0 ? '+' : '' }}{{ agent.sentiment_bias?.toFixed(1) }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="param-item">
|
<div class="param-item">
|
||||||
<span class="param-label">影响力</span>
|
<span class="param-label">{{ $t('step2.influenceWeight') }}</span>
|
||||||
<span class="param-value highlight">{{ agent.influence_weight?.toFixed(1) }}</span>
|
<span class="param-value highlight">{{ agent.influence_weight?.toFixed(1) }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -267,59 +267,59 @@
|
||||||
<!-- 平台配置 -->
|
<!-- 平台配置 -->
|
||||||
<div class="config-block">
|
<div class="config-block">
|
||||||
<div class="config-block-header">
|
<div class="config-block-header">
|
||||||
<span class="config-block-title">推荐算法配置</span>
|
<span class="config-block-title">{{ $t('step2.recommendAlgoConfig') }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="platforms-grid">
|
<div class="platforms-grid">
|
||||||
<div v-if="simulationConfig.twitter_config" class="platform-card">
|
<div v-if="simulationConfig.twitter_config" class="platform-card">
|
||||||
<div class="platform-card-header">
|
<div class="platform-card-header">
|
||||||
<span class="platform-name">平台 1:广场 / 信息流</span>
|
<span class="platform-name">{{ $t('step2.platform1Name') }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="platform-params">
|
<div class="platform-params">
|
||||||
<div class="param-row">
|
<div class="param-row">
|
||||||
<span class="param-label">时效权重</span>
|
<span class="param-label">{{ $t('step2.recencyWeight') }}</span>
|
||||||
<span class="param-value">{{ simulationConfig.twitter_config.recency_weight }}</span>
|
<span class="param-value">{{ simulationConfig.twitter_config.recency_weight }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="param-row">
|
<div class="param-row">
|
||||||
<span class="param-label">热度权重</span>
|
<span class="param-label">{{ $t('step2.popularityWeight') }}</span>
|
||||||
<span class="param-value">{{ simulationConfig.twitter_config.popularity_weight }}</span>
|
<span class="param-value">{{ simulationConfig.twitter_config.popularity_weight }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="param-row">
|
<div class="param-row">
|
||||||
<span class="param-label">相关性权重</span>
|
<span class="param-label">{{ $t('step2.relevanceWeight') }}</span>
|
||||||
<span class="param-value">{{ simulationConfig.twitter_config.relevance_weight }}</span>
|
<span class="param-value">{{ simulationConfig.twitter_config.relevance_weight }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="param-row">
|
<div class="param-row">
|
||||||
<span class="param-label">病毒阈值</span>
|
<span class="param-label">{{ $t('step2.viralThreshold') }}</span>
|
||||||
<span class="param-value">{{ simulationConfig.twitter_config.viral_threshold }}</span>
|
<span class="param-value">{{ simulationConfig.twitter_config.viral_threshold }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="param-row">
|
<div class="param-row">
|
||||||
<span class="param-label">回音室强度</span>
|
<span class="param-label">{{ $t('step2.echoChamberStrength') }}</span>
|
||||||
<span class="param-value">{{ simulationConfig.twitter_config.echo_chamber_strength }}</span>
|
<span class="param-value">{{ simulationConfig.twitter_config.echo_chamber_strength }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="simulationConfig.reddit_config" class="platform-card">
|
<div v-if="simulationConfig.reddit_config" class="platform-card">
|
||||||
<div class="platform-card-header">
|
<div class="platform-card-header">
|
||||||
<span class="platform-name">平台 2:话题 / 社区</span>
|
<span class="platform-name">{{ $t('step2.platform2Name') }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="platform-params">
|
<div class="platform-params">
|
||||||
<div class="param-row">
|
<div class="param-row">
|
||||||
<span class="param-label">时效权重</span>
|
<span class="param-label">{{ $t('step2.recencyWeight') }}</span>
|
||||||
<span class="param-value">{{ simulationConfig.reddit_config.recency_weight }}</span>
|
<span class="param-value">{{ simulationConfig.reddit_config.recency_weight }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="param-row">
|
<div class="param-row">
|
||||||
<span class="param-label">热度权重</span>
|
<span class="param-label">{{ $t('step2.popularityWeight') }}</span>
|
||||||
<span class="param-value">{{ simulationConfig.reddit_config.popularity_weight }}</span>
|
<span class="param-value">{{ simulationConfig.reddit_config.popularity_weight }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="param-row">
|
<div class="param-row">
|
||||||
<span class="param-label">相关性权重</span>
|
<span class="param-label">{{ $t('step2.relevanceWeight') }}</span>
|
||||||
<span class="param-value">{{ simulationConfig.reddit_config.relevance_weight }}</span>
|
<span class="param-value">{{ simulationConfig.reddit_config.relevance_weight }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="param-row">
|
<div class="param-row">
|
||||||
<span class="param-label">病毒阈值</span>
|
<span class="param-label">{{ $t('step2.viralThreshold') }}</span>
|
||||||
<span class="param-value">{{ simulationConfig.reddit_config.viral_threshold }}</span>
|
<span class="param-value">{{ simulationConfig.reddit_config.viral_threshold }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="param-row">
|
<div class="param-row">
|
||||||
<span class="param-label">回音室强度</span>
|
<span class="param-label">{{ $t('step2.echoChamberStrength') }}</span>
|
||||||
<span class="param-value">{{ simulationConfig.reddit_config.echo_chamber_strength }}</span>
|
<span class="param-value">{{ simulationConfig.reddit_config.echo_chamber_strength }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -330,7 +330,7 @@
|
||||||
<!-- LLM 配置推理 -->
|
<!-- LLM 配置推理 -->
|
||||||
<div v-if="simulationConfig.generation_reasoning" class="config-block">
|
<div v-if="simulationConfig.generation_reasoning" class="config-block">
|
||||||
<div class="config-block-header">
|
<div class="config-block-header">
|
||||||
<span class="config-block-title">LLM 配置推理</span>
|
<span class="config-block-title">{{ $t('step2.llmConfigReasoning') }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="reasoning-content">
|
<div class="reasoning-content">
|
||||||
<div
|
<div
|
||||||
|
|
@ -351,19 +351,19 @@
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
<div class="step-info">
|
<div class="step-info">
|
||||||
<span class="step-num">04</span>
|
<span class="step-num">04</span>
|
||||||
<span class="step-title">初始激活编排</span>
|
<span class="step-title">{{ $t('step2.initialActivation') }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="step-status">
|
<div class="step-status">
|
||||||
<span v-if="phase > 3" class="badge success">已完成</span>
|
<span v-if="phase > 3" class="badge success">{{ $t('common.completed') }}</span>
|
||||||
<span v-else-if="phase === 3" class="badge processing">编排中</span>
|
<span v-else-if="phase === 3" class="badge processing">{{ $t('step2.orchestrating') }}</span>
|
||||||
<span v-else class="badge pending">等待</span>
|
<span v-else class="badge pending">{{ $t('common.pending') }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
<p class="api-note">POST /api/simulation/prepare</p>
|
<p class="api-note">POST /api/simulation/prepare</p>
|
||||||
<p class="description">
|
<p class="description">
|
||||||
基于叙事方向,自动生成初始激活事件与热点话题,引导模拟世界的初始状态
|
{{ $t('step2.initialActivationDesc') }}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div v-if="simulationConfig?.event_config" class="orchestration-content">
|
<div v-if="simulationConfig?.event_config" class="orchestration-content">
|
||||||
|
|
@ -380,14 +380,14 @@
|
||||||
</linearGradient>
|
</linearGradient>
|
||||||
</defs>
|
</defs>
|
||||||
</svg>
|
</svg>
|
||||||
叙事引导方向
|
{{ $t('step2.narrativeDirection') }}
|
||||||
</span>
|
</span>
|
||||||
<p class="narrative-text">{{ simulationConfig.event_config.narrative_direction }}</p>
|
<p class="narrative-text">{{ simulationConfig.event_config.narrative_direction }}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 热点话题 -->
|
<!-- 热点话题 -->
|
||||||
<div class="topics-section">
|
<div class="topics-section">
|
||||||
<span class="box-label">初始热点话题</span>
|
<span class="box-label">{{ $t('step2.initialHotTopics') }}</span>
|
||||||
<div class="hot-topics-grid">
|
<div class="hot-topics-grid">
|
||||||
<span v-for="topic in simulationConfig.event_config.hot_topics" :key="topic" class="hot-topic-tag">
|
<span v-for="topic in simulationConfig.event_config.hot_topics" :key="topic" class="hot-topic-tag">
|
||||||
# {{ topic }}
|
# {{ topic }}
|
||||||
|
|
@ -397,7 +397,7 @@
|
||||||
|
|
||||||
<!-- 初始帖子流 -->
|
<!-- 初始帖子流 -->
|
||||||
<div class="initial-posts-section">
|
<div class="initial-posts-section">
|
||||||
<span class="box-label">初始激活序列 ({{ simulationConfig.event_config.initial_posts.length }})</span>
|
<span class="box-label">{{ $t('step2.initialActivationSeq', { count: simulationConfig.event_config.initial_posts.length }) }}</span>
|
||||||
<div class="posts-timeline">
|
<div class="posts-timeline">
|
||||||
<div v-for="(post, idx) in simulationConfig.event_config.initial_posts" :key="idx" class="timeline-item">
|
<div v-for="(post, idx) in simulationConfig.event_config.initial_posts" :key="idx" class="timeline-item">
|
||||||
<div class="timeline-marker"></div>
|
<div class="timeline-marker"></div>
|
||||||
|
|
@ -423,29 +423,29 @@
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
<div class="step-info">
|
<div class="step-info">
|
||||||
<span class="step-num">05</span>
|
<span class="step-num">05</span>
|
||||||
<span class="step-title">准备完成</span>
|
<span class="step-title">{{ $t('step2.setupComplete') }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="step-status">
|
<div class="step-status">
|
||||||
<span v-if="phase >= 4" class="badge processing">进行中</span>
|
<span v-if="phase >= 4" class="badge processing">{{ $t('step1.inProgress') }}</span>
|
||||||
<span v-else class="badge pending">等待</span>
|
<span v-else class="badge pending">{{ $t('common.pending') }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
<p class="api-note">POST /api/simulation/start</p>
|
<p class="api-note">POST /api/simulation/start</p>
|
||||||
<p class="description">模拟环境已准备完成,可以开始运行模拟</p>
|
<p class="description">{{ $t('step2.setupCompleteDesc') }}</p>
|
||||||
|
|
||||||
<!-- 模拟轮数配置 - 只有在配置生成完成且轮数计算出来后才显示 -->
|
<!-- 模拟轮数配置 - 只有在配置生成完成且轮数计算出来后才显示 -->
|
||||||
<div v-if="simulationConfig && autoGeneratedRounds" class="rounds-config-section">
|
<div v-if="simulationConfig && autoGeneratedRounds" class="rounds-config-section">
|
||||||
<div class="rounds-header">
|
<div class="rounds-header">
|
||||||
<div class="header-left">
|
<div class="header-left">
|
||||||
<span class="section-title">模拟轮数设定</span>
|
<span class="section-title">{{ $t('step2.roundsConfig') }}</span>
|
||||||
<span class="section-desc">MiroFish 自动规划推演现实 <span class="desc-highlight">{{ simulationConfig?.time_config?.total_simulation_hours || '-' }}</span> 小时,每轮代表现实 <span class="desc-highlight">{{ simulationConfig?.time_config?.minutes_per_round || '-' }}</span> 分钟时间流逝</span>
|
<span class="section-desc">{{ $t('step2.roundsConfigDesc', { hours: simulationConfig?.time_config?.total_simulation_hours || '-', minutesPerRound: simulationConfig?.time_config?.minutes_per_round || '-' }) }}</span>
|
||||||
</div>
|
</div>
|
||||||
<label class="switch-control">
|
<label class="switch-control">
|
||||||
<input type="checkbox" v-model="useCustomRounds">
|
<input type="checkbox" v-model="useCustomRounds">
|
||||||
<span class="switch-track"></span>
|
<span class="switch-track"></span>
|
||||||
<span class="switch-label">自定义</span>
|
<span class="switch-label">{{ $t('step2.customToggle') }}</span>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -454,10 +454,10 @@
|
||||||
<div class="slider-display">
|
<div class="slider-display">
|
||||||
<div class="slider-main-value">
|
<div class="slider-main-value">
|
||||||
<span class="val-num">{{ customMaxRounds }}</span>
|
<span class="val-num">{{ customMaxRounds }}</span>
|
||||||
<span class="val-unit">轮</span>
|
<span class="val-unit">{{ $t('step2.roundsUnit') }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="slider-meta-info">
|
<div class="slider-meta-info">
|
||||||
<span>若Agent规模为100:预计耗时约 {{ Math.round(customMaxRounds * 0.6) }} 分钟</span>
|
<span>{{ $t('step2.estimatedDuration', { minutes: Math.round(customMaxRounds * 0.6) }) }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -478,7 +478,7 @@
|
||||||
:class="{ active: customMaxRounds === 40 }"
|
:class="{ active: customMaxRounds === 40 }"
|
||||||
@click="customMaxRounds = 40"
|
@click="customMaxRounds = 40"
|
||||||
:style="{ position: 'absolute', left: `calc(${(40 - 10) / (autoGeneratedRounds - 10) * 100}% - 30px)` }"
|
:style="{ position: 'absolute', left: `calc(${(40 - 10) / (autoGeneratedRounds - 10) * 100}% - 30px)` }"
|
||||||
>40 (推荐)</span>
|
>{{ $t('step2.recommendedRounds', { rounds: 40 }) }}</span>
|
||||||
<span>{{ autoGeneratedRounds }}</span>
|
<span>{{ autoGeneratedRounds }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -488,7 +488,7 @@
|
||||||
<div class="auto-info-card">
|
<div class="auto-info-card">
|
||||||
<div class="auto-value">
|
<div class="auto-value">
|
||||||
<span class="val-num">{{ autoGeneratedRounds }}</span>
|
<span class="val-num">{{ autoGeneratedRounds }}</span>
|
||||||
<span class="val-unit">轮</span>
|
<span class="val-unit">{{ $t('step2.roundsUnit') }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="auto-content">
|
<div class="auto-content">
|
||||||
<div class="auto-meta-row">
|
<div class="auto-meta-row">
|
||||||
|
|
@ -497,11 +497,11 @@
|
||||||
<circle cx="12" cy="12" r="10"></circle>
|
<circle cx="12" cy="12" r="10"></circle>
|
||||||
<polyline points="12 6 12 12 16 14"></polyline>
|
<polyline points="12 6 12 12 16 14"></polyline>
|
||||||
</svg>
|
</svg>
|
||||||
若Agent规模为100:预计耗时 {{ Math.round(autoGeneratedRounds * 0.6) }} 分钟
|
{{ $t('step2.estimatedDurationFull', { minutes: Math.round(autoGeneratedRounds * 0.6) }) }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="auto-desc">
|
<div class="auto-desc">
|
||||||
<p class="highlight-tip" @click="useCustomRounds = true">若首次运行,强烈建议切换至‘自定义模式’减少模拟轮数,以便快速预览效果并降低报错风险 ➝</p>
|
<p class="highlight-tip" @click="useCustomRounds = true">{{ $t(‘step2.customTip’) }} ➝</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -514,14 +514,14 @@
|
||||||
class="action-btn secondary"
|
class="action-btn secondary"
|
||||||
@click="$emit('go-back')"
|
@click="$emit('go-back')"
|
||||||
>
|
>
|
||||||
← 返回图谱构建
|
← {{ $t('step2.backToGraphBuild') }}
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
class="action-btn primary"
|
class="action-btn primary"
|
||||||
:disabled="phase < 4"
|
:disabled="phase < 4"
|
||||||
@click="handleStartSimulation"
|
@click="handleStartSimulation"
|
||||||
>
|
>
|
||||||
开始双世界并行模拟 ➝
|
{{ $t('step2.startDualWorldSim') }} ➝
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -547,32 +547,32 @@
|
||||||
<!-- 基本信息 -->
|
<!-- 基本信息 -->
|
||||||
<div class="modal-info-grid">
|
<div class="modal-info-grid">
|
||||||
<div class="info-item">
|
<div class="info-item">
|
||||||
<span class="info-label">事件外显年龄</span>
|
<span class="info-label">{{ $t('step2.profileModalAge') }}</span>
|
||||||
<span class="info-value">{{ selectedProfile.age || '-' }} 岁</span>
|
<span class="info-value">{{ selectedProfile.age || '-' }} {{ $t('step2.yearsOld') }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="info-item">
|
<div class="info-item">
|
||||||
<span class="info-label">事件外显性别</span>
|
<span class="info-label">{{ $t('step2.profileModalGender') }}</span>
|
||||||
<span class="info-value">{{ { male: '男', female: '女', other: '其他' }[selectedProfile.gender] || selectedProfile.gender }}</span>
|
<span class="info-value">{{ { male: $t('step2.genderMale'), female: $t('step2.genderFemale'), other: $t('step2.genderOther') }[selectedProfile.gender] || selectedProfile.gender }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="info-item">
|
<div class="info-item">
|
||||||
<span class="info-label">国家/地区</span>
|
<span class="info-label">{{ $t('step2.profileModalCountry') }}</span>
|
||||||
<span class="info-value">{{ selectedProfile.country || '-' }}</span>
|
<span class="info-value">{{ selectedProfile.country || '-' }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="info-item">
|
<div class="info-item">
|
||||||
<span class="info-label">事件外显MBTI</span>
|
<span class="info-label">{{ $t('step2.profileModalMbti') }}</span>
|
||||||
<span class="info-value mbti">{{ selectedProfile.mbti || '-' }}</span>
|
<span class="info-value mbti">{{ selectedProfile.mbti || '-' }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 简介 -->
|
<!-- 简介 -->
|
||||||
<div class="modal-section">
|
<div class="modal-section">
|
||||||
<span class="section-label">人设简介</span>
|
<span class="section-label">{{ $t('step2.profileModalBio') }}</span>
|
||||||
<p class="section-bio">{{ selectedProfile.bio || '暂无简介' }}</p>
|
<p class="section-bio">{{ selectedProfile.bio || $t('step2.noBio') }}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 关注话题 -->
|
<!-- 关注话题 -->
|
||||||
<div class="modal-section" v-if="selectedProfile.interested_topics?.length">
|
<div class="modal-section" v-if="selectedProfile.interested_topics?.length">
|
||||||
<span class="section-label">现实种子关联话题</span>
|
<span class="section-label">{{ $t('step2.profileModalTopics') }}</span>
|
||||||
<div class="topics-grid">
|
<div class="topics-grid">
|
||||||
<span
|
<span
|
||||||
v-for="topic in selectedProfile.interested_topics"
|
v-for="topic in selectedProfile.interested_topics"
|
||||||
|
|
@ -584,25 +584,25 @@
|
||||||
|
|
||||||
<!-- 详细人设 -->
|
<!-- 详细人设 -->
|
||||||
<div class="modal-section" v-if="selectedProfile.persona">
|
<div class="modal-section" v-if="selectedProfile.persona">
|
||||||
<span class="section-label">详细人设背景</span>
|
<span class="section-label">{{ $t('step2.profileModalPersona') }}</span>
|
||||||
|
|
||||||
<!-- 人设维度概览 -->
|
<!-- 人设维度概览 -->
|
||||||
<div class="persona-dimensions">
|
<div class="persona-dimensions">
|
||||||
<div class="dimension-card">
|
<div class="dimension-card">
|
||||||
<span class="dim-title">事件全景经历</span>
|
<span class="dim-title">{{ $t('step2.personaDimExperience') }}</span>
|
||||||
<span class="dim-desc">在此事件中的完整行为轨迹</span>
|
<span class="dim-desc">{{ $t('step2.personaDimExperienceDesc') }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="dimension-card">
|
<div class="dimension-card">
|
||||||
<span class="dim-title">行为模式侧写</span>
|
<span class="dim-title">{{ $t('step2.personaDimBehavior') }}</span>
|
||||||
<span class="dim-desc">经验总结与行事风格偏好</span>
|
<span class="dim-desc">{{ $t('step2.personaDimBehaviorDesc') }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="dimension-card">
|
<div class="dimension-card">
|
||||||
<span class="dim-title">独特记忆印记</span>
|
<span class="dim-title">{{ $t('step2.personaDimMemory') }}</span>
|
||||||
<span class="dim-desc">基于现实种子形成的记忆</span>
|
<span class="dim-desc">{{ $t('step2.personaDimMemoryDesc') }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="dimension-card">
|
<div class="dimension-card">
|
||||||
<span class="dim-title">社会关系网络</span>
|
<span class="dim-title">{{ $t('step2.personaDimSocial') }}</span>
|
||||||
<span class="dim-desc">个体链接与交互图谱</span>
|
<span class="dim-desc">{{ $t('step2.personaDimSocialDesc') }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -633,14 +633,17 @@
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, computed, watch, onMounted, onUnmounted, nextTick } from 'vue'
|
import { ref, computed, watch, onMounted, onUnmounted, nextTick } from 'vue'
|
||||||
import {
|
import { useI18n } from 'vue-i18n'
|
||||||
prepareSimulation,
|
import {
|
||||||
getPrepareStatus,
|
prepareSimulation,
|
||||||
|
getPrepareStatus,
|
||||||
getSimulationProfilesRealtime,
|
getSimulationProfilesRealtime,
|
||||||
getSimulationConfig,
|
getSimulationConfig,
|
||||||
getSimulationConfigRealtime
|
getSimulationConfigRealtime
|
||||||
} from '../api/simulation'
|
} from '../api/simulation'
|
||||||
|
|
||||||
|
const { t } = useI18n()
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
simulationId: String, // 从父组件传入
|
simulationId: String, // 从父组件传入
|
||||||
projectData: Object,
|
projectData: Object,
|
||||||
|
|
|
||||||
|
|
@ -97,7 +97,7 @@
|
||||||
@click="handleNextStep"
|
@click="handleNextStep"
|
||||||
>
|
>
|
||||||
<span v-if="isGeneratingReport" class="loading-spinner-small"></span>
|
<span v-if="isGeneratingReport" class="loading-spinner-small"></span>
|
||||||
{{ isGeneratingReport ? '启动中...' : '开始生成结果报告' }}
|
{{ isGeneratingReport ? $t('step3.generatingReportBtn') : $t('step3.startGenerateReportBtn') }}
|
||||||
<span v-if="!isGeneratingReport" class="arrow-icon">→</span>
|
<span v-if="!isGeneratingReport" class="arrow-icon">→</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -288,14 +288,17 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, computed, watch, onMounted, onUnmounted, nextTick } from 'vue'
|
import { ref, computed, watch, onMounted, onUnmounted, nextTick } from 'vue'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
import {
|
import { useI18n } from 'vue-i18n'
|
||||||
startSimulation,
|
import {
|
||||||
|
startSimulation,
|
||||||
stopSimulation,
|
stopSimulation,
|
||||||
getRunStatus,
|
getRunStatus,
|
||||||
getRunStatusDetail
|
getRunStatusDetail
|
||||||
} from '../api/simulation'
|
} from '../api/simulation'
|
||||||
import { generateReport } from '../api/report'
|
import { generateReport } from '../api/report'
|
||||||
|
|
||||||
|
const { t } = useI18n()
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
simulationId: String,
|
simulationId: String,
|
||||||
maxRounds: Number, // 从Step2传入的最大轮数
|
maxRounds: Number, // 从Step2传入的最大轮数
|
||||||
|
|
|
||||||
|
|
@ -58,7 +58,7 @@
|
||||||
<path d="M12 2a10 10 0 0 1 10 10" stroke-width="4" stroke="#4B5563" stroke-linecap="round"></path>
|
<path d="M12 2a10 10 0 0 1 10 10" stroke-width="4" stroke="#4B5563" stroke-linecap="round"></path>
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
<span class="loading-text">正在生成{{ section.title }}...</span>
|
<span class="loading-text">{{ $t('step4.generatingSection', { title: section.title }) }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -129,7 +129,7 @@
|
||||||
|
|
||||||
<!-- Next Step Button - 在完成后显示 -->
|
<!-- Next Step Button - 在完成后显示 -->
|
||||||
<button v-if="isComplete" class="next-step-btn" @click="goToInteraction">
|
<button v-if="isComplete" class="next-step-btn" @click="goToInteraction">
|
||||||
<span>进入深度互动</span>
|
<span>{{ $t('step4.goToInteraction') }}</span>
|
||||||
<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2">
|
<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2">
|
||||||
<line x1="5" y1="12" x2="19" y2="12"></line>
|
<line x1="5" y1="12" x2="19" y2="12"></line>
|
||||||
<polyline points="12 5 19 12 12 19"></polyline>
|
<polyline points="12 5 19 12 12 19"></polyline>
|
||||||
|
|
|
||||||
|
|
@ -58,7 +58,7 @@
|
||||||
<path d="M12 2a10 10 0 0 1 10 10" stroke-width="4" stroke="#4B5563" stroke-linecap="round"></path>
|
<path d="M12 2a10 10 0 0 1 10 10" stroke-width="4" stroke="#4B5563" stroke-linecap="round"></path>
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
<span class="loading-text">正在生成{{ section.title }}...</span>
|
<span class="loading-text">{{ $t('step4.generatingSection', { title: section.title }) }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -85,8 +85,8 @@
|
||||||
<path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"></path>
|
<path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"></path>
|
||||||
</svg>
|
</svg>
|
||||||
<div class="action-bar-text">
|
<div class="action-bar-text">
|
||||||
<span class="action-bar-title">Interactive Tools</span>
|
<span class="action-bar-title">{{ $t('step5.interactiveTools') }}</span>
|
||||||
<span class="action-bar-subtitle mono">{{ profiles.length }} agents available</span>
|
<span class="action-bar-subtitle mono">{{ $t('step5.agentsAvailable', { count: profiles.length }) }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="action-bar-tabs">
|
<div class="action-bar-tabs">
|
||||||
|
|
@ -98,7 +98,7 @@
|
||||||
<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2">
|
<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2">
|
||||||
<path d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z"></path>
|
<path d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z"></path>
|
||||||
</svg>
|
</svg>
|
||||||
<span>与Report Agent对话</span>
|
<span>{{ $t('step5.chatWithReportAgent') }}</span>
|
||||||
</button>
|
</button>
|
||||||
<div class="agent-dropdown" v-if="profiles.length > 0">
|
<div class="agent-dropdown" v-if="profiles.length > 0">
|
||||||
<button
|
<button
|
||||||
|
|
@ -110,13 +110,13 @@
|
||||||
<path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"></path>
|
<path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"></path>
|
||||||
<circle cx="12" cy="7" r="4"></circle>
|
<circle cx="12" cy="7" r="4"></circle>
|
||||||
</svg>
|
</svg>
|
||||||
<span>{{ selectedAgent ? selectedAgent.username : '与世界中任意个体对话' }}</span>
|
<span>{{ selectedAgent ? selectedAgent.username : $t('step5.chatWithAgent') }}</span>
|
||||||
<svg class="dropdown-arrow" :class="{ open: showAgentDropdown }" viewBox="0 0 24 24" width="12" height="12" fill="none" stroke="currentColor" stroke-width="2">
|
<svg class="dropdown-arrow" :class="{ open: showAgentDropdown }" viewBox="0 0 24 24" width="12" height="12" fill="none" stroke="currentColor" stroke-width="2">
|
||||||
<polyline points="6 9 12 15 18 9"></polyline>
|
<polyline points="6 9 12 15 18 9"></polyline>
|
||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
<div v-if="showAgentDropdown" class="dropdown-menu">
|
<div v-if="showAgentDropdown" class="dropdown-menu">
|
||||||
<div class="dropdown-header">选择对话对象</div>
|
<div class="dropdown-header">{{ $t('step5.selectChatTarget') }}</div>
|
||||||
<div
|
<div
|
||||||
v-for="(agent, idx) in profiles"
|
v-for="(agent, idx) in profiles"
|
||||||
:key="idx"
|
:key="idx"
|
||||||
|
|
@ -126,13 +126,13 @@
|
||||||
<div class="agent-avatar">{{ (agent.username || 'A')[0] }}</div>
|
<div class="agent-avatar">{{ (agent.username || 'A')[0] }}</div>
|
||||||
<div class="agent-info">
|
<div class="agent-info">
|
||||||
<span class="agent-name">{{ agent.username }}</span>
|
<span class="agent-name">{{ agent.username }}</span>
|
||||||
<span class="agent-role">{{ agent.profession || '未知职业' }}</span>
|
<span class="agent-role">{{ agent.profession || $t('step2.unknownProfession') }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="tab-divider"></div>
|
<div class="tab-divider"></div>
|
||||||
<button
|
<button
|
||||||
class="tab-pill survey-pill"
|
class="tab-pill survey-pill"
|
||||||
:class="{ active: activeTab === 'survey' }"
|
:class="{ active: activeTab === 'survey' }"
|
||||||
@click="selectSurveyTab"
|
@click="selectSurveyTab"
|
||||||
|
|
@ -141,7 +141,7 @@
|
||||||
<path d="M9 11l3 3L22 4"></path>
|
<path d="M9 11l3 3L22 4"></path>
|
||||||
<path d="M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11"></path>
|
<path d="M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11"></path>
|
||||||
</svg>
|
</svg>
|
||||||
<span>发送问卷调查到世界中</span>
|
<span>{{ $t('step5.sendSurvey') }}</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -154,8 +154,8 @@
|
||||||
<div class="tools-card-header">
|
<div class="tools-card-header">
|
||||||
<div class="tools-card-avatar">R</div>
|
<div class="tools-card-avatar">R</div>
|
||||||
<div class="tools-card-info">
|
<div class="tools-card-info">
|
||||||
<div class="tools-card-name">Report Agent - Chat</div>
|
<div class="tools-card-name">{{ $t('step5.reportAgentChat') }}</div>
|
||||||
<div class="tools-card-subtitle">报告生成智能体的快速对话版本,可调用 4 种专业工具,拥有MiroFish的完整记忆</div>
|
<div class="tools-card-subtitle">{{ $t('step5.reportAgentDesc') }}</div>
|
||||||
</div>
|
</div>
|
||||||
<button class="tools-card-toggle" @click="showToolsDetail = !showToolsDetail">
|
<button class="tools-card-toggle" @click="showToolsDetail = !showToolsDetail">
|
||||||
<svg :class="{ 'is-expanded': showToolsDetail }" viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2">
|
<svg :class="{ 'is-expanded': showToolsDetail }" viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2">
|
||||||
|
|
@ -172,8 +172,8 @@
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
<div class="tool-content">
|
<div class="tool-content">
|
||||||
<div class="tool-name">InsightForge 深度归因</div>
|
<div class="tool-name">{{ $t('step5.toolInsightForge') }}</div>
|
||||||
<div class="tool-desc">对齐现实世界种子数据与模拟环境状态,结合Global/Local Memory机制,提供跨时空的深度归因分析</div>
|
<div class="tool-desc">{{ $t('step5.toolInsightForgeDesc') }}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="tool-item tool-blue">
|
<div class="tool-item tool-blue">
|
||||||
|
|
@ -184,8 +184,8 @@
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
<div class="tool-content">
|
<div class="tool-content">
|
||||||
<div class="tool-name">PanoramaSearch 全景追踪</div>
|
<div class="tool-name">{{ $t('step5.toolPanoramaSearch') }}</div>
|
||||||
<div class="tool-desc">基于图结构的广度遍历算法,重构事件传播路径,捕获全量信息流动的拓扑结构</div>
|
<div class="tool-desc">{{ $t('step5.toolPanoramaSearchDesc') }}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="tool-item tool-orange">
|
<div class="tool-item tool-orange">
|
||||||
|
|
@ -195,8 +195,8 @@
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
<div class="tool-content">
|
<div class="tool-content">
|
||||||
<div class="tool-name">QuickSearch 快速检索</div>
|
<div class="tool-name">{{ $t('step5.toolQuickSearch') }}</div>
|
||||||
<div class="tool-desc">基于 GraphRAG 的即时查询接口,优化索引效率,用于快速提取具体的节点属性与离散事实</div>
|
<div class="tool-desc">{{ $t('step5.toolQuickSearchDesc') }}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="tool-item tool-green">
|
<div class="tool-item tool-green">
|
||||||
|
|
@ -208,8 +208,8 @@
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
<div class="tool-content">
|
<div class="tool-content">
|
||||||
<div class="tool-name">InterviewSubAgent 虚拟访谈</div>
|
<div class="tool-name">{{ $t('step5.toolInterviewSubAgent') }}</div>
|
||||||
<div class="tool-desc">自主式访谈,能够并行与模拟世界中个体进行多轮对话,采集非结构化的观点数据与心理状态</div>
|
<div class="tool-desc">{{ $t('step5.toolInterviewSubAgentDesc') }}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -224,7 +224,7 @@
|
||||||
<div class="profile-card-name">{{ selectedAgent.username }}</div>
|
<div class="profile-card-name">{{ selectedAgent.username }}</div>
|
||||||
<div class="profile-card-meta">
|
<div class="profile-card-meta">
|
||||||
<span v-if="selectedAgent.name" class="profile-card-handle">@{{ selectedAgent.name }}</span>
|
<span v-if="selectedAgent.name" class="profile-card-handle">@{{ selectedAgent.name }}</span>
|
||||||
<span class="profile-card-profession">{{ selectedAgent.profession || '未知职业' }}</span>
|
<span class="profile-card-profession">{{ selectedAgent.profession || $t('step2.unknownProfession') }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button class="profile-card-toggle" @click="showFullProfile = !showFullProfile">
|
<button class="profile-card-toggle" @click="showFullProfile = !showFullProfile">
|
||||||
|
|
@ -235,7 +235,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div v-if="showFullProfile && selectedAgent.bio" class="profile-card-body">
|
<div v-if="showFullProfile && selectedAgent.bio" class="profile-card-body">
|
||||||
<div class="profile-card-bio">
|
<div class="profile-card-bio">
|
||||||
<div class="profile-card-label">简介</div>
|
<div class="profile-card-label">{{ $t('step5.profileBio') }}</div>
|
||||||
<p>{{ selectedAgent.bio }}</p>
|
<p>{{ selectedAgent.bio }}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -250,7 +250,7 @@
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
<p class="empty-text">
|
<p class="empty-text">
|
||||||
{{ chatTarget === 'report_agent' ? '与 Report Agent 对话,深入了解报告内容' : '与模拟个体对话,了解他们的观点' }}
|
{{ chatTarget === 'report_agent' ? $t('step5.chatEmptyReportAgent') : $t('step5.chatEmptyAgent') }}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
|
|
@ -292,7 +292,7 @@
|
||||||
<textarea
|
<textarea
|
||||||
v-model="chatInput"
|
v-model="chatInput"
|
||||||
class="chat-input"
|
class="chat-input"
|
||||||
placeholder="输入您的问题..."
|
:placeholder="$t('step5.chatInputPlaceholder')"
|
||||||
@keydown.enter.exact.prevent="sendMessage"
|
@keydown.enter.exact.prevent="sendMessage"
|
||||||
:disabled="isSending || (!selectedAgent && chatTarget === 'agent')"
|
:disabled="isSending || (!selectedAgent && chatTarget === 'agent')"
|
||||||
rows="1"
|
rows="1"
|
||||||
|
|
@ -317,8 +317,8 @@
|
||||||
<div class="survey-setup">
|
<div class="survey-setup">
|
||||||
<div class="setup-section">
|
<div class="setup-section">
|
||||||
<div class="section-header">
|
<div class="section-header">
|
||||||
<span class="section-title">选择调查对象</span>
|
<span class="section-title">{{ $t('step5.selectSurveyTarget') }}</span>
|
||||||
<span class="selection-count">已选 {{ selectedAgents.size }} / {{ profiles.length }}</span>
|
<span class="selection-count">{{ $t('step5.selectedCount', { selected: selectedAgents.size, total: profiles.length }) }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="agents-grid">
|
<div class="agents-grid">
|
||||||
<label
|
<label
|
||||||
|
|
@ -335,7 +335,7 @@
|
||||||
<div class="checkbox-avatar">{{ (agent.username || 'A')[0] }}</div>
|
<div class="checkbox-avatar">{{ (agent.username || 'A')[0] }}</div>
|
||||||
<div class="checkbox-info">
|
<div class="checkbox-info">
|
||||||
<span class="checkbox-name">{{ agent.username }}</span>
|
<span class="checkbox-name">{{ agent.username }}</span>
|
||||||
<span class="checkbox-role">{{ agent.profession || '未知职业' }}</span>
|
<span class="checkbox-role">{{ agent.profession || $t('step2.unknownProfession') }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="checkbox-indicator">
|
<div class="checkbox-indicator">
|
||||||
<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="3">
|
<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="3">
|
||||||
|
|
@ -345,20 +345,20 @@
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="selection-actions">
|
<div class="selection-actions">
|
||||||
<button class="action-link" @click="selectAllAgents">全选</button>
|
<button class="action-link" @click="selectAllAgents">{{ $t('step5.selectAll') }}</button>
|
||||||
<span class="action-divider">|</span>
|
<span class="action-divider">|</span>
|
||||||
<button class="action-link" @click="clearAgentSelection">清空</button>
|
<button class="action-link" @click="clearAgentSelection">{{ $t('step5.clearSelection') }}</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="setup-section">
|
<div class="setup-section">
|
||||||
<div class="section-header">
|
<div class="section-header">
|
||||||
<span class="section-title">问卷问题</span>
|
<span class="section-title">{{ $t('step5.surveyQuestions') }}</span>
|
||||||
</div>
|
</div>
|
||||||
<textarea
|
<textarea
|
||||||
v-model="surveyQuestion"
|
v-model="surveyQuestion"
|
||||||
class="survey-input"
|
class="survey-input"
|
||||||
placeholder="输入您想问所有被选中对象的问题..."
|
:placeholder="$t('step5.surveyInputPlaceholder')"
|
||||||
rows="3"
|
rows="3"
|
||||||
></textarea>
|
></textarea>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -369,15 +369,15 @@
|
||||||
@click="submitSurvey"
|
@click="submitSurvey"
|
||||||
>
|
>
|
||||||
<span v-if="isSurveying" class="loading-spinner"></span>
|
<span v-if="isSurveying" class="loading-spinner"></span>
|
||||||
<span v-else>发送问卷</span>
|
<span v-else>{{ $t('step5.submitSurvey') }}</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Survey Results -->
|
<!-- Survey Results -->
|
||||||
<div v-if="surveyResults.length > 0" class="survey-results">
|
<div v-if="surveyResults.length > 0" class="survey-results">
|
||||||
<div class="results-header">
|
<div class="results-header">
|
||||||
<span class="results-title">调查结果</span>
|
<span class="results-title">{{ $t('step5.surveyResults') }}</span>
|
||||||
<span class="results-count">{{ surveyResults.length }} 条回复</span>
|
<span class="results-count">{{ $t('step5.surveyResultsCount', { count: surveyResults.length }) }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="results-list">
|
<div class="results-list">
|
||||||
<div
|
<div
|
||||||
|
|
@ -389,7 +389,7 @@
|
||||||
<div class="result-avatar">{{ (result.agent_name || 'A')[0] }}</div>
|
<div class="result-avatar">{{ (result.agent_name || 'A')[0] }}</div>
|
||||||
<div class="result-info">
|
<div class="result-info">
|
||||||
<span class="result-name">{{ result.agent_name }}</span>
|
<span class="result-name">{{ result.agent_name }}</span>
|
||||||
<span class="result-role">{{ result.profession || '未知职业' }}</span>
|
<span class="result-role">{{ result.profession || $t('step2.unknownProfession') }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="result-question">
|
<div class="result-question">
|
||||||
|
|
@ -412,9 +412,12 @@
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, computed, watch, onMounted, onUnmounted, nextTick } from 'vue'
|
import { ref, computed, watch, onMounted, onUnmounted, nextTick } from 'vue'
|
||||||
|
import { useI18n } from 'vue-i18n'
|
||||||
import { chatWithReport, getReport, getAgentLog } from '../api/report'
|
import { chatWithReport, getReport, getAgentLog } from '../api/report'
|
||||||
import { interviewAgents, getSimulationProfilesRealtime } from '../api/simulation'
|
import { interviewAgents, getSimulationProfilesRealtime } from '../api/simulation'
|
||||||
|
|
||||||
|
const { t } = useI18n()
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
reportId: String,
|
reportId: String,
|
||||||
simulationId: String
|
simulationId: String
|
||||||
|
|
@ -665,7 +668,7 @@ const sendMessage = async () => {
|
||||||
addLog(`发送失败: ${err.message}`)
|
addLog(`发送失败: ${err.message}`)
|
||||||
chatHistory.value.push({
|
chatHistory.value.push({
|
||||||
role: 'assistant',
|
role: 'assistant',
|
||||||
content: `抱歉,发生了错误: ${err.message}`,
|
content: t('step5.errorOccurred', { error: err.message }),
|
||||||
timestamp: new Date().toISOString()
|
timestamp: new Date().toISOString()
|
||||||
})
|
})
|
||||||
} finally {
|
} finally {
|
||||||
|
|
@ -697,18 +700,18 @@ const sendToReportAgent = async (message) => {
|
||||||
if (res.success && res.data) {
|
if (res.success && res.data) {
|
||||||
chatHistory.value.push({
|
chatHistory.value.push({
|
||||||
role: 'assistant',
|
role: 'assistant',
|
||||||
content: res.data.response || res.data.answer || '无响应',
|
content: res.data.response || res.data.answer || t('step5.noResponse'),
|
||||||
timestamp: new Date().toISOString()
|
timestamp: new Date().toISOString()
|
||||||
})
|
})
|
||||||
addLog('Report Agent 已回复')
|
addLog('Report Agent 已回复')
|
||||||
} else {
|
} else {
|
||||||
throw new Error(res.error || '请求失败')
|
throw new Error(res.error || t('step5.requestFailed'))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const sendToAgent = async (message) => {
|
const sendToAgent = async (message) => {
|
||||||
if (!selectedAgent.value || selectedAgentIndex.value === null) {
|
if (!selectedAgent.value || selectedAgentIndex.value === null) {
|
||||||
throw new Error('请先选择一个模拟个体')
|
throw new Error(t('step5.selectAgentFirst'))
|
||||||
}
|
}
|
||||||
|
|
||||||
addLog(`向 ${selectedAgent.value.username} 发送: ${message.substring(0, 50)}...`)
|
addLog(`向 ${selectedAgent.value.username} 发送: ${message.substring(0, 50)}...`)
|
||||||
|
|
@ -763,10 +766,10 @@ const sendToAgent = async (message) => {
|
||||||
})
|
})
|
||||||
addLog(`${selectedAgent.value.username} 已回复`)
|
addLog(`${selectedAgent.value.username} 已回复`)
|
||||||
} else {
|
} else {
|
||||||
throw new Error('无响应数据')
|
throw new Error(t('step5.noResponse'))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
throw new Error(res.error || '请求失败')
|
throw new Error(res.error || t('step5.requestFailed'))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -830,20 +833,20 @@ const submitSurvey = async () => {
|
||||||
const agent = profiles.value[agentIdx]
|
const agent = profiles.value[agentIdx]
|
||||||
|
|
||||||
// 优先使用 reddit 平台回复,其次 twitter
|
// 优先使用 reddit 平台回复,其次 twitter
|
||||||
let responseContent = '无响应'
|
let responseContent = t('step5.noResponse')
|
||||||
|
|
||||||
if (typeof resultsDict === 'object' && !Array.isArray(resultsDict)) {
|
if (typeof resultsDict === 'object' && !Array.isArray(resultsDict)) {
|
||||||
const redditKey = `reddit_${agentIdx}`
|
const redditKey = `reddit_${agentIdx}`
|
||||||
const twitterKey = `twitter_${agentIdx}`
|
const twitterKey = `twitter_${agentIdx}`
|
||||||
const agentResult = resultsDict[redditKey] || resultsDict[twitterKey]
|
const agentResult = resultsDict[redditKey] || resultsDict[twitterKey]
|
||||||
if (agentResult) {
|
if (agentResult) {
|
||||||
responseContent = agentResult.response || agentResult.answer || '无响应'
|
responseContent = agentResult.response || agentResult.answer || t('step5.noResponse')
|
||||||
}
|
}
|
||||||
} else if (Array.isArray(resultsDict)) {
|
} else if (Array.isArray(resultsDict)) {
|
||||||
// 兼容数组格式
|
// 兼容数组格式
|
||||||
const matchedResult = resultsDict.find(r => r.agent_id === agentIdx)
|
const matchedResult = resultsDict.find(r => r.agent_id === agentIdx)
|
||||||
if (matchedResult) {
|
if (matchedResult) {
|
||||||
responseContent = matchedResult.response || matchedResult.answer || '无响应'
|
responseContent = matchedResult.response || matchedResult.answer || t('step5.noResponse')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -859,7 +862,7 @@ const submitSurvey = async () => {
|
||||||
surveyResults.value = surveyResultsList
|
surveyResults.value = surveyResultsList
|
||||||
addLog(`收到 ${surveyResults.value.length} 条回复`)
|
addLog(`收到 ${surveyResults.value.length} 条回复`)
|
||||||
} else {
|
} else {
|
||||||
throw new Error(res.error || '请求失败')
|
throw new Error(res.error || t('step5.requestFailed'))
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
addLog(`问卷发送失败: ${err.message}`)
|
addLog(`问卷发送失败: ${err.message}`)
|
||||||
|
|
|
||||||
|
|
@ -172,7 +172,9 @@
|
||||||
"genderMale": "Male",
|
"genderMale": "Male",
|
||||||
"genderFemale": "Female",
|
"genderFemale": "Female",
|
||||||
"genderOther": "Other",
|
"genderOther": "Other",
|
||||||
"yearsOld": "years old"
|
"yearsOld": "years old",
|
||||||
|
"initializing": "Initializing",
|
||||||
|
"generating": "Generating"
|
||||||
},
|
},
|
||||||
"step3": {
|
"step3": {
|
||||||
"startGenerateReport": "Generate Result Report",
|
"startGenerateReport": "Generate Result Report",
|
||||||
|
|
@ -201,7 +203,9 @@
|
||||||
"stoppingProcess": "Stopping simulation process...",
|
"stoppingProcess": "Stopping simulation process...",
|
||||||
"checkStatusFailed": "Failed to check simulation status: {error}",
|
"checkStatusFailed": "Failed to check simulation status: {error}",
|
||||||
"forceStopSuccess": "Simulation force stopped",
|
"forceStopSuccess": "Simulation force stopped",
|
||||||
"forceStopFailed": "Force stop failed: {error}"
|
"forceStopFailed": "Force stop failed: {error}",
|
||||||
|
"startGenerateReportBtn": "Generate Result Report",
|
||||||
|
"generatingReportBtn": "Starting..."
|
||||||
},
|
},
|
||||||
"step4": {
|
"step4": {
|
||||||
"generatingSection": "Generating {title}...",
|
"generatingSection": "Generating {title}...",
|
||||||
|
|
@ -237,7 +241,11 @@
|
||||||
"surveyResults": "Survey Results",
|
"surveyResults": "Survey Results",
|
||||||
"surveyResultsCount": "{count} responses",
|
"surveyResultsCount": "{count} responses",
|
||||||
"selectAll": "Select All",
|
"selectAll": "Select All",
|
||||||
"clearSelection": "Clear"
|
"clearSelection": "Clear",
|
||||||
|
"errorOccurred": "Sorry, an error occurred: {error}",
|
||||||
|
"noResponse": "No response",
|
||||||
|
"requestFailed": "Request failed",
|
||||||
|
"selectAgentFirst": "Please select a simulated individual first"
|
||||||
},
|
},
|
||||||
"graph": {
|
"graph": {
|
||||||
"panelTitle": "Graph Relationship Visualization",
|
"panelTitle": "Graph Relationship Visualization",
|
||||||
|
|
@ -246,7 +254,11 @@
|
||||||
"realtimeUpdating": "Updating in real-time...",
|
"realtimeUpdating": "Updating in real-time...",
|
||||||
"pendingContentHint": "Some content is still processing. Consider refreshing the graph manually later.",
|
"pendingContentHint": "Some content is still processing. Consider refreshing the graph manually later.",
|
||||||
"nodeDetails": "Node Details",
|
"nodeDetails": "Node Details",
|
||||||
"relationship": "Relationship"
|
"relationship": "Relationship",
|
||||||
|
"graphDataLoading": "Loading graph data...",
|
||||||
|
"waitingOntology": "Waiting for ontology generation...",
|
||||||
|
"toggleMaximize": "Maximize/Restore",
|
||||||
|
"closeHint": "Close hint"
|
||||||
},
|
},
|
||||||
"history": {
|
"history": {
|
||||||
"title": "Simulation History",
|
"title": "Simulation History",
|
||||||
|
|
@ -263,7 +275,11 @@
|
||||||
"step1Button": "Graph Build",
|
"step1Button": "Graph Build",
|
||||||
"step2Button": "Env Setup",
|
"step2Button": "Env Setup",
|
||||||
"step4Button": "Analysis Report",
|
"step4Button": "Analysis Report",
|
||||||
"replayHint": "Step 3 'Run Simulation' and Step 5 'Deep Interaction' must be started during runtime and do not support history replay"
|
"replayHint": "Step 3 'Run Simulation' and Step 5 'Deep Interaction' must be started during runtime and do not support history replay",
|
||||||
|
"notStarted": "Not started",
|
||||||
|
"roundsProgress": "{current}/{total} rounds",
|
||||||
|
"untitledSimulation": "Untitled simulation",
|
||||||
|
"unknownFile": "Unknown file"
|
||||||
},
|
},
|
||||||
"api": {
|
"api": {
|
||||||
"projectNotFound": "Project not found: {id}",
|
"projectNotFound": "Project not found: {id}",
|
||||||
|
|
|
||||||
|
|
@ -172,7 +172,9 @@
|
||||||
"genderMale": "男",
|
"genderMale": "男",
|
||||||
"genderFemale": "女",
|
"genderFemale": "女",
|
||||||
"genderOther": "其他",
|
"genderOther": "其他",
|
||||||
"yearsOld": "岁"
|
"yearsOld": "岁",
|
||||||
|
"initializing": "初始化",
|
||||||
|
"generating": "生成中"
|
||||||
},
|
},
|
||||||
"step3": {
|
"step3": {
|
||||||
"startGenerateReport": "开始生成结果报告",
|
"startGenerateReport": "开始生成结果报告",
|
||||||
|
|
@ -201,7 +203,9 @@
|
||||||
"stoppingProcess": "正在停止模拟进程...",
|
"stoppingProcess": "正在停止模拟进程...",
|
||||||
"checkStatusFailed": "检查模拟状态失败: {error}",
|
"checkStatusFailed": "检查模拟状态失败: {error}",
|
||||||
"forceStopSuccess": "模拟已强制停止",
|
"forceStopSuccess": "模拟已强制停止",
|
||||||
"forceStopFailed": "强制停止失败: {error}"
|
"forceStopFailed": "强制停止失败: {error}",
|
||||||
|
"startGenerateReportBtn": "开始生成结果报告",
|
||||||
|
"generatingReportBtn": "启动中..."
|
||||||
},
|
},
|
||||||
"step4": {
|
"step4": {
|
||||||
"generatingSection": "正在生成{title}...",
|
"generatingSection": "正在生成{title}...",
|
||||||
|
|
@ -237,7 +241,11 @@
|
||||||
"surveyResults": "调查结果",
|
"surveyResults": "调查结果",
|
||||||
"surveyResultsCount": "{count} 条回复",
|
"surveyResultsCount": "{count} 条回复",
|
||||||
"selectAll": "全选",
|
"selectAll": "全选",
|
||||||
"clearSelection": "清空"
|
"clearSelection": "清空",
|
||||||
|
"errorOccurred": "抱歉,发生了错误: {error}",
|
||||||
|
"noResponse": "无响应",
|
||||||
|
"requestFailed": "请求失败",
|
||||||
|
"selectAgentFirst": "请先选择一个模拟个体"
|
||||||
},
|
},
|
||||||
"graph": {
|
"graph": {
|
||||||
"panelTitle": "Graph Relationship Visualization",
|
"panelTitle": "Graph Relationship Visualization",
|
||||||
|
|
@ -246,7 +254,11 @@
|
||||||
"realtimeUpdating": "实时更新中...",
|
"realtimeUpdating": "实时更新中...",
|
||||||
"pendingContentHint": "还有少量内容处理中,建议稍后手动刷新图谱",
|
"pendingContentHint": "还有少量内容处理中,建议稍后手动刷新图谱",
|
||||||
"nodeDetails": "Node Details",
|
"nodeDetails": "Node Details",
|
||||||
"relationship": "Relationship"
|
"relationship": "Relationship",
|
||||||
|
"graphDataLoading": "图谱数据加载中...",
|
||||||
|
"waitingOntology": "等待本体生成...",
|
||||||
|
"toggleMaximize": "最大化/还原",
|
||||||
|
"closeHint": "关闭提示"
|
||||||
},
|
},
|
||||||
"history": {
|
"history": {
|
||||||
"title": "推演记录",
|
"title": "推演记录",
|
||||||
|
|
@ -263,7 +275,11 @@
|
||||||
"step1Button": "图谱构建",
|
"step1Button": "图谱构建",
|
||||||
"step2Button": "环境搭建",
|
"step2Button": "环境搭建",
|
||||||
"step4Button": "分析报告",
|
"step4Button": "分析报告",
|
||||||
"replayHint": "Step3「开始模拟」与 Step5「深度互动」需在运行中启动,不支持历史回放"
|
"replayHint": "Step3「开始模拟」与 Step5「深度互动」需在运行中启动,不支持历史回放",
|
||||||
|
"notStarted": "未开始",
|
||||||
|
"roundsProgress": "{current}/{total} 轮",
|
||||||
|
"untitledSimulation": "未命名模拟",
|
||||||
|
"unknownFile": "未知文件"
|
||||||
},
|
},
|
||||||
"api": {
|
"api": {
|
||||||
"projectNotFound": "项目不存在: {id}",
|
"projectNotFound": "项目不存在: {id}",
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue