目录:
1.第一部分
1.首页的编写和基本逻辑
(1)分析主要结构,即组成的wxml文件和wxss文件的编写。
(2)c-image自定义组件。
(3)编写wx:for遍历商品信息的wxml文件和wxss文件
2.跳转页面的编写与基本逻辑
(1)分析主要结构,即组成的wxml文件和wxss文件的编写。
(2)简单的js逻辑并且引出了双重组件的定义。
(3)双重组件的嵌套--slot。
(4)子组件与父组件的交互 bind:“”。
(5)介绍c-quantity子组件的逻辑。
3.总结
2.第二部分(http://lindasoft.com/view/article/details?articleId=551)
在前面举了一个简单的例子,登录界面(http://lindasoft.com/view/article/details?articleId=544)。
不过那个是一个非常基础的逻辑界面。下面就是微信小程序的升华了。
接下来的内容是两大部分,请做好准备!!!
首先,先来一张图吧。
1.首先就让我们完成第一幅图的内容吧
(1)其实第一张图由四部分组成,即搜索框,轮播图,四个图标,和底下的商品样式
(2)接下来说第二部分。c-image自定义组件的应用
<!--index.wxml-->
<swiper class="banner" autoplay interval="1000" duration="400" indicator-dots="true" circular="true" indicator-color="rgba(255,255,255,.6)" indicator-active-color="#ff4d61">
<swiper-item wx:for="{{banner}}" wx:key="{{item}}">
<c-image src="{{item}}" mode="scaleToFill"></c-image>
</swiper-item>
</swiper>
<!--index.wxss-->
.banner {
width: 100vw;
height: 183px;
}
.swiper-item {
height: 100%;
}
<!--index.js-->
//获取轮播图渲染列表
getImgsList() {
this.setData({
banner: [
'http://img0.imgtn.bdimg.com/it/u=1250019442,1455740768&fm=26&gp=0.jpg',
'http://img0.imgtn.bdimg.com/it/u=1250019442,1455740768&fm=26&gp=0.jpg',
'http://img0.imgtn.bdimg.com/it/u=1250019442,1455740768&fm=26&gp=0.jpg'
]
})
},
但是其实上面就涉及到了自定义组件了,没错,就是c-image,那么这个c-image是在哪里定义的呢。这个自定义组件实在全局的App.json中定义的,就像这样。
这里自定义了四个自定义组件。剩下来的三个接下来我们会用到。
"usingComponents": {
"c-modal": "./components/c-modal/index",
"c-dialog": "./components/c-dialog/index",
"c-quantity": "./components/c-quantity/index",
"c-image": "./components/c-image/index"
}
那么就让我们看看这个所谓自定义组件是怎么写的吧。
<!--index.wxml-->
<image
mode="{{mode}}"
src="{{src}}"
lazy-load="{{lazyload}}"
binderror="error"
bindload="loaded"
class="c-image {{visible ? 'visible' : ''}}"
></image>
<!--index.wxss-->
.c-image{width: 100%;height: 100%;opacity: 0;transition: opacity 0.4s;}
.c-image.visible{opacity: 1;}
<!--index.json-->
{
"component": true
}
<!--index.js-->
Component({
properties: {
src: {
type: String,
value: ''
},
mode: {
type: String,
value: 'scaleToFill'
},
lazyload: {
type: Boolean,
value: false
}
},
data: {
visible: false
},
methods: {
/*
* 加载完成
* */
loaded() {
this.setData({
visible: true
});
this.triggerEvent('load');
},
/*
* 加载出错
* */
error() {
this.setData({
visible: true
});
this.triggerEvent('error');
}
}
});
/*
* 使用示例:
* <component-image src="{{item}}" mode="aspectFill" bind:error='error' bind:load='load' lazyload='true'></component-image>
* */
上面的代码就是所谓的一个简单的自定义组件了,其实也可以很简单的看到,这个c-image其实就是把image自定义,这样我们自己就想怎么用就怎么用了,这样做在大型的项目中会节约很多时间。不过在这里体现的不是很明显。接下来会明显的体现出来。
然后说说上面的代码,在wxml文件里面,其实说白了就是对于image的简单封装,在wxss文件里,是对image做了一些样式处理,在json文件中,这里要特别强调一点,所有自定义组件里面一定要加上上面代码里的那个"component":true。
接下来就跳过四个图标部分,直接说第一幅图最关键的地方了。
(3)没错,就是产品列表。
<!--index.wxml-->
<view class="goods-list col-2">
<navigator class="goods-item" hover-class="none" wx:for="{{10}}" wx:key="index" url="/pages/details/index?id={{index}}" >
<view class="pic">
<c-image src="https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1551870934503&di=583643db807bd42eb605f71c3347af8a&imgtype=0&src=http%3A%2F%2Fwww.emeixian.com%2Fimages%2F201508%2Fgoods_img%2F1889_G_1440874473697.jpg" mode="scaleToFill"></c-image>
</view>
<view class="info">
<view class="title">海南香蕉</view>
<view class="sub-title">单位:千克</view>
<view class="price">¥439.00</view>
</view>
</navigator>
</view>
<!--index.wxss-->
.goods-list {
display: flex;
flex-wrap: wrap;
padding: 15rpx 5px;
}
.goods-list.col-2 .goods-item {
width: 50%;
}
.goods-item {
display: flex;
align-items: center;
margin-bottom: 5rpx;
padding: 5rpx;
}
.goods-item .pic {
width: 100px;
height: 100px;
flex: none;
}
.goods-item .info {
flex: 1;
display: flex;
flex-direction: column;
background: #fff;
height: 100%;
padding: 5px;
}
.goods-item .info .title {
font-size: 26rpx;
padding-top: 5px;
font-weight: 600;
flex: 1;
}
.goods-item .info .sub-title {
font-size: 23rpx;
color: #969aa3;
margin-bottom: 5rpx;
}
.goods-item .info .price {
font-size: 27rpx;
color: #ef3325;
padding-bottom: 5px;
}
这里其实就是在navigator上设置了wx:for循环,在这里的跳转页面都是跳转到这幅图的,在这里只是做了一个模板供参考。
在这里布局方法不是唯一的!!!!!!切记!!!!!!
2.接下来我们来到了第二幅图的页面,其实就是上面的navigator跳转的页面,在进入第二幅图以后你其实会发现,其实和第一幅图差不多嘛。
第二幅图其实分了六个部分,都差不多,其实最主要的还是布局的wxss文件里面的内容,在这里补充最重要的一点,尽可能的把每一个样式分开写,这样很利于之后的修改。
(1)接下来就是展示代码了,我重点想说明的只有最后一部分了。
<!--index.wxml-->
<swiper class="goods-banner" autoplay interval="1000" duration="400" indicator-dots="true" circular="true" indicator-color="rgba(255,255,255,.6)" indicator-active-color="#ff4d61">
<swiper-item wx:for="{{goodsBanner}}" wx:key="{{item}}" class="swiper-item">
<c-image src="{{item}}" mode="scaleToFill"></c-image>
</swiper-item>
</swiper>
<view class="goods-info">
<view class="title">
<text class="text">海南香蕉</text>
</view>
<view class="price">
<view class="c-price">
<text>价格</text>
<text class="val">¥1040.00</text>
</view>
<view class="o-price">
<text>原价¥1351.00</text>
</view>
</view>
</view>
<view class="info">
<view class="info-item txt-left">
<text>运费:0</text>
</view>
<view class="info-item txt-center">
<text>销量:1200</text>
</view>
<view class="info-item txt-right">
<text>库存:1200</text>
</view>
</view>
<view class="info" bindtap="showBuyDialog">
<view class="info-item">
<text>规格</text>
</view>
<view class="info-item txt-right">
<text class="line">选择规格></text>
</view>
</view>
<view class="info" bindtap="toComment">
<view class="info-item">
<text>用户评价</text>
</view>
<view class="info-item txt-right">
<text class="line">查看评价></text>
</view>
</view>
<view class="introduce-title">
<text class="line">图文详情</text>
</view>
<view class="introduce-content">
<image mode="aspectFit" src="//img12.360buyimg.com/n1/s450x450_jfs/t1/27491/2/4431/289258/5c321c30E280237c3/86ed242b2360a2cd.jpg"></image>
<text>菜篮子产递</text>
</view>
<!-- sku --><!--双重自定义组件-->
<c-dialog show="{{showDialog}}">
<buy-inner bind:hideBuyDialog="hideBuyDialog"/>
</c-dialog>
<!--index.wxss-->
.goods-banner {
width: 100vw;
height: 370rpx;
}
.goods-info {
margin-bottom: 20rpx;
padding: 20rpx;
background-color: #fff;
}
.goods-info .title {
margin-bottom: 10rpx;
}
.goods-info .title .text {
font-size: 32rpx;
color: #333;
font-weight: 600;
}
.goods-info .price {
font-size: 24rpx;
display: flex;
/*交叉轴终点对齐*/
}
.goods-info .price .c-price {
color: #ef3325;
margin-right: 20rpx;
}
.goods-info .price .c-price .val {
font-size: 32rpx;
}
.goods-info .price .o-price {
color: #bbb;
font-style: oblique;/*浏览器会显示一个倾斜的字体样式*/
text-decoration:line-through;/*定义穿过文本的一条线*/
}
.info{
display: flex;
justify-content: space-between;
/*两端对齐,项目之间都相隔*/
padding: 20rpx;
border-top: 3rpx solid #eee;
}
.info .info-item{
width: 33.3%;
}
.info .txt-left{
text-align: left;
}
.info .txt-center{
text-align: center;
}
.info .txt-right{
text-align: right;
}
.line{
color:#ef3325;
}
.introduce-title{
padding: 20rpx;
border-top: 8rpx solid #eee;
}
.introduce-content{
display: flex;
align-items: center;
flex-direction: column;
/*主轴为垂直方向*/
padding: 20rpx;
border-top: 3rpx solid #eee;
font-size: 24rpx;
}
.introduce-content image{
width: 100%;
margin-bottom: 15rpx;
}
<!--index.json-->
{
"navigationBarTitleText": "商品详情",
"usingComponents": {
"buy-inner": "_components/buy-inner/index"
}
}
<!--index.js-->
const app = getApp();
Page({
data: {
detailInfo:{},
goodsBanner: [
'//img12.360buyimg.com/n1/s450x450_jfs/t1/27491/2/4431/289258/5c321c30E280237c3/86ed242b2360a2cd.jpg',
'//img12.360buyimg.com/n1/s450x450_jfs/t1/18848/17/4571/212369/5c32ce88E1ddec98d/67af5d6fb1577cd3.jpg',
'//img12.360buyimg.com/n1/s450x450_jfs/t1/7211/16/8954/485449/5c1088ecE9884e066/542088e742e6169c.jpg'
],
showDialog:false
},
onLoad(options) {
console.log(options.id)
},
//获取货物详情信息
getGoodDetail(){
let info = {
name:"",
}
this.setData({
detailInfo:info
})
},
//获取详情介绍
getIntroduce(){
},
//点击规格,弹窗显示
showBuyDialog(){
this.setData({
showDialog:true
})
},
hideBuyDialog() {
this.setData({
showDialog: false
})
},
//点击用户评价,前往评价页面
toComment(){
wx.navigateTo({
url: 'comment/index?id=' + this.data.detailInfo.id,
})
}
});
在wxml文件里的最后一部分,就是最重要的组件的交互,其实是在自定义组件里面自定义了组件,我叫他双重自定义组件。那么在最里面定义的那个自定义组件没有在app.json中定义,那么你就只能在index.json中定义了。
(2).接下了该说上面说到的那个双重自定义组件了。
这个就是双重组件的图片。
既然是双重自定义组件,那么就先从最外面的那个说起吧,c-dialog
<!--index.wxml-->
<view class="dialog {{className}} {{ show ? 'dialog_show' : '' }}">
<!--遮罩-->
<view class="dialog__mask" catchtap="hide"/>
<!--container-->
<view class="dialog__container">
<!--右上角关闭按钮-->
<image class="close" catchtap="hide" src="icons/icon_close.png"></image>
<slot></slot>
</view>
</view>
<!--index.wxss-->
.dialog__mask{position:fixed;top:0;right:0;bottom:0;left:0;z-index:10;visibility:hidden;background:rgba(0,0,0,.65);opacity:0;transition:all .4s ease}
.dialog__container{position:fixed;z-index:11;visibility:hidden;background:#fff;opacity:0;transition:all .4s ease;overflow:hidden;}
.dialog__container .close{position:absolute;top:12px;right:10px;width:20px;height:20px;}
.dialog .dialog__container{right:0;bottom:0;left:0;height:inherit; transform:translateY(50%);flex-direction:column}
.dialog.dialog_show .dialog__container{transform:translateY(0)}
.dialog.dialog_show .dialog__container{visibility:visible;opacity:1;min-height:36vh;}
.dialog.dialog_show .dialog__mask{visibility:visible;opacity:1}
<!--index.json-->
{
"component": true
}
<!--index.js-->
Component({
options: {
multipleSlots: false // 在组件定义时的选项中启用多slot支持
},
properties: {
show: {
type: Boolean,
value: false
}
},
data: {},
methods: {
// 打开弹窗
show() {
this.setData({
show: true
});
},
// 关闭弹窗
hide() {
this.setData({
show: false
});
},
}
});
在这里还要说一下前面的东西,有一句show="{{showDialog}}" 这个为true才是上面双重组件显示的基础。
(3)接下来说c-dialog。其实吧,基本上所有的自定义组件都是一样的,就像c-image差不多吧。只不过这个组件里面特殊了一些,在c-dialog里面吧存在slot组件吧。这个slot其实就是前面的双重自定义组件里面的子组件。
在看index.json里面,若是app.json里面没有定义的自定义组件那么就在该页面的index.json里面自定义就好,顺便在js文件里面multipleSlots: false // 在组件定义时的选项中启用多slot支持,这样这个最外层就解决了。
接下里看buy-inner,其实这个组件也是其他的自定义组件差不多,只不过就是加了一点点其他的东西。
<!--index.wxml-->
<view class="sku_container">
<!--header-->
<view class="sku_hd">
<image class="sku-pic" src="//m.360buyimg.com/mobilecms/s750x750_jfs/t1/8332/4/12559/107539/5c3644b5E0a2ab9eb/507437b95b0abbad.jpg!q80.dpg"></image>
<view class="sku-desc">
<view class="title">海南香蕉</view>
<view class="stock">库存:900</view>
<view class="price">价格¥690</view>
<view class="o-price">原价¥690</view>
</view>
</view>
<!--body-->
<view class="sku_bd">
<view class="sku-quantity">
<view class="title">购买数量</view>
<c-quantity bind:change="onQuantityChange"></c-quantity>
</view>
</view>
<!--footer-->
<view class="sku_ft">
<view class="btn-add-shop" catchtap="addToShopCart">加入购物车</view>
<view class="btn-buy-now" catchtap="toBuyNow">立即购买</view>
</view>
</view>
<!--index.wxss-->
.sku_container {
display: flex;
flex-direction: column;
height: 100%;
padding: 20rpx;
}
.sku_hd {
flex: none;
padding: 20rpx 0;
border-bottom: 1rpx solid #f0f0f0;
text-align: center;
font-size: 34rpx;
margin-bottom: 20rpx;
}
.sku_hd .title {
color: #333;
}
.sku_bd {
flex: 1;
}
.sku_ft {
flex: none;
display: flex;
justify-content: center;
margin-top: 10rpx;
}
.sku_ft .btn-add-shop {
flex: 1;
border-radius: 10rpx;
padding: 20rpx 0;
font-size: 30rpx;
color: #fff;
background: #FFCC33;
text-align: center;
}
.sku_ft .btn-buy-now {
flex: 1;
border-radius: 10rpx;
padding: 20rpx 0;
font-size: 28rpx;
color: #fff;
background: #FF6600;
text-align: center;
}
.sku_hd {
display: flex;
}
.sku-pic {
width: 150rpx;
height: 150rpx;
flex: none;
margin-right: 20rpx;
}
.sku-desc {
flex: 1;
text-align: left;
font-size: 32rpx;
margin-right: 60rpx;
}
.sku-desc .title {
color: #333;
font-weight: 600;
}
.sku-desc .stock{
color: #aaa;
font-size: 25rpx;
}
.sku-desc .o-price {
color: #bbb;
font-style: oblique;
text-decoration:line-through;
font-size: 27rpx;
}
.sku-desc .price {
color: #ff4d61;
font-size: 27rpx;
}
.sku-quantity {
margin-bottom: 20rpx;
display: flex;
justify-content: space-between;
}
.sku-quantity .title {
font-size: 32rpx;
color: #333;
font-weight: 600;
}
<!--index.json-->
{
"component": true
}
<!--index.js-->
Component({
/**
* 组件的属性列表
*/
properties: {
info: {
type: Object,
default: {}
},
quantity: {
type: Number,
value: 1,
}
},
/**
* 组件的初始数据
*/
data: {
},
/**
* 组件的方法列表
*/
methods: {
onQuantityChange(e) {
this.data.quantity = e.detail;
console.log(this.data.quantity);
},
//添加到购物车
addToShopCart(){
console.log(this.data.quantity);
//请求添加购物车界面
},
//立即购买
toBuyNow() {
this.triggerEvent('hideBuyDialog');
wx.navigateTo({
url: '../../pages/shopping-cart/buy-now/index',
})
},
}
})
所有的自定义组件都差不多,那么wxss和json就不说了。先说wxml文件吧。
有一个前面没有出现过的东西。<c-quantity bind:change="onQuantityChange"></c-quantity>
(4).这个其实就是自定义了一个组件,其实也没有什么,但是它自定义组件里面是这样写的。bind:change=“onQuantityChange”在组件之间如果这样写的话就说了一个最最重要的问题,这样写视为了组件之间的交互,即如两个组件,一个父组件一个子组件。当子组件要给父组件传数据的时候,那么在父组件里面的引用子组件就要这样写。
然后在看js文件吧。出现了一个onQuantityChange(e) {this.data.quantity = e.detail; console.log(this.data.quantity); }
这个函数其实就是为了接受传过来的数据,在这个函数里面,这个e.detail就是传过来的数据。
这就是简单组件与组件之间的数据交互。
(5)那么我们最后来看c-quantity。
这个就是上面说到的子组件了,代码如下:
<!--index.wxml-->
<view class="c-quantity">
<view class="btn decrement {{quantity<2 ? 'disabled' : ''}}"
catchtap="quantity_decrement"></view>
<view class="num">{{quantity}}</view>
<view class="btn increment {{quantityLimit && quantity >= quantityLimit ? 'disabled' :''}}"
catchtap="quantity_increment"></view>
</view>
<!--index.wxss-->
.c-quantity {
overflow: hidden;
display: inline-flex;
border: 1px solid #dcdcdc;
height: 60rpx;
line-height: 60rpx;
text-align: center;
}
.c-quantity .btn {
width: 60rpx;
height: 100%;
}
.c-quantity .btn.disabled {
color: #dcdcdc;
}
.c-quantity .decrement:before {
content: '-';
font-size: 40rpx;
line-height: 1;
}
.c-quantity .increment:before {
content: '+';
font-size: 40rpx;
line-height: 1;
}
.c-quantity .num {
min-width: 64rpx;
height: 100%;
border-left: 2rpx solid #dcdcdc;
border-right: 2rpx solid #dcdcdc;
}
<!--index.json-->
{
"component": true
}
<!--index.js-->
Component({
properties: {
quantity: {
type: Number,
value: 1,
},
quantityLimit: {
type: Number,
value: 10000,
}
},
data: {},
methods: {
/*
* 数量减少
* */
quantity_decrement() {
let _count = this.data.quantity - 1, _min = 1;
if (_count < _min) return false;
this.setData({
quantity: _count
});
this.triggerEvent('change', _count);
},
/*
* 数量增加
* */
quantity_increment() {
let _count = this.data.quantity + 1, _max = this.data.quantityLimit;
if (_count > _max) return false;
this.setData({
quantity: _count
});
this.triggerEvent('change', _count);
},
}
});
其实这个自定义组件还是和其他的一样,那么就不说wxss和json了,先说wxml。
其实就是设置了两个可以点击的view和一个显示数值的view而已。两个可以点击的view,然后设置在逻辑里面设置class属性。
下面是js文件。
在这个js中我主要想说的就是this.traggerEvent();这个语句。
你可以网上看,在那两个函数中是这样写的this.triggerEvent('change', _count);,这个其实就是父组件就是子组件之间子组件给父组件传输的语句了,在这里简单的说一下逻辑和traggerEvent。
this.traggerEvent()这个东西上面传了两个参数,一个是change、一个是_count,第一个参数是你要传递的父组件的传递的那个,怎么讲,之前父组件不是这样写的吗? <c-quantity bind:change="onQuantityChange"></c-quantity> 因为父组件是bind:change这样写的,所以第一个参数就要写change,那么第二个参数是什么呢,其实我上面已经暗示了,就是:
onQuantityChange(e) {this.data.quantity = e.detail; console.log(this.data.quantity); },在这个函数里面有一个e,并且函数里面也有一个e.detail,那么这个_count其实就是e,那么这就是简单组件之间的交互逻辑了。
不过在这里主要没有演示传递过来的数据,只是说了一下,在下一个例子说了传过来的数据该怎么用。
那么,其实这就是简单的组件与组件之间的联系与交互。
总结:
1.在这里可能看到其实在这个例子中用组件更麻烦了,其实用组件是为了更加方便的管理代码,就像我们的c-quantity,就会在下一个案例中反复用到。所以只有重复用到的地方考虑组件,只用一次最好不要用组件。
2.组件之间的数据交互也很重要。
3.在写wxss和wxml代码的时候,最重要的是一条wxss属性最好单独的写在一个view中,这样写的时候可能会很麻烦,但是非常容易再后面管理。
{{ cmt.username }}
{{ cmt.content }}
{{ cmt.commentDate | formatDate('YYYY.MM.DD hh:mm') }}