Compare commits

...

2 Commits

Author SHA1 Message Date
65f5d164de update 2021-09-29 17:23:08 +08:00
aae5520459 update 2021-09-29 10:35:40 +08:00
19 changed files with 743 additions and 147 deletions

View File

@@ -1,12 +1,12 @@
<template>
<router-view v-slot="{ Component }">
<keep-alive :include="includeList">
<div id="layout">
<Header/>
<div id="layout">
<Header/>
<keep-alive :include="includeList">
<component :is="Component"></component>
<Footer/>
</div>
</keep-alive>
</keep-alive>
<Footer/>
</div>
</router-view>
</template>
@@ -100,6 +100,7 @@ a {
width: 16px;
height: 16px;
}
label {
width: 130px;
margin-left: 10px;

View File

@@ -9,12 +9,13 @@
</template>
<script lang="ts" setup>
import { NavItem } from '@/types/nav'
import { ref } from 'vue'
import { useRouter } from 'vue-router'
const router = useRouter()
// region 导航
const navList = ref([
const navList = ref<NavItem[]>([
{
title: '首页',
route: 'Home'
@@ -27,6 +28,14 @@ const navList = ref([
title: '查看数据',
route: 'Trade'
},
{
title: 'Token',
route: 'Token'
},
{
title: '节点',
route: 'Nodes'
},
{
title: '解析数据',
route: 'Analytical'
@@ -34,6 +43,10 @@ const navList = ref([
{
title: '广播数据',
route: 'Broadcast'
},
{
title: '我的钱包',
route: 'Wallet'
}
])
const linkTo = (name: string) => {

View File

@@ -0,0 +1,63 @@
<template>
{{ timeFormat }}
</template>
<script setup lang="ts">
import { timestampToTime } from '@/utils/filters'
import { defineProps, onBeforeUnmount, onMounted, ref } from 'vue'
const props = defineProps({
time: {
type: Number,
default: 0
}
})
const timeFormat = ref('Calculating...')
// eslint-disable-next-line no-undef
const timer = ref<NodeJS.Timeout | null>()
onMounted(() => {
calculateTime()
timer.value = setInterval(() => {
calculateTime()
}, 1000)
})
const calculateTime = () => {
if (props.time == 0) {
return ''
}
let Dt = new Date()
let local = Math.ceil(Dt.getTime() / 1000)
let dValue = local - props.time
if (dValue > 86400) {
timeFormat.value = timestampToTime(props.time)
clearInterval(Number(timer.value))
return
}
let h = Math.floor(dValue / 3600)
dValue -= h * 3600
let m = Math.floor(dValue / 60)
dValue -= m * 60
let s = dValue
if (local - props.time >= 3600) {
timeFormat.value = `${h}小时${m}${s}秒前`
} else if (local - props.time >= 60) {
timeFormat.value = `${m}${s}秒前`
} else {
timeFormat.value = `${s}秒前`
}
}
onBeforeUnmount(() => {
clearInterval(Number(timer.value))
})
</script>
<style scoped>
</style>

View File

@@ -4,6 +4,7 @@ import Footer from './Footer.vue'
import Header from './Header.vue'
import Nav from './Nav.vue'
import Pagination from './Pagination.vue'
import TimeFormat from './TimeFormat.vue'
export {
Banner,
@@ -11,5 +12,6 @@ export {
Footer,
Header,
Nav,
Pagination
Pagination,
TimeFormat
}

View File

@@ -0,0 +1,39 @@
import { block } from '@/api'
import vuex from '@/store'
import { ElMessage } from 'element-plus'
import { computed, onBeforeUnmount, onMounted, ref } from 'vue'
export default function () {
const maxHeight = computed(() => vuex.getters.maxHeight)
// eslint-disable-next-line no-undef
const interval = ref<NodeJS.Timeout | null>()
onMounted(() => {
interval.value = setInterval(() => {
getLastHeader()
}, 5000)
})
const getLastHeader = () => {
block.getLastHeader().then(res => {
if (res.error) {
clearInterval(Number(interval.value))
return ElMessage.error({
message: res.error,
offset: 300
})
} else if (maxHeight.value !== res.result.height) {
vuex.dispatch('setMaxHeight', res.result.height).then()
}
})
}
onBeforeUnmount(() => {
clearInterval(Number(interval.value))
})
return {
maxHeight
}
}

View File

@@ -10,7 +10,7 @@ export default [
requiresAuth: false,
showTabBar: true
},
component: () => import(/* webpackChunkName: "auth" */ '@/views/Block/index.vue')
component: () => import(/* webpackChunkName: "block" */ '@/views/Block/index.vue')
},
{
path: '/blocks/:hash',
@@ -21,7 +21,7 @@ export default [
requiresAuth: false,
showTabBar: true
},
component: () => import(/* webpackChunkName: "auth" */ '@/views/Block/detail.vue')
component: () => import(/* webpackChunkName: "block" */ '@/views/Block/detail.vue')
},
{
path: '/trades',
@@ -32,7 +32,7 @@ export default [
requiresAuth: false,
showTabBar: true
},
component: () => import(/* webpackChunkName: "auth" */ '@/views/Trade/index.vue')
component: () => import(/* webpackChunkName: "trade" */ '@/views/Trade/index.vue')
},
{
path: '/trades/:hash',
@@ -43,7 +43,7 @@ export default [
requiresAuth: false,
showTabBar: true
},
component: () => import(/* webpackChunkName: "auth" */ '@/views/Trade/detail.vue')
component: () => import(/* webpackChunkName: "trade" */ '@/views/Trade/detail.vue')
},
{
path: '/analytical',
@@ -54,7 +54,7 @@ export default [
requiresAuth: false,
showTabBar: true
},
component: () => import(/* webpackChunkName: "auth" */ '@/views/Other/analytical.vue')
component: () => import(/* webpackChunkName: "other" */ '@/views/Other/analytical.vue')
},
{
path: '/broadcast',
@@ -65,7 +65,7 @@ export default [
requiresAuth: false,
showTabBar: true
},
component: () => import(/* webpackChunkName: "auth" */ '@/views/Other/broadcast.vue')
component: () => import(/* webpackChunkName: "other" */ '@/views/Other/broadcast.vue')
},
{
path: '/address/:address',
@@ -76,7 +76,40 @@ export default [
requiresAuth: false,
showTabBar: true
},
component: () => import(/* webpackChunkName: "auth" */ '@/views/Address/index.vue')
}
component: () => import(/* webpackChunkName: "other" */ '@/views/Address/index.vue')
},
{
path: '/nodes',
name: 'Nodes',
meta: {
title: '节点列表',
keepAlive: true,
requiresAuth: false,
showTabBar: true
},
component: () => import(/* webpackChunkName: "other" */ '@/views/Nodes/index.vue')
},
{
path: '/tokens',
name: 'Token',
meta: {
title: 'Token',
keepAlive: true,
requiresAuth: false,
showTabBar: true
},
component: () => import(/* webpackChunkName: "other" */ '@/views/Token/index.vue')
},
{
path: '/wallet',
name: 'Wallet',
meta: {
title: '我的钱包',
keepAlive: false,
requiresAuth: true,
showTabBar: true
},
component: () => import(/* webpackChunkName: "wallet" */ '@/views/Wallet/index.vue')
},
] as MyRouteRecordRaw[]

92
src/types/block.d.ts vendored
View File

@@ -3,3 +3,95 @@ export declare type AssetType = {
exec: string
symbol: string
}
export declare type AddrOverview = {
balance: number
reciver: number
txCount: number
}
export declare type TokenAssetItem = {
symbol: string
account: {
addr: string
balance: string
currency: number
frozen: string
}
}
export declare type NodeItem = {
addr: string
name: string
header: {
height: number
blockTime: number
version: number
difficulty: number
hash: string
parentHash: string
stateHash: string
txCount: number
txHash: string
}
mempoolSize: number
port: number
self: boolean
}
export declare type BlockOverview = {
height: number
hash: string
txCount: number
txHash: string
parentHash: string
blockTime: number
stateHash: string
}
export declare type BlockItem = {
hash: string
height: number
txCount: number
blockTime: number
difficulty: number
parentHash: string
stateHash: string
txHash: string
version: number
}
export declare type BlockDetail = {
actionName: string
amount: number
assets: []
blockTime: number
fromAddr: string
fullHash: string
height: number
index: number
proofs: any
tx: {
hash: string
from: string
to: string
height: number
execer: string
expire: number
fee: number
feefmt: string
nonce: number
payload: any
rawPayload: string
signature: {
pubkey: string
signature: string
ty: number
}
}
receipt: {
logs: any
ty: number
tyName: string
}
}

4
src/types/nav.ts Normal file
View File

@@ -0,0 +1,4 @@
export declare type NavItem = {
title: string
route: string
}

View File

@@ -1,8 +1,11 @@
import { AssetType } from '@/types/block'
import store from '@/store'
import { AssetType } from '@/types/block'
export const filterHash = (str: string, num?: number): string => {
const length = num || 16
if (!str) {
return ''
}
return str.substr(0, length) + '...' + str.substr(-4)
}
@@ -14,3 +17,14 @@ export const parseSymbol = (assets?: AssetType[]): string => {
return store.getters.symbol
}
}
export const timestampToTime = (timestamp: number) => {
const date = new Date(timestamp * 1000)
const Y = date.getFullYear() + '-'
const M = (date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1) + '-'
const D = (date.getDate() < 10 ? '0' + date.getDate() : date.getDate()) + ' '
const h = (date.getHours() < 10 ? '0' + date.getHours() : date.getHours()) + ':'
const m = (date.getMinutes() < 10 ? '0' + date.getMinutes() : date.getMinutes()) + ':'
const s = date.getSeconds() < 10 ? '0' + date.getSeconds() : date.getSeconds()
return Y + M + D + h + m + s
}

View File

@@ -43,8 +43,16 @@
<div class="item">
<img src="../../assets/dots/pink.png">
<label>Tokens</label>
<el-select v-model="token" filterable placeholder="Select">
<el-option label="item.label" value="item.value" />
<el-select v-model="token" filterable placeholder="所有资产">
<el-option
v-for="(item, index) in assets"
:key="index"
:label="item.symbol"
:value="item.symbol"
>
<span style="float: left">{{ item.symbol }}</span>
<span style="float: right;">{{ (Number(item.account.balance) / 1e8).toFixed(2) }}</span>
</el-option>
</el-select>
</div>
</div>
@@ -113,7 +121,11 @@
{{ scope.row.tx.feefmt }} {{ parseSymbol(scope.row.assets) }}
</template>
</el-table-column>
<el-table-column width="180" prop="blockTime" label="上链时间" align="center"/>
<el-table-column prop="" label="上链时间" width="165" align="center">
<template #default="scope">
<TimeFormat :time="scope.row.blockTime"/>
</template>
</el-table-column>
</el-table>
</Pagination>
</div>
@@ -121,8 +133,9 @@
<script lang="ts" setup>
import { block } from '@/api'
import { Breadcrumb, Pagination } from '@/components'
import { Breadcrumb, Pagination, TimeFormat } from '@/components'
import { useStore } from '@/store'
import { AddrOverview, BlockDetail, TokenAssetItem } from '@/types/block'
import { filterHash, parseSymbol } from '@/utils/filters'
import { DArrowLeft, DArrowRight, Warning } from '@element-plus/icons'
import { computed, ref } from 'vue'
@@ -131,13 +144,8 @@ import { useRoute } from 'vue-router'
const store = useStore()
const route = useRoute()
const address = route.params.address as string
const pageSize = 10
const pageSize = Number(process.env.VUE_APP_BLOCK_DETAIL_LIST_SIZE)
type AddrOverview = {
balance: number
reciver: number
txCount: number
}
const balance = ref<AddrOverview>({
balance: 0,
reciver: 0,
@@ -164,6 +172,7 @@ block.getAllExecBalance(address).then(res => {
}
})
const records = ref<BlockDetail[]>([])
/**
* 获取全部交易
*/
@@ -175,20 +184,28 @@ block.getTxByAddr({
height: -1,
index: 1
}).then(res => {
let hashes = res.result.txInfos.map((item: { hash: string }) => item.hash)
if (res.error == null) {
let hashes = res.result.txInfos.map((item: { hash: string }) => item.hash)
block.getTxByHashes(hashes).then(res => {
records.value = res.result.txs
})
block.getTxByHashes(hashes).then(res => {
records.value = res.result.txs
})
}
})
const records = ref([])
const handleCurrentChange = (e: number) => {
console.log(e)
}
const token = ref('')
const token = ref<string>('')
const assets = ref<TokenAssetItem[]>([])
block.getAddrTokenAssets(address, 'token').then(res => {
if (res.error == null) {
console.log(res)
assets.value = res.result.tokenAssets
}
})
</script>
<style scoped lang="less">

View File

@@ -1,6 +1,8 @@
<template>
登录
<button @click="onLogin">一键登录</button>
<div class="container">
登录
<button @click="onLogin">一键登录</button>
</div>
</template>
<script lang="ts" setup>

View File

@@ -28,7 +28,14 @@
<div class="item">
<img src="../../assets/dots/green.png">
<label>上个区块</label>
<span>{{ (info.height - 1) > 0 ? info.height - 1 : '无' }}</span>
<span v-if="info.height -1 > 0">
<router-link :to="{name: 'BlockDetail', params: {hash: info.parentHash}}">
{{ info.height - 1 }}
</router-link>
</span>
<span v-else>
</span>
</div>
</div>
<div class="right">
@@ -45,15 +52,22 @@
<div class="item">
<img src="../../assets/dots/red.png">
<label>下个区块</label>
<span>{{ (info.height + 1) > maxHeight ? '无' : (info.height + 1) }}</span>
<span v-if="next.height">
<router-link :to="{name: 'BlockDetail', params: {hash: next.hash}}">
{{ next.height }}
</router-link>
</span>
<span v-else>
</span>
</div>
</div>
</div>
</div>
<Pagination
:length="maxHeight"
:title="`全部区块(` + maxHeight + `)`"
:length="info.txCount"
:title="`交易记录(` + info.txCount + `)`"
:page-size="pageSize"
@current-change="handleCurrentChange"
>
@@ -85,7 +99,11 @@
</el-table-column>
<el-table-column width="100" prop="amount" label="交易量" align="center"/>
<el-table-column width="100" prop="tx.feefmt" label="手续费" align="center"/>
<el-table-column width="180" prop="blockTime" label="上链时间" align="center"/>
<el-table-column prop="" label="上链时间" width="165" align="center">
<template #default="scope">
<TimeFormat :time="scope.row.blockTime"/>
</template>
</el-table-column>
<el-table-column width="100" label="交易资产" align="center">
<template #default="scope">
{{ parseSymbol(scope.row.assets) }}
@@ -98,37 +116,65 @@
<script lang="ts" setup>
import { block } from '@/api'
import { Breadcrumb, Pagination } from '@/components'
import { useStore } from '@/store'
import { Breadcrumb, Pagination, TimeFormat } from '@/components'
import { BlockOverview } from '@/types/block'
import { filterHash, parseSymbol } from '@/utils/filters'
import { computed, ref } from 'vue'
import { onMounted, ref, watch } from 'vue'
import { useRoute } from 'vue-router'
const route = useRoute()
const store = useStore()
const pageSize = 20
const pageSize = Number(process.env.VUE_APP_BLOCK_DETAIL_LIST_SIZE)
const maxHeight = computed<number>(() => store.getters.maxHeight)
onMounted(() => {
loadBlockData()
})
watch(route, (to) => {
if (to.name === 'BlockDetail') {
loadBlockData()
}
})
const hash: string = route.params.hash as string
const currentPage = ref<number>(1)
const handleCurrentChange = (e: number) => {
console.log(e)
}
const info = ref({})
const info = ref<BlockOverview>({
height: 0,
hash: '',
txCount: 0,
txHash: '',
parentHash: '',
blockTime: 0,
stateHash: ''
})
const next = ref({
height: 0,
hash: ''
})
const records = ref([])
block.getBlockOverview(hash).then(res => {
console.log(res)
const loadBlockData = () => {
console.log('jiazai shuju ')
info.value = res.result.head
block.getBlockOverview(hash).then(res => {
console.log('更新INFO A ')
info.value = res.result.head
block.getTxByHashes(res.result.txHashes).then(txs => {
records.value = txs.result.txs
block.getTxByHashes(res.result.txHashes).then(txs => {
records.value = txs.result.txs
})
block.getBlockHash(res.result.head.height + 1).then(nxt => {
if (nxt.error == null) {
next.value.height = res.result.head.height + 1
next.value.hash = nxt.result.hash
}
})
})
})
}
</script>
<style scoped lang="less">

View File

@@ -8,7 +8,11 @@
:page-size="pageSize"
@current-change="handleCurrentChange"
>
<el-table :data="blockList" stripe>
<el-table
:data="blockList"
stripe
v-loading="loading"
>
<template #empty>
<el-empty description="暂无数据"></el-empty>
</template>
@@ -29,52 +33,67 @@
</template>
</el-table-column>
<el-table-column prop="txCount" label="数据量" width="100" align="center"/>
<el-table-column prop="blockTime" label="上链时间" width="180" align="center"/>
<el-table-column prop="" label="上链时间" width="165" align="center">
<template #default="scope">
<TimeFormat :time="scope.row.blockTime"/>
</template>
</el-table-column>
</el-table>
</Pagination>
</div>
</template>
<script lang="ts">
export default {
name: 'Block'
}
</script>
<script lang="ts" setup>
import { block } from '@/api'
import { Breadcrumb,Pagination } from '@/components'
import { useStore } from '@/store'
import { computed, onBeforeUnmount, ref } from 'vue'
import { Breadcrumb, Pagination, TimeFormat } from '@/components'
import useGetMaxHeight from '@/hooks/useGetMaxHeight'
import { computed, onMounted, ref, watch } from 'vue'
const store = useStore()
const maxHeight = computed(() => store.getters.maxHeight)
const { maxHeight } = useGetMaxHeight()
const pageSize = Number(process.env.VUE_APP_BLOCK_LIST_SIZE)
const currentPage = ref<number>(1)
const loading = ref<boolean>(false)
const start = computed(() => {
return (maxHeight.value - pageSize) + ((1 - currentPage.value) * pageSize)
})
const end = computed(() => {
return start.value + pageSize
})
const start = maxHeight.value - 19
const blockList = ref([])
// eslint-disable-next-line no-undef
const interval = ref<NodeJS.Timeout | null>()
onMounted(() => {
getBlockList()
})
interval.value = setInterval(() => {
block.getLastHeader().then(res => {
if (maxHeight.value !== res.result.height) {
store.dispatch('setMaxHeight', res.result.height)
} else {
console.log('列表', maxHeight.value, res.result.height)
}
watch(maxHeight, (newValue, oldValue) => {
if (newValue != oldValue) {
getBlockList()
}
})
const getBlockList = () => {
loading.value = true
block.getHeaders(start.value > 0 ? start.value + 1 : 1, end.value, false).then(res => {
blockList.value = []
blockList.value = res.result.items.reverse()
}).finally(() => {
loading.value = false
})
}, 5000)
onBeforeUnmount(() => {
clearInterval(Number(interval.value))
})
block.getHeaders(start, maxHeight.value, false).then(res => {
blockList.value = res.result.items.reverse()
})
const currentPage = ref<number>(1)
const pageSize = ref<number>(20)
const handleCurrentChange = () => {
console.log(currentPage.value)
}
const handleCurrentChange = (e: number) => {
currentPage.value = e
getBlockList()
}
</script>
<style scoped lang="less">

View File

@@ -7,7 +7,7 @@
<router-link :to="{name: 'Block'}">查看全部</router-link>
</div>
<div class="items">
<div class="items" v-loading="blockLoading">
<div class="item" v-for="(item, index) in blockList" :key="index">
<div class="height" @click="router.push({name: 'BlockDetail', params: {hash: item.hash}})">
{{ item.height }}
@@ -20,13 +20,13 @@
</div>
<div class="data">
<span class="num">{{ item.txCount }} 笔交易</span>
<span class="time">出块时间{{ item.blockTime }}</span>
<span class="time">出块时间<TimeFormat :time="item.blockTime"/></span>
</div>
</div>
</div>
</div>
</div>
<div class="trades">
<div class="head">
<h1>最新交易</h1>
@@ -45,58 +45,59 @@
</div>
</template>
<script lang="ts">
export default {
name: 'Home'
}
</script>
<script lang="ts" setup>
import { block } from '@/api'
import { Banner } from '@/components'
import { useStore } from '@/store'
import { Banner, TimeFormat } from '@/components'
import useGetMaxHeight from '@/hooks/useGetMaxHeight'
import { BlockItem } from '@/types/block'
import { filterHash } from '@/utils/filters'
import { computed, onBeforeUnmount, onMounted, ref } from 'vue'
import { onMounted, ref, watch } from 'vue'
import { useRouter } from 'vue-router'
const store = useStore()
const router = useRouter()
const maxHeight = computed(() => store.getters.maxHeight)
const { maxHeight } = useGetMaxHeight()
const pageSize = Number(process.env.VUE_APP_HOME_LIST_SIZE)
const blockLoading = ref<boolean>(true)
onMounted(() => {
getLastHeader()
getBlockList()
})
// eslint-disable-next-line no-undef
const interval = ref<NodeJS.Timeout | null>()
interval.value = setInterval(() => {
getLastHeader()
}, 5000)
const getLastHeader = () => {
block.getLastHeader().then(res => {
if (maxHeight.value !== res.result.height) {
store.dispatch('setMaxHeight', res.result.height)
getBlockList()
} else {
console.log('首页', maxHeight.value, res.result.height)
}
})
}
onBeforeUnmount(() => {
clearInterval(Number(interval.value))
watch(maxHeight, (newValue, oldValue) => {
if (newValue != oldValue) {
getBlockList()
}
})
type HomeBlock = {
hash: string,
height: number,
txCount: number,
blockTime: number
}
const blockList = ref<HomeBlock[]>([])
const blockList = ref<BlockItem[]>([])
const tradeList = ref([])
if (blockList.value.length === 0) {
let initBlock = {
height: 0,
hash: 'x',
txCount: 0,
blockTime: 0
}
for (let i = 0; i < 6; i++) {
blockList.value.push(initBlock)
}
}
const getBlockList = () => {
const start = maxHeight.value - 5
const start = maxHeight.value - pageSize + 1 > 0 ? maxHeight.value - pageSize + 1 : 0
block.getHeaders(start, maxHeight.value, false).then(res => {
blockList.value = res.result.items.reverse()
}).finally(() => {
blockLoading.value = false
})
// block.getBlocks(start, maxHeight.value, false).then(res => {
// res.result.items.reverse().forEach((item) => {

43
src/views/Nodes/index.vue Normal file
View File

@@ -0,0 +1,43 @@
<template>
<div class="container">
<Breadcrumb :path="[{name: '节点列表'}]"/>
<el-table :data="peers" stripe border>
<template #empty>
<el-empty description="暂无数据"></el-empty>
</template>
<el-table-column prop="addr" width="200" label="节点地址"/>
<el-table-column prop="name" label="节点名称"/>
<el-table-column width="100" prop="header.height" label="当前高度" align="center"/>
<el-table-column prop="" label="最新出块时间" width="165" align="center">
<template #default="scope">
<TimeFormat :time="scope.row.header.blockTime"/>
</template>
</el-table-column>
</el-table>
</div>
</template>
<script lang="ts">
export default {
name: 'Nodes'
}
</script>
<script lang="ts" setup>
import { block } from '@/api'
import { Breadcrumb, TimeFormat } from '@/components'
import { NodeItem } from '@/types/block'
import { ref } from 'vue'
const peers = ref<NodeItem[]>([])
block.getPeerInfo().then(res => {
peers.value = res.result.peers
})
</script>
<style scoped>
</style>

View File

@@ -8,9 +8,7 @@
您可以在此页面输入签过名的源数据十六进制格式文本并将其广播到区块链网络
</div>
</div>
<div>
<textarea v-model="data"></textarea>
</div>
<textarea v-model="data"></textarea>
<el-button type="primary" :disabled="disabled" :loading="loading" @click="decodeTransaction">广播数据</el-button>
</div>
</template>
@@ -67,6 +65,7 @@ textarea {
border-radius: 5px;
border: 2px solid #ebeff1;
font-size: 14px;
margin-bottom: 16px;
&:focus {
outline: none;

50
src/views/Token/index.vue Normal file
View File

@@ -0,0 +1,50 @@
<template>
<div class="container">
<Breadcrumb :path="[{name: 'Token'}]"/>
<el-table :data="tokens" stripe border>
<template #empty>
<el-empty description="暂无数据"></el-empty>
</template>
<el-table-column prop="symbol" width="100" label="TOKEN符号"/>
<el-table-column prop="name" label="TOKEN名称"/>
<el-table-column width="200" label="所有者地址">
<template #default="scope">
<router-link :to="{name: 'Address', params: { address: scope.row.owner }}">
{{ filterHash(scope.row.owner) }}
</router-link>
</template>
</el-table-column>
<el-table-column prop="total" label="发行数量"/>
<el-table-column label="发行时间" width="165" align="center">
<template #default="scope">
<TimeFormat :time="Number(scope.row.createdTime)"/>
</template>
</el-table-column>
</el-table>
</div>
</template>
<script lang="ts">
export default {
name: 'Token'
}
</script>
<script lang="ts" setup>
import { block } from '@/api'
import { Breadcrumb, TimeFormat } from '@/components'
import { filterHash } from '@/utils/filters'
import { onMounted, ref } from 'vue'
const tokens = ref([])
block.queryAllTokens().then(res => {
tokens.value = res.result.tokens
})
</script>
<style scoped>
</style>

View File

@@ -9,46 +9,191 @@
</div>
</div>
<div>
<div>发送方{{ detail.tx.from }}</div>
<div>接收方{{ detail.tx.to }}</div>
<div>上链时间{{ detail.blockTime }}</div>
<div>资产{{ detail.amount }}</div>
<div>GAS费{{ detail.tx.feefmt }}</div>
<div>随机数{{ detail.tx.nonce }}</div>
<div>执行器{{ detail.tx.execer }}</div>
<div>函数{{ detail.actionName }}</div>
<div>类型{{ detail.index }}</div>
<div>
输入数据
<pre contenteditable="true" >
<code>{{ detail.receipt.logs }}</code>
</pre>
<div class="data">
<div class="head">
<span>概况</span>
</div>
<div class="item">
<label>发送方</label>
<div>
<router-link v-if="detail.tx.from" :to="{name: 'Address', params: {address: detail.tx.from}}">
{{ detail.tx.from }}
</router-link>
</div>
</div>
<div class="item">
<label>接收方</label>
<div>
<router-link v-if="detail.tx.to" :to="{name: 'Address', params: {address: detail.tx.to}}">
{{ detail.tx.to }}
</router-link>
</div>
</div>
<div class="item">
<label>区块高度</label>
<div>
<router-link v-if="blockHash" :to="{name: 'BlockDetail', params: {hash: blockHash}}">
{{ detail.height }}
</router-link>
</div>
</div>
<div class="item">
<label>上链时间</label>
<div>
<TimeFormat :time="detail.blockTime"/>
</div>
</div>
<div class="item">
<label>资产</label>
<div>{{ detail.amount }} {{ parseSymbol(detail.assets) }}</div>
</div>
<div class="item">
<label>GAS费</label>
<div>{{ detail.tx.feefmt }} {{ parseSymbol(detail.assets) }}
</div>
</div>
<div class="item">
<label>随机数</label>
<div>{{ detail.tx.nonce }}</div>
</div>
<div class="item">
<label>执行器</label>
<div>{{ detail.tx.execer }}</div>
</div>
<div class="item">
<label>函数</label>
<div>{{ detail.actionName }}</div>
</div>
<div class="item">
<label>类型</label>
<div>{{ detail.index }}</div>
</div>
<div class="item">
<label>输入数据</label>
<div>
<pre contenteditable="true" class="code">
<code>{{ detail.tx.payload }}</code>
</pre>
</div>
</div>
<div class="item">
<label>输出数据</label>
<div>
<pre contenteditable="true" class="code">
<code>{{ detail.receipt }}</code>
</pre>
</div>
</div>
<hr>
输出数据
<pre contenteditable="true" class="inputTxt">
<code>{{ detail.receipt.logs }}</code>
</pre>
</div>
</div>
</template>
<script lang="ts" setup>
import { block } from '@/api'
import { Breadcrumb } from '@/components'
import { Breadcrumb, TimeFormat } from '@/components'
import { BlockDetail } from '@/types/block'
import { parseSymbol } from '@/utils/filters'
import { ref } from 'vue'
import { useRoute } from 'vue-router'
const route = useRoute()
const detail = ref({})
const detail = ref<BlockDetail>({
tx: {
hash: '',
from: '',
to: '',
feefmt: '',
nonce: 0,
execer: '',
payload: {}
}
} as BlockDetail)
const blockHash = ref('')
block.queryTransaction(route.params.hash as string).then(res => {
console.log(res)
detail.value = res.result
block.getBlockHash(res.result.height).then(res => {
blockHash.value = res.result.hash
})
})
</script>
<style scoped lang="less">
.height {
h2 {
font-size: 24px;
padding: 8px 0;
}
div {
color: #9ea2a9;
vertical-align: middle;
button {
margin-left: 8px;
}
}
}
.data {
margin: 16px 0 32px;
width: 100%;
color: #516379;
font-size: 14px;
background: #fff;
border-radius: 2px;
border: 1px solid #ebeff1;
.head {
height: 70px;
border-bottom: 1px solid #eee;
span {
display: inline-block;
height: 100%;
width: 40px;
margin-left: 32px;
line-height: 67px;
color: #516379;
font-size: 16px;
font-weight: 500;
text-align: center;
border-bottom: 3px solid #6368de;
}
}
.item {
min-height: 60px;
display: flex;
align-items: center;
&:nth-child(even) {
background: #f9fafc;
}
label {
width: 200px;
padding-left: 32px;
}
div {
width: 998px;
padding: 16px;
.code {
border: 1px solid #ebeff1;
cursor: default;
height: 200px;
padding: 15px;
overflow-y: auto;
vertical-align: middle;
width: 100%;
background: #FFFFFF;
word-break: break-all;
}
}
}
}
</style>

View File

@@ -0,0 +1,13 @@
<template>
Wallet
</template>
<script>
export default {
name: 'index'
}
</script>
<style scoped>
</style>