资料地址:https://didi.github.io/cube-ui/#/zh-CN/docs/slide
基本用法:
<cube-slide :data="items" :interval="5000">cube-slide>
:data 代表轮播图的数据;
:interval 代表滚动时间间隔,以毫秒为单位;
打开Home.vue文件,添加轮播图组件。
<cube-slide :data="slider" :interval="5000">
<cube-slide-item v-for="item in slider" :key="item.id">
<router-link :to="`/detail/${item.id}`">
<img :src="item.img" class="slider"/>
router-link>
cube-slide-item>
cube-slide>
轮播图样式:
从后台获取slider轮播图数据。
async created() {
const ret = await this.$http.get('/api/goods');
this.slider = ret.data.slider;
},
data() {
return {
slider: [],
}
},
下面是获取商品接口的代码:
// 获取商品数据
app.get("/api/goods", function(req, res) {
res.json({
code: 0,
slider: [
{
id: 21,
img: "/img/01.jpg"
},
{
id: 22,
img: "/img/02.jpg"
},
{
id: 23,
img: "/img/03.jpg"
},
{
id: 24,
img: "/img/04.jpg"
}
],
});
});
资料地址:https://didi.github.io/cube-ui/#/zh-CN/docs/tab-bar
修改App.vue文件:
<cube-tab-bar show-slider
v-model="selectedLabel"
@change="changeHandler">
<cube-tab v-for="(item, index) in tabs" :key="index"
:icon="item.icon" :label="item.label" :value="item.value">
<span>{{item.label}}span>
cube-tab>
cube-tab-bar>
定义selectedLabel和tabs属性,selectedLabel属性表示默认选定的页签;tabs属性提供了所有页签的数据。
export default {
computed: {
...mapGetters(['isLogin'])
},
data () {
return {
selectedLabel: '/', // 默认选定页签
tabs: [ // 页签数据
{label: 'Home', value: '/', icon: 'cubeic-home'},
{label: 'Cart', value: '/cart', icon: 'cubeic-mall'},
{label: 'Me', value: '/login', icon: 'cubeic-person'},
]
}
},
watch: {
$route(route) {
this.selectedLabel = route.path;
}
}
}
上面在watch中监控路由状态的变化。当路由发生变化,同步tabs选中状态。showBadge方法用于控制购物车商品数量的显示。只有当页签为购物车,并且购物车商品数量大于0的时候,才会显示数量。
/* 页签样式 */
.cube-tab-bar {
position: fixed;
bottom: 0;
left: 0;
right: 0;
background-color: #edf0f4;
}
/* 页签滚动条样式 */
.cube-tab-bar-slider {
top: 0;
}
/* 动画设置 */
.route-move-enter { /* 入场前 */
transform: translate3d(-100%, 0, 0);
}
.route-move-leave-to { /* 出场后 */
transform: translate3d(100%, 0, 0);
}
.route-move-enter,
.route-move-leave-active { /* 播放动画过程中 */
transition: transform 0.3s; /* 完成动画需要的时间 */
}
.child-view {
width: 100%;
position: absolute;
left: 0;
top: 0;
padding-bottom: 36px;
}
span.badge {
display: inline-block;
background: #de3529;
color: white;
width: 1rem;
height: 1rem;
border-radius: 50%;
}
在components目录下新建GoodsList.vue文件,购物车商品数据从外部传入。
<template>
<div>
<div v-for="item in goods" :key="item.id" class="item">
<router-link :to="`/detail/{item.id}`">
<div class="left">
<img :src="item.img" @click.stop.prevent="imgPreview(item.img)"/>
div>
<div class="right">
<div class="title">{{item.title}}div>
<div class="info">
<i class="cubeic-add" @click.stop.prevent="addCart(item)">+i>
<span>{{item.count}}人购买span>
div>
div>
router-link>
div>
div>
template>
<script>
export default {
props: ['goods'],
methods: {
// 添加购物车
addCart(item) {
console.log('添加购物车');
},
// 图片预览
imgPreview(img) {
console.log('图片预览');
},
},
}
script>
<style lang="stylus" scope>
.item {
padding: 10px;
overflow: hidden;
.left {
width: 100px;
float: left;
img {
width: 100%;
}
}
.right {
margin-left: 120px;
text-align: left;
.title {
line-height: 30px;
}
.cubeic-add {
font-size: 22px;
}
}
}
style>
在Home.vue文件中导入GoodsList。
import GoodsList from '@/components/GoodsList.vue'
export default {
name: 'home',
components: {
GoodsList,
},
...
}
在轮播图下定义商品列表。
<goods-list :goods="goods">goods-list>
async created() {
const ret = await this.$http.get('/api/goods');
this.slider = ret.data.slider;
this.goods = ret.data.data;
},
data() {
return {
slider: [],
goods: [],
}
},
获取商品数据的接口如下所示:
// 获取商品数据
app.get("/api/goods", function(req, res) {
res.json({
code: 0,
slider: [
{
id: 21,
img: "/img/01.jpg"
},
{
id: 22,
img: "/img/02.jpg"
},
{
id: 23,
img: "/img/03.jpg"
},
{
id: 24,
img: "/img/04.jpg"
}
],
data: {
fe: [
{
id: 1,
title: "Vue2.x实战",
price: "100",
img: "/img/01.jpg",
count: 100
},
{
id: 2,
title: "React16.x实战",
price: "120",
img: "/img/03.jpg",
count: 100
},
{
id: 3,
title: "nodejs实战",
price: "80",
img: "/img/02.jpg",
count: 100
},
{
id: 4,
title: "前端工程化",
price: "110",
img: "/img/04.jpg",
count: 100
},
{
id: 5,
title: "面试",
price: "200",
img: "/img/02.jpg",
count: 100
},
{
id: 6,
title: "前端安全",
price: "30",
img: "/img/05.jpg",
count: 100
}
],
python: [
{
id: 7,
title: "Python基础语法",
price: "120",
img: "/img/03.jpg",
count: 101
},
{
id: 8,
title: "Flask实战",
price: "80",
img: "/img/02.jpg",
count: 100
},
{
id: 9,
title: "Django实战",
price: "110",
img: "/img/01.jpg",
count: 100
},
{
id: 10,
title: "Python语法进阶",
price: "200",
img: "/img/04.jpg",
count: 100
}
],
java: [
{
id: 11,
title: "java入门实战",
price: "80",
img: "/img/02.jpg",
count: 100
},
{
id: 12,
title: "spring boot实战",
price: "110",
img: "/img/01.jpg",
count: 100
},
{
id: 13,
title: "Java高并发",
price: "30",
img: "/img/04.jpg",
count: 100
}
],
bigdata: [
{
id: 14,
title: "大数据实战",
price: "200",
img: "/img/01.jpg",
count: 100
},
{
id: 15,
title: "Hadoop实战",
price: "120",
img: "/img/03.jpg",
count: 100
},
{
id: 16,
title: "Kafka平台",
price: "80",
img: "/img/02.jpg",
count: 100
}
],
ai: [
{
id: 17,
title: "算法实战",
price: "100",
img: "/img/01.jpg",
count: 100
},
{
id: 18,
title: "个性化推荐",
price: "120",
img: "/img/03.jpg",
count: 100
},
{
id: 19,
title: "机器学习",
price: "80",
img: "/img/02.jpg",
count: 100
},
{
id: 20,
title: "AI实战",
price: "110",
img: "/img/05.jpg",
count: 100
}
]
},
keys: ["fe", "python", "java", "bigdata", "ai"]
});
});
data属性封装了商品分类以及各个分类的商品信息。keys属性封装了所有分类的名称。
修改GoodsList.vue文件,实现addCart方法。
// 添加购物车
addCart(item) {
// 把添加购物车的商品放在state中
this.$store.commit('addCart', item);
},
在state中定义cart属性,该属性存储了购物车的商品。
state: {
token: localStorage.getItem('token') || '', // 令牌
cart: JSON.parse(localStorage.getItem('cart')) || [], // 购物车商品
},
在mutations中定义addCart属性。
// 添加购物车
addCart(state, item) {
const good = state.cart.find(v => v.id === item.id);
if (good) {
good.count += 1;
} else {
state.cart.push({
...item,
count: 1,
});
}
},
当购物车中存在指定商品,则该商品的count属性加1。如果购物车中不存在该商品,则添加商品。
添加购物车后,把购物车商品保存在localStorage中。
store.subscribe((mutation, state) => {
switch (mutation.type) {
case 'setToken':
localStorage.setItem('token', JSON.stringify(state.token));
break;
case 'addCart':
localStorage.setItem('cart', JSON.stringify(state.cart));
break;
}
});
图片预览可以通过cube-ui提供了$createImagePreview全局方法来实现。
// 图片预览
imgPreview(img) {
// 使用ImagePreview组件显示图片
this.$createImagePreview({
imgs: [img],
}).show();
},
drawer组件的使用:https://didi.github.io/cube-ui/#/zh-CN/docs/drawer
<cube-button @click="showDrawer">Show Drawercube-button>
<cube-drawer
ref="drawer"
title="请选择"
:data="data"
@select="selectHandler">cube-drawer>
ref :当前drawer组件的引用
title: 标题
:data 代表分类列表数据,可以是一维数组,也可以是多维数组
@Select:选择事件处理函数
在Home页中定义分类列表:
<cube-drawer ref="drawer" title="请选择分类"
:data="[drawerList]" @select="selectHandler">cube-drawer>
<cube-button @click="showCategory">选择分类cube-button>
定义keys和selectedKeys属性。keys代表分类名称,selectedKeys代表默认的分类。
async created() {
const ret = await this.$http.get('/api/goods');
...
this.keys = ret.data.keys;
this.selectedKeys = this.keys; // 默认选中全部分类
},
data() {
return {
...
keys: [], // 分类
selectedKeys: [], // 选中的分类
}
},
computed: {
...
// 分类列表
drawerList() {
return this.keys.map(v => {
return {
text: labels[v],
value: v,
}
});
}
},
我们在computed中构建分类列表的数据模型。text代表分类名称,value代表分类的值。
定义selectHandler方法,该方法传入一个参数,代表分类的值。该值可能是单个值,也可能是包含多个值的数组。
// 选择分类
selectHandler(val) { // val代表选中分类的值,
this.selectedKeys = [...val];
}
showCategory() {
this.$refs.drawer.show(); // 显示drawer组件
},
之前商品列表是显示所有商品,现在我们要修改为只显示指定分类的商品。
修改商品列表组件的:goods属性:
<goods-list :goods="filterGoods">goods-list>
在computed属性中加入filterGoods属性,该属性函数中获取选定分类的商品。
filterGoods() {
let ret = [];
this.selectedKeys.forEach(v => {
ret = ret.concat(this.goods[v]);
});
return ret;
},
修改Cart.vue文件:
<template>
<div>
<div class="good" v-for="(item,index) in cart" :key="item.id">
{{item.title}}
<div class="right">
<i class="cubeic-remove" @click="countMinus(index)">i>
<span>{{item.count}}span>
<i class="cubeic-add" @click="countAdd(index)">i>
div>
div>
<div>总价 {{total}}div>
<cube-button :disabled="true" v-if="total" >还差{{minTotal-total}}可以购买cube-button>
<cube-button v-else>
下单
<span v-if="!token">(需要登录)span>
cube-button>
div>
template>
<script>
import { mapState, mapGetters } from "vuex";
export default {
data() {
return {
minTotal: 1000
};
},
computed: {
...mapState({
cart: state => state.cart,
token: state => state.token
}),
...mapGetters({
total: "total"
})
},
methods: {
countAdd(index) {
this.$store.commit("countAdd", index);
},
countMinus(index) {
this.$store.commit("countMinus", index);
}
}
};
script>
<style lang="stylus">
.good {
padding: 10px;
text-align: left;
.right {
float: right;
}
i {
font-size: 18px;
}
}
style>
上面minTotal属性的值为1000,代表购物车里面的商品总价必须要大于1000才能够下单。
在getters中定义total属性,该属性返回购物车商品总价。
total(state) {
return state.cart.reduce((num, v) => num += v.count * v.price, 0);
},
在mutations中定义countMinus和countAdd方法。
// 减少商品
countMinus(state, index) {
const item = state.cart[index];
if (item.count > 1) {
item.count -= 1;
} else {
state.cart.splice(index, 1);
}
},
// 增加商品
countAdd(state, index) {
state.cart[index].count += 1;
}
在购物车页签中显示购物车数量:
<cube-tab-bar show-slider
v-model="selectedLabel"
@change="changeHandler">
<cube-tab v-for="(item, index) in tabs" :key="index"
:icon="item.icon" :label="item.label" :value="item.value">
<span>{{item.label}}span>
<span class="badge" v-if="showBadge(item.label)">{{cartTotal}}span>
cube-tab>
cube-tab-bar>
上面在cube-tab中定义了,用于显示购物车商品数量。
样式设置:
span.badge {
display: inline-block;
background: #de3529;
color: white;
width: 1rem;
height: 1rem;
border-radius: 50%;
}
定义showBadge方法,该方法判断labe是否为Cart,并且
computed: {
...mapGetters(['isLogin', 'cartTotal'])
},
methods: {
...
showBadge(label) {
return label === 'Cart' && this.cartTotal > 0;
}
}
修改state,在getters方法中定义cartTotal属性,该属性返回购物车商品数量。
// 计算购物车中商品的总数量
cartTotal(state) {
let num = 0;
state.cart.forEach(v => {
num += v.count;
});
return num;
},