登录功能开发
实现POST提交
HTTP协议规定请求消息内容类型(Content-Type)有哪些?------ 只有四种
text/plain 没有编码的普通数据
application/x-www-form-urlencoded 编码后的普通数据
multipart/form-data 请求主体中包含文件上传域
application/json 请求主体是 JSON 格式字符串
HTTP协议规定响应消息内容类型(Content-Type)有哪些?------ 有很多种text/html、
text/plain、
text/css、
application/javascript、
image/jpeg、
application/mpeg3、
application/json、
...
html
<script>
export default {
data(){
return {
statusBarHeight: 0, //系统状态栏高度
hidePwd: true, //是否隐藏密码
phone: "13501234567", //用户输入的登录手机号
pwd:"123456", //用户输入的登录密码
}
},
//生命周期方法 ------ 页面挂载完成
onLoad(){ //此处此方法类似于mounted()
//console.log('login组件挂载完成')
//获取系统信息,读取其中的"状态栏高度"
let {statusBarHeight} = uni.getSystemInfoSync()
// console.log('屏幕高度:', screenHeight);
this.statusBarHeight = statusBarHeight
},
methods: {
async doLogin(){
//console.log('当前输入:',this.phone, this.pwd)
//1.验证手机号是否合法,不合法就弹出提示框,退出执行
if(!/^1[3-9]\d{9}$/.test(this.phone)){
uni.showToast({
title: '手机号非法', //提示标题
icon: 'none', //图标
duration: 3000 //持续时长
})
return
}
//2.验证密码是否合法,不合法就弹出提示框,退出执行
if(this.pwd.length < 6){
uni.showToast({
title:'密码格式非法',
icon: 'none',
duration: 3000
})
return
}
//3.把手机号/密码提交给服务器端数据API,进行登录验证
/**********使用uni.request()发起POST请求************/
let url = "https://www.codeboy.com/zhsqapi/user/login"
let [err, res] = await uni.request({
url,
method: 'POST',
//header: { 'Content-Type': 'application/json'},
header: { }, //请求内容类型默认就是JSON格式
//data: `{"phone":"${this.phone}", "pwd":"${this.pwd}"}`
//JSON系列化:把普通的JS对象转换为JSON格式的字符串
//data: JSON.stringify( {phone:this.phone, pwd:this.pwd})
//uni.request方法会自动根据请求内容类型,把数据转换为需要的格式
data: {phone:this.phone, pwd:this.pwd}
})
if(err){
console.log('执行失败:', err);
}else {
console.log('异步请求成功:', res);
}
//4.登录成功,提示"欢迎回来",跳转到首页
},
}
}
</script>
实现页面跳转
吐司对话框------ 用于简单的提醒,非重要事件的提示
uni.showToast( {title, icon, duration} )
模态对话框 ------ 用于严重的提示,甚至必须作出选择的提示
uni.showModal({title, content })
加载中对话框------ 提示操作正在进行中
uni.showLoading( ) / uni.hideLoading()
动作清单对话框------ 让用户选择要执行的动作
uni.showActionSheet( )
导航跳转:、 uni.navigateTo( )导航返回跳转:、 uni.navigateBack( )
重定向跳转:、 uni.redirectTo( )
切换页签跳转:、 uni.switchTab ( )
重启跳转:、 uni.reLaunch( )
html
<script>
export default {
data(){
return {
statusBarHeight: 0, //系统状态栏高度
hidePwd: true, //是否隐藏密码
phone: "13501234567", //用户输入的登录手机号
pwd:"123456", //用户输入的登录密码
}
},
//生命周期方法 ------ 页面挂载完成
onLoad(){ //此处此方法类似于mounted()
//console.log('login组件挂载完成')
//获取系统信息,读取其中的"状态栏高度"
let {statusBarHeight} = uni.getSystemInfoSync()
// console.log('屏幕高度:', screenHeight);
this.statusBarHeight = statusBarHeight
},
methods: {
async doLogin(){
//console.log('当前输入:',this.phone, this.pwd)
//1.验证手机号是否合法,不合法就弹出提示框,退出执行
if(!/^1[3-9]\d{9}$/.test(this.phone)){
uni.showToast({
title: '手机号非法', //提示标题
icon: 'none', //图标
duration: 3000 //持续时长
})
return
}
//2.验证密码是否合法,不合法就弹出提示框,退出执行
if(this.pwd.length < 6){
uni.showToast({
title:'密码格式非法',
icon: 'none',
duration: 3000
})
return
}
//3.把手机号/密码提交给服务器端数据API,进行登录验证
/**********使用uni.request()发起POST请求************/
let url = "https://www.codeboy.com/zhsqapi/user/login"
let [err, res] = await uni.request({
url,
method: 'POST',
//header: { 'Content-Type': 'application/json'},
header: { }, //请求内容类型默认就是JSON格式
//data: `{"phone":"${this.phone}", "pwd":"${this.pwd}"}`
//JSON系列化:把普通的JS对象转换为JSON格式的字符串
//data: JSON.stringify( {phone:this.phone, pwd:this.pwd})
//uni.request方法会自动根据请求内容类型,把数据转换为需要的格式
data: {phone:this.phone, pwd:this.pwd}
})
if(err){
console.log('执行失败:', err);
}else {
console.log('异步请求成功:', res);
}
//4.登录成功,提示"欢迎回来",跳转到首页
//跳转到"首页" ------ 切换页签
uni.showToast({
title: '欢迎回来',
icon: 'none', //图标:不要
duration: 3000, //持续时间:3s
complete(){ //对话框成功关闭
//跳转到"首页" ------ 切换页签
uni.switchTab({
url:'/pages/index/index'
})
}
})
},
}
}
</script>
Ajax集中管理
因为ajax访问在应用开发中频繁使用、代码复杂,且接口地址、数据结构等可能会发生变化
所以将ajax相关代码提取为方法,统一保存管理在外部文件中
创建service/index.js
js
/****对服务器端数据API进行访问二次封装****/
/**
* 服务器端基础地址
*/
export let base = 'https://www.codeboy.com/zhsqapi/'
/**
* API-1.2、用户登录
* 接口地址:user/login
* 请求方式:POST
* 请求主体格式:application/json
* 名称 必填 类型 说明
* phone 是 string 手机号
* pwd 是 string 密码
*/
export let userLogin = async (phone, pwd)=>{
//1.准备请求URL
let url = base + 'user/login'
//console.log(url)
//2.显示"加载中"提示框
uni.showLoading({
title: '用户登录中'
})
//3.发起异步请求消息
let [err, res] = await uni.request({
url,
method: 'POST',
data: {phone, pwd}
})
//4.隐藏"加载中"提示框
uni.hideLoading()
//5.返回响应消息主体
return res.data
}
html
<script>
import { userLogin } from '../../service/'
export default {
data(){
return {
statusBarHeight: 0, //系统状态栏高度
hidePwd: true, //是否隐藏密码
phone: "13501234567", //用户输入的登录手机号
pwd:"123456", //用户输入的登录密码
}
},
//生命周期方法 ------ 页面挂载完成
onLoad(){ //此处此方法类似于mounted()
//console.log('login组件挂载完成')
//获取系统信息,读取其中的"状态栏高度"
let {statusBarHeight} = uni.getSystemInfoSync()
// console.log('屏幕高度:', screenHeight);
this.statusBarHeight = statusBarHeight
},
methods: {
async doLogin(){
//console.log('当前输入:',this.phone, this.pwd)
//1.验证手机号是否合法,不合法就弹出提示框,退出执行
if(!/^1[3-9]\d{9}$/.test(this.phone)){
uni.showToast({
title: '手机号非法', //提示标题
icon: 'none', //图标
duration: 3000 //持续时长
})
return
}
//2.验证密码是否合法,不合法就弹出提示框,退出执行
if(this.pwd.length < 6){
uni.showToast({
title:'密码格式非法',
icon: 'none',
duration: 3000
})
return
}
//3.把手机号/密码提交给服务器端数据API,进行登录验证
let data = await userLogin(this.phone, this.pwd)
console.log(data)
//4.登录成功,提示"欢迎回来",跳转到首页
if(data.code===2000){ //登录成功
//弹出一个"吐司"对话框
uni.showToast({
title: '欢迎回来',
icon: 'none', //图标:不要
duration: 3000, //持续时间:3s
complete(){ //对话框成功关闭
//跳转到"首页" ------ 切换页签
uni.switchTab({
url:'/pages/index/index'
})
}
})
}else { //登录失败
//弹出一个"模态"对话框
uni.showModal({
title: '错误',
content: '登录失败!服务器返回消息:'+data.msg
})
}
},
}
}
</script>
处理吐司弹窗
无法实现吐司弹窗结束后页面跳转
所以需要将吐司提示放置到目标页-index.vue中实现
login.vue
html
<script>
import { userLogin } from '../../service/'
export default {
data(){
return {
statusBarHeight: 0, //系统状态栏高度
hidePwd: true, //是否隐藏密码
phone: "13501234567", //用户输入的登录手机号
pwd:"123456", //用户输入的登录密码
}
},
//生命周期方法 ------ 页面挂载完成
onLoad(){ //此处此方法类似于mounted()
//console.log('login组件挂载完成')
//获取系统信息,读取其中的"状态栏高度"
let {statusBarHeight} = uni.getSystemInfoSync()
// console.log('屏幕高度:', screenHeight);
this.statusBarHeight = statusBarHeight
},
methods: {
async doLogin(){
//console.log('当前输入:',this.phone, this.pwd)
//1.验证手机号是否合法,不合法就弹出提示框,退出执行
if(!/^1[3-9]\d{9}$/.test(this.phone)){
uni.showToast({
title: '手机号非法', //提示标题
icon: 'none', //图标
duration: 3000 //持续时长
})
return
}
//2.验证密码是否合法,不合法就弹出提示框,退出执行
if(this.pwd.length < 6){
uni.showToast({
title:'密码格式非法',
icon: 'none',
duration: 3000
})
return
}
//3.把手机号/密码提交给服务器端数据API,进行登录验证
let data = await userLogin(this.phone, this.pwd)
// console.log(data)
//4.登录成功,提示"欢迎回来",跳转到首页
if(data.code===2000){ //登录成功
//跳转到"首页" ------ 切换页签
uni.switchTab({
url:'/pages/index/index'
})
}
}else { //登录失败
//弹出一个"模态"对话框
uni.showModal({
title: '错误',
content: '登录失败!服务器返回消息:'+data.msg
})
}
},
}
}
</script>
index.vue
js
//生命周期方法:组件加载完成
async onLoad() {
//弹出"欢迎回来"提示框
uni.showToast({
title:'欢迎回来',
icon: 'none',
duration: 3000
})
},
令牌机制
令牌概念
HTTP协议属于**"无状态协议"**------客户端发起一个HTTP请求,服务器返回一个HTTP响应,服务器不会记录客户端的任何信息。实际应用中,很多场景下需要服务器记录客户端访问信息:例如根据访问历史进行后续的推荐、主题选择、购物车...
实现这类效果可用的技术:Cookie、SessionStorage&LocalStorage、Session、Token
Token :令牌,用于证明客户端身份的机制。**原理:服务器端把客户端的信息保存在一个对象中,**加密为一个定长字符串,发送给客户端;客户端保存起来;等到下次请求时,客户端可以再把加密字符串返回给服务器;服务器可以解密出其中的原始信息,从而进一步查询更多信息------类似于银行给客户端的"银行卡",其中存储着客户端的信息(加密存储,客户端是读不懂的),后续有些请求需要客户端出示此"银行卡"有些请求则不需要。
客户端 | 服务器 |
---|---|
1、客户端发送简单请求,包含phone和pwd | |
2、服务器验证登录信息,成功后,把客户端信息保存在一个对象中,形如:{ 用户编号:123, 用户名:yaya, 登录时间:x年x月x日 xx:xx:xx, 登录过期时间:x年x月x日 xx:xx:xx .... } | |
3.服务器将上述对象加密为定长字符串(即token);随同响应消息一同返回给客户端: { code:2000, msg: 'login succ', token: '加密后的定长字符串' } | |
4、接收到响应消息,把其中的token保存在客户端 uni.setStorageSync('userToken', data.token) | |
5、从客户端读取之前保存的token let token=uni.getStorageSync('userToken') | |
6、发送请求消息,把token放在请求消息头中(与后端协商好的请求头) uni.request({ url, header:{ token: token } }) 生成的请求消息形如: GET /index/data HTTP1.1 token: ''加密后的字符串'' | |
7、服务器接收到请求消息,从请求头中读取req.headers.token(即token),解密令牌,得到原始的令牌信息,即:{ 用户编号:123, 用户名:yaya 登录时间:x年x月x日 xx:xx:xx, 登录过期时间:x年x月x日 xx:xx:xx .... } | |
8、服务器根据用户信息查询数据库,将用户信息返回给客户端 |
本地存储
异步操作localStorage:
uni.setStorage(k, v, success(){})
uni.getStorage(k, success(){})
uni.removeStorage(k, success(){})
uni.clearStorage( success(){} )
同步操作localStorage:
uni.setStorageSync(k, v)
uni.getStorageSync(k)
uni.removeStorageSync(k)
uni.clearStorageSync( )
html
<script>
import { userLogin } from '../../service/'
export default {
data(){
return {
statusBarHeight: 0, //系统状态栏高度
hidePwd: true, //是否隐藏密码
phone: "13501234567", //用户输入的登录手机号
pwd:"123456", //用户输入的登录密码
}
},
//生命周期方法 ------ 页面挂载完成
onLoad(){ //此处此方法类似于mounted()
//console.log('login组件挂载完成')
//获取系统信息,读取其中的"状态栏高度"
let {statusBarHeight} = uni.getSystemInfoSync()
// console.log('屏幕高度:', screenHeight);
this.statusBarHeight = statusBarHeight
},
methods: {
async doLogin(){
//console.log('当前输入:',this.phone, this.pwd)
//1.验证手机号是否合法,不合法就弹出提示框,退出执行
if(!/^1[3-9]\d{9}$/.test(this.phone)){
uni.showToast({
title: '手机号非法', //提示标题
icon: 'none', //图标
duration: 3000 //持续时长
})
return
}
//2.验证密码是否合法,不合法就弹出提示框,退出执行
if(this.pwd.length < 6){
uni.showToast({
title:'密码格式非法',
icon: 'none',
duration: 3000
})
return
}
//3.把手机号/密码提交给服务器端数据API,进行登录验证
let data = await userLogin(this.phone, this.pwd)
// console.log(data)
//4.登录成功,提示"欢迎回来",跳转到首页
if(data.code===2000){ //登录成功
//在客户端存储服务器返回的token(身份令牌)
uni.setStorageSync('userToken', data.token)
//跳转到"首页" ------ 切换页签
uni.switchTab({
url:'/pages/index/index'
})
}else { //登录失败
//弹出一个"模态"对话框
uni.showModal({
title: '错误',
content: '登录失败!服务器返回消息:'+data.msg
})
}
},
}
}
</script>
获取主页数据
service/index.js中提供后去主页数据方法
传输令牌 获取数据
js
/**
* API-2.1、首页数据
* 接口地址:index/data
* 请求方式:GET
* 请求头部:token - 用户登录后保存在客户端的身份凭证
*/
export let indexData = async ( )=>{
//1.准备请求URL
let url = base + 'index/data'
//2.显示"加载中"提示框
uni.showLoading({
title: '首页数据读取中'
})
//3.发起异步请求消息
let [err, res] = await uni.request({
url, //请求地址
header: { //请求头部-token(客户端身份令牌)
token: uni.getStorageSync('userToken')
}
})
//4.隐藏"加载中"提示框
uni.hideLoading()
//5.返回响应消息主体
return res.data
}
index.vue在onLoad()中获取主页数据
js
import { indexData, base } from '@/service'
js
//生命周期方法:组件加载完成
async onLoad() {
//弹出"欢迎回来"提示框
uni.showToast({
title:'欢迎回来',
icon: 'none',
duration: 3000
})
//向服务器请求首页数据
let data = await indexData()
console.log(data)
},
生命周期方法
应用程序生命周期方法
整个应用程序的生命周期方法 ------ App.vue ------ 高仿微信小程序**
onLaunch():整个应用程序启动了
onShow():应用程序显示出来,例如:第一次启动完成、从其它应用切换会当前应用
onHide():应用程序隐藏起来了,例如:来电话了、用户点击桌面按钮
页面生命周期方法
页面的生命周期方法 ------ pages ------ 高仿微信小程序**
onLoad():当前页面挂载完成,功能类似于mounted
onShow():页面显示出来了,例如:第一次挂载完成、导航返回之前的页面
onReady():页面准备就绪了,每个页面此方法调用且仅调用一次------第一次调用onShow之后
onHide():页面隐藏起来了,例如:导航跳转到下一个页面
onUnload():当前页面完成卸载,功能类似于destroyed
onPageScroll():页面滚动了
onReachBottom():页面滚动到底部了
onPullDownRefresh():页面在顶部下拉刷新了
组件生命周期方法
组件的生命周期方法 ------ components ------ 高仿Vue.js
创建时期:beforeCreate() 、created()
挂载时期:beforeMount() 、mounted()
更新时期:beforeUpdate() 、updated()
销毁时期:beforeDestroy() 、destroyed()
主页开发
保存主页数据
html
<script>
import { indexData, base } from '@/service'
export default {
data() {
return {
base, //把服务器基础地址变量设置为数据属性
carousels:[], //轮播广告条目列表
menuItems:[], //当前用户选中的功能菜单列表
activities:[], //最新的社区活动列表
}
},
//生命周期方法:组件加载完成
async onLoad() {
//弹出"欢迎回来"提示框
uni.showToast({
title:'欢迎回来',
icon: 'none',
duration: 3000
})
//向服务器请求首页数据
let data = await indexData()
this.carousels = data.carousels //轮播广告
this.menuItems = data.menuItems //功能菜单
this.activities = data.activities //社区活动
},
methods: {
}
}
</script>
轮播图实现
html
<!-- F1: 轮播广告 -->
<!-- indicator-dots:是否显示"小圆饼"指示器 -->
<!-- autoplay:是否自动播放轮播广告 -->
<!-- interval:时间间隔,两个广告间停留时间 -->
<!-- duration:持续时长,一个广告的过渡动画持续时长 -->
<swiper :indicator-dots="true" :autoplay="true" :interval="2000" :duration="500">
<swiper-item v-for="(c, i) in carousels" :key="i">
<view class="swiper-item">
<image @click="jump(c.href)" :src="base + c.pic" mode="widthFix"/>
</view>
</swiper-item>
</swiper>
html
<script>
import { indexData, base } from '@/service'
export default {
data() {
return {
base, //把服务器基础地址变量设置为数据属性
carousels:[], //轮播广告条目列表
menuItems:[], //当前用户选中的功能菜单列表
activities:[], //最新的社区活动列表
}
},
//生命周期方法:组件加载完成
async onLoad() {
//弹出"欢迎回来"提示框
uni.showToast({
title:'欢迎回来',
icon: 'none',
duration: 3000
})
//向服务器请求首页数据
let data = await indexData()
this.carousels = data.carousels //轮播广告
this.menuItems = data.menuItems //功能菜单
this.activities = data.activities //社区活动
},
methods: {
jump(url){
console.log(url)
//导航跳转到指定页
uni.navigateTo({ url })
}
}
}
</script>
html
<style scoped lang="scss">
//提示:页面中可以使用标签选择器,但是组件中不能使用
.swiper-item > image {
width: 750rpx;
}
</style>
宫格图实现
需要安装uni-grid组件
html
<!-- F2: 功能菜单 -->
<!-- column:一行中默认显示几列 -->
<!-- showBorder:是否显示边框 -->
<!-- square:每个宫格项是否显示为方形 -->
<uni-grid class="func-menu" :column="4" :showBorder="false" :square="true">
<uni-grid-item v-for="(item,i) in menuItems" :key="i">
<view class="menu-item" @click="jump(item.href)">
<image :src="base+item.pic" mode="widthFix"/>
<text>{{item.title}}</text>
</view>
</uni-grid-item>
</uni-grid>
html
<style scoped lang="scss">
.func-menu {
margin-top: $uni-spacing-col-base;
background-color: $uni-bg-color;
.menu-item {
height: 100%;
//把弹性容器的主轴方向修改为:纵向
flex-direction: column;
//弹性容器中的子元素在主轴方向上居中对齐
justify-content: center;
//弹性容器中的子元素交叉轴方向上居中对齐
align-items: center;
> image { width:35%; margin-bottom: $uni-spacing-col-sm; }
}
}
</style>
商业服务功实现
需要安装uni-card扩展组件
html
<!-- F3: 商业服务 -->
<!-- isFull:是否显示为"通栏卡片"(左右撑满) -->
<uni-card class="card" title="| 商业服务" is-full>
<view class="service">
<!-- 左侧:房屋租售 -->
<view class="service-item">
<view class="txt">
<text>房屋租售</text>
<view>
<navigator>租房</navigator>
<navigator>短租</navigator>
</view>
</view>
<!-- 图片缩放模式1:widthFix -->
<!-- 图片缩放模式2:scaleToFill:不保持原始宽高比,缩放图片填满指定宽高 -->
<image class="img" mode="scaleToFill" src="../../static/img/chuzu.png"/>
</view>
<!-- 右侧:便民服务 -->
<view class="service-item">
<view class="txt">
<text>便民服务</text>
<view>
<navigator>便利店</navigator>
<navigator>超市</navigator>
</view>
</view>
<image class="img" mode="scaleToFill" src="../../static/img/bianmin.png"/>
</view>
</view>
</uni-card>
css
.service {
width: 100%;
.service-item {
//弹性子元素尺寸增长权重为:1
flex: 1;
padding-top: $uni-spacing-col-sm;
padding-bottom: $uni-spacing-col-sm;
justify-content: space-between; //弹性容器中的子元素在主轴方向上:空白在中央
align-items: center; //弹性容器中的子元素在交叉轴方向上:居中对齐
&:nth-child(1) {padding-right: $uni-spacing-row-sm;}
&:nth-child(2) {padding-left: $uni-spacing-row-sm;}
.txt {
font-size: $uni-font-size-sm;
flex-direction: column;
flex: 1; //弹性子元素尺寸增长权重:1
navigator {margin-right: $uni-spacing-row-sm;}
}
.img { width: 150rpx; height: 120rpx;}
}
}