好友列表
一、客户端显示页面
1.好友列表
html/user.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" id="app">
<div class="bg">
<img src="../static/images/bg0.jpg">
</div>
<img class="back" @click="goto_index" src="../static/images/user_back.png" alt="">
<img class="setting" @click='goto_setting' src="../static/images/setting.png" alt="">
<div class="header">
<div class="info">
<div class="avatar">
<img class="avatar_bf" src="../static/images/avatar_bf.png" alt="">
<img class="user_avatar" :src="avatar" alt="">
<img class="avatar_border" src="../static/images/avatar_border.png" alt="">
</div>
<p class="user_name">{{nickname}}</p>
</div>
<div class="wallet">
<div class="balance">
<p class="title"><img src="../static/images/money.png" alt="">钱包</p>
<p class="num">99,999.00</p>
</div>
<div class="balance">
<p class="title"><img src="../static/images/integral.png" alt="">果子</p>
<p class="num">99,999.00</p>
</div>
</div>
<div class="invite">
<img class="invite_btn" src="../static/images/invite.png" alt="">
</div>
</div>
<div class="menu">
<div class="item" @click='open_friend_list'>
<span class="title">我的好友</span>
<span class="value">查看</span>
</div>
<div class="item">
<span class="title">我的主页</span>
<span class="value">查看</span>
</div>
<div class="item">
<span class="title">任务列表</span>
<span class="value">75%</span>
</div>
<div class="item">
<span class="title">收益明细</span>
<span class="value">查看</span>
</div>
<div class="item">
<span class="title">实名认证</span>
<span class="value">未认证</span>
</div>
</ul>
</div>
</div>
<script>
apiready = function(){
init();
new Vue({
el:"#app",
data(){
return {
nickname: '',
avatar: '',
prev:{name:"",url:"",params:{}},
current:{name:"user",url:"user.html",params:{}},
}
},
created(){
this.get_user_info();
this.change_avatar();
},
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}`;
}
});
},
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;
this.game.print(res);
if(parseInt(res.errno) === 1000){
this.nickname = res.nickname;
this.avatar = `${this.settings.avatar_url}?sign=${res.avatar}&token=${token}`;
}
})
},
goto_index(){
// 返回首页
this.game.outWin("user");
},
goto_setting(){
// 进入设置
this.game.goFrame('setting', 'setting.html', this.current);
},
open_friend_list(){
// 进入好友列表
this.game.goFrame('friends', 'friends.html', this.current, {}, {}, true);
}
}
});
}
</script>
</body>
</html>
html/friends.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/friends_bg.png">
</div>
<img class="back" @click="goto_home" src="../static/images/user_back.png" alt="">
<div class="add_friend_btn" @click="add_friend">
<img src="../static/images/add_friend.png" alt="">
</div>
<div class="friends_list">
</div>
</div>
<script>
apiready = function(){
init();
new Vue({
el:"#app",
data(){
return {
friends:[],
prev:{name:"",url:"",params:{}},
current:{name:"friends",url:"friends.html",params:{}},
}
},
methods:{
add_friend(){
// 添加好友
},
goto_home(){
// 退出当前页面
this.game.outFrame("friends","friends.html", this.current);
},
}
});
}
</script>
</body>
</html>
css/main.css
,样式代码:
.add_friend_btn {
position: absolute;
top: 12rem;
left: 3.6rem;
width: 26rem;
height: 6rem;
}
.add_friend_btn img{
box-shadow: 2px 2px 5px rgba(9,9,9,0.1);
}
接下来,添加html/friend_list.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="friends_list">
<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="fruit">果子:9,999.00</p>
</div>
<div class="behavior pick">摘</div>
<div class="goto"><img src="../static/images/arrow1.png" alt=""></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="fruit">果子:9,999.00</p>
</div>
<div class="goto"><img src="../static/images/arrow1.png" alt=""></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="fruit">果子:9,999.00</p>
</div>
<div class="behavior protect">护</div>
<div class="goto"><img src="../static/images/arrow1.png" alt=""></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="fruit">果子:9,999.00</p>
</div>
<div class="behavior pick">摘</div>
<div class="goto"><img src="../static/images/arrow1.png" alt=""></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="fruit">果子:9,999.00</p>
</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,
prev:{name:"",url:"",params:{}},
current:{name:"friend_list",url:"friend_list.html",params:{}},
}
},
created(){
this.get_friends();
},
methods:{
get_friends(){
},
goto_home(){
// 退出当前页面
this.game.outFrame("friend_list","friend_list.html", this.current);
},
}
});
}
</script>
</body>
</html>
css/main.css
,样式代码:
.friends_list .avatar{
width: 6.39rem;
height: 6.39rem;
position: relative;
}
.friends_list .avatar_bf{
position: absolute;
z-index: 1;
margin: auto;
width: 4.56rem;
height: 4.56rem;
top: 0;
bottom: 0;
left: 0;
right: 0;
}
.friends_list .user_avatar{
position: absolute;
z-index: 1;
width: 4.56rem;
height: 4.56rem;
margin: auto;
top: 0;
bottom: 0;
left: 0;
right: 0;
border-radius: 1rem;
}
.friends_list .avatar_border{
position: absolute;
z-index: 1;
margin: auto;
top: 0;
bottom: 0;
left: 0;
right: 0;
width: 6.1rem;
height: 6.1rem;
}
.friends_list{
position: absolute;
top: 0rem;
left: 3.6rem;
}
.friends_list .item{
position: relative;
background-color: rgba(196,81,9,0.1);
border-radius: 4px;
height: 7rem;
width: 25.8rem;
margin-bottom: 1rem;
box-shadow: 2px 2px 5px rgba(9,9,9,0.1);
}
.friends_list .item .avatar{
position: absolute;
left: 1rem;
top: 0;
bottom: 0;
margin: auto;
}
.friends_list .item .info{
position: absolute;
left: 8rem;
top: 2rem;
color: #fff;
width: 10rem;
}
.friends_list .item .behavior{
position: absolute;
left: 16rem;
font-size: 1.5rem;
text-align: center;
line-height: 4rem;
height: 4rem;
width: 4rem;
color: #fff;
top: 0;
bottom: 0;
margin: auto;
box-shadow: 2px 2px 5px #333333;
}
.friends_list .item .pick{
background: #336633;
border-radius: 50%;
}
.friends_list .item .protect{
background: #990000;
border-top-right-radius: 5px;
border-top-left-radius: 5px;
border-bottom-left-radius: 30px;
border-bottom-right-radius: 30px;
}
.friends_list .item .goto{
position: absolute;
left: 23rem;
top: 0;
bottom: 0;
margin: auto;
width: 0.96rem;
height: 1.8rem;
}
设置打开好友列表页面的同时,也打开好友列表数据页面代码,html/user.html
在js代码中修改open_friend_list方法代码:
<!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" id="app">
<div class="bg">
<img src="../static/images/bg0.jpg">
</div>
<img class="back" @click="goto_index" src="../static/images/user_back.png" alt="">
<img class="setting" @click='goto_setting' src="../static/images/setting.png" alt="">
<div class="header">
<div class="info">
<div class="avatar">
<img class="avatar_bf" src="../static/images/avatar_bf.png" alt="">
<img class="user_avatar" :src="avatar" alt="">
<img class="avatar_border" src="../static/images/avatar_border.png" alt="">
</div>
<p class="user_name">{{nickname}}</p>
</div>
<div class="wallet">
<div class="balance">
<p class="title"><img src="../static/images/money.png" alt="">钱包</p>
<p class="num">99,999.00</p>
</div>
<div class="balance">
<p class="title"><img src="../static/images/integral.png" alt="">果子</p>
<p class="num">99,999.00</p>
</div>
</div>
<div class="invite">
<img class="invite_btn" src="../static/images/invite.png" alt="">
</div>
</div>
<div class="menu">
<div class="item" @click='open_friend_list'>
<span class="title">我的好友</span>
<span class="value">查看</span>
</div>
<div class="item">
<span class="title">我的主页</span>
<span class="value">查看</span>
</div>
<div class="item">
<span class="title">任务列表</span>
<span class="value">75%</span>
</div>
<div class="item">
<span class="title">收益明细</span>
<span class="value">查看</span>
</div>
<div class="item">
<span class="title">实名认证</span>
<span class="value">未认证</span>
</div>
</ul>
</div>
</div>
<script>
apiready = function(){
init();
new Vue({
el:"#app",
data(){
return {
nickname: '',
avatar: '',
prev:{name:"",url:"",params:{}},
current:{name:"user",url:"user.html",params:{}},
}
},
created(){
this.get_user_info();
this.change_avatar();
},
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}`;
}
});
},
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;
this.game.print(res);
if(parseInt(res.errno) === 1000){
this.nickname = res.nickname;
this.avatar = `${this.settings.avatar_url}?sign=${res.avatar}&token=${token}`;
}
})
},
goto_index(){
// 返回首页
this.game.outWin("user");
},
goto_setting(){
// 进入设置
this.game.goFrame('setting', 'setting.html', this.current);
},
open_friend_list(){
// 进入好友列表
this.game.goFrame('friends', 'friends.html', this.current, {}, {}, true);
this.game.goFrame('friend_list', 'friend_list.html', this.current, {
x: 0,
y: 194,
w: 'auto',
h: 'auto'
});
}
}
});
}
</script>
</body>
</html>
添加拉下拉新效果,html/friend_list.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="friends_list">
<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="fruit">果子:9,999.00</p>
</div>
<div class="behavior pick">摘</div>
<div class="goto"><img src="../static/images/arrow1.png" alt=""></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="fruit">果子:9,999.00</p>
</div>
<div class="goto"><img src="../static/images/arrow1.png" alt=""></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="fruit">果子:9,999.00</p>
</div>
<div class="behavior protect">护</div>
<div class="goto"><img src="../static/images/arrow1.png" alt=""></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="fruit">果子:9,999.00</p>
</div>
<div class="behavior pick">摘</div>
<div class="goto"><img src="../static/images/arrow1.png" alt=""></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="fruit">果子:9,999.00</p>
</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,
prev:{name:"",url:"",params:{}},
current:{name:"friend_list",url:"friend_list.html",params:{}},
}
},
created(){
this.get_friends();
},
methods:{
get_friends(){
// 通过请求获取当前用户的好友列表
api.setRefreshHeaderInfo({
loadingImg: 'widget://image/refresh.png',
bgColor: null,
textColor: '#fff',
textDown: '下拉刷新...',
textUp: '松开刷新...',
}, (ret, err)=>{
// 在这里从服务器加载数据,加载完成后调用api.refreshHeaderLoadDone()方法恢复组件到默认状态
api.refreshHeaderLoadDone();
});
},
goto_home(){
// 退出当前页面
this.game.outFrame("friend_list","friend_list.html", this.current);
},
}
});
}
</script>
</body>
</html>
关闭好友列表页时,需要同时关闭2个frame页面。所以需要在html/friends.html
关闭时,通知friend_list.html
关闭。
html/friends.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/friends_bg.png">
</div>
<img class="back" @click="goto_home" src="../static/images/user_back.png" alt="">
<div class="add_friend_btn" @click="add_friend">
<img src="../static/images/add_friends.png" alt="">
</div>
</div>
<script>
apiready = function(){
init();
new Vue({
el:"#app",
data(){
return {
friends:[],
prev:{name:"",url:"",params:{}},
current:{name:"friends",url:"friends.html",params:{}},
}
},
created(){
// 打开好友列表数据页面
this.game.goFrame('friend_list', 'friend_list.html', this.current, {
x: 0,
y: 190,
w: 'auto',
h: 'auto',
}, null, true);
},
methods:{
add_friend(){
// 添加好友
},
goto_home(){
// this.game.outWin('friends');
// 发起通知,告知friend_list退出
api.sendEvent({
name: 'out_page_to_user',
extra: {
key1: 'user',
key2: 'user.html'
}
});
// 退出当前页面
setTimeout(()=>{
this.game.outFrame("friends","friends.html", this.current);
}, 100);
},
}
});
}
</script>
</body>
</html>
html/friend_list.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="friends_list">
<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="fruit">果子:9,999.00</p>
</div>
<div class="behavior pick">摘</div>
<div class="goto"><img src="../static/images/arrow1.png" alt=""></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="fruit">果子:9,999.00</p>
</div>
<div class="goto"><img src="../static/images/arrow1.png" alt=""></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="fruit">果子:9,999.00</p>
</div>
<div class="behavior protect">护</div>
<div class="goto"><img src="../static/images/arrow1.png" alt=""></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="fruit">果子:9,999.00</p>
</div>
<div class="behavior pick">摘</div>
<div class="goto"><img src="../static/images/arrow1.png" alt=""></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="fruit">果子:9,999.00</p>
</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,
prev:{name:"",url:"",params:{}},
current:{name:"friend_list",url:"friend_list.html",params:{}},
}
},
created(){
this.get_friends();
this.page_out_listener();
},
methods:{
page_out_listener(){
// 监听什么时候需要退出当前页面
api.addEventListener({
name: 'out_page_to_user'
}, (ret, err)=>{
this.goto_home();
});
},
get_friends(){
// 通过请求获取当前用户的好友列表
api.setRefreshHeaderInfo({
loadingImg: 'widget://image/refresh.png',
bgColor: null,
textColor: '#fff',
textDown: '下拉刷新...',
textUp: '松开刷新...',
}, (ret, err)=>{
// 在这里从服务器加载数据,加载完成后调用api.refreshHeaderLoadDone()方法恢复组件到默认状态
api.refreshHeaderLoadDone();
});
},
goto_home(){
// 退出当前页面
this.game.outFrame("friend_list","friend_list.html", this.current);
},
}
});
}
</script>
</body>
</html>
2.添加好友
html/add_friend.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 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">
<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">刚刚搜索</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">3小时前</p>
</div>
<div class="status" @click="change_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 {
account:"",
prev:{name:"",url:"",params:{}},
current:{name:"add_friend",url:"add_friend.html",params:{}},
}
},
methods:{
close_frame(){
this.game.outFrame("add_friend");
},
add_friend_commit(){
// 提交搜索信息
},
change_status(){
// 状态修改
}
}
});
}
</script>
</body>
</html>
css/main.css
,代码:
input::-webkit-input-placeholder,
textarea::-webkit-input-placeholder{
color: #fff;
}
.add_friend .box{
top: 4rem;
height: 55.56rem;
background: url("../images/long_bg1.png") no-repeat 0 0;
background-size: 100%;
}
.add_friend .nickname{
margin: 4rem 4.6rem 2rem;
width: 19rem;
height: 4rem;
line-height: 4rem;
background-color: #cc9966;
outline: none;
border: 1px solid #330000;
text-align: center;
font-size: 1rem;
color: #ffffcc;
}
.add_friend .friends_list{
position: absolute;
top: 15rem;
left: 3.6rem;
}
.add_friend .friends_list .item{
position: relative;
margin-left: 1rem;
background-color: rgba(196,81,9,0.1);
border-radius: 4px;
height: 4rem;
width: 19rem;
margin-bottom: 1rem;
box-shadow: 2px 2px 5px rgba(9,9,9,0.1);
}
.add_friend .friends_list .avatar{
width: 3.84rem;
height: 3.84rem;
position: absolute;
left: 1rem;
}
.add_friend .friends_list .avatar_bf{
position: absolute;
z-index: 1;
margin: auto;
width: 2.74rem;
height: 2.74rem;
top: 0;
bottom: 0;
left: 0;
right: 0;
}
.add_friend .friends_list .user_avatar{
position: absolute;
z-index: 1;
width: 2.74rem;
height: 2.74rem;
margin: auto;
top: 0;
bottom: 0;
left: 0;
right: 0;
border-radius: 1rem;
}
.add_friend .friends_list .avatar_border{
position: absolute;
z-index: 1;
margin: auto;
top: 0;
bottom: 0;
left: 0;
right: 0;
width: 3.66rem;
height: 3.66rem;
}
.add_friend .friends_list .item .info{
top: 0.6rem;
left: 6rem;
}
.add_friend .friends_list .item .time{
font-size: 0.6rem;
}
.friends_list .item .status{
position: absolute;
left: 12rem;
top: 1.2rem;
width: 8rem;
text-align: center;
height: 2rem;
color: #fff;
}
二、服务端提供接口
1.模型创建
application/apps/users/models.py
,代码:
from werkzeug.security import generate_password_hash, check_password_hash
from application.utils.models import BaseModel,db
class User(BaseModel):
"""用户基本信息"""
__tablename__ = "mf_user"
name = db.Column(db.String(255), index=True, comment="用户账户")
_password = db.Column(db.String(255), comment="登录密码")
_transaction_password = db.Column(db.String(255), comment='交易密码')
nickname = db.Column(db.String(255), comment="用户昵称")
age = db.Column(db.SmallInteger, comment="年龄")
money = db.Column(db.Numeric(7,2), comment="账户余额")
ip_address = db.Column(db.String(255), default="", index=True, comment="登录IP")
intro = db.Column(db.String(500), default="", comment="个性签名")
avatar = db.Column(db.String(255), default="", comment="头像url地址")
sex = db.Column(db.SmallInteger, default=0, comment="性别") # 0表示未设置,保密, 1表示男,2表示女
email = db.Column(db.String(32), index=True, default="", nullable=False, comment="邮箱地址")
mobile = db.Column(db.String(32), index=True, nullable=False, comment="手机号码")
unique_id = db.Column(db.String(255), index=True, default="", comment="客户端唯一标记符")
province = db.Column(db.String(255), default="", comment="省份")
city = db.Column(db.String(255), default="", comment="城市")
area = db.Column(db.String(255), default="", comment="地区")
info = db.relationship('UserProfile', backref='user', uselist=False)
@property
def password(self):
return self._password
@password.setter
def password(self, rawpwd):
"""密码加密"""
self._password = generate_password_hash(rawpwd)
def check_password(self, rawpwd):
"""验证密码"""
return check_password_hash(self.password, rawpwd)
@property
def transaction_password(self):
return self._transaction_password
@transaction_password.setter
def transaction_password(self, rawpwd):
"""密码加密"""
self._transaction_password = generate_password_hash(rawpwd)
def check_transaction_password(self, rawpwd):
"""验证密码"""
return check_password_hash(self.transaction_password, rawpwd)
class UserProfile(BaseModel):
"""用户详情信息表"""
__tablename__ = "mf_user_profile"
user_id = db.Column(db.Integer,db.ForeignKey('mf_user.id'), comment="用户ID")
education = db.Column(db.Integer, comment="学历教育")
middle_school = db.Column(db.String(255), default="", comment="初中/中专")
high_school = db.Column(db.String(255), default="", comment="高中/高职")
college_school = db.Column(db.String(255), default="", comment="大学/大专")
profession_cate = db.Column(db.String(255), default="", comment="职业类型")
profession_info = db.Column(db.String(255), default="", comment="职业名称")
position = db.Column(db.SmallInteger, default=0, comment="职位/职称")
emotion_status = db.Column(db.SmallInteger, default=0, comment="情感状态")
birthday =db.Column(db.DateTime, default="", comment="生日")
hometown_province = db.Column(db.String(255), default="", comment="家乡省份")
hometown_city = db.Column(db.String(255), default="", comment="家乡城市")
hometown_area = db.Column(db.String(255), default="", comment="家乡地区")
hometown_address = db.Column(db.String(255), default="", comment="家乡地址")
living_province = db.Column(db.String(255), default="", comment="现居住省份")
living_city = db.Column(db.String(255), default="", comment="现居住城市")
living_area = db.Column(db.String(255), default="", comment="现居住地区")
living_address = db.Column(db.String(255), default="", comment="现居住地址")
def __repr__(self):
return "<%s %s>" % (self.__class__.__name__, self.user.name)
class UserRelation(BaseModel):
"""用户关系"""
__tablename__ = 'mf_user_relation'
relation_status_choice = (
(1, '好友'),
(2, '关注'),
(3, '拉黑'),
)
relation_type_choice = (
(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)
python manage.py db migrate -m 'add user relation'
python manage.py db upgrade
2.搜索用户信息
html/add_friend.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 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">添加</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" @click="change_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 {
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 = [];
}
}
},
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_llist;
}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_status(){
// 状态修改
}
}
});
}
</script>
</body>
</html>
服务端提供用户搜索功能的视图代码,users/views.py
:
from sqlalchemy import or_
from .models import UserRelation
from .marshmallow import UserSearchInfoSchema as usis
@jsonrpc.method('User.user.relation')
@jwt_required # 验证jwt
def user_relation(account):
"""搜索用户信息"""
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,
}
# 1.识别搜索用户
receive_user_list = User.query.filter(or_(
User.mobile == account,
User.name == account,
User.nickname.contains(account),
User.email == account
)).all()
if len(receive_user_list) < 1:
return {
'errno': status.CODE_NO_USER,
'errmsg': message.receive_user_not_exists,
}
# context 可用于把视图中的数据传递给marshmallow转换器中使用
marshmallow = usis(many=True, context={'user_id': user.id})
user_list = marshmallow.dump(receive_user_list)
return {
'errno': status.CODE_OK,
'errmsg': message.ok,
'user_list': user_list
}
message.py
....
receive_user_not_exists = "搜索的用户不存在!"
marshmallow.py
,代码:
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)
class Meta:
model = User
include_fk = True
include_relationships = True
fields = ["id","nickname","avatar","relation_status"]
sql_session = db.session
在上面服务端接口提供的数据中,增加双方关系状态信息.users/marshmallow.py
,代码:
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):
relationship = 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 relationship is not None:
data['relation_status'] = UserRelation.relation_status_choice[relationship.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
客户端根据不同的关系状态,提供对应的操作菜单,add_friend.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 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.rrelation_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>