项目进行前后端分离
后端采用springboot+mybatis+mysql数据库提供数据接口
前端采用Vue框架获取数据实现页面数据渲染、页面路由
借助springboot实现数据接口,主要内容是Vue实现前端页面功能的流程步骤
轮播图片:http://localhost:8080/data/images
["http://localhost:8080/data/static/fl_03.jpg",
"http://localhost:8080/data/static/fl_06.jpg",
"http://localhost:8080/data/static/fl_11.jpg",
"http://localhost:8080/data/static/fl_14.jpg"]
商品分类:http://localhost:8080/data/types
[{
"id":1,"name":"甜品","img":"http://localhost:8080/data/static/fl_03.jpg"},
{
"id":2,"name":"蛋糕","img":"http://localhost:8080/data/static/fl_06.jpg"},
{
"id":3,"name":"饼干","img":"http://localhost:8080/data/static/fl_11.jpg"},
{
"id":4,"name":"面包","img":"http://localhost:8080/data/static/fl_14.jpg"}]
商品列表:http://localhost:8080/data/foods
[{
"id":1,"name":"芒果慕斯","img":"http://localhost:8080/data/static/good.jpg","price":16,"star":5,"typeId":1},
{
"id":2,"name":"蓝莓蛋挞","img":"http://localhost:8080/data/static/gooddetail.jpg","price":20,"star":4,"typeId":1},
{
"id":3,"name":"草莓蛋糕","img":"http://localhost:8080/data/static/good.jpg","price":12,"star":5,"typeId":1},
{
"id":4,"name":"黑森林","img":"http://localhost:8080/data/static/gooddetail.jpg","price":18,"star":5,"typeId":1},
{
"id":5,"name":"风味曲奇","img":"http://localhost:8080/data/static/gooddetail.jpg","price":20,"star":5,"typeId":2}]
1)、创建vue项目
创建教程:https://blog.csdn.net/booy123/article/details/107127049#2_862
2)、vue项目清理
删除文件:views目录下的文件,components目录下的文件
清理文件内容:
app.vue
<template>
<div id="app">
div>
template>
<style lang="scss">
style>
路由js文件
import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter)
const routes = [
]
const router = new VueRouter({
mode: 'history',
base: process.env.BASE_URL,
routes
})
export default router
地址:https://www.iconfont.cn/
选择需要的图标加入购物车>添加至项目,在个人中心项目中进行下载到本地,把下载的图标文件夹放在public下
views目录下创建四个初始页面(Cart.vue、Index.vue、Order.vue、User.vue),空页面即可
模板代码:
<template>
<div>个人中心div>
template>
<script>
export default {
}
script>
<style scoped>
style>
router/index.js进行路由配置
import Vue from 'vue'
import VueRouter from 'vue-router'
import Index from '../views/Index'
import User from '../views/User'
import Cart from '../views/Cart'
import Order from '../views/Order'
Vue.use(VueRouter)
const routes = [
{
path: '/index',
component: Index
},
{
path: '/user',
component: User
},
{
path: '/cart',
component: Cart
},
{
path: '/order',
component: Order
},
{
path: '*',
component: Index
}
]
const router = new VueRouter({
mode: 'history',
base: process.env.BASE_URL,
routes
})
export default router
<template>
<nav>
<ul>
<router-link to="/index" tag="li" activeClass="active">
<i class="iconfont">i>
首页
router-link>
<router-link to="/cart" tag="li" activeClass="active">
<i class="iconfont">i>
购物车
router-link>
<router-link to="/order" tag="li" activeClass="active">
<i class="iconfont">i>
订单
router-link>
<router-link to="/user" tag="li" activeClass="active">
<i class="iconfont">i>
我的
router-link>
ul>
nav>
template>
<script>
export default {
}
script>
<style lang="scss" scoped>
.active{
color: red}
nav{
position: fixed;
bottom: 0px;
left: 0;
width: 100%;
height: 50px;
background-color: white;
ul{
display: flex;
li{
flex: 1;
line-height: 50px;
text-align: center;
}
}
}
style>
图标生效,在index.html中引入图标样式
<link rel="stylesheet" href="<%= BASE_URL %>iconfont/iconfont.css">
app.vue中注册导航组件
<template>
<div id="app">
<router-view>router-view>
<tabbar>tabbar>
div>
template>
<script>
import tabbar from './components/Tabbar.vue'
export default {
components: {
tabbar: tabbar
}
}
script>
<style lang="scss">
*{
margin: 0;
padding: 0;
}
html{
height: 100%;
}
li{
list-style: none;
}
style>
[
"http://localhost:8080/data/static/f1_03.jpg",
"http://localhost:8080/data/static/f1_06.jpg",
"http://localhost:8080/data/static/f1_11.jpg",
"http://localhost:8080/data/static/f1_14.jpg"
]
封装Swiper.vue,此处引入一个导航插件
cnpm install --save swiper
注意下载的swiper文件路径,不同版本文件位置和名称可能会略微变化
<template>
<div class="swiper-container foodswiper">
<div class="swiper-wrapper">
<slot>
slot>
div>
<div class="swiper-pagination">div>
div>
template>
<script>
import Swiper from 'swiper'
import 'swiper/css/swiper.css'
export default {
mounted () {
// 页面加载完后进行初始化轮播组件
/* eslint-disable no-new */
new Swiper('.foodswiper', {
loop: true, // 是否循环轮播
autoplay: {
delay: 2000 // 延迟2s轮播
},
pagination: {
// 分页放的位置
el: '.swiper-pagination'
}
})
}
}
script>
<style lang="scss" scoped>
.swiper-wrapper {
img {
width: 100%;
}
}
style>
Index.vue
<template>
<div>
<swiper :key="loopList.length">
<div class="swiper-slide" v-for="n in loopList" :key="n">
<img :src="n">
div>
swiper>
div>
template>
<script>
import swiper from '@/components/Swiper'
import axios from 'axios'
export default {
components: {
swiper
},
data () {
return {
loopList: []
}
},
mounted () {
// 页面加载完后获取后端数据
axios({
url: '/data/images'
}).then(res => {
console.log(res.data)
this.loopList = res.data
})
}
}
script>
修改当前页Index.vue
商品分类标签
<div id="head-bar">
商品分类
div>
<div class="cat-list">
<div v-for="c in categories" :key="c.id">
<img :src="c.img" />
div>
div>
安装axios插件npm install axios
根目录src下创建vue.config.js文件:
module.exports = {
devServer: {
proxy: {
'/types': {
target: 'http://localhost:8080',
changeOrigin: true
}
}
}
}
当前页js进行数据请求
axios({
url: '/data/types'
}).then(res => {
console.log(res.data)
this.categories = res.data
})
创建Food.vue组件
<template>
<div>
<div class="shop">
<div class="goods" v-for="i in foodList" :key="i.id">
<div class="goodsbox">
<img :src="i.img">
<div class="title1">¥{
{i.price}}div>
<div class="title2">{
{i.name}}div>
div>
div>
div>
div>
div>
template>
<script>
import axios from 'axios'
export default {
data () {
return {
foodList: []
}
},
mounted () {
console.log(this.$route.params.id) // 输出动态id
axios({
url: '/data/foods?typeId=' + this.$route.params.id
}).then(res => {
this.foodList = res.data
})
}
}
script>
<style lang="scss" scoped>
.shop {
position: relative;
top: 20px;
padding: 1%;
.goods {
width: 50%;
float: left;
text-align: center;
.goodsbox {
background: #fff;
width: 90%;
margin: 10px;
height: 160px;
position: relative;
box-shadow: 0 0 2px #d8d8d8;
}
img {
width: 100%;
height: 100px;
}
.title1 {
width: 50px;
height: 50px;
border-radius: 50%;
position: absolute;
top: 75px;
background-color: #fff;
margin-left: 55px;
text-align: center;
line-height: 35px;
}
.title2 {
position: absolute;
width: 100%;
text-align: center;
}
.title3 {
position: absolute;
top: 130px;
text-align: center;
width: 60px;
height: 20px;
background-color: #393939;
color: #fff;
border-radius: 5px;
left: 30%;
}
}
}
style>
2、动态路由
router/index.js下配置
{
path: '/food/:id',
component: Food
}
Index.vue下配置
动态列表点击事件
<img :src="c.img" @click="changePage(c.id)"/>
methods: {
changePage: function (id) {
console.log(id)
this.$router.push(`/food/${
id}`) // 传url动态参数,这里不是单引号,ES6的模板
}
}
六、购物车
1、页面布局
<div class="cart-detail" v-show="showCartDetail">
<div class="mask" @click="hideCart">div>
<div class="list">
<div class="carttit">
<div class="carttit0">购物车div>
<div class="cartempty" @click="clearCart"><img src="../assets/del.jpg" class="cartimg"/>清空div>
div>
<div class="carttxt">
<div class="carttxt0">满减div>
<div class="carttxt1">订单满100m免运费\配送费div>
div>
<div class="item-list">
<div class="item" v-for="i in cart.list" :key="i.food.id">
<div class="name ellipsis">{
{i.food.name}}div>
<div class="total">¥{
{i.food.price}}div>
<div class="reduce" @click="reduceCart(i.food)">-div>
<div class="num">{
{i.num}}div>
<div class="add" @click="addToCart(i.food)">+div>
div>
div>
div>
div>
<div class="cart">
<div class="data">
<div class="icon" @click="showCart">
<img src="../assets/cart.png"/>
<div class="count">{
{cart.count}}div>
div>
<div class="total">¥{
{cart.total}}div>
div>
<div class="button" :class="{disable:isDisable}">
结算
div>
div>
2、样式
/*下方购物车*/
.cart-detail {
.mask {
position: fixed;
left: 0;
top: 0;
width: 100%;
height: 100%;
z-index: 999999;
background: rgba(0, 0, 0, 0.7);
}
.list {
position: absolute;
left: 0;
bottom: 0px;
width: 100%;
background: #f7f7f7;
padding: 0 0 70px;
z-index: 999999;
.item-list {
height: 130px;
overflow: scroll;
}
}
.item {
display: -webkit-flex;
color: #333;
font-size: 12px;
line-height: 20px;
padding: 5px 15px;
border-bottom: 1px solid #d5d5d5;
.name {
-webkit-flex: 1;
font-size: 12px;
color: #606060;
}
.reduce, .add {
font-size: 14px;
background: #4a4a4a;
width: 20px;
height: 20px;
text-align: center;
border-radius: 50%;
color: #fff;
line-height: 20px;
border: 1px solid #4a4a4a
}
.reduce {
background: #ffffff;
color: #4a4a4a;
}
.total {
width: 120px;
font-size: 13px;
color: #373737;
font-weight: bold
}
.num {
width: 30px;
text-align: center;
margin: 0 5px;
color: #4a4a4a
}
}
}
.cart {
display: -webkit-flex;
position: fixed;
left: 0;
bottom: 0;
width: 100%;
height: 50px;
background: #3c3d41;
z-index: 999999;
.data {
-webkit-flex: 1;
.icon {
position: absolute;
left: 20px;
top: -25px;
width: 50px;
height: 50px;
background: #393939;
border-radius: 50%;
border: 5px solid #3c3d41;
box-shadow: 0 0 5px #000;
img {
position: absolute;
width: 40px;
height: 40px;
top: 5px;
left: 5px;
}
.count {
position: absolute;
left: 35px;
top: -10px;
font-size: 12px;
width: 25px;
height: 25px;
line-height: 25px;
color: #fff;
background: #f45044;
border-radius: 50%;
text-align: center;
}
}
.total {
color: #f45044;
font-size: 16px;
line-height: 40px;
padding-left: 90px;
font-weight: bolder;
}
}
.button {
width: 100px;
height: 100%;
font-size: 14px;
background: #f38815;
color: #fff;
line-height: 50px;
text-align: center;
}
.disable {
color: #fff;
background-color: #333333;
}
}
.carttit {
background: #e5e5e5;
padding: 0 10px;
height: 50px;
line-height: 50px;
}
.carttit0 {
width: 200px;
margin-top: 10px;
height: 30px;
font-size: 16px;
float: left;
color: #4a4a4a;
line-height: 30px;
border-left: 4px solid #ffa404;
text-indent: 15px;
}
.cartempty {
font-size: 12px;
float: right;
color: #4a4a4a;
text-align: right;
}
.carttxt {
padding: 0 15px;
height: 40px;
line-height: 40px;
border-bottom: 1px solid #d5d5d5
}
.carttxt0 {
width: 50px;
height: 25px;
font-size: 12px;
float: left;
color: #f38d1e;
margin-top: 6px;
line-height: 25px;
border: 1px solid #f38d1e;
text-align: center;
border-radius: 3px;
margin-right: 5px;
}
.carttxt1 {
float: left;
font-size: 12px;
color: #4a4a4a;
text-align: right
}
.cartimg {
width: 15px;
height: 20px;
position: relative;
top: 5px;
margin-right: 3px;
}
3、方法
<script>
import axios from 'axios'
export default {
data () {
return {
foodList: [],
cart: {
count: 0, // 商品数量
total: 0, // 商品总价
list: {
} // 键值对对象,以商品id为key,商品和商品数量为value
},
showCartDetail: false, // 默认不展示,点击购物车图标是为true
isDisable: true // 默认样式不可点击
}
},
mounted () {
console.log(this.$route.params.id) // 输出动态id
axios({
url: '/data/foods?typeId=' + this.$route.params.id
}).then(res => {
this.foodList = res.data
})
},
methods: {
// 添加商品
addToCart: function (food) {
this.cart.count++
this.cart.total += food.price
if (this.cart.list[food.id]) {
// 表示list里存在当前添加商品
this.cart.list[food.id].num++
} else {
var temp = {
food: food, num: 1 } // 新加入的商品赋值数量为1
this.cart.list[food.id] = temp // key(food.id)和value(temp)进行存储
}
this.isDisable = false
console.log(this.cart.list)
},
// 减去商品
reduceCart: function (food) {
this.cart.count--
this.cart.total -= food.price
this.cart.list[food.id].num--
if (this.cart.list[food.id].num === 0) {
// 为0时删除商品
delete this.cart.list[food.id]
}
if (this.cart.count === 0) {
this.isDisable = true
}
},
// 清空购物车
clearCart: function () {
this.cart.count = 0
this.cart.total = 0
this.cart.list = {
}
this.isDisable = true
},
hideCart: function () {
this.showCartDetail = false
},
// 显示购物车
showCart: function () {
this.showCartDetail = true
}
}
}
</script>
浏览器显示效果:
源码地址:https://download.csdn.net/download/booy123/12589350