Vue.js 实战系列之实现视频类WebApp的项目开发——14. 用户信息界面实现

如果想看该实战系列的其他内容,请移步至 Vue.js 实战系列之实现视频类WebApp的项目开发。

项目仓库地址,欢迎 Star


实现效果

Vue.js 实战系列之实现视频类WebApp的项目开发——14. 用户信息界面实现_第1张图片


功能实现

  1. “我的”界面设计

    mine/index.vue

    <template>
      <div class="mine">
        
        <div class="mine-top" :style="bgPic">
          
          <div class="menu-box">
            <span class="iconfont icon-caidan">span>
          div>
        div>
        
        <div class="mine-wrap">
          <div class="mine-content">
            
            <div class="info">
              <img
                src="@/assets/images/mine/tx.png"
                style=" height: 100px; width: 100px; border-radius: 50%; margin-right: 20px; "
              />
              <div class="info-right">
                <button class="btn">编辑资料button>
                <button class="btn">
                  <span class="iconfont icon-jiahao1">span> 朋友
                button>
              div>
            div>
            
            <div class="desc">
              <h2>前端小新h2>
              <p class="dyh">
                抖音号:98579335
                <span class="iconfont icon-erweima">span>
              p>
              <p class="jj">没有好看的皮囊,但有有趣的灵魂,技术没有止境p>
            div>
            
            <div class="user-tag">
              <span>
                <span class="iconfont icon-touxiang">span>
                23岁
              span>
              <span>中国span>
              <span><span class="iconfont icon-jiahao1">span>添加学校等标签span>
            div>
            <div class="user-tag2">
              <span><a>2a>获赞span>
              <span><a>543a>关注span>
              <span><a>1087a>粉丝span>
            div>
          div>
        div>
        
        <div class="mine-tab">
          <div class="tab-navbar">
            <div class="item" @click="changeTab(0)" :class="indexTab === 0 ? 'active' : '' ">作品div>
            <div class="item" @click="changeTab(1)" :class="indexTab === 1 ? 'active' : '' ">私密<span class="iconfont icon-suo">span>div>
            <div class="item" @click="changeTab(2)" :class="indexTab === 2 ? 'active' : '' ">喜欢<span class="iconfont icon-suo">span>div>
          div>
          <div class="tab-wrap">
            
            <div class="tab-con" v-show="indexTab === 0">
              <div class="tab-img" v-for="i in 10" :key="i">
                <img src="@/assets/images/mine/bj.png" style="width:100%;height:auto" >
              div>
            div>
            
            <div class="tab-con" v-show="indexTab === 1">
              <div class="tab-con2">
                <h3>没有私密作品h3>
                <p>设为私密的作品和过期的日记会出现在这里,并且只有你能看到p>
              div>
              
            div>
            
            <div class="tab-con" v-show="indexTab === 2">
              <div class="tab-con3">只有你能看到自己的喜欢列表div>
              <div class="tab-img" v-for="i in 10" :key="i">
                <img src="@/assets/images/mine/bj3.png" style="width:100%;height:auto" >
              div>
            div>
          div>
        div>
      div>
    template>
    
    <script>
    export default {
             
      data() {
             
        return {
             
          bgPic: {
             
            backgroundImage: `url(${ require('@/assets/images/mine/bj.png')})`,
            backgroundRepeat: 'no-repeat',
            backgroundSize: '100% 100%',
          },
          indexTab: 0,
        };
      },
      methods: {
             
        changeTab(index) {
             
          this.indexTab = index;
        },
      },
    };
    script>
    
    <style lang="less" scoped>
    .mine {
             
      .mine-top {
             
        height: 160px;
        display: flex;
        justify-content: flex-end;
        padding: 20px;
        .menu-box {
             
          width: 35px;
          height: 35px;
          border-radius: 50%;
          background: rgba(0, 0, 0, 0.3);
          display: flex;
          align-items: center;
          justify-content: center;
          .icon-caidan {
             
            color: #ffffff;
            font-size: 20px;
          }
        }
      }
      .mine-wrap {
             
        position: relative;
        // top: 180px;
        width: 100%;
        background-color: #101821;
        color: #ffffff;
        .mine-content {
             
          padding: 0 15px;
          .info {
             
            position: relative;
            top: -25px;
            display: flex;
            align-items: center;
            justify-content: space-between;
            .info-right {
             
              button {
             
                font-size: 17px;
                letter-spacing: 2px;
                height: 40px;
                width: 120px;
                padding: 0 2px;
                background-color: #393842;
                border: none;
                color: #ffffff;
                margin-left: 10px;
              }
            }
          }
          .desc {
             
            margin-top: -20px;
            h2 {
             
              font-size: 20px;
            }
            .dyh {
             
              font-size: 13px;
              height: 20px;
              line-height: 20px;
              padding: 5px 0 25px 0;
              color: rgb(163, 163, 163);
              .icon-erweima {
             
                font-size: 18px;
                font-weight: 600;
              }
            }
            .jj {
             
              font-size: 14px;
            }
          }
          .user-tag {
             
            height: 30px;
            margin-top: 0;
            font-size: 14px;
            color: #969696;
            span {
             
              margin-right: 5px;
              background: rgba(85, 85, 85, 0.6);
              padding: 5px 8px;
              .icon-touxiang {
             
                width: 10px;
                height: 10px;
                color: rgb(5, 224, 224);
                background-color: rgba(0, 0, 0, 0);
                margin: 0;
                padding: 0;
              }
              .icon-jiahao1 {
             
                background-color: rgba(0, 0, 0, 0);
                margin: 0;
                padding: 0;
              }
            }
          }
          .user-tag2 {
             
            padding: 15px 0;
            color: #9e9e9e;
            span {
             
              font-size: 16px;
              margin-right: 15px;
            }
            a {
             
              font-size: 18px;
              color: #ffffff;
              font-weight: 600;
              margin-right: 5px;
            }
          }
        }
      }
      .mine-tab {
             
        margin-top: -1px;
        height: 300px;
        width: 100%;
        background-color: #101821;
        .tab-navbar {
             
          padding-top: 10px;
          display: flex;
          justify-content: space-around;
          align-items: center;
          .item {
             
            font-size: 16px;
            padding: 10px;
            flex: 1;
            text-align: center;
            color: #686868;
            .icon-suo {
             
              font-size: 14px;
              margin-left: 5px;
            }
          }
          .active {
             
            border-bottom: 2px solid #ffdf03;
            color: #ffffff;
            font-weight: 600;
          }
        }
        .tab-wrap {
             
          background-color: #101821;
          padding-top: 5px;
          .tab-con {
             
            display: flex;
            flex-direction: row;
            justify-content: space-between;
            flex-wrap: wrap;
            .tab-img {
             
              width: 33%;
              &:nth-child(3n) {
             
                border-right: 0;
              }
            }
            .tab-con1 {
             
              color: #a1a1a1;
              width: 100%;
              height: 200px;
              display: flex;
              flex-direction: column;
              justify-content: center;
              align-items: center;
              padding: 0 40px;
              h3 {
             
                color: #f3f3f3;
                font-size: 18px;
                font-weight: normal;
              }
            }
            .tab-con2 {
             
              color: #a1a1a1;
              width: 100%;
              height: 200px;
              display: flex;
              flex-direction: column;
              justify-content: center;
              align-items: center;
              padding: 0 40px;
              h3 {
             
                color: #f3f3f3;
                font-size: 18px;
                font-weight: normal;
              }
              p {
             
                margin-top: 0;
                font-size: 15px;
                text-align: center;
                line-height: 1.3;
              }
            }
            .tab-con3 {
             
              font-size: 14px;
              color: #a1a1a1;
              width: 100%;
              height: 50px;
              text-align: center;
              line-height: 50px;
            }
          }
        }
      }
    }
    style>
    
  2. 使用 mock 数据创建用户信息

    public/static 新建 userInfo.json 文件

    如果不知道为什么要在 public 下面创建该文件,请移步至:10. 本地mock数据实现数据请求 查看。

    {
           
      "userInfo": {
           
    	"name": "前端小新",
    	"dyh": 98579335,
    	"desc": "没有好看的皮囊,但有有趣的灵魂,技术没有止境",
    	"age": 23,
    	"region": "中国",
    	"tag": [],
    	"like": 2,
    	"follow": 543,
    	"fans": 1087,
    	"vlist": {
           
    		"works": [],
    		"private": [],
    		"likes": []
    		}
    	}
    }
    
  3. Vuex 的 modules 进行模块化数据处理

    在前两个章节中我们引入了 Vuex 进行状态管理,在这里,我们继续使用它来对用户的信息进行管理。

    我们使用 axios 请求本地 json 数据来模拟后端的数据请求,实现用户个人信息的获取。

    1. src/store/modules 下,新建 user.js
    import {
            GET } from '@/request/http';
    
    const user = {
           
        namespaced: true,
        state: {
           
            userInfo: {
           },
        },
        mutations: {
           
            assignUseInfo(state, res) {
           
                state.userInfo = res.userInfo;
            },
        },
        actions: {
           
            GetUserInfo({
             state, commit }, params) {
           
                GET('/static/userInfo.json')
                .then(res => {
           
                    console.log(res.data);
                    commit('assignUseInfo', res.data); // 将请求到的数据赋值给vuex中的state
                });
            },
        },
    };
    
    export default user;
    
    1. 模块创建完成之后不要忘记将该模块导出,在 src/store/index.js 中导入才可以使用
    import Vue from 'vue';
    import Vuex from 'vuex';
    import sign from './modules/sign';
    import user from './modules/user';
    
    Vue.use(Vuex);
    
    export default new Vuex.Store({
           
      ...
      modules: {
           
        user,
        sign,
      },
    });
    
    
    1. Vuex 处理完之后就可以在页面中执行该请求方法了
    <script>
    import {
            mapActions } from 'vuex';
    
    export default {
           
      data() {
           
        return {
           
          ...
        };
      },
      created() {
           
        this.getUserInfo();
      },
      methods: {
           
        ...mapActions('user', ['GetUserInfo']),
        // 获取用户数据
        getUserInfo() {
           
          this.GetUserInfo();
        },
      },
    };
    </script>
    
    1. 完整实现
    <template>
      <div class="mine">
        
        <div class="mine-top" :style="bgPic">
          
          <div class="menu-box">
            <span class="iconfont icon-caidan">span>
          div>
        div>
        
        <div class="mine-wrap">
          <div class="mine-content">
            
            <div class="info">
              <img
                :src="userInfo.avathor"
                style="
                  height: 100px;
                  width: 100px;
                  border-radius: 50%;
                  margin-right: 20px;
                "
              />
              <div class="info-right">
                <button class="btn">编辑资料button>
                <button class="btn">
                  <span class="iconfont icon-jiahao1">span> 朋友
                button>
              div>
            div>
            
            <div class="desc">
              <h2>{
          { userInfo.name }}h2>
              <p class="dyh">
                抖音号:{
          { userInfo.dyh }}
                <span class="iconfont icon-erweima">span>
              p>
              <p class="jj">{
          { userInfo.desc }}p>
            div>
            
            <div class="user-tag">
              <span>
                <span class="iconfont icon-touxiang">span>
                {
          { userInfo.age }}岁
              span>
              <span>{
          { userInfo.region }}span>
              <span><span class="iconfont icon-jiahao1">span>添加学校等标签span>
            div>
            <div class="user-tag2">
              <span
                ><a>{
          { userInfo.like }}a
                >获赞span
              >
              <span
                ><a>{
          { userInfo.follow }}a
                >关注span
              >
              <span
                ><a>{
          { userInfo.fans }}a
                >粉丝span
              >
            div>
          div>
        div>
        
        <div class="mine-tab">
          <div class="tab-navbar">
            <div
              class="item"
              @click="changeTab(0)"
              :class="indexTab === 0 ? 'active' : ''"
            >
              作品
            div>
            <div
              class="item"
              @click="changeTab(1)"
              :class="indexTab === 1 ? 'active' : ''"
            >
              私密<span class="iconfont icon-suo">span>
            div>
            <div
              class="item"
              @click="changeTab(2)"
              :class="indexTab === 2 ? 'active' : ''"
            >
              喜欢<span class="iconfont icon-suo">span>
            div>
          div>
          <div class="tab-wrap">
            
            <div class="tab-con" v-show="indexTab === 0">
              <div class="tab-con1" :v-if="userInfo.vlist.works.length === 0">
                <h3>暂无作品h3>
              div>
              <div
                class="tab-img"
                :v-if="userInfo.vlist.works.length !== 0"
                v-for="i in userInfo.vlist.works"
                :key="i"
              >
                <img
                  src="@/assets/images/mine/bj.png"
                  style="width: 100%; height: auto"
                />
              div>
            div>
            
            <div class="tab-con" v-show="indexTab === 1">
              <div class="tab-con2" :v-if="userInfo.vlist.private.length === 0">
                <h3>没有私密作品h3>
                <p>设为私密的作品和过期的日记会出现在这里,并且只有你能看到p>
              div>
              <div class="tab-img" v-for="i in userInfo.vlist.private" :key="i">
                <img
                  src="@/assets/images/mine/bj2.png"
                  style="width: 100%; height: auto"
                />
              div>
            div>
            
            <div class="tab-con" v-show="indexTab === 2">
              <div class="tab-con1" :v-if="userInfo.vlist.likes.length === 0">
                <h3>空空如也h3>
              div>
              <div>
                <div class="tab-con3">只有你能看到自己的喜欢列表div>
                <div class="tab-img" :v-if="userInfo.vlist.likes.length !== 0" v-for="i in userInfo.vlist.likes" :key="i">
                  <img src="@/assets/images/mine/bj3.png" style="width: 100%; height: auto" />
                div>
              div>
            div>
          div>
        div>
      div>
    template>
    
    <script>
    import {
              mapActions, mapState } from 'vuex';
    
    export default {
             
      data() {
             
        return {
             
          bgPic: {
             
            backgroundImage: `url(${ require('@/assets/images/mine/bj.png')})`,
            backgroundRepeat: 'no-repeat',
            backgroundSize: '100% 100%',
          },
          indexTab: 0,
        };
      },
      created() {
             
        // 获取用户数据
        this.GetUserInfo();
      },
      // 计算属性,监控userInfo
      computed: {
             
        ...mapState({
             
          userInfo: (state) => state.user.userInfo,
        }),
      },
      methods: {
             
        ...mapActions('user', ['GetUserInfo']),
        changeTab(index) {
             
          this.indexTab = index;
        },
      },
    };
    script>
    

总结

本章节需要注意的几个点:

  • Vuex 状态管理
  • computed 计算属性

上一章节: 13. 自定义全局弹出框组件的实现

下一章节: 15. 用户信息编辑页面的实现

项目整体介绍:Vue.js 项目实战之实现视频播放类WebApp的项目开发(仿抖音app)


项目仓库地址,欢迎 Star。

有任何问题欢迎评论区留言讨论。

你可能感兴趣的:(新星计划,Vue,实战系列,新星计划,vue,html,web,项目管理)