手摸手实现一个电商-微信小程序(上)
前言
基于微信小程序原生框架,常用原生组件及官方API接口实现的小程序版电商,将一步一个脚印带大家从0到1真正实现一个微信小程序。 源码: https://github.com/panzekun/wx_procedure
1、创建小程序项目
打开微信开发者工具,点击新建项目,选择小程序,AppId 开发时候使用测试号,发布生产需要注册
2、初始化项目
3、实现标题以及tabBar底部导航
app.json
{
"pages": [
"pages/home/index",
"pages/category/index",
"pages/cart/index",
"pages/user/index",
"pages/search/index"
],
"window": {
"navigationBarTitleText": "从0到1实战微信小程序",
"navigationBarBackgroundColor": "#eb4450",
"navigationBarTextStyle": "black",
"backgroundColorTop": "#ffffff",
"backgroundColorBottom": "#ffffff",
"backgroundColor": "#ffffff"
},
"tabBar": {
"color": "#333333",
"selectedColor": "#f06c7a",
"borderStyle": "black",
"backgroundColor": "#ffffff",
"list": [{
"pagePath": "pages/home/index",
"iconPath": "static/tabBar/home.png",
"selectedIconPath": "static/tabBar/home-on.png",
"text": "首页"
},
{
"pagePath": "pages/category/index",
"iconPath": "static/tabBar/category.png",
"selectedIconPath": "static/tabBar/category-on.png",
"text": "分类"
},
{
"pagePath": "pages/cart/index",
"iconPath": "static/tabBar/cart.png",
"selectedIconPath": "static/tabBar/cart-on.png",
"text": "购物车"
},
{
"pagePath": "pages/user/index",
"iconPath": "static/tabBar/user.png",
"selectedIconPath": "static/tabBar/user-on.png",
"text": "我的"
}
]
},
"style": "v2",
"sitemapLocation": "sitemap.json"
}
便捷操作
在page写入路径保存,自动生成对应文件夹以及对应的 js json wxml wxss。
4、实现首页
效果图先上
4.1、首页功能分析
代码
<!--pages/home/index.wxml-->
<page class="home">
<!-- 搜索 -->
<view class="shop-input">
<search-input></search-input>
</view>
<!-- 轮播图 -->
<view class="shop-swiper">
<swiper class="" indicator-dots autoplay interval="5000">
<block wx:for="{{swiperList}}" wx:key="{{item}}">
<swiper-item class="" item-id="">
<image class="" src="{{item.url}}" mode="widthFix" />
</swiper-item>
</block>
</swiper>
</view>
<!-- 分类数据 -->
<view class="category-list">
<block wx:for="{{categoryList}}" wx:key="{{item.name}}">
<view class="category" bindtap="hancleClickItem" data-itemList="{{item}}">
<view class="img">
<image src="{{item.image_src}}" mode="widthFix" />
</view>
<view>
<view class="text">{{item.name}}</view>
</view>
</view>
</block>
</view>
<!-- 广告图 -->
<view class="banner">
<MarqueeUp />
<view class="img-box">
<image src="https://ae01.alicdn.com/kf/H5470c644e6454719a610620f1e6f67d9y.jpg" />
</view>
</view>
<!-- 楼层数据 -->
<view class="goods-list">
<view class="title">
<image src="https://ae01.alicdn.com/kf/H551edcdd154a4936a5762971504fb3e2r.jpg"></image>
猜你喜欢
<image src="https://ae01.alicdn.com/kf/H551edcdd154a4936a5762971504fb3e2r.jpg"></image>
</view>
<view class="product-list">
<block wx:for="{{productList}}" wx:key="{{item}}">
<view class="product">
<image src="{{item.img_url}}" />
<view class="name ellipsis">{{ item.name }}</view>
<view class="info">
<view class="price">{{ item.price }}</view>
<view class="slogan">{{ item.slogan }}</view>
</view>
</view>
</block>
</view>
</view>
</page>
4.2、实现搜索自定义组件
1、微信开发者工具创建自定义组件
根目录创建components/SearchInput
文件,SearchInput
右键选择新建Component,自定生成js json wxss wxml
2、代码实现
SearchInput.wxml
<view class="search_row">
<navigator class="search_input" target="" url="../search/index" hover-class="navigator-hover" open-type="navigate">
搜索
</navigator>
</view>
SearchInput.wxss
.search_row {
height: 90rpx;
padding: 15rpx;
background-color: #eb4450;
}
.search_row .search_input {
border-radius: 10rpx;
background-color: #fff;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
}
引入自定义组件
首先要在页面的
json
文件中进行引用声明。还要提供对应的组件名和组件路径
{
"usingComponents": {
"search-input":"/components/SearchInput/SearchInput"
}
}
- usingComponents 引用声明
- search-input 组件名称(
SearchInput
) - "/components/SearchInput/SearchInput" 组件路径
页面中使用
<!--pages/home/index.wxml-->
<page class="home">
<!-- 搜索 -->
<view class="shop-input">
<search-input></search-input>
</view>
</page>
navigator
导航组件 类似超链接标签
属性名类型默认值说明targetStringself在哪个目标上发生跳转,默认当前小程序,可选值self(当前小程序)/miniProgram(其他小程序)urlString当前小程序内的跳转链接open-typeStringnavigate跳转方式
open-type 合法值:
值说明navigate保留当前页面,跳转到应用内的某个页面,但是不能跳到 tabbar 页面redirect关闭当前页面,跳转到应用内的某个页面,但是不允许跳转到 tabbar 页面。switchTab跳转到 tabBar 页面,并关闭其他所有非 tabBar 页面reLaunch关闭所有页面,打开到应用内的某个页面navigateBack关闭当前页面,返回上一页面或多级页面。可通过 getCurrentPages
获取当前的页面栈,决定需要返回几层exit退出小程序,target="miniProgram"时生效
4.3、实现轮播图(swiper)
使用微信内置
swiper
组件实现
swiper
- 滑块视图容器,
- 默认宽度100%,高度150px;
常用属性
属性名类型默认值说明indicator-dotsBooleanfalse是否显示面板指示点indicator-colorColorrgba(0, 0, 0, .3)指示点颜色indicator-active-colorColor#000000当前选中的指示点颜色autoplayBooleanfalse是否自动切换intervalNumber5000自动切换时间间隔circularBooleanfalses是否循环轮播
swiper-item
- 仅可放置在
swiper
组件中,宽高自动设置为100%。
image
微信内置图片组件,默认宽度320px、高度240px,支持懒加载
常用属性:
属性名类型默认值说明srcString图片资源地址modeString'scaleToFill'图片裁剪、缩放的模式lazy-loadBooleanfalse图片懒加载
mode 的合法值
一共有13种模式,9种裁剪模式,4种缩放模式,常用
widthFix
值说明scaleToFill缩放模式,不保持纵横比缩放图片,使图片的宽高完全拉伸至填满 image 元素aspectFit缩放模式,保持纵横比缩放图片,使图片的长边能完全显示出来。也就是说,可以完整地将图片显示出来。aspectFill缩放模式,保持纵横比缩放图片,只保证图片的短边能完全显示出来。也就是说,图片通常只在水平或垂直方向是完整的,另一个方向将会发生截取。widthFix缩放模式,宽度不变,高度自动变化,保持原图宽高比不变heightFix缩放模式,高度不变,宽度自动变化,保持原图宽高比不变top裁剪模式,不缩放图片,只显示图片的顶部区域bottom裁剪模式,不缩放图片,只显示图片的底部区域center裁剪模式,不缩放图片,只显示图片的中间区域left裁剪模式,不缩放图片,只显示图片的左边区域right裁剪模式,不缩放图片,只显示图片的右边区域top left裁剪模式,不缩放图片,只显示图片的左上边区域top right裁剪模式,不缩放图片,只显示图片的右上边区域bottom left裁剪模式,不缩放图片,只显示图片的左下边区域bottom right裁剪模式,不缩放图片,只显示图片的右下边区域
代码实现
<!-- 轮播图 -->
<view class="shop-swiper">
<swiper class="" indicator-dots autoplay interval="5000">
<block wx:for="{{swiperList}}" wx:key="{{item}}">
<swiper-item class="" item-id="">
<image src="{{item.url}}" mode="widthFix" />
</swiper-item>
</block>
</swiper>
</view>
4.4、实现分类/广告图/实现楼层
手动代码实现,直接贴代码了,主要讲下楼层使用了请求获取数据
<!--pages/home/index.wxml-->
<page class="home">
<!-- 搜索 -->
<view class="shop-input">
<search-input></search-input>
</view>
<!-- 轮播图 -->
<view class="shop-swiper">
<swiper class="" indicator-dots autoplay interval="5000">
<block wx:for="{{swiperList}}" wx:key="{{item}}">
<swiper-item item-id="">
<image class="" src="{{item.url}}" mode="widthFix" />
</swiper-item>
</block>
</swiper>
</view>
<!-- 分类数据 -->
<view class="category-list">
<block wx:for="{{categoryList}}" wx:key="{{item.name}}">
<view class="category" bindtap="hancleClickItem" data-itemList="{{item}}">
<view class="img">
<image src="{{item.image_src}}" mode="widthFix" />
</view>
<view>
<view class="text">{{item.name}}</view>
</view>
</view>
</block>
</view>
<!-- 广告图 -->
<view class="banner">
<MarqueeUp />
<view class="img-box">
<image src="https://ae01.alicdn.com/kf/H5470c644e6454719a610620f1e6f67d9y.jpg" />
</view>
</view>
<!-- 楼层数据 -->
<view class="goods-list">
<view class="title">
<image src="https://ae01.alicdn.com/kf/H551edcdd154a4936a5762971504fb3e2r.jpg"></image>
猜你喜欢
<image src="https://ae01.alicdn.com/kf/H551edcdd154a4936a5762971504fb3e2r.jpg"></image>
</view>
<view class="product-list">
<block wx:for="{{productList}}" wx:key="{{item}}">
<view class="product">
<image src="{{item.img_url}}" />
<view class="name ellipsis">{{ item.name }}</view>
<view class="info">
<view class="price">{{ item.price }}</view>
<view class="slogan">{{ item.slogan }}</view>
</view>
</view>
</block>
</view>
</view>
</page>
/* pages/home/index.wxss */
swiper {
height: 260rpx;
}
/* 分类 */
.category-list {
width: 92%;
margin: 0 4%;
padding: 0 0 30rpx 0;
border-bottom: solid 2rpx #f6f6f6;
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
justify-content: space-between;
flex-wrap: wrap;
}
.category-list .category {
width: 25%;
margin-top: 50rpx;
display: flex;
flex-wrap: wrap;
justify-content: center;
}
.category-list .category .img {
width: 100%;
display: flex;
justify-content: center;
}
.category-list .category .img image {
width: 9vw;
height: 9vw;
}
.category-list .category .text {
margin-top: 16rpx;
width: 100%;
display: flex;
justify-content: center;
font-size: 24rpx;
color: #3c3c3c;
}
/* 广告图 */
.banner {
width: 92%;
margin: 40rpx 4%;
}
.banner .img-box {
position: relative;
width: 100%;
height: 150rpx;
margin-top: 20rpx;
border-radius: 80rpx;
}
.banner .img-box::before {
content: "";
position: absolute;
width: calc(100% + 6%);
height: 180rpx;
border-radius: 100rpx;
top: -15rpx;
left: -3%;
z-index: -1;
background-image: linear-gradient(60deg, red, cornflowerblue, yellow, hotpink, salmon, lightgreen, sandybrown, violet);
background-size: 300%;
animation: animate_bg 5s infinite;
}
@keyframes animate_bg {
0%,
100% {
background-position: 0%, 50%;
}
50% {
background-position: 100%, 50%;
}
}
.banner image {
width: 100%;
height: 20vw;
border-radius: 10vw;
box-shadow: 0rpx 5rpx 25rpx rgba(0, 0, 0, 0.3);
}
/* 楼层 */
.goods-list{
width: 100%;
}
.goods-list .title {
width: 100%;
margin-top: 10rpx;
display: flex;
justify-content: center;
align-items: center;
height: 80rpx;
color: #f47825;
font-size: 30rpx;
}
.goods-list .title image {
width: 30rpx;
height: 30rpx;
}
.goods-list .product-list {
width: 100%;
padding: 0 4% 3vw 4%;
display: flex;
justify-content: space-between;
flex-wrap: wrap;
}
.product {
width: 48%;
border-radius: 20rpx;
background-color: #fff;
margin: 0 0 15rpx 0;
box-shadow: 0rpx 5rpx 25rpx rgba(0, 0, 0, 0.1);
}
.product image {
width: 100%;
height: 332rpx;
border-radius: 20rpx 20rpx 0 0;
}
.product .name {
width: 92%;
padding: 10rpx 4%;
font-size: 30rpx;
}
.product .info {
display: flex;
justify-content: space-between;
align-items: flex-end;
width: 92%;
padding: 10rpx 4% 10rpx 4%;
}
.product .info .price {
color: #e65339;
font-size: 30rpx;
font-weight: 600;
}
.product .info .slogan {
color: #807c87;
font-size: 24rpx;
}
wx.request获取后端数据
发起 HTTPS 网络请求,具体请参阅微信文档
request + promise封装
class request {
constructor() {
this._baseUrl = 'https://www.fastmock.site/mock/31875da47f27128e357e3d3e6ac0cc37/api/';
this._token = wx.getStorageSync('token');
// this._header = {'Authorization': 'Bearer ' + token}
}
/**
* GET类型的网络请求
*/
getRequest(url, data, header = this._header) {
return this.requestAll(url, data, header, 'GET')
}
/**
* DELETE类型的网络请求
*/
deleteRequest(url, data, header = this._header) {
return this.requestAll(url, data, header, 'DELETE')
}
/**
* PUT类型的网络请求
*/
putRequest(url, data, header = this._header) {
return this.requestAll(url, data, header, 'PUT')
}
/**
* POST类型的网络请求
*/
postRequest(url, data, header = this._header) {
return this.requestAll(url, data, header, 'POST')
}
/**
* 网络请求
*/
requestAll(url, data, header, method) {
return new Promise((resolve, reject) => {
// 显示正在等待
wx.showLoading({
title: "正在加载中",
mask: false
});
wx.request({
url: this._baseUrl + url,
data: data,
header: header,
method: method,
success: (res => {
let {
data
} = res;
// console.log(res);
if (data.success && data.code == 200) {
//200: 服务端业务处理正常结束
resolve(data.data)
} else {
//其它错误,提示用户错误信息
reject(data.data)
}
}),
fail: (res => {
reject(res)
}),
complete: () => {
// 隐藏正在等待图标
wx.hideLoading();
}
})
})
}
}
export default request
页面使用request
import request from '../../utils/request'; //引入
const api = new request(); //创建实例
使用方法
- Get请求----------------- api.getRequest(
接口路径
,入参(对象)
) - Post请求---------------- api.postRequest( 'home/getProductList',this.QueryParams )
- .....
注意: 开发过程中需要勾选
不校验合法域名、web-view业务域名
等,生产上线登录小程序官方配置对应域名,不然无法发起请求。
4.5 动态获取楼层数据
import request from '../../utils/request';
const api = new request();
Page({
/**
* 页面的初始数据
*/
data: {
swiperList: [{
id: 1,
url: 'https://ae01.alicdn.com/kf/H9450011bbd2e480882aa986c8ceaa312i.jpg'
},
{
id: 2,
url: 'https://ae01.alicdn.com/kf/H1eeb66ae770b44ad9147c1123fb63a32P.jpg'
},
{
id: 3,
url: 'https://ae01.alicdn.com/kf/H0fdf0f299c2a49d5995582f085d24522G.jpg'
},
],
// 分类菜单
categoryList: [{
id: 1,
"name": "办公",
image_src: 'https://ae01.alicdn.com/kf/H974e4de8124b4783b7768d06a2b847ab0.jpg'
},
{
id: 2,
"name": "家电",
image_src: 'https://ae01.alicdn.com/kf/H53dede49e8bd4037b702eeb1f83f1b55M.jpg'
},
{
id: 3,
"name": "服饰",
image_src: 'https://ae01.alicdn.com/kf/H575960420d6b4990960160fba79f443bw.jpg'
},
{
id: 4,
"name": "日用",
image_src: 'https://ae01.alicdn.com/kf/H30b911d8668d46deb9cfed0fd68da1fex.jpg'
},
{
id: 5,
"name": "蔬果",
image_src: 'https://ae01.alicdn.com/kf/Hd3edad8b6c834a23aa214fa83ecca4ddm.jpg'
},
{
id: 6,
"name": "运动",
image_src: 'https://ae01.alicdn.com/kf/Hdb7bb002f13c4b79bc876c28555ca7ceV.jpg'
},
{
id: 7,
"name": "书籍",
image_src: 'https://ae01.alicdn.com/kf/Hc1a477b73bc147728ef29ab6c74af0f0O.jpg'
},
{
id: 8,
"name": "文具",
image_src: 'https://ae01.alicdn.com/kf/H23940f3a1f3145418b439c32521e263b4.jpg'
}
],
//猜你喜欢列表
productList: [],
},
// 定义接口参数对象
QueryParams: {
// 页码
pagenum: 1,
// 页容量
pagesize: 10
},
// 总页码
totalPage: 1,
// 获取商品列表
getProductList() {
// 使用微信小程序内置发送请求的代码来获取数据
api.postRequest(
'home/getProductList',
this.QueryParams
).then(res => {
console.log(res);
// 执行加载下一页的时候 productList 应该是 叠加
// 拼接数组
let productList = [...this.data.productList, ...res.productList];
this.setData({
productList
});
// 计算总页码
this.totalPage = Math.ceil(res.total / this.QueryParams.pagesize);
})
},
// 点击分类事件
hancleClickItem(e) {
let data = e.currentTarget.dataset.itemlist;
wx.showToast({
title: '点击了' + data.name,
icon: 'none'
})
},
/**
* 生命周期函数--监听页面加载
*/
onLoad: function (options) {
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady: function () {
this.getProductList()
},
/**
* 生命周期函数--监听页面显示
*/
onShow: function () {
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide: function () {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload: function () {
},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh: function () {
// wx.showNavigationBarLoading() //在标题栏中显示加载
this.QueryParams.pagenum = 1;
this.setData({
productList: []
});
this.getProductList();
// 结束下拉刷新组件的显示
wx.stopPullDownRefresh();
},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom: function () {
// 判断有没有下一页数据
if (this.QueryParams.pagenum >= this.totalPage) {
// 没有数据
wx.showToast({
title: '没有更多数据了',
icon: 'none',
duration: 1500,
// 蒙版 遮罩层
mask: false
});
} else {
this.QueryParams.pagenum++;
this.getProductList();
}
},
/**
* 用户点击右上角分享
*/
onShareAppMessage: function () {
}
})
4.6 实现下拉刷新上啦加载更多
首页开启下拉刷新
(1)、在index.json 中开启下拉刷新,需要设置backgroundColor,或者是backgroundTextStyle ,因为加载的动画可能会是白色背景,会看不清。
{
"enablePullDownRefresh": true,
"onReachBottomDistance": 50,
"usingComponents": {
"search-input":"/components/SearchInput/SearchInput"
}
}
(2)、在onPullDownRefresh
函数中监听下拉事件,执行刷新方法
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh: function () {
// wx.showNavigationBarLoading() //在标题栏中显示加载
this.QueryParams.pagenum = 1;
this.setData({
productList: []
});
this.getProductList();
// 结束下拉刷新组件的显示
wx.stopPullDownRefresh();
},
开启上拉加载更多
直接监听 onReachBottom
事件,实现加载更多
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom: function () {
// 判断有没有下一页数据
if (this.QueryParams.pagenum >= this.totalPage) {
// 没有数据
wx.showToast({
title: '没有更多数据了',
icon: 'none',
duration: 1500,
// 蒙版 遮罩层
mask: false
});
} else {
this.QueryParams.pagenum++;
this.getProductList();
}
},
今天内容就到此,下一篇继续带撸商品分类以及商品详情的内容,感谢您的阅读以及支持,欢迎在下方留言一起共同学习进步!
看完三件事:
- 点赞 | 你可以点击——>收藏——>退出一气呵成,但别忘了点赞
- 关注 | 点个关注,下次不迷路