一,元素的使用
1,分析文件
app.json
,全局配置文件app.wxss
,全局样式文件
在app.json
文件中进行全局配置,大体配置如下
pages
中括号中的内容是页面路径,window
中的内容是对小程序最上面的内容进行设置,tabBar
是最下面的导航栏切换,iconPath
是点击之前的图标,selectedIconPath
是点击之后的图标。
注意:pagePath
的路径一定要和上面pages
中的路径保持一致。
{
"pages": [
"pages/mine/mine",
"pages/home/home",
"pages/index/index",
"pages/logs/logs"
],
"window": {
"backgroundTextStyle": "dark",
"navigationBarBackgroundColor": "#0094ff",
"navigationBarTitleText": "垃圾分类系统",
"navigationBarTextStyle": "white",
//开启全局下拉刷新功能
"enablePullDownRefresh":true
},
"tabBar": {
"list": [{
"pagePath": "pages/home/home",
"text": "首页",
"iconPath": "icon/定位.png",
"selectedIconPath": "icon/定位.png"
}, {
"pagePath": "pages/mine/mine",
"text": "我的",
"iconPath": "icon/垃圾桶.png",
"selectedIconPath": "icon/垃圾桶.png"
}]
},
"usingComponents": {},
"style": "v2",
"sitemapLocation": "sitemap.json"
}
2,如何快速新建页面?
直接在上面的全局配置文件app.json
的pages
中添加一个路径并保存,微信开发者工具会自动生成一套页面模板。
生成的一套页面中,home.js
写方法,home.json
文件写配置,home.wxml
文件写页面(元素),home.wxss
文件写样式。
3,行级元素text
和块级元素view
text
元素相当于html
的span
标签,属于行级元素,而view
元素相当于html
的div
标签,属于块级元素。
使用方法如下
<!-- text标签相当于span,行内元素,view标签相当于div,块元素 -->
<!-- selectable 文字长按复制 -->
<text selectable="true">哈哈</text><text>嘿嘿</text>
<view>你好</view><view>我不好</view>
4,复选框checkbox
<!-- check="true" 勾选 -->
<checkbox checked="{{ischeck}}">复选框</checkbox>
5,运算
<!-- 运算 -->
<view>{{1+1}}</view>
<view>{{\'1\'+\'1\'}}</view>
<view>{{10%2==0 ? \'偶数\' : \'奇数\'}}</view>
6,列表循环
首先在js
文件的data
中写一个list
数组
/**
* 页面的初始数据
*/
data: {
msg:"微信小程序,你好!",
ischeck:false,
gender:"",
list:[
{
id:1,
name:"孙悟空"
},
{
id:2,
name:"唐僧"
}
],
obj: {
id:3,
name:"蜘蛛侠"
}
}
然后在wxml
文件中这样循环取值
<!-- 列表循环 -->
<view wx:for="{{list}}" wx:for-item="item" wx:for-index="index" wx:key="id">
索引:{{index}}
值:{{item.name}}
</view>
7,对象循环
首先在js
文件的data
中写一个对象
obj: {
id:3,
name:"蜘蛛侠"
}
然后在wxml
文件中这样循环取值
<!-- 对象循环 -->
<view wx:for="{{obj}}" wx:for-item="value" wx:for-index="key" wx:key="id">
属性:{{key}}
值:{{value}}
8,数据绑定
在js
文件中初始化数据。
/**
* 页面的初始数据
*/
data: {
msg:"微信小程序,你好!",
ischeck:false,
gender:"",
---省略
在wxml
文件中绑定数据
<view>{{msg}}</view>
9,条件渲染if
,else
,elif
<!-- 条件渲染 -->
<view wx:if="{{ischeck}}">登录</view>
<!-- <view wx:elif="true">if else</view> -->
<view wx:else>已登录,退出</view>
隐藏元素
<!-- 隐藏元素 -->
<view hidden="{{true}}">哈哈</view>
10,绑定事件
通过bindtap
进行绑定点击事件。
-
handleInput
:input改变触发 -
bindtap
:点击触发
然后在js
文件中写触发事件
handleInput(e){
console.log(e.detail.value)
},
//触发事件弹出小弹框
login(){
wx.showToast({
title: \'登录成功\',
icon: \'succes\',
duration: 1000,
mask:true
})
11,button
的开发能力
contact 直接打开客服对话功能 需要在微信小程序的后台配置share 转发当前的小程序到微信朋友中getPhoneNumber 获取当前用户的手机号码信息绑定一个事件 bindgetphonenumber 回调函数中通过参数获取手机号(已被加密的,需到后台进行解密)getUserInfo 获取当前用户的个人信息绑定一个事件 bindgetuserinfo 回调函数中通过参数获取用户个人信息,没有加密launchApp 在小程序中直接打开appopenSetting 打开小程序内置的授权页面feedback 打开小程序内置的意见反馈页面
设置按钮的类型,可以触发上面各种情况。
<button open-type="contact">直接打开客服对话功能</button>
<button open-type="share">转发当前的小程序到微信朋友中</button>
<button open-type="getPhoneNumber" bindgetphonenumber="getPhone">获取当前用户的手机号码信息</button>
<button open-type="getUserInfo" bindgetuserinfo="getUser">获取当前用户的个人信息</button>
<button open-type="openSetting">打开小程序内置的授权页面</button>
重要:getPhoneNumber
获取当前用户的手机号码信息以及getUserInfo
获取当前用户的个人信息的使用方法很特殊,需要两个触发事件,如上,触发事件如下。
getPhone(e){
console.log(e)
},
getUser(e){
console.log(e.detail.userInfo.nickName)
},
12,单选框radio
现在实现一个效果,选择一个单选框,并把单选框的value
值设置到text
标签中进行显示。
bindchange
是触发单选的事件。
<!-- 单选框 -->
<radio-group bindchange="change">
<radio value="male">男</radio>
<radio value="female">女</radio>
</radio-group>
<text>您选中的是{{gender}}</text>
js
文件的方法
change(e){
console.log(e.detail.value);
const val=e.detail.value;
// 把值赋给data中的变量
this.setData({gender:val})
},
前提是数据要先在data
中进行初始化。
13,点击事件传递参数
首先 bindtap
绑定点击事件,
在标签中利用 data-xxx
来定义你要传入的参数,,
然后事件中传入event
用 event.currentTarget.dataset.xxx
来取你传入的值
14,搜索框input
回车搜索事件
在微信小程序里的搜索框,按软键盘回车键触发搜索事件。
<input type="text" placeholder="搜索" value="{{inputVal}}" bindinput="inputTyping" bindconfirm="search" />
bindconfirm
即为回车事件,为它绑定上需要触发的事件,search
是对应的搜索方法。
15,列表双重循环
使用场景:遍历朋友圈(包括每条朋友圈下面的评论列表)
//#################第一次循环,遍历朋友圈的全部说说列表####################
<view class="haha" wx:for="{{list}}" wx:for-item="item" wx:for-index="index" wx:key="id">
<view class="content">
<view class="top">
<!-- 头像 -->
<view class="touxiang"><image src="{{item.txurl}}"></image></view>
<!-- 网名 -->
<view class="mingzi">{{item.name}}</view>
</view>
<!-- 朋友圈内容 -->
<view class="bottom">{{item.content}}</view>
<!-- 赞和评论区 -->
<view class="zan">
<view bindtap="gopinglun" data-s="{{item.id}}" class="zancolors iconfont icon-pinglun"><text>{{item.pingluncount}}</text></view>
<view bindtap="gozan" data-s="{{item.id}}" class="zancolor iconfont icon-praise_icon"><text>{{item.zan_num}}</text></view>
</view>
<view class="pl"><input bindinput="sdsm" placeholder="说点儿什么..."></input><view class="but" bindtap="fbpl" data-s="{{item.id}}">评论</view></view>
//###############第二次循环,对第一次循环的pinglunlist值再次进行二次循环,展示该说说的评论列表###############
<view class="haha" wx:for="{{item.pinglunlist}}" wx:for-item="items" wx:for-index="index" wx:key="id">
<view class="plcontent"><view class="plr">{{items.name}}</view>:{{items.content}}</view>
</view>
</view>
</view>
二,组件的使用
1,轮播图swiper
这里提一点,微信小程序中的像素是 rpx,750rpx 是屏幕的宽, rpx是自适应的,也就是不管使用多大的手机,750rpx总是宽度 100%,其实 px 也可以用,但是不是自适应。
在微信小程序中的轮播图其实都已经被微信封装好了,直接使用swiper
标签即可,建议图片使用图床上的图片路径。
swiper高度是要计算的原图高度 352px 原图宽度 1125pxswiper高度 / swiper宽度 = 原图高度 / 原图宽度swiper高度=swiper宽度 * 原图高度 / 原图宽度height: 100vw * 352 / 1125autoplay 自动播放circular 是否循环轮播 true/falseinterval 切换时间间隔indicator-dots true/false 面板指示点
<swiper autoplay indicator-dots="3">
<swiper-item><image mode="widthFix" src="<https://gw.alicdn.com/imgextra/i2/36/O1CN01bTCE2O1C8Wq7EmzdX_>!!36-0-lubanu.jpg"></image></swiper-item>
<swiper-item><image mode="widthFix" src="<https://gw.alicdn.com/imgextra/i1/1424178/O1CN01V9rmMw1gjZLRryx44_>!!1424178-0-lubanu.jpg"></image></swiper-item>
<swiper-item><image mode="widthFix" src="<https://gw.alicdn.com/imgextra/i2/47/O1CN01J3Qecu1CDZCD6R6VD_>!!47-0-lubanu.jpg"></image></swiper-item>
</swiper>
这样,一个轮播图就出来了,但是图片的宽高和swiper
的宽高不一致,所以高度需要计算,计算方法如上。
因此在wxss
样式文件中写样式。
image{
width: 100%;
height: 100%;
}
swiper{
width: 100%;
height: calc(100vw * 352 / 1125);
}
2,跳转页面navigator
open-type 跳转的方式1,navigate 默认值 保留当前页面,跳转到应用内的某个页面,但不能跳tabbar页面2, redirect 关闭当前页面,跳转到应用内的某个页面,但不能跳tabbar页面3,switchTab 跳转到TabBar页面,并关闭所有非tabbar页面4,reLaunch 关闭所有页面,打开到应用内的某个页面5,navigateBack 返回上一级页面,关闭当前页面6,退出小程序
<navigator open-type="navigate" url="/pages/index/index">跳转页面</navigator>
js
跳转页面并传值
下面是一个普通按钮触发事件,让它跳转页面并传值。
denglu(){
const index="测试参数";
wx.navigateTo({
url: \'../logs/logs?params=\'+index
})
}
然后在logs.js
文件中接收参数
这里接收到参数之后,可以发起ajax
请求列表数据,在data
中初始化一个数组,然后把请求到的数据通过 this.setData
赋值给数组,然后在页面让元素进行遍历,显示数据。
//声明周期函数,监听页面加载
onLoad: function (options) {
console.log(options.params)
}
3,新建组件和页面引用组件的方式
组件的作用在于多页面复用,举个例子,下面蓝色区域可以抽出来一个组件,多页面公用一个头部。(原创,分类,关于导航栏, 切换不同页面,导航栏公用)
创建组件的方法,在微信开发者工具中右键新建component
,然后就创建出来了一套组件模板。
该组件名字就叫Tabs
。
比如home.wxml
页面要引用该组件,就在home.json
文件中配置组件。
{
"usingComponents": {
"Tabs":"../../components/Tabs/Tabs"
}
}
然后在home.wxml
最上面(因为要把组件放最上面啊)写组件标签引入组件。
<Tabs></Tabs>
这样该页面就有这个组件了。
4,写一个导航栏的组件
首先到Tabs.js
组件的js
文件中初始化导航栏信息
/**
* 组件的初始数据
*/
data: {
list:[
{
id:1,
name:"原创",
isActive:true
},
{
id:2,
name:"分类",
isActive:false
},
{
id:3,
name:"关于",
isActive:false
}
]
},
然后在Tabs.wxml
文件中遍历循环取值。
bindtap="handle" 绑定点击事件
<view class="tabs">
<view class="tabs_title">
<view wx:for="{{list}}" wx:for-item="item" wx:for-index="index" wx:key="id" class="tabs_item {{item.isActive?\'hover\':\'\'}}" bindtap="handle" data-index="{{index}}">{{item.name}}</view>
</view>
<view class="tabs_content">{{aaa}}</view>
</view>
Tabs.wxss
样式
.tabs_title{
display: flex;
padding: 10rpx;
}
.tabs_item{
flex: 1;
display: flex;
/* 文字左右对齐 */
justify-content: center;
/* 上下对齐 */
align-items: center;
}
/*点击之后的样式*/
.hover{
border-bottom: 5rpx solid red;
}
然后动态切换导航栏组件,更改对应的样式(下边框变红)
在组件中的事件方法一定要写在组件js
文件的methods
里面。
methods: {
handle(e){
console.log(e)
//获取索引
const index=e.currentTarget.dataset.index;
//获取data数组
let list=this.data.list;
//循环数组并设置值
list.forEach((v,i)=>i===index?v.isActive=true:v.isActive=false);
this.setData(
{list}
)
}
}
5,父组件向子组件传值
父组件,也就是页面,子组件就是使用的组件了。
现在,是home.wxml
页面使用Tabs
组件,那么home.wxml
页面怎么把一个值传给Tabs
组件呢?
首先,在父组件(页面)的组件标签中添加一个自定义属性
然后在组件的Tabs.js
文件中接收参数值
/**
* 组件的属性列表
* 接收父组件的传值
*/
properties: {
aaa:{
type:String, //参数数据类型
value:"" //默认值
}
},
然后在组件的Tabs.wxml
中写标签绑定数据(取数据)
{{aaa}}
6,子向父传递数据
在组件的元素中绑定点击事件,并给父组件(页面)传递参数。
这里就还说上面的导航栏的组件吧。
上面的组件是遍历出来的,现在实现点击每个导航按钮,传递index
索引值给父组件(页面)
<view wx:for="{{list}}" wx:for-item="item" wx:for-index="index" wx:key="id" class="tabs_item {{item.isActive?\'hover\':\'\'}}" bindtap="handle" data-index="{{index}}">{{item.name}}</view>
触发方法,在组件的js
中
/**
* 组件的方法列表
*/
methods: {
handle(e){
// console.log(e)
//获取被点击导航按钮的索引
const index=e.currentTarget.dataset.index;
this.triggerEvent("methodName",index);
}
this.triggerEvent("methodName",index);
然后在父组件的wxml
页面的组件标签中绑定事件
不是bindmethodName
事件,而是bind
拼接上methodName
,然后在页面的js
文件写bindmethodName
事件方法。
<Tabs bindmethodName="bindmethodName"></Tabs>
bindmethodName(e){
console.log(e.detail)
}
控制台就打印出了传递过来的索引值了。
7,导航栏切换页面slot
标签
在上面写好的组件中加入slot
标签,这个标签编译后不会显示出来,会被替换掉。
<view class="tabs">
<view class="tabs_title">
<view wx:for="{{list}}" wx:for-item="item" wx:for-index="index" wx:key="id" class="tabs_item {{item.isActive?\'hover\':\'\'}}" bindtap="handle" data-index="{{index}}">{{item.name}}</view>
</view>
<slot></slot>
</view>
这里就利用上面 子向父传递数据 的索引了,页面拿到组件传过来的索引值,然后进行判断赋值。
首先在页面的js
文件初始化三个boolean
类型。
index1:true,
index2:false,
index3:false,
然后在页面js
的获取索引的方法中判断赋值
bindmethodName(e){
console.log(e.detail);
const dex=e.detail;
if(dex==0){
this.setData({index1:true,index2:false,index3:false})
}else if(dex==1){
this.setData({index1:false,index2:true,index3:false})
}else if(dex==2){
this.setData({index1:false,index2:false,index3:true})
}
},
然后回到页面的home.wxml
文件的组件标签里面,添加几个block
标签(你有几个导航分类就添加几个block
标签)
使用条件渲染,切换效果就完成了。
在block
标签中再写自己想要展示的页面即可。
<Tabs bindmethodName="bindmethodName">
<block wx:if="{{index1}}">1</block>
<block wx:elif="{{index2}}">2</block>
<block wx:else>3</block>
</Tabs>
8,页面生命周期函数
/**
* 生命周期函数--监听页面加载
*/
onLoad: function () {},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady: function () {},
/**
* 生命周期函数--监听页面显示
*/
onShow: function () {},
/**
* 生命周期函数--监听页面隐藏
*/
onHide: function () {},
/**
* 生命周期函数--监听页面卸载
*/
onUnload: function () {},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh: function () {},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom: function () {},
/**
* 用户点击右上角分享
*/
onShareAppMessage: function () {}
三,前后端分离
1,微信小程序的ajax
前端就是微信小程序的前端页面,后端则是springboot
项目,为微信小程序提供接口,springboot
使用idea
快速构建,过程就不说了,springboot
项目搭建起来之后前后端就可以连通了。
下面是我的springboot
项目的一个测试方法
@Controller
@RequestMapping("/test")
public class TestController {
@RequestMapping("/one")
@ResponseBody
public User one(){
User user=new User();
user.setId("18838030468");
user.setName("樊江锋");
user.setAge(24);
return user;
}
}
现在测试在微信小程序页面发起请求这个接口并拿到响应数据并回显在页面上。
mine.wxml
页面
<button bindtap="ask">发出请求</button>
<text>{{obj.id}}</text>
<text>{{obj.name}}</text>
<text>{{obj.age}}</text>
mine.js
文件写方法
首先把obj
对象初始化
/**
* 页面的初始数据
*/
data: {
obj:\'\'
},
ask(e){
//必须赋值,否则会报错
var th=this;
console.log("发出请求")
wx.request({
url: \'<http://localhost:8088/test/one>\',
method:\'POST\',
success(data){
console.log(data.data)
//把拿到的对象数据赋给obj
th.setData({
obj:data.data
})
}
})
},
然后obj
在页面进行回显(数据绑定嘛)
前提:在微信开发者工具要把请求放开,否则不行!
把这个勾选上就行了!
2,表单提交form
首先在微信小程序写一个表单,bindsubmit
是绑定一个提交表单的事件。
<form bindsubmit="formsubmit">
手机号:<input name="phone" placeholder="输入手机号"></input>
地址:<input name="address" placeholder="输入地址"></input>
<button form-type="submit">登录</button>
</form>
在js
文件中添加表单提交事件的方法
formsubmit(e){
console.log(e)
const phone=e.detail.value.phone;
const address=e.detail.value.address;
wx:wx.request({
url: \'<http://localhost:8088/test/one>\',
data: {\'phone\':phone,\'address\':address},
dataType: \'json\',
method: \'GET\',
success(data){
console.log(data)
}
})
},
会把参数传到后端接口(springboot
项目),然后就正常操作。
后端接口
@Controller
@RequestMapping("/test")
public class TestController {
@RequestMapping("/one")
@ResponseBody
public User one(@RequestParam("phone") String phone, @RequestParam("address") String address){
System.out.println("手机号:"+phone+" 地址:"+address);
User user=new User();
user.setId("18838030468");
user.setName("樊江锋");
user.setAge(24);
return user;
}
}
后端控制台打印
手机号:18838030468 地址:河南郑州
3,js
获取input
框的值
现在写一个不使用表单提交的方式来登录。
给input
框绑定bindinput
事件,该事件是每次输入input
框,都可以监听到内容。
手机号:<input bindinput="getValue" name="phone" placeholder="输入手机号"></input>
地址:<input bindinput="getAddress" name="address" placeholder="输入地址"></input>
<button bindtap="denglu">登录</button>
然后在js
文件中初始化数据
/**
* 页面的初始数据
*/
data: {
phone:\'\',
address:\'\'
}
然后写给input
框绑定的bindinput
事件来获取手机号值和地址值。
//获取手机号
getValue(e){
const phone=e.detail.value;
this.setData({
phone:phone
})
},
//获取地址
getAddress(e){
const address=e.detail.value;
this.setData({
address:address
})
},
//登录事件
denglu(){
console.log(this.data.phone+"#####"+this.data.address)
},
前台打印
4,使用缓存技术
在页面的js
文件中,页面一加载就执行的方法中使用缓存技术,如果请求的数据量很大,很影响用户体验。
onLoad: function () {
//去缓存拿存储的数据(键值对形式)
const data=wx.getStorageSync(\'catch\');
this.setData({
swiperList:data
})
//如果缓存数据为空,发送新的请求并把数据存储到缓存中
if(!data){
console.log("发出请求了")
wx:wx.request({
url: \'<https://api.zbztb.cn/api/public/v1/home/swiperdata>\',
success: (result) => {
this.setData({
swiperList:result.data.message,
//赋给初始化的缓存
Catch:result.data.message
})
//存入本地存储
wx.setStorageSync("catch",{time:Date.now(),data:this.Catch});
}
})
}
5,页面发送请求渲染页面显示加载中
微信小程序内置了加载中的效果。
wx.showLoading ({}) 调出加载中wx.hideLoading () 隐藏加载中效果
onLoad: function () {
wx.showLoading({
title: \'加载中\',
})
wx:wx.request({
url: \'<https://api.zbztb.cn/api/public/v1/home/catitems>\',
success: (result) => {
console.log(result)
this.setData({
daohang:result.data.message
})
wx.hideLoading();
}
})
6,小程序登录功能
微信进入小程序时要进行授权登录,然后才能获取到用户的个人信息,才可以使用小程序。
这里使用缓存实现小程序登录。
首先新建一套login
登录页面,然后在login.wxml
写一个登录按钮,点击按钮触发login.js
的方法。
在login.json
文件中添加一个标题!
{"usingComponents": {},"navigationBarTitleText": "登录"}
按钮
open-type="getUserInfo" 是点击按钮获取个人信息,bindgetuserinfo是绑定的一个事件。
<button type="primary" plain open-type="getUserInfo" bindgetuserinfo="bindgetuserinfo">登录</button>
方法
bindgetuserinfo(e){
console.log(e)
//拿到用户信息
const userInfo=e.detail.userInfo;
//把用户信息放入缓存
wx:wx.setStorageSync(\'userInfo\', userInfo);
//返回上一层页面
wx:wx.navigateBack({
delta: 1
})
},
然后登录页面就写好了,因为小程序默认会进入首页,因此在首页进行判断是否登录
首先在index.js
的data
初始化一个isLogin:false
标志未登录状态。
在index.js
的onLoad
方法中进行判断
//页面加载就执行的函数
onLoad: function () {
//判断是否登录(从缓存中获取用户信息)
const userInfo=wx.getStorageSync(\'userInfo\');
//如果用户信息为空
if(userInfo!=null && userInfo!=\'\'){
console.log("已登录状态")
this.setData({
//登录状态设置为true
isLogin:true
})
}else{
console.log("该账号未登录")
this.setData({
//登录状态设置为false
isLogin:false
})
}
这就简单了,使用条件渲染对index.wxml
的内容进行渲染。
<view wx:if="{{isLogin}}">
<!--登录之后首页的内容-->
</view>
<view wx:else>
<!--跳转到登陆页面-->
<navigator url="/pages/login/login">登录</navigator>
</view>
跳转到登录页面登录之后,会navigateBack
返回上一层页面,这时页面是不会自动刷新的,还需要最后一个操作:上一层页面(首页嘛)页面刷新,展示登录之后的内容。
在index.js
中添加方法
//页面被看见就执行的方法
onShow(){
this.onLoad()
}
登录完成,不过这样实现有一个BUG
不可避免,就是每个页面都需要上面的操作,因此并不完美,后续在完善吧!
后续补充:上面的登录只是假象,真正登录操作如下:
首先,微信小程序前台点击登录按钮之后,触发登录方法
bindgetuserinfo(e){
//取用户信息,如果登录成功,把用户信息存入缓存
const userInfo=e.detail.userInfo;
wx.login({
success: res => {
//res.code拿到code值
// 发送 res.code 到后台换取 openId, sessionKey, unionId
if (res.code) {
wx.request({
url: \'<http://localhost:8080/weixin/getOpenId>\',
method: \'POST\',
data: {
code: res.code
},
header: {
\'content-type\': \'application/x-www-form-urlencoded\'
},
success(res) {
console.log("openid:"+res.data.openid);
wx:wx.setStorageSync(\'userInfo\', userInfo);
if (res.data.openid != "" || res.data.openid!=null){
// 登录成功
wx.setStorageSync("openid", res.data.openid);//将用户id保存到缓存中
wx.setStorageSync("session_key", res.data.session_key);//将session_key保存到缓存中
wx:wx.navigateBack({
delta: 1
})
}else{
// 登录失败
// TODO 跳转到错误页面,要求用户重试
return false;
}
}
})
} else {
console.log(\'获取用户登录态失败!\' + res.errMsg)
}
}
})
},
前台把code
传到后台,后台通过code
、appID
、appSecret
请求微信接口,换取得到用户唯一标识open_id
。
appid
和appsecret
要到微信公众平台的开发模块中进行获取,然后微信开发者工具也要配置appid
,不能再使用测试号了。请求微信接口使用HttpClient
请求。
@RestController
@RequestMapping("/weixin")
public class WxController {
//微信code换取open_id
private String appID = "wxb44d77c8ddc418c6";
private String appSecret = "5fd925d791680d3444b96db6c7c9da32";
@RequestMapping("/getOpenId")
@JsonIgnoreProperties(ignoreUnknown = true)
public String getOpenId(@RequestParam("code") String code) throws JsonProcessingException {
String result = "";
try{
//请求微信服务器,用code换取openid。HttpUtil是工具类,后面会给出实现,Configure类是小程序配置信息,后面会给出代码
String url="<https://api.weixin.qq.com/sns/jscode2session?appid=>"
+ "wxb44d77c8ddc418c6" + "&secret="
+ "5fd925d791680d3444b96db6c7c9da32" + "&js_code="
+ code
+ "&grant_type=authorization_code";
RequestConfig rc = RequestConfig.custom().setSocketTimeout(5000).setConnectTimeout(5000).build();
CloseableHttpClient httpclient = HttpClients.createDefault();
if(url!=null){
try {
// 创建HttpGet对象,将URL通过构造方法传入HttpGet对象
HttpGet httpGet=new HttpGet(url);
// 将配置好请求信息附加到http请求中
httpGet.setConfig(rc);
// 执行DefaultHttpClient对象的execute方法发送GET请求,通过CloseableHttpResponse接口的实例,可以获取服务器返回的信息
CloseableHttpResponse response = httpclient.execute(httpGet);
try {
// 得到返回对象
HttpEntity entity = response.getEntity();
if(entity!=null){
// 获取返回结果
result = EntityUtils.toString(entity);
}
} catch (IOException e) {
e.printStackTrace();
}finally {
// 关闭到客户端的连接
response.close();
}
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
// 关闭http请求
httpclient.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
catch (Exception e) {
e.printStackTrace();
}
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
OpenIdJson openIdJson = mapper.readValue(result, OpenIdJson.class);
System.out.println(result.toString());
String tenderId = openIdJson.getOpenid();
login(tenderId);
return result;
}
@Autowired
WxMapper wxMapper;
//查询小程序登录用户信息进行登录
private void login(String tenderId){
Map map = wxMapper.selectWxuser(tenderId);
if(map!=null){
}else{
wxMapper.insertWxuser(tenderId);
}
}
}
7,授权获取地理位置信息
wx:wx.chooseLocation({
success: (result) => {
console.log(result)
}
})
上面只是获取地理位置信息的操作,真机调试下,如果不授权使用地理位置,则地理位置是获取不了的。
点击获取地理位置按钮,先授权,授权之后才能获取地理位置信息。
如果拒绝授权之后就再也不会弹出授权框了!
<view bindtap="locations" class="select iconfont icon-weizhi">获取地理位置</view>
locations(e){
//进行授权(就是弹框允许授权位置信息)
wx.authorize({
scope: \'scope.userLocation\',
//授权成功的操作
success: (res) => {
console.log(\'成功:\' , res)
//再获取地理位置信息
wx:wx.chooseLocation({
success: (result) => {
console.log(result)
const location=result.address;
this.setData({
locations:location,
isShowLocation:true
})
}
})
},
//授权失败的操作
fail: (res) => {
console.log(\'失败:\', res)
},
})
四,简单功能实现介绍
1,微信小程序授权登录
前言
由于微信官方修改了 getUserInfo 接口,所以现在无法实现一进入微信小程序就弹出授权窗口,只能通过 button 去触发。
实现思路
自己写一个微信授权登录页面让用户实现点击的功能,也就是实现了通过 button 组件去触发 getUserInof 接口。在用户进入微信小程序的时候,判断用户是否授权了,如果没有授权的话就显示授权页面,让用户去执行授权的操作。如果已经授权了,则直接跳过这个页面,进入首页。
界面简介
index.wxml
<view wx:if="{{isHide}}">
<view wx:if="{{canIUse}}" >
<view class=\'header\'>
<image src=\'/images/wx_login.png\'></image>
</view>
<view class=\'content\'>
<view>申请获取以下权限</view>
<text>获得你的公开信息(昵称,头像等)</text>
</view>
<button class=\'bottom\' type=\'primary\' open-type="getUserInfo" lang="zh_CN" bindgetuserinfo="bindGetUserInfo">
授权登录
</button>
</view>
<view wx:else>请升级微信版本</view>
</view>
<view wx:else>
<view>我的首页内容</view>
</view>
index.wcss
.header {
margin: 90rpx 0 90rpx 50rpx;
border-bottom: 1px solid #ccc;
text-align: center;
width: 650rpx;
height: 300rpx;
line-height: 450rpx;
}
.header image {
width: 200rpx;
height: 200rpx;
}
.content {
margin-left: 50rpx;
margin-bottom: 90rpx;
}
.content text {
display: block;
color: #9d9d9d;
margin-top: 40rpx;
}
.bottom {
border-radius: 80rpx;
margin: 70rpx 50rpx;
font-size: 35rpx;
}
index.js
Page({
data: {
//判断小程序的API,回调,参数,组件等是否在当前版本可用。
canIUse: wx.canIUse(\'button.open-type.getUserInfo\'),
isHide: false
},
onLoad: function() {
var that = this;
// 查看是否授权
wx.getSetting({
success: function(res) {
if (res.authSetting[\'scope.userInfo\']) {
wx.getUserInfo({
success: function(res) {
// 用户已经授权过,不需要显示授权页面,所以不需要改变 isHide 的值
// 根据自己的需求有其他操作再补充
// 我这里实现的是在用户授权成功后,调用微信的 wx.login 接口,从而获取code
wx.login({
success: res => {
// 获取到用户的 code 之后:res.code
console.log("用户的code:" + res.code);
// 可以传给后台,再经过解析获取用户的 openid
// 或者可以直接使用微信的提供的接口直接获取 openid ,方法如下:
// wx.request({
// // 自行补上自己的 APPID 和 SECRET
// url: \'https://api.weixin.qq.com/sns/jscode2session?appid=自己的APPID&secret=自己的SECRET&js_code=\' + res.code + \'&grant_type=authorization_code\',
// success: res => {
// // 获取到用户的 openid
// console.log("用户的openid:" + res.data.openid);
// }
// });
}
});
}
});
} else {
// 用户没有授权
// 改变 isHide 的值,显示授权页面
that.setData({
isHide: true
});
}
}
});
},
bindGetUserInfo: function(e) {
if (e.detail.userInfo) {
//用户按了允许授权按钮
var that = this;
// 获取到用户的信息了,打印到控制台上看下
console.log("用户的信息如下:");
console.log(e.detail.userInfo);
//授权成功后,通过改变 isHide 的值,让实现页面显示出来,把授权页面隐藏起来
that.setData({
isHide: false
});
} else {
//用户按了拒绝按钮
wx.showModal({
title: \'警告\',
content: \'您点击了拒绝授权,将无法进入小程序,请授权之后再进入!!!\',
showCancel: false,
confirmText: \'返回授权\',
success: function(res) {
// 用户没有授权成功,不需要改变 isHide 的值
if (res.confirm) {
console.log(\'用户点击了“返回授权”\');
}
}
});
}
}
})
2,手机号后台java解密
具体思路为:小程序前端点击按钮,弹出授权手机号窗口,用户允许后,js得到加密数据,加密向量,session_key,将这些信息传递到java后台解密,最终拿到电话号码。(直接通过js是获取不到的)
wxml按钮的代码为:
<button open-type="getPhoneNumber" bindgetphonenumber="getPhoneNumber">获取手机号码</button>
核心为open-type="getPhoneNumber"。
js代码:
getPhoneNumber:function(e){
console.log(e.detail);//你可以输出看一下e.detail的内容
if (e.detail.errMsg =="getPhoneNumber:ok"){//同意授权手机号
//请求后台解密
wx.request({
url: app.globalData.host + \'/xx/xxxx\',
data: {
encryptedData: e.detail.encryptedData,//加密数据
iv: e.detail.iv,//向量
session_key: wx.getStorageSync("session_key")//秘钥,在登录的时候可以得到,可以参考本人的博客,微信小程序实现登录
},
method: \'POST\',
header: {
\'content-type\': \'application/x-www-form-urlencoded\'
},
success: function (res) {
console.log("获取电话号码成功" + res.data);
}
})
}
},
java后台解密代码:
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.apache.ibatis.annotations.Param;
import javax.servlet.http.HttpServletRequest;
import com.sun.org.apache.xerces.internal.impl.dv.util.Base64;
import java.util.Arrays;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.AlgorithmParameters;
import java.security.Security;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import com.alibaba.fastjson.JSONObject;
/**所需的maven依赖包
<!-- 阿里JSON解析器 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>${fastjson.version}</version>
</dependency>
<!-- bouncycastle-->
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<version>1.54</version>
</dependency>
**/
/*******************************小程序********************************/
@PostMapping("/xx/xxxx")
@ResponseBody
public String mini_getPhone(HttpServletRequest request,@Param("encryptedData")String encryptedData,@Param("iv")String iv,@Param("session_key")String session_key)
{
//
JSONObject obj=getPhoneNumber(session_key,encryptedData,iv);//解密电话号码
//System.out.println(obj);
String sphone=obj.get("phoneNumber").toString();
return sphone;
}
//解析电话号码
public JSONObject getPhoneNumber(String session_key, String encryptedData, String iv) {
byte[] dataByte = Base64.decode(encryptedData);
byte[] keyByte = Base64.decode(session_key);
byte[] ivByte = Base64.decode(iv);
try {
int base = 16;
if (keyByte.length % base != 0) {
int groups = keyByte.length / base + (keyByte.length % base != 0 ? 1 : 0);
byte[] temp = new byte[groups * base];
Arrays.fill(temp, (byte) 0);
System.arraycopy(keyByte, 0, temp, 0, keyByte.length);
keyByte = temp;
}
// 初始化
Security.addProvider(new BouncyCastleProvider());
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
SecretKeySpec spec = new SecretKeySpec(keyByte, "AES");
AlgorithmParameters parameters = AlgorithmParameters.getInstance("AES");
parameters.init(new IvParameterSpec(ivByte));
cipher.init(Cipher.DECRYPT_MODE, spec, parameters);
byte[] resultByte = cipher.doFinal(dataByte);
if (null != resultByte && resultByte.length > 0) {
String result = new String(resultByte, "UTF-8");
return JSONObject.parseObject(result);
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
3,实现第一次进入引导页
首先,我们分两步走
- 引导页实现
- 如何后续不再进入引导页
引导页实现
第一步app.json 添加一个新页面(也就是引导页面)
{
"pages":[
"pages/index/guide",
"pages/welcome/welcome"
],
"window":{
"backgroundTextStyle":"light",
"navigationBarBackgroundColor": "#b3d4db",
"navigationBarTitleText": "欢迎",
"navigationBarTextStyle":"black"
}
}
第二步利用swiper实现普通单引导页面(配合ui添加一些文字介绍)
guide.js
Page({
data: {
imgs: [
"http://img.kaiyanapp.com/5edbf92b929f9174e3b69500df52ee21.jpeg?imageMogr2/quality/60",
"http://img.kaiyanapp.com/f2c497cb520d8360ef2980ecd0efdee7.jpeg?imageMogr2/quality/60",
"http://img.kaiyanapp.com/64f96635ddc425e3be237b5f70374d87.jpeg?imageMogr2/quality/60",
],
img: "http://img.kaiyanapp.com/7ff70fb62f596267ea863e1acb4fa484.jpeg",
},
start() {
wx.navigateTo({
url: \'../welcome/welcome\'
})
},
})
guide.wxml
<swiper indicator-dots="true">
<block wx:for="{{imgs}}" wx:for-index="index">
<swiper-item class="swiper-items">
<image class="swiper-image" src="{{item}}"></image>
<button class="button-img" bindtap="start" wx:if="{{index == imgs.length - 1}}">立即体验</button>
</swiper-item>
</block>
</swiper>
guide.wxss
swiper {
/*这个是绝对布局*/
position: absolute;
height: 100%;
width: 100%;
}
.swiper-image {
height: 100%;
width: 100%;
/*透明度*/
opacity: 0.9;
}
.button-img {
/*这个是绝对布局*/
position: relative;
bottom: 90px;
height: 40px;
width: 120px;
opacity: 0.6;
}
第三步,判断是否第一次进入? 如果复杂些,那就是要后台传值过来,简单的话就是本地缓存一个有用户信息的key
在app.js 里面的onLaunch里面的判断
1.设置缓存
2.读取缓存 - > 是否存在 - > 是 -> 进入welcome
读取缓存- > 是否存在 - > 否 -> 进入引导页
好了,只要不clear这个小程序的缓存,那么它就不会进入第一次的引导页!
请发表评论