第三方登录功能的实现之 QQ登录 - 未绑定

目录

QQ登录 - 未绑定 - 有账号

获取QQ头像和昵称

表单校验

发送验证码

QQ绑定完成

QQ登录 - 未绑定 - 无账号


QQ登录 - 未绑定 - 有账号

如果账号是绑定的状态,手动调用一下解绑接口

http://pcapi-xiaotuxian-front.itheima.net/login/social/unbind?mobile=手机号

https://apipc-xiaotuxian-front.itheima.net/login/social/unbind?mobile=手机号

获取QQ头像和昵称

如果QQ没绑定过账号,需要绑定已存在的账号或者注册新的账号

(1)获取QQ信息

<script name="CallbackBind" lang="ts" setup>
import { QQUserInfo, QQUserInfoRes } from '@/types/user'
import { ref } from 'vue'
const qqInfo = ref<QQUserInfo>({} as QQUserInfo)
// 1. 判断QQ是否登录
if (QC.Login.check()) {
  // 2. 获取QQ用户信息
  QC.api('get_user_info').success((res: QQUserInfoRes) => {
    console.log(res)
    qqInfo.value = res.data
  })
}
</script>

(2)提供数据类型

// QQ信息-用户详情
export interface QQUserInfo {
  ret: number
  msg: string
  is_lost: number
  nickname: string
  gender: string
  gender_type: number
  province: string
  city: string
  year: string
  constellation: string
  figureurl: string
  figureurl_1: string
  figureurl_2: string
  figureurl_qq_1: string
  figureurl_qq_2: string
  figureurl_qq: string
  figureurl_type: string
  is_yellow_vip: string
  vip: string
  yellow_vip_level: string
  level: string
  is_yellow_year_vip: string
}
// QQ返回信息
export interface QQUserInfoRes {
  status: string
  fmt: string
  ret: number
  code: number
  data: QQUserInfo
  seq: string
  dataText: string
}

 (3)渲染QQ信息 

<div class="user-info">
  <img :src="qqInfo.figureurl_2" alt="" />
  <p>
    Hi,{{ qqInfo.nickname }}
    欢迎来小兔鲜,完成绑定后可以QQ账号一键登录哦~
  </p>
</div>

表单校验

(1)提取校验规则 src/utils/validate.ts

export function accountRule(value: string) {
  // value是将来使用该规则的表单元素的值
  // 1. 必填
  // 2. 6-20个字符,需要以字母开头
  // 如何反馈校验成功还是失败,返回true才是成功,其他情况失败,返回失败原因。
  if (!value) return '请输入用户名'
  if (!/^[a-zA-Z]\w{5,19}$/.test(value)) return '字母开头且6-20个字符'
  return true
}
export function passwordRule(value: string) {
  if (!value) return '请输入密码'
  if (!/^\w{6,24}$/.test(value)) return '密码是6-24个字符'
  return true
}
export function mobileRule(value: string) {
  if (!value) return '请输入手机号'
  if (!/^1[3-9]\d{9}$/.test(value)) return '手机号格式错误'
  return true
}
export function codeRule(value: string) {
  if (!value) return '请输入验证码'
  if (!/^\d{6}$/.test(value)) return '验证码是6个数字'
  return true
}
export function isAgreeRule(value: string) {
  if (!value) return '请勾选同意用户协议'
  return true
}

(2)修改登录功能

import {
  accountRule,
  mobileRule,
  codeRule,
  passwordRule,
  isAgreeRule
} from '@/utils/validate'

useForm({
  validationSchema: {
    account: accountRule,
    mobile: mobileRule,
    code: codeRule,
    password: passwordRule,
    isAgree: isAgreeRule
  },
  initialValues: {
    mobile: '13666666666',
    code: '123456',
    account: 'xiaotuxian001',
    password: '123456',
    isAgree: true
  }
})

(3)在 CallbackBind.vue添加表单校验

// 表单校验
const { validate } = useForm({
  validationSchema: {
    mobile: mobileRule,
    code: codeRule
  }
})
const { value: mobile, errorMessage: mobileError } = useField('mobile')
const { value: code, errorMessage: codeError } = useField('code')


<div class="xtx-form-item">
  <div class="field">
    <i class="icon iconfont icon-phone"></i>
    <input
      class="input"
      v-model="mobile"
      type="text"
      placeholder="绑定的手机号"
    />
  </div>
  <div class="error">{{ mobileError }}</div>
</div>
<div class="xtx-form-item">
  <div class="field">
    <i class="icon iconfont icon-code"></i>
    <input
      v-model="code"
      class="input"
      type="text"
      placeholder="短信验证码"
    />
    <span class="code">发送验证码</span>
  </div>
  <div class="error">{{ codeError }}</div>
</div>

(4)绑定时完成表单校验

<a href="javascript:;" class="submit" @click="bind">立即绑定</a>


const bind = async () => {
  const res = await validate()
  if (!res.valid) return
    // 如果校验,发送请求进行绑定
}

发送验证码

 (1)提供actions,用于获取短信绑定QQ

// 绑定qq的短信验证码
async sendQQBindMsg(mobile: string) {
  await request.get('/login/social/code', {
    params: {
      mobile
    }
  })
}

(2)点击获取短信验证码时,发送请求获取验证码 

const { time, start } = useCountDown()
const send = async () => {
  if (time.value > 0) return
  const res = await validateMobile()
  console.log(res)
  if (!res.valid) return
  // 发送请求绑定qq
  await user.sendQQBindMsg(mobile.value)
  // 开启倒计时
  start(10)
}

(3)渲染倒计时

<span class="code" @click="send">
  {{ time === 0 ? '发送验证码' : `${time}s后发送` }}
</span>

QQ绑定完成

(1)提供绑定的actions 

async qqBindLogin(openId: string, mobile: string, code: string) {
  const res = await request.post<ApiRes<Profile>>('/login/social/bind', {
    mobile,
    code,
    unionId: openId
  })
  // 1. 保存用户信息到 state 中
  this.profile = res.data.result
  setProfile(res.data.result)
},

(2)验证后,发送请求绑定

// 1. 判断QQ是否登录
if (QC.Login.check()) {
  // 2. 获取QQ用户信息
  QC.api('get_user_info').success((res: QQUserInfoRes) => {
    console.log(res)
    qqInfo.value = res.data
  })
  // 3. 获取openId
  QC.Login.getMe((id) => {
    openId = id
  })
}

const bind = async () => {
  const res = await validForm()
  if (!res.valid) return
  // 如果校验,发送请求进行绑定
  await user.qqBindLogin(openId, mobile.value, code.value)
  Message.success('QQ绑定成功')
  router.push('/')
}

QQ登录 - 未绑定 - 无账号

(1)增加校验类型

export function rePasswordRule(value: string, { form }: any) {
  if (!value) return '请输入确认密码'
  if (!/^\w{6,24}$/.test(value)) return '密码是6-24个字符'
  // 校验密码是否一致  form表单数据对象
  if (value !== form.password) return '两次输入的密码不一致'
  return true
}

(2)提供两个actions

async sendQQPathMsg(mobile: string) {
  await request.get('/register/code', {
    params: {
      mobile
    }
  })
},

async qqPatchLogin(data: any) {
  const res = await request.post<ApiRes<Profile>>(
    `/login/social/${data.openId}/complement`,
    data
  )
  // 1. 保存用户信息到 state 中
  this.profile = res.data.result
  setProfile(res.data.result)
}

(3)完整代码

<script lang="ts" setup name="CallbackPatch">
import {
  useField,
  useForm,
  useValidateForm,
  useValidateField
} from 'vee-validate'
import {
  accountRule,
  mobileRule,
  codeRule,
  passwordRule,
  rePasswordRule
} from '@/utils/validate'
import { useCountDown } from '@/utils/hooks'
import Message from '@/components/message'
import useStore from '@/store'
import { useRouter } from 'vue-router'
const { user } = useStore()
const router = useRouter()
// 1. 获取openId
let openId = ''
// 判断QQ是否登录
if (QC.Login.check()) {
  // 获取openId
  QC.Login.getMe((id) => {
    openId = id
  })
}

// 表单校验

useForm({
  validationSchema: {
    account: accountRule,
    mobile: mobileRule,
    code: codeRule,
    password: passwordRule,
    repassword: rePasswordRule
  }
})

const { errorMessage: accountError, value: account } =
  useField<string>('account')

const { errorMessage: passwordError, value: password } =
  useField<string>('password')
const { errorMessage: mobileError, value: mobile } = useField<string>('mobile')
const { errorMessage: codeError, value: code } = useField<string>('code')
const { errorMessage: repasswordError, value: repassword } =
  useField<string>('repassword')

const validForm = useValidateForm()
const bind = async () => {
  const res = await validForm()
  if (!res.valid) return
  await user.qqPatchLogin({
    openId,
    mobile: mobile.value,
    code: code.value,
    account: account.value,
    password: password.value
  })
  Message.success('注册成功')
  router.push('/')
}

// 获取验证码
const validMobile = useValidateField('mobile')
const { time, start } = useCountDown(60)
const send = async () => {
  if (time.value > 0) return
  // console.log('获取验证码')
  // 单独校验手机号
  const res = await validMobile()
  if (!res.valid) {
    return
  }
  await user.sendQQPathMsg(mobile.value)
  Message.success('获取验证码成功')

  start()
}
</script>
<template>
  <div class="xtx-form">
    <div class="xtx-form-item">
      <div class="field">
        <i class="icon iconfont icon-user"></i>
        <input
          class="input"
          v-model="account"
          type="text"
          placeholder="请输入用户名"
        />
      </div>
      <div class="error">{{ accountError }}</div>
    </div>
    <div class="xtx-form-item">
      <div class="field">
        <i class="icon iconfont icon-phone"></i>
        <input
          class="input"
          v-model="mobile"
          type="text"
          placeholder="请输入手机号"
        />
      </div>
      <div class="error">{{ mobileError }}</div>
    </div>
    <div class="xtx-form-item">
      <div class="field">
        <i class="icon iconfont icon-code"></i>
        <input
          class="input"
          v-model="code"
          type="text"
          placeholder="请输入验证码"
        />
        <span class="code" @click="send">{{
          time === 0 ? '发送验证码' : `${time}s后发送`
        }}</span>
      </div>
      <div class="error">{{ codeError }}</div>
    </div>
    <div class="xtx-form-item">
      <div class="field">
        <i class="icon iconfont icon-lock"></i>
        <input
          class="input"
          v-model="password"
          type="password"
          placeholder="请输入密码"
        />
      </div>
      <div class="error">{{ passwordError }}</div>
    </div>
    <div class="xtx-form-item">
      <div class="field">
        <i class="icon iconfont icon-lock"></i>
        <input
          class="input"
          v-model="repassword"
          type="password"
          placeholder="请确认密码"
        />
      </div>
      <div class="error">{{ repasswordError }}</div>
    </div>
    <a href="javascript:;" class="submit" @click="bind">立即提交</a>
  </div>
</template>

<style scoped lang="less">
.code {
  position: absolute;
  right: 0;
  top: 0;
  line-height: 50px;
  width: 80px;
  color: #999;
  &:hover {
    cursor: pointer;
  }
}
</style>

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值