微信小程序(五)个人中心页

1. 效果

1. 逻辑

1. 如果未登录.点击头像可以跳转到登录页面,然后扫码进行登录(只能用登录)
2. 登录完成之后:
(1). 将登录的cookie 信息存到本地,同时每次访问的时候都携带cookie维持登录状态
(2). 将userInfo 信息存到本地
(3). 跳转到登录界面
3. 首页onLoad
(1). 获取用用户信息
(2). 请求接口获取当前用户的历史播放记录

2. 效果演示

2. 核心代码

1. 登录页

  1. wxml
<view class="loginContainer">
  <image class="loginQrImg" src="{{qrCodeSrc}}" />
  <div id="info" class="info">使用网易云音乐扫码登录</div>
</view>
  1. wxss
.loginContainer {
  display: flex;
  flex-direction: column;
  align-items: center;
}
  1. 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}&timestamp=${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&timestamp=${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. 首页代码

  1. 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>
  1. 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;
}
  1. 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;
    })
posted @ 2023-07-16 22:30  QiaoZhi  阅读(492)  评论(0编辑  收藏  举报