事先声明,这是一个高仿小米Lite的微信小程序。
我呢如今是一个大三快大四的学生,这个小程序花了我很长时间,把能写的功能基本上都写了。我秉着分享开源的心理,尽可能把我写的这个小程序怎么写的,为何这样写,详细的告诉你们。为何是尽可能?这是由于,我不太会说,可能说的不是很清楚,因此只能尽可能.html
ok实现的效果就是这样。前端
├<assets> │ ├<images> ├<components> │ ├<goodList> │ ├<icon> │ ├<tabbar> │ ├<userList> ├<pages> │ ├<cart> │ ├<category> │ ├<deleteGoods> │ ├<find> │ ├<goodDetails> │ ├<index> │ ├<selectGoods> │ ├<user> ├<utils> │ └util.js ├<weui> │ └weui.wxss ├<wxapi> │ ├Api.js │ ├main.js │ └mock.js
对于初学者来讲,可能拿到设计图就立马写,其实这样很很差,写出来的代码会有不少重复的代码,这样不利于以后的维护。因此应该把一些公用的代码封装,以后直接调用就好了,以后维护起来也更加的方便。git
咱们前端想要获取页面的数据,就须要发送HTTP请求后端提供给咱们的API接口,从API接口中获取咱们想要的数据。在微信小程序中,微信官方给咱们提供了一个方法wx.request来请求.github
一个程序,须要的HTTP请求会不少,若是咱们每一个请求都去写一个wx.request,这样写出来的代码,看起来会很冗长,他人看咱们的代码时也会很累,也不利于咱们以后的修改。所以为了代码的整洁,和以后的修改方便。我就把全部的API请求请求封装在wxapi文件目录下。web
// Api.js const banners = 'https://www.easy-mock.com/mock/5cf9c392006feb28c7eedf28/banners' const navdata = 'https://www.easy-mock.com/mock/5cf9c392006feb28c7eedf28/navdata' const goodList = 'https://www.easy-mock.com/mock/5cf9c392006feb28c7eedf28/goodList' const category = 'https://www.easy-mock.com/mock/5cf9c392006feb28c7eedf28/category' const findData = 'https://www.easy-mock.com/mock/5cf9c392006feb28c7eedf28/findData' const userData = 'https://www.easy-mock.com/mock/5cf9c392006feb28c7eedf28/userData' const goodDetail = 'https://www.easy-mock.com/mock/5cf9c392006feb28c7eedf28/goodDetail' const QQ_MAP_KEY = 'NNFBZ-6DRCP-IRLDU-VEQ4F-TXLP2-PFBEN' const MAPURL = 'https://apis.map.qq.com/ws/geocoder/v1/' module.exports = { banners, navdata, goodList, category, findData, userData, goodDetail, QQ_MAP_KEY, MAPURL }
import * as MOCK from "./mock" import * as API from "./Api" const request = (url,mock = true,data) => { let _url = url return new Promise((resolve, reject) => { if (mock) { let res = { statusCode: 200, data: MOCK[_url] } if (res && res.statusCode === 200 && res.data) { resolve(res.data) } else { reject(res) } } else { wx.request({ url: url, data, success(request) { resolve(request.data) }, fail(error) { reject(error) } }) } }); } // showLoading const showLoading = () => { wx.showLoading({ title: '数据加载中', mask: true, }); } // 获取地理位置 const geocoder = (lat, lon) => { return request(API.MAPURL,false,{ location: `${lat},${lon}`, key: API.QQ_MAP_KEY, get_poi: 0 }) } module.exports = { getBanners: () => { // return request('banners') return request(API.banners,false) //首页 banners }, getNavData: () => { // return request('navdata') return request(API.navdata,false) //首页 navdata }, getGoodList: () => { // return request('goodList') return request(API.goodList,false) //首页 商品列表 }, getCategroy: () => { // return request('category') return request(API.category,false) //分类页面 }, getFindData: () => { // return request('findData') return request(API.findData,false) //发现 页面 }, getUserData: () => { // return request('userData') return request(API.userData,false) // 个人 页面 }, getGoodDetail: () => { // return request('goodDetail') return request(API.goodDetail,false) //商品详情 }, showLoading, geocoder }
看到这里,可能就会有一些疑问,为何我在每一个请求后面都加上了一个false?面试
这是由于,我在写这个微信小程序开始时,没有使用easy-mock来模拟http请求数据。我是把假数据都放在mock.js文件中。而后使用 return request('banners')这种方式就能够获取我想要的数据。数据库
API封装完了,该怎么调用呢?我就以首页的banners数据为例json
// index.js const WXAPI = require('../../wxapi/main') onLoad: function (options) { WXAPI.showLoading() this.getBanners() }, getBanners() { WXAPI .getBanners() .then(res => { wx.hideLoading() this.setData({ banners: res.data }) }) },
记住,若是想要发送HTTP请求数据的页面,都必须加上这一句const WXAPI = require('../../wxapi/main')小程序
开始准备OK,如今开始写页面。第一步要写的是tabBar部分。后端
看起来是否是有点奇怪,为何有点透明的感受?由于这个tabBar组件是我本身写的。
通常来将,直接在把tabBar组件写在app.json中,就能够了。
可是我以为不是那么好看,因此就本身撸了一个tabBar组件出来。
写完以后查文档才发现,微信小程序官方提供了自定义tabBar组件的方法,有须要的能够查看微信小程序文档
写完这个组件后我总解了一下,须要注意的问题.
因此我在app.js中存入了一个page属性,来存储当前页面,而后在点击事件goToPage()方法中加入判断去解决。
<!--components/tabbar/tabbar.wxml--> <view class="tabbar"> <!-- 首页 --> <view class="shouye {{on === 'index' ? 'on': ''}}" data-page="index" bindtap="goToPage"> <icon type="shouye" size="42" class="icon" color="{{on === 'index' ? '#f96702': ''}}"/> <text >首页</text> </view> <!-- 分类 --> <view class="fenlei {{on === 'category' ? 'on': ''}}" data-page="category" bindtap="goToPage"> <icon type="classify" size="42" class="icon" color="{{on === 'category' ? '#f96702': ''}}"/> <text >分类</text> </view> <!-- 发现 --> <view class="faxian {{on === 'find' ? 'on': ''}}" data-page="find" bindtap="goToPage"> <icon type="faxian" size="42" class="icon" color="{{on === 'find' ? '#f96702': ''}}"/> <text >发现</text> </view> <!-- 购物车 --> <view class="gouwuche {{on === 'cart' ? 'on': ''}}" data-page="cart" bindtap="goToPage"> <icon type="gouwuche" size="42" class="icon" color="{{on === 'cart' ? '#f96702': ''}}"/> <text >购物车</text> </view> <!-- 个人 --> <view class="wode {{on === 'user' ? 'on': ''}}" data-page="user" bindtap="goToPage"> <icon type="wode" size="42" class="icon" color="{{on === 'user' ? '#f96702': ''}}"/> <text >个人</text> </view> </view>
// components/tabbar/tabbar.js // 全局里面存了一个page 表示当前 路由 const app = getApp(); Component({ /** * 组件的属性列表 */ properties: { // 是否选中 on:{ type: String, value: '' } }, /** * 组件的初始数据 */ data: { }, /** * 组件的方法列表 */ methods: { // 跳转到相应的页面 // 加了一个判断 // 由于若是如今显示的是当前页面就不须要再跳转 goToPage(e) { let page = e.currentTarget.dataset.page || 'user'; if(app.globalData.page === page) { return ; } wx.redirectTo({ url: `/pages/${page}/${page}`, }); app.globalData.page = page; } } })
/* components/tabbar/tabbar.wxss */ .tabbar { width: 100%; height: 100rpx; background-color: #ffffff; display: flex; position: fixed; bottom: 0; font-size: 26rpx; z-index: 99; } .shouye,.fenlei,.faxian,.gouwuche,.wode { flex: 1; display: flex; flex-direction: column; justify-content: center; text-align: center; opacity: 0.5; } .icon { height: 60rpx; } .on { color:#f96702; }
关于如何使用的问题,只须要在,须要tabBar功能的页面底部加上就能够.
好比index页面
<tabbar on="index"></tabbar>
记得在json中引入组件
{ "usingComponents": { "goodList": "../../components/goodList/goodList", "tabbar": "../../components/tabbar/tabbar" } }
tabBar组件中须要图标,我使用的是阿里的图标库
如何使用?
这个组件很简单,定义出来就能够直接使用,而且修改颜色和大小.
<!--components/icon/icon.wxml--> <text class='iconfont icon-{{type}}' style='color:{{color}}; font-size:{{size}}rpx;'></text>
properties: { type: { type: String, value: '' }, color: { type: String, value: '#000000' }, size: { type: Number, value: '45' } },
在首页中存在不少这样的商品列表,一个一个写,这样写出来的代码会致使首页的代码显得不少,而且很差维护,因此我就封装成了一个组件.
<!--components/goodList/goodList.wxml--> <view class="goodList-good"> <!-- 商品的图片 --> <view class="goodList-good-img"> <image src="{{url}}" mode="aspectFill" /> </view> <!-- 商品详细的信息 --> <view class="goodList-good_detail"> <!-- 名称 --> <view class="good_detail_name"> {{name}} </view> <!-- 信息 --> <view class="good_detail_brief"> {{brief}} </view> <!-- 价格 --> <view class="good_detail_price"> <text class="price" >¥{{price}}</text> <text class="oldPrice" style="display:{{oldPrice == '' ? 'none': 'inline'}};">¥{{oldPrice}}</text> </view> </view> </view>
properties: { // 图片连接 url: { type: String, value: '' }, // 名称 name: { type: String, value: '' }, // 信息 brief: { type: String, value: '' }, // 新的价格 price: { type: String, value: '' }, // 旧的价格 oldPrice: { type: String, value: '' } },
/* components/goodList/goodList.wxss */ .goodList-good { position: relative; width: 100%; height: 100%; } .goodList-good-img { width: 100%; height: 312rpx; position: relative; } .goodList-good-img image { width: 100%; height: 100%; } .goodList-good_detail { padding: 26rpx 23rpx; } .good_detail_name { width:100%; overflow: hidden; text-overflow: ellipsis; display: -webkit-box; -webkit-line-clamp: 1; -webkit-box-orient: vertical; } .good_detail_brief { width:100%; overflow: hidden; text-overflow: ellipsis; display: -webkit-box; -webkit-line-clamp: 1; -webkit-box-orient: vertical; font-size: 25rpx; color: #8c8c8c; } .good_detail_price { display: flex; justify-content: flex-start; align-items: flex-end; } .good_detail_price .price { color: #a36d4a; font-size: 28rpx; padding-right: 16rpx; } .good_detail_price .oldPrice { font-size: 24rpx; color: #8c8c8c; text-decoration: line-through; }
大家会发现,个人边框为何那么细。
初学者,可能会说,0.5px边框,不就是border: 0.5px吗,其实这是错的。
浏览器会把任何小于1px的边框都解析成1px,因此你写0.5px其实浏览器会解析成1px,这样就实现不了效果。
其实也很简单,使用伪类去画。
例如,goodList组件的右部边框.
在index页面的html中,我在包裹goodList的view标签的class中加上了rightBorder这个类来表示画出上边框。
<view class="item topBorder rightBorder" data-id="{{item.id}}" bindtap="goDetails"> <goodList url="{{item.url}}" name="{{item.name}}" brief="{{item.brief}}" price="{{item.price}}" oldPrice="{{item.oldPrice}}" ></goodList> </view>
.rightBorder::after { content: ''; position: absolute; height: 200%; width: 1rpx; right: -1rpx; top: 0; transform-origin: 0 0; border-right: 1rpx solid#e0e0e0; transform: scale(.5); z-index: 1; }
其实画0.5px边框就记住。
其它还有一些 细节问题就按照所需写就行。
只是个人页面中的一个列表信息,这个很简单
<!--components/userList/userList.wxml--> <view class="main"> <image src="{{List.img}}" /> <view class="text" > <text >{{List.text}}</text> </view> <view class="state" wx:if="{{List.state !== ''}}"> <text >({{List.state}})</text> </view> </view>
/* components/userList/userList.wxss */ .main { width: 100%; height: 120rpx; display: flex; align-items: center; background-color: #fff; padding: 40rpx; box-sizing: border-box; } .main image { width: 80rpx; height: 80rpx; } .main .text { font-size: 32rpx; padding: 0 10rpx 0 5rpx; } .main .state { font-size: 26rpx; color: #8c8c8c; }
properties: { List: { type: Object } },
我这里引入的是weui组件的搜索样式。如何用?
这是微信小程序提供的一个组件swiper,微信小程序开发者文档
swiper和swiper-item组合起来就能够实现,一些配置信息,请查看官方文档
具体代码
<swiper indicator-dots="{{indicatorDots}}" indicator-active-color="#ffffff" autoplay="{{autoPlay}}" interval="{{interval}}" duration="{{duration}}"> <block wx:for="{{banners}}" wx:key="index"> <swiper-item> <image src="{{item.imgurl}}" mode="aspectFill" class="banner-image" /> </swiper-item> </block> </swiper>
data: { banners: [], indicatorDots: true, autoPlay: true, interval: 3000, duration: 1000, navdata: [], goodList: [], goodListOne: {}, name:'', },
在商城小程序中常常要作一个这样的功能.例如:
功能要求:
功能要求并不难,可是对于初学者而言,可能会有些问题。我就直接说功能该怎么作
首先:分析一下,页面结构是左右布局。而且两边均可以滑动.因此可使用微信给咱们提供的
scroll-view组件.两个组件就能够采用float,分布在左右两边.
<!-- miniprogram/pages/category/category.wxml --> <view class="container"> <!-- 左边商品的标签信息 --> <scroll-view scroll-y scroll-with-animation="{{true}}" class="categroy-left"> <view wx:for="{{categroy}}" wx:key="{{index}}" data-index="{{index}}" bindtap="switchTab" class="cate-list {{curIndex === index ? 'on': ''}}"> {{item[0].name}} </view> </scroll-view> <!-- 右边 标签对应的商品信息 --> <scroll-view scroll-y scroll-into-view="{{toView}}" scroll-with-animation="true" bindscroll="scrollContent" bindscrolltolower="scrollEnd" class="categroy-right"> <block wx:for="{{categroy}}" wx:key="inedx"> <view id="right-list" class="right-list" id="{{index}}"> <view class="right-title"> <text>{{item[0].name}}</text> </view> <view class="right-content"> <block wx:for="{{item}}" wx:key="idex" wx:for-item="product" wx:for-index="idex"> <view class="list-detail" wx:if="{{idex > 0}}"> <image src="{{product.picture}}" /> <view class="detail-name"> <text>{{product.desc}}</text> </view> </view> </block> </view> </view> </block> </scroll-view> </view> <tabbar on="category"></tabbar>
/* miniprogram/pages/category/category.wxss */ /*定义滚动条高宽及背景 高宽分别对应横竖滚动条的尺寸*/ ::-webkit-scrollbar { width: 0px; height: 0px; background-color: pink; } .categroy-left { height: 100%; width: 150rpx; float: left; border-right: 1px solid #ebebeb; box-sizing: border-box; position: fixed; font-size: 30rpx; padding-bottom: 100rpx; box-sizing: border-box; } .categroy-left .cate-list { height: 90rpx; line-height: 90rpx; text-align: center; border: 2px solid #fff; } .categroy-left .cate-list.on { color: #ff4800; font-size: 34rpx; } /* 右边的列表 */ .categroy-right { width: 600rpx; float: right; height: 1334rpx; /* height: 100%; */ padding-bottom: 100rpx; box-sizing: border-box; overflow: hidden; } .right-title { width: 100%; text-align: center; position: relative; padding-top: 30rpx; /* font-size: 30rpx; */ padding-bottom: 30rpx; } .right-title text::before, .right-title text::after { content: ''; position: absolute; width: 60rpx; /* height: 1px; */ top: 50%; border-top: 1px solid #e0e0e0; /* transform: scale(.5); */ } .right-title text::before { left: 30%; } .right-title text::after { right: 30%; } .right-list { /* height: 100%; */ background-color: #fff; } .right-content { width: 100%; height: 100%; display: flex; flex-wrap: wrap; } .right-content .list-detail { flex-shrink: 0; width: 33.3%; height: 100%; font-size: 26rpx; text-align: center; } .right-content .list-detail image { width: 120rpx; height: 120rpx; padding: 10rpx; /* background-color: pink; */ }
这个功能的难点就在于js逻辑
先贴出data中须要的数据
data: { categroy:[], //商品信息 curIndex: 'A', //当前的选中的标签 toView: 'A', //去到的标签 // 存入每一个list的高度叠加 heightArr: [], // 最后一个list,就是最后一个标签的id endActive: 'A' },
这一功能很简单就能实现
只须要在右边scroll-view组件中添加事件scroll-into-view="{{toView}}",toView就是商品显示的id
注意,右边每一个商品信息,页面渲染时必须加上id属性
而后左边的scroll-view组件只需添加一个点击事件去修改toView的值就好了
// 点击左边标签要修改的信息 switchTab(e) { this.setData({ curIndex: e.target.dataset.index, toView: e.target.dataset.index }) },
首先须要计算出 右边商品每一块占据的高度,而且存入数组中,能够放入onReady生命周期中
// 计算出右边每一个商品占据的高度 getPageMessage() { // console.log(4) let self = this let heightArr = [] let h = 0 const query = wx.createSelectorQuery() query.selectAll('.right-list').boundingClientRect() query.exec( res => { res[0].forEach( item => { h += item.height heightArr.push(h) }) self.setData({ heightArr: heightArr }) }) },
在右边的scroll-view组件中加上事件。bindscroll="scrollContent,这是scroll-view提供的事件,在滑动时就会触发.
// 页面滑动时触发 scrollContent(e) { const scrollTop = e.detail.scrollTop const scrollArr = this.data.heightArr const length = scrollArr.length - 1 let endChar = String.fromCharCode(65 + length) let curChar = this.getCurrentIndex(scrollTop) if(this.data.endActive != endChar) { this.setData({ curIndex: curChar }) } else { this.setData({ endActive: 'A' }) } },
// 判断curIndex应该是那个 getCurrentIndex(scrollTop) { const scrollArr = this.data.heightArr let find = scrollArr.findIndex(item => { // 提早10rpx触发效果 return scrollTop < item - 10 }) let curChar = String.fromCharCode(65 + find) return curChar },
以上就能够实现全部的功能要求了。
可是这样的滑动并非很完美,右边滑动到最下面,左边高亮却不是最后一个
相对完美效果就是这样
想要完美一点就要在,右边scroll-view添加一个事件:bindscrolltolower="scrollEnd"
// 页面滑动到底部触发 scrollEnd() { const scrollArr = this.data.heightArr const length = scrollArr.length - 1 let endChar = String.fromCharCode(65 + length) this.setData({ curIndex: endChar, endActive: endChar }) },
想要实现这样的效果并不困难。
逻辑顺序: 首页点击商品信息 -> 商品详情页面显示对应的商品详情信息 -> 购物车页面显示商品购买的商品信息. -> 修改以后,商品详情页面显示修改的信息。
想要实现这样的功能,必须有一个 id 在页面跳转时,传入给跳转的页面,跳转的页面再经过id值获取页面所需的数据
例如:首页 -> 商品详情页
这是一条商品的列表的信息,经过点击事件bindtap="goDetails",跳到对应的页面.
<view class="list"> <block wx:for="{{goodList}}" wx:key="{{item.id}}"> <view class="item topBorder rightBorder" data-id="{{item.id}}" bindtap="goDetails"> <goodList url="{{item.url}}" name="{{item.name}}" brief="{{item.brief}}" price="{{item.price}}" oldPrice="{{item.oldPrice}}" ></goodList> </view> </block> </view>
goDetails(e) { const id = e.currentTarget.dataset.id wx.navigateTo({ url: `/pages/goodDetails/goodDetails?id=${id}`, }); },
商品详情页:
传入的id值能够再onLoad生命周期的options参数上获取
onLoad: function (options) { WXAPI.showLoading() // 获取用户的地址信息 const address = wx.getStorageSync('Address'); this.setData({ id: options.id, address }) this.getGoodDetail() },
我是把数据直接存在本地缓存中(也能够直接存入到云数据库中),使用的是 wx.setStorage()
在本地存入了两个数据,一个是全部购买的商品信息,一个是总的商品购买数量
// 添加到购物车 toAddCart() { let cartData = wx.getStorageSync('goods') || []; let data = { id: this.data.id, name: this.data.goodData.name, memory: this.data.memory, color: this.data.color, price: this.data.price, num: this.data.selectNum, img: this.data.imgSrc, select: true } // wx.removeStorageSync('goods'); cartData.push(data) const allNum =this.getAllNum(cartData) wx.setStorage({ key: 'goods', data: cartData, success: (res) => { console.log(res) let pageIndex = getCurrentPages() let backIndex = pageIndex.length - 2 wx.navigateBack({ delta: backIndex }) }, fail: () => {}, complete: () => {} }); // 存储数量到storage wx.setStorageSync('allNum', allNum); // 写到外面就可让showToast 显示在前一个页面 setTimeout(()=>{ wx.showToast({ title: '已加入购物车', icon: 'success', duration: 2000 }); },500) }, // 获取全部的数量 getAllNum(cartData) { return cartData.reduce((sum, item) => { return sum + (+item.num) },0) },
实现这个功能只须要加一个状态,点击时就修改状态的值,而且修改相关渲染的数据就行。
data: { state: 'details_img', //判断概述 参数 }
<view class="summarize-parameter"> <view class="title"> <view class="summarize" bindtap="changeState"> <text class="{{state === 'details_img'? 'on' : ''}}">概述</text> </view> <view class="parameter" bindtap="changeState"> <text class="{{state === 'param_img'? 'on' : ''}}">参数</text> </view> </view> <view class="state"> <block wx:for="{{state === 'details_img'? details_img : param_img}}" wx:key="index"> <image src="{{item}}" mode="widthFix"/> </block> </view> </view>
// 改变概述和参数 changeState() { let state = this.data.state if(state === 'details_img') { state = 'param_img' } else { state = 'details_img' } this.setData({ state }) },
对比一下上面两张图的区别.
在购物页面中选的商品数量和具体的商品信息,以后跳转回商品详情页面中,对应的数据会修改
<view class="sales" bindtap="goSelectGoods"> <text class="describe">已选</text> <view class="detail detail-change"> {{default_change.name}} {{default_change.memory}} {{default_change.color}} <text >× {{default_change.num}}</text> </view> <view class="right"></view> </view>
<view class="shopping-img" bindtap="goCart"> <icon type="gouwuche" color="#e0e0e0" size="40"/> <text wx:if="{{allNum != 0}}">{{allNum}}</text> </view>
上面时两块修改的html结构
在购物页面点击确认以后,我默认就把商品添加到购物车中,而且位于数据的最后一条
返回商品详情页面时,会从新触发onShow生命周期的函数。
因此我只须要,在onShow中触发修改方法就行.
// 改变默认的版本数据 default_change changeDefauleChange() { const goods = wx.getStorageSync('goods') || []; if(goods.length === 0) { return } const id = this.data.id const default_change = goods[goods.length - 1] let memory = default_change.memory.toString() memory = memory.substring(0,memory.length - 4) default_change.memory = memory this.setData({ default_change }) },
这一个三角形是使用CSS画出来的,并非图标。
使用CSS画出一个三角形,也不是那么困难。使用的是伪类和border属性
.right:before, .right:after { content: ''; position: absolute; top: 35%; right: 0; border-width: 8px; /* transform: translateY(10000rpx); */ border-color: transparent transparent transparent transparent; border-style: solid; transform: rotate(90deg); } .right:before { border-bottom: 8px #aaaaaa solid; } .right:after { right: 1px; /*覆盖并错开1px*/ border-bottom: 8px #fff solid; }
能够直接使用微信小程序提供的picker组件,具体配置请查看文档
先搜索腾讯地图,而且注册开发者信息,申请一个密钥key.
// 获取地理位置 const geocoder = (lat, lon) => { return request(API.MAPURL,false,{ location: `${lat},${lon}`, key: API.QQ_MAP_KEY, get_poi: 0 }) }
而后把密钥复制,由于我封装了全部的API接口。因此使用了 API.QQ_MAP_KEY代替,这里就填写申请的密钥就行.
想要获取用户的经纬度信息,可使用wx.getLocation(),就能够获取用户的经纬度信息了.
getLocation() { wx.getLocation({ type: 'gcj02', success: this.getAddress, fail: () => { this.openLocation() } }) }, getAddress(res) { let { latitude: lat, longitude: lon} = res WXAPI.geocoder(lat, lon) .then(res => { if(res.status !== 0 || !res.result) { return } let {address_component } = res.result const Address = { city: address_component.city, district: address_component.district } wx.setStorageSync("Address", Address); }) },
由于我未让用户受权,因此直接把获取的地理位置,保存在本地storage中.
获取的地理位置,就能够在商品详情页面的送至显示出来
作这个项目的过程来讲是快乐的,没有使用云函数(页面数据并很少,我以为不须要就能够写出来了),因此总共加起来写的时间也很短,不到一个星期就写完了。写完以后的那一刻的成就感也很好。若是你以为这篇文章有帮到你的地方,不妨给个赞吧!同时也很是但愿在下方看到给出的建议!最后奉上源码.若是有须要就自取吧!
最后,说点题外话,由于我是2020届的毕业生,如今面临实习压力。由于须要话时间去看面试题,因此后面写的一段文章,我只是简要的把重要的功能逻辑写了出来,若是写的不清楚,请见谅。