Chapter12 — 前台 会员注册与登录
12.1 短信功能
12.1.1 申请阿里云短信服务
12.1.2 工具类:util模块
- CrowdUtil
/** * 阿里官方新API * @param SignName 签名 * @param TemplateCode 模板编号 * @param phoneNumbers 手机号码 * @return 成功则返回验证码,失败返回错误信息 */ public static ResultEntity<String> sendShortMessage(String SignName, String TemplateCode, String phoneNumbers) { DefaultProfile profile = DefaultProfile.getProfile( "cn-hangzhou", ACCESSKEYID, ACCESSKEYSECRET ); IAcsClient client = new DefaultAcsClient( profile ); CommonRequest request = new CommonRequest(); String authCode = RandomUtil.randomNumbers( 4 ); request.setSysMethod( MethodType.POST ); request.setSysDomain( "dysmsapi.aliyuncs.com" ); request.setSysVersion( "2017-05-25" ); request.setSysAction( "SendSms" ); request.putQueryParameter( "RegionId", "cn-hangzhou" ); request.putQueryParameter( "PhoneNumbers", phoneNumbers ); request.putQueryParameter( "SignName", SignName );//短信签名名称。请在控制台签名管理页面签名名称一列查看。 request.putQueryParameter( "TemplateCode", TemplateCode ); request.putQueryParameter( "TemplateParam", "{\"code\":\"" + authCode + "\"}" ); //拼接成官网指定格式 try { CommonResponse response = client.getCommonResponse( request ); //System.out.println( response.getData() ); //回显消息 if(response.getHttpResponse().isSuccess()) { //请求成功 return ResultEntity.successWithData(authCode); }else{ return ResultEntity.failed(response.getData()); } } catch (ServerException e) { e.printStackTrace(); return ResultEntity.failed(e.getMessage()); } catch (ClientException e) { e.printStackTrace(); return ResultEntity.failed(e.getMessage()); } } /** * 阿里旧版的官方API * @param signName 签名 * @param templateCode 模板编号 * @param phoneNumbers 手机号码 * @return 成功则返回验证码,失败返回错误信息 */ public static ResultEntity<String> sendSms(String signName, String templateCode, String phoneNumbers) { //设置超时时间-可自行调整 System.setProperty("sun.net.client.defaultConnectTimeout", "10000"); System.setProperty("sun.net.client.defaultReadTimeout", "10000"); //初始化ascClient需要的几个参数 final String product = "Dysmsapi";//短信API产品名称(短信产品名固定,无需修改) final String domain = "dysmsapi.aliyuncs.com";//短信API产品域名(接口地址固定,无需修改) //替换成你的AK final String accessKeyId = CrowdUtil.ACCESSKEYID;//你的accessKeyId,参考本文档步骤2 final String accessKeySecret = CrowdUtil.ACCESSKEYSECRET;//你的accessKeySecret,参考本文档步骤2 //初始化ascClient,暂时不支持多region(请勿修改) IClientProfile profile = DefaultProfile.getProfile("cn-hangzhou", accessKeyId, accessKeySecret); try { DefaultProfile.addEndpoint("cn-hangzhou", "cn-hangzhou", product, domain); } catch (ClientException e) { e.printStackTrace(); } IAcsClient acsClient = new DefaultAcsClient(profile); // 随机验证码 String authCode = RandomUtil.randomNumbers( 4 ); //组装请求对象 SendSmsRequest request = new SendSmsRequest(); //使用post提交 request.setMethod(MethodType.POST); //必填:待发送手机号。支持以逗号分隔的形式进行批量调用,批量上限为1000个手机号码,批量调用相对于单条调用及时性稍有延迟,验证码类型的短信推荐使用单条调用的方式;发送国际/港澳台消息时,接收号码格式为国际区号+号码,如“85200000000” request.setPhoneNumbers(phoneNumbers); //必填:短信签名-可在短信控制台中找到 request.setSignName(signName); //必填:短信模板-可在短信控制台中找到,发送国际/港澳台消息时,请使用国际/港澳台短信模版 request.setTemplateCode(templateCode); //可选:模板中的变量替换JSON串,如模板内容为"亲爱的${name},您的验证码为${code}"时,此处的值为 //友情提示:如果JSON中需要带换行符,请参照标准的JSON协议对换行符的要求,比如短信内容中包含\r\n的情况在JSON中需要表示成\\r\\n,否则会导致JSON在服务端解析失败 //参考:request.setTemplateParam("{\"变量1\":\"值1\",\"变量2\":\"值2\",\"变量3\":\"值3\"}") request.setTemplateParam("{\"code\":\"" + authCode + "\"}"); //可选-上行短信扩展码(扩展码字段控制在7位或以下,无特殊需求用户请忽略此字段) //request.setSmsUpExtendCode("90997"); //可选:outId为提供给业务方扩展字段,最终在短信回执消息中将此值带回给调用者 //request.setOutId("yourOutId"); try { //请求失败这里会抛ClientException异常 SendSmsResponse sendSmsResponse = acsClient.getAcsResponse(request); if(sendSmsResponse.getCode() != null && sendSmsResponse.getCode().equals("OK")) { //请求成功 return ResultEntity.successWithData(authCode); }else{ return ResultEntity.failed(sendSmsResponse.getMessage()); } } catch (ClientException e) { e.printStackTrace(); return ResultEntity.failed(e.getMessage()); } } /** * 短信服务不可用时的模拟方法 * @param signName 签名,随便填 * @param templateCode 模板编号,随便填 * @param phoneNumbers 手机号码 * @return 返回生成的随机验证码 */ public static ResultEntity<String> sendSmsSimulation(String signName, String templateCode, String phoneNumbers){ String authCode = RandomUtil.randomNumbers( 4 ); return ResultEntity.successWithData(authCode); }
12.2 发送短信验证码
12.2.1 思路
12.2.2 前端代码
- auth模块新建类
package com.atguigu.crowd.config; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.ViewControllerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @Configuration public class CrowdWebMvcConfig implements WebMvcConfigurer { @Override public void addViewControllers(ViewControllerRegistry registry) { // 浏览器访问的地址 String urlPath = "/auth/member/to/reg/page.html"; // 目标视图的名称,将来拼接前后缀 String viewName = "member-reg"; // 添加view-controller registry.addViewController(urlPath).setViewName(viewName); } }
- 修改前端页面
<li><a href="reg.html" th:href="@{/auth/member/to/reg/page.html}">注册</a></li>
- 新建注册页面 member-reg.html
<!DOCTYPE html> <html lang="zh-CN" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="description" content=""> <meta name="keys" content=""> <meta name="author" content=""> <base th:href="@{/}"/> <link rel="stylesheet" href="bootstrap/css/bootstrap.min.css"> <link rel="stylesheet" href="css/font-awesome.min.css"> <link rel="stylesheet" href="css/login.css"> <script src="jquery/jquery-2.1.1.min.js"></script> <script src="bootstrap/js/bootstrap.min.js"></script> <script type="text/javascript" src="layer/layer.js"></script> <script type="text/javascript"> layer.msg("test"); </script> </head> <body> <nav class="navbar navbar-inverse navbar-fixed-top" role="navigation"> <div class="container"> <div class="navbar-header"> <div><a class="navbar-brand" href="index.html" style="font-size:32px;">尚筹网-创意产品众筹平台</a></div> </div> </div> </nav> <div class="container"> <form class="form-signin" role="form"> <h2 class="form-signin-heading"><i class="glyphicon glyphicon-log-in"></i> 用户注册</h2> <div class="form-group has-success has-feedback"> <input type="text" class="form-control" id="inputSuccess4" placeholder="请输入登录账号" autofocus> <span class="glyphicon glyphicon-user form-control-feedback"></span> </div> <div class="form-group has-success has-feedback"> <input type="text" class="form-control" id="inputSuccess4" placeholder="请输入登录密码" style="margin-top:10px;"> <span class="glyphicon glyphicon-lock form-control-feedback"></span> </div> <div class="form-group has-success has-feedback"> <input type="text" class="form-control" id="inputSuccess4" placeholder="请输入邮箱地址" style="margin-top:10px;"> <span class="glyphicon glyphicon glyphicon-envelope form-control-feedback"></span> </div> <div class="form-group has-success has-feedback"> <input type="text" class="form-control" id="inputSuccess4" placeholder="请输入手机号" style="margin-top:10px;"> <span class="glyphicon glyphicon glyphicon-earphone form-control-feedback"></span> </div> <div class="form-group has-success has-feedback"> <input type="text" class="form-control" id="inputSuccess4" placeholder="请输入验证码" style="margin-top:10px;"> <span class="glyphicon glyphicon glyphicon-comment form-control-feedback"></span> </div> <button class="btn btn-lg btn-success btn-block"> 获取验证码</button> <a class="btn btn-lg btn-success btn-block" href="login.html" > 注册</a> </form> </div> </body> </html>
- 修改注册页面
<script type="text/javascript"> $(function (){ $("#sendBtn").click(function (){ // 1.获取接收短信的手机号 let phoneNum = $.trim($("[name=phoneNum]").val()); // 2.发送请求 $.ajax({ url: "auth/member/send/short/message.json", type: "post", data: { phoneNum: phoneNum }, dataType: "json", success: function (resp){ var result = resp.result; if (result == "SUCCESS") { layer.msg("发送成功"); } if (result == "FAILED") { layer.msg("发送失败,请重新发送!") } }, error: function (resp){ layer.msg(resp.status + " " + resp.statusText); } }); }); }); </script>
<input type="text" class="form-control" name="phoneNum" id="inputSuccess4" placeholder="请输入手机号" style="margin-top:10px;">
<button id="sendBtn" type="button" class="btn btn-lg btn-success btn-block"> 获取验证码</button>
12.2.3 后端代码
- 主启动类添加 feign 功能
@EnableFeignClients @SpringBootApplication @EnableEurekaClient public class AuthenticationConsumerMain {
- 新建 MemberHandler
@Controller public class MemberHandler { @Autowired private ShortMessageProperties shortMessageProperties; @Autowired private RedisRemoteService redisRemoteService; @ResponseBody @RequestMapping("/auth/member/send/short/message.json") public ResultEntity<String> sendMessage(@RequestParam("phoneNum") String phoneNum) { // 1.发送验证码到 phoneNum ResultEntity<String> resp = CrowdUtil.sendSms( shortMessageProperties.getSignName(), shortMessageProperties.getTemplateCode(), phoneNum ); // 2.判断是否发送成功 if (ResultEntity.SUCCESS.equals(resp.getResult())) { // 3.如果成功,则将验证码存入Redis String code = resp.getData(); String key = CrowdConstant.REDIS_CODE_PREFIX + phoneNum; ResultEntity<String> saveCodeResultEntity = redisRemoteService.setRedisKeyValueRemoteWithTimeout(key, code, 100, TimeUnit.SECONDS); // 4. 判断是否成功存入 Redis if (ResultEntity.SUCCESS.equals(saveCodeResultEntity.getResult())) { return ResultEntity.successWithoutData(); }else { return saveCodeResultEntity; } }else { return resp; } } }
- 配置文件
short: message: signName: 1 templateCode: SMS_21*****2
- 新建配置类
@Data @NoArgsConstructor @AllArgsConstructor @Component @ConfigurationProperties(prefix = "short.message") public class ShortMessageProperties { private String signName; private String templateCode; }
12.3 注册
12.3.1 思路
目标:用户点击注册,实现功能
思路
12.3.2 后端代码
- mysql模块handler
@RequestMapping("/save/member/remote") public ResultEntity<String> saveMember(@RequestBody MemberPO memberPO) { try { memberService.saveMember(memberPO); return ResultEntity.successWithoutData(); } catch (Exception e) { if (e instanceof DuplicateKeyException) return ResultEntity.failed(CrowdConstant.MESSAGE_LOGIN_ACCT_ALREADY_IN_USE); return ResultEntity.failed(e.getMessage()); } }
- mysql模块service
@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class) @Override public void saveMember(MemberPO memberPO) { memberPOMapper.insertSelective(memberPO); }
- api模块
@RequestMapping("/save/member/remote") public ResultEntity<String> saveMember(@RequestBody MemberPO memberPO);
- entity模块创建MemberVO
package com.atguigu.crowd.entity.vo; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @Data @NoArgsConstructor @AllArgsConstructor public class MemberVO { private String loginacct; private String userpswd; private String username; private String email; private String phoneNum; private String code; }
- MemberHandler
@Autowired private MySQLRemoteService mySQLRemoteService; @RequestMapping("/auth/do/member/register") public String register(MemberVO memberVO, ModelMap modelMap) { // 1.获取用户输入的手机号 String phoneNum = memberVO.getPhoneNum(); // 2.拼接Redis中存储的Key String key = CrowdConstant.REDIS_CODE_PREFIX + phoneNum; // 3.从Redis读取Key对应的value ResultEntity<String> resultEntity = redisRemoteService.getRedisStringValueByKey(key); // 4.检查查询操作是否有效 String result = resultEntity.getResult(); if (ResultEntity.FAILED.equals(result)) { modelMap.addAttribute(CrowdConstant.ATTR_NAME_MESSAGE, resultEntity.getMessage()); return "member-reg"; } String redisCode = resultEntity.getData(); if (redisCode == null) { modelMap.addAttribute(CrowdConstant.ATTR_NAME_MESSAGE, CrowdConstant.MESSAGE_CODE_NOT_EXISTS); return "member-reg"; } // 5.能从Redis查询到,则比较两者是否一致 String formCode = memberVO.getCode(); if (!formCode.equals(redisCode)) { modelMap.addAttribute(CrowdConstant.ATTR_NAME_MESSAGE, CrowdConstant.MESSAGE_CODE_INVALID); } // 6.一致,将value从Redis中删除 ResultEntity<String> removeResultEntity = redisRemoteService.removeRedisKeyRemote(key); /*if (ResultEntity.FAILED.equals(removeResultEntity.getResult())) { return ""; }*/ // 7.执行密码加密 BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); memberVO.setUserpswd(passwordEncoder.encode(memberVO.getUserpswd())); // 8. 执行保存 MemberPO memberPO = new MemberPO(); BeanUtils.copyProperties(memberVO, memberPO); ResultEntity<String> saveMemberResultEntity = mySQLRemoteService.saveMember(memberPO); if (ResultEntity.FAILED.equals(saveMemberResultEntity)) { modelMap.addAttribute(CrowdConstant.ATTR_NAME_MESSAGE, saveMemberResultEntity.getMessage()); return "member-reg"; } return "redirect:/auth/member/to/login/page"; }
- auth模块config类
@Configuration public class CrowdWebMvcConfig implements WebMvcConfigurer { @Override public void addViewControllers(ViewControllerRegistry registry) { // 浏览器访问的地址 String urlPath = "/auth/member/to/reg/page"; // 目标视图的名称,将来拼接前后缀 String viewName = "member-reg"; // 添加view-controller registry.addViewController(urlPath).setViewName(viewName); registry.addViewController("/auth/member/to/login/page").setViewName("member-login");; } }
- 另一个config类
12.3.3 前端
- 修改 member-reg.html
- 添加表单提交地址、method
- 输入文本框添加 name
- 修改注册按钮
<form action="/auth/do/member/register" method="post" class="form-signin" role="form"> <h2 class="form-signin-heading"><i class="glyphicon glyphicon-log-in"></i> 用户注册</h2> <p th:text="${message}">在这里显示从请求域取出的提示消息</p> <div class="form-group has-success has-feedback"> <input type="text" name="loginacct" class="form-control" id="inputSuccess1" placeholder="请输入登录账号" autofocus> <span class="glyphicon glyphicon-user form-control-feedback"></span> </div> <div class="form-group has-success has-feedback"> <input type="text" name="userpswd" class="form-control" id="inputSuccess2" placeholder="请输入登录密码" style="margin-top:10px;"> <span class="glyphicon glyphicon-lock form-control-feedback"></span> </div> <div class="form-group has-success has-feedback"> <input type="text" name="username" class="form-control" id="inputSuccess12" placeholder="请输入用户昵称" style="margin-top:10px;"> <span class="glyphicon glyphicon-lock form-control-feedback"></span> </div> <div class="form-group has-success has-feedback"> <input type="text" name="email" class="form-control" id="inputSuccess3" placeholder="请输入邮箱地址" style="margin-top:10px;"> <span class="glyphicon glyphicon glyphicon-envelope form-control-feedback"></span> </div> <div class="form-group has-success has-feedback"> <input type="text" name="phoneNum" class="form-control" id="inputSuccess4" placeholder="请输入手机号" style="margin-top:10px;"> <span class="glyphicon glyphicon glyphicon-earphone form-control-feedback"></span> </div> <div class="form-group has-success has-feedback"> <input type="text" name="code" class="form-control" id="inputSuccess5" placeholder="请输入验证码" style="margin-top:10px;"> <span class="glyphicon glyphicon glyphicon-comment form-control-feedback"></span> </div> <button id="sendBtn" type="button" class="btn btn-lg btn-success btn-block"> 获取验证码</button> <button type="button" class="btn btn-lg btn-success btn-block">注册</button> </form>
- 新建 member-login.html
<!DOCTYPE html> <html lang="zh-CN" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="description" content=""> <meta name="keys" content=""> <meta name="author" content=""> <base th:href="@{/}"/> <link rel="stylesheet" href="bootstrap/css/bootstrap.min.css"> <link rel="stylesheet" href="css/font-awesome.min.css"> <link rel="stylesheet" href="css/login.css"> <script src="jquery/jquery-2.1.1.min.js"></script> <script src="bootstrap/js/bootstrap.min.js"></script> <style> </style> </head> <body> <nav class="navbar navbar-inverse navbar-fixed-top" role="navigation"> <div class="container"> <div class="navbar-header"> <div><a class="navbar-brand" href="index.html" style="font-size:32px;">尚筹网-创意产品众筹平台</a></div> </div> </div> </nav> <div class="container"> <form action="/auth/member/do/login" method="post" class="form-signin" role="form"> <h2 class="form-signin-heading"> <i class="glyphicon glyphicon-log-in"></i> 用户登录 </h2> <p th:text="${message}">在这里显示登录失败的提示消息</p> <div class="form-group has-success has-feedback"> <input type="text" name="loginacct" class="form-control" id="loginacct" placeholder="请输入登录账号" autofocus> <span class="glyphicon glyphicon-user form-control-feedback"></span> </div> <div class="form-group has-success has-feedback"> <input type="text" name="userpswd" class="form-control" id="userpswd" placeholder="请输入登录密码" style="margin-top:10px;"> <span class="glyphicon glyphicon-lock form-control-feedback"></span> </div> <div class="checkbox" style="text-align:right;"> <a href="reg.html" th:href="@{/auth/member/to/login/page/}">我要注册</a> </div> <button type="submit" class="btn btn-lg btn-success btn-block"> 登录</button> </form> </div> </body> </html>
- 修改portal.html
<li><a href="login.html" th:href="@{/auth/member/to/login/page}">登录</a></li> <li><a href="reg.html" th:href="@{/auth/member/to/reg/page}">注册</a></li>
12.4 登录
12.4.1 目标思路
- 目标
- 检查账号密码是否正确
- 将正确的账号信息存入Session,跳转页面
- 思路
12.4.2 后端代码
- entity模块新建vo
@Data @AllArgsConstructor @NoArgsConstructor public class MemberLoginVO { private Integer id; private String username; private String email; }
- Memberhandler
@RequestMapping("/auth/member/do/login") public String doLogin(@RequestParam("loginacct") String logincacct, @RequestParam("userpswd") String userpswd, ModelMap modelMap, HttpSession session) { // 1.调用远程接口查询登录账号 ResultEntity<MemberPO> resultEntity = mySQLRemoteService.getMemberPOByLoginAcctRemote(logincacct); // 2.失败则返回登录页面 if (ResultEntity.FAILED.equals(resultEntity.getResult())) { modelMap.addAttribute(CrowdConstant.ATTR_NAME_EXCEPTION, resultEntity.getMessage()); return "member-login"; } MemberPO memberPO = resultEntity.getData(); if (memberPO == null) { modelMap.addAttribute(CrowdConstant.ATTR_NAME_EXCEPTION, CrowdConstant.MESSAGE_LOGIN_FAILED); return "member-login"; } // 3.比较密码 String userpswdDB = memberPO.getUserpswd(); BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); boolean matches = passwordEncoder.matches(userpswd, userpswdDB); if (!matches) { modelMap.addAttribute(CrowdConstant.ATTR_NAME_EXCEPTION, CrowdConstant.MESSAGE_LOGIN_FAILED); return "member-login"; } // 4.创建MemberLoginVO对象存入Session域 MemberLoginVO memberLoginVO = new MemberLoginVO(memberPO.getId(), memberPO.getUsername(), memberPO.getEmail()); session.setAttribute(CrowdConstant.ATTR_NAME_LOGIN_MEMBER, memberLoginVO); return "redirect:/auth/member/to/center/page"; }
- CrowdWebMvcConfig
registry.addViewController("/auth/member/to/center/page").setViewName("member-center");
12.4.3 前端代码
- 新建 member-center.html
<!DOCTYPE html> <html lang="zh-CN" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="description" content=""> <meta name="author" content=""> <base th:href="@{/}"/> <link rel="stylesheet" href="bootstrap/css/bootstrap.min.css"> <link rel="stylesheet" href="css/font-awesome.min.css"> <link rel="stylesheet" href="css/theme.css"> <style> #footer { padding: 15px 0; background: #fff; border-top: 1px solid #ddd; text-align: center; } #topcontrol { color: #fff; z-index: 99; width: 30px; height: 30px; font-size: 20px; background: #222; position: relative; right: 14px !important; bottom: 11px !important; border-radius: 3px !important; } #topcontrol:after { /*top: -2px;*/ left: 8.5px; content: "\f106"; position: absolute; text-align: center; font-family: FontAwesome; } #topcontrol:hover { color: #fff; background: #18ba9b; -webkit-transition: all 0.3s ease-in-out; -moz-transition: all 0.3s ease-in-out; -o-transition: all 0.3s ease-in-out; transition: all 0.3s ease-in-out; } </style> </head> <body> <div class="navbar-wrapper"> <div class="container"> <nav class="navbar navbar-inverse navbar-fixed-top" role="navigation"> <div class="container"> <div class="navbar-header"> <a class="navbar-brand" href="index.html" style="font-size:32px;">尚筹网-创意产品众筹平台</a> </div> <div id="navbar" class="navbar-collapse collapse" style="float:right;"> <ul class="nav navbar-nav"> <li class="dropdown"> <a href="#" class="dropdown-toggle" data-toggle="dropdown"><i class="glyphicon glyphicon-user"></i> [[${session.loginMember.username}]]<span class="caret"></span></a> <ul class="dropdown-menu" role="menu"> <li><a href="member.html"><i class="glyphicon glyphicon-scale"></i> 会员中心</a></li> <li><a href="#"><i class="glyphicon glyphicon-comment"></i> 消息</a></li> <li class="divider"></li> <li><a href="index.html"><i class="glyphicon glyphicon-off"></i> 退出系统</a></li> </ul> </li> </ul> </div> </div> </nav> </div> </div> <div class="container"> <div class="row clearfix"> <div class="col-sm-3 col-md-3 column"> <div class="row"> <div class="col-md-12"> <div class="thumbnail" style=" border-radius: 0px;"> <img src="img/services-box1.jpg" class="img-thumbnail" alt=""> <div class="caption" style="text-align:center;"> <h3> [[${session.loginMember.username}]] </h3> <span class="label label-danger" style="cursor:pointer;" onclick="window.location.href='accttype.html'">未实名认证</span> </div> </div> </div> </div> <div class="list-group"> <div class="list-group-item active"> 资产总览<span class="badge"><i class="glyphicon glyphicon-chevron-right"></i></span> </div> <div class="list-group-item " style="cursor:pointer;" onclick="window.location.href='minecrowdfunding.html'"> 我的众筹<span class="badge"><i class="glyphicon glyphicon-chevron-right"></i></span> </div> </div> </div> <div class="col-sm-9 col-md-9 column"> <blockquote style="border-left: 5px solid #f60;color:#f60;padding: 0 0 0 20px;"> <b> 我的钱包 </b> </blockquote> <div id="main" style="width: 600px;height:400px;"></div> <blockquote style="border-left: 5px solid #f60;color:#f60;padding: 0 0 0 20px;"> <b> 理财 </b> </blockquote> <div id="main1" style="width: 600px;height:400px;"></div> <blockquote style="border-left: 5px solid #f60;color:#f60;padding: 0 0 0 20px;"> <b> 比例 </b> </blockquote> <div id="main2" style="width: 600px;height:400px;"></div> </div> </div> </div> <div class="container" style="margin-top:20px;"> <div class="row clearfix"> <div class="col-md-12 column"> <div id="footer"> <div class="footerNav"> <a rel="nofollow" href="http://www.atguigu.com">关于我们</a> | <a rel="nofollow" href="http://www.atguigu.com">服务条款</a> | <a rel="nofollow" href="http://www.atguigu.com">免责声明</a> | <a rel="nofollow" href="http://www.atguigu.com">网站地图</a> | <a rel="nofollow" href="http://www.atguigu.com">联系我们</a> </div> <div class="copyRight"> Copyright ?2017-2017atguigu.com 版权所有 </div> </div> </div> </div> </div> <script src="jquery/jquery-2.1.1.min.js"></script> <script src="bootstrap/js/bootstrap.min.js"></script> <script src="script/docs.min.js"></script> <script src="script/back-to-top.js"></script> <script src="script/echarts.js"></script> <script> $('#myTab a').click(function (e) { e.preventDefault() $(this).tab('show') }) $('#myTab1 a').click(function (e) { e.preventDefault() $(this).tab('show') }) var myChart = echarts.init(document.getElementById('main')); // 指定图表的配置项和数据 option = { title: { text: '七日年化收益率(%)' }, tooltip: { trigger: 'axis' }, legend: { data:['基金','股票'] }, toolbox: { show: false, feature: { dataZoom: { yAxisIndex: 'none' }, dataView: {readOnly: false}, magicType: {type: ['line', 'bar']}, restore: {}, saveAsImage: {} } }, xAxis: { type: 'category', boundaryGap: false, data: ['2017-05-16','2017-05-17','2017-05-18','2017-05-19','2017-05-20','2017-05-21','2017-05-22'] }, yAxis: { type: 'value', axisLabel: { formatter: '{value} ' } }, series: [ { name:'基金', type:'line', data:[1, 1, 5, 3, 2, 3, 2], markPoint: { data: [ {type: 'max', name: '最大值'}, {type: 'min', name: '最小值'} ] }, markLine: { data: [ {type: 'average', name: '平均值'} ] } }, { name:'股票', type:'line', data:[1, -2, 2, 5, 3, 2, 4], markPoint: { data: [ {name: '周最低', value: -2, xAxis: 1, yAxis: -1.5} ] }, markLine: { data: [ {type: 'average', name: '平均值'}, [{ symbol: 'none', x: '90%', yAxis: 'max' }, { symbol: 'circle', label: { normal: { position: 'start', formatter: '最大值' } }, type: 'max', name: '最高点' }] ] } } ] }; myChart.setOption(option); var myChart1 = echarts.init(document.getElementById('main1')); // 指定图表的配置项和数据 option1 = { color: ['#3398DB'], tooltip : { trigger: 'axis', axisPointer : { // 坐标轴指示器,坐标轴触发有效 type : 'shadow' // 默认为直线,可选为:'line' | 'shadow' } }, grid: { left: '3%', right: '4%', bottom: '3%', containLabel: true }, xAxis : [ { type : 'category', data : ['基金', '票据', '定期理财', '变现贷'], axisTick: { alignWithLabel: true } } ], yAxis : [ { type : 'value' } ], series : [ { name:'直接访问', type:'bar', barWidth: '60%', data:[10, 52, 200, 334, 390, 330, 220] } ] }; // 使用刚指定的配置项和数据显示图表。 myChart1.setOption(option1); var myChart2 = echarts.init(document.getElementById('main2')); // 指定图表的配置项和数据 option2 = { title : { text: '某站点用户访问来源', subtext: '纯属虚构', x:'center' }, tooltip : { trigger: 'item', formatter: "{a} <br/>{b} : {c} ({d}%)" }, legend: { orient: 'vertical', left: 'left', data: ['直接访问','邮件营销','联盟广告','视频广告','搜索引擎'] }, series : [ { name: '访问来源', type: 'pie', radius : '55%', center: ['50%', '60%'], data:[ {value:335, name:'直接访问'}, {value:310, name:'邮件营销'}, {value:234, name:'联盟广告'}, {value:135, name:'视频广告'}, {value:1548, name:'搜索引擎'} ], itemStyle: { emphasis: { shadowBlur: 10, shadowOffsetX: 0, shadowColor: 'rgba(0, 0, 0, 0.5)' } } } ] }; // 使用刚指定的配置项和数据显示图表。 myChart2.setOption(option2); </script> </body> </html>
12.4.4 退出登录
- 修改 member-center.html
<li><a href="index.html" th:href="@{/auth/member/logout}"><i class="glyphicon glyphicon-off"></i> 退出系统</a></li>
- handler
@RequestMapping("/auth/member/logout") public String logout(HttpSession session) { session.invalidate(); return "redirect:/"; }