refactor: 清理废弃模块并重构管理端与全端代码
- 移除所有废弃的旧首页配置模块,包括destinationHero、demand相关、HomeExperience、vehicleService等表结构与代码,新增数据库迁移删除遗留表 - 重构管理端后台布局为标准RuoYi风格,新增Sidebar、Navbar、SettingsDrawer等组件,替换旧的过渡菜单实现 - 调整移动端响应断点为768px,更新布局相关测试用例,新增布局组件测试 - 删除OperationsTools发布/重置页面,移除/tools菜单入口,清理对应的API调用与测试代码 - 重构玩法模块编辑器:替换旧的图片手动输入框为图片上传组件,移除冗余的取消按钮 - 新增线索查询offset参数支持,完善查询参数处理逻辑 - 优化rbac菜单构建逻辑,新增可见性控制参数 - 修正管家编辑器提示文案,优化系统资源编辑器表单初始化逻辑 - 清理小程序端废弃的体验推荐组件与相关测试代码 - 更新文档与种子脚本,匹配新的API契约与使用说明
This commit is contained in:
1 parent
f6c5ad589f
commit
fbf2c772a8
63 files changed
+2272
-1991
No files matched your search
+17
-114
@@ -4,15 +4,9 @@ import {
|
||||
} from "@/content";
|
||||
import type {
|
||||
AppData,
|
||||
DemandFeatureCard,
|
||||
DemandFormConfig,
|
||||
DemandHero,
|
||||
DestinationHero,
|
||||
PublicHomeCard,
|
||||
PublicSiteConfig,
|
||||
PublicVehicleService,
|
||||
PublicVehicleServiceSection,
|
||||
PublicVehicleServiceStep,
|
||||
VehicleOption,
|
||||
} from "./types";
|
||||
|
||||
@@ -37,131 +31,40 @@ export function normalizeHomeCards<T extends { id?: string; title: string; image
|
||||
});
|
||||
}
|
||||
|
||||
function normalizeDestinationHero(items: PublicSiteConfig["destinationHero"], fallback: DestinationHero): DestinationHero {
|
||||
const item = normalizeActiveItems(items).find((entry) => entry.title?.trim());
|
||||
return item
|
||||
? { id: item.id, title: item.title.trim(), kicker: item.kicker?.trim() || fallback.kicker, image: item.image?.trim() || fallback.image }
|
||||
: fallback;
|
||||
}
|
||||
|
||||
function normalizeDemandHero(items: PublicSiteConfig["demandHero"], fallback: DemandHero): DemandHero {
|
||||
const item = normalizeActiveItems(items).find((entry) => entry.title?.trim());
|
||||
if (!item) return fallback;
|
||||
const steps = (item.steps ?? []).map((step) => step.trim()).filter(Boolean);
|
||||
return { id: item.id, kicker: item.kicker?.trim() || fallback.kicker, title: item.title.trim(), description: item.description?.trim() || fallback.description, steps: steps.length ? steps : fallback.steps };
|
||||
}
|
||||
|
||||
function normalizeDemandFeatureCards(items: PublicSiteConfig["demandFeatureCards"], fallback: DemandFeatureCard[]): DemandFeatureCard[] {
|
||||
const activeItems = normalizeActiveItems(items).filter((item) => item.title?.trim());
|
||||
return activeItems.length
|
||||
? activeItems.map((item, index) => ({ id: item.id, title: item.title.trim(), description: item.description?.trim() || fallback[index]?.description || "" }))
|
||||
: fallback;
|
||||
}
|
||||
|
||||
function normalizeDemandForm(items: PublicSiteConfig["demandForm"], fallback: DemandFormConfig): DemandFormConfig {
|
||||
const item = (items ?? []).find((entry) => entry.isActive !== false);
|
||||
if (!item) return fallback;
|
||||
const chips = (item.chips ?? []).map((chip) => chip.trim()).filter(Boolean);
|
||||
return {
|
||||
id: item.id,
|
||||
destinationLabel: item.destinationLabel?.trim() || fallback.destinationLabel,
|
||||
destinationPlaceholder: item.destinationPlaceholder?.trim() || fallback.destinationPlaceholder,
|
||||
phoneLabel: item.phoneLabel?.trim() || fallback.phoneLabel,
|
||||
phonePlaceholder: item.phonePlaceholder?.trim() || fallback.phonePlaceholder,
|
||||
noteLabel: item.noteLabel?.trim() || fallback.noteLabel,
|
||||
notePlaceholder: item.notePlaceholder?.trim() || fallback.notePlaceholder,
|
||||
submitLabel: item.submitLabel?.trim() || fallback.submitLabel,
|
||||
chips: chips.length ? chips : fallback.chips,
|
||||
};
|
||||
}
|
||||
|
||||
function normalizeVehicleServiceSection(item: unknown): PublicVehicleServiceSection | null {
|
||||
if (!item || typeof item !== "object") return null;
|
||||
const record = item as Record<string, unknown>;
|
||||
const title = typeof record.title === "string" ? record.title.trim() : "";
|
||||
const description = typeof record.description === "string" ? record.description.trim() : "";
|
||||
return title && description ? { title, description } : null;
|
||||
}
|
||||
|
||||
function normalizeVehicleServiceStep(item: unknown): PublicVehicleServiceStep | null {
|
||||
return normalizeVehicleServiceSection(item);
|
||||
}
|
||||
|
||||
function normalizeVehicleService(item: PublicSiteConfig["vehicleService"], fallback: PublicVehicleService): PublicVehicleService {
|
||||
if (!item || item.isActive === false) return fallback;
|
||||
const serviceSections = (item.serviceSections ?? []).map(normalizeVehicleServiceSection).filter(Boolean) as PublicVehicleServiceSection[];
|
||||
const processSteps = (item.processSteps ?? []).map(normalizeVehicleServiceStep).filter(Boolean) as PublicVehicleServiceStep[];
|
||||
const advantages = (item.advantages ?? []).filter((value): value is string => typeof value === "string").map((value) => value.trim()).filter(Boolean);
|
||||
return {
|
||||
id: item.id,
|
||||
introTitle: item.intro?.trim() && item.introTitle?.trim() ? item.introTitle.trim() : fallback.introTitle,
|
||||
intro: item.intro?.trim() || fallback.intro,
|
||||
serviceSections: serviceSections.length ? serviceSections : fallback.serviceSections,
|
||||
advantages: advantages.length ? advantages : fallback.advantages,
|
||||
processSteps: processSteps.length ? processSteps : fallback.processSteps,
|
||||
};
|
||||
}
|
||||
|
||||
export function normalizeSiteConfig(siteConfig: PublicSiteConfig, fallback: AppData): AppData {
|
||||
return {
|
||||
heroSlides: siteConfig.heroSlides?.length ? siteConfig.heroSlides.map((slide) => ({ id: slide.id, image: slide.image, kicker: slide.kicker, title: slide.title, targetType: slide.targetType, targetValue: slide.targetValue })) : fallback.heroSlides,
|
||||
destinationHero: normalizeDestinationHero(siteConfig.destinationHero, fallback.destinationHero),
|
||||
demandHero: normalizeDemandHero(siteConfig.demandHero, fallback.demandHero),
|
||||
demandFeatureCards: normalizeDemandFeatureCards(siteConfig.demandFeatureCards, fallback.demandFeatureCards),
|
||||
demandForm: normalizeDemandForm(siteConfig.demandForm, fallback.demandForm),
|
||||
heroSlides: siteConfig.heroSlides?.length
|
||||
? siteConfig.heroSlides.map((slide) => ({
|
||||
id: slide.id,
|
||||
image: slide.image,
|
||||
kicker: slide.kicker,
|
||||
title: slide.title,
|
||||
targetType: slide.targetType,
|
||||
targetValue: slide.targetValue,
|
||||
}))
|
||||
: fallback.heroSlides,
|
||||
vehicleOptions: normalizeHomeCards<VehicleOption>(siteConfig.vehicleOptions, fallback.vehicleOptions),
|
||||
vehicleService: normalizeVehicleService(siteConfig.vehicleService, fallback.vehicleService),
|
||||
vehicleService: fallback.vehicleService,
|
||||
};
|
||||
}
|
||||
|
||||
const fallbackDestinationHero: DestinationHero = {
|
||||
image: "/assets/guizhou/huangguoshu-waterfall.jpg",
|
||||
kicker: "贵州小包团目的地",
|
||||
title: "山水、苗寨、古城与野咖同程安排",
|
||||
};
|
||||
const fallbackDemandHero: DemandHero = { kicker: "3步定制", title: "告诉我们日期、人数和想法", description: "管家会按同行人、预算和体力强度,重新组合酒店、用车和景点节奏。", steps: ["提交需求", "管家沟通", "确认方案"] };
|
||||
const fallbackDemandFeatureCards: DemandFeatureCard[] = [
|
||||
{ title: "动线", description: "按天数顺路排" },
|
||||
{ title: "住宿", description: "城市与山野组合" },
|
||||
{ title: "用车", description: "人数行李匹配" },
|
||||
];
|
||||
const fallbackDemandForm: DemandFormConfig = {
|
||||
destinationLabel: "目的地/玩法",
|
||||
destinationPlaceholder: "例如:贵州、黄果树、西江苗寨",
|
||||
phoneLabel: "联系方式",
|
||||
phonePlaceholder: "手机号 / 微信号",
|
||||
noteLabel: "补充说明",
|
||||
notePlaceholder: "出行日期、人数、酒店偏好、预算范围",
|
||||
submitLabel: "提交出行需求",
|
||||
chips: ["贵州", "黄果树", "荔波小七孔", "西江苗寨", "梵净山", "万峰林"],
|
||||
};
|
||||
const fallbackVehicleService: PublicVehicleService = {
|
||||
introTitle: "旅游出行专属用车服务",
|
||||
intro: "覆盖景区接送、短途包车、旅途代步和多人出行场景。提交需求后由管家联系确认车型与服务安排。",
|
||||
intro: "提交用车需求后由管家联系确认车型与服务安排。",
|
||||
serviceSections: [
|
||||
{ title: "旅游专属包车", description: "适配家庭出游、朋友结伴和小团体旅行,支持半天或全天包车,行程可按需求沟通调整。" },
|
||||
{ title: "多车型灵活选择", description: "舒适轿车、SUV、商务车等车型按人数和行李情况匹配,让不同规模的旅行都能舒适出行。" },
|
||||
],
|
||||
advantages: [
|
||||
"专为旅游定制:贴合出游场景,行程灵活",
|
||||
"车型齐全:按人数和行李匹配出行方案",
|
||||
"省心便捷:线上提交需求,管家快速跟进",
|
||||
"出行安心:服务安排清晰,行程有人跟进",
|
||||
{ title: "旅游专属包车", description: "适配家庭出游、朋友结伴和小团体旅行,行程可按需求沟通调整。" },
|
||||
{ title: "多车型灵活选择", description: "按人数和行李情况匹配舒适轿车、SUV 或商务车。" },
|
||||
],
|
||||
advantages: ["专为旅游定制", "按人数和行李匹配", "管家快速跟进"],
|
||||
processSteps: [
|
||||
{ title: "提交需求", description: "填写日期、地点、人数和车型偏好。" },
|
||||
{ title: "管家联系", description: "管家根据需求沟通车辆和服务安排。" },
|
||||
{ title: "确认服务", description: "确认行程细节与报价后按约定出行。" },
|
||||
{ title: "安心出行", description: "按确认方案完成接送或包车服务。" },
|
||||
{ title: "管家联系", description: "沟通车辆和服务安排。" },
|
||||
{ title: "确认服务", description: "确认行程细节后按约定出行。" },
|
||||
],
|
||||
};
|
||||
|
||||
export const fallbackData: AppData = {
|
||||
heroSlides: fallbackHeroSlides,
|
||||
destinationHero: fallbackDestinationHero,
|
||||
demandHero: fallbackDemandHero,
|
||||
demandFeatureCards: fallbackDemandFeatureCards,
|
||||
demandForm: fallbackDemandForm,
|
||||
vehicleOptions: fallbackVehicleOptions.map((option) => ({ ...option })),
|
||||
vehicleService: fallbackVehicleService,
|
||||
};
|
||||
|
||||
@@ -32,23 +32,9 @@ export type PublicHeroSlide = {
|
||||
targetType?: string | null;
|
||||
targetValue?: string | null;
|
||||
isActive?: boolean;
|
||||
sortOrder?: number;
|
||||
};
|
||||
|
||||
export type PublicDestinationHero = { id: string; title: string; kicker?: string | null; image?: string | null; isActive?: boolean; sortOrder?: number };
|
||||
export type PublicDemandHero = { id: string; title: string; kicker?: string | null; description?: string | null; steps?: string[]; isActive?: boolean; sortOrder?: number };
|
||||
export type PublicDemandFeatureCard = { id: string; title: string; description?: string | null; isActive?: boolean; sortOrder?: number };
|
||||
export type PublicDemandForm = {
|
||||
id: string;
|
||||
destinationLabel?: string | null;
|
||||
destinationPlaceholder?: string | null;
|
||||
phoneLabel?: string | null;
|
||||
phonePlaceholder?: string | null;
|
||||
noteLabel?: string | null;
|
||||
notePlaceholder?: string | null;
|
||||
submitLabel?: string | null;
|
||||
chips?: string[];
|
||||
isActive?: boolean;
|
||||
};
|
||||
export type PublicHomeCard = { id: string; title: string; description?: string | null; image?: string | null; isActive?: boolean; sortOrder?: number };
|
||||
|
||||
export type PublicVehicleServiceSection = { title: string; description: string };
|
||||
@@ -63,16 +49,6 @@ export type PublicVehicleService = {
|
||||
isActive?: boolean;
|
||||
};
|
||||
|
||||
export type PublicHomeExperience = {
|
||||
id: string;
|
||||
badge: string;
|
||||
category: string;
|
||||
title: string;
|
||||
englishTitle: string;
|
||||
image: string;
|
||||
demandKeyword: string;
|
||||
};
|
||||
|
||||
export type PublicHomeTeamBuilding = {
|
||||
id: string;
|
||||
tag: string;
|
||||
@@ -169,12 +145,7 @@ export type PublicConciergeResponse = {
|
||||
|
||||
export type PublicSiteConfig = {
|
||||
heroSlides?: PublicHeroSlide[];
|
||||
destinationHero?: PublicDestinationHero[];
|
||||
demandHero?: PublicDemandHero[];
|
||||
demandFeatureCards?: PublicDemandFeatureCard[];
|
||||
demandForm?: PublicDemandForm[];
|
||||
vehicleOptions?: PublicHomeCard[];
|
||||
vehicleService?: PublicVehicleService | null;
|
||||
};
|
||||
|
||||
export type PublicLeadPayload = {
|
||||
@@ -211,18 +182,10 @@ export type VehicleLeadPayload = Omit<PublicLeadPayload, "leadType" | "vehicleDe
|
||||
};
|
||||
|
||||
export type HeroSlide = { id?: string; image: string; kicker?: string | null; title: string; action?: string | null; targetType?: string | null; targetValue?: string | null };
|
||||
export type DestinationHero = { id?: string; image: string; kicker?: string | null; title: string };
|
||||
export type DemandHero = { id?: string; kicker: string; title: string; description: string; steps: string[] };
|
||||
export type DemandFeatureCard = { id?: string; title: string; description: string };
|
||||
export type DemandFormConfig = { id?: string; destinationLabel: string; destinationPlaceholder: string; phoneLabel: string; phonePlaceholder: string; noteLabel: string; notePlaceholder: string; submitLabel: string; chips: string[] };
|
||||
export type VehicleOption = { id?: string; title: string; image: string; description: string };
|
||||
|
||||
export type AppData = {
|
||||
heroSlides: HeroSlide[];
|
||||
destinationHero: DestinationHero;
|
||||
demandHero: DemandHero;
|
||||
demandFeatureCards: DemandFeatureCard[];
|
||||
demandForm: DemandFormConfig;
|
||||
vehicleOptions: VehicleOption[];
|
||||
vehicleService: PublicVehicleService;
|
||||
};
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
<template>
|
||||
<view
|
||||
class="w-full bg-[#fffdf9] pt-[18px] tap-feedback min-[431px]:pt-[22px]"
|
||||
:aria-label="`${item.title},${item.category}`"
|
||||
@click="$emit('select', item)"
|
||||
>
|
||||
<view class="flex items-start justify-between gap-4 pr-[18px] min-[431px]:pr-[22px]">
|
||||
<view class="min-w-0 flex-1">
|
||||
<view class="flex min-h-5 items-center gap-[10px]">
|
||||
<text class="inline-flex min-h-5 items-center rounded-[12px] border border-[#eadfce] bg-[#f8f1e8] px-[6px] text-[10px] leading-[18px] tracking-[0.06em] text-[#98744b]">{{ item.badge }}</text>
|
||||
<text class="text-[13px] font-semibold leading-5 text-[#283b4d]">{{ item.category }}</text>
|
||||
</view>
|
||||
<text class="mt-[10px] block whitespace-pre-line font-serif text-[25px] font-medium leading-8 tracking-[0.04em] text-[#18212a]">{{ item.title }}</text>
|
||||
<text class="mt-0.5 block font-sans text-[11px] font-medium leading-[18px] tracking-[0.22em] text-[#a7a0a0]">{{ item.englishTitle }}</text>
|
||||
</view>
|
||||
<view class="mt-8 flex h-10 w-6 shrink-0 items-center justify-center" aria-hidden="true">
|
||||
<uni-icons type="right" :size="24" color="#c8c8c8" />
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<image class="mt-[17px] block h-[174px] w-full rounded-[12px] bg-[#eae7e1] min-[431px]:h-[190px]" :src="item.image" mode="aspectFill" />
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { HomeExperience } from "./homeExperienceData";
|
||||
|
||||
defineProps<{
|
||||
item: HomeExperience;
|
||||
}>();
|
||||
|
||||
defineEmits<{
|
||||
select: [item: HomeExperience];
|
||||
}>();
|
||||
</script>
|
||||
@@ -1,23 +0,0 @@
|
||||
<template>
|
||||
<view class="bg-[#fffdf9] px-[8.5%] pb-[26px]">
|
||||
<HomeExperienceCard
|
||||
v-for="item in items"
|
||||
:key="item.id"
|
||||
:item="item"
|
||||
@select="$emit('select', $event)"
|
||||
/>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import HomeExperienceCard from "./HomeExperienceCard.vue";
|
||||
import type { HomeExperience } from "./homeExperienceData";
|
||||
|
||||
defineProps<{
|
||||
items: HomeExperience[];
|
||||
}>();
|
||||
|
||||
defineEmits<{
|
||||
select: [item: HomeExperience];
|
||||
}>();
|
||||
</script>
|
||||
@@ -1,78 +0,0 @@
|
||||
export type HomeExperience = {
|
||||
id: string;
|
||||
badge: string;
|
||||
category: string;
|
||||
title: string;
|
||||
englishTitle: string;
|
||||
image: string;
|
||||
demandKeyword: string;
|
||||
};
|
||||
|
||||
export type HomeExperienceApiItem = {
|
||||
id?: string | null;
|
||||
badge?: string | null;
|
||||
category?: string | null;
|
||||
title?: string | null;
|
||||
englishTitle?: string | null;
|
||||
image?: string | null;
|
||||
demandKeyword?: string | null;
|
||||
isActive?: boolean | null;
|
||||
sortOrder?: number | null;
|
||||
};
|
||||
|
||||
export type HomeExperiences = HomeExperienceApiItem[];
|
||||
|
||||
export const homeExperienceMocks: HomeExperience[] = [
|
||||
{
|
||||
id: "waterfall-descent",
|
||||
badge: "玩过推荐",
|
||||
category: "瀑降体验",
|
||||
title: "悬崖瀑降",
|
||||
englishTitle: "WATERFALL DESCENT",
|
||||
image: "https://genk.mediacdn.vn/139269124445442048/2024/4/27/10-23-sinkhole-1714189653945948438879.jpg",
|
||||
demandKeyword: "悬崖瀑降",
|
||||
},
|
||||
{
|
||||
id: "cave-exploration",
|
||||
badge: "玩过推荐",
|
||||
category: "地心探险",
|
||||
title: "森林&探洞",
|
||||
englishTitle: "CAVE EXPLORATION",
|
||||
image: "https://www.zurnal24.si/media/img/5e/d5/9526a56dba168aa136f3.jpeg",
|
||||
demandKeyword: "地心探险",
|
||||
},
|
||||
];
|
||||
|
||||
export const homeExperienceFallback = homeExperienceMocks;
|
||||
|
||||
export function normalizeHomeExperiences(
|
||||
items: HomeExperiences | null | undefined,
|
||||
): HomeExperience[] {
|
||||
const fallback = homeExperienceFallback.map((item) => ({ ...item }));
|
||||
const activeItems = (items ?? [])
|
||||
.map((item, index) => ({ item, index }))
|
||||
.filter(({ item }) => item.isActive !== false && Boolean(item.title?.trim()))
|
||||
.sort(
|
||||
(left, right) =>
|
||||
(left.item.sortOrder ?? left.index) - (right.item.sortOrder ?? right.index),
|
||||
);
|
||||
|
||||
if (activeItems.length === 0) {
|
||||
return fallback;
|
||||
}
|
||||
|
||||
return activeItems.map(({ item }, index) => {
|
||||
const fallbackItem = homeExperienceFallback[index] ?? homeExperienceFallback[0];
|
||||
const title = item.title?.trim() || fallbackItem.title;
|
||||
|
||||
return {
|
||||
id: item.id?.trim() || fallbackItem.id,
|
||||
badge: item.badge?.trim() || fallbackItem.badge,
|
||||
category: item.category?.trim() || fallbackItem.category,
|
||||
title,
|
||||
englishTitle: item.englishTitle?.trim() || fallbackItem.englishTitle,
|
||||
image: item.image?.trim() || fallbackItem.image,
|
||||
demandKeyword: item.demandKeyword?.trim() || title,
|
||||
};
|
||||
});
|
||||
}
|
||||
@@ -7,42 +7,47 @@ describe("WonderQ shared data helpers", () => {
|
||||
expect(normalizeSiteConfig({}, fallbackData)).toEqual(fallbackData);
|
||||
});
|
||||
|
||||
it("normalizes active site modules and ignores incomplete records", () => {
|
||||
it("normalizes the supported home modules and keeps the local vehicle fallback", () => {
|
||||
const config: PublicSiteConfig = {
|
||||
destinationHero: [
|
||||
heroSlides: [
|
||||
{
|
||||
id: "hero-1",
|
||||
title: "后台主视觉标题",
|
||||
title: "后台首页主视觉",
|
||||
kicker: "后台小标题",
|
||||
image: "https://cdn.example.test/hero.jpg",
|
||||
isActive: true,
|
||||
sortOrder: 0,
|
||||
},
|
||||
],
|
||||
demandForm: [
|
||||
vehicleOptions: [
|
||||
{
|
||||
id: "form-1",
|
||||
destinationLabel: "想去哪里",
|
||||
submitLabel: "提交给管家",
|
||||
chips: [" 贵州 ", "", "黄果树"],
|
||||
id: "vehicle-1",
|
||||
title: "舒适型用车",
|
||||
description: "适合家庭出行",
|
||||
image: "https://cdn.example.test/vehicle.jpg",
|
||||
isActive: true,
|
||||
sortOrder: 0,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const data = normalizeSiteConfig(config, fallbackData);
|
||||
|
||||
expect(data.destinationHero).toMatchObject({
|
||||
id: "hero-1",
|
||||
title: "后台主视觉标题",
|
||||
image: "https://cdn.example.test/hero.jpg",
|
||||
});
|
||||
expect(data.demandForm).toMatchObject({
|
||||
id: "form-1",
|
||||
destinationLabel: "想去哪里",
|
||||
submitLabel: "提交给管家",
|
||||
chips: ["贵州", "黄果树"],
|
||||
});
|
||||
expect(data.heroSlides).toMatchObject([
|
||||
{
|
||||
id: "hero-1",
|
||||
title: "后台首页主视觉",
|
||||
image: "https://cdn.example.test/hero.jpg",
|
||||
},
|
||||
]);
|
||||
expect(data.vehicleOptions).toMatchObject([
|
||||
{
|
||||
id: "vehicle-1",
|
||||
title: "舒适型用车",
|
||||
description: "适合家庭出行",
|
||||
},
|
||||
]);
|
||||
expect(data.vehicleService.introTitle).toBe("旅游出行专属用车服务");
|
||||
});
|
||||
|
||||
it("returns a stable submit error message", () => {
|
||||
@@ -50,32 +55,4 @@ describe("WonderQ shared data helpers", () => {
|
||||
expect(getSubmitErrorMessage("unknown")).toBe("提交失败,请稍后再试。");
|
||||
});
|
||||
|
||||
it("normalizes the vehicle service module and falls back incomplete sections", () => {
|
||||
const config: PublicSiteConfig = {
|
||||
vehicleService: {
|
||||
id: "vehicle-service-1",
|
||||
introTitle: "旅游专属用车",
|
||||
intro: " 提交需求后由管家跟进。 ",
|
||||
serviceSections: [{ title: " 包车服务 ", description: " 灵活安排 " }],
|
||||
advantages: [" 车型齐全 ", ""],
|
||||
processSteps: [{ title: "提交需求", description: " 填写出行信息 " }],
|
||||
isActive: true,
|
||||
},
|
||||
};
|
||||
|
||||
const data = normalizeSiteConfig(config, fallbackData);
|
||||
|
||||
expect(data.vehicleService).toMatchObject({
|
||||
id: "vehicle-service-1",
|
||||
introTitle: "旅游专属用车",
|
||||
intro: "提交需求后由管家跟进。",
|
||||
advantages: ["车型齐全"],
|
||||
});
|
||||
expect(data.vehicleService.serviceSections).toEqual([
|
||||
{ title: "包车服务", description: "灵活安排" },
|
||||
]);
|
||||
expect(data.vehicleService.processSteps).toEqual([
|
||||
{ title: "提交需求", description: "填写出行信息" },
|
||||
]);
|
||||
});
|
||||
});
|
||||
@@ -1,6 +1,5 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import { homeExperienceMocks, normalizeHomeExperiences } from "@/pages/home/components/homeExperienceData";
|
||||
import {
|
||||
homeTeamBuildingMocks,
|
||||
normalizeHomeTeamBuildingDetail,
|
||||
@@ -14,27 +13,6 @@ import {
|
||||
import { normalizeHomeWanfaRecommendations } from "@/pages/home/components/homeWanfaRecommendationData";
|
||||
|
||||
describe("home content normalizers", () => {
|
||||
it("normalizes experiences and keeps the existing fallback for missing fields", () => {
|
||||
expect(
|
||||
normalizeHomeExperiences([
|
||||
{
|
||||
id: "api-experience",
|
||||
title: " API 瀑降 ",
|
||||
sortOrder: 0,
|
||||
isActive: true,
|
||||
},
|
||||
{ id: "disabled", title: "不展示", isActive: false },
|
||||
]),
|
||||
).toEqual([
|
||||
{
|
||||
...homeExperienceMocks[0],
|
||||
id: "api-experience",
|
||||
title: "API 瀑降",
|
||||
demandKeyword: "API 瀑降",
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it("normalizes team building and wild archive lists", () => {
|
||||
expect(normalizeHomeTeamBuildings(null)).toEqual(homeTeamBuildingMocks);
|
||||
expect(normalizeHomeWildArchives(undefined)).toEqual(homeWildArchiveMocks);
|
||||
|
||||
Reference in new issue
Block a user