【vue】任务列表案例(实训1.4)

1. 初始化项目

1.在终端运行以下的命令,初始化 vite 项目:

npm init vite-app mytodolist2 

2.切换路径

cd mytodolist2

3.使用 vscode 打开项目,并安装依赖项

npm install

4.启动项目

npm run dev

测试一下:结果如下
在这里插入图片描述

2.梳理项目结构

  1. 重置 index.css 中的全局样式如下:
/*所有元素*/
:root {
font-size: 12px;
}
body {
padding: 8px;
}
  1. 重置 App.vue 组件的代码结构如下:
<template>
	<div>
	<h1>App 根组件</h1>
	</div>
</template>

<script>
	
export default {
		name: 'MyApp',
		data() {
			return {
				// 任务列表的数据
				todolist: [
					{ id: 1, task: '周一早晨9点开会', done: false },
					{ id: 2, task: '周一晚上8点聚餐', done: false },
					{ id: 3, task: '准备周三上午的演讲稿', done: true },
				],
			}
		},
}
</script>

<style lang="less" scoped></style>

当前效果:
在这里插入图片描述

3. 封装 todo-list 组件

3.1 创建并注册 TodoList 组件

  1. src/components/todo-list/ 目录下新建 TodoList.vue 组件:
<template>
	<div>TodoList 组件</div>
</template>

<script>
export default {
	name: 'TodoList',
}
</script>

<style lang="less" scoped></style>
  1. App.vue 组件中导入并注册 TodoList.vue 组件:
// 导入 TodoList 组件
import TodoList from './components/todo-list/TodoList.vue'

export default {
	name: 'MyApp',
	// 注册私有组件
	components: {
		TodoList,
	}
}
  1. App.vue 的 template 模板结构中使用注册的 TodoList 组件:
<template>
	<div>
		<h1>App根组件</h1>
		<!-- 使用 todo-list 组件 -->
  	    <todo-list></todo-list>
	</div>
</template>

3.2 基于 bootstrap 渲染列表组件

  1. 资料 目录下的 css 文件夹拷贝到 src/assets/ 静态资源目录中。
  2. main.js 入口文件中,导入 src/assets/css/bootstrap.css 样式表:
import { createApp } from 'vue'
import App from './App.vue'

// 导入 bootstrap.css 样式表
import './assets/css/bootstrap.css'
import './index.css'

createApp(App).mount('#app')
  1. 根据 bootstrap 提供的列表组( https://v4.bootcss.com/docs/components/list-group/#with
    -badges )和复选框(https://v4.bootcss.com/docs/components/forms/#checkboxes-and-radio
    s-1 )渲染列表组件的基本效果:
<template>
	<ul class="list-group">
		<li class="list-group-item d-flex justify-content-between align-items-center">
			<!-- 复选框 -->
			<div class="custom-control custom-checkbox">
				<input type="checkbox" class="custom-control-input" id="customCheck1" />
				<label class="custom-control-label" for="customCheck1">Check this custom checkbox</label>
			</div>
			<!-- badge 效果 -->
			<span class="badge badge-success badge-pill">完成</span>
			<span class="badge badge-warning badge-pill">未完成</span>
		</li>
	</ul>
</template>

ctrl+shift+f 可自动排版
当前效果如下:
在这里插入图片描述

3.3 为 TodoList 声明 props 属性

  1. 为了接受外界传递过来的列表数据,需要在 TodoList 组件中声明如下的 props 属性:
<script>
export default {
	name: 'TodoList',
	props: {
		// 列表数据
		list: {
			type: Array,
			required: true,
			default: [],
		},
	}
}
</script>
  1. App 组件中通过 list 属性,将数据传递到 TodoList 组件之中:
<todo-list :list="todolist"></todo-list>

当前效果:
在这里插入图片描述

3.4 渲染列表的 DOM 结构

  1. 通过 v-for 指令,循环渲染列表的 DOM 结构:
<template>
	<ul class="list-group">
		<li class="list-group-item d-flex justify-content-between align-items-center" 
			v-for="item in list" :key="item.id">
			<!-- 复选框 -->
			<div class="custom-control custom-checkbox">
				<input type="checkbox" class="custom-control-input" id="item.id" />
				<label class="custom-control-label" for="item.id">{{item.task}}</label>
			</div>
			<!-- badge 效果 -->
			<span class="badge badge-success badge-pill" v-if="item.done ">完成</span>
			<span class="badge badge-warning badge-pill" v-else>未完成</span>
		</li>
	</ul>
</template>

当前效果如下:
在这里插入图片描述
2. 通过 v-if 和 v-else 指令,按需渲染 badge 效果:

			<!-- badge 效果 -->
			<span class="badge badge-success badge-pill" v-if="item.done ">完成</span>
			<span class="badge badge-warning badge-pill" v-else>未完成</span>
  1. 通过 v-model 指令,双向绑定任务的完成状态:
<!-- 复选框 -->
<div class="custom-control custom-checkbox">
	<input type="checkbox" class="custom-control-input" id="item.id" 
					v-model="item.done"/>
	<!-- 注意:App 父组件通过 props 传递过来的 list 是“引用类型”的数据, -->
	<!-- 这里 v-model 双向绑定的结果是:用户的操作修改的是 App 组件中数据的状态 -->
  1. 通过 v-bind 属性绑定,动态切换元素的 class 类名:
<label class="custom-control-label" 
	:class="item.done ? 'delete' :''" :for="item.id">{{item.task}}</label>

TodoList 组件中声明如下的样式,美化当前组件的 UI 结构:

<style lang="less" scoped>
	// 为列表设置固定宽度
	.list-group {
		width: 400px;
	}
	// 删除效果
	.delete {
		text-decoration: line-through;
	}
</style>

当前效果:
在这里插入图片描述

4. 封装 todo-input 组件

4.1 创建并注册 TodoInput 组件

  1. src/components/todo-input/ 目录下新建 TodoInput.vue 组件:
<template>
	<div>TodoInput 组件</div>
</template>

<script>
export default {
	name: 'TodoInput',
}
</script>

<style lang="css" scoped></style>
  1. App.vue 组件中导入并注册 TodoInput.vue 组件:
// 导入 TodoList 组件
import TodoList from './components/todo-list/TodoList.vue'
// 导入 TodoInput 组件
import TodoInput from './components/todo-input/TodoInput.vue'
components:{   //注册组件
		TodoList,TodoInput,
	}
  1. App.vue 的 template 模板结构中使用注册的 TodoInput 组件:
		<!-- 使用 TodoInput 组件 -->
		<todo-input></todo-input>

当前效果:
在这里插入图片描述

4.2 基于 bootstrap 渲染组件结构

  1. 根据 bootstrap 提供的 inline-forms(https://v4.bootcss.com/docs/components/forms/#in
    line-forms )渲染 TodoInput 组件的基本结构。
  2. TodoInput 组件中渲染如下的 DOM 结构:
<template>
	<!-- form 表单 -->
	<form class="form-inline">
		<div class="input-group mb-2 mr-sm-2">
			<!-- 输入框的前缀 -->
			<div class="input-group-prepend">
				<div class="input-group-text">任务</div>
			</div>
			<!-- 文本输入框 -->
			<input type="text" class="form-control" placeholder="请填写任务信息" style="width: 356px;" />
		</div>
		<!-- 添加按钮 -->
		<button type="submit" class="btn btn-primary mb-2">添加新任务</button>
	</form>
</template>

<script>
export default {
	name: 'TodoInput',
}
</script>

<style lang="css" scoped></style>

4.3 通过自定义事件向外传递数据

  1. TodoInput 组件的 data 中声明如下的数据:
data(){
		return{
			//新任务
			taskname:'',
		}
	}
  1. input 输入框进行 v-model 的双向数据绑定:
<input type="text" class="form-control" placeholder="请填写任务信息" 
	style="width: 356px;" v-model.trim="taskname" />
  1. 监听 form 表单的 submit 事件,阻止默认提交行为并指定事件处理函数:
<form class="form-inline" @submit.prevent="onFormSubmit"></form> 
  1. methods 中声明 onFormSubmit 事件处理函数如下:
methods: {
		// 表单提交的事件处理函数
		onFormSubmit() {
			// 1. 判断任务名称是否为空
			if (!this.taskname) return alert('任务名称不能为空!')
			// 2. 触发自定义的 add 事件,并向外界传递数据
			// 3. 清空文本框
		},
	}
  1. 声明自定义事件如下:
//声明自定义事件
	emits:['add'],
  1. 进一步完善 onFormSubmit 事件处理函数如下:
methods: {
		// 表单提交的事件处理函数
		onFormSubmit() {
			// 1. 判断任务名称是否为空
			if (!this.taskname) return alert('任务名称不能为空!')
			// 2. 触发自定义的 add 事件,并向外界传递数据
			this.$emit('add', this.taskname)
			// 3. 清空文本框
			this.taskname = ''
		},
	}

4.4 实现添加任务的功能

  1. App.vue 组件中监听 TodoInput 组件自定义的 add 事件:
		<!-- 使用 TodoInput 组件 -->
		<!-- 监听 TodoInput 的 add 自定义事件 -->
		<todo-input @add="onAddNewTask"></todo-input>
  1. 在 App.vue 组件的 data 中声明 nextId 来模拟 id 自增 +1 的操作。
// 下一个可用的 Id 值
nextId: 4,
  1. App.vue 组件的 methods 中声明 onAddNewTask 事件处理函数如下:
methods: {
	// TodoInput 组件 add 事件的处理函数
	onAddNewTask(taskname) {
	// 1. 向任务列表中新增任务信息
		this.todolist.push({
			id: this.nextId,
			task: taskname,
			done: false, // 完成状态默认为 false
		})
		// 2. 让 nextId 自增+1
		this.nextId++
	},
}

当前效果:
在这里插入图片描述

5. 封装 todo-button 组件

5.1 创建并注册 TodoButton 组件

  1. src/components/todo-button/ 目录下新建 TodoButton.vue 组件:
<template>
	<div>TodoButton 组件</div>
</template>
<script>
export default {
	name: 'TodoButton',
}
</script>
<style lang="css" scoped></style>
  1. App.vue 组件中导入并注册 TodoButton.vue 组件:
// 导入 TodoButton 组件
import TodoButton from './components/todo-button/TodoButton.vue'
components:{   //注册组件
			TodoList,
			TodoInput,
			TodoButton,
		}

5.2 基于 bootstrap 渲染组件结构

  1. 根据 bootstrap 提供的 Button group(https://v4.bootcss.com/docs/components/button-g
    roup/ )渲染 Todobutton 组件的基本结构。
  2. TodoButton 组件中渲染如下的 DOM 结构:
<template>
	<div class="button-container mt-3">
		<div class="btn-group">
			<button type="button" class="btn btn-primary">全部</button>
			<button type="button" class="btn btn-secondary">已完成</button>
			<button type="button" class="btn btn-secondary">未完成</button>
		</div>
	</div>
</template>
  1. 并通过 button-container 类名美化组件的样式:
<style lang="css" scoped>
	.button-container {
		 /*添加固定宽度*/
		width: 400px;
		 /*文本居中效果*/
		text-align: center;
}
</style>

5.3 通过 props 指定默认激活的按钮

  1. TodoButton 组件中声明如下的 props,用来指定默认激活的按钮的索引:
<script>
export default {
	name: 'TodoButton',
	props: {
		// 激活项的索引值
		active: {
			type: Number,
			required: true,
			// 默认激活索引值为 0 的按钮(全部:0,已完成:1,未完成:2)
			default: 0,
		},
	},
}
</script>

当前效果:
在这里插入图片描述
2. 通过 动态绑定 class 类名 的方式控制按钮的激活状态:

<template>
	<div class="button-container mt-3">
		<div class="btn-group">
			<button type="button" class="btn" 
				:class="active === 0 ?'btn-primary' : 'btn-secondary'">全部</button>
			<button type="button" class="btn" 
				:class="active === 1 ?'btn-primary' : 'btn-secondary'">已完成</button>
			<button type="button" class="btn" 
				:class="active === 2 ?'btn-primary' : 'btn-secondary'">未完成</button>
		</div>
	</div>
</template>
  1. App 组件中声明默认激活项的索引,并通过属性绑定的方式传递给 TodoButton 组件:
data() {
	return {
		// 激活的按钮的索引
		activeBtnIndex: 0
	}
}
<!-- 使用 TodoButton 组件 -->
<todo-button :active="activeBtnIndex"></todo-button>

目前效果如下:
在这里插入图片描述

5.4 通过 v-model 更新激活项的索引

  1. TodoButton 组件中的三个按钮分别绑定 click 事件处理函数如下:
<button type="button" class="btn"  @click="onBtnClick(0)"
	:class="active === 0 ?'btn-primary' : 'btn-secondary'">全部</button>
<button type="button" class="btn" @click="onBtnClick(1)"
	:class="active === 1 ?'btn-primary' : 'btn-secondary'">已完成</button>
<button type="button" class="btn" @click="onBtnClick(2)"
	:class="active === 2 ?'btn-primary' : 'btn-secondary'">未完成</button>
  1. TodoButton 组件的 methods 节点中声明 onBtnClick 事件处理函数如下:
//1.声明自定义事件changeactive
		emits:['changeactive'],
		methods:{
			onBtnClick(n){
				//触发自定义事件changeactive
				this.$emit('changeactive',n)
			}
		}

3.在App.vue中修改如下

 	  <todo-button :active="activeBtnIndex"
 	  	@changeactive="changetasklist" ></todo-button>
changetasklist(n){
				alert(n)
			}

当前效果:
点击全部,弹出对话框显示0;点击已完成,弹出对话框显示1;点击未完成,弹出对话框显示2
在这里插入图片描述
在App.vue中修改如下

			changetasklist(n){
				//n表示单击的是哪一个按钮
				this.activeBtnIndex = n
			}

效果:点击哪一个,哪一个变为蓝色
在这里插入图片描述

5.5 通过计算属性动态切换列表的数据

  1. 在 App 根组件中声明如下的计算属性:
  computed:{    // 计算属性
  	tasklist(){
  		// activeBtnIndex发生变化,就会自动执行tasklist计算属性
  		switch(this.activeBtnIndex){
  			case 0:
  				return this.todolist
  			case 1:
  				return this.todolist.filter( (x) => {return x.done} )
  			case 2:
  				return this.todolist.filter( (x) => {return !x.done} )
  		}
  	}
  }
  1. App 根组件的 DOM 结构中,将 TodoList 组件的 :list="todolist" 修改为:
  		<todo-list :list="tasklist"></todo-list>  <!-- 3.使用todo-list组件 -->

案例完整代码

TodoButton.vue

<template>
	<div class="button-container mt-3">
		<div class="btn-group">
			<button type="button" class="btn"  @click="onBtnClick(0)"
				:class="active === 0 ?'btn-primary' : 'btn-secondary'">全部</button>
			<button type="button" class="btn" @click="onBtnClick(1)"
				:class="active === 1 ?'btn-primary' : 'btn-secondary'">已完成</button>
			<button type="button" class="btn" @click="onBtnClick(2)"
				:class="active === 2 ?'btn-primary' : 'btn-secondary'">未完成</button>
		</div>
	</div>
</template>

<script>
	export default {
		name: 'TodoButton',
		props:{
			// 当前激活的按钮
			active:{
				type:Number,
				required:true,
				default:1,
			}
		},
		//1.声明自定义事件changeactive
		emits:['changeactive'],
		methods:{
			onBtnClick(n){
				//触发自定义事件changeactive
				this.$emit('changeactive',n)
			}
		}
	}
</script>

<style lang="css" scoped="scoped">
	.button-container{
		width:400px;
		text-align: center;
	}
</style>

TodoInput.vue

<template>
	<!-- form 表单 -->
	<form class="form-inline" @submit.prevent="onFormSubmit">
		<div class="input-group mb-2 mr-sm-2">
			<!-- 输入框的前缀 -->
			<div class="input-group-prepend">
				<div class="input-group-text">任务</div>
			</div>
			<!-- 文本输入框 -->
			<input type="text" class="form-control" placeholder="请填写任务信息" 
				style="width: 356px;" v-model.trim="taskname" />
		</div>
		<!-- 添加按钮 -->
		<button type="submit" class="btn btn-primary mb-2">添加新任务</button>
	</form>
</template>

<script>
export default {
	name: 'TodoInput',
	//声明自定义事件
	emits:['add'],
	data(){
		return{
			//新任务
			taskname:'',
		}
	},
	methods: {
		// 表单提交的事件处理函数
		onFormSubmit() {
			// 1. 判断任务名称是否为空
			if (!this.taskname) return alert('任务名称不能为空!')
			// 2. 触发自定义的 add 事件,并向外界传递数据
			this.$emit('add', this.taskname)
			// 3. 清空文本框
			this.taskname = ''
		},
	}
}
</script>

<style lang="css" scoped></style>

TodoList.vue

<template>
	<ul class="list-group">
		<li class="list-group-item d-flex justify-content-between align-items-center"
			v-for="item in list" :key="item.id">
			
				
			<!-- 复选框 -->
			<div class="custom-control custom-checkbox">
				<input type="checkbox" class="custom-control-input" 
					:id="item.id" v-model="item.done" />
				<label class="custom-control-label":class="item.done ? 'delete' :
''" :for="item.id">{{item.task}}</label>
			</div>
			
			<!-- badge 效果 -->
			<span class="badge badge-success badge-pill" v-if="item.done">完成</span>
			<span class="badge badge-warning badge-pill" v-else>未完成</span>
		
		</li>
	</ul>

</template>

<script>
	export default {
		name: 'TodoList',
		props:{
			// 任务列表数据
			list:{
				type:Array,   // 类型是数组
				required:true,
				default:[],
			}
		},
	}
</script>

<style lang="css" scoped="scoped">
.list-group {
 width: 400px; }
 .delete {
 text-decoration: line-through; }
 
</style>

App.vue

<template>
  	<div>
  		<!-- 3.监听add事件,执行addNewTask函数 -->
  		<todo-input @add='addNewTask'></todo-input>
  		
  		<todo-list :list="tasklist"></todo-list>  <!-- 3.使用todo-list组件 -->
  		
  		<!-- 5.监听changeactive事件,触发函数changetasklist -->
  		<todo-button :active="activeBtnIndex"
  			@changeactive="changetasklist"
  			></todo-button>
  	</div>
</template>
<script>
// 1.导入组件
import TodoList from './components/todo-list/TodoList.vue'
import TodoInput from './components/todo-input/TodoInput.vue'
import TodoButton from './components/todo-button/TodoButton.vue'

export default {
  name: 'App',
  methods:{
  		// 4.事件函数,参数taskname是this.taskname
  		addNewTask(taskname){
  			this.todolist.push({
  				id:this.nextId,
  				task:taskname,
  				done:false,   // 默认值是false
  			});
  			this.nextId ++
  		},
  		// 6.改变任务列表的函数
  		changetasklist(n){
  			// n表示单击的是哪一个按钮
  			this.activeBtnIndex = n
  		}
  },
  data(){    
  	return{
  		todolist:[    // 任务列表
  			{id:1,task:'9点晨会',done:false},
  			{id:2,task:'10点聚餐',done:true},
  			{id:3,task:'11点准备演讲稿',done:false},
  		],
  		// 新任务id
  		nextId:4,
  		// 激活的按钮的索引
  		activeBtnIndex:0,
  	}
  },
  components: {   // 2. 注册组件
    TodoList,TodoInput,TodoButton,
  },
  computed:{    // 计算属性
  	tasklist(){
  		// activeBtnIndex发生变化,就会自动执行tasklist计算属性
  		switch(this.activeBtnIndex){
  			case 0:
  				return this.todolist
  			case 1:
  				return this.todolist.filter( (x) => {return x.done} )
  			case 2:
  				return this.todolist.filter( (x) => {return !x.done} )
  		}
  	}
  }
}
</script>
<style lang="css" scoped="scoped"></style>

效果如下:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值