feat: 交通信息与 AI 解耦,完善出发/回程路线显示

- 从 finalize_plan schema 和 agent 提示词中移除 transit 字段,AI 只负责活动/POI/坐标
- 新增 enrichTransitInfo:计划生成后查询高德 V3 公交 API,计算出发地→首活动、活动间、末活动→出发地三段交通
- parseTransitSegments 增加起终点站显示(去除线路名中的全程终点括号)
- WeekendPlanData 新增 transitFromStart/transitToEnd 字段
- BlindboxPlan 新增出发地和返回出发地交通连接器,传入 startLocationLabel 显示具体地址
- BlindBoxRoom schema 新增 address 字段存完整逆地理地址,city 保留供 API 使用
- 新增 /api/debug/transit 调试端点(仅开发环境)
- agent userPrompt 要求将出发/回程时间计入全天时间预算
This commit is contained in:
2026-03-02 16:35:38 +08:00
parent e5a255a49e
commit 99120a7042
8 changed files with 262 additions and 30 deletions
+33
View File
@@ -58,6 +58,8 @@ interface BlindboxPlanProps {
onDaysChange?: (newDays: WeekendPlanData[]) => void;
/** "lng,lat" 格式,用于 POI 搜索附近优先 */
location?: string;
/** 出发地名称,用于显示在出发/返回连接器上 */
startLocationLabel?: string;
onRefine?: (instruction: string) => Promise<void>;
}
@@ -275,6 +277,7 @@ export default function BlindboxPlan({
regenerating,
onDaysChange,
location,
startLocationLabel,
onRefine,
}: BlindboxPlanProps) {
const [dayIndex, setDayIndex] = useState(0);
@@ -452,6 +455,21 @@ export default function BlindboxPlan({
items={currentDay.items.map((_, i) => `${dayIndex}-${i}`)}
strategy={verticalListSortingStrategy}
>
{/* Transit from home to first activity */}
{currentDay.transitFromStart != null && (
<div className="flex items-start gap-1.5 py-2 pl-1">
<Navigation size={9} className="mt-0.5 shrink-0 text-purple-400/40" />
<div className="flex flex-col gap-0.5">
<span className="text-[10px] font-medium text-dim">{startLocationLabel ?? "出发地"}</span>
{currentDay.transitFromStartDescription && (
<span className="text-[10px] leading-snug text-dim">
{currentDay.transitFromStartDescription}
</span>
)}
<span className="text-[10px] text-dim/70"> {currentDay.transitFromStart} </span>
</div>
</div>
)}
{currentDay.items.map((item, i) => (
<motion.div
key={`${dayIndex}-${i}`}
@@ -483,6 +501,21 @@ export default function BlindboxPlan({
</div>
</div>
)}
{/* Transit back home after last activity */}
{i === currentDay.items.length - 1 && currentDay.transitToEnd != null && (
<div className="flex items-start gap-1.5 py-2 pl-1">
<Navigation size={9} className="mt-0.5 shrink-0 text-purple-400/40" />
<div className="flex flex-col gap-0.5">
<span className="text-[10px] font-medium text-dim">{startLocationLabel ?? "出发地"}</span>
{currentDay.transitToEndDescription && (
<span className="text-[10px] leading-snug text-dim">
{currentDay.transitToEndDescription}
</span>
)}
<span className="text-[10px] text-dim/70"> {currentDay.transitToEnd} </span>
</div>
</div>
)}
</motion.div>
))}
</SortableContext>