项目分析三

今天主要写的是关于首页——文章列表的渲染

一、文章列表——首页

  • 导航栏:使用组件搭建框,然后再渲染样式
// 在src/wiews/home/index.vue
<template>
  <div class="home-container">
    <!-- 导航栏 -->
    // 1. 使用组件搭建
    <van-nav-bar class="page-nav-bar" fixed>
      <van-button
        class="search-btn"
        slot="title"
        type="info"
        size="small"
        round
        icon="search"
      >
        搜索
      </van-button>
    </van-nav-bar>
    <!-- /导航栏 -->
  </div>
</template>

<script>
....
</script>

<style scoped lang="less">
// 2.调整样式
.home-container {
  /deep/ .van-nav-bar__title {    // 使用了外届的组件或者自己开发一个组件,修改一处就可能会影响到用这个组件的所有样式,所以就需要有一个方法或者方式,既不影响到别的地方,又能修改子组件在当前的样式。/deep/就能实现
    max-width: unset;
  }
  .search-btn {
    width: 555px;
    height: 64px;
    background-color: #5babfb;
    border: none;
    font-size: 28px;
    .van-icon {
      font-size: 32px;
    }
  }
}
</style>

二、文章列表——频道列表

// 在src/wiews/home/index.vue
<template>
  <div class="home-container">
    ......
    <!-- /导航栏 -->
// 1.先在vant组件库找到对应的组件---> Tab标签页
// 下面大括号包起来部分是没有渲染之前,使用Tab标签页组件定义的样式
// {<van-tabs  class="channel-tabs" v-model="active" swipeable animated border>
    <van-tab title="标签 1">内容 1内容 1内容 1内容 1内容 1内容 1内容 1内容 1内容 1</van-tab>
    <van-tab title="标签 2">内容 2内容 1内容 1内容 1内容 1内容 1内容 1</van-tab>
    <van-tab title="标签 3">内容 3内容 1内容 1内容 1内容 1内容 1</van-tab>
    <van-tab title="标签 4">内容 4</van-tab>
    <van-tab title="标签 6">内容 6</van-tab>
    <van-tab title="标签 7">内容 7</van-tab>
    <van-tab title="标签 8">内容 8</van-tab>
    <!-- 右侧自定义内容 -->
    <!-- 占位元素 -->
    <div class="placeholder"></div>
    <!-- 右侧按钮 -->
    <template #nav-right>
      <div class="hamburger-btn">
        <i class="toutiao toutiao-gengduo"></i>
      </div>
    </template>
</van-tabs>}
// 10.获取数据后,渲染到页面中
    <van-tabs class="channel-tabs" v-model="active" swipeable animated border>
      <van-tab v-for="channels in UserChannels" :key="channels.id">
        <div slot="title">{{channels.name}}</div>
        {{channels.name}}的内容
      </van-tab>

      <!-- 右侧自定义内容 -->
      <!-- 占位元素 -->
      <div class="placeholder" slot="nav-right"></div>
      <!-- 右侧按钮 -->
      <div class="hamburger-btn" slot="nav-right">
        <i class="toutiao toutiao-gengduo"></i>
      </div>
    </van-tabs>
  </div>
</template>

<script>
import { getUserChannels } from "@/api/user"; // 6.导入
export default {
  name: "HomeIndex",
  components: {},
  props: {},
  data() {
    return {
      ...
      UserChannels: [], // 5.定义频道列表变量名来接收
    };
  },
  computed: {},
  watch: {},
  created() {
    this.loadUserChannels(); // 9.调用
  },
  mounted() {},
  methods: {
  // 8.获取频道列表数据并展示
    async loadUserChannels() {
      try {
        const { data } = await getUserChannels();
        console.log(data);
        this.UserChannels = data.data.channels;
      } catch (err) {
        this.$toast("获取频道列表失败");
      }
    },
  },
};
</script>

<style scoped lang="less">
.....
// 2.定义样式
/deep/ .channel-tabs {
// 3.设置一个距离上面tabar栏的距离,不要设置全局的,就在这里就可以了
  margin-top: 90px; 
  .van-tab {
    border-right: 1px solid #edeff3;
    min-width: 200px;
    font-size: 30px;
    color: #777777;
  }

  .van-tab--active {
    color: #333333;
  }

  .van-tabs__nav {
    padding-bottom: 0;
  }

  .van-tabs__line {
    bottom: 8px;
    width: 31px !important;
    height: 6px;
    background-color: #3296fa;
  }

  .placeholder {
    flex-shrink: 0;
    width: 66px;
    height: 82px;
  }

  .hamburger-btn {
    position: fixed;
    right: -1px;
    display: flex;
    justify-content: center;
    align-items: center;
    width: 67px;
    height: 82px;
    background-color: #fff;
    background-color: rgba(255, 255, 255, 0.902);
    i.toutiao {
      font-size: 33px;
    }
    &:before {
      content: "";
      position: absolute;
      left: 0;
      width: 1px;
      height: 58px;
      background-image: url(~@/assets/gradient-gray-line.png); 
      background-size: contain;
    }
  }
}
</style>


// src/api/user.js
......
// 4.基本样式定义后,首先先获取频道列表 
export const getUserChannels = () => {
  return request({
    method: 'GET',
    url: '/v1_0/user/channels',
    
  })
};

三、文章列表—加载文章列表的具体数据

// 1.创建文章的列表组件:src/views/home/components/article-list.vue
<template>
  <div class="article-list">
  // 1.1.使用组件搭建框架
    <van-list
      v-model="loading"
      :finished="finished"
      finished-text="没有更多了"
      @load="onLoad"  
    >
      <van-cell v-for="item in list" :key="item" :title="item" />
    </van-list>
  </div>
</template>

<script>
import {getArticle} from '@/api/article'// 10.导入
export default {
  name: "ArticleList",
  components: {},
  props: {
    // 设置一个props用来接收
    属于自己的频道信息数据
    channels: { // 6.传入数据
      type: Object,
      required: true,
    },
  },
  data() {
    return {
      list: [], //存储数据
      loading: false, // 控制加载的状态
      finished: false,// 控制加载完成的状态
    };
  },
  computed: {},
  watch: {},
  created() {},
  mounted() {},
  methods: {
    onLoad() {
      // 异步更新数据
      // setTimeout 仅做示例,真实场景中一般为 ajax 请求 可以修改里面的时间,来延长加载的时间,
      // { setTimeout(() => {
        // for (let i = 0; i < 10; i++) {
         // this.list.push(this.list.length + 1);
        }
        // 加载状态结束
        // this.loading = false;
        // 数据全部加载完成
       //  if (this.list.length >= 40) {
          this.finished = true;
        }
      }, 2000); }
       try {
        //   11.1.发送请求获取的数据
        const { data } = await getArticles({
          channel_id: this.channels.id, // 频道id
          timestamp: this.pre_timestamp || Date.now(), // 时间戳,请求新的推荐数据传当前的时间戳,请求历史推荐传指定的时间戳
          with_top: 1
        })
        // if(Math.random() > 0.5){
        //   throw new Error('错误')
        // }  // 测试代码,测试完删除掉,为了出现错误情况
        // console.log(data);
       // 11.2.将数据追加到data,数据中保存
        const { results } = data.data
        // console.log(results,'-----------');
        this.list = [...this.list, ...results]
        // this.list =data.data.results 这种写法也可以
        // console.log(this.list); 打印结果 
        // 11.3.将loading 设置为false
        this.loading = false
        this.pre_timestamp = data.data.pre_timestamp
        // 11.4.判断数据是否加载完成
        if (data.data.pre_timestamp === null) {
          this.finished = true
        }
      } catch (err) {
        this.loading = false
        this.error = true
        this.$toast('获取当前频道的文章列表失败')
      }
    },
  },
};
</script>

<style scoped lang="less">
</style>



// 2.在src/views/home/index.vue中导入
<van-tabs class="channel-tabs" v-model="active" swipeable animated border>
      <van-tab v-for="channels in UserChannels" :key="channels.id">
        <div slot="title">{{channels.name}}</div>
        <div slot="default">
          <ArtilceList :channels="channels" // 7.接收></ArtilceList> // 5.使用
        </div>
      </van-tab>
      
 <script>     
import ArtilceList from '@/views/home/components/article-list' // 3.导入组件

components: {
    ArtilceList, // 4.定义
  },
</script>

// 8.创建:在src/api/article.js
// 9.封装:
import request from '@/utils/request'

export const getArticles = (params) => {
  return request({
    method: 'GET',
    url: '/v1_0/articles',
    params
  })
};

四、文章列表—下拉刷新 

  • 这里会使用到 vant 组件库中的 PullRefresh 下拉刷新 组件
 <van-pull-refresh 
    v-model="isLoading" 
    @refresh="onRefresh" 
    :success-text="successText">
     <!-- <p>刷新次数: {{ count }}</p> -->

      <van-list
        v-model="loading"
        :error.sync="error"
        error-text="请求失败,点击重新加载"
        :finished="finished"
        finished-text="没有更多了"
        @load="onLoad"
      >
        <van-cell v-for="(article,index) in list" :key="index">
          <div slot="title">
            <ArticleItem :article="article"></ArticleItem>
          </div>
        </van-cell>
      </van-list>

    </van-pull-refresh>
  data () {
    return {
      list: [], // 存储数据
      loading: false, // 控制加载的状态
      finished: false, // 控制加载完成的状态
      pre_timestamp: null, // 保存历史的时间戳
      error: false,
      count: 0,
      isLoading: false, // 控制下拉刷新加载中的状态
      successText: '刷新成功'
    }
  },
methods:{
async onRefresh () {
    //   setTimeout(() => {
    //     this.$toast('刷新成功');
    //     this.isLoading = false;
    //     this.count++;
    //   }, 1000);
      try {
        const { data } = await getArticles({
          channel_id: this.channels.id, // 频道id
          timestamp: Date.now(), // 时间戳,请求新的推荐数据传当前的时间戳,请求历史推荐传指定的时间戳
          with_top: 1
        })
        // console.log(data);
        const { results } = data.data
        // console.log(results,'-----------');
        this.list = [...results, ...this.list]
        this.successText = `刷新了${results.length}条数据`
        // this.$toast('刷新成功')
        this.isLoading = false
      } catch (err) {
        this.isLoading = false
        this.$toast('刷新失败')
      }
    }
}

五、文章列表—记住滚动条的位置

<style scoped lang="less">
.article-list {
  // 设置记住滚动条的位置,这样体验效果比较好,当滚动到某个位置后,我左或右滑动,再返回此页面时,位置没有变化 
  // 百分比单位是相对于父元素的
  // height: 100%;

  // 视口(在移动端是布局视口)单位:vw 和 vh,不受父元素影响
  // 1vw = 视口宽度的百分之一
  // 1vh = 视口高度的百分之一
  height: 79vh;
  overflow-y: auto;
}
</style>

六、文章列表—列表中的内容

// 1.创建src/components/article-item/index.vue
<template>
// 2.使用Cell 单元格 组件搭建框架
  <van-cell class="article-item">
    <!-- 标题插槽 -->
    <template #title>
      <div class="title van-multi-ellipsis--l2">{{ article.title }}</div>
    </template>

    <!-- 标题下方的内容 -->
    <template #label>
      <!-- 文章封面信息 -->
      <div v-if="article.cover.type === 3" class="cover-wrap">
        <div
          class="cover-item"
          v-for="(img, index) in article.cover.images"
          :key="index"
        >
          <van-image class="cover-item-img" fit="cover" :src="img" />
        </div>
      </div>

      <!-- 文章标注信息 -->
      <div class="label-info-wrap">
        <span>{{ article.aut_name }}</span>
        <span>{{ article.comm_count }}评论</span>
        <span>{{ article.pubdate }}</span>
      </div>
    </template>

    <!-- 默认的插槽: 右边的内容 -->
    <van-image
      v-if="article.cover.type === 1"
      class="right-cover"
      fit="cover"
      :src="article.cover.images[0]"
    />
  </van-cell>
</template>

<script>
export default {
  name: 'ArticleItem',
  components: {},
  props: {
    article: {
      type: Object,
      required: true
    }
  },
  data () {
    return {}
  },
  computed: {},
  watch: {},
  created () {},
  mounted () {},
  methods: {}
}
</script>

<style scoped lang="less">
// 3.添加样式
.article-item {
  .title {
    font-size: 32px;
    color: #3a3a3a;
  }

  .van-cell__title {
    display: flex;
    flex-direction: column;
    justify-content: space-between;
  }

  .van-cell__value {
    flex: unset;
    width: 232px;
    height: 146px;
    padding-left: 25px;
  }

  .right-cover {
    width: 100%;
    height: 146px;
  }

  .label-info-wrap span {
    font-size: 22px;
    color: #b4b4b4;
    margin-right: 25px;
  }

  .cover-wrap {
    display: flex;
    padding: 30px 0;
    .cover-item {
      flex: 1;
      height: 146px;
      &:not(:last-child) {
        padding-right: 4px;
      }
      .cover-item-img {
        width: 100%;
        height: 146px;
      }
    }
  }
}
</style>

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值