小程序开发技术知识微信小程序微信小程序

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

2017-02-21  本文已影响3352人  爱情小傻蛋

微信小程序之入门篇(一)
微信小程序之注册篇(二)
微信小程序之开发初体验(三)——开发工具使用和目录结构
微信小程序之生命周期(四)
微信小程序之数据绑定(五)
微信小程序之触控事件(六)
微信小程序之基础组件篇——视图容器(七)
微信小程序之基础组件篇——基础内容(八)
微信小程序之基础组件篇——表单组件(九)
微信小程序之基础组件篇——导航组件(十)
微信小程序之基础组件篇——媒体组件(十一)
微信小程序之API篇——豆瓣图书搜索(十二)
微信小程序之拓展篇——weui-wxss(十三)

他山之石可以攻玉。

本文作为小程序的最后一篇文章,介绍学习大神的代码实现的豆瓣图书搜索功能。采用了网络请求功能,这也是大部分小程序需要的一个功能模块。
注:豆瓣图书代码忘记博客出处了,求大神原谅。

涉及知识
1、布局、数据绑定、模板
2、调用API——wx.request,豆瓣图书接口
3、跳转豆瓣图书详情页

实现效果如下:

粗鲁的我,直接进入主题吧。
首先介绍目录结构,如下图。

目录结构

接下来贴上所有代码和图片。
app.js

//app.js
App({
  onLaunch: function () {
    //调用API从本地缓存中获取数据
    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{
      //调用登录接口
      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');

/**
 * 网路请求
 */
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');

//刷新动态球颜色
var iconColor = [
  '#42BD56', '#31A040'
];

Page({
  data: {
    scrollHeight: 0, //scroll-view高度
    pageIndex: 0, //页码
    totalRecord: 0, //图书总数
    isInit: true, //是否第一次进入应用
    loadingMore: false, //是否正在加载更多
    footerIconColor: iconColor[0], //下拉刷新球初始颜色
    pageData: [], //图书数据
    searchKey: null //搜索关键字
  },

  //页面显示获取设备屏幕高度,以适配scroll-view组件高度
  onShow: function () {
    wx.getSystemInfo({
      success: (res) => {
        console.log(res)
        this.setData({
          scrollHeight: res.windowHeight - (100 * res.windowWidth / 750) //80为顶部搜索框区域高度 rpx转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);
  },

  //下拉请求数据
  scrollLowerEvent: function (e) {
    if (this.data.loadingMore)
      return;
    requestData.call(this);
  },

  //跳转到详细页面
  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
上一篇下一篇

猜你喜欢

热点阅读