基于Spring+SpringMVC+MyBatis博客系统的开发教程(十二)

第12课:个人资料修改页面

正式开始本文内容前,我们先做下准备,即在 WEB-INF 下的 personal 文件夹下导入个人资料修改页面 profile.jsp 文件。

通过访问个人主页的修改个人资料进入个人资料修改页面,如图:

点击事件如下:

<a href="${ctx}/profile"><i class="icon icon-edit"></i><span style="margin-left: 10px">修改个人资料</span></a>

如果不想 a 标签出现下划线,可将 a 标签的 style 属性的 text-decoration 值设置为 none,如下:

style="text-decoration: none"

因为个人资料页面 profile.jsp 在 WEN-INF 下,所以要经过 Controller 处理后跳转,在 PersonalController.java 内添加映射 URL 为 /profile 的方法,如下:

  @Autowired
    private UserInfoService userInfoService;

    /**
     * 进入个人资料修改页面
     * @param model
     * @return
     */
    @RequestMapping("/profile")
    public String profile(Model model) {
        User user = (User)getSession().getAttribute("user");
        if(user==null){
            return "../login";
        }
        UserInfo userInfo =   userInfoService.findByUid(user.getId());
        model.addAttribute("user",user);
        model.addAttribute("userInfo",userInfo);

        return "personal/profile";
    }

代码解读如下:

(1)从 Session 中取出用户信息,判断用户是否登录,未登录则跳转登录。

(2)用户登录后,根据用户 id 查询出用户详细信息 userInfo,将 user 和 userInfo 都放入 model 中。

(3)返回到 personal 目录下的 profile.jsp 页面。

重启项目后,点击修改个人资料进入个人资料修改页面,效果如下:

个人资料修改页面准备分四部分来讲:

  1. 修改个人头像
  2. 基本设置
  3. 账号设置
  4. 绑定设置

修改个人头像

思路就这样的:通过点击头像图片,弹出选择图片窗口,弹出选择窗口需要将 input 框的 type 设置为 file,选择好图片以后,触发 onchange 事件,发送AJAX,通过提交 form 表单将图片上传,success 回调函数返回上传图片路径,将原图片的 src 路径替换,完成修改个人头像。

个人头像 a 标签及 form 表单代码如下:

     <a  title="${user.nickName}" class="avatar"><img id="img-change" src="${user.imgUrl}" onclick="selectImg();" width="100" height="100" style="border-radius:50%;margin-top: 60px;margin-left: 90px"></a>
     <form id="upload-form"   style="width:auto;" >
         <input type="file" id="change-img" name="uploadImg" onchange="changeImg();" style="display:none;">
      </form>

1. 事件源

头像的点击事件 onclick 具体方法如下:

    //点击图片事件
    function selectImg() {
        document.getElementById("change-img").click();
    }

获取 id 为 change-img 的对象,触发其点击事件,即弹出图片选择窗口。图片选择完毕以后触发 onchange 事件,onchange 事件方法如下:

//图片选择后事件
    function changeImg() {
    var formData = new FormData($( "#upload-form" )[0]);
    $.ajax({
        url: '/fileUpload' ,
        type: 'POST',
        data: formData,
        async: false,
        cache: false,
        contentType: false,
        processData: false,
        success: function (data) {
            var msg = data["error"];
            if(msg==0){
                //上传成功
                var url = data["url"];
                document.getElementById("img-change").src = url;
                saveImg(url);
            }

        }
    });
    }

    //保存个人头像
    function saveImg(url) {
    $.ajax({
        type:'post',
        data: {"url":url},
        url: '/saveImage' ,
        dataType:'json',
        success: function (data) {
          // alert(data["msg"]);
        }
    });
    }

代码解读如下:

(1)根据 form 表单的 id 通过 new FormData 获取表单对象数据,赋值给 formData。

(2)发送 AJAX,映射 URL 为:/fileUpload,对应之前上传文件的 UploadController,上传方式为 post 方式,请求参数 data 为 formData。

(3)回调函数 success 返回数据 data,通过 data["error"] 获取上传状态,如果为0说明上传成功,根据 data["url"] 获取上传成功后的图片路径赋值给 URL,然后根据 id 获取 img 标签对象,将其 src 属性值重新赋值为 URL,实现更换头像。

(4)然后调用保存头像方法 saveImg,接收的参数是刚才回调函数返回的URL。

(5)保存头像的方法也是一个 AJAX 异步请求,请求参数 data 是图片的路径 URL,回调函数可返回保存头像的结果,成功还是失败。

2. Java 后台

对应映射 URL 为 /fileUpload 的方法还是之前说过的 UploadController 内的上传文件方法,这里不再赘述。

在 PersonalController 内添加映射 URL 为 /saveImage 的方法:

    @Autowired
    private UserService userService;
    /**
     * 保存个人头像
     * @param model
     * @param url
     * @return
     */
    @RequestMapping("/saveImage")
    @ResponseBody
    public  Map<String,Object>  saveImage(Model model,@RequestParam(value = "url",required = false) String url) {
        Map map = new HashMap<String,Object>(  );
        User user = (User)getSession().getAttribute("user");
        user.setImgUrl(url);
        userService.update(user);
        map.put("msg","success");
        return map;
    }

代码解读如下:

(1)从 Session 中获取用户信息。

(2)将前台传过来的 URL 通过 set 方法赋值给 user 的 imgUrl 属性。

(3)调用 userService 的 update 方法更新用户信息。

(4)将 success 放入 map,返回给前台。

3. 重新启动项目,点击更换头像,效果如下:

发现

个人头像修改之后,发现首页的文章作者头像并未改变。这是我之前挖的一个坑,肯定会有人和我想的一样,想着查询文章信息的时候把用户的昵称和头像也一起查出来,方便省事,事实证明这样做是不好的。因为用户更新了昵称或者个人头像时,都要去更新一遍 user_content 表的 nick_name 和 img_url 字段,这样频繁操作数据库太耗性能。应该怎么解决呢?

解决方法就是使用两表关联查询。查询文章的时候根据用户 id 关联查询用户表 uesr 和文章表 user_content。这里使用左连接查询:

    select u1.*,u2.nick_name name,u2.img_url url from user_content u1 LEFT JOIN user u2 on u1.u_id = u2.id

代码解读如下:

(1)给 user_content 表起别名为 u1,给 user 表取别名为 u2,起别名主要用于区分和操作表。

(2)select u1.*,u2.nick_name name,u2.img_url url是指查询 user_content 表中的所有字段、user 表中的 nick_name 和 img_url 字段,并给 nick_name 起别名为 name,给 img_url 起别名为 url。

(3)from user_content u1 LEFT JOIN user u2 是指查询从 user_content 表左连接 user 表,LEFT JOIN 左连接,是指查询的结果以左表为主表,右表为副表,即以 user_content 表为主表,user 表为副表,副表没有数据的字段用 null 补充。

(4)on u1.u_id = u2.id是指连接条件:user_content 表的 u_id 和 user 表的 id 要相同。

查询效果演示如图:

具体步骤如下:

1. 在 UserContentMapper 接口中增加连接查询方法:

     /**
     * user_content与user连接查询
     * @return
     */
    List<UserContent> findByJoin(UserContent userContent);

2. 在映射文件 userContent.xml 中增加查询 SQL:

 <!--user_content和user表连接查询-->
    <select id="findByJoin"  resultMap="joinMap">
       select u1.id,u1.u_id,u1.title,u1.category,u1.personal,u1.rpt_time,u1.upvote,u1.downvote,u1.comment_num,u1.content,u2.nick_name nickName,u2.img_url imgUrl from user_content u1 LEFT JOIN user u2 on u1.u_id = u2.id
       <where>
           <choose>
               <when test='id!=null and id!=""'>
                    u1.id = #{id}
               </when>
               <otherwise>
                   <if test='personal!=null and personal!=""'>
                       u1.personal = #{personal}
                   </if>
                   <if test='personal==null or personal==""'>
                       u1.personal = '0'
                   </if>
               </otherwise>
           </choose>

       </where>
        <if test='uId!=null and uId!=""'>
            and u1.u_id = #{uId}
        </if>
      order by u1.rpt_time desc
    </select>

    <resultMap type="wang.dreamland.www.entity.UserContent" id="joinMap">
        <!-- property 表示wang.dreamland.www.entity.UserContent; column 表示表中的列名 -->
        <id property="id" column="id" />
        <result property="uId" column="u_id" />
        <result property="title" column="title" />
        <result property="category" column="category" />
        <result property="personal" column="personal" />
        <result property="rptTime" column="rpt_time" />
        <result property="imgUrl" column="img_url" />
        <result property="nickName" column="nick_name" />
        <result property="upvote" column="upvote" />
        <result property="downvote" column="downvote" />
        <result property="commentNum" column="comment_num" />
        <result property="content" column="content" />
    </resultMap>

代码解读如下:

(1)id=findByJoin 对应的是 UserContentMapper 接口中的方法名,joinMap 对应某个 resultMap 的唯一 id。

(2)查询 SQL 中没有使用 select *语句,select * 会降低查询效率,这里把需要查询的字段一一列举出来。注意 其中没有 u1.img_url 和 u1.nick_name,如果加上会覆盖我们的查询结果,我们要的是 user 表中的 imgUrl 和 nickName,还有别名 nickName、imgUrl 要和 UserContent 实体类中的属性对应,主要是通过属性的 setter 方法赋值,页面通过 getter 方法取值的。

(3)where 标签,当标签内条件成立时会形成 where 语句,choose 标签是一个整体,里面的 when 和 otherwise 标签相当于 if 和 else 的作用,如果文章 id 不为空则根据文章 id 查询,否则如果 personal 为空,则默认查询非私密的所有文章,如果 personal 属性不为空,则通过 #{personal}以占位符的形式接收 personal 的值进行条件查询

(4)后面的 if 标签表示如果 uId 不为空,则增加查询条件,用 and 连接,并且根据用户 id 查询。

后面的 order by u1.rpt_time desc 是指按时间倒序。

(5)resultMap 标签是结果集的封装,其中 type 代表的是 UserContent 的实体类,给该 resultMap 取一个唯一的 id,供其他 SQL 语句引用。

result 标签中的 property 对应实体类的属性,column 对应表中的字段(列名),通过实体类属性的 setter 方法赋值。

其实这里可以将 user_content 表中的 img_url 和 nick_name 字段删除,将 UserContent 实体类中的 imgUrl 和 nickName 属性上加上 @Transient 注解,代表表中没有此字段。我这里就不操作了,你们可以试一下。

3. 将自定义过滤器 IndexJspFilter 中的方法修改如下:

     public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        System.out.println("===========自定义过滤器==========");
        ServletContext context = request.getServletContext();
        ApplicationContext ctx = WebApplicationContextUtils.getWebApplicationContext(context);
        UserContentMapper userContentMapper = ctx.getBean(UserContentMapper.class);
        PageHelper.startPage(null, null);//开始分页
        List<UserContent> list = userContentMapper.findByJoin(null);
        PageHelper.Page endPage = PageHelper.endPage();//分页结束
        request.setAttribute("page", endPage );
        chain.doFilter(request, response);
    }

直接调用 userContentMapper 的 findByJoin方法,参数为null,默认查询非私密文章。

4. 将实现类 UserContentServiceImpl 中的三个方法修改如下:

 public Page<UserContent> findAll(UserContent content, Integer pageNum, Integer pageSize) {
        //分页查询
        System.out.println("第"+pageNum+"页");
        System.out.println("每页显示:"+pageSize+"条");
        PageHelper.startPage(pageNum, pageSize);//开始分页
        List<UserContent> list = userContentMapper.findByJoin(content);
        Page endPage = PageHelper.endPage();//分页结束
        List<UserContent> result = endPage.getResult();
        return endPage;
    }

     public Page<UserContent> findAll(Integer pageNum, Integer pageSize) {
        //分页查询
        PageHelper.startPage(pageNum, pageSize);//开始分页
        List<UserContent> list = userContentMapper.findByJoin(null);
        Page endPage = PageHelper.endPage();//分页结束
        return endPage;
    }

    //根据文章id查询
    public UserContent findById(long id) {
        UserContent userContent = new UserContent();
        userContent.setId( id );
        List<UserContent> list = userContentMapper.findByJoin(userContent);
        if(list!=null && list.size()>0){
            return list.get(0);
        }else {
            return null;
        }
    }

主要是将原来使用 Mapper 的通用方法改成我们自定义的连接查询的方法。

根据文章 id 查询的时候如果有返回结果,只会有一个,所以这里取 list 集合的第一个元素,没有则返回 null。

 基本设置

首先说下,这里点击 tab 切换的原理和之前的个人主页的切换原理是一样的。都是通过显示和隐藏来达到切换的效果。比如点击基本设置对应的点击事件如下:

	function base_set() {
    document.getElementById("base").style.backgroundColor = "white";
    document.getElementById("account").style.backgroundColor = "#D1D1D1";
    document.getElementById("binding").style.backgroundColor = "#D1D1D1 ";

    document.getElementById("set_title").innerHTML = "基本设置";

    document.getElementById("base_content").style.display = "";
    document.getElementById("account_content").style.display = "none";
    document.getElementById("binding_content").style.display = "none";

    }

代码解读如下:

(1)将“基本设置”的背景色设置为白色,“账号设置”和“绑定设置”的背景色设置为 #D1D1D1,和左边背景色一致。

(2)将右侧的标题改为“基本设置”。

(3)将“基本设置”的 display 属性设置为空,即可见。“账号设置”和“绑定设置”的 display 属性设置为 none,即不可见。

右边 div 是一个普通的 form 表单,很简单,这里只说下生日对应的 input 框,我们点击 input 框后弹出日期插件,可供我们选择日期。

日期插件

这里使用的是 ZUI 提供的日期插件,

使用方法说的很明白,主要做三件事情。

1. 单独引入日期插件的 CSS 和 JS 文件。

     <link href="${ctx}/css/zui/lib/datetimepicker/datetimepicker.min.css" rel="stylesheet">
     <script src="${ctx}/css/zui/lib/datetimepicker/datetimepicker.min.js"></script>

2. 给 input 框加上指定的 class 属性 form-control form-date

     <input  style="width: 198px;float: right;margin-right: 484px;margin-top: -4px" class="form-control form-date"  readonly="readonly" placeholder="选择一个日期:yyyy-MM-dd" type="text" id="txtEndDate"  name="birthday" value="${userInfo.formateBirthday==null?"":userInfo.formateBirthday}"/><br/><br/>

其它的样式是我加上去的,一个靠右浮动,调整 input 框的长度,以及距离右边的距离,主要是为了与页面协调。

value 中的 EL 表达式是判断 userInfo 的生日是否为空,如果不为空则显示其生日,主要是一个生日信息回显的作用。因为 UserInfo 实体类中没有 getFormateBirthday 方法,所以创建此方法:

     public String getFormateBirthday(){
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
        return simpleDateFormat.format(birthday);
    }

主要是将日期类型 birthday 转成字符串类型。

readonly="readonly" 是只读属性,只允许用户选择日期,不可手动输入。

3. 手动调用初始化函数。

    // 仅选择日期
    $(".form-date").datetimepicker(
        {
            language:  "zh-CN",
            weekStart: 1,
            todayBtn:  1,
            autoclose: 1,
            todayHighlight: 1,
            startView: 2,
            minView: 2,
            forceParse: 0,
            format: "yyyy-mm-dd"
        });

主要是根据 class 属性获取对象,然后调用 datetimepicker 方法进行一些初始化操作。

查看页面效果如下:

感觉还不错!因为我们是做 Java 后台的,页面效果这块肯定没有前端写的好,所以我们要学会利用一些前端框架,将一些组件和插件直接拿来使用。开发时间长了之后,慢慢的也会掌握一些前端开发技能。

保存个人信息

1. 首先看下 form 标签:

       <form id="userInfo_form" action="${ctx}/saveUserInfo" method="post">

action 对应的后台映射 URL 为 saveUserInfo,请求方式为 post 请求。

2. 点击保存对应的点击事件如下:

    function saveUserInfo() {
        $("#userInfo_form").submit();
    }

很简单,只有一行代码,就是获取 form 表单对象之后,调用它的 submit 方法进行提交表单。

3. PersonalController.java 中创建映射 URL saveUserInfo 的方法:

 @Autowired
    private UserInfoService userInfoService;
    @Autowired
    private UserService userService;
    @RequestMapping("/saveUserInfo")
    public String saveUserInfo(Model model, @RequestParam(value = "name",required = false) String name ,
                               @RequestParam(value = "nick_name",required = false) String nickName,
                               @RequestParam(value = "sex",required = false) String sex,
                               @RequestParam(value = "address",required = false) String address,
                               @RequestParam(value = "birthday",required = false) String birthday){
        User user = (User) getSession().getAttribute("user");
        if(user==null){
            return "../login";
        }
        UserInfo userInfo = userInfoService.findByUid(user.getId());
        boolean flag = false;
        if(userInfo == null){
            userInfo = new UserInfo();
        }else {
            flag = true;
        }
        userInfo.setName(name);
        userInfo.setAddress(address);
        userInfo.setSex(sex);
        Date bir =  DateUtils.StringToDate(birthday,"yyyy-MM-dd");
        userInfo.setBirthday(bir);
        userInfo.setuId(user.getId());
        if(!flag){
            userInfoService.add(userInfo);
        }else {
            userInfoService.update(userInfo);
        }

        user.setNickName(nickName);
        userService.update(user);

        model.addAttribute("user",user);
        model.addAttribute("userInfo",userInfo);
        return "personal/profile";
    }

代码解读如下:

(1)首先从 Session 中取出 User,判断是否为空,如果为空,跳转到登录页面。

(2)User 不为空后,根据用户 id 查询出 UserInfo 信息,如果信息不存在,则 new 一个 UserInfo,默认 flag 为 flase,信息存在则 flag 置为 true。

(3)通过set方法将前台传来的参数封装到UserInfo对象中,如果flag 为 false,则代表是插入用户详细信息。如果 flag 为 true 则代表是更新用户详细信息。

(4)更新用户表的昵称信息。

(5)将 user 和 userInfo 对象添加到 model 中,然后返回到个人信息修改页面。

重新启动项目,填写个人信息保存后效果如图:

账号设置

点击“账号设置”切换 tab 和改变右边信息这里就不说了,和上面的原理一样,页面效果如下:

修改密码

首先在 WEB-INF/personal/ 目录下引入 repassword.jsp 和 passwordSuccess.jsp,分别是修改密码页面和密码修改成功页面。

修改密码对应的 a 标签:

    &nbsp;&nbsp;<a href="${ctx}/repassword"><span id="password_span" style="color: grey" onmouseover="changeColor(this);" onmouseout="backColor(this);" >修改</span></a>

其中,&nbsp; 代表一个空格。onmouseover 鼠标悬浮改变字体颜色为紫色,onmouseout 鼠标移除时字体颜色变为灰色。

a 标签的 href 属性链接的 URL 为 /repassword,在 PersonalController 中创建与之对应的方法,如下:

    @RequestMapping("/repassword")
    public String repassword(Model model) {
        User user = (User) getSession().getAttribute("user");
        if(user!=null) {
            model.addAttribute("user",user);
            return "personal/repassword";
        }
        return "../login";
    }

主要是从 Session 获取用户 User,判断如果不为空,则把 user 添加到 model 然后返回到修改密码页面,否则跳转到登录页面。

重新启动项目,进入个人信息修改页面,点击密码的“修改”进入修改密码页面,如下:

需要对密码框做如下校验:

  1. 密码长度不能小于6位;
  2. 旧密码和新密码不能一样;
  3. 新密码和确认密码输入必须一致。

1. 旧密码 input 框的离焦事件如下:

    var f1 = false;
    function oldPassword() {
    var old =  $("#old_password").val();
    if(old==null || old.trim()==''){
       document.getElementById("old_span").innerHTML = "请输入密码!";
       f1 = false;
    }else if(old.length < 6){
       document.getElementById("old_span").innerHTML = "密码长度少于6位,请重新输入!";
       f1 = false;
    }
    else {
       document.getElementById("old_span").innerHTML = "";
       f1 = true;
    }
    }

代码解读如下:

(1)定义变量 f1 赋初始值为 false,表示该 input 框状态错误,不可提交表单。

(2)获取旧密码框输入的值,如果为空则提示请输入密码,如果密码长度低于6位则提示密码长度少于6位,请重新输入,f1 都为 false。

(3)否则清空 span 标签内内容,f1 置为true。

2. 新密码 input 框的离焦事件如下:

 var f2 = false;
    function newPassword() {
    var p = $("#password").val();
    var old =  $("#old_password").val();
    if(p==null || p.trim()==''){
        document.getElementById("old_span").innerHTML = "请输入密码!";
        f2 = false;
    }else if(p.length < 6){

            $("#old_span").text("密码长度少于6位,请重新输入!").css("color","red");
            f2 =  false;

    }else if(p==old){
        $("#old_span").text("新密码与旧密码一致,请重新输入!").css("color","red");
        f2 = false;
    }

    else {
        document.getElementById("old_span").innerHTML = "";
        f2 = true
    }
    }

代码解读如下:

(1)定义变量 f2 赋初始值为 false,表示该 input 框状态错误,不可提交表单。

(2)获取旧密码框和新密码框输入的值,如果新密码为空则提示请输入密码,如果新密码长度低于6位则提示密码长度少于6位,请重新输入,如果新密码和旧密码一致,则提示相应错误,f2 都为false。

(3)否则清空 span 标签内内容,f2 置为 true。

3. 确认密码 input 框的离焦事件如下:

     var f3 = false;
    function rePassword() {
        var p = $("#repassword").val();
        var p1 = $("#password").val();
        if(p==null || p.trim()==''){
            document.getElementById("old_span").innerHTML = "请输入密码!";
            f3 = false;
        }else if(p!=p1){
            document.getElementById("old_span").innerHTML = "两次密码不一致!";
            f3 = false;
        }
        else {
            document.getElementById("old_span").innerHTML = "";
            f3 = true
        }
    }

代码解读如下:

(1)定义变量 f3 赋初始值为 false,表示该 input 框状态错误,不可提交表单。

(2)获取新密码框和确认密码框输入的值,如果确认密码为空则提示请输入密码,如果确认密码和新密码不一致,则提示相应错误,f3 都为 false。

(3)否则清空 span 标签内内容,f3 置为true。

4. 提交表单

form 标签如下:

     <form action="${ctx}/updatePassword" method="post" id="update_password">

action 对应的请求 URL 为 /updatePassword,请求方式为 post 请求。

确认按钮的点击事件 surePost 方法如下:

    function surePost() {
    if(f1 && f2 && f3){
        $("#update_password").submit();
}<span class="hljs-keyword"><span class="hljs-keyword"><span class="hljs-keyword">else</span></span></span> {
    $(<span class="hljs-string"><span class="hljs-string"><span class="hljs-string">"#old_span"</span></span></span>).text(<span class="hljs-string"><span class="hljs-string"><span class="hljs-string">"请重新输入密码!"</span></span></span>).css(<span class="hljs-string"><span class="hljs-string"><span class="hljs-string">"color"</span></span></span>,<span class="hljs-string"><span class="hljs-string"><span class="hljs-string">"red"</span></span></span>);
}
}

 function surePost() {
    if(f1 && f2 && f3){
        $("#update_password").submit();

    }else {
        $("#old_span").text("请重新输入密码!").css("color","red");
    }
    }

如果上面的校验都成功则提交表单,否则提示错误“请重新输入密码!”。

在 PersonalController 中创建映射 URL 为 /updatePassword 的方法如下:

@RequestMapping("/updatePassword")
    public String updatePassword(Model model, @RequestParam(value = "old_password",required = false) String oldPassword,
                                 @RequestParam(value = "password",required = false) String password){

        User user = (User) getSession().getAttribute("user");
        if(user!=null) {
                oldPassword = MD5Util.encodeToHex(Constants.SALT + oldPassword);
                if (user.getPassword().equals(oldPassword)) {
                    password = MD5Util.encodeToHex(Constants.SALT + password);
                    user.setPassword(password);
                    userService.update(user);
                    model.addAttribute("message", "success");
                } else {
                    model.addAttribute("message", "fail");
                }
        }
        model.addAttribute("user",user);
        return "personal/passwordSuccess";
    }

代码解读如下:

(1)通过 Session 获取用户 User。

(2)如果用户不为空,则根据旧密码进行 MD5 加密后与数据库中的密码进行比较,如果相同则说明密码输入正确,将新密码进行 MD5 加密后替换掉旧密码,然后更新用户信息,并将“success”添加到 model 中,如果不相同则说明密码输入错误,则将“fail”添加到 model 中。

(3)将 user 添加到 model 中,返回修改密码成功页面。

重启项目,修改密码,第一次输入错误密码,修改失败后效果如图:

点击返回按钮,返回到修改密码页面。

第二次输入正确的密码,修改成功后效果如图:

点击重新登录(/loginout),即先退出登录然后返回到登录页面。

修改手机号

修改手机号这里就不实现了,具体思路如下:

  1. 点击手机号“修改”后首先进行密码确认或者手机验证码确认。
  2. 输入正确密码或者手机验证码后进入更换手机号页面。
  3. 输入手机号,手机号必须是非注册手机号,然后点击获取验证码,输入验证码正确则更换手机号成功!

绑定设置,我们将在下一课中介绍。

第12课百度网盘地址:

链接:https://pan.baidu.com/s/1WnKCHBFewqEfqljVRU947Q 密码:wfmm

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

exodus3

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值