created
This commit is contained in:
@@ -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>
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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: '选项三' },
|
||||
];
|
||||
```
|
||||
Reference in New Issue
Block a user