<template> <view class="shadow" :class="!show?'':'shadow-show'" :style="{backgroundColor:show?maskBg:'rgba(0,0,0,0)'}" @tap="tapMask"> <view class="popups" :class="[theme]" :style="{top: popupsTop ,left: popupsLeft,flexDirection:direction}"> <text :class="dynPlace" :style="{width:'0px',height:'0px'}" v-if="triangle"></text> <view v-for="(item,index) in popData" :key="index" @tap.stop="tapItem(item)" class="itemChild view" :class="[direction=='row'?'solid-right':'solid-bottom',item.disabled?'disabledColor':'']"> <u-icon size="35" :name="item.icon" v-if="item.icon"></u-icon><span class="title">{{item.title}}</span> </view> <slot></slot> </view> </view> </template> <script> export default { props: { maskBg: { type: String, default: "rgba(0,0,0,0)", }, placement: { type: String, default: "default", //default top-start top-end bottom-start bottom-end }, direction: { type: String, default: "column", //column row }, x: { type: Number, default: 0, }, y: { type: Number, default: 0, }, value: { type: Boolean, default: false, }, popData: { type: Array, default: () => [], }, theme: { type: String, default: "light", //light dark }, dynamic: { type: Boolean, default: false, }, gap: { type: Number, default: 20, }, triangle: { type: Boolean, default: true, }, }, data() { return { popupsTop: "0rpx", popupsLeft: "0rpx", show: false, dynPlace: "", }; }, mounted() { this.popupsPosition(); }, methods: { tapMask() { this.$emit("input", !this.value); }, tapItem(item) { if (item.disabled) return; this.$emit("tapPopup", item); this.$emit("input", !this.value); }, getStatusBar() { let promise = new Promise((resolve, reject) => { uni.getSystemInfo({ success: function (e) { let customBar; // #ifdef H5 customBar = e.statusBarHeight + e.windowTop; // #endif resolve(customBar); }, }); }); return promise; }, async popupsPosition() { let statusBar = await this.getStatusBar(); let promise = new Promise((resolve, reject) => { let popupsDom = uni.createSelectorQuery().in(this).select(".popups"); popupsDom .fields( { size: true, }, (data) => { let width = data.width; let height = data.height; let y = this.dynamic ? this.dynamicGetY(this.y, this.gap) : this.transformRpx(this.y); let x = this.dynamic ? this.dynamicGetX(this.x, this.gap) : this.transformRpx(this.x); // #ifdef H5 y = this.dynamic ? this.y + statusBar : this.transformRpx(this.y + statusBar); // #endif this.dynPlace = this.placement == "default" ? this.getPlacement(x, y) : this.placement; switch (this.dynPlace) { case "top-start": this.popupsTop = `${y + 9}rpx`; this.popupsLeft = `${x - 15}rpx`; break; case "top-end": this.popupsTop = `${y + 9}rpx`; this.popupsLeft = `${x + 15 - width}rpx`; break; case "bottom-start": this.popupsTop = `${y - 18 - height}rpx`; this.popupsLeft = `${x - 15}rpx`; break; case "bottom-end": this.popupsTop = `${y - 9 - height}rpx`; this.popupsLeft = `${x + 15 - width}rpx`; break; } resolve(); } ) .exec(); }); return promise; }, getPlacement(x, y) { let width = uni.getSystemInfoSync().windowWidth; let height = uni.getSystemInfoSync().windowHeight; if (x > width / 2 && y > height / 2) { return "bottom-end"; } else if (x < width / 2 && y < height / 2) { return "top-start"; } else if (x > width / 2 && y < height / 2) { return "top-end"; } else if (x < width / 2 && y > height / 2) { return "bottom-start"; } else if (x > width / 2) { return "top-end"; } else { return "top-start"; } }, dynamicGetY(y, gap) { let height = uni.getSystemInfoSync().windowHeight; y = y < gap ? gap : y; y = height - y < gap ? height - gap : y; return y; }, dynamicGetX(x, gap) { let width = uni.getSystemInfoSync().windowWidth; x = x < gap ? gap : x; x = width - x < gap ? width - gap : x; return x; }, transformRpx(params) { return (params * uni.getSystemInfoSync().screenWidth) / 375; }, }, watch: { value: { immediate: true, handler: async function (newVal, oldVal) { if (newVal) await this.popupsPosition(); this.show = newVal; }, }, placement: { immediate: true, handler(newVal, oldVal) { this.dynPlace = newVal; }, }, }, }; </script> <style lang="scss" scoped> .title { margin-left: 20rpx; } .shadow { position: fixed; top: 0; right: 0; bottom: 0; left: 0; height: 100%; z-index: 9999; transition: background 0.3s ease-in-out; visibility: hidden; &.shadow-show { visibility: visible; } } .popups { position: absolute; padding: 20rpx; border-radius: 5px; display: flex; .view { display: flex; align-items: center; padding: 15rpx 10rpx; font-size: 25rpx; } .image { display: inline-block; vertical-align: middle; width: 40rpx; height: 40rpx; margin-right: 20rpx; } } .dark { background-color: #4c4c4c; color: #fff; .top-start:after { content: ""; position: absolute; top: -18rpx; left: 10rpx; border-width: 0 20rpx 20rpx; border-style: solid; border-color: transparent transparent #4c4c4c; } .top-end:after { content: ""; position: absolute; top: -18rpx; right: 10rpx; border-width: 0 20rpx 20rpx; border-style: solid; border-color: transparent transparent #4c4c4c; } .bottom-start:after { content: ""; position: absolute; bottom: -18rpx; left: 10rpx; border-width: 20rpx 20rpx 0; border-style: solid; border-color: #4c4c4c transparent transparent; } .bottom-end:after { content: ""; position: absolute; bottom: -18rpx; right: 10rpx; border-width: 20rpx 20rpx 0; border-style: solid; border-color: #4c4c4c transparent transparent; } .disabledColor { color: #c5c8ce; } } .light { color: #515a6e; box-shadow: 0upx 0upx 30upx rgba(0, 0, 0, 0.2); background: #fff; .top-start:after { content: ""; position: absolute; top: -18rpx; left: 10rpx; border-width: 0 20rpx 20rpx; border-style: solid; border-color: transparent transparent #fff; } .top-end:after { content: ""; position: absolute; top: -18rpx; right: 10rpx; border-width: 0 20rpx 20rpx; border-style: solid; border-color: transparent transparent #fff; } .bottom-start:after { content: ""; position: absolute; bottom: -18rpx; left: 10rpx; border-width: 20rpx 20rpx 0; border-style: solid; border-color: #fff transparent transparent; } .bottom-end:after { content: ""; position: absolute; bottom: -18rpx; right: 10rpx; border-width: 20rpx 20rpx 0; border-style: solid; border-color: #fff transparent transparent; } .disabledColor { color: #c5c8ce; } } .solid-bottom { border-bottom: 1px solid #f3f5f7; } .solid-right { border-right: 1px solid #ccc; } .popups .itemChild:last-child { border: none; } </style>