forked from UzTech/Vue3-typescript-demo
update
This commit is contained in:
13
src/App.vue
13
src/App.vue
@@ -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;
|
||||
|
||||
@@ -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) => {
|
||||
|
||||
63
src/components/TimeFormat.vue
Normal file
63
src/components/TimeFormat.vue
Normal 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>
|
||||
@@ -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
|
||||
}
|
||||
|
||||
39
src/hooks/useGetMaxHeight.ts
Normal file
39
src/hooks/useGetMaxHeight.ts
Normal 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
|
||||
}
|
||||
}
|
||||
@@ -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[]
|
||||
|
||||
|
||||
82
src/types/block.d.ts
vendored
82
src/types/block.d.ts
vendored
@@ -4,6 +4,41 @@ export declare type AssetType = {
|
||||
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
|
||||
@@ -13,3 +48,50 @@ export declare type BlockOverview = {
|
||||
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
4
src/types/nav.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export declare type NavItem = {
|
||||
title: string
|
||||
route: string
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
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
|
||||
@@ -17,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
|
||||
}
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
<template>
|
||||
登录
|
||||
<button @click="onLogin">一键登录</button>
|
||||
<div class="container">
|
||||
登录
|
||||
<button @click="onLogin">一键登录</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
|
||||
@@ -99,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) }}
|
||||
@@ -112,14 +116,14 @@
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { block } from '@/api'
|
||||
import { Breadcrumb, Pagination } from '@/components'
|
||||
import { Breadcrumb, Pagination, TimeFormat } from '@/components'
|
||||
import { BlockOverview } from '@/types/block'
|
||||
import { filterHash, parseSymbol } from '@/utils/filters'
|
||||
import { onMounted, ref, watch } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
|
||||
const route = useRoute()
|
||||
const pageSize = 20
|
||||
const pageSize = Number(process.env.VUE_APP_BLOCK_DETAIL_LIST_SIZE)
|
||||
|
||||
onMounted(() => {
|
||||
loadBlockData()
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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
43
src/views/Nodes/index.vue
Normal 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>
|
||||
@@ -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
50
src/views/Token/index.vue
Normal 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>
|
||||
@@ -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>
|
||||
|
||||
13
src/views/Wallet/index.vue
Normal file
13
src/views/Wallet/index.vue
Normal file
@@ -0,0 +1,13 @@
|
||||
<template>
|
||||
Wallet
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'index'
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
Reference in New Issue
Block a user