忽略 van-sticky van-notice-bar left-icon="volume-o" text
van-sticky
van-sticky van-notice-bar left-icon="volume-o" text=""
van-sticky
gpt给的demo
<template>
<div class="product-page">
<!-- 顶部分类标签 -->
<van-tabs
v-model:active="activeTab" <!-- 绑定当前激活的标签索引 -->
sticky <!-- 吸顶效果 -->
offset-top="0"
swipeable <!-- 支持左右滑动切换 -->
animated <!-- 开启切换动画 -->
>
<van-tab
v-for="(category, index) in categories"
:key="category.id"
:title="category.name"
>
<!-- 商品列表 -->
<div class="product-list">
<van-card
v-for="item in productsByCategory(category.id)"
:key="item.id"
:thumb="item.image"
:title="item.name"
:desc="item.desc"
/>
</div>
</van-tab>
</van-tabs>
</div>
</template>
<script setup>
// 引入 Vue 组合式 API 和 Vant 组件
import { ref, reactive, watch, nextTick } from 'vue';
import { Tabs, Tab, Sticky, Card } from 'vant';
// Mock 数据:商品分类和商品列表
const categories = ref([
{ id: 1, name: '电子产品' },
{ id: 2, name: '服装' },
{ id: 3, name: '家居' },
]);
const products = ref([
{ id: 101, name: '手机', desc: '旗舰智能手机', image: 'https://via.placeholder.com/150', categoryId: 1 },
{ id: 102, name: '笔记本电脑', desc: '轻薄便携', image: 'https://via.placeholder.com/150', categoryId: 1 },
{ id: 201, name: '连衣裙', desc: '夏季新款', image: 'https://via.placeholder.com/150', categoryId: 2 },
{ id: 202, name: '牛仔裤', desc: '休闲百搭', image: 'https://via.placeholder.com/150', categoryId: 2 },
{ id: 301, name: '沙发', desc: '现代简约', image: 'https://via.placeholder.com/150', categoryId: 3 },
{ id: 302, name: '茶几', desc: '时尚耐用', image: 'https://via.placeholder.com/150', categoryId: 3 },
]);
// 当前激活的标签索引
const activeTab = ref(0);
// 保存每个分类的滚动位置
const scrollPositions = reactive({});
// 根据分类 ID 过滤商品列表
function productsByCategory(catId) {
return products.value.filter(item => item.categoryId === catId);
}
// 监听 activeTab 的变化,保存旧标签的滚动位置并恢复新标签的滚动位置
watch(activeTab, async (newIndex, oldIndex) => {
// 保存切换前标签的滚动值
scrollPositions[oldIndex] = window.scrollY || 0;
await nextTick();
// 恢复新标签的滚动值
window.scrollTo(0, scrollPositions[newIndex] || 0);
});
</script>
<style scoped>
.product-page {
width: 100%;
}
.product-list {
padding: 10px 0;
}
</style>
<template>
<van-sticky>
<van-notice-bar
left-icon="volume-o"
text="无论我们能活多久,我们能够享受的只有无法分割的此刻,此外别无其他。"
/>
</van-sticky>
<div class="my-swipe">
<div>1</div>
<div>2</div>
</div>
<div class="box" :class="scrollClass">
<van-tabs
v-model:active="active"
:lazy-render="false"
swipeable
sticky
offset-top="40"
@scroll="scroll"
@change="handleTabChange"
:before-change="beforeChange"
@click-tab="clickTab"
>
<van-tab
v-for="index in 4"
:title="'标签 ' + index"
:key="index"
:name="index"
class="van-tab-111"
>
<pageF
:ref="(el) => setItemRef(el, index)"
:key="index"
:index="index"
></pageF>
</van-tab>
</van-tabs>
</div>
</template>
<script setup lang="ts">
import { reactive, ref, onMounted, nextTick, onUnmounted } from "vue";
import pageF from "./PageF.vue";
import { getList1 } from "../api";
let active = ref(0);
let scrollClass = ref("scrollClass");
let refs = ref([]);
let scrollList = ref([]);
let isSticky = ref(false);
const setItemRef = (el, index) => {
// console.log(index, "index");
refs.value[index] = el;
};
let arr = [1, 2, 3, 4];
let p1 = new Promise((res, rej) => {
rej("cuowu");
});
let arr2 = [];
arr.forEach((e, index) => {
const url = `http://localhost:3001/api/items?page=${e}&limit=${10 + 10}`;
arr2.push(fetch(url).then((res) => res.json()));
});
let isClick = false;
function scroll(temp) {
console.log(temp)
let el = document.querySelector(" .van-sticky--fixed");
if(el){
console.log('xitng')
} else {
scrollList.value = []
}
if(isClick) return
// console.log(temp, "scroll", active.value);
// console.log(document.documentElement.scrollTop, "scroll",'document.documentElement.scrollTop');
// console.log(
// scrollList.value[active.value],
// "scrollList.value[active.value]"
// );
console.log('scroll')
scrollList.value[active.value] = temp.scrollTop;
}
function clickTab(e) {
console.log("clickTab");
// console.log(e);
active.value = e.name;
// console.log(isSticky.value, scrollList.value[active.value]);
// console.log("clickTab", scrollList.value);
}
// cont prevIndex = -1
async function handleTabChange(index) {
console.log("handletabchange", active.value);
await nextTick();
isClick = true
if (scrollList.value[active.value]) {
setTimeout(async () => {
// let offsetHeight = document.querySelectorAll('.van-swipe-item')[active.value].offsetHeight
// console.log(offsetHeight, 'offsetHeight-out')
document.documentElement.scrollTop = scrollList.value[active.value];
// if(offsetHeight > 0){
// console.log(offsetHeight, 'offsetHeight')
// }
isClick = false
}, 200);
// console.log(document.querySelectorAll('.van-swipe-item')[active.value].offsetHeight, 'van-swipe-item.offsetHeight')
} else {
isClick = false
}
}
function handleSwiper(direction) {
// console.log(direction, "====");
}
function beforeChange(a) {
// console.log("11beforeChange");
// scrollList.value[active.value] = document.documentElement.scrollTop;
// let el = document.querySelector(" .van-sticky--fixed");
// console.log(el, "e", scrollList.value);
// if (el) {
// isSticky.value = true;
// } else {
// isSticky.value = false;
// }
return true;
}
onMounted(() => {
nextTick(() => {
Promise.allSettled(arr2)
.then((res) => {
console.log(res, "==========");
res.forEach((e, index) => {
// console.log(refs.value)
refs.value[index + 1].addList(e.value.products, index);
});
})
.catch((err) => {
console.log(err);
});
});
let e = document.querySelector(".box");
let t = getTop(e);
console.log(getTop(e), "getTop");
function getTop(e) {
var offset = e.offsetTop;
if (e.offsetParent != null) {
offset += getTop(e.offsetParent);
}
return offset;
}
// 添加滚动事件,简写,实际需要写卸载事件
// window.onscroll = function(e){
// let top = document.documentElement.scrollTop
// if(top >= 300){
// scrollClass.value = 'scrollClass'
// }else {
// scrollClass.value = ''
// }
// }
});
</script>
<style lang="scss"></style>
<style lang="scss" scoped>
.tab-wrapper {
width: 400px;
height: 100vh;
box-sizing: border-box;
padding-top: 60px;
margin: 0 auto;
display: flex;
flex-direction: column;
&__top {
flex: none;
background-color: #e7e7e7;
display: flex;
p {
flex: 1;
text-align: center;
line-height: 2;
cursor: pointer;
&.active {
background-color: #ccc;
color: #fff;
}
}
}
&__bottom {
background-color: #f2f2f2;
flex: 1;
position: relative;
overflow: hidden;
}
}
.tab-page {
}
.tab-page-box {
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
overflow-y: auto;
}
img {
width: 168px;
height: 168px;
}
.tab-wrapper__bottom {
background-color: pink;
}
.product-grid {
display: flex;
/* flex-wrap: wrap; */
}
.prod-item {
flex: 1;
}
.good-item {
border: 1px solid #777;
border-radius: 4px;
margin-bottom: 10px;
}
.product-item {
margin: 10px;
border: 1px solid #ccc;
padding: 10px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
}
.product-description {
margin-top: 10px;
}
.my-swipe {
color: #fff;
font-size: 20px;
line-height: 150px;
text-align: center;
background-color: #39a9ed;
}
.box {
// padding: 10px;
// margin: 20px;
}
</style>
``