微信小程序之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