vote:'投票',
votes:'票数'
},
+ listCancel:{
+ voted:'已投票',
+ cancel:'可取消',
+ selectVote:'选择节点'
+ },
vote:{
selectNode:'选择节点'
},
vote:'Vote',
votes:'Votes'
},
+ listCancel:{
+ voted:'Voted',
+ cancel:'Cancel',
+ selectVote:'Choose to vote'
+ },
vote:{
selectNode:'Select Node'
},
float: right;
}
+.text-aglin-right {
+ text-aglin: right;
+}
+
h1,
h2 {
font-weight: 500;
}
},
{
+ path: '/listCancel',
+ name: 'listCancel',
+ meta: { title: '选择节点' },
+ component: resolve => {
+ require(['@/views/vote/listCancel.vue'], resolve)
+ }
+ },
+ {
path: '/vote',
name: 'vote',
meta: { title: '投票' },
}
},
{
+ path: '/veto',
+ name: 'veto',
+ meta: { title: '取消投票' },
+ component: resolve => {
+ require(['@/views/vote/veto.vue'], resolve)
+ }
+ },
+ {
path: '/crossChain',
name: 'cross-chain',
meta: { title: '跨链' },
export const actions = {
[Actions.SET_BYTOM]:({commit}, bytom) => commit(Actions.SET_BYTOM, bytom),
+ [Actions.SET_LIST_VOTE]:({commit}, listVote) => commit(Actions.SET_LIST_VOTE, listVote),
+ [Actions.SET_SELECTED_VOTE]:({commit}, selectVote) => commit(Actions.SET_SELECTED_VOTE, selectVote),
- [Actions.LOAD_BYTOM]:({dispatch}) => {
+
+ [Actions.LOAD_BYTOM]:({dispatch}) => {
return new Promise((resolve, reject) => {
InternalMessage.signal(InternalMessageTypes.LOAD).send().then(_bytom => {
dispatch(Actions.SET_BYTOM, Bytom.fromJson(_bytom));
export const CREATE_NEW_BYTOM_ACCOUNT = 'createNewAccount';
export const IMPORT_BYTOM = 'importBytom';
export const SET_AUTO_LOCK = 'setAutoLock';
+export const SET_LIST_VOTE = 'setListVote';
+export const SET_SELECTED_VOTE = 'setSelectVote';
export const LOCK = 'lock';
export const DESTROY = 'destroy';
[Mutations.PUSH_ALERT_RESULT]:(state, alertResult) => state.alertResult = alertResult,
[Mutations.CLEAR_ALERT_RESULT]:(state) => state.alertResult = null,
[Mutations.PUSH_PROMPT]:(state, prompt) => state.prompt = prompt,
- // [Mutations.SET_AUTO_LOCK]:(state, inactivityInterval) =>
+ [Mutations.SET_LIST_VOTE]:(state, listVote) => state.listVote = listVote,
+ [Mutations.SET_SELECTED_VOTE]:(state, selectVote) => state.selectVote = selectVote,
+
+ // [Mutations.SET_AUTO_LOCK]:(state, inactivityInterval) =>
// state.bytom.settings.inactivityInterval = TimingHelpers.minutes(inactivityInterval),
};
alertResult:null,
prompt:null,
+ listVote:[],
+ selectVote: null,
};
const getters = {
if(host.indexOf('www.') === 0) host = host.replace('www.', '');
return host;
-};
\ No newline at end of file
+};
display:flex;
}
.topbar .topbar-left {
- width: 115px;
+ width: 85px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
height: 48px;
line-height: 23px;
font-size: 16px;
+ padding: 12px 10px;
}
.btn-received {
width: 90%;
}
-.list-item .value {
- float: right;
- margin-top: 13px;
-}
.account-address {
cursor: pointer;
}
</div>
<div class="topbar-middle bg-secondary">
<select :value="netType" @change="netTypeToggle">
- <option value="bytom">{{ $t('main.bytom') }}</option>
- <option value="vapor">{{ $t('main.vapor') }}</option>
+ <option value="bytom">{{ $t('main.bytom') }} {{net}}</option>
+ <option value="vapor">{{ $t('main.vapor') }} {{net}}</option>
</select>
</div>
</div>
<vue-scroll @handle-scroll="handleScroll">
<ul class="list">
<li class="list-item" v-for="(transaction, index) in transactions" :key="index" @click="$router.push({name: 'transfer-info', params: {transaction: transaction, address: currentAccount.address}})">
- <div class="value">{{transaction.direct}} {{transaction.val.toFixed(2)}} BTM</div>
+ <div class="float-right text-aglin-right">
+ <div class="value">{{transaction.direct}} {{transaction.val.toFixed(2)}} BTM</div>
+ <div v-if="transaction.type == 'vote'" class="addr color-grey">{{ $t('listVote.vote')}} {{transaction.vAmount}}</div>
+ <div v-else-if="transaction.type == 'veto'" class="addr color-grey">{{ $t('listVote.cancelVote')}} {{transaction.vAmount}}</div>
+ <div v-else-if="transaction.type == 'crossChain'" class="addr color-grey">{{ $t('crossChain.title')}}</div>
+ </div>
<div>
<div v-if="transaction.hasOwnProperty('block_timestamp')">
{{transaction.submission_timestamp | moment}}
import { mapActions, mapGetters, mapState } from 'vuex'
import * as Actions from '@/store/constants';
import _ from 'lodash';
+import { Number as Num } from "@/utils/Number"
const EnterActive = 'animated faster fadeInLeft';
}
},
refreshBalance: function (guid) {
+ if(guid){
account.balance(guid)
.then((obj)=>{
const balances = obj.balances
.catch(error => {
console.log(error);
});
+ }
},
refreshTransactions: function (guid, address, start, limit) {
return new Promise((resolve, reject) => {
const balanceObject = transaction.balances
.filter(b => b.asset === assetID);
- if(balanceObject.length ===1 ){
+ const filterInput = _.find(transaction.inputs, function(o) { return o.type =='veto'; })
+
+ if(filterInput){
+ transaction.type = 'veto'
+ const inAmount = _.sumBy((transaction.inputs.filter(i => i.type ==='veto')), 'amount')
+ const outAmount = _.sumBy((transaction.outputs.filter(i => i.type ==='vote')), 'amount')
+ transaction.vAmount = Num.formatNue(inAmount-outAmount)
+ }else if(_.find(transaction.outputs, function(o) { return o.type =='vote'; })){
+ const outAmount = _.sumBy((transaction.outputs.filter(i => i.type ==='vote')), 'amount')
+ transaction.vAmount = Num.formatNue(outAmount)
+ transaction.type = 'vote'
+ }else if(_.find(transaction.outputs, function(o) { return o.type =='crosschain_output'; })){
+ transaction.type = 'crossChain'
+ }
+ if(balanceObject.length ===1 ){
const inputAddresses = transaction.inputs
.filter(input => input.asset === assetID && input.address !== this.address)
.map(input => input.address)
--- /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 {
+ border-radius:4px;
+ height: 505px;
+ overflow: scroll;
+ padding: 20px 0px;
+}
+
+.votes{
+ width: 100%;
+}
+
+.vote-item> td{
+ padding: 12px 15px;
+ border-bottom: 1px solid #F0F0F0;
+ cursor: pointer;
+}
+ .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;
+ }
+
+</style>
+
+<template>
+ <div class="warp-chlid bg-gray">
+ <section class="header bg-header">
+ <i class="iconfont icon-back" @click="close"></i>
+ <p>{{ $t('listCancel.selectVote') }}</p>
+ </section>
+
+ <section class="vote-container bg-white">
+ <div class="vote-list">
+ <table class="list votes">
+ <tr class="vote-item" v-for="(vote, index) in filteredList" :key="index" @click="openVeto(vote)">
+ <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('listCancel.voted')}} {{formatNue(vote.total)}}</div>
+ </td>
+ <td>
+ <div class="vote-title">{{formatNue(vote.total-vote.locked)}} </div>
+ <div class="vote-number"> {{$t('listCancel.cancel')}} </div>
+ </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'
+import _ from 'lodash';
+import * as Actions from '@/store/constants';
+
+
+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() {
+ let vote
+ const votes = this.currentAccount.votes
+ if(votes && votes.length >0 ){
+ vote = _.sumBy(votes,'total')
+ }
+ return (vote != null && vote != 0) ? Number.formatNue(vote) : '0.00'
+ },
+ filteredList() {
+ return this.votes.filter(post => {
+ return post.name.toLowerCase().includes(this.search.toLowerCase())
+ })
+ },
+ ...mapState([
+ 'bytom',
+ 'listVote'
+ ]),
+ ...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);
+ },
+ openVeto: function(veto){
+ this[Actions.SET_SELECTED_VOTE](veto);
+ this.$router.push({name: 'veto'});
+ },
+ ...mapActions([
+ Actions.SET_SELECTED_VOTE,
+ ])
+ },
+ mounted() {
+ const originVotes = this.currentAccount.votes
+ const allVotes = this.listVote;
+ this.votes = _.map(originVotes, function(obj) {
+ return _.assign(obj, _.find(allVotes, {pub_key: obj.vote}));
+ });
+ }
+};
+</script>
<section class="vote-container bg-white">
<div>
- <button>{{ $t('listVote.voteRecord')}}</button>
<button>{{ $t('listVote.voteRules')}}</button>
- <button>{{ $t('listVote.cancelVote')}}</button>
+ <router-link :to="{name: 'listCancel'}">
+ {{ $t('listVote.cancelVote')}}
+ </router-link>
</div>
<div class="vote-label color-black">
<div>
<div class="vote-number">{{$t('listVote.votes')}} {{formatNue(vote.vote_num)}} ({{formatFraction(vote.vote_num, totalVote)}})</div>
</td>
<td>
- <router-link :to="{name: 'vote', params: { vote: vote }}">
+ <button @click="openVote(vote)">
{{$t('listVote.vote')}}
- </router-link>
+ </button>
</td>
</tr>
</table>
import { BTM } from "@/utils/constants";
import Number from "@/utils/Number"
import { mapActions, mapGetters, mapState } from 'vuex'
+import * as Actions from '@/store/constants';
import _ from 'lodash';
export default {
},
data() {
return {
- votes:[],
totalVote:0,
search:''
};
return (vote != null && vote != 0) ? Number.formatNue(vote) : '0.00'
},
filteredList() {
- return this.votes.filter(post => {
+ return this.listVote.filter(post => {
return post.name.toLowerCase().includes(this.search.toLowerCase())
})
},
...mapState([
- 'bytom'
+ 'bytom',
+ 'listVote'
]),
...mapGetters([
'currentAccount',
'accountList',
- 'net'
+ 'net',
])
},
watch: {
},
formatFraction: function (upper, lower) {
return Number.fractionalNum(upper, lower);
- }
+ },
+ openVote: function(vote){
+ this[Actions.SET_SELECTED_VOTE](vote);
+ this.$router.push({name: 'vote'});
+ },
+ ...mapActions([
+ Actions.SET_LIST_VOTE,
+ Actions.SET_SELECTED_VOTE,
+ ])
},
mounted() {
query.chainStatus().then(resp => {
if(resp){
this.totalVote = resp.total_vote_num;
- this.votes = resp.consensus_nodes.map( (item, index) => {
+ const votes = resp.consensus_nodes.map( (item, index) => {
item.rank = index+1;
return item
});
+ this[Actions.SET_LIST_VOTE](votes)
}
});
}
--- /dev/null
+<style lang="" scoped>
+.header {
+ display: flex;
+}
+.header p{
+ text-align: center;
+ width: 270px;
+ padding-top: 17px;
+}
+
+.transferType input[type=radio] {
+ position: absolute;
+ visibility: hidden;
+}
+
+.transferType input[type="radio"]:checked+label{
+ color:white;
+ background-color: #333333;
+}
+
+.form-container{
+ margin: 20px;
+}
+.form {
+ margin-bottom: 20px;
+ padding: 10px 15px;
+ border-radius:4px;
+}
+.form-container .btn{
+ height: 48px;
+ bottom: 20px;
+ position: absolute;
+ width: 320px;
+}
+.vote-title{
+ font-size: 14px;
+ color: #000;
+ vertical-align: middle;
+ align-items: center;
+ display: flex;
+}
+.vote-title img{
+ height: 36px;
+ width: 36px;
+ border:1px solid #E0E0E0;
+ opacity:1;
+ border-radius:4px;
+ margin-right: 15px;
+}
+</style>
+
+<template>
+ <div class="warp-chlid bg-gray">
+ <section class="header bg-header">
+ <i class="iconfont icon-back" @click="close"></i>
+ <p>{{ $t('listVote.cancelVote') }}</p>
+ </section>
+
+ <section class="form-container">
+ <div class="form bg-white">
+ <div v-if="selectVote !== null" class="form-item">
+ <label class="form-item-label">{{ $t('vote.selectNode') }}</label>
+ <div class="form-item-content" >
+ <div class="vote-title" >
+ <img :src="selectVote.logo" alt="">
+ <div>
+ {{selectVote.name}}
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ <div class="form bg-white">
+
+ <div class="form-item">
+ <label class="form-item-label">
+ {{ $t('transfer.quantity') }}
+
+ <small class="float-right" style="margin-right: 8px;">{{ transaction.cost||0 }} CNY</small>
+ </label>
+ <div class="form-item-content" style=" display: flex;">
+ <input type="number" v-model="transaction.amount" :placeholder="bytomBalance">
+ <span class="color-grey" style="width: 40px; font-size: 15px;position: absolute;right: 0;">{{unit}}</span>
+ </div>
+ </div>
+ </div>
+ <a class="btn btn-primary" @click="send">{{ $t('transfer.send') }}</a>
+ </section>
+ </div>
+</template>
+
+<script>
+import transaction from "@/models/transaction";
+import getLang from "@/assets/language/sdk";
+import Confirm from "../sendTransaction/transferConfirm";
+import { BTM } from "@/utils/constants";
+import { mapGetters, mapState } from 'vuex'
+import { Number as Num } from "@/utils/Number"
+
+
+export default {
+ components: {
+ Confirm
+ },
+ data() {
+ return {
+ show: false,
+ accountBalance: 0.00,
+ unit: 'BTM',
+ fee: this.$t("transfer.feeType"),
+ feeTypeOptions: [this.$t("transfer.feeType")],
+ transaction: {
+ amount: "",
+ to:'',
+ confirmations: 1
+ }
+ };
+ },
+ computed: {
+ bytomBalance: function () {
+ const vote = this.selectVote
+ let vetoAmount
+ if(vote && vote.total > vote.locked ){
+ vetoAmount = Num.formatNue(vote.total-vote.locked)
+ }
+
+ return `Vapor${this.$t("crossChain.amountPlaceHolder")}${(vetoAmount != null && vetoAmount != 0) ? vetoAmount : '0.00'}`
+ },
+ ...mapState([
+ 'bytom',
+ 'selectVote'
+ ]),
+ ...mapGetters([
+ 'currentAccount',
+ 'accountList',
+ 'net',
+ 'netType'
+ ])
+ },
+ watch: {
+ "transaction.amount": function (newAmount) {
+ transaction.asset(BTM).then(ret => {
+ this.transaction.cost = Number(ret.cny_price * newAmount).toFixed(2);
+ });
+ },
+ account: function (newAccount) {
+ this.guid = newAccount.guid;
+ }
+ },
+ methods: {
+ close: function () {
+ this.$router.go(-1)
+ this.transaction.vote = "";
+ this.transaction.amount = "";
+ },
+ send: function () {
+ if (this.transaction.amount <= 0) {
+ this.$dialog.show({
+ body: this.$t("transfer.noneBTM")
+ });
+ return;
+ }
+
+ let loader = this.$loading.show({
+ // Optional parameters
+ container: null,
+ canCancel: true,
+ onCancel: this.onCancel
+ });
+
+ const vote = this.selectVote.pub_key
+ this.transaction.to = vote
+ transaction.buildVeto(this.currentAccount.guid, vote, this.transaction.amount*100000000, this.transaction.confirmations).then(result => {
+ loader.hide();
+ console.log(result)
+ this.transaction.fee = Number(result.fee / 100000000);
+ this.$router.push({ name: 'transfer-confirm', params: { account: this.currentAccount, transaction: this.transaction, rawData: result} })
+ }).catch(error => {
+ loader.hide();
+ this.$dialog.show({
+ body: getLang(error.message)
+ });
+ });
+
+ }
+ }, mounted() {
+ }
+};
+</script>
<div class="warp-chlid bg-gray">
<section class="header bg-header">
<i class="iconfont icon-back" @click="close"></i>
- <p>{{ $t('crossChain.title') }}</p>
+ <p>{{ $t('listVote.vote') }}</p>
</section>
<section class="form-container">
<div class="form bg-white">
- <div v-if="vote !== null" class="form-item">
+ <div v-if="selectVote !== null" class="form-item">
<label class="form-item-label">{{ $t('vote.selectNode') }}</label>
<div class="form-item-content" >
<div class="vote-title" >
- <img :src="vote.logo" alt="">
+ <img :src="selectVote.logo" alt="">
<div>
- {{vote.name}}
+ {{selectVote.name}}
</div>
</div>
</div>
unit: 'BTM',
fee: this.$t("transfer.feeType"),
feeTypeOptions: [this.$t("transfer.feeType")],
- vote:null,
transaction: {
amount: "",
to:'',
return `Vapor${this.$t("crossChain.amountPlaceHolder")}${(balance != null && balance != 0) ? balance : '0.00'}`
},
...mapState([
- 'bytom'
+ 'bytom',
+ 'selectVote'
]),
...mapGetters([
'currentAccount',
},
watch: {
"transaction.amount": function (newAmount) {
- transaction.asset(this.transaction.asset).then(ret => {
+ transaction.asset(BTM).then(ret => {
this.transaction.cost = Number(ret.cny_price * newAmount).toFixed(2);
});
},
onCancel: this.onCancel
});
- const vote = this.vote.pub_key
+ const vote = this.selectVote.pub_key
this.transaction.to = vote
transaction.buildVote(this.currentAccount.guid, vote, this.transaction.amount*100000000, this.transaction.confirmations).then(result => {
loader.hide();
}
}, mounted() {
- this.vote = this.$route.params.vote
}
};
</script>