refactor(home-api): 统一首页API响应格式并迁移配套代码
完成首页数据模型的统一重构,具体变更如下: 1. 重构公共首页API接口,移除`playRecommendations`字段,将玩法推荐数据统一放入`experiences`字段 2. 更新前后端类型定义、序列化逻辑与前端页面组件,适配新的API响应结构 3. 为微信登录相关接口添加`trust_env=True`配置,支持企业代理环境并新增`socksio`依赖 4. 新增图片画廊上传组件,重构SingleImageUploader组件支持自定义宽高比 5. 重构Toast与Select组件的实现与样式,统一后台UI设计系统 6. 移除个人中心页面不必要的返回事件与顶部标题组件,优化详情卡片布局 7. 新增微信接口单元测试,更新官方文档与测试用例适配变更 8. 删除过期的文档图片资源
This commit is contained in:
1 parent
d3a246a873
commit
6296392e80
27 files changed
+1055
-218
No files matched your search
@@ -1,7 +1,6 @@
|
||||
import { reactive } from "vue";
|
||||
import { createPublicLead, fetchPublicContent, fetchPublicHome } from "./api";
|
||||
import { fallbackData, getSubmitErrorMessage, normalizeSiteConfig } from "./data";
|
||||
import { homeExperienceFallback, normalizeHomeExperiences } from "@/pages/home/components/homeExperienceData";
|
||||
import { homeTeamBuildingFallback, normalizeHomeTeamBuildings } from "@/pages/home/components/homeTeamBuildingData";
|
||||
import { homeWildArchiveFallback, normalizeHomeWildArchives } from "@/pages/home/components/homeWildArchivesData";
|
||||
import { normalizeHomeWanfaRecommendations } from "@/pages/home/components/homeWanfaRecommendationData";
|
||||
@@ -16,10 +15,9 @@ export const appState = reactive({
|
||||
});
|
||||
|
||||
export const homeContentState = reactive({
|
||||
experiences: homeExperienceFallback.map((item) => ({ ...item })),
|
||||
experiences: [] as HomeWanfaRecommendation[],
|
||||
teamBuildings: homeTeamBuildingFallback.map((item) => ({ ...item })),
|
||||
wildArchives: homeWildArchiveFallback.map((item) => ({ ...item })),
|
||||
playRecommendations: [] as HomeWanfaRecommendation[],
|
||||
loading: false,
|
||||
loaded: false,
|
||||
loadError: "",
|
||||
@@ -48,16 +46,14 @@ export async function loadHomeContent() {
|
||||
homeContentState.loadError = "";
|
||||
try {
|
||||
const home = await fetchPublicHome();
|
||||
homeContentState.experiences = normalizeHomeExperiences(home.experiences);
|
||||
homeContentState.experiences = normalizeHomeWanfaRecommendations(home.experiences);
|
||||
homeContentState.teamBuildings = normalizeHomeTeamBuildings(home.teamBuildings);
|
||||
homeContentState.wildArchives = normalizeHomeWildArchives(home.wildArchives);
|
||||
homeContentState.playRecommendations = normalizeHomeWanfaRecommendations(home.playRecommendations);
|
||||
homeContentState.loaded = true;
|
||||
} catch (error) {
|
||||
homeContentState.experiences = homeExperienceFallback.map((item) => ({ ...item }));
|
||||
homeContentState.experiences = [];
|
||||
homeContentState.teamBuildings = homeTeamBuildingFallback.map((item) => ({ ...item }));
|
||||
homeContentState.wildArchives = homeWildArchiveFallback.map((item) => ({ ...item }));
|
||||
homeContentState.playRecommendations = [];
|
||||
homeContentState.loadError = getSubmitErrorMessage(error);
|
||||
homeContentState.loaded = true;
|
||||
} finally {
|
||||
|
||||
@@ -95,10 +95,9 @@ export type PublicHomeWanfaRecommendation = {
|
||||
};
|
||||
|
||||
export type PublicHomeResponse = {
|
||||
experiences: PublicHomeExperience[];
|
||||
experiences: PublicHomeWanfaRecommendation[];
|
||||
teamBuildings: PublicHomeTeamBuilding[];
|
||||
wildArchives: PublicHomeWildArchive[];
|
||||
playRecommendations: PublicHomeWanfaRecommendation[];
|
||||
};
|
||||
|
||||
export type PublicHomeWildArchivesResponse = {
|
||||
|
||||
@@ -1,14 +1,6 @@
|
||||
<template>
|
||||
<view class="relative z-10 -mt-5 mx-3 rounded-[14px] bg-white px-4 py-5 shadow-[0_8px_24px_rgba(63,48,37,0.08)]">
|
||||
<view class="flex flex-wrap items-center gap-2">
|
||||
<text class="rounded-full bg-[#eaf6f1] px-2.5 py-1 text-[10px] font-semibold text-[#16806d]">
|
||||
{{ presentation.eyebrow }}
|
||||
</text>
|
||||
<text class="rounded-full bg-[#f3f0eb] px-2.5 py-1 text-[10px] font-medium text-[#746c64]">
|
||||
{{ presentation.duration }}
|
||||
</text>
|
||||
</view>
|
||||
<text class="mt-3 block text-[24px] font-bold leading-8 text-[#24211f]">{{ presentation.title }}</text>
|
||||
<text class="block text-[24px] font-bold leading-8 text-[#24211f]">{{ presentation.title }}</text>
|
||||
<text class="mt-2 block text-[13px] leading-6 text-[#756d65]">{{ presentation.subtitle }}</text>
|
||||
<view class="mt-4 grid grid-cols-2 gap-2 border-t border-[#f0ebe5] pt-3">
|
||||
<view class="rounded-[8px] bg-[#f8f6f2] px-3 py-2.5">
|
||||
|
||||
@@ -1,30 +1,27 @@
|
||||
<template>
|
||||
<view v-if="items.length" class="bg-[#fffdf9] px-[8.5%] pb-8 pt-7">
|
||||
<view class="mb-[14px] flex items-end justify-between gap-4">
|
||||
<view>
|
||||
<text class="block font-serif text-[21px] font-semibold leading-7 tracking-[0.04em] text-[#18212a]">玩法推荐</text>
|
||||
<text class="mt-1 block text-[12px] leading-[18px] text-[#7a7d78]">按主题挑选适合你的旅行方式</text>
|
||||
</view>
|
||||
<text class="shrink-0 font-sans text-[9px] leading-4 tracking-[0.18em] text-[#a3988e]">WAYS TO GO</text>
|
||||
</view>
|
||||
|
||||
<view class="grid grid-cols-2 gap-3">
|
||||
<view
|
||||
v-for="item in items"
|
||||
:key="item.id"
|
||||
class="min-w-0 overflow-hidden rounded-[12px] border border-[#e8e1d7] bg-white tap-feedback active:border-[#b97b5f] active:bg-[#fffaf4]"
|
||||
:aria-label="`${item.label},${item.routes.length} 条路线`"
|
||||
@click="$emit('select', item)"
|
||||
>
|
||||
<image class="block h-[112px] w-full bg-[#ebe8e2] min-[431px]:h-[126px]" :src="item.routes[0].image" mode="aspectFill" />
|
||||
<view class="p-[11px_10px_13px]">
|
||||
<view class="flex min-h-[22px] items-center justify-between gap-1.5">
|
||||
<text class="min-w-0 flex-1 font-serif text-[15px] font-semibold leading-[22px] text-[#1c252d]">{{ item.label }}</text>
|
||||
<uni-icons type="right" :size="16" color="#b5aaa0" />
|
||||
<view v-if="items.length" class="bg-[#fffdf9] px-[8.5%] pb-[26px]">
|
||||
<view
|
||||
v-for="item in items"
|
||||
:key="item.id"
|
||||
class="w-full bg-[#fffdf9] pt-[18px] tap-feedback min-[431px]:pt-[22px]"
|
||||
:aria-label="`${item.label},${item.routes.length} 条路线`"
|
||||
@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]">玩法推荐</text>
|
||||
<text class="min-w-0 truncate text-[13px] font-semibold leading-5 text-[#283b4d]">{{ item.label }} · {{ item.routes.length }} 条路线</text>
|
||||
</view>
|
||||
<text class="mt-1.5 block text-[11px] leading-[17px] text-[#7a7d78]">{{ item.routes.length }} 条路线可选</text>
|
||||
<text class="mt-[10px] block whitespace-pre-line font-serif text-[25px] font-medium leading-8 tracking-[0.04em] text-[#18212a]">{{ item.routes[0].title }}</text>
|
||||
<text class="mt-0.5 block font-sans text-[11px] font-medium leading-[18px] tracking-[0.08em] text-[#a7a0a0]">{{ item.routes[0].subtitle }}</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.routes[0].image" mode="aspectFill" />
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<view class="wq-phone">
|
||||
<scroll-view scroll-y class="h-screen no-scrollbar" :show-scrollbar="false">
|
||||
<HomeHeroCarousel :slides="appState.data.heroSlides" />
|
||||
<HomeExperienceList :items="homeContentState.experiences" @select="openExperience" />
|
||||
<HomeWanfaRecommendations :items="homeContentState.experiences" @select="openWanfaRecommendation" />
|
||||
<HomeVehicleGrid :vehicle-options="vehicleOptions" @select="openVehicle" />
|
||||
<HomeTeamBuilding :items="homeContentState.teamBuildings" @select="openTeamBuilding" />
|
||||
<HomeWildArchives :items="homeContentState.wildArchives" @select="openWildArchive" @more="openWildArchives" />
|
||||
@@ -16,13 +16,13 @@
|
||||
import { computed } from "vue";
|
||||
import { onShow } from "@dcloudio/uni-app";
|
||||
import HomeHeroCarousel from "./components/HomeHeroCarousel.vue";
|
||||
import HomeExperienceList from "./components/HomeExperienceList.vue";
|
||||
import HomeWanfaRecommendations from "./components/HomeWanfaRecommendations.vue";
|
||||
import HomeTeamBuilding from "./components/HomeTeamBuilding.vue";
|
||||
import HomeWildArchives from "./components/HomeWildArchives.vue";
|
||||
import HomeVehicleGrid from "./components/HomeVehicleGrid.vue";
|
||||
import type { HomeExperience } from "./components/homeExperienceData";
|
||||
import type { HomeTeamBuilding as HomeTeamBuildingItem } from "./components/homeTeamBuildingData";
|
||||
import type { HomeWildArchive } from "./components/homeWildArchivesData";
|
||||
import type { HomeWanfaRecommendation } from "./components/homeWanfaRecommendationData";
|
||||
import {
|
||||
goTeamBuildingDetail,
|
||||
goWanfaRouteDetail,
|
||||
@@ -38,8 +38,11 @@ onShow(() => {
|
||||
void loadHomeContent();
|
||||
});
|
||||
|
||||
function openExperience(item: HomeExperience) {
|
||||
goWanfaRouteDetail(item.id);
|
||||
function openWanfaRecommendation(item: HomeWanfaRecommendation) {
|
||||
const route = item.routes[0];
|
||||
if (route) {
|
||||
goWanfaRouteDetail(route.id);
|
||||
}
|
||||
}
|
||||
|
||||
function openVehicle(item: any) {
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
<template>
|
||||
<view class="min-h-screen bg-transparent">
|
||||
<TopHeader title="我的" subtitle="需求与服务管家" @back="$emit('backHome')" />
|
||||
<view class="px-4 pt-4">
|
||||
<view class="brand-panel rounded-[26px] p-5 text-white">
|
||||
<text class="block text-[12px] font-semibold uppercase tracking-[0.12em] text-[#f6dfb7]">万趣旅行</text>
|
||||
@@ -8,21 +7,9 @@
|
||||
<text class="mt-2 block text-[12px] leading-5 text-white/80">{{ customerLine }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="px-4 py-4">
|
||||
<view class="elevated-card rounded-[22px] p-5">
|
||||
<text class="block text-[17px] font-bold text-[#1f2c25]">已登录</text>
|
||||
<text class="mt-2 block text-[13px] leading-6 text-[#718073]">你的手机号已完成绑定,后续可通过服务管家沟通出行日期、人数与偏好。</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import TopHeader from "@/components/TopHeader.vue";
|
||||
|
||||
defineProps<{ customerLine: string }>();
|
||||
|
||||
defineEmits<{
|
||||
backHome: [];
|
||||
}>();
|
||||
</script>
|
||||
@@ -3,7 +3,7 @@
|
||||
<view class="wq-phone h-screen overflow-hidden">
|
||||
<MineGuest v-if="!session" @login="goLogin" />
|
||||
<scroll-view v-else scroll-y class="h-full no-scrollbar">
|
||||
<MineMember :customer-line="customerLine" @back-home="goRoot('/pages/home/index')" />
|
||||
<MineMember :customer-line="customerLine" />
|
||||
</scroll-view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -13,7 +13,6 @@
|
||||
import { ref } from "vue";
|
||||
import { onShow } from "@dcloudio/uni-app";
|
||||
import { getAuthSession, loginWithPhoneCode } from "@/lib/auth";
|
||||
import { goRoot } from "@/lib/navigation";
|
||||
import { loadAppData } from "@/lib/store";
|
||||
import type { AuthSession } from "@/lib/types";
|
||||
import MineGuest from "./components/MineGuest.vue";
|
||||
|
||||
Reference in new issue
Block a user