【项目实战】基于Vue3+Vant3造一个网页版的类掘金app项目 - 活动

571 阅读3分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第22天,点击查看活动详情

前言

大家好,在上一篇文章话题广场分享中,我们对整个页面布局进行了分析,并通过请求取官方api接口拿到了正式的话题数据实现了页面的动态渲染。而我们今天要分享的“活动”页面整体布局跟话题广场非常类似,也是主要由标题和内容两大块组成。下面我们来具体分析实现一下。

布局分析

image.png 如图所示,活动页整体布局比较简单,主要由顶部标题栏和主页面中的活动列表两部分组成,其中活动列表是一组tab标签页,点击不同的tab页可以查看不同城市的活动。而具体的活动内容布局也很简单,主要包括活动的封面图片,活动标题,活动时间,活动所在城市以及一个报名参加的按钮。

布局实现

根据上面对整体布局的分析我们来把页面的布局实现一下:

  • 标题栏 标题栏我们还是用vant库中的van-nav-bar来实现,对应的设置title属性为“活动”,left-arrow属性为true(显示返回按钮),绑定click-left事件用于点击返回按钮时返回上一页
<van-nav-bar title="活动" left-arrow @click-left="toBack" />
 const router  = useRouter()
 const toBack = () => {
     router.back();
 } 

  • tab标签 tab标签页的标题是几个热门的城市,具体的城市列表可以通过后台api接口“cities”获取得到。因此我们这里需要添加一个van-tabs父组件,然后再给van-tabs添加3个子项van-tab,为什么是3个呢,因为有两个tab(全部和其他)需要做成固定的,而具体的城市则需通过v-for指令循环渲染
 <van-tabs @click-tab="switchCity">
    <van-tab
      :title="c.city_name"
      :name="c.city_alias"
      v-for="c in cityList"
      :key="c.city_alias"
    >
      <event-list :city="c.city_alias" />
    </van-tab>
  </van-tabs>
const store = useStore();
api.citys().then((res) => {
  state.cityList = res.data.banner_citys;
  state.cityList.unshift({ city_alias: "", city_name: "全部" });
  state.cityList.push({ city_alias: "buxian", city_name: "其他" });
});
const switchCity = (tab) => {
  store.state.refreshList();
};

活动列表封装

上面布局分析的时候我们提到,活动列表内容是放在tab标签中的,也就是说通过切换不同的标签(城市)可以展示不同的活动列表,那么我们来看每个标签页中的活动列表数据格式都是一样,唯一不同的就是根据切换不同的城市显示对应城市的活动数据,那也就是说每个城市的列表除了数据源不同外其它都是一样的,那么为了避免每个标签页中的代码冗余,我们就将列表进行封装,然后通过传递不同的数据源或不同的参数以实现代码的复用。 image.png 如上图所示,通过分析官方event_list接口我们发现,每个tab页调用的都是该接口,然后通过传递一个city参数来获取不同城市的活动信息。因为我们封装的这个组件是专门的活动列表组件,只是在活动页面中的tab页面中使用,因此我们可以直接把请求接口的部分也一并封装在组件内,然后通过定义一个city prop来获取不同的城市参数进而获取不同城市的活动数据。

 <van-pull-refresh v-model="refreshing" @refresh="onRefresh">
    <van-list
      v-model:loading="loading"
      :finished="finished"
      finished-text="没有更多了"
      @load="onLoad"
    >
      <van-cell v-for="item in eventList" :key="item.event_id">
        <div class="event-item">
          <van-image class="image" :src="item.screenshot" />
          <div class="event-content">
            <div class="event-title">{{ item.title }}</div>
            <div class="event-time">{{ item.event_start_time }}</div>
            <div class="event-city">{{ item.city }}</div>
            <div class="event-join">报名参加</div>
          </div>
        </div>
      </van-cell>
    </van-list>
  </van-pull-refresh>
props: {
    city: {
      type: String,
      required: true,
    },
  },
  setup(props, ctx) {
    const state = reactive({
      refreshing: false,
      loading: false,
      finished: false,
      eventList: [],
      cursor: 0,
    });

    const store = useStore();
    const { city } = props;
    const onRefresh = () => {
      state.finished = false; //清空列表数据,重新加载
      state.loading = true;
      state.refreshing = true;
      onLoad();
    };

    store.commit("setRefresh", onRefresh);

    const onLoad = () => {
      api.eventList(state.cursor, city).then((res) => {
        if (state.refreshing) {
          state.eventList = [];
          state.refreshing = false;
        }
        console.log(res);
        state.cursor = res.cursor;
        state.eventList.push(...res.data);
        state.loading = false;
      });
    };
    return {
      ...toRefs(state),
      onRefresh,
      onLoad,
    };
  },

test.gif

总结

本次分享我们对活动页面进行了布局分析及搭建,还将活动列表进行了封装以实现代码的复用,最终实现了活动页没的整体功能。本次涉及到的知识点有:组件的封装,父子组件信息传递,列表的下拉刷新和上拉加载。今天的分享就到这里了。小伙伴们给个赞哦,感谢!