select.vue 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121
  1. <template>
  2. <UItext
  3. ref="vmRef"
  4. :disabled="props.disabled"
  5. class="select ready"
  6. :class="{
  7. focus: showOption,
  8. [className]: className,
  9. }"
  10. :model-value="typeof labelValue === 'string' ? labelValue : inputValue"
  11. :width="props.width"
  12. :height="props.height"
  13. :readonly="readonly"
  14. :placeholder="props.placeholder"
  15. @update:model-value="val => emit('update:modelValue', val)"
  16. @blur="blurHandler"
  17. @focus="showHandler"
  18. @click="clickShowHandler"
  19. >
  20. <template #icon>
  21. <icon v-if="!$slots.icon" type="pull-down" small />
  22. <slot v-else name="icon" />
  23. </template>
  24. <template v-if="$slots.preIcon" #preIcon>
  25. <slot name="preIcon" />
  26. </template>
  27. </UItext>
  28. <UIFloating
  29. :mount="mountEl"
  30. :refer="vmRef && vmRef.root"
  31. width="100%"
  32. :class="{
  33. show: showOption || props.showOptions,
  34. [`dire-${dire}`]: true,
  35. ...(floatingClass ? { [floatingClass]: true } : {}),
  36. }"
  37. class="select-float"
  38. :dire="dire === 'top' ? 'left-top' : 'left-bottom'"
  39. >
  40. <slot name="floating-pre" />
  41. <div class="select-replace" :class="{ 'hide-scroll': props.hideScroll }">
  42. <ul>
  43. <template v-for="option in props.options" :key="option.value">
  44. <li v-if="props.options?.length" :key="option.value" :class="{ active: props.modelValue === option.value }" @mousedown="ev => optionClickHandler(ev, option)">
  45. <template v-if="$slots.option">
  46. <slot name="option" :raw="option" :active="props.modelValue === option.value" />
  47. </template>
  48. <template v-else>{{ option.label }}</template>
  49. </li>
  50. <li v-else class="un-data">{{ unplaceholder }}</li>
  51. </template>
  52. </ul>
  53. </div>
  54. </UIFloating>
  55. </template>
  56. <script setup lang="ts">
  57. import { computed, defineExpose, ref } from 'vue'
  58. import icon from '@kankan/components/basic/icon'
  59. import UIFloating from '@kankan/components/basic/floating'
  60. import UItext from '../text/text.vue'
  61. import { selectProps } from './select'
  62. const props = defineProps(selectProps)
  63. const emit = defineEmits(['update:modelValue'])
  64. const vmRef = ref(null)
  65. const showOption = ref(false)
  66. const mountEl = document.body
  67. const inputValue = computed(() => {
  68. const selectOption = props.options.find(({ value }) => value === props.modelValue)
  69. return selectOption ? selectOption.label : ''
  70. })
  71. const optionClickHandler = (ev, option) => {
  72. if (props.stopEl && props.stopEl.toUpperCase() === ev.target.tagName.toUpperCase()) {
  73. setTimeout(() => {
  74. vmRef.value.input.focus()
  75. })
  76. } else {
  77. clickCount = 0
  78. emit('update:modelValue', option.value)
  79. vmRef.value.input.focus()
  80. showOption.value = false
  81. }
  82. }
  83. let clickCount = 0
  84. const clickShowHandler = () => {
  85. clickCount++
  86. if (showOption.value && props.dbhide && !(clickCount % 2)) {
  87. showOption.value = false
  88. vmRef.value.input.blur()
  89. } else {
  90. showHandler()
  91. }
  92. }
  93. const showHandler = () => {
  94. clearTimeout(timeout)
  95. showOption.value = true
  96. vmRef.value.input.focus()
  97. }
  98. let timeout
  99. const blurHandler = () =>
  100. (timeout = setTimeout(() => {
  101. showOption.value = false
  102. clickCount = 0
  103. }, 16))
  104. defineExpose({
  105. vmRef,
  106. animationRef: {
  107. changeShow(show) {
  108. showOption.value = show
  109. },
  110. },
  111. })
  112. </script>