(component): 新增layer.notifiy()方法,修复layer传入time:0时直接执行销毁的错误

ISSUES CLOSED: #I5BUMN
This commit is contained in:
0o张不歪o0 2022-06-29 18:03:31 +08:00
parent 7d52aa5423
commit 79509e99f9
7 changed files with 1132 additions and 827 deletions

View File

@ -231,7 +231,7 @@ const openAreaAuto2 = function(){
title:"area:auto", title:"area:auto",
offset:['10px','50%'], offset:['10px','50%'],
isHtmlFragment:true, isHtmlFragment:true,
content:"<img src='https://chixian.oss-cn-hangzhou.aliyuncs.com/20210819230007_346ce.jpeg'/>" content:"<img src='https://chixian.oss-cn-hangzhou.aliyuncs.com/20210819230007_346ce.jpeg'/>",
}) })
} }
</script> </script>
@ -322,6 +322,105 @@ const groupImg = function() {
::: :::
::: demo 通过 layer.notifiy(options) 创建通知。
<template>
<lay-row :space="30" >
<lay-col :span="24">
<button @click="baseNotifiy">基本使用</button>
<button @click="baseNotifiyRB">右下</button>
<button @click="baseNotifiyLT">左上</button>
<button @click="baseNotifiyLB">左下</button>
<button @click="baseNotifiyTime0">不主动关闭</button>
<button @click="baseNotifiyHtml">HTML解析</button>
</lay-col>
<lay-col :span="24">
<button @click="NotifiySuccess">成功通知</button>
<button @click="NotifiyFailure">失败通知</button>
<button @click="NotifiyWarm">警告通知</button>
<button @click="NotifiyInfo">锁定通知</button>
</lay-col>
</lay-row>
</template>
<script>
import { layer } from "../../../../layer/src/index"
const baseNotifiy = function() {
layer.notifiy({
title:"这是标题",
content:"默认就是右上,也是用得最多的"
})
}
const baseNotifiyRB = function() {
layer.notifiy({
title:"这是标题",
content:"我出现在右下",
offset:'rb',
})
}
const baseNotifiyLT = function() {
layer.notifiy({
title:"这是标题",
content:"我出现在左上",
offset:'lt',
})
}
const baseNotifiyLB = function() {
layer.notifiy({
title:"这是标题",
content:"我出现在左下",
offset:'lb',
})
}
const baseNotifiyTime0 = function() {
layer.notifiy({
title:"这是标题",
content:"不会主动关闭,请点击右上角关闭图标",
time:0
})
}
const baseNotifiyHtml = function() {
layer.notifiy({
title:"这是标题,有图片时请设置area参数",
isHtmlFragment:true,
content:"<img src='https://chixian.oss-cn-hangzhou.aliyuncs.com/20210819230007_346ce.jpeg'/>",
area:['330px','220px'],
time:3000
})
}
const NotifiySuccess=function(){
layer.notifiy({
title:"Success",
content:"默认就是右上,也是用得最多的",
icon:1
})
}
const NotifiyFailure=function(){
layer.notifiy({
title:"Error",
content:"默认就是右上,也是用得最多的",
icon:2
})
}
const NotifiyWarm=function(){
layer.notifiy({
title:"Warming",
content:"默认就是右上,也是用得最多的",
icon:3
})
}
const NotifiyInfo=function(){
layer.notifiy({
title:"Question",
content:"默认就是右上,也是用得最多的",
icon:4
})
}
</script>
:::
<fieldset class="layui-elem-field layui-field-title"> <fieldset class="layui-elem-field layui-field-title">
<legend>销毁</legend> <legend>销毁</legend>
</fieldset> </fieldset>

View File

@ -96,6 +96,7 @@ const changeVisible = () => {
| shadeOpacity | 遮盖层透明度 | string | `0.1` | `0.1` - `1` | | shadeOpacity | 遮盖层透明度 | string | `0.1` | `0.1` - `1` |
| isHtmlFragment | 解析 html 字符 | boolean | `false` | `true` `false` | | isHtmlFragment | 解析 html 字符 | boolean | `false` | `true` `false` |
| imgList | 图片数据数组 | array[{src:图片链接,alt:图片名字可选'}] | - | - | | imgList | 图片数据数组 | array[{src:图片链接,alt:图片名字可选'}] | - | - |
| startIndex | 图片初始浏览索引 | number | 0 | - |
<fieldset class="layui-elem-field layui-field-title"> <fieldset class="layui-elem-field layui-field-title">
<legend>动画</legend> <legend>动画</legend>
</fieldset> </fieldset>

View File

@ -0,0 +1,59 @@
<template>
<div class="layer-notifiy" ref="notifyRef">
<h2 class="title">
<i v-if="icon" :class="iconClass"></i>
{{ title }}
</h2>
<div class="content" v-if="!isHtmlFragment">
<p>{{ content }}</p>
</div>
<div class="content" v-html="content" v-else></div>
<CloseBtnVue @click="close"></CloseBtnVue>
</div>
</template>
<script lang="ts">
export default {
name: "Notifiy",
};
</script>
<script lang="ts" setup>
import { nextTick, onMounted, ref } from 'vue';
import CloseBtnVue from './CloseBtn.vue';
export interface LayNotifyProps {
title: any;
content: any;
isHtmlFragment?: boolean;
icon?: string | number | undefined,
iconClass: string[]
}
const props = withDefaults(defineProps<LayNotifyProps>(), {
isHtmlFragment: false,
});
const emit = defineEmits(['close'])
const close = () => {
emit('close')
}
function addClass(obj: { className: any; }, cls: string) {
// class .
let obj_class = obj.className,
// class , ''.
blank = (obj_class != '') ? ' ' : '';
let added = obj_class + blank + cls;// class class.
obj.className = added;// class.
}
const notifyRef = ref<HTMLElement | null>(null)
onMounted(() => {
nextTick(() => {
if (notifyRef.value) {
setTimeout(() => {//class
// @ts-ignore
addClass(notifyRef.value.parentElement?.parentElement, 'layui-layer-notify')
}, 300);
}
})
})
</script>

View File

@ -11,6 +11,7 @@ import Title from "./Title.vue";
import CloseBtn from "./CloseBtn.vue"; import CloseBtn from "./CloseBtn.vue";
import Resize from "./Resize.vue"; import Resize from "./Resize.vue";
import Photos from "./Photos.vue"; import Photos from "./Photos.vue";
import Notifiy from "./Notifiy.vue";
import { import {
Ref, Ref,
ref, ref,
@ -36,11 +37,12 @@ import {
getDrawerAnimationClass, getDrawerAnimationClass,
calculateDrawerArea, calculateDrawerArea,
calculatePhotosArea, calculatePhotosArea,
calculateNotifOffset,
removeNotifiyFromQueen,
} from "../utils"; } from "../utils";
import useMove from "../composable/useMove"; import useMove from "../composable/useMove";
import useResize from "../composable/useResize"; import useResize from "../composable/useResize";
import { zIndexKey } from "../tokens"; import { zIndexKey } from "../tokens";
import { arrayExpression } from "@babel/types";
export interface LayModalProps { export interface LayModalProps {
id?: string; id?: string;
@ -57,18 +59,20 @@ export interface LayModalProps {
move?: boolean | string; move?: boolean | string;
resize?: boolean | string; resize?: boolean | string;
type?: type?:
| 0 | 0
| 1 | 1
| 2 | 2
| 3 | 3
| 4 | 4
| 5 | 5
| "dialog" | 6
| "page" | "dialog"
| "iframe" | "page"
| "loading" | "iframe"
| "drawer" | "loading"
| "photos"; | "drawer"
| "photos"
| "notifiy";
content?: string | Function | object | VNodeTypes; content?: string | Function | object | VNodeTypes;
isHtmlFragment?: boolean; isHtmlFragment?: boolean;
shade?: boolean | string; shade?: boolean | string;
@ -115,12 +119,12 @@ const props = withDefaults(defineProps<LayModalProps>(), {
resize: false, resize: false,
isHtmlFragment: false, isHtmlFragment: false,
isOutAnim: true, isOutAnim: true,
destroy: () => {}, destroy: () => { },
success: () => {}, success: () => { },
end: () => {}, end: () => { },
full: () => {}, full: () => { },
min: () => {}, min: () => { },
restore: () => {}, restore: () => { },
yesText: "确定", yesText: "确定",
isFunction: false, isFunction: false,
isMessage: false, isMessage: false,
@ -182,6 +186,9 @@ const firstOpenDelayCalculation = function () {
area.value = ["auto", "auto"]; area.value = ["auto", "auto"];
} }
offset.value = calculateOffset(props.offset, area.value, props.type); offset.value = calculateOffset(props.offset, area.value, props.type);
if (type == 6) {
offset.value = calculateNotifOffset(props.offset, area.value, id.value)
}
w.value = area.value[0]; w.value = area.value[0];
h.value = area.value[1]; h.value = area.value[1];
_w.value = area.value[0]; _w.value = area.value[0];
@ -375,6 +382,7 @@ const boxClasses = computed(() => {
type === 3 ? "layui-layer-loading" : "", type === 3 ? "layui-layer-loading" : "",
type === 4 ? "layui-layer-drawer" : "", type === 4 ? "layui-layer-drawer" : "",
type === 5 ? "layui-layer-photos" : "", type === 5 ? "layui-layer-photos" : "",
type === 6 ? "layui-layer-notifiy-border" : "",
props.isMessage ? "layui-layer-msg" : "", props.isMessage ? "layui-layer-msg" : "",
props.isMessage && !props.icon ? "layui-layer-hui" : "", props.isMessage && !props.icon ? "layui-layer-hui" : "",
props.skin, props.skin,
@ -433,9 +441,8 @@ const styles = computed<any>(() => {
offset.value[1].indexOf("%") > -1 offset.value[1].indexOf("%") > -1
) { ) {
// @ts-ignore // @ts-ignore
style.transform = `translate(-${ style.transform = `translate(-${style.left.indexOf("%") > -1 ? style.left : 0
style.left.indexOf("%") > -1 ? style.left : 0 },-${style.top.indexOf("%") > -1 ? style.top : 0})`;
},-${style.top.indexOf("%") > -1 ? style.top : 0})`;
} }
} }
} }
@ -462,9 +469,15 @@ const contentClasses = computed(() => {
* @param null * @param null
*/ */
const closeHandle = () => { const closeHandle = () => {
emit("close"); emit("close");
emit("update:modelValue", false); emit("update:modelValue", false);
props.destroy(); props.destroy();
//Notify
if(type===6){
removeNotifiyFromQueen(props.id)
}
}; };
/** /**
@ -578,7 +591,7 @@ const showResize = computed(() => {
* @param type 类型 * @param type 类型
*/ */
const showTitle = computed(() => { const showTitle = computed(() => {
return props.title && props.type != 3 && props.type != "photos"; return props.title && props.type != 3 && props.type != 5 && props.type != 6;
}); });
/* /*
@ -606,33 +619,15 @@ defineExpose({ reset, open, close });
<template> <template>
<div> <div>
<!-- 遮盖层 --> <!-- 遮盖层 -->
<Shade <Shade :index="index" :visible="shadeVisible" :opacity="shadeOpacity" @shadeClick="shadeHandle"></Shade>
:index="index"
:visible="shadeVisible"
:opacity="shadeOpacity"
@shadeClick="shadeHandle"
></Shade>
<!-- 动画容器 --> <!-- 动画容器 -->
<transition <transition :enter-active-class="enterActiveClass" :leave-active-class="leaveActiveClass">
:enter-active-class="enterActiveClass"
:leave-active-class="leaveActiveClass"
>
<!-- 弹出层 --> <!-- 弹出层 -->
<div <div ref="layero" class="layui-layer layui-layer-border" :class="boxClasses" :style="styles" v-if="visible">
ref="layero"
class="layui-layer layui-layer-border"
:class="boxClasses"
:style="styles"
v-if="visible"
>
<!-- 标题 --> <!-- 标题 -->
<Title v-if="showTitle" :title="title"></Title> <Title v-if="showTitle" :title="title"></Title>
<!-- 内容 --> <!-- 内容 -->
<div <div class="layui-layer-content" :style="{ height: contentHeight }" :class="contentClasses">
class="layui-layer-content"
:style="{ height: contentHeight }"
:class="contentClasses"
>
<template v-if="type === 0 || type === 1 || type === 4"> <template v-if="type === 0 || type === 1 || type === 4">
<i v-if="icon" :class="iconClass"></i> <i v-if="icon" :class="iconClass"></i>
<slot v-if="slots.default"></slot> <slot v-if="slots.default"></slot>
@ -644,43 +639,26 @@ defineExpose({ reset, open, close });
</template> </template>
</template> </template>
<Iframe v-if="type === 2" :src="props.content"></Iframe> <Iframe v-if="type === 2" :src="props.content"></Iframe>
<Photos <Photos v-if="type === 5" :imgList="props.imgList" :startIndex="props.startIndex" @resetCalculationPohtosArea="resetCalculationPohtosArea"></Photos>
v-if="type === 5" <Notifiy v-if="type === 6" @close="closeHandle" :title="props.title" :content="props.content" :isHtmlFragment="props.isHtmlFragment" :icon="props.icon" :iconClass="iconClass"></Notifiy>
:imgList="props.imgList"
:startIndex="props.startIndex"
@resetCalculationPohtosArea="resetCalculationPohtosArea"
></Photos>
</div> </div>
<!-- 工具栏 --> <!-- 工具栏 -->
<span class="layui-layer-setwin" v-if="type != 3 && type != 5"> <span class="layui-layer-setwin" v-if="type != 3 && type != 5 && type != 6">
<a <a v-if="maxmin && !max" class="layui-layer-min" :class="[min ? 'layui-layer-ico layui-layer-maxmin' : '']"
v-if="maxmin && !max" href="javascript:;" @click="minHandle">
class="layui-layer-min"
:class="[min ? 'layui-layer-ico layui-layer-maxmin' : '']"
href="javascript:;"
@click="minHandle"
>
<cite v-if="!min"></cite> <cite v-if="!min"></cite>
</a> </a>
<a <a v-if="maxmin && !min" class="layui-layer-ico layui-layer-max" :class="[max ? 'layui-layer-maxmin' : '']"
v-if="maxmin && !min" href="javascript:;" @click="maxHandle"></a>
class="layui-layer-ico layui-layer-max"
:class="[max ? 'layui-layer-maxmin' : '']"
href="javascript:;"
@click="maxHandle"
></a>
<CloseBtn v-if="closeBtn" @closeHandle="closeHandle"></CloseBtn> <CloseBtn v-if="closeBtn" @closeHandle="closeHandle"></CloseBtn>
</span> </span>
<!-- 操作栏 --> <!-- 操作栏 -->
<div <div v-if="((btn && btn.length > 0) || type === 0) && !isMessage" class="layui-layer-btn"
v-if="((btn && btn.length > 0) || type === 0) && !isMessage" :class="[`layui-layer-btn-${btnAlign}`]">
class="layui-layer-btn"
:class="[`layui-layer-btn-${btnAlign}`]"
>
<template v-if="btn && btn.length > 0"> <template v-if="btn && btn.length > 0">
<template v-for="(b, index) in btn" :key="index"> <template v-for="(b, index) in btn" :key="index">
<a :class="[`layui-layer-btn${index}`]" @click="b.callback(id)">{{ <a :class="[`layui-layer-btn${index}`]" @click="b.callback(id)">{{
b.text b.text
}}</a> }}</a>
</template> </template>
</template> </template>

View File

@ -2,7 +2,7 @@ import { render, h, isVNode, getCurrentInstance, AppContext, App } from "vue";
import LayLayer from "./component/index.vue"; import LayLayer from "./component/index.vue";
import { InstallOptions } from "./types"; import { InstallOptions } from "./types";
import { zIndexKey } from "./tokens"; import { zIndexKey } from "./tokens";
import { nextId } from "./utils"; import { nextId, removeNotifiyFromQueen } from "./utils";
// 实例队列 // 实例队列
const layerInstance: any = []; const layerInstance: any = [];
@ -129,7 +129,7 @@ const layer = {
}; };
} }
let defaultOption = { let defaultOption = {
type: "photos", type: 5,
anim: 2, anim: 2,
startIndex: 0, startIndex: 0,
isOutAnim: true, isOutAnim: true,
@ -138,6 +138,18 @@ const layer = {
}; };
return layer.create(option, defaultOption, callback); return layer.create(option, defaultOption, callback);
}, },
//通知
notifiy: (option: any = {}, callback?: Function) => {
option.anim = 5
option.shade = false
option.type = 6
let defaultOption = {
offset: 'rt',
time:2000,
area:'auto'
}
return layer.create(option, defaultOption, callback);
},
// 创建弹出层 // 创建弹出层
create: (option: any, defaultOption: any, callback?: Function) => { create: (option: any, defaultOption: any, callback?: Function) => {
// 销毁定时 // 销毁定时
@ -174,7 +186,7 @@ const layer = {
// 调用 open 函数 // 调用 open 函数
modalInstance.component?.exposed?.open(); modalInstance.component?.exposed?.open();
// 延时 time 销毁 // 延时 time 销毁
if (defaultOption && defaultOption.time != undefined) { if (defaultOption && defaultOption.time != undefined && defaultOption.time != 0) {
timer = setTimeout(() => { timer = setTimeout(() => {
modalInstance.component?.exposed?.close(); modalInstance.component?.exposed?.close();
if (callback) callback(modalContainer.id); if (callback) callback(modalContainer.id);
@ -186,6 +198,10 @@ const layer = {
}, 2000); }, 2000);
// 销毁实例 // 销毁实例
delInstance(modalContainer.id); delInstance(modalContainer.id);
//Notifiy特殊处理
if (options.type === 6) {
removeNotifiyFromQueen(options.id)
}
}, defaultOption.time); }, defaultOption.time);
} }
// 维护实例 // 维护实例

File diff suppressed because it is too large Load Diff

View File

@ -75,6 +75,9 @@ export function calculateOffset(offset: any, area: any, type: any) {
if (offset instanceof Array && type === "drawer") { if (offset instanceof Array && type === "drawer") {
offset = "r"; offset = "r";
} }
if (offset instanceof Array && type === 6) {
offset = "rt-15";
}
// @ts-ignore // @ts-ignore
if (arr.indexOf(offset) > -1) { if (arr.indexOf(offset) > -1) {
t = "50%"; t = "50%";
@ -102,6 +105,9 @@ export function calculateOffset(offset: any, area: any, type: any) {
} else if (offset === "rb") { } else if (offset === "rb") {
t = "calc(100% - " + area[1] + ")"; t = "calc(100% - " + area[1] + ")";
l = "calc(100% - " + area[0] + ")"; l = "calc(100% - " + area[0] + ")";
} else if (offset === "rt-15") {
t = "15px";
l = "calc(100% - " + (parseFloat(area[0]) + 15) + "px)";
} }
// 返回位置 // 返回位置
@ -120,8 +126,10 @@ export function calculateType(modalType: number | string) {
return 3; return 3;
} else if (modalType === "drawer" || modalType == 4) { } else if (modalType === "drawer" || modalType == 4) {
return 4; return 4;
} else if (modalType === "photos") { } else if (modalType === "photos" || modalType == 5) {
return 5; return 5;
} else if (modalType === "notifiy" || modalType == 6) {
return 6;
} }
return 0; return 0;
} }
@ -235,6 +243,7 @@ export function getDrawerAnimationClass(offset: any, isClose: boolean = false) {
return isClose ? `${prefix}-${suffix}-close` : `${prefix}-${suffix}`; return isClose ? `${prefix}-${suffix}-close` : `${prefix}-${suffix}`;
} }
//计算图片大小 并缩放
export async function calculatePhotosArea(url: string, options: object) { export async function calculatePhotosArea(url: string, options: object) {
let img = new Image(); let img = new Image();
img.src = url; img.src = url;
@ -273,3 +282,98 @@ export async function calculatePhotosArea(url: string, options: object) {
return [imgarea[0] + "px", imgarea[1] + "px"]; return [imgarea[0] + "px", imgarea[1] + "px"];
} }
} }
// 计算Notify位置 队列 此处先暂时定义Notify的间距为15px
export function calculateNotifOffset(offset: any, area: any, layerId: string) {
let arr = ["lt", "lb", "rt", "rb"];
let t = '0', l = '0';
// 间隙
let transOffsetLeft = 15;
let transOffsetTop = 15;
// @ts-ignore
window.NotifiyQueen = window.NotifiyQueen || [];
// @ts-ignore
let notifiyQueen = window.NotifiyQueen;
if (typeof offset != 'string' || arr.indexOf(offset) === -1) {
offset = "rt";
}
// 当前区域元素集合
let nodeList = notifiyQueen.filter((e: { offset: any; }) => {
if (e.offset === offset) {
return e
}
})
//前一个元素
let prevNode = nodeList.length > 0 ? nodeList[nodeList.length - 1] : null;
if (prevNode) {
prevNode = document.getElementById(prevNode['id'])?.firstElementChild?.firstElementChild;
if (offset === "rt" || offset === "lt") {
transOffsetTop += prevNode.offsetHeight + parseFloat(prevNode.style['top'])
} else {
let bottom = parseFloat(prevNode.style['top'].split(' - ')[1])
transOffsetTop += prevNode.offsetHeight + bottom;
}
} else {
if (offset === "rb" || offset === "lb") {
transOffsetTop += parseFloat(area[1]);
}
}
// 关键字处理
if (offset === "rt") {
t = transOffsetTop + "px";
l = "calc(100% - " + (parseFloat(area[0]) + transOffsetLeft) + "px)";
} else if (offset === "rb") {
t = "calc(100vh - " + transOffsetTop + "px)"
l = "calc(100% - " + (parseFloat(area[0]) + transOffsetLeft) + "px)";
} else if (offset === "lt") {
t = transOffsetTop + "px";
l = transOffsetLeft + "px";
} else if (offset === "lb") {
t = "calc(100vh - " + transOffsetTop + "px)"
l = transOffsetLeft + "px";
}
notifiyQueen.push({
id: layerId,
offset: offset
})
// 返回位置
return [t, l];
}
//移除Notify队列中某项并且重新计算其他Notify位置
export function removeNotifiyFromQueen(layerId: string | undefined) {
// 间隙
let transOffsetTop = 15;
// @ts-ignore 删除项的高度
let offsetHeight = document.getElementById(layerId)?.firstElementChild?.firstElementChild?.offsetHeight;
// @ts-ignore
window.NotifiyQueen = window.NotifiyQueen || [];
// @ts-ignore
let notifiyQueen = window.NotifiyQueen;
//console.log(notifiyQueen)
let index = notifiyQueen.findIndex((e: { id: string; }) => e.id === layerId)
let offsetType = notifiyQueen[index].offset;
let list = notifiyQueen.filter((e: { offset: any; }) => {
if (e.offset === offsetType) {
return e;
}
})
let findIndex = list.findIndex((e: { id: string; }) => e.id === layerId)
// //得到需要修改的定位的Notifiy集合
let needCalculatelist = list.slice(findIndex + 1)
needCalculatelist.forEach((e: { id: string; }) => {
let dom = document.getElementById(e.id)?.firstElementChild?.firstElementChild
if (offsetType === 'rt' || offsetType === 'lt') {
// @ts-ignore
dom.style['top'] = parseFloat(dom.style['top']) - transOffsetTop - offsetHeight + 'px'
} else {
// @ts-ignore
let bottom = parseFloat(dom.style['top'].split(' - ')[1]) - transOffsetTop - offsetHeight
// @ts-ignore
dom.style['top'] = "calc(100vh - " + bottom + "px)"
}
})
notifiyQueen.splice(index, 1);//删除
}