day110:MoFang:重新构造用户关系状态&添加好友&处理好友申请&获取申请好友历史记录&好友列表显示
day109+day110所学内容流程图
在day109博客的前提下, 我们对现在的用户关系处理功能在服务端和客户端上面进行重新调整下.
class UserRelation(BaseModel): """用户关系""" __tablename__ = "mf_user_relation" relation_status_chioce = ( (1,"好友"), (2,"关注"), (3,"拉黑"), ) relation_type_chioce = ( (1, "手机"), (2, "账号"), (3, "邮箱"), (4, "昵称"), (5, "群聊"), (6, "二维码邀请注册"), ) send_user = db.Column(db.Integer, comment="用户1") # 主动构建关系的用户 receive_user = db.Column(db.Integer, comment="用户2") # 接受关系请求的用户 relation_type = db.Column(db.Integer, default=0, comment="构建关系类型") relation_status = db.Column(db.Integer, default=0, comment="关系状态") def __repr__(self): return "用户%s通过%s对%s进行了%s操作" % (self.send_user,self.relation_type, self.receive_user,self.relation_status)
from sqlalchemy import or_,and_ from .models import UserRelation class UserSearchInfoSchema(SQLAlchemyAutoSchema): """用户搜索信息返回""" id = auto_field() nickname = auto_field() avatar = auto_field() relation_status = fields.String(dump_only=True) @post_dump() def relation_status_post(self, data, **kwargs): relaionship = UserRelation.query.filter( or_( and_(UserRelation.send_user==self.context["user_id"], UserRelation.receive_user==data["id"]), and_(UserRelation.receive_user==self.context["user_id"], UserRelation.send_user==data["id"]), ) ).first() if relaionship is not None: data["relation_status"] = UserRelation.relation_status_chioce[relaionship.relation_status-1] else: data["relation_status"] = (0,"添加") return data class Meta: model = User include_fk = True include_relationships = True fields = ["id","nickname","avatar","relation_status"] sql_session = db.session
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<!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 update_nickname add_friend" id="app"> <div class="box"> <p class="title">添加好友</p> <img class="close" @click="close_frame" src="../static/images/close_btn1.png" alt=""> <div class="content"> <input class="nickname" type="text" v-model="account" placeholder="输入昵称/手机/邮箱/魔方账号...."> </div> <div class="friends_list"> <!-- for循环 --> <div class="item" v-for="user in search_user_list"> <div class="avatar"> <img class="avatar_bf" src="../static/images/avatar_bf.png" alt=""> <img class="user_avatar" :src="avatar_url(user.avatar)" alt=""> <img class="avatar_border" src="../static/images/avatar_border.png" alt=""> </div> <div class="info"> <p class="username">{{user.nickname}}</p> <p class="time">刚刚搜索</p> </div> <div class="status" @click="change_relation(user.id,user.relation_status)">{{user.relation_status[1]}}</div> </div> <div class="item"> <div class="avatar"> <img class="avatar_bf" src="../static/images/avatar_bf.png" alt=""> <img class="user_avatar" src="../static/images/avatar.png" alt=""> <img class="avatar_border" src="../static/images/avatar_border.png" alt=""> </div> <div class="info"> <p class="username">长昵称都很好</p> <p class="time">3小时前</p> </div> <div class="status">等待通过</div> </div> <div class="item"> <div class="avatar"> <img class="avatar_bf" src="../static/images/avatar_bf.png" alt=""> <img class="user_avatar" src="../static/images/avatar.png" alt=""> <img class="avatar_border" src="../static/images/avatar_border.png" alt=""> </div> <div class="info"> <p class="username">长昵称都很好</p> <p class="time">1天前</p> </div> <div class="status">已通过</div> </div> <div class="item"> <div class="avatar"> <img class="avatar_bf" src="../static/images/avatar_bf.png" alt=""> <img class="user_avatar" src="../static/images/avatar.png" alt=""> <img class="avatar_border" src="../static/images/avatar_border.png" alt=""> </div> <div class="info"> <p class="username">长昵称都很好</p> <p class="time">7天前</p> </div> <div class="status">已超时</div> </div> <div class="item"> <div class="avatar"> <img class="avatar_bf" src="../static/images/avatar_bf.png" alt=""> <img class="user_avatar" src="../static/images/avatar.png" alt=""> <img class="avatar_border" src="../static/images/avatar_border.png" alt=""> </div> <div class="info"> <p class="username">长昵称都很好</p> <p class="time">7天前</p> </div> <div class="status">已拒绝</div> </div> </div> </div> </div> <script> apiready = function(){ init(); new Vue({ el:"#app", data(){ return { user_id: "", // 当前登陆用户Id search_user_list:[], search_timer:"", account:"", prev:{name:"",url:"",params:{}}, current:{name:"add_friend",url:"add_friend.html",params:{}}, } }, watch:{ account(){ clearTimeout(this.search_timer); if(this.account.length>0){ this.search_timer = setTimeout(()=>{ this.search_user(); },2000); }else{ this.search_user_list = []; } } }, created(){ this.user_id = this.game.get("user_id") || this.game.fget("user_id"); }, methods:{ avatar_url(avatar){ var token = this.game.get("access_token") || this.game.fget("access_token"); return `${this.settings.avatar_url}?sign=${avatar}&token=${token}`; }, search_user(){ // 搜素用户 var token = this.game.get("access_token") || this.game.fget("access_token"); if(!token){ this.game.goFrame("login","login.html", this.current); return ; } this.game.checkout(this, token, (new_access_token)=>{ if(!new_access_token){ this.game.print(new_access_token); return ; } this.axios.post("",{ "jsonrpc": "2.0", "id": this.uuid(), "method": "User.user.relation", "params": { "account": this.account, } },{ headers:{ Authorization: "jwt " + new_access_token, } }).then(response=>{ this.game.print(response.data.result); if(parseInt(response.data.result.errno)==1000){ this.search_user_list = response.data.result.user_list; }else if(parseInt(response.data.result.errno) == 1008){ this.search_user_list = []; }else{ this.game.print(response.data); } }).catch(error=>{ // 网络等异常 this.game.print(error); }); }); }, close_frame(){ this.game.outFrame("add_friend"); }, add_friend_commit(){ // 提交搜索信息 }, change_relation(user_id,status){ // ***关系状态修改*** this.game.print(user_id); todo = []; if(status[0] == 0){ // 未添加 todo.push("添加对方为好友"); }else if(status[0]==1){ // 已添加 return ; }else if(status[0]==2){ // 关注关系 todo.push("添加对方为好友"); } api.actionSheet({ title: '操作', buttons: todo }, (ret, err)=>{ if(status[0] == 0 && ret.buttonIndex == 1 ){ // 申请添加好友 this.game.print(status[0]); this.game.print(ret.buttonIndex); } }); } } }); } </script> </body> </html>
2.添加好友
1.添加好友后端接口:User.friend.add
@jsonrpc.method("User.friend.add") @jwt_required # 验证jwt def add_friend_apply(user_id): """申请添加好友""" 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, } receive_user = User.query.get(user_id) if receive_user is None: return { "errno": status.CODE_NO_USER, "errmsg": message.receive_user_not_exists, } # todo 查看是否被对方拉黑了 # 添加一个申请记录到MongoDB中 document = { "send_user_id": user.id, "send_user_nickname": user.nickname, "send_user_avatar": user.avatar, "receive_user_id": receive_user.id, "receive_user_nickname": receive_user.nickname, "receive_user_avatar": receive_user.avatar, "time": datetime.now().timestamp(), # 操作时间 "status": 0, } mongo.db.user_relation_history.insert_one(document) return { "errno": status.CODE_OK, "errmsg": message.ok, }
html/add_friend.html
,代码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<!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 update_nickname add_friend" id="app"> <div class="box"> <p class="title">添加好友</p> <img class="close" @click="close_frame" src="../static/images/close_btn1.png" alt=""> <div class="content"> <input class="nickname" type="text" v-model="account" placeholder="输入昵称/手机/邮箱/魔方账号...."> </div> <div class="friends_list"> <div class="item" v-for="user in search_user_list"> <div class="avatar"> <img class="avatar_bf" src="../static/images/avatar_bf.png" alt=""> <img class="user_avatar" :src="avatar_url(user.avatar)" alt=""> <img class="avatar_border" src="../static/images/avatar_border.png" alt=""> </div> <div class="info"> <p class="username">{{user.nickname}}</p> <p class="time">刚刚搜索</p> </div> <div class="status" @click="change_relation(user.id,user.relation_status)">{{user.relation_status[1]}}</div> </div> <div class="item"> <div class="avatar"> <img class="avatar_bf" src="../static/images/avatar_bf.png" alt=""> <img class="user_avatar" src="../static/images/avatar.png" alt=""> <img class="avatar_border" src="../static/images/avatar_border.png" alt=""> </div> <div class="info"> <p class="username">长昵称都很好</p> <p class="time">3小时前</p> </div> <div class="status">等待通过</div> </div> <div class="item"> <div class="avatar"> <img class="avatar_bf" src="../static/images/avatar_bf.png" alt=""> <img class="user_avatar" src="../static/images/avatar.png" alt=""> <img class="avatar_border" src="../static/images/avatar_border.png" alt=""> </div> <div class="info"> <p class="username">长昵称都很好</p> <p class="time">1天前</p> </div> <div class="status">已通过</div> </div> <div class="item"> <div class="avatar"> <img class="avatar_bf" src="../static/images/avatar_bf.png" alt=""> <img class="user_avatar" src="../static/images/avatar.png" alt=""> <img class="avatar_border" src="../static/images/avatar_border.png" alt=""> </div> <div class="info"> <p class="username">长昵称都很好</p> <p class="time">7天前</p> </div> <div class="status">已超时</div> </div> <div class="item"> <div class="avatar"> <img class="avatar_bf" src="../static/images/avatar_bf.png" alt=""> <img class="user_avatar" src="../static/images/avatar.png" alt=""> <img class="avatar_border" src="../static/images/avatar_border.png" alt=""> </div> <div class="info"> <p class="username">长昵称都很好</p> <p class="time">7天前</p> </div> <div class="status">已拒绝</div> </div> </div> </div> </div> <script> apiready = function(){ init(); new Vue({ el:"#app", data(){ return { user_id: "", // 当前登陆用户Id search_user_list:[], search_timer:"", account:"", prev:{name:"",url:"",params:{}}, current:{name:"add_friend",url:"add_friend.html",params:{}}, } }, watch:{ account(){ clearTimeout(this.search_timer); if(this.account.length>0){ this.search_timer = setTimeout(()=>{ this.search_user(); },2000); }else{ this.search_user_list = []; } } }, created(){ this.user_id = this.game.get("user_id") || this.game.fget("user_id"); }, methods:{ avatar_url(avatar){ var token = this.game.get("access_token") || this.game.fget("access_token"); return `${this.settings.avatar_url}?sign=${avatar}&token=${token}`; }, search_user(){ // 搜素用户 var token = this.game.get("access_token") || this.game.fget("access_token"); if(!token){ this.game.goFrame("login","login.html", this.current); return ; } this.game.checkout(this, token, (new_access_token)=>{ if(!new_access_token){ this.game.print(new_access_token); return ; } this.axios.post("",{ "jsonrpc": "2.0", "id": this.uuid(), "method": "User.user.relation", "params": { "account": this.account, } },{ headers:{ Authorization: "jwt " + new_access_token, } }).then(response=>{ this.game.print(response.data.result); if(parseInt(response.data.result.errno)==1000){ this.search_user_list = response.data.result.user_list; }else if(parseInt(response.data.result.errno) == 1008){ this.search_user_list = []; }else{ this.game.print(response.data); } }).catch(error=>{ // 网络等异常 this.game.print(error); }); }); }, close_frame(){ this.game.outFrame("add_friend"); }, add_friend_commit(){ // 提交搜索信息 }, change_relation(user_id,status){ // 关系状态修改 this.game.print(user_id); todo = []; if(status[0] == 0){ // 未添加 todo.push("添加对方为好友"); }else if(status[0]==1){ // 已添加 return ; }else if(status[0]==2){ // 关注关系 todo.push("添加对方为好友"); } api.actionSheet({ title: '操作', buttons: todo }, (ret, err)=>{ // ***当两个用户关系为未添加时,可以添加对方为好友*** if(status[0] == 0 && ret.buttonIndex == 1 ){ // ***申请添加好友*** var token = this.game.get("access_token") || this.game.fget("access_token"); this.axios.post("",{ "jsonrpc": "2.0", "id": this.uuid(), "method": "User.friend.add", "params": { "user_id": user_id, } },{ headers:{ Authorization: "jwt " + token, } }).then(response=>{ if(parseInt(response.data.result.errno)==1000){ // 添加好友成功 this.game.print(">>>> 3") this.game.print("添加好友成功!"); }else{ this.game.print(response.data); } }).catch(error=>{ // 网络等异常 this.game.print(error); }); } }); } } }); } </script> </body> </html>
3.处理好友申请
1.处理好友申请-前端
当两个人不是好友,并且状态为等待通过时,会触发处理好友申请接口
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<!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 update_nickname add_friend" id="app"> <div class="box"> <p class="title">添加好友</p> <img class="close" @click="close_frame" src="../static/images/close_btn1.png" alt=""> <div class="content"> <input class="nickname" type="text" v-model="account" placeholder="输入昵称/手机/邮箱/魔方账号...."> </div> <div class="friends_list"> <div class="item" v-for="user in search_user_list"> <div class="avatar"> <img class="avatar_bf" src="../static/images/avatar_bf.png" alt=""> <img class="user_avatar" :src="avatar_url(user.avatar)" alt=""> <img class="avatar_border" src="../static/images/avatar_border.png" alt=""> </div> <div class="info"> <p class="username">{{user.nickname}}</p> <p class="time">刚刚搜索</p> </div> <div class="status" @click="change_relation(user.id,user.relation_status)">{{user.relation_status[1]}}</div> </div> <div class="item"> <div class="avatar"> <img class="avatar_bf" src="../static/images/avatar_bf.png" alt=""> <img class="user_avatar" src="../static/images/avatar.png" alt=""> <img class="avatar_border" src="../static/images/avatar_border.png" alt=""> </div> <div class="info"> <p class="username">长昵称都很好</p> <p class="time">3小时前</p> </div> <div class="status">等待通过</div> </div> <div class="item"> <div class="avatar"> <img class="avatar_bf" src="../static/images/avatar_bf.png" alt=""> <img class="user_avatar" src="../static/images/avatar.png" alt=""> <img class="avatar_border" src="../static/images/avatar_border.png" alt=""> </div> <div class="info"> <p class="username">长昵称都很好</p> <p class="time">1天前</p> </div> <div class="status">已通过</div> </div> <div class="item"> <div class="avatar"> <img class="avatar_bf" src="../static/images/avatar_bf.png" alt=""> <img class="user_avatar" src="../static/images/avatar.png" alt=""> <img class="avatar_border" src="../static/images/avatar_border.png" alt=""> </div> <div class="info"> <p class="username">长昵称都很好</p> <p class="time">7天前</p> </div> <div class="status">已超时</div> </div> <div class="item"> <div class="avatar"> <img class="avatar_bf" src="../static/images/avatar_bf.png" alt=""> <img class="user_avatar" src="../static/images/avatar.png" alt=""> <img class="avatar_border" src="../static/images/avatar_border.png" alt=""> </div> <div class="info"> <p class="username">长昵称都很好</p> <p class="time">7天前</p> </div> <div class="status">已拒绝</div> </div> </div> </div> </div> <script> apiready = function(){ init(); new Vue({ el:"#app", data(){ return { user_id: "", // 当前登陆用户Id search_user_list:[], search_timer:"", account:"", prev:{name:"",url:"",params:{}}, current:{name:"add_friend",url:"add_friend.html",params:{}}, } }, watch:{ account(){ clearTimeout(this.search_timer); if(this.account.length>0){ this.search_timer = setTimeout(()=>{ this.search_user(); },2000); }else{ this.search_user_list = []; } } }, created(){ this.user_id = this.game.get("user_id") || this.game.fget("user_id"); }, methods:{ avatar_url(avatar){ var token = this.game.get("access_token") || this.game.fget("access_token"); return `${this.settings.avatar_url}?sign=${avatar}&token=${token}`; }, search_user(){ // 搜素用户 var token = this.game.get("access_token") || this.game.fget("access_token"); if(!token){ this.game.goFrame("login","login.html", this.current); return ; } this.game.checkout(this, token, (new_access_token)=>{ if(!new_access_token){ this.game.print(new_access_token); return ; } this.axios.post("",{ "jsonrpc": "2.0", "id": this.uuid(), "method": "User.user.relation", "params": { "account": this.account, } },{ headers:{ Authorization: "jwt " + new_access_token, } }).then(response=>{ this.game.print(response.data.result); if(parseInt(response.data.result.errno)==1000){ this.search_user_list = response.data.result.user_list; }else if(parseInt(response.data.result.errno) == 1008){ this.search_user_list = []; }else{ this.game.print(response.data); } }).catch(error=>{ // 网络等异常 this.game.print(error); }); }); }, close_frame(){ this.game.outFrame("add_friend"); }, add_friend_commit(){ // 提交搜索信息 }, change_relation(user_id,status){ // 关系状态修改 this.game.print(status); todo = []; if(status[0] == 0){ // 未添加 if(status[1]=="等待通过"){ todo.push("通过"); todo.push("拒绝"); }else if(status[1]=="添加"){ todo.push("添加对方为好友"); } }else if(status[0]==1){ // 已添加 return ; }else if(status[0]==2){ // 关注关系 todo.push("添加对方为好友"); } api.actionSheet({ title: '操作', buttons: todo }, (ret, err)=>{ var token = this.game.get("access_token") || this.game.fget("access_token"); // 第一种情况 if( status[0] == 0 && status[1]=="添加" && ret.buttonIndex == 1 ){ // 申请添加好友 this.axios.post("",{ "jsonrpc": "2.0", "id": this.uuid(), "method": "User.friend.add", "params": { "user_id": user_id, } },{ headers:{ Authorization: "jwt " + token, } }).then(response=>{ if(parseInt(response.data.result.errno)==1000){ // ***添加好友成功*** api.alert({ title: '提示', msg: '"添加好友成功!"', }, function(ret, err){ }); }else{ this.game.print(response.data); } }).catch(error=>{ // 网络等异常 this.game.print(error); }); // ***第二种情况:处理好友申请*** }else if(status[0] == 0 && status[1]=="等待通过"){ // 处理好友申请 this.axios.post("",{ "jsonrpc": "2.0", "id": this.uuid(), "method": "User.friend.apply", "params": { "user_id": user_id, "agree": ret.buttonIndex==1?true:false, "search_text": this.account, // 搜索框中的搜索内容 } },{ headers:{ Authorization: "jwt " + token, } }).then(response=>{ if(parseInt(response.data.result.errno)==1000){ // 添加好友成功 api.alert({ title: '提示', msg: '"处理好友申请成功!"', }, function(ret, err){ }); }else{ this.game.print(response.data); } }).catch(error=>{ // 网络等异常 this.game.print(error); }); } }); } } }); } </script> </body> </html>
2.
from sqlalchemy import and_ @jsonrpc.method("User.friend.apply") @jwt_required # 验证jwt def add_friend_apply(user_id,agree,search_text): """处理好友申请""" 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, } receive_user = User.query.get(user_id) if receive_user is None: return { "errno": status.CODE_NO_USER, "errmsg": message.receive_user_not_exists, } relaionship = UserRelation.query.filter( or_( and_(UserRelation.send_user == user.id, UserRelation.receive_user == receive_user.id), and_(UserRelation.receive_user == user.id, UserRelation.send_user == receive_user.id), ) ).first() if agree: if receive_user.mobile == search_text: chioce = 0 elif receive_user.name == search_text: chioce = 1 elif receive_user.email== search_text: chioce = 2 elif receive_user.nickname == search_text: chioce = 3 else: chioce = 4 # ????? if relaionship is not None: relaionship.relation_status = 1 relaionship.relation_type = chioce db.session.commit() else: relaionship = UserRelation( send_user=user.id, receive_user=receive_user.id, relation_status=1, relation_type=chioce, ) db.session.add(relaionship) db.session.commit() # 调整mongoDB中用户关系的记录状态 query = { "$or": [{ "$and": [ { "send_user_id": user.id, "receive_user_id": receive_user.id, "time": {"$gte": datetime.now().timestamp() - 60 * 60 * 24 * 7} } ], }, { "$and": [ { "send_user_id": receive_user.id, "receive_user_id": user.id, "time": {"$gte": datetime.now().timestamp() - 60 * 60 * 24 * 7} } ], }] } if agree: argee_status = 1 else: argee_status = 2 ret = mongo.db.user_relation_history.update(query, {"$set":{"status":argee_status}}) if ret and ret.get("nModified") < 1: return { "errno": status.CODE_UPDATE_USER_RELATION_ERROR, "errmsg": message.update_user_relation_fail, } else: return { "errno": status.CODE_OK, "errmsg": message.update_success, }
4.获取申请好友历史记录
1.服务端提供接口:User.relation.history
服务端提供api接口, user/views.py
,代码:
@jsonrpc.method("Use.relation.history") @jwt_required # 验证jwt def history_relation(): """查找好友关系历史记录""" 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, } query = { "$or":[ {"send_user_id":user.id,"time": {"$gte": datetime.now().timestamp() - 60 * 60 * 24 * 7}}, {"receive_user_id": user.id,"time": {"$gte": datetime.now().timestamp() - 60 * 60 * 24 * 7}}, ] } document_list = mongo.db.user_relation_history.find(query,{"_id":0}) data_list = [] for document in document_list: if document.get("send_user_id") == user.id and document.get("status") == 0: document["status"] = (0,"已添加") elif document.get("receive_user_id") == user.id and document.get("status") == 0: document["status"] = (0a, "等待通过") elif document.get("status") == 1: document["status"] = (1, "已通过") else: document["status"] = (2, "已拒绝") data_list.append(document) return { "errno": status.CODE_OK, "errmsg": message.ok, "data_list": data_list, }
2.前端显示申请好友的所有历史记录
add_friend.html
,代码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<!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 update_nickname add_friend" id="app"> <div class="box"> <p class="title">添加好友</p> <img class="close" @click="close_frame" src="../static/images/close_btn1.png" alt=""> <div class="content"> <input class="nickname" type="text" v-model="account" placeholder="输入昵称/手机/邮箱/魔方账号...."> </div> <div class="friends_list"> <div class="item" v-for="user in search_user_list"> <div class="avatar"> <img class="avatar_bf" src="../static/images/avatar_bf.png" alt=""> <img class="user_avatar" :src="avatar_url(user.avatar)" alt=""> <img class="avatar_border" src="../static/images/avatar_border.png" alt=""> </div> <div class="info"> <p class="username">{{user.nickname}}</p> <p class="time">刚刚搜索</p> </div> <div class="status" @click="change_relation(user.id,user.relation_status)">{{user.relation_status[1]}}</div> </div> <div class="item" v-for="relation in history_list"> <div class="avatar"> <img class="avatar_bf" src="../static/images/avatar_bf.png" alt=""> <img class="user_avatar" v-if="relation.send_user_id==user_id" :src="avatar_url(relation.receive_user_avatar)" alt=""> <img class="user_avatar" v-else :src="avatar_url(relation.send_user_avatar)" alt=""> <img class="avatar_border" src="../static/images/avatar_border.png" alt=""> </div> <div class="info"> <p class="username" v-if="relation.send_user_id==user_id">{{relation.receive_user_nickname}}</p> <p class="username" v-else>{{relation.send_user_nickname}}</p> <p class="time">{{game.time_format(relation.time)}}</p> </div> <div class="status">{{relation.status[1]}}</div> </div> </div> </div> </div> <script> apiready = function(){ init(); new Vue({ el:"#app", data(){ return { user_id: "", // 当前登陆用户Id search_user_list:[], search_timer:"", account:"", history_list:[], prev:{name:"",url:"",params:{}}, current:{name:"add_friend",url:"add_friend.html",params:{}}, } }, watch:{ account(){ clearTimeout(this.search_timer); if(this.account.length>0){ this.search_timer = setTimeout(()=>{ this.search_user(); },2000); }else{ this.search_user_list = []; } } }, created(){ this.user_id = this.game.get("id") || this.game.fget("id"); this.get_relation_history(); }, methods:{ get_relation_history(){ // ***获取历史信息记录*** var token = this.game.get("access_token") || this.game.fget("access_token"); this.game.checkout(this, token, (new_access_token)=>{ this.axios.post("",{ "jsonrpc": "2.0", "id": this.uuid(), "method": "Use.relation.history", "params": {} },{ headers:{ Authorization: "jwt " + token, } }).then(response=>{ this.game.print(response.data.result); if(parseInt(response.data.result.errno)==1000){ this.history_list = response.data.result.data_list; }else if(parseInt(response.data.result.errno) == 1008){ this.history_list = []; }else{ this.game.print(response.data); } }).catch(error=>{ // 网络等异常 this.game.print(error); }); }) }, avatar_url(avatar){ var token = this.game.get("access_token") || this.game.fget("access_token"); return `${this.settings.avatar_url}?sign=${avatar}&token=${token}`; }, search_user(){ // 搜素用户 var token = this.game.get("access_token") || this.game.fget("access_token"); if(!token){ this.game.goFrame("login","login.html", this.current); return ; } this.game.checkout(this, token, (new_access_token)=>{ if(!new_access_token){ this.game.print(new_access_token); return ; } this.axios.post("",{ "jsonrpc": "2.0", "id": this.uuid(), "method": "User.user.relation", "params": { "account": this.account, } },{ headers:{ Authorization: "jwt " + new_access_token, } }).then(response=>{ this.game.print(response.data.result); if(parseInt(response.data.result.errno)==1000){ this.search_user_list = response.data.result.user_list; }else if(parseInt(response.data.result.errno) == 1008){ this.search_user_list = []; }else{ this.game.print(response.data); } }).catch(error=>{ // 网络等异常 this.game.print(error); }); }); }, close_frame(){ this.game.outFrame("add_friend"); }, add_friend_commit(){ // 提交搜索信息 }, change_relation(user_id,status){ // 关系状态修改 this.game.print(status); todo = []; if(status[0] == 0){ // 未添加 if(status[1]=="等待通过"){ todo.push("通过"); todo.push("拒绝"); }else if(status[1]=="添加"){ todo.push("添加对方为好友"); } }else if(status[0]==1){ // 已添加 return ; }else if(status[0]==2){ // 关注关系 todo.push("添加对方为好友"); } api.actionSheet({ title: '操作', buttons: todo }, (ret, err)=>{ var token = this.game.get("access_token") || this.game.fget("access_token"); if( status[0] == 0 && status[1]=="添加" && ret.buttonIndex == 1 ){ // 申请添加好友 this.axios.post("",{ "jsonrpc": "2.0", "id": this.uuid(), "method": "User.friend.add", "params": { "user_id": user_id, } },{ headers:{ Authorization: "jwt " + token, } }).then(response=>{ if(parseInt(response.data.result.errno)==1000){ // 添加好友成功 api.alert({ title: '提示', msg: '"添加好友成功!"', }, function(ret, err){ }); }else{ this.game.print(response.data); } }).catch(error=>{ // 网络等异常 this.game.print(error); }); }else if(status[0] == 0 && status[1]=="等待通过"){ // 处理好友申请 this.axios.post("",{ "jsonrpc": "2.0", "id": this.uuid(), "method": "User.friend.apply", "params": { "user_id": user_id, "agree": ret.buttonIndex==1?true:false, "search_text": this.account, // 搜索框中的搜索内容 } },{ headers:{ Authorization: "jwt " + token, } }).then(response=>{ if(parseInt(response.data.result.errno)==1000){ // 添加好友成功 api.alert({ title: '提示', msg: '"处理好友申请成功!"', }, function(ret, err){ }); }else{ this.game.print(response.data); } }).catch(error=>{ // 网络等异常 this.game.print(error); }); } }); } } }); } </script> </body> </html>
time_format(time){ // 时间距离格式化显示 var now_time = parseInt((new Date() - 0) / 1000); // 当前客户端的秒时间戳 var has_time = now_time - time; if(has_time<5 * 60){ return "刚刚"; }else if(has_time<30*60){ return parseInt(has_time/60)+"分钟前"; }else if(has_time<60*60){ return "半个小时前"; }else if(has_time<12*60*60){ return parseInt(has_time/60/60)+"小时前"; }else if(has_time<24*60*60){ return "半天前"; }else if(has_time<7*24*60*60){ return parseInt(has_time/24/60/60)+"天前"; }else if(has_time<14*24*60*60){ return "一周前"; }else if(has_time<30*24*60*60){ return "半个月前"; } }
5.好友列表
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<!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="friends_list"> <div class="item" v-for="user in friends"> <div class="avatar"> <img class="avatar_bf" src="../static/images/avatar_bf.png" alt=""> <img class="user_avatar" :src="avatar_url(user.avatar)" alt=""> <img class="avatar_border" src="../static/images/avatar_border.png" alt=""> </div> <div class="info"> <p class="username">{{user.nickname}}</p> <p class="fruit">果子:{{user.fruit}}</p> </div> <div v-if="user.fruit_status==1" class="behavior pick">摘</div> <div v-if="user.fruit_status==2" class="behavior protect">护</div> <div class="goto"><img src="../static/images/arrow1.png" alt=""></div> </div> </div> </div> <script> apiready = function(){ init(); new Vue({ el:"#app", data(){ return { friends:[], page: 1, is_send_ajax:false, prev:{name:"",url:"",params:{}}, current:{name:"friend_list",url:"friend_list.html",params:{}}, } }, created(){ this.get_friends(); this.get_friends_listener(); this.page_out_listener(); }, methods:{ avatar_url(avatar){ var token = this.game.get("access_token") || this.game.fget("access_token"); return `${this.settings.avatar_url}?sign=${avatar}&token=${token}`; }, get_friends_listener(){ // 监听下拉刷新获取好友列表信息 api.setRefreshHeaderInfo({ loadingImg: 'widget://image/refresh.png', bgColor: null, textColor: '#fff', textDown: '下拉刷新...', textUp: '松开刷新...' }, (ret, err)=>{ //在这里从服务器加载数据,加载完成后调用api.refreshHeaderLoadDone()方法恢复组件到默认状态 this.get_friends(); setTimeout(()=>{ api.refreshHeaderLoadDone(); },4000); }); }, page_out_listener(){ // 监听什么时候需要退出当前页面 api.addEventListener({ name: 'out_page_to_user' }, (ret, err)=>{ this.goto_home(); }); }, get_friends(){ if(this.is_send_ajax){ return ; } // ***通过请求获取当前用户的好友列表*** var token = this.game.get("access_token") || this.game.fget("access_token"); this.game.checkout(this, token, (new_access_token)=>{ this.is_send_ajax = true; this.axios.post("",{ "jsonrpc": "2.0", "id": this.uuid(), "method": "User.friend.list", "params": { "page": this.page } },{ headers:{ Authorization: "jwt " + token, } }).then(response=>{ if(parseInt(response.data.result.errno)==1000){ if(this.page+1 == response.data.result.pages){ this.is_send_ajax = true; }else{ this.is_send_ajax = false; this.page+=1; } if(this.page>1){ api.refreshHeaderLoadDone(); } this.friends = response.data.result.friend_list.concat(this.friends); }else if(parseInt(response.data.result.errno) == 1008){ this.friends = []; }else{ this.game.print(response.data); } }).catch(error=>{ // 网络等异常 this.game.print(error); }); }) }, goto_home(){ // 退出当前页面 this.game.outFrame("friend_list","friend_list.html", this.current); }, } }); } </script> </body> </html>
@jsonrpc.method("User.friend.list") @jwt_required # 验证jwt def list_friend(page=1,limit=2): """好友列表""" 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, } pagination = UserRelation.query.filter( or_( and_(UserRelation.send_user == user.id), and_(UserRelation.receive_user == user.id), ) ).paginate(page,per_page=limit) user_id_list = [] for relation in pagination.items: if relation.send_user == user.id: user_id_list.append(relation.receive_user) else: user_id_list.append(relation.send_user) # 获取用户详细信息 user_list = User.query.filter(User.id.in_(user_id_list)).all() friend_list = [{"avatar":user.avatar,"nickname":user.nickname,"id":user.id,"fruit":0,"fruit_status":0} for user in user_list] pages = pagination.pages return { "errno": status.CODE_OK, "errmsg": message.ok, "friend_list": friend_list, "pages": pages }