Vue+ts 自定义select组件,返回选中的元素
自定义子组件selecTemp
// SelectTemp.Data 参数类型
export interface SelectData {
selectOption: any[], // 下拉框选项
type: string, // select类型,主要用来区分不同样式
labelTxt?: string, // 是否显示左侧的label
defaultValue?: number | null // 默认值
}
这种样式的select,type为‘label’,labelText为显示的label内容
{ type: 'label', labelTxt: '所属管廊' }
这种样式的select,type为‘label’,labelText为空
{ type: 'border', labelTxt: '所属管廊' }
这种样式的select,type为‘label’,labelText为空
{ type: 'solid', labelTxt: '所属管廊' }
<template>
<div class="SelectTemp-wrap" :type="data.type">
<div class="select-wrap" :class="[data.type === 'solid' ? 'solid-select' : data.type === 'border' ? 'border-select' : 'label-selct']">
<!-- 只有当type为label的时候,才显示 -->
<div class="label-wrap" v-show="data.type === 'label'">{{data.labelTxt}}</div>
<div class="value-wrap">
<input class="choosed-li" v-model="value" readonly @click="isShowUL = !isShowUL"/>
<!-- 下拉列表展开收起动画 -->
<transition name="draw">
<ul class="ul-box" v-if="isShowUL">
<li v-if="isShowUL" class="li-box" v-for="(item, index) in data.selectOption" :key="index" @click="choosedLi(item)">{{item.name}}</li>
</ul>
</transition>
</div>
<div class="icon" @click="this.isShowUL = !this.isShowUL">
<div class="triangle"></div>
</div>
</div>
</div>
</template>
<script lang="ts">
import { Component, Vue, Prop, Emit, Watch } from "vue-property-decorator";
import { SelectData } from '@/types/components/selectTemp.interface'
@Component({})
export default class About extends Vue {
// prop
@Prop({
required: false,
default: {}
}) data!: SelectData
isShowUL: boolean = false
value: any = ''
choosedItem: any = ''
@Watch('data.selectOption')
watchData(newVal: any, oldVal: any) {
if (newVal.length !== 0) {
if ((this.data as SelectData).defaultValue !== undefined) {
newVal.forEach((item: any) => {
if(item.id === this.data.defaultValue){
this.choosedLi(item)
}
})
}
}
}
// 将选中的option传给父组件
@Emit('on-change')
send(choosedItem: any) {}
mounted() {
// 是否显示默认值
if (this.data.defaultValue !== undefined) {
this.data.selectOption.forEach((option: any) => {
if (option.id === this.data.defaultValue) {
this.choosedLi(option)
}
})
}
// 点击非select区域,隐藏下拉框
document.body.addEventListener('click', (e) => {
if ((e.target as any).className !== 'choosed-li' &&
(e.target as any).className !== 'triangle' &&
(e.target as any).className !== 'icon'
) {
this.isShowUL = false
}
})
}
// 选择option、
choosedLi(item: any) {
this.isShowUL = false
this.value = item.name
this.choosedItem = item
this.send(this.choosedItem)
}
}
</script>
<style lang="less">
.SelectTemp-wrap {
position: absolute;
z-index: 9;
.select-wrap {
.choosed-li,
.icon {
display: inline-block;
vertical-align: top;
:hover {
cursor: pointer;
}
}
.value-wrap {
display: inline-block;
vertical-align: top;
}
.choosed-li {
min-width: 120px;
background-color: #474747;
outline: none;
padding: 4px 7px;
border-radius: 0;
border-top-left-radius: 4px;
border-bottom-left-radius: 4px;
border: none;
height: 20px;
color: @base-black-color5;
width: 120px;
text-align: center;
}
.choosed-li:hover {
border: none;
cursor: pointer;
}
.choosed-li:focus {
outline: none;
border: none;
box-shadow: 0 0 0 2px transparent;
}
.icon {
width: 20px;
height: 20px;
background: #474747;
margin-left: 5px;
border-top-right-radius: 4px;
border-bottom-right-radius: 4px;
display: inline-block;
.triangle {
width: 0;
height: 0;
border-width: 7px;
border-color: @base-black-color5 transparent transparent transparent;
border-style: solid dashed dashed dashed;
border-radius: 4px;
margin-top: 6px;
margin-left: 2px;
}
}
.ul-box {
z-index: 999999;
background: #474747;
margin-top: 3px;
width: 120px;
border-radius: 4px;
max-height: 200px;
overflow-y: hidden;
.li-box {
line-height: 1.5;
padding: 2px 7px;
cursor: pointer;
}
}
.draw-enter-active, .draw-leave-active {
transition: max-height .4s ease;
}
.draw-enter, .draw-leave-to {
max-height: 0;
}
.ul-box::-webkit-scrollbar {
width: 4px;
height: 4px;
overflow-x: hidden;
overflow-y: auto;
}
.ul-box::-webkit-scrollbar-thumb {
border-radius: 5px;
box-shadow: inset 0 0 5px rgb(27, 102, 207);
background: rgba(0, 0, 0, 0.2)
}
.ul-box::-webkit-scrollbar-track {
border-radius: 4px;
box-shadow: inset 0 0 5px rgb(235, 227, 235);
background: rgba(0, 0, 0, 0.1)
}
}
.border-select.select-wrap {
.choosed-li {
border: 1px solid #797979;
}
.icon {
border: 1px solid #797979;
}
}
.label-selct.select-wrap {
.label-wrap {
background: #323A43;
border-top-left-radius: 6px;
border-bottom-left-radius: 6px;
display: inline-block;
padding: 7px 14px;
margin-right: 5px;
}
.choosed-li {
border-radius: 0;
background-color: #323A43;
height: 32px;
}
.icon {
background-color: #323A43;
height: 32px;
width: 32px;
border-top-right-radius: 4px;
border-bottom-right-radius: 4px;
.triangle {
border-width: 8px;
border-color: @base-black-color5 transparent transparent transparent;
border-style: solid dashed dashed dashed;
border-radius: 4px;
margin-top: 13px;
margin-left: 8px;
}
}
.ul-box {
background-color: #323A43;
}
}
}
</style>
父组件使用select
<template>
<!-- getTunneld 不可以加写成getTunneld() -->
<SelectTemp class="select-temp-wrap" :data="tunnelSelect" @on-change="getTunnelId"/>
</template>
import { Component, Vue } from 'vue-property-decorator'
import SelectTemp from '@/components/common/selectTemp/selectTemp.vue'
import { SelectData } from '@/types/components/selectTemp.interface'
@Component({
components: { SelectTemp }
})
export default class About extends Vue {
tunnelSelect: SelectData = {
selectOption: [
{ id: 1, name: '古城大街' },
{ id: 2, name: '实验路' },
{ id: 3, name: '经二路' },
{ id: 4, name: '经三路' },
{ id: 5, name: '纬三路' }
],
type: 'label',
labelTxt: '管廊',
defaultValue: 0
}
// choosedItem由子组件传给父组件
getTunnelId(choosedItem: any) {
this.conditions.tunnelId = choosedItem.id
}
}