todo-list案例--新增编辑功能【vue知识点:$nextTick】

1. 语法:this.$nextTick(回调函数)
2. 作用:在下一次DOM更新结束后执行其指定的回调
3. 使用场景:当改变数据后,要基于更新后的新DOM进行某些操作时,要在$nextTick所指定的回调函数中执行

App.vue

   <template>
       <div id="root">
           <div class="todo-container">
               <div class="todo-wrap">
                 //给MyHeader组件添加自定义事件addTodo
	             <MyHeader @addTodo="addTodo"/>
		         <MyList 
			         :todos="todos" 
			         :checkTodo="checkTodo" 
			         :deleteTodo="deleteTodo"
		         />
		         //给MyFooter组件添加自定义事件checkAllTodo、clearAllTodo
		         <MyFooter 
			         :todos="todos" 
			         @checkAllTodo="checkAllTodo" 
			         @clearAllTodo="clearAllTodo"
		         />
		       </div>
	       </div>
       </div>
   </template>
   
   <script>
       import MyHeader from './components/MyHeader.vue'
       import MyList from './components/MyList.vue'
       import MyFooter from './components/MyFooter.vue'
       export default {
           name: 'App',
           components:{
              MyHeader,
              MyList,
              MyFooter
           },
           data(){
              return {
                 //不使用写死的数据,采用本地存储读取用户动态数据
                 //JSON.parse 将本地存储中的json字符串解析转化为数组形式读取
                 // || [] 防止数据为空时报错,localStorage里没有数据时页面读取的todos是null,
                 //todos没有length的属性所以一定会报错,因此 || [] 是必要的条件
                 todos: JSON.parse(localStorage.getItem('todos')) || []
              }
           },
           methods: {
              //添加一个todo
              addTodo(todo) {
                 this.todos.unshift(todo)
              },
              //勾选或取消勾选一个todo
              checkTodo(id) {
                 this.todos.forEach((todo)=> {
                    if(todo.id === id) {
                       todo.done = !todo.done
                    }
                 })
              },
              //更新一个todo
              updateTodo(id,title) {
                 this.todos.forEach((todo)=> {
                    if(todo.id === id) {
                       todo.title = title
                    }
                 })
              }
              //删除一个todo
              deleteTodo(id) {
                this.todos = this.todos.filter((todo)=> {
                      return todo.id !== todo.id
                 })
              },
              //全选 or 取消全选
              checkAllTodo(done) {
                 this.todos.foreach((todo)=> {
                      return todo.done = done
                 })
              },
              //清除所有已完成的todo
              clearAllTodo() {
                 this.todos = this.todos.filter((todo)=>{
                    return !todo.done
                 })
              }
           },
           //监听数据变化
           watch() {
              //这是监视的简写形式监听不到深层次(是否勾选)
              //方法(1)
              todos:(value) {
                 //将用户编辑的数据存储到本地存储localStorage中
                 //JSON.stringify将数组形式的数据转化为字符串形式存储在localStorage中
                 localStorage.setItem('todos',JSON.stringify(value))
              },
              //监视的完整形式(可以监听到是否勾选),采用方法(2)
              //方法(2)
              todos:() {
                 handler(value) {
                    deep: true, //开启深度监视
                    localStorage.setItem('todos',JSON.stringify(value))
                 }
              }
           },
           mounted() {
              this.$bus.$on('updateTodo',this.updateTodo)
           },
           beforeCreate() {
              this.$bus.$off('updateTodo')
           }
       }
   </script>

   <style>
      /*base*/
      body {
         background-color: #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>

MyItem.vue

    <template>
       <li>
          <label>
             <input type="checkbox" :checked="todo.done" @change="handleCheck(todo.id)"/>
             //如下代码也能实现功能,但不推荐,因为违反原则,修改了props,只不过vue没有监测到
             //<input type="checkbox" v-model="todo.done"/>
             <span v-show="!todo.isEedit">{{ todo.title }}</span>
             <input 
	             v-show="todo.isEedit" 
	             type="text" 
	             :value="todo.title"
	             @blur="handleBlur(todo,$event)"
             />
          </label>
          <button class="btn btn-danger" @click="handleDelete(todo.id)">删除</button>
          <button 
	          class="btn btn-edit" 
	          v-show="!todo.isEedit" 
	          @click="handleEdit(todo)"
	          ref="inputTitle"
	      >编辑</button>
       </li>
   </template>
   
   <script>
       export default {
           name: 'MyItem',
           props:['todo','checkTodo','deleteTodo'],
           methods:{
             //勾选 or 取消勾选
             handleCheck(id) {
                //通知App组件将对应的todo对象的done值取反
                this.checkTodo(id)
             },
             //删除
             handleDelete(id) {
                if(confirm('确定删除吗?')) {
                   this.deleteTodo(id)
                }
             },
             //编辑
             handleEdit(todo) {
                //判断todo上是否存在isEdit
                if(todo.hasOwnProperty('isEdit')){
                   todo.isEdit = true
                }else {
                   //this.$set(添加的对象,添加的名称,添加的值)
                   //初次编辑的时候todo里没有isEdit,故使用this.$set()追加
                   this.$set(todo,'isEdit',true)
                }
                
                //点击编辑按钮后input自动获取焦点
                //$nextTick 所指定的回调会在dom节点更新完毕之后再执行
                this.$nextTick(function(){
                   this.$refs.inputTitle.focus()
                })
             },
             //失去焦点回调(真正执行修改逻辑)
             handleBlur(todo,e) {
                todo.isEdit = false
                if(!e.target.value.trim()){
                   return alert('输入不能为空!')
                }
                this.$bus.$emit('updateTodo',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-color: #ddd;
      }
      li:hover button {
          display: block;
      }
   </style>

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
实现简洁的导入导出功能可以使用 element-ui 提供的 el-upload 组件和 js-xlsx 库来实现。下面是一个示例代码: 1. 导入 js-xlsx 库 ```javascript import XLSX from 'xlsx' ``` 2. 模板文件下载 ```html <el-button type="primary" icon="el-icon-download" @click="downloadTemplate">下载模板</el-button> ``` ```javascript // 下载模板 downloadTemplate() { const template = [ ['姓名', '性别', '年龄'], ['张三', '男', '23'], ['李四', '女', '25'] ] const ws = XLSX.utils.aoa_to_sheet(template) const wb = XLSX.utils.book_new() XLSX.utils.book_append_sheet(wb, ws, 'Sheet1') XLSX.writeFile(wb, '导入模板.xlsx') } ``` 3. 导入文件上传 ```html <el-upload class="upload-demo" action="" :on-change="handleUpload"> <el-button slot="trigger" type="primary">上传文件</el-button> <div slot="tip" class="el-upload__tip">支持xlsx、xls格式</div> </el-upload> ``` ```javascript // 处理上传文件 handleUpload(file) { const reader = new FileReader() reader.onload = (e) => { const data = e.target.result const workbook = XLSX.read(data, { type: 'binary' }) const sheetName = workbook.SheetNames[0] const sheet = workbook.Sheets[sheetName] const json = XLSX.utils.sheet_to_json(sheet) console.log(json) // TODO: 处理上传数据 } reader.readAsBinaryString(file.raw) } ``` 4. 导出文件功能 ```javascript // 导出文件 exportExcel(data, fileName) { const ws = XLSX.utils.json_to_sheet(data) const wb = XLSX.utils.book_new() XLSX.utils.book_append_sheet(wb, ws, 'Sheet1') XLSX.writeFile(wb, fileName + '.xlsx') } ``` 以上就是一个简单的 vue + element-ui 实现导入导出功能的示例代码。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值