consumer-app/pages/profile/profile.vue

652 lines
16 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<template>
<view class="profile-page">
<view class="header" :style="{ paddingTop: statusBarHeight + 'px' }">
<text class="header-text">个人中心</text>
</view>
<!-- 用户资料卡片 -->
<view
class="user-card"
:style="{
backgroundImage: userInfo.level.backgroundUrl
? `url(${userInfo.level.backgroundUrl})`
: 'url(/static/profile/member-card-bg.png)',
}"
>
<view class="user-info">
<view class="user-level">{{ userInfo.level.name }}</view>
<!-- #ifdef MP-WEIXIN -->
<button
class="avatar-btn"
open-type="chooseAvatar"
@chooseavatar="handleChooseAvatar"
>
<image
class="avatar"
:src="userInfo.avatar || '/static/tabbar/profile.png'"
mode="aspectFill"
></image>
</button>
<!-- #endif -->
<!-- #ifndef MP-WEIXIN -->
<view class="avatar-wrapper" @click="handleChooseAvatar">
<image
class="avatar"
:src="userInfo.avatar || '/static/tabbar/profile.png'"
mode="aspectFill"
></image>
</view>
<!-- #endif -->
<image
src="/static/profile/avatar-hg.png"
mode="aspectFill"
class="avatar-hg"
></image>
<view class="user-details">
<text class="login-method">{{
userInfo.nickname || "微信登录"
}}</text>
<text class="user-id">NO.{{ userInfo.id }}</text>
</view>
</view>
<view class="member-benefits-btn" @click="goToMemberBenefits">
<image :src="userInfo.level.icon" mode="aspectFill"></image>
</view>
<view class="user-notice">
<!-- <text class="user-notice-text">{{ noticeNum }}</text> -->
<image src="/static/profile/user-notice.png" mode="aspectFill"></image>
</view>
</view>
<!-- 功能卡片 -->
<view class="action-cards">
<view class="action-card real-name-auth" @tap="goToRealNameAuth">
<view class="card-content">
<text class="card-title">实名认证</text>
<text class="card-desc">请完善身份信息</text>
</view>
</view>
<view class="action-card service-records" @tap="goToServiceRecords">
<view class="card-content">
<text class="card-title">服务记录</text>
<text class="card-desc">查看服务记录</text>
</view>
</view>
</view>
<!-- 菜单列表 -->
<view class="menu-list">
<view class="menu-item" @tap="goToFavorites">
<view class="menu-icon"
><image
src="/static/profile/my-favorite.png"
mode="aspectFill"
></image
></view>
<text class="menu-text">我的收藏</text>
<text class="menu-arrow"></text>
</view>
<view class="menu-item" @tap="goToComplaints">
<view class="menu-icon"
><image src="/static/profile/complaints.png" mode="aspectFill"></image
></view>
<text class="menu-text">投诉建议</text>
<text class="menu-arrow"></text>
</view>
<view class="menu-item" @tap="goToPostMessage">
<view class="menu-icon"
><image
src="/static/profile/publish-message.png"
mode="aspectFill"
></image
></view>
<text class="menu-text">发布消息</text>
<text class="menu-arrow"></text>
</view>
</view>
<!-- 退出登录按钮 -->
<view class="logout-section">
<button class="logout-btn" @click="handleLogout">退</button>
</view>
</view>
</template>
<script>
import { getUserInfo } from "@/api/profile.js";
export default {
data() {
return {
statusBarHeight: 0,
navBarHeight: 0,
currentTime: "10:55",
userInfo: {
level: {}, // 防止 level 未定义时报错
},
noticeNum: 3,
uploading: false, // 是否正在上传头像
};
},
onLoad() {
this.getSystemInfo();
this.updateTime();
// 先加载本地存储的用户信息,然后刷新
// this.loadUserInfo();
},
onShow() {
// 每次显示页面时刷新用户信息
this.loadUserInfo();
},
methods: {
// 获取系统信息
getSystemInfo() {
// 获取系统信息
const systemInfo = uni.getSystemInfoSync();
// 获取状态栏高度,如果获取不到则根据平台设置默认值
// iOS通常为44pxiPhone X及以后或20pxiPhone 8及以前
// Android通常为24px或更高
let defaultHeight = 20;
// #ifdef APP-PLUS
if (systemInfo.platform === "ios") {
// iPhone X及以后机型状态栏高度为44px
defaultHeight = systemInfo.screenHeight >= 812 ? 44 : 20;
} else {
defaultHeight = 24;
}
// #endif
// #ifdef H5
defaultHeight = 0; // H5环境通常不需要状态栏高度
// #endif
// #ifdef MP
defaultHeight = systemInfo.statusBarHeight || 20; // 小程序环境
// #endif
this.statusBarHeight = systemInfo.statusBarHeight || defaultHeight;
const navBarContentHeight = 44; // 导航栏内容高度,可根据设计稿调整
this.navBarHeight = this.statusBarHeight + navBarContentHeight;
console.log(
"状态栏高度:",
this.statusBarHeight,
"平台:",
systemInfo.platform
);
},
updateTime() {
const now = new Date();
const hours = String(now.getHours()).padStart(2, "0");
const minutes = String(now.getMinutes()).padStart(2, "0");
this.currentTime = `${hours}:${minutes}`;
// 每分钟更新一次时间
setInterval(() => {
const now = new Date();
const hours = String(now.getHours()).padStart(2, "0");
const minutes = String(now.getMinutes()).padStart(2, "0");
this.currentTime = `${hours}:${minutes}`;
}, 60000);
},
async loadUserInfo() {
try {
// 先尝试从本地存储获取,立即显示(避免等待接口响应)
const localUserInfo = uni.getStorageSync("userInfo");
if (localUserInfo) {
this.userInfo = localUserInfo;
}
// 调用接口获取最新用户信息(登录时已调用,这里主要是刷新)
const res = await getUserInfo();
// 更新用户信息
this.userInfo = res;
// 保存到本地存储
uni.setStorageSync("userInfo", this.userInfo);
} catch (error) {
console.error("获取用户信息失败:", error);
// 如果接口调用失败,使用本地存储的数据(登录时已保存)
const localUserInfo = uni.getStorageSync("userInfo");
if (localUserInfo) {
this.userInfo = localUserInfo;
}
}
},
// 会员权益
goToMemberBenefits() {
uni.navigateTo({
url: "/pages/profileSub/pointsMemberRules",
});
},
goToRealNameAuth() {
uni.navigateTo({
url: "/pages/profileSub/realNameAuth",
});
},
goToServiceRecords() {
uni.navigateTo({
url: "/pages/profileSub/serviceRecords",
});
},
goToFavorites() {
uni.navigateTo({
url: "/pages/activities/myCollect",
});
},
goToComplaints() {
uni.navigateTo({
url: "/pages/activities/complaints",
});
},
goToPostMessage() {
uni.navigateTo({
url: "/pages/activities/postMessage",
});
},
handleLogout() {
uni.showModal({
title: "提示",
content: "确定要退出登录吗?",
success: (res) => {
if (res.confirm) {
// 清除所有登录信息
uni.removeStorageSync("token");
uni.removeStorageSync("refreshToken");
uni.removeStorageSync("tokenExpiresTime");
uni.removeStorageSync("userId");
uni.removeStorageSync("userInfo");
uni.reLaunch({
url: "/pages/index/index",
});
}
},
});
},
// 选择头像(微信小程序使用 chooseAvatar其他平台使用 chooseImage
handleChooseAvatar(e) {
// #ifdef MP-WEIXIN
// 微信小程序新版本使用 chooseAvatar
if (e.detail && e.detail.avatarUrl) {
const avatarUrl = e.detail.avatarUrl;
// 先显示本地预览
this.userInfo.avatar = avatarUrl;
// 上传头像
this.uploadAvatar(avatarUrl);
}
// #endif
// #ifndef MP-WEIXIN
// 其他平台使用 chooseImage
uni.chooseImage({
count: 1,
sizeType: ["compressed"],
sourceType: ["album", "camera"],
success: (res) => {
const tempFilePath = res.tempFilePaths[0];
// 先显示本地预览
this.userInfo.avatar = tempFilePath;
// 上传头像
this.uploadAvatar(tempFilePath);
},
fail: (err) => {
console.error("选择图片失败:", err);
uni.showToast({
title: "选择图片失败",
icon: "none",
});
},
});
// #endif
},
// 上传头像
uploadAvatar(filePath) {
if (this.uploading) {
return;
}
this.uploading = true;
uni.showLoading({
title: "上传中...",
mask: true,
});
// 获取token
const token = uni.getStorageSync("token");
const BASE_URL = "https://guangsh.manage.hschengtai.com";
uni.uploadFile({
url: `${BASE_URL}/app-api/infra/file/upload`,
filePath: filePath,
name: "file",
formData: {
directory: "avatar", // 头像目录
},
header: {
Authorization: `Bearer ${token}`,
"tenant-id": "1",
},
success: (res) => {
uni.hideLoading();
try {
const data = JSON.parse(res.data);
if (data.code === 200 || data.code === 0) {
// 上传成功获取图片URL
const imageUrl = data.data?.url || data.data || data.url;
if (imageUrl) {
// 更新用户信息中的头像
this.userInfo.avatar = imageUrl;
// 保存到本地存储
uni.setStorageSync("userInfo", this.userInfo);
// 调用更新用户信息接口(如果有的话)
this.updateUserAvatar(imageUrl);
uni.showToast({
title: "头像上传成功",
icon: "success",
duration: 1500,
});
} else {
throw new Error("上传成功但未返回图片地址");
}
} else {
throw new Error(data.message || data.msg || "上传失败");
}
} catch (error) {
console.error("解析上传结果失败:", error);
uni.showToast({
title: error.message || "上传失败,请重试",
icon: "none",
});
// 上传失败,恢复原头像
this.loadUserInfo();
}
},
fail: (err) => {
uni.hideLoading();
console.error("上传头像失败:", err);
uni.showToast({
title: "上传失败,请检查网络",
icon: "none",
});
// 上传失败,恢复原头像
this.loadUserInfo();
},
complete: () => {
this.uploading = false;
},
});
},
// 更新用户头像(如果有更新用户信息的接口)
async updateUserAvatar(avatarUrl) {
try {
// 这里可以调用更新用户信息的接口
// 例如await updateUserInfo({ avatar: avatarUrl });
// 暂时只更新本地存储,如果后端有更新接口可以在这里调用
console.log("头像已更新:", avatarUrl);
} catch (error) {
console.error("更新用户头像失败:", error);
// 即使更新失败,本地已经保存了,不影响使用
}
},
},
};
</script>
<style lang="scss" scoped>
.profile-page {
min-height: 100vh;
background: radial-gradient(0% 0% at 0% 0%, #ffffff 0%, #e2e8f1 100%);
padding-bottom: 120rpx;
position: relative;
}
/* 自定义导航栏 */
.header {
display: flex;
align-items: center;
justify-content: center;
box-sizing: border-box;
.header-text {
font-family: PingFang-SC, PingFang-SC;
font-weight: bold;
font-size: 34rpx;
color: #000000;
height: 88rpx;
line-height: 88rpx;
}
}
/* 用户资料卡片 */
.user-card {
margin: 10rpx 10rpx 22rpx 10rpx;
height: 289rpx;
background-size: 100% 100%;
background-repeat: no-repeat;
position: relative;
.user-info {
display: flex;
align-items: center;
padding-top: 89rpx;
padding-left: 23rpx;
.user-level {
position: absolute;
top: 9rpx;
left: 28rpx;
font-family: PingFang-SC, PingFang-SC;
font-weight: bold;
font-size: 28rpx;
color: #fdd7b2;
}
.avatar-btn {
width: 109rpx;
height: 108rpx;
margin-right: 24rpx;
padding: 0;
background: transparent;
border: none;
line-height: 1;
&::after {
border: none;
}
.avatar {
width: 100%;
height: 100%;
border-radius: 50%;
}
}
.avatar-wrapper {
width: 109rpx;
height: 108rpx;
margin-right: 24rpx;
.avatar {
width: 100%;
height: 100%;
border-radius: 50%;
}
}
.avatar {
width: 109rpx;
height: 108rpx;
margin-right: 24rpx;
border-radius: 50%;
}
.avatar-hg {
width: 51rpx;
height: 54rpx;
position: absolute;
top: 72rpx;
left: 103rpx;
}
.user-details {
flex: 1;
display: flex;
flex-direction: column;
gap: 12rpx;
.login-method {
font-family: PingFang-SC, PingFang-SC;
font-weight: bold;
font-size: 32rpx;
color: #333333;
}
.user-id {
font-family: PingFang-SC, PingFang-SC;
font-weight: 500;
font-size: 20rpx;
color: #999999;
}
}
}
.member-benefits-btn {
position: absolute;
right: 50rpx;
bottom: 24rpx;
width: 175rpx;
height: 42rpx;
image {
width: 100%;
height: 100%;
}
}
.user-notice {
position: absolute;
right: 44rpx;
top: 31rpx;
.user-notice-text {
display: block;
width: 22rpx;
height: 22rpx;
background: #d51c3c;
border: 2rpx solid #e2e8f1;
font-size: 20rpx;
color: #ffffff;
text-align: center;
line-height: 22rpx;
border-radius: 50%;
position: absolute;
top: 0;
left: 0;
}
image {
width: 24rpx;
height: 29rpx;
}
}
}
/* 功能卡片 */
.action-cards {
display: flex;
gap: 30rpx;
margin: 0 20rpx 27rpx 20rpx;
}
.action-card {
flex: 1;
display: flex;
align-items: center;
background-size: 100% 100%;
background-repeat: no-repeat;
background-position: center center;
height: 124rpx;
.card-content {
padding: 41rpx 36rpx;
display: flex;
flex-direction: column;
gap: 8rpx;
.card-title {
font-family: PingFang-SC, PingFang-SC;
font-weight: bold;
font-size: 28rpx;
color: #3c454c;
}
.card-desc {
font-family: PingFang-SC, PingFang-SC;
font-weight: 500;
font-size: 22rpx;
color: #ba9666;
}
}
}
.real-name-auth {
background-image: url("https://resource2.ctshenglong.cn/20251219/实名认证_1766107765758.png");
}
.service-records {
background-image: url("https://resource2.ctshenglong.cn/20251219/服务记录_1766107759755.png");
}
/* 菜单列表 */
.menu-list {
margin: 0 20rpx 20rpx;
}
.menu-item {
display: flex;
align-items: center;
padding: 29rpx 36rpx 28rpx 20rpx;
background-color: #ffffff;
border-radius: 20rpx;
margin-bottom: 11rpx;
.menu-icon {
width: 43rpx;
height: 43rpx;
margin-right: 24rpx;
image {
width: 100%;
height: 100%;
}
}
.menu-text {
flex: 1;
font-size: 32rpx;
color: #333333;
}
.menu-arrow {
font-size: 40rpx;
color: #cccccc;
line-height: 1;
}
}
/* 退出登录 */
.logout-section {
margin-top: 132rpx;
}
.logout-btn {
width: 85%;
height: 80rpx;
background: #004294;
border-radius: 39rpx;
font-family: PingFang-SC, PingFang-SC;
font-weight: bold;
font-size: 28rpx;
color: #ffffff;
border: none;
display: flex;
align-items: center;
justify-content: center;
}
</style>