微信小程序之API篇——豆瓣圖書搜索(十二)

微信小程序之入門篇(一)
微信小程序之注冊篇(二)
微信小程序之開發(fā)初體驗(三)——開發(fā)工具使用和目錄結(jié)構(gòu)
微信小程序之生命周期(四)
微信小程序之數(shù)據(jù)綁定(五)
微信小程序之觸控事件(六)
微信小程序之基礎組件篇——視圖容器(七)
微信小程序之基礎組件篇——基礎內(nèi)容(八)
微信小程序之基礎組件篇——表單組件(九)
微信小程序之基礎組件篇——導航組件(十)
微信小程序之基礎組件篇——媒體組件(十一)
微信小程序之API篇——豆瓣圖書搜索(十二)
微信小程序之拓展篇——weui-wxss(十三)

他山之石可以攻玉库继。

本文作為小程序的最后一篇文章贸诚,介紹學習大神的代碼實現(xiàn)的豆瓣圖書搜索功能。采用了網(wǎng)絡請求功能衫冻,這也是大部分小程序需要的一個功能模塊叁幢。
注:豆瓣圖書代碼忘記博客出處了刃泡,求大神原諒锨络。

涉及知識
1潦俺、布局拒课、數(shù)據(jù)綁定、模板
2事示、調(diào)用API——wx.request早像,豆瓣圖書接口
3、跳轉(zhuǎn)豆瓣圖書詳情頁

實現(xiàn)效果如下:

粗魯?shù)奈倚ぞ簦苯舆M入主題吧卢鹦。
首先介紹目錄結(jié)構(gòu),如下圖劝堪。

目錄結(jié)構(gòu)

接下來貼上所有代碼和圖片冀自。
app.js

//app.js
App({
  onLaunch: function () {
    //調(diào)用API從本地緩存中獲取數(shù)據(jù)
    var logs = wx.getStorageSync('logs') || []
    logs.unshift(Date.now())
    wx.setStorageSync('logs', logs)
  },
  getUserInfo:function(cb){
    var that = this
    if(this.globalData.userInfo){
      typeof cb == "function" && cb(this.globalData.userInfo)
    }else{
      //調(diào)用登錄接口
      wx.login({
        success: function () {
          wx.getUserInfo({
            success: function (res) {
              that.globalData.userInfo = res.userInfo
              typeof cb == "function" && cb(that.globalData.userInfo)
            }
          })
        }
      })
    }
  },
  globalData:{
    userInfo:null
  }
})

app.json

{
  "pages": [
    "pages/index/index",
    "pages/detail/detail"
  ],
  "window": {
    "backgroundTextStyle": "light",
    "navigationBarBackgroundColor": "#42BD56",
    "navigationBarTitleText": "豆瓣圖書",
    "navigationBarTextStyle": "white"
  }
}

app.wxss

page {
  font-family: 'microsoft yahei';
  height: 100%;
}

/*common list*/
.list-item {
  position: relative;
  overflow: hidden
}

/*index list*/
.index-list-item {
  background: #FFF;
  padding: 15rpx 30rpx;
  overflow: hidden;
}
.index-list-item:active {
  background: #EEE;
}
.index-list-item .cover {
  float: left;
  width: 120rpx;
  height: 160rpx;
  overflow: hidden
}
.index-list-item .cover image.cover-img {
  width: 120rpx;
  height: 160rpx;
}
.index-list-item .content {
  margin-left: 140rpx;
}
.index-list-item .title {
  font-size: 35rpx;
  display: inline-block;
  height: 90rpx;
  padding-top: 20rpx;
  overflow: hidden;
}
.index-list-item .desc  {
  display: block;
  font-size: 30rpx;
  padding-top: 10rpx;
  color: #AAA;
  white-space:nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.refresh-footer {
  text-align: center;
  padding: 10rpx 0;
}

util.js

function formatTime( date ) {
  var year = date.getFullYear()
  var month = date.getMonth() + 1
  var day = date.getDate()

  var hour = date.getHours()
  var minute = date.getMinutes()
  var second = date.getSeconds()
  
  return [ year, month, day ].map( formatNumber ).join( '/' ) + ' ' + [ hour, minute, second ].map( formatNumber ).join( ':' )
}

function formatNumber( n ) {
  n = n.toString()
  return n[ 1 ] ? n : '0' + n
}

function isFunction( obj ) {
  return typeof obj === 'function';
}

function parseInteger(val) {
  if (isNaN(val))
    return 0;
  return parseInt(val);
}

module.exports = {
  formatTime: formatTime,
  isFunction: isFunction,
  parseInteger: parseInt
}

api.js

const API_BASE = "https://api.douban.com/v2/book";

module.exports = {
  API_BOOK_SEARCH: API_BASE + "/search",
  API_BOOK_DETAIL: API_BASE + "/:id"
}

request.js

var api = require('./api.js');
var utils = require('../utils/util.js');

/**
 * 網(wǎng)路請求
 */
function request(url, data, successCb, errorCb, completeCb) {
    wx.request({
        url: url,
        method: 'GET',
        data: data,
        success: function(res) {
            if (res.statusCode == 200)
                utils.isFunction(successCb) && successCb(res.data);
            else
                console.log('請求異常', res);
        },
        error: function() {
            utils.isFunction(errorCb) && errorCb();
        },
        complete: function() {
            utils.isFunction(completeCb) && completeCb();
        }
    });
}

/**
 * 搜索圖書
 */
function requestSearchBook(data, successCb, errorCb, completeCb) {
    request(api.API_BOOK_SEARCH, data, successCb, errorCb, completeCb);
}

/**
 * 獲取圖書詳細信息
 */
function requestBookDokDetail(id, data, successCb, errorCb, completeCb) {
    request(api.API_BOOK_DETAIL.replace(':id', id), data, successCb, errorCb, completeCb);
}

/**
 * 關鍵字是否是tag
 */
function requestHasTag(tag, successCb, errorCb, completeCb) {
    request(api.API_BOOK_SEARCH, {tag: tag, count: 1}, successCb, errorCb, completeCb);
}

module.exports = {
  requestSearchBook: requestSearchBook,
  requestBookDokDetail: requestBookDokDetail
}

index.js

var requests = require('../../requests/request.js');
var utils = require('../../utils/util.js');

//刷新動態(tài)球顏色
var iconColor = [
  '#42BD56', '#31A040'
];

Page({
  data: {
    scrollHeight: 0, //scroll-view高度
    pageIndex: 0, //頁碼
    totalRecord: 0, //圖書總數(shù)
    isInit: true, //是否第一次進入應用
    loadingMore: false, //是否正在加載更多
    footerIconColor: iconColor[0], //下拉刷新球初始顏色
    pageData: [], //圖書數(shù)據(jù)
    searchKey: null //搜索關鍵字
  },

  //頁面顯示獲取設備屏幕高度,以適配scroll-view組件高度
  onShow: function () {
    wx.getSystemInfo({
      success: (res) => {
        console.log(res)
        this.setData({
          scrollHeight: res.windowHeight - (100 * res.windowWidth / 750) //80為頂部搜索框區(qū)域高度 rpx轉(zhuǎn)px 屏幕寬度/750
        });
      }
    })
  },

  //搜索輸入框輸入取值
  searchInputEvent: function (e) {
    this.setData({ searchKey: e.detail.value });
  },

  //搜索按鈕點擊事件
  searchClickEvent: function (e) {
    if (!this.data.searchKey) {
      return;
    }
    this.setData({ pageIndex: 0, pageData: [] });
    requestData.call(this);
  },

  //下拉請求數(shù)據(jù)
  scrollLowerEvent: function (e) {
    if (this.data.loadingMore)
      return;
    requestData.call(this);
  },

  //跳轉(zhuǎn)到詳細頁面
  toDetailPage: function (e) {
    var bid = e.currentTarget.dataset.bid; //圖書id [data-bid]
    wx.navigateTo({
      url: '../detail/detail?id=' + bid
    });
  }

});

/**
 * 請求圖書信息
 */
function requestData() {
  var _this = this;
  var q = this.data.searchKey;
  var start = this.data.pageIndex;

  this.setData({ loadingMore: true, isInit: false });
  updateRefreshBall.call(this);
  console.log(start)
  requests.requestSearchBook({ q: q, start: start }, (data) => {
    if (data.total == 0) {
      //沒有記錄
      _this.setData({ totalRecord: 0 });
    } else {
      _this.setData({
        pageData: _this.data.pageData.concat(data.books),
        pageIndex: start + 1,
        totalRecord: data.total
      });
    }
  }, () => {
    _this.setData({ totalRecord: 0 });
  }, () => {
    _this.setData({ loadingMore: false });
  });
}

/**
 * 刷新下拉效果變色球
 */
function updateRefreshBall() {
  var cIndex = 0;
  var _this = this;
  var timer = setInterval(function () {
    if (!_this.data['loadingMore']) {
      clearInterval(timer);
    }
    if (cIndex >= iconColor.length)
      cIndex = 0;
    _this.setData({ footerIconColor: iconColor[cIndex++] });
  }, 100);
}

index.wxml

<view class="search-container">
  <input type="text" bindinput="searchInputEvent" placeholder="輸入書名搜索"></input><icon bindtap="searchClickEvent"  type="search" size="20"/>
</view>

<scroll-view scroll-y="true" style="height:{{scrollHeight}}px"
  bindscrolltolower="scrollLowerEvent">

    <view class="logo" wx:if="{{!loadingMore && totalRecord == 0 && !isInit}}">
      <icon type="cancel" color="#B0AAAA" size="50" />
      <view><text>沒有找到相關圖書</text></view>
    </view>

    <view class="logo" wx:if="{{isInit}}">
      <image src="../../images/book.png" />
      <view><text>豆瓣圖書</text></view>
      <text style="font-size:30rpx;">Designed by Oopsguy</text>
    </view>

    <view class="header" wx:if="{{totalRecord > 0 && !isInit}}">
      <text>圖書 {{totalRecord}}本圖書</text>
    </view>

    <view class="common-list" wx:if="{{totalRecord > 0}}">

      <block wx:for="{{pageData}}">
        <view class="list-item" data-bid="{{item.id}}" bindtap="toDetailPage">
          <view class="index-list-item">
            <view class="cover">
              <image class="cover-img" src="{{item.image}}"></image>
            </view>
            <view class="content">
              <view class="title">{{item.title}}</view>
              <text class="desc">{{item.rating.average == '0.0' ? '無' : item.rating.average}}/<block wx:for="{{item.author}}" wx:for-item="it" wx:key="*this">{{it}}/</block>{{item.pubdate}}</text>
            </view>
          </view>
        </view>
      </block>

    </view>

    <view class="refresh-footer" wx:if="{{loadingMore}}">
      <icon type="waiting" size="30" color="{{footerIconColor}}"  />
    </view>

</scroll-view>

index.wxss

page {
  background: #F2F1EE;
}

.logo {
  text-align:center;
  padding-top:50rpx;
  font-size: 40rpx;
  font-weight: bold;
  color: #AAA
}
.logo image {
  width: 250rpx;
  height: 250rpx;
}

.search-container {
  position: relative;
  display: flex;
  justify-content: center;
  align-items: center;
  background-color: #42BD56;
  color: #FFF;
  height: 80rpx;
  padding: 10rpx 20rpx;
  z-index: 100;
}
.search-container input {
  background: #FFF;
  color: #AAA;
  padding: 5px 10rpx;
  height: 40rpx;
  width: 100%;
  border-radius: 8rpx;
}
.search-container icon {
  position: absolute;
  z-index: 10;
  top: 50%;
  margin-top: -20rpx;
  right: 40rpx;
}

/*header*/
.header {
  padding: 20rpx 30rpx;
}
.header text {
  color: #A6A6A6;
  font-size: 35rpx
}

detail.js

var requests = require( '../../requests/request.js' );
var utils = require( '../../utils/util.js' );

Page( {
  data: {
    id: null,
    loadidngHidden: false,
    bookData: null
  },
  onLoad: function( option ) {
    console.log(option)
    this.setData({
      id: option.id
    });
  },
  onReady: function() {
    var id = this.data.id;
    var _this = this;
    requests.requestBookDokDetail(
      id, 
      {fields: 'image,summary,publisher,title,rating,pubdate,author,author_intro,catalog'}, 
      ( data ) => {
        console.log(data)
        _this.setData({
          bookData: data
        });
    }, () => {
      wx.navigateBack();
    }, () => {
      _this.setData( {
        loadidngHidden: true
      });
    });
  }
});

detail.wxml

<view wx:if="{{bookData}}">
    <view class="cover-container">
        <image src="{{bookData.image}}"></image>
    </view>

    <view class="book-meta">
        <view class="meta-info">
            <text class="book-title">{{bookData.title}}</text>
            <text class="other-meta">作者:<block wx:for="{{bookData.author}}" wx:for-item="it" wx:key="*this">{{it}} </block></text>
            <text class="other-meta">出版社:{{bookData.publisher}}</text>
            <text class="other-meta">出版日期:{{bookData.pubdate}}</text>
        </view>
        <view class="range">
            <text class="score">{{bookData.rating.average}}</text>
            <text class="viewers">{{bookData.rating.numRaters ? bookData.rating.numRaters : 0}}參與</text>
        </view>
    </view>

    <view class="book-intro" wx:if="{{bookData.summary}}">
        <view class="intro-header"><text>簡介</text></view>
        <text class="intro-content">{{bookData.summary}}</text>
    </view>

    <view class="book-intro" wx:if="{{bookData.author_intro}}">
        <view class="intro-header"><text>作者</text></view>
        <text class="intro-content">{{bookData.author_intro}}</text>
    </view>
</view>

<loading hidden="{{loadidngHidden}}">
    加載中...
</loading>

detail.wxss

page {
    background: #EEE;
}
.cover-container {
    background: #42BD56;
    text-align: center;
    padding: 50rpx 0;
}
.cover-container image {
    display: inline-block;
    width: 300rpx;
    height: 400rpx;
}

.book-meta {
    position: relative;
    padding: 20rpx;
    overflow: hidden;
}
.book-meta .range {
    position: absolute;
    top: 30rpx;
    right: 20rpx;
    width: 180rpx;
    background: #FFF;
    padding: 20rpx 10rpx;
    text-align: center;
    box-shadow: 2px 2px 10px #CCC;
}
.book-meta .meta-info {
    margin-right: 200rpx;
}
.meta-info text {
    display: block
}
.book-title {
    font-weight: bold;
    font-size: 40rpx;
}
.other-meta {
    padding-top: 10rpx;
    color: #888;
    font-size: 30rpx;
}
.range text {
    display: block;
}
.range .score {
    font-size: 50rpx;
    font-weight: bold;
}
.range .starts {
    font-size: 40rpx;
}
.range .viewers {
    font-size: 30rpx;
}

.book-intro {
    margin-bottom: 40rpx;
    padding: 20rpx;
    background: #FAFAFA;
}
.book-intro .intro-header {
    color: #888;
    font-weight: bold;
    padding: 40rpx 0;
}
.book-intro .intro-content {
    font-size: 35rpx;
    line-height: 2;
    text-align: justify
}

book.png


book.png
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末秒啦,一起剝皮案震驚了整個濱河市熬粗,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌余境,老刑警劉巖驻呐,帶你破解...
    沈念sama閱讀 216,372評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異芳来,居然都是意外死亡含末,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評論 3 392
  • 文/潘曉璐 我一進店門即舌,熙熙樓的掌柜王于貴愁眉苦臉地迎上來佣盒,“玉大人,你說我怎么就攤上這事侥涵≌铀海” “怎么了?”我有些...
    開封第一講書人閱讀 162,415評論 0 353
  • 文/不壞的土叔 我叫張陵芜飘,是天一觀的道長务豺。 經(jīng)常有香客問我,道長嗦明,這世上最難降的妖魔是什么笼沥? 我笑而不...
    開封第一講書人閱讀 58,157評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上奔浅,老公的妹妹穿的比我還像新娘馆纳。我一直安慰自己,他們只是感情好汹桦,可當我...
    茶點故事閱讀 67,171評論 6 388
  • 文/花漫 我一把揭開白布鲁驶。 她就那樣靜靜地躺著,像睡著了一般舞骆。 火紅的嫁衣襯著肌膚如雪钥弯。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,125評論 1 297
  • 那天督禽,我揣著相機與錄音脆霎,去河邊找鬼。 笑死狈惫,一個胖子當著我的面吹牛睛蛛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播胧谈,決...
    沈念sama閱讀 40,028評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼忆肾,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了第岖?” 一聲冷哼從身側(cè)響起难菌,我...
    開封第一講書人閱讀 38,887評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎蔑滓,沒想到半個月后郊酒,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,310評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡键袱,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,533評論 2 332
  • 正文 我和宋清朗相戀三年燎窘,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蹄咖。...
    茶點故事閱讀 39,690評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡褐健,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出澜汤,到底是詐尸還是另有隱情蚜迅,我是刑警寧澤,帶...
    沈念sama閱讀 35,411評論 5 343
  • 正文 年R本政府宣布俊抵,位于F島的核電站谁不,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏徽诲。R本人自食惡果不足惜刹帕,卻給世界環(huán)境...
    茶點故事閱讀 41,004評論 3 325
  • 文/蒙蒙 一吵血、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧偷溺,春花似錦蹋辅、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至砍濒,卻和暖如春淋肾,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背爸邢。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評論 1 268
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留拿愧,地道東北人杠河。 一個月前我還...
    沈念sama閱讀 47,693評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像浇辜,于是被迫代替她去往敵國和親券敌。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,577評論 2 353

推薦閱讀更多精彩內(nèi)容