"integrity": "sha512-+hN/Zh2D08Mx65pZ/4g5bsmNiZUuChDiQfTUQ7qJr4/kuopCr88xZsAXv6mBoZEsUI4OuGHlX59qE94K2mMW8Q==",
"dev": true
},
+ "bignumber.js": {
+ "version": "9.0.0",
+ "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.0.tgz",
+ "integrity": "sha512-t/OYhhJ2SD+YGBQcjY8GzzDHEk9f3nerxjtfa6tlMXfe7frs/WozhvCNoGvpM0P3bNf3Gq5ZRMlGr5f3r4/N8A=="
+ },
"binary-extensions": {
"version": "1.11.0",
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.11.0.tgz",
"animate.css": "^3.7.0",
"axios": "^0.18.0",
"babel-preset-es2015": "^6.24.1",
+ "bignumber.js": "^9.0.0",
"bytom-js-sdk": "^1.4.0",
"clipboard": "^2.0.1",
"extension-streams": "^1.0.7",
toBytom:'转出至\nBytom链',
asset:'选择资产'
},
+ listVote:{
+ title:'节点投票',
+ myVote:'我的投票',
+ totalVote:'全网投票',
+ voteRecord:'投票记录',
+ voteRules:'投票规则',
+ cancelVote:'取消投票',
+ bp:'正式共识节点',
+ standbyBP:'备选共识节点',
+ bpName:'节点名称',
+ vote:'投票',
+ votes:'票数'
+ },
signMessage:{
title:'请求签名',
address: '签名地址',
toBytom:'Transfer to \n Bytom Chain',
asset:'Select Asset'
},
+ listVote:{
+ title:'Block Producer Voting',
+ myVote:'My votes',
+ totalVote:'All votes',
+ voteRecord:'voting history',
+ voteRules:'voting rules',
+ cancelVote:'veto',
+ bp:'BP',
+ standbyBP:'Standby BP',
+ bpName:'BP name',
+ vote:'Vote',
+ votes:'Votes'
+ },
signMessage:{
title:'Request Signature',
address: 'Sign Address',
.listAddressUseServer(guid)
.then(addresses => {
let balances = []
+ let votes = []
addresses.forEach(address => {
if (address.balances != null) {
balances = balances.concat(address.balances)
}
+ if (address.votes != null) {
+ votes = votes.concat(address.votes)
+ }
})
let obj = {};
res.push(obj[prop]);
}
- resolve(res)
+ resolve({
+ balances:res,
+ votes
+ })
})
.catch(error => {
reject(error)
import bytom from "./bytom";
-function query(bytom) {
- this.bytom = bytom;
-}
+let query = {};
+query.chainStatus = function() {
+ return bytom.query.getVoteStatus();
+};
export default query;
}
},
{
+ path: '/listVote',
+ name: 'listVote',
+ meta: { title: '节点展示' },
+ component: resolve => {
+ require(['@/views/vote/listVote.vue'], resolve)
+ }
+ },
+ {
path: '/crossChain',
name: 'cross-chain',
meta: { title: '跨链' },
--- /dev/null
+import BigNumber from "bignumber.js"
+
+export class Number {
+ /***
+ * format num to BTM
+ * @returns number
+ */
+ static formatNue(num) {
+ let n = new BigNumber(num);
+ let base = new BigNumber(100000000)
+
+ let result = n.dividedBy(base)
+
+ return result.toFormat();
+ }
+
+ /***
+ * format num to percent
+ * @returns percentage
+ */
+ static fractionalNum(upper, lower) {
+ let n = new BigNumber(upper).div(lower);
+
+ let result = n.shiftedBy(2).decimalPlaces(2)
+ return result+'%';
+ }
+
+}
+
+export default Number;
<div v-if="netType =='vapor'" class="btn-send-transfer">
- <a v-if="address!=undefined" class="btn btn-primary btn-received" @click="showQrcode">
+ <a v-if="address!=undefined" class="btn btn-primary btn-received" @click="listVoteOpen">
vote
</a>
<a v-if="address!=undefined " class="btn btn-primary btn-transfer" @click="crossChainOpen">
crossChainOpen: function () {
this.$router.push('crossChain')
},
+ listVoteOpen: function () {
+ this.$router.push('listVote')
+ },
handleScroll(vertical, horizontal, nativeEvent) {
if (vertical.process == 0) {
this.start = 0;
},
refreshBalance: function (guid) {
account.balance(guid)
- .then((balances)=>{
- if(!_.isEqual(this.balances, balances)){
- const bytom = this.bytom.clone();
+ .then((obj)=>{
+ const balances = obj.balances
+ const votes = obj.votes
+
+ const balanceNotEqual = !_.isEqual(this.balances, balances)
+ const voteNotEqual = (this.netType === 'vapor' && !_.isEqual(this.currentAccount.votes, votes))
+ if(balanceNotEqual || voteNotEqual){
//update AccountList
+
+ const bytom = this.bytom.clone();
+
const objectIndex = bytom.accountList.findIndex(a => a.guid == this.currentAccount.guid)
- if(this.netType === 'vapor'){
- bytom.currentAccount.vpBalances = balances;
- bytom.accountList[objectIndex].vpBalances = balances
- }else{
- bytom.currentAccount.balances = balances;
- bytom.accountList[objectIndex].balances = balances
+ if(balanceNotEqual){
+ if(this.netType === 'vapor'){
+ bytom.currentAccount.vpBalances = balances;
+ bytom.accountList[objectIndex].vpBalances = balances
+ }else{
+ bytom.currentAccount.balances = balances;
+ bytom.accountList[objectIndex].balances = balances
+ }
}
+ if(voteNotEqual){
+ bytom.currentAccount.votes = votes;
+ bytom.accountList[objectIndex].votes = votes
+ }
this[Actions.UPDATE_STORED_BYTOM](bytom)
}
<section class="form-container">
<div class="form bg-white">
- <div class="form-item">
- <label class="form-item-label">{{ $t('crossChain.asset') }}</label>
- <!--<div class="form-item-content" >-->
- <v-select style="height: 32px;" class="v-select" v-bind:colorBlack="true" :clearable="false" :value="aOptions[0]" :options="aOptions"></v-select>
- <!--</div>-->
- </div>
+ <!--<div class="form-item">-->
+ <!--<label class="form-item-label">{{ $t('crossChain.asset') }}</label>-->
+ <!--<!–<div class="form-item-content" >–>-->
+ <!--<v-select style="height: 32px;" class="v-select" v-bind:colorBlack="true" :clearable="false" :value="aOptions[0]" :options="aOptions"></v-select>-->
+ <!--<!–</div>–>-->
+ <!--</div>-->
<div class="form-item">
<label class="form-item-label">
{{ $t('transfer.quantity') }}
--- /dev/null
+<style lang="" scoped>
+.header {
+ display: flex;
+}
+.header p{
+ text-align: center;
+ width: 270px;
+ padding-top: 17px;
+}
+.my-vote {
+ height: 115px;
+ padding: 20px;
+ display: flex;
+ text-align: center;
+ flex-direction: column;
+ font-size:14px;
+}
+
+.my-vote .vote-number{
+ font-size: 28px;
+ padding: 15px 0;
+}
+
+.vote-list {
+ margin-bottom: 20px;
+ padding: 10px 15px;
+ border-radius:4px;
+ height: 300px;
+ overflow: scroll;
+}
+
+.transfer-header{
+ background: #035BD4;
+}
+.vote-item> td{
+ padding: 12px 5px;
+ border-bottom: 1px solid #F0F0F0;
+}
+ .vote-item img{
+ height: 36px;
+ width: 36px;
+ border:1px solid #E0E0E0;
+ opacity:1;
+ border-radius:4px;
+ margin-right: 15px;
+ }
+ .vote-item .vote-title{
+ font-size: 14px;
+ }
+ .vote-item .vote-number{
+ font-size: 12px;
+ color: #8A8A8A;
+ }
+
+ .vote-title{
+ font-size: 14px;
+ line-height: 36px;
+ vertical-align: middle;
+ align-items: center;
+ display: flex;
+ }
+
+ .bp{
+ background: #F4FBE5;
+ color: #91D303;
+ }
+
+ .stanbybp{
+ background: #FFFAE5;
+ color: #FFCC00;
+ }
+
+ .otherbp{
+ background: #F2F3F4;
+ color: #808A95;
+ }
+ .vote-role{
+ width: 20px;
+ height: 20px;
+ border-radius: 12px;
+ font-size: 12px;
+ line-height: 20px;
+ text-align: center
+ }
+
+ .vote-label{
+ font-size: 14px;
+ padding: 20px;
+ display:flex;
+ border-bottom: 1px solid #F0F0F0;
+
+ }
+
+</style>
+
+<template>
+ <div class="warp-chlid bg-gray">
+ <section class="header bg-header">
+ <i class="iconfont icon-back" @click="close"></i>
+ <p>{{ $t('listVote.title') }}</p>
+ </section>
+
+ <section class="my-vote transfer-header">
+ <div>{{ $t('listVote.myVote') }}</div>
+ <div class="vote-number">{{myVote}}</div>
+ <div>{{ $t('listVote.totalVote')}} {{formatNue(totalVote)}}</div>
+ </section>
+
+ <section class="vote-container bg-white">
+ <div>
+ <button>{{ $t('listVote.voteRecord')}}</button>
+ <button>{{ $t('listVote.voteRules')}}</button>
+ <button>{{ $t('listVote.cancelVote')}}</button>
+ </div>
+ <div class="vote-label color-black">
+ <div>
+ {{ $t('listVote.bp') }}
+ </div>
+ <div>{{ $t('listVote.standbyBP') }}</div>
+ <div class="search-wrapper">
+ <input type="text" v-model="search" placeholder="Search title.."/>
+ </div>
+ </div>
+ <div class="vote-list">
+ <table class="list accounts">
+ <tr class="vote-item" v-for="(vote, index) in filteredList" :key="index">
+ <td>
+ <div :class="voteRole(vote.role)">
+ {{ vote.rank }}
+ </div>
+ </td>
+ <td >
+ <div class="vote-title" >
+ <img :src="vote.logo" alt="">
+ <div v-if="net === 'mainnet'">
+ <a :href="`https://vapor.blockmeta.com/node/${vote.pub_key}`" target="_blank">
+ {{vote.name}}
+ </a>
+ </div>
+ <div v-else>
+ {{vote.name}}
+ </div>
+ </div>
+ <div class="vote-number">{{$t('listVote.votes')}} {{formatNue(vote.vote_num)}} ({{formatFraction(vote.vote_num, totalVote)}})</div>
+ </td>
+ <td><button>{{$t('listVote.vote')}}</button></td>
+ </tr>
+ </table>
+ </div>
+ </section>
+ </div>
+</template>
+
+<script>
+import query from "@/models/query";
+import { BTM } from "@/utils/constants";
+import Number from "@/utils/Number"
+import { mapActions, mapGetters, mapState } from 'vuex'
+
+export default {
+ components: {
+ },
+ data() {
+ return {
+ votes:[],
+ totalVote:0,
+ search:''
+ };
+ },
+ computed: {
+ unit() {
+ return this.assets[this.transaction.asset];
+ },
+ voteRole(){
+ return function (roleNum) {
+ switch (roleNum){
+ case 0:
+ return 'vote-role bp';
+ case 1:
+ return 'vote-role stanbybp';
+ case 2:
+ return 'vote-role otherbp';
+ default:
+ return 'vote-role otherbp';
+ }
+ }
+ },
+ myVote() {
+ if(this.currentAccount.votes && this.currentAccount.votes.length === 0){
+ return 0
+ }else{
+ return 'ddddd'
+ }
+ },
+ filteredList() {
+ return this.votes.filter(post => {
+ return post.name.toLowerCase().includes(this.search.toLowerCase())
+ })
+ },
+ ...mapState([
+ 'bytom'
+ ]),
+ ...mapGetters([
+ 'currentAccount',
+ 'accountList',
+ 'net'
+ ])
+ },
+ watch: {
+
+ },
+ methods: {
+ close: function () {
+ this.$router.go(-1)
+ },
+ formatNue: function (nue) {
+ return Number.formatNue(nue);
+ },
+ formatFraction: function (upper, lower) {
+ return Number.fractionalNum(upper, lower);
+ }
+ }, mounted() {
+ query.chainStatus().then(resp => {
+ if(resp){
+ this.totalVote = resp.total_vote_num;
+ this.votes = resp.consensus_nodes.map( (item, index) => {
+ item.rank = index+1;
+ return item
+ });
+ console.log(this.votes)
+ }
+ });
+ }
+};
+</script>