好 上文web3 前端dapp从redux过滤出 (我创建与别人创建)正在执行的订单 并展示在Table上中 我们过滤出了 我创建的 与 别人创建的 且 未完成 未取消的订单数据
这边 我们起一下 ganache 环境
javascript
ganache -d
然后 我们项目 发布一下智能合约
javascript
truffle migrate --reset
然后 登录一下 MetaMask
在 运行一下测试脚本
javascript
truffle exec .\scripts\test.js
然后 打开我们的项目 在根目录下的 scripts 目录中 创建一个文件 我这里叫 createOrder.js
编写代码如下
javascript
//指定以token grtoken合约
const GrToken = artifacts.require("grToken.sol")
//交易所合约
const Exchange = artifacts.require("Exchange.sol")
//定义E代理地址
const ETHER_ADDRESS = '0x0000000000000000000000000000000000000000';
const toWei = (bn) => {
return web3.utils.toWei(bn.toString(), "ether");
}
module.exports = async function(callback) {
const grTokenDai = await GrToken.deployed();
const exchage = await Exchange.deployed();
//获取用户列表
const accounts = await web3.eth.getAccounts();
//循环用 accounts 用户列表中第一个用户创建 五个订单
for (let i = 1; i <= 5; i++) {
await exchage.makeOrder(grTokenDai.address,toWei(101), ETHER_ADDRESS ,toWei(0.01),{
from: accounts[0]
});
}
//循环用 accounts 用户列表中第二个用户创建 五个订单
for (let i = 1; i <= 5; i++) {
await exchage.makeOrder(grTokenDai.address,toWei(101), ETHER_ADDRESS ,toWei(0.01),{
from: accounts[1]
});
}
callback()
}
这里 我们直接脚本执行 第一和第二个用户 分别创建出 五个订单 方便 我们后期操作
然后 我们终端运行
javascript
truffle exec .\scripts\createOrder.js
然后 启动我们的dapp
这里 我们第一个用户 已经可用看到非常多订单信息了
然后 我们 MetaMask 切换一下当前账户
我们切换成第二个用户
然后刷新dapp
也是能看到自己的订单和第一个用户创建的订单
然后 我们将 src目录下的 components 下的 Order.jsx 组件代码更改如下
javascript
import React from 'react';
import { Card, Col, Row ,Table,Button } from 'antd';
import {useSelector} from "react-redux"
import moment from "moment"
function converTime(t){
return moment(t*1000).format("YYYY/MM/DD")
}
function convert(unit) {
return window.WebData ? unit&&window.WebData.web3.utils.fromWei(unit, "ether") : ""
}
function getRenderOrder(order,type) {
if(!window.WebData) {
return []
}
const account = window.WebData.account
//收集起所有 已完成 或 已取消的数据id
let filterIds = [...order.Cancelorders,...order.Fillorders].map(item=>item.id)
let makeorders = order.Allorders.filter(item=> !filterIds.includes(item.id))
if (type === 1) {
return makeorders.filter(item=>item.user === account)
} else {
return makeorders.filter(item=>item.user !== account)
}
}
export default function Order() {
const order = useSelector(state => state.order)
const columns = [
{
title: 'ETH',
dataIndex: 'amountGive',
render:(amountGive)=><b>{ convert(amountGive) }</b>,
key: 'amountGive'
},
{
title: 'GrToken',
dataIndex: 'amountGet',
render:(amountGet)=><b>{ convert(amountGet) }</b>,
key: 'amountGet'
},
{
title: '创建时间',
dataIndex: 'timestamp',
render:(timestamp)=><div>{ converTime(timestamp) }</div>,
key: 'timestamp'
},
];
const columns1 = [
...columns,
{
title: '操作',
render:(item)=><Button type = "text">取消</Button>
}
]
const columns2 = [
...columns,
{
title: '操作',
render:(item)=><Button type = "text">买入</Button>
}
]
return (
<div style = {{marginTop:'10px'}}>
<Row>
<Col span={8}>
<Card title="已完成" bordered={false} style = {{ margin: '10px' }}>
<Table dataSource={order.Fillorders} columns={columns} rowKey={item=> item.id}/>
</Card>
</Col>
<Col span={8}>
<Card title="我创建的" bordered={false} style = {{ margin: '10px' }}>
<Table dataSource={getRenderOrder(order,1)} columns={columns1} rowKey={item=> item.id}/>
</Card>
</Col>
<Col span={8}>
<Card title="其他交易中订单" bordered={false} style = {{ margin: '10px' }}>
<Table dataSource={getRenderOrder(order,2)} columns={columns2} rowKey={item=> item.id}/>
</Card>
</Col>
</Row>
</div>
);
}
这里 我们引入了 Button 按钮
然后 重新加了 两个 表头数据 columns1 和 columns2
他们都合并了 columns 原本的数据结果 然后多加了一列 叫操作 对应一个Button按钮
我创建的 叫取消
另一个叫 买入 买入别人创建的订单
然后 这里 我们先找到 antd 文档中的 对话框
首先 这里 我们需要 从react中引入 useState 然后从antd中 引入 Modal对话框组件
然后 定义一下 对话框开关 和当前操作的订单id 以及逻辑函数
javascript
//控制对话框的 布尔值
const [isModalOpen, setIsModalOpen] = useState(false);
//记录当前正在操作的订单id
const [currentId, setCurrentId] = useState(null);
//点击取消订单时触发
const triggerCancellation = (id) => {
setIsModalOpen(true);
setCurrentId(id);
}
//对话框点击 确定 时触发
const handleOk = () => {
setIsModalOpen(false);
};
//取消 或 关闭 对话框时触发
const handleCancel = () => {
setIsModalOpen(false);
};
然后 在元素中写出这个Modal对话框组件
最后 在取消订单的按钮 绑定上 onClick 开启对话框函数
item.id 表示 我们调用函数时 将当前订单的id 传进来
然后 我们运行项目 点击取消订单
对话框 就弹出来了
至于取消订单的具体逻辑 我们直接来看交易所代码 我们之前写了个cancelorder函数 用来取消订单 它只需要一个参数 订单的id 然后其他的具体逻辑 我们会自己调事件的
这里 我们先换回第一个账号吧
然后 我们将刚才在 src下 components 下的 Order.jsx中 的 handleOk编写代码如下
javascript
//对话框点击 确定 时触发
const handleOk = () => {
const {
account,
Exchange
} = window.WebData;
Exchange.methods.cancelorder(currentId).send({from: account})
setIsModalOpen(false);
};
这里 我们函数也写了注释了 他是只有用户点击确认之后才触发的 就表示 用户已经确定 要取消这个订单了
然后 我们通过我们存在window上的 WebData对象 拿到 交易所智能合约Exchange对象 和 account当前登录用户
然后通过Exchange 调用我们自己写的 取消订单 cancelorder函数 传入 我们之前存起来的 当前订单id currentId 然后 指定 send 因为 我们这个操作是要上链的
然后 我们传入当前用户为from 字段
然后 我们运行代码 点击取消订单
然后点击确定
然后 页面右侧 就会弹出这个操作提示按钮 因为这设计到了 燃料操作
我们直接点击确认
一路确认 最后完成之后 我们的数据并不会自动更新 因为我们没有订阅 但是 当你手动刷新界面 会发现 我们取消的那条 3ETH 的订单就没有了 因为已经被取消掉了
然后 我们也可以看MetaMask下面这个活动 会将你取消订单的动作记录下来
这样 就确认了 我们取消订单的功能 确实是好用的 然后 我们来看买入
我们先加上 逻辑层的一些控制对话框代码
javascript
//控制买入弹出开启
const [buy, setBuy] = useState(false);
//取消 或 关闭 对话框时触发
const buyCancel = () => {
setBuy(false);
};
//对话框点击 确定 时触发
const buyleOk = () => {
setIsModalOpen(false);
};
//点击买入订单时触发
const startBuying = (id) => {
setBuy(true);
setCurrentId(id);
}
然后 我们在元素层 加上 买入确定的对话框
这样 我们点击买入 提示弹窗就出来了
然后 我们交易所中 也写了一个 fillorder 函数 他也是 只需要一个参数id
通过id完成订单买入
这样 我们直接将 刚才写的 buyleOk 函数 改成这样就好了
javascript
//对话框点击 确定 时触发
const buyleOk = () => {
const {
account,
Exchange
} = window.WebData;
Exchange.methods.fillorder(currentId).send({from: account})
setBuy(false);
};
逻辑还是跟取消订单差不多 用户确定之后 从我们挂在 window上的WebData 对象中 结构出 当前用户和交易所合约对象
然后 我们通过交易所对象 调用fillorder函数 传入当前操作订单的id
他也会改变链上结构 要用send
当前用户为from字段
然后 我们运行代码
然后 我们记好 自己在交易所 ETH的数量 因为我们的grtoken存在燃料的概念 所以 可能有点额外扣除 我们就看ETH
然后 我们点击买入
然后我们选择确定
然后 界面右侧 会弹出操作提示 这里 我们点击确定
因为没有订阅 所以 我们界面并不会自动更新 但我们刷新界面 我们看三个点
当前用户 在交易所的 ETH 加了 0.01 然后 我们完成的订单多了一条 别人的订单 少了一条 还有就是 在交易所的 grToken 减少了
这就说明 我们操作成功了
最后 我们订单组件的代码是这样
typescript
import React, { useState } from 'react';
import { Card, Col, Row ,Table,Button, Modal } from 'antd';
import {useSelector} from "react-redux"
import moment from "moment"
function converTime(t){
return moment(t*1000).format("YYYY/MM/DD")
}
function convert(unit) {
return window.WebData ? unit&&window.WebData.web3.utils.fromWei(unit, "ether") : ""
}
function getRenderOrder(order,type) {
if(!window.WebData) {
return []
}
const account = window.WebData.account
//收集起所有 已完成 或 已取消的数据id
let filterIds = [...order.Cancelorders,...order.Fillorders].map(item=>item.id)
let makeorders = order.Allorders.filter(item=> !filterIds.includes(item.id))
if (type === 1) {
return makeorders.filter(item=>item.user === account)
} else {
return makeorders.filter(item=>item.user !== account)
}
}
export default function Order() {
//控制对话框的 布尔值
const [isModalOpen, setIsModalOpen] = useState(false);
//记录当前正在操作的订单id
const [currentId, setCurrentId] = useState(null);
//点击取消订单时触发
const triggerCancellation = (id) => {
setIsModalOpen(true);
setCurrentId(id);
}
//对话框点击 确定 时触发
const handleOk = () => {
const {
account,
Exchange
} = window.WebData;
Exchange.methods.cancelorder(currentId).send({from: account})
setIsModalOpen(false);
};
//取消 或 关闭 对话框时触发
const handleCancel = () => {
setIsModalOpen(false);
};
//控制买入弹出开启
const [buy, setBuy] = useState(false);
//取消 或 关闭 对话框时触发
const buyCancel = () => {
setBuy(false);
};
//对话框点击 确定 时触发
const buyleOk = () => {
const {
account,
Exchange
} = window.WebData;
Exchange.methods.fillorder(currentId).send({from: account})
setBuy(false);
};
//点击买入订单时触发
const startBuying = (id) => {
setBuy(true);
setCurrentId(id);
}
const order = useSelector(state => state.order)
const columns = [
{
title: 'ETH',
dataIndex: 'amountGive',
render:(amountGive)=><b>{ convert(amountGive) }</b>,
key: 'amountGive'
},
{
title: 'GrToken',
dataIndex: 'amountGet',
render:(amountGet)=><b>{ convert(amountGet) }</b>,
key: 'amountGet'
},
{
title: '创建时间',
dataIndex: 'timestamp',
render:(timestamp)=><div>{ converTime(timestamp) }</div>,
key: 'timestamp'
},
];
const columns1 = [
...columns,
{
title: '操作',
render:(item)=><Button type = "text" onClick={()=>{triggerCancellation(item.id)}}>取消</Button>
}
]
const columns2 = [
...columns,
{
title: '操作',
render:(item)=><Button type = "text" onClick={()=>{startBuying(item.id)}}>买入</Button>
}
]
return (
<div style = {{marginTop:'10px'}}>
<Row>
<Col span={8}>
<Card title="已完成" bordered={false} style = {{ margin: '10px' }}>
<Table dataSource={order.Fillorders} columns={columns} rowKey={item=> item.id}/>
</Card>
</Col>
<Col span={8}>
<Card title="我创建的" bordered={false} style = {{ margin: '10px' }}>
<Table dataSource={getRenderOrder(order,1)} columns={columns1} rowKey={item=> item.id}/>
</Card>
</Col>
<Col span={8}>
<Card title="其他交易中订单" bordered={false} style = {{ margin: '10px' }}>
<Table dataSource={getRenderOrder(order,2)} columns={columns2} rowKey={item=> item.id}/>
</Card>
</Col>
</Row>
<Modal
title="操作提示"
open={isModalOpen}
okText="确定"
cancelText="取消"
onOk={handleOk}
onCancel={handleCancel}
>
<p>您确定要取消当前订单吗?</p>
</Modal>
<Modal
title="操作提示"
open={buy}
okText="确定"
cancelText="取消"
onOk={buyleOk}
onCancel={buyCancel}
>
<p>您确认要买入当前订单吗?</p>
</Modal>
</div>
);
}
但是 目前体验比较差 因为我们操作完成 不会自动更新 需要手动刷新界面数据才会更新
因为 我们需要对这些事件进行订阅
那么 我们下文继续