Compare commits

...

4 Commits

Author SHA1 Message Date
de7d9b29b2 第一版本 2021-09-30 17:00:15 +08:00
d03cef9685 数据分页 2021-09-30 16:56:41 +08:00
4e1ca61828 types 2021-09-30 15:42:53 +08:00
7785e2bd5a 首页样式修改 2021-09-30 12:19:32 +08:00
21 changed files with 349 additions and 155 deletions

View File

@@ -1,10 +1,17 @@
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_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

View File

@@ -1,3 +1,3 @@
import Chain33Rpc from '@33cn/chain33-rpc-api'
export default new Chain33Rpc('https://explorer.lianshang.vip/api', null)
export default new Chain33Rpc(process.env.VUE_APP_BLOCK_URL, null)

BIN
src/assets/home/box.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 720 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 763 B

View File

@@ -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>

View File

@@ -24,10 +24,10 @@ const navList = ref<NavItem[]>([
title: '查看区块',
route: 'Block'
},
{
title: '查看数据',
route: 'Trade'
},
// {
// title: '查看数据',
// route: 'Trade'
// },
{
title: 'Token',
route: 'Token'
@@ -43,10 +43,6 @@ const navList = ref<NavItem[]>([
{
title: '广播数据',
route: 'Broadcast'
},
{
title: '我的钱包',
route: 'Wallet'
}
])
const linkTo = (name: string) => {

View File

@@ -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>

View File

@@ -1,18 +1,23 @@
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, onMounted, ref } from 'vue'
import { computed, ComputedRef, onActivated, ref } from 'vue'
import { onBeforeRouteLeave } from 'vue-router'
export default () => {
const maxHeight = computed(() => vuex.getters.maxHeight)
const lastHash = computed(() => vuex.getters.lastHash)
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(() => {
@@ -23,8 +28,9 @@ export default () => {
/**
* 获取最新的区块
*/
const getLastHeader = () => {
const getLastHeader = (): void => {
block.getLastHeader().then(res => {
console.log('获取最新区块', res.result.height)
if (res.error) {
clearInterval(Number(interval.value))
return ElMessage.error({
@@ -32,8 +38,6 @@ export default () => {
offset: 300
})
} else if (maxHeight.value !== res.result.height) {
console.log('获取最新区块', res.result.height)
vuex.dispatch('setMaxHeight', res.result.height).then()
vuex.dispatch('setLastHash', res.result.hash).then()
}
@@ -43,7 +47,7 @@ export default () => {
/**
* 查询从交易量和交易费
*/
const queryTotalFee = () => {
const queryTotalFee = (): Promise<TotalFee> => {
return block.queryTotalFee(hexCharCodeToStr(lastHash.value))
}

View File

@@ -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[]

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

@@ -105,4 +105,13 @@ export declare type TradeItem = {
tx: {
to: string
}
}
}
export declare type TotalFee = {
error: string | null
id: number
result: {
fee: number
txCount: number
}
}

View File

@@ -60,73 +60,83 @@
</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 width="80" 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 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="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="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="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="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>
</template>
</Pagination>
</div>
</template>
@@ -138,12 +148,12 @@ 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 balance = ref<AddrOverview>({
@@ -151,61 +161,92 @@ const balance = ref<AddrOverview>({
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)
watch(route, (to) => {
if (to.name == 'Address') {
records.value = []
initAddressData()
}
})
const records = ref<BlockDetail[]>([])
/**
* 获取全部交易
*/
block.getTxByAddr({
addr: address,
onMounted(() => {
initAddressData()
})
const initAddressData = () => {
/**
* 获取地址的基本信息
*/
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">

View File

@@ -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)

View File

@@ -1,7 +1,5 @@
<template>
注册
<button @click="onRegister">注册一个号</button>
</template>
<script lang="ts" setup>

View File

@@ -71,6 +71,7 @@ const end = computed(() => {
const blockList = ref([])
onMounted(() => {
console.log('BLOCK INDEX')
getBlockList()
})

View File

@@ -30,7 +30,7 @@
<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" v-loading="tradeLoading">
@@ -141,6 +141,7 @@ const getBlockList = () => {
getTradeList()
}).finally(() => {
blockLoading.value = false
tradeLoading.value = false
})
}
@@ -157,7 +158,6 @@ async function getTradeList () {
}
block.getTxByHashes(txHashes).then(res => {
console.log(res.result.txs)
tradeList.value = res.result.txs
}).finally(() => {
tradeLoading.value = false

View File

@@ -31,10 +31,13 @@ export default {
<script lang="ts" setup>
import { Breadcrumb, Pagination } from '@/components'
import useGetMaxHeight from '@/hooks/useGetMaxHeight'
import { ref } from 'vue'
import { onMounted, ref } from 'vue'
const { queryTotalFee } = useGetMaxHeight()
onMounted(() => {
console.log('TRADE onMounted')
})
const txCount = ref<number>(0)
queryTotalFee().then(res => {