242 lines
6.5 KiB
Vue
242 lines
6.5 KiB
Vue
<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> |