Revamp simulation rounds configuration in Step2EnvSetup.vue for enhanced user experience

- Redesigned the simulation rounds section with a new header layout and improved descriptions for clarity.
- Introduced a toggle for custom round settings, allowing users to switch between automatic and custom configurations.
- Enhanced the slider for custom rounds with better styling and estimated time display, improving usability.
- Updated CSS for a more modern look and feel, including transitions for smoother interactions.
This commit is contained in:
666ghj 2025-12-12 10:53:41 +08:00
parent 65c1835879
commit bf7fe8b053

View file

@ -436,48 +436,77 @@
<!-- 模拟轮数配置 --> <!-- 模拟轮数配置 -->
<div class="rounds-config-section"> <div class="rounds-config-section">
<div class="rounds-notice"> <div class="rounds-header">
<div class="notice-icon"></div> <div class="header-left">
<div class="notice-content"> <span class="section-title">模拟轮数设定</span>
<p class="notice-main">系统自动配置的模拟轮数为 <strong>{{ autoGeneratedRounds }} </strong>每轮代表现实中 <strong>1 小时</strong>的时间流逝</p> <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>
<p class="notice-sub">完整模拟耗时较长建议将轮数设置为 <strong>50 </strong>预计耗时约 30 分钟以快速预览效果</p>
</div> </div>
</div> <label class="switch-control">
<input type="checkbox" v-model="useCustomRounds">
<div class="rounds-control"> <span class="switch-track"></span>
<label class="custom-checkbox"> <span class="switch-label">自定义</span>
<input type="checkbox" v-model="useCustomRounds" />
<span class="checkmark"></span>
<span class="checkbox-label">自定义模拟轮数 <span class="recommend-badge">推荐</span></span>
</label> </label>
<div v-if="useCustomRounds" class="slider-container">
<div class="slider-header">
<span class="slider-label">模拟轮数</span>
<span class="slider-value">{{ customMaxRounds }} </span>
</div> </div>
<Transition name="fade" mode="out-in">
<div v-if="useCustomRounds" class="rounds-content custom" key="custom">
<div class="slider-display">
<div class="slider-main-value">
<span class="val-num">{{ customMaxRounds }}</span>
<span class="val-unit"></span>
</div>
<div class="slider-meta-info">
<span>预计耗时约 {{ Math.round(customMaxRounds * 0.6) }} 分钟</span>
</div>
</div>
<div class="range-wrapper">
<input <input
type="range" type="range"
v-model.number="customMaxRounds" v-model.number="customMaxRounds"
min="10" min="10"
max="120" max="120"
step="5" step="5"
class="rounds-slider" class="minimal-slider"
:style="{ '--percent': ((customMaxRounds - 10) / (120 - 10)) * 100 + '%' }"
/> />
<div class="slider-marks"> <div class="range-marks">
<span>10</span> <span>10</span>
<span class="mark-recommend">50 (推荐)</span> <span
class="mark-recommend"
:class="{ active: customMaxRounds === 40 }"
@click="customMaxRounds = 40"
:style="{ position: 'absolute', left: 'calc(27.27% - 30px)' }"
>40 (推荐)</span>
<span>120</span> <span>120</span>
</div> </div>
<p class="slider-estimate">预计耗时 {{ Math.round(customMaxRounds * 0.6) }} 分钟</p> </div>
</div> </div>
<div v-else class="auto-mode-hint"> <div v-else class="rounds-content auto" key="auto">
<span class="hint-icon"></span> <div class="auto-info-card">
<span>将使用自动配置的 {{ autoGeneratedRounds }} 轮完整模拟</span> <div class="auto-value">
<span class="val-num">{{ autoGeneratedRounds }}</span>
<span class="val-unit"></span>
</div>
<div class="auto-content">
<div class="auto-meta-row">
<span class="duration-badge">
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<circle cx="12" cy="12" r="10"></circle>
<polyline points="12 6 12 12 16 14"></polyline>
</svg>
预计耗时 {{ Math.round(autoGeneratedRounds * 0.6) }} 分钟
</span>
</div>
<div class="auto-desc">
<p class="highlight-tip" @click="useCustomRounds = true">若首次运行强烈建议切换至自定义模式减少模拟轮数以便快速预览效果并降低报错风险 </p>
</div> </div>
</div> </div>
</div> </div>
</div>
</Transition>
</div>
<div class="action-group dual"> <div class="action-group dual">
<button <button
@ -634,8 +663,8 @@ const selectedProfile = ref(null)
const showProfilesDetail = ref(true) const showProfilesDetail = ref(true)
// //
const useCustomRounds = ref(true) // 使 const useCustomRounds = ref(false) // 使
const customMaxRounds = ref(50) // 50 const customMaxRounds = ref(40) // 40
const autoGeneratedRounds = ref(120) // const autoGeneratedRounds = ref(120) //
// Watch stage to update phase // Watch stage to update phase
@ -2155,220 +2184,305 @@ onUnmounted(() => {
/* 模拟轮数配置样式 */ /* 模拟轮数配置样式 */
.rounds-config-section { .rounds-config-section {
margin: 20px 0; margin: 24px 0;
padding: 16px; padding-top: 24px;
background: #FAFAFA; border-top: 1px solid #EAEAEA;
border-radius: 8px;
border: 1px solid #E5E5E5;
} }
.rounds-notice { .rounds-header {
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; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
margin-bottom: 12px; margin-bottom: 20px;
} }
.slider-label { .header-left {
display: flex;
flex-direction: column;
gap: 4px;
}
.section-title {
font-size: 14px;
font-weight: 600;
color: #1E293B;
}
.section-desc {
font-size: 12px;
color: #94A3B8;
}
.desc-highlight {
font-family: 'JetBrains Mono', monospace;
font-weight: 600;
color: #1E293B;
background: #F1F5F9;
padding: 1px 6px;
border-radius: 4px;
margin: 0 2px;
}
/* Switch Control */
.switch-control {
display: flex;
align-items: center;
gap: 8px;
cursor: pointer;
padding: 4px 8px 4px 4px;
border-radius: 20px;
transition: background 0.2s;
}
.switch-control:hover {
background: #F8FAFC;
}
.switch-control input {
display: none;
}
.switch-track {
width: 36px;
height: 20px;
background: #E2E8F0;
border-radius: 10px;
position: relative;
transition: all 0.3s cubic-bezier(0.4, 0.0, 0.2, 1);
}
.switch-track::after {
content: '';
position: absolute;
left: 2px;
top: 2px;
width: 16px;
height: 16px;
background: #FFF;
border-radius: 50%;
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
transition: transform 0.3s cubic-bezier(0.4, 0.0, 0.2, 1);
}
.switch-control input:checked + .switch-track {
background: #000;
}
.switch-control input:checked + .switch-track::after {
transform: translateX(16px);
}
.switch-label {
font-size: 12px;
font-weight: 500;
color: #64748B;
}
.switch-control input:checked ~ .switch-label {
color: #1E293B;
}
/* Slider Content */
.rounds-content {
animation: fadeIn 0.3s ease;
}
.slider-display {
display: flex;
justify-content: space-between;
align-items: flex-end;
margin-bottom: 16px;
}
.slider-main-value {
display: flex;
align-items: baseline;
gap: 4px;
}
.val-num {
font-family: 'JetBrains Mono', monospace;
font-size: 24px;
font-weight: 700;
color: #000;
}
.val-unit {
font-size: 12px; font-size: 12px;
color: #666; color: #666;
font-weight: 500; font-weight: 500;
} }
.slider-value { .slider-meta-info {
font-family: 'JetBrains Mono', monospace; font-family: 'JetBrains Mono', monospace;
font-size: 16px; font-size: 11px;
font-weight: 700; color: #64748B;
color: #FF5722; background: #F1F5F9;
padding: 4px 8px;
border-radius: 4px;
} }
.rounds-slider { .range-wrapper {
position: relative;
padding: 0 2px;
}
.minimal-slider {
-webkit-appearance: none;
width: 100%; width: 100%;
height: 6px; height: 4px;
-webkit-appearance: none; background: #E2E8F0;
appearance: none; border-radius: 2px;
background: linear-gradient(to right, #E0E0E0, #E0E0E0);
border-radius: 3px;
outline: none; outline: none;
background-image: linear-gradient(#000, #000);
background-size: var(--percent, 0%) 100%;
background-repeat: no-repeat;
cursor: pointer; cursor: pointer;
} }
.rounds-slider::-webkit-slider-thumb { .minimal-slider::-webkit-slider-thumb {
-webkit-appearance: none; -webkit-appearance: none;
appearance: none; width: 16px;
width: 20px; height: 16px;
height: 20px;
background: #FF5722;
border-radius: 50%; border-radius: 50%;
background: #FFF;
border: 2px solid #000;
cursor: pointer; cursor: pointer;
box-shadow: 0 2px 6px rgba(255, 87, 34, 0.3); box-shadow: 0 1px 4px rgba(0,0,0,0.1);
transition: transform 0.2s; transition: transform 0.1s;
margin-top: -6px; /* Center thumb */
} }
.rounds-slider::-webkit-slider-thumb:hover { .minimal-slider::-webkit-slider-thumb:hover {
transform: scale(1.1); transform: scale(1.1);
} }
.rounds-slider::-moz-range-thumb { .minimal-slider::-webkit-slider-runnable-track {
width: 20px; height: 4px;
height: 20px; border-radius: 2px;
background: #FF5722;
border: none;
border-radius: 50%;
cursor: pointer;
box-shadow: 0 2px 6px rgba(255, 87, 34, 0.3);
} }
.slider-marks { .range-marks {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
margin-top: 8px; margin-top: 8px;
font-family: 'JetBrains Mono', monospace; font-family: 'JetBrains Mono', monospace;
font-size: 10px; font-size: 10px;
color: #999; color: #94A3B8;
position: relative;
} }
.mark-recommend { .mark-recommend {
color: #4CAF50; cursor: pointer;
transition: color 0.2s;
position: relative;
}
.mark-recommend:hover {
color: #000;
}
.mark-recommend.active {
color: #000;
font-weight: 600; font-weight: 600;
} }
.slider-estimate { .mark-recommend::after {
margin: 12px 0 0 0; content: '';
font-size: 12px; position: absolute;
color: #666; top: -12px;
text-align: center; left: 50%;
padding: 8px; transform: translateX(-50%);
background: #F5F5F5; width: 1px;
border-radius: 4px; height: 4px;
background: #CBD5E1;
} }
.auto-mode-hint { /* Auto Info */
margin-top: 12px; .auto-info-card {
padding: 12px;
background: #F5F5F5;
border-radius: 6px;
display: flex; display: flex;
align-items: center; align-items: center;
gap: 8px; gap: 24px;
font-size: 12px; background: #F8FAFC;
color: #666; padding: 16px 20px;
border-radius: 8px;
} }
.hint-icon { .auto-value {
font-size: 14px; display: flex;
flex-direction: row;
align-items: baseline;
gap: 4px;
padding-right: 24px;
border-right: 1px solid #E2E8F0;
}
.auto-content {
flex: 1;
display: flex;
flex-direction: column;
gap: 8px;
justify-content: center;
}
.auto-meta-row {
display: flex;
align-items: center;
}
.duration-badge {
display: inline-flex;
align-items: center;
gap: 5px;
font-family: 'JetBrains Mono', monospace;
font-size: 11px;
font-weight: 500;
color: #64748B;
background: #FFFFFF;
border: 1px solid #E2E8F0;
padding: 3px 8px;
border-radius: 6px;
box-shadow: 0 1px 2px rgba(0,0,0,0.02);
}
.auto-desc {
display: flex;
flex-direction: column;
gap: 2px;
}
.auto-desc p {
margin: 0;
font-size: 13px;
color: #64748B;
line-height: 1.5;
}
.highlight-tip {
margin-top: 4px !important;
font-size: 12px !important;
color: #000 !important;
font-weight: 500;
cursor: pointer;
}
.highlight-tip:hover {
text-decoration: underline;
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(4px); }
to { opacity: 1; transform: translateY(0); }
}
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.2s ease;
}
.fade-enter-from,
.fade-leave-to {
opacity: 0;
} }
/* Modal Transition */ /* Modal Transition */