23.后台系统-短信服务
# 使用阿里云短信服务
提示
需要开通阿里云短信服务,这里使用的是测试专用签名模板
- name: 🚀 阿里云短信
desc: '接入阿里云短信服务'
link: /pages/aff355/
bgColor: '#DFEEE7'
textColor: '#2A3344'
2
3
4
5
# 构建短信服务模块 service-msm
搭建过程同service-hosp
模块
# 修改pom文件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>service</artifactId>
<groupId>com.stt.yygh</groupId>
<version>0.0.1</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<version>1.0</version>
<artifactId>service-msm</artifactId>
<packaging>jar</packaging>
<name>service-msm</name>
<description>service-msm</description>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>dysmsapi20170525</artifactId>
<version>2.0.8</version>
</dependency>
</dependencies>
</project>
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
# 添加配置文件application.properties
# 服务端口
server.port=8204
# 服务名
spring.application.name=service-msm
#返回json的全局时间格式
spring.jackson.date-format=yyyy-MM-dd HH:mm:ss
spring.jackson.time-zone=GMT+8
spring.redis.host=localhost
spring.redis.port=6379
spring.redis.database= 0
spring.redis.timeout=1800000
spring.redis.lettuce.pool.max-active=20
spring.redis.lettuce.pool.max-wait=-1
#最大阻塞等待时间(负数表示没限制)
spring.redis.lettuce.pool.max-idle=5
spring.redis.lettuce.pool.min-idle=0
# nacos服务地址
spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848
aliyun.sms.regionId=default
aliyun.sms.accessKeyId=需要开通阿里云的accessKey
aliyun.sms.secret=需要开通阿里云的accessKeySecret
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 添加启动类
package com.stt.yygh.msm;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.context.annotation.ComponentScan;
// 取消数据源自动配置
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
@EnableDiscoveryClient
@ComponentScan(basePackages = "com.stt")
public class ServiceMsmApplication {
public static void main(String[] args) {
SpringApplication.run(ServiceMsmApplication.class, args);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 配置网关
在 service-gateway
服务模块中添加路由
#设置路由id
spring.cloud.gateway.routes[3].id=service-msm
#设置路由的uri
spring.cloud.gateway.routes[3].uri=lb://service-msm
#设置路由断言,代理servicerId为auth-service的/auth/路径
spring.cloud.gateway.routes[3].predicates= Path=/*/msm/**
2
3
4
5
6
# 注册短信验证码接口实现
# 添加配置类
创建 com.stt.yygh.msm.config
与 ConstantPropertiesUtils
类
用于读取阿里云短信业务的配置信息
package com.stt.yygh.msm.config;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component
public class ConstantPropertiesUtils implements InitializingBean {
@Value("${aliyun.sms.regionId}")
private String regionId;
@Value("${aliyun.sms.accessKeyId}")
private String accessKeyId;
@Value("${aliyun.sms.secret}")
private String secret;
public static String REGION_Id;
public static String ACCESS_KEY_ID;
public static String SECRECT;
@Override
public void afterPropertiesSet() throws Exception {
REGION_Id=regionId;
ACCESS_KEY_ID=accessKeyId;
SECRECT=secret;
}
}
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
# 添加controller
在service-msm
模块中创建发送验证码接口
随机生成验证码,并发送
package com.stt.yygh.msm.controller;
import com.stt.yygh.common.result.Result;
import com.stt.yygh.common.util.RandomUtil;
import com.stt.yygh.msm.service.MsmService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.concurrent.TimeUnit;
@RestController
@RequestMapping("/api/msm")
public class MsmApiController {
@Autowired
private MsmService msmService;
@Autowired
private RedisTemplate<String, String> redisTemplate;
//发送手机验证码
@GetMapping("send/{phone}")
public Result sendCode(@PathVariable String phone) {
//从redis获取验证码,如果获取获取到,返回ok
// key 手机号 value 验证码
String code = redisTemplate.opsForValue().get(phone);
if (!StringUtils.isEmpty(code)) {
return Result.ok();
}
//如果从redis获取不到,生成验证码
code = RandomUtil.getSixBitRandom();
//调用service方法,通过整合短信服务进行发送
boolean isSend = msmService.send(phone, code);
if (isSend) {
//生成验证码放到redis里面,设置有效时间
redisTemplate.opsForValue().set(phone, code, 2, TimeUnit.MINUTES);
return Result.ok();
}
return Result.fail().message("发送短信失败");
}
}
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
# 添加service以及实现
在 service-msm
中创建 MsmService
接口
package com.stt.yygh.msm.service;
public interface MsmService {
//发送手机验证码
boolean send(String phone, String code);
}
2
3
4
5
6
在 service-msm
中创建 MsmServiceImpl
实现类
package com.stt.yygh.msm.service.impl;
import com.alibaba.fastjson.JSONObject;
import com.aliyun.dysmsapi20170525.Client;
import com.aliyun.dysmsapi20170525.models.SendSmsRequest;
import com.aliyun.dysmsapi20170525.models.SendSmsResponse;
import com.aliyun.teaopenapi.models.Config;
import com.stt.yygh.msm.config.ConstantPropertiesUtils;
import com.stt.yygh.msm.service.MsmService;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
@Service
public class MsmServiceImpl implements MsmService {
// 初始化账号客户端
public static Client createClient() throws Exception {
Config config = new Config()
.setEndpoint("dysmsapi.aliyuncs.com") // 访问的域名
.setAccessKeyId(ConstantPropertiesUtils.ACCESS_KEY_ID) // AccessKey ID
.setAccessKeySecret(ConstantPropertiesUtils.SECRECT); // AccessKey Secret
return new Client(config);
}
//整合阿里云短信服务
@Override
public boolean send(String phone, String code) {
//判断手机号是否为空
if (StringUtils.isEmpty(phone)) {
return false;
}
//验证码 使用json格式 {"code":"123456"}
Map<String, Object> param = new HashMap();
param.put("code", code);
try {
Client client = createClient();
SendSmsRequest sendSmsRequest = new SendSmsRequest()
.setSignName("阿里云短信测试") //签名名称
.setTemplateCode("SMS_154950909") //模板code
.setPhoneNumbers(phone) //手机号
.setTemplateParam(JSONObject.toJSONString(param)); // 验证码
//调用方法进行短信发送
SendSmsResponse response = client.sendSms(sendSmsRequest);
System.out.println(response.getBody());
if (Objects.equals(response.getBody().getCode(), "OK")) {
return true;
}
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
}
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
# 测试
使用swagger2进行接口测试
# 完善登录模块 service-user
# 添加redis配置
修改service-user
模块中的配置,添加redis访问配置
spring.redis.host=localhost
spring.redis.port=6379
spring.redis.database= 0
spring.redis.timeout=1800000
spring.redis.lettuce.pool.max-active=20
spring.redis.lettuce.pool.max-wait=-1
#最大阻塞等待时间(负数表示没限制)
spring.redis.lettuce.pool.max-idle=5
spring.redis.lettuce.pool.min-idle=0
2
3
4
5
6
7
8
9
# 修改service实现
修改 service-user
的 UserInfoServiceImpl
实现,增加验证码校验
package com.stt.yygh.user.service.impl;
...
@Service
public class UserInfoServiceImpl extends ServiceImpl<UserInfoMapper, UserInfo> implements UserInfoService {
@Autowired
private RedisTemplate<String, String> redisTemplate;
@Override
public Map<String, Object> login(LoginVo loginVo) {
String phone = loginVo.getPhone();
String code = loginVo.getCode();
//校验参数
if (StringUtils.isEmpty(phone) || StringUtils.isEmpty(code)) {
throw new YyghException(ResultCodeEnum.PARAM_ERROR);
}
// 校验校验验证码
String phoneCode = redisTemplate.opsForValue().get(phone);
if (!Objects.equals(phoneCode, code)) {
throw new YyghException(ResultCodeEnum.CODE_ERROR);
}
// 通过手机号获取 会员,如果存在则返回,如果不存在则创建
UserInfo userInfo = saveUserInfoIfNotExistsByPhone(phone);
//TODO 记录登录
String name = getUserInfoName(userInfo);
Map<String, Object> map = new HashMap<>();
map.put("name", name);
// jwt生成token字符串
String token = JwtHelper.createToken(userInfo.getId(), name);
map.put("token", token);
return map;
}
...
}
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
# 登录前端实现 yygh-site
前端通过手机号以及验证码向后台访问,同时返回对应的name和jwt token存储在浏览器的cookie中,后期每次请求访问携带jwt访问后台,后台gateway进行验证
# 在yygh-site
项目中封装api
创建api/user文件夹,创建/api/user/userInfo.js文件
import request from '~/utils/request'
const api_name = `/api/user`
export function login(userInfo) {
return request({
url: `${api_name}/login`,
method: `post`,
data: userInfo
})
}
2
3
4
5
6
7
8
9
10
11
12
创建/api/msm文件夹,创建/api/msm/msm.js文件
import request from '~/utils/request'
const api_name = `/api/msm`
export function sendCode(mobile) {
return request({
url: `${api_name}/send/${mobile}`,
method: `get`
})
}
2
3
4
5
6
7
8
9
10
# 安装cookie
登录成功后,用户信息记录在cookie里面,需要安装支持cookie的前端组件
# 命令行执行
npm install js-cookie
2
# 在my-header.vue中添加登录组件
在my-header.vue头部增加登录弹框
<template>
<div class="header-container">
...
<!-- 登录弹出层 -->
<!-- 增加v-if dialogUserFormVisible 用于 解决第一次样式错乱的bug -->
<el-dialog :visible.sync="dialogUserFormVisible" v-if="dialogUserFormVisible"
:append-to-body="true"
:modal-append-to-body="false"
width="960px" @close="closeDialog()">
<div class="container">
<!-- 手机登录 #start -->
<div class="operate-view" v-if="dialogAtrr.showLoginType === 'phone'">
<div class="wrapper" style="width: 100%">
<div class="mobile-wrapper" style="position: static;width: 70%">
<span class="title">{{ dialogAtrr.labelTips }}</span>
<el-form>
<el-form-item>
<el-input v-model="dialogAtrr.inputValue" :placeholder="dialogAtrr.placeholder"
:maxlength="dialogAtrr.maxlength" class="input v-input">
<span slot="suffix" class="sendText v-link"
v-if="dialogAtrr.second>0">{{ dialogAtrr.second }}s</span>
<span slot="suffix" class="sendText v-link highlight clickable selected"
v-if="dialogAtrr.second === 0" @click="getCodeFun()">重新发送</span>
</el-input>
</el-form-item>
</el-form>
<div class="send-button v-button" @click="btnClick()">{{ dialogAtrr.loginBtn }}</div>
</div>
<div class="bottom">
<div class="wechat-wrapper" @click="weixinLogin()"><span
class="iconfont icon"></span></div>
<span class="third-text">第三方账号登录</span></div>
</div>
</div>
<!-- 手机登录 #end -->
<!-- 微信登录 #start -->
<div class="operate-view" v-if="dialogAtrr.showLoginType==='weixin'">
<div class="wrapper wechat" style="height: 400px">
<div>
<div id="weixinLogin"></div>
</div>
<div class="bottom wechat" style="margin-top: -80px;">
<div class="phone-container">
<div class="phone-wrapper" @click="phoneLogin()">
<span class="iconfont icon"></span>
</div>
<span class="third-text">手机短信验证码登录</span>
</div>
</div>
</div>
</div>
<!-- 微信登录 #end -->
<div class="info-wrapper">
<div class="code-wrapper">
<div><img src="//img.114yygh.com/static/web/code_login_wechat.png" class="code-img">
<div class="code-text"><span class="iconfont icon"></span>微信扫一扫关注</div>
<div class="code-text"> “快速预约挂号”</div>
</div>
<div class="wechat-code-wrapper"><img src="//img.114yygh.com/static/web/code_app.png" class="code-img">
<div class="code-text"> 扫一扫下载</div>
<div class="code-text"> “预约挂号”APP</div>
</div>
</div>
<div class="slogan">
<div>xxxxxx官方指定平台</div>
<div>快速挂号 安全放心</div>
</div>
</div>
</div>
</el-dialog>
</div>
</template>
...
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
73
添加响应的逻辑动作
...
<script>
import cookie from 'js-cookie'
import { login } from '@/api/user/userInfo'
import { sendCode } from '@/api/msm/msm'
const defaultDialogAtrr = {
showLoginType: 'phone', // 控制手机登录与微信登录切换
labelTips: '手机号码', // 输入框提示
inputValue: '', // 输入框绑定对象
placeholder: '请输入您的手机号', // 输入框placeholder
maxlength: 11, // 输入框长度控制
loginBtn: '获取验证码', // 登录按钮或获取验证码按钮文本
sending: true, // 是否可以发送验证码
second: -1, // 倒计时间 second>0 : 显示倒计时 second=0 :重新发送 second=-1 :什么都不显示
clearSmsTime: null // 倒计时定时任务引用 关闭登录层清除定时任务
}
export default {
data() {
return {
userInfo: {
phone: '',
code: '',
openid: ''
},
dialogUserFormVisible: false,
// 弹出层相关属性
dialogAtrr: defaultDialogAtrr,
name: '' // 用户登录显示的名称
}
},
mounted() {
},
methods: {
btnClick() { // 绑定登录或获取验证码按钮
if (this.dialogAtrr.loginBtn === '获取验证码') { // 判断是获取验证码还是登录
this.userInfo.phone = this.dialogAtrr.inputValue
// 获取验证码
this.getCodeFun()
return
}
this.login()
},
showLogin() { // 绑定登录,点击显示登录层
this.dialogUserFormVisible = true
// 初始化登录层相关参数
this.dialogAtrr = { ...defaultDialogAtrr }
},
login() {
this.userInfo.code = this.dialogAtrr.inputValue
if (this.dialogAtrr.loginBtn === '正在提交...') {
this.$message.error('重复提交')
return
}
if (this.userInfo.code === '') {
this.$message.error('验证码必须输入')
return
}
if (this.userInfo.code.length !== 6) {
this.$message.error('验证码格式不正确')
return
}
this.dialogAtrr.loginBtn = '正在提交...'
login(this.userInfo).then(res => {
console.log(res.data)
this.setCookies(res.data.name, res.data.token) // 登录成功 设置cookie
}).catch(e => {
this.dialogAtrr.loginBtn = '马上登录'
})
},
setCookies(name, token) {
cookie.set('token', token, { domain: 'localhost' })
cookie.set('name', name, { domain: 'localhost' })
window.location.reload()
},
getCodeFun() { // 获取验证码
if (!(/^1[34578]\d{9}$/.test(this.userInfo.phone))) {
this.$message.error('手机号码不正确')
return
}
// 初始化验证码相关属性
this.dialogAtrr.inputValue = ''
this.dialogAtrr.placeholder = '请输入验证码'
this.dialogAtrr.maxlength = 6
this.dialogAtrr.loginBtn = '马上登录'
// 控制重复发送
if (!this.dialogAtrr.sending) return
// 发送短信验证码
this.timeDown()
this.dialogAtrr.sending = false
sendCode(this.userInfo.phone).then(res => {
this.timeDown()
}).catch(e => {
this.$message.error('发送失败,重新发送')
// 发送失败,回到重新获取验证码界面
this.showLogin()
})
},
// 倒计时
timeDown() {
clearInterval(this.clearSmsTime)
this.dialogAtrr.second = 60
this.dialogAtrr.labelTips = '验证码已发送至' + this.userInfo.phone
this.clearSmsTime = setInterval(() => {
--this.dialogAtrr.second
if (this.dialogAtrr.second < 1) {
clearInterval(this.clearSmsTime)
this.dialogAtrr.sending = true
this.dialogAtrr.second = 0
}
}, 1000)
},
// 关闭登录层
closeDialog() {
if (this.clearSmsTime) {
clearInterval(this.clearSmsTime)
}
},
loginMenu(command) {
if ('/logout' === command) {
cookie.set('name', '', { domain: 'localhost' })
cookie.set('token', '', { domain: 'localhost' })
//跳转页面
window.location.href = '/'
} else {
window.location.href = command
}
},
handleSelect(item) {
window.location.href = '/hospital/' + item.hoscode
},
weixinLogin() {
this.dialogAtrr.showLoginType = 'weixin'
},
phoneLogin() {
this.dialogAtrr.showLoginType = 'phone'
this.showLogin()
}
}
}
</script>
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
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
# 头部登录状态显示
当name从cookie中获取,并且不为空时,显示对应的菜单信息,同时特别针对 /logout 进行处理
<template>
<div class="header-container">
...
<!-- 右侧 -->
<div class="right-wrapper">
<span class="v-link clickable">帮助中心</span>
<span v-if="name === ''" class="v-link clickable"
@click="showLogin()" id="loginDialog">登录/注册</span>
<el-dropdown v-if="name !== ''" @command="loginMenu">
<span class="el-dropdown-link">
{{ name }}<i class="el-icon-arrow-down el-icon--right"/></span>
<el-dropdown-menu class="user-name-wrapper" slot="dropdown">
<el-dropdown-item command="/user">实名认证</el-dropdown-item>
<el-dropdown-item command="/order">挂号订单</el-dropdown-item>
<el-dropdown-item command="/patient">就诊人管理</el-dropdown-item>
<el-dropdown-item command="/logout" divided>退出登录</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</div>
</div>
<!-- 登录弹出层 -->
<el-dialog :visible.sync="dialogUserFormVisible"
...
</el-dialog>
</div>
</template>
<script>
...
export default {
data() {
...
},
created() {
this.showInfo()
},
...
methods: {
...
showInfo() {
let token = cookie.get('token')
if (token) {
this.name = cookie.get('name')
console.log(this.name)
}
},
loginMenu(command) {
if ('/logout' === command) {
cookie.set('name', '', { domain: 'localhost' })
cookie.set('token', '', { domain: 'localhost' })
//跳转页面
window.location.href = '/'
} else {
window.location.href = command
}
},
...
}
</script>
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
# 登录全局事件
问题:
目前登录层在my-header组件中,登录按钮在同一个组件里面,点击登录,调用
showLogin()
方法即可在预约挂号页面,选择科室去挂号时需要判断当前是否登录
- 如果已登录可以进入下一个页面
- 如果没有登录需要显示登录层,但是不能直接调用头部登录方法
解决方案
- 注册一个全局登录事件,需要登录时,发送一个登录事件,头部监听登录事件并触发登录按钮的点击事件即可打开登录层
# my-header.vue修改
添加全局登录事件,并将全局事件对象存储在window.loginEvent
中
<template>
<div class="header-container">
...
<!-- 右侧 -->
<div class="right-wrapper">
<span class="v-link clickable">帮助中心</span>
<span v-if="name === ''" class="v-link clickable"
@click="showLogin()" id="loginDialog">登录/注册</span>
...
</div>
</div>
...
</div>
</template>
<script>
import cookie from 'js-cookie'
import Vue from 'vue'
...
export default {
...
mounted() {
// 注册全局登录事件对象
window.loginEvent = new Vue()
// 监听登录事件
loginEvent.$on('loginDialogEvent', function () {
document.getElementById('loginDialog').click()
})
// 触发事件,显示登录层:loginEvent.$emit('loginDialogEvent')
},
methods: {
...
}
</script>
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
# 预约挂号页面调整
修改/pages/hospital/_hoscode.vue
组件
增加对登录的判断,从cookie中获取token,如果token不存在,表示需要重新登录
<template>
...
</template>
<script>
...
import cookie from 'js-cookie'
export default {
...
methods: {
...
schedule(depcode) {
// 登录判断
if (!cookie.get('token')) {
window.loginEvent.$emit('loginDialogEvent')
return
}
window.location.href = '/hospital/schedule?hoscode=' + this.hoscode + "&depcode="+ depcode
},
}
}
</script>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
说明:清除cookie,点击科室测试,之后在没有登录的情况下,选择科室进行预约会弹出登录框
# 完整my-header.vue组件代码
点击查看
<template>
<div class="header-container">
<div class="wrapper">
<!-- logo -->
<div class="left-wrapper v-link selected">
<img style="width: 50px" width="50" height="50" src="~assets/images/logo.png">
<span class="text">尚医通 预约挂号统一平台</span>
</div>
<!-- 搜索框 -->
<div class="search-wrapper">
<div class="hospital-search animation-show">
<el-autocomplete class="search-input small" prefix-icon="el-icon-search"
v-model="state" :fetch-suggestions="querySearchAsync" placeholder="点击输入医院名称"
@select="handleSelect">
<span slot="suffix" class="search-btn v-link highlight clickable selected">搜索 </span>
</el-autocomplete>
</div>
</div>
<!-- 右侧 -->
<div class="right-wrapper">
<span class="v-link clickable">帮助中心</span>
<span v-if="name === ''" class="v-link clickable"
@click="showLogin()" id="loginDialog">登录/注册</span>
<el-dropdown v-if="name !== ''" @command="loginMenu">
<span class="el-dropdown-link">
{{ name }}<i class="el-icon-arrow-down el-icon--right"/></span>
<el-dropdown-menu class="user-name-wrapper" slot="dropdown">
<el-dropdown-item command="/user">实名认证</el-dropdown-item>
<el-dropdown-item command="/order">挂号订单</el-dropdown-item>
<el-dropdown-item command="/patient">就诊人管理</el-dropdown-item>
<el-dropdown-item command="/logout" divided>退出登录</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</div>
</div>
<!-- 登录弹出层 -->
<!-- 增加v-if dialogUserFormVisible 用于 解决第一次样式错乱的bug -->
<el-dialog :visible.sync="dialogUserFormVisible" v-if="dialogUserFormVisible"
:append-to-body="true"
:modal-append-to-body="false"
width="960px" @close="closeDialog()">
<div class="container">
<!-- 手机登录 #start -->
<div class="operate-view" v-if="dialogAtrr.showLoginType === 'phone'">
<div class="wrapper" style="width: 100%">
<div class="mobile-wrapper" style="position: static;width: 70%">
<span class="title">{{ dialogAtrr.labelTips }}</span>
<el-form>
<el-form-item>
<el-input v-model="dialogAtrr.inputValue" :placeholder="dialogAtrr.placeholder"
:maxlength="dialogAtrr.maxlength" class="input v-input">
<span slot="suffix" class="sendText v-link"
v-if="dialogAtrr.second>0">{{ dialogAtrr.second }}s</span>
<span slot="suffix" class="sendText v-link highlight clickable selected"
v-if="dialogAtrr.second === 0" @click="getCodeFun()">重新发送</span>
</el-input>
</el-form-item>
</el-form>
<div class="send-button v-button" @click="btnClick()">{{ dialogAtrr.loginBtn }}</div>
</div>
<div class="bottom">
<div class="wechat-wrapper" @click="weixinLogin()"><span
class="iconfont icon"></span></div>
<span class="third-text">第三方账号登录</span></div>
</div>
</div>
<!-- 手机登录 #end -->
<!-- 微信登录 #start -->
<div class="operate-view" v-if="dialogAtrr.showLoginType==='weixin'">
<div class="wrapper wechat" style="height: 400px">
<div>
<div id="weixinLogin"></div>
</div>
<div class="bottom wechat" style="margin-top: -80px;">
<div class="phone-container">
<div class="phone-wrapper" @click="phoneLogin()">
<span class="iconfont icon"></span>
</div>
<span class="third-text">手机短信验证码登录</span>
</div>
</div>
</div>
</div>
<!-- 微信登录 #end -->
<div class="info-wrapper">
<div class="code-wrapper">
<div><img src="//img.114yygh.com/static/web/code_login_wechat.png" class="code-img">
<div class="code-text"><span class="iconfont icon"></span>微信扫一扫关注</div>
<div class="code-text"> “快速预约挂号”</div>
</div>
<div class="wechat-code-wrapper">
<img src="//img.114yygh.com/static/web/code_app.png" class="code-img">
<div class="code-text"> 扫一扫下载</div>
<div class="code-text"> “预约挂号”APP</div>
</div>
</div>
<div class="slogan">
<div>xxxxxx官方指定平台</div>
<div>快速挂号 安全放心</div>
</div>
</div>
</div>
</el-dialog>
</div>
</template>
<script>
import cookie from 'js-cookie'
import Vue from 'vue'
import { login } from '@/api/user/userInfo'
import { sendCode } from '@/api/msm/msm'
const defaultDialogAtrr = {
showLoginType: 'phone', // 控制手机登录与微信登录切换
labelTips: '手机号码', // 输入框提示
inputValue: '', // 输入框绑定对象
placeholder: '请输入您的手机号', // 输入框placeholder
maxlength: 11, // 输入框长度控制
loginBtn: '获取验证码', // 登录按钮或获取验证码按钮文本
sending: true, // 是否可以发送验证码
second: -1, // 倒计时间 second>0 : 显示倒计时 second=0 :重新发送 second=-1 :什么都不显示
clearSmsTime: null // 倒计时定时任务引用 关闭登录层清除定时任务
}
export default {
data() {
return {
userInfo: {
phone: '',
code: '',
openid: ''
},
dialogUserFormVisible: false,
// 弹出层相关属性
dialogAtrr: defaultDialogAtrr,
name: '' // 用户登录显示的名称
}
},
created() {
this.showInfo()
},
mounted() {
// 注册全局登录事件对象
window.loginEvent = new Vue()
// 监听登录事件
loginEvent.$on('loginDialogEvent', function () {
document.getElementById('loginDialog').click()
})
// 触发事件,显示登录层:loginEvent.$emit('loginDialogEvent')
},
methods: {
btnClick() { // 绑定登录或获取验证码按钮
if (this.dialogAtrr.loginBtn === '获取验证码') { // 判断是获取验证码还是登录
this.userInfo.phone = this.dialogAtrr.inputValue
// 获取验证码
this.getCodeFun()
return
}
this.login()
},
showLogin() { // 绑定登录,点击显示登录层
this.dialogUserFormVisible = true
// 初始化登录层相关参数
this.dialogAtrr = { ...defaultDialogAtrr }
},
login() {
this.userInfo.code = this.dialogAtrr.inputValue
if (this.dialogAtrr.loginBtn === '正在提交...') {
this.$message.error('重复提交')
return
}
if (this.userInfo.code === '') {
this.$message.error('验证码必须输入')
return
}
if (this.userInfo.code.length !== 6) {
this.$message.error('验证码格式不正确')
return
}
this.dialogAtrr.loginBtn = '正在提交...'
login(this.userInfo).then(res => {
console.log(res.data)
this.setCookies(res.data.name, res.data.token) // 登录成功 设置cookie
}).catch(e => {
this.dialogAtrr.loginBtn = '马上登录'
})
},
setCookies(name, token) {
cookie.set('token', token, { domain: 'localhost' })
cookie.set('name', name, { domain: 'localhost' })
window.location.reload()
},
getCodeFun() { // 获取验证码
if (!(/^1[34578]\d{9}$/.test(this.userInfo.phone))) {
this.$message.error('手机号码不正确')
return
}
// 初始化验证码相关属性
this.dialogAtrr.inputValue = ''
this.dialogAtrr.placeholder = '请输入验证码'
this.dialogAtrr.maxlength = 6
this.dialogAtrr.loginBtn = '马上登录'
// 控制重复发送
if (!this.dialogAtrr.sending) return
// 发送短信验证码
this.timeDown()
this.dialogAtrr.sending = false
sendCode(this.userInfo.phone).then(res => {
this.timeDown()
}).catch(e => {
this.$message.error('发送失败,重新发送')
// 发送失败,回到重新获取验证码界面
this.showLogin()
})
},
// 倒计时
timeDown() {
clearInterval(this.clearSmsTime)
this.dialogAtrr.second = 60
this.dialogAtrr.labelTips = '验证码已发送至' + this.userInfo.phone
this.clearSmsTime = setInterval(() => {
--this.dialogAtrr.second
if (this.dialogAtrr.second < 1) {
clearInterval(this.clearSmsTime)
this.dialogAtrr.sending = true
this.dialogAtrr.second = 0
}
}, 1000)
},
// 关闭登录层
closeDialog() {
if (this.clearSmsTime) {
clearInterval(this.clearSmsTime)
}
},
showInfo() {
let token = cookie.get('token')
if (token) {
this.name = cookie.get('name')
console.log(this.name)
}
},
loginMenu(command) {
if ('/logout' === command) {
cookie.set('name', '', { domain: 'localhost' })
cookie.set('token', '', { domain: 'localhost' })
//跳转页面
window.location.href = '/'
} else {
window.location.href = command
}
},
handleSelect(item) {
window.location.href = '/hospital/' + item.hoscode
},
weixinLogin() {
this.dialogAtrr.showLoginType = 'weixin'
},
phoneLogin() {
this.dialogAtrr.showLoginType = 'phone'
this.showLogin()
}
}
}
</script>
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
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264