以太坊众筹DAPP实战(三)

一、搭建主页面

主界面使用semantic-tab组件。

(1)在display目录下创建一个TabCenter.js文件,在该文件中定义一个Tab组件。代码如下所示:

import React from 'react'
import { Tab } from 'semantic-ui-react'

const panes = [
    { menuItem: '所有的', render: () => <Tab.Pane>1111111</Tab.Pane> },
    { menuItem: '我发起的', render: () => <Tab.Pane>22222222</Tab.Pane> },
    { menuItem: '我参与的', render: () => <Tab.Pane>333333333</Tab.Pane> },
]

const TabCenter = () => <Tab panes={panes} />

export default TabCenter

(2)在App.js文件显示主界面内容。

import React, {Component} from 'react';
import web3 from './utils/InitWeb3'
import TabCenter from './display/TabCenter'

class App extends Component {
    constructor() {
        super()
        this.state = {
            currentAccount: '',
        }
    }

    async componentWillMount() {
        // 获取合约的所有账户
        let accounts = await web3.eth.getAccounts()
        this.setState({
            currentAccount: accounts[0],
        })
    }

    render() {
        return (
            <div>
                <h1>人人众筹</h1>
                <p>当前账户:{this.state.currentAccount}</p>
                <TabCenter/>
            </div>
        )
    }

}

export default App;

(3)在index.js文件中导入css。

import 'semantic-ui-css/semantic.min.css'

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

 

(4)创建三个子页面,分别对应着Tab1、Tab2、Tab3的内容。

在display目录下创建AllFundingTab.js文件,代码如下:

import React, {Component} from 'react';

class AllFundingTab extends Component {

    render() {
        return (
            <p>Tab11111.....</p>
        )
    }
}

export default AllFundingTab

在display目录下创建CreatorFundingTab.js文件,代码如下:

import React, {Component} from 'react';

class CreatorFundingTab extends Component {

    render() {
        return (
            <p>Tab22222.....</p>
        )
    }
}

export default CreatorFundingTab

在display目录下创建SupportorFundingTab.js文件,代码如下:

import React, {Component} from 'react';

class SupportorFundingTab extends Component {

    render() {
        return (
            <p>Tab33333.....</p>
        )
    }
}

export default SupportorFundingTab

(5)修改TabCenter.js文件,引入上面三个文件。

import AllFundingTab from "./AllFundingTab";
import SupportorFundingTab from "./SupportorFundingTab";
import CreatorFundingTab from "./CreatorFundingTab";

const panes = [
    { menuItem: '所有的', render: () => <Tab.Pane><AllFundingTab/></Tab.Pane> },
    { menuItem: '我发起的', render: () => <Tab.Pane><CreatorFundingTab/></Tab.Pane> },
    { menuItem: '我参与的', render: () => <Tab.Pane><SupportorFundingTab/></Tab.Pane> },
]

 

二、显示我发起的众筹

(1)修改CreatorFundingTab.js文件,引入web3实例。

import web3 from '../utils/InitWeb3'

(2)在componentWillMount方法中获取我发起的众筹,并设置到state中。

state = {
    creatorFundings : [],
}

async componentWillMount() {
    let accounts = await web3.eth.getAccounts()
    let creatorFundings = await contractInstance.methods.getCreatorFunding().call({
        from : accounts[0]
    })
    this.setState({
        creatorFundings
    }) 
}

(3)在render方法中从state获取数据。

render() {
    return (
        <p>{this.state.creatorFundings}</p>
    )
}

运行程序,界面如下图所示:

 

三、获取合约详情

现在我们已经获取到了我发起过的合约。接下来我们就要根据合约地址获取合约的详细信息,然后再显示在界面上。

(1)修改instance.js文件,定义变量分别保存FundingFactory和Funding的ABI和合约地址。

// Factory合约的ABI
let factoryAbi = [ { "constant": true, "inputs": [], "name": "platformManager", "outputs": [ { "name": "", "type": "address" } ], "payable": false, "stateMutability": "view", "type": "function" }, { "constant": false, "inputs": [ { "name": "_name", "type": "string" }, { "name": "_targetMoney", "type": "uint256" }, { "name": "_supportMoney", "type": "uint256" }, { "name": "_duration", "type": "uint256" } ], "name": "createFunding", "outputs": [], "payable": false, "stateMutability": "nonpayable", "type": "function" }, { "constant": true, "inputs": [], "name": "getAllFundings", "outputs": [ { "name": "", "type": "address[]" } ], "payable": false, "stateMutability": "view", "type": "function" }, { "constant": true, "inputs": [], "name": "getCreatorFunding", "outputs": [ { "name": "", "type": "address[]" } ], "payable": false, "stateMutability": "view", "type": "function" }, { "constant": true, "inputs": [], "name": "getSupportorFunding", "outputs": [ { "name": "", "type": "address[]" } ], "payable": false, "stateMutability": "view", "type": "function" }, { "inputs": [], "payable": false, "stateMutability": "nonpayable", "type": "constructor" } ]
// Factory合约的地址
let factoryAddress = '0xa046025217a4550013a5720350a775d480bf1971'
// Funding合约的ABI
let fundingAbi = [ { "constant": false, "inputs": [ { "name": "i", "type": "uint256" } ], "name": "finalizeRequest", "outputs": [], "payable": false, "stateMutability": "nonpayable", "type": "function" }, { "constant": true, "inputs": [], "name": "getBalance", "outputs": [ { "name": "", "type": "uint256" } ], "payable": false, "stateMutability": "view", "type": "function" }, { "constant": true, "inputs": [], "name": "endTime", "outputs": [ { "name": "", "type": "uint256" } ], "payable": false, "stateMutability": "view", "type": "function" }, { "constant": true, "inputs": [], "name": "getRequestCount", "outputs": [ { "name": "", "type": "uint256" } ], "payable": false, "stateMutability": "view", "type": "function" }, { "constant": true, "inputs": [], "name": "manager", "outputs": [ { "name": "", "type": "address" } ], "payable": false, "stateMutability": "view", "type": "function" }, { "constant": false, "inputs": [], "name": "refund", "outputs": [], "payable": false, "stateMutability": "nonpayable", "type": "function" }, { "constant": true, "inputs": [], "name": "getLeftTime", "outputs": [ { "name": "", "type": "uint256" } ], "payable": false, "stateMutability": "view", "type": "function" }, { "constant": false, "inputs": [ { "name": "_purpose", "type": "string" }, { "name": "_cost", "type": "uint256" }, { "name": "_seller", "type": "address" } ], "name": "createRequest", "outputs": [], "payable": false, "stateMutability": "nonpayable", "type": "function" }, { "constant": true, "inputs": [], "name": "targetMoney", "outputs": [ { "name": "", "type": "uint256" } ], "payable": false, "stateMutability": "view", "type": "function" }, { "constant": true, "inputs": [], "name": "getInvestorCount", "outputs": [ { "name": "", "type": "uint256" } ], "payable": false, "stateMutability": "view", "type": "function" }, { "constant": true, "inputs": [], "name": "projectName", "outputs": [ { "name": "", "type": "string" } ], "payable": false, "stateMutability": "view", "type": "function" }, { "constant": true, "inputs": [], "name": "supportMoney", "outputs": [ { "name": "", "type": "uint256" } ], "payable": false, "stateMutability": "view", "type": "function" }, { "constant": true, "inputs": [], "name": "getInvestors", "outputs": [ { "name": "", "type": "address[]" } ], "payable": false, "stateMutability": "view", "type": "function" }, { "constant": true, "inputs": [ { "name": "i", "type": "uint256" } ], "name": "getRequest", "outputs": [ { "name": "", "type": "string" }, { "name": "", "type": "uint256" }, { "name": "", "type": "address" }, { "name": "", "type": "uint256" }, { "name": "", "type": "uint8" } ], "payable": false, "stateMutability": "view", "type": "function" }, { "constant": false, "inputs": [ { "name": "i", "type": "uint256" } ], "name": "approveRequest", "outputs": [], "payable": false, "stateMutability": "nonpayable", "type": "function" }, { "constant": false, "inputs": [], "name": "invest", "outputs": [ { "name": "", "type": "uint256" } ], "payable": true, "stateMutability": "payable", "type": "function" }, { "inputs": [ { "name": "_projectName", "type": "string" }, { "name": "_targetMoney", "type": "uint256" }, { "name": "_supportMoney", "type": "uint256" }, { "name": "_duration", "type": "uint256" }, { "name": "_creator", "type": "address" }, { "name": "_supportFunding", "type": "address" } ], "payable": false, "stateMutability": "nonpayable", "type": "constructor" } ]

创建FundingFactory和Funding的实例。因为一个众筹合约对应一个Funding实例,所以,这里需要创建多次Funding实例。而且创建Funding实例时候不需要传入合约地址,可以动态设置。

// 获取合约实例
let factoryContractInstance = new web3.eth.Contract(factoryAbi, factoryAddress)
// 获取Funding合约实例的时候,不需要指定合约地址。
let getFundingContractInstance = () => {
    return new web3.eth.Contract(fundingAbi)
}

导出getFundingContractInstance。

// 导出合约实例
export {
    factoryContractInstance,
    getFundingContractInstance,
}

(2)在eth目录下新建interaction.js文件,在文件中定义一个方法,用于获取所有我发起过的合约详情。

import web3 from "../utils/InitWeb3";
import {factoryContractInstance, getFundingContractInstance} from "./instance";

// 获取我发起合约的详情
// 该函数返回一个Promise对象,该Promise对象包含了所有的合约详情
let getFundingDetail = async (funding) => {
    // 遍历数组
    let details = funding.map(async function(fundingAddress) {
        let fundingContractInstance = getFundingContractInstance()
        // 1.把Funding实例拿过来,并且为实例填充地址
        fundingContractInstance.options.address = fundingAddress
        // 2.调用方法获取Funding实例的详情
        let manager = await fundingContractInstance.methods.manager().call()
        let projectName = await fundingContractInstance.methods.projectName().call()
        let targetMoney = await fundingContractInstance.methods.targetMoney().call()
        let supportMoney = await fundingContractInstance.methods.supportMoney().call()
        let leftTime = await fundingContractInstance.methods.getLeftTime().call()
        // 3.创建结构体对象,封装合约信息
        let detail = {manager, projectName, targetMoney, supportMoney, leftTime}
        return detail
    })

    // 把多个Promise处理成一个Promise
    let detailsPromise = Promise.all(details)
    return detailsPromise
}

定义好方法之后,需要把该方法导出。

export {
    getFundingDetail
}

(3)修改CreatorFundingTab.js文件,在componentWillMount方法中调用上面方法获取合约详情,然后再把获取到的结果保存在state中。

import {getFundingDetail} from '../eth/interaction'

state = {
    // creatorFundings : [],
    creatorFundingDetail : []
}

async componentWillMount() {
    // 获取当前用户地址
    let accounts = await web3.eth.getAccounts()
    // 调用合约的getCreatorFunding方法,获取我发起的众筹合约,该方法返回一个合约数组
    let creatorFundings = await factoryContractInstance.methods.getCreatorFunding().call({
       from : accounts[0]
    })
    // 获取合约详情的Promise
    let creatorFundingDetail = getFundingDetail(creatorFundings)
    this.setState({
        // creatorFundings
        creatorFundingDetail
    })
}

 

四、搭建合约列表界面

(1)在display目录下新建common子目录,然后在子目录下新建CardList.js文件。

(2)在文件中定义一个CardList方法,该方法负责把details中的合约信息设置到Card组件中。关于semantic-ui的card组件的具体用法,请自行参考:https://react.semantic-ui.com/views/card/#variations-colored

import React from 'react'
import {Card, Image, Icon, List, Progress} from 'semantic-ui-react'

const src = '/images/elliot.jpg'

const CardList = (props) => {
    // 遍历众筹合约
    let details = props.details
    let cards = details.map(detail => {
        // 获取合约信息,并且设置到Card组件中
        return <CardExampleCard/>
    })
    return (
        <Card.Group itemsPerRow={4}>
            {cards}
        </Card.Group>
    )
}

const CardExampleCard = () => (
    <div>
        <Card>
            <Image src='/images/elliot.jpg'/>
            <Card.Content>
                <Card.Header>发动机</Card.Header>
                <Card.Meta>
                    <span>剩余时间:00秒</span>
                    <Progress indicating percent='50' size='small' progress/>
                </Card.Meta>
                <Card.Description>物超所值!!!</Card.Description>
            </Card.Content>
            <Card.Content extra>
                <List divided horizontal style={{display: 'flex', justifyContent: 'space-around'}}>
                    <List.Item>
                        <List.Content>
                            <List.Header>已筹</List.Header>
                            <List.Description>1000 eth</List.Description>
                        </List.Content>
                    </List.Item>
                    <List.Item>
                        <List.Content>
                            <List.Header>已达</List.Header>
                            <List.Description>50%</List.Description>
                        </List.Content>
                    </List.Item>
                    <List.Item>
                        <List.Content>
                            <List.Header>参与人数</List.Header>
                            <List.Description>500人</List.Description>
                        </List.Content>
                    </List.Item>
                </List>
            </Card.Content>
        </Card>
    </div>
)

export default CardList

(2)修改CreatorFundingTab.js文件,在rendor方法加载CardList组件,并且把creatorFundingDetail传递给CardList中。

import CardList from './common/CardList'

class CreatorFundingTab extends Component {

    render() {
        return (
            <div>
                <CardList details={this.state.creatorFundingDetail}/>
            </div>
        )
    }
}

(3)显示Card数据

第一步:修改interaction.js文件中的getCreatorFundingDetail方法,把findingAddress、balance、investorCount封装到结构体中对象中。

let getFundingDetail = (fundings) => {
    let details = fundings.map(async function(fundingAddress) {
        ...
        let balance = await fundingContractInstance.methods.getBalance().call()
        let investorCount = await fundingContractInstance.methods.getInvestorCount().call()
        // 3.创建结构体对象
        let detail = {
            fundingAddress,
            ...
            balance,
            investorCount}
        return detail
    })

    ...
}

第二步:修改CardList.js文件的CardList方法,把detail对象传给CardExampleCard中。

const CardList = (props) => {
    ...

    let cards = details.map(detail => {
        // 获取合约信息,并且设置到Card组件中
        return <CardExampleCard key={detail.fundingAddress} detail={detail}/>
    })

    ...
}

第三步:修改CardExampleCard方法,把detail中的数据结构出来。

// 解构出props.detail中的数据
let detail = props.detail
const {contractAddress, manager, projectName, targetMoney, supportMoney, leftTime, balance, investorCount} 

计算已众筹金额的百分比,以及把balance的单位转换成eth。

// 计算已筹金额的百分比
let percentage = (parseFloat(balance) / parseFloat(targetMoney)).toFixed(2) * 100
// 把balance单位转换成eth
let accountBalance = parseFloat(balance) / 10**18

最后把结构出来的数据填充到相应位置。

return (
        <div>
            <Card>
                <Image src='/images/elliot.jpg'/>
                <Card.Content>
                    <Card.Header>{projectName}</Card.Header>
                    <Card.Meta>
                        <span>剩余时间:{leftTime}秒</span>
                        <Progress indicating percent={percentage} size='small' progress/>
                    </Card.Meta>
                    <Card.Description>不容错过</Card.Description>
                </Card.Content>
                <Card.Content extra>
                    <List divided horizontal style={{display: 'flex', justifyContent: 'space-around'}}>
                        <List.Item>
                            <List.Content>
                                <List.Header>已筹</List.Header>
                                <List.Description>{accountBalance}eth</List.Description>
                            </List.Content>
                        </List.Item>
                        <List.Item>
                            <List.Content>
                                <List.Header>已达</List.Header>
                                <List.Description>{percentage}%</List.Description>
                            </List.Content>
                        </List.Item>
                        <List.Item>
                            <List.Content>
                                <List.Header>参与人数</List.Header>
                                <List.Description>{investorCount}人</List.Description>
                            </List.Content>
                        </List.Item>
                    </List>
                </Card.Content>
            </Card>
        </div>
    )

至此,我发起众筹界面的展示已经完成,效果如下图所示:

五、所有众筹和我参与过的众筹

所有众筹和我参与过的众筹的实现非常类似,所以AllFundingTab和SupportorFundingTab代码都是从CreatorFundingTab中复制过来稍作改动。代码如下:

(1)AllFundingTab.js的实现

import React, {Component} from 'react';
import {factoryContractInstance, fundingContractInstance} from '../eth/instance'
import {getFundingDetail} from '../eth/interaction'
import web3 from '../utils/InitWeb3'
import CardList from './common/CardList'

class AllFundingTab extends Component {

    state = {
        allFundingDetail : []
    }

    async componentWillMount() {
        // 获取当前用户地址
        let accounts = await web3.eth.getAccounts()
        // 调用合约方法,获取所有的众筹合约,该方法返回一个合约数组
        let allFundings = await factoryContractInstance.methods.getAllFundings().call({
            from : accounts[0]
        })
        // 获取合约详情的Promise
        let allFundingDetail = await getFundingDetail(allFundings)
        this.setState({
            allFundingDetail
        })
    }

    render() {
        return (
            <div>
                <CardList details={this.state.allFundingDetail}/>
            </div>
        )
    }
}

export default AllFundingTab

(2)SupportorFundingTab的实现

import React, {Component} from 'react';
import {factoryContractInstance, fundingContractInstance} from '../eth/instance'
import {getFundingDetail} from '../eth/interaction'
import web3 from '../utils/InitWeb3'
import CardList from './common/CardList'

class SupportorFundingTab extends Component {

    state = {
        supportorFundingDetail : []
    }

    async componentWillMount() {
        // 获取当前用户地址
        let accounts = await web3.eth.getAccounts()
        // 调用合约方法,获取我支持的众筹合约,该方法返回一个合约数组
        let allFundings = await factoryContractInstance.methods.getSupportorFunding().call({
            from : accounts[0]
        })
        // 获取合约详情的Promise
        let supportorFundingDetail = await getFundingDetail(allFundings)
        this.setState({
            supportorFundingDetail
        })
    }

    render() {
        return (
            <div>
                <CardList details={this.state.supportorFundingDetail}/>
            </div>
        )
    }
}

export default SupportorFundingTab

 

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值