easy-select.vue 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237
  1. <template>
  2. <view class="easy-select" @click.stop="trigger" :style="[easySelectSize]">
  3. <input type="text" v-model="value" :placeholder="placeholder" disabled clearable>
  4. <view class="easy-select-suffix" :style="{border: '1px solid rgba(0,0,0,0)'}" :class="[showSuffix]">
  5. <view class="easy-select-down-tag">^</view>
  6. </view>
  7. <view class="easy-select-options" v-if="showOptions" :style="{'min-width': boundingClientRect.width + 'px', top: optionsGroupTop, margin: optionsGroupMargin}">
  8. <view class="easy-select-options-item" v-for="item in options" :key="item.value" @click.stop="select(item)" :class="{active: currentSelect.label === item.label}">
  9. <text>{{item.label}}</text>
  10. </view>
  11. </view>
  12. </view>
  13. </template>
  14. <script>
  15. /**
  16. * easy-select
  17. * @author Snoop zhang
  18. * @description Select Component
  19. * */
  20. const COMPONENT_NAME = 'easy-select'
  21. const MAX_OPTIONS_HEIGHT = 137 // 修改务必也修改easy-select-options的css部分
  22. const OPTIONS_ITEM_HEIGHT = 33 // 修改务必也修改easy-select-options-item的css部分
  23. const OPTIONS_MARGIN = 10
  24. const OPTIONS_PADDING = 6 * 2 + 2 // + 2是border
  25. const OPTIONS_OTHER_HEIGHT = OPTIONS_MARGIN + OPTIONS_PADDING
  26. const STORAGE_KEY = '_easyWindowHeight'
  27. const SIZE = {
  28. 'medium': {
  29. width: '240px',
  30. height: '40px'
  31. },
  32. 'small': {
  33. width: '200px',
  34. height: '30px'
  35. },
  36. 'mini': {
  37. width: '160px',
  38. height: '30px'
  39. }
  40. }
  41. export default {
  42. name: COMPONENT_NAME,
  43. props: {
  44. windowHeight: {
  45. type: [Number, String],
  46. default: 0
  47. },
  48. placeholder: {
  49. type: String,
  50. default: '请选择'
  51. },
  52. value: {
  53. type: String,
  54. default: '双皮奶'
  55. },
  56. size: {
  57. type: String,
  58. default: 'medium'
  59. },
  60. options: {
  61. type: Array,
  62. default () {
  63. return [{
  64. value: '选项1',
  65. label: '黄金糕'
  66. }, {
  67. value: '选项2',
  68. label: '双皮奶'
  69. }, {
  70. value: '选项3',
  71. label: '蚵仔煎'
  72. }, {
  73. value: '选项4',
  74. label: '龙须面'
  75. }, {
  76. value: '选项5',
  77. label: '北京烤鸭'
  78. }]
  79. }
  80. }
  81. },
  82. data() {
  83. return {
  84. showOptions: false,
  85. boundingClientRect: {},
  86. currentSelect: {},
  87. optionsGroupTop: 'auto',
  88. optionsGroupMargin: ''
  89. }
  90. },
  91. computed: {
  92. showSuffix() {
  93. return this.showOptions ? 'showOptions' : 'no-showOptions'
  94. },
  95. easySelectSize() {
  96. let size = this.size.toLowerCase()
  97. if (size in SIZE) {
  98. return {
  99. width: SIZE[size].width,
  100. height: SIZE[size].height
  101. }
  102. } else {
  103. return {}
  104. }
  105. }
  106. },
  107. mounted() {
  108. const elQuery = uni.createSelectorQuery().in(this)
  109. elQuery.select('.easy-select').boundingClientRect(data => {
  110. this.boundingClientRect = data
  111. }).exec();
  112. try {
  113. if (!this.windowHeight) {
  114. const storageHeihgt = uni.getStorageSync(STORAGE_KEY)
  115. if (storageHeihgt) {
  116. this.easyWindowHeight = storageHeihgt
  117. return
  118. }
  119. const res = uni.getSystemInfoSync();
  120. this.easyWindowHeight = res.windowHeight
  121. uni.setStorageSync(STORAGE_KEY, this.easyWindowHeight)
  122. }
  123. } catch (e) {
  124. // error
  125. }
  126. },
  127. methods: {
  128. trigger(e) {
  129. const view = uni.createSelectorQuery().in(this)
  130. view.select('.easy-select').fields({rect: true}, data => {
  131. let { top, bottom } = data
  132. const thresholdHeight = Math.min(MAX_OPTIONS_HEIGHT + OPTIONS_MARGIN, (this.options.length * OPTIONS_ITEM_HEIGHT) +
  133. OPTIONS_OTHER_HEIGHT)
  134. bottom = Number(this.windowHeight || this.easyWindowHeight) - (top + this.boundingClientRect.height) // 距离底部的距离等于视口的高度减上top加select组件的高度
  135. // judge direction
  136. if (bottom < thresholdHeight) {
  137. this.optionsGroupDirection = 'up'
  138. this.optionsGroupTop = -thresholdHeight - 12 + 'px'
  139. this.optionsGroupMargin = '0'
  140. } else {
  141. this.optionsGroupDirection = 'down'
  142. this.optionsGroupTop = 'auto'
  143. this.optionsGroupMargin = '10px 0 0 0'
  144. }
  145. // if (this.scrollTop < )
  146. this.showOptions = !this.showOptions
  147. }).exec();
  148. },
  149. select(options) {
  150. this.showOptions = false
  151. this.currentSelect = options
  152. this.$emit('selectOne', options)
  153. },
  154. hideOptions() {
  155. this.showOptions = false
  156. }
  157. }
  158. }
  159. </script>
  160. <style scoped>
  161. .easy-select {
  162. position: relative;
  163. border: 1px solid #dcdfe6;
  164. border-radius: 4px;
  165. /* font-size: 28rpx; */
  166. color: #606266;
  167. outline: none;
  168. box-sizing: content-box;
  169. height: 30px;
  170. }
  171. .easy-select input {
  172. padding: 0 18rpx;
  173. padding-right: 60rpx;
  174. overflow: hidden;
  175. white-space: nowrap;
  176. text-overflow: ellipsis;
  177. height: 100% !important;
  178. min-height: 100% !important;
  179. }
  180. .easy-select .easy-select-suffix {
  181. position: absolute;
  182. box-sizing: border-box;
  183. height: 100%;
  184. right: 5px;
  185. top: 0;
  186. display: flex;
  187. align-items: center;
  188. transform: rotate(180deg);
  189. transition: all .3s;
  190. transform-origin: center;
  191. }
  192. .easy-select .showOptions {
  193. transform: rotate(0) !important;
  194. }
  195. .easy-select .no-showOptions {
  196. transform: rotate(180deg) !important;
  197. }
  198. .easy-select .easy-select-options {
  199. position: absolute;
  200. padding: 6px 0;
  201. margin-top: 10px;
  202. border: 1px solid #e4e7ed;
  203. border-radius: 4px;
  204. background-color: #fff;
  205. box-shadow: 0 2px 12px 0 rgba(0, 0, 0, .1);
  206. box-sizing: border-box;
  207. transform-origin: center top;
  208. z-index: 2238;
  209. overflow: scroll;
  210. max-height: 274rpx;
  211. }
  212. .easy-select .easy-select-options-item {
  213. padding: 0 20rpx;
  214. position: relative;
  215. white-space: nowrap;
  216. font-size: 14px;
  217. color: #606266;
  218. height: 33px;
  219. line-height: 33px;
  220. box-sizing: border-box;
  221. }
  222. .easy-select .active {
  223. background-color: #F5F7FA
  224. }
  225. </style>