1.创建四个页面:选择scss
分别是home、cate、cart、my
2.在pages.json里面创建文件
{
"path":"pages/cart/cart",
"style":{
"enablePullDownRefresh":false
}
}
3.创建tabar页面,和pages同级
"tabBar": {
"selectedColor":"#ea4350" // 设置小程序选中的颜色
"list": [
{
"pagePath": "pages/home/home",
"text":"首页",
"iconPath": "static/images/index.png",
"selectedIconPath": "static/images/index_selected.png"
}
]
},
4.配置request合法域名(https相关域名)
5.没有https合法域名可以先不设置
6.小程序发起请求,在小程序onLoad生命周期里
onLoad(){
uni.request({
url:'https://api-hmugo-web.itheima.net/api/public/...',
method:'GET',
success:res=>{
console.log(res)
}
})
}
7.小程序不支持axios
由于平台的限制,小程序项目中不支持axios,而且原生的wx.request() API功能较为简单,不支持拦截器等全局定制的功能。因此,建议在uni-app项目中使用 @escook/request-miniprogram 第三方包发起网络请求。
(1)因为没有package.json文件,所以要先创建一个package.json文件,在终端中输入npm init -y命令 (2)下载包:@escook/request-miniprogram
8.在main.js里配置全部信息
9.配置信息
import Vue from 'vue'
import App from './App'
// 配置请求包
// 1.引入
import {$http} from '@escook/request-miniprogram'
// 2.设置请求的根路径
$http.baseUrl = 'https://api-hmugo-web.itheima.net'
// 3.设置拦截器 - 设置加载弹窗
$http.beforeRequest = function(){
// 打开弹窗提示用户正在加载
uni.showLoading({
title:'加载中...'
})
}
$http.afterRequest = function(){
// 关闭弹窗
uni.hideLoading()
}
// 4.将$http挂载到全局
uni.$http = $http
Vue.config.productionTip = false
App.mpType = 'app'
const app = new Vue({
...App
})
app.$mount()
10.请求失败后可以弹窗提示用户错了
// 判断请求是否成功
if(meta.status!==200){
// 提示用户
uni.showToast({
title:'错了',
icon:'none'
})
}
和API相关的用小程序(比如:小程序的支付接口、小程序的弹窗方法),和语法相关的用vue(比如:保存一个值、循环)
<template>
<view>
home
</view>
</template>
<script>
export default {
data() {
return {
swiperList:[]
}
},
onLoad(){
this.getSwiperList()
},
methods:{
// 获取轮播图
async getSwiperList(){
const {data:{message,meta}}=await uni.$http.get('/api/public/v1/home/swiperdata')
// 判断请求是否成功
if(meta.status!==200){
// 判断请求是否成功
if(meta.status !==200){
// 提示用户
uni.showToast({
title:'错了',
icon:'none'
})
return
}
this.swiperList = message
}
console.log(message)
}
},
}
</script>
<style lang="scss">
</style>
11.在main.js里面封装全局弹窗
// 封装弹窗,在 将$http挂载到全局 后面
uni.$showMsg = function(title='获取失败...'){
uni.showToast({
title,
icon:'none'
})
}
判断请求失败后调用
if(meta.status!==200) return uni.$showMsg()
12.home页面轮播图
<!-- 轮播图 -->
<swiper circular :indicator-dots="true" :autoplay="true" :interval="3000" :duration="1000">
<swiper-item v-for="(item,i) in swiperList" :key="i">
<image class="swiper-img" :src="item.image_src"></image>
</swiper-item>
</swiper>
轮播图样式:
.swiper-img{
width: 100%;
height: 340rpx;
}
13.小程序采用分包进行优化,用户不一定会看的页面先不下载就用分包,首页一定会看的页面就给你下载
创建subpkg页面,再创建goods-detail页面,会自动生成pages里面的内容会自动生成
在pages.json里面进行设置
"subPackages": [
{
"root":"subpkg",
"pages": [{
"path" : "goods-detail/goods-detail",
"style" :
{
"enablePullDownRefresh": false
}
}
]
}
],
14.点击首页轮播图跳转到详情页面( 在轮播图外面加上navigator标签进行跳转 )
// 跳转到tabBar页面必须用 open-type="switchTab" 声明是switchTab,这个页面是跳转到非tabBar页面,所以不用写了
<navigator url="/subpkg/goods-detail/goods-detail">
<image class="swiper-img" :src="item.image_src"></image>
</navigator>
15.轮播图跳转时携带页面参数
<navigator :url="`/subpkg/goods-detail/goods-detail?goods_id=${item.goods_id}`">
<image class="swiper-img" :src="item.image_src"></image>
</navigator>
16.获取导航的数据
<!-- 导航 -->
<view class="nav-list">
<view class="nav-item" v-for="item,index in navList" :key="index">
<image :src="item.image_src"></image>
</view>
</view>
// 获取导航数据
async getNavList(){
const {data:{message,meta}} = await uni.$http.get('/api/public/v1/home/catitems')
// 判断请求是否成功
if(meta.status !==200)return uni.$showMsg()
console.log(message)
},
.nav-list{
display: flex;
justify-content: space-around;
margin-top: 6px 0;
.nav-item{
width: 128rpx;
height: 140rpx;
image{
width: 100%;
height: 100%;
}
}
}
17.点击分类进行跳转,因为点击分类时,传的分类的index是0,所以当index=0时进行跳转
<image :src="item.image_src" @click="goCate(index)"></image>
goCate(index){
if(index===0){
// 跳转到 tabBar 页面
uni.switchTab({
url:'/pages/cate/cate'
})
}
}
18.楼层的渲染
image图片里面有这样一个属性,mode="widthFix"根据宽度自适应图片高度
<!-- 楼层 -->
<view class="floor-list">
<view class="floor-item" v-for="item,index in floorList" :key="index">
<!-- 标题 -->
<view class="floor-title">
<image :src="item.floor_title.image_src" mode=""></image>
</view>
<!-- 内容 -->
<view class="floor-content">
<!-- 左 -->
<view class="left">
<image mode="widthFix" :style="{width:item.product_list[0].image_width+'rpx'}" :src="item.product_list[0].image_src"></image>
</view>
<!-- 右 -->
<view class="right">
<image mode="widthFix" :style="{width:e.image_width+'rpx'}" v-for="e,i in item.product_list" v-if="i!==0" :key="i" :src="e.image_src"></image>
</view>
</view>
</view>
</view>
// 获取楼层数据
async getFloorList(){
const {data:{message,meta }} = await uni.$http.get('/api/public/v1/home/floordata')
// 判断请求是否成功
if(meta.status !==200)return uni.$showMsg()
this.floorList = message
},
// 楼层样式
.floor-title{
image{
width: 100%;
height: 59rpx;
}
}
.floor-content{
display: flex;
padding:6px;
.right{
margin-left:6px;
display: flex;
justify-content: space-between;
flex-wrap: wrap;
}
}
19.点击楼层图片进行跳转
获取到的楼层数据:
(1) 跳转时需要对楼层数据navigator_url进行切割
(2) 将当前项传递过去,点击事件写在图片上,view上面也行
(3)跳转到商品列表页面事件
cate分类页面
19.实现左右两边的滚动效果
必须设置滚动的高度,最好是直接设置成设备的高度,设备高度的获取方法是(使用uni.getSystemInfoSync()):
data(){
return {
wh:0
}
}
onLoad(){
// 获取设备的屏幕高度
const res = uni.getSystemInfoSync()
this.wh = res.windowHeight
}
在tamplate里面通过: :style="{ height:wh + 'px' }"
<!-- 内容区域 -->
<view class="content-box">
<!-- 左边 -->
<scroll-view :style="{ height:wh + 'px' } " scroll-y="true" class="left-scroll scroll-box">
<view>1</view>
// 此处省略一百个盒子
</scroll-view>
<!-- 右边 -->
<scroll-view :style="{ height:wh + 'px' } " scroll-y="true">
<view>1</view>
<view>2</view>
<view>3</view>
<view>4</view>
<view>5</view>
<view>1</view>
<view>2</view>
<view>3</view>
<view>4</view>
<view>5</view>
<view>1</view>
<view>2</view>
<view>3</view>
<view>4</view>
<view>5</view>
</scroll-view>
</view>
css样式
.content-box{
display: flex;
}
20.使用v-for将元素循环渲染出来
添加样式:
.left{
width: 90px;
view{
font-size:12px;
line-height: 60px;
text-align: center;
background-color: #f7f7f7;
}
}
21.可以在uni.scss里面设置全局样式
22.左侧点击时的active样式,使用伪类选择器写
// 小红线盒子样式
.active{
background-color: #fff;
position:relative;
&::before{
content:'';
position: absolute;
left:0;
top:50%;
margin-top:-15px;
height: 30px;
width: 3px;
background-color: #ea4350;
}
}
23.点击切换右边的内容
因为右边的内容是由cateList2确定的,而cateList2的值为某一项的children,所以在进行点击切换右侧内容时只需要将当前项的值传过去,然后将当前项的children给到cateList2即可
//切换分类
changeCate(item){
// 切换高亮样式
// 切换右侧内容
this.cateList2 = item.children
},
24.切换高亮样式
设置一个current:0,将循环中的index的值给到current,如果点击的是某一项,则index===current,就将active给到这一项,否则为空''
<scroll-view :style="{ height:wh + 'px' } " scroll-y="true" class="left scroll-box">
<view :class="[current===index ? 'active':'']" @click="changeCate(item,index)" v-for="item,index in cateList" :key="index">{{item.cat_name}}</view>
</scroll-view>
data(){
return{
current:0
}
}
//切换分类
changeCate(item,index){
// 切换高亮样式
this.current = index
// 切换右侧内容
this.cateList2 = item.children
},
25.解决右边滚动位置的问题
在scroll-view里面有一个属性scroll-top用来设置竖向滚动条的位置
在右侧滚动条的scroll-view里面设置:
没有给滚动条赋值,所以他的值一直会是0,则需要在0.1和0之间反复横跳才能保证每点击左侧因此右侧页面从头开始显示:
26.点击图片跳转到商品详情页面
//跳转到商品列表页面
goList(item){ //传递的是e,相当于message下面的children
uni.navigateTo({
url:'/subpkg/goods-list/goods-list?cat_id='+item.cat_id
})
},
27.封装搜索组件
(1)创建组件search-bar
使用scss的模板组件,创建同名目录,点击确定
(2)使用组件时可以不导入和注册,直接在页面上使用
(3)search-bar搜索组件页面代码
<template>
<view>
<view class="search-bar">
<view class="search-input">
<uni-icons type="search"></uni-icons>
<text>搜索</text>
</view>
</view>
</view>
</template>
<script>
export default {
name:"search-bar",
data() {
return {
};
}
}
</script>
<style lang="scss">
.search-bar{
display: flex;
justify-content: center;
align-items: center;
height: 50px;
background-color: #ea4350;
.search-input{
display: flex;
justify-content: center;
align-items: center;
font-size: 12px;
width: 95%;
height: 35px;
border-radius: 30px;
background-color: #fff;
}
}
</style>
28.解决搜索组件在首页和分类页面的bug
(1)解决首页面搜索框吸顶的问题
<view class="search">
<search-bar></search-bar>
</view>
fixed会脱离文档流,但是sticky不会脱离文档流
.search{
position: sticky;
top:0;
left:0;
z-index:999;
}
(2)解决分类页面展示不完全的问题
因为搜索页面高度是50px,所以在分类页面获取设备高度后需要减去50
29.点击搜索标签进行跳转
在cate页面也是一样
<search-bar @click="goSearch"></search-bar>
// 跳转到搜索页面
goSearch(){
uni.navigateTo({
url:'/subpkg/search/search'
})
},
search.vue页面
30.搜索的样式
使用uniapp的内置组件uni-search-bar
搜索框的背景组件不知道可以直接在微信开发工具的Wxml里用鼠标箭头选中搜索框来查看
<template>
<view>
<uni-search-bar radius="30" cancelButton="none" placeholder="搜索"></uni-search-bar>
</view>
</template>
<script>
export default {
data() {
return {
}
},
methods: {
}
}
</script>
<style lang="scss">
.uni-searchbar{
background-color: #ea4350;
}
</style>
31.搜索历史的样式
<!-- 搜索历史 -->
<view class="history">
<!-- 标题 -->
<view class="title">
<text>搜索历史</text>
<uni-icons type="trash" size="20"></uni-icons>
</view>
</view>
.history{
.title{
height:40px;
border-bottom:1px solid #eee;
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 6px;
font-size: 12px;
}
}
32.标签样式
代码结构和样式:
33.点击搜索框发送请求
<uni-search-bar @input="search" radius="30" cancelButton="none" placeholder="搜索"></uni-search-bar>
// 搜索 这个val就是用户输入的值
async search(val){
const {data:{message,meta}} = await uni.$http.get('/api/public/v1/goods/qsearch',{query:val})
if(meta.status!==200) return uni.$showMsg()
this.searchList = message
console.log(message)
}
34.搜索框优化
(1) 当用户输入为空时,直接return返回
(2) 节流处理
使用定时器,每500ms没有输入内容就触发定时器发送请求,在发送请求前要清除上一次的定时器
data() {
return {
searchList:[],
timer:null
}
},
methods: {
// 搜索 这个val就是用户输入的值
search(val){
// 清理上一次的定时器
clearTimeout(this.timer)
// 判断用户输入是否为空
if(val.trim()===''){
this.searchList = []
return
}
this.timer = setTimeout(async ()=>{
const {data:{message,meta}} = await uni.$http.get('/api/public/v1/goods/qsearch',{query:val})
if(meta.status!==200) return uni.$showMsg()
this.searchList = message
console.log(message)
},500)
}
35.搜索历史和搜索列表的切换
在data里面创建一个新的字符串kw:'',当搜索框为空时(v-if="kw===''")显示搜索历史,有值(v-else)时显示搜索列表
36.搜索列表样式
<!-- 搜索列表 -->
<view class="search-list" v-else>
<view class="search-item" v-for="item,index in searchList" :key="index">
<text>{{item.goods_name}}</text>
<uni-icons type="right"></uni-icons>
</view>
</view>
溢出用...表示在这里一定要包在text里面
.search-list{
.search-item{
height: 40px;
border-bottom:1px solid #eee;
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 6px;
font-size:12px;
// 溢出用...表示
text{
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}
}
36.搜索历史 (1) 在data里面创建一个假的数组
history:['apple','app','a']
(2)在页面上进行渲染
<!-- 内容 -->
<view class="content">
<uni-tag v-for="item,index in history" :key="index" type="warning" inverted="true" :text="item"></uni-tag>
</view>
(3)在页面接口调用后调用搜索历史方法
(4)搜索历史
// 搜索历史
saveHistory(){
// [app,apple]
const set=new Set(this.history)
// 使用set.delete删除一个不存在的数据也不会报错
set.delete(this.kw)
set.add(this.kw)
this.history = Array.from(set)
},
(5)持久化处理
(6) 翻转数组
在计算属性里面做处理
computed:{
reHistory(){
return [...this.history].reverse()
}
},
最后更改循环渲染的值:
(7)点击搜索历史和商品列表进行跳转
goods-list商品列表页面
37.商品列表页面
在onLoad里面获取传过来的商品参数,其中onLoad里面的参数就是传递过来的参数
onLoad(options){
// onLoad的形参就是跳转页面传递的参数,如果当前页面是tabBar页面,无法接收参数
console.log(options)
this.params.query = options.query || ''
this.params.cid = options.cat_id || ''
this.getGoodsList()
},
获取商品列表页面:
// 获取商品列表
async getGoodsList(){
const {data:{message,meta}} = await uni.$http.get('/api/public/v1/goods/search',this.params)
if(meta.status !== 200)return uni.$showMsg()
this.goodsList = message.goods
this.total = message.total
}
38.渲染页面结构
<template>
<view>
<!-- 列表项 -->
<view class="goods-item" v-for="item,index in goodsList" :key="index">
<!-- 左 -->
<view class="left">
<image :src="item.goods_small_logo"></image>
</view>
<!-- 右 -->
<view class="right">
<!-- 上 -->
<view class="title">{{item.goods_name}}</view>
<!-- 下 -->
<view class="bottom">
<view class="price">¥{{item.goods_price}}</view>
</view>
</view>
</view>
</view>
</template>
39.设置默认图片
40.添加价格过滤器(保留两位小数)
使用管道符|
filters:{
priceFormat(val){
return val.toFixed(2)
}
}
使用:
41.将列表项封装成组件
(1)在components里面新建组件my-goods,使用scss的组件并且创建同名的目录
(2)将template里面的数据全部剪切过来,样式也全部剪切过来,还有defaultImageSrc的默认图片以及过滤器。
(3)将需要循环的goodsList数据删掉,因为封装的组件里面没有
(4)父组件里面要传递循环的数据
<template>
<view>
<block v-for="item,index in goodsList" :key="index">
<my-goods :goods="item"></my-goods>
</block>
</view>
</template>
封装的子组件里面需要进行接收:
props:{
goods:{
type:Object,
default:()=>{}
}
},
(5)封装组件整体结构图:
XX.轮播图的预览
<image :src="item.pics_big" @click="preview(index)"></image>
// 预览轮播图
preview(index){
uni.previewImage({
current:index,
urls:this.goods.pics.map(e=>{
return e.pics_big
})
})
}
37.vuex的初始化 (1) 新建目录store,再新建store.js文件