index.vue 7.58 KB
<template>
  <transition :name="transition ? 'van-slide-bottom' : ''">
    <div v-show="show" :class="cusClass" class="keyboard-backdrop" @click="closeKeyboard">
      <div
        v-show="show"
        :style="style"
        class="number-keyboard"
        @touchstart.stop
        @animationend="onAnimationEnd"
        @webkitAnimationEnd="onAnimationEnd"
      >
        <div v-if="title || showTitleClose" class="number-keyboard__title van-hairline--top">
          <span v-text="title"/>
          <span
            v-if="showTitleClose"
            class="number-keyboard__close"
            @click="onClose"
            v-text="closeButtonText"
          />
        </div>
        <div class="number-keyboard__body">
          <key
            v-for="key in keys"
            :key="key.text"
            :text="key.text"
            :type="key.type"
            @press="onPressKey"
          />
        </div>
      </div>
    </div>
  </transition>
</template>

<style lang="less" scoped rel="stylesheet">
  .keyboard-backdrop {
    -webkit-transition: background-color 150ms ease-in-out;
    transition: background-color 150ms ease-in-out;
    position: fixed;
    top: 0;
    left: 0;
    z-index: 999;
    width: 100%;
    height: 100%;
    background-color: transparent;
    &.active {
      background-color: rgba(0, 0, 0, 0);
    }
  }

  @van-number-keyboard-key-height: 54px;

  .number-keyboard {
    left: 0;
    bottom: 0;
    width: 100%;
    position: fixed;
    user-select: none;
    background-color: @baseColor;
    animation-timing-function: ease-out;

    &__title {
      height: 30px;
      font-size: 13px;
      line-height: 30px;
      text-align: center;
      position: relative;
      color: #38f;
      border-bottom: 1px solid #e5e5e5; /*no*/
      box-shadow: 0px -1px 3px rgba(0,0,0,0.1) /*no*/
    }

    &__body {
      box-sizing: border-box;
    }

    &__close {
      right: 0;
      color: #38f;
      font-size: 14px;
      padding: 0 15px;
      position: absolute;

      &:active {
        background-color: #e8e8e8;
      }
    }
    .key {
      width: calc(100% / 3);
      font-size: 24px;
      font-style: normal;
      text-align: center;
      display: inline-block;
      vertical-align: middle;
      height: @van-number-keyboard-key-height;
      line-height: @van-number-keyboard-key-height;
      border-width: 1px 1px 0 0; /*no*/
      border-bottom: 1px solid #e5e5e5; /*no*/
      border-right: 1px solid #e5e5e5; /*no*/

    }

    .delete {
      font-size: 0;
      background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACwAAAAeCAMAAABg6AyVAAAAbFBMVEUAAAAfHiIdHB4eHR8dHR4eHB4dHB4dHR8gICIdHB4dHB4dHB4dHB8eHh8hISEeHR8fHB8fHR8fHR8fHx8eHiArKyszMzMeHB8eHB8fHR8eHiAeHh4dHB4vLjDY2Nn////b29zKysq9vb28vLzkfBRpAAAAHHRSTlMAK/PW+I/llBv77N1kSCPwWlFAOTMGBb28hHlu08g5sgAAAMlJREFUOMuV1MsWgiAQgGHQyOx+s+sgYO//jnnMGIdDDfwbN99CYEDQFiVEKkolPUG7gl9VTWC31NKuDbVz+Fc1tRJtPDmxS2BS3p5ZC+XXnnbAVoz2WEBCH7uZAalzGoa06whGiznT6sG2xgX4QO2Aej1+KN7XBKL2FvGaMtTWBhbQhtoaYzVQrHKwuGf8hhAPSF5g3xPSt45sCHcouNWx436FGA+RHyQcD35EcUj54U8ff4WYvVi1zLjelUh/OG6XjOeLWv5hfAOI+HLwwOAqhAAAAABJRU5ErkJggg==") no-repeat center center;
      background-size: auto 15px;
    }

    .gray {
      background-color: #f3f3f6;
    }

    .active {
      background-color: #e8e8e8;
    }
  }

  @keyframes van-slide-bottom-enter {
    from {
      transform: translate3d(0, 100%, 0);
    }
  }

  @keyframes van-slide-bottom-leave {
    to {
      transform: translate3d(0, 100%, 0);
    }
  }

  @keyframes van-fade-in {
    from {
      opacity: 0;
    }

    to {
      opacity: 1;
    }
  }

  @keyframes van-fade-out {
    from {
      opacity: 1;
    }

    to {
      opacity: 0;
    }
  }

  @keyframes van-rotate {
    from {
      transform: rotate(0deg);
    }

    to {
      transform: rotate(360deg);
    }
  }

  .van-fade {
    &-enter-active {
      animation: .3s van-fade-in;
    }

    &-leave-active {
      animation: .3s van-fade-out;
    }
  }

  .van-slide-bottom {
    &-enter-active {
      animation: van-slide-bottom-enter .3s both ease;
    }

    &-leave-active {
      animation: van-slide-bottom-leave .3s both ease;
    }
  }

  // iPhoneX适配
  @media (device-width: 375px) and (device-height: 812px) and (-webkit-min-device-pixel-ratio: 3) {
    .platform-ios {
      .number-keyboard {
        &__body {
          margin-bottom: 34px; /*no*/
        }
      }
    }
  }
  // iPhoneX Max适配
  @media (device-width: 414px) and (device-height: 896px) {
    .platform-ios {
      .number-keyboard {
        &__body {
          margin-bottom: 34px; /*no*/
        }
      }
    }
  }

</style>

<script>
import Key from './key'

export default {
  name: 'NumberKeyboard',
  components: {Key},
  props: {
    show: Boolean, // eslint-disable-line
    title: String, // eslint-disable-line
    closeButtonText: String, // eslint-disable-line
    theme: {
      type: String,
      default: 'default',
    },
    extraKey: {
      type: String,
      default: '',
    },
    zIndex: {
      type: Number,
      default: 100,
    },
    transition: {
      type: Boolean,
      default: true,
    },
    showDeleteKey: {
      type: Boolean,
      default: true,
    },
    hideOnClickOutside: {
      type: Boolean,
      default: true,
    },
    cusClass: {
      type: String,
      default: '',
    },
  },
  data () {
    return {
      keyBoardPlugin: false,
    }
  },
  computed: {
    keys () {
      const keys = []
      for (let i = 1; i <= 9; i++) {
        keys.push({text: i})
      }
      keys.push(
        {text: this.extraKey, type: 'gray'},
        {text: 0},
        {text: 'delete', type: 'delete gray'}
      )

      return keys
    },
    style () {
      return {
        zIndex: this.zIndex,
      }
    },
    showTitleClose () {
      return this.closeButtonText && this.theme === 'default'
    },
  },
  watch: {
    show () {
      if (!this.transition) {
        this.$emit(this.show ? 'show' : 'hide')
      }
    },
  },
  mounted () {
    this.handler(true)
    this.$el.setAttribute('vum-show-keyborad', '')
  },
  destroyed () {
    this.handler(false)
    if (this.keyBoardPlugin) {
      document.body.removeChild(this.$el)
    }
  },
  activated () {
    this.handler(true)
  },
  deactivated () {
    this.handler(false)
  },
  methods: {
    closeKeyboard () {
      let vm = this
      this.show = false
      let wrapper = document.querySelector('[vum-show-keyborad]')
      if (wrapper && vm.keyBoardPlugin) {
        document.body.removeChild(wrapper)
      }
    },
    showNumberKeyboard (options) {
      this.keyBoardPlugin = true
      this.title = options.title
      this.closeButtonText = options.closeButtonText
      this.extraKey = options.extraKey
      this.keyDown = options.keyDown
      this.keyDelete = options.keyDelete
      this.show = true
    },
    handler (action) {
      if (action !== this.handlerStatus && this.hideOnClickOutside) {
        this.handlerStatus = action
        document.body[(action ? 'add' : 'remove') + 'EventListener']('touchstart', this.onBlur)
      }
    },
    onBlur () {
      this.$emit('blur')
    },
    onClose () {
      this.$emit('close')
      this.show = false
      this.onBlur()
    },
    onAnimationEnd () {
      this.$emit(this.show ? 'show' : 'hide')
    },
    onPressKey (text) {
      let vm = this
      if (text === '') {
        return
      }
      if (text === 'delete') {
        this.$emit('delete')
        if (vm.keyDelete) {
          vm.keyDelete()
        }
      } else if (text === this.closeButtonText) {
        this.onClose()
      } else {
        this.$emit('input', text)
        if (vm.keyDown) {
          vm.keyDown(text)
        }
      }
    },
  },
}
</script>