profile.vue 41 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457
  1. <template>
  2. <view class="container">
  3. <!-- 未登录 -->
  4. <view v-if="!isLoggedIn">
  5. <view class="login-prompt">
  6. <text class="prompt-icon">👤</text>
  7. <text class="prompt-title">登录浠艾福</text>
  8. <text class="prompt-desc">登录后查看个人资料、积分记录、能量数据与家庭管理</text>
  9. <button class="login-btn" @click="goLogin">登录 / 注册</button>
  10. <text class="skip-link" @click="goSkip">暂不登录,先逛逛 ›</text>
  11. </view>
  12. <view class="philosophy-card">
  13. <text class="philosophy-title">照顾家庭,从照顾好自己开始</text>
  14. <text class="philosophy-desc">你的状态,就是家的状态。先安顿好自己,才有余力温暖家人。</text>
  15. </view>
  16. </view>
  17. <!-- 已登录 -->
  18. <template v-else>
  19. <!-- 退出切换 -->
  20. <view class="exit-switch" v-if="isSwitchedChild" @click="onExitSwitch">
  21. <text>🔄 退出切换</text>
  22. </view>
  23. <!-- 身份卡 -->
  24. <view class="user-card">
  25. <view class="user-card-left">
  26. <view class="avatar-wrap" @click="goToEditInfo">
  27. <image v-if="userInfo.avatar" class="avatar-img" :src="userInfo.avatar" mode="aspectFill" />
  28. <text v-else class="avatar-text">{{ nickname.charAt(0) || '👤' }}</text>
  29. </view>
  30. <view class="user-meta">
  31. <view class="user-name-row">
  32. <text class="nickname">{{ nickname || '未设置昵称' }}</text>
  33. <text v-if="roleLabel" class="role-tag">{{ roleLabel }}</text>
  34. </view>
  35. <view class="user-sub" v-if="memberLevel">
  36. <view class="level-badge" :class="'level-' + memberLevel">{{ memberLevelText }}</view>
  37. <text v-if="isLifetime" class="days-left">永久有效</text>
  38. <text v-else-if="membershipDays > 0 && membershipDays <= 30" class="days-left">剩余 {{ membershipDays }} 天</text>
  39. <view v-if="hasUpgradeSpace" class="upgrade-btn" @click="goUpgrade">
  40. <text class="upgrade-btn-text">{{ upgradeBtnText }}</text>
  41. </view>
  42. </view>
  43. <view class="user-sub" v-else>
  44. <text class="days-left">开通会员解锁更多权益</text>
  45. <view class="upgrade-btn" @click="goUpgrade">
  46. <text class="upgrade-btn-text">立即开通</text>
  47. </view>
  48. </view>
  49. </view>
  50. </view>
  51. <view class="user-card-right" @click="goToPoints">
  52. <text class="points-num">{{ totalPoints }}</text>
  53. <text class="points-label">积分</text>
  54. </view>
  55. </view>
  56. <!-- 我的推广码浮动按钮 -->
  57. <view class="share-qr-float" @click="showShareQr">
  58. <text class="share-qr-icon">📱</text>
  59. </view>
  60. <!-- 家族成员速览 -->
  61. <view class="family-strip" v-if="familyMembers.length > 0">
  62. <view class="family-strip-header">
  63. <text class="family-strip-title">我的家庭</text>
  64. <text class="family-strip-count">{{ familyMembers.length }} 位成员</text>
  65. </view>
  66. <view class="family-avatars">
  67. <view
  68. v-for="member in familyMembers"
  69. :key="member.id"
  70. class="family-avatar-item"
  71. @click="goMemberDetail(member)"
  72. >
  73. <view class="family-avatar-wrap">
  74. <image v-if="member.avatar" class="family-avatar-img" :src="member.avatar" mode="aspectFill" />
  75. <text v-else class="family-avatar-text">{{ member.nickname ? member.nickname.charAt(0) : '?' }}</text>
  76. <view class="family-avatar-tag" :class="'tag-' + (member.effectiveRole || member.source || 'member')">{{ member.roleLabel || memberRoleLabel(member.effectiveRole) }}</view>
  77. </view>
  78. <text class="family-avatar-name">{{ member.nickname || member.realName || '成员' }}</text>
  79. </view>
  80. <view class="family-avatar-item" @click="goToFamily">
  81. <view class="family-avatar-wrap family-avatar-add">
  82. <text class="family-avatar-plus">+</text>
  83. </view>
  84. <text class="family-avatar-name">添加</text>
  85. </view>
  86. </view>
  87. </view>
  88. <!-- 个人成就统计 -->
  89. <view class="achievement-section">
  90. <view class="section-header-row">
  91. <text class="section-title">🏆 我的成就</text>
  92. <text class="section-sub">持续成长,看见改变</text>
  93. </view>
  94. <view class="achievement-grid">
  95. <view class="ach-item" @click="goToPoints">
  96. <view class="ach-icon-wrap" style="background:rgba(249,115,22,0.12)">
  97. <text class="ach-icon">💰</text>
  98. </view>
  99. <text class="ach-num">{{ totalPoints }}</text>
  100. <text class="ach-label">累计积分</text>
  101. </view>
  102. <view class="ach-item" @click="goToStreak">
  103. <view class="ach-icon-wrap" style="background:rgba(255,140,66,0.12)">
  104. <text class="ach-icon">🔥</text>
  105. </view>
  106. <text class="ach-num">{{ streakDays }}</text>
  107. <text class="ach-label">连续打卡</text>
  108. </view>
  109. <view class="ach-item" @click="goToTasks">
  110. <view class="ach-icon-wrap" style="background:rgba(99,102,241,0.12)">
  111. <text class="ach-icon">✅</text>
  112. </view>
  113. <text class="ach-num">{{ completedTasks }}</text>
  114. <text class="ach-label">完成任务</text>
  115. </view>
  116. <view class="ach-item" @click="goToWishes">
  117. <view class="ach-icon-wrap" style="background:rgba(245,158,11,0.12)">
  118. <text class="ach-icon">🎁</text>
  119. </view>
  120. <text class="ach-num">{{ rewardsEarned }}</text>
  121. <text class="ach-label">获得奖励</text>
  122. </view>
  123. <view class="ach-item" @click="goToBadges">
  124. <view class="ach-icon-wrap" style="background:rgba(236,72,153,0.12)">
  125. <text class="ach-icon">🏅</text>
  126. </view>
  127. <text class="ach-num">{{ badgesCollected }}</text>
  128. <text class="ach-label">收集勋章</text>
  129. </view>
  130. <view class="ach-item" @click="goToGrowth">
  131. <view class="ach-icon-wrap" style="background:rgba(16,185,129,0.12)">
  132. <text class="ach-icon">📈</text>
  133. </view>
  134. <text class="ach-num">{{ growthRecords }}</text>
  135. <text class="ach-label">成长记录</text>
  136. </view>
  137. <view class="ach-item" @click="goToChallenges">
  138. <view class="ach-icon-wrap" style="background:rgba(255,107,157,0.12)">
  139. <text class="ach-icon">🎯</text>
  140. </view>
  141. <text class="ach-num">{{ challengesJoined }}</text>
  142. <text class="ach-label">参加挑战</text>
  143. </view>
  144. <view class="ach-item" @click="goToHealthReports">
  145. <view class="ach-icon-wrap" style="background:rgba(14,165,233,0.12)">
  146. <text class="ach-icon">📋</text>
  147. </view>
  148. <text class="ach-num">{{ healthReports }}</text>
  149. <text class="ach-label">上传报告</text>
  150. </view>
  151. <view class="ach-item" @click="goToWealthCenter">
  152. <view class="ach-icon-wrap" style="background:rgba(245,158,11,0.12)">
  153. <text class="ach-icon">💰</text>
  154. </view>
  155. <text class="ach-num">{{ referralEarnings }}</text>
  156. <text class="ach-label">推广收益</text>
  157. </view>
  158. <view class="ach-item" @click="goTeamTree">
  159. <view class="ach-icon-wrap" style="background:rgba(16,185,129,0.12)">
  160. <text class="ach-icon">🌳</text>
  161. </view>
  162. <text class="ach-num">{{ teamMembers }}</text>
  163. <text class="ach-label">我的团队</text>
  164. </view>
  165. </view>
  166. </view>
  167. <!-- 我的订单(电商风格) -->
  168. <view class="order-section">
  169. <view class="order-section-header">
  170. <text class="section-title">🛒 我的订单</text>
  171. <text class="order-all-link" @click="goToOrders">全部订单 ›</text>
  172. </view>
  173. <view class="order-card">
  174. <view class="order-grid">
  175. <view class="order-grid-item" @click="goToOrderStatus('pending')">
  176. <view class="order-icon-wrap">
  177. <text class="order-icon">💳</text>
  178. <text class="order-badge" v-if="orderPending > 0">{{ orderPending }}</text>
  179. </view>
  180. <text class="order-grid-text">待付款</text>
  181. </view>
  182. <view class="order-grid-item" @click="goToOrderStatus('paid')">
  183. <view class="order-icon-wrap">
  184. <text class="order-icon">📦</text>
  185. <text class="order-badge" v-if="orderUnpaid > 0">{{ orderUnpaid }}</text>
  186. </view>
  187. <text class="order-grid-text">待发货</text>
  188. </view>
  189. <view class="order-grid-item" @click="goToOrderStatus('shipped')">
  190. <view class="order-icon-wrap">
  191. <text class="order-icon">🚚</text>
  192. <text class="order-badge" v-if="orderShipped > 0">{{ orderShipped }}</text>
  193. </view>
  194. <text class="order-grid-text">待收货</text>
  195. </view>
  196. <view class="order-grid-item" @click="goToOrderStatus('refunding')">
  197. <view class="order-icon-wrap">
  198. <text class="order-icon">🔧</text>
  199. <text class="order-badge" v-if="orderAfterSale > 0">{{ orderAfterSale }}</text>
  200. </view>
  201. <text class="order-grid-text">售后</text>
  202. </view>
  203. <view class="order-grid-item" @click="goToOrders">
  204. <view class="order-icon-wrap">
  205. <text class="order-icon">📋</text>
  206. </view>
  207. <text class="order-grid-text">全部</text>
  208. </view>
  209. </view>
  210. <view class="order-extra-row">
  211. <view class="order-extra-item" @click="goToCart">
  212. <text class="order-extra-icon">🛒</text>
  213. <text class="order-extra-text">购物车</text>
  214. <text class="order-extra-badge" v-if="cartCount > 0">{{ cartCount > 99 ? '99+' : cartCount }}</text>
  215. </view>
  216. <view class="order-extra-divider"></view>
  217. <view class="order-extra-item" @click="goToAddress">
  218. <text class="order-extra-icon">📍</text>
  219. <text class="order-extra-text">收货地址</text>
  220. </view>
  221. </view>
  222. </view>
  223. </view>
  224. <!-- 规划师服务中心入口(仅规划师可见) -->
  225. <view class="service-center-section" v-if="role === 'teacher' && teacherStatus === 'approved'">
  226. <view class="service-center-card" @click="goToServiceCenter">
  227. <view class="service-center-icon">👨‍🏫</view>
  228. <view class="service-center-info">
  229. <text class="service-center-title">服务中心</text>
  230. <text class="service-center-desc">待处理任务:{{ pendingTasks }} 条 · 消息:{{ unreadMessages }} 条</text>
  231. </view>
  232. <text class="service-center-arrow">›</text>
  233. </view>
  234. </view>
  235. <!-- 规划师待审核提示 -->
  236. <view class="service-center-section" v-if="role === 'teacher' && teacherStatus !== 'approved'">
  237. <view class="service-center-card disabled-card">
  238. <view class="service-center-icon">👨‍🏫</view>
  239. <view class="service-center-info">
  240. <text class="service-center-title">服务中心</text>
  241. <text class="service-center-desc">{{ teacherStatus === 'pending' ? '申请审核中,暂未开放' : teacherStatus === 'rejected' ? '申请未通过,请联系管理员' : '规划师服务' }}</text>
  242. </view>
  243. </view>
  244. </view>
  245. <!-- 设置 + 退出 -->
  246. <view class="bottom-menu-section">
  247. <view class="menu-row" @click="goToSettings">
  248. <text class="menu-row-icon">⚙️</text>
  249. <text class="menu-row-text">设置</text>
  250. <text class="menu-row-arrow">›</text>
  251. </view>
  252. <view class="logout-btn" @click="logout">
  253. <text class="logout-text">退出登录</text>
  254. </view>
  255. </view>
  256. </template>
  257. <view class="share-qr-modal" v-if="showShareQrModal" @click="closeShareQrModal">
  258. <view class="modal-mask" @click.stop />
  259. <view class="modal-qr" @click.stop>
  260. <view class="modal-qr-header">
  261. <text class="modal-qr-title">我的分享海报</text>
  262. <text class="modal-qr-close" @click="closeShareQrModal">✕</text>
  263. </view>
  264. <view class="modal-qr-content">
  265. <view v-if="qrLoading" class="qrcode-loading">
  266. <text class="loading-spinner">⏳</text>
  267. <text class="loading-text">生成中...</text>
  268. </view>
  269. <view v-else="!qrBase64" class="qrcode-empty">
  270. <text>暂无二维码</text>
  271. </view>
  272. <view v-else class="qrcode-image">
  273. <image class="qrcode-img" :src="qrBase64" mode="aspectFit"></image>
  274. </view>
  275. <view class="qrcode-info">
  276. <text class="qrcode-text">邀请码: {{ referralCode }}</text>
  277. <text class="qrcode-desc">扫码注册,好友成为您的推荐人</text>
  278. </view>
  279. </view>
  280. <view class="modal-qr-actions">
  281. <view class="action-btn copy-btn" @click="copyReferralCode">
  282. <text class="copy-icon">📋</text>
  283. <text class="copy-text">复制邀请码</text>
  284. </view>
  285. <view class="action-btn share-btn" @click="shareToWechat">
  286. <text class="share-icon">📤</text>
  287. <text class="share-text">分享给朋友</text>
  288. </view>
  289. </view>
  290. </view>
  291. </view>
  292. <FloatingAvatar />
  293. </view>
  294. </template>
  295. <script>
  296. import FloatingAvatar from '../../components/AIFloatingAvatar.vue'
  297. import {
  298. getChildren, getFamilyMemberList,
  299. getTodayTasksByCategory, getChallengeList, getMyMembership,
  300. verifyPassword, getPointsBalance, getUserInfo,
  301. cartCount, getOrderCounts, getReferralSummary,
  302. getProfileStats, getMyReferrer, getTeamConsumption,
  303. getTeacherInfo, getTeacherPendingTasks, getTeacherUnreadCount,
  304. getReferralCode, extractReferralCode, getShareQrCode
  305. } from '../../utils/api.js'
  306. export default {
  307. components: { FloatingAvatar },
  308. data() {
  309. return {
  310. nickname: '',
  311. role: 'parent',
  312. isSwitchedChild: false,
  313. userInfo: {},
  314. memberLevel: null,
  315. membershipDays: 0,
  316. totalPoints: 0,
  317. referralEarnings: 0,
  318. teamMembers: 0,
  319. // 个人成就
  320. completedTasks: 0,
  321. streakDays: 0,
  322. rewardsEarned: 0,
  323. badgesCollected: 0,
  324. growthRecords: 0,
  325. challengesJoined: 0,
  326. healthReports: 0,
  327. // 家庭
  328. familyMembers: [],
  329. // 商城
  330. orderPending: 0,
  331. orderUnpaid: 0,
  332. orderShipped: 0,
  333. orderAfterSale: 0,
  334. cartCount: 0,
  335. // 待办
  336. pendingTasks: 0,
  337. unreadMessages: 0,
  338. teacherStatus: 'pending',
  339. profileProgress: 0,
  340. myReferrer: null,
  341. // 分享推广码
  342. showShareQrModal: false,
  343. referralCode: '',
  344. qrBase64: '',
  345. qrLoading: false,
  346. referralCodeLoaded: false,
  347. }
  348. },
  349. computed: {
  350. isLoggedIn() {
  351. return !!uni.getStorageSync('token')
  352. },
  353. roleLabel() {
  354. var map = { parent: '', child: '孩子', teacher: '规划师' }
  355. return map[this.role] || '用户'
  356. },
  357. memberLevelText() {
  358. var map = { FREE: '免费用户', FAMILY: '家庭会员', PREMIUM: '高级会员', LIFETIME: '终身会员' }
  359. return map[this.memberLevel] || this.memberLevel || ''
  360. },
  361. isLifetime() {
  362. return this.memberLevel === 'LIFETIME'
  363. },
  364. hasUpgradeSpace() {
  365. return this.memberLevel === 'FREE' || this.memberLevel === 'FAMILY'
  366. },
  367. upgradeBtnText() {
  368. return this.memberLevel === 'FAMILY' ? '升级' : '立即开通'
  369. }
  370. },
  371. onShow() {
  372. if (!this.isLoggedIn) {
  373. return
  374. }
  375. var role = uni.getStorageSync('currentRole') || uni.getStorageSync('role') || 'parent'
  376. this.role = role
  377. this.isSwitchedChild = uni.getStorageSync('isSwitchedChild') || false
  378. this.nickname = (uni.getStorageSync('userInfo') || {}).nickname || ''
  379. // 规划师加载额外数据
  380. if (role === 'teacher') {
  381. this.loadTeacherStatus()
  382. this.loadTeacherData()
  383. } else {
  384. this.loadAllData()
  385. this.loadMyReferrer()
  386. }
  387. },
  388. methods: {
  389. loadTeacherStatus() {
  390. var self = this
  391. getTeacherInfo().then(function(res) {
  392. if (res && res.code === 200 && res.data) {
  393. self.teacherStatus = res.data.verificationStatus || res.data.status || 'pending'
  394. }
  395. }).catch(function() {})
  396. },
  397. loadTeacherData() {
  398. var self = this
  399. // 并行加载待处理任务和未读消息
  400. getTeacherPendingTasks().then(function(res) {
  401. if (res && res.data && Array.isArray(res.data)) {
  402. self.pendingTasks = res.data.length
  403. } else if (res && res.data && res.data.pendingCount != null) {
  404. self.pendingTasks = res.data.pendingCount
  405. }
  406. }).catch(function() {})
  407. getTeacherUnreadCount().then(function(res) {
  408. if (res && res.data != null) {
  409. self.unreadMessages = res.data
  410. }
  411. }).catch(function() {})
  412. },
  413. loadAllData() {
  414. this.loadUserInfo()
  415. this.loadMembership()
  416. this.loadPoints()
  417. this.loadFamily()
  418. this.loadTasks()
  419. this.loadChallenges()
  420. this.loadOrderCounts()
  421. this.loadCartCount()
  422. this.loadProfileStats()
  423. this.loadReferralEarnings()
  424. this.loadTeamInfo()
  425. },
  426. loadUserInfo() {
  427. var self = this
  428. getUserInfo().then(function(res) {
  429. if (res && res.code === 200 && res.data) {
  430. self.userInfo = res.data
  431. if (!self.nickname) self.nickname = res.data.nickname || ''
  432. }
  433. }).catch(function() {})
  434. },
  435. loadMembership() {
  436. var self = this
  437. getMyMembership().then(function(res) {
  438. if (res && res.data) {
  439. self.memberLevel = res.data.memberLevel || res.data.levelCode || null
  440. self.membershipDays = res.data.membershipDays || res.data.daysRemaining || 0
  441. }
  442. }).catch(function() {})
  443. },
  444. loadPoints() {
  445. // 累计积分由 loadProfileStats 汇总家庭成员积分,此处不再单独覆盖
  446. },
  447. loadFamily() {
  448. var self = this
  449. getFamilyMemberList({ visibleOnly: true }).then(function(res) {
  450. var data = res && res.data ? res.data : []
  451. self.familyMembers = Array.isArray(data) ? data : []
  452. }).catch(function() {
  453. self.familyMembers = []
  454. })
  455. },
  456. loadTasks() {
  457. var self = this
  458. var memberId = uni.getStorageSync('currentChildId') || uni.getStorageSync('userId')
  459. if (!memberId) return
  460. getTodayTasksByCategory(memberId, 'action').then(function(res) {
  461. if (res && res.data) {
  462. self.pendingTasks = Array.isArray(res.data) ? res.data.length : 0
  463. }
  464. }).catch(function() {})
  465. // fallback to storage
  466. if (!self.pendingTasks) {
  467. self.pendingTasks = parseInt(uni.getStorageSync('pendingTaskCount') || 0)
  468. }
  469. },
  470. loadChallenges() {
  471. var self = this
  472. getChallengeList({}).then(function(res) {
  473. if (res && res.data) {
  474. // 不在此处使用,仅确保请求
  475. }
  476. }).catch(function() {})
  477. },
  478. loadOrderCounts() {
  479. var self = this
  480. getOrderCounts().then(function(res) {
  481. if (res && res.data) {
  482. self.orderPending = res.data.pending || 0
  483. self.orderUnpaid = res.data.paid || 0
  484. self.orderShipped = res.data.shipped || 0
  485. self.orderAfterSale = res.data.refunding || 0
  486. }
  487. }).catch(function() {})
  488. },
  489. loadCartCount() {
  490. var self = this
  491. cartCount().then(function(res) {
  492. if (res && res.data != null) {
  493. self.cartCount = res.data || 0
  494. }
  495. }).catch(function() {})
  496. },
  497. loadProfileStats() {
  498. var self = this
  499. getProfileStats().then(function(res) {
  500. if (res && res.code === 200 && res.data) {
  501. var d = res.data
  502. if (d.totalPoints != null) self.totalPoints = d.totalPoints
  503. if (d.streakDays != null) self.streakDays = d.streakDays
  504. if (d.completedTasks != null) self.completedTasks = d.completedTasks
  505. if (d.rewardsEarned != null) self.rewardsEarned = d.rewardsEarned
  506. if (d.badgesCollected != null) self.badgesCollected = d.badgesCollected
  507. if (d.growthRecords != null) self.growthRecords = d.growthRecords
  508. if (d.challengesJoined != null) self.challengesJoined = d.challengesJoined
  509. if (d.healthReports != null) self.healthReports = d.healthReports
  510. }
  511. }).catch(function() {})
  512. },
  513. loadReferralEarnings() {
  514. var self = this
  515. getReferralSummary().then(function(res) {
  516. if (res && res.code === 200 && res.data) {
  517. self.referralEarnings = res.data.totalEarnings || 0
  518. }
  519. }).catch(function() {})
  520. },
  521. loadTeamInfo() {
  522. var self = this
  523. getTeamConsumption(3).then(function(res) {
  524. if (res && res.data && res.data.totalMembers != null) {
  525. self.teamMembers = res.data.totalMembers
  526. }
  527. }).catch(function() {})
  528. },
  529. async loadMyReferrer() {
  530. try {
  531. var memberId = uni.getStorageSync('currentMemberId') || uni.getStorageSync('currentChildId') || 0
  532. var res = await getMyReferrer(memberId)
  533. if (res && res.code === 200 && res.data && res.data.hasReferrer) {
  534. this.myReferrer = res.data
  535. }
  536. } catch (e) {
  537. console.error('获取推荐人失败', e)
  538. }
  539. },
  540. // ===== 分享推广码 =====
  541. showShareQr() {
  542. this.showShareQrModal = true
  543. if (!this.referralCodeLoaded) {
  544. this.loadReferralCode()
  545. } else if (this.referralCode && !this.qrBase64) {
  546. this.generateQrcode()
  547. }
  548. },
  549. closeShareQrModal() {
  550. this.showShareQrModal = false
  551. },
  552. async loadReferralCode() {
  553. if (this.referralCodeLoaded) return
  554. var self = this
  555. try {
  556. var res = await getReferralCode()
  557. var code = extractReferralCode(res)
  558. self.referralCode = code || ''
  559. self.referralCodeLoaded = true
  560. if (code) {
  561. self.generateQrcode()
  562. }
  563. } catch (e) {
  564. console.error('获取推广码失败', e)
  565. self.referralCodeLoaded = true
  566. }
  567. },
  568. generateQrcode() {
  569. if (!this.referralCode) return
  570. this.qrLoading = true
  571. var self = this
  572. var scene = 'ref=' + this.referralCode
  573. // 首页路径,扫码后进入首页并绑定推荐人
  574. getShareQrCode('/pages/index-home/index', scene)
  575. .then(function(res) {
  576. if (res && res.code === 200 && res.data) {
  577. self.qrBase64 = res.data.qrCodeBase64
  578. }
  579. })
  580. .catch(function(e) {
  581. console.error('生成二维码失败', e)
  582. uni.showToast({ title: '生成失败,请重试', icon: 'none' })
  583. })
  584. .finally(function() {
  585. self.qrLoading = false
  586. })
  587. },
  588. copyReferralCode() {
  589. if (!this.referralCode) return
  590. uni.setClipboardData({
  591. data: this.referralCode,
  592. success: function() {
  593. uni.showToast({ title: '邀请码已复制', icon: 'success' })
  594. }
  595. })
  596. },
  597. shareToWechat() {
  598. if (!this.referralCode) return
  599. var path = '/pages/index-home/index?refCode=' + this.referralCode
  600. uni.share({
  601. provider: 'weixin',
  602. scene: { type: 0 },
  603. type: 5,
  604. imageUrl: this.qrBase64,
  605. path: path,
  606. title: '浠艾福 — 邀请你加入',
  607. success: function() {
  608. uni.showToast({ title: '分享成功', icon: 'success' })
  609. },
  610. fail: function() {
  611. uni.showToast({ title: '分享失败', icon: 'none' })
  612. }
  613. })
  614. },
  615. goToServiceCenter() {
  616. uni.navigateTo({ url: '/pages/service-center/index' })
  617. },
  618. goTeamTree() {
  619. uni.navigateTo({ url: '/pages/profile/team-tree' })
  620. },
  621. goLogin: function() {
  622. uni.navigateTo({ url: '/pages/login/login' })
  623. },
  624. goSkip: function() {
  625. uni.switchTab({ url: '/pages/index-home/index' })
  626. },
  627. goToEditInfo: function() {
  628. uni.navigateTo({ url: '/pages/user-edit/user-edit' })
  629. },
  630. goToPoints: function() {
  631. uni.navigateTo({ url: '/pages/points/points' })
  632. },
  633. goToStreak: function() {
  634. var memberId = uni.getStorageSync('currentMemberId') || uni.getStorageSync('currentChildId')
  635. var url = '/pages/stats/completion-rate'
  636. if (memberId) {
  637. url += '?memberId=' + memberId
  638. } else {
  639. url += '?familyView=1'
  640. }
  641. uni.navigateTo({ url: url })
  642. },
  643. goToTasks: function() {
  644. uni.navigateTo({ url: '/pages/tasks/tasks' })
  645. },
  646. goToWishes: function() {
  647. uni.navigateTo({ url: '/pages/wishes/index' })
  648. },
  649. goToBadges: function() {
  650. uni.navigateTo({ url: '/pages/rewards/badge' })
  651. },
  652. goToGrowth: function() {
  653. uni.navigateTo({ url: '/pages/growth-main/index' })
  654. },
  655. goToChallenges: function() {
  656. uni.navigateTo({ url: '/pages/health/challenge-manage' })
  657. },
  658. goToHealthReports: function() {
  659. uni.navigateTo({ url: '/pages/health/report-list' })
  660. },
  661. goToWealthCenter: function() {
  662. uni.navigateTo({ url: '/pages/promotion/index' })
  663. },
  664. goUpgrade: function() {
  665. uni.navigateTo({ url: '/pages/membership/upgrade' })
  666. },
  667. goToFamily: function() {
  668. uni.navigateTo({ url: '/pages/profile-extra/family-members' })
  669. },
  670. goMemberDetail: function(member) {
  671. uni.navigateTo({ url: '/pages/home-pages/member-home-detail?memberId=' + member.id })
  672. },
  673. goToAppointments: function() {
  674. uni.navigateTo({ url: '/pages/parent/appointments' })
  675. },
  676. goToOrders: function() {
  677. uni.navigateTo({ url: '/pages/shop/order-list/order-list' })
  678. },
  679. goToOrderStatus: function(status) {
  680. uni.navigateTo({ url: '/pages/shop/order-list/order-list?status=' + status })
  681. },
  682. goToCart: function() {
  683. uni.navigateTo({ url: '/pages/shop/cart/cart' })
  684. },
  685. goToAddress: function() {
  686. uni.navigateTo({ url: '/pages/shop/address/address' })
  687. },
  688. goToSettings: function() {
  689. uni.showActionSheet({
  690. itemList: ['完善信息', '家庭成员', '邀请加入', '隐私政策', '用户协议', '我的画像'],
  691. success: function(res) {
  692. var urls = {
  693. 0: '/pages/user-edit/user-edit',
  694. 1: '/pages/profile-extra/family-members',
  695. 2: '/pages/guide/invite/share',
  696. 3: '/pages/policy/privacy',
  697. 4: '/pages/policy/agreement',
  698. 5: '/pages/profile/portrait-edit'
  699. }
  700. if (urls[res.tapIndex]) {
  701. uni.navigateTo({ url: urls[res.tapIndex] })
  702. }
  703. }
  704. })
  705. },
  706. memberRoleLabel: function(role) {
  707. var map = { parent: '家长', child: '孩子', teacher: '规划师' }
  708. return map[role] || '成员'
  709. },
  710. onExitSwitch: function() {
  711. var self = this
  712. uni.showModal({
  713. title: '退出切换',
  714. content: '请输入家长密码以确认退出',
  715. editable: true,
  716. success: function(res) {
  717. if (res.confirm && res.content) {
  718. verifyPassword(res.content).then(function(result) {
  719. if (result.code === 200) {
  720. self.$store.commit('switchBackToParent')
  721. self.isSwitchedChild = false
  722. self.role = 'parent'
  723. uni.showToast({ title: '已退出切换', icon: 'success' })
  724. uni.reLaunch({ url: '/pages/index-home/index' })
  725. } else {
  726. uni.showToast({ title: '密码错误', icon: 'none' })
  727. }
  728. }).catch(function() {
  729. uni.showToast({ title: '验证失败', icon: 'none' })
  730. })
  731. }
  732. }
  733. })
  734. },
  735. logout: function() {
  736. uni.showModal({
  737. title: '确认退出',
  738. content: '确定要退出登录吗?',
  739. success: function(res) {
  740. if (res.confirm) {
  741. this.$store.commit('logout')
  742. uni.reLaunch({ url: '/pages/index-home/index' })
  743. }
  744. }.bind(this)
  745. })
  746. }
  747. }
  748. }
  749. </script>
  750. <style scoped>
  751. .container {
  752. min-height: 100vh;
  753. background: #F8F4EF;
  754. padding-bottom: calc(160rpx + env(safe-area-inset-bottom));
  755. }
  756. /* 登录引导 */
  757. .login-prompt {
  758. display: flex;
  759. flex-direction: column;
  760. align-items: center;
  761. padding: 200rpx 60rpx;
  762. }
  763. .prompt-icon { font-size: 100rpx; margin-bottom: 30rpx; }
  764. .prompt-title { font-size: 40rpx; font-weight: bold; color: #333; margin-bottom: 16rpx; }
  765. .prompt-desc { font-size: 26rpx; color: #999; margin-bottom: 20rpx; }
  766. .login-btn {
  767. background: linear-gradient(135deg, #667eea, #764ba2);
  768. color: #fff;
  769. border-radius: 50rpx;
  770. font-size: 30rpx;
  771. padding: 24rpx 80rpx;
  772. }
  773. .skip-link {
  774. font-size: 26rpx;
  775. color: #999;
  776. margin-top: 24rpx;
  777. text-decoration: underline;
  778. }
  779. .philosophy-card {
  780. margin: 0 40rpx 48rpx;
  781. background: linear-gradient(135deg, #FFF7ED, rgba(249, 115, 22, 0.06));
  782. border-radius: 20rpx;
  783. padding: 28rpx 32rpx;
  784. border-left: 6rpx solid #F97316;
  785. }
  786. .philosophy-title {
  787. font-size: 28rpx;
  788. font-weight: bold;
  789. color: #333;
  790. display: block;
  791. margin-bottom: 8rpx;
  792. }
  793. .philosophy-desc {
  794. font-size: 24rpx;
  795. color: #888;
  796. line-height: 1.6;
  797. display: block;
  798. }
  799. /* 退出切换 */
  800. .exit-switch {
  801. margin: 0 30rpx 16rpx;
  802. background: linear-gradient(135deg, #667eea, #764ba2);
  803. color: #fff;
  804. padding: 16rpx;
  805. border-radius: 12rpx;
  806. text-align: center;
  807. font-size: 26rpx;
  808. }
  809. /* 身份卡 */
  810. .user-card {
  811. margin: 24rpx 30rpx 20rpx;
  812. background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  813. border-radius: 24rpx;
  814. padding: 32rpx 32rpx;
  815. display: flex;
  816. justify-content: space-between;
  817. align-items: center;
  818. box-shadow: 0 8rpx 32rpx rgba(102,126,234,0.3);
  819. }
  820. .user-card-left {
  821. display: flex;
  822. align-items: center;
  823. flex: 1;
  824. }
  825. .avatar-wrap {
  826. width: 100rpx;
  827. height: 100rpx;
  828. border-radius: 50%;
  829. background: rgba(255,255,255,0.2);
  830. display: flex;
  831. align-items: center;
  832. justify-content: center;
  833. margin-right: 20rpx;
  834. overflow: hidden;
  835. border: 3rpx solid rgba(255,255,255,0.3);
  836. }
  837. .avatar-img {
  838. width: 100rpx;
  839. height: 100rpx;
  840. border-radius: 50%;
  841. }
  842. .avatar-text {
  843. font-size: 44rpx;
  844. color: #fff;
  845. }
  846. .user-meta { flex: 1; }
  847. .user-name-row {
  848. display: flex;
  849. align-items: center;
  850. gap: 12rpx;
  851. margin-bottom: 8rpx;
  852. }
  853. .nickname {
  854. font-size: 34rpx;
  855. font-weight: bold;
  856. color: #fff;
  857. }
  858. .role-tag {
  859. font-size: 20rpx;
  860. background: rgba(255,255,255,0.2);
  861. color: #fff;
  862. padding: 4rpx 14rpx;
  863. border-radius: 20rpx;
  864. }
  865. .referrer-line {
  866. font-size: 24rpx;
  867. color: rgba(255,255,255,0.7);
  868. margin-bottom: 6rpx;
  869. }
  870. .user-sub {
  871. display: flex;
  872. align-items: center;
  873. gap: 12rpx;
  874. }
  875. .level-badge {
  876. font-size: 22rpx;
  877. padding: 6rpx 20rpx;
  878. border-radius: 22rpx;
  879. font-weight: bold;
  880. border: 2rpx solid rgba(255,255,255,0.35);
  881. display: flex;
  882. align-items: center;
  883. gap: 6rpx;
  884. box-sizing: border-box;
  885. height: 40rpx;
  886. }
  887. .level-badge::before {
  888. content: '👑';
  889. font-size: 18rpx;
  890. }
  891. .level-FREE { background: linear-gradient(135deg, #9CA3AF, #6B7280); color: #fff; }
  892. .level-FREE::before { content: ''; }
  893. .level-FAMILY { background: linear-gradient(135deg, #F59E0B, #D97706); color: #fff; }
  894. .level-PREMIUM { background: linear-gradient(135deg, #6366F1, #4F46E5); color: #fff; }
  895. .level-LIFETIME {
  896. background: linear-gradient(135deg, #EC4899, #DB2777);
  897. color: #fff;
  898. box-shadow: 0 4rpx 16rpx rgba(236,72,153,0.45);
  899. border-color: rgba(255,255,255,0.55);
  900. }
  901. .days-left {
  902. font-size: 20rpx;
  903. color: rgba(255,255,255,0.85);
  904. }
  905. .upgrade-btn {
  906. background: rgba(255,255,255,0.22);
  907. border: 2rpx solid rgba(255,255,255,0.6);
  908. border-radius: 20rpx;
  909. padding: 6rpx 20rpx;
  910. flex-shrink: 0;
  911. box-sizing: border-box;
  912. height: 40rpx;
  913. display: flex;
  914. align-items: center;
  915. }
  916. .upgrade-btn-text {
  917. font-size: 20rpx;
  918. font-weight: bold;
  919. color: #fff;
  920. line-height: 1;
  921. }
  922. .user-card-right {
  923. display: flex;
  924. flex-direction: column;
  925. align-items: center;
  926. gap: 6rpx;
  927. background: rgba(255,255,255,0.15);
  928. padding: 12rpx 24rpx;
  929. border-radius: 14rpx;
  930. }
  931. .points-num {
  932. font-size: 36rpx;
  933. font-weight: bold;
  934. color: #fff;
  935. }
  936. .points-label {
  937. font-size: 20rpx;
  938. color: rgba(255,255,255,0.7);
  939. }
  940. /* 家庭成员速览 */
  941. .family-strip {
  942. margin: 0 30rpx 20rpx;
  943. background: #fff;
  944. border-radius: 20rpx;
  945. padding: 20rpx 24rpx 16rpx;
  946. box-shadow: 0 2rpx 12rpx rgba(0,0,0,0.06);
  947. }
  948. .family-strip-header {
  949. display: flex;
  950. justify-content: space-between;
  951. align-items: center;
  952. margin-bottom: 14rpx;
  953. }
  954. .family-strip-title {
  955. font-size: 26rpx;
  956. font-weight: bold;
  957. color: #333;
  958. }
  959. .family-strip-count {
  960. font-size: 22rpx;
  961. color: #999;
  962. }
  963. .family-avatars {
  964. display: flex;
  965. flex-direction: row;
  966. align-items: center;
  967. gap: 16rpx;
  968. overflow-x: auto;
  969. padding-bottom: 4rpx;
  970. }
  971. .family-avatar-item {
  972. display: flex;
  973. flex-direction: column;
  974. align-items: center;
  975. gap: 6rpx;
  976. flex-shrink: 0;
  977. }
  978. .family-avatar-wrap {
  979. width: 72rpx;
  980. height: 72rpx;
  981. border-radius: 50%;
  982. background: #f0f0f0;
  983. display: flex;
  984. align-items: center;
  985. justify-content: center;
  986. overflow: hidden;
  987. position: relative;
  988. }
  989. .family-avatar-img {
  990. width: 72rpx;
  991. height: 72rpx;
  992. border-radius: 50%;
  993. }
  994. .family-avatar-text {
  995. font-size: 28rpx;
  996. color: #666;
  997. }
  998. .family-avatar-wrap.family-avatar-add {
  999. background: #F8F4EF;
  1000. border: 2rpx dashed #ccc;
  1001. }
  1002. .family-avatar-plus {
  1003. font-size: 36rpx;
  1004. color: #999;
  1005. }
  1006. .family-avatar-tag {
  1007. position: absolute;
  1008. bottom: -4rpx;
  1009. right: -4rpx;
  1010. font-size: 14rpx;
  1011. padding: 2rpx 6rpx;
  1012. border-radius: 8rpx;
  1013. color: #fff;
  1014. }
  1015. .family-avatar-tag.tag-parent { background: #6366F1; }
  1016. .family-avatar-tag.tag-child { background: #F97316; }
  1017. .family-avatar-tag.tag-teacher { background: #10B981; }
  1018. .family-avatar-tag.tag-member { background: #9CA3AF; }
  1019. .family-avatar-name {
  1020. font-size: 20rpx;
  1021. color: #666;
  1022. white-space: nowrap;
  1023. }
  1024. /* 个人成就 */
  1025. .achievement-section {
  1026. margin: 0 30rpx 20rpx;
  1027. background: #fff;
  1028. border-radius: 20rpx;
  1029. padding: 24rpx;
  1030. box-shadow: 0 2rpx 12rpx rgba(0,0,0,0.06);
  1031. }
  1032. .section-header-row {
  1033. display: flex;
  1034. justify-content: space-between;
  1035. align-items: baseline;
  1036. margin-bottom: 16rpx;
  1037. }
  1038. .section-title {
  1039. font-size: 28rpx;
  1040. font-weight: bold;
  1041. color: #333;
  1042. }
  1043. .section-sub {
  1044. font-size: 22rpx;
  1045. color: #999;
  1046. }
  1047. .achievement-grid {
  1048. display: flex;
  1049. flex-wrap: wrap;
  1050. gap: 0;
  1051. }
  1052. .ach-item {
  1053. width: 25%;
  1054. display: flex;
  1055. flex-direction: column;
  1056. align-items: center;
  1057. gap: 6rpx;
  1058. padding: 10rpx 0;
  1059. }
  1060. .ach-icon-wrap {
  1061. width: 64rpx;
  1062. height: 64rpx;
  1063. border-radius: 50%;
  1064. display: flex;
  1065. align-items: center;
  1066. justify-content: center;
  1067. margin-bottom: 4rpx;
  1068. }
  1069. .ach-icon {
  1070. font-size: 32rpx;
  1071. line-height: 1;
  1072. }
  1073. .ach-num {
  1074. font-size: 28rpx;
  1075. font-weight: bold;
  1076. color: #333;
  1077. line-height: 1;
  1078. }
  1079. .ach-label {
  1080. font-size: 20rpx;
  1081. color: #999;
  1082. line-height: 1;
  1083. }
  1084. /* 我的订单(电商风格) */
  1085. .order-section {
  1086. margin: 0 30rpx 20rpx;
  1087. }
  1088. .order-section-header {
  1089. display: flex;
  1090. align-items: center;
  1091. justify-content: space-between;
  1092. margin-bottom: 14rpx;
  1093. }
  1094. .order-all-link {
  1095. font-size: 24rpx;
  1096. color: #999;
  1097. }
  1098. .order-card {
  1099. background: #fff;
  1100. border-radius: 20rpx;
  1101. padding: 24rpx 12rpx;
  1102. box-shadow: 0 2rpx 12rpx rgba(0,0,0,0.06);
  1103. }
  1104. .order-grid {
  1105. display: flex;
  1106. flex-wrap: wrap;
  1107. }
  1108. .order-grid-item {
  1109. width: 20%;
  1110. display: flex;
  1111. flex-direction: column;
  1112. align-items: center;
  1113. gap: 10rpx;
  1114. padding: 8rpx 0;
  1115. }
  1116. .order-icon-wrap {
  1117. position: relative;
  1118. width: 64rpx;
  1119. height: 64rpx;
  1120. display: flex;
  1121. align-items: center;
  1122. justify-content: center;
  1123. }
  1124. .order-icon {
  1125. font-size: 40rpx;
  1126. line-height: 1;
  1127. }
  1128. .order-badge {
  1129. position: absolute;
  1130. top: -8rpx;
  1131. right: -14rpx;
  1132. min-width: 30rpx;
  1133. height: 30rpx;
  1134. line-height: 30rpx;
  1135. border-radius: 15rpx;
  1136. background: #FF4D4F;
  1137. color: #fff;
  1138. font-size: 18rpx;
  1139. text-align: center;
  1140. padding: 0 6rpx;
  1141. }
  1142. .order-grid-text {
  1143. font-size: 22rpx;
  1144. color: #666;
  1145. line-height: 1;
  1146. }
  1147. .order-extra-row {
  1148. display: flex;
  1149. align-items: center;
  1150. margin-top: 20rpx;
  1151. padding-top: 20rpx;
  1152. border-top: 1rpx solid #F2F2F2;
  1153. }
  1154. .order-extra-item {
  1155. flex: 1;
  1156. display: flex;
  1157. align-items: center;
  1158. justify-content: center;
  1159. gap: 8rpx;
  1160. }
  1161. .order-extra-icon {
  1162. font-size: 28rpx;
  1163. }
  1164. .order-extra-text {
  1165. font-size: 24rpx;
  1166. color: #333;
  1167. }
  1168. .order-extra-badge {
  1169. min-width: 30rpx;
  1170. height: 30rpx;
  1171. line-height: 30rpx;
  1172. border-radius: 15rpx;
  1173. background: #F97316;
  1174. color: #fff;
  1175. font-size: 18rpx;
  1176. text-align: center;
  1177. padding: 0 6rpx;
  1178. }
  1179. .order-extra-divider {
  1180. width: 1rpx;
  1181. height: 30rpx;
  1182. background: #F2F2F2;
  1183. }
  1184. /* 规划师服务中心入口 */
  1185. .service-center-section {
  1186. margin: 0 30rpx 20rpx;
  1187. }
  1188. .service-center-card {
  1189. background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  1190. border-radius: 20rpx;
  1191. padding: 24rpx;
  1192. display: flex;
  1193. align-items: center;
  1194. gap: 20rpx;
  1195. box-shadow: 0 4rpx 16rpx rgba(102,126,234,0.25);
  1196. }
  1197. .service-center-card.disabled-card {
  1198. background: linear-gradient(135deg, #e2e8f0, #94a3b8);
  1199. box-shadow: none;
  1200. opacity: 0.8;
  1201. }
  1202. .service-center-icon {
  1203. font-size: 48rpx;
  1204. }
  1205. .service-center-info {
  1206. flex: 1;
  1207. }
  1208. .service-center-title {
  1209. font-size: 30rpx;
  1210. font-weight: bold;
  1211. color: #fff;
  1212. display: block;
  1213. }
  1214. .service-center-desc {
  1215. font-size: 22rpx;
  1216. color: rgba(255,255,255,0.8);
  1217. margin-top: 6rpx;
  1218. display: block;
  1219. }
  1220. .service-center-card.disabled-card .service-center-title {
  1221. color: #fff;
  1222. }
  1223. .service-center-card.disabled-card .service-center-desc {
  1224. color: rgba(255,255,255,0.9);
  1225. }
  1226. .service-center-arrow {
  1227. font-size: 40rpx;
  1228. color: #fff;
  1229. }
  1230. /* 设置 + 退出 */
  1231. .bottom-menu-section {
  1232. margin: 0 30rpx;
  1233. padding-bottom: 20rpx;
  1234. }
  1235. .menu-row {
  1236. width: 100%;
  1237. background: #fff;
  1238. border-radius: 16rpx;
  1239. padding: 24rpx;
  1240. display: flex;
  1241. align-items: center;
  1242. gap: 14rpx;
  1243. box-shadow: 0 2rpx 8rpx rgba(0,0,0,0.04);
  1244. margin-bottom: 20rpx;
  1245. }
  1246. .menu-row-icon {
  1247. font-size: 30rpx;
  1248. }
  1249. .menu-row-text {
  1250. flex: 1;
  1251. font-size: 26rpx;
  1252. color: #333;
  1253. }
  1254. .menu-row-arrow {
  1255. font-size: 28rpx;
  1256. color: #ccc;
  1257. }
  1258. .logout-btn {
  1259. width: 100%;
  1260. background: #fff;
  1261. border-radius: 16rpx;
  1262. padding: 24rpx;
  1263. text-align: center;
  1264. box-shadow: 0 2rpx 8rpx rgba(0,0,0,0.04);
  1265. }
  1266. .logout-text {
  1267. font-size: 26rpx;
  1268. color: #FF4D4F;
  1269. font-weight: 500;
  1270. }
  1271. /* 分享推广码浮动按钮 */
  1272. .share-qr-float {
  1273. position: fixed;
  1274. top: 40rpx;
  1275. right: 30rpx;
  1276. width: 80rpx;
  1277. height: 80rpx;
  1278. border-radius: 50%;
  1279. background: #fff;
  1280. box-shadow: 0 4rpx 16rpx rgba(0,0,0,0.15);
  1281. display: flex;
  1282. align-items: center;
  1283. justify-content: center;
  1284. z-index: 99;
  1285. }
  1286. .share-qr-icon {
  1287. font-size: 40rpx;
  1288. line-height: 1;
  1289. }
  1290. /* 分享海报弹窗 */
  1291. .share-qr-modal {
  1292. position: fixed;
  1293. top: 0;
  1294. left: 0;
  1295. right: 0;
  1296. bottom: 0;
  1297. z-index: 999;
  1298. display: flex;
  1299. align-items: center;
  1300. justify-content: center;
  1301. }
  1302. .modal-mask {
  1303. position: absolute;
  1304. top: 0;
  1305. left: 0;
  1306. right: 0;
  1307. bottom: 0;
  1308. background: rgba(0, 0, 0, 0.5);
  1309. }
  1310. .modal-qr {
  1311. position: relative;
  1312. width: 70%;
  1313. max-width: 600rpx;
  1314. background: #fff;
  1315. border-radius: 24rpx;
  1316. padding: 30rpx;
  1317. display: flex;
  1318. flex-direction: column;
  1319. align-items: center;
  1320. }
  1321. .modal-qr-header {
  1322. width: 100%;
  1323. display: flex;
  1324. justify-content: space-between;
  1325. align-items: center;
  1326. margin-bottom: 20rpx;
  1327. }
  1328. .modal-qr-title {
  1329. font-size: 30rpx;
  1330. font-weight: bold;
  1331. color: #333;
  1332. }
  1333. .modal-qr-close {
  1334. font-size: 32rpx;
  1335. color: #999;
  1336. padding: 10rpx;
  1337. }
  1338. .modal-qr-content {
  1339. display: flex;
  1340. flex-direction: column;
  1341. align-items: center;
  1342. width: 100%;
  1343. }
  1344. .qrcode-loading,
  1345. .qrcode-empty {
  1346. width: 340rpx;
  1347. height: 340rpx;
  1348. display: flex;
  1349. flex-direction: column;
  1350. align-items: center;
  1351. justify-content: center;
  1352. background: #FAFAFA;
  1353. border-radius: 16rpx;
  1354. border: 2rpx dashed #E5E7EB;
  1355. }
  1356. .loading-spinner {
  1357. font-size: 48rpx;
  1358. margin-bottom: 16rpx;
  1359. }
  1360. .loading-text {
  1361. font-size: 24rpx;
  1362. color: #94A3B8;
  1363. }
  1364. .qrcode-empty text {
  1365. font-size: 24rpx;
  1366. color: #94A3B8;
  1367. }
  1368. .qrcode-image {
  1369. width: 340rpx;
  1370. height: 340rpx;
  1371. display: flex;
  1372. align-items: center;
  1373. justify-content: center;
  1374. background: #fff;
  1375. border-radius: 16rpx;
  1376. border: 1rpx solid #E5E7EB;
  1377. overflow: hidden;
  1378. }
  1379. .qrcode-img {
  1380. width: 330rpx;
  1381. height: 330rpx;
  1382. }
  1383. .qrcode-info {
  1384. display: flex;
  1385. flex-direction: column;
  1386. align-items: center;
  1387. margin-top: 20rpx;
  1388. gap: 8rpx;
  1389. }
  1390. .qrcode-text {
  1391. font-size: 28rpx;
  1392. font-weight: bold;
  1393. color: #F97316;
  1394. }
  1395. .qrcode-desc {
  1396. font-size: 22rpx;
  1397. color: #94A3B8;
  1398. }
  1399. .modal-qr-actions {
  1400. width: 100%;
  1401. display: flex;
  1402. gap: 20rpx;
  1403. margin-top: 24rpx;
  1404. }
  1405. .action-btn {
  1406. flex: 1;
  1407. display: flex;
  1408. flex-direction: column;
  1409. align-items: center;
  1410. gap: 8rpx;
  1411. padding: 24rpx;
  1412. border-radius: 16rpx;
  1413. font-size: 24rpx;
  1414. font-weight: bold;
  1415. }
  1416. .copy-btn {
  1417. background: #FFF7ED;
  1418. color: #F97316;
  1419. border: 1rpx solid #FED7AA;
  1420. }
  1421. .share-btn {
  1422. background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  1423. color: #fff;
  1424. }
  1425. .copy-icon, .share-icon {
  1426. font-size: 40rpx;
  1427. }
  1428. .copy-text, .share-text {
  1429. font-size: 24rpx;
  1430. font-weight: bold;
  1431. }
  1432. </style>