Initial commit of akmon project

This commit is contained in:
2026-01-20 08:04:15 +08:00
commit 77a2bab985
1309 changed files with 343305 additions and 0 deletions

View File

@@ -0,0 +1,38 @@
## 0.1.92025-05-30
- fix: 修复演示demo问题
## 0.1.82025-05-14
- feat: 关闭icon 单位由rpx改成px
## 0.1.72025-05-07
- feat: 增加`radius`圆角属性
## 0.1.62025-04-22
- feat: 兼容uniappx 鸿蒙next
## 0.1.52025-04-18
- chore: 更新文档
## 0.1.42025-03-26
- chore: 组件的`position`值由`top`改为`center`,与文档保持一致
## 0.1.32025-03-24
- chore: 组件的`click-icon`改为`click-close`,与文档保持一致
## 0.1.22025-02-13
- fix: 修复遮罩层z-index固定值的问题
## 0.1.12025-01-22
- chore: `closeOnOverlayClick`改为`closeOnClickOverlay`
## 0.1.02025-01-18
- chore: 更新文档
## 0.0.92025-01-15
- fix: 修复`closeOnOverlayClick`无效的问题
## 0.0.82025-01-13
- fix: uniapp x ios 居中弹出不使用scale,因为会导致不居中为官方bug
## 0.0.72024-11-26
- feat: z-index改成999
## 0.0.62024-11-13
- chore: 更新文档
## 0.0.52024-11-03
- feat: z-index降到888
## 0.0.42024-10-27
- chore: 更新文档
## 0.0.32024-10-26
- chore: 去掉多余log
## 0.0.22024-10-24
- feat: 兼容vue2
## 0.0.12024-10-24
- init

View File

@@ -0,0 +1,140 @@
@import '~@/uni_modules/lime-style/index.scss';
// $prefix: l !default;
$popup: #{$prefix}-popup;
$popup-bg-color: create-var(popup-bg-color, #fff);
$popup-close-icon-color: create-var(popup-close-icon-color, rgba(0,0,0,0.6));
$popup-border-radius: create-var(popup-border-radius, $border-radius-lg);
.#{$popup} {
position: fixed;
// z-index: 11500;
// max-height: 100vh;
// transition: translateY 1000ms ease;
transition-duration: 300ms;
transition-property: transform, opacity;
transition-timing-function: ease;
background-color: $popup-bg-color;
overflow: visible;
opacity: 1; // uniapp x ios 必须写
&__close {
position: absolute;
top: 0;
right: 0;
padding: 20rpx;
/* #ifndef APP-ANDROID || APP-IOS || APP-HARMONY */
color: $popup-close-icon-color;
/* #endif */
&-icon {
color: $popup-close-icon-color;
}
}
&--top {
top: 0;
left: 0;
right: 0;
border-bottom-left-radius: $popup-border-radius;
border-bottom-right-radius: $popup-border-radius;
// transform: scale(1) translateY(0)
transform: scale(1) translate(0, 0)
}
&--bottom {
bottom: 0;
left: 0;
right: 0;
border-top-left-radius: $popup-border-radius;
border-top-right-radius: $popup-border-radius;
// transform: scale(1) translateY(0);
transform: scale(1) translate(0, 0);
}
&--safe-top {
/* #ifndef APP-ANDROID || APP-IOS || APP-HARMONY */
padding-top: constant(safe-area-inset-top);
padding-top: env(safe-area-inset-top);
/* #endif */
}
&--safe-bottom {
/* #ifndef APP-ANDROID || APP-IOS || APP-HARMONY */
padding-bottom: constant(safe-area-inset-bottom);
padding-bottom: env(safe-area-inset-bottom);
/* #endif */
}
&--left {
top: 0;
left: 0;
bottom: 0;
// transform: scale(1) translateX(0);
transform: scale(1) translate(0, 0);
// height: 100vh;
}
&--right {
top: 0;
right: 0;
bottom: 0;
// transform: scale(1) translateX(0);
transform: scale(1) translate(0, 0);
// height: 100vh;
}
&--center {
top: 50%;
left: 50%;
/* #ifdef APP-IOS */
transform: translate(-50%, -50%);
/* #endif */
/* #ifndef APP-IOS */
transform: translate(-50%, -50%) scale(1);
/* #endif */
transform-origin: 50% 50%;
border-radius: $popup-border-radius;
}
&.#{$popup}-fade-enter,
&.#{$popup}-fade-leave-to {
opacity: 0;
&.#{$popup}--top {
// transform: translateY(-100%);
transform: scale(1) translate(0, -100%);
}
&.#{$popup}--bottom {
// transform: translateY(100%);
transform: scale(1) translate(0, 100%);
}
&.#{$popup}--left {
// transform: translateX(-100%);
transform: scale(1) translate(-100%, 0);
}
&.#{$popup}--right {
// transform: translateX(100%);
transform: scale(1) translate(100%, 0);
}
&.#{$popup}--center {
// transform: scale(0.6) translate(-50%, -50%);
/* #ifndef APP-IOS */
transform: translate(-50%, -50%) scale(0.6);
/* #endif */
/* #ifdef APP-IOS */
transform: translate(-50%, -50%);
/* #endif */
opacity: 0;
}
}
&.#{$prefix}-dialog-enter,
&.#{$prefix}-dialog-leave-to {
&.#{$popup}--center {
transform: scale(0.6) translate(-50%, -50%);
opacity: 0;
}
}
}

View File

@@ -0,0 +1,201 @@
<template>
<l-overlay
:visible="innerValue"
:zIndex="overlayZIndex"
:appear="true"
:preventScrollThrough="preventScrollThrough"
:l-style="overlayStyle"
@click="handleOverlayClick"
v-if="destroyOnClose ? display && overlay : overlay">
</l-overlay>
<view class="l-popup"
ref="popupRef"
v-if="destroyOnClose ? display : inited"
:class="rootClass"
:style="[styles, lStyle]"
@transitionend="finished">
<slot></slot>
<view class="l-popup__close" v-if="closeable" @click="handleClose">
<slot name="close-btn">
<l-icon class="l-popup__close-icon" :name="closeIcon" size="27px" :color="iconColor" />
</slot>
</view>
</view>
</template>
<script lang="uts" setup>
/**
* Popup 弹出层组件
* @description 提供多种位置的弹窗展示能力,支持自定义内容和动画效果
* <br>插件类型LPopupComponentPublicInstance
* @tutorial https://ext.dcloud.net.cn/plugin?name=lime-popup
*
* @property {boolean} closeable 显示关闭按钮默认false
* @property {boolean} closeOnClickOverlay 点击遮罩关闭默认true
* @property {boolean} destroyOnClose 关闭时销毁内容默认false
* @property {string} overlayStyle 遮罩层样式支持CSS字符串
* @property {'top' | 'left' | 'right' | 'bottom' | 'center' | ''} position 弹出位置
* @value top 从顶部滑入
* @value bottom 从底部滑入
* @value left 从左侧滑入
* @value right 从右侧滑入
* @value center 居中显示(默认)
* @property {boolean} preventScrollThrough 阻止滚动穿透默认true
* @property {boolean} overlay 显示遮罩层默认true
* @property {string} transitionName 自定义动画名称配合transition使用
* @property {string|number|Array} radius 圆角 可以是字符,数值,数组
* @property {boolean} visible 控制显示/隐藏支持v-model
* @property {number} zIndex 组件层级默认999
* @property {number} duration 动画时长单位ms默认300
* @property {string} bgColor 内容区域背景色(默认:#ffffff
* @property {string} closeIcon 关闭图标名称/路径(默认:'close'
* @property {string} iconColor 关闭图标颜色(默认:#333
* @property {string} lStyle 自定义内容区样式支持CSS字符串
* @property {boolean} safeAreaInsetBottom 适配底部安全区域默认true
* @property {boolean} safeAreaInsetTop 适配顶部安全区域默认true
* @event {Function} close 关闭时触发(返回触发来源:'close-btn' | 'overlay'
* @event {Function} change 切换时触发
* @event {Function} click-overlay 点击遮罩触发
* @event {Function} click-close 点击关闭触发
* @event {Function} open 打开触发
* @event {Function} opened 打开完成触发
* @event {Function} closed 关闭完成触发
* @event {Function} before-enter
* @event {Function} enter
* @event {Function} after-enter
* @event {Function} before-leave
* @event {Function} leave
* @event {Function} after-leave
*/
import { useTransition, UseTransitionOptions, TransitionEmitStatus } from '@/uni_modules/lime-transition';
import { convertRadius } from './utils'
import { PopupProps } from './type';
const emit = defineEmits(['change', 'click-overlay', 'click-close', 'open', 'opened','close','closed', 'before-enter', 'enter', 'after-enter', 'before-leave', 'leave', 'after-leave'])
const props = withDefaults(defineProps<PopupProps>(),{
closeable: false,
overlay: true,
closeOnClickOverlay: true,
preventScrollThrough: true,
destroyOnClose: false,
safeAreaInsetBottom: true,
safeAreaInsetTop: false,
position: 'center',
zIndex: 999,
duration: 300,
closeIcon: 'close'
})
const modelValue = defineModel({type: Boolean});
const innerValue = computed({
set(value: boolean) {
modelValue.value = value;
emit('change', value)
},
get():boolean {
// #ifdef APP-ANDROID
return (props.visible ?? false) || modelValue.value// ?? false
// #endif
// #ifndef APP-ANDROID
return props.visible || modelValue.value
// #endif
}
} as WritableComputedOptions<boolean>)
const status = ref<TransitionEmitStatus>('before-enter')
const {inited, display, classes, finished} = useTransition({
defaultName: props.transitionName ?? 'popup-fade',
appear: innerValue.value,
emits: (name:TransitionEmitStatus) => {
status.value = name
if(name == 'before-enter') {
emit('open')
} else if(name == 'after-enter') {
emit('opened')
} else if(name == 'before-leave') {
emit('close')
} else if(name == 'after-leave') {
emit('closed')
}
emit(name)
},
visible: (): boolean => innerValue.value,
duration: props.duration,
} as UseTransitionOptions)
const overlayZIndex = computed(():number => props.zIndex > 0 ? props.zIndex - 1: 998);
const rootClass = computed(():string=>{
const safe = props.safeAreaInsetTop && props.position == 'top'
? 'l-popup--safe-top'
: props.safeAreaInsetBottom && props.position == 'bottom'
? 'l-popup--safe-bottom'
: ''
return `l-popup--${props.position} ${safe} ${classes.value}`
})
const {safeAreaInsets} = uni.getWindowInfo()
const styles = computed<Map<string,any>>(():Map<string,any> => {
const style = new Map<string,any>();
// #ifdef APP-ANDROID || APP-IOS || APP-HARMONY
style.set('transition-duration', (['after-leave', 'before-enter'].includes(status.value) ? 0 : props.duration) + 'ms')
// #endif
// #ifndef APP-ANDROID || APP-IOS || APP-HARMONY
style.set('transition-duration', props.duration + 'ms')
// #endif
if (props.bgColor != null) {
style.set("background", props.bgColor!)
}
if (props.zIndex > 0) {
style.set("z-index", props.zIndex)
}
if(props.safeAreaInsetBottom && props.position == 'bottom') {
style.set('padding-bottom', safeAreaInsets.bottom + 'px')
}
if(props.safeAreaInsetTop && props.position == 'top') {
style.set('padding-top', safeAreaInsets.top + 'px')
}
if(props.radius != null) {
const values = convertRadius(props.radius!)
style.set('border-top-left-radius', values[0])
style.set('border-top-right-radius', values[1])
style.set('border-bottom-right-radius', values[2])
style.set('border-bottom-left-radius', values[3])
}
// #ifndef APP
if (!display.value) {
style.set("display", "none")
}
// #endif
return style
})
const handleOverlayClick = () => {
if(props.closeOnClickOverlay) {
innerValue.value = false;
emit('click-overlay')
}
}
const handleClose = () => {
innerValue.value = false;
emit('click-close')
}
// #ifdef APP
const popupRef = ref<UniElement|null>(null)
watchEffect(()=>{
if(!display.value) {
popupRef.value?.style.setProperty('display', 'none')
} else {
popupRef.value?.style.setProperty('display', 'flex')
}
})
// #endif
</script>
<style lang="scss">
@import "./index-u";
</style>

View File

@@ -0,0 +1,181 @@
<template>
<view>
<l-overlay
:visible="innerValue"
:zIndex="overlayZIndex"
:preventScrollThrough="preventScrollThrough"
:l-style="overlayStyle"
@click="handleOverlayClick"
v-if="destroyOnClose ? display && overlay : overlay">
</l-overlay>
<view class="l-popup"
v-if="destroyOnClose ? display : inited"
:class="rootClass"
:style="[styles, lStyle]"
@transitionend="finished">
<slot></slot>
<view class="l-popup__close" v-if="closeable" @click="handleClose">
<slot name="close-btn">
<l-icon class="l-popup__close-icon" :name="closeIcon" v-if="closeable" size="54rpx" :color="iconColor" />
</slot>
</view>
</view>
</view>
</template>
<script lang="ts">
// @ts-nocheck
/**
* Popup 弹出层组件
* @description 提供多种位置的弹窗展示能力,支持自定义内容和动画效果
* <br>插件类型LPopupComponentPublicInstance
* @tutorial https://ext.dcloud.net.cn/plugin?name=lime-popup
*
* @property {boolean} closeable 显示关闭按钮默认false
* @property {boolean} closeOnClickOverlay 点击遮罩关闭默认true
* @property {boolean} destroyOnClose 关闭时销毁内容默认false
* @property {string} overlayStyle 遮罩层样式支持CSS字符串
* @property {'top' | 'left' | 'right' | 'bottom' | 'center' | ''} position 弹出位置
* @value top 从顶部滑入
* @value bottom 从底部滑入
* @value left 从左侧滑入
* @value right 从右侧滑入
* @value center 居中显示(默认)
* @property {boolean} preventScrollThrough 阻止滚动穿透默认true
* @property {boolean} overlay 显示遮罩层默认true
* @property {string} transitionName 自定义动画名称配合transition使用
* @property {string|number|Array} radius 圆角 可以是字符,数值,数组
* @property {boolean} visible 控制显示/隐藏支持v-model
* @property {number} zIndex 组件层级默认999
* @property {number} duration 动画时长单位ms默认300
* @property {string} bgColor 内容区域背景色(默认:#ffffff
* @property {string} closeIcon 关闭图标名称/路径(默认:'close'
* @property {string} iconColor 关闭图标颜色(默认:#333
* @property {string} lStyle 自定义内容区样式支持CSS字符串
* @property {boolean} safeAreaInsetBottom 适配底部安全区域默认true
* @property {boolean} safeAreaInsetTop 适配顶部安全区域默认true
* @event {Function} close 关闭时触发(返回触发来源:'close-btn' | 'overlay'
* @event {Function} change 切换时触发
* @event {Function} click-overlay 点击遮罩触发
* @event {Function} click-close 点击关闭触发
* @event {Function} open 打开触发
* @event {Function} opened 打开完成触发
* @event {Function} closed 关闭完成触发
* @event {Function} before-enter
* @event {Function} enter
* @event {Function} after-enter
* @event {Function} before-leave
* @event {Function} leave
* @event {Function} after-leave
*/
import { computed, defineComponent } from '@/uni_modules/lime-shared/vue';
import { useTransition, UseTransitionOptions, TransitionEmitStatus } from '@/uni_modules/lime-transition';
import popupProps from './props'
import { convertRadius } from './utils'
export default defineComponent({
name: 'l-popup',
props: popupProps,
options: {
addGlobalClass: true,
virtualHost: true,
},
externalClasses: ['l-class'],
emits: ['change', 'click-overlay', 'click-close', 'open', 'opened','close','closed','update:modelValue', 'input', 'before-enter', 'enter', 'after-enter', 'before-leave', 'leave', 'after-leave'],
setup(props, { emit }) {
const innerValue = computed({
set(value: boolean) {
emit('change', value)
emit('update:modelValue', value)
// #ifdef VUE2
emit('input', value)
// #endif
},
get():boolean {
return props.visible || props.modelValue || props.value || false
}
} as WritableComputedOptions<boolean>)
const {inited, display, classes, finished} = useTransition({
defaultName: props.transitionName || 'popup-fade',
appear: innerValue.value,
emits: (name:TransitionEmitStatus) => {
if(name == 'before-enter') {
emit('open')
} else if(name == 'after-enter') {
emit('opened')
} else if(name == 'before-leave') {
emit('close')
} else if(name == 'after-leave') {
emit('closed')
}
emit(name)
},
visible: (): boolean => innerValue.value,
duration: props.duration,
} as UseTransitionOptions)
const overlayZIndex = computed(():number => props.zIndex > 0? props.zIndex - 1 : 998);
const rootClass = computed(():string=>{
const safe = props.safeAreaInsetTop && props.position == 'top'
? 'l-safe-area-top'
: props.safeAreaInsetBottom && props.position == 'bottom'
? 'l-safe-area-bottom'
: ''
return `l-popup--${props.position} ${safe} ${classes.value}`
// return `l-popup--${props.position} ${classes.value}`
})
const styles = computed(() => {
const style:Record<string, any> = {}
style['transition-duration'] = props.duration + 'ms'
if (props.bgColor != null) {
style["background"] = props.bgColor!
}
if (props.zIndex > 0) {
style["z-index"] = props.zIndex
}
if (!display.value) {
style["display"] = "none"
}
if(props.radius) {
const values = convertRadius(props.radius!)
style['border-top-left-radius'] = values[0]
style['border-top-right-radius'] = values[1]
style['border-bottom-right-radius'] = values[2]
style['border-bottom-left-radius'] = values[3]
}
return style
})
const handleOverlayClick = () => {
if(props.closeOnClickOverlay) {
innerValue.value = false
emit('click-overlay')
}
}
const handleClose = () => {
innerValue.value = false
emit('click-close')
}
return {
innerValue,
inited,
display,
finished,
overlayZIndex,
rootClass,
styles,
handleOverlayClick,
handleClose
}
}
})
</script>
<style lang="scss">
@import "./index-u";
</style>

View File

@@ -0,0 +1,82 @@
// @ts-nocheck
export default {
/** 是否展示关闭按钮,值为 `true` 显示默认关闭按钮;值为 `false` 则不显示关闭按钮;也可以自定义关闭按钮 */
closeable: {
type: Boolean,
default: false
},
/** 点击遮罩层是否关闭 */
closeOnClickOverlay: {
type: Boolean,
default: true,
},
/** 是否在关闭浮层时销毁浮层 */
destroyOnClose: Boolean,
/** 浮层出现位置 */
position: {
type: String,
default: 'center',
validator(val: string) : boolean {
if (!val) return true;
return ['top', 'left', 'right', 'bottom', 'center'].includes(val);
},
},
/** 防止滚动穿透 */
preventScrollThrough: {
type: Boolean,
default: true,
},
overlayStyle: {
type: [String, Object]
},
/** 是否显示遮罩层 */
overlay: {
type: Boolean,
default: true,
},
/** 弹出层内容区的动画名等价于transition组件的name属性 */
transitionName: {
type: String,
default: '',
},
/** 是否显示浮层 */
visible: {
type: Boolean,
default: undefined,
},
// vue2
value: {
type: Boolean,
default: undefined,
},
modelValue: {
type: Boolean,
default: undefined,
},
/** 组件层级 默认为 999 */
zIndex: {
type: Number,
default: 999
},
duration: {
type: Number,
default: 300
},
bgColor: {
type: String
},
iconColor: {
type: String
},
lStyle: {
type: String
},
closeIcon: {
type: String,
default: 'close'
},
radius: {
type: [String, Number, Array],
default: undefined
}
};

View File

@@ -0,0 +1,59 @@
// @ts-nocheck
export interface PopupProps {
/**
* 是否展示关闭按钮,值为 `true` 显示默认关闭按钮;值为 `false` 则不显示关闭按钮;也可以自定义关闭按钮
*/
closeable : boolean;
/**
* 点击遮罩层是否关闭
*/
closeOnClickOverlay : boolean;
/**
* 是否在关闭浮层时销毁浮层
*/
destroyOnClose : boolean;
/**
* 遮罩层的属性,透传至 overlay
*/
overlayStyle ?: string | UTSJSONObject;
// overlayProps ?: {
// preventScrollThrough: boolean
// zIndex: number
// lStyle: string
// };
/**
* 浮层出现位置
*/
position : 'top' | 'left' | 'right' | 'bottom' | 'center' | '';
/**
* 防止滚动穿透
*/
preventScrollThrough : boolean;
/**
* 是否显示遮罩层
*/
overlay : boolean;
/**
* 弹出层内容区的动画名等价于transition组件的name属性
*/
transitionName ?: string;
/**
* 是否显示浮层
*/
visible ?: boolean;
/**
* 组件层级Web 侧样式默认为 999移动端和小程序样式默认为 999
*/
zIndex : number;
duration : number;
bgColor?: string;
closeIcon: string;
iconColor?: string;
lStyle?: string | UTSJSONObject;
safeAreaInsetBottom:boolean;
safeAreaInsetTop:boolean;
radius?: string | number | (string|number)[]
}
export type PopupSource = 'close-btn' | 'overlay';

View File

@@ -0,0 +1,21 @@
import { addUnit } from '@/uni_modules/lime-shared/addUnit'
export function convertRadius(radius : any) : string[] {
if (Array.isArray(radius)) {
const values = radius.map((item) : string|null => addUnit(item))
if (values.length == 1) {
return [values[0]!, values[0]!, values[0]!, values[0]!]
}
if (values.length == 2) {
return [values[0]!, values[1]!, values[0]!, values[1]!]
}
if (values.length == 3) {
return [values[0]!, values[1]!, values[2]!, values[1]!]
}
if (values.length == 4) {
return [values[0]!, values[1]!, values[2]!, values[3]!]
}
return ['0', '0', '0', '0']
}
const value = addUnit(radius) ?? '0'
return [value, value, value, value]
}

View File

@@ -0,0 +1,171 @@
<template>
<view class="demo-block">
<text class="demo-block__title-text ultra">Popup 弹出层</text>
<text class="demo-block__desc-text">弹出层容器,用于展示弹窗、信息提示等内容</text>
<view class="demo-block__body">
<view class="demo-block">
<text class="demo-block__title-text">基础类型{{visible}}</text>
<view class="demo-block__body">
<button class="button"
v-for="item in placement"
@click="onClick(item.value)"
:key="item.value">{{item.text}}</button>
<l-popup v-model="visible"
:position="currentPlacement"
:closeable="true">
<view style="padding: 100px;"></view>
</l-popup>
</view>
</view>
<view class="demo-block">
<text class="demo-block__title-text">其它</text>
<view class="demo-block__body">
<button class="button" @click="visible1 = true">底部弹出层-带标题及操作</button>
<l-popup v-model="visible1" position="bottom" lStyle="height: 258px">
<view class="header">
<text class="btn btn--cancel" aria-role="button" @click="onHide">取消</text>
<text class="title">标题文字</text>
<text class="btn btn--confirm" aria-role="button" @click="onHide">确定</text>
</view>
</l-popup>
<button class="button" @click="visible2 = true">居中弹出层-带自定义关闭按钮</button>
<l-popup v-model="visible2" position="center" @change="change">
<view style="width: 240px; height: 240px; overflow: visible;">
</view>
<view class="close-btn">
<l-icon name="close-circle" size="32" color="white" @click="onClose" />
</view>
</l-popup>
</view>
</view>
</view>
</view>
</template>
<script lang="uts" setup>
type Placement = {
value : string
text : string
}
const top = ref(10)
const placement = [
{ value: 'top', text: '顶部弹出' },
{ value: 'left', text: '左侧弹出' },
{ value: 'center', text: '中间弹出' },
{ value: 'bottom', text: '底部弹出' },
{ value: 'right', text: '右侧弹出' },
] as Placement[]
const visible = ref(false)
const visible1 = ref(false)
const visible2 = ref(false)
const currentPlacement = ref('top')
const onClick = (value: string) => {
visible.value = true
currentPlacement.value = value
}
const change = () => {}
const onHide = () =>{
visible1.value= false
}
const onClose = ()=>{
visible2.value= false
}
</script>
<style lang="scss">
.header {
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
height: 116rpx;
}
.title {
flex: 1;
text-align: center;
// font-weight: 600;
font-size: 18px;
}
.button {
margin-bottom: 10rpx;
}
.btn {
font-size: 16px;
padding: 16px;
}
.btn--cancel {
color: rgba(0, 0, 0, 0.6);
}
.btn--confirm {
color: #0052d9;
}
.close-btn {
overflow: visible;
position: absolute;
// background-color: aqua;
left: 50%;
transform: translateX(-50%);
z-index: 11501;
// margin-left: -32rpx;
bottom: -100rpx;
// bottom: -112rpx // calc(-1 * (48rpx + 64rpx));
}
.demo-block {
margin: 32px 20px 0;
overflow: visible;
&__title {
margin: 0;
margin-top: 8px;
&-text {
color: rgba(0, 0, 0, 0.6);
font-weight: 400;
font-size: 14px;
line-height: 16px;
&.large {
color: rgba(0, 0, 0, 0.9);
font-size: 18px;
font-weight: 700;
line-height: 26px;
}
&.ultra {
color: rgba(0, 0, 0, 0.9);
font-size: 24px;
font-weight: 700;
line-height: 32px;
}
}
}
&__desc-text {
color: rgba(0, 0, 0, 0.6);
margin: 8px 16px 0 0;
font-size: 14px;
line-height: 22px;
}
&__body {
margin: 16px 0;
overflow: visible;
.demo-block {
// margin-top: 0px;
margin: 0;
}
}
}
</style>

View File

@@ -0,0 +1,174 @@
<template>
<view class="demo-block">
<text class="demo-block__title-text ultra">Popup 弹出层</text>
<text class="demo-block__desc-text">弹出层容器用于展示弹窗信息提示等内容</text>
<view class="demo-block__body">
<view class="demo-block">
<text class="demo-block__title-text">基础类型{{visible}}</text>
<view class="demo-block__body">
<button class="button"
v-for="item in placement"
@click="onClick(item.value)"
:key="item.value">{{item.text}}</button>
<l-popup v-model="visible"
:position="currentPlacement"
:closeable="true">
<view style="padding: 100px;"></view>
</l-popup>
</view>
</view>
<view class="demo-block">
<text class="demo-block__title-text">其它</text>
<view class="demo-block__body">
<button class="button" @click="visible1 = true">底部弹出层-带标题及操作</button>
<l-popup v-model="visible1" position="bottom" lStyle="height: 258px">
<view class="header">
<text class="btn btn--cancel" aria-role="button" @click="onHide">取消</text>
<text class="title">标题文字</text>
<text class="btn btn--confirm" aria-role="button" @click="onHide">确定</text>
</view>
</l-popup>
<button class="button" @click="visible2 = true">居中弹出层-带自定义关闭按钮</button>
<l-popup v-model="visible2" position="center" @change="change">
<view style="width: 240px; height: 240px; overflow: visible;">
</view>
<view class="close-btn">
<l-icon name="close-circle" size="32" color="white" @click="onClose" />
</view>
</l-popup>
</view>
</view>
</view>
</view>
</template>
<script>
export default {
data() {
return {
visible: false,
visible1: false,
visible2: false,
currentPlacement: 'top',
placement: [
{ value: 'top', text: '顶部弹出' },
{ value: 'left', text: '左侧弹出' },
{ value: 'center', text: '中间弹出' },
{ value: 'bottom', text: '底部弹出' },
{ value: 'right', text: '右侧弹出' },
]
}
},
methods: {
onClick(type) {
this.visible = true
this.currentPlacement = type
},
onHide() {
this.visible1 = !this.visible1
},
onClose() {
this.visible2 = !this.visible2
},
change() {
console.log('change')
}
}
}
</script>
<style lang="scss" scoped>
popup-demo {
padding: 0 16px;
}
.header {
display: flex;
align-items: center;
height: 116rpx;
}
.title {
flex: 1;
text-align: center;
// font-weight: 600;
font-size: 18px;
}
.btn {
font-size: 16px;
padding: 16px;
}
.btn--cancel {
color: rgba(0, 0, 0, 0.6);
}
.btn--confirm {
color: #0052d9;
}
.close-btn {
position: absolute;
left: 50%;
margin-left: -16px;
bottom: calc(-1 * (24px + 32px));
}
.wrapper {
margin: 16px;
display: block;
}
.demo-block {
margin: 32px 20px 0;
overflow: visible;
&__title {
margin: 0;
margin-top: 8px;
&-text {
color: rgba(0, 0, 0, 0.6);
font-weight: 400;
font-size: 14px;
line-height: 16px;
&.large {
color: rgba(0, 0, 0, 0.9);
font-size: 18px;
font-weight: 700;
line-height: 26px;
}
&.ultra {
color: rgba(0, 0, 0, 0.9);
font-size: 24px;
font-weight: 700;
line-height: 32px;
}
}
}
&__desc-text {
color: rgba(0, 0, 0, 0.6);
margin: 8px 16px 0 0;
font-size: 14px;
line-height: 22px;
}
&__body {
margin: 16px 0;
overflow: visible;
.demo-block {
// margin-top: 0px;
margin: 0;
}
}
}
</style>

View File

@@ -0,0 +1,93 @@
{
"id": "lime-popup",
"displayName": "lime-popup 弹出层",
"version": "0.1.9",
"description": "lime-popup 弹出层容器,用于展示弹窗、信息提示等内容,兼容uniapp/uniappx",
"keywords": [
"lime-popup",
"popup",
"弹出层",
"弹窗"
],
"repository": "",
"engines": {
"HBuilderX": "^4.29"
},
"dcloudext": {
"type": "component-vue",
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "无",
"data": "无",
"permissions": "无"
},
"npmurl": ""
},
"uni_modules": {
"dependencies": [
"lime-style",
"lime-shared",
"lime-overlay",
"lime-transition",
"lime-icon"
],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y",
"alipay": "y"
},
"client": {
"Vue": {
"vue2": "y",
"vue3": "y"
},
"App": {
"app-vue": "y",
"app-uvue": "y",
"app-nvue": "u",
"app-harmony": "u"
},
"H5-mobile": {
"Safari": "y",
"Android Browser": "y",
"微信浏览器(Android)": "u",
"QQ浏览器(Android)": "y"
},
"H5-pc": {
"Chrome": "u",
"IE": "u",
"Edge": "u",
"Firefox": "u",
"Safari": "u"
},
"小程序": {
"微信": "y",
"阿里": "u",
"百度": "u",
"字节跳动": "u",
"QQ": "u",
"钉钉": "u",
"快手": "u",
"飞书": "u",
"京东": "u"
},
"快应用": {
"华为": "u",
"联盟": "u"
}
}
}
}
}

View File

@@ -0,0 +1,218 @@
# lime-popup 弹出层
- 弹出层容器,用于展示弹窗、信息提示等内容,兼容uniapp/uniappx
- 插件依赖`lime-style`,`lime-shared`,`lime-overlay`,`lime-transition`,`lime-icon`lime-svg 为收费若不需要svg可以在代码里注释掉即可
## 安装
插件市场导入即可,首次导入可能需要重新编译
**注意**
* 🔔 本插件依赖的[lime-svg](https://ext.dcloud.net.cn/plugin?id=18519)在 uniapp x app中是原生插件如果购买(收费为5元)则需要自定义基座才能使用uniapp可忽略。
* 🔔 如果不需要[lime-svg](https://ext.dcloud.net.cn/plugin?id=18519)在lime-icon代码中注释掉即可
```html
// lime-icon/components/l-icon.uvue 第4行 注释掉即可。
<!-- <l-svg class="l-icon" :class="classes" :style="styles" :color="color" :src="iconUrl" v-else :web="web" @error="imageError" @load="imageload" @click="$emit('click')"></l-svg> -->
```
## 代码演示
### 基础用法
通过 `v-model` 控制弹出层是否展示。
```html
<button @click="show = true">展示弹出层</button>
<l-popup v-model="show">
<view style="padding: 100px;"></view>
</l-popup>
```
```js
const show = ref(false);
```
### 弹出位置
通过 `position` 属性设置弹窗的弹出位置,默认为居中弹出,可以设置为 `top``bottom``left``right``center`
- 当弹窗从顶部或底部弹出时,默认宽度与屏幕宽度保持一致,弹窗高度取决于内容的高度。
- 当弹窗从左侧或右侧弹出时,默认不设置宽度和高度,弹窗的宽高取决于内容的宽高。
```html
<!-- 顶部弹出 -->
<l-popup v-model"showTop" position="top">
<view style="padding: 100px;"></view>
</l-popup>
<!-- 左边弹出 -->
<l-popup v-model"showTop" position="left">
<view style="padding: 100px;"></view>
</l-popup>
<!-- 底部弹出 -->
<l-popup v-model"showTop" position="bottom">
<view style="padding: 100px;"></view>
</l-popup>
<!-- 右边弹出 -->
<l-popup v-model"showTop" position="right">
<view style="padding: 100px;"></view>
</l-popup>
```
### 关闭图标
设置 `closeable` 属性后,会在弹出层的右上角显示关闭图标,并且可以通过 `close-icon` 属性自定义图标。
```html
<l-popup v-model"showTop" :closeable="true">
<view style="padding: 100px;"></view>
</l-popup>
```
### 监听点击事件
Popup 支持以下点击事件:
- `click-overlay`: 点击遮罩层时触发。
- `click-close`: 点击关闭图标时触发。
```html
<button @click="show = true">展示弹出层</button>
<l-popup
v-model"show"
position="bottom"
:closeable="true"
@click-overlay="onClickOverlay"
@click-close="onClickClose"
/>
```
```js
const onClickOverlay = () => {
console.log('click-overlay');
};
const onClickClose = () => {
console.log('click-close');
};
```
### 监听显示事件
当 Popup 被打开或关闭时,会触发以下事件:
- `open`: 打开弹出层时立即触发。
- `opened`: 打开弹出层且动画结束后触发。
- `close`: 关闭弹出层时立即触发。
- `closed`: 关闭弹出层且动画结束后触发。
```html
<button @click="show = true">展示弹出层</button>
<l-popup
v-model="show"
position="bottom"
@open="handlePopupOpen"
@opened="handlePopupOpened("
@close="handlePopupClose"
@closed="handlePopupClosed"
/>
```
```js
const show = ref(false);
const handlePopupOpen = () => {
// 处理弹出框打开前的逻辑
}
const handlePopupOpened = () => {
// 处理弹出框打开后的逻辑
}
const handlePopupClose = () => {
// 处理弹出框关闭前的逻辑
}
const handlePopupClosed = () => {
// 处理弹出框关闭后的逻辑
}
```
### 查看示例
- 导入后直接使用这个标签查看演示效果
```html
<!-- // 代码位于 uni_modules/lime-popup/compoents/lime-popup -->
<lime-popup />
```
### 插件标签
- 默认 l-popup 为 component
- 默认 lime-popup 为 demo
### 关于vue2的使用方式
- 插件使用了`composition-api`, 如果你希望在vue2中使用请按官方的教程[vue-composition-api](https://uniapp.dcloud.net.cn/tutorial/vue-composition-api.html)配置
- 关键代码是: 在main.js中 在vue2部分加上这一段即可
```js
// vue2
import Vue from 'vue'
import VueCompositionAPI from '@vue/composition-api'
Vue.use(VueCompositionAPI)
```
## API
### Props
| 参数 | 说明 | 类型 | 默认值 |
| --- | --- | --- | --- |
| v-model | 是否显示弹出层 | _boolean_ | `false` |
| overlay | 是否显示遮罩层 | _boolean_ | `true` |
| position | 弹出位置,可选值为 `top` `bottom` `right` `left` | _string_ | `center` |
| duration | 动画时长,单位毫秒,设置为 0 可以禁用动画 | _number_ | `300` |
| z-index | 将弹窗的 z-index 层级设置为一个固定值 | _number_ | `999` |
| preventScrollThrough | 是否锁定背景滚动 | _boolean_ | `true` |
| closeOnClickOverlay | 是否在点击遮罩层后关闭 | _boolean_ | `true` |
| destroyOnClose | 关闭后是否销毁 | _boolean_ | `false` |
| closeable | 是否显示关闭图标 | _boolean_ | `false` |
| closeIcon | 关闭图标名称或图片链接,等同于 Icon 组件的 [name 属性](https://ext.dcloud.net.cn/plugin?id=14057) | _string_ | `cross` |
| bgColor | 背景色 | _string_ | `-` |
| iconColor | 图标色 | _string_ | `-` |
| lStyle | 自定义样式 | _string\|object_ | `-` |
| radius | 圆角,可以是字符,数值,数组 | _string\|number\|Array\<string\|number\>_ | `-` |
### Events
| 事件名 | 说明 | 回调参数 |
| ---------------- | -------------------------- | ------------------- |
| click-overlay | 点击遮罩层时触发 | - |
| click-close | 点击关闭图标时触发 | - |
| open | 打开弹出层时立即触发 | - |
| close | 关闭弹出层时立即触发 | - |
| opened | 打开弹出层且动画结束后触发 | - |
| closed | 关闭弹出层且动画结束后触发 | - |
### Slots
| 名称 | 说明 |
| --------------- | ------------ |
| default | 弹窗内容 |
| close-btn | 关闭按钮 |
## 主题定制
### 样式变量
组件提供了下列 CSS 变量可用于自定义样式uvue app无效。
| 名称 | 默认值 | 描述 |
| ------------------------------ | ------------------------------------ | ---- |
| --l-popup-bg-color | _#fff_ | - |
| --l-popup-close-icon-color | _rgba(0,0,0,0.6)_ | - |
| --l-popup-border-radius | _$border-radius_ | - |
## 打赏
如果你觉得本插件,解决了你的问题,赠人玫瑰,手留余香。
![](https://testingcf.jsdelivr.net/gh/liangei/image@1.9/alipay.png)
![](https://testingcf.jsdelivr.net/gh/liangei/image@1.9/wpay.png)