wlzboy
9 小时以前 5f2ee03958a1a16dc27195c76ea7cffb422c95d1
app/pages/login.vue
@@ -3,7 +3,7 @@
    <view class="logo-content align-center justify-center flex">
      <image style="width: 100rpx;height: 100rpx;" :src="globalConfig.appInfo.logo" mode="widthFix">
      </image>
      <text class="title">若依移动端登录</text>
      <text class="title">民航调度系统</text>
    </view>
    <view class="login-form-content">
      <view class="input-item flex align-center">
@@ -21,30 +21,50 @@
          <image :src="codeUrl" @click="getCode" class="login-code-img" mode="aspectFit"></image>
        </view>
      </view>
      <view class="agreement-checkbox">
        <checkbox-group @change="onAgreementChange">
          <label class="checkbox-label">
            <checkbox :checked="agreedToPolicy" value="agreed" color="#007AFF" class="round-checkbox" style="margin-top: 0;" />
            <text class="agreement-text">
              <text class="text-grey1">同意</text>
              <text @click.stop="handleUserAgrement" class="text-blue agreement-link">《用户协议》</text>
              <text class="text-grey1">和</text>
              <text @click.stop="handlePrivacy" class="text-blue agreement-link">《隐私政策》</text>
            </text>
          </label>
        </checkbox-group>
      </view>
      <view class="action-btn">
        <button @click="handleLogin" class="login-btn cu-btn block bg-blue lg round">登录</button>
      </view>
      <view class="wechat-login" @click="handleWechatLogin">
        <view class="wechat-btn">
          <image class="wechat-icon" src="/static/icons/profile.png"></image>
          <text class="wechat-text">微信一键登录</text>
        </view>
      </view>
      <view class="reg text-center" v-if="register">
        <text class="text-grey1">没有账号?</text>
        <text @click="handleUserRegister" class="text-blue">立即注册</text>
      </view>
      <view class="xieyi text-center">
        <text class="text-grey1">登录即代表同意</text>
        <text @click="handleUserAgrement" class="text-blue">《用户协议》</text>
        <text @click="handlePrivacy" class="text-blue">《隐私协议》</text>
        <!-- 微信一键登录按钮(仅在微信小程序环境显示) -->
        <!-- #ifdef MP-WEIXIN -->
        <button
          v-if="isWechat && wechatOpenId"
          @click="loginByOpenId"
          class="wechat-login-btn cu-btn block bg-green lg round"
          style="margin-top: 20rpx;">
          <text class="cuIcon-wechat" style="margin-right: 10rpx;"></text>
        手机号码快捷登录
        </button>
        <button
          v-else-if="isWechat"
          open-type="getPhoneNumber"
          @getphonenumber="onGetPhoneNumber"
          class="wechat-login-btn cu-btn block bg-green lg round"
          style="margin-top: 20rpx;">
          <text class="cuIcon-wechat" style="margin-right: 10rpx;"></text>
          手机号码快捷登录
        </button>
        <!-- #endif -->
      </view>
    </view>
  </scroll-view>
</template>
<script>
  import { getCodeImg } from '@/api/login'
  import { getCodeImg, loginByOpenId, loginByWechatPhone } from '@/api/login'
  import { isWxWorkEnvironment,redirectToQyLogin } from '@/utils/wechat'
  export default {
    data() {
@@ -53,17 +73,38 @@
        captchaEnabled: true,
        // 用户注册开关
        register: false,
        // 隐私政策同意状态(默认未选中,需要用户主动勾选)
        agreedToPolicy: false,
        globalConfig: getApp().globalData.config,
        loginForm: {
          username: "admin",
          password: "admin123",
          username: "",
          password: "",
          code: "",
          uuid: ''
        }
        },
        // 微信一键登录相关
        isWechat: false, // 是否为微信小程序环境
        wechatOpenId: '', // 微信OpenID
        wechatUnionId: '', // 微信UnionID
        // 页面参数
        pageOptions: {}
      }
    },
    onLoad(options) {
      // 保存页面参数
      this.pageOptions = options || {}
      isWxWorkEnvironment().then(res=>{
        if(res){
          // console.log("企业微信环境 login.vue....")
          redirectToQyLogin(options,this.$tab);
        }
      });
    },
    created() {
      this.getCode()
      this.checkWechatEnv()
      this.tryAutoLogin()
    },
    methods: {
      // 用户注册
@@ -72,13 +113,15 @@
      },
      // 隐私协议
      handlePrivacy() {
        let site = this.globalConfig.appInfo.agreements[0]
        this.$tab.navigateTo(`/pages/common/webview/index?title=${site.title}&url=${site.url}`)
        this.$tab.navigateTo('/pages/mine/privacy-policy/index')
      },
      // 用户协议
      handleUserAgrement() {
        let site = this.globalConfig.appInfo.agreements[1]
        this.$tab.navigateTo(`/pages/common/webview/index?title=${site.title}&url=${site.url}`)
        this.$tab.navigateTo('/pages/mine/user-agreement/index')
      },
      // 协议同意状态变更
      onAgreementChange(e) {
        this.agreedToPolicy = e.detail.value.length > 0
      },
      // 获取图形验证码
      getCode() {
@@ -92,6 +135,10 @@
      },
      // 登录方法
      async handleLogin() {
        if (!this.agreedToPolicy) {
          this.$modal.msgError("请先阅读并同意用户协议和隐私政策")
          return
        }
        if (this.loginForm.username === "") {
          this.$modal.msgError("请输入您的账号")
        } else if (this.loginForm.password === "") {
@@ -102,40 +149,6 @@
          this.$modal.loading("登录中,请耐心等待...")
          this.pwdLogin()
        }
      },
      // 微信登录方法
      async handleWechatLogin() {
        // #ifdef MP-WEIXIN
        // 微信小程序登录
        uni.login({
          provider: 'weixin',
          success: (loginRes) => {
            console.log('微信登录成功', loginRes);
            // 获取用户信息
            uni.getUserInfo({
              provider: 'weixin',
              success: (infoRes) => {
                console.log('用户信息获取成功', infoRes);
                // 跳转到微信登录确认页面
                this.$tab.navigateTo(`/pages/login/wechat?userInfo=${encodeURIComponent(JSON.stringify(infoRes.userInfo))}`);
              },
              fail: (error) => {
                console.error('获取用户信息失败', error);
                this.$modal.msgError("获取微信用户信息失败");
              }
            });
          },
          fail: (error) => {
            console.error('微信登录失败', error);
            this.$modal.msgError("微信登录失败");
          }
        });
        // #endif
        // #ifndef MP-WEIXIN
        // H5或其他平台提示
        this.$modal.msgError("请在微信客户端中使用微信登录功能");
        // #endif
      },
      // 密码登录
      async pwdLogin() {
@@ -152,8 +165,202 @@
      loginSuccess(result) {
        // 设置用户信息
        this.$store.dispatch('GetInfo').then(res => {
          this.$tab.reLaunch('/pages/index')
          // 触发登录成功事件,启动消息轮询
          uni.$emit('user-login')
          // 处理自动跳转逻辑
          this.redirectAfterLogin()
        })
      },
      // ==================== 微信一键登录相关方法 ====================
      // 检查是否在微信小程序环境
      checkWechatEnv() {
        // #ifdef MP-WEIXIN
        this.isWechat = true
        console.log('当前环境:微信小程序')
        // #endif
        // #ifndef MP-WEIXIN
        this.isWechat = false
        console.log('当前环境:非微信小程序')
        // #endif
      },
      // 尝试自动登录(检查本地是否有保存的OpenID)
      tryAutoLogin() {
        if (!this.isWechat) {
          return
        }
        // 从本地存储中获取OpenID和UnionID
        const savedOpenId = uni.getStorageSync('wechat_openid')
        const savedUnionId = uni.getStorageSync('wechat_unionid')
        const autoLogin=true;
        if (savedOpenId && autoLogin) {
          console.log('检测到已保存的OpenID,尝试自动登录')
          this.wechatOpenId = savedOpenId
          this.wechatUnionId = savedUnionId // 可能为null
          this.loginByOpenId()
        }
      },
      // 处理获取手机号的回调
      onGetPhoneNumber(e) {
        console.log('获取手机号回调:', e)
        if (!this.agreedToPolicy) {
          this.$modal.msgError("请先阅读并同意用户协议和隐私政策")
          return
        }
        if (e.detail.errMsg === 'getPhoneNumber:ok') {
          // 用户同意授权
          const { code } = e.detail
          this.$modal.loading("正在获取手机号...")
          // 先获取微信登录code
          uni.login({
            provider: 'weixin',
            success: (loginRes) => {
              console.log('微信登录code:', loginRes.code)
              console.log('手机号code:', code)
              // 调用后端接口,传入loginCode和phoneCode
              loginByWechatPhone(loginRes.code, code).then(response => {
                this.$modal.closeLoading()
                if (response.code === 200) {
                  // 登录成功,保存OpenID和UnionID
                  const openId = response.openId
                  const unionId = response.unionId
                  uni.setStorageSync('wechat_openid', openId)
                  this.wechatOpenId = openId
                  if (unionId) {
                    uni.setStorageSync('wechat_unionid', unionId)
                    this.wechatUnionId = unionId
                  }
                  // 保存token到本地存储和Vuex
                  const token = response.token
                  this.$store.commit('SET_TOKEN', token)
                  // 必须调用setToken保存到本地存储
                  const { setToken } = require('@/utils/auth')
                  setToken(token)
                  // 获取用户信息并处理自动跳转逻辑
                  this.$store.dispatch('GetInfo').then(() => {
                    this.redirectAfterLogin()
                  });
                } else {
                  this.$modal.msgError(response.msg || '登录失败')
                }
              }).catch(error => {
                this.$modal.closeLoading()
                console.error('登录失败:', error)
                this.$modal.msgError('登录失败,请重试')
              })
            },
            fail: (err) => {
              this.$modal.closeLoading()
              console.error('获取微信登录code失败:', err)
              this.$modal.msgError('获取微信信息失败')
            }
          })
        } else {
          // 用户拒绝授权
          this.$modal.msgError('需要您的手机号才能登录')
        }
      },
      // 通过OpenID和UnionID登录
      loginByOpenId() {
        this.$modal.loading("正在登录...")
        // 同时传入openId和unionId进行双重验证
        loginByOpenId(this.wechatOpenId, this.wechatUnionId).then(response => {
          this.$modal.closeLoading()
          if (response.code === 200) {
            // 登录成功,保存token到本地存储和Vuex
            const token = response.token
            this.$store.commit('SET_TOKEN', token)
            // 必须调用setToken保存到本地存储
            const { setToken } = require('@/utils/auth')
            setToken(token)
            // 获取用户信息并处理自动跳转逻辑
            this.$store.dispatch('GetInfo').then(() => {
              this.redirectAfterLogin()
            });
          } else {
            // OpenID未绑定或验证失败,需要获取手机号绑定
            console.log('该OpenID尚未绑定或验证失败,需要获取手机号')
            this.$modal.closeLoading()
            this.$modal.msgError(response.msg || '该微信账号尚未绑定,请点击微信一键登录按钮获取手机号授权')
          }
        }).catch(error => {
          this.$modal.closeLoading()
          console.error('登录失败:', error)
          this.$modal.msgError('登录失败,请重试')
        })
      },
      /**
       * 获取URL参数
       */
      getUrlParam(name) {
        // #ifdef H5
        const reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)");
        const r = window.location.search.substr(1).match(reg);
        if (r != null) return decodeURIComponent(r[2]);
        // #endif
        // #ifdef MP-WEIXIN
        // 在小程序中,参数通过onLoad的options传递
        if (this.pageOptions && this.pageOptions[name]) {
          return decodeURIComponent(this.pageOptions[name]);
        }
        // #endif
        return null;
      },
      /**
       * 登录成功后的跳转处理
       */
      redirectAfterLogin() {
        try {
          // 检查是否有redirect参数指定跳转页面
          let redirectUrl = this.getUrlParam("redirect");
          // 如果没有redirect参数,检查是否有保存的目标页面
          if (!redirectUrl) {
            const { getTargetUrl } = require('@/utils/auth')
            redirectUrl = getTargetUrl()
          }
          if (redirectUrl) {
            // 解码redirect参数
            redirectUrl = decodeURIComponent(redirectUrl);
            console.log("自动跳转到指定页面:", redirectUrl);
            this.$tab.reLaunch(redirectUrl);
          } else {
            // 默认跳转到首页
            console.log("跳转到首页");
            this.$tab.reLaunch('/pages/index');
          }
        } catch (e) {
          console.error("跳转失败,使用默认跳转:", e);
          // 出现异常时,默认跳转到首页
          this.$tab.reLaunch('/pages/index');
        }
      }
    }
  }
@@ -271,33 +478,17 @@
          height: 90rpx;
          font-size: 32rpx;
        }
      }
      .wechat-login {
        margin: 20rpx 0;
        .wechat-btn {
        .wechat-login-btn {
          height: 90rpx;
          font-size: 32rpx;
          display: flex;
          align-items: center;
          justify-content: center;
          background-color: #07c160;
          height: 90rpx;
          border-radius: 20px;
          .wechat-icon {
            width: 40rpx;
            height: 40rpx;
            margin-right: 10rpx;
          }
          .wechat-text {
            color: white;
            font-size: 32rpx;
          }
        }
      }
      .reg, .xieyi {
      .reg {
        margin: 20rpx 0;
        .text-grey1 {
@@ -309,6 +500,99 @@
          color: #007AFF;
        }
      }
      .agreement-checkbox {
        margin: 50rpx 0 30rpx 0;
        padding: 20rpx;
        display: flex;
        justify-content: flex-start;
        align-items: left;
        checkbox-group {
          display: flex;
          align-items: center;
        }
        .checkbox-label {
          display: flex;
          align-items: center;
          justify-content: flex-start;
          checkbox {
            margin-right: 15rpx;
            transform: scale(1.2);
            flex-shrink: 0;
            vertical-align: middle;
          }
          // 圆形复选框样式
          .round-checkbox {
            border-radius: 50% !important;
          }
          // 针对微信小程序的圆形样式
          ::v-deep .uni-checkbox-input,
          ::v-deep .wx-checkbox-input {
            border-radius: 50% !important;
          }
          // 针对H5的圆形样式
          ::v-deep input[type="checkbox"] {
            border-radius: 50% !important;
            -webkit-appearance: none;
            appearance: none;
            width: 36rpx;
            height: 36rpx;
            border: 2rpx solid #d1d1d1;
            background-color: #fff;
            position: relative;
            &:checked {
              background-color: #007AFF;
              border-color: #007AFF;
              &::after {
                content: '';
                position: absolute;
                top: 50%;
                left: 50%;
                transform: translate(-50%, -50%);
                width: 12rpx;
                height: 12rpx;
                background-color: #fff;
                border-radius: 50%;
              }
            }
          }
          .agreement-text {
            display: inline-flex;
            align-items: center;
            flex-wrap: wrap;
            line-height: 1.5;
            font-size: 26rpx;
            text-align: left;
            justify-content: flex-start;
            .text-grey1 {
              color: #666;
              font-size: 26rpx;
              line-height: 1.5;
            }
            .text-blue {
              color: #007AFF;
              font-size: 26rpx;
              padding: 8rpx 10rpx;
              margin: 0 5rpx;
              display: inline-block;
              position: relative;
              z-index: 10;
              line-height: 1.5;
            }
          }
        }
      }
    }
  }
</style>