consumer-app/pages/profile/profile.vue

1099 lines
30 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 header-fixed"
:style="{ paddingTop: statusBarHeight + 'px' }"
>
<text class="header-text">个人中心</text>
</view>
<!-- 主体内容预留头部高度 -->
<view class="profile-main" :style="{ paddingTop: navBarHeight + 'px' }">
<!-- 用户资料卡片 -->
<view
class="user-card"
:style="{ backgroundImage: userCardBgImage }"
>
<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="avatarSrc"
mode="aspectFill"
></image>
</button>
<!-- #endif -->
<!-- #ifndef MP-WEIXIN -->
<view class="avatar-wrapper" @click="handleChooseAvatar">
<image
class="avatar"
:src="avatarSrc"
mode="aspectFill"
></image>
</view>
<!-- #endif -->
<image
src="/static/profile/avatar-hg.png"
mode="aspectFill"
class="avatar-hg"
></image>
<view class="user-details">
<!-- #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 -->
<text class="user-id">NO.{{ userInfo.id }}</text>
</view>
</view>
<view class="invite-btn" @click="goToInvite">
<image src="https://resource2.ctshenglong.cn/20260309/yaoqingma_btn_1773020094853.png" mode="aspectFill"></image>
</view>
<view class="member-benefits-btn" @click="goToMemberBenefits">
<image :src="memberLevelIcon" 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>
<!-- 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>
</view>
</template>
<script>
import { getUserInfo, getInviteQRCode } from "@/api/profile.js";
export default {
data() {
return {
statusBarHeight: 0,
navBarHeight: 0,
currentTime: "10:55",
userInfo: {
level: {}, // 防止 level 未定义时报错
},
noticeNum: 3,
uploading: false, // 是否正在上传头像
inviteModalVisible: false,
inviteQRCodeUrl: "",
inviteCodeStr: "", // 邀请码,格式:邀请类型-用户id如 0-123
posterCanvasWidth: 680,
posterCanvasHeight: 860,
posterSaving: false,
};
},
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)";
},
},
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;
}
}
},
// 邀请好友:弹出邀请二维码。邀请码格式 "邀请类型-用户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" });
}
},
// 会员权益
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
if (e.detail && e.detail.avatarUrl) {
const avatarUrl = typeof e.detail.avatarUrl === "string" ? e.detail.avatarUrl : String(e.detail.avatarUrl || "");
if (!avatarUrl) return;
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
},
// 微信内昵称输入框失焦时保存昵称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);
}
},
// 上传头像
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);
const msg = (err && err.errMsg && err.errMsg.indexOf("ENOENT") !== -1)
? "模拟器暂不支持更换头像,请使用真机预览"
: "上传失败,请检查网络";
uni.showToast({ title: msg, icon: "none", duration: 2500 });
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;
}
&.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%);
}
}
/* 用户资料卡片 */
.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;
}
.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%;
}
.user-id {
font-family: PingFang-SC, PingFang-SC;
font-weight: 500;
font-size: 20rpx;
color: #999999;
}
}
}
.invite-btn {
position: absolute;
right: 255rpx;
bottom: 24rpx;
width: 175rpx;
height: 42rpx;
image {
width: 100%;
height: 100%;
}
}
.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;
}
/* 邀请海报弹窗图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;
right: 96rpx;
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;
}
</style>