feat: 首页的搭建
This commit is contained in:
@@ -1,5 +1,187 @@
|
||||
<template>
|
||||
<view>
|
||||
|
||||
<view class="box-border w-full px-[12px] pb-[20px] pt-[12px]">
|
||||
<!-- 主景区 -->
|
||||
<view
|
||||
v-if="panoramaMainCards.length > 0"
|
||||
class="relative h-[158px] w-full overflow-hidden rounded-[18px] bg-[#17463d]"
|
||||
hover-class="opacity-90"
|
||||
hover-stay-time="80"
|
||||
@tap="handleCardTap(panoramaMainCards[0])"
|
||||
>
|
||||
<image
|
||||
class="absolute inset-0 h-full w-full"
|
||||
:src="panoramaMainCards[0].coverImageUrl"
|
||||
mode="aspectFill"
|
||||
/>
|
||||
<view class="absolute inset-0 bg-[linear-gradient(90deg,rgba(3,39,33,0.78)_0%,rgba(3,39,33,0.34)_55%,rgba(3,39,33,0.08)_100%)]" />
|
||||
|
||||
<view class="absolute inset-0 box-border flex flex-col items-start justify-center px-[20px] py-[16px]">
|
||||
<text class="block text-[27px] font-bold leading-[1.15] tracking-[1px] text-white">
|
||||
{{ panoramaMainCards[0].title }}
|
||||
</text>
|
||||
<text class="mt-[7px] block text-[12px] leading-[1.4] tracking-[0.5px] text-white/90">
|
||||
{{ panoramaMainCards[0].subtitle }}
|
||||
</text>
|
||||
|
||||
<view class="mt-[15px] flex h-[36px] items-center rounded-full bg-white/95 px-[16px]">
|
||||
<text class="text-[14px] font-semibold text-[#149474]">开始探索</text>
|
||||
<text class="ml-[9px] text-[22px] leading-none text-[#149474]">→</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view
|
||||
v-if="panoramaMainCards[0].locationName"
|
||||
class="absolute bottom-[14px] right-[14px] flex h-[28px] items-center rounded-full border border-white/35 bg-black/20 px-[10px]"
|
||||
>
|
||||
<text class="mr-[5px] text-[13px] leading-none text-white">⌖</text>
|
||||
<text class="text-[11px] text-white/90">{{ panoramaMainCards[0].locationName }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 其他景区 -->
|
||||
<view
|
||||
v-if="panoramaOtherCards.length > 0 || fineFoodCards.length > 0"
|
||||
class="mt-[12px] grid grid-cols-12 gap-[10px]"
|
||||
>
|
||||
<view
|
||||
v-if="panoramaOtherCards.length > 0"
|
||||
class="relative col-span-6 row-span-2 min-h-0 overflow-hidden rounded-[18px] bg-[#173a34]"
|
||||
hover-class="opacity-90"
|
||||
hover-stay-time="80"
|
||||
@tap="handleCardTap(activeOtherCard)"
|
||||
>
|
||||
<swiper
|
||||
class="h-full w-full"
|
||||
circular
|
||||
autoplay
|
||||
:interval="3500"
|
||||
:duration="500"
|
||||
@change="handleSwiperChange"
|
||||
>
|
||||
<swiper-item
|
||||
v-for="(card, cardIndex) in panoramaOtherCards"
|
||||
:key="card.homePageId || cardIndex"
|
||||
>
|
||||
<image class="h-full w-full" :src="card.coverImageUrl" mode="aspectFill" />
|
||||
</swiper-item>
|
||||
</swiper>
|
||||
|
||||
<view class="pointer-events-none absolute inset-0 bg-[linear-gradient(180deg,rgba(5,24,24,0.72)_0%,rgba(5,24,24,0.18)_46%,rgba(5,24,24,0.34)_100%)]" />
|
||||
|
||||
<view
|
||||
v-if="activeOtherCard"
|
||||
class="pointer-events-none absolute left-[16px] right-[14px] top-[17px]"
|
||||
>
|
||||
<text class="block text-[21px] font-bold leading-[1.2] tracking-[0.5px] text-white">
|
||||
{{ activeOtherCard.title }}
|
||||
</text>
|
||||
<text class="mt-[7px] block text-[11px] leading-[1.45] tracking-[0.2px] text-white/85">
|
||||
{{ activeOtherCard.subtitle }}
|
||||
</text>
|
||||
</view>
|
||||
|
||||
<view class="pointer-events-none absolute bottom-[16px] left-[16px] flex h-[46px] w-[46px] items-center justify-center rounded-full border border-white/20 bg-[#25a98f]/85">
|
||||
<text class="text-[27px] font-light leading-none text-white">→</text>
|
||||
</view>
|
||||
|
||||
<view
|
||||
v-if="panoramaOtherCards.length > 1"
|
||||
class="pointer-events-none absolute bottom-[20px] right-[14px] flex items-center gap-[5px]"
|
||||
>
|
||||
<view
|
||||
v-for="(card, dotIndex) in panoramaOtherCards"
|
||||
:key="`${card.homePageId || dotIndex}-dot`"
|
||||
class="h-[5px] rounded-full transition-all duration-300"
|
||||
:class="dotIndex === activeSwiperIndex ? 'w-[14px] bg-white' : 'w-[5px] bg-white/45'"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 美食 -->
|
||||
<view
|
||||
v-for="(card, cardIndex) in fineFoodCards"
|
||||
:key="card.homePageId || cardIndex"
|
||||
class="relative col-span-6 aspect-video min-h-0 overflow-hidden rounded-[18px] bg-[#173a34]"
|
||||
hover-class="opacity-90"
|
||||
hover-stay-time="80"
|
||||
@tap="handleCardTap(card)"
|
||||
>
|
||||
<image class="h-full w-full" :src="card.coverImageUrl" mode="aspectFill" />
|
||||
|
||||
<view class="pointer-events-none absolute inset-0 bg-[linear-gradient(180deg,rgba(5,24,24,0.72)_0%,rgba(5,24,24,0.18)_46%,rgba(5,24,24,0.34)_100%)]" />
|
||||
|
||||
<view class="pointer-events-none absolute left-[12px] right-[10px] top-[11px]">
|
||||
<text class="block text-[17px] font-bold leading-[1.2] tracking-[0.5px] text-white">
|
||||
{{ card.title }}
|
||||
</text>
|
||||
<text class="mt-[3px] block text-[9px] leading-[1.45] tracking-[0.2px] text-white/85">
|
||||
{{ card.subtitle }}
|
||||
</text>
|
||||
</view>
|
||||
|
||||
<view class="pointer-events-none absolute bottom-[10px] left-[12px] flex h-[30px] w-[30px] items-center justify-center rounded-full border border-white/20 bg-[#9b7545]/80">
|
||||
<text class="text-[20px] font-light leading-none text-white">→</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed, onMounted, ref } from "vue";
|
||||
import { homeAllScenicData } from "@/request/api/MainPageDataApi";
|
||||
|
||||
const emit = defineEmits(["select"]);
|
||||
const panoramaMainCards = ref([]);
|
||||
const panoramaOtherCards = ref([]);
|
||||
const fineFoodCards = ref([]);
|
||||
const activeSwiperIndex = ref(0);
|
||||
|
||||
const activeOtherCard = computed(() => {
|
||||
if (panoramaOtherCards.value.length === 0) return null;
|
||||
return panoramaOtherCards.value[activeSwiperIndex.value] || panoramaOtherCards.value[0];
|
||||
});
|
||||
|
||||
const handleSwiperChange = (event) => {
|
||||
activeSwiperIndex.value = Number(event.detail?.current || 0);
|
||||
};
|
||||
|
||||
const resetScenicCards = () => {
|
||||
panoramaMainCards.value = [];
|
||||
panoramaOtherCards.value = [];
|
||||
fineFoodCards.value = [];
|
||||
activeSwiperIndex.value = 0;
|
||||
};
|
||||
|
||||
const getHomeAllScenicData = async () => {
|
||||
try {
|
||||
const res = await homeAllScenicData();
|
||||
if (res?.code === 0 && Array.isArray(res.data)) {
|
||||
const scenicCards = res.data.filter(
|
||||
(item) => item && typeof item === "object"
|
||||
);
|
||||
const fineFoodStartIndex = Math.max(1, scenicCards.length - 2);
|
||||
|
||||
panoramaMainCards.value = scenicCards.slice(0, 1);
|
||||
panoramaOtherCards.value = scenicCards.slice(1, fineFoodStartIndex);
|
||||
fineFoodCards.value = scenicCards.slice(fineFoodStartIndex);
|
||||
activeSwiperIndex.value = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
resetScenicCards();
|
||||
console.error("获取景区数据失败", res);
|
||||
} catch (error) {
|
||||
resetScenicCards();
|
||||
console.error("获取景区数据异常", error);
|
||||
}
|
||||
};
|
||||
|
||||
const handleCardTap = (card) => {
|
||||
emit("select", card);
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
getHomeAllScenicData();
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -1,14 +1,25 @@
|
||||
<template>
|
||||
<view class="discovery-content">
|
||||
<view v-if="!isPanoramaGuideTab" class="discovery-tabs-sticky">
|
||||
<FindTabs v-model="activeIndex" :tabs="discoveryTabs" @change="handleTabChange" />
|
||||
<view class="flex h-full min-h-0 w-full flex-col bg-[#F0F8F3]">
|
||||
<view
|
||||
v-if="!isMainPanoramaGuide"
|
||||
class="z-[12] flex w-full shrink-0 flex-row items-center bg-[#F0F8F3]"
|
||||
>
|
||||
<view
|
||||
class="ml-[12px] shrink-0 whitespace-nowrap px-[4px] text-[16px] font-medium text-[rgba(128,140,153,0.9)]"
|
||||
@click="handleMainPanoramaClick"
|
||||
>
|
||||
荔波全景
|
||||
</view>
|
||||
<view v-if="discoveryTabs.length > 0" class="min-w-0 flex-1 overflow-hidden">
|
||||
<FindTabs v-model="activeIndex" :tabs="discoveryTabs" @change="handleTabChange" />
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<scroll-view class="discovery-scroll" scroll-y @scroll="handleContentScroll">
|
||||
<view v-if="isPanoramaGuideTab" class="discovery-body">
|
||||
<PanoramicHome />
|
||||
<scroll-view class="h-0 min-h-0 w-full flex-1 overflow-hidden" scroll-y @scroll="handleContentScroll">
|
||||
<view v-if="isMainPanoramaGuide" class="flex w-full flex-col gap-[12px]">
|
||||
<PanoramicHome @select="handlePanoramicHomeSelect" />
|
||||
</view>
|
||||
<view v-else class="discovery-body">
|
||||
<view v-else class="flex w-full flex-col gap-[12px]">
|
||||
<CardSwiper v-if="discoveryCards.length > 0" :list="discoveryCards" @didSelectItem="handleCardClick" />
|
||||
<ExploreCards v-if="exploreCards.length > 0" :list="exploreCards" @didSelectItem="handleCardClick" />
|
||||
|
||||
@@ -24,7 +35,7 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed, onMounted, ref } from "vue";
|
||||
import { computed, ref } from "vue";
|
||||
import { SEND_MESSAGE_CONTENT_TEXT, SEND_MESSAGE_COMMAND_TYPE } from "@/constant/constant";
|
||||
import { navigateTo } from "@/router";
|
||||
import { getAccessToken } from "@/constant/token";
|
||||
@@ -48,18 +59,14 @@ const locationStore = useLocationStore();
|
||||
const sceneId = appStore.sceneId || "";
|
||||
|
||||
const activeIndex = ref(0);
|
||||
const discoveryTabs = ref([{
|
||||
id: '99999',
|
||||
tagName: "荔波全景",
|
||||
}]);
|
||||
const discoveryTabs = ref([]);
|
||||
const discoveryCards = ref([]);
|
||||
const exploreCards = ref([]);
|
||||
const discoveryQuickQuestions = ref([]);
|
||||
const emit = defineEmits(["content-scroll"]);
|
||||
|
||||
const isPanoramaGuideTab = computed(() => {
|
||||
return discoveryTabs.value[activeIndex.value]?.tagName === "荔波全景";
|
||||
});
|
||||
/// 是否是主景区引导页 默认是 true,点击主景区后为 false
|
||||
const isMainPanoramaGuide = ref(true);
|
||||
|
||||
const handleContentScroll = (e) => {
|
||||
emit("content-scroll", {
|
||||
@@ -67,22 +74,24 @@ const handleContentScroll = (e) => {
|
||||
});
|
||||
};
|
||||
|
||||
/// 点击主景区
|
||||
const handleMainPanoramaClick = () => {
|
||||
isMainPanoramaGuide.value = true;
|
||||
discoveryCards.value = [];
|
||||
exploreCards.value = [];
|
||||
discoveryQuickQuestions.value = [];
|
||||
};
|
||||
|
||||
|
||||
/// tabs 切换事件
|
||||
const handleTabChange = ({ tab, idx }) => {
|
||||
activeIndex.value = idx;
|
||||
if (isPanoramaGuideTab.value) {
|
||||
discoveryCards.value = [];
|
||||
exploreCards.value = [];
|
||||
discoveryQuickQuestions.value = [];
|
||||
return;
|
||||
}
|
||||
queryDiscoveryData(tab.id);
|
||||
}
|
||||
|
||||
/// 请求发现页tab数据
|
||||
const queryTabsList = async () => {
|
||||
const res = await homeTabsData();
|
||||
const queryTabsList = async (homePageId) => {
|
||||
const res = await homeTabsData({ homePageId });
|
||||
if (res.code === 0) {
|
||||
/// 处理tab数据
|
||||
const tabList = res.data;
|
||||
@@ -92,9 +101,18 @@ const queryTabsList = async () => {
|
||||
findTabByIdWithActiveTabIndex(sceneId);
|
||||
}
|
||||
}
|
||||
|
||||
const handlePanoramicHomeSelect = (item) => {
|
||||
const homePageId = item?.homePageId;
|
||||
if (!homePageId) return;
|
||||
isMainPanoramaGuide.value = false;
|
||||
|
||||
queryTabsList(homePageId);
|
||||
getLocation();
|
||||
};
|
||||
|
||||
/// 根据id查询tab并设置activeIndex
|
||||
const findTabByIdWithActiveTabIndex = (tabsId) => {
|
||||
if (isPanoramaGuideTab.value) return;
|
||||
/// 查询是否有id参数
|
||||
const activeTabIndex = discoveryTabs.value.findIndex((tab) => tab.id === tabsId);
|
||||
/// 如果有则优先展示对应tab数据,没有则展示第一个tab数据
|
||||
@@ -236,43 +254,4 @@ const getNearbyTagsData = async () => {
|
||||
}
|
||||
}
|
||||
|
||||
/// 组件挂载后请求数据
|
||||
onMounted(() => {
|
||||
queryTabsList();
|
||||
getLocation();
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.discovery-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
min-height: 0;
|
||||
background: #F0F8F3;
|
||||
}
|
||||
|
||||
.discovery-tabs-sticky {
|
||||
flex-shrink: 0;
|
||||
z-index: 12;
|
||||
width: 100%;
|
||||
background: #F0F8F3;
|
||||
}
|
||||
|
||||
.discovery-scroll {
|
||||
flex: 1;
|
||||
width: 100%;
|
||||
height: 0;
|
||||
min-height: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.discovery-body {
|
||||
width: 100%;
|
||||
gap: 12px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
import request from "../base/request";
|
||||
|
||||
/// 首页全部景区数据
|
||||
function homeAllScenicData(args) {
|
||||
return request.post("/hotelBiz/mainScene/queryHomePageListWithCache", args);
|
||||
}
|
||||
|
||||
/// 首页tab场景数据
|
||||
function homeTabsData(args) {
|
||||
return request.post("/hotelBiz/mainScene/queryTagListWithCache", args);
|
||||
@@ -47,6 +52,7 @@ function discoveryCradComponent() {
|
||||
}
|
||||
|
||||
export {
|
||||
homeAllScenicData,
|
||||
homeTabsData,
|
||||
homeTabContentData,
|
||||
getNearbyTags,
|
||||
|
||||
@@ -6,8 +6,8 @@ import { getServiceUrl } from "../api/GetServiceUrlApi";
|
||||
/// 版本号, 每次发版本前增加
|
||||
const versionValue = "1.1.3";
|
||||
|
||||
/// 是否是测试版本, 测试版本为true, 发布版本为false
|
||||
const developVersion = false;
|
||||
/// 是否是测试版本, 测试版本为 true , 发布版本为 false
|
||||
const developVersion = true;
|
||||
|
||||
// 获取服务地址
|
||||
const getEvnUrl = async () => {
|
||||
|
||||
Reference in New Issue
Block a user