前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >从零玩转系列之腾讯云扫码授权系统-PC+小程序篇

从零玩转系列之腾讯云扫码授权系统-PC+小程序篇

原创
作者头像
杨不易呀
修改于 2024-12-03 02:19:55
修改于 2024-12-03 02:19:55
53300
代码可运行
举报
文章被收录于专栏:杨不易呀杨不易呀
运行总次数:0
代码可运行

推荐

在文章开始之前,推荐一些很值得阅读的好文章!感兴趣的也可以去看一下哦!

推荐文章

推荐原因

立即前往

『前端必修课』视频文字特效

这篇文章是腾讯云开发者社区的BNTang的“前端必修课”系列之一 在这篇文章中,介绍了如何实现视频文字特效。文章总结了通过mix-blend-mode实现视频与文字融合特效的方法,以及如何通过CSS的object-fit属性让视频适应容器尺寸. 感兴趣的同学快来阅读吧~

前言

halo 好久不见,我是杨不易呀,本片文章是写给热爱折腾的技术小伙伴们!

本篇文章是继《从零玩转系列之腾讯云微信扫码授权系统》的前端和移动端篇, 在前面我们搭建完毕后端工程和接口功能, 接下来就需要开发前端和移动端进行对接,

前面章节我画了一个这个业务流程图, 那么扫码步骤当然是首次加载去渲染我们的一个小程序二维码, 所以我们就先开发 PC 端再去开发小程序端.

PC 前端搭建

准备工作: 准备好 Vue3 支持的 NodeJs 版本

NodeJs 版本 18.17.0 推荐 可以使用版本工具 nvm 管理

我这里为同学们提供脚手架我已经搭建好了只需要编写核心功能即可.

脚手架项目初始化

frontend.zip

点击上面链接下载脚手架前端项目, 项目结构介绍如下图:

执行 npm install 来下载依赖, 下载完毕后执行 npm run dev 即可运行项目

cd frontend npm install npm run dev

运行项目 npm run dev

项目依赖介绍

  • 基础依赖:element-plus(UI)、vue-router(路由)、axios(请求)
  • 开发工具:sass(样式)、mitt(通信)
  • 构建插件:
    • setup-extend(组件命名)
    • auto-import(API 自动导入)
    • vue-components(组件自动导入)
    • compression(资源压缩)

对接二维码渲染

在前面章节我们已经完成了后端的接口实现了生成二维码、查询二维码状态、取消授权接口 在这里为什么要提到取消授权接口?

在前面的章节一当中《装修createQrCodeLogin 接口 - 目录》讲过

前端有个短轮训, 顾名思义就是短期内会去实时查询二维码的状态那么你可以根据你的业务来设置,比如五分钟、两分钟、一分钟都可以的, 那么这个时候过期了我们就要同步后端二维码接口去更新该二维码已经超时.

接下来我们开始编写扫码登录页面

新增扫码登录页面

登录页面项目一般都会有,只需要新增一个扫码的组件, 那么我就直接编写了一个小案例提供大家选择, 也可自己改造哦.

需求评审:

我们需要调用生成小程序二维码接口,拿到 Base64 直接渲染

开启定时器实时查询二维码状态, 实时渲染二维码扫码状态.

那么总结下主要功能包括:

  • 展示登录二维码
  • 实时显示扫码状态
  • 倒计时功能
  • 二维码过期显示刷新按钮
  • 授权成功跳转成功页面

创建 Login 页面等配置

打开项目在 src 目录下创建存放业务页面文件夹 pages 也可以叫 views在该目录下创建 Login.vue 页面组合式 API

如果你是用的 jetbrits 产品工具开发的那么就和我一样不需要写 Vue3 语法模版不是的话可能是空白的要你自己编写一下.

代码语言:vue
AI代码解释
复制
<template>
  <div>
    这里是登录页面
  </div>
</template>

<script setup>

</script>

<style scoped lang="scss">

</style>

编写好后还需要配置页面路由, 然后观察页面, 你会发现为什么还是 halo 页面?

那是因为在 Vue 当中的 App 表示全局入口它需要配置一个 Router-view 组件

什么是 router-view ?

<router-view/> 是 Vue.js 中的一个组件, 它用于渲染与当前路由匹配的组件。

通常在单页应用(SPA)中使用 Vue Router 进行路由管理时, 你会在应用的根组件中放置一个

<router-view/> 标签,以便根据用户的导航动态地展示不同的页面组件.

那么修改一下 APP 页面添加 <router-view/>组件

在刷新页面观察可以看到我们的登录页面成功渲染

装修 Login 页面

之前有同学私信我说, 杨老师, 您讲的太细了每一个步骤一段代码, 我是都会的部分代码想直接 copy 这样子下来我有点累, 我心想也是哦, 但是我的标题是从零玩转系列.

那么如果需要完整的代码移步后面的《完整 Login.vue 页面代码》目录直接复制, 这里就不用看啦~

那么这里我就已经为大家写好了页面元素和样式, 只需要编写核心的功能即可

先修改 api/xcxqrcode.js新增需要的接口请求

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import request from '@/utils/request';

/**
 * 获取登录二维码
 * @returns {*}
 */
export function createAppletQrCode() {
  return request({
    url: `/applet/createAppletQrCode`,
    method: 'post'
  });
}

/**
 * 检查登录状态
 * @param scene
 * @returns {*}
 */
export function checkLoginStatus(scene) {
  return request({
    url: `/applet/userAuthStatus/${scene}/applet`,
    method: 'get'
  });
}

/**
 * 取消授权
 */
export function userCancelAuth(scene) {
  return request({
    url: `/applet/userCancelAuth/${scene}/applet`,
    method: 'post'
  });
}

这里我已经给大家写好了前端样式, 不用去作重复工作了, 直接复制即可, 我们主要关心的是业务和思路.

将下面代码复制到 login 页面 template标签当中

代码语言:vue
AI代码解释
复制
<!--腾讯云开发者社区领袖-杨不易呀-->
<template>
  <div class="login-container">
    <div class="qr-code-wrapper" v-loading="loading">
      <!-- 二维码显示区域 -->
      <div v-if="!state.scanning" class="qr-code">
        <img :src="state.qrcodeUrl" alt="登录二维码" />
      </div>

      <!-- 状态显示区域 -->
      <div v-else class="status-display">
        <template v-if="state.scanStatus === ScanState.TIMEOUT || state.scanStatus === ScanState.CANCEL">
          <div class="error-status">
            <img src="@/assets/svg/login_time.svg" class="status-icon">
              <p class="error-tip">{{ state.scanStatus === ScanState.TIMEOUT ? '二维码已过期,请刷新' : '已取消授权,请重新扫码' }}</p>
            </div>
        </template>

        <template v-else>
          <div class="scan-status">
            <img src="@/assets/svg/auth_success.svg" class="status-icon">
              <p class="sub-text">微信扫码成功</p>
              <p class="sub-text">请在微信中确认授权</p>
            </div>
        </template>
      </div>

      <!-- 状态提示文本 -->
      <div class="status-text" :class="getStatusClass">
        {{ getStatusText }}
      </div>

      <!-- 在状态文本上方添加倒计时 -->
      <div class="countdown" v-if="state.loginTime > 0 && !state.scanning">
        <span>{{ state.loginTime }}秒后过期</span>
      </div>
    </div>

    <!-- 底部操作区 -->
    <div class="refresh-overlay" v-if="state.scanStatus === ScanState.TIMEOUT">
      <el-button type="primary" @click="handleRefresh">刷新二维码</el-button>
    </div>
  </div>
</template>

将下面样式代码复制到 style标签当中

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<style scoped lang="scss">
.login-container {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  min-height: 100vh;
  background-color: #f5f5f5;

  .qr-code-wrapper {
    background: white;
    padding: 2rem;
    border-radius: 0.5rem;
    box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
    width: 320px;

    .qr-code {
      position: relative;

      img {
        width: 100%;
        height: auto;
      }

      .refresh-overlay {
        position: absolute;
        inset: 0;
        display: flex;
        align-items: center;
        justify-content: center;
        background: rgba(0, 0, 0, 0.5);
      }
    }

    .status-display {
      width: 320px;
      height: 320px;
      display: flex;
      align-items: center;
      justify-content: center;

      .error-status {
        width: 100%;
        height: 100%;
        display: flex;
        flex-direction: column;
        align-items: center;
        justify-content: center;
        background: #ffffff;
        border-radius: 24px;
        padding: 32px;

        .status-icon {
          width: 130px;
          height: 130px;
          margin: 0 auto 1rem;
        }

        p {
          font-size: 20px;
          font-weight: 500;
          color: #1d2129;
          margin: 8px 0;
          text-align: center;

          &.error-tip {
            font-size: 14px;
            color: #ff4d4f;
            margin-top: 16px;
            padding: 8px 16px;
            background: #fff2f0;
            border-radius: 4px;
          }
        }

      }

      .sub-text {
        text-align: center;
        color: #666;
        font-size: 14px;
        margin-top: 0.5rem;
      }
    }

    .status-text {
      text-align: center;
      margin-top: 1rem;
      font-size: 20px;
      font-weight: bold;
      
      &.wait {
        color: #58595B;
      }

      &.success {
        color: green;
      }

      &.scanned {
        color: #58595B;
      }

      &.error {
        color: #ff4d4f;
      }
    }

    .countdown {
      text-align: center;
      margin-top: 10px;
      color: #909399;
      font-size: 14px;
    }
  }

  .footer-actions {
    margin-top: 1.5rem;
    text-align: center;

    .help-text {
      color: #666;
      font-size: 14px;
      margin-bottom: 1rem;
    }
  }
}
</style>

PC 前端代码讲解视频(小卡拉米必看)

扫码登录功能实现

  1. 定义路由和扫码状态
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
const router = useRouter()

const ScanState = {
  WAIT: 'wait',// 等待扫码
  SCANNED: 'scanned', // 用户扫码成功
  SUCCESS: 'success', // 用户授权成功
  CANCEL: 'cancel', // 用户主动取消授权
  TIMEOUT: 'loginTime' // 前端定义的登录超时状态
}

我们使用Vue Router的useRouter来获取路由实例,用于后续的页面跳转。ScanState对象定义了扫码登录过程中可能遇到的几种状态。

  1. 状态管理
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
const state = reactive({
  scanStatus: ScanState.WAIT, // 扫码状态
  qrcodeUrl: 'img.png', // 二维码Base64数据
  scanning: false, // 是否扫码成功
  loginTime: 60 // 登录倒计时
})

使用Vue的reactive函数来创建一个响应式的状态对象,用于存储扫码状态、二维码URL、扫码是否成功和登录倒计时。

  1. 加载层和定时器
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
const loading = ref(false)
const timer = ref(null)

定义一个响应式的loading变量来控制加载层的显示和隐藏,以及一个timer变量来存储定时器的引用。

  1. 计算扫码状态文本 和 文本样式
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
const getStatusText = computed(() => {
  const statusMessages = {
    [ScanState.WAIT]: '请扫码完成登录',
    [ScanState.SCANNED]: '已扫码,等待认证',
    [ScanState.SUCCESS]: '用户认证成功',
    [ScanState.CANCEL]: '用户取消认证',
    [ScanState.TIMEOUT]: '扫码超时'
  }
  return statusMessages[state.scanStatus]
})

const getStatusClass = computed(() => ({
  'wait': state.scanStatus === ScanState.WAIT,
  'success': state.scanStatus === ScanState.SUCCESS,
  'scanned': state.scanStatus === ScanState.SCANNED,
  'error': [ScanState.TIMEOUT, ScanState.CANCEL].includes(state.scanStatus)
}))

定义两个计算属性getStatusClassgetStatusText,分别用于根据当前的扫码状态返回对应的CSS类和状态文本。

  1. 登录相关方法
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
const handleRefresh = async () => {
  clearLoginTimer()
  await initLogin()
}

const initLogin = async () => {
  try {
    const res = await createAppletQrCode()
    const { scene, qrcode } = res.data

    state.qrcodeUrl = qrcode
    state.scanStatus = ScanState.WAIT
    state.scanning = false
    state.loginTime = 60

    startLoginCheck(scene)
  } catch (error) {
    console.error('获取二维码失败:', error)
  } finally {
    loading.value = false
  }
}

handleRefresh方法用于重新初始化登录流程,initLogin方法用于初始化扫码登录参数,包括获取小程序二维码,并开始登录检查。

  1. 开启登录检查

startLoginCheck方法用于开启一个定时器,每隔一秒检查一次登录状态,并更新登录倒计时。

checkLogin方法用于检查登录状态,并根据状态更新前端显示和执行相应的操作。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
const startLoginCheck = (scene) => {
  timer.value = setInterval(async () => {
    if (state.loginTime <= 0) {
      await handleTimeout(scene)
      return
    }
    await checkLogin(scene)
    state.loginTime -= 1
  }, 1000)
}

const checkLogin = async (scene) => {
  try {
    const { msg, data } = await checkLoginStatus(scene)
    state.scanStatus = msg

    switch (msg) {
      case ScanState.SCANNED:
        state.scanning = true
        break
      case ScanState.SUCCESS:
        await handleLoginSuccess(scene, data)
        break
      case ScanState.CANCEL:
        await handleCancelStatus(scene)
        break
    }
  } catch (error) {
    console.error('登录状态检查失败:', error)
    clearLoginTimer()
  }
}
  1. 登录成功后的操作

handleLoginSuccess方法用于处理登录成功后的逻辑,包括更新状态、发送事件和跳转到成功页面。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
const handleLoginSuccess = async (scene, data) => {
  state.scanStatus = ScanState.SUCCESS
  clearLoginTimer()
  setTimeout(async () => {
    eventBus.$emit('authSuccess', data)
    await router.push('/index')
  }, 2000)
  await remoteuUserCancelAuth(scene)
}
  1. 取消授权

小程序端用户进行取消授权

handleCancelStatus方法用于处理用户取消授权的情况,更新状态并清除定时器。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
const handleCancelStatus = async (scene) => {
  state.scanStatus = ScanState.CANCEL
  state.scanning = true
  clearLoginTimer()
}
  1. 登录超时

handleTimeout方法用于处理登录超时的情况,更新状态、清除定时器并取消授权。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
const handleTimeout = async (scene) => {
  state.scanStatus = ScanState.TIMEOUT
  state.scanning = true
  clearLoginTimer()
  await remoteuUserCancelAuth(scene)
}
  1. 清除定时器

clearLoginTimer方法用于清除登录检查的定时器

代码语言:java
AI代码解释
复制
const clearLoginTimer = () => {
    if (timer.value) {
        clearInterval(timer.value)
        timer.value = null
    }
}
  1. 拆分远程请求 - 取消用户授权

remoteuUserCancelAuth方法用于发送请求取消用户的授权。

代码语言:java
AI代码解释
复制
const remoteuUserCancelAuth = async (scene) => {
  try {
    await userCancelAuth(scene)
  } catch (error) {
    console.error('删除登录信息失败:', error)
  }
}
  1. 生命周期钩子

在组件挂载时,显示加载层并初始化登录流程。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
onMounted(() => {
  loading.value = true
  initLogin()
})

完整 Login.vue 页面代码(推荐观看讲解视频)

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<!--腾讯云开发者社区领袖-杨不易呀-->
<template>
  <div class="login-container">
    <div class="qr-code-wrapper" v-loading="loading">
      <!-- 二维码显示区域 -->
      <div v-if="!state.scanning" class="qr-code">
        <img :src="state.qrcodeUrl" alt="登录二维码" />
      </div>

      <!-- 状态显示区域 -->
      <div v-else class="status-display">
        <template v-if="state.scanStatus === ScanState.TIMEOUT || state.scanStatus === ScanState.CANCEL">
          <div class="error-status">
            <img src="@/assets/svg/login_time.svg" class="status-icon">
            <p class="error-tip">{{ state.scanStatus === ScanState.TIMEOUT ? '二维码已过期,请刷新' : '已取消授权,请重新扫码' }}</p>
          </div>
        </template>

        <template v-else>
          <div class="scan-status">
            <img src="@/assets/svg/auth_success.svg" class="status-icon">
            <p class="sub-text">微信扫码成功</p>
            <p class="sub-text">请在微信中确认授权</p>
          </div>
        </template>
      </div>

      <!-- 状态提示文本 -->
      <div class="status-text" :class="getStatusClass">
        {{ getStatusText }}
      </div>

      <!-- 在状态文本上方添加倒计时 -->
      <div class="countdown" v-if="state.loginTime > 0 && !state.scanning">
        <span>{{ state.loginTime }}秒后过期</span>
      </div>
    </div>

    <!-- 底部操作区 -->
    <div class="refresh-overlay" v-if="state.scanStatus === ScanState.TIMEOUT">
      <el-button type="primary" @click="handleRefresh">刷新二维码</el-button>
    </div>
  </div>
</template>

<script setup>
import { checkLoginStatus, createAppletQrCode, userCancelAuth } from '@/api/xcxqrcode.js'
import eventBus from "@/utils/eventBus.js"

const router = useRouter()

// 定义扫码状态
const ScanState = {
  WAIT: 'wait',// 等待扫码
  SCANNED: 'scanned', // 用户扫码成功
  SUCCESS: 'success', // 用户授权成功
  CANCEL: 'cancel', // 用户主动取消授权
  TIMEOUT: 'loginTime' // 前端定义的登录超时状态
}

// 统一状态管理
const state = reactive({
  scanStatus: ScanState.WAIT, // 扫码状态
  qrcodeUrl: 'img.png', // 二维码Base64数据
  scanning: false, // 是否扫码成功
  loginTime: 60 // 登录倒计时
})
// 加载层
const loading = ref(false)
// 定时器
const timer = ref(null)

// 计算属性
const getStatusClass = computed(() => ({
  'wait': state.scanStatus === ScanState.WAIT,
  'success': state.scanStatus === ScanState.SUCCESS,
  'scanned': state.scanStatus === ScanState.SCANNED,
  'error': [ScanState.TIMEOUT, ScanState.CANCEL].includes(state.scanStatus)
}))

// 翻译状态文本
const getStatusText = computed(() => {
  const statusMessages = {
    [ScanState.WAIT]: '请扫码完成登录',
    [ScanState.SCANNED]: '已扫码,等待认证',
    [ScanState.SUCCESS]: '用户认证成功',
    [ScanState.CANCEL]: '用户取消认证',
    [ScanState.TIMEOUT]: '扫码超时'
  }
  return statusMessages[state.scanStatus]
})

// 登录相关方法
const handleRefresh = async () => {
  clearLoginTimer()
  await initLogin()
}

// 初始化扫码参数
const initLogin = async () => {
  try {
    const res = await createAppletQrCode()
    const { scene, qrcode } = res.data

    state.qrcodeUrl = qrcode
    state.scanStatus = ScanState.WAIT
    state.scanning = false
    state.loginTime = 60

    startLoginCheck(scene)
  } catch (error) {
    console.error('获取二维码失败:', error)
  } finally {
    loading.value = false
  }
}

// 开启登录检查
const startLoginCheck = (scene) => {
  // 开启定时器短轮训
  timer.value = setInterval(async () => {
    if (state.loginTime <= 0) {
      await handleTimeout(scene)
      return
    }
    await checkLogin(scene)
    state.loginTime -= 1
  }, 1000)
}

// 检查登录
const checkLogin = async (scene) => {
  try {
    const { msg, data } = await checkLoginStatus(scene)
    console.log('status', msg)
    state.scanStatus = msg

    // 根据状态判断是否成功登录
    switch (msg) {
      case ScanState.SCANNED:
        state.scanning = true
        break
      case ScanState.SUCCESS:
        await handleLoginSuccess(scene, data)
        break
      case ScanState.CANCEL:
        await handleCancelStatus(scene)
        break
    }
  } catch (error) {
    console.error('登录状态检查失败:', error)
    clearLoginTimer()
  }
}


const handleLoginSuccess = async (scene, data) => {
  state.scanStatus = ScanState.SUCCESS
  clearLoginTimer()
  try {
    setTimeout(async () => {
      // 发送登录成功事件
      eventBus.$emit('authSuccess', data)
      await router.push('/index')
    }, 2000)
  } finally {
    await deleteLoginInfo(scene)
  }
}

const handleCancelStatus = async (scene) => {
  state.scanStatus = ScanState.CANCEL
  state.scanning = true
  clearLoginTimer()
  await deleteLoginInfo(scene)

  setTimeout(() => {
    state.scanStatus = ScanState.WAIT
  }, 1000)
}

const handleTimeout = async (scene) => {
  state.scanStatus = ScanState.TIMEOUT
  state.scanning = true
  clearLoginTimer()
  await deleteLoginInfo(scene)
}

const clearLoginTimer = () => {
  if (timer.value) {
    clearInterval(timer.value)
    timer.value = null
  }
}

const deleteLoginInfo = async (scene) => {
  try {
    await userCancelAuth(scene)
  } catch (error) {
    console.error('删除登录信息失败:', error)
  }
}

// 生命周期钩子
onMounted(() => {
  loading.value = true
  initLogin()
})
</script>

<style scoped lang="scss">
.login-container {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  min-height: 100vh;
  background-color: #f5f5f5;

  .qr-code-wrapper {
    background: white;
    padding: 2rem;
    border-radius: 0.5rem;
    box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
    width: 320px;

    .qr-code {
      position: relative;

      img {
        width: 100%;
        height: auto;
      }

      .refresh-overlay {
        position: absolute;
        inset: 0;
        display: flex;
        align-items: center;
        justify-content: center;
        background: rgba(0, 0, 0, 0.5);
      }
    }

    .status-display {
      width: 320px;
      height: 320px;
      display: flex;
      align-items: center;
      justify-content: center;

      .error-status {
        width: 100%;
        height: 100%;
        display: flex;
        flex-direction: column;
        align-items: center;
        justify-content: center;
        background: #ffffff;
        border-radius: 24px;
        padding: 32px;

        .status-icon {
          width: 130px;
          height: 130px;
          margin: 0 auto 1rem;
        }

        p {
          font-size: 20px;
          font-weight: 500;
          color: #1d2129;
          margin: 8px 0;
          text-align: center;

          &.error-tip {
            font-size: 14px;
            color: #ff4d4f;
            margin-top: 16px;
            padding: 8px 16px;
            background: #fff2f0;
            border-radius: 4px;
          }
        }

      }

      .sub-text {
        text-align: center;
        color: #666;
        font-size: 14px;
        margin-top: 0.5rem;
      }
    }

    .status-text {
      text-align: center;
      margin-top: 1rem;
      font-size: 20px;
      font-weight: bold;

      &.wait {
        color: #58595B;
      }

      &.success {
        color: green;
      }

      &.scanned {
        color: #58595B;
      }

      &.error {
        color: #ff4d4f;
      }
    }

    .countdown {
      text-align: center;
      margin-top: 10px;
      color: #909399;
      font-size: 14px;
    }
  }

  .footer-actions {
    margin-top: 1.5rem;
    text-align: center;

    .help-text {
      color: #666;
      font-size: 14px;
      margin-bottom: 1rem;
    }
  }
}
</style>

那么到这里我们的 PC 前端就告一段落, 接下来就是开发 uniapp 小程序端

微信小程序搭建

在第一章节我已经介绍了我们使用的技术栈与对应的框架, uniapp + vue3

前往官方文档: https://uniapp.dcloud.net.cn/quickstart-hx.html

可以看到官方介绍有两种方式一种通过 HB 编辑器构建一种通过 vue-cli 命令构建

使用 HB 编辑器需要安装软件所以 看你喜欢看一种,我这里为了极速就直接使用编辑器快速构建 uniapp 基本架构项目

下载安装如下工具:

HBuilderX是通用的前端开发工具,但为uni-app做了特别强化。

创建uni-app

在点击工具栏里的文件 -> 新建 -> 项目(快捷键Ctrl+N

或者直接右键侧边栏也可以创建

创建工程

选择uni-app类型,输入工程名,选择模板,点击创建,即可成功创建。

工程项目名称 : tencent-wechat-scan-login-project, vue 版本别忘记选择版本 3

配置微信开发者工具

那么初始化项目就完成了如下:

还需要配置微信开发者工具地址, 使用 HB 启动项目会自动打开开发者工具

配置小程序 APPID

最重要一步配置项目的小程序 AppID

接着我们直接运行项目

第一次会弹出权限, 直接信任即可

信任后会默认重新加载项目, 如果没有则主动点击 编译 将会重新加载

新建 Login 页面

右击 pages 文件夹新增页面 Login

如果你不是工具创建的 pages 里面的路由别忘记配置, 没有配置项目会启动不了.

小程序启动页

小程序默认 pages 第一个为项目启动页面, 也就是主页面, 所以我们需要将 Login 设置为主页面, 这样子我们才能看得, 如果不能看到那么就点击 编译即可重新加载

可以看到默认就是登录页面, 那么开始编写登录页面

封装请求工具

在 PC 端项目里面有个 request 工具类里面就是对应 axios 请求工具, uniapp 它有自己的请求我也进行了简单的封装, 直接 cv 到你的项目工程当中即可, 不需要关心这个.

新增 common 工具

这里面存放常用的方法, 提供系统当中全局调用

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// yangbuyi Copyright (c) https://yby6.com 2023.

/**
 * 显示消息提示框
 * @param content 提示的标题
 * @param duration 时间
 */
export function toast(content, duration = 2) {
	uni.showToast({
		icon: 'none',
		title: content,
		duration: duration * 1000
	})
}

/**
 * 显示模态弹窗
 * @param content 提示的标题
 */
export function showConfirm(content) {
	return new Promise((resolve, reject) => {
		uni.showModal({
			title: '提示',
			content: content,
			cancelText: '取消',
			confirmText: '确定',
			success: function(res) {
				resolve(res)
			}
		})
	})
}

/**
 * 从剪切板获取文本并粘贴到输入框
 */
export function getClipboardData() {
  return new Promise((resolve, reject) => {
    uni.getClipboardData({
      success: (res) => {
        uni.showToast({
          title: '粘贴成功',
          icon: 'success',
          duration: 2000,
        });
        resolve(res.data); // 返回粘贴的内容
      },
      fail: (err) => {
        uni.showToast({
          title: '粘贴失败',
          icon: 'none',
        });
        reject(err); // 返回错误信息
      },
    });
  });
}


/**
 * 参数处理
 * @param params 参数
 */
export function tansParams(params) {
	let result = ''
	for (const propName of Object.keys(params)) {
		const value = params[propName]
		var part = encodeURIComponent(propName) + "="
		if (value !== null && value !== "" && typeof(value) !== "undefined") {
			if (typeof value === 'object') {
				for (const key of Object.keys(value)) {
					if (value[key] !== null && value[key] !== "" && typeof(value[key]) !== 'undefined') {
						let params = propName + '[' + key + ']'
						var subPart = encodeURIComponent(params) + "="
						result += subPart + encodeURIComponent(value[key]) + "&"
					}
				}
			} else {
				result += part + encodeURIComponent(value) + "&"
			}
		}
	}
	return result
}

新增 errorCode 状态码

用于请求后端翻译后端状态码给前端交互显示, 错误不可能给用户看对吧.

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// yangbuyi Copyright (c) https://yby6.com 2024.

export default {
  '401': '认证失败,无法访问系统资源',
  '403': '当前操作没有权限',
  '404': '访问资源不存在',
  'default': '系统未知错误,请反馈给管理员'
}

新作 request 请求工具类

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// yangbuyi Copyright (c) https://yby6.com 2024.

import {tansParams, toast} from '@/utils/common'
import errorCode from '@/utils/errorCode'
// 请求超时时间
let timeout = 10000
// 请求后端地址
const baseUrl = "http://127.0.0.1:9876"

// 封装请求函数
const request = config => {
    // get请求映射params参数
    if (config.params) {
        let url = config.url + '?' + tansParams(config.params)
        url = url.slice(0, -1)
        config.url = url
    }
    return new Promise((resolve, reject) => {
        // 如果 url 是http的
        let url = baseUrl + config.url
        if (config.url.indexOf("http") !== -1) {
            url = config.url
        }

        const requestConfig = {
            method: config.method || 'get',
            timeout: timeout,
            url: url,
            data: config.data,
            header: config.header
        }

        if (requestConfig.header === undefined) {
            requestConfig.header = {}
        }

        // 判断 responseType
        if (config.responseType) {
            requestConfig.responseType = config.responseType
        }

        // headers
        if (config.headers) {
            requestConfig.header = config.headers
        }

        uni.request(requestConfig).then(response => {
            const res = response.data
            const code = res.code || 200
            const msg = errorCode[code] || res.msg || errorCode['default']
            if (code === 500) {
                toast(msg)
                reject('500')
            } else if (code === -1) {
                toast(msg)
                reject('500')
            } else if (code !== 200 && code !== 100) {
                toast(msg)
                reject(code)
            } else {
                resolve(res)
            }
        }).catch(error => {
            console.log("请求错误:", error);
            toast("网络不太好~")
            reject(error)
        })
    })
}

export default request

编写小程序授权页面

在前面章节我介绍了我们需要做的小程序页面如下:

是不是非常好好看, 我设计的,接下来我带着同学们一步步来制作一下

页面拆解

可以看到页面首先有一个大的容器外层(红色框), 接着有一个黑色框容器包含里面的内容元素用于定位内容的位置

接着里面有绿色和蓝色框分别为授权模块和安全提示

绿色框里面就是一行行的元素安全图标、标题和安全提示文本

接着最后就是我们的授权功能按钮和取消授权按钮, 那么就一步步制作吧

装修授权页面

编写外部容器红色区域

1.整体容器布局

代码语言:vue
AI代码解释
复制
<view class="login-container">
  <view class="card-wrapper">
    <view class="main-card">
        <!-- 内容区域 -->
    </view>
     <!-- 底部区域 -->
    <view class="bottom-text">安全登录由系统加密保护</view>
  </view>
</view>

对应的样式, 专门新建一个外部层叠样式表 login.less

在 style 标签当中引用即可

代码语言:vue
AI代码解释
复制
<style scoped lang="less">
@import "login.less";
</style>
代码语言:vue
AI代码解释
复制
.login-container {
  min-height: 100vh;
  padding: 24rpx;
  background-color: #fafafa;
  display: flex;
  align-items: center;
  justify-content: center;
}

.card-wrapper {
  width: 100%;
  max-width: 650rpx;
  text-align: center;
  margin-top: -260rpx;
}

.main-card {
  background-color: #ffffff;
  border-radius: 48rpx;
  padding: 48rpx;
  box-shadow: 0 8rpx 30rpx rgba(0, 0, 0, 0.06);
}

.bottom-text {
  font-size: 24rpx;
  color: #9ca3af;
  text-align: center;
  margin-top: 30rpx;
}

这个 main-card 是整个登录界面的核心容器

  • 使用纯白背景(background-color: #ffffff)提供清晰的视觉对比
  • 大圆角设计(border-radius: 48rpx)使卡片更现代化
  • 适当的内边距(padding: 48rpx)确保内容不会太挤
  • 柔和的阴影效果(box-shadow)提供层次感

2.安全图标区域

这个就可以去 https://www.iconfont.cn/ 里面搜索你想要的安全图标

我这里就选择了其中的一个图标, 在稍微调整一下样式

代码语言:vue
AI代码解释
复制
<view class="icon-wrapper">
  <view class="icon-bg">
   <image class="security-icon" src="https://foruda.gitee.com/images/1731338756166193727/bb72a6ea_5151444.png"></image>
  </view>
</view>

对应样式给 icon 增加内边距、颜色偏白色系

代码语言:less
AI代码解释
复制
.icon-wrapper {
  display: flex;
  justify-content: center;
  margin-bottom: 48rpx;
}

.icon-bg {
  padding: 24rpx;
  background-color: rgba(59, 130, 246, 0.1);
  border-radius: 24rpx;
}

.security-icon {
  width: 64rpx;
  height: 64rpx;
}

3.标题和提示文本

文本区域头部

代码语言:vue
AI代码解释
复制
<view class="header">
  <text class="title">授权登录Web端服务</text>
  <text class="subtitle">授权后,将在浏览器授权登录</text>
</view>

对应的样式

代码语言:less
AI代码解释
复制
.header {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 16rpx;
  margin-bottom: 48rpx;
}

.title {
  font-size: 36rpx;
  font-weight: 600;
  color: #1f2937;
}

.subtitle {
  font-size: 28rpx;
  color: #6b7280;
}

4. 安全提示区域

显著的安全提示语, 让用户知道这个有些危险操作, 加以红色背景展示

代码语言:vue
AI代码解释
复制
<view class="security-notice">
  <text class="notice-text">若非本人操作,请取消授权!</text>
</view>

对应样式

代码语言:less
AI代码解释
复制
.security-notice {
  background-color: rgba(239, 68, 68, 0.1);
  border-radius: 24rpx;
  padding: 24rpx;
  margin-bottom: 48rpx;
}

.notice-text {
  font-size: 28rpx;
  color: #ef4444;
}

看一下效果是不是有哪个味道了哈哈哈, 好看的清爽

5.授权按钮组

两个按钮分别占一行, 显著的蓝色为重要的授权按钮快速看到点击, 取消授权按钮特意点击

代码语言:vue
AI代码解释
复制
<view class="button-group">
  <button class="confirm-button">
    确认授权登录
  </button>
  <text class="cancel-button">
    <text>取消授权</text>
  </text>
</view>

对应的样式, 做了点击的效果颜色加深一点

代码语言:less
AI代码解释
复制
.button-group {
  display: flex;
  flex-direction: column;
  gap: 24rpx;
}

.confirm-button {
  width: 100%;
  background-color: #3b82f6;
  color: #ffffff;
  font-size: 30rpx;
  font-weight: 500;
  padding: 10rpx;
  box-sizing: border-box;
  border-radius: 24rpx;
  text-align: center;
  box-shadow: 0 4rpx 12rpx rgba(59, 130, 246, 0.2);
  transition: all 0.2s ease;
}

.confirm-button:active {
  transform: translateY(2rpx);
  background-color: #2563eb;
  box-shadow: 0 2rpx 6rpx rgba(59, 130, 246, 0.1);
}

.cancel-button {
  width: 100%;
  text-align: center;
}

.cancel-button:active {
  color: #9ca3af;
}

.cancel-icon {
  width: 32rpx;
  height: 32rpx;
}

最终效果查看

非常的好看啊哈哈哈, 喜欢的不要忘记点个赞支持哦

实现确认授权登录

在前面章节当中我们生成了二维码这个二维码当中存在我们存入的 scene 场景值, 那么我们就要在 onLoad 函数当中去获取并且需要转一下参数

获取 scene 参数

scene 需要使用 decodeURIComponent 才能获取到生成二维码时传入的 scene

代码语言:less
AI代码解释
复制
import {onHide, onLaunch, onLoad} from '@dcloudio/uni-app'

onLoad((query) => {
  // scene 需要使用 decodeURIComponent 才能获取到生成二维码时传入的 scene
  const scene = decodeURIComponent(query.scene)
  console.log('index onLoad', scene)
  // 记录到本地方便接口调用传递参数
  modal.value.scene = scene
})

写好后模拟测试一下是否获取得到, 添加编译模式 启动参数填入 scene 参数

然后点击编译即可查看控制台

获取 code 码

我在前面章节的编写授权成功接口

在授权接口里面我们需要传递一个 Code这个 Code码就是微信小程序自带的授权唯一码

前往官方文档: https://developers.weixin.qq.com/miniprogram/dev/api/open-api/login/wx.login.html

可以看到我们只需要调用 wx.login 方法即可获得 code 码

因此我们给授权按钮增加点击实现来触发这个接口

装修确定授权功能

给确认授权登录按钮增加点击事件 handleLogin 方法, 并且新增一个禁止点击防止点击后重复点击的情况

定义 handleLogin 方法, 也分别定义了 modalloginDisabled 响应式变量

成功拿到 code 直接存放在 modal 对象下

测试查看页面效果

可以看到成功拿到了 Code 码, 并且按钮也是禁止状态

打通接口实现确认授权

定义请求接口, 在 login 目录新增 **api**目录

分别定义确认授权、取消授权、查询状态接口

代码语言:less
AI代码解释
复制
import request from '@/utils/request';


/**
 * 确认授权
 * @param data
 * @returns {*}=
 */
export function userAuthWebPro(data) {
    return request({
        url: `/applet/userAuthWebPro/${data.scene}/${data.code}/applet`,
        method: 'post'
    });
}

/**
 * 修改已扫码
 * @param data
 * @returns {*}=
 */
export function userScanQrcode(scene) {
    return request({
        url: `/applet/userScanQrcode/${scene}/applet`,
        method: 'post'
    });
}

/**
 * 取消授权
 * @param scene
 * @returns {*}=
 */
export function userCancelAuth(scene) {
    return request({
        url: `/applet/userCancelAuth/${scene}/applet`,
        method: 'post'
    });
}

和 PC 端一样的请求, 传递场景值和 code 码

代码语言:less
AI代码解释
复制
// 处理登录授权
const handleLogin = () => {
  // 开启登录按钮禁用
  loginDisabled.value = true
  wx.login({
    provider: 'weixin',
    success: (res) => {
      // console.log("登录code:", res)
      modal.value.code = res.code

      // 发起远程授权接口
      userAuthWebPro({
        scene: modal.value.scene,
        code: res.code
      }).then(res => {
        console.log('auth', res)
        uni.showToast({
          title: '授权成功',
          icon: 'success',
          duration: 1500
        });
        // 开启登录按钮禁用
        loginDisabled.value = false
         // 跳转到主页面
        setTimeout(() => {
    		  uni.reLaunch({
    			url: "/pages/index/index"
    		  })
    		}, 1700);
      })
        .catch(err => {
          console.log(err)
          uni.showToast({
            title: '授权异常!',
            icon: 'error',
            duration: 1500
          });
        })
    }
  })
}

初始化加载逻辑

在前面我介绍了整个业务的流程, 在小程序的时候跳转到指定页面的时候会主动发起请求修改扫码,在后端扫码逻辑当中会进行校验是否合法请求

不合法接口, 那么就退出授权页面到主页面即可 代码如下

代码语言:less
AI代码解释
复制
onLoad((query) => {
  // scene 需要使用 decodeURIComponent 才能获取到生成二维码时传入的 scene
  const scene = decodeURIComponent(query.scene)
  console.log('index onLoad', scene)
  if (!['', undefined, null].includes(scene)) {
    loginDisabled.value = false
    modal.value.scene = scene
	// 发送扫码
    userScanQrcode(scene)
      .then(res => {
        console.log(res)
      })
      .catch(error => {
        console.log('err', error)
        uni.showToast({
          title: '当前环境异常!',
          icon: 'error',
          duration: 1500
        });
		// 这里编写跳转到首页
		setTimeout(() => {
		  uni.reLaunch({
		    url: "/pages/index/index"
		  })
		}, 1500)
      })
  } else {
    uni.showToast({
      title: '当前环境异常!',
      icon: 'error',
      duration: 1500
    });
	 setTimeout(() => {
	   uni.reLaunch({
		 url: "/pages/index/index"
	   })
	 }, 1500)
  }
})

后端配置修改

后端工程别忘记把小程序授权页面的路径修改一下

新作要打开的小程序版本参数

后端小程序完整配置

代码语言:java
AI代码解释
复制
/**
 * 小程序配置
 *
 * @author Yang Shuai
 * Create By 2024/11/26
 */
@Configuration
@ConfigurationProperties(prefix = "login")
public class WechatLoginConfig {
    /**
     * 是否开启
     */
    public static boolean enable;

    /**
     * 小程序appId
     */
    public static String appId;

    /**
     * 小程序密钥
     */
    public static String secret;

    /**
     * 小程序授权登录页面
     */
    public static String authPage;

    /**
     * 二维码大小 默认430
     */
    public static String width = "430";

    /**
     * 要打开的小程序版本。正式版为 "release",体验版为 "trial",开发版为 "develop"。默认是正式版。
     */
    public static String envVersion = "release";

    /**
     * 是否检查路径
     */
    public static boolean checkPath;


    public void setEnable(boolean enable) {
        WechatLoginConfig.enable = enable;
    }

    public void setAppId(String appId) {
        WechatLoginConfig.appId = appId;
    }

    public void setSecret(String secret) {
        WechatLoginConfig.secret = secret;
    }

    public void setAuthpage(String authPage) {
        WechatLoginConfig.authPage = authPage;
    }
    public void setEnvVersion(String envVersion) {
        WechatLoginConfig.envVersion = envVersion;
    }

}

还有生成小程序二维码新增版本请求参数即可

完整测试扫码流程

因为我们是将小程序发布到了体验版小程序服务器当中, 所以我们小程序里面定义的请求后端接口是请求不到的, 所以需要准备一个内网穿透代理到本地后端 9876 端口

测试流程 ->>> 启动前端工程 ->>> 启动后端工程 ->>> 启动内网穿透对应后端端口

->>> 将最新版的小程序提交到体验版 ->>> 手机微信扫码小程序二维码会跳转到小程序授权页面, 点击授权观察前端 PC 页面效果

授权扫码流程 - 测试视频演示

实现取消授权

最后就是取消授权, 调用一下接口即可, 然后观察前端是否状态变成取消授权

代码语言:less
AI代码解释
复制
// 处理取消授权
const handleCancel = () => {
  userCancelAuth(modal.value.scene)
    .then(() => {
      uni.reLaunch({
        url: "/pages/home/home"
      })
    })
    .catch(error => {
      uni.showToast({
        title: '请重新扫码授权',
        icon: 'error',
        duration: 1500
      });
      setTimeout(() => {
        uni.reLaunch({
          url: "/pages/home/home"
        })
      }, 1500)
    })
}

那么到这从零玩转系列之腾讯云扫码授权系统到结束啦!

最后

本期结束咱们下次再见👋

关注我不迷路,如果本篇文章对你有所帮助,或者你有什么疑问,欢迎在评论区留言,我一般看到都会回复的。大家点赞支持一下哟~ 💗

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
4 条评论
热度
最新
杨哥,太适合小白了
杨哥,太适合小白了
112举报
学习起来吧
学习起来吧
回复回复1举报
写的不错,对于学习与练手非常好的文章。
写的不错,对于学习与练手非常好的文章。
111举报
操练起来
操练起来
回复回复1举报
推荐阅读
从零玩转系列之腾讯云微信扫码授权系统
在我读书的时候就想玩这个功能很久了那个时候受限于这个功能需要企业或个体户去花费三百块认证服务号等方式, 反正企业或者个体户就难倒一大片了吧? 还要钱, 对于很多程序员是舍不得的, 那么不想认证又不想花费怎么办?
杨不易呀
2024/11/29
1.6K16
从零玩转系列之腾讯云微信扫码授权系统
小程序引导用户点击登录授权弹出层,以组件的形式调用
简介 小程序用户登录,功能页的使用,是需要用户授权登录,但是登陆的代码不可能每个页面都写一次,解决的办法有很多种,下面介绍一种方式,以组件调用的方式简单实现用户授权登录
子润先生
2021/06/25
1.5K0
黑马程序员uni-app 小兔鲜儿 项目及bug记录(上)
https://blog.csdn.net/qq_42880714/article/details/126509087
Qiuner
2024/07/20
5690
黑马程序员uni-app 小兔鲜儿 项目及bug记录(上)
用小程序来实现扫码登录
在 web 开发中,少不了用户系统,开发者需要开发注册登录这些重复的功能,而对于用户来说,要要注册才可以使用,往往会不愿意,因为我们有太多的账号和密码了,而现在,微信拥有 12 亿的月活用户,使用微信实现扫码登录,会大大减少需要用户注册而造成的流失率,而实现微信扫码登录有一定门槛,首先需要是企业用户才可以在微信开发平台注册账号,紧接着需要认证缴费 300 元才可以,简直就是黑店,而现在我们可以使用小程序来实现,今天就来讲讲小程序扫码登录的实现方式。
狂奔滴小马
2022/09/21
2K0
用小程序来实现扫码登录
微信扫描小程序码登录 PC 网站 Demo
本文主要介绍如何基于小程序页面授权,使用微信扫描PC端小程序码实现获取用户信息进行系统登录。
薛定喵君
2021/01/27
3.4K0
二维码登录实现(EggJS, 小程序)
扫码后, 判断是否存在二维码, 若存在, 则判断是否有效, 有效则判断当前微信用户是否绑定了后管账号, 绑定则进行登录操作, 反之返回未绑定状态码
治电小白菜
2020/08/25
1.2K0
二维码登录实现(EggJS, 小程序)
【uniapp】视频分享预览小程序
六一收到个不同以往的需求,我的指导老师最近有点忙,让我们帮忙做一个可以通过二维码预览视频的小程序
德宏大魔王
2023/08/08
3750
【uniapp】视频分享预览小程序
我的 Serverless 实战—SCF构建小型服务端并结合uni-app
【本文正在参与“100%有奖|我的Serverless 实战”征稿活动】,活动地址:https://marketing.csdn.net/p/15940c87f66c68188cfe5228cf4a0c3f
代码哈士奇
2021/10/25
9830
我的 Serverless 实战—SCF构建小型服务端并结合uni-app
【愚公系列】2022年11月 uniapp专题-优购电商-个人中心页面
个人中心的信息复杂度与产品本身有关。体系越庞大的产品,对应个人中心页面需要承载的内容越多,结构势必复杂。
愚公搬代码
2022/12/05
6050
【愚公系列】2022年11月 uniapp专题-优购电商-个人中心页面
从零玩转系列之微信支付实战Uni-App微信授权登录和装修下单页面和搭建下单接口以及发起下单请求
本篇将为您介绍微信支付在小程序 Uniapp 端的全新篇章。微信支付作为移动支付领域的先驱之一,不断演进与创新,为用户和开发者提供更便捷、安全的支付体验。在本文中,我们将深入探讨微信支付在小程序 Uniapp 端的应用与优势。
杨不易呀
2023/09/27
3K13
从零玩转系列之微信支付实战Uni-App微信授权登录和装修下单页面和搭建下单接口以及发起下单请求
uni-app(优医咨询)项目实战 - 第3天
上述配置内容是关于 Prettier 的常用的配置项,以后实际开发过程中可以根据需要逐步完善。
程序员朱永胜
2024/04/20
4560
小程序扫码成功后带着参数跳转到指定页面
要扫的二维码,通过草料二维码生成的,这个网站挺好的,用起来比较简单方便,可以直接输入文字生成二维码,也可以放入链接生成二维码。
王小婷
2018/12/26
5K0
测试平台分支-小程序端-3-小程序登录(上)
一年没更新了,今天本地运行了下都快忘了写啥了。ε=(´ο`*)))唉现在的行情还是多学习吧。
怪盗LYL
2023/09/15
4110
测试平台分支-小程序端-3-小程序登录(上)
【uniapp】文件授权验真系统(含代码)
之前的同学联系我说,他们公司想做一个能将客户的证明材料通过二维码扫描显示验真结果的一个系统(经他们公司核对无误后的验真),这个功能不难开发,我们先梳理一下思路:
德宏大魔王
2023/08/08
2500
【uniapp】文件授权验真系统(含代码)
微信小程序实现扫码登录网站
最近使用腾讯云时,用的都是微信扫码登入,发现会跳转到腾讯云助手小程序进行确认登入。感觉挺好用的,就想做一个扫码登入。
王秀龙
2021/09/09
7.4K3
uni-app(优医咨询)项目实战 - 第4天
此处的权限验证是指服务端接口验证码 token 是否存在或有效,这就需要我们在调用接口时将 token 以自定义头信息的方式发送给服务端接口,如果 token 不存在或者 token 过期了,则接口会返回状态码的值为 401。
程序员朱永胜
2024/04/25
3640
不会小程序设计?10分钟AI工具助你从需求到uniapp开发
之前2025年的年初开年之时,我立下了一个Flag,那就是写一个记账类型的小程序。
半月无霜
2025/03/29
1550
使用uni-app开发微信小程序之登录模块
从微信小程序官方发布的公告中我们可获知:小程序体验版、开发版调用 wx.getUserInfo 接口,将无法弹出授权询问框,默认调用失败,需使用 <button open-type="getUserInfo"></button> 引导用户主动进行授权操作:
wfaceboss
2019/04/08
5.8K1
使用uni-app开发微信小程序之登录模块
小程序生成普通二维码_注册一个小程序
参考了https://blog.csdn.net/lemontealin/article/details/104437584 这篇文章并做了修改,要想实现二维码的生成的话是需要引用相应插件的, 这个插件的作者是echo,echo写了整个Thor UI组件,我个人很佩服他,喜欢他的可以去Thor UI网站看看,学习一下。 1)首先下载你需要下载weapp-qrcode.js(百度网盘下载链接:链接:https://pan.baidu.com/s/1VXhq3ZjxmDcH1tFujKg75Q 提取码:vj2y) (2)在你的uni-app项目中需要的地方引入下载的weapp-qrcode.js文件。在标签中引入,如下
全栈程序员站长
2022/11/15
7750
小程序生成普通二维码_注册一个小程序
微信小程序+PHP 实现授权登录
发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/155584.html原文链接:https://javaforall.cn
全栈程序员站长
2022/09/07
2.5K0
推荐阅读
相关推荐
从零玩转系列之腾讯云微信扫码授权系统
更多 >
LV.6
腾讯云TDPKOL
目录
  • 推荐
  • 前言
  • PC 前端搭建
    • 脚手架项目初始化
      • 项目依赖介绍
  • 对接二维码渲染
  • 新增扫码登录页面
    • 需求评审:
    • 创建 Login 页面等配置
      • 什么是 router-view ?
    • 装修 Login 页面
      • PC 前端代码讲解视频(小卡拉米必看)
      • 扫码登录功能实现
      • 完整 Login.vue 页面代码(推荐观看讲解视频)
  • 微信小程序搭建
    • 创建uni-app
      • 创建工程
      • 配置微信开发者工具
      • 配置小程序 APPID
    • 新建 Login 页面
      • 小程序启动页
    • 封装请求工具
      • 新增 common 工具
      • 新增 errorCode 状态码
      • 新作 request 请求工具类
  • 编写小程序授权页面
    • 页面拆解
    • 装修授权页面
      • 1.整体容器布局
      • 2.安全图标区域
      • 3.标题和提示文本
      • 4. 安全提示区域
      • 5.授权按钮组
      • 最终效果查看
  • 实现确认授权登录
    • 获取 scene 参数
    • 获取 code 码
    • 装修确定授权功能
      • 测试查看页面效果
      • 打通接口实现确认授权
      • 初始化加载逻辑
      • 后端配置修改
    • 完整测试扫码流程
    • 授权扫码流程 - 测试视频演示
  • 实现取消授权
    • 最后
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档