一、目录结构示意图

miniprogram/                    // 小程序根目录
├── app.js                      // 小程序入口文件(配置云环境)
├── app.json                    // 小程序配置文件
├── app.wxss                    // 小程序全局样式
├── pages/                      // 页面文件夹
│   └── index/                  // 登录页面
│       ├── index.js           // 登录页面逻辑
│       ├── index.wxml         // 登录页面结构
│       └── index.wxss         // 登录页面样式
└── cloudfunctions/             // 云函数文件夹
    └── login/                  // 登录云函数
        ├── index.js           // 云函数入口文件
        └── package.json       // 云函数配置文件

二、实现步骤

1. 环境准备

  1. 开通云开发环境
  2. 创建云数据库集合 users
  3. 配置 app.js
App({
  onLaunch: function () {
    if (!wx.cloud) {
      console.error("请使用 2.2.3 或以上的基础库以使用云能力");
    } else {
      wx.cloud.init({
        env: "你的云环境ID",  // 替换为你的云环境ID
        traceUser: true,
      });
    }
  }
});

2. 页面结构(miniprogram/pages/index/index.wxml)

<view class="container">
  <view class="userinfo">
    <block wx:if="{{!hasUserInfo}}">
      <view class="login-tips">需要您的授权才能获取用户信息</view>
      <image wx:if="{{tempAvatar}}" class="userinfo-avatar" src="{{tempAvatar}}" mode="cover"></image>
      <button class="login-btn" 
              open-type="chooseAvatar" 
              bind:chooseavatar="onChooseAvatar">{{tempAvatar ? '重新设置头像' : '设置头像'}}</button>
      <view class="input-box">
        <text class="label">昵称:</text>
        <input type="nickname" 
               class="nickname-input" 
               placeholder="请输入昵称" 
               bind:change="onInputNickname"
               bind:nicknamereview="onNickNameReview"/>
      </view>
      <button class="confirm-btn" bindtap="confirmLogin">确认登录</button>
    </block>
    <block wx:else>
      <image class="userinfo-avatar" src="{{userInfo.avatarUrl}}" mode="cover"></image>
      <text class="userinfo-nickname">{{userInfo.nickName}}</text>
      <button class="reauth-btn" bindtap="reauthorize">重新设置</button>
    </block>
  </view>
</view>

3. 页面样式(miniprogram/pages/index/index.wxss)

.container {
  height: 100vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  background-color: #f6f6f6;
}

.userinfo {
  display: flex;
  flex-direction: column;
  align-items: center;
  background: #fff;
  padding: 40rpx;
  border-radius: 20rpx;
  box-shadow: 0 2rpx 10rpx rgba(0,0,0,0.1);
  width: 80%;
  max-width: 600rpx;
}

.login-tips {
  font-size: 28rpx;
  color: #666;
  margin-bottom: 20rpx;
  text-align: center;
}

.userinfo-avatar {
  width: 128rpx;
  height: 128rpx;
  margin: 20rpx;
  border-radius: 50%;
  overflow: hidden;
  border: 2rpx solid #e5e5e5;
  box-shadow: 0 2rpx 10rpx rgba(0,0,0,0.1);
}

.login-btn {
  background: #07c160;
  color: #fff;
  border: none;
  padding: 0 40rpx;
  border-radius: 40rpx;
  font-size: 32rpx;
  margin: 20rpx 0;
}

.input-box {
  width: 100%;
  margin: 20rpx 0;
  display: flex;
  align-items: center;
  padding: 0 20rpx;
  box-sizing: border-box;
}

.label {
  font-size: 28rpx;
  color: #333;
  margin-right: 20rpx;
}

.nickname-input {
  flex: 1;
  height: 80rpx;
  border: 1px solid #ddd;
  border-radius: 8rpx;
  padding: 0 20rpx;
  font-size: 28rpx;
}

.confirm-btn {
  background: #1aad19;
  color: #fff;
  border: none;
  padding: 0 40rpx;
  border-radius: 40rpx;
  font-size: 32rpx;
  margin-top: 30rpx;
  width: 60%;
}

.reauth-btn {
  margin-top: 20rpx;
  background: #f8f8f8;
  color: #333;
  border: 1px solid #ddd;
  padding: 0 30rpx;
  border-radius: 30rpx;
  font-size: 28rpx;
}

.login-btn::after,
.reauth-btn::after,
.confirm-btn::after {
  border: none;
}

.userinfo-nickname {
  color: #333;
  font-size: 32rpx;
  margin-top: 20rpx;
}

4. 页面逻辑(miniprogram/pages/index/index.js)

Page({
  /**
   * 页面的初始数据
   */
  data: {
    userInfo: null,
    hasUserInfo: false,
    tempAvatar: '',
    tempNickName: ''
  },

  /**
   * 生命周期函数--监听页面加载
   */
  onLoad: function (options) {
    // 检查用户是否已登录
    const db = wx.cloud.database()
    wx.cloud.callFunction({
      name: 'login',
      success: res => {
        // 查询数据库中是否已有该用户
        db.collection('users').where({
          openid: res.result.openid
        }).get().then(dbRes => {
          if (dbRes.data.length > 0) {
            this.setData({
              userInfo: dbRes.data[0],
              hasUserInfo: true
            })
          }
        })
      }
    })
  },

  // 选择头像
  onChooseAvatar(e) {
    const { avatarUrl } = e.detail
    this.setData({
      tempAvatar: avatarUrl
    })
    wx.showToast({
      title: '头像已选择',
      icon: 'success'
    })
  },

  // 输入昵称
  onInputNickname(e) {
    this.setData({
      tempNickName: e.detail.value
    })
  },

  // 昵称审核回调
  onNickNameReview(e) {
    console.log('昵称审核结果', e.detail)
  },

  // 确认登录
  confirmLogin() {
    if (!this.data.tempAvatar) {
      wx.showToast({
        title: '请选择头像',
        icon: 'error'
      })
      return
    }

    if (!this.data.tempNickName) {
      wx.showToast({
        title: '请输入昵称',
        icon: 'error'
      })
      return
    }

    wx.showLoading({
      title: '登录中...'
    })

    wx.cloud.callFunction({
      name: 'login',
      success: loginRes => {
        console.log('云函数登录成功', loginRes)
        if (loginRes.result.error) {
          console.error('云函数返回错误:', loginRes.result.error)
          this.loginFail(new Error(loginRes.result.error))
          return
        }

        const db = wx.cloud.database()
        const userInfo = {
          nickName: this.data.tempNickName,
          avatarUrl: this.data.tempAvatar,
          openid: loginRes.result.openid
        }
        
        // 检查用户是否已存在
        db.collection('users').where({
          openid: loginRes.result.openid
        }).get().then(checkRes => {
          if (checkRes.data.length > 0) {
            // 更新已存在的用户信息
            db.collection('users').doc(checkRes.data[0]._id).update({
              data: {
                nickName: userInfo.nickName,
                avatarUrl: userInfo.avatarUrl,
                updateTime: db.serverDate()
              }
            }).then(() => {
              this.loginSuccess(userInfo)
            }).catch(err => {
              console.error('更新用户信息失败', err)
              this.loginFail(err)
            })
          } else {
            // 添加新用户
            db.collection('users').add({
              data: {
                ...userInfo,
                createTime: db.serverDate()
              }
            }).then(() => {
              this.loginSuccess(userInfo)
            }).catch(err => {
              console.error('添加新用户失败', err)
              this.loginFail(err)
            })
          }
        }).catch(err => {
          console.error('查询用户失败', err)
          this.loginFail(err)
        })
      },
      fail: err => {
        console.error('调用云函数失败', err)
        this.loginFail(err)
      }
    })
  },

  // 重新设置
  reauthorize() {
    this.setData({
      hasUserInfo: false,
      userInfo: null,
      tempAvatar: '',
      tempNickName: ''
    })
  },

  // 登录成功处理
  loginSuccess(userInfo) {
    wx.hideLoading()
    this.setData({
      userInfo: userInfo,
      hasUserInfo: true,
      tempAvatar: '',
      tempNickName: ''
    })
    wx.showToast({
      title: '登录成功',
      icon: 'success'
    })
  },

  // 登录失败处理
  loginFail(err) {
    console.error('登录失败', err)
    wx.hideLoading()
    wx.showToast({
      title: err.message || '登录失败',
      icon: 'error'
    })
  }
})

5. 云函数实现(cloudfunctions/login/index.js)

// 云函数入口文件
const cloud = require('wx-server-sdk')

cloud.init({
  env: cloud.DYNAMIC_CURRENT_ENV
})

// 云函数入口函数
exports.main = async (event, context) => {
  try {
    const wxContext = cloud.getWXContext()
    
    return {
      event,
      openid: wxContext.OPENID,
      appid: wxContext.APPID,
      unionid: wxContext.UNIONID,
    }
  } catch (err) {
    console.error(err)
    return {
      error: err.message
    }
  }
}

6. 云函数实现(cloudfunctions/login/package.json)

{
  "name": "login",
  "version": "1.0.0",
  "description": "微信登录云函数",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "wx-server-sdk": "~2.6.3"
  }
}

记得上传云函数哈!

这样就可以实现小程序链接云开发微信登录了

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐