refactor: 清理废弃模块并重构管理端与全端代码
- 移除所有废弃的旧首页配置模块,包括destinationHero、demand相关、HomeExperience、vehicleService等表结构与代码,新增数据库迁移删除遗留表 - 重构管理端后台布局为标准RuoYi风格,新增Sidebar、Navbar、SettingsDrawer等组件,替换旧的过渡菜单实现 - 调整移动端响应断点为768px,更新布局相关测试用例,新增布局组件测试 - 删除OperationsTools发布/重置页面,移除/tools菜单入口,清理对应的API调用与测试代码 - 重构玩法模块编辑器:替换旧的图片手动输入框为图片上传组件,移除冗余的取消按钮 - 新增线索查询offset参数支持,完善查询参数处理逻辑 - 优化rbac菜单构建逻辑,新增可见性控制参数 - 修正管家编辑器提示文案,优化系统资源编辑器表单初始化逻辑 - 清理小程序端废弃的体验推荐组件与相关测试代码 - 更新文档与种子脚本,匹配新的API契约与使用说明
This commit is contained in:
@@ -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,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user