vue.js 实现简单的下拉框实现
效果图如下:
代码如下:
- <template>
- <div class="y-warp"
- v-clickoutside="handleClose"
- >
- <input readonly="readonly"
- ref="input"
- class="y-select"
- @click="clickInput"
- :value="textValue"/>
- <ul class="y-options"
- :style="showOptionsStyle"
- ref="options"
- >
- <li v-for="item of options"
- :class="{'y-selected-icon': itemSelected(item.value) }"
- @click="selectedValue(item, $event)"
- :key="item.value"
- >
- <span>{{item.label}}</span>
- </li>
- </ul>
- <span class="y-select-arrow">
- <i class="icon-font i-v-bottom"
- :class="isBottom ?'arrow-down':'arrow-up'"
- ></i>
- </span>
- </div>
- </template>
- <script>
- import clickoutside from '@/directives/clickoutside'
- export default {
- name: 'YSelect',
- directives: { clickoutside },
- props: {
- options: {
- type: Array,
- required: true
- },
- multiple: Boolean,
- value: {}
- },
- data () {
- return {
- isBottom: true
- }
- },
- methods: {
- clickInput () {
- this.$refs.input.style.borderColor = '#337AB7'
- this.isBottom = !this.isBottom
- },
- selectedValue ({ value, label }, e) {
- !this.multiple ? this.singleSelected(value) : this.multipleSelected(value)
- },
- singleSelected (value) {
- this.$emit('input', value)
- this.isBottom = true
- },
- multipleSelected (val) {
- const selectedVal = this.value || []
- const index = selectedVal.indexOf(val)
- index === -1 ? selectedVal.push(val) : selectedVal.splice(index, 1)
- },
- handleClose () {
- this.isBottom = true
- this.$refs.input.style.borderColor = ''
- },
- isHave (value) {
- return !this.multiple
- ? value === this.value
- : this.value.includes(value)
- },
- itemSelected (value) {
- return this.isHave(value)
- },
- },
- computed: {
- showOptionsStyle () {
- const length = this.options.length
- return !this.isBottom ? {
- height: `${32 * length + 8}px`,
- padding: `4px 8px`,
- display: 'block'
- } : {}
- },
- textValue () {
- var textVal = this.options.filter(value => {
- return this.isHave(value.value)
- }).map(item => {
- return item.label
- })
- return !this.multiple ? textVal[0] : textVal.join(';')
- }
- }
- }
- </script>
- <style lang="less" scoped>
- .y-warp{
- position: relative;
- width: 100%;
- .y-select {
- display: inline-block;
- box-sizing: border-box;
- font-family: inherit;
- padding: 10px 20px;
- background: transparent;
- border: 1px solid #CCCCCC;
- border-radius: 4px;
- font-size: inherit;
- height: 40px;
- width: 100%;
- outline: none;
- cursor: pointer;
- }
- .y-options {
- position: absolute;
- height: 0;
- left: 0;
- top: 30px;
- width: 100%;
- list-style-type: none;
- box-shadow: 0px 0px 12px -2px rgba(0, 0, 0, 0.3);
- padding: 0 8px;
- box-sizing: border-box;
- overflow: hidden;
- border-radius: 4px;
- transition: padding .1s ease, height .3s linear;
- li {
- width: 100%;
- line-height: 32px;
- cursor: pointer;
- &:hover {
- background: #FAFAFA;
- }
- }
- }
- .y-select-arrow {
- position: absolute;
- height: 100%;
- right: 5px;
- z-index: -1;
- i {
- position: absolute;
- top: 50%;
- right: 5px;
- transition: transform .5s linear;
- }
- .arrow-down {
- transform: translateY(-50%) rotate(0deg);
- }
- .arrow-up {
- transform: translateY(-50%) rotate(180deg);
- }
- }
- }
- .y-selected-icon {
- color: #337AB7;
- font-weight: 500;
- &:after{
- position: absolute;
- right: 20px;
- font-family: iconfont;
- content: "\f132";
- font-size: 12px;
- font-weight: 700;
- line-height: 30px;
- -webkit-font-smoothing: antialiased;
- }
- }
- </style>
这里有一个指令 clickoutside 代码如下
- const clickoutside = {
- bind (el, binding, vnode) {
- function documentHandler (e) {
- if (el.contains(e.target)) {
- return false
- }
- if (binding.expression) {
- binding.value(e)
- }
- }
- el.__vueClickoutside__ = documentHandler
- document.addEventListener('click', documentHandler)
- },
- update () {
- },
- unbind (el, binding) {
- document.removeEventListener('click', el.__vueClickoutside__)
- delete el.__vueClickoutside__
- }
- }
- export default clickoutside
来源: http://www.qdfuns.com/article/50158/a79d3b740582421cf02f137aac6d5442.html