Files
----/前端源码/uni-app/components/screens/Profile.vue

1135 lines
28 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<view class="profile-screen">
<view class="profile-bg"></view>
<!-- 状态栏占位 -->
<view class="status-bar-placeholder"></view>
<!-- Header -->
<view class="profile-header">
<view class="profile-user-row">
<view class="profile-avatar-wrap">
<image :src="defaultAvatar" mode="cover" class="profile-avatar-img" />
<view class="profile-avatar-badge">
<text class="profile-avatar-badge-text"></text>
</view>
</view>
<view class="profile-user-info">
<text class="profile-nickname">{{ user.nickname }}</text>
</view>
</view>
<!-- <view class="profile-stats">
<view class="profile-stat-item" @click="emitNav('favorites')">
<text class="profile-stat-num">{{ stats.favoritesCount }}</text>
<text class="profile-stat-label">我的收藏</text>
</view>
<view class="profile-stat-item" @click="emitNav('reports')">
<text class="profile-stat-num">{{ stats.reportsCount }}</text>
<text class="profile-stat-label">已解锁报告</text>
</view>
<view class="profile-stat-item" @click="showPromoter = true">
<text class="profile-stat-num">{{ stats.earningsCount }}</text>
<text class="profile-stat-label"></text>
</view>
</view> -->
</view>
<!-- Main cards -->
<view class="profile-cards">
<view class="profile-card membership-card">
<view class="membership-card-inner">
<view class="membership-card-head">
<view class="membership-card-icon"></view>
<text class="membership-card-title">会员与额度</text>
</view>
<view v-if="!isLoggedIn()" class="membership-card-tip">登录后可查看会员等级与剩余额度</view>
<template v-else>
<view class="membership-row">
<text class="membership-label">会员等级</text>
<text class="membership-value">{{ membership.levelText }}</text>
</view>
<view class="membership-row">
<text class="membership-label">剩余额度</text>
<text class="membership-value membership-value-strong">
<template v-if="membership.remainText !== ''">{{ membership.remainText }}</template>
<template v-else></template>
<text v-if="membership.totalText !== ''" class="membership-total-suffix"> / {{ membership.totalText }}</text>
</text>
</view>
<text v-if="membership.loadFailed" class="membership-card-tip">额度加载失败请稍后下拉刷新重试</text>
</template>
</view>
</view>
<view class="profile-card">
<view class="profile-card-btn" @click="emitNav('myNamingPlans')">
<view class="profile-card-left">
<view class="profile-card-icon profile-card-icon-red"></view>
<text class="profile-card-text">我的方案</text>
</view>
<text class="profile-card-arrow"></text>
</view>
<!-- <view class="profile-card-btn profile-card-btn-border" @click="emitNav('reports')">
<view class="profile-card-left">
<view class="profile-card-icon profile-card-icon-gold"></view>
<text class="profile-card-text">财运解析报告</text>
</view>
<text class="profile-card-arrow"></text>
</view> -->
<view class="profile-card-btn profile-card-btn-border" @click="emitNav('orders')">
<view class="profile-card-left">
<view class="profile-card-icon profile-card-icon-blue"></view>
<text class="profile-card-text">我的订单</text>
</view>
<text class="profile-card-arrow"></text>
</view>
<view class="profile-card-btn profile-card-btn-border" @click="emitNav('favorites')">
<view class="profile-card-left">
<view class="profile-card-icon profile-card-icon-blue"></view>
<text class="profile-card-text">我的收藏</text>
</view>
<text class="profile-card-arrow"></text>
</view>
<view class="profile-card-btn profile-card-btn-border" @click="emitNav('profile_user_info')">
<view class="profile-card-left">
<view class="profile-card-icon profile-card-icon-gold"></view>
<text class="profile-card-text">我的信息</text>
</view>
<text class="profile-card-arrow"></text>
</view>
</view>
<view class="profile-card">
<view class="profile-card-btn" @click="showShare = true">
<view class="profile-card-left">
<view class="profile-card-icon profile-card-icon-dark"></view>
<text class="profile-card-text">分享给好友</text>
</view>
<text class="profile-card-arrow"></text>
</view>
<view class="profile-card-btn profile-card-btn-border" @click="showPromoter = true">
<view class="profile-card-left">
<view class="profile-card-icon profile-card-icon-green"></view>
<view class="profile-card-text-wrap">
<text class="profile-card-text">申请成为会员</text>
<text class="profile-card-subtext">自用省钱分享赚钱</text>
</view>
</view>
<view class="profile-card-right">
<text class="profile-card-hot">HOT</text>
<text class="profile-card-arrow"></text>
</view>
</view>
</view>
<view class="profile-card">
<view class="profile-card-btn" @click="emitNav('settings')">
<view class="profile-card-left">
<view class="profile-card-icon profile-card-icon-gray"></view>
<text class="profile-card-text">设置与反馈</text>
</view>
<text class="profile-card-arrow"></text>
</view>
</view>
</view>
<!-- Promoter modal -->
<view v-if="showPromoter" class="profile-modal" @click="showPromoter = false">
<view class="profile-modal-content" @click.stop>
<view class="profile-modal-close" @click="showPromoter = false">×</view>
<view class="profile-modal-body">
<view class="profile-modal-header">
<view class="profile-modal-icon"></view>
<text class="profile-modal-title">申请成为推广会员</text>
<text class="profile-modal-subtitle">开启您的睡后收入之旅</text>
</view>
<view class="profile-modal-form">
<input v-model="promoter.name" type="text" placeholder="真实姓名" class="profile-modal-input" />
<input v-model="promoter.phone" type="tel" placeholder="手机号码" class="profile-modal-input" />
<input v-model="promoter.wechat" type="text" placeholder="微信号 (选填)" class="profile-modal-input" />
<view class="profile-modal-level">
<text class="profile-modal-level-label">会员类型</text>
<view class="profile-modal-level-options">
<view
class="profile-modal-level-item"
:class="{ 'profile-modal-level-item-active': promoter.level === 'junior' }"
@click="promoter.level = 'junior'"
>
初级会员
</view>
<view
class="profile-modal-level-item"
:class="{ 'profile-modal-level-item-active': promoter.level === 'senior' }"
@click="promoter.level = 'senior'"
>
高级会员
</view>
</view>
</view>
</view>
<view class="profile-modal-submit" :disabled="submitting" @click="handlePromoterSubmit">
{{ submitting ? '提交中...' : '开启旅程' }}
</view>
<text class="profile-modal-note">点击支付即代表同意会员合作协议</text>
</view>
</view>
</view>
<!-- Payment Modal -->
<PaymentModal :visible="showPayment" :product-name="paymentInfo.productName" :product-desc="paymentInfo.productDesc"
:product-icon="paymentInfo.productIcon" :amount="paymentInfo.amount" :business-type="paymentInfo.businessType"
:business-id="paymentInfo.businessId" @close="showPayment = false" @success="handlePaymentSuccess"
@fail="handlePaymentFail" />
<!-- Share modal -->
<view v-if="showShare" class="profile-share-modal" @click="showShare = false">
<view class="profile-share-content" @click.stop>
<view class="profile-share-options">
<view class="profile-share-item" @click="handleShare('poster')">
<view class="profile-share-icon profile-share-icon-poster"></view>
<text class="profile-share-label">生成海报</text>
</view>
</view>
<view class="profile-share-cancel" @click="showShare = false">
取消
</view>
</view>
</view>
<!-- Share Poster Modal -->
<SharePosterModal :visible="showSharePoster" :user-id="user.id" @close="showSharePoster = false" />
</view>
</template>
<script setup lang="ts">
import { reactive, ref, onMounted } from "vue";
import { logout, getUserInfo, setUserInfo, isLoggedIn, navigateToLogin } from "@/utils/auth";
import { userApi } from "@/api";
import SharePosterModal from "../SharePosterModal.vue";
import PaymentModal from "../PaymentModal.vue";
// @ts-ignore - uni is a global object provided by uni-app runtime
declare const uni: any;
const emit = defineEmits<{
navigate: [screen: string]
}>();
const user = reactive({
nickname: "玄墨",
avatar: "",
id: 1001, // 用户ID实际应该从登录信息获取
});
const defaultAvatar = new URL("../../style/statics/default-avatar.svg", import.meta.url).href;
user.avatar = defaultAvatar;
const showPromoter = ref(false);
const showShare = ref(false);
const showSharePoster = ref(false);
const showPayment = ref(false);
const submitting = ref(false);
const promoter = reactive<{
name: string;
phone: string;
wechat: string;
level: "junior" | "senior";
}>({ name: "", phone: "", wechat: "", level: "junior" });
const partnerApplyId = ref(0); // 保存申请ID用于支付
// 支付信息
const paymentInfo = reactive({
productName: '',
productDesc: '',
productIcon: '💼',
amount: 0,
businessType: '',
businessId: 0
});
// 统计数据
const stats = reactive({
favoritesCount: 0,
reportsCount: 0,
earningsCount: 0,
});
const membership = reactive({
levelText: "普通会员",
remainText: "",
totalText: "",
loadFailed: false,
});
const requireLoginThen = (next: () => void) => {
if (isLoggedIn()) {
next();
return;
}
// Web环境使用confirmuni-app环境使用showModal
if (typeof uni?.showModal === 'function') {
uni.showModal({
title: '提示',
content: '登录后可使用该功能,是否前往登录?',
confirmText: '去登录',
cancelText: '先逛逛',
success: (res: any) => {
if (res.confirm) {
navigateToLogin();
}
},
});
} else {
// Web环境使用原生confirm
const confirmed = confirm('登录后可使用该功能,是否前往登录?');
if (confirmed) {
navigateToLogin();
}
}
};
const handleRefreshWxProfile = async () => {
try {
if (!uni?.getUserProfile) {
uni.showToast({ title: '当前环境不支持获取微信信息', icon: 'none' });
return;
}
const profileRes = await new Promise<any>((resolve, reject) => {
uni.getUserProfile({
desc: '用于更新头像和昵称',
success: resolve,
fail: reject,
});
});
const nickName = profileRes?.userInfo?.nickName;
const avatarUrl = profileRes?.userInfo?.avatarUrl;
if (!nickName && !avatarUrl) {
uni.showToast({ title: '未获取到微信头像或昵称', icon: 'none' });
return;
}
if (nickName) user.nickname = nickName;
if (avatarUrl) user.avatar = avatarUrl;
const old = getUserInfo() || {};
setUserInfo({
...old,
id: old.id || user.id,
nickname: nickName || old.nickname || user.nickname,
avatar: avatarUrl || old.avatar || user.avatar,
});
uni.showToast({ title: '已更新', icon: 'success' });
} catch (e: any) {
const msg = e?.errMsg && String(e.errMsg).includes('cancel') ? '已取消' : '获取失败';
uni.showToast({ title: msg, icon: 'none' });
}
};
// 加载用户信息
onMounted(async () => {
const userInfo = getUserInfo();
if (userInfo) {
user.nickname = userInfo.username;
user.avatar = userInfo.avatar;
user.id = userInfo.id ;
}
if (!isLoggedIn()) {
membership.levelText = "普通会员";
membership.remainText = "";
membership.totalText = "";
membership.loadFailed = false;
return;
}
// 统计类接口失败时不执行 clearToken避免从子页返回「我的」时误清登录态与修改资料等流程叠加
const statsRequestOpts = { clearAuthOnError: false };
// 加载收藏数量
try {
const res = await userApi.getMyFavorites({ page_no: 1, page_size: 1 }, statsRequestOpts);
stats.favoritesCount = res?.total || 0;
} catch (e) {
console.error('加载收藏数量失败:', e);
}
// 加载方案数量
try {
const res = await userApi.getMyReports({ page_no: 1, page_size: 1 }, statsRequestOpts);
stats.reportsCount = res?.total || 0;
} catch (e) {
console.error('加载方案数量失败:', e);
}
// 加载会员等级与额度
membership.loadFailed = false;
try {
const quotaRes: any = await userApi.getMyMembershipQuota(statsRequestOpts);
const level = String(
quotaRes?.membership_level ??
quotaRes?.level_name ??
quotaRes?.tier_name ??
quotaRes?.level ??
""
).trim();
const totalRaw = quotaRes?.total_quota ?? quotaRes?.quota;
const usedRaw = quotaRes?.used_quota;
const freeRename = Number(quotaRes?.free_rename_quota);
const remainRaw = quotaRes?.remaining_quota ?? quotaRes?.remain_quota ?? quotaRes?.left_quota;
const total = Number(totalRaw);
const used = Number(usedRaw);
const remainFallback = Number(remainRaw);
const remain = Number.isFinite(freeRename) ? freeRename : remainFallback;
membership.levelText = level || "普通会员";
membership.remainText = "";
membership.totalText = "";
// 剩余额度:优先使用后端字段 free_rename_quota
if (Number.isFinite(remain)) {
membership.remainText = String(remain);
if (Number.isFinite(total)) {
membership.totalText = String(total);
}
} else if (Number.isFinite(used) && Number.isFinite(total)) {
membership.remainText = String(Math.max(0, total - used));
membership.totalText = String(total);
} else if (Number.isFinite(total)) {
membership.remainText = "—";
membership.totalText = String(total);
}
} catch (e) {
console.error('加载会员额度失败:', e);
membership.loadFailed = true;
membership.remainText = "";
membership.totalText = "";
}
});
const emitNav = (screen: string) => {
const loginRequiredScreens = ['myNamingPlans', 'favorites', 'reports', 'orders', 'profile_user_info'];
if (loginRequiredScreens.includes(screen)) {
requireLoginThen(() => emit("navigate", screen));
return;
}
emit("navigate", screen);
};
// 处理分享
const handleShare = (type: 'poster') => {
showShare.value = false;
showSharePoster.value = true;
};
// 验证手机号
const isValidPhone = (phone: string) => /^1[3-9]\d{9}$/.test(phone);
// 处理推广员申请提交(先验证信息,再打开支付弹窗)
const handlePromoterSubmit = async () => {
if (!promoter.name.trim()) {
uni.showToast({ title: "请填写真实姓名", icon: "none" });
return;
}
if (!promoter.phone.trim()) {
uni.showToast({ title: "请填写手机号码", icon: "none" });
return;
}
if (!isValidPhone(promoter.phone)) {
uni.showToast({ title: "请输入正确的手机号", icon: "none" });
return;
}
if (submitting.value) return;
submitting.value = true;
try {
// 提交申请信息
const res = await userApi.applyPartner({
real_name: promoter.name.trim(),
phone: promoter.phone.trim(),
wechat_id: promoter.wechat.trim() || undefined,
member_level: promoter.level,
});
// 假设后端返回申请ID实际需要根据API返回调整
partnerApplyId.value = (res as any)?.id || Date.now(); // 优先使用后端返回ID
// 关闭申请弹窗
showPromoter.value = false;
// 打开支付弹窗
paymentInfo.productName = '推广会员权益';
paymentInfo.productDesc = promoter.level === "senior" ? "高级会员申请" : "初级会员申请";
paymentInfo.productIcon = '💼';
paymentInfo.amount = 99;
paymentInfo.businessType = 'partner_apply';
paymentInfo.businessId = partnerApplyId.value;
showPayment.value = true;
} catch (e: any) {
uni.showToast({ title: e.msg || "申请失败,请重试", icon: "none" });
} finally {
submitting.value = false;
}
};
// 支付成功回调
const handlePaymentSuccess = (outTradeNo: string) => {
console.log('支付成功,订单号:', outTradeNo);
// 重置表单
promoter.name = "";
promoter.phone = "";
promoter.wechat = "";
promoter.level = "junior";
// Web环境使用alertuni-app环境使用showModal
if (typeof uni?.showModal === 'function') {
uni.showModal({
title: '恭喜您',
content: '已成功开通推广会员权益!',
showCancel: false,
success: () => {
// 可以跳转到推广页面或刷新数据
}
});
} else {
// Web环境使用原生alert
alert('恭喜您,已成功开通推广会员权益!');
}
};
// 支付失败回调
const handlePaymentFail = (message: string) => {
console.log('支付失败:', message);
};
// 退出登录
const handleLogout = async () => {
// Web环境使用confirmuni-app环境使用showModal
if (typeof uni?.showModal === 'function') {
uni.showModal({
title: '提示',
content: '确定要退出登录吗?',
success: async (res: any) => {
if (res.confirm) {
try {
await logout();
} catch (e) {
console.error('退出登录失败:', e);
}
}
},
});
} else {
// Web环境使用原生confirm
const confirmed = confirm('确定要退出登录吗?');
if (confirmed) {
try {
await logout();
} catch (e) {
console.error('退出登录失败:', e);
}
}
}
};
</script>
<style scoped>
.profile-screen {
height: 100%;
display: flex;
flex-direction: column;
background-color: #f0efe9;
position: relative;
overflow: auto;
}
/* 状态栏占位 */
.status-bar-placeholder {
height: var(--status-bar-height, 0);
width: 100%;
flex-shrink: 0;
}
.profile-bg {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
pointer-events: none;
opacity: 0.3;
background-image: url("https://www.transparenttextures.com/patterns/rice-paper.png");
}
/* Header */
.profile-header {
position: relative;
z-index: 10;
padding: 80rpx 48rpx 64rpx;
background-color: #2c2c2c;
color: #f2e6d8;
border-bottom-left-radius: 80rpx;
border-bottom-right-radius: 80rpx;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}
.profile-user-row {
display: flex;
align-items: center;
gap: 32rpx;
}
.profile-avatar-wrap {
width: 128rpx;
height: 128rpx;
border-radius: 50%;
border: 4rpx solid #d4af37;
padding: 4rpx;
position: relative;
overflow: visible;
}
.profile-avatar-img {
width: 100%;
height: 100%;
border-radius: 50%;
}
.profile-avatar-badge {
position: absolute;
bottom: -4rpx;
right: -4rpx;
background-color: #d4af37;
border-radius: 50%;
width: 36rpx;
height: 36rpx;
display: flex;
align-items: center;
justify-content: center;
border: 2rpx solid #2c2c2c;
}
.profile-avatar-badge-text {
font-size: 20rpx;
color: #2c2c2c;
}
.profile-user-info {
flex: 1;
}
.profile-nickname {
font-size: 40rpx;
font-weight: 700;
letter-spacing: 0.15em;
font-family: SimSun, "Songti SC", serif;
display: block;
}
.profile-user-meta {
display: flex;
align-items: center;
gap: 16rpx;
margin-top: 8rpx;
}
.profile-user-level {
font-size: 24rpx;
background-color: rgba(212, 175, 55, 0.2);
color: #d4af37;
padding: 4rpx 16rpx;
border-radius: 999rpx;
border: 1px solid rgba(212, 175, 55, 0.5);
}
.profile-user-quota {
font-size: 22rpx;
color: rgba(255, 255, 255, 0.78);
background-color: rgba(255, 255, 255, 0.08);
padding: 4rpx 14rpx;
border-radius: 999rpx;
border: 1px solid rgba(255, 255, 255, 0.18);
}
.profile-user-id {
font-size: 24rpx;
color: rgba(255, 255, 255, 0.5);
}
.profile-stats {
display: flex;
justify-content: space-between;
margin-top: 64rpx;
padding: 0 32rpx;
}
.profile-stat-item {
text-align: center;
}
.profile-stat-num {
font-size: 36rpx;
font-weight: 700;
color: #d4af37;
display: block;
}
.profile-stat-label {
font-size: 24rpx;
color: rgba(255, 255, 255, 0.6);
}
/* Cards */
.profile-cards {
position: relative;
z-index: 10;
padding: 0 24rpx;
margin-top: -24rpx;
display: flex;
flex-direction: column;
gap: 16rpx;
padding-bottom: 192rpx;
}
.membership-card {
padding: 28rpx 24rpx;
background: linear-gradient(145deg, #fffdf9 0%, #f5efe4 100%);
border: 1px solid rgba(212, 175, 55, 0.35);
box-shadow: 0 4px 14px rgba(44, 44, 44, 0.08);
}
.membership-card-inner {
display: flex;
flex-direction: column;
gap: 20rpx;
}
.membership-card-head {
display: flex;
align-items: center;
gap: 16rpx;
}
.membership-card-icon {
width: 56rpx;
height: 56rpx;
border-radius: 12rpx;
background: rgba(212, 175, 55, 0.18);
color: #8b2323;
font-size: 28rpx;
font-weight: 800;
display: flex;
align-items: center;
justify-content: center;
border: 1px solid rgba(212, 175, 55, 0.4);
}
.membership-card-title {
font-size: 30rpx;
font-weight: 700;
color: #2c2c2c;
letter-spacing: 0.08em;
}
.membership-row {
display: flex;
align-items: baseline;
justify-content: space-between;
gap: 24rpx;
padding-bottom: 12rpx;
border-bottom: 1px solid rgba(0, 0, 0, 0.06);
}
.membership-row:last-of-type {
border-bottom: none;
padding-bottom: 0;
}
.membership-label {
font-size: 26rpx;
color: #6b7280;
flex-shrink: 0;
}
.membership-value {
font-size: 28rpx;
color: #2c2c2c;
text-align: right;
flex: 1;
}
.membership-value-strong {
font-weight: 800;
color: #8b2323;
}
.membership-total-suffix {
font-size: 24rpx;
font-weight: 500;
color: #6b7280;
}
.membership-card-tip {
font-size: 24rpx;
color: #888;
line-height: 1.5;
}
.profile-card {
background-color: #fffdf9;
border-radius: 16rpx;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
border: 1px solid #e5e5e5;
overflow: hidden;
}
.profile-card-btn {
width: 100%;
display: flex;
align-items: center;
justify-content: space-between;
padding: 24rpx;
min-height: 96rpx;
background: transparent;
border: none;
box-sizing: border-box;
}
.profile-card-btn-border {
border-top: 1px solid #f0f0f0;
}
.profile-card-left {
display: flex;
align-items: center;
gap: 24rpx;
}
.profile-card-icon {
width: 64rpx;
height: 64rpx;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 28rpx;
}
.profile-card-icon-red {
background-color: rgba(139, 35, 35, 0.1);
color: #8b2323;
}
.profile-card-icon-gold {
background-color: rgba(212, 175, 55, 0.1);
color: #d4af37;
}
.profile-card-icon-blue {
background-color: rgba(37, 99, 235, 0.1);
color: #2563eb;
}
.profile-card-icon-dark {
background-color: rgba(26, 26, 46, 0.1);
color: #1a1a2e;
}
.profile-card-icon-green {
background-color: rgba(21, 128, 61, 0.1);
color: #15803d;
}
.profile-card-icon-gray {
background-color: #f3f4f6;
color: #6b7280;
}
.profile-card-text {
font-size: 28rpx;
font-weight: 500;
color: #2c2c2c;
}
.profile-card-text-wrap {
text-align: left;
}
.profile-card-subtext {
font-size: 20rpx;
color: #999;
display: block;
margin-top: 4rpx;
}
.profile-card-right {
display: flex;
align-items: center;
gap: 12rpx;
flex-shrink: 0;
}
.profile-card-hot {
font-size: 18rpx;
background: linear-gradient(135deg, #ff6b6b, #8b2323);
color: #fff;
padding: 6rpx 14rpx;
border-radius: 6rpx;
font-weight: 600;
line-height: 1;
}
.profile-card-arrow {
color: #ccc;
font-size: 32rpx;
}
/* Promoter Modal */
.profile-modal {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 50;
display: flex;
align-items: center;
justify-content: center;
background-color: rgba(0, 0, 0, 0.6);
padding: 32rpx;
}
.profile-modal-content {
background-color: #fffdf9;
width: 100%;
max-width: 640rpx;
border-radius: 24rpx;
overflow: hidden;
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
position: relative;
}
.profile-modal-close {
position: absolute;
top: 24rpx;
right: 24rpx;
color: #999;
font-size: 40rpx;
background: transparent;
border: none;
}
.profile-modal-body {
padding: 48rpx;
}
.profile-modal-header {
text-align: center;
margin-bottom: 48rpx;
}
.profile-modal-icon {
width: 96rpx;
height: 96rpx;
background-color: #8b2323;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
color: #d4af37;
font-size: 40rpx;
margin: 0 auto 24rpx;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}
.profile-modal-title {
font-size: 36rpx;
font-weight: 700;
color: #2c2c2c;
display: block;
}
.profile-modal-subtitle {
font-size: 24rpx;
color: #5a5a5a;
display: block;
margin-top: 8rpx;
}
.profile-modal-form {
display: flex;
flex-direction: column;
gap: 24rpx;
}
.profile-modal-input {
width: 100%;
height: 88rpx;
background-color: #fff;
border: 1px solid #e5e5e5;
padding: 0 24rpx;
border-radius: 8rpx;
font-size: 28rpx;
color: #2c2c2c;
box-sizing: border-box;
line-height: 88rpx;
}
.profile-modal-input::placeholder {
color: #999;
font-size: 26rpx;
}
.profile-modal-level {
display: flex;
flex-direction: column;
gap: 16rpx;
}
.profile-modal-level-label {
font-size: 24rpx;
color: #666;
}
.profile-modal-level-options {
display: flex;
gap: 16rpx;
}
.profile-modal-level-item {
flex: 1;
height: 76rpx;
display: flex;
align-items: center;
justify-content: center;
border-radius: 8rpx;
border: 1px solid #dcdcdc;
background: #fff;
color: #555;
font-size: 26rpx;
}
.profile-modal-level-item-active {
border-color: #2c2c2c;
color: #2c2c2c;
background: #f3efe3;
font-weight: 600;
}
.profile-modal-submit {
width: 100%;
background-color: #2c2c2c;
color: #d4af37;
font-weight: 700;
padding: 24rpx 0;
border-radius: 8rpx;
margin-top: 48rpx;
border: none;
font-size: 28rpx;
display: flex;
justify-content: center;
}
.profile-modal-note {
text-align: center;
font-size: 20rpx;
color: #999;
margin-top: 24rpx;
display: block;
}
/* Share Modal */
.profile-share-modal {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 50;
display: flex;
flex-direction: column;
justify-content: flex-end;
background-color: rgba(0, 0, 0, 0.6);
}
.profile-share-content {
background-color: #fdfbf7;
border-top-left-radius: 32rpx;
border-top-right-radius: 32rpx;
padding: 48rpx;
}
.profile-share-title {
text-align: center;
font-weight: 700;
color: #2c2c2c;
margin-bottom: 48rpx;
display: block;
font-size: 32rpx;
}
.profile-share-options {
display: flex;
justify-content: space-around;
margin-bottom: 64rpx;
}
.profile-share-item {
display: flex;
flex-direction: column;
align-items: center;
gap: 16rpx;
}
.profile-share-icon {
width: 96rpx;
height: 96rpx;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 36rpx;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
}
.profile-share-icon-wechat {
background-color: #07c160;
color: #fff;
}
.profile-share-icon-moments {
background-color: #fff;
border: 1px solid #e5e5e5;
color: #2c2c2c;
}
.profile-share-icon-poster {
background-color: #2c2c2c;
color: #fff;
}
.profile-share-label {
font-size: 24rpx;
color: #5a5a5a;
}
.profile-share-cancel {
width: 100%;
padding: 24rpx 0;
background-color: #f5f5f5;
color: #5a5a5a;
border-radius: 16rpx;
font-size: 28rpx;
font-weight: 700;
border: none;
}
</style>