前言
写在项目前的话:
临近期末了,各学科纷纷结课,随之而来的是各个课设的纷至沓来。俗话说得好,大学生的生活前五个月是温水泡脚,那么最后一个月的就是将前五个月泡脚的水喝下去。作为一个平日里摸鱼摸惯的摸鱼王,课设是最为头疼却又逃不掉的一环。这次的课设,是要模仿一个微信小程序,按照功能的完成度和难度来打分。作为摸鱼王的我,自然而然想起了“看起来简单”又界面好看(主要是靠图片撑起来的颜值)且天天都在用的————古茗点单小程序。而在实现的过程中,我才发现原来看起来简单的古茗小程序实际上细节满满。让我这个摸鱼王叫苦连天。
效果展示(模仿的是上个版本的古茗小程序,因为在模仿小程序中古茗更新过一次
)
首页页面
点单页面
选购页面
订单页面
我的页面
以上就是我们模仿的古茗小程序的主要功能。
一、开始前所需要的准备
-
申请账号:小程序注册,填写信息和提交相应的资料,就可以拥有自己的小程序帐号。
-
开发工具:微信开发工具
-
数据来源:
古茗各种图片来源:Fiddler抓包工具下载
如何使用Fiddler抓取图片教程:Fiddler教程
-
icon:iconfont阿里巴巴矢量图标库
-
部分组件的引用:Vant Weapp
二、初始化一个小程序
- 新建一个文件夹
- 打开小程序,新建一个小程序
- 目录结构,如下所示
WEAPP
| -cloudfunctions
| -miniprogram
| - assets // 静态变量
| -GIF //抓取的GIF
| -icons //源于iconfont的icon图标
| -images//抓取的静态图片
| - componnents //组件
| - panel // 购物车组件
| - database //数据库
| - db.js //存放所有数据的地方
| - pages //各个页面
| -home //主页
| -home.js//js文件
| -home.wxss//css样式文件
| - utils //全局函数
| - app.js // 系统的方法处理文件
| -app.json // 系统全局配置文件
| -app.wxss // 全局的样式表
| -config.json // 域名等配置文件
| -README
复制代码
- 小程序app.json文件配置
"pages": ["pages/home/home",//主页"pages/jump/jump",//shopping假跳转页面"pages/shopping/shopping",//显示各个店面信息页面"pages/order/order",//点单页面"pages/my/my",//我的页面"pages/teaList/teaList",//奶茶点单页面"pages/orderDetail/index"//点单详情页面],"permission": {"scope.userLocation": {"desc": "你的位置信息将用于小程序点单定位"}}, //请求访问用户地理位置(小程序使用位置所必要的)//窗口"window": {"backgroundColor": "#F6F6F6",//窗口背景色"backgroundTextStyle": "dark",// 下拉背景字体、loading 图的样式,仅支持 dark/light"navigationBarBackgroundColor": "#fff",// 顶部tab背景颜色"navigationBarTitleText": "古茗",//顶部显示标题"navigationBarTextStyle": "black"// 导航栏标题颜色,仅支持 black/white},//tab导航条"tabBar": {"selectedColor": "#000000", //选中时样式"borderStyle": "black", // tabbar上边框的颜色, 仅支持 black/white"backgroundColor": "#fff",//背景颜色/** * tab列表,最少2个,最多5个 * text: 对应文案* pagePath: 对应页面路由* iconpath: 图标对应路径* selectIconPath: 选中时的图标对应位置 **/"list": [{"text": "首页","pagePath": "pages/home/home","iconPath": "assets/icons/home.png","selectedIconPath": "assets/icons/home-active.png"},{"text": "点单","pagePath": "pages/jump/jump","iconPath": "assets/icons/shopping.png","selectedIconPath": "assets/icons/shopping-active.png"},{"text": "订单","pagePath": "pages/order/order","iconPath": "assets/icons/order.png","selectedIconPath": "assets/icons/order-active.png"},{"text": "我的","pagePath": "pages/my/my","iconPath": "assets/icons/my.png","selectedIconPath": "assets/icons/my-active.png"}]},//全局调用的Vant组件"usingComponents": {"van-search": "./miniprogram_npm/@vant/weapp/search/index","van-cell": "./miniprogram_npm/@vant/weapp/cell/index"},//默认样式"sitemapLocation": "sitemap.json","style": "v2"
}
复制代码
5.创建两个数据表
注意:一定要创建名字一样的数据表哦
这些弄完就可以开始做一个古茗点单小程序啦!
三、开发时所遇到的问题以及解决方案
1. swiper 组件
在古茗小程序中,很多地方都用到了swiper组件,我们通过查阅文档,也都实现了古茗小程序所展示的效果 以下是一些我学习完后的心得
心得:
- swiper是滑块视图容器。其中只可放置组件,否则会导致未定义的行为。
- autoplay 类型是
boolean
默认值是false
非必填 作用为:是否自动切换我们将它的值改为true,它就会自动播放了
- circular 类型是
boolean
默认值是false
非必填 作用为:是否采用衔接滑动 - 其中的swiper-item 都用占位符绑定了图片的位置和图片id
2.假页面跳转
在开发中,我们发现古茗的shopping页面(即点单页面),当我们点击后,下面并没有出现导航栏,经过查询资料发现,小程序并没有提供让导航栏暂时消失的的方法,在我们摸不着头脑时。我们发现当点击点单页面时,出现页面跳转的效果。因此我们推断,这个选店的页面,可能只是普通的页面并非导航栏页面。(小程序中导航栏页面与普通页面有很多不一样的地方)
解决方案:
只要在导航栏页面中的onShow方法中,添加跳转页面的方法,让导航栏页面加载完后(但实际什么也没有写),自动跳转我们渲染好的页面即可(即真正的点单页面)。解决代码如下:
onShow: function () {wx.navigateTo({url: '/pages/shopping/shopping',})},
复制代码
但随着假页面(跳转页面)的出现,同时也有很多问题浮出了水面。例如wx.navigateBack(跳回上一个或多级页面)方法失效问题。
当点击这个按钮时,要返回上一个页面 即使用wx.navigateBack 返回到上一页面或多级页面。无论设置返回上级多少个页面,他会一直会在此页面不动。
出现问题的原因:
因为我们设置了一个假页面,选择店面的页面上一级是我们设置的假页面,假页面中的onShow方法。当加载完后又会跳到选择门店页面,所以在此时wx.navigateBack就已经完全失效了。
三、wx.navigateBack失效问题
由第二点我们知道,在这种情况下wx.navigateBack已经完全失去他的作用。但在古茗小程序中,这个跳转又是不可缺少的,因为一旦缺少,就会出现严重的页面跳转的BUG。因此我们不得不想出新的解决方案。
解决方案:
经过讨论,我们选择重新写一个方法来实现wx.navigateBack既定的功能。我们发现,如果从主页点击点单页面,再返回,那么就是返回到主页。如果从订单页面点击到点单页面,那么就是返回到订单页面。如果从我的页面点击到点单页面,那么就是返回到我的页面。因此,我们认为在这三个(主页,订单页面,我的页面)onShow方法中,将它们的跳转地址交给Storage(储存器储存起来)。到需要用时再取出来: 解决代码如下:
将地址存进去
onShow: function () {wx.setStorageSync('address', "/pages/home/home");},//此为主页,其他三个页面依此类推
复制代码
选择店面的页面中取出来
backLastPage() {let address = wx.getStorageSync('address')wx.switchTab({url: address,})},
复制代码
此时,wx.navigateBack失效的问题就完全解决了
你看,问题一个一个的来,一个一个解决不就行了。此时的我直接杀神附体,感觉什么问题都能KO
四、自定义顶栏的样式
我们发现,我们在app.json中这样设置,是全局性质的。但我们在选择店面的页面中,它的顶栏和全局的顶栏是不一样的
因此,我们需要diy选择店面的页面顶栏。我们只需要在需要diy的页面中的json的文件中添加"navigationStyle": "custom"
就可以自己用wxss写出一个顶栏了
五、小程序中map组件的运用
腾讯位置服务推出《微信小程序解决方案》,从检索API、基础地图组件、个性化、插件、行业方案等多个层面,为不同场景需求的小程序开发者提供完整的地图能力。 个性化地图在小程序中,是已经给出了map组件的,因此运用起来是非常方便的。 以下,是我们设计的古茗小程序的map组件运用的一些总结:
- longitude 类型为
number
无默认值 是必填的 作用是提供中心经度 - latitude 类型为
number
无默认值 是必填的 作用是提供中心纬度 - scale 类型为
number
默认值为16 不是必填 作用是用鼠标滑轮控制缩放级别,取值范围为3-20 - show-location 类型为
boolean
默认值为false
不是必填的 作用是显示带有方向的当前定位点 - markers 类型为
Array.marker
无默认值 不是必填的 标记点用于在地图上显示标记的位置(即奶茶店的位置)
而markers也是值得研究的
markers:
- id |类型为
number
无默认值 不是必填的 marker点击事件回调会返回此id - latitude | 类型为
number
无默认值 是必填的 浮点数,范围 -90 ~ 90 - longitude | 类型是
number
无默认值 是必填的 浮点数,范围 -180 ~ 180 - iconPath |类型是
string
无默认值 是必填的 支持网络、本地、代码包路径 - width |类型是
number/string
无默认值 不是必填的 默认为图片实际宽度 - height | 类型是
number/string
无默认值 不是必填的 默认为图片实际高度
在我们模仿的古茗小程序中,由于没有店面的实体数据,没办法完全模拟古茗小程序,但为了增加使用者的体验感,我们写了个方法————获取到使用者当前的位置。并以使用者为原点,建立平面坐标系。生成了六个店面(位置随机),但店面的名字都是死数据。最大可能的模仿了古茗小程序的地图功能,实现代码如下:
onLoad: function () {var that = this;wx.getLocation({type: 'gcj02',success: (res) => {var latitude = res.latitudevar longitude = res.longitudethat.setData({latitude: latitude,longitude: longitude,}) }})//获取自身位置
复制代码
虽然这些都没有学过,但还是通过我查阅资料,没想到最终还是被我给解决了,此时浓浓的成就感就油然而生了
六、选择店面时,出现黄色边框以及确认店面提示框。
通过之前的效果展示,我们可以看见在选择店面的页面,我们选择不同的店面时,那店面信息四边会出现黄色的边框来表现已经选中,同时会出现对话框来确定这家店的信息。
要实现这个效果,我们首先得在写wxml时,在可点击的店面(view)中分别绑定data-index
,在最外层bindtap
绑定的方法中获取所点击的view的index
值,再将index
的值赋给我们所设的变量activeNavIndex
然后在wxml中用一个三元运算符进行判断
最后在wxss中写出默认时的样式和点击后的样式
这里有两个值得注意的点:
- activeNavIndex(我们所设置的变量)默认值应该为0,这样当我们不点击时就会默认第一家店出现黄色边框,这样符合我们的设计要求。如果以后需求不同(如都是默认样式又或者是最火爆的店面先出现黄色边框),也可以根据自己的需求设置
- 默认样式中,也要设置边框(这里运用的是border),宽度要和点击后的宽度一致,达到控制变量的效果。但默认的时候,可以将边框设置成白色或者添加透明度,让其“消失”。如果不加默认样式时的边框,那么点击后全局的样式就会改变,非常的别扭。
然后就是点击后出现的对话框了,小程序中给了一个wx.showModal方法
在小程序中,非常方便的写出了对话框,这也是小程序的优点之一,开发非常的方便。
七、选商品页面菜单的开发
其实一开始要我解决这个问题,我是拒绝的,我毕竟可是摸鱼王,这么难的问题怎么可能交给我?我当时本来就想偷偷的划水。但想起我之前碰见的问题都解决的那么漂亮总不能在这半途而废吧。于是我又开始翻翻翻….
后来我发现,这个菜单还是有迹可循的,其中左边菜单栏的开发类似于第六点,也是添加点击事件,然后埋下index…… 不过值得一提的是,这里由于我wxml设计的一些原因。用border-left
达不到我们所想要的效果,于是我们运用了另一个方法来添加点击后的样式。那就是————做了一个伪类,也算是这类问题的另一种解决方案了吧,如果以后碰见需要点击后的样式更花哨,更复杂的需求的话。伪类应该可以给你一个更大的发挥空间。
然后右边的商品栏才是这个问题的难点了。在解决这个问题前,我们得先来学习一下小程序中的scroll。
scroll总结:
- scroll-y 类型为
boolean
默认值为false
不是必填的 作用是允许纵向滚动 - scroll-with-animation 类型为
boolean
默认值为false
不是必填的 作用是在设置滚动条位置时使用动画过渡 - enable-back-to-top 类型为
boolean
默认值为false
不是必填的 作用是iOS点击顶部状态栏、安卓双击标题栏时,滚动条返回顶部,只支持竖向 - scroll-into-view 类型是
string
无默认值 不是必填的 作用是值应为某子元素id(id不能以数字开头)。设置哪个方向可滚动,则在哪个方向滚动到该元素 - scroll-top 类型是
number/string
无默认值 不是必填的 作用是设置竖向滚动条位置 - bindscroll 类型是
eventhandle
无默认值 不是必填的 作用是滚动时触发,event.detail = {scrollLeft, scrollTop, scrollHeight, scrollWidth, deltaX, deltaY}
知道这些基础知识后,我们来走进这个难点。
首先,数据的设计就十分讲究
{title:'当季新品',products:[{title:'生椰拿铁冰激淋',tag:'新品',desc:'珍珠、冰激淋、生椰乳、咖啡液、牛奶生...',price:'16',pic:'https://img.gumingnc.com/newton/20210719053749CST84721626687469148.png?x-oss-process=image/resize,w_204,m_lfit '},{title:'龙井奶芙',tag:'新品',desc:'龙井、鲜奶乳、奶芙顶、坚果碎色清味...',price:'16',pic:'https://img.gumingnc.com/newton/20210719041227CST36011626682347798.png?x-oss-process=image/resize,w_204,m_lfit'},{title:'泡鲁达',tag:'新品',desc:'西米、多肉、红宝石、椰香白糯米、桂花...',price:'17',pic:'https://img.gumingnc.com/newton/20210609082139CST16401623241299533.png?x-oss-process=image/resize,w_204,m_lfit'}]}
复制代码
这是其中一个分类的所有数据。该项目有十一个这样的类
其次是wxml的布局,也很讲究,其布局大概是这样的
了解了结果,然后开始完成其功能了。 首先,因为scroll-into-view接收的是一个id,所以我们给每一个大类埋下一个index,加一个id='position{{index}}',然后我们在左边导航栏的中的点击事件添加一个setData。因为在左边我们也埋下了一个index,所以两边的index是一一对应的。所以有
this.setData({ idx: e, toView: 'position' + e })
复制代码
此时,我们再加上scroll-with-animation,将其的值设为true。此时,我们点击左边的导航栏时,右边就将商品对应上了。 然后,我们在onLoad函数中,将所有的类名带position的view筛选出来(注:一个大类的类名class='scroll-box position')
wx.createSelectorQuery().selectAll('.position').boundingClientRect(function (rects) {that.setData({ positions:rects })}).exec();
复制代码
这里的rects返回的是一个数组,其中包含所有类名带position的节点信息,返回的节点信息中,每个节点的位置用left
、right
、top
、bottom
、width
、height
字段描述。如果提供了callback回调函数,在执行selectQuery的exec方法后,节点信息会在callback中返回。这样,每个大类的信息就被储存起来了。
最后,就是写bindscroll函数了,
如果这个大类距离顶部小于等于滚动条加swiper宽度的话,我们就把她加进arr这个空数组, 最后拿到可视范围的大类之前埋入的index。返回给左边导航栏的idx,这样相对应的左边导航栏就会出现选中样式。如此,一个成功的菜单就完成了!
八、用户的等级制度以及满十赠一活动
这个可以说是比较简单的一个方法了,但却极大的增加了用户的交互。看起来高大上实际却又十分简单。 首先,从用户表中,拿出历史总杯数。也拿出此时用户的VIP等级。
以上,就是这个项目我们所解决的问题了
注意:
此处,由于数组的特性以及没有VIP等级大于等于0,等级和经验条一一对应。即达到一级需要六杯,二级需要十二杯,三级需要18杯,四级需要30杯……如果达到了经验条则等级加一。如果没达到,返回当前等级。
四、总结
本次开发,学到了很多小程序有关的知识。想起一路上碰到的困难,此刻终于也能画上一个圆满的句号。相比起知识储备,解决问题的方法和思维才是更为重要的东西。一路上磕磕碰碰,也算是干了这五个月的洗脚水吧。在开发过程中,我也深刻的感觉到,微信小程序确实对新人很友好,很多方法小程序都给了现成的。非常的方便(手动点赞),小程序真的很适合新人学习前端。可以当作前端的敲门砖哦,毕竟这么菜的我,也能写出这样的小程序(不是自卖自夸)。小程序是真的是你下一点功夫,就能立竿见影的一门学问了。
另外,此次项目中仍有许多功能不够完善,一些细节还可以继续优化,路漫漫其修远兮。希望大家能给出宝贵的意见,如果文章中有错误和不足欢迎批评指正。也欢迎和我在评论区讨论各种有关小程序的问题哦~
如果你觉得这篇文章让你学习到了知识的话,麻烦给个免费的赞呗~,这对创作者真的很重要T.T
另外另外,项目地址也想要个免费的star(づ。◕‿‿◕。)づ★