一、发起众筹
(1)在display目录下创建CreateFundingForm.js文件。该页面定义了发起众筹的表单。
import React, {Component} from 'react';
import {Dimmer, Form, Label, Loader, Segment} from 'semantic-ui-react'
import {createFunding} from "../eth/interaction";
class CreateFundingForm extends Component {
// 定义状态变量
state = {
active: false,
projectName: '',
supportMoney: '',
targetMoney: '',
duration: '',
}
render() {
let {active, projectName, targetMoney, supportMoney, duration} = this.state
return (
<div>
<Dimmer.Dimmable as={Segment} dimmed={active}>
<Dimmer active={active} inverted>
<Loader>Loading</Loader>
</Dimmer>
<Form onSubmit={this.handleCreate}>
<Form.Input required type='text' placeholder='项目名称' name='projectName'
value={projectName} label='项目名称:'
onChange={this.handleChange}/>
<Form.Input required type='text' placeholder='支持金额' name='supportMoney'
value={supportMoney} label='支持金额:'
labelPosition='left'
onChange={this.handleChange}>
<Label basic>¥</Label>
<input/>
</Form.Input>
<Form.Input required type='text' placeholder='目标金额' name='targetMoney' value={targetMoney}
label='目标金额:'
labelPosition='left'
onChange={this.handleChange}>
<Label basic>¥</Label>
<input/>
</Form.Input>
<Form.Input required type='text' placeholder='众筹时间' name='duration' value={duration}
label='众筹时间:'
labelPosition='left'
onChange={this.handleChange}>
<Label basic>S</Label>
<input/>
</Form.Input>
<Form.Button primary content='创建众筹'/>
</Form>
</Dimmer.Dimmable>
</div>
)
}
}
export default CreateFundingForm
上面代码使用到了semantic-ui的form组件。具体可以参考:https://react.semantic-ui.com/collections/form/
(2)定义表单项的onchange事件函数,该函数把表单项的值设置到状态变量中。
// 回调函数,表单数据数据变化时触发
handleChange = (e, {name, value}) => this.setState({[name]: value})
(3)定义表单提交函数。
import {createFunding} from "../eth/interaction";
// 该函数提交表单触发
handleCreate = async () => {
// 把状态变量的数据解构出来
let {active, projectName, targetMoney, supportMoney, duration} = this.state
// 显示遮挡层
this.setState({active: true})
// 通过web3创建合约
try {
let res = await createFunding(projectName, targetMoney, supportMoney, duration)
alert(`创建合约成功!\n`)
} catch (e) {
console.log(e)
}
// 隐藏遮挡层
this.setState({active: false})
}
(4)在interaction.js文件中定义createFunding方法,并导出该方法。
// 创建合约
let createFunding = (projectName, targetMoney, supportMoney, duration) => {
return new Promise(async (resolve, reject) => {
try { // 调用创建方法
let accounts = await web3.eth.getAccounts()
let res = await factoryContractInstance.methods.createFunding(projectName, targetMoney, supportMoney, duration).send({
from: accounts[0],
})
resolve(res)
} catch (e) {
reject(e)
}
})
}
export {
...
createFunding,
}
二、参与众筹
(1)给Card添加onClick事件。
<Card onClick={}>
...
</Card>
(2)在FullFundingTab中定义回调函数,并通过props传递给Card。
// 回调函数,点击每一个Card时候自动调用
onCardClick = (detail) => {
console.log("detail : ", detail)
}
render() {
return (
<div>
<CardList details={this.state.allFundingDetail} onCardClick={this.onCardClick}/>
...
</div>
)
}
<Card onClick={() => {onCardClick(detail)}}>
...
</Card>
(3)在CardList中接收传递过来的回调函数,并且设置到onlick时间中。
const CardList = (props) => {
...
let onCardClick = props.onCardClick
let cards = details.map(detail => {
// 获取合约信息,并且设置到Card组件中
return <CardExampleCard key={detail.fundingAddress} detail={detail} onCardClick={onCardClick}/>
})
...
}
const CardExampleCard = (props) => {
let onCardClick = props.onCardClick
...
return (
<div>
<Card onClick={() => {onCardClick(detail)}}>
...
</Card>
</div>
)
}
(3)点击Card的时候,将这个Card的详细信息通过回调函数返回给主界面,从而完成数据的加载。
在AllFundingTab添加一个状态变量,记录当前点击Card的详情。
state = {
...
currentFundingDetail : ''
}
在onCardClick函数中设置该状态变量。
// 回调函数,点击每一个Card时候自动调用
onCardClick = (currentFundingDetail) => {
this.setState({
currentFundingDetail
})
}
在render方法中把状态变量的数据结构出来,然后设置到对应的表单项。
render() {
let detail = this.state.currentFundingDetail
let {fundingAddress, manager, projectName, targetMoney, supportMoney, leftTime, balance, investorCount} = detail
return (
<div>
<CardList details={this.state.allFundingDetail} onCardClick={this.onCardClick}/>
<Dimmer.Dimmable as={Segment} dimmed={this.state.active}>
<Dimmer active={this.state.active} inverted>
<Loader>支持中</Loader>
</Dimmer>
<Form onSubmit={this.handleInvest}>
<Form.Input type='text' value={projectName || ''} label='项目名称:'/>
<Form.Input type='text' value={fundingAddress || ''} label='项目地址:'/>
<Form.Input type='text' value={supportMoney || ''} label='支持金额:'
labelPosition='left'>
<Label basic>¥</Label>
<input/>
</Form.Input>
<Form.Button primary content='参与众筹'/>
</Form>
</Dimmer.Dimmable>
</div>
)
}
(4)发起众筹
第一步:为表单添加事件。
import {getFundingDetail, handleInvestFunc} from '../eth/interaction'
handleInvest = async () => {
// 对detail进行解构
let detail = this.state.currentFundingDetail
const {fundingAddress, manager, projectName, targetMoney, supportMoney, leftTime, balance, investorCount} = detail
this.setState({active : true})
try {
// 调用方法发起参与
await handleInvestFunc(fundingAddress, supportMoney)
alert('参与成功')
} catch (e) {
console.log(e)
}
this.setState({active : false})
}
第二步:在interaction.js中定义handleInvestFunc函数,该函数调用合约的invest方法。
// 参与众筹
let handleInvestFunc = async (fundingAddress, supportMoney) => {
// 获取合约实例
let fundingContractInstance = getFundingContractInstance()
// 设置地址
fundingContractInstance.options.address = fundingAddress
// 获取账户
let accounts = await web3.eth.getAccounts()
// 调用合约的invest方法
let res = await fundingContractInstance.methods.invest().send({
from: accounts[0],
value: supportMoney,
})
return res
}
export {
...
handleInvestFunc,
}
执行效果如下图所示:
三、花费申请
(1)创建花费申请表单
第一步:修改CreatorFundingTab页面,添加currentFundingDetail、requestDesc、requestBalance、requestAddress状态变量。
state = {
creatorFundingDetail : [],
currentFundingDetail : '',
requestDesc : '', // 请求目的
requestBalance : '', // 请求金额
requestAddress : '', // 商家地址
}
第二步:在render方法中定义花费请求的表单。
render() {
let {creatorFundingDetail, currentFundingDetail, requestDesc, requestBalance, requestAddress} = this.state
return (
<div>
<CardList details={this.state.creatorFundingDetail} onCardClick={this.onCardClick}/>
<CreateFundingForm/>
{
<div>
<h3>发起付款请求</h3>
<Segment>
<h4>当前项目:{currentFundingDetail.projectName}, 地址: {currentFundingDetail.fundingAddress}</h4>
<Form onSubmit={this.handleCreate}>
<Form.Input type='text' name='requestDesc' required value={requestDesc}
label='请求描述' placeholder='请求描述' onChange={this.handleChange}/>
<Form.Input type='text' name='requestBalance' required value={requestBalance}
label='付款金额' labelPosition='left' placeholder='付款金额'
onChange={this.handleChange}>
<Label basic>¥</Label>
<input/>
</Form.Input>
<Form.Input type='text' name='requestAddress' required value={requestAddress}
label='商家收款地址' labelPosition='left' placeholder='商家地址'
onChange={this.handleChange}>
<Label basic><span role='img' aria-label='location'>?</span></Label>
<input/>
</Form.Input>
<Form.Button primary content='开始请求'/>
</Form>
</Segment>
</div>
}
</div>
)
}
第三步:定义onCardClick、handleChange、handleRequest方法。
// 回调函数,点击每一个Card时候自动调用
onCardClick = (currentFundingDetail) => {
this.setState({
currentFundingDetail
})
}
// 回调函数,表单数据数据变化时触发
handleChange = (e, {name, value}) => this.setState({[name]: value})
// 该函数提交表单触发
handleCreate = async () => {
let {creatorFundingDetail, currentFundingDetail, requestDesc, requestBalance, requestAddress} = this.state
console.log(creatorFundingDetail, currentFundingDetail, requestDesc)
}
运行程序,效果如下图所示:
(2)实现花费请求的的业务逻辑
第一步:在interaction.js中定义一个方法,实现创建请求功能,然后再导出该方法。
// 创建花费请求
let createRequest = (fundingAddress, desc, targetMoney, seller) => {
return new Promise(async (resolve, reject) => {
try { // 调用创建方法
let accounts = await web3.eth.getAccounts()
let fundingInstance = getFundingContractInstance()
fundingInstance.options.address = fundingAddress
// function createRequest(string _purpose, uint256 _cost, address _seller) onlyManager public {
let res = await fundingInstance.methods.createRequest(desc, targetMoney, seller).send({
from: accounts[0],
})
resolve(res)
} catch (e) {
reject(e)
}
})
}
export {
...
createRequest,
}
第二步:在CreatorFundingTab文件中导入该方法,然后在handleCreate方法中调用上面方法。
import {factoryContractInstance, fundingContractInstance} from '../eth/instance'
// 该函数提交表单触发
handleCreate = async () => {
let {creatorFundingDetail, currentFundingDetail, requestDesc, requestBalance, requestAddress} = this.state
// createRequest(fundingAddress, desc, targetMoney, seller)
try {
await createRequest(currentFundingDetail.fundingAddress, requestDesc, requestBalance,requestAddress )
} catch (e) {
console.log(e)
}
}
(3)显示请求申请
第一步:在commons目录下新建RequestTable文件,该文件定义了一个表格用来显示请求申请记录。
import React from 'react'
import { Icon, Label, Menu, Table } from 'semantic-ui-react'
const RequestTable = () => (
<Table celled>
<Table.Header>
<Table.Row>
<Table.HeaderCell>花费描述</Table.HeaderCell>
<Table.HeaderCell>花费金额</Table.HeaderCell>
<Table.HeaderCell>商家地址</Table.HeaderCell>
<Table.HeaderCell>当前赞成人数</Table.HeaderCell>
<Table.HeaderCell>当前状态</Table.HeaderCell>
<Table.HeaderCell>操作</Table.HeaderCell>
</Table.Row>
</Table.Header>
<Table.Body>
<Table.Row>
<Table.Cell>
<Label ribbon>First</Label>
</Table.Cell>
<Table.Cell>Cell</Table.Cell>
<Table.Cell>Cell</Table.Cell>
<Table.Cell>Cell</Table.Cell>
<Table.Cell>Cell</Table.Cell>
<Table.Cell>Cell</Table.Cell>
</Table.Row>
</Table.Body>
</Table>
)
export default RequestTable
这里使用semantic-ui的table组件来显示众筹合约的请求申请记录。
关于table组件的用法可以参考:https://react.semantic-ui.com/collections/table/
第二步:在CreatorFundingTab文件中引入表格。
import RequestTable from './common/RequestTable'
render() {
...
return (
<div>
...
<RequestTable/>
</div>
)
}
运行程序的效果如下所示:
(4)获取请求数据,并展示在页面中。
第一步:在interaction.js文件中定义一个方法,用于获取合约的花费请求记录,并导出该方法。
// 获取所有花费请求申请
let showRequests = (fundingAddress) => {
return new Promise(async (resolve, reject) => {
try {
// 获取Funding合约实例
let fundingInstance = getFundingContractInstance()
fundingInstance.options.address = fundingAddress
// 调用合约方法获取请求总数
let requestCount = await fundingInstance.methods.getRequestCount().call()
// 定义一个数组,保存所有请求申请
let requestDetails = []
for (let i = 0; i < requestCount; i++) {
// 调用合约方法,根据索引查询请求申请
let requestDetail = await fundingInstance.methods.getRequest(i).call()
// 把结果添加到数组中
requestDetails.push(requestDetail)
}
resolve(requestDetails)
} catch (e) {
reject(e)
}
})
}
export {
...
showRequests,
}
第二步:在CreatorFundingTab中导入该方法,并且定义状态变量requests,记录所有的请求申请。
import {..., showRequests} from '../eth/interaction'
state = {
...
requests : [], // 请求详情
}
第三步:定义onRequestDetailClick方法,该方法调用showRequests方法获取指定合约的请求申请,并设置到状态变量中。
// 申请详情按钮事件
onRequestDetailClick = async () => {
try {
let {currentFundingDetail} = this.state
let requests = await showRequests(currentFundingDetail.fundingAddress)
this.setState({"requests": requests})
} catch (e) {
console.log(e)
}
}
第四步:把状态变量传递到RequestTable中。
render() {
...
return (
...
<RequestTable requests={requests}/>
)
}
第五步:在RequestTable获取requests,然后遍历requests,获取每一个request出来。一个request对应着一行数据。
const RequestTable = (props) => {
let {requests} = props
// 遍历requestDetails,每一个RequestDetail都生成一个Table.Row。
let rows = requests.map((request, i) => {
return <RowInfo key={i} request={request}/>
})
return (
<Table celled>
<Table.Header>
<Table.Row>
<Table.HeaderCell>花费描述</Table.HeaderCell>
<Table.HeaderCell>花费金额</Table.HeaderCell>
<Table.HeaderCell>商家地址</Table.HeaderCell>
<Table.HeaderCell>当前赞成人数</Table.HeaderCell>
<Table.HeaderCell>当前状态</Table.HeaderCell>
<Table.HeaderCell>操作</Table.HeaderCell>
</Table.Row>
</Table.Header>
<Table.Body>
{
rows
}
</Table.Body>
</Table>
)
}
定义RowInfo方法,该方法生成并返回一行数据。
let RowInfo = (props) => {
let {request} = props
let {0:purpose, 1:cost, 2:seller, 3:approveCount, 4:status} = request
return (
<Table.Row>
<Table.Cell>
<Label ribbon>{purpose}</Label>
</Table.Cell>
<Table.Cell>{cost}</Table.Cell>
<Table.Cell>{seller}</Table.Cell>
<Table.Cell>{approveCount}</Table.Cell>
<Table.Cell>{status}</Table.Cell>
<Table.Cell></Table.Cell>
</Table.Row>
)
}
运行程序,效果如下图所示: