Day02
指令修饰符
通过"."指明一些指令的后缀,不同的后缀封装了不同的处理操作
1.按键修饰符 @keyup.enter 键盘回车监听
2.v-model修饰符 v-model.trim 去除首尾空格,v-model.number 转数字
3.事件修饰符 @事件名.stop 阻止冒泡,@事件名.prevent阻止默认行为
<div id="app">
<h3>v-model修饰符 .trim .number</h3>
姓名:<input v-model.trim="username" type="text"><br>
年纪:<input v-model.number="age" type="text"><br>
<h3>@事件名.stop → 阻止冒泡</h3>
<div @click="fatherFn" class="father">
<div @click.stop="sonFn" class="son">儿子</div>
</div>
<h3>@事件名.prevent → 阻止默认行为</h3>
<a @click.prevent href="http://www.baidu.com">阻止默认行为</a>
</div>
v-bind对于样式控制的增强-操作class
语法::class=“对象/数组”
1.当class中是对象时,键就是类名,值就是布尔值,如果为true则有这个类,为false则没有这个类
.pink {
background-color: pink;
}
.big {
width: 300px;
height: 300px;
}
<div class="box" :class="{pink:true,big:false}">程序员</div>
2.当class中是数组时,数组中所有的类都会添加到盒子上,本质就是一个class列表
.pink {
background-color: pink;
}
.big {
width: 300px;
height: 300px;
}
<div class="box" :class="['pink','big']">程序员</div>
案例 tab栏的active效果
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
* {
margin: 0;
padding: 0;
}
ul {
display: flex;
border-bottom: 2px solid #e01222;
padding: 0 10px;
}
li {
width: 100px;
height: 50px;
line-height: 50px;
list-style: none;
text-align: center;
}
li a {
display: block;
text-decoration: none;
font-weight: bold;
color: #333333;
}
li a.active {
background-color: #e01222;
color: #fff;
}
</style>
</head>
<body>
<div id="app">
<ul>
<li v-for="(item,index) in list" :key="item.id"><a class="" :class="{active:index===activeIndex}" href="#" @click="activeIndex=index">{{item.name}}</a></li>
</ul>
</div>
<script src="./vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
activeIndex:0,
list: [
{ id: 1, name: '京东秒杀' },
{ id: 2, name: '每日特价' },
{ id: 3, name: '品类秒杀' }
]
},
methods: {
},
})
</script>
</body>
</html>
v-bind对于样式控制的增强-操作style
语法::style=“样式对象”
.box {
width: 200px;
height: 200px;
background-color: rgb(187, 150, 156);
}
<div id="app">
<div class="box" :style="{backgroundColor:'blue'}"></div>
</div>
<script src="./vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
}
})
</script>
案例 进度条
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.progress {
height: 25px;
width: 400px;
border-radius: 15px;
background-color: #272425;
border: 3px solid #272425;
box-sizing: border-box;
margin-bottom: 30px;
}
.inner {
width: 50%;
height: 20px;
border-radius: 10px;
text-align: right;
position: relative;
background-color: #409eff;
background-size: 20px 20px;
box-sizing: border-box;
transition: all 1s;
}
.inner span {
position: absolute;
right: -20px;
bottom: -25px;
}
</style>
</head>
<body>
<div id="app">
<div class="progress">
<div class="inner" :style="{width:widt+'%'}">
<span>{{widt}}%</span>
</div>
</div>
<button @click="widt=25">设置25%</button>
<button @click="widt=50">设置50%</button>
<button @click="widt=75">设置75%</button>
<button @click="widt=100">设置100%</button>
</div>
<script src="./vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
widt:50,
},
methods: {
},
})
</script>
</body>
</html>
计算属性
概念:基于现有的数据,计算出来的新属性,依赖数据的变化,自动重新计算
语法:声明在computed配置项中,一个计算属性对应一个函数,使用起来和普通函数一样使用插值表达式
computed计算属性和methods方法的区别
computed计算属性:
作用:封装了一段对于数据处理,求得一个结果
缓存特性:计算属性会对计算出来的结果进行缓存,再次使用直接读取缓存,依赖项变化了会自动计算,并且再次缓存
methods方法:
作用:给实例提供一个方法,调用以处理业务逻辑
计算属性完整写法
计算属性默认的简写只能读取访问不能修改,如果要修改就要写计算属性的完整写法。
<div id="app">
姓:<input type="text" v-model="firstName"><br>
名:<input type="text" v-model="lastName"><br>
<p>姓名:{{fullName}}</p>
<button @click="changeName">修改姓名</button>
</div>
<script src="./vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
firstName:'刘',
lastName:'备',
// name:firstName+lastName
},
methods:{
changeName(){
this.fullName = '卢西奥不'
}
},
computed: {
// name(){
// return this.firstName+this.lastName
// }
fullName:{
get(){
return this.firstName+this.lastName
},
set(value){
// console.log(value);
// console.log(value.slice(0,1));截取1个字符串,从下标为0的字符串开始截取
// console.log(value.slice(1));截取下标为1以后的所有字符串
this.firstName = value.slice(0,1)
this.lastName = value.slice(1)
}
}
},
})
</script>
watch侦听器
作用:监视数据变化,执行一些业务逻辑或异步操作
语法:简单写法-简单类型数据,直接填充
完整写法-添加额外的配置项
<div id="app">
<!-- 条件选择框 -->
<div class="query">
<span>翻译成的语言:</span>
<select v-model="obj.lang">
<option value="italy">意大利</option>
<option value="english">英语</option>
<option value="german">德语</option>
</select>
</div>
<!-- 翻译框 -->
<div class="box">
<div class="input-wrap">
<textarea v-model="obj.words"></textarea>
<span><i>⌨️</i>文档翻译</span>
</div>
<div class="output-wrap">
<div class="transbox">{{result}}</div>
</div>
</div>
</div>
<script src="./vue.js"></script>
<script src="./axios.js"></script>
<script>
// 接口地址:https://applet-base-api-t.itheima.net/api/translate
// 请求方式:get
// 请求参数:
// (1)words:需要被翻译的文本(必传)
// (2)lang: 需要被翻译成的语言(可选)默认值-意大利
// -----------------------------------------------
const app = new Vue({
el: '#app',
data: {
// words: '',
obj: {
words: '小河',
lang: 'english'
},
result: '',//翻译结果,
timer: null
},
// 具体讲解:(1) watch语法 (2) 具体业务实现
watch: {
//该方法会在数据变化时调用
//newValue表示变化后的新值
// words(newValue,oldValue){
// console.log("变化了",newValue,oldValue);
// },
// "obj.words" (newValue,oldValue){
// // console.log("变化了",newValue,oldValue);
// //防抖:延迟执行(干啥事先等一等,延迟一会,如果一段时间没有执行,才会被执行)
// clearTimeout(this.timer)
// this.timer = setTimeout(async ()=>{
// const res=await axios({
// url:"https://applet-base-api-t.itheima.net/api/translate",
// params:{
// words:newValue
// }
// })
// // console.log(res);
// this.result = res.data.data
// },300)
// }
obj: {
deep: true,
immediate:true,
handler(newValue) {
console.log('对象被修改了', newValue);
clearTimeout(this.timer)
this.timer = setTimeout(async () => {
const res = await axios({
url: "https://applet-base-api-t.itheima.net/api/translate",
params: {
// words: newValue.words,
// lang:newValue.lang
newValue
}
})
// console.log(res);
this.result = res.data.data
}, 300)
}
}
}
}
)
</script>
练习
逛水果店
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<h3>苹果10元一斤,打八折</h3>
请你输入你要购买的斤数:<input type="text" v-model="fruit">
<button @click="add()">结账买单</button><br>
总价<span>{{sum}}元 </span>折后价为<span>{{da}}元 </span>省了<span>{{sum-da}}元</span>
</div>
</body>
<script src="./vue.js"></script>
<script>
const app = new Vue({
el:'#app',
data:{
fruit:0,
sum:0,
da:0
},
methods: {
add(){
this.sum = this.fruit*10
this.da=this.sum*0.8
}
},
})
</script>
</html>
选择喜欢的
目标: 用户选择栏目, 把用户选中的栏目信息在下面列表显示出来
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<span v-for="(item,index) in list" :key="index">
<input type="checkbox" v-model="hobby" id="" :value="item">{{item}}
</span>
<ul>
<li v-for="(item,index) in hobby">{{item}}</li>
</ul>
</div>
<script src="./vue.js"></script>
<script>
const app = new Vue({
el:"#app",
data:{
list:["科幻", "喜剧", "动漫", "冒险", "科技", "军事", "娱乐", "奇闻"],
hobby:[]
}
})
</script>
</body>
</html>
全选和反选
目标: 完成全选和反选的功能
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<span>全选</span>
<input type="checkbox" v-model="isAll">
<button @click="gai(isAll)">反选</button>
<ul>
<li v-for="(item,index) in arr"><input type="checkbox" v-model="item.checked">{{item.name}}</li>
</ul>
</div>
<script src="./vue.js"></script>
<script>
const app = new Vue({
el:"#app",
data:{
arr: [
{
name: "猪八戒",
checked: false,
},
{
name: "孙悟空",
checked: false,
},
{
name: "唐僧",
checked: false,
},
{
name: "白龙马",
checked: false,
},
],
},
computed: {
isAll:{
get(){
return this.arr.every(item=>item.checked===true)
},
set(value){
return this.arr.forEach(element => {
return element.checked=value
});
}
}
},
methods: {
gai(a){
// console.log(a);
this.isAll =!this.isAll
}
},
})
</script>
</body>
</html>
品牌管理
目标: 数据铺设, 数据新增, 数据删除
- 需求1: 把默认数据显示到表格上
- 需求2: 注意资产超过100的, 都用红色字体标记出来
- 需求3: 点击删除的a标签, 删除数据
- 需求4: 实现底部添加资产的功能
- 需求5: 完成总价和均价
- 需求6: 侦听list变化, 同步到浏览器本地
细节:
- 注意a标签有默认行为-跳转刷新页面(如果有href属性)
- 添加资产时, 提示用户数据不能为空
- form表单里的button的点击事件, 会触发默认表单提交的行为
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link rel="stylesheet" href="../bootstrap-3.4.1-dist/css/bootstrap.min.css">
<style>
.red {
color: red;
}
</style>
</head>
<body>
<div id="app">
<div class="container">
<!-- 顶部搜索框模块 -->
<div class="form-group">
<div class="input-group">
<h4>品牌管理</h4>
</div>
</div>
<!-- 数据表格 -->
<table class="table table-bordered table-hover mt-2">
<thead>
<tr>
<th>编号</th>
<th>资产名称</th>
<th>价格</th>
<th>创建时间</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr v-for="(item,index) in list" :key="item.id">
<td>{{index}}</td>
<td>{{item.name}}</td>
<!-- 如果价格超过100,就有red这个类 -->
<td :class="{red:item.price>100}">{{item.price}}</td>
<td>{{item.time}}</td>
<td><a href="#" @click.prevent="del(item.id)">删除</a></td>
</tr>
<tr style="background-color: #EEE" v-if="list.length!==0">
<td>统计:</td>
<td colspan="2">{{totalCount}}</td>
<td colspan="2">{{(totalCount/list.length).toFixed(2)}}</td>
</tr>
</tbody>
<tfoot>
<tr>
<td colspan="5" style="text-align: center" v-if="list.length===0">暂无数据</td>
</tr>
</tfoot>
</table>
<!-- 添加资产 -->
<form class="form-inline">
<div class="form-group">
<div class="input-group">
<input type="text" class="form-control" placeholder="资产名称" v-model="name" />
</div>
</div>
<div class="form-group">
<div class="input-group">
<input type="text" class="form-control" placeholder="价格" v-model="price">
</div>
</div>
<!-- 阻止表单提交 -->
<button class="btn btn-primary" @click.prevent="add(name,price)">添加资产</button>
</form>
</div>
</div>
<script src="./vue.js"></script>
<script>
const defaultArr = [
{ id: 100, name: "外套", price: 199, time: new Date('2010-08-12') },
{ id: 101, name: "裤子", price: 34, time: new Date('2013-09-01') },
{ id: 102, name: "鞋", price: 25.4, time: new Date('2018-11-22') },
{ id: 103, name: "头发", price: 19900, time: new Date('2020-12-12') }
]
const app = new Vue({
el: '#app',
data: {
name: "", // 名称
price: null, // 价格
list:JSON.parse(localStorage.getItem('list'))||defaultArr
},
methods: {
del(id){
this.list=this.list.filter((item) => {
return item.id!=id
})
},
add(a,b){
if(a.trim()==='') return alert('请输入完整的资产名称')
if(b===null) return alert('请输入价格')
this.list.push({
id:100+this.list.length,
name:a,
price:b,
time:new Date()
})
this.name=''
this.price=null
}
},
computed: {
totalCount(){
return this.list.reduce((previous, current) => {
return previous+current.price
}, 0)
}
},
watch: {
list:{
deep:true,
handler(newValue){
localStorage.setItem('list',JSON.stringify(newValue))
}
}
},
})
</script>
</body>
</html>
买点书练习
目标: 把数据铺设到页面上, 当用户点击买书按钮, 书籍数量增加1, 并且要计算累计的和
演示:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<div>
<p>请选择你要购买的书籍</p>
<ul>
<li style="margin-bottom: 10px;" v-for="(item,index) in arr" :key="index">{{item.name}} <button @click="add(index)">买书</button></li>
</ul>
<table border="1" width="500" cellspacing="0">
<tr>
<th>序号</th>
<th>书名</th>
<th>单价</th>
<th>数量</th>
<th>合计</th>
</tr>
<tr v-for="(item,index) in arr" :key="index">
<td>{{index+1}}</td>
<td>{{item.name}}</td>
<td>{{item.price}}</td>
<td>{{item.count}}</td>
<td>{{item.price*item.count}}</td>
</tr>
</table>
<p>总价格为: {{totalCount}}</p>
</div>
</div>
<script src="./vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
arr: [
{
name: "水浒传",
price: 107,
count: 0,
},
{
name: "西游记",
price: 192,
count: 0,
},
{
name: "三国演义",
price: 219,
count: 0,
},
{
name: "红楼梦",
price: 178,
count: 0,
},
],
},
methods: {
add(id){
this.arr[id].count++
}
},
computed: {
totalCount(){
return this.arr.reduce((previous, current) => {
return previous+current.price*current.count
}, 0)
}
},
})
</script>
</body>
</html>
学生信息管理
目标:
- 需求1: 铺设页面, 准备初始的数据(自己手写数据结构) - 前面是数组索引+1 *作为序号
- 需求2: 当输入框没有值, 要给用户一个提示, 必须都有值才能增加新数据 (数据驱动页面哦)
- 需求3: 添加功能 - 想好数据结构统一对象的key
- 需求4: 点击编辑功能, 把值赋予到输入框上(不要操作dom, 数据驱动页面)
- 需求5: 用户修改后, 点击相同按钮 - 想想怎么判断怎么是添加还是修改的功能 (提示: 准备一个全局变量, 点过编辑按钮可以让它为true) - 实现编辑后更新页面效果
- 需求6: 点击删除, 删除这行数据
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<div>
<span >姓名:</span>
<input v-model="username" type="text" />
</div>
<div>
<span>年龄:</span>
<input type="number" v-model="old">
</div>
<div>
<span>性别:</span>
<select v-model="sex">
<option value="男">男</option>
<option value="女">女</option>
</select>
</div>
<div>
<button @click="target(flag)">添加/修改</button>
</div>
<div>
<table border="1" cellpadding="10" cellspacing="0">
<tr>
<th>序号</th>
<th>姓名</th>
<th>年龄</th>
<th>性别</th>
<th>操作</th>
</tr>
<tr v-for="(item,index) in list" :key="item.id">
<td>{{index+1}}</td>
<td>{{item.name}}</td>
<td>{{item.age}}</td>
<td>{{item.gender}}</td>
<td>
<button @click="del(item.id)">删除</button>
<button @click="tar(item.id)">编辑</button>
</td>
</tr>
</table>
</div>
</div>
<script src="./vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
list:[
{id:1,name:'Tom',age:19,gender:'男'},
{id:2,name:'Jone',age:21,gender:'女'}
],
flag:false,
od:0,
username:'',
old:0,
sex:null
},
methods: {
target(b){
if(this.username==='') return alert('请你输入名字')
if(this.old<0) return alert('请输入正确的年龄')
if(b===false){
this.list.push({
id:this.list.length+1,
name:this.username,
age:this.old,
gender:this.sex
})
this.username = ''
this.old=0
this.sex=null
}else{
const arr = this.list.find(item=>{
return item.id===this.od
})
arr.name=this.username
arr.age=this.old
arr.gender=this.sex
this.flag = false
this.username = ''
this.old=0
this.sex=null
}
},
tar(a){
const arr = this.list.filter((item) => {
return item.id===a
})
this.username=arr[0].name
this.old=arr[0].age
this.sex=arr[0].gender
this.flag=true
this.od=a
},
del(a){
this.list=this.list.filter(item=>item.id!==a)
}
},
})
</script>
</body>
</html>
购物车
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" href="./css/inputnumber.css" />
<link rel="stylesheet" href="./css/index.css" />
<title>购物车</title>
</head>
<body>
<div class="app-container" id="app">
<!-- 顶部banner -->
<div class="banner-box"><img src="http://autumnfish.cn/static/fruit.jpg" alt="" /></div>
<!-- 面包屑 -->
<div class="breadcrumb">
<span>🏠</span>
/
<span>购物车</span>
</div>
<!-- 购物车主体 -->
<div class="main">
<div class="table">
<!-- 头部 -->
<div class="thead">
<div class="tr">
<div class="th">选中</div>
<div class="th th-pic">图片</div>
<div class="th">单价</div>
<div class="th num-th">个数</div>
<div class="th">小计</div>
<div class="th">操作</div>
</div>
</div>
<!-- 身体 -->
<div class="tbody">
<div class="tr" :class="{active:item.isChecked}" v-for="(item,index) in fruitList" :key="item.id">
<div class="td"><input type="checkbox" v-model="item.isChecked" /></div>
<div class="td"><img :src="item.icon" alt="" /></div>
<div class="td">{{item.price}}</div>
<div class="td">
<div class="my-input-number">
<button class="decrease" @click="sub(item.id)" :disabled="item.num<=1"> - </button>
<span class="my-input__inner">{{item.num}}</span>
<button class="increase" @click="add(item.id)"> + </button>
</div>
</div>
<div class="td">{{item.num*item.price}}</div>
<div class="td"><button @click="del(item.id)">删除</button></div>
</div>
</div>
</div>
<!-- 底部 -->
<div class="bottom">
<!-- 全选 -->
<label class="check-all">
<input type="checkbox" v-model="isAll">
全选
</label>
<div class="right-box">
<!-- 所有商品总价 -->
<span class="price-box">总价 : ¥ <span class="price">{{totalPrice}}</span></span>
<!-- 结算按钮 -->
<button class="pay">结算{{totalCount}}</button>
</div>
</div>
</div>
<!-- 空车 -->
<div class="empty" v-if="fruitList.length===0">🛒空空如也</div>
</div>
<script src="../vue.js"></script>
<script>
const defaultArr = [
{
id: 1,
icon: '../13-综合案例-购物车/img/火龙果.png',
isChecked: true,
num: 2,
price: 6,
},
{
id: 2,
icon: '../13-综合案例-购物车/img/荔枝.png',
isChecked: false,
num: 7,
price: 20,
},
{
id: 3,
icon: '../13-综合案例-购物车/img/榴莲.png',
isChecked: false,
num: 3,
price: 40,
},
{
id: 4,
icon: '../13-综合案例-购物车/img/鸭梨.png',
isChecked: true,
num: 10,
price: 3,
},
{
id: 5,
icon: '../13-综合案例-购物车/img/樱桃.png',
isChecked: false,
num: 20,
price: 34,
},
]
const app = new Vue({
el: '#app',
data: {
// 水果列表
fruitList: JSON.parse(localStorage.getItem('friutlist')) || defaultArr
},
computed: {
// 直接当做普通属性调用不加括号
// 任何data中数据变化立即重新计算
// 计算属性会缓存
//默认获取不能设置,要想设置必须写完整写法
// isAll(){
// 必须所有的小选择框都选中 every
// return this.fruitList.every((item) => {
// return item.isChecked===true
// })
// }
isAll:{
get(){
return this.fruitList.every((item) => {
return item.isChecked===true
})
},
set(value){
// value复选框的布尔值
// console.log(value);
this.fruitList.forEach(item => {
return item.isChecked=value
});
}
},
//统计总数
totalCount(){
return this.fruitList.reduce((sum,item)=>{
//或者三元表达式 item.isChecked?sum+item.num:sum
if(item.isChecked){
return sum+item.num
}else{
//没选中不需要累加
return sum
}
},0)
},
//统计总价
totalPrice(){
return this.fruitList.reduce((sum,item)=>{
//或者三元表达式
if(item.isChecked){
return sum+item.num*item.price
}else{
//没选中不需要累加
return sum
}
},0)
}
},
watch: {
fruitList:{
deep:true,
handler(newValue){
localStorage.setItem('fruitlist',JSON.stringify(newValue))
}
}
},
methods: {
del(a) {
this.fruitList = this.fruitList.filter(item => item.id != a)
},
add(id) {
//1.根据id找到数组中的对应项
const fruit = this.fruitList.find(item => item.id === id)
//2.操作num
// console.log(fruit);
fruit.num++
},
sub(id) {
const fruit = this.fruitList.find(item => item.id === id)
//2.操作num
// console.log(fruit);
fruit.num--
}
},
})
</script>
</body>
</html>
希望大家都可以天天开心~