forked from LaconicNetwork/cosmos-explorer
@@ -0,0 +1,22 @@
|
||||
<script lang="ts" setup>
|
||||
import type { PropType } from 'vue';
|
||||
const props = defineProps({
|
||||
cardItem: {
|
||||
type: Object as PropType<{title: string;items: Array<any>;}>,
|
||||
},
|
||||
})
|
||||
|
||||
</script>
|
||||
<template>
|
||||
<div class="bg-card px-4 pt-3 pb-4 rounded-sm mt-6"
|
||||
v-if="props.cardItem?.items && props.cardItem?.items?.length > 0">
|
||||
<div class="text-base mb-3 text-main">{{ props.cardItem?.title }}</div>
|
||||
<div class="grid grid-cols-5 gap-4">
|
||||
<div v-for="(item,index) of props.cardItem?.items" :key="index" class="rounded-sm bg-active px-4 py-2">
|
||||
<div class="text-xs mb-2 text-secondary">{{ item?.subtitle }}</div>
|
||||
<div class="text-base text-main">{{ item?.value }}</div>
|
||||
<!-- {{ item }} -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -0,0 +1,26 @@
|
||||
<script lang="ts" setup>
|
||||
import type { PropType } from 'vue';
|
||||
const props = defineProps({
|
||||
tableItem: {
|
||||
type: Object as PropType<{title: string;items: Object;}>,
|
||||
},
|
||||
})
|
||||
|
||||
function formatTitle (name: string){
|
||||
return String(name).replaceAll('_', ' ')
|
||||
}
|
||||
|
||||
</script>
|
||||
<template>
|
||||
<div class="bg-card px-4 pt-3 pb-4 rounded-sm mt-6">
|
||||
<div class="text-base mb-3 text-main">{{ props.tableItem?.title }}</div>
|
||||
<div class="">
|
||||
<div class="d-flex flex-nowrap" v-for="(item,index ) of props.tableItem?.items" :key="index">
|
||||
<div class="mr-6">{{ formatTitle(item?.subtitle) }}</div>
|
||||
<div class="flex-1" >{{ item?.value }}</div>
|
||||
<div>{{typeof(item?.value )}}</div>
|
||||
<!-- {{ item }} -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -0,0 +1,48 @@
|
||||
export function getLocalObject(name: string) {
|
||||
const text = localStorage.getItem(name)
|
||||
if (text) {
|
||||
return JSON.parse(text)
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
export function getLocalChains() {
|
||||
return 'osmosis'
|
||||
}
|
||||
|
||||
export const percent = (num: number) => {
|
||||
return parseFloat((num * 100).toFixed(2))
|
||||
}
|
||||
|
||||
const COUNT_ABBRS = ['', 'K', 'M', 'B', 't', 'q', 's', 'S', 'o', 'n', 'd', 'U', 'D', 'T', 'Qt', 'Qd', 'Sd', 'St']
|
||||
|
||||
export function formatNumber(count:number, withAbbr = false, decimals = 2) {
|
||||
const i = count === 0 ? count : Math.floor(Math.log(count) / Math.log(1000))
|
||||
let result: any = parseFloat((count / (1000 ** i)).toFixed(decimals))
|
||||
if (withAbbr && COUNT_ABBRS[i]) {
|
||||
result += `${COUNT_ABBRS[i]}`
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
export function formatTokenAmount(assets: any ,tokenAmount: any, decimals = 2, tokenDenom = 'uatom', format = true) {
|
||||
const denom = tokenDenom?.denom_trace ? tokenDenom?.denom_trace?.base_denom : tokenDenom
|
||||
let amount = 0
|
||||
const asset = assets.find((a:any) => (a.base === denom))
|
||||
let exp = asset? asset.exponent: String(denom).startsWith('gravity') ? 18 : 6
|
||||
const config = Object.values(getLocalChains())
|
||||
|
||||
amount = Number(Number(tokenAmount)) / (10 ** exp)
|
||||
if (amount > 10) {
|
||||
if (format) { return numberWithCommas(parseFloat(amount.toFixed(decimals))) }
|
||||
return parseFloat(amount.toFixed(decimals))
|
||||
}
|
||||
return parseFloat(amount.toFixed(exp))
|
||||
}
|
||||
|
||||
export function numberWithCommas(x: any) {
|
||||
const parts = x.toString().split('.')
|
||||
parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ',')
|
||||
return parts.join('.')
|
||||
}
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
<template>
|
||||
<div class="text-main text-h5 mb-4">Parameters</div>
|
||||
<div class="bg-card px-4 pt-3 pb-4 rounded-sm">
|
||||
<div class="text-base mb-3 text-main">Minting Parameters</div>
|
||||
<div class="grid grid-cols-5 gap-4">
|
||||
<div v-for="item in 10" :key="item" class="rounded-sm bg-active px-4 py-2">
|
||||
<div class="text-xs mb-2 text-secondary">Blocks Per Year</div>
|
||||
<div class="text-base text-main">4,360,000</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -19,15 +19,15 @@ onMounted(()=>{
|
||||
</VTabs>
|
||||
<VWindow v-model="tab" class="mt-5">
|
||||
<VWindowItem value="2">
|
||||
<ProposalListItem :proposals="store.proposals['2']"/>
|
||||
<ProposalListItem :proposals="store?.proposals['2']"/>
|
||||
</VWindowItem>
|
||||
|
||||
<VWindowItem value="3">
|
||||
<ProposalListItem :proposals="store.proposals['3']"/>
|
||||
<ProposalListItem :proposals="store?.proposals['3']"/>
|
||||
</VWindowItem>
|
||||
|
||||
<VWindowItem value="4">
|
||||
<ProposalListItem :proposals="store.proposals['4']"/>
|
||||
<ProposalListItem :proposals="store?.proposals['4']"/>
|
||||
</VWindowItem>
|
||||
</VWindow>
|
||||
</div>
|
||||
|
||||
@@ -25,9 +25,7 @@ const format = useFormatter();
|
||||
const ticker = computed(() => store.coinInfo.tickers[store.tickerIndex]);
|
||||
|
||||
blockchain.$subscribe((m, s) => {
|
||||
console.log('index:', m);
|
||||
if (!Array.isArray(m.events) && ['chainName', 'endpoint'].includes(m.events.key)) {
|
||||
console.log(m.events.key);
|
||||
store.loadDashboard();
|
||||
}
|
||||
});
|
||||
@@ -154,7 +152,7 @@ function shortName(name: string, id: string) {
|
||||
<VCardTitle>Active Proposals</VCardTitle>
|
||||
</VCardItem>
|
||||
<VCardItem>
|
||||
<ProposalListItem :proposals="store.proposals" />
|
||||
<ProposalListItem :proposals="store?.proposals" />
|
||||
</VCardItem>
|
||||
<VCardText v-if="store.proposals.length === 0">No active proposals</VCardText>
|
||||
</VCard>
|
||||
|
||||
@@ -176,7 +176,6 @@ export const useIndexModule = defineStore('module-index', {
|
||||
},
|
||||
actions: {
|
||||
async loadDashboard() {
|
||||
console.log('initial dashboard')
|
||||
this.$reset()
|
||||
this.initCoingecko()
|
||||
useMintStore().fetchInflation()
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
<script lang="ts" setup>
|
||||
import { useParamStore } from '@/stores';
|
||||
import { ref, onMounted } from 'vue'
|
||||
import CardParameter from '@/components/CardParameter.vue'
|
||||
import TableParameter from '@/components/TableParameter.vue'
|
||||
import { sort } from 'semver';
|
||||
const store = useParamStore()
|
||||
const chain = ref(store.chain)
|
||||
onMounted(() => {
|
||||
// fetch the data
|
||||
store.initial()
|
||||
})
|
||||
|
||||
</script>
|
||||
<template>
|
||||
<div>
|
||||
<!-- Chain ID -->
|
||||
<div class="bg-card px-4 pt-3 pb-4 rounded-sm">
|
||||
<div class="text-base mb-3 text-main">{{ chain.title }}</div>
|
||||
<div class="grid grid-cols-5 gap-4">
|
||||
<div v-for="(item,index) of chain.items" :key="index" class="rounded-sm bg-active px-4 py-2">
|
||||
<div class="text-xs mb-2 text-secondary">{{ item.subtitle }}</div>
|
||||
<div class="text-base text-main">{{ item.value }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- minting Parameters -->
|
||||
<CardParameter :cardItem="store.mint"/>
|
||||
<!-- Staking Parameters -->
|
||||
<CardParameter :cardItem="store.staking"/>
|
||||
<!-- Governance Parameters -->
|
||||
<CardParameter :cardItem="store.gov"/>
|
||||
<!-- Distribution Parameters -->
|
||||
<CardParameter :cardItem="store.distribution"/>
|
||||
<!-- Slashing Parameters -->
|
||||
<CardParameter :cardItem="store.slashing"/>
|
||||
<!-- Application Version -->
|
||||
<TableParameter :tableItem="store.appVersion"/>
|
||||
<!-- Node Information -->
|
||||
<TableParameter :tableItem="store.nodeVersion"/>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
<route>
|
||||
{
|
||||
meta: {
|
||||
i18n: 'parameters'
|
||||
}
|
||||
}
|
||||
</route>
|
||||
@@ -3,7 +3,8 @@
|
||||
"dashboard": "面板首页",
|
||||
"blocks": "区块和交易",
|
||||
"staking": "质押生息",
|
||||
"governance": "社区治理"
|
||||
"governance": "社区治理",
|
||||
"parameters": "参数"
|
||||
},
|
||||
"index": {
|
||||
"slogan": "Ping Dashboard 是一个区块链浏览器,也是一个网页钱包,还有更多 ... 🛠",
|
||||
|
||||
@@ -3,7 +3,8 @@
|
||||
"dashboard": "Dashboard",
|
||||
"blocks": "Blocks&Transaction",
|
||||
"staking": "Staking",
|
||||
"governance": "Governance"
|
||||
"governance": "Governance",
|
||||
"parameters": "Parameters"
|
||||
},
|
||||
"index": {
|
||||
"slogan": "Ping Dashboard is not just an explorer but also a wallet and more ... 🛠",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import { useThemeConfig } from '@core/composable/useThemeConfig';
|
||||
import type { ThemeSwitcherTheme } from '@layouts/types';
|
||||
import { onMounted } from 'vue';
|
||||
import { onMounted, watch } from 'vue';
|
||||
|
||||
const props = defineProps<{
|
||||
themes: ThemeSwitcherTheme[];
|
||||
@@ -18,11 +18,15 @@ const {
|
||||
);
|
||||
|
||||
const changeTheme = () => {
|
||||
theme.value = getNextThemeName();
|
||||
theme.value = getNextThemeName();
|
||||
};
|
||||
|
||||
const changeMode = (val: 'dark' | 'light') => {
|
||||
if (val === 'dark') {
|
||||
const changeMode = (val: 'dark' | 'light' | 'system') => {
|
||||
let value = val;
|
||||
if (theme.value === 'system' && window.matchMedia('(prefers-color-scheme: dark)').matches) {
|
||||
value = 'dark';
|
||||
}
|
||||
if (value === 'dark') {
|
||||
document.documentElement.classList.add('dark');
|
||||
document.documentElement.classList.remove('light');
|
||||
} else {
|
||||
@@ -31,7 +35,7 @@ const changeMode = (val: 'dark' | 'light') => {
|
||||
}
|
||||
};
|
||||
// Update icon if theme is changed from other sources
|
||||
watch(theme, (val: 'dark' | 'light') => {
|
||||
watch(theme, (val: 'dark' | 'light' | 'system') => {
|
||||
currentThemeName.value = val;
|
||||
changeMode(val);
|
||||
});
|
||||
|
||||
@@ -230,8 +230,8 @@ watch(isVerticalNavMini(windowWidth, isVerticalNavHovered), val => {
|
||||
class="nav-group-children"
|
||||
>
|
||||
<Component
|
||||
:is="'children' in child ? 'VerticalNavGroup' : VerticalNavLink"
|
||||
v-for="child in item.children"
|
||||
:is="'children' in child ? 'VerticalNavGroup' : VerticalNavLink"
|
||||
:key="child.title"
|
||||
:item="child"
|
||||
/>
|
||||
|
||||
+2
-1
@@ -7,4 +7,5 @@ export * from './useFormatter'
|
||||
export * from './useGovStore'
|
||||
export * from './useMintStore'
|
||||
export * from './useStakingStore'
|
||||
export * from './useDistributionStore'
|
||||
export * from './useDistributionStore'
|
||||
export * from './useParamsStore'
|
||||
@@ -36,7 +36,6 @@ export const useBlockchain = defineStore("blockchain", {
|
||||
|
||||
const router = useRouter()
|
||||
const routes = router?.getRoutes()||[]
|
||||
console.log(routes)
|
||||
if(this.current && routes) {
|
||||
currNavItem = [{
|
||||
title: this.current?.prettyName || this.chainName || '',
|
||||
@@ -115,7 +114,6 @@ export const useBlockchain = defineStore("blockchain", {
|
||||
},
|
||||
setCurrent(name: string) {
|
||||
this.chainName = name
|
||||
console.log('set current', name)
|
||||
},
|
||||
|
||||
}
|
||||
|
||||
@@ -70,6 +70,8 @@ export interface ChainConfig {
|
||||
cosmosSdk?: string,
|
||||
tendermint?: string,
|
||||
},
|
||||
exponent: string,
|
||||
excludes?: string,
|
||||
}
|
||||
|
||||
export interface LocalConfig {
|
||||
@@ -109,7 +111,8 @@ export function fromLocal(lc: LocalConfig ): ChainConfig {
|
||||
display: x.symbol,
|
||||
symbol: x.symbol,
|
||||
logo_URIs: { svg: x.logo },
|
||||
coingecko_id: x.coingecko_id,
|
||||
coingecko_id: x.coingecko_id,
|
||||
exponent: x.exponent,
|
||||
denom_units: [{denom: x.base, exponent: 0}, {denom: x.symbol.toLowerCase(), exponent: Number(x.exponent)}]
|
||||
}))
|
||||
conf.bech32Prefix = lc.addr_prefix
|
||||
|
||||
@@ -0,0 +1,195 @@
|
||||
import { defineStore } from "pinia";
|
||||
import { useBlockchain } from "./useBlockchain";
|
||||
import { percent,formatNumber,formatTokenAmount } from '@/libs/utils'
|
||||
export interface stakingItem {
|
||||
unbonding_time: string
|
||||
max_validators: number
|
||||
max_entries:number
|
||||
historical_entries:number
|
||||
bond_denom: string
|
||||
min_commission_rate: string
|
||||
min_self_delegation:string
|
||||
}
|
||||
|
||||
export const useParamStore = defineStore("paramstore", {
|
||||
state: () => ({
|
||||
latestTime: '',
|
||||
chain: {
|
||||
title: '',
|
||||
class: 'border-primary',
|
||||
items: [
|
||||
{ subtitle: 'height', icon: 'BoxIcon', color: 'light-success', value: '-' },
|
||||
{ subtitle: 'bonded_and_supply', icon: 'DollarSignIcon', color: 'light-danger', value: '-' },
|
||||
{ subtitle: 'bonded_ratio', icon: 'PercentIcon', color: 'light-warning', value: '-' },
|
||||
{ subtitle: 'inflation', icon: 'TrendingUpIcon', color: 'light-primary', value: '-' },
|
||||
],
|
||||
},
|
||||
mint: {
|
||||
title: 'Mint Parameters',
|
||||
items: [] as Array<any>,
|
||||
},
|
||||
staking: {
|
||||
title: 'Staking Parameters',
|
||||
items: [] as Array<any>,
|
||||
},
|
||||
distribution: {
|
||||
title: 'Distribution Parameters',
|
||||
items: [] as Array<any>,
|
||||
},
|
||||
slashing: {
|
||||
title: 'Slashing Parameters',
|
||||
items: [] as Array<any>,
|
||||
},
|
||||
gov: {
|
||||
title: 'Governance Parameters',
|
||||
items: [] as Array<any>,
|
||||
},
|
||||
appVersion: {
|
||||
title: 'Application Version',
|
||||
items: {},
|
||||
},
|
||||
nodeVersion: {
|
||||
title: 'Node Information',
|
||||
items: {},
|
||||
},
|
||||
}),
|
||||
getters: {
|
||||
blockchain() {
|
||||
return useBlockchain()
|
||||
},
|
||||
},
|
||||
actions: {
|
||||
initial() {
|
||||
this.handleBaseBlockLatest()
|
||||
// this.handleMintParam()
|
||||
this.handleStakingParams()
|
||||
this.handleSlashingParams()
|
||||
this.handleDistributionParams()
|
||||
this.handleGovernanceParams()
|
||||
this.handleAbciInfo()
|
||||
},
|
||||
async handleBaseBlockLatest() {
|
||||
try {
|
||||
const res = await this.getBaseTendermintBlockLatest()
|
||||
const height = this.chain.items.findIndex(x => x.subtitle === 'height')
|
||||
this.chain.title = `Chain ID: ${res.block.header.chain_id}`
|
||||
this.chain.items[height].value = res.block.header.height
|
||||
// if (timeIn(res.block.header.time, 3, 'm')) {
|
||||
// this.syncing = true
|
||||
// } else {
|
||||
// this.syncing = false
|
||||
// }
|
||||
// this.latestTime = toDay(res.block.header.time, 'long')
|
||||
this.latestTime = res.block.header.time
|
||||
} catch (error) {
|
||||
console.warn(error)
|
||||
}
|
||||
},
|
||||
async handleStakingParams() {
|
||||
const res = await this.getStakingParams()
|
||||
const bond_denom = res?.params.bond_denom
|
||||
this.staking.items = Object.entries(res.params).map(([key, value]) => ({ subtitle:key,
|
||||
value: value })).filter((item: any) => {
|
||||
if (!['min_commission_rate','min_self_delegation'].includes(item.subtitle)) return item
|
||||
})
|
||||
Promise.all([this.getStakingPool(), this.getBankTotal(bond_denom)])
|
||||
.then(resArr => {
|
||||
const pool = resArr[0]?.pool
|
||||
const amount =resArr[1]?.amount?.amount
|
||||
const assets = this.blockchain.current?.assets
|
||||
const bondedAndSupply = this.chain.items.findIndex(x => x.subtitle === 'bonded_and_supply')
|
||||
this.chain.items[bondedAndSupply].value = `${formatNumber(formatTokenAmount(assets,pool.bonded_tokens, 2, bond_denom, false), true, 0)}/${formatNumber(formatTokenAmount(assets,amount, 2, bond_denom, false), true, 0)}`
|
||||
const bondedRatio = this.chain.items.findIndex(x => x.subtitle === 'bonded_ratio')
|
||||
this.chain.items[bondedRatio].value = `${percent(Number(pool.bonded_tokens) /Number(amount)) }%`
|
||||
})
|
||||
},
|
||||
async handleMintParam() {
|
||||
const excludes = this.blockchain.current?.excludes
|
||||
if(excludes && excludes.indexOf('mint') > -1){
|
||||
return
|
||||
}
|
||||
// this.getMintingInflation().then(res => {
|
||||
// const chainIndex = this.chain.items.findIndex(x => x.subtitle === 'inflation')
|
||||
// this.chain.items[chainIndex].value = `${percent(res)}%`
|
||||
// })
|
||||
const res = await this.getMintParam()
|
||||
console.log(res, 'mint')
|
||||
},
|
||||
async handleSlashingParams(){
|
||||
const res = await this.getSlashingParams()
|
||||
this.slashing.items = Object.entries(res.params).map(([key, value]) => ({ subtitle:key,
|
||||
value: value }))
|
||||
},
|
||||
async handleDistributionParams(){
|
||||
const res = await this.getDistributionParams()
|
||||
this.distribution.items = Object.entries(res.params).map(([key, value]) => ({ subtitle: key,
|
||||
value: value }))
|
||||
},
|
||||
async handleGovernanceParams() {
|
||||
const excludes = this.blockchain.current?.excludes
|
||||
if(excludes && excludes.indexOf('governance') > -1){
|
||||
return
|
||||
}
|
||||
Promise.all([this.getGovParamsVoting(),this.getGovParamsDeposit(),this.getGovParamsTally()]).then((resArr) => {
|
||||
const govParams = {...resArr[0]?.voting_params,...resArr[1]?.deposit_params,...resArr[2]?.tally_params}
|
||||
this.gov.items = Object.entries(govParams).map(([key, value]) => ({ subtitle:key,
|
||||
value: value }))
|
||||
})
|
||||
|
||||
},
|
||||
async handleAbciInfo(){
|
||||
const res = await this.fetchAbciInfo()
|
||||
this.appVersion.items = Object.entries(res.application_version).map(([key, value]) => ({ subtitle:key,
|
||||
value: value }))
|
||||
this.nodeVersion.items = Object.entries(res.default_node_info).map(([key, value]) => ({ subtitle:key,
|
||||
value: value }))
|
||||
console.log('handleAbciInfo', res)
|
||||
},
|
||||
async getBaseTendermintBlockLatest() {
|
||||
return await this.blockchain.rpc.getBaseBlockLatest()
|
||||
},
|
||||
async getMintParam() {
|
||||
return await this.blockchain.rpc.getMintParam()
|
||||
},
|
||||
async getStakingParams() {
|
||||
return await this.blockchain.rpc.getStakingParams()
|
||||
},
|
||||
async getStakingPool(){
|
||||
return await this.blockchain.rpc.getStakingPool()
|
||||
},
|
||||
async getBankTotal(denom: string){
|
||||
return await this.blockchain.rpc.getBankSupplyByDenom(denom)
|
||||
// if (compareVersions(this.config.sdk_version, '0.46.2') > 0) {
|
||||
// return this.get(`/cosmos/bank/v1beta1/supply/by_denom?denom=${denom}`).then(data => commonProcess(data).amount)
|
||||
// }
|
||||
// if (compareVersions(this.config.sdk_version, '0.40') < 0) {
|
||||
// return this.get(`/supply/total/${denom}`).then(data => ({ amount: commonProcess(data), denom }))
|
||||
// }
|
||||
// return this.get(`/cosmos/bank/v1beta1/supply/${denom}`).then(data => commonProcess(data).amount)
|
||||
},
|
||||
async getSlashingParams() {
|
||||
return await this.blockchain.rpc.getSlashingParams()
|
||||
},
|
||||
async getDistributionParams() {
|
||||
return await this.blockchain.rpc.getDistributionParams()
|
||||
},
|
||||
async getGovParamsVoting() {
|
||||
return await this.blockchain.rpc.getGovParamsVoting()
|
||||
},
|
||||
async getGovParamsDeposit() {
|
||||
return await this.blockchain.rpc.getGovParamsDeposit()
|
||||
},
|
||||
async getGovParamsTally() {
|
||||
return await this.blockchain.rpc.getGovParamsTally()
|
||||
},
|
||||
async fetchAbciInfo() {
|
||||
return this.blockchain.rpc.getBaseNodeInfo()
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
})
|
||||
Reference in New Issue
Block a user