Browse Source

增加popover组件

ming 2 years ago
parent
commit
85a0001b3c
2 changed files with 279 additions and 1 deletions
  1. 5 1
      components/fs-mask/fs-mask.vue
  2. 274 0
      components/fs-popover/fs-popover.vue

+ 5 - 1
components/fs-mask/fs-mask.vue

@@ -19,6 +19,10 @@ const props = defineProps({
 	maskClickable: {
 		type: Boolean,
 		default: true
+	},
+	bgColor: {
+		type: String,
+		default: 'rgba(0, 0, 0, 0.5)'
 	}
 })
 const emits = defineEmits(['update:modelValue','close'])
@@ -38,7 +42,7 @@ const handleMask = () => {
 	right: 0;
 	bottom: var(--window-bottom);
 	left: 0;
-	background-color: rgba(0, 0, 0, 0.5);
+	background-color: v-bind(bgColor);
 	
 	&-blur{
 		backdrop-filter: blur(12rpx);

+ 274 - 0
components/fs-popover/fs-popover.vue

@@ -0,0 +1,274 @@
+<template>
+	<view class="fs-popover">
+		<view class="fs-popover-refer" @click="handleClickRefer">
+			<slot name="refer"></slot>
+		</view>
+		<view class="fs-popover-action" v-if="actionVisible" :class="['fs-popover-' + placement]" @click="handleClose">
+			<view class="fs-popover-arrow"></view>
+			<slot>
+				<view class="fs-popover-action-item line1" v-for="(item, index) in actions" :key="index" @click="handleClickAction(item)">{{item.text}}</view>
+			</slot>
+		</view>
+		
+		<fs-mask v-model="actionVisible" :z-index="100" bgColor="rgba(0,0,0,0)"></fs-mask>
+	</view>
+</template>
+
+<script>
+export default {
+	name:"fs-popover",
+}
+</script>
+
+<script setup>
+import { ref, onMounted } from 'vue'
+import utils from '@/utils/utils'
+
+const props = defineProps({
+	placement: {
+		type: String,
+		default: 'bottom',
+		validator(value) {
+			return ['top', 'top-start', 'top-end', 'bottom', 'bottom-start', 'bottom-end','left', 'left-start', 'left-end','right', 'right-start', 'right-end'].includes(value)
+		}
+	},
+	actions: Array,
+	bgColor: {
+		type: String,
+		default: '#fff'
+	},
+	textColor: {
+		type: String,
+		default: '#666'
+	},
+	borderColor: {
+		type: String,
+		default: '#E8EAF2'
+	},
+	zIndex: {
+		type: Number,
+		default: 99
+	}
+})
+const emits = defineEmits(['clickAction'])
+
+const uuid = utils.uuid()
+
+const actionVisible = ref(false)
+const handleClickRefer = () => {
+	uni.$emit('popoverClose', { uuid: uuid })
+	actionVisible.value = !actionVisible.value
+}
+
+const handleClickAction = item => {
+	emits('clickAction', item)
+}
+const handleClose = () => {
+	actionVisible.value = false
+}
+
+uni.$on('popoverClose', res => {
+	if (uuid !== res.uuid) {
+		handleClose()
+	}
+})
+
+defineExpose({
+	handleClose
+})
+</script>
+
+<style lang="scss" scoped>
+$margin: 20rpx;
+$arrowOffset: 30rpx;
+
+.fs-popover{
+	display: inline-block;
+	position: relative;
+	z-index: v-bind(zIndex);
+	
+	&-action{
+		position: absolute;
+		min-width: 260rpx;
+		z-index: calc(v-bind(zIndex) + 1);
+		background-color: v-bind(bgColor);
+		transition: opacity .15s,transform .15s;
+		border-radius: 12rpx;
+		padding: 0 30rpx;
+		color: v-bind(textColor);
+		
+		&-item{
+			padding: 20rpx;
+			& + &{
+				border-top: 2rpx solid v-bind(borderColor);
+			}
+		}
+	}
+	
+	&-arrow{
+		position: absolute;
+		width: 0;
+		height: 0;
+		border-color: transparent;
+		border-style: solid;
+		border-width: 12rpx;
+	}
+	
+	&-top{
+		left: 50%;
+		bottom: 100%;
+		transform: translateX(-50%);
+		margin-bottom: $margin;
+		.fs-popover-arrow{
+			border-bottom-width: 0;
+			border-top-color: v-bind(bgColor);
+			bottom: 0;
+			left: 50%;
+			transform: translate(-50%, 100%);
+		}
+	}
+	&-top-start{
+		left: 0;
+		bottom: 100%;
+		margin-bottom: $margin;
+		.fs-popover-arrow{
+			border-bottom-width: 0;
+			border-top-color: v-bind(bgColor);
+			bottom: 0;
+			left: $arrowOffset;
+			transform: translateY(100%);
+		}
+	}
+	&-top-end{
+		right: 0;
+		bottom: 100%;
+		margin-bottom: $margin;
+		.fs-popover-arrow{
+			border-bottom-width: 0;
+			border-top-color: v-bind(bgColor);
+			bottom: 0;
+			right: $arrowOffset;
+			transform: translateY(100%);
+		}
+	}
+	
+	&-bottom{
+		left: 50%;
+		top: 100%;
+		transform: translateX(-50%);
+		margin-top: $margin;
+		
+		.fs-popover-arrow{
+			border-top-width: 0;
+			border-bottom-color: v-bind(bgColor);
+			top: 0;
+			left: 50%;
+			transform: translate(-50%, -100%);
+		}
+	}
+	&-bottom-start{
+		left: 0;
+		top: 100%;
+		margin-top: $margin;
+		
+		.fs-popover-arrow{
+			border-top-width: 0;
+			border-bottom-color: v-bind(bgColor);
+			top: 0;
+			left: $arrowOffset;
+			transform: translateY(-100%);
+		}
+	}
+	&-bottom-end{
+		right: 0;
+		top: 100%;
+		margin-top: $margin;
+		
+		.fs-popover-arrow{
+			border-top-width: 0;
+			border-bottom-color: v-bind(bgColor);
+			top: 0;
+			right: $arrowOffset;
+			transform: translateY(-100%);
+		}
+	}
+	
+	&-left{
+		left: -$margin;
+		top: 50%;
+		transform: translate(-100%, -50%);
+		.fs-popover-arrow{
+			border-right-width: 0;
+			border-left-color: v-bind(bgColor);
+			top: 50%;
+			right: 0;
+			transform: translate(100%, -50%);
+		}
+	}
+	&-left-start{
+		left: -$margin;
+		top: 0;
+		transform: translateX(-100%);
+		
+		.fs-popover-arrow{
+			border-right-width: 0;
+			border-left-color: v-bind(bgColor);
+			top: $arrowOffset;
+			right: 0;
+			transform: translateX(100%);
+		}
+	}
+	&-left-end{
+		left: -$margin;
+		bottom: 0;
+		transform: translateX(-100%);
+		
+		.fs-popover-arrow{
+			border-right-width: 0;
+			border-left-color: v-bind(bgColor);
+			bottom: $arrowOffset;
+			right: 0;
+			transform: translateX(100%);
+		}
+	}
+	
+	&-right{
+		left: 100%;
+		top: 0;
+		transform: translateY(-50%);
+		margin-left: $margin;
+		
+		.fs-popover-arrow{
+			border-left-width: 0;
+			border-right-color: v-bind(bgColor);
+			top: 50%;
+			left: 0;
+			transform: translateX(-50%);
+		}
+	}
+	&-right-start{
+		left: 100%;
+		top: 0;
+		margin-left: $margin;
+		.fs-popover-arrow{
+			border-left-width: 0;
+			border-right-color: v-bind(bgColor);
+			top: $arrowOffset;
+			left: 0;
+			transform: translateX(-100%);
+		}
+	}
+	&-right-end{
+		left: 100%;
+		bottom: 0;
+		margin-left: $margin;
+		.fs-popover-arrow{
+			border-left-width: 0;
+			border-right-color: v-bind(bgColor);
+			left: 0;
+			bottom: $arrowOffset;
+			transform: translateX(-100%);
+		}
+	}
+}
+</style>