This commit is contained in:
2025-06-13 20:48:15 +08:00
parent 898b816655
commit a41fbbd663
42 changed files with 7189 additions and 0 deletions
+6
View File
@@ -0,0 +1,6 @@
## 1.0.2(2023-10-22)
修改报错
## 1.0.1(2023-06-14)
增加 readme.md
## 1.0.0(2023-06-14)
初始化
@@ -0,0 +1,492 @@
<template>
<view class="zbPopover"
:style="{
'--theme-bg-color':bgStyleColor
}"
>
<view @click.stop="handleClick" class="zb-button-popover">
<slot></slot>
</view>
<view
class="zb-popover"
v-show="inited"
ref="zb-transition"
:class="[classes,`zb-popover-${placement}`]"
:style="[mergeStyle]"
@touchmove="noop"
>
<view class="zb-popover-arrow"
:style="[arrowStyle]"
:class="[{
'zb_popper__up':placement.indexOf('bottom')===0,
'zb_popper__arrow':placement.indexOf('top')===0,
'zb_popper__right':placement.indexOf('right')===0,
'zb_popper__left':placement.indexOf('left')===0,
}]"
>
</view>
<slot name="content">
<view :class="[{
'horizontal__action':actionsDirection==='horizontal'
}]">
<view
@click.stop="actionAction(item)"
v-for="item,index in options"
class="zb-popover__action"
:class="[{
'dark__action':theme==='dark'
}]"
:key="index">
<view
class="zb-popover__action-text"
>{{item.text}}</view>
</view>
</view>
</slot>
</view>
</view>
</template>
<script>
const tranClass = {
enter: "zb-fade-zoom-enter zb-fade-zoom-enter-active",
'enter-to': "zb-fade-zoom-enter-to zb-fade-zoom-enter-active",
leave: "zb-fade-zoom-leave zb-fade-zoom-leave-active",
'leave-to': "zb-fade-zoom-leave-to zb-fade-zoom-leave-active",
}
export default {
props:{
options:{
type:Array,
default: ()=>[]
},
placement:{
type:String,
default:'bottom-start'
},
bgColor:{
type:String,
},
// light dark
theme:{
type:String,
default:'light'
},
// horizontal vertical
actionsDirection:{
type:String,
default:'vertical'
}
},
name:"Popover",
watch: {
show: {
handler(newVal) {
newVal ? this.vueEnter() : this.vueLeave()
},
// 表示同时监听初始化时的props的show的意思
immediate: true
}
},
data() {
return {
show:false,
inited: false, // 是否显示/隐藏组件
classes: '', // 应用的类名
display: false, // 组件是否展示
duration:100,
popoverStyle:{
},
arrowOldStyle:{}
};
},
computed:{
bgStyleColor(){
if(this.bgColor){
return this.bgColor
}
if(this.theme ==='light'){
return 'white'
}
if(this.theme ==='dark'){
return '#4a4a4a'
}
},
mergeStyle(){
return {
transitionDuration: `${this.duration}ms`,
transitionTimingFunction: `ease-out`,
...this.popoverStyle
}
},
arrowStyle(){
return {
...this.arrowOldStyle
}
}
},
mounted(){
// #ifdef H5
window.addEventListener('click',()=>{
this.show = false
})
// #endif
},
methods:{
handleClick(){
if(this.show){
this.show = false
}else {
this.show = true
}
this.$emit('handleClick',this.show)
},
close(){
this.show = false
},
actionAction(item){
this.$emit('select',item)
this.show = false
},
sleep(value){
return new Promise((resolve) => {
setTimeout(() => {
resolve()
}, value)
})
},
vueEnter(){
this.inited = true
this.getPosition()
this.classes = tranClass.enter
this.$nextTick(async () => {
await this.sleep(30)
this.classes = tranClass['enter-to']
})
},
vueLeave(){
this.classes = tranClass.leave
this.$nextTick(async () => {
this.classes = tranClass['leave-to']
await this.sleep(120)
this.inited = false
})
},
// 阻止事件冒泡
preventEvent(e) {
e && typeof (e.stopPropagation) === 'function' && e.stopPropagation()
},
getPosition(){
return new Promise((resolve) => {
this.$nextTick(()=>{
let selectorQuery = uni.createSelectorQuery().in(this).selectAll('.zb-button-popover,.zb-popover')
selectorQuery.boundingClientRect(async (data)=>{
let {left,bottom,right,top,width,height} = data[0]
let popoverClientRect = data[1]
let popoverStyle = {}
let arrowOldStyle ={}
switch (this.placement) {
case 'top':
if(popoverClientRect.width > width){
popoverStyle.left = `-${(popoverClientRect.width - width)/2}px`
}else{
popoverStyle.left = `${Math.abs(popoverClientRect.width - width)/2}px`
}
popoverStyle.bottom =`${height+8}px`
arrowOldStyle.left = (popoverClientRect.width/2-6)+'px'
break;
case 'top-start':
popoverStyle.left = `0px`
popoverStyle.bottom = `${height+8}px`
arrowOldStyle.left = '16px'
break;
case 'top-end':
popoverStyle.right = `0px`
popoverStyle.bottom = `${height+8}px`
arrowOldStyle.right = '16px'
break;
case 'bottom':
if(popoverClientRect.width>width){
popoverStyle.left = `-${(popoverClientRect.width - width)/2}px`
}else{
popoverStyle.left = `${Math.abs(popoverClientRect.width - width)/2}px`
}
popoverStyle.top =`${height+8}px`
arrowOldStyle.left = (popoverClientRect.width/2-6)+'px'
break;
case 'bottom-start':
popoverStyle.top = `${height+8}px`
popoverStyle.left = `0px`
arrowOldStyle.left = '16px'
break;
case 'bottom-end':
popoverStyle.top = `${height+8}px`
popoverStyle.right = `0px`
arrowOldStyle.right = '16px'
break;
case 'right':
popoverStyle.left = `${width+8}px`
if(popoverClientRect.height>height){
popoverStyle.top =`-${(popoverClientRect.height - height)/2}px`
}else{
popoverStyle.top =`${Math.abs((popoverClientRect.height - height)/2)}px`
}
arrowOldStyle.top = `${popoverClientRect.height/2-6}px`
break;
case 'right-start':
popoverStyle.left = `${width+8}px`
popoverStyle.top =`0px`
arrowOldStyle.top = `8px`
break;
case 'right-end':
popoverStyle.left = `${width+8}px`
popoverStyle.bottom =`0px`
arrowOldStyle.bottom = `8px`
break;
case 'left':
popoverStyle.right = `${width+8}px`
if(popoverClientRect.height>height){
popoverStyle.top =`-${(popoverClientRect.height - height)/2}px`
}else{
popoverStyle.top =`${Math.abs((popoverClientRect.height - height)/2)}px`
}
arrowOldStyle.top = `${popoverClientRect.height/2-6}px`
break;
case 'left-start':
popoverStyle.right = `${width+8}px`
popoverStyle.top =`0px`
arrowOldStyle.top = `8px`
break;
case 'left-end':
popoverStyle.right = `${width+8}px`
popoverStyle.bottom =`0px`
arrowOldStyle.bottom = `8px`
break;
}
this.popoverStyle = popoverStyle
this.arrowOldStyle = arrowOldStyle
resolve()
}).exec()
})
})
},
// 空操作
noop(e) {
this.preventEvent(e)
}
}
}
</script>
<style lang="scss" scoped>
$theme-bg-color: var(--theme-bg-color);
.zbPopover{
position: relative;
}
.zb-button-popover{
display: inline-block;
}
.zb-popover{
border-radius: 8px;
z-index: 2144;
position: absolute;
background-color: $theme-bg-color;
box-shadow: 0 2px 12px #3232331f;
}
.zb-popover-top{
transform-origin: 50% bottom;
}
.zb-popover-top-start{
transform-origin: 50% bottom;
}
.zb-popover-top-end{
transform-origin: 0 bottom;
}
.zb-popover-bottom{
transform-origin: 50% 0;
}
.zb-popover-bottom-end{
transform-origin: 100% 0;
}
.zb-popover-bottom-start{
transform-origin: 0 0;
}
.zb-popover-right{
transform-origin: left 50%;
}
.zb-popover-right-start{
transform-origin: left 0;
}
.zb-popover-right-end{
transform-origin: left 100%;
}
.zb-popover-left{
transform-origin: right 50%;
}
.zb-popover-left-start{
transform-origin: right 0;
}
.zb-popover-left-end{
transform-origin: right 100%;
}
.zb-popover-arrow{
position: absolute;
width: 0;
height: 0;
border-color: transparent;
border-style: solid;
border-width: 6px;
color:$theme-bg-color;
}
.zb_popper__up{
border-top-width: 0;
border-bottom-color: currentColor;
top: -6px;
}
.zb_popper__right{
border-left-width: 0;
border-right-color: currentColor;
left:-5px;
}
.zb_popper__left{
border-right-width: 0;
border-left-color: currentColor;
right:-5px;
}
.zb_popper__arrow{
border-bottom-width: 0;
border-top-color: currentColor;
bottom: -6px;
}
.zb-popover__action{
position: relative;
display: flex;
align-items: center;
box-sizing: border-box;
height: 88rpx;
padding: 0 40rpx;
font-size: 32rpx;
cursor: pointer;
}
.zb-popover__action-text{
display: flex;
flex: 1;
align-items: center;
justify-content: center;
height: 100%;
padding: 0 30rpx;
border-bottom:1rpx solid #ebedf0;
word-wrap: break-word;
white-space: nowrap;
}
.zb-popover__action:last-child{
.zb-popover__action-text{
border-bottom:none;
}
}
.dark__action{
color: white;
.zb-popover__action-text{
border-bottom:1rpx solid #ebedf033
}
}
.horizontal__action{
display: flex;
.zb-popover__action{
padding: 0 20rpx;
border-right:1rpx solid #ebedf0;
}
.zb-popover__action-text{
padding:0;
//border-right:1rpx solid #ebedf0;
}
}
$u-zoom-scale: scale(0.95);
.zb-fade-enter-active,
.zb-fade-leave-active {
transition-property: opacity;
}
.zb-fade-enter,
.zb-fade-leave-to {
opacity: 0
}
.zb-fade-zoom-enter,
.zb-fade-zoom-leave-to {
transform: $u-zoom-scale;
opacity: 0;
}
.zb-fade-zoom-enter-active,
.zb-fade-zoom-leave-active {
transition-property: transform, opacity;
}
.zb-fade-down-enter-active,
.zb-fade-down-leave-active,
.zb-fade-left-enter-active,
.zb-fade-left-leave-active,
.zb-fade-right-enter-active,
.zb-fade-right-leave-active,
.zb-fade-up-enter-active,
.zb-fade-up-leave-active {
transition-property: opacity, transform;
}
.zb-fade-up-enter,
.zb-fade-up-leave-to {
transform: translate3d(0, 100%, 0);
opacity: 0
}
.zb-fade-down-enter,
.zb-fade-down-leave-to {
transform: translate3d(0, -100%, 0);
opacity: 0
}
.zb-fade-left-enter,
.zb-fade-left-leave-to {
transform: translate3d(-100%, 0, 0);
opacity: 0
}
.zb-fade-right-enter,
.zb-fade-right-leave-to {
transform: translate3d(100%, 0, 0);
opacity: 0
}
.zb-popover__action {
&:active {
background-color: rgba(0, 0, 0, 0.2);
}
&--disabled {
color: var(--van-popover-dark-action-disabled-text-color);
&:active {
background-color: transparent;
}
}
}
</style>
+82
View File
@@ -0,0 +1,82 @@
{
"id": "zb-popover",
"displayName": "zb-popover 气泡弹出框",
"version": "1.0.2",
"description": "Popover 气泡弹出框",
"keywords": [
"Popover",
"气泡弹出框"
],
"repository": "",
"engines": {
"HBuilderX": "^3.1.0"
},
"dcloudext": {
"type": "component-vue",
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "无",
"data": "无",
"permissions": "无"
},
"npmurl": ""
},
"uni_modules": {
"dependencies": [],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y"
},
"client": {
"Vue": {
"vue2": "y",
"vue3": "y"
},
"App": {
"app-vue": "u",
"app-nvue": "u"
},
"H5-mobile": {
"Safari": "y",
"Android Browser": "y",
"微信浏览器(Android)": "y",
"QQ浏览器(Android)": "y"
},
"H5-pc": {
"Chrome": "y",
"IE": "y",
"Edge": "y",
"Firefox": "y",
"Safari": "y"
},
"小程序": {
"微信": "y",
"阿里": "y",
"百度": "y",
"字节跳动": "y",
"QQ": "y",
"钉钉": "y",
"快手": "y",
"飞书": "y",
"京东": "y"
},
"快应用": {
"华为": "y",
"联盟": "y"
}
}
}
}
}
+73
View File
@@ -0,0 +1,73 @@
## 介绍
弹出式的气泡菜单。
## 友情链接
#### vue-admin-perfect —— [企业级、通用型中后台前端解决方案 预览地址](https://yuanzbz.gitee.io/vue-admin-perfect/#/home/)
#### vue-admin-perfect —— [企业级、通用型中后台前端解决方案(基于vue3.0+TS+Element-Plus 最新版,同时支持电脑,手机,平板)](https://github.com/zouzhibin/vue-admin-perfect)
## Events 事件
|事件名 | 说明 | 回调参数 |
| ------ | ------ |
| select | 点击选项时触发 | item |
| handleClick | 点击按钮事件 | boolean |
## Tooltip 属性
| 参数 | 说明 | 类型 | 可选值 | 默认值 |
| ------ | ------ | ------ | ------ | ------ |
| options | 选项列表 | Array |-- | [] |
| theme | 主题风格,可选值为 dark| String |dark,light | light |
| actionsDirection | 选项列表的排列方向,可选值为 horizontal| String |vertical,horizontal | vertical |
| placement | Tooltip 的出现位置 | String |top/top-start/top-end/bottom/bottom-start/bottom-end/left/left-start/left-end/right/right-start/right-end | top |
placement 支持以下值:
```
top # 顶部中间位置
top-start # 顶部左侧位置
top-end # 顶部右侧位置
left # 左侧中间位置
left-start # 左侧上方位置
left-end # 左侧下方位置
right # 右侧中间位置
right-start # 右侧上方位置
right-end # 右侧下方位置
bottom # 底部中间位置
bottom-start # 底部左侧位置
bottom-end # 底部右侧位置
```
## Slots 插槽
| 参数 | 说明 |
| ------ | ------ |
| content | 显示提示框得内容 |
```
因为uniapp 中小程序中没有window对象,需手动调用 关闭
第一种办法关闭:this.$refs.Popover.close()
```
示例
```
<zb-popover placement="bottom-start"
:options="actions"
ref="Popover1"
@handleClick="handleClick"
@select="onSelect"
class="item-popover">
<button class="mini-btn"
type="primary"
size="mini">浅色风格</button>
</zb-popover>
const actions = [
{ text: '选项一' },
{ text: '选项二' },
{ text: '选项三' },
];
```