x-dropdown.vue 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. <template>
  2. <view>
  3. <view class="x-dropdown" :style="customStyle" @click="click">
  4. <slot></slot>
  5. </view>
  6. <view class="x-dropdown-mask" :class="{ 'x-dropdown-mask-show': maskShow }" @touchmove.prevent="() => { }"
  7. @click="close">
  8. <view class="x-dropdown-menu-container" :style="[layout]">
  9. <slot name="menu">
  10. <view class="menu">
  11. <view class="menu-item" :style="[{ width: dropdownRect.width + 'px' }, menuStyle]"
  12. v-for="i, idx in menuList" @click="menuClick(i, idx)">
  13. <text>{{ i }}</text>
  14. </view>
  15. </view>
  16. </slot>
  17. </view>
  18. </view>
  19. </view>
  20. </template>
  21. <script>
  22. import { queryElementRect } from '@/uni_modules/x-tools/tools/sugar.js'
  23. import { str2px, commonProps } from '@/uni_modules/x-tools/tools/com.js'
  24. export default {
  25. name: 'x-dropdown',
  26. props: {
  27. ...commonProps,
  28. menuList: {
  29. type: Object,
  30. default: () => ['菜单1', '菜单2', '菜单3']
  31. },
  32. menuStyle: {
  33. type: Object,
  34. default: () => ({})
  35. },
  36. interspace: {
  37. type: [String, Number],
  38. default: '10rpx'
  39. }
  40. },
  41. emits: ["open", "close", "change"],
  42. data() {
  43. return {
  44. maskShow: false,
  45. window: {
  46. width: 0,
  47. height: 0,
  48. },
  49. layout: {},
  50. innerInterspace: 0,
  51. dropdownRect: {
  52. width: 0,
  53. }
  54. };
  55. },
  56. mounted() {
  57. this.init();
  58. console.log(2342312312313);
  59. this.innerInterspace = this.str2px(this.interspace)
  60. },
  61. methods: {
  62. str2px,
  63. queryElementRect,
  64. init() {
  65. console.log('init');
  66. if (this.window.width === 0) {
  67. uni.getSystemInfo({
  68. success: (res) => {
  69. const { windowWidth, windowHeight } = res
  70. this.window = {
  71. width: windowWidth,
  72. height: windowHeight,
  73. }
  74. },
  75. fail: () => {
  76. this.init();
  77. },
  78. });
  79. }
  80. if (this.dropdownRect.width === 0) {
  81. this.queryElementRect('.x-dropdown').then(res => {
  82. this.dropdownRect = res
  83. }).catch(res => {
  84. this.init();
  85. })
  86. }
  87. },
  88. async click(e) {
  89. const dropdownRect = await this.queryElementRect('.x-dropdown')
  90. const menuRect = await this.queryElementRect('.x-dropdown-menu-container')
  91. const { innerInterspace, window } = this;
  92. const { width, height } = window;
  93. const { left, right, top, bottom } = dropdownRect
  94. const layout = {
  95. transition: 'transform 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275)',
  96. transform: "scaleY(1)"
  97. };
  98. layout.top = `${bottom + innerInterspace}px`;
  99. if (left + menuRect.width < width) {
  100. layout.left = `${left}px`;
  101. } else {
  102. const val = width - right
  103. layout.right = `${val > 0 ? val : 0}px`;
  104. }
  105. this.layout = layout;
  106. this.maskShow = true;
  107. this.$emit("open")
  108. this.$emit("change", true)
  109. },
  110. menuClick(value, index) {
  111. this.$emit('menuClick', { value, index })
  112. },
  113. close() {
  114. this.maskShow = false;
  115. this.layout = {
  116. transform: "scaleY(0)"
  117. };
  118. this.$emit("close");
  119. this.$emit("change", false)
  120. }
  121. },
  122. };
  123. </script>
  124. <style lang="scss" scoped>
  125. .x-dropdown {
  126. width: fit-content;
  127. position: relative;
  128. overflow: hidden;
  129. }
  130. .x-dropdown-mask {
  131. position: fixed;
  132. width: 100vw;
  133. height: 100vh;
  134. left: 0;
  135. top: 0;
  136. z-index: 999;
  137. transition: all 0.3s;
  138. opacity: 0;
  139. pointer-events: none;
  140. .x-dropdown-menu-container {
  141. position: absolute;
  142. transform-origin: top;
  143. transform: scaleY(0);
  144. .menu {
  145. position: relative;
  146. background: #FFFFFF;
  147. box-shadow: 0rpx 2rpx 20rpx 0rpx rgba(0, 0, 0, 0.3);
  148. border-radius: 8rpx;
  149. .menu-item {
  150. padding: 24rpx 32rpx;
  151. text-align: center;
  152. text {
  153. font-size: 28rpx;
  154. color: #000;
  155. }
  156. &:active {
  157. background-color: rgba(0, 0, 0, 0.1);
  158. }
  159. }
  160. }
  161. }
  162. }
  163. .x-dropdown-mask-show {
  164. opacity: 1;
  165. pointer-events: auto;
  166. }
  167. </style>