移除前端中的会员卡和支付相关,新增微信一键登录

This commit is contained in:
2026-07-14 18:50:35 +08:00
parent 13b99428de
commit 6cc72ae0f7
26 changed files with 331 additions and 9159 deletions
@@ -1,398 +0,0 @@
<template>
<view class="scroll-container theme-light">
<view class="card-detail-page">
<view v-if="card" class="card-detail-page__body">
<view class="cd-hero">
<view class="cd-hero__card">
<view class="cd-hero__card-header">
<text class="cd-hero__card-name">{{ card.memberCardName || card.cardName }}</text>
<view class="cd-hero__card-tag" :class="'cd-hero__card-tag--' + getTagClass(card.memberCardType || card.cardType)">
<text class="cd-hero__card-tag-text">{{ getCardTypeName(card.memberCardType || card.cardType) }}</text>
</view>
</view>
<view class="cd-hero__card-body">
<view class="cd-hero__card-item" v-if="card.memberCardValidityDays || card.validityDays">
<text class="cd-hero__card-label">有效期</text>
<text class="cd-hero__card-value">{{ card.memberCardValidityDays || card.validityDays }}</text>
</view>
<view class="cd-hero__card-item" v-if="(card.memberCardType === 'COUNT_CARD' || card.cardType === 'COUNT') && (card.memberCardTotalTimes || card.totalTimes)">
<text class="cd-hero__card-label">总次数</text>
<text class="cd-hero__card-value">{{ card.memberCardTotalTimes || card.totalTimes }}</text>
</view>
<view class="cd-hero__card-item" v-if="(card.memberCardType === 'STORED_VALUE_CARD' || card.cardType === 'BALANCE') && (card.memberCardAmount || card.amount)">
<text class="cd-hero__card-label">储值金额</text>
<text class="cd-hero__card-value">{{ card.memberCardAmount || card.amount }}</text>
</view>
</view>
<view class="cd-hero__card-footer">
<text class="cd-hero__card-price">¥{{ card.memberCardPrice || card.price }}</text>
</view>
</view>
</view>
<view class="cd-section">
<text class="cd-section__title">会员卡权益</text>
<view class="cd-benefits">
<view class="cd-benefit">
<view class="cd-benefit__icon">💪</view>
<view class="cd-benefit__content">
<text class="cd-benefit__title">全场器械使用</text>
<text class="cd-benefit__desc">免费使用所有健身器械和设备</text>
</view>
</view>
<view class="cd-benefit">
<view class="cd-benefit__icon">🏋</view>
<view class="cd-benefit__content">
<text class="cd-benefit__title">免费团课</text>
<text class="cd-benefit__desc">预约参加各类团课次卡除外</text>
</view>
</view>
<view class="cd-benefit">
<view class="cd-benefit__icon">🧴</view>
<view class="cd-benefit__content">
<text class="cd-benefit__title">淋浴服务</text>
<text class="cd-benefit__desc">免费使用淋浴间和储物柜</text>
</view>
</view>
<view class="cd-benefit">
<view class="cd-benefit__icon">📱</view>
<view class="cd-benefit__content">
<text class="cd-benefit__title">在线预约</text>
<text class="cd-benefit__desc">APP在线预约课程和签到</text>
</view>
</view>
</view>
</view>
<view class="cd-section" v-if="card.extraConfig">
<text class="cd-section__title">详细解读</text>
<view class="cd-desc">
<text class="cd-desc__text">{{ parseExtraConfig(card.extraConfig) }}</text>
</view>
</view>
<view class="cd-section">
<text class="cd-section__title">购买须知</text>
<view class="cd-notes">
<text class="cd-notes__item">1. 会员卡一经购买非特殊情况不予退款</text>
<text class="cd-notes__item">2. 请在有效期内使用过期自动失效</text>
<text class="cd-notes__item">3. 会员卡仅限本人使用不得转借他人</text>
<text class="cd-notes__item">4. 支付订单有效时间为15分钟超时自动取消</text>
</view>
</view>
<view class="cd-bottom">
<view class="cd-bottom__total">
<text class="cd-bottom__total-label">价格</text>
<text class="cd-bottom__total-price">¥{{ card.memberCardPrice || card.price }}</text>
</view>
<view class="cd-bottom__btn" @tap="handleSelectCard">
<text class="cd-bottom__btn-text">选择该卡</text>
</view>
</view>
</view>
<view v-else class="cd-loading">
<text>{{ loading ? '加载中...' : '暂无数据' }}</text>
</view>
</view>
</view>
</template>
<script setup>
import { ref, onMounted, onUnmounted } from 'vue'
import { getMemberCardById } from '@/api/main.js'
const card = ref(null)
const loading = ref(false)
function getCardTypeName(type) {
const map = {
TIME_CARD: '时长卡',
DURATION: '时长卡',
COUNT_CARD: '次卡',
COUNT: '次卡',
STORED_VALUE_CARD: '储值卡',
BALANCE: '储值卡'
}
return map[type] || type
}
function getTagClass(type) {
if (type === 'TIME_CARD' || type === 'DURATION') return 'time'
if (type === 'COUNT_CARD' || type === 'COUNT') return 'count'
if (type === 'STORED_VALUE_CARD' || type === 'BALANCE') return 'value'
return 'default'
}
function parseExtraConfig(config) {
if (!config) return ''
try {
const obj = JSON.parse(config)
return obj.description || obj.desc || config
} catch (e) {
return config
}
}
function handleSelectCard() {
if (!card.value) return
uni.navigateTo({ url: `/pages/memberInfo/purchaseCard?cardId=${card.value.memberCardId || card.value.id}` })
}
async function loadCardDetail(cardId) {
if (!cardId) return
loading.value = true
try {
const res = await getMemberCardById(cardId)
if (res && res.memberCardId) {
card.value = res
} else if (res && res.code === 200 && res.data) {
card.value = res.data
} else {
uni.showToast({ title: res?.message || '加载失败', icon: 'none' })
}
} catch (e) {
console.error('加载会员卡详情失败:', e)
uni.showToast({ title: '加载失败', icon: 'none' })
} finally {
loading.value = false
}
}
onMounted(() => {
const pages = getCurrentPages()
const currentPage = pages[pages.length - 1]
const options = currentPage.options || {}
const cardId = options.cardId || options.id
if (cardId) {
loadCardDetail(cardId)
}
})
onUnmounted(() => {
})
</script>
<style lang="scss">
@import '@/common/style/base.css';
@import '@/common/style/memberInfo/pages/page-reset.css';
@import '@/common/style/memberInfo/pages/sub-page-base.css';
@import '@/common/style/memberInfo/member-info-component-reset.css';
@import '@/common/style/memberInfo/member-info-sub-nav.css';
.card-detail-page {
min-height: 100vh;
background: #F5F7FA;
}
.card-detail-page__body {
padding-bottom: 120px;
}
.cd-loading {
display: flex;
justify-content: center;
align-items: center;
height: 60vh;
font-size: 14px;
color: #718096;
}
.cd-hero {
background: linear-gradient(135deg, #1A365D 0%, #2C5282 100%);
padding: 32px 24px;
}
.cd-hero__card {
background: rgba(255, 255, 255, 0.1);
border-radius: 16px;
padding: 24px;
backdrop-filter: blur(10px);
}
.cd-hero__card-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 20px;
}
.cd-hero__card-name {
font-size: 22px;
font-weight: 700;
color: #FFFFFF;
}
.cd-hero__card-tag {
padding: 4px 12px;
border-radius: 20px;
font-size: 12px;
&--time { background: rgba(66, 153, 225, 0.3); color: #90CDF4; }
&--count { background: rgba(237, 137, 54, 0.3); color: #FBD38D; }
&--value { background: rgba(56, 161, 105, 0.3); color: #9AE6B4; }
&--default { background: rgba(160, 174, 192, 0.3); color: #CBD5E0; }
}
.cd-hero__card-body {
display: flex;
gap: 24px;
margin-bottom: 20px;
}
.cd-hero__card-item {
display: flex;
flex-direction: column;
gap: 4px;
}
.cd-hero__card-label {
font-size: 12px;
color: rgba(255, 255, 255, 0.6);
}
.cd-hero__card-value {
font-size: 18px;
font-weight: 600;
color: #FFFFFF;
}
.cd-hero__card-footer {
display: flex;
align-items: baseline;
gap: 8px;
padding-top: 16px;
border-top: 1px solid rgba(255, 255, 255, 0.1);
}
.cd-hero__card-price {
font-size: 32px;
font-weight: 700;
color: #F6E05E;
}
.cd-hero__card-original {
font-size: 14px;
color: rgba(255, 255, 255, 0.5);
text-decoration: line-through;
}
.cd-section {
margin: 16px;
background: #FFFFFF;
border-radius: 16px;
padding: 20px;
}
.cd-section__title {
font-size: 16px;
font-weight: 600;
color: #1A202C;
margin-bottom: 16px;
display: block;
}
.cd-benefits {
display: flex;
flex-direction: column;
gap: 16px;
}
.cd-benefit {
display: flex;
align-items: flex-start;
gap: 12px;
}
.cd-benefit__icon {
font-size: 24px;
flex-shrink: 0;
}
.cd-benefit__content {
flex: 1;
display: flex;
flex-direction: column;
gap: 4px;
}
.cd-benefit__title {
font-size: 14px;
font-weight: 600;
color: #2D3748;
}
.cd-benefit__desc {
font-size: 12px;
color: #718096;
}
.cd-desc__text {
font-size: 14px;
color: #4A5568;
line-height: 1.6;
}
.cd-notes {
display: flex;
flex-direction: column;
gap: 8px;
}
.cd-notes__item {
font-size: 13px;
color: #718096;
line-height: 1.5;
}
.cd-bottom {
position: fixed;
bottom: 0;
left: 0;
right: 0;
display: flex;
align-items: center;
justify-content: space-between;
padding: 16px 24px;
padding-bottom: calc(16px + env(safe-area-inset-bottom));
background: #FFFFFF;
box-shadow: 0 -4px 20px rgba(0, 0, 0, 0.05);
z-index: 100;
}
.cd-bottom__total {
display: flex;
align-items: baseline;
gap: 8px;
}
.cd-bottom__total-label {
font-size: 14px;
color: #718096;
}
.cd-bottom__total-price {
font-size: 28px;
font-weight: 700;
color: #E53E3E;
}
.cd-bottom__btn {
background: linear-gradient(135deg, #1677FF 0%, #0958D9 100%);
border-radius: 12px;
padding: 16px 48px;
transition: all 0.2s;
&:active {
transform: scale(0.98);
opacity: 0.9;
}
}
.cd-bottom__btn-text {
font-size: 16px;
font-weight: 600;
color: #FFFFFF;
}
</style>
File diff suppressed because it is too large Load Diff
+239 -3
View File
@@ -1,10 +1,61 @@
<template>
<template>
<view class="login-page">
<!-- 自定义Toast弹窗 -->
<view class="custom-toast" :class="{ show: toastShow }">
<text class="toast-text">{{ toastMessage }}</text>
</view>
<!-- ==================== 微信小程序登录 ==================== -->
<template v-if="isWechatMiniProgram">
<view class="login-header" :style="{ paddingTop: statusBarHeightPx }">
<view class="logo-wrapper">
<image class="logo-image" src="/static/logo.png" mode="aspectFit" />
<text class="app-name">活氧舱欢迎您</text>
<text class="app-slogan">享受运动拥抱健康</text>
</view>
</view>
<view class="wechat-card">
<view class="card-body wechat-login-body">
<text class="phone-display">微信一键登录</text>
<text class="auth-tip">授权获取您的微信信息</text>
<!-- 获取微信头像昵称 -->
<button
v-if="canUseGetUserProfile"
class="login-btn wechat-btn"
hover-class="wechat-btn-hover"
@tap="handleGetUserProfile"
:disabled="isLoading"
>
<text>{{ isLoading ? '登录中...' : '微信一键登录' }}</text>
</button>
<!-- 低版本兼容 -->
<button
v-else
class="login-btn wechat-btn"
hover-class="wechat-btn-hover"
open-type="getUserInfo"
@getuserinfo="handleGetUserInfo"
:disabled="isLoading"
>
<text>{{ isLoading ? '登录中...' : '微信一键登录' }}</text>
</button>
<view class="agreement-text" :class="{ shake: shakeAgreement }">
<checkbox-group @change="onAgreementChange">
<checkbox :checked="agreed" value="agreed" color="#07C160" />
</checkbox-group>
<text>登录即表示同意</text>
<text class="link" @tap="showAgreement">用户协议</text>
<text></text>
<text class="link" @tap="showPrivacy">隐私政策</text>
</view>
</view>
</view>
</template>
<!-- ==================== App/其他平台登录原有 ==================== -->
<template v-else>
<view class="login-header">
<view class="logo-wrapper">
<image class="logo-image" src="/static/logo.png" mode="aspectFit" />
@@ -98,15 +149,36 @@
</view>
</view>
</view>
</template>
</view>
</template>
<script setup>
import { ref, onUnmounted } from 'vue'
import { ref, onMounted, onUnmounted } from 'vue'
import { setToken, setRefreshToken } from '@/utils/request.js'
import { loginWithPhone, oneClickLogin, sendCode } from '@/api/main.js'
import { loginWithPhone, oneClickLogin, sendCode, login } from '@/api/main.js'
import VerifyCodeInput from '@/components/global/VerifyCodeInput.vue'
// ===== 平台检测 =====
const isWechatMiniProgram = ref(false)
const statusBarHeightPx = ref('0px')
onMounted(() => {
// 获取状态栏高度,为刘海屏留出安全空间
const sysInfo = uni.getSystemInfoSync()
statusBarHeightPx.value = (sysInfo.statusBarHeight || 0) + 'px'
// 检测当前是否为微信小程序环境
// #ifdef MP-WEIXIN
isWechatMiniProgram.value = true
// #endif
if (!isWechatMiniProgram.value) {
// 非微信小程序,保持原有默认卡片
activeCard.value = 'phone'
}
})
const activeCard = ref('phone')
const isLoading = ref(false)
const agreed = ref(true)
@@ -440,6 +512,121 @@ async function handleSmsLogin() {
}
}
// ===== 微信小程序登录 =====
const canUseGetUserProfile = ref(false)
// 检查是否支持 getUserProfile(基础库 2.10.4+
onMounted(() => {
// #ifdef MP-WEIXIN
if (wx.getUserProfile) {
canUseGetUserProfile.value = true
}
// #endif
})
/**
* 微信小程序登录(新版:getUserProfile
*/
function handleGetUserProfile() {
if (!agreed.value) {
triggerAgreementShake()
return
}
isLoading.value = true
// #ifdef MP-WEIXIN
wx.getUserProfile({
desc: '用于完善会员资料',
success: (profileRes) => {
const userInfo = profileRes.userInfo
console.log('[微信登录] 获取用户信息成功:', userInfo)
// 执行微信登录
performWechatLogin(userInfo)
},
fail: (err) => {
console.error('[微信登录] 获取用户信息失败:', err)
isLoading.value = false
// 用户拒绝授权时,仍然尝试静默登录
performWechatLogin(null)
}
})
// #endif
}
/**
* 微信小程序登录(旧版:getUserInfo
*/
function handleGetUserInfo(e) {
if (!agreed.value) {
triggerAgreementShake()
return
}
isLoading.value = true
const userInfo = e.detail.userInfo
if (userInfo) {
console.log('[微信登录] 获取用户信息成功:', userInfo)
}
performWechatLogin(userInfo)
}
/**
* 执行微信登录:调用 uni.login 获取 code,发送到后端
*/
function performWechatLogin(userInfo) {
// #ifdef MP-WEIXIN
uni.login({
provider: 'weixin',
success: async (loginRes) => {
const code = loginRes.code
console.log('[微信登录] 获取 code 成功:', code)
if (!code) {
showToast('微信登录失败,请重试')
isLoading.value = false
return
}
try {
const res = await login({
code: code,
nickname: userInfo?.nickName || '',
avatar: userInfo?.avatarUrl || ''
})
console.log('[微信登录] 后端返回:', res)
if (res && res.accessToken) {
setToken(res.accessToken)
if (res.refreshToken) {
setRefreshToken(res.refreshToken)
}
saveLoginInfo(res)
showToast('登录成功')
setTimeout(() => {
uni.switchTab({ url: '/pages/memberInfo/memberInfo' })
}, 1500)
} else {
showToast('登录失败,请重试')
}
} catch (e) {
console.error('[微信登录] 后端调用失败:', e)
showToast(e?.message || '登录失败,请重试')
} finally {
isLoading.value = false
}
},
fail: (err) => {
console.error('[微信登录] uni.login 失败:', err)
showToast('微信登录失败,请重试')
isLoading.value = false
}
})
// #endif
}
onUnmounted(() => {
if (timer) clearInterval(timer)
})
@@ -481,6 +668,8 @@ button::after {
display: flex;
flex-direction: column;
padding: 48rpx;
box-sizing: border-box;
overflow: hidden;
}
.login-header {
@@ -523,6 +712,53 @@ button::after {
height: 500rpx;
}
/* ===== 微信小程序登录卡片 ===== */
.wechat-card {
width: 100%;
box-sizing: border-box;
background: $bg-white;
border-radius: $radius-lg;
box-shadow: 0 24rpx 60rpx rgba(0, 0, 0, 0.18), 0 8rpx 20rpx rgba(0, 0, 0, 0.1);
padding: 48rpx 40rpx;
}
.wechat-login-body {
min-height: 400rpx;
justify-content: flex-start;
gap: 24rpx;
}
.wechat-btn {
width: 100%;
height: 100rpx;
border-radius: $radius-md;
border: none;
background: #07C160;
color: #ffffff;
font-size: 34rpx;
font-weight: $font-weight-bold;
box-shadow: 0 8rpx 24rpx rgba(7, 193, 96, 0.3);
margin-top: 24rpx;
display: flex;
justify-content: center;
align-items: center;
&:active {
transform: scale(0.98);
opacity: 0.9;
}
&[disabled] {
background: #A0E0B0;
box-shadow: none;
}
}
.wechat-btn-hover {
opacity: 0.85;
transform: scale(0.98);
}
.login-card {
position: absolute;
height: 620rpx;
File diff suppressed because it is too large Load Diff
@@ -1,4 +1,4 @@
<template>
<template>
<scroll-view scroll-y class="scroll-container theme-light">
<view class="member-page">
<!-- Header: 用户信息区 -->
@@ -47,105 +47,6 @@
<view class="member-page__body">
<view class="member-page__sections">
<!-- 快过期会员卡提醒 -->
<view class="expiring-cards-section" v-if="expiringCards.length > 0">
<view class="expiring-cards__inner">
<view class="expiring-cards__header">
<view class="expiring-cards__header-inner">
<view class="expiring-cards__title-row">
<image class="expiring-cards__warn-icon" src="https://gymfuture.oss-cn-chengdu.aliyuncs.com/static/images/clock1.png" mode="aspectFit" />
<text class="expiring-cards__title">会员卡即将到期</text>
</view>
<view class="expiring-cards__link" hover-class="mi-tap--hover" :hover-stay-time="150" @tap="goMemberCard">
<text class="expiring-cards__link-text">查看全部</text>
<image class="expiring-cards__link-arrow" src="https://gymfuture.oss-cn-chengdu.aliyuncs.com/static/images/chevronright12.png" mode="aspectFit" />
</view>
</view>
</view>
<view class="expiring-cards__list">
<view
v-for="(card, index) in expiringCards"
:key="card.id || index"
class="expiring-card-item"
hover-class="mi-tap-row--hover"
:hover-stay-time="150"
@tap="goMemberCard"
>
<view class="expiring-card-item__inner">
<view class="expiring-card-item__icon-wrap">
<image class="expiring-card-item__icon" src="https://gymfuture.oss-cn-chengdu.aliyuncs.com/static/images/ticket.png" mode="aspectFit" />
</view>
<view class="expiring-card-item__info">
<text class="expiring-card-item__name">{{ card.name }}</text>
<text class="expiring-card-item__validity">{{ card.validity }}</text>
</view>
<view class="expiring-card-item__days">
<text class="expiring-card-item__days-num">{{ computeRemainingDays(card.validityEnd) }}</text>
<text class="expiring-card-item__days-unit">天后到期</text>
</view>
</view>
</view>
</view>
</view>
</view>
<!-- 会员卡区域 -->
<view class="member-card-section">
<view class="member-card-section__inner">
<view class="member-card-section__head">
<view class="member-card-section__head-inner">
<text class="member-card-section__title">我的会员卡</text>
<view class="member-card-section__link" hover-class="mi-tap--hover" :hover-stay-time="150" @tap="goMemberCard">
<text class="member-card-section__link-text">查看全部</text>
<image class="member-card-section__link-arrow" src="https://gymfuture.oss-cn-chengdu.aliyuncs.com/static/images/chevronright12.png" mode="aspectFit" />
</view>
</view>
</view>
<view class="member-card-preview" hover-class="mi-tap-card--hover" :hover-stay-time="150" @tap="goMemberCard">
<view class="member-card-preview__inner">
<view class="member-card-preview__head">
<view class="member-card-preview__head-inner">
<view class="member-card-preview__type-row">
<view class="member-card-preview__icon-wrap">
<view class="member-card-preview__icon-border">
<view class="member-card-preview__icon-bg"></view>
<view class="member-card-preview__icon-stroke"></view>
</view>
<image class="member-card-preview__icon-line" src="https://gymfuture.oss-cn-chengdu.aliyuncs.com/static/images/Line_2_468.png" mode="aspectFill" />
</view>
<text class="member-card-preview__name">{{ cardInfo.name }}</text>
</view>
<view class="member-card-preview__tag">
<text class="member-card-preview__tag-text">{{ cardInfo.detailTag || '详情' }}</text>
</view>
</view>
</view>
<text class="member-card-preview__expire">{{ cardInfo.expireDate }}</text>
<view class="member-card-preview__footer">
<view class="member-card-preview__footer-inner">
<view class="member-card-preview__days">
<text class="member-card-preview__days-num">{{ cardInfo.remainingDays }}</text>
<text class="member-card-preview__days-unit">天剩余</text>
</view>
<view class="member-card-preview__renew" hover-class="mi-tap-btn--hover" :hover-stay-time="150" @tap.stop="onRenewCard">
<text class="member-card-preview__renew-text">续费</text>
</view>
</view>
</view>
</view>
</view>
<view class="member-card-tip">
<view class="member-card-tip__inner">
<view class="member-card-tip__content">
<image class="member-card-tip__icon" src="https://gymfuture.oss-cn-chengdu.aliyuncs.com/static/images/clock1.png" mode="aspectFit" />
<text class="member-card-tip__text">{{ cardInfo.tip }}</text>
</view>
</view>
<view class="member-card-tip__border"></view>
</view>
</view>
</view>
<!-- 快捷操作区域 -->
<view class="quick-actions">
<view class="quick-actions__inner">
@@ -390,9 +291,9 @@
import { ref, computed, onMounted } from 'vue'
import { onShow } from '@dcloudio/uni-app'
import { PAGE, navigateToPage } from '@/common/constants/routes.js'
import { getCenterPageData, renewMemberCard, computeRemainingDays, saveMemberStore, loadMemberStore } from '@/common/memberInfo/store.js'
import { getCenterPageData, saveMemberStore, loadMemberStore } from '@/common/memberInfo/store.js'
import { getMemberId } from '@/utils/request.js'
import { getMyMemberCards, getCheckInStats, getMemberDetail } from '@/api/main.js'
import { getCheckInStats, getMemberDetail } from '@/api/main.js'
import { getMemberBookings } from '@/api/groupCourse.js'
import TabBar from '@/components/TabBar.vue'
import PageHeader from '@/components/index/PageHeader.vue'
@@ -400,8 +301,6 @@ import PageHeader from '@/components/index/PageHeader.vue'
// 页面数据
const userInfo = ref({})
const stats = ref({})
const cardInfo = ref({})
const expiringCards = ref([])
const bodyReport = ref({})
const couponPoints = ref({})
const referral = ref({})
@@ -468,8 +367,6 @@ function refreshFromStore() {
const pageData = getCenterPageData(store)
userInfo.value = pageData.userInfo
stats.value = pageData.stats
cardInfo.value = pageData.cardInfo
expiringCards.value = pageData.expiringCards || []
bodyReport.value = pageData.bodyReport
couponPoints.value = pageData.couponPoints
referral.value = pageData.referral
@@ -558,72 +455,10 @@ async function refreshStatsFromServer() {
}
}
async function refreshCardsFromServer() {
const memberId = getMemberId()
if (!memberId) return
try {
const res = await getMyMemberCards(memberId)
let cards = []
if (Array.isArray(res)) {
cards = res
} else if (res.code === 200 && res.data) {
cards = res.data
} else if (Array.isArray(res.data)) {
cards = res.data
}
if (cards.length > 0) {
const allCards = cards.map(myCard => {
const cardName = myCard.memberCardName || myCard.cardName || myCard.name || `会员卡#${myCard.memberCardId || myCard.id}`
const validityEnd = myCard.validityEnd || myCard.expireTime
const validityStart = myCard.validityStart || myCard.purchaseTime || myCard.createdAt
return {
id: myCard.id,
name: cardName,
status: myCard.status || 'active',
validity: validityEnd ? `有效期至 ${validityEnd}` : '永久有效',
validityEnd: validityEnd,
validityStart: validityStart
}
})
store.cards = allCards
const myCard = cards[0]
const cardName = myCard.memberCardName || myCard.cardName || myCard.name || `会员卡#${myCard.memberCardId || myCard.id}`
const validityEnd = myCard.validityEnd || myCard.expireTime
const validityStart = myCard.validityStart || myCard.purchaseTime || myCard.createdAt
store.card = {
id: myCard.id,
name: cardName,
status: myCard.status || 'active',
validity: validityEnd ? `有效期至 ${validityEnd}` : '永久有效',
validityEnd: validityEnd,
validityStart: validityStart
}
store.cardInfo = {
expireDate: validityEnd ? `有效期至 ${validityEnd}` : '永久有效',
remainingDays: computeRemainingDays(validityEnd)
}
saveMemberStore(store)
refreshFromStore()
}
} catch (e) {
console.error('[MemberCenter] 刷新会员卡数据失败:', e)
}
}
function goUserInfo() {
navigateToPage(PAGE.USER_INFO)
}
function goMemberCard() {
navigateToPage(PAGE.MEMBER_CARD)
}
function goBodyTestHistory() {
navigateToPage(PAGE.BODY_TEST_HISTORY)
}
@@ -646,20 +481,6 @@ function goReferral() {
navigateToPage(PAGE.REFERRAL)
}
function onRenewCard() {
uni.showModal({
title: '续费会员卡',
content: '确认续费 90 天?',
success: (res) => {
if (!res.confirm) return
const store = loadMemberStore()
renewMemberCard(store, 90)
refreshFromStore()
uni.showToast({ title: '续费成功', icon: 'success' })
}
})
}
function onQuickAction(type) {
const routes = {
booking: PAGE.COURSE_LIST,
@@ -733,7 +554,6 @@ onMounted(() => {
onShow(async () => {
await refreshStatsFromServer()
refreshCardsFromServer()
})
</script>
@@ -866,125 +686,4 @@ onShow(async () => {
background-color: transparent;
}
.expiring-cards-section {
margin: 0 16px 12px;
background: linear-gradient(135deg, #FFF7E6 0%, #FFE4CC 100%);
border-radius: 16px;
overflow: hidden;
}
.expiring-cards__inner {
padding: 16px;
}
.expiring-cards__header {
margin-bottom: 12px;
}
.expiring-cards__header-inner {
display: flex;
align-items: center;
justify-content: space-between;
}
.expiring-cards__title-row {
display: flex;
align-items: center;
gap: 8px;
}
.expiring-cards__warn-icon {
width: 20px;
height: 20px;
}
.expiring-cards__title {
font-size: 16px;
font-weight: 600;
color: #D46B08;
}
.expiring-cards__link {
display: flex;
align-items: center;
gap: 4px;
}
.expiring-cards__link-text {
font-size: 13px;
color: #D46B08;
}
.expiring-cards__link-arrow {
width: 12px;
height: 12px;
}
.expiring-cards__list {
background: rgba(255, 255, 255, 0.6);
border-radius: 12px;
padding: 4px 0;
}
.expiring-card-item {
padding: 12px;
}
.expiring-card-item__inner {
display: flex;
align-items: center;
gap: 12px;
}
.expiring-card-item__icon-wrap {
width: 40px;
height: 40px;
border-radius: 10px;
background: #FFFFFF;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
}
.expiring-card-item__icon {
width: 24px;
height: 24px;
}
.expiring-card-item__info {
flex: 1;
display: flex;
flex-direction: column;
gap: 4px;
}
.expiring-card-item__name {
font-size: 14px;
font-weight: 600;
color: #1A202C;
}
.expiring-card-item__validity {
font-size: 12px;
color: #718096;
}
.expiring-card-item__days {
display: flex;
flex-direction: column;
align-items: flex-end;
gap: 2px;
}
.expiring-card-item__days-num {
font-size: 20px;
font-weight: 700;
color: #E74C3C;
}
.expiring-card-item__days-unit {
font-size: 11px;
color: #E74C3C;
}
</style>
@@ -21,15 +21,6 @@
/>
<view class="member-page__body">
<view class="member-page__sections">
<MemberInfoMemberCard
v-if="isLogin"
ref="memberCardRef"
:card-info="cardInfo"
@view-all="goMemberCard"
@renew="onRenewCard"
@purchase="goPurchaseCard"
@stored-card-tap="goRechargeStoredCard"
/>
<MemberInfoLogout v-if="isLogin" @logout="handleLogout" />
</view>
</view>
@@ -50,7 +41,6 @@ import { onShow } from '@dcloudio/uni-app'
import { PAGE, navigateToPage } from '@/common/constants/routes.js'
import {
getCenterPageData,
renewMemberCard,
saveMemberStore,
getDefaultStore,
loadMemberStore
@@ -59,7 +49,6 @@ import { getMemberId } from '@/utils/request.js'
import { login as miniappLogin, getUserInfo, getMemberDetail, getCheckInStats, getMemberBookings, updateUserInfo } from '@/api/main.js'
import { setToken, getToken, clearToken } from '@/utils/request.js'
import MemberInfoHeader from '@/components/memberInfo/MemberInfoHeader.vue'
import MemberInfoMemberCard from '@/components/memberInfo/MemberInfoMemberCard.vue'
import MemberInfoLogout from '@/components/memberInfo/MemberInfoLogout.vue'
import MemberCenterSkeleton from '@/components/Skeleton/MemberCenterSkeleton.vue'
import TabBar from '@/components/TabBar.vue'
@@ -73,8 +62,6 @@ const pageReady = ref(false)
const hasActiveCard = ref(false)
const isLogin = ref(false)
const memberCardRef = ref(null)
onMounted(() => {
const token = uni.getStorageSync('token')
const loginMember = uni.getStorageSync('loginMemberInfo')
@@ -203,29 +190,6 @@ function goUserInfo() {
navigateToPage(PAGE.USER_INFO)
}
function goMemberCard() {
navigateToPage(PAGE.MEMBER_CARD)
}
function goPurchaseCard() {
navigateToPage(PAGE.PURCHASE_CARD)
}
function goRechargeStoredCard() {
navigateToPage(PAGE.RECHARGE_STORED_CARD)
}
function onRenewCard() {
uni.showModal({
title: '续费会员卡',
content: '确认续费 90 天?',
success: (res) => {
if (!res.confirm) return
uni.showToast({ title: '续费成功', icon: 'success' })
}
})
}
function handleLogout() {
uni.showModal({
title: '退出登录',
@@ -268,10 +232,6 @@ onShow(() => {
// 强制刷新数据(忽略 loading 防重入)
loading.value = false
fetchMemberInfo()
// 刷新子组件数据
if (memberCardRef.value && typeof memberCardRef.value.loadMemberCard === 'function') {
memberCardRef.value.loadMemberCard()
}
})
</script>
@@ -61,60 +61,22 @@
</view>
</view>
<!-- 支付密码弹窗 -->
<PayPasswordModal
:visible="showPayPwd"
:title="payPwdTitle"
:subtitle="payPwdSubtitle"
:cancel-notice="payPwdNotice"
@confirm="onPayPasswordConfirm"
@cancel="onPayPasswordCancel"
/>
</template>
<script setup>
import { ref, computed, onMounted } from 'vue'
import PageHeader from '@/components/index/PageHeader.vue'
import ListSkeleton from '@/components/Skeleton/ListSkeleton.vue'
import PayPasswordModal from '@/components/global/PayPasswordModal.vue'
import { getMemberId } from '@/utils/request.js'
import { getCourseCoverUrl } from '@/utils/request.js'
import { getMemberBookings, qrSignInGroupCourse } from '@/api/groupCourse.js'
import { cancelBooking as cancelBookingApi } from '@/api/groupCourse.js'
import { getConfigByKey } from '@/api/main.js'
const activeTab = ref('ongoing')
const ongoingBookings = ref([])
const completedBookings = ref([])
const loading = ref(false)
// 支付密码弹窗状态
const showPayPwd = ref(false)
const payPwdTitle = ref('')
const payPwdSubtitle = ref('')
const payPwdNotice = ref('')
let payPwdResolve = null
let cancelTarget = null
// 已取消次数
const cancelCount = ref(0)
const freeCancelLimit = ref(3) // 默认值,从API获取
const freeCampaignInitialized = ref(false)
const freeCancelsLeft = computed(() => Math.max(0, freeCancelLimit.value - cancelCount.value))
async function fetchFreeLimit() {
try {
const res = await getConfigByKey('group_course.cancel_free_limit')
const value = res?.configValue || res?.data?.configValue || '3'
freeCancelLimit.value = parseInt(value) || 3
freeCampaignInitialized.value = true
console.log('[myCourses] 免费取消次数阈值:', freeCancelLimit.value, '已取消次数:', cancelCount.value, '剩余:', freeCancelsLeft.value)
} catch (e) {
console.warn('[myCourses] 获取免费取消次数配置失败,使用默认值3:', e)
freeCampaignInitialized.value = true
}
}
const displayedBookings = computed(() => {
return activeTab.value === 'ongoing'
? ongoingBookings.value
@@ -139,8 +101,6 @@ async function fetchBookings() {
console.log('[myCourses] 预约记录:', bookings.length, '条', JSON.stringify(bookings))
if (Array.isArray(bookings)) {
// 统计已取消次数(status='1'
cancelCount.value = bookings.filter(b => String(b.status) === '1').length
const mapped = bookings.map(mapBooking)
ongoingBookings.value = mapped.filter(b => b.status === 'ongoing')
completedBookings.value = mapped.filter(b => b.status === 'completed')
@@ -220,27 +180,10 @@ async function handleCancel(item) {
success: async (res) => {
if (!res.confirm) return
// 弹出支付密码输入
cancelTarget = item
payPwdTitle.value = '取消验证'
payPwdSubtitle.value = item.title
// 实时获取最新免费次数阈值
await fetchFreeLimit()
// 设置退款提示
if (freeCancelsLeft.value > 0) {
payPwdNotice.value = `您还有 ${freeCancelsLeft.value} 次免手续费退款机会(超出后将扣除10%手续费)`
} else {
payPwdNotice.value = '本次取消将扣除10%手续费'
}
console.log('[myCourses] 取消弹窗: payPwdNotice=', payPwdNotice.value, 'freeCancelsLeft=', freeCancelsLeft.value, 'cancelCount=', cancelCount.value, 'freeLimit=', freeCancelLimit.value)
const payPassword = await requestPayPassword()
if (!payPassword) return
uni.showLoading({ title: '取消中...' })
try {
const result = await cancelBookingApi(Number(item.id), {
memberId: getMemberId(),
payPassword: payPassword
memberId: getMemberId()
})
uni.hideLoading()
if (result.success) {
@@ -259,23 +202,6 @@ async function handleCancel(item) {
})
}
function requestPayPassword() {
return new Promise((resolve) => {
payPwdResolve = resolve
showPayPwd.value = true
})
}
function onPayPasswordConfirm(password) {
payPwdResolve?.(password)
showPayPwd.value = false
}
function onPayPasswordCancel() {
payPwdResolve?.(null)
showPayPwd.value = false
}
// ===== 扫码签到 =====
function handleScan() {
uni.scanCode({
@@ -287,7 +213,7 @@ function handleScan() {
let courseId = null
try {
const parsed = JSON.parse(qrContent)
courseId = Number(parsed.courseId)
courseId = Number(parsed.id || parsed.courseId)
} catch {
const num = Number(qrContent)
if (!isNaN(num)) {
@@ -332,7 +258,6 @@ function handleScan() {
onMounted(() => {
fetchBookings()
fetchFreeLimit()
})
</script>
File diff suppressed because it is too large Load Diff
@@ -1,405 +0,0 @@
<template>
<view class="scroll-container theme-light">
<view class="recharge-page">
<MemberInfoSubNav title="储值卡充值" @back="goBack" />
<view class="recharge-page__body">
<!-- 当前余额 -->
<view class="rc-balance-card">
<view class="rc-balance-card__inner">
<text class="rc-balance-card__label">当前余额</text>
<view class="rc-balance-card__amount">
<text class="rc-balance-card__symbol">¥</text>
<text class="rc-balance-card__num">{{ currentBalance }}</text>
</view>
<text class="rc-balance-card__card-name">{{ currentCardName }}</text>
</view>
</view>
<!-- 充值档位 -->
<view class="rc-section">
<text class="rc-section__title">选择充值金额</text>
<view class="rc-grid">
<view
v-for="(item, index) in rechargeOptions"
:key="index"
class="rc-grid-item"
:class="{ 'rc-grid-item--selected': selectedIndex === index }"
@tap="selectRecharge(index)"
>
<view class="rc-grid-item__top">
<text class="rc-grid-item__amount">¥{{ item.amount }}</text>
</view>
<view class="rc-grid-item__bottom">
<text class="rc-grid-item__bonus">¥{{ item.bonus }}</text>
</view>
<view class="rc-grid-item__check" v-if="selectedIndex === index">
<text></text>
</view>
</view>
</view>
</view>
<!-- 充值说明 -->
<view class="rc-rules">
<text class="rc-rules__title">充值说明</text>
<view class="rc-rules__list">
<view class="rc-rules__item">
<text>1. 充值金额实时到账赠送金额同步到账</text>
</view>
<view class="rc-rules__item">
<text>2. 余额可用于购买会员卡消费等</text>
</view>
<view class="rc-rules__item">
<text>3. 充值金额一经到账不予退还</text>
</view>
</view>
</view>
<!-- 底部按钮 -->
<view class="rc-bottom">
<view class="rc-bottom__info">
<text class="rc-bottom__label">实付金额</text>
<view class="rc-bottom__amount">
<text class="rc-bottom__symbol">¥</text>
<text class="rc-bottom__num">{{ selectedAmount }}</text>
</view>
</view>
<view
class="rc-bottom__btn"
:class="{ 'rc-bottom__btn--disabled': loading }"
@tap="handleRecharge"
>
<text>{{ loading ? '处理中...' : '立即充值' }}</text>
</view>
</view>
</view>
</view>
</view>
</template>
<script setup>
import { ref, computed } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import MemberInfoSubNav from '@/components/memberInfo/MemberInfoSubNav.vue'
import { getMemberId } from '@/utils/request.js'
import { checkPayPasswordSet, getStoredCardInfo } from '@/api/main.js'
import { navigateToPage, PAGE } from '@/common/constants/routes.js'
const currentBalance = ref('0.00')
const selectedIndex = ref(0)
const loading = ref(false)
const rechargeOptions = ref([
{ amount: 100, bonus: 10, payAmount: 100 },
{ amount: 200, bonus: 20, payAmount: 200 },
{ amount: 300, bonus: 30, payAmount: 300 }
])
const selectedAmount = computed(() => {
return rechargeOptions.value[selectedIndex.value]?.payAmount || 0
})
onLoad(() => {
loadBalance()
})
async function loadBalance() {
const memberId = getMemberId()
if (!memberId) return
try {
const res = await getStoredCardInfo(memberId)
const data = res.data || res
currentBalance.value = ((data.balance || 0).toFixed(2))
} catch (e) {
console.error('获取储值卡余额失败:', e)
}
}
function selectRecharge(index) {
selectedIndex.value = index
}
async function handleRecharge() {
if (loading.value) return
const memberId = getMemberId()
if (!memberId) {
uni.showToast({ title: '用户未登录', icon: 'none' })
return
}
// 检查支付密码
uni.showLoading({ title: '检查中...' })
try {
const checkRes = await checkPayPasswordSet(memberId)
uni.hideLoading()
const isSet = checkRes.isSet || checkRes.data?.isSet || false
if (!isSet) {
uni.showModal({
title: '提示',
content: '您尚未设置支付密码,请先设置支付密码后再进行充值',
confirmText: '去设置',
success: (res) => {
if (res.confirm) {
navigateToPage(PAGE.SET_PAY_PASSWORD)
}
}
})
return
}
// 确认充值
const option = rechargeOptions.value[selectedIndex.value]
uni.showModal({
title: '确认充值',
content: `确认充值 ${option.amount} 元?\n到账金额:${option.amount + option.bonus}\n(含赠送 ${option.bonus} 元)`,
confirmText: '确认充值',
success: async (res) => {
if (res.confirm) {
await initiatePayment(option)
}
}
})
} catch (e) {
uni.hideLoading()
console.error('检查支付密码失败:', e)
uni.showToast({ title: '检查失败,请稍后重试', icon: 'none' })
}
}
async function initiatePayment(option) {
const amount = option.payAmount
const desc = `储值卡充值:充${option.amount}${option.amount + option.bonus}`
const remark = `rechargeAmount=${option.amount}&bonus=${option.bonus}`
uni.navigateTo({
url: `/pages/memberInfo/confirmPayment?orderType=STORED_CARD_RECHARGE&amount=${amount}&desc=${encodeURIComponent(desc)}&remark=${encodeURIComponent(remark)}&returnPage=memberCard&cardType=stored`,
fail: (err) => {
console.error('跳转支付页面失败:', err)
uni.showToast({ title: '跳转支付页面失败', icon: 'none' })
}
})
}
function goBack() {
uni.navigateBack()
}
</script>
<style lang="scss">
@import '@/common/style/base.css';
@import '@/common/style/memberInfo/pages/page-reset.css';
@import '@/common/style/memberInfo/pages/sub-page-base.css';
.recharge-page {
min-height: 100vh;
background: #F5F7FA;
padding-bottom: 100px;
}
.recharge-page__body {
padding: 16px;
}
.rc-balance-card {
margin-bottom: 20px;
}
.rc-balance-card__inner {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
border-radius: 16px;
padding: 24px;
color: #FFFFFF;
}
.rc-balance-card__label {
font-size: 13px;
opacity: 0.8;
margin-bottom: 8px;
display: block;
}
.rc-balance-card__amount {
display: flex;
align-items: baseline;
margin-bottom: 8px;
}
.rc-balance-card__symbol {
font-size: 18px;
font-weight: 600;
margin-right: 4px;
}
.rc-balance-card__num {
font-size: 36px;
font-weight: 700;
}
.rc-balance-card__card-name {
font-size: 13px;
opacity: 0.9;
}
.rc-section {
background: #FFFFFF;
border-radius: 12px;
padding: 16px;
margin-bottom: 16px;
}
.rc-section__title {
font-size: 15px;
font-weight: 600;
color: #1A202C;
margin-bottom: 16px;
display: block;
}
.rc-grid {
display: flex;
gap: 12px;
flex-wrap: wrap;
}
.rc-grid-item {
width: calc(33.33% - 8px);
background: #F7FAFC;
border: 2px solid transparent;
border-radius: 12px;
padding: 16px 8px;
text-align: center;
position: relative;
transition: all 0.2s;
&--selected {
background: #F0F4FF;
border-color: #1677FF;
}
}
.rc-grid-item__top {
margin-bottom: 4px;
}
.rc-grid-item__amount {
font-size: 20px;
font-weight: 700;
color: #1A202C;
}
.rc-grid-item__bottom {
margin-bottom: 0;
}
.rc-grid-item__bonus {
font-size: 12px;
color: #E53E3E;
font-weight: 500;
}
.rc-grid-item__check {
position: absolute;
top: -1px;
right: -1px;
width: 20px;
height: 20px;
background: #1677FF;
border-radius: 0 10px 0 10px;
display: flex;
align-items: center;
justify-content: center;
text {
color: #FFFFFF;
font-size: 12px;
font-weight: 700;
}
}
.rc-rules {
background: #FFFFFF;
border-radius: 12px;
padding: 16px;
}
.rc-rules__title {
font-size: 14px;
font-weight: 600;
color: #1A202C;
margin-bottom: 12px;
display: block;
}
.rc-rules__list {
display: flex;
flex-direction: column;
gap: 8px;
}
.rc-rules__item {
font-size: 13px;
color: #718096;
line-height: 1.5;
}
.rc-bottom {
position: fixed;
left: 0;
right: 0;
bottom: 0;
background: #FFFFFF;
padding: 12px 16px;
padding-bottom: calc(12px + env(safe-area-inset-bottom));
display: flex;
align-items: center;
justify-content: space-between;
box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.05);
}
.rc-bottom__info {
display: flex;
align-items: baseline;
gap: 8px;
}
.rc-bottom__label {
font-size: 13px;
color: #718096;
}
.rc-bottom__amount {
display: flex;
align-items: baseline;
}
.rc-bottom__symbol {
font-size: 14px;
font-weight: 600;
color: #E53E3E;
margin-right: 2px;
}
.rc-bottom__num {
font-size: 24px;
font-weight: 700;
color: #E53E3E;
}
.rc-bottom__btn {
padding: 12px 32px;
background: #1677FF;
border-radius: 24px;
color: #FFFFFF;
font-size: 15px;
font-weight: 600;
&--disabled {
opacity: 0.6;
}
}
</style>
@@ -1,217 +0,0 @@
<template>
<view class="scroll-container theme-light">
<view class="set-pay-password-page">
<MemberInfoSubNav :title="isReset ? '修改支付密码' : '设置支付密码'" @back="goBack" />
<view class="set-pay-password-page__body">
<view class="spp-intro">
<image class="spp-intro__icon" src="https://gymfuture.oss-cn-chengdu.aliyuncs.com/static/images/lock.png" mode="aspectFit" />
</view>
<VerifyCodeInput
ref="verifyInputRef"
v-model="password"
:title="currentTitle"
:desc="currentDesc"
:length="6"
:mask="true"
type="box"
:errorMsg="errorMsg"
@complete="handleComplete"
/>
<view class="spp-forgot" v-if="isReset && step === 1" @tap="goForgotPassword">
<text>忘记密码</text>
</view>
</view>
</view>
</view>
</template>
<script setup>
import { ref, computed, nextTick } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import MemberInfoSubNav from '@/components/memberInfo/MemberInfoSubNav.vue'
import VerifyCodeInput from '@/components/global/VerifyCodeInput.vue'
import { getMemberId } from '@/utils/request.js'
import { setPayPassword, verifyPayPassword, resetPayPassword } from '@/api/main.js'
const password = ref('')
const step = ref(1)
const firstPassword = ref('')
const isReset = ref(false)
const errorMsg = ref('')
const verifyInputRef = ref(null)
const currentTitle = computed(() => {
if (isReset.value && step.value === 1) {
return '请输入原6位支付密码'
}
if (step.value === 1) {
return isReset.value ? '请输入新的6位支付密码' : '请设置6位支付密码'
}
return '请再次输入6位支付密码'
})
const currentDesc = computed(() => {
if (isReset.value && step.value === 1) {
return '验证原密码后可设置新密码'
}
return '支付密码用于充值、消费等资金操作,请妥善保管'
})
onLoad((options) => {
if (options && options.reset === 'true') {
isReset.value = true
}
})
function handleComplete(pwd) {
errorMsg.value = ''
if (isReset.value && step.value === 1) {
verifyOldPassword(pwd)
return
}
if (step.value === 1) {
firstPassword.value = pwd
step.value = 2
password.value = ''
nextTick(() => {
verifyInputRef.value?.focusInput()
})
} else {
if (pwd !== firstPassword.value) {
errorMsg.value = '两次密码输入不一致,请重新输入'
password.value = ''
step.value = 1
firstPassword.value = ''
nextTick(() => {
verifyInputRef.value?.focusInput()
})
return
}
submitPassword(pwd)
}
}
async function verifyOldPassword(pwd) {
const memberId = getMemberId()
if (!memberId) {
uni.showToast({ title: '用户未登录', icon: 'none' })
return
}
uni.showLoading({ title: '验证中...' })
try {
const res = await verifyPayPassword(memberId, pwd)
uni.hideLoading()
if (res && (res.valid === true || res.data?.valid === true)) {
step.value = 2
password.value = ''
firstPassword.value = ''
nextTick(() => {
verifyInputRef.value?.focusInput()
})
} else {
errorMsg.value = '原密码错误,请重新输入'
password.value = ''
nextTick(() => {
verifyInputRef.value?.focusInput()
})
}
} catch (e) {
uni.hideLoading()
errorMsg.value = '验证失败,请稍后重试'
password.value = ''
}
}
async function submitPassword(pwd) {
const memberId = getMemberId()
if (!memberId) {
uni.showToast({ title: '用户未登录', icon: 'none' })
return
}
uni.showLoading({ title: '设置中...' })
try {
let res
if (isReset.value) {
res = await resetPayPassword(memberId, firstPassword.value, pwd)
} else {
res = await setPayPassword(memberId, pwd)
}
uni.hideLoading()
if (res && (res.code === 200 || res.code === 200 || res.message === '设置成功' || res.message === '重置成功')) {
uni.showToast({ title: isReset.value ? '修改成功' : '设置成功', icon: 'success' })
setTimeout(() => {
uni.navigateBack()
}, 1500)
} else {
uni.showToast({ title: res.message || '设置失败', icon: 'none' })
password.value = ''
step.value = 1
firstPassword.value = ''
}
} catch (e) {
uni.hideLoading()
uni.showToast({ title: '设置失败,请稍后重试', icon: 'none' })
password.value = ''
step.value = 1
firstPassword.value = ''
}
}
function goBack() {
uni.navigateBack()
}
function goForgotPassword() {
uni.showToast({ title: '请联系客服重置密码', icon: 'none' })
}
</script>
<style lang="scss">
@import '@/common/style/base.css';
@import '@/common/style/memberInfo/pages/page-reset.css';
@import '@/common/style/memberInfo/pages/sub-page-base.css';
.set-pay-password-page {
min-height: 100vh;
background: #F5F7FA;
}
.set-pay-password-page__body {
padding: 40px 24px;
}
.spp-intro {
display: flex;
flex-direction: column;
align-items: center;
margin-bottom: 24px;
}
.spp-intro__icon {
width: 56px;
height: 56px;
}
.spp-forgot {
text-align: right;
margin-top: 8px;
}
.spp-forgot text {
font-size: 13px;
color: #1677FF;
}
</style>