<!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>待办事项</title>
</head>
<body>
<div id="app">
<todo-header>
<!-- 2.6 之前 -->
<div
style="font-size: 18px; color: #f00; font-weight: 700;"
slot="title"
>这是自定义的主标题</div>
<!-- 2.6 中使用 v-slot 指令,它需要在 template 标签中使用 -->
<!-- <template v-slot:subtitle>
<div>这是自定义的副标题</div>
</template> -->
<template #subtitle>
<div>这是自定义的副标题</div>
</template>
</todo-header>
<todo-input v-on:add="addTodoItem"></todo-input>
<!-- <todo-list list="传递给子组件的数据"></todo-list> -->
<todo-list v-bind:list="todos"></todo-list>
</div>
<script src="./libs/vue/vue.js"></script>
<script>
// 创建全局变量,用于保存 event-bus 中的对象
const bus = new Vue()
// 创建 TodoHeader 选项
const todoHeaderOption = {
template: `
<div class="header">
<slot name="title">
<h2 class="title">待办事项</h2>
</slot>
<slot name="subtitle">
<h3 class="subtitle">ToDoList...</h3>
</slot>
</div>
`,
}
// 创建 TodoInput 选项
const todoInputOption = {
template: `
<div class="input">
<input
class="input-todo"
type="text"
placeholder="请输入新待办事项"
ref="inputRef"
v-model.trim="inputValue"
@keydown.enter="handleAdd"
/>
<button @click="handleAdd">添加</button>
</div>
`,
data() {
return {
inputValue: ''
}
},
methods: {
handleAdd() {
// 文本框自动获得焦点
this.$refs.inputRef.focus()
// 没有输入内容,则结束不添加
if (this.inputValue.length === 0) {
return
}
// 触发在父组件中定义的 add 事件
this.$emit('add', this.inputValue)
// 清空文本框
this.inputValue = ''
}
},
}
// 创建 TodoItem 选项
const todoItemOption = {
template: `
<li class="list-item">
<input type="checkbox" v-model="item.completed">
{{item.id}} - {{item.title}} - {{item.completed ? '已': '未'}}完成 -
<button @click="handleModify">修改</button>
<button @click="handleRemove">删除</button>
</li>
`,
props: {
item: Object
},
methods: {
handleModify() {
this.item.completed = !this.item.completed
},
// 处理删除
handleRemove() {
// 触发 bus 对象上绑定的 'remove' 事件
bus.$emit('remove', this.item.id)
}
}
}
// 创建 TodoList 选项
const todoListOption = {
template: `
<div>
<ul class="list" v-if="list.length">
<todo-item v-for="todo in list" :key="todo.id" :item="todo"></todo-item>
</ul>
<div v-else>待办事项为空,请添加新待办事项!</div>
<div>
共有{{ total }}项待办事项,
已完成{{ completedCount }}项,
未完成{{ total - completedCount }}项
</div>
</div>
`,
components: { // 局部组件注册
'todo-item': todoItemOption,
},
// props: ['list'], // 利用数组声明当前组件可以从父组件中接收哪些名称的属性
props: { // 利用对象声明可接收属性及进行属性验证
list: {
type: Array,
required: true,
}
},
computed: { // 计算属性
total() { // 所有待办事项总项数
return this.list.length
},
completedCount() { // 已完成的待办事项
return this.list.reduce((sum, todo) => todo.completed ? sum + 1 : sum, 0)
}
}
}
// 注册组件
Vue.component('todo-header', todoHeaderOption)
// 创建根实例,TodoApp 组件
const vm = new Vue({
el: '#app',
data() {
return {
// 所有待办事项
todos: Array(3).fill(null).map((_, index) => ({
id: index + 1,
title: '待办事项' + (index + 1),
completed: Math.random() > 0.5
}))
}
},
components: { // 局部组件注册(子组件)
'todo-input': todoInputOption,
'todo-list': todoListOption,
},
methods: {
// 向 todos 数组中添加新待办事项
addTodoItem(title) {
this.todos.push({
id: Math.random(),
title,
completed: false,
})
},
// 删除 todos 数组中 id 对应的待办事项
removeTodoItem(id) {
this.todos = this.todos.filter(todo => todo.id !== id)
}
},
// 实例创建后自动执行
created() {
// 绑定自定义事件,用于接收数据
bus.$on('remove', this.removeTodoItem)
},
})
</script>
</body>
</html>
待办事项处理
最新推荐文章于 2024-06-29 22:00:35 发布