


文章目录
- 一、路由规划
- 三、首页组件
- 3.1. 轮播图SwiperCom
- 3.2. Grid 居家-志趣组件
- 3.3. 类别页组件
- 3.4. 品牌制造商直供
- 3.5. 品牌详情页
- 3.6. 周一周四新品首发
- 3.7. 人气推荐 top组件
- 3.8. 专题精选TopicBox
技术选型
组件 | 版本 | 说明 |
vue | ^2.6.11 | 数据处理框架 |
vue-router | ^3.5.3 | 动态路由 |
vant | ^2.12.37 | 移动端UI |
axios | ^0.24.0 | 前后端交互 |
amfe-flexible | ^2.2.1 | Rem 布局适配 |
postcss-pxtorem | ^5.1.1 | Rem 布局适配 |
less | ^4.1.2 | css编译 |
less-loader | ^5.0.0 | css编译 |
vue/cli | ~4.5.0 | 项目脚手架 |
vue-cli + vant+ less +axios 开发
一、路由规划
项目路由规划配置
如果项目中所有的路由都写在入口文件中,那么将不便于编写项目和后期维护。因此路由需要进行模块化处理。
1. 新建路由配置
在src目录下创建router目录,该目录下新建index.js 用于 编写路由配置
2. 下载vue-router
npm install3. 路由注册
在index.js 文件中安装使用vue-router
router/index.js 内容:
import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter)
4. 路由基础配置
在index.js文件中编写项目路由基础配置
- List item
- 首页模块
- 专题模块
- 分类模块
- 购物车模块
- 我的模块

router/index.js 新增基础路由内容:
// 配置项目路由
const router = new VueRouter({
routes: [{
path: '/',
redirect: '/home' // 重定向
},
{
path: '/home', //首页
name: 'Home',
component: () => import('@/views/home/Home'),
children: [ // 二级路由 不能加'/' 加'/'表示一级路由
{
path: 'searchPopup',
component: () => import('@/views/home/search/searchPopup.vue'),
name: 'searchpopup',
meta: {
isshowtabbar: false
},
}
],
meta: {
isShowTabbar: true
}
},
{
path: '/topic', //专题
name: 'Topic',
component: () => import('@/views/topic/Topic'),
meta: {
isShowTabbar: true
}
},
{
path: '/category', //分类
name: 'Category',
component: () => import('@/views/category/Category'),
meta: {
isShowTabbar: true
}
},
{
path: '/cart', //购物车
name: 'Cart',
component: () => import('@/views/cart/Cart'),
meta: {
isShowTabbar: true
}
},
{
path: '/user', //我的
name: 'User',
component: () => import('@/views/user/User'),
meta: {
isShowTabbar: true
}
},
{
path: '/productDetail', //产品详情
name: 'ProductDetail',
component: () => import('@/views/productdetail/ProductDetail')
},
{
path: '/channel', // 产品分类页面(从首页类别点进去如 居家-餐厨-配件。。。 )
name: 'Channel',
component: () => import('@/views/channel/Channel.vue')
},
{
path: '/brand', // 品牌
name: 'brand',
component: () => import('@/views/brand/Brand.vue')
},
]
})
export
5. 路由挂载
main.js: router 挂载到根实例对象上
根据路由配置,在src目录下的views 文件中,分别创建tabbar 对应的组件文件。
在main.js 文件中引入router 文件下的index.js 路由配置文件,并在根实例中注册。
import router from '@/router/index.js';
new Vue({
render: h => h(App),
router // router 挂载到根实例对象上
}).$mount('#app')
6. AppTabBar
components /AppTabBar.vue

在components 文件中,创建一个AppTabBar.vue组件,配置底部tabbar,直接可以使用vant的tabbar组件

<template>
<div class="app-tab-bar">
<!-- 下方导航 -->
<van-tabbar
v-model="active"
active-color="#ee0a24"
inactive-color="#000"
@change="onChange"
shape="round"
route>
<van-tabbar-item icon="home-o" to="/home">首页</van-tabbar-item>
<van-tabbar-item icon="label-o" to="/topic">专题</van-tabbar-item>
<van-tabbar-item icon="apps-o" to="/category">分类</van-tabbar-item>
<van-tabbar-item icon="shopping-cart-o" to="/cart">购物车</van-tabbar-item>
<van-tabbar-item icon="user-o" to="/user">我的</van-tabbar-item>
</van-tabbar>
</div>
</template>
<script>
export default {
name: "app-tab-bar",
data () {
return {
active:0
}
},
methods: {
onChange(m) {
this.active = m
},
},
};
</script>
<style lang='less' scoped>
</style>
将AppTabBar.vue导入app.vue 跟组件
<template>
<div id="app">
<!-- 路由对应的组件存放到router-view中-->
<router-view></router-view>
<!-- 底部tabbar 组件 -->
<AppTabBar v-show="$route.meta.isShowTabbar"></AppTabBar>
</div>
</template>
<script>
// 引入底部tabbar 组件
import AppTabBar from './components/AppTabBar.vue';
export default {
name: 'App',
components: {
AppTabBar,
},
};
</script>
<style lang='less' scoped>
</style>
7.


二、移动端首页
2.1.首页效果

首页展示的数据

2.2. 首页接口请求
http.js文件中的接口请求
//1. 首页 Home
// 获取首页数据列表
export function getIndexList() {
return instance.get('/index/index')
}
2.3. 首页页面
home/home.vue

2.4. 首页页面
<template>
<div class="home">
<!-- 二级路由坑 -->
<router-view v-if="$route.path === '/home/searchPopup'"></router-view>
<main v-else>
<!--搜索框 -->
<van-search
shape="round"
v-model="value"
show-action
placeholder="请输入搜索关键词"
@search="onSearch"
@cancel="onCancel"
@click="$router.push('/home/searchPopup')"
/>
<!-- 轮播图 -->
<SwiperCom :banner="banner"></SwiperCom>
<!-- grid 居家-志趣组件 -->
<Grid :channel="channel"></Grid>
<!-- 品牌制造商直供 -->
<Support :brandList="brandList"></Support>
<!-- 周一周四新品首发 -->
<Weekproduct
:newGoodsList="newGoodsList"
title="周一周四新品首发"
></Weekproduct>
<!-- 人气推荐 top组件 -->
<!-- v-if 保证了有数据才渲染该组件,等渲染该组件的时候,才执行该组件的生命周期函数-->
<Top :hotGoodsList="hotGoodsList" v-if="hotGoodsList.length>0"></Top>
<!-- 专题精选 -->
<TopicBox :topicList="topicList" title="专题精选"></TopicBox>
<!-- 产品列表 -->
<Weekproduct
v-for="item in categoryList"
:key="item.id"
:newGoodsList="item.goodsList"
:title="item.name"
>
</Weekproduct>
</main>
</div>
</template>
<script>
// 导入轮播图组件
import SwiperCom from "@/components/SwiperCom";
import { getIndexList } from "@/https/http.js";
// 引入 grid 居家-志趣组件
import Grid from "@/views/home/Grid";
// 引入support 组件-品牌制造商直供
import Support from "@/views/home/Support";
import Weekproduct from "@/views/home/Weekproduct";
import TopicBox from "@/views/home/TopicBox";
import Top from '@/views/home/Top'
export default {
name: "home",
data() {
return {
value: "",
banner: [], //轮播图
channel: [], //居家-志趣数据
brandList: [], // 品牌制造商数据
newGoodsList: [], // 周一周四新品首发数据
hotGoodsList:[], // 人气推荐
topicList: [], // 专题精选
categoryList: [] //产品列表
};
},
components: {
SwiperCom,
Grid,
Support,
Weekproduct,
TopicBox,
Top,
},
created() {
// 发送请求,获取数据
getIndexList().then((res) => {
console.log("res", res);
this.banner = res.data.banner;
this.channel = res.data.channel;
this.brandList = res.data.brandList;
this.newGoodsList = res.data.newGoodsList;
this.topicList = res.data.topicList;
this.categoryList = res.data.categoryList;
this.hotGoodsList = res.data.hotGoodsList;
});
},
methods: {
onSearch() { },
onCancel() { },
},
};
</script>
<style lang="less" scoped>
.home {
padding-bottom: 100px;
box-sizing: border-box;
}
</style>
三、首页组件
3.1. 轮播图SwiperCom


<template>
<!-- 轮播图 -->
<div class="swiper-com">
<van-swipe class="my-swipe" :autoplay="1000" indicator-color="white">
<van-swipe-item v-for="item in banner" :key="item.id" >
<img :src="item.image_url" alt="">
</van-swipe-item>
</van-swipe>
</div>
</template>
<script>
export default {
name:'swiper-com',
props:['banner'],
}
</script>
<style lang="less" scoped>
.swiper-com{
width: 100%;
img{
width: 100%;
}
}
</style>
3.2. Grid 居家-志趣组件


<template>
<div class="gird">
<van-grid :column-num="5" channel.length>
<van-grid-item
v-for="item in channel"
:key="item.id"
:icon="item.icon_url"
:text="item.name"
@click="btn(item.id)"
/>
</van-grid>
</div>
</template>
<script>
export default {
name: "gird",
props: ["channel"],
methods: {
btn(id){
this.$router.push({path: '/channel', query:{id: id}})
}
}
};
</script>
<style>
</style>
3.3. 类别页组件

Channel.vue

<template>
<div class="channel-box">
<van-tabs sticky @change="changeFn">
<van-tab v-for="item in brotherCategory" :key="item.id" :title="item.name" >
<h4>{{ item.name }}</h4>
<p>{{ front_desc }}</p>
<!-- 产品列表 -->
<Products :goodsList="goodsList" />
</van-tab>
</van-tabs>
</div>
</template>
<script>
import { GetCateGoryData, GetCateGoryList } from "@/https/http";
import Products from "@/components/Products";
export default {
name: "channel",
data() {
return {
id_: "0", // 当前类别的id
page: 1, // 当前页数
size: 1000, //每页显示条数
brotherCategory: [], // 分类数组
goodsList: [], //当前类别对应的商品列表
front_desc: "",
};
},
created() {
this.id_ = this.$route.query.id;
this.getCategoryData(); //获取所有分类数据
this.getCategoryListData(); //获取当前类别对应的产品数组
},
methods: {
//获取所有分类数据
getCategoryData() {
GetCateGoryData({ id: this.id_ }).then((res) => {
this.brotherCategory = res.data.brotherCategory; // 全部分类数组
this.currentCategory = res.data.currentCategory; // 当前分类对象
this.front_desc = this.currentCategory.front_desc; // 当前类别文字描述
});
},
//获取当前类别对应的产品数组
getCategoryListData() {
GetCateGoryList({
categoryId: this.id_,
page: this.page,
size: this.size,
}).then((res) => {
console.log(33, res);
if (res.errno == 0) {
this.goodsList = res.data.goodsList;
}
});
},
// / 切换分类
changeFn(title, name) {
// title 下标
// name: 分类标题
this.brotherCategory.forEach((item) => {
if (item.name == name) {
this.id_ = item.id;
}
});
this.getCategoryListData();
this.getCategoryData();
},
},
components: {
Products,
},
};
</script>
<style lang="less" scoped>
.channel-box {
text-align: center;
font-size: 16px;
line-height: 40px;
p {
color: #666;
}
}
</style>
3.4. 品牌制造商直供


<template>
<div class="support">
<ul>
<li v-for="item in brandList" :key="item.id" @click="clickFn(item.id)">
<img :src="item.pic_url" alt="">
<h4>{{item.name}}</h4>
<p>{{item.floor_price|moneyFlrmat}}</p>
</li>
</ul>
</div>
</template>
<script>
export default {
name:'support',
props:['brandList'],
methods: {
clickFn(id){
this.$router.push({path:'/brand', query:{id: id}})
}
}
}
</script>
<style lang="less" scoped>
.support>ul{
width: 100%;
display: flex;
justify-content: space-between;
flex-wrap: wrap;
li{
width: 49%;
position: relative;
img{
width: 100%;
}
h4 {
font-size: 16px;
position: absolute;
left: 20px;
top: 30px;
}
p {
font-size: 15px;
position: absolute;
left: 30px;
top: 60px;
color: red;
}
}
}
</style>
3.5. 品牌详情页


<template>
<div class="brand-box">
<h4>{{ name }}</h4>
<img :src="list_pic_url" alt="" />
<p>
{{ simple_desc }}
</p>
</div>
</template>
<script>
import { GetBrandList } from '@/https/http'
export default {
name: 'brand',
data() {
return {
name: '',
simple_desc: '',
list_pic_url: '',
page: 1,
size: 1000,
}
},
created() {
this.getBrandData()
},
methods: {
// 发送请求,获取品牌详情页数据
getBrandData() {
GetBrandList({ id: this.$route.query.id }).then((res) => {
console.log(res);
=
this.simple_desc = res.data.brand.simple_desc
this.list_pic_url = res.data.brand.list_pic_url
})
},
}
}
</script>
<style lang="less" scoped>
.brand-box {
text-align: center;
font-size: 16px;
line-height: 40px;
img {
width: 100%;
}
}
</style>
3.6. 周一周四新品首发


<template>
<div class="week-product">
<div class="mytitle">
<span></span>
<h3>{{ title }}</h3>
</div>
<ul>
<li v-for="item in newGoodsList" :key="item.id" @click="clickFn(item.id)">
<img :src="item.list_pic_url" alt="" />
<p>{{ item.name }}</p>
<p>{{ item.retail_price }}</p>
</li>
</ul>
</div>
</template>
<script>
export default {
name: 'week-product',
props: ['newGoodsList', 'title'],
methods: {
clickFn(id) {
this.$router.push({ path: '/productDetail', query: { id: id } })
}
}
}
</script>
<style lang="less" scoped>
.week-product {
.mytitle {
text-align: center;
font-size: 16px;
margin-top: 20px;
position: relative;
height: 50px;
span {
width: 50%;
height: 2px;
background-color: #ccc;
display: inline-block;
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
}
h3 {
width: 30%;
background-color: #fff;
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
}
}
ul {
display: flex;
justify-content: space-between;
flex-wrap: wrap;
li {
width: 49%;
img {
width: 100%;
}
p {
text-align: center;
font-size: 16px;
}
}
}
}
</style>
3.7. 人气推荐 top组件


<template>
<div class="top-box">
<h3>人气推荐</h3>
<div class="content" v-for="item in hotGoodsList" :key="item.id">
<van-card
:key="item.id"
:price="item.retail_price"
:desc="item.goods_brief"
:title="item.name"
:thumb="item.list_pic_url"
@click="clickFn(item.id)"
/>
</div>
</div>
<!-- v-for="item in hotGoodsList -->
</template>
<script>
export default {
name: 'top',
props: ['hotGoodsList'],
created() {
console.log(555, this.hotGoodsList);
},
methods: {
clickFn(id) {
this.$router.push({ path: '/productDetail', query: { id: id } })
}
}
}
</script>
<style lang="less" scoped>
.top-box {
h3 {
font-size: 22px;
line-height: 60px;
text-align: center;
}
}
</style>
3.8. 专题精选TopicBox


<template>
<div class="topic">
<h3>{{ title }}</h3>
<van-swipe class="my-swipe" :autoplay="1000" indicator-color="white">
<van-swipe-item
v-for="item in topicList"
:key="item.id"
>
<img :src="item.item_pic_url" alt="" />
<p>{{ item.title }}</p>
<p>{{ item.subtitle }}</p>
</van-swipe-item>
</van-swipe>
</div>
</template>
<script>
export default {
name: 'topic',
props: ['topicList', 'title'],
}
</script>
<style lang="less" scoped>
.topic {
width: 100%;
text-align: center;
font-size: 16px;
h3 {
font-size: 22px;
line-height: 60px;
text-align: center;
}
.my-swipe {
display: flex;
img {
width: 100%;
height: 200px;
}
}
}
</style>
















