1.todo-list组件
1.1.渲染列表组件结构
<div class="home container">
<ul class="list-group">
<!-- 列表组 -->
<li class="list-group-item d-flex justify-content-between align-items-center">
<!-- 复选框 -->
<div class="form-group form-check">
<input type="checkbox" class="form-check-input" id="customCheck1" />
<label class="form-check-label" for="customCheck1"
>Check this custom checkbox</label>
</div>
<!-- 徽标 -->
<span class="badge badge-success badge-pill">完成</span>
<span class="badge badge-warning badge-pill">未完成</span>
</li>
</ul>
</div>
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/073d86353be79ae9e602946e7e88a8ee.png)
1.2.循环渲染列表组数据
const todolist = ref([
{ id: 1, task: "周一早晨9点开会", done: false },
{ id: 2, task: "周一晚上8点聚餐", done: false },
{ id: 3, task: "准备周三上午的演讲稿", done: true },
]);
<TodoList :list="todolist" />
<script setup>
defineProps({
list: Array,
default: [],
});
</script>
<template>
<div class="home container">
<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="form-group form-check">
<!-- 复选框和完成状态间的双向绑定,当勾选之后,会改变item.done中的值 -->
<input type="checkbox" class="form-check-input" :id="item.id" />
<label class="form-check-label" :for="item.id">{{ item.task }}</label>
</div>
<!-- 徽标 -->
<span class="badge badge-success badge-pill" v-if="item.done"
>完成</span
>
<span class="badge badge-warning badge-pill" v-else>未完成</span>
</li>
</ul>
</div>
</template>
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/e886d75a9c86a1b6dc05b2ee4608f409.png)
1.3.复选框和状态的双向绑定(即勾选之后,状态变换:未完成/完成)
<input type="checkbox" class="form-check-input" :id="item.id" v-model="item.done"/>
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/4edf70e801a5d7ea58a23f98d7774779.png)
1.4.已完成的任务样式变化(删除的横线)
<label class="form-check-label" :class="item.done?'delete':''" :for="item.id">{{ item.task }}</label>
.delete {
text-decoration: line-through;
color: gray;
font-style: iitalic;
}
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/ebb834bf281f888c3e66c812598a67cd.png)
2.todo-input组件
2.1.渲染输入框组件
<template>
<div class="home container">
<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: 376px"
/>
</div>
<button type="submit" class="btn btn-primary mb-2">添加</button>
</form>
</div>
</template>
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/b88971852a0e6d00aff481b3b98b3840.png)
2.2.子传父(添加的新任务追加到App.vue组件中)
<script setup>
import { ref } from "vue";
const taskname = ref("");
const emits = defineEmits(["add"]);
const onFormSubmit = () => {
if (!taskname.value) return alert("任务名称不能为空");
emits("add", taskname.value);
taskname.value = "";
};
</script>
<template>
<div class="home container">
<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: 376px"
v-model.trim="taskname"
/>
</div>
<button type="submit" class="btn btn-primary mb-2">添加</button>
</form>
</div>
</template>
<TodoInput @add="onAddNewTask" />
const onAddNewTask = (taskname) => {
console.log(taskname);
};
2.3.实现添加新任务功能(将一个对象添加到列表中)
const nextId = ref(4);
const onAddNewTask = (taskname) => {
console.log(taskname);
todolist.value.push({
id: nextId.value,
task: taskname,
done: false,
});
nextId.value++;
console.log(nextId.value);
};
3.todo-button组件
3.1.渲染按钮组件
<template>
<div class="home container">
<div class="mt-3 btn-container">
<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>
3.2.按钮样式激活(动态绑定class类名)
const activeIndex = ref(0);
<TodoButton :active="activeIndex" />
<script setup>
const props = defineProps({
active: Number
});
</script>
<template>
<div class="home container">
<div class="mt-3 btn-container">
<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>
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/3c81fcd1037ebfac00d09ab70f116584.png)
3.3.通过v-model激活按钮索引
需求分析:
父--->子:通过defineProps传递了激活项的索引(active)
子--->父:需要更新父组件中激活的索引
<template>
<div class="home container">
<div class="mt-3 btn-container">
<button
type="button"
class="btn"
:class="active === 0 ? 'btn-primary' : 'btn-secondary'"
@click="onBtnClick(0)"
>
全部
</button>
<button
type="button"
class="btn"
:class="active === 1 ? 'btn-primary' : 'btn-secondary'"
@click="onBtnClick(1)"
>
完成
</button>
<button
type="button"
class="btn"
:class="active === 2 ? 'btn-primary' : 'btn-secondary'"
@click="onBtnClick(2)"
>
未完成
</button>
</div>
</div>
</template>
const emits = defineEmits(["onBtnChange"]);
const onBtnClick = (index) => {
if (index === props.active) return;
emits("onBtnChange", index);
};
<TodoButton :active="activeIndex" @onBtnChange="onBtnChange" />
const onBtnChange = (index) => {
activeIndex.value = index;
};
3.4.用计算属性过滤数据
const tasklist = computed(() => {
switch (activeIndex.value) {
case 0:
return todolist.value;
case 1:
return todolist.value.filter((x) => x.done === true);
case 2:
return todolist.value.filter((x) => x.done !== true);
}
});
<TodoList :list="tasklist"/>