页面
首页
分类
购物车
我的
搜索
商品列表
商品详情
微信支付
使用组件
tabBar
本程序中用到了首页、分类、购物车、我的
轮播图
<!-- 轮播图区域 -->
<swiper :indicator-dots="true" :autoplay="true" :interval="3000" :duration="1000" :circular="true">
<!-- 循环渲染轮播图的 item 项 -->
<swiper-item v-for="(item, i) in swiperList" :key="i">
<view class="swiper-item">
<!-- 动态绑定图片的 src 属性 -->
<image :src="item.image_src"></image>
</view>
</swiper-item>
</swiper>
页面跳转
<swiper-item v-for="(item, i) in swiperList" :key="i">
<navigator class="swiper-item" :url="'/subpkg/goods_detail/goods_detail?goods_id=' + item.goods_id">
<!-- 动态绑定图片的 src 属性 -->
<image :src="item.image_src"></image>
</navigator>
</swiper-item>
图片展示
<view class="left-img-box">
<image :src="item.product_list[0].image_src" :style="{width: item.product_list[0].image_width + 'rpx'}" mode="widthFix"> </image>
</view>
滚动视图
<!-- 左侧的滚动视图区域 -->
<scroll-view class="left-scroll-view" scroll-y :style="{height: wh + 'px'}">
<view class="left-scroll-view-item active">xxx</view>
<view class="left-scroll-view-item">xxx</view>
<view class="left-scroll-view-item">xxx</view>
<view class="left-scroll-view-item">xxx</view>
<view class="left-scroll-view-item">xxx</view>
<view class="left-scroll-view-item">多复制一些节点,演示纵向滚动效果...</view>
</scroll-view>
uni-icons
<!-- 使用 view 组件模拟 input 输入框的样式 -->
<view class="my-search-box">
<uni-icons type="search" size="17"></uni-icons>
<text class="placeholder">搜索</text>
</view>
uni-search-bar
<view class="search-box">
<!-- 使用 uni-ui 提供的搜索组件 -->
<uni-search-bar @input="input" :radius="100" cancelButton="none"></uni-search-bar>
</view>
uni-tag
<!-- 标题区域 -->
<view class="history-title">
<text>搜索历史</text>
<uni-icons type="trash" size="17"></uni-icons>
</view>
block 使用
<view>
<view class="goods-list">
<block v-for="(goods, i) in goodsList" :key="i">
<view class="goods-item">
<!-- 商品左侧图片区域 -->
<view class="goods-item-left">
<image :src="goods.goods_small_logo || defaultPic" class="goods-pic"></image>
</view>
<!-- 商品右侧信息区域 -->
<view class="goods-item-right">
<!-- 商品标题 -->
<view class="goods-name">{{goods.goods_name}}</view>
<view class="goods-info-box">
<!-- 商品价格 -->
<view class="goods-price">¥{{goods.goods_price}}</view>
</view>
</view>
</view>
</block>
</view>
</view>
uni-goods-nav
<!-- 商品导航组件 -->
<view class="goods_nav">
<!-- fill 控制右侧按钮的样式 -->
<!-- options 左侧按钮的配置项 -->
<!-- buttonGroup 右侧按钮的配置项 -->
<!-- click 左侧按钮的点击事件处理函数 -->
<!-- buttonClick 右侧按钮的点击事件处理函数 -->
<uni-goods-nav :fill="true" :options="options" :buttonGroup="buttonGroup" @click="onClick" @buttonClick="buttonClick" />
</view>
radio
<!-- 商品左侧图片区域 -->
<view class="goods-item-left">
<!-- 存储在购物车中的商品,包含 goods_state 属性,表示商品的勾选状态 -->
<radio :checked="goods.goods_state" color="#C00000" v-if="showRadio"></radio>
<image :src="goods.goods_small_logo || defaultPic" class="goods-pic"></image>
</view>
uni-number-box
<view class="goods-info-box">
<!-- 商品价格 -->
<view class="goods-price">¥{{goods.goods_price | tofixed}}</view>
<!-- 商品数量 -->
<uni-number-box :min="1"></uni-number-box>
</view>
uni-swipe-action 和 uni-swipe-action-item
<!-- 商品列表区域 -->
<!-- uni-swipe-action 是最外层包裹性质的容器 -->
<uni-swipe-action>
<block v-for="(goods, i) in cart" :key="i">
<!-- uni-swipe-action-item 可以为其子节点提供滑动操作的效果。需要通过 options 属性来指定操作按钮的配置信息 -->
<uni-swipe-action-item :options="options" @click="swipeActionClickHandler(goods)">
<my-goods :goods="goods" :show-radio="true" :show-num="true" @radio-change="radioChangeHandler" @num-change="numberChangeHandler"></my-goods>
</uni-swipe-action-item>
</block>
</uni-swipe-action>
button
<!-- 选择收货地址的盒子 -->
<view class="address-choose-box" v-if="JSON.stringify(address) === '{}'">
<button type="primary" size="mini" class="btnChooseAddress" @click="chooseAddress">请选择收货地址+</button>
</view>
样式美化
常用样式
display
justify-content
flex-direction
align-items
display
flex-wrap
top
z-index
text-align
line-height
position
transform
text-overflow
overflow
white-space
box-sizing
flex
&::
border-radius
box-shadow
使用颜色
技术点
下拉刷新
pages.json 中添加如下
{
"path" : "pages/cate/cate",
"style" :{
// "navigationBarTitleText": "",
"enablePullDownRefresh": false
}
},
页面中 methods 方法增加
onPullDownRefresh() {
this.queryObj.pagenum = 1
this.total = 0
this.isloading = false
this.goodsList = []
this.getGoodsList(()=> uni.stopPullDownRefresh())
}
上拉加载
page.json 中添加如下
{
"path" : "goods_list/goods_list",
"style" :
{
// "navigationBarTitleText": "",
"enablePullDownRefresh": true,
"onReachBottomDistance": 150,
"backgroundColor": "#f8f8f8"
}
}
页面方法中增加
onReachBottom() {
if (this.queryObj.pagenum * this.queryObj.pagesize >= this.total) return uni.$showMsg('数据加载完毕!')
if(this.isloading) return
this.queryObj.pagenum += 1
// console.log(this.queryObj.pagenum)
this.getGoodsList()
},
左移删除
使用组件 uni-swipe-action
<!-- 商品列表区域 -->
<!-- uni-swipe-action 是最外层包裹性质的容器 -->
<uni-swipe-action>
<block v-for="(goods, i) in cart" :key="i">
<!-- uni-swipe-action-item 可以为其子节点提供滑动操作的效果。需要通过 options 属性来指定操作按钮的配置信息 -->
<uni-swipe-action-item :options="options" @click="swipeActionClickHandler(goods)">
<my-goods :goods="goods" :show-radio="true" :show-num="true" @radio-change="radioChangeHandler" @num-change="numberChangeHandler"></my-goods>
</uni-swipe-action-item>
</block>
</uni-swipe-action>
methods 中实现删除功能
// 点击了滑动操作按钮
swipeActionClickHandler(goods) {
console.log(goods)
}
吸顶效果
封装组件
组件的构成:页面布局;
父页面如下:
<my-goods :goods="goods" :show-radio="true" @radio-change="radioChangeHandler" :showNum="true" @num-change="numberChangeHandler"></my-goods>
//商品的勾选状态发生了变化
radioChangeHandler(e){
this.updateGoodsState(e)
console.log("-------")
// console.log(e)
},
// 商品的数量发生了变化
numberChangeHandler(e){
this.updateGoodsCount(e)
console.log(e)
},
子页面如下:
<template>
<view class="goods-item">
<view class="goods-item-left">
<radio :checked="goods.goods_state" color="#c00" v-if="showRadio" @click="radioClickHandler"></radio>
<!-- <text>{{goods.goods_small_logo}}</text> -->
<image :src="goods.goods_small_logo || defaultPic" class="goods-pic"></image>
</view>
<view class="goods-item-right">
<view class="goods-name">{{goods.goods_name}}</view>
<view class="goods-info-box">
<view class="goods-price">${{goods.goods_price | tofixed}}</view>
<uni-number-box :min="1" :value="goods.goods_count" v-if="showNum" @change="numChangeHandler"></uni-number-box>
</view>
</view>
</view>
</template>
<script>
export default {
name:"my-goods",
props: {
goods: {
type: Object,
default: {},
},
// 是否展示图片左侧的 radio
showRadio: {
type: Boolean,
// 如果外界没有指定 show-radio 属性的值,则默认不展示 radio 组件
default: false
},
// 是否显示价格右侧的 NumberBox 组件
showNum:{
type: Boolean,
default: false,
},
},
data() {
return {
defaultPic: 'https://img3.doubanio.com/f/movie/8dd0c794499fe925ae2ae89ee30cd225750457b4/pics/movie/celebrity-default-medium.png',
};
},
filters: {
tofixed(num){
return Number(num).toFixed(2)
}
},
methods:{
//radio 组件 的点击事件处理函数
radioClickHandler(){
// 通过 this.$emit() 触发外界通过@绑定的 radio-change 事件,
//同时把商品的 id 和勾选状态作为参数传递给 radio-change 事件处理函数
this.$emit('radio-change',{
//商品 id
goods_id: this.goods.goods_id,
//商品最新的勾选状态
goods_state: !this.goods.goods_state
})
},
//NumberBox 组件的 change 事件处理函数
numChangeHandler(val){
console.log(val)
//通过 this.$emit()触发外界通过@ 绑定 num-change 事件
this.$emit('num-change',{
goods_id : this.goods.goods_id,
goods_count: +val
})
}
}
}
</script>
<style lang="scss">
</style>
其中:props中定义在子页面中使用的数据,方法使用 this.$emit 进行向父窗口传值。
分包
分包的目的:为了小程序首次加载时速度变快
pages.json 中增加
"subPackages": [
{
"root": "subpkg",
"pages": [],
}
]
在根目录下增加 subpkg 文件夹,增加新的页面
mixins
抽离出一个公共的 js 文件,方法进行徽标的设置
在根目录下新建一个 mixins 文件夹,新建一个 tabbar-badge.js 文件
import {mapGetters} from 'vuex'
export default{
computed: {
// 将 m_cart 模块中的 total 映射为当前页面的计算属性
...mapGetters('m_cart',['total'])
},
watch:{
total() {
this.setBadge()
}
},
onShow() {
//在页面展示的时候,设置数字徽标
this.setBadge()
},
methods: {
setBadge(){
//调用 uni.setTabBarBadger()方法,为购物车设置右上角的徽标
uni.setTabBarBadge({
index: 2, //索引
text: this.total + '' // 注意,text 的值必须是字符串,不能是数字
})
}
},
}
home.vue 页面进入导入
// 导入自己封装的 mixin 模块
import badgeMix from '@/mixins/tabbar-badge.js'
export default {
//将 badgeMix 混入到当前页面中进行使用
mixins: [badgeMix],
}
持久化
持久化数据都放在 store.js 中
import Vue from 'vue'
import Vuex from 'vuex'
import moduleCart from '@/store/cart.js'
import moduleUser from '@/store/user.js'
Vue.use(Vuex)
const store = new Vuex.Store({
modules:{
'm_cart': moduleCart,
'm_user': moduleUser
}
})
export default store
然后在每个 js 中进行各自类型的持久化,文件的结构如下。其中包括 state,mutation 和 getters
export default {
namespaced: true,
state: () => ({
cart: JSON.parse(uni.getStorageSync('cart') || '[]'),
}),
mutations: {
// 增加商品到购物车
addToCart(state,goods){
const findResult = state.cart.find(x => x.goods_id === goods.goods_id)
// console.log(findResult)
if(!findResult){
state.cart.push(goods)
}else{
findResult.goods_count++
}
// 通过 commit 方法,调用 m_cart 命名空间下 saveToStorage方法
this.commit('m_cart/saveToStorage')
},
// 将购物车的数据持久化存储到本地
saveToStorage(state){
// console.log(state.cart)
uni.setStorageSync('cart',JSON.stringify(state.cart))
},
},
getters: {
// 勾选的商品的总数量
checkedCount(state){
let checkedCount = state.cart.filter(x => x.goods_state).reduce((total,item) => total += item.goods_count,0)
return checkedCount
},
}
}
调用如下:
import {mapState,mapMutations} from 'vuex'
export default {
mixins: [badgeMix],
computed: {
...mapState('m_cart',['cart'])
},
data() {
return {
}
},
methods:{
...mapMutations('m_cart',['updateGoodsState','updateGoodsCount','removeGoodsById']),
//商品的勾选状态发生了变化
radioChangeHandler(e){
this.updateGoodsState(e)
console.log("-------")
// console.log(e)
},
}
}