-
叙事引导方向
+
+
+ 叙事引导方向
+
{{ simulationConfig.event_config.narrative_direction }}
@@ -419,20 +431,67 @@
+
POST /api/simulation/start
模拟环境已准备完成,可以开始运行模拟
-
+
+
+
+
+
⏱️
+
+
系统自动配置的模拟轮数为 {{ autoGeneratedRounds }} 轮,每轮代表现实中 1 小时的时间流逝。
+
完整模拟耗时较长,建议将轮数设置为 50 轮(预计耗时约 30 分钟)以快速预览效果。
+
+
+
+
+
+
+
+
+
+
+ 10
+ 50 (推荐)
+ 120
+
+
预计耗时:约 {{ Math.round(customMaxRounds * 0.6) }} 分钟
+
+
+
+ ℹ️
+ 将使用自动配置的 {{ autoGeneratedRounds }} 轮完整模拟
+
+
+
+
+
@@ -572,6 +631,11 @@ const simulationConfig = ref(null)
const selectedProfile = ref(null)
const showProfilesDetail = ref(true)
+// 模拟轮数配置
+const useCustomRounds = ref(true) // 默认使用自定义轮数(推荐)
+const customMaxRounds = ref(50) // 默认推荐50轮
+const autoGeneratedRounds = ref(120) // 自动生成的轮数(从配置中读取)
+
// Watch stage to update phase
watch(currentStage, (newStage) => {
if (newStage === '生成Agent人设' || newStage === 'generating_profiles') {
@@ -588,6 +652,16 @@ watch(currentStage, (newStage) => {
}
})
+// Watch simulationConfig to update autoGeneratedRounds
+watch(simulationConfig, (newConfig) => {
+ if (newConfig?.time_config) {
+ const totalHours = newConfig.time_config.total_simulation_hours || 120
+ const minutesPerRound = newConfig.time_config.minutes_per_round || 60
+ const calculatedRounds = Math.floor((totalHours * 60) / minutesPerRound)
+ autoGeneratedRounds.value = calculatedRounds || 120
+ }
+}, { immediate: true })
+
// Polling timer
let pollTimer = null
let profilesTimer = null
@@ -622,6 +696,23 @@ const addLog = (msg) => {
emit('add-log', msg)
}
+// 处理开始模拟按钮点击
+const handleStartSimulation = () => {
+ // 构建传递给父组件的参数
+ const params = {}
+
+ if (useCustomRounds.value) {
+ // 用户自定义轮数,传递 max_rounds 参数
+ params.maxRounds = customMaxRounds.value
+ addLog(`开始模拟,自定义轮数: ${customMaxRounds.value} 轮`)
+ } else {
+ // 用户选择保持自动生成的轮数,不传递 max_rounds 参数
+ addLog(`开始模拟,使用自动配置轮数: ${autoGeneratedRounds.value} 轮`)
+ }
+
+ emit('next-step', params)
+}
+
const truncateBio = (bio) => {
if (bio.length > 80) {
return bio.substring(0, 80) + '...'
@@ -942,7 +1033,7 @@ onUnmounted(() => {
}
.badge.success { background: #E8F5E9; color: #2E7D32; }
-.badge.processing { background: #FFF3E0; color: #E65100; }
+.badge.processing { background: #FF5722; color: #FFF; }
.badge.pending { background: #F5F5F5; color: #999; }
.badge.accent { background: #E3F2FD; color: #1565C0; }
@@ -988,7 +1079,7 @@ onUnmounted(() => {
}
.action-btn.primary:hover:not(:disabled) {
- background: #FF5722;
+ opacity: 0.8;
}
.action-btn.secondary {
@@ -1011,6 +1102,15 @@ onUnmounted(() => {
margin-top: 16px;
}
+.action-group.dual {
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+}
+
+.action-group.dual .action-btn {
+ width: 100%;
+}
+
/* Info Card */
.info-card {
background: #F5F5F5;
@@ -1923,14 +2023,25 @@ onUnmounted(() => {
}
.narrative-box .box-label {
- display: block;
+ display: flex;
+ align-items: center;
+ gap: 8px;
color: #666;
- font-size: 12px;
+ font-size: 13px;
letter-spacing: 0.5px;
margin-bottom: 12px;
font-weight: 600;
}
+.special-icon {
+ filter: drop-shadow(0 2px 4px rgba(255, 87, 34, 0.2));
+ transition: transform 0.6s cubic-bezier(0.34, 1.56, 0.64, 1);
+}
+
+.narrative-box:hover .special-icon {
+ transform: rotate(180deg);
+}
+
.narrative-text {
font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
font-size: 14px;
@@ -2039,5 +2150,223 @@ onUnmounted(() => {
line-height: 1.5;
margin: 0;
}
+
+/* 模拟轮数配置样式 */
+.rounds-config-section {
+ margin: 20px 0;
+ padding: 16px;
+ background: #FAFAFA;
+ border-radius: 8px;
+ border: 1px solid #E5E5E5;
+}
+
+.rounds-notice {
+ display: flex;
+ gap: 12px;
+ align-items: flex-start;
+ padding: 14px;
+ background: #FFF8E1;
+ border-radius: 6px;
+ border-left: 3px solid #FFC107;
+ margin-bottom: 16px;
+}
+
+.notice-icon {
+ font-size: 20px;
+ line-height: 1;
+}
+
+.notice-content {
+ flex: 1;
+}
+
+.notice-main {
+ font-size: 13px;
+ color: #333;
+ line-height: 1.6;
+ margin: 0 0 6px 0;
+}
+
+.notice-main strong {
+ color: #E65100;
+}
+
+.notice-sub {
+ font-size: 12px;
+ color: #666;
+ line-height: 1.5;
+ margin: 0;
+}
+
+.notice-sub strong {
+ color: #2E7D32;
+}
+
+.rounds-control {
+ padding: 0 4px;
+}
+
+.custom-checkbox {
+ display: flex;
+ align-items: center;
+ cursor: pointer;
+ user-select: none;
+ gap: 10px;
+}
+
+.custom-checkbox input[type="checkbox"] {
+ display: none;
+}
+
+.checkmark {
+ width: 18px;
+ height: 18px;
+ border: 2px solid #CCC;
+ border-radius: 4px;
+ position: relative;
+ transition: all 0.2s;
+ flex-shrink: 0;
+}
+
+.custom-checkbox input:checked + .checkmark {
+ background: #FF5722;
+ border-color: #FF5722;
+}
+
+.custom-checkbox input:checked + .checkmark::after {
+ content: '';
+ position: absolute;
+ left: 5px;
+ top: 2px;
+ width: 5px;
+ height: 9px;
+ border: solid white;
+ border-width: 0 2px 2px 0;
+ transform: rotate(45deg);
+}
+
+.checkbox-label {
+ font-size: 13px;
+ font-weight: 500;
+ color: #333;
+ display: flex;
+ align-items: center;
+ gap: 8px;
+}
+
+.recommend-badge {
+ font-size: 10px;
+ font-weight: 600;
+ color: #FFF;
+ background: #4CAF50;
+ padding: 2px 6px;
+ border-radius: 3px;
+ text-transform: uppercase;
+}
+
+.slider-container {
+ margin-top: 16px;
+ padding: 16px;
+ background: #FFF;
+ border-radius: 6px;
+ border: 1px solid #E5E5E5;
+}
+
+.slider-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 12px;
+}
+
+.slider-label {
+ font-size: 12px;
+ color: #666;
+ font-weight: 500;
+}
+
+.slider-value {
+ font-family: 'JetBrains Mono', monospace;
+ font-size: 16px;
+ font-weight: 700;
+ color: #FF5722;
+}
+
+.rounds-slider {
+ width: 100%;
+ height: 6px;
+ -webkit-appearance: none;
+ appearance: none;
+ background: linear-gradient(to right, #E0E0E0, #E0E0E0);
+ border-radius: 3px;
+ outline: none;
+ cursor: pointer;
+}
+
+.rounds-slider::-webkit-slider-thumb {
+ -webkit-appearance: none;
+ appearance: none;
+ width: 20px;
+ height: 20px;
+ background: #FF5722;
+ border-radius: 50%;
+ cursor: pointer;
+ box-shadow: 0 2px 6px rgba(255, 87, 34, 0.3);
+ transition: transform 0.2s;
+}
+
+.rounds-slider::-webkit-slider-thumb:hover {
+ transform: scale(1.1);
+}
+
+.rounds-slider::-moz-range-thumb {
+ width: 20px;
+ height: 20px;
+ background: #FF5722;
+ border: none;
+ border-radius: 50%;
+ cursor: pointer;
+ box-shadow: 0 2px 6px rgba(255, 87, 34, 0.3);
+}
+
+.slider-marks {
+ display: flex;
+ justify-content: space-between;
+ margin-top: 8px;
+ font-family: 'JetBrains Mono', monospace;
+ font-size: 10px;
+ color: #999;
+}
+
+.mark-recommend {
+ color: #4CAF50;
+ font-weight: 600;
+}
+
+.slider-estimate {
+ margin: 12px 0 0 0;
+ font-size: 12px;
+ color: #666;
+ text-align: center;
+ padding: 8px;
+ background: #F5F5F5;
+ border-radius: 4px;
+}
+
+.auto-mode-hint {
+ margin-top: 12px;
+ padding: 12px;
+ background: #F5F5F5;
+ border-radius: 6px;
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ font-size: 12px;
+ color: #666;
+}
+
+.hint-icon {
+ font-size: 14px;
+}
diff --git a/frontend/src/views/MainView.vue b/frontend/src/views/MainView.vue
index 333efe9..2a152d9 100644
--- a/frontend/src/views/MainView.vue
+++ b/frontend/src/views/MainView.vue
@@ -156,10 +156,15 @@ const toggleMaximize = (target) => {
}
}
-const handleNextStep = () => {
+const handleNextStep = (params = {}) => {
if (currentStep.value < 5) {
currentStep.value++
addLog(`进入 Step ${currentStep.value}: ${stepNames[currentStep.value - 1]}`)
+
+ // 如果是从 Step 2 进入 Step 3,记录模拟轮数配置
+ if (currentStep.value === 3 && params.maxRounds) {
+ addLog(`自定义模拟轮数: ${params.maxRounds} 轮`)
+ }
}
}
diff --git a/frontend/src/views/SimulationView.vue b/frontend/src/views/SimulationView.vue
index db567a2..12053e8 100644
--- a/frontend/src/views/SimulationView.vue
+++ b/frontend/src/views/SimulationView.vue
@@ -145,10 +145,24 @@ const handleGoBack = () => {
}
}
-const handleNextStep = () => {
+const handleNextStep = (params = {}) => {
addLog('进入 Step 3: 开始模拟')
- // TODO: 跳转到 Step 3
- alert('Step 3: 开始模拟 - Coming soon...')
+
+ // 记录模拟轮数配置
+ if (params.maxRounds) {
+ addLog(`自定义模拟轮数: ${params.maxRounds} 轮`)
+ } else {
+ addLog('使用自动配置的模拟轮数')
+ }
+
+ // TODO: 调用 startSimulation API 并跳转到 Step 3
+ // 可以在这里调用 /api/simulation/start 接口
+ // const startParams = {
+ // simulation_id: currentSimulationId.value,
+ // ...(params.maxRounds && { max_rounds: params.maxRounds })
+ // }
+
+ alert(`Step 3: 开始模拟 - Coming soon...\n${params.maxRounds ? `轮数: ${params.maxRounds}` : '使用自动配置轮数'}`)
}
// --- Data Logic ---