以太坊众筹DAPP实战(四)

一、发起众筹

(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>
    )
}

运行程序,效果如下图所示:

 

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值