缘由
去年寒假里学习了Vue.js,开学后想做一个完整的练手项目实战一下,最后决定模仿小米手机官网做一个网站项目,具体参考了Github上一位作者的项目。
现在已经基本完成了,分享在CSDN作为学习记录。
简介
- 技术支持 :该项目采用前后端分离的设计结构,使用Vue2+Vue-Router+Vuex+Axios+ElementUI作为前端技术框架,后端采用了Koa框架,使用MySQL数据库作为数据存储支持。
- 项目功能:项目包含登录、注册、商品浏览、商品检索、收藏、购物车、订单生成与结算等功能
- 项目页面:项目整体页面分为首页、全部商品、商品详情页、登录注册、我的购物车、我的订单、订单结算等
- 后端模式:使用MVC模式,设计对应的模型层、控制层。
- 数据支持:使用Github项目中的小米商城数据
首页实现
首页主要分为4个部分:头部信息栏、导航栏、商品列表、底部信息栏。
1. 头部信息栏
头部信息栏主要功能为:登录注册按钮、登录成功后显示用户信息、我的订单、我的收藏、购物车商品数目信息。
效果图
效果实现
- 源码
<template><div class="topbar"><div class="nav"><ul><li v-if="!this.$store.getters.getUser"><el-button type="text" @click="login">登录</el-button><span class="sep">|</span><el-button type="text" @click="register = true">注册</el-button></li><li v-else>
欢迎
<el-popover placement="top" width="180" v-model="visible"><p>确定退出登录吗?</p><div style="text-align: right; margin: 10px 0 0"><el-button size="mini" type="text" @click="visible = false">取消</el-button><el-button type="primary" size="mini" @click="logout">确定</el-button></div><el-button type="text">{{this.$store.getters.getUser.userName}}</el-button></el-popover></li><li><router-link to="/order">我的订单</router-link></li><li><router-link to="/collect">我的收藏</router-link></li><li :class="getNum > 0 ? 'shopCart-full' : 'shopCart'"><router-link to="/shoppingCart"><i class="el-icon-shopping-cart-full"></i> 购物车
<span class="num">({{getNum}})</span></router-link></li></ul></div></div></template>
- 逻辑实现
- 进入首页之后,判断localStorage内是否存在登录用户,若不存在则设置登录列表初始状态为隐藏。
- 点击触发 login 事件后更改vuex中的setShowLogin显示登录组件。
- 登录成功后将用户信息存入vuex中,通过watch监听vuex的登录状态。
- 若已经登录,从后台查询该用户的购物车信息,并通过vuex中的setShoppingCart方法更新购物车信息,从而获取购物车内的商品数量并显示在头部栏中。
- 为我的订单和我的收藏添加路由信息,实现点击跳转对应页面。
<script>exportdefault{data(){return{activeIndex:"",// 头部导航栏选中的标签search:"",// 搜索条件register:false,// 是否显示注册组件visible:false// 是否退出登录};},created(){if(localStorage.getItem("user")){this.setUser(JSON.parse(localStorage.getItem("user")));}},watch:{// 获取vuex的登录状态getUser:function(val){if(val ===""){// 用户没有登录this.setShoppingCart([]);}else{// 用户已经登录,获取该用户的购物车信息this.$axios.post("/api/user/shoppingCart/getShoppingCart",{user_id: val.user_id
}).then(res=>{if(res.data.code ==="001"){// 001 为成功, 更新vuex购物车状态this.setShoppingCart(res.data.shoppingCartData);}else{// 提示失败信息this.notifyError(res.data.msg);}}).catch(err=>{return Promise.reject(err);});}}},methods:{...mapActions(["setUser","setShowLogin","setShoppingCart"]),login(){// 点击登录按钮, 通过更改vuex的showLogin值显示登录组件this.setShowLogin(true);},// 退出登录logout(){this.visible =false;// 清空本地登录信息
localStorage.setItem("user","");// 清空vuex登录信息this.setUser("");this.notifySucceed("成功退出登录");},// 接收注册子组件传过来的数据isRegister(val){this.register = val;}},computed:{...mapGetters(["getUser","getNum"]);}};</script>
- 登录组件
<div id="myLogin"><el-dialog title="登录" width="300px"center:visible.sync="isLogin"><el-form :model="LoginUser":rules="rules" status-icon ref="ruleForm"class="demo-ruleForm"><el-form-item prop="name"><el-input prefix-icon="el-icon-user-solid" placeholder="请输入账号" v-model="LoginUser.name"></el-input></el-form-item><el-form-item prop="pass"><el-input
prefix-icon="el-icon-view"
type="password"
placeholder="请输入密码"
v-model="LoginUser.pass"></el-input></el-form-item><el-form-item><el-button size="medium" type="primary" @click="Login" style="width:100%;">登录</el-button></el-form-item></el-form></el-dialog></div><script>exportdefault{name:"MyLogin",data(){// 用户名的校验方法letvalidateName=(rule, value, callback)=>{if(!value){returncallback(newError("请输入用户名"));}// 用户名以字母开头,长度在5-16之间,允许字母数字下划线const userNameRule =/^[a-zA-Z][a-zA-Z0-9_]{4,15}$/;if(userNameRule.test(value)){this.$refs.ruleForm.validateField("checkPass");returncallback();}else{returncallback(newError("字母开头,长度5-16之间,允许字母数字下划线"));}};// 密码的校验方法letvalidatePass=(rule, value, callback)=>{if(value ===""){returncallback(newError("请输入密码"));}// 密码以字母开头,长度在6-18之间,允许字母数字和下划线const passwordRule =/^[a-zA-Z]\w{5,17}$/;if(passwordRule.test(value)){this.$refs.ruleForm.validateField("checkPass");returncallback();}else{returncallback(newError("字母开头,长度6-18之间,允许字母数字和下划线"));}};return{LoginUser:{name:"",pass:""},rules:{name:[{validator: validateName,trigger:"blur"}],pass:[{validator: validatePass,trigger:"blur"}]}};},computed:{// 获取vuex中的showLogin,控制登录组件是否显示isLogin:{get(){returnthis.$store.getters.getShowLogin;},set(val){this.$refs["ruleForm"].resetFields();this.setShowLogin(val);}}},methods:{...mapActions(["setUser","setShowLogin"]),Login(){this.$refs["ruleForm"].validate(valid=>{if(valid){this.$axios.post("/api/users/login",{userName:this.LoginUser.name,password:this.LoginUser.pass
}).then(res=>{// “001”代表登录成功,其他的均为失败if(res.data.code ==="001"){this.isLogin =false;let user =JSON.stringify(res.data.user);
localStorage.setItem("user", user);this.setUser(res.data.user);this.notifySucceed(res.data.msg);}else{this.$refs["ruleForm"].resetFields();this.notifyError(res.data.msg);}}).catch(err=>{return Promise.reject(err);});}else{returnfalse;}});}}};</script>
2. 导航栏及轮播图
导航栏主要功能为首页、全部商品路由跳转,条件搜索、轮播图显示
效果图
效果实现
- 路由分配
<el-menu-item index="/">首页</el-menu-item><el-menu-item index="/goods">全部商品</el-menu-item><el-menu-item index="/about">关于我们</el-menu-item>
- 搜索功能
<el-input placeholder="请输入搜索内容" v-model="search"><el-button slot="append" icon="el-icon-search" @click="searchClick"></el-button></el-input>searchClick(){if(this.search !=""){// 跳转到全部商品页面,并传递搜索条件this.$router.push({path:"/goods",query:{search:this.search }});this.search ="";}}
- 轮播图
<div class="block"><el-carousel height="460px"><el-carousel-item v-for="item in carousel":key="item.carousel_id"><img style="height:460px;":src="$target + item.imgPath":alt="item.describes"/></el-carousel-item></el-carousel></div>// 获取轮播图数据this.$axios.post("/api/resources/carousel",{}).then(res=>{this.carousel = res.data.carousel;}).catch(err=>{return Promise.reject(err);});
3. 商品列表及切换
商品列表主要显示从后台获取到的商品信息
效果图
效果实现
- 商品列表
<ul><li v-for="item in list":key="item.product_id"><router-link :to="{ path: '/goods/details', query: {productID:item.product_id} }"><img :src="$target +item.product_picture" alt /><h2>{{item.product_name}}</h2><h3>{{item.product_title}}</h3><p><span>{{item.product_selling_price}}元</span><span
v-show="item.product_price != item.product_selling_price"class="del">{{item.product_price}}元</span></p></router-link></li><li v-show="isMore && list.length>=1" id="more"><router-link :to="{ path: '/goods', query: {categoryID:categoryID} }">
浏览更多
<i class="el-icon-d-arrow-right"></i></router-link></li></ul>getPromo(categoryName, val, api){
api = api !=undefined? api :"/api/product/getPromoProduct";this.$axios
.post(api,{
categoryName
}).then(res=>{this[val]= res.data.Product;}).catch(err=>{return Promise.reject(err);});}
- 类别菜单切换
<ul><li
v-for="item in val":key="item":class="activeClass == item ? 'active':''"
@mouseover="mouseover($event,item)"><router-link to><slot :name="item"></slot></router-link></li></ul>props:["val"],name:"MyMenu",data(){return{activeClass:1};},methods:{// 通过mouseover事件控制当前显示的商品分类,1为该类别的热门商品mouseover(e, val){this.activeClass = val;}},watch:{// 向父组件传过去当前要显示的商品分类,从而更新商品列表activeClass:function(val){this.$emit("fromChild", val);}}
4. 底部信息栏
底部信息栏仅几个链接及样式设计,不作详细介绍。
效果图
往期内容
Vue2实现仿小米商城练手项目前端篇(1-项目介绍)
版权归原作者 freshfish丶 所有, 如有侵权,请联系我们删除。