让小程序跟h5样式一样
- 问题:小程序里搜索出来的结果没有一行显示,超出带省略号
- 原因:我们之前用的是标签选择器
span
,而在小程序中是没有span
这个标签的 - 解决办法:直接不要用标签选择器,就是把之前span去掉变成
::v-deep .uni-list-item__content-title {
display: inline-block;
width: 100%;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
- 推荐以后尽量要用类选择器
清空历史记录
- 就是给
删除的图标
加点击事件
<uni-icons @click="del" type="trash" size="18"></uni-icons>
- 点击事件里清空本地存储以及数组即可
// 删除图标的点击事件
del() {
// 光删本地存储界面不会变
uni.removeStorageSync('history')
// 还要把data里的数组清空
this.historyList = []
},
点击历史记录跳转到商品列表页
- 无非就是给每个历史记录的
tag
加点击事件,传入被点的历史,然后跳转到商品列表页,并携带被点的历史名作为参数
<uni-tag class="tag" v-for="item in historyList" :text="item" type="success" @click="toList(item)" />
toList(item) {
uni.navigateTo({
url: "/subpkg/goods_list/goods_list?query=" + item
})
},
作业:搜索历史如果存在就提前
- 思路:
- 先拿到输入的内容在数组中的下标,判断这个下标是否不等于
-1
,不等于-1
代表要搜索的内容在里面,在里面我就先把以前的删掉,再加到最前面。就相当于把这个提前了
- 先拿到输入的内容在数组中的下标,判断这个下标是否不等于
- 代码:
// 发请求之前我先把历史记录存起来
// 拿到下标
const index = this.historyList.indexOf(this.key)
// 判断在里面
if (index !== -1) {
this.historyList.splice(index, 1)
}
// 先把输入的内容加到数组里
this.historyList.unshift(this.key)
// 把数组存到本地
uni.setStorageSync('history', this.historyList)
search - 分支处理
# 把改动添加到暂存区
git add .
# 提交成一条记录
git commit -m'search完成'
# 切换到dev
git checkout dev
# 合并到dev来
git merge search
# 推送dev
git push
# 可选择---删除本地的分支
git branch -d search
goodslist 分支准备
运行如下的命令,基于 master 分支在本地创建 goodslist 子分支,用来开发商品列表相关的功能:
git checkout -b goodslist
商品列表 - 准备商品列表的结构和样式
- 如上图分析,每个商品应该是一个大vie,大view里分为一左一右,左边是图片,右边又是一个view,右边view里放两个text,到时候一上一下
<view class="goods-item">
<!-- 左边图片 -->
<image class="pic"
src="http://image3.suning.cn/uimg/b2c/newcatentries/0000000000-000000000614133369_1_400x400.jpg">
</image>
<!-- 右边的盒子 -->
<view class="info-box">
<text class="title">我是标题</text>
<text class="price">¥ 79.00</text>
</view>
</view>
- 样式:大盒子用弹性布局,图片给宽高。右边的view也给弹性布局,只不过要改方向为一上一下、并且排列方式为一个起点,一个终点,最后就是价格颜色变红
.goods-item {
display: flex;
margin: 20rpx;
.pic {
width: 300rpx;
height: 300rpx;
}
.info-box {
display: flex;
flex-direction: column;
justify-content: space-between;
.price {
color: #c00;
}
}
}
商品列表 - 发请求获取列表数据
- 因为这个接口要传递参数,所以先声明一个参数对象,用来准备参数
data () {
return {
params:{
query: '',
cid: '',
pagenum: 1,
pagesize:10
}
}
}
- 来到商品列表一般都会携带一个参数,代表要查询的内容,所以得接收这个参数,所以写了onLoad来接收
onLoad (options) {
// 把传递过来的参数存起来
this.params.query = options.query
this.params.cid = options.cid
}
- 然后封装一个发请求的方法,在这之前可以在
data
中声明一个数组用来保存请求到的结果
data () {
return {
......
goodsList: []
}
}
methods: {
async getGoodsList() {
const res = await uni.$http.get('goods/search', this.params)
this.goodsList = res.data.message.goods
}
}
- 最后去
onLoad
里调用
onLoad (options) {
.........
this.getGoodsList()
}
商品列表 - 渲染数据
- 对着之前写好的结构
v-for
把对应的属性渲染到对应的地方
<template>
<view>
<view class="goods-item" v-for="item in goodsList" :key="item.goods_id">
<!-- 左边图片 -->
<image class="pic" :src="item.goods_small_logo">
</image>
<!-- 右边的盒子 -->
<view class="info-box">
<text class="title">{{ item.goods_name }}</text>
<text class="price">¥ {{ item.goods_price }}</text>
</view>
</view>
</view>
</template>
- 染后发现右侧文字把左边图片挤压了,原因是右侧是纯文字,不给宽度会拉伸盒子,所以给右侧盒子加
flex:1
相当于给个宽度
.info-box {
flex: 1;
......
}
商品列表 - 设置默认图片以及处理价格小数点
- 默认图片地址:
- 因为有些商品是没有图片的,所以导致
goods_small_logo
为空,那就没有图片显示,而我们需要如果没图片显示就给一张默认图
<image class="pic"
:src="item.goods_small_logo || 'https://img3.doubanio.com/f/movie/8dd0c794499fe925ae2ae89ee30cd225750457b4/pics/movie/celebrity-default-medium.png'">
</image>
- 最后让价格保留两位小数即可
<text class="price">¥ {{ item.goods_price.toFixed(2) }}</text>
商品列表 - 上拉加载更多
- 一般app也不会让你拉到底才去加载,所以要设置触底距离,稍微大一点
- 来到
pages.json
(这个文件既有全局配置,也有局部的页面配置),找到分包里的goods_list
,设置上拉触底距离
{
"path": "goods_list/goods_list",
"style": {
"enablePullDownRefresh": false,
"onReachBottomDistance": 230
}
},
- 来到`goods_list`写上拉触底的回调:先让页码+1,再调用请求
// 触底触发的钩子
onReachBottom() {
// 让页码+1
this.params.pagenum++
// 发请求加载数据
this.getGoodsList()
},
- 因为之前是对数组进行赋值,那么会导致,每次加载新的都会前面的给覆盖,所以修改 `getGoodsList`里的方法,改成push添加
- 并且因为需要判断是否加载完成,所以data里先声明一个变量用来记录有没完成
data () {
return {
finished: false
}
}
- 修改`getGoodsList`方法
// 加载数据的方法
async getGoodsList() {
const res = await uni.$http.get('goods/search', this.params)
// 每次追加
this.goodsList.push(...res.data.message.goods)
// 加载完数据都要判断是否加载完毕
this.finished = this.goodsList.length >= res.data.message.total
}
- 然后在上拉触底触发的钩子里要判断如果完成了,就不往下执行,且弹出提示
// 触底触发的钩子
onReachBottom() {
// 判断是否完成
if (this.finished) return uni.showToast({
title: '数据加载完毕',
icon: 'none'
})
// 让页码+1
this.params.pagenum++
// 发请求加载数据
this.getGoodsList()
},
商品列表 - 上拉加载更多-节流阀
- 先data里声明一个节流阀变量
data () {
return {
isLoading: false
}
}
- 发请求前变true,发完请求变false
// 加载数据的方法
async getGoodsList() {
this.isLoading = true
.......
// 发完请求就变false
this.isLoading = false
// 加载完数据都要判断是否加载完毕
this.finished = this.goodsList.length >= res.data.message.total
}
- 然后上拉钩子里做判断,当前如果正在发请求则return
// 触底触发的钩子
onReachBottom() {
if (this.isLoading) return
// 判断是否完成
if (this.finished) return uni.showToast({
title: '数据加载完毕',
icon: 'none'
})
// 让页码+1
this.params.pagenum++
// 发请求加载数据
this.getGoodsList()
},
小程序里发请求参数的说明
- 在网页中,如果参数写undefined,浏览器会自动优化成空参数,而小程序没有这个优化,所以导致如果是undefined,那么也给你传递到接口里了
- 而我们接口的要求的是,要么就传值,要么就给空。而你给个undefined它表示找不到商品,所以小程序里返回空数组
- 如何处理?
- 不管有没有优化成空,我就干脆自己手动判断如果是undefined,就改空字符串
- 来到 onl oad
// onl oad有形参,形参里就有传递过来的数据
onl oad(options) {
// 把传递过来的参数存起来
this.params.query = options.query || ''
this.params.cid = options.cid || ''
// 调用请求方法
this.getGoodsList()
},
商品列表 - 下拉刷新
- 下拉刷新需要先改配置开启,所以来到
pages.json
,找到goods_list
开启
{
"path": "goods_list/goods_list",
"style": {
"enablePullDownRefresh": true,
"onReachBottomDistance": 230
}
- 然后回到
goods_list
页面,监听下拉,在下拉里把数据恢复并重新请求
// 监听下拉刷新的钩子
onPullDownRefresh() {
// 重新加载数据
this.params.pagenum = 1
// 数组要清空
this.goodsList = []
// 加载状态都变成完成了,所以要恢复
this.finished = false
// 发请求加载数据
this.getGoodsList()
},
- 最后在请求完成后,手动停止下拉状态
async getGoodsList() {
...................
...................
...................
// 停止下拉状态
uni.stopPullDownRefresh()
}
商品列表 - 点击商品跳转到详情页
- 来到
goods_list
,给每个商品盒子加点击事件
<view ..... @click="toDetail(item)">
- 点击事件里跳转到商品详情并携带id
// 每个商品的点击事件
toDetail(item) {
uni.navigateTo({
url: '/subpkg/goods_detail/goods_detail?goods_id=' + item.goods_id
})
},
商品列表 - 分支合并与提交
- 将
goodslist
分支进行本地提交:
git add .
git commit -m "完成了商品列表页面的开发"
- 将本地的
goodslist
分支推送到码云:
git push -u origin goodslist
- 将本地
goodslist
分支中的代码合并到master
分支:
git checkout dev
git merge goodslist
git push
- 删除本地的
goodslist
分支:
git branch -d goodslist
商品详情 - 创建分支
运行如下的命令,基于 master 分支在本地创建 goodsdetail 子分支,用来开发商品详情页相关的功能:
git checkout -b goodsdetail
商品详情 - 获取详情数据
- 建议添加编译命令:goods_id=47866
- 来到
goods_detail
页面,声明变量接收传递过来的id和声明变量用来保存获取到的详情数据
data() {
return {
detail: {}
};
},
- 在
onLoad
里拿到传递过来的id,并调用发请求的方法
async onl oad(options) {
const res = await uni.$http.get('goods/detail', {
goods_id: options.goods_id
})
this.detail = res.data.message
},
商品详情 - 渲染轮播图
<!-- 轮播图区域--快捷键:uswiper -->
<swiper class="swiper" :indicator-dots="true" :autoplay="true" :interval="3000" :duration="1000">
<swiper-item v-for="item in detail.pics">
<image class="swiper-pic" :src="item.pics_sma"></image>
</swiper-item>
</swiper>
.swiper {
height: 500rpx;
.swiper-pic {
width: 100%;
height: 100%;
}
}
商品详情 - 预览效果
- 给轮播图的image加点击事件,需要传入当前的下标
<swiper-item v-for="(item, index) in detail.pics" @click="showPreview(index)">
<image class="swiper-pic" :src="item.pics_sma"></image>
</swiper-item>
- 点击事件里,调用预览的方法即可(提取出pics里所有的大图产生一个新的数组给urls)
// 预览的点击事件
showPreview (index) {
// 图片预览
uni.previewImage({
current:index,
urls: this.detail.pics.map(v => v.pics_big)
})
},
商品详情 - 渲染商品信息区域
- 如上图布局即可
<!-- 信息区域 -->
<view class="info-box">
<view class="price">
¥ 89
</view>
<view class="title-box">
<view class="title">
韵语孕期全包裹托腹带 韵语孕妇托腹腰带 孕产妇服饰舒适透气薄款托腹带
</view>
<view class="icon-box">
<uni-icons size="25" type="star"></uni-icons>
<view>
收藏
</view>
</view>
</view>
<view class="yf">
快递:免运费
</view>
</view>
- 样式如下
.info-box {
margin: 10px;
.price {
font-size: 18px;
font-weight: 700;
color: #c00;
}
.title-box {
margin: 10px 0;
display: flex;
.icon-box {
width: 240rpx;
text-align: center;
}
}
.yf {
color: #ccc;
font-size: 13px;
}
}
商品详情 - 渲染商品详细内容
- 发现商品详情其实就是服务器返回的一段富文本,所以直接用即可
<rich-text :nodes="detail.goods_introduce"></rich-text>
商品详情 - 解决图片多出空格问题
- 多出空格的原因:图片默认是基线对齐,所以会空出大约4px的高度
- 解决办法1:设置对齐方式为top、middle等等都行,只要不是baseline
- 解决办法2:直接把图片转成块级元素
- 因此我们需要发请求拿到数据后,先对数据做改良,然后再赋值即可
// 在渲染之前把所有的<img替换成 <img style="display:block"
res.message.goods_introduce = res.message.goods_introduce.replaceAll('<img', '<img style="display:block" ')
this.detail= res.message
商品详情 - 渲染底部购物车区域
- 其实主要就是用
uni-ui
提供的商品导航
- 来到详情页,在最后加一个view,view里包一个商品导航
- options是用来设置左侧的图标
- button-group是用来设置右侧的按钮
<view class="nav-bottom">
<!-- options绑定左侧图标的 -->
<uni-goods-nav :fill="true" :options="options" :button-group="customButtonGroup" />
</view>
options: [{
icon: 'shop',
text: '店铺'
}, {
icon: 'cart',
text: '购物车',
info: 2
}],
customButtonGroup: [{
text: '加入购物车',
backgroundColor: '#f00',
color: '#fff'
},
{
text: '立即购买',
backgroundColor: '#ffa200',
color: '#fff'
}
],
- 设置它为固定定位到底部
.nav-bottom {
position: fixed;
left: 0;
bottom:0;
width:100%;
}
- 此时底部购物车区域会挡住一部分内容,所以需要给上面整体的内容加一个大盒子包起来,然后让大盒子加一个
padding-bottom:50px
即可
<view style="padding-bottom:50px;">
<!-- 这里包内容 -->
</view>
商品详情 - 点击跳转到购物车页面
-
经过查阅文档,发现上面的
商品导航组件
有两个事件,一个叫click
,代表点击左侧任意图标触发,还有一个事件叫buttonClick
,点击右侧任何按钮触发 -
通过事件调用的函数里的参数可以区分是谁被点
ge
- 给购物车加
click
事件(上面已经写好了),然后写一个函数即可
onClick(e) {
// 通过e可以区分点的是哪一个
if (e.index === 1) {
// 跳转到购物车页面
uni.switchTab({
url: '/pages/cart/cart'
})
}
},
商品详情 - 分支与合并
- 将
goodsdetail
分支进行本地提交:
git add .
git commit -m "完成了商品详情页面的开发"
- 将本地的
goodsdetail
分支推送到码云:
git push -u origin goodsdetail
- 将本地
goodsdetail
分支中的代码合并到master
分支:
git checkout master
git merge goodsdetail
git push
- 删除本地的
goodsdetail
分支:
git branch -d goodsdetail
购物车 - 分支准备
运行如下的命令,基于 master 分支在本地创建 cart 子分支,用来开发购物车相关的功能:
git checkout -b cart
购物车 - 准备vuex管理购物车数据
- 来到项目根目录新建
store
文件夹,里面放一个index.js,代码如下
// 导入Vue
import Vue from 'vue'
// 导入Vuex
import Vuex from 'vuex'
// 安装vuex
Vue.use(Vuex)
// 实例化store对象
const store = new Vuex.Store({
// 注册子模块的地方
modules: {
}
})
// 把store对象暴露出去
export default store
- 来到
main.js
导入上面的store,并挂在到vue的根实例
import store from './store'
const app = new Vue({
store,
...App
})
- 来到
store
文件夹新建modules
文件夹,里面放一个cart.js
,代码如下
export default {
// 开启命名空间
namespaced: true,
state() {
return {
// 购物车是一个数组,数组里每个元素是一个对象
// 对象里至少要存:商品id、商品名字、商品价格、商品数量、商品图片、商品是否选中
cartList: []
}
},
mutations: {
}
}
- 来到
index.js
做导入和注册
import cart from './modules/cart.js'
// 实例化store对象
const store = new Vuex.Store({
// 注册子模块的地方
modules: {
cart
}
})
购物车 - 实现添加购物车功能
- 因为要修改到vuex里的数据,所以就要提供一个mutations,这个mutations的方法,只是实现添加购物车,也就是把一个商品加到购物车里,那相当于只是要让数组多一个元素,所以没必要替换,而是只是传一个商品,加到数组里
mutations: {
addGoods (state, goods) {
state.cartList.push(goods)
}
}
- 回到
goods_detail
页,给添加购物车加点击事件(之前有绑定,但是还没写方法,现在只要写方法)
buttonClick(e) {
// 代表是加入购物车
if (e.index === 0) {
// 如果点的是添加购物车,就创建一个对象,给vuex里的方法
this.$store.commit('cart/addGoods', {
goods_id: this.detail.goods_id,
goods_name: this.detail.goods_name,
goods_price: this.detail.goods_price,
goods_small_logo: this.detail.goods_small_logo,
// 写死true代表默认选中
goods_checked: true,
// 代表默认只有1个数量
goods_count: 1
})
}
},
- 此时发现,添加购物车确实能添加进去,但是如果我对同一个商品点多次添加,它会重复添加多个到数组里,而我们要的逻辑是: 如果在数组里,只要让数量+1,不在数组里才加到数组里,所以修改
mutations
里的逻辑
// 把要添加的商品传进来
addGoods(state, goods) {
// 判断当前商品在不在数组里
// 左边的v代表当前数组里的每一项,右边的代表当前要添加的商品的id
// 通过id来找,如果找到了会得到这个元素,没找到会得到undefined
const item = state.cartList.find(v => v.goods_id === goods.goods_id)
if (item) { // 只有0、空字符串、null、undefined、NaN会得到false,其他都是true
// 如果在就只要让数量+1
item.goods_count++
} else {
// 如果不在就是加到数组里
state.cartList.push(goods)
}
}
购物车 - 使用getters动态统计购物车商品总数量
- 来到
modules/cart.js
封装一个getters统计总数量
getters: {
total(state) {
// 统计出总数量--要统计出选中的数量
let count = 0
state.cartList.forEach(v => {
if (v.goods_checked) {
count += v.goods_count
}
})
// 把统计的数量返回出去
return count
}
},
购物车 - 使用计算属性把总数量赋值到界面
- 先导入
mapGetters
然后做计算属性的映射
import { mapGetters } from 'vuex'
export default {
computed: {
...mapGetters('cart', ['total'])
},
}
- 侦听
total
的改变,一旦改变就赋值给info
// 侦听器
watch:{
total () {
// 把购物车总数量赋值给徽标
this.options[1].info = this.total
}
}
购物车 - 持久化购物车数据
- 也就是来到
store/modules/cart.js
,找到addGoods
方法,在操作完数据后,立即缓存到本地
addGoods(state, goods) {
......
// 把当前购物车缓存到本地
uni.setStorageSync('cart', state.cartList)
}
- 然后给
state
默认值
state() {
return {
// 购物车是一个数组,数组里每个元素是一个对象
// 对象里至少要存:商品id、商品名字、商品价格、商品数量、商品图片、商品是否选中
cartList: uni.getStorageSync('cart') || []
}
},
- 解决页面一打开购物车不显示徽标的问题
- 问题原因: 只有当total发生改变,才会对徽标进行赋值,而我们需要页面一打开就自动执行
- 所以就只要把侦听器改成完整版,让他页面一打开就默认执行
watch:{
total: {
handler (newVal) {
this.options[1].info = newVal
},
// 开启页面一打开就默认执行一次
immediate: true
}
}
标签:goods,数组,购物车,商品,徽标,git,tabbar,uni
From: https://www.cnblogs.com/strundent/p/17078365.html