Skip to content

Commit c337f53

Browse files
committed
fix(select): 下拉效果
1. 菜单项位置可通过`shouldMenuOverlap`配置 2. 修复多选可过滤的下拉框位置错误问题
1 parent 3de492e commit c337f53

3 files changed

Lines changed: 73 additions & 25 deletions

File tree

src/components/select/index.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,34 @@ export default {
3838
</script>
3939
```
4040

41+
### 弹出选项是否覆盖
42+
43+
select 组件触发后,弹出的选项默认会覆盖在选择框上。但有时候产品并不想要这种效果。这时候可以通过`shouldMenuOverlap` 属性(`should-menu-overlap`)加以调整。下面分别是两种不同的效果:
44+
45+
```html
46+
47+
<c-select v-model="dim" :options="options" />
48+
<c-select v-model="dim" :options="options" :should-menu-overlap="false" />
49+
50+
<script>
51+
export default {
52+
data () {
53+
return {
54+
dim: '',
55+
options: [
56+
{ label: '浏览量', value: 'pv' },
57+
{ label: '访客数', value: 'uv' },
58+
{ label: '新访客数', value: 'nv' },
59+
{ label: '访问时长', value: 'du' },
60+
{ label: '转化次数', value: 'cv' },
61+
{ label: 'IP 数', value: 'ip' }
62+
]
63+
}
64+
}
65+
}
66+
</script>
67+
```
68+
4169
### 多选
4270

4371
`c-select` 添加 `multiple` 属性可以将其设置为多选。在多选时,`v-model` 绑定的值是一个数组。

src/components/select/index.vue

Lines changed: 30 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
:class="multiple ? 'is-multiple' : 'is-single'"
4141
)
4242
input(
43+
ref="input"
4344
v-model="query"
4445
autocomplete="off"
4546
@click.stop="noop"
@@ -87,7 +88,7 @@
8788
import throttle from 'lodash/throttle'
8889
8990
import zIndex from '@util/zIndexManager'
90-
import { getPosition, POSITION } from './position'
91+
import { getPosition } from './position'
9192
import resettable from '@scripts/mixins/resettable'
9293
import validatable from '@scripts/mixins/validatable'
9394
import PortalComponent from '../portal'
@@ -118,6 +119,10 @@ export default {
118119
type: String,
119120
default: '请选择...'
120121
},
122+
shouldMenuOverlap: {
123+
type: Boolean,
124+
default: true
125+
},
121126
multiple: Boolean,
122127
combobox: Boolean,
123128
autocomplete: Boolean,
@@ -205,7 +210,7 @@ export default {
205210
},
206211
showPlaceholder () {
207212
const empty = !this.selectedOptions.length
208-
return empty && !this.isOpen
213+
return empty && !this.showInput
209214
},
210215
exceedMaxChipCount () {
211216
return this.selectedOptions.length > this.maxChipCount
@@ -225,7 +230,10 @@ export default {
225230
isOpen () {
226231
if (this.isOpen) {
227232
this.menuStyle.minWidth = `${this.$el.offsetWidth}px`
228-
this.positionMenu()
233+
// reset positon to next tick
234+
// this would fix cases where input element in `<c-select autocomplete />`
235+
// is expanding (add a new line)
236+
this.$nextTick(this.positionMenu)
229237
window.addEventListener('click', this.onBodyClick, true)
230238
} else {
231239
window.removeEventListener('click', this.onBodyClick, true)
@@ -248,9 +256,7 @@ export default {
248256
249257
selectedOptions: function () {
250258
if (!this.multiple || this.$isServer) return
251-
this.$nextTick(function () {
252-
this.positionMenu()
253-
})
259+
this.$nextTick(this.positionMenu)
254260
}
255261
},
256262
@@ -354,14 +360,18 @@ export default {
354360
}
355361
},
356362
363+
focusOnInput () {
364+
this.$nextTick(() => {
365+
this.$refs.input.focus()
366+
})
367+
},
368+
357369
open () {
358370
this.isOpen = true;
359371
[this.activeOption] = this.filteredOptions
360372
if (this.showInput) {
361373
this.query = ''
362-
this.$nextTick(_ => {
363-
this.$el.querySelector('input').focus()
364-
})
374+
this.focusOnInput()
365375
}
366376
},
367377
@@ -426,15 +436,20 @@ export default {
426436
const index = this.selectedOptions.indexOf(option)
427437
this.selectedOptions.splice(index, 1)
428438
this.emitChange()
439+
// 输入框重新获得焦点
440+
this.focusOnInput()
429441
},
430442
431443
positionMenu () {
432-
const pos = this.canInput ? POSITION.BOTTOM : POSITION.TOP
433-
const { top, left } = getPosition(this.menuEl, this.$el, pos)
434-
const { style } = this.menuEl
435-
style.top = `${top}px`
436-
style.left = `${left}px`
437-
style.zIndex = zIndex.next()
444+
const { shouldMenuOverlap, canInput, menuEl } = this
445+
const { top, left, height } = getPosition(menuEl, this.$el)
446+
const overlap = shouldMenuOverlap && !canInput
447+
448+
const offset = 4
449+
const menuTop = overlap ? top : (top + height + offset)
450+
menuEl.style.left = `${left}px`
451+
menuEl.style.top = `${menuTop}px`
452+
menuEl.style.zIndex = zIndex.next()
438453
},
439454
440455
onBodyClick (e) {

src/components/select/position.js

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,22 @@
1-
/**
2-
* get absolute position relative to another element
3-
*/
41
export const POSITION = {
52
TOP: 'top',
63
BOTTOM: 'bottom'
74
}
8-
export function getPosition (el, refEl, pos = POSITION.TOP) {
9-
const refRect = refEl.getBoundingClientRect()
10-
const refTop = refRect.top + window.pageYOffset
11-
const refLeft = refRect.left + window.pageXOffset
12-
const left = refLeft
13-
const top = pos === POSITION.TOP ? refTop : refTop + refEl.clientHeight
14-
return { left, top }
5+
6+
/**
7+
* get absolute position relative to another element
8+
*/
9+
export function getPosition (el, refEl) {
10+
const { top, left, width, height } = refEl.getBoundingClientRect()
11+
const refTop = top + window.pageYOffset
12+
const refLeft = left + window.pageXOffset
13+
14+
return {
15+
width,
16+
height,
17+
left: refLeft,
18+
top: refTop
19+
}
1520
}
1621

1722
export default { getPosition }

0 commit comments

Comments
 (0)