<template> <view> <view class="u-dropdown-select" :class="{ 'active': showOptions }" @touchstart="touchStart"> <view class="u-dropdown-label">{{ label || placeholder }} </view> <van-icon name="arrow-down" /> </view> <view v-if="showOptions" class="u-dropdown-select-mask" :style="{ 'top': offsetTop +'px' }" @click="showOptions = false"></view> <view v-if="showOptions" class="u-dropdown-panel" :style="{ 'top': offsetTop +'px' }"> <view class="u-dropdown-tabs"> <u-tabs :active.sync="tabActive" :tabs="tabs" tab-style="tag" @change="tabChange"/> </view> <view class="u-dropdown-options"> <template v-if="list.length"> <template v-for="(item, index) in list"> <view class="u-dropdown-option" :class="{ 'active': item.active }" @click="clickOption(item, index)"> <view>{{ item.label }}</view> <van-icon class="van-icon" name="arrow" /> </view> </template> </template> <van-empty v-else class="van-empty" description="没有数据"/> </view> <view class="u-dropdown-action"> <van-button type="info" class="button-info" block @click="reset">重置</van-button> <van-button type="primary" class="button-primary" block @click="confirm">确定</van-button> </view> </view> </view> </template> <script> import UTabs from '@/components/common/u-tabs/index.vue' export default { options: { styleIsolation: 'shared' }, components: { UTabs }, props: { placeholder: String, options: Array, dataApi: Function, dataParams: Object, value: [String, Number] }, watch: { options() { this.initComponent() } }, data() { return { showOptions: false, offsetTop: 0, tabActive: 3, tabs: [], list: [], label: '' } }, created() { this.initComponent() console.log('value:' + this.value) }, mounted() { }, methods: { initComponent() { if (this.options) { this.setOptions(this.options) } if (this.value) { this.setLabel(this.value) } if (this.dataApi) { this.dataApi(this.dataParams || {}).then(res => { this.setOptions(res.data) }) } }, tabChange(e) { this.list = this.tabs[e.detail.index].children }, setOptions(options) { if (options) { if (!options.length) { this.tabs = [] this.list = [] } else if (!options[0].children || options[0].children.length === 0) { this.tabs = [] this.list = options.map(v => { if (v.value === this.value) { v.active = true } else { v.active = false } return v }) } else { this.tabs = options.map(v => { return { label: v.label, value: v.value, children: v.children || [] } }) this.list = this.tabs[0].children.map(v => { if (v.value === this.value) { v.active = true } else { v.active = false } return v }) this.tabActive = this.tabs[0].value } } }, setLabel(value) { const option = this.getSelectOption(value) if (option) { if (option.tab) { this.label = option.tab.label + ' / ' + option.item.label } else { this.label = option.item.label } } else { this.label = '' } }, reset() { this.cleanListActive() this.label = '' this.showOptions = false this.$emit('input', null) this.$emit('change', { detail: { value: null } }) }, confirm() { this.showOptions = false let val = null const option = this.getSelectOption() if (option) { this.setLabel() if (option.tab) { val = option.item.value } else { val = option.value } this.$emit('input', val) } this.$emit('change', { detail: { value: val } }) }, getSelectOption(value) { if (this.tabs.length) { for (const tab of this.tabs) { for (const item of tab.children) { if (item.active || item.value === value) { return { tab: tab, item: item } } } } } else { for (const item of this.list) { if (item.active || item.value === value) { return item } } } }, cleanListActive() { this.list.forEach(item => { this.$set(item, 'active', false) }) this.$emit('change', { detail: { value: null } }) this.$emit('input', undefined) }, clickOption(item) { this.cleanListActive() this.$set(item, 'active', true) }, touchStart() { this.showOptions = !this.showOptions const query = uni.createSelectorQuery().in(this) query.select('.u-dropdown-select').boundingClientRect(data => { if (data) { this.offsetTop = data.top + data.height // data.top 就是元素的offsetTop console.log('offsetTop:', data.top); } }).exec() console.log(this.showOptions) if (this.showOptions) { this.$emit('dropdown') } }, showDropdown() { this.showOptions = true }, hideDropdown() { this.showOptions = false } } } </script> <style lang="scss"> .u-dropdown-tabs{ padding: 10rpx 0; margin-top: 20rpx; } .van-empty{ --image-width: 160rpx; --image-height: 160rpx; } .u-dropdown-select{ @include display-flex-between; min-width: 211rpx; height: 50rpx; padding: 0 10rpx; border: $borderLight; border-radius: 8rpx; background-color: #FFFFFF; border-radius: 8rpx; border: 1rpx solid #D9D9D9; &.active{ background-color: $buttonPrimaryColor; border-color: $buttonPrimaryColor; color: white; } .van-icon{ font-size: 15rpx; } } .u-dropdown-label{ max-width: 100%; @include text-ellipsis; font-size: $fontSize2; } .u-dropdown-tabs{ --tabs-line-height: 44px; --tabs-card-height: 30px; --tabs-bottom-bar-height: 0rpx; --tab-active-text-color: #E57519; } .u-dropdown-panel{ position: fixed; left: 0; top: 0; display: block; width: 100%; z-index: 9999; background-color: white; } .u-dropdown-options{ min-height: 100rpx; max-height: 400rpx; padding: 20rpx; overflow-y: auto; } .u-dropdown-option{ line-height: 50rpx; @include display-flex-between; font-size: $fontSize3; &.active{ color: $buttonPrimaryColor; } } .u-dropdown-action{ display: grid; grid-template-columns: 1fr 1fr; .van-button{ border-radius: 0rpx; height: 80rpx; font-size: $fontSize3; } } .van-empty__description{ font-size: $fontSize3; margin-top: 0rpx; } .button-info{ --button-info-background-color: black; --button-info-border-color: black; --button-info-color: white; } .u-dropdown-select-mask{ position: fixed; display: block; left: 0; right: 0; bottom: 0; z-index: 9998; background-color: rgba(0, 0, 0, 0.5); } </style>