Edit in JSFiddle

<script src="https://unpkg.com/vue@1.0/dist/vue.js"></script>

<div id="app">
  <currency-input 
    label="Price" 
    v-bind:value.sync="price"
  ></currency-input>
  <currency-input 
    label="Shipping" 
    v-bind:value.sync="shipping"
  ></currency-input>
  <currency-input 
    label="Handling" 
    v-bind:value.sync="handling"
  ></currency-input>
  <currency-input 
    label="Discount" 
    v-bind:value.sync="discount"
  ></currency-input>
  
  <p>Total: ${{ total }}</p>
</div>
var currencyValidator = {
	format: function (number) {
  	return (Math.trunc(number * 100) / 100).toFixed(2)
  },
	parse: function (newString, oldNumber) {
  	var CleanParse = function (value) {
    	return { value: value }
    }
  	var CurrencyWarning = function (warning, value) {
    	return { 
      	warning: warning,
        value: value,
      	attempt: newString 
      }
    }
    var NotAValidDollarAmountWarning = function (value) {
    	return new CurrencyWarning(newString + ' is not a valid dollar amount', value)
    }
    var AutomaticConversionWarning = function (value) {
    	return new CurrencyWarning(newString + ' was automatically converted to ' + value, value)
    }
  
  	var newNumber = Number(newString)
    var indexOfDot = newString.indexOf('.')
    var indexOfE = newString.indexOf('e')
    
    if (isNaN(newNumber)) {	
    	if (
      	indexOfDot === -1 &&
        indexOfE > 0 &&
      	indexOfE === newString.length - 1 &&
        Number(newString.slice(0, indexOfE)) !== 0
      ) {
      	return new CleanParse(oldNumber)
      } else {
        return new NotAValidDollarAmountWarning(oldNumber)
	    }
    }
    
    var newCurrencyString = currencyValidator.format(newNumber)
    var newCurrencyNumber = Number(newCurrencyString)

    if (newCurrencyNumber === newNumber) {
			if (indexOfE !== -1 && indexOfE === newString.length - 2) {
      	return new AutomaticConversionWarning(newNumber)
      } else {
      	return new CleanParse(newNumber)
      }
    } else {
    	return new NotAValidDollarAmountWarning(
				newNumber > newCurrencyNumber
          ? newCurrencyNumber
          : oldNumber
      )
    }
  }
}

Vue.component('currency-input', {
	template: '\
  	<div>\
    	<label v-if="label">{{ label }}</label>\
      $\
      <input\
      	v-el:input\
        v-model="value | currency"\
        v-on:focus="$event.target.select()"\
      >\
    </div>\
  ',
  props: {
  	value: {
    	type: Number,
      default: 0,
      twoWay: true
    },
    label: {
    	type: String,
      default: ''
    }
  },
  filters: {
    currency: {
    	read: currencyValidator.format,
      write (newValue, oldValue) {
      	var result = currencyValidator.parse(newValue, Number(oldValue))
        if (result.warning) {
        	this.$els.input.value = result.value
        }
        return result.value
      }
    }
  }
})

new Vue({
  el: '#app',
  data: {
    price: 0,
    shipping: 0,
    handling: 0,
    discount: 0
  },
  computed: {
    total: function () {
      return ((
      	this.price * 100 + 
        this.shipping * 100 + 
        this.handling * 100 - 
        this.discount * 100
      ) / 100).toFixed(2)
    }
  }
})