跑马灯效果
<div id="app0">
<button @click="go()">走起来</button>
<button @click="stop()">停下来</button>
<p>{{msg}}</p>
</div>
const vm = new Vue({
el:'#app0',
data:{
msg:'走起来呀走起来,停停停!!',
intervalId:null, //在data上定义定时器id
},
methods:{
go(){
if(this.intervalId != null ) return;
this.intervalId = setInterval( () => {
//先获得 第一个字符,注意用substring, 不改变原来的字符串
var start = this.msg.substring(0, 1)
//获得 除了第一个字符以外,剩下的所有的字符串
var end = this.msg.substring(1)
//将第一个字符start 添加到字符串end 的后面
this.msg = end + start
},400)
//subtring(beginIndex, endIndex)方法返回字符串的子字符串
//beginIndex:起始索引(包括), 索引从 0 开始。
//endIndex:结束索引(不包括)
//this 指向问题:在ES6中,箭头函数内部的this与箭头函数外部的this指向是一样的
//也可以用普通匿名函数:function(){},只不过在这个函数外边,先重新定义一下this:var _this=this,在函数体内部,把this全部换成_this,结果是一样的。
},
stop(){
clearInterval(this.intervalId);
//每当清除了定时器之后,要重新把intervalId设置为null。
this.intervalId = null
},
},
打开的效果如下图:
点击【走起来】按钮以后,下边的文字就会开始走动,过一段时间,点击【停下来】按钮,就会停止,再次点击【走起来】会在停止的基础上接着走起来。
点击那个,那个的文字变成红色
可以应用在导航切换等地方
<style>
.active{ color:red; }
</style>
<div id="app1">
<ul>
<li :class="{active:currentIndex == index}"
@click="btnClick2(index)"
v-for="(item,index) in movies">
{{index}} -- {{item}}
</li>
</ul>
</div>
var vm=new Vue({
el:'#app1',
data:{
movies:['海王','泰坦尼克号','速度与激情','花木兰','我和我的祖国','大鱼海棠'],
currentIndex:0, //数字为下标,如果是null就表示默认的没有选择任何一个。
},
methods:{
btnClick2 (index){ this.currentIndex = index; },
},
})
默认的打开以后,效果如下图:
品牌案例,添加新的品牌,删除,筛选
添加和删除比较容易,添加用push,删除用splice就可以实现,下面是添加和删除的案例
<!--引入bootstrap.css-->
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css" />
<div id="app2">
<div class="table-box">
<div class="panel panel-primary">
<div class="panel-heading">
<h3 class="panel-title">添加品牌</h3>
</div>
<div class="panel-body form-inline">
<label>Id:<input type="text" class="form-control" v-model="id"/></label>
<!-- @keyup.enter 键盘事件监听,监听回车键 -->
<label>Name:<input type="text" class="form-control" v-model="name" @keyup.enter="addTr"/></label>
<button @click="addTr" class="btn btn-primary">添加</button>
</div>
</div>
<table class="table table-bordered table-hover table-striped">
<thead>
<tr>
<th>Id</th>
<th>品牌名称</th>
<th>添加时间</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr v-for="(value,index) in list">
<td>{{value.id}}</td>
<td v-text="value.name"></td>
<td>{{value.ctime}}</td>
<td><a @click="deleteTr(index)">删除</a></td>
</tr>
</tbody>
</table>
</div>
</div>
var vm=Vue({
el:"#app2",
data:{
list:[
{id:1,name:'宝马',ctime:new Date()},
{id:2,name:'奔驰',ctime:new Date()}
],
id:'',
name:'',
keywords:'', //Vue.js 调试工具 devtools
},
methods:{
//添加 list.push()
addTr(){
var newTr = {
id:this.id,
name:this.name,
ctime:new Date()
};
this.list.push(newTr);
this.id = this.name = '';
},
//删除 list.splice(要删除项的下标,1)
deleteTr(index){
this.list.splice(index,1)
},
},
})
过滤:这里的例子主要是过滤表格中的“ 品牌名称 ”这一列。用 filter 和 includes 结合来进行过滤
展示的列表是因为循环了list数组,根据内容才展示出来的。我们现在需要根据新的关键字,来确定下边展示的列表内容(过滤关键字),也就是这个列表的内容是不固定的。所以这时候,就不能循环list数组了,要循环一个新数组,并且在循环数组的div上要绑定一个key,这个key就是关键字。
<tr v-for="(value,index) in search(keywords)" :key='value.id'>... ...</tr>
要根据关键字搜索,在表格列表上方添加一个输入框,输入需要搜索的关键字,并且对这个输入框进行一个双向绑定
<label>搜索名称关键字:<input type="text" class="form-control" v-model="keywords" /></label>
完整的代码:
<!--引入bootstrap.css-->
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css" />
<div id="app2">
<div class="table-box">
<div class="panel panel-primary">
<div class="panel-heading">
<h3 class="panel-title">添加品牌</h3>
</div>
<div class="panel-body form-inline">
<label>Id:<input type="text" class="form-control" v-model="id"/></label>
<!-- @keyup.enter 键盘事件监听,监听回车键 -->
<label>Name:<input type="text" class="form-control" v-model="name" @keyup.enter="addTr"/></label>
<button @click="addTr" class="btn btn-primary">添加</button>
<label>搜索名称关键字:<input type="text" class="form-control" v-model="keywords" /></label>
</div>
</div>
<table class="table table-bordered table-hover table-striped">
<thead>
<tr>
<th>Id</th>
<th>品牌名称</th>
<th>添加时间</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<!--之前都是在list数组中循环渲染过来的,现在定义了一个search方法,
同时把搜索的关键字,通过传参的形式传递给search方法
search方法 通过for循环 过滤list数组上的数据,把符合的数据保存到一个新数组中,
再重新进行渲染
-->
<tr v-for="(value,index) in search(keywords)" :key='value.id'>
<td>{{value.id}}</td>
<td v-text="value.name"></td>
<td>{{value.ctime}}</td>
<td><a @click="deleteTr(index)">删除</a></td>
</tr>
</tbody>
</table>
</div>
</div>
var vm=Vue({
el:"#app2",
data:{
list:[
{id:1,name:'宝马',ctime:new Date()},
{id:2,name:'奔驰',ctime:new Date()}
],
id:'',
name:'',
keywords:'', //Vue.js 调试工具 devtools
},
methods:{
//添加 list.push()
addTr(){
var newTr = {
id:this.id,
name:this.name,
ctime:new Date()
};
this.list.push(newTr);
this.id = this.name = '';
},
//删除 list.splice(要删除项的下标,1)
deleteTr(index){
this.list.splice(index,1)
},
search(keywords){
var newList = [];
this.list.forEach(item => {
if(item.name.indexOf(keywords) != -1){ //indexOf 查询复合字符串的下标
newList.push(item);
}
})
return newList
// 【注意】forEach some filter findIndex 这些都属于数组新方法
// 都会对数组中的每一项,进行遍历,执行相关的操作。
//ES6中新增的字符串方法,string.prototype.includes("要包含的字符串"),包含返回true,不包含返回false
// var newList = this.list.filter(item => {
// if(item.name.includes(keywords)){ //includes 判断是否包含指定字符串,空都包含
// return item
// }
// })
// return newList;
},
},
})
这里,时间的格式不是标准的时间格式,我们可以对时间进行一个调整,这里可以用vue里面提供的过滤器 filter 来进行格式化。
过滤器可被用作一些常见的文本格式化。过滤器可以用在两个地方:mustache插值和v-bind表达式。放在script表达式的尾部。
先写个 filter 例子:
<div id="app4">
<!--每个过滤器可以有多个替换值,用逗号隔开。
过滤器可以多次调用,用 “管道” 也就是 “ | ” 隔开就可以-->
<p>{{msg | msgFormat("123","哈哈哈") | test}}</p>
</div>
Vue.filter('msgFormat', function(msg, arg, arg2){
return msg.replace(/单纯/g, arg + arg2);
})
Vue.filter('test',function(msg){
return msg + "======"
})
var vm=new Vue({
el:'#app4',
data:{
msg:'单纯的少年,单纯的问,谁是最单纯的人?'
}
})
渲染出来的效果:
现在就根据上边说的filter的特性,给 品牌案例 的 “添加事件” 做修改吧。
(包括把一位数的时间调成为两位数,这里用了两种方法)
<!--引入bootstrap.css-->
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css" />
<div id="app2">
<div class="table-box">
<div class="panel panel-primary">
<div class="panel-heading">
<h3 class="panel-title">添加品牌</h3>
</div>
<div class="panel-body form-inline">
<label>Id:<input type="text" class="form-control" v-model="id"/></label>
<!-- @keyup.enter 键盘事件监听,监听回车键 -->
<label>Name:<input type="text" class="form-control" v-model="name" @keyup.enter="addTr"/></label>
<button @click="addTr" class="btn btn-primary">添加</button>
<label>搜索名称关键字:<input type="text" class="form-control" v-model="keywords" /></label>
</div>
</div>
<table class="table table-bordered table-hover table-striped">
<thead>
<tr>
<th>Id</th>
<th>品牌名称</th>
<th>添加时间</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<!--之前都是在list数组中循环渲染过来的,现在定义了一个search方法,
同时把搜索的关键字,通过传参的形式传递给search方法
search方法 通过for循环 过滤list数组上的数据,把符合的数据保存到一个新数组中,
再重新进行渲染
-->
<tr v-for="(value,index) in list">
<td>{{value.id}}</td>
<td v-text="value.name"></td>
<td>{{value.ctime | dateFormat()}}</td>
<td><a @click="deleteTr(index)">删除</a></td>
</tr>
</tbody>
</table>
</div>
</div>
//全局过滤器 过滤 定义时间格式
Vue.filter('dateFormat',function(dateStr,pattern="" ){
//根据给定的字符串,得到特定的时间
var dt = new Date(dateStr)
var y = dt.getFullYear()
var m = dt.getMonth() + 1
var d = dt.getDate()
//不确定是大写还是小写,就先统一一下,这里都统一成小写的
if(pattern.toLowerCase() === 'yyyy-mm-dd'){
return `${y}-${m}-${d}`
}else{
var hh = dt.getHours()
var mm = dt.getMinutes()
var ss = dt.getSeconds()
return `${y}-${t(m)}-${t(d)} ${t(hh)}:${t(mm)}:${t(ss)}`
}
function t(s){return s<10 ? '0'+s : s} //时间写成两位数的
})
var vm=Vue({
el:"#app2",
data:{
list:[
{id:1,name:'宝马',ctime:"2021-10-18 16:5:4"},
{id:2,name:'奔驰',ctime:new Date()}
],
id:'',
name:'',
keywords:'', //Vue.js 调试工具 devtools
},
methods:{
addTr(){
var newTr = {
id:this.id,
name:this.name,
ctime:new Date()
};
this.list.push(newTr);
this.id = this.name = '';
},
deleteTr(index){
this.list.splice(index,1)
},
search(keywords){
var newList = [];
this.list.forEach(item => {
if(item.name.indexOf(keywords) != -1){ //indexOf 查询复合字符串的下标
newList.push(item);
}
})
return newList
},
},
filters:{
//定义私有过滤器,过滤器有两个条件,过滤器名称和函数
//过滤器调用的时候,采用的是就近原则,如果私有过滤器和全局过滤器名称一致,优先私有过滤器
dateFormat(dateStr,pattern="" ){
var dt = new Date(dateStr)
var y = dt.getFullYear()
var m = dt.getMonth() + 1
var d = dt.getDate()
if(pattern.toLowerCase() === 'yyyy-mm-dd'){
return `${y}-${m}-${d}`
}else{
//把时间定义成两位(比如8分钟写成08)
//第一种方法
//function t(s){return s<10 ? '0'+s : s}
//var hh = dt.getHours()
//var mm = dt.getMinutes()
//var ss = dt.getSeconds()
//return `${y}-${t(m)}-${t(d)} ${t(hh)}:${t(mm)}:${t(ss)} ~~~~~`
//第二种 使用ES6中的字符串新方法String.prototype.padStart(maxLength, fillString='')
//或者String.prototype.padEnd(maxLength, fillString='') 来填充字符串
var hh = dt.getHours().toString().padStart(2,'0')
var mm = dt.getMinutes().toString().padStart(2,'0')
var ss = dt.getSeconds().toString().padStart(2,'0')
return `${y}-${m}-${d} ${hh}:${mm}:${ss} ~~~~~`
}
},
}
})
我们希望啊,页面刚打开的时候,搜索关键字的输入框可以 自动获焦 ,我们可以用autofocus属性,也可以自己 自定义一个属性,得到我们要的结果。
使用 Vue.directive() 自定义全局的指令 v-focus。【注意】局部的指令,用directives:{focus:{… …}}
参数1:指令的名称,注意,在定义的时候,指令的名称前边,不需要加 v- 前缀,
但是在调用的时候,需要在指令名称前加上 v- 前缀
参数2:是一个对象,这个对象身上,有一些指令相关的函数,这些函数可以在特定的阶段,执行相关的操作
//注意:在每个函数中,第一个参数,永远是el,代表被绑定的元素,这个el参数,是一个原生的JS对象
Vue.directive('focus',{
// bind:function(el){ el.focus() }
//每当指令绑定到元素上的时候, 会立即执行这个bind 函数,只执行一次
//在元素刚绑定了指令的时候,还没有插入到DOM中去,这时候,调用focus方法没有作用,因为,一个元素,只有插入DOM之后,才能获取焦点
//样式,只要通过指令帮顶给了元素,不管这个元素有没有被插入到页面中去,这个元素肯定有了一个内联样式
//将来元素肯定会显示到页面中,这时候,浏览器的渲染引擎必然回解析样式,应用给这个元素。
和样式相关的操作,一般都可以在bind执行
// inserted 表示元素插入到DOM中的时候,会执行inserted函数,触发一次
和JS行为有关的操作,最好在 inserted 中去执行
inserted:function(el){ el.focus() }
// updated:function(el){ el.focus() }
//当组件 VNode 更新的时候,会执行updated,可能会触发多次
})
Vue.directive('color',{
//这里用bind就可以,因为样式不需要关心这个元素在哪里。
//而上边自定义的focus,是要给制定的DOM元素设置的,只能用inserted,它表示元素插入到DOM中了已经
// bind:function(el){el.style.color = 'red'}
bind(el,binding){
console.log(binding.name)
console.log(binding.value)
console.log(binding.expression)
el.style.color = binding.value
}
})
也可以在Vue的内部使用 directives 属性,进行自定义的设置:
var vm=new Vue({
el:'#app2',
//定义私有 自定义指令,名称和函数体
directives:{
'bold':{
bind:function(el,binding){ el.style.fontWeight = binding.value }
}
}
})
在bind函数里边,参数中的第二个参数 bingding 是怎么来的?在vue的文档里边有说明
在HTML中进行应用:
<label>搜索名称关键字:<input type="text" class="form-control" v-model="keywords" v-focus v-color="'blue'" /></label>
【注意】v-color=" ‘blue’ " 这里面的blue是字符串类型的,如果不加里边的引号,就是变量了。
最终最终的完整代码如下:
<!--引入bootstrap.css-->
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css" />
<div id="app2">
<div class="table-box">
<div class="panel panel-primary">
<div class="panel-heading">
<h3 class="panel-title">添加品牌</h3>
</div>
<div class="panel-body form-inline">
<label>Id:<input type="text" class="form-control" v-model="id"/></label>
<!-- @keyup.enter 键盘事件监听,监听回车键 -->
<label>Name:<input type="text" class="form-control" v-model="name" @keyup.enter="addTr"/></label>
<button @click="addTr" class="btn btn-primary">添加</button>
<label>搜索名称关键字:<input type="text" class="form-control" v-model="keywords" v-focus v-color="'blue'" /></label>
</div>
</div>
<table class="table table-bordered table-hover table-striped">
<thead>
<tr>
<th>Id</th>
<th>品牌名称</th>
<th>添加时间</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<!--之前都是在list数组中循环渲染过来的,现在定义了一个search方法,
同时把搜索的关键字,通过传参的形式传递给search方法
search方法 通过for循环 过滤list数组上的数据,把符合的数据保存到一个新数组中,
再重新进行渲染
-->
<tr v-for="(value,index) in list">
<td>{{value.id}}</td>
<td v-text="value.name"></td>
<td>{{value.ctime | dateFormat()}}</td>
<td><a @click="deleteTr(index)">删除</a></td>
</tr>
</tbody>
</table>
</div>
</div>
//全局过滤器 过滤 定义时间格式
Vue.filter('dateFormat',function(dateStr,pattern="" ){
//根据给定的字符串,得到特定的时间
var dt = new Date(dateStr)
var y = dt.getFullYear()
var m = dt.getMonth() + 1
var d = dt.getDate()
//不确定是大写还是小写,就先统一一下,这里都统一成小写的
if(pattern.toLowerCase() === 'yyyy-mm-dd'){
return `${y}-${m}-${d}`
}else{
var hh = dt.getHours()
var mm = dt.getMinutes()
var ss = dt.getSeconds()
return `${y}-${t(m)}-${t(d)} ${t(hh)}:${t(mm)}:${t(ss)}`
}
function t(s){return s<10 ? '0'+s : s} //时间写成两位数的
})
//自定义属性 v-focus
//注意:在每个函数中,第一个参数,永远是el,代表被绑定的元素,这个el参数,是一个原生的JS对象
Vue.directive('focus',{
inserted:function(el){ el.focus() }
})
//自定义属性 v-color
Vue.directive('color',{
bind(el,binding){
el.style.color = binding.value
}
})
var vm=Vue({
el:"#app2",
data:{
list:[
{id:1,name:'宝马',ctime:"2021-10-18 16:5:4"},
{id:2,name:'奔驰',ctime:new Date()}
],
id:'',
name:'',
keywords:'', //Vue.js 调试工具 devtools
},
methods:{
addTr(){
var newTr = {
id:this.id,
name:this.name,
ctime:new Date()
};
this.list.push(newTr);
this.id = this.name = '';
},
deleteTr(index){
this.list.splice(index,1)
},
search(keywords){
var newList = [];
this.list.forEach(item => {
if(item.name.indexOf(keywords) != -1){ //indexOf 查询复合字符串的下标
newList.push(item);
}
})
return newList
},
},
//定义私有的 自定义指令,名称和函数体
directives:{
'bold':{
bind:function(el,binding){ el.style.fontWeight = binding.value }
},
'fontsize':function(el,binding){ //注意:这个function等同于把代码写到了bind和updated中去
el.style.fontSize = parseInt(binding.value)+'px'
},
},
filters:{
//定义私有过滤器,过滤器有两个条件,过滤器名称和函数
//过滤器调用的时候,采用的是就近原则,如果私有过滤器和全局过滤器名称一致,优先私有过滤器
dateFormat(dateStr,pattern="" ){
var dt = new Date(dateStr)
var y = dt.getFullYear()
var m = dt.getMonth() + 1
var d = dt.getDate()
if(pattern.toLowerCase() === 'yyyy-mm-dd'){
return `${y}-${m}-${d}`
}else{
//把时间定义成两位(比如8分钟写成08)
//第一种方法
//function t(s){return s<10 ? '0'+s : s}
//var hh = dt.getHours()
//var mm = dt.getMinutes()
//var ss = dt.getSeconds()
//return `${y}-${t(m)}-${t(d)} ${t(hh)}:${t(mm)}:${t(ss)} ~~~~~`
//第二种 使用ES6中的字符串新方法String.prototype.padStart(maxLength, fillString='')
//或者String.prototype.padEnd(maxLength, fillString='') 来填充字符串
var hh = dt.getHours().toString().padStart(2,'0')
var mm = dt.getMinutes().toString().padStart(2,'0')
var ss = dt.getSeconds().toString().padStart(2,'0')
return `${y}-${m}-${d} ${hh}:${mm}:${ss} ~~~~~`
}
},
}
})
默认打开的样子是这样的
添加一个“宝宝”,渲染以后是这样的
在过滤的input框输入“宝”,结果只显示两项,如图
点击“宝宝”一行的“删除”,这一行都删除了,结果如下
购物车案例
<div id="app4">
<!--如果购物车有内容-->
<div v-if="books.length">
<table border="1" cellspacing="0">
<thead>
<tr>
<td></td>
<td>书籍名称</td>
<td>出版日期</td>
<td>价格</td>
<td>购买数量</td>
<td>操作</td>
</tr>
</thead>
<tbody>
<tr v-for="(item,index) in books">
<td>{{item.id}}</td>
<td>{{item.name}}</td>
<td>{{item.date}}</td>
<td>{{item.price | showPrice}}</td> <!--<td>{{'¥' + item.price.toFixed(2)}}</td>--> <!--<td>{{result(item.price)}}</td>-->
<td>
<!--<button @click="reduceNum(index)" :disabled="item.num <= 1">-</button>-->
<button @click="reduceNum(index)">-</button>
{{item.num}}
<button @click="addNum(index)">+</button>
</td>
<td><button @click="deleteTr(index)">移除</button></td>
</tr>
</tbody>
</table>
<h3>总价格:{{allPrice | showPrice}}</h3>
</div>
<!--如果没有数据,购物车为空-->
<div v-else><h3>购物车为空</h3></div>
</div>
var vm=new Vue({
el:'#app4',
data:{
books:[
{id:1,name:'算法导论',date:'2006-9',price:85.00,num:5},
{id:2,name:'UNIX编程艺术',date:'2006-2',price:59.00,num:1},
{id:3,name:'编程珠玑',date:'2008-10',price:39.00,num:1},
{id:4,name:'代码大全',date:'2006-3',price:128.00,num:1}
]
},
methods:{
result (price){ return '¥' + price.toFixed(2) },
reduceNum(index){
if(this.books[index].num > 1) {
this.books[index].num -= 1;
} else {
alert('不能再减了');
}
},
addNum(index){ this.books[index].num += 1; },
deleteTr(index){
let result = confirm("确定要删除吗?");
if(result == true){
this.books.splice(index,1)
}
},
},
computed:{
allPrice(){
let result = 0;
for(let i in this.books){
result += this.books[i].price * this.books[i].num;
}
return result;
//第二种
//let result = 0;
//for(let item of this.books){
// result += item.price * item.num;
//}
//return result;
//第三种
//reurn this.books.reduce(function(pre,book){
// return pre + book.price * book.count
//},0)
}
},
filters:{
showPrice(price){ return '¥' + price.toFixed(2) }
},
})
效果如下图:
删除第一项(书籍名称为《算法导论》的一行),效果如下图:
全部删除的时候,执行v-else 的语句,效果如下图: