【小兔鲜】day03 Home模块与一级分类
1. Home-整体结构搭建和分类实现
1.1 页面结构
分类实现
2. 左侧分类
在views
下新建Home
在Home
下新建components
最后新建一个HomeCategory
HomeCategory.vue
<script setup>
import { useCategoryStore } from '@/stores/category'
const categoryStore = useCategoryStore()
</script>
<template>
<div class="home-category">
<ul class="menu">
<li v-for="item in categoryStore.categoryList" :key="item.id">
<RouterLink to="/">{{ item.name }}</RouterLink>
<RouterLink v-for="i in item.children.slice(0, 2)" :key="i" to="/">{{ i.name }}</RouterLink>
<!-- 弹层layer位置 -->
<div class="layer">
<h4>分类推荐 <small>根据您的购买或浏览记录推荐</small></h4>
<ul>
<li v-for="i in item.goods" :key="i.id">
<RouterLink to="/">
<img :src="i.picture" alt="" />
<div class="info">
<p class="name ellipsis-2">
{{ i.name }}
</p>
<p class="desc ellipsis">{{ i.desc }}</p>
<p class="price"><i>¥</i>{{ i.price }}</p>
</div>
</RouterLink>
</li>
</ul>
</div>
</li>
</ul>
</div>
</template>
<style scoped lang='scss'>
.home-category {
width: 250px;
height: 500px;
background: rgba(0, 0, 0, 0.8);
position: relative;
z-index: 99;
.menu {
li {
padding-left: 40px;
height: 55px;
line-height: 55px;
&:hover {
background: $xtxColor;
}
a {
margin-right: 4px;
color: #fff;
&:first-child {
font-size: 16px;
}
}
.layer {
width: 990px;
height: 500px;
background: rgba(255, 255, 255, 0.8);
position: absolute;
left: 250px;
top: 0;
display: none;
padding: 0 15px;
h4 {
font-size: 20px;
font-weight: normal;
line-height: 80px;
small {
font-size: 16px;
color: #666;
}
}
ul {
display: flex;
flex-wrap: wrap;
li {
width: 310px;
height: 120px;
margin-right: 15px;
margin-bottom: 15px;
border: 1px solid #eee;
border-radius: 4px;
background: #fff;
&:nth-child(3n) {
margin-right: 0;
}
a {
display: flex;
width: 100%;
height: 100%;
align-items: center;
padding: 10px;
&:hover {
background: #e3f9f4;
}
img {
width: 95px;
height: 95px;
}
.info {
padding-left: 10px;
line-height: 24px;
overflow: hidden;
.name {
font-size: 16px;
color: #666;
}
.desc {
color: #999;
}
.price {
font-size: 22px;
color: $priceColor;
i {
font-size: 16px;
}
}
}
}
}
}
}
// 关键样式 hover状态下的layer盒子变成block
&:hover {
.layer {
display: block;
}
}
}
}
}
</style>
2.1 解释代码
2.1.1 <script setup>
部分
<script setup>
import { useCategoryStore } from '@/stores/category'
const categoryStore = useCategoryStore()
</script>
说明:
- useCategoryStore 是通过 Pinia 定义的一个分类(category)状态管理模块,路径为 @/stores/category。
- categoryStore 是分类状态的实例对象,通常包含状态(state)、获取器(getters)、动作(actions)。
- categoryStore.categoryList 应该是一个分类数组,包含主分类与子分类等信息。
2.1.2 <template>
部分
<template>
<div class="home-category">
<ul class="menu">
<li v-for="item in categoryStore.categoryList" :key="item.id">
<RouterLink to="/">{{ item.name }}</RouterLink>
<RouterLink v-for="i in item.children.slice(0, 2)" :key="i" to="/">{{ i.name }}</RouterLink>
<!-- 弹层layer -->
<div class="layer">
<h4>分类推荐 <small>根据您的购买或浏览记录推荐</small></h4>
<ul>
<li v-for="i in item.goods" :key="i.id">
<RouterLink to="/">
<img :src="i.picture" alt="" />
<div class="info">
<p class="name ellipsis-2">{{ i.name }}</p>
<p class="desc ellipsis">{{ i.desc }}</p>
<p class="price"><i>¥</i>{{ i.price }}</p>
</div>
</RouterLink>
</li>
</ul>
</div>
</li>
</ul>
</div>
</template>
说明:
主体结构:
- 外层是
.home-category
容器,包裹一个.menu
列表。 v-for="item in categoryStore.categoryList"
表示遍历所有一级分类。- 显示主分类名 item.name
- 遍历主分类下的前两个子分类 item.children.slice(0, 2)
.layer
是一个悬浮层,在鼠标悬停主分类时显示(通过 CSS 实现)。
.layer
内容:- 标题:“分类推荐”,小字提示推荐是基于用户行为。
- 推荐商品列表:item.goods
- 包含商品图片、名称、描述、价格。
2.1.3 样式部分 (<style scoped lang='scss'>)
这部分使用了 SCSS 和 Scoped CSS,具体样式如下:
.home-category
- 宽 250px、高 500px,黑色背景(透明度 0.8)
- z-index: 99 保证层级高于其它组件
- 子元素 .menu 是分类菜单
.menu > li
- 每个分类项高度 55px,文字居中对齐
- 鼠标悬停时背景色变成
$xtxColor
(变量定义在全局 SCSS 中)
内部 a 标签(链接)
- 第一个链接加大字体(主分类)
- 其余子分类使用默认字体大小
.layer
(弹层)
- 宽 990px,高 500px,背景白色半透明
- 默认 display: none,通过 li:hover .layer 控制显示
- 内部结构有标题 + 商品推荐列表
商品推荐列表布局:
- 使用 flex-wrap 包装
- 每行显示 3 个商品卡片,超过自动换行
- 每个卡片为
.li > a
,包含图片 + info 信息
.info 区域:
- 商品名称 .name:字号 16px,灰色
- 描述 .desc:浅灰色
- 价格 .price:字号 22px,颜色为 $priceColor
2.1.4 请求接口
接口地址:https://pcapi-xiaotuxian-front-devtest.itheima.net/home/category/head
{
"code": "1",
"msg": "操作成功",
"result": [
{
"id": "1005000",
"name": "居家",
"picture": "http://yjy-xiaotuxian-dev.oss-cn-beijing.aliyuncs.com/picture/2021-05-06/201516e3-25d0-48f5-bcee-7f0cafb14176.png",
"children": [
{
"id": "1005999003",
"name": "居家生活用品",
"picture": "http://yjy-xiaotuxian-dev.oss-cn-beijing.aliyuncs.com/picture/2021-04-22/7f6a7b20-7902-4b43-b9c5-f33151ef1334.jpg?quality=95&imageView",
"children": null,
"goods": null
},
{
"id": "1008017",
"name": "收纳",
"picture": "https://yanxuan.nosdn.127.net/366989e4d730594e86fcd60b5ab19acc.png?quality=95&imageView",
"children": null,
"goods": null
},
{
"id": "1017000",
"name": "宠物食品",
"picture": "https://yanxuan.nosdn.127.net/b42a85ef15f856081ea9f49e5f6893e2.png?quality=95&imageView",
"children": null,
"goods": null
},
{
"id": "109243003",
"name": "艺术藏品",
"picture": "https://yanxuan.nosdn.127.net/9544b81aaa14c26a8038c2365ff3c2bc.png?quality=95&imageView",
"children": null,
"goods": null
},
{
"id": "109248004",
"name": "宠物用品",
"picture": "https://yanxuan.nosdn.127.net/e766b09029ca00680d1e651b5cdc42bd.png?quality=95&imageView",
"children": null,
"goods": null
},
{
"id": "109293000",
"name": "家庭医疗",
"picture": "https://yanxuan.nosdn.127.net/3f34039fa8c26e18e2f4fc96ed8cb6de.png?quality=95&imageView",
"children": null,
"goods": null
},
{
"id": "109308000",
"name": "中医保健",
"picture": "https://yanxuan.nosdn.127.net/2bfba997fd031317caecc4f0bad17569.png?quality=95&imageView",
"children": null,
"goods": null
}
],
"goods": [
{
"id": "3997602",
"name": "暖腰暖腹暖胃,蕲艾暖宫腰带",
"desc": "暖宫暖腰暖胃,大面积热敷≈3片暖宝宝",
"price": "49.00",
"picture": "https://yanxuan-item.nosdn.127.net/9892cc73945da1591fa5259ad076b23c.jpg",
"orderNum": null
},
{
"id": "3995284",
"name": "新年佳礼,家用SPA多功能按摩床垫",
"desc": "坐卧两用,随时随地做spa,多个场合随你心意",
"price": "599.00",
"picture": "https://yanxuan-item.nosdn.127.net/c41003c6df90d7673195dcc2482e13f3.jpg",
"orderNum": null
},
{
"id": "3993828",
"name": "七夕礼物·姐姐同款,穿出自信体态矫姿带",
"desc": "背薄一寸,年轻十岁,时尚百搭,约会职场小心机",
"price": "159.00",
"picture": "https://yanxuan-item.nosdn.127.net/a6939f41c48fa9e9c8f7a7ed855351f1.jpg",
"orderNum": null
},
{
"id": "3993481",
"name": "带提手可站立炫彩硅胶热水袋",
"desc": "杯型式设计,轻松注水防烫伤",
"price": "35.00",
"picture": "https://yanxuan-item.nosdn.127.net/0b120e1c37f312981f4f2242997cc862.png",
"orderNum": null
},
{
"id": "3992367",
"name": "七夕礼物·挺拔身姿,隐形内穿矫姿带",
"desc": "众筹爆品回归,专注矫姿20年,轻松矫正身姿",
"price": "149.00",
"picture": "https://yanxuan-item.nosdn.127.net/7d1bc78607a08c088b2a7cdbe88c05af.png",
"orderNum": null
},
{
"id": "3991840",
"name": "消炎镇痛一贴灵医用冷敷贴60贴/盒",
"desc": "mini型尺寸设计性价比更高",
"price": "79.00",
"picture": "https://yanxuan-item.nosdn.127.net/d3807695c7d42a0247e308a936201d7c.jpg",
"orderNum": null
},
{
"id": "3987204",
"name": "真空拔罐器",
"desc": "居家养生必备超简便拔罐套装",
"price": "49.00",
"picture": "https://yanxuan-item.nosdn.127.net/16222847be7d090eb538a944a7e0b01b.jpg",
"orderNum": null
},
{
"id": "3986121",
"name": "艾灸理疗随时随地,灸疗装置8头/12头",
"desc": "哪里不适灸哪里,全身都可灸",
"price": "99.00",
"picture": "https://yanxuan-item.nosdn.127.net/941abaee58a88cb104228fe2c4cfdeb1.png",
"orderNum": null
},
{
"id": "3997974",
"name": "入门首选,语音播报电子血压计",
"desc": "全程语音指导,一键测量,监测心率,贴心守护爸妈健康",
"price": "109.00",
"picture": "https://yanxuan-item.nosdn.127.net/8f0c15f981c5cbcb1aa17215a259fa62.png",
"orderNum": null
}
]
},
{
"id": "1005002",
"name": "美食",
"picture": "http://yjy-xiaotuxian-dev.oss-cn-beijing.aliyuncs.com/picture/2021-05-06/cf82e5b4-bf1b-4c68-aa86-96f66e8e5282.png",
"children": [
{
"id": "1005012",
"name": "南北干货",
"picture": "https://yanxuan.nosdn.127.net/9af51a1090fd32f668b14451f06d6e72.png?quality=95&imageView",
"children": null,
"goods": null
},
{
"id": "1036003",
"name": "调味酱菜",
"picture": "https://yanxuan.nosdn.127.net/5fae33a840870b487cc903535383bf97.png?quality=95&imageView",
"children": null,
"goods": null
},
{
"id": "109201001",
"name": "方便食品",
"picture": "https://yanxuan.nosdn.127.net/f9872b4aad6c0a943d45629ac96ee8d3.png?quality=95&imageView",
"children": null,
"goods": null
},
{
"id": "109206007",
"name": "米面粮油",
"picture": "https://yanxuan.nosdn.127.net/8578759aed2268f7aa8641273cac7cb3.png?quality=95&imageView",
"children": null,
"goods": null
},
{
"id": "109264007",
"name": "名酒馆",
"picture": "https://yanxuan.nosdn.127.net/91413b1476a0697bb0592609a42d4498.png?quality=95&imageView",
"children": null,
"goods": null
},
{
"id": "109285003",
"name": "进口酒",
"picture": "https://yanxuan.nosdn.127.net/bf705060f01b60fe9c11c345931b1891.png?quality=95&imageView",
"children": null,
"goods": null
}
],
"goods": [
{
"id": "1270008",
"name": "黄金玉粟粥米500克",
"desc": "谷物缤纷,香甜软糯",
"price": "9.90",
"picture": "https://yanxuan-item.nosdn.127.net/5f62c7ab543b1152383a0e9941c09bfd.png",
"orderNum": null
},
{
"id": "1269010",
"name": "什锦素食粥米500克",
"desc": "谷香米糯,营养美味",
"price": "9.90",
"picture": "https://yanxuan-item.nosdn.127.net/dc4826ddb838d8e17f90402adfa3f4fe.png",
"orderNum": null
},
{
"id": "1135081",
"name": "紫米410克",
"desc": "一年一熟的御田胭脂米",
"price": "19.00",
"picture": "https://yanxuan-item.nosdn.127.net/a3c0bb3be38ccf510ecaedb81f5bcd9f.png",
"orderNum": null
},
{
"id": "1135080",
"name": "加量50%不加价,贵州兴仁薏仁米600克",
"desc": "粒粒饱满,颗颗香糯",
"price": "19.00",
"picture": "https://yanxuan-item.nosdn.127.net/3e93790bba8db8aab54ee5ba72799567.png",
"orderNum": null
},
{
"id": "1135079",
"name": "免浸泡,12种谷物一次同享,五谷米400克",
"desc": "无需浸泡,同煮同熟",
"price": "9.90",
"picture": "https://yanxuan-item.nosdn.127.net/bfe70bd66efe94f2f18061c707d2a097.png",
"orderNum": null
},
{
"id": "1135077",
"name": "加量45%不加价,内蒙古赤峰黄小米700克",
"desc": "赤峰好谷,米香浓郁",
"price": "19.00",
"picture": "https://yanxuan-item.nosdn.127.net/564eb51c22d898179b468f5dec52d502.png",
"orderNum": null
},
{
"id": "1135076",
"name": "红小豆450克*3袋",
"desc": "皮薄易煮,粒粒香甜",
"price": "19.00",
"picture": "https://yanxuan-item.nosdn.127.net/eaec90be370a438e565e10189b7a390a.png",
"orderNum": null
},
{
"id": "1135075",
"name": "来自东北的“黑珍珠”,黑米470克*3袋",
"desc": "米汁香稠,Q弹醇香",
"price": "19.00",
"picture": "https://yanxuan-item.nosdn.127.net/1452cfeb14b5d252fb1ba97b278e9cc3.png",
"orderNum": null
},
{
"id": "3464033",
"name": "冻干藤椒豚骨面74克*12盒",
"desc": "日式风味,汤浓肉鲜,椒麻过瘾",
"price": "79.00",
"picture": "https://yanxuan-item.nosdn.127.net/7a5ffe313fe5671709d5a9826f5a83a2.jpg",
"orderNum": null
}
]
},
{
"id": "1010000",
"name": "服饰",
"picture": "http://yjy-xiaotuxian-dev.oss-cn-beijing.aliyuncs.com/picture/2021-05-06/33e1f5de-0fdb-4cfa-9ba9-781233024b53.png",
"children": [
{
"id": "109303000",
"name": "钱包/胸包",
"picture": "https://yanxuan.nosdn.127.net/237613bc9c22eb422dade63e3ed7c61a.png?quality=95&imageView",
"children": null,
"goods": null
},
{
"id": "109311005",
"name": "女式靴子",
"picture": "https://yanxuan.nosdn.127.net/62c5dacf3e0cbe8e4188ccd263358d1a.png?quality=95&imageView",
"children": null,
"goods": null
},
{
"id": "109311006",
"name": "女式休闲鞋",
"picture": "https://yanxuan.nosdn.127.net/8cd3a76ffffb14e9fe92ad2369117af0.png?quality=95&imageView",
"children": null,
"goods": null
},
{
"id": "109311007",
"name": "女式运动鞋",
"picture": "https://yanxuan.nosdn.127.net/7be561f2ddc2179a7e116c413636eba9.png?quality=95&imageView",
"children": null,
"goods": null
},
{
"id": "109315000",
"name": "11.11购物狂欢",
"picture": "https://yanxuan.nosdn.127.net/b29297263032957553d7153b309db74b.png?quality=95&imageView",
"children": null,
"goods": null
},
{
"id": "109318003",
"name": "【年末狂欢季】",
"picture": "https://yanxuan.nosdn.127.net/8f8092d5bf6a133a8cb59ab7b9f790e9.png?quality=95&imageView",
"children": null,
"goods": null
}
],
"goods": [
{
"id": "1621018",
"name": "懒人瘦出好身材,多功能甩脂塑形机",
"desc": "懒人福音居家轻松塑身",
"price": "589.00",
"picture": "https://yanxuan-item.nosdn.127.net/540f84174dbf9b98d4072abbd886819e.png",
"orderNum": null
},
{
"id": "1593000",
"name": "软糯似baby肌肤,男式高领纯小山羊绒衫",
"desc": "绒毛取自未满1岁的山羊羊羔",
"price": "559.00",
"picture": "https://yanxuan-item.nosdn.127.net/06156761337ba0fd1f8f51470f698b5f.png",
"orderNum": null
},
{
"id": "1555000",
"name": "舒眠真丝眼罩",
"desc": "顺滑真丝,感受舒适睡眠",
"price": "79.00",
"picture": "https://yanxuan-item.nosdn.127.net/85432452d5f40c4798c08f1f0beb4a43.png",
"orderNum": null
},
{
"id": "1436033",
"name": "柔软透气,女式精梳棉内裤6条装",
"desc": "爱慕供应商柔软透气",
"price": "125.00",
"picture": "https://yanxuan-item.nosdn.127.net/dc30e9603306e59d959e7fac6968244e.jpg",
"orderNum": null
},
{
"id": "1281002",
"name": "轻灵碳素耐用型羽毛球拍单双拍",
"desc": "超轻球拍,运动随心",
"price": "119.00",
"picture": "https://yanxuan-item.nosdn.127.net/89cc3ab3e332bb1df5dc20241b5ca4bb.png",
"orderNum": null
},
{
"id": "4001285",
"name": "英伦长柄自动晴雨伞",
"desc": "雨天有情调,英伦风设计,给你好品味",
"price": "40.90",
"picture": "https://yanxuan-item.nosdn.127.net/e77b8f4a8ddd1e777394d84347859f7c.png",
"orderNum": null
},
{
"id": "4001126",
"name": "瑜伽裸感女式运动训练紧身裤",
"desc": "面料韧性足,修身又舒适",
"price": "83.90",
"picture": "https://yanxuan-item.nosdn.127.net/872275c2e2edacc917e0364065c90121.png",
"orderNum": null
},
{
"id": "4000914",
"name": "恍若没穿鞋,女式轻软舒弹健步鞋2.0",
"desc": "宛若没穿鞋的轻盈,体验更自由的奔跑",
"price": "159.00",
"picture": "https://yanxuan-item.nosdn.127.net/feec7bde4d52521ae70ab4c5010ce992.png",
"orderNum": null
},
{
"id": "4000620",
"name": "「一件三穿好过冬」男中长款鹅绒防水外套",
"desc": "一件衣服三种穿法,应对多变天气",
"price": "659.00",
"picture": "https://yanxuan-item.nosdn.127.net/aa9e03278e3274a2a83f3f8e7df5ee6f.png",
"orderNum": null
}
]
},
{
"id": "1011000",
"name": "母婴",
"picture": "http://yjy-xiaotuxian-dev.oss-cn-beijing.aliyuncs.com/picture/2021-05-06/b514a526-4010-4ce8-8cb9-757ed382f84a.png",
"children": [
{
"id": "1020003",
"name": "T恤/polo/衬衫",
"picture": "https://yanxuan.nosdn.127.net/1f0089afcec911db7202fbcdae57d5f8.png?quality=95&imageView",
"children": null,
"goods": null
},
{
"id": "1037006",
"name": "儿童鞋",
"picture": "https://yanxuan.nosdn.127.net/7fd14a409302391da16970981cacd336.png?quality=95&imageView",
"children": null,
"goods": null
},
{
"id": "109243018",
"name": "外套/套装",
"picture": "https://yanxuan.nosdn.127.net/773a8777f66c286f97af6d74a27d7fe1.png?quality=95&imageView",
"children": null,
"goods": null
},
{
"id": "109243019",
"name": "裤子/裙装",
"picture": "https://yanxuan.nosdn.127.net/a8c52cea5f953019484a74883ad8f14b.png?quality=95&imageView",
"children": null,
"goods": null
},
{
"id": "109243021",
"name": "连体衣/礼盒",
"picture": "https://yanxuan.nosdn.127.net/773677cc0922628152a9b3cbd862426f.png?quality=95&imageView",
"children": null,
"goods": null
},
{
"id": "109243022",
"name": "学步鞋",
"picture": "https://yanxuan.nosdn.127.net/9a50280bb69e1c12f557f601cce1c480.png?quality=95&imageView",
"children": null,
"goods": null
}
],
"goods": [
{
"id": "4027473",
"name": "新款LOGO印花时尚学步叫叫鞋",
"desc": "软底包头叫叫鞋",
"price": "259.00",
"picture": "https://yanxuan-item.nosdn.127.net/841d7669e6cc23cb249f47d8d39fc17c.jpg",
"orderNum": null
},
{
"id": "4027466",
"name": "儿童气泵软底学步二阶段学步鞋",
"desc": "气泵大底学步鞋",
"price": "239.00",
"picture": "https://yanxuan-item.nosdn.127.net/19bedfd20a12842b5d7f7b909a62e877.jpg",
"orderNum": null
},
{
"id": "4023738",
"name": "舒适软弹,糖果色儿童网孔透气运动鞋25-30",
"desc": "防踢鞋头,透气网孔更舒适",
"price": "139.00",
"picture": "https://yanxuan-item.nosdn.127.net/c437179bc1d0d5ad77a79f15a5c6f166.png",
"orderNum": null
},
{
"id": "4023641",
"name": "防踢鞋头,儿童学步健康机能鞋21-30",
"desc": "包头防踢用心呵护",
"price": "129.00",
"picture": "https://yanxuan-item.nosdn.127.net/33ba674fbb13e2380ee8a74371eacf43.jpg",
"orderNum": null
},
{
"id": "4023638",
"name": "抓绒保暖,毛毛虫儿童运动鞋26-30",
"desc": "宝贝上脚活力出街",
"price": "109.00",
"picture": "https://yanxuan-item.nosdn.127.net/d917c92e663c5ed0bb577c7ded73e4ec.png",
"orderNum": null
},
{
"id": "4007784",
"name": "婴幼儿童透气网面学步鞋小童鞋4-6码",
"desc": "优质透气网布鞋面,轻盈自在",
"price": "119.00",
"picture": "https://yanxuan-item.nosdn.127.net/a9f428091f172db89977a511f6639fec.jpg",
"orderNum": null
},
{
"id": "3998535",
"name": "学步更自如,婴幼童机能学步鞋4-6码",
"desc": "宝宝的第一双鞋,成长不将就",
"price": "109.00",
"picture": "https://yanxuan-item.nosdn.127.net/ef01de6dfac9cc4c548f317f2feddafe.jpg",
"orderNum": null
},
{
"id": "3995013",
"name": "时髦出街更亮眼,儿童休闲老爹鞋21-26码",
"desc": "高弹鞋底,行走脚感舒适",
"price": "109.00",
"picture": "https://yanxuan-item.nosdn.127.net/088c9f9208b82fa4777336d22faa919d.jpg",
"orderNum": null
},
{
"id": "4023751",
"name": "释放可爱天性,棉毛布造型领哈衣59-90cm",
"desc": "A类婴幼儿标准,安全放心",
"price": "62.00",
"picture": "https://yanxuan-item.nosdn.127.net/5e2b5629af983dadbceed483dd677eeb.jpg",
"orderNum": null
}
]
},
{
"id": "1013001",
"name": "个护",
"picture": "http://yjy-xiaotuxian-dev.oss-cn-beijing.aliyuncs.com/picture/2021-05-06/d38a73b8-cd03-48aa-a60b-e7c4e16667ed.png",
"children": [
{
"id": "1009000",
"name": "家庭清洁",
"picture": "https://yanxuan.nosdn.127.net/718318c0d3b55d011fcb7c7c843902ce.png?quality=95&imageView",
"children": null,
"goods": null
},
{
"id": "1020002",
"name": "浴室用品",
"picture": "https://yanxuan.nosdn.127.net/dfb6142de1bd2f59b251eb8f7c7ea2fb.png?quality=95&imageView",
"children": null,
"goods": null
},
{
"id": "109243016",
"name": "餐厨清洁",
"picture": "https://yanxuan.nosdn.127.net/55d927e337c1f6d394359e99ef72a621.png?quality=95&imageView",
"children": null,
"goods": null
},
{
"id": "109256012",
"name": "纸品",
"picture": "https://yanxuan.nosdn.127.net/07fef43b0d14882d6662233ab30dc588.png?quality=95&imageView",
"children": null,
"goods": null
},
{
"id": "109256013",
"name": "干湿巾",
"picture": "https://yanxuan.nosdn.127.net/985897ea31fdfc159e12696f4dbb4c13.png?quality=95&imageView",
"children": null,
"goods": null
},
{
"id": "109261055",
"name": "毛巾浴巾",
"picture": "https://yanxuan.nosdn.127.net/949c5f8b077cf386ff9f1f18bec3408b.png?quality=95&imageView",
"children": null,
"goods": null
}
],
"goods": [
{
"id": "3987992",
"name": "【新疆棉】简约山形纹全棉提花毛巾",
"desc": "新疆长绒棉毛巾,吸水好颜值高",
"price": "18.50",
"picture": "https://yanxuan-item.nosdn.127.net/e0cea368f41da1587b3b7fc523f169d7.png",
"orderNum": null
},
{
"id": "3436033",
"name": "1件装【新疆棉】云珍·轻软旅行长绒棉方巾",
"desc": "轻巧无捻小方巾,旅行便携",
"price": "7.90",
"picture": "https://yanxuan-item.nosdn.127.net/99c83709ca5f9fd5c5bb35d207ad7822.png",
"orderNum": null
},
{
"id": "1436010",
"name": "囤货装云珍·轻软旅行长绒棉毛巾6条装",
"desc": "触碰云软,热销50万条",
"price": "139.00",
"picture": "https://yanxuan-item.nosdn.127.net/f4ed2e5d7b268bb270bce9ce63122ec0.jpg",
"orderNum": null
},
{
"id": "1129016",
"name": "【新疆棉】快速擦干,阿瓦提长绒棉毛巾",
"desc": "瞬间吸水,亲肤近0掉毛率",
"price": "16.90",
"picture": "https://yanxuan-item.nosdn.127.net/2f4ff18d601494cb7805ee099ed8a50f.png",
"orderNum": null
},
{
"id": "1021000",
"name": "厚厚一按就干爽,埃及进口长绒棉毛巾",
"desc": "厚实大毛圈,干爽瞬吸",
"price": "16.90",
"picture": "https://yanxuan-item.nosdn.127.net/a5c5398a7c033edb8a052fe1fddf8a81.png",
"orderNum": null
},
{
"id": "1009026",
"name": "【新疆棉】云珍·轻软长绒棉浴巾",
"desc": "云朵般轻软,至柔至软",
"price": "129.00",
"picture": "https://yanxuan-item.nosdn.127.net/957d7a338fc0de044965cc279fce68b2.png",
"orderNum": null
},
{
"id": "1006051",
"name": "【新疆棉】云珍·轻软旅行长绒棉毛巾",
"desc": "轻软亲肤,热销50万条",
"price": "31.90",
"picture": "https://yanxuan-item.nosdn.127.net/2a16452169f9d2e8841ddef76fdd684a.png",
"orderNum": null
},
{
"id": "1006029",
"name": "【新疆棉】宝宝也能用,全棉华夫格毛巾",
"desc": "空气华夫格,自然新疆棉",
"price": "15.90",
"picture": "https://yanxuan-item.nosdn.127.net/3480e3fa68b810f23afb43f6bf2fe45b.png",
"orderNum": null
},
{
"id": "3465081",
"name": "清洁养护一片搞定皮革护理湿巾",
"desc": "一擦如新懒人必备",
"price": "9.90",
"picture": "https://yanxuan-item.nosdn.127.net/bc32744cdf844b5136d3d84b0e6ba9d4.jpg",
"orderNum": null
}
]
},
{
"id": "1019000",
"name": "严选",
"picture": "http://yjy-xiaotuxian-dev.oss-cn-beijing.aliyuncs.com/picture/2021-05-06/4b02f01f-a365-4b6c-9f7a-8b0f591dda02.png",
"children": [
{
"id": "1065004",
"name": "滋补保健",
"picture": "https://yanxuan.nosdn.127.net/1e619a2b22f40bf83070e6f8f6e0c8ff.png?quality=95&imageView",
"children": null,
"goods": null
},
{
"id": "109256014",
"name": "床品家纺",
"picture": "https://yanxuan.nosdn.127.net/e6580910c1f98ed61bda867aeaf07929.png?quality=95&imageView",
"children": null,
"goods": null
},
{
"id": "109256015",
"name": "锅具配件",
"picture": "https://yanxuan.nosdn.127.net/50ccbf04857e86cccf44d25da0577deb.png?quality=95&imageView",
"children": null,
"goods": null
},
{
"id": "109275000",
"name": "清洁用品",
"picture": "https://yanxuan.nosdn.127.net/926d919bc3e95f5c93dc5dc973faa378.png?quality=95&imageView",
"children": null,
"goods": null
},
{
"id": "109309012",
"name": "个护电器",
"picture": "https://yanxuan.nosdn.127.net/7a0eea3c515ad247c092749bcdd29855.png?quality=95&imageView",
"children": null,
"goods": null
}
],
"goods": [
{
"id": "4000009",
"name": "医美级冰脱韩国Ulike蓝宝石脉冲光脱毛仪",
"desc": "蓝宝石冰点脱毛,高效更持久",
"price": "1599.00",
"picture": "https://yanxuan-item.nosdn.127.net/3567b2ac2526e6b89124f3fb62fd09d3.png",
"orderNum": null
},
{
"id": "4027998",
"name": "亮碟多效合一洗涤块495g",
"desc": "洗碗机专用,强力去污",
"price": "69.90",
"picture": "https://yanxuan-item.nosdn.127.net/e07c2b63765cf9f4a46d489c6e09c1c1.jpg",
"orderNum": null
},
{
"id": "4017718",
"name": "油污克星Mootaa重油污清洁剂",
"desc": "一喷快速溶解油污",
"price": "26.90",
"picture": "https://yanxuan-item.nosdn.127.net/53a1579ead54c61a9b296d20c78a67ff.jpg",
"orderNum": null
},
{
"id": "4010699",
"name": "除味净化神器Mootaa冰箱抑菌清洁剂250ml",
"desc": "除臭杀毒,持久保鲜,解决冰箱各种食材串味产生的难闻气味",
"price": "35.00",
"picture": "https://yanxuan-item.nosdn.127.net/838c1b94f15e3b200bea21cef8989592.png",
"orderNum": null
},
{
"id": "3999485",
"name": "意大利大公鸡管家重油污克星清洁剂625ml",
"desc": "强效去污,操作便捷",
"price": "39.00",
"picture": "https://yanxuan-item.nosdn.127.net/6a669deef604cd8f080991b3207a3078.png",
"orderNum": null
},
{
"id": "3998109",
"name": "强力去垢不伤锅天然棕榈清洁刷长/短款",
"desc": "短柄针对顽渍,长柄清洁死角",
"price": "8.00",
"picture": "https://yanxuan-item.nosdn.127.net/7537da93f26f0303d94c59f3889836ed.png",
"orderNum": null
},
{
"id": "3990408",
"name": "1滴强力去污澳洲超浓缩不伤手洗洁精400ml",
"desc": "温和高效去污,一瓶可作多用",
"price": "17.90",
"picture": "https://yanxuan-item.nosdn.127.net/9ffdc0b1f6dbbe2e9f10a187444c01a3.png",
"orderNum": null
},
{
"id": "3990343",
"name": "【囤货装】日本橙油精华去油污泡沫3瓶装",
"desc": "强力除油污,萃取橙油精华",
"price": "67.50",
"picture": "https://yanxuan-item.nosdn.127.net/188e05584a056b0e2f17e8c568da8c00.png",
"orderNum": null
},
{
"id": "4023839",
"name": "日本冰块冰球制冰模具",
"desc": "轻轻一拍,轻松取冰",
"price": "29.80",
"picture": "https://yanxuan-item.nosdn.127.net/2be38fc160992fe41f7d4a45bd0f90e5.png",
"orderNum": null
}
]
},
{
"id": "1043000",
"name": "数码",
"picture": "http://yjy-xiaotuxian-dev.oss-cn-beijing.aliyuncs.com/picture/2021-05-06/9660870d-6a59-4624-8064-b3a8cbf50d5c.png",
"children": [
{
"id": "1008006",
"name": "影音娱乐",
"picture": "https://yanxuan.nosdn.127.net/f5797ca77cfe413e7753ec69f9bd4bb1.png?quality=95&imageView",
"children": null,
"goods": null
},
{
"id": "1022000",
"name": "3C数码",
"picture": "https://yanxuan.nosdn.127.net/99b8f97b2e5449606fd558574aa15982.png?quality=95&imageView",
"children": null,
"goods": null
},
{
"id": "1028001",
"name": "乐器",
"picture": "https://yanxuan.nosdn.127.net/da0ac345e98c04594b697b56ebc373a5.png?quality=95&imageView",
"children": null,
"goods": null
},
{
"id": "109243035",
"name": "手机配件",
"picture": "https://yanxuan.nosdn.127.net/0276d68f4b7a03bbd16675ada6e707ff.png?quality=95&imageView",
"children": null,
"goods": null
},
{
"id": "109243036",
"name": "车载用品",
"picture": "https://yanxuan.nosdn.127.net/3f45fbcdac7e8532b6a1570e6d7fe171.png?quality=95&imageView",
"children": null,
"goods": null
},
{
"id": "109243046",
"name": "办公文具",
"picture": "https://yanxuan.nosdn.127.net/801583d2f58274b13dc6a03daed1c3c9.png?quality=95&imageView",
"children": null,
"goods": null
}
],
"goods": [
{
"id": "3529022",
"name": "书源彩色按动中性笔",
"desc": "化繁为简,书写本源",
"price": "13.90",
"picture": "https://yanxuan-item.nosdn.127.net/21216556e7d47a4e4277dcb36a16a486.png",
"orderNum": null
},
{
"id": "3506005",
"name": "意外设计小时光便携钢笔",
"desc": "精致与优雅随身藏\n4.16-4.19号停止发货",
"price": "328.00",
"picture": "https://yanxuan-item.nosdn.127.net/f3636b063dad944aa527e4652392a553.png",
"orderNum": null
},
{
"id": "3499024",
"name": "创意金属立体拼酷模型",
"desc": "凤舞龙翔呈现华丽视觉",
"price": "158.00",
"picture": "https://yanxuan-item.nosdn.127.net/f3784ed01706f2f2722f12410a6429c9.png",
"orderNum": null
},
{
"id": "3468024",
"name": "意外设计便携钢笔花信礼盒",
"desc": "生活的美,诗人的心\n4.16-4.19号停止发货",
"price": "398.00",
"picture": "https://yanxuan-item.nosdn.127.net/cc1c57b701f202bc585d8fff3b65571e.png",
"orderNum": null
},
{
"id": "1512000",
"name": "用心记录每一句诗,飞鸟集钢笔本册文具礼盒",
"desc": "谱写情谊之歌",
"price": "89.00",
"picture": "https://yanxuan-item.nosdn.127.net/3892e311f3494d2bcc2c1f8ed9e34271.png",
"orderNum": null
},
{
"id": "1458011",
"name": "谱写爱的篇章,莫扎特钢笔墨水礼盒",
"desc": "谱写爱的乐章",
"price": "88.00",
"picture": "https://yanxuan-item.nosdn.127.net/b20165617d2f901e9ad926f39b737e87.png",
"orderNum": null
},
{
"id": "1111002",
"name": "剪出精致感,金致圆柄复古剪刀",
"desc": "轻薄设计,简约大方",
"price": "9.90",
"picture": "https://yanxuan-item.nosdn.127.net/4c6a9c8a579b00e5e9c7b002d15a33a2.jpg",
"orderNum": null
},
{
"id": "3994432",
"name": "平台严选x敦煌博物馆鹿游仙踪植萃车载香氛",
"desc": "复古敦煌色系,植萃天然香氛,安全健康孕婴可用",
"price": "33.90",
"picture": "https://yanxuan-item.nosdn.127.net/15db90ea8c8c7456c1fee4e18abb25da.png",
"orderNum": null
},
{
"id": "3992720",
"name": "旅途手机好伴侣,三合一车载手机支架",
"desc": "带安全锤和应急割刀的手机支架",
"price": "49.00",
"picture": "https://yanxuan-item.nosdn.127.net/2245a3d6898361236babb66d834e19c8.jpg",
"orderNum": null
}
]
},
{
"id": "109243029",
"name": "运动",
"picture": "http://yjy-xiaotuxian-dev.oss-cn-beijing.aliyuncs.com/picture/2021-05-06/7d19752c-baff-49b6-bd02-5ece1d729214.png",
"children": [
{
"id": "109312000",
"name": "健身大器械",
"picture": "https://yanxuan.nosdn.127.net/6a1d37ffb2e28622a71e3c4415eaee35.png?quality=95&imageView",
"children": null,
"goods": null
},
{
"id": "109312001",
"name": "健身小器械",
"picture": "https://yanxuan.nosdn.127.net/8c9f060e6fddb2b75af851a9a2c60087.png?quality=95&imageView",
"children": null,
"goods": null
},
{
"id": "109312002",
"name": "城市出行",
"picture": "https://yanxuan.nosdn.127.net/b41b50710c3823f44a9f5b549a67ca81.png?quality=95&imageView",
"children": null,
"goods": null
},
{
"id": "109312003",
"name": "运动护具",
"picture": "https://yanxuan.nosdn.127.net/ef9bcb99d88b3a1cfd9d2e120c158c21.png?quality=95&imageView",
"children": null,
"goods": null
},
{
"id": "109313000",
"name": "垂钓",
"picture": "https://yanxuan.nosdn.127.net/c4eeeae307d0562cf3e95303146282b2.png?quality=95&imageView",
"children": null,
"goods": null
},
{
"id": "109313003",
"name": "户外装备",
"picture": "https://yanxuan.nosdn.127.net/6bbd92c68741c857d842f0afd1c7bdd5.png?quality=95&imageView",
"children": null,
"goods": null
}
],
"goods": [
{
"id": "3996603",
"name": "飞宇出游拍摄防抖神器手机稳定器",
"desc": "小巧便携,三轴防抖,模式随心选",
"price": "439.00",
"picture": "https://yanxuan-item.nosdn.127.net/2d0a47a51fa4b3e3857f2010bd83bead.jpg",
"orderNum": null
},
{
"id": "3995454",
"name": "黑科技专利款100%防水保暖针织毛线帽",
"desc": "黑科技专利,做到真正的保暖防水",
"price": "135.00",
"picture": "https://yanxuan-item.nosdn.127.net/3683d874b9623434a10b4ab0c2e6be9f.png",
"orderNum": null
},
{
"id": "3994572",
"name": "伊海诗冬日出行多功能防风保暖围脖",
"desc": "时尚针织印花面料,多种穿戴方式,正反两穿,亲肤保暖",
"price": "35.00",
"picture": "https://yanxuan-item.nosdn.127.net/25effebb31ea6fc58b92f5a6aecda8b9.png",
"orderNum": null
},
{
"id": "3994179",
"name": "运动健身跑步腰包",
"desc": "防水反光设计,大容量双口袋收纳,运动跑步更时尚",
"price": "40.00",
"picture": "https://yanxuan-item.nosdn.127.net/610a693cfd6f01adc6923dc1c0ec91aa.jpg",
"orderNum": null
},
{
"id": "3993694",
"name": "可触屏户外出行手套-合集",
"desc": "户外轻松骑行",
"price": "69.00",
"picture": "https://yanxuan-item.nosdn.127.net/264df9f0faddb60e88a8b108058d25e6.png",
"orderNum": null
},
{
"id": "3988016",
"name": "户外快干轻巧遮阳帽",
"desc": "有效遮阳,可折叠收纳",
"price": "44.00",
"picture": "https://yanxuan-item.nosdn.127.net/310eac832a1d90fbea41c4e6a9e604e4.png",
"orderNum": null
},
{
"id": "3987478",
"name": "可折叠户外防紫外线鸭舌帽",
"desc": "可折叠,透气网纱棒球帽",
"price": "44.00",
"picture": "https://yanxuan-item.nosdn.127.net/87fbba3cf009e5b69dd14781c594ef79.png",
"orderNum": null
},
{
"id": "3550194",
"name": "防晒快干长檐棒球帽",
"desc": "遮阳防晒,保暖新潮",
"price": "69.00",
"picture": "https://yanxuan-item.nosdn.127.net/7d0b373a6a63882783202c836bae264d.png",
"orderNum": null
},
{
"id": "4026116",
"name": "探险者黑胶防晒防雨遮阳伞户外钓鱼伞",
"desc": "防雨遮阳隔绝紫外线",
"price": "169.00",
"picture": "https://yanxuan-item.nosdn.127.net/66090c5de391e43e4516601e14870842.jpg",
"orderNum": null
}
]
},
{
"id": "19999999",
"name": "杂项",
"picture": "http://yjy-xiaotuxian-dev.oss-cn-beijing.aliyuncs.com/picture/2021-05-06/4ff20b9e-8150-4bd3-87a3-0cd6766938dd.png",
"children": [
{
"id": "19999999001",
"name": "家庭清洁杂项",
"picture": "https://yanxuan.nosdn.127.net/718318c0d3b55d011fcb7c7c843902ce.png?quality=95&imageView",
"children": null,
"goods": null
}
],
"goods": [
{
"id": "1589017",
"name": "分场景使用不污染,超细纤维抹布分类5件套",
"desc": "精细分工,洁净家居",
"price": "14.90",
"picture": "https://yanxuan-item.nosdn.127.net/c520bba8209bae877158e8810d889aca.png",
"orderNum": null
},
{
"id": "1548001",
"name": "添加茂金属更强韧,金属色垃圾袋",
"desc": "3卷90只,茂金属添加,柔韧加倍",
"price": "8.90",
"picture": "https://yanxuan-item.nosdn.127.net/251393c0d8db7b0b594ccf86f1e796d0.png",
"orderNum": null
},
{
"id": "1540017",
"name": "随用随丢,懒人真爱地板清洁/除菌湿巾",
"desc": "材质加厚,耐磨珍珠纹",
"price": "8.90",
"picture": "https://yanxuan-item.nosdn.127.net/4c8a1cf2b8e40c250870491307dde11f.jpg",
"orderNum": null
},
{
"id": "1525018",
"name": "灰尘毛发静电吸附地板除尘干巾40片/包",
"desc": "干湿两用,静电除尘",
"price": "7.90",
"picture": "https://yanxuan-item.nosdn.127.net/cf7584e037706a8df54fecfc6dfdd2af.jpg",
"orderNum": null
},
{
"id": "1306019",
"name": "日式和风简约无盖垃圾桶11L",
"desc": "敞口设计易洗易干触手可得的洁净与精致",
"price": "9.90",
"picture": "https://yanxuan-item.nosdn.127.net/ca7144b1f5fda5528dd8e4c40abc3bcc.png",
"orderNum": null
},
{
"id": "1306017",
"name": "大容量干湿分类,脚踏缓降静音垃圾桶",
"desc": "一踩开合,解放双手",
"price": "89.00",
"picture": "https://yanxuan-item.nosdn.127.net/1b322ed8c25f94f1fbf39ddb5fedeb47.jpg",
"orderNum": null
},
{
"id": "1085007",
"name": "一抽即提,免脏手,加厚抽绳垃圾袋3卷60只",
"desc": "18μm及10μm两种厚度,袋身不怕漏,3秒抽绳不脏手",
"price": "9.50",
"picture": "https://yanxuan-item.nosdn.127.net/72af0b7c1d7b0b9710de621788326d2b.png",
"orderNum": null
},
{
"id": "1076004",
"name": "软糯治愈系擦手,雪尼尔擦手球",
"desc": "吸水快干,不易掉屑",
"price": "9.50",
"picture": "https://yanxuan-item.nosdn.127.net/43d99e9b97cc55033b0f59b95a91ae49.png",
"orderNum": null
}
]
}
]
}
3. Home-banner轮播图功能实现
轮播图实现
在HomeBanner.vue
中写出轮播图的结构
在apis
目录下新建home.js
HomeBanner.vue
补充script
中的代码,下面直接给出完整代码
相关文档 https://cn.element-plus.org/zh-CN/component/carousel.html#基础用法
<script setup>
import { getBannerAPI } from '@/apis/home'
import { onMounted, ref } from 'vue'
const bannerList = ref([])
const getBanner = async () => {
const res = await getBannerAPI()
console.log(res)
bannerList.value = res.result
}
onMounted(() => getBanner())
</script>
<template>
<div class="home-banner">
<el-carousel height="500px">
<el-carousel-item v-for="item in bannerList" :key="item.id">
<img :src="item.imgUrl" alt="">
</el-carousel-item>
</el-carousel>
</div>
</template>
<style scoped lang='scss'>
.home-banner {
width: 1240px;
height: 500px;
position: absolute;
left: 0;
top: 0;
z-index: 98;
img {
width: 100%;
height: 500px;
}
}
</style>
import { getBannerAPI } from '@/apis/home'
import { onMounted, ref } from 'vue'
- 引入轮播图数据的 API 方法 getBannerAPI。
- ref 用来创建响应式数据。
- onMounted 是生命周期钩子,在组件挂载后执行。
const bannerList = ref([])
- 定义一个响应式的数组,用来存放轮播图数据。
const getBanner = async () => {
const res = await getBannerAPI()
console.log(res)
bannerList.value = res.result
}
- 异步函数 getBanner() 负责调用后端 API 获取数据。
- res.result 是轮播图的数据数组,赋值给 bannerList。
- console.log(res) 是调试信息,方便开发者查看返回的数据。
home.js
import httpInstance from "@/utils/http";
// 获取banner
export function getBannerAPI(params = {}) {
// 默认为1 商品为2
const { distributionSite = "1" } = params;
return httpInstance({
url: "/home/banner",
params: {
distributionSite,
},
});
}
/**
* @description: 获取新鲜好物
* @param {*}
* @return {*}
*/
export const findNewAPI = () => {
return httpInstance({
url: "/home/new",
});
};
/**
* @description: 获取人气推荐
* @param {*}
* @return {*}
*/
export const getHotAPI = () => {
return httpInstance({
url: "/home/hot",
});
};
/**
* @description: 获取所有商品模块
* @param {*}
* @return {*}
*/
export const getGoodsAPI = () => {
return httpInstance({
url: "/home/goods",
});
};
接口返回的数据:
{
"code": "1",
"msg": "操作成功",
"result": [
{
"id": "19",
"imgUrl": "http://yjy-xiaotuxian-dev.oss-cn-beijing.aliyuncs.com/picture/2021-04-15/6d202d8e-bb47-4f92-9523-f32ab65754f4.jpg",
"hrefUrl": "/category/1013001",
"type": "1"
},
{
"id": "20",
"imgUrl": "http://yjy-xiaotuxian-dev.oss-cn-beijing.aliyuncs.com/picture/2021-04-15/e83efb1b-309c-46f7-98a3-f1fefa694338.jpg",
"hrefUrl": "/category/1005000",
"type": "1"
},
{
"id": "17",
"imgUrl": "http://yjy-xiaotuxian-dev.oss-cn-beijing.aliyuncs.com/picture/2021-04-15/4a79180a-1a5a-4042-8a77-4db0b9c800a8.jpg",
"hrefUrl": "/category/1019000",
"type": "1"
},
{
"id": "16",
"imgUrl": "http://yjy-xiaotuxian-dev.oss-cn-beijing.aliyuncs.com/picture/2021-04-15/dfc11bb0-4af5-4e9b-9458-99f615cc685a.jpg",
"hrefUrl": "/category/1005000",
"type": "1"
},
{
"id": "18",
"imgUrl": "http://yjy-xiaotuxian-dev.oss-cn-beijing.aliyuncs.com/picture/2021-04-15/1ba86bcc-ae71-42a3-bc3e-37b662f7f07e.jpg",
"hrefUrl": "/category/1013001",
"type": "1"
}
]
}
最后的效果
4. Home-面板组件封装
组件封装解决了什么问题?
1.复用问题
2.业务维护问题
新鲜好物与人气推荐模块,在结构上非常相似,只是内容不同,通过组件封装可以实现复用结构的效果。
组件封装核心思路:把可复用的结构只写一次,把可能发生变化的部分抽象成组件参数(props / 插槽)
实现步骤
- 不做任何抽象,准备静态模版
- 抽象可变的部分
主标题和副标题是纯文本,可以抽象成prop传入
主体内容是复杂的模版,抽象成插槽传入
新建HomePanel.vue
HomePanel.vue
代码
<script setup>
// 定义props
defineProps({
// 主标题
title: {
type: String
},
// 副标题
subTitle: {
type: String
}
})
</script>
<template>
<div class="home-panel">
<div class="container">
<div class="head">
<!-- 主标题和副标题 -->
<h3>
{{ title }}<small>{{ subTitle }}</small>
</h3>
</div>
<!-- 主体内容区域 -->
<slot />
</div>
</div>
</template>
<style scoped lang='scss'>
.home-panel {
background-color: #fff;
.head {
padding: 40px 0;
display: flex;
align-items: flex-end;
h3 {
flex: 1;
font-size: 32px;
font-weight: normal;
margin-left: 6px;
height: 35px;
line-height: 35px;
small {
font-size: 16px;
color: #999;
margin-left: 20px;
}
}
}
}
</style>
测试组件,在Home -> index.vue中增加HomePanel
4.1 为什么 HomePanel 与其他组件不一样
4.1.1 使用方式上的不同
从<template>
代码可以看到:
<HomeCategory />
<HomeBanner />
<HomeNew />
<HomeHot />
<HomeProduct />
这些组件都是“单标签闭合”的写法,也就是说,它们本身就完成了自己的渲染逻辑,不需要插槽(slot)或包裹内容。
而 HomePanel 是这样使用的:
<HomePanel>
</HomePanel>
也就是双标签形式,像容器一样,表示它可以包裹别的内容,用到了 Vue 的 <slot>
插槽机制。
4.2 Home-新鲜好物业务实现
HomeNew.vue
<script setup>
import HomePanel from './HomePanel.vue'
import { findNewAPI } from '@/apis/home'
import { onMounted, ref } from 'vue'
// 获取数据
const newList = ref([])
const getNewList = async () => {
const res = await findNewAPI()
newList.value = res.result
}
onMounted(() => getNewList())
</script>
<template>
<HomePanel title="新鲜好物" sub-title="新鲜出炉 品质靠谱">
<ul class="goods-list">
<li v-for="item in newList" :key="item.id">
<RouterLink :to="`/detail/${item.id}`">
<img :src="item.picture" alt="" />
<p class="name">{{ item.name }}</p>
<p class="price">¥{{ item.price }}</p>
</RouterLink>
</li>
</ul>
</HomePanel>
</template>
<style scoped lang='scss'>
.goods-list {
display: flex;
justify-content: space-between;
height: 406px;
li {
width: 306px;
height: 406px;
background: #f0f9f4;
transition: all .5s;
&:hover {
transform: translate3d(0, -3px, 0);
box-shadow: 0 3px 8px rgb(0 0 0 / 20%);
}
img {
width: 306px;
height: 306px;
}
p {
font-size: 22px;
padding-top: 12px;
text-align: center;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
}
.price {
color: $priceColor;
}
}
}
</style>
4.2.1 代码分析
4.2.1.1 <script setup>
区域解释
import HomePanel from './HomePanel.vue'
import { findNewAPI } from '@/apis/home'
import { onMounted, ref } from 'vue'
导入组件和 API 方法
- HomePanel 是我们之前提到的通用面板容器组件。
- findNewAPI 是获取“新鲜好物”数据的 API 函数。
- ref 创建响应式数据,onMounted 是生命周期钩子。
获取商品数据逻辑
在const newList = ref([]) // 初始化为响应式空数组,用来存放商品数据
const getNewList = async () => {
const res = await findNewAPI() // 调用 API 获取数据
newList.value = res.result // 将结果赋值给响应式变量
}
onMounted(() => getNewList()) // 组件挂载时调用获取数据的函数
📌 ref 创建了响应式的数据 newList,一旦数据变化,模板部分会自动重新渲染。
4.2.1.2 <template>
区域解释
<HomePanel title="新鲜好物" sub-title="新鲜出炉 品质靠谱">
使用 HomePanel,并传入主标题和副标题作为 props。中间的内容就是传给它的 插槽内容(slot),用于展示具体的商品。
4.2.1.3 商品列表展示
<ul class="goods-list">
<li v-for="item in newList" :key="item.id">
<RouterLink :to="`/detail/${item.id}`">
<img :src="item.picture" alt="" />
<p class="name">{{ item.name }}</p>
<p class="price">¥{{ item.price }}</p>
</RouterLink>
</li>
</ul>
- v-for 遍历 newList 商品数组
每个商品项:
- 使用 实现点击跳转到详情页(动态路径 /detail/:id)
- 显示商品图片、名称、价格
4.3 Home-图片懒加载指令实现
1.场景:电商网站的首页通常会很长,用户不一定能访问到页面靠下面的图片,这类图片通过懒加载优化手段可以做到
只有进入视口区域才发送图片请求。
2.指令用法:
在图片img身上绑定指令,该图片只有在正式进入到视口区域时才会发送图片网络请求。
实现思路和步骤
核心步骤代码
官方文档 https://vueuse.nodejs.cn/core/useIntersectionObserver/#directive-usage
main.js
4.4 Home-懒加载指令优化
具体实现逻辑,在src下新建目录directives
在这个目录下新建index.js文件
// 定义懒加载插件
import { useIntersectionObserver } from '@vueuse/core'
export const lazyPlugin = {
install (app) {
// 懒加载指令逻辑
app.directive('img-lazy', {
mounted (el, binding) {
// el: 指令绑定的那个元素 img
// binding: binding.value 指令等于号后面绑定的表达式的值 图片url
// console.log(el, binding.value)
const { stop } = useIntersectionObserver(
el,
([{ isIntersecting }]) => {
console.log(isIntersecting)
if (isIntersecting) {
// 进入视口区域
el.src = binding.value
stop()
}
},
)
}
})
}
}
main.js
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
import App from './App.vue'
import router from './router'
// 引入初始化样式文件
import '@/styles/common.scss'
// 引入懒加载指令插件并且注册
import { lazyPlugin } from '@/directives'
// 引入全局组件插件
import { componentPlugin } from '@/components'
const app = createApp(App)
const pinia = createPinia()
// 注册持久化插件
pinia.use(piniaPluginPersistedstate)
app.use(pinia)
app.use(router)
app.use(lazyPlugin)
app.use(componentPlugin)
app.mount('#app')
// 定义懒加载插件
import { useIntersectionObserver } from '@vueuse/core'
export const lazyPlugin = {
install (app) {
// 懒加载指令逻辑
app.directive('img-lazy', {
mounted (el, binding) {
// el: 指令绑定的那个元素 img
// binding: binding.value 指令等于号后面绑定的表达式的值 图片url
// console.log(el, binding.value)
const { stop } = useIntersectionObserver(
el,
([{ isIntersecting }]) => {
console.log(isIntersecting)
if (isIntersecting) {
// 进入视口区域
el.src = binding.value
stop()
}
},
)
}
})
}
}
4.5 Home-Product产品列表实现
Day3 07
home.js
import httpInstance from '@/utils/http'
// 获取banner
export function getBannerAPI (params = {}) {
// 默认为1 商品为2
const { distributionSite = '1' } = params
return httpInstance({
url: '/home/banner',
params: {
distributionSite
}
})
}
/**
* @description: 获取新鲜好物
* @param {*}
* @return {*}
*/
export const findNewAPI = () => {
return httpInstance({
url: '/home/new'
})
}
/**
* @description: 获取人气推荐
* @param {*}
* @return {*}
*/
export const getHotAPI = () => {
return httpInstance({
url: '/home/hot'
})
}
/**
* @description: 获取所有商品模块
* @param {*}
* @return {*}
*/
export const getGoodsAPI = () => {
return httpInstance({
url: '/home/goods'
})
}
HomeProduct.vue
<script setup>
import HomePanel from './HomePanel.vue'
import { getGoodsAPI } from '@/apis/home'
import { onMounted, ref } from 'vue'
import GoodsItem from './GoodsItem.vue'
// 获取数据列表
const goodsProduct = ref([])
const getGoods = async () => {
const res = await getGoodsAPI()
goodsProduct.value = res.result
}
onMounted(() => getGoods())
</script>
<template>
<div class="home-product">
<HomePanel :title="cate.name" v-for="cate in goodsProduct" :key="cate.id">
<div class="box">
<RouterLink class="cover" to="/">
<img v-img-lazy="cate.picture" />
<strong class="label">
<span>{{ cate.name }}馆</span>
<span>{{ cate.saleInfo }}</span>
</strong>
</RouterLink>
<ul class="goods-list">
<li v-for="goods in cate.goods" :key="goods.id">
<GoodsItem :goods="goods" />
</li>
</ul>
</div>
</HomePanel>
</div>
</template>
<style scoped lang='scss'>
.home-product {
background: #fff;
margin-top: 20px;
.sub {
margin-bottom: 2px;
a {
padding: 2px 12px;
font-size: 16px;
border-radius: 4px;
&:hover {
background: $xtxColor;
color: #fff;
}
&:last-child {
margin-right: 80px;
}
}
}
.box {
display: flex;
.cover {
width: 240px;
height: 610px;
margin-right: 10px;
position: relative;
img {
width: 100%;
height: 100%;
}
.label {
width: 188px;
height: 66px;
display: flex;
font-size: 18px;
color: #fff;
line-height: 66px;
font-weight: normal;
position: absolute;
left: 0;
top: 50%;
transform: translate3d(0, -50%, 0);
span {
text-align: center;
&:first-child {
width: 76px;
background: rgba(0, 0, 0, 0.9);
}
&:last-child {
flex: 1;
background: rgba(0, 0, 0, 0.7);
}
}
}
}
.goods-list {
width: 990px;
display: flex;
flex-wrap: wrap;
li {
width: 240px;
height: 300px;
margin-right: 10px;
margin-bottom: 10px;
&:nth-last-child(-n + 4) {
margin-bottom: 0;
}
&:nth-child(4n) {
margin-right: 0;
}
}
}
}
}
</style>
5. 一级分类-使用逻辑函数拆分业务
基于逻辑函数拆分业务是指把同一个组件中独立的业务代码通过函数做封装处理,提升代码的可维护性。
实现步骤:
- 按照业务声明以
use
打头的逻辑函数 - 把独立的业务逻辑封装到各个函数内部
- 函数内部把组件中需要用到的数据或者方法return出去
- 在组件中调用函数把数据或者方法组合回来使用
useBanner.js
// 封装banner轮播图相关的业务代码
import { ref, onMounted } from "vue";
import { getBannerAPI } from "@/apis/home";
export function useBanner() {
const bannerList = ref([]);
const getBanner = async () => {
const res = await getBannerAPI({
distributionSite: "2",
});
console.log(res);
bannerList.value = res.result;
};
onMounted(() => getBanner());
return {
bannerList,
};
}
useCategory.js
// 封装分类数据业务相关代码
import { onMounted, ref } from 'vue'
import { getCategoryAPI } from '@/apis/category'
import { useRoute } from 'vue-router'
import { onBeforeRouteUpdate } from 'vue-router'
export function useCategory () {
// 获取分类数据
const categoryData = ref({})
const route = useRoute()
const getCategory = async (id = route.params.id) => {
const res = await getCategoryAPI(id)
categoryData.value = res.result
}
onMounted(() => getCategory())
// 目标:路由参数变化的时候 可以把分类数据接口重新发送
onBeforeRouteUpdate((to) => {
// 存在问题:使用最新的路由参数请求最新的分类数据
getCategory(to.params.id)
})
return {
categoryData
}
}
index.vue
<script setup>
import GoodsItem from '../Home/components/GoodsItem.vue'
import { useBanner } from './composables/useBanner'
import { useCategory } from './composables/useCategory'
const { bannerList } = useBanner()
const { categoryData } = useCategory()
</script>
<template>
<div class="top-category">
<div class="container m-top-20">
<!-- 面包屑 -->
<div class="bread-container">
<el-breadcrumb separator=">">
<el-breadcrumb-item :to="{ path: '/' }">首页</el-breadcrumb-item>
<el-breadcrumb-item>{{ categoryData.name }}</el-breadcrumb-item>
</el-breadcrumb>
</div>
<!-- 轮播图 -->
<div class="home-banner">
<el-carousel height="500px">
<el-carousel-item v-for="item in bannerList" :key="item.id">
<img :src="item.imgUrl" alt="">
</el-carousel-item>
</el-carousel>
</div>
<div class="sub-list">
<h3>全部分类</h3>
<ul>
<li v-for="i in categoryData.children" :key="i.id">
<RouterLink :to="`/category/sub/${i.id}`">
<img :src="i.picture" />
<p>{{ i.name }}</p>
</RouterLink>
</li>
</ul>
</div>
<div class="ref-goods" v-for="item in categoryData.children" :key="item.id">
<div class="head">
<h3>- {{ item.name }}-</h3>
</div>
<div class="body">
<GoodsItem v-for="good in item.goods" :goods="good" :key="good.id" />
</div>
</div>
</div>
</div>
</template>
<style scoped lang="scss">
.top-category {
h3 {
font-size: 28px;
color: #666;
font-weight: normal;
text-align: center;
line-height: 100px;
}
.sub-list {
margin-top: 20px;
background-color: #fff;
ul {
display: flex;
padding: 0 32px;
flex-wrap: wrap;
li {
width: 168px;
height: 160px;
a {
text-align: center;
display: block;
font-size: 16px;
img {
width: 100px;
height: 100px;
}
p {
line-height: 40px;
}
&:hover {
color: $xtxColor;
}
}
}
}
}
.ref-goods {
background-color: #fff;
margin-top: 20px;
position: relative;
.head {
.xtx-more {
position: absolute;
top: 20px;
right: 20px;
}
.tag {
text-align: center;
color: #999;
font-size: 20px;
position: relative;
top: -20px;
}
}
.body {
display: flex;
justify-content: space-around;
padding: 0 40px 30px;
}
}
.bread-container {
padding: 25px 0;
}
}
.home-banner {
width: 1240px;
height: 500px;
margin: 0 auto;
img {
width: 100%;
height: 500px;
}
}
</style>
5.1 .index.vue代码解析
5.1.1 <script setup>
部分
<script setup>
import GoodsItem from '../Home/components/GoodsItem.vue'
import { useBanner } from './composables/useBanner'
import { useCategory } from './composables/useCategory'
const { bannerList } = useBanner()
const { categoryData } = useCategory()
</script>
说明:
- 使用了 Vue 3 的
<script setup>
语法。 - 引入了组件 GoodsItem.vue —— 用于渲染具体的商品项。
- 引入了两个自定义组合式函数(Composables):
- useBanner():用于获取轮播图数据。
- useCategory():用于获取当前分类的数据,包括分类名称、子分类、子分类下的商品等。
5.1.2 <template>
模板部分
<template>
<div class="top-category">
<div class="container m-top-20">
这部分是整个页面的主要内容,以下是分块分析:
1.面包屑导航
<div class="bread-container">
<el-breadcrumb separator=">">
<el-breadcrumb-item :to="{ path: '/' }">首页</el-breadcrumb-item>
<el-breadcrumb-item>{{ categoryData.name }}</el-breadcrumb-item>
</el-breadcrumb>
</div>
- 使用 el-breadcrumb(来自 Element Plus)组件构建导航路径。
- 显示当前分类的名称。
2.轮播图
<div class="home-banner">
<el-carousel height="500px">
<el-carousel-item v-for="item in bannerList" :key="item.id">
<img :src="item.imgUrl" alt="">
</el-carousel-item>
</el-carousel>
</div>
- 使用 Element Plus 的 el-carousel 实现图片轮播。
- 数据来源于 useBanner() 获取的 bannerList。
3.子分类展示(全部分类)
<div class="sub-list">
<h3>全部分类</h3>
<ul>
<li v-for="i in categoryData.children" :key="i.id">
<RouterLink :to="`/category/sub/${i.id}`">
<img :src="i.picture" />
<p>{{ i.name }}</p>
</RouterLink>
</li>
</ul>
</div>
- 遍历 categoryData.children,展示所有子分类。
- 使用
<RouterLink>
跳转到对应的子分类页面。 - 展示子分类图片和名称。
4.每个子分类下的推荐商品
<div class="ref-goods" v-for="item in categoryData.children" :key="item.id">
<div class="head">
<h3>- {{ item.name }} -</h3>
</div>
<div class="body">
<GoodsItem v-for="good in item.goods" :goods="good" :key="good.id" />
</div>
</div>
- 对每一个子分类,再次展示其推荐商品。
- 使用 GoodsItem 子组件进行商品展示。