wd-number-keyboard.vue 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. <template>
  2. <wd-popup
  3. v-model="show"
  4. position="bottom"
  5. :z-index="zIndex"
  6. :safe-area-inset-bottom="safeAreaInsetBottom"
  7. :modal-style="modal ? '' : 'opacity: 0;'"
  8. :modal="hideOnClickOutside"
  9. :lockScroll="lockScroll"
  10. :root-portal="rootPortal"
  11. @click-modal="handleClose"
  12. >
  13. <view :class="`wd-number-keyboard ${customClass}`" :style="customStyle">
  14. <view class="wd-number-keyboard__header" v-if="showHeader">
  15. <slot name="title" v-if="showTitle">
  16. <text class="wd-number-keyboard__title">{{ title }}</text>
  17. </slot>
  18. <view class="wd-number-keyboard__close" hover-class="wd-number-keyboard__close--hover" v-if="showClose" @click="handleClose">
  19. <text>{{ closeText }}</text>
  20. </view>
  21. </view>
  22. <view class="wd-number-keyboard__body">
  23. <view class="wd-number-keyboard__keys">
  24. <wd-key v-for="key in keys" :key="key.text" :text="key.text" :type="key.type" :wider="key.wider" @press="handlePress"></wd-key>
  25. </view>
  26. <view class="wd-number-keyboard__sidebar" v-if="mode === 'custom'">
  27. <wd-key v-if="showDeleteKey" large :text="deleteText" type="delete" @press="handlePress"></wd-key>
  28. <wd-key large :text="closeText" type="close" :loading="closeButtonLoading" @press="handlePress"></wd-key>
  29. </view>
  30. </view>
  31. </view>
  32. </wd-popup>
  33. </template>
  34. <script lang="ts">
  35. export default {
  36. name: 'wd-number-keyboard',
  37. options: {
  38. virtualHost: true,
  39. addGlobalClass: true,
  40. styleIsolation: 'shared'
  41. }
  42. }
  43. </script>
  44. <script lang="ts" setup>
  45. import wdPopup from '../wd-popup/wd-popup.vue'
  46. import { computed, ref, useSlots, watch } from 'vue'
  47. import WdKey from './key/index.vue'
  48. import { numberKeyboardProps, type Key } from './types'
  49. import type { NumberKeyType } from './key/types'
  50. const props = defineProps(numberKeyboardProps)
  51. const emit = defineEmits(['update:visible', 'input', 'close', 'delete', 'update:modelValue'])
  52. const slots = useSlots()
  53. const show = ref(props.visible)
  54. watch(
  55. () => props.visible,
  56. (newValue) => {
  57. show.value = newValue
  58. }
  59. )
  60. const keys = computed(() => (props.mode === 'custom' ? genCustomKeys() : genDefaultKeys()))
  61. const showClose = computed(() => {
  62. return props.closeText && props.mode === 'default'
  63. })
  64. const showTitle = computed(() => {
  65. return !!props.title || !!slots.title
  66. })
  67. const showHeader = computed(() => {
  68. return showTitle.value || showClose.value
  69. })
  70. /**
  71. * 随机打乱数组的顺序
  72. * @param arr 要打乱顺序的数组
  73. * @returns 打乱顺序后的数组
  74. */
  75. function shuffleArray<T>(arr: T[]): T[] {
  76. const newArr = [...arr]
  77. for (let i = newArr.length - 1; i > 0; i--) {
  78. // 生成一个随机索引 j,范围是 [0, i]
  79. const j = Math.floor(Math.random() * (i + 1))
  80. // 交换索引 i 和 j 处的元素
  81. ;[newArr[i], newArr[j]] = [newArr[j], newArr[i]]
  82. }
  83. return newArr
  84. }
  85. function genBasicKeys(): Key[] {
  86. const keys = Array.from({ length: 9 }, (_, i) => ({ text: i + 1 }))
  87. // 如果需要随机顺序,则调用 shuffleArray 方法打乱数组顺序
  88. return props.randomKeyOrder ? shuffleArray(keys) : keys
  89. }
  90. function genDefaultKeys(): Key[] {
  91. return [
  92. ...genBasicKeys(),
  93. { text: props.extraKey as string, type: 'extra' },
  94. { text: 0 },
  95. {
  96. // 根据条件是否显示删除键的文本和类型
  97. text: props.showDeleteKey ? props.deleteText : '',
  98. type: props.showDeleteKey ? 'delete' : ''
  99. }
  100. ]
  101. }
  102. function genCustomKeys(): Key[] {
  103. const keys = genBasicKeys()
  104. const extraKeys = Array.isArray(props.extraKey) ? props.extraKey : [props.extraKey]
  105. if (extraKeys.length === 1) {
  106. // 如果只有一个额外按键,则添加一个宽度较大的数字0和一个额外按键
  107. keys.push({ text: 0, wider: true }, { text: extraKeys[0], type: 'extra' })
  108. } else if (extraKeys.length === 2) {
  109. // 如果有两个额外按键,则添加两个额外按键和一个数字0
  110. keys.push({ text: extraKeys[0], type: 'extra' }, { text: 0 }, { text: extraKeys[1], type: 'extra' })
  111. }
  112. return keys
  113. }
  114. const handleClose = () => {
  115. emit('close')
  116. emit('update:visible', false)
  117. }
  118. const handlePress = (text: string, type: NumberKeyType) => {
  119. if (text === '' && type === 'extra') {
  120. return handleClose()
  121. }
  122. const value = props.modelValue
  123. if (type === 'delete') {
  124. emit('delete')
  125. emit('update:modelValue', value.slice(0, value.length - 1))
  126. } else if (type === 'close') {
  127. handleClose()
  128. } else if (value.length < +props.maxlength) {
  129. emit('input', text)
  130. emit('update:modelValue', value + text)
  131. }
  132. }
  133. </script>
  134. <style lang="scss">
  135. @import './index.scss';
  136. </style>