Selaa lähdekoodia

redesign: 重写登录前首页与登录页,清晰传达产品价值

Sisyphus 2 kuukautta sitten
vanhempi
sitoutus
c464422283
2 muutettua tiedostoa jossa 676 lisäystä ja 426 poistoa
  1. 150 7
      cfc-frontend/pages/index-home/index.vue
  2. 526 419
      cfc-frontend/pages/login/login.vue

+ 150 - 7
cfc-frontend/pages/index-home/index.vue

@@ -220,9 +220,39 @@
 
   <!-- 未登录态 — 发现页/引流内容,直接内嵌,不跳转 -->
   <view v-else class="discover-container">
-<!-- 品牌定位语 -->
-    <view class="brand-strap">
-      <text class="brand-strap-text">身 · 心 · 智 · 行 · 富 &nbsp;五维幸福成长</text>
+    <!-- 品牌 Hero 区 -->
+    <view class="hero-section">
+      <image class="hero-logo" src="/static/logo.png" mode="aspectFit" />
+      <text class="hero-name">浠艾福</text>
+      <text class="hero-slogan">给全家的一站式幸福提案</text>
+      <text class="hero-powered">亿图腾科技出品</text>
+
+      <view class="hero-dims">
+        <view class="hero-dim"><text class="hero-dim-emoji">🌏</text><text class="hero-dim-label">身·健康</text></view>
+        <view class="hero-dim"><text class="hero-dim-emoji">🔥</text><text class="hero-dim-label">心·情绪</text></view>
+        <view class="hero-dim"><text class="hero-dim-emoji">⚡</text><text class="hero-dim-label">智·赋能</text></view>
+        <view class="hero-dim"><text class="hero-dim-emoji">🌿</text><text class="hero-dim-label">行·习惯</text></view>
+        <view class="hero-dim"><text class="hero-dim-emoji">💧</text><text class="hero-dim-label">富·财商</text></view>
+      </view>
+    </view>
+
+    <!-- 价值说明 -->
+    <view class="value-cards">
+      <view class="value-card">
+        <text class="value-card-emoji">📊</text>
+        <text class="value-card-title">五维健康自检</text>
+        <text class="value-card-desc">15 题快速评估全家身·心·智·行·富状态</text>
+      </view>
+      <view class="value-card">
+        <text class="value-card-emoji">👨‍👩‍👧</text>
+        <text class="value-card-title">家庭成长计划</text>
+        <text class="value-card-desc">为孩子定制每日任务,家长随时跟踪进度</text>
+      </view>
+      <view class="value-card">
+        <text class="value-card-emoji">🧠</text>
+        <text class="value-card-title">专业心智测评</text>
+        <text class="value-card-desc">预约成长规划师,获取个性化成长建议</text>
+      </view>
     </view>
 
     <!-- 五行沙盘预览 -->
@@ -1190,7 +1220,118 @@ export default {
   padding-bottom: 100rpx;
 }
 
-/* ===== 品牌定位语 ===== */
+/* ===== 品牌 Hero 区 ===== */
+.hero-section {
+  margin: 24rpx 30rpx 0;
+  padding: 40rpx 32rpx;
+  background: linear-gradient(160deg, #FFF7ED 0%, #FED7AA 60%, rgba(249,115,22,0.15) 100%);
+  border-radius: 24rpx;
+  text-align: center;
+  border: 1rpx solid rgba(249,115,22,0.12);
+}
+
+.hero-logo {
+  width: 100rpx;
+  height: 100rpx;
+  border-radius: 50rpx;
+  background: #fff;
+  box-shadow: 0 8rpx 24rpx rgba(249,115,22,0.18);
+}
+
+.hero-name {
+  display: block;
+  font-size: 48rpx;
+  font-weight: bold;
+  color: #7C2D12;
+  margin-top: 16rpx;
+  letter-spacing: 6rpx;
+}
+
+.hero-slogan {
+  display: block;
+  font-size: 26rpx;
+  color: #9A3412;
+  margin-top: 8rpx;
+  font-weight: 500;
+}
+
+.hero-powered {
+  display: block;
+  font-size: 20rpx;
+  color: rgba(124,45,18,0.45);
+  margin-top: 10rpx;
+}
+
+.hero-dims {
+  display: flex;
+  justify-content: center;
+  gap: 16rpx;
+  margin-top: 28rpx;
+  flex-wrap: wrap;
+}
+
+.hero-dim {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  gap: 6rpx;
+  padding: 12rpx 16rpx;
+  background: rgba(255,255,255,0.7);
+  border-radius: 16rpx;
+}
+
+.hero-dim-emoji {
+  font-size: 32rpx;
+}
+
+.hero-dim-label {
+  font-size: 20rpx;
+  color: #7C2D12;
+  font-weight: 500;
+  white-space: nowrap;
+}
+
+/* ===== 价值说明卡片 ===== */
+.value-cards {
+  display: flex;
+  flex-direction: column;
+  gap: 16rpx;
+  margin: 24rpx 30rpx;
+}
+
+.value-card {
+  background: #fff;
+  border-radius: 20rpx;
+  padding: 28rpx 28rpx;
+  display: flex;
+  align-items: flex-start;
+  gap: 20rpx;
+  box-shadow: 0 2rpx 12rpx rgba(0,0,0,0.06);
+  border: 1rpx solid rgba(249,115,22,0.08);
+}
+
+.value-card-emoji {
+  font-size: 44rpx;
+  flex-shrink: 0;
+  margin-top: 2rpx;
+}
+
+.value-card-title {
+  display: block;
+  font-size: 30rpx;
+  font-weight: bold;
+  color: #1F2937;
+  margin-bottom: 6rpx;
+}
+
+.value-card-desc {
+  display: block;
+  font-size: 24rpx;
+  color: #6B7280;
+  line-height: 1.5;
+}
+
+/* ===== 品牌定位语(保留旧样式,不再使用) ===== */
 .brand-strap {
   margin: 16rpx 30rpx 0;
   padding: 20rpx 28rpx;
@@ -1353,21 +1494,23 @@ export default {
   width: 100%;
   height: 88rpx;
   line-height: 88rpx;
-  background: linear-gradient(135deg, #5B9BD5, #3A7CC4);
+  background: linear-gradient(135deg, #F97316, #FB923C);
   color: #fff;
   font-size: 32rpx;
   font-weight: bold;
   border-radius: 44rpx;
   text-align: center;
   border: none;
+  box-shadow: 0 8rpx 24rpx rgba(249,115,22,0.3);
 }
 .login-btn::after {
   border: none;
 }
 .login-footer-text {
-  font-size: 20rpx;
-  color: #bbb;
+  font-size: 22rpx;
+  color: #F97316;
   margin-top: 16rpx;
+  font-weight: 500;
 }
 
 /* ===== 加载占位 ===== */

+ 526 - 419
cfc-frontend/pages/login/login.vue

@@ -1,419 +1,526 @@
-<template>
-  <view class="login-container">
-    <!-- Logo 区 -->
-    <view class="logo-section">
-      <image class="logo" src="/static/logo.png" mode="aspectFit" @error="handleImageError"></image>
-      <text class="app-name">浠艾福</text>
-      <text class="slogan">Care Family · 家庭健康成长俱乐部</text>
-    </view>
-
-    <!-- 平台简介 -->
-    <view class="intro-section">
-      <view class="intro-card">
-        <view class="intro-title">🌏 给全家的一站式幸福提案</view>
-        <view class="intro-desc">
-          围绕<span class="highlight">身、心、智、行、富</span>五个维度,用科技、智慧与温情,为您定制全生命周期解决方案。
-        </view>
-        <view class="dimension-tags">
-          <view class="dim-tag dim-body">身 · 主动健康</view>
-          <view class="dim-tag dim-mind">心 · 情绪丰盈</view>
-          <view class="dim-tag dim-wisdom">智 · 赋能未来</view>
-          <view class="dim-tag dim-action">行 · 知行合一</view>
-          <view class="dim-tag dim-wealth">富 · 物质精神</view>
-        </view>
-        <view class="intro-powered">亿图腾科技出品</view>
-      </view>
-    </view>
-
-    <!-- 手机号快捷登录 -->
-    <view class="login-form">
-      <view class="phone-section">
-          <button
-            class="phone-btn"
-            open-type="getPhoneNumber"
-            @getphonenumber="getPhoneNumber"
-          >
-            <text>手机号快捷登录</text>
-          </button>
-      </view>
-
-      <!-- 暂不登录 -->
-      <view class="skip-section">
-        <text class="skip-btn" @click="handleSkipLogin">暂不登录,先逛逛</text>
-      </view>
-
-      <view class="agreement">
-        <checkbox-group @change="onAgreementChange">
-          <label>
-            <checkbox value="agree" :checked="agreed" />
-            <text class="agreement-text">我已阅读并同意</text>
-          </label>
-        </checkbox-group>
-        <text class="link" @click="showAgreement">《用户协议》</text>
-        <text class="agreement-text">和</text>
-        <text class="link" @click="showPrivacy">《隐私政策》</text>
-      </view>
-    </view>
-  </view>
-</template>
-
-<script>
-import { wechatPhoneLogin, checkTeamInviteStatus, joinTeamByInvite, bindReferral } from '../../utils/api.js'
-
-export default {
-  data() {
-    return {
-      loading: false,
-      agreed: false,
-      nickname: '',
-      avatar: '',
-      redirectUrl: ''
-    }
-  },
-  onLoad(options) {
-      // 处理邀请码
-      if (options.inviteCode) {
-        uni.setStorageSync('inviteCode', options.inviteCode)
-        uni.setStorageSync('inviteType', options.inviteType || 'family')
-      }
-      // 存储登录后跳转地址
-      if (options.redirect) {
-        this.redirectUrl = decodeURIComponent(options.redirect)
-      }
-      // 注意:不在 onLoad 预取 login code —— 微信 code 一次性有效(5分钟且用过即废),
-      // 预取后如果用户隔几分钟再点击授权,code 已失效会报 invalid code (40029)。
-      // 正确做法:getPhoneNumber 中用户点击授权时实时获取。
-    },
-  methods: {
-    handleImageError(e) {
-      console.log('Logo 加载失败,使用默认显示')
-    },
-    onAgreementChange(e) {
-      this.agreed = e.detail.value.length > 0
-    },
-    showAgreement() {
-      uni.navigateTo({
-        url: '/pages/policy/agreement'
-      })
-    },
-    showPrivacy() {
-      uni.navigateTo({
-        url: '/pages/policy/privacy'
-      })
-    },
-    handleSkipLogin() {
-      // 不登录直接进入首页
-      uni.switchTab({ url: '/pages/index-home/index' })
-    },
-    getPhoneNumber: function(e) {
-      if (!this.agreed) {
-        uni.showToast({ title: '请先同意用户协议', icon: 'none' })
-        return
-      }
-      if (!e.detail || !e.detail.code) {
-        uni.showToast({ title: '授权失败,请重试', icon: 'none' })
-        return
-      }
-      var self = this
-      self.loading = true
-      // 点击授权时实时获取 login code(微信 code 一次性有效,不能复用旧 code)
-      uni.login({
-        provider: 'weixin',
-        success: function(loginRes) {
-          if (!loginRes.code) {
-            uni.showToast({ title: '网络异常,请重试', icon: 'none' })
-            self.loading = false
-            return
-          }
-          wechatPhoneLogin({ code: loginRes.code, phoneCode: e.detail.code }).then(function(res) {
-            self.completeLogin(res.data)
-          }).catch(function(err) {
-            uni.showToast({ title: err.message || '登录失败', icon: 'none' })
-            self.loading = false
-          })
-        },
-        fail: function() {
-          uni.showToast({ title: '网络异常,请重试', icon: 'none' })
-          self.loading = false
-        }
-      })
-    },
-    completeLogin: function(data) {
-      var self = this
-      uni.setStorageSync('token', data.token)
-      uni.setStorageSync('userId', data.userId)
-      uni.setStorageSync('role', data.role)
-      uni.setStorageSync('currentRole', data.role)
-      uni.setStorageSync('isSwitchedChild', false)
-      uni.setStorageSync('isSwitchedTeacher', false)
-      if (data.openid) uni.setStorageSync('openid', data.openid)
-      // familyId 必须持久化:家庭在注册时已由后端创建,缺失会导致页面误判无家庭而重复创建
-      uni.setStorageSync('userInfo', { nickname: data.nickname, userId: data.userId, familyId: data.familyId })
-      if (data.phone) uni.setStorageSync('phone', data.phone)
-      if (data.teacherStatus) uni.setStorageSync('teacherStatus', data.teacherStatus)
-      if (data.teacherRejectReason) uni.setStorageSync('teacherRejectReason', data.teacherRejectReason)
-      self.$store.commit('setFamilyId', data.familyId)
-      uni.showToast({ title: '登录成功', icon: 'success' })
-      self.loading = false
-      if (data.needsRoleSelect) {
-        uni.redirectTo({ url: '/pages/user-edit/user-edit?token=' + data.token + '&userId=' + data.userId + '&autoParent=true' })
-      } else {
-        self.handlePostLoginRouting()
-      }
-    },
-    handlePostLoginRouting() {
-      const inviteCode = uni.getStorageSync('inviteCode')
-      const inviteType = uni.getStorageSync('inviteType')
-
-      if (inviteCode && inviteType === 'team') {
-        this.handleTeamInviteFlow(inviteCode)
-      } else if (inviteCode) {
-        // 非 team 邀请码(如分享文章/商品/活动的邀请码),绑定推荐关系
-        this.handleReferralBind(inviteCode)
-      } else {
-        this.navigateToHome()
-      }
-    },
-    async handleReferralBind(inviteCode) {
-      try {
-        var res = await bindReferral(inviteCode)
-        if (res && res.code === 200) {
-          console.log('登录后绑定邀请码成功', inviteCode)
-        } else if (res && res.code === 400) {
-          console.log('用户已有邀请人,跳过绑定')
-        }
-      } catch (e) {
-        console.log('邀请码绑定失败', e)
-      }
-      uni.removeStorageSync('inviteCode')
-      uni.removeStorageSync('inviteType')
-      this.navigateToHome()
-    },
-    async handleTeamInviteFlow(inviteCode) {
-      try {
-        const res = await checkTeamInviteStatus(inviteCode)
-        const status = res.data.status
-
-        if (status === 'not_planner') {
-          uni.redirectTo({ url: `/pages/guide/register/index?inviteCode=${inviteCode}` })
-        } else if (status === 'not_in_team') {
-          uni.showModal({
-            title: '加入团队',
-            content: '是否加入该规划师团队?',
-            success: async (confirmRes) => {
-              if (confirmRes.confirm) {
-                try {
-                  await joinTeamByInvite(inviteCode)
-                  uni.showToast({ title: '加入成功', icon: 'success' })
-                  uni.removeStorageSync('inviteCode')
-                  uni.removeStorageSync('inviteType')
-                  this.navigateToHome()
-                } catch (e) {
-                  uni.showToast({ title: e.message || '加入失败', icon: 'none' })
-                }
-              } else {
-                this.navigateToHome()
-              }
-            }
-          })
-        } else if (status === 'already_in_team') {
-          uni.showModal({
-            title: '加入新团队',
-            content: '您已在其他团队中,离开当前团队并加入新团队?',
-            success: async (confirmRes) => {
-              if (confirmRes.confirm) {
-                try {
-                  await joinTeamByInvite(inviteCode)
-                  uni.showToast({ title: '加入成功', icon: 'success' })
-                  uni.removeStorageSync('inviteCode')
-                  uni.removeStorageSync('inviteType')
-                  this.navigateToHome()
-                } catch (e) {
-                  uni.showToast({ title: e.message || '加入失败', icon: 'none' })
-                }
-              } else {
-                this.navigateToHome()
-              }
-            }
-          })
-        } else {
-          this.navigateToHome()
-        }
-      } catch (e) {
-        uni.showToast({ title: e.message || '处理邀请失败', icon: 'none' })
-        this.navigateToHome()
-      }
-    },
-    navigateToHome() {
-      if (this.redirectUrl) {
-        const redirect = this.redirectUrl
-        this.redirectUrl = ''
-        // 注意: redirectTo 不支持跳转 tabBar 页面,必须用 switchTab
-        // 只有 pages.json tabBar.list 注册的页面才能用 switchTab
-        var tabBarPages = ['/pages/index-home/index', '/pages/mind/index', '/pages/body/index', '/pages/wisdom/index', '/pages/wealth/index']
-        if (tabBarPages.indexOf(redirect) !== -1) {
-          uni.switchTab({ url: redirect })
-        } else {
-          uni.redirectTo({ url: redirect })
-        }
-      } else {
-        uni.switchTab({ url: '/pages/index-home/index' })
-      }
-    }
-  }
-}
-</script>
-
-<style scoped>
-.login-container {
-  min-height: 100vh;
-  background: linear-gradient(160deg, #FFF7ED 0%, #F97316 100%);
-  padding: 60rpx 40rpx;
-}
-
-/* ===== Logo 区 ===== */
-.logo-section {
-  text-align: center;
-  margin-bottom: 48rpx;
-}
-
-.logo {
-  width: 160rpx;
-  height: 160rpx;
-  border-radius: 80rpx;
-  background: #fff;
-}
-
-.app-name {
-  display: block;
-  font-size: 52rpx;
-  font-weight: bold;
-  color: #fff;
-  margin-top: 20rpx;
-}
-
-.slogan {
-  display: block;
-  font-size: 26rpx;
-  color: rgba(255, 255, 255, 0.8);
-  margin-top: 10rpx;
-}
-
-/* ===== 平台简介卡片 ===== */
-.intro-section {
-  margin-bottom: 48rpx;
-}
-
-.intro-card {
-  background: rgba(255, 255, 255, 0.15);
-  border: 1rpx solid rgba(255, 255, 255, 0.3);
-  border-radius: 20rpx;
-  padding: 32rpx 28rpx;
-  backdrop-filter: blur(10px);
-}
-
-.intro-title {
-  font-size: 30rpx;
-  font-weight: bold;
-  color: #fff;
-  text-align: center;
-  margin-bottom: 16rpx;
-}
-
-.intro-desc {
-  font-size: 24rpx;
-  color: rgba(255, 255, 255, 0.9);
-  line-height: 1.7;
-  text-align: center;
-  margin-bottom: 24rpx;
-}
-
-.intro-desc .highlight {
-  color: #FFD700;
-  font-weight: 600;
-}
-
-.dimension-tags {
-  display: flex;
-  flex-wrap: wrap;
-  justify-content: center;
-  gap: 12rpx;
-  margin-bottom: 20rpx;
-}
-
-.dim-tag {
-  font-size: 22rpx;
-  padding: 8rpx 16rpx;
-  border-radius: 30rpx;
-  background: rgba(255, 255, 255, 0.2);
-  color: #fff;
-  font-weight: 500;
-}
-
-.dim-body { background: rgba(91, 155, 213, 0.4); }
-.dim-mind { background: rgba(236, 72, 153, 0.4); }
-.dim-wisdom { background: rgba(139, 92, 246, 0.4); }
-.dim-action { background: rgba(16, 185, 129, 0.4); }
-.dim-wealth { background: rgba(245, 158, 11, 0.4); }
-
-.intro-powered {
-  text-align: center;
-  font-size: 20rpx;
-  color: rgba(255, 255, 255, 0.6);
-}
-
-/* ===== 登录表单 ===== */
-.login-form {
-  background: #fff;
-  border-radius: 20rpx;
-  padding: 40rpx;
-}
-
-.phone-section {
-  margin-bottom: 32rpx;
-}
-
-.phone-btn {
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  gap: 10rpx;
-  width: 100%;
-  height: 88rpx;
-  background: #07c160;
-  color: #fff;
-  border-radius: 44rpx;
-  font-size: 32rpx;
-}
-
-/* ===== 跳过登录 ===== */
-.skip-section {
-  text-align: center;
-  margin-bottom: 20rpx;
-}
-
-.skip-btn {
-  font-size: 28rpx;
-  color: #999;
-  text-decoration: underline;
-  padding: 16rpx 0;
-}
-
-.agreement {
-  margin-top: 16rpx;
-  text-align: center;
-  font-size: 24rpx;
-}
-
-.agreement-text {
-  color: #666;
-}
-
-.link {
-  color: #667eea;
-}
-
-.agreement-text.link {
-  text-decoration: underline;
-}
-</style>
+<template>
+  <view class="login-container">
+    <!-- Logo 区 -->
+    <view class="logo-section">
+      <image class="logo" src="/static/logo.png" mode="aspectFit" @error="handleImageError"></image>
+      <text class="app-name">浠艾福</text>
+      <text class="slogan">给全家的一站式幸福提案</text>
+      <text class="powered">亿图腾科技出品</text>
+    </view>
+
+    <!-- 角色价值说明 -->
+    <view class="value-section">
+      <view class="value-title">你能得到什么</view>
+
+      <!-- 家长端 -->
+      <view class="role-card role-parent">
+        <view class="role-badge">
+          <text class="role-badge-icon">👨‍👩‍👧</text>
+          <text class="role-badge-text">家长</text>
+        </view>
+        <view class="role-items">
+          <view class="role-item"><text class="role-item-icon">📋</text><text class="role-item-text">一键创建家庭成长计划</text></view>
+          <view class="role-item"><text class="role-item-icon">📊</text><text class="role-item-text">跟踪孩子任务完成情况</text></view>
+          <view class="role-item"><text class="role-item-icon">🧠</text><text class="role-item-text">预约专业心智测评</text></view>
+          <view class="role-item"><text class="role-item-icon">🩺</text><text class="role-item-text">健康报告 AI 智能解析</text></view>
+        </view>
+      </view>
+
+      <!-- 孩子端 -->
+      <view class="role-card role-child">
+        <view class="role-badge role-badge-child">
+          <text class="role-badge-icon">👶</text>
+          <text class="role-badge-text">孩子</text>
+        </view>
+        <view class="role-items">
+          <view class="role-item"><text class="role-item-icon">🎯</text><text class="role-item-text">每日趣味任务挑战</text></view>
+          <view class="role-item"><text class="role-item-icon">🏆</text><text class="role-item-text">积分兑换心愿奖励</text></view>
+          <view class="role-item"><text class="role-item-icon">🧩</text><text class="role-item-text">专注力小游戏训练</text></view>
+        </view>
+      </view>
+    </view>
+
+    <!-- 五维能量 -->
+    <view class="wuxing-section">
+      <view class="wuxing-title">五维成长体系</view>
+      <view class="wuxing-tags">
+        <view class="wuxing-tag wuxing-body"><text class="wuxing-emoji">🌏</text>身·健康</view>
+        <view class="wuxing-tag wuxing-mind"><text class="wuxing-emoji">🔥</text>心·情绪</view>
+        <view class="wuxing-tag wuxing-wisdom"><text class="wuxing-emoji">⚡</text>智·赋能</view>
+        <view class="wuxing-tag wuxing-action"><text class="wuxing-emoji">🌿</text>行·习惯</view>
+        <view class="wuxing-tag wuxing-wealth"><text class="wuxing-emoji">💧</text>富·财商</view>
+      </view>
+      <text class="wuxing-desc">五行平衡 · 全面评估全家健康状态</text>
+    </view>
+
+    <!-- 登录表单 -->
+    <view class="login-form">
+      <view class="phone-section">
+        <button
+          class="phone-btn"
+          open-type="getPhoneNumber"
+          @getphonenumber="getPhoneNumber"
+        >
+          <text class="phone-btn-icon">📱</text>
+          <text>手机号快捷登录</text>
+        </button>
+      </view>
+
+      <!-- 暂不登录 -->
+      <view class="skip-section">
+        <text class="skip-btn" @click="handleSkipLogin">暂不登录,先逛逛</text>
+      </view>
+
+      <view class="agreement">
+        <checkbox-group @change="onAgreementChange">
+          <label>
+            <checkbox value="agree" :checked="agreed" />
+            <text class="agreement-text">我已阅读并同意</text>
+          </label>
+        </checkbox-group>
+        <text class="link" @click="showAgreement">《用户协议》</text>
+        <text class="agreement-text">和</text>
+        <text class="link" @click="showPrivacy">《隐私政策》</text>
+      </view>
+    </view>
+  </view>
+</template>
+
+<script>
+import { wechatPhoneLogin, checkTeamInviteStatus, joinTeamByInvite, bindReferral } from '../../utils/api.js'
+
+export default {
+  data() {
+    return {
+      loading: false,
+      agreed: false,
+      nickname: '',
+      avatar: '',
+      redirectUrl: ''
+    }
+  },
+  onLoad(options) {
+      // 处理邀请码
+      if (options.inviteCode) {
+        uni.setStorageSync('inviteCode', options.inviteCode)
+        uni.setStorageSync('inviteType', options.inviteType || 'family')
+      }
+      // 存储登录后跳转地址
+      if (options.redirect) {
+        this.redirectUrl = decodeURIComponent(options.redirect)
+      }
+      // 注意:不在 onLoad 预取 login code —— 微信 code 一次性有效(5分钟且用过即废),
+      // 预取后如果用户隔几分钟再点击授权,code 已失效会报 invalid code (40029)。
+      // 正确做法:getPhoneNumber 中用户点击授权时实时获取。
+    },
+  methods: {
+    handleImageError(e) {
+      console.log('Logo 加载失败,使用默认显示')
+    },
+    onAgreementChange(e) {
+      this.agreed = e.detail.value.length > 0
+    },
+    showAgreement() {
+      uni.navigateTo({
+        url: '/pages/policy/agreement'
+      })
+    },
+    showPrivacy() {
+      uni.navigateTo({
+        url: '/pages/policy/privacy'
+      })
+    },
+    handleSkipLogin() {
+      // 不登录直接进入首页
+      uni.switchTab({ url: '/pages/index-home/index' })
+    },
+    getPhoneNumber: function(e) {
+      if (!this.agreed) {
+        uni.showToast({ title: '请先同意用户协议', icon: 'none' })
+        return
+      }
+      if (!e.detail || !e.detail.code) {
+        uni.showToast({ title: '授权失败,请重试', icon: 'none' })
+        return
+      }
+      var self = this
+      self.loading = true
+      // 点击授权时实时获取 login code(微信 code 一次性有效,不能复用旧 code)
+      uni.login({
+        provider: 'weixin',
+        success: function(loginRes) {
+          if (!loginRes.code) {
+            uni.showToast({ title: '网络异常,请重试', icon: 'none' })
+            self.loading = false
+            return
+          }
+          wechatPhoneLogin({ code: loginRes.code, phoneCode: e.detail.code }).then(function(res) {
+            self.completeLogin(res.data)
+          }).catch(function(err) {
+            uni.showToast({ title: err.message || '登录失败', icon: 'none' })
+            self.loading = false
+          })
+        },
+        fail: function() {
+          uni.showToast({ title: '网络异常,请重试', icon: 'none' })
+          self.loading = false
+        }
+      })
+    },
+    completeLogin: function(data) {
+      var self = this
+      uni.setStorageSync('token', data.token)
+      uni.setStorageSync('userId', data.userId)
+      uni.setStorageSync('role', data.role)
+      uni.setStorageSync('currentRole', data.role)
+      uni.setStorageSync('isSwitchedChild', false)
+      uni.setStorageSync('isSwitchedTeacher', false)
+      if (data.openid) uni.setStorageSync('openid', data.openid)
+      // familyId 必须持久化:家庭在注册时已由后端创建,缺失会导致页面误判无家庭而重复创建
+      uni.setStorageSync('userInfo', { nickname: data.nickname, userId: data.userId, familyId: data.familyId })
+      if (data.phone) uni.setStorageSync('phone', data.phone)
+      if (data.teacherStatus) uni.setStorageSync('teacherStatus', data.teacherStatus)
+      if (data.teacherRejectReason) uni.setStorageSync('teacherRejectReason', data.teacherRejectReason)
+      self.$store.commit('setFamilyId', data.familyId)
+      uni.showToast({ title: '登录成功', icon: 'success' })
+      self.loading = false
+      if (data.needsRoleSelect) {
+        uni.redirectTo({ url: '/pages/user-edit/user-edit?token=' + data.token + '&userId=' + data.userId + '&autoParent=true' })
+      } else {
+        self.handlePostLoginRouting()
+      }
+    },
+    handlePostLoginRouting() {
+      const inviteCode = uni.getStorageSync('inviteCode')
+      const inviteType = uni.getStorageSync('inviteType')
+
+      if (inviteCode && inviteType === 'team') {
+        this.handleTeamInviteFlow(inviteCode)
+      } else if (inviteCode) {
+        // 非 team 邀请码(如分享文章/商品/活动的邀请码),绑定推荐关系
+        this.handleReferralBind(inviteCode)
+      } else {
+        this.navigateToHome()
+      }
+    },
+    async handleReferralBind(inviteCode) {
+      try {
+        var res = await bindReferral(inviteCode)
+        if (res && res.code === 200) {
+          console.log('登录后绑定邀请码成功', inviteCode)
+        } else if (res && res.code === 400) {
+          console.log('用户已有邀请人,跳过绑定')
+        }
+      } catch (e) {
+        console.log('邀请码绑定失败', e)
+      }
+      uni.removeStorageSync('inviteCode')
+      uni.removeStorageSync('inviteType')
+      this.navigateToHome()
+    },
+    async handleTeamInviteFlow(inviteCode) {
+      try {
+        const res = await checkTeamInviteStatus(inviteCode)
+        const status = res.data.status
+
+        if (status === 'not_planner') {
+          uni.redirectTo({ url: `/pages/guide/register/index?inviteCode=${inviteCode}` })
+        } else if (status === 'not_in_team') {
+          uni.showModal({
+            title: '加入团队',
+            content: '是否加入该规划师团队?',
+            success: async (confirmRes) => {
+              if (confirmRes.confirm) {
+                try {
+                  await joinTeamByInvite(inviteCode)
+                  uni.showToast({ title: '加入成功', icon: 'success' })
+                  uni.removeStorageSync('inviteCode')
+                  uni.removeStorageSync('inviteType')
+                  this.navigateToHome()
+                } catch (e) {
+                  uni.showToast({ title: e.message || '加入失败', icon: 'none' })
+                }
+              } else {
+                this.navigateToHome()
+              }
+            }
+          })
+        } else if (status === 'already_in_team') {
+          uni.showModal({
+            title: '加入新团队',
+            content: '您已在其他团队中,离开当前团队并加入新团队?',
+            success: async (confirmRes) => {
+              if (confirmRes.confirm) {
+                try {
+                  await joinTeamByInvite(inviteCode)
+                  uni.showToast({ title: '加入成功', icon: 'success' })
+                  uni.removeStorageSync('inviteCode')
+                  uni.removeStorageSync('inviteType')
+                  this.navigateToHome()
+                } catch (e) {
+                  uni.showToast({ title: e.message || '加入失败', icon: 'none' })
+                }
+              } else {
+                this.navigateToHome()
+              }
+            }
+          })
+        } else {
+          this.navigateToHome()
+        }
+      } catch (e) {
+        uni.showToast({ title: e.message || '处理邀请失败', icon: 'none' })
+        this.navigateToHome()
+      }
+    },
+    navigateToHome() {
+      if (this.redirectUrl) {
+        const redirect = this.redirectUrl
+        this.redirectUrl = ''
+        // 注意: redirectTo 不支持跳转 tabBar 页面,必须用 switchTab
+        // 只有 pages.json tabBar.list 注册的页面才能用 switchTab
+        var tabBarPages = ['/pages/index-home/index', '/pages/mind/index', '/pages/body/index', '/pages/wisdom/index', '/pages/wealth/index']
+        if (tabBarPages.indexOf(redirect) !== -1) {
+          uni.switchTab({ url: redirect })
+        } else {
+          uni.redirectTo({ url: redirect })
+        }
+      } else {
+        uni.switchTab({ url: '/pages/index-home/index' })
+      }
+    }
+  }
+}
+</script>
+
+<style scoped>
+.login-container {
+  min-height: 100vh;
+  background: linear-gradient(160deg, #FFF7ED 0%, #FED7AA 60%, #F97316 100%);
+  padding: 48rpx 40rpx 40rpx;
+}
+
+/* ===== Logo 区 ===== */
+.logo-section {
+  text-align: center;
+  margin-bottom: 40rpx;
+}
+
+.logo {
+  width: 120rpx;
+  height: 120rpx;
+  border-radius: 60rpx;
+  background: #fff;
+  box-shadow: 0 8rpx 32rpx rgba(249,115,22,0.2);
+}
+
+.app-name {
+  display: block;
+  font-size: 52rpx;
+  font-weight: bold;
+  color: #fff;
+  margin-top: 16rpx;
+  letter-spacing: 8rpx;
+}
+
+.slogan {
+  display: block;
+  font-size: 26rpx;
+  color: rgba(255, 255, 255, 0.85);
+  margin-top: 8rpx;
+}
+
+.powered {
+  display: block;
+  font-size: 20rpx;
+  color: rgba(255, 255, 255, 0.55);
+  margin-top: 12rpx;
+}
+
+/* ===== 角色价值说明 ===== */
+.value-section {
+  margin-bottom: 32rpx;
+}
+
+.value-title {
+  font-size: 28rpx;
+  font-weight: 600;
+  color: #fff;
+  margin-bottom: 20rpx;
+  padding-left: 4rpx;
+}
+
+.role-card {
+  background: rgba(255, 255, 255, 0.92);
+  border-radius: 20rpx;
+  padding: 24rpx 28rpx;
+  margin-bottom: 16rpx;
+  box-shadow: 0 4rpx 20rpx rgba(0,0,0,0.08);
+}
+
+.role-badge {
+  display: inline-flex;
+  align-items: center;
+  gap: 8rpx;
+  background: linear-gradient(135deg, #FF8C42, #FF6B2D);
+  border-radius: 30rpx;
+  padding: 8rpx 20rpx;
+  margin-bottom: 20rpx;
+}
+
+.role-badge-child {
+  background: linear-gradient(135deg, #FF6B9D, #FF3D7F);
+}
+
+.role-badge-icon {
+  font-size: 28rpx;
+}
+
+.role-badge-text {
+  font-size: 26rpx;
+  font-weight: 600;
+  color: #fff;
+}
+
+.role-items {
+  display: flex;
+  flex-direction: column;
+  gap: 14rpx;
+}
+
+.role-item {
+  display: flex;
+  align-items: center;
+  gap: 12rpx;
+}
+
+.role-item-icon {
+  font-size: 28rpx;
+  flex-shrink: 0;
+}
+
+.role-item-text {
+  font-size: 26rpx;
+  color: #374151;
+  line-height: 1.5;
+}
+
+/* ===== 五维能量 ===== */
+.wuxing-section {
+  background: rgba(255, 255, 255, 0.92);
+  border-radius: 20rpx;
+  padding: 28rpx;
+  margin-bottom: 32rpx;
+  text-align: center;
+  box-shadow: 0 4rpx 20rpx rgba(0,0,0,0.08);
+}
+
+.wuxing-title {
+  font-size: 28rpx;
+  font-weight: 600;
+  color: #1F2937;
+  margin-bottom: 20rpx;
+}
+
+.wuxing-tags {
+  display: flex;
+  flex-wrap: wrap;
+  justify-content: center;
+  gap: 12rpx;
+  margin-bottom: 16rpx;
+}
+
+.wuxing-tag {
+  font-size: 24rpx;
+  padding: 10rpx 20rpx;
+  border-radius: 30rpx;
+  color: #fff;
+  font-weight: 500;
+}
+
+.wuxing-body { background: rgba(255, 140, 66, 0.85); }
+.wuxing-mind { background: rgba(255, 107, 157, 0.85); }
+.wuxing-wisdom { background: rgba(99, 102, 241, 0.85); }
+.wuxing-action { background: rgba(16, 185, 129, 0.85); }
+.wuxing-wealth { background: rgba(245, 158, 11, 0.85); }
+
+.wuxing-emoji {
+  margin-right: 4rpx;
+}
+
+.wuxing-desc {
+  font-size: 22rpx;
+  color: #9CA3AF;
+}
+
+/* ===== 登录表单 ===== */
+.login-form {
+  background: #fff;
+  border-radius: 20rpx;
+  padding: 40rpx;
+  box-shadow: 0 4rpx 24rpx rgba(0,0,0,0.06);
+}
+
+.phone-section {
+  margin-bottom: 28rpx;
+}
+
+.phone-btn {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  gap: 12rpx;
+  width: 100%;
+  height: 88rpx;
+  background: #07c160;
+  color: #fff;
+  border-radius: 44rpx;
+  font-size: 32rpx;
+  font-weight: 500;
+  border: none;
+}
+
+.phone-btn::after {
+  border: none;
+}
+
+.phone-btn-icon {
+  font-size: 36rpx;
+}
+
+/* ===== 跳过登录 ===== */
+.skip-section {
+  text-align: center;
+  margin-bottom: 24rpx;
+}
+
+.skip-btn {
+  font-size: 28rpx;
+  color: #F97316;
+  font-weight: 500;
+  padding: 12rpx 24rpx;
+  background: rgba(249, 115, 22, 0.08);
+  border-radius: 30rpx;
+}
+
+/* ===== 协议 ===== */
+.agreement {
+  margin-top: 8rpx;
+  text-align: center;
+  font-size: 24rpx;
+  line-height: 1.6;
+}
+
+.agreement checkbox {
+  transform: scale(0.8);
+}
+
+.agreement-text {
+  color: #9CA3AF;
+}
+
+.link {
+  color: #5B9BD5;
+  text-decoration: underline;
+}
+</style>