🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
在分页加载商品列表等数据的时候,希望当最后一项达到页面底部的时候触发新的一页数据的加载动作。 贝店是一个社区营销模式的新型的电商平台,允许个人开设贝店,例如作者的贝店邀请码为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>&nbsp;&nbsp;</text> <text class="uni-h6">库存剩{{value.stock}}件</text> </view> <view> <text class="product-price">¥{{value.price/100}}</text> <text>&nbsp;&nbsp;</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> ```