webpack-cms
这篇文章主要介绍了基于vue使用webpack构建的一个简单的CMS(内容管理系统),记录各个步骤的实施以及问题的解决过程。
1. webpack-cms
全局设置时间过滤器
1 | 安装时间格式插件 |
1 | // 在main.js中先导入moment时间格式插件再设置过滤器 |
使用方法:
1 | <span>时间:{{ item.time | dataFormat }}</span> |
新闻详情页制作
将新闻列表改造成router-link的形式
1
2<!--注意:to前面的冒号代表v-bind:to 后面是url后面拼接id,to前面的冒号说明后面是一个表达式,表达式里面是字符串的拼接,所以要用单引号-->
<router-link :to="'/home/newsList/' + item.id">创建newsInfo.vue页面
在router.js中配置新闻详情的路由
1
2
3
4// 导入刚创建的页面组件
import newsInfo from './coms/news/newsInfo.vue'
// 注意id前面的冒号一定不能少,表示id是一个参数
{ path: '/home/newsList/:id', component: newsInfo },在newsInfo.vue页面获取id
1
2// 通过:id传递的参数用params获取,这与?传参不同
$route.params.idinfo的内容使用v-html绑定
1
<div class="j-info-con" v-html="newsInfo.content"></div>
封装一个comment子组件
在subcom文件夹中创建一个comment.vue子组件
在需要comment的页面导入comment组件
- import comment from ‘./comment.vue’
在父组件中使用components将comment组件注册为自己的子组件
注册子组件时,将标签名在页面中引用即可
1
2
3
4// newsInfo.vue--从父组件把id传递给子组件 父组件中:
<comment-box :id="this.id"></comment-box>
// comments.vue--子组件中添加一个props属性接受id(这个props属性与data,methods是平级的):
props: ['id']
点击获取更多评论数据
为按钮添加点击事件
1
<mt-button type="danger" size="large" plain @click="getMore">加载更多</mt-button>
点击加载更多按钮,让pageNum++,然后调用getCommentList()重新渲染数据
1
2
3
4getMore(){
this.pageNum++;
this.getCommentList()
}因为每次取10条数据,为防止新数据覆盖旧数据,使用contact将数据连起来
1
2
3
4
5
6
7
8
9
10
11getCommentList(){
this.$http.get("api/getcomments/"+ this.id +"?pageindex=" + this.pageNum).then(res => {
if(res.body.status === 0){
// this.cmtList = res.body.message;
// 为防止后面请求的数据覆盖前面的,每次加载更多都把数组数据拼接起来
this.cmtList = this.cmtList.concat(res.body.message)
}else{
Toast('请求数据失败')
}
})
}
图片分享
制作顶部分类
1
2
3
4
5
6
7
8
9
10
11
12
13
141. 使用mui中的tab-top-webview-main.html中的顶部选项卡-可左右拖动(webview)
问题 1???
2. 将代码copy到发现picList中发现顶到最顶端,去掉最外层的class类 mui-fullscreen,此时显示正常了,但是不能滑动,通过检查官方文档发现这是一个js组件,引入mui.js并执行初始化:
import mui from '../../lib/mui/js/mui.js'
mui('.mui-scroll-wrapper').scroll({
deceleration: 0.0005 //flick 减速系数,系数越大,滚动速度越慢,滚动距离越小,默认值0.0006
});
问题 2???
// 此时报错了!
Uncaught TypeError: 'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments objects for calls to them
// 报错显示:mui.js中使用的'caller', 'callee', and 'arguments' 等这些属性在严格模式下不能使用,而webpack打包bundle.js默认启用的时严格模式,这用就有了冲突??1
2
33. 解决冲突:移除webpack的严格模式,使用一下插件
https://github.com/genify/babel-plugin-transform-remove-strict-mode
npm install babel-plugin-transform-remove-strict-mode在.babelrc文件中添加:
1
2
3{
"plugins": ["transform-remove-strict-mode"]
}问题 3 ?? 此时能滑动了在移动端模式下页面上点击滑来滑去console中出现一下信息警告:
[Intervention] Unable to preventDefault inside passive event listener due to target being treated as passive.解决方法,在样式中添加:
1
* { touch-action: pan-y;}
原因:(是chrome为了提高页面的滑动流畅度而新折腾出来的一个东西)
http://www.cnblogs.com/pearl07/p/6589114.html
https://developer.mozilla.org/zh-CN/docs/Web/CSS/touch-action
https://www.zhangxinxu.com/wordpress/2018/07/chrome-safari-touchmove-preventdefault-treated-as-passive/问题 4??? 进入页面需要刷新一次才能开始执行滑动效果,因为初始化mui的scroll时候,DOM还没有渲染好
1
2
3
4
5mounted(){ // 在组件中DOM元素被渲染好后,会执行此vue的生命周期mounted钩子函数
mui('.mui-scroll-wrapper').scroll({
deceleration: 0.0005 //flick 减速系数,默认值0.0006
});
},问题 5??? 此时点击底部的路由导航,失灵了,点击无效
分析:在App.vue中去掉mui-tab-item这个类名,路由又能点击了,可能是这个class类名与之前的某些操作有冲突,解决方法:样式copy不变,将mui-tab-item更名为 j-mui-tab-item
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27.mui-bar-tab .j-mui-tab-item.mui-active {
color: #007aff;
}
.mui-bar-tab .j-mui-tab-item {
display: table-cell;
overflow: hidden;
width: 1%;
height: 50px;
text-align: center;
vertical-align: middle;
white-space: nowrap;
text-overflow: ellipsis;
color: #929292;
}
.mui-bar-tab .j-mui-tab-item .mui-icon {
top: 3px;
width: 24px;
height: 24px;
padding-top: 0;
padding-bottom: 0;
}
.mui-bar-tab .j-mui-tab-item .mui-icon~.mui-tab-label {
font-size: 11px;
display: block;
overflow: hidden;
text-overflow: ellipsis;
}
图片详情
导入评论子组件
导入comment.vue
1
import comment from '../subcoms/comment.vue'
创建评论组件
1
2
3components: {
'cmt-box': comment
}创建组件标签,要传递一个id过去
1
<cmt-box :id="this.id"></cmt-box>
图片展示预览
使用 vue-preview 这个集成了 PhotoSwipe 图片预览插件
1
2NOTICE: This plugin currently support vue2.5 and above
npm i vue-preview -S安装插件,在main.js中
1
2
3import VuePreview from 'vue-preview'
// defalut install
Vue.use(VuePreview)使用
1
2<!--页面上放下面的代码,其中thumbImg是图片列表数组-->
<vue-preview :slides="thumbImg" @close="handleClose"></vue-preview>1
2
3
4
5
6
7// 2. 在获取图片的成功回调中根据文档循环每个图片,补全预览时候图片的宽和高
res.body.message.forEach(item => {
item.msrc = item.src;
item.w = 600;
item.h = 400;
});
this.thumbImg = res.body.message;1
2
3
4
5
6
7
8
9/*因为插件中使用的figure是块元素,想要图片水平排列要加inline-block,同时可以设置显示图片的宽高*/
figure {
display: inline-block;
margin: 5px;
height: 60px;
img {
width: 60px;
}
}
在手机上进行项目的预览和测试
- 保证手机和电脑处于同一WiFi环境
- 在项目的package.json的dev中添加一个 –host 指令,把当前WiFi的IP地址设置为 –host的指令值
- Total Contral
编程式的导航
创建组件模块
引入组件模块,创建路由
1
2
3
4import goodsInfo from './coms/goods/goodsInfo.vue'
// 商品列表 name是这个路由的别名,为了后面实现编程式导航使用这个属性
{ path: '/home/goodsInfo/:id', component: goodsInfo, name: 'goodsInfo' },实现跳转:
1
2
3
4
5
6
7
8
9<div class="goods-item" v-for="item in goodsList" :key="item.id" @click="getGoodsInfo(item.id)"></div>
// 以下三种方法都可以,可根据需求来写,注意:如果提供了path,params会被忽略。
// 最后都能跳转到 /home/goodsInfo/123
getGoodsInfo(id=123){
// this.$router.push({path: `/home/goodsInfo/${id}`})
// this.$router.push({name: 'goodsInfo', params: {id}})
this.$router.push('/home/goodsInfo/' + id)
}
封装轮播图组件
将轮播图抽离出来,做成一个能公用的组件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28<template>
<div>
<mt-swipe :auto="4000">
<mt-swipe-item v-for="item in banner" :key="item.id">
<img :src="item.img" :class="{wFull: isFull}">
</mt-swipe-item>
</mt-swipe>
</div>
</template>
<script>
export default {
props: ['banner', 'isFull']
}
</script>
<style lang="less" scoped>
.mint-swipe {
height: 150px;
.mint-swipe-item {
text-align: center;
img {
height: 100%;
}
}
}
.wFull {
width: 100%;
}
</style>调用的时候:
1
2
3
4
5
6
7
8
9
10// 引入组件模板,goodsBanner 是传进去的图片循环数组,isFull是宽度是否100%显示
<swiper :banner="goodsBanner" :isFull="false"></swiper>
// 引入组件模块
import swiper from '../subcoms/swiper.vue'
// 在components中定义
components: {
swiper
}
制作加入购物车的小球动画
需求:点击购物车,小球出现从购买数量处添加到购物车,实现动画效果
html
1
2
3
4
5
6<transition
@before-enter="beforeEnter"
@enter="enter"
@after-enter="afterEnter">
<div class="ball" v-show="ballFlag" ref="ball"></div>
</transition>css
1
2
3
4
5
6
7
8
9
10.ball { /*小球最初在购买数量上面显示,点击加入购物车小球调到购物车里然后隐藏*/
width: 15px;
height: 15px;
border-radius: 50%;
background: red;
position: absolute;
top: 313px;
left: 152px;
z-index: 99;
}js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24// 在data中定义一个 ballFlag:0 默认隐藏小球
addCart(){
this.ballFlag = !this.ballFlag
},
beforeEnter(el){
el.style.transform = 'translate(0, 0)'
},
enter(el, done){
// 获取小球需要移动的距离
// id为badge的元素就是home页面中购物车徽章的那个元素
let ballPos = this.$refs.ball.getBoundingClientRect(); //小球位置
let badgePos = document.getElementById('badge').getBoundingClientRect(); //目标位置
// x和y需要移动的距离
let xDist = badgePos.left - ballPos.left;
let yDist = badgePos.top - ballPos.top;
el.offsetWidth; // 保证小球有动画
el.style.transform = `translate(${xDist}px, ${yDist}px)`;
el.style.transition = 'all .8s cubic-bezier(.65,-0.71,1,.59)';
done(); // 调用afterEnter()
},
afterEnter(el){
this.ballFlag = !this.ballFlag
}备注:
将库存数量作为numbox的最大值
父组件-html
1
2<!--在组件上定义一个max值传递给子组件,这个值就是库存数-->
<numBox @sendSelNum="getSelNum" :max="goodsInfo.stock_quantity"></numBox>子组件接受-html
1
2<!--data-numbox-max前面加冒号表明后面是一个表达式,即接受父组件传递过来的max-->
<div class="mui-numbox" data-numbox-min='1' :data-numbox-max='max'>1
2
3
4
5
6
7props: ['max'], // 在props中接受父组件传递来的max
watch: { // 监听父组件传递过来的max值的变化,将变化后的值作为numbox的最大值
'max': function (newVal, oldVal){
// 使用js API设置numbox的最大值
mui(".mui-numbox").numbox().setOption('max', newVal)
}
}