2026-05-29 17:40:10 +08:00
|
|
|
|
<template>
|
2026-06-09 17:00:23 +08:00
|
|
|
|
<view class="scenario-page">
|
2026-05-29 17:40:10 +08:00
|
|
|
|
<view class="scenario-shell">
|
|
|
|
|
|
<view class="hero">
|
|
|
|
|
|
<image class="hero-image" src="/static/config-hospital.png" mode="aspectFill"></image>
|
|
|
|
|
|
<view class="hero-fade"></view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
|
|
|
|
|
|
<view class="director-row">
|
|
|
|
|
|
<view class="director-avatar">
|
|
|
|
|
|
<image class="director-image" src="/static/config-doctor.png" mode="aspectFit"></image>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
<view class="speech-bubble">
|
|
|
|
|
|
<text>欢迎回来!请根据您的教学目标配置临床场景。</text>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
|
|
|
|
|
|
<scroll-view class="config-scroll" scroll-y>
|
|
|
|
|
|
<view class="selected-case">
|
|
|
|
|
|
<text class="case-label">当前病例</text>
|
|
|
|
|
|
<text class="case-title">{{ currentCaseTitle }}</text>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
|
|
|
|
|
|
<view class="section">
|
|
|
|
|
|
<view class="section-title">
|
|
|
|
|
|
<view class="star-icon"></view>
|
|
|
|
|
|
<text>王主任推荐</text>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
<view class="recommend-list">
|
|
|
|
|
|
<button
|
|
|
|
|
|
v-for="item in recommendations"
|
|
|
|
|
|
:key="item.id"
|
|
|
|
|
|
class="recommend-card"
|
|
|
|
|
|
:class="{ active: selectedRecommendationId === item.id }"
|
|
|
|
|
|
@click="applyRecommendation(item)"
|
|
|
|
|
|
>
|
|
|
|
|
|
<view class="recommend-head">
|
|
|
|
|
|
<text class="recommend-icon">{{ item.tags[0] }}</text>
|
|
|
|
|
|
<text class="recommend-title">{{ item.title }}</text>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
<text class="recommend-desc">{{ item.desc }}</text>
|
|
|
|
|
|
<view class="tag-row">
|
|
|
|
|
|
<text
|
|
|
|
|
|
v-for="tag in item.tags"
|
|
|
|
|
|
:key="tag"
|
|
|
|
|
|
class="tag"
|
|
|
|
|
|
:class="{ danger: tag === '急躁' }"
|
|
|
|
|
|
>
|
|
|
|
|
|
{{ tag }}
|
|
|
|
|
|
</text>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</button>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
|
2026-06-09 17:00:23 +08:00
|
|
|
|
<view class="section custom-section" :class="{ expanded: customConfigExpanded }">
|
|
|
|
|
|
<button
|
|
|
|
|
|
class="section-title custom-config-toggle"
|
|
|
|
|
|
:class="{ expanded: customConfigExpanded }"
|
|
|
|
|
|
:aria-label="customConfigExpanded ? '收起自定义配置' : '展开自定义配置'"
|
|
|
|
|
|
@click="toggleCustomConfig"
|
|
|
|
|
|
>
|
2026-05-29 17:40:10 +08:00
|
|
|
|
<view class="tune-icon"></view>
|
|
|
|
|
|
<text>自定义配置</text>
|
2026-06-09 17:00:23 +08:00
|
|
|
|
<view class="custom-toggle-spacer"></view>
|
|
|
|
|
|
<view class="toggle-chevron"></view>
|
|
|
|
|
|
</button>
|
|
|
|
|
|
|
|
|
|
|
|
<view class="custom-options" :class="{ expanded: customConfigExpanded }">
|
|
|
|
|
|
<view class="option-block">
|
|
|
|
|
|
<view class="option-label">
|
|
|
|
|
|
<view class="location-icon"></view>
|
|
|
|
|
|
<text>就诊环境</text>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
<view class="option-grid grid-3">
|
|
|
|
|
|
<button
|
|
|
|
|
|
v-for="option in options.environments"
|
|
|
|
|
|
:key="option.value"
|
|
|
|
|
|
class="choice"
|
|
|
|
|
|
:class="{ selected: form.environment === option.value }"
|
|
|
|
|
|
@click="selectOption('environment', option.value)"
|
|
|
|
|
|
>
|
|
|
|
|
|
{{ option.label }}
|
|
|
|
|
|
</button>
|
|
|
|
|
|
</view>
|
2026-05-29 17:40:10 +08:00
|
|
|
|
</view>
|
|
|
|
|
|
|
2026-06-09 17:00:23 +08:00
|
|
|
|
<view class="option-block">
|
|
|
|
|
|
<view class="option-label">
|
|
|
|
|
|
<view class="cake-icon"></view>
|
|
|
|
|
|
<text>年龄段</text>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
<view class="option-grid grid-4">
|
|
|
|
|
|
<button
|
|
|
|
|
|
v-for="option in options.ageGroups"
|
|
|
|
|
|
:key="option.value"
|
|
|
|
|
|
class="choice small"
|
|
|
|
|
|
:class="{ selected: form.ageGroup === option.value }"
|
|
|
|
|
|
@click="selectOption('ageGroup', option.value)"
|
|
|
|
|
|
>
|
|
|
|
|
|
{{ option.label }}
|
|
|
|
|
|
</button>
|
|
|
|
|
|
</view>
|
2026-05-29 17:40:10 +08:00
|
|
|
|
</view>
|
|
|
|
|
|
|
2026-06-09 17:00:23 +08:00
|
|
|
|
<view class="option-block">
|
|
|
|
|
|
<view class="option-label">
|
|
|
|
|
|
<view class="school-icon"></view>
|
|
|
|
|
|
<text>文化程度</text>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
<view class="option-grid grid-3">
|
|
|
|
|
|
<button
|
|
|
|
|
|
v-for="option in options.educations"
|
|
|
|
|
|
:key="option.value"
|
|
|
|
|
|
class="choice mini"
|
|
|
|
|
|
:class="{ selected: form.education === option.value }"
|
|
|
|
|
|
@click="selectOption('education', option.value)"
|
|
|
|
|
|
>
|
|
|
|
|
|
{{ option.label }}
|
|
|
|
|
|
</button>
|
|
|
|
|
|
</view>
|
2026-05-29 17:40:10 +08:00
|
|
|
|
</view>
|
|
|
|
|
|
|
2026-06-09 17:00:23 +08:00
|
|
|
|
<view class="option-block personality-block">
|
|
|
|
|
|
<view class="option-label">
|
|
|
|
|
|
<view class="mind-icon"></view>
|
|
|
|
|
|
<text>性格</text>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
<view class="option-grid grid-3">
|
|
|
|
|
|
<button
|
|
|
|
|
|
v-for="option in options.personalities"
|
|
|
|
|
|
:key="option.value"
|
|
|
|
|
|
class="choice"
|
|
|
|
|
|
:class="{ selected: form.personality === option.value }"
|
|
|
|
|
|
@click="selectOption('personality', option.value)"
|
|
|
|
|
|
>
|
|
|
|
|
|
{{ option.label }}
|
|
|
|
|
|
</button>
|
|
|
|
|
|
</view>
|
2026-05-29 17:40:10 +08:00
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</scroll-view>
|
|
|
|
|
|
|
|
|
|
|
|
<view class="bottom-action">
|
|
|
|
|
|
<button class="generate-button" :disabled="generating" @click="handleGenerate">
|
|
|
|
|
|
<text>{{ generating ? '正在生成...' : '生成模拟场景' }}</text>
|
|
|
|
|
|
<view class="arrow-icon"></view>
|
|
|
|
|
|
</button>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
|
|
|
|
|
|
<view class="toast" :class="{ visible: toastVisible }">{{ toastMessage }}</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
<script setup lang="ts">
|
|
|
|
|
|
import { computed, onMounted, onUnmounted, reactive, ref } from 'vue'
|
2026-06-09 17:00:23 +08:00
|
|
|
|
import { readStoredClinicalCase, type ClinicalCase } from '../../api/cases'
|
2026-05-29 17:40:10 +08:00
|
|
|
|
import {
|
2026-06-09 17:00:23 +08:00
|
|
|
|
DEFAULT_SCENARIO_OPTIONS,
|
2026-05-29 17:40:10 +08:00
|
|
|
|
createScenarioConfig,
|
|
|
|
|
|
fetchScenarioOptions,
|
2026-06-09 17:00:23 +08:00
|
|
|
|
fetchTrainingConfigOptions,
|
2026-05-29 17:40:10 +08:00
|
|
|
|
type ScenarioForm,
|
|
|
|
|
|
type ScenarioOptions,
|
|
|
|
|
|
type ScenarioRecommendation
|
|
|
|
|
|
} from '../../api/scenario'
|
|
|
|
|
|
|
|
|
|
|
|
const props = defineProps<{
|
2026-06-09 17:00:23 +08:00
|
|
|
|
caseItem?: ClinicalCase | null
|
2026-05-29 17:40:10 +08:00
|
|
|
|
}>()
|
|
|
|
|
|
|
|
|
|
|
|
type ScenarioFormKey = keyof ScenarioForm
|
|
|
|
|
|
|
|
|
|
|
|
const form = reactive<ScenarioForm>({
|
|
|
|
|
|
environment: 'outpatient',
|
2026-06-05 15:27:29 +08:00
|
|
|
|
ageGroup: 'youth',
|
2026-05-29 17:40:10 +08:00
|
|
|
|
education: 'higher',
|
|
|
|
|
|
personality: 'calm'
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
const recommendations = ref<ScenarioRecommendation[]>([])
|
2026-06-09 17:00:23 +08:00
|
|
|
|
const options = ref<ScenarioOptions>(DEFAULT_SCENARIO_OPTIONS)
|
2026-05-29 17:40:10 +08:00
|
|
|
|
const selectedRecommendationId = ref('')
|
|
|
|
|
|
const generating = ref(false)
|
|
|
|
|
|
const toastMessage = ref('')
|
|
|
|
|
|
const toastVisible = ref(false)
|
2026-06-09 17:00:23 +08:00
|
|
|
|
const customConfigExpanded = ref(false)
|
|
|
|
|
|
const storedCase = ref<ClinicalCase | null>(null)
|
2026-05-29 17:40:10 +08:00
|
|
|
|
|
|
|
|
|
|
let toastTimer: ReturnType<typeof setTimeout> | null = null
|
|
|
|
|
|
|
2026-06-09 17:00:23 +08:00
|
|
|
|
const activeCase = computed(() => props.caseItem || storedCase.value)
|
|
|
|
|
|
|
2026-05-29 17:40:10 +08:00
|
|
|
|
const currentCaseTitle = computed(() => {
|
2026-06-09 17:00:23 +08:00
|
|
|
|
if (!activeCase.value) return '未选择病例'
|
|
|
|
|
|
return `${activeCase.value.title}(${activeCase.value.caseNo})`
|
2026-05-29 17:40:10 +08:00
|
|
|
|
})
|
|
|
|
|
|
|
2026-06-09 17:00:23 +08:00
|
|
|
|
async function loadScenarioOptions() {
|
|
|
|
|
|
const fallback = await fetchScenarioOptions()
|
|
|
|
|
|
recommendations.value = fallback.recommendations
|
|
|
|
|
|
options.value = fallback.options
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
const result = await fetchTrainingConfigOptions(1)
|
2026-05-29 17:40:10 +08:00
|
|
|
|
options.value = result.options
|
2026-06-09 17:00:23 +08:00
|
|
|
|
Object.assign(form, result.recommended)
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
showToast(error instanceof Error ? error.message : '推荐配置加载失败')
|
|
|
|
|
|
}
|
2026-05-29 17:40:10 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function applyRecommendation(item: ScenarioRecommendation) {
|
|
|
|
|
|
selectedRecommendationId.value = item.id
|
|
|
|
|
|
Object.assign(form, item.defaults)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function selectOption(key: ScenarioFormKey, value: string) {
|
|
|
|
|
|
form[key] = value
|
|
|
|
|
|
selectedRecommendationId.value = ''
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-09 17:00:23 +08:00
|
|
|
|
function toggleCustomConfig() {
|
|
|
|
|
|
customConfigExpanded.value = !customConfigExpanded.value
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-29 17:40:10 +08:00
|
|
|
|
function handleGenerate() {
|
2026-06-09 17:00:23 +08:00
|
|
|
|
if (!activeCase.value) {
|
2026-05-29 17:40:10 +08:00
|
|
|
|
showToast('请先选择病例')
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
generating.value = true
|
|
|
|
|
|
createScenarioConfig({
|
|
|
|
|
|
...form,
|
2026-06-09 17:00:23 +08:00
|
|
|
|
caseId: activeCase.value.id,
|
|
|
|
|
|
caseNo: activeCase.value.caseNo,
|
|
|
|
|
|
mode: activeCase.value.mode === 'teaching' ? 'teaching' : 'practice',
|
2026-05-29 17:40:10 +08:00
|
|
|
|
recommendationId: selectedRecommendationId.value || undefined
|
|
|
|
|
|
}).then(result => {
|
|
|
|
|
|
uni.setStorageSync('clinical-thinking-scenario', result)
|
|
|
|
|
|
showToast('模拟场景已生成')
|
|
|
|
|
|
setTimeout(() => {
|
2026-06-09 17:00:23 +08:00
|
|
|
|
uni.navigateTo({
|
|
|
|
|
|
url: '/pages/chat/chat'
|
|
|
|
|
|
})
|
2026-05-29 17:40:10 +08:00
|
|
|
|
}, 450)
|
2026-06-05 15:27:29 +08:00
|
|
|
|
}).catch(error => {
|
|
|
|
|
|
showToast(error instanceof Error ? error.message : '模拟场景生成失败')
|
2026-05-29 17:40:10 +08:00
|
|
|
|
}).finally(() => {
|
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
|
generating.value = false
|
|
|
|
|
|
}, 600)
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function showToast(message: string) {
|
|
|
|
|
|
if (toastTimer) clearTimeout(toastTimer)
|
|
|
|
|
|
toastMessage.value = message
|
|
|
|
|
|
toastVisible.value = true
|
|
|
|
|
|
toastTimer = setTimeout(() => {
|
|
|
|
|
|
toastVisible.value = false
|
|
|
|
|
|
}, 2200)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-09 17:00:23 +08:00
|
|
|
|
onMounted(() => {
|
|
|
|
|
|
storedCase.value = readStoredClinicalCase()
|
|
|
|
|
|
loadScenarioOptions()
|
|
|
|
|
|
})
|
2026-05-29 17:40:10 +08:00
|
|
|
|
|
|
|
|
|
|
onUnmounted(() => {
|
|
|
|
|
|
if (toastTimer) clearTimeout(toastTimer)
|
|
|
|
|
|
})
|
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
|
|
<style>
|
|
|
|
|
|
page {
|
|
|
|
|
|
min-height: 100%;
|
|
|
|
|
|
background: #f9f9ff;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.scenario-page {
|
|
|
|
|
|
min-height: 100vh;
|
|
|
|
|
|
background: #f9f9ff;
|
|
|
|
|
|
color: #191c21;
|
|
|
|
|
|
font-family: Inter, -apple-system, BlinkMacSystemFont, 'PingFang SC', 'Helvetica Neue', Arial, sans-serif;
|
|
|
|
|
|
-webkit-tap-highlight-color: transparent;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.scenario-shell {
|
|
|
|
|
|
position: relative;
|
|
|
|
|
|
min-height: 100vh;
|
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
|
background: #f9f9ff;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.hero {
|
|
|
|
|
|
position: relative;
|
|
|
|
|
|
flex: 0 0 auto;
|
|
|
|
|
|
height: 33.333vh;
|
|
|
|
|
|
min-height: 245px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.hero-image {
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
height: 100%;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.hero-fade {
|
|
|
|
|
|
position: absolute;
|
|
|
|
|
|
left: 0;
|
|
|
|
|
|
right: 0;
|
|
|
|
|
|
top: 0;
|
|
|
|
|
|
bottom: 0;
|
|
|
|
|
|
background: linear-gradient(180deg, rgba(249, 249, 255, 0), rgba(249, 249, 255, 0.3));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.director-row {
|
|
|
|
|
|
position: relative;
|
|
|
|
|
|
z-index: 2;
|
|
|
|
|
|
margin-top: -40px;
|
|
|
|
|
|
padding: 0 20px;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: flex-start;
|
|
|
|
|
|
gap: 16px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.director-avatar {
|
|
|
|
|
|
flex: 0 0 auto;
|
|
|
|
|
|
width: 96px;
|
|
|
|
|
|
height: 96px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.director-image {
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
height: 100%;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.speech-bubble {
|
|
|
|
|
|
position: relative;
|
|
|
|
|
|
flex: 1;
|
|
|
|
|
|
margin-top: 16px;
|
|
|
|
|
|
padding: 16px;
|
|
|
|
|
|
border: 1px solid rgba(194, 198, 212, 0.3);
|
|
|
|
|
|
border-radius: 16px;
|
|
|
|
|
|
background: #ffffff;
|
|
|
|
|
|
box-shadow: 0 4px 10px rgba(25, 28, 33, 0.1);
|
|
|
|
|
|
color: #00478d;
|
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
|
line-height: 22px;
|
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.speech-bubble::after {
|
|
|
|
|
|
content: '';
|
|
|
|
|
|
position: absolute;
|
|
|
|
|
|
left: -9px;
|
|
|
|
|
|
top: 20px;
|
|
|
|
|
|
width: 16px;
|
|
|
|
|
|
height: 16px;
|
|
|
|
|
|
background: #ffffff;
|
|
|
|
|
|
border-left: 1px solid rgba(194, 198, 212, 0.3);
|
|
|
|
|
|
border-bottom: 1px solid rgba(194, 198, 212, 0.3);
|
|
|
|
|
|
transform: rotate(45deg);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.config-scroll {
|
|
|
|
|
|
box-sizing: border-box;
|
|
|
|
|
|
flex: 1;
|
|
|
|
|
|
height: 1px;
|
|
|
|
|
|
padding: 16px 20px 108px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.selected-case {
|
|
|
|
|
|
margin-bottom: 20px;
|
|
|
|
|
|
padding: 12px;
|
|
|
|
|
|
border: 1px solid rgba(194, 198, 212, 0.35);
|
|
|
|
|
|
border-radius: 12px;
|
|
|
|
|
|
background: rgba(255, 255, 255, 0.7);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.case-label {
|
|
|
|
|
|
display: block;
|
|
|
|
|
|
margin-bottom: 4px;
|
|
|
|
|
|
color: #727783;
|
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
|
line-height: 16px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.case-title {
|
|
|
|
|
|
color: #191c21;
|
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
|
line-height: 20px;
|
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.section {
|
|
|
|
|
|
margin-bottom: 24px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.section-title {
|
|
|
|
|
|
margin-bottom: 16px;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
gap: 8px;
|
|
|
|
|
|
color: #191c21;
|
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
|
line-height: 20px;
|
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.star-icon,
|
|
|
|
|
|
.tune-icon,
|
|
|
|
|
|
.location-icon,
|
|
|
|
|
|
.cake-icon,
|
|
|
|
|
|
.school-icon,
|
|
|
|
|
|
.mind-icon,
|
|
|
|
|
|
.arrow-icon {
|
|
|
|
|
|
background: #00478d;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.star-icon,
|
|
|
|
|
|
.tune-icon {
|
|
|
|
|
|
width: 20px;
|
|
|
|
|
|
height: 20px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.star-icon {
|
|
|
|
|
|
-webkit-mask: url("data:image/svg+xml,%3Csvg%20viewBox='0%200%2024%2024'%20xmlns='http://www.w3.org/2000/svg'%3E%3Cpath%20d='m12%202%202.7%206.6%207.1.6-5.4%204.6%201.7%206.9L12%2017l-6.1%203.7%201.7-6.9-5.4-4.6%207.1-.6L12%202z'/%3E%3C/svg%3E") center / contain no-repeat;
|
|
|
|
|
|
mask: url("data:image/svg+xml,%3Csvg%20viewBox='0%200%2024%2024'%20xmlns='http://www.w3.org/2000/svg'%3E%3Cpath%20d='m12%202%202.7%206.6%207.1.6-5.4%204.6%201.7%206.9L12%2017l-6.1%203.7%201.7-6.9-5.4-4.6%207.1-.6L12%202z'/%3E%3C/svg%3E") center / contain no-repeat;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.tune-icon {
|
|
|
|
|
|
-webkit-mask: url("data:image/svg+xml,%3Csvg%20viewBox='0%200%2024%2024'%20xmlns='http://www.w3.org/2000/svg'%3E%3Cpath%20d='M3%2017v2h6v-2H3zM3%205v2h10V5H3zm10%2016v-2h8v-2h-8v-2h-2v6h2zM7%209v2H3v2h4v2h2V9H7zm14%204v-2H11v2h10zm-6-4h2V7h4V5h-4V3h-2v6z'/%3E%3C/svg%3E") center / contain no-repeat;
|
|
|
|
|
|
mask: url("data:image/svg+xml,%3Csvg%20viewBox='0%200%2024%2024'%20xmlns='http://www.w3.org/2000/svg'%3E%3Cpath%20d='M3%2017v2h6v-2H3zM3%205v2h10V5H3zm10%2016v-2h8v-2h-8v-2h-2v6h2zM7%209v2H3v2h4v2h2V9H7zm14%204v-2H11v2h10zm-6-4h2V7h4V5h-4V3h-2v6z'/%3E%3C/svg%3E") center / contain no-repeat;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.recommend-list,
|
|
|
|
|
|
.custom-section {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
gap: 12px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-09 17:00:23 +08:00
|
|
|
|
.custom-section {
|
|
|
|
|
|
margin-bottom: 16px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.custom-section.expanded {
|
|
|
|
|
|
margin-bottom: 24px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.custom-config-toggle {
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
min-height: 44px;
|
|
|
|
|
|
margin: 0;
|
|
|
|
|
|
padding: 0;
|
|
|
|
|
|
border: 0;
|
|
|
|
|
|
background: transparent;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
gap: 8px;
|
|
|
|
|
|
text-align: left;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.custom-config-toggle::after {
|
|
|
|
|
|
border: 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.custom-toggle-spacer {
|
|
|
|
|
|
flex: 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.toggle-chevron {
|
|
|
|
|
|
width: 20px;
|
|
|
|
|
|
height: 20px;
|
|
|
|
|
|
background: #424752;
|
|
|
|
|
|
transition: transform 0.2s ease, background 0.2s ease;
|
|
|
|
|
|
-webkit-mask: url("data:image/svg+xml,%3Csvg%20viewBox='0%200%2024%2024'%20xmlns='http://www.w3.org/2000/svg'%3E%3Cpath%20d='M7.4%208.6%2012%2013.2l4.6-4.6L18%2010l-6%206-6-6%201.4-1.4z'/%3E%3C/svg%3E") center / contain no-repeat;
|
|
|
|
|
|
mask: url("data:image/svg+xml,%3Csvg%20viewBox='0%200%2024%2024'%20xmlns='http://www.w3.org/2000/svg'%3E%3Cpath%20d='M7.4%208.6%2012%2013.2l4.6-4.6L18%2010l-6%206-6-6%201.4-1.4z'/%3E%3C/svg%3E") center / contain no-repeat;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.custom-config-toggle.expanded .toggle-chevron {
|
|
|
|
|
|
background: #00478d;
|
|
|
|
|
|
transform: rotate(180deg);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.custom-options {
|
|
|
|
|
|
max-height: 0;
|
|
|
|
|
|
opacity: 0;
|
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
|
transform: translateY(-4px);
|
|
|
|
|
|
transition: max-height 0.24s ease, opacity 0.18s ease, transform 0.18s ease;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
gap: 16px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.custom-options.expanded {
|
|
|
|
|
|
max-height: 760px;
|
|
|
|
|
|
opacity: 1;
|
|
|
|
|
|
transform: translateY(0);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-29 17:40:10 +08:00
|
|
|
|
.recommend-card {
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
padding: 16px;
|
|
|
|
|
|
border: 1px solid #c2c6d4;
|
|
|
|
|
|
border-radius: 12px;
|
|
|
|
|
|
background: rgba(255, 255, 255, 0.7);
|
|
|
|
|
|
text-align: left;
|
|
|
|
|
|
backdrop-filter: blur(12px);
|
|
|
|
|
|
-webkit-backdrop-filter: blur(12px);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.recommend-card.active {
|
|
|
|
|
|
border-color: #00478d;
|
|
|
|
|
|
background: rgba(214, 227, 255, 0.46);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.recommend-card::after,
|
|
|
|
|
|
.choice::after,
|
|
|
|
|
|
.generate-button::after {
|
|
|
|
|
|
border: 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.recommend-card:active,
|
|
|
|
|
|
.choice:active,
|
|
|
|
|
|
.generate-button:active {
|
|
|
|
|
|
transform: scale(0.98);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.recommend-head {
|
|
|
|
|
|
margin-bottom: 8px;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
gap: 16px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.recommend-icon {
|
|
|
|
|
|
color: #00478d;
|
|
|
|
|
|
font-size: 13px;
|
|
|
|
|
|
line-height: 18px;
|
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.recommend-title {
|
|
|
|
|
|
color: #191c21;
|
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
|
line-height: 20px;
|
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.recommend-desc {
|
|
|
|
|
|
display: block;
|
|
|
|
|
|
margin-bottom: 8px;
|
|
|
|
|
|
color: #424752;
|
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
|
line-height: 18px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.tag-row {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
gap: 8px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.tag {
|
|
|
|
|
|
padding: 2px 8px;
|
|
|
|
|
|
border-radius: 999px;
|
|
|
|
|
|
background: #ecedf6;
|
|
|
|
|
|
color: #191c21;
|
|
|
|
|
|
font-size: 10px;
|
|
|
|
|
|
line-height: 16px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.tag.danger {
|
|
|
|
|
|
background: rgba(255, 218, 214, 0.35);
|
|
|
|
|
|
color: #ba1a1a;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.option-block {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
gap: 12px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.option-label {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
gap: 4px;
|
|
|
|
|
|
color: #424752;
|
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
|
line-height: 16px;
|
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.location-icon,
|
|
|
|
|
|
.cake-icon,
|
|
|
|
|
|
.school-icon,
|
|
|
|
|
|
.mind-icon {
|
|
|
|
|
|
width: 14px;
|
|
|
|
|
|
height: 14px;
|
|
|
|
|
|
background: #424752;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.location-icon {
|
|
|
|
|
|
-webkit-mask: url("data:image/svg+xml,%3Csvg%20viewBox='0%200%2024%2024'%20xmlns='http://www.w3.org/2000/svg'%3E%3Cpath%20d='M12%202a7%207%200%200%200-7%207c0%205.25%207%2013%207%2013s7-7.75%207-13a7%207%200%200%200-7-7zm0%209.5A2.5%202.5%200%201%201%2012%206a2.5%202.5%200%200%201%200%205.5z'/%3E%3C/svg%3E") center / contain no-repeat;
|
|
|
|
|
|
mask: url("data:image/svg+xml,%3Csvg%20viewBox='0%200%2024%2024'%20xmlns='http://www.w3.org/2000/svg'%3E%3Cpath%20d='M12%202a7%207%200%200%200-7%207c0%205.25%207%2013%207%2013s7-7.75%207-13a7%207%200%200%200-7-7zm0%209.5A2.5%202.5%200%201%201%2012%206a2.5%202.5%200%200%201%200%205.5z'/%3E%3C/svg%3E") center / contain no-repeat;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.cake-icon,
|
|
|
|
|
|
.school-icon,
|
|
|
|
|
|
.mind-icon {
|
|
|
|
|
|
border-radius: 50%;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.option-grid {
|
|
|
|
|
|
display: grid;
|
2026-06-09 17:00:23 +08:00
|
|
|
|
gap: 10px;
|
|
|
|
|
|
width: 100%;
|
2026-05-29 17:40:10 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.grid-3 {
|
|
|
|
|
|
grid-template-columns: repeat(3, minmax(0, 1fr));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.grid-4 {
|
|
|
|
|
|
grid-template-columns: repeat(4, minmax(0, 1fr));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.choice {
|
2026-06-09 17:00:23 +08:00
|
|
|
|
box-sizing: border-box;
|
|
|
|
|
|
width: 100%;
|
2026-05-29 17:40:10 +08:00
|
|
|
|
min-width: 0;
|
2026-06-09 17:00:23 +08:00
|
|
|
|
min-height: 42px;
|
|
|
|
|
|
padding: 8px 6px;
|
2026-05-29 17:40:10 +08:00
|
|
|
|
border: 1px solid #c2c6d4;
|
|
|
|
|
|
border-radius: 8px;
|
|
|
|
|
|
background: rgba(255, 255, 255, 0.76);
|
|
|
|
|
|
color: #424752;
|
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
|
line-height: 20px;
|
2026-06-09 17:00:23 +08:00
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
text-align: center;
|
|
|
|
|
|
white-space: nowrap;
|
2026-05-29 17:40:10 +08:00
|
|
|
|
backdrop-filter: blur(12px);
|
|
|
|
|
|
-webkit-backdrop-filter: blur(12px);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.choice.small {
|
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.choice.mini {
|
|
|
|
|
|
font-size: 11px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.choice.selected {
|
|
|
|
|
|
border-color: #00478d;
|
|
|
|
|
|
background: rgba(0, 71, 141, 0.1);
|
|
|
|
|
|
color: #00478d;
|
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.personality-block {
|
|
|
|
|
|
padding-bottom: 32px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.bottom-action {
|
|
|
|
|
|
position: absolute;
|
|
|
|
|
|
left: 0;
|
|
|
|
|
|
right: 0;
|
|
|
|
|
|
bottom: 0;
|
|
|
|
|
|
z-index: 3;
|
|
|
|
|
|
box-sizing: border-box;
|
|
|
|
|
|
padding: 32px 20px 20px;
|
|
|
|
|
|
background: linear-gradient(180deg, rgba(249, 249, 255, 0), #f9f9ff 38%, #f9f9ff);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.generate-button {
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
min-height: 56px;
|
|
|
|
|
|
border-radius: 999px;
|
|
|
|
|
|
background: #00478d;
|
|
|
|
|
|
box-shadow: 0 8px 18px rgba(0, 71, 141, 0.22);
|
|
|
|
|
|
color: #ffffff;
|
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
|
line-height: 20px;
|
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
gap: 12px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.arrow-icon {
|
|
|
|
|
|
width: 20px;
|
|
|
|
|
|
height: 20px;
|
|
|
|
|
|
background: #ffffff;
|
|
|
|
|
|
-webkit-mask: url("data:image/svg+xml,%3Csvg%20viewBox='0%200%2024%2024'%20xmlns='http://www.w3.org/2000/svg'%3E%3Cpath%20d='m12%204-1.41%201.41L16.17%2011H4v2h12.17l-5.58%205.59L12%2020l8-8-8-8z'/%3E%3C/svg%3E") center / contain no-repeat;
|
|
|
|
|
|
mask: url("data:image/svg+xml,%3Csvg%20viewBox='0%200%2024%2024'%20xmlns='http://www.w3.org/2000/svg'%3E%3Cpath%20d='m12%204-1.41%201.41L16.17%2011H4v2h12.17l-5.58%205.59L12%2020l8-8-8-8z'/%3E%3C/svg%3E") center / contain no-repeat;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.toast {
|
|
|
|
|
|
position: fixed;
|
|
|
|
|
|
left: 50%;
|
|
|
|
|
|
bottom: 96px;
|
|
|
|
|
|
z-index: 100;
|
|
|
|
|
|
max-width: 320px;
|
|
|
|
|
|
padding: 12px 24px;
|
|
|
|
|
|
border-radius: 999px;
|
|
|
|
|
|
background: #2e3037;
|
|
|
|
|
|
color: #eff0f8;
|
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
|
line-height: 20px;
|
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
text-align: center;
|
|
|
|
|
|
pointer-events: none;
|
|
|
|
|
|
opacity: 0;
|
|
|
|
|
|
transform: translate(-50%, 16px);
|
|
|
|
|
|
transition: opacity 0.3s ease, transform 0.3s ease;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.toast.visible {
|
|
|
|
|
|
opacity: 1;
|
|
|
|
|
|
transform: translate(-50%, 0);
|
|
|
|
|
|
}
|
|
|
|
|
|
</style>
|