Files
wagoo-douy3/src/pages/client/home/index.vue
2026-03-06 13:41:22 +08:00

745 lines
17 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>
<view class="home-page">
<scroll-view class="homeContainer" scroll-y :show-scrollbar="false" :enhanced="true">
<view class="swiperWrapper">
<swiper indicator-dots="true" autoplay="true" interval="3000" duration="500" circular="true"
class="swiper">
<swiper-item v-for="(item, index) in swiperDataList" :key="index">
<image class="swiper-img" :src="item.image || `${imgPrefix}home-ground.png`" mode="aspectFill" />
</swiper-item>
</swiper>
<view class="userInfoWrapper">
<view class="userInfo" @click="handleUserInfo">
<image class="avatar-img"
:src="userInfo.avatar ? userInfo.avatar : `${imgPrefix}defaultHeadImg.png`"
mode="aspectFill" />
<view class="userContent">
<view class="userName">
{{ userInfo.username ? userInfo.username : "嗨,你好呀" }}
</view>
<view class="userTips" v-if="!userInfo.userID">
登陆享受更多精彩内容
</view>
<view v-else class="user-membership-row">
<view class="vipWrapper">
<image class="lableImg" :src="`${imgPrefix}home-vipLabel.png`" />
v{{ userInfo.vipLevel ? userInfo.vipLevel : 0 }}会员
</view>
<view
v-if="userInfo.membershipTier && userInfo.membershipTier > 0"
class="membership-tier-badge"
:class="{
'membership-tier-1': userInfo.membershipTier === 1,
'membership-tier-2': userInfo.membershipTier === 2,
'membership-tier-3': userInfo.membershipTier === 3
}"
>
<text class="membership-tier-text">{{ getMembershipTierText(userInfo.membershipTier) }}</text>
</view>
</view>
</view>
</view>
<view class="loginBtn" @click="toLogin" v-if="!userInfo.userID">
注册/登陆
</view>
<view class="loginBtn flexClass" v-else @click="toCouponList">
<view class="couponText">{{ userInfo.couponCount ? userInfo.couponCount : 0 }}张优惠券</view>
<image :src="`${imgPrefix}home-rightWhite Arrow.png`" class="discountCoupon" />
</view>
</view>
<view class="shadowBackground" />
</view>
<view class="menuBody">
<view class="firstMenu">
<view class="itemWrapper" @click="toReservation">
<text class="titlWrapper" style="transform: translateY(-4rpx);">你们在哪我们去哪</text>
<view class="content">预约洗护</view>
<view class="tips">随时随地上车洗澡</view>
<view class="itemImg">
<image class="menu-img-lg" :src="`${imgPrefix}home-menuBath.png`" mode="aspectFill" />
</view>
</view>
<view class="line" />
<view class="itemWrapper" @click="toDogTraining">
<text class="titlWrapper">狗狗训练</text>
<view class="content">上门服务</view>
<view class="tips">上门训犬寄养喂猫遛狗</view>
<view class="itemImg">
<image class="menu-img-sm" :src="`${imgPrefix}home-dogTraining.png`" mode="aspectFill" />
</view>
</view>
</view>
<view class="secondMenu">
<scroll-view class="scrollWrapper" :scroll-x="secondMenuItemList.length > 4" enable-flex
:scroll-with-animation="false"
@scroll="onSecondMenuScroll">
<view class="secondMenuInner" :class="{ 'no-scroll': secondMenuItemList.length <= 4 }">
<view class="itemWrapper" v-for="(item, i) in secondMenuItemList" :key="i"
@click="handleNav(item)">
<view class="imgWrapper">
<image class="second-icon" :src="item.img" mode="aspectFill" />
</view>
<view class="itemTitle">
{{ item.title }}
</view>
<view class="itemTips">
{{ item.tips }}
</view>
</view>
</view>
</scroll-view>
<view class="custom-indicator" v-if="secondMenuItemList.length > 4">
<view class="itemLine" :class="{ active: secondMenuIndicatorIndex === 0 }"></view>
<view class="itemLine" :class="{ active: secondMenuIndicatorIndex === 1 }"></view>
</view>
</view>
<view class="thirdMenu">
<view class="itemWrapper" @click="toPublicBenefit">
<view>
<view class="title">公益助力</view>
<view class="tips">帮我找个家</view>
</view>
<image class="third-icon" :src="`${imgPrefix}home-publicBenefit.png`" mode="aspectFill" />
</view>
<view class="itemWrapper" @click="toJoin">
<view>
<view class="title">加盟咨询</view>
<view class="tips">立即加盟咨询</view>
</view>
<image class="third-icon" :src="`${imgPrefix}home-joinIn.png`" mode="aspectFill" />
</view>
</view>
<view class="serviceMenu" @click="jumpToWeChat">
<view class="left">
<image class="service-icon" :src="`${imgPrefix}supportStaff.png`" mode="aspectFill" />
<view class="content">
<view class="title">在线客服</view>
<view class="tips">遇到什么问题您尽管说哦~</view>
</view>
</view>
<view>
<image :src="`${imgPrefix}right-arrow.png`" class="rightArrow" />
</view>
</view>
</view>
</scroll-view>
</view>
</template>
<script>
import {
imgPrefix,
jumpToWeChat,
showLoginConfirmModal
} from "@/utils/common";
import {
userWllet
} from "../../../api/login";
export default {
name: "HomePage",
data() {
return {
imgPrefix,
couponCount: 1,
swiperDataList: [
{
image: `${imgPrefix}banner1.png`
},
{
image: `${imgPrefix}bannerShare.png`
}
],
secondMenuItemList: [{
title: "邀请有礼",
tips: "邀请好友得好礼",
img: `${imgPrefix}home-invite .png`,
naviUrl: "/pages/client/recharge/membership-code",
params: {
yaoqing_code: (userInfo) => userInfo?.userID || '',
member_id: (userInfo) => userInfo?.userID || ''
}
},
{
title: "领券中心",
tips: "领取更多优惠券",
img: `${imgPrefix}home-getCoupon.png`,
naviUrl: "/pages/client/coupon/get-list",
},
{
title: "充值有礼",
tips: "最高返赠1650元",
img: `${imgPrefix}home-recharge.png`,
naviUrl: '/pages/client/recharge/index',
params: {
user_id: (userInfo) => userInfo?.userID || '',
}
},
{
title: "开通会员",
tips: "服务享8折",
img: `${imgPrefix}home-openVip.png`,
naviUrl: "/pages/richText/member-interests"
},
],
walletInfo: {}, // 钱包信息
secondMenuIndicatorIndex: 0, // 第二排菜单指示器当前页 0=第一页 1=第二页
};
},
computed: {
couponCountText() {
return `${this.couponCount}张优惠券`;
},
userInfo() {
return this.$store.state?.user?.userInfo || {};
}
},
created() {
this.buyService()
},
methods: {
toLogin() {
uni.navigateTo({
url: "/pages/client/auth/index",
});
},
// 统一的 token 检查方法,未登录时弹窗让用户自主选择
async checkTokenAndExecute(callback) {
const token = uni.getStorageSync('token');
if (!token) {
const willLogin = await showLoginConfirmModal();
if (!willLogin) return;
// 用户选择去登录,跳转后不执行 callback
return;
}
if (typeof callback === 'function') {
callback();
}
},
async handleUserInfo() {
const token = uni.getStorageSync('token');
if (token) {
uni.navigateTo({
url: "/pages/client/mine/userInfo",
});
} else {
await showLoginConfirmModal();
}
},
toReservation() {
this.checkTokenAndExecute(() => {
// 直接调用父组件的 handleTabChange 方法来切换 TabBar
// 这是最可靠的方式,因为 home 组件是 index 组件的子组件
if (this.$parent && typeof this.$parent.handleTabChange === 'function') {
this.$parent.handleTabChange(["reservationPage"]);
}
});
},
toDogTraining() {
uni.navigateTo({
url: '/pageHome/service/index'
});
},
// 我的钱包
buyService() {
const value = JSON.parse(uni.getStorageSync('vuex'));
console.log(value, '??')
this.userId = value.user.userInfo.userID
this.nick_name = value.user.userInfo.username
// this.yaoqing_code = value.user.userInfo.yaoqing_code
// console.log(value,'--')
userWllet(value.user.userInfo.userID).then((res) => {
uni.setStorage({ //存入Storage
key: 'userInfo',
data: { //存的数据(可以是多条)
'user_id': res.data.user_id,
'wallet_id': res.data.id
}
})
});
},
onSecondMenuScroll(e) {
const scrollLeft = e.detail.scrollLeft || 0;
const sysInfo = uni.getSystemInfoSync();
const windowWidth = sysInfo.windowWidth || 375;
// 5项一屏4个可滚动范围=1项宽≈windowWidth/4过半即第二页
const maxScroll = windowWidth / 4;
this.secondMenuIndicatorIndex = scrollLeft >= maxScroll * 0.5 ? 1 : 0;
},
getMembershipTierText(tier) {
const tierMap = {
1: '黄金会员',
2: '白金会员',
3: '黑金会员'
};
return tierMap[tier] || '';
},
handleNav(item) {
this.checkTokenAndExecute(() => {
// 健康顾问暂未开放
if (item.title === '健康顾问') {
uni.showToast({
title: '暂未开放',
icon: 'none',
duration: 2000
});
return;
}
if (item.naviUrl) {
let url = item.naviUrl;
// 如果有 params 参数,进行拼接
if (item.params && typeof item.params === 'object') {
const params = [];
for (const key in item.params) {
const value = item.params[key];
// 支持函数形式,动态获取值(可接收 userInfo 和 walletInfo
const paramValue = typeof value === 'function'
? value(this.userInfo, this.walletInfo)
: value;
if (paramValue !== undefined && paramValue !== null) {
params.push(`${key}=${encodeURIComponent(paramValue)}`);
}
}
if (params.length > 0) {
url += (url.includes('?') ? '&' : '?') + params.join('&');
}
}
uni.navigateTo({
url: url,
});
}
});
},
toCouponList() { // 前往优惠券
uni.navigateTo({
url: "/pages/client/coupon/list",
});
},
toPublicBenefit() {
this.checkTokenAndExecute(() => {
uni.navigateTo({
url: '/pageHome/welfare/index'
});
});
},
toJoin() {
this.checkTokenAndExecute(() => {
uni.navigateTo({
url: '/pageHome/franchise/index'
});
});
},
jumpToWeChat() {
this.checkTokenAndExecute(() => {
jumpToWeChat();
});
},
},
};
</script>
<style lang="scss" scoped>
.home-page {
height: 100vh;
background-color: #ffecf3;
}
.homeContainer {
height: calc(100vh - #{$app_tabbar_height + 36});
background-color: #ffecf3;
overflow: scroll;
.avatar-img {
width: 92rpx;
height: 92rpx;
border-radius: 50%;
}
.menu-img-lg {
width: 160rpx;
height: 160rpx;
}
.menu-img-sm {
width: 120rpx;
height: 164rpx;
}
.second-icon,
.third-icon,
.service-icon {
width: 96rpx;
height: 96rpx;
}
.service-icon {
width: 66rpx;
height: 66rpx;
}
.swiperWrapper {
position: relative;
.swiper {
height: 552rpx;
width: 100%;
.swiper-img {
width: 100%;
height: 100%;
}
}
.userInfoWrapper {
display: flex;
justify-content: space-between;
align-items: center;
background-color: #fff;
position: absolute;
bottom: -5%;
left: 50%;
transform: translateX(-50%);
width: calc(100% - 40rpx);
padding: 32rpx 28rpx;
box-sizing: border-box;
border-radius: 16rpx;
z-index: 10;
.userInfo {
display: flex;
align-items: center;
.userContent {
margin-left: 16rpx;
.userName {
font-weight: 500;
font-size: 28rpx;
}
.userTips {
font-size: 20rpx;
color: #808080;
margin-top: 8rpx;
}
.user-membership-row {
display: flex;
align-items: center;
gap: 16rpx;
margin-top: 16rpx;
flex-wrap: wrap;
}
.vipWrapper {
display: inline-flex;
align-items: center;
background-color: #ff19a0;
padding: 4rpx 8rpx;
border-radius: 50px;
font-size: 20rpx;
color: #fff;
text-align: center;
.lableImg {
width: 24rpx;
height: 20rpx;
margin-right: 4rpx;
}
}
.membership-tier-badge {
display: inline-flex;
align-items: center;
justify-content: center;
padding: 4rpx 8rpx;
border-radius: 50px;
font-size: 20rpx;
font-weight: 500;
}
.membership-tier-1 {
background-color: #EDCA69;
}
.membership-tier-1 .membership-tier-text {
color: #754410;
font-size: 20rpx;
}
.membership-tier-2 {
background-color: #CECECE;
}
.membership-tier-2 .membership-tier-text {
color: #3D3D3D;
font-size: 20rpx;
}
.membership-tier-3 {
background-color: #321500;
}
.membership-tier-3 .membership-tier-text {
color: #CBAD78;
font-size: 20rpx;
}
}
}
.loginBtn {
background-color: #ff19a0;
border-radius: 218px;
color: #fff;
font-size: 23rpx;
padding: 16rpx 24rpx;
display: flex;
align-items: center;
.couponText {
color: #fff;
font-size: 24rpx;
}
.discountCoupon {
width: 11rpx;
height: 18rpx;
margin-left: 8rpx;
}
}
}
.shadowBackground {
position: absolute;
left: 0;
bottom: -20rpx;
z-index: 8;
width: 100%;
height: 72rpx;
background: linear-gradient(180deg, rgba(255, 255, 255, 0) 0%, #ffecf3 64%);
}
}
.menuBody {
margin-top: 32rpx;
padding: 20rpx;
.firstMenu {
display: flex;
background-color: #fff;
border-radius: 16rpx;
justify-content: space-between;
align-items: center;
box-shadow: 0 6rpx 24rpx rgba(0, 0, 0, 0.04);
.itemWrapper {
flex: 1;
text-align: center;
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
position: relative;
}
.line {
width: 2rpx;
background-color: #e8e8e8;
height: 280rpx;
}
.titlWrapper {
background-color: #ff19a0;
color: #fff;
font-size: 20rpx;
padding: 8rpx 16rpx;
border-radius: 0px 0px 12rpx 12rpx;
display: inline-block;
}
.content {
font-size: 36rpx;
font-weight: 500;
margin-top: 24rpx;
}
.tips {
font-size: 20rpx;
margin-top: 16rpx;
color: #808080;
}
.itemImg {
margin: auto;
margin-top: 48rpx;
margin-bottom: 32rpx;
}
}
.secondMenu {
margin-top: 20rpx;
background-color: #fff;
border-radius: 16rpx;
overflow: hidden;
box-shadow: 0 6rpx 24rpx rgba(0, 0, 0, 0.04);
.scrollWrapper {
width: 100%;
height: auto; /* 由内容撑开;小程序 scroll-view 横向滚动时默认可能不随内容计算高度 */
min-height: 220rpx; /* 兜底:约等于 padding-top + 图标 + 标题 + 副标题 */
display: flex;
align-items: flex-start; /* 不拉伸子项,高度由内容决定 */
white-space: nowrap;
padding-top: 24rpx;
padding-bottom: 24rpx;
box-sizing: border-box;
.secondMenuInner {
display: flex;
flex-wrap: nowrap;
flex-shrink: 0;
width: 937.5rpx; /* 5 * 187.5 一屏4个共5项 */
&.no-scroll {
width: 100%; /* 四项时一屏排满,不滚动 */
.itemWrapper {
flex: 1;
width: 0; /* 均分宽度,避免被 menuBody padding 裁切 */
min-width: 0;
}
}
}
.itemWrapper {
width: 187.5rpx; /* 750/4 一屏固定4个 */
flex-shrink: 0;
text-align: center;
.imgWrapper {
display: inline-block;
margin: auto;
}
.itemTitle {
font-size: 24rpx;
font-weight: 500;
margin-top: 8rpx;
}
.itemTips {
font-size: 20rpx;
color: #808080;
margin-top: 8rpx;
}
}
}
.custom-indicator {
display: flex;
margin-top: 12rpx;
justify-content: center;
margin-bottom: 20rpx;
gap: 12rpx;
.itemLine {
background-color: #ededed;
width: 20rpx;
height: 6rpx;
border-radius: 170px;
&.active {
background-color: #ff19a0;
}
}
}
}
.thirdMenu {
display: flex;
gap: 16rpx;
margin-top: 20rpx;
.itemWrapper {
flex: 1;
background-color: #fff;
border-radius: 16rpx;
display: flex;
justify-content: space-between;
align-items: center;
padding: 20rpx 24rpx;
box-shadow: 0 6rpx 24rpx rgba(0, 0, 0, 0.04);
.title {
font-size: 24rpx;
font-weight: 500;
}
.tips {
font-size: 20rpx;
color: #808080;
margin-top: 16rpx;
}
}
}
.serviceMenu {
display: flex;
background-color: #fff;
margin-top: 20rpx;
padding: 20rpx 24rpx;
align-items: center;
justify-content: space-between;
border-radius: 16rpx;
box-shadow: 0 6rpx 24rpx rgba(0, 0, 0, 0.04);
.left {
display: flex;
align-items: center;
}
.rightArrow {
width: 11rpx;
height: 18rpx;
}
.content {
margin-left: 16rpx;
.title {
font-size: 28rpx;
font-weight: 500;
}
.tips {
font-size: 22rpx;
margin-top: 8rpx;
color: #808080;
}
}
}
}
}
.flexClass {
display: flex;
align-items: center;
justify-content: center;
}
</style>