一、本节项目预备知识
1、效果演示
2、常见组件
1、view组件
视图容器,它类似于传统html中的div,用于包裹各种元素内容。
2、swiper组件
swiper是滑动视图容器,经常看到的轮播图就是通过它来完成的
swiper-item是swiper子组件,仅能放到swiper组件中
swiper常用属性
属性名 | 类型 | 默认值 | 说明 |
---|---|---|---|
indicator-dots | Boolean | false | 是否显示面板指示点 |
indicator-color | Color | rgba(0, 0, 0, .3) | 指示点颜色 |
indicator-active-color | Color | #000000 | 当前选中的指示点颜色 |
autoplay | Boolean | false | 是否自动切换 |
current | Number | 0 | 当前所在滑块的 index |
interval | Number | 5000 | 自动切换时间间隔 |
duration | Number | 500 | 滑动动画时长 |
<template>
<view class="container">
<swiper class="swiper" :indicator-dots="true" :autoplay="true" :interval="2000">
<swiper-item>
<text>1</text>
</swiper-item>
<swiper-item>
<text>2</text>
</swiper-item>
<swiper-item>
<text>3</text>
</swiper-item>
</swiper>
</view>
</template>
<style lang="scss">
.swiper{
width: 750rpx;
height: 300rpx;
background-color: #ccc;
}
</style>
3、image组件
图片组件的常见属性
属性名 | 类型 | 说明 |
---|---|---|
src | String | 图片的资源地址 |
mode | String | 图片裁剪、缩放模式 |
lazy-load | Boolean | 图片懒加载 |
常见的mode值
值 | 含义 |
---|---|
scaleToFill | 缩放模式,不保持纵横比缩放图片,使图片的宽高完全拉伸至填满 image 元素 |
aspectFit | 缩放模式,保持纵横比缩放图片,使图片的长边能完全显示出来。也就是说,可以完整地将图片显示出来。 |
aspectFill | 缩放模式,保持纵横比缩放图片,只保证图片的短边能完全显示出来。也就是说,图片通常只在水平或垂直方向是完整的,另一个方向将会发生截取。 |
widthFix | 缩放模式,宽度不变,高度自动变化,保持原图宽高比不变 |
heightFix | 缩放模式,高度不变,宽度自动变化,保持原图宽高比不变 |
<template>
<view class="container">
<swiper class="swiper" :indicator-dots="true" :autoplay="true" :interval="2000">
<swiper-item v-for="(item,index) in swiperList" :key="index">
<image :src="item.image_src" mode="widthFix" class="swiper-image"></image>
</swiper-item>
</swiper>
</view>
</template>
<script>
export default {
data() {
return {
swiperList:[ {
"image_src": "https://api-hmugo-web.itheima.net/pyg/banner1.png",
"open_type": "navigate",
"goods_id": 129,
"navigator_url": "/pages/goods_detail/main?goods_id=129"
},
{
"image_src": "https://api-hmugo-web.itheima.net/pyg/banner2.png",
"open_type": "navigate",
"goods_id": 395,
"navigator_url": "/pages/goods_detail/main?goods_id=395"
},
{
"image_src": "https://api-hmugo-web.itheima.net/pyg/banner3.png",
"open_type": "navigate",
"goods_id": 38,
"navigator_url": "/pages/goods_detail/main?goods_id=38"
}]
}
}
}
</script>
<style lang="scss">
.swiper{
width: 750rpx;
height: 300rpx;
.swiper-image{
width: 100%;
height: 100%;
}
}
</style>
3、网络
3.1、发起请求
uni.request(OBJECT)
OBJECT 参数说明
属性 | 类型 | 必填项 | 说明 |
---|---|---|---|
url | String | 是 | 开发者服务器接口地址 |
data | String/Object/ArrayBuffer | 否 | 请求的参数 |
header | Object | 否 | 设置请求的 header,header 中不能设置 Referer。content-type默认为 application/json |
method | string | 否 | HTTP 请求方法 |
dataType | string | 否 | 返回的数据格式 |
success | function | 否 | 接口调用成功的回调函数 |
fail | function | 否 | 接口调用失败的回调函数 |
complete | function | 否 | 接口调用结束的回调函数(调用成功、失败都会执行) |
案例:首页轮播图
export default {
data() {
return {
swiperList:[]
}
},
created() {
uni.request({
url:'https://api-hmugo-web.itheima.net/api/public/v1/home/swiperdata',
method:'GET',
success:(res) =>{
this.swiperList=res.data.message
}
})
}
}
3.2、微信小程序网络
1)、微信小程序网络请求限制
处于安全性方面的考虑,小程序官方对数据接口的请求做出了如下两个限制
-
只能请求HTTPS类型的接口
-
必须将接口的域名添加到信任列表中
2)、配置request合法域名
配置步骤:登录微信小程序管理后台->开发->开发设置->服务器域名->修改request合法域名
注意事项:
-
域名只支持https协议
-
域名不能使用IP地址或localhost
-
域名必须经过ICP备案
-
服务器域名一个月内最多可申请5次修改
3)、跳过requesth合法域名校验
如果后台程序员仅仅提供了http协议的接口,暂时没有提供https协议的接口,此时为了不耽误开发的进度,我们可以在微信开发者工具中,临时开启【开发者不校验请求域名、TLS版本及HTTPS证书】选项,跳过request合法域名的校验。
注意:跳过requesth合法域名校验的选项,仅限在开发与调试阶段使用。
3.3、封装uni.reqeust
在src/utils目录下创建request.js
const BASE_URL="https://api-hmugo-web.itheima.net/api/public/v1"
export default{
//发送GET请求
get:(url,data)=>{
return new Promise((resolve,reject)=>{
uni.showLoading({
title:'网络正在加载中,请稍等'
})
//发送网路请求
uni.request({
method:'GET',
url:BASE_URL+url,
data:data,
header:{
'Content-Type':'application/json'
},
success(data) {
resolve(data)
},
fail(err) {
reject(err)
},
complete() {
uni.hideLoading() //关闭加载框
}
})
})
},
//发送POST请求
post:(url,data)=>{
return new Promise((resolve,reject)=>{
uni.showLoading({
title:'网络正在加载中,请稍等'
})
//发送网路请求
uni.request({
method:'POST',
url:BASE_URL+url,
data:data,
header:{
'Content-Type':'application/json'
},
success(data) {
resolve(data)
},
fail(err) {
reject(err)
},
complete() {
uni.hideLoading() //关闭加载框
}
})
})
}
}
在main.js中将request.js挂载到全局
import request from '@/utils/request.js'
uni.$api=request
组件中调用
<template>
<view class="container">
<swiper class="swiper" :indicator-dots="true" :autoplay="true" :interval="2000">
<swiper-item v-for="(item,index) in swiperList" :key="index">
<image :src="item.image_src" mode="widthFix" class="swiper-image" :lazy-load="true"></image>
</swiper-item>
</swiper>
</view>
</template>
<script>
export default {
data() {
return {
swiperList:[]
}
},
methods:{
async getSwiperList(){
let result=await uni.$api.get('/home/swiperdata')
this.swiperList=result.message
}
},
created() {
this.getSwiperList()
}
}
</script>
<style lang="scss">
.swiper{
width: 750rpx;
height: 300rpx;
.swiper-image{
width: 100%;
height: 100%;
}
}
</style>
二、商城首页
1、首页轮播图
1.1、首页请求轮播图数据
实现步骤
-
在 data 中定义轮播图的数组
-
在 created生命周期函数中调用获取轮播图数据的方法
-
在 methods 中定义获取轮播图数据的方法
export default {
data() {
return {
swiperList:[]
}
},
methods:{
async getSwiperList(){
let result=await this.$request({url:'/home/swiperdata'})
this.swiperList=result.message
}
},
created() {
this.getSwiperList()
}
}
1.2、自定义轮播图组件
<template>
<swiper class="swiper" :indicator-dots="true" :autoplay="true" :interval="2000">
<swiper-item v-for="(item,index) in swiperList" :key="index">
<image :src="item.image_src" mode="widthFix" class="swiper-image" :lazy-load="true"></image>
</swiper-item>
</swiper>
</template>
<script>
export default {
props: ['swiperList']
}
</script>
<style lang="scss">
.swiper {
width: 750rpx;
height: 300rpx;
.swiper-image {
width: 100%;
height: 100%;
}
}
</style>
1.3、首页引用轮播图组件
<template>
<view>
<homeSwiper :swiperList="swiperList"></homeSwiper>
</view>
</template>
<script>
import homeSwiper from '@/pages/index/homeSwiper.vue'
export default {
components:{
homeSwiper
}
}
</script>
2、首页分类导航区域
实现步骤
-
在 data 中定义首页导航的数组
-
在 created生命周期函数中调用获取导航数据的方法
-
在 methods 中定义获取导航数据的方法
2.1、首页请求轮播图数据
export default {
data() {
return {
navList:[]
}
},
methods:{
async getNavList(){
let result=await this.$request({url:'/home/catitems'})
this.navList=result.message
}
},
created() {
this.getNavList()
}
}
2.2、自定义首页导航组件
<template>
<view class="nav-list">
<view class="nav-item" v-for="(item,index) in navList" :key="index">
<image :src="item.image_src" class="nav-item-image">
</view>
</view>
</template>
<script>
export default{
props:['navList']
}
</script>
<style lang="scss">
.nav-list{
display: flex;
justify-content: center;
margin-top: 50rpx;
.nav-item{
flex-grow: 1;
display: flex;
justify-content: center;
.nav-item-image{
width: 128rpx;
height: 140rpx;
}
}
}
</style>
2.3、首页引用导航组件
<template>
<view>
<home-nav :navList="navList"></home-nav>
</view>
</template>
<script>
import homeNav from '@/components/homeNav/index.vue'
export default {
components:{
homeNav
}
}
</script>
3、首页楼层区域
-
在 data 中定义首页楼层的数组
-
在 created生命周期函数中调用首页楼层的方法
-
在 methods 中定义获取首页楼层数据的方法
3.1、首页请求楼层数据
export default {
data() {
return {
floorList:[]
}
},
methods:{
async getFloorList(){
let result=await this.$request({url:'/home/floordata'})
this.floorList=result.message
}
},
created() {
this.getFloorList()
}
}
3.2、自定义首页楼层组件
-
渲染楼层中的标题
<template>
<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" class="floor-item-img" mode="widthFix"></image>
</view>
</view>
</view>
</template>
<script>
export default{
props:['floorList'],
mounted() {
console.log('floorList',this.floorListData);
}
}
</script>
<style lang="scss">
.floor-title{
display: flex;
margin-top: 30rpx;
}
</style>
-
渲染楼层中的图片
<template>
<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" class="floor-item-img" mode="widthFix"></image>
</view>
<view class="floor-img">
<view class="floor-left-img">
<view class="left-img-box">
<image :src="item.product_list[0].image_src" mode="widthFix" class="big-img"></image>
</view>
</view>
<view class="floor-right-img">
<view v-for="(subItem,idx) in item.product_list" :key="idx" v-if="idx!=0" class="right-img-box-item">
<image :src="subItem.image_src" mode="widthFix" class="small-img"></image>
</view>
</view>
</view>
</view>
</view>
</template>
<style>
.floor-title{
display: flex;
margin-top: 30rpx;
}
.floor-item-img{
width: 100%;
height: 60rpx;
}
.floor-img{
display: flex;
margin-left: 20rpx;
}
.floor-right-img{
display: flex;
flex-wrap: wrap;
}
.big-img{
width: 232rpx;
}
.small-img{
width: 233rpx;
}
</style>
3.3、首页引用楼层组件
<template>
<view>
<home-floor :floorList="floorList"></home-floor>
</view>
</template>
<script>
import homeFloor from '@/components/homeFloor/index.vue'
export default {
components:{
homeFloor
}
}
</script>
三、商城分类页
1、渲染分类页面的基本结构
<template>
<view class="container">
<scroll-view class="left-scroll-view" scroll-y="true">
<view class="left-scroll-view-item active">产品0</view>
<view v-for="i in 100" class="left-scroll-view-item">
<text>产品{{i}}</text>
</view>
</scroll-view>
<scroll-view class="right-scroll-view" scroll-y="true" >
<view v-for="i in 100">xxxxxx</view>
</scroll-view>
</view>
</template>
<script>
</script>
<style lang="scss">
.container{
display: flex
}
.left-scroll-view{
width: 240rpx;
height: 100vh;
.left-scroll-view-item{
line-height: 120rpx;
background-color: #f7f7f7;
text-align: center;
}
.active{
background-color: #fff;
position: relative;
}
.active::before{
content: ' ';
display: block;
position:absolute;
width: 10rpx;
height: 80rpx;
background-color: darkorange;
top:50%;
transform: translateY(-50%);
}
}
.right-scroll-view{
flex-grow: 1;
height: 100vh;
}
</style>
2、获取分类数据
export default{
data(){
return{
categoryList:[]
}
},
methods:{
async getCategoryList(){
let result=await this.$request({url:'/categories'})
this.categoryList=result.message
}
},
created() {
this.getCategoryList()
}
}
3、动态渲染左侧的一级分类列表
-
循环渲染列表结构
<view class="container">
<scroll-view class="left-scroll-view" scroll-y="true">
<view v-for="(item,index) in categoryList" :key="index" class="left-scroll-view-item">
<text>{{item.cat_name}}</text>
</view>
</scroll-view>
</view>
-
在 data 中定义默认选中项的索引
data(){
return{
active:0
}
},
-
循环渲染结构时,为选中项动态添加
.active
样式
<view v-for="(item,index) in categoryList"
:key="index"
:class="{'left-scroll-view-item':true,'active':index==active?true:false}">
<text>{{item.cat_name}}</text>
</view>
-
为一级分类的 Item 项绑定点击事件处理函数
activeChanged
<view v-for="(item,index) in categoryList"
:key="index"
:class="{'left-scroll-view-item':true,'active':index==active?true:false}"
@click="activeChange(index)">
<text>{{item.cat_name}}</text>
</view>
-
定义
activeChanged
事件处理函数,动态修改选中项的索引
methods:{
activeChange(index){
this.active=index
}
}
4、动态渲染右侧的二级分类列表
-
在data中定义二级分类的数据
data() {
return {
secondCategoryList: []
}
},
-
修改
getCategoryList
方法,在请求到数据之后,为二级分类列表数据赋值
async getCategoryList() {
let result = await this.$request({url: '/categories'})
this.categoryList = result.message
this.secondCategoryList = result.message[0].children
},
-
修改
activeChange
方法,在一级分类选中项改变之后,为二级分类列表数据重新赋值
activeChange(index) {
this.active = index
this.secondCategoryList = this.categoryList[index].children
}
-
循环渲染右侧二级分类列表的 UI 结构:
<scroll-view class="right-scroll-view" scroll-y="true">
<view class="cate-lv2" v-for="(item,index) in secondCategoryList" :key="index">
<view class="cate-lv2-title">
<text>{{item.cat_name}}</text>
</view>
</view>
</scroll-view>
-
样式
.cate-lv2-title {
font-size: 12px;
font-weight: bold;
text-align: center;
padding: 15px 0;
}
5、动态渲染右侧的三级分类列表
-
三级菜单结构
<template>
<view class="container">
<!--左侧栏代码省略-->
<scroll-view class="right-scroll-view" scroll-y="true">
<view class="cate-lv2" v-for="(item,index) in secondCategoryList" :key="index">
<view class="cate-lv2-title">
<text>{{item.cat_name}}</text>
</view>
<!--三级菜单-->
<view class="cate-lv3-list">
<view class="cate-lv3-item" v-for="(subitem,subIndex) in item.children" :key="subIndex">
<image :src="subitem.cat_icon" class="item3_img"></image>
<text>{{subitem.cat_name}}</text>
</view>
</view>
</view>
</scroll-view>
</view>
</template>
-
样式
.cate-lv3-list{
display: flex;
flex-wrap: wrap;
justify-content: center;
.cate-lv3-item{
flex-grow: 1;
display: flex;
flex-direction: column;
margin: 20rpx;
.item3_img{
width: 120rpx;
height: 120rpx;
}
}
}