937 lines
24 KiB
Vue
Raw Normal View History

2025-03-20 13:59:39 +08:00
<template>
<!-- tabbar页面设置safe-area-inset-bottom为false取消底部安全区域适配 -->
<view class="Mall4j index page-index">
<!-- #ifndef H5-->
<!-- 自定义头部 -->
<custom-navigation-bar
v-if="navigationBarIsShow"
bg-color="bg-gradual-pink"
:show-back="false"
:navigation-bar-style="tabConfig"
:is-left="false"
:is-bg-img="isBgImg"
:title="title"
/>
<!-- #endif -->
<z-paging
v-if="!renovationId && isLoad"
ref="paging"
v-model="categoryProdList"
:empty-view-text="'没有找到对应商品,看看别的吧'"
:hide-empty-view="!!renovationId"
:refresher-only="!!renovationId"
:safe-area-inset-bottom="false"
:auto-scroll-to-top-when-reload="false"
:auto-clean-list-when-reload="false"
:auto-hide-empty-view-when-loading="false"
:paging-style="{'top': pagingTop,'background': '#f2f3f7'}"
@query="onGetSearchList"
@scrolltolower="onScrolltolower"
@on-refresh="onRefresh"
>
<view
v-if="!renovationId && isLoad"
class="container"
>
<!-- 搜索框 -->
<view class="search-box">
<view
class="search"
@tap="onToSrearch"
>
<view class="icon">
<image src="/static/images/search.png" />
</view>
<view class="text">
{{ $t('index.searchTips') }}
</view>
</view>
</view>
<!-- banner -->
<view
v-if="indexImgs.length"
class="banner"
>
<view class="bg">
<image src="/static/images/bannerBg.png" />
</view>
<swiper
class="img"
indicator-dots
indicator-color="rgba(255,252,255, .3)"
indicator-active-color="rgba(255,252,255, .6)"
autoplay
circular
>
<swiper-item
v-for="(imgItem, imgIdx) in indexImgs"
:key="imgIdx"
class="item"
@tap="onToProdDetail(imgItem.spuId)"
>
<img-show :src="imgItem.imgUrl" />
</swiper-item>
</swiper>
</view>
<!-- 导航 -->
<view class="nav-box">
<view
class="item"
@tap="onToNewProds"
>
<view class="icon">
<image src="/static/images/icon-new.png" />
</view>
<view class="text">
{{ $t('index.newProduct') }}
</view>
</view>
<view
class="item"
@tap="onToDiscountList"
>
<view class="icon">
<image src="/static/images/icon-sale.png" />
</view>
<view class="text">
{{ $t('index.specialDiscounts') }}
</view>
</view>
<view
class="item"
@tap="onToGroupProds"
>
<view class="icon">
<image src="/static/images/icon-group.png" />
</view>
<view class="text">
{{ $t('index.groupDiscount') }}
</view>
</view>
<view
class="item"
@tap="onToSeckillProds"
>
<view class="icon">
<image src="/static/images/icon-flash.png" />
</view>
<view class="text">
{{ $t('index.spikeSpecial') }}
</view>
</view>
<view
class="item"
@tap="onToCouponCenter"
>
<view class="icon">
<image src="/static/images/icon-coupon.png" />
</view>
<view class="text">
{{ $t('index.couponCentre') }}
</view>
</view>
<view
class="item"
@tap="onAuthRouteTo('member-center-general')"
>
<view class="icon">
<image src="/static/images/icon-member.png" />
</view>
<view class="text">
{{ $t('index.memberCentre') }}
</view>
</view>
<view
class="item"
@tap="onAuthRouteTo('distribution-center')"
>
<view class="icon">
<image src="/static/images/icon-distribution.png" />
</view>
<view class="text">
{{ $t('index.distributionCentre') }}
</view>
</view>
<view
class="item"
@tap="onAuthRouteTo('integral-mall')"
>
<view class="icon">
<image src="/static/images/icon-integral.png" />
</view>
<view class="text">
{{ $t('index.pointsMall') }}
</view>
</view>
</view>
<!-- 秒杀专区 -->
<view
v-if="seckillSpuList && seckillSpuList.length"
class="flash-sale"
>
<view class="tit">
<view class="text">
{{ $t('index.spikeZone') }}
</view>
<view
v-if="nextTime > 0"
class="time"
>
<view class="num">
{{ countDownHour }}
</view>
<view class="colon">
:
</view>
<view class="num">
{{ countDownInfo.min }}
</view>
<view class="colon">
:
</view>
<view class="num">
{{ countDownInfo.sec }}
</view>
</view>
<view
class="more text-arrow"
@click="onToSeckillProdPage"
>
{{ $t('index.more') }}
</view>
</view>
<view class="goods">
<block
v-for="(item, index) in seckillSpuList"
:key="index"
>
<view
class="item"
@tap="onToProdDetailPage(item.spuId, item.seckillId)"
>
<view class="img">
<img-show
:src="item.mainImgUrl"
img-mode="aspectFit"
/>
</view>
<view class="price">
<text class="symbol">
</text>
<text class="big">
{{ wxs.parsePrice(item.seckillPrice)[0] + '.' + wxs.parsePrice(item.seckillPrice)[1] }}
</text>
</view>
</view>
</block>
</view>
</view>
<!-- 分组商品列表 -->
<block
v-for="(item, index) in spuTagList"
:key="index"
>
<view
v-if="item.spuList.length > 0"
class="card-goods"
>
<view class="text-box">
<view class="text">
{{ item.title }}
</view>
<view class="big">
{{ $t('index.dailyRecommendation') }}
</view>
<view
class="more text-arrow"
@tap="onToTagListPage(item.title, item.id)"
>
{{ $t('index.recommendationTips') }}
</view>
</view>
<view class="goods">
<block
v-for="(prodItem, prodIndex) in item.spuList"
:key="prodIndex"
>
<view
v-if="prodItem && prodIndex < 3"
class="item"
>
<img-show
:src="prodItem.mainImgUrl"
img-mode="aspectFit"
@handle-tap="()=>onToProdDetail(prodItem.spuId)"
/>
</view>
</block>
</view>
</view>
</block>
<!-- 推荐 -->
<view class="recommend">
<scroll-view
:scroll-x="true"
class="category"
:scroll-left="categoryScrollLeft"
scroll-with-animation
@touchmove.stop
>
<view
v-for="(item, index) in categoryList"
:key="index"
class="category-item"
:class="{active: selectedIndex === index}"
@tap="onSwitchCategory(index, item.categoryId)"
>
{{ item.name }}
</view>
</scroll-view>
</view>
<view
v-if="categoryProdList.length>0"
class="prods"
>
<block
v-for="(item, index) in categoryProdList"
:key="index"
>
<view
class="item"
@tap="onToProdDetail(item.spuId)"
>
<view class="img">
<img-show
:src="item.mainImgUrl"
img-mode="aspectFit"
/>
</view>
<view class="text-box">
<view class="name">
{{ item.spuName }}
</view>
<view class="price-box">
<view class="price">
<view class="symbol">
</view>
<view class="big">
{{ wxs.parsePrice(item.priceFee)[0] }}
</view>
<view class="symbol">
.{{ wxs.parsePrice(item.priceFee)[1] }}
</view>
</view>
</view>
</view>
</view>
</block>
</view>
</view>
</z-paging>
<view v-if="renovationId">
<feature
ref="featureIndexRef"
:page-load="pageLoad"
:page-id="renovationId"
:shop-id="0"
:page-scorll-top="pageScorllTop"
@page-loaded="onPageLoaded"
/>
</view>
<privacy-pop
v-if="showPop"
@hide-pop="hidePop"
/>
<popup-ad
v-model="popupadShow"
:popup-info="popupInfo"
:shop-id="0"
/>
<!-- #ifdef APP-PLUS -->
<!-- 隐私/条款弹窗 -->
<!-- #endif -->
</view>
</template>
<script setup>
import * as cartCount from '@/utils/cart-count'
import { nextTick, reactive } from 'vue'
const wxs = number()
const paging = ref(null)
// 装修
const pageLoad = ref(false)
// 活动倒计时
const countDownInfo = ref({})
const countDownHour = computed(() => {
return countDownInfo.value.day === '00' ? countDownInfo.value.hour : countDownInfo.value.hour * 1 + countDownInfo.value.day * 24
})
const navigationBarIsShow = ref(false)
const res = uni.getSystemInfoSync()
if (res.system.toString().indexOf('Windows') < 0) {
navigationBarIsShow.value = true
}
const pageScorllTop = ref(0) // 页面滚动距离
onPageScroll((e) => {
pageScorllTop.value = e.scrollTop
})
const pagingTop = ref('0')
const showPop = ref(false)
onLoad(() => {
// #ifndef H5
const app = uni.getSystemInfoSync()
const topHight = app.statusBarHeight
pagingTop.value = topHight + 44 + 'px'
// #endif
// 获取装修数据
onGetFeatureIndex()
// #ifdef APP-PLUS
if (!uni.getStorageSync('cloudIsPrivacy') || uni.getStorageSync('cloudIsPrivacy') === -1) {
uni.hideTabBar()
showPop.value = true
}
// #endif
})
const renovationId = ref('') // 页面id
onLoad(() => {
// 获取首页秒杀数据
if (!renovationId.value) onGetIndexSeckillList()
// 获取分销开关设置
onGetDistInfo()
onGetPopupInfo()
})
onShow(() => {
uni.stopPullDownRefresh()
cartCount.getCartCount()
})
// 分类商品列表的请求参数
const searchListQuery = {
primaryCategoryId: 0,
sort: 1,
appDisplay: 1
}
// 分类商品列表返回的参数
const featureIndexRef = ref(null)
// 倒计时定时器
let countDownTimer = ''
/**
* 生命周期函数--监听页面隐藏
*/
onHide(() => {
// 清除定时器
clearTimeout(countDownTimer)
countDownTimer = null
})
const onScrolltolower = () => {
if (renovationId.value) {
featureIndexRef.value.getNextPage()
}
}
const hidePop = () => {
showPop.value = false
uni.showTabBar()
}
// 获取弹窗广告信息
const popupadShow = ref(false)
const popupInfo = ref({})
const onGetPopupInfo = async () => {
const info = await util.getPopupInfo({ pageType: 1, shopId: 0 })
if (info) {
popupInfo.value = info
popupadShow.value = true
}
}
/**
* 跳转商品详情
*/
const onToProdDetail = (spuId) => {
if (!spuId) {
return
}
uni.navigateTo({
url: '/pages/detail/detail?spuId=' + spuId
})
}
/**
* 查询页面数据方法合集
*/
const onQueryDataGroup = () => {
onGetIndexImgs()
onGetPlatformCategoryList()
// 获取分组商品列表
onGetSpuTagList()
}
// 查询的参数
const pageQuery = {
shopId: 0
}
const title = ref('')
const isLoad = ref(false)
const tabConfig = reactive({
background: '',
fontColor: '#FFFFFF',
iconColor: '#FFFFFF'
})
// 头部数据
const isBgImg = ref(false)
/**
* 获取装修页面数据
*/
const onGetFeatureIndex = () => {
http.request({
url: '/mall4cloud_admin/ua/shop_renovation/get_home',
data: {
renovationType: 2, // 装修类型 1.pc 2.移动端
shopId: 0
},
method: 'GET'
}).then((res) => {
if (res && res.homeStatus === 1 && res.renovationType === 2) {
renovationId.value = res.renovationId
pageQuery.shopId = res.shopId
} else {
renovationId.value = ''
onQueryDataGroup()
uni.setNavigationBarTitle({
title: uni.getStorageSync('cloudUniWebConfigData').titleContentCn
})
// 头部标题
title.value = uni.getStorageSync('cloudUniWebConfigData').titleContentCn
}
isLoad.value = true
if (!renovationId.value) {
tabConfig.background = '#F81A1A'
tabConfig.fontColor = '#FFFFFF'
tabConfig.iconColor = '#FFFFFF'
isBgImg.value = false
}
})
uni.hideNavigationBarLoading()
}
let bgImg = ''
let titleTextColor = '#ffffff'
// 页面加载回调
const onPageLoaded = (e) => {
uni.setNavigationBarTitle({
title: e.detail.title
})
bgImg = e.detail.bgImg
title.value = e.detail.title
// 头部标题
titleTextColor = e.detail.textColor || '#ffffff'
tabConfig.fontColor = titleTextColor || '#ffffff'
if (e.detail.bgType) {
if (bgImg) {
tabConfig.backgroundImage = `url(${bgImg})`
tabConfig.background = e.detail.bgColor || '#F81A1A'
tabConfig.fontColor = titleTextColor
tabConfig.iconColor = '#FFFFFF'
isBgImg.value = true
} else {
isBgImg.value = false
}
} else {
isBgImg.value = false
tabConfig.backgroundImage = `url(${bgImg})`
tabConfig.background = e.detail.bgColor || '#F81A1A'
tabConfig.fontColor = titleTextColor
tabConfig.iconColor = '#FFFFFF'
}
}
const indexImgs = ref([]) // 轮播图
/**
* 轮播图
*/
const onGetIndexImgs = () => {
const params = {
url: '/mall4cloud_admin/ua/index_img/list',
method: 'GET',
data: {
shopId: pageQuery.shopId,
imgType: 0
}
}
http.request(params).then((res) => {
// imgType 图片类型 0:移动端 1:pc
indexImgs.value = res.filter(imgItem => {
return !imgItem.imgType
})
})
}
const seckillSpuList = ref([])
const nextTime = ref(null)
/**
* 获取首页秒杀数据
*/
const onGetIndexSeckillList = () => {
const params = {
url: '/mall4cloud_seckill/ua/seckill/page',
method: 'GET',
data: {
pageNum: 1,
pageSize: 10
}
}
http.request(params).then((res) => {
if (res.seckillSpuList && res.seckillSpuList.length) {
seckillSpuList.value = res.seckillSpuList.slice(0, 3)
nextTime.value = Math.abs(res.nextTime) || 0
if (res.nextTime) {
if (countDownTimer) {
clearTimeout(countDownTimer)
countDownTimer = null
// 启动倒计时
onCountingDown()
} else {
// 启动倒计时
onCountingDown()
}
}
} else {
seckillSpuList.value = []
countDownInfo.value = {}
}
})
}
/**
* 跳转到秒杀商品详情页
*/
const onToProdDetailPage = (spuId, seckillId) => {
uni.navigateTo({
url: `/pages/detail/detail?spuId=${spuId}&seckillId=${seckillId}`
})
}
/**
* 倒计时
*/
const onCountingDown = () => {
countDownInfo.value = util.countDown(nextTime.value--)
countDownTimer = setTimeout(onCountingDown, 1000)
}
const categoryList = ref([]) // 分类列表
const selectedIndex = ref(0) // 选中的分类项(默认为第一个分类)
let selectedCategoryId = 0
/**
* 获取平台分类
*/
const onGetPlatformCategoryList = () => {
const params = {
url: '/mall4cloud_product/ua/category/category_list',
method: 'GET',
data: {
shopId: 0,
parentId: 0
}
}
http.request(params).then((res) => {
categoryList.value = res.filter(item => item.status === 1)
selectedIndex.value = 0 // 初始化选中项
searchListQuery.primaryCategoryId = selectedCategoryId = res[0].categoryId // 默认选中分类第一项
paging.value.reload()
onGetCategoryScrollWidth()
})
}
// 分类滚动的总宽度
let contentScrollW = 0
/**
* 获取分类栏 scroll-view 滚动相关宽度
*/
const onGetCategoryScrollWidth = () => {
nextTick(() => {
const query = uni.createSelectorQuery()
query.select('.category').boundingClientRect(data => {
// scroll-view 的宽度
contentScrollW = data.width
}).exec()
query.selectAll('.category-item').boundingClientRect(data => {
const dataLen = data.length
for (let i = 0; i < dataLen; i++) {
// scroll-view 各个分类项目的距离左边栏的距离
categoryList.value[i].left = data[i].left
// scroll-view 各个分类项目的宽度
categoryList.value[i].width = data[i].width
}
}).exec()
})
}
// 分类滚动栏横向滚动距离
const categoryScrollLeft = ref(0)
/**
* 切换分类
*/
const onSwitchCategory = (index, categoryId) => {
selectedCategoryId = categoryId
if (selectedIndex.value === index) {
return
}
selectedIndex.value = index
searchListQuery.primaryCategoryId = selectedCategoryId
// scorll-view 实现居中显示的滚动位置(偏移值): 当前点击子元素距离左边栏的距离 - scroll-view 宽度的一半 + 当前点击子元素一半的宽度 - 父盒子边距(如有)
categoryScrollLeft.value = categoryList.value[index].left - contentScrollW / 2 + categoryList.value[index].width / 2 - 15
paging.value.reload()
}
const spuTagList = ref([])
/**
* 获取分组商品
*/
const onGetSpuTagList = () => {
const params = {
url: '/mall4cloud_product/ua/spu_tag/list',
method: 'GET',
data: {
shopId: 0,
spuNum: 3
}
}
http.request(params).then((res) => {
spuTagList.value = res
})
}
const categoryProdList = ref([]) // 分类的商品列
/**
* 获取商品列表
*/
const onGetSearchList = (pageNum, pageSize) => {
if (!renovationId.value) {
const params = {
url: '/mall4cloud_search/ua/search/page',
method: 'GET',
data: {
pageNum,
pageSize,
...searchListQuery
}
}
http.request(params).then((res) => {
// 将请求结果通过complete传给z-paging处理同时也代表请求结束这一行必须调用
paging.value.complete(res.list[0].spus)
})
} else {
onGetFeatureIndex()
if (!renovationId.value) onGetIndexSeckillList()
setTimeout(() => {
paging.value.endRefresh()
paging.value.complete([])
}, 100)
}
}
// 列表刷新时,同时刷新其它数据
const onRefresh = () => {
onQueryDataGroup()
}
// 跳转搜索页
const onToSrearch = () => {
uni.navigateTo({
url: '/pages/search-page/search-page'
})
}
// 跳转新品推荐
const onToNewProds = () => {
uni.navigateTo({
url: '/package-activities/pages/new-prods/new-prods'
})
}
// 跳转打折特惠
const onToDiscountList = () => {
uni.navigateTo({
url: '/package-activities/pages/discount-list/discount-list'
})
}
// 跳转优惠团购
const onToGroupProds = () => {
uni.navigateTo({
url: '/package-activities/pages/group-prods/group-prods'
})
}
// 跳转限时秒杀
const onToSeckillProds = () => {
uni.navigateTo({
url: '/package-activities/pages/seckill-prods/seckill-prods'
})
}
// 跳转领券中心
const onToCouponCenter = () => {
uni.navigateTo({
url: '/package-activities/pages/coupon-center/coupon-center'
})
}
// 鉴权跳转
const onAuthRouteTo = (url) => {
util.checkAuthInfo(async () => {
if (url === 'distribution-center') {
// 获取分销开关设置
const distributionSwitch = await onGetDistInfo()
if (distributionSwitch) {
onToDistributionCenter()
} else {
uni.showToast({
title: '分销功能已关闭,请咨询平台',
icon: 'none'
})
}
} else {
uni.navigateTo({
url: `/package-activities/pages/${url}/${url}`
})
}
})
}
// 跳转到分组列表页
const onToTagListPage = (title, id) => {
uni.navigateTo({
url: `/pages/tag-list/tag-list?tTitle=${title}&tId=${id}`
})
}
// 跳转到秒杀专区页
const onToSeckillProdPage = () => {
uni.navigateTo({
url: '/package-activities/pages/seckill-prods/seckill-prods'
})
}
// 跳转分销中心
const onToDistributionCenter = () => {
const params = {
url: '/mall4cloud_marketing/distribution_user/distribution_user_info',
method: 'GET',
data: {}
}
http.request(params).then((res) => {
if (res && res.state === 1) {
uni.setStorageSync('cloudDistributionUserId', res.distributionUserId)
uni.setStorageSync('cloudDistInfo', res)
uni.navigateTo({
url: '/package-activities/pages/distribution-center/distribution-center'
})
} else if (res && res.state === 0) {
uni.showToast({
title: $t('distribution.applicationReview'),
icon: 'none'
})
} else if (res && res.state === 3) {
uni.showModal({
title: '',
content: $t('distribution.applicationFailed'),
cancelText: $t('cancel'),
confirmText: $t('confirm'),
confirmColor: '#eb2444',
success (res2) {
if (res2.confirm) {
if (res.recruitState) {
uni.navigateTo({
url: '/package-activities/pages/apply-distribution/apply-distribution'
})
} else {
uni.navigateTo({
url: '/package-activities/pages/apply-distCon/apply-distCon'
})
}
}
}
})
} else if (res && res.state === -1) {
uni.showModal({
title: '',
content: $t('distribution.distributorBanned'),
confirmColor: '#eb2444',
cancelText: $t('cancel'),
confirmText: $t('confirm'),
showCancel: false
})
} else if (res && res.state === 2) {
uni.showModal({
title: '',
content: $t('distribution.distributorCleared'),
confirmColor: '#eb2444',
cancelText: $t('cancel'),
confirmText: $t('confirm'),
showCancel: false
})
} else {
if (res.recruitState) {
uni.navigateTo({
url: '/package-activities/pages/apply-distribution/apply-distribution'
})
} else {
uni.navigateTo({
url: '/package-activities/pages/apply-distCon/apply-distCon'
})
}
}
})
}
// 查询分销开关是否开启
const onGetDistInfo = () => {
return new Promise((resolve) => {
http.request({
url: '/mall4cloud_marketing/distribution_set/info',
method: 'GET',
dontShowLogin: true
}).then((res) => {
resolve(res.distributionSwitch)
}).catch(() => {
resolve(false)
})
})
}
/**
* 用户点击右上角分享(勿删)
*/
onShareAppMessage(() => {})
</script>
<style lang="scss" scoped>
@use "./index.scss";
</style>