14-首页主体-左侧分类-处理品牌
目的: 品牌展示特殊,需要额外获取数据和额外的布局。
大致步骤:
- 定义API接口,在 home-category.vue 组件获取数据。
- 完成基础布局,根据数据进行渲染。
- 处理左侧分类激活显示。
落地代码:
1.定义API接口,在 home-category.vue
组件获取数据。
src/api/home.js
export const findBrand = (limit) => {
return request('/home/brand', 'get', {limit})
}
src/views/home/components/home-category.vue
const brand = reactive({
id: 'brand',
name: '品牌',
children: [{ id: 'brand-children', name: '品牌推荐' }],
+ brands: []
})
+import { findBrand } from '@/api/home.js'
// ... 省略代码
setup () {
// ... 省略代码
+ findBrand().then(data=>{
+ brand.brands = data.result
+ })
return { menuList, categoryId, currCategory }
}
2.进行渲染:src/views/home/components/home-category.vue
- 布局样式
<ul>
<li class="brand" v-for="i in 6" :key="i">
<RouterLink to="/">
<img src="http://zhoushugang.gitee.io/erabbit-client-pc-static/uploads/brand_goods_1.jpg" alt="">
<div class="info">
<p class="place"><i class="iconfont icon-dingwei"></i>北京</p>
<p class="name ellipsis">DW</p>
<p class="desc ellipsis-2">DW品牌闪购</p>
</div>
</RouterLink>
</li>
</ul>
li.brand {
height: 180px;
a {
align-items: flex-start;
img {
width: 120px;
height: 160px;
}
.info {
p {
margin-top: 8px;
}
.place {
color: #999;
}
}
}
}
- 进行渲染
<!-- 弹层 -->
<div class="layer">
+ <h4 v-if="currCategory">{{currCategory.id==='brand'?'品牌':'分类'}}推荐 <small>根据您的购买或浏览记录推荐</small></h4>
<ul v-if="currCategory && currCategory.goods && currCategory.goods.length">
<li v-for="item in currCategory.goods" :key="item.id">
<RouterLink to="/">
<img :src="item.picture" alt="">
<div class="info">
<p class="name ellipsis-2">{{item.name}}</p>
<p class="desc ellipsis">{{item.desc}}</p>
<p class="price"><i>¥</i>{{item.price}}</p>
</div>
</RouterLink>
</li>
</ul>
+ <ul v-if="currCategory && currCategory.brands && currCategory.brands.length">
+ <li class="brand" v-for="item in currCategory.brands" :key="item.id">
+ <RouterLink to="/">
+ <img :src="item.picture" alt="">
+ <div class="info">
+ <p class="place"><i class="iconfont icon-dingwei"></i>{{item.place}}</p>
+ <p class="name ellipsis">{{item.name}}</p>
+ <p class="desc ellipsis-2">{{item.desc}}</p>
+ </div>
+ </RouterLink>
+ </li>
+ </ul>
</div>
3.处理左侧分类激活显示 src/views/home/components/home-category.vue
- 激活类active
.menu {
li {
padding-left: 40px;
height: 50px;
line-height: 50px;
+ &:hover,&.active {
background: @xtxColor;
}
- 绑定类
<ul class="menu">
+ <li :class="{active:categoryId===item.id}"
- 移除类
+ <div class='home-category' @mouseleave="categoryId=null">
<ul class="menu">
总结: 品牌数据需要请求后台,再汇总到所有数据中,然后渲染,然后激活当前的分类。
15-首页主体-左侧分类-骨架效果
目的: 为了在加载的过程中等待效果更好,封装一个骨架屏组件。
大致步骤:
- 需要一个组件,做占位使用。这个占位组件有个专业术语:骨架屏组件。
- 暴露一些属性:高,宽,背景,是否有闪动画。
- 这是一个公用组件,需要全局注册,将来这样的组件建议再vue插件中定义。
- 使用组件完成左侧分类骨架效果。
落的代码:
.封装组件:
src/components/library/xtx-skeleton.vue`
<template>
<div class="xtx-skeleton" :style="{width,height}" :class="{shan:animated}">
<!-- 1 盒子-->
<div class="block" :style="{backgroundColor:bg}"></div>
<!-- 2 闪效果 xtx-skeleton 伪元素 --->
</div>
</template>
<script>
export default {
name: 'XtxSkeleton',
// 使用的时候需要动态设置 高度,宽度,背景颜色,是否闪下
props: {
bg: {
type: String,
default: '#efefef'
},
width: {
type: String,
default: '100px'
},
height: {
type: String,
default: '100px'
},
animated: {
type: Boolean,
default: false
}
}
}
</script>
<style scoped lang="less">
.xtx-skeleton {
display: inline-block;
position: relative;
overflow: hidden;
vertical-align: middle;
.block {
width: 100%;
height: 100%;
border-radius: 2px;
}
}
.shan {
&::after {
content: "";
position: absolute;
animation: shan 1.5s ease 0s infinite;
top: 0;
width: 50%;
height: 100%;
background: linear-gradient(
to left,
rgba(255, 255, 255, 0) 0,
rgba(255, 255, 255, 0.3) 50%,
rgba(255, 255, 255, 0) 100%
);
transform: skewX(-45deg);
}
}
@keyframes shan {
0% {
left: -100%;
}
100% {
left: 120%;
}
}
</style>
2.封装插件:插件定义 src/componets/library/index.js
使用插件 src/main.js
// 扩展vue原有的功能:全局组件,自定义指令,挂载原型方法,注意:没有全局过滤器。
// 这就是插件
// vue2.0插件写法要素:导出一个对象,有install函数,默认传入了Vue构造函数,Vue基础之上扩展
// vue3.0插件写法要素:导出一个对象,有install函数,默认传入了app应用实例,app基础之上扩展
import XtxSkeleton from './xtx-skeleton.vue'
export default {
install (app) {
// 在app上进行扩展,app提供 component directive 函数
// 如果要挂载原型 app.config.globalProperties 方式
app.component(XtxSkeleton.name, XtxSkeleton)
}
}
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import './mock'
+import ui from './components/library'
import 'normalize.css'
import '@/assets/styles/common.less'
+// 插件的使用,在main.js使用app.use(插件)
+createApp(App).use(store).use(router).use(ui).mount('#app')
3.最后使用组件完成左侧分类骨架效果: src/views/home/components/home-category.vue
<ul class="menu">
<li :class="{active:categoryId===item.id}" v-for="item in menuList" :key="item.id" @mouseenter="categoryId=item.id">
<RouterLink to="/">{{item.name}}</RouterLink>
<template v-if="item.children">
<RouterLink to="/" v-for="sub in item.children" :key="sub.id">{{sub.name}}</RouterLink>
</template>
+ <span v-else>
+ <XtxSkeleton width="60px" height="18px" style="margin-right:5px" bg="rgba(255,255,255,0.2)" />
+ <XtxSkeleton width="50px" height="18px" bg="rgba(255,255,255,0.2)" />
+ </span>
</li>
</ul>
.xtx-skeleton {
animation: fade 1s linear infinite alternate;
}
@keyframes fade {
from {
opacity: 0.2;
}
to {
opacity: 1;
}
}
16-首页主体-轮播图-基础布局
目的: 封装小兔鲜轮播图组件,第一步:基础结构的使用。
大致步骤:
- 准备xtx-carousel组件基础布局,全局注册
- 准备home-banner组件,使用xtx-carousel组件,再首页注册使用。
- 深度作用xtx-carousel组件的默认样式
落的代码:
- 轮播图基础结构
src/components/library/xtx-carousel.vue
<template>
<div class='xtx-carousel'>
<ul class="carousel-body">
<li class="carousel-item fade">
<RouterLink to="/">
<img src="http://yjy-xiaotuxian-dev.oss-cn-beijing.aliyuncs.com/picture/2021-04-15/1ba86bcc-ae71-42a3-bc3e-37b662f7f07e.jpg" alt="">
</RouterLink>
</li>
</ul>
<a href="javascript:;" class="carousel-btn prev"><i class="iconfont icon-angle-left"></i></a>
<a href="javascript:;" class="carousel-btn next"><i class="iconfont icon-angle-right"></i></a>
<div class="carousel-indicator">
<span v-for="i in 5" :key="i"></span>
</div>
</div>
</template>
<script>
export default {
name: 'XtxCarousel'
}
</script>
<style scoped lang="less">
.xtx-carousel{
width: 100%;
height: 100%;
min-width: 300px;
min-height: 150px;
position: relative;
.carousel{
&-body {
width: 100%;
height: 100%;
}
&-item {
width: 100%;
height: 100%;
position: absolute;
left: 0;
top: 0;
opacity: 0;
transition: opacity 0.5s linear;
&.fade {
opacity: 1;
z-index: 1;
}
img {
width: 100%;
height: 100%;
}
}
&-indicator {
position: absolute;
left: 0;
bottom: 20px;
z-index: 2;
width: 100%;
text-align: center;
span {
display: inline-block;
width: 12px;
height: 12px;
background: rgba(0,0,0,0.2);
border-radius: 50%;
cursor: pointer;
~ span {
margin-left: 12px;
}
&.active {
background: #fff;
}
}
}
&-btn {
width: 44px;
height: 44px;
background: rgba(0,0,0,.2);
color: #fff;
border-radius: 50%;
position: absolute;
top: 228px;
z-index: 2;
text-align: center;
line-height: 44px;
opacity: 0;
transition: all 0.5s;
&.prev{
left: 20px;
}
&.next{
right: 20px;
}
}
}
&:hover {
.carousel-btn {
opacity: 1;
}
}
}
</style>
- 全局注册轮播图
src/components/library/index.js
import XtxSkeleton from './xtx-skeleton.vue'
+import XtxCarousel from './xtx-carousel.vue'
export default {
install (app) {
app.component(XtxSkeleton.name, XtxSkeleton)
+ app.component(XtxCarousel.name, XtxCarousel)
}
}
- 首页广告组件基础结构
src/views/home/components/home-banner.vue
<template>
<div class="home-banner">
<XtxCarousel />
</div>
</template>
<script>
export default {
name: 'HomeBanner'
}
</script>
<style scoped lang="less">
.home-banner {
width: 1240px;
height: 500px;
position: absolute;
left: 0;
top: 0;
z-index: 98
}
</style>
- 首页使用广告组件
<template>
+ <!-- 首页入口 -->
+ <div class="home-entry">
+ <div class="container">
<!-- 左侧分类 -->
<HomeCategory />
<!-- 轮播图 -->
<HomeBanner />
</div>
</div>
</template>
<script>
import HomeCategory from './components/home-category'
+import HomeBanner from './components/home-banner'
export default {
name: 'HomePage',
components: {
+ HomeCategory,
HomeBanner
}
}
</script>
<style scoped lang="less"></style>
- 覆盖轮播图组件样式
src/views/home/components/home-banner.vue
.xtx-carousel {
::v-deep .carousel-btn.prev {
left: 270px;
}
::v-deep .carousel-indicator {
padding-left: 250px;
}
}
总结: 需要注意要覆盖样式,首页轮播图特殊些。
17-首页主体-轮播图-渲染结构
目的: 封装小兔鲜轮播图组件,第二步:动态渲染结构。
大致步骤:
- 定义获取广告图API函数
- 在home-banner组件获取轮播图数据,传递给xtx-carousel组件
- 在xtx-carousel组件完成渲染
落的代码:
- API函数
src/api/home.js
/**
* 获取广告图
* @returns Promise
*/
export const findBanner = () => {
return request('/home/banner', 'get')
}
- 广告组件获取数据,传给轮播图
src/views/home/components/home-banner.vue
<template>
<div class="home-banner">
+ <XtxCarousel :sliders="sliders" />
</div>
</template>
<script>
import { ref } from 'vue'
import { findBanner } from '@/api/home'
export default {
name: 'HomeBanner',
+ setup () {
+ const sliders = ref([])
+ findBanner().then(data => {
+ sliders.value = data.result
+ })
+ return { sliders }
+ }
}
</script>
- 完成轮播图结构渲染
src/components/library/xtx-carousel.vue
<template>
<div class='xtx-carousel'>
<ul class="carousel-body">
+ <li class="carousel-item" v-for="(item,i) in sliders" :key="i" :class="{fade:index===i}">
<RouterLink to="/">
+ <img :src="item.imgUrl" alt="">
</RouterLink>
</li>
</ul>
<a href="javascript:;" class="carousel-btn prev"><i class="iconfont icon-angle-left"></i></a>
<a href="javascript:;" class="carousel-btn next"><i class="iconfont icon-angle-right"></i></a>
<div class="carousel-indicator">
+ <span v-for="(item,i) in sliders" :key="i" :class="{active:index===i}"></span>
</div>
</div>
</template>
<script>
+import { ref } from 'vue'
export default {
name: 'XtxCarousel',
+ props: {
+ sliders: {
+ type: Array,
+ default: () => []
+ }
+ },
+ setup () {
+ // 默认显示的图片的索引
+ const index = ref(0)
+ return { index }
+ }
}
</script>
总结: fade是控制显示那张图片的,需要一个默认索引数据,渲染第一张图和激活第一个点。