This commit is contained in:
2026-03-06 13:41:22 +08:00
commit f39c6a705f
394 changed files with 159599 additions and 0 deletions

View File

@ -0,0 +1,745 @@
<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>