Update Step1GraphBuild.vue and Step2EnvSetup.vue with API endpoint notes and enhanced styling

- Added API endpoint notes for simulation creation and preparation in Step1GraphBuild.vue and Step2EnvSetup.vue respectively.
- Updated badge styling for processing state to improve visibility.
- Enhanced the narrative box layout and added a custom checkbox for configuring simulation rounds in Step2EnvSetup.vue.
- Introduced a new section for simulation rounds configuration, including user-defined options and estimated time for completion.
This commit is contained in:
666ghj 2025-12-11 23:55:54 +08:00
parent 5c6a5696d8
commit fdbb0e2dc4
4 changed files with 364 additions and 15 deletions

View file

@ -156,6 +156,7 @@
</div>
<div class="card-content">
<p class="api-note">POST /api/simulation/create</p>
<p class="description">图谱构建已完成请进入下一步进行模拟环境搭建</p>
<button
class="action-btn"
@ -343,7 +344,7 @@ watch(() => props.systemLogs.length, () => {
}
.badge.success { background: #E8F5E9; color: #2E7D32; }
.badge.processing { background: #FFF3E0; color: #EF6C00; }
.badge.processing { background: #FF5722; color: #FFF; }
.badge.accent { background: #FF5722; color: #FFF; }
.badge.pending { background: #F5F5F5; color: #999; }

View file

@ -361,7 +361,7 @@
</div>
<div class="card-content">
<p class="api-note">Event Orchestration</p>
<p class="api-note">POST /api/simulation/prepare</p>
<p class="description">
基于叙事方向自动生成初始激活事件与热点话题引导模拟世界的初始状态
</p>
@ -369,7 +369,19 @@
<div v-if="simulationConfig?.event_config" class="orchestration-content">
<!-- 叙事方向 -->
<div class="narrative-box">
<span class="box-label">叙事引导方向</span>
<span class="box-label narrative-label">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" class="special-icon">
<path d="M12 22C17.5228 22 22 17.5228 22 12C22 6.47715 17.5228 2 12 2C6.47715 2 2 6.47715 2 12C2 17.5228 6.47715 22 12 22Z" stroke="url(#paint0_linear)" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M16.24 7.76L14.12 14.12L7.76 16.24L9.88 9.88L16.24 7.76Z" fill="url(#paint0_linear)" stroke="url(#paint0_linear)" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<defs>
<linearGradient id="paint0_linear" x1="2" y1="2" x2="22" y2="22" gradientUnits="userSpaceOnUse">
<stop stop-color="#FF5722"/>
<stop offset="1" stop-color="#FF9800"/>
</linearGradient>
</defs>
</svg>
叙事引导方向
</span>
<p class="narrative-text">{{ simulationConfig.event_config.narrative_direction }}</p>
</div>
@ -419,20 +431,67 @@
</div>
<div class="card-content">
<p class="api-note">POST /api/simulation/start</p>
<p class="description">模拟环境已准备完成可以开始运行模拟</p>
<div class="action-group">
<!-- 模拟轮数配置 -->
<div class="rounds-config-section">
<div class="rounds-notice">
<div class="notice-icon"></div>
<div class="notice-content">
<p class="notice-main">系统自动配置的模拟轮数为 <strong>{{ autoGeneratedRounds }} </strong>每轮代表现实中 <strong>1 小时</strong>的时间流逝</p>
<p class="notice-sub">完整模拟耗时较长建议将轮数设置为 <strong>50 </strong>预计耗时约 30 分钟以快速预览效果</p>
</div>
</div>
<div class="rounds-control">
<label class="custom-checkbox">
<input type="checkbox" v-model="useCustomRounds" />
<span class="checkmark"></span>
<span class="checkbox-label">自定义模拟轮数 <span class="recommend-badge">推荐</span></span>
</label>
<div v-if="useCustomRounds" class="slider-container">
<div class="slider-header">
<span class="slider-label">模拟轮数</span>
<span class="slider-value">{{ customMaxRounds }} </span>
</div>
<input
type="range"
v-model.number="customMaxRounds"
min="10"
max="120"
step="5"
class="rounds-slider"
/>
<div class="slider-marks">
<span>10</span>
<span class="mark-recommend">50 (推荐)</span>
<span>120</span>
</div>
<p class="slider-estimate">预计耗时 {{ Math.round(customMaxRounds * 0.6) }} 分钟</p>
</div>
<div v-else class="auto-mode-hint">
<span class="hint-icon"></span>
<span>将使用自动配置的 {{ autoGeneratedRounds }} 轮完整模拟</span>
</div>
</div>
</div>
<div class="action-group dual">
<button
class="action-btn secondary"
@click="$emit('go-back')"
>
返回图谱
返回图谱构建
</button>
<button
class="action-btn primary"
:disabled="phase < 4"
@click="$emit('next-step')"
@click="handleStartSimulation"
>
开始模拟
开始双世界并行模拟
</button>
</div>
</div>
@ -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;
}
</style>

View file

@ -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}`)
}
}
}

View file

@ -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 ---