.前言
最近页面写的挺多的,最初是给了一个公司开发好的小程序源代码,然后找了一个别的公司开发好的商城小程序,让按它的样式和功能实现。实现起来发现的问题有,本身的源代码一些页面的方法、组件的调用,后端的接口,有的还需要用到有的需要优化或者重新写,关于小程序前端与后端的交互,接口api的调用我也是才开始研究,下面就先整理一下微信小程序开发前端比较常用/实用的一些前端知识点。
样式模块
.顶部导航栏_navigationBar
"navigationBarTitleText": "购物车", //页面顶部文字
"navigationBarBackgroundColor": "#FFFFFF", //页面顶部背景颜色
"navigationBarTextStyle": "black" //页面顶部文字颜色
.轮播图_swiper
<!-- //轮播图 -->
<view class="mz-swiper">
<view class="x-swiper"wx:if="{{goods}}"wx:key="id"bindtap="goGoodsDetail"data-id="{{item.id}}">
<swiper autoplay="true"indicator-dots="true"indicator-active-color="white" interval="3000" imageProps="{slides}">
<block wx:for="{{slides}}" wx:for-index="index"wx:key="*this">
<swiper-item>
<image src="{{picUrl + item}}" mode="spectFill"/>
</swiper-item>
</block>
</swiper>
</view>
</view>
属性 | 类型 | 默认值 | 备注 |
indicator-dots | boolean | false | 面板指示点的显示 |
indicator-color | color | rgba(0,0,0,.3) | 指示点颜色 |
indicator-active-color | color | #000000 | 当前选中的指示点颜色 |
autoplay | boolean | false | 自动切换 |
interval | number | 5000 | 自动切换时间间隔 |
circular | boolean | false | 衔接滑动 |
.弹窗
弹窗我的实现方法是:
1.在.js中定义初始化 isShow判断是否显示弹窗,以及seeNotice打开弹窗closeNotice关闭弹窗方法
index.js
Page({
data: {
isShow:false, //弹窗的状态,默认为关闭 false
}
seeNotice:function(){
this.setData({isShow:true})
},
closeNotice:function(){
this.setData({isShow:false})
},
})
2.在.wxml中需要实现弹窗的位置绑定点击打开/关闭弹窗事件,我这里是实现在公告的位置点击公告出现弹窗显示具体信息,另外在弹窗显示的时候弹窗以外的部分用遮罩层覆盖同样绑定事件实现点击弹窗以外部分关闭弹窗
index.wxml
<!-- 公告 -->
<view class="notice" bind:tap="seeNotice">
<text>{{notice}}</text>
</view>
<!-- 公告信息弹窗 -->
<view wx:if="{{isShow==true}}">
<view class="tanxx" bind:tap="seeNotice"></view>
<view class="detail">
<view class="icon" bind:tap="seeNotice"></view>
<view class="detail-person">
<image src="{{shopInfo.mtzpath}}" mode="aspectFill"bind:tap="seeNotice"/>
<text style="width: 28%;" bind:tap="seeNotice">{{shopInfo.clientname}}</text>
</view>
<view class="detail-notice">{{notice}}</view>
</view>
</view>
<!--遮罩层-->
<view class="container">
<view class="mask" wx:if="{{isShow}}" bind:tap="closeNotice"> </view>
</view>
3.把对应的样式写在 .wxss 文件中
.底部导航栏_tabar
1. 全局导航:直接在app.json文件中添加对应的页面到 "list"[{ }]中并配置
app.json
{
"tabBar": {
"color": "#a9b7b7", // color未选择时 底部导航文字的颜色
"selectedColor": "#0A0A0A", // selectedColor 选择时 底部导航文字的颜色
"borderStyle": "white", // borderStyle 底部导航边框的样色
"list": [ // list 导航配置数组
{
"selectedIconPath": "/assets/images/shouye.png", // 选中时 图标路径
"iconPath": "/assets/images/shouye.png", // 未选择时 图标路径
"pagePath": "pages/index/index", // 页面访问地址
"text": "地图首页" // 导航图标下方文字
},
{
"selectedIconPath": "/assets/images/wode.png",
"iconPath": "/assets/images/wode.png",
"pagePath": "pages/my/index/index",
"text": "我的"
}
]
},
}
2.导航栏不同界面的跳转:
页面要返回/跳转至tabbar的某一页面,可用:
wx.switchTab({
url: '../x/x'
});
组件components
自定义组件
例:导航组件
效果
<!--pages/components/navigation-bar/navigation-bar.wxml-->
<view style="height: {{navBarHeight}}px;background-color: {{ bgColor }};" class="mz-nav-container">
<view class="mz-content-box" style="height: {{menuButtonHeight}}px;margin-bottom: {{menuButtonBottom}}px;">
<view class="mz-content-six" style="width: {{customNavWidth}}px;">
<view class="{{canGoBack ? 'iconfont icon-31fanhui1' : 'iconfont icon-31fanhui1'}}" style="color: {{ fanhuiColor }};" bindtap="goBack"></view>
<view class="mz-view-six" bindtap="goSearchPage">
<view class="iconfont icon-sousuo"></view>
<input class="mz-text" placeholder="{{ place }}" bindconfirm="goSearch"/>
</view>
<view wx:if="{{place=='搜索店内商品'}}" style="background-color: rgb(255, 255, 255); width: 30px;height: 30px; text-align: center;margin-left: 3%;border-radius: 90%;line-height: 100%; ">
<image src="/assets/images/hcps_icon.png" style="width: 20px;height: 20px; margin-top: 18%;" bind:tap="checkOrder" />
</view>
</view>
</view>
</view>
// pages/components/navigation-bar/navigation-bar.js
const app = getApp();
import { getStorageSync, removeStorageSync } from '../../../utils/token.js'
Component({
properties: {
place: {
type: String,
value: '搜索店内商品'
},
bgColor: {
type: String,
value: '#0A0A0A'
},
fanhuiColor: {
type: String,
value: '#fff'
},
},
data: {
navBarHeight: app.globalData.navBarHeight,
customNavWidth: app.globalData.customNavWidth,
menuButtonRight: app.globalData.menuButtonRight,
menuButtonBottom: app.globalData.menuButtonBottom,
menuButtonHeight: app.globalData.menuButtonHeight,
menuButtonTop: app.globalData.menuButtonTop,
styleType: 6,
canGoBack: true,
},
methods: {
goSearch(e) {
const searchContent = e.detail.value;
this.triggerEvent('goSearch', searchContent)
},
goSearchPage() {
this.triggerEvent('goSearchPage')
},
goBack() {
if (this.data.canGoBack) {
wx.navigateBack()
}else {
wx.reLaunch({
url: '/pages/index/index',
success: function () {
console.log("跳转到指定页面成功");
},
fail: function (err) {
console.log("跳转到指定页面失败", err);
},
});
}
},
checkOrder(){
const userInfo = getStorageSync('userInfo') || {};
const isNoLogin= JSON.stringify(userInfo) == '{}'
if(isNoLogin) {
wx.showToast({
title: '请先登录',
icon: 'none'
})
setTimeout(() => {
wx.navigateTo({
url: '/pages/profie/phoneLogin/phoneLogin',
})
}, 1500)
return;
}
wx.navigateTo({
url: '/pages/order/list/index',
})
},
},
// 组件所在页面的生命周期声明对象
pageLifetimes: {
// 组件所在的页面被展示时执行
show: function () {
let curerntPages = getCurrentPages().length
this.setData({
canGoBack: curerntPages>1 ? true : false
})
},
},
})
/* pages/components/navigation-bar/navigation-bar.wxss */
@import '/assets/css/iconfont.wxss';
.mz-nav-container {
width: 100%;
box-sizing: border-box;
display: flex;
align-items: flex-end;
position: fixed;
left: 0;
top: 0;
background-color: #0A0A0A;
color: #fff;
z-index: 10;
}
.mz-content-box {
width: 100vw;
box-sizing: border-box;
display: flex;
align-items: center;
box-sizing: border-box;
}
.mz-content-six {
display: flex;
align-items: center;
height: 100%;
padding-left: 20rpx;
box-sizing: border-box;
}
.mz-content-six .mz-view-six{
width: 425rpx;
height: 58rpx;
border-radius: 14rpx;
display: flex;
align-items: center;
background-color: #f3f3f3;
padding: 0 20rpx;
margin-left: 10rpx;
color: #878181;
font-size: 25rpx;
}
.mz-content-six {
font-size: 32rpx;
cursor: pointer;
}
.icon-31fanhui1 {
/* font-size: 58rpx; */
width: 58rpx;
cursor: pointer;
}
.mz-content-six .icon-sousuo {
font-size: 30rpx;
}
.mz-content-six .mz-view-six .mz-text {
margin-left: 10rpx;
width: auto;
height: 100%;
display: flex;
align-items: center;
}
在对应的页面使用
<!--pages/shop/index/index.json-->
{
"usingComponents": {
"navigation-bar": "../../components/navigation-bar/navigation-bar"
},
"navigationStyle": "custom"
}
<!--pages/shop/index/index.wxml-->
<!-- 顶部导航 -->
<navigation-bar class="mz-Top" bind:goSearchPage="clickSearch"/>
事件方法
.input事件
1. bindinput 事件
<input type="text" placeholder="请输入门店名称" confirm-type="search" maxlength="100" bindconfirm="handleSearch" bindinput="setSearchValue" value="{{searchValue}}"/>
//bindinput 事件是用户在输入框中输入内容时触发的事件。每输入一个字符,都会触发一次。
//实时搜索:根据用户输入的关键字实时向后端请求数据并展示。
//实时反馈:例如实时显示字符数、格式化输入内容等。
2. bindconfirm 事件
3. bindfocus 和 bindblur 事件
.事件绑定
1.点击事件(tap)
当用户点击某个元素时触发。可以通过在组件上使用 bindtap 或 catchtap 属性来绑定点击事件,需要在对应的 Page 或 Component 的 JavaScript 文件中,定义对应的事件处理函数。
<view class="Gomy" bindtap="gotoMy">
单击触发 'gotoMy'方法
<view>
2.长按事件(longpress)
在用户长时间按住某个元素时触发。可以通过在组件上使用 bindlongpress 或 catchlongpress 属性来绑定长按事件。
<view bindlongpress="handleLongPress">长按这里</view>
3.滑动事件(touchmove)
在用户触摸屏幕并滑动时触发。可以通过在组件上使用 bindtouchmove 或 catchtouchmove 属性来绑定滑动事件。
<view bindtouchmove="handleTouchMove">滑动这里</view>
4.输入事件(input)
在用户输入内容时触发。可以通过在输入组件上使用 bindinput 或 catchinput 属性来绑定输入事件。
<input bindinput="handleInput" placeholder="请输入内容" />
微信客服
在页面使用客服消息
需要将 button
组件 open-type
的值设置为 contact
,当用户点击后就会进入客服会话,如果用户在会话中点击了小程序消息,则会返回到小程序,开发者可以通过 bindcontact
事件回调获取到用户所点消息的页面路径 path
和对应的参数 query
,开发者需根据路径自行跳转。此外,开发者可以通过设置 session-from
将会话来源透传到客服。
<button open-type="contact" bindcontact="handleContact" session-from="sessionFrom">在线客服</button>
点击button实现图片切换
<!-- 点击不同品种图片切换操作 -->
<view>
<button data-index="0" bindtap="changeImage">单瓶</button>
<button data-index="1" bindtap="changeImage">整箱</button>
</view>
// 商品分类点击按钮切换图片
changeImage: function(e) {
const index = e.currentTarget.dataset.index; // 获取点击按钮的索引
this.setData({
imagePath: this.data.imagePaths[index]
});
},
data: {
picUrl: config.picRootOrigin,
totalPrice: 0,
goodsObj: {}, // 商品信息
cart: [], // 购物车
goodsId: "", // 商品id
clientId: "", // 商户id
isShow: false,
chooseNumber:1,
goods: [],
imagePath: '/../assets/images/xiaoxu/gooddetail1.jpg', //商品分类原始图片
imagePaths: [
'/../assets/images/xiaoxu/gooddetail1.jpg', //商品分类图片地址
'/../assets/images/xiaoxu/gooddetail2.jpg'
]
},
不过这里目前我是把两张图片直接引入到项目,并在对应页面data:{}中直接引入的,通过点击事件实现点击不同的分类切换图片,后续有待优化...
.事件冒泡
..........
.页面跳转
有父子关系(最多5级)
1.wx:navigateTo.
跳转后左上角有返回小箭头,点击可返回原本页面.
wx:navigateTo({
url:"../xiaoxu/xiao/xi" //跳转目标页面目录
});
wx.navigateTo({
url: `/pages/shop_detail/shop_detail?id=${id}` // 携带参数
})
2.wx.redirectTo.
关闭当前页面,跳转到应用内的某个页面(不能跳转tabbar页面)。
3.wx.switchTab.
跳转到 tabBar 页面,并关闭其他所有非 tabBar 页面;该方法只能跳转tabbar页面.
// 我的主页跳转
gotoMy(){
wx.switchTab({
url: '/pages/my/index/index',
})
},
4.wx.reLaunch.
关闭所有页面,打开到应用内的某个页面
5.wx.navigateback
返回页面
wx.navigateBack({
delta: 1 // 返回上一个界面,如果要返回多个界面,可以增加 delta 的值
});
常用控件
全局配置
属性 | 类型 | 必填 | 描述 |
---|---|---|---|
pages | string[] | 是 | 页面路径列表 |
window | Object | 否 | 全局的默认窗口表现 |
tabBar | Object | 否 | 底部tab栏的表现 |
图片标签
<image src "/../../../..项目本地图片路径"/>
<image sre "{{picUrl + item.photopath}}"/> //后端传入
通用属性
属性 | 类型 | 默认值 | 必填 | 说明 | 最低版本 | |||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
src | string | 否 | 图片资源地址 | 1.0.0 | ||||||||||||||||||||
mode | string | scaleToFill | 否 | 图片裁剪、缩放的模式 | 1.0.0 | |||||||||||||||||||
|
文本标签
text
- 文本标签,只能嵌套
text
标签 - 长按文字可以复制(只有这个标签有这个功能)
- 可以对空格 回车进行编码
wx:if
控制图片显示/隐藏
<!-- 选择弹窗 -->
<view wx:if="{{isShow==true}}">
<view class="buy-bg" bind:tap="isShow"></view>
<view class="detail">
<view class="back" bind:tap="isShow"></view>
<view>
<view>
<image id="myImage" src="{{imagePath}}" mode="widthFix" class="my-image"/>
</view>
<view>
<text>¥{{goodsObj.price}}</text>
<text>库存</text>
<text>198</text>
<text>已选</text>
</view>
</view>
<view>
<text>品种</text>
<!-- 点击不同品种图片切换操作 -->
<view>
<button data-index="0" bindtap="changeImage">单瓶</button>
<button data-index="1" bindtap="changeImage">整箱</button>
</view>
</view>
<view>
<text>数量</text>
<view class="num">
<view bind:tap="minus">-</view>
<text>{{chooseNumber}}</text>
<view bind:tap="add">+</view>
</view>
</view>
<view class="di_buy">
<!-- 分类弹窗内加入购物袋操作 -->
<button bind:tap="addToCart">加入购物袋</button>
<!-- 分类弹窗内购买操作 -->
<view class="buyback" bind:tap="goBuy">
<view class="buy">
<text>买</text>
</view>
</view>
<!-- <button bind:tap="isShow">买</button> -->
</view>
</view>
</view>
和上面的弹窗一样,我这里用 wx:if="{{isShow==true}}" 来判断是否显示弹窗
wx:for
列表循环 循环遍历
<view class="good" wx:for="{{goods}}
//遍历{{goods}}中的数据
</view>
例:
<!-- 商品列表 -->
<view class="good-list">
<view class="good" wx:for="{{goods}}"wx:key="pid"bindtap="goGoodsDetail"data-id="{{item.pid}}">
<image src="{{picUrl + item.photopath}}" mode="spectFill"/>
<view class="name" >
<text>{{item.pname}}</text>
<view class="price-red">
<text>¥{{item.price}}</text>
<view class="lookNum" > <text>xx人看过</text>
</view>
</view>
</view>
</view>
<view class='placeholder-view'> </view>
</view>
上面的是我写的遍历{{goods}}中的数据并将商品对应的 图片、名称、价格等数据列出
列表循环:wx:for="{{数组或者对象}}" wx:for-item="循环项的名称" wx:for-index="循环项的索引"
wx-key="唯一的值" 用来提高列表渲染的性能
wx-key 绑定一个普通的字符串的时候,那么这个字符串名称肯定是循环数组中对象的唯一属性
wx-key=*this 就表示普通数组 *this表示循环项
当出现数组的嵌套循环的时候,尤其要注意,以下绑定的名称不要重名
wx:for-item="item" wx:for-index="index"(默认情况下不写)小程序默认循环项的名称和索引的名称为item和index
对象循环:wx:for-item="{{对象}}" wx:for-item="对象的值" wx:for-index="对象的属性"
循环对象的时候,最好改一下 wx:for-item="value" wx:for-index="key"
.wxss
常用样式
width:组件宽度。百分比情况下会对父组件的总宽度进行百分比换算。单位:1px=2rpx
height:组件高度。同上。
background-color:所有组件都可以设置背景颜色,取值:rgb(a,b,c)(a,b,c取值0~255);#000000~ffffff(十六进制取色),RED/BLUE/PINK(英文单词对应的各种颜色)
display:组件内的元素显示方式。通常设置为flex,使元素更加灵活。
flex-direction:设置为flex后,可以使用该属性。column为列排序;row为行排序。
align-items:各元素在盒子内的位置。center居中,flex-start居首,flex-end居尾。
justify-content:元素周围留白的方式。space-around各元素周围留白。space-between元素与元素之间留白。center元素居中在这里居中的话,align-items就会被屏蔽。
position:规定该元素/盒子定位方式。relative相对定位-以上一层盒子的左上角为(0,0),absolute绝对定位-以整个页面的左上角为(0,0)。fixed黏着定位-以整个页面的左上角为(0,0),不会随着屏幕的滚动而改变位置,悬浮窗、悬浮按钮之类的就是这样做的。
left、right、top、bottom:距离左、由、上、下盒子的距离,用来具体定位元素在盒子里的位置。单位同width。设置left的时候就没有必要设置right,因为你只需要让该组件距离left多少,而并不需要知道它距离right多少,同理top、bottom。
z-index:设置该元素/盒子的层次顺序,变量取数字0~无穷大,越大的排在视图的前面,低级的会被高级的遮挡住。悬浮窗、悬浮组件的实现同样需要将顺序设置为全局中最高的。
padding:变量取值: a b c d 。分别代表该元素距离所在盒子的左、上、右、下的内边距距离。也可padding-left/right/top/bottom:a 。具体规定某个方向的内边距距离。
margin:设置外边距。变量取值、具体设置、用法同上。
border-radius:变量取值:a b c d。代表左上、右上、右下、左下角的圆角框的半径。不设置的情况下为0,即该元素/组件为方角。
border:设置元素/盒子的边框样式。变量取值:a b c。a-边框宽度,b-边框样式,c-边框颜色。border是统一设置四个边框(左右上下)的属性,而border-left/right/top/bottom是具体规定某个边框的样式。
opacity:元素的不透明级别,取值0.0~1.0,对应完全透明~完全不透明。
font:设置字体属性,常用四种:font-size:大小;color:颜色;font-style:字体样式;font-width:字体粗细。
样式导入
使用WXSS提供的@import
语法,可以导入样式表。
个人总结
设置 <view> 111 </view>中的文字等的居中布局
例:
<view wx:if="{{cart.length === 0}}" class="empty-cart">购物袋为空</view>
这里写了一个判断如果购物车内容为空,就显示购物袋为空
.empty-cart{
position: relative;
height: 1180rpx;
width: 750rpx;
font-size: 50rpx;
left: 30%;
display: flex; //通过使用display: flex属性来创建一个弹性容器
align-items: center; //垂直居中
justify-content: center; //水平居中
}