微信小程序(初学篇)——仿美团外卖

不念不忘少年蓝@ 2022-06-11 01:22 461阅读 0赞

初识小程序,为它的小巧玲珑所吸引,不由得心血来潮。这不正是用户所需要的吗?既方便快捷,又不占手机内存。所以我下定决心一定要做出一个自己的小程序,然后赚钱、赚钱、赚钱…当然现在只是学习阶段,所以先仿一个高端产品来挑战自我吧。说到高端,自然然而的就想到了美团。之后噼里啪啦一顿忙乎,终于做出了一点样子来,希望能为同为小白的同学们提供一点帮助和参考,现在我们进入正题。

开发工具

微信web开发者工具: 官网就可以下载,相信大家早就安装好了吧。
小程序 API: 官网提供的文档,不懂得地方多看两遍基本上就可以解决了。
Easy Mock: 一个能够提供虚拟数据接口的网站,在前端独自开发的情况下,实在是再好不过的工具了。
功能

已经实现的功能:

主界面
订单界面
用户界面
点菜界面
定位界面
未实现的功能:

数都数不清,毕竟大企业的产品,不是说模仿就模仿的,所以只实现了一些主要的功能,和一些能力之内的功能…
项目启动

创建界面

  1. 1."pages":[
  2. 2."pages/home/home",
  3. 3."pages/menu/menu",
  4. 4."pages/location/location",
  5. 5."pages/my/my",
  6. 6."pages/order/order"
  7. 7.],

只要编辑app.js中的pages属性,就会在项目目录下的pages文件夹里自动生成一个文件夹,里面包扩了.wxml 、 .wxss 、 .json 、 .js这样四个文件。wxml就是界面结构文件, .wxss就是样式文件, .js是用来存放js代码并实现界面逻辑的地方,至于 .json就是用来配置页面属性的地方,如:修改标题栏的颜色,和文字。

配置标题栏的样式

  1. 1."window":{
  2. 2."navigationBarTitleText": "美团外卖+",
  3. 3."navigationBarTextStyle": "white",
  4. 4."navigationBarBackgroundColor": "#FFC640"
  5. 5.},

同样是在app.json中配置,其他页面的标题栏都以此为例。

添加底栏

  1. 1."tabBar": {
  2. 2."color": "#272636",
  3. 3."selectedColor": "#FFD161",
  4. 4."backgroundColor": "#fff",
  5. 5."borderStyle": "#a8a8a8",
  6. 6."list": [
  7. 7.{
  8. 8."pagePath": "pages/home/home",
  9. 9."iconPath": "pages/images/home.png",
  10. 10."selectedIconPath": "pages/images/home-selected.png",
  11. 11."color":"white",
  12. 12."text": "首页"
  13. 13.},
  14. 14.{
  15. 15."pagePath": "pages/order/order",
  16. 16."iconPath": "pages/images/order.png",
  17. 17."selectedIconPath": "pages/images/order-selected.png",
  18. 18."text": "订单"
  19. 19.},
  20. 20.{
  21. 21."pagePath": "pages/my/my",
  22. 22."iconPath": "pages/images/my.png",
  23. 23."selectedIconPath": "pages/images/my-selected.png",
  24. 24."text": "我的"
  25. 25.}
  26. 26.]
  27. 27.}

在app.json中编写以上代码,这是小程序自带的功能,只需要照搬照抄就可以了,极其方便,效果如下:

080221v9j3yyi3e2yzuary.gif

image
数据请求

  1. 1./**
  2. 2.* 生命周期函数--监听页面显示
  3. 3.*/
  4. 4.onShow: function () {
  5. 5.var that = this;
  6. 6.wx.request({
  7. 7.url: "https://www.easy-mock.com/mock/596257bc9adc231f357c4664/restaurant/info",//easy-mock生成的虚拟数据接口链接
  8. 8.method: "GET",
  9. 9.success: function (res) {
  10. //成功得到数据,对数据进行处理
  11. 10.that.setData({
  12. //将数据发送到data中
  13. 11.restaurant: res.data.data.restaurant,
  14. 12.location: wx.getStorageSync('location')
  15. 13.})
  16. 14.}
  17. 15.});
  18. 16.},

data是每个页面.js文件中都存在的一个键,用来储存本页面需要用到的数据。具体使用,可在wxml文件中用{ {‘data中的键名’}}的形式调用数据。
虚拟数据大致如下:

  1. 1.{
  2. 2."success": true,
  3. 3."data": {
  4. 4."restaurant": [{
  5. 5."name": "御膳房",
  6. 6."src": "http://i2.kiimg.com/601998/a955867016875a41.jpg",
  7. 7."star": 4.5,
  8. 8."sales": 641,
  9. 9."initial_price": 0,
  10. 10."distribution_price": 0,
  11. 11."distance": "156m",
  12. 12."time": 33
  13. 13.}, {
  14. 14."name": "韩式炸鸡啤酒屋",
  15. 15."star": 4.5,
  16. 16."sales": 731,
  17. 17."src": "http://i4.piimg.com/601998/9ce47f2f19d7717d.jpg",
  18. 18."initial_price": 15,
  19. 19."distribution_price": 0,
  20. 20."distance": "1.3km",
  21. 21."time": 52
  22. 22.},{
  23. 23.//略去
  24. 24.},{
  25. 25.//略去
  26. 26.},{
  27. 27.//...
  28. 28.}]
  29. 29.}
  30. 30.}

主界面
效果图:

080222xf8j9qm806qtfo8f.gif

image
swiper控件应用

首先是两页标签的滑动切换,这里使用的是swiper,它是一款小程序自带的滑块组件,使用掌握起来非常简单,具体代码如下:

  1. 1.<swiper class="categoryList" indicator-dots="true"
  2. 2.indicator-color="rgba(228,228,228,1)"
  3. 3.indicator-active-color="#FECA49">
  4. 4.<block wx:for="{
  5. {categoryList}}" wx:key="">
  6. 5.<swiper-item>
  7. 6.<block wx:for="{
  8. {item}}" wx:key="">
  9. 7.<view class="category-info">
  10. 8.<image src="{
  11. {item.src}}"
  12. 9.class="category-image"></image>
  13. 10.<view class="category-text">{
  14. {item.name}}</view>
  15. 11.</view>
  16. 12.</block>
  17. 13.</swiper-item>
  18. 14.</block>
  19. 15.</swiper>

swiper标签就是滑块组件的主体,表示可以滑动的区域,其中indicator-dots属性是设置设置点是否显示。接下来swiper-item标签在swiper之中表示的是每一个用来作为滑动的页面。这里用包裹着swiper-item表示的是使用categoryList对象数组中数据来循环渲染swiper-item,swiper-item的数量取决于categoryList中有多少组数据。之后在swiper-item中的block标签表示的是在一个页面中用categoryList.item中的数据循环渲染多个类似的标签,这些标签就是效果图中的类别项,总共两页,每页八个。这就是swiper和循环渲染的一些基本用法。

弹出层的实现

  1. 1.<view class="mask"
  2. 2.hidden="{
  3. {mask2Hidden}}" bindtap="mask2Cancel">
  4. 3.<template is="sort_list" data="{
  5. {selected,sortSelected}}"/>
  6. 4.<scroll-view class="filterList" scroll-y="true" >
  7. 5.<view class="filterList-characteristic-title">商家特色</view>
  8. 6.<view class="filterList-characteristic-items">
  9. 7.<block wx:for="{
  10. {characteristicList}}" wx:key="">
  11. 8.<view class="filterList-characteristic-item {
  12. {characteristicSelected[index]==true?'characteristic-selected':''}}"
  13. 9.catchtap="characteristicSelected" data-index="{
  14. {index}}">{
  15. {item.text}}</view>
  16. 10.</block>
  17. 11.</view>
  18. 12.<view class="filterList-discount-title">优惠活动(单选)</view>
  19. 13.<view class="filterList-discount-items">
  20. 14.<block wx:for="{
  21. {discountList}}" wx:key="">
  22. 15.<view class="filterList-discount-item {
  23. {discountSelected==index?'discount-selected':''}}"
  24. 16.catchtap="discountSelected" data-index="{
  25. {index}}">
  26. 17.<text class="filterList-discount-item-icon"
  27. 18.style="background:{
  28. {item.iconColor}}">{
  29. {item.icon}}</text>
  30. 19.{
  31. {item.text}}</view>
  32. 20.</block>
  33. 21.</view>
  34. 22.</scroll-view>
  35. 23.<view class="filterList-footer">
  36. 24.<view class="filterList-footer-delect"
  37. 25.catchtap="clearSelectedNumb">清除筛选</view>
  38. 26.<view class="filterList-footer-finish" bindtap="finish">完成
  39. 27.<view class="filterList-footer-finish-number" hidden="{
  40. {selectedNumb==0}}">{
  41. {selectedNumb}}
  42. 28.</view>
  43. 29.</view>
  44. 30.</view>
  45. 31.</view>

最外层的mask类的view就是一个遮罩层,用来覆盖之前的界面形成遮罩的效果,并在上面显示新的界面也就是弹出层。以上的代码就是效果图中点击筛选按钮所呈现出来的内容了。其中bindtap属性就是点击事件的绑定了,具体的点击事件需要在.js文件中设置。值得一提的是,bindtap事件是会把当前标签受到的点击冒泡给它的父容器,这就相当与同时点击了他的父容器,如果想阻止冒泡的话就需要使用catchtap。

定位界面

先上效果图:

080222yfu5msux91rwo7wz.gif

image

页面结构:

  1. 1.<view class="header">
  2. 2.<view class="search-input">
  3. 3.<input placeholder="请输入收货地址"
  4. 4.bindinput="input"></input>
  5. 5.</view>
  6. 6.<view class="search-btn">搜索</view>
  7. 7.</view>
  8. 8.<view class="result-container" hidden="{
  9. {hidden}}">
  10. 9.<scroll-view scroll-y="true"class="search-result-list" hidden="{
  11. {hidden}}">
  12. 10.<block wx:for="{
  13. {locationList}}" wx:key="">
  14. 11.<view class="search-result" bindtap="onTap" data-key="{
  15. {item.address}}">{
  16. {item.name}}
  17. 12.<view class="search-result-desc">{
  18. {item.address}}</view>
  19. 13.</view>
  20. 14.</block>
  21. 15.</scroll-view>
  22. 16.</view>
  23. 17.<view class="getLocation"
  24. 18.bindtap="getLocation">点击定位当前位置</view>
  25. 19.<view class="addLocation">新增收货地址
  26. 20.<view class="addLocation-icon">+</view>
  27. 21.</view>
  28. 22.<view class="myLocation">我的收货地址</view>
  29. 23.<view class="LocatonInfo"></view>
  30. 24.<view class="userTel"></view>

这个界面主要涉及到的就是弹出层和百度地图API的调用,调用方法可以查看百度地图API,具体点击事件代码如下:

  1. 1.getLocation: function () {
  2. 2.wx.getLocation({
  3. 3.type: 'gcj02',
  4. 4.success: function (res) {
  5. 5.var latitude = res.latitude
  6. 6.var longitude = res.longitude
  7. 7.wx.request({
  8. 8.url: 'http://api.map.baidu.com/geocoder/v2/?ak=btsVVWf0TM1zUBEbzFz6QqWF&coordtype=gcj02ll&location=' + latitude + ',' + longitude + '&output=json&pois=0',
  9. 9.method: "get",
  10. 10.success: function (res) {
  11. 11.console.log(res.data.result.formatted_address)
  12. 12.wx.setStorageSync('location',
  13. 13.res.data.result.formatted_address.substr(res.data.result.formatted_address.indexOf('市') + 1, 10))
  14. 14.}
  15. 15.})
  16. 16.}
  17. 17.})
  18. 18.wx.switchTab({
  19. 19.url: '/pages/home/home'
  20. 20.})
  21. 21.},
  22. 22.input: function (e){
  23. 23.if(e.detail.value){
  24. 24.this.setData({
  25. 25.hidden: false
  26. 26.})
  27. 27.this.search(e.detail.value);
  28. 28.}else{
  29. 29.this.setData({
  30. 30.hidden: true
  31. 31.})
  32. 32.}
  33. 33.},
  34. 34.search: function (text){
  35. 35.var that = this;
  36. 36.wx.request({
  37. 37.url: 'http://api.map.baidu.com/place/v2/search?query=' + text +'&page_size=20&page_num=0&scope=2&region=南昌&output=json&ak=btsVVWf0TM1zUBEbzFz6QqWF',
  38. 38.success: function(res){
  39. 39.console.log(res);
  40. 40.that.setData({
  41. 41.locationList:res.data.results
  42. 42.})
  43. 43.}
  44. 44.})
  45. 45.},

点菜界面
效果图如下:

080223cdimkidpe3de22ex.gif

image

页面结构如下:

  1. 1.<import src = "../common/orderPage.wxml"/>
  2. 2.<import src = "../common/commentPage.wxml"/>
  3. 3.<view class="container" disable-scroll="true">
  4. 4.<view class="header">
  5. 5.<block wx:for="{
  6. {swiperTitle}}" wx:key="">
  7. 5.<view class="title {
  8. {index==currentPage?'selected':''}}" data-index="{
  9. {index}} 6.bindtap="turnPage">{
  10. {item.text}}</view>
  11. 7.</block>
  12. 8.</view>
  13. 9.<swiper class="swiper" current="{
  14. {currentPage}} 10.bindchange="turnTitle">
  15. 11.<swiper-item id="orderPage">
  16. 12.<template is="orderPage" data="{
  17. {menu,selected,howMuch,cost,pullBar}}"/>
  18. 13.</swiper-item>
  19. 14.<swiper-item id="commentPage">
  20. 15.<template is="commentPage" data="{
  21. {categoryList}}"/>
  22. 16.</swiper-item>
  23. 17.<swiper-item id="restaurantPage"></swiper-item>
  24. 18.</swiper>
  25. 19.</view>

菜单页面如下:<template name="orderPage">

2.<scroll-view class="orderPage-sideBar"

3.bindscrolltolower="lower"

  1. scroll-y="true">
  2. <block wx:for="{ {menu}}" wx:key="">
  3. <view class="menuList">
  4. <view class="menu { {index==selected?'selected':''}}"
  5. data-index="{ {index}}"
  6. catchtap="turnMenu">{ {item.typeName}}</view>
  7. </view>
  8. </block>
  9. </scroll-view>
  10. <scroll-view class="foodList" scroll-y="true">
  11. <view class="title">{ {menu[selected].typeName}}</view>
  12. <block wx:for="{ {menu[selected].menuContent}}" wx:key="">
  13. <view class="food">
  14. <image class="img" src="{ {item.src}}"></image>
  15. <view class="food-info">
  16. <view class="name">{ {item.name}}</view>
  17. <view class="sales">月售 { {item.sales}} 赞 { {item.rating}}
  18. </view>
  19. <view class="price">¥ { {item.price}}</view>
  20. </view>
  21. <view class="food-numb">
  22. <view class="remove"
  23. bindtap="removeFromTrolley"
  24. hidden="{ {item.numb==0}}"
  25. data-index="{ {index}}">-</view>
  26. <text class="text"
  27. hidden="{ {item.numb==0}}">{ {item.numb}}</text>
  28. <view class="add"
  29. bindtap="addToTrolley" data-index="{ {index}}">+</view>
  30. </view>
  31. </view>
  32. </block>
  33. </scroll-view>
  34. <view class="footer { {cost!=0?'active':''}}">
  35. <view class="howMuch">
  36. <view class="img" style="background:{ {cost!=0?'#FFD161':'#E7E7E7'}};">
  37. <image src="/pages/images/trolley.png" style="width:60rpx;height:60rpx;"></image>
  38. </view>
  39. <view class="cost" hidden="{ {cost==0}}">¥{ {cost}}</view>
  40. <view class="free">免配送费</view>
  41. </view>
  42. <view class="pay">{ {cost!=0?'去结算':'15元起送'}}</view>
  43. </view>
  44. </template>

tab切换

这个界面最主要的功能就是tab切换,和点菜功能。其中tab切换其实用的还是swiper,因为swiper有一个current属性表示的是swiper当下显示的页面的序号,只需要将tab中被激活的项与swiper的页面互相绑定就可以了,具体代码如下:

  1. 1.turnPage: function (e) {
  2. 2.this.setData({
  3. 3.currentPage: e.currentTarget.dataset.index
  4. 4.})
  5. 5.},
  6. 6.turnTitle: function (e) {
  7. 7.if(e.detail.source=="touch"){
  8. //判断是否是滑动引起的界面切换
  9. 8.this.setData({
  10. 9.currentPage: e.detail.current
  11. 10.})
  12. 11.}
  13. 12.},

当点击title中的项时获取当前序号,再将它赋值给current,当手指滑动swiper时触发bindchange事件,获取当前页面序号,使相应序号的title处于被选中的状态。有一个值得注意的地方是当点击title中的项时也会触发swiper的bindchange事件,但是我们只想让它在滑动swiper时触发,否则就会出现setData过于频繁的警告,所以我们需要在turnTitle中加一段判断语句,判断页面滑动的原因是否为滑动,如果不是则不执行下面的语句。
点菜功能只是数据绑定界面的更加复杂的应用,而且还有许多不妥之处,这里就不作说明了,有兴趣的朋友可以去我的GitHub看详细的代码。

总结

这次项目是本人的第一个微信小程序项目,希望能给大家提供一些参考价值,有什么问题和想说的都可以在评论区告诉我,文章和代码中诸多不妥当的地方也劳烦各位不吝言辞,多多斧正。这样才能帮助我更快的进步,感谢!

项目地址:https://github.com/tzc123/wx_project_meituan

发表评论

表情:
评论列表 (有 0 条评论,461人围观)

还没有评论,来说两句吧...

相关阅读