Sfoglia il codice sorgente

条件编译修改

Zhangbw 2 mesi fa
parent
commit
04f40d0891

+ 56 - 0
src/App.vue

@@ -8,11 +8,67 @@ export default {
   },
   onLaunch: function() {
     console.log('App Launch')
+
+    // #ifdef H5
+    // 检查URL中是否有微信授权回调的code参数
+    const urlParams = new URLSearchParams(window.location.search)
+    const code = urlParams.get('code')
+
+    if (code) {
+      console.log('[App] 检测到微信授权回调code:', code)
+      // H5环境下使用原生跳转
+      const loginUrl = `${window.location.origin}${window.location.pathname}#/pages/login/login?code=${code}`
+      console.log('[App] 跳转到登录页:', loginUrl)
+      window.location.replace(loginUrl)
+      return
+    }
+    // #endif
+
     // 如果已登录,启动用户状态定时检查
     if (isLoggedIn()) {
       console.log('[App] 用户已登录,启动状态检查')
       startStatusCheck()
     }
+
+    // #ifdef H5
+    // H5键盘适配
+    let baseLayoutHeight = window.innerHeight || document.documentElement.clientHeight || 0
+    const setKeyboardOffset = () => {
+      const vv = window.visualViewport
+      if (!vv) {
+        document.documentElement.style.setProperty('--keyboard-offset', '0px')
+        return
+      }
+
+      const currentLayoutHeight = window.innerHeight || document.documentElement.clientHeight || 0
+      baseLayoutHeight = Math.max(baseLayoutHeight, currentLayoutHeight)
+
+      const vvHeight = vv.height
+      const offsetTop = vv.offsetTop || 0
+      const keyboardHeight = Math.max(0, baseLayoutHeight - vvHeight - offsetTop)
+      document.documentElement.style.setProperty('--keyboard-offset', `${keyboardHeight}px`)
+    }
+
+    this.__keyboardOffsetHandler = setKeyboardOffset
+
+    if (window.visualViewport) {
+      window.visualViewport.addEventListener('resize', setKeyboardOffset)
+      window.visualViewport.addEventListener('scroll', setKeyboardOffset)
+    }
+
+    window.addEventListener('resize', () => {
+      const h = window.innerHeight || document.documentElement.clientHeight || 0
+      baseLayoutHeight = Math.max(baseLayoutHeight, h)
+      setKeyboardOffset()
+    })
+
+    window.addEventListener('focusin', setKeyboardOffset)
+    window.addEventListener('focusout', () => {
+      document.documentElement.style.setProperty('--keyboard-offset', '0px')
+    })
+
+    setKeyboardOffset()
+    // #endif
   },
   onShow: function() {
 	  const accountInfo = wx.getAccountInfoSync();

+ 21 - 2
src/pages/index/index.vue

@@ -1,7 +1,12 @@
 <template>
   <view class="page-container">
+    <!-- #ifdef MP-WEIXIN -->
 	  <PoolDev v-if="app.globalData.envVersion == 'develop'"></PoolDev>
     <scroll-view class="scroll-view" scroll-y v-else>
+    <!-- #endif -->
+    <!-- #ifdef H5 -->
+    <scroll-view class="scroll-view" scroll-y>
+    <!-- #endif -->
       <view class="content-wrapper">
 
         <!-- 顶部查询卡片 -->
@@ -243,7 +248,9 @@
         <view class="bottom-safe-area"></view>
       </view>
     </scroll-view>
+    <!-- #ifdef MP-WEIXIN -->
 	<ComTabBar :url="'/pages/index/index'"></ComTabBar>
+    <!-- #endif -->
   </view>
 </template>
 
@@ -255,8 +262,10 @@ import DateSelector from '../../components/DateSelector.vue'
 
 // 引入 onShow 生命周期
 import { onShow } from '@dcloudio/uni-app'
+// #ifdef MP-WEIXIN
 import PoolDev from '../../components/poolDev.vue'
 import ComTabBar from '../../components/ComTabBar.vue'
+// #endif
 
 const keyword = ref('')
 const loading = ref(false)
@@ -270,7 +279,9 @@ const isLoggedIn = ref(false)
 const selectedDate = ref('') // 选中的日期
 const currentStockCode = ref('') // 当前查询的股票代码
 let timer = null
+// #ifdef MP-WEIXIN
 const app = getApp();
+// #endif
 
 /**
  * 页面加载时检查登录状态
@@ -284,17 +295,25 @@ onMounted(() => {
  * 页面显示时检查登录状态(从登录页返回时会触发)
  */
 onShow(() => {
-	
+  // #ifdef MP-WEIXIN
 	uni.hideTabBar({
 		success: () => {},
 		fail: () => {}
 	});
-	
+  // #endif
+
   isLoggedIn.value = checkLoginStatus()
   // 设置导航栏标题
+  // #ifdef MP-WEIXIN
   uni.setNavigationBarTitle({
   	title: app.globalData.envVersion == 'develop' ? '首页' : '量化交易大师'
   })
+  // #endif
+  // #ifdef H5
+  uni.setNavigationBarTitle({
+  	title: '量化交易大师'
+  })
+  // #endif
 })
 
 /**

+ 440 - 118
src/pages/login/login.vue

@@ -1,10 +1,17 @@
 <template>
   <view class="login-container">
     <!-- 返回按钮 -->
+    <!-- #ifdef H5 -->
+    <view class="back-btn" @click="handleBack">
+      <text class="back-icon">←</text>
+    </view>
+    <!-- #endif -->
+    <!-- #ifdef MP-WEIXIN -->
     <view class="back-button" @click="handleBack">
       <text class="back-icon">←</text>
       <text class="back-text">返回</text>
     </view>
+    <!-- #endif -->
 
     <!-- 背景装饰 -->
     <view class="bg-decoration">
@@ -14,15 +21,58 @@
 
     <!-- Logo和标题 -->
     <view class="header">
+      <!-- #ifdef H5 -->
+      <image class="logo" src="/static/images/logo.png" mode="aspectFit"></image>
+      <!-- #endif -->
+      <!-- #ifdef MP-WEIXIN -->
       <image class="logo" :src="getImageUrl('/static/images/logo.png')" mode="aspectFit"></image>
+      <!-- #endif -->
       <text class="title">量化交易大师</text>
       <text class="subtitle">专业的市场量化分析工具</text>
     </view>
 
     <!-- 登录按钮区域 -->
     <view class="login-actions">
+      <!-- #ifdef H5 -->
+      <!-- 初始状态:显示登录和注册按钮 -->
+      <view v-if="currentStep === 'initial'" class="button-group">
+        <button class="login-btn primary-btn" @click="handleLoginClick">
+          <text class="btn-text">登录</text>
+        </button>
+        <button class="login-btn secondary-btn" @click="handleRegisterClick">
+          <text class="btn-text">注册</text>
+        </button>
+      </view>
+
+      <!-- 输入手机号密码表单 -->
+      <view v-else-if="currentStep === 'input'" class="form-container">
+        <view class="form-title">{{ isLogin ? '登录账号' : '注册账号' }}</view>
+        <input
+          v-model="phoneNumber"
+          type="number"
+          placeholder="请输入手机号"
+          maxlength="11"
+          class="form-input"
+        />
+        <input
+          v-model="password"
+          type="text"
+          :password="true"
+          placeholder="请输入密码"
+          class="form-input"
+        />
+        <button class="login-btn primary-btn" @click="handleSubmit">
+          <text class="btn-text">{{ isLogin ? '登录' : '注册' }}</text>
+        </button>
+        <button class="login-btn cancel-btn" @click="handleCancel">
+          <text class="btn-text">取消</text>
+        </button>
+      </view>
+      <!-- #endif -->
+
+      <!-- #ifdef MP-WEIXIN -->
       <!-- 老用户:微信一键登录 -->
-      <button 
+      <button
         v-if="showOneClickLogin"
         class="login-btn primary-btn"
         @click="handleWxLogin"
@@ -31,7 +81,7 @@
       </button>
 
       <!-- 新用户第二步:授权手机号登录(已完成头像昵称授权后显示) -->
-      <button 
+      <button
         v-else-if="showPhoneAuth"
         class="login-btn primary-btn"
         open-type="getPhoneNumber"
@@ -42,7 +92,7 @@
       </button>
 
       <!-- 新用户第一步:获取头像昵称 -->
-      <button 
+      <button
         v-else
         class="login-btn primary-btn"
         @click="handleGetUserProfile"
@@ -50,8 +100,25 @@
         <text class="btn-icon">👤</text>
         <text class="btn-text">授权登录</text>
       </button>
+      <!-- #endif -->
 
       <!-- 用户协议 -->
+      <!-- #ifdef H5 -->
+      <view v-if="currentStep === 'initial'" class="agreement">
+        <checkbox-group @change="handleAgreementChange">
+          <label class="agreement-label">
+            <checkbox :checked="agreedToTerms" color="#5d55e8" />
+            <text class="agreement-text">
+              我已阅读并同意
+              <text class="link" @click.stop="showAgreement('user')">《用户协议》</text>
+              和
+              <text class="link" @click.stop="showAgreement('privacy')">《隐私政策》</text>
+            </text>
+          </label>
+        </checkbox-group>
+      </view>
+      <!-- #endif -->
+      <!-- #ifdef MP-WEIXIN -->
       <view class="agreement">
         <checkbox-group @change="handleAgreementChange">
           <label class="agreement-label">
@@ -65,44 +132,110 @@
           </label>
         </checkbox-group>
       </view>
+      <!-- #endif -->
     </view>
 
+    <!-- #ifdef MP-WEIXIN -->
     <!-- 用户信息完善弹窗 -->
-    <user-info-popup 
+    <user-info-popup
       ref="userInfoPopup"
       @confirm="handleUserInfoConfirm"
     />
+    <!-- #endif -->
   </view>
 </template>
 
 <script>
+// #ifdef H5
+import { startH5Auth, handleH5AuthCallback, setToken } from '@/utils/auth.js'
+import { checkUserStatus, h5PhoneLogin } from '@/utils/api.js'
+// #endif
+
+// #ifdef MP-WEIXIN
 import { wxSilentLogin, wxPhoneLogin, wxCompleteUserInfo } from '@/utils/auth.js'
 import { uploadFile, updateUserProfile, getImageUrl } from '@/utils/api.js'
 import UserInfoPopup from '@/components/UserInfoPopup.vue'
+// #endif
 
 export default {
+  // #ifdef MP-WEIXIN
   components: {
     UserInfoPopup
   },
-  
+  // #endif
+
   data() {
     return {
+      // #ifdef H5
+      currentStep: 'initial', // 'initial' | 'input'
+      isLogin: true, // true=登录, false=注册
+      phoneNumber: '',
+      password: '',
+      tempWxData: null, // 临时存储微信授权数据
+      // #endif
+      // #ifdef MP-WEIXIN
       showOneClickLogin: true,  // 是否显示一键登录按钮
       showPhoneAuth: false,     // 是否显示手机号授权按钮(新用户第二步)
-      agreedToTerms: false,     // 是否同意协议
       loginCode: '',            // 微信登录code
       tempUserData: null,       // 临时存储的用户数据(openid, unionid等)
-      tempUserProfile: null     // 临时存储的用户头像昵称信息
+      tempUserProfile: null,    // 临时存储的用户头像昵称信息
+      // #endif
+      agreedToTerms: false      // 是否同意协议
+    }
+  },
+
+  onLoad(options) {
+    // #ifdef H5
+    // 恢复登录/注册状态
+    const savedIsLogin = uni.getStorageSync('temp_isLogin')
+    if (savedIsLogin !== null && savedIsLogin !== undefined && savedIsLogin !== '') {
+      this.isLogin = savedIsLogin === 'true'
+      uni.removeStorageSync('temp_isLogin')
+    }
+
+    // 检查URL中是否有微信授权回调的code参数
+    let code = options && options.code
+
+    // H5首屏加载时 getCurrentPages/options 可能不稳定,做兜底解析
+    if (!code && typeof window !== 'undefined') {
+      try {
+        const urlParams = new URLSearchParams(window.location.search)
+        code = urlParams.get('code') || code
+      } catch (e) {
+        // ignore
+      }
+
+      if (!code && window.location.hash) {
+        const hash = window.location.hash
+        const queryIndex = hash.indexOf('?')
+        if (queryIndex >= 0) {
+          const query = hash.substring(queryIndex + 1)
+          try {
+            const hashParams = new URLSearchParams(query)
+            code = hashParams.get('code') || code
+          } catch (e) {
+            // ignore
+          }
+        }
+      }
+    }
+
+    if (code) {
+      console.log('[登录] 检测到授权回调code:', code)
+      this.handleAuthCallback(code)
     }
+    // #endif
   },
 
   methods: {
+    // #ifdef MP-WEIXIN
     /**
      * 获取图片URL
      */
     getImageUrl(url) {
       return getImageUrl(url)
     },
+    // #endif
 
     /**
      * 返回上一页
@@ -110,21 +243,198 @@ export default {
     handleBack() {
       const pages = getCurrentPages()
       if (pages.length > 1) {
-        // 有上一页,返回
         uni.navigateBack()
       } else {
-        // 没有上一页,跳转到首页
         uni.switchTab({
           url: '/pages/index/index'
         })
       }
     },
 
-    /**
-     * 第一步:微信一键登录(老用户静默登录)
-     */
+    // #ifdef H5
+    async handleLoginClick() {
+      if (!this.agreedToTerms) {
+        uni.showToast({
+          title: '请先阅读并同意用户协议',
+          icon: 'none',
+          duration: 2000
+        })
+        return
+      }
+
+      this.isLogin = true
+      uni.setStorageSync('temp_isLogin', 'true')
+
+      try {
+        uni.showLoading({ title: '授权中...' })
+        await startH5Auth()
+        uni.hideLoading()
+      } catch (error) {
+        uni.hideLoading()
+        console.error('[登录] 微信授权失败:', error)
+        uni.showToast({
+          title: error.message || '授权失败,请重试',
+          icon: 'none',
+          duration: 2000
+        })
+      }
+    },
+
+    async handleRegisterClick() {
+      if (!this.agreedToTerms) {
+        uni.showToast({
+          title: '请先阅读并同意用户协议',
+          icon: 'none',
+          duration: 2000
+        })
+        return
+      }
+
+      this.isLogin = false
+      uni.setStorageSync('temp_isLogin', 'false')
+
+      try {
+        uni.showLoading({ title: '授权中...' })
+        await startH5Auth()
+        uni.hideLoading()
+      } catch (error) {
+        uni.hideLoading()
+        console.error('[注册] 微信授权失败:', error)
+        uni.showToast({
+          title: error.message || '授权失败,请重试',
+          icon: 'none',
+          duration: 2000
+        })
+      }
+    },
+
+    async handleAuthCallback(code) {
+      try {
+        uni.showLoading({ title: '处理中...' })
+
+        const result = await handleH5AuthCallback(code)
+        const statusResult = await checkUserStatus(result.openid)
+        const isRegistered = statusResult.data
+
+        uni.hideLoading()
+
+        this.tempWxData = {
+          openid: result.openid,
+          unionid: result.unionid,
+          nickname: result.nickname,
+          avatarUrl: result.avatarUrl
+        }
+
+        if (this.isLogin) {
+          if (isRegistered) {
+            this.currentStep = 'input'
+          } else {
+            uni.showToast({
+              title: '该微信未注册,请先注册',
+              icon: 'none',
+              duration: 2000
+            })
+            this.currentStep = 'initial'
+          }
+        } else {
+          if (!isRegistered) {
+            this.currentStep = 'input'
+          } else {
+            uni.showToast({
+              title: '该微信已注册,请直接登录',
+              icon: 'none',
+              duration: 2000
+            })
+            this.currentStep = 'initial'
+          }
+        }
+      } catch (error) {
+        uni.hideLoading()
+        console.error('[授权] 处理失败:', error)
+        uni.showToast({
+          title: error.message || '处理失败,请重试',
+          icon: 'none',
+          duration: 2000
+        })
+        this.currentStep = 'initial'
+      }
+    },
+
+    async handleSubmit() {
+      if (!this.phoneNumber) {
+        uni.showToast({
+          title: '请输入手机号',
+          icon: 'none',
+          duration: 2000
+        })
+        return
+      }
+
+      if (!/^1[3-9]\d{9}$/.test(this.phoneNumber)) {
+        uni.showToast({
+          title: '手机号格式不正确',
+          icon: 'none',
+          duration: 2000
+        })
+        return
+      }
+
+      if (!this.password) {
+        uni.showToast({
+          title: '请输入密码',
+          icon: 'none',
+          duration: 2000
+        })
+        return
+      }
+
+      try {
+        uni.showLoading({ title: this.isLogin ? '登录中...' : '注册中...' })
+
+        const params = {
+          openid: this.tempWxData.openid,
+          unionid: this.tempWxData.unionid,
+          phone: this.phoneNumber,
+          password: this.password
+        }
+
+        if (!this.isLogin) {
+          params.nickname = this.tempWxData.nickname
+          params.avatarUrl = this.tempWxData.avatarUrl
+        }
+
+        const result = await h5PhoneLogin(params)
+
+        uni.hideLoading()
+
+        if (result.code === 200 && result.data.token) {
+          setToken(result.data.token)
+          console.log('[登录] 成功')
+          this.handleLoginSuccess()
+        } else {
+          throw new Error(result.message || (this.isLogin ? '登录失败' : '注册失败'))
+        }
+      } catch (error) {
+        uni.hideLoading()
+        console.error('[提交] 失败:', error)
+        uni.showToast({
+          title: error.message || (this.isLogin ? '登录失败,请重试' : '注册失败,请重试'),
+          icon: 'none',
+          duration: 2000
+        })
+      }
+    },
+
+    handleCancel() {
+      this.currentStep = 'initial'
+      this.phoneNumber = ''
+      this.password = ''
+      this.tempWxData = null
+    },
+    // #endif
+
+    // #ifdef MP-WEIXIN
     async handleWxLogin() {
-      // 检查是否同意协议
       if (!this.agreedToTerms) {
         uni.showToast({
           title: '请先阅读并同意用户协议',
@@ -136,36 +446,30 @@ export default {
 
       try {
         uni.showLoading({ title: '登录中...' })
-        
-        // 调用 wx.login 获取 code
+
         const loginRes = await this.wxLoginAsync()
         this.loginCode = loginRes.code
-        
+
         console.log('[登录] 获取到微信code:', this.loginCode)
-        
-        // 调用后端接口检查是否为老用户
+
         const result = await wxSilentLogin(this.loginCode)
-        
+
         uni.hideLoading()
-        
+
         if (result && result.isSign === 'true') {
-          // 老用户,直接登录成功
           console.log('[登录] 老用户登录成功')
           this.handleLoginSuccess()
         } else if (result && result.isSign === 'false') {
-          // 新用户,需要先获取头像昵称
           console.log('[登录] 新用户,需要先获取头像昵称')
           this.showOneClickLogin = false
           this.showPhoneAuth = false
         } else if (result && result.code === 103) {
-          // 账号被禁用
           uni.showModal({
             title: '账号异常',
             content: '您的账号已被禁用,如有疑问请联系客服',
             showCancel: false
           })
         } else {
-          // 后端返回异常
           throw new Error('登录接口返回数据异常')
         }
       } catch (error) {
@@ -179,11 +483,7 @@ export default {
       }
     },
 
-    /**
-     * 第二步:获取用户头像昵称(新用户)
-     */
     handleGetUserProfile() {
-      // 检查是否同意协议
       if (!this.agreedToTerms) {
         uni.showToast({
           title: '请先阅读并同意用户协议',
@@ -192,27 +492,22 @@ export default {
         })
         return
       }
-      
+
       console.log('[登录] 打开用户信息弹窗')
       this.$refs.userInfoPopup.open()
     },
 
-    /**
-     * 用户信息弹窗确认回调(新用户第二步完成后)
-     */
     handleUserInfoConfirm(userInfo) {
       console.log('[登录] 用户信息已获取:', userInfo)
-      
-      // 保存用户头像昵称信息
+
       this.tempUserProfile = {
         nickname: userInfo.nickname,
         avatarUrl: userInfo.avatarUrl,
         tempAvatarPath: userInfo.tempAvatarPath
       }
-      
-      // 显示手机号授权按钮
+
       this.showPhoneAuth = true
-      
+
       uni.showToast({
         title: '请继续授权手机号',
         icon: 'none',
@@ -220,18 +515,14 @@ export default {
       })
     },
 
-    /**
-     * 第三步:获取手机号授权(新用户)
-     */
     async handleGetPhoneNumber(e) {
       console.log('[登录] 手机号授权回调:', e)
-      
+
       if (e.detail.errMsg !== 'getPhoneNumber:ok') {
-        // 检查是否是权限问题
         if (e.detail.errMsg.includes('no permission')) {
           uni.showModal({
             title: '权限不足',
-            content: '获取手机号功能需要:\n1. 小程序企业认证"权限 \n2. 在真机上测试',
+            content: '获取手机号功能需要:\n1. 小程序企业认证权限 \n2. 在真机上测试',
             showCancel: false
           })
         } else {
@@ -245,55 +536,45 @@ export default {
 
       try {
         uni.showLoading({ title: '验证中...' })
-        
-        // 重新获取 code(微信要求)
+
         const loginRes = await this.wxLoginAsync()
         const newLoginCode = loginRes.code
-        
-        // 构建请求参数
+
         const params = {
           loginCode: newLoginCode,
           phoneCode: e.detail.code,
           encryptedData: e.detail.encryptedData,
           iv: e.detail.iv
         }
-        
+
         console.log('[登录] 发送手机号验证请求')
-        
-        // 调用后端接口验证手机号
+
         const result = await wxPhoneLogin(params)
-        
+
         uni.hideLoading()
-        
+
         if (result && result.isSign === 'false') {
-          // 需要完成注册(已有头像昵称,现在有手机号)
           console.log('[登录] 需要完成注册')
-          // 保存手机号相关信息
           this.tempUserData = {
             openid: result.openid,
             unionid: result.unionid,
             phoneNumber: result.phoneNumber
           }
-          // 直接提交注册
           await this.submitRegistration()
         } else if (result && result.isSign === 'true') {
-          // 已注册,直接登录成功
           console.log('[登录] 已注册用户,登录成功')
           this.handleLoginSuccess()
         } else {
-          // 后端返回异常
           throw new Error('验证接口返回数据异常')
         }
       } catch (error) {
         uni.hideLoading()
         console.error('[登录] 手机号验证失败:', error)
-        
-        // 根据错误信息提供友好提示
+
         let errorMsg = '验证失败,请重试'
-        
+
         if (error.message) {
           if (error.message.includes('48001') || error.message.includes('未开通手机号快速验证组件')) {
-            // 未开通权限
             uni.showModal({
               title: '权限未开通',
               content: '小程序未开通"手机号快速验证组件"权限\n\n请前往微信公众平台:\n开发 → 开发管理 → 接口设置\n开通"手机号快速验证组件"',
@@ -310,7 +591,7 @@ export default {
             errorMsg = error.message
           }
         }
-        
+
         uni.showToast({
           title: errorMsg,
           icon: 'none',
@@ -319,14 +600,10 @@ export default {
       }
     },
 
-    /**
-     * 第四步:提交注册(新用户完成所有授权后)
-     */
     async submitRegistration() {
       try {
         uni.showLoading({ title: '注册中...' })
-        
-        // 构建用户信息
+
         const completeInfo = {
           openid: this.tempUserData.openid,
           unionid: this.tempUserData.unionid,
@@ -334,24 +611,20 @@ export default {
           nickname: this.tempUserProfile.nickname,
           avatarUrl: this.tempUserProfile.avatarUrl
         }
-        
+
         console.log('[登录] 提交用户信息')
-        
-        // 调用后端接口完成注册
+
         await wxCompleteUserInfo(completeInfo)
-        
-        // 注册成功后,上传头像到服务器
+
         if (this.tempUserProfile.tempAvatarPath) {
           console.log('[登录] 开始上传头像到服务器')
           try {
             const uploadedUrl = await this.uploadAvatarWithToken(this.tempUserProfile.tempAvatarPath)
             console.log('[登录] 头像上传成功:', uploadedUrl)
             if (uploadedUrl) {
-              // 更新用户头像为服务器URL
               const updateResult = await updateUserProfile({ avatar: uploadedUrl })
               console.log('[登录] updateUserProfile返回:', updateResult)
 
-              // 更新本地存储的用户信息(使用后端返回的最新数据)
               if (updateResult && updateResult.code === 200 && updateResult.data) {
                 uni.setStorageSync('user_info', JSON.stringify(updateResult.data))
                 console.log('[登录] 本地用户信息已更新')
@@ -361,7 +634,7 @@ export default {
             console.warn('[登录] 头像上传失败:', uploadErr)
           }
         }
-        
+
         uni.hideLoading()
         console.log('[登录] 注册成功')
         this.handleLoginSuccess()
@@ -376,9 +649,6 @@ export default {
       }
     },
 
-    /**
-     * 带token上传头像
-     */
     uploadAvatarWithToken(filePath) {
       return new Promise((resolve, reject) => {
         uni.uploadFile({
@@ -403,19 +673,29 @@ export default {
       })
     },
 
-    /**
-     * 登录成功后的处理
-     */
+    wxLoginAsync() {
+      return new Promise((resolve, reject) => {
+        uni.login({
+          provider: 'weixin',
+          success: (res) => {
+            resolve(res)
+          },
+          fail: (err) => {
+            reject(err)
+          }
+        })
+      })
+    },
+    // #endif
+
     handleLoginSuccess() {
       uni.showToast({
         title: '登录成功',
         icon: 'success',
         duration: 1500
       })
-      
-      // 延迟跳转,让用户看到成功提示
+
       setTimeout(() => {
-        // 跳转到首页或返回上一页
         const pages = getCurrentPages()
         if (pages.length > 1) {
           uni.navigateBack()
@@ -427,44 +707,89 @@ export default {
       }, 1500)
     },
 
-    /**
-     * 协议勾选变化
-     */
     handleAgreementChange(e) {
       this.agreedToTerms = e.detail.value.length > 0
     },
 
-    /**
-     * 显示协议内容
-     */
     showAgreement(type) {
       uni.navigateTo({
         url: `/pages/agreement/agreement?type=${type}`
       })
-    },
-
-    /**
-     * 封装 wx.login 为 Promise
-     */
-    wxLoginAsync() {
-      return new Promise((resolve, reject) => {
-        uni.login({
-          provider: 'weixin',
-          success: (res) => {
-            resolve(res)
-          },
-          fail: (err) => {
-            reject(err)
-          }
-        })
-      })
     }
   }
 }
 </script>
 
 <style scoped>
-/* 返回按钮 */
+/* #ifdef H5 */
+.back-btn {
+  position: fixed;
+  top: env(safe-area-inset-top, 20rpx);
+  left: 40rpx;
+  width: 80rpx;
+  height: 80rpx;
+  background: rgba(255, 255, 255, 0.2);
+  border-radius: 50%;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  z-index: 9999;
+  cursor: pointer;
+  transition: all 0.3s ease;
+  margin-top: 20rpx;
+}
+
+.back-btn:active {
+  background: rgba(255, 255, 255, 0.3);
+  transform: scale(0.95);
+}
+
+.back-icon {
+  font-size: 40rpx;
+  color: #fff;
+  font-weight: bold;
+}
+
+.button-group {
+  width: 100%;
+}
+
+.secondary-btn {
+  background: rgba(255, 255, 255, 0.2);
+  color: #fff;
+  border: 2rpx solid #fff;
+}
+
+.cancel-btn {
+  background: rgba(255, 255, 255, 0.1);
+  color: #fff;
+}
+
+.form-container {
+  width: 100%;
+}
+
+.form-title {
+  font-size: 36rpx;
+  color: #fff;
+  font-weight: bold;
+  margin-bottom: 40rpx;
+  text-align: center;
+}
+
+.form-input {
+  width: 100%;
+  height: 96rpx;
+  border-radius: 48rpx;
+  background: #fff;
+  padding: 0 40rpx;
+  font-size: 32rpx;
+  margin-bottom: 24rpx;
+  box-sizing: border-box;
+}
+/* #endif */
+
+/* #ifdef MP-WEIXIN */
 .back-button {
   position: absolute;
   top: 80rpx;
@@ -490,6 +815,12 @@ export default {
   color: #fff;
 }
 
+.btn-icon {
+  font-size: 40rpx;
+  margin-right: 16rpx;
+}
+/* #endif */
+
 .login-container {
   min-height: 100vh;
   background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
@@ -502,7 +833,6 @@ export default {
   overflow: hidden;
 }
 
-/* 背景装饰 */
 .bg-decoration {
   position: absolute;
   top: 0;
@@ -532,7 +862,6 @@ export default {
   left: -50rpx;
 }
 
-/* 头部 */
 .header {
   display: flex;
   flex-direction: column;
@@ -562,7 +891,6 @@ export default {
   color: rgba(255, 255, 255, 0.8);
 }
 
-/* 登录按钮区域 */
 .login-actions {
   width: 100%;
   z-index: 1;
@@ -577,7 +905,7 @@ export default {
   justify-content: center;
   font-size: 32rpx;
   font-weight: 500;
-  margin-bottom: 40rpx;
+  margin-bottom: 24rpx;
   border: none;
   box-shadow: 0 8rpx 24rpx rgba(0, 0, 0, 0.15);
 }
@@ -587,16 +915,10 @@ export default {
   color: #667eea;
 }
 
-.btn-icon {
-  font-size: 40rpx;
-  margin-right: 16rpx;
-}
-
 .btn-text {
   font-size: 32rpx;
 }
 
-/* 用户协议 */
 .agreement {
   margin-top: 60rpx;
 }

+ 16 - 1
src/pages/mine/mine.vue

@@ -56,7 +56,9 @@
         <view class="bottom-safe-area"></view>
       </view>
     </scroll-view>
+    <!-- #ifdef MP-WEIXIN -->
 		<ComTabBar :url="'/pages/mine/mine'"></ComTabBar>
+    <!-- #endif -->
   </view>
 </template>
 
@@ -65,7 +67,9 @@ import { ref } from 'vue'
 import { onShow } from '@dcloudio/uni-app'
 import { isLoggedIn as checkLogin, getUserInfo as getStoredUserInfo, refreshUserInfo, logout, checkLogin as requireLogin } from '@/utils/auth.js'
 import { getImageUrl } from '@/utils/api.js'
+// #ifdef MP-WEIXIN
 import ComTabBar from '../../components/ComTabBar.vue'
+// #endif
 
 const isLoggedIn = ref(false)
 const isAdmin = ref(false)
@@ -74,21 +78,32 @@ const userInfo = ref({
   avatar: '',
   status: 0
 })
+// #ifdef MP-WEIXIN
 const app = getApp();
+// #endif
 
 /**
  * 页面显示时刷新用户信息
  */
 onShow(() => {
+  // #ifdef MP-WEIXIN
 	uni.hideTabBar({
 		success: () => {},
 		fail: () => {}
 	});
-	
+  // #endif
+
   loadUserInfo()
+  // #ifdef MP-WEIXIN
   uni.setNavigationBarTitle({
   	title: app.globalData.envVersion == 'develop' ? '个人中心' : '量化交易大师'
   })
+  // #endif
+  // #ifdef H5
+  uni.setNavigationBarTitle({
+  	title: '量化交易大师'
+  })
+  // #endif
 })
 
 /**

+ 24 - 2
src/pages/pool/pool.vue

@@ -1,7 +1,12 @@
 <template>
   <view class="page-container">
+    <!-- #ifdef MP-WEIXIN -->
 	  <PoolDev v-if="app.globalData.envVersion == 'develop'"></PoolDev>
     <scroll-view class="scroll-view" scroll-y v-else>
+    <!-- #endif -->
+    <!-- #ifdef H5 -->
+    <scroll-view class="scroll-view" scroll-y>
+    <!-- #endif -->
       <view class="content-wrapper">
         <!-- 超短精选池标题 -->
         <view class="pool-header-section">
@@ -72,7 +77,9 @@
         <view class="bottom-safe-area"></view>
       </view>
     </scroll-view>
+	<!-- #ifdef MP-WEIXIN -->
 	<ComTabBar :url="'/pages/pool/pool'"></ComTabBar>
+	<!-- #endif -->
     <!-- 购买弹窗 -->
     <PurchaseModal
       :visible="showModal"
@@ -95,8 +102,10 @@ import { getStockQuotes, addUserStock, getStockPoolList, createOrder, wxPay, che
 import PurchaseModal from '../../components/PurchaseModal.vue'
 import PerformanceCard from '../../components/PerformanceCard.vue'
 import HistorySearchCard from '../../components/HistorySearchCard.vue'
+// #ifdef MP-WEIXIN
 import PoolDev from '../../components/poolDev.vue'
 import ComTabBar from '../../components/ComTabBar.vue'
+// #endif
 
 const isPurchased = ref(false)
 const showModal = ref(false)
@@ -108,7 +117,9 @@ const isPaying = ref(false) // 是否正在支付中(防止重复点击)
 const stockList = ref([])
 let refreshTimer = null
 let subscriptionTimer = null
+// #ifdef MP-WEIXIN
 const app = getApp();
+// #endif
 
 // 性能统计数据
 const performanceStats = reactive({
@@ -407,19 +418,27 @@ onLoad(() => {
 })
 
 onShow(() => {
-	
+	// #ifdef MP-WEIXIN
 	uni.hideTabBar({
 		success: () => {},
 		fail: () => {}
 	});
-	
+	// #endif
+
   console.log('[超短池] onShow')
   isPageVisible.value = true
   checkPurchaseStatus()
   startSubscriptionRefresh()
+  // #ifdef MP-WEIXIN
   uni.setNavigationBarTitle({
   	title: app.globalData.envVersion == 'develop' ? '商品' : '量化交易大师'
   })
+  // #endif
+  // #ifdef H5
+  uni.setNavigationBarTitle({
+  	title: '量化交易大师'
+  })
+  // #endif
 })
 
 onHide(() => {
@@ -447,6 +466,7 @@ onUnmounted(() => {
 .scroll-view {
   flex: 1;
   height: 0;
+  -webkit-overflow-scrolling: touch;
 }
 
 .content-wrapper {
@@ -660,5 +680,7 @@ onUnmounted(() => {
 
 .bottom-safe-area {
   height: 80rpx;
+  padding-bottom: constant(safe-area-inset-bottom);
+  padding-bottom: env(safe-area-inset-bottom);
 }
 </style>

+ 21 - 2
src/pages/rank/rank.vue

@@ -1,9 +1,14 @@
 <template>
   <view class="page-rank">
+    <!-- #ifdef MP-WEIXIN -->
 	  <view class="develop" v-if="app.globalData.envVersion == 'develop'">
 	  		  暂无选择商品
 	  </view>
     <scroll-view class="scroll-view" scroll-y v-else>
+    <!-- #endif -->
+    <!-- #ifdef H5 -->
+    <scroll-view class="scroll-view" scroll-y>
+    <!-- #endif -->
       <view class="content-wrapper">
         <!-- 上证指数卡片 -->
         <view class="index-card">
@@ -157,7 +162,9 @@
         <view class="bottom-safe-area"></view>
       </view>
     </scroll-view>
+	<!-- #ifdef MP-WEIXIN -->
 	<ComTabBar :url="'/pages/rank/rank'"></ComTabBar>
+	<!-- #endif -->
   </view>
 </template>
 
@@ -166,7 +173,9 @@ import { ref, nextTick, reactive } from 'vue'
 import { onLoad, onShow, onHide, onUnload } from '@dcloudio/uni-app'
 import { isLoggedIn as checkLoginStatus } from '../../utils/auth.js'
 import { getStockQuotes, getIndexQuote, getUserStocks, deleteUserStock } from '../../utils/api.js'
+// #ifdef MP-WEIXIN
 import ComTabBar from '../../components/ComTabBar.vue'
+// #endif
 // 保存组件实例(已移除,不再需要)
 
 const isLoggedIn = ref(false)
@@ -176,7 +185,9 @@ const isLoading = ref(false) // 加载状态
 const lastLoadTime = ref(0) // 上次加载时间
 const CACHE_DURATION = 5000 // 缓存有效期5秒
 const isPageVisible = ref(false) // 页面是否可见
+// #ifdef MP-WEIXIN
 const app = getApp();
+// #endif
 
 // 获取随机刷新间隔 (2000-3000ms)
 const getRandomInterval = () => 2000 + Math.random() * 1000
@@ -907,12 +918,13 @@ onLoad(() => {
 })
 
 onShow(() => {
-	
+	// #ifdef MP-WEIXIN
 	uni.hideTabBar({
 		success: () => {},
 		fail: () => {}
 	});
-	
+	// #endif
+
   console.log('[我的股票] onShow 触发')
   isPageVisible.value = true
 
@@ -949,9 +961,16 @@ onShow(() => {
     // 否则使用缓存策略
     loadMyStocks(false)
   }
+  // #ifdef MP-WEIXIN
   uni.setNavigationBarTitle({
   	title: app.globalData.envVersion == 'develop' ? '我的自选' : '量化交易大师'
   })
+  // #endif
+  // #ifdef H5
+  uni.setNavigationBarTitle({
+  	title: '量化交易大师'
+  })
+  // #endif
 })
 
 onHide(() => {

+ 21 - 1
src/pages/strong/strong.vue

@@ -1,5 +1,6 @@
 <template>
   <view class="page-container">
+    <!-- #ifdef MP-WEIXIN -->
 	  <view class="develop" v-if="app.globalData.envVersion == 'develop'">
 		  强势池 · 文具介绍
 		  ——以笔为刃,以纸为疆,书写你的强势人生
@@ -17,6 +18,10 @@
 		  写下的每一笔,都是宣言。
 	  </view>
     <scroll-view class="scroll-view" scroll-y v-else>
+    <!-- #endif -->
+    <!-- #ifdef H5 -->
+    <scroll-view class="scroll-view" scroll-y>
+    <!-- #endif -->
       <view class="content-wrapper">
         <!-- 强势趋势池标题 -->
         <view class="pool-header-section">
@@ -67,7 +72,9 @@
         <view class="bottom-safe-area"></view>
       </view>
     </scroll-view>
+		<!-- #ifdef MP-WEIXIN -->
 		<ComTabBar :url="'/pages/strong/strong'"></ComTabBar>
+		<!-- #endif -->
   </view>
 </template>
 
@@ -77,12 +84,16 @@ import { onLoad, onShow, onHide } from '@dcloudio/uni-app'
 import { isLoggedIn as checkLoginStatus } from '../../utils/auth.js'
 import { getStockQuotes, addUserStock, getStockPoolList } from '../../utils/api.js'
 import HistorySearchCard from '../../components/HistorySearchCard.vue'
+// #ifdef MP-WEIXIN
 import ComTabBar from '../../components/ComTabBar.vue'
+// #endif
 
 const isPageVisible = ref(false) // 页面是否可见
 const stockList = ref([])
 let refreshTimer = null
+// #ifdef MP-WEIXIN
 const app = getApp();
+// #endif
 
 // 获取涨跌样式
 const getChangeClass = (changePercent) => {
@@ -203,17 +214,26 @@ onLoad(() => {
 })
 
 onShow(() => {
+	// #ifdef MP-WEIXIN
 	uni.hideTabBar({
 		success: () => {},
 		fail: () => {}
 	});
-	
+	// #endif
+
   console.log('[强势池] onShow')
   isPageVisible.value = true
   loadAndStartRefresh()
+  // #ifdef MP-WEIXIN
   uni.setNavigationBarTitle({
   	title: app.globalData.envVersion == 'develop' ? '介绍' : '量化交易大师'
   })
+  // #endif
+  // #ifdef H5
+  uni.setNavigationBarTitle({
+  	title: '量化交易大师'
+  })
+  // #endif
 })
 
 onHide(() => {