两周撸一个微信小程序(代码片段)

homehtml homehtml     2022-12-25     395

关键词:

利益相关

说明

该小程序代码已开源,点击可查看源码,可随意 star。也可以先扫描下方的小程序码直接体验。

技术图片

写在前面

前段时间写了一个简单的小程序 QuietWeather源码在这里,具体实现相关可查看这篇文章:两天撸一个天气应用微信小程序。但是这个 小程序QuietWeather 完全不是一个数量级的。so,该文章梳理内容会有那么一点儿多,想跳过的可以直接拉到最下面。。。

这里先上效果图,感兴趣的也可以 查看源码 。实际体验可扫描??上面的小程序码。

效果图

PC 开发者工具录制,会有些卡顿

技术图片技术图片技术图片技术图片技术图片技术图片技术图片技术图片

文章数据入口调整了,也保留了动画,请酌情忽略 gif 卡顿

技术图片技术图片技术图片技术图片技术图片技术图片技术图片技术图片

实现

过滤器

filter 目录下创建了一个 wxs,里面是用到的过滤器,需要注意的是,wxs 的语法只能是 es5,而且部分 js 语法是不支持的,具体支持的语法可查看微信小程序开发文档。

组件

细分的话,细分的话目前总共写了 8 个组件:

  • 页面为空组件 empty
  • 沸点 item 组件 feidianItem:分为上半部分 feidianItemTop 和下半部分组件 feidianItemBottom,会在沸点、赞过的沸点等相关页面复用
  • 文章 item:有两种展现样式,这里写了独立的两个组件:postItemOnepostItemTwo
  • 标签展现 item tagItem:会在标签管理等相关页面复用
  • 小册 item xiaoceItem:会在小册 tab、购买的小册等相关页面复用

组件相对比较简单,基本上是以展现为主,并没有太多交互,可下载源码 在微信开发者工具调试查看相关效果。

页面

HOME 页

HOME 页主要展现两部分:顶部热门文章推荐和下面的推荐文章列表,使用了两个上面提到的组件: postItemOnepostItemTwo。且未登录时,会有一个登陆提示。实现如下:

<wxs module=‘filters‘ src=‘../../filter/filter.wxs‘></wxs>
<view class=‘container‘>
  <navigator url=‘/pages/login/login‘ wx:if=‘!logined‘>
    <view class=‘card guide‘>
      <view class=‘l‘>
        <view class=‘t‘>登录账号</view>
        <view class=‘c‘>收藏文章,同步阅读记录,数据永不丢失</view>
      </view>
      <view class=‘r‘>登录</view>
    </view>
  </navigator>
  <view class=‘hot card‘ wx:if=‘hotRecomment.length && hotRrecommendShow‘>
    <view class=‘btitle‘>
      <view class=‘l‘>
        <image class=‘icon‘ src=‘/img/ic_hot_home.png‘></image>
        <view>热门推荐</view>
      </view>
      <view class=‘r‘>
        <image catchtap=‘refreshHot‘ class=‘refresh rotate‘ src=‘/img/refresh_icon.png‘></image>
        <image catchtap=‘closeHot‘ class=‘close‘ src=‘/img/chart_close.png‘></image>
      </view>
    </view>
    <postItemOne list=‘hotRecomment‘ graphics=‘true‘></postItemOne>
  </view>
  <view class=‘timeline‘>
    <postItemTwo list=‘timeline‘></postItemTwo>
  </view>
</view>

其中,顶部热门推荐的刷新会有以下的实现效果,这里需要稍微注意下:

热门推荐点击刷新,将当前的 3 条文章 objectId 以 id|id|id 的格式发送请求,然后重新拉取热门推荐列表

看抓包,热门推荐只返回 20 条,刷新一次移除三条,所以简单处理的话,user_filter_entry 之后直接将热门推荐数组的前三条移除即可;上面方式更精确,以防服务端之后又有什么返回呢

同时,搜索 tab 的顶部 banner 列表也是在这个页面预先请求,然后保存到本地。实现如下:

getBannerImgList() 
    const auth = this.data.auth
    wx.request(
      url: `$config.bannerRequestUrl/get_banner`,
      data: 
        position: ‘explore‘,
        page: 0,
        pageSize: 20,
        platform: ‘android‘,
        device_id: auth.clientId,
        client_id: auth.clientId,
        token: auth.token,
        src: ‘android‘,
      ,
      success: (res) => 
        let data = res.data
        if (data.s === 1) 
          let bannerImgList = (data.d && data.d.banner) || []
          wx.setStorage(
            key: ‘bannerImgList‘,
            data: bannerImgList,
          )
         else 
          wx.showToast(
            title: data.m.toString(),
            icon: ‘none‘,
          )
        
      ,
      fail: () => 
        wx.showToast(
          title: ‘网路开小差,请稍后再试‘,
          icon: ‘none‘,
        )
      ,
    )
  ,

然后配合下拉刷新 onPullDownRefresh 重新获取数据。

其他具体细节这里不再赘述可查看源码

搜索 TAB

搜索 tab 页两部分组成:顶部的 swiper 和下面的热门文章列表,这里复用 postItemOne 组件即可。顶部的 swiper 在首页已经预先获取过了(见上面),这里直接读取即可。实现如下:

<wxs module=‘filters‘ src=‘../../filter/filter.wxs‘></wxs>
<view class=‘container‘>
  <swiper autoplay circular interval="3500" duration="500" wx:if=‘bannerImgList.length‘ style=‘height:swiperHeight‘>
    <block wx:for="bannerImgList" wx:key="index">
      <swiper-item>
        <image src=‘item.screenshot‘ class="banner" mode=‘widthFix‘></image>
      </swiper-item>
    </block>
  </swiper>
  <view class=‘hot card‘>
    <view class=‘btitle‘>
      <view class=‘l‘>
        <image class=‘icon‘ src=‘/img/pin_hot.png‘></image>
        <view>热门文章</view>
      </view>
    </view>
    <recommendItem list=‘rankList‘ graphics=‘false‘></recommendItem>
  </view>
</view>

沸点 TAB

该页面复用的组件是 feidianItem,顶部的热门沸点是使用的 swiper 实现的。

该页面有一个小细节需要稍微注意下:如果页面保持在顶部,那么切换 tab 后需要重新刷新获取新数据,如果页面已经往下滑动了,那么切换 tab 后就不需要刷新获取新数据。实现如下:

wxml:

<view class=‘container‘>
  <view class=‘recommendList‘>
    <swiper autoplay=‘false‘ circular=‘false‘ duration="500" wx:if=‘recommendList.length‘ next-margin=‘100rpx‘ style=‘height:swiperHeight‘>
      <block wx:for="recommendList" wx:key="index">
        <swiper-item>
          <view class=‘item‘>
            <view class=‘title‘ wx:if=‘item.isRecommend‘>
              <image class=‘icon‘ src=‘/img/ic_topic_star.png‘></image>
              <text>编辑推荐</text>
            </view>
            <view class=‘title‘ wx:else>
              <image class=‘icon‘ src=‘/img/pin_hot.png‘></image>
              <text>热门沸点</text>
            </view>
            <view class=‘content‘>
              <view class=‘text‘>item.content</view>
              <view class=‘img‘ wx:if=‘item.pictures && item.pictures.length‘>
                <image mode=‘aspectFill‘ src=‘item.pictures[0]‘></image>
              </view>
            </view>
          </view>
        </swiper-item>
      </block>
    </swiper>
  </view>
  <view class=‘pinList‘>
    <feidianItem item=‘item‘ wx:for=‘list‘ wx:key=‘index‘></feidianItem>
  </view>
</view>

js:

const config = getApp().globalData.config
const utils = require(‘../../utils/utils.js‘)
Page(
  data: 
    COUNT: 30,
    swiperHeight: ‘auto‘,
    recommendList: [],
    list: [],
    auth: ,
    scrollTop: 0,
  ,
  onShow () 
    // 如果 scrollTop 为 0,也 reload
    if (utils.pageReload(this.data.auth, [this.data.list]) || !this.data.scrollTop) 
      this.init()
    
  ,
  onPullDownRefresh() 
    this.init()
  ,
  init() 
    wx.showLoading(
      title: ‘数据加载中‘,
    )
    this.setData(
      auth: ,
    )
    let auth = utils.ifLogined()
    this.setData(
      auth,
    )
    this.initSwiper()
    this.getHotRecommendList()
    this.pinListRecommend(true)
  ,
  initSwiper() 
    wx.getSystemInfo(
      success: (res) => 
        this.setData(
          swiperHeight: `$(res.windowWidth || res.screenWidth) / 375 * 135px`
        )
      ,
    )
  ,
  // 热门推荐列表
  getHotRecommendList() 
    const auth = this.data.auth
    wx.request(
      url: `$config.shortMsgMsRequestUrl/getHotRecommendList`,
      data: 
        uid: auth.uid,
        device_id: auth.clientId,
        client_id: auth.client_id,
        token: auth.token,
        src: ‘web‘,
      ,
      success: (res) => 
        let data = res.data
        if (data.s === 1) 
          this.setData(
            recommendList: (data.d && data.d.list) || [],
          )
         else 
          wx.showToast(
            title: data.m.toString(),
            icon: ‘none‘,
          )
        
      ,
      fail: () => 
        wx.showToast(
          title: ‘网路开小差,请稍后再试‘,
          icon: ‘none‘,
        )
      ,
    )
  ,
  // 沸点列表
  pinListRecommend(reload) 
    const auth = this.data.auth
    let list = this.data.list
    if (utils.isEmptyObject(list) || reload) 
      list = [ createdAt: ‘‘ ]
    
    let createdAt = (list.slice(-1)[0].createdAt) || ‘‘
    wx.request(
      url: `$config.shortMsgMsRequestUrl/pinList/recommend`,
      data: 
        uid: auth.uid,
        device_id: auth.clientId,
        token: auth.token,
        src: ‘web‘,
        limit: this.data.COUNT,
        before: createdAt,
      ,
      success: (res) => 
        let data = res.data
        if (data.s === 1) 
          wx.hideLoading()
          let list = (data.d && data.d.list) || []
          this.setData(
            list: reload ? list : this.data.list.concat(list),
          )
         else 
          wx.showToast(
            title: data.m.toString(),
            icon: ‘none‘,
          )
        
      ,
      fail: () => 
        wx.showToast(
          title: ‘网路开小差,请稍后再试‘,
          icon: ‘none‘,
        )
      ,
    )
  ,
  onReachBottom() 
    this.pinListRecommend()
  ,
  onPageScroll (e) 
    this.setData(
      scrollTop: e.scrollTop,
    )
  ,
  onShareAppMessage(res) 
    return 
  ,
)

小册 TAB

小册页面基本上没有什么可说的,只有小册展现。。。组件复用 xiaoceItem,实现如下:

<view class=‘lists‘>
  <xiaoceItem list=‘xiaoceList‘></xiaoceItem>
</view>

我的 TAB

该页面以简单的数目展现(未读消息条数、收藏集数目、阅读过的文章数)和跳转为主,调用相关的 API 即可,没有什么难度。实现如下:

wxml:

<view class=‘wrapper‘>
  <view class=‘card profile‘  catchtap=‘navigatItem‘ data-url=‘/pages/personal/personal‘>
    <view class=‘info‘>
      <image class=‘avatar‘ src=‘userInfo.avatarLarge‘ wx:if=‘userInfo.avatarLarge‘></image>
      <image class=‘avatar‘ src=‘/img/empty_avatar_user.png‘ wx:else></image>
      <view class=‘text‘>
        <view class=‘name‘>userInfo.username || ‘登录/注册‘</view>
        <view>userInfo.jobTitle || ‘添加职位‘ @ userInfo.company || ‘添加公司‘</view>
      </view>
    </view>
    <view class=‘more‘>
      <view class=‘reddot‘ wx:if=‘auth && !userInfo.company‘></view>
      <image src=‘/img/profile_arrow.png‘></image>
    </view>
  </view>

  <view class=‘card items‘>
    <view class=‘item‘ hover-class=‘hover-class‘ catchtap=‘navigatItem‘ data-url=‘/pages/infoCenter/infoCenter‘>
      <view class=‘title‘>
        <image src=‘/img/ic_notification.png‘></image>
        <view>消息中心</view>
      </view>
      <view class=‘count reddot‘ wx:if=‘userNotificationNum‘>userNotificationNum</view>
    </view>
    <view class=‘item‘ hover-class=‘hover-class‘ catchtap=‘navigatItem‘ data-url=‘/pages/favorate/favorate‘>
      <view class=‘title‘>
        <image src=‘/img/ic_heart_entry_bottom_full.png‘></image>
        <view>我喜欢的</view>
      </view>
      <view class=‘count‘>userInfo.collectedEntriesCount || 0篇</view>
    </view>
    <view class=‘item‘ hover-class=‘hover-class‘ catchtap=‘navigatItem‘ data-url=‘/pages/collectionSet/collectionSet‘>
      <view class=‘title‘>
        <image src=‘/img/ic_collection_set.png‘></image>
        <view>收藏集</view>
      </view>
      <view class=‘count‘>userInfo.collectionSetCount || 0个</view>
    </view>
    <view class=‘item‘ hover-class=‘hover-class‘ catchtap=‘navigatItem‘ data-url=‘/pages/purchasedXiaoce/purchasedXiaoce‘>
      <view class=‘title‘>
        <image src=‘/img/user_buy.png‘></image>
        <view>已购小册</view>
      </view>
      <view class=‘count‘>userInfo.purchasedBookletCount
 || 0本</view>
    </view>
    <view class=‘item‘ hover-class=‘hover-class‘ catchtap=‘navigatItem‘ data-url=‘/pages/myPins/myPins?liked=1‘>
      <view class=‘title‘>
        <image src=‘/img/user_liked_pin.png‘></image>
        <view>赞过的沸点</view>
      </view>
      <view class=‘count‘>userInfo.likedPinCount || 0个</view>
    </view>
    <view class=‘item‘ hover-class=‘hover-class‘ catchtap=‘navigatItem‘ data-url=‘/pages/readHistory/readHistory‘>
      <view class=‘title‘>
        <image src=‘/img/view.png‘></image>
        <view>阅读过的文章</view>
      </view>
      <view class=‘count‘>userInfo.viewedEntriesCount || 0篇</view>
    </view>
    <view class=‘item‘ hover-class=‘hover-class‘ catchtap=‘navigatItem‘ data-url=‘/pages/manageTag/manageTag‘>
      <view class=‘title‘>
        <image src=‘/img/tag.png‘></image>
        <view>标签管理</view>
      </view>
      <view class=‘count‘>userInfo.subscribedTagsCount || 0个</view>
    </view>
  </view>

  <view class=‘card items‘>
    <view class=‘item‘ hover-class=‘hover-class‘ catchtap=‘navigatItem‘ data-url=‘/pages/feedback/feedback‘ data-open=‘true‘>
      <view class=‘title‘>
        <image src=‘/img/icon_feed_back.png‘></image>
        <view>意见反馈</view>
      </view>
    </view>
    <view class=‘item‘ hover-class=‘hover-class‘ catchtap=‘navigatItem‘ data-url=‘/pages/setting/setting‘ data-open=‘true‘>
      <view class=‘title‘>
        <image src=‘/img/settings.png‘></image>
        <view>设置</view>
      </view>
    </view>
    <view class=‘item‘ hover-class=‘hover-class‘ catchtap=‘navigatItem‘ data-url=‘/pages/miniqrcode/miniqrcode‘ data-open=‘true‘>
      <view class=‘title‘>
        <image src=‘/img/qrcode.png‘ style=‘width:28rpx;height:28rpx;padding:10rpx‘></image>
        <view>小程序码</view>
      </view>
    </view>
  </view>

</view>

js:

const utils = require(‘../../utils/utils.js‘)
const config = getApp().globalData.config
Page(
  data: 
    userInfo: ,
    userNotificationNum: 0,
    auth: ,
  ,
  onShow () 
    let auth = utils.ifLogined()
    this.setData(
      auth,
    )
    if (auth) 
      this.getUserInfo()
      this.userNotificationNum()
     else 
      this.setData(
        userInfo: ,
        userNotificationNum: 0,
      )
    
  ,
  navigatItem (e) 
    return utils.navigatItem(e)
  ,
  // 获取用户信息
  getUserInfo() 
    const auth = this.data.auth
    wx.request(
      url: `$config.apiRequestUrl/getUserInfo`,
      data: 
        src: ‘web‘,
        device_id: auth.clientId,
        uid: auth.uid,
        token: auth.token,
        current_uid: auth.uid,
      ,
      success: (res) => 
        let data = res.data
        if (data.s === 1) 
          this.setData(
            userInfo: data.d,
          )
         else 
          wx.showToast(
            title: data.m.toString(),
            icon: ‘none‘,
          )
        
      ,
      fail: () => 
        wx.showToast(
          title: ‘网路开小差,请稍后再试‘,
          icon: ‘none‘,
        )
      ,
    )
  ,
  // 消息中心消息条数
  userNotificationNum() 
    const auth = this.data.auth
    wx.request(
      url: `$config.notifyRequestUrl/getUserNotificationNum`,
      data: 
        src: ‘web‘,
        uid: auth.uid,
        token: auth.token,
      ,
      success: (res) => 
        let data = res.data
        if (data.s === 1) 
          this.setData(
            userNotificationNum: data.d && data.d.notification_num,
          )
         else 
          wx.showToast(
            title: data.m.toString(),
            icon: ‘none‘,
          )
        
      ,
      fail: () => 
        wx.showToast(
          title: ‘网路开小差,请稍后再试‘,
          icon: ‘none‘,
        )
      ,
    )
  ,
)

文章详情页

文章详情页返回的数据是整篇文章的 html 格式,如果是浏览器的话,直接显示即可,小程序里这里使用的是 wxParse 解析的。实现如下:

<wxs module=‘filters‘ src=‘../../filter/filter.wxs‘></wxs>
<import src="../../wxParse/wxParse.wxml"/>
<view class=‘container‘>
  <image wx:if=‘postInfo.screenshot‘ style=‘width:100%;height:340rpx;‘ mode=‘aspectFill‘ src=‘postInfo.screenshot‘></image>
  <view class=‘content‘>
    <view class=‘user‘>
      <view class=‘avatar‘ catchtap=‘toPersonal‘>
        <image mode=‘aspectFill‘ src=‘(postInfo.user && postInfo.user.avatarLarge) || "/img/default_avatar.png"‘></image>
      </view>
      <view class=‘info‘>
        <view class=‘name‘>postInfo.user && postInfo.user.username</view>
        <view class=‘others‘>
          <text class=‘time‘>postInfo.createdAt</text>
          <text>阅读 postInfo.viewsCount</text>
        </view>
      </view>
    </view>
    <view class=‘title‘>postInfo.title</view>
    <template is="wxParse" data="wxParseData:article.nodes"/>
  </view>
</view>

至于评论相关的,还没有写。。。

个人中心页

个人中心页和我的页面展现差不多,也是显示条目和页面跳转为主,调用相关的 API 即可,不再赘述。实现如下:

<view class=‘wrapper‘>
  <view class=‘card profile‘>
    <view class=‘info‘>
      <image class=‘avatar‘ mode=‘aspectFill‘ src=‘userInfo.avatarLarge‘ wx:if=‘userInfo.avatarLarge‘></image>
    <image class=‘avatar‘ src=‘/img/empty_avatar_user.png‘ wx:else></image>
      <view class=‘text‘>
        <view class=‘name‘>userInfo.username</view>
        <view class=‘jobtitle‘>userInfo.jobTitle</view>
        <view class=‘others‘>userInfo.selfDescription </view>
      </view>
    </view>
    <view class=‘bottom‘>
      <view class=‘l‘>
        <view class=‘action‘>
          <view>userInfo.followeesCount</view>
          <view class=‘key‘>关注</view>
        </view>
        <view class=‘action‘>
          <view>userInfo.followersCount</view>
          <view class=‘key‘>关注者</view>
        </view>
      </view>
      <!-- <view class=‘edit‘>编辑</view> -->
      <image src=‘/img/ic_dynamic_vote.png‘ class=‘trend‘ catchtap=‘showDataTrend‘></image>
    </view>
  </view>

  <view class=‘card items‘>
    <view class=‘item‘ hover-class=‘hover-class‘ catchtap=‘navigatItem‘ data-url=‘/pages/dynamic/dynamic?thirduid=thirduid‘ data-open=‘true‘>
      <view class=‘title‘>
        <view>动态</view>
      </view>
    </view>
  </view>

  <view class=‘card items‘>
    <view class=‘item‘ hover-class=‘hover-class‘ catchtap=‘navigatItem‘ data-url=‘/pages/myPins/myPins?thirduid=thirduid‘ data-open=‘true‘>
      <view class=‘title‘>
        <view>沸点</view>
      </view>
      <view class=‘count‘>userInfo.pinCount || 0</view>
    </view>
    <view class=‘item‘ hover-class=‘hover-class‘ catchtap=‘navigatItem‘ data-url=‘/pages/originalPost/originalPost?thirduid=thirduid‘ data-open=‘true‘>
      <view class=‘title‘>
        <view>原创文章</view>
      </view>
      <view class=‘count‘>userInfo.postedPostsCount</view>
    </view>
    <view class=‘item‘ wx:if=‘userInfo.postedEntriesCount‘ hover-class=‘hover-class‘ catchtap=‘navigatItem‘ data-url=‘/pages/sharePost/sharePost?thirduid=thirduid‘ data-open=‘true‘>
      <view class=‘title‘>
        <view>分享文章</view>
      </view>
      <view class=‘count‘>userInfo.postedEntriesCount</view>
    </view>
    <view class=‘item‘ hover-class=‘hover-class‘ catchtap=‘navigatItem‘ data-url=‘/pages/collectionSet/collectionSet?thirduid=thirduid‘ data-open=‘true‘>
      <view class=‘title‘>
        <view>收藏集</view>
      </view>
      <view class=‘count‘>userInfo.collectionSetCount</view>
    </view>
  </view>

  <view class=‘card items‘>
    <view class=‘item‘ hover-class=‘hover-class‘ catchtap=‘navigatItem‘ data-url=‘/pages/favorate/favorate?thirduid=thirduid‘ data-open=‘true‘>
      <view class=‘title‘>
        <view>喜欢的文章</view>
      </view>
      <view class=‘count‘>userInfo.collectedEntriesCount</view>
    </view>
    <view class=‘item‘ hover-class=‘hover-class‘ catchtap=‘navigatItem‘ data-url=‘/pages/subscribedTag/subscribedTag?thirduid=thirduid‘ data-open=‘true‘>
      <view class=‘title‘>
        <view>关注的标签</view>
      </view>
      <view class=‘count‘>userInfo.subscribedTagsCount</view>
    </view>
  </view>

  <view class=‘card items‘>
    <view class=‘item‘ wx:if=‘userInfo.community && userInfo.community.weibo && userInfo.community.weibo.username‘>
      <view class=‘title‘>
        <image src=‘/img/icon_profile_weibo.png‘></image>
        <view class=‘val‘>userInfo.community.weibo.username</view>
      </view>
    </view>
    <view class=‘item‘ wx:if=‘userInfo.blogAddress‘>
      <view class=‘title‘>
        <image src=‘/img/icon_profile_blog.png‘></image>
        <view class=‘val‘>userInfo.blogAddress</view>
      </view>
    </view>
  </view>

</view>

文章数据页

文章数据页就是显示你的文章获得了多少收藏、多少评论、多少阅读相关的数据,该页面主要是数字滚动动画的实现。这里的实现思路是这样的:

将数字从 0 到 N 纵向排列,然后 translateY 到相应的数字即可,主要的实现如下:

<view class=‘countInner‘ style=‘transform:translateY(-100*(item.length-1)/item.length%)‘ wx:for=‘filters.strToNumArr(userInfo.totalCollectionsCount)‘ wx:key=‘index‘ wx:for-item=‘item‘ wx:for-index=‘index‘>
    <view wx:for=‘item‘ wx:key=‘idx‘ wx:for-item=‘i‘ wx:for-index=‘idx‘>i</view>
</view>

// 按照长度生成 0 字符串
generateZeroArr (len) 
  Array.apply(null, Array(len)).map(function (item, i) 
    return 0
  )

其他细节不再赘述,可查看源码

消息中心页

消息中心有两个 tab,可以点击、滑动切换,这里的实现是 swiper。这里需要注意的是:消息分为不同的 category,不同的 category 展现的内容是不一样的,所以这里需要区分下,目前我获取到的 category 只有几种,是否全部覆盖所有的 category 只能等遇到没有覆盖的时候随手补上了。。。实现如下:

<wxs module=‘filters‘ src=‘../../filter/filter.wxs‘></wxs>
<view class=‘container‘>
  <view class=‘top tabs‘>
    <view class=‘inner‘>
      <view class=‘tab currentSwiper === "0" ? "active" : ""‘ data-index=‘0‘ catchtap=‘switchSwiper‘>用户消息</view>
      <view class=‘tab currentSwiper === "1" ? "active" : ""‘ data-index=‘1‘ catchtap=‘switchSwiper‘>系统消息</view>
    </view>
    <view class=‘bar‘ style=‘left:currentSwiper*50%‘></view>
  </view>
  <swiper class=‘swiper‘ autoplay=‘false‘ indicator-dots=‘false‘ bindchange=‘swiperChanged‘ current=‘currentSwiper‘>
    <swiper-item item-id=‘0‘>
      <scroll-view scroll-y bindscrolltolower=‘getMoreUserNotification‘>
        <view class=‘item‘ wx:for=‘list‘ wx:key=‘index‘>
          <view class=‘avatar‘ data-id=‘item.users[0].objectId‘ catchtap=‘toPersonal‘>
            <image mode=‘aspectFill‘ src=‘item.users[0].avatarLarge || "/img/default_avatar.png"‘></image>
          </view>
          <view class=‘content‘ wx:if=‘item.category==="collection"‘>
            <view>item.users[0].username等item.count人 喜欢了你的文章 <text data-id=‘item.entry && item.entry.objectId‘ catchtap=‘toPostDetail‘>item.entry && item.entry.title</text></view>
            <view class=‘time‘>filters.timeBefore(item.updatedAtString)</view>
          </view>
          <view class=‘content‘ wx:elif=‘item.category==="comment"‘>
            <view>item.users[0].username回复了你在文章 <text data-id=‘item.entry && item.entry.objectId‘ catchtap=‘toPostDetail‘>item.entry && item.entry.title</text> 的评论</view>
            <view class=‘comment‘>(item.reply && item.reply.content) || (item.comment && item.comment.content)</view>
            <view class=‘time‘>filters.timeBefore(item.updatedAtString)</view>
          </view>
          <view class=‘content‘ wx:elif=‘item.category==="follow"‘>
            <view>item.users[0] && item.users[0].username 关注了你</view>
            <view class=‘time‘>item.users[0] && item.users[0].jobTitle</view>
          </view>
          <view class=‘content‘ wx:elif=‘item.category==="comment-like"‘>
            <view>item.users[0] && item.users[0].username 赞了你在 <text>item.entry && item.entry.title</text> 的评论</view>
          </view>
          <view class=‘content‘ wx:elif=‘item.category==="pin-like"‘>
            <view>item.users[0] && item.users[0].username 赞了你的 <text>沸点</text></view>
            <view class=‘time‘>filters.timeBefore(item.updatedAtString)</view>
          </view>
          <view class=‘content‘ wx:elif=‘item.category==="pin-comment"‘>
            <view>item.users[0] && item.users[0].username 回复了你的 <text>沸点</text></view>
            <view class=‘comment‘>(item.reply && item.reply.content) || (item.pinComment && item.pinComment.content)</view>
            <view class=‘time‘>filters.timeBefore(item.updatedAtString)</view>
          </view>
          <view class=‘content‘ wx:else>未知状态,可提交给开发者</view>
        </view>
      </scroll-view>
      <empty wx:if=‘!list.length‘ tip=‘暂无消息‘></empty>
    </swiper-item>
    <swiper-item item-id=‘1‘>
      <view wx:if=‘systemInfoList.length‘>
        不好意思,我没有系统消息,所以看不到系统消息 API 的数据结构,也看不到样式。。。
      </view>
      <empty wx:if=‘!systemInfoList.length‘ tip=‘暂时没有系统通知‘></empty>
    </swiper-item>
  </swiper>
</view>

动态页

动态页也需要稍微注意一点,动态页分不同的 category,这里覆盖的有 followcollectionsubscribe,如果遇到未覆盖的只能顺手补上。实现如下:

<wxs module=‘filters‘ src=‘../../filter/filter.wxs‘></wxs>
<view class=‘container‘>
  <view class=‘item‘ wx:for=‘list‘ wx:key=‘index‘>
    <view class=‘inner‘ wx:if=‘item.category === "follow"‘>
      <view class=‘top‘>
        <view class=‘l‘>
          <image mode=‘widthFix‘ src=‘/img/ic_dynamic_user.png‘></image>
          <view>关注了:</view>
        </view>
        <view class=‘r‘>filters.timeBefore(item.createdAtString)</view>
      </view>
      <view class=‘bottom‘>
        <view class=‘tagcard‘>
          <image mode=‘aspectFill‘ src=‘item.users[0].avatarLarge || "/img/entry_image_default.png"‘></image>
          <view class=‘others‘>
            <view class=‘title‘>item.users && item.users[0].username</view>
            <view class=‘info‘ wx:if=‘item.users && item.users[0].jobTitle && item.users[0].company‘>item.users && item.users[0].jobTitle @ item.users && item.users[0].company</view>
          </view>
        </view>
      </view>
    </view>
    <view class=‘inner‘ wx:if=‘item.category === "collection"‘>
      <view class=‘top‘>
        <view class=‘l‘>
          <image mode=‘widthFix‘ src=‘/img/ic_dynamic_collect.png‘></image>
          <view>喜欢了:</view>
        </view>
        <view class=‘r‘>filters.timeBefore(item.createdAtString)</view>
      </view>
      <view class=‘bottom‘>
        <view class=‘tagcard‘ data-id=‘item.entry && item.entry.objectId‘ catchtap=‘toPostDetail‘>
          <image mode=‘aspectFill‘ src=‘item.entry.screenshotUrl  || "/img/entry_image_default.png"‘></image>
          <view class=‘others‘>
            <view class=‘title‘>item.entry && item.entry.title</view>
          </view>
        </view>
      </view>
    </view>
    <view class=‘inner‘ wx:if=‘item.category === "subscribe"‘>
      <view class=‘top‘>
        <view class=‘l‘>
          <image mode=‘widthFix‘ src=‘/img/ic_dynamic_tag.png‘></image>
          <view>关注了 item.tags && item.tags[0].title 等 item.tags && item.tags.length 个标签</view>
        </view>
        <view class=‘r‘>filters.timeBefore(item.createdAtString)</view>
      </view>
      <view class=‘bottom‘>
        <view class=‘imglist‘>
          <image mode=‘aspectFill‘ wx:for=‘item.tags‘ wx:key=‘idx‘ wx:for-index=‘idx‘ wx:for-item=‘i‘ src=‘i.icon‘></image>
        </view>
      </view>
    </view>
  </view>
</view>

标签管理页

这个页面的 tab 也是用 swiper 实现的,复用的组件是 tagItem。实现如下:

<view class=‘container‘>
  <view class=‘top tabs‘>
    <view class=‘inner‘>
      <view class=‘tab currentSwiper === "0" ? "active" : ""‘ data-index=‘0‘ catchtap=‘switchSwiper‘>已关注标签</view>
      <view class=‘tab currentSwiper === "1" ? "active" : ""‘ data-index=‘1‘ catchtap=‘switchSwiper‘>所有标签</view>
    </view>
    <view class=‘bar‘ style=‘left:currentSwiper*50%‘></view>
  </view>
  <swiper class=‘swiper‘ autoplay=‘false‘ indicator-dots=‘false‘ bindchange=‘swiperChanged‘ current=‘currentSwiper‘>
    <swiper-item item-id=‘0‘>
      <view>
        <tagItem list=‘tagList‘></tagItem>
      </view>
      <empty wx:if=‘!tagList.length‘ tip=‘暂无消息‘></empty>
    </swiper-item>
    <swiper-item item-id=‘1‘>
      <scroll-view scroll-y bindscrolltolower=‘getMoreRecommendTags‘>
        <view class=‘hot‘ wx:if=‘hotTagList.length‘>
          <view class=‘title‘>推荐标签</view>
          <tagItem list=‘hotTagList‘></tagItem>
        </view>
        <view class=‘suggest‘ wx:if=‘recommendTagList.length‘>
          <view class=‘title‘>你可能感兴趣的标签</view>
          <tagItem list=‘recommendTagList‘></tagItem>
        </view>
      </scroll-view>
      <empty wx:if=‘!hotTagList.length && recommendTagList.length‘ tip=‘暂时没有系统通知‘></empty>
    </swiper-item>
  </swiper>
</view>

原创文章页、喜欢的文章页、阅读过的文章页、赞过的沸点页、关注的标签页

这几个页面都是组件的复用,没有太多要说的。

意见反馈页、设置页

这两个页面只是一个关于页面而已。。。

完成度

APP 里面的东西实在是不少,包括页面和交互,要完全照抄实现确实需要一些时间和精力,UI 之类的都是简单测量+肉眼调试实现的,下面列出页面和交互的完成度,这里应该只是列出了绝大部分(还是上面那句话,APP 里面的东西实在是不少),未列出、未实现的后续会根据时间、精力来实现。

实际完成度请以代码为主(线上小程序也会持续更新)。

页面完成度

貌似不支持 markdown 待办事宜写法?QAQ

  • [x] 启动页
  • [x] 登录、未登录跳转逻辑和页面数据刷新逻辑等
  • [x] HOME、搜索、沸点、小册 TAB 涉及到的上拉、下拉刷新
  • [x] POST、ENTRY(文章类型不同) 详情页
  • [ ] HOME TAB

    • [x] 首页

      • [x] 热门推荐
      • [x] 下部列表
    • [ ] 标签展示相关
  • [ ] 搜索 TAB

    • [x] 顶部轮播
    • [x] 热门文章
    • [ ] 搜索功能相关
    • [ ] 本周最热
    • [ ] 收藏集

      • [ ] ...
    • [ ] 活动

      • [ ] ...
  • [ ] 沸点 TAB

    • [ ] 推荐

      • [x] 顶部热门沸点
      • [x] 沸点列表
      • [x] 沸点详情
    • [ ] 话题
    • [ ] 动态
    • [ ] 发布沸点
  • [ ] 小册 TAB

    • [x] 小册列表
    • [ ] 小册详情
  • [ ] 我的 TAB

    • [ ] 个人主页

      • [x] 文章数据
      • [ ] 编辑
      • [ ] 关注、被关注列表
      • [x] 动态页
      • [x] 沸点页
      • [x] 原创文章页
      • [x] 收藏集

        • [ ] 收藏集详情页
      • [x] 喜欢的文章
      • [x] 关注的标签

        • [ ] 标签详情页
    • [x] 我喜欢的
    • [x] 收藏集
    • [ ] 已购小册
    • [x] 赞过的沸点
    • [x] 阅读过的文章
    • [x] 标签管理

      • [x] 已关注标签
      • [x] 所有标签

        • [x] 推荐标签
        • [x] 所有标签
    • [ ] 夜间模式
    • [x] 意见反馈(和官方 APP 有差异,这里是个简单的关于页)
    • [ ] 设置

      • [ ] ...
  • [x] 登录页
  • [ ] 注册页
  • [ ] 修改密码页
  • [x] 其他完成部分...
  • [ ] 未完待续部分...

交互完成度

评论、留言、关注、添加到收藏集、喜欢、发表沸点等暂时均没有实现,因为 APP 里面的东西实在是不少......

  • [ ] 评论
  • [ ] 留言
  • [ ] 关注
  • [ ] 喜欢
  • [ ] 未完待续部分...

说明

  • 1、话说 API 域名(二级)真是多啊,小程序后台域名白名单最多只能配 20 个,现在已经占了 16 个了,感觉要完整抄完实现 APP 版小程序,配额不够啊。不行的话,就只能搭个 server 代理了;
  • 2、个别接口只有 APP 用到了,请求字段需要按照 web 的略作调整;个别接口也要设置对应的 header
  • 3、文章详情页返回的是整片文章的 html 格式的 content,这里使用的是开源的 wxParse 进行富文本解析;
  • 4、由于小程序的限制,第三方的 url 不能在 webview 中打开,所以文章里面的外链能点开算我输;
  • 5、开发时,个别细节需要稍微注意,比如:沸点 tab 页,如果已经滑到了顶部,onShow 获取新数据,否则,不刷新;未登录时,首页 APP 调用的 API 是 get_recommended_entry就是懒为了方便小程序里仍然使用 get_entry_by_timeline;其他的不一一赘述,详情可 查看源码
  • 6、由于账号权限等问题有些 API 需要天时地利人和,部分 API 返回的数据格式没有拿到,所以对应的页面也没有写,比如:系统消息页面(最近一直没有系统消息)等;
  • 7、部分数据可能未完全覆盖,比如:用户消息这块,目前列举出的 categorycollectioncommentfollowcomment-likepin-likepin-comment,可能还会有其他消息类型,遇到了会一一补上;还有动态页,也是同样的问题;可能还有其他没有完全覆盖的数据;
  • 8、点击某些文章进入详情页会提示 illegal token,亦或文章没有正常显示出来,应该是请求参数需要略作调整,或者文章类型需要判断。类似这样的小问题,后续会调整补充;
  • 9、小程序(非小游戏)在于一个 字,应该是一个应用的浓缩精华版,而不应该是一个内容丰富多彩的 APP100% 的复制版,这样会显得比较臃肿,此处应该有 but,该小程序仅仅是出于学习交流的目的,所以这个问题不在我们的考虑范围内;
  • 10、登录现在只能手机号登录,邮箱登录给忘记了,回头一并加上;
  • 11、我也是有人生梦想的人;
转载请注明出处

本文转载于猿2048:两周撸一个微信小程序

微信小程序是什么?如何快速搭建一个微信小程序?(代码片段)

...是什么二、安全管理三、微信小程序的功能四、快速开发一个微信小程序APP1、集成即构实时音视频SDK2、初始化SDK引擎3、创建房间与登录房间4、房主创建房间5、推流与拉流6、拉流预览7、播放实时画面统一封装8、其他工作五、... 查看详情

入门系列微信小程序简介(代码片段)

...构首先,我们使用微信公众平台提供的开发者工具,创建一个简单的小程序项目,观察项目的目录结构不难看出,一个典型的微信小程序,通常包含一个描述整体的主体部分,以及一个描述页面的pages文件夹主体:用于描述整体... 查看详情

微信小程序如何跳转到另一个小程序?(代码片段)

如何实现小程序跳转其他小程序? 首先说一下到另一个小程序需要得东西:  跳转目标小程序的APPID  使用微信小程序API(wx.navigateToMiniProgram)一、如何获取目标小程序APPID(如已知目标小程序appid可直接阅读第二... 查看详情

微信小程序学习(代码片段)

一、环境准备     1、注册一个微信小程序帐号            https://mp.weixin.qq.com/wxopen/waregister?action=step1        注册完成后申请一个AppId,前期开发也可以不申请,使用测试id,测试id一些支付等功能不能... 查看详情

微信小程序入门之构建一个简单todos应用(代码片段)

...信中开发具有原生APP体验的服务。2.框架:框架的核心是一个响应的数据绑定系统。整个系统分为两块视图层(View)和逻辑层(AppSe 查看详情

微信小程序工具类(代码片段)

wechat-common-sdk?场景:目前工作中的项目需要包含并使用另一个项目。也许是第三方库,或者你独立开发的,用于多个父项目的库。现在问题来了:你想要把它们当做两个独立的项目,同时又想在一个项目中使用另一个。我们举一... 查看详情

微信小程序学习:基本框架(代码片段)

...和微信小程序基本框架1.HelloWorld!2.架构分析来新建一个带基础模板的页面看看①目录结构②主体文件③页面文件④其他文件3.配置文件详解主配置文件app.json页面配置文件4.逻辑层js文件App函数注册小程序Page函数注册页面初... 查看详情

微信小程序生成excel(代码片段)

...直在查找在微信小程序中生成Excel的办法。需求就是根据一个json数据或者对象数组,生成一个Excel文件,或者打开Excel文件。网上找了很久,没找到很有效的解决办法。最后自己动手做了一个,不过生成效率还比较... 查看详情

微信小程序生成excel(代码片段)

...直在查找在微信小程序中生成Excel的办法。需求就是根据一个json数据或者对象数组,生成一个Excel文件,或者打开Excel文件。网上找了很久,没找到很有效的解决办法。最后自己动手做了一个,不过生成效率还比较... 查看详情

微信小程序生成excel(代码片段)

...直在查找在微信小程序中生成Excel的办法。需求就是根据一个json数据或者对象数组,生成一个Excel文件,或者打开Excel文件。网上找了很久,没找到很有效的解决办法。最后自己动手做了一个,不过生成效率还比较... 查看详情

微信小程序的模板语法,如何编写一个微信查询页面(代码片段)

微信小程序的基本模板语法<!--pages/dome/dome.wxml--><!--view==divtext==span--><block>block占位符,渲染时会不见标签,内容还是可见的</block><text>我是一个行内元素</text><view>我是一个块元素</view><vi 查看详情

搭建mock服务器(微信小程序)(代码片段)

...完全模拟访问路由和数据,选择在搭建本地mock服务器是一个不错的选择。以下示例了一个mock服务器的搭建过程以及以学生为对象进行增删改查分页的示例。前提要求安装了node.js创建服务器我们在自己电脑上选择一个位置,创建... 查看详情

两天撸一个天气应用微信小程序(代码片段)

这是一个完整的已经线上运行的天气应用小程序,点击可查看源码,可随意star。也可以扫描下方的小程序码直接体验。效果图:说明鸣谢:pure天气APP:首页样式借鉴了pure天气APP。如侵删。数据来源地理编码、天气数据均来自百... 查看详情

微信小程序后端java接口开发(代码片段)

...信小程序使用wx.request(OBJECT)来调用后端接口。首先我们来一个简单案例——helloworld实现1、搭建一个springboot项目并引入依赖<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-s 查看详情

微信小程序一个页面多个按钮分享怎么处理(代码片段)

首先呢,第一步先看api文档:组件:buttonhttps://developers.weixin.qq.com/miniprogram/dev/component/button.html框架-逻辑层-注册页面-页面事件处理函数:onShareAppMessagehttps://developers.weixin.qq.com/miniprogram/dev/framework/app-servi 查看详情

微信小程序一个页面多个视频(video组件),限制一个播放,其它暂停(代码片段)

微信小程序一个页面多个视频(video组件),限制一个播放,其它暂停html<viewclass='videoBoxTitle'>视频列表</view><blockwx:for="videoListData"wx:index='index'w 查看详情

微信小程序学习:组件学习(代码片段)

...ss③style④hidden⑤data-*属性⑥`bind*/catch*`属性二.通过一个计算器来学组件1.view组件2.input组件常用属性:valuepassword属性placeholder属性disabledmaxlength属性bind 查看详情

微信小程序的那些坑(代码片段)

...发社区的问题,没有很完整的解决方案,于是自己摸索了一个方法)wxml<checkbox-groupclass="checkbox-group"&g 查看详情