consumer-app/pages/profile/profile.vue

1099 lines
30 KiB
Vue
Raw Normal View History

2025-12-19 12:27:55 +00:00
<template>
<view class="profile-page">
2026-03-09 03:37:41 +00:00
<!-- 头部固定滑动时不动 -->
<view
class="header header-fixed"
:style="{ paddingTop: statusBarHeight + 'px' }"
>
2025-12-19 12:27:55 +00:00
<text class="header-text">个人中心</text>
</view>
2026-03-09 03:37:41 +00:00
<!-- 主体内容预留头部高度 -->
<view class="profile-main" :style="{ paddingTop: navBarHeight + 'px' }">
2025-12-19 12:27:55 +00:00
<!-- 用户资料卡片 -->
<view
class="user-card"
2026-03-05 11:04:23 +00:00
:style="{ backgroundImage: userCardBgImage }"
2025-12-19 12:27:55 +00:00
>
<view class="user-info">
2026-03-02 07:32:41 +00:00
<view class="user-level">{{ userInfo.level.name }}</view>
2026-01-13 04:12:48 +00:00
<!-- #ifdef MP-WEIXIN -->
2026-03-02 07:32:41 +00:00
<button
class="avatar-btn"
open-type="chooseAvatar"
@chooseavatar="handleChooseAvatar"
>
2026-01-13 04:12:48 +00:00
<image
class="avatar"
2026-03-05 11:04:23 +00:00
:src="avatarSrc"
2026-01-13 04:12:48 +00:00
mode="aspectFill"
></image>
</button>
<!-- #endif -->
<!-- #ifndef MP-WEIXIN -->
<view class="avatar-wrapper" @click="handleChooseAvatar">
<image
class="avatar"
2026-03-05 11:04:23 +00:00
:src="avatarSrc"
2026-01-13 04:12:48 +00:00
mode="aspectFill"
></image>
</view>
<!-- #endif -->
2025-12-19 12:27:55 +00:00
<image
src="/static/profile/avatar-hg.png"
mode="aspectFill"
class="avatar-hg"
></image>
<view class="user-details">
2026-03-05 11:04:23 +00:00
<!-- #ifdef MP-WEIXIN -->
<input
type="nickname"
class="nickname-input"
:value="userInfo.nickname || ''"
placeholder="点击设置微信昵称"
@blur="handleNicknameBlur"
/>
<!-- #endif -->
<!-- #ifndef MP-WEIXIN -->
<text
class="login-method"
@click="handleNicknameClickNonWeixin"
>{{ userInfo.nickname || "微信登录" }}</text>
<!-- #endif -->
2025-12-19 12:27:55 +00:00
<text class="user-id">NO.{{ userInfo.id }}</text>
</view>
</view>
2026-03-09 03:37:41 +00:00
<view class="invite-btn" @click="goToInvite">
<image src="https://resource2.ctshenglong.cn/20260309/yaoqingma_btn_1773020094853.png" mode="aspectFill"></image>
</view>
2025-12-19 12:27:55 +00:00
<view class="member-benefits-btn" @click="goToMemberBenefits">
2026-03-05 11:04:23 +00:00
<image :src="memberLevelIcon" mode="aspectFill"></image>
2025-12-19 12:27:55 +00:00
</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">
2026-01-13 04:12:48 +00:00
<view class="action-card real-name-auth" @tap="goToRealNameAuth">
2025-12-19 12:27:55 +00:00
<view class="card-content">
<text class="card-title">实名认证</text>
<text class="card-desc">请完善身份信息</text>
</view>
</view>
2026-01-13 04:12:48 +00:00
<view class="action-card service-records" @tap="goToServiceRecords">
2025-12-19 12:27:55 +00:00
<view class="card-content">
<text class="card-title">服务记录</text>
<text class="card-desc">查看服务记录</text>
</view>
</view>
</view>
<!-- 菜单列表 -->
<view class="menu-list">
2026-01-13 04:12:48 +00:00
<view class="menu-item" @tap="goToFavorites">
2025-12-19 12:27:55 +00:00
<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>
2026-01-13 04:12:48 +00:00
<view class="menu-item" @tap="goToComplaints">
2025-12-19 12:27:55 +00:00
<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>
2026-01-13 04:12:48 +00:00
<view class="menu-item" @tap="goToPostMessage">
2025-12-19 12:27:55 +00:00
<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>
2026-03-09 03:37:41 +00:00
</view>
<!-- 邀请海报弹窗图5样式 -->
<view class="invite-modal-mask" v-if="inviteModalVisible" @click="closeInviteModal"></view>
<view class="invite-modal-wrap" v-if="inviteModalVisible" @click.stop>
<view class="invite-poster" id="invite-poster">
<view class="invite-poster-close" @click="closeInviteModal">×</view>
<view class="poster-inner">
<view class="poster-tag">邀请好友</view>
<view class="poster-qr-wrap" v-if="inviteQRCodeUrl">
<view class="poster-qr-outer">
<image class="poster-qr-image" :src="inviteQRCodeUrl" mode="aspectFit"></image>
</view>
<text class="poster-qr-tip">长按识别图中二维码</text>
</view>
</view>
</view>
<view class="poster-actions">
<view class="poster-btn poster-btn-cancel" @click="closeInviteModal"></view>
<view class="poster-btn poster-btn-save" @click="savePosterImage"></view>
</view>
<!-- 用于导出海报的 canvas隐藏 -->
<canvas
class="poster-canvas"
type="2d"
id="poster-canvas"
:style="{ width: posterCanvasWidth + 'px', height: posterCanvasHeight + 'px' }"
></canvas>
</view>
2025-12-19 12:27:55 +00:00
</view>
</template>
<script>
2026-03-09 03:37:41 +00:00
import { getUserInfo, getInviteQRCode } from "@/api/profile.js";
2025-12-19 12:27:55 +00:00
export default {
data() {
return {
statusBarHeight: 0,
navBarHeight: 0,
currentTime: "10:55",
2026-01-13 04:12:48 +00:00
userInfo: {
2026-03-02 07:32:41 +00:00
level: {}, // 防止 level 未定义时报错
2026-01-13 04:12:48 +00:00
},
2025-12-19 12:27:55 +00:00
noticeNum: 3,
2026-01-13 04:12:48 +00:00
uploading: false, // 是否正在上传头像
2026-03-09 03:37:41 +00:00
inviteModalVisible: false,
inviteQRCodeUrl: "",
inviteCodeStr: "", // 邀请码,格式:邀请类型-用户id如 0-123
posterCanvasWidth: 680,
posterCanvasHeight: 860,
posterSaving: false,
2025-12-19 12:27:55 +00:00
};
},
2026-03-05 11:04:23 +00:00
computed: {
avatarSrc() {
const v = this.userInfo.avatar;
return (v && typeof v === "string") ? v : "/static/tabbar/profile.png";
},
memberLevelIcon() {
const icon = this.userInfo.level && this.userInfo.level.icon;
return (icon && typeof icon === "string") ? icon : "/static/tabbar/profile.png";
},
userCardBgImage() {
const url = this.userInfo.level && this.userInfo.level.backgroundUrl;
return (url && typeof url === "string")
? `url(${url})`
: "url(/static/profile/member-card-bg.png)";
},
},
2025-12-19 12:27:55 +00:00
onLoad() {
this.getSystemInfo();
this.updateTime();
// 先加载本地存储的用户信息,然后刷新
2026-01-13 04:12:48 +00:00
// this.loadUserInfo();
2025-12-19 12:27:55 +00:00
},
onShow() {
2026-01-13 04:12:48 +00:00
// 每次显示页面时刷新用户信息
this.loadUserInfo();
2025-12-19 12:27:55 +00:00
},
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;
}
}
},
2026-03-09 03:37:41 +00:00
// 邀请好友:弹出邀请二维码。邀请码格式 "邀请类型-用户id",扫码后打开小程序登录注册页,登录接口 /app-api/member/auth/weixin-mini-app-login 使用 inviteCode
// 邀请类型0=会员邀请会员1=系统用户邀请会员。当前为会员邀请,故用 0-用户id
async goToInvite() {
const uid = this.userInfo && this.userInfo.id;
if (!uid) {
uni.showToast({ title: "请先登录", icon: "none" });
return;
}
const inviteCodeStr = `0-${uid}`;
this.inviteCodeStr = inviteCodeStr;
this.inviteModalVisible = true;
this.inviteQRCodeUrl = "";
try {
const url = await getInviteQRCode(inviteCodeStr);
if (url) {
this.inviteQRCodeUrl = url;
}
} catch (e) {
console.error("获取邀请二维码失败:", e);
}
},
closeInviteModal() {
this.inviteModalVisible = false;
},
// 保存海报图片到相册按图5样式绘制 canvas 后导出)
async savePosterImage() {
if (this.posterSaving || !this.inviteQRCodeUrl) return;
this.posterSaving = true;
uni.showLoading({ title: "生成中...", mask: true });
const dpr = uni.getSystemInfoSync().pixelRatio || 2;
const w = this.posterCanvasWidth;
const h = this.posterCanvasHeight;
const padding = 48;
const qrSize = 260;
const qrTop = 220;
const avatarSize = 68;
try {
const query = uni.createSelectorQuery().in(this);
query
.select("#poster-canvas")
.fields({ node: true, size: true })
.exec(async (res) => {
if (!res || !res[0] || !res[0].node) {
uni.hideLoading();
this.posterSaving = false;
uni.showToast({ title: "生成失败,请重试", icon: "none" });
return;
}
const canvas = res[0].node;
const ctx = canvas.getContext("2d");
canvas.width = w * dpr;
canvas.height = h * dpr;
ctx.scale(dpr, dpr);
// 白底圆角
ctx.fillStyle = "#ffffff";
const r = 16;
ctx.beginPath();
ctx.moveTo(padding + r, padding);
ctx.lineTo(w - padding - r, padding);
ctx.quadraticCurveTo(w - padding, padding, w - padding, padding + r);
ctx.lineTo(w - padding, h - padding - r);
ctx.quadraticCurveTo(w - padding, h - padding, w - padding - r, h - padding);
ctx.lineTo(padding + r, h - padding);
ctx.quadraticCurveTo(padding, h - padding, padding, h - padding - r);
ctx.lineTo(padding, padding + r);
ctx.quadraticCurveTo(padding, padding, padding + r, padding);
ctx.closePath();
ctx.fill();
// 文案
ctx.fillStyle = "#333333";
ctx.font = "bold 14px PingFang SC, sans-serif";
ctx.textAlign = "center";
const nickname = (this.userInfo.nickname || "微信用户").slice(0, 20);
ctx.font = "bold 16px PingFang SC, sans-serif";
ctx.fillText(nickname, w / 2, 96);
ctx.font = "13px PingFang SC, sans-serif";
ctx.fillStyle = "#666666";
ctx.fillText("长按识别图中二维码", w / 2, qrTop + qrSize + 44);
const drawImage = (src) => {
return new Promise((resolve, reject) => {
const img = canvas.createImage();
img.onload = () => resolve(img);
img.onerror = () => reject(new Error("图片加载失败"));
img.src = src;
});
};
try {
const qrImg = await drawImage(this.inviteQRCodeUrl);
const qrX = (w - qrSize) / 2;
ctx.save();
ctx.beginPath();
ctx.arc(w / 2, qrTop + qrSize / 2, qrSize / 2, 0, Math.PI * 2);
ctx.closePath();
ctx.clip();
ctx.drawImage(qrImg, qrX, qrTop, qrSize, qrSize);
ctx.restore();
const avatarSrc = this.avatarSrc;
const avatarImg = await drawImage(avatarSrc);
const ax = (w - avatarSize) / 2;
const ay = qrTop + (qrSize - avatarSize) / 2;
ctx.save();
ctx.beginPath();
ctx.arc(w / 2, qrTop + qrSize / 2, avatarSize / 2, 0, Math.PI * 2);
ctx.closePath();
ctx.clip();
ctx.drawImage(avatarImg, ax, ay, avatarSize, avatarSize);
ctx.restore();
} catch (imgErr) {
console.warn("海报图片绘制跳过:", imgErr);
}
setTimeout(() => {
uni.createSelectorQuery()
.in(this)
.select("#poster-canvas")
.fields({ node: true, size: true })
.exec((r2) => {
if (!r2 || !r2[0] || !r2[0].node) {
uni.hideLoading();
this.posterSaving = false;
uni.showToast({ title: "导出失败", icon: "none" });
return;
}
const node = r2[0].node;
uni.canvasToTempFilePath({
canvas: node,
fileType: "png",
quality: 1,
success: (pathRes) => {
uni.saveImageToPhotosAlbum({
filePath: pathRes.tempFilePath,
success: () => {
uni.hideLoading();
this.posterSaving = false;
uni.showToast({ title: "已保存到相册", icon: "success" });
},
fail: (err) => {
uni.hideLoading();
this.posterSaving = false;
if (err.errMsg && err.errMsg.indexOf("auth") !== -1) {
uni.showModal({
title: "提示",
content: "需要您授权保存图片到相册",
confirmText: "去设置",
success: (s) => {
if (s.confirm) uni.openSetting();
},
});
} else {
uni.showToast({ title: "保存失败", icon: "none" });
}
},
});
},
fail: () => {
uni.hideLoading();
this.posterSaving = false;
uni.showToast({ title: "导出失败", icon: "none" });
},
});
});
}, 400);
});
} catch (e) {
console.error("保存海报失败:", e);
uni.hideLoading();
this.posterSaving = false;
uni.showToast({ title: "生成失败,请重试", icon: "none" });
}
},
2026-03-02 07:32:41 +00:00
// 会员权益
2025-12-19 12:27:55 +00:00
goToMemberBenefits() {
2026-03-02 07:32:41 +00:00
uni.navigateTo({
url: "/pages/profileSub/pointsMemberRules",
2025-12-19 12:27:55 +00:00
});
},
goToRealNameAuth() {
uni.navigateTo({
2026-01-13 04:12:48 +00:00
url: "/pages/profileSub/realNameAuth",
2025-12-19 12:27:55 +00:00
});
},
goToServiceRecords() {
uni.navigateTo({
2026-01-13 04:12:48 +00:00
url: "/pages/profileSub/serviceRecords",
2025-12-19 12:27:55 +00:00
});
},
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",
});
}
},
});
},
2026-01-13 04:12:48 +00:00
// 选择头像(微信小程序使用 chooseAvatar其他平台使用 chooseImage
handleChooseAvatar(e) {
// #ifdef MP-WEIXIN
if (e.detail && e.detail.avatarUrl) {
2026-03-05 11:04:23 +00:00
const avatarUrl = typeof e.detail.avatarUrl === "string" ? e.detail.avatarUrl : String(e.detail.avatarUrl || "");
if (!avatarUrl) return;
2026-01-13 04:12:48 +00:00
this.userInfo.avatar = avatarUrl;
this.uploadAvatar(avatarUrl);
}
// #endif
2026-03-02 07:32:41 +00:00
2026-01-13 04:12:48 +00:00
// #ifndef MP-WEIXIN
// 其他平台使用 chooseImage
uni.chooseImage({
count: 1,
2026-03-02 07:32:41 +00:00
sizeType: ["compressed"],
sourceType: ["album", "camera"],
2026-01-13 04:12:48 +00:00
success: (res) => {
const tempFilePath = res.tempFilePaths[0];
this.userInfo.avatar = tempFilePath;
this.uploadAvatar(tempFilePath);
},
fail: (err) => {
2026-03-02 07:32:41 +00:00
console.error("选择图片失败:", err);
2026-03-05 11:04:23 +00:00
uni.showToast({ title: "选择图片失败", icon: "none" });
2026-03-02 07:32:41 +00:00
},
2026-01-13 04:12:48 +00:00
});
// #endif
},
2026-03-05 11:04:23 +00:00
// 微信内昵称输入框失焦时保存昵称input type="nickname" 会弹出微信昵称选项)
handleNicknameBlur(e) {
const nickname = (e.detail && e.detail.value) ? String(e.detail.value).trim() : "";
if (nickname) {
this.userInfo.nickname = nickname;
uni.setStorageSync("userInfo", this.userInfo);
this.updateUserNickname(nickname);
uni.showToast({ title: "昵称已更新", icon: "success" });
}
},
// 非微信环境点击昵称提示
handleNicknameClickNonWeixin() {
uni.showToast({
title: "请在微信中打开以修改昵称",
icon: "none",
});
},
// 同步昵称到后端(若后端有更新接口可在此调用)
async updateUserNickname(nickname) {
try {
// 若有更新用户信息接口可在此调用例如await updateUserInfo({ nickname });
} catch (err) {
console.error("更新昵称失败:", err);
}
},
2026-01-13 04:12:48 +00:00
// 上传头像
uploadAvatar(filePath) {
if (this.uploading) {
return;
}
this.uploading = true;
uni.showLoading({
2026-03-02 07:32:41 +00:00
title: "上传中...",
mask: true,
2026-01-13 04:12:48 +00:00
});
// 获取token
2026-03-02 07:32:41 +00:00
const token = uni.getStorageSync("token");
const BASE_URL = "https://guangsh.manage.hschengtai.com";
2026-01-13 04:12:48 +00:00
uni.uploadFile({
url: `${BASE_URL}/app-api/infra/file/upload`,
filePath: filePath,
2026-03-02 07:32:41 +00:00
name: "file",
2026-01-13 04:12:48 +00:00
formData: {
2026-03-02 07:32:41 +00:00
directory: "avatar", // 头像目录
2026-01-13 04:12:48 +00:00
},
header: {
2026-03-02 07:32:41 +00:00
Authorization: `Bearer ${token}`,
"tenant-id": "1",
2026-01-13 04:12:48 +00:00
},
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;
// 保存到本地存储
2026-03-02 07:32:41 +00:00
uni.setStorageSync("userInfo", this.userInfo);
2026-01-13 04:12:48 +00:00
// 调用更新用户信息接口(如果有的话)
this.updateUserAvatar(imageUrl);
2026-03-02 07:32:41 +00:00
2026-01-13 04:12:48 +00:00
uni.showToast({
2026-03-02 07:32:41 +00:00
title: "头像上传成功",
icon: "success",
duration: 1500,
2026-01-13 04:12:48 +00:00
});
} else {
2026-03-02 07:32:41 +00:00
throw new Error("上传成功但未返回图片地址");
2026-01-13 04:12:48 +00:00
}
} else {
2026-03-02 07:32:41 +00:00
throw new Error(data.message || data.msg || "上传失败");
2026-01-13 04:12:48 +00:00
}
} catch (error) {
2026-03-02 07:32:41 +00:00
console.error("解析上传结果失败:", error);
2026-01-13 04:12:48 +00:00
uni.showToast({
2026-03-02 07:32:41 +00:00
title: error.message || "上传失败,请重试",
icon: "none",
2026-01-13 04:12:48 +00:00
});
// 上传失败,恢复原头像
this.loadUserInfo();
}
},
fail: (err) => {
uni.hideLoading();
2026-03-02 07:32:41 +00:00
console.error("上传头像失败:", err);
2026-03-05 11:04:23 +00:00
const msg = (err && err.errMsg && err.errMsg.indexOf("ENOENT") !== -1)
? "模拟器暂不支持更换头像,请使用真机预览"
: "上传失败,请检查网络";
uni.showToast({ title: msg, icon: "none", duration: 2500 });
2026-01-13 04:12:48 +00:00
this.loadUserInfo();
},
complete: () => {
this.uploading = false;
2026-03-02 07:32:41 +00:00
},
2026-01-13 04:12:48 +00:00
});
},
// 更新用户头像(如果有更新用户信息的接口)
async updateUserAvatar(avatarUrl) {
try {
// 这里可以调用更新用户信息的接口
// 例如await updateUserInfo({ avatar: avatarUrl });
// 暂时只更新本地存储,如果后端有更新接口可以在这里调用
2026-03-02 07:32:41 +00:00
console.log("头像已更新:", avatarUrl);
2026-01-13 04:12:48 +00:00
} catch (error) {
2026-03-02 07:32:41 +00:00
console.error("更新用户头像失败:", error);
2026-01-13 04:12:48 +00:00
// 即使更新失败,本地已经保存了,不影响使用
}
},
2025-12-19 12:27:55 +00:00
},
};
</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;
}
2026-03-09 03:37:41 +00:00
&.header-fixed {
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 100;
background: radial-gradient(0% 0% at 0% 0%, #ffffff 0%, #e2e8f1 100%);
}
2025-12-19 12:27:55 +00:00
}
/* 用户资料卡片 */
.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;
2026-03-02 07:32:41 +00:00
.user-level {
position: absolute;
top: 9rpx;
left: 28rpx;
font-family: PingFang-SC, PingFang-SC;
font-weight: bold;
font-size: 28rpx;
color: #fdd7b2;
}
2026-01-13 04:12:48 +00:00
.avatar-btn {
width: 109rpx;
height: 108rpx;
margin-right: 24rpx;
padding: 0;
background: transparent;
border: none;
line-height: 1;
2026-03-02 07:32:41 +00:00
2026-01-13 04:12:48 +00:00
&::after {
border: none;
}
2026-03-02 07:32:41 +00:00
2026-01-13 04:12:48 +00:00
.avatar {
width: 100%;
height: 100%;
border-radius: 50%;
}
}
2026-03-02 07:32:41 +00:00
2026-01-13 04:12:48 +00:00
.avatar-wrapper {
width: 109rpx;
height: 108rpx;
margin-right: 24rpx;
2026-03-02 07:32:41 +00:00
2026-01-13 04:12:48 +00:00
.avatar {
width: 100%;
height: 100%;
border-radius: 50%;
}
}
2026-03-02 07:32:41 +00:00
2025-12-19 12:27:55 +00:00
.avatar {
width: 109rpx;
height: 108rpx;
margin-right: 24rpx;
2026-01-13 04:12:48 +00:00
border-radius: 50%;
2025-12-19 12:27:55 +00:00
}
.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;
}
2026-03-05 11:04:23 +00:00
.nickname-input {
font-family: PingFang-SC, PingFang-SC;
font-weight: bold;
font-size: 32rpx;
color: #333333;
padding: 0;
margin: 0;
height: 44rpx;
line-height: 44rpx;
background: transparent;
width: 100%;
}
2025-12-19 12:27:55 +00:00
.user-id {
font-family: PingFang-SC, PingFang-SC;
font-weight: 500;
font-size: 20rpx;
color: #999999;
}
}
}
2026-03-09 03:37:41 +00:00
.invite-btn {
position: absolute;
right: 255rpx;
bottom: 24rpx;
width: 175rpx;
height: 42rpx;
image {
width: 100%;
height: 100%;
}
}
2025-12-19 12:27:55 +00:00
.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;
}
2026-03-09 03:37:41 +00:00
/* 邀请海报弹窗图5样式 */
.invite-modal-mask {
position: fixed;
left: 0;
right: 0;
top: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
z-index: 900;
}
.invite-modal-wrap {
position: fixed;
left: 0;
right: 0;
top: 0;
bottom: 0;
z-index: 901;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 24rpx;
box-sizing: border-box;
}
.invite-poster {
width: 100%;
max-width: 660rpx;
background: #ffffff;
border-radius: 24rpx;
overflow: visible;
box-shadow: 0 8rpx 32rpx rgba(0, 0, 0, 0.08);
position: relative;
}
.invite-poster-close {
position: absolute;
right: 20rpx;
top: 20rpx;
width: 64rpx;
height: 64rpx;
display: flex;
align-items: center;
justify-content: center;
font-size: 44rpx;
color: #999999;
z-index: 2;
}
.poster-inner {
padding: 48rpx 48rpx 44rpx;
display: flex;
flex-direction: column;
align-items: center;
}
.poster-tag {
position: absolute;
top: 28rpx;
2026-03-09 08:53:29 +00:00
left: 28rpx;
2026-03-09 03:37:41 +00:00
padding: 8rpx 20rpx;
background: #f5f5f5;
border-radius: 8rpx;
font-size: 26rpx;
color: #666666;
font-family: PingFang-SC, PingFang-SC;
}
.poster-nickname {
font-size: 38rpx;
font-weight: bold;
font-family: PingFang-SC, PingFang-SC;
color: #333333;
margin-bottom: 40rpx;
}
.poster-qr-wrap {
display: flex;
flex-direction: column;
align-items: center;
}
.poster-qr-outer {
width: 380rpx;
height: 380rpx;
position: relative;
border-radius: 50%;
overflow: hidden;
background: #f5f5f5;
}
.poster-qr-image {
width: 100%;
height: 100%;
border-radius: 50%;
}
.poster-qr-avatar-wrap {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
width: 120rpx;
height: 120rpx;
border-radius: 50%;
overflow: hidden;
background: #fff;
padding: 8rpx;
box-sizing: border-box;
}
.poster-qr-avatar {
width: 100%;
height: 100%;
border-radius: 50%;
}
.poster-qr-tip {
font-size: 26rpx;
color: #999999;
margin-top: 28rpx;
font-family: PingFang-SC, PingFang-SC;
}
.poster-actions {
display: flex;
align-items: center;
justify-content: center;
gap: 28rpx;
margin-top: 32rpx;
width: 100%;
max-width: 660rpx;
}
.poster-btn {
flex: 1;
max-width: 280rpx;
height: 88rpx;
border-radius: 44rpx;
display: flex;
align-items: center;
justify-content: center;
font-size: 30rpx;
font-family: PingFang-SC, PingFang-SC;
}
.poster-btn-cancel {
background: #f0f0f0;
color: #666666;
}
.poster-btn-save {
background: #004294;
color: #ffffff;
font-weight: 600;
}
.poster-canvas {
position: fixed;
left: -9999px;
top: 0;
z-index: -1;
}
2025-12-19 12:27:55 +00:00
</style>