今天学习了下小程序,感觉与web开发差别不大,如果学过vue react angluar等mvvm框架的话,基本无门槛,只需要熟悉一下微信小程序的开发模式和api即可;

路由有点区别,需要注意下看看API。

本地测试时是没有https的,需要在开发工具右上角详情里勾选不校验..以及HTTPS证书。有时候需要真机测试,但是自己把后台上传到公网太麻烦,这里安利一个内外网穿透工具,直接将本地ip:port映射成一个临时公网地址,虽然这个临时公网地址比较慢,但是足够了,很方便

效果图:

今天学习的demo结构如下

1、app.js

//app.js
App({onLaunch: function () {// 展示本地存储能力var logs = wx.getStorageSync('logs') || []logs.unshift(Date.now())wx.setStorageSync('logs', logs)},globalData: {userInfo: null,//用于存储用户信息token: null//后台用户凭证,一般就是openId或openId+unionId},onPageNotFound(res) {wx.redirectTo({url: 'pages/notFound/notFound'}) // 如果是 tabbar 页面,请使用 wx.switchTab},serverUrl: 'http://3s.dkys.org:16912/wxapp'
})

2、index

index.wxml

<!--index.wxml-->
<view class="container"><view wx:if="{{userInfo==null}}"><button wx:if="{{canIUse}}" open-type="getUserInfo" bindgetuserinfo="bindGetUserInfo">授权登录</button><view wx:else>请升级微信版本</view></view><view wx:if="{{userInfo!=null&&token==null}}"><button bindtap='regist'>请先注册</button>  </view><view wx:if="{{userInfo!=null&&token!=null}}" class="userinfo"><image class="userinfo-avatar" src="{{userInfo.avatarUrl}}" mode="cover"></image><text class="userinfo-nickname">{{userInfo.nickName}}</text><text class="userinfo-nickname">性别:{{userInfo.gender === 1 ? '男':'女'}}</text><text class="userinfo-nickname">城市:{{userInfo.city}}</text><text class="userinfo-nickname">省份:{{userInfo.province}}</text><text class="userinfo-nickname">国家:{{userInfo.country}}</text><text class="userinfo-nickname">使用语言:{{userInfo.language}}</text></view>
</view>

index.wxss

/**index.wxss**/
.userinfo {display: flex;flex-direction: column;align-items: center;
}.userinfo-avatar {width: 128rpx;height: 128rpx;margin: 20rpx;border-radius: 50%;
}.userinfo-nickname {color: #aaa;
}

index.js

//index.js
//获取应用实例
const app = getApp()Page({data: {userInfo: app.globalData.userInfo,token: app.globalData.token,//判断小程序的API,回调,参数,组件等是否在当前版本可用。canIUse: wx.canIUse('button.open-type.getUserInfo')},bindGetUserInfo: function(){var index = this;wx.getUserInfo({success: function (res) {app.globalData.userInfo = res.userInfo;index.setData({userInfo: res.userInfo,});index.checkToken();}});},onLoad: function () {console.log("index onLoad");// 查看是否授权this.checkAuth(); },regist: function(){wx.navigateTo({url: '../regist/regist'})},checkAuth: function(){console.log("index checkAuth");var index = this;wx.getSetting({success: function (res) {if (res.authSetting['scope.userInfo']) {wx.getUserInfo({success: function (res) {//用户已经授权过app.globalData.userInfo = res.userInfo;index.setData({userInfo: res.userInfo,});index.checkToken();}})}}})},checkToken: function(){var index = this;console.log("index checkToken");//初次登录需要验证token,即后台查不到这个openId的用户,就表示没有这个用户,跳到注册页面if (app.globalData.token == "" || app.globalData.token == null) {//验证用户信息wx.login({success: res => {// 发送 res.code 到后台换取 openId, sessionKey, unionIdif (res.code) {app.globalData.code = res.code;// 发送请求,服务端能获取到openid和unionid,之前登录过则可以获取到之前的用户信息。wx.request({url: app.serverUrl + '/login?code=' + res.code, //请求路径method: "GET",success: function (res) {//转到注册if (res.data == "") {wx.navigateTo({url: '../regist/regist'})} else {app.globalData.token = res.data;index.setData({token: app.globalData.token});}}})}}})} else {//有token就不管了console.log("用户验证通过");this.setData({token: app.globalData.token,userInfo: app.globalData.userInfo});console.log(app.globalData.token);console.log(app.globalData.userInfo);}},onShow:function(){console.log("show index");}
})

2、regist

regist.wxml

<view wx:if="{{!success}}"><view class='row'><view class='center'>您还未注册,请先注册!</view><view class='info'><input  class= 'info-input1' bindinput="handleInputPhone" placeholder="请输入你的手机号" /></view><button class='button' bindtap='doGetCode' disabled='{{disabled}}' style="background-color:{{color}}" >{{text}}</button></view><view class='row'><view class='info'><input  class= 'info-input' bindinput="handleVerificationCode" placeholder="请输入你的验证码" /></view></view><view class='row'><view class='info'><input type='password' class= 'info-input' bindinput="handleNewChanges" placeholder="请输入你的密码" /></view></view><view class='row'><view class='info'><input  type='password' class= 'info-input' bindinput="handleNewChangesAgain" placeholder="请重新输入你的密码" /></view></view><button class='submit' bindtap='submit'>提交</button></view><view class = 'success' wx:if="{{success}}"><view class='cheer'><icon type="success" size="24"/> 恭喜您注册成功!</view><button type = "default" class = 'return' bindtap='return_home'>返回首页</button></view>

regist.wxss

page{background: #F0F0F0 ;
}
.row{margin-top: 20rpx;overflow: hidden;line-height: 100rpx;border-bottom: 1rpx solid #ccc;margin-left: 20rpx;margin-right: 20rpx;color: #777;background: #fff;}
.center{text-align: center;background-color: gainsboro
}
.info-input{height: 100rpx;margin-left: 50rpx;color: #777;float: left;
}
.info-input1{height: 100rpx;margin-left: 50rpx;color: #777;float: left;width: 420rpx;
}
.button{width: 200rpx;height: 70rpx;line-height: 70rpx;font-size: 28rpx;background: #33FF99;float: left;margin-left: 10rpx;margin-top: 15rpx;color: #FFFFFF;
}
.submit{margin-top: 50rpx;margin-left: 20rpx;margin-right: 20rpx;background: #00CCFF;color: #FFFFFF;
}
.success{background: #ffffff;}
.cheer{text-align: center;line-height: 400rpx;font-size: 60rpx;position: relative;
}
.return{margin: 20rpx;}

regist.js

var app = getApp();
Page({/*** 页面的初始数据*/data: {text: '获取验证码', //按钮文字currentTime: 61, //倒计时disabled: false, //按钮是否禁用phone: '', //获取到的手机栏中的值VerificationCode: '',Code: '',NewChanges: '',NewChangesAgain: '',success: false,state: ''},/*** 获取验证码*/return_home: function (e) {wx.navigateTo({url: '../index/index',})},handleInputPhone: function (e) {this.setData({phone: e.detail.value})},handleVerificationCode: function (e) {console.log(e);this.setData({Code: e.detail.value})},handleNewChanges: function (e) {console.log(e);this.setData({NewChanges: e.detail.value})},handleNewChangesAgain: function (e) {console.log(e);this.setData({NewChangesAgain: e.detail.value})},doGetCode: function () {var that = this;that.setData({disabled: true, //只要点击了按钮就让按钮禁用 (避免正常情况下多次触发定时器事件)color: '#ccc',})var phone = that.data.phone;var currentTime = that.data.currentTime //把手机号跟倒计时值变例成js值var warn = null; //warn为当手机号为空或格式不正确时提示用户的文字,默认为空var phone = that.data.phone;var currentTime = that.data.currentTime //把手机号跟倒计时值变例成js值var warn = null; //warn为当手机号为空或格式不正确时提示用户的文字,默认为空wx.request({url: app.serverUrl + '/checkPhoneRegist?phone=' + phone, //后端判断是否已被注册, 已被注册返回1 ,未被注册返回0method: "GET",header: {'content-type': 'application/x-www-form-urlencoded'},success: function (res) {that.setData({state: res.data})if (phone == '') {warn = "号码不能为空";} else if (phone.trim().length != 11 || !/^1[3|4|5|6|7|8|9]\d{9}$/.test(phone)) {warn = "手机号格式不正确";} //手机号已被注册提示信息else if (that.data.state == 1) {  //判断是否被注册warn = "手机号已被注册";}else {wx.request({url: app.serverUrl + '/sendCodeMsg?phone=' + phone, //填写发送验证码接口method: "POST",data: {coachid: that.data.phone},header: {'content-type': 'application/x-www-form-urlencoded'},success: function (res) {console.log(res.data)that.setData({VerificationCode: res.data})//当手机号正确的时候提示用户短信验证码已经发送wx.showToast({title: '短信验证码已发送',icon: 'none',duration: 2000});//设置一分钟的倒计时var interval = setInterval(function () {currentTime--; //每执行一次让倒计时秒数减一that.setData({text: currentTime + 's', //按钮文字变成倒计时对应秒数})//如果当秒数小于等于0时 停止计时器 且按钮文字变成重新发送 且按钮变成可用状态 倒计时的秒数也要恢复成默认秒数 即让获取验证码的按钮恢复到初始化状态只改变按钮文字if (currentTime <= 0) {clearInterval(interval)that.setData({text: '重新发送',currentTime: 61,disabled: false,color: '#33FF99'})}}, 100);}})};//判断 当提示错误信息文字不为空 即手机号输入有问题时提示用户错误信息 并且提示完之后一定要让按钮为可用状态 因为点击按钮时设置了只要点击了按钮就让按钮禁用的情况if (warn != null) {wx.showModal({title: '提示',content: warn})that.setData({disabled: false,color: '#33FF99'})return;}}})},submit: function (e) {var that = thisif (this.data.Code == '') {wx.showToast({title: '请输入验证码',image: '/images/error.png',duration: 2000})return} else if (this.data.Code != this.data.VerificationCode) {wx.showToast({title: '验证码错误',image: '/images/error.png',duration: 2000})return}else if (this.data.NewChanges == '') {wx.showToast({title: '请输入密码',image: '/images/error.png',duration: 2000})return} else if (this.data.NewChangesAgain != this.data.NewChanges) {wx.showToast({title: '两次密码不一致',image: '/images/error.png',duration: 2000})return} else {var that = thisvar phone = that.data.phone;wx.login({success: res => {// 发送 res.code 到后台换取 openId, sessionKey, unionIdif (res.code) {wx.request({url: app.serverUrl + '/regist',method: "POST",data: {phone: phone,passwd: that.data.NewChanges,avatarUrl: app.globalData.userInfo.avatarUrl,nickName: app.globalData.userInfo.nickName,gender: app.globalData.userInfo.gender,city: app.globalData.userInfo.city,province: app.globalData.userInfo.province,country: app.globalData.userInfo.country,language: app.globalData.userInfo.language,code: res.code},header: {"content-type": "application/x-www-form-urlencoded"},success: function (res) {app.globalData.token = res.data;wx.showToast({title: '提交成功~',icon: 'loading',duration: 2000});that.setData({success: true})}})}}});}},/*** 用户点击右上角分享*/onShareAppMessage: function () {}
})

后台controller代码

@Slf4j
@RestController
@RequestMapping("/wxapp")
public class WxLoginController extends BaseController {/*** 假数据*/private static Map<String, WxUser> users = new HashMap<>();@Value("${weixin.secret.appid}")private String appid;@Value("${weixin.secret.key}")private String key;/*** 登录,验证数据库用户信息* <p>Title: login</p>  * <p>Description: </p>  * @param code* @return*/@RequestMapping("/login")@ResponseBodypublic String login(String code){String url = "https://api.weixin.qq.com/sns/jscode2session?" +"appid=" + appid +"&secret=" + key +"&js_code=" + code +"&grant_type=authorization_code";JSONObject object = JSONObject.parseObject(HttpClientUtils.sendGet(url));log.info(object.toJSONString());// 请求,获取openid或unionid// 从数据库中查询是否存储// 成功获取String unionid = (String) object.get("unionid");String openid = (String) object.get("openid");//用户唯一标识String token = openid+"-" + unionid;//通过token查找user,找到说明已注册,否则提示请注册boolean findUser = false;if(users.get(token)!=null) {findUser = true;}if(findUser) {return token;}else {return "";}}/*** 判断号码是否已被注册, 已被注册返回1 ,未被注册返回0* <p>Title: checkPhoneRegist</p>  * <p>Description: </p>  * @param phone* @return*/@RequestMapping("/checkPhoneRegist")@ResponseBodypublic Integer checkPhoneRegist(String phone){Integer result = 0;log.info("判断号码是否已被注册:"+phone+",结果:"+result);return result;}/*** 发送验证码* <p>Title: sendCodeMsg</p>  * <p>Description: </p>  * @param phone*/@RequestMapping("/sendCodeMsg")@ResponseBodypublic String sendCodeMsg(String phone){String result = "123456";log.info("发送验证码到:"+phone+",验证码:"+result);return result;}@RequestMapping("/regist")@ResponseBodypublic String regist(WxUser user){String url = "https://api.weixin.qq.com/sns/jscode2session?" +"appid=" + appid +"&secret=" + key +"&js_code=" + user.getCode() +"&grant_type=authorization_code";JSONObject object = JSONObject.parseObject(HttpClientUtils.sendGet(url));// 请求,获取openid或unionid// 从数据库中查询是否存储// 成功获取String unionid = (String) object.get("unionid");String openid = (String) object.get("openid");//用户唯一标识String token = openid+"-" + unionid;user.setToken(token);log.info("注册用户:"+user.toString());users.put(token, user);return token;}
}

遇到的问题:

验证用户所发送的code需要实时获取,不然会出现code been used的错误

微信小程序学习(一):开发准备、授权与验证相关推荐

  1. 微信小程序学习:开发注意点

    11月2日更新: 微信小程序支持内嵌网页,新增 <web-view /> 组件调试支持: 传送门 <!-- wxml --> <!-- 指向微信公众平台首页的web-vi ...

  2. 微信小程序学习(1)-基础开发

    学习微信小程序 微信小程序学习(1) 微信小程序学习(2) 文章目录 学习微信小程序 注册和初始化 小程序配置 tabbar导航栏 模板插样与WXML 循环渲染 条件渲染 模板 微信小程序脚本WXS ...

  3. 小程序 pagescrollto_微信小程序学习笔记(三)-- 首页及详情页开发

    一.常用组件 在上一个章节中讲解了封装请求数据的模块,在此处请求轮播图的数据 1.首页轮播图数据的请求以及渲染 1.1 轮播图数据的请求 pages/home/home.js import 2 使用组 ...

  4. 微信小程序实现lot开发01 学习微信小程序 helloworld

    最近走进一个新项目的任务里,主要的任务是实现用微信小程序利用websocket使用mqtt协议走网络控制继电器(其实在生活中这个技术已经普及了,我们用的充电桩扫码充电,我们学校里的饮水机扫码接水以及我 ...

  5. 微信小程序学习2:开发工具快速创建页面(pages)的四个文件(.js,.json,.wxml, .wxss)

    微信小程序学习2:开发工具快速创建页面(pages)的四个文件(.js,.json,.wxml, .wxss) [1]首先在pages文件夹下创建一个页面文件夹,比如我打算创建个人中心,我创建一个ho ...

  6. 微信小程序学习(2)-云开发

    微信小程序-云开发 微信小程序学习(1) 微信小程序学习(2) 文章目录 微信小程序-云开发 初始化+云函数的坑 云函数使用 老陈段子 小程序云存储 小程序云数据库 初始化插入 简单查询 更新数据 删 ...

  7. 上拉加载更多后台数据_6-7【微信小程序全栈开发课程】记录页面(七)--分页加载记录数据...

    现在是一次性加载所有的记录数据,数据多的时候,会加载比较慢,所以我们改成分页加载,一次最多加载15条数据 每次拉倒底部都会自动加载下一页的数据,知道所有的数据加载完成 1.添加data变量 编辑rec ...

  8. 3.2【微信小程序全栈开发课程】登录功能(一)--实现登录功能

    在本地搭建好后端环境之后,我们来实现登录功能 1.安装SDK插件 SDK插件用来获取用户的openId SDK是server端(也就是后端)的插件,帮助我们很容易的获取openId.openId是微信 ...

  9. 微信小程序学习笔记(1)

    微信小程序学习笔记 1.小程序代码结构 2.逻辑层和视图层 3. 小程序的宿主环境(通信模型.运行机制.组件.API) 4. 数据绑定和事件绑定 1.小程序代码结构 当开发者新建一个工程时,项目文件包 ...

  10. 微信小程序学习之路(一)

    微信小程序学习之路(一) 1.前言 2.准备工作 (1).IDE的选择 (2).微信小程序的文件说明 (3)开发者用户注册 3.编写 (1).新建项目 (2).代码的编写 3.代码的发布以及审核 1. ...

最新文章

  1. 知道了05后的隐藏技能之后,我酸了…​
  2. 2017级面向对象程序设计——团队作业1
  3. javascript学习系列(16):数组中的every方法
  4. document.write() 和writeln()方法注意事项
  5. Selenium WebDriver架构
  6. SSMS 2005 连接 SQL SERVER 2008问题
  7. 每周荐书:Web扫描、HTML 5、Python(评论送书)
  8. flink的jar包和服务器的包冲突解决方案
  9. 遭遇nat.exe,socks.exe,USP10.dll,BOSC.dll,kb080387.CNT,~ctwxw.txt等1
  10. DEDE源码分析与学习--index.php文件解读
  11. C语言中的取绝对值函数
  12. C++ qt实现打开关闭状态按钮
  13. iia期是第几期_IIa期和IIb期的定义,区别
  14. java宠物医院,基于SSM框架的JAVA宠物医院管理信息系统,源码分享
  15. echarts最新版做中国地图(详细版+避雷版)
  16. 计算机组成原理(王道精讲课 + 天勤高分笔记) note
  17. 用户故事地图学习笔记(四):如何创建用户故事地图
  18. 什么情况,听说网络安全工程师已经没落?
  19. android是否支持 ipv6,判断你的网络是否支持IPv6
  20. 抢占智能制造“制高点” 个性化生产不可或缺

热门文章

  1. 健康程序员:五分钟与鼠标手说再见
  2. 手机照片局部放大镜_想让旅行照与众不同?堪比PS的手机修图神器了解一下!...
  3. 国产deepin深度操作系统20.2.3 发布
  4. AndroidStudio运行app,会装上多个app
  5. MySQL编码致使varchar类型不区分大小写
  6. qt show widget_Qt Widget不显示
  7. Python实现发送邮件(实现单发/群发邮件验证码)
  8. ​PBlaze6上新!Memblaze发布首款基于长存颗粒的企业级SSD
  9. 【36C++STL-常用容器----5、stack容器详解】
  10. dnf手游服务器维护时效,DNF手游延期到2021年2月11日是真的吗 延期日期详细说明...