今天学习React,写一个小demo记录一下,弄一个简单的个人消费记账系统.

使用脚手架 create-react-app ,后台使用mockAPI模拟,网络请求使用axios,css效果使用bootstrap/4.0.0 .
先来个效果图:
react-accounts-result
其主要代码均在../src/components中,主文件是Records.js,
具体的消费记录是Record.js,总消费记录是Box.js,消费记录的创建是RecordForm.js ,下面是其源码:
Records.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
import React, { Component } from 'react';
import Record from './Record'
import RecordForm from './RecordForm'
import Box from './Box'
import axios from 'axios'

export default class Records extends Component {

// 构造
constructor(props) {
super(props);
// 初始状态
this.state = {
error:null, //网络错误信息
isLoader:true, //加载标示
records:[] //数据
};
}


render() {
//根据 error isLoader 显示不同UI
//如果error有值,显示errorUI
//如果isLoader 为true,显示加载UI
//如果isLoader 为false,显示正确数据UI

const {error,isLoader,records} = this.state
let renderRecords;
if(error){
renderRecords = <div>出错了:{error}</div>
}else if(isLoader){
renderRecords = <div><img src={require('./loading.gif')} /></div>
}else {
renderRecords = (
<div>
<table className="table table-bordered">
<thead>
<tr>
<th>日期</th>
<th>标题</th>
<th>金额</th>
<th>事件</th>

</tr>
</thead>
<tbody>
{records.map((item,index)=>
<Record
key ={item.id}
record={item}
updateData={this.updateRecord.bind(this)}
deleteData={this.deleteRecord.bind(this)}
/>
)}
</tbody>
</table>
</div>
);
}

return(
<div>
<h2>消费记录</h2>
<div className="row mb-3">
<Box text="收入" type="success" amount={this.credits()} />
<Box text="支出" type="danger" amount={this.debits()} />
<Box text="余额" type="info" amount={this.balance()} />
</div>
<RecordForm handleNewRecord = {this.addRecord.bind(this)} />
{renderRecords}
</div>
)
}


/**
* 收入计算
* */
credits() {
let credits = this.state.records.filter((record) => {
return record.account >= 0;
})

return credits.reduce((prev, curr) => {
return prev + Number.parseInt(curr.account, 0)
}, 0)
}
/**
* 支出计算
* */
debits() {
let credits = this.state.records.filter((record) => {
return record.account < 0;
})

return credits.reduce((prev, curr) => {
return prev + Number.parseInt(curr.account, 0)
}, 0)
}
/**
* 余额计算
* */
balance() {
return this.credits() + this.debits();
}

/**
* 更新账单
* */
updateRecord(record, data) {
const recordIndex = this.state.records.indexOf(record);
const newRecords = this.state.records.map( (item, index) => {
if(index !== recordIndex) {
// This isn't the item we care about - keep it as-is
return item;
}

// Otherwise, this is the one we want - return an updated value
return {
...item,
...data
};
});
this.setState({
records: newRecords
});
}
/**
* 删除账单
* */
deleteRecord(record){
// console.log(record)
const recordIndex = this.state.records.indexOf(record);
const newRecords = this.state.records.filter( (item, index) => index !== recordIndex);
this.setState({
records: newRecords
});
}

/**
* 把最新数据赋值state
* */
addRecord (record) {
console.log(record)
this.setState({
error:null,
isLoader:false, //加载标示
records:[
...this.state.records,
record
]
})

}

//请求数据
componentDidMount() {
var that = this
axios.get('http://5b3450a9d167760014c265b5.mockapi.io/accounts/v1/accounts')
.then(response =>
that.setState({
isLoader:false,
records:response.data
})
)
.catch(err =>
that.setState({
error:err.message,
})
)


}

}

Record.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
import React, { Component } from 'react';
import moment from "moment"; //日期格式
import axios from 'axios'

export default class Record extends Component {
// 构造
constructor(props) {
super(props);
// 初始状态
this.state = {
edit:false //编辑按钮状态
};
}

render() {
//如果编辑状态为true,显示编辑状态,否则显示默认状态
if(this.state.edit){
return this.renderEditRow()
}else {
return this.renderRow()
}
}


renderEditRow () {
var date = this.props.record.date
var newDate = moment(date).format('YYYY-MM-DD');
return (
<tr>
<td><input type="text" className="form-cantrol" defaultValue={newDate} ref="date" /></td>
<td><input type="text" className="form-cantrol" defaultValue={this.props.record.title} ref="title" /></td>
<td><input type="text" className="form-cantrol" defaultValue={this.props.record.account} ref="account" /></td>
<td>
<button className="btn btn-info mr-1" onClick={this.handleUpdate.bind(this)}>更新</button>
<button className="btn btn-danger " onClick={this.handleToggle.bind(this)}>取消</button>
</td>
</tr>
);
}

renderRow () {
var date = this.props.record.date
var newDate = moment(date).format('YYYY-MM-DD');
return (
<tr>
<td>{newDate}</td>
<td>{this.props.record.title}</td>
<td>{this.props.record.account}</td>
<td>
<button className="btn btn-info mr-1" onClick={this.handleToggle.bind(this)}>编辑</button>
<button className="btn btn-danger" onClick={this.handleDelete.bind(this)}>删除</button>
</td>
</tr>
);
}

/**
* 编辑按钮点击事件
* 对state edit做取反操作
* */
handleToggle () {
this.setState({
edit:!this.state.edit
})
}

/**
* 删除 按钮点击事件
* */
handleDelete (event) {
event.preventDefault()

var that = this
// alert(that.props.record.id)
axios.delete('http://5b3450a9d167760014c265b5.mockapi.io/accounts/v1/accounts/' + that.props.record.id)
.then(response =>{
console.log(response)
this.props.deleteData(that.props.record)
})
.catch(err =>{

})


}

/**
* 更新记录事件
* */
handleUpdate (event) {
event.preventDefault() // 方法阻止元素发生默认的行为

const record ={
date:this.refs.date.value,
title:this.refs.title.value,
account:this.refs.account.value
}

var that = this
// alert(that.props.record.id)
axios.put('http://5b3450a9d167760014c265b5.mockapi.io/accounts/v1/accounts/' + that.props.record.id,{
date:record.date,
title:record.title,
account:record.account
})
.then(response =>{
this.props.updateData(this.props.record,response.data)
this.setState({
edit:false
})
})
.catch(err =>
that.setState({
error:err.message,
})
)


}



}

Box.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
* 上部的总消费记录
* */
import React from 'react';

const Box = ({ text, type, amount }) => {
return (
<div className="col">
<div className="card">
<div className={`card-header bg-${type} text-white`}>
{text}
</div>
<div className="card-body">
{amount}
</div>
</div>
</div>
);
}

export default Box

RecordForm.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
import React, { Component } from 'react';
import axios from 'axios'

export default class RecordForm extends Component {
// 构造
constructor(props) {
super(props);
// 初始状态
this.state = {
date:"",
title:"",
account:""
};
}
/**
* 判断按钮是否可用
* */

valid(){
return this.state.date && this.state.title && this.state.account;
}

/**
* 输入改变触发
* */
handleChange (event){
let name,obj;
name = event.target.name;
this.setState((
obj = {},
obj["" + name] = event.target.value,
obj
))
}
/**
* 提交按钮触发
* */

handleSubmit (event){
event.preventDefault()
var that = this
axios.post('http://5b3450a9d167760014c265b5.mockapi.io/accounts/v1/accounts',{
date:that.state.date,
title:that.state.title,
account:that.state.account
})
.then(response =>{
console.log(response);
this.props.handleNewRecord(response.data)
that.setState({
date:"",
title:"",
account:""
})

})
.catch(err =>
that.setState({
error:err.message,
})
)
}

render() {
return (
<form className="form-inline mb-2" onSubmit={this.handleSubmit.bind(this)} >
<div className="form-group mr-1">
<input type="text" onChange={this.handleChange.bind(this)} value={this.state.date} className="form-control" placeholder="时间" name="date" />
</div>
<div className="form-group mr-1">
<input type="text" onChange={this.handleChange.bind(this)} value={this.state.title} className="form-control" placeholder="标题" name="title" />
</div>
<div className="form-group mr-1">
<input type="text" onChange={this.handleChange.bind(this)} value={this.state.account} className="form-control" placeholder="账目" name="account" />
</div>
<button type="submit" className="btn btn-primary" disabled={!this.valid()} >创建</button>
</form>
);
}

}

ok,下面是源码:
源码地址
学习前端