finish staking list

This commit is contained in:
liangping 2021-07-30 23:24:46 +08:00
parent e02cac6154
commit df6a20ee67
26 changed files with 839 additions and 158 deletions

View File

@ -12,7 +12,14 @@
class="d-flex align-items-center"
@click="() => updateGroupOpen(!isOpen)"
>
<feather-icon :icon="item.icon || 'CircleIcon'" />
<b-avatar
variant="info"
:src="item.icon"
icon="people-fill"
size="sm"
class="mr-1"
/>
<span class="menu-title text-truncate">{{ t(item.title) }}</span>
<b-badge
v-if="item.tag"
@ -40,7 +47,9 @@
</template>
<script>
import { BLink, BBadge, BCollapse } from 'bootstrap-vue'
import {
BLink, BBadge, BCollapse, BAvatar,
} from 'bootstrap-vue'
import { resolveVerticalNavMenuItemComponent as resolveNavItemComponent } from '@core/layouts/utils'
import { useUtils as useI18nUtils } from '@core/libs/i18n'
import { useUtils as useAclUtils } from '@core/libs/acl'
@ -56,6 +65,7 @@ export default {
components: {
VerticalNavMenuHeader,
VerticalNavMenuLink,
BAvatar,
BLink,
BBadge,
BCollapse,

View File

@ -11,8 +11,8 @@
v-bind="linkProps"
class="d-flex align-items-center"
>
<feather-icon :icon="item.icon || 'CircleIcon'" />
<span class="menu-title text-truncate">{{ t(item.title) }}</span>
<feather-icon :icon="item.icon || 'ChevronRightIcon'" />
<span class="text-truncate">{{ t(item.title) }}</span>
<b-badge
v-if="item.tag"
pill

View File

@ -9,6 +9,20 @@
"staking": "Staking",
"governance": "Governance",
"summary": "Summary",
"blockchains": "Blockchains"
"blockchains": "Blockchains",
"proposal_id": "Proposal ID",
"proposal_type": "Proposal Type",
"proposal_proposer": "Proposer",
"proposal_submit_time": "Submited Time",
"proposal_voting_start_time": "Voting Begin Time",
"proposal_voting_end_time": "Voting End Time",
"proposal_description": "Description",
"proposal_content": "Content",
"proposal_total_deposit": "Total Deposit",
"btn_vote": "Vote",
"btn_detail": "Detail",
"btn_back_list": "Back To List"
}

View File

@ -33,7 +33,7 @@
<h6 class="mb-0 text-uppercase">
{{ selected_chain.chain_name }}
</h6>
<small v-b-tooltip.hover.bottom="'Data Provider'">{{ selected_chain.api }}</small>
<small v-b-tooltip.hover.bottom="'Data Provider'">{{ selected_chain.api }} {{ selected_chain.sdk_version }}</small>
</b-media-body>
</b-media>
</div>
@ -94,11 +94,10 @@ export default {
},
computed: {
selected_chain() {
const c = this.$route.params.chain
const has = Object.keys(store.state.chains.chains).findIndex(i => i === c)
const selected = (has > -1) ? store.state.chains.chains[c] : store.state.chains.chains.cosmos
localStorage.setItem('selected_chain', selected)
return selected
// const c = this.$route.params.chain
// const has = Object.keys(store.state.chains.config).findIndex(i => i === c)
// const selected = (has > -1) ? store.state.chains.config[c] : store.state.chains.config.cosmos
return store.state.chains.selected
},
},
}

View File

@ -1,7 +1,7 @@
<template>
<layout-vertical>
<router-view />
<router-view :key="$route.fullPath" />
<template #navbar="{ toggleVerticalMenuActive }">
<navbar :toggle-vertical-menu-active="toggleVerticalMenuActive" />

View File

@ -1,16 +1,14 @@
import dayjs from 'dayjs'
export function toDay(time) {
return dayjs(time).format('YYYY-MM-DD')
return dayjs(time).format('YYYY-MM-DD HH:mm')
}
export function percent(num) {
return (num * 100).toFixed(2)
return parseFloat((num * 100).toFixed(2))
}
export function formatToken(token) {
// token.denom
// token.amount
let denom = token.denom.toUpperCase()
if (denom.charAt(0) === 'U') {
denom = denom.substring(1)
@ -18,6 +16,13 @@ export function formatToken(token) {
return `${(token.amount / 1000000).toFixed()} ${denom}`
}
export function tokenFormatter(tokens) {
if (Array.isArray(tokens)) {
return tokens.map(t => formatToken(t))
}
return formatToken(tokens)
}
export * from 'compare-versions'
export class Data {

18
src/libs/data/deposit.js Normal file
View File

@ -0,0 +1,18 @@
export default class Deposit {
constructor() {
this.proposal_id = ''
this.depositor = ''
this.amount = ''
}
init(element) {
this.proposal_id = element.proposal_id
this.depositor = element.depositor
this.amount = element.amount
return this
}
debug() {
return console.log(this)
}
}

View File

@ -1,5 +1,10 @@
export { default as Proposal } from './proposal'
export { default as ProposalTally } from './proposal-tally'
export { default as StakingPool } from './staking-pool'
export { default as Proposer } from './proposer'
export { default as Votes } from './votes'
export { default as Deposit } from './deposit'
export { default as Validator } from './validator'
export * from './data'
export default class Test {}

View File

@ -1,13 +1,28 @@
import { percent } from './data'
export default class ProposalTally {
constructor(element, total) {
this.yes = percent(Number(element.yes) / total)
this.no = percent(Number(element.no) / total)
this.veto = percent(Number(element.no_with_veto) / total)
this.abstain = percent(Number(element.abstain) / total)
this.turnout = percent((Number(element.yes) + Number(element.no) + Number(element.abstain) + Number(element.no_with_veto)) / total)
this.total = total
constructor() {
this.total = 0
this.yes = 0
this.no = 0
this.veto = 0
this.abstain = 0
this.turnout = 0
}
init(element, total) {
const subtotal = Number(element.yes) + Number(element.no) + Number(element.abstain) + Number(element.no_with_veto)
if (total < 1) {
this.total = subtotal + 1
} else {
this.total = total
}
this.yes = percent(Number(element.yes) / this.total)
this.no = percent(Number(element.no) / this.total)
this.veto = percent(Number(element.no_with_veto) / this.total)
this.abstain = percent(Number(element.abstain) / this.total)
this.turnout = percent(subtotal / this.total)
return this
}
total() {

View File

@ -1,31 +1,43 @@
import compareVersions from 'compare-versions'
import { toDay, formatToken, percent } from './data'
import { toDay, formatToken } from './data'
import ProposalTally from './proposal-tally'
export default class Proposal {
constructor(element, total) {
constructor() {
this.element = null
this.id = 0
this.status = 1
this.type = '-'
this.title = '-'
this.description = '-'
this.tally = new ProposalTally()
this.submit_time = ' - '
this.voting_end_time = '0000-00-00'
this.voting_start_time = '-'
this.total_deposit = '-'
this.contents = null
}
init(element, total) {
this.element = element
const yes = Number(element.final_tally_result.yes)
const no = Number(element.final_tally_result.no)
const abstain = Number(element.final_tally_result.abstain)
const veto = Number(element.final_tally_result.no_with_veto)
this.id = element.id
this.status = element.status
this.type = element.content.type
this.title = element.content.value.title
this.description = element.content.value.description
this.tally = {
yes: percent(yes / total),
no: percent(no / total),
abstain: percent(abstain / total),
veto: percent(veto / total),
turnout: percent((yes + no + abstain + veto) / total),
total,
}
this.tally = new ProposalTally().init(element.final_tally_result, total)
this.submit_time = toDay(element.submit_time)
this.voting_end_time = toDay(element.voting_end_time)
this.voting_start_time = toDay(element.voting_start_time)
this.total_deposit = formatToken(element.total_deposit[0])
this.contents = element.content.value
return this
}
hello() {
return this.title
}
updateTally(newTally) {
@ -33,7 +45,6 @@ export default class Proposal {
}
versionFixed(ver) {
console.log(ver, compareVersions(ver, '0.4'))
if (compareVersions(ver, '0.40') >= 0) {
// do nothing
} else if (compareVersions(ver, '0.37') >= 0) {
@ -50,7 +61,6 @@ export default class Proposal {
default:
this.status = 1
}
console.log(this)
}
}
}

18
src/libs/data/proposer.js Normal file
View File

@ -0,0 +1,18 @@
export default class Proposer {
constructor() {
this.proposal_id = ''
this.proposer = ''
}
init(element) {
if (element != null) {
this.proposal_id = element.proposal_id
this.proposer = element.proposer
}
return this
}
debug() {
return console.log(this)
}
}

View File

@ -1,10 +1,17 @@
import { percent } from './data'
export default class StakingPool {
constructor(element) {
constructor() {
this.element = null
this.bondedToken = 0
this.notBondedToken = 0
}
init(element) {
this.element = element
this.bondedToken = Number(element.bonded_tokens)
this.notBondedToken = Number(element.not_bonded_tokens)
return this
}
total() {
@ -12,6 +19,6 @@ export default class StakingPool {
}
bondedRatio() {
return percent((this.bondedToken * 100) / this.total)
return percent((this.bondedToken * 100) / this.total())
}
}

View File

@ -0,0 +1,24 @@
export default class ValidatorDescription {
constructor() {
this.moniker = 'string'
this.identity = 'string'
this.website = 'string'
this.security_contact = 'string'
this.details = 'string'
}
init(element) {
if (element != null) {
this.moniker = element.moniker
this.identity = element.identity
this.website = element.website
this.security_contact = element.security_contact
this.details = element.details
}
return this
}
debug() {
return console.log(this)
}
}

View File

@ -0,0 +1,22 @@
export default class ValidatorCommission {
constructor() {
this.rate = '0'
this.max_rate = '0'
this.max_change_rate = '0'
this.update_time = '1970-01-01T00:00:00Z'
}
init(element) {
if (element != null) {
this.rate = element.commission_rates.rate
this.max_rate = element.commission_rates.max_rate
this.max_change_rate = element.commission_rates.max_change_rate
this.update_time = element.update_time
}
return this
}
debug() {
return console.log(this)
}
}

View File

@ -0,0 +1,35 @@
import ValidatorCommission from './validator-commission'
import ValidatorDescription from './valdiator-description'
export default class Validator {
constructor() {
this.operator_address = 'cosmosvaloper16xyempempp92x9hyzz9wrgf94r6j9h5f2w4n2l'
this.consensus_pubkey = 'cosmosvalconspub1zcjduepq0vu2zgkgk49efa0nqwzndanq5m4c7pa3u4apz4g2r9gspqg6g9cs3k9cuf'
this.jailed = true
this.status = 0
this.tokens = '-'
this.delegator_shares = 0
this.description = new ValidatorDescription()
this.bond_height = 0
this.bond_intra_tx_counter = 0
this.unbonding_height = 0
this.unbonding_time = '1970-01-01T00:00:00Z'
this.commission = new ValidatorCommission()
}
init(element) {
this.operator_address = element.operator_address
this.consensus_pubkey = element.consensus_pubkey
this.jailed = element.jailed
this.status = element.status
this.tokens = element.tokens
this.delegator_shares = element.delegator_shares
this.description = new ValidatorDescription().init(element.description)
this.bond_height = element.bond_height
this.bond_intra_tx_counter = element.bond_intra_tx_counter
this.unbonding_height = element.unbonding_height
this.unbonding_time = element.unbonding_time
this.commission = new ValidatorCommission().init(element.commission)
return this
}
}

18
src/libs/data/votes.js Normal file
View File

@ -0,0 +1,18 @@
export default class Votes {
constructor() {
this.proposal_id = 0
this.voter = ''
this.option = ''
}
init(element) {
this.proposal_id = element.proposal_id
this.voter = element.voter
this.option = element.option
return this
}
debug() {
return console.log(this)
}
}

View File

@ -1,50 +1,67 @@
import fetch from 'node-fetch'
import store from '@/store'
import DefaultHandler from '@/libs/default-handler'
import { Proposal, ProposalTally, StakingPool } from './data'
const requestVersion = async (c, url) => {
const response = await fetch(url)
const json = await response.json()
const sdk = json.application_version.build_deps.find(e => e.startsWith('github.com/cosmos/cosmos-sdk'))
const re = /(\d+(\.\d+)*)/i
const version = sdk.match(re)
console.log(`${c} sdk version:`, version[0])
store.commit('setup_sdk_version', { chain_name: c, version: version[0] })
return version[0]
}
import {
Proposal, ProposalTally, Proposer, StakingPool, Votes, Deposit,
Validator,
} from './data'
function commonProcess(res) {
return res.result
}
// 头像
export async function keybase(identity) {
return fetch(`https://keybase.io/_/api/1.0/user/lookup.json?key_suffix=${identity}&fields=pictures`)
.then(res => res.json())
}
const chainAPI = class ChainFetch {
constructor(route) {
const c = route.params.chain
const has = Object.keys(store.state.chains.chains).findIndex(i => i === c)
const selected = (has > -1) ? store.state.chains.chains[c] : store.state.chains.chains.cosmos
if (selected.sdk_version === undefined) {
const v = requestVersion(c, `${selected.api}/node_info`)
selected.sdk_version = v
}
this.config = selected
this.handler = new DefaultHandler()
getSelectedConfig() {
this.config = store.state.chains.selected
return this.config
}
async getStakingPool() {
return this.get('/staking/pool').then(data => new StakingPool(commonProcess(data)))
return this.get('/staking/pool').then(data => new StakingPool().init(commonProcess(data)))
}
async getValidatorList() {
return this.get('/staking/validators').then(data => commonProcess(data).map(i => new Validator().init(i)))
}
async getGovernanceTally(pid, total) {
return this.get(`/gov/proposals/${pid}/tally`).then(data => new ProposalTally(commonProcess(data), total))
return this.get(`/gov/proposals/${pid}/tally`).then(data => new ProposalTally().init(commonProcess(data), total))
}
getGovernance(pid) {
return this.get(`/gov/proposals/${pid}`).then(data => {
const p = new Proposal().init(commonProcess(data), 0)
p.versionFixed(this.config.sdk_version)
return p
})
}
async getGovernanceProposer(pid) {
return this.get(`/gov/proposals/${pid}/proposer`).then(data => new Proposer().init(commonProcess(data))).catch(e => console.log(e))
}
async getGovernanceDeposits(pid) {
return this.get(`/gov/proposals/${pid}/deposits`).then(data => {
const result = commonProcess(data)
return Array.isArray(result) ? result.reverse().map(d => new Deposit().init(d)) : result
})
}
async getGovernanceVotes(pid) {
return this.get(`/gov/proposals/${pid}/votes`).then(data => commonProcess(data).map(d => new Votes().init(d)))
}
async getGovernanceList() {
return Promise.all([this.get('/gov/proposals'), this.get('/staking/pool')]).then(data => {
const pool = new StakingPool(commonProcess(data[1]))
const pool = new StakingPool().init(commonProcess(data[1]))
const ret = []
commonProcess(data[0]).forEach(e => {
const g = new Proposal(e, pool.bondedToken)
const g = new Proposal().init(e, pool.bondedToken)
g.versionFixed(this.config.sdk_version)
ret.push(g)
})
@ -53,8 +70,9 @@ const chainAPI = class ChainFetch {
}
async get(url) {
const response = await fetch(this.config.api + url)
return response.json()
this.getSelectedConfig()
const ret = await fetch(this.config.api + url).then(response => response.json()).catch(e => console.log(e))
return ret
}
}

View File

@ -6,13 +6,14 @@ import messages from '@/lang'
import VueI18n from 'vue-i18n'
import ChainAPI from '@/libs/fetch'
import router from './router'
import store from './store'
import App from './App.vue'
// Global Components
import './global-components'
import '@/libs/fetch'
// 3rd party plugins
import '@/libs/portal-vue'
@ -41,6 +42,7 @@ require('@core/scss/core.scss')
require('@/assets/scss/style.scss')
Vue.config.productionTip = false
Vue.prototype.$http = new ChainAPI()
new Vue({
router,

View File

@ -29,10 +29,10 @@ function processMenu() {
header: 'blockchains',
},
]
Object.keys(store.state.chains.chains).forEach(chain => {
Object.keys(store.state.chains.config).forEach(chain => {
const menu = {
title: chain,
icon: 'PieChartIcon',
icon: store.state.chains.config[chain].logo,
}
const children = []
modules.forEach(m => {

View File

@ -1,5 +1,7 @@
import Vue from 'vue'
import VueRouter from 'vue-router'
import fetch from 'node-fetch'
import store from '../store'
Vue.use(VueRouter)
@ -24,20 +26,6 @@ const router = new VueRouter({
],
},
},
{
path: '/second-page',
name: 'second-page',
component: () => import('@/views/Info.vue'),
meta: {
pageTitle: 'Second Page',
breadcrumb: [
{
text: 'Second Page',
active: true,
},
],
},
},
// chain modules
{
path: '/:chain/info',
@ -79,6 +67,28 @@ const router = new VueRouter({
text: 'Governance',
active: true,
},
{
text: 'Proposals',
active: true,
},
],
},
},
{
path: '/:chain/gov/:proposalid',
name: 'proposal',
component: () => import('@/views/ProposalView.vue'),
meta: {
pageTitle: 'Governance',
breadcrumb: [
{
text: 'Governance',
active: true,
},
{
text: 'Proposal Detail',
active: true,
},
],
},
},
@ -105,6 +115,26 @@ const router = new VueRouter({
],
})
router.beforeEach((to, from, next) => {
const c = to.params.chain
const has = Object.keys(store.state.chains.config).findIndex(i => i === c)
if (has > -1) {
const chain = store.state.chains.config[c]
store.commit('select', { chain_name: c })
if (chain.sdk_version === undefined) {
fetch(`${chain.api}/node_info`)
.then(res => res.json())
.then(json => {
const sdk = json.application_version.build_deps.find(e => e.startsWith('github.com/cosmos/cosmos-sdk'))
const re = /(\d+(\.\d+)*)/i
const version = sdk.match(re)
store.commit('setup_sdk_version', { chain_name: c, version: version[0] })
})
}
}
next()
})
// ? For splash screen
// Remove afterEach hook if you are not using splash screen
router.afterEach(() => {

View File

@ -0,0 +1,7 @@
{
"chain_name": "crypto",
"chain_id": "akashnet-1",
"api": "https://mainnet.crypto.org:1317",
"logo": "https://look.ping.pub/static/chains/cosmoshub.svg"
}

View File

@ -9,14 +9,18 @@ configs.keys().forEach(k => {
export default {
namespaced: true,
state: {
chains,
config: chains,
selected: {},
},
getters: {
getchains: state => state.chains,
},
mutations: {
setup_sdk_version(state, info) {
state.chains.chains[info.chain_name].sdk_version = info.version
state.chains.config[info.chain_name].sdk_version = info.version
},
select(state, args) {
state.chains.selected = state.chains.config[args.chain_name]
},
},

View File

@ -37,7 +37,7 @@
>
Rejected
</b-badge>
#{{ p.id }}. {{ p.title }}
#{{ p.id }}. <a :href="`./gov/${p.id}`">{{ p.title }}</a>
</b-card-title>
<b-card-body md="12">
<div class="gov-wrapper">
@ -57,14 +57,6 @@
{{ p.voting_end_time }}
</h6>
</div>
<!-- <div class="gov">
<p class="card-text mb-25">
Proposer
</p>
<h6 class="mb-0">
Ping.pub
</h6>
</div> -->
<div class="gov">
<p class="card-text mb-25">
Deposit
@ -136,17 +128,26 @@
{{ p.tally.abstain }}% voted Abstain
</b-tooltip>
<b-card-footer>
<b-button
<router-link
v-ripple.400="'rgba(113, 102, 240, 0.15)'"
:to="`./gov/${p.id}`"
variant="outline-primary"
class="btn"
>
Detail
</b-button>
<b-button
v-ripple.400="'rgba(113, 102, 240, 0.15)'"
:href="`./gov/${p.id}`"
variant="outline-primary"
>
{{ $t('btn_detail') }}
</b-button>
</router-link>
<b-button
:disabled="p.status!=2"
variant="primary"
class="btn float-right mg-2"
>
Vote
{{ $t('btn_vote') }}
</b-button>
</b-card-footer>
</b-card>
@ -158,7 +159,7 @@ import {
BCard, BCardTitle, BCardBody, BCardFooter, BButton, BProgressBar, BProgress, BBadge, BTooltip,
} from 'bootstrap-vue'
import Ripple from 'vue-ripple-directive'
import ChainAPI from '@/libs/fetch'
import { Proposal } from '@/libs/data'
export default {
components: {
@ -177,43 +178,64 @@ export default {
},
data() {
return {
proposals: [new Proposal()],
values: [15, 50, 10, 5],
max: 1,
}
},
computed: {
selected_c() {
console.log(localStorage.getItem('selected_chain'))
return localStorage.getItem('selected_chain')
},
// this.$http.get()
mounted() {
this.getList()
},
asyncComputed: {
proposals: {
get() {
const api = new ChainAPI(this.$route)
return api.getGovernanceList().then(res => {
const voting = res.filter(i => i.status === 2)
if (voting.length > 0) {
let i = 0
Promise.all(voting.reverse().map(p => api.getGovernanceTally(p.id, p.tally.total))).then(update => {
this.proposals.map(x => {
if (x.status === 2) {
const xh = x
xh.tally = update[i]
i += 1
return xh
}
return x
})
methods: {
getList() {
// this.$http.setup(this.$route)
this.$http.getGovernanceList().then(res => {
const voting = res.filter(i => i.status === 2)
if (voting.length > 0) {
let i = 0
Promise.all(voting.reverse().map(p => this.$http.getGovernanceTally(p.id, p.tally.total))).then(update => {
this.proposals.map(x => {
if (x.status === 2) {
const xh = x
xh.tally = update[i]
i += 1
return xh
}
return x
})
}
return res.reverse()
})
},
// default: 'loading...',
})
}
this.proposals = res.reverse()
})
},
},
// asyncComputed: {
// proposals: {
// get() {
// const api = new ChainAPI(this.$route)
// api.setup(this.$route)
// return api.getGovernanceList().then(res => {
// const voting = res.filter(i => i.status === 2)
// if (voting.length > 0) {
// let i = 0
// Promise.all(voting.reverse().map(p => api.getGovernanceTally(p.id, p.tally.total))).then(update => {
// this.proposals.map(x => {
// if (x.status === 2) {
// const xh = x
// xh.tally = update[i]
// i += 1
// return xh
// }
// return x
// })
// })
// }
// return res.reverse()
// })
// },
// // default: 'loading...',
// },
// },
// watch: {
// proposals(val, newdata) {
// console.log('In watch', val, newdata)
@ -251,6 +273,6 @@ section {
}
.gov-wrapper .gov {
width: 8.4rem;
width: 9.8rem;
}
</style>

View File

@ -8,7 +8,6 @@
<script>
import { BCard, BCardText } from 'bootstrap-vue'
// import fetch from 'node-fetch'
import '@/libs/fetch'
export default {
components: {
@ -26,14 +25,6 @@ export default {
// }).catch(e => {
// console.log(e)
// })
this.fetchdata('http://lcd.akash.forbole.com/node_info', {
Headers: {
'Content-Type': 'text/html',
},
})
.then(response => {
console.log(response)
})
},
}
</script>

357
src/views/ProposalView.vue Normal file
View File

@ -0,0 +1,357 @@
<template>
<section>
<b-card>
<b-card-header>
<b-card-title>
<b-badge
v-if="proposal.status == 1"
pill
variant="light-info"
class="text-right"
>
Deposit
</b-badge>
<b-badge
v-if="proposal.status == 2"
pill
variant="light-primary"
class="text-right"
>
Voting
</b-badge>
<b-badge
v-if="proposal.status == 3"
pill
variant="light-success"
class="text-right"
>
Passed
</b-badge>
<b-badge
v-if="proposal.status == 4"
pill
variant="light-danger"
class="text-right"
>
Rejected
</b-badge>
#{{ proposal.id }} {{ proposal.title }}
</b-card-title>
</b-card-header>
<b-card-body>
<b-table-simple>
<b-tr>
<b-td lg="2">
{{ $t('proposal_id') }}
</b-td><b-td>{{ proposal.id }}</b-td>
</b-tr>
<b-tr>
<b-td>
{{ $t('proposal_proposer') }}
</b-td><b-td>{{ proposer.proposer }} </b-td>
</b-tr>
<b-tr>
<b-td>
{{ $t('proposal_total_deposit') }}
</b-td><b-td>{{ proposal.total_deposit }} </b-td>
</b-tr>
<b-tr>
<b-td>
{{ $t('proposal_submit_time') }}
</b-td><b-td>{{ proposal.submit_time }}</b-td>
</b-tr>
<b-tr>
<b-td>
{{ $t('proposal_voting_start_time') }}
</b-td><b-td>{{ proposal.voting_start_time }}</b-td>
</b-tr>
<b-tr>
<b-td>
{{ $t('proposal_voting_end_time') }}
</b-td><b-td>{{ proposal.voting_end_time }}</b-td>
</b-tr>
<b-tr>
<b-td>
{{ $t('proposal_type') }}
</b-td><b-td>{{ proposal.type }}</b-td>
</b-tr>
<b-tr
v-for="(value, name) in proposal.contents"
:key="name"
>
<b-td style="text-transform: capitalize; vertical-align: top;">
{{ name.replaceAll('_',' ') }}
</b-td>
<b-td v-if="!Array.isArray(value)">
{{ value }}
</b-td>
<b-td v-if="Array.isArray(value) && name === 'amount'">
<span
v-for="token in value"
:key="token.amount"
>
{{ token.amount }} {{ token.denom }}
</span>
</b-td>
<b-td v-if="Array.isArray(value) && name != 'amount'">
<b-table :items="value">
<!-- A custom formatted column -->
<template #cell(amount)="data">
<span
v-for="token in data.value"
:key="token.amount"
>
{{ token.amount }} {{ token.denom }}
</span>
</template>
</b-table>
</b-td>
</b-tr>
</b-table-simple>
</b-card-body>
<b-card-footer>
<router-link :to="`../gov`">
<b-button
variant="outline-primary"
>
{{ $t('btn_back_list') }}
</b-button>
</router-link>
<b-button
:disabled="proposer.status!=2"
variant="primary"
class="btn float-right mg-2"
>
{{ $t('btn_vote') }}
</b-button>
</b-card-footer>
</b-card>
<b-card>
<b-card-header>
<b-card-title>
Votes
</b-card-title>
</b-card-header>
<b-card-body>
<b-progress
:max="100"
height="2rem"
class="mb-2"
show-progress
>
<b-progress-bar
:id="'vote-yes'+proposal.id"
variant="success"
:value="proposal.tally.yes"
show-progress
/>
<b-progress-bar
:id="'vote-no'+proposal.id"
variant="warning"
:value="proposal.tally.no"
show-progress
/>
<b-progress-bar
:id="'vote-veto'+proposal.id"
variant="danger"
:value="proposal.tally.veto"
show-progress
/>
<b-progress-bar
:id="'vote-abstain'+proposal.id"
variant="info"
:value="proposal.tally.abstain"
show-progress
/>
</b-progress>
<b-tooltip
:target="'vote-yes'+proposal.id"
>
{{ proposal.tally.yes }}% voted Yes
</b-tooltip>
<b-tooltip
:target="'vote-no'+proposal.id"
>
{{ proposal.tally.no }}% voted No
</b-tooltip>
<b-tooltip
:target="'vote-veto'+proposal.id"
>
{{ proposal.tally.veto }}% voted No With Veta
</b-tooltip>
<b-tooltip
:target="'vote-abstain'+proposal.id"
>
{{ proposal.tally.abstain }}% voted Abstain
</b-tooltip>
<b-table
:fields="votes_fields"
:items="votes"
/>
</b-card-body>
</b-card>
<b-card>
<b-card-header>
<b-card-title>
Deposits ({{ proposal.total_deposit }})
</b-card-title>
</b-card-header>
<b-card-body>
<b-table
:items="deposits"
:fields="deposit_fields"
/>
</b-card-body>
<b-card-footer>
<router-link :to="`../gov`">
<b-button
variant="outline-primary"
>
{{ $t('btn_back_list') }}
</b-button>
</router-link>
<b-button
:disabled="proposer.status!=2"
variant="primary"
class="btn float-right mg-2"
>
{{ $t('btn_vote') }}
</b-button>
</b-card-footer>
</b-card>
</section>
</template>
<script>
import {
BCard, BCardBody, BCardFooter, BButton, BTable, BTableSimple, BTr, BTd, BCardTitle, BCardHeader,
BProgressBar, BProgress, BTooltip, BBadge,
} from 'bootstrap-vue'
// import fetch from 'node-fetch'
import { tokenFormatter } from '@/libs/data/data'
import { Proposal, Proposer } from '@/libs/data'
// import { formatToken } from '@/libs/data/data'
export default {
components: {
BCard,
BCardTitle,
BCardBody,
BCardFooter,
BButton,
BTable,
BTableSimple,
BCardHeader,
BTr,
BTd,
BProgressBar,
BProgress,
BTooltip,
BBadge,
},
data() {
return {
proposal: new Proposal(),
proposer: new Proposer(),
deposits: [],
votes: [],
votes_fields: [
{ key: 'voter', sortable: true },
{
key: 'option',
sortable: true,
formatter: value => {
switch (value) {
case 1:
return 'Yes'
case 2:
return 'Abstain'
case 3:
return 'No'
case 4:
return 'No With Veto'
default:
return value
}
},
},
],
deposit_fields: [
'depositor',
{
key: 'amount',
sortable: true,
formatter: tokenFormatter,
},
],
}
},
created() {
const pid = this.$route.params.proposalid
this.$http.getGovernance(pid).then(p => {
if (p.status === 2) {
this.$http.getGovernanceTally(pid, 0).then(t => p.updateTally(t)).catch(e => console.log('failed on update voting tally:', e))
}
this.proposal = p
})
this.$http.getGovernanceProposer(pid).then(res => {
this.proposer = res
})
this.$http.getGovernanceDeposits(pid).then(res => {
this.deposits = res
})
this.$http.getGovernanceVotes(pid).then(res => {
this.votes = res
})
},
// asyncComputed: {
// proposal: {
// get() {
// const pid = this.$route.params.proposalid
// // const api = new ChainAPI(this.$route)
// return this.$http.getGovernance(pid).then(p => {
// if (p.status === 2) {
// this.$http.getGovernanceTally(pid, 0).then(t => p.updateTally(t)).catch(e => console.log('failed on update voting tally:', e))
// }
// return p
// })
// },
// default: new Proposal(),
// },
// proposer: {
// get() {
// const pid = this.$route.params.proposalid
// // const api = new ChainAPI(this.$route)
// return this.$http.getGovernanceProposer(pid)
// },
// default: new Proposer(),
// },
// deposits: {
// get() {
// const pid = this.$route.params.proposalid
// // const api = new ChainAPI(this.$route)
// return this.$http.getGovernanceDeposits(pid)
// },
// default: [],
// },
// votes: {
// get() {
// const pid = this.$route.params.proposalid
// // const api = new ChainAPI(this.$route)
// return this.$http.getGovernanceVotes(pid)
// },
// default: [],
// },
// },
}
</script>
<style>
</style>

View File

@ -6,7 +6,8 @@
>
<b-table
responsive="sm"
:items="items"
:items="delegations"
:fields="validator_fields"
/>
</b-card-code>
@ -16,41 +17,90 @@
>
<b-table
responsive="sm"
:items="items"
/>
striped
:items="validators"
:fields="validator_fields"
:sort-desc="true"
sort-by="tokens"
>
<!-- Column: Validator -->
<template #cell(description)="data">
<b-media vertical-align="center">
<template #aside>
<b-avatar
size="32"
icon="ChevronRightIcon"
variant="light-primary"
:src="data.item.avatar"
/>
</template>
<span class="font-weight-bold d-block text-nowrap">
{{ data.item.description.moniker }}
</span>
<small class="text-muted">{{ data.item.description.website || data.item.description.identity }}</small>
</b-media>
</template>
</b-table>
</b-card-code>
</div>
</template>
<script>
import { BTable } from 'bootstrap-vue'
import { BTable, BMedia, BAvatar } from 'bootstrap-vue'
import BCardCode from '@core/components/b-card-code/BCardCode.vue'
import { Validator, percent } from '@/libs/data'
import { keybase } from '@/libs/fetch'
export default {
components: {
BCardCode,
BTable,
BMedia,
BAvatar,
},
data() {
return {
items: [
sortBy: 'tokens',
sortDesc: true,
validators: [new Validator()],
delegations: [new Validator()],
validator_fields: [
{ key: 'description', label: 'Validator', sortable: true },
{
age: 40, first_name: 'Dickerson', last_name: 'Macdonald', Occupation: 'Job',
key: 'tokens',
sortable: true,
formatter: value => parseInt(value / 100000, 0),
tdClass: 'text-right',
thClass: 'text-right',
sortByFormatted: true,
},
{
age: 21, first_name: 'Larsen', last_name: 'Shaw', Occupation: 'Job',
},
{
age: 89, first_name: 'Geneva', last_name: 'Wilson', Occupation: 'Bussiness',
},
{
age: 38, first_name: 'Jami', last_name: 'Carney', Occupation: 'Bussiness',
},
{
age: 40, first_name: 'James', last_name: 'Thomson', Occupation: 'Job',
key: 'commission',
sortable: true,
formatter: value => `${percent(value.rate)}%`,
tdClass: 'text-right',
thClass: 'text-right',
},
{ key: 'delegator_shares', sortable: true },
],
}
},
created() {
this.$http.getValidatorList().then(res => {
this.validators = res
this.validators.forEach(i => {
if (i.description.identity) {
keybase(i.description.identity).then(d => {
if (Array.isArray(d.them) && d.them.length > 0) {
console.log(d.them[0].pictures.primary.url)
const validator = this.validators.find(u => u.description.identity === i.description.identity)
validator.avatar = d.them[0].pictures.primary.url
}
})
}
})
})
},
}
</script>