最近在用vue做一個(gè)小demo,做了幾個(gè)小小的功能模塊宽档,當(dāng)做是學(xué)習(xí)練手吧炭懊。畢竟自己能力還是比較受限的并级,只能慢慢進(jìn)步啦。最近就想著自己做一個(gè)移動(dòng)端的記賬小demo凛虽,因?yàn)樽约簺]有弄后臺(其實(shí)是還沒去接觸學(xué)習(xí)哇咔咔)死遭,所以關(guān)于數(shù)據(jù)的存儲(chǔ)暫時(shí)就先用localstorage來存儲(chǔ)數(shù)據(jù)啦。
項(xiàng)目在線demo
項(xiàng)目在線演示demo(切換到移動(dòng)端調(diào)試模式哦)
項(xiàng)目github地址
最近在做的是小demo,這個(gè)是其中的一兩個(gè)頁面凯旋,是記賬模塊呀潭。專門抽出來講≈练牵總的代碼我會(huì)放在github上钠署,今天講的這一部分代碼主要是下面三個(gè)文件內(nèi)。
1 非常粗糙的草圖 (莫嫌棄哈哈哈荒椭,丑帥丑帥的字)
首先谐鼎,上一下一一開始的設(shè)計(jì)圖(略丑)。我的目的是趣惠,可以記錄每一天的消費(fèi)收入情況(包括消費(fèi)項(xiàng)目 ,時(shí)間以及消費(fèi)金額)狸棍,通過選擇時(shí)間可以篩選每個(gè)月份的消費(fèi)收入情況。每一天的收入消費(fèi)情況為一個(gè)節(jié)點(diǎn)味悄,點(diǎn)擊每條條目都可以進(jìn)入編輯頁面進(jìn)行消費(fèi)項(xiàng)目 ,時(shí)間以及消費(fèi)金額的編輯草戈。所以編輯項(xiàng)目頁面和新增項(xiàng)目頁面是同一個(gè)頁面。
對了侍瑟,關(guān)于收入支出的icon列表唐片,我一開始就已經(jīng)定義了一些常見的icon。在icon list中的設(shè)置可以進(jìn)行icon的編輯涨颜。
2 成果先看一步(UI有點(diǎn)丑费韭,我后期要美化=钕帧H锍獭!K斡妗5稹)
3 項(xiàng)目的整個(gè)過程
問題難點(diǎn):
- 數(shù)據(jù)格式的定義:因?yàn)樯婕暗娇梢院Y選出某一年的某一個(gè)月的所有數(shù)據(jù)钉汗,同時(shí)每一個(gè)月的某一天的數(shù)據(jù)也可以被篩選羹令,所以數(shù)據(jù)格式的定義以及數(shù)據(jù)的存儲(chǔ)方式很重要。
- 數(shù)據(jù)后期處理篩選:存儲(chǔ)了數(shù)據(jù)之后损痰,如何根據(jù)年月來選擇篩選數(shù)據(jù),同時(shí)要將屬于同一天的數(shù)據(jù)篩選出來酒来。
- 獲取當(dāng)前被編輯的項(xiàng)目:因?yàn)辄c(diǎn)擊每一條項(xiàng)目之后卢未,都可以相應(yīng)跳轉(zhuǎn)進(jìn)去編輯頁面,當(dāng)前的編輯頁面需要自動(dòng)填充當(dāng)前項(xiàng)目的數(shù)據(jù)堰汉,因?yàn)檫@涉及到兩個(gè)頁面之間的數(shù)據(jù)傳遞辽社,我最后還是選擇了用localstorage 來存儲(chǔ)傳遞數(shù)據(jù)。
實(shí)踐開始:
1 數(shù)據(jù)存儲(chǔ)的形式:
我決定把每一條消費(fèi)收入項(xiàng)目定義成一條這樣的數(shù)據(jù)形式,然后存儲(chǔ)在一個(gè)數(shù)組里翘鸭。
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是消費(fèi)的類型滴铅,是收入還是支出。money是消費(fèi)的金額就乓。
date則是消費(fèi)時(shí)間汉匙,icontype是我存儲(chǔ)的icon的名字,可以根據(jù)icontype的名字來顯示icon
接著就是數(shù)據(jù)的篩選了生蚁。上面的示例里是有9月噩翠,10月,11月的數(shù)據(jù)邦投,當(dāng)然我們只需要的是某一個(gè)月份的數(shù)據(jù)伤锚,所以需要做一個(gè)filterData的方法來先過濾數(shù)據(jù)。
// 通過年和月來篩選數(shù)據(jù),返回篩選出來的數(shù)據(jù)志衣。傳進(jìn)去的data參數(shù)是要篩選的數(shù)據(jù)
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
}
接著屯援,就已經(jīng)篩選出來了某一年某一月的消費(fèi)數(shù)據(jù)了。我指定了年月是2017年10月念脯,篩選出來之后數(shù)據(jù)如下所示:
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"}
]
篩選出某一年某一個(gè)月的數(shù)據(jù)還是不夠的狞洋。因?yàn)槲覀冃枰蜻@樣的一種格式去顯示出來,就意味著需要將屬于同一天的數(shù)據(jù)存儲(chǔ)在一起
所以和二,我又寫了一個(gè)方法sortDatabyDate()
徘铝,來將數(shù)據(jù)進(jìn)行篩選組合,先看一下轉(zhuǎn)換之后的數(shù)據(jù)格式惯吕,如下所示:這個(gè)格式的好處就是惕它,計(jì)算總的收入支出的時(shí)候,
list = [
// 這是2017-10-6的數(shù)據(jù)
{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的數(shù)據(jù)
{date: "2017-10-6", income:34, outcome:'10377', sortindex:"6", list: [
{type: "收入", money: "34", date: "2017-10-7", icontype: "pay"}]}
]
其實(shí)就是废登,將每一天的數(shù)據(jù)存在一個(gè)對象里淹魄,然后其中的list就是這一天的每一條消費(fèi)收入。其中的sortindex是為了排序使用的堡距,就是將每天的數(shù)據(jù)存儲(chǔ)在list中之后甲锡,還需要按照日期從上到下排序兆蕉,所以我會(huì)將這個(gè)月的日期,存儲(chǔ)在sortindex中缤沦。后續(xù)要排序也比較方便了虎韵。
sortDatabyDate () {
var map = []
var dest = []
var income = 0
var outcome = 0
// 獲取當(dāng)前年月的所有的數(shù)據(jù)
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是存儲(chǔ)這個(gè)月的日期的數(shù)組,如果當(dāng)前數(shù)據(jù)的時(shí)間不存在mapl里面缸废,就直接先創(chuàng)建一條數(shù)據(jù)
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 {
// 當(dāng)前這個(gè)數(shù)據(jù)的日期已經(jīng)存在了包蓝,找到這條數(shù)據(jù)的索引,存儲(chǔ)進(jìn)這條數(shù)據(jù)的list對象內(nèi)就可以了
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, '這是排序之前的')
// 再將得到的數(shù)據(jù)進(jìn)行排序企量,**sortByfield方法可以根據(jù)對象的屬性進(jìn)行排序**
dest.sort(this.sortByfield('sortindex'))
this.showConsumeList = dest // 這是得到的最終的數(shù)據(jù)
// 將得到的最終的數(shù)據(jù)测萎,獲取當(dāng)前的總收入和總支出
// 一開始先賦值為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)
}
}
其中的排序方法,其實(shí)就是根據(jù)數(shù)組對象中每一個(gè)對象中的sortIndex屬性來排序届巩。這個(gè)可以結(jié)合數(shù)組的sort()
屬性來使用硅瞧。
(友情鏈接)數(shù)組對象根據(jù)對象排序 sort
// 其中field就是要排序的對象屬性,然后結(jié)合數(shù)組的sort方法恕汇,直接使用就可以腕唧,。
// array.sort(sortByfield(屬性名))
sortByfield (field) {
return function (a, b) {
return a[field] - b[field]
}
}
這樣寫下來就可以實(shí)現(xiàn)數(shù)據(jù)的轉(zhuǎn)換了拇勃。結(jié)合vue的for循環(huán)指令四苇,就可以很愉快地將數(shù)據(jù)渲染出來了。是不是很棒方咆。
2 如何獲取當(dāng)前的編輯項(xiàng)目
細(xì)心的你會(huì)發(fā)現(xiàn)月腋,就是在我點(diǎn)擊每一個(gè)條目之后,會(huì)跳轉(zhuǎn)到編輯頁面瓣赂。而且這個(gè)編輯頁面會(huì)自動(dòng)渲染初始數(shù)據(jù)榆骚,那么這個(gè)數(shù)據(jù)如何去傳遞呢?
我的做法
我是通過這個(gè)當(dāng)前這個(gè)點(diǎn)擊的條目的信息煌集,去獲取這個(gè)條目在總數(shù)據(jù)中的索引值妓肢,再將這個(gè)索引值用localStorage中存儲(chǔ),跳轉(zhuǎn)到編輯頁面之后苫纤,只要從localstorage中獲取就可以了,如果編輯改動(dòng)了碉钠,就直接在總數(shù)據(jù)中根據(jù)索引值去修改就可以了。
editList (item) {
this.$router.replace({path: '/moneyRecord'}) // 頁面跳轉(zhuǎn)到編輯頁面
let totalData = JSON.parse(localStorage.getItem('list') || '[]') //這是所有的條目數(shù)據(jù)
// 點(diǎn)擊進(jìn)去之后就將數(shù)據(jù)傳遞到頁面
this.editIndex = this.findIndex(totalData, item) // 自定義的一個(gè)方法卷拘,從所有的數(shù)據(jù)中獲取到index值喊废。
localStorage.setItem('editIndex', JSON.stringify(this.editIndex)) // 將index存儲(chǔ)下來
localStorage.setItem('editItem', JSON.stringify(item))
},
其中的 findIndex方法
定義如下,使用了數(shù)組自帶的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 新增項(xiàng)目和編輯項(xiàng)目共用一個(gè)頁面
其實(shí)不管是編輯還是新增,都只是需要填寫下面的基本信息而已乍赫,時(shí)間 金額 項(xiàng)目瓣蛀。
所以我是共用一個(gè)頁面的陆蟆,唯一的區(qū)別就是編輯項(xiàng)目的時(shí)候需要數(shù)據(jù)初始化。那么如何知道是編輯還是新增呢惋增?
我的做法
前面我已經(jīng)提到了用localstorage去存儲(chǔ)editIndex
了叠殷。只要在進(jìn)入當(dāng)前頁面的時(shí)候,即monted的時(shí)候
獲取這個(gè)editIndex
是否存在器腋,存在的話溪猿,就定義editType = 'edit'
,相反,就是editType = 'add'
.
當(dāng)然纫塌,在你離開頁面的時(shí)候,還需要將editIndex
給remove掉讲弄。
所以措左,在moneyRecord頁面,我會(huì)刪除。
mounted () {
this.getIconList()
let editItem = JSON.parse(localStorage.getItem('editItem') || '[]')
if (editItem.length !== 0) {
// 編輯狀態(tài)避除,進(jìn)行數(shù)據(jù)的初始化
this.Edittype = 'edit' // 當(dāng)前是編輯狀態(tài)
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 {
// 新增狀態(tài)怎披,將數(shù)據(jù)清空。
this.Edittype = 'add' // 當(dāng)前是新增狀態(tài)
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 實(shí)現(xiàn)icon的開關(guān)設(shè)置
可以手動(dòng)控制icon的顯示和隱藏瓶摆。我會(huì)先初始化定義一些icon的數(shù)據(jù)凉逛,初始化存儲(chǔ)在localstorage中。然后通過監(jiān)聽數(shù)據(jù)的變化群井,來實(shí)時(shí)變化存儲(chǔ)的數(shù)據(jù)状飞。因?yàn)橐O(jiān)聽到的是對象屬性值的變化,所以需要深度監(jiān)聽书斜。
// 通過type 中的狀態(tài)來判斷是否顯示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: '獎(jiǎng)金', 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: '理財(cái)', 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: '醫(yī)療', 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))
}
}
// 監(jiān)聽數(shù)據(jù)的變化诬辈,數(shù)據(jù)變化,就重新存儲(chǔ)數(shù)據(jù)荐吉。
outputIcon: {
handler: function (val) { localStorage.setItem('outputIcon', JSON.stringify(val)) },
deep: true
},
incomeIcon: {
handler: function (val) { localStorage.setItem('incomeIcon', JSON.stringify(val)) },
deep: true
}
vue 的主要核心就是數(shù)據(jù)驅(qū)動(dòng)焙糟,做這個(gè)項(xiàng)目的時(shí)候就深刻地意識到,事先定義好比較好的數(shù)據(jù)結(jié)構(gòu)是多么重要样屠。一旦數(shù)據(jù)結(jié)構(gòu)定義好之后穿撮,再進(jìn)行后期的數(shù)據(jù)處理,就可以很好地根據(jù)數(shù)據(jù)進(jìn)行渲染了痪欲。
所以在這里數(shù)據(jù)的后期處理就很重要悦穿,掌握好數(shù)組的一些方法,像sort findIndex 以及split等方法都很重要勤揩。
是時(shí)候?qū)W點(diǎn)后端的東西啦咧党。。陨亡。傍衡。深员。