Browse Source

feat: 添加H5平台支持并优化用户信息处理

- 在Vuex store中为H5平台添加localStorage存储用户信息
- 为H5平台添加头像选择和上传功能
- 优化二维码生成逻辑,为H5平台添加official类型参数
- 修复分享链接参数从vid改为inviter
- 移除默认头像和会员权益的硬编码内容
- 优化代码格式和注释
hfll
hflllll 2 weeks ago
parent
commit
7319b657c3
9 changed files with 624 additions and 489 deletions
  1. +5
    -1
      App.vue
  2. +5
    -5
      mixins/config.js
  3. +21
    -19
      pages/index/member.vue
  4. +3
    -0
      stores/index.js
  5. +79
    -3
      subPages/login/userInfo.vue
  6. +430
    -445
      subPages/member/recharge.vue
  7. +64
    -3
      subPages/user/profile.vue
  8. +5
    -1
      subPages/user/share.vue
  9. +12
    -12
      utils/share.js

+ 5
- 1
App.vue View File

@ -7,7 +7,11 @@
//
async getQrcode() {
uni.getImageInfo({
src: `${this.$config.baseURL}/promotion/qrCode?token=${uni.getStorageSync('token')}`,
src: `${this.$config.baseURL}/promotion/qrCode?token=${uni.getStorageSync('token')}`
// #ifdef H5
+ '&type=official'
// #endif
,
success: (image) => {
this.Qrcode = image.path;
this.$store.commit('setQrcode', this.Qrcode)


+ 5
- 5
mixins/config.js View File

@ -16,17 +16,17 @@ export default {
const statusBarHeight = systemInfo.statusBarHeight || 0;
const navigationBarHeight = 44; // 默认导航栏高度
this.containerHeight = 2 * (systemInfo.windowHeight - statusBarHeight - navigationBarHeight);
console.log('最后的容器高度为', this.containerHeight/2 + 'px', '状态栏高度:', statusBarHeight + 'px', 'nav导航栏高度:', navigationBarHeight + 'px');
console.log('最后的容器高度为', this.containerHeight / 2 + 'px', '状态栏高度:', statusBarHeight + 'px', 'nav导航栏高度:', navigationBarHeight + 'px');
},
},
computed: {
// 获取全局配置的文本
configParamInfo() {
return key => this.$store.state.configList[key]?.info || '默认资料'
return key => this.$store.state.configList[key]?.info
},
// 获取全局配置的图片
configParamContent() {
return key => this.$store.state.configList[key]?.content || '默认内容'
return key => this.$store.state.configList[key]?.content
},
// 默认的全局分享参数
@ -34,7 +34,7 @@ export default {
const obj = {}
if (this.$store.state.userInfo.id) {
obj.path = this.configParamContent('xcxSharePage') + '?inviter=' + this.$store.state.userInfo.id
}else {
} else {
obj.path = this.configParamContent('xcxSharePage')
}
return {
@ -61,7 +61,7 @@ export default {
},
onLoad(args) {
// this.calculateContainerHeight();
if (args.inviter){
if (args.inviter) {
uni.setStorageSync('inviter', args.inviter)
}
}


+ 21
- 19
pages/index/member.vue View File

@ -22,12 +22,12 @@
</view>
<view v-if="!isMember" class="noVip-container">
<image
src="/static/vip.png"
src="/static/VIP.png"
mode="aspectFit"
class="VIP-img"
/>
<text class="intro" >共19项会员特权 | 3 项年VIP专属特权</text>
<view class="border" ></view>
<text class="intro" >{{ configParamContent('member_info') || '共19项会员特权 | 3 项年VIP专属特权'}}</text>
<view class="border"></view>
<view class="info" >
<view class="avatar-box">
<image
@ -82,10 +82,12 @@
<view class="benefits-list">
<!-- 碎片学习 系统掌握 -->
<!-- <view class="benefit-item" v-for="(item, index) in memberBenefits" :key="index"> -->
<!-- <view class="benefit-item"> -->
<uv-parse :content="memberBenefits[0]"></uv-parse>
<!-- </view> -->
<!-- <view class="benefit-item" v-for="(item, index) in memberBenefits"
:key="index">
<uv-parse :content="item"></uv-parse>
</view> -->
<uv-parse :content="memberBenefits[0]"></uv-parse>
<!-- 匹配水平 -->
<!-- <view class="benefit-item">
@ -94,7 +96,7 @@
<view class="benefit-desc">依据水平精准推课不做无用功快速提升</view>
</view>
<view class="benefit-icon">
<image src="/static/member-image-2.png" mode="aspectFit"></image>
<image src="/static/会员图片2.png" mode="aspectFit"></image>
</view>
</view> -->
@ -105,7 +107,7 @@
<view class="benefit-desc">精心设计科学的学习流程 测试-讲解-练习-检验知识掌握更牢固</view>
</view>
<view class="benefit-icon">
<image src="/static/member-image-3.png" mode="aspectFit"></image>
<image src="/static/会员图片3.png" mode="aspectFit"></image>
</view>
</view> -->
</view>
@ -139,7 +141,7 @@
<view class="plan-book-meta" >
<text class="plan-book-grade" :class="{ 'highlight-title': index === 1 }">{{ book.book.categoryName || '--' }}/</text>
<image v-if="index !== 1" src="/static/play-icon.png" class="plan-book-duration-icon" />
<image v-else src="/static/play-icon-highlight.png" class="plan-book-duration-icon" />
<image v-else src="/static/play-icon-highlight.png" class="plan-book-duration-icon" />
<text class="plan-book-duration" :class="{ 'highlight-title': index === 1 }">{{ book.book.duration }}</text>
</view>
</view>
@ -171,7 +173,7 @@
<text class="recommend-grid-title">{{ book.booksName }}</text>
<view class="recommend-grid-meta">
<text class="recommend-grid-grade">{{ book.categoryName }}/</text>
<image src="/static/play-icon.png" class="recommend-grid-duration-icon" />
<image src="/static/播放图标.png" class="recommend-grid-duration-icon" />
<text class="recommend-grid-duration">{{ book.duration }}</text>
</view>
</view>
@ -191,7 +193,7 @@ export default{
memberBenefits: [],
userInfo: {
name: '战斗世界',
avatar: '/static/default-avatar.png'
avatar: '/static/默认头像.png'
},
//
@ -293,7 +295,7 @@ export default{
this.isLogin = false
this.userInfo = {
name: '登录后查看会员情况',
avatar: '/static/default-avatar.png'
avatar: '/static/默认头像.png'
}
}
}
@ -462,10 +464,10 @@ export default{
}
.benefit-item {
background: #F8F8F8;
border: 1px solid #FFFFFF;
border-radius: 48rpx;
padding: 27rpx 40rpx;
// background: #F8F8F8;
// border: 1px solid #FFFFFF;
// border-radius: 48rpx;
// padding: 27rpx 40rpx;
display: flex;
align-items: center;
justify-content: space-between;
@ -490,8 +492,8 @@ export default{
}
.benefit-icon {
width: 152rpx;
height: 152rpx;
// width: 152rpx;
// height: 152rpx;
// flex-shrink: 0;
}


+ 3
- 0
stores/index.js View File

@ -59,6 +59,9 @@ const store = new Vuex.Store({
},
setUserInfo(state, data) {
state.userInfo = data
// #ifdef H5
localStorage.setItem('userInfo', data)
// #endif
},
setQrcode(state, data) {
state.Qrcode = data


+ 79
- 3
subPages/login/userInfo.vue View File

@ -17,6 +17,7 @@
<view class="form-item">
<text class="form-label">头像</text>
<view class="avatar-button">
<!-- #ifdef MP-WEIXIN -->
<button class="avatar-btn" open-type="chooseAvatar" @chooseavatar="onChooseAvatar">
<image
class="avatar-image"
@ -24,6 +25,17 @@
mode="aspectFill"
></image>
</button>
<!-- #endif -->
<!-- #ifndef MP-WEIXIN -->
<button class="avatar-btn" @click="chooseImageH5">
<image
class="avatar-image"
:src="userInfo.avatar || '/static/default-avatar.png'"
mode="aspectFill"
></image>
</button>
<!-- #endif -->
</view>
</view>
@ -43,6 +55,7 @@
<!-- 手机号 -->
<view class="form-item">
<text class="form-label">手机号</text>
<!-- #ifdef MP-WEIXIN -->
<view class="phone-container" v-if="!userInfo.phone">
<button
class="get-phone-btn"
@ -53,6 +66,16 @@
</button>
</view>
<text class="form-label" v-else>{{ userInfo.phone }}</text>
<!-- #endif -->
<!-- #ifndef MP-WEIXIN -->
<input
class="form-input"
type="number"
v-model="userInfo.phone"
placeholder="请输入手机号"
/>
<!-- #endif -->
</view>
<!-- 所在部门 -->
@ -199,6 +222,47 @@ export default {
});
}
},
// /H5
// #ifndef MP-WEIXIN
async chooseImageH5() {
try {
const res = await uni.chooseImage({
count: 1,
sizeType: ['compressed'],
sourceType: ['album', 'camera']
})
const filePath = (res.tempFilePaths && res.tempFilePaths[0])
|| (res.tempFiles && res.tempFiles[0] && (res.tempFiles[0].path || res.tempFiles[0].tempFilePath))
if (!filePath) {
uni.showToast({ title: '未选择图片', icon: 'none' })
return
}
uni.showLoading({ title: '上传头像中...' })
const file = { path: filePath, tempFilePath: filePath }
const uploadResult = await this.$utils.uploadImage(file)
uni.hideLoading()
if (uploadResult && uploadResult.success) {
this.userInfo.avatar = uploadResult.url
uni.showToast({ title: '头像上传成功', icon: 'success' })
} else {
//
this.userInfo.avatar = filePath
uni.showToast({ title: '头像已选择', icon: 'none' })
}
} catch (error) {
uni.hideLoading()
console.error('选择/上传头像异常:', error)
uni.showToast({ title: '头像处理异常', icon: 'none' })
}
},
// #endif
//
@ -250,6 +314,7 @@ export default {
},
//
// #ifdef MP-WEIXIN
async getPhoneNumber(e) {
console.log('获取手机号回调', e);
if (e.detail.errMsg === 'getPhoneNumber:ok') {
@ -272,10 +337,11 @@ export default {
})
}
},
// #endif
//
async submitUserInfo() {
if (!this.userInfo.name.trim()) {
if (!this.userInfo.name?.trim()) {
uni.showToast({
title: '请输入昵称',
icon: 'none'
@ -283,14 +349,24 @@ export default {
return;
}
if (!this.userInfo.phone.trim()) {
if (!this.userInfo.phone?.trim()) {
uni.showToast({
title: '请获取手机号',
title: '请输入手机号',
icon: 'none'
});
return;
}
//
const phoneReg = /^1[3-9]\d{9}$/
if (!phoneReg.test(this.userInfo.phone)) {
uni.showToast({
title: '请输入正确的手机号',
icon: 'none'
})
return
}
console.log('提交用户信息', this.userInfo);


+ 430
- 445
subPages/member/recharge.vue View File

@ -1,102 +1,76 @@
<template>
<view class="container">
<view class="header">
<view class="header-bg">
<image
src="/static/member-background.png"
class="header-img"
mode="scaleToFill"
/>
<!-- #ifndef H5 -->
<text class="header-title">会员开通</text>
<!-- 加一个推出箭头 -->
<view class="header-icon" @click="goBack">
<uv-icon name="arrow-left" color="#000" size="20" />
</view>
<!-- #endif -->
<view class="header">
<view class="header-bg">
<image src="/static/member-background.png" class="header-img" mode="scaleToFill" />
<!-- #ifndef H5 -->
<text class="header-title">会员开通</text>
<!-- 加一个推出箭头 -->
<view class="header-icon" @click="goBack">
<uv-icon name="arrow-left" color="#000" size="20" />
</view>
<!-- #endif -->
<!-- 轮播容器 -->
<view class="uv-demo-block swiper-container">
<uv-swiper
bgColor="transparent"
:list="list"
:loading="!list.length"
@change="changeSwiper"
keyName="img"
previousMargin="70"
nextMargin="70"
acceleration
height="75"
circular
:autoplay="false"
radius="5"
>
<uv-swiper bgColor="transparent" :list="list" :loading="!list.length" @change="changeSwiper"
keyName="img" previousMargin="70" nextMargin="70" acceleration height="75" circular
:autoplay="false" radius="5">
</uv-swiper>
<button class="swiper-btn">
已选择
</button>
<image
class="swiper-arrow"
src="/static/decorative-arrow.png"
mode="aspectFill"
/>
<image class="swiper-arrow" src="/static/decorative-arrow.png" mode="aspectFill" />
</view>
</view>
</view>
<!-- 会员套餐选择容器 -->
<view class="membership-container">
<!-- 套餐选择 -->
<view class="package-list">
<view
class="package-item"
:class="{ 'active': selectedPackage === index }"
v-for="(item, index) in packageList"
:key="index"
@click="selectPackage(index)"
>
</view>
</view>
<!-- 会员套餐选择容器 -->
<view class="membership-container">
<!-- 套餐选择 -->
<view class="package-list">
<view class="package-item" :class="{ 'active': selectedPackage === index }"
v-for="(item, index) in packageList" :key="index" @click="selectPackage(index)">
<view class="info">
<!-- 赠送标识 -->
<view class="gift-tag" v-if="item.content">
<view class="gift-tag" v-if="item.content">
{{ item.content }}
</view>
<view class="package-title">{{ item.title }}</view>
<view class="package-price">¥{{ getInt(item.discountedprice) }}.<text class="package-decimal">{{ getDecimal(item.discountedprice) }}</text></view>
<view class="package-original">¥{{ item.originalprice }}</view>
</view>
<view class="package-title">{{ item.title }}</view>
<view class="package-price">¥{{ getInt(item.discountedprice) }}.<text
class="package-decimal">{{ getDecimal(item.discountedprice) }}</text></view>
<view class="package-original">¥{{ item.originalprice }}</view>
</view>
<view class="package-btn" :class="{ 'active': selectedPackage === index }">
{{ selectedPackage === index ? '已选择' : '点击选择' }}
</view>
</view>
</view>
<!-- 优惠券选择 -->
<view class="coupon-section">
<view class="coupon-title">选择优惠券</view>
<view class="coupon-selector" @click="selectCoupon">
<view v-if="!selectedCoupon" class="coupon-placeholder">
<text class="coupon-text">请选择</text>
<uv-icon name="arrow-right" color="#999" size="16" />
</view>
<view v-else class="coupon-selected">
<view class="coupon-info">
<text class="coupon-name">{{ selectedCoupon.name }}</text>
<text class="coupon-amount">-¥{{ selectedCoupon.money }}</text>
</view>
<uv-icon name="arrow-right" color="#999" size="16" />
</view>
</view>
</view>
</view>
<!-- 立即开通会员按钮 -->
<view class="member-button-section">
<uv-button
type="primary"
text="立即开通会员"
:custom-style="{
<view class="package-btn" :class="{ 'active': selectedPackage === index }">
{{ selectedPackage === index ? '已选择' : '点击选择' }}
</view>
</view>
</view>
<!-- 优惠券选择 -->
<view class="coupon-section">
<view class="coupon-title">选择优惠券</view>
<view class="coupon-selector" @click="selectCoupon">
<view v-if="!selectedCoupon" class="coupon-placeholder">
<text class="coupon-text">请选择</text>
<uv-icon name="arrow-right" color="#999" size="16" />
</view>
<view v-else class="coupon-selected">
<view class="coupon-info">
<text class="coupon-name">{{ selectedCoupon.name }}</text>
<text class="coupon-amount">-¥{{ selectedCoupon.money }}</text>
</view>
<uv-icon name="arrow-right" color="#999" size="16" />
</view>
</view>
</view>
</view>
<!-- 立即开通会员按钮 -->
<view class="member-button-section">
<uv-button type="primary" text="立即开通会员" :custom-style="{
width: '100%',
height: '82rpx',
borderRadius: '44rpx',
@ -104,29 +78,27 @@
fontSize: '36rpx',
fontWeight: '500',
border: '1px solid #06DADC'
}"
@click="handleRecharge"
></uv-button>
</view>
<!-- 会员权益 -->
<view class="benefits-section">
<view class="benefits-title">会员权益</view>
<view class="benefits-list">
<!-- 碎片学习 系统掌握 -->
<view class="benefit-item" v-for="item in packageList" :key="item.id">
<uv-parse :content="item.content" />
</view>
<!-- 匹配水平 -->
<!-- <view class="benefit-item">
}" @click="handleRecharge"></uv-button>
</view>
<!-- 会员权益 -->
<view class="benefits-section">
<view class="benefits-title">会员权益</view>
<view class="benefits-list">
<!-- 碎片学习 系统掌握 -->
<!-- <view class="benefit-item" v-for="item in packageList" :key="item.id"> -->
<uv-parse :content="list[selectedMember].content" />
<!-- </view> -->
<!-- 匹配水平 -->
<!-- <view class="benefit-item">
<view class="benefit-content">
<view class="benefit-title">匹配水平</view>
<view class="benefit-desc">依据水平精准推课不做无用功快速提升</view>
</view>
<view class="benefit-icon">
<image src="/static/member-image-2.png" mode="aspectFit"></image>
<image src="/static/会员图片2.png" mode="aspectFit"></image>
</view>
</view>
@ -137,12 +109,12 @@
<view class="benefit-desc">精心设计科学的学习流程 测试-讲解-练习-检验知识掌握更牢固</view>
</view>
<view class="benefit-icon">
<image src="/static/member-image-3.png" mode="aspectFit"></image>
<image src="/static/会员图片3.png" mode="aspectFit"></image>
</view>
</view> -->
</view>
</view>
</view>
</view>
</view>
</template>
@ -159,67 +131,68 @@
defaultPackageList: [
],
couponId: '',
selectedCoupon: null //
couponId: '',
selectedCoupon: null //
}
},
computed: {
packageList() {
return this.list[this.selectedMember]?.setmeals ? this.list[this.selectedMember].setmeals : this
.defaultPackageList
}
},
computed:{
packageList(){
return this.list[this.selectedMember].setmeals.length ? this.list[this.selectedMember].setmeals : this.defaultPackageList
}
},
methods: {
handleCouponSelected(coupon) {
//
this.selectedCoupon = coupon
this.couponId = coupon.id
uni.showToast({
title: `已选择优惠券:¥${coupon.money}`,
icon: 'success'
})
},
async handleRecharge(){
// console.log('id:', this.packageList[this.selectedPackage]);
const object = {
// #ifdef H5
type : 'official',
// #endif
memberId: this.list[this.selectedMember].id,
setmealId: this.packageList[this.selectedPackage].id
}
if (this.couponId) {
object.couponId = this.couponId
}
const res = await this.$api.member.openMember({...object})
if (res.code === 200){
if (res.result === 0){
//
uni.showToast({
title: '充值成功',
icon: 'success'
})
this.goBack()
}else {
//
this.$utils.wxPay(res.result, (res) => {
setTimeout(() => {
this.goBack()
}, 1000)
})
}
}
},
goBack(){
uni.navigateBack()
},
handleCouponSelected(coupon) {
//
this.selectedCoupon = coupon
this.couponId = coupon.id
uni.showToast({
title: `已选择优惠券:¥${coupon.money}`,
icon: 'success'
})
},
async handleRecharge() {
// console.log('id:', this.packageList[this.selectedPackage]);
const object = {
// #ifdef H5
type: 'official',
// #endif
memberId: this.list[this.selectedMember].id,
setmealId: this.packageList[this.selectedPackage].id
}
if (this.couponId) {
object.couponId = this.couponId
}
const res = await this.$api.member.openMember({
...object
})
if (res.code === 200) {
if (res.result === 0) {
//
uni.showToast({
title: '充值成功',
icon: 'success'
})
this.goBack()
} else {
//
this.$utils.wxPay(res.result, (res) => {
setTimeout(() => {
this.goBack()
}, 1000)
})
}
}
},
goBack() {
uni.navigateBack()
},
//
getInt(pri){
const price = String(pri)
getInt(pri) {
const price = String(pri)
if (price.indexOf('.') === -1) {
return price
}
@ -228,11 +201,11 @@
}
return String(price).split('.')[0]
},
getDecimal(pri){
const price = String(pri)
getDecimal(pri) {
const price = String(pri)
if (price === null) return '00'
const parts = price.split('.')
return parts[1] ? parts[1].padEnd(2, '0') : '00'
return parts[1] ? parts[1].padEnd(2, '0') : '00'
},
selectPackage(index) {
this.selectedPackage = index
@ -242,294 +215,306 @@
url: '/subPages/user/discount?from=recharge'
})
},
async getMemberList(){
const memberRes = await this.$api.member.getMemberList()
if (memberRes.code === 200){
this.list = memberRes.result
}
},
changeSwiper(e){
this.selectedMember = e.current
this.selectedPackage = 0
}
async getMemberList() {
const memberRes = await this.$api.member.getMemberList()
if (memberRes.code === 200) {
this.list = memberRes.result
}
},
changeSwiper(e) {
this.selectedMember = e.current
this.selectedPackage = 0
}
},
onShow() {
//
uni.$on('couponSelected', this.handleCouponSelected)
this.getMemberList()
},
onUnload() {
//
uni.$off('couponSelected', this.handleCouponSelected)
console.log('接触监听');
},
onShow(){
//
uni.$on('couponSelected', this.handleCouponSelected)
this.getMemberList()
},
onUnload() {
//
uni.$off('couponSelected', this.handleCouponSelected)
console.log('接触监听');
},
}
</script>
<style lang="scss" scoped>
.container {
min-height: 100%;
}
.header{
width: 100%;
.header-bg{
position: relative;
width: 100%;
height: 500rpx;
/* #ifdef H5 */
margin-top: -100rpx;
/* #endif */
// background: red;
.header-img{
width: 100%;
height: 500rpx;
}
.header-title{
font-size: 32rpx;
color: black;
position: absolute;
top: 100rpx;
font-weight: 500;
left: 50%;
transform: translateX(-50%);
}
.header-icon{
position: absolute;
top: 100rpx;
left: 30rpx;
}
.swiper-container{
margin-top: -300rpx;
.swiper-arrow{
.container {
min-height: 100%;
}
.header {
width: 100%;
.header-bg {
position: relative;
width: 100%;
height: 500rpx;
/* #ifdef H5 */
margin-top: -100rpx;
/* #endif */
// background: red;
.header-img {
width: 100%;
height: 22rpx;
height: 500rpx;
}
.swiper-btn{
margin: 35rpx auto 15rpx;
width: 150rpx;
height: 52rpx;
border-radius: 999px;
background: #06DADC;
color: white;
font-size: 28rpx;
.header-title {
font-size: 32rpx;
color: black;
position: absolute;
top: 100rpx;
font-weight: 500;
left: 50%;
transform: translateX(-50%);
}
.header-icon {
position: absolute;
top: 100rpx;
left: 30rpx;
}
.swiper-container {
margin-top: -300rpx;
.swiper-arrow {
width: 100%;
height: 22rpx;
}
.swiper-btn {
margin: 35rpx auto 15rpx;
width: 150rpx;
height: 52rpx;
border-radius: 999px;
background: #06DADC;
color: white;
font-size: 28rpx;
font-weight: 500;
text-align: center;
line-height: 52rpx;
}
}
}
}
/* 立即开通会员按钮样式 */
.member-button-section {
margin: 0 50rpx 40rpx;
}
//
.membership-container {
background: linear-gradient(180deg, #DEFFFF 0%, #FBFEFF 22.65%, #FFFFFF 100%);
padding: 40rpx 30rpx;
margin-top: 20rpx;
.package-list {
display: flex;
justify-content: space-between;
gap: 16rpx;
margin-bottom: 20rpx;
.package-item {
position: relative;
flex: 1;
background: #fff;
border-radius: 20rpx;
text-align: center;
line-height: 52rpx;
border: 2rpx solid #EEEEEE;
transition: all 0.3s;
// width: 119;
height: 210rpx;
// width: 238rpx;
.info {
padding: 16rpx 16rpx 0 16rpx;
}
&.active {
border-color: $primary-color;
// box-shadow: 0 4rpx 20rpx rgba(34, 242, 235, 0.2);
}
.gift-tag {
position: absolute;
top: -10rpx;
left: 0%;
// transform: translateX(-50%);
background: #FF6B6B;
color: #fff;
font-size: 20rpx;
padding: 8rpx 16rpx;
border-radius: 20rpx 20rpx 20rpx 0;
white-space: nowrap;
}
.package-title {
font-size: 28rpx;
color: #000;
margin-bottom: 16rpx;
font-weight: 500;
}
.package-price {
font-size: 36rpx;
color: #FF4800;
font-weight: 500;
margin-bottom: 8rpx;
.package-decimal {
font-size: 24rpx;
}
}
.package-original {
font-size: 24rpx;
color: #8B8B8B;
line-height: 1.4;
text-decoration: line-through;
margin-bottom: 8rpx;
}
.package-btn {
background: #E4E7EB;
color: #191919;
font-size: 28rpx;
// padding: 12rpx 20rpx;
width: 100%;
// border-radius: 30rpx;
transition: all 0.3s;
height: 52rpx;
line-height: 52rpx;
border-radius: 0 0 24rpx 24rpx;
&.active {
background: $primary-color;
color: #fff;
}
}
}
}
}
}
/* 立即开通会员按钮样式 */
.member-button-section {
margin: 0 50rpx 40rpx;
}
//
.membership-container {
background: linear-gradient(180deg, #DEFFFF 0%, #FBFEFF 22.65%, #FFFFFF 100%);
padding: 40rpx 30rpx;
margin-top: 20rpx;
.package-list {
display: flex;
justify-content: space-between;
gap: 16rpx;
margin-bottom: 20rpx;
.package-item {
position: relative;
flex: 1;
background: #fff;
border-radius: 20rpx;
text-align: center;
border: 2rpx solid #EEEEEE;
transition: all 0.3s;
// width: 119;
height: 210rpx;
// width: 238rpx;
.info{
padding: 16rpx 16rpx 0 16rpx ;
.coupon-section {
.coupon-title {
font-size: 26rpx;
color: #181818;
margin-bottom: 10rpx;
// font-weight: 500;
}
&.active {
border-color: $primary-color;
// box-shadow: 0 4rpx 20rpx rgba(34, 242, 235, 0.2);
}
.gift-tag {
position: absolute;
top: -10rpx;
left: 0%;
// transform: translateX(-50%);
background: #FF6B6B;
color: #fff;
font-size: 20rpx;
padding: 8rpx 16rpx;
border-radius: 20rpx 20rpx 20rpx 0;
white-space: nowrap;
}
.package-title {
font-size: 28rpx;
color: #000;
margin-bottom: 16rpx;
font-weight: 500;
}
.package-price {
font-size: 36rpx;
color: #FF4800;
font-weight: 500;
margin-bottom: 8rpx;
.package-decimal{
font-size: 24rpx;
.coupon-selector {
background: #fff;
border-radius: 16rpx;
padding: 10rpx 0;
display: flex;
justify-content: space-between;
align-items: center;
border-bottom: 2rpx solid #f0f0f0;
.coupon-placeholder {
display: flex;
justify-content: space-between;
align-items: center;
width: 100%;
.coupon-text {
font-size: 32rpx;
color: #C6C6C6;
}
}
}
.package-original {
font-size: 24rpx;
color: #8B8B8B;
line-height: 1.4;
text-decoration: line-through;
margin-bottom: 8rpx;
}
.package-btn {
background: #E4E7EB;
color: #191919;
font-size: 28rpx;
// padding: 12rpx 20rpx;
width: 100%;
// border-radius: 30rpx;
transition: all 0.3s;
height: 52rpx;
line-height: 52rpx;
border-radius: 0 0 24rpx 24rpx;
&.active {
background: $primary-color;
color: #fff;
}
}
}
}
.coupon-section {
.coupon-title {
font-size: 26rpx;
color: #181818;
margin-bottom: 10rpx;
// font-weight: 500;
}
.coupon-selector {
background: #fff;
border-radius: 16rpx;
padding: 10rpx 0;
display: flex;
justify-content: space-between;
align-items: center;
border-bottom: 2rpx solid #f0f0f0;
.coupon-placeholder {
display: flex;
justify-content: space-between;
align-items: center;
width: 100%;
.coupon-text {
font-size: 32rpx;
color: #C6C6C6;
}
}
.coupon-selected {
display: flex;
justify-content: space-between;
align-items: center;
width: 100%;
.coupon-info {
display: flex;
// flex-direction: column;
align-items: center;
justify-content: space-between;
.coupon-name {
font-size: 28rpx;
color: #181818;
margin-bottom: 4rpx;
}
.coupon-amount {
font-size: 28rpx;
color: red;
font-weight: 500;
}
}
}
}
}
}
/* 会员权益样式 */
.benefits-section {
margin-top: 40rpx;
padding: 0 30rpx;
}
.benefits-title {
font-size: 36rpx;
font-weight: bold;
color: #191919;
margin-bottom: 32rpx;
}
.benefits-list {
display: flex;
flex-direction: column;
gap: 32rpx;
}
.benefit-item {
background: #F8F8F8;
border: 1px solid #FFFFFF;
border-radius: 48rpx;
padding: 27rpx 40rpx;
display: flex;
align-items: center;
justify-content: space-between;
}
.benefit-content {
flex: 1;
margin-right: 40rpx;
}
.benefit-title {
font-size: 32rpx;
font-weight: 600;
color: #333;
margin-bottom: 16rpx;
}
.benefit-desc {
font-size: 24rpx;
color: #09B1B3;
line-height: 36rpx;
}
.benefit-icon {
width: 152rpx;
height: 152rpx;
// flex-shrink: 0;
}
.benefit-icon image {
width: 100%;
height: 100%;
}
.coupon-selected {
display: flex;
justify-content: space-between;
align-items: center;
width: 100%;
.coupon-info {
display: flex;
// flex-direction: column;
align-items: center;
justify-content: space-between;
.coupon-name {
font-size: 28rpx;
color: #181818;
margin-bottom: 4rpx;
}
.coupon-amount {
font-size: 28rpx;
color: red;
font-weight: 500;
}
}
}
}
}
}
/* 会员权益样式 */
.benefits-section {
margin-top: 40rpx;
padding: 0 30rpx;
}
.benefits-title {
font-size: 36rpx;
font-weight: bold;
color: #191919;
margin-bottom: 32rpx;
}
.benefits-list {
display: flex;
flex-direction: column;
gap: 32rpx;
}
.benefit-item {
background: #F8F8F8;
border: 1px solid #FFFFFF;
border-radius: 48rpx;
padding: 27rpx 40rpx;
display: flex;
align-items: center;
justify-content: space-between;
}
.benefit-content {
flex: 1;
margin-right: 40rpx;
}
.benefit-title {
font-size: 32rpx;
font-weight: 600;
color: #333;
margin-bottom: 16rpx;
}
.benefit-desc {
font-size: 24rpx;
color: #09B1B3;
line-height: 36rpx;
}
.benefit-icon {
width: 152rpx;
height: 152rpx;
// flex-shrink: 0;
}
.benefit-icon image {
width: 100%;
height: 100%;
}
</style>

+ 64
- 3
subPages/user/profile.vue View File

@ -40,6 +40,7 @@
<text class="required">*</text>
<text>头像</text>
</view>
<!-- #ifdef MP-WEIXIN -->
<button class="avatar-container" open-type="chooseAvatar" @chooseavatar="onChooseAvatar">
<view
v-if="userInfo.avatar === 'undefined'"
@ -55,6 +56,25 @@
></image>
</button>
<!-- #endif -->
<!-- #ifndef MP-WEIXIN -->
<button class="avatar-container" @click="chooseImageH5">
<view
v-if="userInfo.avatar === 'undefined'"
class="avatar-image"
>
<uv-icon name="camera" size="40" color="white"></uv-icon>
</view>
<image
v-else
:src="userInfo.avatar || '/static/default-avatar.png'"
class="avatar-image"
mode="aspectFill"
></image>
</button>
<!-- #endif -->
</view>
</view>
@ -136,7 +156,7 @@ export default {
uni.hideLoading();
console.error('头像上传异常:', error);
// 使
this.userInfo.avatar = e.detail.avatarUrl;
// this.userInfo.avatar = e.detail.avatarUrl;
uni.showToast({
title: '头像处理异常,使用本地头像',
icon: 'none'
@ -149,10 +169,51 @@ export default {
});
}
},
// /H5
// #ifndef MP-WEIXIN
async chooseImageH5() {
try {
const res = await uni.chooseImage({
count: 1,
sizeType: ['compressed'],
sourceType: ['album', 'camera']
})
const filePath = (res.tempFilePaths && res.tempFilePaths[0])
|| (res.tempFiles && res.tempFiles[0] && (res.tempFiles[0].path || res.tempFiles[0].tempFilePath))
if (!filePath) {
uni.showToast({ title: '未选择图片', icon: 'none' })
return
}
uni.showLoading({ title: '上传头像中...' })
const file = { path: filePath, tempFilePath: filePath }
const uploadResult = await this.$utils.uploadImage(file)
uni.hideLoading()
if (uploadResult && uploadResult.success) {
this.userInfo.avatar = uploadResult.url
uni.showToast({ title: '头像上传成功', icon: 'success' })
} else {
//
this.userInfo.avatar = filePath
uni.showToast({ title: '头像已选择', icon: 'none' })
}
} catch (error) {
uni.hideLoading()
console.error('选择/上传头像异常:', error)
uni.showToast({ title: '头像处理异常', icon: 'none' })
}
},
// #endif
//
async saveProfile() {
if (!this.userInfo.name.trim()) {
if (!this.userInfo.name?.trim()) {
uni.showToast({
title: '请输入昵称',
icon: 'none'
@ -160,7 +221,7 @@ export default {
return
}
if (!this.userInfo.phone.trim()) {
if (!this.userInfo.phone?.trim()) {
uni.showToast({
title: '请输入手机号',
icon: 'none'


+ 5
- 1
subPages/user/share.vue View File

@ -162,7 +162,11 @@ export default {
async getQrcode() {
this.isLoading = true
uni.getImageInfo({
src: `${this.$config.baseURL}/promotion/qrCode?token=${uni.getStorageSync('token')}`,
src: `${this.$config.baseURL}/promotion/qrCode?token=${uni.getStorageSync('token')}`
// #ifdef H5
+ '&type=official'
// #endif
,
success: (image) => {
this.Qrcode = image.path;


+ 12
- 12
utils/share.js View File

@ -37,21 +37,21 @@ function share() { //微信分享
'checkJsApi',
'scanQRCode',
],
success: function() {
console.log('配置成功');
}
success: function () {
console.log('配置成功');
}
});
jWeixin.ready(function() {
jWeixin.ready(function () {
// 微信分享的数据
var shareData = {
"link": addQueryParams(data.url),
"desc": "四零语境",
"title": "四零语境,温柔呵护每一刻!",
imgUrl : location.href.split('#')[0] + '/static/share/logo.png',
success: function() {
imgUrl: location.href.split('#')[0] + '/static/share/logo.png',
success: function () {
//分享成功可以做相应的数据处理
console.log('注册分享成功');
console.log('注册分享成功');
// uni.showToast({
// mask: true,
// duration: 1000,
@ -66,10 +66,10 @@ function share() { //微信分享
//分享到微博内容设置
jWeixin.onMenuShareWeibo(shareData);
});
jWeixin.error(function(err){
jWeixin.error(function (err) {
console.error(err);
// config信息验证失败会执行error函数,如签名过期导致验证失败,具体错误信息可以打开config的debug模式查看,也可以在返回的res参数中查看,对于SPA可以在这里更新签名。
// config信息验证失败会执行error函数,如签名过期导致验证失败,具体错误信息可以打开config的debug模式查看,也可以在返回的res参数中查看,对于SPA可以在这里更新签名。
})
}
})
@ -79,8 +79,8 @@ function addQueryParams(url) {
if (url) {
//获取用户id
let userInfo = localStorage.getItem('userInfo') ? JSON.parse(localStorage.getItem('userInfo')) : null
if(userInfo){
url += `?vid=${userInfo.id}`
if (userInfo) {
url += `?inviter=${userInfo.id}`
}
}
return url


Loading…
Cancel
Save