美文网首页我们就爱程序媛
vue移动助手实践(三)————结合vue和localstora

vue移动助手实践(三)————结合vue和localstora

作者: Katherine的小世界 | 来源:发表于2017-10-08 17:58 被阅读0次

    最近在用vue做一个小demo,做了几个小小的功能模块,当做是学习练手吧。毕竟自己能力还是比较受限的,只能慢慢进步啦。最近就想着自己做一个移动端的记账小demo,因为自己没有弄后台(其实是还没去接触学习哇咔咔),所以关于数据的存储暂时就先用localstorage来存储数据啦。

    项目在线demo

    项目在线演示demo(切换到移动端调试模式哦)

    项目github地址

    项目github地址

    最近在做的是小demo,这个是其中的一两个页面,是记账模块。专门抽出来讲。总的代码我会放在github上,今天讲的这一部分代码主要是下面三个文件内。

    image.png
    1 非常粗糙的草图 (莫嫌弃哈哈哈,丑帅丑帅的字)

    首先,上一下一一开始的设计图(略丑)。我的目的是,可以记录每一天的消费收入情况(包括消费项目 ,时间以及消费金额),通过选择时间可以筛选每个月份的消费收入情况。每一天的收入消费情况为一个节点,点击每条条目都可以进入编辑页面进行消费项目 ,时间以及消费金额的编辑。所以编辑项目页面和新增项目页面是同一个页面。
    对了,关于收入支出的icon列表,我一开始就已经定义了一些常见的icon。在icon list中的设置可以进行icon的编辑。

    image.png
    2 成果先看一步(UI有点丑,我后期要美化!!!!!)
    test.gif
    3 项目的整个过程
    问题难点:
    • 数据格式的定义:因为涉及到可以筛选出某一年的某一个月的所有数据,同时每一个月的某一天的数据也可以被筛选,所以数据格式的定义以及数据的存储方式很重要。
    • 数据后期处理筛选:存储了数据之后,如何根据年月来选择筛选数据,同时要将属于同一天的数据筛选出来。
    • 获取当前被编辑的项目:因为点击每一条项目之后,都可以相应跳转进去编辑页面,当前的编辑页面需要自动填充当前项目的数据,因为这涉及到两个页面之间的数据传递,我最后还是选择了用localstorage 来存储传递数据。

    实践开始:
    1 数据存储的形式:

    我决定把每一条消费收入项目定义成一条这样的数据形式,然后存储在一个数组里。

    list = [
         {type: "支出", money: "9999", date: "2017-10-6", icontype: "travel"},
         {type: "支出", money: "449", date: "2017-10-6", icontype: "travel"},
         {type: "支出", money: "799", date: "2017-10-8", icontype: "travel"},
         {type: "收入", money: "34", date: "2017-10-7", icontype: "pay"},
         {type: "支出", money: "9999", date: "2017-11-6", icontype: "travel"},
         {type: "收入", money: "34", date: "2017-11-6", icontype: "pay"},
         {type: "收入", money: "34", date: "2017-9-6", icontype: "pay"},
    ]
    // 其中type是消费的类型,是收入还是支出。money是消费的金额。
    date则是消费时间,icontype是我存储的icon的名字,可以根据icontype的名字来显示icon
    

    接着就是数据的筛选了。上面的示例里是有9月10月11月的数据,当然我们只需要的是某一个月份的数据,所以需要做一个filterData的方法来先过滤数据。

      // 通过年和月来筛选数据,返回筛选出来的数据。传进去的data参数是要筛选的数据
        filterData (data, year, month) {
          let filterData = []
          for (let i = 0; i < data.length; i++) {
            let dateArr = data[i].date.split('-')
            if (dateArr[0] === year) {
              if (dateArr[1] === month) {
                filterData.push(data[i])
              }
            }
          }
          return filterData
        }
    

    接着,就已经筛选出来了某一年某一月的消费数据了。我指定了年月是2017年10月,筛选出来之后数据如下所示:

    list = [
         {type: "支出", money: "9999", date: "2017-10-6", icontype: "travel"},
         {type: "支出", money: "449", date: "2017-10-6", icontype: "travel"},
         {type: "支出", money: "799", date: "2017-10-6", icontype: "travel"},
         {type: "收入", money: "34", date: "2017-10-7", icontype: "pay"}
    ]
    

    筛选出某一年某一个月的数据还是不够的。因为我们需要向这样的一种格式去显示出来,就意味着需要将属于同一天的数据存储在一起

    image.png

    所以,我又写了一个方法sortDatabyDate(),来将数据进行筛选组合,先看一下转换之后的数据格式,如下所示:这个格式的好处就是,计算总的收入支出的时候,

    list = [
    // 这是2017-10-6的数据
        {date: "2017-10-6", income:34, outcome:'10377', sortindex:"6", list: [
           {type: "支出", money: "9999", date: "2017-10-6", icontype: "travel"},
           {type: "支出", money: "449", date: "2017-10-6", icontype: "travel"},
           {type: "支出", money: "799", date: "2017-10-6", icontype: "travel"}]},
    // 这是2017-10-7的数据
        {date: "2017-10-6", income:34, outcome:'10377', sortindex:"6", list: [
           {type: "收入", money: "34", date: "2017-10-7", icontype: "pay"}]}
    ]
    

    其实就是,将每一天的数据存在一个对象里,然后其中的list就是这一天的每一条消费收入。其中的sortindex是为了排序使用的,就是将每天的数据存储在list中之后,还需要按照日期从上到下排序,所以我会将这个月的日期,存储在sortindex中。后续要排序也比较方便了。

     sortDatabyDate () {
          var map = []
          var dest = []
          var income = 0
          var outcome = 0
    // 获取当前年月的所有的数据
          for (let i = 0; i < this.filterConsumeData.length; i++) {
            var time = this.filterConsumeData[i].date
            if (this.filterConsumeData[i].type === '收入') {
              income = this.filterConsumeData[i].money
              outcome = 0
            } else {
              outcome = this.filterConsumeData[i].money
              income = 0
            }
    // map是存储这个月的日期的数组,如果当前数据的时间不存在mapl里面,就直接先创建一条数据
            if (map.indexOf(time) === -1) {
              dest.push({
                income: +income,
                outcome: +outcome,
                sortindex: time.split('-')[2],
                date: time,
                list: [this.filterConsumeData[i]]
              })
              map.push(time)
            } else {
    // 当前这个数据的日期已经存在了,找到这条数据的索引,存储进这条数据的list对象内就可以了
              for (let j = 0; j < dest.length; j++) {
                if (dest[j].date === time) {
                  let oldIncome = dest[j].income
                  let oldOutcome = dest[j].outcome
                  dest[j].income = (+oldIncome) + (+income)
                  dest[j].outcome = (+oldOutcome) + (+outcome)
                  dest[j].list.push(this.filterConsumeData[i])
                }
              }
            }
          }
          console.log(dest, '这是排序之前的')
          // 再将得到的数据进行排序,**sortByfield方法可以根据对象的属性进行排序**
          dest.sort(this.sortByfield('sortindex'))
          this.showConsumeList = dest  // 这是得到的最终的数据
          // 将得到的最终的数据,获取当前的总收入和总支出
        // 一开始先赋值为0
          this.inCome = 0
          this.outCome = 0
          for (let i = 0; i < this.showConsumeList.length; i++) {
            this.inCome = (+this.inCome) + (+this.showConsumeList[i].income)
            this.outCome = (+this.outCome) + (+this.showConsumeList[i].outcome)
          }
        }
    

    其中的排序方法,其实就是根据数组对象中每一个对象中的sortIndex属性来排序。这个可以结合数组的sort()属性来使用。
    (友情链接)数组对象根据对象排序 sort

    // 其中field就是要排序的对象属性,然后结合数组的sort方法,直接使用就可以,。
    // array.sort(sortByfield(属性名))
       sortByfield (field) {
          return function (a, b) {
            return a[field] - b[field]
          }
        }
    

    这样写下来就可以实现数据的转换了。结合vue的for循环指令,就可以很愉快地将数据渲染出来了。是不是很棒。

    2 如何获取当前的编辑项目

    细心的你会发现,就是在我点击每一个条目之后,会跳转到编辑页面。而且这个编辑页面会自动渲染初始数据,那么这个数据如何去传递呢?

    我的做法
    我是通过这个当前这个点击的条目的信息,去获取这个条目在总数据中的索引值,再将这个索引值用localStorage中存储,跳转到编辑页面之后,只要从localstorage中获取就可以了,如果编辑改动了,就直接在总数据中根据索引值去修改就可以了。

       editList (item) {
          this.$router.replace({path: '/moneyRecord'}) // 页面跳转到编辑页面
          let totalData = JSON.parse(localStorage.getItem('list') || '[]')  //这是所有的条目数据
          // 点击进去之后就将数据传递到页面
          this.editIndex = this.findIndex(totalData, item) // 自定义的一个方法,从所有的数据中获取到index值。
          localStorage.setItem('editIndex', JSON.stringify(this.editIndex)) // 将index存储下来
          localStorage.setItem('editItem', JSON.stringify(item))
        },
    

    其中的 findIndex方法定义如下,使用了数组自带的findindex方法,可以自己去google一下,arr.findIndex(callback[, thisArg])

        findIndex (array, target) {
          let index = array.findIndex((item) => {
            return (item.date === target.date) && (item.type === target.type) && (item.icontype === target.icontype) && (item.money === target.money)
          })
          return index
        }
    
    3 新增项目和编辑项目共用一个页面

    其实不管是编辑还是新增,都只是需要填写下面的基本信息而已,时间 金额 项目。
    所以我是共用一个页面的,唯一的区别就是编辑项目的时候需要数据初始化。那么如何知道是编辑还是新增呢?

    我的做法

    前面我已经提到了用localstorage去存储editIndex了。只要在进入当前页面的时候,即monted的时候获取这个editIndex是否存在,存在的话,就定义editType = 'edit',相反,就是editType = 'add'.
    当然,在你离开页面的时候,还需要将editIndex给remove掉。

    所以,在moneyRecord页面,我会删除。

      mounted () {
        this.getIconList()
        let editItem = JSON.parse(localStorage.getItem('editItem') || '[]')
        if (editItem.length !== 0) {
    //  编辑状态,进行数据的初始化
          this.Edittype = 'edit'  // 当前是编辑状态
          this.type = editItem.type
          this.selectedIcon = editItem.icontype
          this.consumeMoney = editItem.money
          this.pickerFormateValue = editItem.date
          if (this.type === '支出') {
            this.highlight = 'output'
            this.showIcon = this.outputIcon
          } else {
            this.highlight = 'income'
            this.showIcon = this.incomeIcon
          }
        } else {
    //  新增状态,将数据清空。
          this.Edittype = 'add'  // 当前是新增状态
          this.pickerFormateValue = this.setDateFormate(new Date())
          this.highlight = 'output'
          this.showIcon = this.outputIcon
          this.selectedIcon = ''
          this.consumeMoney = ''
        }
      },
    beforeDestroy () {
        bus.$off('get', this.myhandle)
        localStorage.removeItem('editItem')
        localStorage.removeItem('editIndex')
      }
    

    4 实现icon的开关设置

    可以手动控制icon的显示和隐藏。我会先初始化定义一些icon的数据,初始化存储在localstorage中。然后通过监听数据的变化,来实时变化存储的数据。因为要监听到的是对象属性值的变化,所以需要深度监听。

    // 通过type 中的状态来判断是否显示icon
        getIconList () {
          this.outputIcon = JSON.parse(localStorage.getItem('outputIcon') || '[]')
          this.incomeIcon = JSON.parse(localStorage.getItem('incomeIcon') || '[]')
          console.log(this.incomeIcon, this.outputIcon, '这是新的输出icon', '这是新的输入icon')
          if (this.incomeIcon.length === 0) {
            this.incomeIcon = [
          {name: 'pay', title: '薪资', iconClass: 'icon-zhifuxinshui', type: true},
          {name: 'getmoney', title: '奖金', iconClass: 'icon-jiangxuejinv', type: true},
          {name: 'shorttime', title: '兼职', iconClass: 'icon-jianzhizhongdiangong', type: true},
          {name: 'rate', title: '投资收益', iconClass: 'icon-touzihouhuodeshouyi', type: true}]
            this.outputIcon = [
          {name: 'shopping', title: '购物', iconClass: 'icon-gouwu', type: true},
          {name: 'money', title: '理财', iconClass: 'icon-licai', type: true},
          {name: 'traffic', title: '交通', iconClass: 'icon-jiaotong', type: true},
          {name: 'fun', title: '娱乐', iconClass: 'icon-yule', type: true},
          {name: 'meal', title: '餐饮', iconClass: 'icon-icon', type: true},
          {name: 'travel', title: '旅行', iconClass: 'icon-lvyou', type: true},
          {name: 'medical', title: '医疗', iconClass: 'icon-yiliao', type: true},
          {name: 'specialMoney', title: '礼金', iconClass: 'icon-lijin', type: true},
          {name: 'beauty', title: '美容', iconClass: 'icon-meirong', type: true}]
            localStorage.setItem('outputIcon', JSON.stringify(this.outputIcon))
            localStorage.setItem('incomeIcon', JSON.stringify(this.incomeIcon))
          }
        }
    
    // 监听数据的变化,数据变化,就重新存储数据。
      outputIcon: {
          handler: function (val) { localStorage.setItem('outputIcon', JSON.stringify(val)) },
          deep: true
        },
        incomeIcon: {
          handler: function (val) { localStorage.setItem('incomeIcon', JSON.stringify(val)) },
          deep: true
        }
    
    test.gif

    vue 的主要核心就是数据驱动,做这个项目的时候就深刻地意识到,事先定义好比较好的数据结构是多么重要。一旦数据结构定义好之后,再进行后期的数据处理,就可以很好地根据数据进行渲染了。
    所以在这里数据的后期处理就很重要,掌握好数组的一些方法,像sort findIndex 以及split等方法都很重要。

    是时候学点后端的东西啦。。。。。

    相关文章

      网友评论

        本文标题:vue移动助手实践(三)————结合vue和localstora

        本文链接:https://www.haomeiwen.com/subject/rddlyxtx.html