vue移動助手實踐(三)————結合vue和localstorage的移動端記帳demo

最近在用vue作一個小demo,作了幾個小小的功能模塊,當作是學習練手吧。畢竟本身能力仍是比較受限的,只能慢慢進步啦。最近就想着本身作一個移動端的記帳小demo,由於本身沒有弄後臺(實際上是還沒去接觸學習哇咔咔),因此關於數據的存儲暫時就先用localstorage來存儲數據啦。html

項目在線demo

項目在線演示demo(切換到移動端調試模式哦)vue

項目github地址

項目github地址git

最近在作的是小demo,這個是其中的一兩個頁面,是記帳模塊。專門抽出來說。總的代碼我會放在github上,今天講的這一部分代碼主要是下面三個文件內。github

image.png
image.png

1 很是粗糙的草圖 (莫嫌棄哈哈哈,醜帥醜帥的字)

首先,上一下一一開始的設計圖(略醜)。個人目的是,能夠記錄每一天的消費收入狀況(包括消費項目 ,時間以及消費金額),經過選擇時間能夠篩選每月份的消費收入狀況。每一天的收入消費狀況爲一個節點,點擊每條條目均可以進入編輯頁面進行消費項目 ,時間以及消費金額的編輯。因此編輯項目頁面和新增項目頁面是同一個頁面。
對了,關於收入支出的icon列表,我一開始就已經定義了一些常見的icon。在icon list中的設置能夠進行icon的編輯。後端

image.png
image.png

2 成果先看一步(UI有點醜,我後期要美化!!!!!)

test.gif
test.gif

3 項目的整個過程
問題難點:
  • 數據格式的定義:由於涉及到能夠篩選出某一年的某一個月的全部數據,同時每個月的某一天的數據也能夠被篩選,因此數據格式的定義以及數據的存儲方式很重要。
  • 數據後期處理篩選:存儲了數據以後,如何根據年月來選擇篩選數據,同時要將屬於同一天的數據篩選出來。
  • 獲取當前被編輯的項目:由於點擊每一條項目以後,均可以相應跳轉進去編輯頁面,當前的編輯頁面須要自動填充當前項目的數據,由於這涉及到兩個頁面之間的數據傳遞,我最後仍是選擇了用localstorage 來存儲傳遞數據。

實踐開始:
1 數據存儲的形式:數組

我決定把每一條消費收入項目定義成一條這樣的數據形式,而後存儲在一個數組裏。bash

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"}
]複製代碼

篩選出某一年某一個月的數據仍是不夠的。由於咱們須要向這樣的一種格式去顯示出來,就意味着須要將屬於同一天的數據存儲在一塊兒ui

image.png
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
test.gif

vue 的主要核心就是數據驅動,作這個項目的時候就深入地意識到,事先定義比如較好的數據結構是多麼重要。一旦數據結構定義好以後,再進行後期的數據處理,就能夠很好地根據數據進行渲染了。
因此在這裏數據的後期處理就很重要,掌握好數組的一些方法,像sort findIndex 以及split等方法都很重要。

是時候學點後端的東西啦。。。。。

相關文章
相關標籤/搜索