vue3基础练习之todoList

对于这一段时间vue3基础的练习,实现todoList

  • APP.vue
<template>
  <div class="todo-container">
    <div class="todo-wrap">
      <Header
          @addTodo="addTodo"
      />
      <List
          :todoList="todoList"
          :changeChecked="changeChecked"
          :deleteTodo="deleteTodo"
      />
      <Footer
          :todoList="todoList"
          @changeAllTodo="changeAllTodo"
          @delFinished="delFinished"
          :key="footerKey"
      />
    </div>
  </div>
</template>
<script lang="ts" >
import {defineComponent} from "vue";
import Header from "@/components/Header/index.vue";
import List from "@/components/List/index.vue";
import Footer from "@/components/Footer/index.vue";
export default defineComponent({
  name:'App',
  components:{
    Header,
    List,
    Footer
  }
})
</script>
<script setup lang="ts">
//引入nanoid 用来生成id
import {nanoid} from "nanoid";
import {todoListType} from '@/types/todoList'
import {watch, ref, onMounted} from "vue";
import {getTodoList, saveTodoList} from '@/utils/localStorage'
//初始定义任务数据todoList
// const todoList = ref<todoListType>([
//     {id:nanoid(),thing:'吃饭',done:false},
//     {id:nanoid(),thing:'睡觉',done:true},
//     {id:nanoid(),thing:'打豆豆',done:false},
// ])

//定义数据,并在页面初始化后通过本地存储获取数据
const todoList = ref<todoListType>([])
//初始化函数(挂载完成)
onMounted(()=>{
  getTodoList()
})
//改变数据是否被选中状态函数
const changeChecked = (id:string,done:boolean)=>{
  todoList.value.forEach(item =>{
    item.id === id ? item.done = done : '';
  })
}

//添加数据的自定义事件(子传父)
const addTodo = (thing:string)=>todoList.value.push({id:nanoid(),thing,done:false})

//点击删除按钮事件函数
const deleteTodo = (id:string)=> {
  //一定要重新赋值
  todoList.value = todoList.value.filter(item => item.id != id)
}
//全选/全不选自定义事件
const changeAllTodo = (done:boolean)=> todoList.value.forEach(item=>item.done=done)

//定义footer组件key值(根据dff算法,用来改变key值进行组件重新加载)
let footerKey = ref<number>(0)

//清楚已完成任务自定义事件
const delFinished = () =>{
  todoList.value = todoList.value.filter(item => !item.done)
  //用来改变key值进行组件重新加载,更新已完成任务的数据显示
  footerKey.value++
}

//监听数据变化,进行数据永久化存储
//watch 三个参数  1.监听的数据可以是数据,也可以写个函数,return出一个数据
//              2.回调函数        3.配置对象
//第一个参数 监听的是.value时,只会监听内部数据的状态变化
//第一个参数 监听的不是.value时,只会监听数据的数量变化
//所以最好的方法就是  监听原始数据 + deep:true  的配置
// watch(todoList,(newVal,oldVal)=>{
//   saveTodoList(newVal)
// },{deep:true})
//简写:
watch(todoList,saveTodoList,{deep:true})
</script>

<style scoped>
body {
  background: #fff;
}

.btn {
  display: inline-block;
  padding: 4px 12px;
  margin-bottom: 0;
  font-size: 14px;
  line-height: 20px;
  text-align: center;
  vertical-align: middle;
  cursor: pointer;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2),
  0 1px 2px rgba(0, 0, 0, 0.05);
  border-radius: 4px;
}

.btn-danger {
  color: #fff;
  background-color: #da4f49;
  border: 1px solid #bd362f;
}

.btn-danger:hover {
  color: #fff;
  background-color: #bd362f;
}

.btn:focus {
  outline: none;
}

.todo-container {
  width: 600px;
  margin: 0 auto;
}
.todo-container .todo-wrap {
  padding: 10px;
  border: 1px solid #ddd;
  border-radius: 5px;
}
</style>

  • /src/components/Header/index.vue
<template>
  <div class="todo-header">
<!--简写: @keydown.enter="$emit('addTodo',($event.target as HTMLInputElement).value)"   -->
<!--  keydown.enter vue3中取消了键盘事件的数字,改为固定的键盘英文  -->
    <input
        type="text"
        placeholder="请输入你的任务名称,按回车键确认"
        @keydown.enter="add"
    />
  </div>
</template>

<script lang="ts">
import {defineComponent} from "vue";
export default defineComponent({
  name: "Header"
})
</script>

<script lang="ts" setup>
//1.自定义事件直接接收(已经接收到了),可直接在模板中$emit使用,因为模板中是插值语法,存在组件实例,能找到$emit
// defineEmits<{
//   (emit:'addTodo',thing:string):void
// }>()

//defineEmits接收后得到一个emit函数,可以以函数接收并调用emit函数书写额外逻辑
const emit = defineEmits<{(emit:'addTodo',thing:string):void}>()

// 2.自定义事件接收后书写逻辑
const add = (e:Event)=>{
  emit('addTodo',(e.target as HTMLInputElement).value);
  //清空显示的添加数据
  //  对 e.target  目标元素进行断言 书写类型为 HTMLInputElement
  (e.target as HTMLInputElement).value = ''
}
</script>

<style scoped>
.todo-header input {
  width: 560px;
  height: 28px;
  font-size: 14px;
  border: 1px solid #ccc;
  border-radius: 4px;
  padding: 4px 7px;
}

.todo-header input:focus {
  outline: none;
  border-color: rgba(82, 168, 236, 0.8);
  box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075),
  0 0 8px rgba(82, 168, 236, 0.6);
}
</style>

  • /src/components/List/index.vue
<template>
  <ul class="todo-main">
    <!--  :todo="todo" 数据批量传递给子组件  -->
    <Item
        v-for="todo in todoList"
        :key="todo.id"
        :todo="todo"
        :changeChecked="changeChecked"
        :deleteTodo="deleteTodo"
    />
  </ul>
</template>

<script lang="ts">
import {defineComponent} from "vue";
import Item from "@/components/List/Item/index.vue";
export  default defineComponent({
  name: "List",
  components:{Item}

})
</script>
<script setup lang="ts">
import {todoListType} from '@/types/todoList'

//接收props传参定义类型(事件函数一般都没有返回值,返回值类型写void)
interface propsType{
  todoList:todoListType,
  changeChecked:(id:string,done:boolean)=>void,
  deleteTodo:(id:string)=>void,

}
//接收props传参   todoList数据
defineProps<propsType>()
</script>
<style scoped>
.todo-main {
  margin-left: 0px;
  border: 1px solid #ddd;
  border-radius: 2px;
  padding: 0px;
}

.todo-empty {
  height: 40px;
  line-height: 40px;
  border: 1px solid #ddd;
  border-radius: 2px;
  padding-left: 5px;
  margin-top: 10px;
}
</style>

  • /src/components/List/Item/index.vue
<template>
  <li @mousemove="isEnter = true"
      @mouseleave="isEnter = false"
      :class="isEnter ? 'active' : ''"
  >
    <label>
      <!--  $event.target 进行类型断言  HTMLInputElement-->
      <!--  数据操控试图变化-->
      <input
         :checked="todo.done"
          @change="changeChecked(todo.id,($event.target as HTMLInputElement).checked)"
          type="checkbox"
      />
      <span>{{ todo.thing }}</span>
    </label>
    <button class="btn btn-danger" @click="deleteTodo(todo.id)">删除</button>
  </li>
</template>

<script lang="ts">
import {defineComponent,ref} from 'vue';
export default defineComponent({
  name: "Item"
})
</script>

<script lang="ts" setup>
import {todoType} from '@/types/todoList'

//接收props传参定义类型(事件函数一般都没有返回值,返回值类型写void)
interface propsType{
  todo:todoType,
  changeChecked:(id:string,done:boolean)=>void,
  deleteTodo:(id:string)=>void,
}
// defineProps返回一个Proxy对象,包含所有props  ,页面中使用要   Proxy对象.数据
// Proxy(Object) {todo: Proxy(Object), changeChecked: ƒ, deleteTodo: ƒ}
defineProps<propsType>()

// 定义变量,来区分移入移出情况
let isEnter = ref<boolean>(false)
</script>

<style scoped>
li {
  list-style: none;
  height: 36px;
  line-height: 36px;
  padding: 0 5px;
  border-bottom: 1px solid #ddd;
}

li label {
  float: left;
  cursor: pointer;
}

li label li input {
  vertical-align: middle;
  margin-right: 6px;
  position: relative;
  top: -1px;
}

li button {
  float: right;
  display: none;
}

li:before {
  content: initial;
}

li:last-child {
  border-bottom: none;
}

li.active {
  background-color: #ccc;
}

li.active button {
  display: block;
}
</style>

  • /src/components/Footer/index.vue
<template>
  <div class="todo-footer">
    <label>
      <input type="checkbox" v-model="allChange"/>
      <span>全选/全不选</span>
    </label>
    <span> <span>已完成 {{finished}}</span> / 全部 {{todoList.length}} </span>
    <!--  $emit('delFinished')   自定义事件简写,模板中使用插值语法,存在组件实例,上面有$emit-->
    <button class="btn btn-danger" @click="$emit('delFinished')">清除已完成任务</button>
  </div>
</template>

<script lang="ts">
import {defineComponent} from "vue";
export default defineComponent({
  name: "Footer"
})
</script>
<script setup lang="ts">
import {todoListType, todoType} from '@/types/todoList'
import {computed} from "vue";
//直接从defineProps返回的proxy对象中解构出通过props传递的todoList数据
const {todoList} = defineProps<{
  todoList:todoListType
}>()
const emit = defineEmits<{
  (emit:'changeAllTodo',done:boolean):void,
  (emit:'delFinished'):void
}>()
//计算属性计算已完成
const finished = computed(()=>{
  return todoList.filter((item:todoType)=>item.done===true).length
})

//全选按钮实现全选和全不选
const allChange = computed({
  get(){
   return todoList.every((item:todoType)=>item.done)
  },
  set(newVal:boolean){
    //newVal是最新的 get函数得到的值
    //emit 函数 第一参数 是函数名,第二参数是传参
    emit('changeAllTodo',newVal);

  }
})

</script>

<style scoped>
.todo-footer {
  display: flex;
  justify-content: space-between;
  line-height: 40px;
}

.todo-footer label {
  display: inline-block;
  margin-right: 20px;
  cursor: pointer;
}

.todo-footer label input {
  position: relative;
  top: -1px;
  vertical-align: middle;
  margin-right: 5px;
}

.todo-footer button {
  background-color: #ccc;
}
</style>

  • /src/types/todoList.ts
//接口定义每条数据类型
export interface todoType{
    id:string,
    thing:string,
    done:boolean,
}
//定义todoList的类型
export type todoListType = todoType[];

  • /src/utils/localStorage.ts
import {todoListType} from "@/types/todoList";

//存储todoList数据到浏览器本地存储
export function saveTodoList(todoList:todoListType){
    localStorage.setItem('todoList', JSON.stringify(todoList))
}

//通过浏览器本地存储获取todoList数据
export function getTodoList(){
    //断言 localStorage.getItem('todoList' ) 一定是一个字符串
   return JSON.parse(localStorage.getItem('todoList' ) as string) || '[]'
}

/* 仓库地址*/
https://gitee.com/jiangyiqian/todo-list.git

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
你好!对于使用Vue 3和Pinia编写Todolist的问题,我可以提供一些基本的指导。 首先,你需要确保已经安装了Vue 3和Pinia。你可以使用以下命令来安装它们: ```bash npm install vue@next npm install @pinia/vue@next pinia@next ``` 接下来,你可以创建一个Vue组件来实现Todolist。你可以在组件中设置一个数据属性来存储待办事项列表,并使用v-for指令来遍历列表并渲染每个待办事项。你还可以添加一个表单来允许用户输入新的待办事项,并在提交表单时更新列表。 这是一个简单的Todolist组件示例: ```vue <template> <div> <h2>Todolist</h2> <!-- 渲染待办事项列表 --> <ul> <li v-for="item in todos" :key="item.id"> {{ item.text }} </li> </ul> <!-- 表单用于添加新的待办事项 --> <form @submit.prevent="addItem"> <input type="text" v-model="newItemText" /> <button type="submit">Add</button> </form> </div> </template> <script> import { defineComponent } from 'vue'; import { useStore } from 'pinia'; export default defineComponent({ setup() { const store = useStore(); // 数据属性 const newItemText = ''; // 计算属性,获取待办事项列表 const todos = store.todos; // 方法,添加待办事项 const addItem = () => { store.addTodo(newItemText); newItemText.value = ''; }; return { todos, newItemText, addItem, }; }, }); </script> ``` 在上述示例中,我们使用了Pinia提供的`useStore`函数来获取TodoStore实例,并通过`store.todos`获取待办事项列表。`store.addTodo`用于添加新的待办事项。 需要注意的是,上述代码仅为示例,你需要根据自己的具体需求进行相应的调整和扩展。 希望以上信息能对你有所帮助!如果你还有其他问题,请随时提问。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值