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

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

项目仓库地址,欢迎 Star


实现效果

在这里插入图片描述


功能实现

  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>2</a>获赞</span>
              <span><a>543</a>关注</span>
              <span><a>1087</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-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 class="tab-img" v-for="i in 10" :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-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。

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

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 14
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 14
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值