个人音乐播放器过程总结1

网络请求api的封装

requiest/index.js

import axios from 'axios';
let service = axios.create({
    baseURL: 'http://localhost:3000/',  //baseURL自动添加在请求地址上
    timeout: 3000,
    withCredentials: true,  //允许跨域
})

export default service

request/home/api.js

import service from '..';
//邮箱登录接口
export function getEmailLogin(user) {
    // 此时的service已经配置了baseURL和timeout
    return service({
        method: 'GET',
        url: `/login?email=${user.email}&password=${user.password}`
    })
}
//其他接口
export function getXXX(params){
    returnservice({
        methods:'',
        url:`...`
    })
}

登录功能

这是第一次真正实现登录功能,所谓的登录,就是前端向后端传递必要参数(用户名,密码)过后,后端返回cookie(token)及其他信息,前端将cookie(token)保存在浏览器localStorage里,此后基于登录的请求头都需要添加上cookie(token),才能合法访问服务器资源。

store/actions.js

  import { getEmailLogin } from '@/request/api/home.js'
actions: {
    async getLogin(context, value) {
      let res = await getEmailLogin(value)
      console.log(res);
      return res
    }
  },

login.vue

  methods: {
    async login() {
    //触发store/actions.js中的getLogin函数
      let res = await this.$store.dispatch("getLogin", {
        email: this.email,
        password: this.password,
      });
      //res为登录返回信息,包括cookie、token、和用户信息
      if (res.data.code == 200) {
        //说明登陆成功
        /*修改登录状态值、设置token、获取用户信息并添加到user、跳转路由到用户中心 */
        this.$store.commit("updateIsLogin", true);//修改登录状态值
        this.$store.commit("updateToken", res.data.token); //存储token到store里
        let userinfo = await getUser(res.data.account.id); //获取用户详细信息,包含userId
        //将用户信息保存到store.state.user中
        this.$store.commit("updateUser", userinfo); 
        //跳转到个人中心,并携带userId
        this.$router.push({
          path: "/default/userinfo",
          query: { uid: userinfo.data.profile.userId },
        });
      } else {
        this.$message({
          message: "登录失败:" + res.message,
          type: "warning",
        });
      }
    },
  },

UserInfo.vue

/* 进入用户中心前有路由守卫,进入后需要获取用户详情 */
computed: {
  ...mapState(["user"]), //未登录状态为{}
},  
mounted() {
    //复制$store.state.user,若是从登录界面跳转,则user刚得到保存不为空
    //若在登录后刷新浏览器,则$store.state.user为空
    this.userinfo = this.user; 
    if (JSON.stringify(this.userinfo) !== "{}") {
    // 如果$store.state.user不为空(说明在登录),则将最新用户的信息存储到localStorage中
    localStorage.setItem("userInfo", JSON.stringify(this.userinfo)); //obj转json
    //同时将登录界面传递的uid路由参数赋值给uid
    this.uid = this.$route.query.uid;
    } else {
      //若为空{},则说明未登录或登陆后浏览器被刷新,则从localStorage取值
      this.userinfo = JSON.parse(localStorage.getItem("userInfo")); //json转obj
    }
    //此时userinfo或从最新的vuex中取到值(从登录界面跳转),
    //或从本地lacalStorage取值(其他界面跳转或浏览器刷新过),一定为用户信息
     
    //如果query.uid为undefined,说明不是从登录页面跳转,则从localStorage中取uid
    if (this.uid == undefined || this.uid == '' || this.uid == {}) {
      this.uid = this.userinfo.data.profile.userId;
      console.log("this.uid",this.uid);
    }
    //此时uid一定为用户id,然后就可以根据uid请求用户其他信息并渲染到页面即可。

  },

搜索历史

localStorage实现持久化,存在data函数中实现响应式

//每次进入该页面均从localStorage中获取搜索记录 
mounted() {
    this.keyWordList = JSON.parse(localStorage.getItem("keyWordList")) || []; //json转对象
},
methods:{
    //添加搜索历史
    addHistory(searchKeyWord) {
      //如果关键词不为空则将关键词添加到keyWordList列表中
      if (this.searchKeyWord !== "") {
        this.keyWordList.unshift(searchKeyWord);
        //去重
        let arr = new Set(this.keyWordList); //一个没有重复内容的类数组
        this.keyWordList = [...arr]; //将类数组变成数组并赋值给keyWordList
        //固定搜索记录长度
        if (this.keyWordList.length > 10) {
        this.keyWordList.splice(this.keyWordList.length - 1, 1); //从最后一个开始删除一个元素
        }
        //将搜索记录添加在本地存储中
        localStorage.setItem("keyWordList", JSON.stringify(this.keyWordList)); //对象转JSON
      }
}

进度条的实现

播放组件(不可见)、slider滑块组件

<audio
  ref="audio"
  preload="metadata"
  :src="`https://music.163.com/song/media/outer/url?id=${id}.mp3`"
></audio>
<!--input事件在拖动滑块时触发,回调参数为实时值;change事件在拖拽结束时触发,参数为拖拽结束位置的值-->
<el-slider
    @input="sliderStop"
    @change="sliderPlay"
    :show-tooltip="false"
    :min="0"
    :max="duration"
    :format-tooltip="(val) => durationFormat(val)"
    v-model="currentTime"
    @mousedown.native="isChange = true"
    @mouseup.native="isChange = false"
  ></el-slider>
data(){
  return{
    interVal: 0, //定时器
    isChange: false, //是否正在被拖动
  }
}
mounted() {
    //获取音频时长
    setTimeout(() => {
      //audio.duration较为特殊,不能直接获取,需要延迟才能拿到该属性
      this.$store.commit("updateDuration", this.$refs.audio.duration);
    }, 3400);
  },
methods:{
  //每秒更新一次store里的currentTime值
  updateTime() {
    this.interVal = setInterval(() => {
     this.$store.commit("updateCurrentTime", this.$refs.audio.currentTime);
   }, 1000);
  },
  play() {
    if (this.$refs.audio.paused) {
    //如果暂停时play则播放
    this.$refs.audio.play();
    this.updatePlaying(false); //隐藏暂停键
    this.updateTime(); //播放就一直更新currentTime
    } else {
    //如果播放时play则暂停
    this.$refs.audio.pause();
    this.updatePlaying(true); //显示暂停键
    clearInterval(this.interVal); //清除定时器,停止更新currentTime
    }
  },
    //正在被拖动时执行
    sliderStop(value) {
      if (this.isChange) {
        clearInterval(this.interVal); //清除定时器
        let audio = this.$refs.audio;
        audio.pause(); //暂停播放音乐
        audio.currentTime = value;  //[1]将audio的currentTime属性设为拖动值
        this.$store.commit("updateCurrentTime", value); //[2]防止进度条反弹,快速响应
        this.playing = false;
      }
    },
    //拖拽结束时执行
    sliderPlay(value) {
      this.play();
      this.isChange = false;
    }
}

面包屑组件

router.js

import Vue from 'vue';
import vueRouter from 'vue-router'
Vue.use(vueRouter)

const routes = [
        { path: '*', name: 'NotFound', component: NotFound, hidden: true },
        {
        path: '/home',
        name: '学生管理',
        iconClass: 'fa fa-user',
        component: () => import('@/components/Home.vue'),
        children: [
            {
                path: '/home/student',
                name: '学生列表',
                iconClass: 'fa fa-list',
                component: () => import('@/components/students/StudentsList.vue'),
            },
            {
                path: '/home/info',
                name: '信息列表',
                iconClass: 'fa fa-list-alt',
                component: () => import('@/components/students/InfoList.vue'),
            },
        ]
    },
]

const router = new vueRouter({
    routes,
})
export default router

面包屑公共组件

<el-card>
  <el-breadcrumb separator-class="el-icon-arrow-right">
    <el-breadcrumb-item :to="{ path: '/' }">首页</el-breadcrumb-item>
    <el-breadcrumb-item
      v-for="(item, index) in $route.matched"
      :key="index"
    >{{ item.name }}</el-breadcrumb-item>
  </el-breadcrumb>
 </el-card>

其他补充

组件传参:

路由传参

登录页跳转个人中心:

login.vue

login(){
...
  this.$router.push({
    path: "/default/userinfo",
    query: { uid: userinfo.data.profile.userId },
  });
//uid为参数
}

个人中心

this.uid = this.$route.query.uid;

父子组件传参

父组件Footer.vue

<!-- 底部播放组件传值给弹出框组件 -->
<el-dialog :visible.sync="dialogVisible" width="30%" :fullscreen="true">
  <Song :dt="duration" />
</el-dialog>

子组件Song.vue:

export default {
  props: ["dt"],
}

CSS

超长文本用省略号代替

<div class="des" @click="centerDialogVisible = true">
  {{ playListInfo.description }}
</div>
<el-dialog
  title="歌单介绍"
  :visible.sync="centerDialogVisible"
  width="50%"
  center
 >
<div>{{ playListInfo.description }}</div>
<span slot="footer" class="dialog-footer">
<el-button type="primary" @click="centerDialogVisible = false">
确定
</el-button>
 </span>
</el-dialog>

.des {
      display: -webkit-box;
      text-overflow: ellipsis; /*超出内容用省略号*/
      overflow: hidden; /*内容超出后隐藏*/
      -webkit-box-orient: vertical;
      -webkit-line-clamp: 3; /*2行*/
      word-break: break-all;
      cursor: pointer;
    }

内容横向滚动

<div class="artists">
  <div class="artistItem" v-for="(item, index) in topSingers" :key="index">
    <router-link :to="{ path: '/singer', query: { id: item.id } }">
      <img :src="item.img1v1Url" />
      <p>{{ item.name }}</p>
    </router-link>
  </div>
</div>
<style>
.artists {
  width: 100%;
  overflow: scroll;
  overflow-y: hidden;
  overflow-x: auto;
  display: flex;
  .artistItem {
    margin-left: 12px;
    img {
      border-radius: 50%;
    }
    text-align: center;
  }
}
</style>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值