Files
cheflinkuser/src/pages/home/components/tabbar-mine/tabbar-mine.vue
T
2026-06-18 21:04:03 +08:00

532 lines
13 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.
<script setup lang="ts">
import * as R from "ramda";
import dayjs from 'dayjs'
import useEventEmit from "@/hooks/useEventEmit";
import { EventEnum } from "@/constant/enums";
import { useConfigStore, useUserStore } from "@/store";
import MineSkeleton from "./components/mine-skeleton.vue";
const configStore = useConfigStore();
const userStore = useUserStore();
import { Agreement } from "@/constant/enums";
import Config from '@/config/index'
import {formatTimestampWithMonthName, loadWeixinService} from "@/utils/utils";
const { t } = useI18n();
const loading = ref(true);
const emits = defineEmits<{
chooseLanguage: [];
logOut: [];
inviteUser: [];
customerService: [];
changeOrder: [];
}>();
interface Tab {
iconPath: string;
path: string;
text: string;
code: string;
isLogin: boolean;
}
const tabBarList = ref<Tab[]>([
{
iconPath: "/static/images/chef/100201.png",
path: "/pages-user/pages/coupon/index",
text: t("pages.mine.discount"),
code: "discount",
isLogin: true,
},
{
iconPath: "/static/images/chef/100200.png",
path: "/pages-user/pages/faqs/index",
text: t("pages.mine.help"),
code: "help",
isLogin: true,
},
{
iconPath: "/static/images/chef/100199.png",
path: "/pages-user/pages/coupon/index",
text: t("pages.mine.inviteFriends"),
code: "inviteFriends",
isLogin: true,
},
{
iconPath: "/static/images/100203.png",
path: "/pages-user/pages/invited-person/index",
text: t('pages.mine.my-invitations'),
code: "myInvitations",
isLogin: true,
},
{
iconPath: "/static/images/chef/100198.png",
path: "/pages-user/pages/store-settle-in/index",
text: t("pages.mine.storeSettled"),
code: "storeSettled",
isLogin: false,
},
{
iconPath: "/static/images/chef/100197.png",
path: "/pages-user/pages/coupon/index",
text: t("pages.mine.support"),
code: "support",
isLogin: true,
},
{
iconPath: "/static/images/chef/100196.png",
path: "/pages/agreement/index?code=" + Agreement.CHEF_PLATFORM_AGREEMENT,
text: t("pages.mine.platformAgreement"),
code: "platformAgreement",
isLogin: true,
},
{
iconPath: "/static/images/chef/100195.png",
path: "/pages/agreement/index?code=" + Agreement.PRIVACY_POLICY,
text: t("pages.mine.privacyPolicy"),
code: "privacyPolicy",
isLogin: true,
},
{
iconPath: "/static/images/chef/100194.png",
path: "/pages-user/pages/complaints/index",
text: t("pages.mine.complaintsAndSuggestions"),
code: "complaintsAndSuggestions",
isLogin: true,
},
{
iconPath: "/static/images/chef/100192.png",
path: "/pages-user/pages/setting/index",
text: t("pages.mine.set"),
code: "set",
isLogin: true,
},
]);
function navigateTo(url: string) {
if(userStore.checkLogin()) {
uni.navigateTo({
url,
});
}
}
function checkNeedLogin(isNeedLogin: boolean) {
return isNeedLogin ? userStore.checkLogin() : true;
}
// 用户会员状态是否已开通
const isUserMember = computed(()=> {
if(!userStore.userInfo.userMembershipVo) return false
if(userStore.userInfo.userMembershipVo && userStore.userInfo.userMembershipVo.expireTime ){
return dayjs().isBefore(dayjs(Number(userStore.userInfo.userMembershipVo.expireTime)))
} else {
return false
}
})
function handleTabClick(item: Tab) {
switch (item.code) {
case "inviteFriends": {
R.both(
checkNeedLogin,
R.pipe(() => emits("inviteUser"), R.T)
)(item.isLogin);
break;
}
case "support": {
// emits("customerService");
loadWeixinService()
break;
}
default: {
// navigateTo(item.path)
if (item.code === "set") {
navigateTo("/pages-user/pages/setting/index");
} else {
R.both(
checkNeedLogin,
R.pipe(() => navigateTo(item.path), R.T)
)(item.isLogin);
}
break;
}
}
}
function changeOrderFn() {
emits("changeOrder");
}
async function initData() {
loading.value = false;
// setTimeout(() => {
// loading.value = false;
// }, 300);
userStore.getUserInfo()
}
async function getPlatformDefaultStoreInfo() {}
defineExpose({
initData,
init: getPlatformDefaultStoreInfo,
});
</script>
<template>
<view
class="view-bg"
:style="[
{
height: configStore.windowHeight + 'px',
},
]"
>
<z-paging ref="paging">
<template #top>
<status-bar />
</template>
<view
v-show="loading"
class="animate-in fade-in animate-ease-out animate-duration-300"
>
<mine-skeleton />
</view>
<view
v-show="!loading"
class="animate-in fade-in animate-ease-in animate-duration-300"
>
<view class="mine-page ">
<!-- 顶部用户信息 -->
<view class="mine-header" @click="navigateTo('/pages-user/pages/user-info/index')">
<view class="mine-header__left">
<text class="mine-header__name">
{{
userStore.isLogin
? ([userStore.userInfo.firstName, userStore.userInfo.surname].filter(Boolean).join(' ') ||
t('common.unknownUser'))
: t('common.pleaseLogin')
}}
</text>
<view class="mine-header__sub" v-if="userStore.isLogin">
<text class="mine-header__member">{{ Config.appName }} {{ t('pages.mine.member') }}</text>
<view class="mine-header__badge" v-if="!isUserMember">
{{ t('pages.mine.openMember') }}
</view>
<text class="mine-header__chev"></text>
</view>
<text class="mine-header__date" v-if="isUserMember && userStore.userInfo.userMembershipVo?.expireTime">
{{ t('common.expireTime') }}{{ formatTimestampWithMonthName(userStore.userInfo.userMembershipVo?.expireTime) }}
</text>
</view>
<image
v-if="userStore.isLogin"
:src="userStore.userInfo.avatar"
class="mine-header__avatar"
mode="aspectFill"
/>
<image v-else class="mine-header__avatar" mode="aspectFill" src="@img/chef/default_avatar.png" />
</view>
<!-- 三项数据胶囊卡 -->
<view class="mine-stats">
<view class="mine-stats__item" @click="navigateTo('/pages-user/pages/collection/index')">
<text class="mine-stats__num">{{ userStore.userInfo.collectNum || 0 }}</text>
<text class="mine-stats__label">{{ t('pages.mine.collection') }}</text>
</view>
<view class="mine-stats__divider" />
<view class="mine-stats__item" @click="navigateTo('/pages-user/pages/balance/index')">
<text class="mine-stats__num">{{ userStore.userInfo?.balance || 0 }}</text>
<text class="mine-stats__label">{{ t('pages.mine.wallet') }}</text>
</view>
<view class="mine-stats__divider" />
<view class="mine-stats__item" @click="changeOrderFn">
<text class="mine-stats__num">{{ userStore.userInfo.orderNum || 0 }}</text>
<text class="mine-stats__label">{{ t('pages.mine.order') }}</text>
</view>
</view>
<template v-if="isUserMember">
<view @click="navigateTo('/pages-user/pages/member/index')" class="member-banner">
<image src="@img/chef/100203.png" class="member-banner__bg"></image>
<view class="member-banner__content">
<view class="member-banner__title">{{ Config.appName }}</view>
<view class="member-banner__sub">
{{ t('common.expireTime') }}{{ formatTimestampWithMonthName(userStore.userInfo.userMembershipVo?.expireTime) }}
</view>
</view>
</view>
</template>
<template v-else>
<view @click="navigateTo('/pages-user/pages/member/index')" class="member-banner">
<image src="@img/chef/100203.png" class="member-banner__bg"></image>
<view class="member-banner__content">
<view class="member-banner__title">
<template v-if="!userStore.userInfo.userMembershipVo">{{ t('pages.mine.member-title') }}</template>
<template v-else>{{ t('pages.mine.join') }} {{ Config.appName }}</template>
</view>
<view class="member-banner__sub">{{ t('pages.mine.member-desc') }}</view>
</view>
</view>
</template>
<view style="height: 30rpx;background-color: #f5f5f5;" />
<!-- 菜单列表卡片 -->
<view class="mine-menu">
<view
class="mine-menu__row"
v-for="(item, index) in tabBarList"
:key="item.code"
:class="[index === tabBarList.length - 1 ? 'mine-menu__row--last' : '']"
@click="handleTabClick(item)"
>
<view class="mine-menu__left">
<image class="mine-menu__icon" :src="item.iconPath"></image>
<text class="mine-menu__text">{{ item.text }}</text>
</view>
<image class="mine-menu__arrow" src="@img/chef/100202.png"></image>
</view>
</view>
<view class="mine-bottom-spacer" />
</view>
</view>
<template #bottom>
<view class="h-50px"></view>
<view :style="[configStore.iosSafeBottomPlaceholder]"></view>
</template>
</z-paging>
</view>
</template>
<style scoped lang="scss">
.view-bg {
background-color: #fff;
}
.mine-page {
padding-bottom: 20rpx;
}
.mine-header {
margin: 0 30rpx;
padding: 32rpx 0 28rpx;
display: flex;
align-items: center;
justify-content: space-between;
&__left {
min-width: 0;
flex: 1;
display: flex;
flex-direction: column;
justify-content: center;
}
&__name {
font-size: 38rpx;
line-height: 40rpx;
font-weight: 700;
color: #333;
letter-spacing: 0.04em;
max-width: 520rpx;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
&__sub {
margin-top: 12rpx;
display: inline-flex;
align-items: center;
gap: 10rpx;
color: #b68b3e;
font-size: 22rpx;
line-height: 22rpx;
font-weight: 600;
}
&__member {
color: #b68b3e;
}
&__badge {
padding: 8rpx 16rpx;
border-radius: 999rpx;
background: rgba(182, 139, 62, 0.12);
color: #b68b3e;
font-size: 20rpx;
line-height: 20rpx;
font-weight: 700;
letter-spacing: 0.02em;
}
&__chev {
transform: translateY(-1rpx);
color: #b68b3e;
font-size: 24rpx;
line-height: 22rpx;
}
&__date {
margin-top: 10rpx;
font-size: 22rpx;
line-height: 22rpx;
color: #999;
font-weight: 500;
}
&__avatar {
width: 90rpx;
height: 90rpx;
border-radius: 50%;
flex-shrink: 0;
background-color: #eee;
}
}
.mine-stats {
margin: 15rpx 35rpx;
// margin-top: 24rpx;
background: #fff;
border-radius: 36rpx;
height: 120rpx;
padding: 0 20rpx;
display: flex;
align-items: center;
box-shadow: 0 12rpx 30rpx rgba(10, 10, 10, 0.06);
&__item {
flex: 1;
min-width: 0;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 8rpx;
}
&__num {
font-size: 32rpx;
line-height: 30rpx;
font-weight: 700;
color: #333;
}
&__label {
font-size: 26rpx;
line-height: 28rpx;
color: #666;
font-weight: 700;
letter-spacing: 0.04em;
// font-weight: 500;
}
&__divider {
width: 1rpx;
height: 56rpx;
background: #eaeaea;
flex-shrink: 0;
}
}
.member-banner {
margin: 15rpx 30rpx;
height: 152rpx;
position: relative;
border-radius: 36rpx;
overflow: hidden;
&__bg {
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
}
&__content {
position: relative;
z-index: 1;
height: 100%;
padding: 22rpx 165rpx 22rpx 28rpx;
display: flex;
flex-direction: column;
justify-content: space-between;
}
&__title {
font-size: 32rpx;
line-height: 32rpx;
font-weight: 800;
color: #333;
}
&__sub {
font-size: 20rpx;
line-height: 20rpx;
font-weight: 600;
color: #935d04;
letter-spacing: 0.08em;
}
}
.mine-menu {
margin:20rpx 30rpx 30rpx;
background: #fff;
border-radius: 24rpx;
overflow: hidden;
&__row {
padding: 28rpx 24rpx;
display: flex;
align-items: center;
justify-content: space-between;
border-bottom: 1rpx solid #efefef;
}
&__row--last {
border-bottom: none;
}
&__left {
min-width: 0;
display: flex;
align-items: center;
gap: 18rpx;
}
&__icon {
width: 38rpx;
height: 38rpx;
flex-shrink: 0;
}
&__text {
font-size: 26rpx;
line-height: 26rpx;
color: #333;
// font-weight: 600;
letter-spacing: 0.04em;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
&__arrow {
width: 22rpx;
height: 30rpx;
flex-shrink: 0;
opacity: 0.9;
}
}
.mine-bottom-spacer {
height: 58rpx;
}
</style>