无html
该小程序代码已开源,点击可查看源码,可随意 star。也能够先扫描下方的小程序码直接体验。node
前段时间写了一个简单的小程序 QuietWeather,源码在这里,具体实现相关可查看这篇文章:两天撸一个天气应用微信小程序。可是这个 小程序 和 QuietWeather 彻底不是一个数量级的。so,该文章梳理内容会有那么一点儿多,想跳过的能够直接拉到最下面。。。android
这里先上效果图,感兴趣的也能够 查看源码 。实际体验可扫描👆上面的小程序码。git
PC
开发者工具录制,会有些卡顿
文章数据入口调整了,也保留了动画,请酌情忽略
gif
卡顿
在 filter
目录下建立了一个 wxs
,里面是用到的过滤器,须要注意的是,wxs
的语法只能是 es5
,并且部分 js
语法是不支持的,具体支持的语法可查看微信小程序开发文档。github
细分的话,细分的话目前总共写了 8 个组件:web
empty
item
组件 feidianItem
:分为上半部分 feidianItemTop
和下半部分组件 feidianItemBottom
,会在沸点、赞过的沸点等相关页面复用item
:有两种展示样式,这里写了独立的两个组件:postItemOne
、postItemTwo
item
tagItem
:会在标签管理等相关页面复用item
xiaoceItem
:会在小册 tab
、购买的小册等相关页面复用组件相对比较简单,基本上是以展示为主,并无太多交互,可下载源码 在微信开发者工具调试查看相关效果。小程序
HOME
页主要展示两部分:顶部热门文章推荐和下面的推荐文章列表,使用了两个上面提到的组件: postItemOne
、postItemTwo
。且未登陆时,会有一个登录提示。实现以下:微信小程序
<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>
其中,顶部热门推荐的刷新会有如下的实现效果,这里须要稍微注意下:api
热门推荐点击刷新,将当前的 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
页两部分组成:顶部的 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>
该页面复用的组件是 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 * 135}px` }) }, }) }, // 热门推荐列表 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 {} }, })
小册页面基本上没有什么可说的,只有小册展示。。。组件复用 xiaoceItem
,实现以下:
<view class='lists'> <xiaoceItem list='{{xiaoceList}}'></xiaoceItem> </view>
该页面以简单的数目展示(未读消息条数、收藏集数目、阅读过的文章数)和跳转为主,调用相关的 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
,这里覆盖的有 follow
、collection
、subscribe
,若是遇到未覆盖的只能顺手补上。实现以下:
<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
[ ] HOME TAB
[x] 首页
[ ] 搜索 TAB
[ ] 收藏集
[ ] 活动
[ ] 沸点 TAB
[ ] 推荐
[ ] 小册 TAB
[ ] 个人 TAB
[ ] 我的主页
[x] 收藏集
[x] 关注的标签
[x] 标签管理
[x] 全部标签
[ ] 设置
评论、留言、关注、添加到收藏集、喜欢、发表沸点等暂时均没有实现,由于 APP 里面的东西实在是很多......
API
域名(二级)真是多啊,小程序后台域名白名单最多只能配 20
个,如今已经占了 16
个了,感受要完整APP
版小程序,配额不够啊。不行的话,就只能搭个 server
代理了;APP
用到了,请求字段须要按照 web
的略做调整;个别接口也要设置对应的 header
;html
格式的 content
,这里使用的是开源的 wxParse 进行富文本解析;url
不能在 webview
中打开,因此文章里面的外链能点开算我输;tab
页,若是已经滑到了顶部,onShow
获取新数据,不然,不刷新;未登陆时,首页 APP
调用的 API 是 get_recommended_entry
,get_entry_by_timeline
;其余的不一一赘述,详情可 查看源码;API
须要天时地利人和,部分 API
返回的数据格式没有拿到,因此对应的页面也没有写,好比:系统消息页面(最近一直没有系统消息)等;category
有 collection
、comment
、follow
、comment-like
、pin-like
、pin-comment
,可能还会有其余消息类型,遇到了会一一补上;还有动态页,也是一样的问题;可能还有其余没有彻底覆盖的数据;illegal token
,亦或文章没有正常显示出来,应该是请求参数须要略做调整,或者文章类型须要判断。相似这样的小问题,后续会调整补充;APP
的 100%
的复制版,这样会显得比较臃肿,此处应该有 but
,该小程序仅仅是出于学习交流的目的,因此这个问题不在咱们的考虑范围内;转载请注明出处