微信小程序(五)个人中心页
1. 效果
1. 逻辑
1. 如果未登录.点击头像可以跳转到登录页面,然后扫码进行登录(只能用登录)
2. 登录完成之后:
(1). 将登录的cookie 信息存到本地,同时每次访问的时候都携带cookie维持登录状态
(2). 将userInfo 信息存到本地
(3). 跳转到登录界面
3. 首页onLoad
(1). 获取用用户信息
(2). 请求接口获取当前用户的历史播放记录
2. 效果演示
2. 核心代码
1. 登录页
- wxml
<view class="loginContainer">
<image class="loginQrImg" src="{{qrCodeSrc}}" />
<div id="info" class="info">使用网易云音乐扫码登录</div>
</view>
- wxss
.loginContainer {
display: flex;
flex-direction: column;
align-items: center;
}
- js
import request from '../../utils/request'
Page({
/**
* 页面的初始数据
*/
data: {
qrCodeSrc: ''
},
/**
* 生命周期函数--监听页面加载
*/
onLoad: async function (options) {
await this.login();
},
/**S 自定义函数 */
// 获取登录结果。二维码检测扫码状态接口
checkStatus: async function(key) {
const res = await request(`/login/qr/check?key=${key}×tamp=${Date.now()}&noCookie=true`);
return res;
},
// 获取登录状态
getLoginStatus: async function (cookie = '') {
const res = await request(
`/login/status?timestamp=${Date.now()}`,{cookie},'post'
);
return res;
},
login: async function() {
let timer
let timestamp = Date.now()
// 如果判断已经登录跳转可以用这里的方法
// const cookie = wx.getStorageSync('cookies')
// const loginStatus1 = await this.getLoginStatus(cookie)
// console.log("loginStatus1", loginStatus1)
// 1. 获取qr key
const res = await request(
`/login/qr/key?timestamp=${Date.now()}`
);
// 2. 获取验证码地址
const key = res.data.unikey
const res2 = await request(
`/login/qr/create?key=${key}&qrimg=true×tamp=${Date.now()}`
);
this.setData({
qrCodeSrc: res2.data.qrimg
});
// 3. 定时器获取验证码扫码状态。 登录成功之后带着cookie 获取登录状态拿到用户信息
timer = setInterval(async () => {
const statusRes = await this.checkStatus(key)
if (statusRes.code === 800) {
wx.showToast('二维码已过期,请重新获取')
clearInterval(timer)
}
if (statusRes.code === 803) {
// 这一步会返回cookie
clearInterval(timer)
wx.showToast({
title: '授权登录成功',
duration: 2000
})
// 带着cookie获取用户信息
const loginStatus2 = await this.getLoginStatus(statusRes.cookie)
// 将用户的cookie存入至本地
wx.setStorage({
key: 'cookies',
data: statusRes.cookie
})
// 用户信息存到本地
wx.setStorageSync('userInfo', JSON.stringify(loginStatus2.data.profile))
// 跳转至个人中心personal页面
wx.reLaunch({
url: '/pages/personal/personal'
})
}
}, 3000)
}
/**E 自定义函数 */
})
2. 首页代码
- wxml
<view class="personalContainer">
<view class="user-section">
<image class="bg" src="/static/images/personal/bgImg2.jpg"></image>
<view class="user-info-box" bindtap="toLogin">
<view class="portrait-box">
<image class="portrait" src='{{userInfo.avatarUrl?userInfo.avatarUrl:"/static/images/personal/missing-face.png"}}'></image>
</view>
<view class="info-box">
<text class="username">{{userInfo.nickname?userInfo.nickname: '游客'}}</text>
</view>
</view>
<view class="vip-card-box">
<image class="card-bg" src="/static/images/personal/vip-card-bg.png" mode=""></image>
<view class="b-btn">
立即开通
</view>
<view class="tit">
<!-- 会员图标-->
<text class="iconfont icon-huiyuan-"></text>
Geogre会员
</view>
<text class="e-m">geogre Union</text>
<text class="e-b">开通会员听歌, 撸代码</text>
</view>
</view>
<view class="cover-container" catchtouchstart="handleTouchStart"
catchtouchmove="handleTouchMove"
catchtouchend="handleTouchEnd"
style="transform: {{coverTransform}}; transition: {{coveTransition}}">
<image class="arc" src="/static/images/personal/arc.png"></image>
<!-- 个人中心导航 -->
<view class="nav-section">
<view class="nav-item" hover-class="common-hover" hover-stay-time="50">
<text class="iconfont icon-xiaoxi"></text>
<text>我的消息</text>
</view>
<view class="nav-item" hover-class="common-hover" hover-stay-time="50">
<text class="iconfont icon-myRecommender"></text>
<text>我的好友</text>
</view>
<view class="nav-item" hover-class="common-hover" hover-stay-time="50">
<text class="iconfont icon-gerenzhuye"></text>
<text>个人主页</text>
</view>
<view class="nav-item" hover-class="common-hover" hover-stay-time="50">
<text class="iconfont icon-gexingzhuangban"></text>
<text>个性装扮</text>
</view>
</view>
<!-- 个人中心列表 -->
<view class="personalContent">
<view class="recentPlayContainer">
<text class="title">最近播放</text>
<!-- 最近播放记录 -->
<!-- 最近播放记录 -->
<scroll-view wx:if="{{recentPlayList.length}}" scroll-x class="recentScroll" enable-flex>
<view class="recentItem" wx:for="{{recentPlayList}}" wx:key="{{id}}">
<image src="{{item.song.al.picUrl}}"></image>
<view class="recentSongName">{{item.song.al.name}}</view>
</view>
</scroll-view>
<view wx:else>暂无播放记录</view>
</view>
<view class="cardList">
<view class="card-item">
<text class="title">我的音乐</text>
<text class="more"> > </text>
</view>
<view class="card-item">
<text class="title">我的收藏</text>
<text class="more"> > </text>
</view>
<view class="card-item">
<text class="title">我的电台</text>
<text class="more"> > </text>
</view>
</view>
</view>
</view>
</view>
- wxss
/* pages/personal/personal.wxss */
.personalContainer {
width: 100%;
height: 100%;
}
.personalContainer .user-section {
height: 520rpx;
position: relative;
padding: 100rpx 30rpx 0;
}
.user-section .bg {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
opacity: 0.7;
filter: blur(1px);
}
.user-info-box{
height: 180rpx;
display:flex;
align-items:center;
position:relative;
z-index: 1;
}
.user-info-box .portrait{
width: 130rpx;
height: 130rpx;
border:5rpx solid #fff;
border-radius: 50%;
}
.user-info-box .username{
font-size: 24;
color: #303133;
margin-left: 20rpx;
}
/* vip-box */
.vip-card-box {
position: relative;
display: flex;
flex-direction: column;
background: rgba(0, 0, 0, .7);
height: 240rpx;
color: #f7d680;
border-radius: 16rpx 16rpx 0 0;
padding: 20rpx 24rpx;
}
.vip-card-box .card-bg{
position:absolute;
top: 20rpx;
right: 0;
width: 380rpx;
height: 260rpx;
}
.vip-card-box .b-btn{
position: absolute;
right: 20rpx;
top: 16rpx;
width: 132rpx;
height: 40rpx;
text-align: center;
line-height: 40rpx;
font-size: 22rpx;
color: #36343c;
border-radius: 20px;
background: #f9e6af;
z-index: 1;
}
.vip-card-box .b-btn{
position: absolute;
right: 20rpx;
top: 16rpx;
width: 132rpx;
height: 40rpx;
text-align: center;
line-height: 40rpx;
font-size: 22rpx;
color: #36343c;
border-radius: 20px;
/*background: linear-gradient(left, #f9e6af, #ffd465);*/ /*渐变不生效*/
background: #f9e6af;
z-index: 1;
}
.vip-card-box .tit {
font-size: 22rpx;
color: #f7d680;
margin-bottom: 28rpx;
}
.vip-card-box .tit .iconfont{
color: #f6e5a3;
margin-right: 16rpx;
}
.vip-card-box .e-m{
font-size: 34rpx;
margin-top: 10rpx;
}
.vip-card-box .e-b{
font-size: 24rpx;
color: #d8cba9;
margin-top: 10rpx;
}
.cover-container{
margin-top: -150rpx;
padding: 0 30rpx;
position:relative;
background: #f5f5f5;
padding-bottom: 20rpx;
}
.cover-container .arc{
position:absolute;
left: 0;
top: -34rpx;
width: 100%;
height: 36rpx;
}
/* 导航部分 */
.cover-container .nav-section {
display: flex;
background: #fff;
padding: 20rpx 0;
border-radius: 15rpx;
}
.nav-section .nav-item {
width: 25%;
box-sizing: border-box;
display: flex;
flex-direction: column;
align-items: center;
}
.nav-section .nav-item .iconfont {
font-size: 50rpx;
color: #d43c33;
line-height: 70rpx;
}
.nav-section .nav-item text:last-child {
font-size: 22rpx;
}
/* 个人中心列表 */
.personalContent {
background: #fff;
margin-top: 20rpx;
}
/* 最近播放 */
.personalContent .scrollView {
display: flex;
height: 160rpx;
}
.personalContent .recentPlay {
display: flex;
}
.recentPlayContainer .title {
padding-left: 20rpx;
font-size: 26rpx;
color: #333;
line-height: 80rpx;
}
.personalContent .recentPlay image {
width: 160rpx;
height: 160rpx;
margin-left: 20rpx;
border-radius: 20rpx;
}
.cardList {
margin-top: 20rpx;
}
.cardList .card-item{
border-top: 1rpx solid #eee;
height: 80rpx;
line-height: 80rpx;
padding: 10rpx;
font-size: 26rpx;
color: #333;
}
.cardList .card-item .more {
float: right;
}
/* 最近播放记录 */
.recentScroll {
display: flex;
height: 250rpx;
}
.recentItem {
margin-right: 20rpx;
}
.recentItem image {
width: 200rpx;
height: 200rpx;
border-radius: 10rpx;
}
.recentSongName {
display: -webkit-box;
-webkit-box-orient: vertical; /**对齐模式*/
-webkit-line-clamp: 1;
overflow: hidden;
text-overflow: ellipsis;
}
- js
import request from '../../utils/request'
let startY = 0; // 手指起始的坐标
let moveY = 0; // 手指移动的坐标
let moveDistance = 0; // 手指移动的距离
Page({
/**
* 页面的初始数据
*/
data: {
coverTransform: 'translateY(0)',
coveTransition: '',
userInfo: {}, // 用户信息
},
/**
* 生命周期函数--监听页面加载
*/
onLoad(options) {
// 从本地读取用户的基本信息
let userInfo = wx.getStorageSync('userInfo');
if(userInfo){ // 用户登录
// 更新userInfo的状态
this.setData({
userInfo: JSON.parse(userInfo)
})
// 获取用户播放记录
this.getUserRecentPlayList(this.data.userInfo.userId)
}
},
/****S 自定义函数 */
handleTouchStart(event){
this.setData({
coveTransition: ''
})
// 获取手指起始坐标
startY = event.touches[0].clientY;
},
handleTouchMove(event){
moveY = event.touches[0].clientY;
moveDistance = moveY - startY;
if(moveDistance <= 0){
return;
}
if(moveDistance >= 80){
moveDistance = 80;
}
// 动态更新coverTransform的状态值
this.setData({
coverTransform: `translateY(${moveDistance}rpx)`
})
},
handleTouchEnd(){
// 动态更新coverTransform的状态值
this.setData({
coverTransform: `translateY(0rpx)`,
coveTransition: 'transform 1s linear'
})
},
toLogin(){
wx.navigateTo({
url: '/pages/login/login'
})
},
// 获取用户播放记录的功能函数
async getUserRecentPlayList(userId){
let recentPlayListData = await request('/user/record', {uid: userId, type: 0});
let index = 0;
let recentPlayList = recentPlayListData.allData.splice(0, 10).map(item => {
item.id = index++;
return item;
})
this.setData({
recentPlayList
})
},
/****E 自定义函数 */
})
3. 知识点记录
1. `` 取变量
Js 中使用`` 是es6 语法,相当于直接使用变量。
this.setData({
coverTransform: `translateY(${moveDistance}rpx)`
})
2. {{ }} 中可以使用三元表达式:
<image class="portrait" src='{{userInfo.avatarUrl?userInfo.avatarUrl:"/static/images/personal/missing-face.png"}}'></image>
3. catchtouch... 是手指相关事件
<view class="cover-container" catchtouchstart="handleTouchStart"
catchtouchmove="handleTouchMove"
catchtouchend="handleTouchEnd"
style="transform: {{coverTransform}}; transition: {{coveTransition}}">
4. map 遍历改变对象
let recentPlayList = recentPlayListData.allData.splice(0, 10).map(item => {
item.id = index++;
return item;
})
【当你用心写完每一篇博客之后,你会发现它比你用代码实现功能更有成就感!】