在分頁加載商品列表等數據的時候,希望當最后一項達到頁面底部的時候觸發新的一頁數據的加載動作。
貝店是一個社區營銷模式的新型的電商平臺,允許個人開設貝店,例如作者的貝店邀請碼為690638,任何人下載貝店APP,輸入邀請碼就可以使用貝店購物,同時也可以申請自行開店,每一個貝店有一個唯一的編號,比如作者的貝店編號(shop\_id)為682731用于識別分享的商品的來源,點擊以下鏈接可以直達我的貝店
```
https://m.beidian.com/shop/shopkeeper.html?shop\_id=682731
```
以下的示例從貝店加載推薦產品列表。貝店提供推薦列表的API格式如下:
```
https://api.beidian.com/mroute.html?method=beidian.h5.shop.product.list&page=1&shop\_id=682731
```
其中有兩個參數:加載的數據分頁page和貝店的編號shop\_id,返回JSON格式的數據,包含幾個域,如圖所示
這里我們只用到shop\_products字段,該字段包含貝店店主推薦的商品列表,商品的價格以分為單位,shop\_products的格式如下
首先定義必要的字段,全局變量self用來保存vue實例本身的指針。
```
var self;
export default {
data: {
shop_id: 682731,
shop_products: [],
page: 0,
loadMoreText: "加載更多...",
showLoadMore: true,
has_more: true,
},
…
}
```
首先我們假設有更多的數據并嘗試加載第一頁數據,根據返回的字段has\_more判斷是否還有更多的數據,如果有就更新頁碼再次加載數據,并將返回的數據合并到現有列表。
```
onLoad: function ({shop_id}) {
self = this;
//解構啟動參數獲取貝店的id值
if (shop_id) this.shop_id = shop_id;
this.getShopProducts();
},
```
在onLoad事件中,首先將實例指針保存到全局變量self方便在異步請求中訪問實例本身,并且允許通過給頁面傳遞參數shop\_id指定加載的貝店產品列表。這個啟動參數可以在page.json文件中設置,方便調試。
```
"condition": { //模式配置,僅開發期間生效
"current": 0, //當前激活的模式(list 的索引項)
"list": [{
"name": "首頁", //模式名稱
"path": "pages/index/load-more", //啟動頁面,必選
"query": "shop_id=682731" //啟動參數,在頁面的onLoad函數里面得到。
}]
}
```
具體加載產品列表封裝到函數getShopProducts中,首先判斷是否還有數據可以加載,如果有更新新的頁碼,否則直接終止數據加載。
```
if (this.has_more) {
this.page = this.page + 1;
} else {
return;
}
```
然后動態生成請求鏈接,使用uni.request異步請求服務器的數據,成功返回數據設置新的狀態。
```
getShopProducts() {
if (this.has_more) {
this.page = this.page + 1;
} else {
return;
}
const api = 'https://api.beidian.com/mroute.html?method=beidian.h5.shop.product.list';
let url = `${api}&page=${this.page}&shop_id=${this.shop_id}`;
uni.request({
url: url,
success: (res) => {
self.has_more = res.data.has_more;
self.shop_products = self.shop_products.concat(res.data.shop_products)
},
fail: (data, code) => {
console.log('fail' + JSON.stringify(data));
}
});
},
```
并將新的產品列表數據合并到已有數據列表,注意這里的this指針的作用域的更改。
```
success: (res) => {
self.has_more = res.data.has_more;
self.shop_products = self.shop_products.concat(res.data.shop_products)
},
```
當頁面滾動到底部的時候觸發onReachBottom事件,加載新的一頁數據
```
onReachBottom() {
if (this.has_more === false) {
this.loadMoreText = "沒有更多數據了!"
return;
}
this.showLoadMore = true;
//加載下一頁數據
this.getShopProducts();
},
```
詳情頁面的鏈接示例:
```
https://m.beidian.com/detail/detail.html?iid=29064225&shop\_id=682731
```
由于在APP和微信小程序中打開外部鏈接的權限不一樣,所以使用條件編譯分別處理對應的邏輯。貝店網頁版代碼限制WebView模式下無法打開詳情頁,因此調用外部瀏覽器打開,
```
// 貝店程序限制,WebView模式下無法打開詳情頁,調用外部瀏覽器打開
// #ifdef APP-PLUS
plus.runtime.openURL(api)
// #endif
```
在微信小程序中無法直接打開外部鏈接,因此我們將鏈接復制到剪貼板,提示用戶在瀏覽器中打開。
```
// #ifdef MP-WEIXIN
uni.setClipboardData({
data: api,
success: function () {
uni.showModal({
title: '提示',
content: '鏈接已復制,請在瀏覽器中訪問',
showCancel: false,
});
}
})
// #endif
```
*****
文件pages/load-more.vue
```
<template>
<view class="uni-flex uni-flex-item uni-column">
<view class="uni-list" id="tuijian-product">
<view class="uni-list-cell" hover-class="uni-list-cell-hover" v-for="(value,key) in shop_products" :key="key">
<view class="uni-media-list" v-if="value.stock>0">
<image class="uni-media-list-logo" :src="value.img" @tap="showDetail(value)"></image>
<view class="uni-media-list-body">
<view class="uni-media-list-text-top uni-ellipsis-2" @tap="showDetail(value)">{{value.title}}</view>
<view class="uni-media-list-text-bottom">
<view>
<text class="uni-h6">{{value.seller_count}}</text>
<text> </text>
<text class="uni-h6">庫存剩{{value.stock}}件</text>
</view>
<view>
<text class="product-price">¥{{value.price/100}}</text>
<text> </text>
<text class="product-origin-price">¥{{value.origin_price/100}}</text>
</view>
</view>
</view>
</view>
</view>
</view>
<view class="loadMore" v-if="showLoadMore" @tap="getShopProducts()">{{loadMoreText}}</view>
</view>
</template>
<script>
import {
mapState
} from 'vuex'
var self;
export default {
data: {
shop_id: 682731,
shop_products: [],
page: 0,
loadMoreText: "加載更多...",
showLoadMore: true,
has_more: true,
},
onLoad: function ({
shop_id
}) {
self = this;
//解構啟動參數獲取貝店的id值
if (shop_id) this.shop_id = shop_id;
console.log("shop_id:" + this.shop_id);
this.getShopProducts();
},
onUnload: function () {
this.loadMoreText = "加載更多...";
this.showLoadMore = false;
this.has_more = false;
},
onReachBottom() {
console.log("onReachBottom");
if (this.has_more === false) {
this.loadMoreText = "沒有更多數據了!"
console.log(this.loadMoreText);
return;
}
this.showLoadMore = true;
//加載下一頁數據
this.getShopProducts();
},
methods: {
getShopProducts() {
if (this.has_more) {
this.page = this.page + 1;
} else {
return;
}
const api = 'https://api.beidian.com/mroute.html?method=beidian.h5.shop.product.list';
let url = `${api}&page=${this.page}&shop_id=${this.shop_id}`;
console.log(url)
uni.request({
url: url,
success: (res) => {
self.has_more = res.data.has_more;
self.shop_products = self.shop_products.concat(res.data.shop_products)
},
fail: (data, code) => {
console.log('fail' + JSON.stringify(data));
}
});
},
showDetail(value) {
//https://m.beidian.com/detail/detail.html?iid=29064225&shop_id=682731
let iid = value.iid;
let api = `https://m.beidian.com/detail/detail.html?iid=${iid}&shop_id=${this.shop_id}`;
// 貝店程序限制,WebView模式下無法打開詳情頁,調用外部瀏覽器打開
// #ifdef APP-PLUS
plus.runtime.openURL(api)
// #endif
// 微信小程序中暫時沒有解決方案
// #ifdef MP-WEIXIN
uni.setClipboardData({
data: api,
success: function () {
uni.showModal({
title: '提示',
content: '鏈接已復制,請在瀏覽器中訪問',
showCancel: false,
success: function (res) {
if (res.confirm) {
console.log('用戶點擊確定');
} else if (res.cancel) {
console.log('用戶點擊取消');
}
}
});
}
})
// #endif
},
},
}
</script>
<style>
@import "../../common/uni.css";
page,
view {
display: flex;
}
page {
min-height: 100%;
}
#tuijian-product .uni-media-list-logo {
height: 200px;
width: 200px;
margin-right: 20px;
}
#tuijian-product .uni-media-list-body {
height: 200px;
}
#tuijian-product .uni-media-list-text-top {
font-size: 26px;
}
#tuijian-product .uni-media-list-text-bottom {
flex-direction: column;
}
.product-price {
font-size: 25px;
color: red;
}
.product-origin-price {
font-size: 20px;
text-decoration: line-through;
color: #929292;
}
.loadMore {
text-align: center;
line-height: 100px;
font-size: 30px;
justify-content: center;
align-items: flex-start;
}
</style>
```
- 內容介紹
- EcmaScript基礎
- 快速入門
- 常量與變量
- 字符串
- 函數的基本概念
- 條件判斷
- 數組
- 循環
- while循環
- for循環
- 函數基礎
- 對象
- 對象的方法
- 函數
- 變量作用域
- 箭頭函數
- 閉包
- 高階函數
- map/reduce
- filter
- sort
- Promise
- 基本對象
- Arguments 對象
- 剩余參數
- Map和Set
- Json基礎
- RegExp
- Date
- async
- callback
- promise基礎
- promise-api
- promise鏈
- async-await
- 項目實踐
- 標簽系統
- 遠程API請求
- 面向對象編程
- 創建對象
- 原型繼承
- 項目實踐
- Classes
- 構造函數
- extends
- static
- 項目實踐
- 模塊
- import
- export
- 項目實踐
- 第三方擴展庫
- immutable
- Vue快速入門
- 理解MVVM
- Vue中的MVVM模型
- Webpack+Vue快速入門
- 模板語法
- 計算屬性和偵聽器
- Class 與 Style 綁定
- 條件渲染
- 列表渲染
- 事件處理
- 表單輸入綁定
- 組件基礎
- 組件注冊
- Prop
- 自定義事件
- 插槽
- 混入
- 過濾器
- 項目實踐
- 標簽編輯
- 移動客戶端開發
- uni-app基礎
- 快速入門程序
- 單頁程序
- 底部Tab導航
- Vue語法基礎
- 模版語法
- 計算屬性與偵聽器
- Class與Style綁定
- 樣式與布局
- Box模型
- Flex布局
- 內置指令
- 基本指令
- v-model與表單
- 條件渲染指令
- 列表渲染指令v-for
- 事件與自定義屬性
- 生命周期
- 項目實踐
- 學生實驗
- 貝店商品列表
- 加載更多數據
- 詳情頁面
- 自定義組件
- 內置組件
- 表單組件
- 技術專題
- 狀態管理vuex
- Flyio
- Mockjs
- SCSS
- 條件編譯
- 常用功能實現
- 上拉加載更多數據
- 數據加載綜合案例
- Teaset UI組件庫
- Teaset設計
- Teaset使用基礎
- ts-tag
- ts-badge
- ts-button
- ta-banner
- ts-list
- ts-icon
- ts-load-more
- ts-segmented-control
- 代碼模版
- 項目實踐
- 標簽組件
- 失物招領客戶端原型
- 發布頁面
- 檢索頁面
- 詳情頁面
- 服務端開發技術
- 服務端開發環境配置
- Koajs快速入門
- 快速入門
- 常用Koa中間件介紹
- 文件上傳
- RestfulApi
- 一個復雜的RESTful例子
- 使用Mockjs生成模擬數據
- Thinkjs快速入門
- MVC模式
- Thinkjs介紹
- 快速入門
- RESTful服務
- RBAC案例
- 關聯模型
- 應用開發框架
- 服務端開發
- PC端管理界面開發
- 移動端開發
- 項目實踐
- 失物招領項目
- 移動客戶端UI設計
- 服務端設計
- 數據庫設計
- Event(事件)
- 客戶端設計
- 事件列表頁面
- 發布頁面
- 事件詳情頁面
- API設計
- image
- event
- 微信公眾號開發
- ui設計規范