|
|
|
|
<template>
|
|
|
|
|
<view class="uv-drop-down-popup">
|
|
|
|
|
<uv-transition :show="show" mode="fade" :duration="300" :custom-style="overlayStyle" @click="clickOverlay">
|
|
|
|
|
<view class="uv-dp__container" ref="uvDPContainer" :style="{height: `${height}px`}" @click.stop="blockClick">
|
|
|
|
|
<view class="uv-dp__container__list" ref="uvDPList">
|
|
|
|
|
<slot>
|
|
|
|
|
<view class="uv-dp__container__list--item" v-for="(item,index) in list" :key="index" @click="clickHandler(item,index)" :style="[itemCustomStyle(index)]">
|
|
|
|
|
<uv-text :text="item[keyName]" :size="getTextSize(index)" :color="getTextColor(index)"></uv-text>
|
|
|
|
|
</view>
|
|
|
|
|
</slot>
|
|
|
|
|
</view>
|
|
|
|
|
</view>
|
|
|
|
|
</uv-transition>
|
|
|
|
|
</view>
|
|
|
|
|
</template>
|
|
|
|
|
<script>
|
|
|
|
|
import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js';
|
|
|
|
|
import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js';
|
|
|
|
|
// #ifdef APP-NVUE
|
|
|
|
|
const animation = uni.requireNativePlugin('animation');
|
|
|
|
|
const dom = uni.requireNativePlugin('dom');
|
|
|
|
|
// #endif
|
|
|
|
|
/**
|
|
|
|
|
* DropDownPopup 下拉框
|
|
|
|
|
* @description 下拉筛选框
|
|
|
|
|
* @tutorial https://ext.dcloud.net.cn/plugin?name=uv-drop-down
|
|
|
|
|
* @property {String | Number} name 字段标识
|
|
|
|
|
* @property {String | Number} zIndex 弹出层的层级
|
|
|
|
|
* @property {String | Number} opacity 遮罩层的透明度
|
|
|
|
|
* @property {Boolean} clickOverlayOnClose 是否允许点击遮罩层关闭弹窗
|
|
|
|
|
* @property {Object} currentDropItem 当前下拉筛选菜单对象
|
|
|
|
|
* @property {String} keyName 指定从当前下拉筛选菜单对象元素中读取哪个属性作为文本展示
|
|
|
|
|
*/
|
|
|
|
|
export default {
|
|
|
|
|
name: 'uv-drop-down-popup',
|
|
|
|
|
mixins: [mpMixin, mixin],
|
|
|
|
|
props: {
|
|
|
|
|
sign: {
|
|
|
|
|
type: [String, Number],
|
|
|
|
|
default: 'UVDROPDOWN'
|
|
|
|
|
},
|
|
|
|
|
zIndex: {
|
|
|
|
|
type: [Number, String],
|
|
|
|
|
default: 999
|
|
|
|
|
},
|
|
|
|
|
opacity: {
|
|
|
|
|
type: [Number, String],
|
|
|
|
|
default: 0.5
|
|
|
|
|
},
|
|
|
|
|
clickOverlayOnClose: {
|
|
|
|
|
type: Boolean,
|
|
|
|
|
default: true
|
|
|
|
|
},
|
|
|
|
|
// 当前下拉选项对象
|
|
|
|
|
currentDropItem: {
|
|
|
|
|
type: Object,
|
|
|
|
|
default () {
|
|
|
|
|
return {
|
|
|
|
|
activeIndex: 0,
|
|
|
|
|
child: []
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
keyName: {
|
|
|
|
|
type: String,
|
|
|
|
|
default: 'label'
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
data() {
|
|
|
|
|
return {
|
|
|
|
|
show: false,
|
|
|
|
|
rect: {},
|
|
|
|
|
height: 0
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
computed: {
|
|
|
|
|
overlayStyle() {
|
|
|
|
|
let { height = 0, top = 0 } = this.rect;
|
|
|
|
|
// #ifdef H5
|
|
|
|
|
top += this.$uv.sys().windowTop;
|
|
|
|
|
// #endif
|
|
|
|
|
const style = {
|
|
|
|
|
position: 'fixed',
|
|
|
|
|
top: `${top+height}px`,
|
|
|
|
|
left: 0,
|
|
|
|
|
right: 0,
|
|
|
|
|
zIndex: this.zIndex,
|
|
|
|
|
bottom: 0,
|
|
|
|
|
'background-color': `rgba(0, 0, 0, ${this.opacity})`
|
|
|
|
|
}
|
|
|
|
|
return this.$uv.deepMerge(style, this.$uv.addStyle(this.customStyle))
|
|
|
|
|
},
|
|
|
|
|
list() {
|
|
|
|
|
try {
|
|
|
|
|
return Array.isArray(this.currentDropItem.child) ? this.currentDropItem.child : [];
|
|
|
|
|
} catch (e) {
|
|
|
|
|
return [];
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
getTextColor(index) {
|
|
|
|
|
return index => {
|
|
|
|
|
const active = this.currentDropItem.activeIndex == index;
|
|
|
|
|
const color = this.currentDropItem.color;
|
|
|
|
|
const activeColor = this.currentDropItem.activeColor;
|
|
|
|
|
if (active) {
|
|
|
|
|
return activeColor ? activeColor : '#3c9cff';
|
|
|
|
|
}
|
|
|
|
|
return color ? color : '#333';
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
getTextSize(index) {
|
|
|
|
|
return index => {
|
|
|
|
|
const active = this.currentDropItem.activeIndex == index;
|
|
|
|
|
const size = this.currentDropItem.size;
|
|
|
|
|
const activeSize = this.currentDropItem.activeSize;
|
|
|
|
|
if (active) {
|
|
|
|
|
return activeSize ? activeSize : '30rpx';
|
|
|
|
|
}
|
|
|
|
|
return size ? size : '30rpx';
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
itemCustomStyle() {
|
|
|
|
|
return index => {
|
|
|
|
|
const active = this.currentDropItem.activeIndex == index;
|
|
|
|
|
const style = {};
|
|
|
|
|
if (active && this.currentDropItem.itemActiveCustomStyle) {
|
|
|
|
|
return this.$uv.deepMerge(style, this.$uv.addStyle(this.currentDropItem.itemActiveCustomStyle));
|
|
|
|
|
}
|
|
|
|
|
if (this.currentDropItem.itemCustomStyle) {
|
|
|
|
|
return this.$uv.deepMerge(style, this.$uv.addStyle(this.currentDropItem.itemCustomStyle))
|
|
|
|
|
}
|
|
|
|
|
return style;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
created() {
|
|
|
|
|
this.init();
|
|
|
|
|
},
|
|
|
|
|
methods: {
|
|
|
|
|
blockClick() {},
|
|
|
|
|
clickHandler(item, index) {
|
|
|
|
|
this.currentDropItem.activeIndex = index;
|
|
|
|
|
this.$emit('clickItem', item);
|
|
|
|
|
this.close();
|
|
|
|
|
},
|
|
|
|
|
init() {
|
|
|
|
|
uni.$off(`${this.sign}_GETRECT`);
|
|
|
|
|
uni.$on(`${this.sign}_GETRECT`, rect => {
|
|
|
|
|
this.rect = rect;
|
|
|
|
|
})
|
|
|
|
|
uni.$off(`${this.sign}_CLICKMENU`);
|
|
|
|
|
uni.$on(`${this.sign}_CLICKMENU`, async res => {
|
|
|
|
|
if (res.show) {
|
|
|
|
|
this.open();
|
|
|
|
|
} else {
|
|
|
|
|
this.close();
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
},
|
|
|
|
|
open() {
|
|
|
|
|
this.show = true;
|
|
|
|
|
this.$nextTick(async () => {
|
|
|
|
|
// #ifndef H5 || MP-WEIXIN
|
|
|
|
|
await this.$uv.sleep(60);
|
|
|
|
|
// #endif
|
|
|
|
|
const res = await this.queryRect();
|
|
|
|
|
// #ifndef APP-NVUE
|
|
|
|
|
this.height = res.height;
|
|
|
|
|
// #endif
|
|
|
|
|
// #ifdef APP-NVUE
|
|
|
|
|
this.animation(res.height);
|
|
|
|
|
// #endif
|
|
|
|
|
this.$emit('popupChange', { show: true });
|
|
|
|
|
})
|
|
|
|
|
},
|
|
|
|
|
close() {
|
|
|
|
|
if(!this.show) return;
|
|
|
|
|
this.height = 0;
|
|
|
|
|
// #ifndef APP-NVUE
|
|
|
|
|
this.height = 0;
|
|
|
|
|
// #endif
|
|
|
|
|
// #ifdef APP-NVUE
|
|
|
|
|
this.animation(0);
|
|
|
|
|
// #endif
|
|
|
|
|
this.show = false;
|
|
|
|
|
uni.$emit(`${this.sign}_CLOSEPOPUP`);
|
|
|
|
|
this.$emit('popupChange', { show: false });
|
|
|
|
|
},
|
|
|
|
|
clickOverlay() {
|
|
|
|
|
if (this.clickOverlayOnClose) {
|
|
|
|
|
this.close();
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
// 查询内容高度
|
|
|
|
|
queryRect() {
|
|
|
|
|
// #ifndef APP-NVUE
|
|
|
|
|
// 组件内部一般用this.$uvGetRect,对外的为getRect,二者功能一致,名称不同
|
|
|
|
|
return new Promise(resolve => {
|
|
|
|
|
this.$uvGetRect(`.uv-dp__container__list`).then(size => {
|
|
|
|
|
resolve(size)
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
// #endif
|
|
|
|
|
// #ifdef APP-NVUE
|
|
|
|
|
// nvue下,使用dom模块查询元素高度
|
|
|
|
|
// 返回一个promise,让调用此方法的主体能使用then回调
|
|
|
|
|
return new Promise(resolve => {
|
|
|
|
|
dom.getComponentRect(this.$refs.uvDPList, res => {
|
|
|
|
|
resolve(res.size)
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
// #endif
|
|
|
|
|
},
|
|
|
|
|
// nvue下设置高度
|
|
|
|
|
animation(height, duration = 200) {
|
|
|
|
|
// #ifdef APP-NVUE
|
|
|
|
|
const ref = this.$refs['uvDPContainer'];
|
|
|
|
|
animation.transition(ref, {
|
|
|
|
|
styles: {
|
|
|
|
|
height: `${height}px`
|
|
|
|
|
},
|
|
|
|
|
duration
|
|
|
|
|
})
|
|
|
|
|
// #endif
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
</script>
|
|
|
|
|
<style scoped lang="scss">
|
|
|
|
|
.uv-dp__container {
|
|
|
|
|
/* #ifndef APP-NVUE */
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
transition: all .15s;
|
|
|
|
|
/* #endif */
|
|
|
|
|
background-color: #fff;
|
|
|
|
|
}
|
|
|
|
|
.uv-dp__container__list {
|
|
|
|
|
&--item {
|
|
|
|
|
padding: 20rpx 60rpx;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
</style>
|