feat(mini-app, docs): 新增小程序详情页,更新项目文档与规范

新增全套关联组件:DetailHero、DetailOverviewCard、DetailInfoCard、DetailMediaGallery、DetailActionBar及ConciergeContactSheet。
新增详情页数据处理工具类detailPresentation.ts,处理商品展示数据的格式化与默认值兼容。
更新项目文档:补充三个领域API补充文档引用,优化AGENTS.md中的前端代码规范与docs文档列表。
This commit is contained in:
duanshuwen committed 2026-08-17 23:28:07 +08:00
1 parent 548f91c37f
commit ca6f9397e0
17 files changed
+1351 -7

No files matched your search

@@ -0,0 +1,93 @@
import { stripTitle } from "@/lib/data";
import type { Product } from "@/lib/types";
export type DetailPresentation = {
eyebrow: string;
duration: string;
title: string;
subtitle: string;
intro: string;
highlights: string[];
included: string[];
excluded: string[];
notes: string[];
gallery: string[];
};
const fallbackGallery = [
"https://www.zurnal24.si/media/img/5e/d5/9526a56dba168aa136f3.jpeg",
"https://genk.mediacdn.vn/139269124445442048/2024/4/27/10-23-sinkhole-1714189653945948438879.jpg",
"https://p2.cri.cn/M00/89/21/rBABC2aHnWeACxj5AAAAAAAAAAA159.1000x566.jpg",
"https://dimg04.c-ctrip.com/images/0EQ5712000ca7t504EC0E_W_640_10000.jpg?proc=autoorient",
];
function getDuration(title: string) {
return title.match(/\d+天\d+晚/)?.[0] ?? "5天4晚";
}
function getTextBlocks(product: Product) {
return (product.detailSections ?? [])
.flatMap((section) => section.blocks)
.filter(
(block): block is { type: "text"; text: string } => block.type === "text",
)
.map((block) => block.text.trim())
.filter(Boolean);
}
export function createDetailPresentation(product: Product): DetailPresentation {
const title = stripTitle(product.title);
const combinedText = `${product.title} ${product.summary ?? ""}`;
const isCaveRoute =
combinedText.includes("洞") || combinedText.includes("探险");
const detailTexts = getTextBlocks(product);
const apiImages = (product.images ?? [])
.slice()
.sort((left, right) => left.sortOrder - right.sortOrder)
.map((image) => image.url.trim())
.filter(Boolean);
return {
eyebrow: product.tags[0] || "玩法推荐",
duration: getDuration(product.title),
title: isCaveRoute ? "地心探险大环线" : title,
subtitle:
product.subtitle || `${product.destinationName || "贵州"}·小包团路线`,
intro:
detailTexts[0] ||
product.summary ||
"沿着贵州山地的自然纹理深入探索,把核心景观、在地体验和轻户外节奏安排在一条线路里。",
highlights: isCaveRoute
? [
"深入喀斯特洞穴与地下河,安排专业向导陪同",
"小团出行,按同行人的体力和兴趣灵活调整",
"山野咖啡与自然景观穿插,留出松弛的停留时间",
"行程前由服务管家确认天气、装备和接送细节",
]
: [
"核心景观串联,减少无效往返和重复换乘",
"小团出行,按同行人的节奏灵活调整",
"在地体验与舒适住宿合理衔接",
"行程前由服务管家确认天气、装备和接送细节",
],
included: [
"行程内用车与接送服务",
"列明景点门票和体验项目",
"服务管家行前确认与途中跟进",
"行程内住宿及方案中注明的服务",
],
excluded: [
"往返大交通及个人消费",
"未列明餐食和自选体验",
"因个人原因产生的额外费用",
],
notes: [
"贵州多山多雨,请准备防滑鞋、轻便雨具和薄外套。",
"溶洞、漂流、徒步等体验会根据天气和同行人体力适当调整。",
"页面价格为参考起价,最终方案以出行日期、人数和资源确认结果为准。",
],
gallery: Array.from(
new Set([product.image, ...apiImages, ...fallbackGallery]),
).slice(0, 6),
};
}