Files
longli-mp/src/pages-sub/catalog/index.vue
T

120 lines
4.4 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<script setup>
/**
* 二级 · 板块列表页(需求 §5 · v10 定稿)
* 250px 头图 + 白面板上浮 30px;面板内「左侧 86px 分组导航 + 右侧卡片列表」双列。
* 分组导航带数量角标;列表按 type_order → sort_within_type 排序;不展示 tags;无搜索。
*/
import { computed, ref } from "vue";
import { onLoad, onShareAppMessage } from "@dcloudio/uni-app";
import contentApi from "@/api/content";
import { REGION_TEXT } from "@/constant";
import { previewImages } from "@/utils/nav";
const sectionKey = ref("");
const section = ref(null);
const activeIndex = ref(0);
const bannerImages = ref([]);
const scrollIntoView = ref("");
const groups = computed(() => section.value?.groups || []);
const title = computed(() => section.value?.title || "");
const cover = computed(() => section.value?.coverImage || "");
onLoad(async (options) => {
sectionKey.value = options?.section || "tourism";
const [data, banners] = await Promise.all([
contentApi.getSection(sectionKey.value),
contentApi.getSectionBannerImages(sectionKey.value),
]);
section.value = data;
bannerImages.value = banners || [];
if (groups.value.length) {
scrollIntoView.value = `g-${groups.value[0].groupId}`;
}
});
/** 点左侧分组:右侧列表滚动到对应锚点 */
const onGroupTap = (index) => {
activeIndex.value = index;
const group = groups.value[index];
if (group) scrollIntoView.value = "";
setTimeout(() => {
scrollIntoView.value = `g-${group.groupId}`;
}, 0);
};
/** 点头图预览:优先看板块全景图集,没有图集时退回预览头图本身 */
const onCoverPreview = () => {
const list = bannerImages.value.length
? bannerImages.value
: [cover.value].filter(Boolean);
if (!list.length) return;
previewImages(list, list[0]);
};
onShareAppMessage(() => ({
title: `${title.value} · 龙里文旅`,
path: `/pages-sub/catalog/index?section=${sectionKey.value}`,
imageUrl: cover.value,
}));
</script>
<template>
<view class="relative min-h-screen w-full overflow-hidden">
<!-- 头图(点击直接预览大图,原「全景图」胶囊按钮已去除) -->
<view class="relative h-[250px] overflow-hidden">
<image class="absolute inset-0 w-full h-full" :src="cover" mode="aspectFill" @click="onCoverPreview" />
<view class="absolute inset-0 scrim-hd" />
<view class="relative z-[5]">
<AppStatusBar />
<AppNavBar theme="light" />
</view>
<view class="absolute left-5 right-5 bottom-[46px] z-[5]">
<text class="text-h1 font-bold text-white tracking-[0.3px]">{{ title }}</text>
<view class="mt-[7px] text-cap text-[rgba(255,255,255,0.86)]">{{ REGION_TEXT }}</view>
</view>
</view>
<!-- 内容面板:分组导航 + 卡片列表(双列) -->
<view
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"
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>
<text class="ml-[3px] text-[10px]" :class="i === activeIndex ? 'text-brand' : 'text-ink3'">
{{ 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>
<view v-for="(item, i) in group.items" :key="item.id" :class="i ? 'mt-[10px]' : ''">
<ContentCard :item="item" show-address :thumb-size="100" />
</view>
</view>
<EmptyBox v-if="!groups.length" text="该板块暂无内容" />
<view class="h-5" />
</scroll-view>
</view>
</view>
</view>
</template>