You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

716 lines
18 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<template>
<view class="shopPage">
<image class="indexBG" src="/static/shop/indexBG.png" mode="widthFix"></image>
<view class="head" :style="{'top': (indexStore.screen.total)+'px'}">
<uni-search-bar :radius="5" v-model="searchText" class="searchBar" placeholder="请输入您正在找的商品..."
bgColor="#FFFFFF" @confirm="search" cancelButton="none" style="flex: 1;" />
<view class="carBox" @click.stop="$util.redirectTo('./cart')">
<image class="carIcon" src="/static/shop/carIcon.png" mode="aspectFill"></image>
<text>购物车</text>
</view>
</view>
<view class="body">
<view class="content1">
<view class="card1">
<view class="cardItem" v-for="item in 5">
<view class="cardItemHead">
<image src="/static/test/test.png" mode="aspectFill"></image>
</view>
<view class="cardItemBody">
<text class="title">刮刮烫圆标镭射防伪标签</text>
<text class="yuan"><text class="yuanIcon">¥</text>3600</text>
</view>
</view>
</view>
<view class="retangle">
<image class="yingxiao" src="/static/shop/c1BG.png" mode="widthFix"></image>
<view class="textBox">
<text>营销系统定制开发</text>
<image class="mobile" src="/static/shop/mobile.png" mode="widthFix"></image>
<button class="checkBtn">查看</button>
</view>
</view>
</view>
<view class="content2">
<view class="left">
<swiper style="height: 100%;" :indicator-dots="true" :autoplay="true" :interval="3000"
:duration="1000" indicator-color="rgba(255, 255, 255, 0.3)" indicator-active-color="#3A71FF">
<swiper-item v-for="item in 3">
<view class="swiperItem">
<image class="swiperImg" src="/static/shop/swiperImg.png" mode="aspectFill"></image>
<button class="customBtn">
定制方案
<image class="rightIcon" src="/static/shop/rightsanjiao.png" mode="aspectFill">
</image>
</button>
</view>
</swiper-item>
</swiper>
</view>
<view class="right">
<view class="card">
<view class="cardHead">
<text>防伪溯源系统</text>
</view>
<view class="cardBody">
<view class="imgList">
<view class="imgItem">
<image src="/static/shop/c1.png" mode="aspectFill"></image>
</view>
<view class="imgItem">
<image src="/static/shop/c2.png" mode="aspectFill"></image>
</view>
</view>
</view>
</view>
<view class="card">
<view class="cardHead">
<text>云监码</text>
</view>
<view class="cardBody">
<view class="imgList1">
<view class="imgItem">
<image src="/static/shop/yunjianma.png" mode="aspectFill"></image>
</view>
</view>
</view>
</view>
</view>
</view>
<view class="content3" >
<view class="headbar" :style="{ 'top': indexStore.screen.total + 'px' }">
<view class="barItem" :class="activeIndex === 0?'active':''" @click.stop="changeBar(0)">
<text>为你推荐</text>
<view class="xiahuaxian" v-if="activeIndex === 0"></view>
</view>
<view class="barItem" :class="activeIndex === 1?'active':''" @click.stop="changeBar(1)">
<text>防伪标签</text>
<view class="xiahuaxian" v-if="activeIndex === 1"></view>
</view>
<view class="barItem" :class="activeIndex === 2?'active':''" @click.stop="changeBar(2)">
<text>防伪溯源系统</text>
<view class="xiahuaxian" v-if="activeIndex === 2"></view>
</view>
<view class="barItem" :class="activeIndex === 3?'active':''" @click.stop="changeBar(3)">
<text>产线升级</text>
<view class="xiahuaxian" v-if="activeIndex === 3"></view>
</view>
<view class="barItem" :class="activeIndex === 4?'active':''" @click.stop="changeBar(4)">
<text>云监码</text>
<view class="xiahuaxian" v-if="activeIndex === 4"></view>
</view>
</view>
<scroll-view
id="listScroll"
class="list-scroll"
scroll-y
:scroll-with-animation="true"
style="height:1000rpx"
ref="scrollViewRef"
:show-scrollbar="false"
:scroll-into-view="targetID"
@scroll="onScroll"
>
<view class="list">
<view class="listItem" v-for="(item,index) in categories" :id='`list-${index}`'>
<view class="listItemHead">
<view class="titleBox">
<text>{{ item.name }}</text>
<view class="purpleBox"></view>
</view>
<text class="seeMore">查看更多></text>
</view>
<view class="listItemBody">
<view class="card" v-for="item in 5">
<view class="cardHead">
<image src="/static/test/test.png" mode="aspectFill"></image>
</view>
<view class="cardBody">
<text class="title">智能产线升级改造配套方案定制</text>
<text class="subTitle">单一线缆提供丰富IO接口可接入多路输入、输出信号顶部环形指示灯快速观察工作状态</text>
<view class="">
<text class="yuan"><text class="yuanText">¥</text>12333</text>
<text class="yuan1"><text class="yuanText1">¥</text>12333</text>
</view>
</view>
</view>
</view>
</view>
</view>
</scroll-view>
</view>
</view>
<tabbar :tabIndex="1"></tabbar>
</view>
</template>
<script setup>
import {
onMounted,
nextTick,
ref,
watch,
} from 'vue';
import {
useIndexStore
} from '../../store';
const indexStore = useIndexStore()
const searchText = ref('')
const activeIndex = ref(0)
const search = () => {
console.log("search")
}
// const changeBar = (index) => {
// if (activeIndex.value === index) return;
// activeIndex.value = index
// }
onMounted(() => {
uni.getSystemInfo({
success: (e) => {
let custom = uni.getMenuButtonBoundingClientRect();
const data = {
tops: e.statusBarHeight,
height: custom.height + (custom.top - e.statusBarHeight) * 2,
total: custom.height + (custom.top - e.statusBarHeight) * 2 + e.statusBarHeight
}
indexStore.setScreen(data)
}
})
})
const categories = ref([{
name: '为你推荐',
items: ['推荐1', '推荐2', '推荐3', '推荐4', '推荐5', '推荐6', '推荐7']
},
{
name: '防伪标签',
items: ['标签1', '标签2', '标签3', '标签4', '标签5']
},
{
name: '防伪溯源系统',
items: ['系统1', '系统2', '系统3', '系统4']
},
{
name: '产线升级',
items: ['升级1', '升级2', '升级3', '升级4', '升级5']
},
{
name: '云监码',
items: ['监码1', '监码2', '监码3']
} // 新增第5项对应索引4
])
const categoryHeights = ref([])
const testRef = ref(null)
const initHeights = async () => {
await nextTick()
const query = wx.createSelectorQuery()
categories.value.forEach((item, index) => {
query
.select(`#list-${index}`) // 选择id为"test-id"的元素
.boundingClientRect((data) => { // 回调函数:获取元素布局信息
if (!data) {
console.error("未找到id为test-id的元素")
return
}
categoryHeights.value.push(data.height)
})
.exec() // 执行查询
})
console.log("total", categoryHeights.value)
}
// 响应式变量
const scrollViewRef = ref(null)
const targetID = ref(null)
// 存储每个 listItem 的关键信息:相对 scroll-view 的顶部偏移 + 自身高度
const itemPositions = ref([])
// scroll-view 自身的可视高度
const scrollViewHeight = ref(0)
// 封装:通过选择器获取元素信息(返回 Promise
const getElementsInfo = (selector) => {
return new Promise(resolve => {
wx.createSelectorQuery()
.selectAll(selector)
.boundingClientRect(resolve)
.exec()
})
}
// 初始化:动态获取每个 listItem 的位置和高度(相对于 scroll-view 内部)
const initItemPositions = async () => {
await nextTick()
await new Promise(resolve => setTimeout(resolve, 100)) // 确保动态内容渲染完成
// 1. 获取 scroll-view 自身的位置和高度
const [scrollViewInfo] = await getElementsInfo('.list-scroll')
if (!scrollViewInfo) return
const scrollViewTop = scrollViewInfo.top // scroll-view 顶部在视口中的位置
scrollViewHeight.value = scrollViewInfo.height // scroll-view 可视高度
// 2. 获取所有 listItem 的位置和高度(动态高度)
const listItemsInfo = await getElementsInfo('.listItem')
if (!listItemsInfo || listItemsInfo.length === 0) return
// 3. 计算每个 listItem 相对于 scroll-view 内部的信息
itemPositions.value = listItemsInfo.map((item, index) => ({
index,
// 相对 scroll-view 顶部的偏移(内部坐标)
topInScroll: item.top - scrollViewTop,
// 自身实际高度(动态获取)
height: item.height,
// 相对 scroll-view 底部的偏移(内部坐标)= 顶部偏移 + 自身高度
bottomInScroll: (item.top - scrollViewTop) + item.height
}))
console.log("动态获取的 listItem 信息:", itemPositions.value)
}
// 点击分类跳转
const changeBar = async (index) => {
if (activeIndex.value === index) return
activeIndex.value = index
const target = `list-${index}`
targetID.value = target
await nextTick() // 等待滚动触发
}
// 滚动时判断:根据动态高度和位置激活对应分类
const onScroll = (e) => {
const scrollTop = e.detail.scrollTop // scroll-view 内部滚动距离
// 遍历所有 listItem找到当前视口内最顶部的项
for (let i = 0; i < itemPositions.value.length; i++) {
const { topInScroll, bottomInScroll, index } = itemPositions.value[i]
// 视口判断条件(适配动态高度):
// 1. 元素顶部 <= 滚动距离 + 100rpx允许轻微误差
// 2. 元素底部 > 滚动距离(确保元素至少有一部分在视口内)
if (topInScroll <= scrollTop + 200 && bottomInScroll > scrollTop) {
activeIndex.value = index
break // 优先激活视口内最顶部的元素
}
}
}
// 监听数据变化:若 categories 动态更新,重新计算位置
watch(categories, async () => {
await initItemPositions()
}, { deep: true })
// 页面挂载后初始化
onMounted(async () => {
await initItemPositions()
})
</script>
<style lang="scss" scoped>
.shopPage {
width: 100%;
min-height: 100vh;
position: relative;
background-color: #EFF6FF;
// padding-bottom: 100rpx;
}
.indexBG {
width: 100%;
height: auto;
}
.head {
width: 100%;
position: absolute;
z-index: 2;
box-sizing: border-box;
padding: 16rpx 26rpx;
@include flexBox(between, center);
.carBox {
@include fontStyle(20rpx, #3A71FF, center, 400, 30rpx);
margin-left: 16rpx;
.carIcon {
width: 44rpx;
height: 44rpx;
display: block;
margin: 0 auto;
}
}
}
.body {
width: 100%;
.content1 {
margin: 26rpx;
background: #FFFFFF;
box-shadow: 0rpx 4rpx 7rpx 0rpx rgba(0, 0, 0, 0.08);
border-radius: 18rpx 18rpx 18rpx 18rpx;
box-sizing: border-box;
padding: 26rpx;
@include flexBox(start, center);
.card1 {
@include flexBox(start, center);
width: 84%;
overflow-x: auto;
white-space: nowrap;
scrollbar-width: none;
/* 隐藏 WebKit 内核浏览器Chrome、Safari、小程序等的滚动条 */
&::-webkit-scrollbar {
display: none;
/* 直接隐藏滚动条 */
}
.cardItem {
width: 226rpx;
background: #F5F5F5;
border-radius: 18rpx 18rpx 18rpx 18rpx;
margin-right: 16rpx;
.cardItemHead {
box-sizing: border-box;
padding: 16rpx;
@include flexBox();
image {
width: 192rpx;
height: 192rpx;
border-radius: 18rpx;
}
}
.cardItemBody {
background: #E1EDFC;
box-sizing: border-box;
padding: 16rpx;
border-radius: 0rpx 0rpx 18rpx 18rpx;
.title {
@include fontStyle(24rpx, #000000, left, 500, 38rpx);
@include truncateText(1);
}
.yuan {
@include fontStyle(34rpx, #3A71FF, left, bold, 50rpx);
}
.yuanIcon {
@include fontStyle(24rpx, #3A71FF, left, 500, 38rpx);
}
}
}
}
.retangle {
width: 30%;
position: relative;
min-height: 340rpx;
.yingxiao {
position: absolute;
top: 0;
right: -26rpx;
z-index: 2;
transform: translate(0, -10%);
width: 254rpx;
height: auto;
}
.textBox {
@include fontStyle(26rpx, #FFFFFF, left, bold, 38rpx);
position: absolute;
top: 0;
right: -26rpx;
transform: translate(0, -10%);
box-sizing: border-box;
padding: 26rpx;
z-index: 10;
width: 254rpx;
@include flexBox(end, end, column);
.mobile {
width: 120rpx;
height: auto;
margin: 16rpx 0;
}
.checkBtn {
width: 124rpx;
height: 54rpx;
background: #FFFFFF;
border-radius: 54rpx 54rpx 54rpx 54rpx;
font-weight: 400;
font-size: 25rpx;
color: #0C53FF;
@include flexBox();
margin: 0;
}
}
}
}
.content2 {
margin: 40rpx 16rpx 26rpx;
margin-right: 10rpx;
@include flexBox(between, center);
.left {
width: 410rpx;
height: 570rpx;
.swiperItem {
position: relative;
.swiperImg {
width: 410rpx;
height: 566rpx;
border-radius: 18rpx;
display: block;
}
.customBtn {
position: absolute;
bottom: 56rpx;
left: 50%;
transform: translate(-50%, 0);
width: 194rpx;
height: 58rpx;
background: linear-gradient(180deg, #3A71FF 0%, #739CFF 100%);
border-radius: 54rpx 54rpx 54rpx 54rpx;
font-weight: 500;
font-size: 25rpx;
color: #FFFFFF;
@include flexBox();
.rightIcon {
width: 36rpx !important;
height: 36rpx !important;
}
}
}
}
.right {
flex: 1;
height: 570rpx;
@include flexBox(between, center, column);
.card {
width: 300rpx;
background: #FFFFFF;
box-shadow: 0rpx 4rpx 7rpx 0rpx rgba(0, 0, 0, 0.08);
border-radius: 18rpx 18rpx 18rpx 18rpx;
.cardHead {
box-sizing: border-box;
padding: 18rpx;
@include fontStyle(30rpx, #000000, left, 500, 38rpx);
}
.cardBody {
box-sizing: border-box;
padding: 18rpx 0;
.imgList {
@include flexBox(start, center);
overflow-x: auto;
.imgItem {
width: 50%;
text-align: center;
image {
width: 118rpx;
height: 158rpx;
}
}
}
.imgList1 {
height: 100%;
box-sizing: border-box;
padding: 40rpx 16rpx;
@include flexBox(center, center);
.imgItem {
image {
width: 244rpx;
height: 80rpx;
}
}
}
}
}
}
}
.content3 {
margin: 26rpx;
.headbar {
position: sticky;
left: 0;
width: 100%;
height: 92rpx;
background: #FFFFFF;
box-shadow: 0rpx 4rpx 7rpx 0rpx rgba(0, 0, 0, 0.08);
border-radius: 18rpx;
@include flexBox(start, center);
flex-wrap: nowrap;
box-sizing: border-box;
padding: 20rpx 16rpx;
overflow-x: auto;
scrollbar-width: none;
/* 隐藏 WebKit 内核浏览器Chrome、Safari、小程序等的滚动条 */
&::-webkit-scrollbar {
display: none;
/* 直接隐藏滚动条 */
}
.barItem {
height: 100%;
position: relative;
@include fontStyle(30rpx, #333333, center, 400, 40rpx);
margin-right: 26rpx;
white-space: nowrap;
.xiahuaxian {
position: absolute;
left: 50%;
transform: translate(-50%, 0);
bottom: 0rpx;
width: 54rpx;
height: 4rpx;
background: #333333;
border-radius: 9rpx 9rpx 9rpx 9rpx;
}
}
.active {
font-weight: bold;
font-size: 32rpx;
}
}
.list-scroll{
margin-top: 26rpx;
}
.list {
.listItem {
.listItemHead {
margin: 26rpx 0 16rpx;
@include flexBox(between, center);
@include fontStyle(34rpx, #000000, left, bold, 44rpx);
.seeMore {
@include fontStyle(26rpx, #666666, right, 400, 44rpx);
}
.titleBox {
position: relative;
text {
position: relative;
z-index: 2;
}
.purpleBox {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, 0%);
width: 92rpx;
height: 28rpx;
background: linear-gradient(180deg, #86AAFA 0%, #C9D6FF 100%);
opacity: 0.9;
z-index: 1;
}
}
}
.listItemBody {
@include flexBox(between, center);
flex-wrap: wrap;
.card {
width: 334rpx;
background: #FFFFFF;
box-shadow: 0rpx 4rpx 9rpx 0rpx rgba(0, 0, 0, 0.08);
border-radius: 24rpx 24rpx 24rpx 24rpx;
margin-top: 26rpx;
.cardHead {
image {
width: 100%;
height: 334rpx;
display: block;
margin: 0;
}
}
.cardBody {
box-sizing: border-box;
padding: 16rpx 26rpx;
.title {
@include fontStyle(28rpx, #000000, left, 500, 48rpx);
@include truncateText(1);
}
.subTitle {
@include fontStyle(26rpx, #666666, left, 500, 38rpx);
@include truncateText();
}
.yuan {
@include fontStyle(36rpx, #3A71FF, left, bold, 48rpx);
padding-right: 20rpx;
.yuanText {
@include fontStyle(26rpx, #3A71FF, left, bold, 48rpx);
}
}
.yuan1 {
@include fontStyle(26rpx, #666666, left, 400, 48rpx);
text-decoration: line-through;
.yuanText1 {
@include fontStyle(24rpx, #666666, left, 400, 48rpx);
}
}
}
}
}
}
}
}
}
</style>