vue项目-商城app
.gitignore 里面设置上传项目时可以忽略的文件,eg:node_modules一、制作首页hadder和tabbar完成Header区域,使用的的是mint-ui中的Header组件制作底部的Tabbar区域,使用的是mui中的Tabbar.html- 在制作购物车小图标的时候,先把扩展图标的css样式,拷贝到项目中- 再拷贝我们的字体库ttf文件到项目中- 为购物车小图标添...
.gitignore 里面设置上传项目时可以忽略的文件,eg:node_modules
一、制作首页hadder和tabbar
- 完成Header区域,使用的的是mint-ui中的Header组件
- 制作底部的Tabbar区域,使用的是mui中的Tabbar.html
- 在制作购物车小图标的时候,先把扩展图标的css样式,拷贝到项目中
- 再拷贝我们的字体库ttf文件到项目中
- 为购物车小图标添加图像样式 - 要在中间区域放置一个router-view来展示路由匹配的组件
切换页面(改造tabbar为router-link):
- 在main.js中引入vue-router
import VueRouter from "vue-router"
- 将a标签改为router-link,href改为to
- 在router.js中引入路由组件
import VueRouter from 'vue-router'
// 导入对应的路由组件
import HomeContainer from '../components/tabbar/HomeContainer.vue'
import MemberContainer from '../components/tabbar/MemberContainer.vue'
import ShopcarContainer from '../components/tabbar/ShopcarContainer.vue'
import SearchContainer from '../components/tabbar/SearchContainer.vue'
var router = new VueRouter({
routes: [// 配置路由规则
{ path: '/', redirect: '/home' },
{ path: '/home', component: HomeContainer },
{ path: '/member', component: MemberContainer },
{ path: '/shopcar', component: ShopcarContainer },
{ path: '/search', component: SearchContainer }
],
linkActiveClass: 'mui-active' // 覆盖默认的路由高亮的类,默认的类叫做router-link-active
})
export default router
- 给App.vue中添加一个router-view标签
scss中&符号是交集选择器,不写是后代选择器
二、制作轮播图
-
获取数据使用vue-resource
-
在main.js中导入vue-rosource
-
在main.js中配置请求路径
Vue.http.options.root = 'http://www.liulongbin.top:3005'
- 在script中methods中定义方法,获取数据
<script>
export default {
data () {
return{
lunbotulist:[]
}
},
created() {
this.getlunbotu()
},
methods: {
getlunbotu() {
this.$http.get('api/getlunbo').then(res => {
if(res.body.status === 0) {
this.lunbotulist = res.body.message
}
})
}
}
}
</script>
渲染页面使用v-for
循环,在组件中使用v-for时,要使用:key
三、九宫格到六宫格改造工程
- 使用mui中的组件,将九宫格改造为六宫格
- 图标可引入自己的图标,修改样式就ok
四、组件切换时的动画效果
用transition将中间内容包起来
<transition>
<router-view></router-view>
</transition>
设置类
<style>
.v-enter,
.v-leave-to {
opacity: 0;
transform: translateX(100%);
}
.v-enter-active,
.v-leave-active {
transition: all 0.5s ease;
}
</style>
问题:
- Header栏向右偏移
- 底部也会偏移,并且出现滚动条
- 要进入的元素先向左然后向上飘
解决:
- 出现滚动条的原因:因为在切换页面的时候,他的真正宽度是两个页面拼接起来的宽度
- 给外层container添加一个overflow-x: hidden
.app-container {
padding-top:40px;
padding-bottom: 50px;
overflow-x: hidden;
}
- 解决底部滚动条问题
.v-enter {
opacity: 0;
transform: translateX(100%);
}
.v-leave-to {
opacity: 0;
transform: translateX(-100%);
}
.v-enter-active,
.v-leave-active {
transition: all 0.5s ease;
}
- 解决元素飘动问题,给
.v-leave-to
动画加一个定位
.v-enter {
opacity: 0;
transform: translateX(100%);
}
.v-leave-to {
opacity: 0;
transform: translateX(-100%);
position: absolute;
}
.v-enter-active,
.v-leave-active {
transition: all 0.5s ease;
}
五、改造新闻资讯的路由连接
- 将a标签改为router-link,href改为to,to=‘home/newslist’
- 在router.js导入对应的路由组件
六、新闻资讯页面绘制
- 绘制界面,使用mui中的midia-list.html
- 使用vue-resource获取数据
- 渲染真实数据
给时间定义一个全局过滤器
安装moment npm install moment -S
import moment from 'moment' // 导入格式化的时间插件
Vue.filter('dateFormat', function (dataStr, pattern = 'YYYY-MM-DD HH:mm:ss') {
return moment(dataStr).format(pattern)
})
使用:
<span>发表时间:{{item.add_time | dateFormat('YYYY-MM-DD')}}</span>
七、点击新闻资讯列表跳转到新闻详情
- 把列表中的每一项改造为 router-link,同时在跳转的时候应该提供唯一的id标识符
<router-link :to="'/home/newsinfo/'+item.id">
- 创建新闻详情的组件页面NewsInfo.vue
- 在路由模块router.js中,将新闻详情的路由地址和组件页面对应起来
<script>
export default {
data(){
return {
id: this.$route.params.id, //将url地址中传递过来的id值,挂载到data上,方便以后调用
newsinfo:{}
}
}
}
</script>
- 实现新闻详情页面布局和渲染
八、单独封装一个 comment.vue 的评论子组件
-
先创建一个单独的comment.vue组件模板
-
在需要使用comment组件的页面中,先动手导入comment组件
import comment from './comment.vue'
-
在父组件中,使用
components
属性。将刚才导入comment组件注册为自己的子组件
methods: {
getNewsInfo(){//获取新闻详情
this.$http.get('api/getnew/'+this.id).then(res => {
if(res.body.status ===0 ){
this.newsinfo = res.body.message[0];
}else {
Toast('加载失败');
}
})
}
},
components: {// 用来注册子组件
"comment-box": comment
}
-
将注册子组件时候的注册名称以标签形式在页面中引用即可
<comment-box></comment-box>
-
获取所有的评论数据显示到页面中
getComments()
方法父组件向子组件传值
<comment-box :id="this.id"></comment-box>
props: ['id']
九、点击加载更多评论的功能
- 为加载更多按钮绑定点击事件,在事件中,请求下一页数据
<mt-button type="danger" size="large" plain @click="getMore">加载更多</mt-button>
-
点击加载更多,让pageIndex++,然后重新调用this.getComments()方法,重新获取最新一页的数据
-
为了防止新数据覆盖老数据的情况,点击加载更多的时候,每当获取到新数据,应该让老数据调用数组concat方法,拼接新数组
methods:{
getComments(){// 获取评论
this.$http.get('api/getcomments/' + this.id + '?pageindex=' + this.pageIndex).then(res => {
if(res.body.status === 0){
// 获取新评论时不会把老数据清空覆盖
this.comments = this.comments.concat(res.body.message);
}else {
Toast('获取评论失败');
}
})
},
getMore(){// 加载更多
this.pageIndex++;
this.getComments();
}
}
十、发表评论
- 把文本框做双向数据绑定
v-model='msg'
- 为发表按钮绑定一个事件
@click="postComment"
- 校验评论内容是否为空,如果为空则Toast提示用户
- 通过vue-resource发送一个请求,把评论内容提交给服务器并保存
- 当发表评论完成后,重新刷新列表,以查看最近的评论
- 如果调用getComments方法重新刷新评论列表的话,可能只能得到 最后一页的评论,前几页的评论获取不到
- 换一种思路:当评论成功后,在客户端手动拼接出一个最新的评论对象,然后调用数组的unshift方法,把最新的评论追加到date中comments的开头;这样就能完美实现刷新评论列表的需求
在main.js中全局设置post时候表单数据格式组织形式 application/x-www-form-urlencoded
Vue.http.options.emulateJSON = true
postComment(){// 发表评论
// 校验是否为空内容
if(this.msg.trim().length === 0){
return Toast('评论内容不能为空!');
}
this.$http.post("api/postcomment/" + this.$route.params.id,{content:this.msg.trim()}).then(function(res) {
if(res.body.status === 0){
//拼接出一个评论对象
var cmt = {user_name: '匿名用户', add_time: Date.now(), content: this.msg.trim()};
this.comments.unshift(cmt);
this.msg = "";
}
})
}
十一、制作图片分享页面
- 改造图片分析按钮为路由的链接并显示对应的组件页面
- 绘制图片列表组件页面结构并美化样式
- 制作顶部的滑动条
- 制作底部的图片列表 - 获取所有分类,并渲染分类列表
制作顶部滑动条的坑
另有博客专门介绍这部分内容:vue在引用mui.js文件时会遇到的各种问题
- 使用MUI中的
tab-top-webview-main.html
- 需要把slider区域的
mui-fullscreen
类去掉 - 滑动条无法正常触发滑动,通过检查官方文档,发现这是JS组件,需要被初始化一下
- 导入 mui.js
- 调用官方提供的方式去初始化:
mui('.mui-scroll-wrapper').scroll({
deceleration: 0.0005 //flick 减速系数,系数越大,滚动速度越慢,滚动距离越小,默认值0.0006
});
- 我们在初始化滑动条的时候,导入的mui.js ,但是控制台报错:
Uncaught TypeError: 'caller', 'callee', and 'arguments' properties may not be accessed on strict mode
- mui.js 中用到了’caller’,‘callee’,and’arguments’,但是webpack打包好的bundle.js 中,默认是启用严格模式的,所以这两者冲突了
- 解决方案:
a. 把mui.js中的非严格模式的代码改掉,但是不现实
b. 把webpack打包时候的严格模式禁用掉
- 最终,我们选择了plan B移除严格模式:使用这个插件babel-plugin-transform-remove-strict-mode
- 刚进入图片分享页面的时候滑动条无法正常工作,然后发现如果要初始化滑动条,必须要等DOM元素加载完毕,所以我们把初始化滑动条的代码,搬到了mounted生命周期函数中
- 当滑动条调试OK后,发现tabbar无法正常工作了,这时需要把每个tabbar按钮的样式中
mui-tab-item
重新改一下名字,并且复制其样式
十二、制作图片列表区域
- 图片列表需要使用懒加载技术,使用Mint-UI提供的现成的组件
lazy-load
- 渲染图片列表数据
- 实现了点击图片跳转到图片详情页面
- 实现详情页面的布局和美化,同时获取数据渲染页面
- 实现图片详情中缩略图的功能
- 使用插件vue-preview这个缩略图插件
安装 npm i vue-preview -S
直接使用标签 <vue-preview :slides="list"></vue-preview>
- 获取到所有的图片列表
- 每个图片数据对象中,必须有w和h属性
十三、完成商品列表页面
- 绘制商品列表页面
- 实现商品列表的经典两列布局
.good-list {
display: flex;
flex-wrap: wrap;
padding: 7px;
justify-content: space-between;
.goods-item {
padding: 2px;
width: 49%;
border: 1px solid #ccc;
box-shadow: 0 0 8px #ccc;
margin: 4px 0;
display: flex;
flex-direction: column;
justify-content: space-between;
min-height: 293px;
img {
width: 100%;
}
.title {
font-size: 14px;
}
.info {
background-color: #ddd;
p {
margin: 0;
padding: 5px;
}
.price {
.now {
color: red;
font-weight: bold;
font-size: 16px;
}
.old {
text-decoration: line-through;
font-size: 12px;
margin-left: 10px;
}
}
.sell {
display: flex;
justify-content: space-between;
}
}
}
}
- 加载商品列表中的数据并实现加载更多
十四、商品详情页面
- 改造路由链接,绘制商品详情页面并美化
- 使用编程式导航,传递对象
goDesc(id) {
this.$router.push({name: 'goodsdesc', params: {id}})
},
goComment(id) {
this.$router.push({name: 'goodscomment', params: {id}})
}
- 绘制商品购买区域样式
- 使用编程式导航实现图文介绍和商品评论跳转
- 完成商品详情中的图文介绍和评论页面的渲染
十五、加入购物车的小球动画
- 绘制小球,添加样式
.ball {
width: 15px;
height: 15px;
border-radius: 50%;
background-color: red;
position: absolute;
z-index: 99;
top: 390px;
left: 146px;
}
- 小球动画
<transition
@before-enter="beforeEnter"
@enter="enter"
@after-enter="afterEnter">
<div class="ball" v-show="ballFlag" ref="ball"></div>
</transition>
beforeEnter(el) {
el.style.transform = 'translate(0,0)'
},
enter(el,done) {
//先得到徽标的横纵坐标,再得到小球的横纵坐标
//然后让 y 值求差, x 值也求差,得到的结果,就是横纵坐标要位移的距离
el.offsetWidth;
//获取小球在页面上的位置
const ballPosition = this.$refs.ball.getBoundingClientRect();
//获取徽标在页面上的位置
const badgePosition = document.getElementById('badge').getBoundingClientRect();
const xDist = badgePosition.left - ballPosition.left;
const yDist = badgePosition.top - ballPosition.top;
el.style.transform = `translate(${xDist}px,${yDist}px)`;
el.style.transition = 'all 0.5s cubic-bezier(.4,-0.3,1,.68)';
done()
},
afterEnter(el) {
this.ballFlag = !this.ballFlag
}
十六、加入购物车及购物车页面的数量传递问题
如何实现加入购物车时,拿到选择的数量
- 涉及到子组件向父组件传值(事件调用机制)
- 事件调用本质:父向子传递方法,子调用这个方法, 同时把数据当作参数传递给这个方法
getSelectedCount(count) {
//当子组件把选中的数量传递给父组件时,把选中的值保存到data上
this.selectedCount = count;
}
<p>购买数量:<numbox @getcount="getSelectedCount" :max="goodsinfo.stock_quantity"></numbox></p>
- 子组件什么时候把值传给父组件
<input id="test" class="mui-input-numbox" type="number" value="1" @change="countChanged" ref="numbox"/>
methods: {
countChanged() {
//每当文本框的数据被修改时,立即把最新的数据通过事件调用,传递给父组件
this.$emit("getcount",parseInt(this.$refs.numbox.value));
}
},
props: ["max"], // 设置可加入购物车数量最大值
watch: {
//使用watch监听父组件传递过来的max值
max: function(newVal,oldVal) {
mui(".mui-numbox").numbox().setOption("max",newVal);
}
}
在父组件的getSelectedCount()方法中可以拿到传递过来的值
十七、使用vuex并设计购物车数据存储方式
将商品对象设计成以下样式
{ id: 商品id, count: 要购买数量, price: 商品价格, selected: false }
- 在goodsInfo.vue中拼接出一个要保存到store中car数组里的商品信息对象
- 点击加入购物车,把商品信息保存到store中的car上
- 如果在购物车中,之前就已经有了这个对应的商品了,那么,只需要更新数据
- 如果没有,则直接把商品数量push到car中即可
项目源码:商城app源码 vue_app
更多推荐
所有评论(0)