uniapp朝夕社区——登录

loginFlow

登录界面支持两种登录方式,一种是手机号+验证码登录;第二种是账号+密码登录。用户可以在这两种登录方式之间进行切换。登录成功以后会获取用户信息存储在 vuex 中并使用 localStorage 做一个持久化存储;同时也会打开 websocket 的链接,并且跳回上一级页面,提示用户登录成功。

checkLogin

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
<view
class="text-center"
style="padding-top: 130rpx;padding-bottom: 70rpx;font-size: 55rpx;">
{{status ? '手机验证码登录':'账号密码登录'}}
</view>

<view class="px-2">
<template v-if="!status">
<view class="mb-2">
<input
type="text"
v-model="username"
placeholder="昵称/手机号/邮箱"
class="border-bottom p-2" />
</view>
<view class="mb-2 flex align-stretch">
<input
type="text"
v-model="password"
placeholder="请输入密码"
class="border-bottom p-2 flex-1" />
<view
class="border-bottom flex align-center justify-center font text-muted"
style="width: 150rpx;">
忘记密码?</view
>
</view>
</template>

<template v-else>
<view class="mb-2 flex align-stretch">
<view class="border-bottom flex align-center justify-center font px-2"
>+86</view
>
<input
type="text"
v-model="phone"
placeholder="手机号"
class="border-bottom p-2 flex-1" />
</view>
<view class="mb-2 flex align-stretch">
<input
type="text"
v-model="code"
placeholder="请输入验证码"
class="border-bottom p-2 flex-1" />
<view
class="border-bottom flex align-center justify-center font-sm text-white"
:class="codeTime > 0 ? 'bg-main-disabled' : 'bg-main'"
style="width: 180rpx;"
@click="getCode">
{{codeTime > 0 ? codeTime + ' s' : '获取验证码'}}
</view>
</view>
</template>
</view>

<script>
// 切换登录方式
changeStatus() {
// 初始化表单
this.initForm()
this.status = !this.status
},
// 初始化表单
initForm() {
this.username = ''
this.password = ''
this.phone = ''
this.code = ''
},
</script>

disabledButton

在用户信息没有填写完整之前,我们应该将登录按钮设置为禁用状态。如下代码所示:

1
2
3
4
5
6
7
8
9
10
11
12
<view class="py-2 px-3">
<button
class="text-white"
style="border-radius: 50rpx;border: 0;"
type="primary"
:disabled="disabled"
:class="disabled ? 'bg-main-disabled':'bg-main'"
@click="submit"
:loading="loading">
{{loading ? '登录中...' : '登录'}}
</button>
</view>
1
2
3
4
5
6
7
8
computed: {
disabled() {
if ((this.username === '' || this.password === '') && (this.phone === '' || this.code === '')) {
return true
}
return false
}
}

getPhoneCode

获取验证码登录是很常见的一种方式,一般都是通过获取验证码的接口发短信到用户的手机上,用户填入对应的验证码,进行比对以后就可以正常登录。

验证手机号格式
用户如果采用手机号+验证码的方式登录,那么我们应该校验手机号格式。如下代码所示:

1
2
3
4
5
6
7
8
<!-- 省去其他代码... -->
<view
class="border-bottom flex align-center justify-center font-sm text-white"
:class="codeTime > 0 ? 'bg-main-disabled' : 'bg-main'"
style="width: 180rpx;"
@click="getCode">
{{codeTime > 0 ? codeTime + ' s' : '获取验证码'}}
</view>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
// 表单验证
validate() {
//手机号正则
var mPattern = /^1[34578]\d{9}$/;
if (!mPattern.test(this.phone)) {
uni.showToast({
title: '手机号格式不正确',
icon: 'none'
});
return false
}
// ...更多验证
return true
}
getCode() {
// 防止重复获取
if (this.codeTime > 0) {
return;
}
// 验证手机号
if (!this.validate()) return;
// 请求数据
this.$H.post('/user/sendcode', {
phone: this.phone
}, {
native: true
}).then(res => {
uni.showToast({
title: res.data.msg,
icon: 'none'
});
// 倒计时
this.codeTime = 60
let timer = setInterval(() => {
if (this.codeTime >= 1) {
this.codeTime--
} else {
this.codeTime = 0
clearInterval(timer)
}
}, 1000)
})
}

如上代码所示,在验证手机号以后,访问接口拿到验证码,同时倒计时 60 秒开始计时。

loginAction

当用户填入表单信息以后,点击登录按钮就会触发登录操作。在这里要注意的是要先判断一下是用的哪种登录方式,从而改变对应的后台 url 和表单信息。如下代码所示:

1
2
3
4
5
6
7
8
9
10
11
12
<view class="py-2 px-3">
<button
class="text-white"
style="border-radius: 50rpx;border: 0;"
type="primary"
:disabled="disabled"
:class="disabled ? 'bg-main-disabled':'bg-main'"
@click="submit"
:loading="loading">
{{loading ? '登录中...' : '登录'}}
</button>
</view>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
submit() {
// 后端请求地址
let url = ''
// 表单信息
let data = ''
if (this.status) {
// 手机验证码登录
if (!this.validate()) return;
url = '/user/phonelogin'
data = {
phone: this.phone,
code: this.code
}
} else {
// 账号密码登录
url = '/user/login'
data = {
username: this.username,
password: this.password
}
}
}

setStorage

登录成功以后,会获取用户的信息,并且放在前端的vuex状态中,同时呢使用了uni.setStorageSync将信息做一个持久化存储。如代码所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// pages/login/login.vue
// 触发名为login的mutation,来修改vuex的state,并做持久化存储
this.$store.commit('login', res)

// store/index.js
// 登录
login(state,user){
// 修改登录状态
state.loginStatus = true
// 保存用户信息
state.user = user
// 保存当前用户的token
state.token = state.user.token
// 持久化存储
uni.setStorageSync('user', JSON.stringify(user));
}

openSocket

用户在登录成功以后,除了要将用户信息保存在前端供其他页面使用以外,还要开启socket,连接上 socket 以后就等于说是上线了,可以跟其他用户进行实时通讯,比如私信。这里用到了uni.connectSocket如代码所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
// pages/login/login.vue 触发名为openSocket的action
this.$store.dispatch('openSocket')

// store/index.js
// 打开socket
openSocket({ state,dispatch }){
// 防止重复连接
if(state.IsOpen) return
// 连接
state.SocketTask = uni.connectSocket({
url: $C.websocketUrl,
complete: ()=> {}
});
if (!state.SocketTask) return;
// 监听开启
state.SocketTask.onOpen(()=>{
// 将连接状态设为已连接
console.log('将连接状态设为已连接');
state.IsOpen = true
})
// 监听关闭
state.SocketTask.onClose(()=>{
console.log('连接已关闭');
state.IsOpen = false;
state.SocketTask = false;
state.IsOnline = false
// 清空会话列表
// 更新未读数提示
})
// 监听错误
state.SocketTask.onError(()=>{
console.log('连接错误');
state.IsOpen = false;
state.SocketTask = false;
state.IsOnline = false
})
// 监听接收信息
state.SocketTask.onMessage((e)=>{
console.log('接收消息',e);
// 字符串转json
let res = JSON.parse(e.data);
// 绑定返回结果
if (res.type === "bind"){
// 用户绑定
return dispatch('userBind',res.data)
}
// 处理接收信息
if (res.type !== 'text') return;
/*
{
to_id:1, // 接收人
from_id:12, // 发送人id
from_username:"某某", // 发送人昵称
from_userpic:"http://pic136.nipic.com/file/20170725/10673188_152559977000_2.jpg",
type:"text", // 发送类型
data:"你好啊", // 发送内容
time:151235451 // 接收到的时间
}
*/
// 处理接收消息
dispatch('handleChatMessage',res)
})
},

loginSuccess

用户登录成功以后要给用户一个成功的弹窗提示,并且跳回到之前的页面中去。这里跳转用到了uni.navigateBackuni.showToast这两个 API。如下代码所示:

1
2
3
4
5
6
7
8
// 提示和跳转
uni.navigateBack({
delta: 1
})
uni.showToast({
title: '登录成功',
icon: 'none'
})

uniApi

uni.navigateBack

uni.navigateBack()链接地址

关闭当前页面,返回上一页面或多级页面。可通过 getCurrentPages() 获取当前的页面栈,决定需要返回几层。

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 注意:调用 navigateTo 跳转时,调用该方法的页面会被加入堆栈,而 redirectTo 方法则不会。见下方示例代码

// 此处是A页面
uni.navigateTo({
url: 'B?id=1'
})

// 此处是B页面
uni.navigateTo({
url: 'C?id=1'
})

// 在C页面内 navigateBack,将返回A页面
uni.navigateBack({
delta: 2
})

uni.showToast

uni.showToast()链接地址

显示消息提示框。

示例

1
2
3
4
uni.showToast({
title: '标题',
duration: 2000
})

uni.setStorageSync

uni.setStorageSync链接地址

将 data 存储在本地缓存中指定的 key 中,会覆盖掉原来该 key 对应的内容,这是一个同步接口。

示例代码

1
2
3
4
5
try {
uni.setStorageSync('storage_key', 'hello');
} catch (e) {
// error
}

uni.connectSocket

uni.connectSocket链接地址

创建一个 WebSocket 连接。

示例代码

1
2
3
4
5
6
7
8
9
uni.connectSocket({
url: 'wss://www.example.com/socket',
header: {
'content-type': 'application/json'
},
protocols: ['protocol1'],
method: 'GET'
});

返回值

如果希望返回一个socketTask对象,需要至少传入 success / fail / complete 参数中的一个。例如:

1
2
3
4
5
var socketTask = uni.connectSocket({
url: 'wss://www.example.com/socket', //仅为示例,并非真实接口地址。
complete: ()=> {}
});

api doc

登录页面用到的几个接口,以下是文档说明。

sendCode

简介:发送验证码的接口。

  • url: http://localhost:8081/api/user/sendcode
  • method: ‘POST’

请求参数

1
2
3
{ 
"phone": "18888888888"
}

phoneLogin

简介:使用验证码登录的接口。

  • url: http://localhost:8081/api/user/phonelogin
  • method: ‘POST’

请求参数

1
2
3
4
{
"code":"8888",
"phone": "18830004000"
}

userLogin

简介:使用账户密码登录的接口。

  • url: http://localhost:8081/api/user/login
  • method: ‘POST’

请求参数

1
2
3
4
{
"username": "admin",
"password": "123456"
}

登录页源码