今天学习React,写一个小demo记录一下,弄一个简单的个人消费记账系统.
使用脚手架 create-react-app ,后台使用mockAPI 模拟,网络请求使用axios ,css效果使用bootstrap/4.0.0 . 先来个效果图: 其主要代码均在../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,下面是源码:源码地址 学习前端