|
|
|
@ -1,150 +1,162 @@
|
|
|
|
<template>
|
|
|
|
<template>
|
|
|
|
<div class="quick-plan">
|
|
|
|
<div class="quick-plan">
|
|
|
|
<!-- ========== List View ========== -->
|
|
|
|
<!-- ========== Main View ========== -->
|
|
|
|
<template v-if="viewMode === 'list'">
|
|
|
|
<div class="qp-header">
|
|
|
|
<div class="qp-header">
|
|
|
|
<h1>⚡ 快速规划</h1>
|
|
|
|
<h1>⚡ 快速规划</h1>
|
|
|
|
<p>输入目的地和天数,立即生成多个旅行方案</p>
|
|
|
|
<p>输入目的地和天数,立即生成多个旅行方案</p>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- Input form -->
|
|
|
|
<!-- Input form -->
|
|
|
|
<div v-if="!isGenerating && allSchemes.length === 0" class="form-wrap">
|
|
|
|
<div v-if="allSchemes.length === 0" class="form-wrap">
|
|
|
|
<div class="form-row">
|
|
|
|
<div class="form-row">
|
|
|
|
<div class="fg"><label>目的地</label><input v-model="destination" placeholder="如:云南、贵州、四川" :disabled="isGenerating" value="四川"></div>
|
|
|
|
<div class="fg"><label>目的地</label><input v-model="destination" placeholder="如:云南、贵州、四川" :disabled="isGenerating" value="四川"></div>
|
|
|
|
<div class="fg"><label>出行天数</label>
|
|
|
|
<div class="fg"><label>出行天数</label>
|
|
|
|
<select v-model="days" :disabled="isGenerating">
|
|
|
|
<select v-model="days" :disabled="isGenerating">
|
|
|
|
<option v-for="d in [2,3,5,7]" :key="d" :value="d">{{ d }}天</option>
|
|
|
|
<option v-for="d in [2,3,5,7]" :key="d" :value="d">{{ d }}天</option>
|
|
|
|
</select>
|
|
|
|
</select>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div class="form-row">
|
|
|
|
</div>
|
|
|
|
<div class="fg"><label>出发日期</label><input type="date" v-model="startDate" :disabled="isGenerating"></div>
|
|
|
|
<div class="form-row">
|
|
|
|
<div class="fg"><label>出行方式</label>
|
|
|
|
<div class="fg"><label>出发日期</label><input type="date" v-model="startDate" :disabled="isGenerating"></div>
|
|
|
|
<select v-model="transport" :disabled="isGenerating">
|
|
|
|
<div class="fg"><label>出行方式</label>
|
|
|
|
<option value="自驾">自驾</option>
|
|
|
|
<select v-model="transport" :disabled="isGenerating">
|
|
|
|
<option value="公共交通">公共交通</option>
|
|
|
|
<option value="自驾">自驾</option>
|
|
|
|
<option value="步行/骑行">步行/骑行</option>
|
|
|
|
<option value="公共交通">公共交通</option>
|
|
|
|
</select>
|
|
|
|
<option value="步行/骑行">步行/骑行</option>
|
|
|
|
</div>
|
|
|
|
</select>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div class="fg"><label>其他需求(选填)</label><input v-model="extraNeeds" placeholder="如:带老人小孩、预算范围、必去景点等" :disabled="isGenerating"></div>
|
|
|
|
|
|
|
|
<button class="btn-primary" @click="generatePlan" :disabled="!destination.trim() || isGenerating">
|
|
|
|
|
|
|
|
{{ isGenerating ? '生成中...' : '立即生成方案' }}
|
|
|
|
|
|
|
|
</button>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<div class="fg"><label>其他需求(选填)</label><input v-model="extraNeeds" placeholder="如:带老人小孩、预算范围、必去景点等" :disabled="isGenerating"></div>
|
|
|
|
<!-- AI Thinking process -->
|
|
|
|
<button class="btn-primary" @click="generatePlan" :disabled="!destination.trim() || isGenerating">
|
|
|
|
<div v-if="isGenerating" class="load-panel">
|
|
|
|
{{ isGenerating ? '生成中...' : '立即生成方案' }}
|
|
|
|
<div class="lp-head" @click="thinkingExpanded = !thinkingExpanded">
|
|
|
|
</button>
|
|
|
|
<div class="spin"></div><span>AI 正在规划中...</span>
|
|
|
|
</div>
|
|
|
|
<span class="lp-toggle">{{ thinkingExpanded ? '收起' : '展开' }}</span>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- AI Thinking process -->
|
|
|
|
|
|
|
|
<div v-if="isGenerating" class="load-panel">
|
|
|
|
|
|
|
|
<div class="lp-head" @click="thinkingExpanded = !thinkingExpanded">
|
|
|
|
|
|
|
|
<div class="spin"></div><span>AI 正在规划中...</span>
|
|
|
|
|
|
|
|
<span class="lp-toggle">{{ thinkingExpanded ? '收起' : '展开' }}</span>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<transition name="expand">
|
|
|
|
|
|
|
|
<div v-if="thinkingExpanded" class="lp-body">
|
|
|
|
|
|
|
|
<div class="lp-step" v-if="thinkingText">{{ thinkingText }}</div>
|
|
|
|
|
|
|
|
<div class="lp-step" v-else>分析用户需求中...</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<transition name="expand">
|
|
|
|
</transition>
|
|
|
|
<div v-if="thinkingExpanded" class="lp-body">
|
|
|
|
</div>
|
|
|
|
<div class="lp-step" v-if="thinkingText">{{ thinkingText }}</div>
|
|
|
|
|
|
|
|
<div class="lp-step" v-else>分析用户需求中...</div>
|
|
|
|
<!-- Scheme list with regenerate button -->
|
|
|
|
</div>
|
|
|
|
<div v-if="allSchemes.length > 0 && !isGenerating" class="plan-list">
|
|
|
|
</transition>
|
|
|
|
<div class="pl-head">
|
|
|
|
|
|
|
|
<h2>为你生成了 {{ allSchemes.length }} 个方案</h2>
|
|
|
|
|
|
|
|
<button class="btn-sm outline" @click="regeneratePlan" :disabled="isGenerating">🔄 重新生成</button>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- Regenerate button -->
|
|
|
|
<!-- Scheme cards -->
|
|
|
|
<div v-if="allSchemes.length > 0 && !isGenerating" class="plan-list">
|
|
|
|
<div class="plan-card" v-for="(scheme, i) in allSchemes" :key="'scheme-' + i">
|
|
|
|
<div class="pl-head"><h2>为你生成了 {{ allSchemes.length }} 个方案</h2><button class="btn-sm outline" @click="generatePlan" :disabled="isGenerating">重新生成</button></div>
|
|
|
|
<div class="pc-top">
|
|
|
|
|
|
|
|
<span class="pc-badge" :class="getBadgeClass(i)">{{ getSchemeLabel(i) }}</span>
|
|
|
|
<div class="plan-card" v-for="(scheme, i) in allSchemes" :key="'current-' + i" @click="selectScheme(i, 'current')">
|
|
|
|
<span class="pc-title">{{ scheme.name }}</span>
|
|
|
|
<div class="pc-top"><span class="pc-badge" :class="getBadgeClass(i)">{{ getSchemeLabel(i) }}</span><span class="pc-title">{{ scheme.name }}</span></div>
|
|
|
|
</div>
|
|
|
|
<div class="pc-meta"><span>{{ scheme.days }}天</span><span>~{{ scheme.totalKm }}km</span><span>{{ scheme.budget }}</span></div>
|
|
|
|
<div class="pc-meta">
|
|
|
|
<div class="pc-route">{{ scheme.route }}</div>
|
|
|
|
<span>{{ scheme.days }}天</span>
|
|
|
|
<div class="pc-tags"><span v-for="(h, j) in scheme.highlights" :key="j" class="pc-tag">{{ h }}</span></div>
|
|
|
|
<span>~{{ scheme.totalKm }}km</span>
|
|
|
|
<div class="pc-actions"><button class="pc-btn">选择此方案</button></div>
|
|
|
|
<span>{{ scheme.budget }}</span>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<div class="pc-route">{{ scheme.route }}</div>
|
|
|
|
|
|
|
|
<div class="pc-tags">
|
|
|
|
|
|
|
|
<span v-for="(h, j) in (scheme.highlights || []).slice(0, 3)" :key="j" class="pc-tag">{{ h }}</span>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<div class="pc-actions">
|
|
|
|
|
|
|
|
<button class="pc-btn preview" @click.stop="openPreview(i)">预览</button>
|
|
|
|
|
|
|
|
<button class="pc-btn select" @click.stop="selectScheme(i)">选择此方案</button>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
<!-- History section -->
|
|
|
|
|
|
|
|
<div v-if="historySchemes.length > 0 && !isGenerating" class="history-section">
|
|
|
|
<!-- Preview Modal -->
|
|
|
|
<h3 @click="historyExpanded = !historyExpanded" style="cursor:pointer;">
|
|
|
|
<Teleport to="body">
|
|
|
|
历史方案 ({{ historySchemes.length }})
|
|
|
|
<div v-if="previewIndex >= 0" class="modal-overlay" @click="closePreview">
|
|
|
|
<span class="toggle-icon">{{ historyExpanded ? '▲' : '▼' }}</span>
|
|
|
|
<div class="modal-content" @click.stop>
|
|
|
|
</h3>
|
|
|
|
<div class="modal-header">
|
|
|
|
<transition name="expand">
|
|
|
|
<div>
|
|
|
|
<div v-if="historyExpanded">
|
|
|
|
<span class="pc-badge" :class="getBadgeClass(previewIndex)">{{ getSchemeLabel(previewIndex) }}</span>
|
|
|
|
<div v-for="(group, gi) in historySchemes" :key="'history-' + gi" class="history-group">
|
|
|
|
<h2>{{ previewScheme.name }}</h2>
|
|
|
|
<div class="group-label">第 {{ gi + 1 }} 组</div>
|
|
|
|
|
|
|
|
<div v-for="(scheme, si) in group.schemes" :key="si" class="history-card" @click="selectScheme(si, 'history', gi)">
|
|
|
|
|
|
|
|
<div class="card-badge">{{ getSchemeLabel(si) }}</div>
|
|
|
|
|
|
|
|
<h5>{{ scheme.name }}</h5>
|
|
|
|
|
|
|
|
<div class="card-stats"><span>{{ scheme.days }}天</span><span>~{{ scheme.totalKm }}km</span></div>
|
|
|
|
|
|
|
|
<div class="card-action">查看详情</div>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<button class="modal-close" @click="closePreview">×</button>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</transition>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
<div class="modal-meta">
|
|
|
|
</template>
|
|
|
|
<span>{{ previewScheme.days }}天</span>
|
|
|
|
|
|
|
|
<span>{{ previewScheme.route }}</span>
|
|
|
|
<!-- ========== Detail View ========== -->
|
|
|
|
<span>预算 {{ previewScheme.budget }}</span>
|
|
|
|
<template v-else-if="viewMode === 'detail'">
|
|
|
|
|
|
|
|
<div class="detail-view">
|
|
|
|
|
|
|
|
<div class="detail-header">
|
|
|
|
|
|
|
|
<button class="back-btn" @click="goBackToList">返回</button>
|
|
|
|
|
|
|
|
<h2>{{ currentDetailScheme.name }}</h2>
|
|
|
|
|
|
|
|
<div class="detail-meta">
|
|
|
|
|
|
|
|
<span>{{ currentDetailScheme.days }}天</span>
|
|
|
|
|
|
|
|
<span>{{ currentDetailScheme.route }}</span>
|
|
|
|
|
|
|
|
<span>预算 {{ currentDetailScheme.budget }}</span>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<button class="use-btn" @click="useThisScheme">使用此方案进入工作台</button>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<!-- Scheme switcher -->
|
|
|
|
|
|
|
|
<div class="scheme-switcher">
|
|
|
|
|
|
|
|
<div class="switcher-label">切换方案:</div>
|
|
|
|
|
|
|
|
<div class="switcher-btns">
|
|
|
|
|
|
|
|
<template v-if="currentDetailSource === 'current'">
|
|
|
|
|
|
|
|
<button v-for="(s, i) in allSchemes" :key="'switch-' + i" :class="['switch-btn', { active: currentDetailIndex === i }]" @click="selectScheme(i, 'current')">
|
|
|
|
|
|
|
|
{{ getSchemeLabel(i) }}
|
|
|
|
|
|
|
|
</button>
|
|
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
<template v-else>
|
|
|
|
|
|
|
|
<button v-for="(s, i) in (historySchemes[currentDetailHistoryGroup]?.schemes || [])" :key="'switch-h-' + i" :class="['switch-btn', { active: currentDetailIndex === i }]" @click="selectScheme(i, 'history', currentDetailHistoryGroup)">
|
|
|
|
|
|
|
|
{{ getSchemeLabel(i) }}
|
|
|
|
|
|
|
|
</button>
|
|
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<!-- Detail content -->
|
|
|
|
<div class="modal-scroll">
|
|
|
|
<div class="detail-content">
|
|
|
|
<!-- Highlights -->
|
|
|
|
<div class="detail-highlights">
|
|
|
|
<div class="modal-section">
|
|
|
|
<h3>亮点</h3>
|
|
|
|
<h3>✨ 亮点</h3>
|
|
|
|
<ul>
|
|
|
|
<ul>
|
|
|
|
<li v-for="(h, i) in currentDetailScheme.highlights" :key="i">{{ h }}</li>
|
|
|
|
<li v-for="(h, i) in (previewScheme.highlights || [])" :key="i">{{ h }}</li>
|
|
|
|
</ul>
|
|
|
|
</ul>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div v-if="currentDetailScheme.daysDetail" class="detail-days">
|
|
|
|
<!-- Daily schedule -->
|
|
|
|
<h3>每日行程</h3>
|
|
|
|
<div v-if="previewScheme.daysDetail && previewScheme.daysDetail.length > 0" class="modal-section">
|
|
|
|
<div v-for="(day, di) in currentDetailScheme.daysDetail" :key="di" class="day-card">
|
|
|
|
<h3>📅 每日行程</h3>
|
|
|
|
<div class="day-header">
|
|
|
|
<div v-for="(day, di) in previewScheme.daysDetail" :key="di" class="day-card">
|
|
|
|
<span class="day-badge">Day {{ di + 1 }}</span>
|
|
|
|
<div class="day-header">
|
|
|
|
<span class="day-title">{{ day.location }}</span>
|
|
|
|
<span class="day-badge">Day {{ di + 1 }}</span>
|
|
|
|
</div>
|
|
|
|
<span class="day-title">{{ day.location }}</span>
|
|
|
|
<div class="day-content">
|
|
|
|
<span class="day-km">{{ day.km }}km · 驾车{{ day.driveTime }}h</span>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
<p class="day-desc">{{ day.desc }}</p>
|
|
|
|
<p class="day-desc">{{ day.desc }}</p>
|
|
|
|
<div v-if="day.spots && day.spots.length > 0" class="day-spots">
|
|
|
|
|
|
|
|
<span v-for="(spot, si) in day.spots" :key="si" class="spot-tag">{{ spot }}</span>
|
|
|
|
<!-- Schedule -->
|
|
|
|
|
|
|
|
<div v-if="day.schedule && day.schedule.length > 0" class="day-schedule">
|
|
|
|
|
|
|
|
<div v-for="(item, si) in day.schedule" :key="si" class="schedule-item">
|
|
|
|
|
|
|
|
<span class="schedule-time">{{ item.time }}</span>
|
|
|
|
|
|
|
|
<div class="schedule-content">
|
|
|
|
|
|
|
|
<strong>{{ item.title }}</strong>
|
|
|
|
|
|
|
|
<p>{{ item.content }}</p>
|
|
|
|
|
|
|
|
<span class="schedule-desc">{{ item.desc }}</span>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<!-- Waypoints -->
|
|
|
|
|
|
|
|
<div v-if="day.waypoints && day.waypoints.length > 0" class="day-waypoints">
|
|
|
|
|
|
|
|
<span class="waypoint-label">途径推荐:</span>
|
|
|
|
|
|
|
|
<span v-for="(wp, wi) in day.waypoints" :key="wi" class="waypoint-tag">
|
|
|
|
|
|
|
|
{{ wp.icon }} {{ wp.name }}
|
|
|
|
|
|
|
|
</span>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<!-- Foods -->
|
|
|
|
|
|
|
|
<div v-if="day.foods && day.foods.length > 0" class="day-foods">
|
|
|
|
|
|
|
|
<span class="food-label">美食推荐:</span>
|
|
|
|
|
|
|
|
<span v-for="(food, fi) in day.foods" :key="fi" class="food-tag">
|
|
|
|
|
|
|
|
{{ food.icon || '🍜' }} {{ food.name }}
|
|
|
|
|
|
|
|
</span>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<!-- Tips -->
|
|
|
|
|
|
|
|
<div v-if="previewScheme.tips" class="modal-section">
|
|
|
|
|
|
|
|
<h3>💡 旅行贴士</h3>
|
|
|
|
|
|
|
|
<p>{{ previewScheme.tips }}</p>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div v-if="currentDetailScheme.tips" class="detail-tips">
|
|
|
|
<div class="modal-footer">
|
|
|
|
<h3>旅行贴士</h3>
|
|
|
|
<button class="btn-sm outline" @click="closePreview">关闭</button>
|
|
|
|
<p>{{ currentDetailScheme.tips }}</p>
|
|
|
|
<button class="btn-sm primary" @click="selectScheme(previewIndex); closePreview()">选择此方案</button>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</template>
|
|
|
|
</Teleport>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</template>
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
@ -155,7 +167,6 @@ import { quickPlan } from '../services/aiService'
|
|
|
|
|
|
|
|
|
|
|
|
const store = useItineraryStore()
|
|
|
|
const store = useItineraryStore()
|
|
|
|
|
|
|
|
|
|
|
|
const viewMode = ref('list')
|
|
|
|
|
|
|
|
const allSchemes = computed(() => store.quickSchemes)
|
|
|
|
const allSchemes = computed(() => store.quickSchemes)
|
|
|
|
const historySchemes = computed(() => store.historySchemeGroups)
|
|
|
|
const historySchemes = computed(() => store.historySchemeGroups)
|
|
|
|
const historyExpanded = ref(false)
|
|
|
|
const historyExpanded = ref(false)
|
|
|
|
@ -171,16 +182,11 @@ const isGenerating = ref(false)
|
|
|
|
const thinkingText = ref('')
|
|
|
|
const thinkingText = ref('')
|
|
|
|
const thinkingExpanded = ref(true)
|
|
|
|
const thinkingExpanded = ref(true)
|
|
|
|
|
|
|
|
|
|
|
|
const currentDetailIndex = ref(-1)
|
|
|
|
// Preview modal
|
|
|
|
const currentDetailSource = ref('current')
|
|
|
|
const previewIndex = ref(-1)
|
|
|
|
const currentDetailHistoryGroup = ref(-1)
|
|
|
|
const previewScheme = computed(() => {
|
|
|
|
|
|
|
|
if (previewIndex.value >= 0 && previewIndex.value < allSchemes.value.length) {
|
|
|
|
const currentDetailScheme = computed(() => {
|
|
|
|
return allSchemes.value[previewIndex.value] || {}
|
|
|
|
if (currentDetailSource.value === 'current' && currentDetailIndex.value >= 0) {
|
|
|
|
|
|
|
|
return allSchemes.value[currentDetailIndex.value] || {}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if (currentDetailSource.value === 'history' && currentDetailHistoryGroup.value >= 0) {
|
|
|
|
|
|
|
|
return historySchemes.value[currentDetailHistoryGroup.value]?.schemes?.[currentDetailIndex.value] || {}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return {}
|
|
|
|
return {}
|
|
|
|
})
|
|
|
|
})
|
|
|
|
@ -214,7 +220,19 @@ async function generatePlan() {
|
|
|
|
thinkingText.value = text
|
|
|
|
thinkingText.value = text
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
console.log('[generatePlan] AI 返回结果:', result)
|
|
|
|
|
|
|
|
console.log('[generatePlan] schemes 数量:', result.schemes?.length)
|
|
|
|
|
|
|
|
|
|
|
|
const newSchemes = result.schemes || []
|
|
|
|
const newSchemes = result.schemes || []
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 检查每个方案的 points
|
|
|
|
|
|
|
|
newSchemes.forEach((scheme, idx) => {
|
|
|
|
|
|
|
|
console.log(`[generatePlan] 方案 ${idx} - ${scheme.name}:`, {
|
|
|
|
|
|
|
|
pointsCount: scheme.points?.length,
|
|
|
|
|
|
|
|
points: scheme.points?.map(p => p.name)
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
const currentSchemes = [...allSchemes.value]
|
|
|
|
const currentSchemes = [...allSchemes.value]
|
|
|
|
const currentHistory = [...historySchemes.value]
|
|
|
|
const currentHistory = [...historySchemes.value]
|
|
|
|
|
|
|
|
|
|
|
|
@ -231,41 +249,56 @@ async function generatePlan() {
|
|
|
|
currentHistory.push({ schemes: excess, timestamp: Date.now() })
|
|
|
|
currentHistory.push({ schemes: excess, timestamp: Date.now() })
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
console.log('[generatePlan] 保存方案总数:', currentSchemes.length)
|
|
|
|
store.saveSchemesToStore(currentSchemes, currentHistory)
|
|
|
|
store.saveSchemesToStore(currentSchemes, currentHistory)
|
|
|
|
} catch (error) {
|
|
|
|
} catch (error) {
|
|
|
|
thinkingText.value = '生成失败:' + (error.message || '请重试')
|
|
|
|
thinkingText.value = '生成失败:' + (error.message || '请重试')
|
|
|
|
|
|
|
|
console.error('[generatePlan] 生成失败:', error)
|
|
|
|
} finally {
|
|
|
|
} finally {
|
|
|
|
isGenerating.value = false
|
|
|
|
isGenerating.value = false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function selectScheme(index, source, historyGroup) {
|
|
|
|
function regeneratePlan() {
|
|
|
|
currentDetailIndex.value = index
|
|
|
|
// Clear current schemes and regenerate with same conditions
|
|
|
|
currentDetailSource.value = source
|
|
|
|
store.saveSchemesToStore([], [...historySchemes.value])
|
|
|
|
currentDetailHistoryGroup.value = historyGroup ?? -1
|
|
|
|
generatePlan()
|
|
|
|
viewMode.value = 'detail'
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function useThisScheme() {
|
|
|
|
function selectScheme(index) {
|
|
|
|
const scheme = currentDetailScheme.value
|
|
|
|
const scheme = allSchemes.value[index]
|
|
|
|
if (!scheme) return
|
|
|
|
if (!scheme) {
|
|
|
|
|
|
|
|
console.error('[selectScheme] 方案不存在,index:', index)
|
|
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
console.log('[selectScheme] 选择方案:', scheme.name)
|
|
|
|
|
|
|
|
console.log('[selectScheme] 方案 points 数量:', scheme.points?.length)
|
|
|
|
|
|
|
|
console.log('[selectScheme] 方案 points 列表:', scheme.points?.map(p => p.name))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 先保存方案到 store(深拷贝)
|
|
|
|
store.saveSchemesToStore(allSchemes.value, historySchemes.value)
|
|
|
|
store.saveSchemesToStore(allSchemes.value, historySchemes.value)
|
|
|
|
|
|
|
|
store.setActiveSchemeIndex(index)
|
|
|
|
const idx = currentDetailSource.value === 'current' ? currentDetailIndex.value : -1
|
|
|
|
|
|
|
|
store.setActiveSchemeIndex(idx)
|
|
|
|
// 使用深拷贝后的数据加载
|
|
|
|
|
|
|
|
const savedScheme = store.quickSchemes[index]
|
|
|
|
store.loadFromAI(scheme)
|
|
|
|
console.log('[selectScheme] 深拷贝后的方案 points 数量:', savedScheme?.points?.length)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
store.loadFromAI(savedScheme)
|
|
|
|
store.setPhase('workbench')
|
|
|
|
store.setPhase('workbench')
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function goBackToList() {
|
|
|
|
function openPreview(index) {
|
|
|
|
viewMode.value = 'list'
|
|
|
|
previewIndex.value = index
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function closePreview() {
|
|
|
|
|
|
|
|
previewIndex.value = -1
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
onMounted(() => {
|
|
|
|
onMounted(() => {
|
|
|
|
if (store.quickSchemes.length > 0) {
|
|
|
|
if (store.quickSchemes.length > 0) {
|
|
|
|
viewMode.value = 'list'
|
|
|
|
// Stay in list view to show existing schemes
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
})
|
|
|
|
</script>
|
|
|
|
</script>
|
|
|
|
@ -456,12 +489,13 @@ onMounted(() => {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.btn-sm {
|
|
|
|
.btn-sm {
|
|
|
|
padding: 6px 16px;
|
|
|
|
padding: 8px 18px;
|
|
|
|
border-radius: 6px;
|
|
|
|
border-radius: 8px;
|
|
|
|
font-size: 12px;
|
|
|
|
font-size: 13px;
|
|
|
|
cursor: pointer;
|
|
|
|
cursor: pointer;
|
|
|
|
border: none;
|
|
|
|
border: none;
|
|
|
|
font-weight: 500;
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
|
|
transition: all 0.2s;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.btn-sm.outline {
|
|
|
|
.btn-sm.outline {
|
|
|
|
@ -475,6 +509,16 @@ onMounted(() => {
|
|
|
|
color: #6c5ce7;
|
|
|
|
color: #6c5ce7;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.btn-sm.primary {
|
|
|
|
|
|
|
|
background: linear-gradient(135deg, #6c5ce7, #a29bfe);
|
|
|
|
|
|
|
|
color: #fff;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.btn-sm.primary:hover {
|
|
|
|
|
|
|
|
transform: translateY(-1px);
|
|
|
|
|
|
|
|
box-shadow: 0 4px 12px rgba(108,92,231,0.3);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.plan-card {
|
|
|
|
.plan-card {
|
|
|
|
background: #fff;
|
|
|
|
background: #fff;
|
|
|
|
border-radius: 14px;
|
|
|
|
border-radius: 14px;
|
|
|
|
@ -483,7 +527,6 @@ onMounted(() => {
|
|
|
|
box-shadow: 0 2px 8px rgba(0,0,0,0.04);
|
|
|
|
box-shadow: 0 2px 8px rgba(0,0,0,0.04);
|
|
|
|
border: 2px solid #eee;
|
|
|
|
border: 2px solid #eee;
|
|
|
|
transition: all 0.2s;
|
|
|
|
transition: all 0.2s;
|
|
|
|
cursor: pointer;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.plan-card:hover {
|
|
|
|
.plan-card:hover {
|
|
|
|
@ -503,6 +546,7 @@ onMounted(() => {
|
|
|
|
padding: 3px 10px;
|
|
|
|
padding: 3px 10px;
|
|
|
|
border-radius: 6px;
|
|
|
|
border-radius: 6px;
|
|
|
|
font-weight: 700;
|
|
|
|
font-weight: 700;
|
|
|
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.pc-badge.a { background: #fff3e0; color: #e17055; }
|
|
|
|
.pc-badge.a { background: #fff3e0; color: #e17055; }
|
|
|
|
@ -548,248 +592,166 @@ onMounted(() => {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.pc-actions {
|
|
|
|
.pc-actions {
|
|
|
|
text-align: right;
|
|
|
|
display: flex;
|
|
|
|
|
|
|
|
justify-content: flex-end;
|
|
|
|
|
|
|
|
gap: 10px;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.pc-btn {
|
|
|
|
.pc-btn {
|
|
|
|
padding: 8px 22px;
|
|
|
|
padding: 8px 20px;
|
|
|
|
background: linear-gradient(135deg, #6c5ce7, #a29bfe);
|
|
|
|
|
|
|
|
color: #fff;
|
|
|
|
|
|
|
|
border: none;
|
|
|
|
|
|
|
|
border-radius: 8px;
|
|
|
|
border-radius: 8px;
|
|
|
|
font-size: 13px;
|
|
|
|
font-size: 13px;
|
|
|
|
font-weight: 600;
|
|
|
|
font-weight: 600;
|
|
|
|
cursor: pointer;
|
|
|
|
cursor: pointer;
|
|
|
|
}
|
|
|
|
border: none;
|
|
|
|
|
|
|
|
|
|
|
|
/* History */
|
|
|
|
|
|
|
|
.history-section {
|
|
|
|
|
|
|
|
margin-top: 24px;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.history-section h3 {
|
|
|
|
|
|
|
|
font-size: 16px;
|
|
|
|
|
|
|
|
color: #2d3436;
|
|
|
|
|
|
|
|
margin-bottom: 12px;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.toggle-icon {
|
|
|
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
|
|
|
margin-left: 8px;
|
|
|
|
|
|
|
|
color: #999;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.history-group {
|
|
|
|
|
|
|
|
background: #fff;
|
|
|
|
|
|
|
|
border-radius: 12px;
|
|
|
|
|
|
|
|
padding: 16px;
|
|
|
|
|
|
|
|
box-shadow: 0 2px 8px rgba(0,0,0,0.05);
|
|
|
|
|
|
|
|
margin-bottom: 12px;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.group-label {
|
|
|
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
|
|
color: #999;
|
|
|
|
|
|
|
|
margin-bottom: 10px;
|
|
|
|
|
|
|
|
padding-bottom: 6px;
|
|
|
|
|
|
|
|
border-bottom: 1px solid #f0f0f0;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.history-card {
|
|
|
|
|
|
|
|
background: #f8f9fa;
|
|
|
|
|
|
|
|
border: 1px solid #e0e0e0;
|
|
|
|
|
|
|
|
border-radius: 8px;
|
|
|
|
|
|
|
|
padding: 12px;
|
|
|
|
|
|
|
|
margin-bottom: 8px;
|
|
|
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
|
|
|
transition: all 0.2s;
|
|
|
|
transition: all 0.2s;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.history-card:last-child { margin-bottom: 0; }
|
|
|
|
.pc-btn.preview {
|
|
|
|
|
|
|
|
|
|
|
|
.history-card:hover {
|
|
|
|
|
|
|
|
border-color: #6c5ce7;
|
|
|
|
|
|
|
|
background: #fff;
|
|
|
|
background: #fff;
|
|
|
|
|
|
|
|
border: 1.5px solid #6c5ce7;
|
|
|
|
|
|
|
|
color: #6c5ce7;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.history-card h5 {
|
|
|
|
.pc-btn.preview:hover {
|
|
|
|
margin: 8px 0 6px;
|
|
|
|
background: #f0f0f5;
|
|
|
|
font-size: 14px;
|
|
|
|
|
|
|
|
color: #333;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.card-badge {
|
|
|
|
.pc-btn.select {
|
|
|
|
display: inline-block;
|
|
|
|
background: linear-gradient(135deg, #6c5ce7, #a29bfe);
|
|
|
|
background: #6c5ce7;
|
|
|
|
|
|
|
|
color: #fff;
|
|
|
|
color: #fff;
|
|
|
|
padding: 2px 8px;
|
|
|
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
|
|
|
font-size: 11px;
|
|
|
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.card-stats {
|
|
|
|
.pc-btn.select:hover {
|
|
|
|
display: flex;
|
|
|
|
transform: translateY(-1px);
|
|
|
|
gap: 12px;
|
|
|
|
box-shadow: 0 4px 12px rgba(108,92,231,0.3);
|
|
|
|
font-size: 12px;
|
|
|
|
|
|
|
|
color: #666;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.card-action {
|
|
|
|
/* Modal Overlay */
|
|
|
|
margin-top: 8px;
|
|
|
|
.modal-overlay {
|
|
|
|
font-size: 12px;
|
|
|
|
position: fixed;
|
|
|
|
color: #6c5ce7;
|
|
|
|
top: 0;
|
|
|
|
font-weight: 600;
|
|
|
|
left: 0;
|
|
|
|
|
|
|
|
right: 0;
|
|
|
|
|
|
|
|
bottom: 0;
|
|
|
|
|
|
|
|
background: rgba(0, 0, 0, 0.5);
|
|
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
|
|
z-index: 1000;
|
|
|
|
|
|
|
|
padding: 20px;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Detail View */
|
|
|
|
.modal-content {
|
|
|
|
.detail-view {
|
|
|
|
background: #fff;
|
|
|
|
|
|
|
|
border-radius: 20px;
|
|
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
|
|
max-width: 800px;
|
|
|
|
|
|
|
|
max-height: 85vh;
|
|
|
|
display: flex;
|
|
|
|
display: flex;
|
|
|
|
flex-direction: column;
|
|
|
|
flex-direction: column;
|
|
|
|
background: #f5f7fa;
|
|
|
|
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.2);
|
|
|
|
|
|
|
|
animation: modalIn 0.3s ease;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.detail-header {
|
|
|
|
@keyframes modalIn {
|
|
|
|
background: linear-gradient(135deg, #6c5ce7, #a29bfe);
|
|
|
|
from {
|
|
|
|
color: #fff;
|
|
|
|
opacity: 0;
|
|
|
|
padding: 20px 20px 24px;
|
|
|
|
transform: scale(0.95) translateY(-20px);
|
|
|
|
position: relative;
|
|
|
|
}
|
|
|
|
border-radius: 16px;
|
|
|
|
to {
|
|
|
|
margin-bottom: 16px;
|
|
|
|
opacity: 1;
|
|
|
|
}
|
|
|
|
transform: scale(1) translateY(0);
|
|
|
|
|
|
|
|
}
|
|
|
|
.detail-header h2 {
|
|
|
|
|
|
|
|
margin: 12px 0 8px;
|
|
|
|
|
|
|
|
font-size: 22px;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.detail-meta {
|
|
|
|
.modal-header {
|
|
|
|
display: flex;
|
|
|
|
display: flex;
|
|
|
|
gap: 16px;
|
|
|
|
justify-content: space-between;
|
|
|
|
font-size: 14px;
|
|
|
|
align-items: flex-start;
|
|
|
|
opacity: 0.9;
|
|
|
|
padding: 24px 28px 16px;
|
|
|
|
margin-bottom: 12px;
|
|
|
|
border-bottom: 1px solid #eee;
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.back-btn {
|
|
|
|
.modal-header h2 {
|
|
|
|
background: rgba(255,255,255,0.2);
|
|
|
|
font-size: 20px;
|
|
|
|
border: none;
|
|
|
|
font-weight: 700;
|
|
|
|
color: #fff;
|
|
|
|
color: #2d3436;
|
|
|
|
padding: 8px 14px;
|
|
|
|
margin: 8px 0 0;
|
|
|
|
border-radius: 8px;
|
|
|
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
|
|
|
transition: background 0.2s;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.back-btn:hover { background: rgba(255,255,255,0.3); }
|
|
|
|
.modal-close {
|
|
|
|
|
|
|
|
width: 32px;
|
|
|
|
.use-btn {
|
|
|
|
height: 32px;
|
|
|
|
padding: 12px 24px;
|
|
|
|
|
|
|
|
background: #fff;
|
|
|
|
|
|
|
|
color: #6c5ce7;
|
|
|
|
|
|
|
|
border: none;
|
|
|
|
border: none;
|
|
|
|
border-radius: 10px;
|
|
|
|
background: #f0f0f5;
|
|
|
|
font-size: 15px;
|
|
|
|
border-radius: 50%;
|
|
|
|
font-weight: 600;
|
|
|
|
font-size: 20px;
|
|
|
|
|
|
|
|
color: #636e72;
|
|
|
|
cursor: pointer;
|
|
|
|
cursor: pointer;
|
|
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
|
|
justify-content: center;
|
|
|
|
transition: all 0.2s;
|
|
|
|
transition: all 0.2s;
|
|
|
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.use-btn:hover {
|
|
|
|
.modal-close:hover {
|
|
|
|
transform: scale(1.02);
|
|
|
|
background: #6c5ce7;
|
|
|
|
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
|
|
|
|
color: #fff;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.scheme-switcher {
|
|
|
|
|
|
|
|
background: #fff;
|
|
|
|
|
|
|
|
padding: 12px 20px;
|
|
|
|
|
|
|
|
border-radius: 12px;
|
|
|
|
|
|
|
|
margin-bottom: 16px;
|
|
|
|
|
|
|
|
box-shadow: 0 2px 8px rgba(0,0,0,0.04);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.switcher-label {
|
|
|
|
|
|
|
|
font-size: 13px;
|
|
|
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
|
|
color: #666;
|
|
|
|
|
|
|
|
margin-bottom: 8px;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.switcher-btns {
|
|
|
|
.modal-meta {
|
|
|
|
display: flex;
|
|
|
|
display: flex;
|
|
|
|
gap: 8px;
|
|
|
|
gap: 16px;
|
|
|
|
flex-wrap: wrap;
|
|
|
|
padding: 12px 28px;
|
|
|
|
}
|
|
|
|
font-size: 14px;
|
|
|
|
|
|
|
|
|
|
|
|
.switch-btn {
|
|
|
|
|
|
|
|
padding: 6px 14px;
|
|
|
|
|
|
|
|
border: 1.5px solid #dfe6e9;
|
|
|
|
|
|
|
|
background: #fff;
|
|
|
|
|
|
|
|
border-radius: 6px;
|
|
|
|
|
|
|
|
font-size: 13px;
|
|
|
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
|
|
|
transition: all 0.2s;
|
|
|
|
|
|
|
|
color: #636e72;
|
|
|
|
color: #636e72;
|
|
|
|
|
|
|
|
background: #f8f9fa;
|
|
|
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.switch-btn:hover {
|
|
|
|
.modal-scroll {
|
|
|
|
border-color: #6c5ce7;
|
|
|
|
overflow-y: auto;
|
|
|
|
color: #6c5ce7;
|
|
|
|
padding: 20px 28px;
|
|
|
|
}
|
|
|
|
flex: 1;
|
|
|
|
|
|
|
|
|
|
|
|
.switch-btn.active {
|
|
|
|
|
|
|
|
background: #6c5ce7;
|
|
|
|
|
|
|
|
color: #fff;
|
|
|
|
|
|
|
|
border-color: #6c5ce7;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.detail-content {
|
|
|
|
|
|
|
|
max-width: 800px;
|
|
|
|
|
|
|
|
margin: 0 auto;
|
|
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.detail-highlights, .detail-days h3, .detail-tips h3 {
|
|
|
|
.modal-section {
|
|
|
|
background: #fff;
|
|
|
|
margin-bottom: 20px;
|
|
|
|
border-radius: 12px;
|
|
|
|
|
|
|
|
padding: 20px;
|
|
|
|
|
|
|
|
margin-bottom: 16px;
|
|
|
|
|
|
|
|
box-shadow: 0 2px 8px rgba(0,0,0,0.05);
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.detail-highlights h3, .detail-days h3, .detail-tips h3 {
|
|
|
|
.modal-section h3 {
|
|
|
|
margin: 0 0 12px;
|
|
|
|
|
|
|
|
font-size: 16px;
|
|
|
|
font-size: 16px;
|
|
|
|
|
|
|
|
font-weight: 700;
|
|
|
|
color: #2d3436;
|
|
|
|
color: #2d3436;
|
|
|
|
|
|
|
|
margin: 0 0 12px;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.detail-highlights ul {
|
|
|
|
.modal-section ul {
|
|
|
|
margin: 0;
|
|
|
|
margin: 0;
|
|
|
|
padding-left: 20px;
|
|
|
|
padding-left: 20px;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.detail-highlights li {
|
|
|
|
.modal-section li {
|
|
|
|
margin-bottom: 6px;
|
|
|
|
margin-bottom: 6px;
|
|
|
|
color: #555;
|
|
|
|
color: #555;
|
|
|
|
line-height: 1.6;
|
|
|
|
line-height: 1.6;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.day-card {
|
|
|
|
.day-card {
|
|
|
|
background: #fff;
|
|
|
|
background: #f8f9fa;
|
|
|
|
border-radius: 12px;
|
|
|
|
border-radius: 12px;
|
|
|
|
padding: 16px;
|
|
|
|
padding: 16px;
|
|
|
|
margin-bottom: 12px;
|
|
|
|
margin-bottom: 12px;
|
|
|
|
box-shadow: 0 2px 8px rgba(0,0,0,0.05);
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.day-header {
|
|
|
|
.day-header {
|
|
|
|
display: flex;
|
|
|
|
display: flex;
|
|
|
|
align-items: center;
|
|
|
|
align-items: center;
|
|
|
|
gap: 10px;
|
|
|
|
gap: 10px;
|
|
|
|
margin-bottom: 10px;
|
|
|
|
margin-bottom: 8px;
|
|
|
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.day-badge {
|
|
|
|
.day-badge {
|
|
|
|
@ -799,6 +761,7 @@ onMounted(() => {
|
|
|
|
border-radius: 4px;
|
|
|
|
border-radius: 4px;
|
|
|
|
font-size: 12px;
|
|
|
|
font-size: 12px;
|
|
|
|
font-weight: 600;
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.day-title {
|
|
|
|
.day-title {
|
|
|
|
@ -807,6 +770,12 @@ onMounted(() => {
|
|
|
|
color: #333;
|
|
|
|
color: #333;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.day-km {
|
|
|
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
|
|
|
color: #999;
|
|
|
|
|
|
|
|
margin-left: auto;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.day-desc {
|
|
|
|
.day-desc {
|
|
|
|
margin: 0 0 10px;
|
|
|
|
margin: 0 0 10px;
|
|
|
|
font-size: 14px;
|
|
|
|
font-size: 14px;
|
|
|
|
@ -814,28 +783,71 @@ onMounted(() => {
|
|
|
|
line-height: 1.6;
|
|
|
|
line-height: 1.6;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.day-spots {
|
|
|
|
.day-schedule {
|
|
|
|
|
|
|
|
margin-bottom: 10px;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.schedule-item {
|
|
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
|
|
gap: 10px;
|
|
|
|
|
|
|
|
margin-bottom: 8px;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.schedule-time {
|
|
|
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
|
|
color: #6c5ce7;
|
|
|
|
|
|
|
|
min-width: 50px;
|
|
|
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.schedule-content strong {
|
|
|
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
|
|
|
color: #2d3436;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.schedule-content p {
|
|
|
|
|
|
|
|
margin: 4px 0;
|
|
|
|
|
|
|
|
font-size: 13px;
|
|
|
|
|
|
|
|
color: #555;
|
|
|
|
|
|
|
|
line-height: 1.5;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.schedule-desc {
|
|
|
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
|
|
|
color: #999;
|
|
|
|
|
|
|
|
font-style: italic;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.day-waypoints, .day-foods {
|
|
|
|
display: flex;
|
|
|
|
display: flex;
|
|
|
|
flex-wrap: wrap;
|
|
|
|
flex-wrap: wrap;
|
|
|
|
gap: 6px;
|
|
|
|
gap: 6px;
|
|
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
|
|
margin-top: 8px;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.spot-tag {
|
|
|
|
.waypoint-label, .food-label {
|
|
|
|
background: #f0f0f5;
|
|
|
|
font-size: 12px;
|
|
|
|
|
|
|
|
font-weight: 600;
|
|
|
|
color: #636e72;
|
|
|
|
color: #636e72;
|
|
|
|
padding: 2px 10px;
|
|
|
|
}
|
|
|
|
border-radius: 4px;
|
|
|
|
|
|
|
|
|
|
|
|
.waypoint-tag, .food-tag {
|
|
|
|
|
|
|
|
background: #fff;
|
|
|
|
|
|
|
|
border: 1px solid #dfe6e9;
|
|
|
|
|
|
|
|
padding: 3px 8px;
|
|
|
|
|
|
|
|
border-radius: 6px;
|
|
|
|
font-size: 12px;
|
|
|
|
font-size: 12px;
|
|
|
|
|
|
|
|
color: #555;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.detail-tips {
|
|
|
|
.modal-footer {
|
|
|
|
background: #fff8e1;
|
|
|
|
display: flex;
|
|
|
|
border-left: 3px solid #f39c12;
|
|
|
|
justify-content: flex-end;
|
|
|
|
padding: 12px;
|
|
|
|
gap: 10px;
|
|
|
|
border-radius: 0 8px 8px 0;
|
|
|
|
padding: 16px 28px;
|
|
|
|
font-size: 13px;
|
|
|
|
border-top: 1px solid #eee;
|
|
|
|
color: #856404;
|
|
|
|
|
|
|
|
line-height: 1.5;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@media (max-width: 768px) {
|
|
|
|
@media (max-width: 768px) {
|
|
|
|
@ -846,5 +858,14 @@ onMounted(() => {
|
|
|
|
.form-row {
|
|
|
|
.form-row {
|
|
|
|
grid-template-columns: 1fr;
|
|
|
|
grid-template-columns: 1fr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.modal-content {
|
|
|
|
|
|
|
|
max-height: 95vh;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.modal-header, .modal-meta, .modal-scroll, .modal-footer {
|
|
|
|
|
|
|
|
padding-left: 16px;
|
|
|
|
|
|
|
|
padding-right: 16px;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
</style>
|
|
|
|
</style>
|
|
|
|
|