微信小程序之入門篇(一)
微信小程序之注冊篇(二)
微信小程序之開發(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),如下圖劝堪。
接下來貼上所有代碼和圖片冀自。
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