上等同糟的《微信小程序的小豆瓣图书》制作了一个书籍的查询功能,只是简短地动至了网络要,其他大部略带程序应该之文化。而此次的演示是知乎日报,功能点于多,页面也正如上次复杂了多。在自身修这DEMO之前,网上早就有过多网友将来了扳平之DEMO,也是怪不易的,毕竟这个案例很经典,有比较完好的API,很值得模仿学习。本次个人的DEMO也终究一浅小小的练习吧。

是因为知乎日报是一个资讯类的App,UI的布局要是为新闻列表页、资讯详情页和评论页为主,当然本次也顺手了下设置页,不过当下效能尚未编写,过段时间会更新上,继续全面。

API分析

本次用使用了知乎日报的API,相比上次豆瓣图书的数额比较多了,但是一些仍有限定,而且好从未有过找到评论接口的分页参数,所以评论这块没有做多少的分页。

以下是运用及之具体API,更加详细参数和归结构可参考网上网友分享的
知乎日报-API-分析
,在斯即非做重新分析了。

启航界面图

http://news-at.zhihu.com/api/4/start-image/{size}

参数 说明
size 图片尺寸,格式:宽*高。例如: 768*1024

获取刚进入以时之亮封面,可以因传递的尺码参数来抱适配用户屏幕的书面。

取最新日报

http://news-at.zhihu.com/api/4/news/latest

返的多寡用于日报的首页列表,首页的布局来内外部分,上部分凡是图滑动模块,用于展示热门日报,下有些是首页日报列表,以上接口返回的数码来热日报及首页日报

获取日报详细

http://news-at.zhihu.com/api/4/news/{id}

参数 说明
id 日报id

在点击日报列表也的日报项时,需要跨越反到日报详情页显示日报的实际信息,这个接口用来获取日报的亮封面及具体内容。

史日报

http://news.at.zhihu.com/api/4/news/before/{date}

参数 说明
date 年月日格式时间yyyyMMdd,例如:20150903、20161202

是接口也是故以及首页列表的日报显示,但是差之是这个接口需要传一个日期参数,如20150804格式。获取最新日报接口只能获得当天之日报列表,如果用取前天要更久之前的日报,则需之接口单独赢得。

日报额外信息

http://news-at.zhihu.com/api/4/story-extra/{id}

参数 说明
id 日报id

当日报详情页面被,不仅要显示日报的情节,好内需格外获取这日报的评价数及推举人等额外信息。

日报长评

http://news-at.zhihu.com/api/4/story/{id}/long-comments

参数 说明
id 日报id

日报的评页面显示丰富评用到的接口(没有找到分页参数,分页没有做)

日报短评

http://news-at.zhihu.com/api/4/story/{id}/short-comments

参数 说明
id 日报id

日报的评论页面显示段评用到之接口(没有找到分页参数,分页没有开)

主题日报栏目列表

http://news-at.zhihu.com/api/4/themes

主页的侧边栏显示出主题日报的列表,需要经此接口获取主题日报栏目列表

主题日报具体内容列表

http://news-at.zhihu.com/api/4/theme/{themeId}

参数 说明
themeId 主题日报栏目id

在主页侧栏点击主题日报上正题日报的情页,需要展示是主题日报下之日报列表。

代码编写

启动页

作一个仿制知乎日报的伪APP,高大上之启航封面是要的,哈哈。启动页面很简短,请求一个采取启动封面接口,获取封面路径和版权信息。当上页面,在onLoad事件被落屏幕的从容和高来呼吁适合尺寸的图纸,在onReady遭到要加载图片,在恳求成果后,延迟2s进入首页,防止页面一闪而过。

onLoad: function( options ) {
    var _this = this;
    wx.getSystemInfo( {
      success: function( res ) {
        _this.setData( {
          screenHeight: res.windowHeight,
          screenWidth: res.windowWidth,
        });
      }
    });
},

onReady: function() {
    var _this = this;
    var size = this.data.screenWidth + '*' + this.data.screenHeight;
    requests.getSplashCover( size, ( data ) => {
      _this.setData( { splash: data });
    }, null, () => {
      toIndexPage.call(_this);
    });
}

/**
 * 跳转到首页
 */
function toIndexPage() {
  setTimeout( function() {
    wx.redirectTo( {
      url: '../index/index'
    });
  }, 2000 );
}

图片 1

首页

轮播图

首页顶部需要运用轮播图来展示热门日报,小程序中的Swipe组件可以兑现。

<swiper class="index-swiper" indicator-dots="true" interval="10000">
    <block wx:for="{{sliderData}}">
        <swiper-item data-id="{{item.id}}" bindtap="toDetailPage">
            <image mode="aspectFill" src="{{item.image}}" style="width:100%" />
            <view class="mask"></view>
            <view class="desc"><text>{{item.title}}</text></view>
        </swiper-item>
    </block>
</swiper>

富有的情还要要当swiper-item签中,因为咱们的图纸不止有雷同布置,而是来多独热门日报信息,需要用循环来显示数据。这里用指定的凡image里之属性mode设置为aspectFill举凡为适应组件的大幅度,这需要牺牲他的高度,即产生或推,但马上是无比好的来得力量。toDetailPage举凡点击事件,触发跳转到日报详情页。在超反至日报详情页需要附带日报的id过去,我们以循环列表的早晚将当下日报的id满怀到标签的data中,用data-id标识,这来硌类似与html5被之data-*API。当在斯标签上起点击事件之时段,我们可通过Event.currentTarget.dataset.id来获取data-id的值。

图片 2

日报列表

列表的布局大同小异,不过这里的列表涉及到分页,我们好毫不犹豫地运用scroll-view组件,它的scrolltolower凡雅好用的,当组件滚动到底层就见面硌这个波。上次之稍豆瓣图书为是动了之组件分页。不过这次的分页动画与上次非雷同,而是用一个附带旋转动画的基础代谢图标,使用官方的卡通片api来落实转。

<view class="refresh-block" wx:if="{{loadingMore}}">
    <image animation="{{refreshAnimation}}" src="../..refresh.png"></image>
</view>

代码中生出一个强烈的animation属性,这个特性就是用来决定动画的。

/**
 * 旋转上拉加载图标
 */
function updateRefreshIcon() {
  var deg = 360;
  var _this = this;

  var animation = wx.createAnimation( {
    duration: 1000
  });

  var timer = setInterval( function() {
    if( !_this.data.loadingMore )
      clearInterval( timer );
    animation.rotateZ( deg ).step();
    deg += 360;
    _this.setData( {
      refreshAnimation: animation.export()
    })
  }, 1000 );
}

当列表加载数据常常,给动画设置一个时长duration,然后按照Z轴旋转,即垂直方向旋转rotateZ,每次转360渡过,周期是1000毫秒。

列表的布局以及上次的多少豆瓣图书的构造基本上,用到了巡回结构wx:for暨判断语句wx:if
wx:else来支配不同的显示方向。

<view class="common-list">
    <block wx:for="{{pageData}}">
        <view class="list-item {{item.images[0] ? 'has-img': ''}}" wx:if="{{item.type != 3}}" data-id="{{item.id}}" bindtap="toDetailPage">
            <view class="content">
                <text>{{item.title}}</text>
            </view>
            <image wx:if="{{item.images[0]}}" src="{{item.images[0]}}" class="cover"></image>
        </view>
        <view class="list-spliter" wx:else>
            <text>{{item.title}}</text>
        </view>
    </block>
</view>

class="list-spliter"这块是用来展示日期,列表中的日报只要非是当天底记录,就以当中插一漫长日期显示块。在列表项中生一个老三长运算判断输出具体的class{{item.images[0] ? 'has-img': ''}},是以列表中或许没有图,因此用判定当前产生没发图,没有图就非弥加class为has-img来决定带有图片列表项的布局。

别按钮

以小程序中从不侧栏组件,无法完成侧滑手势显示侧栏(本人发现touchstart事件和tap事件闹冲突,无法实现有手势侧滑判断,所以并未用侧滑手势,可能是本人理解太浅了,没有发现解决方法,嘿嘿…),浮动按钮的样式参照了Android中的FloatAction经典按钮。可以变在界面及,还足以滑动到任意位置,背景也小透明。

<view class="float-action" bindtap="ballClickEvent" style="opacity: {{ballOpacity}};bottom:{{ballBottom}}px;right:{{ballRight}}px;" bindtouchmove="ballMoveEvent"> 
</view>

.float-action {
  position: absolute;
  bottom: 20px;
  right: 30px;
  width: 50px;
  height: 50px;
  border-radius: 50%;
  box-shadow: 2px 2px 10px #AAA;
  background: #1891D4;
  z-index: 100;
}

按钮的体随便抓了转,宽高用了px举凡因后的位移判断用取屏幕的松动高信息,这些信息的单位是px。wxml绑定了点击事件及活动事件,点击事件是控制侧栏弹有,滑动事件是按钮移动。

//浮动球移动事件
ballMoveEvent: function( e ) {
    var touchs = e.touches[ 0 ];
    var pageX = touchs.pageX;
    var pageY = touchs.pageY;
    if( pageX < 25 ) return;
    if( pageX > this.data.screenWidth - 25 ) return;
    if( this.data.screenHeight - pageY <= 25 ) return;
    if( pageY <= 25 ) return;
    var x = this.data.screenWidth - pageX - 25;
    var y = this.data.screenHeight - pageY - 25;
    this.setData( {
        ballBottom: y,
        ballRight: x
    });
}

touchmove事件备受之见面传递一个event参数,通过之参数可以拿走到手上手势滑动到之切切实实坐标信息e.touches[ 0 ]

侧滑菜单

侧滑菜单是一个经APP布局方案,小序中并未提供者组件,甚是不满。不过实现起来也不是好麻烦,但是究竟觉得出硌转扭…

侧滑菜单的样式采用了原则性定位的布局position: fixed,默认隐藏和左边,当点击浮动按钮时弹有,点击遮罩或者侧栏上边的闭馆按钮时收回。侧栏的弹奏有同取消动画采用小程序提供的动画API。

<view class="slide-mask" style="display:{{maskDisplay}}" bindtap="slideCloseEvent"></view>
<view class="slide-menu" style="right: {{slideRight}}px;width: {{slideWidth}}px;height:{{slideHeight}}px;" animation="{{slideAnimation}}">
  <icon type="cancel" size="30" class="close-btn" color="#FFF" bindtap="slideCloseEvent" />
  <scroll-view scroll-y="true" style="height:100%;width:100%">
    <view class="header">
      <view class="userinfo">
        <image src="../..vatar.png" class="avatar"></image>
        <text>Oopsguy</text>
      </view>
      <view class="toolbar">
        <view class="item">
          <image src="../..v.png"></image>
          <text>收藏</text>
        </view>
        <view class="item" bindtap="toSettingPage">
          <image src="../..setting.png"></image>
          <text>设置</text>
        </view>
      </view>
    </view>
    <view class="menu-item home">
      <text>首页</text>
    </view>
    <view class="slide-inner">
      <block wx:for="{{themeData}}">
        <view class="menu-item" data-id="{{item.id}}" bindtap="toThemePage">
          <text>{{item.name}}</text>
          <image src="../..plus.png"></image>
        </view>
      </block>
    </view>    
  </scroll-view>
</view>

/*slide-menu*/
.slide-mask {
  position: fixed;
  width: 100%;
  top: 0;
  left: 0;
  bottom: 0;
  background: rgba(0, 0, 0, .3);
  z-index: 800;
}
.slide-menu {
  position: fixed;
  top: 0;
  background: #FFF;
  z-index: 900;
}
/*.slide-menu .slide-inner {
  padding: 40rpx;
}*/
.slide-menu .header {
  background: #019DD6;
  height: 200rpx;
  color: #FFF;
  padding: 20rpx 40rpx 0 40rpx;
}

.userinfo {
  height: 80rpx;
  line-height: 80rpx;
  overflow: hidden;
}
.userinfo .avatar {
  width: 80rpx;
  height: 80rpx;
  border-radius: 50%;
  margin-right: 40rpx;
  float: left;
}
.userinfo text {
  float: left;
  font-size: 35rpx;
}
.toolbar {
  height: 100rpx;
  padding-top: 25rpx;
  line-height: 75rpx;
}
.toolbar .item {
  width: 50%;
  display: inline-block;
  overflow: hidden;
  text-align: center
}
.toolbar .item text {
  display: inline-block;
  font-size: 30rpx
}
.toolbar .item image {
  display: inline-block;
  position: relative;
  top: 10rpx;
  margin-right: 10rpx;
  height: 50rpx;
  width: 50rpx;
}

.slide-menu .menu-item {
  position: relative;
  height: 100rpx;
  line-height: 100rpx;
  padding: 0 40rpx;
  font-size: 35rpx;
}
.slide-menu .menu-item:active {
  background: #FAFAFA;
}
.slide-menu .menu-item image {
  position: absolute;
  top: 25rpx;
  right: 40rpx;
  width: 50rpx;
  height: 50rpx;
}
.slide-menu .home {
  color: #019DD6
}

.slide-menu .close-btn {
  position: absolute;
  top: 20rpx;
  right: 40rpx;
  z-index: 1000
}

如上是侧栏的一个简单易行的布局和样式,包含了侧栏中的用户信息块和主题日报列表。当然这些信是得通过js的负网要来获得之。侧栏结构上方来一个class为slide-mask的view,这是一个遮罩元素,当侧栏弹出底时刻,侧栏后止就有同等交汇轻微透明的黑色遮罩。侧栏的冲天与宽窄初始是未肯定的,需要在入页面的时候,马上获得装备信息来获得屏幕的冲天小幅调整侧栏样式。

//获取设备信息,屏幕的高度宽度
onLoad: function() {
    var _this = this;
    wx.getSystemInfo( {
      success: function( res ) {
        _this.setData( {
          screenHeight: res.windowHeight,
          screenWidth: res.windowWidth,
          slideHeight: res.windowHeight,
          slideRight: res.windowWidth,
          slideWidth: res.windowWidth * 0.7
        });
      }
    });
}

宽窄我赢得了屏幕宽度之70%,高度一致。侧栏的弹奏有收回动画使用内置动画API

//侧栏展开
function slideUp() {
  var animation = wx.createAnimation( {
    duration: 600
  });
  this.setData( { maskDisplay: 'block' });
  animation.translateX( '100%' ).step();
  this.setData( {
    slideAnimation: animation.export()
  });
}

//侧栏关闭
function slideDown() {
  var animation = wx.createAnimation( {
    duration: 800
  });
  animation.translateX( '-100%' ).step();
  this.setData( {
    slideAnimation: animation.export()
  });
  this.setData( { maskDisplay: 'none' });
}

侧栏弹来底时刻,遮罩的css属性display设置为block显,侧栏通过css动画transform来纪念右侧移动了100%之升幅translateX(100%),侧栏收回时,动画恰好跟弹有的反倒,其实这些动画片最后还见面翻为css3动辄画属性,这些API只是css3动画片的包装。为了点击遮罩收回侧栏,遮罩的tap事件吧如绑定slideCloseEvent

//浮动球点击 侧栏展开
ballClickEvent: function() {
    slideUp.call( this );
},

//遮罩点击  侧栏关闭
slideCloseEvent: function() {
    slideDown.call( this );
}

图片 3

日报详情页

内容

日报的内容为是极致为难开的,因为接口返回的始末是html…,天呀,是html!小程序肯本就无支持,解析html的进程非常痛苦,因为自身的正则表达式只是几乎也0,解析方案的搜过程很虐心,经典的jQuery是为此非了了,又从不dom,无法用传统的方解析html。尝试了刚则学,但是也是无法在短缺日外掌握,寻找了众解析库,大多是恃浏览器api。不过,上天凡不会见忽视有心人的,哈哈,还是给自己找到了化解方案。幸运的我发觉了一个就此刚刚则编写的跟好像与语法分析方法的xml解析库。这个库房是一个very
good的网友封装的html解析库。详情点击
用Javascript解析html。

由日报详情内容的html部分结构太好,这里仅排有了概括的组织,这个布局是通用的(不过不保证知乎会变动组织,要是变动了,之前的分析可能就是从未因此了…心累)

<div class="question">
    <h2 class="question-title">日本的六大财阀现在怎么样了?</h2>
    <div class="answer">
        <div class="meta">
            ![](//upload-images.jianshu.io/upload_images/2192094-454caed2ef752b34.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
            leon,data analyst
        </div>
        <div class="content">
            <p>“财阀”在战后统称为 Group(集团),是以银行和传统工业企业为核心的松散集合体,由于历史渊源而有相互持股。</p>
            <p>Group 对于当今日本企业的意义在于:</p>
            <p><strong>MUFG:三菱集团、三和集团(みどり会)</strong></p>
            <p><img class="content-image" src="http://pic1.zhimg.com/70/90c319ac7a7b2723e5b511de954f45bc_b.jpg" alt="图片 4"
                /></p>
        </div>
    </div>
    <div class="view-more"><a href="http://www.zhihu.com/question/23907827">查看知乎讨论</a></div>
</div>

外层的.question凡日报中问题答案的展示单位,可能出差不多个,因此需要循环显示。.question-title是题材的题,.meta中是作者的信,img.avatar凡用户之头像,span.author是用户之名目,span.bio兴许而用户之签吧。最难以解析的是.content饱受之情,比较多。但是生个规律就是是还是坐<p>签包裹正在,获取了.content遭到的富有p纵然好取所有的段子。之后再分析出段落中的图样。

以下是详情页的内容展示模版

<view style="padding-bottom: 150rpx;">
    <block wx:for="{{news.body}}">
        <view class="article">
            <view class="title" wx:if="{{item.title && item.title != ''}}">
                <text>{{item.title}}</text>
            </view>
            <view class="author-info" wx:if="{{(item.avatar && item.avatar != '') || (item.author && item.author != '') || (item.bio && item.bio != '')}}">
                <image wx:if="{{item.avatar && item.avatar != ''}}" class="avatar" src="{{item.avatar}}"></image>
                <text wx:if="{{item.author && item.author != ''}}" class="author-name">{{item.author}}</text>
                <text wx:if="{{item.bio && item.bio != ''}}" class="author-mark">,{{item.bio}}</text>
            </view>
            <view class="content" wx:if="{{item.content && item.content.length > 0}}">
                <block wx:for="{{item.content}}" wx:for-item="it">
                    <block wx:if="{{it.type == 'p'}}">
                        <text>{{it.value}}</text>
                    </block>
                    <block wx:elif="{{it.type == 'img'}}">
                        <image mode="aspectFill" src="{{it.value}}" data-src="{{it.value}}" bindtap="previewImgEvent" />
                    </block>
                    <block wx:elif="{{it.type == 'pstrong'}}">
                        <text class="strong">{{it.value}}</text>
                    </block>
                    <block wx:elif="{{it.type == 'pem'}}">
                        <text class="em">{{it.value}}</text>
                    </block>
                    <block wx:elif="{{it.type == 'blockquote'}}">
                        <text class="qoute">{{it.value}}</text>
                    </block>
                    <block wx:else>
                        <text>{{it.value}}</text>
                    </block>
                </block>

            </view>

            <view  class="discuss" wx:if="{{item.more && item.more != ''}}">
                <navigator url="{{item.more}}">查看知乎讨论</navigator>
            </view>
        </view>
    </block>
</view>

得看来模版中的内容显示部分据此了那个多的block加判断语句wx:if wx:elif wx:else。这些还是为索要根据分析后的内容类型来判定用展示什么标签及体制。解析后的内容大概格式是如此的:

{
    body: [
       title: '标题',
       author: '作者', 
       bio: '签名', 
       avatar: '头像', 
       more: '更多地址',
       content: [   //内容
            {
                type: 'p',
                value: '普通段落内容'
            },
            {
                type: 'img',
                value: 'http://xxx.xx.xx/1.jpg'
            },
            {
                type: 'pem',
                value: '...'
            },
            ...
       ]
    ],
    ...
}

需要留意的一点是主题日报有时候回来的html内容是透过unicode编码的未可知直接显示,里边全是近似&#xxxx;的字符,这得独自为主题日报的日报详情解析编码。

重点击主题日报中之列表项是,传递一个标记是主题日报的参数theme

//跳转到日报详情页
toDetailPage: function( e ) {
    var id = e.currentTarget.dataset.id;
    wx.navigateTo( {
      url: '../detail/detail?theme=1&id=' + id
    });
},    

然后在Detail.js的onLoad事件受到受参数

//获取列表残过来的参数 id:日报id, theme:是否是主题日报内容(因为主题日报的内容有些需要单独解析)
onLoad: function( options ) {
    var id = options.id;
    var isTheme = options[ 'theme' ];
    this.setData( { id: id, isTheme: isTheme });
},

此后开始请接口获取日报详情,并因是否是主题日报进行个性化解析

//加载页面相关数据
function loadData() {
  var _this = this;
  var id = this.data.id;
  var isTheme = this.data.isTheme;
  //获取日报详情内容
  _this.setData( { loading: true });
  requests.getNewsDetail( id, ( data ) => {
    data.body = utils.parseStory( data.body, isTheme );
    _this.setData( { news: data, pageShow: 'block' });
    wx.setNavigationBarTitle( { title: data.title }); //设置标题
  }, null, () => {
    _this.setData( { loading: false });
  });
}

如上传入一个isTheme参数上解析方法,解析方法根据这个参数判断是否要开展单独的编码解析。

情节分析的库代码比较多,就未粘发了,可以交git上查看。这里被有分析的包。

var HtmlParser = require( 'htmlParseUtil.js' );

String.prototype.trim = function() {
  return this.replace( /(^\s*)|(\s*$)/g, '' );
}

String.prototype.isEmpty = function() {
  return this.trim() == '';
}

/**
 * 快捷方法 获取HtmlParser对象
 * @param {string} html html文本
 * @return {object} HtmlParser
 */
function $( html ) {
  return new HtmlParser( html );
}

/**
 * 解析story对象的body部分
 * @param {string} html body的html文本
 * @param {boolean} isDecode 是否需要unicode解析
 * @return {object} 解析后的对象
 */
function parseStory( html, isDecode ) {
  var questionArr = $( html ).tag( 'div' ).attr( 'class', 'question' ).match();
  var stories = [];
  var $story;
  if( questionArr ) {
    for( var i = 0, len = questionArr.length;i < len;i++ ) {
      $story = $( questionArr[ i ] );
      stories.push( {
        title: getArrayContent( $story.tag( 'h2' ).attr( 'class', 'question-title' ).match() ),
        avatar: getArrayContent( getArrayContent( $story.tag( 'div' ).attr( 'class', 'meta' ).match() ).jhe_ma( 'img', 'src' ) ),
        author: getArrayContent( $story.tag( 'span' ).attr( 'class', 'author' ).match() ),
        bio: getArrayContent( $story.tag( 'span' ).attr( 'class', 'bio' ).match() ),
        content: parseStoryContent( $story, isDecode ),
        more: getArrayContent( getArrayContent( $( html ).tag( 'div' ).attr( 'class', 'view-more' ).match() ).jhe_ma( 'a', 'href' ) )
      });
    }
  }
  return stories;
}

/**
 * 解析文章内容
 * @param {string} $story htmlparser对象
 * @param {boolean} isDecode 是否需要unicode解析
 * @returb {object} 文章内容对象
 */
function parseStoryContent( $story, isDecode ) {
  var content = [];
  var ps = $story.tag( 'p' ).match();
  var p, strong, img, blockquote, em;
  if( ps ) {
    for( var i = 0, len = ps.length;i < len;i++ ) {
      p = ps[ i ]; //获取<p>的内容
      if( !p || p.isEmpty() )
        continue;

      img = getArrayContent(( p.jhe_ma( 'img', 'src' ) ) );
      strong = getArrayContent( p.jhe_om( 'strong' ) );
      em = getArrayContent( p.jhe_om( 'em' ) );
      blockquote = getArrayContent( p.jhe_om( 'blockquote' ) );

      if( !img.isEmpty() ) { //获取图片
        content.push( { type: 'img', value: img });
      }
      else if( isOnly( p, strong ) ) { //获取加粗段落<p><strong>...</strong></p>
        strong = decodeHtml( strong, isDecode );
        if( !strong.isEmpty() )
          content.push( { type: 'pstrong', value: strong });
      }
      else if( isOnly( p, em ) ) { //获取强调段落 <p><em>...</em></p>
        em = decodeHtml( em, isDecode );
        if( !em.isEmpty() )
          content.push( { type: 'pem', value: em });
      }
      else if( isOnly( p, blockquote ) ) { //获取引用块 <p><blockquote>...</blockquote></p>
        blockquote = decodeHtml( blockquote, isDecode );
        if( !blockquote.isEmpty() )
          content.push( { type: 'blockquote', value: blockquote });
      }
      else { //其他类型 归类为普通段落 ....太累了 不想解析了T_T
        p = decodeHtml( p, isDecode );
        if( !p.isEmpty() )
          content.push( { type: 'p', value: p });
      }
    }
  }
  return content;
}

/**
 * 取出多余或者难以解析的html并且替换转义符号
 */
function decodeHtml( value, isDecode ) {
  if( !value ) return '';
  value = value.replace( /<[^>]+>/g, '' )
    .replace( / /g, ' ' )
    .replace( /“/g, '"' )
    .replace( /”/g, '"' ).replace( /·/g, '·' );
  if( isDecode )
    return decodeUnicode( value.replace( /&#/g, '\\u' ) );
  return value;

}

/**
 * 解析段落的unicode字符,主题日报中的内容又很多是编码过的
 */
function decodeUnicode( str ) {
  var ret = '';
  var splits = str.split( ';' );
  for( let i = 0;i < splits.length;i++ ) {
    ret += spliteDecode( splits[ i ] );
  }
  return ret;
};

/**
 * 解析单个unidecode字符
 */
function spliteDecode( value ) {
  var target = value.match( /\\u\d+/g );
  if( target && target.length > 0 ) { //解析类似  "7.1 \u20998" 参杂其他字符
    target = target[ 0 ];
    var temp = value.replace( target, '{{@}}' );
    target = target.replace( '\\u', '' );
    target = String.fromCharCode( parseInt( target ) );
    return temp.replace( "{{@}}", target );
  } else {
    // value = value.replace( '\\u', '' );
    // return String.fromCharCode( parseInt( value, '10' ) )
    return value;
  }
}

/**
 * 获取数组中的内容(一般为第一个元素)
 * @param {array} arr 内容数组
 * @return {string} 内容
 */
function getArrayContent( arr ) {
  if( !arr || arr.length == 0 ) return '';
  return arr[ 0 ];
}

function isOnly( src, target ) {
  return src.trim() == target;
}

module.exports = {
  parseStory: parseStory
}

代码的剖析过程较散乱,大家可以根据返回的html结构及参照解析库的撰稿人写的文章来解读。

图片 5

图片 6

底层工具栏

相似资讯APP的详情页还发生一个底的工具栏用于操作分享、收藏、评论和点赞等等。为了还好地闯荡动手能力,自己呢举行了一个根工具栏,虽然合法的APP并没这个事物。前面介绍及的取额外信息API在这边虽于运用了。本来好是眷恋管引进人以及评价数显示在底部的图纸右侧上斗,但是由于自家的统筹问题,底部的字号已经是蛮有点了,显示数量的地方的字号又无能够更聊了,这样看起数字显示的地方同图标的轻重缓急几乎等同,很是不对,所以即便不具体数字了。这块还是有不少亟需圆之职能的,比较收藏功能以及是否出评论提示意义等。

<view class="toolbar">
    <view class="inner">
        <view class="item" bindtap="showModalEvent"><image src="../..share.png" /></view>
        <view class="item" bindtap="reloadEvent"><image src="../..refresh.png" /></view>
        <view class="item"><image src="../..vorite.png" /></view>
        <view class="item" data-id="{{id}}" bindtap="toCommentPage"><image src="../..insert_comment.png" />
            <view class="tip"></view>
        </view>
        <view class="item">
            <image src="../..thumb_up_active.png" />
        </view>
    </view>
</view>

根有分享、收藏、评论与点赞按钮,分享肯定是开不了呀,哈哈,但是效果或要有些,就一个modal弹窗,显示各类社交应用的图标就推行呐。

<modal class="modal" confirm-text="取消" no-cancel hidden="{{modalHidden}}" bindconfirm="hideModalEvent">
    <view class="share-list">
        <view class="item"><image src="../..share_qq.png" /></view>
        <view class="item"><image src="../..share_pengyouquan.png" /></view>
        <view class="item"><image src="../..share_qzone.png" /></view>
    </view>
    <view class="share-list" style="margin-top: 20rpx">
        <view class="item"><image src="../..share_weibo.png" /></view>
        <view class="item"><image src="../..share_alipay.png" /></view>
        <view class="item"><image src="../..share_plus.png" /></view>
    </view>
</modal>

model的藏身和显示都是透过hidden属性来支配。

底层工具栏中还有一个按钮是刷新,其实就是是一个又调用接口请求数据的经过而已。

//重新加载数据
reloadEvent: function() {
    loadData.call( this );
},

图片 7

评价页面

评价页面蛮简单的,就是展示品列表,但是要是显示两局部,一部分凡长评,另一样片段是短评。长评跟短评的布局都是通用的。进入到评论页面时,如果长评有数据,则先加载长评,短评需要用户点击短评标题才加载,否则便直接加载短评。这要达成一个端详页面被传递日报的附加信息过来(即长评数量与短评数量)。

前曾经在日报详情页面中,顺便加载了额外的音

//请求日报额外信息(主要是评论数和推荐人数)
requests.getStoryExtraInfo( id, ( data ) => {
    _this.setData( { extraInfo: data });
});

以超反到评论页面的时光顺便传递评论数,这样咱们就算不用在评价页面在请一糟额外信息了。

//跳转到评论页面
toCommentPage: function( e ) {
    var storyId = e.currentTarget.dataset.id;
    var longCommentCount = this.data.extraInfo ? this.data.extraInfo.long_comments : 0; //长评数目
    var shortCommentCount = this.data.extraInfo ? this.data.extraInfo.short_comments : 0; //短评数目
    //跳转到评论页面,并传递评论数目信息
    wx.navigateTo( {
      url: '../comment/comment?lcount=' + longCommentCount + '&scount=' + shortCommentCount + '&id=' + storyId
    });
}

品页面接受参数

//获取传递过来的日报id 和 评论数目
onLoad: function( options ) {
    var storyId = options[ 'id' ];
    var longCommentCount = parseInt( options[ 'lcount' ] );
    var shortCommentCount = parseInt( options[ 'scount' ] );
    this.setData( { storyId: storyId, longCommentCount: longCommentCount, shortCommentCount: shortCommentCount });
},

上页面就加载数据

//加载长评列表
onReady: function() {
    var storyId = this.data.storyId;
    var _this = this;
    this.setData( { loading: true, toastHidden: true });

    //如果长评数量大于0,则加载长评,否则加载短评
    if( this.data.longCommentCount > 0 ) {
      requests.getStoryLongComments( storyId, ( data ) => {
        console.log( data );
        _this.setData( { longCommentData: data.comments });
      }, () => {
        _this.setData( { toastHidden: false, toastMsg: '请求失败' });
      }, () => {
        _this.setData( { loading: false });
      });
    } else {
      loadShortComments.call( this );
    }
}


/**
 * 加载短评列表
 */
function loadShortComments() {
  var storyId = this.data.storyId;
  var _this = this;
  this.setData( { loading: true, toastHidden: true });
  requests.getStoryShortComments( storyId, ( data ) => {
    _this.setData( { shortCommentData: data.comments });
  }, () => {
    _this.setData( { toastHidden: false, toastMsg: '请求失败' });
  }, () => {
    _this.setData( { loading: false });
  });
}

评价页面的展示也是颇之粗略,一下为出长评模版,短评也是千篇一律的,里面的点赞按钮功能木有实现啊。

<view class="headline">
    <text>{{longCommentCount}}条长评</text>
</view>

<view class="common-list">
    <block wx:for="{{longCommentData}}">
        <view class="list-item has-img" data-id="{{item.id}}">
            <view class="content">
                <view class="header">
                    <text class="title">{{item.author}}</text>
                    <image class="vote" src="../..thumb_up.png" />
                </view>
                <text class="body">{{item.content}}</text>
                <text class="bottom">{{item.time}}</text>
            </view>
            <image src="{{item.avatar}}" class="cover" />
        </view>  
    </block>
</view>

图片 8

主题日报

主题日报的样式与首页几乎一模一样,我是拷贝过来修改了一点点(懒)。却变化当多了扳平执主编区域。不过是主编区域并未兑现啊效力,本来是点击主编的头像跳反到主编的个体首页简介,没有时间安排就未做了,这吗是得解析html的(累)。

主题日报列表需要经受一个切实的主题日报id,根据这个id来要接口获取主题日报的日报列表。

//接受主页传递过来的主题日报id
onLoad: function( options ) {
    this.setData( { id: options.themeId });
}

主题日报的请列表方式和主页的列表方式大多,由于并未意识分页参数,主题日报的日报列表这一部分吗未尝分页请求。主题日报的日报详情还是超越反至日报详情页面的。

图片 9

装页面

自想做设置页面里排有之效力,但是工作比较繁忙,还是归属到后面的一揽子计划吧,现阶段只有做了简便的页面布局。

只是还是道一下投机的笔触

  • 夜里模式就是是转使用之显得样式,利用到了css,我们得以当page中放置一个顶层的view来概括打有着的wxml元素,当切换主题时受页面顶层元素一个主题控制类。


    ….


那么怎么落实换肤立即生效呢?一个页面刚起步是会见透过onLoad、onShow等于,当次不行进的时节页面的onLoad事件就是未会见以次沾,而是触发onShow事件,我们得以经onShow事件来抱有全局缓存中的主题设置。

onShow: function() {
    var app = getApp();
    this.setData({theme: app.globalData.theme});
}

<view class="{{theme}}">
    ...
</view>
  • 清除缓存功能,当然是管临时文件和localStorage中之数据清空。

    clearDataEvent: function() {

    wx.clearStorage(); //清除应用数据
    

    }

  • 运之无图浏览模式和主题的思路差不多,就是判用缓存中的装是否是无图模式,如果是就以情节展示的下加一个断定,根据这价值来判断是否出示图片类型的内容。

    onLoad: function() {

    var app = getApp();
    this.setData({imageMode: app.getImageMode()});
    

    }







图片 10

总结

问题

  • 酷多图显示不出来,不知到是怎,src路径正常,以前的粗豆瓣图书为是发图表列表,但是从未起这种状态。
  • 代码结构比较烂,很多地方都没优化处理,复用率较逊色,待重构。
  • 页面布局有些不客观,尺寸控制的免敷好。
  • 片wxml没有就此模板功能代替重复的渲染工作,达不交复用效果。

闲语

此次编写的小程序用到了充分多知识点,虽然花费了累累时空,但是一切都是非常的值得。编写的历程遭到遇到最特别之困苦就是解析html内容,可以说凡是煞费苦心,哈哈,智商不足啦。很希望会闹网友能够献出重新好的缓解措施。这个略带例子做的比简陋,很多效果没有了落实,跟他人的Android和React仿客户端相比,小巫见大巫啦。还得抽空完成后续之再度多效益。

至目前为止,小程序已经更新了几乎软,支持了ES5/ES6换、下拉刷新事件、上传文件等效果,不过还有多API还非能够以法条件下显得效果。自己当一直开类似于豆瓣图书以及知乎日报等除了网络要外界没什么特别之地方的以为坏,需要尝试新的API来扩张自己之视野,后续打算为不利用及之API进行案例制作。不知不觉已踏上出校园准备生4单月了,很怀念以前的修生活,做过众多案例,但是都无写日记与保存之惯。这次写的篇幅蛮多的,可麻烦很我了。很幸运自己初称工作围绕就会冲击多少序风暴,期待它正式公测!

当前比较完整的效用动态图

图片 11

本次示例的源码地址:

https://github.com/oopsguy/WechatSmallApps

http://git.oschina.net/oopsguy/WechatSmallApps

有些图纸展示不了底题材已赢得修复,感谢热心网友pull request

如果大家喜欢,给个star刺激一下自己,以后会发更好之著作和大家大饱眼福:)

相关文章

网站地图xml地图