feat:mock 的对接了首页、分类、商家列表和详情的接口

This commit is contained in:
zoujing committed 2026-09-17 15:51:58 +08:00
1 parent 87cf7dfbaa
commit d723eb1232
20 files changed
+3948 -333

No files matched your search

+42 -36
View File
@@ -1,49 +1,64 @@
<script setup>
/**
* 二级 · 板块列表页(需求 §5 · v10 定稿)
* 250px 头图 + 白面板上浮 30px;面板内「左侧 86px 分组导航 + 右侧卡片列表」双列。
* 分组导航带数量角标;列表按 type_order → sort_within_type 排序;不展示 tags;无搜索。
* 二级 · 板块列表页(需求 §5)
* 数据链路:GET /v1/categories?section_key= → 左列分类导航;
* GET /v1/merchants?section_key= → 全部商家,前端按 category_id 归组到各分类下。
* 250px 头图 + 白面板上浮 30px;面板内「左侧 86px 分类导航 + 右侧卡片列表」双列。
* 头图与大标题**前端自持**:头图取首页同 section_key 的板块卡图,大标题走 SECTION_TITLE。
*/
import { computed, ref } from "vue";
import { onLoad, onShareAppMessage } from "@dcloudio/uni-app";
import contentApi from "@/api/content";
import { REGION_TEXT } from "@/constant";
import { REGION_TEXT, SECTION_TITLE } from "@/constant";
import { previewImages } from "@/utils/nav";
const sectionKey = ref("");
const section = ref(null);
const activeIndex = ref(0);
const categories = ref([]);
const merchants = ref([]);
const cover = ref("");
const bannerImages = ref([]);
const activeIndex = ref(0);
const scrollIntoView = ref("");
const groups = computed(() => section.value?.groups || []);
const title = computed(() => section.value?.title || "");
const cover = computed(() => section.value?.coverImage || "");
/** 左列分类 + 右列该分类下的商家卡片 */
const groups = computed(() =>
categories.value.map((c) => ({
id: c.id,
name: c.name,
items: merchants.value.filter((m) => m.category_id === c.id),
}))
);
const title = computed(() => SECTION_TITLE[sectionKey.value] || "");
onLoad(async (options) => {
sectionKey.value = options?.section || "tourism";
const [data, banners] = await Promise.all([
contentApi.getSection(sectionKey.value),
sectionKey.value = options?.section || "scenic";
const [cats, list, banners, coverUrl] = await Promise.all([
contentApi.getCategories(sectionKey.value),
contentApi.getMerchants({ section_key: sectionKey.value }),
contentApi.getSectionBannerImages(sectionKey.value),
contentApi.getSectionCover(sectionKey.value),
]);
section.value = data;
categories.value = cats?.items || [];
merchants.value = list?.items || [];
bannerImages.value = banners || [];
cover.value = coverUrl || "";
if (groups.value.length) {
scrollIntoView.value = `g-${groups.value[0].groupId}`;
scrollIntoView.value = `g-${groups.value[0].id}`;
}
});
/** 点左侧分组:右侧列表滚动到对应锚点 */
/** 点左侧分类:右侧列表滚动到对应锚点 */
const onGroupTap = (index) => {
activeIndex.value = index;
const group = groups.value[index];
if (group) scrollIntoView.value = "";
setTimeout(() => {
scrollIntoView.value = `g-${group.groupId}`;
if (group) scrollIntoView.value = `g-${group.id}`;
}, 0);
};
/** 点头图预览:优先看板块全景图集,没有图集时退回预览头图本身 */
/** 点头图预览:优先看板块图集,没有图集时退回预览头图本身 */
const onCoverPreview = () => {
const list = bannerImages.value.length
? bannerImages.value
@@ -77,35 +92,26 @@ onShareAppMessage(() => ({
</view>
</view>
<!-- 内容面板:分组导航 + 卡片列表(双列) -->
<!-- 内容面板:分类导航 + 卡片列表(双列) -->
<view
class="relative -mt-[30px] rounded-t-panel bg-white h-[calc(100vh_-_220px)] min-h-[460px] overflow-hidden shadow-pan"
>
class="relative -mt-[30px] rounded-t-panel bg-white h-[calc(100vh_-_220px)] min-h-[460px] overflow-hidden shadow-pan">
<view class="flex h-full">
<scroll-view class="w-[86px] shrink-0 bg-alt overflow-hidden pt-3" scroll-y :show-scrollbar="false">
<view
v-for="(group, i) in groups"
:key="group.groupId"
<view v-for="(group, i) in groups" :key="group.id"
class="h-16 flex items-center justify-center text-cap relative text-center leading-[1.3]"
:class="i === activeIndex ? 'bg-white text-brand-deep font-semibold rounded-r-[14px] deco-mark' : 'text-ink2'"
@click="onGroupTap(i)"
>
<text>{{ group.type }}</text>
@click="onGroupTap(i)">
<text>{{ group.name }}</text>
<text class="ml-[3px] text-[10px]" :class="i === activeIndex ? 'text-brand' : 'text-ink3'">
{{ group.items.length }}
({{ group.items.length }})
</text>
</view>
</scroll-view>
<scroll-view
class="flex-1 min-w-0 bg-white px-3 pt-1 pb-5"
scroll-y
:show-scrollbar="false"
:scroll-into-view="scrollIntoView"
:scroll-with-animation="true"
>
<view v-for="group in groups" :id="`g-${group.groupId}`" :key="group.groupId">
<view class="text-[16px] font-semibold mt-3 mb-[9px] text-ink">{{ group.type }}</view>
<scroll-view class="flex-1 min-w-0 bg-white px-3 pt-1 pb-5" scroll-y :show-scrollbar="false"
:scroll-into-view="scrollIntoView" :scroll-with-animation="true">
<view v-for="group in groups" :id="`g-${group.id}`" :key="group.id">
<view class="text-[16px] font-semibold mt-3 mb-[9px] text-ink">{{ group.name }}</view>
<view v-for="(item, i) in group.items" :key="item.id" :class="i ? 'mt-[10px]' : ''">
<ContentCard :item="item" show-address :thumb-size="100" />
</view>