Touchable.vue 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120
  1. <template>
  2. <view
  3. :id="innerId ?? id"
  4. :class="[
  5. 'nana-flex-layout',
  6. innerClass,
  7. ]"
  8. :style="finalStyle"
  9. @mouseenter="handleMouseEnter"
  10. @mouseleave="handleMouseLeave"
  11. @mousedown="handleTouchStart"
  12. @mouseup="handleTouchEnd"
  13. @touchstart="handleTouchStart"
  14. @touchend="handleTouchEnd"
  15. @click.native.stop="handleClick"
  16. >
  17. <slot />
  18. </view>
  19. </template>
  20. <script setup lang="ts">
  21. /**
  22. * 组件说明:Flex组件,用于一些布局中快速写容器,是一系列盒子的基础组件。
  23. */
  24. import { computed, getCurrentInstance, inject, onMounted, ref, type Ref } from 'vue';
  25. import { useTheme } from '../theme/ThemeDefine';
  26. import { RandomUtils } from '@imengyu/imengyu-utils';
  27. import { useBaseViewStyleBuilder } from '../layout/BaseView';
  28. import { TouchableClickEventInceptorKey, type TouchableFlexProps } from './Touchable';
  29. const props = withDefaults(defineProps<TouchableFlexProps>(), {
  30. activeOpacity: 0.7,
  31. touchable: true,
  32. setCursor: true,
  33. });
  34. const themeContext = useTheme();
  35. const { commonStyle } = useBaseViewStyleBuilder(props);
  36. const finalStyle = computed(() => {
  37. const obj : Record<string, any> = {};
  38. if (props.pressedColor != undefined) {
  39. if (isPressed.value)
  40. obj.backgroundColor = themeContext.resolveThemeColor(props.pressedColor);
  41. } else if (props.activeOpacity != undefined)
  42. obj.opacity = isPressed.value ? props.activeOpacity : 1;
  43. const o : Record<string, any> = {
  44. ...commonStyle.value,
  45. ...obj,
  46. cursor: props.setCursor ? (props.touchable ? 'pointer' : 'not-allowed') : 'default',
  47. }
  48. for (const key in o) {
  49. if (o[key] === undefined)
  50. delete o[key];
  51. }
  52. return o;
  53. })
  54. defineOptions({
  55. options: {
  56. styleIsolation: "shared",
  57. virtualHost: true
  58. }
  59. })
  60. const emit = defineEmits([ "click", "state" ]);
  61. const isPressed = ref(false);
  62. const clickEventInceptor = inject<(Ref<() => void>)|null>(TouchableClickEventInceptorKey, null);
  63. function handleTouchStart() {
  64. if (props.touchable) {
  65. isPressed.value = true; // 按下时改变状态
  66. emit('state', 'active');
  67. }
  68. }
  69. function handleMouseEnter() {
  70. if (props.touchable)
  71. emit('state', 'active')
  72. }
  73. function handleMouseLeave() {
  74. if (props.touchable)
  75. emit('state', 'default')
  76. }
  77. function handleClick(e: Event) {
  78. if (clickEventInceptor && clickEventInceptor.value) {
  79. clickEventInceptor.value();
  80. return;
  81. }
  82. if (props.touchable) {
  83. emit('state', 'default')
  84. emit('click', e);
  85. }
  86. }
  87. function handleTouchEnd() {
  88. if (props.touchable) {
  89. isPressed.value = false; // 释放时恢复状态
  90. emit('state', 'default');
  91. }
  92. }
  93. onMounted(() => {
  94. emit('state', 'default')
  95. })
  96. const instance = getCurrentInstance();
  97. const id = 'flex-item-' + RandomUtils.genNonDuplicateID(15);
  98. defineExpose({
  99. measure() {
  100. return new Promise((resolve) => {
  101. const tabItem = uni.createSelectorQuery()
  102. .in(instance)
  103. .select(`#${id}`);
  104. tabItem.boundingClientRect().exec((res) => {
  105. resolve(res)
  106. })
  107. });
  108. },
  109. })
  110. </script>