<!--
  Forked from:
  https://quasar.dev/vue-components/input#Example--Design-Overview
-->
<div id="q-app">
  <q-card class="q-ma-sm bg-grey-10" style="max-width: 300px">
    <q-card-section>
      <div class="q-gutter-sm">
        <q-phone-input v-model="landline" outlined dark label="Tel. Fixo"></q-phone-input>
        <q-phone-input v-model="cellphone" outlined dark label="Celular"></q-phone-input>
      </div>
    </q-card-section>
  </q-card>
</div>
<template id="tmpl-q-phone-input">
  <q-input ref="root" v-bind="$attrs" v-on="$listeners" unmasked-value :mask="mask" v-model="__value">
    <slot v-for="(slot, key) in $slots" :name="key" :slot="key" />
    <template v-for="(slot, key) in $scopedSlots" :slot="key" slot-scope="scope">
      <slot :name="key" v-bind="scope"/>
    </template>
  </q-input>
</template>
Vue.component('q-phone-input', {
  name: 'QPhoneInput',
  template: '#tmpl-q-phone-input',
  props: {
    value: String
  },
  watch: {
    mask () {
      let input = this.$refs.root.$refs.input
      requestAnimationFrame(() => {
        input.selectionStart = input.value.length
      })
    }
  },
  computed: {
    mask () {
      switch (true) {
        case (this.value || '').length <= 8: return '####-#####'
        case (this.value || '').length === 9: return '#####-#####'
        case (this.value || '').length === 10: return '(##) ####-#####'
        default: return '(##) #####-####'
      }
    },
    __value: {
      get () { return this.value },
      set (value) { this.$emit('input', value) }
    }
  }
})

new Vue({
  el: '#q-app',
  data () {
    return {
      landline: '',
      cellphone: ''
    }
  }
})
View Compiled

External CSS

  1. https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900|Material+Icons
  2. https://cdn.jsdelivr.net/npm/quasar@1.9.3/dist/quasar.min.css

External JavaScript

  1. https://cdn.jsdelivr.net/npm/vue/dist/vue.js
  2. https://cdn.jsdelivr.net/npm/quasar@1.9.3/dist/quasar.umd.min.js