2025-04-13 23:40:58 +08:00

937 lines
24 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.

<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: '/tmerclub_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: '/tmerclub_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: '/tmerclub_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: '/tmerclub_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: '/tmerclub_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: '/tmerclub_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: '/tmerclub_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: '/tmerclub_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>