vue2 src_Todolist编辑($nextTick)

news2024/11/24 8:02:00

 main.js

//引入Vue
import Vue from "vue";
//引入App
import App from './App';

//关闭Vue的生产提示
Vue.config.productionTip = false;


new Vue({
    el: '#app',
    render: h => h(App),
    beforeCreate() {
        //事件总线
       Vue.prototype.$bus = this;
    }
});

App.vue

<template>
  <div id="root">
    <div class="todo-container">
      <div class="todo-wrap">
        <MyHeader @addTodo="addTodo"/>
        <List
            :todos="todos"
        />
        <MyFooter
            :todos="todos"
            @checkAllTodo="checkAllTodo"
            @clearAllDoneTodo="clearAllDoneTodo"
        />
      </div>
    </div>
  </div>
</template>

<script>
import MyHeader from "@/components/MyHeader";
import List from "@/components/List";
import MyFooter from '@/components/MyFooter';
import pubsub from "pubsub-js";
export default {
  name: "App",
  components:{
    List,
    MyFooter,
    MyHeader
  },
  data() {
    return {
      // todos: [
      //   {id: '001', title: '吃饭', done: false},
      //   {id: '002', title: "睡觉", done: true},
      //   {id: '003', title: '打代码', done: false}
      // ]
      todos:JSON.parse(localStorage.getItem('todos')) || []
    }
  },
  methods:{
    //添加的todo
    addTodo(todo){
      console.log('我是app组件,我收到了数据');
      this.todos.unshift(todo);
    },
    checkTodo(id){
      const todo = this.todos.find(todo => todo.id === id);
      todo.done = !todo.done;
    },
    deleteTodo(_, id){
      this.todos = this.todos.filter(todo => todo.id !== id);
    },
    checkAllTodo(done){
      this.todos.forEach(todo => todo.done = done);
    },
    clearAllDoneTodo(){
      this.todos = this.todos.filter(todo => !todo.done)
    },
    updateTodo(id, title){
      this.todos.forEach(todo => {
         if(todo.id === id) todo.title = title;
      })
    }
  },
  watch:{
    //深度监视
    todos:{
      deep: true, //深度监视当我监视数组中的对象的某个属性的变化它也会产生反应
      handler(newValue) {
        //本地存储存的是key和value都是字符串
        //数据存放在本地存储中
        localStorage.setItem("todos", JSON.stringify(newValue))
      }
    },
  },
  //已挂在绑定事件总线
  mounted() {
    this.$bus.$on('checkTodo', this.checkTodo);
    this.$bus.$on('updateTodo', this.updateTodo);
    this.pubId = pubsub.subscribe('deleteTodo', this.deleteTodo);
  },
  //被卸载注意解绑
  beforeMount() {
    this.$bus.$off('checkTodo');
    this.$bus.$off('updateTodo');
    pubsub.unsubscribe(this.pubId); //取消订阅的方式与取消定时器的方式是类似的,记住
  }
}
</script>

<style>
/*base*/
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-edit{
  color: #fff;
  background-color: skyblue;
  border: 1px solid rgb(103, 159, 180);
  margin-right: 5px;
}

.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>


MyHeader.vue

<template>
  <div class="todo-header">
    <input type="text" placeholder="请输入你的任务名称,按回车键确认" v-model="title" @keyup.enter="add"/>
  </div>
</template>

<script>
import { nanoid } from 'nanoid';
export default {
  //注意不管是你写的data也还还是methods也好,甚至是computed计算属性也好都会出现在组件事例对象vc身上
  //属性值不能重名
  name: "MyHeader",
  data(){
    return {
      title: ''
    }
  },
  methods:{
    add(){
      //将用户的输入包装成一个todo对象
      console.log(this.title)
      if(!this.title.trim()) {
        alert('代办事项不能为空')
        return; //输入的代办事项为空则不走下面流程
      }
      const todoObj = {
        id: nanoid(),
        title: this.title,
        done:false
      }
      // console.log(todoObj);
      // this.addTodo(todoObj);
      //采用自定义事件来修改
      this.$emit('addTodo',todoObj);
      this.title = '';
    }
  },
}
</script>

<style scoped>
/*header*/
.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>

MyFooter.vue

<template>
  <!--隐式类型转换-->
  <div class="todo-footer" v-show="total">
    <label>
      <!--这里也可用v-model来替代,此时不需要计算属性了-->
      <!--<input type="checkbox" :checked="isAll" @change="checkAll"/>-->
      <input type="checkbox" v-model="isAll"/>
    </label>
    <span>
       <span>已完成{{ doneTotal }}</span> / 全部{{total}}
    </span>
    <button class="btn btn-danger" @click="clearAll">清除已完成任务</button>
  </div>
</template>
<script>
export default {
  name: "MyFooter",
  props: ['todos'],
  computed:{
    total(){
      return this.todos.length;
    },
    doneTotal(){
      return this.todos.reduce((todoTotal, todo) => {
        //隐士类型转换
        return todoTotal + todo.done;
      }, 0);
      // return this.todos.filter(todo => todo.done).length;
    },
    isAll:{
      get(){
        return this.total === this.doneTotal && this.doneTotal > 0; //计算属性可以通过其他的计算属性接着进行计算得到结果
      },
      set(value){
        //value注意要么为true,要么为false,因为你是把它应用在了checkbox上
        //this.checkAllTodo(value);
        //采用自定义事件来修改
        this.$emit('checkAllTodo', value);
      }
    }
  },
  methods:{
    // checkAll(e){
    //   // console.log(e.target.checked); //判断这个checkbox到底是不是全选 true全选 false全不选
    //   this.checkAllTodo(e.target.checked);
    // }
    clearAll(){
       // this.clearAllDoneTodo();
      //修改为自定义事件
      this.$emit('clearAllDoneTodo');
    }
  }
}
</script>

<style scoped>
/*footer*/
.todo-footer {
  height: 40px;
  line-height: 40px;
  padding-left: 6px;
  margin-top: 5px;
}

.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 {
  float: right;
  margin-top: 5px;
}
</style>

List.vue

<template>
  <ul class="todo-main">
    <Item
        v-for="todoObj in todos"
        :key="todoObj.id"
        :todo="todoObj"
    />
  </ul>
</template>

<script>
import Item from "@/components/Item";

export default {
  name: "List",
  components: {
    Item,
  },
  props:['todos']
}
</script>

<style scoped>
/*main*/
.todo-main {
  margin-left: 0;
  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>

Item.vue

<template>
  <li>
    <label>
      <!--这里勾选和取消勾选可以使用change和click作为事件处理-->
      <input type="checkbox" :checked="todo.done" @change="handleCheck(todo.id)"/>
      <!--v-model数据的双向绑定,checkbox使用v-model来双向绑定其是否被勾选,也可以实现效果但不推荐(因为其实修改了props中的数据)-->
      <!--这里修改了从List修改过来的props,这里的不允许改是浅层次,就是如果props是一个对象则这个修改这个对象的某一个属性vue是放行的-->
      <!-- <input type="checkbox" v-model="todo.done"/>-->
      <span v-show="!todo.isEdit">{{  todo.title }}</span>
      <input
          type="text"
          :value="todo.title"
          v-show="todo.isEdit"
          @blur="handleBlur(todo, $event)"
          ref="inputTitle"
      />
    </label>
    <button class="btn btn-danger" @click="handleDelete(todo.id)">删除</button>
    <button v-show="!todo.isEdit" class="btn btn-edit" @click="handleEdit(todo)">编辑</button>
  </li>
</template>

<script>
import pubsub from "pubsub-js";
export default {
  name: "Item",
  //声明接收todo
  props: ['todo'],
  methods:{
    handleCheck(id){
      //事件总线
      this.$bus.$emit('checkTodo',id);
    },
    handleDelete(id){
      if(confirm(`确定删除编号为${id}的todo吗`)){
        // console.log(id);
        //事件总线
        // this.$bus.$emit('deleteTodo',id);
        //消息订阅改写
        pubsub.publish('deleteTodo', id);
      }
    },
    //编辑
    handleEdit(todo){
      // todo.isEdit = true; //注意这里添加的数据并不是响应式的 一定清楚
      if(Object.prototype.hasOwnProperty.call(todo, 'isEdit')){
        todo.isEdit = true;
      }else{
        this.$set(todo, 'isEdit', true); //保证初次加入的时候存在响应式的数据
      }
      //自动获取焦点
      //this.$refs.inputTitle.focus(); //此时你这行代码执行了,但是注意vue并没有重新解析模版(input并没有出现在页面上,dom节点并没有被更新),它一定要等这个回调函数执行完之后才会去重新渲染模版
      //使用nextTick来解决
      this.$nextTick(() => {
        //这里的回调函数注意是在dom节点被更新之后才会运行的
        this.$refs.inputTitle.focus();
      })
      console.log(todo);
    },
    //失去焦点回调
    handleBlur(todo, e){
      todo.isEdit = false; //注意我在这里确保你身上一定存在isEdit属性
      if(!e.target.value.trim()) {
        alert('输入不能为空');
        return;
      }
      this.$bus.$emit('updateTodo', todo.id, e.target.value);
    }
  }
}
</script>

<style scoped>
/*item*/
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;
  margin-top: 3px;
}

li:before {
  content: initial;
}

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

li:hover{
  background: #ddd;
}

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2246544.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

速度革命:esbuild如何改变前端构建游戏 (1)

什么是 esbuild&#xff1f; esbuild 是一款基于 Go 语言开发的 JavaScript 构建打包工具&#xff0c;以其卓越的性能著称。相比传统的构建工具&#xff08;如 Webpack&#xff09;&#xff0c;esbuild 在打包速度上有着显著的优势&#xff0c;能够将打包速度提升 10 到 100 倍…

Excel的图表使用和导出准备

目的 导出Excel图表是很多软件要求的功能之一&#xff0c;那如何导出Excel图表呢&#xff1f;或者说如何使用Excel图表。 一种方法是软件生成图片&#xff0c;然后把图片写到Excel上&#xff0c;这种方式&#xff0c;因为格式种种原因&#xff0c;导出的图片不漂亮&#xff0c…

自动化运维-Linux通用性日志切割脚本

一、公司提供的参考脚本&#xff1a; #!/bin/bash # 定义需要清理的文件 log_file("/mpjava/ly.mp.dfpv.acc.biz/bin/nohup.out""/mpjava/ly.mp.dfpv.acc.service/bin/nohup.out"# 添加更多微服务的日志目录路径 ) # 获取当天日期 date_now$(date %Y%m%d)…

Let‘s Encrypt SSL证书:acmessl.cn申请免费3个月证书

目录 一、CA机构 二、Lets Encrypt特点 三、申请SSL 一、CA机构 ‌Lets Encrypt‌是一个由非营利组织Internet Security Research Group (ISRG)运营的证书颁发机构&#xff08;CA&#xff09;&#xff0c;旨在通过自动化和开放的方式为全球网站提供免费、可靠的SSL/TLS证书。…

Java连接MySQL数据库进行增删改查操作

Test 1 首先去查看一下MySQL的版本&#xff1a;mysql -V&#xff08;在cmd中&#xff09;记得要启动MySQL服务在cmd中验证是否可以登录数据库成功&#xff1a;mysql -u root -p&#xff08;然后输入密码&#xff1a;root&#xff09;Test 2 在IDEA创建项目在SQLyog中创建数据…

从搭建uni-app+vue3工程开始

技术栈 uni-app、vue3、typescript、vite、sass、uview-plus、pinia 一、项目搭建 1、创建以 typescript 开发的工程 npx degit dcloudio/uni-preset-vue#vite-ts my-vue3-project2、安装sass npm install -D sass// 安装sass-loader&#xff0c;注意需要版本10&#xff0c;…

React (三)

文章目录 项目地址十二、性能优化12.1 使用useMemo避免不必要的计算12.2 使用memo缓存组件,防止过度渲染12.3 useCallBack缓存函数12.4 useCallBack里访问之前的状态(没懂)十三、Styled-Components13.1 安装13.2给普通html元素添加样式13.3 继承和覆盖样式13.4 给react组件添…

javascrip页面交互

元素的三大系列 offset系列 offset初相识 offset系列属性 作用 element.offsetParent 返回作为该元素带有定位的父级元素&#xff0c;如果父级没有定位&#xff0c;则返回body element.offsetTop 返回元素相对于有定位父元素上方的偏移量 element.offsetLeft 返回元素…

生成对抗网络(简要介绍)

生成对抗网络&#xff08;Generative Adversarial Network&#xff0c;GAN&#xff09;是一种深度学习模型架构&#xff0c;由 Ian Goodfellow 等人在 2014 年提出。GAN 由两个神经网络组成——生成器&#xff08;Generator&#xff09;和判别器&#xff08;Discriminator&…

(C语言)文件操作

目录 文件 程序文件 数据文件 文件名 ​编辑数据文件的分类 文件的打开和关闭 流 标准流 1&#xff09;stdin 2&#xff09;stdout 3&#xff09;stderr 文件指针 文件的打开和关闭 对文件内容操作的函数 1&#xff09;fgetc&#xff0c;fputc 2&#xff09;fp…

招商蛇口|在低密园林里,开启生活的“任意门”

“最好的建筑是这样的&#xff0c;我们深处在其中,却不知道自然在哪里终了&#xff0c;艺术在哪里开始。” 凭借深耕西安10载的城市远见&#xff0c;以及建立在成功人居经验之上的敏锐洞察&#xff0c;招商蛇口将林语堂名言里的生活&#xff0c;变成了现实。 都市化越是加速&…

【es6进阶】vue3中的数据劫持的最新实现方案的proxy的详解

vuejs中实现数据的劫持,v2中使用的是Object.defineProperty()来实现的&#xff0c;在大版本v3中彻底重写了这部分&#xff0c;使用了proxy这个数据代理的方式&#xff0c;来修复了v2中对数组和对象的劫持的遗留问题。 proxy是什么 Proxy 用于修改某些操作的默认行为&#xff0…

掌上单片机实验室 – RT-Thread + ROS2 初探(25)

在初步尝试RT-Thread之后&#xff0c;一直在琢磨如何进一步感受它的优点&#xff0c;因为前面只是用了它的内核&#xff0c;感觉和FreeRTOS、uCOS等RTOS差别不大&#xff0c;至于它们性能、可靠性上的差异&#xff0c;在这种学习性的程序中&#xff0c;很难有所察觉。 RT-Threa…

李宏毅机器学习课程知识点摘要(14-18集)

线性回归&#xff0c;逻辑回归&#xff08;线性回归sigmoid&#xff09;&#xff0c;神经网络 linear regression &#xff0c; logistic regression &#xff0c; neutral network 里面的偏导的相量有几百万维&#xff0c;这就是neutral network的不同&#xff0c;他是…

ThingsBoard规则链节点:AWS SQS 节点详解

引言 ThingsBoard 是一个开源的物联网平台&#xff0c;提供了设备管理、数据收集、处理和可视化等功能。规则链是 ThingsBoard 中的一个强大功能&#xff0c;允许用户定义复杂的业务逻辑来处理设备上报的数据。在规则链中&#xff0c;AWS SQS 节点用于将消息发送到 Amazon Simp…

在Windows 11上启用远程桌面及其故障排查

在Windows 11上启用远程桌面 这将允许其它设备远程连接到你的电脑&#xff0c;并像坐在你的电脑前一样操纵你的电脑桌面。 启用方法很简单&#xff0c;先在设置中找到远程桌面。 点击进入后&#xff0c;点击右侧的滑钮启用远程桌面即可。 勾选启用网络级别验证使得登录者在能…

【H2O2|全栈】JS进阶知识(八)ES6(4)

目录 前言 开篇语 准备工作 浅拷贝和深拷贝 浅拷贝 概念 常见方法 弊端 案例 深拷贝 概念 常见方法 弊端 逐层拷贝 原型 构造函数 概念 形式 成员 弊端 显式原型和隐式原型 概念 形式 constructor 概念 形式 原型链 概念 形式 结束语 前言 开篇语…

RabbitMQ3:Java客户端快速入门

欢迎来到“雪碧聊技术”CSDN博客&#xff01; 在这里&#xff0c;您将踏入一个专注于Java开发技术的知识殿堂。无论您是Java编程的初学者&#xff0c;还是具有一定经验的开发者&#xff0c;相信我的博客都能为您提供宝贵的学习资源和实用技巧。作为您的技术向导&#xff0c;我将…

zotero7 插件使用

zotero style 1、下载地址 Zotero 插件商店 | Zotero 中文社区 2、配置 在工具插件里 3、配置 style 进入高级→设置编辑器 查找 easy 设置完即可显示&#xff0c; 注1&#xff1a;easyscholar的密钥要自行申请注册&#xff0c;注册地址&#xff1a;easySchol…

uniapp+vue3+ts H5端使用Quill富文本插件以及解决上传图片反显的问题

uniappvue3ts H5端使用Quill富文本插件以及解决上传图片反显的问题 1.在项目中安装Quill npm i quill1.3.72.需要显示富文本的页面完整代码 <template><view><div ref"quillEditor" style"height: 65vh"></div></view> &…