您好,登錄后才能下訂單哦!
小編給大家分享一下京東優選小程序如何實現,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!
開發工具
微信開發者工具
VS Code
效果速覽
廢話不多說,咱先來搞一波圖片看看,點這里查看更多圖片
項目結構
這個項目我使用的是普通的開發,把所有的數據都放在了json-server中模擬。
可能很多人會覺得很奇怪,但這是因為我發現easy mock的網站經常打不開請求失敗非常的不方便,所以我暫時沒有選擇mock數據,后期有時間我會把數據挪到easy mock上。
|-jd_recommend 項目名 |-api 模擬數據接口 |-db.json 模擬的數據 |-assets 資源文件 |-icons 圖標資源 |-images 圖片資源 |-components 組件模塊 |-navigationBar 自定義導航欄 |-toast 自定義toast |-stepper 有贊vant步進器組件 |-... 其他小程序所需組件 |-pages 項目頁面 |-about 關于頁面 |-account 我的訂單頁面 |-afterMarket 售后類型頁面 |-appointment 我的預約頁面 |-buy 填寫訂單信息頁面 |-commentDetail 評論詳情頁面 |-discount 優惠券頁面 |-explore 發現頁面 |-feedback 反饋頁面 |-fix 售后頁面 |-goodsDetail 值得買優惠詳情頁面 |-index 首頁 |-jd 京東商品詳情頁面 |-login 登錄頁面 |-orderDetail 訂單詳情頁面 |-seller 客服頁面 |-service 退換/售后頁面 |-shopCart 購物車頁面 |-user 個人中心頁面 |-style 公共樣式 |-comment.wxss 評論區樣式 |-goodsCard.wxss 商品卡片樣式 |-nav.wxss 導航欄樣式 |-orderCard.wxss 訂單卡片樣式 |-popright.wxss 篩選框樣式 |-popup.wxss 上拉菜單樣式 |-utils 公共模塊 |-util.js promise封裝接口 app.js 全局js app.json 全局json配置 app.wxss 全局wxss
自定義組件
大部分人寫小程序肯定要涉及修改navigationBar的title,微信小程序開發內置了這個組件,可以直接在app.json中配置。但是,自帶的navigationBar的樣子是固定的,你肯定見過長成下面這樣的navigationBar:
相比平時常見的navigationBar,它左上角多了一個返回主頁的按鈕,這對于有多級頁面的小程序來說是非常必要的,不然訪問的層級太深用戶不知道怎么返回主頁。然而,小程序開發自帶并沒有這個樣子的,好在可以自定義,接下來我們就來自定義一個。
navigationBar
首先,我們構建一下頁面的結構:
<!-- components/navigationBar/index.wxml --> <view class='nav-wrap' style='height: {{height*2 + 20}}px;'> <!-- 導航欄 中間的標題 --> <view class='nav-title' style='line-height: {{height*2 + 44}}px;'>{{navbarData.title}}</view> <view style='display: flex; justify-content: space-around;flex-direction: column'> <!-- 導航欄 左上角的返回按鈕和home按鈕 --> <!-- 其中wx:if='{{navbarData.showCapsule}}' 是控制左上角按鈕的顯示隱藏,首頁不顯示 --> <view class='nav-capsule' style='height: {{height*2 + 44}}px;' wx:if='{{navbarData.showCapsule}}'> <!-- 左上角的返回按鈕,wx:if='{{!share}}'空制返回按鈕顯示 --> <view bindtap='_navback'> <image src='../../assets/icons/back.png' mode='aspectFill' class='back-pre'></image> </view> <view class='navbar-v-line' wx:if='{{!share}}'></view> <view bindtap='_backhome'> <image src='../../assets/icons/back_home.png' mode='aspectFill' class='back-home'></image> </view> </view> </view> </view>
這就是一個很普通的頁面結構,值得注意的是,它的高度是根據獲取的設備的高度來確定的。
接下來又到了切圖仔上線的時候了(誤):
/* components/navigationBar/index.wxss */ /* 頂部要固定定位 標題要居中 自定義按鈕和標題要和右邊微信原生的膠囊上下對齊 */ .nav-wrap { position: fixed; width: 100%; top: 0; background: #fff; color: #000; z-index: 9999999; border-bottom: 1rpx solid #EFEFF4; } /* 標題要居中 */ .nav-title { position: absolute; text-align: center; max-width: 400rpx; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; top: 0; left: 0; right: 0; bottom: 0; margin: auto; font-size: 36rpx; color: #2c2b2b; /* font-weight: 600; */ } .nav-capsule { display: flex; align-items: center; margin-left: 30rpx; width: 140rpx; justify-content: space-between; height: 100%; } .navbar-v-line { width: 1px; height: 32rpx; background-color: #e5e5e5; } .back-pre, .back-home { width: 32rpx; height: 36rpx; margin-top: 4rpx; padding: 10rpx; } .nav-capsule .back-home { width: 36rpx; height: 40rpx; margin-top: 3rpx; }
// components/navigationBar/index.js const app = getApp() Component({ properties: { navbarData: { //navbarData 由父頁面傳遞的數據,變量名字自命名 type: Object, value: {}, // observer: function (newVal, oldVal) { } } }, data: { height: '', //默認值 默認顯示左上角 navbarData: { showCapsule: 1 } }, attached: function () { // 定義導航欄的高度 方便對齊 this.setData({ height: app.globalData.height }) }, methods: { // 返回上一頁面 _navback() { wx.navigateBack() }, //返回到首頁 _backhome() { wx.switchTab({ url: '/pages/index/index', }) } } })
京東優選小程序這里的兩個按鈕都是返回首頁,我在開發的時候覺得不對勁,所以我改過來了。
在這里還去取了一下全局定義的變量,也就是獲取的設備頂部窗口的高度(不同設備窗口高度不一樣,根據這個來設置自定義導航欄的高度),在app.js中要定義一下:
app.js App({ onLaunch: function () { ...... wx.getSystemInfo({ success: (res) => { this.globalData.height = res.statusBarHeight } }) }, globalData: { ... height: 0 } })
記得自定組件的時候一定要在json中寫成自定義組件
// components/navigationBar/index.json { "component": true }
接下來就是調用該組件了
<navigationBar navbar-data='{{navbarData}}'></navigationBar>
別忘了在要引用頁面的json中引入該組件
"usingComponents": { "navigationBar": "../../components/navigationBar/index" }
Toast
Toast同樣也是小程序開發已經做好給你用的了,雖然它可以支持替換里面的圖標,但是你會發現很雞肋的一點是,如果你想顯示兩行文字你就沒辦法做到了。我在開發過程中也搜索過相關的實現方法,找到了大部分是說在要換行的文字后背加上rn就能實現了,但是我自己親測無效,所以實在忍不住也自己做了一個。
<!-- components/toast/index.wxml --> <!-- 距離頂部高度由業務需要動態確定 --> <view class='mask' hidden="{{hide}}" style='top: {{toastData.top}}'> <image class="image" src='../../assets/icons/{{toastData.icon}}.png' mode='aspectFit'></image> <view class="info"> <view class='info1' wx:if="{{toastData.info1 != ''}}">{{toastData.info1}}</view> <view class="info2" wx:if="{{toastData.info2 != ''}}">{{toastData.info2}}</view> </view> </view>
/* components/toast/index.wxss */ .mask { width: 440rpx; height: auto; border-radius: 20rpx; position: fixed; left: 155rpx; z-index: 1000; background: rgba(0, 0, 0, 0.6); text-align: center; padding-bottom: 30rpx; } .image { z-index: 1000; width: 80rpx; height: 80rpx; padding-top: 30rpx; padding-bottom: 20rpx; } .info1, .info2 { color: #ffffff; font-size: 32rpx; } .info { display: flex; flex-direction: column; justify-content: center; align-items: center; }
// components/toast/index.js Component({ properties: { //定義組件屬性 toastData: { //用來顯示提示信息 type: Object, // 類型(必填),目前接受的類型包括:String, Number, Boolean, Object, Array, null(表示任意類型) value: { icon: 'success' } // 屬性初始值(可選),如果未指定則會根據類型選擇一個 }, }, data: { hide: true }, methods: { showToast: function () { let that = this; that.setData({ hide: false }); }, hideToast: function (e) { let that = this; setTimeout(function () { that.setData({ hide: true }); }, 2000); } } })
這里給組件定義了兩個方法,是用來顯示和隱藏Toast的。這里要注意一下,調用給自定義組件定義方法要先在頁面上獲取該組件
<toast id="toast" toast-data="{{toastData}}"></toast>
Page({ data: { toastData: { // toast需要的參數 icon: "success", info1: "加入購物車成功", top: "50%" } }, onReady() { this.toast = this.selectComponent("#toast"); } })
然后在需要觸發Toast的事件中寫上這兩句:
this.toast.showToast() this.toast.hideToast()
功能實現
導航
所謂導航,也是很常見了,就是根據選擇欄目的不同,顯示不同的類別內容。例如:
功能要求:
點擊導航欄目,顯示對應的欄目數據。
如果欄目中沒有東西,要顯示對應的提示信息。
實現它的功能并不難,直接sroll-view往上懟。個人覺得,京東優選在這里有一點不足的地方就是,如果點擊了偏右側的導航欄目的話,導航條不會跟著右移顯示后面的項目,可能它的開發者有不一樣的想法吧。
<view class="navigator"> <scroll-view scroll-x="true" class="nav" scroll-left="{{navScrollLeft}}" scroll-with-animation="{{true}}"> <block wx:for="{{navData}}" wx:for-index="id" wx:for-item="navItem" wx:key="id"> <view class="nav-item {{currentTab == id?'active':''}}" data-name="{{navItem.name}}" data-current="{{id}}" bindtap="switchNav"> {{navItem.name}} </view> </block> </scroll-view> </view>
通過js可以實現動態的填放數據,這里設置的current就是當前選擇的欄目,可以根據這個改變樣式等。
switchNav(e) { const cur = e.currentTarget.dataset.current; // Number let currData = [] // console.log(cur.toString()); if (cur === 0) { currData = this.data.goods } else { this.data.goods.forEach(val => { if (val.category === cur.toString()) { currData.push(val) } }) } this.setData({ currentTab: cur, category: cur, currData }); }
如果是要實現點擊之后自動向點擊的方法滑出顯示更多的內容,可以通過動態改變navScrollLeft的值去實現,這里我就不細說了,不過我在實現的時候還是花了一番功夫,實現的不是很好所以就沒有放在代碼里,如果你以后想做出這種效果的導航欄建議去網上搜一搜demo看懂了之后借過來用一用,畢竟傳說程序猿最高的境界是復制粘貼,狗頭(誤)
上拉菜單和篩選框
這兩個比較相似,只是拉出的位置不一樣,這里我就舉一個篩選框的例子,我們先看看它長啥樣:
我們先看看結構,這里我省略了中間的一些內容:
<!-- 點擊篩選彈出的選擇菜單 --> <view class="float {{isRuleTrue?'isRuleShow':'isRuleHide'}}"> <view class="animation-element" animation="{{animation}}"> ...中間自己放的具體內容... <!-- 底部的兩個按鈕 --> <view class='bottom'> <view class="animation-reset" bindtap="reset">重置</view> <view class="animation-button" bindtap="success">確定</view> </view> </view> </view>
/* 篩選彈框 */ /* 彈框的布局 */ .isRuleShow { display: block; } .isRuleHide { display: none; } .float { height: 100%; width: 100%; position: fixed; z-index: 999; top: 0; left: 0; /* 彈出后背景的顏色 */ background-color: rgba(0, 0, 0, 0.5); padding-left: 30rpx; padding-left: 30rpx; /* margin-top:80rpx; */ } .animation-element { width: 600rpx; height: 100%; padding-left: 30rpx; padding-right: 30rpx; background-color: #ffffff; border: 1px solid #f3f0f0; position: absolute; right: -550rpx; box-sizing: border-box; } .bottom { width: 600rpx; height: 110rpx; font-size: 32rpx; padding-top: 55rpx; position: absolute; bottom: 0; left: 0; right: 0; display: flex; } .animation-reset { width: 50%; height: 100%; line-height: 50%; text-align: center; padding-top: 55rpx; border-top: 1px solid #EFEFF4; } .animation-button { width: 50%; height: 100%; line-height: 50%; color: #fff; text-align: center; background-color: #ED7358; padding-top: 55rpx; }
重點是它的顯示和隱藏事件,需要用到animation,如果有不熟悉animation,可以去參考一些資料,或者是官方文檔。同樣,我也去掉了我實現其他業務的一些內容。
showSelect() { // 顯示選擇菜單 this.setData({ isRuleTrue: true }) // 左偏移245 step表示一個動作的開始 this.animation.translate(-245, 0).step() this.setData({ animation: this.animation.export() }) }, success: function () { // 關閉選擇菜單 this.setData({ isRuleTrue: false, selected: true }) this.animation.translate(0, 0).step() this.setData({ animation: this.animation.export() }) },
購物車邏輯
要實現這樣的效果并不困難,需要自己思路清晰,不能被繞進去了。實現加入購物車并不難,細節是購物車圖標右上角的數字要根據加入購物車的數量進行動態的改變,還要注意如果是同一件商品就不需要添加新的,只需要修改原來的數量。
在這里我使用的是小程序的wx.setStorage()實現的:
<view class='bottom'> <view class="animation-reset" bindtap="addCart">加入購物車</view> <view class="animation-button" bindtap="buy">立即購買</view> </view>
addCart() { // 加入購物車 this.setData({ toastData: { // toast需要的參數 icon: "success", info1: "加入購物車成功", top: "50%" } }) this.toast.showToast() this.toast.hideToast() this.hideModal() // 真正實現添加購物車的部分 let cartData = wx.getStorageSync('cart') || []; let count = 0 cartData.map(val => { if (val.title === this.data.currData[0].title && val.type === this.data.choose_value) { val.num += this.data.num count++ // 標記是否有找到相同的商品 } }) if (count === 0) { // 沒找到 添加新的商品信息進購物車 let data = { id: this.data.currData[0]._id, title: this.data.currData[0].title, weight: "0.78kg", type: this.data.choose_value, num: this.data.num, price: this.data.currData[0].plain_price, img: this.data.currData[0].thumb, discount: 20, select: true // 是否選中,方便后續計算總價 } cartData.push(data) } // 刷新購物車圖標上的數量 let allNum = 0 cartData.forEach(val => { allNum += val.num }); this.setData({ allNum }) wx.setStorage({ key: 'cart', data: cartData }) },
這里你可以根據自己的開發來決定方式,如果你使用的是云開發的話,可以選擇把數據存進云數據庫里。
回到頂部
這也是一個老生常談的功能,當你滑到頁面比較后的位置的時候需要快速回頂。這里要記住,用swiper實現。首先是在頁面上擼一個回到頂部的圖標出來:
<!-- 滑動一段距離后顯示返回頂部的按鈕 --> <scroll-view class="bigWrap" scroll-y="true" scroll-top="{{scrollTop}}" bindscroll="scroll" > <view class="goTop" bindtap="goTop" wx:if="{{&& floorstatus}}"> <image class="icon_goTop" src="../../assets/icons/back_to_top.png"></image> </view> </scroll-view>
{{scrollTop}}用來表示滑動的時候距離頂部的位置。它的樣式也很簡單,使用固定定位把它定在屏幕上,這里一定要注意頁面的層級,不然它可能會被其他組件給遮擋掉!
/* 回到頂部 */ .goTop { position: fixed; bottom: 200rpx; right: 20rpx; width: 65rpx; height: 65rpx; border: 1px solid #DDDDDD; border-radius: 50%; background-color: #fff; text-align: center; } .icon_goTop { width: 40rpx; height: 40rpx; padding-top: 12rpx; padding-left: 2rpx; }
goTop(e) { // 回到頂部 this.setData({ scrollTop: 0 }) }
你肯定也注意到了,當滑到了一定距離的時候它才顯示出來,這就要靠swiper綁定的滾動事件了:
scroll(e) { // 滾動事件 // 容器滾動時將此時的滾動距離賦值給 this.data.scrollTop let floorstatus = false if (e.detail.scrollTop > 300) { floorstatus = true } this.setData({ floorstatus }) }
功能大致先說這么一點,可能在大牛看起來都是些很容易不起眼的功能,但是對應我這個初學者來說還是有點困難的,希望如果有大牛看了我的一些功能的實現之后我不會被罵死。
值得注意的一點
做過小程序開發或者是vue等開發的人一定聽過事件冒泡這個名詞:子元素的事件觸發了父元素的事件,例如點擊事件。我就是那個幸運鵝,我在開發的時候就遇到了這個情況。
在購物車中點擊商品可以跳轉商品詳情,但是我一開始把跳轉事件綁定在了每個商品卡片上,這樣就導致了點擊修改商品數量的時候修改了數字但是也會直接跳轉商品詳情,比如下面這樣...
這就很不友好了,用戶體驗很差,關于事件冒泡,微信小程序的解決方法是把bindtap替換成catchtap,這樣可以阻止子元素事件向上冒泡。
然而巧的是,我就是那個最幸運的鵝,步進器我用的是有贊Vant Weapp組件庫里的,我搜索了很多資料都沒有找到有效的解決方案,差點就放棄使用組件庫了,好在最后發現京東優選小程序購物車綁定的跳轉事件是在商品的圖片和標題上。
這一點還是比較重要的,所以大家在開發的時候一定要考慮事件的冒泡,這也是我把它放在最后來寫的原因。
寫在后面
最后,我想說的是小程序開發真的不容易,開發一個好的小程序更是需要考慮性能和用戶體驗的方方面面。當我覺得自己第一個小程序差不多要完工的時候真的要跳起來唱joyful了(誤)。作為一個程序猿真的不容易,難怪是個容易掉發的群體。但好在愿意分享技術的人很多,在這次開發的過程中我也查閱了很多的資料、社區和文檔。小程序的學習我也不會停下腳步,這個項目還有非常多做的不好的地方,我發出來也是希望大家和我進行交流分享,后期我也會繼續完善優化這個小程序項目。希望我的作品可以對那些初學小程序的人有所幫助。
最后附上我的github項目地址:https://github.com/tearill/jd_recommend
以上是“京東優選小程序如何實現”這篇文章的所有內容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內容對大家有所幫助,如果還想學習更多知識,歡迎關注億速云行業資訊頻道!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。