微信小程序,让你一见倾心
image
前言
小程序发布以来,凭借无需安装、用完即走、触手可及、无需注册、无需登录、以及社交裂变等多个优势,一路高歌,变得愈来愈火爆,它革命性的降低了移动应用的开发成本,也正好迎合了用户的使用应用的习惯。小程序魅力如此之大,作为一枚程序猿,我想怎么不自己做一个呢?话不多说,咱撸起袖子就是干
准备工作
项目目录结构
├── assets 用到的一些图标文件 ├── lib ├── weui.wxss 引用了weui├── modules ├── showDetail.js 跳转展示商品详情的公共js文件 ├── showcDetail.js ├── pages 项目的各个页面 ├── index 商城首页 ├── categories 商品分类页 ├── discovery 发现页 ├── channel 商品频道目录 ├── phone 手机频道 ├── tv 电视频道 ├── computer 电脑频道 ├── cart 购物车 ├── mine 个人信息页 ├── goods 商品详情页 ├── selectGoods 商品属性选择页 ├── search 商品搜索页 ├── addr 收货地址页 ├── template 使用到的模版文件 ├── slide 轮播图模版 ├── goods_list 商品展示模版 ├── cover 商品展示模版 ├── util 使用到的工具类 ├── mock.js 项目中使用到的一些数据 ├── app.js 项目逻辑 ├── app.wxss 项目公共样式表 └── app.json 项目公共设置
功能的展示与实现
一、商城首页
image
页面结构分析:
顶部搜索条
这里看上去是一个搜索框,但其实,它要实现的仅仅是个页面跳转功能,只要把它的disabled
设置为true
就可以了,另外要想让它placeholder占位符居中显示的话,微信小程序提供了一个placeholder-class
的属性,通过它可以改变placeholder的样式。轮播图区域
这里微信小程序给我们提供了swiper组件,直接用就可以了。但是轮播图在各个页面都可能存在,只是其中所显示的图片不一样而已,所以使用组件化思想,把它写成一个模版,哪里要使用,就引入这个模版即可。
<template name="slide"> <view class="section section-swiper"> <swiper class="slide" indicator-dots="{{true}}" autoplay="{{true}}" interval="2000" duration="1000"> <block wx:for="{{slides}}" wx:key="{{index}}"> <swiper-item> <image class="lazyload" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAANSURBVBhXYzh8+PB/AAffA0nNPuCLAAAAAElFTkSuQmCC" data-original="{{item.slide_url}}" mode="widthFix" class="slide-image" data-id="{{item.id}}" /> </swiper-item> </block> </swiper> </view></template>
使用时,这样引入
<import class="lazyload" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAANSURBVBhXYzh8+PB/AAffA0nNPuCLAAAAAElFTkSuQmCC" data-original="../../../templates/slide/slide.wxml" /><view class="container"> <template is="slide" data="{{slides}}"></template></view>
商城导航区、活动区
这里只是个简单的布局,就不赘述了。但是需要注意的是在微信小程序里,强烈推荐使用弹性布局首页商品展示区
这里的商品都是分块展示,很有规律,因此整个商品展示都可以直接用wx:for
遍历出来。
wxml:
<!-- 首页商品版块 --> <view class="section block"> <block wx:for="{{index_block}}" wx:key="{{item.id}}"> <view class="section cover"> <image class="cover-img" class="lazyload" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAANSURBVBhXYzh8+PB/AAffA0nNPuCLAAAAAElFTkSuQmCC" data-original="{{item.img_url}}" data-cid="{{item.id}}" bindtap="showcDetail"/> </view> <view class="section goods-list"> <block wx:for="{{item.section}}" wx:key="index" wx:for-item="product"> <view class="goods-item"> <image class="goods-img {{product.is_new?'new':''}} {{product.on_sale?'on-sale':''}}" class="lazyload" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAANSURBVBhXYzh8+PB/AAffA0nNPuCLAAAAAElFTkSuQmCC" data-original="{{product.goods_cover}}" data-pid="{{product.id}}" mode="aspectFill" bindtap="showDetail"/> <text class="title">{{product.header}}</text> <text class="desp">{{product.description}}</text> <text class="meta">{{product.meta}}</text> <text class="discount">{{product.discount}}</text> </view> </block> </view> </block> </view><!-- end-section block -->
这里有个细节,每个版块里的商品会分成“新品”、“立减”(即有折扣)、“无折扣”三种,着该怎么去做呢?这里我用了一个巧妙的方法:给每个商品的class
里绑定布尔值is_new
和on_sale
通过三元运算符判断是否给该商品挂载一个类名,再使用伪元素给该商品打上“新品”或“立减”的标签如下:
wxml:
<image class="goods-img {{product.is_new?'new':''}} {{product.on_sale?'on-sale':''}}" class="lazyload" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAANSURBVBhXYzh8+PB/AAffA0nNPuCLAAAAAElFTkSuQmCC" data-original="{{product.goods_cover}}" data-pid="{{product.id}}" mode="aspectFill" bindtap="showDetail"/>
wxss
.goods-img.new:before{ /*新品标签样式*/ position: absolute; left: 0; top: 0; width: 100rpx; height: 40rpx; line-height: 40rpx; content: "新品"; color: #fff; font-size: 9pt; text-align: center; background: #8CC64A; }.goods-img.on-sale:before{ /*立减标签样式*/ position: absolute; left: 0; top: 0; width: 100rpx; height: 40rpx; line-height: 40rpx; content: "立减"; font-size: 9pt; color: #fff; text-align: center; background: #ec6055; }
逻辑分析:
首页只是些商品,所以逻辑层只要根据每个商品的id
来跳到对应商品的详情页即可,很显然这个方法在多个页面都要使用的,所以使用模块化思想,创建一个modules
文件夹,把方法写在单独的js文件里,并向外输出
const showDetail=(e)=>{ const id=e.currentTarget.dataset.pid; //获取每个商品的id wx.navigateTo({ url: `/pages/goods/show?id=${id}` }) };export default showDetail;
哪里要使用,就用import引入
import showDetail from "../../modules/showDetail";
二、商品分类页
image
页面结构分析:
商品分类页分为左侧的商品分类菜单和右边的商品分类展示区,
用两个scroll-view
就可以了,左右两边都设置scroll-y
让它们垂直方向滚动,此外,scroll-view
还有一个scroll-into-view
属性能让我们实现类似a
标签的锚点功能,scroll-into-view
的值是某个子元素的id,但是此处有一个小坑,这个id不能以数字开头
image
当时查了一下文档就开做了,于是乎给左侧菜单取了些数字id,现在想起来当时我太自以为然了 ,此外如果内容太多,是会产生滚动条的,如图:
image
这样看起来也太丑了。。
**解决办法:给全局样式加入下面的样式
//隐藏滚动条::-webkit-scrollbar{ height: 0; width: 0; color: transparent; }
嗯,beautiful !!
商品分类功能
逻辑分析:给页面注册个curIndex(当前选中菜单的下标),如果当前下标和选中的菜单下标相同,则处于激活状态
部分代码:
wxml:
<view class="main"> <scroll-view scroll-y class="category-left"> <view class="cate-nav-list" wx:for="{{cate_nav_list}}" wx:key="{{item.id}}" data-id="{{item.id}}" data-index="{{index}}" bindtap="switchCategory"> <text class="cate-name {{curIndex===index?'on':''}}">{{item.name}}</text> </view> </scroll-view> <scroll-view class="category-right" scroll-y="{{true}}" scroll-into-view="{{toView}}" scroll-with-animation="true"> <view class="cate-content"> <view class="cate-list-content" wx:for="{{detail}}" wx:key="{{item.id}}" id="{{item.id}}"> <view class="banner"> <image class="lazyload" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAANSURBVBhXYzh8+PB/AAffA0nNPuCLAAAAAElFTkSuQmCC" data-original="{{item.banner}}"/> </view> <view class="header">{{item.cate_name}}</view> <view class="cate-list"> <view class="cate-item" wx:for="{{item.cate_list}}" wx:key="{{index}}" wx:for-item="cateList"> <image class="lazyload" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAANSURBVBhXYzh8+PB/AAffA0nNPuCLAAAAAElFTkSuQmCC" data-original="{{cateList.item_img}}" /> <text>{{cateList.item_name}}</text> </view> </view> </view> </view> </scroll-view></view>
js:
const app=getApp(); Page({ /** * 页面的初始数据 */ data: { cate_nav_list:[ {name:"新品",id:"new"}, {name:"手机",id:"phone"}, {name:"电视",id:"tv"}, {name:"电脑",id:"laptop"}, {name:"家电",id:"appliance"}, {name:"路由",id:"router"}, {name:"智能",id:"smart"}, {name:"儿童",id:"kids"}, {name:"灯具",id:"lignts"}, {name:"电源",id:"adapter"}, {name:"耳机",id:"headset"}, {name:"音箱",id:"voicebox"}, {name:"生活",id:"life"}, {name:"服务",id:"service"}, {name:"米粉卡",id:"card"} ], curIndex:0, //初始化当前下标为0 toView:"new", //默认显示“新品展示” detail:[] }, switchCategory(e){ const curIndex=e.currentTarget.dataset.index?e.currentTarget.dataset.index:0; //获取每个菜单的id //更新数据 this.setData({ toView:e.currentTarget.dataset.id, curIndex }); }, onLoad: function (options) { const detail=app.globalData.category; //获取分类展示数据 this.setData({ detail }); } })
三、发现页
页面结构分析:
image
里面展示了一些商品宣传视频(当时还是不太想切太多的页面)这里用弹性布局+video组件就搞定了。这里是文档
四、商品详情页
image
页面结构分析:
商品详情顶部由一个swiper组成,中间部分由一些cells组成(weui)点击这里快速查看
底部是商品的概述及参数配置两部分组成,这个还是比较简单的, 给两个元素分别绑定一个布尔值来控制谁显示谁隐藏。
当然,要使用weui必须引入它的样式文件,我们在app.wxss里引入,然后全局都可以使用了
@import "./lib/weui.wxss";
嗯,weui的官网和github地址自然少不了。weui官网 、weui github官方文档,在github上阅读代码是一个非常有效的学习方式,但是文件夹嵌套太深找个文件也不容易,这里奉上github阅读神器
使用到的weui:
请容许我贴一段weui抄来的结构
<view class="weui-cells"> <view class="weui-cell"> <view class="weui-cell__bd"> <view class="title">{{goods.header}}</view> <view class="desp">{{goods.description}}</view> <view class="meta">{{goods.meta}}</view> </view> </view></view>
商品详情页的显示
逻辑分析:
每个商品通过id来跳转到相应的详情页,但是这个id会在哪里呢因为先这个详情页是通过一个个商品来打开的,所以在商品详情页加载时,可以在 onLoad 中获取打开当前页面所调用的 query 参数(是条json数据),因为在showDetail里只用了id来跳转,所以options只有id属性
onLoad: function (options) { console.log(options); //{id:"4"} const id=options.id; //获取options里的id const goods=app.globalData.goodsDetail.filter(item=>{ return item.id==id; //通过id来筛选显示对应的商品 }); this.setData({ goods:goods[0] //因为id是唯一的,所以上面筛选出来的数组只有一条数据,这条数据就是要显示的商品数据 }); }
商品图片预览实现
微信小程序为我们提供了wx.previewImage()方法来实现图片的预览,实现方法如下:
previewImage(e){ const index=e.currentTarget.dataset.index; //获取swiper里的图片的下标 const slide=this.data.goods.goods_slides; //获取商品轮播图 const imgList=[]; //定义一个数组来存放轮播图的url slide.map(item=>{ imgList.push(item.slide_url); //用js的map方法把图片的url地址取出来放到数组里 }); wx.previewImage({ current: imgList[index], // 当前显示图片的链接,不填则默认为 urls 的第一张 urls: imgList }) }
五、商品属性选择
image
页面结构:整个商品属性页的结构是由一些cells组成,所以用weui最合适(插一段:原生的radio真的是很丑,不过weui对它做了改进)
一开始想有weui就简单多了,直接用form表单的bindsubmit
处理就可以了,奈何在weui里使用bindsubmit,取不到radio的值,折腾了很久,还是取不到,但是换成原生的radio竟可以取到!!
image
这个不行,那就换一种吧,用weui radiogroup
里的bindchange
事件吧,虽然繁琐了些,但是还是能解决问题的。。
话不多说,看代码(感觉写了一些很累赘的代码。。)
selectVersion(e) { const version = e.detail.value; const memory = version.split(",")[0]; const price = version.split(",")[1]; wx.setStorageSync('memory', memory); wx.setStorageSync('price', price); this.setData({ memory, price }); }, selectColor(e) { let color = e.detail.value; let cover_img=this.data.goods_attrSelect[0].goods_slides[0].slide_url; wx.setStorageSync('color', color); wx.setStorageSync('cover', cover_img); this.setData({ color, cover_img }); }, colorHasSelected(e) { const curcIndex = e.currentTarget.dataset.index ? e.currentTarget.dataset.index : 0; console.log(curcIndex); this.setData({ curcIndex }); }, versionHasSelected(e) { const curvIndex = e.currentTarget.dataset.index ? e.currentTarget.dataset.index : 0; console.log(curvIndex); this.setData({ curvIndex }); }
对应商品的属性页的跳转
逻辑分析:在商品详情页中,通过当前商品的id跳转
toSelect(e){ const id=e.currentTarget.dataset.id; wx.navigateTo({ url:`../selectGoods/selectGoods?id=${id}` }); }
在商品的属性选择页里,同样是通过id筛选显示不同商品的属性选择信息
onLoad: function (options) { const id = options.id; console.log(id); const goods_attrSelect = app.globalData.goodsDetail.filter(item => { return item.id == id; }); }
商品属性的联动选择
既然是商品,没有属性选择怎么行呢?兴致一来二话不说就是写。但是这里有一个难点:用户的选择多样性,难道你会把所有用户可能做出的选择都列出来吗?天哪,商品这么多,这是要累死我吗?no,no,no,这种方法不可取。哪还有什么解决办法呢?当时一想到的就是用数据缓存,嗯,这种方法可行,但是用缓存存取用户选择的值的话,这里有一个很大的问题:微信小程序里的数据缓存将data存储在本地缓存中指定的key中,会覆盖掉原来该key对应的内容,无论是wx.setStorage(异步接口)还是wx.setStorageSync(同步接口),这样的话,无论用户选择了多少个商品,只要key值一样,缓存数据永远只有一条!!!我此时的内心是痛苦的。
image
有什么方法能让数据缓存不覆盖原来的值,而是加上去?说实话,这里卡了我好久。废话不多说,直接上代码
submit(e) { const pre_item = wx.getStorageSync('attr_item'); const temp = { 'goods_name': wx.getStorageSync('goods_name'), 'memory': wx.getStorageSync('memory'), 'price': wx.getStorageSync('price'), 'color': wx.getStorageSync('color'), 'select_num': wx.getStorageSync('select_num'), 'cover': wx.getStorageSync('cover'), 'selected': false, 'isTouchMove': false } wx.setStorageSync('attr_item', [temp, ...pre_item]); //把获取到的pre_item和temp的数据缓存存入attr_item wx.showToast({ title: '已加入购物车', icon: 'success', duration: 3000, success() { setTimeout(() => { wx.navigateBack({ url: "../goods/show" }); }, 1000) } }); }
商品数量的加减操作
实现思路:分别绑定一个事件来操作商品数量的加减,最后将用户选择的数量设置到数据缓存里。
实现代码:
data: { select_num: 1 //默认选择为数量为1 }, minusCount(e) { //用户减操作 let select_num = this.data.select_num; select_num--; if (select_num < 1) { return; } this.setData({ select_num }); wx.setStorageSync('select_num', select_num); }, addCount(e) { //用户加操作 let select_num = this.data.select_num; select_num++; if (select_num > 5) { return; } this.setData({ select_num }); wx.setStorageSync('select_num', select_num); }
六、购物车操作
image
页面结构分析:购物车列表用一个弹性布局就搞定。底部的操作栏用固定定位 + 弹性布局就搞定。
<view wx:if="{{cart_list==''}}"> <view class="empty-cart"> <view class="cart-icon"> <image class="lazyload" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAANSURBVBhXYzh8+PB/AAffA0nNPuCLAAAAAElFTkSuQmCC" data-original="../../assets/icons/cart_empty.png" mode="aspectFill" /> </view> <view class="prompt">购物车还是空的</view> <button type="warn" class="btn-warn" style="background: #ff6700;" bindtap="goIndex">到小米商城逛逛</button> </view></view><view wx:else> <view class="cart-box"> <view class="cart-list" wx:for="{{cart_list}}" wx:key="{{index}}"> <view class="cart-item {{item.isTouchMove? 'touch-move-active': ''}}" bindtouchstart="touchstart" bindtouchmove="touchmove" data-index="{{index}}"> <view class="cart-content"> <icon type="{{item.selected?'success':'circle'}}" class="" color="#ff6700" size="20" bindtap="selectList" data-index="{{index}}" /> <image class="lazyload" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAANSURBVBhXYzh8+PB/AAffA0nNPuCLAAAAAElFTkSuQmCC" data-original="{{item.cover}}" mode="aspectFill" /> <text class="item-title">{{item.goods_name}} {{item.memory}}</text> <text class="item-num">{{item.color}}</text> <text class="item-price">{{item.select_num}}×</text> <text class="item-price">{{item.price}}</text> <view class="del-cart-item" catchtap="delCartItem">删除</view> </view> </view> </view> </view> <view class="user-operation"> <view class="select-all"> <icon wx:if="{{selectAllStatus}}" type="success" class="total-select" color="#ff6700" bindtap="selectAll" /> <icon wx:else type="circle" class="total-select" color="#ff6700" size="20" bindtap="selectAll" /> <text>全选</text> </view> <view class="total-price">合计: <text>{{totalPrice}}元</text> </view> <view class="btn-primary pay" bindtap="checkOut">结算</view> </view></view>
底部操作栏样式
.user-operation{ width: 100%; height: 100rpx; line-height: 100rpx; display: flex; flex-direction: row; justify-content: space-between; align-items: center; position: fixed; left: 0; bottom: 0; }.select-all,.total-price,.btn-primary.pay{ flex: 1; //三个盒子等分所有设备宽度 font-size: 12pt; text-align: center; }
加入购物车操作
逻辑分析:之前解决数据缓存问题,是为加入购物车功能做铺垫。在商品属性的级联选择中,已经获得了用户的所有要加入购物车的数据,这时候,把数据取出来在绑定到购物车页面上就可以了。
实现代码:
data: { cart_list: [], //初始化一个空数组用来存放购物车列表 }, goIndex() { //如果购物车为空,则让用户去首页 wx.switchTab({ url: "../index/index" }) },onShow: function () { const attr_item = wx.getStorageSync('attr_item'); //获取数据缓存里将要加入购物车的数据 let cart_list = this.data.cart_list; cart_list = [...attr_item]; //把缓存里的数据加到购物车列表里 const select_num = cart_list.map(item => { //获取用户每次选择的数量 return item.select_num; }) let goods_sum=select_num.reduce(function(prev,cur){ return prev+cur; //用es6的reduce()方法把用户每次选择的数量相加 }); wx.setStorageSync('goods_sum', goods_sum); //再次存入缓存 this.setData({ //更新购物车列表 cart_list }); }
购物车全选、反选、计算总价功能
这是一个很经典的问题,凡事涉及到购物车的操作,这个功能都是少不了的。
data: { cart_list: [], totalPrice: 0, }, selectList(e) { let selectAllStatus = this.data.selectAllStatus; const index = e.currentTarget.dataset.index; let cart_list = this.data.cart_list; // console.log(cart_list[index].selected); const selected = cart_list[index].selected; cart_list[index].selected = !selected; console.log(selected); //购物车列表里的条目只要有一个取消,全选就取消 const symbol = cart_list.some(cart => { //这里用es6的some()函数 return cart.selected === false; }); if (symbol) { //如果找到false,全选就取消 this.data.selectAllStatus = false; } else { this.data.selectAllStatus = true; } this.setData({ //更新数据 cart_list, selectAllStatus: this.data.selectAllStatus }); this.getTotalPrice(); }, getTotalPrice() { //定义一个计算总价的方法 let cart_list = this.data.cart_list; let totalPrice = 0; for (let i = 0; i < cart_list.length; i++) { if (cart_list[i].selected) { totalPrice += parseInt(cart_list[i].select_num) * parseInt(cart_list[i].price); //注意这里要用parseInt()把数量和价格取出来 } } //更新总价 this.setData({ totalPrice }); }, selectAll(e) { let selectAllStatus = this.data.selectAllStatus; selectAllStatus = !selectAllStatus; let cart_list = this.data.cart_list; for (let i = 0; i < cart_list.length; i++) { cart_list[i].selected = selectAllStatus; //全选为true,则所有购物车列表为true,全选为false,则所有购物车列表为false } //更新数据 this.setData({ cart_list, selectAllStatus }); this.getTotalPrice(); }
删除购物车操作
data: { startX: 0, //开始坐标 startY: 0, },//滑动事件处理 touchmove(e) { let index = e.currentTarget.dataset.index, //获取当前索引 startX = this.data.startX, //获取开始X坐标 startY = this.data.startY, //获取开始Y坐标 touchMoveX = e.changedTouches[0].clientX, //滑动变化坐标 touchMoveY = e.changedTouches[0].clientY, //滑动变化坐标 //获取滑动角度 angle = this.getAngle({ X: startX, Y: startY }, { X: touchMoveX, Y: touchMoveY }); this.data.cart_list.forEach(function (v, i) { v.isTouchMove = false if (Math.abs(angle) > 30) return;//用户滑动超过30度,删除按钮就不出来 if (i == index) { if (touchMoveX > startX) //右滑 v.isTouchMove = false else //左滑 v.isTouchMove = true } }) //更新数据 this.setData({ cart_list: this.data.cart_list }) }, getAngle(start, end) { let X = end.X - start.X, Y = end.Y - start.Y //返回角度 /Math.atan()返回数字的反正切值 return 360 * Math.atan(Y / X) / (2 * Math.PI); }, delCartItem(e) { const index=e.currentTarget.dataset.index; //获取购物车要删除商品的下标 this.data.cart_list.splice(index, 1); wx.clearStorageSync("select_num"); this.setData({ cart_list: this.data.cart_list }); }
七、商品的匹配及搜索功能实现
image
页面结构分析:先把搜索提示框固定定位到搜索栏下方,如果搜索到商品,则用商品展示模版输出该数据
<import class="lazyload" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAANSURBVBhXYzh8+PB/AAffA0nNPuCLAAAAAElFTkSuQmCC" data-original="../../templates/goods_list/goods_list.wxml" /><view class="weui-search-bar"> <view class="weui-search-bar__form"> <view class="weui-search-bar__box"> <icon class="weui-icon-search_in-box" type="search" size="14"></icon> <input type="text" class="weui-search-bar__input" placeholder="搜索" placeholder-class="plac" bindinput="searchInput" /> </view> </view> <view class="weui-search-bar__cancel-btn" bindtap="search">搜索</view></view><view class="search-list {{is_hidden?'hidden':''}}"> <block wx:for="{{search_list}}" wx:key="{{item.id}}"> <text class="search-item" bindtap="showItemDetail" data-header="{{item.header}}">{{item.header}}</text> </block></view><template is="goods_list" data="{{goods_list}}"></template>
逻辑分析:
我的实现思路是:
如果匹配到商品,搜索框下方就要让搜索提示框显示;
当用户输入搜索的内容为空,搜索提示框隐藏
用户点击搜索按钮,则把所有匹配到的商品列表显示出来,注意要模糊搜索,不区分大小写,提高用户体验;
用户点击匹配到的商品条目,则搜索该商品
实现方法:
filter()
+indexOf()
+toLowerCase()
;
代码如下:
import showDetail from "../../modules/showDetail";const app=getApp(); Page({ /** * 页面的初始数据 */ data: { goods_list:[], search_list:[], is_hidden:true }, searchInput(e){ let search_list=this.getList(e.detail.value); //获取用户的输入值 if(e.detail.value==""){ //如果用户没输入,搜索提示列表置空,并且让搜索提示框隐藏 search_list=[]; this.data.is_hidden=true; }else{ this.data.is_hidden=false; } //更新数据 this.setData({ search_list, is_hidden:this.data.is_hidden }); }, search(e){ //按关键字筛选商品,如果关键字匹配到商品名称,则返回该商品列表 const keywords=wx.getStorageSync('keywords'); wx.showLoading({ title: '请稍等', }); setTimeout(()=>{ this.setData({ goods_list:this.getList(keywords), is_hidden:true //如果搜索到了商品,就让搜索框隐藏 }); wx.hideLoading(); },500); }, showDetail, showItemDetail(e){ //按关键字筛选商品,如果关键字匹配到商品名称,则返回该商品列表 const header=e.currentTarget.dataset.header.toLowerCase(); console.log(header); wx.showLoading({ title: '请稍等', }) setTimeout(()=>{ wx.hideLoading() this.setData({ goods_list:this.getList(header), is_hidden:true }); },500) }, /** * attr:需要匹配筛选的数据 */ getList(attr){ //定义一个获取商品标题的方法 return app.globalData.phone.goods_list.filter(item=>{ return item.header.toString().toLowerCase().indexOf(attr)>-1; }); } })
八、收货地址页
image
实现思路:用数据缓存和数据绑定来控制input的disabled实现实时编辑
代码如下:
// pages/address/address.jsPage({ data: { receiverName: "", mobile: "", addressDetail: "", postCode: "", isDisabled: false, isComplete: false, buttonTitle: "保存" }, formSubmit(e) { const addrInfo = e.detail.value; let {receiverName,mobile,addressDetail,postCode}=addrInfo; //把data里的数据结构出来 if(receiverName==""||mobile==""||addressDetail==""||postCode==""){ this.data.isComplete=false; wx.showModal({ title:"提示", content:"请完善信息", showCancel:false }); }else if(!/^[1][3,4,5,7,8]\d{9}$/.test(mobile)){ //判断手机号格式 wx.showModal({ title:"提示", content:"手机号格式不规范", showCancel:false }); }else if(!/^[0-9]{6}$/.test(postCode)){ wx.showModal({ title:"提示", content:"邮政编码不规范", showCancel:false }); }else{ this.data.isComplete=true; wx.setStorageSync('addrInfo', addrInfo); } this.setData({ isDisabled: true, isComplete: this.data.isComplete }); }, updateAddr(e){ this.setData({ isDisabled:false, isComplete:false, buttonTitle: "保存" }); }, /** * 生命周期函数--监听页面加载 */ onLoad: function (options) { let addrInfo=wx.getStorageSync("addrInfo"); console.log(addrInfo); let {receiverName,mobile,addressDetail,postCode}=addrInfo; this.setData({ receiverName, mobile, addressDetail, postCode, isDisabled: true, isComplete:true, buttonTitle: "修改" }); } })
总结:
在做这个项目的时候遇到了很多问题,中途因为一个问题都要卡很久,但是现在想想,经历过肯定是能学到东西的,之前对于一些东西想都不敢想,但是没关系,只要你想做,那就去做吧,去网上查资料,多去社区问问大牛,你总是能学到东西的。对于这个项目,很多功能都还没完善,比如商品评论,生成订单等等,但是我会不定时的更新,有时间的话,我会用mpvue或者wepy改写,又能学到新技术
作者:云中玉卷
链接:https://www.jianshu.com/p/906c95d6ccb4
共同学习,写下你的评论
评论加载中...
作者其他优质文章