Compare commits
10 Commits
65f5d164de
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 529f4360d7 | |||
| 896a3be236 | |||
| de7d9b29b2 | |||
| d03cef9685 | |||
| 4e1ca61828 | |||
| 7785e2bd5a | |||
| 9ca6c78f66 | |||
| d4c4605d7e | |||
|
|
4b2ce62cea | ||
| 33dc44c4c7 |
15
.env.example
@@ -1,10 +1,19 @@
|
||||
NODE_ENV=production
|
||||
NODE_ENV=development
|
||||
BASE_URL=/
|
||||
|
||||
VUE_APP_VUEX_KEY=vuex
|
||||
VUE_APP_VUEX_KEY=ex_vuex
|
||||
|
||||
VUE_APP_TITLE=Jason.Chen
|
||||
VUE_APP_PAGE_TITLE="ZH Explorer"
|
||||
VUE_APP_PAGE_COPYRIGHT="@2021 域展科技 All Rights Reserved.Powered by 哈尔滨域展科技有限公司 黑ICP备19007143号-2"
|
||||
VUE_APP_TITLE=联盟链浏览器
|
||||
VUE_APP_API_URL=/api
|
||||
VUE_APP_BLOCK_URL=https://explorer.lianshang.vip/api
|
||||
|
||||
VUE_APP_MAIN_COIN_SYMBOL=XHC
|
||||
|
||||
VUE_APP_USERNAME=
|
||||
VUE_APP_PASSWORD=
|
||||
|
||||
VUE_APP_HOME_LIST_SIZE=6
|
||||
VUE_APP_BLOCK_LIST_SIZE=20
|
||||
VUE_APP_BLOCK_DETAIL_LIST_SIZE=10
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"name": "league-explorer",
|
||||
"license": "MIT",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
@@ -12,6 +13,7 @@
|
||||
"@33cn/chain33-rpc-api": "^1.5.29",
|
||||
"@element-plus/icons": "^0.0.11",
|
||||
"axios": "^0.21.4",
|
||||
"clipboard": "^2.0.8",
|
||||
"element-plus": "^1.1.0-beta.16",
|
||||
"vue": "^3.2.11",
|
||||
"vue-router": "^4.0.11",
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
import auth from './interfaces/auth'
|
||||
import user from './interfaces/user'
|
||||
import block from './interfaces/block'
|
||||
import esdb from './interfaces/esdb'
|
||||
import user from './interfaces/user'
|
||||
|
||||
export {
|
||||
auth,
|
||||
user,
|
||||
block
|
||||
block,
|
||||
esdb,
|
||||
user
|
||||
}
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
import Chain33Rpc from '@33cn/chain33-rpc-api'
|
||||
|
||||
export default new Chain33Rpc('http://47.100.214.15:8080/api', null)
|
||||
export default new Chain33Rpc(process.env.VUE_APP_BLOCK_URL as string, null)
|
||||
|
||||
46
src/api/interfaces/esdb.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
import { AuthResponse } from '@/types/auth'
|
||||
import axios, { AxiosRequestConfig } from 'axios'
|
||||
|
||||
const request = axios.create({
|
||||
baseURL: process.env.VUE_APP_API_URL as string + process.env.VUE_APP_ESDB_URL as string,
|
||||
timeout: 5000
|
||||
})
|
||||
const axiosConf = (config: AxiosRequestConfig) => {
|
||||
config.headers.Accept = 'application/json'
|
||||
return config
|
||||
}
|
||||
request.interceptors.response.use(async (response) => {
|
||||
if (response.data?.error !== null) {
|
||||
return Promise.reject(response.data.error)
|
||||
}
|
||||
return Promise.resolve(response.data?.result)
|
||||
}, () => {
|
||||
return Promise.reject('网络请求超时')
|
||||
})
|
||||
|
||||
request.interceptors.request.use(axiosConf, err => {
|
||||
return Promise.reject(err)
|
||||
})
|
||||
|
||||
const txList = (size?: number): Promise<AuthResponse> => {
|
||||
const body = {
|
||||
id: 1,
|
||||
method: 'Tx.TxList',
|
||||
params: [{
|
||||
'sort': [{
|
||||
'key': 'height',
|
||||
'ascending': false
|
||||
}],
|
||||
'page': {
|
||||
'number': 1,
|
||||
'size': size
|
||||
}
|
||||
}]
|
||||
}
|
||||
|
||||
return request.post('', body)
|
||||
}
|
||||
|
||||
export default {
|
||||
txList
|
||||
}
|
||||
BIN
src/assets/home/box.png
Normal file
|
After Width: | Height: | Size: 2.2 KiB |
BIN
src/assets/home/cicle-inner.png
Normal file
|
After Width: | Height: | Size: 2.2 KiB |
BIN
src/assets/home/cicle-out.png
Normal file
|
After Width: | Height: | Size: 2.6 KiB |
BIN
src/assets/home/left-line.png
Normal file
|
After Width: | Height: | Size: 720 B |
BIN
src/assets/home/line-left.gif
Normal file
|
After Width: | Height: | Size: 5.2 KiB |
BIN
src/assets/home/line-right.gif
Normal file
|
After Width: | Height: | Size: 3.3 KiB |
BIN
src/assets/home/right-line.png
Normal file
|
After Width: | Height: | Size: 763 B |
@@ -1,5 +1,16 @@
|
||||
<template>
|
||||
<div class="banner">
|
||||
<div class="left-trans">
|
||||
<div class="animation-area">
|
||||
<img src="../assets/home/line-right.gif" class="line-left" alt="">
|
||||
<img src="../assets/home/box.png" class="box-left" alt="">
|
||||
<div class="circle-area">
|
||||
<img src="../assets/home/cicle-out.png" class="left-out-cicle" alt="">
|
||||
<img src="../assets/home/cicle-inner.png" class="left-inner-cicle" alt="">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="middle">
|
||||
<div class="search-box">
|
||||
<h1>区块链浏览器</h1>
|
||||
@@ -15,6 +26,17 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="right-trans">
|
||||
<div class="animation-area flex-between-stright">
|
||||
<div class="circle-area">
|
||||
<img src="../assets/home/cicle-out.png" class="right-out-cicle" alt="">
|
||||
<img src="../assets/home/cicle-inner.png" class="right-inner-cicle" alt="">
|
||||
</div>
|
||||
<img src="../assets/home/box.png" class="box-right" alt="">
|
||||
<img src="../assets/home/line-left.gif" class="line-right" alt="">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -124,12 +146,107 @@ const onSearch = () => {
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
.left-trans,
|
||||
.middle,
|
||||
.right-trans {
|
||||
display: inline-block;
|
||||
height: 556px;
|
||||
}
|
||||
|
||||
.left-trans {
|
||||
width: 150px;
|
||||
background: url("../assets/home/left-line.png") no-repeat top 60px left;
|
||||
overflow: hidden;
|
||||
|
||||
.animation-area {
|
||||
position: relative;
|
||||
margin-top: 60px;
|
||||
height: 450px;
|
||||
float: right;
|
||||
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.box-left {
|
||||
margin-top: -40px;
|
||||
margin-left: -40px;
|
||||
animation: jump 2s ease-in-out 1s infinite alternate;
|
||||
}
|
||||
|
||||
.circle-area {
|
||||
position: relative;
|
||||
width: 91px;
|
||||
height: 91px;
|
||||
|
||||
.left-out-cicle {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
animation: clockwise 2.5s linear infinite;
|
||||
}
|
||||
|
||||
.left-inner-cicle {
|
||||
width: 70px;
|
||||
position: absolute;
|
||||
left: 10px;
|
||||
top: 11px;
|
||||
animation: anticlockwise 2.5s linear infinite;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.right-trans {
|
||||
width: 150px;
|
||||
background: url("../assets/home/right-line.png") no-repeat top 60px right;
|
||||
overflow: hidden;
|
||||
|
||||
.animation-area {
|
||||
position: relative;
|
||||
margin-top: 60px;
|
||||
height: 450px;
|
||||
float: left;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.box-right {
|
||||
margin-right: -30px;
|
||||
animation: jump 2s ease-in-out 0.1s infinite alternate;
|
||||
}
|
||||
|
||||
.circle-area {
|
||||
position: relative;
|
||||
width: 91px;
|
||||
height: 91px;
|
||||
|
||||
.right-out-cicle {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
animation: clockwise 2.5s linear infinite;
|
||||
}
|
||||
|
||||
.right-inner-cicle {
|
||||
width: 70px;
|
||||
position: absolute;
|
||||
left: 10px;
|
||||
top: 12px;
|
||||
animation: anticlockwise 2.5s linear infinite;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.middle {
|
||||
height: 556px;
|
||||
position: relative;
|
||||
width: 1000px;
|
||||
flex-shrink: 0;
|
||||
background: url("/assets/images/ball.png") center center no-repeat;
|
||||
background: url("../assets/ball.png") center center no-repeat;
|
||||
|
||||
.search-box {
|
||||
width: 700px;
|
||||
@@ -190,4 +307,32 @@ const onSearch = () => {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 动画区
|
||||
@keyframes clockwise {
|
||||
0% {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes anticlockwise {
|
||||
0% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
100% {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes jump {
|
||||
from {
|
||||
transform: translate(0, 0);
|
||||
}
|
||||
to {
|
||||
transform: translate(0, 15px);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
<template>
|
||||
<div class="footer ">
|
||||
<div class="wrap">
|
||||
@2021 域展科技 All Rights Reserved.Powered by 哈尔滨域展科技有限公司
|
||||
黑ICP备19007143号-2
|
||||
{{ copyright }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
|
||||
const copyright = process.env.VUE_APP_PAGE_COPYRIGHT;
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
|
||||
@@ -2,18 +2,19 @@
|
||||
<div class="header">
|
||||
<div class="wrap">
|
||||
<div class="logo" @click="router.push({name: 'Home'})">
|
||||
LOGO
|
||||
{{ title }}
|
||||
</div>
|
||||
<Nav/>
|
||||
<Nav />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { useRouter } from 'vue-router'
|
||||
import { Nav } from '@/components'
|
||||
import { Nav } from '@/components';
|
||||
import { useRouter } from 'vue-router';
|
||||
|
||||
const router = useRouter()
|
||||
const router = useRouter();
|
||||
const title = process.env.VUE_APP_PAGE_TITLE;
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
@@ -34,6 +35,7 @@ const router = useRouter()
|
||||
}
|
||||
|
||||
.logo {
|
||||
font-size: 32px;
|
||||
align-items: center;
|
||||
display: flex;
|
||||
margin-right: 32px;
|
||||
|
||||
@@ -24,18 +24,18 @@ const navList = ref<NavItem[]>([
|
||||
title: '查看区块',
|
||||
route: 'Block'
|
||||
},
|
||||
{
|
||||
title: '查看数据',
|
||||
route: 'Trade'
|
||||
},
|
||||
// {
|
||||
// title: '查看数据',
|
||||
// route: 'Trade'
|
||||
// },
|
||||
{
|
||||
title: 'Token',
|
||||
route: 'Token'
|
||||
},
|
||||
{
|
||||
title: '节点',
|
||||
route: 'Nodes'
|
||||
},
|
||||
// {
|
||||
// title: '节点',
|
||||
// route: 'Nodes'
|
||||
// },
|
||||
{
|
||||
title: '解析数据',
|
||||
route: 'Analytical'
|
||||
@@ -43,10 +43,6 @@ const navList = ref<NavItem[]>([
|
||||
{
|
||||
title: '广播数据',
|
||||
route: 'Broadcast'
|
||||
},
|
||||
{
|
||||
title: '我的钱包',
|
||||
route: 'Wallet'
|
||||
}
|
||||
])
|
||||
const linkTo = (name: string) => {
|
||||
|
||||
@@ -2,14 +2,16 @@
|
||||
<div class="records">
|
||||
<div class="head">
|
||||
<h2>{{ title }}</h2>
|
||||
<el-pagination
|
||||
background
|
||||
layout="total,prev,pager,next,jumper"
|
||||
:total="length"
|
||||
:page-size="pageSize"
|
||||
@current-change="handleCurrentChange"
|
||||
>
|
||||
</el-pagination>
|
||||
<slot name="page">
|
||||
<el-pagination
|
||||
background
|
||||
layout="total,prev,pager,next,jumper"
|
||||
:total="length"
|
||||
:page-size="pageSize"
|
||||
@current-change="handleCurrentChange"
|
||||
>
|
||||
</el-pagination>
|
||||
</slot>
|
||||
</div>
|
||||
|
||||
<slot name="default"></slot>
|
||||
|
||||
@@ -1,22 +1,36 @@
|
||||
import { block } from '@/api'
|
||||
import vuex from '@/store'
|
||||
import { TotalFee } from '@/types/block'
|
||||
import { hexCharCodeToStr } from '@/utils/filters'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { computed, onBeforeUnmount, onMounted, ref } from 'vue'
|
||||
import { computed, ComputedRef, onActivated, ref } from 'vue'
|
||||
import { onBeforeRouteLeave } from 'vue-router'
|
||||
|
||||
export default function () {
|
||||
const maxHeight = computed(() => vuex.getters.maxHeight)
|
||||
export default function (): {
|
||||
maxHeight: ComputedRef<number>;
|
||||
queryTotalFee: () => Promise<TotalFee>;
|
||||
lastHash: ComputedRef<string>;
|
||||
} {
|
||||
const maxHeight: ComputedRef<number> = computed(() => vuex.getters.maxHeight)
|
||||
const lastHash: ComputedRef<string> = computed(() => vuex.getters.lastHash)
|
||||
|
||||
// eslint-disable-next-line no-undef
|
||||
const interval = ref<NodeJS.Timeout | null>()
|
||||
|
||||
onMounted(() => {
|
||||
onActivated(() => {
|
||||
getLastHeader()
|
||||
console.log('开始轮询头信息')
|
||||
interval.value = setInterval(() => {
|
||||
getLastHeader()
|
||||
}, 5000)
|
||||
})
|
||||
|
||||
const getLastHeader = () => {
|
||||
/**
|
||||
* 获取最新的区块
|
||||
*/
|
||||
const getLastHeader = (): void => {
|
||||
block.getLastHeader().then(res => {
|
||||
console.log('获取最新区块', res.result.height)
|
||||
if (res.error) {
|
||||
clearInterval(Number(interval.value))
|
||||
return ElMessage.error({
|
||||
@@ -25,15 +39,26 @@ export default function () {
|
||||
})
|
||||
} else if (maxHeight.value !== res.result.height) {
|
||||
vuex.dispatch('setMaxHeight', res.result.height).then()
|
||||
vuex.dispatch('setLastHash', res.result.hash).then()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
/**
|
||||
* 查询从交易量和交易费
|
||||
*/
|
||||
const queryTotalFee = (): Promise<TotalFee> => {
|
||||
return block.queryTotalFee(hexCharCodeToStr(lastHash.value))
|
||||
}
|
||||
|
||||
onBeforeRouteLeave(() => {
|
||||
console.log('结束轮询')
|
||||
clearInterval(Number(interval.value))
|
||||
})
|
||||
|
||||
return {
|
||||
maxHeight
|
||||
maxHeight,
|
||||
lastHash,
|
||||
queryTotalFee
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,12 +5,14 @@ import App from './App.vue'
|
||||
import router from './router'
|
||||
import store, { key } from './store'
|
||||
import zhCn from 'element-plus/es/locale/lang/zh-cn'
|
||||
import directives from '@/utils/directives'
|
||||
|
||||
const app = createApp(App)
|
||||
|
||||
app.use(ElementPlus, {
|
||||
locale: zhCn,
|
||||
})
|
||||
app.use(directives)
|
||||
app.use(store, key)
|
||||
app.use(router)
|
||||
app.mount('#app')
|
||||
|
||||
@@ -99,17 +99,6 @@ export default [
|
||||
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[]
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ export interface State {
|
||||
openId: string
|
||||
loginAt: number
|
||||
maxHeight: number
|
||||
lastHash: string
|
||||
user: BaseInfo
|
||||
auth?: AuthState
|
||||
refresh?: RefreshState
|
||||
@@ -31,6 +32,7 @@ export default createStore<State>({
|
||||
openId: '',
|
||||
loginAt: 0,
|
||||
maxHeight: 0,
|
||||
lastHash: '',
|
||||
user: {} as BaseInfo
|
||||
},
|
||||
getters: {
|
||||
@@ -48,6 +50,9 @@ export default createStore<State>({
|
||||
},
|
||||
symbol: (): string => {
|
||||
return process.env.VUE_APP_MAIN_COIN_SYMBOL as string
|
||||
},
|
||||
lastHash: (state: State): string => {
|
||||
return state.lastHash
|
||||
}
|
||||
},
|
||||
mutations: {
|
||||
@@ -74,17 +79,23 @@ export default createStore<State>({
|
||||
},
|
||||
setMaxHeight: (state: State, height: number): void => {
|
||||
state.maxHeight = height
|
||||
},
|
||||
setLastHash: (state: State, hash: string): void => {
|
||||
state.lastHash = hash
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
setUserInfo: ({ commit }, info: BaseInfo): void => {
|
||||
setUserInfo: ({commit}, info: BaseInfo): void => {
|
||||
commit('setUserInfo', info)
|
||||
},
|
||||
setOpenId: ({ commit }, openId: string): void => {
|
||||
setOpenId: ({commit}, openId: string): void => {
|
||||
commit('setOpenId', openId)
|
||||
},
|
||||
setMaxHeight: ({ commit }, height: number): void => {
|
||||
setMaxHeight: ({commit}, height: number): void => {
|
||||
commit('setMaxHeight', height)
|
||||
},
|
||||
setLastHash: ({commit}, hash: string): void => {
|
||||
commit('setLastHash', hash)
|
||||
}
|
||||
},
|
||||
modules: {
|
||||
|
||||
20
src/types/block.d.ts
vendored
@@ -95,3 +95,23 @@ export declare type BlockDetail = {
|
||||
tyName: string
|
||||
}
|
||||
}
|
||||
|
||||
export declare type TradeItem = {
|
||||
txHash: string
|
||||
blockTime: number
|
||||
fromAddr: string
|
||||
amount: number
|
||||
assets: any
|
||||
tx: {
|
||||
to: string
|
||||
}
|
||||
}
|
||||
|
||||
export declare type TotalFee = {
|
||||
error: string | null
|
||||
id: number
|
||||
result: {
|
||||
fee: number
|
||||
txCount: number
|
||||
}
|
||||
}
|
||||
|
||||
22
src/utils/directives.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import { App, DirectiveBinding } from '@vue/runtime-core'
|
||||
import ClipboardJS from 'clipboard'
|
||||
import { ElMessage } from 'element-plus'
|
||||
|
||||
export default (Vue: App) => {
|
||||
Vue.directive('copy', {
|
||||
updated (el: Element, binding: DirectiveBinding) {
|
||||
const clipboard = new ClipboardJS(el, {
|
||||
text: () => {
|
||||
return binding.value
|
||||
}
|
||||
})
|
||||
|
||||
clipboard.on('error', (): void => {
|
||||
ElMessage.warning({
|
||||
message: '当前浏览器不支持复制'
|
||||
})
|
||||
clipboard.destroy()
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -18,7 +18,7 @@ export const parseSymbol = (assets?: AssetType[]): string => {
|
||||
}
|
||||
}
|
||||
|
||||
export const timestampToTime = (timestamp: number) => {
|
||||
export const timestampToTime = (timestamp: number): string => {
|
||||
const date = new Date(timestamp * 1000)
|
||||
const Y = date.getFullYear() + '-'
|
||||
const M = (date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1) + '-'
|
||||
@@ -28,3 +28,56 @@ export const timestampToTime = (timestamp: number) => {
|
||||
const s = date.getSeconds() < 10 ? '0' + date.getSeconds() : date.getSeconds()
|
||||
return Y + M + D + h + m + s
|
||||
}
|
||||
|
||||
export function bin2hex (str: string): string {
|
||||
let ret = ''
|
||||
const r = /[0-9a-zA-Z_.~!*()]/
|
||||
for (let i = 0, l = str.length; i < l; i++) {
|
||||
if (r.test(str.charAt(i))) {
|
||||
ret += str.charCodeAt(i).toString(16)
|
||||
console.log(ret)
|
||||
} else {
|
||||
ret += encodeURIComponent(str.charAt(i)).replace(/%/g, '')
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
export function hexCharCodeToStr (hexCharCodeStr: string): string[] {
|
||||
const trimedStr = hexCharCodeStr.trim()
|
||||
const rawStr = trimedStr.substr(0, 2).toLowerCase() === '0x' ? trimedStr.substr(2) : trimedStr
|
||||
const len = rawStr.length
|
||||
|
||||
let curCharCode
|
||||
const resultStr = []
|
||||
for (let i = 0; i < len; i = i + 2) {
|
||||
curCharCode = parseInt(rawStr.substr(i, 2), 16)
|
||||
resultStr.push(String.fromCharCode(curCharCode))
|
||||
}
|
||||
return [base64Encode(`TotalFeeKey:${resultStr.join('')}`)]
|
||||
}
|
||||
|
||||
const base64Encode = (input: string): string => {
|
||||
const _keyStr = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='
|
||||
let output = ''
|
||||
let chr1, chr2, chr3, enc1, enc2, enc3, enc4
|
||||
let i = 0
|
||||
while (i < input.length) {
|
||||
chr1 = input.charCodeAt(i++)
|
||||
chr2 = input.charCodeAt(i++)
|
||||
chr3 = input.charCodeAt(i++)
|
||||
enc1 = chr1 >> 2
|
||||
enc2 = ((chr1 & 3) << 4) | (chr2 >> 4)
|
||||
enc3 = ((chr2 & 15) << 2) | (chr3 >> 6)
|
||||
enc4 = chr3 & 63
|
||||
if (isNaN(chr2)) {
|
||||
enc3 = enc4 = 64
|
||||
} else if (isNaN(chr3)) {
|
||||
enc4 = 64
|
||||
}
|
||||
output = output +
|
||||
_keyStr.charAt(enc1) + _keyStr.charAt(enc2) +
|
||||
_keyStr.charAt(enc3) + _keyStr.charAt(enc4)
|
||||
}
|
||||
return output
|
||||
}
|
||||
|
||||
@@ -3,8 +3,12 @@
|
||||
<Breadcrumb :path="[{name: '地址信息'}]"/>
|
||||
|
||||
<div class="top-info">
|
||||
<span>地址</span>
|
||||
<span>地址:</span>
|
||||
<span class="addr">{{ address }}</span>
|
||||
<el-tag size="medium" v-if="isContract">合约地址</el-tag>
|
||||
<el-tag size="medium" type="info" v-else>普通地址</el-tag>
|
||||
|
||||
<el-button type="primary" v-copy="address" size="mini">复制</el-button>
|
||||
</div>
|
||||
|
||||
<div class="assets">
|
||||
@@ -16,24 +20,24 @@
|
||||
<div class="item">
|
||||
<img src="../../assets/dots/red.png">
|
||||
<label>余额</label>
|
||||
<span>{{ balance.balance.toFixed(2) }} {{ store.getters.symbol }}</span>
|
||||
<span>{{ balance.balance.toFixed(2) }} {{ symbol }}</span>
|
||||
</div>
|
||||
<div class="item">
|
||||
<img src="../../assets/dots/blue.png">
|
||||
<label>总接收</label>
|
||||
<span>{{ balance.reciver.toFixed(2) }} {{ store.getters.symbol }}</span>
|
||||
<span>{{ balance.reciver.toFixed(2) }} {{ symbol }}</span>
|
||||
</div>
|
||||
<div class="item">
|
||||
<img src="../../assets/dots/green.png">
|
||||
<label>总发送</label>
|
||||
<span>{{ sended.toFixed(2) }} {{ store.getters.symbol }}</span>
|
||||
<span>{{ sended.toFixed(2) }} {{ symbol }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="right">
|
||||
<div class="item">
|
||||
<img src="../../assets/dots/blue.png">
|
||||
<label>冻结</label>
|
||||
<span>{{ frozen.toFixed(2) }} {{ store.getters.symbol }}</span>
|
||||
<span>{{ frozen.toFixed(2) }} {{ symbol }}</span>
|
||||
</div>
|
||||
<div class="item">
|
||||
<img src="../../assets/dots/green.png">
|
||||
@@ -60,73 +64,94 @@
|
||||
</div>
|
||||
|
||||
<Pagination
|
||||
:length="balance.txCount"
|
||||
:title="`数据记录(` + balance.txCount + `)`"
|
||||
:page-size="pageSize"
|
||||
@current-change="handleCurrentChange"
|
||||
>
|
||||
<el-table :data="records" stripe class="table">
|
||||
<template #empty>
|
||||
<el-empty description="暂无数据"></el-empty>
|
||||
</template>
|
||||
<template #page>
|
||||
<el-pagination
|
||||
background
|
||||
:page-size="pageSize"
|
||||
layout="prev,next"
|
||||
:total="balance.txCount"
|
||||
@current-change="handleCurrentChange"
|
||||
>
|
||||
</el-pagination>
|
||||
</template>
|
||||
<template #default>
|
||||
<el-table :data="records" v-loading="loading" stripe class="table">
|
||||
<template #empty>
|
||||
<el-empty description="暂无数据"></el-empty>
|
||||
</template>
|
||||
|
||||
<el-table-column width="34" align="right">
|
||||
<template #default="scope">
|
||||
<el-icon v-if="scope.row.receipt.ty == 1">
|
||||
<Warning class="warning"/>
|
||||
</el-icon>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="height" label="高度"/>
|
||||
<el-table-column width="34" align="right">
|
||||
<template #default="scope">
|
||||
<el-icon v-if="scope.row.receipt.ty == 1">
|
||||
<Warning class="warning"/>
|
||||
</el-icon>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="交易哈希">
|
||||
<template #default="scope">
|
||||
<router-link :to="{name: 'TradeDetail', params: { hash: scope.row.txHash }}">
|
||||
{{ filterHash(scope.row.txHash, 32) }}
|
||||
</router-link>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column width="200" label="发送方">
|
||||
<template #default="scope">
|
||||
<router-link :to="{name: 'Address', params: { address: scope.row.fromAddr }}">
|
||||
{{ filterHash(scope.row.fromAddr) }}
|
||||
</router-link>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column width="200" label="交易哈希">
|
||||
<template #default="scope">
|
||||
<router-link :to="{name: 'TradeDetail', params: { hash: scope.row.txHash }}">
|
||||
{{ filterHash(scope.row.txHash, 16) }}
|
||||
</router-link>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column width="40" align="center">
|
||||
<template #default="scope">
|
||||
<el-icon v-if="scope.row.fromAddr == address">
|
||||
<DArrowRight/>
|
||||
</el-icon>
|
||||
<el-icon v-else>
|
||||
<DArrowLeft/>
|
||||
</el-icon>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column width="160" label="发送方">
|
||||
<template #default="scope">
|
||||
<router-link :to="{name: 'Address', params: { address: scope.row.fromAddr }}">
|
||||
{{ filterHash(scope.row.fromAddr, 10) }}
|
||||
</router-link>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column width="200" label="接收方">
|
||||
<template #default="scope">
|
||||
<router-link :to="{name: 'Address', params: { address: scope.row.tx.to }}">
|
||||
{{ filterHash(scope.row.tx.to) }}
|
||||
</router-link>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column width="100" label="交易量" align="center">
|
||||
<template #default="scope">
|
||||
{{ scope.row.amount }} {{ parseSymbol(scope.row.assets) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column width="100" label="手续费" align="center">
|
||||
<template #default="scope">
|
||||
{{ scope.row.tx.feefmt }} {{ parseSymbol(scope.row.assets) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="" label="上链时间" width="165" align="center">
|
||||
<template #default="scope">
|
||||
<TimeFormat :time="scope.row.blockTime"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<el-table-column width="40" align="center">
|
||||
<template #default="scope">
|
||||
<el-icon v-if="scope.row.fromAddr == address">
|
||||
<DArrowRight/>
|
||||
</el-icon>
|
||||
<el-icon v-else>
|
||||
<DArrowLeft/>
|
||||
</el-icon>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column width="160" label="接收方">
|
||||
<template #default="scope">
|
||||
<router-link :to="{name: 'Address', params: { address: scope.row.tx.to }}">
|
||||
{{ filterHash(scope.row.tx.to, 10) }}
|
||||
</router-link>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="交易量" align="center">
|
||||
<template #default="scope">
|
||||
{{ (scope.row.amount / 1e8).toFixed(4) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="手续费" align="center">
|
||||
<template #default="scope">
|
||||
{{ scope.row.tx.feefmt }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="调用函数" align="center">
|
||||
<template #default="scope">
|
||||
{{ scope.row.actionName }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column width="165" label="上链时间" align="center">
|
||||
<template #default="scope">
|
||||
<TimeFormat :time="scope.row.blockTime"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="交易资产" align="center">
|
||||
<template #default="scope">
|
||||
{{ parseSymbol(scope.row.assets) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</template>
|
||||
</Pagination>
|
||||
</div>
|
||||
</template>
|
||||
@@ -138,74 +163,117 @@ 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'
|
||||
import { computed, onMounted, reactive, ref, watch } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
|
||||
const store = useStore()
|
||||
const route = useRoute()
|
||||
const address = route.params.address as string
|
||||
const address = computed<string>(() => route.params.address as string)
|
||||
const pageSize = Number(process.env.VUE_APP_BLOCK_DETAIL_LIST_SIZE)
|
||||
|
||||
const symbol = computed<string>(() => store.getters.symbol)
|
||||
|
||||
const balance = ref<AddrOverview>({
|
||||
balance: 0,
|
||||
reciver: 0,
|
||||
txCount: 0
|
||||
})
|
||||
|
||||
/**
|
||||
* 获取地址的基本信息
|
||||
*/
|
||||
block.getAddrOverview(address).then(res => {
|
||||
balance.value.balance = res.result.balance ? res.result.balance / 1e8 : 0
|
||||
balance.value.reciver = res.result.reciver ? res.result.reciver / 1e8 : 0
|
||||
balance.value.txCount = res.result.txCount ? res.result.txCount : 0
|
||||
})
|
||||
|
||||
const sended = computed(() => balance.value.reciver - balance.value.balance)
|
||||
const frozen = ref<number>(0)
|
||||
/**
|
||||
* 获取冻结的主代币
|
||||
*/
|
||||
block.getAllExecBalance(address).then(res => {
|
||||
if (res.result.execAccount) {
|
||||
frozen.value = res.result.execAccount.find((item: { execer: string }) => item.execer == 'coins').account.frozen / 1e8
|
||||
const records = ref<BlockDetail[]>([])
|
||||
const token = ref<string>('')
|
||||
const assets = ref<TokenAssetItem[]>([])
|
||||
const loading = ref<boolean>(false)
|
||||
const isContract = ref<boolean>(false)
|
||||
|
||||
watch(route, (to) => {
|
||||
if (to.name == 'Address') {
|
||||
records.value = []
|
||||
initAddressData()
|
||||
}
|
||||
})
|
||||
|
||||
const records = ref<BlockDetail[]>([])
|
||||
/**
|
||||
* 获取全部交易
|
||||
*/
|
||||
block.getTxByAddr({
|
||||
addr: address,
|
||||
onMounted(() => {
|
||||
initAddressData()
|
||||
})
|
||||
|
||||
const initAddressData = () => {
|
||||
block.callPromiseAPI('Query', {
|
||||
execer: 'evm',
|
||||
funcName: 'CheckAddrExists',
|
||||
payload: {
|
||||
addr: address.value
|
||||
}
|
||||
}).then(res => {
|
||||
isContract.value = res.result.contract
|
||||
})
|
||||
/**
|
||||
* 获取地址的基本信息
|
||||
*/
|
||||
block.getAddrOverview(address.value).then(res => {
|
||||
balance.value.balance = res.result.balance ? res.result.balance / 1e8 : 0
|
||||
balance.value.reciver = res.result.reciver ? res.result.reciver / 1e8 : 0
|
||||
balance.value.txCount = res.result.txCount ? res.result.txCount : 0
|
||||
})
|
||||
/**
|
||||
* 获取冻结的主代币
|
||||
*/
|
||||
block.getAllExecBalance(address.value).then(res => {
|
||||
if (res.result.execAccount) {
|
||||
frozen.value = res.result.execAccount.find((item: { execer: string }) => item.execer == 'coins').account.frozen / 1e8
|
||||
}
|
||||
})
|
||||
|
||||
block.getAddrTokenAssets(address.value, 'token').then(res => {
|
||||
if (res.error == null) {
|
||||
console.log(res)
|
||||
assets.value = res.result.tokenAssets
|
||||
}
|
||||
})
|
||||
loadTradeList()
|
||||
}
|
||||
|
||||
const initParams = {
|
||||
addr: address.value,
|
||||
flag: 0,
|
||||
count: pageSize,
|
||||
direction: 0,
|
||||
height: -1,
|
||||
index: 1
|
||||
}).then(res => {
|
||||
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
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
index: 0
|
||||
}
|
||||
const pageParams = reactive(initParams)
|
||||
const handleCurrentChange = (e: number) => {
|
||||
console.log(e)
|
||||
if (e === 1) {
|
||||
pageParams.direction = 0
|
||||
pageParams.height = -1
|
||||
} else {
|
||||
pageParams.direction = 0
|
||||
pageParams.height = records.value[records.value.length - 1].height
|
||||
pageParams.index = records.value[records.value.length - 1].index
|
||||
}
|
||||
console.log(pageParams)
|
||||
records.value = []
|
||||
loadTradeList()
|
||||
}
|
||||
|
||||
const token = ref<string>('')
|
||||
const assets = ref<TokenAssetItem[]>([])
|
||||
const loadTradeList = () => {
|
||||
loading.value = true
|
||||
|
||||
block.getAddrTokenAssets(address, 'token').then(res => {
|
||||
if (res.error == null) {
|
||||
console.log(res)
|
||||
assets.value = res.result.tokenAssets
|
||||
}
|
||||
})
|
||||
block.getTxByAddr(pageParams).then(res => {
|
||||
if (res.error == null) {
|
||||
let hashes = res.result.txInfos.map((item: { hash: string }) => item.hash)
|
||||
|
||||
block.getTxByHashes(hashes).then(res => {
|
||||
console.log(' pageParams.height ', pageParams.height)
|
||||
records.value = res.result.txs
|
||||
}).finally(() => {
|
||||
loading.value = false
|
||||
})
|
||||
} else {
|
||||
loading.value = false
|
||||
}
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
@@ -218,6 +286,13 @@ block.getAddrTokenAssets(address, 'token').then(res => {
|
||||
color: #516379;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.el-tag,
|
||||
.el-button {
|
||||
vertical-align: middle;
|
||||
margin-left: 16px;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.warning {
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
<template>
|
||||
<div class="container">
|
||||
登录
|
||||
<button @click="onLogin">一键登录</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -14,7 +13,7 @@ const route = useRoute()
|
||||
const store = useStore()
|
||||
|
||||
const onLogin = () => {
|
||||
store.dispatch('auth/Login', { username: '15555555555', password: '123123' }).then(() => {
|
||||
store.dispatch('auth/Login', { username: '', password: '' }).then(() => {
|
||||
route.query.to ? router.replace({ path: route.query.to as string }) : router.replace({ name: 'Home' })
|
||||
}).catch(err => {
|
||||
alert(err.message)
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
<template>
|
||||
注册
|
||||
|
||||
<button @click="onRegister">注册一个号</button>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<div class="height">
|
||||
<h2>区块高度 {{ info.height }}</h2>
|
||||
<div>区块哈希 {{ info.hash }}
|
||||
<el-button type="primary" round size="mini">复 制</el-button>
|
||||
<el-button type="primary" v-copy="info.hash" round size="mini">复 制</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
<div class="item">
|
||||
<img src="../../assets/dots/green.png">
|
||||
<label>上个区块</label>
|
||||
<span v-if="info.height -1 > 0">
|
||||
<span v-if="info.height - 1 > 0">
|
||||
<router-link :to="{name: 'BlockDetail', params: {hash: info.parentHash}}">
|
||||
{{ info.height - 1 }}
|
||||
</router-link>
|
||||
@@ -42,7 +42,7 @@
|
||||
<div class="item">
|
||||
<img src="../../assets/dots/red.png">
|
||||
<label>时间</label>
|
||||
<span>{{ info.blockTime }}</span>
|
||||
<span><TimeFormat :time="info.blockTime"/></span>
|
||||
</div>
|
||||
<div class="item">
|
||||
<img src="../../assets/dots/green.png">
|
||||
@@ -76,35 +76,44 @@
|
||||
<el-empty description="暂无数据"></el-empty>
|
||||
</template>
|
||||
|
||||
<el-table-column label="交易哈希">
|
||||
<el-table-column width="200" label="交易哈希">
|
||||
<template #default="scope">
|
||||
<router-link :to="{name: 'TradeDetail', params: { hash: scope.row.txHash }}">
|
||||
{{ filterHash(scope.row.txHash) }}
|
||||
{{ filterHash(scope.row.txHash, 16) }}
|
||||
</router-link>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="发送方">
|
||||
<el-table-column width="160" label="发送方">
|
||||
<template #default="scope">
|
||||
<router-link :to="{name: 'Address', params: { address: scope.row.fromAddr }}">
|
||||
{{ filterHash(scope.row.fromAddr) }}
|
||||
{{ filterHash(scope.row.fromAddr, 10) }}
|
||||
</router-link>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="接收方">
|
||||
<el-table-column width="160" label="接收方">
|
||||
<template #default="scope">
|
||||
<router-link :to="{name: 'Address', params: { address: scope.row.tx.to }}">
|
||||
{{ filterHash(scope.row.tx.to) }}
|
||||
{{ filterHash(scope.row.tx.to, 10) }}
|
||||
</router-link>
|
||||
</template>
|
||||
</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 prop="" label="上链时间" width="165" align="center">
|
||||
<el-table-column label="交易量" align="center">
|
||||
<template #default="scope">
|
||||
{{ (scope.row.amount / 1e8).toFixed(4) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="tx.feefmt" label="手续费" align="center"/>
|
||||
<el-table-column 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">
|
||||
<el-table-column label="调用函数" align="center">
|
||||
<template #default="scope">
|
||||
{{ scope.row.actionName }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="交易资产" align="center">
|
||||
<template #default="scope">
|
||||
{{ parseSymbol(scope.row.assets) }}
|
||||
</template>
|
||||
@@ -124,18 +133,20 @@ import { useRoute } from 'vue-router'
|
||||
|
||||
const route = useRoute()
|
||||
const pageSize = Number(process.env.VUE_APP_BLOCK_DETAIL_LIST_SIZE)
|
||||
const thisHash = ref<string>('')
|
||||
|
||||
onMounted(() => {
|
||||
thisHash.value = route.params.hash as string
|
||||
loadBlockData()
|
||||
})
|
||||
|
||||
watch(route, (to) => {
|
||||
if (to.name === 'BlockDetail') {
|
||||
thisHash.value = info.value.parentHash
|
||||
loadBlockData()
|
||||
}
|
||||
})
|
||||
|
||||
const hash: string = route.params.hash as string
|
||||
|
||||
const handleCurrentChange = (e: number) => {
|
||||
console.log(e)
|
||||
@@ -157,10 +168,7 @@ const next = ref({
|
||||
const records = ref([])
|
||||
|
||||
const loadBlockData = () => {
|
||||
console.log('jiazai shuju ')
|
||||
|
||||
block.getBlockOverview(hash).then(res => {
|
||||
console.log('更新INFO A ')
|
||||
block.getBlockOverview(thisHash.value).then(res => {
|
||||
info.value = res.result.head
|
||||
|
||||
block.getTxByHashes(res.result.txHashes).then(txs => {
|
||||
|
||||
@@ -71,6 +71,7 @@ const end = computed(() => {
|
||||
const blockList = ref([])
|
||||
|
||||
onMounted(() => {
|
||||
console.log('BLOCK INDEX')
|
||||
getBlockList()
|
||||
})
|
||||
|
||||
|
||||
@@ -30,15 +30,40 @@
|
||||
<div class="trades">
|
||||
<div class="head">
|
||||
<h1>最新交易</h1>
|
||||
<router-link :to="{name: 'Trade'}">查看全部</router-link>
|
||||
<!-- <router-link :to="{name: 'Trade'}">查看全部</router-link>-->
|
||||
</div>
|
||||
|
||||
<div class="items">
|
||||
<div class="items" v-loading="tradeLoading">
|
||||
<div class="item" v-for="(item, index) in tradeList" :key="index">
|
||||
{{ item.from }}
|
||||
{{ item.to }}
|
||||
{{ item.hash }}
|
||||
{{ item.feefmt }}
|
||||
<div class="hash">
|
||||
<div>
|
||||
交易哈希:
|
||||
<router-link :to="{name: 'TradeDetail', params: {hash: item.txHash}}">
|
||||
{{ filterHash(item.txHash, 10) }}
|
||||
</router-link>
|
||||
</div>
|
||||
<div class="time">
|
||||
<TimeFormat :time="item.blockTime"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="addr">
|
||||
<div>
|
||||
发送方:
|
||||
<router-link :to="{name: 'Address', params: {address: item.fromAddr}}">
|
||||
{{ filterHash(item.fromAddr, 10) }}
|
||||
</router-link>
|
||||
</div>
|
||||
<div>
|
||||
接收方:
|
||||
<router-link :to="{name: 'Address', params: {address: item.tx.to}}">
|
||||
{{ filterHash(item.tx.to, 10) }}
|
||||
</router-link>
|
||||
</div>
|
||||
</div>
|
||||
<div class="asset">
|
||||
<div>交易资产</div>
|
||||
<div>{{ item.amount }} {{ parseSymbol(item.assets) }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -52,11 +77,11 @@ export default {
|
||||
</script>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { block } from '@/api'
|
||||
import { block, esdb } from '@/api'
|
||||
import { Banner, TimeFormat } from '@/components'
|
||||
import useGetMaxHeight from '@/hooks/useGetMaxHeight'
|
||||
import { BlockItem } from '@/types/block'
|
||||
import { filterHash } from '@/utils/filters'
|
||||
import { BlockItem, TradeItem } from '@/types/block'
|
||||
import { filterHash, parseSymbol } from '@/utils/filters'
|
||||
import { onMounted, ref, watch } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
|
||||
@@ -65,6 +90,7 @@ const { maxHeight } = useGetMaxHeight()
|
||||
const pageSize = Number(process.env.VUE_APP_HOME_LIST_SIZE)
|
||||
|
||||
const blockLoading = ref<boolean>(true)
|
||||
const tradeLoading = ref<boolean>(true)
|
||||
|
||||
onMounted(() => {
|
||||
getBlockList()
|
||||
@@ -77,50 +103,65 @@ watch(maxHeight, (newValue, oldValue) => {
|
||||
})
|
||||
|
||||
const blockList = ref<BlockItem[]>([])
|
||||
const tradeList = ref([])
|
||||
const tradeList = ref<TradeItem[]>([])
|
||||
|
||||
if (blockList.value.length === 0) {
|
||||
let initBlock = {
|
||||
const initBlock = {
|
||||
height: 0,
|
||||
hash: 'x',
|
||||
hash: ' ',
|
||||
txCount: 0,
|
||||
blockTime: 0
|
||||
}
|
||||
} as BlockItem
|
||||
for (let i = 0; i < 6; i++) {
|
||||
blockList.value.push(initBlock)
|
||||
}
|
||||
}
|
||||
|
||||
if (tradeList.value.length === 0) {
|
||||
const initTrade = {
|
||||
txHash: ' ',
|
||||
blockTime: 0,
|
||||
fromAddr: ' ',
|
||||
amount: 0,
|
||||
assets: null,
|
||||
tx: {
|
||||
to: ' '
|
||||
}
|
||||
} as TradeItem
|
||||
for (let i = 0; i < 6; i++) {
|
||||
tradeList.value.push(initTrade)
|
||||
}
|
||||
}
|
||||
|
||||
const getBlockList = () => {
|
||||
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()
|
||||
getTradeList()
|
||||
}).finally(() => {
|
||||
blockLoading.value = false
|
||||
tradeLoading.value = false
|
||||
})
|
||||
}
|
||||
|
||||
async function getTradeList () {
|
||||
let txHashes = []
|
||||
for (let i = 0; i < blockList.value.length; i++) {
|
||||
let res = await block.getBlockOverview(blockList.value[i].hash)
|
||||
txHashes.push(...res.result.txHashes)
|
||||
|
||||
if (txHashes.length > pageSize) {
|
||||
txHashes = txHashes.slice(0, 6)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
block.getTxByHashes(txHashes).then(res => {
|
||||
tradeList.value = res.result.txs
|
||||
}).finally(() => {
|
||||
tradeLoading.value = false
|
||||
})
|
||||
// block.getBlocks(start, maxHeight.value, false).then(res => {
|
||||
// res.result.items.reverse().forEach((item) => {
|
||||
//
|
||||
// blockList.value.push({
|
||||
// height: item.block.height,
|
||||
// blockTime: item.block.blockTime,
|
||||
// txCount: item.block.txs.length,
|
||||
// // hash: block.getBlockHash(item.block.height)
|
||||
// })
|
||||
//
|
||||
// // block.getBlockHash(item.block.height).then(hash => {
|
||||
// // blockList.value.push({
|
||||
// // height: item.block.height,
|
||||
// // blockTime: item.block.blockTime,
|
||||
// // txCount: item.block.txs.length,
|
||||
// // hash: hash.result.hash
|
||||
// // })
|
||||
// // })
|
||||
// tradeList.value.push(...item.block.txs)
|
||||
// console.log(tradeList.value)
|
||||
// })
|
||||
// })
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -161,6 +202,10 @@ const getBlockList = () => {
|
||||
height: 90px;
|
||||
padding: 16px;
|
||||
display: flex;
|
||||
|
||||
.time {
|
||||
color: #9ea2a9;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -198,18 +243,30 @@ const getBlockList = () => {
|
||||
|
||||
.num {
|
||||
color: #6368de;
|
||||
margin-right: 16px;
|
||||
}
|
||||
|
||||
.time {
|
||||
margin-left: 16px;
|
||||
color: #9ea2a9;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.trades {
|
||||
|
||||
.item {
|
||||
justify-content: space-between;
|
||||
|
||||
.hash,
|
||||
.addr,
|
||||
.asset {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.asset {
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,11 @@
|
||||
</router-link>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="total" label="发行数量"/>
|
||||
<el-table-column label="发行数量">
|
||||
<template #default="scope">
|
||||
{{ (scope.row.total / 1e8).toFixed(2) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="发行时间" width="165" align="center">
|
||||
<template #default="scope">
|
||||
<TimeFormat :time="Number(scope.row.createdTime)"/>
|
||||
@@ -36,7 +40,7 @@ export default {
|
||||
import { block } from '@/api'
|
||||
import { Breadcrumb, TimeFormat } from '@/components'
|
||||
import { filterHash } from '@/utils/filters'
|
||||
import { onMounted, ref } from 'vue'
|
||||
import { ref } from 'vue'
|
||||
|
||||
const tokens = ref([])
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<div class="height">
|
||||
<h2>数据详情</h2>
|
||||
<div>数据哈希 {{ detail.tx.hash }}
|
||||
<el-button type="primary" round size="mini">复 制</el-button>
|
||||
<el-button type="primary" v-copy="detail.tx.hash" round size="mini">复 制</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
</div>
|
||||
<div class="item">
|
||||
<label>资产</label>
|
||||
<div>{{ detail.amount }} {{ parseSymbol(detail.assets) }}</div>
|
||||
<div>{{ (detail.amount / 1e8).toFixed(4) }} {{ parseSymbol(detail.assets) }}</div>
|
||||
</div>
|
||||
<div class="item">
|
||||
<label>GAS费</label>
|
||||
@@ -108,16 +108,16 @@ const detail = ref<BlockDetail>({
|
||||
payload: {}
|
||||
}
|
||||
} as BlockDetail)
|
||||
const blockHash = ref('')
|
||||
|
||||
const blockHash = ref<string>('')
|
||||
|
||||
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">
|
||||
|
||||
@@ -2,10 +2,9 @@
|
||||
<div class="container">
|
||||
<Breadcrumb :path="[{name: '全部数据'}]"/>
|
||||
|
||||
|
||||
<Pagination
|
||||
:length="maxHeight"
|
||||
:title="`全部区块(` + maxHeight + `)`"
|
||||
:length="txCount"
|
||||
:title="`数据总数(` + txCount + `)`"
|
||||
:page-size="pageSize"
|
||||
@current-change="handleCurrentChange"
|
||||
>
|
||||
@@ -23,13 +22,29 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
export default {
|
||||
name: 'Trade'
|
||||
}
|
||||
</script>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { Breadcrumb, Pagination } from '@/components'
|
||||
import useGetMaxHeight from '@/hooks/useGetMaxHeight'
|
||||
import { onMounted, ref } from 'vue'
|
||||
|
||||
import { ref } from 'vue'
|
||||
const { queryTotalFee } = useGetMaxHeight()
|
||||
|
||||
const currentPage = ref<number>(1)
|
||||
const pageSize = ref<number>(20)
|
||||
onMounted(() => {
|
||||
console.log('TRADE onMounted')
|
||||
})
|
||||
const txCount = ref<number>(0)
|
||||
|
||||
queryTotalFee().then(res => {
|
||||
txCount.value = res.result.txCount
|
||||
})
|
||||
|
||||
const pageSize = Number(process.env.VUE_APP_BLOCK_LIST_SIZE)
|
||||
const handleCurrentChange = (e: number) => {
|
||||
console.log(e)
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ function resolve (dir) {
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
productionSourceMap: false,
|
||||
configureWebpack: {
|
||||
resolve: {
|
||||
alias: {
|
||||
@@ -20,15 +21,21 @@ module.exports = {
|
||||
javascriptEnabled: true
|
||||
}
|
||||
}
|
||||
},
|
||||
devServer: {
|
||||
proxy: {
|
||||
'/api': {
|
||||
target: 'https://explorer.lianshang.vip/api',
|
||||
pathRewrite: {
|
||||
'^/api': ''
|
||||
}
|
||||
},
|
||||
'/esdb': {
|
||||
target: 'https://explorer.lianshang.vip/esdb',
|
||||
pathRewrite: {
|
||||
'^/esdb': ''
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// devServer: {
|
||||
// proxy: {
|
||||
// '/api': {
|
||||
// target: '',
|
||||
// pathRewrite: {
|
||||
// '^/api': ''
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
74
yarn.lock
@@ -990,10 +990,10 @@
|
||||
"@nodelib/fs.scandir" "2.1.5"
|
||||
fastq "^1.6.0"
|
||||
|
||||
"@popperjs/core@^2.10.1":
|
||||
version "2.10.1"
|
||||
resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.10.1.tgz#728ecd95ab207aab8a9a4e421f0422db329232be"
|
||||
integrity sha512-HnUhk1Sy9IuKrxEMdIRCxpIqPw6BFsbYSEUO9p/hNw5sMld/+3OLMWQP80F8/db9qsv3qUjs7ZR5bS/R+iinXw==
|
||||
"@popperjs/core@^2.10.2":
|
||||
version "2.11.0"
|
||||
resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.0.tgz#6734f8ebc106a0860dff7f92bf90df193f0935d7"
|
||||
integrity sha512-zrsUxjLOKAzdewIDRWy9nsV1GQsKBCWaGwsZQlCgr6/q+vjyZhFgqedLfFBuI9anTPEUT4APq9Mu0SZBTzIcGQ==
|
||||
|
||||
"@soda/friendly-errors-webpack-plugin@^1.7.1":
|
||||
version "1.8.0"
|
||||
@@ -2064,10 +2064,10 @@ async-limiter@~1.0.0:
|
||||
resolved "https://registry.nlark.com/async-limiter/download/async-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd"
|
||||
integrity sha1-3TeelPDbgxCwgpH51kwyCXZmF/0=
|
||||
|
||||
async-validator@^3.4.0:
|
||||
version "3.5.2"
|
||||
resolved "https://registry.yarnpkg.com/async-validator/-/async-validator-3.5.2.tgz#68e866a96824e8b2694ff7a831c1a25c44d5e500"
|
||||
integrity sha512-8eLCg00W9pIRZSB781UUX/H6Oskmm8xloZfr09lz5bikRpBVDlJ3hRVuxxP1SxcwsEYfJ4IU8Q19Y8/893r3rQ==
|
||||
async-validator@^4.0.3:
|
||||
version "4.0.7"
|
||||
resolved "https://registry.yarnpkg.com/async-validator/-/async-validator-4.0.7.tgz#034a0fd2103a6b2ebf010da75183bec299247afe"
|
||||
integrity sha512-Pj2IR7u8hmUEDOwB++su6baaRi+QvsgajuFB9j95foM1N2gy5HM4z60hfusIO0fBPG5uLAEl6yCJr1jNSVugEQ==
|
||||
|
||||
async@^2.6.2:
|
||||
version "2.6.3"
|
||||
@@ -2575,9 +2575,9 @@ caniuse-api@^3.0.0:
|
||||
lodash.uniq "^4.5.0"
|
||||
|
||||
caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001109, caniuse-lite@^1.0.30001254:
|
||||
version "1.0.30001257"
|
||||
resolved "https://registry.nlark.com/caniuse-lite/download/caniuse-lite-1.0.30001257.tgz#150aaf649a48bee531104cfeda57f92ce587f6e5"
|
||||
integrity sha1-FQqvZJpIvuUxEEz+2lf5LOWH9uU=
|
||||
version "1.0.30001320"
|
||||
resolved "https://registry.npmmirror.com/caniuse-lite/-/caniuse-lite-1.0.30001320.tgz"
|
||||
integrity sha512-MWPzG54AGdo3nWx7zHZTefseM5Y1ccM7hlQKHRqJkPozUaw3hNbBTMmLn16GG2FUzjR13Cr3NPfhIieX5PzXDA==
|
||||
|
||||
case-sensitive-paths-webpack-plugin@^2.3.0:
|
||||
version "2.4.0"
|
||||
@@ -2737,6 +2737,15 @@ cli-width@^3.0.0:
|
||||
resolved "https://registry.nlark.com/cli-width/download/cli-width-3.0.0.tgz#a2f48437a2caa9a22436e794bf071ec9e61cedf6"
|
||||
integrity sha1-ovSEN6LKqaIkNueUvwceyeYc7fY=
|
||||
|
||||
clipboard@^2.0.8:
|
||||
version "2.0.8"
|
||||
resolved "https://registry.yarnpkg.com/clipboard/-/clipboard-2.0.8.tgz#ffc6c103dd2967a83005f3f61976aa4655a4cdba"
|
||||
integrity sha512-Y6WO0unAIQp5bLmk1zdThRhgJt/x3ks6f30s3oE3H1mgIEU33XyQjEf8gsf6DxC7NPX8Y1SsNWjUjL/ywLnnbQ==
|
||||
dependencies:
|
||||
good-listener "^1.2.2"
|
||||
select "^1.1.2"
|
||||
tiny-emitter "^2.0.0"
|
||||
|
||||
clipboardy@^2.3.0:
|
||||
version "2.3.0"
|
||||
resolved "https://registry.nlark.com/clipboardy/download/clipboardy-2.3.0.tgz#3c2903650c68e46a91b388985bc2774287dba290"
|
||||
@@ -3446,6 +3455,11 @@ delayed-stream@~1.0.0:
|
||||
resolved "https://registry.nlark.com/delayed-stream/download/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
|
||||
integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk=
|
||||
|
||||
delegate@^3.1.2:
|
||||
version "3.2.0"
|
||||
resolved "https://registry.yarnpkg.com/delegate/-/delegate-3.2.0.tgz#b66b71c3158522e8ab5744f720d8ca0c2af59166"
|
||||
integrity sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw==
|
||||
|
||||
depd@~1.1.2:
|
||||
version "1.1.2"
|
||||
resolved "https://registry.nlark.com/depd/download/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9"
|
||||
@@ -3648,17 +3662,18 @@ electron-to-chromium@^1.3.830:
|
||||
integrity sha1-J6WyFGjp/vsOMooClANhfyCs7Jw=
|
||||
|
||||
element-plus@^1.1.0-beta.16:
|
||||
version "1.1.0-beta.16"
|
||||
resolved "https://registry.yarnpkg.com/element-plus/-/element-plus-1.1.0-beta.16.tgz#4409d9e33d005693f6039f5ed1fe05e301b3170d"
|
||||
integrity sha512-4BZEldnIfFZs5A/saRqaWE4PwTot4p3YZU7qsDr3ev2zp35pcCL9TtpWMLIvNTMxvxKew0HTDPTk9fAWIZFQrQ==
|
||||
version "1.1.0-beta.24"
|
||||
resolved "https://registry.yarnpkg.com/element-plus/-/element-plus-1.1.0-beta.24.tgz#858b05932ebc0be15419d3974d15be2a4f4b696c"
|
||||
integrity sha512-dmo61e/D6mwJVacMhxOMSPb5sZPt/FPsuQQfsOs1kJWkhGDmTlny/sZvgIQr1z0zh3pjlJadGAlNS+0nySPMmw==
|
||||
dependencies:
|
||||
"@popperjs/core" "^2.10.1"
|
||||
"@element-plus/icons" "^0.0.11"
|
||||
"@popperjs/core" "^2.10.2"
|
||||
"@vueuse/core" "~6.1.0"
|
||||
async-validator "^3.4.0"
|
||||
async-validator "^4.0.3"
|
||||
dayjs "^1.10.7"
|
||||
lodash "^4.17.21"
|
||||
memoize-one "^5.2.1"
|
||||
normalize-wheel "^1.0.1"
|
||||
normalize-wheel-es "^1.1.0"
|
||||
resize-observer-polyfill "^1.5.1"
|
||||
|
||||
elliptic@^6.5.3:
|
||||
@@ -4618,6 +4633,13 @@ globby@^9.2.0:
|
||||
pify "^4.0.1"
|
||||
slash "^2.0.0"
|
||||
|
||||
good-listener@^1.2.2:
|
||||
version "1.2.2"
|
||||
resolved "https://registry.yarnpkg.com/good-listener/-/good-listener-1.2.2.tgz#d53b30cdf9313dffb7dc9a0d477096aa6d145c50"
|
||||
integrity sha1-1TswzfkxPf+33JoNR3CWqm0UXFA=
|
||||
dependencies:
|
||||
delegate "^3.1.2"
|
||||
|
||||
graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0:
|
||||
version "4.2.8"
|
||||
resolved "https://registry.nlark.com/graceful-fs/download/graceful-fs-4.2.8.tgz#e412b8d33f5e006593cbd3cee6df9f2cebbe802a"
|
||||
@@ -6305,10 +6327,10 @@ normalize-url@^3.0.0:
|
||||
resolved "https://registry.nlark.com/normalize-url/download/normalize-url-3.3.0.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fnormalize-url%2Fdownload%2Fnormalize-url-3.3.0.tgz#b2e1c4dc4f7c6d57743df733a4f5978d18650559"
|
||||
integrity sha1-suHE3E98bVd0PfczpPWXjRhlBVk=
|
||||
|
||||
normalize-wheel@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/normalize-wheel/-/normalize-wheel-1.0.1.tgz#aec886affdb045070d856447df62ecf86146ec45"
|
||||
integrity sha1-rsiGr/2wRQcNhWRH32Ls+GFG7EU=
|
||||
normalize-wheel-es@^1.1.0:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/normalize-wheel-es/-/normalize-wheel-es-1.1.1.tgz#a8096db6a56f94332d884fd8ebeda88f2fc79569"
|
||||
integrity sha512-157VNH4CngrcsvF8xOVOe22cwniIR3nxSltdctvQeHZj8JttEeOXffK28jucWfWBXs0QNetAumjc1GiInnwX4w==
|
||||
|
||||
npm-run-path@^2.0.0:
|
||||
version "2.0.2"
|
||||
@@ -7768,6 +7790,11 @@ select-hose@^2.0.0:
|
||||
resolved "https://registry.nlark.com/select-hose/download/select-hose-2.0.0.tgz#625d8658f865af43ec962bfc376a37359a4994ca"
|
||||
integrity sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo=
|
||||
|
||||
select@^1.1.2:
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/select/-/select-1.1.2.tgz#0e7350acdec80b1108528786ec1d4418d11b396d"
|
||||
integrity sha1-DnNQrN7ICxEIUoeG7B1EGNEbOW0=
|
||||
|
||||
selfsigned@^1.10.8:
|
||||
version "1.10.11"
|
||||
resolved "https://registry.nlark.com/selfsigned/download/selfsigned-1.10.11.tgz#24929cd906fe0f44b6d01fb23999a739537acbe9"
|
||||
@@ -8490,6 +8517,11 @@ timsort@^0.3.0:
|
||||
resolved "https://registry.nlark.com/timsort/download/timsort-0.3.0.tgz#405411a8e7e6339fe64db9a234de11dc31e02bd4"
|
||||
integrity sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=
|
||||
|
||||
tiny-emitter@^2.0.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/tiny-emitter/-/tiny-emitter-2.1.0.tgz#1d1a56edfc51c43e863cbb5382a72330e3555423"
|
||||
integrity sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==
|
||||
|
||||
tmp@^0.0.33:
|
||||
version "0.0.33"
|
||||
resolved "https://registry.nlark.com/tmp/download/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9"
|
||||
|
||||