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-03-21 13:38:13 +00:00
|
|
|
|
<view class="menu-item" @tap="goToMyCoupons">
|
|
|
|
|
|
<view class="menu-icon"
|
|
|
|
|
|
><image src="/static/profile/my_icon.png" mode="aspectFill"></image
|
|
|
|
|
|
></view>
|
|
|
|
|
|
<text class="menu-text">我的优惠卷</text>
|
|
|
|
|
|
<text class="menu-arrow">›</text>
|
|
|
|
|
|
</view>
|
2026-03-10 01:05:02 +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>
|
2026-03-10 01:05:02 +00:00
|
|
|
|
</view> -->
|
2025-12-19 12:27:55 +00:00
|
|
|
|
</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通常为44px(iPhone X及以后)或20px(iPhone 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",
|
|
|
|
|
|
});
|
|
|
|
|
|
},
|
2026-03-21 13:38:13 +00:00
|
|
|
|
goToMyCoupons() {
|
|
|
|
|
|
uni.navigateTo({
|
|
|
|
|
|
url: "/pages/profileSub/myCoupons",
|
|
|
|
|
|
});
|
|
|
|
|
|
},
|
2025-12-19 12:27:55 +00:00
|
|
|
|
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>
|
|
|
|
|
|
|