zoukankan      html  css  js  c++  java
  • Vuex + localStorage + html实现简易todolist

    1.项目结构

    2.Vuex,什么是Vuex?

    官方文档上的介绍是:Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。Vuex 也集成到 Vue 的官方调试工具 devtools extension,提供了诸如零配置的 time-travel 调试、状态快照导入导出等高级调试功能。

    https://vuex.vuejs.org/zh/

    我的理解是Vuex类似于一个 '全局变量' 管理器,你可以在这个 '管理器' 中对全局变量进行监听、修改、取值、赋值等操作;

    基础用法:

    2.1在Vue项目初始化时添加Vuex的安装,项目初始化后会生成 store 文件夹,index.js文件是Vuex的入口文件:

    2.1.1.namespaced:true,vuex中的store是模块管理,在store的index.js中引入各个模块时为了解决不同模块命名冲突的问题,将 namespace置为 true可以加上模块名后调用不同模块的mutations、actions等属性;

    2.1.2 state,当前模块管理的‘状态‘ ,可以理解为index模块中管理的变量;

    2.1.3 mutations,当前模块的提交方法,所有对state中的 '状态' 的改动都需要通过mutations来进行,只接受两个参数,一个是默认参数,当前模块的state对象,另一个是传入的数据,传入多个数据只接受第1个,在同一模块下调用其他mutation要使用 this.otherMuation来进行调用;

    2.1.4 actions,类似于mutations但是actions是用来提交mutations,他并不像mutations那样直接更改管理的 '状态',actions可以包含异步操作,比如在可以actions调用一个接口,在接口的回调用再提交mutations;

    2.1.5 modules,引入的模块;

    2.2 新建一个todolist模块,新建todo.js文件,unfinishedList和finishedList没有用到可以忽略; 

    2.3 调用一次mutations来更改 '状态':

     1 let body = {
     2     title:'测试数据',
     3     date:'2020-03-01',
     4     content:'测试内容测试内容'
     5 };
     6 // 提交todo模块中的pushStuff,传入的数据是一个body对象;数据将会被push到todo模块的list中
     7 this.$store.commit("todo/pushStuff", body);
     8 
     9 // 如果不加模块名,则默认提交index文件中的mutation
    10 // this.$store.commit("someMutaion", body);

    2.4 获取一次 '状态':

     1 // 在Vue项目的任何 .vue文件中使用 下列语句进行 '状态' 的获取
     2 
     3 ...
     4 mounted(){
     5 // this.$store.state.(模块名).(模块state中的变量);
     6 // 获取的是todo模块中的 '状态'
     7 
     8 // this.$store.state.(index中state中的变量);
     9 // 获取的是index模块中的状态
    10    
    11     let list = this.$store.state.todo.list;
    12 }

    2.5 到这一步,已经完成一次Vuex的基础使用;

    3.localStorage

    3.1 localStorage是window对象中的一个存储对象属性,将传入的数据以键值对(Key/Value)的形式存储起来;Vuex中管理的 '状态' 在页面刷新时会丢失,配合localStorage本地存储数据实现数据持久化;

    3.2 基础用法:

    从localStorage取出数据:

     1 ...
     2 mounted(){
     3 if (window.localStorage) {
     4         let storage = window.localStorage;
     5 // localStorage存有内容
     6         if (storage.length > 0) {
     7 /*
     8  * 取出 key为 'staffList' 的值,取出来的值是JSON字符串需要
     9  * 用JSON.parse()转成对象,将取出来的值存入Vuex
    10  */
    11      if (localList && localList.length > 0) {
    12             this.$store.commit("todo/concatStaff", JSON.parse(localList));
    13           }
    14     }
    15 }else{
    16 console.log('浏览器不支持 localStorage');    
    17     }
    18 }
    19 ...

    向localStorage存储数据:

     1 let body={title:"标题",date:"2020-01-01",content:"内容"}; 2 // 将数据转换成JSON字符串再处存入 3 window.localStorage.setItem('body',JSON.stringify(body)); 

    4.项目代码:

    App.vue

     1 <template>
     2   <div id="app">
     3     <div id="nav">
     4       <router-link to="/">Home</router-link>|
     5       <router-link to="/about">About</router-link>
     6     </div>
     7     <router-view />
     8   </div>
     9 </template>
    10 <script>
    11 export default {
    12   created() {
    13     this.localStoreCheck();
    14     // window绑定 '刷新前' 触发的事件,将数据存储在localStorage中
    15     window.addEventListener("beforeunload", () => {
    16       this.localStorageSet();
    17     });
    18   },
    19   computed: {},
    20   beforeMount() {
    21     this.localStoreCheck();
    22   },
    23   methods: {
    24     localStoreCheck() {
    25       if (window.localStorage) {
    26         let storage = window.localStorage;
    27         if (storage.length > 0) {
    28           let localList = storage.getItem("staffList");
    29           if (localList && localList.length > 0) {
    30             this.$store.commit("todo/concatStaff", JSON.parse(localList));
    31           }
    32         }
    33       } else {
    34         console.log("浏览器不支持 localStorage");
    35       }
    36     },
    37     localStorageSet() {
    38       let list = JSON.stringify(this.$store.state.todo.list);
    39       console.log("---------", list);
    40       localStorage.setItem("staffList", list);
    41     },
    42   },
    43 };
    44 </script>
    45 
    46 <style>
    47 #app {
    48   font-family: Avenir, Helvetica, Arial, sans-serif;
    49   -webkit-font-smoothing: antialiased;
    50   -moz-osx-font-smoothing: grayscale;
    51   text-align: center;
    52   color: #2c3e50;
    53 }
    54 
    55 #nav {
    56   padding: 30px;
    57 }
    58 
    59 #nav a {
    60   font-weight: bold;
    61   color: #2c3e50;
    62 }
    63 
    64 #nav a.router-link-exact-active {
    65   color: #42b983;
    66 }
    67 </style>

    Home.vue

     1 <template>
     2   <div class="home">
     3     <ToDoList></ToDoList>
     4   </div>
     5 </template>
     6 
     7 <script>
     8 import ToDoList from "@/views/todolist/ToDoList";
     9 export default {
    10   name: "Home",
    11   components: {
    12     ToDoList,
    13   },
    14   mounted() {
    15   },
    16   data() {
    17     return {};
    18   },
    19   methods: {},
    20 };
    21 </script>
    22 <style lang="less" scoped>
    23 </style>

     

    ToDoList.vue

      1 <template>
      2   <div class="container">
      3     <div class="lt-form">
      4       <form class="form">
      5         <div class="title-label">
      6           <label for="title">{{TITLE}}</label>
      7           <input
      8             :disabled="status!=='SUBMIT'?false:true"
      9             autocomplete="off"
     10             type="text"
     11             id="title"
     12             v-model="form.title"
     13             data-rule="title:required"
     14           />
     15         </div>
     16 
     17         <div class="date-label">
     18           <label for="date">{{DATE}}</label>
     19           <input
     20             :disabled="status!=='SUBMIT'?false:true"
     21             autocomplete="off"
     22             type="date"
     23             id="date"
     24             v-model="form.date"
     25           />
     26         </div>
     27 
     28         <div class="content-label">
     29           <label for="content">{{CONTENT}}</label>
     30           <textarea
     31             :disabled="status!=='SUBMIT'?false:true"
     32             id="content"
     33             rows="5"
     34             v-model="form.content"
     35           ></textarea>
     36         </div>
     37         <div class="btns" v-if="status==='ADD'">
     38           <div class="btn btn-submit">
     39             <input type="button" value="提交" @click="submit" />
     40           </div>
     41           <div class="btn btn-reset">
     42             <input type="button" value="清空" @click="reset" />
     43           </div>
     44           <div class="btn btn-cancle">
     45             <input type="button" value="取消" @click="cancle" />
     46           </div>
     47         </div>
     48         <div class="btns" v-else-if="status==='EDIT'">
     49           <div class="btn btn-save">
     50             <input type="button" value="保存" @click="save" />
     51           </div>
     52           <div class="btn btn-cancle">
     53             <input type="button" value="取消" @click="cancle" />
     54           </div>
     55         </div>
     56         <div class="btns" v-else>
     57           <div class="btn btn-new">
     58             <input type="button" value="新增" @click="newItem" />
     59           </div>
     60           <div class="btn btn-delete">
     61             <input type="button" value="删除" @click="deleteItem" />
     62           </div>
     63           <div class="btn btn-edit">
     64             <input type="button" value="修改" @click="modifyItem" />
     65           </div>
     66         </div>
     67       </form>
     68     </div>
     69     <div class="rt-part">
     70       <div class="rt-nav">
     71         <div class="rt-nav-block">
     72           <div
     73             :class="'nav-item item-'+index "
     74             @click="clickHandler(index,item)"
     75             v-for="(item,index) in arr "
     76             :key="index"
     77           >
     78             <span>{{item.title|doSlice}}</span>
     79             <span>{{item.date}}</span>
     80             <span>{{item.content|doSlice}}</span>
     81           </div>
     82         </div>
     83       </div>
     84     </div>
     85   </div>
     86 </template>
     87 
     88 <script>
     89 export default {
     90   name: "Home",
     91   components: {
     92     // HelloWorld
     93   },
     94   mounted() {
     95     this.renderNav();
     96     this.$nextTick(() => {
     97       // console.log(document.querySelector(".item-0"));
     98       if (this.list.length > 0) {
     99         this.clickHandler("0");
    100       }
    101     });
    102 
    103     // setTimeout(() => {}, 0);
    104   },
    105 
    106   computed: {
    107     list() {
    108       // this.renderNav();
    109       return this.$store.state.todo.list;
    110     },
    111   },
    112   data() {
    113     return {
    114       form: {
    115         title: "",
    116         date: "",
    117         content: "",
    118       },
    119       TITLE: "标题",
    120       DATE: "日期",
    121       CONTENT: "事件",
    122       FORMSTATUS: {
    123         ADD: "ADD",
    124         EDIT: "EDIT",
    125         SUBMIT: "SUBMIT",
    126       },
    127       arr: [],
    128       currTarget: "",
    129       lastIndex: "",
    130       status: "ADD", // ADD EDIT SUBMIT
    131     };
    132   },
    133   filters: {
    134     doSlice: function (value) {
    135       let newVal = value.length > 5 ? value.slice(0, 5) + "..." : value;
    136       return newVal;
    137     },
    138   },
    139   methods: {
    140     submit() {
    141       let objKey;
    142       let flag = Object.keys(this.form).some((key) => {
    143         if (this.form[key] === "") {
    144           objKey = key;
    145           return true;
    146         }
    147       });
    148       if (flag) {
    149         alert(`${this[objKey.toUpperCase()]} 不能为空`);
    150         return false;
    151       }
    152       let body = this.$clone(this.form);
    153       this.$store.commit("todo/pushStuff", body);
    154       this.arr.push(body);
    155 
    156       // 等到DOM渲染完成后调用的钩子函数
    157       this.$nextTick(() => {
    158         this.clickHandler(this.arr.length - 1);
    159       });
    160       this.changeFormStatus(this.FORMSTATUS.SUBMIT);
    161     },
    162     reset() {
    163       Object.keys(this.form).forEach((key) => {
    164         this.form[key] = "";
    165       });
    166     },
    167     renderNav() {
    168       this.arr = this.$clone(this.list);
    169       if (this.arr.length > 0) {
    170         this.status = true;
    171         let temp = this.$clone(this.list[0]);
    172         this.form = temp;
    173       }
    174     },
    175     clickHandler(index) {
    176       // console.log(index);
    177       // console.log(this.arr);
    178       // this.$store.commit("todo/deleteStuff");
    179       if (this.status === "ADD" || this.status === "EDIT") {
    180         this.changeFormStatus(this.FORMSTATUS.SUBMIT);
    181       }
    182       this.changeClassByClass(index);
    183       this.lastIndex = index;
    184       let temp = this.$clone(this.list[index]);
    185       this.form = temp;
    186     },
    187     changeClassByClass(index) {
    188       if (this.lastIndex === "") {
    189         let currClass = document.querySelector(".item-" + index);
    190         currClass.className = currClass.className + " active";
    191       } else {
    192         // let lastClass = document.querySelector(".item-" + this.lastIndex);
    193         // lastClass.className = lastClass.className.replace(/active/g, " ");
    194         this.clearActiveClass(this.lastIndex);
    195         let currClass = document.querySelector(".item-" + index);
    196         currClass.className = currClass.className + " active";
    197       }
    198     },
    199     changeBtnByClass() {
    200       document.querySelector;
    201     },
    202     newItem() {
    203       this.reset();
    204       // this.status = true;
    205       this.changeFormStatus(this.FORMSTATUS.ADD);
    206       this.clearActiveClass(this.lastIndex);
    207     },
    208     clearActiveClass(index) {
    209       let dom = document.querySelector(".item-" + index);
    210       dom.className = dom.className.replace(/active/g, " ");
    211     },
    212     modifyItem() {
    213       if (this.arr.length > 0) {
    214         this.changeFormStatus(this.FORMSTATUS.EDIT);
    215       } else {
    216         return;
    217       }
    218     },
    219     deleteItem() {
    220       // console.log();
    221       let operation = {
    222         index: this.lastIndex,
    223         num: 1,
    224       };
    225       this.$store.commit("todo/deleteStuff", operation);
    226       this.arr.splice(operation.index, operation.num);
    227       this.reset();
    228       // if(this.){}
    229       this.setSelectedAfterDelete();
    230     },
    231     cancle() {
    232       // this.status = "EDITING";
    233       this.changeFormStatus(this.FORMSTATUS.SUBMIT);
    234     },
    235     changeFormStatus(status) {
    236       this.status = status;
    237     },
    238     setSelectedAfterDelete() {
    239       let lastIndex = parseInt(this.lastIndex);
    240       let length = this.arr.length;
    241 
    242       // lastIndex是 被删掉的最后一个数据的索引
    243       if (lastIndex == length && length > 0) {
    244         // this.lastIndex = lastIndex - 1;
    245         this.clickHandler(lastIndex - 1);
    246       } else if (length > 0) {
    247         this.clickHandler(this.lastIndex);
    248       } else {
    249         return;
    250       }
    251     },
    252     save() {
    253       let body = this.$clone(this.form);
    254       this.$set(this.arr, this.lastIndex, body);
    255       this.changeFormStatus(this.FORMSTATUS.SUBMIT);
    256     },
    257   },
    258 };
    259 </script>
    260 <style lang="less" scoped>
    261 * {
    262   padding: 0;
    263   margin: 0;
    264 }
    265 .container {
    266   padding: 15px;
    267   min-height: 300px;
    268   display: flex;
    269   justify-content: center;
    270   align-items: stretch;
    271   .lt-form {
    272     height: 200px;
    273     margin: 0;
    274      500px;
    275     height: 100%;
    276     border: 1px solid;
    277     padding: 15px;
    278     margin: 0 15px;
    279     .form {
    280       .title-label,
    281       .date-label {
    282          50%;
    283         display: inline-block;
    284         text-align: left;
    285         label {
    286           display: inline-block;
    287            15%;
    288         }
    289         input {
    290            80%;
    291           font-size: 16px;
    292           // margin-right: -15px;
    293         }
    294       }
    295       .content-label {
    296         text-align: left;
    297         display: block;
    298          100%;
    299         label {
    300           display: inline-block;
    301         }
    302         textarea {
    303           font-size: 16px;
    304           resize: none;
    305            100%;
    306         }
    307       }
    308       .btns {
    309         display: flex;
    310         justify-content: space-between;
    311         .btn-submit,
    312         .btn-reset,
    313         .btn-delete,
    314         .btn-new,
    315         .btn-edit,
    316         .btn-cancle,
    317         .btn-save {
    318           //  50%;
    319           flex: 1;
    320           // display: inline-block;
    321           // text-align: start;
    322           input {
    323              50%;
    324             // line-height: 10px;
    325             padding: 5px;
    326             font-size: 16px;
    327             // background: rgb(123, 321, 313);
    328             // border-radius: 5px;
    329             margin: 10px 0;
    330           }
    331         }
    332         // .btn.btn-submit,
    333         // .btn.btn-edit {
    334         //   text-align: start;
    335         // }
    336         // .btn-new {
    337         //   text-align: center;
    338         // }
    339         // .btn.btn.btn-reset,
    340         // .btn.btn-delete {
    341         //   text-align: end;
    342         // }
    343       }
    344     }
    345   }
    346   .rt-part {
    347     display: flex;
    348     flex-direction: column;
    349     .rt-nav {
    350        100%;
    351       height: 200px;
    352       min-height: 230px;
    353       border: 1px solid;
    354       margin: 0 15px;
    355       // position: relative;
    356       .rt-nav-block {
    357         flex: 1;
    358         height: 100%;
    359         overflow: auto;
    360         .nav-item {
    361           display: flex;
    362           align-items: center;
    363           justify-content: space-around;
    364           // position: relative;
    365           flex-wrap: nowrap;
    366           span {
    367             flex: 1;
    368             overflow: hidden;
    369           }
    370         }
    371       }
    372     }
    373   }
    374 }
    375 .active {
    376   background: #4f4f4f;
    377   color: white;
    378 }
    379 </style>

    /store/todolist/todo.js:

     1 export default {
     2     namespaced: true,
     3     state: {
     4         list: [{
     5             title: "测试数据1",
     6             date: "2020-02-02",
     7             content: "测试数据1测试数据1测试数据1"
     8         }],
     9         unfinishedList: [],
    10         finishedList: [],
    11     },
    12     mutations: {
    13         pushStuff(state, value) {
    14             state.list.push(value);
    15         },
    16         concatStaff(state, arr) {
    17             state.list = [].concat(arr);
    18         },
    19         deleteStuff(state, opertation) {
    20             // console.log(arguments);
    21             state.list.splice(opertation.index, opertation.num);
    22         }
    23     },
    24     actions: {},
    25     modules: {}
    26 }

      /js/utils.js:

    1 export default {
    2     clone: function (body) {
    3         return JSON.parse(JSON.stringify(body));
    4     },
    5 }

    效果图:超过5个字符则只截取前5个字符加上 '...' 展示。

  • 相关阅读:
    [文档].艾米电子 移位寄存器,Verilog
    [书籍].Pong P. Chu FPGA Prototyping By Verilog Examples
    [转载].FPGA三国志
    [笔记].电机行业常用的中英文对照
    [原创][连载].基于SOPC的简易数码相框 – Nios II SBTE部分(软件部分) 配置工作
    [转载].振南带你入门FAT32文件系统 视频
    [文档].艾米电子 在综合中使用函数,Verilog
    [转载].阿迪老师 《SD卡入门到精通》视频
    [文档].艾米电子 寄存器,Verilog
    [笔记].怎样消除pointer targets in passing argument n of 'func_xxx' differ in signedness警告
  • 原文地址:https://www.cnblogs.com/iwannadoflex/p/13471828.html
Copyright © 2011-2022 走看看