day105:Mofang:设置页面初始化&更新头像/上传头像&设置页面显示用户基本信息

目录

1.设置页面初始化

2.更新头像

  1.点击头像进入更新头像界面

  2.更新头像页面初始化

  3.更新头像页面CSS样式

  4.头像上传来源选择:相册/相机

  5.调用api提供的本地接口从相册/相机提取图片

  6.后端提供头像更新的接口

  7.前端基于axios上传图片数据

3.本地更新头像信息

  1.头像上传成功后-关闭页面/将头像数据存储在前端

  2.setting.js提供avatar_url头像访问地址

  3.后端提供展示头像的视图方法

  4.设置页面头像立即刷新

  5.前端页面显示当前登录用户的基本信息

  6.后端提供用户基本信息的API接口

1.设置页面初始化

1.设置页面setting.html初始化

APP项目中对于用户的退出登录,一般都在设置中进行。

客户端新增配置页面setting.html,代码:

<!DOCTYPE html>
<html>
<head>
    <title>用户中心</title>
    <meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">
    <meta charset="utf-8">
    <link rel="stylesheet" href="../static/css/main.css">
    <script src="../static/js/vue.js"></script>
    <script src="../static/js/axios.js"></script>
    <script src="../static/js/main.js"></script>
    <script src="../static/js/uuid.js"></script>
    <script src="../static/js/settings.js"></script>
</head>
<body>
    <div class="app user setting" id="app">
        <div class="bg">
      <img src="../static/images/form_bg.png">
    </div>
        <!-- 1.设置页面的返回按钮 -->
        <img class="back" @click="goto_home" src="../static/images/user_back.png" alt="">
    <div class="form">
      <div class="item avatar">
        <span class="title">头像</span>
        <span class="goto">&gt;</span>
        <span class="value">
          <img src="../static/images/avatar.png" alt="">
        </span>
      </div>
      <div class="item">
        <span class="title">昵称</span>
        <span class="goto">&gt;</span>
        <span class="value">iR.Poke</span>
      </div>
      <div class="item">
        <span class="title">手机号</span>
        <span class="goto">&gt;</span>
        <span class="value">134****9284</span>
      </div>
      <div class="item">
        <span class="title">登陆密码</span>
        <span class="value"></span>
        <span class="goto">&gt;</span>
      </div>
      <div class="item">
        <span class="title">交易密码</span>
        <span class="value"></span>
        <span class="goto">&gt;</span>
      </div>
      <div class="item">
        <span class="title">地址管理</span>
        <span class="value"></span>
        <span class="goto">&gt;</span>
      </div>
      <div class="item">
        <span class="title">设备管理</span>
        <span class="value"></span>
        <span class="goto">&gt;</span>
      </div>
      <div class="item logout">
         <!-- 2.设置页面的切换账号 --> 
        <img @click="change_account" src="../static/images/change_account.png" alt="">
         <!--3.设置页面的退出账号 --> 
        <p @click="logout">退出账号</p>
      </div>
    </div>
    </div>
    <script>
    apiready = function(){
        init();
        new Vue({
            el:"#app",
            data(){
                return {
                    prev:{name:"",url:"",params:{}},
                    current:{name:"setting",url:"setting.html",params:{}},
                }
            },
            methods:{
        goto_home(){
          // 回到个人中心界面
          this.game.goFrame("user","user.html",this.current);
        },
        change_account(){
          // 切换账号
          this.game.goFrame("login","login.html", this.current);
        },
        logout(){
          // 退出账号
          api.actionSheet({
              title: '您确认要退出当前登录吗?',
              cancelTitle: '取消',
              destructiveTitle: '退出登录'
          }, (ret, err)=>{
              if( ret ){
                  this.game.print(ret);
                   if(ret.buttonIndex==1){
                     this.game.save({"access_token":"","refresh_token":""});
                     this.game.fremove(["access_token","refresh_token"]);
                     this.game.outWin("user");
                   }
              }
          });

        }
            }
        });
    }
    </script>
</body>
</html>

设置页面setting.html初始化

2.设置页面的CSS样式

static/css/main.css,样式代码:

.setting .bg img{
  animation: normal;
}
.setting .back{
  top: 4rem;
}
.setting .form {
  top: 9rem;
}
.setting .form .item{
  height: 3.9rem;
  line-height: 3.9rem;
  font-size: 1.25rem;
  text-indent: 0.6rem;
  border-bottom: 1px solid rgba(204,153,102,0.2);
}
.setting .form .avatar{
  height: 6.11rem;
  line-height: 6.11rem;
}
.setting .form .avatar img{
  width: 4.56rem;
  height: 4.56rem;
  vertical-align: middle;
}
.setting .form .item .value,
.setting .form .item .goto{
  float: right;
}
.setting .form .logout{
  margin-top: 3rem;
  text-align: center;
  height: 4rem;
  line-height: 2rem;
}
.setting .form .logout img{
  width: 11rem;
}

设置页面的CSS样式

3.用户界面点击设置按钮跳转到设置界面

<!-- 点击设置按钮去到设置界面 -->
<img class="setting" @click="goto_setting" src="../static/images/setting.png" alt="">
        
<script>
    apiready = function(){
        init();
        new Vue({
            el:"#app",
            data(){
                return {
                    prev:{name:"",url:"",params:{}},
                    current:{name:"user",url:"user.html",params:{}},
                }
            },
            methods:{
                    goto_setting(){
                        this.game.goFrame("setting","setting.html", this.current);
                    }
            }
        });
    }
    </script>
</body>
</html>

2.更新头像

1.点击头像进入更新头像界面

点击头像进入更新头像页面,setting.html,代码:

<span class="value">
    <!-- 点击头像进入到更新头像界面 -->
    <img @click="update_avatar_frame" src="../static/images/avatar.png" alt="">
</span>
     
<script>

    update_avatar_frame(){
        // 点击头像,跳到更新头像的界面:avatar.html
        this.game.goFrame("avatar","avatar.html", this.current,null,{
            type:"push",             //动画类型(详见动画类型常量)
            subType:"from_top",    //动画子类型(详见动画子类型常量)
            duration:300             //动画过渡时间,默认300毫秒
        })
    }

</script>

2.更新头像页面初始化

avatar.html,显示页面,代码:

<!DOCTYPE html>
<html>
<head>
    <title>用户中心</title>
    <meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">
    <meta charset="utf-8">
    <link rel="stylesheet" href="../static/css/main.css">
    <script src="../static/js/vue.js"></script>
    <script src="../static/js/axios.js"></script>
    <script src="../static/js/main.js"></script>
    <script src="../static/js/uuid.js"></script>
    <script src="../static/js/settings.js"></script>
</head>
<body>
    <div class="app frame avatar" id="app">
    <div class="box">
      <p class="title">上传头像</p>
      <img class="close" src="../static/images/close_btn1.png" alt="">
      <div class="content">
        <p class="header">!注意事项</p>
        <p class="text">禁止使用有诱导性的内容,二维码,联系方式等违规违法违约的图片,一经发现,永久封号,
          并保留追究法律责任的权利。</p>
      </div>
      <img @click="update_avatar_confirm" class="btn" src="../static/images/yes.png" alt="">
    </div>
    </div>
    <script>
    apiready = function(){
        init();
        new Vue({
            el:"#app",
            data(){
                return {
                    prev:{name:"",url:"",params:{}},
                    current:{name:"avatar",url:"avatar.html",params:{}},
                }
            },
            methods:{
        update_avatar_confirm(){
          // 确认上传头像

        },
        upload_avatar(ret){
          // 头像上传处理
          
        }
            }
        });
    }
    </script>
</body>
</html>

更新头像页面avatar.html初始化

3.更新头像页面CSS样式

main.css,页面样式,代码:

.avatar.frame{
  background-color: rgba(0,0,0,0.6);
}
.avatar{
  overflow: hidden;
}
.avatar .box{
  width: 28.89rem;
  height: 34.44rem;
  background: url("../images/board_bg1.png") no-repeat 0 0;
  background-size: 100%;
  position: absolute;
  top: 11rem;
  margin: 0 auto;
  left: 0;
  right: 0;
}

.avatar .box .title{
  color: #fff;
  font-size: 2rem;
  text-align: center;
  margin-top: 2.8rem;
}
.avatar .box .close{
  width: 5.22rem;
  height: 5.78rem;
  position: absolute;
  right: 0;
  top: 8rem;
}
.avatar .box .header{
  margin-top: 3.6rem;
  font-size: 1.8rem;
  text-align: center;
  color: #ff3333;
  font-weight: bold;
}
.avatar .box .text{
  width: 16.67rem;
  margin: 1.4rem auto 0;
  font-size: 1.22rem;
  color: #ffffcc;
}
.avatar .box .btn{
  display: block;
  width: 12.22rem;
  height: 4.55rem;
  margin: 1.4rem auto 0;
}

更新头像页面CSS样式

4.头像上传来源选择:相册/相机

头像上传来源选择,avatar.html,代码:

<!-- 1.关闭更新头像界面: close_frame -->
<img class="close" @click="close_frame" src="../static/images/close_btn1.png" alt="">
<div class="content">
    <p class="header">!注意事项</p>
    <p class="text">禁止使用有诱导性的内容,二维码,联系方式等违规违法违约的图片,一经发现,永久封号,
        并保留追究法律责任的权利。</p>
</div>
<!-- 2.头像上传来源选择 :相册/相机 -->
<img @click="update_avatar_confirm" class="btn" src="../static/images/yes.png" alt="">
</div>

    <script>
    
        close_frame(){
          this.game.outFrame("avatar");
        },
            
        update_avatar_confirm(){
          // 确认上传头像的方式
          api.actionSheet({
              title: '请选择上传头像的来源',
              cancelTitle: '取消',
              buttons: ['相册','相机'],
          }, function(ret, err){
              if( ret ){
                   alert( JSON.stringify( ret ) );
              }else{
                   this.game.print( err );
              }
          });

        },
        
    </script>

5.调用api提供的本地接口从相册/相机提取图片

接下来,调用apicloud提供的本地接口从相册或者相机中提取图片.

avatar.html代码

<!-- 1.关闭更新头像界面: close_frame -->
<img class="close" @click="close_frame" src="../static/images/close_btn1.png" alt="">
<div class="content">
    <p class="header">!注意事项</p>
    <p class="text">禁止使用有诱导性的内容,二维码,联系方式等违规违法违约的图片,一经发现,永久封号,
        并保留追究法律责任的权利。</p>
</div>
<!-- 2.头像上传来源选择 :相册/相机 -->
<img @click="update_avatar_confirm" class="btn" src="../static/images/yes.png" alt="">
</div>
    <script>
    
        close_frame(){
          this.game.outFrame("avatar");
        },
        update_avatar_confirm(){
          // 确认上传头像的方式
          api.actionSheet({
              title: '请选择上传头像的来源',
              cancelTitle: '取消',
              buttons: ['相册','相机'],
          }, (ret, err)=>{
              if( ret ){
                   var sourceType = ["album","camera"];
                   if(ret.buttonIndex > sourceType.length){
                     // 如果用户选择了取消,则关闭当前修改头像的页面
                     this.game.outFrame("avatar");
                     return;
                   }
                   // ***使用APIcloud提供的api.getPicture方法从相机/相册获取图片***
                   api.getPicture({
                       sourceType: sourceType[ret.buttonIndex-1],
                       mediaValue: 'pic',
                       destinationType: 'base64',
                       allowEdit: true,
                       preview:true,
                       quality: 50,
                       targetWidth: 100,
                       targetHeight: 100,
                       saveToPhotoAlbum: true,
                   }, (ret, err)=>{
                       if(ret){
                            this.game.print(ret);
                       }else{
                            this.game.print(err);
                       }
                   });

                   alert( JSON.stringify( ret ) );
              }else{
                   this.game.print( err );
              }
          });

        },
        upload_avatar(ret){
          // 头像上传处理

        }
            }
        });
    }
    </script>
</body>
</html>

6.后端提供头像更新的接口

服务端提供头像更新接口,application/apps/users/views.py,代码

import base64, uuid,os
@jsonrpc.method("User.avatar.update")
@jwt_required # 验证jwt
def update_avatar(avatar):
    """获取用户信息"""
    # 1.接受客户端上传的头像信息
    ext = avatar[avatar.find("/")+1:avatar.find(";")]  # 资源格式:jpeg/jpg/png....
    b64_avatar = avatar[avatar.find(",")+1:]
    b64_image = base64.b64decode(b64_avatar)
    
    # 2.使用uuid给头像文件生成随机的文件名
    filename = uuid.uuid4()
    
    # 3.拼接头像文件在后端存储的路径
    static_path = os.path.join( current_app.BASE_DIR,current_app.config["STATIC_DIR"] )
    with open("%s/%s.%s" % (static_path, filename,ext),"wb") as f:
        f.write(b64_image)
    
    # 4.查询当前用户是否存在
    current_user_id = get_jwt_identity()
    user = User.query.get(current_user_id)
    if user is None:
        return {
            "errno": status.CODE_NO_USER,
            "errmsg": message.user_not_exists,
        }
    
    # 5.将用户的头像数据存储到mysql数据库中
    user.avatar = "%s.%s" % (filename,ext)
    db.session.commit()
    return {
        "errno": status.CODE_OK,
        "errmsg": message.avatar_save_success,
        "avatar": "%s.%s" % (filename,ext)
    }

在项目后端全局配置文件中添加静态文件目录存储路径

application/settings/__init__.py,代码:

# 静态文件目录存储路径
STATIC_DIR = "application/static"

7.前端基于axios上传图片数据

客户端基于axios上传图片数据,avatar.html代码

<!-- 1.关闭更新头像界面: close_frame -->
<img class="close" @click="close_frame" src="../static/images/close_btn1.png" alt="">
<div class="content">
    <p class="header">!注意事项</p>
    <p class="text">禁止使用有诱导性的内容,二维码,联系方式等违规违法违约的图片,一经发现,永久封号,
        并保留追究法律责任的权利。</p>
</div>
<!-- 2.头像上传来源选择 :相册/相机 -->
<img @click="update_avatar_confirm" class="btn" src="../static/images/yes.png" alt="">
</div>
    <script>
    
        close_frame(){
          this.game.outFrame("avatar");
        },
        update_avatar_confirm(){
          api.actionSheet({
              title: '请选择上传头像的来源',
              cancelTitle: '取消',
              buttons: ['相册','相机'],
          }, (ret, err)=>{
              if( ret ){
                   var sourceType = ["album","camera"];
                   if(ret.buttonIndex > sourceType.length){
                     this.game.outFrame("avatar");
                     return;
                   }
                   api.getPicture({
                      ...
                   }, (ret, err)=>{
                       if(ret){
                               // 1.获取本地图片成功后,开始上传图片
                            this.upload_avatar(ret);
                       }else{
                            this.game.print(err);
                       }
                   });

              }else{
                   this.game.print( err );
              }
          });

        },
        // 头像上传处理
        upload_avatar(ret){
          
          var token = this.game.get("access_token") || this.game.fget("access_token");
          if(!token){
            this.game.goFrame("login","login.html", this.current);
            return ;
          }
          // 2.前端基于axios上传头像
          this.axios.post("",{
                        "jsonrpc": "2.0",
                        "id": this.uuid(),
                        "method": "User.avatar.update",
                        "params": {
                            "avatar": ret.base64Data,
                        }
                    },{
            headers:{
              Authorization: "jwt " + token,
            }
          }).then(response=>{
            if(parseInt(response.data.result.errno)==1000){
              this.game.print(response);
            }else{
              this.game.print(response.data.result);
            }
          }).catch(error=>{
            // 网络等异常
            this.game.print(error);
          })
        }
            }
        });
    }
    </script>
</body>
</html>

3.本地更新头像信息

1.头像上传成功后-关闭页面/将头像数据存储在前端

关闭avatar.html页面,展示底下的setting.html,并将头像存储在前端

客户端的avatar.html代码:

upload_avatar(ret){
    // 头像上传处理
    var token = this.game.get("access_token") || this.game.fget("access_token");
    if(!token){
        this.game.goFrame("login","login.html", this.current);
        return ;
    }
    this.axios.post("",{
        "jsonrpc": "2.0",
        "id": this.uuid(),
        "method": "User.avatar.update",
        "params": {
            "avatar": ret.base64Data,
        }
    },{
        headers:{
            Authorization: "jwt " + token,
        }
    }).then(response=>{
        // 当后端存储头像数据成功后
        if(parseInt(response.data.result.errno)==1000){
            // 1.将头像存储在前端
            this.game.fsave({"avatar": response.data.result.avatar});
            // 2.跳转到设置页面
            this.game.goFrame("setting","setting.html", this.current);
        }else{
            this.game.print(">>>> fail");
            this.game.print(response.data);
        }
    }).catch(error=>{
        // 网络等异常
        this.game.print(error);
    })
}
}
});
}

2.setting.js提供avatar_url头像访问地址

客户端的配置文件static/js/settings.js新增配置项avatar_url提供头像访问地址,代码;

function init(){
  if(Game){
    var game = new Game("../mp3/bg1.mp3");
    Vue.prototype.game = game;
  }
  if(axios){
    // 初始化axios
    axios.defaults.baseURL = "//192.168.20.251:5000/api" // 服务端api接口网关地址
    axios.defaults.timeout = 2500; // 请求超时时间
    axios.defaults.withCredentials = false; // 跨域请求资源的情况下,忽略cookie的发送
    Vue.prototype.axios = axios;
    Vue.prototype.uuid  = UUID.generate;
  }
  // 接口相关的配置项
  Vue.prototype.settings = {
    captcha_app_id: "2071340228",  // 腾讯防水墙验证码应用ID
    avatar_url: "//192.168.20.251:5000/users/avatar", // ***头像前端访问地址***
  }
}

3.后端提供展示头像的视图方法

服务端提供展示头像的视图方法地址

application/apps/users/urls.py,代码:

from . import views
from application.utils import path
urlpatterns = [
    path("/avatar", views.avatar),
]

确认总路由设置了访问蓝图的url前缀,application/urls.py,代码:

from application.utils import include
urlpatterns = [
    include("","home.urls"),
    include("/users","users.urls"),
    include("/marsh","marsh.urls"),
]

视图中, 显示图片时验证用户身份.,application/apps/users/views.py代码:

from flask import make_response,request
@jwt_required # 验证jwt
def avatar():
    """获取头像信息"""
    
    # 1.获取前端存储头像的sign参数
    avatar = request.args.get("sign")
    
    # 2.获取头像文件格式:jpg/jpeg/png
    ext  = avatar[avatar.find(".")+1:]
    
    # 3.获取头像文件的文件名
    filename = avatar[:avatar.find(".")]
    
    # 4.后端文件存储的路径
    static_path = os.path.join(current_app.BASE_DIR, current_app.config["STATIC_DIR"])
    
    # 5.读取后端文件数据
    with open("%s/%s.%s" % (static_path,filename,ext), "rb") as f:
        content = f.read() #  此时content存储的是二进制文件
    response = make_response(content) # 
    response.headers["Content-Type"] = "image/%s" % ext
    return response

4.设置页面头像立即刷新

完成了上面步骤代码以后, 我们现在能在客户端修改用户头像了,但是在关闭avatar.html页面以后, setting.html页面中的头像并没有及时发生变化,.所以我们在avatar.html页面更新了头像以后, 我们借住apicloud提供的自定义事件和事件监听接口,来实现,通知setting.html页面,用户头衔已经发生改变了.

文档: //docs.apicloud.com/Client-API/api#72

当前功能,我们需要根据文档了解关于sendEventaddEventListener的使用.

// a页面可以通过sendEvent发起一个自定义事件
api.sendEvent({
    name: 'myEvent', # 自定义事件名称
    extra: {
        key1: 'value1',  # 事件传参
        key2: 'value2'
    }
});

// b页面可以通过addEventListener进行监听是否有对应名称的自定义事件进行发送了,一旦监听到,则自动执行回调函数:
api.addEventListener({
    name: 'myEvent' # 监听指定名称的事件
}, function(ret, err) {   # ret接收指定名称的事件参数
    alert(JSON.stringify(ret.value));
});

// c页面也可以通过addEventListener进行监听是否有对应名称的自定义事件进行发送了,一旦监听到,则自动执行回调函数:
api.addEventListener({
    name: 'myEvent'
}, function(ret, err) {
    alert(JSON.stringify(ret.value));
});

// b.c 页面都将收到 myEvent 事件

1.avatar.html发送自定义事件

接下来,我们就可以在avatar.html页面中,发送一个自定义事件,告知其他页面,当前用户的头像发生了改变.

avatar.html代码

        upload_avatar(ret){
          // 头像上传处理
          var token = this.game.get("access_token") || this.game.fget("access_token");
          if(!token){
            this.game.goFrame("login","login.html", this.current);
            return ;
          }
          this.axios.post("",{
                        "jsonrpc": "2.0",
                        "id": this.uuid(),
                        "method": "User.avatar.update",
                        "params": {
                            "avatar": ret.base64Data,
                        }
                    },{
            headers:{
              Authorization: "jwt " + token,
            }
          }).then(response=>{
            if(parseInt(response.data.result.errno)==1000){
              this.game.fsave({"avatar": response.data.result.avatar});
                            // ***发送自定义事件***
                            api.sendEvent({
                                name: 'change_avatar',
                                extra: {
                                        "avatar": response.data.result.avatar
                                    }
                            });
              this.game.outFrame("avatar");
                    }else{
                            this.game.print(">>>> fail");
              this.game.print(response.data);
            }
          }).catch(error=>{
            // 网络等异常
            this.game.print(error);
          })
        }
            }
        });
    }

2.setting.html监听自定义事件

setting.html页面中,监听自定义事件,这个监听过程是不需要刷新页面的.

methods:{
    change_avatar(){
        // 监听自定义事件
        api.addEventListener({
            name: 'change_avatar'
        }, (ret, err)=>{
            if( ret ){
                var token = this.game.get("access_token") || this.game.fget("access_token");
                this.avatar = `${this.settings.avatar_url}?sign=${ret.value.avatar}&token=${token}`;
            }
        });
    },
        

5.前端页面显示当前登录用户的基本信息

setting.html页面提供当前登陆用户的基本信息,代码:

      </div>
      <div class="item">
        <span class="title">昵称</span>
        <span class="goto">&gt;</span>
        <span class="value">{{nickname}}</span>
      </div>
      <div class="item">
        <span class="title">手机号</span>
        <span class="goto">&gt;</span>
        <span class="value">{{mobile}}</span>
      </div>


    <script>
    apiready = function(){
        init();
        new Vue({
            el:"#app",
            data(){
                return {
                    nickname: "", // 昵称
                    mobile: "",  // 手机号
                    avatar: "../static/images/avatar.png",
                    prev:{name:"",url:"",params:{}},
                    current:{name:"setting",url:"setting.html",params:{}},
                }
            },
            created(){
                this.get_user_info(); // 触发获取用户信息方法
                this.change_avatar();
            },
            methods:{
                 // 获取当前用户登录基本信息
                get_user_info(){
                    var token = this.game.get("access_token") || this.game.fget("access_token");
                     // 获取当前登陆用户基本信息
                     this.axios.post("",{
                         "jsonrpc": "2.0",
                         "id": this.uuid(),
                         "method": "User.info",
                         "params": {}
                     },{
             headers:{
               Authorization: "jwt " + token,
             }
           }).then(response=>{
                          var res = response.data.result;
                          if(parseInt(res.errno) === 1000){
                                this.nickname = res.nickname;
                              // avatar从前端获取传到:src上
                                this.avatar = `${this.settings.avatar_url}?sign=${res.avatar}&token=${token}`;
                                this.mobile = res.mobile;
                            }
                     })
                },
            
    </script>
</body>
</html>

6.后端提供用户基本信息的API接口

服务端提供用户基本信息APi接口,application/apps/users/views.py,代码:

from .marshmallow import UserInfoSchema
@jsonrpc.method("User.info")
@jwt_required # 验证jwt
def info():
    """获取用户信息"""
    current_user_id = get_jwt_identity() # 根据token反解出当前用户id
    
    # 判断当前用户是否存在
    user = User.query.get(current_user_id)
    if user is None:
        return {
            "errno": status.CODE_NO_USER,
            "errmsg": message.user_not_exists,
        }
    uis = UserInfoSchema()
    data = uis.dump(user) # 将用户信息序列化传递给前端
    return {
        "errno": status.CODE_OK,
        "errmsg": message.ok,
        **data
    }

marshmallow.py,代码:

from marshmallow import post_dump
class UserInfoSchema(SQLAlchemyAutoSchema):
    id = auto_field()
    mobile = auto_field()
    nickname = auto_field()
    avatar = auto_field()

    class Meta:
        model = User
        include_fk = True
        include_relationships = True
        fields = ["id","mobile","nickname","avatar"]
        sql_session = db.session

    @post_dump()
    def mobile_format(self, data, **kwargs):
        data["mobile"] = data["mobile"][:3]+"****"+data["mobile"][-4:]
        return data